@interfere/next 0.1.0-alpha.10 → 0.1.0-alpha.11

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 (120) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +3 -3
  3. package/dist/_virtual/{rolldown_runtime.mjs → _rolldown/runtime.mjs} +1 -1
  4. package/dist/build/env-config.d.mts +3 -1
  5. package/dist/build/env-config.d.mts.map +1 -1
  6. package/dist/build/env-config.mjs +9 -1
  7. package/dist/build/env-config.mjs.map +1 -1
  8. package/dist/build/exchange-surface.d.mts +9 -0
  9. package/dist/build/exchange-surface.d.mts.map +1 -0
  10. package/dist/build/exchange-surface.mjs +36 -0
  11. package/dist/build/exchange-surface.mjs.map +1 -0
  12. package/dist/build/loaders/value-injection-loader.d.mts.map +1 -1
  13. package/dist/build/logger.d.mts.map +1 -1
  14. package/dist/build/nextjs-version.d.mts.map +1 -1
  15. package/dist/build/nextjs-version.mjs +1 -1
  16. package/dist/build/release-program.d.mts +3 -3
  17. package/dist/build/release-program.d.mts.map +1 -1
  18. package/dist/build/release-program.mjs +4 -6
  19. package/dist/build/release-program.mjs.map +1 -1
  20. package/dist/build/services/config.service.d.mts.map +1 -1
  21. package/dist/build/services/config.service.mjs.map +1 -1
  22. package/dist/build/services/instrumentation-detection.service.d.mts.map +1 -1
  23. package/dist/build/services/preflight.service.d.mts +2 -3
  24. package/dist/build/services/preflight.service.d.mts.map +1 -1
  25. package/dist/build/services/preflight.service.mjs +24 -33
  26. package/dist/build/services/preflight.service.mjs.map +1 -1
  27. package/dist/build/services/release-identity.service.d.mts +4 -3
  28. package/dist/build/services/release-identity.service.d.mts.map +1 -1
  29. package/dist/build/services/release-identity.service.mjs +23 -10
  30. package/dist/build/services/release-identity.service.mjs.map +1 -1
  31. package/dist/build/services/source-map.service.d.mts +3 -7
  32. package/dist/build/services/source-map.service.d.mts.map +1 -1
  33. package/dist/build/services/source-map.service.mjs.map +1 -1
  34. package/dist/build/source-maps/api.d.mts +25 -16
  35. package/dist/build/source-maps/api.d.mts.map +1 -1
  36. package/dist/build/source-maps/api.mjs +11 -8
  37. package/dist/build/source-maps/api.mjs.map +1 -1
  38. package/dist/build/source-maps/client.d.mts +22 -21
  39. package/dist/build/source-maps/client.d.mts.map +1 -1
  40. package/dist/build/source-maps/client.mjs +14 -9
  41. package/dist/build/source-maps/client.mjs.map +1 -1
  42. package/dist/build/source-maps/errors.d.mts +118 -106
  43. package/dist/build/source-maps/errors.d.mts.map +1 -1
  44. package/dist/build/source-maps/errors.mjs +42 -18
  45. package/dist/build/source-maps/errors.mjs.map +1 -1
  46. package/dist/build/source-maps/files.d.mts.map +1 -1
  47. package/dist/build/source-maps/providers/deployment/detector.d.mts +7 -16
  48. package/dist/build/source-maps/providers/deployment/detector.d.mts.map +1 -1
  49. package/dist/build/source-maps/providers/deployment/detector.mjs +2 -2
  50. package/dist/build/source-maps/providers/deployment/detector.mjs.map +1 -1
  51. package/dist/build/source-maps/providers/deployment/types.d.mts +2 -2
  52. package/dist/build/source-maps/providers/deployment/types.d.mts.map +1 -1
  53. package/dist/build/source-maps/providers/deployment/vercel.d.mts.map +1 -1
  54. package/dist/build/source-maps/providers/deployment/vercel.mjs +7 -18
  55. package/dist/build/source-maps/providers/deployment/vercel.mjs.map +1 -1
  56. package/dist/build/source-maps/providers/source-control/detector.d.mts +5 -4
  57. package/dist/build/source-maps/providers/source-control/detector.d.mts.map +1 -1
  58. package/dist/build/source-maps/providers/source-control/detector.mjs +1 -1
  59. package/dist/build/source-maps/providers/source-control/detector.mjs.map +1 -1
  60. package/dist/build/source-maps/providers/source-control/git.d.mts.map +1 -1
  61. package/dist/build/source-maps/providers/source-control/git.mjs +4 -7
  62. package/dist/build/source-maps/providers/source-control/git.mjs.map +1 -1
  63. package/dist/build/source-maps/providers/source-control/types.d.mts +5 -3
  64. package/dist/build/source-maps/providers/source-control/types.d.mts.map +1 -1
  65. package/dist/build/with-interfere.d.mts +23 -23
  66. package/dist/build/with-interfere.d.mts.map +1 -1
  67. package/dist/build/with-interfere.mjs +47 -26
  68. package/dist/build/with-interfere.mjs.map +1 -1
  69. package/dist/client/auto-init.d.mts +3 -3
  70. package/dist/client/auto-init.d.mts.map +1 -1
  71. package/dist/client/auto-init.mjs +1 -1
  72. package/dist/client/auto-init.mjs.map +1 -1
  73. package/dist/client/provider.d.mts +2 -2
  74. package/dist/client/provider.d.mts.map +1 -1
  75. package/dist/client/provider.mjs.map +1 -1
  76. package/dist/lib/env.d.mts.map +1 -1
  77. package/dist/lib/types.d.mts +6 -6
  78. package/dist/lib/types.d.mts.map +1 -1
  79. package/dist/lib/types.mjs.map +1 -1
  80. package/dist/server/auto-init.d.mts +11 -10
  81. package/dist/server/auto-init.d.mts.map +1 -1
  82. package/dist/server/auto-init.mjs +3 -3
  83. package/dist/server/auto-init.mjs.map +1 -1
  84. package/dist/server/middleware.d.mts.map +1 -1
  85. package/dist/server/middleware.mjs +9 -10
  86. package/dist/server/middleware.mjs.map +1 -1
  87. package/dist/server/on-request-error.d.mts +7 -7
  88. package/dist/server/on-request-error.d.mts.map +1 -1
  89. package/dist/server/on-request-error.mjs +2 -2
  90. package/dist/server/on-request-error.mjs.map +1 -1
  91. package/dist/server/proxy.d.mts.map +1 -1
  92. package/dist/server/proxy.mjs +4 -5
  93. package/dist/server/proxy.mjs.map +1 -1
  94. package/dist/server/route-handler.d.mts +31 -1
  95. package/dist/server/route-handler.d.mts.map +1 -1
  96. package/dist/server/route-handler.mjs +72 -67
  97. package/dist/server/route-handler.mjs.map +1 -1
  98. package/dist/server/sdk.d.mts +29 -29
  99. package/dist/server/sdk.d.mts.map +1 -1
  100. package/dist/server/sdk.mjs +2 -2
  101. package/dist/server/sdk.mjs.map +1 -1
  102. package/dist/server/services/config.service.d.mts +33 -6
  103. package/dist/server/services/config.service.d.mts.map +1 -1
  104. package/dist/server/services/config.service.mjs +54 -30
  105. package/dist/server/services/config.service.mjs.map +1 -1
  106. package/dist/server/services/error-tracking.service.d.mts.map +1 -1
  107. package/dist/server/services/error-tracking.service.mjs +1 -1
  108. package/dist/server/services/error-tracking.service.mjs.map +1 -1
  109. package/dist/server/session-context.d.mts +11 -11
  110. package/dist/server/session-context.d.mts.map +1 -1
  111. package/dist/server/session-context.mjs.map +1 -1
  112. package/package.json +38 -34
  113. package/dist/build/secret-key.d.mts +0 -10
  114. package/dist/build/secret-key.d.mts.map +0 -1
  115. package/dist/build/secret-key.mjs +0 -16
  116. package/dist/build/secret-key.mjs.map +0 -1
  117. package/dist/lib/test-utils/make-next-request.d.mts +0 -6
  118. package/dist/lib/test-utils/make-next-request.d.mts.map +0 -1
  119. package/dist/lib/test-utils/make-next-request.mjs +0 -12
  120. package/dist/lib/test-utils/make-next-request.mjs.map +0 -1
@@ -1,15 +1,15 @@
1
1
  //#region src/server/sdk.d.ts
2
2
  interface ServerSDKConfig {
3
+ readonly features: {
4
+ readonly errorTracking: boolean;
5
+ readonly performanceMonitoring: boolean;
6
+ };
3
7
  readonly metadata: {
4
8
  readonly buildId: string | null;
5
9
  readonly releaseId: string | null;
6
10
  readonly environment: string;
7
11
  readonly runtime: "nodejs" | "edge";
8
12
  };
9
- readonly features: {
10
- readonly errorTracking: boolean;
11
- readonly performanceMonitoring: boolean;
12
- };
13
13
  }
14
14
  /**
15
15
  * Server-side SDK singleton for automatic error tracking and instrumentation.
@@ -45,51 +45,51 @@ declare class ServerSDK {
45
45
  private globalHandlersInstalled;
46
46
  private constructor();
47
47
  /**
48
- * Initialize the server SDK singleton.
49
- * If already initialized, returns the existing instance.
50
- */
48
+ * Initialize the server SDK singleton.
49
+ * If already initialized, returns the existing instance.
50
+ */
51
51
  static init(config: ServerSDKConfig): ServerSDK;
52
52
  /**
53
- * Get the current SDK instance, if initialized.
54
- */
53
+ * Get the current SDK instance, if initialized.
54
+ */
55
55
  static getInstance(): ServerSDK | null;
56
56
  /**
57
- * Check if the SDK is initialized.
58
- */
57
+ * Check if the SDK is initialized.
58
+ */
59
59
  static isInitialized(): boolean;
60
60
  /**
61
- * Reset the SDK instance (primarily for testing).
62
- * @internal
63
- */
61
+ * Reset the SDK instance (primarily for testing).
62
+ * @internal
63
+ */
64
64
  static reset(): void;
65
65
  /**
66
- * Install global error handlers for uncaught exceptions and unhandled rejections.
67
- * These handlers capture errors and send them to Interfere before the process exits.
68
- */
66
+ * Install global error handlers for uncaught exceptions and unhandled rejections.
67
+ * These handlers capture errors and send them to Interfere before the process exits.
68
+ */
69
69
  installGlobalHandlers(): void;
70
70
  /**
71
- * Remove global error handlers.
72
- * @internal
73
- */
71
+ * Remove global error handlers.
72
+ * @internal
73
+ */
74
74
  private uninstallGlobalHandlers;
75
75
  private readonly handleUncaughtException;
76
76
  private readonly handleUnhandledRejection;
77
77
  /**
78
- * Capture an error and send it to Interfere.
79
- * This is a best-effort operation that will not throw.
80
- */
78
+ * Capture an error and send it to Interfere.
79
+ * This is a best-effort operation that will not throw.
80
+ */
81
81
  captureError(error: Error | unknown, context?: Record<string, unknown>): void;
82
82
  /**
83
- * Get the SDK configuration.
84
- */
83
+ * Get the SDK configuration.
84
+ */
85
85
  getConfig(): Readonly<ServerSDKConfig>;
86
86
  /**
87
- * Get a specific metadata value.
88
- */
87
+ * Get a specific metadata value.
88
+ */
89
89
  getMetadata<K extends keyof ServerSDKConfig["metadata"]>(key: K): ServerSDKConfig["metadata"][K];
90
90
  /**
91
- * Check if a feature is enabled.
92
- */
91
+ * Check if a feature is enabled.
92
+ */
93
93
  isFeatureEnabled<K extends keyof ServerSDKConfig["features"]>(feature: K): boolean;
94
94
  }
