ras-stack 0.30.1 → 0.32.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.
Files changed (44) hide show
  1. package/README.md +64 -534
  2. package/dist/build/cli.d.ts +1 -2
  3. package/dist/build/cli.js +8 -9
  4. package/dist/build/cli.js.map +1 -1
  5. package/dist/cli.d.ts +18 -0
  6. package/dist/cli.js +29 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/conformance/index.d.ts +13 -0
  9. package/dist/conformance/index.js +43 -0
  10. package/dist/conformance/index.js.map +1 -1
  11. package/dist/policy/cli.d.ts +1 -2
  12. package/dist/policy/cli.js +18 -17
  13. package/dist/policy/cli.js.map +1 -1
  14. package/dist/posthog/client.d.ts +13 -0
  15. package/dist/posthog/client.js +31 -0
  16. package/dist/posthog/client.js.map +1 -0
  17. package/dist/posthog/config.d.ts +12 -0
  18. package/dist/posthog/config.js +49 -0
  19. package/dist/posthog/config.js.map +1 -0
  20. package/dist/posthog/coverage.d.ts +18 -0
  21. package/dist/posthog/coverage.js +25 -0
  22. package/dist/posthog/coverage.js.map +1 -0
  23. package/dist/posthog/index.d.ts +6 -0
  24. package/dist/posthog/index.js +4 -0
  25. package/dist/posthog/index.js.map +1 -0
  26. package/dist/posthog/proxy.d.ts +25 -0
  27. package/dist/posthog/proxy.js +25 -0
  28. package/dist/posthog/proxy.js.map +1 -0
  29. package/dist/posthog/react.d.ts +19 -0
  30. package/dist/posthog/react.js +18 -0
  31. package/dist/posthog/react.js.map +1 -0
  32. package/dist/posthog/request.d.ts +14 -0
  33. package/dist/posthog/request.js +22 -0
  34. package/dist/posthog/request.js.map +1 -0
  35. package/dist/posthog/server.d.ts +4 -0
  36. package/dist/posthog/server.js +17 -0
  37. package/dist/posthog/server.js.map +1 -0
  38. package/dist/preview/cli.d.ts +1 -2
  39. package/dist/preview/cli.js +5 -4
  40. package/dist/preview/cli.js.map +1 -1
  41. package/dist/runtime/dev-cli.d.ts +1 -2
  42. package/dist/runtime/dev-cli.js +8 -7
  43. package/dist/runtime/dev-cli.js.map +1 -1
  44. package/package.json +38 -6
package/dist/build/cli.js CHANGED
@@ -1,12 +1,11 @@
1
- #!/usr/bin/env node
2
1
  import { checkServerAssets, loadServerAssetsConfig, syncServerAssets } from './index.js';
