@bleedingdev/modern-js-surface-resolution 0.0.0 → 3.9.0-ultramodern.6
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 +21 -0
- package/dist/cjs/surface-resolution/env-static-provider.js +318 -0
- package/dist/cjs/surface-resolution/index.js +69 -0
- package/dist/cjs/surface-resolution/surface-ref.js +132 -0
- package/dist/cjs/surface-resolution/types.js +18 -0
- package/dist/cjs/surface-resolution/validation.js +202 -0
- package/dist/esm/surface-resolution/env-static-provider.mjs +274 -0
- package/dist/esm/surface-resolution/index.mjs +3 -0
- package/dist/esm/surface-resolution/surface-ref.mjs +88 -0
- package/dist/esm/surface-resolution/types.mjs +0 -0
- package/dist/esm/surface-resolution/validation.mjs +155 -0
- package/dist/esm-node/surface-resolution/env-static-provider.mjs +275 -0
- package/dist/esm-node/surface-resolution/index.mjs +4 -0
- package/dist/esm-node/surface-resolution/surface-ref.mjs +89 -0
- package/dist/esm-node/surface-resolution/types.mjs +1 -0
- package/dist/esm-node/surface-resolution/validation.mjs +156 -0
- package/dist/types/surface-resolution/env-static-provider.d.ts +107 -0
- package/dist/types/surface-resolution/index.d.ts +4 -0
- package/dist/types/surface-resolution/surface-ref.d.ts +72 -0
- package/dist/types/surface-resolution/types.d.ts +102 -0
- package/dist/types/surface-resolution/validation.d.ts +53 -0
- package/package.json +40 -4
- package/src/surface-resolution/env-static-provider.ts +596 -0
- package/src/surface-resolution/index.ts +43 -0
- package/src/surface-resolution/surface-ref.ts +145 -0
- package/src/surface-resolution/types.ts +136 -0
- package/src/surface-resolution/validation.ts +337 -0
- package/README.md +0 -3
|
@@ -0,0 +1,202 @@
|
|
|
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
|
+
createDiscoveryError: ()=>createDiscoveryError,
|
|
32
|
+
matchDeliveryUnitIdentity: ()=>matchDeliveryUnitIdentity,
|
|
33
|
+
selectResolvedSurface: ()=>selectResolvedSurface,
|
|
34
|
+
validateResolvedDeliveryUnit: ()=>validateResolvedDeliveryUnit
|
|
35
|
+
});
|
|
36
|
+
const external_surface_ref_js_namespaceObject = require("./surface-ref.js");
|
|
37
|
+
const SEGMENT_PATTERN = /^[A-Za-z0-9._-]+$/;
|
|
38
|
+
function createDiscoveryError(code, ref, message, details) {
|
|
39
|
+
const refString = 'string' == typeof ref ? ref : (0, external_surface_ref_js_namespaceObject.formatSurfaceRef)(ref);
|
|
40
|
+
return void 0 === details ? {
|
|
41
|
+
code,
|
|
42
|
+
ref: refString,
|
|
43
|
+
message
|
|
44
|
+
} : {
|
|
45
|
+
code,
|
|
46
|
+
ref: refString,
|
|
47
|
+
message,
|
|
48
|
+
details
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function isNonEmptyString(value) {
|
|
52
|
+
return 'string' == typeof value && value.length > 0;
|
|
53
|
+
}
|
|
54
|
+
function isValidUnitId(unitId) {
|
|
55
|
+
return unitId.split('/').every((segment)=>SEGMENT_PATTERN.test(segment));
|
|
56
|
+
}
|
|
57
|
+
function isRecord(value) {
|
|
58
|
+
return 'object' == typeof value && null !== value && !Array.isArray(value);
|
|
59
|
+
}
|
|
60
|
+
const COMPATIBILITY_STATUSES = [
|
|
61
|
+
'compatible',
|
|
62
|
+
'incompatible',
|
|
63
|
+
'degraded'
|
|
64
|
+
];
|
|
65
|
+
const SURFACE_KINDS = [
|
|
66
|
+
'component',
|
|
67
|
+
'route',
|
|
68
|
+
'api',
|
|
69
|
+
'backend'
|
|
70
|
+
];
|
|
71
|
+
const LOCATION_PLATFORMS = [
|
|
72
|
+
'browser-mf-manifest',
|
|
73
|
+
'node-mf-manifest',
|
|
74
|
+
'http-api',
|
|
75
|
+
'cloudflare-service-binding'
|
|
76
|
+
];
|
|
77
|
+
const LOCATION_REQUIRED_FIELDS = {
|
|
78
|
+
'browser-mf-manifest': [
|
|
79
|
+
'manifestUrl'
|
|
80
|
+
],
|
|
81
|
+
'node-mf-manifest': [
|
|
82
|
+
'manifestRef'
|
|
83
|
+
],
|
|
84
|
+
'http-api': [
|
|
85
|
+
'baseUrl',
|
|
86
|
+
'prefix'
|
|
87
|
+
],
|
|
88
|
+
'cloudflare-service-binding': [
|
|
89
|
+
'serviceBinding'
|
|
90
|
+
]
|
|
91
|
+
};
|
|
92
|
+
function validateLocation(location, path, seenPlatforms, push) {
|
|
93
|
+
if (!isRecord(location)) return void push(path, 'must be an object');
|
|
94
|
+
const platform = location.platform;
|
|
95
|
+
if ('string' != typeof platform || !LOCATION_PLATFORMS.includes(platform)) return void push(`${path}.platform`, `must be one of ${LOCATION_PLATFORMS.join(', ')}`);
|
|
96
|
+
if (seenPlatforms.has(platform)) push(`${path}.platform`, `duplicate platform ${platform} within one surface`);
|
|
97
|
+
seenPlatforms.add(platform);
|
|
98
|
+
for (const field of LOCATION_REQUIRED_FIELDS[platform] ?? [])if (!isNonEmptyString(location[field])) push(`${path}.${field}`, 'must be a non-empty string');
|
|
99
|
+
if ('cloudflare-service-binding' === platform && void 0 !== location.dispatchNamespace && !isNonEmptyString(location.dispatchNamespace)) push(`${path}.dispatchNamespace`, 'must be a non-empty string when present');
|
|
100
|
+
}
|
|
101
|
+
function validateSurface(surface, path, seenSurfaceIds, push) {
|
|
102
|
+
if (!isRecord(surface)) return void push(path, 'must be an object');
|
|
103
|
+
if (isNonEmptyString(surface.surfaceId)) if (SEGMENT_PATTERN.test(surface.surfaceId)) if (seenSurfaceIds.has(surface.surfaceId)) push(`${path}.surfaceId`, 'must be unique within the record');
|
|
104
|
+
else seenSurfaceIds.add(surface.surfaceId);
|
|
105
|
+
else push(`${path}.surfaceId`, 'must match the SurfaceRef SurfaceId grammar');
|
|
106
|
+
else push(`${path}.surfaceId`, 'must be a non-empty string');
|
|
107
|
+
if ('string' != typeof surface.kind || !SURFACE_KINDS.includes(surface.kind)) push(`${path}.kind`, `must be one of ${SURFACE_KINDS.join(', ')}`);
|
|
108
|
+
if (void 0 !== surface.servedMajor && (!Number.isSafeInteger(surface.servedMajor) || surface.servedMajor < 1)) push(`${path}.servedMajor`, 'must be a positive safe integer when present');
|
|
109
|
+
if (!Array.isArray(surface.locations)) return void push(`${path}.locations`, 'must be an array of locations');
|
|
110
|
+
if (0 === surface.locations.length) push(`${path}.locations`, 'must contain at least one location (no partial records)');
|
|
111
|
+
const seenPlatforms = new Set();
|
|
112
|
+
surface.locations.forEach((location, locationIndex)=>{
|
|
113
|
+
validateLocation(location, `${path}.locations[${locationIndex}]`, seenPlatforms, push);
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
function validateResolvedDeliveryUnit(unit) {
|
|
117
|
+
const issues = [];
|
|
118
|
+
const push = (path, message)=>{
|
|
119
|
+
issues.push({
|
|
120
|
+
path,
|
|
121
|
+
message
|
|
122
|
+
});
|
|
123
|
+
};
|
|
124
|
+
if (!isRecord(unit)) {
|
|
125
|
+
push('', 'must be an object');
|
|
126
|
+
return {
|
|
127
|
+
ok: false,
|
|
128
|
+
issues
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
const raw = unit;
|
|
132
|
+
if (isNonEmptyString(raw.unitId)) {
|
|
133
|
+
if (!isValidUnitId(raw.unitId)) push('unitId', 'must match the SurfaceRef UnitId grammar');
|
|
134
|
+
} else push('unitId', 'must be a non-empty string');
|
|
135
|
+
if (!isNonEmptyString(raw.buildMarker)) push('buildMarker', 'must be a non-empty string');
|
|
136
|
+
if (!isNonEmptyString(raw.sourceRevision)) push('sourceRevision', 'must be a non-empty string');
|
|
137
|
+
if (!isNonEmptyString(raw.baselineCohortId)) push('baselineCohortId', 'must be a non-empty string');
|
|
138
|
+
const compatibility = raw.compatibility;
|
|
139
|
+
if (isRecord(compatibility)) {
|
|
140
|
+
if ('string' != typeof compatibility.status || !COMPATIBILITY_STATUSES.includes(compatibility.status)) push('compatibility.status', `must be one of ${COMPATIBILITY_STATUSES.join(', ')}`);
|
|
141
|
+
if (compatibility.baselineCohortId !== raw.baselineCohortId) push('compatibility.baselineCohortId', 'must equal the record-level baselineCohortId (one verdict per record)');
|
|
142
|
+
if (void 0 !== compatibility.reason && !isNonEmptyString(compatibility.reason)) push('compatibility.reason', 'must be a non-empty string when present');
|
|
143
|
+
} else push('compatibility', 'must be an object');
|
|
144
|
+
if (!Array.isArray(raw.surfaces)) {
|
|
145
|
+
push('surfaces', 'must be an array of surfaces');
|
|
146
|
+
return {
|
|
147
|
+
ok: false,
|
|
148
|
+
issues
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
if (0 === raw.surfaces.length) push('surfaces', 'must contain at least one surface');
|
|
152
|
+
const seenSurfaceIds = new Set();
|
|
153
|
+
raw.surfaces.forEach((surface, surfaceIndex)=>{
|
|
154
|
+
validateSurface(surface, `surfaces[${surfaceIndex}]`, seenSurfaceIds, push);
|
|
155
|
+
});
|
|
156
|
+
return {
|
|
157
|
+
ok: 0 === issues.length,
|
|
158
|
+
issues
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
function selectResolvedSurface(unit, ref) {
|
|
162
|
+
if (unit.unitId !== ref.unitId) return {
|
|
163
|
+
ok: false,
|
|
164
|
+
error: createDiscoveryError('unknown-unit', ref, `Resolved record is for unit ${unit.unitId}, not ${ref.unitId}.`, {
|
|
165
|
+
recordUnitId: unit.unitId
|
|
166
|
+
})
|
|
167
|
+
};
|
|
168
|
+
const surface = unit.surfaces.find((candidate)=>candidate.surfaceId === ref.surfaceId);
|
|
169
|
+
if (void 0 === surface) return {
|
|
170
|
+
ok: false,
|
|
171
|
+
error: createDiscoveryError('unknown-surface', ref, `Unit ${unit.unitId} (buildMarker ${unit.buildMarker}) does not publish surface ${ref.surfaceId}.`, {
|
|
172
|
+
availableSurfaces: unit.surfaces.map((entry)=>entry.surfaceId)
|
|
173
|
+
})
|
|
174
|
+
};
|
|
175
|
+
return {
|
|
176
|
+
ok: true,
|
|
177
|
+
surface
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
function matchDeliveryUnitIdentity(expected, unit, ref) {
|
|
181
|
+
if (expected.unitId === unit.unitId && expected.buildMarker === unit.buildMarker) return;
|
|
182
|
+
return createDiscoveryError('identity-mismatch', ref, `Delivery-unit identity mismatch: expected ${expected.unitId}@${expected.buildMarker}, resolved ${unit.unitId}@${unit.buildMarker}.`, {
|
|
183
|
+
expected,
|
|
184
|
+
resolved: {
|
|
185
|
+
unitId: unit.unitId,
|
|
186
|
+
buildMarker: unit.buildMarker
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
exports.createDiscoveryError = __webpack_exports__.createDiscoveryError;
|
|
191
|
+
exports.matchDeliveryUnitIdentity = __webpack_exports__.matchDeliveryUnitIdentity;
|
|
192
|
+
exports.selectResolvedSurface = __webpack_exports__.selectResolvedSurface;
|
|
193
|
+
exports.validateResolvedDeliveryUnit = __webpack_exports__.validateResolvedDeliveryUnit;
|
|
194
|
+
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
195
|
+
"createDiscoveryError",
|
|
196
|
+
"matchDeliveryUnitIdentity",
|
|
197
|
+
"selectResolvedSurface",
|
|
198
|
+
"validateResolvedDeliveryUnit"
|
|
199
|
+
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
200
|
+
Object.defineProperty(exports, '__esModule', {
|
|
201
|
+
value: true
|
|
202
|
+
});
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import { formatSurfaceRef } from "./surface-ref.mjs";
|
|
2
|
+
import { createDiscoveryError, validateResolvedDeliveryUnit } from "./validation.mjs";
|
|
3
|
+
const DEFAULT_LOCAL_ENVIRONMENTS = [
|
|
4
|
+
'development',
|
|
5
|
+
'local'
|
|
6
|
+
];
|
|
7
|
+
const ENV_STATIC_PROVIDER_NAME = 'env-static';
|
|
8
|
+
function trimTrailingSlashes(value) {
|
|
9
|
+
return value.replace(/\/+$/u, '');
|
|
10
|
+
}
|
|
11
|
+
function envValue(env, name) {
|
|
12
|
+
const value = env[name]?.trim();
|
|
13
|
+
return void 0 !== value && value.length > 0 ? value : void 0;
|
|
14
|
+
}
|
|
15
|
+
function stripMfNamePrefix(value, mfName) {
|
|
16
|
+
return value.startsWith(`${mfName}@`) ? value.slice(mfName.length + 1) : value;
|
|
17
|
+
}
|
|
18
|
+
function createEnvContext(env, allowLocalFallback) {
|
|
19
|
+
return {
|
|
20
|
+
env,
|
|
21
|
+
allowLocalFallback,
|
|
22
|
+
cloudflareDeployEnabled: 'cloudflare' === env.MODERNJS_DEPLOY,
|
|
23
|
+
workersDevSubdomain: envValue(env, 'ULTRAMODERN_CLOUDFLARE_WORKERS_DEV_SUBDOMAIN'),
|
|
24
|
+
requireCloudflarePublicUrls: 'true' === env.ULTRAMODERN_CLOUDFLARE_REQUIRE_PUBLIC_URLS
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function resolveBaseUrl(context, unit) {
|
|
28
|
+
const publicUrlEnv = `ULTRAMODERN_PUBLIC_URL_${unit.envSegment}`;
|
|
29
|
+
const configuredPublicUrl = envValue(context.env, publicUrlEnv);
|
|
30
|
+
if (void 0 !== configuredPublicUrl) return {
|
|
31
|
+
ok: true,
|
|
32
|
+
baseUrl: trimTrailingSlashes(configuredPublicUrl)
|
|
33
|
+
};
|
|
34
|
+
if (context.cloudflareDeployEnabled && void 0 !== context.workersDevSubdomain && void 0 !== unit.workerName) return {
|
|
35
|
+
ok: true,
|
|
36
|
+
baseUrl: `https://${unit.workerName}.${context.workersDevSubdomain}.workers.dev`
|
|
37
|
+
};
|
|
38
|
+
if (context.cloudflareDeployEnabled && context.requireCloudflarePublicUrls) return {
|
|
39
|
+
ok: false,
|
|
40
|
+
reason: `Cloudflare deploy requires ${publicUrlEnv} (or a configured manifest env / ULTRAMODERN_CLOUDFLARE_WORKERS_DEV_SUBDOMAIN with a worker name)`,
|
|
41
|
+
details: {
|
|
42
|
+
publicUrlEnv
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
if (void 0 !== unit.port && context.allowLocalFallback) return {
|
|
46
|
+
ok: true,
|
|
47
|
+
baseUrl: `http://localhost:${unit.port}`
|
|
48
|
+
};
|
|
49
|
+
if (void 0 !== unit.port) return {
|
|
50
|
+
ok: false,
|
|
51
|
+
reason: `No base URL available: set ${publicUrlEnv} (localhost fallback is disabled outside designated local environments)`,
|
|
52
|
+
details: {
|
|
53
|
+
publicUrlEnv
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
return {
|
|
57
|
+
ok: false,
|
|
58
|
+
reason: `No base URL available: set ${publicUrlEnv} or configure a localhost port`,
|
|
59
|
+
details: {
|
|
60
|
+
publicUrlEnv
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function resolveBrowserMfManifest(context, unit) {
|
|
65
|
+
const manifestEnv = `VERTICAL_${unit.envSegment}_MF_MANIFEST`;
|
|
66
|
+
const configuredManifest = envValue(context.env, manifestEnv);
|
|
67
|
+
if (void 0 !== configuredManifest) return {
|
|
68
|
+
ok: true,
|
|
69
|
+
location: {
|
|
70
|
+
platform: 'browser-mf-manifest',
|
|
71
|
+
manifestUrl: stripMfNamePrefix(configuredManifest, unit.mfName)
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
const base = resolveBaseUrl(context, unit);
|
|
75
|
+
if (!base.ok) return {
|
|
76
|
+
ok: false,
|
|
77
|
+
reason: `browser-mf-manifest unavailable (${manifestEnv} unset; ${base.reason})`,
|
|
78
|
+
details: {
|
|
79
|
+
manifestEnv,
|
|
80
|
+
...base.details
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
return {
|
|
84
|
+
ok: true,
|
|
85
|
+
location: {
|
|
86
|
+
platform: 'browser-mf-manifest',
|
|
87
|
+
manifestUrl: `${base.baseUrl}/mf-manifest.json`
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function resolveNodeMfManifest(context, unit) {
|
|
92
|
+
const manifestEnv = `VERTICAL_${unit.envSegment}_BACKEND_MF_MANIFEST`;
|
|
93
|
+
const configuredManifest = envValue(context.env, manifestEnv);
|
|
94
|
+
if (void 0 !== configuredManifest) return {
|
|
95
|
+
ok: true,
|
|
96
|
+
location: {
|
|
97
|
+
platform: 'node-mf-manifest',
|
|
98
|
+
manifestRef: configuredManifest
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
if (void 0 !== unit.port && context.allowLocalFallback) return {
|
|
102
|
+
ok: true,
|
|
103
|
+
location: {
|
|
104
|
+
platform: 'node-mf-manifest',
|
|
105
|
+
manifestRef: `http://localhost:${unit.port}/backend-mf-manifest.json`
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
if (void 0 !== unit.port) return {
|
|
109
|
+
ok: false,
|
|
110
|
+
reason: `node-mf-manifest unavailable: set ${manifestEnv} (localhost fallback is disabled outside designated local environments)`,
|
|
111
|
+
details: {
|
|
112
|
+
manifestEnv
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
return {
|
|
116
|
+
ok: false,
|
|
117
|
+
reason: `node-mf-manifest unavailable: set ${manifestEnv} or configure a localhost port`,
|
|
118
|
+
details: {
|
|
119
|
+
manifestEnv
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function resolveHttpApi(context, unit, config) {
|
|
124
|
+
const base = resolveBaseUrl(context, unit);
|
|
125
|
+
if (!base.ok) return {
|
|
126
|
+
ok: false,
|
|
127
|
+
reason: `http-api unavailable (${base.reason})`,
|
|
128
|
+
details: base.details
|
|
129
|
+
};
|
|
130
|
+
return {
|
|
131
|
+
ok: true,
|
|
132
|
+
location: {
|
|
133
|
+
platform: 'http-api',
|
|
134
|
+
baseUrl: base.baseUrl,
|
|
135
|
+
prefix: config.prefix
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function resolveCloudflareServiceBinding(config) {
|
|
140
|
+
if (0 === config.serviceBinding.length) return {
|
|
141
|
+
ok: false,
|
|
142
|
+
reason: 'cloudflare-service-binding unavailable: empty serviceBinding'
|
|
143
|
+
};
|
|
144
|
+
return {
|
|
145
|
+
ok: true,
|
|
146
|
+
location: {
|
|
147
|
+
platform: 'cloudflare-service-binding',
|
|
148
|
+
serviceBinding: config.serviceBinding,
|
|
149
|
+
...void 0 === config.dispatchNamespace ? {} : {
|
|
150
|
+
dispatchNamespace: config.dispatchNamespace
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
function resolveSurfaceLocations(context, unit, surface) {
|
|
156
|
+
const outcomes = [];
|
|
157
|
+
if (surface.platforms.browserMfManifest) outcomes.push(resolveBrowserMfManifest(context, unit));
|
|
158
|
+
if (surface.platforms.nodeMfManifest) outcomes.push(resolveNodeMfManifest(context, unit));
|
|
159
|
+
if (void 0 !== surface.platforms.httpApi) outcomes.push(resolveHttpApi(context, unit, surface.platforms.httpApi));
|
|
160
|
+
if (void 0 !== surface.platforms.cloudflareServiceBinding) outcomes.push(resolveCloudflareServiceBinding(surface.platforms.cloudflareServiceBinding));
|
|
161
|
+
const failure = outcomes.find((outcome)=>!outcome.ok);
|
|
162
|
+
if (void 0 !== failure && !failure.ok) return {
|
|
163
|
+
ok: false,
|
|
164
|
+
reason: `surface ${surface.surfaceId}: ${failure.reason}`,
|
|
165
|
+
details: failure.details
|
|
166
|
+
};
|
|
167
|
+
const locations = outcomes.flatMap((outcome)=>outcome.ok ? [
|
|
168
|
+
outcome.location
|
|
169
|
+
] : []);
|
|
170
|
+
if (0 === locations.length) return {
|
|
171
|
+
ok: false,
|
|
172
|
+
reason: `surface ${surface.surfaceId} declares no platform locations`
|
|
173
|
+
};
|
|
174
|
+
return {
|
|
175
|
+
ok: true,
|
|
176
|
+
surface: {
|
|
177
|
+
surfaceId: surface.surfaceId,
|
|
178
|
+
kind: surface.kind,
|
|
179
|
+
locations
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
function createEnvStaticSurfaceResolutionProvider(options) {
|
|
184
|
+
const env = options.env ?? {};
|
|
185
|
+
const localEnvironments = new Set(options.localEnvironments ?? DEFAULT_LOCAL_ENVIRONMENTS);
|
|
186
|
+
const identityVerification = options.identityVerification ?? 'static-trust';
|
|
187
|
+
const unitsById = new Map(options.units.map((unit)=>[
|
|
188
|
+
unit.unitId,
|
|
189
|
+
unit
|
|
190
|
+
]));
|
|
191
|
+
return {
|
|
192
|
+
name: ENV_STATIC_PROVIDER_NAME,
|
|
193
|
+
resolve (ref, environment) {
|
|
194
|
+
const refString = formatSurfaceRef(ref);
|
|
195
|
+
const unit = unitsById.get(ref.unitId);
|
|
196
|
+
if (void 0 === unit) return {
|
|
197
|
+
ok: false,
|
|
198
|
+
error: createDiscoveryError('unknown-unit', refString, `No statically configured delivery unit ${ref.unitId}.`, {
|
|
199
|
+
environment,
|
|
200
|
+
knownUnits: [
|
|
201
|
+
...unitsById.keys()
|
|
202
|
+
]
|
|
203
|
+
})
|
|
204
|
+
};
|
|
205
|
+
if (!unit.surfaces.some((surface)=>surface.surfaceId === ref.surfaceId)) return {
|
|
206
|
+
ok: false,
|
|
207
|
+
error: createDiscoveryError('unknown-surface', refString, `Unit ${unit.unitId} does not declare surface ${ref.surfaceId}.`, {
|
|
208
|
+
environment,
|
|
209
|
+
availableSurfaces: unit.surfaces.map((surface)=>surface.surfaceId)
|
|
210
|
+
})
|
|
211
|
+
};
|
|
212
|
+
let effectiveUnit = unit;
|
|
213
|
+
if (void 0 !== ref.major) {
|
|
214
|
+
const majorConfig = (unit.majors ?? []).find((candidate)=>candidate.major === ref.major);
|
|
215
|
+
if (void 0 === majorConfig) return {
|
|
216
|
+
ok: false,
|
|
217
|
+
error: createDiscoveryError('major-not-published', refString, `Unit ${unit.unitId} has no materialization for external major v${ref.major}.`, {
|
|
218
|
+
environment,
|
|
219
|
+
publishedMajors: (unit.majors ?? []).map((candidate)=>candidate.major)
|
|
220
|
+
})
|
|
221
|
+
};
|
|
222
|
+
effectiveUnit = {
|
|
223
|
+
...unit,
|
|
224
|
+
envSegment: majorConfig.envSegment ?? `${unit.envSegment}_V${majorConfig.major}`,
|
|
225
|
+
port: majorConfig.port,
|
|
226
|
+
workerName: majorConfig.workerName
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
const context = createEnvContext(env, localEnvironments.has(environment));
|
|
230
|
+
const surfaces = [];
|
|
231
|
+
for (const surfaceConfig of unit.surfaces){
|
|
232
|
+
const resolved = resolveSurfaceLocations(context, effectiveUnit, surfaceConfig);
|
|
233
|
+
if (!resolved.ok) return {
|
|
234
|
+
ok: false,
|
|
235
|
+
error: createDiscoveryError('provider-unavailable', refString, `env-static provider cannot assemble a complete record for ${unit.unitId}: ${resolved.reason}.`, {
|
|
236
|
+
environment,
|
|
237
|
+
...resolved.details
|
|
238
|
+
})
|
|
239
|
+
};
|
|
240
|
+
surfaces.push(void 0 === ref.major ? resolved.surface : {
|
|
241
|
+
...resolved.surface,
|
|
242
|
+
servedMajor: ref.major
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
const record = {
|
|
246
|
+
unitId: unit.unitId,
|
|
247
|
+
buildMarker: unit.buildMarker,
|
|
248
|
+
sourceRevision: unit.sourceRevision,
|
|
249
|
+
baselineCohortId: unit.baselineCohortId,
|
|
250
|
+
surfaces,
|
|
251
|
+
compatibility: {
|
|
252
|
+
status: 'compatible',
|
|
253
|
+
baselineCohortId: unit.baselineCohortId,
|
|
254
|
+
...'static-trust' === identityVerification ? {
|
|
255
|
+
reason: 'static-identity-unverified'
|
|
256
|
+
} : {}
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
const validation = validateResolvedDeliveryUnit(record);
|
|
260
|
+
if (!validation.ok) return {
|
|
261
|
+
ok: false,
|
|
262
|
+
error: createDiscoveryError('provider-unavailable', refString, `env-static provider assembled an invalid record for ${unit.unitId}.`, {
|
|
263
|
+
environment,
|
|
264
|
+
issues: validation.issues
|
|
265
|
+
})
|
|
266
|
+
};
|
|
267
|
+
return {
|
|
268
|
+
ok: true,
|
|
269
|
+
unit: record
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
export { DEFAULT_LOCAL_ENVIRONMENTS, ENV_STATIC_PROVIDER_NAME, createEnvStaticSurfaceResolutionProvider };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { DEFAULT_LOCAL_ENVIRONMENTS, ENV_STATIC_PROVIDER_NAME, createEnvStaticSurfaceResolutionProvider } from "./env-static-provider.mjs";
|
|
2
|
+
export { formatSurfaceRef, parseSurfaceRef, validateSurfaceRef } from "./surface-ref.mjs";
|
|
3
|
+
export { createDiscoveryError, matchDeliveryUnitIdentity, selectResolvedSurface, validateResolvedDeliveryUnit } from "./validation.mjs";
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
const SEGMENT_PATTERN = /^[A-Za-z0-9._-]+$/;
|
|
2
|
+
const MAJOR_PATTERN = /^v[1-9][0-9]*$/;
|
|
3
|
+
function parseSurfaceRef(input) {
|
|
4
|
+
if ('' === input) return {
|
|
5
|
+
ok: false,
|
|
6
|
+
error: {
|
|
7
|
+
code: 'empty'
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
const hashCount = countChar(input, '#');
|
|
11
|
+
if (0 === hashCount) return {
|
|
12
|
+
ok: false,
|
|
13
|
+
error: {
|
|
14
|
+
code: 'missing-surface-separator'
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
if (hashCount > 1) return {
|
|
18
|
+
ok: false,
|
|
19
|
+
error: {
|
|
20
|
+
code: 'multiple-surface-separators'
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
const hashIndex = input.indexOf('#');
|
|
24
|
+
const unitPart = input.slice(0, hashIndex);
|
|
25
|
+
const rest = input.slice(hashIndex + 1);
|
|
26
|
+
const atIndex = rest.indexOf('@');
|
|
27
|
+
const surfaceId = -1 === atIndex ? rest : rest.slice(0, atIndex);
|
|
28
|
+
const ref = {
|
|
29
|
+
unitId: unitPart,
|
|
30
|
+
surfaceId
|
|
31
|
+
};
|
|
32
|
+
if (-1 !== atIndex) {
|
|
33
|
+
const majorPart = rest.slice(atIndex + 1);
|
|
34
|
+
if ('' === majorPart) return {
|
|
35
|
+
ok: false,
|
|
36
|
+
error: {
|
|
37
|
+
code: 'empty-major'
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
if (!MAJOR_PATTERN.test(majorPart)) return {
|
|
41
|
+
ok: false,
|
|
42
|
+
error: {
|
|
43
|
+
code: 'invalid-major',
|
|
44
|
+
value: majorPart
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
ref.major = Number(majorPart.slice(1));
|
|
48
|
+
}
|
|
49
|
+
const error = validateSurfaceRef(ref);
|
|
50
|
+
return void 0 === error ? {
|
|
51
|
+
ok: true,
|
|
52
|
+
ref
|
|
53
|
+
} : {
|
|
54
|
+
ok: false,
|
|
55
|
+
error
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function formatSurfaceRef(ref) {
|
|
59
|
+
const error = validateSurfaceRef(ref);
|
|
60
|
+
if (void 0 !== error) throw new TypeError(`Cannot format invalid SurfaceRef: ${error.code}.`);
|
|
61
|
+
const base = `${ref.unitId}#${ref.surfaceId}`;
|
|
62
|
+
return void 0 === ref.major ? base : `${base}@v${ref.major}`;
|
|
63
|
+
}
|
|
64
|
+
function validateSurfaceRef(ref) {
|
|
65
|
+
if ('' === ref.unitId) return {
|
|
66
|
+
code: 'empty-unit-id'
|
|
67
|
+
};
|
|
68
|
+
for (const segment of ref.unitId.split('/'))if (!SEGMENT_PATTERN.test(segment)) return {
|
|
69
|
+
code: 'invalid-unit-id',
|
|
70
|
+
segment
|
|
71
|
+
};
|
|
72
|
+
if ('' === ref.surfaceId) return {
|
|
73
|
+
code: 'empty-surface-id'
|
|
74
|
+
};
|
|
75
|
+
if (!SEGMENT_PATTERN.test(ref.surfaceId)) return {
|
|
76
|
+
code: 'invalid-surface-id'
|
|
77
|
+
};
|
|
78
|
+
if (void 0 !== ref.major && (!Number.isSafeInteger(ref.major) || ref.major < 1)) return {
|
|
79
|
+
code: 'invalid-major',
|
|
80
|
+
value: String(ref.major)
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function countChar(input, char) {
|
|
84
|
+
let count = 0;
|
|
85
|
+
for (const current of input)if (current === char) count += 1;
|
|
86
|
+
return count;
|
|
87
|
+
}
|
|
88
|
+
export { formatSurfaceRef, parseSurfaceRef, validateSurfaceRef };
|
|
File without changes
|