@bleedingdev/modern-js-server-runtime-extensions 0.0.0-trusted-publisher-bootstrap

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 (64) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +67 -0
  3. package/dist/cjs/contractGateAutopilot.js +162 -0
  4. package/dist/cjs/contractGateSnapshotStore.js +253 -0
  5. package/dist/cjs/env.js +58 -0
  6. package/dist/cjs/index.js +162 -0
  7. package/dist/cjs/mfCache.js +106 -0
  8. package/dist/cjs/moduleFederationCss.js +285 -0
  9. package/dist/cjs/runtimeFallbackSignal.js +311 -0
  10. package/dist/cjs/telemetry.js +373 -0
  11. package/dist/cjs/telemetryCore.js +819 -0
  12. package/dist/esm/contractGateAutopilot.mjs +124 -0
  13. package/dist/esm/contractGateSnapshotStore.mjs +190 -0
  14. package/dist/esm/env.mjs +17 -0
  15. package/dist/esm/index.mjs +6 -0
  16. package/dist/esm/mfCache.mjs +55 -0
  17. package/dist/esm/moduleFederationCss.mjs +225 -0
  18. package/dist/esm/runtimeFallbackSignal.mjs +222 -0
  19. package/dist/esm/telemetry.mjs +275 -0
  20. package/dist/esm/telemetryCore.mjs +759 -0
  21. package/dist/esm-node/contractGateAutopilot.mjs +125 -0
  22. package/dist/esm-node/contractGateSnapshotStore.mjs +192 -0
  23. package/dist/esm-node/env.mjs +18 -0
  24. package/dist/esm-node/index.mjs +7 -0
  25. package/dist/esm-node/mfCache.mjs +56 -0
  26. package/dist/esm-node/moduleFederationCss.mjs +226 -0
  27. package/dist/esm-node/runtimeFallbackSignal.mjs +223 -0
  28. package/dist/esm-node/telemetry.mjs +276 -0
  29. package/dist/esm-node/telemetryCore.mjs +760 -0
  30. package/dist/types/contractGateAutopilot.d.ts +35 -0
  31. package/dist/types/contractGateSnapshotStore.d.ts +57 -0
  32. package/dist/types/env.d.ts +40 -0
  33. package/dist/types/index.d.ts +6 -0
  34. package/dist/types/mfCache.d.ts +27 -0
  35. package/dist/types/moduleFederationCss.d.ts +87 -0
  36. package/dist/types/runtimeFallbackSignal.d.ts +94 -0
  37. package/dist/types/telemetry.d.ts +12 -0
  38. package/dist/types/telemetryCore.d.ts +257 -0
  39. package/package.json +69 -0
  40. package/rslib.config.mts +4 -0
  41. package/rstest.config.mts +7 -0
  42. package/src/contractGateAutopilot.ts +247 -0
  43. package/src/contractGateSnapshotStore.ts +420 -0
  44. package/src/env.ts +63 -0
  45. package/src/index.ts +84 -0
  46. package/src/mfCache.ts +119 -0
  47. package/src/moduleFederationCss.ts +473 -0
  48. package/src/runtimeFallbackSignal.ts +584 -0
  49. package/src/telemetry.ts +554 -0
  50. package/src/telemetryCore.ts +1332 -0
  51. package/tests/contractGateAutopilot.test.ts +203 -0
  52. package/tests/contractGateSnapshotStore.test.ts +223 -0
  53. package/tests/env.test.ts +73 -0
  54. package/tests/helpers.ts +19 -0
  55. package/tests/mfCache.test.ts +150 -0
  56. package/tests/moduleFederationCss.test.ts +392 -0
  57. package/tests/registration.test.ts +112 -0
  58. package/tests/telemetry.test.ts +360 -0
  59. package/tests/telemetryAutopilot.test.ts +993 -0
  60. package/tests/telemetryCanaryOrchestrator.test.ts +140 -0
  61. package/tests/telemetryLifecycle.test.ts +168 -0
  62. package/tests/telemetryTraceparent.test.ts +167 -0
  63. package/tests/tsconfig.json +11 -0
  64. package/tsconfig.json +10 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021-present Modern.js
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,67 @@
1
+ # @modern-js/server-runtime-extensions
2
+
3
+ Fork-owned ultramodern.js server runtime extensions. This package hosts the
4
+ server-side fork features that previously lived inside the upstream-owned
5
+ `@modern-js/server-core` sources, so the fork diff against upstream Modern.js
6
+ stays small:
7
+
8
+ - **Telemetry pipeline** — `TelemetryRegistry`, OTLP / VictoriaMetrics
9
+ exporters, SLO alerts, telemetry-aware metrics wrapping and the
10
+ `injectTelemetryPlugin()` server plugin (runtime status + runtime fallback
11
+ signal endpoints).
12
+ - **Contract-gate canary autopilot** — `TelemetryCanaryOrchestrator`,
13
+ `ContractGateAutopilot` and the file/HTTP contract-gate snapshot stores.
14
+ - **Module federation runtime helpers** — remote CSS collection for SSR
15
+ (`collectDirectRemoteModuleFederationCss`, `injectModuleFederationCssPlugin()`)
16
+ and MF asset cache-header policies (`resolveMfAssetCacheHeaders`,
17
+ `injectMfAssetCacheHeadersPlugin()`).
18
+
19
+ ## Registration
20
+
21
+ None of these plugins are part of the bare `@modern-js/server-core` default
22
+ plugin chain. `@modern-js/prod-server` registers them in its plugin assembly
23
+ (`applyPlugins`), which is also the assembly used by the dev server
24
+ (`@modern-js/server` via `@modern-js/app-tools`), so production and dev behave
25
+ identically:
26
+
27
+ - `injectTelemetryPlugin()` — no-op unless `server.telemetry` is configured.
28
+ The runtime-fallback-signal endpoint is opt-in
29
+ (`canary.autopilot.runtimeFallbackSignal.enabled: true`) and requires an
30
+ auth token (`auth.expectedValue` / `auth.expectedValueEnv`); the
31
+ `/_modern/runtime/status` endpoint returns a bare health probe unless the
32
+ caller authenticates with that token.
33
+ - `injectModuleFederationCssPlugin()` — no-op unless the dist directory
34
+ contains an `mf-manifest.json` host manifest. Must be registered after
35
+ `injectResourcePlugin()` so the request-scoped server manifest exists. In
36
+ production the remote CSS collection is cached with a 30s TTL (configurable
37
+ via the plugin's `remoteCssCacheTtlMs` option) instead of being pinned at
38
+ boot.
39
+ - `injectMfAssetCacheHeadersPlugin()` — applies the ADR-0002 MF cache-header
40
+ policy to `mf-manifest.json`/`mf-stats.json` (`no-store`) and
41
+ `remoteEntry*.js` (revalidate, or `immutable` when version-pinned via
42
+ `?mfv=`) responses served by the static middleware.
43
+
44
+ ## Environment variables
45
+
46
+ The environment variables consumed by this package at runtime are parsed in a
47
+ single typed pass by `parseServerRuntimeExtensionsEnv()` in `src/env.ts`:
48
+
49
+ | Variable | Default | Description |
50
+ | --- | --- | --- |
51
+ | `MODERN_ENV` | _unset_ | Deployment environment name (also drives `.env.{MODERN_ENV}` loading in the server bootstrap). First candidate for the telemetry `environment` label. |
52
+ | `NODE_ENV` | _unset_ | Standard Node.js environment name. Second candidate for the telemetry `environment` label; the final fallback is `development`. |
53
+ | `MODERN_CONTRACT_GATES_FILE` | `.modern/contract-gates.json` (resolved against the app directory) | Path of the contract-gate snapshot file used by the canary autopilot and the runtime fallback signal endpoint when `server.telemetry.canary.autopilot.gateSnapshotPath` is not configured. |
54
+
55
+ Exporter endpoints and the module federation remote manifest timeout are
56
+ configured through `server.telemetry.exporters.*.endpoint` and plugin options
57
+ only; their defaults (`http://127.0.0.1:4318/v1/logs`,
58
+ `http://127.0.0.1:8428/api/v1/import/prometheus`, `1500`ms) are hard-coded,
59
+ matching the pre-extraction server-core behavior.
60
+ `MODERN_TELEMETRY_OTLP_ENDPOINT` / `MODERN_TELEMETRY_VICTORIA_ENDPOINT` are
61
+ read only at config time by `@modern-js/app-tools` (`baseline.ts`), not at
62
+ server runtime.
63
+
64
+ One dynamic indirection cannot be statically parsed:
65
+ `server.telemetry.canary.autopilot.runtimeFallbackSignal.auth.expectedValueEnv`
66
+ names an arbitrary environment variable that holds the expected runtime-signal
67
+ auth token; it is read when the auth config is normalized.
@@ -0,0 +1,162 @@
1
+ "use strict";
2
+ var __webpack_require__ = {};
3
+ (()=>{
4
+ __webpack_require__.d = (exports1, getters, values)=>{
5
+ var define = (defs, kind)=>{
6
+ for(var key in defs)if (__webpack_require__.o(defs, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
7
+ enumerable: true,
8
+ [kind]: defs[key]
9
+ });
10
+ };
11
+ define(getters, "get");
12
+ define(values, "value");
13
+ };
14
+ })();
15
+ (()=>{
16
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
17
+ })();
18
+ (()=>{
19
+ __webpack_require__.r = (exports1)=>{
20
+ if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
21
+ value: 'Module'
22
+ });
23
+ Object.defineProperty(exports1, '__esModule', {
24
+ value: true
25
+ });
26
+ };
27
+ })();
28
+ var __webpack_exports__ = {};
29
+ __webpack_require__.r(__webpack_exports__);
30
+ __webpack_require__.d(__webpack_exports__, {
31
+ ContractGateAutopilot: ()=>ContractGateAutopilot
32
+ });
33
+ const external_contractGateSnapshotStore_js_namespaceObject = require("./contractGateSnapshotStore.js");
34
+ const DEFAULT_POLL_INTERVAL_MS = 15000;
35
+ const DEFAULT_GATE_STALE_AFTER_MS = 600000;
36
+ class ContractGateAutopilot {
37
+ async start() {
38
+ await this.syncOnce();
39
+ if (this.poller) return;
40
+ this.poller = setInterval(()=>{
41
+ this.syncOnce();
42
+ }, this.pollIntervalMs);
43
+ if ('function' == typeof this.poller.unref) this.poller.unref();
44
+ }
45
+ stop() {
46
+ if (this.poller) {
47
+ clearInterval(this.poller);
48
+ this.poller = void 0;
49
+ }
50
+ }
51
+ async syncOnce() {
52
+ const snapshot = await this.loadSnapshot();
53
+ if (!snapshot) return 0;
54
+ const gates = this.normalizeSnapshot(snapshot);
55
+ let updatedCount = 0;
56
+ for (const gate of gates){
57
+ this.orchestrator.addRequiredContractGate(gate.name);
58
+ const fingerprint = `${gate.passed ? '1' : '0'}:${gate.reason || ''}`;
59
+ if (this.appliedGateFingerprints.get(gate.name) !== fingerprint) {
60
+ this.orchestrator.setContractGate(gate.name, gate.passed, gate.reason);
61
+ this.appliedGateFingerprints.set(gate.name, fingerprint);
62
+ updatedCount += 1;
63
+ this.logger?.info?.(`[telemetry.canary.autopilot] gate=${gate.name} passed=${String(gate.passed)} reason=${gate.reason || 'none'}`);
64
+ }
65
+ }
66
+ return updatedCount;
67
+ }
68
+ async loadSnapshot() {
69
+ try {
70
+ const snapshot = await this.gateSnapshotStore.readSnapshot();
71
+ if (!snapshot) return;
72
+ const fingerprint = JSON.stringify(snapshot);
73
+ if (fingerprint === this.lastSnapshotFingerprint) return;
74
+ this.lastSnapshotFingerprint = fingerprint;
75
+ return snapshot;
76
+ } catch (error) {
77
+ const source = this.gateSnapshotPath || this.gateSnapshotStore.name || 'stateStore';
78
+ this.logger?.warn?.(`[telemetry.canary.autopilot] failed to load gate snapshot ${source}: ${error instanceof Error ? error.message : String(error)}`);
79
+ return;
80
+ }
81
+ }
82
+ normalizeSnapshot(snapshot) {
83
+ const now = Date.now();
84
+ const output = [];
85
+ const gates = snapshot.gates;
86
+ if (!gates || 'object' != typeof gates) return output;
87
+ for (const [name, value] of Object.entries(gates)){
88
+ const normalizedName = name.trim();
89
+ if (!normalizedName) continue;
90
+ const gate = this.normalizeGateValue(value, snapshot.updatedAt, now);
91
+ if (!gate) continue;
92
+ if ('number' == typeof gate.expiresAt && Number.isFinite(gate.expiresAt) && gate.expiresAt > 0 && now >= gate.expiresAt) {
93
+ output.push({
94
+ name: normalizedName,
95
+ passed: true,
96
+ reason: void 0,
97
+ updatedAt: gate.updatedAt,
98
+ expiresAt: gate.expiresAt
99
+ });
100
+ continue;
101
+ }
102
+ const isStale = this.gateStaleAfterMs > 0 && now - gate.updatedAt > this.gateStaleAfterMs;
103
+ if (isStale) {
104
+ output.push({
105
+ name: normalizedName,
106
+ passed: false,
107
+ reason: gate.reason || 'Gate snapshot is stale',
108
+ updatedAt: gate.updatedAt
109
+ });
110
+ continue;
111
+ }
112
+ output.push({
113
+ name: normalizedName,
114
+ passed: gate.passed,
115
+ reason: gate.reason,
116
+ updatedAt: gate.updatedAt
117
+ });
118
+ }
119
+ return output;
120
+ }
121
+ normalizeGateValue(value, snapshotUpdatedAt, now) {
122
+ if ('boolean' == typeof value) return {
123
+ passed: value,
124
+ updatedAt: this.normalizeUpdatedAt(snapshotUpdatedAt, now)
125
+ };
126
+ if (!value || 'object' != typeof value) return;
127
+ const hasPassed = 'boolean' == typeof value.passed;
128
+ const passed = true === value.passed;
129
+ let reason = 'string' == typeof value.reason && value.reason.trim().length > 0 ? value.reason : void 0;
130
+ if (!hasPassed) reason = reason || 'Gate snapshot record is missing "passed" boolean';
131
+ return {
132
+ passed,
133
+ reason,
134
+ updatedAt: this.normalizeUpdatedAt(value.updatedAt ?? snapshotUpdatedAt, now),
135
+ expiresAt: this.normalizeExpiresAt(value.expiresAt)
136
+ };
137
+ }
138
+ normalizeUpdatedAt(value, fallback) {
139
+ if ('number' == typeof value && Number.isFinite(value) && value > 0) return value;
140
+ return fallback;
141
+ }
142
+ normalizeExpiresAt(value) {
143
+ if ('number' == typeof value && Number.isFinite(value) && value > 0) return value;
144
+ }
145
+ constructor(options){
146
+ this.appliedGateFingerprints = new Map();
147
+ this.orchestrator = options.orchestrator;
148
+ if (!options.gateSnapshotStore && !options.gateSnapshotPath) throw new Error('ContractGateAutopilot requires gateSnapshotPath or gateSnapshotStore');
149
+ this.gateSnapshotPath = options.gateSnapshotPath;
150
+ this.gateSnapshotStore = options.gateSnapshotStore || (0, external_contractGateSnapshotStore_js_namespaceObject.createFileContractGateSnapshotStore)(options.gateSnapshotPath);
151
+ this.pollIntervalMs = Math.max(250, options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS);
152
+ this.gateStaleAfterMs = Math.max(0, options.gateStaleAfterMs ?? DEFAULT_GATE_STALE_AFTER_MS);
153
+ this.logger = options.logger;
154
+ }
155
+ }
156
+ exports.ContractGateAutopilot = __webpack_exports__.ContractGateAutopilot;
157
+ for(var __rspack_i in __webpack_exports__)if (-1 === [
158
+ "ContractGateAutopilot"
159
+ ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
160
+ Object.defineProperty(exports, '__esModule', {
161
+ value: true
162
+ });
@@ -0,0 +1,253 @@
1
+ "use strict";
2
+ var __webpack_require__ = {};
3
+ (()=>{
4
+ __webpack_require__.n = (module)=>{
5
+ var getter = module && module.__esModule ? ()=>module['default'] : ()=>module;
6
+ __webpack_require__.d(getter, {
7
+ a: getter
8
+ });
9
+ return getter;
10
+ };
11
+ })();
12
+ (()=>{
13
+ __webpack_require__.d = (exports1, getters, values)=>{
14
+ var define = (defs, kind)=>{
15
+ for(var key in defs)if (__webpack_require__.o(defs, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
16
+ enumerable: true,
17
+ [kind]: defs[key]
18
+ });
19
+ };
20
+ define(getters, "get");
21
+ define(values, "value");
22
+ };
23
+ })();
24
+ (()=>{
25
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
26
+ })();
27
+ (()=>{
28
+ __webpack_require__.r = (exports1)=>{
29
+ if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
30
+ value: 'Module'
31
+ });
32
+ Object.defineProperty(exports1, '__esModule', {
33
+ value: true
34
+ });
35
+ };
36
+ })();
37
+ var __webpack_exports__ = {};
38
+ __webpack_require__.r(__webpack_exports__);
39
+ __webpack_require__.d(__webpack_exports__, {
40
+ CONTRACT_GATE_SNAPSHOT_SCHEMA_VERSION: ()=>CONTRACT_GATE_SNAPSHOT_SCHEMA_VERSION,
41
+ DEFAULT_CONTRACT_GATE_SNAPSHOT_PATH: ()=>DEFAULT_CONTRACT_GATE_SNAPSHOT_PATH,
42
+ createFileContractGateSnapshotStore: ()=>createFileContractGateSnapshotStore,
43
+ createHttpContractGateSnapshotStore: ()=>createHttpContractGateSnapshotStore,
44
+ resolveContractGateSnapshotPath: ()=>resolveContractGateSnapshotPath,
45
+ resolveContractGateSnapshotStore: ()=>resolveContractGateSnapshotStore
46
+ });
47
+ const external_node_module_namespaceObject = require("node:module");
48
+ const utils_namespaceObject = require("@modern-js/utils");
49
+ const external_fs_namespaceObject = require("fs");
50
+ const external_path_namespaceObject = require("path");
51
+ var external_path_default = /*#__PURE__*/ __webpack_require__.n(external_path_namespaceObject);
52
+ const external_env_js_namespaceObject = require("./env.js");
53
+ const CONTRACT_GATE_SNAPSHOT_SCHEMA_VERSION = 1;
54
+ const DEFAULT_CONTRACT_GATE_SNAPSHOT_PATH = '.modern/contract-gates.json';
55
+ const DEFAULT_HTTP_STORE_TIMEOUT_MS = 5000;
56
+ const BUILTIN_HTTP_STATE_STORE_MODULES = new Set([
57
+ 'http',
58
+ '@modern-js/server-core/http',
59
+ '@modern-js/server-core/contract-gate-http-store',
60
+ '@modern-js/server-runtime-extensions/http',
61
+ '@modern-js/server-runtime-extensions/contract-gate-http-store'
62
+ ]);
63
+ const isRecord = (value)=>'object' == typeof value && null !== value && !Array.isArray(value);
64
+ const normalizeSnapshot = (snapshot)=>{
65
+ if (!isRecord(snapshot)) return;
66
+ const schemaVersion = 'number' == typeof snapshot.schemaVersion ? snapshot.schemaVersion : CONTRACT_GATE_SNAPSHOT_SCHEMA_VERSION;
67
+ const updatedAt = 'number' == typeof snapshot.updatedAt ? snapshot.updatedAt : Date.now();
68
+ const gates = isRecord(snapshot.gates) ? snapshot.gates : {};
69
+ return {
70
+ schemaVersion,
71
+ updatedAt,
72
+ gates
73
+ };
74
+ };
75
+ const normalizeHttpStoreOptions = (options)=>{
76
+ const endpoint = 'string' == typeof options?.endpoint ? options.endpoint.trim() : '';
77
+ if (!endpoint) throw new Error('[telemetry.canary.autopilot] HTTP stateStore requires options.endpoint');
78
+ const readMethod = 'string' == typeof options?.readMethod && options.readMethod.trim() ? options.readMethod.trim().toUpperCase() : 'GET';
79
+ const writeMethod = 'string' == typeof options?.writeMethod && options.writeMethod.trim() ? options.writeMethod.trim().toUpperCase() : 'PUT';
80
+ const timeoutMsRaw = Number(options?.timeoutMs);
81
+ const timeoutMs = Number.isFinite(timeoutMsRaw) && timeoutMsRaw > 0 ? Math.floor(timeoutMsRaw) : DEFAULT_HTTP_STORE_TIMEOUT_MS;
82
+ const headersRaw = options?.headers;
83
+ const headers = {};
84
+ if (headersRaw && 'object' == typeof headersRaw && !Array.isArray(headersRaw)) Object.entries(headersRaw).forEach(([key, value])=>{
85
+ if ('string' == typeof key && key.trim().length > 0 && null != value) headers[key] = String(value);
86
+ });
87
+ return {
88
+ endpoint,
89
+ readMethod,
90
+ writeMethod,
91
+ headers,
92
+ timeoutMs
93
+ };
94
+ };
95
+ const withTimeoutAbort = (timeoutMs)=>{
96
+ const controller = new AbortController();
97
+ const timer = setTimeout(()=>controller.abort(), timeoutMs);
98
+ return {
99
+ signal: controller.signal,
100
+ clear: ()=>clearTimeout(timer)
101
+ };
102
+ };
103
+ const createHttpContractGateSnapshotStore = (options)=>{
104
+ const normalized = normalizeHttpStoreOptions(options);
105
+ const endpoint = normalized.endpoint;
106
+ return {
107
+ name: `http:${endpoint}`,
108
+ async readSnapshot () {
109
+ const { signal, clear } = withTimeoutAbort(normalized.timeoutMs || 5000);
110
+ try {
111
+ const response = await fetch(endpoint, {
112
+ method: normalized.readMethod || 'GET',
113
+ headers: {
114
+ accept: 'application/json',
115
+ ...normalized.headers || {}
116
+ },
117
+ signal
118
+ });
119
+ if (404 === response.status) return;
120
+ if (!response.ok) throw new Error(`HTTP stateStore read failed with status ${String(response.status)}`);
121
+ const payload = await response.json();
122
+ return normalizeSnapshot(payload);
123
+ } finally{
124
+ clear();
125
+ }
126
+ },
127
+ async writeSnapshot (snapshot) {
128
+ const body = JSON.stringify(normalizeSnapshot(snapshot) || {
129
+ schemaVersion: CONTRACT_GATE_SNAPSHOT_SCHEMA_VERSION,
130
+ updatedAt: Date.now(),
131
+ gates: {}
132
+ });
133
+ const { signal, clear } = withTimeoutAbort(normalized.timeoutMs || 5000);
134
+ try {
135
+ const response = await fetch(endpoint, {
136
+ method: normalized.writeMethod || 'PUT',
137
+ headers: {
138
+ 'content-type': 'application/json',
139
+ ...normalized.headers || {}
140
+ },
141
+ body,
142
+ signal
143
+ });
144
+ if (!response.ok) throw new Error(`HTTP stateStore write failed with status ${String(response.status)}`);
145
+ } finally{
146
+ clear();
147
+ }
148
+ }
149
+ };
150
+ };
151
+ const tryResolveBuiltinSnapshotStore = (input)=>{
152
+ const moduleName = input.stateStore.module.trim();
153
+ if (!BUILTIN_HTTP_STATE_STORE_MODULES.has(moduleName)) return;
154
+ return createHttpContractGateSnapshotStore(input.stateStore.options || {});
155
+ };
156
+ const pickStoreFactory = (mod)=>{
157
+ if ('function' == typeof mod) return mod;
158
+ if ('function' == typeof mod.createContractGateSnapshotStore) return mod.createContractGateSnapshotStore;
159
+ if ('function' == typeof mod.default) return mod.default;
160
+ if (mod.default && 'object' == typeof mod.default && 'function' == typeof mod.default.createContractGateSnapshotStore) return mod.default.createContractGateSnapshotStore;
161
+ };
162
+ const ensureStoreShape = (store, modulePath)=>{
163
+ if (!store || 'object' != typeof store || 'function' != typeof store.readSnapshot || 'function' != typeof store.writeSnapshot) throw new Error(`Invalid contract gate snapshot store from "${modulePath}". Expected { readSnapshot(), writeSnapshot() }.`);
164
+ };
165
+ const resolveStoreModulePath = (appDirectory, modulePath)=>{
166
+ const normalized = modulePath.trim();
167
+ if (!normalized) throw new Error('Contract gate snapshot stateStore.module must be non-empty');
168
+ if (external_path_default().isAbsolute(normalized)) return normalized;
169
+ if (normalized.startsWith('.')) return external_path_default().resolve(appDirectory, normalized);
170
+ const appRequire = (0, external_node_module_namespaceObject.createRequire)(external_path_default().join(appDirectory, 'package.json'));
171
+ try {
172
+ return appRequire.resolve(normalized);
173
+ } catch (_error) {
174
+ return normalized;
175
+ }
176
+ };
177
+ const resolveContractGateSnapshotPath = (appDirectory, configuredPath)=>{
178
+ const rawPath = configuredPath || (0, external_env_js_namespaceObject.parseServerRuntimeExtensionsEnv)().contractGatesFile || DEFAULT_CONTRACT_GATE_SNAPSHOT_PATH;
179
+ if (external_path_default().isAbsolute(rawPath)) return rawPath;
180
+ return external_path_default().resolve(appDirectory, rawPath);
181
+ };
182
+ const createFileContractGateSnapshotStore = (gateSnapshotPath)=>{
183
+ const resolvedPath = external_path_default().resolve(gateSnapshotPath);
184
+ return {
185
+ name: `file:${resolvedPath}`,
186
+ async readSnapshot () {
187
+ if (!await utils_namespaceObject.fs.pathExists(resolvedPath)) return;
188
+ try {
189
+ const raw = await external_fs_namespaceObject.promises.readFile(resolvedPath, 'utf8');
190
+ return normalizeSnapshot(JSON.parse(raw));
191
+ } catch (_error) {
192
+ return;
193
+ }
194
+ },
195
+ async writeSnapshot (snapshot) {
196
+ const normalized = normalizeSnapshot(snapshot) || {
197
+ schemaVersion: CONTRACT_GATE_SNAPSHOT_SCHEMA_VERSION,
198
+ updatedAt: Date.now(),
199
+ gates: {}
200
+ };
201
+ await external_fs_namespaceObject.promises.mkdir(external_path_default().dirname(resolvedPath), {
202
+ recursive: true
203
+ });
204
+ await external_fs_namespaceObject.promises.writeFile(resolvedPath, `${JSON.stringify(normalized, null, 2)}\n`);
205
+ }
206
+ };
207
+ };
208
+ const resolveContractGateSnapshotStore = async (input)=>{
209
+ const { appDirectory, gateSnapshotPath, stateStore, logger } = input;
210
+ if (!stateStore?.module) return createFileContractGateSnapshotStore(gateSnapshotPath);
211
+ const builtinStore = tryResolveBuiltinSnapshotStore({
212
+ stateStore
213
+ });
214
+ if (builtinStore) {
215
+ logger?.info?.(`[telemetry.canary.autopilot] using built-in contract gate snapshot store "${builtinStore.name}"`);
216
+ return builtinStore;
217
+ }
218
+ const modulePath = resolveStoreModulePath(appDirectory, stateStore.module);
219
+ let mod;
220
+ try {
221
+ mod = require(modulePath);
222
+ } catch (error) {
223
+ throw new Error(`[telemetry.canary.autopilot] Failed to load stateStore.module "${stateStore.module}" (${modulePath}): ${error instanceof Error ? error.message : String(error)}`);
224
+ }
225
+ const factory = pickStoreFactory(mod);
226
+ if (!factory) throw new Error(`[telemetry.canary.autopilot] stateStore.module "${stateStore.module}" does not export createContractGateSnapshotStore()`);
227
+ const store = await factory({
228
+ appDirectory,
229
+ gateSnapshotPath,
230
+ options: stateStore.options,
231
+ logger
232
+ });
233
+ ensureStoreShape(store, modulePath);
234
+ logger?.info?.(`[telemetry.canary.autopilot] using contract gate snapshot store "${store.name || modulePath}"`);
235
+ return store;
236
+ };
237
+ exports.CONTRACT_GATE_SNAPSHOT_SCHEMA_VERSION = __webpack_exports__.CONTRACT_GATE_SNAPSHOT_SCHEMA_VERSION;
238
+ exports.DEFAULT_CONTRACT_GATE_SNAPSHOT_PATH = __webpack_exports__.DEFAULT_CONTRACT_GATE_SNAPSHOT_PATH;
239
+ exports.createFileContractGateSnapshotStore = __webpack_exports__.createFileContractGateSnapshotStore;
240
+ exports.createHttpContractGateSnapshotStore = __webpack_exports__.createHttpContractGateSnapshotStore;
241
+ exports.resolveContractGateSnapshotPath = __webpack_exports__.resolveContractGateSnapshotPath;
242
+ exports.resolveContractGateSnapshotStore = __webpack_exports__.resolveContractGateSnapshotStore;
243
+ for(var __rspack_i in __webpack_exports__)if (-1 === [
244
+ "CONTRACT_GATE_SNAPSHOT_SCHEMA_VERSION",
245
+ "DEFAULT_CONTRACT_GATE_SNAPSHOT_PATH",
246
+ "createFileContractGateSnapshotStore",
247
+ "createHttpContractGateSnapshotStore",
248
+ "resolveContractGateSnapshotPath",
249
+ "resolveContractGateSnapshotStore"
250
+ ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
251
+ Object.defineProperty(exports, '__esModule', {
252
+ value: true
253
+ });
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ var __webpack_require__ = {};
3
+ (()=>{
4
+ __webpack_require__.d = (exports1, getters, values)=>{
5
+ var define = (defs, kind)=>{
6
+ for(var key in defs)if (__webpack_require__.o(defs, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
7
+ enumerable: true,
8
+ [kind]: defs[key]
9
+ });
10
+ };
11
+ define(getters, "get");
12
+ define(values, "value");
13
+ };
14
+ })();
15
+ (()=>{
16
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
17
+ })();
18
+ (()=>{
19
+ __webpack_require__.r = (exports1)=>{
20
+ if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
21
+ value: 'Module'
22
+ });
23
+ Object.defineProperty(exports1, '__esModule', {
24
+ value: true
25
+ });
26
+ };
27
+ })();
28
+ var __webpack_exports__ = {};
29
+ __webpack_require__.r(__webpack_exports__);
30
+ const DEFAULT_ENVIRONMENT_NAME = 'development';
31
+ const readString = (value)=>{
32
+ if ('string' != typeof value) return;
33
+ const trimmed = value.trim();
34
+ return trimmed.length > 0 ? trimmed : void 0;
35
+ };
36
+ const parseServerRuntimeExtensionsEnv = (env = process.env)=>{
37
+ const modernEnv = readString(env.MODERN_ENV);
38
+ const nodeEnv = readString(env.NODE_ENV);
39
+ return {
40
+ modernEnv,
41
+ nodeEnv,
42
+ environmentName: modernEnv || nodeEnv || DEFAULT_ENVIRONMENT_NAME,
43
+ contractGatesFile: readString(env.MODERN_CONTRACT_GATES_FILE)
44
+ };
45
+ };
46
+ __webpack_require__.d(__webpack_exports__, {}, {
47
+ DEFAULT_ENVIRONMENT_NAME: DEFAULT_ENVIRONMENT_NAME,
48
+ parseServerRuntimeExtensionsEnv: parseServerRuntimeExtensionsEnv
49
+ });
50
+ exports.DEFAULT_ENVIRONMENT_NAME = __webpack_exports__.DEFAULT_ENVIRONMENT_NAME;
51
+ exports.parseServerRuntimeExtensionsEnv = __webpack_exports__.parseServerRuntimeExtensionsEnv;
52
+ for(var __rspack_i in __webpack_exports__)if (-1 === [
53
+ "DEFAULT_ENVIRONMENT_NAME",
54
+ "parseServerRuntimeExtensionsEnv"
55
+ ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
56
+ Object.defineProperty(exports, '__esModule', {
57
+ value: true
58
+ });