Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions packages/adapter-nuxt/tests/module.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -337,9 +337,9 @@ export default defineDatabaseConfig({
expect(addImports).toHaveBeenCalledTimes(1)
expect(addImports.mock.calls[0]?.[0]).toHaveLength(6)
expect(addImports.mock.calls[0]?.[0]).toEqual(expect.arrayContaining([
expect.objectContaining({ name: 'holo', as: 'holo', from: './runtime/composables' }),
expect.objectContaining({ name: 'useStorage', as: 'useStorage', from: './runtime/composables/storage' }),
expect.objectContaining({ name: 'Storage', as: 'Storage', from: './runtime/composables/storage' }),
expect.objectContaining({ name: 'holo', as: 'holo', from: '@holo-js/adapter-nuxt/runtime' }),
expect.objectContaining({ name: 'useStorage', as: 'useStorage', from: '@holo-js/adapter-nuxt/storage' }),
expect.objectContaining({ name: 'Storage', as: 'Storage', from: '@holo-js/adapter-nuxt/storage' }),
]))
expect(addServerImportsDir).toHaveBeenCalledWith('./runtime/server/imports')
expect(addServerImportsDir).toHaveBeenCalledTimes(1)
Expand Down
6 changes: 3 additions & 3 deletions packages/adapter-nuxt/tests/setup.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -423,9 +423,9 @@ export default defineStorageConfig({
expect(addImports).toHaveBeenCalledTimes(1)
expect(addImports.mock.calls[0]?.[0]).toHaveLength(6)
expect(addImports.mock.calls[0]?.[0]).toEqual(expect.arrayContaining([
expect.objectContaining({ name: 'holo', as: 'holo', from: './runtime/composables' }),
expect.objectContaining({ name: 'useStorage', as: 'useStorage', from: './runtime/composables/storage' }),
expect.objectContaining({ name: 'Storage', as: 'Storage', from: './runtime/composables/storage' }),
expect.objectContaining({ name: 'holo', as: 'holo', from: '@holo-js/adapter-nuxt/runtime' }),
expect.objectContaining({ name: 'useStorage', as: 'useStorage', from: '@holo-js/adapter-nuxt/storage' }),
expect.objectContaining({ name: 'Storage', as: 'Storage', from: '@holo-js/adapter-nuxt/storage' }),
]))
expect(addServerImportsDir).toHaveBeenCalledWith('./runtime/server/imports')
expect(addServerImportsDir).toHaveBeenCalledTimes(1)
Expand Down
57 changes: 37 additions & 20 deletions packages/adapter-sveltekit/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { AsyncLocalStorage } from 'node:async_hooks'
import {
createHoloFrameworkAdapter,
type HoloAdapterProject,
type HoloFrameworkOptions,
} from '@holo-js/core'
import { getRequestEvent } from '$app/server'
import type { HoloConfigMap } from '@holo-js/config'
export {
holoSvelteKitTransport,
Expand All @@ -14,49 +14,66 @@ export type SvelteKitHoloOptions = HoloFrameworkOptions

export type SvelteKitHoloProject<TCustom extends HoloConfigMap = HoloConfigMap> = HoloAdapterProject<TCustom>

function withSvelteKitAuthRequest(options: SvelteKitHoloOptions = {}): SvelteKitHoloOptions {
if (options.authRequest) {
return options
type SvelteKitRequestEvent = {
readonly cookies: {
get(name: string): string | undefined
}

return {
...options,
authRequest: {
async getCookie(name: string) {
const event = getRequestEvent()
return event.cookies.get(name) ?? undefined
},
async getHeader(name: string) {
const event = getRequestEvent()
return event.request.headers.get(name) ?? undefined
},
},
readonly request: {
readonly headers: Headers
}
}

const svelteKitAdapter = createHoloFrameworkAdapter<SvelteKitHoloOptions>({
stateKey: '__holoSvelteKitAdapter__',
displayName: 'SvelteKit',
})
const svelteKitRequestEventStore = new AsyncLocalStorage<SvelteKitRequestEvent>()

function resolveSvelteKitAuthRequestAccessors(): NonNullable<SvelteKitHoloOptions['authRequest']> {
return {
async getCookie(name: string) {
const event = svelteKitRequestEventStore.getStore()
return event?.cookies.get(name) ?? undefined
},
async getHeader(name: string) {
const event = svelteKitRequestEventStore.getStore()
return event?.request.headers.get(name) ?? undefined
},
}
}

function resolveSvelteKitOptions(options: SvelteKitHoloOptions): SvelteKitHoloOptions {
return {
...options,
authRequest: options.authRequest ?? resolveSvelteKitAuthRequestAccessors(),
}
}

export const svelteKitHoloCapabilities = svelteKitAdapter.capabilities

export function runWithSvelteKitRequestEvent<TValue>(
event: SvelteKitRequestEvent,
callback: () => TValue,
): TValue {
return svelteKitRequestEventStore.run(event, callback)
}

export async function createSvelteKitHoloProject<TCustom extends HoloConfigMap = HoloConfigMap>(
options: SvelteKitHoloOptions = {},
): Promise<SvelteKitHoloProject<TCustom>> {
return svelteKitAdapter.createProject<TCustom>(withSvelteKitAuthRequest(options))
return svelteKitAdapter.createProject<TCustom>(resolveSvelteKitOptions(options))
}

export async function initializeSvelteKitHoloProject<TCustom extends HoloConfigMap = HoloConfigMap>(
options: SvelteKitHoloOptions = {},
): Promise<SvelteKitHoloProject<TCustom>> {
return svelteKitAdapter.initializeProject<TCustom>(withSvelteKitAuthRequest(options))
return svelteKitAdapter.initializeProject<TCustom>(resolveSvelteKitOptions(options))
}

export function createSvelteKitHoloHelpers<TCustom extends HoloConfigMap = HoloConfigMap>(
options: SvelteKitHoloOptions = {},
) {
return svelteKitAdapter.createHelpers<TCustom>(withSvelteKitAuthRequest(options))
return svelteKitAdapter.createHelpers<TCustom>(resolveSvelteKitOptions(options))
}

export async function resetSvelteKitHoloProject(): Promise<void> {
Expand Down
10 changes: 0 additions & 10 deletions packages/adapter-sveltekit/src/sveltekit.d.ts

This file was deleted.

15 changes: 0 additions & 15 deletions packages/adapter-sveltekit/tests/adapter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,20 +121,6 @@ async function loadAdapterModule() {
if (!adapterModulePromise) {
const { coreEntryUrl } = await ensureIsolatedCoreBuild()
vi.doMock('@holo-js/core', () => import(coreEntryUrl))
vi.doMock('$app/server', () => ({
getRequestEvent() {
return {
cookies: {
get() {
return undefined
},
},
request: {
headers: new Headers(),
},
}
},
}))
adapterModulePromise = import('../src')
}

Expand Down Expand Up @@ -187,7 +173,6 @@ afterEach(async () => {
await resetSvelteKitHoloProject()
adapterModulePromise = null
vi.doUnmock('@holo-js/core')
vi.doUnmock('$app/server')
vi.resetModules()
await Promise.all(tempDirs.splice(0).map(dir => rm(dir, { recursive: true, force: true })))
}, 45000)
Expand Down
10 changes: 7 additions & 3 deletions packages/adapter-sveltekit/tests/adapter.type.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import { execFileSync } from 'node:child_process'
import { mkdtemp, mkdir, rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join, resolve } from 'node:path'
import { describe, expect, it } from 'vitest'
import { createSvelteKitHoloHelpers } from '../src'
import { afterEach, describe, expect, it, vi } from 'vitest'
import type { SerializedSvelteKitData } from '../src/transport'
import {
linkInstalledDependenciesForPackage,
Expand Down Expand Up @@ -33,8 +32,13 @@ declare module '@holo-js/config' {
}
}

afterEach(() => {
vi.resetModules()
})

describe('@holo-js/adapter-sveltekit typing', () => {
it('preserves inference for helper accessors', () => {
it('preserves inference for helper accessors', async () => {
const { createSvelteKitHoloHelpers } = await import('../src')
const helpers = createSvelteKitHoloHelpers()

type Helpers = typeof helpers
Expand Down
171 changes: 171 additions & 0 deletions packages/adapter-sveltekit/tests/runtime.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import { afterEach, describe, expect, it, vi } from 'vitest'

afterEach(() => {
vi.doUnmock('@holo-js/core')
vi.resetModules()
})

describe('@holo-js/adapter-sveltekit request context', () => {
it('owns auth request accessors inside the adapter and resolves them from the current request event', async () => {
let capturedAuthRequest:
| {
getCookie(name: string): Promise<string | undefined>
getHeader(name: string): Promise<string | undefined>
}
| undefined

vi.doMock('@holo-js/core', () => ({
createHoloFrameworkAdapter: () => ({
capabilities: {},
async createProject() {
return {}
},
async initializeProject() {
return {}
},
createHelpers(options: {
authRequest?: {
getCookie(name: string): Promise<string | undefined>
getHeader(name: string): Promise<string | undefined>
}
}) {
capturedAuthRequest = options.authRequest

return {
async getApp() {
return {}
},
async getProject() {
return {}
},
async getSession() {
return undefined
},
async getAuth() {
return undefined
},
async useConfig() {
return undefined
},
async config() {
return undefined
},
}
},
async resetProject() {},
internals: {
getState() {
return {}
},
resolveOptions() {
return {}
},
},
}),
}))

const { createSvelteKitHoloHelpers, runWithSvelteKitRequestEvent } = await import('../src')
const helpers = createSvelteKitHoloHelpers({
projectRoot: '/tmp/holo-sveltekit-runtime',
})

await runWithSvelteKitRequestEvent({
cookies: {
get(name: string) {
return name === 'session' ? 'cookie-value' : undefined
},
},
request: {
headers: new Headers({
'x-request-id': 'header-value',
}),
},
}, async () => {
await helpers.getProject()

await expect(capturedAuthRequest?.getCookie('session')).resolves.toBe('cookie-value')
await expect(capturedAuthRequest?.getHeader('x-request-id')).resolves.toBe('header-value')
})

await expect(capturedAuthRequest?.getCookie('session')).resolves.toBeUndefined()
await expect(capturedAuthRequest?.getHeader('x-request-id')).resolves.toBeUndefined()
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
})

it('preserves explicit auth request overrides', async () => {
let capturedAuthRequest:
| {
getCookie(name: string): Promise<string | undefined>
getHeader(name: string): Promise<string | undefined>
}
| undefined

vi.doMock('@holo-js/core', () => ({
createHoloFrameworkAdapter: () => ({
capabilities: {},
async createProject() {
return {}
},
async initializeProject() {
return {}
},
createHelpers(options: {
authRequest?: {
getCookie(name: string): Promise<string | undefined>
getHeader(name: string): Promise<string | undefined>
}
}) {
capturedAuthRequest = options.authRequest

return {
async getApp() {
return {}
},
async getProject() {
return {}
},
async getSession() {
return undefined
},
async getAuth() {
return undefined
},
async useConfig() {
return undefined
},
async config() {
return undefined
},
}
},
async resetProject() {},
internals: {
getState() {
return {}
},
resolveOptions() {
return {}
},
},
}),
}))

const customAuthRequest = {
async getCookie() {
return 'custom-cookie'
},
async getHeader() {
return 'custom-header'
},
}

const { createSvelteKitHoloHelpers } = await import('../src')
const helpers = createSvelteKitHoloHelpers({
projectRoot: '/tmp/holo-sveltekit-runtime',
authRequest: customAuthRequest,
})

await helpers.getProject()

expect(capturedAuthRequest).toBe(customAuthRequest)
})
})
4 changes: 4 additions & 0 deletions packages/auth/src/contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,10 @@ export interface AuthSessionRuntime {
sessionId: string,
options?: { readonly store?: string },
): Promise<string>
consumeRememberMeToken?(
token: string,
options?: { readonly store?: string },
): Promise<AuthSessionRecord | null>
cookie?(
name: string,
value: string,
Expand Down
Loading