@_linked/core 2.34.0 → 2.35.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 (85) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +24 -0
  3. package/lib/esm/index.d.ts +2 -1
  4. package/lib/esm/index.js +2 -0
  5. package/lib/esm/index.js.map +1 -1
  6. package/lib/esm/live/LiveQueryStore.d.ts +22 -0
  7. package/lib/esm/live/LiveQueryStore.js +86 -19
  8. package/lib/esm/live/LiveQueryStore.js.map +1 -1
  9. package/lib/esm/live/changes.d.ts +9 -1
  10. package/lib/esm/live/changes.js +7 -2
  11. package/lib/esm/live/changes.js.map +1 -1
  12. package/lib/esm/live/keys.d.ts +6 -0
  13. package/lib/esm/live/keys.js +10 -1
  14. package/lib/esm/live/keys.js.map +1 -1
  15. package/lib/esm/live/matcher.d.ts +4 -1
  16. package/lib/esm/live/matcher.js +10 -5
  17. package/lib/esm/live/matcher.js.map +1 -1
  18. package/lib/esm/queries/AskBuilder.d.ts +6 -0
  19. package/lib/esm/queries/AskBuilder.js +14 -0
  20. package/lib/esm/queries/AskBuilder.js.map +1 -1
  21. package/lib/esm/queries/CountBuilder.d.ts +6 -0
  22. package/lib/esm/queries/CountBuilder.js +14 -0
  23. package/lib/esm/queries/CountBuilder.js.map +1 -1
  24. package/lib/esm/queries/CreateBuilder.d.ts +4 -0
  25. package/lib/esm/queries/CreateBuilder.js +15 -1
  26. package/lib/esm/queries/CreateBuilder.js.map +1 -1
  27. package/lib/esm/queries/DeleteBuilder.d.ts +4 -0
  28. package/lib/esm/queries/DeleteBuilder.js +15 -1
  29. package/lib/esm/queries/DeleteBuilder.js.map +1 -1
  30. package/lib/esm/queries/FieldSet.d.ts +10 -0
  31. package/lib/esm/queries/FieldSet.js +85 -18
  32. package/lib/esm/queries/FieldSet.js.map +1 -1
  33. package/lib/esm/queries/IRLower.js +3 -2
  34. package/lib/esm/queries/IRLower.js.map +1 -1
  35. package/lib/esm/queries/IRMutation.js +15 -14
  36. package/lib/esm/queries/IRMutation.js.map +1 -1
  37. package/lib/esm/queries/MutationSerialization.js +2 -2
  38. package/lib/esm/queries/MutationSerialization.js.map +1 -1
  39. package/lib/esm/queries/PropertyPath.js +2 -2
  40. package/lib/esm/queries/PropertyPath.js.map +1 -1
  41. package/lib/esm/queries/QueryBuilder.d.ts +7 -0
  42. package/lib/esm/queries/QueryBuilder.js +40 -3
  43. package/lib/esm/queries/QueryBuilder.js.map +1 -1
  44. package/lib/esm/queries/SelectQuery.js +2 -2
  45. package/lib/esm/queries/SelectQuery.js.map +1 -1
  46. package/lib/esm/queries/UpdateBuilder.d.ts +4 -0
  47. package/lib/esm/queries/UpdateBuilder.js +15 -1
  48. package/lib/esm/queries/UpdateBuilder.js.map +1 -1
  49. package/lib/esm/queries/fromJSON.d.ts +12 -1
  50. package/lib/esm/queries/fromJSON.js +5 -1
  51. package/lib/esm/queries/fromJSON.js.map +1 -1
  52. package/lib/esm/queries/lower.js +12 -5
  53. package/lib/esm/queries/lower.js.map +1 -1
  54. package/lib/esm/queries/queryDependencies.js +14 -10
  55. package/lib/esm/queries/queryDependencies.js.map +1 -1
  56. package/lib/esm/queries/resolveShape.js +14 -2
  57. package/lib/esm/queries/resolveShape.js.map +1 -1
  58. package/lib/esm/shapes/Shape.js +2 -1
  59. package/lib/esm/shapes/Shape.js.map +1 -1
  60. package/lib/esm/shapes/nodeShapeData.js +7 -10
  61. package/lib/esm/shapes/nodeShapeData.js.map +1 -1
  62. package/lib/esm/shapes/registerRuntimeShape.js +3 -1
  63. package/lib/esm/shapes/registerRuntimeShape.js.map +1 -1
  64. package/lib/esm/shapes/syncShapes.d.ts +6 -2
  65. package/lib/esm/shapes/syncShapes.js +16 -9
  66. package/lib/esm/shapes/syncShapes.js.map +1 -1
  67. package/lib/esm/shapes/validation.js +2 -2
  68. package/lib/esm/shapes/validation.js.map +1 -1
  69. package/lib/esm/sparql/irToAlgebra.js +117 -52
  70. package/lib/esm/sparql/irToAlgebra.js.map +1 -1
  71. package/lib/esm/sparql/ownedLinkCheck.js +6 -1
  72. package/lib/esm/sparql/ownedLinkCheck.js.map +1 -1
  73. package/lib/esm/utils/LinkedStorage.d.ts +7 -0
  74. package/lib/esm/utils/LinkedStorage.js +39 -12
  75. package/lib/esm/utils/LinkedStorage.js.map +1 -1
  76. package/lib/esm/utils/ShapeCatalog.d.ts +57 -0
  77. package/lib/esm/utils/ShapeCatalog.js +86 -0
  78. package/lib/esm/utils/ShapeCatalog.js.map +1 -0
  79. package/lib/esm/utils/ShapeClass.d.ts +73 -8
  80. package/lib/esm/utils/ShapeClass.js +245 -66
  81. package/lib/esm/utils/ShapeClass.js.map +1 -1
  82. package/lib/esm/utils/registryCache.d.ts +15 -0
  83. package/lib/esm/utils/registryCache.js +50 -0
  84. package/lib/esm/utils/registryCache.js.map +1 -0
  85. package/package.json +1 -1