95
95
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"sdk.d.mts","names":[],"sources":["../../src/server/sdk.ts"],"mappings":";UAUiB,eAAA;EAAA,SAAA,QAAA;IAAA,SAAA,OAAA;IAAA,SAAA,SAAA;IAAA,SAAA,WAAA;IAAA,SAAA,OAAA;EAAA;EAAA,SAAA,QAAA;IAAA,SAAA,aAAA;IAAA,SAAA,qBAAA;EAAA;AAAA;AAAA;AA8CjB;;;;;;;;;;;;;;;;;;;;;;;;;;;AA9CiB,cA8CJ,SAAA;EAAA,eAAA,QAAA;EAAA,iBAAA,MAAA;EAAA,QAAA,uBAAA;EAAA,QAAA,YAAA;EAAA;;;;EAAA,OAAA,KAAA,MAAA,EAcS,eAAA,GAAkB,SAAA;EAAA;;;EAAA,OAAA,YAAA,GA2BhB,SAAA;EAAA;;;EAAA,OAAA,cAAA;EAAA;;;;EAAA,OAAA,MAAA;EAAA;;;;EAAA,sBAAA;EAAA;;;;EAAA,QAAA,uBAAA;EAAA,iBAAA,uBAAA;EAAA,iBAAA,wBAAA;EAAA;;;;EAAA,aAAA,KAAA,EAuFb,KAAA,YAAA,OAAA,GACG,MAAA;EAAA;;;EAAA,UAAA,GAyBC,QAAA,CAAS,eAAA;EAAA;;;EAAA,WAAA,iBAOM,eAAA,aAAA,CAAA,GAAA,EACrB,CAAA,GACJ,eAAA,aAA4B,CAAA;EAAA;;;EAAA,gBAAA,iBAOE,eAAA,aAAA,CAAA,OAAA,EACtB,CAAA;AAAA"}
1
+ {"version":3,"file":"sdk.d.mts","names":[],"sources":["../../src/server/sdk.ts"],"mappings":";UAUiB,eAAA;EAAA,SACN,QAAA;IAAA,SACE,aAAA;IAAA,SACA,qBAAA;EAAA;EAAA,SAEF,QAAA;IAAA,SACE,OAAA;IAAA,SACA,SAAA;IAAA,SACA,WAAA;IAAA,SACA,OAAA;EAAA;AAAA;;;;AAqCb;;;;;;;;;;;;;;;;;;;;;;;;;cAAa,SAAA;EAAA,eACI,QAAA;EAAA,iBAEE,MAAA;EAAA,QACT,uBAAA;EAAA,QAED,WAAA,CAAA;EA6DP;;;;EAAA,OArDO,IAAA,CAAK,MAAA,EAAQ,eAAA,GAAkB,SAAA;EAkH7B;;;EAAA,OAvFF,WAAA,CAAA,GAAe,SAAA;EAiHtB;;;EAAA,OA1GO,aAAA,CAAA;EAiHK;;;;EAAA,OAzGL,KAAA,CAAA;EA2GwB;;;;EAhG/B,qBAAA,CAAA;EAwGE;;;;EAAA,QAjFM,uBAAA;EAAA,iBAWS,uBAAA;EAAA,iBAYA,wBAAA;;;;;EAcjB,YAAA,CACE,KAAA,EAAO,KAAA,YACP,OAAA,GAAU,MAAA;;;;EAyBZ,SAAA,CAAA,GAAa,QAAA,CAAS,eAAA;;;;EAOtB,WAAA,iBAA4B,eAAA,aAAA,CAC1B,GAAA,EAAK,CAAA,GACJ,eAAA,aAA4B,CAAA;;;;EAO/B,gBAAA,iBAAiC,eAAA,aAAA,CAC/B,OAAA,EAAS,CAAA;AAAA"}
@@ -1,10 +1,10 @@
1
- import { ConfigServiceLive } from "./services/config.service.mjs";
1
+ import { NextConfigLive } from "./services/config.service.mjs";
2
2
  import { ErrorTrackingService, ErrorTrackingServiceLive } from "./services/error-tracking.service.mjs";
3
3
  import { withInterfereLogger } from "@interfere/effect-utils/observability";
4
4
  import { Effect, Layer } from "effect";
5
5
 
6
6
  //#region src/server/sdk.ts
