@formspec/analysis 0.1.0-alpha.20

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 (53) hide show
  1. package/README.md +14 -0
  2. package/dist/__tests__/comment-syntax.test.d.ts +2 -0
  3. package/dist/__tests__/comment-syntax.test.d.ts.map +1 -0
  4. package/dist/__tests__/compiler-signatures.test.d.ts +2 -0
  5. package/dist/__tests__/compiler-signatures.test.d.ts.map +1 -0
  6. package/dist/__tests__/cursor-context.test.d.ts +2 -0
  7. package/dist/__tests__/cursor-context.test.d.ts.map +1 -0
  8. package/dist/__tests__/file-snapshots.test.d.ts +2 -0
  9. package/dist/__tests__/file-snapshots.test.d.ts.map +1 -0
  10. package/dist/__tests__/helpers.d.ts +7 -0
  11. package/dist/__tests__/helpers.d.ts.map +1 -0
  12. package/dist/__tests__/semantic-protocol.test.d.ts +2 -0
  13. package/dist/__tests__/semantic-protocol.test.d.ts.map +1 -0
  14. package/dist/__tests__/semantic-targets.test.d.ts +2 -0
  15. package/dist/__tests__/semantic-targets.test.d.ts.map +1 -0
  16. package/dist/__tests__/tag-registry.test.d.ts +2 -0
  17. package/dist/__tests__/tag-registry.test.d.ts.map +1 -0
  18. package/dist/__tests__/tag-value-parser.test.d.ts +2 -0
  19. package/dist/__tests__/tag-value-parser.test.d.ts.map +1 -0
  20. package/dist/__tests__/ts-binding.test.d.ts +2 -0
  21. package/dist/__tests__/ts-binding.test.d.ts.map +1 -0
  22. package/dist/analysis.d.ts +812 -0
  23. package/dist/comment-syntax.d.ts +43 -0
  24. package/dist/comment-syntax.d.ts.map +1 -0
  25. package/dist/compiler-signatures.d.ts +100 -0
  26. package/dist/compiler-signatures.d.ts.map +1 -0
  27. package/dist/cursor-context.d.ts +89 -0
  28. package/dist/cursor-context.d.ts.map +1 -0
  29. package/dist/file-snapshots.d.ts +18 -0
  30. package/dist/file-snapshots.d.ts.map +1 -0
  31. package/dist/index.cjs +3582 -0
  32. package/dist/index.cjs.map +1 -0
  33. package/dist/index.d.ts +20 -0
  34. package/dist/index.d.ts.map +1 -0
  35. package/dist/index.js +3497 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/path-target.d.ts +11 -0
  38. package/dist/path-target.d.ts.map +1 -0
  39. package/dist/semantic-protocol.d.ts +234 -0
  40. package/dist/semantic-protocol.d.ts.map +1 -0
  41. package/dist/semantic-targets.d.ts +86 -0
  42. package/dist/semantic-targets.d.ts.map +1 -0
  43. package/dist/source-bindings.d.ts +21 -0
  44. package/dist/source-bindings.d.ts.map +1 -0
  45. package/dist/tag-registry.d.ts +46 -0
  46. package/dist/tag-registry.d.ts.map +1 -0
  47. package/dist/tag-value-parser.d.ts +20 -0
  48. package/dist/tag-value-parser.d.ts.map +1 -0
  49. package/dist/ts-binding.d.ts +28 -0
  50. package/dist/ts-binding.d.ts.map +1 -0
  51. package/dist/workspace-runtime.d.ts +15 -0
  52. package/dist/workspace-runtime.d.ts.map +1 -0
  53. package/package.json +42 -0