@@ -1,10 +1,12 @@
1
- var _a, _b;
1
+ var _a, _b, _c;
2
2
  // Namespace import, read at CALL time rather than captured at module-evaluation time.
3
3
  // `getOrCreateShapeAdapter` subclasses `Shape` at runtime, and under some import orders this
4
4
  // module finishes evaluating before `Shape.js` does — a named import captured then is
5
5
  // permanently `undefined`, and the subclass fails with "Class extends value undefined".
6
6
  import * as ShapeModule from '../shapes/Shape.js';
7
7
  import { getPropertyShape, } from '../shapes/nodeShapeData.js';
8
+ import { RegistryCache } from './registryCache.js';
9
+ export { RegistryCache };
8
10
  const resolveTargetClassId = (targetClass) => {
9
11
  var _a;
10
12
  if (!targetClass)
@@ -72,13 +74,163 @@ const registryState = ((_b = shapeRegistryGlobal.__linkedShapeRegistry) !== null
72
74
  nodeShapeRegistry: new Map(),
73
75
  shapeAdapters: new Map(),
74
76
  registryVersion: 0,
77
+ activeCatalog: undefined,
75
78
  }));
79
+ // A container created by an older copy has no slot yet; give it one so every read below
80
+ // sees the same object layout.
81
+ if (!('activeCatalog' in registryState))
82
+ registryState.activeCatalog = undefined;
76
83
  /** How many distinct copies of this module are loaded. One is expected. */
77
84
  export function getShapeRegistryInstanceCount() {
78
85
  return copies.size;
79
86
  }