7
- const errorTrackingLayer = Layer.mergeAll(ConfigServiceLive, ErrorTrackingServiceLive);
7
+ const errorTrackingLayer = Layer.mergeAll(NextConfigLive, ErrorTrackingServiceLive);
8
8
  /**
9
9
  * Server-side SDK singleton for automatic error tracking and instrumentation.
10
10
  *
@@ -1 +1 @@
1
- {"version":3,"file":"sdk.mjs","names":[],"sources":["../../src/server/sdk.ts"],"sourcesContent":["import { withInterfereLogger } from \"@interfere/effect-utils/observability\";\n\nimport { Effect, Layer } from \"effect\";\n\nimport { ConfigServiceLive } from \"./services/config.service.js\";\nimport {\n ErrorTrackingService,\n ErrorTrackingServiceLive,\n} from \"./services/error-tracking.service.js\";\n\nexport interface ServerSDKConfig {\n readonly metadata: {\n readonly buildId: string | null;\n readonly releaseId: string | null;\n readonly environment: string;\n readonly runtime: \"nodejs\" | \"edge\";\n };\n readonly features: {\n readonly errorTracking: boolean;\n readonly performanceMonitoring: boolean;\n };\n}\n\nconst errorTrackingLayer = Layer.mergeAll(\n ConfigServiceLive,\n ErrorTrackingServiceLive\n);\n\n/**\n * Server-side SDK singleton for automatic error tracking and instrumentation.\n *\n * This SDK is designed to be initialized once via `instrumentation.ts` and\n * provides global error handlers and a unified interface for capturing errors.\n *\n * @example\n * ```ts\n * // instrumentation.ts\n * export async function register() {\n * if (process.env.NEXT_RUNTIME === 'nodejs') {\n * const { ServerSDK } = await import('@interfere/next/server/sdk');\n * ServerSDK.init({\n * metadata: {\n * buildId: globalThis.__INTERFERE_BUILD_ID__,\n * releaseId: globalThis.__INTERFERE_RELEASE_ID__,\n * environment: globalThis.__INTERFERE_ENVIRONMENT__ ?? 'production',\n * runtime: 'nodejs',\n * },\n * features: {\n * errorTracking: true,\n * performanceMonitoring: true,\n * },\n * });\n * }\n * }\n * ```\n */\nexport class ServerSDK {\n private static instance: ServerSDK | null = null;\n\n private readonly config: ServerSDKConfig;\n private globalHandlersInstalled = false;\n\n private constructor(config: ServerSDKConfig) {\n this.config = config;\n }\n\n /**\n * Initialize the server SDK singleton.\n * If already initialized, returns the existing instance.\n */\n static init(config: ServerSDKConfig): ServerSDK {\n if (ServerSDK.instance) {\n if (process.env.NODE_ENV === \"development\") {\n console.log(\n \"[Interfere] Server SDK already initialized, reusing instance\"\n );\n }\n return ServerSDK.instance;\n }\n\n ServerSDK.instance = new ServerSDK(config);\n\n if (process.env.NODE_ENV === \"development\") {\n console.log(\"[Interfere] Server SDK initialized:\", {\n buildId: config.metadata.buildId,\n releaseId: config.metadata.releaseId,\n environment: config.metadata.environment,\n features: config.features,\n });\n }\n\n return ServerSDK.instance;\n }\n\n /**\n * Get the current SDK instance, if initialized.\n */\n static getInstance(): ServerSDK | null {\n return ServerSDK.instance;\n }\n\n /**\n * Check if the SDK is initialized.\n */\n static isInitialized(): boolean {\n return ServerSDK.instance !== null;\n }\n\n /**\n * Reset the SDK instance (primarily for testing).\n * @internal\n */\n static reset(): void {\n if (ServerSDK.instance) {\n ServerSDK.instance.uninstallGlobalHandlers();\n ServerSDK.instance = null;\n }\n }\n\n /**\n * Install global error handlers for uncaught exceptions and unhandled rejections.\n * These handlers capture errors and send them to Interfere before the process exits.\n */\n installGlobalHandlers(): void {\n if (this.globalHandlersInstalled) {\n return;\n }\n\n if (!this.config.features.errorTracking) {\n return;\n }\n\n process.on(\"uncaughtException\", this.handleUncaughtException);\n process.on(\"unhandledRejection\", this.handleUnhandledRejection);\n\n this.globalHandlersInstalled = true;\n\n if (process.env.NODE_ENV === \"development\") {\n console.log(\"[Interfere] Global error handlers installed\");\n }\n }\n\n /**\n * Remove global error handlers.\n * @internal\n */\n private uninstallGlobalHandlers(): void {\n if (!this.globalHandlersInstalled) {\n return;\n }\n\n process.off(\"uncaughtException\", this.handleUncaughtException);\n process.off(\"unhandledRejection\", this.handleUnhandledRejection);\n\n this.globalHandlersInstalled = false;\n }\n\n private readonly handleUncaughtException = (\n error: Error,\n origin: string\n ): void => {\n this.captureError(error, {\n type: \"uncaught_exception\",\n origin,\n fatal: true,\n });\n // Don't prevent process exit - let it crash after capturing\n };\n\n private readonly handleUnhandledRejection = (\n reason: unknown,\n _promise: Promise<unknown>\n ): void => {\n const error = reason instanceof Error ? reason : new Error(String(reason));\n this.captureError(error, {\n type: \"unhandled_rejection\",\n });\n };\n\n /**\n * Capture an error and send it to Interfere.\n * This is a best-effort operation that will not throw.\n */\n captureError(\n error: Error | unknown,\n context?: Record<string, unknown>\n ): void {\n if (!this.config.features.errorTracking) {\n return;\n }\n\n const errorObj = error instanceof Error ? error : new Error(String(error));\n\n const program = Effect.gen(function* () {\n const errorTracking = yield* ErrorTrackingService;\n yield* errorTracking.captureError(errorObj, undefined, {\n ...context,\n sdk: \"server\",\n });\n }).pipe(Effect.provide(errorTrackingLayer));\n\n // Run the effect and ignore any errors\n Effect.runPromise(withInterfereLogger(\"next-server\", program)).catch(() => {\n // Swallow errors in error tracking to avoid cascading failures\n });\n }\n\n /**\n * Get the SDK configuration.\n */\n getConfig(): Readonly<ServerSDKConfig> {\n return this.config;\n }\n\n /**\n * Get a specific metadata value.\n */\n getMetadata<K extends keyof ServerSDKConfig[\"metadata\"]>(\n key: K\n ): ServerSDKConfig[\"metadata\"][K] {\n return this.config.metadata[key];\n }\n\n /**\n * Check if a feature is enabled.\n */\n isFeatureEnabled<K extends keyof ServerSDKConfig[\"features\"]>(\n feature: K\n ): boolean {\n return this.config.features[feature];\n }\n}\n"],"mappings":";;;;;;AAuBA,MAAM,qBAAqB,MAAM,SAC/B,mBACA,yBACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BD,IAAa,YAAb,MAAa,UAAU;CACrB,OAAe,WAA6B;CAE5C,AAAiB;CACjB,AAAQ,0BAA0B;CAElC,AAAQ,YAAY,QAAyB;AAC3C,OAAK,SAAS;;;;;;CAOhB,OAAO,KAAK,QAAoC;AAC9C,MAAI,UAAU,UAAU;AACtB,OAAI,QAAQ,IAAI,aAAa,cAC3B,SAAQ,IACN,+DACD;AAEH,UAAO,UAAU;;AAGnB,YAAU,WAAW,IAAI,UAAU,OAAO;AAE1C,MAAI,QAAQ,IAAI,aAAa,cAC3B,SAAQ,IAAI,uCAAuC;GACjD,SAAS,OAAO,SAAS;GACzB,WAAW,OAAO,SAAS;GAC3B,aAAa,OAAO,SAAS;GAC7B,UAAU,OAAO;GAClB,CAAC;AAGJ,SAAO,UAAU;;;;;CAMnB,OAAO,cAAgC;AACrC,SAAO,UAAU;;;;;CAMnB,OAAO,gBAAyB;AAC9B,SAAO,UAAU,aAAa;;;;;;CAOhC,OAAO,QAAc;AACnB,MAAI,UAAU,UAAU;AACtB,aAAU,SAAS,yBAAyB;AAC5C,aAAU,WAAW;;;;;;;CAQzB,wBAA8B;AAC5B,MAAI,KAAK,wBACP;AAGF,MAAI,CAAC,KAAK,OAAO,SAAS,cACxB;AAGF,UAAQ,GAAG,qBAAqB,KAAK,wBAAwB;AAC7D,UAAQ,GAAG,sBAAsB,KAAK,yBAAyB;AAE/D,OAAK,0BAA0B;AAE/B,MAAI,QAAQ,IAAI,aAAa,cAC3B,SAAQ,IAAI,8CAA8C;;;;;;CAQ9D,AAAQ,0BAAgC;AACtC,MAAI,CAAC,KAAK,wBACR;AAGF,UAAQ,IAAI,qBAAqB,KAAK,wBAAwB;AAC9D,UAAQ,IAAI,sBAAsB,KAAK,yBAAyB;AAEhE,OAAK,0BAA0B;;CAGjC,AAAiB,2BACf,OACA,WACS;AACT,OAAK,aAAa,OAAO;GACvB,MAAM;GACN;GACA,OAAO;GACR,CAAC;;CAIJ,AAAiB,4BACf,QACA,aACS;EACT,MAAM,QAAQ,kBAAkB,QAAQ,SAAS,IAAI,MAAM,OAAO,OAAO,CAAC;AAC1E,OAAK,aAAa,OAAO,EACvB,MAAM,uBACP,CAAC;;;;;;CAOJ,aACE,OACA,SACM;AACN,MAAI,CAAC,KAAK,OAAO,SAAS,cACxB;EAGF,MAAM,WAAW,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,MAAM,CAAC;EAE1E,MAAM,UAAU,OAAO,IAAI,aAAa;AAEtC,WADsB,OAAO,sBACR,aAAa,UAAU,QAAW;IACrD,GAAG;IACH,KAAK;IACN,CAAC;IACF,CAAC,KAAK,OAAO,QAAQ,mBAAmB,CAAC;AAG3C,SAAO,WAAW,oBAAoB,eAAe,QAAQ,CAAC,CAAC,YAAY,GAEzE;;;;;CAMJ,YAAuC;AACrC,SAAO,KAAK;;;;;CAMd,YACE,KACgC;AAChC,SAAO,KAAK,OAAO,SAAS;;;;;CAM9B,iBACE,SACS;AACT,SAAO,KAAK,OAAO,SAAS"}
1
+ {"version":3,"file":"sdk.mjs","names":[],"sources":["../../src/server/sdk.ts"],"sourcesContent":["import { withInterfereLogger } from \"@interfere/effect-utils/observability\";\n\nimport { Effect, Layer } from \"effect\";\n\nimport { NextConfigLive } from \"./services/config.service.js\";\nimport {\n ErrorTrackingService,\n ErrorTrackingServiceLive,\n} from \"./services/error-tracking.service.js\";\n\nexport interface ServerSDKConfig {\n readonly features: {\n readonly errorTracking: boolean;\n readonly performanceMonitoring: boolean;\n };\n readonly metadata: {\n readonly buildId: string | null;\n readonly releaseId: string | null;\n readonly environment: string;\n readonly runtime: \"nodejs\" | \"edge\";\n };\n}\n\nconst errorTrackingLayer = Layer.mergeAll(\n NextConfigLive,\n ErrorTrackingServiceLive\n);\n\n/**\n * Server-side SDK singleton for automatic error tracking and instrumentation.\n *\n * This SDK is designed to be initialized once via `instrumentation.ts` and\n * provides global error handlers and a unified interface for capturing errors.\n *\n * @example\n * ```ts\n * // instrumentation.ts\n * export async function register() {\n * if (process.env.NEXT_RUNTIME === 'nodejs') {\n * const { ServerSDK } = await import('@interfere/next/server/sdk');\n * ServerSDK.init({\n * metadata: {\n * buildId: globalThis.__INTERFERE_BUILD_ID__,\n * releaseId: globalThis.__INTERFERE_RELEASE_ID__,\n * environment: globalThis.__INTERFERE_ENVIRONMENT__ ?? 'production',\n * runtime: 'nodejs',\n * },\n * features: {\n * errorTracking: true,\n * performanceMonitoring: true,\n * },\n * });\n * }\n * }\n * ```\n */\nexport class ServerSDK {\n private static instance: ServerSDK | null = null;\n\n private readonly config: ServerSDKConfig;\n private globalHandlersInstalled = false;\n\n private constructor(config: ServerSDKConfig) {\n this.config = config;\n }\n\n /**\n * Initialize the server SDK singleton.\n * If already initialized, returns the existing instance.\n */\n static init(config: ServerSDKConfig): ServerSDK {\n if (ServerSDK.instance) {\n if (process.env.NODE_ENV === \"development\") {\n console.log(\n \"[Interfere] Server SDK already initialized, reusing instance\"\n );\n }\n return ServerSDK.instance;\n }\n\n ServerSDK.instance = new ServerSDK(config);\n\n if (process.env.NODE_ENV === \"development\") {\n console.log(\"[Interfere] Server SDK initialized:\", {\n buildId: config.metadata.buildId,\n releaseId: config.metadata.releaseId,\n environment: config.metadata.environment,\n features: config.features,\n });\n }\n\n return ServerSDK.instance;\n }\n\n /**\n * Get the current SDK instance, if initialized.\n */\n static getInstance(): ServerSDK | null {\n return ServerSDK.instance;\n }\n\n /**\n * Check if the SDK is initialized.\n */\n static isInitialized(): boolean {\n return ServerSDK.instance !== null;\n }\n\n /**\n * Reset the SDK instance (primarily for testing).\n * @internal\n */\n static reset(): void {\n if (ServerSDK.instance) {\n ServerSDK.instance.uninstallGlobalHandlers();\n ServerSDK.instance = null;\n }\n }\n\n /**\n * Install global error handlers for uncaught exceptions and unhandled rejections.\n * These handlers capture errors and send them to Interfere before the process exits.\n */\n installGlobalHandlers(): void {\n if (this.globalHandlersInstalled) {\n return;\n }\n\n if (!this.config.features.errorTracking) {\n return;\n }\n\n process.on(\"uncaughtException\", this.handleUncaughtException);\n process.on(\"unhandledRejection\", this.handleUnhandledRejection);\n\n this.globalHandlersInstalled = true;\n\n if (process.env.NODE_ENV === \"development\") {\n console.log(\"[Interfere] Global error handlers installed\");\n }\n }\n\n /**\n * Remove global error handlers.\n * @internal\n */\n private uninstallGlobalHandlers(): void {\n if (!this.globalHandlersInstalled) {\n return;\n }\n\n process.off(\"uncaughtException\", this.handleUncaughtException);\n process.off(\"unhandledRejection\", this.handleUnhandledRejection);\n\n this.globalHandlersInstalled = false;\n }\n\n private readonly handleUncaughtException = (\n error: Error,\n origin: string\n ): void => {\n this.captureError(error, {\n type: \"uncaught_exception\",\n origin,\n fatal: true,\n });\n // Don't prevent process exit - let it crash after capturing\n };\n\n private readonly handleUnhandledRejection = (\n reason: unknown,\n _promise: Promise<unknown>\n ): void => {\n const error = reason instanceof Error ? reason : new Error(String(reason));\n this.captureError(error, {\n type: \"unhandled_rejection\",\n });\n };\n\n /**\n * Capture an error and send it to Interfere.\n * This is a best-effort operation that will not throw.\n */\n captureError(\n error: Error | unknown,\n context?: Record<string, unknown>\n ): void {\n if (!this.config.features.errorTracking) {\n return;\n }\n\n const errorObj = error instanceof Error ? error : new Error(String(error));\n\n const program = Effect.gen(function* () {\n const errorTracking = yield* ErrorTrackingService;\n yield* errorTracking.captureError(errorObj, undefined, {\n ...context,\n sdk: \"server\",\n });\n }).pipe(Effect.provide(errorTrackingLayer));\n\n // Run the effect and ignore any errors\n Effect.runPromise(withInterfereLogger(\"next-server\", program)).catch(() => {\n // Swallow errors in error tracking to avoid cascading failures\n });\n }\n\n /**\n * Get the SDK configuration.\n */\n getConfig(): Readonly<ServerSDKConfig> {\n return this.config;\n }\n\n /**\n * Get a specific metadata value.\n */\n getMetadata<K extends keyof ServerSDKConfig[\"metadata\"]>(\n key: K\n ): ServerSDKConfig[\"metadata\"][K] {\n return this.config.metadata[key];\n }\n\n /**\n * Check if a feature is enabled.\n */\n isFeatureEnabled<K extends keyof ServerSDKConfig[\"features\"]>(\n feature: K\n ): boolean {\n return this.config.features[feature];\n }\n}\n"],"mappings":";;;;;;AAuBA,MAAM,qBAAqB,MAAM,SAC/B,gBACA,yBACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BD,IAAa,YAAb,MAAa,UAAU;CACrB,OAAe,WAA6B;CAE5C,AAAiB;CACjB,AAAQ,0BAA0B;CAElC,AAAQ,YAAY,QAAyB;AAC3C,OAAK,SAAS;;;;;;CAOhB,OAAO,KAAK,QAAoC;AAC9C,MAAI,UAAU,UAAU;AACtB,OAAI,QAAQ,IAAI,aAAa,cAC3B,SAAQ,IACN,+DACD;AAEH,UAAO,UAAU;;AAGnB,YAAU,WAAW,IAAI,UAAU,OAAO;AAE1C,MAAI,QAAQ,IAAI,aAAa,cAC3B,SAAQ,IAAI,uCAAuC;GACjD,SAAS,OAAO,SAAS;GACzB,WAAW,OAAO,SAAS;GAC3B,aAAa,OAAO,SAAS;GAC7B,UAAU,OAAO;GAClB,CAAC;AAGJ,SAAO,UAAU;;;;;CAMnB,OAAO,cAAgC;AACrC,SAAO,UAAU;;;;;CAMnB,OAAO,gBAAyB;AAC9B,SAAO,UAAU,aAAa;;;;;;CAOhC,OAAO,QAAc;AACnB,MAAI,UAAU,UAAU;AACtB,aAAU,SAAS,yBAAyB;AAC5C,aAAU,WAAW;;;;;;;CAQzB,wBAA8B;AAC5B,MAAI,KAAK,wBACP;AAGF,MAAI,CAAC,KAAK,OAAO,SAAS,cACxB;AAGF,UAAQ,GAAG,qBAAqB,KAAK,wBAAwB;AAC7D,UAAQ,GAAG,sBAAsB,KAAK,yBAAyB;AAE/D,OAAK,0BAA0B;AAE/B,MAAI,QAAQ,IAAI,aAAa,cAC3B,SAAQ,IAAI,8CAA8C;;;;;;CAQ9D,AAAQ,0BAAgC;AACtC,MAAI,CAAC,KAAK,wBACR;AAGF,UAAQ,IAAI,qBAAqB,KAAK,wBAAwB;AAC9D,UAAQ,IAAI,sBAAsB,KAAK,yBAAyB;AAEhE,OAAK,0BAA0B;;CAGjC,AAAiB,2BACf,OACA,WACS;AACT,OAAK,aAAa,OAAO;GACvB,MAAM;GACN;GACA,OAAO;GACR,CAAC;;CAIJ,AAAiB,4BACf,QACA,aACS;EACT,MAAM,QAAQ,kBAAkB,QAAQ,SAAS,IAAI,MAAM,OAAO,OAAO,CAAC;AAC1E,OAAK,aAAa,OAAO,EACvB,MAAM,uBACP,CAAC;;;;;;CAOJ,aACE,OACA,SACM;AACN,MAAI,CAAC,KAAK,OAAO,SAAS,cACxB;EAGF,MAAM,WAAW,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,MAAM,CAAC;EAE1E,MAAM,UAAU,OAAO,IAAI,aAAa;AAEtC,WADsB,OAAO,sBACR,aAAa,UAAU,QAAW;IACrD,GAAG;IACH,KAAK;IACN,CAAC;IACF,CAAC,KAAK,OAAO,QAAQ,mBAAmB,CAAC;AAG3C,SAAO,WAAW,oBAAoB,eAAe,QAAQ,CAAC,CAAC,YAAY,GAEzE;;;;;CAMJ,YAAuC;AACrC,SAAO,KAAK;;;;;CAMd,YACE,KACgC;AAChC,SAAO,KAAK,OAAO,SAAS;;;;;CAM9B,iBACE,SACS;AACT,SAAO,KAAK,OAAO,SAAS"}
@@ -1,21 +1,48 @@
1
1
  import { NonEmptyString } from "../../lib/types.mjs";
2
2
  import { Context, Effect, Layer } from "effect";
3
- import * as effect_Types16 from "effect/Types";
4
- import * as effect_Cause16 from "effect/Cause";
3
+ import * as effect_Types0 from "effect/Types";
4
+ import * as effect_Cause0 from "effect/Cause";
5
5
 
6
6
  //#region src/server/services/config.service.d.ts
7
- declare const ConfigError_base: new <A extends Record<string, any> = {}>(args: effect_Types16.Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }) => effect_Cause16.YieldableError & {
7
+ declare global {
8
+ var __INTERFERE_SURFACE_SLUG__: string | undefined;
9
+ }
10
+ declare const ConfigError_base: new <A extends Record<string, any> = {}>(args: effect_Types0.Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }) => effect_Cause0.YieldableError & {
8
11
  readonly _tag: "ConfigError";
9
12
  } & Readonly<A>;
10
13
  declare class ConfigError extends ConfigError_base<{
11
14
  readonly message: string;
12
15
  }> {}
13
16
  interface ConfigService {
17
+ readonly getApiKey: Effect.Effect<NonEmptyString, ConfigError>;
14
18
  readonly getApiUrl: Effect.Effect<string>;
15
- readonly getSecretKey: Effect.Effect<NonEmptyString, ConfigError>;
16
19
  readonly getPublicToken: Effect.Effect<string, ConfigError>;
17
20
  }
18
21
  declare const ConfigService: Context.Tag<ConfigService, ConfigService>;
19
- declare const ConfigServiceLive: Layer.Layer<ConfigService, never, never>;
22
+ interface TokenExchange {
23
+ readonly exchange: (args: {
24
+ readonly apiUrl: string;
25
+ readonly apiKey: NonEmptyString;
26
+ readonly surfaceSlug?: NonEmptyString;
27
+ }) => Effect.Effect<string, TokenExchangeError>;
28
+ }
29
+ declare const TokenExchange: Context.Tag<TokenExchange, TokenExchange>;
30
+ declare const TokenExchangeCallFailed_base: new <A extends Record<string, any> = {}>(args: effect_Types0.Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }) => effect_Cause0.YieldableError & {
31
+ readonly _tag: "TokenExchangeCallFailed";
32
+ } & Readonly<A>;
33
+ declare class TokenExchangeCallFailed extends TokenExchangeCallFailed_base<{
34
+ readonly message: string;
35
+ readonly cause: unknown;
36
+ }> {}
37
+ declare const TokenExchangeReturnedNull_base: new <A extends Record<string, any> = {}>(args: effect_Types0.Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }) => effect_Cause0.YieldableError & {
38
+ readonly _tag: "TokenExchangeReturnedNull";
39
+ } & Readonly<A>;
40
+ declare class TokenExchangeReturnedNull extends TokenExchangeReturnedNull_base<{
41
+ readonly message: string;
42
+ }> {}
43
+ type TokenExchangeError = TokenExchangeCallFailed | TokenExchangeReturnedNull;
44
+ declare const TokenExchangeLive: Layer.Layer<TokenExchange, never, never>;
45
+ declare const ConfigServiceLive: Layer.Layer<ConfigService, never, TokenExchange>;
46
+ declare const NextConfigLive: Layer.Layer<ConfigService, never, never>;
20
47
  //#endregion
