@formspec/build 0.1.0-alpha.28 → 0.1.0-alpha.30

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 (50) hide show
  1. package/dist/analyzer/class-analyzer.d.ts +11 -5
  2. package/dist/analyzer/class-analyzer.d.ts.map +1 -1
  3. package/dist/analyzer/program.d.ts +3 -2
  4. package/dist/analyzer/program.d.ts.map +1 -1
  5. package/dist/browser.cjs +485 -76
  6. package/dist/browser.cjs.map +1 -1
  7. package/dist/browser.js +486 -77
  8. package/dist/browser.js.map +1 -1
  9. package/dist/build-alpha.d.ts +18 -2
  10. package/dist/build-beta.d.ts +18 -2
  11. package/dist/build-internal.d.ts +18 -2
  12. package/dist/build.d.ts +18 -2
  13. package/dist/canonicalize/chain-dsl-canonicalizer.d.ts +5 -2
  14. package/dist/canonicalize/chain-dsl-canonicalizer.d.ts.map +1 -1
  15. package/dist/canonicalize/tsdoc-canonicalizer.d.ts +5 -1
  16. package/dist/canonicalize/tsdoc-canonicalizer.d.ts.map +1 -1
  17. package/dist/cli.cjs +1031 -170
  18. package/dist/cli.cjs.map +1 -1
  19. package/dist/cli.js +1032 -171
  20. package/dist/cli.js.map +1 -1
  21. package/dist/generators/class-schema.d.ts +6 -1
  22. package/dist/generators/class-schema.d.ts.map +1 -1
  23. package/dist/generators/method-schema.d.ts.map +1 -1
  24. package/dist/generators/mixed-authoring.d.ts.map +1 -1
  25. package/dist/index.cjs +998 -170
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.d.ts +4 -1
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +999 -171
  30. package/dist/index.js.map +1 -1
  31. package/dist/internals.cjs +921 -155
  32. package/dist/internals.cjs.map +1 -1
  33. package/dist/internals.js +922 -156
  34. package/dist/internals.js.map +1 -1
  35. package/dist/json-schema/generator.d.ts +3 -1
  36. package/dist/json-schema/generator.d.ts.map +1 -1
  37. package/dist/json-schema/ir-generator.d.ts.map +1 -1
  38. package/dist/metadata/collision-guards.d.ts +3 -0
  39. package/dist/metadata/collision-guards.d.ts.map +1 -0
  40. package/dist/metadata/index.d.ts +7 -0
  41. package/dist/metadata/index.d.ts.map +1 -0
  42. package/dist/metadata/policy.d.ts +11 -0
  43. package/dist/metadata/policy.d.ts.map +1 -0
  44. package/dist/metadata/resolve.d.ts +20 -0
  45. package/dist/metadata/resolve.d.ts.map +1 -0
  46. package/dist/ui-schema/generator.d.ts +11 -2
  47. package/dist/ui-schema/generator.d.ts.map +1 -1
  48. package/dist/ui-schema/ir-generator.d.ts +2 -1
  49. package/dist/ui-schema/ir-generator.d.ts.map +1 -1
  50. package/package.json +4 -4
package/dist/cli.js CHANGED
@@ -9,8 +9,336 @@ var __export = (target, all) => {
9
9
  __defProp(target, name, { get: all[name], enumerable: true });
10
10
  };
11
11
 
12
+ // src/metadata/policy.ts
13
+ function normalizePluralization(input) {
14
+ if (input?.mode === "infer-if-missing") {
15
+ return {
16
+ mode: "infer-if-missing",
17
+ infer: () => "",
18
+ inflect: input.inflect
19
+ };
20
+ }
21
+ if (input?.mode === "require-explicit") {
22
+ return {
23
+ mode: "require-explicit",
24
+ infer: () => "",
25
+ inflect: NOOP_INFLECT
26
+ };
27
+ }
28
+ return {
29
+ mode: "disabled",
30
+ infer: () => "",
31
+ inflect: NOOP_INFLECT
32
+ };
33
+ }
34
+ function normalizeScalarPolicy(input) {
35
+ if (input?.mode === "infer-if-missing") {
36
+ return {
37
+ mode: "infer-if-missing",
38
+ infer: input.infer,
39
+ pluralization: normalizePluralization(input.pluralization)
40
+ };
41
+ }
42
+ if (input?.mode === "require-explicit") {
43
+ return {
44
+ mode: "require-explicit",
45
+ infer: () => "",
46
+ pluralization: normalizePluralization(input.pluralization)
47
+ };
48
+ }
49
+ return {
50
+ mode: "disabled",
51
+ infer: () => "",
52
+ pluralization: normalizePluralization(input?.pluralization)
53
+ };
54
+ }
55
+ function normalizeDeclarationPolicy(input) {
56
+ return {
57
+ apiName: normalizeScalarPolicy(input?.apiName),
58
+ displayName: normalizeScalarPolicy(input?.displayName)
59
+ };
60
+ }
61
+ function normalizeMetadataPolicy(input) {
62
+ return {
63
+ type: normalizeDeclarationPolicy(input?.type),
64
+ field: normalizeDeclarationPolicy(input?.field),
65
+ method: normalizeDeclarationPolicy(input?.method)
66
+ };
67
+ }
68
+ function getDeclarationMetadataPolicy(policy, declarationKind) {
69
+ return policy[declarationKind];
70
+ }
71
+ function makeMetadataContext(surface, declarationKind, logicalName, buildContext) {
72
+ return {
73
+ surface,
74
+ declarationKind,
75
+ logicalName,
76
+ ...buildContext !== void 0 && { buildContext }
77
+ };
78
+ }
79
+ var NOOP_INFLECT;
80
+ var init_policy = __esm({
81
+ "src/metadata/policy.ts"() {
82
+ "use strict";
83
+ NOOP_INFLECT = () => "";
84
+ }
85
+ });
86
+
87
+ // src/metadata/resolve.ts
88
+ function toExplicitScalar(value) {
89
+ return value !== void 0 && value.trim() !== "" ? { value, source: "explicit" } : void 0;
90
+ }
91
+ function toExplicitResolvedMetadata(explicit) {
92
+ if (explicit === void 0) {
93
+ return void 0;
94
+ }
95
+ const apiName = toExplicitScalar(explicit.apiName);
96
+ const displayName = toExplicitScalar(explicit.displayName);
97
+ const apiNamePlural = toExplicitScalar(explicit.apiNamePlural);
98
+ const displayNamePlural = toExplicitScalar(explicit.displayNamePlural);
99
+ const metadata = {
100
+ ...apiName !== void 0 && { apiName },
101
+ ...displayName !== void 0 && { displayName },
102
+ ...apiNamePlural !== void 0 && { apiNamePlural },
103
+ ...displayNamePlural !== void 0 && { displayNamePlural }
104
+ };
105
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
106
+ }
107
+ function resolveScalar(current, policy, context, metadataLabel) {
108
+ if (current !== void 0) {
109
+ return current;
110
+ }
111
+ if (policy.mode === "require-explicit") {
112
+ throw new Error(
113
+ `Metadata policy requires explicit ${metadataLabel} for ${context.declarationKind} "${context.logicalName}" on the ${context.surface} surface.`
114
+ );
115
+ }
116
+ if (policy.mode !== "infer-if-missing") {
117
+ return void 0;
118
+ }
119
+ const inferredValue = policy.infer(context);
120
+ return inferredValue.trim() !== "" ? { value: inferredValue, source: "inferred" } : void 0;
121
+ }
122
+ function resolvePlural(current, singular, policy, context, metadataLabel) {
123
+ if (current !== void 0) {
124
+ return current;
125
+ }
126
+ if (policy.mode === "require-explicit") {
127
+ throw new Error(
128
+ `Metadata policy requires explicit ${metadataLabel} for ${context.declarationKind} "${context.logicalName}" on the ${context.surface} surface.`
129
+ );
130
+ }
131
+ if (singular === void 0 || policy.mode !== "infer-if-missing") {
132
+ return void 0;
133
+ }
134
+ const pluralValue = policy.inflect({ ...context, singular: singular.value });
135
+ return pluralValue.trim() !== "" ? { value: pluralValue, source: "inferred" } : void 0;
136
+ }
137
+ function resolveResolvedMetadata(current, policy, context) {
138
+ const apiName = resolveScalar(current?.apiName, policy.apiName, context, "apiName");
139
+ const displayName = resolveScalar(
140
+ current?.displayName,
141
+ policy.displayName,
142
+ context,
143
+ "displayName"
144
+ );
145
+ const apiNamePlural = resolvePlural(
146
+ current?.apiNamePlural,
147
+ apiName,
148
+ policy.apiName.pluralization,
149
+ context,
150
+ "apiNamePlural"
151
+ );
152
+ const displayNamePlural = resolvePlural(
153
+ current?.displayNamePlural,
154
+ displayName,
155
+ policy.displayName.pluralization,
156
+ context,
157
+ "displayNamePlural"
158
+ );
159
+ if (apiName === void 0 && displayName === void 0 && apiNamePlural === void 0 && displayNamePlural === void 0) {
160
+ return void 0;
161
+ }
162
+ return {
163
+ ...apiName !== void 0 && { apiName },
164
+ ...displayName !== void 0 && { displayName },
165
+ ...apiNamePlural !== void 0 && { apiNamePlural },
166
+ ...displayNamePlural !== void 0 && { displayNamePlural }
167
+ };
168
+ }
169
+ function pickResolvedMetadataValue(baseValue, overlayValue) {
170
+ if (overlayValue?.source === "explicit") {
171
+ return overlayValue;
172
+ }
173
+ if (baseValue?.source === "explicit") {
174
+ return baseValue;
175
+ }
176
+ return baseValue ?? overlayValue;
177
+ }
178
+ function resolveTypeNodeMetadata(type, options) {
179
+ switch (type.kind) {
180
+ case "array":
181
+ return {
182
+ ...type,
183
+ items: resolveTypeNodeMetadata(type.items, options)
184
+ };
185
+ case "object":
186
+ return {
187
+ ...type,
188
+ properties: type.properties.map((property) => resolveObjectPropertyMetadata(property, options))
189
+ };
190
+ case "record":
191
+ return {
192
+ ...type,
193
+ valueType: resolveTypeNodeMetadata(type.valueType, options)
194
+ };
195
+ case "union":
196
+ return {
197
+ ...type,
198
+ members: type.members.map((member) => resolveTypeNodeMetadata(member, options))
199
+ };
200
+ case "reference":
201
+ case "primitive":
202
+ case "enum":
203
+ case "dynamic":
204
+ case "custom":
205
+ return type;
206
+ default: {
207
+ const _exhaustive = type;
208
+ return _exhaustive;
209
+ }
210
+ }
211
+ }
212
+ function resolveObjectPropertyMetadata(property, options) {
213
+ const metadata = resolveResolvedMetadata(property.metadata, options.policy.field, {
214
+ surface: options.surface,
215
+ declarationKind: "field",
216
+ logicalName: property.name,
217
+ ...options.buildContext !== void 0 && { buildContext: options.buildContext }
218
+ });
219
+ return {
220
+ ...property,
221
+ ...metadata !== void 0 && { metadata },
222
+ type: resolveTypeNodeMetadata(property.type, options)
223
+ };
224
+ }
225
+ function resolveFieldMetadataNode(field, options) {
226
+ const metadata = resolveResolvedMetadata(field.metadata, options.policy.field, {
227
+ surface: options.surface,
228
+ declarationKind: "field",
229
+ logicalName: field.name,
230
+ ...options.buildContext !== void 0 && { buildContext: options.buildContext }
231
+ });
232
+ return {
233
+ ...field,
234
+ ...metadata !== void 0 && { metadata },
235
+ type: resolveTypeNodeMetadata(field.type, options)
236
+ };
237
+ }
238
+ function resolveFormElementMetadata(element, options) {
239
+ switch (element.kind) {
240
+ case "field":
241
+ return resolveFieldMetadataNode(element, options);
242
+ case "group":
243
+ return {
244
+ ...element,
245
+ elements: element.elements.map((child) => resolveFormElementMetadata(child, options))
246
+ };
247
+ case "conditional":
248
+ return {
249
+ ...element,
250
+ elements: element.elements.map((child) => resolveFormElementMetadata(child, options))
251
+ };
252
+ default: {
253
+ const _exhaustive = element;
254
+ return _exhaustive;
255
+ }
256
+ }
257
+ }
258
+ function resolveTypeDefinitionMetadata(typeDefinition, options) {
259
+ const metadata = resolveResolvedMetadata(typeDefinition.metadata, options.policy.type, {
260
+ surface: options.surface,
261
+ declarationKind: "type",
262
+ logicalName: typeDefinition.name,
263
+ ...options.buildContext !== void 0 && { buildContext: options.buildContext }
264
+ });
265
+ return {
266
+ ...typeDefinition,
267
+ ...metadata !== void 0 && { metadata },
268
+ type: resolveTypeNodeMetadata(typeDefinition.type, options)
269
+ };
270
+ }
271
+ function resolveMetadata(explicit, policy, context) {
272
+ return resolveResolvedMetadata(toExplicitResolvedMetadata(explicit), policy, context);
273
+ }
274
+ function mergeResolvedMetadata(baseMetadata, overlayMetadata) {
275
+ const apiName = pickResolvedMetadataValue(baseMetadata?.apiName, overlayMetadata?.apiName);
276
+ const displayName = pickResolvedMetadataValue(
277
+ baseMetadata?.displayName,
278
+ overlayMetadata?.displayName
279
+ );
280
+ const apiNamePlural = pickResolvedMetadataValue(
281
+ baseMetadata?.apiNamePlural,
282
+ overlayMetadata?.apiNamePlural
283
+ );
284
+ const displayNamePlural = pickResolvedMetadataValue(
285
+ baseMetadata?.displayNamePlural,
286
+ overlayMetadata?.displayNamePlural
287
+ );
288
+ if (apiName === void 0 && displayName === void 0 && apiNamePlural === void 0 && displayNamePlural === void 0) {
289
+ return void 0;
290
+ }
291
+ return {
292
+ ...apiName !== void 0 && { apiName },
293
+ ...displayName !== void 0 && { displayName },
294
+ ...apiNamePlural !== void 0 && { apiNamePlural },
295
+ ...displayNamePlural !== void 0 && { displayNamePlural }
296
+ };
297
+ }
298
+ function getSerializedName(logicalName, metadata) {
299
+ return metadata?.apiName?.value ?? logicalName;
300
+ }
301
+ function getDisplayName(metadata) {
302
+ return metadata?.displayName?.value;
303
+ }
304
+ function resolveFormIRMetadata(ir, options) {
305
+ const rootLogicalName = options.rootLogicalName ?? ir.name ?? "FormSpec";
306
+ const metadata = resolveResolvedMetadata(ir.metadata, options.policy.type, {
307
+ surface: options.surface,
308
+ declarationKind: "type",
309
+ logicalName: rootLogicalName,
310
+ ...options.buildContext !== void 0 && { buildContext: options.buildContext }
311
+ });
312
+ return {
313
+ ...ir,
314
+ ...metadata !== void 0 && { metadata },
315
+ elements: ir.elements.map((element) => resolveFormElementMetadata(element, options)),
316
+ typeRegistry: Object.fromEntries(
317
+ Object.entries(ir.typeRegistry).map(([name, definition]) => [
318
+ name,
319
+ resolveTypeDefinitionMetadata(definition, options)
320
+ ])
321
+ )
322
+ };
323
+ }
324
+ var init_resolve = __esm({
325
+ "src/metadata/resolve.ts"() {
326
+ "use strict";
327
+ }
328
+ });
329
+
330
+ // src/metadata/index.ts
331
+ var init_metadata = __esm({
332
+ "src/metadata/index.ts"() {
333
+ "use strict";
334
+ init_policy();
335
+ init_resolve();
336
+ init_resolve();
337
+ }
338
+ });
339
+
12
340
  // src/canonicalize/chain-dsl-canonicalizer.ts
