@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.
- package/LICENSE +1 -1
- package/README.md +3 -3
- package/dist/_virtual/{rolldown_runtime.mjs → _rolldown/runtime.mjs} +1 -1
- package/dist/build/env-config.d.mts +3 -1
- package/dist/build/env-config.d.mts.map +1 -1
- package/dist/build/env-config.mjs +9 -1
- package/dist/build/env-config.mjs.map +1 -1
- package/dist/build/exchange-surface.d.mts +9 -0
- package/dist/build/exchange-surface.d.mts.map +1 -0
- package/dist/build/exchange-surface.mjs +36 -0
- package/dist/build/exchange-surface.mjs.map +1 -0
- package/dist/build/loaders/value-injection-loader.d.mts.map +1 -1
- package/dist/build/logger.d.mts.map +1 -1
- package/dist/build/nextjs-version.d.mts.map +1 -1
- package/dist/build/nextjs-version.mjs +1 -1
- package/dist/build/release-program.d.mts +3 -3
- package/dist/build/release-program.d.mts.map +1 -1
- package/dist/build/release-program.mjs +4 -6
- package/dist/build/release-program.mjs.map +1 -1
- package/dist/build/services/config.service.d.mts.map +1 -1
- package/dist/build/services/config.service.mjs.map +1 -1
- package/dist/build/services/instrumentation-detection.service.d.mts.map +1 -1
- package/dist/build/services/preflight.service.d.mts +2 -3
- package/dist/build/services/preflight.service.d.mts.map +1 -1
- package/dist/build/services/preflight.service.mjs +24 -33
- package/dist/build/services/preflight.service.mjs.map +1 -1
- package/dist/build/services/release-identity.service.d.mts +4 -3
- package/dist/build/services/release-identity.service.d.mts.map +1 -1
- package/dist/build/services/release-identity.service.mjs +23 -10
- package/dist/build/services/release-identity.service.mjs.map +1 -1
- package/dist/build/services/source-map.service.d.mts +3 -7
- package/dist/build/services/source-map.service.d.mts.map +1 -1
- package/dist/build/services/source-map.service.mjs.map +1 -1
- package/dist/build/source-maps/api.d.mts +25 -16
- package/dist/build/source-maps/api.d.mts.map +1 -1
- package/dist/build/source-maps/api.mjs +11 -8
- package/dist/build/source-maps/api.mjs.map +1 -1
- package/dist/build/source-maps/client.d.mts +22 -21
- package/dist/build/source-maps/client.d.mts.map +1 -1
- package/dist/build/source-maps/client.mjs +14 -9
- package/dist/build/source-maps/client.mjs.map +1 -1
- package/dist/build/source-maps/errors.d.mts +118 -106
- package/dist/build/source-maps/errors.d.mts.map +1 -1
- package/dist/build/source-maps/errors.mjs +42 -18
- package/dist/build/source-maps/errors.mjs.map +1 -1
- package/dist/build/source-maps/files.d.mts.map +1 -1
- package/dist/build/source-maps/providers/deployment/detector.d.mts +7 -16
- package/dist/build/source-maps/providers/deployment/detector.d.mts.map +1 -1
- package/dist/build/source-maps/providers/deployment/detector.mjs +2 -2
- package/dist/build/source-maps/providers/deployment/detector.mjs.map +1 -1
- package/dist/build/source-maps/providers/deployment/types.d.mts +2 -2
- package/dist/build/source-maps/providers/deployment/types.d.mts.map +1 -1
- package/dist/build/source-maps/providers/deployment/vercel.d.mts.map +1 -1
- package/dist/build/source-maps/providers/deployment/vercel.mjs +7 -18
- package/dist/build/source-maps/providers/deployment/vercel.mjs.map +1 -1
- package/dist/build/source-maps/providers/source-control/detector.d.mts +5 -4
- package/dist/build/source-maps/providers/source-control/detector.d.mts.map +1 -1
- package/dist/build/source-maps/providers/source-control/detector.mjs +1 -1
- package/dist/build/source-maps/providers/source-control/detector.mjs.map +1 -1
- package/dist/build/source-maps/providers/source-control/git.d.mts.map +1 -1
- package/dist/build/source-maps/providers/source-control/git.mjs +4 -7
- package/dist/build/source-maps/providers/source-control/git.mjs.map +1 -1
- package/dist/build/source-maps/providers/source-control/types.d.mts +5 -3
- package/dist/build/source-maps/providers/source-control/types.d.mts.map +1 -1
- package/dist/build/with-interfere.d.mts +23 -23
- package/dist/build/with-interfere.d.mts.map +1 -1
- package/dist/build/with-interfere.mjs +47 -26
- package/dist/build/with-interfere.mjs.map +1 -1
- package/dist/client/auto-init.d.mts +3 -3
- package/dist/client/auto-init.d.mts.map +1 -1
- package/dist/client/auto-init.mjs +1 -1
- package/dist/client/auto-init.mjs.map +1 -1
- package/dist/client/provider.d.mts +2 -2
- package/dist/client/provider.d.mts.map +1 -1
- package/dist/client/provider.mjs.map +1 -1
- package/dist/lib/env.d.mts.map +1 -1
- package/dist/lib/types.d.mts +6 -6
- package/dist/lib/types.d.mts.map +1 -1
- package/dist/lib/types.mjs.map +1 -1
- package/dist/server/auto-init.d.mts +11 -10
- package/dist/server/auto-init.d.mts.map +1 -1
- package/dist/server/auto-init.mjs +3 -3
- package/dist/server/auto-init.mjs.map +1 -1
- package/dist/server/middleware.d.mts.map +1 -1
- package/dist/server/middleware.mjs +9 -10
- package/dist/server/middleware.mjs.map +1 -1
- package/dist/server/on-request-error.d.mts +7 -7
- package/dist/server/on-request-error.d.mts.map +1 -1
- package/dist/server/on-request-error.mjs +2 -2
- package/dist/server/on-request-error.mjs.map +1 -1
- package/dist/server/proxy.d.mts.map +1 -1
- package/dist/server/proxy.mjs +4 -5
- package/dist/server/proxy.mjs.map +1 -1
- package/dist/server/route-handler.d.mts +31 -1
- package/dist/server/route-handler.d.mts.map +1 -1
- package/dist/server/route-handler.mjs +72 -67
- package/dist/server/route-handler.mjs.map +1 -1
- package/dist/server/sdk.d.mts +29 -29
- package/dist/server/sdk.d.mts.map +1 -1
- package/dist/server/sdk.mjs +2 -2
- package/dist/server/sdk.mjs.map +1 -1
- package/dist/server/services/config.service.d.mts +33 -6
- package/dist/server/services/config.service.d.mts.map +1 -1
- package/dist/server/services/config.service.mjs +54 -30
- package/dist/server/services/config.service.mjs.map +1 -1
- package/dist/server/services/error-tracking.service.d.mts.map +1 -1
- package/dist/server/services/error-tracking.service.mjs +1 -1
- package/dist/server/services/error-tracking.service.mjs.map +1 -1
- package/dist/server/session-context.d.mts +11 -11
- package/dist/server/session-context.d.mts.map +1 -1
- package/dist/server/session-context.mjs.map +1 -1
- package/package.json +38 -34
- package/dist/build/secret-key.d.mts +0 -10
- package/dist/build/secret-key.d.mts.map +0 -1
- package/dist/build/secret-key.mjs +0 -16
- package/dist/build/secret-key.mjs.map +0 -1
- package/dist/lib/test-utils/make-next-request.d.mts +0 -6
- package/dist/lib/test-utils/make-next-request.d.mts.map +0 -1
- package/dist/lib/test-utils/make-next-request.mjs +0 -12
- package/dist/lib/test-utils/make-next-request.mjs.map +0 -1
package/dist/server/sdk.d.mts
CHANGED
|
@@ -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
|
-
|
|
49
|
-
|
|
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
|
-
|
|
54
|
-
|
|
53
|
+
* Get the current SDK instance, if initialized.
|
|
54
|
+
*/
|
|
55
55
|
static getInstance(): ServerSDK | null;
|
|
56
56
|
/**
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
* Check if the SDK is initialized.
|
|
58
|
+
*/
|
|
59
59
|
static isInitialized(): boolean;
|
|
60
60
|
/**
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
* Reset the SDK instance (primarily for testing).
|
|
62
|
+
* @internal
|
|
63
|
+
*/
|
|
64
64
|
static reset(): void;
|
|
65
65
|
/**
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
|
|
72
|
-
|
|
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
|
-
|
|
79
|
-
|
|
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
|
-
|
|
84
|
-
|
|
83
|
+
* Get the SDK configuration.
|
|
84
|
+
*/
|
|
85
85
|
getConfig(): Readonly<ServerSDKConfig>;
|
|
86
86
|
/**
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
* Get a specific metadata value.
|
|
88
|
+
*/
|
|
89
89
|
getMetadata<K extends keyof ServerSDKConfig["metadata"]>(key: K): ServerSDKConfig["metadata"][K];
|
|
90
90
|
/**
|
|
91
|
-
|
|
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,
|
|
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"}
|
package/dist/server/sdk.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
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(
|
|
7
|
+
const errorTrackingLayer = Layer.mergeAll(NextConfigLive, ErrorTrackingServiceLive);
|
|
8
8
|
/**
|
|
9
9
|
* Server-side SDK singleton for automatic error tracking and instrumentation.
|
|
10
10
|
*
|
package/dist/server/sdk.mjs.map
CHANGED
|
@@ -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 {
|
|
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
|
|
4
|
-
import * as
|
|
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
|
|
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
|
-
|
|
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":";;;;;;
|
|
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 {
|
|
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
|
|
11
|
-
const
|
|
12
|
-
if (
|
|
13
|
-
return
|
|
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
|
|
16
|
-
const
|
|
17
|
-
if (
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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.
|
|
37
|
-
|
|
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 {
|
|
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,
|
|
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.
|
|
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.
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
readonly
|
|
13
|
+
* Timestamp when this context was created.
|
|
14
|
+
* Used for debugging and monitoring.
|
|
15
|
+
*/
|
|
16
|
+
readonly createdAt: number;
|
|
17
17
|
/**
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
readonly
|
|
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":"
|
|
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 *
|
|
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.
|
|
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": "
|
|
90
|
-
"
|
|
91
|
-
"effect": "
|
|
92
|
-
"
|
|
93
|
-
"
|
|
94
|
-
"
|
|
95
|
-
"
|
|
96
|
-
"
|
|
97
|
-
"
|
|
98
|
-
"
|
|
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
|
-
"@
|
|
108
|
-
"@
|
|
109
|
-
"@
|
|
110
|
-
"@
|
|
111
|
-
"
|
|
112
|
-
"
|
|
113
|
-
"
|
|
114
|
-
"
|
|
115
|
-
"
|
|
116
|
-
"
|
|
117
|
-
"
|
|
118
|
-
"
|
|
119
|
-
"
|
|
120
|
-
"
|
|
121
|
-
"
|
|
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 +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 };
|