21
- export { ConfigError, ConfigService, ConfigServiceLive };
48
+ export { ConfigError, ConfigService, ConfigServiceLive, NextConfigLive, TokenExchange, TokenExchangeCallFailed, TokenExchangeLive, TokenExchangeReturnedNull };
@@ -1 +1 @@
1
- {"version":3,"file":"config.service.d.mts","names":[],"sources":["../../../src/server/services/config.service.ts"],"mappings":";;;;;;cAKyD,gBAAA;;;cAI5C,WAAA,SAAoB,gBAAA;EAAA,SAAA,OAAA;AAAA;AAAA,UAIhB,aAAA;EAAA,SAAA,SAAA,EACK,MAAA,CAAO,MAAA;EAAA,SAAA,YAAA,EACJ,MAAA,CAAO,MAAA,CAAO,cAAA,EAAgB,WAAA;EAAA,SAAA,cAAA,EAC5B,MAAA,CAAO,MAAA,SAAe,WAAA;AAAA;AAAA,cAGpC,aAAA,EAAa,OAAA,CAAA,GAAA,CAAA,aAAA,EAAA,aAAA;AAAA,cAsDb,iBAAA,EAAiB,KAAA,CAAA,KAAA,CAAA,aAAA"}
1
+ {"version":3,"file":"config.service.d.mts","names":[],"sources":["../../../src/server/services/config.service.ts"],"mappings":";;;;;;QAQQ,MAAA;EAAA,IACF,0BAAA;AAAA;AAAA,cAA8C,gBAAA;;;cAKvC,WAAA,SAAoB,gBAAA;EAAA,SACtB,OAAA;AAAA;AAAA,UAGM,aAAA;EAAA,SACN,SAAA,EAAW,MAAA,CAAO,MAAA,CAAO,cAAA,EAAgB,WAAA;EAAA,SACzC,SAAA,EAAW,MAAA,CAAO,MAAA;EAAA,SAClB,cAAA,EAAgB,MAAA,CAAO,MAAA,SAAe,WAAA;AAAA;AAAA,cAGpC,aAAA,EAAa,OAAA,CAAA,GAAA,CAAA,aAAA,EAAA,aAAA;AAAA,UAIT,aAAA;EAAA,SACN,QAAA,GAAW,IAAA;IAAA,SACT,MAAA;IAAA,SACA,MAAA,EAAQ,cAAA;IAAA,SACR,WAAA,GAAc,cAAA;EAAA,MACnB,MAAA,CAAO,MAAA,SAAe,kBAAA;AAAA;AAAA,cAGjB,aAAA,EAAa,OAAA,CAAA,GAAA,CAAA,aAAA,EAAA,aAAA;AAAA,cAExB,4BAAA;;;cAEW,uBAAA,SAAgC,4BAAA;EAAA,SAGlC,OAAA;EAAA,SACA,KAAA;AAAA;AAAA,cACN,8BAAA;;;cAEQ,yBAAA,SAAkC,8BAAA;EAAA,SAGpC,OAAA;AAAA;AAAA,KAGN,kBAAA,GAAqB,uBAAA,GAA0B,yBAAA;AAAA,cA6BvC,iBAAA,EAAiB,KAAA,CAAA,KAAA,CAAA,aAAA;AAAA,cAiFjB,iBAAA,EAAiB,KAAA,CAAA,KAAA,CAAA,aAAA,SAAA,aAAA;AAAA,cAEjB,cAAA,EAAc,KAAA,CAAA,KAAA,CAAA,aAAA"}
@@ -1,43 +1,67 @@
1
- import { normalizeSecretKey, resolveApiUrl } from "../../build/env-config.mjs";
2
- import { Context, Data, Effect, Layer } from "effect";
1
+ import { normalizeApiKey, resolveApiUrl } from "../../build/env-config.mjs";
2
+ import { Context, Data, Effect, Layer, Schedule } from "effect";
3
+ import { createBackoffSchedule } from "@interfere/effect-utils/retry";
3
4
  import { getPublicToken } from "@interfere/react/server/auth";
4
5
 
5
6
  //#region src/server/services/config.service.ts
6
7
  const ONE_HOUR_MS = 36e5;
7
8
  var ConfigError = class extends Data.TaggedError("ConfigError") {};
8
9
  const ConfigService = Context.GenericTag("@interfere/next/ConfigService");
10
+ const TokenExchange = Context.GenericTag("@interfere/next/TokenExchange");
11
+ var TokenExchangeCallFailed = class extends Data.TaggedError("TokenExchangeCallFailed") {};
12
+ var TokenExchangeReturnedNull = class extends Data.TaggedError("TokenExchangeReturnedNull") {};
13
+ const tokenExchangeSchedule = createBackoffSchedule().pipe(Schedule.whileInput((error) => error instanceof TokenExchangeCallFailed));
9
14
  let cachedPublicToken = null;