80
- let subShapesCache = new Map();
81
- let mostSpecificSubShapesCache = new Map();
87
+ // ---------------------------------------------------------------------------
88
+ // The active shape catalog
89
+ // ---------------------------------------------------------------------------
90
+ /**
91
+ * Run `fn` with `catalog` as the shapes every registry accessor reads; `undefined` means
92
+ * the global registry (explicitly — it does not inherit an outer catalog).
93
+ *
94
+ * Synchronous on purpose: it works the same in the browser and in Node, with no
95
+ * AsyncLocalStorage. Scopes nest, the previous catalog is restored in `finally`, and fn's
96
+ * value is returned unchanged. A promise returned by fn is not awaited, so its
97
+ * continuation runs outside the scope. That is why builders and IRs capture their catalog
98
+ * and every place that picks one up later re-enters it (see docs/architecture/runtime-instances.md).
99
+ */
100
+ export function withShapeCatalog(catalog, fn) {
101
+ const previous = registryState.activeCatalog;
102
+ if (previous === catalog)
103
+ return fn();
104
+ registryState.activeCatalog = catalog;
105
+ try {
106
+ const result = fn();
107
+ if (result instanceof Promise)
108
+ warnPromiseInScope();
109
+ return result;
110
+ }
111
+ finally {
112
+ registryState.activeCatalog = previous;
113
+ }
114
+ }
115
+ /**
116
+ * Run `fn` with no catalog active, without the promise warning: for core's own calls into a
117
+ * dataset, whose asynchronous continuation is meant to run outside any catalog. @internal
118
+ */
119
+ export function outsideShapeCatalog(fn) {
120
+ const previous = registryState.activeCatalog;
121
+ if (previous === undefined)
122
+ return fn();
123
+ registryState.activeCatalog = undefined;
124
+ try {
125
+ return fn();
126
+ }
127
+ finally {
128
+ registryState.activeCatalog = previous;
129
+ }
130
+ }
131
+ let warnedPromiseInScope = false;
132
+ /**
133
+ * A scope ends when `fn` returns, so the continuation of a promise `fn` returns runs outside
134
+ * it. Said once, outside production. A builder (a thenable, not a Promise) is the intended
135
+ * return value: it captured the catalog and re-enters it.
136
+ */
137
+ function warnPromiseInScope() {
138
+ var _a;
139
+ if (warnedPromiseInScope)
140
+ return;
141
+ const env = (_a = globalThis.process) === null || _a === void 0 ? void 0 : _a.env;
142
+ if ((env === null || env === void 0 ? void 0 : env.NODE_ENV) === 'production')
143
+ return;
144
+ warnedPromiseInScope = true;
145
+ console.warn('[linked] withShapeCatalog(catalog, fn): fn returned a promise. The scope ended when fn ' +
146
+ 'returned, so code after its first await runs outside the catalog. Build the query inside ' +
147
+ 'the scope and await it outside — a builder keeps its catalog.');
148
+ }
149
+ /** The catalog of the current `withShapeCatalog` scope, or undefined for the global registry. */
150
+ export function getActiveShapeCatalog() {
151
+ return registryState.activeCatalog;
152
+ }
153
+ /**
154
+ * The catalog a builder, a FieldSet or an IR captured — undefined means the global registry.
155
+ * Reads the object's `catalog` field, which builders keep as a getter and IRs as a
156
+ * non-enumerable property, so neither appears in `toJSON` or a JSON round trip.
157
+ */
158
+ export function catalogOf(queryOrIr) {
159
+ return queryOrIr === null || queryOrIr === void 0 ? void 0 : queryOrIr.catalog;
160
+ }
161
+ /**
162
+ * Record `catalog` on an IR object as a non-enumerable `catalog` field, so the code that
163
+ * compiles it later (often after an `await`) re-enters the catalog it was lowered in.
164
+ * Nothing is written for the global registry: an IR without the field compiles globally.
165
+ * @internal
166
+ */
167
+ export function attachCatalog(ir, catalog = registryState.activeCatalog) {
168
+ if (catalog !== undefined) {
169
+ Object.defineProperty(ir, 'catalog', {
170
+ value: catalog,
171
+ enumerable: false,
172
+ configurable: true,
173
+ writable: true,
174
+ });
175
+ }
176
+ return ir;
177
+ }
178
+ /**
179
+ * Process-unique ids for catalogs, for live-query template keys. On `globalThis`, like the
180
+ * registry, so two copies of core give one catalog the same id.
181
+ */
182
+ const keyState = ((_c = shapeRegistryGlobal.__linkedShapeCatalogKeys) !== null && _c !== void 0 ? _c : (shapeRegistryGlobal.__linkedShapeCatalogKeys = { keys: new WeakMap(), next: 1 }));
183
+ /** 0 for the global registry, else a process-unique id for the catalog. */
184
+ export function shapeCatalogKey(catalog) {
185
+ if (catalog === undefined)
186
+ return 0;
187
+ let key = keyState.keys.get(catalog);
188
+ if (key === undefined) {
189
+ key = keyState.next++;
190
+ keyState.keys.set(catalog, key);
191
+ }
192
+ return key;
193
+ }
194
+ /** The catalog of an adapter class a catalog built, keyed on the class. */
195
+ const adapterCatalogs = new WeakMap();
196
+ /**
197
+ * The catalog a builder made from `shape` captures.
198
+ *
199
+ * Inside a scope: the active catalog when the root is an IRI (`shape` undefined) or a
200
+ * shape the catalog holds; otherwise none. A class the active catalog does not hold is
201
+ * authored code (CN's own shapes, built by CN code while a project scope is active) and
202
+ * must lower against the global registry. Outside a scope: the catalog that built the
203
+ * adapter class, if it is one, else none.
204
+ * @internal
205
+ */
206
+ export function captureCatalog(shape) {
207
+ var _a;
208
+ const active = registryState.activeCatalog;
209
+ if (shape === undefined || shape === null)
210
+ return active;
211
+ if (active !== undefined) {
212
+ const id = (_a = shape.shape) === null || _a === void 0 ? void 0 : _a.id;
213
+ if (id && active.has(id))
214
+ return active;
215
+ }
216
+ return typeof shape === 'function' ? adapterCatalogs.get(shape) : undefined;
217
+ }
218
+ /**
219
+ * Throw when a registry write is attempted inside a catalog scope. A catalog is
220
+ * immutable, and a write meant for it would land in the global registry instead.
221
+ * @internal
222
+ */
223
+ export function assertNoActiveCatalog() {
224
+ if (registryState.activeCatalog !== undefined) {
225
+ throw new Error('Cannot register shapes while a ShapeCatalog is active. Register outside ' +
226
+ 'withShapeCatalog, or build a new catalog with ShapeCatalog.from().');
227
+ }
228
+ }
229
+ function contentsOf(catalog) {
230
+ return catalog;
231
+ }
232
+ const subShapesCache = new RegistryCache();
233
+ const mostSpecificSubShapesCache = new RegistryCache();
82
234
  const nodeShapeToShapeClass = registryState.nodeShapeToShapeClass;
