@contentful/experience-design-system-types 2.42.1-dev-build-f69db6bf.0 → 2.43.0-alpha.0

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 (94) hide show
  1. package/README.md +15 -0
  2. package/dist/package.json +1 -1
  3. package/dist/src/agent-process-lifecycle.d.ts +26 -0
  4. package/dist/src/agent-process-lifecycle.js +171 -0
  5. package/dist/src/agent-prompt-artifact.d.ts +8 -0
  6. package/dist/src/agent-prompt-artifact.js +37 -0
  7. package/dist/src/agent-prompt-transport.d.ts +1 -0
  8. package/dist/src/agent-prompt-transport.js +4 -0
  9. package/dist/src/cdf/build.d.ts +9 -2
  10. package/dist/src/cdf/build.js +134 -17
  11. package/dist/src/cdf/finite-values.d.ts +8 -0
  12. package/dist/src/cdf/finite-values.js +21 -0
  13. package/dist/src/cdf/index.d.ts +2 -1
  14. package/dist/src/cdf/index.js +1 -0
  15. package/dist/src/cdf/property-validation.d.ts +16 -0
  16. package/dist/src/cdf/property-validation.js +211 -0
  17. package/dist/src/cdf/representation-validation.d.ts +6 -0
  18. package/dist/src/cdf/representation-validation.js +182 -0
  19. package/dist/src/cdf/schema.d.ts +778 -20
  20. package/dist/src/cdf/schema.js +362 -5
  21. package/dist/src/cdf/types.d.ts +57 -59
  22. package/dist/src/cdf/types.js +72 -15
  23. package/dist/src/cdf/validate.js +31 -1
  24. package/dist/src/cdf/vocabularies.d.ts +1 -1
  25. package/dist/src/cdf/vocabularies.js +9 -1
  26. package/dist/src/config/index.js +6 -3
  27. package/dist/src/exploration/constants.d.ts +10 -0
  28. package/dist/src/exploration/constants.js +18 -0
  29. package/dist/src/exploration/index.d.ts +3 -0
  30. package/dist/src/exploration/index.js +3 -0
  31. package/dist/src/exploration/schemas.d.ts +201 -0
  32. package/dist/src/exploration/schemas.js +126 -0
  33. package/dist/src/exploration/types.d.ts +393 -0
  34. package/dist/src/exploration/types.js +1 -0
  35. package/dist/src/handoff/index.d.ts +1 -1
  36. package/dist/src/index.d.ts +15 -1
  37. package/dist/src/index.js +12 -4
  38. package/dist/src/property-contract/analysis-representation-candidates.d.ts +19 -0
  39. package/dist/src/property-contract/analysis-representation-candidates.js +45 -0
  40. package/dist/src/property-contract/classification.d.ts +38 -0
  41. package/dist/src/property-contract/classification.js +84 -0
  42. package/dist/src/property-contract/compact-model-components.d.ts +16 -0
  43. package/dist/src/property-contract/compact-model-components.js +113 -0
  44. package/dist/src/property-contract/content-digest.d.ts +2 -0
  45. package/dist/src/property-contract/content-digest.js +69 -0
  46. package/dist/src/property-contract/decision-protocol.d.ts +45 -0
  47. package/dist/src/property-contract/decision-protocol.js +251 -0
  48. package/dist/src/property-contract/definition-import-rules.d.ts +10 -0
  49. package/dist/src/property-contract/definition-import-rules.js +105 -0
  50. package/dist/src/property-contract/definition-import.d.ts +13 -0
  51. package/dist/src/property-contract/definition-import.js +52 -0
  52. package/dist/src/property-contract/discriminated-union-source.d.ts +17 -0
  53. package/dist/src/property-contract/discriminated-union-source.js +180 -0
  54. package/dist/src/property-contract/import-prerequisites.d.ts +70 -0
  55. package/dist/src/property-contract/import-prerequisites.js +208 -0
  56. package/dist/src/property-contract/index.d.ts +18 -1
  57. package/dist/src/property-contract/index.js +18 -6
  58. package/dist/src/property-contract/logical-source-graph.d.ts +36 -0
  59. package/dist/src/property-contract/logical-source-graph.js +240 -0
  60. package/dist/src/property-contract/mapper-readiness.d.ts +6 -0
  61. package/dist/src/property-contract/mapper-readiness.js +34 -0
  62. package/dist/src/property-contract/model-exploration.d.ts +42 -0
  63. package/dist/src/property-contract/model-exploration.js +94 -0
  64. package/dist/src/property-contract/native-source-region.d.ts +5 -0
  65. package/dist/src/property-contract/native-source-region.js +125 -0
  66. package/dist/src/property-contract/null-handling.d.ts +10 -0
  67. package/dist/src/property-contract/null-handling.js +45 -0
  68. package/dist/src/property-contract/physical-property-import.d.ts +6 -0
  69. package/dist/src/property-contract/physical-property-import.js +55 -0
  70. package/dist/src/property-contract/property-decision.d.ts +14 -0
  71. package/dist/src/property-contract/property-decision.js +155 -0
  72. package/dist/src/property-contract/property-model.d.ts +197 -0
  73. package/dist/src/property-contract/property-model.js +833 -0
  74. package/dist/src/property-contract/representation-plan.d.ts +133 -0
  75. package/dist/src/property-contract/representation-plan.js +756 -0
  76. package/dist/src/property-contract/representation-proposal.d.ts +65 -0
  77. package/dist/src/property-contract/representation-proposal.js +204 -0
  78. package/dist/src/property-contract/resource-link-schema.d.ts +55 -0
  79. package/dist/src/property-contract/resource-link-schema.js +28 -0
  80. package/dist/src/property-contract/source-representation.d.ts +9 -0
  81. package/dist/src/property-contract/source-representation.js +79 -0
  82. package/dist/src/property-contract/target-definition.d.ts +17 -16
  83. package/dist/src/property-contract/target-definition.js +4 -26
  84. package/dist/src/schema-export.d.ts +30 -0
  85. package/dist/src/schema-export.js +285 -0
  86. package/dist/src/shared-source.d.ts +105 -0
  87. package/dist/src/shared-source.js +1 -0
  88. package/dist/src/source-input-association.d.ts +23 -0
  89. package/dist/src/source-input-association.js +52 -0
  90. package/dist/src/sources-api/preview/index.d.ts +37 -3
  91. package/dist/src/sources-api/preview/index.js +39 -1
  92. package/dist/src/sources-api/preview/schema.d.ts +2 -0
  93. package/dist/src/sources-api/preview/schema.js +74 -0
  94. package/package.json +1 -1