13
- import { IR_VERSION } from "@formspec/core/internals";
341
+ import { IR_VERSION, _getFormSpecMetadataPolicy } from "@formspec/core/internals";
14
342
  function isGroup(el) {
15
343
  return el._type === "group";
16
344
  }
@@ -20,57 +348,60 @@ function isConditional(el) {
20
348
  function isField(el) {
21
349
  return el._type === "field";
22
350
  }
23
- function canonicalizeChainDSL(form) {
351
+ function canonicalizeChainDSL(form, options) {
352
+ const metadataPolicy = normalizeMetadataPolicy(
353
+ options?.metadata ?? _getFormSpecMetadataPolicy(form)
354
+ );
24
355
  return {
25
356
  kind: "form-ir",
26
357
  irVersion: IR_VERSION,
27
- elements: canonicalizeElements(form.elements),
358
+ elements: canonicalizeElements(form.elements, metadataPolicy),
28
359
  rootAnnotations: [],
29
360
  typeRegistry: {},
30
361
  provenance: CHAIN_DSL_PROVENANCE
31
362
  };
32
363
  }
33
- function canonicalizeElements(elements) {
34
- return elements.map(canonicalizeElement);
364
+ function canonicalizeElements(elements, metadataPolicy) {
365
+ return elements.map((element) => canonicalizeElement(element, metadataPolicy));
35
366
  }
36
- function canonicalizeElement(element) {
367
+ function canonicalizeElement(element, metadataPolicy) {
37
368
  if (isField(element)) {
38
- return canonicalizeField(element);
369
+ return canonicalizeField(element, metadataPolicy);
39
370
  }
40
371
  if (isGroup(element)) {
41
- return canonicalizeGroup(element);
372
+ return canonicalizeGroup(element, metadataPolicy);
42
373
  }
43
374
  if (isConditional(element)) {
44
- return canonicalizeConditional(element);
375
+ return canonicalizeConditional(element, metadataPolicy);
45
376
  }
46
377
  const _exhaustive = element;
47
378
  throw new Error(`Unknown element type: ${JSON.stringify(_exhaustive)}`);
48
379
  }
49
- function canonicalizeField(field) {
380
+ function canonicalizeField(field, metadataPolicy) {
50
381
  switch (field._field) {
51
382
  case "text":
52
- return canonicalizeTextField(field);
383
+ return canonicalizeTextField(field, metadataPolicy);
53
384
  case "number":
54
- return canonicalizeNumberField(field);
385
+ return canonicalizeNumberField(field, metadataPolicy);
55
386
  case "boolean":
56
- return canonicalizeBooleanField(field);
387
+ return canonicalizeBooleanField(field, metadataPolicy);
57
388
  case "enum":
58
- return canonicalizeStaticEnumField(field);
389
+ return canonicalizeStaticEnumField(field, metadataPolicy);
59
390
  case "dynamic_enum":
60
- return canonicalizeDynamicEnumField(field);
391
+ return canonicalizeDynamicEnumField(field, metadataPolicy);
61
392
  case "dynamic_schema":
62
- return canonicalizeDynamicSchemaField(field);
393
+ return canonicalizeDynamicSchemaField(field, metadataPolicy);
63
394
  case "array":
64
- return canonicalizeArrayField(field);
395
+ return canonicalizeArrayField(field, metadataPolicy);
65
396
  case "object":
66
- return canonicalizeObjectField(field);
397
+ return canonicalizeObjectField(field, metadataPolicy);
67
398
  default: {
68
399
  const _exhaustive = field;
69
400
  throw new Error(`Unknown field type: ${JSON.stringify(_exhaustive)}`);
70
401
  }
71
402
  }
72
403
  }
73
- function canonicalizeTextField(field) {
404
+ function canonicalizeTextField(field, metadataPolicy) {
74
405
  const type = { kind: "primitive", primitiveKind: "string" };
75
406
  const constraints = [];
76
407
  if (field.minLength !== void 0) {
@@ -102,13 +433,14 @@ function canonicalizeTextField(field) {
102
433
  }
103
434
  return buildFieldNode(
104
435
  field.name,
436
+ resolveFieldMetadata(field.name, field, metadataPolicy),
105
437
  type,
106
438
  field.required,
107
- buildAnnotations(field.label, field.placeholder),
439
+ buildAnnotations(getExplicitDisplayName(field), field.placeholder),
108
440
  constraints
109
441
  );
110
442
  }
111
- function canonicalizeNumberField(field) {
443
+ function canonicalizeNumberField(field, metadataPolicy) {
112
444
  const type = { kind: "primitive", primitiveKind: "number" };
113
445
  const constraints = [];
114
446
  if (field.min !== void 0) {
@@ -140,17 +472,24 @@ function canonicalizeNumberField(field) {
140
472
  }
141
473
  return buildFieldNode(
142
474
  field.name,
475
+ resolveFieldMetadata(field.name, field, metadataPolicy),
143
476
  type,
144
477
  field.required,
145
- buildAnnotations(field.label),
478
+ buildAnnotations(getExplicitDisplayName(field)),
146
479
  constraints
147
480
  );
148
481
  }
149
- function canonicalizeBooleanField(field) {
482
+ function canonicalizeBooleanField(field, metadataPolicy) {
150
483
  const type = { kind: "primitive", primitiveKind: "boolean" };
151
- return buildFieldNode(field.name, type, field.required, buildAnnotations(field.label));
484
+ return buildFieldNode(
485
+ field.name,
486
+ resolveFieldMetadata(field.name, field, metadataPolicy),
487
+ type,
488
+ field.required,
489
+ buildAnnotations(getExplicitDisplayName(field))
490
+ );
152
491
  }
153
- function canonicalizeStaticEnumField(field) {
492
+ function canonicalizeStaticEnumField(field, metadataPolicy) {
154
493
  const members = field.options.map((opt) => {
155
494
  if (typeof opt === "string") {
156
495
  return { value: opt };
@@ -158,28 +497,46 @@ function canonicalizeStaticEnumField(field) {
158
497
  return { value: opt.id, displayName: opt.label };
159
498
  });
160
499
  const type = { kind: "enum", members };
161
- return buildFieldNode(field.name, type, field.required, buildAnnotations(field.label));
500
+ return buildFieldNode(
501
+ field.name,
502
+ resolveFieldMetadata(field.name, field, metadataPolicy),
503
+ type,
504
+ field.required,
505
+ buildAnnotations(getExplicitDisplayName(field))
506
+ );
162
507
  }
163
- function canonicalizeDynamicEnumField(field) {
508
+ function canonicalizeDynamicEnumField(field, metadataPolicy) {
164
509
  const type = {
165
510
  kind: "dynamic",
166
511
  dynamicKind: "enum",
167
512
  sourceKey: field.source,
168
513
  parameterFields: field.params ? [...field.params] : []
169
514
  };
170
- return buildFieldNode(field.name, type, field.required, buildAnnotations(field.label));
515
+ return buildFieldNode(
516
+ field.name,
517
+ resolveFieldMetadata(field.name, field, metadataPolicy),
518
+ type,
519
+ field.required,
520
+ buildAnnotations(getExplicitDisplayName(field))
521
+ );
171
522
  }
172
- function canonicalizeDynamicSchemaField(field) {
523
+ function canonicalizeDynamicSchemaField(field, metadataPolicy) {
173
524
  const type = {
174
525
  kind: "dynamic",
175
526
  dynamicKind: "schema",
176
527
  sourceKey: field.schemaSource,
177
528
  parameterFields: []
178
529
  };
179
- return buildFieldNode(field.name, type, field.required, buildAnnotations(field.label));
530
+ return buildFieldNode(
531
+ field.name,
532
+ resolveFieldMetadata(field.name, field, metadataPolicy),
533
+ type,
534
+ field.required,
535
+ buildAnnotations(getExplicitDisplayName(field))
536
+ );
180
537
  }
181
- function canonicalizeArrayField(field) {
182
- const itemProperties = buildObjectProperties(field.items);
538
+ function canonicalizeArrayField(field, metadataPolicy) {
539
+ const itemProperties = buildObjectProperties(field.items, metadataPolicy);
183
540
  const itemsType = {
184
541
  kind: "object",
185
542
  properties: itemProperties,
@@ -207,37 +564,44 @@ function canonicalizeArrayField(field) {
207
564
  }
208
565
  return buildFieldNode(
209
566
  field.name,
567
+ resolveFieldMetadata(field.name, field, metadataPolicy),
210
568
  type,
211
569
  field.required,
212
- buildAnnotations(field.label),
570
+ buildAnnotations(getExplicitDisplayName(field)),
213
571
  constraints
214
572
  );
215
573
  }
216
- function canonicalizeObjectField(field) {
217
- const properties = buildObjectProperties(field.properties);
574
+ function canonicalizeObjectField(field, metadataPolicy) {
575
+ const properties = buildObjectProperties(field.properties, metadataPolicy);
218
576
  const type = {
219
577
  kind: "object",
220
578
  properties,
221
579
  additionalProperties: true
222
580
  };
223
- return buildFieldNode(field.name, type, field.required, buildAnnotations(field.label));
581
+ return buildFieldNode(
582
+ field.name,
583
+ resolveFieldMetadata(field.name, field, metadataPolicy),
584
+ type,
585
+ field.required,
586
+ buildAnnotations(getExplicitDisplayName(field))
587
+ );
224
588
  }
225
- function canonicalizeGroup(g) {
589
+ function canonicalizeGroup(g, metadataPolicy) {
226
590
  return {
227
591
  kind: "group",
228
592
  label: g.label,
229
- elements: canonicalizeElements(g.elements),
593
+ elements: canonicalizeElements(g.elements, metadataPolicy),
230
594
  provenance: CHAIN_DSL_PROVENANCE
231
595
  };
232
596
  }
233
- function canonicalizeConditional(c) {
597
+ function canonicalizeConditional(c, metadataPolicy) {
234
598
  return {
235
599
  kind: "conditional",
236
600
  fieldName: c.field,
237
601
  // Conditional values from the chain DSL are JSON-serializable primitives
238
602
  // (strings, numbers, booleans) produced by the `is()` predicate helper.
239
603
  value: assertJsonValue(c.value),
240
- elements: canonicalizeElements(c.elements),
604
+ elements: canonicalizeElements(c.elements, metadataPolicy),
241
605
  provenance: CHAIN_DSL_PROVENANCE
242
606
  };
243
607
  }
@@ -257,10 +621,11 @@ function assertJsonValue(v) {
257
621
  }
258
622
  throw new TypeError(`Conditional value is not a valid JsonValue: ${typeof v}`);
259
623
  }
260
- function buildFieldNode(name, type, required, annotations, constraints = []) {
624
+ function buildFieldNode(name, metadata, type, required, annotations, constraints = []) {
261
625
  return {
262
626
  kind: "field",
263
627
  name,
628
+ ...metadata !== void 0 && { metadata },
264
629
  type,
265
630
  required: required === true,
266
631
  constraints,
@@ -290,13 +655,14 @@ function buildAnnotations(label, placeholder) {
290
655
  }
291
656
  return annotations;
292
657
  }
293
- function buildObjectProperties(elements, insideConditional = false) {
658
+ function buildObjectProperties(elements, metadataPolicy, insideConditional = false) {
294
659
  const properties = [];
295
660
  for (const el of elements) {
296
661
  if (isField(el)) {
297
- const fieldNode = canonicalizeField(el);
662
+ const fieldNode = canonicalizeField(el, metadataPolicy);
298
663
  properties.push({
299
664
  name: fieldNode.name,
665
+ ...fieldNode.metadata !== void 0 && { metadata: fieldNode.metadata },
300
666
  type: fieldNode.type,
301
667
  // Fields inside a conditional branch are always optional in the
302
668
  // data schema, regardless of their `required` flag — the condition
@@ -307,17 +673,35 @@ function buildObjectProperties(elements, insideConditional = false) {
307
673
  provenance: CHAIN_DSL_PROVENANCE
308
674
  });
309
675
  } else if (isGroup(el)) {
310
- properties.push(...buildObjectProperties(el.elements, insideConditional));
676
+ properties.push(...buildObjectProperties(el.elements, metadataPolicy, insideConditional));
311
677
  } else if (isConditional(el)) {
312
- properties.push(...buildObjectProperties(el.elements, true));
678
+ properties.push(...buildObjectProperties(el.elements, metadataPolicy, true));
313
679
  }
314
680
  }
315
681
  return properties;
316
682
  }
683
+ function getExplicitDisplayName(field) {
684
+ if (field.label !== void 0 && field.displayName !== void 0) {
685
+ throw new Error('Chain DSL fields cannot specify both "label" and "displayName".');
686
+ }
687
+ return field.displayName ?? field.label;
688
+ }
689
+ function resolveFieldMetadata(logicalName, field, metadataPolicy) {
690
+ const displayName = getExplicitDisplayName(field);
691
+ return resolveMetadata(
692
+ {
693
+ ...field.apiName !== void 0 && { apiName: field.apiName },
694
+ ...displayName !== void 0 && { displayName }
695
+ },
696
+ getDeclarationMetadataPolicy(metadataPolicy, "field"),
697
+ makeMetadataContext("chain-dsl", "field", logicalName)
698
+ );
699
+ }
317
700
  var CHAIN_DSL_PROVENANCE;
318
701
  var init_chain_dsl_canonicalizer = __esm({
319
702
  "src/canonicalize/chain-dsl-canonicalizer.ts"() {
320
703
  "use strict";
704
+ init_metadata();
321
705
  CHAIN_DSL_PROVENANCE = {
322
706
  surface: "chain-dsl",
323
707
  file: "",
@@ -329,7 +713,7 @@ var init_chain_dsl_canonicalizer = __esm({
329
713
 
330
714
  // src/canonicalize/tsdoc-canonicalizer.ts
331
715
  import { IR_VERSION as IR_VERSION2 } from "@formspec/core/internals";
332
- function canonicalizeTSDoc(analysis, source) {
716
+ function canonicalizeTSDoc(analysis, source, options) {
333
717
  const file = source?.file ?? "";
334
718
  const provenance = {
335
719
  surface: "tsdoc",
@@ -338,15 +722,21 @@ function canonicalizeTSDoc(analysis, source) {
338
722
  column: 0
339
723
  };
340
724
  const elements = assembleElements(analysis.fields, analysis.fieldLayouts, provenance);
341
- return {
725
+ const ir = {
342
726
  kind: "form-ir",
727
+ name: analysis.name,
343
728
  irVersion: IR_VERSION2,
344
729
  elements,
730
+ ...analysis.metadata !== void 0 && { metadata: analysis.metadata },
345
731
  typeRegistry: analysis.typeRegistry,
346
732
  ...analysis.annotations !== void 0 && analysis.annotations.length > 0 && { rootAnnotations: analysis.annotations },
347
733
  ...analysis.annotations !== void 0 && analysis.annotations.length > 0 && { annotations: analysis.annotations },
348
734
  provenance
349
735
  };
736
+ return resolveFormIRMetadata(ir, {
737
+ policy: normalizeMetadataPolicy(options?.metadata),
738
+ surface: "tsdoc"
739
+ });
350
740
  }
351
741
  function assembleElements(fields, layouts, provenance) {
352
742
  const elements = [];
@@ -405,6 +795,7 @@ function wrapInConditional(field, layout, provenance) {
405
795
  var init_tsdoc_canonicalizer = __esm({
406
796
  "src/canonicalize/tsdoc-canonicalizer.ts"() {
407
797
  "use strict";
798
+ init_metadata();
408
799
  }
409
800
  });
410
801
 
@@ -417,6 +808,126 @@ var init_canonicalize = __esm({
417
808
  }
418
809
  });
419
810
 
811
+ // src/metadata/collision-guards.ts
812
+ function assertUniqueSerializedNames(entries, scope) {
813
+ const seen = /* @__PURE__ */ new Map();
814
+ for (const entry of entries) {
815
+ const previous = seen.get(entry.serializedName);
816
+ if (previous !== void 0) {
817
+ if (previous.logicalName === entry.logicalName && previous.category === entry.category) {
818
+ continue;
819
+ }
820
+ throw new Error(
821
+ `Serialized name collision in ${scope}: ${previous.category} "${previous.logicalName}" and ${entry.category} "${entry.logicalName}" both resolve to "${entry.serializedName}".`
822
+ );
823
+ }
824
+ seen.set(entry.serializedName, entry);
825
+ }
826
+ }
827
+ function collectFlattenedFields(elements) {
828
+ const fields = [];
829
+ for (const element of elements) {
830
+ switch (element.kind) {
831
+ case "field":
832
+ fields.push(element);
833
+ break;
834
+ case "group":
835
+ case "conditional":
836
+ fields.push(...collectFlattenedFields(element.elements));
837
+ break;
838
+ default: {
839
+ const exhaustive = element;
840
+ void exhaustive;
841
+ }
842
+ }
843
+ }
844
+ return fields;
845
+ }
846
+ function validateObjectProperties(properties, scope) {
847
+ assertUniqueSerializedNames(
848
+ properties.map((property) => ({
849
+ logicalName: property.name,
850
+ serializedName: getSerializedName(property.name, property.metadata),
851
+ category: "object property"
852
+ })),
853
+ scope
854
+ );
855
+ for (const property of properties) {
856
+ validateTypeNode(
857
+ property.type,
858
+ `${scope}.${getSerializedName(property.name, property.metadata)}`
859
+ );
860
+ }
861
+ }
862
+ function validateTypeNode(type, scope) {
863
+ switch (type.kind) {
864
+ case "array":
865
+ validateTypeNode(type.items, `${scope}[]`);
866
+ break;
867
+ case "object":
868
+ validateObjectProperties(type.properties, scope);
869
+ break;
870
+ case "record":
871
+ validateTypeNode(type.valueType, `${scope}.*`);
872
+ break;
873
+ case "union":
874
+ type.members.forEach((member, index) => {
875
+ validateTypeNode(member, `${scope}|${String(index)}`);
876
+ });
877
+ break;
878
+ case "reference":
879
+ case "primitive":
880
+ case "enum":
881
+ case "dynamic":
882
+ case "custom":
883
+ break;
884
+ default: {
885
+ const exhaustive = type;
886
+ void exhaustive;
887
+ }
888
+ }
889
+ }
890
+ function validateTypeDefinitions(typeRegistry) {
891
+ const definitions = Object.values(typeRegistry);
892
+ assertUniqueSerializedNames(
893
+ definitions.map((definition) => ({
894
+ logicalName: definition.name,
895
+ serializedName: getSerializedName(definition.name, definition.metadata),
896
+ category: "type definition"
897
+ })),
898
+ "$defs"
899
+ );
900
+ for (const definition of definitions) {
901
+ validateTypeDefinition(definition);
902
+ }
903
+ }
904
+ function validateTypeDefinition(definition) {
905
+ validateTypeNode(
906
+ definition.type,
907
+ `type "${getSerializedName(definition.name, definition.metadata)}"`
908
+ );
909
+ }
910
+ function assertNoSerializedNameCollisions(ir) {
911
+ assertUniqueSerializedNames(
912
+ collectFlattenedFields(ir.elements).map((field) => ({
913
+ logicalName: field.name,
914
+ serializedName: getSerializedName(field.name, field.metadata),
915
+ category: "field"
916
+ })),
917
+ "form root"
918
+ );
919
+ for (const field of collectFlattenedFields(ir.elements)) {
920
+ validateTypeNode(field.type, `field "${getSerializedName(field.name, field.metadata)}"`);
921
+ }
922
+ validateTypeDefinitions(ir.typeRegistry);
923
+ }
924
+ var init_collision_guards = __esm({
925
+ "src/metadata/collision-guards.ts"() {
926
+ "use strict";
927
+ init_resolve();
928
+ }
929
+ });
930
+
420
931
  // src/json-schema/ir-generator.ts
421
932
  function makeContext(options) {
422
933
  const vendorPrefix = options?.vendorPrefix ?? "x-formspec";
@@ -427,19 +938,33 @@ function makeContext(options) {
427
938
  }
428
939
  return {
429
940
  defs: {},
941
+ typeNameMap: {},
942
+ typeRegistry: {},
430
943
  extensionRegistry: options?.extensionRegistry,
431
944
  vendorPrefix
432
945
  };
433
946
  }
434
947
  function generateJsonSchemaFromIR(ir, options) {
435
- const ctx = makeContext(options);
948
+ assertNoSerializedNameCollisions(ir);
949
+ const ctx = {
950
+ ...makeContext(options),
951
+ typeRegistry: ir.typeRegistry,
952
+ typeNameMap: Object.fromEntries(
953
+ Object.entries(ir.typeRegistry).map(([name, typeDef]) => [
954
+ name,
955
+ getSerializedName(name, typeDef.metadata)
956
+ ])
957
+ )
958
+ };
436
959
  for (const [name, typeDef] of Object.entries(ir.typeRegistry)) {
437
- ctx.defs[name] = generateTypeNode(typeDef.type, ctx);
960
+ const schemaName = ctx.typeNameMap[name] ?? name;
961
+ ctx.defs[schemaName] = generateTypeNode(typeDef.type, ctx);
962
+ applyResolvedMetadata(ctx.defs[schemaName], typeDef.metadata);
438
963
  if (typeDef.constraints && typeDef.constraints.length > 0) {
439
- applyConstraints(ctx.defs[name], typeDef.constraints, ctx);
964
+ applyConstraints(ctx.defs[schemaName], typeDef.constraints, ctx);
440
965
  }
441
966
  if (typeDef.annotations && typeDef.annotations.length > 0) {
442
- applyAnnotations(ctx.defs[name], typeDef.annotations, ctx);
967
+ applyAnnotations(ctx.defs[schemaName], typeDef.annotations, ctx);
443
968
  }
444
969
  }
445
970
  const properties = {};
@@ -452,6 +977,7 @@ function generateJsonSchemaFromIR(ir, options) {
452
977
  properties,
453
978
  ...uniqueRequired.length > 0 && { required: uniqueRequired }
454
979
  };
980
+ applyResolvedMetadata(result, ir.metadata);
455
981
  if (ir.annotations && ir.annotations.length > 0) {
456
982
  applyAnnotations(result, ir.annotations, ctx);
457
983
  }
@@ -464,9 +990,9 @@ function collectFields(elements, properties, required, ctx) {
464
990
  for (const element of elements) {
465
991
  switch (element.kind) {
466
992
  case "field":
467
- properties[element.name] = generateFieldSchema(element, ctx);
993
+ properties[getSerializedName(element.name, element.metadata)] = generateFieldSchema(element, ctx);
468
994
  if (element.required) {
469
- required.push(element.name);
995
+ required.push(getSerializedName(element.name, element.metadata));
470
996
  }
471
997
  break;
472
998
  case "group":
@@ -510,6 +1036,7 @@ function generateFieldSchema(field, ctx) {
510
1036
  rootAnnotations.push(annotation);
511
1037
  }
512
1038
  }
1039
+ applyResolvedMetadata(schema, field.metadata);
513
1040
  applyAnnotations(schema, rootAnnotations, ctx);
514
1041
  if (itemStringSchema !== void 0) {
515
1042
  applyAnnotations(itemStringSchema, itemAnnotations, ctx);
@@ -517,7 +1044,7 @@ function generateFieldSchema(field, ctx) {
517
1044
  if (pathConstraints.length === 0) {
518
1045
  return schema;
519
1046
  }
520
- return applyPathTargetedConstraints(schema, pathConstraints, ctx);
1047
+ return applyPathTargetedConstraints(schema, pathConstraints, ctx, field.type);
521
1048
  }
522
1049
  function isStringItemConstraint(constraint) {
523
1050
  switch (constraint.constraintKind) {
@@ -529,9 +1056,11 @@ function isStringItemConstraint(constraint) {
529
1056
  return false;
530
1057
  }
531
1058
  }
532
- function applyPathTargetedConstraints(schema, pathConstraints, ctx) {
1059
+ function applyPathTargetedConstraints(schema, pathConstraints, ctx, typeNode) {
533
1060
  if (schema.type === "array" && schema.items) {
534
- schema.items = applyPathTargetedConstraints(schema.items, pathConstraints, ctx);
1061
+ const referencedType = typeNode?.kind === "reference" ? resolveReferencedType(typeNode, ctx) : void 0;
1062
+ const nestedType = typeNode?.kind === "array" ? typeNode.items : referencedType?.kind === "array" ? referencedType.items : void 0;
1063
+ schema.items = applyPathTargetedConstraints(schema.items, pathConstraints, ctx, nestedType);
535
1064
  return schema;
536
1065
  }
537
1066
  const byTarget = /* @__PURE__ */ new Map();
@@ -546,7 +1075,7 @@ function applyPathTargetedConstraints(schema, pathConstraints, ctx) {
546
1075
  for (const [target, constraints] of byTarget) {
547
1076
  const subSchema = {};
548
1077
  applyConstraints(subSchema, constraints, ctx);
549
- propertyOverrides[target] = subSchema;
1078
+ propertyOverrides[resolveSerializedPropertyName(target, typeNode, ctx)] = subSchema;
550
1079
  }
551
1080
  if (schema.$ref) {
552
1081
  const { $ref, ...rest } = schema;
@@ -594,7 +1123,7 @@ function generateTypeNode(type, ctx) {
594
1123
  case "union":
595
1124
  return generateUnionType(type, ctx);
596
1125
  case "reference":
597
- return generateReferenceType(type);
1126
+ return generateReferenceType(type, ctx);
598
1127
  case "dynamic":
599
1128
  return generateDynamicType(type);
600
1129
  case "custom":
@@ -635,9 +1164,10 @@ function generateObjectType(type, ctx) {
635
1164
  const properties = {};
636
1165
  const required = [];
637
1166
  for (const prop of type.properties) {
638
- properties[prop.name] = generatePropertySchema(prop, ctx);
1167
+ const propertyName = getSerializedName(prop.name, prop.metadata);
1168
+ properties[propertyName] = generatePropertySchema(prop, ctx);
639
1169
  if (!prop.optional) {
640
- required.push(prop.name);
1170
+ required.push(propertyName);
641
1171
  }
642
1172
  }
643
1173
  const schema = { type: "object", properties };
@@ -658,6 +1188,7 @@ function generateRecordType(type, ctx) {
658
1188
  function generatePropertySchema(prop, ctx) {
659
1189
  const schema = generateTypeNode(prop.type, ctx);
660
1190
  applyConstraints(schema, prop.constraints, ctx);
1191
+ applyResolvedMetadata(schema, prop.metadata);
661
1192
  applyAnnotations(schema, prop.annotations, ctx);
662
1193
  return schema;
663
1194
  }
@@ -686,8 +1217,28 @@ function isNullableUnion(type) {
686
1217
  ).length;
687
1218
  return nullCount === 1;
688
1219
  }
689
- function generateReferenceType(type) {
690
- return { $ref: `#/$defs/${type.name}` };
1220
+ function generateReferenceType(type, ctx) {
1221
+ return { $ref: `#/$defs/${ctx.typeNameMap[type.name] ?? type.name}` };
1222
+ }
1223
+ function applyResolvedMetadata(schema, metadata) {
1224
+ const displayName = getDisplayName(metadata);
1225
+ if (displayName !== void 0) {
1226
+ schema.title = displayName;
1227
+ }
1228
+ }
1229
+ function resolveReferencedType(type, ctx) {
1230
+ return ctx.typeRegistry[type.name]?.type;
1231
+ }
1232
+ function resolveSerializedPropertyName(logicalName, typeNode, ctx) {
1233
+ if (typeNode?.kind === "object") {
1234
+ const property = typeNode.properties.find((candidate) => candidate.name === logicalName);
1235
+ return property === void 0 ? logicalName : getSerializedName(property.name, property.metadata);
1236
+ }
1237
+ if (typeNode?.kind === "reference") {
1238
+ const referencedType = resolveReferencedType(typeNode, ctx);
1239
+ return referencedType === void 0 ? logicalName : resolveSerializedPropertyName(logicalName, referencedType, ctx);
1240
+ }
1241
+ return logicalName;
691
1242
  }
692
1243
  function generateDynamicType(type) {
693
1244
  if (type.dynamicKind === "enum") {
@@ -767,7 +1318,7 @@ function applyAnnotations(schema, annotations, ctx) {
767
1318
  for (const annotation of annotations) {
768
1319
  switch (annotation.annotationKind) {
769
1320
  case "displayName":
770
- schema.title = annotation.value;
1321
+ schema.title ??= annotation.value;
771
1322
  break;
772
1323
  case "description":
773
1324
  schema.description = annotation.value;
@@ -854,12 +1405,17 @@ function assignVendorPrefixedExtensionKeywords(schema, extensionSchema, vendorPr
854
1405
  var init_ir_generator = __esm({
855
1406
  "src/json-schema/ir-generator.ts"() {
856
1407
  "use strict";
1408
+ init_metadata();
1409
+ init_collision_guards();
857
1410
  }
858
1411
  });
859
1412
 
860
1413
  // src/json-schema/generator.ts
861
1414
  function generateJsonSchema(form, options) {
862
- const ir = canonicalizeChainDSL(form);
1415
+ const ir = canonicalizeChainDSL(
1416
+ form,
1417
+ options?.metadata !== void 0 ? { metadata: options.metadata } : void 0
1418
+ );
863
1419
  const internalOptions = options?.vendorPrefix === void 0 ? void 0 : { vendorPrefix: options.vendorPrefix };
864
1420
  return generateJsonSchemaFromIR(ir, internalOptions);
865
1421
  }
@@ -1042,13 +1598,21 @@ function combineRules(parentRule, childRule) {
1042
1598
  }
1043
1599
  };
1044
1600
  }
1045
- function fieldNodeToControl(field, parentRule) {
1046
- const displayNameAnnotation = field.annotations.find((a) => a.annotationKind === "displayName");
1601
+ function getFieldDisplayName(field) {
1602
+ const resolvedDisplayName = getDisplayName(field.metadata);
1603
+ if (resolvedDisplayName !== void 0) {
1604
+ return resolvedDisplayName;
1605
+ }
1606
+ return field.annotations.find((annotation) => annotation.annotationKind === "displayName")?.value;
1607
+ }
1608
+ function fieldNodeToControl(field, fieldNameMap, parentRule) {
1047
1609
  const placeholderAnnotation = field.annotations.find((a) => a.annotationKind === "placeholder");
1610
+ const serializedName = fieldNameMap.get(field.name) ?? getSerializedName(field.name, field.metadata);
1611
+ const displayName = getFieldDisplayName(field);
1048
1612
  const control = {
1049
1613
  type: "Control",
1050
- scope: fieldToScope(field.name),
1051
- ...displayNameAnnotation !== void 0 && { label: displayNameAnnotation.value },
1614
+ scope: fieldToScope(serializedName),
1615
+ ...displayName !== void 0 && { label: displayName },
1052
1616
  ...placeholderAnnotation !== void 0 && {
1053
1617
  options: { placeholder: placeholderAnnotation.value }
1054
1618
  },
@@ -1056,30 +1620,30 @@ function fieldNodeToControl(field, parentRule) {
1056
1620
  };
1057
1621
  return control;
1058
1622
  }
1059
- function groupNodeToLayout(group, parentRule) {
1623
+ function groupNodeToLayout(group, fieldNameMap, parentRule) {
1060
1624
  return {
1061
1625
  type: "Group",
1062
1626
  label: group.label,
1063
- elements: irElementsToUiSchema(group.elements, parentRule),
1627
+ elements: irElementsToUiSchema(group.elements, fieldNameMap, parentRule),
1064
1628
  ...parentRule !== void 0 && { rule: parentRule }
1065
1629
  };
1066
1630
  }
1067
- function irElementsToUiSchema(elements, parentRule) {
1631
+ function irElementsToUiSchema(elements, fieldNameMap, parentRule) {
1068
1632
  const result = [];
1069
1633
  for (const element of elements) {
1070
1634
  switch (element.kind) {
1071
1635
  case "field": {
1072
- result.push(fieldNodeToControl(element, parentRule));
1636
+ result.push(fieldNodeToControl(element, fieldNameMap, parentRule));
1073
1637
  break;
1074
1638
  }
1075
1639
  case "group": {
1076
- result.push(groupNodeToLayout(element, parentRule));
1640
+ result.push(groupNodeToLayout(element, fieldNameMap, parentRule));
1077
1641
  break;
1078
1642
  }
1079
1643
  case "conditional": {
1080
- const newRule = createShowRule(element.fieldName, element.value);
1644
+ const newRule = createShowRule(fieldNameMap.get(element.fieldName) ?? element.fieldName, element.value);
1081
1645
  const combinedRule = parentRule !== void 0 ? combineRules(parentRule, newRule) : newRule;
1082
- const childElements = irElementsToUiSchema(element.elements, combinedRule);
1646
+ const childElements = irElementsToUiSchema(element.elements, fieldNameMap, combinedRule);
1083
1647
  result.push(...childElements);
1084
1648
  break;
1085
1649
  }
@@ -1093,22 +1657,50 @@ function irElementsToUiSchema(elements, parentRule) {
1093
1657
  return result;
1094
1658
  }
1095
1659
  function generateUiSchemaFromIR(ir) {
1660
+ assertNoSerializedNameCollisions(ir);
1661
+ const fieldNameMap = collectFieldNameMap(ir.elements);
1096
1662
  const result = {
1097
1663
  type: "VerticalLayout",
1098
- elements: irElementsToUiSchema(ir.elements)
1664
+ elements: irElementsToUiSchema(ir.elements, fieldNameMap)
1099
1665
  };
1100
1666
  return parseOrThrow(uiSchema, result, "UI Schema");
1101
1667
  }
1668
+ function collectFieldNameMap(elements) {
1669
+ const map = /* @__PURE__ */ new Map();
1670
+ for (const element of elements) {
1671
+ switch (element.kind) {
1672
+ case "field":
1673
+ map.set(element.name, getSerializedName(element.name, element.metadata));
1674
+ break;
1675
+ case "group":
1676
+ case "conditional":
1677
+ for (const [key, value] of collectFieldNameMap(element.elements)) {
1678
+ map.set(key, value);
1679
+ }
1680
+ break;
1681
+ default: {
1682
+ const _exhaustive = element;
1683
+ void _exhaustive;
1684
+ }
1685
+ }
1686
+ }
1687
+ return map;
1688
+ }
1102
1689
  var init_ir_generator2 = __esm({
1103
1690
  "src/ui-schema/ir-generator.ts"() {
1104
1691
  "use strict";
1692
+ init_metadata();
1693
+ init_collision_guards();
1105
1694
  init_schema();
1106
1695
  }
1107
1696
  });
1108
1697
 
1109
1698
  // src/ui-schema/generator.ts
1110
- function generateUiSchema(form) {
1111
- const ir = canonicalizeChainDSL(form);
1699
+ function generateUiSchema(form, options) {
1700
+ const ir = canonicalizeChainDSL(
1701
+ form,
1702
+ options?.metadata !== void 0 ? { metadata: options.metadata } : void 0
1703
+ );
1112
1704
  return generateUiSchemaFromIR(ir);
1113
1705
  }
1114
1706
  var init_generator2 = __esm({
@@ -2190,7 +2782,76 @@ function makeParseOptions(extensionRegistry, fieldType, checker, subjectType, ho
2190
2782
  ...hostType !== void 0 && { hostType }
2191
2783
  };
2192
2784
  }
2193
- function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry) {
2785
+ function makeExplicitScalarMetadata(value) {
2786
+ return value === void 0 || value === "" ? void 0 : { value, source: "explicit" };
2787
+ }
2788
+ function extractExplicitMetadata(node) {
2789
+ let apiName;
2790
+ let displayName;
2791
+ let apiNamePlural;
2792
+ let displayNamePlural;
2793
+ for (const tag of getLeadingParsedTags(node)) {
2794
+ const value = tag.argumentText.trim();
2795
+ if (value === "") {
2796
+ continue;
2797
+ }
2798
+ if (tag.normalizedTagName === "apiName") {
2799
+ if (tag.target === null) {
2800
+ apiName ??= value;
2801
+ } else if (tag.target.kind === "variant") {
2802
+ if (tag.target.rawText === "singular") {
2803
+ apiName ??= value;
2804
+ } else if (tag.target.rawText === "plural") {
2805
+ apiNamePlural ??= value;
2806
+ }
2807
+ }
2808
+ continue;
2809
+ }
2810
+ if (tag.normalizedTagName === "displayName") {
2811
+ if (tag.target === null) {
2812
+ displayName ??= value;
2813
+ } else if (tag.target.kind === "variant") {
2814
+ if (tag.target.rawText === "singular") {
2815
+ displayName ??= value;
2816
+ } else if (tag.target.rawText === "plural") {
2817
+ displayNamePlural ??= value;
2818
+ }
2819
+ }
2820
+ }
2821
+ }
2822
+ const resolvedApiName = makeExplicitScalarMetadata(apiName);
2823
+ const resolvedDisplayName = makeExplicitScalarMetadata(displayName);
2824
+ const resolvedApiNamePlural = makeExplicitScalarMetadata(apiNamePlural);
2825
+ const resolvedDisplayNamePlural = makeExplicitScalarMetadata(displayNamePlural);
2826
+ const metadata = {
2827
+ ...resolvedApiName !== void 0 && { apiName: resolvedApiName },
2828
+ ...resolvedDisplayName !== void 0 && { displayName: resolvedDisplayName },
2829
+ ...resolvedApiNamePlural !== void 0 && { apiNamePlural: resolvedApiNamePlural },
2830
+ ...resolvedDisplayNamePlural !== void 0 && {
2831
+ displayNamePlural: resolvedDisplayNamePlural
2832
+ }
2833
+ };
2834
+ return Object.keys(metadata).length === 0 ? void 0 : metadata;
2835
+ }
2836
+ function resolveNodeMetadata(metadataPolicy, declarationKind, logicalName, node, buildContext) {
2837
+ const explicit = extractExplicitMetadata(node);
2838
+ return resolveMetadata(
2839
+ {
2840
+ ...explicit?.apiName !== void 0 && { apiName: explicit.apiName.value },
2841
+ ...explicit?.displayName !== void 0 && { displayName: explicit.displayName.value },
2842
+ ...explicit?.apiNamePlural !== void 0 && {
2843
+ apiNamePlural: explicit.apiNamePlural.value
2844
+ },
2845
+ ...explicit?.displayNamePlural !== void 0 && {
2846
+ displayNamePlural: explicit.displayNamePlural.value
2847
+ }
2848
+ },
2849
+ getDeclarationMetadataPolicy(metadataPolicy, declarationKind),
2850
+ makeMetadataContext("tsdoc", declarationKind, logicalName, buildContext)
2851
+ );
2852
+ }
2853
+ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry, metadataPolicy) {
2854
+ const normalizedMetadataPolicy = normalizeMetadataPolicy(metadataPolicy);
2194
2855
  const name = classDecl.name?.text ?? "AnonymousClass";
2195
2856
  const fields = [];
2196
2857
  const fieldLayouts = [];
@@ -2217,6 +2878,7 @@ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry) {
2217
2878
  visiting,
2218
2879
  diagnostics,
2219
2880
  classType,
2881
+ normalizedMetadataPolicy,
2220
2882
  extensionRegistry
2221
2883
  );
2222
2884
  if (fieldNode) {
@@ -2241,10 +2903,18 @@ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry) {
2241
2903
  classType,
2242
2904
  checker,
2243
2905
  file,
2244
- diagnostics
2906
+ diagnostics,
2907
+ normalizedMetadataPolicy
2245
2908
  );
2909
+ const metadata = resolveNodeMetadata(normalizedMetadataPolicy, "type", name, classDecl, {
2910
+ checker,
2911
+ declaration: classDecl,
2912
+ subjectType: classType,
2913
+ hostType: classType
2914
+ });
2246
2915
  return {
2247
2916
  name,
2917
+ ...metadata !== void 0 && { metadata },
2248
2918
  fields: specializedFields,
2249
2919
  fieldLayouts,
2250
2920
  typeRegistry,
@@ -2254,7 +2924,8 @@ function analyzeClassToIR(classDecl, checker, file = "", extensionRegistry) {
2254
2924
  staticMethods
2255
2925
  };
2256
2926
  }
2257
- function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegistry) {
2927
+ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegistry, metadataPolicy) {
2928
+ const normalizedMetadataPolicy = normalizeMetadataPolicy(metadataPolicy);
2258
2929
  const name = interfaceDecl.name.text;
2259
2930
  const fields = [];
2260
2931
  const typeRegistry = {};
@@ -2278,6 +2949,7 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegist
2278
2949
  visiting,
2279
2950
  diagnostics,
2280
2951
  interfaceType,
2952
+ normalizedMetadataPolicy,
2281
2953
  extensionRegistry
2282
2954
  );
2283
2955
  if (fieldNode) {
@@ -2291,11 +2963,19 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegist
2291
2963
  interfaceType,
2292
2964
  checker,
2293
2965
  file,
2294
- diagnostics
2966
+ diagnostics,
2967
+ normalizedMetadataPolicy
2295
2968
  );
2296
2969
  const fieldLayouts = specializedFields.map(() => ({}));
2970
+ const metadata = resolveNodeMetadata(normalizedMetadataPolicy, "type", name, interfaceDecl, {
2971
+ checker,
2972
+ declaration: interfaceDecl,
2973
+ subjectType: interfaceType,
2974
+ hostType: interfaceType
2975
+ });
2297
2976
  return {
2298
2977
  name,
2978
+ ...metadata !== void 0 && { metadata },
2299
2979
  fields: specializedFields,
2300
2980
  fieldLayouts,
2301
2981
  typeRegistry,
@@ -2305,7 +2985,7 @@ function analyzeInterfaceToIR(interfaceDecl, checker, file = "", extensionRegist
2305
2985
  staticMethods: []
2306
2986
  };
2307
2987
  }
2308
- function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry) {
2988
+ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry, metadataPolicy) {
2309
2989
  if (!ts3.isTypeLiteralNode(typeAlias.type)) {
2310
2990
  const sourceFile = typeAlias.getSourceFile();
2311
2991
  const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
@@ -2315,6 +2995,8 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry)
2315
2995
  error: `Type alias "${typeAlias.name.text}" at line ${String(line + 1)} is not an object type literal (found ${kindDesc})`
2316
2996
  };
2317
2997
  }
2998
+ const typeLiteral = typeAlias.type;
2999
+ const normalizedMetadataPolicy = normalizeMetadataPolicy(metadataPolicy);
2318
3000
  const name = typeAlias.name.text;
2319
3001
  const fields = [];
2320
3002
  const typeRegistry = {};
@@ -2328,7 +3010,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry)
2328
3010
  const annotations = [...typeAliasDoc.annotations];
2329
3011
  diagnostics.push(...typeAliasDoc.diagnostics);
2330
3012
  const visiting = /* @__PURE__ */ new Set();
2331
- for (const member of typeAlias.type.members) {
3013
+ for (const member of typeLiteral.members) {
2332
3014
  if (ts3.isPropertySignature(member)) {
2333
3015
  const fieldNode = analyzeInterfacePropertyToIR(
2334
3016
  member,
@@ -2338,6 +3020,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry)
2338
3020
  visiting,
2339
3021
  diagnostics,
2340
3022
  aliasType,
3023
+ normalizedMetadataPolicy,
2341
3024
  extensionRegistry
2342
3025
  );
2343
3026
  if (fieldNode) {
@@ -2351,12 +3034,20 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry)
2351
3034
  aliasType,
2352
3035
  checker,
2353
3036
  file,
2354
- diagnostics
3037
+ diagnostics,
3038
+ normalizedMetadataPolicy
2355
3039
  );
3040
+ const metadata = resolveNodeMetadata(normalizedMetadataPolicy, "type", name, typeAlias, {
3041
+ checker,
3042
+ declaration: typeAlias,
3043
+ subjectType: aliasType,
3044
+ hostType: aliasType
3045
+ });
2356
3046
  return {
2357
3047
  ok: true,
2358
3048
  analysis: {
2359
3049
  name,
3050
+ ...metadata !== void 0 && { metadata },
2360
3051
  fields: specializedFields,
2361
3052
  fieldLayouts: specializedFields.map(() => ({})),
2362
3053
  typeRegistry,
@@ -2396,31 +3087,20 @@ function getLeadingParsedTags(node) {
2396
3087
  }
2397
3088
  return parsedTags;
2398
3089
  }
2399
- function findDiscriminatorProperty(node, fieldName) {
2400
- if (ts3.isClassDeclaration(node)) {
2401
- for (const member of node.members) {
2402
- if (ts3.isPropertyDeclaration(member) && ts3.isIdentifier(member.name) && member.name.text === fieldName) {
2403
- return member;
2404
- }
2405
- }
2406
- return null;
2407
- }
2408
- if (ts3.isInterfaceDeclaration(node)) {
2409
- for (const member of node.members) {
2410
- if (ts3.isPropertySignature(member) && ts3.isIdentifier(member.name) && member.name.text === fieldName) {
2411
- return member;
2412
- }
2413
- }
3090
+ function resolveDiscriminatorProperty(node, checker, fieldName) {
3091
+ const subjectType = checker.getTypeAtLocation(node);
3092
+ const propertySymbol = subjectType.getProperty(fieldName);
3093
+ if (propertySymbol === void 0) {
2414
3094
  return null;
2415
3095
  }
2416
- if (ts3.isTypeLiteralNode(node.type)) {
2417
- for (const member of node.type.members) {
2418
- if (ts3.isPropertySignature(member) && ts3.isIdentifier(member.name) && member.name.text === fieldName) {
2419
- return member;
2420
- }
2421
- }
2422
- }
2423
- return null;
3096
+ const declaration = propertySymbol.valueDeclaration ?? propertySymbol.declarations?.find(
3097
+ (candidate) => ts3.isPropertyDeclaration(candidate) || ts3.isPropertySignature(candidate)
3098
+ ) ?? propertySymbol.declarations?.[0];
3099
+ return {
3100
+ declaration,
3101
+ type: checker.getTypeOfSymbolAtLocation(propertySymbol, declaration ?? node),
3102
+ optional: !!(propertySymbol.flags & ts3.SymbolFlags.Optional) || declaration !== void 0 && "questionToken" in declaration && declaration.questionToken !== void 0
3103
+ };
2424
3104
  }
2425
3105
  function isLocalTypeParameterName(node, typeParameterName) {
2426
3106
  return node.typeParameters?.some((typeParameter) => typeParameter.name.text === typeParameterName) ?? false;
@@ -2513,8 +3193,8 @@ function validateDiscriminatorDirective(node, checker, file, diagnostics) {
2513
3193
  );
2514
3194
  return null;
2515
3195
  }
2516
- const propertyDecl = findDiscriminatorProperty(node, directive.fieldName);
2517
- if (propertyDecl === null) {
3196
+ const property = resolveDiscriminatorProperty(node, checker, directive.fieldName);
3197
+ if (property === null) {
2518
3198
  diagnostics.push(
2519
3199
  makeAnalysisDiagnostic(
2520
3200
  "UNKNOWN_PATH_TARGET",
@@ -2524,36 +3204,35 @@ function validateDiscriminatorDirective(node, checker, file, diagnostics) {
2524
3204
  );
2525
3205
  return null;
2526
3206
  }
2527
- if (propertyDecl.questionToken !== void 0) {
3207
+ if (property.optional) {
2528
3208
  diagnostics.push(
2529
3209
  makeAnalysisDiagnostic(
2530
3210
  "TYPE_MISMATCH",
2531
3211
  `Discriminator field "${directive.fieldName}" must be required; optional discriminator fields are not supported.`,
2532
3212
  directive.provenance,
2533
- [provenanceForNode(propertyDecl, file)]
3213
+ property.declaration !== void 0 ? [provenanceForNode(property.declaration, file)] : []
2534
3214
  )
2535
3215
  );
2536
3216
  return null;
2537
3217
  }
2538
- const propertyType = checker.getTypeAtLocation(propertyDecl);
2539
- if (isNullishSemanticType(propertyType)) {
3218
+ if (isNullishSemanticType(property.type)) {
2540
3219
  diagnostics.push(
2541
3220
  makeAnalysisDiagnostic(
2542
3221
  "TYPE_MISMATCH",
2543
3222
  `Discriminator field "${directive.fieldName}" must not be nullable.`,
2544
3223
  directive.provenance,
2545
- [provenanceForNode(propertyDecl, file)]
3224
+ property.declaration !== void 0 ? [provenanceForNode(property.declaration, file)] : []
2546
3225
  )
2547
3226
  );
2548
3227
  return null;
2549
3228
  }
2550
- if (!isStringLikeSemanticType(propertyType)) {
3229
+ if (!isStringLikeSemanticType(property.type)) {
2551
3230
  diagnostics.push(
2552
3231
  makeAnalysisDiagnostic(
2553
3232
  "TYPE_MISMATCH",
2554
3233
  `Discriminator field "${directive.fieldName}" must be string-like.`,
2555
3234
  directive.provenance,
2556
- [provenanceForNode(propertyDecl, file)]
3235
+ property.declaration !== void 0 ? [provenanceForNode(property.declaration, file)] : []
2557
3236
  )
2558
3237
  );
2559
3238
  return null;
@@ -2574,25 +3253,58 @@ function getConcreteTypeArgumentForDiscriminator(node, subjectType, checker, typ
2574
3253
  const localTypeParameter = node.typeParameters?.[typeParameterIndex];
2575
3254
  return localTypeParameter === void 0 ? null : checker.getTypeAtLocation(localTypeParameter);
2576
3255
  }
2577
- function extractDeclarationApiName(node) {
2578
- for (const tag of getLeadingParsedTags(node)) {
2579
- if (tag.normalizedTagName !== "apiName") {
2580
- continue;
2581
- }
2582
- if (tag.target === null && tag.argumentText.trim() !== "") {
2583
- return tag.argumentText.trim();
2584
- }
2585
- if (tag.target?.kind === "variant" && tag.target.rawText === "singular") {
2586
- const value = tag.argumentText.trim();
2587
- if (value !== "") {
2588
- return value;
2589
- }
3256
+ function resolveLiteralDiscriminatorPropertyValue(boundType, fieldName, checker, provenance, diagnostics) {
3257
+ const propertySymbol = boundType.getProperty(fieldName);
3258
+ if (propertySymbol === void 0) {
3259
+ return void 0;
3260
+ }
3261
+ const declaration = propertySymbol.valueDeclaration ?? propertySymbol.declarations?.[0];
3262
+ const anchorNode = declaration ?? boundType.symbol.declarations?.[0] ?? null;
3263
+ const resolvedAnchorNode = anchorNode ?? resolveNamedDiscriminatorDeclaration(boundType, checker);
3264
+ if (resolvedAnchorNode === null) {
3265
+ return void 0;
3266
+ }
3267
+ const propertyType = checker.getTypeOfSymbolAtLocation(
3268
+ propertySymbol,
3269
+ resolvedAnchorNode
3270
+ );
3271
+ if (propertyType.isStringLiteral()) {
3272
+ return propertyType.value;
3273
+ }
3274
+ if (propertyType.isUnion()) {
3275
+ const nonNullMembers = propertyType.types.filter(
3276
+ (member) => !(member.flags & (ts3.TypeFlags.Null | ts3.TypeFlags.Undefined))
3277
+ );
3278
+ if (nonNullMembers.length > 0 && nonNullMembers.every((member) => member.isStringLiteral())) {
3279
+ diagnostics.push(
3280
+ makeAnalysisDiagnostic(
3281
+ "INVALID_TAG_ARGUMENT",
3282
+ "Discriminator resolution for union-valued identity properties is out of scope for v1.",
3283
+ provenance
3284
+ )
3285
+ );
3286
+ return null;
2590
3287
  }
2591
3288
  }
2592
- return null;
3289
+ return void 0;
2593
3290
  }
2594
- function inferJsonFacingName(name) {
2595
- return name.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2").replace(/[-\s]+/g, "_").toLowerCase();
3291
+ function resolveDiscriminatorApiName(boundType, checker, metadataPolicy) {
3292
+ const declaration = resolveNamedDiscriminatorDeclaration(boundType, checker);
3293
+ if (declaration === null) {
3294
+ return void 0;
3295
+ }
3296
+ const metadata = resolveNodeMetadata(
3297
+ metadataPolicy,
3298
+ "type",
3299
+ getDiscriminatorLogicalName(boundType, declaration, checker),
3300
+ declaration,
3301
+ {
3302
+ checker,
3303
+ declaration,
3304
+ subjectType: boundType
3305
+ }
3306
+ );
3307
+ return metadata?.apiName;
2596
3308
  }
2597
3309
  function resolveNamedDiscriminatorDeclaration(type, checker, seen = /* @__PURE__ */ new Set()) {
2598
3310
  if (seen.has(type)) {
@@ -2619,7 +3331,7 @@ function resolveNamedDiscriminatorDeclaration(type, checker, seen = /* @__PURE__
2619
3331
  }
2620
3332
  return null;
2621
3333
  }
2622
- function resolveDiscriminatorValue(boundType, checker, provenance, diagnostics) {
3334
+ function resolveDiscriminatorValue(boundType, fieldName, checker, provenance, diagnostics, metadataPolicy) {
2623
3335
  if (boundType === null) {
2624
3336
  diagnostics.push(
2625
3337
  makeAnalysisDiagnostic(
@@ -2648,9 +3360,22 @@ function resolveDiscriminatorValue(boundType, checker, provenance, diagnostics)
2648
3360
  return null;
2649
3361
  }
2650
3362
  }
2651
- const declaration = resolveNamedDiscriminatorDeclaration(boundType, checker);
2652
- if (declaration !== null) {
2653
- return extractDeclarationApiName(declaration) ?? inferJsonFacingName(getDeclarationName(declaration));
3363
+ const literalIdentityValue = resolveLiteralDiscriminatorPropertyValue(
3364
+ boundType,
3365
+ fieldName,
3366
+ checker,
3367
+ provenance,
3368
+ diagnostics
3369
+ );
3370
+ if (literalIdentityValue !== void 0) {
3371
+ return literalIdentityValue;
3372
+ }
3373
+ const apiName = resolveDiscriminatorApiName(boundType, checker, metadataPolicy);
3374
+ if (apiName?.source === "explicit") {
3375
+ return apiName.value;
3376
+ }
3377
+ if (apiName?.source === "inferred") {
3378
+ return apiName.value;
2654
3379
  }
2655
3380
  diagnostics.push(
2656
3381
  makeAnalysisDiagnostic(
@@ -2667,7 +3392,15 @@ function getDeclarationName(node) {
2667
3392
  }
2668
3393
  return "anonymous";
2669
3394
  }
2670
- function applyDeclarationDiscriminatorToFields(fields, node, subjectType, checker, file, diagnostics) {
3395
+ function getResolvedTypeArguments(type) {
3396
+ return (isTypeReference(type) ? type.typeArguments : void 0) ?? type.aliasTypeArguments ?? [];
3397
+ }
3398
+ function getDiscriminatorLogicalName(type, declaration, checker) {
3399
+ const baseName = getDeclarationName(declaration);
3400
+ const typeArguments = getResolvedTypeArguments(type);
3401
+ return typeArguments.length === 0 ? baseName : buildInstantiatedReferenceName(baseName, typeArguments, checker);
3402
+ }
3403
+ function applyDeclarationDiscriminatorToFields(fields, node, subjectType, checker, file, diagnostics, metadataPolicy) {
2671
3404
  const directive = validateDiscriminatorDirective(node, checker, file, diagnostics);
2672
3405
  if (directive === null) {
2673
3406
  return [...fields];
@@ -2679,9 +3412,11 @@ function applyDeclarationDiscriminatorToFields(fields, node, subjectType, checke
2679
3412
  checker,
2680
3413
  directive.typeParameterName
2681
3414
  ),
3415
+ directive.fieldName,
2682
3416
  checker,
2683
3417
  directive.provenance,
2684
- diagnostics
3418
+ diagnostics,
3419
+ metadataPolicy
2685
3420
  );
2686
3421
  if (discriminatorValue === null) {
2687
3422
  return [...fields];
@@ -2702,7 +3437,7 @@ function buildInstantiatedReferenceName(baseName, typeArguments, checker) {
2702
3437
  ).filter((value) => value !== "");
2703
3438
  return renderedArguments.length === 0 ? baseName : `${baseName}__${renderedArguments.join("__")}`;
2704
3439
  }
2705
- function extractReferenceTypeArguments(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry, diagnostics) {
3440
+ function extractReferenceTypeArguments(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy, extensionRegistry, diagnostics) {
2706
3441
  const typeNode = sourceNode === void 0 ? void 0 : extractTypeNodeFromSource(sourceNode);
2707
3442
  if (typeNode === void 0) {
2708
3443
  return [];
@@ -2722,13 +3457,14 @@ function extractReferenceTypeArguments(type, checker, file, typeRegistry, visiti
2722
3457
  typeRegistry,
2723
3458
  visiting,
2724
3459
  argumentNode,
3460
+ metadataPolicy,
2725
3461
  extensionRegistry,
2726
3462
  diagnostics
2727
3463
  )
2728
3464
  };
2729
3465
  });
2730
3466
  }
2731
- function applyDiscriminatorToObjectProperties(properties, node, subjectType, checker, file, diagnostics) {
3467
+ function applyDiscriminatorToObjectProperties(properties, node, subjectType, checker, file, diagnostics, metadataPolicy) {
2732
3468
  const directive = validateDiscriminatorDirective(node, checker, file, diagnostics);
2733
3469
  if (directive === null) {
2734
3470
  return properties;
@@ -2740,9 +3476,11 @@ function applyDiscriminatorToObjectProperties(properties, node, subjectType, che
2740
3476
  checker,
2741
3477
  directive.typeParameterName
2742
3478
  ),
3479
+ directive.fieldName,
2743
3480
  checker,
2744
3481
  directive.provenance,
2745
- diagnostics
3482
+ diagnostics,
3483
+ metadataPolicy
2746
3484
  );
2747
3485
  if (discriminatorValue === null) {
2748
3486
  return properties;
@@ -2757,7 +3495,7 @@ function applyDiscriminatorToObjectProperties(properties, node, subjectType, che
2757
3495
  } : property
2758
3496
  );
2759
3497
  }
2760
- function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, diagnostics, hostType, extensionRegistry) {
3498
+ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, diagnostics, hostType, metadataPolicy, extensionRegistry) {
2761
3499
  if (!ts3.isIdentifier(prop.name)) {
2762
3500
  return null;
2763
3501
  }
@@ -2772,6 +3510,7 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, diagnosti
2772
3510
  typeRegistry,
2773
3511
  visiting,
2774
3512
  prop,
3513
+ metadataPolicy,
2775
3514
  extensionRegistry,
2776
3515
  diagnostics
2777
3516
  );
@@ -2795,9 +3534,16 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, diagnosti
2795
3534
  annotations.push(defaultAnnotation);
2796
3535
  }
2797
3536
  ({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
3537
+ const metadata = resolveNodeMetadata(metadataPolicy, "field", name, prop, {
3538
+ checker,
3539
+ declaration: prop,
3540
+ subjectType: tsType,
3541
+ hostType
3542
+ });
2798
3543
  return {
2799
3544
  kind: "field",
2800
3545
  name,
3546
+ ...metadata !== void 0 && { metadata },
2801
3547
  type,
2802
3548
  required: !optional,
2803
3549
  constraints,
@@ -2805,7 +3551,7 @@ function analyzeFieldToIR(prop, checker, file, typeRegistry, visiting, diagnosti
2805
3551
  provenance
2806
3552
  };
2807
3553
  }
2808
- function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visiting, diagnostics, hostType, extensionRegistry) {
3554
+ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visiting, diagnostics, hostType, metadataPolicy, extensionRegistry) {
2809
3555
  if (!ts3.isIdentifier(prop.name)) {
2810
3556
  return null;
2811
3557
  }
@@ -2820,6 +3566,7 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
2820
3566
  typeRegistry,
2821
3567
  visiting,
2822
3568
  prop,
3569
+ metadataPolicy,
2823
3570
  extensionRegistry,
2824
3571
  diagnostics
2825
3572
  );
@@ -2839,9 +3586,16 @@ function analyzeInterfacePropertyToIR(prop, checker, file, typeRegistry, visitin
2839
3586
  let annotations = [];
2840
3587
  annotations.push(...docResult.annotations);
2841
3588
  ({ type, annotations } = applyEnumMemberDisplayNames(type, annotations));
3589
+ const metadata = resolveNodeMetadata(metadataPolicy, "field", name, prop, {
3590
+ checker,
3591
+ declaration: prop,
3592
+ subjectType: tsType,
3593
+ hostType
3594
+ });
2842
3595
  return {
2843
3596
  kind: "field",
2844
3597
  name,
3598
+ ...metadata !== void 0 && { metadata },
2845
3599
  type,
2846
3600
  required: !optional,
2847
3601
  constraints,
@@ -2966,7 +3720,7 @@ function getTypeNodeRegistrationName(typeNode) {
2966
3720
  }
2967
3721
  return null;
2968
3722
  }
2969
- function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry, diagnostics) {
3723
+ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
2970
3724
  const customType = resolveRegisteredCustomType(sourceNode, extensionRegistry, checker);
2971
3725
  if (customType) {
2972
3726
  return customType;
@@ -2978,6 +3732,7 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
2978
3732
  typeRegistry,
2979
3733
  visiting,
2980
3734
  sourceNode,
3735
+ metadataPolicy,
2981
3736
  extensionRegistry,
2982
3737
  diagnostics
2983
3738
  );
@@ -3022,6 +3777,7 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
3022
3777
  typeRegistry,
3023
3778
  visiting,
3024
3779
  sourceNode,
3780
+ metadataPolicy,
3025
3781
  extensionRegistry,
3026
3782
  diagnostics
3027
3783
  );
@@ -3034,6 +3790,7 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
3034
3790
  typeRegistry,
3035
3791
  visiting,
3036
3792
  sourceNode,
3793
+ metadataPolicy,
3037
3794
  extensionRegistry,
3038
3795
  diagnostics
3039
3796
  );
@@ -3046,13 +3803,14 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
3046
3803
  typeRegistry,
3047
3804
  visiting,
3048
3805
  sourceNode,
3806
+ metadataPolicy,
3049
3807
  extensionRegistry,
3050
3808
  diagnostics
3051
3809
  );
3052
3810
  }
3053
3811
  return { kind: "primitive", primitiveKind: "string" };
3054
3812
  }
3055
- function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry, diagnostics) {
3813
+ function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
3056
3814
  if (!(type.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null))) {
3057
3815
  return null;
3058
3816
  }
@@ -3072,14 +3830,21 @@ function tryResolveNamedPrimitiveAlias(type, checker, file, typeRegistry, visiti
3072
3830
  file,
3073
3831
  makeParseOptions(extensionRegistry)
3074
3832
  );
3833
+ const metadata = resolveNodeMetadata(metadataPolicy, "type", aliasName, aliasDecl, {
3834
+ checker,
3835
+ declaration: aliasDecl,
3836
+ subjectType: aliasType
3837
+ });
3075
3838
  typeRegistry[aliasName] = {
3076
3839
  name: aliasName,
3840
+ ...metadata !== void 0 && { metadata },
3077
3841
  type: resolveAliasedPrimitiveTarget(
3078
3842
  aliasType,
3079
3843
  checker,
3080
3844
  file,
3081
3845
  typeRegistry,
3082
3846
  visiting,
3847
+ metadataPolicy,
3083
3848
  extensionRegistry,
3084
3849
  diagnostics
3085
3850
  ),
@@ -3108,7 +3873,7 @@ function shouldEmitPrimitiveAliasDefinition(typeNode, checker) {
3108
3873
  const resolved = checker.getTypeFromTypeNode(aliasDecl.type);
3109
3874
  return !!(resolved.flags & (ts3.TypeFlags.String | ts3.TypeFlags.Number | ts3.TypeFlags.BigInt | ts3.TypeFlags.BigIntLiteral | ts3.TypeFlags.Boolean | ts3.TypeFlags.Null));
3110
3875
  }
3111
- function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiting, extensionRegistry, diagnostics) {
3876
+ function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiting, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
3112
3877
  const nestedAliasDecl = type.aliasSymbol?.declarations?.find(ts3.isTypeAliasDeclaration);
3113
3878
  if (nestedAliasDecl !== void 0) {
3114
3879
  return resolveAliasedPrimitiveTarget(
@@ -3117,6 +3882,7 @@ function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiti
3117
3882
  file,
3118
3883
  typeRegistry,
3119
3884
  visiting,
3885
+ metadataPolicy,
3120
3886
  extensionRegistry,
3121
3887
  diagnostics
3122
3888
  );
@@ -3128,11 +3894,12 @@ function resolveAliasedPrimitiveTarget(type, checker, file, typeRegistry, visiti
3128
3894
  typeRegistry,
3129
3895
  visiting,
3130
3896
  void 0,
3897
+ metadataPolicy,
3131
3898
  extensionRegistry,
3132
3899
  diagnostics
3133
3900
  );
3134
3901
  }
3135
- function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry, diagnostics) {
3902
+ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
3136
3903
  const typeName = getNamedTypeName(type);
3137
3904
  const namedDecl = getNamedTypeDeclaration(type);
3138
3905
  if (typeName && typeName in typeRegistry) {
@@ -3167,8 +3934,14 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
3167
3934
  return result;
3168
3935
  }
3169
3936
  const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
3937
+ const metadata = namedDecl !== void 0 ? resolveNodeMetadata(metadataPolicy, "type", typeName, namedDecl, {
3938
+ checker,
3939
+ declaration: namedDecl,
3940
+ subjectType: type
3941
+ }) : void 0;
3170
3942
  typeRegistry[typeName] = {
3171
3943
  name: typeName,
3944
+ ...metadata !== void 0 && { metadata },
3172
3945
  type: result,
3173
3946
  ...annotations !== void 0 && annotations.length > 0 && { annotations },
3174
3947
  provenance: provenanceForDeclaration(namedDecl ?? sourceNode, file)
@@ -3222,6 +3995,7 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
3222
3995
  typeRegistry,
3223
3996
  visiting,
3224
3997
  nonNullMembers[0].sourceNode ?? sourceNode,
3998
+ metadataPolicy,
3225
3999
  extensionRegistry,
3226
4000
  diagnostics
3227
4001
  );
@@ -3239,6 +4013,7 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
3239
4013
  typeRegistry,
3240
4014
  visiting,
3241
4015
  memberSourceNode ?? sourceNode,
4016
+ metadataPolicy,
3242
4017
  extensionRegistry,
3243
4018
  diagnostics
3244
4019
  )
@@ -3248,7 +4023,7 @@ function resolveUnionType(type, checker, file, typeRegistry, visiting, sourceNod
3248
4023
  }
3249
4024
  return registerNamed({ kind: "union", members });
3250
4025
  }
3251
- function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry, diagnostics) {
4026
+ function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
3252
4027
  const typeArgs = isTypeReference(type) ? type.typeArguments : void 0;
3253
4028
  const elementType = typeArgs?.[0];
3254
4029
  const elementSourceNode = extractArrayElementTypeNode(sourceNode, checker);
@@ -3259,12 +4034,13 @@ function resolveArrayType(type, checker, file, typeRegistry, visiting, sourceNod
3259
4034
  typeRegistry,
3260
4035
  visiting,
3261
4036
  elementSourceNode,
4037
+ metadataPolicy,
3262
4038
  extensionRegistry,
3263
4039
  diagnostics
3264
4040
  ) : { kind: "primitive", primitiveKind: "string" };
3265
4041
  return { kind: "array", items };
3266
4042
  }
3267
- function tryResolveRecordType(type, checker, file, typeRegistry, visiting, extensionRegistry, diagnostics) {
4043
+ function tryResolveRecordType(type, checker, file, typeRegistry, visiting, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
3268
4044
  if (type.getProperties().length > 0) {
3269
4045
  return null;
3270
4046
  }
@@ -3279,6 +4055,7 @@ function tryResolveRecordType(type, checker, file, typeRegistry, visiting, exten
3279
4055
  typeRegistry,
3280
4056
  visiting,
3281
4057
  void 0,
4058
+ metadataPolicy,
3282
4059
  extensionRegistry,
3283
4060
  diagnostics
3284
4061
  );
@@ -3309,7 +4086,22 @@ function typeNodeContainsReference(type, targetName) {
3309
4086
  }
3310
4087
  }
3311
4088
  }
3312
- function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNode, extensionRegistry, diagnostics) {
4089
+ function shouldEmitResolvedObjectProperty(property, declaration) {
4090
+ if (property.name.startsWith("__@")) {
4091
+ return false;
4092
+ }
4093
+ if (declaration !== void 0 && "name" in declaration && declaration.name !== void 0) {
4094
+ const name = declaration.name;
4095
+ if (ts3.isComputedPropertyName(name) || ts3.isPrivateIdentifier(name)) {
4096
+ return false;
4097
+ }
4098
+ if (!ts3.isIdentifier(name) && !ts3.isStringLiteral(name) && !ts3.isNumericLiteral(name)) {
4099
+ return false;
4100
+ }
4101
+ }
4102
+ return true;
4103
+ }
4104
+ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNode, metadataPolicy = normalizeMetadataPolicy(void 0), extensionRegistry, diagnostics) {
3313
4105
  const collectedDiagnostics = diagnostics ?? [];
3314
4106
  const typeName = getNamedTypeName(type);
3315
4107
  const namedTypeName = typeName ?? void 0;
@@ -3321,6 +4113,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
3321
4113
  typeRegistry,
3322
4114
  visiting,
3323
4115
  sourceNode,
4116
+ metadataPolicy,
3324
4117
  extensionRegistry,
3325
4118
  collectedDiagnostics
3326
4119
  );
@@ -3371,6 +4164,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
3371
4164
  file,
3372
4165
  typeRegistry,
3373
4166
  visiting,
4167
+ metadataPolicy,
3374
4168
  extensionRegistry,
3375
4169
  collectedDiagnostics
3376
4170
  );
@@ -3383,8 +4177,14 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
3383
4177
  return recordNode;
3384
4178
  }
3385
4179
  const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
4180
+ const metadata = namedDecl !== void 0 ? resolveNodeMetadata(metadataPolicy, "type", registryTypeName, namedDecl, {
4181
+ checker,
4182
+ declaration: namedDecl,
4183
+ subjectType: type
4184
+ }) : void 0;
3386
4185
  typeRegistry[registryTypeName] = {
3387
4186
  name: registryTypeName,
4187
+ ...metadata !== void 0 && { metadata },
3388
4188
  type: recordNode,
3389
4189
  ...annotations !== void 0 && annotations.length > 0 && { annotations },
3390
4190
  provenance: provenanceForDeclaration(namedDecl, file)
@@ -3404,12 +4204,14 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
3404
4204
  file,
3405
4205
  typeRegistry,
3406
4206
  visiting,
4207
+ metadataPolicy,
3407
4208
  collectedDiagnostics,
3408
4209
  extensionRegistry
3409
4210
  );
3410
4211
  for (const prop of type.getProperties()) {
3411
4212
  const declaration = prop.valueDeclaration ?? prop.declarations?.[0];
3412
4213
  if (!declaration) continue;
4214
+ if (!shouldEmitResolvedObjectProperty(prop, declaration)) continue;
3413
4215
  const propType = checker.getTypeOfSymbolAtLocation(prop, declaration);
3414
4216
  const optional = !!(prop.flags & ts3.SymbolFlags.Optional);
3415
4217
  const propTypeNode = resolveTypeNode(
@@ -3419,12 +4221,14 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
3419
4221
  typeRegistry,
3420
4222
  visiting,
3421
4223
  declaration,
4224
+ metadataPolicy,
3422
4225
  extensionRegistry,
3423
4226
  collectedDiagnostics
3424
4227
  );
3425
4228
  const fieldNodeInfo = fieldInfoMap?.get(prop.name);
3426
4229
  properties.push({
3427
4230
  name: prop.name,
4231
+ ...fieldNodeInfo?.metadata !== void 0 && { metadata: fieldNodeInfo.metadata },
3428
4232
  type: propTypeNode,
3429
4233
  optional,
3430
4234
  constraints: fieldNodeInfo?.constraints ?? [],
@@ -3441,14 +4245,21 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
3441
4245
  type,
3442
4246
  checker,
3443
4247
  file,
3444
- collectedDiagnostics
4248
+ collectedDiagnostics,
4249
+ metadataPolicy
3445
4250
  ) : properties,
3446
4251
  additionalProperties: true
3447
4252
  };
3448
4253
  if (registryTypeName !== void 0 && shouldRegisterNamedType) {
3449
4254
  const annotations = namedDecl ? extractJSDocAnnotationNodes(namedDecl, file, makeParseOptions(extensionRegistry)) : void 0;
4255
+ const metadata = namedDecl !== void 0 ? resolveNodeMetadata(metadataPolicy, "type", registryTypeName, namedDecl, {
4256
+ checker,
4257
+ declaration: namedDecl,
4258
+ subjectType: type
4259
+ }) : void 0;
3450
4260
  typeRegistry[registryTypeName] = {
3451
4261
  name: registryTypeName,
4262
+ ...metadata !== void 0 && { metadata },
3452
4263
  type: objectNode,
3453
4264
  ...annotations !== void 0 && annotations.length > 0 && { annotations },
3454
4265
  provenance: provenanceForDeclaration(namedDecl, file)
@@ -3461,7 +4272,7 @@ function resolveObjectType(type, checker, file, typeRegistry, visiting, sourceNo
3461
4272
  }
3462
4273
  return objectNode;
3463
4274
  }
3464
- function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visiting, diagnostics, extensionRegistry) {
4275
+ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visiting, metadataPolicy, diagnostics, extensionRegistry) {
3465
4276
  const symbols = [type.getSymbol(), type.aliasSymbol].filter(
3466
4277
  (s) => s?.declarations != null && s.declarations.length > 0
3467
4278
  );
@@ -3482,10 +4293,12 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
3482
4293
  visiting,
3483
4294
  diagnostics,
3484
4295
  hostType,
4296
+ metadataPolicy,
3485
4297
  extensionRegistry
3486
4298
  );
3487
4299
  if (fieldNode) {
3488
4300
  map.set(fieldNode.name, {
4301
+ ...fieldNode.metadata !== void 0 && { metadata: fieldNode.metadata },
3489
4302
  constraints: [...fieldNode.constraints],
3490
4303
  annotations: [...fieldNode.annotations],
3491
4304
  provenance: fieldNode.provenance
@@ -3503,6 +4316,7 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
3503
4316
  file,
3504
4317
  typeRegistry,
3505
4318
  visiting,
4319
+ metadataPolicy,
3506
4320
  checker.getTypeAtLocation(interfaceDecl),
3507
4321
  diagnostics,
3508
4322
  extensionRegistry
@@ -3516,6 +4330,7 @@ function getNamedTypeFieldNodeInfoMap(type, checker, file, typeRegistry, visitin
3516
4330
  file,
3517
4331
  typeRegistry,
3518
4332
  visiting,
4333
+ metadataPolicy,
3519
4334
  checker.getTypeAtLocation(typeAliasDecl),
3520
4335
  diagnostics,
3521
4336
  extensionRegistry
@@ -3567,7 +4382,7 @@ function isNullishTypeNode(typeNode) {
3567
4382
  }
3568
4383
  return ts3.isLiteralTypeNode(typeNode) && (typeNode.literal.kind === ts3.SyntaxKind.NullKeyword || typeNode.literal.kind === ts3.SyntaxKind.UndefinedKeyword);
3569
4384
  }
3570
- function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting, hostType, diagnostics, extensionRegistry) {
4385
+ function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting, metadataPolicy, hostType, diagnostics, extensionRegistry) {
3571
4386
  const map = /* @__PURE__ */ new Map();
3572
4387
  for (const member of members) {
3573
4388
  if (ts3.isPropertySignature(member)) {
@@ -3579,10 +4394,12 @@ function buildFieldNodeInfoMap(members, checker, file, typeRegistry, visiting, h
3579
4394
  visiting,
3580
4395
  diagnostics,
3581
4396
  hostType,
4397
+ metadataPolicy,
3582
4398
  extensionRegistry
3583
4399
  );
3584
4400
  if (fieldNode) {
3585
4401
  map.set(fieldNode.name, {
4402
+ ...fieldNode.metadata !== void 0 && { metadata: fieldNode.metadata },
3586
4403
  constraints: [...fieldNode.constraints],
3587
4404
  annotations: [...fieldNode.annotations],
3588
4405
  provenance: fieldNode.provenance
@@ -3612,6 +4429,7 @@ function extractTypeAliasConstraintNodes(typeNode, checker, file, extensionRegis
3612
4429
  {},
3613
4430
  /* @__PURE__ */ new Set(),
3614
4431
  aliasDecl.type,
4432
+ void 0,
3615
4433
  extensionRegistry
3616
4434
  );
3617
4435
  const constraints = extractJSDocConstraintNodes(
@@ -3721,6 +4539,7 @@ var init_class_analyzer = __esm({
3721
4539
  "use strict";
3722
4540
  init_jsdoc_constraints();
3723
4541
  init_tsdoc_parser();
4542
+ init_metadata();
3724
4543
  RESOLVING_TYPE_PLACEHOLDER = {
3725
4544
  kind: "object",
3726
4545
  properties: [],
@@ -3813,19 +4632,37 @@ function findInterfaceByName(sourceFile, interfaceName) {
3813
4632
  function findTypeAliasByName(sourceFile, aliasName) {
3814
4633
  return findNodeByName(sourceFile, aliasName, ts4.isTypeAliasDeclaration, (n) => n.name.text);
3815
4634
  }
3816
- function analyzeNamedTypeToIR(filePath, typeName, extensionRegistry) {
4635
+ function analyzeNamedTypeToIR(filePath, typeName, extensionRegistry, metadataPolicy) {
3817
4636
  const ctx = createProgramContext(filePath);
3818
- return analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensionRegistry);
4637
+ return analyzeNamedTypeToIRFromProgramContext(
4638
+ ctx,
4639
+ filePath,
4640
+ typeName,
4641
+ extensionRegistry,
4642
+ metadataPolicy
4643
+ );
3819
4644
  }
3820
- function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensionRegistry) {
4645
+ function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensionRegistry, metadataPolicy) {
3821
4646
  const analysisFilePath = path.resolve(filePath);
3822
4647
  const classDecl = findClassByName(ctx.sourceFile, typeName);
3823
4648
  if (classDecl !== null) {
3824
- return analyzeClassToIR(classDecl, ctx.checker, analysisFilePath, extensionRegistry);
4649
+ return analyzeClassToIR(
4650
+ classDecl,
4651
+ ctx.checker,
4652
+ analysisFilePath,
4653
+ extensionRegistry,
4654
+ metadataPolicy
4655
+ );
3825
4656
  }
3826
4657
  const interfaceDecl = findInterfaceByName(ctx.sourceFile, typeName);
3827
4658
  if (interfaceDecl !== null) {
3828
- return analyzeInterfaceToIR(interfaceDecl, ctx.checker, analysisFilePath, extensionRegistry);
4659
+ return analyzeInterfaceToIR(
4660
+ interfaceDecl,
4661
+ ctx.checker,
4662
+ analysisFilePath,
4663
+ extensionRegistry,
4664
+ metadataPolicy
4665
+ );
3829
4666
  }
3830
4667
  const typeAlias = findTypeAliasByName(ctx.sourceFile, typeName);
3831
4668
  if (typeAlias !== null) {
@@ -3833,7 +4670,8 @@ function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensi
3833
4670
  typeAlias,
3834
4671
  ctx.checker,
3835
4672
  analysisFilePath,
3836
- extensionRegistry
4673
+ extensionRegistry,
4674
+ metadataPolicy
3837
4675
  );
3838
4676
  if (result.ok) {
3839
4677
  return result.analysis;
@@ -3948,7 +4786,11 @@ function generateClassSchemas(analysis, source, options) {
3948
4786
  if (errorDiagnostics !== void 0 && errorDiagnostics.length > 0) {
3949
4787
  throw new Error(formatValidationError(errorDiagnostics));
3950
4788
  }
3951
- const ir = canonicalizeTSDoc(analysis, source);
4789
+ const ir = canonicalizeTSDoc(
4790
+ analysis,
4791
+ source,
4792
+ options?.metadata !== void 0 ? { metadata: options.metadata } : void 0
4793
+ );
3952
4794
  const validationResult = validateIR(ir, {
3953
4795
  ...options?.extensionRegistry !== void 0 && {
3954
4796
  extensionRegistry: options.extensionRegistry
@@ -3985,13 +4827,15 @@ function generateSchemasFromClass(options) {
3985
4827
  classDecl,
3986
4828
  ctx.checker,
3987
4829
  options.filePath,
3988
- options.extensionRegistry
4830
+ options.extensionRegistry,
4831
+ options.metadata
3989
4832
  );
3990
4833
  return generateClassSchemas(
3991
4834
  analysis,
3992
4835
  { file: options.filePath },
3993
4836
  {
3994
4837
  extensionRegistry: options.extensionRegistry,
4838
+ metadata: options.metadata,
3995
4839
  vendorPrefix: options.vendorPrefix
3996
4840
  }
3997
4841
  );
@@ -4009,13 +4853,15 @@ function generateSchemasFromProgram(options) {
4009
4853
  ctx,
4010
4854
  options.filePath,
4011
4855
  options.typeName,
4012
- options.extensionRegistry
4856
+ options.extensionRegistry,
4857
+ options.metadata
4013
4858
  );
4014
4859
  return generateClassSchemas(
4015
4860
  analysis,
4016
4861
  { file: options.filePath },
4017
4862
  {
4018
4863
  extensionRegistry: options.extensionRegistry,
4864
+ metadata: options.metadata,
4019
4865
  vendorPrefix: options.vendorPrefix
4020
4866
  }
4021
4867
  );
@@ -4035,16 +4881,28 @@ var init_class_schema = __esm({
4035
4881
  // src/generators/mixed-authoring.ts
4036
4882
  function buildMixedAuthoringSchemas(options) {
4037
4883
  const { filePath, typeName, overlays, ...schemaOptions } = options;
4038
- const analysis = analyzeNamedTypeToIR(filePath, typeName, schemaOptions.extensionRegistry);
4039
- const composedAnalysis = composeAnalysisWithOverlays(analysis, overlays);
4040
- const ir = canonicalizeTSDoc(composedAnalysis, { file: filePath });
4884
+ const analysis = analyzeNamedTypeToIR(
4885
+ filePath,
4886
+ typeName,
4887
+ schemaOptions.extensionRegistry,
4888
+ schemaOptions.metadata
4889
+ );
4890
+ const composedAnalysis = composeAnalysisWithOverlays(analysis, overlays, schemaOptions.metadata);
4891
+ const ir = canonicalizeTSDoc(
4892
+ composedAnalysis,
4893
+ { file: filePath },
4894
+ schemaOptions.metadata !== void 0 ? { metadata: schemaOptions.metadata } : void 0
4895
+ );
4041
4896
  return {
4042
4897
  jsonSchema: generateJsonSchemaFromIR(ir, schemaOptions),
4043
4898
  uiSchema: generateUiSchemaFromIR(ir)
4044
4899
  };
4045
4900
  }
4046
- function composeAnalysisWithOverlays(analysis, overlays) {
4047
- const overlayIR = canonicalizeChainDSL(overlays);
4901
+ function composeAnalysisWithOverlays(analysis, overlays, metadata) {
4902
+ const overlayIR = canonicalizeChainDSL(
4903
+ overlays,
4904
+ metadata !== void 0 ? { metadata } : void 0
4905
+ );
4048
4906
  const overlayFields = collectOverlayFields(overlayIR.elements);
4049
4907
  if (overlayFields.length === 0) {
4050
4908
  return analysis;
@@ -4100,8 +4958,10 @@ function collectOverlayFields(elements) {
4100
4958
  }
4101
4959
  function mergeFieldOverlay(baseField, overlayField, typeRegistry) {
4102
4960
  assertSupportedOverlayField(baseField, overlayField);
4961
+ const metadata = mergeResolvedMetadata(baseField.metadata, overlayField.metadata);
4103
4962
  return {
4104
4963
  ...baseField,
4964
+ ...metadata !== void 0 && { metadata },
4105
4965
  type: mergeFieldType(baseField, overlayField, typeRegistry),
4106
4966
  annotations: mergeAnnotations(baseField.annotations, overlayField.annotations)
4107
4967
  };
@@ -4214,6 +5074,7 @@ var init_mixed_authoring = __esm({
4214
5074
  init_ir_generator2();
4215
5075
  init_canonicalize();
4216
5076
  init_program();
5077
+ init_metadata();
4217
5078
  }
4218
5079
  });
4219
5080
 
@@ -4237,12 +5098,12 @@ import * as path2 from "path";
4237
5098
  function buildFormSchemas(form, options) {
4238
5099
  return {
4239
5100
  jsonSchema: generateJsonSchema(form, options),
4240
- uiSchema: generateUiSchema(form)
5101
+ uiSchema: generateUiSchema(form, options)
4241
5102
  };
4242
5103
  }
4243
5104
  function writeSchemas(form, options) {
4244
- const { outDir, name = "schema", indent = 2, vendorPrefix } = options;
4245
- const buildOptions = vendorPrefix === void 0 ? void 0 : { vendorPrefix };
5105
+ const { outDir, name = "schema", indent = 2, vendorPrefix, metadata } = options;
5106
+ const buildOptions = vendorPrefix === void 0 && metadata === void 0 ? void 0 : { vendorPrefix, metadata };
4246
5107
  const { jsonSchema, uiSchema: uiSchema2 } = buildFormSchemas(form, buildOptions);
4247
5108
  if (!fs.existsSync(outDir)) {
4248
5109
  fs.mkdirSync(outDir, { recursive: true });