10
- const getSecretKey = Effect.fn("getSecretKey")(function* () {
11
- const normalized = normalizeSecretKey(process.env.INTERFERE_SECRET_KEY);
12
- if (!normalized) return yield* new ConfigError({ message: "INTERFERE_SECRET_KEY not set or empty" });
13
- return normalized;
15
+ const getApiKey = Effect.fn("getApiKey")(function* () {
16
+ const apiKey = normalizeApiKey(process.env.INTERFERE_API_KEY);
17
+ if (apiKey) return apiKey;
18
+ return yield* new ConfigError({ message: "INTERFERE_API_KEY not set. Set this environment variable and ensure you are using the correct key (Clerk API key scoped to a surface)." });
14
19
  });
15
- const getPublicTokenImpl = Effect.fn("getPublicTokenImpl")(function* () {
16
- const override = process.env.INTERFERE_PUBLIC_TOKEN;
17
- if (override) return override;
18
- const secret = yield* getSecretKey();
19
- const apiUrl = resolveApiUrl();
20
- const now = Date.now();
21
- if (cachedPublicToken && cachedPublicToken.expiresAt > now) return cachedPublicToken.token;
22
- const token = yield* Effect.tryPromise({
23
- try: () => getPublicToken({
20
+ const getSurfaceSlugOptional = () => Effect.sync(() => {
21
+ const fromBundle = globalThis.__INTERFERE_SURFACE_SLUG__;
22
+ if (typeof fromBundle === "string" && fromBundle.trim().length > 0) return fromBundle.trim();
23
+ });
24
+ const TokenExchangeLive = Layer.succeed(TokenExchange, TokenExchange.of({ exchange: ({ apiUrl, apiKey, surfaceSlug }) => Effect.tryPromise({
25
+ try: () => getPublicToken({
26
+ apiUrl,
27
+ apiKey,
28
+ surfaceSlug: surfaceSlug ?? void 0
29
+ }),
30
+ catch: (cause) => new TokenExchangeCallFailed({
31
+ message: `Failed to exchange API key for public token at ${apiUrl}/auth/exchange`,
32
+ cause
33
+ })
34
+ }).pipe(Effect.flatMap((token) => token ? Effect.succeed(token) : Effect.fail(new TokenExchangeReturnedNull({ message: `Token exchange at ${apiUrl}/auth/exchange returned null` })))) }));
35
+ const makeConfigService = Effect.gen(function* () {
36
+ const tokenExchange = yield* TokenExchange;
37
+ const getPublicToken = Effect.fn("getPublicToken")(function* () {
38
+ const override = process.env.INTERFERE_PUBLIC_TOKEN;
39
+ if (override) return override;
40
+ const apiKey = yield* getApiKey();
41
+ const surfaceSlug = yield* getSurfaceSlugOptional();
42
+ const apiUrl = resolveApiUrl();
43
+ const now = Date.now();
44
+ if (cachedPublicToken && cachedPublicToken.expiresAt > now) return cachedPublicToken.token;
45
+ const token = yield* tokenExchange.exchange({
24
46
  apiUrl,
25
- secret
26
- }),
27
- catch: (error) => new ConfigError({ message: `Failed to exchange secret for public token at ${apiUrl}/auth/exchange: ${error instanceof Error ? error.message : String(error)}` })
47
+ apiKey,
48
+ surfaceSlug
49
+ }).pipe(Effect.retry(tokenExchangeSchedule), Effect.mapError((error) => new ConfigError({ message: `Failed to exchange API key for public token at ${apiUrl}/auth/exchange: ${error.message}` })));
50
+ if (!token) return yield* new ConfigError({ message: `Token exchange at ${apiUrl}/auth/exchange returned empty response - check if the API URL is correct and the API key is valid` });
51
+ cachedPublicToken = {
52
+ token,
53
+ expiresAt: now + ONE_HOUR_MS
54
+ };
55
+ return token;
56
+ });
57
+ return ConfigService.of({
58
+ getApiUrl: Effect.succeed(resolveApiUrl()),
59
+ getApiKey: getApiKey(),
60
+ getPublicToken: getPublicToken()
28
61
  });
29
- if (!token) return yield* new ConfigError({ message: `Token exchange at ${apiUrl}/auth/exchange returned empty response - check if the API URL is correct and the secret key is valid` });
30
- cachedPublicToken = {
31
- token,
32
- expiresAt: now + ONE_HOUR_MS
33
- };
34
- return token;
35
62
  });
36
- const ConfigServiceLive = Layer.succeed(ConfigService, ConfigService.of({
37
- getApiUrl: Effect.succeed(resolveApiUrl()),
38
- getSecretKey: getSecretKey(),
39
- getPublicToken: getPublicTokenImpl()
40
- }));
63
+ const ConfigServiceLive = Layer.effect(ConfigService, makeConfigService);
64
+ const NextConfigLive = ConfigServiceLive.pipe(Layer.provide(TokenExchangeLive));
41
65
 
42
66
  //#endregion
43
- export { ConfigError, ConfigService, ConfigServiceLive };
67
+ export { ConfigError, ConfigService, ConfigServiceLive, NextConfigLive, TokenExchange, TokenExchangeCallFailed, TokenExchangeLive, TokenExchangeReturnedNull };
@@ -1 +1 @@
1
- {"version":3,"file":"config.service.mjs","names":["getPublicTokenShared"],"sources":["../../../src/server/services/config.service.ts"],"sourcesContent":["import { getPublicToken as getPublicTokenShared } from \"@interfere/react/server/auth\";\n\nimport { Context, Data, Effect, Layer } from \"effect\";\n\nimport { normalizeSecretKey, resolveApiUrl } from \"../../build/env-config.js\";\nimport type { NonEmptyString } from \"../../lib/types.js\";\n\nconst ONE_HOUR_MS = 3_600_000;\n\nexport class ConfigError extends Data.TaggedError(\"ConfigError\")<{\n readonly message: string;\n}> {}\n\nexport interface ConfigService {\n readonly getApiUrl: Effect.Effect<string>;\n readonly getSecretKey: Effect.Effect<NonEmptyString, ConfigError>;\n readonly getPublicToken: Effect.Effect<string, ConfigError>;\n}\n\nexport const ConfigService = Context.GenericTag<ConfigService>(\n \"@interfere/next/ConfigService\"\n);\n\nlet cachedPublicToken: { token: string; expiresAt: number } | null = null;\n\nconst getSecretKey = Effect.fn(\"getSecretKey\")(function* () {\n const normalized = normalizeSecretKey(process.env.INTERFERE_SECRET_KEY);\n\n if (!normalized) {\n return yield* new ConfigError({\n message: \"INTERFERE_SECRET_KEY not set or empty\",\n });\n }\n\n return normalized;\n});\n\nconst getPublicTokenImpl = Effect.fn(\"getPublicTokenImpl\")(function* () {\n const override = process.env.INTERFERE_PUBLIC_TOKEN;\n if (override) {\n return override;\n }\n\n const secret = yield* getSecretKey();\n const apiUrl = resolveApiUrl();\n\n const now = Date.now();\n if (cachedPublicToken && cachedPublicToken.expiresAt > now) {\n return cachedPublicToken.token;\n }\n\n const token = yield* Effect.tryPromise({\n try: () => getPublicTokenShared({ apiUrl, secret }),\n catch: (error) =>\n new ConfigError({\n message: `Failed to exchange secret for public token at ${apiUrl}/auth/exchange: ${error instanceof Error ? error.message : String(error)}`,\n }),\n });\n\n if (!token) {\n return yield* new ConfigError({\n message: `Token exchange at ${apiUrl}/auth/exchange returned empty response - check if the API URL is correct and the secret key is valid`,\n });\n }\n\n cachedPublicToken = {\n token,\n expiresAt: now + ONE_HOUR_MS,\n };\n\n return token;\n});\n\nexport const ConfigServiceLive = Layer.succeed(\n ConfigService,\n ConfigService.of({\n getApiUrl: Effect.succeed(resolveApiUrl()),\n getSecretKey: getSecretKey(),\n getPublicToken: getPublicTokenImpl(),\n })\n);\n"],"mappings":";;;;;AAOA,MAAM,cAAc;AAEpB,IAAa,cAAb,cAAiC,KAAK,YAAY,cAAc,CAE7D;AAQH,MAAa,gBAAgB,QAAQ,WACnC,gCACD;AAED,IAAI,oBAAiE;AAErE,MAAM,eAAe,OAAO,GAAG,eAAe,CAAC,aAAa;CAC1D,MAAM,aAAa,mBAAmB,QAAQ,IAAI,qBAAqB;AAEvE,KAAI,CAAC,WACH,QAAO,OAAO,IAAI,YAAY,EAC5B,SAAS,yCACV,CAAC;AAGJ,QAAO;EACP;AAEF,MAAM,qBAAqB,OAAO,GAAG,qBAAqB,CAAC,aAAa;CACtE,MAAM,WAAW,QAAQ,IAAI;AAC7B,KAAI,SACF,QAAO;CAGT,MAAM,SAAS,OAAO,cAAc;CACpC,MAAM,SAAS,eAAe;CAE9B,MAAM,MAAM,KAAK,KAAK;AACtB,KAAI,qBAAqB,kBAAkB,YAAY,IACrD,QAAO,kBAAkB;CAG3B,MAAM,QAAQ,OAAO,OAAO,WAAW;EACrC,WAAWA,eAAqB;GAAE;GAAQ;GAAQ,CAAC;EACnD,QAAQ,UACN,IAAI,YAAY,EACd,SAAS,iDAAiD,OAAO,kBAAkB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,IAC1I,CAAC;EACL,CAAC;AAEF,KAAI,CAAC,MACH,QAAO,OAAO,IAAI,YAAY,EAC5B,SAAS,qBAAqB,OAAO,uGACtC,CAAC;AAGJ,qBAAoB;EAClB;EACA,WAAW,MAAM;EAClB;AAED,QAAO;EACP;AAEF,MAAa,oBAAoB,MAAM,QACrC,eACA,cAAc,GAAG;CACf,WAAW,OAAO,QAAQ,eAAe,CAAC;CAC1C,cAAc,cAAc;CAC5B,gBAAgB,oBAAoB;CACrC,CAAC,CACH"}
1
+ {"version":3,"file":"config.service.mjs","names":["getPublicTokenShared"],"sources":["../../../src/server/services/config.service.ts"],"sourcesContent":["import { createBackoffSchedule } from \"@interfere/effect-utils/retry\";\nimport { getPublicToken as getPublicTokenShared } from \"@interfere/react/server/auth\";\n\nimport { Context, Data, Effect, Layer, Schedule } from \"effect\";\n\nimport { normalizeApiKey, resolveApiUrl } from \"../../build/env-config.js\";\nimport type { NonEmptyString } from \"../../lib/types.js\";\n\ndeclare global {\n var __INTERFERE_SURFACE_SLUG__: string | undefined;\n}\n\nconst ONE_HOUR_MS = 3_600_000;\n\nexport class ConfigError extends Data.TaggedError(\"ConfigError\")<{\n readonly message: string;\n}> {}\n\nexport interface ConfigService {\n readonly getApiKey: Effect.Effect<NonEmptyString, ConfigError>;\n readonly getApiUrl: Effect.Effect<string>;\n readonly getPublicToken: Effect.Effect<string, ConfigError>;\n}\n\nexport const ConfigService = Context.GenericTag<ConfigService>(\n \"@interfere/next/ConfigService\"\n);\n\nexport interface TokenExchange {\n readonly exchange: (args: {\n readonly apiUrl: string;\n readonly apiKey: NonEmptyString;\n readonly surfaceSlug?: NonEmptyString;\n }) => Effect.Effect<string, TokenExchangeError>;\n}\n\nexport const TokenExchange = Context.GenericTag<TokenExchange>(\n \"@interfere/next/TokenExchange\"\n);\n\nexport class TokenExchangeCallFailed extends Data.TaggedError(\n \"TokenExchangeCallFailed\"\n)<{\n readonly message: string;\n readonly cause: unknown;\n}> {}\n\nexport class TokenExchangeReturnedNull extends Data.TaggedError(\n \"TokenExchangeReturnedNull\"\n)<{\n readonly message: string;\n}> {}\n\ntype TokenExchangeError = TokenExchangeCallFailed | TokenExchangeReturnedNull;\n\nconst tokenExchangeSchedule = createBackoffSchedule().pipe(\n Schedule.whileInput((error) => error instanceof TokenExchangeCallFailed)\n);\n\nlet cachedPublicToken: { token: string; expiresAt: number } | null = null;\n\nconst getApiKey = Effect.fn(\"getApiKey\")(function* () {\n const apiKey = normalizeApiKey(process.env.INTERFERE_API_KEY);\n if (apiKey) {\n return apiKey;\n }\n\n return yield* new ConfigError({\n message:\n \"INTERFERE_API_KEY not set. Set this environment variable and ensure you are using the correct key (Clerk API key scoped to a surface).\",\n });\n});\n\nconst getSurfaceSlugOptional = (): Effect.Effect<NonEmptyString | undefined> =>\n Effect.sync(() => {\n const fromBundle = globalThis.__INTERFERE_SURFACE_SLUG__;\n if (typeof fromBundle === \"string\" && fromBundle.trim().length > 0) {\n return fromBundle.trim() as NonEmptyString;\n }\n return undefined;\n });\n\nexport const TokenExchangeLive = Layer.succeed(\n TokenExchange,\n TokenExchange.of({\n exchange: ({ apiUrl, apiKey, surfaceSlug }) =>\n Effect.tryPromise({\n try: () =>\n getPublicTokenShared({\n apiUrl,\n apiKey,\n surfaceSlug: surfaceSlug ?? undefined,\n }),\n catch: (cause) =>\n new TokenExchangeCallFailed({\n message: `Failed to exchange API key for public token at ${apiUrl}/auth/exchange`,\n cause,\n }),\n }).pipe(\n Effect.flatMap((token) =>\n token\n ? Effect.succeed(token)\n : Effect.fail(\n new TokenExchangeReturnedNull({\n message: `Token exchange at ${apiUrl}/auth/exchange returned null`,\n })\n )\n )\n ),\n })\n);\n\nconst makeConfigService = Effect.gen(function* () {\n const tokenExchange = yield* TokenExchange;\n\n const getPublicToken = Effect.fn(\"getPublicToken\")(function* () {\n const override = process.env.INTERFERE_PUBLIC_TOKEN;\n if (override) {\n return override;\n }\n\n const apiKey = yield* getApiKey();\n const surfaceSlug = yield* getSurfaceSlugOptional();\n const apiUrl = resolveApiUrl();\n\n const now = Date.now();\n if (cachedPublicToken && cachedPublicToken.expiresAt > now) {\n return cachedPublicToken.token;\n }\n\n const token = yield* tokenExchange\n .exchange({ apiUrl, apiKey, surfaceSlug })\n .pipe(\n Effect.retry(tokenExchangeSchedule),\n Effect.mapError(\n (error) =>\n new ConfigError({\n message: `Failed to exchange API key for public token at ${apiUrl}/auth/exchange: ${error.message}`,\n })\n )\n );\n\n if (!token) {\n return yield* new ConfigError({\n message: `Token exchange at ${apiUrl}/auth/exchange returned empty response - check if the API URL is correct and the API key is valid`,\n });\n }\n\n cachedPublicToken = {\n token,\n expiresAt: now + ONE_HOUR_MS,\n };\n\n return token;\n });\n\n return ConfigService.of({\n getApiUrl: Effect.succeed(resolveApiUrl()),\n getApiKey: getApiKey(),\n getPublicToken: getPublicToken(),\n });\n});\n\nexport const ConfigServiceLive = Layer.effect(ConfigService, makeConfigService);\n\nexport const NextConfigLive = ConfigServiceLive.pipe(\n Layer.provide(TokenExchangeLive)\n);\n"],"mappings":";;;;;;AAYA,MAAM,cAAc;AAEpB,IAAa,cAAb,cAAiC,KAAK,YAAY,cAAc,CAE7D;AAQH,MAAa,gBAAgB,QAAQ,WACnC,gCACD;AAUD,MAAa,gBAAgB,QAAQ,WACnC,gCACD;AAED,IAAa,0BAAb,cAA6C,KAAK,YAChD,0BACD,CAGE;AAEH,IAAa,4BAAb,cAA+C,KAAK,YAClD,4BACD,CAEE;AAIH,MAAM,wBAAwB,uBAAuB,CAAC,KACpD,SAAS,YAAY,UAAU,iBAAiB,wBAAwB,CACzE;AAED,IAAI,oBAAiE;AAErE,MAAM,YAAY,OAAO,GAAG,YAAY,CAAC,aAAa;CACpD,MAAM,SAAS,gBAAgB,QAAQ,IAAI,kBAAkB;AAC7D,KAAI,OACF,QAAO;AAGT,QAAO,OAAO,IAAI,YAAY,EAC5B,SACE,0IACH,CAAC;EACF;AAEF,MAAM,+BACJ,OAAO,WAAW;CAChB,MAAM,aAAa,WAAW;AAC9B,KAAI,OAAO,eAAe,YAAY,WAAW,MAAM,CAAC,SAAS,EAC/D,QAAO,WAAW,MAAM;EAG1B;AAEJ,MAAa,oBAAoB,MAAM,QACrC,eACA,cAAc,GAAG,EACf,WAAW,EAAE,QAAQ,QAAQ,kBAC3B,OAAO,WAAW;CAChB,WACEA,eAAqB;EACnB;EACA;EACA,aAAa,eAAe;EAC7B,CAAC;CACJ,QAAQ,UACN,IAAI,wBAAwB;EAC1B,SAAS,kDAAkD,OAAO;EAClE;EACD,CAAC;CACL,CAAC,CAAC,KACD,OAAO,SAAS,UACd,QACI,OAAO,QAAQ,MAAM,GACrB,OAAO,KACL,IAAI,0BAA0B,EAC5B,SAAS,qBAAqB,OAAO,+BACtC,CAAC,CACH,CACN,CACF,EACJ,CAAC,CACH;AAED,MAAM,oBAAoB,OAAO,IAAI,aAAa;CAChD,MAAM,gBAAgB,OAAO;CAE7B,MAAM,iBAAiB,OAAO,GAAG,iBAAiB,CAAC,aAAa;EAC9D,MAAM,WAAW,QAAQ,IAAI;AAC7B,MAAI,SACF,QAAO;EAGT,MAAM,SAAS,OAAO,WAAW;EACjC,MAAM,cAAc,OAAO,wBAAwB;EACnD,MAAM,SAAS,eAAe;EAE9B,MAAM,MAAM,KAAK,KAAK;AACtB,MAAI,qBAAqB,kBAAkB,YAAY,IACrD,QAAO,kBAAkB;EAG3B,MAAM,QAAQ,OAAO,cAClB,SAAS;GAAE;GAAQ;GAAQ;GAAa,CAAC,CACzC,KACC,OAAO,MAAM,sBAAsB,EACnC,OAAO,UACJ,UACC,IAAI,YAAY,EACd,SAAS,kDAAkD,OAAO,kBAAkB,MAAM,WAC3F,CAAC,CACL,CACF;AAEH,MAAI,CAAC,MACH,QAAO,OAAO,IAAI,YAAY,EAC5B,SAAS,qBAAqB,OAAO,oGACtC,CAAC;AAGJ,sBAAoB;GAClB;GACA,WAAW,MAAM;GAClB;AAED,SAAO;GACP;AAEF,QAAO,cAAc,GAAG;EACtB,WAAW,OAAO,QAAQ,eAAe,CAAC;EAC1C,WAAW,WAAW;EACtB,gBAAgB,gBAAgB;EACjC,CAAC;EACF;AAEF,MAAa,oBAAoB,MAAM,OAAO,eAAe,kBAAkB;AAE/E,MAAa,iBAAiB,kBAAkB,KAC9C,MAAM,QAAQ,kBAAkB,CACjC"}
@@ -1 +1 @@
1
- {"version":3,"file":"error-tracking.service.d.mts","names":[],"sources":["../../../src/server/services/error-tracking.service.ts"],"mappings":";;;;;;cAUoD,uBAAA;;;cAEvC,kBAAA,SAA2B,uBAAA;EAAA,SAAA,OAAA;AAAA;AAAA,UAIvB,oBAAA;EAAA,SAAA,YAAA,GAAA,KAAA,WAAA,OAAA,GAGH,OAAA,EAAA,OAAA,GACA,MAAA,sBACP,MAAA,CAAO,MAAA,cAAoB,aAAA;AAAA;AAAA,cAGrB,oBAAA,EAAoB,OAAA,CAAA,GAAA,CAAA,oBAAA,EAAA,oBAAA;AAAA,cAIpB,wBAAA,EAAwB,KAAA,CAAA,KAAA,CAAA,oBAAA"}
1
+ {"version":3,"file":"error-tracking.service.d.mts","names":[],"sources":["../../../src/server/services/error-tracking.service.ts"],"mappings":";;;;;;cAUoD,uBAAA;;;cAEvC,kBAAA,SAA2B,uBAAA;EAAA,SAC7B,OAAA;AAAA;AAAA,UAGM,oBAAA;EAAA,SACN,YAAA,GACP,KAAA,WACA,OAAA,GAAU,OAAA,EACV,OAAA,GAAU,MAAA,sBACP,MAAA,CAAO,MAAA,cAAoB,aAAA;AAAA;AAAA,cAGrB,oBAAA,EAAoB,OAAA,CAAA,GAAA,CAAA,oBAAA,EAAA,oBAAA;AAAA,cAIpB,wBAAA,EAAwB,KAAA,CAAA,KAAA,CAAA,oBAAA"}
@@ -24,7 +24,7 @@ const ErrorTrackingServiceLive = Layer.succeed(ErrorTrackingService, ErrorTracki
24
24
  sessionResolverDeps: { getAsyncContext: () => getSessionContext() }
25
25
  });
26
26
  yield* Effect.tryPromise({
27
- try: () => sendEnvelopesToIngest([envelope], `${apiUrl}${API_PATHS.INGEST_V0}`, token),
27
+ try: () => sendEnvelopesToIngest([envelope], `${apiUrl}${API_PATHS.INGEST_V1}`, token),
28
28
  catch: () => new ErrorTrackingError({ message: "Failed to send error to ingest" })
29
29
  }).pipe(Effect.catchAll(() => Effect.void));
30
30
  }) }));
