@formspec/build 0.1.0-alpha.40 → 0.1.0-alpha.42
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/dist/browser.cjs +305 -23
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.js +305 -23
- package/dist/browser.js.map +1 -1
- package/dist/build-alpha.d.ts +10 -0
- package/dist/build-beta.d.ts +10 -0
- package/dist/build-internal.d.ts +10 -0
- package/dist/build.d.ts +10 -0
- package/dist/canonicalize/chain-dsl-canonicalizer.d.ts.map +1 -1
- package/dist/cli.cjs +243 -33
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +243 -33
- package/dist/cli.js.map +1 -1
- package/dist/generators/class-schema.d.ts +5 -0
- package/dist/generators/class-schema.d.ts.map +1 -1
- package/dist/generators/discovered-schema.d.ts.map +1 -1
- package/dist/generators/method-schema.d.ts +9 -2
- package/dist/generators/method-schema.d.ts.map +1 -1
- package/dist/index.cjs +222 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +222 -30
- package/dist/index.js.map +1 -1
- package/dist/internals.cjs +199 -33
- package/dist/internals.cjs.map +1 -1
- package/dist/internals.js +199 -33
- package/dist/internals.js.map +1 -1
- package/dist/json-schema/generator.d.ts +5 -0
- package/dist/json-schema/generator.d.ts.map +1 -1
- package/dist/json-schema/ir-generator.d.ts +5 -0
- package/dist/json-schema/ir-generator.d.ts.map +1 -1
- package/dist/metadata/policy.d.ts +3 -2
- package/dist/metadata/policy.d.ts.map +1 -1
- package/dist/metadata/resolve.d.ts +1 -0
- package/dist/metadata/resolve.d.ts.map +1 -1
- package/package.json +4 -4
package/dist/internals.js
CHANGED
|
@@ -51,11 +51,35 @@ function normalizeDeclarationPolicy(input) {
|
|
|
51
51
|
displayName: normalizeScalarPolicy(input?.displayName)
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
|
+
function normalizeEnumMemberDisplayNamePolicy(input) {
|
|
55
|
+
if (input?.mode === "infer-if-missing") {
|
|
56
|
+
return {
|
|
57
|
+
mode: "infer-if-missing",
|
|
58
|
+
infer: input.infer
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
if (input?.mode === "require-explicit") {
|
|
62
|
+
return {
|
|
63
|
+
mode: "require-explicit",
|
|
64
|
+
infer: () => ""
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
mode: "disabled",
|
|
69
|
+
infer: () => ""
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function normalizeEnumMemberPolicy(input) {
|
|
73
|
+
return {
|
|
74
|
+
displayName: normalizeEnumMemberDisplayNamePolicy(input?.displayName)
|
|
75
|
+
};
|
|
76
|
+
}
|
|
54
77
|
function normalizeMetadataPolicy(input) {
|
|
55
78
|
return {
|
|
56
79
|
type: normalizeDeclarationPolicy(input?.type),
|
|
57
80
|
field: normalizeDeclarationPolicy(input?.field),
|
|
58
|
-
method: normalizeDeclarationPolicy(input?.method)
|
|
81
|
+
method: normalizeDeclarationPolicy(input?.method),
|
|
82
|
+
enumMember: normalizeEnumMemberPolicy(input?.enumMember)
|
|
59
83
|
};
|
|
60
84
|
}
|
|
61
85
|
function getDeclarationMetadataPolicy(policy, declarationKind) {
|
|
@@ -152,6 +176,40 @@ function resolveResolvedMetadata(current, policy, context) {
|
|
|
152
176
|
...displayNamePlural !== void 0 && { displayNamePlural }
|
|
153
177
|
};
|
|
154
178
|
}
|
|
179
|
+
function resolveEnumMemberDisplayName(current, policy, context) {
|
|
180
|
+
if (current !== void 0) {
|
|
181
|
+
return current;
|
|
182
|
+
}
|
|
183
|
+
if (policy.mode === "require-explicit") {
|
|
184
|
+
throw new Error(
|
|
185
|
+
`Metadata policy requires explicit displayName for enum member "${context.logicalName}" on the ${context.surface} surface.`
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
if (policy.mode !== "infer-if-missing") {
|
|
189
|
+
return void 0;
|
|
190
|
+
}
|
|
191
|
+
const inferredValue = policy.infer(context).trim();
|
|
192
|
+
return inferredValue !== "" ? inferredValue : void 0;
|
|
193
|
+
}
|
|
194
|
+
function resolveEnumTypeMetadata(type, options) {
|
|
195
|
+
const members = type.members.map((member) => {
|
|
196
|
+
const displayName = resolveEnumMemberDisplayName(
|
|
197
|
+
member.displayName,
|
|
198
|
+
options.policy.enumMember.displayName,
|
|
199
|
+
{
|
|
200
|
+
surface: options.surface,
|
|
201
|
+
logicalName: String(member.value),
|
|
202
|
+
memberValue: member.value,
|
|
203
|
+
...options.buildContext !== void 0 && { buildContext: options.buildContext }
|
|
204
|
+
}
|
|
205
|
+
);
|
|
206
|
+
if (displayName === member.displayName) {
|
|
207
|
+
return member;
|
|
208
|
+
}
|
|
209
|
+
return displayName === void 0 ? { value: member.value } : { value: member.value, displayName };
|
|
210
|
+
});
|
|
211
|
+
return members.some((member, index) => member !== type.members[index]) ? { ...type, members } : type;
|
|
212
|
+
}
|
|
155
213
|
function resolveTypeNodeMetadata(type, options) {
|
|
156
214
|
switch (type.kind) {
|
|
157
215
|
case "array":
|
|
@@ -174,9 +232,10 @@ function resolveTypeNodeMetadata(type, options) {
|
|
|
174
232
|
...type,
|
|
175
233
|
members: type.members.map((member) => resolveTypeNodeMetadata(member, options))
|
|
176
234
|
};
|
|
235
|
+
case "enum":
|
|
236
|
+
return resolveEnumTypeMetadata(type, options);
|
|
177
237
|
case "reference":
|
|
178
238
|
case "primitive":
|
|
179
|
-
case "enum":
|
|
180
239
|
case "dynamic":
|
|
181
240
|
case "custom":
|
|
182
241
|
return type;
|
|
@@ -255,11 +314,10 @@ function getDisplayName(metadata) {
|
|
|
255
314
|
return metadata?.displayName?.value;
|
|
256
315
|
}
|
|
257
316
|
function resolveFormIRMetadata(ir, options) {
|
|
258
|
-
const
|
|
259
|
-
const metadata = resolveResolvedMetadata(ir.metadata, options.policy.type, {
|
|
317
|
+
const metadata = options.resolveRootTypeMetadata === false ? ir.metadata : resolveResolvedMetadata(ir.metadata, options.policy.type, {
|
|
260
318
|
surface: options.surface,
|
|
261
319
|
declarationKind: "type",
|
|
262
|
-
logicalName: rootLogicalName,
|
|
320
|
+
logicalName: options.rootLogicalName ?? ir.name ?? "FormSpec",
|
|
263
321
|
...options.buildContext !== void 0 && { buildContext: options.buildContext }
|
|
264
322
|
});
|
|
265
323
|
return {
|
|
@@ -295,7 +353,7 @@ function canonicalizeChainDSL(form, options) {
|
|
|
295
353
|
const metadataPolicy = normalizeMetadataPolicy(
|
|
296
354
|
options?.metadata ?? _getFormSpecMetadataPolicy(form)
|
|
297
355
|
);
|
|
298
|
-
|
|
356
|
+
const ir = {
|
|
299
357
|
kind: "form-ir",
|
|
300
358
|
irVersion: IR_VERSION,
|
|
301
359
|
elements: canonicalizeElements(form.elements, metadataPolicy),
|
|
@@ -303,6 +361,13 @@ function canonicalizeChainDSL(form, options) {
|
|
|
303
361
|
typeRegistry: {},
|
|
304
362
|
provenance: CHAIN_DSL_PROVENANCE
|
|
305
363
|
};
|
|
364
|
+
return resolveFormIRMetadata(ir, {
|
|
365
|
+
policy: metadataPolicy,
|
|
366
|
+
surface: "chain-dsl",
|
|
367
|
+
// Chain DSL has no root/type-metadata authoring surface, so only resolve
|
|
368
|
+
// field/type-registry metadata and enum-member labels here.
|
|
369
|
+
resolveRootTypeMetadata: false
|
|
370
|
+
});
|
|
306
371
|
}
|
|
307
372
|
function canonicalizeElements(elements, metadataPolicy) {
|
|
308
373
|
return elements.map((element) => canonicalizeElement(element, metadataPolicy));
|
|
@@ -4171,17 +4236,25 @@ function assertNoSerializedNameCollisions(ir) {
|
|
|
4171
4236
|
// src/json-schema/ir-generator.ts
|
|
4172
4237
|
function makeContext(options) {
|
|
4173
4238
|
const vendorPrefix = options?.vendorPrefix ?? "x-formspec";
|
|
4239
|
+
const rawEnumSerialization = options?.enumSerialization;
|
|
4174
4240
|
if (!vendorPrefix.startsWith("x-")) {
|
|
4175
4241
|
throw new Error(
|
|
4176
4242
|
`Invalid vendorPrefix "${vendorPrefix}". Extension JSON Schema keywords must start with "x-".`
|
|
4177
4243
|
);
|
|
4178
4244
|
}
|
|
4245
|
+
if (rawEnumSerialization !== void 0 && rawEnumSerialization !== "enum" && rawEnumSerialization !== "oneOf") {
|
|
4246
|
+
throw new Error(
|
|
4247
|
+
`Invalid enumSerialization "${rawEnumSerialization}". Expected "enum" or "oneOf".`
|
|
4248
|
+
);
|
|
4249
|
+
}
|
|
4250
|
+
const enumSerialization = rawEnumSerialization ?? "enum";
|
|
4179
4251
|
return {
|
|
4180
4252
|
defs: {},
|
|
4181
4253
|
typeNameMap: {},
|
|
4182
4254
|
typeRegistry: {},
|
|
4183
4255
|
extensionRegistry: options?.extensionRegistry,
|
|
4184
|
-
vendorPrefix
|
|
4256
|
+
vendorPrefix,
|
|
4257
|
+
enumSerialization
|
|
4185
4258
|
};
|
|
4186
4259
|
}
|
|
4187
4260
|
function generateJsonSchemaFromIR(ir, options) {
|
|
@@ -4355,7 +4428,7 @@ function generateTypeNode(type, ctx) {
|
|
|
4355
4428
|
case "primitive":
|
|
4356
4429
|
return generatePrimitiveType(type);
|
|
4357
4430
|
case "enum":
|
|
4358
|
-
return generateEnumType(type);
|
|
4431
|
+
return generateEnumType(type, ctx);
|
|
4359
4432
|
case "array":
|
|
4360
4433
|
return generateArrayType(type, ctx);
|
|
4361
4434
|
case "object":
|
|
@@ -4381,20 +4454,37 @@ function generatePrimitiveType(type) {
|
|
|
4381
4454
|
type: type.primitiveKind === "integer" || type.primitiveKind === "bigint" ? "integer" : type.primitiveKind
|
|
4382
4455
|
};
|
|
4383
4456
|
}
|
|
4384
|
-
function generateEnumType(type) {
|
|
4385
|
-
|
|
4386
|
-
if (hasDisplayNames) {
|
|
4457
|
+
function generateEnumType(type, ctx) {
|
|
4458
|
+
if (ctx.enumSerialization === "oneOf") {
|
|
4387
4459
|
return {
|
|
4388
|
-
oneOf: type.members.map((m) => {
|
|
4389
|
-
const
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
}
|
|
4393
|
-
return entry;
|
|
4394
|
-
})
|
|
4460
|
+
oneOf: type.members.map((m) => ({
|
|
4461
|
+
const: m.value,
|
|
4462
|
+
title: m.displayName ?? String(m.value)
|
|
4463
|
+
}))
|
|
4395
4464
|
};
|
|
4396
4465
|
}
|
|
4397
|
-
|
|
4466
|
+
const schema = { enum: type.members.map((m) => m.value) };
|
|
4467
|
+
const displayNames = buildEnumDisplayNameExtension(type);
|
|
4468
|
+
if (displayNames !== void 0) {
|
|
4469
|
+
schema[`${ctx.vendorPrefix}-display-names`] = displayNames;
|
|
4470
|
+
}
|
|
4471
|
+
return schema;
|
|
4472
|
+
}
|
|
4473
|
+
function buildEnumDisplayNameExtension(type) {
|
|
4474
|
+
if (!type.members.some((member) => member.displayName !== void 0)) {
|
|
4475
|
+
return void 0;
|
|
4476
|
+
}
|
|
4477
|
+
const displayNames = /* @__PURE__ */ Object.create(null);
|
|
4478
|
+
for (const member of type.members) {
|
|
4479
|
+
const key = String(member.value);
|
|
4480
|
+
if (Object.hasOwn(displayNames, key)) {
|
|
4481
|
+
throw new Error(
|
|
4482
|
+
`Enum display-name key "${key}" is ambiguous after stringification. Use oneOf serialization for mixed string/number enum values that collide.`
|
|
4483
|
+
);
|
|
4484
|
+
}
|
|
4485
|
+
displayNames[key] = member.displayName ?? key;
|
|
4486
|
+
}
|
|
4487
|
+
return displayNames;
|
|
4398
4488
|
}
|
|
4399
4489
|
function generateArrayType(type, ctx) {
|
|
4400
4490
|
return {
|
|
@@ -4743,6 +4833,66 @@ function generateCustomType(type, ctx) {
|
|
|
4743
4833
|
}
|
|
4744
4834
|
return registration.toJsonSchema(type.payload, ctx.vendorPrefix);
|
|
4745
4835
|
}
|
|
4836
|
+
var JSON_SCHEMA_STRUCTURAL_KEYWORDS = /* @__PURE__ */ new Set([
|
|
4837
|
+
"$schema",
|
|
4838
|
+
"$ref",
|
|
4839
|
+
"$defs",
|
|
4840
|
+
"$id",
|
|
4841
|
+
"$anchor",
|
|
4842
|
+
"$dynamicRef",
|
|
4843
|
+
"$dynamicAnchor",
|
|
4844
|
+
"$vocabulary",
|
|
4845
|
+
"$comment",
|
|
4846
|
+
"type",
|
|
4847
|
+
"enum",
|
|
4848
|
+
"const",
|
|
4849
|
+
"properties",
|
|
4850
|
+
"patternProperties",
|
|
4851
|
+
"additionalProperties",
|
|
4852
|
+
"required",
|
|
4853
|
+
"items",
|
|
4854
|
+
"prefixItems",
|
|
4855
|
+
"additionalItems",
|
|
4856
|
+
"contains",
|
|
4857
|
+
"allOf",
|
|
4858
|
+
"oneOf",
|
|
4859
|
+
"anyOf",
|
|
4860
|
+
"not",
|
|
4861
|
+
"if",
|
|
4862
|
+
"then",
|
|
4863
|
+
"else",
|
|
4864
|
+
"minimum",
|
|
4865
|
+
"maximum",
|
|
4866
|
+
"exclusiveMinimum",
|
|
4867
|
+
"exclusiveMaximum",
|
|
4868
|
+
"multipleOf",
|
|
4869
|
+
"minLength",
|
|
4870
|
+
"maxLength",
|
|
4871
|
+
"pattern",
|
|
4872
|
+
"minItems",
|
|
4873
|
+
"maxItems",
|
|
4874
|
+
"uniqueItems",
|
|
4875
|
+
"minProperties",
|
|
4876
|
+
"maxProperties",
|
|
4877
|
+
"minContains",
|
|
4878
|
+
"maxContains",
|
|
4879
|
+
"format",
|
|
4880
|
+
"title",
|
|
4881
|
+
"description",
|
|
4882
|
+
"default",
|
|
4883
|
+
"deprecated",
|
|
4884
|
+
"readOnly",
|
|
4885
|
+
"writeOnly",
|
|
4886
|
+
"examples",
|
|
4887
|
+
"dependentRequired",
|
|
4888
|
+
"dependentSchemas",
|
|
4889
|
+
"propertyNames",
|
|
4890
|
+
"unevaluatedItems",
|
|
4891
|
+
"unevaluatedProperties",
|
|
4892
|
+
"contentEncoding",
|
|
4893
|
+
"contentMediaType",
|
|
4894
|
+
"contentSchema"
|
|
4895
|
+
]);
|
|
4746
4896
|
function applyCustomConstraint(schema, constraint, ctx) {
|
|
4747
4897
|
const registration = ctx.extensionRegistry?.findConstraint(constraint.constraintId);
|
|
4748
4898
|
if (registration === void 0) {
|
|
@@ -4750,12 +4900,25 @@ function applyCustomConstraint(schema, constraint, ctx) {
|
|
|
4750
4900
|
`Cannot generate JSON Schema for custom constraint "${constraint.constraintId}" without a matching extension registration`
|
|
4751
4901
|
);
|
|
4752
4902
|
}
|
|
4753
|
-
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
|
|
4903
|
+
const extensionSchema = registration.toJsonSchema(constraint.payload, ctx.vendorPrefix);
|
|
4904
|
+
if (registration.emitsVocabularyKeywords) {
|
|
4905
|
+
const target = schema;
|
|
4906
|
+
for (const [key, value] of Object.entries(extensionSchema)) {
|
|
4907
|
+
if (JSON_SCHEMA_STRUCTURAL_KEYWORDS.has(key)) {
|
|
4908
|
+
throw new Error(
|
|
4909
|
+
`Custom constraint "${constraint.constraintId}" with emitsVocabularyKeywords must not overwrite standard JSON Schema keyword "${key}"`
|
|
4910
|
+
);
|
|
4911
|
+
}
|
|
4912
|
+
target[key] = value;
|
|
4913
|
+
}
|
|
4914
|
+
} else {
|
|
4915
|
+
assignVendorPrefixedExtensionKeywords(
|
|
4916
|
+
schema,
|
|
4917
|
+
extensionSchema,
|
|
4918
|
+
ctx.vendorPrefix,
|
|
4919
|
+
`custom constraint "${constraint.constraintId}"`
|
|
4920
|
+
);
|
|
4921
|
+
}
|
|
4759
4922
|
}
|
|
4760
4923
|
function applyCustomAnnotation(schema, annotation, ctx) {
|
|
4761
4924
|
const registration = ctx.extensionRegistry?.findAnnotation(annotation.annotationId);
|
|
@@ -5319,7 +5482,7 @@ function createExtensionRegistry(extensions) {
|
|
|
5319
5482
|
|
|
5320
5483
|
// src/generators/method-schema.ts
|
|
5321
5484
|
import { IR_VERSION as IR_VERSION3 } from "@formspec/core/internals";
|
|
5322
|
-
function typeToJsonSchema(type, checker) {
|
|
5485
|
+
function typeToJsonSchema(type, checker, options) {
|
|
5323
5486
|
const typeRegistry = {};
|
|
5324
5487
|
const visiting = /* @__PURE__ */ new Set();
|
|
5325
5488
|
const diagnostics = [];
|
|
@@ -5359,7 +5522,10 @@ function typeToJsonSchema(type, checker) {
|
|
|
5359
5522
|
typeRegistry,
|
|
5360
5523
|
provenance: fieldProvenance
|
|
5361
5524
|
};
|
|
5362
|
-
const schema = generateJsonSchemaFromIR(
|
|
5525
|
+
const schema = generateJsonSchemaFromIR(
|
|
5526
|
+
ir,
|
|
5527
|
+
options?.enumSerialization === void 0 ? void 0 : { enumSerialization: options.enumSerialization }
|
|
5528
|
+
);
|
|
5363
5529
|
const fieldSchema = schema.properties?.["__result"];
|
|
5364
5530
|
if (fieldSchema) {
|
|
5365
5531
|
if (schema.$defs && Object.keys(schema.$defs).length > 0) {
|
|
@@ -5369,16 +5535,16 @@ function typeToJsonSchema(type, checker) {
|
|
|
5369
5535
|
}
|
|
5370
5536
|
return { type: "object" };
|
|
5371
5537
|
}
|
|
5372
|
-
function generateMethodSchemas(method, checker, loadedFormSpecs) {
|
|
5373
|
-
const returnType = typeToJsonSchema(method.returnType, checker);
|
|
5374
|
-
const params = generateParamsSchemas(method.parameters, checker, loadedFormSpecs);
|
|
5538
|
+
function generateMethodSchemas(method, checker, loadedFormSpecs, options) {
|
|
5539
|
+
const returnType = typeToJsonSchema(method.returnType, checker, options);
|
|
5540
|
+
const params = generateParamsSchemas(method.parameters, checker, loadedFormSpecs, options);
|
|
5375
5541
|
return {
|
|
5376
5542
|
name: method.name,
|
|
5377
5543
|
params,
|
|
5378
5544
|
returnType
|
|
5379
5545
|
};
|
|
5380
5546
|
}
|
|
5381
|
-
function generateParamsSchemas(parameters, checker, loadedFormSpecs) {
|
|
5547
|
+
function generateParamsSchemas(parameters, checker, loadedFormSpecs, options) {
|
|
5382
5548
|
if (parameters.length === 0) {
|
|
5383
5549
|
return null;
|
|
5384
5550
|
}
|
|
@@ -5399,7 +5565,7 @@ function generateParamsSchemas(parameters, checker, loadedFormSpecs) {
|
|
|
5399
5565
|
}
|
|
5400
5566
|
if (parameters.length === 1 && parameters[0]) {
|
|
5401
5567
|
const param = parameters[0];
|
|
5402
|
-
const jsonSchema = typeToJsonSchema(param.type, checker);
|
|
5568
|
+
const jsonSchema = typeToJsonSchema(param.type, checker, options);
|
|
5403
5569
|
return {
|
|
5404
5570
|
jsonSchema,
|
|
5405
5571
|
uiSchema: null,
|
|
@@ -5409,7 +5575,7 @@ function generateParamsSchemas(parameters, checker, loadedFormSpecs) {
|
|
|
5409
5575
|
const properties = {};
|
|
5410
5576
|
const required = [];
|
|
5411
5577
|
for (const param of parameters) {
|
|
5412
|
-
const paramSchema = typeToJsonSchema(param.type, checker);
|
|
5578
|
+
const paramSchema = typeToJsonSchema(param.type, checker, options);
|
|
5413
5579
|
properties[param.name] = paramSchema;
|
|
5414
5580
|
if (!param.optional) {
|
|
5415
5581
|
required.push(param.name);
|