package/dist/index.cjs ADDED
@@ -0,0 +1,3582 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ FORMSPEC_ANALYSIS_PROTOCOL_VERSION: () => FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
34
+ FORMSPEC_ANALYSIS_SCHEMA_VERSION: () => FORMSPEC_ANALYSIS_SCHEMA_VERSION,
35
+ analyzeConstraintTargets: () => analyzeConstraintTargets,
36
+ buildConstraintTargetStates: () => buildConstraintTargetStates,
37
+ buildFormSpecAnalysisFileSnapshot: () => buildFormSpecAnalysisFileSnapshot,
38
+ buildSyntheticHelperPrelude: () => buildSyntheticHelperPrelude,
39
+ checkSyntheticTagApplication: () => checkSyntheticTagApplication,
40
+ collectCompatiblePathTargets: () => collectCompatiblePathTargets,
41
+ collectReferencedTypeAnnotations: () => collectReferencedTypeAnnotations,
42
+ collectReferencedTypeConstraints: () => collectReferencedTypeConstraints,
43
+ computeFormSpecTextHash: () => computeFormSpecTextHash,
44
+ dereferenceAnalysisType: () => dereferenceAnalysisType,
45
+ extractPathTarget: () => extractPathTarget,
46
+ findCommentTagAtOffset: () => findCommentTagAtOffset,
47
+ findDeclarationForCommentOffset: () => findDeclarationForCommentOffset,
48
+ findEnclosingDocComment: () => findEnclosingDocComment,
49
+ formatConstraintTargetName: () => formatConstraintTargetName,
50
+ formatPathTarget: () => formatPathTarget,
51
+ getAllTagDefinitions: () => getAllTagDefinitions,
52
+ getCommentCompletionContextAtOffset: () => getCommentCompletionContextAtOffset,
53
+ getCommentCursorTargetAtOffset: () => getCommentCursorTargetAtOffset,
54
+ getCommentHoverInfoAtOffset: () => getCommentHoverInfoAtOffset,
55
+ getCommentTagSemanticContext: () => getCommentTagSemanticContext,
56
+ getConstraintTagDefinitions: () => getConstraintTagDefinitions,
57
+ getFormSpecManifestPath: () => getFormSpecManifestPath,
58
+ getFormSpecWorkspaceId: () => getFormSpecWorkspaceId,
59
+ getFormSpecWorkspaceRuntimeDirectory: () => getFormSpecWorkspaceRuntimeDirectory,
60
+ getHostType: () => getHostType,
61
+ getLastLeadingDocCommentRange: () => getLastLeadingDocCommentRange,
62
+ getMatchingTagSignatures: () => getMatchingTagSignatures,
63
+ getSemanticCommentCompletionContextAtOffset: () => getSemanticCommentCompletionContextAtOffset,
64
+ getSubjectType: () => getSubjectType,
65
+ getTagCompletionPrefixAtOffset: () => getTagCompletionPrefixAtOffset,
66
+ getTagDefinition: () => getTagDefinition,
67
+ getTagHoverMarkdown: () => getTagHoverMarkdown,
68
+ getTypeSemanticCapabilities: () => getTypeSemanticCapabilities,
69
+ hasTypeSemanticCapability: () => hasTypeSemanticCapability,
70
+ isFormSpecAnalysisManifest: () => isFormSpecAnalysisManifest,
71
+ isFormSpecSemanticQuery: () => isFormSpecSemanticQuery,
72
+ isFormSpecSemanticResponse: () => isFormSpecSemanticResponse,
73
+ lowerTagApplicationToSyntheticCall: () => lowerTagApplicationToSyntheticCall,
74
+ normalizeFormSpecTagName: () => normalizeFormSpecTagName,
75
+ parseCommentBlock: () => parseCommentBlock,
76
+ parseConstraintTagValue: () => parseConstraintTagValue,
77
+ parseDefaultValueTagValue: () => parseDefaultValueTagValue,
78
+ parseTagSyntax: () => parseTagSyntax,
79
+ resolveConstraintTargetState: () => resolveConstraintTargetState,
80
+ resolveDeclarationPlacement: () => resolveDeclarationPlacement,
81
+ resolvePathTargetType: () => resolvePathTargetType,
82
+ serializeCommentTagSemanticContext: () => serializeCommentTagSemanticContext,
83
+ serializeCommentTargetSpecifier: () => serializeCommentTargetSpecifier,
84
+ serializeCompletionContext: () => serializeCompletionContext,
85
+ serializeHoverInfo: () => serializeHoverInfo,
86
+ serializeParsedCommentTag: () => serializeParsedCommentTag,
87
+ sliceCommentSpan: () => sliceCommentSpan
88
+ });
89
+ module.exports = __toCommonJS(index_exports);
90
+
91
+ // src/path-target.ts
92
+ function extractPathTarget(text) {
93
+ const trimmed = text.trimStart();
94
+ const match = /^:([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)(?:\s+([\s\S]*))?$/u.exec(trimmed);
95
+ if (!match?.[1]) {
96
+ return null;
97
+ }
98
+ return {
99
+ path: { segments: match[1].split(".") },
100
+ remainingText: match[2] ?? ""
101
+ };
102
+ }
103
+ function formatPathTarget(path2) {
104
+ if ("segments" in path2) {
105
+ return path2.segments.join(".");
106
+ }
107
+ return path2.join(".");
108
+ }
109
+
110
+ // src/tag-registry.ts
111
+ var import_core = require("@formspec/core");
112
+ var FIELD_PLACEMENTS = [
113
+ "class-field",
114
+ "interface-field",
115
+ "type-alias-field",
116
+ "variable",
117
+ "function-parameter",
118
+ "method-parameter"
119
+ ];
120
+ var TYPE_PLACEMENTS = [
121
+ "class",
122
+ "interface",
123
+ "type-alias"
124
+ ];
125
+ var DECLARATION_PLACEMENTS = [
126
+ "class-method",
127
+ "function"
128
+ ];
129
+ var ALL_PLACEMENTS = [
130
+ ...TYPE_PLACEMENTS,
131
+ ...FIELD_PLACEMENTS,
132
+ ...DECLARATION_PLACEMENTS
133
+ ];
134
+ var INTEGER_VALUE_TAGS = /* @__PURE__ */ new Set(["minLength", "maxLength", "minItems", "maxItems"]);
135
+ var SIGNED_INTEGER_VALUE_TAGS = /* @__PURE__ */ new Set(["order"]);
136
+ var JSON_VALUE_TAGS = /* @__PURE__ */ new Set(["const", "enumOptions"]);
137
+ var BOOLEAN_VALUE_TAGS = /* @__PURE__ */ new Set(["uniqueItems"]);
138
+ var STRING_VALUE_TAGS = /* @__PURE__ */ new Set([
139
+ "pattern",
140
+ "displayName",
141
+ "description",
142
+ "format",
143
+ "placeholder",
144
+ "group",
145
+ "example",
146
+ "remarks",
147
+ "see",
148
+ "apiName"
149
+ ]);
150
+ var CONDITION_VALUE_TAGS = /* @__PURE__ */ new Set(["showWhen", "hideWhen", "enableWhen", "disableWhen"]);
151
+ var CONSTRAINT_COMPLETION_DETAIL = {
152
+ minimum: "Minimum numeric value (inclusive). Example: `@minimum 0`",
153
+ maximum: "Maximum numeric value (inclusive). Example: `@maximum 100`",
154
+ exclusiveMinimum: "Minimum numeric value (exclusive). Example: `@exclusiveMinimum 0`",
155
+ exclusiveMaximum: "Maximum numeric value (exclusive). Example: `@exclusiveMaximum 100`",
156
+ multipleOf: "Value must be a multiple of this number. Example: `@multipleOf 0.01`",
157
+ minLength: "Minimum string length. Example: `@minLength 1`",
158
+ maxLength: "Maximum string length. Example: `@maxLength 255`",
159
+ minItems: "Minimum number of array items. Example: `@minItems 1`",
160
+ maxItems: "Maximum number of array items. Example: `@maxItems 10`",
161
+ uniqueItems: "Require all array items to be distinct. Example: `@uniqueItems`",
162
+ pattern: "Regular expression pattern for string validation. Example: `@pattern ^[a-z]+$`",
163
+ enumOptions: 'Inline JSON array of allowed enum values. Example: `@enumOptions ["a","b","c"]`',
164
+ const: 'Require a constant JSON value. Example: `@const "USD"`'
165
+ };
166
+ var CONSTRAINT_HOVER_DOCS = {
167
+ minimum: [
168
+ "**@minimum** `<number>`",
169
+ "",
170
+ "Sets an inclusive lower bound on a numeric field.",
171
+ "",
172
+ "Maps to `minimum` in JSON Schema.",
173
+ "",
174
+ "**Signature:** `@minimum [:path] <number>`"
175
+ ].join("\n"),
176
+ maximum: [
177
+ "**@maximum** `<number>`",
178
+ "",
179
+ "Sets an inclusive upper bound on a numeric field.",
180
+ "",
181
+ "Maps to `maximum` in JSON Schema.",
182
+ "",
183
+ "**Signature:** `@maximum [:path] <number>`"
184
+ ].join("\n"),
185
+ exclusiveMinimum: [
186
+ "**@exclusiveMinimum** `<number>`",
187
+ "",
188
+ "Sets an exclusive lower bound on a numeric field.",
189
+ "",
190
+ "Maps to `exclusiveMinimum` in JSON Schema.",
191
+ "",
192
+ "**Signature:** `@exclusiveMinimum [:path] <number>`"
193
+ ].join("\n"),
194
+ exclusiveMaximum: [
195
+ "**@exclusiveMaximum** `<number>`",
196
+ "",
197
+ "Sets an exclusive upper bound on a numeric field.",
198
+ "",
199
+ "Maps to `exclusiveMaximum` in JSON Schema.",
200
+ "",
201
+ "**Signature:** `@exclusiveMaximum [:path] <number>`"
202
+ ].join("\n"),
203
+ multipleOf: [
204
+ "**@multipleOf** `<number>`",
205
+ "",
206
+ "Requires the numeric value to be a multiple of the given number.",
207
+ "",
208
+ "Maps to `multipleOf` in JSON Schema.",
209
+ "",
210
+ "**Signature:** `@multipleOf [:path] <number>`"
211
+ ].join("\n"),
212
+ minLength: [
213
+ "**@minLength** `<integer>`",
214
+ "",
215
+ "Sets a minimum character length on a string field.",
216
+ "",
217
+ "Maps to `minLength` in JSON Schema.",
218
+ "",
219
+ "**Signature:** `@minLength [:path] <integer>`"
220
+ ].join("\n"),
221
+ maxLength: [
222
+ "**@maxLength** `<integer>`",
223
+ "",
224
+ "Sets a maximum character length on a string field.",
225
+ "",
226
+ "Maps to `maxLength` in JSON Schema.",
227
+ "",
228
+ "**Signature:** `@maxLength [:path] <integer>`"
229
+ ].join("\n"),
230
+ minItems: [
231
+ "**@minItems** `<integer>`",
232
+ "",
233
+ "Sets a minimum number of items in an array field.",
234
+ "",
235
+ "Maps to `minItems` in JSON Schema.",
236
+ "",
237
+ "**Signature:** `@minItems [:path] <integer>`"
238
+ ].join("\n"),
239
+ maxItems: [
240
+ "**@maxItems** `<integer>`",
241
+ "",
242
+ "Sets a maximum number of items in an array field.",
243
+ "",
244
+ "Maps to `maxItems` in JSON Schema.",
245
+ "",
246
+ "**Signature:** `@maxItems [:path] <integer>`"
247
+ ].join("\n"),
248
+ uniqueItems: [
249
+ "**@uniqueItems**",
250
+ "",
251
+ "Requires all items in an array field to be distinct.",
252
+ "",
253
+ "Maps to `uniqueItems` in JSON Schema.",
254
+ "",
255
+ "**Signature:** `@uniqueItems [:path]`"
256
+ ].join("\n"),
257
+ pattern: [
258
+ "**@pattern** `<regex>`",
259
+ "",
260
+ "Sets a regular expression pattern that a string field must match.",
261
+ "",
262
+ "Maps to `pattern` in JSON Schema.",
263
+ "",
264
+ "**Signature:** `@pattern [:path] <regex>`"
265
+ ].join("\n"),
266
+ enumOptions: [
267
+ "**@enumOptions** `<json-array>`",
268
+ "",
269
+ "Specifies the allowed values for an enum field as an inline JSON array.",
270
+ "",
271
+ "Maps to `enum` in JSON Schema.",
272
+ "",
273
+ "**Signature:** `@enumOptions <json-array>`"
274
+ ].join("\n"),
275
+ const: [
276
+ "**@const** `<json-literal>`",
277
+ "",
278
+ "Requires the field value to equal a single constant JSON value.",
279
+ "",
280
+ "Maps to `const` in JSON Schema.",
281
+ "",
282
+ "**Signature:** `@const [:path] <json-literal>`"
283
+ ].join("\n")
284
+ };
285
+ function inferValueKind(name) {
286
+ if (INTEGER_VALUE_TAGS.has(name)) return "integer";
287
+ if (SIGNED_INTEGER_VALUE_TAGS.has(name)) return "signedInteger";
288
+ if (JSON_VALUE_TAGS.has(name)) return "json";
289
+ if (BOOLEAN_VALUE_TAGS.has(name)) return "boolean";
290
+ if (STRING_VALUE_TAGS.has(name)) return "string";
291
+ if (CONDITION_VALUE_TAGS.has(name)) return "condition";
292
+ return null;
293
+ }
294
+ function getBuiltinValueKind(name) {
295
+ return inferValueKind(name) ?? "number";
296
+ }
297
+ function getBuiltinConstraintCapability(name) {
298
+ switch (name) {
299
+ case "minimum":
300
+ case "maximum":
301
+ case "exclusiveMinimum":
302
+ case "exclusiveMaximum":
303
+ case "multipleOf":
304
+ return "numeric-comparable";
305
+ case "minLength":
306
+ case "maxLength":
307
+ case "pattern":
308
+ return "string-like";
309
+ case "minItems":
310
+ case "maxItems":
311
+ case "uniqueItems":
312
+ return "array-like";
313
+ case "enumOptions":
314
+ return "enum-member-addressable";
315
+ case "const":
316
+ return "json-like";
317
+ default: {
318
+ const exhaustive = name;
319
+ return exhaustive;
320
+ }
321
+ }
322
+ }
323
+ function capabilitiesForValueKind(valueKind) {
324
+ switch (valueKind) {
325
+ case "number":
326
+ case "integer":
327
+ case "signedInteger":
328
+ return ["numeric-comparable"];
329
+ case "string":
330
+ return ["string-like"];
331
+ case "json":
332
+ return ["json-like"];
333
+ case "condition":
334
+ return ["condition-like"];
335
+ case "boolean":
336
+ case null:
337
+ return [];
338
+ default: {
339
+ const exhaustive = valueKind;
340
+ return exhaustive;
341
+ }
342
+ }
343
+ }
344
+ function valueLabelForKind(valueKind, fallback = "<value>") {
345
+ switch (valueKind) {
346
+ case "number":
347
+ return "<number>";
348
+ case "integer":
349
+ case "signedInteger":
350
+ return "<integer>";
351
+ case "string":
352
+ return "<text>";
353
+ case "json":
354
+ return "<json>";
355
+ case "condition":
356
+ return "<condition>";
357
+ case "boolean":
358
+ case null:
359
+ return "";
360
+ default: {
361
+ const exhaustive = valueKind;
362
+ void exhaustive;
363
+ return fallback;
364
+ }
365
+ }
366
+ }
367
+ function targetLabelForKind(kind) {
368
+ switch (kind) {
369
+ case "path":
370
+ return "[:path]";
371
+ case "member":
372
+ return ":member";
373
+ case "variant":
374
+ return ":variant";
375
+ default: {
376
+ const exhaustive = kind;
377
+ return exhaustive;
378
+ }
379
+ }
380
+ }
381
+ function parameterKindForTarget(targetKind) {
382
+ switch (targetKind) {
383
+ case "path":
384
+ return "target-path";
385
+ case "member":
386
+ return "target-member";
387
+ case "variant":
388
+ return "target-variant";
389
+ default: {
390
+ const exhaustive = targetKind;
391
+ return exhaustive;
392
+ }
393
+ }
394
+ }
395
+ function createTargetParameter(targetKind, valueKind, pathCapability) {
396
+ const base = {
397
+ kind: parameterKindForTarget(targetKind),
398
+ label: targetLabelForKind(targetKind),
399
+ optional: targetKind === "path"
400
+ };
401
+ if (targetKind === "path") {
402
+ const capability = pathCapability ?? capabilitiesForValueKind(valueKind)[0];
403
+ return capability === void 0 ? base : { ...base, capability };
404
+ }
405
+ if (targetKind === "member") {
406
+ return { ...base, capability: "enum-member-addressable" };
407
+ }
408
+ return base;
409
+ }
410
+ function createSignature(name, placements, targetKind, valueKind, valueLabel, pathCapability) {
411
+ const parameters = [];
412
+ if (targetKind !== null) {
413
+ parameters.push(createTargetParameter(targetKind, valueKind, pathCapability));
414
+ }
415
+ if (valueLabel !== "") {
416
+ parameters.push(
417
+ valueKind === null ? {
418
+ kind: "value",
419
+ label: valueLabel
420
+ } : {
421
+ kind: "value",
422
+ label: valueLabel,
423
+ valueKind
424
+ }
425
+ );
426
+ }
427
+ const targetLabel = targetKind === null ? "" : ` ${targetLabelForKind(targetKind)}`;
428
+ const valueLabelSuffix = valueLabel === "" ? "" : ` ${valueLabel}`;
429
+ return {
430
+ label: `@${name}${targetLabel}${valueLabelSuffix}`,
431
+ placements,
432
+ parameters
433
+ };
434
+ }
435
+ function buildHoverMarkdown(name, hoverSummary, signatures, valueLabel) {
436
+ const header = valueLabel === "" ? `**@${name}**` : `**@${name}** \`${valueLabel}\``;
437
+ const signatureLines = signatures.length === 1 ? [`**Signature:** \`${signatures[0]?.label ?? `@${name}`}\``] : ["**Signatures:**", ...signatures.map((signature) => `- \`${signature.label}\``)];
438
+ return [header, "", hoverSummary, "", ...signatureLines].join("\n");
439
+ }
440
+ function makeConstraintSignatures(name) {
441
+ const valueKind = getBuiltinValueKind(name);
442
+ const subjectCapability = getBuiltinConstraintCapability(name);
443
+ const valueLabel = name === "pattern" ? "<regex>" : name === "enumOptions" ? "<json-array>" : name === "const" ? "<json-literal>" : valueLabelForKind(valueKind);
444
+ return [
445
+ createSignature(name, FIELD_PLACEMENTS, null, valueKind, valueLabel),
446
+ createSignature(name, FIELD_PLACEMENTS, "path", valueKind, valueLabel, subjectCapability)
447
+ ];
448
+ }
449
+ var BUILTIN_TAG_DEFINITIONS = Object.fromEntries(
450
+ Object.keys(import_core.BUILTIN_CONSTRAINT_DEFINITIONS).map((name) => {
451
+ const valueKind = getBuiltinValueKind(name);
452
+ const subjectCapability = getBuiltinConstraintCapability(name);
453
+ return [
454
+ name,
455
+ {
456
+ canonicalName: name,
457
+ valueKind,
458
+ requiresArgument: valueKind !== "boolean",
459
+ supportedTargets: ["none", "path"],
460
+ allowDuplicates: false,
461
+ category: "constraint",
462
+ placements: FIELD_PLACEMENTS,
463
+ capabilities: [subjectCapability],
464
+ completionDetail: CONSTRAINT_COMPLETION_DETAIL[name] ?? `@${name}`,
465
+ hoverMarkdown: CONSTRAINT_HOVER_DOCS[name] ?? `**@${name}**`,
466
+ signatures: makeConstraintSignatures(name)
467
+ }
468
+ ];
469
+ })
470
+ );
471
+ var EXTRA_TAG_SPECS = {
472
+ displayName: {
473
+ requiresArgument: true,
474
+ supportedTargets: ["none", "member", "variant"],
475
+ allowDuplicates: false,
476
+ category: "annotation",
477
+ placements: [...TYPE_PLACEMENTS, ...FIELD_PLACEMENTS],
478
+ completionDetail: "Display label for a type, field, or enum member.",
479
+ hoverSummary: "Provides a user-facing display label.",
480
+ valueLabel: "<label>",
481
+ targetPlacements: {
482
+ member: FIELD_PLACEMENTS,
483
+ variant: TYPE_PLACEMENTS
484
+ }
485
+ },
486
+ description: {
487
+ requiresArgument: true,
488
+ supportedTargets: ["none"],
489
+ allowDuplicates: false,
490
+ category: "annotation",
491
+ placements: [...TYPE_PLACEMENTS, ...FIELD_PLACEMENTS],
492
+ completionDetail: "Description text for a type or field.",
493
+ hoverSummary: "Provides descriptive documentation for a type or field."
494
+ },
495
+ format: {
496
+ requiresArgument: true,
497
+ supportedTargets: ["none"],
498
+ allowDuplicates: false,
499
+ category: "annotation",
500
+ placements: FIELD_PLACEMENTS,
501
+ completionDetail: "Format hint for a field.",
502
+ hoverSummary: "Provides a format hint for a field.",
503
+ valueLabel: "<format>"
504
+ },
505
+ placeholder: {
506
+ requiresArgument: true,
507
+ supportedTargets: ["none"],
508
+ allowDuplicates: false,
509
+ category: "annotation",
510
+ placements: FIELD_PLACEMENTS,
511
+ completionDetail: "Placeholder text for a field.",
512
+ hoverSummary: "Provides placeholder text for a field."
513
+ },
514
+ order: {
515
+ requiresArgument: true,
516
+ supportedTargets: ["none"],
517
+ allowDuplicates: false,
518
+ category: "annotation",
519
+ placements: FIELD_PLACEMENTS,
520
+ completionDetail: "Field display order hint.",
521
+ hoverSummary: "Provides an integer ordering hint for UI layout."
522
+ },
523
+ apiName: {
524
+ requiresArgument: true,
525
+ supportedTargets: ["none", "member", "variant"],
526
+ allowDuplicates: false,
527
+ category: "annotation",
528
+ placements: [...TYPE_PLACEMENTS, ...FIELD_PLACEMENTS],
529
+ completionDetail: "API-facing serialized name for a type, field, or variant.",
530
+ hoverSummary: "Overrides the serialized API name used in generated schema output.",
531
+ valueLabel: "<identifier>",
532
+ targetPlacements: {
533
+ member: FIELD_PLACEMENTS,
534
+ variant: TYPE_PLACEMENTS
535
+ }
536
+ },
537
+ group: {
538
+ requiresArgument: true,
539
+ supportedTargets: ["none"],
540
+ allowDuplicates: false,
541
+ category: "structure",
542
+ placements: FIELD_PLACEMENTS,
543
+ completionDetail: "Assigns a field to a UI group.",
544
+ hoverSummary: "Assigns the field to a named grouping container.",
545
+ valueLabel: "<group>"
546
+ },
547
+ showWhen: {
548
+ requiresArgument: true,
549
+ supportedTargets: ["none"],
550
+ allowDuplicates: true,
551
+ category: "structure",
552
+ placements: FIELD_PLACEMENTS,
553
+ completionDetail: "Conditional visibility rule.",
554
+ hoverSummary: "Shows the field only when the condition is satisfied."
555
+ },
556
+ hideWhen: {
557
+ requiresArgument: true,
558
+ supportedTargets: ["none"],
559
+ allowDuplicates: true,
560
+ category: "structure",
561
+ placements: FIELD_PLACEMENTS,
562
+ completionDetail: "Conditional visibility suppression rule.",
563
+ hoverSummary: "Hides the field when the condition is satisfied."
564
+ },
565
+ enableWhen: {
566
+ requiresArgument: true,
567
+ supportedTargets: ["none"],
568
+ allowDuplicates: true,
569
+ category: "structure",
570
+ placements: FIELD_PLACEMENTS,
571
+ completionDetail: "Conditional interactivity rule.",
572
+ hoverSummary: "Enables the field only when the condition is satisfied."
573
+ },
574
+ disableWhen: {
575
+ requiresArgument: true,
576
+ supportedTargets: ["none"],
577
+ allowDuplicates: true,
578
+ category: "structure",
579
+ placements: FIELD_PLACEMENTS,
580
+ completionDetail: "Conditional disablement rule.",
581
+ hoverSummary: "Disables the field when the condition is satisfied."
582
+ },
583
+ defaultValue: {
584
+ requiresArgument: true,
585
+ supportedTargets: ["none"],
586
+ allowDuplicates: false,
587
+ category: "ecosystem",
588
+ placements: FIELD_PLACEMENTS,
589
+ completionDetail: "Default JSON value for a field.",
590
+ hoverSummary: "Provides a default JSON value for ecosystem integrations.",
591
+ valueLabel: "<value>"
592
+ },
593
+ deprecated: {
594
+ requiresArgument: false,
595
+ supportedTargets: ["none"],
596
+ allowDuplicates: false,
597
+ category: "ecosystem",
598
+ placements: ALL_PLACEMENTS,
599
+ completionDetail: "Marks a declaration as deprecated.",
600
+ hoverSummary: "Marks the declaration as deprecated."
601
+ },
602
+ example: {
603
+ requiresArgument: true,
604
+ supportedTargets: ["none"],
605
+ allowDuplicates: true,
606
+ category: "ecosystem",
607
+ placements: [...TYPE_PLACEMENTS, ...FIELD_PLACEMENTS],
608
+ completionDetail: "Example serialized value.",
609
+ hoverSummary: "Provides an example value for documentation and tooling."
610
+ },
611
+ remarks: {
612
+ requiresArgument: true,
613
+ supportedTargets: ["none"],
614
+ allowDuplicates: false,
615
+ category: "ecosystem",
616
+ placements: ALL_PLACEMENTS,
617
+ completionDetail: "Additional remarks text.",
618
+ hoverSummary: "Provides additional remarks for the declaration."
619
+ },
620
+ see: {
621
+ requiresArgument: true,
622
+ supportedTargets: ["none"],
623
+ allowDuplicates: true,
624
+ category: "ecosystem",
625
+ placements: ALL_PLACEMENTS,
626
+ completionDetail: "Reference to related documentation.",
627
+ hoverSummary: "References related documentation or declarations.",
628
+ valueLabel: "<reference>"
629
+ }
630
+ };
631
+ function buildExtraTagDefinition(canonicalName, spec) {
632
+ const valueKind = spec.valueKind ?? inferValueKind(canonicalName);
633
+ const valueLabel = spec.requiresArgument ? spec.valueLabel ?? valueLabelForKind(valueKind) : "";
634
+ const signatures = [];
635
+ if (spec.supportedTargets.includes("none")) {
636
+ signatures.push(createSignature(canonicalName, spec.placements, null, valueKind, valueLabel));
637
+ }
638
+ if (spec.supportedTargets.includes("path")) {
639
+ signatures.push(
640
+ createSignature(
641
+ canonicalName,
642
+ spec.targetPlacements?.path ?? spec.placements,
643
+ "path",
644
+ valueKind,
645
+ valueLabel
646
+ )
647
+ );
648
+ }
649
+ if (spec.supportedTargets.includes("member")) {
650
+ signatures.push(
651
+ createSignature(
652
+ canonicalName,
653
+ spec.targetPlacements?.member ?? spec.placements,
654
+ "member",
655
+ valueKind,
656
+ valueLabel
657
+ )
658
+ );
659
+ }
660
+ if (spec.supportedTargets.includes("variant")) {
661
+ signatures.push(
662
+ createSignature(
663
+ canonicalName,
664
+ spec.targetPlacements?.variant ?? spec.placements,
665
+ "variant",
666
+ valueKind,
667
+ valueLabel
668
+ )
669
+ );
670
+ }
671
+ return {
672
+ canonicalName,
673
+ valueKind,
674
+ requiresArgument: spec.requiresArgument,
675
+ supportedTargets: spec.supportedTargets,
676
+ allowDuplicates: spec.allowDuplicates,
677
+ category: spec.category,
678
+ placements: spec.placements,
679
+ capabilities: capabilitiesForValueKind(valueKind),
680
+ completionDetail: spec.completionDetail,
681
+ hoverMarkdown: buildHoverMarkdown(canonicalName, spec.hoverSummary, signatures, valueLabel),
682
+ signatures
683
+ };
684
+ }
685
+ var EXTRA_TAG_DEFINITIONS = Object.fromEntries(
686
+ Object.entries(EXTRA_TAG_SPECS).map(([canonicalName, spec]) => [
687
+ canonicalName,
688
+ buildExtraTagDefinition(canonicalName, spec)
689
+ ])
690
+ );
691
+ function normalizeFormSpecTagName(rawName) {
692
+ return (0, import_core.normalizeConstraintTagName)(rawName);
693
+ }
694
+ function getTagDefinition(rawName, extensions) {
695
+ const normalized = normalizeFormSpecTagName(rawName);
696
+ const builtin = BUILTIN_TAG_DEFINITIONS[normalized];
697
+ if (builtin !== void 0) {
698
+ return builtin;
699
+ }
700
+ const extra = EXTRA_TAG_DEFINITIONS[normalized];
701
+ if (extra !== void 0) {
702
+ return extra;
703
+ }
704
+ const extensionRegistration = getExtensionConstraintTags(extensions).find(
705
+ (tag) => tag.tagName === normalized
706
+ );
707
+ if (extensionRegistration === void 0) {
708
+ return null;
709
+ }
710
+ return {
711
+ canonicalName: extensionRegistration.tagName,
712
+ valueKind: null,
713
+ requiresArgument: true,
714
+ supportedTargets: ["none"],
715
+ allowDuplicates: true,
716
+ category: "constraint",
717
+ placements: FIELD_PLACEMENTS,
718
+ capabilities: [],
719
+ completionDetail: `Extension constraint tag from ${extensionRegistration.extensionId}`,
720
+ hoverMarkdown: [
721
+ `**@${extensionRegistration.tagName}** \`<value>\``,
722
+ "",
723
+ `Extension-defined constraint tag from \`${extensionRegistration.extensionId}\`.`,
724
+ "",
725
+ `**Signature:** \`@${extensionRegistration.tagName} <value>\``
726
+ ].join("\n"),
727
+ signatures: [
728
+ {
729
+ label: `@${extensionRegistration.tagName} <value>`,
730
+ placements: FIELD_PLACEMENTS,
731
+ parameters: [{ kind: "value", label: "<value>" }]
732
+ }
733
+ ]
734
+ };
735
+ }
736
+ function getConstraintTagDefinitions(extensions) {
737
+ const builtins = Object.values(BUILTIN_TAG_DEFINITIONS);
738
+ const custom = getExtensionConstraintTags(extensions).map((tag) => getTagDefinition(tag.tagName, extensions)).filter((tag) => tag !== null);
739
+ return [...builtins, ...custom];
740
+ }
741
+ function getAllTagDefinitions(extensions) {
742
+ const builtins = Object.values(BUILTIN_TAG_DEFINITIONS);
743
+ const extras = Object.values(EXTRA_TAG_DEFINITIONS);
744
+ const custom = getExtensionConstraintTags(extensions).map((tag) => getTagDefinition(tag.tagName, extensions)).filter((tag) => tag !== null);
745
+ return [...builtins, ...extras, ...custom];
746
+ }
747
+ function getTagHoverMarkdown(rawName, extensions) {
748
+ return getTagDefinition(rawName, extensions)?.hoverMarkdown ?? null;
749
+ }
750
+ function getExtensionConstraintTags(extensions) {
751
+ return extensions?.flatMap((extension) => {
752
+ const tagRecords = extension.constraintTags ?? [];
753
+ return tagRecords.map((tag) => ({
754
+ extensionId: extension.extensionId,
755
+ tagName: tag.tagName
756
+ }));
757
+ }) ?? [];
758
+ }
759
+
760
+ // src/comment-syntax.ts
761
+ function isWhitespace(char) {
762
+ return char === " " || char === " " || char === "\n" || char === "\r";
763
+ }
764
+ function isTagStart(lineText, index) {
765
+ if (lineText[index] !== "@") {
766
+ return false;
767
+ }
768
+ const nextChar = lineText[index + 1];
769
+ if (nextChar === void 0 || !/[A-Za-z]/u.test(nextChar)) {
770
+ return false;
771
+ }
772
+ const previousChar = lineText[index - 1];
773
+ return previousChar === void 0 || isWhitespace(previousChar);
774
+ }
775
+ function findTagEnd(lineText, index) {
776
+ let cursor = index + 1;
777
+ while (cursor < lineText.length && /[A-Za-z0-9]/u.test(lineText[cursor] ?? "")) {
778
+ cursor += 1;
779
+ }
780
+ return cursor;
781
+ }
782
+ function trimTrailingWhitespace(lineText, end) {
783
+ let cursor = end;
784
+ while (cursor > 0 && isWhitespace(lineText[cursor - 1])) {
785
+ cursor -= 1;
786
+ }
787
+ return cursor;
788
+ }
789
+ function spanFromLine(line, start, end, baseOffset) {
790
+ const rawStart = line.rawOffsets[start];
791
+ if (rawStart === void 0) {
792
+ throw new Error(`Invalid projected span start: ${String(start)}`);
793
+ }
794
+ const rawEnd = end >= line.text.length ? line.rawContentEnd : (line.rawOffsets[end - 1] ?? line.rawContentEnd - 1) + 1;
795
+ return {
796
+ start: baseOffset + rawStart,
797
+ end: baseOffset + rawEnd
798
+ };
799
+ }
800
+ function classifyTargetKind(canonicalName, targetText, extensions) {
801
+ if (targetText === "singular" || targetText === "plural") {
802
+ return "variant";
803
+ }
804
+ if (targetText.includes(".")) {
805
+ return "path";
806
+ }
807
+ const definition = getTagDefinition(canonicalName, extensions);
808
+ const supportedTargets = definition?.supportedTargets.filter((target) => target !== "none") ?? [];
809
+ if (supportedTargets.includes("path")) {
810
+ return "path";
811
+ }
812
+ if (supportedTargets.includes("member") && supportedTargets.includes("variant")) {
813
+ return "ambiguous";
814
+ }
815
+ if (supportedTargets.includes("member")) {
816
+ return "member";
817
+ }
818
+ if (supportedTargets.includes("variant")) {
819
+ return "variant";
820
+ }
821
+ return "path";
822
+ }
823
+ function parseTargetSpecifier(line, payloadStart, payloadEnd, canonicalName, baseOffset, extensions) {
824
+ if (payloadStart >= payloadEnd || line.text[payloadStart] !== ":") {
825
+ return null;
826
+ }
827
+ let targetEnd = payloadStart + 1;
828
+ while (targetEnd < payloadEnd && !isWhitespace(line.text[targetEnd])) {
829
+ targetEnd += 1;
830
+ }
831
+ const fullText = line.text.slice(payloadStart, targetEnd);
832
+ const targetText = fullText.slice(1);
833
+ const parsedPath = extractPathTarget(fullText);
834
+ const specifierSpan = spanFromLine(line, payloadStart + 1, targetEnd, baseOffset);
835
+ return {
836
+ rawText: targetText,
837
+ valid: parsedPath !== null && parsedPath.remainingText === "",
838
+ kind: classifyTargetKind(canonicalName, targetText, extensions),
839
+ fullSpan: spanFromLine(line, payloadStart, targetEnd, baseOffset),
840
+ colonSpan: spanFromLine(line, payloadStart, payloadStart + 1, baseOffset),
841
+ span: specifierSpan,
842
+ path: parsedPath?.path ?? null,
843
+ localEnd: targetEnd
844
+ };
845
+ }
846
+ function projectCommentLines(commentText) {
847
+ const projections = [];
848
+ const commentBodyStart = commentText.startsWith("/**") ? 3 : commentText.startsWith("/*") ? 2 : 0;
849
+ const commentBodyEnd = commentText.endsWith("*/") ? commentText.length - 2 : commentText.length;
850
+ let cursor = commentBodyStart;
851
+ while (cursor <= commentBodyEnd) {
852
+ const lineStart = cursor;
853
+ let lineEnd = cursor;
854
+ while (lineEnd < commentBodyEnd && commentText[lineEnd] !== "\n") {
855
+ lineEnd += 1;
856
+ }
857
+ let contentEnd = lineEnd;
858
+ if (contentEnd > lineStart && commentText[contentEnd - 1] === "\r") {
859
+ contentEnd -= 1;
860
+ }
861
+ let contentStart = lineStart;
862
+ while (contentStart < contentEnd && (commentText[contentStart] === " " || commentText[contentStart] === " ")) {
863
+ contentStart += 1;
864
+ }
865
+ if (contentStart < contentEnd && commentText[contentStart] === "*") {
866
+ contentStart += 1;
867
+ while (contentStart < contentEnd && (commentText[contentStart] === " " || commentText[contentStart] === " ")) {
868
+ contentStart += 1;
869
+ }
870
+ }
871
+ const rawOffsets = [];
872
+ let text = "";
873
+ for (let index = contentStart; index < contentEnd; index += 1) {
874
+ rawOffsets.push(index);
875
+ text += commentText[index] ?? "";
876
+ }
877
+ projections.push({
878
+ text,
879
+ rawOffsets,
880
+ rawContentEnd: contentEnd
881
+ });
882
+ if (lineEnd >= commentBodyEnd) {
883
+ break;
884
+ }
885
+ cursor = lineEnd + 1;
886
+ }
887
+ return projections;
888
+ }
889
+ function parseCommentBlock(commentText, options) {
890
+ const tags = [];
891
+ const baseOffset = options?.offset ?? 0;
892
+ for (const line of projectCommentLines(commentText)) {
893
+ const tagStarts = [];
894
+ for (let index = 0; index < line.text.length; index += 1) {
895
+ if (isTagStart(line.text, index)) {
896
+ tagStarts.push(index);
897
+ }
898
+ }
899
+ for (let tagIndex = 0; tagIndex < tagStarts.length; tagIndex += 1) {
900
+ const tagStart = tagStarts[tagIndex];
901
+ if (tagStart === void 0) {
902
+ continue;
903
+ }
904
+ const tagEnd = findTagEnd(line.text, tagStart);
905
+ const nextTagStart = tagStarts[tagIndex + 1] ?? line.text.length;
906
+ const trimmedTagSegmentEnd = trimTrailingWhitespace(line.text, nextTagStart);
907
+ const rawName = line.text.slice(tagStart + 1, tagEnd);
908
+ const canonicalName = normalizeFormSpecTagName(rawName);
909
+ let payloadStart = tagEnd;
910
+ while (payloadStart < trimmedTagSegmentEnd && isWhitespace(line.text[payloadStart])) {
911
+ payloadStart += 1;
912
+ }
913
+ const target = parseTargetSpecifier(
914
+ line,
915
+ payloadStart,
916
+ trimmedTagSegmentEnd,
917
+ canonicalName,
918
+ baseOffset,
919
+ options?.extensions
920
+ );
921
+ let valueStart = payloadStart;
922
+ if (target !== null) {
923
+ valueStart = target.localEnd;
924
+ while (valueStart < trimmedTagSegmentEnd && isWhitespace(line.text[valueStart])) {
925
+ valueStart += 1;
926
+ }
927
+ }
928
+ const payloadSpan = payloadStart < trimmedTagSegmentEnd ? spanFromLine(line, payloadStart, trimmedTagSegmentEnd, baseOffset) : null;
929
+ const valueSpan = valueStart < trimmedTagSegmentEnd ? spanFromLine(line, valueStart, trimmedTagSegmentEnd, baseOffset) : null;
930
+ const parsedTarget = target === null ? null : {
931
+ rawText: target.rawText,
932
+ valid: target.valid,
933
+ kind: target.kind,
934
+ fullSpan: target.fullSpan,
935
+ colonSpan: target.colonSpan,
936
+ span: target.span,
937
+ path: target.path
938
+ };
939
+ tags.push({
940
+ rawTagName: rawName,
941
+ normalizedTagName: canonicalName,
942
+ recognized: getTagDefinition(canonicalName, options?.extensions) !== null,
943
+ fullSpan: spanFromLine(line, tagStart, trimmedTagSegmentEnd, baseOffset),
944
+ tagNameSpan: spanFromLine(line, tagStart, tagEnd, baseOffset),
945
+ payloadSpan,
946
+ colonSpan: parsedTarget?.colonSpan ?? null,
947
+ target: parsedTarget,
948
+ argumentSpan: valueSpan,
949
+ argumentText: valueSpan === null ? "" : commentText.slice(valueSpan.start - baseOffset, valueSpan.end - baseOffset)
950
+ });
951
+ }
952
+ }
953
+ return {
954
+ commentText,
955
+ offset: baseOffset,
956
+ tags
957
+ };
958
+ }
959
+ function parseTagSyntax(rawTagName, payloadText, options) {
960
+ const separator = payloadText === "" || isWhitespace(payloadText[0]) ? "" : " ";
961
+ const parsed = parseCommentBlock(`/** @${rawTagName}${separator}${payloadText} */`, options);
962
+ const [tag] = parsed.tags;
963
+ if (tag === void 0) {
964
+ throw new Error(`Unable to parse synthetic tag syntax for @${rawTagName}`);
965
+ }
966
+ return tag;
967
+ }
968
+ function sliceCommentSpan(commentText, span, options) {
969
+ const baseOffset = options?.offset ?? 0;
970
+ return commentText.slice(span.start - baseOffset, span.end - baseOffset);
971
+ }
972
+
973
+ // src/ts-binding.ts
974
+ var ts = __toESM(require("typescript"), 1);
975
+ function stripNullishUnion(type) {
976
+ if (!type.isUnion()) {
977
+ return type;
978
+ }
979
+ const nonNullish = type.types.filter(
980
+ (member) => (member.flags & (ts.TypeFlags.Null | ts.TypeFlags.Undefined)) === 0
981
+ );
982
+ if (nonNullish.length === 1 && nonNullish[0] !== void 0) {
983
+ return nonNullish[0];
984
+ }
985
+ return type;
986
+ }
987
+ function isIntersectionWithBase(type, predicate) {
988
+ return type.isIntersection() && type.types.some((member) => predicate(member));
989
+ }
990
+ function isNumberLike(type) {
991
+ const stripped = stripNullishUnion(type);
992
+ if (stripped.flags & (ts.TypeFlags.Number | ts.TypeFlags.NumberLiteral | ts.TypeFlags.BigInt | ts.TypeFlags.BigIntLiteral)) {
993
+ return true;
994
+ }
995
+ if (stripped.isUnion()) {
996
+ return stripped.types.every((member) => isNumberLike(member));
997
+ }
998
+ return isIntersectionWithBase(stripped, isNumberLike);
999
+ }
1000
+ function isStringLike(type) {
1001
+ const stripped = stripNullishUnion(type);
1002
+ if (stripped.flags & (ts.TypeFlags.String | ts.TypeFlags.StringLiteral)) {
1003
+ return true;
1004
+ }
1005
+ if (stripped.isUnion()) {
1006
+ return stripped.types.every((member) => isStringLike(member));
1007
+ }
1008
+ return isIntersectionWithBase(stripped, isStringLike);
1009
+ }
1010
+ function isBooleanLike(type) {
1011
+ const stripped = stripNullishUnion(type);
1012
+ if (stripped.flags & (ts.TypeFlags.Boolean | ts.TypeFlags.BooleanLiteral)) {
1013
+ return true;
1014
+ }
1015
+ if (stripped.isUnion()) {
1016
+ return stripped.types.every((member) => isBooleanLike(member));
1017
+ }
1018
+ return isIntersectionWithBase(stripped, isBooleanLike);
1019
+ }
1020
+ function isNullLike(type) {
1021
+ const stripped = stripNullishUnion(type);
1022
+ if (stripped.flags & ts.TypeFlags.Null) {
1023
+ return true;
1024
+ }
1025
+ if (stripped.isUnion()) {
1026
+ return stripped.types.every((member) => isNullLike(member));
1027
+ }
1028
+ return isIntersectionWithBase(stripped, isNullLike);
1029
+ }
1030
+ function isArrayLike(type, checker) {
1031
+ const stripped = stripNullishUnion(type);
1032
+ if (checker.isArrayType(stripped)) {
1033
+ return true;
1034
+ }
1035
+ const symbol = stripped.getSymbol();
1036
+ return symbol?.name === "Array" || symbol?.name === "ReadonlyArray";
1037
+ }
1038
+ function isStringLiteralUnion(type) {
1039
+ const stripped = stripNullishUnion(type);
1040
+ return stripped.isUnion() && stripped.types.length > 0 && stripped.types.every(isStringLike);
1041
+ }
1042
+ function isObjectLike(type, checker) {
1043
+ const stripped = stripNullishUnion(type);
1044
+ return !isArrayLike(stripped, checker) && (stripped.flags & ts.TypeFlags.Object) !== 0;
1045
+ }
1046
+ function isJsonLike(type, checker) {
1047
+ const stripped = stripNullishUnion(type);
1048
+ if (isNullLike(stripped) || isNumberLike(stripped) || isStringLike(stripped) || isBooleanLike(stripped)) {
1049
+ return true;
1050
+ }
1051
+ if (stripped.isUnion()) {
1052
+ return stripped.types.every((member) => isJsonLike(member, checker));
1053
+ }
1054
+ if (isArrayLike(stripped, checker) || isObjectLike(stripped, checker)) {
1055
+ return true;
1056
+ }
1057
+ return isIntersectionWithBase(stripped, (member) => isJsonLike(member, checker));
1058
+ }
1059
+ function getArrayElementType(type, checker) {
1060
+ const stripped = stripNullishUnion(type);
1061
+ if (!checker.isArrayType(stripped)) {
1062
+ return null;
1063
+ }
1064
+ return checker.getTypeArguments(stripped)[0] ?? null;
1065
+ }
1066
+ function resolveDeclarationPlacement(node) {
1067
+ if (ts.isClassDeclaration(node)) return "class";
1068
+ if (ts.isPropertyDeclaration(node)) return "class-field";
1069
+ if (ts.isMethodDeclaration(node)) return "class-method";
1070
+ if (ts.isInterfaceDeclaration(node)) return "interface";
1071
+ if (ts.isPropertySignature(node)) return "interface-field";
1072
+ if (ts.isTypeAliasDeclaration(node)) return "type-alias";
1073
+ if (ts.isVariableDeclaration(node)) return "variable";
1074
+ if (ts.isFunctionDeclaration(node)) return "function";
1075
+ if (ts.isParameter(node)) {
1076
+ if (ts.isMethodDeclaration(node.parent) || ts.isConstructorDeclaration(node.parent)) {
1077
+ return "method-parameter";
1078
+ }
1079
+ return "function-parameter";
1080
+ }
1081
+ return null;
1082
+ }
1083
+ function getTypeSemanticCapabilities(type, checker) {
1084
+ const capabilities = /* @__PURE__ */ new Set();
1085
+ if (isNumberLike(type)) {
1086
+ capabilities.add("numeric-comparable");
1087
+ }
1088
+ if (isStringLike(type)) {
1089
+ capabilities.add("string-like");
1090
+ }
1091
+ if (isJsonLike(type, checker)) {
1092
+ capabilities.add("json-like");
1093
+ }
1094
+ if (isArrayLike(type, checker)) {
1095
+ capabilities.add("array-like");
1096
+ }
1097
+ if (isStringLiteralUnion(type)) {
1098
+ capabilities.add("enum-member-addressable");
1099
+ }
1100
+ if (isObjectLike(type, checker)) {
1101
+ capabilities.add("object-like");
1102
+ }
1103
+ return [...capabilities];
1104
+ }
1105
+ function hasTypeSemanticCapability(type, checker, capability) {
1106
+ return getTypeSemanticCapabilities(type, checker).includes(capability);
1107
+ }
1108
+ function resolvePathTargetType(type, checker, segments) {
1109
+ const stripped = stripNullishUnion(type);
1110
+ if (segments.length === 0) {
1111
+ return {
1112
+ kind: "resolved",
1113
+ type: stripped
1114
+ };
1115
+ }
1116
+ const arrayElementType = getArrayElementType(stripped, checker);
1117
+ if (arrayElementType !== null) {
1118
+ return resolvePathTargetType(arrayElementType, checker, segments);
1119
+ }
1120
+ if ((stripped.flags & ts.TypeFlags.Object) === 0) {
1121
+ return {
1122
+ kind: "unresolvable",
1123
+ type: stripped
1124
+ };
1125
+ }
1126
+ const [segment, ...rest] = segments;
1127
+ if (segment === void 0) {
1128
+ throw new Error("Invariant violation: path traversal requires a segment");
1129
+ }
1130
+ const property = stripped.getProperty(segment);
1131
+ if (property === void 0) {
1132
+ return {
1133
+ kind: "missing-property",
1134
+ segment
1135
+ };
1136
+ }
1137
+ const declaration = property.valueDeclaration ?? property.declarations?.[0];
1138
+ if (declaration === void 0) {
1139
+ return {
1140
+ kind: "unresolvable",
1141
+ type: stripped
1142
+ };
1143
+ }
1144
+ return resolvePathTargetType(
1145
+ checker.getTypeOfSymbolAtLocation(property, declaration),
1146
+ checker,
1147
+ rest
1148
+ );
1149
+ }
1150
+ function collectPropertyPaths(type, checker, capability, prefix, visited) {
1151
+ const stripped = stripNullishUnion(type);
1152
+ if (visited.has(stripped)) {
1153
+ return [];
1154
+ }
1155
+ visited.add(stripped);
1156
+ const suggestions = [];
1157
+ if (hasTypeSemanticCapability(stripped, checker, capability) && prefix.length > 0) {
1158
+ suggestions.push(prefix.join("."));
1159
+ }
1160
+ for (const property of stripped.getProperties()) {
1161
+ const declaration = property.valueDeclaration ?? property.declarations?.[0];
1162
+ if (declaration === void 0) {
1163
+ continue;
1164
+ }
1165
+ const propertyType = checker.getTypeOfSymbolAtLocation(property, declaration);
1166
+ const nextPrefix = [...prefix, property.name];
1167
+ suggestions.push(
1168
+ ...collectPropertyPaths(propertyType, checker, capability, nextPrefix, visited)
1169
+ );
1170
+ }
1171
+ return suggestions;
1172
+ }
1173
+ function collectCompatiblePathTargets(type, checker, capability) {
1174
+ return collectPropertyPaths(type, checker, capability, [], /* @__PURE__ */ new Set());
1175
+ }
1176
+
1177
+ // src/cursor-context.ts
1178
+ function isWordChar(char) {
1179
+ return char !== void 0 && /[A-Za-z0-9]/u.test(char);
1180
+ }
1181
+ function isWhitespaceLike(char) {
1182
+ return char === void 0 || /\s/u.test(char) || char === "*";
1183
+ }
1184
+ function containsOffset(tag, offset) {
1185
+ return offset >= tag.tagNameSpan.start && offset <= tag.tagNameSpan.end;
1186
+ }
1187
+ function filterSignaturesByPlacement(signatures, placement) {
1188
+ if (placement === void 0 || placement === null) {
1189
+ return signatures;
1190
+ }
1191
+ const filtered = signatures.filter((signature) => signature.placements.includes(placement));
1192
+ return filtered.length > 0 ? filtered : signatures;
1193
+ }
1194
+ function getCompatiblePathTargetsForSignatures(signatures, checker, subjectType) {
1195
+ if (checker === void 0 || subjectType === void 0) {
1196
+ return [];
1197
+ }
1198
+ const suggestions = /* @__PURE__ */ new Set();
1199
+ for (const signature of signatures) {
1200
+ for (const parameter of signature.parameters) {
1201
+ if (parameter.kind !== "target-path" || parameter.capability === void 0) {
1202
+ continue;
1203
+ }
1204
+ for (const target of collectCompatiblePathTargets(
1205
+ subjectType,
1206
+ checker,
1207
+ parameter.capability
1208
+ )) {
1209
+ suggestions.add(target);
1210
+ }
1211
+ }
1212
+ }
1213
+ return [...suggestions].sort();
1214
+ }
1215
+ function getSupportedTargets(signatures) {
1216
+ const supportedTargets = /* @__PURE__ */ new Set(["none"]);
1217
+ for (const signature of signatures) {
1218
+ for (const parameter of signature.parameters) {
1219
+ switch (parameter.kind) {
1220
+ case "target-path":
1221
+ supportedTargets.add("path");
1222
+ break;
1223
+ case "target-member":
1224
+ supportedTargets.add("member");
1225
+ break;
1226
+ case "target-variant":
1227
+ supportedTargets.add("variant");
1228
+ break;
1229
+ default:
1230
+ break;
1231
+ }
1232
+ }
1233
+ }
1234
+ return [...supportedTargets];
1235
+ }
1236
+ function getTargetCompletions(signatures, compatiblePathTargets) {
1237
+ const completions = /* @__PURE__ */ new Set();
1238
+ for (const signature of signatures) {
1239
+ for (const parameter of signature.parameters) {
1240
+ switch (parameter.kind) {
1241
+ case "target-path":
1242
+ for (const target of compatiblePathTargets) {
1243
+ completions.add(target);
1244
+ }
1245
+ break;
1246
+ case "target-variant":
1247
+ completions.add("singular");
1248
+ completions.add("plural");
1249
+ break;
1250
+ default:
1251
+ break;
1252
+ }
1253
+ }
1254
+ }
1255
+ return [...completions];
1256
+ }
1257
+ function getCommentTagSemanticContext(tag, options) {
1258
+ const tagDefinition = getTagDefinition(tag.normalizedTagName, options?.extensions);
1259
+ const signatures = filterSignaturesByPlacement(
1260
+ tagDefinition?.signatures ?? [],
1261
+ options?.placement
1262
+ );
1263
+ const compatiblePathTargets = getCompatiblePathTargetsForSignatures(
1264
+ signatures,
1265
+ options?.checker,
1266
+ options?.subjectType
1267
+ );
1268
+ const semantic = {
1269
+ tag,
1270
+ tagDefinition,
1271
+ placement: options?.placement ?? null,
1272
+ signatures,
1273
+ supportedTargets: getSupportedTargets(signatures),
1274
+ targetCompletions: getTargetCompletions(signatures, compatiblePathTargets),
1275
+ compatiblePathTargets,
1276
+ valueLabels: getValueLabels(signatures),
1277
+ tagHoverMarkdown: tagDefinition?.hoverMarkdown ?? null,
1278
+ targetHoverMarkdown: null,
1279
+ argumentHoverMarkdown: null
1280
+ };
1281
+ return {
1282
+ ...semantic,
1283
+ targetHoverMarkdown: buildTargetHoverMarkdown(semantic),
1284
+ argumentHoverMarkdown: buildArgumentHoverMarkdown(semantic)
1285
+ };
1286
+ }
1287
+ function getValueLabels(signatures) {
1288
+ const labels = /* @__PURE__ */ new Set();
1289
+ for (const signature of signatures) {
1290
+ for (const parameter of signature.parameters) {
1291
+ if (parameter.kind === "value") {
1292
+ labels.add(parameter.label);
1293
+ }
1294
+ }
1295
+ }
1296
+ return [...labels];
1297
+ }
1298
+ function getTargetKindLabels(supportedTargets) {
1299
+ const labels = supportedTargets.filter((kind) => kind !== "none").map((kind) => `\`${kind}\``);
1300
+ return labels.length === 0 ? "none" : labels.join(", ");
1301
+ }
1302
+ function buildTargetHoverMarkdown(semantic) {
1303
+ if (semantic.tagDefinition === null) {
1304
+ return null;
1305
+ }
1306
+ const currentTarget = semantic.tag.target?.rawText ?? "";
1307
+ const lines = [
1308
+ `**Target for @${semantic.tagDefinition.canonicalName}**`,
1309
+ "",
1310
+ `Supported target forms: ${getTargetKindLabels(semantic.supportedTargets)}`
1311
+ ];
1312
+ if (currentTarget !== "") {
1313
+ lines.push("", `Current target: \`:${currentTarget}\``);
1314
+ }
1315
+ const MAX_HOVER_PATH_TARGETS = 8;
1316
+ if (semantic.compatiblePathTargets.length > 0) {
1317
+ lines.push("", "**Compatible path targets:**");
1318
+ for (const target of semantic.compatiblePathTargets.slice(0, MAX_HOVER_PATH_TARGETS)) {
1319
+ lines.push(`- \`:${target}\``);
1320
+ }
1321
+ } else if (semantic.supportedTargets.includes("variant")) {
1322
+ lines.push("", "Use `:singular` or `:plural` for variant-specific names.");
1323
+ } else if (semantic.supportedTargets.includes("path")) {
1324
+ lines.push(
1325
+ "",
1326
+ "Type-aware path completions become available when TypeScript binding is provided."
1327
+ );
1328
+ }
1329
+ return lines.join("\n");
1330
+ }
1331
+ function buildArgumentHoverMarkdown(semantic) {
1332
+ if (semantic.tagDefinition === null) {
1333
+ return null;
1334
+ }
1335
+ const valueLabels = getValueLabels(semantic.signatures);
1336
+ const formattedValueLabels = valueLabels.map((label) => `\`${label}\``);
1337
+ const soleSignature = semantic.signatures.length === 1 ? semantic.signatures[0] : void 0;
1338
+ const signatureLines = semantic.signatures.length === 0 ? [] : soleSignature !== void 0 ? [`**Signature:** \`${soleSignature.label}\``] : [
1339
+ "**Signatures:**",
1340
+ ...semantic.signatures.map((signature) => `- \`${signature.label}\``)
1341
+ ];
1342
+ return [
1343
+ `**Argument for @${semantic.tagDefinition.canonicalName}**`,
1344
+ "",
1345
+ `Expected value: ${formattedValueLabels.join(" or ") || "`<value>`"}`,
1346
+ "",
1347
+ ...signatureLines
1348
+ ].join("\n");
1349
+ }
1350
+ function findEnclosingDocComment(documentText, offset, options) {
1351
+ const commentPattern = /\/\*\*[\s\S]*?\*\//gu;
1352
+ for (const match of documentText.matchAll(commentPattern)) {
1353
+ const fullMatch = match[0];
1354
+ const index = match.index;
1355
+ const start = index;
1356
+ const end = start + fullMatch.length;
1357
+ if (offset >= start && offset <= end) {
1358
+ return {
1359
+ text: fullMatch,
1360
+ start,
1361
+ end,
1362
+ parsed: parseCommentBlock(fullMatch, {
1363
+ offset: start,
1364
+ ...options?.extensions !== void 0 ? { extensions: options.extensions } : {}
1365
+ })
1366
+ };
1367
+ }
1368
+ }
1369
+ return null;
1370
+ }
1371
+ function findCommentTagAtOffset(documentText, offset, options) {
1372
+ const comment = findEnclosingDocComment(documentText, offset, options);
1373
+ if (comment === null) {
1374
+ return null;
1375
+ }
1376
+ return comment.parsed.tags.find((tag) => containsOffset(tag, offset)) ?? null;
1377
+ }
1378
+ function getCommentCursorTargetAtOffset(documentText, offset, options) {
1379
+ const comment = findEnclosingDocComment(documentText, offset, options);
1380
+ if (comment === null) {
1381
+ return null;
1382
+ }
1383
+ for (const tag of comment.parsed.tags) {
1384
+ if (containsOffset(tag, offset)) {
1385
+ return {
1386
+ kind: "tag-name",
1387
+ tag
1388
+ };
1389
+ }
1390
+ if (tag.colonSpan !== null && offset >= tag.colonSpan.start && offset <= tag.colonSpan.end) {
1391
+ return {
1392
+ kind: "colon",
1393
+ tag
1394
+ };
1395
+ }
1396
+ if (tag.target !== null && offset >= tag.target.span.start && offset <= tag.target.span.end) {
1397
+ return {
1398
+ kind: "target",
1399
+ tag
1400
+ };
1401
+ }
1402
+ if (tag.argumentSpan !== null && offset >= tag.argumentSpan.start && offset <= tag.argumentSpan.end) {
1403
+ return {
1404
+ kind: "argument",
1405
+ tag
1406
+ };
1407
+ }
1408
+ }
1409
+ return null;
1410
+ }
1411
+ function getTagCompletionPrefixAtOffset(documentText, offset) {
1412
+ const comment = findEnclosingDocComment(documentText, offset);
1413
+ if (comment === null) {
1414
+ return null;
1415
+ }
1416
+ const relativeOffset = offset - comment.start;
1417
+ if (relativeOffset < 0 || relativeOffset > comment.text.length) {
1418
+ return null;
1419
+ }
1420
+ let cursor = relativeOffset;
1421
+ while (cursor > 0 && isWordChar(comment.text[cursor - 1])) {
1422
+ cursor -= 1;
1423
+ }
1424
+ const atIndex = cursor - 1;
1425
+ if (atIndex < 0 || comment.text[atIndex] !== "@") {
1426
+ return null;
1427
+ }
1428
+ const previousChar = atIndex > 0 ? comment.text[atIndex - 1] : void 0;
1429
+ if (!isWhitespaceLike(previousChar)) {
1430
+ return null;
1431
+ }
1432
+ return comment.text.slice(cursor, relativeOffset);
1433
+ }
1434
+ function getCommentCompletionContextAtOffset(documentText, offset, options) {
1435
+ const prefix = getTagCompletionPrefixAtOffset(documentText, offset);
1436
+ if (prefix !== null) {
1437
+ return {
1438
+ kind: "tag-name",
1439
+ prefix
1440
+ };
1441
+ }
1442
+ const target = getCommentCursorTargetAtOffset(documentText, offset, options);
1443
+ if (target?.kind === "target" || target?.kind === "colon") {
1444
+ return {
1445
+ kind: "target",
1446
+ tag: target.tag
1447
+ };
1448
+ }
1449
+ if (target?.kind === "argument") {
1450
+ return {
1451
+ kind: "argument",
1452
+ tag: target.tag
1453
+ };
1454
+ }
1455
+ return {
1456
+ kind: "none"
1457
+ };
1458
+ }
1459
+ function getSemanticCommentCompletionContextAtOffset(documentText, offset, options) {
1460
+ const prefix = getTagCompletionPrefixAtOffset(documentText, offset);
1461
+ if (prefix !== null) {
1462
+ return {
1463
+ kind: "tag-name",
1464
+ prefix,
1465
+ availableTags: getAllTagDefinitions(options?.extensions)
1466
+ };
1467
+ }
1468
+ const target = getCommentCursorTargetAtOffset(
1469
+ documentText,
1470
+ offset,
1471
+ options?.extensions ? { extensions: options.extensions } : void 0
1472
+ );
1473
+ if (target?.kind === "target" || target?.kind === "colon") {
1474
+ return {
1475
+ kind: "target",
1476
+ semantic: getCommentTagSemanticContext(target.tag, options)
1477
+ };
1478
+ }
1479
+ if (target?.kind === "argument") {
1480
+ const semantic = getCommentTagSemanticContext(target.tag, options);
1481
+ return {
1482
+ kind: "argument",
1483
+ semantic,
1484
+ valueLabels: semantic.valueLabels
1485
+ };
1486
+ }
1487
+ return { kind: "none" };
1488
+ }
1489
+ function getCommentHoverInfoAtOffset(documentText, offset, options) {
1490
+ const target = getCommentCursorTargetAtOffset(
1491
+ documentText,
1492
+ offset,
1493
+ options?.extensions ? { extensions: options.extensions } : void 0
1494
+ );
1495
+ if (target === null) {
1496
+ return null;
1497
+ }
1498
+ const semantic = getCommentTagSemanticContext(target.tag, options);
1499
+ let markdown = null;
1500
+ switch (target.kind) {
1501
+ case "tag-name":
1502
+ markdown = semantic.tagHoverMarkdown;
1503
+ break;
1504
+ case "colon":
1505
+ case "target":
1506
+ markdown = semantic.targetHoverMarkdown;
1507
+ break;
1508
+ case "argument":
1509
+ markdown = semantic.argumentHoverMarkdown;
1510
+ break;
1511
+ default: {
1512
+ const exhaustive = target.kind;
1513
+ void exhaustive;
1514
+ break;
1515
+ }
1516
+ }
1517
+ return markdown === null ? null : {
1518
+ kind: target.kind === "colon" ? "target" : target.kind,
1519
+ markdown
1520
+ };
1521
+ }
1522
+
1523
+ // src/tag-value-parser.ts
1524
+ var import_core2 = require("@formspec/core");
1525
+ var NUMERIC_CONSTRAINT_MAP = {
1526
+ minimum: "minimum",
1527
+ maximum: "maximum",
1528
+ exclusiveMinimum: "exclusiveMinimum",
1529
+ exclusiveMaximum: "exclusiveMaximum",
1530
+ multipleOf: "multipleOf"
1531
+ };
1532
+ var LENGTH_CONSTRAINT_MAP = {
1533
+ minLength: "minLength",
1534
+ maxLength: "maxLength",
1535
+ minItems: "minItems",
1536
+ maxItems: "maxItems"
1537
+ };
1538
+ function tryParseJson(text) {
1539
+ try {
1540
+ return JSON.parse(text);
1541
+ } catch {
1542
+ return null;
1543
+ }
1544
+ }
1545
+ function syntaxOptions(registry) {
1546
+ return registry?.extensions !== void 0 ? { extensions: registry.extensions } : void 0;
1547
+ }
1548
+ function parseConstraintTagValue(tagName, text, provenance, options) {
1549
+ const customConstraint = parseExtensionConstraintTagValue(tagName, text, provenance, options);
1550
+ if (customConstraint !== null) {
1551
+ return customConstraint;
1552
+ }
1553
+ if (!(0, import_core2.isBuiltinConstraintName)(tagName)) {
1554
+ return null;
1555
+ }
1556
+ const parsedTag = parseTagSyntax(tagName, text, syntaxOptions(options?.registry));
1557
+ if (parsedTag.target !== null && !parsedTag.target.valid) {
1558
+ return null;
1559
+ }
1560
+ const effectiveText = parsedTag.argumentText;
1561
+ const path2 = parsedTag.target?.path ?? void 0;
1562
+ const expectedType = import_core2.BUILTIN_CONSTRAINT_DEFINITIONS[tagName];
1563
+ if (expectedType !== "boolean" && effectiveText.trim() === "") {
1564
+ return null;
1565
+ }
1566
+ if (expectedType === "number") {
1567
+ const value = Number(effectiveText);
1568
+ if (Number.isNaN(value)) {
1569
+ return null;
1570
+ }
1571
+ const numericKind = NUMERIC_CONSTRAINT_MAP[tagName];
1572
+ if (numericKind !== void 0) {
1573
+ return {
1574
+ kind: "constraint",
1575
+ constraintKind: numericKind,
1576
+ value,
1577
+ ...path2 !== void 0 && { path: path2 },
1578
+ provenance
1579
+ };
1580
+ }
1581
+ const lengthKind = LENGTH_CONSTRAINT_MAP[tagName];
1582
+ if (lengthKind !== void 0) {
1583
+ return {
1584
+ kind: "constraint",
1585
+ constraintKind: lengthKind,
1586
+ value,
1587
+ ...path2 !== void 0 && { path: path2 },
1588
+ provenance
1589
+ };
1590
+ }
1591
+ return null;
1592
+ }
1593
+ if (expectedType === "boolean") {
1594
+ const trimmed = effectiveText.trim();
1595
+ if (trimmed !== "" && trimmed !== "true") {
1596
+ return null;
1597
+ }
1598
+ if (tagName === "uniqueItems") {
1599
+ return {
1600
+ kind: "constraint",
1601
+ constraintKind: "uniqueItems",
1602
+ value: true,
1603
+ ...path2 !== void 0 && { path: path2 },
1604
+ provenance
1605
+ };
1606
+ }
1607
+ return null;
1608
+ }
1609
+ if (expectedType === "json") {
1610
+ if (tagName === "const") {
1611
+ const trimmedText = effectiveText.trim();
1612
+ if (trimmedText === "") {
1613
+ return null;
1614
+ }
1615
+ try {
1616
+ const parsed2 = JSON.parse(trimmedText);
1617
+ return {
1618
+ kind: "constraint",
1619
+ constraintKind: "const",
1620
+ value: parsed2,
1621
+ ...path2 !== void 0 && { path: path2 },
1622
+ provenance
1623
+ };
1624
+ } catch {
1625
+ return {
1626
+ kind: "constraint",
1627
+ constraintKind: "const",
1628
+ value: trimmedText,
1629
+ ...path2 !== void 0 && { path: path2 },
1630
+ provenance
1631
+ };
1632
+ }
1633
+ }
1634
+ const parsed = tryParseJson(effectiveText);
1635
+ if (!Array.isArray(parsed)) {
1636
+ return null;
1637
+ }
1638
+ const members = [];
1639
+ for (const item of parsed) {
1640
+ if (typeof item === "string" || typeof item === "number") {
1641
+ members.push(item);
1642
+ continue;
1643
+ }
1644
+ if (typeof item === "object" && item !== null && "id" in item) {
1645
+ const id = item["id"];
1646
+ if (typeof id === "string" || typeof id === "number") {
1647
+ members.push(id);
1648
+ }
1649
+ }
1650
+ }
1651
+ return {
1652
+ kind: "constraint",
1653
+ constraintKind: "allowedMembers",
1654
+ members,
1655
+ ...path2 !== void 0 && { path: path2 },
1656
+ provenance
1657
+ };
1658
+ }
1659
+ return {
1660
+ kind: "constraint",
1661
+ constraintKind: "pattern",
1662
+ pattern: effectiveText,
1663
+ ...path2 !== void 0 && { path: path2 },
1664
+ provenance
1665
+ };
1666
+ }
1667
+ function parseDefaultValueTagValue(text, provenance) {
1668
+ const trimmed = text.trim();
1669
+ let value;
1670
+ if (trimmed === "null") {
1671
+ value = null;
1672
+ } else if (trimmed === "true") {
1673
+ value = true;
1674
+ } else if (trimmed === "false") {
1675
+ value = false;
1676
+ } else {
1677
+ const parsed = tryParseJson(trimmed);
1678
+ value = parsed !== null ? parsed : trimmed;
1679
+ }
1680
+ return {
1681
+ kind: "annotation",
1682
+ annotationKind: "defaultValue",
1683
+ value,
1684
+ provenance
1685
+ };
1686
+ }
1687
+ function parseExtensionConstraintTagValue(tagName, text, provenance, options) {
1688
+ const parsedTag = parseTagSyntax(tagName, text, syntaxOptions(options?.registry));
1689
+ if (parsedTag.target !== null && !parsedTag.target.valid) {
1690
+ return null;
1691
+ }
1692
+ const effectiveText = parsedTag.argumentText;
1693
+ const path2 = parsedTag.target?.path ?? void 0;
1694
+ const registry = options?.registry;
1695
+ if (registry === void 0) {
1696
+ return null;
1697
+ }
1698
+ if (effectiveText.trim() === "") {
1699
+ return null;
1700
+ }
1701
+ const directTag = registry.findConstraintTag(tagName);
1702
+ if (directTag !== void 0) {
1703
+ return makeCustomConstraintNode(
1704
+ directTag.extensionId,
1705
+ directTag.registration.constraintName,
1706
+ directTag.registration.parseValue(effectiveText),
1707
+ provenance,
1708
+ path2,
1709
+ registry
1710
+ );
1711
+ }
1712
+ if (!(0, import_core2.isBuiltinConstraintName)(tagName)) {
1713
+ return null;
1714
+ }
1715
+ const broadenedTypeId = getBroadenedCustomTypeId(options?.fieldType);
1716
+ if (broadenedTypeId === void 0) {
1717
+ return null;
1718
+ }
1719
+ const broadened = registry.findBuiltinConstraintBroadening(broadenedTypeId, tagName);
1720
+ if (broadened === void 0) {
1721
+ return null;
1722
+ }
1723
+ return makeCustomConstraintNode(
1724
+ broadened.extensionId,
1725
+ broadened.registration.constraintName,
1726
+ broadened.registration.parseValue(effectiveText),
1727
+ provenance,
1728
+ path2,
1729
+ registry
1730
+ );
1731
+ }
1732
+ function getBroadenedCustomTypeId(fieldType) {
1733
+ if (fieldType?.kind === "custom") {
1734
+ return fieldType.typeId;
1735
+ }
1736
+ if (fieldType?.kind !== "union") {
1737
+ return void 0;
1738
+ }
1739
+ const customMembers = fieldType.members.filter(
1740
+ (member) => member.kind === "custom"
1741
+ );
1742
+ if (customMembers.length !== 1) {
1743
+ return void 0;
1744
+ }
1745
+ const nonCustomMembers = fieldType.members.filter((member) => member.kind !== "custom");
1746
+ const allOtherMembersAreNull = nonCustomMembers.every(
1747
+ (member) => member.kind === "primitive" && member.primitiveKind === "null"
1748
+ );
1749
+ const customMember = customMembers[0];
1750
+ return allOtherMembersAreNull && customMember !== void 0 ? customMember.typeId : void 0;
1751
+ }
1752
+ function makeCustomConstraintNode(extensionId, constraintName, payload, provenance, path2, registry) {
1753
+ const constraintId = `${extensionId}/${constraintName}`;
1754
+ const registration = registry.findConstraint(constraintId);
1755
+ if (registration === void 0) {
1756
+ throw new Error(
1757
+ `Custom TSDoc tag resolved to unregistered constraint "${constraintId}". Register the constraint before using its tag.`
1758
+ );
1759
+ }
1760
+ return {
1761
+ kind: "constraint",
1762
+ constraintKind: "custom",
1763
+ constraintId,
1764
+ payload,
1765
+ compositionRule: registration.compositionRule,
1766
+ ...path2 !== void 0 && { path: path2 },
1767
+ provenance
1768
+ };
1769
+ }
1770
+
1771
+ // src/semantic-targets.ts
1772
+ var import_core3 = require("@formspec/core");
1773
+ function pathKey(path2) {
1774
+ return path2?.segments.join(".") ?? "";
1775
+ }
1776
+ function formatConstraintTargetName(fieldName, path2) {
1777
+ if (path2 === null || path2.segments.length === 0) {
1778
+ return fieldName;
1779
+ }
1780
+ return `${fieldName}.${path2.segments.join(".")}`;
1781
+ }
1782
+ function dereferenceAnalysisType(type, typeRegistry) {
1783
+ let current = type;
1784
+ const seen = /* @__PURE__ */ new Set();
1785
+ while (current.kind === "reference") {
1786
+ if (seen.has(current.name)) {
1787
+ return current;
1788
+ }
1789
+ seen.add(current.name);
1790
+ const definition = typeRegistry[current.name];
1791
+ if (definition === void 0) {
1792
+ return current;
1793
+ }
1794
+ current = definition.type;
1795
+ }
1796
+ return current;
1797
+ }
1798
+ function collectReferencedTypeConstraints(type, typeRegistry) {
1799
+ const collected = [];
1800
+ let current = type;
1801
+ const seen = /* @__PURE__ */ new Set();
1802
+ while (current.kind === "reference") {
1803
+ if (seen.has(current.name)) {
1804
+ break;
1805
+ }
1806
+ seen.add(current.name);
1807
+ const definition = typeRegistry[current.name];
1808
+ if (definition === void 0) {
1809
+ break;
1810
+ }
1811
+ if (definition.constraints !== void 0) {
1812
+ collected.push(...definition.constraints);
1813
+ }
1814
+ current = definition.type;
1815
+ }
1816
+ return collected;
1817
+ }
1818
+ function collectReferencedTypeAnnotations(type, typeRegistry) {
1819
+ const collected = [];
1820
+ let current = type;
1821
+ const seen = /* @__PURE__ */ new Set();
1822
+ while (current.kind === "reference") {
1823
+ if (seen.has(current.name)) {
1824
+ break;
1825
+ }
1826
+ seen.add(current.name);
1827
+ const definition = typeRegistry[current.name];
1828
+ if (definition === void 0) {
1829
+ break;
1830
+ }
1831
+ if (definition.annotations !== void 0) {
1832
+ collected.push(...definition.annotations);
1833
+ }
1834
+ current = definition.type;
1835
+ }
1836
+ return collected;
1837
+ }
1838
+ function resolveProperty(type, typeRegistry, segments) {
1839
+ const effectiveType = dereferenceAnalysisType(type, typeRegistry);
1840
+ if (segments.length === 0) {
1841
+ return { kind: "resolved", property: null, rawType: type, type: effectiveType };
1842
+ }
1843
+ if (effectiveType.kind === "array") {
1844
+ return resolveProperty(effectiveType.items, typeRegistry, segments);
1845
+ }
1846
+ if (effectiveType.kind !== "object") {
1847
+ return { kind: "unresolvable", type: effectiveType };
1848
+ }
1849
+ const [segment, ...rest] = segments;
1850
+ if (segment === void 0) {
1851
+ throw new Error("Invariant violation: object traversal requires a segment");
1852
+ }
1853
+ const property = effectiveType.properties.find((candidate) => candidate.name === segment);
1854
+ if (property === void 0) {
1855
+ return { kind: "missing-property", segment };
1856
+ }
1857
+ if (rest.length === 0) {
1858
+ return {
1859
+ kind: "resolved",
1860
+ property,
1861
+ rawType: property.type,
1862
+ type: dereferenceAnalysisType(property.type, typeRegistry)
1863
+ };
1864
+ }
1865
+ return resolveProperty(property.type, typeRegistry, rest);
1866
+ }
1867
+ function resolveConstraintTargetState(fieldName, fieldType, path2, localConstraints, typeRegistry) {
1868
+ if (path2 === null) {
1869
+ const inheritedConstraints2 = collectReferencedTypeConstraints(fieldType, typeRegistry);
1870
+ const inheritedAnnotations2 = collectReferencedTypeAnnotations(fieldType, typeRegistry);
1871
+ const type = dereferenceAnalysisType(fieldType, typeRegistry);
1872
+ return {
1873
+ kind: "resolved",
1874
+ fieldName,
1875
+ path: path2,
1876
+ targetName: fieldName,
1877
+ type,
1878
+ inheritedConstraints: inheritedConstraints2,
1879
+ inheritedAnnotations: inheritedAnnotations2,
1880
+ localConstraints,
1881
+ effectiveConstraints: [...inheritedConstraints2, ...localConstraints]
1882
+ };
1883
+ }
1884
+ const resolution = resolveProperty(fieldType, typeRegistry, path2.segments);
1885
+ const targetName = formatConstraintTargetName(fieldName, path2);
1886
+ if (resolution.kind === "missing-property") {
1887
+ return {
1888
+ kind: "missing-property",
1889
+ fieldName,
1890
+ path: path2,
1891
+ targetName,
1892
+ segment: resolution.segment,
1893
+ localConstraints
1894
+ };
1895
+ }
1896
+ if (resolution.kind === "unresolvable") {
1897
+ return {
1898
+ kind: "unresolvable",
1899
+ fieldName,
1900
+ path: path2,
1901
+ targetName,
1902
+ type: resolution.type,
1903
+ localConstraints
1904
+ };
1905
+ }
1906
+ const propertyConstraints = resolution.property?.constraints ?? [];
1907
+ const propertyAnnotations = resolution.property?.annotations ?? [];
1908
+ const referencedConstraints = collectReferencedTypeConstraints(resolution.rawType, typeRegistry);
1909
+ const referencedAnnotations = collectReferencedTypeAnnotations(resolution.rawType, typeRegistry);
1910
+ const inheritedConstraints = [...propertyConstraints, ...referencedConstraints];
1911
+ const inheritedAnnotations = [...propertyAnnotations, ...referencedAnnotations];
1912
+ return {
1913
+ kind: "resolved",
1914
+ fieldName,
1915
+ path: path2,
1916
+ targetName,
1917
+ type: resolution.type,
1918
+ inheritedConstraints,
1919
+ inheritedAnnotations,
1920
+ localConstraints,
1921
+ effectiveConstraints: [...inheritedConstraints, ...localConstraints]
1922
+ };
1923
+ }
1924
+ function cloneTargetPath(path2) {
1925
+ if (path2 === void 0) {
1926
+ return null;
1927
+ }
1928
+ return { segments: [...path2.segments] };
1929
+ }
1930
+ function buildConstraintTargetStates(fieldName, fieldType, constraints, typeRegistry) {
1931
+ const grouped = /* @__PURE__ */ new Map([
1932
+ ["", { path: null, constraints: [] }]
1933
+ ]);
1934
+ for (const constraint of constraints) {
1935
+ const path2 = cloneTargetPath(constraint.path);
1936
+ const key = pathKey(path2);
1937
+ let bucket = grouped.get(key);
1938
+ if (bucket === void 0) {
1939
+ bucket = { path: path2, constraints: [] };
1940
+ grouped.set(key, bucket);
1941
+ }
1942
+ bucket.constraints.push(constraint);
1943
+ }
1944
+ return [...grouped.values()].map(
1945
+ (group) => resolveConstraintTargetState(fieldName, fieldType, group.path, group.constraints, typeRegistry)
1946
+ );
1947
+ }
1948
+ function addContradiction(diagnostics, message, primary, related) {
1949
+ diagnostics.push({
1950
+ code: "CONTRADICTING_CONSTRAINTS",
1951
+ message,
1952
+ severity: "error",
1953
+ primaryLocation: primary,
1954
+ relatedLocations: [related]
1955
+ });
1956
+ }
1957
+ function addTypeMismatch(diagnostics, message, primary) {
1958
+ diagnostics.push({
1959
+ code: "TYPE_MISMATCH",
1960
+ message,
1961
+ severity: "error",
1962
+ primaryLocation: primary,
1963
+ relatedLocations: []
1964
+ });
1965
+ }
1966
+ function addUnknownExtension(diagnostics, message, primary) {
1967
+ diagnostics.push({
1968
+ code: "UNKNOWN_EXTENSION",
1969
+ message,
1970
+ severity: "warning",
1971
+ primaryLocation: primary,
1972
+ relatedLocations: []
1973
+ });
1974
+ }
1975
+ function addUnknownPathTarget(diagnostics, message, primary) {
1976
+ diagnostics.push({
1977
+ code: "UNKNOWN_PATH_TARGET",
1978
+ message,
1979
+ severity: "error",
1980
+ primaryLocation: primary,
1981
+ relatedLocations: []
1982
+ });
1983
+ }
1984
+ function addConstraintBroadening(diagnostics, message, primary, related) {
1985
+ diagnostics.push({
1986
+ code: "CONSTRAINT_BROADENING",
1987
+ message,
1988
+ severity: "error",
1989
+ primaryLocation: primary,
1990
+ relatedLocations: [related]
1991
+ });
1992
+ }
1993
+ function getExtensionIdFromConstraintId(constraintId) {
1994
+ const separator = constraintId.lastIndexOf("/");
1995
+ if (separator <= 0) {
1996
+ return null;
1997
+ }
1998
+ return constraintId.slice(0, separator);
1999
+ }
2000
+ function typeLabel(type) {
2001
+ switch (type.kind) {
2002
+ case "primitive":
2003
+ return type.primitiveKind;
2004
+ case "enum":
2005
+ return "enum";
2006
+ case "array":
2007
+ return "array";
2008
+ case "object":
2009
+ return "object";
2010
+ case "record":
2011
+ return "record";
2012
+ case "union":
2013
+ return "union";
2014
+ case "reference":
2015
+ return `reference(${type.name})`;
2016
+ case "dynamic":
2017
+ return `dynamic(${type.dynamicKind})`;
2018
+ case "custom":
2019
+ return `custom(${type.typeId})`;
2020
+ default: {
2021
+ const exhaustive = type;
2022
+ return String(exhaustive);
2023
+ }
2024
+ }
2025
+ }
2026
+ function isJsonObject(value) {
2027
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2028
+ }
2029
+ function isJsonArray(value) {
2030
+ return Array.isArray(value);
2031
+ }
2032
+ function jsonValueEquals(left, right) {
2033
+ if (left === right) {
2034
+ return true;
2035
+ }
2036
+ if (isJsonArray(left) || isJsonArray(right)) {
2037
+ if (!isJsonArray(left) || !isJsonArray(right) || left.length !== right.length) {
2038
+ return false;
2039
+ }
2040
+ for (const [index, item] of left.entries()) {
2041
+ const rightItem = right[index];
2042
+ if (rightItem === void 0 || !jsonValueEquals(item, rightItem)) {
2043
+ return false;
2044
+ }
2045
+ }
2046
+ return true;
2047
+ }
2048
+ if (isJsonObject(left) || isJsonObject(right)) {
2049
+ if (!isJsonObject(left) || !isJsonObject(right)) {
2050
+ return false;
2051
+ }
2052
+ const leftKeys = Object.keys(left).sort();
2053
+ const rightKeys = Object.keys(right).sort();
2054
+ if (leftKeys.length !== rightKeys.length) {
2055
+ return false;
2056
+ }
2057
+ return leftKeys.every((key, index) => {
2058
+ const rightKey = rightKeys[index];
2059
+ if (rightKey !== key) {
2060
+ return false;
2061
+ }
2062
+ const leftValue = left[key];
2063
+ const rightValue = right[rightKey];
2064
+ return leftValue !== void 0 && rightValue !== void 0 && jsonValueEquals(leftValue, rightValue);
2065
+ });
2066
+ }
2067
+ return false;
2068
+ }
2069
+ function findNumeric(constraints, constraintKind) {
2070
+ return constraints.find(
2071
+ (constraint) => constraint.constraintKind === constraintKind
2072
+ );
2073
+ }
2074
+ function findLength(constraints, constraintKind) {
2075
+ return constraints.find(
2076
+ (constraint) => constraint.constraintKind === constraintKind
2077
+ );
2078
+ }
2079
+ function findAllowedMembers(constraints) {
2080
+ return constraints.filter(
2081
+ (constraint) => constraint.constraintKind === "allowedMembers"
2082
+ );
2083
+ }
2084
+ function findConstConstraints(constraints) {
2085
+ return constraints.filter(
2086
+ (constraint) => constraint.constraintKind === "const"
2087
+ );
2088
+ }
2089
+ function isOrderedBoundConstraint(constraint) {
2090
+ return constraint.constraintKind === "minimum" || constraint.constraintKind === "exclusiveMinimum" || constraint.constraintKind === "minLength" || constraint.constraintKind === "minItems" || constraint.constraintKind === "maximum" || constraint.constraintKind === "exclusiveMaximum" || constraint.constraintKind === "maxLength" || constraint.constraintKind === "maxItems";
2091
+ }
2092
+ function constraintPathKey(constraint) {
2093
+ return constraint.path?.segments.join(".") ?? "";
2094
+ }
2095
+ function orderedBoundFamily(kind) {
2096
+ switch (kind) {
2097
+ case "minimum":
2098
+ case "exclusiveMinimum":
2099
+ return "numeric-lower";
2100
+ case "maximum":
2101
+ case "exclusiveMaximum":
2102
+ return "numeric-upper";
2103
+ case "minLength":
2104
+ return "minLength";
2105
+ case "minItems":
2106
+ return "minItems";
2107
+ case "maxLength":
2108
+ return "maxLength";
2109
+ case "maxItems":
2110
+ return "maxItems";
2111
+ default: {
2112
+ const exhaustive = kind;
2113
+ return exhaustive;
2114
+ }
2115
+ }
2116
+ }
2117
+ function isNumericLowerKind(kind) {
2118
+ return kind === "minimum" || kind === "exclusiveMinimum";
2119
+ }
2120
+ function isNumericUpperKind(kind) {
2121
+ return kind === "maximum" || kind === "exclusiveMaximum";
2122
+ }
2123
+ function describeConstraintTag(constraint) {
2124
+ return `@${constraint.constraintKind}`;
2125
+ }
2126
+ function compareConstraintStrength(current, previous) {
2127
+ const family = orderedBoundFamily(current.constraintKind);
2128
+ if (family === "numeric-lower") {
2129
+ if (!isNumericLowerKind(current.constraintKind) || !isNumericLowerKind(previous.constraintKind)) {
2130
+ throw new Error("numeric-lower family received non-numeric lower-bound constraint");
2131
+ }
2132
+ if (current.value !== previous.value) {
2133
+ return current.value > previous.value ? 1 : -1;
2134
+ }
2135
+ if (current.constraintKind === "exclusiveMinimum" && previous.constraintKind === "minimum") {
2136
+ return 1;
2137
+ }
2138
+ if (current.constraintKind === "minimum" && previous.constraintKind === "exclusiveMinimum") {
2139
+ return -1;
2140
+ }
2141
+ return 0;
2142
+ }
2143
+ if (family === "numeric-upper") {
2144
+ if (!isNumericUpperKind(current.constraintKind) || !isNumericUpperKind(previous.constraintKind)) {
2145
+ throw new Error("numeric-upper family received non-numeric upper-bound constraint");
2146
+ }
2147
+ if (current.value !== previous.value) {
2148
+ return current.value < previous.value ? 1 : -1;
2149
+ }
2150
+ if (current.constraintKind === "exclusiveMaximum" && previous.constraintKind === "maximum") {
2151
+ return 1;
2152
+ }
2153
+ if (current.constraintKind === "maximum" && previous.constraintKind === "exclusiveMaximum") {
2154
+ return -1;
2155
+ }
2156
+ return 0;
2157
+ }
2158
+ switch (family) {
2159
+ case "minLength":
2160
+ case "minItems":
2161
+ if (current.value === previous.value) {
2162
+ return 0;
2163
+ }
2164
+ return current.value > previous.value ? 1 : -1;
2165
+ case "maxLength":
2166
+ case "maxItems":
2167
+ if (current.value === previous.value) {
2168
+ return 0;
2169
+ }
2170
+ return current.value < previous.value ? 1 : -1;
2171
+ default: {
2172
+ const exhaustive = family;
2173
+ return exhaustive;
2174
+ }
2175
+ }
2176
+ }
2177
+ function compareSemanticInclusivity(currentInclusive, previousInclusive) {
2178
+ if (currentInclusive === previousInclusive) {
2179
+ return 0;
2180
+ }
2181
+ return currentInclusive ? -1 : 1;
2182
+ }
2183
+ function compareCustomConstraintStrength(current, previous) {
2184
+ const order = current.comparePayloads(current.constraint.payload, previous.constraint.payload);
2185
+ const equalPayloadTiebreaker = order === 0 ? compareSemanticInclusivity(current.role.inclusive, previous.role.inclusive) : order;
2186
+ switch (current.role.bound) {
2187
+ case "lower":
2188
+ return equalPayloadTiebreaker;
2189
+ case "upper":
2190
+ return equalPayloadTiebreaker === 0 ? 0 : -equalPayloadTiebreaker;
2191
+ case "exact":
2192
+ return order === 0 ? 0 : Number.NaN;
2193
+ default: {
2194
+ const exhaustive = current.role.bound;
2195
+ return exhaustive;
2196
+ }
2197
+ }
2198
+ }
2199
+ function customConstraintsContradict(lower, upper) {
2200
+ const order = lower.comparePayloads(lower.constraint.payload, upper.constraint.payload);
2201
+ if (order > 0) {
2202
+ return true;
2203
+ }
2204
+ if (order < 0) {
2205
+ return false;
2206
+ }
2207
+ return !lower.role.inclusive || !upper.role.inclusive;
2208
+ }
2209
+ function describeCustomConstraintTag(constraint) {
2210
+ return constraint.provenance.tagName ?? constraint.constraintId;
2211
+ }
2212
+ function isNullType(type) {
2213
+ return type.kind === "primitive" && type.primitiveKind === "null";
2214
+ }
2215
+ function collectCustomConstraintCandidateTypes(type, typeRegistry) {
2216
+ const effectiveType = dereferenceAnalysisType(type, typeRegistry);
2217
+ const candidates = [effectiveType];
2218
+ if (effectiveType.kind === "array") {
2219
+ candidates.push(...collectCustomConstraintCandidateTypes(effectiveType.items, typeRegistry));
2220
+ }
2221
+ if (effectiveType.kind === "union") {
2222
+ const memberTypes = effectiveType.members.map(
2223
+ (member) => dereferenceAnalysisType(member, typeRegistry)
2224
+ );
2225
+ const nonNullMembers = memberTypes.filter((member) => !isNullType(member));
2226
+ if (nonNullMembers.length === 1 && nonNullMembers.length < memberTypes.length) {
2227
+ const [nullableMember] = nonNullMembers;
2228
+ if (nullableMember !== void 0) {
2229
+ candidates.push(...collectCustomConstraintCandidateTypes(nullableMember, typeRegistry));
2230
+ }
2231
+ }
2232
+ }
2233
+ return candidates;
2234
+ }
2235
+ function checkNumericContradictions(diagnostics, fieldName, constraints) {
2236
+ const min = findNumeric(constraints, "minimum");
2237
+ const max = findNumeric(constraints, "maximum");
2238
+ const exMin = findNumeric(constraints, "exclusiveMinimum");
2239
+ const exMax = findNumeric(constraints, "exclusiveMaximum");
2240
+ if (min !== void 0 && max !== void 0 && min.value > max.value) {
2241
+ addContradiction(
2242
+ diagnostics,
2243
+ `Field "${fieldName}": minimum (${String(min.value)}) is greater than maximum (${String(max.value)})`,
2244
+ min.provenance,
2245
+ max.provenance
2246
+ );
2247
+ }
2248
+ if (exMin !== void 0 && max !== void 0 && exMin.value >= max.value) {
2249
+ addContradiction(
2250
+ diagnostics,
2251
+ `Field "${fieldName}": exclusiveMinimum (${String(exMin.value)}) is greater than or equal to maximum (${String(max.value)})`,
2252
+ exMin.provenance,
2253
+ max.provenance
2254
+ );
2255
+ }
2256
+ if (min !== void 0 && exMax !== void 0 && min.value >= exMax.value) {
2257
+ addContradiction(
2258
+ diagnostics,
2259
+ `Field "${fieldName}": minimum (${String(min.value)}) is greater than or equal to exclusiveMaximum (${String(exMax.value)})`,
2260
+ min.provenance,
2261
+ exMax.provenance
2262
+ );
2263
+ }
2264
+ if (exMin !== void 0 && exMax !== void 0 && exMin.value >= exMax.value) {
2265
+ addContradiction(
2266
+ diagnostics,
2267
+ `Field "${fieldName}": exclusiveMinimum (${String(exMin.value)}) is greater than or equal to exclusiveMaximum (${String(exMax.value)})`,
2268
+ exMin.provenance,
2269
+ exMax.provenance
2270
+ );
2271
+ }
2272
+ }
2273
+ function checkLengthContradictions(diagnostics, fieldName, constraints) {
2274
+ const minLen = findLength(constraints, "minLength");
2275
+ const maxLen = findLength(constraints, "maxLength");
2276
+ if (minLen !== void 0 && maxLen !== void 0 && minLen.value > maxLen.value) {
2277
+ addContradiction(
2278
+ diagnostics,
2279
+ `Field "${fieldName}": minLength (${String(minLen.value)}) is greater than maxLength (${String(maxLen.value)})`,
2280
+ minLen.provenance,
2281
+ maxLen.provenance
2282
+ );
2283
+ }
2284
+ const minItems = findLength(constraints, "minItems");
2285
+ const maxItems = findLength(constraints, "maxItems");
2286
+ if (minItems !== void 0 && maxItems !== void 0 && minItems.value > maxItems.value) {
2287
+ addContradiction(
2288
+ diagnostics,
2289
+ `Field "${fieldName}": minItems (${String(minItems.value)}) is greater than maxItems (${String(maxItems.value)})`,
2290
+ minItems.provenance,
2291
+ maxItems.provenance
2292
+ );
2293
+ }
2294
+ }
2295
+ function checkAllowedMembersContradiction(diagnostics, fieldName, constraints) {
2296
+ const members = findAllowedMembers(constraints);
2297
+ if (members.length < 2) {
2298
+ return;
2299
+ }
2300
+ const firstSet = new Set(members[0]?.members ?? []);
2301
+ for (let index = 1; index < members.length; index += 1) {
2302
+ const current = members[index];
2303
+ if (current === void 0) {
2304
+ continue;
2305
+ }
2306
+ for (const member of firstSet) {
2307
+ if (!current.members.includes(member)) {
2308
+ firstSet.delete(member);
2309
+ }
2310
+ }
2311
+ }
2312
+ if (firstSet.size === 0) {
2313
+ const first = members[0];
2314
+ const second = members[1];
2315
+ if (first !== void 0 && second !== void 0) {
2316
+ addContradiction(
2317
+ diagnostics,
2318
+ `Field "${fieldName}": allowedMembers constraints have an empty intersection (no valid values remain)`,
2319
+ first.provenance,
2320
+ second.provenance
2321
+ );
2322
+ }
2323
+ }
2324
+ }
2325
+ function checkConstContradictions(diagnostics, fieldName, constraints) {
2326
+ const constConstraints = findConstConstraints(constraints);
2327
+ if (constConstraints.length < 2) {
2328
+ return;
2329
+ }
2330
+ const first = constConstraints[0];
2331
+ if (first === void 0) {
2332
+ return;
2333
+ }
2334
+ for (let index = 1; index < constConstraints.length; index += 1) {
2335
+ const current = constConstraints[index];
2336
+ if (current === void 0 || jsonValueEquals(first.value, current.value)) {
2337
+ continue;
2338
+ }
2339
+ addContradiction(
2340
+ diagnostics,
2341
+ `Field "${fieldName}": conflicting @const constraints require both ${JSON.stringify(first.value)} and ${JSON.stringify(current.value)}`,
2342
+ first.provenance,
2343
+ current.provenance
2344
+ );
2345
+ }
2346
+ }
2347
+ function checkConstraintBroadening(diagnostics, fieldName, constraints) {
2348
+ const strongestByKey = /* @__PURE__ */ new Map();
2349
+ for (const constraint of constraints) {
2350
+ if (!isOrderedBoundConstraint(constraint)) {
2351
+ continue;
2352
+ }
2353
+ const key = `${orderedBoundFamily(constraint.constraintKind)}:${constraintPathKey(constraint)}`;
2354
+ const previous = strongestByKey.get(key);
2355
+ if (previous === void 0) {
2356
+ strongestByKey.set(key, constraint);
2357
+ continue;
2358
+ }
2359
+ const strength = compareConstraintStrength(constraint, previous);
2360
+ if (strength < 0) {
2361
+ addConstraintBroadening(
2362
+ diagnostics,
2363
+ `Field "${fieldName}": ${describeConstraintTag(constraint)} (${String(constraint.value)}) is broader than earlier ${describeConstraintTag(previous)} (${String(previous.value)}). Constraints can only narrow.`,
2364
+ constraint.provenance,
2365
+ previous.provenance
2366
+ );
2367
+ continue;
2368
+ }
2369
+ if (strength > 0) {
2370
+ strongestByKey.set(key, constraint);
2371
+ }
2372
+ }
2373
+ }
2374
+ function checkCustomConstraintSemantics(diagnostics, fieldName, constraints, extensionRegistry) {
2375
+ if (extensionRegistry === void 0) {
2376
+ return;
2377
+ }
2378
+ const strongestByKey = /* @__PURE__ */ new Map();
2379
+ const lowerByFamily = /* @__PURE__ */ new Map();
2380
+ const upperByFamily = /* @__PURE__ */ new Map();
2381
+ for (const constraint of constraints) {
2382
+ if (constraint.constraintKind !== "custom") {
2383
+ continue;
2384
+ }
2385
+ const registration = extensionRegistry.findConstraint(constraint.constraintId);
2386
+ if (registration?.comparePayloads === void 0 || registration.semanticRole === void 0) {
2387
+ continue;
2388
+ }
2389
+ const entry = {
2390
+ constraint,
2391
+ comparePayloads: registration.comparePayloads,
2392
+ role: registration.semanticRole
2393
+ };
2394
+ const familyKey = `${registration.semanticRole.family}:${constraintPathKey(constraint)}`;
2395
+ const boundKey = `${familyKey}:${registration.semanticRole.bound}`;
2396
+ const previous = strongestByKey.get(boundKey);
2397
+ if (previous !== void 0) {
2398
+ const strength = compareCustomConstraintStrength(entry, previous);
2399
+ if (Number.isNaN(strength)) {
2400
+ addContradiction(
2401
+ diagnostics,
2402
+ `Field "${fieldName}": ${describeCustomConstraintTag(constraint)} conflicts with ${describeCustomConstraintTag(previous.constraint)}`,
2403
+ constraint.provenance,
2404
+ previous.constraint.provenance
2405
+ );
2406
+ continue;
2407
+ }
2408
+ if (strength < 0) {
2409
+ addConstraintBroadening(
2410
+ diagnostics,
2411
+ `Field "${fieldName}": ${describeCustomConstraintTag(constraint)} is broader than earlier ${describeCustomConstraintTag(previous.constraint)}. Constraints can only narrow.`,
2412
+ constraint.provenance,
2413
+ previous.constraint.provenance
2414
+ );
2415
+ continue;
2416
+ }
2417
+ if (strength > 0) {
2418
+ strongestByKey.set(boundKey, entry);
2419
+ }
2420
+ } else {
2421
+ strongestByKey.set(boundKey, entry);
2422
+ }
2423
+ if (registration.semanticRole.bound === "lower") {
2424
+ lowerByFamily.set(familyKey, strongestByKey.get(boundKey) ?? entry);
2425
+ } else if (registration.semanticRole.bound === "upper") {
2426
+ upperByFamily.set(familyKey, strongestByKey.get(boundKey) ?? entry);
2427
+ }
2428
+ }
2429
+ for (const [familyKey, lower] of lowerByFamily) {
2430
+ const upper = upperByFamily.get(familyKey);
2431
+ if (upper === void 0 || !customConstraintsContradict(lower, upper)) {
2432
+ continue;
2433
+ }
2434
+ addContradiction(
2435
+ diagnostics,
2436
+ `Field "${fieldName}": ${describeCustomConstraintTag(lower.constraint)} contradicts ${describeCustomConstraintTag(upper.constraint)}`,
2437
+ lower.constraint.provenance,
2438
+ upper.constraint.provenance
2439
+ );
2440
+ }
2441
+ }
2442
+ function checkCustomConstraint(diagnostics, fieldName, type, constraint, typeRegistry, extensionRegistry) {
2443
+ if (extensionRegistry === void 0) {
2444
+ return;
2445
+ }
2446
+ const registration = extensionRegistry.findConstraint(constraint.constraintId);
2447
+ if (registration === void 0) {
2448
+ addUnknownExtension(
2449
+ diagnostics,
2450
+ `Field "${fieldName}": custom constraint "${constraint.constraintId}" is not registered in the extension registry`,
2451
+ constraint.provenance
2452
+ );
2453
+ return;
2454
+ }
2455
+ const candidateTypes = collectCustomConstraintCandidateTypes(type, typeRegistry);
2456
+ const normalizedTagName = constraint.provenance.tagName === void 0 ? void 0 : (0, import_core3.normalizeConstraintTagName)(constraint.provenance.tagName.replace(/^@/, ""));
2457
+ if (normalizedTagName !== void 0) {
2458
+ const tagRegistration = extensionRegistry.findConstraintTag(normalizedTagName);
2459
+ const extensionId = getExtensionIdFromConstraintId(constraint.constraintId);
2460
+ if (extensionId !== null && tagRegistration?.extensionId === extensionId && tagRegistration.registration.constraintName === registration.constraintName && !candidateTypes.some(
2461
+ (candidateType) => tagRegistration.registration.isApplicableToType?.(candidateType) !== false
2462
+ )) {
2463
+ addTypeMismatch(
2464
+ diagnostics,
2465
+ `Field "${fieldName}": custom constraint "${constraint.constraintId}" is not applicable to type "${typeLabel(type)}"`,
2466
+ constraint.provenance
2467
+ );
2468
+ return;
2469
+ }
2470
+ }
2471
+ if (registration.applicableTypes === null) {
2472
+ if (!candidateTypes.some(
2473
+ (candidateType) => registration.isApplicableToType?.(candidateType) !== false
2474
+ )) {
2475
+ addTypeMismatch(
2476
+ diagnostics,
2477
+ `Field "${fieldName}": custom constraint "${constraint.constraintId}" is not applicable to type "${typeLabel(type)}"`,
2478
+ constraint.provenance
2479
+ );
2480
+ }
2481
+ return;
2482
+ }
2483
+ const applicableTypes = registration.applicableTypes;
2484
+ const matchesApplicableType = candidateTypes.some(
2485
+ (candidateType) => applicableTypes.includes(candidateType.kind) && registration.isApplicableToType?.(candidateType) !== false
2486
+ );
2487
+ if (!matchesApplicableType) {
2488
+ addTypeMismatch(
2489
+ diagnostics,
2490
+ `Field "${fieldName}": custom constraint "${constraint.constraintId}" is not applicable to type "${typeLabel(type)}"`,
2491
+ constraint.provenance
2492
+ );
2493
+ }
2494
+ }
2495
+ function checkConstraintOnType(diagnostics, fieldName, type, constraint, typeRegistry, extensionRegistry) {
2496
+ const effectiveType = dereferenceAnalysisType(type, typeRegistry);
2497
+ const isNumber = effectiveType.kind === "primitive" && ["number", "integer", "bigint"].includes(effectiveType.primitiveKind);
2498
+ const isString = effectiveType.kind === "primitive" && effectiveType.primitiveKind === "string";
2499
+ const isArray = effectiveType.kind === "array";
2500
+ const isEnum = effectiveType.kind === "enum";
2501
+ const arrayItemType = effectiveType.kind === "array" ? dereferenceAnalysisType(effectiveType.items, typeRegistry) : void 0;
2502
+ const isStringArray2 = arrayItemType?.kind === "primitive" && arrayItemType.primitiveKind === "string";
2503
+ const label = typeLabel(effectiveType);
2504
+ switch (constraint.constraintKind) {
2505
+ case "minimum":
2506
+ case "maximum":
2507
+ case "exclusiveMinimum":
2508
+ case "exclusiveMaximum":
2509
+ case "multipleOf":
2510
+ if (!isNumber) {
2511
+ addTypeMismatch(
2512
+ diagnostics,
2513
+ `Field "${fieldName}": constraint "${constraint.constraintKind}" is only valid on number fields, but field type is "${label}"`,
2514
+ constraint.provenance
2515
+ );
2516
+ }
2517
+ break;
2518
+ case "minLength":
2519
+ case "maxLength":
2520
+ case "pattern":
2521
+ if (!isString && !isStringArray2) {
2522
+ addTypeMismatch(
2523
+ diagnostics,
2524
+ `Field "${fieldName}": constraint "${constraint.constraintKind}" is only valid on string fields or string array items, but field type is "${label}"`,
2525
+ constraint.provenance
2526
+ );
2527
+ }
2528
+ break;
2529
+ case "minItems":
2530
+ case "maxItems":
2531
+ case "uniqueItems":
2532
+ if (!isArray) {
2533
+ addTypeMismatch(
2534
+ diagnostics,
2535
+ `Field "${fieldName}": constraint "${constraint.constraintKind}" is only valid on array fields, but field type is "${label}"`,
2536
+ constraint.provenance
2537
+ );
2538
+ }
2539
+ break;
2540
+ case "allowedMembers":
2541
+ if (!isEnum) {
2542
+ addTypeMismatch(
2543
+ diagnostics,
2544
+ `Field "${fieldName}": constraint "allowedMembers" is only valid on enum fields, but field type is "${label}"`,
2545
+ constraint.provenance
2546
+ );
2547
+ }
2548
+ break;
2549
+ case "const": {
2550
+ const isPrimitiveConstType = effectiveType.kind === "primitive" && ["string", "number", "integer", "bigint", "boolean", "null"].includes(
2551
+ effectiveType.primitiveKind
2552
+ ) || effectiveType.kind === "enum";
2553
+ if (!isPrimitiveConstType) {
2554
+ addTypeMismatch(
2555
+ diagnostics,
2556
+ `Field "${fieldName}": constraint "const" is only valid on primitive or enum fields, but field type is "${label}"`,
2557
+ constraint.provenance
2558
+ );
2559
+ break;
2560
+ }
2561
+ if (effectiveType.kind === "primitive") {
2562
+ const valueType = constraint.value === null ? "null" : Array.isArray(constraint.value) ? "array" : typeof constraint.value;
2563
+ const expectedValueType = effectiveType.primitiveKind === "integer" || effectiveType.primitiveKind === "bigint" ? "number" : effectiveType.primitiveKind;
2564
+ if (valueType !== expectedValueType) {
2565
+ addTypeMismatch(
2566
+ diagnostics,
2567
+ `Field "${fieldName}": @const value type "${valueType}" is incompatible with field type "${effectiveType.primitiveKind}"`,
2568
+ constraint.provenance
2569
+ );
2570
+ }
2571
+ break;
2572
+ }
2573
+ const memberValues = effectiveType.members.map((member) => member.value);
2574
+ if (!memberValues.some((member) => jsonValueEquals(member, constraint.value))) {
2575
+ addTypeMismatch(
2576
+ diagnostics,
2577
+ `Field "${fieldName}": @const value ${JSON.stringify(constraint.value)} is not one of the enum members`,
2578
+ constraint.provenance
2579
+ );
2580
+ }
2581
+ break;
2582
+ }
2583
+ case "custom":
2584
+ checkCustomConstraint(
2585
+ diagnostics,
2586
+ fieldName,
2587
+ effectiveType,
2588
+ constraint,
2589
+ typeRegistry,
2590
+ extensionRegistry
2591
+ );
2592
+ break;
2593
+ default: {
2594
+ const exhaustive = constraint;
2595
+ throw new Error(`Unhandled constraint: ${JSON.stringify(exhaustive)}`);
2596
+ }
2597
+ }
2598
+ }
2599
+ function analyzeResolvedTargetState(diagnostics, state, typeRegistry, extensionRegistry) {
2600
+ checkNumericContradictions(diagnostics, state.targetName, state.effectiveConstraints);
2601
+ checkLengthContradictions(diagnostics, state.targetName, state.effectiveConstraints);
2602
+ checkAllowedMembersContradiction(diagnostics, state.targetName, state.effectiveConstraints);
2603
+ checkConstContradictions(diagnostics, state.targetName, state.effectiveConstraints);
2604
+ checkConstraintBroadening(diagnostics, state.targetName, state.effectiveConstraints);
2605
+ checkCustomConstraintSemantics(
2606
+ diagnostics,
2607
+ state.targetName,
2608
+ state.effectiveConstraints,
2609
+ extensionRegistry
2610
+ );
2611
+ for (const constraint of state.effectiveConstraints) {
2612
+ checkConstraintOnType(
2613
+ diagnostics,
2614
+ state.targetName,
2615
+ state.type,
2616
+ constraint,
2617
+ typeRegistry,
2618
+ extensionRegistry
2619
+ );
2620
+ }
2621
+ }
2622
+ function analyzeConstraintTargets(fieldName, fieldType, constraints, typeRegistry, options) {
2623
+ const diagnostics = [];
2624
+ const targetStates = buildConstraintTargetStates(fieldName, fieldType, constraints, typeRegistry);
2625
+ for (const targetState of targetStates) {
2626
+ switch (targetState.kind) {
2627
+ case "resolved":
2628
+ analyzeResolvedTargetState(
2629
+ diagnostics,
2630
+ targetState,
2631
+ typeRegistry,
2632
+ options?.extensionRegistry
2633
+ );
2634
+ break;
2635
+ case "missing-property":
2636
+ for (const constraint of targetState.localConstraints) {
2637
+ addUnknownPathTarget(
2638
+ diagnostics,
2639
+ `Field "${targetState.targetName}": path-targeted constraint "${constraint.constraintKind}" references unknown path segment "${targetState.segment}"`,
2640
+ constraint.provenance
2641
+ );
2642
+ }
2643
+ break;
2644
+ case "unresolvable":
2645
+ for (const constraint of targetState.localConstraints) {
2646
+ addTypeMismatch(
2647
+ diagnostics,
2648
+ `Field "${targetState.targetName}": path-targeted constraint "${constraint.constraintKind}" is invalid because type "${typeLabel(targetState.type)}" cannot be traversed`,
2649
+ constraint.provenance
2650
+ );
2651
+ }
2652
+ break;
2653
+ default: {
2654
+ const exhaustive = targetState;
2655
+ throw new Error(`Unhandled target state: ${String(exhaustive)}`);
2656
+ }
2657
+ }
2658
+ }
2659
+ return {
2660
+ diagnostics,
2661
+ targetStates
2662
+ };
2663
+ }
2664
+
2665
+ // src/file-snapshots.ts
2666
+ var ts4 = __toESM(require("typescript"), 1);
2667
+
2668
+ // src/compiler-signatures.ts
2669
+ var ts2 = __toESM(require("typescript"), 1);
2670
+ var PRELUDE_LINES = [
2671
+ "type FormSpecPlacement =",
2672
+ ' | "class"',
2673
+ ' | "class-field"',
2674
+ ' | "class-method"',
2675
+ ' | "interface"',
2676
+ ' | "interface-field"',
2677
+ ' | "type-alias"',
2678
+ ' | "type-alias-field"',
2679
+ ' | "variable"',
2680
+ ' | "function"',
2681
+ ' | "function-parameter"',
2682
+ ' | "method-parameter";',
2683
+ "",
2684
+ "type FormSpecCapability =",
2685
+ ' | "numeric-comparable"',
2686
+ ' | "string-like"',
2687
+ ' | "array-like"',
2688
+ ' | "enum-member-addressable"',
2689
+ ' | "json-like"',
2690
+ ' | "condition-like"',
2691
+ ' | "object-like";',
2692
+ "",
2693
+ "interface TagContext<P extends FormSpecPlacement, Host, Subject> {",
2694
+ " readonly placement: P;",
2695
+ " readonly hostType: Host;",
2696
+ " readonly subjectType: Subject;",
2697
+ "}",
2698
+ "",
2699
+ "type NonNullish<T> = Exclude<T, null | undefined>;",
2700
+ "",
2701
+ "type ProvidesCapability<T, Capability extends FormSpecCapability> =",
2702
+ ' Capability extends "numeric-comparable"',
2703
+ " ? NonNullish<T> extends number | bigint",
2704
+ " ? true",
2705
+ " : false",
2706
+ ' : Capability extends "string-like"',
2707
+ " ? NonNullish<T> extends string",
2708
+ " ? true",
2709
+ " : false",
2710
+ ' : Capability extends "array-like"',
2711
+ " ? NonNullish<T> extends readonly unknown[]",
2712
+ " ? true",
2713
+ " : false",
2714
+ ' : Capability extends "enum-member-addressable"',
2715
+ " ? NonNullish<T> extends string",
2716
+ " ? true",
2717
+ " : false",
2718
+ ' : Capability extends "json-like"',
2719
+ " ? true",
2720
+ ' : Capability extends "condition-like"',
2721
+ " ? true",
2722
+ ' : Capability extends "object-like"',
2723
+ " ? NonNullish<T> extends readonly unknown[]",
2724
+ " ? false",
2725
+ " : NonNullish<T> extends object",
2726
+ " ? true",
2727
+ " : false",
2728
+ " : false;",
2729
+ "",
2730
+ "type NestedPathOfCapability<Subject, Capability extends FormSpecCapability> =",
2731
+ " NonNullish<Subject> extends readonly (infer Item)[]",
2732
+ " ? NestedPathOfCapability<Item, Capability>",
2733
+ " : NonNullish<Subject> extends object",
2734
+ " ? {",
2735
+ " [Key in Extract<keyof NonNullish<Subject>, string>]:",
2736
+ " | (ProvidesCapability<NonNullish<Subject>[Key], Capability> extends true ? Key : never)",
2737
+ " | (NestedPathOfCapability<NonNullish<Subject>[Key], Capability> extends never",
2738
+ " ? never",
2739
+ " : `${Key}.${NestedPathOfCapability<NonNullish<Subject>[Key], Capability>}`);",
2740
+ " }[Extract<keyof NonNullish<Subject>, string>]",
2741
+ " : never;",
2742
+ "",
2743
+ "type PathOfCapability<Subject, Capability extends FormSpecCapability> =",
2744
+ " NestedPathOfCapability<Subject, Capability>;",
2745
+ "",
2746
+ "type MemberTarget<Subject> = Extract<keyof NonNullish<Subject>, string>;",
2747
+ "",
2748
+ 'type VariantTarget<Subject> = "singular" | "plural";',
2749
+ "",
2750
+ "type FormSpecCondition = unknown;",
2751
+ "type JsonValue = unknown;",
2752
+ "",
2753
+ "declare function __ctx<P extends FormSpecPlacement, Host, Subject>(): TagContext<P, Host, Subject>;",
2754
+ "declare function __path<Subject, Capability extends FormSpecCapability>(",
2755
+ " path: PathOfCapability<Subject, Capability>",
2756
+ "): PathOfCapability<Subject, Capability>;",
2757
+ "declare function __member<Subject>(member: MemberTarget<Subject>): MemberTarget<Subject>;",
2758
+ "declare function __variant<Subject>(variant: VariantTarget<Subject>): VariantTarget<Subject>;"
2759
+ ];
2760
+ function placementUnion(placements) {
2761
+ return placements.map((placement) => JSON.stringify(placement)).join(" | ");
2762
+ }
2763
+ function renderValueType(valueKind) {
2764
+ switch (valueKind) {
2765
+ case "number":
2766
+ case "integer":
2767
+ case "signedInteger":
2768
+ return "number";
2769
+ case "string":
2770
+ return "string";
2771
+ case "json":
2772
+ return "JsonValue";
2773
+ case "boolean":
2774
+ return "boolean";
2775
+ case "condition":
2776
+ return "FormSpecCondition";
2777
+ case void 0:
2778
+ return "unknown";
2779
+ default: {
2780
+ const exhaustive = valueKind;
2781
+ return exhaustive;
2782
+ }
2783
+ }
2784
+ }
2785
+ function renderTargetParameterType(parameter) {
2786
+ switch (parameter.kind) {
2787
+ case "target-path":
2788
+ return parameter.capability === void 0 ? "PathOfCapability<Subject, FormSpecCapability>" : `PathOfCapability<Subject, ${JSON.stringify(parameter.capability)}>`;
2789
+ case "target-member":
2790
+ return "MemberTarget<Subject>";
2791
+ case "target-variant":
2792
+ return "VariantTarget<Subject>";
2793
+ case "value":
2794
+ return renderValueType(parameter.valueKind);
2795
+ default: {
2796
+ const exhaustive = parameter.kind;
2797
+ return exhaustive;
2798
+ }
2799
+ }
2800
+ }
2801
+ function renderSignature(tagName, signature) {
2802
+ const parameters = signature.parameters.map((parameter, index) => {
2803
+ const name = parameter.kind === "value" ? "value" : `target${String(index)}`;
2804
+ return `${name}: ${renderTargetParameterType(parameter)}`;
2805
+ });
2806
+ return [
2807
+ ` function ${getSyntheticTagHelperName(tagName)}<Host, Subject>(`,
2808
+ ` ctx: TagContext<${placementUnion(signature.placements)}, Host, Subject>${parameters.length > 0 ? "," : ""}`,
2809
+ ...parameters.map(
2810
+ (parameter, index) => ` ${parameter}${index === parameters.length - 1 ? "" : ","}`
2811
+ ),
2812
+ " ): void;"
2813
+ ].join("\n");
2814
+ }
2815
+ function getSyntheticTagHelperName(tagName) {
2816
+ return `tag_${tagName}`;
2817
+ }
2818
+ function targetKindForParameter(parameter) {
2819
+ switch (parameter.kind) {
2820
+ case "target-path":
2821
+ return "path";
2822
+ case "target-member":
2823
+ return "member";
2824
+ case "target-variant":
2825
+ return "variant";
2826
+ case "value":
2827
+ return null;
2828
+ default: {
2829
+ const exhaustive = parameter.kind;
2830
+ return exhaustive;
2831
+ }
2832
+ }
2833
+ }
2834
+ function getSignatureTargetKind(signature) {
2835
+ for (const parameter of signature.parameters) {
2836
+ const targetKind = targetKindForParameter(parameter);
2837
+ if (targetKind !== null) {
2838
+ return targetKind;
2839
+ }
2840
+ }
2841
+ return null;
2842
+ }
2843
+ function getTargetParameter(signature) {
2844
+ return signature.parameters.find(
2845
+ (parameter) => parameter.kind !== "value"
2846
+ ) ?? null;
2847
+ }
2848
+ function getPathTargetCapability(signature) {
2849
+ const parameter = getTargetParameter(signature);
2850
+ if (parameter?.kind !== "target-path") {
2851
+ throw new Error(`Invariant violation: expected a path-target synthetic signature`);
2852
+ }
2853
+ if (parameter.capability === void 0) {
2854
+ throw new Error(
2855
+ `Invariant violation: path-target synthetic signatures must declare a capability`
2856
+ );
2857
+ }
2858
+ return JSON.stringify(parameter.capability);
2859
+ }
2860
+ function renderTargetArgument(target, signature, subjectType) {
2861
+ switch (target.kind) {
2862
+ case "path":
2863
+ return `__path<${subjectType}, ${getPathTargetCapability(signature)}>(${JSON.stringify(
2864
+ target.text
2865
+ )})`;
2866
+ case "member":
2867
+ return `__member<${subjectType}>(${JSON.stringify(target.text)})`;
2868
+ case "variant":
2869
+ return `__variant<${subjectType}>(${JSON.stringify(target.text)})`;
2870
+ }
2871
+ }
2872
+ function getMatchingTagSignatures(definition, placement, targetKind) {
2873
+ return definition.signatures.filter(
2874
+ (signature) => signature.placements.includes(placement) && getSignatureTargetKind(signature) === targetKind
2875
+ );
2876
+ }
2877
+ function buildSyntheticHelperPrelude(extensions) {
2878
+ const lines = [...PRELUDE_LINES, "", "declare namespace __formspec {"];
2879
+ for (const definition of getAllTagDefinitions(extensions)) {
2880
+ for (const signature of definition.signatures) {
2881
+ lines.push(renderSignature(definition.canonicalName, signature));
2882
+ }
2883
+ }
2884
+ lines.push("}");
2885
+ return lines.join("\n");
2886
+ }
2887
+ function lowerTagApplicationToSyntheticCall(options) {
2888
+ const definition = getTagDefinition(options.tagName, options.extensions);
2889
+ if (definition === null) {
2890
+ throw new Error(`Unknown FormSpec tag: ${options.tagName}`);
2891
+ }
2892
+ const targetKind = options.target?.kind ?? null;
2893
+ const matchingSignatures = getMatchingTagSignatures(definition, options.placement, targetKind);
2894
+ if (matchingSignatures.length === 0) {
2895
+ throw new Error(
2896
+ `No synthetic signature for @${definition.canonicalName} on placement "${options.placement}"` + (targetKind === null ? "" : ` with target kind "${targetKind}"`)
2897
+ );
2898
+ }
2899
+ const args = [
2900
+ `__ctx<${JSON.stringify(options.placement)}, ${options.hostType}, ${options.subjectType}>()`
2901
+ ];
2902
+ const signature = matchingSignatures[0];
2903
+ if (signature === void 0) {
2904
+ throw new Error(
2905
+ `Invariant violation: missing synthetic signature for @${definition.canonicalName}`
2906
+ );
2907
+ }
2908
+ if (options.target !== void 0 && options.target !== null) {
2909
+ args.push(renderTargetArgument(options.target, signature, options.subjectType));
2910
+ }
2911
+ if (options.argumentExpression !== void 0 && options.argumentExpression !== null) {
2912
+ args.push(options.argumentExpression);
2913
+ }
2914
+ return {
2915
+ definition,
2916
+ matchingSignatures,
2917
+ callExpression: `__formspec.${getSyntheticTagHelperName(definition.canonicalName)}(${args.join(", ")});`
2918
+ };
2919
+ }
2920
+ function createSyntheticCompilerHost(fileName, sourceText, compilerOptions) {
2921
+ const host = ts2.createCompilerHost(compilerOptions, true);
2922
+ const originalGetSourceFile = host.getSourceFile.bind(host);
2923
+ host.getSourceFile = (requestedFileName, languageVersion, onError, shouldCreateNewSourceFile) => {
2924
+ if (requestedFileName === fileName) {
2925
+ return ts2.createSourceFile(requestedFileName, sourceText, languageVersion, true);
2926
+ }
2927
+ return originalGetSourceFile(
2928
+ requestedFileName,
2929
+ languageVersion,
2930
+ onError,
2931
+ shouldCreateNewSourceFile
2932
+ );
2933
+ };
2934
+ host.readFile = (requestedFileName) => {
2935
+ if (requestedFileName === fileName) {
2936
+ return sourceText;
2937
+ }
2938
+ return ts2.sys.readFile(requestedFileName);
2939
+ };
2940
+ host.fileExists = (requestedFileName) => requestedFileName === fileName || ts2.sys.fileExists(requestedFileName);
2941
+ host.writeFile = () => void 0;
2942
+ return host;
2943
+ }
2944
+ function flattenDiagnosticMessage(message) {
2945
+ return ts2.flattenDiagnosticMessageText(message, "\n");
2946
+ }
2947
+ var syntheticCheckCache = /* @__PURE__ */ new Map();
2948
+ function checkSyntheticTagApplication(options) {
2949
+ const lowered = lowerTagApplicationToSyntheticCall(options);
2950
+ const sourceText = [
2951
+ buildSyntheticHelperPrelude(options.extensions),
2952
+ "",
2953
+ ...options.supportingDeclarations ?? [],
2954
+ "",
2955
+ lowered.callExpression
2956
+ ].join("\n");
2957
+ const cached = syntheticCheckCache.get(sourceText);
2958
+ if (cached !== void 0) {
2959
+ return cached;
2960
+ }
2961
+ const fileName = "/virtual/formspec-synthetic.ts";
2962
+ const compilerOptions = {
2963
+ strict: true,
2964
+ noEmit: true,
2965
+ target: ts2.ScriptTarget.ES2022,
2966
+ module: ts2.ModuleKind.ESNext,
2967
+ lib: ["lib.es2022.d.ts"]
2968
+ };
2969
+ const host = createSyntheticCompilerHost(fileName, sourceText, compilerOptions);
2970
+ const program = ts2.createProgram([fileName], compilerOptions, host);
2971
+ const diagnostics = ts2.getPreEmitDiagnostics(program).filter((diagnostic) => diagnostic.file === void 0 || diagnostic.file.fileName === fileName).map((diagnostic) => ({
2972
+ code: diagnostic.code,
2973
+ message: flattenDiagnosticMessage(diagnostic.messageText)
2974
+ }));
2975
+ const result = {
2976
+ sourceText,
2977
+ diagnostics
2978
+ };
2979
+ syntheticCheckCache.set(sourceText, result);
2980
+ return result;
2981
+ }
2982
+
2983
+ // src/source-bindings.ts
2984
+ var ts3 = __toESM(require("typescript"), 1);
2985
+ function getLastLeadingDocCommentRange(node, sourceFile) {
2986
+ const ranges = ts3.getLeadingCommentRanges(sourceFile.text, node.getFullStart()) ?? [];
2987
+ const docRanges = ranges.filter(
2988
+ (range) => sourceFile.text.slice(range.pos, range.end).startsWith("/**")
2989
+ );
2990
+ return docRanges.length === 0 ? null : docRanges[docRanges.length - 1] ?? null;
2991
+ }
2992
+ function getSubjectType(node, checker) {
2993
+ if (ts3.isClassDeclaration(node) || ts3.isInterfaceDeclaration(node) || ts3.isTypeAliasDeclaration(node) || ts3.isPropertyDeclaration(node) || ts3.isPropertySignature(node) || ts3.isMethodDeclaration(node) || ts3.isFunctionDeclaration(node) || ts3.isVariableDeclaration(node) || ts3.isParameter(node)) {
2994
+ return checker.getTypeAtLocation(node);
2995
+ }
2996
+ return void 0;
2997
+ }
2998
+ function getHostType(node, checker) {
2999
+ const parent = node.parent;
3000
+ if (ts3.isClassDeclaration(parent) || ts3.isInterfaceDeclaration(parent) || ts3.isTypeLiteralNode(parent) || ts3.isTypeAliasDeclaration(parent)) {
3001
+ return checker.getTypeAtLocation(parent);
3002
+ }
3003
+ return getSubjectType(node, checker);
3004
+ }
3005
+ function findDeclarationForCommentOffset(sourceFile, offset) {
3006
+ let bestMatch = null;
3007
+ const visit = (node) => {
3008
+ if (resolveDeclarationPlacement(node) !== null) {
3009
+ const range = getLastLeadingDocCommentRange(node, sourceFile);
3010
+ if (range !== null && offset >= range.pos && offset <= range.end) {
3011
+ if (bestMatch === null || node.getWidth(sourceFile) < bestMatch.getWidth(sourceFile)) {
3012
+ bestMatch = node;
3013
+ }
3014
+ }
3015
+ }
3016
+ ts3.forEachChild(node, visit);
3017
+ };
3018
+ visit(sourceFile);
3019
+ return bestMatch;
3020
+ }
3021
+
3022
+ // src/semantic-protocol.ts
3023
+ var FORMSPEC_ANALYSIS_PROTOCOL_VERSION = 1;
3024
+ var FORMSPEC_ANALYSIS_SCHEMA_VERSION = 1;
3025
+ function isObjectRecord(value) {
3026
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3027
+ }
3028
+ function isCommentSpan(value) {
3029
+ if (!isObjectRecord(value)) {
3030
+ return false;
3031
+ }
3032
+ const candidate = value;
3033
+ return typeof candidate.start === "number" && typeof candidate.end === "number";
3034
+ }
3035
+ function isStringArray(value) {
3036
+ return Array.isArray(value) && value.every((entry) => typeof entry === "string");
3037
+ }
3038
+ function isPlacementArray(value) {
3039
+ return isStringArray(value);
3040
+ }
3041
+ function isTargetKindArray(value) {
3042
+ return isStringArray(value);
3043
+ }
3044
+ function isIpcEndpoint(value) {
3045
+ if (!isObjectRecord(value)) {
3046
+ return false;
3047
+ }
3048
+ const candidate = value;
3049
+ return (candidate.kind === "unix-socket" || candidate.kind === "windows-pipe") && typeof candidate.address === "string";
3050
+ }
3051
+ function isSerializedTagDefinition(value) {
3052
+ if (!isObjectRecord(value)) {
3053
+ return false;
3054
+ }
3055
+ const candidate = value;
3056
+ return typeof candidate.canonicalName === "string" && typeof candidate.completionDetail === "string" && typeof candidate.hoverMarkdown === "string";
3057
+ }
3058
+ function isSerializedTagSignature(value) {
3059
+ if (!isObjectRecord(value)) {
3060
+ return false;
3061
+ }
3062
+ const candidate = value;
3063
+ return typeof candidate.label === "string" && isPlacementArray(candidate.placements);
3064
+ }
3065
+ function isSerializedCommentTargetSpecifier(value) {
3066
+ if (!isObjectRecord(value)) {
3067
+ return false;
3068
+ }
3069
+ const candidate = value;
3070
+ return typeof candidate.rawText === "string" && typeof candidate.valid === "boolean" && typeof candidate.kind === "string" && isCommentSpan(candidate.fullSpan) && isCommentSpan(candidate.colonSpan) && isCommentSpan(candidate.span);
3071
+ }
3072
+ function isSerializedTagSemanticContext(value) {
3073
+ if (!isObjectRecord(value)) {
3074
+ return false;
3075
+ }
3076
+ const candidate = value;
3077
+ return typeof candidate.tagName === "string" && (candidate.tagDefinition === null || isSerializedTagDefinition(candidate.tagDefinition)) && (candidate.placement === null || typeof candidate.placement === "string") && isTargetKindArray(candidate.supportedTargets) && isStringArray(candidate.targetCompletions) && isStringArray(candidate.compatiblePathTargets) && isStringArray(candidate.valueLabels) && Array.isArray(candidate.signatures) && candidate.signatures.every(isSerializedTagSignature) && (candidate.tagHoverMarkdown === null || typeof candidate.tagHoverMarkdown === "string") && (candidate.targetHoverMarkdown === null || typeof candidate.targetHoverMarkdown === "string") && (candidate.argumentHoverMarkdown === null || typeof candidate.argumentHoverMarkdown === "string");
3078
+ }
3079
+ function isSerializedCompletionContext(value) {
3080
+ if (!isObjectRecord(value)) {
3081
+ return false;
3082
+ }
3083
+ const candidate = value;
3084
+ if (typeof candidate.kind !== "string") {
3085
+ return false;
3086
+ }
3087
+ switch (candidate.kind) {
3088
+ case "tag-name":
3089
+ return typeof candidate.prefix === "string" && Array.isArray(candidate.availableTags) ? candidate.availableTags.every(isSerializedTagDefinition) : false;
3090
+ case "target":
3091
+ return isSerializedTagSemanticContext(candidate.semantic);
3092
+ case "argument":
3093
+ return isSerializedTagSemanticContext(candidate.semantic) && isStringArray(candidate.valueLabels);
3094
+ case "none":
3095
+ return true;
3096
+ default:
3097
+ return false;
3098
+ }
3099
+ }
3100
+ function isSerializedHoverInfo(value) {
3101
+ if (!isObjectRecord(value)) {
3102
+ return false;
3103
+ }
3104
+ const candidate = value;
3105
+ return (candidate.kind === "tag-name" || candidate.kind === "target" || candidate.kind === "argument") && typeof candidate.markdown === "string";
3106
+ }
3107
+ function hasCurrentProtocolVersion(value) {
3108
+ return isObjectRecord(value) && value["protocolVersion"] === FORMSPEC_ANALYSIS_PROTOCOL_VERSION;
3109
+ }
3110
+ function isAnalysisDiagnostic(value) {
3111
+ if (!isObjectRecord(value)) {
3112
+ return false;
3113
+ }
3114
+ const candidate = value;
3115
+ return typeof candidate.code === "string" && typeof candidate.message === "string" && isCommentSpan(candidate.range) && (candidate.severity === "error" || candidate.severity === "warning" || candidate.severity === "info");
3116
+ }
3117
+ function isAnalysisTagSnapshot(value) {
3118
+ if (!isObjectRecord(value)) {
3119
+ return false;
3120
+ }
3121
+ const candidate = value;
3122
+ return typeof candidate.rawTagName === "string" && typeof candidate.normalizedTagName === "string" && typeof candidate.recognized === "boolean" && isCommentSpan(candidate.fullSpan) && isCommentSpan(candidate.tagNameSpan) && (candidate.payloadSpan === null || isCommentSpan(candidate.payloadSpan)) && (candidate.target === null || isSerializedCommentTargetSpecifier(candidate.target)) && (candidate.argumentSpan === null || isCommentSpan(candidate.argumentSpan)) && typeof candidate.argumentText === "string" && isSerializedTagSemanticContext(candidate.semantic);
3123
+ }
3124
+ function isAnalysisCommentSnapshot(value) {
3125
+ if (!isObjectRecord(value)) {
3126
+ return false;
3127
+ }
3128
+ const candidate = value;
3129
+ return isCommentSpan(candidate.commentSpan) && isCommentSpan(candidate.declarationSpan) && (candidate.placement === null || typeof candidate.placement === "string") && (candidate.subjectType === null || typeof candidate.subjectType === "string") && (candidate.hostType === null || typeof candidate.hostType === "string") && Array.isArray(candidate.tags) && candidate.tags.every(isAnalysisTagSnapshot);
3130
+ }
3131
+ function isAnalysisFileSnapshot(value) {
3132
+ if (!isObjectRecord(value)) {
3133
+ return false;
3134
+ }
3135
+ const candidate = value;
3136
+ return typeof candidate.filePath === "string" && typeof candidate.sourceHash === "string" && typeof candidate.generatedAt === "string" && Array.isArray(candidate.comments) && candidate.comments.every(isAnalysisCommentSnapshot) && Array.isArray(candidate.diagnostics) && candidate.diagnostics.every(isAnalysisDiagnostic);
3137
+ }
3138
+ function isFormSpecAnalysisManifest(value) {
3139
+ if (!isObjectRecord(value)) {
3140
+ return false;
3141
+ }
3142
+ const candidate = value;
3143
+ return candidate.protocolVersion === FORMSPEC_ANALYSIS_PROTOCOL_VERSION && candidate.analysisSchemaVersion === FORMSPEC_ANALYSIS_SCHEMA_VERSION && typeof candidate.workspaceRoot === "string" && typeof candidate.workspaceId === "string" && isIpcEndpoint(candidate.endpoint) && typeof candidate.typescriptVersion === "string" && typeof candidate.extensionFingerprint === "string" && typeof candidate.generation === "number" && typeof candidate.updatedAt === "string";
3144
+ }
3145
+ function isFormSpecSemanticQuery(value) {
3146
+ if (!hasCurrentProtocolVersion(value)) {
3147
+ return false;
3148
+ }
3149
+ const candidate = value;
3150
+ if (typeof candidate.kind !== "string") {
3151
+ return false;
3152
+ }
3153
+ switch (candidate.kind) {
3154
+ case "health":
3155
+ return true;
3156
+ case "completion":
3157
+ case "hover":
3158
+ return typeof candidate.filePath === "string" && typeof candidate.offset === "number";
3159
+ case "diagnostics":
3160
+ case "file-snapshot":
3161
+ return typeof candidate.filePath === "string";
3162
+ default:
3163
+ return false;
3164
+ }
3165
+ }
3166
+ function isFormSpecSemanticResponse(value) {
3167
+ if (!hasCurrentProtocolVersion(value)) {
3168
+ return false;
3169
+ }
3170
+ const candidate = value;
3171
+ if (typeof candidate.kind !== "string") {
3172
+ return false;
3173
+ }
3174
+ switch (candidate.kind) {
3175
+ case "health":
3176
+ return isFormSpecAnalysisManifest(candidate.manifest);
3177
+ case "completion":
3178
+ return typeof candidate.sourceHash === "string" && isSerializedCompletionContext(candidate.context);
3179
+ case "hover":
3180
+ return typeof candidate.sourceHash === "string" && (candidate.hover === null || isSerializedHoverInfo(candidate.hover));
3181
+ case "diagnostics":
3182
+ return typeof candidate.sourceHash === "string" && Array.isArray(candidate.diagnostics) && candidate.diagnostics.every(isAnalysisDiagnostic);
3183
+ case "file-snapshot":
3184
+ return candidate.snapshot === null || isAnalysisFileSnapshot(candidate.snapshot);
3185
+ case "error":
3186
+ return typeof candidate.error === "string";
3187
+ default:
3188
+ return false;
3189
+ }
3190
+ }
3191
+ function computeFormSpecTextHash(text) {
3192
+ let hash = 2166136261;
3193
+ for (let index = 0; index < text.length; index += 1) {
3194
+ hash ^= text.charCodeAt(index);
3195
+ hash = Math.imul(hash, 16777619);
3196
+ }
3197
+ return (hash >>> 0).toString(16).padStart(8, "0");
3198
+ }
3199
+ function serializeCommentTargetSpecifier(target) {
3200
+ if (target === null) {
3201
+ return null;
3202
+ }
3203
+ return {
3204
+ rawText: target.rawText,
3205
+ valid: target.valid,
3206
+ kind: target.kind,
3207
+ fullSpan: target.fullSpan,
3208
+ colonSpan: target.colonSpan,
3209
+ span: target.span
3210
+ };
3211
+ }
3212
+ function serializeCommentTagSemanticContext(semantic) {
3213
+ return {
3214
+ tagName: semantic.tag.normalizedTagName,
3215
+ tagDefinition: semantic.tagDefinition === null ? null : {
3216
+ canonicalName: semantic.tagDefinition.canonicalName,
3217
+ completionDetail: semantic.tagDefinition.completionDetail,
3218
+ hoverMarkdown: semantic.tagDefinition.hoverMarkdown
3219
+ },
3220
+ placement: semantic.placement,
3221
+ supportedTargets: semantic.supportedTargets,
3222
+ targetCompletions: semantic.targetCompletions,
3223
+ compatiblePathTargets: semantic.compatiblePathTargets,
3224
+ valueLabels: semantic.valueLabels,
3225
+ signatures: semantic.signatures.map((signature) => ({
3226
+ label: signature.label,
3227
+ placements: signature.placements
3228
+ })),
3229
+ tagHoverMarkdown: semantic.tagHoverMarkdown,
3230
+ targetHoverMarkdown: semantic.targetHoverMarkdown,
3231
+ argumentHoverMarkdown: semantic.argumentHoverMarkdown
3232
+ };
3233
+ }
3234
+ function serializeCompletionContext(context) {
3235
+ switch (context.kind) {
3236
+ case "tag-name":
3237
+ return {
3238
+ kind: "tag-name",
3239
+ prefix: context.prefix,
3240
+ availableTags: context.availableTags.map((tag) => ({
3241
+ canonicalName: tag.canonicalName,
3242
+ completionDetail: tag.completionDetail,
3243
+ hoverMarkdown: tag.hoverMarkdown
3244
+ }))
3245
+ };
3246
+ case "target":
3247
+ return {
3248
+ kind: "target",
3249
+ semantic: serializeCommentTagSemanticContext(context.semantic)
3250
+ };
3251
+ case "argument":
3252
+ return {
3253
+ kind: "argument",
3254
+ semantic: serializeCommentTagSemanticContext(context.semantic),
3255
+ valueLabels: context.valueLabels
3256
+ };
3257
+ case "none":
3258
+ return { kind: "none" };
3259
+ default: {
3260
+ const exhaustive = context;
3261
+ return exhaustive;
3262
+ }
3263
+ }
3264
+ }
3265
+ function serializeHoverInfo(hover) {
3266
+ return hover === null ? null : {
3267
+ kind: hover.kind,
3268
+ markdown: hover.markdown
3269
+ };
3270
+ }
3271
+ function serializeParsedCommentTag(tag, semantic) {
3272
+ return {
3273
+ rawTagName: tag.rawTagName,
3274
+ normalizedTagName: tag.normalizedTagName,
3275
+ recognized: tag.recognized,
3276
+ fullSpan: tag.fullSpan,
3277
+ tagNameSpan: tag.tagNameSpan,
3278
+ payloadSpan: tag.payloadSpan,
3279
+ target: serializeCommentTargetSpecifier(tag.target),
3280
+ argumentSpan: tag.argumentSpan,
3281
+ argumentText: tag.argumentText,
3282
+ semantic: serializeCommentTagSemanticContext(semantic)
3283
+ };
3284
+ }
3285
+
3286
+ // src/file-snapshots.ts
3287
+ function spanFromPos(start, end) {
3288
+ return { start, end };
3289
+ }
3290
+ function typeToString(type, checker) {
3291
+ if (type === void 0) {
3292
+ return null;
3293
+ }
3294
+ return checker.typeToString(type, void 0, ts4.TypeFormatFlags.NoTruncation);
3295
+ }
3296
+ function supportingDeclarationsForType(type) {
3297
+ if (type === void 0) {
3298
+ return [];
3299
+ }
3300
+ const symbol = type.aliasSymbol ?? type.getSymbol();
3301
+ const declarations = symbol?.declarations ?? [];
3302
+ return declarations.map(
3303
+ (declaration) => declaration.getSourceFile().text.slice(declaration.getFullStart(), declaration.getEnd())
3304
+ ).filter((declarationText) => declarationText.trim().length > 0);
3305
+ }
3306
+ function getSyntheticTargetForTag(tag) {
3307
+ if (tag.target === null) {
3308
+ return null;
3309
+ }
3310
+ switch (tag.target.kind) {
3311
+ case "path":
3312
+ case "member":
3313
+ case "variant":
3314
+ return {
3315
+ kind: tag.target.kind,
3316
+ text: tag.target.rawText
3317
+ };
3318
+ case "ambiguous":
3319
+ return {
3320
+ kind: "path",
3321
+ text: tag.target.rawText
3322
+ };
3323
+ default: {
3324
+ const exhaustive = tag.target.kind;
3325
+ return exhaustive;
3326
+ }
3327
+ }
3328
+ }
3329
+ function getArgumentExpression(argumentText, valueLabels, capabilityTargets) {
3330
+ const trimmed = argumentText.trim();
3331
+ if (trimmed === "") {
3332
+ return null;
3333
+ }
3334
+ if (valueLabels.some((label) => label.includes("number") || label.includes("integer"))) {
3335
+ return trimmed;
3336
+ }
3337
+ if (valueLabels.some((label) => label.includes("boolean"))) {
3338
+ return trimmed === "true" || trimmed === "false" ? trimmed : null;
3339
+ }
3340
+ if (valueLabels.some((label) => label.includes("json"))) {
3341
+ try {
3342
+ return JSON.stringify(JSON.parse(trimmed));
3343
+ } catch {
3344
+ return null;
3345
+ }
3346
+ }
3347
+ if (valueLabels.some((label) => label.includes("condition"))) {
3348
+ return "undefined as unknown as FormSpecCondition";
3349
+ }
3350
+ if (capabilityTargets.length > 0 || valueLabels.some((label) => label.includes("string"))) {
3351
+ return JSON.stringify(trimmed);
3352
+ }
3353
+ return JSON.stringify(trimmed);
3354
+ }
3355
+ function diagnosticSeverity(code) {
3356
+ switch (code) {
3357
+ case "INVALID_TAG_ARGUMENT":
3358
+ case "INVALID_TAG_PLACEMENT":
3359
+ case "TYPE_MISMATCH":
3360
+ case "UNKNOWN_PATH_TARGET":
3361
+ return "error";
3362
+ default:
3363
+ return "warning";
3364
+ }
3365
+ }
3366
+ function buildTagDiagnostics(sourceFile, checker, placement, hostType, subjectType, commentTags, semanticOptions) {
3367
+ if (placement === null || subjectType === void 0) {
3368
+ return [];
3369
+ }
3370
+ const diagnostics = [];
3371
+ const hostTypeText = typeToString(hostType, checker) ?? "unknown";
3372
+ const subjectTypeText = typeToString(subjectType, checker) ?? "unknown";
3373
+ const supportingDeclarations = [
3374
+ ...supportingDeclarationsForType(hostType),
3375
+ ...supportingDeclarationsForType(subjectType)
3376
+ ];
3377
+ for (const tag of commentTags) {
3378
+ const semantic = getCommentTagSemanticContext(tag, semanticOptions);
3379
+ if (semantic.tagDefinition === null) {
3380
+ continue;
3381
+ }
3382
+ const target = getSyntheticTargetForTag(tag);
3383
+ const argumentExpression = getArgumentExpression(
3384
+ tag.argumentText,
3385
+ semantic.valueLabels,
3386
+ semantic.compatiblePathTargets
3387
+ );
3388
+ try {
3389
+ const result = checkSyntheticTagApplication({
3390
+ tagName: tag.normalizedTagName,
3391
+ placement,
3392
+ hostType: hostTypeText,
3393
+ subjectType: subjectTypeText,
3394
+ supportingDeclarations,
3395
+ ...target === null ? {} : { target },
3396
+ ...argumentExpression === null ? {} : { argumentExpression },
3397
+ ...semanticOptions.extensions === void 0 ? {} : { extensions: semanticOptions.extensions }
3398
+ });
3399
+ for (const diagnostic of result.diagnostics) {
3400
+ const code = target !== null && diagnostic.message.includes("not assignable") ? target.kind === "path" ? "UNKNOWN_PATH_TARGET" : "TYPE_MISMATCH" : diagnostic.message.includes("Expected") ? "INVALID_TAG_ARGUMENT" : diagnostic.message.includes("No overload") ? "INVALID_TAG_PLACEMENT" : "TYPE_MISMATCH";
3401
+ diagnostics.push({
3402
+ code,
3403
+ message: diagnostic.message,
3404
+ range: tag.fullSpan,
3405
+ severity: diagnosticSeverity(code)
3406
+ });
3407
+ }
3408
+ } catch (error) {
3409
+ diagnostics.push({
3410
+ code: "INVALID_TAG_PLACEMENT",
3411
+ message: error instanceof Error ? error.message : String(error),
3412
+ range: tag.fullSpan,
3413
+ severity: "error"
3414
+ });
3415
+ }
3416
+ }
3417
+ return diagnostics;
3418
+ }
3419
+ function buildCommentSnapshot(node, sourceFile, checker, extensions) {
3420
+ const docComment = getLastLeadingDocCommentRange(node, sourceFile);
3421
+ if (docComment === null) {
3422
+ return null;
3423
+ }
3424
+ const commentText = sourceFile.text.slice(docComment.pos, docComment.end);
3425
+ const parsed = parseCommentBlock(commentText, {
3426
+ offset: docComment.pos,
3427
+ ...extensions === void 0 ? {} : { extensions }
3428
+ });
3429
+ if (parsed.tags.length === 0) {
3430
+ return null;
3431
+ }
3432
+ const placement = resolveDeclarationPlacement(node);
3433
+ const subjectType = getSubjectType(node, checker);
3434
+ const hostType = getHostType(node, checker);
3435
+ const semanticOptions = {
3436
+ checker,
3437
+ ...subjectType === void 0 ? {} : { subjectType },
3438
+ ...placement === null ? {} : { placement },
3439
+ ...extensions === void 0 ? {} : { extensions }
3440
+ };
3441
+ const tags = parsed.tags.map(
3442
+ (tag) => serializeParsedCommentTag(tag, getCommentTagSemanticContext(tag, semanticOptions))
3443
+ );
3444
+ return {
3445
+ commentSpan: spanFromPos(docComment.pos, docComment.end),
3446
+ declarationSpan: spanFromPos(node.getStart(sourceFile), node.getEnd()),
3447
+ placement,
3448
+ subjectType: typeToString(subjectType, checker),
3449
+ hostType: typeToString(hostType, checker),
3450
+ tags
3451
+ };
3452
+ }
3453
+ function buildFormSpecAnalysisFileSnapshot(sourceFile, options) {
3454
+ const comments = [];
3455
+ const diagnostics = [];
3456
+ const visit = (node) => {
3457
+ const placement = resolveDeclarationPlacement(node);
3458
+ if (placement !== null) {
3459
+ const snapshot = buildCommentSnapshot(node, sourceFile, options.checker, options.extensions);
3460
+ if (snapshot !== null) {
3461
+ comments.push(snapshot);
3462
+ const subjectType = getSubjectType(node, options.checker);
3463
+ const hostType = getHostType(node, options.checker);
3464
+ diagnostics.push(
3465
+ ...buildTagDiagnostics(
3466
+ sourceFile,
3467
+ options.checker,
3468
+ placement,
3469
+ hostType,
3470
+ subjectType,
3471
+ snapshot.tags.map((tag) => ({
3472
+ rawTagName: tag.rawTagName,
3473
+ normalizedTagName: tag.normalizedTagName,
3474
+ recognized: tag.recognized,
3475
+ fullSpan: tag.fullSpan,
3476
+ tagNameSpan: tag.tagNameSpan,
3477
+ payloadSpan: tag.payloadSpan,
3478
+ colonSpan: tag.target?.colonSpan ?? null,
3479
+ target: tag.target === null ? null : {
3480
+ rawText: tag.target.rawText,
3481
+ valid: tag.target.valid,
3482
+ kind: tag.target.kind,
3483
+ fullSpan: tag.target.fullSpan,
3484
+ colonSpan: tag.target.colonSpan,
3485
+ span: tag.target.span,
3486
+ path: null
3487
+ },
3488
+ argumentSpan: tag.argumentSpan,
3489
+ argumentText: tag.argumentText
3490
+ })),
3491
+ {
3492
+ checker: options.checker,
3493
+ ...subjectType === void 0 ? {} : { subjectType },
3494
+ placement,
3495
+ ...options.extensions === void 0 ? {} : { extensions: options.extensions }
3496
+ }
3497
+ )
3498
+ );
3499
+ }
3500
+ }
3501
+ ts4.forEachChild(node, visit);
3502
+ };
3503
+ visit(sourceFile);
3504
+ return {
3505
+ filePath: sourceFile.fileName,
3506
+ sourceHash: computeFormSpecTextHash(sourceFile.text),
3507
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
3508
+ comments,
3509
+ diagnostics
3510
+ };
3511
+ }
3512
+
3513
+ // src/workspace-runtime.ts
3514
+ var import_node_path = __toESM(require("path"), 1);
3515
+ function getFormSpecWorkspaceId(workspaceRoot) {
3516
+ return computeFormSpecTextHash(workspaceRoot);
3517
+ }
3518
+ function getFormSpecWorkspaceRuntimeDirectory(workspaceRoot) {
3519
+ return import_node_path.default.join(workspaceRoot, ".cache", "formspec", "tooling");
3520
+ }
3521
+ function getFormSpecManifestPath(workspaceRoot) {
3522
+ return import_node_path.default.join(getFormSpecWorkspaceRuntimeDirectory(workspaceRoot), "manifest.json");
3523
+ }
3524
+ // Annotate the CommonJS export names for ESM import in node:
3525
+ 0 && (module.exports = {
3526
+ FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
3527
+ FORMSPEC_ANALYSIS_SCHEMA_VERSION,
3528
+ analyzeConstraintTargets,
3529
+ buildConstraintTargetStates,
3530
+ buildFormSpecAnalysisFileSnapshot,
3531
+ buildSyntheticHelperPrelude,
3532
+ checkSyntheticTagApplication,
3533
+ collectCompatiblePathTargets,
3534
+ collectReferencedTypeAnnotations,
3535
+ collectReferencedTypeConstraints,
3536
+ computeFormSpecTextHash,
3537
+ dereferenceAnalysisType,
3538
+ extractPathTarget,
3539
+ findCommentTagAtOffset,
3540
+ findDeclarationForCommentOffset,
3541
+ findEnclosingDocComment,
3542
+ formatConstraintTargetName,
3543
+ formatPathTarget,
3544
+ getAllTagDefinitions,
3545
+ getCommentCompletionContextAtOffset,
3546
+ getCommentCursorTargetAtOffset,
3547
+ getCommentHoverInfoAtOffset,
3548
+ getCommentTagSemanticContext,
3549
+ getConstraintTagDefinitions,
3550
+ getFormSpecManifestPath,
3551
+ getFormSpecWorkspaceId,
3552
+ getFormSpecWorkspaceRuntimeDirectory,
3553
+ getHostType,
3554
+ getLastLeadingDocCommentRange,
3555
+ getMatchingTagSignatures,
3556
+ getSemanticCommentCompletionContextAtOffset,
3557
+ getSubjectType,
3558
+ getTagCompletionPrefixAtOffset,
3559
+ getTagDefinition,
3560
+ getTagHoverMarkdown,
3561
+ getTypeSemanticCapabilities,
3562
+ hasTypeSemanticCapability,
3563
+ isFormSpecAnalysisManifest,
3564
+ isFormSpecSemanticQuery,
3565
+ isFormSpecSemanticResponse,
3566
+ lowerTagApplicationToSyntheticCall,
3567
+ normalizeFormSpecTagName,
3568
+ parseCommentBlock,
3569
+ parseConstraintTagValue,
3570
+ parseDefaultValueTagValue,
3571
+ parseTagSyntax,
3572
+ resolveConstraintTargetState,
3573
+ resolveDeclarationPlacement,
3574
+ resolvePathTargetType,
3575
+ serializeCommentTagSemanticContext,
3576
+ serializeCommentTargetSpecifier,
3577
+ serializeCompletionContext,
3578
+ serializeHoverInfo,
3579
+ serializeParsedCommentTag,
3580
+ sliceCommentSpan
3581
+ });
3582
+ //# sourceMappingURL=index.cjs.map