@@ -1 +1 @@
1
- {"version":3,"file":"error-tracking.service.mjs","names":[],"sources":["../../../src/server/services/error-tracking.service.ts"],"sourcesContent":["import { API_PATHS } from \"@interfere/constants/api\";\nimport { getRuntime } from \"@interfere/react/core/runtime/config\";\nimport {\n buildServerErrorEnvelope,\n sendEnvelopesToIngest,\n} from \"@interfere/react/server/capture\";\n\nimport { Context, Data, Effect, Layer } from \"effect\";\n\nimport { getSessionContext } from \"../session-context.js\";\nimport { ConfigService } from \"./config.service.js\";\n\nexport class ErrorTrackingError extends Data.TaggedError(\"ErrorTrackingError\")<{\n readonly message: string;\n}> {}\n\nexport interface ErrorTrackingService {\n readonly captureError: (\n error: unknown,\n request?: Request,\n context?: Record<string, unknown>\n ) => Effect.Effect<void, never, ConfigService>;\n}\n\nexport const ErrorTrackingService = Context.GenericTag<ErrorTrackingService>(\n \"@interfere/next/ErrorTrackingService\"\n);\n\nexport const ErrorTrackingServiceLive = Layer.succeed(\n ErrorTrackingService,\n ErrorTrackingService.of({\n captureError: Effect.fn(\"captureError\")(\n function* (error, request, context) {\n const config = yield* ConfigService;\n const token = yield* config.getPublicToken.pipe(\n Effect.catchAll(() => Effect.succeed(null))\n );\n\n if (!token) {\n return;\n }\n\n const apiUrl = yield* config.getApiUrl;\n const inferredValues = {\n buildId: process.env.NEXT_PUBLIC_INTERFERE_BUILD_ID ?? null,\n releaseId: process.env.NEXT_PUBLIC_INTERFERE_RELEASE_ID ?? null,\n environment: process.env.NODE_ENV || null,\n runtime: getRuntime(),\n };\n\n const envelope = buildServerErrorEnvelope({\n ...inferredValues,\n error,\n request,\n context: {\n ...context,\n },\n sessionResolverDeps: {\n getAsyncContext: () => getSessionContext(),\n },\n });\n\n yield* Effect.tryPromise({\n try: () =>\n sendEnvelopesToIngest(\n [envelope],\n `${apiUrl}${API_PATHS.INGEST_V0}`,\n token\n ),\n catch: () =>\n new ErrorTrackingError({\n message: \"Failed to send error to ingest\",\n }),\n }).pipe(\n Effect.catchAll(() => Effect.void) // Best-effort\n );\n }\n ),\n })\n);\n"],"mappings":";;;;;;;;AAYA,IAAa,qBAAb,cAAwC,KAAK,YAAY,qBAAqB,CAE3E;AAUH,MAAa,uBAAuB,QAAQ,WAC1C,uCACD;AAED,MAAa,2BAA2B,MAAM,QAC5C,sBACA,qBAAqB,GAAG,EACtB,cAAc,OAAO,GAAG,eAAe,CACrC,WAAW,OAAO,SAAS,SAAS;CAClC,MAAM,SAAS,OAAO;CACtB,MAAM,QAAQ,OAAO,OAAO,eAAe,KACzC,OAAO,eAAe,OAAO,QAAQ,KAAK,CAAC,CAC5C;AAED,KAAI,CAAC,MACH;CAGF,MAAM,SAAS,OAAO,OAAO;CAQ7B,MAAM,WAAW,yBAAyB;EANxC,SAAS,QAAQ,IAAI,kCAAkC;EACvD,WAAW,QAAQ,IAAI,oCAAoC;EAC3D,aAAa,QAAQ,IAAI,YAAY;EACrC,SAAS,YAAY;EAKrB;EACA;EACA,SAAS,EACP,GAAG,SACJ;EACD,qBAAqB,EACnB,uBAAuB,mBAAmB,EAC3C;EACF,CAAC;AAEF,QAAO,OAAO,WAAW;EACvB,WACE,sBACE,CAAC,SAAS,EACV,GAAG,SAAS,UAAU,aACtB,MACD;EACH,aACE,IAAI,mBAAmB,EACrB,SAAS,kCACV,CAAC;EACL,CAAC,CAAC,KACD,OAAO,eAAe,OAAO,KAAK,CACnC;EAEJ,EACF,CAAC,CACH"}
1
+ {"version":3,"file":"error-tracking.service.mjs","names":[],"sources":["../../../src/server/services/error-tracking.service.ts"],"sourcesContent":["import { API_PATHS } from \"@interfere/constants/api\";\nimport { getRuntime } from \"@interfere/react/core/runtime/config\";\nimport {\n buildServerErrorEnvelope,\n sendEnvelopesToIngest,\n} from \"@interfere/react/server/capture\";\n\nimport { Context, Data, Effect, Layer } from \"effect\";\n\nimport { getSessionContext } from \"../session-context.js\";\nimport { ConfigService } from \"./config.service.js\";\n\nexport class ErrorTrackingError extends Data.TaggedError(\"ErrorTrackingError\")<{\n readonly message: string;\n}> {}\n\nexport interface ErrorTrackingService {\n readonly captureError: (\n error: unknown,\n request?: Request,\n context?: Record<string, unknown>\n ) => Effect.Effect<void, never, ConfigService>;\n}\n\nexport const ErrorTrackingService = Context.GenericTag<ErrorTrackingService>(\n \"@interfere/next/ErrorTrackingService\"\n);\n\nexport const ErrorTrackingServiceLive = Layer.succeed(\n ErrorTrackingService,\n ErrorTrackingService.of({\n captureError: Effect.fn(\"captureError\")(\n function* (error, request, context) {\n const config = yield* ConfigService;\n const token = yield* config.getPublicToken.pipe(\n Effect.catchAll(() => Effect.succeed(null))\n );\n\n if (!token) {\n return;\n }\n\n const apiUrl = yield* config.getApiUrl;\n const inferredValues = {\n buildId: process.env.NEXT_PUBLIC_INTERFERE_BUILD_ID ?? null,\n releaseId: process.env.NEXT_PUBLIC_INTERFERE_RELEASE_ID ?? null,\n environment: process.env.NODE_ENV || null,\n runtime: getRuntime(),\n };\n\n const envelope = buildServerErrorEnvelope({\n ...inferredValues,\n error,\n request,\n context: {\n ...context,\n },\n sessionResolverDeps: {\n getAsyncContext: () => getSessionContext(),\n },\n });\n\n yield* Effect.tryPromise({\n try: () =>\n sendEnvelopesToIngest(\n [envelope],\n `${apiUrl}${API_PATHS.INGEST_V1}`,\n token\n ),\n catch: () =>\n new ErrorTrackingError({\n message: \"Failed to send error to ingest\",\n }),\n }).pipe(\n Effect.catchAll(() => Effect.void) // Best-effort\n );\n }\n ),\n })\n);\n"],"mappings":";;;;;;;;AAYA,IAAa,qBAAb,cAAwC,KAAK,YAAY,qBAAqB,CAE3E;AAUH,MAAa,uBAAuB,QAAQ,WAC1C,uCACD;AAED,MAAa,2BAA2B,MAAM,QAC5C,sBACA,qBAAqB,GAAG,EACtB,cAAc,OAAO,GAAG,eAAe,CACrC,WAAW,OAAO,SAAS,SAAS;CAClC,MAAM,SAAS,OAAO;CACtB,MAAM,QAAQ,OAAO,OAAO,eAAe,KACzC,OAAO,eAAe,OAAO,QAAQ,KAAK,CAAC,CAC5C;AAED,KAAI,CAAC,MACH;CAGF,MAAM,SAAS,OAAO,OAAO;CAQ7B,MAAM,WAAW,yBAAyB;EANxC,SAAS,QAAQ,IAAI,kCAAkC;EACvD,WAAW,QAAQ,IAAI,oCAAoC;EAC3D,aAAa,QAAQ,IAAI,YAAY;EACrC,SAAS,YAAY;EAKrB;EACA;EACA,SAAS,EACP,GAAG,SACJ;EACD,qBAAqB,EACnB,uBAAuB,mBAAmB,EAC3C;EACF,CAAC;AAEF,QAAO,OAAO,WAAW;EACvB,WACE,sBACE,CAAC,SAAS,EACV,GAAG,SAAS,UAAU,aACtB,MACD;EACH,aACE,IAAI,mBAAmB,EACrB,SAAS,kCACV,CAAC;EACL,CAAC,CAAC,KACD,OAAO,eAAe,OAAO,KAAK,CACnC;EAEJ,EACF,CAAC,CACH"}
@@ -10,20 +10,20 @@ import { AsyncLocalStorage } from "node:async_hooks";
10
10
  */
