@ng-org/shex-orm 0.1.2

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 (64) hide show
  1. package/README.md +240 -0
  2. package/dist/ShexJTypes.d.ts +542 -0
  3. package/dist/ShexJTypes.d.ts.map +1 -0
  4. package/dist/ShexJTypes.js +10 -0
  5. package/dist/build.d.ts +8 -0
  6. package/dist/build.d.ts.map +1 -0
  7. package/dist/build.js +72 -0
  8. package/dist/cli.d.ts +3 -0
  9. package/dist/cli.d.ts.map +1 -0
  10. package/dist/cli.js +15 -0
  11. package/dist/index.d.ts +2 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +1 -0
  14. package/dist/schema-converter/__tests__/typingTransformer.test.d.ts +2 -0
  15. package/dist/schema-converter/__tests__/typingTransformer.test.d.ts.map +1 -0
  16. package/dist/schema-converter/__tests__/typingTransformer.test.js +76 -0
  17. package/dist/schema-converter/converter.d.ts +12 -0
  18. package/dist/schema-converter/converter.d.ts.map +1 -0
  19. package/dist/schema-converter/converter.js +79 -0
  20. package/dist/schema-converter/templates/schema.ejs +8 -0
  21. package/dist/schema-converter/templates/shapeTypes.ejs +14 -0
  22. package/dist/schema-converter/templates/typings.ejs +14 -0
  23. package/dist/schema-converter/transformers/ShexJSchemaTransformer.d.ts +348 -0
  24. package/dist/schema-converter/transformers/ShexJSchemaTransformer.d.ts.map +1 -0
  25. package/dist/schema-converter/transformers/ShexJSchemaTransformer.js +239 -0
  26. package/dist/schema-converter/transformers/ShexJTypingTransformer.d.ts +366 -0
  27. package/dist/schema-converter/transformers/ShexJTypingTransformer.d.ts.map +1 -0
  28. package/dist/schema-converter/transformers/ShexJTypingTransformer.js +623 -0
  29. package/dist/schema-converter/util/ShapeInterfaceDeclaration.d.ts +5 -0
  30. package/dist/schema-converter/util/ShapeInterfaceDeclaration.d.ts.map +1 -0
  31. package/dist/schema-converter/util/ShapeInterfaceDeclaration.js +1 -0
  32. package/dist/schema-converter/util/annotateReadablePredicates.d.ts +8 -0
  33. package/dist/schema-converter/util/annotateReadablePredicates.d.ts.map +1 -0
  34. package/dist/schema-converter/util/annotateReadablePredicates.js +148 -0
  35. package/dist/schema-converter/util/dedupeObjectTypeMembers.d.ts +3 -0
  36. package/dist/schema-converter/util/dedupeObjectTypeMembers.d.ts.map +1 -0
  37. package/dist/schema-converter/util/dedupeObjectTypeMembers.js +47 -0
  38. package/dist/schema-converter/util/getRdfTypesForTripleConstraint.d.ts +4 -0
  39. package/dist/schema-converter/util/getRdfTypesForTripleConstraint.d.ts.map +1 -0
  40. package/dist/schema-converter/util/getRdfTypesForTripleConstraint.js +98 -0
  41. package/dist/types.d.ts +37 -0
  42. package/dist/types.d.ts.map +1 -0
  43. package/dist/types.js +10 -0
  44. package/dist/util/forAllShapes.d.ts +2 -0
  45. package/dist/util/forAllShapes.d.ts.map +1 -0
  46. package/dist/util/forAllShapes.js +25 -0
  47. package/package.json +67 -0
  48. package/src/ShexJTypes.ts +616 -0
  49. package/src/build.ts +106 -0
  50. package/src/cli.ts +23 -0
  51. package/src/index.ts +1 -0
  52. package/src/schema-converter/__tests__/typingTransformer.test.ts +85 -0
  53. package/src/schema-converter/converter.ts +128 -0
  54. package/src/schema-converter/templates/schema.ejs +8 -0
  55. package/src/schema-converter/templates/shapeTypes.ejs +14 -0
  56. package/src/schema-converter/templates/typings.ejs +14 -0
  57. package/src/schema-converter/transformers/ShexJSchemaTransformer.ts +284 -0
  58. package/src/schema-converter/transformers/ShexJTypingTransformer.ts +807 -0
  59. package/src/schema-converter/util/ShapeInterfaceDeclaration.ts +5 -0
  60. package/src/schema-converter/util/annotateReadablePredicates.ts +173 -0
  61. package/src/schema-converter/util/dedupeObjectTypeMembers.ts +61 -0
  62. package/src/schema-converter/util/getRdfTypesForTripleConstraint.ts +153 -0
  63. package/src/types.ts +51 -0
  64. package/src/util/forAllShapes.ts +39 -0