@@ -0,0 +1,240 @@
1
+ import { contentDigest } from './content-digest.js';
2
+ import { cloneJson, own } from './json-value.js';
3
+ import { sourceDiscriminatedUnion } from './discriminated-union-source.js';
4
+ import { validateSourceGraph, validateSourceValue, } from './property-model.js';
5
+ /** Only own record fields identify a default; an array sample cannot establish item defaults. */
6
+ export function sourceDefaultAtPath(model, path) {
7
+ if (model.source.default?.state !== 'value' || !own(model.source.default, 'value'))
8
+ return;
9
+ let value = model.source.default.value;
10
+ const nodes = new Map(model.source.graph.nodes.map((node) => [node.id, node]));
11
+ let node = nodes.get(model.source.graph.rootId);
12
+ const resolve = () => {
13
+ const seen = new Set();
14
+ while (node?.kind === 'reference' && !seen.has(node.id)) {
15
+ seen.add(node.id);
16
+ node = nodes.get(node.alternatives?.[0] ?? '');
17
+ }
18
+ };
19
+ for (const segment of path) {
20
+ resolve();
21
+ if (segment.kind === 'union' || segment.kind === 'intersection') {
22
+ const memberId = typeof segment.key === 'number' ? node?.alternatives?.[segment.key] : undefined;
23
+ if (!memberId ||
24
+ (segment.kind === 'union' && validateSourceValue(model.source.graph, value, '', memberId).length))
25
+ return;
26
+ node = nodes.get(memberId);
27
+ continue;
28
+ }
29
+ if (segment.kind !== 'field' ||
30
+ typeof segment.key !== 'string' ||
31
+ !value ||
32
+ typeof value !== 'object' ||
33
+ Array.isArray(value) ||
34
+ !own(value, segment.key))
35
+ return;
36
+ value = value[segment.key];
37
+ node = nodes.get(node?.fields?.find((field) => field.key === segment.key)?.nodeId ?? '');
38
+ }
39
+ return { action: 'set', value: cloneJson(value) };
40
+ }
41
+ /** Retain shared references while expanding finite occurrences with distinct branch ownership. */
42
+ export function prepareLogicalSourceGraph(model) {
43
+ const source = model.source.graph;
44
+ const invalid = validateSourceGraph(source);
45
+ if (invalid.length)
46
+ throw new Error(invalid.map((issue) => issue.message).join('; '));
47
+ if (model.identity.nodeId !== source.rootId ||
48
+ Object.values(model.identity).some((value) => typeof value !== 'string' || !value))
49
+ throw new Error('Logical source root owner is incomplete or mismatched');
50
+ const byId = new Map(source.nodes.map((node) => [node.id, node]));
51
+ const parents = new Map();
52
+ for (const node of source.nodes)
53
+ for (const childId of [
54
+ ...(node.fields?.map((field) => field.nodeId) ?? []),
55
+ ...(node.items ?? []),
56
+ ...(node.alternatives ?? []),
57
+ ...(node.indexSignatures?.map((signature) => signature.valueNodeId) ?? []),
58
+ ])
59
+ parents.set(childId, [...(parents.get(childId) ?? []), node.id]);
60
+ const branchSensitive = new Set();
61
+ const pending = source.nodes.filter((node) => sourceDiscriminatedUnion(node, byId)).map((node) => node.id);
62
+ while (pending.length) {
63
+ const id = pending.pop();
64
+ if (branchSensitive.has(id))
65
+ continue;
66
+ branchSensitive.add(id);
67
+ pending.push(...(parents.get(id) ?? []));
68
+ }
69
+ const intrinsicReadonly = (id) => {
70
+ const seen = new Set();
71
+ let current = byId.get(id);
72
+ while (current && !seen.has(current.id)) {
73
+ seen.add(current.id);
74
+ if (current.readonly === true)
75
+ return true;
76
+ if (current.kind === 'reference' && current.alternatives?.length === 1) {
77
+ current = byId.get(current.alternatives[0]);
78
+ }
79
+ else if (current.kind === 'union') {
80
+ const values = (current.alternatives ?? [])
81
+ .map((child) => byId.get(child))
82
+ .filter((child) => !['null', 'undefined', 'never'].includes(child.kind));
83
+ if (values.length !== 1)
84
+ return false;
85
+ current = values[0];
86
+ }
87
+ else
88
+ return false;
89
+ }
90
+ return false;
91
+ };
92
+ const nodes = [];
93
+ const canonical = new Map();
94
+ const activeSources = new Set();
95
+ let expandedOccurrences = 0;
96
+ let occurrenceBoundReached = false;
97
+ const root = byId.get(source.rootId);
98
+ const rootPath = root.path ?? root.keyPath.map((key) => ({ kind: 'field', key }));
99
+ const visit = function* (request) {
100
+ const original = byId.get(request.sourceId);
101
+ const known = canonical.get(original.id);
102
+ const sharedReference = known &&
103
+ ((!request.branchConditions?.length && !branchSensitive.has(original.id)) || activeSources.has(original.id));
104
+ if (sharedReference && request.target)
105
+ return known;
106
+ const boundedOccurrence = known && !sharedReference && expandedOccurrences >= (source.limits.maxEdges ?? 4096);
107
+ const id = known
108
+ ? 'occurrence_' +
109
+ contentDigest({ owner: model.identity, sourceNodeId: original.id, path: request.path }).slice(0, 32)
110
+ : original.id;
111
+ const node = {
112
+ ...cloneJson(original),
113
+ id,
114
+ sourceNodeId: original.id,
115
+ path: cloneJson(request.path),
116
+ required: request.required,
117
+ readonly: request.readonly || intrinsicReadonly(original.id),
118
+ occurrenceReadonly: request.readonly,
119
+ intrinsicReadonly: intrinsicReadonly(original.id),
120
+ ...(request.declarations ? { declarations: cloneJson(request.declarations) } : {}),
121
+ ...(request.branchConditions?.length ? { branchConditions: cloneJson(request.branchConditions) } : {}),
122
+ };
123
+ const intent = sourceDefaultAtPath(model, request.path.slice(rootPath.length));
124
+ if (intent)
125
+ node.defaultIntent = intent;
126
+ nodes.push(node);
127
+ if (sharedReference || boundedOccurrence) {
128
+ node.kind = 'reference';
129
+ node.reason = boundedOccurrence ? 'branch-occurrence-expansion-bound' : 'shared-source-occurrence';
130
+ if (boundedOccurrence) {
131
+ node.coverage = 'partial';
132
+ occurrenceBoundReached = true;
133
+ }
134
+ node.alternatives = [known];
135
+ delete node.fields;
136
+ delete node.items;
137
+ delete node.indexSignatures;
138
+ delete node.tupleElements;
139
+ return id;
140
+ }
141
+ if (known)
142
+ expandedOccurrences += 1;
143
+ if (!known)
144
+ canonical.set(original.id, id);
145
+ activeSources.add(original.id);
146
+ const child = (sourceId, kind, key, required = request.required, readonly = false, declarations) => ({
147
+ sourceId,
148
+ path: [...request.path, { kind, key }],
149
+ required,
150
+ readonly,
151
+ ...(declarations ? { declarations } : {}),
152
+ ...(request.branchConditions?.length ? { branchConditions: request.branchConditions } : {}),
153
+ });
154
+ if (original.fields) {
155
+ node.fields = [];
156
+ for (const field of original.fields) {
157
+ const nodeId = yield child(field.nodeId, 'field', field.key, field.required, field.readonly ?? false, field.declarations);
158
+ node.fields.push({ ...cloneJson(field), nodeId, path: [...request.path, { kind: 'field', key: field.key }] });
159
+ }
160
+ }
161
+ if (original.items) {
162
+ node.items = [];
163
+ node.tupleElements = original.tupleElements ? [] : undefined;
164
+ if (!original.tupleElements)
165
+ delete node.tupleElements;
166
+ for (const [index, sourceId] of original.items.entries()) {
167
+ const tuple = original.tupleElements?.[index];
168
+ const nodeId = yield child(sourceId, original.kind === 'tuple' ? 'tuple' : 'item', index, tuple ? !tuple.optional : true);
169
+ node.items.push(nodeId);
170
+ if (tuple)
171
+ node.tupleElements.push({ ...cloneJson(tuple), nodeId });
172
+ }
173
+ }
174
+ if (original.alternatives) {
175
+ node.alternatives = [];
176
+ const union = sourceDiscriminatedUnion(original, byId);
177
+ if (union)
178
+ node.unionBranches = { discriminator: union.discriminator, branches: [] };
179
+ for (const [index, sourceId] of original.alternatives.entries()) {
180
+ const branch = union?.branches.find((candidate) => candidate.memberIndex === index);
181
+ const childRequest = child(sourceId, original.kind === 'intersection' ? 'intersection' : 'union', index);
182
+ if (branch) {
183
+ childRequest.required = true;
184
+ childRequest.branchConditions = [
185
+ ...(request.branchConditions ?? []),
186
+ {
187
+ unionNodeId: id,
188
+ memberIndex: index,
189
+ sourceMemberNodeId: sourceId,
190
+ discriminator: union.discriminator,
191
+ tag: branch.tag,
192
+ },
193
+ ];
194
+ }
195
+ const nodeId = original.kind === 'reference'
196
+ ? yield {
197
+ sourceId,
198
+ path: request.path,
199
+ required: request.required,
200
+ readonly: request.readonly,
201
+ target: true,
202
+ ...(request.branchConditions?.length ? { branchConditions: request.branchConditions } : {}),
203
+ }
204
+ : yield childRequest;
205
+ node.alternatives.push(nodeId);
206
+ if (branch)
207
+ node.unionBranches.branches.push({ memberIndex: index, nodeId, sourceNodeId: sourceId, tag: branch.tag });
208
+ }
209
+ }
210
+ if (original.indexSignatures) {
211
+ node.indexSignatures = [];
212
+ for (const index of original.indexSignatures) {
213
+ const valueNodeId = yield child(index.valueNodeId, 'index', index.keyType, false);
214
+ node.indexSignatures.push({ ...cloneJson(index), valueNodeId });
215
+ }
216
+ }
217
+ activeSources.delete(original.id);
218
+ return id;
219
+ };
220
+ const frames = [
221
+ visit({ sourceId: source.rootId, path: rootPath, required: root.required, readonly: root.readonly ?? false }),
222
+ ];
223
+ let returned = '';
224
+ let rootId = '';
225
+ while (frames.length) {
226
+ const step = frames[frames.length - 1].next(returned);
227
+ returned = '';
228
+ if (step.done) {
229
+ frames.pop();
230
+ returned = step.value;
231
+ if (!frames.length)
232
+ rootId = returned;
233
+ }
234
+ else
235
+ frames.push(visit(step.value));
236
+ }
237
+ if (canonical.size !== source.nodes.length)
238
+ throw new Error('Logical compiler accounting is incomplete');
239
+ return { authority: 'compiler', rootId, nodes, coverage: occurrenceBoundReached ? 'partial' : source.coverage };
240
+ }
@@ -0,0 +1,6 @@
1
+ import type { CDFComponentEntry, CDFFieldDefinition } from '../cdf/types.js';
2
+ export declare function propertyMapperBlockers(definition: CDFFieldDefinition, path?: string, category?: "content" | "design" | "state" | "unattached" | undefined): string[];
3
+ export declare function mapperApplyBlockers(components: readonly {
4
+ key: string;
5
+ entry: CDFComponentEntry;
6
+ }[]): string[];
@@ -0,0 +1,34 @@
1
+ import { childPath, own } from './json-value.js';
2
+ /** This vocabulary describes the deployed transport, independently of desired file support. */
3
+ const DEPLOYED_CONTENT_TYPES = [
4
+ 'string',
5
+ 'richtext',
6
+ 'number',
7
+ 'media',
8
+ 'link',
9
+ 'token',
10
+ 'boolean',
11
+ 'array',
12
+ 'record',
13
+ ];
14
+ const DEPLOYED_DESIGN_TYPES = ['string', 'boolean', 'token'];
15
+ export function propertyMapperBlockers(definition, path = '', category = definition.$category) {
16
+ if (category !== 'content' && category !== 'design')
17
+ return [];
18
+ const types = category === 'design' ? DEPLOYED_DESIGN_TYPES : DEPLOYED_CONTENT_TYPES;
19
+ const blockers = types.includes(definition.$type) ? [] : [`${path} (${definition.$type} mapper unavailable)`];
20
+ if (own(definition, '$values'))
21
+ blockers.push(`${path}/$values (finite-choice mapper unavailable)`);
22
+ if (definition.$items)
23
+ blockers.push(...propertyMapperBlockers(definition.$items, `${path}/$items`, category));
24
+ if (definition.valueType)
25
+ blockers.push(...propertyMapperBlockers(definition.valueType, `${path}/valueType`, category));
26
+ for (const [index, member] of (definition.members ?? []).entries())
27
+ blockers.push(...propertyMapperBlockers(member, `${path}/members/${index}`, category));
28
+ for (const [key, field] of Object.entries(definition.$fields ?? {}))
29
+ blockers.push(...propertyMapperBlockers(field, childPath(`${path}/$fields`, key), category));
30
+ return blockers;
31
+ }
32
+ export function mapperApplyBlockers(components) {
33
+ return components.flatMap(({ key, entry }) => Object.entries(entry.$properties).flatMap(([name, definition]) => propertyMapperBlockers(definition, childPath(childPath('', key) + '/$properties', name))));
34
+ }
@@ -0,0 +1,42 @@
1
+ import { z } from 'zod';
2
+ import type { CDFComponentEntry } from '../cdf/types.js';
3
+ import type { LogicalSourceNode } from './logical-source-graph.js';
4
+ import type { LogicalPhysicalMapping, RepresentationPlan } from './representation-plan.js';
5
+ export declare const ModelComponentExplorationSchema: z.ZodObject<{
6
+ modelKey: z.ZodString;
7
+ nodeId: z.ZodString;
8
+ sourceRevision: z.ZodString;
9
+ name: z.ZodString;
10
+ description: z.ZodString;
11
+ properties: z.ZodArray<z.ZodObject<{
12
+ category: z.ZodEnum<{
13
+ content: "content";
14
+ design: "design";
15
+ }>;
16
+ authorability: z.ZodEnum<{
17
+ editable: "editable";
18
+ fixed: "fixed";
19
+ uncertain: "uncertain";
20
+ }>;
21
+ name: z.ZodString;
22
+ description: z.ZodString;
23
+ reasoning: z.ZodString;
24
+ sourceNodeIds: z.ZodArray<z.ZodString>;
25
+ }, z.core.$strict>>;
26
+ slots: z.ZodArray<z.ZodObject<{
27
+ name: z.ZodString;
28
+ description: z.ZodString;
29
+ reasoning: z.ZodString;
30
+ sourceNodeIds: z.ZodArray<z.ZodString>;
31
+ }, z.core.$strict>>;
32
+ modelingNotes: z.ZodArray<z.ZodString>;
33
+ }, z.core.$strict>;
34
+ export type ModelComponentExploration = z.infer<typeof ModelComponentExplorationSchema>;
35
+ export declare const modelComponentExplorationJsonSchema: ReturnType<typeof z.toJSONSchema>;
36
+ /** Apply source-bound authoring judgments before model definitions receive their signed identities. */
37
+ export declare function applyModelComponentExploration(input: ModelComponentExploration, model: {
38
+ key: string;
39
+ nodeId: string;
40
+ entry: CDFComponentEntry;
41
+ }, nodes: ReadonlyMap<string, LogicalSourceNode>, mappings: Map<string, LogicalPhysicalMapping>, sourceRevision: string): void;
42
+ export declare function modelExplorationComplete(plan: RepresentationPlan): boolean;
@@ -0,0 +1,94 @@
1
+ import { z } from 'zod';
2
+ import { cloneJson, jsonEqual, own } from './json-value.js';
3
+ const text = z.string().trim().min(1);
4
+ const judgment = {
5
+ name: text,
6
+ description: text,
7
+ reasoning: text,
8
+ sourceNodeIds: z.array(text).min(1),
9
+ };
10
+ export const ModelComponentExplorationSchema = z
11
+ .object({
12
+ modelKey: text,
13
+ nodeId: text,
14
+ sourceRevision: text,
15
+ name: text,
16
+ description: text,
17
+ properties: z.array(z
18
+ .object({
19
+ ...judgment,
20
+ category: z.enum(['content', 'design']),
21
+ authorability: z.enum(['editable', 'fixed', 'uncertain']),
22
+ })
23
+ .strict()),
24
+ slots: z.array(z.object(judgment).strict()),
25
+ modelingNotes: z.array(text),
26
+ })
27
+ .strict();
28
+ // Generated descriptors carry non-enumerable standard-schema helpers; publish plain JSON.
29
+ export const modelComponentExplorationJsonSchema = JSON.parse(JSON.stringify(z.toJSONSchema(ModelComponentExplorationSchema, { target: 'draft-7' })));
30
+ /** Apply source-bound authoring judgments before model definitions receive their signed identities. */
31
+ export function applyModelComponentExploration(input, model, nodes, mappings, sourceRevision) {
32
+ const report = ModelComponentExplorationSchema.parse(input);
33
+ if (report.modelKey !== model.key || report.nodeId !== model.nodeId || report.sourceRevision !== sourceRevision)
34
+ throw new Error('Model exploration has a stale or different source owner');
35
+ if (!/^[A-Za-z][A-Za-z0-9_-]*$/.test(report.name) || /^Model_[a-f0-9]{32}$/.test(report.name))
36
+ throw new Error('Model exploration name must be a readable component identifier');
37
+ const exact = (names, expected, subject) => {
38
+ if (new Set(names).size !== names.length || !jsonEqual([...names].sort(), [...expected].sort()))
39
+ throw new Error(`Model exploration must account for every ${subject} exactly once`);
40
+ };
41
+ exact(report.properties.map((field) => field.name), Object.keys(model.entry.$properties), 'property');
42
+ exact(report.slots.map((slot) => slot.name), Object.keys(model.entry.$slots ?? {}), 'slot');
43
+ const checkNodes = (ids, expected) => {
44
+ if (new Set(ids).size !== ids.length || ids.some((id) => !expected.includes(id)))
45
+ throw new Error('Model exploration refers to evidence outside its exact field or slot');
46
+ };
47
+ model.entry.$description = report.description;
48
+ for (const field of report.properties) {
49
+ const mapped = [...mappings.values()].filter((item) => item.componentKey === model.key && item.field === field.name);
50
+ checkNodes(field.sourceNodeIds, mapped.map((item) => item.nodeId));
51
+ const property = model.entry.$properties[field.name];
52
+ if (field.authorability === 'fixed') {
53
+ // A source default or a caller sample does not make a value invariant.
54
+ const value = property.$type === 'Literal' && own(property, 'value')
55
+ ? property.value
56
+ : property.$values?.length === 1
57
+ ? property.$values[0]
58
+ : undefined;
59
+ if (!property.$required || value === undefined)
60
+ throw new Error(`Cannot hide ${field.name}: no required intrinsic literal proves a reconstruction value`);
61
+ const root = mapped.find((item) => item.withinValue?.path.length === 0);
62
+ const node = root && nodes.get(root.nodeId);
63
+ if (!node ||
64
+ !node.required ||
65
+ node.domain.provenance !== 'compiler' ||
66
+ node.domain.openness !== 'closed' ||
67
+ node.domain.declaredValues.length !== 1 ||
68
+ !jsonEqual(node.domain.declaredValues[0], value) ||
69
+ node.nullable !== false ||
70
+ node.undefinable !== false)
71
+ throw new Error(`Cannot hide ${field.name}: source presence or literal identity remains uncertain`);
72
+ for (const item of mapped) {
73
+ item.disposition = 'reconstruction';
74
+ item.reconstruction = { kind: 'fixed-value', value: cloneJson(value) };
75
+ item.reason = field.reasoning;
76
+ }
77
+ delete model.entry.$properties[field.name];
78
+ }
79
+ else {
80
+ property.$category = field.category;
81
+ property.$description = field.description;
82
+ }
83
+ }
84
+ for (const slot of report.slots) {
85
+ const definition = model.entry.$slots[slot.name];
86
+ checkNodes(slot.sourceNodeIds, definition.$modelBinding ? [definition.$modelBinding.nodeId] : []);
87
+ definition.$description = slot.description;
88
+ }
89
+ }
90
+ export function modelExplorationComplete(plan) {
91
+ return plan.models.every((model) => plan.modelExplorations?.some((report) => report.modelKey === model.originalKey &&
92
+ report.nodeId === model.nodeId &&
93
+ report.sourceRevision === plan.identity.sourceRevision));
94
+ }
@@ -0,0 +1,5 @@
1
+ import type { CDFFieldDefinition } from '../cdf/types.js';
2
+ import type { LogicalSourceNode } from './logical-source-graph.js';
3
+ /** Derive only structures available at the target position, retaining nullish evidence separately. */
4
+ export declare function nativeSourceRegion(nodeId: string, nodes: ReadonlyMap<string, LogicalSourceNode>, position?: 'root' | 'item' | 'field', active?: Set<string>, required?: boolean, region?: 'ordinary' | 'union-branch'): CDFFieldDefinition | undefined;
5
+ export declare function sourceRecordFields(node: LogicalSourceNode, nodes: ReadonlyMap<string, LogicalSourceNode>): LogicalSourceNode['fields'];
@@ -0,0 +1,125 @@
1
+ import { cloneJson } from './json-value.js';
2
+ import { sourceDiscriminatedUnion } from './discriminated-union-source.js';
3
+ /** Derive only structures available at the target position, retaining nullish evidence separately. */
4
+ export function nativeSourceRegion(nodeId, nodes, position = 'root', active = new Set(), required, region = 'ordinary') {
5
+ const node = nodes.get(nodeId);
6
+ if (!node ||
7
+ active.has(nodeId) ||
8
+ node.coverage !== 'complete-within-declared-bound' ||
9
+ node.domain.unrepresentedValues?.length)
10
+ return;
11
+ const next = new Set(active).add(nodeId);
12
+ const presence = required ?? node.required;
13
+ const currentRegion = node.branchConditions?.length ? 'union-branch' : region;
14
+ if (node.kind === 'reference' && node.alternatives?.length === 1)
15
+ return nativeSourceRegion(node.alternatives[0], nodes, position, next, presence, currentRegion);
16
+ if (node.kind === 'union') {
17
+ const members = (node.alternatives ?? []).map((id) => nodes.get(id));
18
+ const values = members.filter((member) => !['null', 'undefined', 'never'].includes(member.kind));
19
+ if (values.length === 1)
20
+ return nativeSourceRegion(values[0].id, nodes, position, next, presence, currentRegion);
21
+ const union = sourceDiscriminatedUnion(node, nodes);
22
+ if (union) {
23
+ const members = [];
24
+ for (const branch of union.branches) {
25
+ const member = nativeSourceRegion(branch.nodeId, nodes, 'item', next, presence, 'union-branch');
26
+ if (!member?.$fields || member.$type !== 'record')
27
+ return;
28
+ member.$fields[union.discriminator] = {
29
+ $type: 'Literal',
30
+ $required: true,
31
+ value: branch.tag,
32
+ valueType: { $type: typeof branch.tag },
33
+ };
34
+ members.push(member);
35
+ }
36
+ return { $type: 'DiscriminatedUnion', $required: presence, discriminator: union.discriminator, members };
37
+ }
38
+ const scalar = values[0]?.kind;
39
+ if (!values.length ||
40
+ !['string', 'number', 'boolean'].includes(scalar ?? '') ||
41
+ values.some((member) => member.kind !== scalar))
42
+ return;
43
+ const field = { $type: scalar, $required: presence };
44
+ applyChoices(field, node);
45
+ return branchSingleton(field, currentRegion);
46
+ }
47
+ if (['string', 'number', 'boolean'].includes(node.kind)) {
48
+ const field = { $type: node.kind, $required: presence };
49
+ applyChoices(field, node);
50
+ return branchSingleton(field, currentRegion);
51
+ }
52
+ if (node.kind === 'array') {
53
+ if (position !== 'root' || node.items?.length !== 1)
54
+ return;
55
+ const items = nativeSourceRegion(node.items[0], nodes, 'item', next, true, currentRegion);
56
+ return items ? { $type: 'array', $required: presence, $items: items } : undefined;
57
+ }
58
+ if ((node.kind === 'record' || node.kind === 'intersection') &&
59
+ position !== 'field' &&
60
+ !node.indexSignatures?.length &&
61
+ !node.callSignatures?.length) {
62
+ const fields = Object.create(null);
63
+ if (!node.fields || (node.kind === 'intersection' && !disjointRecordIntersection(node, nodes)))
64
+ return;
65
+ for (const field of node.fields) {
66
+ const child = nativeSourceRegion(field.nodeId, nodes, 'field', next, field.required, currentRegion);
67
+ if (!child)
68
+ return;
69
+ if (field.description)
70
+ child.$description = field.description;
71
+ fields[field.key] = child;
72
+ }
73
+ return { $type: 'record', $required: presence, $fields: fields };
74
+ }
75
+ return;
76
+ }
77
+ function branchSingleton(field, region) {
78
+ if (region !== 'union-branch' || field.$values?.length !== 1)
79
+ return field;
80
+ return {
81
+ $type: 'Literal',
82
+ $required: field.$required,
83
+ value: field.$values[0],
84
+ valueType: { $type: field.$type },
85
+ };
86
+ }
87
+ function applyChoices(field, node) {
88
+ const values = node.domain.declaredValues;
89
+ if (node.domain.provenance === 'compiler' &&
90
+ node.domain.openness === 'closed' &&
91
+ values.length &&
92
+ values.every((value) => typeof value === field.$type) &&
93
+ !(field.$type === 'boolean' && values.length === 2 && values.includes(true) && values.includes(false)))
94
+ field.$values = cloneJson(values);
95
+ }
96
+ function disjointRecordIntersection(node, nodes) {
97
+ const keys = new Set();
98
+ const seen = new Set();
99
+ const pending = [...(node.alternatives ?? [])];
100
+ if (!pending.length)
101
+ return false;
102
+ while (pending.length) {
103
+ const member = nodes.get(pending.pop());
104
+ if (seen.has(member.id))
105
+ return false;
106
+ seen.add(member.id);
107
+ if (member.kind === 'reference' || member.kind === 'intersection')
108
+ pending.push(...(member.alternatives ?? []));
109
+ else {
110
+ if (member.kind !== 'record' || member.indexSignatures?.length || member.callSignatures?.length)
111
+ return false;
112
+ for (const field of member.fields ?? []) {
113
+ if (keys.has(field.key))
114
+ return false;
115
+ keys.add(field.key);
116
+ }
117
+ }
118
+ }
119
+ return node.fields?.length === keys.size && node.fields.every((field) => keys.has(field.key));
120
+ }
121
+ export function sourceRecordFields(node, nodes) {
122
+ return node.kind === 'record' || (node.kind === 'intersection' && disjointRecordIntersection(node, nodes))
123
+ ? node.fields
124
+ : undefined;
125
+ }
@@ -0,0 +1,10 @@
1
+ import { type PropertyIssue } from './json-value.js';
2
+ import type { PropertySourceGraph } from './property-model.js';
3
+ /** A source-bound behavioral judgment, separate from compiler nullability facts. */
4
+ export interface NullHandlingJudgment {
5
+ sourceNodeId: string;
6
+ handling: 'omit-equivalent' | 'distinct' | 'unknown';
7
+ reason: string;
8
+ }
9
+ export declare function validateNullHandling(input: unknown): PropertyIssue[];
10
+ export declare function validateNullHandlingSource(input: unknown, graph: PropertySourceGraph): PropertyIssue[];
@@ -0,0 +1,45 @@
1
+ import { jsonValueIssue } from './json-value.js';
2
+ export function validateNullHandling(input) {
3
+ const unsafe = jsonValueIssue(input);
4
+ if (unsafe)
5
+ return [unsafe];
6
+ if (!Array.isArray(input) || !input.length)
7
+ return [{ path: '/null_handling', message: 'Null handling requires a nonempty judgment array' }];
8
+ const issues = [];
9
+ const seen = new Set();
10
+ for (const [index, value] of input.entries()) {
11
+ const path = `/null_handling/${index}`;
12
+ if (!value ||
13
+ typeof value !== 'object' ||
14
+ Array.isArray(value) ||
15
+ Object.keys(value).some((key) => !['sourceNodeId', 'handling', 'reason'].includes(key)) ||
16
+ typeof value.sourceNodeId !== 'string' ||
17
+ !value.sourceNodeId.trim() ||
18
+ !['omit-equivalent', 'distinct', 'unknown'].includes(value.handling) ||
19
+ typeof value.reason !== 'string' ||
20
+ !value.reason.trim()) {
21
+ issues.push({ path, message: 'Invalid source-bound null-handling judgment' });
22
+ continue;
23
+ }
24
+ if (seen.has(value.sourceNodeId))
25
+ issues.push({ path, message: 'Duplicate null-handling source node' });
26
+ seen.add(value.sourceNodeId);
27
+ }
28
+ return issues;
29
+ }
30
+ export function validateNullHandlingSource(input, graph) {
31
+ const issues = validateNullHandling(input);
32
+ if (issues.length)
33
+ return issues;
34
+ const nodes = new Map(graph.nodes.map((node) => [node.id, node]));
35
+ for (const judgment of input) {
36
+ const node = nodes.get(judgment.sourceNodeId);
37
+ const members = (node?.alternatives ?? []).map((id) => nodes.get(id));
38
+ if (node?.kind !== 'union' || !members.some((member) => member?.kind === 'null'))
39
+ issues.push({ path: '/null_handling', message: 'Null judgment must reference a current nullable source union' });
40
+ else if (judgment.handling === 'omit-equivalent' &&
41
+ !members.some((member) => member && !['null', 'undefined', 'never'].includes(member.kind)))
42
+ issues.push({ path: '/null_handling', message: 'Null omission requires a non-null source alternative' });
43
+ }
44
+ return issues;
45
+ }
@@ -0,0 +1,6 @@
1
+ import type { CDFFieldDefinition } from '../cdf/types.js';
2
+ import { type PropertyIssue } from './json-value.js';
3
+ /** Finite choices stay in the source; target mapping owns enforcement and loss reporting. */
4
+ export declare function propertyDefinitionImportBlockers(definition: CDFFieldDefinition, path?: string): string[];
5
+ /** Physical restrictions apply to emitted fields, never to retained source evidence. */
6
+ export declare function propertyDefinitionImportIssues(definition: CDFFieldDefinition): PropertyIssue[];