@mermanjs/node 0.8.0-alpha.5 → 0.8.0-alpha.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/CHANGELOG.md +12 -1
- package/README.md +86 -8
- package/THIRD_PARTY_LICENSES/non-layered-tidy-tree-layout/LICENSE +21 -0
- package/THIRD_PARTY_LICENSES/rust-cargo-dependencies.json +90 -71
- package/THIRD_PARTY_LICENSES/tree-sitter/LICENSE +21 -0
- package/THIRD_PARTY_LICENSES/tree-sitter-mermaid-monaqa/LICENSE +21 -0
- package/THIRD_PARTY_LICENSES/tree-sitter-mermaid-pappasam/LICENSE +21 -0
- package/THIRD_PARTY_LICENSES/tree-sitter-mermaid-singularity/LICENSE +22 -0
- package/THIRD_PARTY_NOTICES.md +160 -36
- package/dist/bounded-executor.mjs +3 -0
- package/dist/candidates/native.mjs +1 -0
- package/dist/candidates/wrap-engine.mjs +11 -3
- package/dist/engine.mjs +114 -29
- package/dist/errors.mjs +125 -6
- package/dist/generated/binding-contract.mjs +154 -8
- package/dist/generated/capability-surface.mjs +1 -1
- package/dist/generated/node-wire-contract.json +1 -1
- package/dist/index.d.ts +48 -4
- package/dist/index.mjs +1 -0
- package/dist/native-loader.mjs +13 -0
- package/package.json +6 -6
package/dist/engine.mjs
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
NODE_TRANSPORT_FIELD_LIMITS,
|
|
5
5
|
NODE_TRANSPORT_LIMITS,
|
|
6
6
|
NODE_WIRE_CONTRACT,
|
|
7
|
+
abortError,
|
|
7
8
|
assertUtf8Field,
|
|
8
9
|
decodeWireInvocationError,
|
|
9
10
|
decodeWireResponse,
|
|
@@ -28,7 +29,7 @@ import {
|
|
|
28
29
|
TEXT_MEASUREMENT_PROTOCOL_VERSION,
|
|
29
30
|
TEXT_MEASUREMENT_PROVIDER_IDS,
|
|
30
31
|
TEXT_MEASUREMENT_PROVIDER_SPECS,
|
|
31
|
-
|
|
32
|
+
DETERMINISTIC_TEXT_MEASUREMENT_PROVIDER_ID,
|
|
32
33
|
} from "./generated/binding-contract.mjs";
|
|
33
34
|
|
|
34
35
|
const RESOURCE_PROFILES = new Set([
|
|
@@ -86,6 +87,7 @@ const COMPILED_PREREQUISITES_BY_OPERATION_ID = new Map(
|
|
|
86
87
|
const KNOWN_OUTPUT_CONTRACT_BY_ID = new Map(
|
|
87
88
|
NODE_WIRE_CONTRACT.artifact.output_contracts.map((contract) => [contract.id, contract]),
|
|
88
89
|
);
|
|
90
|
+
const MAX_OPERATION_TIMEOUT_MS = 0xffff_ffff;
|
|
89
91
|
const MERMAN_ENGINE_CONSTRUCTION_TOKEN = Symbol("MermanEngine construction");
|
|
90
92
|
|
|
91
93
|
export async function createNodeEngine(
|
|
@@ -144,6 +146,19 @@ export function normalizeBindingOptions(value = {}) {
|
|
|
144
146
|
if (!RESOURCE_PROFILES.has(normalized.resources.profile)) {
|
|
145
147
|
throw new RangeError(`Unknown resource profile \`${normalized.resources.profile}\`.`);
|
|
146
148
|
}
|
|
149
|
+
if (normalized.environment !== undefined) {
|
|
150
|
+
if (!isPlainObject(normalized.environment)) {
|
|
151
|
+
throw new TypeError("bindingOptions.environment must be a plain object.");
|
|
152
|
+
}
|
|
153
|
+
if (
|
|
154
|
+
normalized.environment.text_measurement !== undefined &&
|
|
155
|
+
normalized.environment.text_measurement !== "deterministic"
|
|
156
|
+
) {
|
|
157
|
+
throw new RangeError(
|
|
158
|
+
"bindingOptions.environment.text_measurement must be `deterministic`.",
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
147
162
|
return normalized;
|
|
148
163
|
}
|
|
149
164
|
|
|
@@ -181,31 +196,37 @@ export class MermanEngine {
|
|
|
181
196
|
renderSvg(source, options = {}) {
|
|
182
197
|
return this.executeOperation(
|
|
183
198
|
{ operationId: "svg", source, optionsJson: options.optionsJson },
|
|
184
|
-
{ signal: options.signal },
|
|
199
|
+
{ signal: options.signal, timeoutMs: options.timeoutMs },
|
|
185
200
|
).then((result) => result.data);
|
|
186
201
|
}
|
|
187
202
|
|
|
188
203
|
renderSvgSync(source, options = {}) {
|
|
189
|
-
return this.executeOperationSync(
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
204
|
+
return this.executeOperationSync(
|
|
205
|
+
{
|
|
206
|
+
operationId: "svg",
|
|
207
|
+
source,
|
|
208
|
+
optionsJson: options.optionsJson,
|
|
209
|
+
},
|
|
210
|
+
{ timeoutMs: options.timeoutMs },
|
|
211
|
+
).data;
|
|
194
212
|
}
|
|
195
213
|
|
|
196
214
|
svgPlanJson(source, options = {}) {
|
|
197
215
|
return this.executeOperation(
|
|
198
216
|
{ operationId: "svg-plan-json", source, optionsJson: options.optionsJson },
|
|
199
|
-
{ signal: options.signal },
|
|
217
|
+
{ signal: options.signal, timeoutMs: options.timeoutMs },
|
|
200
218
|
).then((result) => result.data);
|
|
201
219
|
}
|
|
202
220
|
|
|
203
221
|
svgPlanJsonSync(source, options = {}) {
|
|
204
|
-
return this.executeOperationSync(
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
222
|
+
return this.executeOperationSync(
|
|
223
|
+
{
|
|
224
|
+
operationId: "svg-plan-json",
|
|
225
|
+
source,
|
|
226
|
+
optionsJson: options.optionsJson,
|
|
227
|
+
},
|
|
228
|
+
{ timeoutMs: options.timeoutMs },
|
|
229
|
+
).data;
|
|
209
230
|
}
|
|
210
231
|
|
|
211
232
|
metadataJson(id) {
|
|
@@ -232,17 +253,33 @@ export class MermanEngine {
|
|
|
232
253
|
return value;
|
|
233
254
|
}
|
|
234
255
|
|
|
235
|
-
executeOperation(request, { signal } = {}) {
|
|
236
|
-
|
|
256
|
+
executeOperation(request, { signal, timeoutMs } = {}) {
|
|
257
|
+
try {
|
|
258
|
+
this.#executor.assertOpen();
|
|
259
|
+
} catch (error) {
|
|
260
|
+
return Promise.reject(error);
|
|
261
|
+
}
|
|
262
|
+
const prepared = prepareOperationRequest(request, { timeoutMs });
|
|
263
|
+
if (signal?.aborted) return Promise.reject(abortError());
|
|
264
|
+
const encoded = operationRequestJson(prepared);
|
|
237
265
|
return this.#executor.submit(
|
|
238
266
|
async () => {
|
|
267
|
+
// A signal can flip after queue admission but before this start microtask runs. That work
|
|
268
|
+
// has not crossed the transport boundary, so preserve the established AbortError result.
|
|
269
|
+
if (signal?.aborted) throw abortError();
|
|
239
270
|
let responseJson;
|
|
240
271
|
try {
|
|
241
|
-
responseJson = await
|
|
272
|
+
responseJson = await executeTransportOperation(
|
|
273
|
+
this.#transport,
|
|
274
|
+
encoded.json,
|
|
275
|
+
signal,
|
|
276
|
+
timeoutMs,
|
|
277
|
+
);
|
|
242
278
|
} catch (cause) {
|
|
243
279
|
throw decodeWireInvocationError(cause, "Merman operation");
|
|
244
280
|
}
|
|
245
281
|
return decodeWireResponse(responseJson, encoded.expectation, {
|
|
282
|
+
allowedCancellationReasons: invocationCancellationReasons(signal, timeoutMs),
|
|
246
283
|
requireUnavailable: !this.#operationIds.has(encoded.expectation.operation_id),
|
|
247
284
|
});
|
|
248
285
|
},
|
|
@@ -250,16 +287,17 @@ export class MermanEngine {
|
|
|
250
287
|
);
|
|
251
288
|
}
|
|
252
289
|
|
|
253
|
-
executeOperationSync(request) {
|
|
290
|
+
executeOperationSync(request, { timeoutMs } = {}) {
|
|
254
291
|
this.#executor.assertSyncAvailable();
|
|
255
|
-
const encoded = operationRequestJson(request);
|
|
292
|
+
const encoded = operationRequestJson(prepareOperationRequest(request, { timeoutMs }));
|
|
256
293
|
let responseJson;
|
|
257
294
|
try {
|
|
258
|
-
responseJson = this.#transport.executeSync(encoded.json);
|
|
295
|
+
responseJson = this.#transport.executeSync(encoded.json, timeoutMs);
|
|
259
296
|
} catch (cause) {
|
|
260
297
|
throw decodeWireInvocationError(cause, "Merman operation");
|
|
261
298
|
}
|
|
262
299
|
return decodeWireResponse(responseJson, encoded.expectation, {
|
|
300
|
+
allowedCancellationReasons: invocationCancellationReasons(undefined, timeoutMs),
|
|
263
301
|
requireUnavailable: !this.#operationIds.has(encoded.expectation.operation_id),
|
|
264
302
|
});
|
|
265
303
|
}
|
|
@@ -273,7 +311,14 @@ export class MermanEngine {
|
|
|
273
311
|
}
|
|
274
312
|
}
|
|
275
313
|
|
|
276
|
-
function
|
|
314
|
+
function invocationCancellationReasons(signal, timeoutMs) {
|
|
315
|
+
const reasons = [];
|
|
316
|
+
if (signal?.aborted) reasons.push("requested");
|
|
317
|
+
if (timeoutMs !== undefined) reasons.push("deadline_exceeded");
|
|
318
|
+
return reasons;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function prepareOperationRequest(value, { timeoutMs } = {}) {
|
|
277
322
|
if (!isPlainObject(value)) throw new TypeError("operation request must be a plain object.");
|
|
278
323
|
const knownFields = new Set(["operationId", "source", "uri", "optionsJson"]);
|
|
279
324
|
for (const field of Object.keys(value)) {
|
|
@@ -281,12 +326,9 @@ function operationRequestJson(value) {
|
|
|
281
326
|
throw new TypeError(`unknown operation request field \`${field}\`.`);
|
|
282
327
|
}
|
|
283
328
|
}
|
|
284
|
-
const
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
uri = null,
|
|
288
|
-
optionsJson = undefined,
|
|
289
|
-
} = value;
|
|
329
|
+
const operationId = value.operationId;
|
|
330
|
+
const source = value.source;
|
|
331
|
+
const uri = value.uri === undefined ? null : value.uri;
|
|
290
332
|
if (typeof operationId !== "string" || operationId.length === 0) {
|
|
291
333
|
throw new TypeError("operationId must be a non-empty string.");
|
|
292
334
|
}
|
|
@@ -327,6 +369,23 @@ function operationRequestJson(value) {
|
|
|
327
369
|
source,
|
|
328
370
|
uri: normalizedUri,
|
|
329
371
|
};
|
|
372
|
+
if (timeoutMs !== undefined) {
|
|
373
|
+
if (
|
|
374
|
+
!Number.isSafeInteger(timeoutMs) ||
|
|
375
|
+
timeoutMs < 0 ||
|
|
376
|
+
timeoutMs > MAX_OPERATION_TIMEOUT_MS
|
|
377
|
+
) {
|
|
378
|
+
throw new RangeError(
|
|
379
|
+
`operation timeoutMs must be an integer from 0 through ${MAX_OPERATION_TIMEOUT_MS}.`,
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
request.operation_control = { timeout_ms: timeoutMs };
|
|
383
|
+
}
|
|
384
|
+
return { expectation, request, value };
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function operationRequestJson({ expectation, request, value }) {
|
|
388
|
+
const optionsJson = value.optionsJson;
|
|
330
389
|
if (optionsJson !== undefined) {
|
|
331
390
|
if (typeof optionsJson !== "string") {
|
|
332
391
|
throw new TypeError("optionsJson must be a JSON string when provided.");
|
|
@@ -349,6 +408,32 @@ function operationRequestJson(value) {
|
|
|
349
408
|
};
|
|
350
409
|
}
|
|
351
410
|
|
|
411
|
+
async function executeTransportOperation(transport, requestJson, signal, timeoutMs) {
|
|
412
|
+
if (!signal) return transport.execute(requestJson, undefined, timeoutMs);
|
|
413
|
+
|
|
414
|
+
// Forward into a private signal so the N-API bridge cannot replace a caller-owned `onabort`
|
|
415
|
+
// handler while it installs its cooperative cancellation callback.
|
|
416
|
+
const transportAbort = new AbortController();
|
|
417
|
+
let transportReady = false;
|
|
418
|
+
let pendingAbort = false;
|
|
419
|
+
const forwardAbort = () => {
|
|
420
|
+
if (transportReady) {
|
|
421
|
+
transportAbort.abort(signal.reason);
|
|
422
|
+
} else {
|
|
423
|
+
pendingAbort = true;
|
|
424
|
+
}
|
|
425
|
+
};
|
|
426
|
+
signal.addEventListener("abort", forwardAbort, { once: true });
|
|
427
|
+
try {
|
|
428
|
+
const operation = transport.execute(requestJson, transportAbort.signal, timeoutMs);
|
|
429
|
+
transportReady = true;
|
|
430
|
+
if (pendingAbort || signal.aborted) transportAbort.abort(signal.reason);
|
|
431
|
+
return await operation;
|
|
432
|
+
} finally {
|
|
433
|
+
signal.removeEventListener("abort", forwardAbort);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
352
437
|
function assertTransport(transport) {
|
|
353
438
|
if (
|
|
354
439
|
!transport ||
|
|
@@ -893,7 +978,7 @@ function validateTextMeasurement(value, { requiresSvgPipeline }) {
|
|
|
893
978
|
providerIds.some(
|
|
894
979
|
(id) =>
|
|
895
980
|
KNOWN_TEXT_MEASUREMENT_PROVIDER_IDS.has(id) &&
|
|
896
|
-
id !==
|
|
981
|
+
id !== DETERMINISTIC_TEXT_MEASUREMENT_PROVIDER_ID,
|
|
897
982
|
)
|
|
898
983
|
) {
|
|
899
984
|
throw new MermanInvalidTransportError(
|
|
@@ -902,10 +987,10 @@ function validateTextMeasurement(value, { requiresSvgPipeline }) {
|
|
|
902
987
|
}
|
|
903
988
|
if (
|
|
904
989
|
requiresSvgPipeline &&
|
|
905
|
-
!providerIds.includes(
|
|
990
|
+
!providerIds.includes(DETERMINISTIC_TEXT_MEASUREMENT_PROVIDER_ID)
|
|
906
991
|
) {
|
|
907
992
|
throw new MermanInvalidTransportError(
|
|
908
|
-
"Merman runtime catalog must expose the callable
|
|
993
|
+
"Merman runtime catalog must expose the callable deterministic text measurement provider for the SVG pipeline.",
|
|
909
994
|
);
|
|
910
995
|
}
|
|
911
996
|
return providerIds;
|
package/dist/errors.mjs
CHANGED
|
@@ -29,7 +29,10 @@ const BINDING_STATUS_NAME_BY_CODE = new Map([
|
|
|
29
29
|
[9, "MERMAN_INTERNAL_ERROR"],
|
|
30
30
|
[10, "MERMAN_RESOURCE_LIMIT_EXCEEDED"],
|
|
31
31
|
[11, "MERMAN_BUSY"],
|
|
32
|
+
[12, "MERMAN_CANCELLED"],
|
|
32
33
|
]);
|
|
34
|
+
const CANCELLATION_REASONS = new Set(["requested", "deadline_exceeded"]);
|
|
35
|
+
const OPERATION_PHASE_IDENTIFIER = /^[a-z][a-z0-9_-]{0,63}$/;
|
|
33
36
|
const JSON_NUMBER_TOKEN = /-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?/y;
|
|
34
37
|
const JSON_NUMBER_PARTS = /^-?(0|[1-9]\d*)(?:\.(\d+))?(?:[eE]([+-]?)(\d+))?$/;
|
|
35
38
|
const MAX_SAFE_INTEGER_DECIMAL = String(RUNTIME_CATALOG_MAX_SAFE_INTEGER);
|
|
@@ -67,6 +70,10 @@ const RESPONSE_EXACT_SAFE_INTEGER_PATHS = Object.freeze([
|
|
|
67
70
|
["error", "code"],
|
|
68
71
|
["error", "details", "resource", "actual"],
|
|
69
72
|
["error", "details", "resource", "max"],
|
|
73
|
+
["error", "details", "diagnostic", "span", "start"],
|
|
74
|
+
["error", "details", "diagnostic", "span", "end"],
|
|
75
|
+
["error", "details", "diagnostic", "requested_max_width"],
|
|
76
|
+
["error", "details", "diagnostic", "actual_width"],
|
|
70
77
|
["error", "details", "icon_registry", "pack_index"],
|
|
71
78
|
]);
|
|
72
79
|
const OPERATION_METADATA_EXACT_SAFE_INTEGER_PATHS = Object.freeze([
|
|
@@ -96,6 +103,8 @@ export class MermanOperationError extends MermanError {
|
|
|
96
103
|
this.kind = payload.kind ?? "generic";
|
|
97
104
|
this.capabilityId = payload.capability_id ?? null;
|
|
98
105
|
this.resourceDetails = payload.details?.resource ?? null;
|
|
106
|
+
this.diagnosticDetails = payload.details?.diagnostic ?? null;
|
|
107
|
+
this.cancellationDetails = payload.details?.cancellation ?? null;
|
|
99
108
|
}
|
|
100
109
|
}
|
|
101
110
|
|
|
@@ -143,6 +152,17 @@ export class MermanMissingPlatformPackageError extends MermanError {
|
|
|
143
152
|
}
|
|
144
153
|
}
|
|
145
154
|
|
|
155
|
+
export class MermanNativeLoadError extends MermanError {
|
|
156
|
+
constructor({ packageName, target, cause }) {
|
|
157
|
+
super(
|
|
158
|
+
`The installed Merman platform package ${packageName} could not load its native addon for ${target}. This is a native ABI or shared-library loader failure, not a missing npm package. Use a compatible glibc baseline or opt into @mermanjs/node-wasm.`,
|
|
159
|
+
{ code: "MERMAN_NATIVE_LOAD_ERROR", cause },
|
|
160
|
+
);
|
|
161
|
+
this.packageName = packageName;
|
|
162
|
+
this.target = target;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
146
166
|
export class MermanInvalidTransportError extends MermanError {
|
|
147
167
|
constructor(message, cause) {
|
|
148
168
|
super(message, { code: "MERMAN_INVALID_TRANSPORT", cause });
|
|
@@ -843,8 +863,9 @@ export function abortError() {
|
|
|
843
863
|
export function decodeWireResponse(
|
|
844
864
|
value,
|
|
845
865
|
expectation,
|
|
846
|
-
{ requireUnavailable = false } = {},
|
|
866
|
+
{ allowedCancellationReasons = [], requireUnavailable = false } = {},
|
|
847
867
|
) {
|
|
868
|
+
const cancellationReasons = validateAllowedCancellationReasons(allowedCancellationReasons);
|
|
848
869
|
const envelope = parseTransportJsonText(
|
|
849
870
|
value,
|
|
850
871
|
"response",
|
|
@@ -863,7 +884,12 @@ export function decodeWireResponse(
|
|
|
863
884
|
"Merman transport error envelopes must contain error only.",
|
|
864
885
|
);
|
|
865
886
|
}
|
|
866
|
-
const error = validateErrorPayload(
|
|
887
|
+
const error = validateErrorPayload(
|
|
888
|
+
envelope.error,
|
|
889
|
+
expectation,
|
|
890
|
+
requireUnavailable,
|
|
891
|
+
cancellationReasons,
|
|
892
|
+
);
|
|
867
893
|
throw new MermanOperationError(error);
|
|
868
894
|
}
|
|
869
895
|
if (envelope.ok !== true || Object.hasOwn(envelope, "error")) {
|
|
@@ -879,6 +905,16 @@ export function decodeWireResponse(
|
|
|
879
905
|
return validateSuccessResult(envelope.result, expectation);
|
|
880
906
|
}
|
|
881
907
|
|
|
908
|
+
function validateAllowedCancellationReasons(reasons) {
|
|
909
|
+
if (
|
|
910
|
+
!Array.isArray(reasons) ||
|
|
911
|
+
reasons.some((reason) => !CANCELLATION_REASONS.has(reason))
|
|
912
|
+
) {
|
|
913
|
+
throw new TypeError("allowedCancellationReasons must contain known cancellation reasons.");
|
|
914
|
+
}
|
|
915
|
+
return new Set(reasons);
|
|
916
|
+
}
|
|
917
|
+
|
|
882
918
|
export function decodeWireCreationError(cause, label) {
|
|
883
919
|
return decodeWireFailure(cause, `${label} failed to initialize.`);
|
|
884
920
|
}
|
|
@@ -990,7 +1026,12 @@ function validateSuccessResult(result, expectation) {
|
|
|
990
1026
|
return result;
|
|
991
1027
|
}
|
|
992
1028
|
|
|
993
|
-
function validateErrorPayload(
|
|
1029
|
+
function validateErrorPayload(
|
|
1030
|
+
error,
|
|
1031
|
+
expectation = null,
|
|
1032
|
+
requireUnavailable = false,
|
|
1033
|
+
allowedCancellationReasons = new Set(),
|
|
1034
|
+
) {
|
|
994
1035
|
if (
|
|
995
1036
|
!isPlainJsonObject(error) ||
|
|
996
1037
|
!Number.isSafeInteger(error.code) ||
|
|
@@ -1029,9 +1070,18 @@ function validateErrorPayload(error, expectation = null, requireUnavailable = fa
|
|
|
1029
1070
|
);
|
|
1030
1071
|
}
|
|
1031
1072
|
validateKnownErrorRelations(error);
|
|
1032
|
-
|
|
1073
|
+
validateErrorDetails(error.details);
|
|
1033
1074
|
|
|
1034
|
-
if (
|
|
1075
|
+
if (
|
|
1076
|
+
error.code === 12 &&
|
|
1077
|
+
!allowedCancellationReasons.has(error.details.cancellation.reason)
|
|
1078
|
+
) {
|
|
1079
|
+
throw new MermanInvalidTransportError(
|
|
1080
|
+
"Merman transport returned cancellation without a matching invocation control.",
|
|
1081
|
+
);
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
if (requireUnavailable && expectation?.unavailable && error.code !== 12) {
|
|
1035
1085
|
const unavailable = expectation.unavailable;
|
|
1036
1086
|
if (
|
|
1037
1087
|
error.code !== unavailable.status_code ||
|
|
@@ -1105,9 +1155,29 @@ function validateKnownErrorRelations(error) {
|
|
|
1105
1155
|
} else {
|
|
1106
1156
|
throw new MermanInvalidTransportError("Merman transport returned an unknown error kind.");
|
|
1107
1157
|
}
|
|
1158
|
+
const cancellation = error.details?.cancellation;
|
|
1159
|
+
if (error.code === 12) {
|
|
1160
|
+
if (
|
|
1161
|
+
error.kind !== "generic" ||
|
|
1162
|
+
error.capability_id !== null ||
|
|
1163
|
+
!isPlainJsonObject(error.details) ||
|
|
1164
|
+
cancellation === undefined ||
|
|
1165
|
+
error.details.resource !== undefined ||
|
|
1166
|
+
error.details.diagnostic !== undefined ||
|
|
1167
|
+
error.details.icon_registry !== undefined
|
|
1168
|
+
) {
|
|
1169
|
+
throw new MermanInvalidTransportError(
|
|
1170
|
+
"Merman transport returned an inconsistent cancellation error.",
|
|
1171
|
+
);
|
|
1172
|
+
}
|
|
1173
|
+
} else if (cancellation !== undefined) {
|
|
1174
|
+
throw new MermanInvalidTransportError(
|
|
1175
|
+
"Merman transport attached cancellation details to a non-cancellation error.",
|
|
1176
|
+
);
|
|
1177
|
+
}
|
|
1108
1178
|
}
|
|
1109
1179
|
|
|
1110
|
-
function
|
|
1180
|
+
function validateErrorDetails(details) {
|
|
1111
1181
|
if (details === undefined) return;
|
|
1112
1182
|
if (!isPlainJsonObject(details)) {
|
|
1113
1183
|
throw new MermanInvalidTransportError("Merman transport error details must be an object.");
|
|
@@ -1132,6 +1202,49 @@ function validateResourceDetails(details) {
|
|
|
1132
1202
|
);
|
|
1133
1203
|
}
|
|
1134
1204
|
}
|
|
1205
|
+
if (details.diagnostic !== undefined) {
|
|
1206
|
+
const diagnostic = details.diagnostic;
|
|
1207
|
+
const span = diagnostic?.span;
|
|
1208
|
+
if (
|
|
1209
|
+
!isPlainJsonObject(diagnostic) ||
|
|
1210
|
+
typeof diagnostic.code !== "string" ||
|
|
1211
|
+
diagnostic.code.length === 0 ||
|
|
1212
|
+
(diagnostic.field !== null && typeof diagnostic.field !== "string") ||
|
|
1213
|
+
(diagnostic.diagram_type !== null && typeof diagnostic.diagram_type !== "string") ||
|
|
1214
|
+
!isOptionalDiagnosticUnsignedInteger(diagnostic.requested_max_width) ||
|
|
1215
|
+
!isOptionalDiagnosticUnsignedInteger(diagnostic.actual_width) ||
|
|
1216
|
+
(diagnostic.width_profile !== undefined &&
|
|
1217
|
+
diagnostic.width_profile !== null &&
|
|
1218
|
+
typeof diagnostic.width_profile !== "string") ||
|
|
1219
|
+
(diagnostic.fallback_reason !== undefined &&
|
|
1220
|
+
diagnostic.fallback_reason !== null &&
|
|
1221
|
+
typeof diagnostic.fallback_reason !== "string") ||
|
|
1222
|
+
(span !== null &&
|
|
1223
|
+
(!isPlainJsonObject(span) ||
|
|
1224
|
+
!Number.isSafeInteger(span.start) ||
|
|
1225
|
+
span.start < 0 ||
|
|
1226
|
+
!Number.isSafeInteger(span.end) ||
|
|
1227
|
+
span.end < span.start ||
|
|
1228
|
+
!["exact", "insertion-point", "fallback"].includes(span.kind)))
|
|
1229
|
+
) {
|
|
1230
|
+
throw new MermanInvalidTransportError(
|
|
1231
|
+
"Merman transport returned invalid diagnostic error details.",
|
|
1232
|
+
);
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
if (details.cancellation !== undefined) {
|
|
1236
|
+
const cancellation = details.cancellation;
|
|
1237
|
+
if (
|
|
1238
|
+
!isPlainJsonObject(cancellation) ||
|
|
1239
|
+
!CANCELLATION_REASONS.has(cancellation.reason) ||
|
|
1240
|
+
typeof cancellation.phase !== "string" ||
|
|
1241
|
+
!OPERATION_PHASE_IDENTIFIER.test(cancellation.phase)
|
|
1242
|
+
) {
|
|
1243
|
+
throw new MermanInvalidTransportError(
|
|
1244
|
+
"Merman transport returned invalid cancellation error details.",
|
|
1245
|
+
);
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1135
1248
|
if (details.icon_registry !== undefined) {
|
|
1136
1249
|
const iconRegistry = details.icon_registry;
|
|
1137
1250
|
if (
|
|
@@ -1161,6 +1274,12 @@ function isBindingResourceCount(value) {
|
|
|
1161
1274
|
compareCanonicalUnsignedDecimals(value, U64_MAX_DECIMAL) <= 0;
|
|
1162
1275
|
}
|
|
1163
1276
|
|
|
1277
|
+
function isOptionalDiagnosticUnsignedInteger(value) {
|
|
1278
|
+
return value === undefined ||
|
|
1279
|
+
value === null ||
|
|
1280
|
+
(Number.isSafeInteger(value) && value >= 0);
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1164
1283
|
function compareCanonicalUnsignedDecimals(left, right) {
|
|
1165
1284
|
if (left.length !== right.length) return left.length < right.length ? -1 : 1;
|
|
1166
1285
|
if (left === right) return 0;
|
|
@@ -68,6 +68,14 @@ export const CAPABILITY_SPECS = [
|
|
|
68
68
|
"id": "png",
|
|
69
69
|
"implication_ids": []
|
|
70
70
|
},
|
|
71
|
+
{
|
|
72
|
+
"id": "rustdoc",
|
|
73
|
+
"implication_ids": [
|
|
74
|
+
"layout-cytoscape",
|
|
75
|
+
"markdown",
|
|
76
|
+
"math"
|
|
77
|
+
]
|
|
78
|
+
},
|
|
71
79
|
{
|
|
72
80
|
"id": "shell-completions",
|
|
73
81
|
"implication_ids": []
|
|
@@ -142,6 +150,144 @@ export const BINDING_OPERATION_METADATA_CONTRACT = {
|
|
|
142
150
|
}
|
|
143
151
|
],
|
|
144
152
|
"output_plans": [
|
|
153
|
+
{
|
|
154
|
+
"kind": "ascii",
|
|
155
|
+
"fields": [
|
|
156
|
+
{
|
|
157
|
+
"name": "kind",
|
|
158
|
+
"json_type": "string",
|
|
159
|
+
"required": true,
|
|
160
|
+
"integer_width_bits": null,
|
|
161
|
+
"open_value": false
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
"name": "schema_version",
|
|
165
|
+
"json_type": "unsigned-integer",
|
|
166
|
+
"required": true,
|
|
167
|
+
"integer_width_bits": 16,
|
|
168
|
+
"open_value": false
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"name": "family",
|
|
172
|
+
"json_type": "string",
|
|
173
|
+
"required": true,
|
|
174
|
+
"integer_width_bits": null,
|
|
175
|
+
"open_value": true
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
"name": "projection",
|
|
179
|
+
"json_type": "string",
|
|
180
|
+
"required": true,
|
|
181
|
+
"integer_width_bits": null,
|
|
182
|
+
"open_value": false
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
"name": "encoding",
|
|
186
|
+
"json_type": "string",
|
|
187
|
+
"required": true,
|
|
188
|
+
"integer_width_bits": null,
|
|
189
|
+
"open_value": false
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
"name": "primary_width",
|
|
193
|
+
"json_type": "unsigned-integer",
|
|
194
|
+
"required": true,
|
|
195
|
+
"integer_width_bits": 64,
|
|
196
|
+
"open_value": false
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
"name": "primary_height",
|
|
200
|
+
"json_type": "unsigned-integer",
|
|
201
|
+
"required": true,
|
|
202
|
+
"integer_width_bits": 64,
|
|
203
|
+
"open_value": false
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
"name": "emitted_width",
|
|
207
|
+
"json_type": "unsigned-integer",
|
|
208
|
+
"required": true,
|
|
209
|
+
"integer_width_bits": 64,
|
|
210
|
+
"open_value": false
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
"name": "emitted_height",
|
|
214
|
+
"json_type": "unsigned-integer",
|
|
215
|
+
"required": true,
|
|
216
|
+
"integer_width_bits": 64,
|
|
217
|
+
"open_value": false
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
"name": "width_profile",
|
|
221
|
+
"json_type": "string",
|
|
222
|
+
"required": true,
|
|
223
|
+
"integer_width_bits": null,
|
|
224
|
+
"open_value": false
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
"name": "layout_profile",
|
|
228
|
+
"json_type": "string",
|
|
229
|
+
"required": true,
|
|
230
|
+
"integer_width_bits": null,
|
|
231
|
+
"open_value": false
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
"name": "requested_max_width",
|
|
235
|
+
"json_type": "unsigned-integer",
|
|
236
|
+
"required": false,
|
|
237
|
+
"integer_width_bits": 64,
|
|
238
|
+
"open_value": false
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
"name": "overflowed",
|
|
242
|
+
"json_type": "boolean",
|
|
243
|
+
"required": true,
|
|
244
|
+
"integer_width_bits": null,
|
|
245
|
+
"open_value": false
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
"name": "outcome",
|
|
249
|
+
"json_type": "string",
|
|
250
|
+
"required": true,
|
|
251
|
+
"integer_width_bits": null,
|
|
252
|
+
"open_value": false
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
"name": "fallback_capability",
|
|
256
|
+
"json_type": "string",
|
|
257
|
+
"required": true,
|
|
258
|
+
"integer_width_bits": null,
|
|
259
|
+
"open_value": false
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
"name": "fallback_attempted",
|
|
263
|
+
"json_type": "boolean",
|
|
264
|
+
"required": true,
|
|
265
|
+
"integer_width_bits": null,
|
|
266
|
+
"open_value": false
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
"name": "fallback_reason",
|
|
270
|
+
"json_type": "string",
|
|
271
|
+
"required": false,
|
|
272
|
+
"integer_width_bits": null,
|
|
273
|
+
"open_value": false
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
"name": "trimmed",
|
|
277
|
+
"json_type": "boolean",
|
|
278
|
+
"required": true,
|
|
279
|
+
"integer_width_bits": null,
|
|
280
|
+
"open_value": false
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
"name": "lossiness",
|
|
284
|
+
"json_type": "string",
|
|
285
|
+
"required": true,
|
|
286
|
+
"integer_width_bits": null,
|
|
287
|
+
"open_value": false
|
|
288
|
+
}
|
|
289
|
+
]
|
|
290
|
+
},
|
|
145
291
|
{
|
|
146
292
|
"kind": "raster",
|
|
147
293
|
"fields": [
|
|
@@ -611,23 +757,23 @@ export const BINDING_OPTION_GROUP_SPECS = [
|
|
|
611
757
|
];
|
|
612
758
|
|
|
613
759
|
export const TEXT_MEASUREMENT_PROVIDER_SPECS = [
|
|
760
|
+
{
|
|
761
|
+
"id": "deterministic",
|
|
762
|
+
"source": "svg-pipeline",
|
|
763
|
+
"constructor_service_id": null
|
|
764
|
+
},
|
|
614
765
|
{
|
|
615
766
|
"id": "host-callback",
|
|
616
767
|
"source": "constructor-service",
|
|
617
768
|
"constructor_service_id": "host-text-measurement"
|
|
618
|
-
},
|
|
619
|
-
{
|
|
620
|
-
"id": "vendored",
|
|
621
|
-
"source": "svg-pipeline",
|
|
622
|
-
"constructor_service_id": null
|
|
623
769
|
}
|
|
624
770
|
];
|
|
625
771
|
|
|
626
772
|
export const TEXT_MEASUREMENT_PROVIDER_IDS = [
|
|
627
|
-
"
|
|
628
|
-
"
|
|
773
|
+
"deterministic",
|
|
774
|
+
"host-callback"
|
|
629
775
|
];
|
|
630
|
-
export const
|
|
776
|
+
export const DETERMINISTIC_TEXT_MEASUREMENT_PROVIDER_ID = "deterministic";
|
|
631
777
|
export const HOST_CALLBACK_TEXT_MEASUREMENT_PROVIDER_ID = "host-callback";
|
|
632
778
|
|
|
633
779
|
export const CONSTRUCTOR_SERVICE_SPECS = [
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Source: capabilities/feature-surface-v1.json. Do not edit directly.
|
|
3
3
|
|
|
4
4
|
export const CAPABILITY_DESCRIPTOR_SCHEMA_VERSION = 1;
|
|
5
|
-
export const CAPABILITY_DESCRIPTOR_DIGEST = "sha256:
|
|
5
|
+
export const CAPABILITY_DESCRIPTOR_DIGEST = "sha256:e3dfc678c1ccd830bb4e2a10b24b0e5957c6cb6112c9e1c9f75d847317ba31d7";
|
|
6
6
|
|
|
7
7
|
export const NODE_BINDING_OPERATIONS = [
|
|
8
8
|
{
|