3
- const command = process.argv[2];
4
- const configFile = process.argv[3];
5
- if (command !== 'check' && command !== 'sync') {
6
- console.error('usage: ras-stack-assets <check|sync> [config-file]');
7
- process.exitCode = 2;
8
- }
9
- else {
2
+ export async function runAssetsCli(arguments_) {
3
+ const [command, configFile] = arguments_;
4
+ if (command !== 'check' && command !== 'sync') {
5
+ console.error('usage: ras assets <check|sync> [config-file]');
6
+ process.exitCode = 2;
7
+ return;
8
+ }
10
9
  const root = process.cwd();
11
10
  const config = await loadServerAssetsConfig(root, configFile);
12
11
  if (command === 'sync') {
@@ -17,7 +16,7 @@ else {
17
16
  for (const destination of drift)
18
17
  console.error(`server asset drift: ${destination}`);
19
18
  if (drift.length > 0) {
20
- console.error('run ras-stack-assets sync after the production build');
19
+ console.error('run ras assets sync after the production build');
21
20
  process.exitCode = 1;
22
21
  }
23
22
  }
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/build/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAExF,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AAC/B,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AAClC,IAAI,OAAO,KAAK,OAAO,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;IAC9C,OAAO,CAAC,KAAK,CAAC,oDAAoD,CAAC,CAAA;IACnE,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAA;AACtB,CAAC;KAAM,CAAC;IACN,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE,CAAA;IAC1B,MAAM,MAAM,GAAG,MAAM,sBAAsB,CAAC,IAAI,EAAE,UAAU,CAAC,CAAA;IAC7D,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;QACvB,MAAM,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;IACtC,CAAC;SAAM,CAAC;QACN,MAAM,KAAK,GAAG,MAAM,iBAAiB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;QACnD,KAAK,MAAM,WAAW,IAAI,KAAK;YAAE,OAAO,CAAC,KAAK,CAAC,uBAAuB,WAAW,EAAE,CAAC,CAAA;QACpF,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrB,OAAO,CAAC,KAAK,CAAC,sDAAsD,CAAC,CAAA;YACrE,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAA;QACtB,CAAC;IACH,CAAC;AACH,CAAC","sourcesContent":["#!/usr/bin/env node\nimport { checkServerAssets, loadServerAssetsConfig, syncServerAssets } from './index.js'\n\nconst command = process.argv[2]\nconst configFile = process.argv[3]\nif (command !== 'check' && command !== 'sync') {\n console.error('usage: ras-stack-assets <check|sync> [config-file]')\n process.exitCode = 2\n} else {\n const root = process.cwd()\n const config = await loadServerAssetsConfig(root, configFile)\n if (command === 'sync') {\n await syncServerAssets(root, config)\n } else {\n const drift = await checkServerAssets(root, config)\n for (const destination of drift) console.error(`server asset drift: ${destination}`)\n if (drift.length > 0) {\n console.error('run ras-stack-assets sync after the production build')\n process.exitCode = 1\n }\n }\n}\n"]}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/build/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAExF,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,UAAoB;IACrD,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,UAAU,CAAA;IACxC,IAAI,OAAO,KAAK,OAAO,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;QAC9C,OAAO,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAA;QAC7D,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAA;QACpB,OAAM;IACR,CAAC;IAED,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE,CAAA;IAC1B,MAAM,MAAM,GAAG,MAAM,sBAAsB,CAAC,IAAI,EAAE,UAAU,CAAC,CAAA;IAC7D,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;QACvB,MAAM,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;IACtC,CAAC;SAAM,CAAC;QACN,MAAM,KAAK,GAAG,MAAM,iBAAiB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;QACnD,KAAK,MAAM,WAAW,IAAI,KAAK;YAAE,OAAO,CAAC,KAAK,CAAC,uBAAuB,WAAW,EAAE,CAAC,CAAA;QACpF,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrB,OAAO,CAAC,KAAK,CAAC,gDAAgD,CAAC,CAAA;YAC/D,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAA;QACtB,CAAC;IACH,CAAC;AACH,CAAC","sourcesContent":["import { checkServerAssets, loadServerAssetsConfig, syncServerAssets } from './index.js'\n\nexport async function runAssetsCli(arguments_: string[]): Promise<void> {\n const [command, configFile] = arguments_\n if (command !== 'check' && command !== 'sync') {\n console.error('usage: ras assets <check|sync> [config-file]')\n process.exitCode = 2\n return\n }\n\n const root = process.cwd()\n const config = await loadServerAssetsConfig(root, configFile)\n if (command === 'sync') {\n await syncServerAssets(root, config)\n } else {\n const drift = await checkServerAssets(root, config)\n for (const destination of drift) console.error(`server asset drift: ${destination}`)\n if (drift.length > 0) {\n console.error('run ras assets sync after the production build')\n process.exitCode = 1\n }\n }\n}\n"]}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env node
2
+ import { runAssetsCli } from './build/cli.js';
3
+ import { runPolicyCli } from './policy/cli.js';
4
+ import { runPreviewCli } from './preview/cli.js';
5
+ import { runRealtimeCli } from './runtime/dev-cli.js';
6
+ declare const commands: {
7
+ readonly assets: typeof runAssetsCli;
8
+ readonly policy: typeof runPolicyCli;
9
+ readonly preview: typeof runPreviewCli;
10
+ readonly realtime: typeof runRealtimeCli;
11
+ };
12
+ export type RasCommand = keyof typeof commands;
13
+ export declare function parseRasCommand(arguments_: string[]): {
14
+ arguments: string[];
15
+ command: RasCommand;
16
+ } | undefined;
17
+ export declare function runRasCli(arguments_: string[]): Promise<void>;
18
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env node
2
+ import { runAssetsCli } from './build/cli.js';
3
+ import { runPolicyCli } from './policy/cli.js';
4
+ import { runPreviewCli } from './preview/cli.js';
5
+ import { runRealtimeCli } from './runtime/dev-cli.js';
6
+ const commands = {
7
+ assets: runAssetsCli,
8
+ policy: runPolicyCli,
9
+ preview: runPreviewCli,
10
+ realtime: runRealtimeCli,
11
+ };
12
+ export function parseRasCommand(arguments_) {
13
+ const [command, ...argumentsRest] = arguments_;
14
+ if (!(command && Object.hasOwn(commands, command)))
15
+ return undefined;
16
+ return { arguments: argumentsRest, command: command };
17
+ }
18
+ export async function runRasCli(arguments_) {
19
+ const parsed = parseRasCommand(arguments_);
20
+ if (!parsed) {
21
+ console.error('usage: ras <assets|policy|preview|realtime> [arguments]');
22
+ process.exitCode = 2;
23
+ return;
24
+ }
25
+ await commands[parsed.command](parsed.arguments);
26
+ }
27
+ if (import.meta.main)
28
+ await runRasCli(process.argv.slice(2));
29
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAErD,MAAM,QAAQ,GAAG;IACf,MAAM,EAAE,YAAY;IACpB,MAAM,EAAE,YAAY;IACpB,OAAO,EAAE,aAAa;IACtB,QAAQ,EAAE,cAAc;CAChB,CAAA;AAIV,MAAM,UAAU,eAAe,CAAC,UAAoB;IAClD,MAAM,CAAC,OAAO,EAAE,GAAG,aAAa,CAAC,GAAG,UAAU,CAAA;IAC9C,IAAI,CAAC,CAAC,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAAE,OAAO,SAAS,CAAA;IACpE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,OAAO,EAAE,OAAqB,EAAE,CAAA;AACrE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,UAAoB;IAClD,MAAM,MAAM,GAAG,eAAe,CAAC,UAAU,CAAC,CAAA;IAC1C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,CAAC,KAAK,CAAC,yDAAyD,CAAC,CAAA;QACxE,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAA;QACpB,OAAM;IACR,CAAC;IACD,MAAM,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;AAClD,CAAC;AAED,IAAI,OAAO,IAAI,CAAC,IAAI;IAAE,MAAM,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA","sourcesContent":["#!/usr/bin/env node\nimport { runAssetsCli } from './build/cli.js'\nimport { runPolicyCli } from './policy/cli.js'\nimport { runPreviewCli } from './preview/cli.js'\nimport { runRealtimeCli } from './runtime/dev-cli.js'\n\nconst commands = {\n assets: runAssetsCli,\n policy: runPolicyCli,\n preview: runPreviewCli,\n realtime: runRealtimeCli,\n} as const\n\nexport type RasCommand = keyof typeof commands\n\nexport function parseRasCommand(arguments_: string[]): { arguments: string[]; command: RasCommand } | undefined {\n const [command, ...argumentsRest] = arguments_\n if (!(command && Object.hasOwn(commands, command))) return undefined\n return { arguments: argumentsRest, command: command as RasCommand }\n}\n\nexport async function runRasCli(arguments_: string[]): Promise<void> {\n const parsed = parseRasCommand(arguments_)\n if (!parsed) {\n console.error('usage: ras <assets|policy|preview|realtime> [arguments]')\n process.exitCode = 2\n return\n }\n await commands[parsed.command](parsed.arguments)\n}\n\nif (import.meta.main) await runRasCli(process.argv.slice(2))\n"]}
@@ -23,3 +23,16 @@ export type DatabaseTargetResolver = (options: {
23
23
  sqliteFile: string;
24
24
  }) => DatabaseTarget;
25
25
  export declare function assertDatabaseTargetConformance(resolve: DatabaseTargetResolver): void;
26
+ export declare function assertPostHogBrowserConformance(options: Record<string, unknown>): void;
27
+ type PostHogContextParser = (request: Request, options?: {
28
+ authenticatedDistinctId?: string;
29
+ allowAnonymousDistinctId?: boolean;
30
+ }) => {
31
+ distinctId?: string;
32
+ sessionId?: string;
33
+ properties: {
34
+ $session_id?: string;
35
+ };
36
+ };
37
+ export declare function assertPostHogRequestConformance(parse: PostHogContextParser): void;
38
+ export {};
@@ -94,6 +94,49 @@ export function assertDatabaseTargetConformance(resolve) {
94
94
  }
95
95
  throw new ConformanceError('invalid database target', 'expected a non-PostgreSQL URL to be rejected');
96
96
  }
97
+ export function assertPostHogBrowserConformance(options) {
98
+ if (typeof options.api_host !== 'string' || !options.api_host.trim()) {
99
+ throw new ConformanceError('PostHog browser initialization', 'api_host must be configured');
100
+ }
101
+ if (typeof options.ui_host !== 'string' || !options.ui_host.trim()) {
102
+ throw new ConformanceError('PostHog browser initialization', 'ui_host must be configured');
103
+ }
104
+ if (typeof options.defaults !== 'string' || !options.defaults.trim()) {
105
+ throw new ConformanceError('PostHog browser initialization', 'SDK defaults must be pinned');
106
+ }
107
+ if (!options.capture_exceptions) {
108
+ throw new ConformanceError('PostHog browser initialization', 'exception autocapture must be enabled');
109
+ }
110
+ if (options.capture_pageview !== 'history_change') {
111
+ throw new ConformanceError('PostHog browser initialization', 'SPA pageviews must follow history changes');
112
+ }
113
+ if (options.person_profiles !== 'identified_only') {
114
+ throw new ConformanceError('PostHog browser initialization', 'person profiles must be limited to identified users');
115
+ }
116
+ const recording = options.session_recording;
117
+ if (!recording || typeof recording !== 'object' || !('maskAllInputs' in recording) || recording.maskAllInputs !== true) {
118
+ throw new ConformanceError('PostHog browser initialization', 'session replay must mask all inputs by default');
119
+ }
120
+ }
121
+ export function assertPostHogRequestConformance(parse) {
122
+ const matched = parse(postHogRequest('person-123', 'session-456'), { authenticatedDistinctId: 'person-123' });
123
+ if (matched.distinctId !== 'person-123' || matched.sessionId !== 'session-456' || matched.properties.$session_id !== 'session-456') {
124
+ throw new ConformanceError('authenticated PostHog request', 'expected verified identity and session propagation');
125
+ }
126
+ const spoofed = parse(postHogRequest('attacker', 'session-456'), { authenticatedDistinctId: 'person-123' });
127
+ if (spoofed.distinctId !== undefined) {
128
+ throw new ConformanceError('spoofed PostHog request', 'unverified distinct id was trusted');
129
+ }
130
+ const malformed = parse(postHogRequest('person-123', 'x'.repeat(129)), { authenticatedDistinctId: 'person-123' });
131
+ if (malformed.sessionId !== undefined || malformed.properties.$session_id !== undefined) {
132
+ throw new ConformanceError('malformed PostHog request', 'unbounded session id was propagated');
133
+ }
134
+ }
135
+ function postHogRequest(distinctId, sessionId) {
136
+ return new Request('https://app.example/action', {
137
+ headers: { 'x-posthog-distinct-id': distinctId, 'x-posthog-session-id': sessionId },
138
+ });
139
+ }
97
140
  async function accepted(scenario, work) {
98
141
  try {
99
142
  await work();
@@ -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\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 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,2 +1 @@
1
- #!/usr/bin/env node
2
- export {};
1
+ export declare function runPolicyCli(arguments_: string[]): Promise<void>;
@@ -1,23 +1,24 @@
1
- #!/usr/bin/env node
2
1
  import { readFile } from 'node:fs/promises';
3
2
  import { resolve } from 'node:path';
4
3
  import { fleetConfig, fleetMarkdown, inspectFleet } from './fleet.js';
5
4
  import { checkRepositoryPolicy, syncAdoptionPolicy, syncRepositoryPolicy } from './index.js';
6
- const command = process.argv[2];
7
- if (command !== 'check' && command !== 'sync' && command !== 'fleet') {
8
- console.error('usage: ras-stack-policy <check|sync|fleet> [adoption]');
9
- process.exitCode = 2;
10
- }
11
- else if (command === 'fleet') {
12
- const path = resolve(process.argv[3] ?? 'ras-stack.fleet.json');
13
- const config = fleetConfig(JSON.parse(await readFile(path, 'utf8')));
14
- const results = await inspectFleet(config);
15
- process.stdout.write(fleetMarkdown(results));
16
- if (results.some((result) => result.drift.length > 0))
17
- process.exitCode = 1;
18
- }
19
- else {
20
- const adoption = process.argv[3] === 'adoption';
5
+ export async function runPolicyCli(arguments_) {
6
+ const [command, argument] = arguments_;
7
+ if (command !== 'check' && command !== 'sync' && command !== 'fleet') {
8
+ console.error('usage: ras policy <check|sync|fleet> [adoption]');
9
+ process.exitCode = 2;
10
+ return;
11
+ }
12
+ if (command === 'fleet') {
13
+ const path = resolve(argument ?? 'ras-stack.fleet.json');
14
+ const config = fleetConfig(JSON.parse(await readFile(path, 'utf8')));
15
+ const results = await inspectFleet(config);
16
+ process.stdout.write(fleetMarkdown(results));
17
+ if (results.some((result) => result.drift.length > 0))
18
+ process.exitCode = 1;
19
+ return;
20
+ }
21
+ const adoption = argument === 'adoption';
21
22
  const changed = adoption
22
23
  ? await syncAdoptionPolicy(process.cwd(), command === 'check' ? 'check' : 'write')
23
24
  : command === 'check'
@@ -26,7 +27,7 @@ else {
26
27
  if (command === 'check' && changed.length > 0) {
27
28
  for (const message of changed)
28
29
  console.error(message);
29
- console.error(`run ras-stack-policy sync${adoption ? ' adoption' : ''} and commit the result`);
30
+ console.error(`run ras policy sync${adoption ? ' adoption' : ''} and commit the result`);
30
31
  process.exitCode = 1;
31
32
  }
32
33
  else if (command === 'sync') {
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/policy/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AACrE,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAE5F,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AAC/B,IAAI,OAAO,KAAK,OAAO,IAAI,OAAO,KAAK,MAAM,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;IACrE,OAAO,CAAC,KAAK,CAAC,uDAAuD,CAAC,CAAA;IACtE,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAA;AACtB,CAAC;KAAM,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;IAC/B,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,sBAAsB,CAAC,CAAA;IAC/D,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAA;IACpE,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,CAAA;IAC1C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAA;IAC5C,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;QAAE,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAA;AAC7E,CAAC;KAAM,CAAC;IACN,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,UAAU,CAAA;IAC/C,MAAM,OAAO,GAAG,QAAQ;QACtB,CAAC,CAAC,MAAM,kBAAkB,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;QAClF,CAAC,CAAC,OAAO,KAAK,OAAO;YACnB,CAAC,CAAC,MAAM,qBAAqB,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;YAC5C,CAAC,CAAC,MAAM,oBAAoB,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,CAAA;IACxD,IAAI,OAAO,KAAK,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9C,KAAK,MAAM,OAAO,IAAI,OAAO;YAAE,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;QACrD,OAAO,CAAC,KAAK,CAAC,4BAA4B,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,wBAAwB,CAAC,CAAA;QAC9F,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAA;IACtB,CAAC;SAAM,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;QAC9B,KAAK,MAAM,IAAI,IAAI,OAAO;YAAE,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC,CAAA;IAC5D,CAAC;AACH,CAAC","sourcesContent":["#!/usr/bin/env node\nimport { readFile } from 'node:fs/promises'\nimport { resolve } from 'node:path'\nimport { fleetConfig, fleetMarkdown, inspectFleet } from './fleet.js'\nimport { checkRepositoryPolicy, syncAdoptionPolicy, syncRepositoryPolicy } from './index.js'\n\nconst command = process.argv[2]\nif (command !== 'check' && command !== 'sync' && command !== 'fleet') {\n console.error('usage: ras-stack-policy <check|sync|fleet> [adoption]')\n process.exitCode = 2\n} else if (command === 'fleet') {\n const path = resolve(process.argv[3] ?? 'ras-stack.fleet.json')\n const config = fleetConfig(JSON.parse(await readFile(path, 'utf8')))\n const results = await inspectFleet(config)\n process.stdout.write(fleetMarkdown(results))\n if (results.some((result) => result.drift.length > 0)) process.exitCode = 1\n} else {\n const adoption = process.argv[3] === 'adoption'\n const changed = adoption\n ? await syncAdoptionPolicy(process.cwd(), command === 'check' ? 'check' : 'write')\n : command === 'check'\n ? await checkRepositoryPolicy(process.cwd())\n : await syncRepositoryPolicy(process.cwd(), 'write')\n if (command === 'check' && changed.length > 0) {\n for (const message of changed) console.error(message)\n console.error(`run ras-stack-policy sync${adoption ? ' adoption' : ''} and commit the result`)\n process.exitCode = 1\n } else if (command === 'sync') {\n for (const path of changed) console.log(`updated ${path}`)\n }\n}\n"]}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/policy/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AACrE,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAE5F,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,UAAoB;IACrD,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,GAAG,UAAU,CAAA;IACtC,IAAI,OAAO,KAAK,OAAO,IAAI,OAAO,KAAK,MAAM,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;QACrE,OAAO,CAAC,KAAK,CAAC,iDAAiD,CAAC,CAAA;QAChE,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAA;QACpB,OAAM;IACR,CAAC;IAED,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;QACxB,MAAM,IAAI,GAAG,OAAO,CAAC,QAAQ,IAAI,sBAAsB,CAAC,CAAA;QACxD,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAA;QACpE,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,CAAA;QAC1C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAA;QAC5C,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;YAAE,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAA;QAC3E,OAAM;IACR,CAAC;IAED,MAAM,QAAQ,GAAG,QAAQ,KAAK,UAAU,CAAA;IACxC,MAAM,OAAO,GAAG,QAAQ;QACtB,CAAC,CAAC,MAAM,kBAAkB,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;QAClF,CAAC,CAAC,OAAO,KAAK,OAAO;YACnB,CAAC,CAAC,MAAM,qBAAqB,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;YAC5C,CAAC,CAAC,MAAM,oBAAoB,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,CAAA;IACxD,IAAI,OAAO,KAAK,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9C,KAAK,MAAM,OAAO,IAAI,OAAO;YAAE,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;QACrD,OAAO,CAAC,KAAK,CAAC,sBAAsB,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,wBAAwB,CAAC,CAAA;QACxF,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAA;IACtB,CAAC;SAAM,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;QAC9B,KAAK,MAAM,IAAI,IAAI,OAAO;YAAE,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC,CAAA;IAC5D,CAAC;AACH,CAAC","sourcesContent":["import { readFile } from 'node:fs/promises'\nimport { resolve } from 'node:path'\nimport { fleetConfig, fleetMarkdown, inspectFleet } from './fleet.js'\nimport { checkRepositoryPolicy, syncAdoptionPolicy, syncRepositoryPolicy } from './index.js'\n\nexport async function runPolicyCli(arguments_: string[]): Promise<void> {\n const [command, argument] = arguments_\n if (command !== 'check' && command !== 'sync' && command !== 'fleet') {\n console.error('usage: ras policy <check|sync|fleet> [adoption]')\n process.exitCode = 2\n return\n }\n\n if (command === 'fleet') {\n const path = resolve(argument ?? 'ras-stack.fleet.json')\n const config = fleetConfig(JSON.parse(await readFile(path, 'utf8')))\n const results = await inspectFleet(config)\n process.stdout.write(fleetMarkdown(results))\n if (results.some((result) => result.drift.length > 0)) process.exitCode = 1\n return\n }\n\n const adoption = argument === 'adoption'\n const changed = adoption\n ? await syncAdoptionPolicy(process.cwd(), command === 'check' ? 'check' : 'write')\n : command === 'check'\n ? await checkRepositoryPolicy(process.cwd())\n : await syncRepositoryPolicy(process.cwd(), 'write')\n if (command === 'check' && changed.length > 0) {\n for (const message of changed) console.error(message)\n console.error(`run ras policy sync${adoption ? ' adoption' : ''} and commit the result`)\n process.exitCode = 1\n } else if (command === 'sync') {\n for (const path of changed) console.log(`updated ${path}`)\n }\n}\n"]}
@@ -0,0 +1,13 @@
1
+ import type { PostHogConfig } from 'posthog-js';
2
+ export { POSTHOG_DISTINCT_ID_HEADER, POSTHOG_SESSION_ID_HEADER } from './request.js';
3
+ export declare const POSTHOG_BROWSER_DEFAULTS = "2026-05-30";
4
+ export declare function postHogBrowserOptions(input: {
5
+ apiHost: string;
6
+ uiHost: string;
7
+ tracingHostnames?: string[];
8
+ options?: Partial<PostHogConfig>;
9
+ }): Partial<PostHogConfig>;
10
+ export declare function postHogBrowserHeaders(client: {
11
+ get_distinct_id(): string;
12
+ get_session_id(): string | undefined;
13
+ }): Record<string, string>;
@@ -0,0 +1,31 @@
1
+ import { POSTHOG_DISTINCT_ID_HEADER, POSTHOG_SESSION_ID_HEADER, postHogIdentifier } from './request.js';
2
+ export { POSTHOG_DISTINCT_ID_HEADER, POSTHOG_SESSION_ID_HEADER } from './request.js';
3
+ export const POSTHOG_BROWSER_DEFAULTS = '2026-05-30';
4
+ export function postHogBrowserOptions(input) {
5
+ return {
6
+ api_host: required(input.apiHost, 'apiHost'),
7
+ ui_host: required(input.uiHost, 'uiHost'),
8
+ defaults: POSTHOG_BROWSER_DEFAULTS,
9
+ capture_exceptions: true,
10
+ capture_pageview: 'history_change',
11
+ person_profiles: 'identified_only',
12
+ session_recording: { maskAllInputs: true, blockSelector: '.ph-no-capture' },
13
+ ...(input.tracingHostnames ? { tracing_headers: input.tracingHostnames } : {}),
14
+ ...input.options,
15
+ };
16
+ }
17
+ export function postHogBrowserHeaders(client) {
18
+ const distinctId = postHogIdentifier(client.get_distinct_id());
19
+ const sessionId = postHogIdentifier(client.get_session_id());
20
+ return {
21
+ ...(distinctId ? { [POSTHOG_DISTINCT_ID_HEADER]: distinctId } : {}),
22
+ ...(sessionId ? { [POSTHOG_SESSION_ID_HEADER]: sessionId } : {}),
23
+ };
24
+ }
25
+ function required(value, name) {
26
+ const normalized = value.trim();
27
+ if (!normalized)
28
+ throw new Error(`${name} is required`);
29
+ return normalized;
30
+ }
31
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/posthog/client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,0BAA0B,EAAE,yBAAyB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAEvG,OAAO,EAAE,0BAA0B,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAA;AACpF,MAAM,CAAC,MAAM,wBAAwB,GAAG,YAAY,CAAA;AAEpD,MAAM,UAAU,qBAAqB,CAAC,KAKrC;IACC,OAAO;QACL,QAAQ,EAAE,QAAQ,CAAC,KAAK,CAAC,OAAO,EAAE,SAAS,CAAC;QAC5C,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC;QACzC,QAAQ,EAAE,wBAAwB;QAClC,kBAAkB,EAAE,IAAI;QACxB,gBAAgB,EAAE,gBAAgB;QAClC,eAAe,EAAE,iBAAiB;QAClC,iBAAiB,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,aAAa,EAAE,gBAAgB,EAAE;QAC3E,GAAG,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,KAAK,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9E,GAAG,KAAK,CAAC,OAAO;KACjB,CAAA;AACH,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,MAA2E;IAC/G,MAAM,UAAU,GAAG,iBAAiB,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC,CAAA;IAC9D,MAAM,SAAS,GAAG,iBAAiB,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC,CAAA;IAC5D,OAAO;QACL,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,0BAA0B,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACnE,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,yBAAyB,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACjE,CAAA;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,KAAa,EAAE,IAAY;IAC3C,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,EAAE,CAAA;IAC/B,IAAI,CAAC,UAAU;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,cAAc,CAAC,CAAA;IACvD,OAAO,UAAU,CAAA;AACnB,CAAC","sourcesContent":["import type { PostHogConfig } from 'posthog-js'\nimport { POSTHOG_DISTINCT_ID_HEADER, POSTHOG_SESSION_ID_HEADER, postHogIdentifier } from './request.js'\n\nexport { POSTHOG_DISTINCT_ID_HEADER, POSTHOG_SESSION_ID_HEADER } from './request.js'\nexport const POSTHOG_BROWSER_DEFAULTS = '2026-05-30'\n\nexport function postHogBrowserOptions(input: {\n apiHost: string\n uiHost: string\n tracingHostnames?: string[]\n options?: Partial<PostHogConfig>\n}): Partial<PostHogConfig> {\n return {\n api_host: required(input.apiHost, 'apiHost'),\n ui_host: required(input.uiHost, 'uiHost'),\n defaults: POSTHOG_BROWSER_DEFAULTS,\n capture_exceptions: true,\n capture_pageview: 'history_change',\n person_profiles: 'identified_only',\n session_recording: { maskAllInputs: true, blockSelector: '.ph-no-capture' },\n ...(input.tracingHostnames ? { tracing_headers: input.tracingHostnames } : {}),\n ...input.options,\n }\n}\n\nexport function postHogBrowserHeaders(client: { get_distinct_id(): string; get_session_id(): string | undefined }): Record<string, string> {\n const distinctId = postHogIdentifier(client.get_distinct_id())\n const sessionId = postHogIdentifier(client.get_session_id())\n return {\n ...(distinctId ? { [POSTHOG_DISTINCT_ID_HEADER]: distinctId } : {}),\n ...(sessionId ? { [POSTHOG_SESSION_ID_HEADER]: sessionId } : {}),\n }\n}\n\nfunction required(value: string, name: string) {\n const normalized = value.trim()\n if (!normalized) throw new Error(`${name} is required`)\n return normalized\n}\n"]}
@@ -0,0 +1,12 @@
1
+ export type PostHogEnvironment = {
2
+ projectToken: string;
3
+ host: string;
4
+ uiHost: string;
5
+ assetsHost: string;
6
+ };
7
+ export declare function postHogEnvironment(input: {
8
+ projectToken?: string;
9
+ host?: string;
10
+ assetsHost?: string;
11
+ }): PostHogEnvironment | undefined;
12
+ export declare function postHogHttpUrl(value: string, name: string): string;
@@ -0,0 +1,49 @@
1
+ export function postHogEnvironment(input) {
2
+ const projectToken = input.projectToken?.trim();
3
+ const host = input.host?.trim();
4
+ if (!projectToken && !host && !input.assetsHost)
5
+ return undefined;
6
+ if (!projectToken)
7
+ throw new Error('PostHog projectToken is required when a host is configured');
8
+ const parsed = httpUrl(host || 'https://us.i.posthog.com', 'PostHog host');
9
+ return {
10
+ projectToken,
11
+ host: normalizedUrl(parsed),
12
+ uiHost: postHogUiHost(parsed),
13
+ assetsHost: input.assetsHost ? postHogHttpUrl(input.assetsHost, 'PostHog assetsHost') : postHogAssetsHost(parsed),
14
+ };
15
+ }
16
+ export function postHogHttpUrl(value, name) {
17
+ return normalizedUrl(httpUrl(value, name));
18
+ }
19
+ function httpUrl(value, name) {
20
+ let parsed;
21
+ try {
22
+ parsed = new URL(value);
23
+ }
24
+ catch {
25
+ throw new Error(`${name} must be an HTTP URL without credentials, query, or fragment`);
26
+ }
27
+ if (!['http:', 'https:'].includes(parsed.protocol) || parsed.username || parsed.password || parsed.search || parsed.hash) {
28
+ throw new Error(`${name} must be an HTTP URL without credentials, query, or fragment`);
29
+ }
30
+ return parsed;
31
+ }
32
+ function normalizedUrl(url) {
33
+ return url.toString().replace(/\/$/, '');
34
+ }
35
+ function postHogUiHost(host) {
36
+ if (host.hostname === 'us.i.posthog.com')
37
+ return 'https://us.posthog.com';
38
+ if (host.hostname === 'eu.i.posthog.com')
39
+ return 'https://eu.posthog.com';
40
+ return host.origin;
41
+ }
42
+ function postHogAssetsHost(host) {
43
+ if (host.hostname === 'us.i.posthog.com')
44
+ return 'https://us-assets.i.posthog.com';
45
+ if (host.hostname === 'eu.i.posthog.com')
46
+ return 'https://eu-assets.i.posthog.com';
47
+ return normalizedUrl(host);
48
+ }
49
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/posthog/config.ts"],"names":[],"mappings":"AAOA,MAAM,UAAU,kBAAkB,CAAC,KAAoE;IACrG,MAAM,YAAY,GAAG,KAAK,CAAC,YAAY,EAAE,IAAI,EAAE,CAAA;IAC/C,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,CAAA;IAC/B,IAAI,CAAC,YAAY,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,UAAU;QAAE,OAAO,SAAS,CAAA;IACjE,IAAI,CAAC,YAAY;QAAE,MAAM,IAAI,KAAK,CAAC,4DAA4D,CAAC,CAAA;IAChG,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,IAAI,0BAA0B,EAAE,cAAc,CAAC,CAAA;IAC1E,OAAO;QACL,YAAY;QACZ,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC;QAC3B,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC;QAC7B,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,UAAU,EAAE,oBAAoB,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,MAAM,CAAC;KAClH,CAAA;AACH,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAa,EAAE,IAAY;IACxD,OAAO,aAAa,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAA;AAC5C,CAAC;AAED,SAAS,OAAO,CAAC,KAAa,EAAE,IAAY;IAC1C,IAAI,MAAW,CAAA;IACf,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAA;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,8DAA8D,CAAC,CAAA;IACxF,CAAC;IACD,IAAI,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;QACzH,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,8DAA8D,CAAC,CAAA;IACxF,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED,SAAS,aAAa,CAAC,GAAQ;IAC7B,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;AAC1C,CAAC;AAED,SAAS,aAAa,CAAC,IAAS;IAC9B,IAAI,IAAI,CAAC,QAAQ,KAAK,kBAAkB;QAAE,OAAO,wBAAwB,CAAA;IACzE,IAAI,IAAI,CAAC,QAAQ,KAAK,kBAAkB;QAAE,OAAO,wBAAwB,CAAA;IACzE,OAAO,IAAI,CAAC,MAAM,CAAA;AACpB,CAAC;AAED,SAAS,iBAAiB,CAAC,IAAS;IAClC,IAAI,IAAI,CAAC,QAAQ,KAAK,kBAAkB;QAAE,OAAO,iCAAiC,CAAA;IAClF,IAAI,IAAI,CAAC,QAAQ,KAAK,kBAAkB;QAAE,OAAO,iCAAiC,CAAA;IAClF,OAAO,aAAa,CAAC,IAAI,CAAC,CAAA;AAC5B,CAAC","sourcesContent":["export type PostHogEnvironment = {\n projectToken: string\n host: string\n uiHost: string\n assetsHost: string\n}\n\nexport function postHogEnvironment(input: { projectToken?: string; host?: string; assetsHost?: string }): PostHogEnvironment | undefined {\n const projectToken = input.projectToken?.trim()\n const host = input.host?.trim()\n if (!projectToken && !host && !input.assetsHost) return undefined\n if (!projectToken) throw new Error('PostHog projectToken is required when a host is configured')\n const parsed = httpUrl(host || 'https://us.i.posthog.com', 'PostHog host')\n return {\n projectToken,\n host: normalizedUrl(parsed),\n uiHost: postHogUiHost(parsed),\n assetsHost: input.assetsHost ? postHogHttpUrl(input.assetsHost, 'PostHog assetsHost') : postHogAssetsHost(parsed),\n }\n}\n\nexport function postHogHttpUrl(value: string, name: string) {\n return normalizedUrl(httpUrl(value, name))\n}\n\nfunction httpUrl(value: string, name: string) {\n let parsed: URL\n try {\n parsed = new URL(value)\n } catch {\n throw new Error(`${name} must be an HTTP URL without credentials, query, or fragment`)\n }\n if (!['http:', 'https:'].includes(parsed.protocol) || parsed.username || parsed.password || parsed.search || parsed.hash) {\n throw new Error(`${name} must be an HTTP URL without credentials, query, or fragment`)\n }\n return parsed\n}\n\nfunction normalizedUrl(url: URL) {\n return url.toString().replace(/\\/$/, '')\n}\n\nfunction postHogUiHost(host: URL) {\n if (host.hostname === 'us.i.posthog.com') return 'https://us.posthog.com'\n if (host.hostname === 'eu.i.posthog.com') return 'https://eu.posthog.com'\n return host.origin\n}\n\nfunction postHogAssetsHost(host: URL) {\n if (host.hostname === 'us.i.posthog.com') return 'https://us-assets.i.posthog.com'\n if (host.hostname === 'eu.i.posthog.com') return 'https://eu-assets.i.posthog.com'\n return normalizedUrl(host)\n}\n"]}
@@ -0,0 +1,18 @@
1
+ export type PostHogCoverageDecision = true | {
2
+ disabled: string;
3
+ };
4
+ export type PostHogCoverage = {
5
+ browser: {
6
+ analytics: PostHogCoverageDecision;
7
+ errorTracking: PostHogCoverageDecision;
8
+ sessionReplay: PostHogCoverageDecision;
9
+ featureFlags: PostHogCoverageDecision;
10
+ };
11
+ server: {
12
+ analytics: PostHogCoverageDecision;
13
+ errorTracking: PostHogCoverageDecision;
14
+ logs: PostHogCoverageDecision;
15
+ };
16
+ sourceMaps: PostHogCoverageDecision;
17
+ };
18
+ export declare function definePostHogCoverage(coverage: PostHogCoverage): PostHogCoverage;
@@ -0,0 +1,25 @@
1
+ export function definePostHogCoverage(coverage) {
2
+ for (const [area, decision] of coverageDecisions(coverage)) {
3
+ if (decision === true)
4
+ continue;
5
+ if (!decision || typeof decision !== 'object' || typeof decision.disabled !== 'string') {
6
+ throw new Error(`${area} must be enabled or include a disabled reason`);
7
+ }
8
+ if (!decision.disabled.trim())
9
+ throw new Error(`${area} disabled reason must not be empty`);
10
+ }
11
+ return coverage;
12
+ }
13
+ function coverageDecisions(coverage) {
14
+ return [
15
+ ['browser.analytics', coverage.browser.analytics],
16
+ ['browser.errorTracking', coverage.browser.errorTracking],
17
+ ['browser.sessionReplay', coverage.browser.sessionReplay],
18
+ ['browser.featureFlags', coverage.browser.featureFlags],
19
+ ['server.analytics', coverage.server.analytics],
20
+ ['server.errorTracking', coverage.server.errorTracking],
21
+ ['server.logs', coverage.server.logs],
22
+ ['sourceMaps', coverage.sourceMaps],
23
+ ];
24
+ }
25
+ //# sourceMappingURL=coverage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coverage.js","sourceRoot":"","sources":["../../src/posthog/coverage.ts"],"names":[],"mappings":"AAiBA,MAAM,UAAU,qBAAqB,CAAC,QAAyB;IAC7D,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,iBAAiB,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3D,IAAI,QAAQ,KAAK,IAAI;YAAE,SAAQ;QAC/B,IAAI,CAAC,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,OAAO,QAAQ,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YACvF,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,+CAA+C,CAAC,CAAA;QACzE,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,oCAAoC,CAAC,CAAA;IAC7F,CAAC;IACD,OAAO,QAAQ,CAAA;AACjB,CAAC;AAED,SAAS,iBAAiB,CAAC,QAAyB;IAClD,OAAO;QACL,CAAC,mBAAmB,EAAE,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC;QACjD,CAAC,uBAAuB,EAAE,QAAQ,CAAC,OAAO,CAAC,aAAa,CAAC;QACzD,CAAC,uBAAuB,EAAE,QAAQ,CAAC,OAAO,CAAC,aAAa,CAAC;QACzD,CAAC,sBAAsB,EAAE,QAAQ,CAAC,OAAO,CAAC,YAAY,CAAC;QACvD,CAAC,kBAAkB,EAAE,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC;QAC/C,CAAC,sBAAsB,EAAE,QAAQ,CAAC,MAAM,CAAC,aAAa,CAAC;QACvD,CAAC,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC;QACrC,CAAC,YAAY,EAAE,QAAQ,CAAC,UAAU,CAAC;KACpC,CAAA;AACH,CAAC","sourcesContent":["export type PostHogCoverageDecision = true | { disabled: string }\n\nexport type PostHogCoverage = {\n browser: {\n analytics: PostHogCoverageDecision\n errorTracking: PostHogCoverageDecision\n sessionReplay: PostHogCoverageDecision\n featureFlags: PostHogCoverageDecision\n }\n server: {\n analytics: PostHogCoverageDecision\n errorTracking: PostHogCoverageDecision\n logs: PostHogCoverageDecision\n }\n sourceMaps: PostHogCoverageDecision\n}\n\nexport function definePostHogCoverage(coverage: PostHogCoverage) {\n for (const [area, decision] of coverageDecisions(coverage)) {\n if (decision === true) continue\n if (!decision || typeof decision !== 'object' || typeof decision.disabled !== 'string') {\n throw new Error(`${area} must be enabled or include a disabled reason`)\n }\n if (!decision.disabled.trim()) throw new Error(`${area} disabled reason must not be empty`)\n }\n return coverage\n}\n\nfunction coverageDecisions(coverage: PostHogCoverage): Array<[string, PostHogCoverageDecision]> {\n return [\n ['browser.analytics', coverage.browser.analytics],\n ['browser.errorTracking', coverage.browser.errorTracking],\n ['browser.sessionReplay', coverage.browser.sessionReplay],\n ['browser.featureFlags', coverage.browser.featureFlags],\n ['server.analytics', coverage.server.analytics],\n ['server.errorTracking', coverage.server.errorTracking],\n ['server.logs', coverage.server.logs],\n ['sourceMaps', coverage.sourceMaps],\n ]\n}\n"]}
@@ -0,0 +1,6 @@
1
+ export { postHogEnvironment, postHogHttpUrl } from './config.js';
2
+ export type { PostHogEnvironment } from './config.js';
3
+ export { definePostHogCoverage } from './coverage.js';
4
+ export type { PostHogCoverage, PostHogCoverageDecision } from './coverage.js';
5
+ export { POSTHOG_DISTINCT_ID_HEADER, POSTHOG_SESSION_ID_HEADER, postHogRequestContext } from './request.js';
6
+ export type { PostHogRequestContext } from './request.js';
@@ -0,0 +1,4 @@
1
+ export { postHogEnvironment, postHogHttpUrl } from './config.js';
2
+ export { definePostHogCoverage } from './coverage.js';
3
+ export { POSTHOG_DISTINCT_ID_HEADER, POSTHOG_SESSION_ID_HEADER, postHogRequestContext } from './request.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/posthog/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAEhE,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AAErD,OAAO,EAAE,0BAA0B,EAAE,yBAAyB,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA","sourcesContent":["export { postHogEnvironment, postHogHttpUrl } from './config.js'\nexport type { PostHogEnvironment } from './config.js'\nexport { definePostHogCoverage } from './coverage.js'\nexport type { PostHogCoverage, PostHogCoverageDecision } from './coverage.js'\nexport { POSTHOG_DISTINCT_ID_HEADER, POSTHOG_SESSION_ID_HEADER, postHogRequestContext } from './request.js'\nexport type { PostHogRequestContext } from './request.js'\n"]}
@@ -0,0 +1,25 @@
1
+ import type { PostHogEnvironment } from './config.js';
2
+ type ViteProxyTarget = {
3
+ target: string;
4
+ changeOrigin: true;
5
+ rewrite: (path: string) => string;
6
+ };
7
+ export declare function postHogIngestProxy(input: Pick<PostHogEnvironment, 'host' | 'assetsHost'>): {
8
+ vite: {
9
+ '/ingest/static': ViteProxyTarget;
10
+ '/ingest/array': ViteProxyTarget;
11
+ '/ingest': ViteProxyTarget;
12
+ };
13
+ nitro: {
14
+ '/ingest/static/**': {
15
+ proxy: string;
16
+ };
17
+ '/ingest/array/**': {
18
+ proxy: string;
19
+ };
20
+ '/ingest/**': {
21
+ proxy: string;
22
+ };
23
+ };
24
+ };
25
+ export {};
@@ -0,0 +1,25 @@
1
+ import { postHogHttpUrl } from './config.js';
2
+ function viteTarget(host) {
3
+ return {
4
+ target: host,
5
+ changeOrigin: true,
6
+ rewrite: (path) => path.replace(/^\/ingest/, ''),
7
+ };
8
+ }
9
+ export function postHogIngestProxy(input) {
10
+ const ingestionHost = postHogHttpUrl(input.host, 'host');
11
+ const assetsHost = postHogHttpUrl(input.assetsHost, 'assetsHost');
12
+ return {
13
+ vite: {
14
+ '/ingest/static': viteTarget(assetsHost),
15
+ '/ingest/array': viteTarget(assetsHost),
16
+ '/ingest': viteTarget(ingestionHost),
17
+ },
18
+ nitro: {
19
+ '/ingest/static/**': { proxy: `${assetsHost}/static/**` },
20
+ '/ingest/array/**': { proxy: `${assetsHost}/array/**` },
21
+ '/ingest/**': { proxy: `${ingestionHost}/**` },
22
+ },
23
+ };
24
+ }
25
+ //# sourceMappingURL=proxy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy.js","sourceRoot":"","sources":["../../src/posthog/proxy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAS5C,SAAS,UAAU,CAAC,IAAY;IAC9B,OAAO;QACL,MAAM,EAAE,IAAI;QACZ,YAAY,EAAE,IAAI;QAClB,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC;KACjD,CAAA;AACH,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,KAAsD;IACvF,MAAM,aAAa,GAAG,cAAc,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;IACxD,MAAM,UAAU,GAAG,cAAc,CAAC,KAAK,CAAC,UAAU,EAAE,YAAY,CAAC,CAAA;IACjE,OAAO;QACL,IAAI,EAAE;YACJ,gBAAgB,EAAE,UAAU,CAAC,UAAU,CAAC;YACxC,eAAe,EAAE,UAAU,CAAC,UAAU,CAAC;YACvC,SAAS,EAAE,UAAU,CAAC,aAAa,CAAC;SACrC;QACD,KAAK,EAAE;YACL,mBAAmB,EAAE,EAAE,KAAK,EAAE,GAAG,UAAU,YAAY,EAAE;YACzD,kBAAkB,EAAE,EAAE,KAAK,EAAE,GAAG,UAAU,WAAW,EAAE;YACvD,YAAY,EAAE,EAAE,KAAK,EAAE,GAAG,aAAa,KAAK,EAAE;SAC/C;KACF,CAAA;AACH,CAAC","sourcesContent":["import { postHogHttpUrl } from './config.js'\nimport type { PostHogEnvironment } from './config.js'\n\ntype ViteProxyTarget = {\n target: string\n changeOrigin: true\n rewrite: (path: string) => string\n}\n\nfunction viteTarget(host: string): ViteProxyTarget {\n return {\n target: host,\n changeOrigin: true,\n rewrite: (path) => path.replace(/^\\/ingest/, ''),\n }\n}\n\nexport function postHogIngestProxy(input: Pick<PostHogEnvironment, 'host' | 'assetsHost'>) {\n const ingestionHost = postHogHttpUrl(input.host, 'host')\n const assetsHost = postHogHttpUrl(input.assetsHost, 'assetsHost')\n return {\n vite: {\n '/ingest/static': viteTarget(assetsHost),\n '/ingest/array': viteTarget(assetsHost),\n '/ingest': viteTarget(ingestionHost),\n },\n nitro: {\n '/ingest/static/**': { proxy: `${assetsHost}/static/**` },\n '/ingest/array/**': { proxy: `${assetsHost}/array/**` },\n '/ingest/**': { proxy: `${ingestionHost}/**` },\n },\n }\n}\n"]}
@@ -0,0 +1,19 @@
1
+ import { type ReactNode } from 'react';
2
+ import type { PostHogConfig } from 'posthog-js';
3
+ import type { PostHogEnvironment } from './config.js';
4
+ export declare function PostHogIntegration({ children, environment, fallback, options, }: {
5
+ children?: ReactNode;
6
+ environment: PostHogEnvironment | undefined;
7
+ fallback?: ReactNode;
8
+ options?: Partial<PostHogConfig>;
9
+ }): string | number | bigint | boolean | import("react").FunctionComponentElement<({
10
+ client: import("posthog-js").PostHog;
11
+ apiKey?: never;
12
+ options?: never;
13
+ } | {
14
+ apiKey: string;
15
+ options?: Partial<PostHogConfig>;
16
+ client?: never;
17
+ }) & {
18
+ children?: ReactNode | undefined;
19
+ }> | Iterable<ReactNode> | Promise<string | number | bigint | boolean | Iterable<ReactNode> | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | import("react").ReactPortal | null | undefined> | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | null | undefined;
@@ -0,0 +1,18 @@
1
+ import { PostHogErrorBoundary, PostHogProvider } from '@posthog/react';
2
+ import { createElement } from 'react';
3
+ import { postHogBrowserOptions } from './client.js';
4
+ export function PostHogIntegration({ children, environment, fallback = createElement('main', null, 'Something went wrong. Refresh the page to try again.'), options, }) {
5
+ if (!environment)
6
+ return children;
7
+ const tracingHostnames = typeof window === 'undefined' ? undefined : [window.location.hostname];
8
+ return createElement(PostHogProvider, {
9
+ apiKey: environment.projectToken,
10
+ options: postHogBrowserOptions({
11
+ apiHost: '/ingest',
12
+ uiHost: environment.uiHost,
13
+ ...(tracingHostnames ? { tracingHostnames } : {}),
14
+ ...(options ? { options } : {}),
15
+ }),
16
+ }, createElement(PostHogErrorBoundary, { fallback }, children));
17
+ }
18
+ //# sourceMappingURL=react.js.map