83
235
  const warnedDuplicateBases = new Set();
84
236
  /**
@@ -92,8 +244,19 @@ const warnedDuplicateBases = new Set();
92
244
  * metadata, so they use this map and work identically for both kinds of shape.
93
245
  */
94
246
  const nodeShapeRegistry = registryState.nodeShapeRegistry;
95
- /** Version the sub-shape caches above were filled at. */
96
- let subShapesCacheVersion = -1;
247
+ /**
248
+ * The shapes the accessors read: the active catalog's (its shapes plus core's built-ins),
249
+ * else the global registry. One shared read and a null check on the global path.
250
+ */
251
+ function activeNodeShapes() {
252
+ const catalog = registryState.activeCatalog;
253
+ return catalog === undefined ? nodeShapeRegistry : contentsOf(catalog).nodeShapes;
254
+ }
255
+ /** The authored classes the accessors read: the catalog's built-ins, else every registered class. */
256
+ function activeShapeClasses() {
257
+ const catalog = registryState.activeCatalog;
258
+ return catalog === undefined ? nodeShapeToShapeClass : contentsOf(catalog).shapeClasses;
259
+ }
97
260
  /**
98
261
  * The current registration version — a monotonic counter, bumped on every write.
99
262
  *
@@ -110,13 +273,8 @@ export function getRegistryVersion() {
110
273
  return registryState.registryVersion;
111
274
  }
112
275
  /** Reverse index parentIri → direct child IRIs, rebuilt when the registry changes. */