@@ -0,0 +1,807 @@
1
+ // Copyright (c) 2025 Laurin Weger, Par le Peuple, NextGraph.org developers
2
+ // All rights reserved.
3
+ // Copyright (c) 2023 Jackson Morgan
4
+ // Licensed under the Apache License, Version 2.0
5
+ // <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
6
+ // or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
7
+ // at your option. All files in the project carrying such
8
+ // notice may not be copied, modified, or distributed except
9
+ // according to those terms.
10
+ // SPDX-License-Identifier: Apache-2.0 OR MIT
11
+
12
+ /* eslint-disable @typescript-eslint/no-explicit-any */
13
+
14
+ import ShexJTraverser from "@ldo/traverser-shexj";
15
+ import type { Annotation } from "shexj";
16
+ import * as dom from "dts-dom";
17
+ import type { InterfaceDeclaration } from "dts-dom";
18
+
19
+ export interface ShapeInterfaceDeclaration extends InterfaceDeclaration {
20
+ shapeId?: string;
21
+ }
22
+
23
+ // Collected enum alias names (e.g., AuthenticatedAgentId) to emit at end
24
+ export const additionalCompactEnumAliases = new Set<string>();
25
+
26
+ export interface CompactTransformerContext {
27
+ getNameFromIri: (iri: string, rdfType?: string) => string;
28
+ }
29
+
30
+ function commentFromAnnotations(
31
+ annotations?: Annotation[]
32
+ ): string | undefined {
33
+ const commentAnnotationObject = annotations?.find(
34
+ (annotation) =>
35
+ annotation.predicate ===
36
+ "http://www.w3.org/2000/01/rdf-schema#comment"
37
+ )?.object;
38
+ if (typeof commentAnnotationObject === "string")
39
+ return commentAnnotationObject;
40
+ return commentAnnotationObject?.value;
41
+ }
42
+
43
+ export function toCamelCase(text: string) {
44
+ return text
45
+ .replace(/([-_ ]){1,}/g, " ")
46
+ .split(/[-_ ]/)
47
+ .reduce((cur, acc) => {
48
+ return cur + acc[0].toUpperCase() + acc.substring(1);
49
+ });
50
+ }
51
+
52
+ /**
53
+ * Name functions
54
+ */
55
+ export function iriToName(iri: string): string {
56
+ try {
57
+ const url = new URL(iri);
58
+ let name: string;
59
+ if (url.hash) {
60
+ name = url.hash.slice(1);
61
+ } else {
62
+ const splitPathname = url.pathname.split("/");
63
+ name = splitPathname[splitPathname.length - 1];
64
+ }
65
+ return name.replace(/(?<!^)Shape$/, "");
66
+ } catch (err) {
67
+ return iri;
68
+ }
69
+ }
70
+
71
+ export function nameFromAnnotationOrId(obj: {
72
+ id?: string;
73
+ annotations?: Annotation[];
74
+ }): string | undefined {
75
+ const labelAnnotationObject = obj.annotations?.find(
76
+ (annotation) =>
77
+ annotation.predicate ===
78
+ "http://www.w3.org/2000/01/rdf-schema#label"
79
+ )?.object;
80
+ if (labelAnnotationObject && typeof labelAnnotationObject === "string") {
81
+ return toCamelCase(iriToName(labelAnnotationObject));
82
+ } else if (
83
+ labelAnnotationObject &&
84
+ typeof labelAnnotationObject !== "string"
85
+ ) {
86
+ return toCamelCase(labelAnnotationObject.value);
87
+ } else if (obj.id) {
88
+ return toCamelCase(iriToName(obj.id));
89
+ }
90
+ }
91
+
92
+ // Helper: classify a dom.Type into categories we care about.
93
+ function isObjectLike(t: dom.Type): boolean {
94
+ return (
95
+ (t as dom.ObjectType).kind === "object" ||
96
+ (t as dom.InterfaceDeclaration).kind === "interface"
97
+ );
98
+ }
99
+
100
+ function isPrimitiveLike(t: dom.Type): boolean {
101
+ const kind = (t as any)?.kind;
102
+ if (kind === "name") return true; // named references and intrinsic tokens
103
+ if (kind === "union") {
104
+ return (t as dom.UnionType).members.every(isPrimitiveLike);
105
+ }
106
+ if (kind === "type-parameter") return true;
107
+ // Fallback: treat scalar intrinsic tokens as primitive
108
+ const intrinsicKinds = new Set([
109
+ "string",
110
+ "number",
111
+ "boolean",
112
+ "undefined",
113
+ ]);
114
+ return intrinsicKinds.has(kind || "");
115
+ }
116
+
117
+ // Small helpers for unions and alias naming
118
+ function isUnionType(t: dom.Type): t is dom.UnionType {
119
+ return (t as any)?.kind === "union";
120
+ }
121
+
122
+ function unionOf(types: dom.Type[]): dom.Type {
123
+ const flat: dom.Type[] = [];
124
+ const collect = (tt: dom.Type) => {
125
+ if (isUnionType(tt)) tt.members.forEach(collect);
126
+ else flat.push(tt);
127
+ };
128
+ types.forEach(collect);
129
+ const seen = new Set<string>();
130
+ const unique: dom.Type[] = [];
131
+ flat.forEach((m) => {
132
+ const key =
133
+ (m as any).name ||
134
+ (m as any).value ||
135
+ (m as any).kind + JSON.stringify(m);
136
+ if (!seen.has(key)) {
137
+ seen.add(key);
138
+ unique.push(m);
139
+ }
140
+ });
141
+ if (unique.length === 0) return dom.type.any as unknown as dom.Type;
142
+ if (unique.length === 1) return unique[0];
143
+ return dom.create.union(unique);
144
+ }
145
+
146
+ function setOf(inner: dom.Type): dom.NamedTypeReference {
147
+ return {
148
+ kind: "name",
149
+ name: "Set",
150
+ typeArguments: [inner],
151
+ } as any;
152
+ }
153
+
154
+ function recordOf(key: dom.Type, value: dom.Type): dom.NamedTypeReference {
155
+ return {
156
+ kind: "name",
157
+ name: "Record",
158
+ typeArguments: [key, value],
159
+ } as any;
160
+ }
161
+
162
+ type ValueSetLiteralCandidate = {
163
+ value?: string;
164
+ id?: string;
165
+ languageTag?: string;
166
+ stem?: string;
167
+ };
168
+
169
+ function literalFromValueSetValue(value: unknown): string | undefined {
170
+ if (typeof value === "string") return value;
171
+ if (value && typeof value === "object") {
172
+ const candidate = value as ValueSetLiteralCandidate;
173
+ if (typeof candidate.value === "string") return candidate.value;
174
+ if (typeof candidate.id === "string") return candidate.id;
175
+ if (typeof candidate.languageTag === "string")
176
+ return candidate.languageTag;
177
+ if (typeof candidate.stem === "string") return candidate.stem;
178
+ }
179
+ return undefined;
180
+ }
181
+
182
+ const extraTripleConstraints = new WeakSet<object>();
183
+
184
+ function tagExtraPredicatesFromExpression(
185
+ expression: any,
186
+ extras: Set<string>
187
+ ): void {
188
+ if (!expression || typeof expression !== "object") return;
189
+ const exprType = expression.type;
190
+ if (exprType === "TripleConstraint") {
191
+ if (extras.has(expression.predicate)) {
192
+ extraTripleConstraints.add(expression);
193
+ }
194
+ return;
195
+ }
196
+ if (exprType === "EachOf" || exprType === "OneOf") {
197
+ (expression.expressions || []).forEach((child: any) =>
198
+ tagExtraPredicatesFromExpression(child, extras)
199
+ );
200
+ return;
201
+ }
202
+ if (exprType === "Shape") {
203
+ tagExtraPredicatesFromExpression(expression.expression, extras);
204
+ }
205
+ }
206
+
207
+ // Note: aliasing helpers previously used in earlier versions were removed.
208
+
209
+ // Property name collision resolution using predicate IRI mapping
210
+ const predicateIriByProp = new WeakMap<dom.PropertyDeclaration, string>();
211
+
212
+ // Note: collisions are handled by annotateReadablePredicates pre-pass.
213
+
214
+ // Merge duplicate properties without introducing LdSet. If a property appears multiple
215
+ // times (e.g., via EXTENDS or grouped expressions) we:
216
+ // - union the types (flattening existing unions)
217
+ // - if one side is Set<T> and the other is plain U, produce Set<T|U>
218
+ // - if both are Set<A>, Set<B> -> Set<A|B>
219
+ // - preserve optional flag if any occurrence optional
220
+ function dedupeCompactProperties(
221
+ props: dom.PropertyDeclaration[]
222
+ ): dom.PropertyDeclaration[] {
223
+ const isSetRef = (t: dom.Type): t is dom.NamedTypeReference =>
224
+ (t as any).kind === "name" && (t as any).name === "Set";
225
+ const getSetInner = (t: dom.Type): dom.Type =>
226
+ isSetRef(t) ? (t as any).typeArguments[0] : t;
227
+
228
+ // Group by composite key (name + predicate IRI)
229
+ const groups = new Map<string, dom.PropertyDeclaration[]>();
230
+ for (const p of props) {
231
+ const pred = predicateIriByProp.get(p) || "";
232
+ const key = `${p.name}\u0000${pred}`;
233
+ if (!groups.has(key)) groups.set(key, []);
234
+ groups.get(key)!.push(p);
235
+ }
236
+
237
+ const merged: dom.PropertyDeclaration[] = [];
238
+ for (const [, group] of groups) {
239
+ if (group.length === 1) {
240
+ merged.push(group[0]);
241
+ continue;
242
+ }
243
+ let acc = group[0];
244
+ for (let i = 1; i < group.length; i++) {
245
+ const next = group[i];
246
+ const accSet = isSetRef(acc.type);
247
+ const nextSet = isSetRef(next.type);
248
+ let mergedType: dom.Type;
249
+ if (accSet && nextSet) {
250
+ mergedType = setOf(
251
+ unionOf([getSetInner(acc.type), getSetInner(next.type)])
252
+ );
253
+ } else if (accSet && !nextSet) {
254
+ mergedType = setOf(unionOf([getSetInner(acc.type), next.type]));
255
+ } else if (!accSet && nextSet) {
256
+ mergedType = setOf(unionOf([acc.type, getSetInner(next.type)]));
257
+ } else {
258
+ mergedType = unionOf([acc.type, next.type]);
259
+ }
260
+ const optional =
261
+ acc.flags === dom.DeclarationFlags.Optional ||
262
+ next.flags === dom.DeclarationFlags.Optional
263
+ ? dom.DeclarationFlags.Optional
264
+ : dom.DeclarationFlags.None;
265
+ const mergedProp = dom.create.property(
266
+ acc.name,
267
+ mergedType,
268
+ optional
269
+ );
270
+ mergedProp.jsDocComment =
271
+ acc.jsDocComment && next.jsDocComment
272
+ ? `${acc.jsDocComment} | ${next.jsDocComment}`
273
+ : acc.jsDocComment || next.jsDocComment;
274
+ const pred =
275
+ predicateIriByProp.get(acc) || predicateIriByProp.get(next);
276
+ if (pred) predicateIriByProp.set(mergedProp, pred);
277
+ acc = mergedProp;
278
+ }
279
+ merged.push(acc);
280
+ }
281
+ return merged;
282
+ }
283
+
284
+ /** Add `@id` and `@graph` optional readonly props for nested objects */
285
+ function addIdAndGraphProperties(t: dom.Type): dom.Type {
286
+ if ((t as any)?.kind === "object") {
287
+ const members = (t as any).members as
288
+ | dom.PropertyDeclaration[]
289
+ | undefined;
290
+ if (!members) return t;
291
+
292
+ const props = (members.filter?.((m: any) => m?.kind === "property") ||
293
+ []) as dom.PropertyDeclaration[];
294
+ if (!props.some((m) => m.name === "@id")) {
295
+ members.unshift(
296
+ dom.create.property(
297
+ "@id",
298
+ dom.create.namedTypeReference("IRI"),
299
+ dom.DeclarationFlags.Optional |
300
+ dom.DeclarationFlags.ReadOnly
301
+ ),
302
+ dom.create.property(
303
+ "@graph",
304
+ dom.create.namedTypeReference("IRI"),
305
+ dom.DeclarationFlags.Optional |
306
+ dom.DeclarationFlags.ReadOnly
307
+ )
308
+ );
309
+ }
310
+ }
311
+ return t;
312
+ }
313
+
314
+ function addIdAndGraphIriToUnionObjects(t: dom.Type): dom.Type {
315
+ if (!isUnionType(t)) return t;
316
+ const members = (t as dom.UnionType).members.map((m) =>
317
+ (m as any)?.kind === "object" ? addIdAndGraphProperties(m) : m
318
+ );
319
+ return dom.create.union(members);
320
+ }
321
+
322
+ // Create property and attach predicate IRI and annotations consistently
323
+ function createProperty(
324
+ name: string,
325
+ type: dom.Type,
326
+ flags: dom.DeclarationFlags,
327
+ predicateIri?: string,
328
+ annotations?: Annotation[]
329
+ ): dom.PropertyDeclaration {
330
+ const prop = dom.create.property(name, type, flags);
331
+ if (predicateIri) predicateIriByProp.set(prop, predicateIri);
332
+ const cmt = commentFromAnnotations(annotations) || "";
333
+ prop.jsDocComment = cmt
334
+ ? `${cmt}\n\nOriginal IRI: ${predicateIri ?? ""}`.trim()
335
+ : `Original IRI: ${predicateIri ?? ""}`;
336
+ return prop;
337
+ }
338
+
339
+ export const ShexJTypingTransformerCompact = ShexJTraverser.createTransformer<
340
+ {
341
+ Schema: { return: dom.TopLevelDeclaration[] };
342
+ ShapeDecl: { return: dom.InterfaceDeclaration };
343
+ Shape: { return: dom.InterfaceDeclaration };
344
+ EachOf: { return: dom.ObjectType | dom.InterfaceDeclaration };
345
+ TripleConstraint: { return: dom.PropertyDeclaration };
346
+ NodeConstraint: { return: dom.Type };
347
+ ShapeOr: { return: dom.UnionType };
348
+ ShapeAnd: { return: dom.IntersectionType };
349
+ ShapeNot: { return: never };
350
+ ShapeExternal: { return: never };
351
+ },
352
+ null
353
+ >({
354
+ // Transformer from Schema to interfaces
355
+ Schema: {
356
+ transformer: async (_schema, getTransformedChildren) => {
357
+ const transformedChildren = await getTransformedChildren();
358
+ const interfaces: dom.TopLevelDeclaration[] = [];
359
+ transformedChildren.shapes?.forEach((shape) => {
360
+ if (
361
+ typeof shape !== "string" &&
362
+ (shape as dom.InterfaceDeclaration).kind === "interface"
363
+ ) {
364
+ interfaces.push(shape as dom.InterfaceDeclaration);
365
+ }
366
+ });
367
+ return interfaces;
368
+ },
369
+ },
370
+
371
+ // Transformer from ShapeDecl to interface
372
+ ShapeDecl: {
373
+ transformer: async (shapeDecl, getTransformedChildren) => {
374
+ const shapeName = nameFromAnnotationOrId(shapeDecl) || "Shape";
375
+ const { shapeExpr } = await getTransformedChildren();
376
+ if ((shapeExpr as dom.InterfaceDeclaration).kind === "interface") {
377
+ const shapeInterface = shapeExpr as ShapeInterfaceDeclaration;
378
+ shapeInterface.name = shapeName;
379
+ // Preserve shape id for downstream shapeTypes generation
380
+ shapeInterface.shapeId = shapeDecl.id;
381
+
382
+ // Ensure root-level @id and @graph are present as readonly (mandatory)
383
+ const hasId = shapeInterface.members.find(
384
+ (m) => m.kind === "property" && m.name === "@id"
385
+ );
386
+ const hasGraph = shapeInterface.members.find(
387
+ (m) => m.kind === "property" && m.name === "@graph"
388
+ );
389
+
390
+ if (!hasId || !hasGraph) {
391
+ const propsToAdd: dom.PropertyDeclaration[] = [];
392
+ if (!hasGraph) {
393
+ const graphProp = dom.create.property(
394
+ "@graph",
395
+ dom.create.namedTypeReference("IRI"),
396
+ dom.DeclarationFlags.ReadOnly
397
+ );
398
+ graphProp.jsDocComment = "The graph IRI.";
399
+ propsToAdd.push(graphProp);
400
+ }
401
+ if (!hasId) {
402
+ const idProp = dom.create.property(
403
+ "@id",
404
+ dom.create.namedTypeReference("IRI"),
405
+ dom.DeclarationFlags.ReadOnly
406
+ );
407
+ idProp.jsDocComment = "The subject IRI.";
408
+ propsToAdd.push(idProp);
409
+ }
410
+ shapeInterface.members.unshift(...propsToAdd);
411
+ }
412
+ return shapeInterface;
413
+ }
414
+ throw new Error(
415
+ "Unsupported direct shape expression on ShapeDecl for compact format."
416
+ );
417
+ },
418
+ },
419
+
420
+ // Transformer from Shape to interface
421
+ Shape: {
422
+ transformer: async (
423
+ _shape,
424
+ getTransformedChildren,
425
+ setReturnPointer
426
+ ) => {
427
+ const newInterface: ShapeInterfaceDeclaration =
428
+ dom.create.interface("");
429
+ setReturnPointer(newInterface);
430
+ if (_shape.extra?.length ?? 0 > 0) {
431
+ tagExtraPredicatesFromExpression(
432
+ _shape.expression,
433
+ new Set(_shape.extra)
434
+ );
435
+ }
436
+ const transformedChildren = await getTransformedChildren();
437
+ if (
438
+ typeof transformedChildren.expression !== "string" &&
439
+ transformedChildren.expression &&
440
+ ((transformedChildren.expression as dom.ObjectType).kind ===
441
+ "object" ||
442
+ (transformedChildren.expression as dom.InterfaceDeclaration)
443
+ .kind === "interface")
444
+ ) {
445
+ newInterface.members.push(
446
+ ...(transformedChildren.expression as dom.ObjectType)
447
+ .members
448
+ );
449
+ } else if (
450
+ (transformedChildren.expression as dom.PropertyDeclaration)
451
+ ?.kind === "property"
452
+ ) {
453
+ newInterface.members.push(
454
+ transformedChildren.expression as dom.PropertyDeclaration
455
+ );
456
+ }
457
+ if (transformedChildren.extends) {
458
+ transformedChildren.extends.forEach((ext) => {
459
+ const extInt = ext as dom.InterfaceDeclaration;
460
+ if (extInt.kind === "interface") {
461
+ const merged = [
462
+ ...extInt.members.filter(
463
+ (m) =>
464
+ !(m.kind === "property" && m.name === "@id")
465
+ ),
466
+ ...newInterface.members,
467
+ ].filter(
468
+ (m): m is dom.PropertyDeclaration =>
469
+ m.kind === "property"
470
+ );
471
+ newInterface.members = dedupeCompactProperties(merged);
472
+ }
473
+ });
474
+ }
475
+ // Final pass: ensure only a single @id and a single @graph property, normalize to readonly
476
+ const idSeen = new Set<number>();
477
+ const graphSeen = new Set<number>();
478
+ newInterface.members = newInterface.members.filter((m, idx) => {
479
+ if (m.kind !== "property") return true;
480
+
481
+ if (m.name === "@id") {
482
+ if (idSeen.size === 0) {
483
+ idSeen.add(idx);
484
+ // normalize id type to IRI and make readonly
485
+ m.type = dom.create.namedTypeReference("IRI");
486
+ m.flags = dom.DeclarationFlags.ReadOnly;
487
+ return true;
488
+ }
489
+ return false;
490
+ }
491
+
492
+ if (m.name === "@graph") {
493
+ if (graphSeen.size === 0) {
494
+ graphSeen.add(idx);
495
+ // normalize graph type to IRI and make readonly
496
+ m.type = dom.create.namedTypeReference("IRI");
497
+ m.flags = dom.DeclarationFlags.ReadOnly;
498
+ return true;
499
+ }
500
+ return false;
501
+ }
502
+
503
+ return true;
504
+ });
505
+ return newInterface;
506
+ },
507
+ },
508
+
509
+ // Transformer from EachOf to object type. EachOf contains the `expressions` array of properties (TripleConstraint)
510
+ EachOf: {
511
+ transformer: async (
512
+ eachOf,
513
+ getTransformedChildren,
514
+ setReturnPointer
515
+ ) => {
516
+ const transformedChildren = await getTransformedChildren();
517
+ const name = nameFromAnnotationOrId(eachOf);
518
+
519
+ const objectType = name
520
+ ? dom.create.interface(name)
521
+ : dom.create.objectType([]);
522
+ setReturnPointer(objectType);
523
+ const inputProps: dom.PropertyDeclaration[] = [];
524
+ transformedChildren.expressions.forEach((expr) => {
525
+ if (!expr || typeof expr === "string") return;
526
+ const kind = (expr as any).kind;
527
+ if (kind === "property") {
528
+ inputProps.push(expr as dom.PropertyDeclaration);
529
+ } else if (kind === "object" || kind === "interface") {
530
+ const mlist = (
531
+ expr as dom.ObjectType | dom.InterfaceDeclaration
532
+ ).members;
533
+ mlist.forEach((m) => {
534
+ if ((m as any).kind === "property") {
535
+ inputProps.push(m as dom.PropertyDeclaration);
536
+ }
537
+ });
538
+ }
539
+ });
540
+ const deduped = dedupeCompactProperties(inputProps);
541
+ objectType.members.push(...deduped);
542
+ return objectType;
543
+ },
544
+ },
545
+
546
+ // Transformer from triple constraints to type properties.
547
+ TripleConstraint: {
548
+ transformer: async (
549
+ tripleConstraint,
550
+ getTransformedChildren,
551
+ _setReturnPointer,
552
+ _node
553
+ ) => {
554
+ const transformedChildren = await getTransformedChildren();
555
+ const baseName = (tripleConstraint as any)
556
+ .readablePredicate as string;
557
+
558
+ const max = tripleConstraint.max;
559
+ const isExtra = extraTripleConstraints.has(tripleConstraint);
560
+ const isPlural =
561
+ isExtra || max === -1 || (max !== undefined && max !== 1);
562
+ const isOptional =
563
+ tripleConstraint.min === 0 && tripleConstraint.max == 1;
564
+
565
+ let valueType: dom.Type = dom.type.any;
566
+ if (transformedChildren.valueExpr)
567
+ valueType = transformedChildren.valueExpr as dom.Type;
568
+
569
+ // Generic: If valueExpr is a NodeConstraint with concrete `values`,
570
+ // build a union of named alias references derived from those values.
571
+ // Works for any predicate (not only rdf:type).
572
+ const originalValueExpr: any = (tripleConstraint as any)?.valueExpr;
573
+ if (
574
+ originalValueExpr &&
575
+ typeof originalValueExpr === "object" &&
576
+ originalValueExpr.type === "NodeConstraint" &&
577
+ Array.isArray(originalValueExpr.values) &&
578
+ originalValueExpr.values.length > 0
579
+ ) {
580
+ const aliasRefs: dom.Type[] = [];
581
+ for (const v of originalValueExpr.values) {
582
+ const literalVal = literalFromValueSetValue(v);
583
+ if (literalVal !== undefined) {
584
+ aliasRefs.push(dom.type.stringLiteral(literalVal));
585
+ }
586
+ }
587
+ if (aliasRefs.length > 0) {
588
+ let union = unionOf(aliasRefs);
589
+ if (isExtra) {
590
+ // Type like (string & {}), to preserve literal values.
591
+ const intersectedString = dom.create.intersection([
592
+ dom.create.namedTypeReference("IRI"),
593
+ dom.create.objectType([]),
594
+ ]);
595
+ union = unionOf([union, intersectedString]);
596
+ }
597
+ const final = isPlural ? setOf(union) : union;
598
+ return createProperty(
599
+ baseName,
600
+ final,
601
+ isOptional
602
+ ? dom.DeclarationFlags.Optional
603
+ : dom.DeclarationFlags.None,
604
+ tripleConstraint.predicate,
605
+ tripleConstraint.annotations
606
+ );
607
+ }
608
+ }
609
+
610
+ if (
611
+ (valueType as dom.InterfaceDeclaration).kind === "interface" &&
612
+ !(valueType as dom.InterfaceDeclaration).name
613
+ ) {
614
+ valueType = dom.create.objectType(
615
+ (valueType as dom.InterfaceDeclaration)
616
+ .members as dom.PropertyDeclaration[]
617
+ );
618
+ }
619
+
620
+ // Normalize NodeConstraint returned object forms for IRIs into IRI
621
+ // Heuristic: existing transformer (compact) returns string/number/boolean OR object/interface.
622
+ // We treat any simple string/number/boolean/name as primitive.
623
+
624
+ // Determine category
625
+ const objLike = isObjectLike(valueType);
626
+ const isUnion =
627
+ (valueType as unknown as { kind?: string })?.kind === "union";
628
+ const unionMembers: dom.Type[] = isUnion
629
+ ? (valueType as dom.UnionType).members
630
+ : [];
631
+ const unionAllObjLike =
632
+ isUnion &&
633
+ unionMembers.length > 0 &&
634
+ unionMembers.every(isObjectLike);
635
+ const primLike = isPrimitiveLike(valueType);
636
+ if (
637
+ !primLike &&
638
+ !objLike &&
639
+ (valueType as dom.UnionType).kind === "union"
640
+ ) {
641
+ const u = valueType as dom.UnionType;
642
+ const hasObj = u.members.some(isObjectLike);
643
+ const hasPrim = u.members.some(isPrimitiveLike);
644
+ if (isPlural && hasObj && hasPrim) {
645
+ throw new Error(
646
+ `Mixed plural union (object + primitive) not supported for predicate ${tripleConstraint.predicate}`
647
+ );
648
+ }
649
+ }
650
+
651
+ let finalType: dom.Type;
652
+ if (isPlural) {
653
+ if (objLike || unionAllObjLike) {
654
+ if (
655
+ (valueType as dom.InterfaceDeclaration).kind ===
656
+ "interface" &&
657
+ (valueType as dom.InterfaceDeclaration).name
658
+ ) {
659
+ const ifaceName = (
660
+ valueType as dom.InterfaceDeclaration
661
+ ).name;
662
+ // Set of full object instances
663
+ finalType = setOf(
664
+ dom.create.namedTypeReference(ifaceName)
665
+ );
666
+ } else {
667
+ // Anonymous object or union of anonymous/interface objects
668
+ let valueForSet: dom.Type = valueType;
669
+ if (unionAllObjLike) {
670
+ // Ensure each union member has @id and @graph as optional readonly
671
+ valueForSet =
672
+ addIdAndGraphIriToUnionObjects(valueType);
673
+ } else {
674
+ valueForSet = addIdAndGraphProperties(valueType);
675
+ }
676
+ finalType = setOf(valueForSet);
677
+ }
678
+ } else {
679
+ finalType = setOf(valueType);
680
+ }
681
+ } else {
682
+ // Singular
683
+ // If anonymous object or union of object-like types, ensure id: IRI is present (mandatory)
684
+ if (objLike) {
685
+ if ((valueType as dom.ObjectType).kind === "object") {
686
+ valueType = addIdAndGraphProperties(valueType);
687
+ }
688
+ } else if (isUnion && unionAllObjLike) {
689
+ valueType = addIdAndGraphIriToUnionObjects(valueType);
690
+ }
691
+ // Singular: always the interface/object type itself (never Id union)
692
+ if (
693
+ (valueType as dom.InterfaceDeclaration).kind ===
694
+ "interface" &&
695
+ (valueType as dom.InterfaceDeclaration).name
696
+ ) {
697
+ finalType = dom.create.namedTypeReference(
698
+ (valueType as dom.InterfaceDeclaration).name
699
+ );
700
+ } else {
701
+ finalType = valueType;
702
+ }
703
+ }
704
+ return createProperty(
705
+ baseName,
706
+ finalType,
707
+ isOptional
708
+ ? dom.DeclarationFlags.Optional
709
+ : dom.DeclarationFlags.None,
710
+ tripleConstraint.predicate,
711
+ tripleConstraint.annotations
712
+ );
713
+ },
714
+ },
715
+
716
+ // Transformer from node constraint to type
717
+ NodeConstraint: {
718
+ transformer: async (nodeConstraint) => {
719
+ if (nodeConstraint.datatype) {
720
+ switch (nodeConstraint.datatype) {
721
+ case "http://www.w3.org/2001/XMLSchema#boolean":
722
+ return dom.type.boolean;
723
+ case "http://www.w3.org/2001/XMLSchema#byte":
724
+ case "http://www.w3.org/2001/XMLSchema#decimal":
725
+ case "http://www.w3.org/2001/XMLSchema#double":
726
+ case "http://www.w3.org/2001/XMLSchema#float":
727
+ case "http://www.w3.org/2001/XMLSchema#int":
728
+ case "http://www.w3.org/2001/XMLSchema#integer":
729
+ case "http://www.w3.org/2001/XMLSchema#long":
730
+ case "http://www.w3.org/2001/XMLSchema#negativeInteger":
731
+ case "http://www.w3.org/2001/XMLSchema#nonNegativeInteger":
732
+ case "http://www.w3.org/2001/XMLSchema#nonPositiveInteger":
733
+ case "http://www.w3.org/2001/XMLSchema#positiveInteger":
734
+ case "http://www.w3.org/2001/XMLSchema#short":
735
+ case "http://www.w3.org/2001/XMLSchema#unsignedLong":
736
+ case "http://www.w3.org/2001/XMLSchema#unsignedInt":
737
+ case "http://www.w3.org/2001/XMLSchema#unsignedShort":
738
+ case "http://www.w3.org/2001/XMLSchema#unsignedByte":
739
+ return dom.type.number;
740
+ default:
741
+ return dom.type.string; // treat most as string
742
+ }
743
+ }
744
+ if (nodeConstraint.nodeKind) {
745
+ switch (nodeConstraint.nodeKind) {
746
+ case "iri":
747
+ return dom.create.namedTypeReference("IRI");
748
+ case "bnode":
749
+ return dom.type.string; // opaque id as string
750
+ case "nonliteral":
751
+ return dom.create.namedTypeReference("IRI");
752
+ case "literal":
753
+ default:
754
+ return dom.type.string;
755
+ }
756
+ }
757
+ if (nodeConstraint.values) {
758
+ const union = dom.create.union([]);
759
+ nodeConstraint.values.forEach((v) => {
760
+ const literalVal = literalFromValueSetValue(v);
761
+ if (literalVal !== undefined) {
762
+ union.members.push(dom.type.stringLiteral(literalVal));
763
+ }
764
+ });
765
+ if (!union.members.length) return dom.type.string;
766
+ if (union.members.length === 1) return union.members[0];
767
+ return union;
768
+ }
769
+ return dom.type.any;
770
+ },
771
+ },
772
+
773
+ // Transformer from ShapeOr to union type
774
+ ShapeOr: {
775
+ transformer: async (_shapeOr, getTransformedChildren) => {
776
+ const tc = await getTransformedChildren();
777
+
778
+ return dom.create.union(tc.shapeExprs as dom.Type[]);
779
+ },
780
+ },
781
+
782
+ // Transformer from ShapeAnd to intersection type
783
+ ShapeAnd: {
784
+ transformer: async (_shapeAnd, getTransformedChildren) => {
785
+ const tc = await getTransformedChildren();
786
+ const valid: dom.Type[] = [];
787
+ tc.shapeExprs.forEach((t) => {
788
+ if (typeof t === "object") valid.push(t);
789
+ });
790
+ return dom.create.intersection(valid);
791
+ },
792
+ },
793
+
794
+ // Transformer from ShapeNot to type - not supported.
795
+ ShapeNot: {
796
+ transformer: async () => {
797
+ throw new Error("ShapeNot not supported (compact)");
798
+ },
799
+ },
800
+
801
+ // Transformer from ShapeExternal to type - not supported.
802
+ ShapeExternal: {
803
+ transformer: async () => {
804
+ throw new Error("ShapeExternal not supported (compact)");
805
+ },
806
+ },
807
+ });