11
11
  interface SessionContext {
12
12
  /**
13
- * The browser session ID from the x-interfere-request header.
14
- * May be null if the header wasn't provided.
15
- */
16
- readonly sessionId: string | null;
13
+ * Timestamp when this context was created.
14
+ * Used for debugging and monitoring.
15
+ */
16
+ readonly createdAt: number;
17
17
  /**
18
- * The request ID from the x-interfere-request header.
19
- * Used for correlating with the session cache.
20
- */
18
+ * The request ID from the x-interfere-request header.
19
+ * Used for correlating with the session cache.
20
+ */
21
21
  readonly requestId: string | null;
22
22
  /**
23
- * Timestamp when this context was created.
24
- * Used for debugging and monitoring.
25
- */
26
- readonly createdAt: number;
23
+ * The browser session ID from the x-interfere-request header.
24
+ * May be null if the header wasn't provided.
25
+ */
26
+ readonly sessionId: string | null;
27
27
  }
28
28
  /**
29
29
  * AsyncLocalStorage instance for session context.
@@ -1 +1 @@
1
- {"version":3,"file":"session-context.d.mts","names":[],"sources":["../../src/server/session-context.ts"],"mappings":";;;;AASA;AA0BA;AAQA;;;;UAlCiB,cAAA;EAAA;AA0BjB;AAQA;;EAlCiB,SAAA,SAAA;EAAA;AA0BjB;AAQA;;EAlCiB,SAAA,SAAA;EAAA;AA0BjB;AAQA;;EAlCiB,SAAA,SAAA;AAAA;AAAA;AA0BjB;AAQA;;;;AAlCiB,cA0BJ,qBAAA,EAAqB,iBAAA,CAAA,cAAA;AAAA;AAQlC;;;;;AARkC,iBAQlB,qBAAA,GAAA,CAAA,OAAA,EACL,cAAA,EAAA,EAAA,QACC,CAAA,GACT,CAAA;AAAA;;AAUH;AASA;AAsBA;;AAzCG,iBAUa,iBAAA,CAAA,GAAqB,cAAA;AAAA;AASrC;AAsBA;;;AA/BqC,iBASrB,2BAAA,CAAA,WAAA,8BAEb,IAAA,CAAK,cAAA;AAAA;AAoBR;;AApBQ,iBAoBQ,+BAAA,CAAA,OAAA,EACL,OAAA,GACR,cAAA"}
1
+ {"version":3,"file":"session-context.d.mts","names":[],"sources":["../../src/server/session-context.ts"],"mappings":";;;;;AASA;;;;;UAAiB,cAAA;EAgBN;;;AASX;EATW,SAXA,SAAA;;;;AA4BX;WAtBW,SAAA;EAsB0B;;;;EAAA,SAjB1B,SAAA;AAAA;;;;;;;cASE,qBAAA,EAAqB,iBAAA,CAAA,cAAA;;AAqBlC;;;;;iBAbgB,qBAAA,GAAA,CACd,OAAA,EAAS,cAAA,EACT,EAAA,QAAU,CAAA,GACT,CAAA;;;;;;;iBAUa,iBAAA,CAAA,GAAqB,cAAA;;AA+BrC;;;;iBAtBgB,2BAAA,CACd,WAAA,8BACC,IAAA,CAAK,cAAA;;;;iBAoBQ,+BAAA,CACd,OAAA,EAAS,OAAA,GACR,cAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"session-context.mjs","names":[],"sources":["../../src/server/session-context.ts"],"sourcesContent":["import { AsyncLocalStorage } from \"node:async_hooks\";\n\n/**\n * Session context stored in AsyncLocalStorage.\n *\n * This allows session information to propagate through async call chains,\n * enabling proper session correlation even in global error handlers\n * that don't have direct access to the request object.\n */\nexport interface SessionContext {\n /**\n * The browser session ID from the x-interfere-request header.\n * May be null if the header wasn't provided.\n */\n readonly sessionId: string | null;\n\n /**\n * The request ID from the x-interfere-request header.\n * Used for correlating with the session cache.\n */\n readonly requestId: string | null;\n\n /**\n * Timestamp when this context was created.\n * Used for debugging and monitoring.\n */\n readonly createdAt: number;\n}\n\n/**\n * AsyncLocalStorage instance for session context.\n *\n * This is a singleton that persists across the Node.js process.\n * Each async execution context gets its own isolated store.\n */\nexport const sessionContextStorage = new AsyncLocalStorage<SessionContext>();\n\n/**\n * Run a function with session context.\n *\n * The context will be available to all sync and async code\n * within the callback via `getSessionContext()`.\n */\nexport function runWithSessionContext<T>(\n context: SessionContext,\n fn: () => T\n): T {\n return sessionContextStorage.run(context, fn);\n}\n\n/**\n * Get the current session context from AsyncLocalStorage.\n *\n * Returns undefined if called outside of a session context\n * (i.e., not within a `runWithSessionContext` callback).\n */\nexport function getSessionContext(): SessionContext | undefined {\n return sessionContextStorage.getStore();\n}\n\n/**\n * Parse the x-interfere-request header and create a SessionContext.\n *\n * Format: {sessionId}/{requestId}\n */\nexport function parseInterfereRequestHeader(\n headerValue: string | null | undefined\n): Pick<SessionContext, \"sessionId\" | \"requestId\"> {\n if (!headerValue) {\n return { sessionId: null, requestId: null };\n }\n\n const parts = headerValue.split(\"/\");\n\n if (parts.length !== 2) {\n return { sessionId: null, requestId: null };\n }\n\n return {\n sessionId: parts[0] || null,\n requestId: parts[1] || null,\n };\n}\n\n/**\n * Create a SessionContext from a Request object.\n */\nexport function createSessionContextFromRequest(\n request: Request\n): SessionContext {\n const headerValue = request.headers.get(\"x-interfere-request\");\n const { sessionId, requestId } = parseInterfereRequestHeader(headerValue);\n\n return {\n sessionId,\n requestId,\n createdAt: Date.now(),\n };\n}\n"],"mappings":";;;;;;;;;AAmCA,MAAa,wBAAwB,IAAI,mBAAmC;;;;;;;AAQ5E,SAAgB,sBACd,SACA,IACG;AACH,QAAO,sBAAsB,IAAI,SAAS,GAAG;;;;;;;;AAS/C,SAAgB,oBAAgD;AAC9D,QAAO,sBAAsB,UAAU;;;;;;;AAQzC,SAAgB,4BACd,aACiD;AACjD,KAAI,CAAC,YACH,QAAO;EAAE,WAAW;EAAM,WAAW;EAAM;CAG7C,MAAM,QAAQ,YAAY,MAAM,IAAI;AAEpC,KAAI,MAAM,WAAW,EACnB,QAAO;EAAE,WAAW;EAAM,WAAW;EAAM;AAG7C,QAAO;EACL,WAAW,MAAM,MAAM;EACvB,WAAW,MAAM,MAAM;EACxB;;;;;AAMH,SAAgB,gCACd,SACgB;CAEhB,MAAM,EAAE,WAAW,cAAc,4BADb,QAAQ,QAAQ,IAAI,sBAAsB,CACW;AAEzE,QAAO;EACL;EACA;EACA,WAAW,KAAK,KAAK;EACtB"}
1
+ {"version":3,"file":"session-context.mjs","names":[],"sources":["../../src/server/session-context.ts"],"sourcesContent":["import { AsyncLocalStorage } from \"node:async_hooks\";\n\n/**\n * Session context stored in AsyncLocalStorage.\n *\n * This allows session information to propagate through async call chains,\n * enabling proper session correlation even in global error handlers\n * that don't have direct access to the request object.\n */\nexport interface SessionContext {\n /**\n * Timestamp when this context was created.\n * Used for debugging and monitoring.\n */\n readonly createdAt: number;\n\n /**\n * The request ID from the x-interfere-request header.\n * Used for correlating with the session cache.\n */\n readonly requestId: string | null;\n /**\n * The browser session ID from the x-interfere-request header.\n * May be null if the header wasn't provided.\n */\n readonly sessionId: string | null;\n}\n\n/**\n * AsyncLocalStorage instance for session context.\n *\n * This is a singleton that persists across the Node.js process.\n * Each async execution context gets its own isolated store.\n */\nexport const sessionContextStorage = new AsyncLocalStorage<SessionContext>();\n\n/**\n * Run a function with session context.\n *\n * The context will be available to all sync and async code\n * within the callback via `getSessionContext()`.\n */\nexport function runWithSessionContext<T>(\n context: SessionContext,\n fn: () => T\n): T {\n return sessionContextStorage.run(context, fn);\n}\n\n/**\n * Get the current session context from AsyncLocalStorage.\n *\n * Returns undefined if called outside of a session context\n * (i.e., not within a `runWithSessionContext` callback).\n */\nexport function getSessionContext(): SessionContext | undefined {\n return sessionContextStorage.getStore();\n}\n\n/**\n * Parse the x-interfere-request header and create a SessionContext.\n *\n * Format: {sessionId}/{requestId}\n */\nexport function parseInterfereRequestHeader(\n headerValue: string | null | undefined\n): Pick<SessionContext, \"sessionId\" | \"requestId\"> {\n if (!headerValue) {\n return { sessionId: null, requestId: null };\n }\n\n const parts = headerValue.split(\"/\");\n\n if (parts.length !== 2) {\n return { sessionId: null, requestId: null };\n }\n\n return {\n sessionId: parts[0] || null,\n requestId: parts[1] || null,\n };\n}\n\n/**\n * Create a SessionContext from a Request object.\n */\nexport function createSessionContextFromRequest(\n request: Request\n): SessionContext {\n const headerValue = request.headers.get(\"x-interfere-request\");\n const { sessionId, requestId } = parseInterfereRequestHeader(headerValue);\n\n return {\n sessionId,\n requestId,\n createdAt: Date.now(),\n };\n}\n"],"mappings":";;;;;;;;;AAkCA,MAAa,wBAAwB,IAAI,mBAAmC;;;;;;;AAQ5E,SAAgB,sBACd,SACA,IACG;AACH,QAAO,sBAAsB,IAAI,SAAS,GAAG;;;;;;;;AAS/C,SAAgB,oBAAgD;AAC9D,QAAO,sBAAsB,UAAU;;;;;;;AAQzC,SAAgB,4BACd,aACiD;AACjD,KAAI,CAAC,YACH,QAAO;EAAE,WAAW;EAAM,WAAW;EAAM;CAG7C,MAAM,QAAQ,YAAY,MAAM,IAAI;AAEpC,KAAI,MAAM,WAAW,EACnB,QAAO;EAAE,WAAW;EAAM,WAAW;EAAM;AAG7C,QAAO;EACL,WAAW,MAAM,MAAM;EACvB,WAAW,MAAM,MAAM;EACxB;;;;;AAMH,SAAgB,gCACd,SACgB;CAEhB,MAAM,EAAE,WAAW,cAAc,4BADb,QAAQ,QAAQ,IAAI,sBAAsB,CACW;AAEzE,QAAO;EACL;EACA;EACA,WAAW,KAAK,KAAK;EACtB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@interfere/next",
3
- "version": "0.1.0-alpha.10",
3
+ "version": "0.1.0-alpha.11",
4
4
  "license": "MIT",
5
5
  "description": "Build apps that never break.",
6
6
  "keywords": [
@@ -16,6 +16,11 @@
16
16
  "url": "mailto:support@interfere.com"
17
17
  },
18
18
  "author": "Interfere <support@interfere.com> (https://interfere.com)",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/interfere-inc/interfere.git",
22
+ "directory": "src/packages/public/next"
23
+ },
19
24
  "files": [
20
25
  "dist"
21
26
  ],