113
- let childIndex = null;
114
- let childIndexVersion = -1;
115
- function getChildIndex() {
116
- if (childIndex && childIndexVersion === registryState.registryVersion)
117
- return childIndex;
118
- const index = new Map();
119
- nodeShapeRegistry.forEach((shape, id) => {
276
+ const childIndex = new RegistryCache((index) => {
277
+ activeNodeShapes().forEach((shape, id) => {
120
278
  var _a;
121
279
  const parent = (_a = shape.extends) === null || _a === void 0 ? void 0 : _a.id;
122
280
  if (!parent)
@@ -127,16 +285,14 @@ function getChildIndex() {
127
285
  else
128
286
  index.set(parent, [id]);
129
287
  });
130
- childIndex = index;
131
- childIndexVersion = registryState.registryVersion;
132
- return index;
288
+ });
289
+ function getChildIndex() {
290
+ return childIndex.get();
133
291
  }
292
+ // Every derived cache is a RegistryCache keyed on this version, so bumping it is the
293
+ // whole invalidation.
134
294
  function invalidateRegistryCaches() {
135
295
  registryState.registryVersion++;
136
- subShapesCache.clear();
137
- mostSpecificSubShapesCache.clear();
138
- childIndex = null;
139
- targetClassIndex = null;
140
296
  }
141
297
  /** Drop the adapter for a shape whose metadata was replaced. */
142
298
  function invalidateShapeAdapter(id) {
@@ -151,6 +307,7 @@ function invalidateShapeAdapter(id) {
151
307
  * invalidation cannot be keyed on registry size.
152
308
  */
153
309
  export function registerNodeShape(nodeShape) {
310
+ assertNoActiveCatalog();
154
311
  if (!(nodeShape === null || nodeShape === void 0 ? void 0 : nodeShape.id))
155
312
  return;
156
313
  nodeShapeRegistry.set(nodeShape.id, nodeShape);
@@ -162,11 +319,14 @@ export function getNodeShape(nodeShape) {
162
319
  const id = typeof nodeShape === 'string' ? nodeShape : nodeShape === null || nodeShape === void 0 ? void 0 : nodeShape.id;
163
320
  if (!id)
164
321
  return undefined;
165
- return nodeShapeRegistry.get(id);
322
+ return activeNodeShapes().get(id);
166
323
  }
167
- /** Every registered shape's metadata, keyed by node-shape IRI. */
324
+ /**
325
+ * Every registered shape's metadata, keyed by node-shape IRI. Inside a catalog scope:
326
+ * exactly the catalog's shapes plus core's built-ins.
327
+ */
168
328
  export function getAllNodeShapes() {
169
- return nodeShapeRegistry;
329
+ return activeNodeShapes();
170
330
  }
171
331
  /**
172
332
  * Lazily-built constructors for shapes that exist only as data.
@@ -191,15 +351,25 @@ const shapeAdapters = registryState.shapeAdapters;
191
351
  * you specifically need an authored class; use this when you need *something*
192
352
  * constructor-shaped to drive the query builders.
193
353
  */
354
+ /** Adapters built inside a catalog: one map per catalog, keyed by IRI. */
355
+ const catalogAdapters = new WeakMap();
194
356
  export function getOrCreateShapeAdapter(nodeShape) {
195
357
  var _a;
196
- const data = typeof nodeShape === 'string' ? nodeShapeRegistry.get(nodeShape) : nodeShape;
358
+ const catalog = registryState.activeCatalog;
359
+ const shapes = catalog === undefined ? nodeShapeRegistry : contentsOf(catalog).nodeShapes;
360
+ const data = typeof nodeShape === 'string' ? shapes.get(nodeShape) : nodeShape;
197
361
  if (!(data === null || data === void 0 ? void 0 : data.id))
198
362
  return undefined;
199
- const authored = nodeShapeToShapeClass.get(data.id);
363
+ const authored = (catalog === undefined ? nodeShapeToShapeClass : contentsOf(catalog).shapeClasses).get(data.id);
200
364
  if (authored)
201
365
  return authored;
202
- const cached = shapeAdapters.get(data.id);
366
+ let adapters = shapeAdapters;
367
+ if (catalog !== undefined) {
368
+ adapters = catalogAdapters.get(catalog);
369
+ if (!adapters)
370
+ catalogAdapters.set(catalog, (adapters = new Map()));
371
+ }
372
+ const cached = adapters.get(data.id);
203
373
  // Re-registering a shape replaces its metadata, so an adapter built from the old
204
374
  // object must not be reused.
205
375
  if (cached && cached.shape === data)
@@ -211,7 +381,9 @@ export function getOrCreateShapeAdapter(nodeShape) {
211
381
  Object.defineProperty(RuntimeShape, 'name', {
212
382
  value: data.label || 'RuntimeShape',
213
383
  });
214
- shapeAdapters.set(data.id, RuntimeShape);
384
+ adapters.set(data.id, RuntimeShape);
385
+ if (catalog !== undefined)
386
+ adapterCatalogs.set(RuntimeShape, catalog);
215
387
  return RuntimeShape;
216
388
  }
217
389
  /** Resolve a ShapeLike to node-shape metadata, or undefined if it isn't a shape. */
@@ -219,17 +391,18 @@ function toNodeShapeData(shape) {
219
391
  var _a, _b;
220
392
  if (!shape)
221
393
  return undefined;
394
+ const shapes = activeNodeShapes();
222
395
  if (typeof shape === 'string')
223
- return nodeShapeRegistry.get(shape);
396
+ return shapes.get(shape);
224
397
  const asData = shape;
225
398
  // NodeShapeData is a plain object with an id; a Shape class is a function.
226
399
  if (typeof shape === 'object' && typeof asData.id === 'string') {
227
- return (_a = nodeShapeRegistry.get(asData.id)) !== null && _a !== void 0 ? _a : asData;
400
+ return (_a = shapes.get(asData.id)) !== null && _a !== void 0 ? _a : asData;
228
401
  }
229
402
  const asClass = shape;
230
403
  const shapeData = asClass === null || asClass === void 0 ? void 0 : asClass.shape;
231
404
  if (shapeData === null || shapeData === void 0 ? void 0 : shapeData.id)
232
- return (_b = nodeShapeRegistry.get(shapeData.id)) !== null && _b !== void 0 ? _b : shapeData;
405
+ return (_b = shapes.get(shapeData.id)) !== null && _b !== void 0 ? _b : shapeData;
233
406
  return undefined;
234
407
  }
235
408
  const warnedUnresolvedExtends = new Set();
@@ -265,7 +438,8 @@ export function getSuperShapes(shape) {
265
438
  if (!start)
266
439
  return chain;
267
440
  // Class-backed: the prototype chain is the authority.
268
- const startClass = nodeShapeToShapeClass.get(start.id);
441
+ const shapes = activeNodeShapes();
442
+ const startClass = activeShapeClasses().get(start.id);
269
443
  if (startClass) {
270
444
  let current = startClass;
271
445
  while (current) {
@@ -286,7 +460,7 @@ export function getSuperShapes(shape) {
286
460
  if (seen.has(parentId))
287
461
  break; // cycle guard — malformed data must not hang a read
288
462
  seen.add(parentId);
289
- const parent = nodeShapeRegistry.get(parentId);
463
+ const parent = shapes.get(parentId);
290
464
  if (!parent) {
291
465
  if (!warnedUnresolvedExtends.has(current.id)) {
292
466
  warnedUnresolvedExtends.add(current.id);
@@ -308,6 +482,7 @@ export function getSubShapes(shape) {
308
482
  if (!start)
309
483
  return [];
310
484
  const index = getChildIndex();
485
+ const shapes = activeNodeShapes();
311
486
  const out = [];
312
487
  const seen = new Set([start.id]);
313
488
  const queue = [start.id];
@@ -317,7 +492,7 @@ export function getSubShapes(shape) {
317
492
  if (seen.has(childId))
318
493
  continue;
319
494
  seen.add(childId);
320
- const child = nodeShapeRegistry.get(childId);
495
+ const child = shapes.get(childId);
321
496
  if (child) {
322
497
  out.push(child);
323
498
  queue.push(childId);
@@ -355,7 +530,7 @@ function superShapesWithin(shape, local) {
355
530
  seen.add(parentId);
356
531
  const parent = local.get(parentId);
357
532
  if (!parent) {
358
- const registered = nodeShapeRegistry.get(parentId);
533
+ const registered = activeNodeShapes().get(parentId);
359
534
  if (registered)
360
535
  chain.push(registered, ...getSuperShapes(registered));
361
536
  break;
@@ -389,11 +564,12 @@ function targetClassIdWithin(shape, local) {
389
564
  if (!id)
390
565
  return undefined;
391
566
  const given = typeof shape === 'string' ? undefined : shape;
392
- const own = (_k = (_g = (_d = (_c = given === null || given === void 0 ? void 0 : given.targetClass) === null || _c === void 0 ? void 0 : _c.id) !== null && _d !== void 0 ? _d : (_f = (_e = local === null || local === void 0 ? void 0 : local.get(id)) === null || _e === void 0 ? void 0 : _e.targetClass) === null || _f === void 0 ? void 0 : _f.id) !== null && _g !== void 0 ? _g : (_j = (_h = getShapeClass(id)) === null || _h === void 0 ? void 0 : _h.targetClass) === null || _j === void 0 ? void 0 : _j.id) !== null && _k !== void 0 ? _k : (_m = (_l = nodeShapeRegistry.get(id)) === null || _l === void 0 ? void 0 : _l.targetClass) === null || _m === void 0 ? void 0 : _m.id;
567
+ const shapes = activeNodeShapes();
568
+ const own = (_k = (_g = (_d = (_c = given === null || given === void 0 ? void 0 : given.targetClass) === null || _c === void 0 ? void 0 : _c.id) !== null && _d !== void 0 ? _d : (_f = (_e = local === null || local === void 0 ? void 0 : local.get(id)) === null || _e === void 0 ? void 0 : _e.targetClass) === null || _f === void 0 ? void 0 : _f.id) !== null && _g !== void 0 ? _g : (_j = (_h = getShapeClass(id)) === null || _h === void 0 ? void 0 : _h.targetClass) === null || _j === void 0 ? void 0 : _j.id) !== null && _k !== void 0 ? _k : (_m = (_l = shapes.get(id)) === null || _l === void 0 ? void 0 : _l.targetClass) === null || _m === void 0 ? void 0 : _m.id;
393
569
  if (own)
394
570
  return own;
395
571
  // A data-only shape's targetClass is not inherited for free, so walk `extends`.
396
- const start = (_o = local === null || local === void 0 ? void 0 : local.get(id)) !== null && _o !== void 0 ? _o : ((given === null || given === void 0 ? void 0 : given.extends) ? given : (_p = nodeShapeRegistry.get(id)) !== null && _p !== void 0 ? _p : given);
572
+ const start = (_o = local === null || local === void 0 ? void 0 : local.get(id)) !== null && _o !== void 0 ? _o : ((given === null || given === void 0 ? void 0 : given.extends) ? given : (_p = shapes.get(id)) !== null && _p !== void 0 ? _p : given);
397
573
  if (!start)
398
574
  return undefined;
399
575
  return (_r = (_q = superShapesWithin(start, local).find((s) => { var _a; return (_a = s.targetClass) === null || _a === void 0 ? void 0 : _a.id; })) === null || _q === void 0 ? void 0 : _q.targetClass) === null || _r === void 0 ? void 0 : _r.id;
@@ -428,14 +604,8 @@ function leastSpecific(candidates, local) {
428
604
  return (roots.length ? roots : [...candidates]).sort(byId);
429
605
  }
430
606
  /** targetClass IRI → registered shapes targeting it, ordered. Rebuilt when the registry changes. */
431
- let targetClassIndex = null;
432
- let targetClassIndexVersion = -1;
433
- function getTargetClassIndex() {
434
- if (targetClassIndex && targetClassIndexVersion === registryState.registryVersion) {
435
- return targetClassIndex;
436
- }
437
- const index = new Map();
438
- nodeShapeRegistry.forEach((shape) => {
607
+ const targetClassIndex = new RegistryCache((index) => {
608
+ activeNodeShapes().forEach((shape) => {
439
609
  const classId = targetClassIdWithin(shape);
440
610
  if (!classId)
441
611
  return;
@@ -446,9 +616,9 @@ function getTargetClassIndex() {
446
616
  index.set(classId, [shape]);
447
617
  });
448
618
  index.forEach((shapes, classId) => index.set(classId, leastSpecific(shapes)));
449
- targetClassIndex = index;
450
- targetClassIndexVersion = registryState.registryVersion;
451
- return index;
619
+ });
620
+ function getTargetClassIndex() {
621
+ return targetClassIndex.get();
452
622
  }
453
623
  export function getShapesForTargetClass(classIri, shapes) {
454
624
  var _a;
@@ -462,6 +632,7 @@ export function getShapesForTargetClass(classIri, shapes) {
462
632
  }
463
633
  export function addNodeShapeToShapeClass(nodeShape, shapeClass) {
464
634
  var _a, _b;
635
+ assertNoActiveCatalog();
465
636
  if (!(nodeShape === null || nodeShape === void 0 ? void 0 : nodeShape.id)) {
466
637
  return;
467
638
  }
@@ -526,6 +697,17 @@ export function resolveShapeConstructor(nodeShape) {
526
697
  // i.e. a ShapeConstructor. Same cast getShapeClass documents.
527
698
  return getOrCreateShapeAdapter(id);
528
699
  }
700
+ /**
701
+ * The constructor the query layer resolves an IRI to. In the global registry it is
702
+ * `getShapeClass` (authored classes only, as always). Inside a catalog, where every shape
703
+ * but core's built-ins is data, it is the catalog's constructor for any shape it holds.
704
+ * @internal
705
+ */
706
+ export function getQueryShapeClass(nodeShape) {
707
+ return registryState.activeCatalog === undefined
708
+ ? getShapeClass(nodeShape)
709
+ : resolveShapeConstructor(nodeShape);
710
+ }
529
711
  export function getShapeClass(nodeShape) {
530
712
  const id = typeof nodeShape === 'string' ? nodeShape : nodeShape === null || nodeShape === void 0 ? void 0 : nodeShape.id;
531
713
  if (!id) {
@@ -533,13 +715,18 @@ export function getShapeClass(nodeShape) {
533
715
  }
534
716
  // SAFETY: The map stores `typeof Shape` (abstract), but registered shapes are always
535
717
  // concrete subclasses with a constructor and static .shape — i.e. ShapeConstructor.
718
+ return activeShapeClasses().get(id);
719
+ }
720
+ /** The class registered globally for an IRI, whatever catalog is active. @internal */
721
+ export function getRegisteredShapeClass(id) {
536
722
  return nodeShapeToShapeClass.get(id);
537
723
  }
538
724
  /**
539
- * Returns all registered shape classes (keyed by NodeShape URI).
725
+ * Returns all registered shape classes (keyed by NodeShape URI). Inside a catalog scope:
726
+ * core's built-in classes only — a catalog holds no other class.
540
727
  */
541
728
  export function getAllShapeClasses() {
542
- return nodeShapeToShapeClass;
729
+ return activeShapeClasses();
543
730
  }
544
731
  /**
545
732
  * Returns all the sub shapes of the given shape
@@ -548,21 +735,17 @@ export function getAllShapeClasses() {
548
735
  */
549
736
  export function getSubShapesClasses(shape, _internalKey) {
550
737
  let key = _internalKey || getKey(shape);
551
- if (subShapesCacheVersion !== registryState.registryVersion) {
552
- subShapesCache.clear();
553
- mostSpecificSubShapesCache.clear();
554
- subShapesCacheVersion = registryState.registryVersion;
555
- }
556
- if (!subShapesCache.has(key)) {
738
+ const cache = subShapesCache.get();
739
+ if (!cache.has(key)) {
557
740
  //apply the hasSuperclass function to the shape
558
741
  let filterFunction = applyFnToShapeOrArray(shape, hasSubClass);
559
742
  //filter and then sort the results based on their inheritance (most specific classes first, so we use hasSuperClass for the sorting)
560
- subShapesCache.set(key, filterShapeClasses(filterFunction).sort((a, b) => {
743
+ cache.set(key, filterShapeClasses(filterFunction).sort((a, b) => {
561
744
  return hasSubClass(a, b) ? 1 : -1;
562
745
  }));
563
746
  }
564
747
  //return a copy of the array to prevent it from being modified
565
- return [...subShapesCache.get(key)];
748
+ return [...cache.get(key)];
566
749
  }
567
750
  /**
568
751
  * Returns all the superclasses of the given shape
@@ -622,7 +805,7 @@ function applyFnToShapeOrArray(shape, filterFn) {
622
805
  }
623
806
  function filterShapeClasses(filterFn) {
624
807
  let result = [];
625
- nodeShapeToShapeClass.forEach((shapeClass) => {
808
+ activeShapeClasses().forEach((shapeClass) => {
626
809
  if (filterFn(shapeClass)) {
627
810
  result.push(shapeClass);
628
811
  }
@@ -630,7 +813,7 @@ function filterShapeClasses(filterFn) {
630
813
  return result;
631
814
  }
632
815
  export function getLeastSpecificShapeClasses(shapes) {
633
- let shapeClasses = shapes.map((shape) => getShapeClass(shape.nodeShape.id));
816
+ let shapeClasses = shapes.map((shape) => getQueryShapeClass(shape.nodeShape.id));
634
817
  return filterShapesToLeastSpecific(shapeClasses);
635
818
  }
636
819
  export function getMostSpecificSubShapes(shape) {
@@ -639,18 +822,14 @@ export function getMostSpecificSubShapes(shape) {
639
822
  }
640
823
  //get the subshapes of the given shapes
641
824
  let key = shape.map((s) => s.name).join(',');
642
- if (subShapesCacheVersion !== registryState.registryVersion) {
643
- subShapesCache.clear();
644
- mostSpecificSubShapesCache.clear();
645
- subShapesCacheVersion = registryState.registryVersion;
646
- }
647
- if (!mostSpecificSubShapesCache.has(key)) {
825
+ const cache = mostSpecificSubShapesCache.get();
826
+ if (!cache.has(key)) {
648
827
  //get the subshapes of the given shapes
649
828
  let subShapes = getSubShapesClasses(shape, key);
650
829
  //filter them down to the most specific ones (that are not extended by any other shape)
651
- mostSpecificSubShapesCache.set(key, filterShapesToMostSpecific(subShapes));
830
+ cache.set(key, filterShapesToMostSpecific(subShapes));
652
831
  }
653
- return mostSpecificSubShapesCache.get(key);
832
+ return cache.get(key);
654
833
  }
655
834
  /**
656
835
  * Filters out all shapes that are extended by any other shape in the given set/array