@mermanjs/web-analysis 0.8.0-alpha.5

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 (68) hide show
  1. package/LICENSE +231 -0
  2. package/README.md +31 -0
  3. package/THIRD_PARTY_LICENSES/dompurify/LICENSE +202 -0
  4. package/THIRD_PARTY_LICENSES/mermaid/LICENSE +21 -0
  5. package/THIRD_PARTY_LICENSES/rust-cargo-dependencies.json +1237 -0
  6. package/THIRD_PARTY_LICENSES/sanitize-url/LICENSE +21 -0
  7. package/THIRD_PARTY_LICENSES/zenuml-core/LICENSE +21 -0
  8. package/THIRD_PARTY_NOTICES.md +72 -0
  9. package/artifacts/provenance.json +317 -0
  10. package/artifacts/wasm/merman_wasm.d.ts +102 -0
  11. package/artifacts/wasm/merman_wasm.js +642 -0
  12. package/artifacts/wasm/merman_wasm_bg.wasm +0 -0
  13. package/artifacts/wasm/merman_wasm_bg.wasm.d.ts +31 -0
  14. package/dist/generated/binding-contract.js +497 -0
  15. package/dist/generated/binding-contract.js.map +1 -0
  16. package/dist/generated/capability-surface.d.ts +118 -0
  17. package/dist/generated/capability-surface.d.ts.map +1 -0
  18. package/dist/generated/capability-surface.js +171 -0
  19. package/dist/generated/capability-surface.js.map +1 -0
  20. package/dist/generated/diagram-catalog.d.ts +2 -0
  21. package/dist/generated/diagram-catalog.d.ts.map +1 -0
  22. package/dist/generated/diagram-catalog.js +40 -0
  23. package/dist/generated/diagram-catalog.js.map +1 -0
  24. package/dist/generated/resource-contract.d.ts +31 -0
  25. package/dist/generated/resource-contract.d.ts.map +1 -0
  26. package/dist/generated/resource-contract.js +193 -0
  27. package/dist/generated/resource-contract.js.map +1 -0
  28. package/dist/generated/text-measurement-abi.d.ts +154 -0
  29. package/dist/generated/text-measurement-abi.d.ts.map +1 -0
  30. package/dist/generated/text-measurement-abi.js +50 -0
  31. package/dist/generated/text-measurement-abi.js.map +1 -0
  32. package/dist/generated/token-descriptor.d.ts +205 -0
  33. package/dist/generated/token-descriptor.d.ts.map +1 -0
  34. package/dist/index.d.ts +16 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/package-entries/analysis.d.ts +33 -0
  37. package/dist/package-entries/analysis.d.ts.map +1 -0
  38. package/dist/package-entries/analysis.js +67 -0
  39. package/dist/package-entries/analysis.js.map +1 -0
  40. package/dist/public-catalog.d.ts +127 -0
  41. package/dist/public-catalog.d.ts.map +1 -0
  42. package/dist/public-catalog.js +130 -0
  43. package/dist/public-catalog.js.map +1 -0
  44. package/dist/public-types.d.ts +764 -0
  45. package/dist/public-types.d.ts.map +1 -0
  46. package/dist/runtime-analysis.d.ts +11 -0
  47. package/dist/runtime-analysis.d.ts.map +1 -0
  48. package/dist/runtime-analysis.js +214 -0
  49. package/dist/runtime-analysis.js.map +1 -0
  50. package/dist/runtime-ascii.d.ts +6 -0
  51. package/dist/runtime-ascii.d.ts.map +1 -0
  52. package/dist/runtime-core.d.ts +18 -0
  53. package/dist/runtime-core.d.ts.map +1 -0
  54. package/dist/runtime-core.js +946 -0
  55. package/dist/runtime-core.js.map +1 -0
  56. package/dist/runtime-editor.d.ts +16 -0
  57. package/dist/runtime-editor.d.ts.map +1 -0
  58. package/dist/runtime-render.d.ts +13 -0
  59. package/dist/runtime-render.d.ts.map +1 -0
  60. package/dist/runtime-state.d.ts +16 -0
  61. package/dist/runtime-state.d.ts.map +1 -0
  62. package/dist/runtime-state.js +27 -0
  63. package/dist/runtime-state.js.map +1 -0
  64. package/dist/surface-runtime.js +45 -0
  65. package/dist/surface-runtime.js.map +1 -0
  66. package/dist/svg-safety.d.ts +27 -0
  67. package/dist/svg-safety.d.ts.map +1 -0
  68. package/package.json +41 -0