@@ -85,17 +90,23 @@
85
90
  "publishConfig": {
86
91
  "access": "public"
87
92
  },
93
+ "scripts": {
94
+ "build": "tsdown",
95
+ "dev": "tsdown --watch",
96
+ "typecheck": "tsc --noEmit --incremental",
97
+ "test": "vitest run --coverage"
98
+ },
88
99
  "dependencies": {
89
- "@effect/platform": "^0.94.1",
90
- "chalk": "^5.6.2",
91
- "effect": "^3.19.14",
92
- "glob": "^13.0.0",
93
- "uuid": "^13.0.0",
94
- "zod": "^4.3.5",
95
- "@interfere/effect-utils": "0.0.2-alpha.2",
96
- "@interfere/constants": "0.0.2-alpha.0",
97
- "@interfere/react": "0.1.0-alpha.5",
98
- "@interfere/types": "0.1.0-alpha.1"
100
+ "@effect/platform": "catalog:",
101
+ "@interfere/constants": "workspace:*",
102
+ "@interfere/effect-utils": "workspace:*",
103
+ "@interfere/react": "workspace:*",
104
+ "@interfere/types": "workspace:*",
105
+ "chalk": "catalog:",
106
+ "effect": "catalog:",
107
+ "glob": "catalog:",
108
+ "uuid": "catalog:",
109
+ "zod": "catalog:"
99
110
  },
100
111
  "peerDependencies": {
101
112
  "@vercel/blob": "^2",
@@ -104,27 +115,20 @@
104
115
  "react-dom": ">=19"
105
116
  },
106
117
  "devDependencies": {
107
- "@types/node": "^22.19.7",
108
- "@types/react": "19.2.8",
109
- "@types/react-dom": "19.2.3",
110
- "@vitest/coverage-v8": "^4.0.17",
111
- "jsdom": "^27.4.0",
112
- "msw": "^2.12.7",
113
- "next": "^16.1.2",
114
- "react": "^19.2.3",
115
- "react-dom": "^19.2.3",
116
- "tsdown": "0.20.0-beta.3",
117
- "typescript": "5.9.3",
118
- "vitest": "^4.0.17",
119
- "webpack": "^5.104.1",
120
- "@interfere/test-utils": "0.0.0",
121
- "@interfere/typescript-config": "1.0.3-alpha.0",
122
- "@interfere/vitest-config": "1.0.1-alpha.0"
123
- },
124
- "scripts": {
125
- "build": "tsdown",
126
- "dev": "tsdown --watch",
127
- "typecheck": "tsc --noEmit --incremental",
128
- "test": "vitest run --coverage"
118
+ "@effect/vitest": "catalog:",
119
+ "@interfere/typescript-config": "workspace:*",
120
+ "@interfere/vitest-config": "workspace:*",
121
+ "@types/node": "catalog:",
122
+ "@types/react": "catalog:",
123
+ "@types/react-dom": "catalog:",
124
+ "@vitest/coverage-v8": "catalog:",
125
+ "jsdom": "catalog:",
126
+ "next": "catalog:",
127
+ "react": "catalog:",
128
+ "react-dom": "catalog:",
129
+ "tsdown": "catalog:",
130
+ "typescript": "catalog:",
131
+ "vitest": "catalog:",
132
+ "webpack": "catalog:"
129
133
  }
130
- }
134
+ }
@@ -1,10 +0,0 @@
1
- //#region src/build/secret-key.d.ts
2
- /**
3
- * Parse surface slug from secret key
4
- * Format: int_sk_{surfaceSlug}_{random}
5
- * Note: This is for display/validation purposes only.
6
- * The actual surface lookup should use the key hash via Redis/DB.
7
- */
8
- declare function parseSurfaceSlugFromSecretKey(secretKey: string): string | undefined;
9
- //#endregion
10
- export { parseSurfaceSlugFromSecretKey };
@@ -1 +0,0 @@
1
- {"version":3,"file":"secret-key.d.mts","names":[],"sources":["../../src/build/secret-key.ts"],"mappings":";;AAQA;;;;;iBAAgB,6BAAA,CAAA,SAAA"}
@@ -1,16 +0,0 @@
1
- import { SECRET_KEY_REGEX } from "@interfere/types/auth/secret-key";
2
-
3
- //#region src/build/secret-key.ts
4
- /**
5
- * Parse surface slug from secret key
6
- * Format: int_sk_{surfaceSlug}_{random}
7
- * Note: This is for display/validation purposes only.
8
- * The actual surface lookup should use the key hash via Redis/DB.
9
- */
10
- function parseSurfaceSlugFromSecretKey(secretKey) {
11
- const match = secretKey.match(SECRET_KEY_REGEX);
12
- if (match) return match[1];
13
- }
14
-
15
- //#endregion
16
- export { parseSurfaceSlugFromSecretKey };
@@ -1 +0,0 @@
1
- {"version":3,"file":"secret-key.mjs","names":[],"sources":["../../src/build/secret-key.ts"],"sourcesContent":["import { SECRET_KEY_REGEX } from \"@interfere/types/auth/secret-key\";\n\n/**\n * Parse surface slug from secret key\n * Format: int_sk_{surfaceSlug}_{random}\n * Note: This is for display/validation purposes only.\n * The actual surface lookup should use the key hash via Redis/DB.\n */\nexport function parseSurfaceSlugFromSecretKey(\n secretKey: string\n): string | undefined {\n const match = secretKey.match(SECRET_KEY_REGEX);\n\n if (match) {\n return match[1];\n }\n\n return;\n}\n"],"mappings":";;;;;;;;;AAQA,SAAgB,8BACd,WACoB;CACpB,MAAM,QAAQ,UAAU,MAAM,iBAAiB;AAE/C,KAAI,MACF,QAAO,MAAM"}
@@ -1,6 +0,0 @@
1
- import { NextRequest } from "next/server";
2
-
3
- //#region src/lib/test-utils/make-next-request.d.ts
4
- declare function makeNextRequest(url: string | URL, init?: RequestInit): NextRequest;
5
- //#endregion
6
- export { makeNextRequest };
@@ -1 +0,0 @@
1
- {"version":3,"file":"make-next-request.d.mts","names":[],"sources":["../../../src/lib/test-utils/make-next-request.ts"],"mappings":";;;iBAGgB,eAAA,CAAA,GAAA,WACA,GAAA,EAAA,IAAA,GACP,WAAA,GACN,WAAA"}
@@ -1,12 +0,0 @@
1
- import { NextRequest } from "next/server";
2
-
3
- //#region src/lib/test-utils/make-next-request.ts
4
- function makeNextRequest(url, init) {
5
- return new NextRequest(url, {
6
- ...init,
7
- signal: init?.signal ?? void 0
8
- });
9
- }
10
-
11
- //#endregion
12
- export { makeNextRequest };