ras-stack 0.36.2 → 0.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,9 @@
1
- export * from './origins.js';
2
- export * from './providers.js';
3
- export * from './random.js';
4
- export * from './secret.js';
5
- export * from './settings.js';
1
+ export { acceptedOrigins, forwardedOrigin, parseOrigin, requireSameOrigin, trustedOrigins, validSameOriginRequest } from './origins.js';
2
+ export type { OriginOptions } from './origins.js';
3
+ export { configuredProviderOptions, configuredProviders, providerCredentials } from './providers.js';
4
+ export type { ProviderCredentials } from './providers.js';
5
+ export { randomId, randomToken } from './random.js';
6
+ export { persistedSecret } from './secret.js';
7
+ export type { PersistedSecretOptions } from './secret.js';
8
+ export { standardRateLimitOptions, standardSessionOptions } from './settings.js';
9
+ export type { RateLimitRule } from './settings.js';
@@ -1,6 +1,6 @@
1
- export * from './origins.js';
2
- export * from './providers.js';
3
- export * from './random.js';
4
- export * from './secret.js';
5
- export * from './settings.js';
1
+ export { acceptedOrigins, forwardedOrigin, parseOrigin, requireSameOrigin, trustedOrigins, validSameOriginRequest } from './origins.js';
2
+ export { configuredProviderOptions, configuredProviders, providerCredentials } from './providers.js';
3
+ export { randomId, randomToken } from './random.js';
4
+ export { persistedSecret } from './secret.js';
5
+ export { standardRateLimitOptions, standardSessionOptions } from './settings.js';
6
6
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA;AAC5B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA","sourcesContent":["export * from './origins.js'\nexport * from './providers.js'\nexport * from './random.js'\nexport * from './secret.js'\nexport * from './settings.js'\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,WAAW,EAAE,iBAAiB,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAA;AAEvI,OAAO,EAAE,yBAAyB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAA;AAEpG,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAE7C,OAAO,EAAE,wBAAwB,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAA","sourcesContent":["export { acceptedOrigins, forwardedOrigin, parseOrigin, requireSameOrigin, trustedOrigins, validSameOriginRequest } from './origins.js'\nexport type { OriginOptions } from './origins.js'\nexport { configuredProviderOptions, configuredProviders, providerCredentials } from './providers.js'\nexport type { ProviderCredentials } from './providers.js'\nexport { randomId, randomToken } from './random.js'\nexport { persistedSecret } from './secret.js'\nexport type { PersistedSecretOptions } from './secret.js'\nexport { standardRateLimitOptions, standardSessionOptions } from './settings.js'\nexport type { RateLimitRule } from './settings.js'\n"]}
@@ -11,6 +11,13 @@ export type HealthHandlerFactory = (check: () => void | Promise<void>) => () =>
11
11
  export declare function assertHealthHandlerConformance(createHandler: HealthHandlerFactory): Promise<void>;
12
12
  export type SqlitePragmaReader = (name: 'journal_mode' | 'synchronous' | 'busy_timeout' | 'foreign_keys') => unknown;
13
13
  export declare function assertSqliteConformance(readPragma: SqlitePragmaReader): Promise<void>;
14
+ export type RealtimeTokenSigner = (subject: string, claims: Record<string, unknown>) => string | Promise<string>;
15
+ export type RealtimeTokenConformanceOptions = {
16
+ secret: string;
17
+ maxTtlSeconds?: number;
18
+ now?: number;
19
+ };
20
+ export declare function assertRealtimeTokenConformance(sign: RealtimeTokenSigner, options: RealtimeTokenConformanceOptions): Promise<void>;
14
21
  export type DatabaseTarget = {
15
22
  provider: 'sqlite';
16
23
  file: string;
@@ -74,6 +74,74 @@ export async function assertSqliteConformance(readPragma) {
74
74
  throw new ConformanceError('SQLite foreign keys', `expected enabled (1), received ${values.foreignKeys}`);
75
75
  }
76
76
  }