@@ -0,0 +1,946 @@
1
+ import { createMermanRuntimeState, currentMermanRuntimeState, } from "./runtime-state.js";
2
+ import { MERMAN_TEXT_MEASUREMENT_PROTOCOL_VERSION, } from "./generated/text-measurement-abi.js";
3
+ import { BINDING_OPTION_GROUP_SPECS, BINDING_PAYLOAD_SCHEMAS, BINDING_TRANSPORT_EXPOSURE_SPECS, CAPABILITY_SPECS, CONSTRUCTOR_SERVICE_SPECS, METADATA_SPECS, RUNTIME_CATALOG_FIELD_IDENTIFIER_PATTERN, RUNTIME_CATALOG_IDENTIFIER_PATTERN, RUNTIME_CATALOG_MAX_SAFE_INTEGER, RUNTIME_CATALOG_SCHEMA_VERSION, TEXT_MEASUREMENT_PROVIDER_SPECS, } from "./generated/binding-contract.js";
4
+ import { WEB_BINDING_OPERATIONS, } from "./generated/capability-surface.js";
5
+ import { BINDING_OPTIONS_SCHEMA_VERSION, tightenResourceOptions, } from "./generated/resource-contract.js";
6
+ import { isDiagramType, isThemeName, } from "./public-catalog.js";
7
+ const defaultRuntimeState = createMermanRuntimeState(defaultLoader);
8
+ const METADATA_SPEC_BY_ID = new Map(METADATA_SPECS.map((spec) => [spec.id, spec]));
9
+ const CAPABILITY_SPEC_BY_ID = new Map(CAPABILITY_SPECS.map((spec) => [spec.id, spec]));
10
+ const RUNTIME_CATALOG_IDENTIFIER = new RegExp(RUNTIME_CATALOG_IDENTIFIER_PATTERN);
11
+ const RUNTIME_CATALOG_FIELD_IDENTIFIER = new RegExp(RUNTIME_CATALOG_FIELD_IDENTIFIER_PATTERN);
12
+ const BINDING_OPTION_GROUP_SPEC_BY_ID = new Map(BINDING_OPTION_GROUP_SPECS.map((spec) => [spec.id, spec]));
13
+ const CONSTRUCTOR_SERVICE_SPEC_BY_ID = new Map(CONSTRUCTOR_SERVICE_SPECS.map((spec) => [spec.id, spec]));
14
+ const WEB_TRANSPORT_EXPOSURE = BINDING_TRANSPORT_EXPOSURE_SPECS.find((spec) => spec.id === "web");
15
+ if (WEB_TRANSPORT_EXPOSURE === undefined) {
16
+ throw new Error("Generated binding contract is missing the Web transport exposure.");
17
+ }
18
+ const WEB_BINDING_OPERATION_SPEC_BY_ID = new Map(WEB_BINDING_OPERATIONS.map((spec) => [spec.id, spec]));
19
+ const WEB_PAYLOAD_SCHEMA_VERSION_BY_ID = new Map(WEB_TRANSPORT_EXPOSURE.payload_schema_ids.map((id) => {
20
+ const spec = BINDING_PAYLOAD_SCHEMAS.find((candidate) => candidate.id === id);
21
+ if (spec === undefined) {
22
+ throw new Error(`Generated Web payload schema ${id} has no typed schema contract.`);
23
+ }
24
+ return [id, spec.version];
25
+ }));
26
+ const WEB_CONSTRUCTOR_SERVICE_CANDIDATE_IDS = new Set(WEB_TRANSPORT_EXPOSURE.constructor_service_candidate_ids);
27
+ const TEXT_MEASUREMENT_PROVIDER_SPEC_BY_ID = new Map(TEXT_MEASUREMENT_PROVIDER_SPECS.map((spec) => [spec.id, spec]));
28
+ export function currentRuntimeState() {
29
+ return currentMermanRuntimeState(defaultRuntimeState);
30
+ }
31
+ export function initMerman(init) {
32
+ const state = currentRuntimeState();
33
+ if (state.wasmModule) {
34
+ return Promise.resolve(state.wasmModule);
35
+ }
36
+ if (state.initPromise) {
37
+ return state.initPromise;
38
+ }
39
+ state.initPromise = doInit(state, init).catch((error) => {
40
+ state.initPromise = null;
41
+ throw error;
42
+ });
43
+ return state.initPromise;
44
+ }
45
+ async function doInit(state, init) {
46
+ const loader = typeof init === "function" ? init : init?.loader;
47
+ const wasm = typeof init === "function" ? undefined : init?.wasm;
48
+ const module = loader ? await loader() : await state.defaultLoader();
49
+ if (wasm === undefined) {
50
+ await module.default();
51
+ }
52
+ else {
53
+ await module.default({ module_or_path: wasm });
54
+ }
55
+ state.wasmModule = module;
56
+ return module;
57
+ }
58
+ async function defaultLoader() {
59
+ throw new Error("The shared @mermanjs/web implementation has no WASM artifact. Import one browser package entry such as @mermanjs/web, @mermanjs/web-render, @mermanjs/web-analysis, @mermanjs/web-editor, or @mermanjs/web-ascii.");
60
+ }
61
+ export function getMerman() {
62
+ const state = currentRuntimeState();
63
+ if (!state.wasmModule) {
64
+ throw new Error("Merman WASM is not initialized. Call initMerman() first.");
65
+ }
66
+ return state.wasmModule;
67
+ }
68
+ export function isMermanInitialized() {
69
+ return currentRuntimeState().wasmModule !== null;
70
+ }
71
+ export const UNAVAILABLE_DIAGRAM_DETECTION = Object.freeze({
72
+ status: "unavailable",
73
+ validity: "unknown",
74
+ diagramType: null,
75
+ syntaxId: null,
76
+ effectiveLayoutId: null,
77
+ });
78
+ export function runtimeCatalog() {
79
+ const state = currentRuntimeState();
80
+ state.runtimeCatalogCache ?? (state.runtimeCatalogCache = normalizeRuntimeCatalog(getMerman().runtimeCatalog()));
81
+ return structuredCloneValue(state.runtimeCatalogCache);
82
+ }
83
+ export function presentationCatalog() {
84
+ const state = currentRuntimeState();
85
+ state.presentationCatalogCache ?? (state.presentationCatalogCache = normalizePresentationCatalog(getMerman().presentationCatalog()));
86
+ return structuredCloneValue(state.presentationCatalogCache);
87
+ }
88
+ export function supportedDiagrams() {
89
+ const state = currentRuntimeState();
90
+ state.supportedDiagramsCache ?? (state.supportedDiagramsCache = getMerman().supportedDiagrams().map(assertDiagramType));
91
+ return [...state.supportedDiagramsCache];
92
+ }
93
+ export function diagramFamilyCapabilities() {
94
+ return cachedDiagramFamilyCapabilities().map((capability) => ({ ...capability }));
95
+ }
96
+ function cachedDiagramFamilyCapabilities() {
97
+ const state = currentRuntimeState();
98
+ state.diagramFamilyCapabilitiesCache ?? (state.diagramFamilyCapabilitiesCache = getMerman()
99
+ .diagramFamilyCapabilities()
100
+ .map(normalizeDiagramFamilyCapability));
101
+ return state.diagramFamilyCapabilitiesCache;
102
+ }
103
+ export function supportedThemes() {
104
+ const state = currentRuntimeState();
105
+ state.supportedThemesCache ?? (state.supportedThemesCache = getMerman().supportedThemes().map(assertThemeName));
106
+ return [...state.supportedThemesCache];
107
+ }
108
+ export function transportApiVersion() {
109
+ return assertSafeIntegerField(getMerman().transportApiVersion(), "Web transport API version", 1);
110
+ }
111
+ export function packageVersion() {
112
+ return getMerman().packageVersion();
113
+ }
114
+ export function encodeOptions(options) {
115
+ if (options === undefined) {
116
+ return undefined;
117
+ }
118
+ return typeof options === "string" ? options : JSON.stringify(options);
119
+ }
120
+ export function withResourceOptions(options, resources) {
121
+ const result = { ...options };
122
+ const wrappers = ["analysis", "merman"].filter((key) => Object.prototype.hasOwnProperty.call(options, key));
123
+ if (wrappers.length > 1) {
124
+ throw new TypeError("Merman options must not contain both analysis and merman wrappers.");
125
+ }
126
+ if (wrappers.length !== 0 &&
127
+ Object.prototype.hasOwnProperty.call(options, "resources")) {
128
+ throw new TypeError("Merman options must not mix top-level resources with an analysis or merman wrapper.");
129
+ }
130
+ const wrapperKey = wrappers[0];
131
+ if (wrapperKey !== undefined) {
132
+ const wrapper = options[wrapperKey];
133
+ if (!isRecord(wrapper)) {
134
+ throw new TypeError(`Merman ${wrapperKey} wrapper must be an object.`);
135
+ }
136
+ result[wrapperKey] = {
137
+ ...wrapper,
138
+ resources: tightenResourceOptions(resources, wrapper.resources),
139
+ };
140
+ }
141
+ else {
142
+ result.resources = tightenResourceOptions(resources, options.resources);
143
+ }
144
+ return result;
145
+ }
146
+ function assertDiagramType(diagram) {
147
+ if (isDiagramType(diagram)) {
148
+ return diagram;
149
+ }
150
+ throw new Error(`Merman WASM returned unknown diagram type: ${diagram}`);
151
+ }
152
+ function isRecord(value) {
153
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
154
+ return false;
155
+ }
156
+ const prototype = Object.getPrototypeOf(value);
157
+ return prototype === Object.prototype || prototype === null;
158
+ }
159
+ function normalizeDiagramFamilyCapability(capability) {
160
+ if (!capability || typeof capability !== "object") {
161
+ throw new Error("Merman WASM returned an invalid diagram family capability.");
162
+ }
163
+ if (typeof capability.diagram_type !== "string") {
164
+ throw new Error("Merman WASM returned an invalid diagram family capability.");
165
+ }
166
+ const metadataId = capability.metadata_id === undefined || capability.metadata_id === null
167
+ ? null
168
+ : assertDiagramType(String(capability.metadata_id));
169
+ return {
170
+ diagram_type: capability.diagram_type,
171
+ logical_family_kind: assertStringField(capability.logical_family_kind, "diagram logical family kind"),
172
+ metadata_id: metadataId,
173
+ render_model_kind: assertNullableStringField(capability.render_model_kind, "diagram render model kind"),
174
+ has_detector: assertBooleanField(capability.has_detector, "diagram detector capability"),
175
+ has_semantic_parser: assertBooleanField(capability.has_semantic_parser, "diagram semantic parser capability"),
176
+ has_editor_parser: assertBooleanField(capability.has_editor_parser, "diagram editor parser capability"),
177
+ has_combined_parser: assertBooleanField(capability.has_combined_parser, "diagram combined parser capability"),
178
+ has_render_parser: assertBooleanField(capability.has_render_parser, "diagram render parser capability"),
179
+ has_header: assertBooleanField(capability.has_header, "diagram header capability"),
180
+ config_namespace: assertNullableStringField(capability.config_namespace, "diagram config namespace"),
181
+ };
182
+ }
183
+ function assertStringField(value, label) {
184
+ if (typeof value === "string") {
185
+ return value;
186
+ }
187
+ throw new Error(`Merman WASM returned an invalid ${label}.`);
188
+ }
189
+ function assertNullableStringField(value, label) {
190
+ if (value === undefined || value === null) {
191
+ return null;
192
+ }
193
+ return assertStringField(value, label);
194
+ }
195
+ function assertBooleanField(value, label) {
196
+ if (typeof value === "boolean") {
197
+ return value;
198
+ }
199
+ throw new Error(`Merman WASM returned an invalid ${label}.`);
200
+ }
201
+ function assertThemeName(theme) {
202
+ if (isThemeName(theme)) {
203
+ return theme;
204
+ }
205
+ throw new Error(`Merman WASM returned unknown theme: ${theme}`);
206
+ }
207
+ function normalizeRuntimeCatalog(value) {
208
+ if (!isRecord(value) || value.schema_version !== RUNTIME_CATALOG_SCHEMA_VERSION) {
209
+ throw new Error("Merman WASM returned an unsupported runtime catalog schema.");
210
+ }
211
+ assertRequiredRecordKeys(value, [
212
+ "capabilities",
213
+ "metadata_ids",
214
+ "options_schema_versions",
215
+ "output_contracts",
216
+ "package_version",
217
+ "payload_schemas",
218
+ "registry",
219
+ "resources",
220
+ "schema_version",
221
+ "transport_api_version",
222
+ ], "Merman WASM runtime catalog");
223
+ const catalogTransportApiVersion = assertSafeIntegerField(value.transport_api_version, "runtime transport API version", 1);
224
+ if (catalogTransportApiVersion !== transportApiVersion()) {
225
+ throw new Error("Merman WASM runtime catalog transport API does not match the loaded module.");
226
+ }
227
+ if (typeof value.package_version !== "string" ||
228
+ value.package_version.length === 0 ||
229
+ value.package_version !== packageVersion()) {
230
+ throw new Error("Merman WASM runtime catalog package version does not match the loaded module.");
231
+ }
232
+ if (!isRecord(value.registry)) {
233
+ throw new Error("Merman WASM returned an invalid runtime registry catalog.");
234
+ }
235
+ assertRequiredRecordKeys(value.registry, ["diagram_family_count"], "Merman WASM runtime registry catalog");
236
+ const diagramFamilyCount = assertSafeIntegerField(value.registry.diagram_family_count, "runtime registry diagram family count", 0);
237
+ if (!isRecord(value.resources)) {
238
+ throw new Error("Merman WASM returned an invalid runtime resource contract.");
239
+ }
240
+ const capabilities = normalizeRuntimeCapabilities(value.capabilities);
241
+ const usesSvgPipeline = runtimeUsesSvgPipeline(capabilities);
242
+ const metadataIds = normalizeRuntimeMetadataIds(value.metadata_ids, capabilities);
243
+ const optionGroupIds = Object.prototype.hasOwnProperty.call(value, "option_group_ids")
244
+ ? normalizeRuntimeOptionGroupIds(value.option_group_ids, capabilities, usesSvgPipeline)
245
+ : [];
246
+ const constructorServices = normalizeRuntimeConstructorServices(value, capabilities);
247
+ const optionsSchemaVersions = normalizeSortedPositiveIntegers(value.options_schema_versions, "runtime options schema versions");
248
+ if (!optionsSchemaVersions.includes(BINDING_OPTIONS_SCHEMA_VERSION)) {
249
+ throw new Error(`Merman WASM runtime catalog does not advertise options schema v${BINDING_OPTIONS_SCHEMA_VERSION}.`);
250
+ }
251
+ return {
252
+ ...structuredCloneValue(value),
253
+ schema_version: RUNTIME_CATALOG_SCHEMA_VERSION,
254
+ transport_api_version: catalogTransportApiVersion,
255
+ package_version: value.package_version,
256
+ options_schema_versions: optionsSchemaVersions,
257
+ payload_schemas: normalizeRuntimePayloadSchemas(value.payload_schemas),
258
+ metadata_ids: metadataIds,
259
+ option_group_ids: optionGroupIds,
260
+ constructor_service_ids: constructorServices.ids,
261
+ constructor_service_contracts: constructorServices.contracts,
262
+ capabilities,
263
+ output_contracts: normalizeRuntimeOutputContracts(value.output_contracts, capabilities.output_ids),
264
+ registry: {
265
+ ...structuredCloneValue(value.registry),
266
+ diagram_family_count: diagramFamilyCount,
267
+ },
268
+ resources: normalizeRuntimeResourceContract(value.resources, new Set(capabilities.operation_ids)),
269
+ };
270
+ }
271
+ function runtimeUsesSvgPipeline(capabilities) {
272
+ return capabilities.capability_ids.includes("svg") || capabilities.text_measurement !== null;
273
+ }
274
+ function normalizeRuntimeMetadataIds(value, capabilities) {
275
+ const ids = normalizeSortedIdentifierIds(value, "runtime metadata IDs");
276
+ const capabilityIds = new Set(capabilities.capability_ids);
277
+ for (const id of ids) {
278
+ const spec = METADATA_SPEC_BY_ID.get(id);
279
+ if (spec?.required_capability_id !== null &&
280
+ spec?.required_capability_id !== undefined &&
281
+ !capabilityIds.has(spec.required_capability_id)) {
282
+ throw new Error(`Merman WASM runtime metadata ${id} requires capability ${spec.required_capability_id}.`);
283
+ }
284
+ }
285
+ return ids;
286
+ }
287
+ function normalizeRuntimeOptionGroupIds(value, capabilities, usesSvgPipeline) {
288
+ const ids = normalizeSortedOptionGroupIds(value);
289
+ const capabilityIds = new Set(capabilities.capability_ids);
290
+ const expectedKnownIds = BINDING_OPTION_GROUP_SPECS
291
+ .filter((spec) => spec.always_available ||
292
+ (spec.requires_svg_pipeline && usesSvgPipeline) ||
293
+ spec.any_capability_ids.some((id) => capabilityIds.has(id)))
294
+ .map((spec) => spec.id)
295
+ .sort();
296
+ const actualKnownIds = ids.filter((id) => BINDING_OPTION_GROUP_SPEC_BY_ID.has(id));
297
+ if (!sameStringArrays(actualKnownIds, expectedKnownIds)) {
298
+ throw new Error("Merman WASM runtime option group IDs do not match the artifact capability closure.");
299
+ }
300
+ return ids;
301
+ }
302
+ function normalizeRuntimeConstructorServices(catalog, capabilities) {
303
+ const hasIds = Object.prototype.hasOwnProperty.call(catalog, "constructor_service_ids");
304
+ const hasContracts = Object.prototype.hasOwnProperty.call(catalog, "constructor_service_contracts");
305
+ if (hasIds !== hasContracts) {
306
+ throw new Error("Merman WASM runtime constructor service IDs and contracts must appear together.");
307
+ }
308
+ if (!hasIds) {
309
+ return { ids: [], contracts: [] };
310
+ }
311
+ const ids = normalizeSortedIdentifierIds(catalog.constructor_service_ids, "runtime constructor service IDs");
312
+ const usesSvgPipeline = runtimeUsesSvgPipeline(capabilities);
313
+ for (const id of ids) {
314
+ const knownSpec = CONSTRUCTOR_SERVICE_SPEC_BY_ID.get(id);
315
+ if (knownSpec === undefined) {
316
+ continue;
317
+ }
318
+ if (!WEB_CONSTRUCTOR_SERVICE_CANDIDATE_IDS.has(id)) {
319
+ throw new Error(`Merman WASM advertises constructor service ${id}, which this Web facade cannot provide.`);
320
+ }
321
+ if (knownSpec.requires_svg_pipeline && !usesSvgPipeline) {
322
+ throw new Error(`Merman WASM advertises constructor service ${id} without an SVG pipeline.`);
323
+ }
324
+ }
325
+ const contracts = normalizeRuntimeConstructorServiceContracts(catalog.constructor_service_contracts);
326
+ const contractIds = contracts.map((contract) => contract.id);
327
+ if (!sameStringArrays(ids, contractIds)) {
328
+ throw new Error("Merman WASM runtime constructor service IDs and contracts must be in one-to-one correspondence.");
329
+ }
330
+ validateRuntimeConstructorServiceProviders(contracts, capabilities);
331
+ return { ids, contracts };
332
+ }
333
+ function normalizeRuntimeConstructorServiceContracts(value) {
334
+ if (!Array.isArray(value)) {
335
+ throw new Error("Merman WASM returned invalid runtime constructor service contracts.");
336
+ }
337
+ const contracts = value.map((contract) => {
338
+ if (!isRecord(contract)) {
339
+ throw new Error("Merman WASM returned an invalid runtime constructor service contract.");
340
+ }
341
+ assertRequiredRecordKeys(contract, ["id", "provided_text_measurement_provider_ids", "resource_limits"], "Merman WASM runtime constructor service contract");
342
+ const id = assertRuntimeIdentifier(contract.id, "runtime constructor service contract ID");
343
+ const providerIds = normalizeSortedIdentifierIds(contract.provided_text_measurement_provider_ids, `runtime constructor service ${id} text measurement provider IDs`);
344
+ return {
345
+ ...structuredCloneValue(contract),
346
+ id,
347
+ provided_text_measurement_provider_ids: providerIds,
348
+ resource_limits: normalizeRuntimeConstructorResourceLimits(contract.resource_limits, id),
349
+ };
350
+ });
351
+ const ids = contracts.map((contract) => contract.id);
352
+ for (let index = 1; index < ids.length; index += 1) {
353
+ if (ids[index - 1] >= ids[index]) {
354
+ throw new Error("Merman WASM runtime constructor service contracts must be sorted and unique by ID.");
355
+ }
356
+ }
357
+ return contracts;
358
+ }
359
+ function normalizeRuntimeConstructorResourceLimits(value, serviceId) {
360
+ if (!Array.isArray(value)) {
361
+ throw new Error(`Merman WASM returned invalid runtime constructor service ${serviceId} resource limits.`);
362
+ }
363
+ const limits = value.map((limit) => {
364
+ if (!isRecord(limit)) {
365
+ throw new Error(`Merman WASM returned an invalid runtime constructor service ${serviceId} resource limit.`);
366
+ }
367
+ assertRequiredRecordKeys(limit, ["id", "phase", "unit", "description", "value"], `Merman WASM runtime constructor service ${serviceId} resource limit`);
368
+ const id = assertRuntimeFieldIdentifier(limit.id, `runtime constructor service ${serviceId} resource limit ID`);
369
+ if (typeof limit.phase !== "string" ||
370
+ limit.phase.length === 0 ||
371
+ typeof limit.unit !== "string" ||
372
+ limit.unit.length === 0 ||
373
+ typeof limit.description !== "string" ||
374
+ limit.description.length === 0) {
375
+ throw new Error(`Merman WASM returned an invalid runtime constructor service ${serviceId} resource limit.`);
376
+ }
377
+ return {
378
+ ...structuredCloneValue(limit),
379
+ id,
380
+ phase: limit.phase,
381
+ unit: limit.unit,
382
+ description: limit.description,
383
+ value: assertSafeIntegerField(limit.value, `runtime constructor service ${serviceId} resource limit value`, 0),
384
+ };
385
+ });
386
+ const ids = limits.map((limit) => limit.id);
387
+ for (let index = 1; index < ids.length; index += 1) {
388
+ if (ids[index - 1] >= ids[index]) {
389
+ throw new Error(`Merman WASM runtime constructor service ${serviceId} resource limits must be sorted and unique by ID.`);
390
+ }
391
+ }
392
+ return limits;
393
+ }
394
+ function validateRuntimeConstructorServiceProviders(contracts, capabilities) {
395
+ const runtimeProviderIds = new Set(capabilities.text_measurement?.provider_ids ?? []);
396
+ const contractById = new Map(contracts.map((contract) => [contract.id, contract]));
397
+ const providerOwnerById = new Map();
398
+ for (const contract of contracts) {
399
+ const serviceSpec = CONSTRUCTOR_SERVICE_SPEC_BY_ID.get(contract.id);
400
+ if (serviceSpec !== undefined) {
401
+ const actualKnownProviderIds = contract.provided_text_measurement_provider_ids.filter((id) => TEXT_MEASUREMENT_PROVIDER_SPEC_BY_ID.has(id));
402
+ if (!sameStringArrays(actualKnownProviderIds, [...serviceSpec.provided_text_measurement_provider_ids])) {
403
+ throw new Error(`Merman WASM runtime constructor service ${contract.id} does not match its known provider contract.`);
404
+ }
405
+ }
406
+ for (const providerId of contract.provided_text_measurement_provider_ids) {
407
+ if (!runtimeProviderIds.has(providerId)) {
408
+ throw new Error(`Merman WASM runtime constructor service ${contract.id} provides an unavailable text measurement provider.`);
409
+ }
410
+ const previousOwner = providerOwnerById.get(providerId);
411
+ if (previousOwner !== undefined) {
412
+ throw new Error(`Merman WASM runtime text measurement provider ${providerId} has multiple constructor service owners.`);
413
+ }
414
+ providerOwnerById.set(providerId, contract.id);
415
+ const providerSpec = TEXT_MEASUREMENT_PROVIDER_SPEC_BY_ID.get(providerId);
416
+ if (providerSpec !== undefined &&
417
+ (providerSpec.source !== "constructor-service" ||
418
+ providerSpec.constructor_service_id !== contract.id)) {
419
+ throw new Error(`Merman WASM runtime text measurement provider ${providerId} has the wrong constructor service owner.`);
420
+ }
421
+ }
422
+ }
423
+ for (const providerId of runtimeProviderIds) {
424
+ const providerSpec = TEXT_MEASUREMENT_PROVIDER_SPEC_BY_ID.get(providerId);
425
+ if (providerSpec?.source !== "constructor-service") {
426
+ continue;
427
+ }
428
+ const ownerId = providerSpec.constructor_service_id;
429
+ const ownerContract = ownerId === null ? undefined : contractById.get(ownerId);
430
+ if (ownerContract === undefined ||
431
+ !ownerContract.provided_text_measurement_provider_ids.includes(providerId)) {
432
+ throw new Error(`Merman WASM runtime text measurement provider ${providerId} is missing its constructor service owner.`);
433
+ }
434
+ }
435
+ }
436
+ function normalizePresentationCatalog(value) {
437
+ if (!isRecord(value) || value.schema_version !== 1) {
438
+ throw new Error("Merman WASM returned an unsupported presentation catalog schema.");
439
+ }
440
+ assertRequiredRecordKeys(value, ["profiles", "schema_version", "theme_presets"], "Merman WASM presentation catalog");
441
+ if (!Array.isArray(value.theme_presets) || !Array.isArray(value.profiles)) {
442
+ throw new Error("Merman WASM returned an invalid presentation catalog.");
443
+ }
444
+ return {
445
+ schema_version: 1,
446
+ theme_presets: normalizePresentationThemePresets(value.theme_presets),
447
+ profiles: normalizePresentationProfiles(value.profiles),
448
+ };
449
+ }
450
+ function normalizePresentationThemePresets(value) {
451
+ const seen = new Set();
452
+ return value.map((entry) => {
453
+ if (!isRecord(entry)) {
454
+ throw new Error("Merman WASM returned an invalid presentation theme preset.");
455
+ }
456
+ assertRequiredRecordKeys(entry, ["appearance", "fully_available", "id", "missing_capability_ids"], "Merman WASM presentation theme preset");
457
+ const id = assertUniquePresentationId(entry.id, seen, "theme preset");
458
+ return {
459
+ id,
460
+ appearance: assertRuntimeIdentifier(entry.appearance, `presentation theme preset ${id} appearance`),
461
+ fully_available: assertBooleanField(entry.fully_available, `presentation theme preset ${id} availability`),
462
+ missing_capability_ids: normalizeSortedIdentifierIds(entry.missing_capability_ids, `presentation theme preset ${id} missing capability IDs`),
463
+ };
464
+ });
465
+ }
466
+ function normalizePresentationProfiles(value) {
467
+ const seen = new Set();
468
+ return value.map((entry) => {
469
+ if (!isRecord(entry)) {
470
+ throw new Error("Merman WASM returned an invalid presentation profile.");
471
+ }
472
+ assertRequiredRecordKeys(entry, ["aspects", "fully_available", "id", "missing_capability_ids"], "Merman WASM presentation profile");
473
+ if (!Array.isArray(entry.aspects)) {
474
+ throw new Error("Merman WASM returned invalid presentation profile aspects.");
475
+ }
476
+ const id = assertUniquePresentationId(entry.id, seen, "profile");
477
+ return {
478
+ id,
479
+ fully_available: assertBooleanField(entry.fully_available, `presentation profile ${id} availability`),
480
+ missing_capability_ids: normalizeSortedIdentifierIds(entry.missing_capability_ids, `presentation profile ${id} missing capability IDs`),
481
+ aspects: normalizePresentationAspects(id, entry.aspects),
482
+ };
483
+ });
484
+ }
485
+ function normalizePresentationAspects(profileId, value) {
486
+ const seen = new Set();
487
+ return value.map((entry) => {
488
+ if (!isRecord(entry) || !isRecord(entry.applicability)) {
489
+ throw new Error("Merman WASM returned an invalid presentation profile aspect.");
490
+ }
491
+ assertRequiredRecordKeys(entry, [
492
+ "applicability",
493
+ "available",
494
+ "id",
495
+ "missing_capability_ids",
496
+ "required_capability_id",
497
+ ], "Merman WASM presentation profile aspect");
498
+ assertRequiredRecordKeys(entry.applicability, ["family_id", "kind"], "Merman WASM presentation aspect applicability");
499
+ const id = assertUniquePresentationId(entry.id, seen, `profile ${profileId} aspect`);
500
+ const familyId = entry.applicability.family_id;
501
+ return {
502
+ id,
503
+ applicability: {
504
+ kind: assertRuntimeIdentifier(entry.applicability.kind, `presentation aspect ${id} applicability kind`),
505
+ family_id: familyId === null
506
+ ? null
507
+ : assertRuntimeIdentifier(familyId, `presentation aspect ${id} family ID`),
508
+ },
509
+ required_capability_id: entry.required_capability_id === null
510
+ ? null
511
+ : assertRuntimeIdentifier(entry.required_capability_id, `presentation aspect ${id} required capability ID`),
512
+ available: assertBooleanField(entry.available, `presentation aspect ${id} availability`),
513
+ missing_capability_ids: normalizeSortedIdentifierIds(entry.missing_capability_ids, `presentation aspect ${id} missing capability IDs`),
514
+ };
515
+ });
516
+ }
517
+ function assertUniquePresentationId(value, seen, label) {
518
+ const id = assertRuntimeIdentifier(value, `presentation ${label} ID`);
519
+ if (seen.has(id)) {
520
+ throw new Error(`Merman WASM presentation ${label} IDs must be unique.`);
521
+ }
522
+ seen.add(id);
523
+ return id;
524
+ }
525
+ function normalizeRuntimePayloadSchemas(value) {
526
+ if (!Array.isArray(value)) {
527
+ throw new Error("Merman WASM returned invalid runtime payload schemas.");
528
+ }
529
+ const schemas = value.map((entry) => {
530
+ if (!isRecord(entry)) {
531
+ throw new Error("Merman WASM returned an invalid runtime payload schema.");
532
+ }
533
+ assertRequiredRecordKeys(entry, ["id", "version"], "Merman WASM runtime payload schema");
534
+ return {
535
+ ...structuredCloneValue(entry),
536
+ id: assertRuntimeIdentifier(entry.id, "runtime payload schema IDs"),
537
+ version: assertSafeIntegerField(entry.version, "runtime payload schema version", 1),
538
+ };
539
+ });
540
+ for (let index = 1; index < schemas.length; index += 1) {
541
+ if (schemas[index - 1].id >= schemas[index].id) {
542
+ throw new Error("Merman WASM runtime payload schema IDs must be sorted and unique.");
543
+ }
544
+ }
545
+ const knownSchemas = schemas.filter((schema) => BINDING_PAYLOAD_SCHEMAS.some((known) => known.id === schema.id));
546
+ if (knownSchemas.length !== WEB_PAYLOAD_SCHEMA_VERSION_BY_ID.size ||
547
+ knownSchemas.some((schema) => WEB_PAYLOAD_SCHEMA_VERSION_BY_ID.get(schema.id) !== schema.version)) {
548
+ throw new Error("Merman WASM runtime payload schemas do not match the Web transport contract.");
549
+ }
550
+ return schemas;
551
+ }
552
+ function normalizeRuntimeOutputContracts(value, outputIds) {
553
+ if (!Array.isArray(value)) {
554
+ throw new Error("Merman WASM returned invalid runtime output contracts.");
555
+ }
556
+ const contracts = value.map((entry) => {
557
+ if (!isRecord(entry)) {
558
+ throw new Error("Merman WASM returned an invalid runtime output contract.");
559
+ }
560
+ assertRequiredRecordKeys(entry, ["id", "media_type", "system_fonts", "embedded_images"], "Merman WASM runtime output contract");
561
+ const id = assertRuntimeIdentifier(entry.id, "runtime output contract IDs");
562
+ const mediaType = assertStringField(entry.media_type, "runtime output media type");
563
+ if (mediaType.length === 0) {
564
+ throw new Error("Merman WASM returned an empty runtime output media type.");
565
+ }
566
+ return {
567
+ ...structuredCloneValue(entry),
568
+ id,
569
+ media_type: mediaType,
570
+ system_fonts: normalizeRuntimeSystemFonts(entry.system_fonts),
571
+ embedded_images: normalizeRuntimeEmbeddedImages(entry.embedded_images),
572
+ };
573
+ });
574
+ const contractIds = contracts.map((contract) => contract.id);
575
+ for (let index = 1; index < contractIds.length; index += 1) {
576
+ if (contractIds[index - 1] >= contractIds[index]) {
577
+ throw new Error("Merman WASM runtime output contract IDs must be sorted and unique.");
578
+ }
579
+ }
580
+ if (contractIds.length !== outputIds.length ||
581
+ contractIds.some((id, index) => id !== outputIds[index])) {
582
+ throw new Error("Merman WASM runtime output contracts do not match runtime output IDs.");
583
+ }
584
+ return contracts;
585
+ }
586
+ function normalizeRuntimeSystemFonts(value) {
587
+ if (value === null) {
588
+ return null;
589
+ }
590
+ if (!isRecord(value)) {
591
+ throw new Error("Merman WASM returned an invalid runtime system-font contract.");
592
+ }
593
+ assertRequiredRecordKeys(value, [
594
+ "source_id",
595
+ "discovery",
596
+ "cache_scope",
597
+ "host_dependent",
598
+ "caller_configurable",
599
+ "resource_bounded",
600
+ ], "Merman WASM runtime system-font contract");
601
+ return {
602
+ ...structuredCloneValue(value),
603
+ source_id: assertRuntimeIdentifier(value.source_id, "runtime font source ID"),
604
+ discovery: assertRuntimeIdentifier(value.discovery, "runtime font discovery ID"),
605
+ cache_scope: assertRuntimeIdentifier(value.cache_scope, "runtime font cache-scope ID"),
606
+ host_dependent: assertBooleanField(value.host_dependent, "runtime font host dependence"),
607
+ caller_configurable: assertBooleanField(value.caller_configurable, "runtime font configurability"),
608
+ resource_bounded: assertBooleanField(value.resource_bounded, "runtime font resource bound"),
609
+ };
610
+ }
611
+ function normalizeRuntimeEmbeddedImages(value) {
612
+ if (value === null) {
613
+ return null;
614
+ }
615
+ if (!isRecord(value)) {
616
+ throw new Error("Merman WASM returned an invalid runtime embedded-image contract.");
617
+ }
618
+ assertRequiredRecordKeys(value, ["source_ids", "filesystem_access", "network_access", "caller_configurable", "limits"], "Merman WASM runtime embedded-image contract");
619
+ if (!isRecord(value.limits)) {
620
+ throw new Error("Merman WASM returned invalid runtime embedded-image limits.");
621
+ }
622
+ const limitValues = value.limits;
623
+ const limitNames = [
624
+ "max_bytes_per_image",
625
+ "max_total_bytes",
626
+ "max_pixels_per_image",
627
+ "max_total_pixels",
628
+ ];
629
+ assertRequiredRecordKeys(limitValues, [...limitNames], "Merman WASM runtime embedded-image limits");
630
+ const readLimit = (name) => {
631
+ const limit = limitValues[name];
632
+ return limit === null
633
+ ? null
634
+ : assertSafeIntegerField(limit, `runtime embedded-image ${name}`, 1);
635
+ };
636
+ const limits = {
637
+ max_bytes_per_image: readLimit("max_bytes_per_image"),
638
+ max_total_bytes: readLimit("max_total_bytes"),
639
+ max_pixels_per_image: readLimit("max_pixels_per_image"),
640
+ max_total_pixels: readLimit("max_total_pixels"),
641
+ };
642
+ return {
643
+ ...structuredCloneValue(value),
644
+ source_ids: normalizeSortedIdentifierIds(value.source_ids, "runtime embedded-image source IDs"),
645
+ filesystem_access: assertBooleanField(value.filesystem_access, "runtime embedded-image filesystem access"),
646
+ network_access: assertBooleanField(value.network_access, "runtime embedded-image network access"),
647
+ caller_configurable: assertBooleanField(value.caller_configurable, "runtime embedded-image configurability"),
648
+ limits: {
649
+ ...structuredCloneValue(limitValues),
650
+ ...limits,
651
+ },
652
+ };
653
+ }
654
+ function normalizeRuntimeCapabilities(value) {
655
+ if (!isRecord(value)) {
656
+ throw new Error("Merman WASM returned an invalid runtime capability report.");
657
+ }
658
+ assertRequiredRecordKeys(value, [
659
+ "capability_ids",
660
+ "output_ids",
661
+ "operation_ids",
662
+ "system_adapter_ids",
663
+ "text_measurement",
664
+ ], "Merman WASM runtime capability report");
665
+ const capabilityIds = normalizeSortedIdentifierIds(value.capability_ids, "runtime capability IDs");
666
+ const capabilitySet = new Set(capabilityIds);
667
+ const operationIds = normalizeSortedIdentifierIds(value.operation_ids, "runtime binding operation IDs");
668
+ const outputIds = normalizeSortedIdentifierIds(value.output_ids, "runtime output IDs");
669
+ const systemAdapterIds = normalizeSortedIdentifierIds(value.system_adapter_ids, "system adapter IDs");
670
+ for (const adapterId of systemAdapterIds) {
671
+ if (!capabilitySet.has(adapterId)) {
672
+ throw new Error(`Merman WASM system adapter ${adapterId} is absent from runtime capability IDs.`);
673
+ }
674
+ }
675
+ if (systemAdapterIds.length !== 0) {
676
+ throw new Error("Merman browser WASM must not expose native system adapters.");
677
+ }
678
+ validateCapabilityImplications(capabilityIds);
679
+ const textMeasurement = normalizeTextMeasurementCapabilities(value.text_measurement);
680
+ const svgAvailable = capabilitySet.has("svg");
681
+ if (svgAvailable && textMeasurement === null) {
682
+ throw new Error("Merman WASM text measurement must be present when SVG is available.");
683
+ }
684
+ validateKnownRuntimeOperationRelations(operationIds, capabilitySet, new Set(outputIds));
685
+ return {
686
+ ...structuredCloneValue(value),
687
+ capability_ids: capabilityIds,
688
+ output_ids: outputIds,
689
+ operation_ids: operationIds,
690
+ system_adapter_ids: systemAdapterIds,
691
+ text_measurement: textMeasurement,
692
+ };
693
+ }
694
+ function validateCapabilityImplications(capabilityIds) {
695
+ const capabilityIdSet = new Set(capabilityIds);
696
+ for (const capabilityId of capabilityIds) {
697
+ const spec = CAPABILITY_SPEC_BY_ID.get(capabilityId);
698
+ if (spec === undefined) {
699
+ continue;
700
+ }
701
+ for (const implicationId of spec.implication_ids) {
702
+ if (!capabilityIdSet.has(implicationId)) {
703
+ throw new Error(`Merman WASM runtime capability ${capabilityId} is missing implied capability ${implicationId}.`);
704
+ }
705
+ }
706
+ }
707
+ }
708
+ function validateKnownRuntimeOperationRelations(operationIds, capabilityIds, outputIds) {
709
+ for (const operationId of operationIds) {
710
+ const operation = WEB_BINDING_OPERATION_SPEC_BY_ID.get(operationId);
711
+ if (operation === undefined) {
712
+ continue;
713
+ }
714
+ if (operation.capability !== null && !capabilityIds.has(operation.capability)) {
715
+ throw new Error(`Merman WASM runtime operation ${operationId} is missing capability ${operation.capability}.`);
716
+ }
717
+ if (operation.output !== null && !outputIds.has(operation.output)) {
718
+ throw new Error(`Merman WASM runtime operation ${operationId} is missing output ${operation.output}.`);
719
+ }
720
+ }
721
+ }
722
+ function normalizeRuntimeResourceContract(value, operationIds) {
723
+ assertRequiredRecordKeys(value, [
724
+ "general_binding_default_profile",
725
+ "cli_default_profile",
726
+ "limits",
727
+ "profiles",
728
+ ], "Merman WASM runtime resource contract");
729
+ if (typeof value.general_binding_default_profile !== "string" ||
730
+ value.general_binding_default_profile.length === 0 ||
731
+ typeof value.cli_default_profile !== "string" ||
732
+ value.cli_default_profile.length === 0 ||
733
+ !Array.isArray(value.limits) ||
734
+ !Array.isArray(value.profiles)) {
735
+ throw new Error("Merman WASM returned an invalid runtime resource contract.");
736
+ }
737
+ const limits = value.limits.map((limit) => {
738
+ if (!isRecord(limit)) {
739
+ throw new Error("Merman WASM returned an invalid runtime resource limit.");
740
+ }
741
+ assertRequiredRecordKeys(limit, [
742
+ "id",
743
+ "phase",
744
+ "description",
745
+ "overridable",
746
+ "hard_cap",
747
+ "minimum_value",
748
+ "operation_ids",
749
+ ], "Merman WASM runtime resource limit");
750
+ if (typeof limit.id !== "string" ||
751
+ !RUNTIME_CATALOG_FIELD_IDENTIFIER.test(limit.id) ||
752
+ typeof limit.phase !== "string" ||
753
+ typeof limit.description !== "string" ||
754
+ typeof limit.overridable !== "boolean" ||
755
+ typeof limit.hard_cap !== "boolean" ||
756
+ typeof limit.minimum_value !== "number" ||
757
+ !isSafeCatalogInteger(limit.minimum_value) ||
758
+ limit.minimum_value < 0) {
759
+ throw new Error("Merman WASM returned an invalid runtime resource limit.");
760
+ }
761
+ if (limit.hard_cap && limit.overridable) {
762
+ throw new Error("Merman WASM runtime hard resource limits cannot be overridable.");
763
+ }
764
+ const limitOperationIds = normalizeSortedIdentifierIds(limit.operation_ids, `runtime resource limit ${limit.id} operation IDs`);
765
+ if (limitOperationIds.some((id) => !operationIds.has(id))) {
766
+ throw new Error("Merman WASM runtime resource limit names an unavailable operation.");
767
+ }
768
+ return {
769
+ ...structuredCloneValue(limit),
770
+ id: limit.id,
771
+ phase: limit.phase,
772
+ description: limit.description,
773
+ overridable: limit.overridable,
774
+ hard_cap: limit.hard_cap,
775
+ minimum_value: limit.minimum_value,
776
+ operation_ids: limitOperationIds,
777
+ };
778
+ });
779
+ const profiles = value.profiles.map((profile) => {
780
+ if (!isRecord(profile)) {
781
+ throw new Error("Merman WASM returned an invalid runtime resource profile.");
782
+ }
783
+ assertRequiredRecordKeys(profile, ["id", "purpose", "trust_assumption", "recommended_binding_default", "limits"], "Merman WASM runtime resource profile");
784
+ if (typeof profile.id !== "string" ||
785
+ !RUNTIME_CATALOG_IDENTIFIER.test(profile.id) ||
786
+ typeof profile.purpose !== "string" ||
787
+ typeof profile.trust_assumption !== "string" ||
788
+ typeof profile.recommended_binding_default !== "boolean" ||
789
+ !(isRecord(profile.limits) || profile.limits instanceof Map)) {
790
+ throw new Error("Merman WASM returned an invalid runtime resource profile.");
791
+ }
792
+ const rawProfileLimits = new Map(normalizeStringMapEntries(profile.limits, `resource profile ${profile.id} limits`));
793
+ if (rawProfileLimits.size !== limits.length ||
794
+ limits.some((limit) => !rawProfileLimits.has(limit.id))) {
795
+ throw new Error("Merman WASM runtime resource profile does not cover the declared limits.");
796
+ }
797
+ const profileLimits = {};
798
+ for (const descriptor of limits) {
799
+ const limit = rawProfileLimits.get(descriptor.id);
800
+ profileLimits[descriptor.id] =
801
+ limit === null || limit === undefined
802
+ ? null
803
+ : assertSafeIntegerField(limit, `resource profile limit ${descriptor.id}`, descriptor.minimum_value);
804
+ if (descriptor.hard_cap && profileLimits[descriptor.id] === null) {
805
+ throw new Error("Merman WASM runtime resource profile removed a finite hard cap.");
806
+ }
807
+ }
808
+ return {
809
+ ...structuredCloneValue(profile),
810
+ id: profile.id,
811
+ purpose: profile.purpose,
812
+ trust_assumption: profile.trust_assumption,
813
+ recommended_binding_default: profile.recommended_binding_default,
814
+ limits: profileLimits,
815
+ };
816
+ });
817
+ const profileById = new Map();
818
+ for (const profile of profiles) {
819
+ if (profileById.has(profile.id)) {
820
+ throw new Error("Merman WASM runtime resource profile IDs must be unique.");
821
+ }
822
+ profileById.set(profile.id, profile);
823
+ }
824
+ const generalDefault = profileById.get(value.general_binding_default_profile);
825
+ if (!generalDefault || !profileById.has(value.cli_default_profile)) {
826
+ throw new Error("Merman WASM runtime resource defaults name unknown profiles.");
827
+ }
828
+ const recommendedProfiles = profiles.filter((profile) => profile.recommended_binding_default);
829
+ if (recommendedProfiles.length !== 1 ||
830
+ recommendedProfiles[0]?.id !== generalDefault.id) {
831
+ throw new Error("Merman WASM runtime resources must recommend exactly the binding default profile.");
832
+ }
833
+ return {
834
+ ...structuredCloneValue(value),
835
+ general_binding_default_profile: value.general_binding_default_profile,
836
+ cli_default_profile: value.cli_default_profile,
837
+ limits,
838
+ profiles,
839
+ };
840
+ }
841
+ function structuredCloneValue(value) {
842
+ return JSON.parse(JSON.stringify(value));
843
+ }
844
+ function normalizeTextMeasurementCapabilities(value) {
845
+ if (value === null || value === undefined) {
846
+ return null;
847
+ }
848
+ if (!isRecord(value)) {
849
+ throw new Error("Merman WASM returned invalid text measurement capabilities.");
850
+ }
851
+ assertRequiredRecordKeys(value, ["protocol_version", "provider_ids"], "Merman WASM text measurement capabilities");
852
+ if (typeof value.protocol_version !== "number" ||
853
+ !isSafeCatalogInteger(value.protocol_version) ||
854
+ value.protocol_version !== MERMAN_TEXT_MEASUREMENT_PROTOCOL_VERSION) {
855
+ throw new Error("Merman WASM returned an unsupported text measurement protocol.");
856
+ }
857
+ const providerIds = normalizeSortedIdentifierIds(value.provider_ids, "text measurement provider IDs");
858
+ if (!providerIds.includes("vendored")) {
859
+ throw new Error("Merman WASM text measurement must include vendored support.");
860
+ }
861
+ return {
862
+ ...structuredCloneValue(value),
863
+ protocol_version: value.protocol_version,
864
+ provider_ids: providerIds,
865
+ };
866
+ }
867
+ function normalizeStringMapEntries(value, label) {
868
+ const entries = value instanceof Map
869
+ ? [...value.entries()]
870
+ : isRecord(value)
871
+ ? Object.entries(value)
872
+ : null;
873
+ if (entries === null || entries.some(([key]) => typeof key !== "string" || key.length === 0)) {
874
+ throw new Error(`Merman WASM returned invalid ${label}.`);
875
+ }
876
+ return entries;
877
+ }
878
+ function normalizeSortedIdentifierIds(value, label) {
879
+ if (!Array.isArray(value)) {
880
+ throw new Error(`Merman WASM returned invalid ${label}.`);
881
+ }
882
+ const identifiers = value.map((entry) => assertRuntimeIdentifier(entry, label));
883
+ for (let index = 1; index < identifiers.length; index += 1) {
884
+ if (identifiers[index - 1] >= identifiers[index]) {
885
+ throw new Error(`Merman WASM ${label} must be sorted and unique.`);
886
+ }
887
+ }
888
+ return identifiers;
889
+ }
890
+ function normalizeSortedOptionGroupIds(value) {
891
+ if (!Array.isArray(value)) {
892
+ throw new Error("Merman WASM returned invalid runtime option group IDs.");
893
+ }
894
+ const identifiers = value.map((entry) => assertRuntimeFieldIdentifier(entry, "runtime option group ID"));
895
+ for (let index = 1; index < identifiers.length; index += 1) {
896
+ if (identifiers[index - 1] >= identifiers[index]) {
897
+ throw new Error("Merman WASM runtime option group IDs must be sorted and unique.");
898
+ }
899
+ }
900
+ return identifiers;
901
+ }
902
+ function normalizeSortedPositiveIntegers(value, label) {
903
+ if (!Array.isArray(value)) {
904
+ throw new Error(`Merman WASM returned invalid ${label}.`);
905
+ }
906
+ const integers = value.map((entry) => assertSafeIntegerField(entry, label, 1));
907
+ for (let index = 1; index < integers.length; index += 1) {
908
+ if (integers[index - 1] >= integers[index]) {
909
+ throw new Error(`Merman WASM ${label} must be sorted and unique.`);
910
+ }
911
+ }
912
+ return integers;
913
+ }
914
+ function assertRuntimeIdentifier(value, label) {
915
+ if (typeof value === "string" &&
916
+ RUNTIME_CATALOG_IDENTIFIER.test(value)) {
917
+ return value;
918
+ }
919
+ throw new Error(`Merman WASM returned an invalid ${label}.`);
920
+ }
921
+ function assertRuntimeFieldIdentifier(value, label) {
922
+ if (typeof value === "string" &&
923
+ RUNTIME_CATALOG_FIELD_IDENTIFIER.test(value)) {
924
+ return value;
925
+ }
926
+ throw new Error(`Merman WASM returned an invalid ${label}.`);
927
+ }
928
+ function sameStringArrays(left, right) {
929
+ return left.length === right.length && left.every((value, index) => value === right[index]);
930
+ }
931
+ function assertSafeIntegerField(value, label, minimum) {
932
+ if (typeof value === "number" && isSafeCatalogInteger(value) && value >= minimum) {
933
+ return value;
934
+ }
935
+ throw new Error(`Merman WASM returned an invalid ${label}.`);
936
+ }
937
+ function isSafeCatalogInteger(value) {
938
+ return Number.isInteger(value) && Math.abs(value) <= RUNTIME_CATALOG_MAX_SAFE_INTEGER;
939
+ }
940
+ function assertRequiredRecordKeys(value, required, label) {
941
+ const missing = required.filter((key) => !Object.prototype.hasOwnProperty.call(value, key));
942
+ if (missing.length !== 0) {
943
+ throw new Error(`${label} is missing required fields: ${missing.join(", ")}.`);
944
+ }
945
+ }
946
+ //# sourceMappingURL=runtime-core.js.map