77
+ export async function assertRealtimeTokenConformance(sign, options) {
78
+ const now = options.now ?? Math.floor(Date.now() / 1000);
79
+ const maxTtlSeconds = options.maxTtlSeconds ?? 60 * 60;
80
+ const token = await sign('person-123', { channel: 'room:1' });
81
+ const { header, payload, signed, signature } = decodeToken(token);
82
+ if (header.alg !== 'HS256') {
83
+ throw new ConformanceError('realtime token algorithm', `expected HS256, received ${JSON.stringify(header.alg)}`);
84
+ }
85
+ if (payload.sub !== 'person-123') {
86
+ throw new ConformanceError('realtime token subject', 'token must bind the subject it was signed for');
87
+ }
88
+ if (payload.channel !== 'room:1') {
89
+ throw new ConformanceError('realtime token claims', 'token must carry the claims it was signed with');
90
+ }
91
+ if (typeof payload.exp !== 'number') {
92
+ throw new ConformanceError('realtime token expiry', 'token must expire');
93
+ }
94
+ if (payload.exp <= now) {
95
+ throw new ConformanceError('realtime token expiry', 'token expired before it was issued');
96
+ }
97
+ if (payload.exp - now > maxTtlSeconds) {
98
+ throw new ConformanceError('realtime token expiry', `token outlives the ${maxTtlSeconds} second maximum`);
99
+ }
100
+ if (!(await verifyHmac(signed, signature, options.secret))) {
101
+ throw new ConformanceError('realtime token signature', 'token is not signed with the shared Centrifugo secret');
102
+ }
103
+ const other = decodeToken(await sign('person-456', { channel: 'room:1' })).payload;
104
+ if (other.sub === payload.sub) {
105
+ throw new ConformanceError('realtime token subject', 'every subject received the same identity');
106
+ }
107
+ }
108
+ function decodeToken(token) {
109
+ const segments = token.split('.');
110
+ if (segments.length !== 3)
111
+ throw new ConformanceError('realtime token format', 'expected a three-segment JWT');
112
+ const [header, claims, signature] = segments;
113
+ return {
114
+ header: decodeSegment(header, 'header'),
115
+ payload: decodeSegment(claims, 'payload'),
116
+ signed: `${header}.${claims}`,
117
+ signature,
118
+ };
119
+ }
120
+ function decodeSegment(segment, name) {
121
+ try {
122
+ const padded = segment
123
+ .replaceAll('-', '+')
124
+ .replaceAll('_', '/')
125
+ .padEnd(Math.ceil(segment.length / 4) * 4, '=');
126
+ const value = JSON.parse(atob(padded));
127
+ if (value && typeof value === 'object')
128
+ return value;
129
+ }
130
+ catch (error) {
131
+ throw new ConformanceError('realtime token format', `${name} is not base64url JSON`, { cause: error });
132
+ }
133
+ throw new ConformanceError('realtime token format', `${name} is not an object`);
134
+ }
135
+ async function verifyHmac(signed, signature, secret) {
136
+ const encoder = new TextEncoder();
137
+ const key = await crypto.subtle.importKey('raw', encoder.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
138
+ const digest = new Uint8Array(await crypto.subtle.sign('HMAC', key, encoder.encode(signed)));
139
+ const expected = btoa(String.fromCharCode(...digest))
140
+ .replaceAll('+', '-')
141
+ .replaceAll('/', '_')
142
+ .replaceAll('=', '');
143
+ return expected === signature;
144
+ }
77
145
  export function assertDatabaseTargetConformance(resolve) {
78
146
  const sqliteFile = '/data/application.sqlite';
79
147
  const sqlite = resolve({ sqliteFile });
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/conformance/index.ts"],"names":[],"mappings":"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","sourcesContent":["export class ConformanceError extends Error {\n constructor(\n readonly scenario: string,\n message: string,\n options: { cause?: unknown } = {},\n ) {\n super(`${scenario}: ${message}`, options.cause === undefined ? undefined : { cause: options.cause })\n this.name = 'ConformanceError'\n }\n}\n\nexport async function assertMutationOriginConformance(\n guard: (request: Request) => void | Promise<void>,\n options: { trustForwardedHeaders?: boolean } = {},\n) {\n await accepted('same-origin request', () =>\n guard(\n new Request('https://app.example/action', {\n method: 'POST',\n headers: { origin: 'https://app.example', 'sec-fetch-site': 'same-origin' },\n }),\n ),\n )\n await rejected('cross-origin request', () =>\n guard(\n new Request('https://app.example/action', {\n method: 'POST',\n headers: { origin: 'https://attacker.example', 'sec-fetch-site': 'cross-site' },\n }),\n ),\n )\n await rejected('missing-origin request', () => guard(new Request('https://app.example/action', { method: 'POST' })))\n if (!options.trustForwardedHeaders) {\n await rejected('spoofed forwarded-origin request', () =>\n guard(\n new Request('https://app.example/action', {\n method: 'POST',\n headers: {\n origin: 'https://attacker.example',\n 'x-forwarded-host': 'attacker.example',\n 'x-forwarded-proto': 'https',\n },\n }),\n ),\n )\n }\n}\n\nexport type HealthHandlerFactory = (check: () => void | Promise<void>) => () => Response | Promise<Response>\n\nexport async function assertHealthHandlerConformance(createHandler: HealthHandlerFactory) {\n const healthy = await createHandler(() => undefined)()\n if (healthy.status !== 200) throw new ConformanceError('healthy dependency', `expected status 200, received ${healthy.status}`)\n const healthyBody = await responseBody(healthy, 'healthy dependency')\n if (healthyBody.ok !== true) throw new ConformanceError('healthy dependency', 'response body must contain ok: true')\n\n const privateMessage = 'password=private-diagnostic'\n const unavailable = await createHandler(() => Promise.reject(new Error(privateMessage)))()\n if (unavailable.status !== 503) {\n throw new ConformanceError('unavailable dependency', `expected status 503, received ${unavailable.status}`)\n }\n const unavailableText = await unavailable.text()\n if (unavailableText.includes(privateMessage)) {\n throw new ConformanceError('unavailable dependency', 'response exposed the private diagnostic message')\n }\n let unavailableBody: unknown\n try {\n unavailableBody = JSON.parse(unavailableText)\n } catch (error) {\n throw new ConformanceError('unavailable dependency', 'response body must be JSON', { cause: error })\n }\n if (!unavailableBody || typeof unavailableBody !== 'object' || !('ok' in unavailableBody) || unavailableBody.ok !== false) {\n throw new ConformanceError('unavailable dependency', 'response body must contain ok: false')\n }\n}\n\nexport type SqlitePragmaReader = (name: 'journal_mode' | 'synchronous' | 'busy_timeout' | 'foreign_keys') => unknown\n\nexport async function assertSqliteConformance(readPragma: SqlitePragmaReader) {\n const values = {\n journalMode: String(await readPragma('journal_mode')).toLowerCase(),\n synchronous: Number(await readPragma('synchronous')),\n busyTimeout: Number(await readPragma('busy_timeout')),\n foreignKeys: Number(await readPragma('foreign_keys')),\n }\n if (values.journalMode !== 'wal' && values.journalMode !== 'memory') {\n throw new ConformanceError('SQLite journal mode', `expected wal or memory, received ${values.journalMode}`)\n }\n if (values.synchronous !== 2) {\n throw new ConformanceError('SQLite synchronous mode', `expected FULL (2), received ${values.synchronous}`)\n }\n if (values.busyTimeout !== 5000) {\n throw new ConformanceError('SQLite busy timeout', `expected 5000, received ${values.busyTimeout}`)\n }\n if (values.foreignKeys !== 1) {\n throw new ConformanceError('SQLite foreign keys', `expected enabled (1), received ${values.foreignKeys}`)\n }\n}\n\nexport type DatabaseTarget = { provider: 'sqlite'; file: string } | { provider: 'postgres'; url: string }\n\nexport type DatabaseTargetResolver = (options: { databaseUrl?: string; sqliteFile: string }) => DatabaseTarget\n\nexport function assertDatabaseTargetConformance(resolve: DatabaseTargetResolver) {\n const sqliteFile = '/data/application.sqlite'\n const sqlite = resolve({ sqliteFile })\n if (sqlite.provider !== 'sqlite' || sqlite.file !== sqliteFile) {\n throw new ConformanceError('default database target', 'expected the configured SQLite file')\n }\n\n for (const url of ['postgres://user:secret@database/application', 'postgresql://user:secret@database/application']) {\n const target = resolve({ databaseUrl: url, sqliteFile })\n if (target.provider !== 'postgres' || target.url !== url) {\n throw new ConformanceError('PostgreSQL database target', `expected the configured ${new URL(url).protocol} URL`)\n }\n }\n\n try {\n resolve({ databaseUrl: 'https://database.example/application', sqliteFile })\n } catch {\n return\n }\n throw new ConformanceError('invalid database target', 'expected a non-PostgreSQL URL to be rejected')\n}\n\nexport function assertPostHogBrowserConformance(options: Record<string, unknown>) {\n if (typeof options.api_host !== 'string' || !options.api_host.trim()) {\n throw new ConformanceError('PostHog browser initialization', 'api_host must be configured')\n }\n if (typeof options.ui_host !== 'string' || !options.ui_host.trim()) {\n throw new ConformanceError('PostHog browser initialization', 'ui_host must be configured')\n }\n if (typeof options.defaults !== 'string' || !options.defaults.trim()) {\n throw new ConformanceError('PostHog browser initialization', 'SDK defaults must be pinned')\n }\n if (!options.capture_exceptions) {\n throw new ConformanceError('PostHog browser initialization', 'exception autocapture must be enabled')\n }\n if (options.capture_pageview !== 'history_change') {\n throw new ConformanceError('PostHog browser initialization', 'SPA pageviews must follow history changes')\n }\n if (options.person_profiles !== 'identified_only') {\n throw new ConformanceError('PostHog browser initialization', 'person profiles must be limited to identified users')\n }\n const recording = options.session_recording\n if (!recording || typeof recording !== 'object' || !('maskAllInputs' in recording) || recording.maskAllInputs !== true) {\n throw new ConformanceError('PostHog browser initialization', 'session replay must mask all inputs by default')\n }\n}\n\ntype PostHogContextParser = (\n request: Request,\n options?: { authenticatedDistinctId?: string; allowAnonymousDistinctId?: boolean },\n) => { distinctId?: string; sessionId?: string; properties: { $session_id?: string } }\n\nexport function assertPostHogRequestConformance(parse: PostHogContextParser) {\n const matched = parse(postHogRequest('person-123', 'session-456'), { authenticatedDistinctId: 'person-123' })\n if (matched.distinctId !== 'person-123' || matched.sessionId !== 'session-456' || matched.properties.$session_id !== 'session-456') {\n throw new ConformanceError('authenticated PostHog request', 'expected verified identity and session propagation')\n }\n const spoofed = parse(postHogRequest('attacker', 'session-456'), { authenticatedDistinctId: 'person-123' })\n if (spoofed.distinctId !== undefined) {\n throw new ConformanceError('spoofed PostHog request', 'unverified distinct id was trusted')\n }\n const malformed = parse(postHogRequest('person-123', 'x'.repeat(129)), { authenticatedDistinctId: 'person-123' })\n if (malformed.sessionId !== undefined || malformed.properties.$session_id !== undefined) {\n throw new ConformanceError('malformed PostHog request', 'unbounded session id was propagated')\n }\n}\n\nfunction postHogRequest(distinctId: string, sessionId: string) {\n return new Request('https://app.example/action', {\n headers: { 'x-posthog-distinct-id': distinctId, 'x-posthog-session-id': sessionId },\n })\n}\n\nasync function accepted(scenario: string, work: () => void | Promise<void>) {\n try {\n await work()\n } catch (error) {\n throw new ConformanceError(scenario, 'expected request to be accepted', { cause: error })\n }\n}\n\nasync function rejected(scenario: string, work: () => void | Promise<void>) {\n try {\n await work()\n } catch {\n return\n }\n throw new ConformanceError(scenario, 'expected request to be rejected')\n}\n\nasync function responseBody(response: Response, scenario: string): Promise<Record<string, unknown>> {\n try {\n const body: unknown = await response.json()\n if (body && typeof body === 'object') return body as Record<string, unknown>\n } catch (error) {\n throw new ConformanceError(scenario, 'response body must be JSON', { cause: error })\n }\n throw new ConformanceError(scenario, 'response body must be an object')\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/conformance/index.ts"],"names":[],"mappings":"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","sourcesContent":["export class ConformanceError extends Error {\n constructor(\n readonly scenario: string,\n message: string,\n options: { cause?: unknown } = {},\n ) {\n super(`${scenario}: ${message}`, options.cause === undefined ? undefined : { cause: options.cause })\n this.name = 'ConformanceError'\n }\n}\n\nexport async function assertMutationOriginConformance(\n guard: (request: Request) => void | Promise<void>,\n options: { trustForwardedHeaders?: boolean } = {},\n) {\n await accepted('same-origin request', () =>\n guard(\n new Request('https://app.example/action', {\n method: 'POST',\n headers: { origin: 'https://app.example', 'sec-fetch-site': 'same-origin' },\n }),\n ),\n )\n await rejected('cross-origin request', () =>\n guard(\n new Request('https://app.example/action', {\n method: 'POST',\n headers: { origin: 'https://attacker.example', 'sec-fetch-site': 'cross-site' },\n }),\n ),\n )\n await rejected('missing-origin request', () => guard(new Request('https://app.example/action', { method: 'POST' })))\n if (!options.trustForwardedHeaders) {\n await rejected('spoofed forwarded-origin request', () =>\n guard(\n new Request('https://app.example/action', {\n method: 'POST',\n headers: {\n origin: 'https://attacker.example',\n 'x-forwarded-host': 'attacker.example',\n 'x-forwarded-proto': 'https',\n },\n }),\n ),\n )\n }\n}\n\nexport type HealthHandlerFactory = (check: () => void | Promise<void>) => () => Response | Promise<Response>\n\nexport async function assertHealthHandlerConformance(createHandler: HealthHandlerFactory) {\n const healthy = await createHandler(() => undefined)()\n if (healthy.status !== 200) throw new ConformanceError('healthy dependency', `expected status 200, received ${healthy.status}`)\n const healthyBody = await responseBody(healthy, 'healthy dependency')\n if (healthyBody.ok !== true) throw new ConformanceError('healthy dependency', 'response body must contain ok: true')\n\n const privateMessage = 'password=private-diagnostic'\n const unavailable = await createHandler(() => Promise.reject(new Error(privateMessage)))()\n if (unavailable.status !== 503) {\n throw new ConformanceError('unavailable dependency', `expected status 503, received ${unavailable.status}`)\n }\n const unavailableText = await unavailable.text()\n if (unavailableText.includes(privateMessage)) {\n throw new ConformanceError('unavailable dependency', 'response exposed the private diagnostic message')\n }\n let unavailableBody: unknown\n try {\n unavailableBody = JSON.parse(unavailableText)\n } catch (error) {\n throw new ConformanceError('unavailable dependency', 'response body must be JSON', { cause: error })\n }\n if (!unavailableBody || typeof unavailableBody !== 'object' || !('ok' in unavailableBody) || unavailableBody.ok !== false) {\n throw new ConformanceError('unavailable dependency', 'response body must contain ok: false')\n }\n}\n\nexport type SqlitePragmaReader = (name: 'journal_mode' | 'synchronous' | 'busy_timeout' | 'foreign_keys') => unknown\n\nexport async function assertSqliteConformance(readPragma: SqlitePragmaReader) {\n const values = {\n journalMode: String(await readPragma('journal_mode')).toLowerCase(),\n synchronous: Number(await readPragma('synchronous')),\n busyTimeout: Number(await readPragma('busy_timeout')),\n foreignKeys: Number(await readPragma('foreign_keys')),\n }\n if (values.journalMode !== 'wal' && values.journalMode !== 'memory') {\n throw new ConformanceError('SQLite journal mode', `expected wal or memory, received ${values.journalMode}`)\n }\n if (values.synchronous !== 2) {\n throw new ConformanceError('SQLite synchronous mode', `expected FULL (2), received ${values.synchronous}`)\n }\n if (values.busyTimeout !== 5000) {\n throw new ConformanceError('SQLite busy timeout', `expected 5000, received ${values.busyTimeout}`)\n }\n if (values.foreignKeys !== 1) {\n throw new ConformanceError('SQLite foreign keys', `expected enabled (1), received ${values.foreignKeys}`)\n }\n}\n\nexport type RealtimeTokenSigner = (subject: string, claims: Record<string, unknown>) => string | Promise<string>\n\nexport type RealtimeTokenConformanceOptions = { secret: string; maxTtlSeconds?: number; now?: number }\n\nexport async function assertRealtimeTokenConformance(sign: RealtimeTokenSigner, options: RealtimeTokenConformanceOptions) {\n const now = options.now ?? Math.floor(Date.now() / 1000)\n const maxTtlSeconds = options.maxTtlSeconds ?? 60 * 60\n const token = await sign('person-123', { channel: 'room:1' })\n const { header, payload, signed, signature } = decodeToken(token)\n\n if (header.alg !== 'HS256') {\n throw new ConformanceError('realtime token algorithm', `expected HS256, received ${JSON.stringify(header.alg)}`)\n }\n if (payload.sub !== 'person-123') {\n throw new ConformanceError('realtime token subject', 'token must bind the subject it was signed for')\n }\n if (payload.channel !== 'room:1') {\n throw new ConformanceError('realtime token claims', 'token must carry the claims it was signed with')\n }\n if (typeof payload.exp !== 'number') {\n throw new ConformanceError('realtime token expiry', 'token must expire')\n }\n if (payload.exp <= now) {\n throw new ConformanceError('realtime token expiry', 'token expired before it was issued')\n }\n if (payload.exp - now > maxTtlSeconds) {\n throw new ConformanceError('realtime token expiry', `token outlives the ${maxTtlSeconds} second maximum`)\n }\n if (!(await verifyHmac(signed, signature, options.secret))) {\n throw new ConformanceError('realtime token signature', 'token is not signed with the shared Centrifugo secret')\n }\n\n const other = decodeToken(await sign('person-456', { channel: 'room:1' })).payload\n if (other.sub === payload.sub) {\n throw new ConformanceError('realtime token subject', 'every subject received the same identity')\n }\n}\n\nfunction decodeToken(token: string) {\n const segments = token.split('.')\n if (segments.length !== 3) throw new ConformanceError('realtime token format', 'expected a three-segment JWT')\n const [header, claims, signature] = segments as [string, string, string]\n return {\n header: decodeSegment(header, 'header'),\n payload: decodeSegment(claims, 'payload'),\n signed: `${header}.${claims}`,\n signature,\n }\n}\n\nfunction decodeSegment(segment: string, name: string): Record<string, unknown> {\n try {\n const padded = segment\n .replaceAll('-', '+')\n .replaceAll('_', '/')\n .padEnd(Math.ceil(segment.length / 4) * 4, '=')\n const value: unknown = JSON.parse(atob(padded))\n if (value && typeof value === 'object') return value as Record<string, unknown>\n } catch (error) {\n throw new ConformanceError('realtime token format', `${name} is not base64url JSON`, { cause: error })\n }\n throw new ConformanceError('realtime token format', `${name} is not an object`)\n}\n\nasync function verifyHmac(signed: string, signature: string, secret: string) {\n const encoder = new TextEncoder()\n const key = await crypto.subtle.importKey('raw', encoder.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign'])\n const digest = new Uint8Array(await crypto.subtle.sign('HMAC', key, encoder.encode(signed)))\n const expected = btoa(String.fromCharCode(...digest))\n .replaceAll('+', '-')\n .replaceAll('/', '_')\n .replaceAll('=', '')\n return expected === signature\n}\n\nexport type DatabaseTarget = { provider: 'sqlite'; file: string } | { provider: 'postgres'; url: string }\n\nexport type DatabaseTargetResolver = (options: { databaseUrl?: string; sqliteFile: string }) => DatabaseTarget\n\nexport function assertDatabaseTargetConformance(resolve: DatabaseTargetResolver) {\n const sqliteFile = '/data/application.sqlite'\n const sqlite = resolve({ sqliteFile })\n if (sqlite.provider !== 'sqlite' || sqlite.file !== sqliteFile) {\n throw new ConformanceError('default database target', 'expected the configured SQLite file')\n }\n\n for (const url of ['postgres://user:secret@database/application', 'postgresql://user:secret@database/application']) {\n const target = resolve({ databaseUrl: url, sqliteFile })\n if (target.provider !== 'postgres' || target.url !== url) {\n throw new ConformanceError('PostgreSQL database target', `expected the configured ${new URL(url).protocol} URL`)\n }\n }\n\n try {\n resolve({ databaseUrl: 'https://database.example/application', sqliteFile })\n } catch {\n return\n }\n throw new ConformanceError('invalid database target', 'expected a non-PostgreSQL URL to be rejected')\n}\n\nexport function assertPostHogBrowserConformance(options: Record<string, unknown>) {\n if (typeof options.api_host !== 'string' || !options.api_host.trim()) {\n throw new ConformanceError('PostHog browser initialization', 'api_host must be configured')\n }\n if (typeof options.ui_host !== 'string' || !options.ui_host.trim()) {\n throw new ConformanceError('PostHog browser initialization', 'ui_host must be configured')\n }\n if (typeof options.defaults !== 'string' || !options.defaults.trim()) {\n throw new ConformanceError('PostHog browser initialization', 'SDK defaults must be pinned')\n }\n if (!options.capture_exceptions) {\n throw new ConformanceError('PostHog browser initialization', 'exception autocapture must be enabled')\n }\n if (options.capture_pageview !== 'history_change') {\n throw new ConformanceError('PostHog browser initialization', 'SPA pageviews must follow history changes')\n }\n if (options.person_profiles !== 'identified_only') {\n throw new ConformanceError('PostHog browser initialization', 'person profiles must be limited to identified users')\n }\n const recording = options.session_recording\n if (!recording || typeof recording !== 'object' || !('maskAllInputs' in recording) || recording.maskAllInputs !== true) {\n throw new ConformanceError('PostHog browser initialization', 'session replay must mask all inputs by default')\n }\n}\n\ntype PostHogContextParser = (\n request: Request,\n options?: { authenticatedDistinctId?: string; allowAnonymousDistinctId?: boolean },\n) => { distinctId?: string; sessionId?: string; properties: { $session_id?: string } }\n\nexport function assertPostHogRequestConformance(parse: PostHogContextParser) {\n const matched = parse(postHogRequest('person-123', 'session-456'), { authenticatedDistinctId: 'person-123' })\n if (matched.distinctId !== 'person-123' || matched.sessionId !== 'session-456' || matched.properties.$session_id !== 'session-456') {\n throw new ConformanceError('authenticated PostHog request', 'expected verified identity and session propagation')\n }\n const spoofed = parse(postHogRequest('attacker', 'session-456'), { authenticatedDistinctId: 'person-123' })\n if (spoofed.distinctId !== undefined) {\n throw new ConformanceError('spoofed PostHog request', 'unverified distinct id was trusted')\n }\n const malformed = parse(postHogRequest('person-123', 'x'.repeat(129)), { authenticatedDistinctId: 'person-123' })\n if (malformed.sessionId !== undefined || malformed.properties.$session_id !== undefined) {\n throw new ConformanceError('malformed PostHog request', 'unbounded session id was propagated')\n }\n}\n\nfunction postHogRequest(distinctId: string, sessionId: string) {\n return new Request('https://app.example/action', {\n headers: { 'x-posthog-distinct-id': distinctId, 'x-posthog-session-id': sessionId },\n })\n}\n\nasync function accepted(scenario: string, work: () => void | Promise<void>) {\n try {\n await work()\n } catch (error) {\n throw new ConformanceError(scenario, 'expected request to be accepted', { cause: error })\n }\n}\n\nasync function rejected(scenario: string, work: () => void | Promise<void>) {\n try {\n await work()\n } catch {\n return\n }\n throw new ConformanceError(scenario, 'expected request to be rejected')\n}\n\nasync function responseBody(response: Response, scenario: string): Promise<Record<string, unknown>> {\n try {\n const body: unknown = await response.json()\n if (body && typeof body === 'object') return body as Record<string, unknown>\n } catch (error) {\n throw new ConformanceError(scenario, 'response body must be JSON', { cause: error })\n }\n throw new ConformanceError(scenario, 'response body must be an object')\n}\n"]}
@@ -1,3 +1,6 @@
1
- export * from './client.js';
2
- export * from './publisher.js';
3
- export * from './tokens.js';
1
+ export { connectRealtimeClient, createSameOriginRealtimeClient, openRealtimeSubscription, requestRealtimeTicket, sameOriginWebSocketUrl, watchServerChannel, watchSubscriptionPresence, } from './client.js';
2
+ export type { RealtimeSubscription, RealtimeTicketOptions } from './client.js';
3
+ export { CentrifugoPublisher } from './publisher.js';
4
+ export type { CentrifugoPublisherOptions } from './publisher.js';
5
+ export { signRealtimeToken } from './tokens.js';
6
+ export type { RealtimeTokenOptions } from './tokens.js';
@@ -1,4 +1,4 @@
1
- export * from './client.js';
2
- export * from './publisher.js';
3
- export * from './tokens.js';
1
+ export { connectRealtimeClient, createSameOriginRealtimeClient, openRealtimeSubscription, requestRealtimeTicket, sameOriginWebSocketUrl, watchServerChannel, watchSubscriptionPresence, } from './client.js';
2
+ export { CentrifugoPublisher } from './publisher.js';
3
+ export { signRealtimeToken } from './tokens.js';
4
4
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/realtime/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA","sourcesContent":["export * from './client.js'\nexport * from './publisher.js'\nexport * from './tokens.js'\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/realtime/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,8BAA8B,EAC9B,wBAAwB,EACxB,qBAAqB,EACrB,sBAAsB,EACtB,kBAAkB,EAClB,yBAAyB,GAC1B,MAAM,aAAa,CAAA;AAEpB,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAA;AAEpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA","sourcesContent":["export {\n connectRealtimeClient,\n createSameOriginRealtimeClient,\n openRealtimeSubscription,\n requestRealtimeTicket,\n sameOriginWebSocketUrl,\n watchServerChannel,\n watchSubscriptionPresence,\n} from './client.js'\nexport type { RealtimeSubscription, RealtimeTicketOptions } from './client.js'\nexport { CentrifugoPublisher } from './publisher.js'\nexport type { CentrifugoPublisherOptions } from './publisher.js'\nexport { signRealtimeToken } from './tokens.js'\nexport type { RealtimeTokenOptions } from './tokens.js'\n"]}
@@ -1,5 +1,9 @@
1
- export * from './canonical-host.js';
2
- export * from './errors.js';
3
- export * from './health.js';
4
- export * from './rpc.js';
5
- export * from './singleton.js';
1
+ export { canonicalRedirect } from './canonical-host.js';
2
+ export type { CanonicalRedirectOptions } from './canonical-host.js';
3
+ export { errorHasCode, infrastructureDiagnostic, infrastructureFailure, InfrastructureError, safeInfrastructureError } from './errors.js';
4
+ export type { InfrastructureFailure } from './errors.js';
5
+ export { databaseHealthFailure, healthResponse } from './health.js';
6
+ export type { HealthResponseOptions } from './health.js';
7
+ export { createRpc } from './rpc.js';
8
+ export type { RpcErrorContext, RpcLogger, RpcOptions } from './rpc.js';
9
+ export { clearGlobalSingleton, globalAsyncSingleton, globalSingleton, peekGlobalSingleton } from './singleton.js';
@@ -1,6 +1,6 @@
1
- export * from './canonical-host.js';
2
- export * from './errors.js';
3
- export * from './health.js';
4
- export * from './rpc.js';
5
- export * from './singleton.js';
1
+ export { canonicalRedirect } from './canonical-host.js';
2
+ export { errorHasCode, infrastructureDiagnostic, infrastructureFailure, InfrastructureError, safeInfrastructureError } from './errors.js';
3
+ export { databaseHealthFailure, healthResponse } from './health.js';
4
+ export { createRpc } from './rpc.js';
5
+ export { clearGlobalSingleton, globalAsyncSingleton, globalSingleton, peekGlobalSingleton } from './singleton.js';
6
6
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAA;AACnC,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,UAAU,CAAA;AACxB,cAAc,gBAAgB,CAAA","sourcesContent":["export * from './canonical-host.js'\nexport * from './errors.js'\nexport * from './health.js'\nexport * from './rpc.js'\nexport * from './singleton.js'\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAEvD,OAAO,EAAE,YAAY,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA;AAEzI,OAAO,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAEnE,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAEpC,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAA","sourcesContent":["export { canonicalRedirect } from './canonical-host.js'\nexport type { CanonicalRedirectOptions } from './canonical-host.js'\nexport { errorHasCode, infrastructureDiagnostic, infrastructureFailure, InfrastructureError, safeInfrastructureError } from './errors.js'\nexport type { InfrastructureFailure } from './errors.js'\nexport { databaseHealthFailure, healthResponse } from './health.js'\nexport type { HealthResponseOptions } from './health.js'\nexport { createRpc } from './rpc.js'\nexport type { RpcErrorContext, RpcLogger, RpcOptions } from './rpc.js'\nexport { clearGlobalSingleton, globalAsyncSingleton, globalSingleton, peekGlobalSingleton } from './singleton.js'\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ras-stack",
3
- "version": "0.36.2",
3
+ "version": "0.37.0",
4
4
  "description": "Composable full-stack primitives shared across Richard Solomou's applications.",
5
5
  "keywords": [
6
6
  "authentication",
@@ -140,7 +140,7 @@
140
140
  "access": "public"
141
141
  },
142
142
  "scripts": {
143
- "build": "tsc -p tsconfig.build.json",
143
+ "build": "rm -rf dist && tsc -p tsconfig.build.json",
144
144
  "config:check": "tsc -p test/config-consumer/tsconfig.json && tsc -p test/config-consumer/tsconfig-bundler.json && tsc -p test/config-consumer/tsconfig-node-bundler.json && tsc -p test/config-consumer/tsconfig-library.json && oxlint --config config/oxlint/application.json --print-config > /dev/null && oxlint --config config/oxlint/tanstack.json --print-config > /dev/null",
145
145
  "typecheck": "tsc --noEmit",
146
146
  "lint": "oxlint --type-aware --deny-warnings .",
@@ -165,10 +165,11 @@
165
165
  "@tanstack/react-query": "^5.101.4",
166
166
  "@tanstack/react-start": "^1.168.32",
167
167
  "@types/better-sqlite3": "9.6.0",
168
- "@types/node": "^26.0.0",
168
+ "@types/node": "^24.0.0",
169
169
  "@types/nodemailer": "^8.0.1",
170
170
  "@types/react": "19.2.18",
171
171
  "@types/react-test-renderer": "19.1.0",
172
+ "@vitest/coverage-v8": "^4.1.10",
172
173
  "better-sqlite3": "13.0.2",
173
174
  "centrifuge": "5.7.0",
174
175
  "drizzle-orm": "0.45.2",
@@ -177,7 +178,7 @@
177
178
  "oxlint": "^1.74.0",
178
179
  "oxlint-tsgolint": "^7.0.2001",
179
180
  "postgres": "3.4.9",
180
- "posthog-js": "^1.415.0",
181
+ "posthog-js": "^1.414.0",
181
182
  "posthog-node": "^5.48.1",
182
183
  "react": "19.2.8",
183
184
  "react-test-renderer": "19.2.8",