@_linked/core 2.34.0 → 2.36.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 (98) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +54 -0
  3. package/lib/esm/index.d.ts +5 -1
  4. package/lib/esm/index.js +6 -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 +88 -20
  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 +17 -0
  31. package/lib/esm/queries/FieldSet.js +110 -22
  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/MutationQuery.js +3 -2
  38. package/lib/esm/queries/MutationQuery.js.map +1 -1
  39. package/lib/esm/queries/MutationSerialization.js +2 -2
  40. package/lib/esm/queries/MutationSerialization.js.map +1 -1
  41. package/lib/esm/queries/PropertyPath.js +4 -3
  42. package/lib/esm/queries/PropertyPath.js.map +1 -1
  43. package/lib/esm/queries/QueryBuilder.d.ts +7 -0
  44. package/lib/esm/queries/QueryBuilder.js +42 -4
  45. package/lib/esm/queries/QueryBuilder.js.map +1 -1
  46. package/lib/esm/queries/QueryContext.js +2 -0
  47. package/lib/esm/queries/QueryContext.js.map +1 -1
  48. package/lib/esm/queries/SelectQuery.js +8 -3
  49. package/lib/esm/queries/SelectQuery.js.map +1 -1
  50. package/lib/esm/queries/UpdateBuilder.d.ts +4 -0
  51. package/lib/esm/queries/UpdateBuilder.js +15 -1
  52. package/lib/esm/queries/UpdateBuilder.js.map +1 -1
  53. package/lib/esm/queries/fromJSON.d.ts +12 -1
  54. package/lib/esm/queries/fromJSON.js +5 -1
  55. package/lib/esm/queries/fromJSON.js.map +1 -1
  56. package/lib/esm/queries/lower.js +12 -5
  57. package/lib/esm/queries/lower.js.map +1 -1
  58. package/lib/esm/queries/queryDependencies.js +14 -10
  59. package/lib/esm/queries/queryDependencies.js.map +1 -1
  60. package/lib/esm/queries/resolveShape.js +16 -3
  61. package/lib/esm/queries/resolveShape.js.map +1 -1
  62. package/lib/esm/shapes/Shape.js +2 -1
  63. package/lib/esm/shapes/Shape.js.map +1 -1
  64. package/lib/esm/shapes/nodeShapeData.js +7 -10
  65. package/lib/esm/shapes/nodeShapeData.js.map +1 -1
  66. package/lib/esm/shapes/registerRuntimeShape.js +3 -1
  67. package/lib/esm/shapes/registerRuntimeShape.js.map +1 -1
  68. package/lib/esm/shapes/syncShapes.d.ts +6 -2
  69. package/lib/esm/shapes/syncShapes.js +16 -9
  70. package/lib/esm/shapes/syncShapes.js.map +1 -1
  71. package/lib/esm/shapes/validation.js +10 -6
  72. package/lib/esm/shapes/validation.js.map +1 -1
  73. package/lib/esm/sparql/irToAlgebra.js +117 -52
  74. package/lib/esm/sparql/irToAlgebra.js.map +1 -1
  75. package/lib/esm/sparql/ownedLinkCheck.js +6 -1
  76. package/lib/esm/sparql/ownedLinkCheck.js.map +1 -1
  77. package/lib/esm/utils/LinkedStorage.d.ts +7 -0
  78. package/lib/esm/utils/LinkedStorage.js +39 -12
  79. package/lib/esm/utils/LinkedStorage.js.map +1 -1
  80. package/lib/esm/utils/ShapeCatalog.d.ts +57 -0
  81. package/lib/esm/utils/ShapeCatalog.js +86 -0
  82. package/lib/esm/utils/ShapeCatalog.js.map +1 -0
  83. package/lib/esm/utils/ShapeClass.d.ts +73 -8
  84. package/lib/esm/utils/ShapeClass.js +245 -66
  85. package/lib/esm/utils/ShapeClass.js.map +1 -1
  86. package/lib/esm/utils/linkedError.d.ts +35 -0
  87. package/lib/esm/utils/linkedError.js +52 -0
  88. package/lib/esm/utils/linkedError.js.map +1 -0
  89. package/lib/esm/utils/registryCache.d.ts +15 -0
  90. package/lib/esm/utils/registryCache.js +50 -0
  91. package/lib/esm/utils/registryCache.js.map +1 -0
  92. package/lib/esm/utils/sharedState.d.ts +25 -0
  93. package/lib/esm/utils/sharedState.js +34 -0
  94. package/lib/esm/utils/sharedState.js.map +1 -0
  95. package/lib/esm/utils/unresolvedShape.d.ts +42 -0
  96. package/lib/esm/utils/unresolvedShape.js +68 -0
  97. package/lib/esm/utils/unresolvedShape.js.map +1 -0
  98. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.36.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#349](https://github.com/linked-fw/core/pull/349) [`c68a46a`](https://github.com/linked-fw/core/commit/c68a46acab79aa36ba0d5282f01af535ac5253b6) Thanks [@flyon](https://github.com/flyon)! - Add `sharedState(pkg, name, init)`, which keeps module state on `globalThis` under `Symbol.for('<pkg>:<name>')` so two copies of a module share it, and `brandLinkedError(Cls, pkg, name)` with `isLinkedError(err, Cls)`, which recognises an error thrown by another copy of its defining module, where `instanceof` does not.
8
+
9
+ ## 2.35.1
10
+
11
+ ### Patch Changes
12
+
13
+ - [#350](https://github.com/linked-fw/core/pull/350) [`77a7b77`](https://github.com/linked-fw/core/commit/77a7b778fba33e91413e6037221b8db65544c405) Thanks [@flyon](https://github.com/flyon)! - Errors for a shape that does not resolve name the property that points at it, the shape that declares that property, and the active catalog: `Shape class not found for <Place> (property 'home' of '<Person>' points at it; catalog 'p1#main' does not hold it)`. The errors stay plain `Error`s (and `ShapeValidationError` for `validate`), now thrown as `UnresolvedShapeError` with `shapeIri`, `property`, `propertyIri`, `ownerShapeIri` and `catalog` fields. `fromJSON` no longer reads a relation's sub-fields against the parent shape when the relation's value shape does not resolve; it throws the same error.
14
+
15
+ ## 2.35.0
16
+
17
+ ### Minor Changes
18
+
19
+ - [#343](https://github.com/linked-fw/core/pull/343) [`84ddff8`](https://github.com/linked-fw/core/commit/84ddff8fc91a773296461da6983237ba1a1f8c31) Thanks [@flyon](https://github.com/flyon)! - ShapeCatalog and withShapeCatalog: build and run queries against a catalog without registering it.
20
+
21
+ `ShapeCatalog.from(shapes, {name})` builds an immutable set of shapes (core's six built-ins always included from core's code); `withShapeCatalog(catalog, fn)` makes it the shapes every registry accessor reads while `fn` runs. Builders, field sets and IRs capture the catalog and re-enter it when they are lowered, compiled or live-tracked later, so one shape IRI can mean different shapes in different catalogs at once. `fromJSON(json, {catalog})` decodes against a catalog; the wire format is unchanged. Registry caches are kept per catalog, and an app that never uses a catalog runs as before.
22
+
23
+ `syncShapes` now also writes core's six built-in shapes (`Shape`, `NodeShape`, `PropertyShape`, `List`, `PathNode`, `PathList`) into the dataset, with the same hash skip as every other shape. The first sync after upgrading writes them once; after that a sync rewrites one only when its hash changes, which happens when processes on different core versions sync the same dataset (each rewrites the built-ins its version describes differently). Under `orphanScope: 'ownedNamespaces'` core's namespace is never pruned. A `ShapeCatalog` ignores built-in rows in its data and resolves the six from code.
24
+
3
25
  ## 2.34.0
4
26
 
5
27
  ### Minor Changes
package/README.md CHANGED
@@ -1246,6 +1246,30 @@ import {lower} from '@_linked/core';
1246
1246
  const ir = lower(CreateBuilder.from(Person).set({name: 'Alice'}));
1247
1247
  ```
1248
1248
 
1249
+ ### Shape catalogs — queries against shapes you do not register
1250
+
1251
+ The shape registry holds one definition per IRI. A host serving several projects (or branches)
1252
+ that share shape IRIs with different properties resolves each query against a `ShapeCatalog`
1253
+ instead:
1254
+
1255
+ ```typescript
1256
+ import {ShapeCatalog, withShapeCatalog, fromJSON} from '@_linked/core';
1257
+
1258
+ const branch = ShapeCatalog.from(nodeShapeWires, {name: 'project-1#main'}); // immutable
1259
+ const query = withShapeCatalog(branch, () =>
1260
+ SelectBuilder.from('https://example.org/shape/Person').select(['name']),
1261
+ );
1262
+ await query; // lowers and compiles against `branch`, though the scope has closed
1263
+
1264
+ const received = fromJSON(json, {catalog: branch}); // the wire never carries a catalog
1265
+ ```
1266
+
1267
+ A builder keeps the catalog it was made in (`catalogOf(query)`), through clones, `exists()`,
1268
+ `count()`, lowering, compile and live queries. Inside a scope every registry read sees the
1269
+ catalog's shapes plus core's six built-ins (always from code, though `syncShapes` writes them
1270
+ into every dataset); a class the catalog does not hold keeps resolving
1271
+ globally. See [runtime instances](./docs/architecture/runtime-instances.md#shape-catalogs).
1272
+
1249
1273
  ### DSL-JSON — the standard query format
1250
1274
 
1251
1275
  Every Linked query — select **and** every mutation — has one canonical, language-neutral form:
@@ -1344,6 +1368,36 @@ await Team.update({members: {add: [{id: personId}]}}).for({id: teamId});
1344
1368
  - A dataset can report changes made elsewhere through the optional `IDataset.subscribeChanges(listener)`;
1345
1369
  `publishChange(event)` does the same from application code; `invalidate(Shape | {id} | query)` is the manual lever.
1346
1370
 
1371
+ ## Shared state and error brands
1372
+
1373
+ A framework module can be evaluated twice in one process (under `linked start`, once by Vite's SSR
1374
+ module runner and once by Node) or twice in a browser. Two conventions make that second copy harmless:
1375
+
1376
+ - **Module state lives on `globalThis`.** A module-level value that more than one caller reads or writes
1377
+ (a registry, a configured singleton, a static that callers assign) is kept with `sharedState`, under
1378
+ `Symbol.for('<pkg>:<name>')`, so every copy reads and writes the same container:
1379
+
1380
+ ```ts
1381
+ import {sharedState} from '@_linked/core';
1382
+ const state = sharedState('@_linked/my-pkg', 'Mailer.state', () => ({provider: null as Provider | null}));
1383
+ export function setProvider(p: Provider) { state.provider = p; }
1384
+ ```
1385
+
1386
+ - **Errors are matched by brand, not `instanceof`.** A framework error class is branded once with
1387
+ `brandLinkedError(Cls, '<pkg>', '<ClassName>')`, and code that catches an error from another package
1388
+ checks it with `isLinkedError(err, Cls)`. That matches an instance thrown by any copy of the defining
1389
+ module, and a subclass instance against its base.
1390
+
1391
+ ```ts
1392
+ export class StoreError extends Error {}
1393
+ brandLinkedError(StoreError, '@_linked/my-pkg', 'StoreError');
1394
+ // elsewhere
1395
+ if (isLinkedError(err, StoreError)) { /* … */ }
1396
+ ```
1397
+
1398
+ The full convention, including what stays per copy and how React contexts are handled, is in
1399
+ [shared state and error brands](https://github.com/linked-fw/cli/blob/main/docs/architecture/shared-state-and-error-brands.md).
1400
+
1347
1401
  ## TODO
1348
1402
 
1349
1403
  - Allow `preloadFor` to accept another query (not just a component).
@@ -7,13 +7,17 @@ export { rdfList } from './shapes/List.js';
7
7
  export { serializePathToNodeData } from './shapes/serializePathToNodeData.js';
8
8
  export { Shape } from './shapes/Shape.js';
9
9
  export { validate, assertValid, ShapeValidationError } from './shapes/validation.js';
10
+ export { UnresolvedShapeError, type UnresolvedShapeDetails } from './utils/unresolvedShape.js';
10
11
  export type { ValidationReport, ValidationResult, ValidationMode, ValidateOptions, ValidatableShape, } from './shapes/validation.js';
11
12
  export { LinkedStorage } from './utils/LinkedStorage.js';
13
+ export { sharedState, sharedStateKey } from './utils/sharedState.js';
14
+ export { brandLinkedError, isLinkedError, linkedErrorBrand } from './utils/linkedError.js';
12
15
  export { SelectBuilder, QueryBuilder } from './queries/QueryBuilder.js';
13
16
  export { lower } from './queries/lower.js';
14
17
  export type { LowerableQuery } from './queries/lower.js';
15
18
  export { fromJSON } from './queries/fromJSON.js';
16
- export type { QueryJSON } from './queries/fromJSON.js';
19
+ export type { QueryJSON, FromJSONOptions } from './queries/fromJSON.js';
20
+ export { ShapeCatalog, withShapeCatalog, getActiveShapeCatalog, catalogOf, shapeCatalogKey, } from './utils/ShapeCatalog.js';
17
21
  export { getQueryContext, setQueryContext, subscribeQueryContext, PendingQueryContext, UnresolvedContextError, } from './queries/QueryContext.js';
18
22
  export { subscribeQueryDispatch } from './queries/queryDispatch.js';
19
23
  export type { QueryDispatchEvent, QueryDispatchListener } from './queries/queryDispatch.js';
package/lib/esm/index.js CHANGED
@@ -15,11 +15,17 @@ export { serializePathToNodeData } from './shapes/serializePathToNodeData.js';
15
15
  export { Shape } from './shapes/Shape.js';
16
16
  // SHACL-aligned validation of plain data objects against a shape.
17
17
  export { validate, assertValid, ShapeValidationError } from './shapes/validation.js';
18
+ export { UnresolvedShapeError } from './utils/unresolvedShape.js';
18
19
  export { LinkedStorage } from './utils/LinkedStorage.js';
20
+ // Conventions that make a second copy of a framework module harmless.
21
+ export { sharedState, sharedStateKey } from './utils/sharedState.js';
22
+ export { brandLinkedError, isLinkedError, linkedErrorBrand } from './utils/linkedError.js';
19
23
  // New dynamic query building API (Phase 2)
20
24
  export { SelectBuilder, QueryBuilder } from './queries/QueryBuilder.js';
21
25
  export { lower } from './queries/lower.js';
22
26
  export { fromJSON } from './queries/fromJSON.js';
27
+ // Shape catalogs: resolve queries against a set of shapes without registering them.
28
+ export { ShapeCatalog, withShapeCatalog, getActiveShapeCatalog, catalogOf, shapeCatalogKey, } from './utils/ShapeCatalog.js';
23
29
  export { getQueryContext, setQueryContext, subscribeQueryContext, PendingQueryContext, UnresolvedContextError, } from './queries/QueryContext.js';
24
30
  export { subscribeQueryDispatch } from './queries/queryDispatch.js';
25
31
  export { CONTEXT_REF_KEY, encodeContextRef, isContextRefJSON, resolveContextId, } from './queries/ContextRef.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,0DAA0D;AAC1D,OAAO,mBAAmB,CAAC;AAC3B,8EAA8E;AAC9E,OAAO,WAAW,CAAC;AACnB,OAAO,EAAC,UAAU,EAAE,SAAS,EAAC,MAAM,wBAAwB,CAAC;AAE7D,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,2BAA2B,GAC5B,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAC,OAAO,EAAC,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAC,uBAAuB,EAAC,MAAM,qCAAqC,CAAC;AAC5E,sEAAsE;AACtE,OAAO,EAAC,KAAK,EAAC,MAAM,mBAAmB,CAAC;AACxC,kEAAkE;AAClE,OAAO,EAAC,QAAQ,EAAE,WAAW,EAAE,oBAAoB,EAAC,MAAM,wBAAwB,CAAC;AAQnF,OAAO,EAAC,aAAa,EAAC,MAAM,0BAA0B,CAAC;AACvD,2CAA2C;AAC3C,OAAO,EAAC,aAAa,EAAE,YAAY,EAAC,MAAM,2BAA2B,CAAC;AACtE,OAAO,EAAC,KAAK,EAAC,MAAM,oBAAoB,CAAC;AAEzC,OAAO,EAAC,QAAQ,EAAC,MAAM,uBAAuB,CAAC;AAE/C,OAAO,EACL,eAAe,EACf,eAAe,EACf,qBAAqB,EACrB,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAC,sBAAsB,EAAC,MAAM,4BAA4B,CAAC;AAElE,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EAAC,YAAY,EAAE,gBAAgB,EAAC,MAAM,2BAA2B,CAAC;AAEzE,sBAAsB;AACtB,OAAO,EAAC,QAAQ,EAAC,MAAM,uBAAuB,CAAC;AAM/C,+BAA+B;AAC/B,OAAO,EAAC,UAAU,EAAE,UAAU,EAAC,MAAM,yBAAyB,CAAC;AAG/D,OAAO,EAAC,YAAY,EAAE,YAAY,EAAC,MAAM,2BAA2B,CAAC;AAGrE,OAAO,EAAC,aAAa,EAAC,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAC,aAAa,EAAC,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAC,aAAa,EAAC,MAAM,4BAA4B,CAAC;AAGzD,8CAA8C;AAC9C,OAAO,EAAC,cAAc,EAAC,MAAM,iCAAiC,CAAC;AAE/D,OAAO,EAAC,IAAI,EAAC,MAAM,uBAAuB,CAAC;AAe3C,sEAAsE;AACtE,OAAO,EACL,cAAc,EACd,WAAW,GACZ,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EACL,cAAc,EACd,iBAAiB,GAClB,MAAM,gCAAgC,CAAC;AAWxC,0FAA0F;AAC1F,OAAO,EAAC,iBAAiB,EAAE,eAAe,EAAC,MAAM,gCAAgC,CAAC;AAElF,OAAO,EAAC,qBAAqB,EAAC,MAAM,2BAA2B,CAAC;AAEhE,mDAAmD;AACnD,OAAO,EAAC,cAAc,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,aAAa,EAAE,UAAU,EAAC,MAAM,0BAA0B,CAAC","sourcesContent":["/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n// Registers every shape core defines (side effects only).\nimport './shapes/index.js';\n// Registers the live-query store so `query.live()` works (side effects only).\nimport './live.js';\nexport {syncShapes, syncShape} from './shapes/syncShapes.js';\nexport type {SyncShapesOptions, SyncShapeOptions} from './shapes/syncShapes.js';\nexport {\n singleOwnerShapeIri,\n singleOwnerProperties,\n buildSingleOwnerConstraints,\n} from './shapes/singleOwnerConstraint.js';\nexport {rdfList} from './shapes/List.js';\nexport {serializePathToNodeData} from './shapes/serializePathToNodeData.js';\n// Core public API surface (documented as root imports in the README).\nexport {Shape} from './shapes/Shape.js';\n// SHACL-aligned validation of plain data objects against a shape.\nexport {validate, assertValid, ShapeValidationError} from './shapes/validation.js';\nexport type {\n ValidationReport,\n ValidationResult,\n ValidationMode,\n ValidateOptions,\n ValidatableShape,\n} from './shapes/validation.js';\nexport {LinkedStorage} from './utils/LinkedStorage.js';\n// New dynamic query building API (Phase 2)\nexport {SelectBuilder, QueryBuilder} from './queries/QueryBuilder.js';\nexport {lower} from './queries/lower.js';\nexport type {LowerableQuery} from './queries/lower.js';\nexport {fromJSON} from './queries/fromJSON.js';\nexport type {QueryJSON} from './queries/fromJSON.js';\nexport {\n getQueryContext,\n setQueryContext,\n subscribeQueryContext,\n PendingQueryContext,\n UnresolvedContextError,\n} from './queries/QueryContext.js';\nexport {subscribeQueryDispatch} from './queries/queryDispatch.js';\nexport type {QueryDispatchEvent, QueryDispatchListener} from './queries/queryDispatch.js';\nexport {\n CONTEXT_REF_KEY,\n encodeContextRef,\n isContextRefJSON,\n resolveContextId,\n} from './queries/ContextRef.js';\nexport type {ContextRefJSON} from './queries/ContextRef.js';\nexport {PropertyPath, walkPropertyPath} from './queries/PropertyPath.js';\n\n// Phase 3a — FieldSet\nexport {FieldSet} from './queries/FieldSet.js';\nexport type {FieldSetEntry, FieldSetInput, FieldSetJSON, FieldSetFieldJSON} from './queries/FieldSet.js';\n\n// Phase 4 — Serialization types\nexport type {QueryBuilderJSON} from './queries/QueryBuilder.js';\n\n// Phase 3b — Mutation builders\nexport {AskBuilder, isAskQuery} from './queries/AskBuilder.js';\nexport type {AskSpec} from './queries/AskBuilder.js';\nexport type {AskQuery, AskQueryJSON, RawAskInput} from './queries/AskQuery.js';\nexport {CountBuilder, isCountQuery} from './queries/CountBuilder.js';\nexport type {CountSpec} from './queries/CountBuilder.js';\nexport type {CountQuery, CountQueryJSON, RawCountInput} from './queries/CountQuery.js';\nexport {CreateBuilder} from './queries/CreateBuilder.js';\nexport {UpdateBuilder} from './queries/UpdateBuilder.js';\nexport {DeleteBuilder} from './queries/DeleteBuilder.js';\nexport type {DeleteId} from './queries/DeleteBuilder.js';\n\n// Expressions — computed fields and functions\nexport {ExpressionNode} from './expressions/ExpressionNode.js';\nexport type {ExpressionInput, PropertyRefMap} from './expressions/ExpressionNode.js';\nexport {Expr} from './expressions/Expr.js';\nexport type {\n ExpressionUpdateProxy,\n ExpressionUpdateResult,\n BaseExpressionMethods,\n NumericExpressionMethods,\n StringExpressionMethods,\n DateExpressionMethods,\n BooleanExpressionMethods,\n} from './expressions/ExpressionMethods.js';\n\n// Phase 5 — Component query integration\nexport type {LinkedComponentInterface, QueryComponentLike} from './queries/SelectQuery.js';\n\n\n// Mutation DSL-JSON serialization (create/update/delete to/from JSON)\nexport {\n encodeNodeData,\n encodeValue,\n} from './queries/MutationSerialization.js';\nexport {\n decodeNodeData,\n lowerMutationJSON,\n} from './queries/lowerMutationJSON.js';\nexport type {\n MutationJSON,\n CreateMutationJSON,\n UpdateMutationJSON,\n DeleteMutationJSON,\n MutationValueJSON,\n MutationNodeDataJSON,\n} from './queries/MutationSerialization.js';\n\n\n// Live-query dependency helpers — what a query reads and a mutation writes, as predicates\nexport {queryDependencies, mutationEffects} from './queries/queryDependencies.js';\nexport type {QueryDependencies, MutationEffects} from './queries/queryDependencies.js';\nexport {findPropertyShapeById} from './shapes/nodeShapeData.js';\n\n// Live queries — see documentation/live-queries.md\nexport {LiveQueryStore, getLiveQueryStore, resetLiveQueryStore, publishChange, invalidate} from './live/LiveQueryStore.js';\nexport type {ChangeEvent} from './live/changes.js';\nexport type {\n LiveQuery,\n LiveState,\n LiveStatus,\n LiveListener,\n LiveQueryOptions,\n LiveQueryStoreOptions,\n} from './live/LiveQueryStore.js';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,0DAA0D;AAC1D,OAAO,mBAAmB,CAAC;AAC3B,8EAA8E;AAC9E,OAAO,WAAW,CAAC;AACnB,OAAO,EAAC,UAAU,EAAE,SAAS,EAAC,MAAM,wBAAwB,CAAC;AAE7D,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,2BAA2B,GAC5B,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAC,OAAO,EAAC,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAC,uBAAuB,EAAC,MAAM,qCAAqC,CAAC;AAC5E,sEAAsE;AACtE,OAAO,EAAC,KAAK,EAAC,MAAM,mBAAmB,CAAC;AACxC,kEAAkE;AAClE,OAAO,EAAC,QAAQ,EAAE,WAAW,EAAE,oBAAoB,EAAC,MAAM,wBAAwB,CAAC;AACnF,OAAO,EAAC,oBAAoB,EAA8B,MAAM,4BAA4B,CAAC;AAQ7F,OAAO,EAAC,aAAa,EAAC,MAAM,0BAA0B,CAAC;AACvD,sEAAsE;AACtE,OAAO,EAAC,WAAW,EAAE,cAAc,EAAC,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAC,gBAAgB,EAAE,aAAa,EAAE,gBAAgB,EAAC,MAAM,wBAAwB,CAAC;AACzF,2CAA2C;AAC3C,OAAO,EAAC,aAAa,EAAE,YAAY,EAAC,MAAM,2BAA2B,CAAC;AACtE,OAAO,EAAC,KAAK,EAAC,MAAM,oBAAoB,CAAC;AAEzC,OAAO,EAAC,QAAQ,EAAC,MAAM,uBAAuB,CAAC;AAE/C,oFAAoF;AACpF,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,qBAAqB,EACrB,SAAS,EACT,eAAe,GAChB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,eAAe,EACf,eAAe,EACf,qBAAqB,EACrB,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAC,sBAAsB,EAAC,MAAM,4BAA4B,CAAC;AAElE,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EAAC,YAAY,EAAE,gBAAgB,EAAC,MAAM,2BAA2B,CAAC;AAEzE,sBAAsB;AACtB,OAAO,EAAC,QAAQ,EAAC,MAAM,uBAAuB,CAAC;AAM/C,+BAA+B;AAC/B,OAAO,EAAC,UAAU,EAAE,UAAU,EAAC,MAAM,yBAAyB,CAAC;AAG/D,OAAO,EAAC,YAAY,EAAE,YAAY,EAAC,MAAM,2BAA2B,CAAC;AAGrE,OAAO,EAAC,aAAa,EAAC,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAC,aAAa,EAAC,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAC,aAAa,EAAC,MAAM,4BAA4B,CAAC;AAGzD,8CAA8C;AAC9C,OAAO,EAAC,cAAc,EAAC,MAAM,iCAAiC,CAAC;AAE/D,OAAO,EAAC,IAAI,EAAC,MAAM,uBAAuB,CAAC;AAe3C,sEAAsE;AACtE,OAAO,EACL,cAAc,EACd,WAAW,GACZ,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EACL,cAAc,EACd,iBAAiB,GAClB,MAAM,gCAAgC,CAAC;AAWxC,0FAA0F;AAC1F,OAAO,EAAC,iBAAiB,EAAE,eAAe,EAAC,MAAM,gCAAgC,CAAC;AAElF,OAAO,EAAC,qBAAqB,EAAC,MAAM,2BAA2B,CAAC;AAEhE,mDAAmD;AACnD,OAAO,EAAC,cAAc,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,aAAa,EAAE,UAAU,EAAC,MAAM,0BAA0B,CAAC","sourcesContent":["/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n// Registers every shape core defines (side effects only).\nimport './shapes/index.js';\n// Registers the live-query store so `query.live()` works (side effects only).\nimport './live.js';\nexport {syncShapes, syncShape} from './shapes/syncShapes.js';\nexport type {SyncShapesOptions, SyncShapeOptions} from './shapes/syncShapes.js';\nexport {\n singleOwnerShapeIri,\n singleOwnerProperties,\n buildSingleOwnerConstraints,\n} from './shapes/singleOwnerConstraint.js';\nexport {rdfList} from './shapes/List.js';\nexport {serializePathToNodeData} from './shapes/serializePathToNodeData.js';\n// Core public API surface (documented as root imports in the README).\nexport {Shape} from './shapes/Shape.js';\n// SHACL-aligned validation of plain data objects against a shape.\nexport {validate, assertValid, ShapeValidationError} from './shapes/validation.js';\nexport {UnresolvedShapeError, type UnresolvedShapeDetails} from './utils/unresolvedShape.js';\nexport type {\n ValidationReport,\n ValidationResult,\n ValidationMode,\n ValidateOptions,\n ValidatableShape,\n} from './shapes/validation.js';\nexport {LinkedStorage} from './utils/LinkedStorage.js';\n// Conventions that make a second copy of a framework module harmless.\nexport {sharedState, sharedStateKey} from './utils/sharedState.js';\nexport {brandLinkedError, isLinkedError, linkedErrorBrand} from './utils/linkedError.js';\n// New dynamic query building API (Phase 2)\nexport {SelectBuilder, QueryBuilder} from './queries/QueryBuilder.js';\nexport {lower} from './queries/lower.js';\nexport type {LowerableQuery} from './queries/lower.js';\nexport {fromJSON} from './queries/fromJSON.js';\nexport type {QueryJSON, FromJSONOptions} from './queries/fromJSON.js';\n// Shape catalogs: resolve queries against a set of shapes without registering them.\nexport {\n ShapeCatalog,\n withShapeCatalog,\n getActiveShapeCatalog,\n catalogOf,\n shapeCatalogKey,\n} from './utils/ShapeCatalog.js';\nexport {\n getQueryContext,\n setQueryContext,\n subscribeQueryContext,\n PendingQueryContext,\n UnresolvedContextError,\n} from './queries/QueryContext.js';\nexport {subscribeQueryDispatch} from './queries/queryDispatch.js';\nexport type {QueryDispatchEvent, QueryDispatchListener} from './queries/queryDispatch.js';\nexport {\n CONTEXT_REF_KEY,\n encodeContextRef,\n isContextRefJSON,\n resolveContextId,\n} from './queries/ContextRef.js';\nexport type {ContextRefJSON} from './queries/ContextRef.js';\nexport {PropertyPath, walkPropertyPath} from './queries/PropertyPath.js';\n\n// Phase 3a — FieldSet\nexport {FieldSet} from './queries/FieldSet.js';\nexport type {FieldSetEntry, FieldSetInput, FieldSetJSON, FieldSetFieldJSON} from './queries/FieldSet.js';\n\n// Phase 4 — Serialization types\nexport type {QueryBuilderJSON} from './queries/QueryBuilder.js';\n\n// Phase 3b — Mutation builders\nexport {AskBuilder, isAskQuery} from './queries/AskBuilder.js';\nexport type {AskSpec} from './queries/AskBuilder.js';\nexport type {AskQuery, AskQueryJSON, RawAskInput} from './queries/AskQuery.js';\nexport {CountBuilder, isCountQuery} from './queries/CountBuilder.js';\nexport type {CountSpec} from './queries/CountBuilder.js';\nexport type {CountQuery, CountQueryJSON, RawCountInput} from './queries/CountQuery.js';\nexport {CreateBuilder} from './queries/CreateBuilder.js';\nexport {UpdateBuilder} from './queries/UpdateBuilder.js';\nexport {DeleteBuilder} from './queries/DeleteBuilder.js';\nexport type {DeleteId} from './queries/DeleteBuilder.js';\n\n// Expressions — computed fields and functions\nexport {ExpressionNode} from './expressions/ExpressionNode.js';\nexport type {ExpressionInput, PropertyRefMap} from './expressions/ExpressionNode.js';\nexport {Expr} from './expressions/Expr.js';\nexport type {\n ExpressionUpdateProxy,\n ExpressionUpdateResult,\n BaseExpressionMethods,\n NumericExpressionMethods,\n StringExpressionMethods,\n DateExpressionMethods,\n BooleanExpressionMethods,\n} from './expressions/ExpressionMethods.js';\n\n// Phase 5 — Component query integration\nexport type {LinkedComponentInterface, QueryComponentLike} from './queries/SelectQuery.js';\n\n\n// Mutation DSL-JSON serialization (create/update/delete to/from JSON)\nexport {\n encodeNodeData,\n encodeValue,\n} from './queries/MutationSerialization.js';\nexport {\n decodeNodeData,\n lowerMutationJSON,\n} from './queries/lowerMutationJSON.js';\nexport type {\n MutationJSON,\n CreateMutationJSON,\n UpdateMutationJSON,\n DeleteMutationJSON,\n MutationValueJSON,\n MutationNodeDataJSON,\n} from './queries/MutationSerialization.js';\n\n\n// Live-query dependency helpers — what a query reads and a mutation writes, as predicates\nexport {queryDependencies, mutationEffects} from './queries/queryDependencies.js';\nexport type {QueryDependencies, MutationEffects} from './queries/queryDependencies.js';\nexport {findPropertyShapeById} from './shapes/nodeShapeData.js';\n\n// Live queries — see documentation/live-queries.md\nexport {LiveQueryStore, getLiveQueryStore, resetLiveQueryStore, publishChange, invalidate} from './live/LiveQueryStore.js';\nexport type {ChangeEvent} from './live/changes.js';\nexport type {\n LiveQuery,\n LiveState,\n LiveStatus,\n LiveListener,\n LiveQueryOptions,\n LiveQueryStoreOptions,\n} from './live/LiveQueryStore.js';\n"]}
@@ -1,4 +1,5 @@
1
1
  import { type MutationEffects, type QueryDependencies } from '../queries/queryDependencies.js';
2
+ import type { ShapeCatalog } from '../utils/ShapeCatalog.js';
2
3
  import type { ShapeConstructor } from '../shapes/Shape.js';
3
4
  import { type ChangeEvent, type ChangeOrigin } from './changes.js';
4
5
  import { type InstanceParams, type LiveBuilder, type LiveQueryKind } from './keys.js';
@@ -64,6 +65,11 @@ export interface LiveQuery<R = unknown> extends PromiseLike<R> {
64
65
  export type Template = {
65
66
  key: string;
66
67
  kind: LiveQueryKind;
68
+ /**
69
+ * The catalog the template's queries resolve in (undefined = the global registry). Part
70
+ * of its identity: the same JSON in two catalogs is two templates.
71
+ */
72
+ catalog?: ShapeCatalog;
67
73
  name?: string;
68
74
  json: Record<string, unknown>;
69
75
  /** Subject-stripped builder, used for dependency analysis only. */
@@ -124,6 +130,8 @@ export declare class LiveQueryStore {
124
130
  private readonly _recentChanges;
125
131
  private _pendingRefetch;
126
132
  private _flushScheduled;
133
+ /** How many templates each catalog has; a change is decoded once per catalog listed here. */
134
+ private readonly _catalogTemplates;
127
135
  constructor();
128
136
  /** Register (or find) the template of `query`. Options merge into an existing template. */
129
137
  template(query: LiveBuilder, opts?: LiveQueryOptions): Template;
@@ -190,6 +198,19 @@ export declare class LiveQueryStore {
190
198
  * in quick succession are two changes — and manual invalidations never are.
191
199
  */
192
200
  publish(event: ChangeEvent, origin?: ChangeOrigin): void;
201
+ /**
202
+ * `within` limits the change to one catalog's templates (undefined = the global registry's):
203
+ * a local mutation changes the data of the catalog it was made in, and only that one.
204
+ */
205
+ private _publish;
206
+ /**
207
+ * The effects of a change in the global registry and in each catalog that has
208
+ * templates. A mutation is decoded once per catalog; one that does not resolve in a
209
+ * catalog (its shape is not there) leaves that catalog's templates alone, and effects
210
+ * given as data apply to a catalog only when it holds their shape. Throws only when
211
+ * the change resolves nowhere, as a change that cannot be decoded did before catalogs.
212
+ */
213
+ private _decodePerCatalog;
193
214
  private _scheduleFlush;
194
215
  /**
195
216
  * Local mutations. Select and ask events are ignored: the store's own fetches
@@ -220,6 +241,7 @@ export declare class LiveQueryStore {
220
241
  private _onContextChange;
221
242
  /** Remove an instance from its template (and the template when it is empty and not pinned). */
222
243
  private _dropInstance;
244
+ private _forgetCatalogTemplate;
223
245
  private _scheduleGc;
224
246
  private _cancelGc;
225
247
  }
@@ -17,11 +17,12 @@ import { subscribeQueryContext, UnresolvedContextError } from '../queries/QueryC
17
17
  import { resolveContextId } from '../queries/ContextRef.js';
18
18
  import { subscribeQueryDispatch } from '../queries/queryDispatch.js';
19
19
  import { mutationEffects, queryDependencies, } from '../queries/queryDependencies.js';
20
- import { getShapeClass } from '../utils/ShapeClass.js';
20
+ import { catalogOf, getActiveShapeCatalog, getShapeClass, withShapeCatalog, } from '../utils/ShapeClass.js';
21
21
  import { effectsHash, normalizeChange } from './changes.js';
22
22
  import { selectInstances } from './matcher.js';
23
- import { kindOf, paramsKey, splitQuery, stableStringify, stripSubjects, } from './keys.js';
23
+ import { kindOf, paramsKey, splitQuery, stripSubjects, templateKeyFor, } from './keys.js';
24
24
  import { setLiveQueryStore, peekLiveQueryStore } from './registry.js';
25
+ import { isLinkedError } from '../utils/linkedError.js';
25
26
  const IDLE = Object.freeze({ status: 'pending', notFound: false, refreshing: false });
26
27
  // ---------------------------------------------------------------------------
27
28
  // Store
@@ -48,6 +49,8 @@ export class LiveQueryStore {
48
49
  this._recentChanges = new Map();
49
50
  this._pendingRefetch = new Set();
50
51
  this._flushScheduled = false;
52
+ /** How many templates each catalog has; a change is decoded once per catalog listed here. */
53
+ this._catalogTemplates = new Map();
51
54
  this._disposers.push(subscribeQueryContext((name) => this._onContextChange(name)));
52
55
  this._disposers.push(subscribeQueryDispatch((e) => this._onDispatch(e)));
53
56
  this._disposers.push(LinkedStorage.onRoutingChanged(() => this._onRoutingChanged()));
@@ -61,20 +64,16 @@ export class LiveQueryStore {
61
64
  return this._template(splitQuery(query).templateJson, query, opts);
62
65
  }
63
66
  _template(templateJson, query, opts) {
64
- const key = stableStringify(templateJson);
67
+ var _a;
68
+ const catalog = catalogOf(query);
69
+ const key = templateKeyFor(templateJson, catalog);
65
70
  let t = this._templates.get(key);
66
71
  if (!t) {
67
- t = {
68
- key,
69
- kind: kindOf(query),
70
- json: templateJson,
71
- builder: stripSubjects(query),
72
- shapeIri: typeof templateJson.shape === 'string' ? templateJson.shape : undefined,
73
- reactive: true,
74
- pinned: false,
75
- instances: new Map(),
76
- };
72
+ t = Object.assign(Object.assign({ key, kind: kindOf(query) }, (catalog !== undefined ? { catalog } : {})), { json: templateJson, builder: stripSubjects(query), shapeIri: typeof templateJson.shape === 'string' ? templateJson.shape : undefined, reactive: true, pinned: false, instances: new Map() });
77
73
  this._templates.set(key, t);
74
+ if (catalog !== undefined) {
75
+ this._catalogTemplates.set(catalog, ((_a = this._catalogTemplates.get(catalog)) !== null && _a !== void 0 ? _a : 0) + 1);
76
+ }
78
77
  }
79
78
  if (opts.name !== undefined)
80
79
  t.name = opts.name;
@@ -107,7 +106,7 @@ export class LiveQueryStore {
107
106
  template.depsProvisional = false;
108
107
  }
109
108
  catch (err) {
110
- if (!(err instanceof UnresolvedContextError))
109
+ if (!isLinkedError(err, UnresolvedContextError))
111
110
  throw err;
112
111
  if (template.deps)
113
112
  return template.deps; // still provisional
@@ -245,7 +244,7 @@ export class LiveQueryStore {
245
244
  instances.add(i);
246
245
  }
247
246
  else if (typeof target.__queryKind === 'string') {
248
- const t = this._templates.get(stableStringify(splitQuery(target).templateJson));
247
+ const t = this._templates.get(templateKeyFor(splitQuery(target).templateJson, catalogOf(target)));
249
248
  if (t)
250
249
  for (const i of t.instances.values())
251
250
  instances.add(i);
@@ -281,7 +280,17 @@ export class LiveQueryStore {
281
280
  * in quick succession are two changes — and manual invalidations never are.
282
281
  */
283
282
  publish(event, origin = 'app') {
284
- const effects = normalizeChange(event);
283
+ this._publish(event, origin);
284
+ }
285
+ /**
286
+ * `within` limits the change to one catalog's templates (undefined = the global registry's):
287
+ * a local mutation changes the data of the catalog it was made in, and only that one.
288
+ */
289
+ _publish(event, origin, within) {
290
+ const decoded = within
291
+ ? [[within.catalog, normalizeChange(event, { catalog: within.catalog })]]
292
+ : this._decodePerCatalog(event);
293
+ const effects = decoded[0][1];
285
294
  if (origin !== 'manual') {
286
295
  const hash = effectsHash(effects);
287
296
  const now = Date.now();
@@ -297,10 +306,52 @@ export class LiveQueryStore {
297
306
  this._recentChanges.delete(h);
298
307
  }
299
308
  }
300
- for (const inst of selectInstances(this, effects))
301
- this._pendingRefetch.add(inst);
309
+ if (decoded.length === 1 && decoded[0][0] === undefined && this._catalogTemplates.size === 0) {
310
+ for (const inst of selectInstances(this, effects))
311
+ this._pendingRefetch.add(inst);
312
+ }
313
+ else {
314
+ for (const [catalog, e] of decoded) {
315
+ for (const inst of selectInstances(this, e, { catalog }))
316
+ this._pendingRefetch.add(inst);
317
+ }
318
+ }
302
319
  this._scheduleFlush();
303
320
  }
321
+ /**
322
+ * The effects of a change in the global registry and in each catalog that has
323
+ * templates. A mutation is decoded once per catalog; one that does not resolve in a
324
+ * catalog (its shape is not there) leaves that catalog's templates alone, and effects
325
+ * given as data apply to a catalog only when it holds their shape. Throws only when
326
+ * the change resolves nowhere, as a change that cannot be decoded did before catalogs.
327
+ */
328
+ _decodePerCatalog(event) {
329
+ if (this._catalogTemplates.size === 0) {
330
+ return [[undefined, normalizeChange(event, { catalog: undefined })]];
331
+ }
332
+ const out = [];
333
+ let globalError;
334
+ try {
335
+ out.push([undefined, normalizeChange(event, { catalog: undefined })]);
336
+ }
337
+ catch (err) {
338
+ globalError = err;
339
+ }
340
+ for (const catalog of this._catalogTemplates.keys()) {
341
+ try {
342
+ const effects = normalizeChange(event, { catalog });
343
+ if ('effects' in event && !catalog.has(effects.shape))
344
+ continue;
345
+ out.push([catalog, effects]);
346
+ }
347
+ catch (_a) {
348
+ // Not a change to this catalog's shapes.
349
+ }
350
+ }
351
+ if (out.length === 0)
352
+ throw globalError;
353
+ return out;
354
+ }
304
355
  _scheduleFlush() {
305
356
  if (this._flushScheduled)
306
357
  return;
@@ -321,7 +372,12 @@ export class LiveQueryStore {
321
372
  if (e.kind !== 'create' && e.kind !== 'update' && e.kind !== 'delete')
322
373
  return;
323
374
  const query = e.query;
324
- const dataset = (_a = e.target) !== null && _a !== void 0 ? _a : LinkedStorage.getDatasetForShapeClass((((_b = query.shape) === null || _b === void 0 ? void 0 : _b.id) ? getShapeClass(query.shape.id) : undefined));
375
+ // The routing lookup reads the registry, so it runs in the mutation's catalog.
376
+ const catalog = catalogOf(query);
377
+ const shapeClassOf = (id) => catalog === getActiveShapeCatalog()
378
+ ? getShapeClass(id)
379
+ : withShapeCatalog(catalog, () => getShapeClass(id));
380
+ const dataset = (_a = e.target) !== null && _a !== void 0 ? _a : LinkedStorage.getDatasetForShapeClass((((_b = query.shape) === null || _b === void 0 ? void 0 : _b.id) ? shapeClassOf(query.shape.id) : undefined));
325
381
  if (dataset === null || dataset === void 0 ? void 0 : dataset.authoritativeChanges)
326
382
  return; // the dataset's own feed will report it
327
383
  e.result.then((result) => {
@@ -333,7 +389,7 @@ export class LiveQueryStore {
333
389
  console.error('[linked] could not derive the effects of a mutation; live queries were not refetched', err);
334
390
  return;
335
391
  }
336
- this.publish({ effects }, 'local');
392
+ this._publish({ effects }, 'local', { catalog });
337
393
  }, () => { });
338
394
  }
339
395
  /** Subscribe to the change feeds of the datasets currently routed; drop feeds of datasets that left. */
@@ -383,6 +439,7 @@ export class LiveQueryStore {
383
439
  this._dropInstance(i, true);
384
440
  }
385
441
  this._templates.clear();
442
+ this._catalogTemplates.clear();
386
443
  this.templatesByProp.clear();
387
444
  this.templatesByShape.clear();
388
445
  this.instancesById.clear();
@@ -548,8 +605,19 @@ export class LiveQueryStore {
548
605
  if (!keepTemplate && t.instances.size === 0 && !t.pinned) {
549
606
  this._templates.delete(t.key);
550
607
  this._unindexTemplate(t);
608
+ this._forgetCatalogTemplate(t);
551
609
  }
552
610
  }
611
+ _forgetCatalogTemplate(t) {
612
+ var _a;
613
+ if (t.catalog === undefined)
614
+ return;
615
+ const left = ((_a = this._catalogTemplates.get(t.catalog)) !== null && _a !== void 0 ? _a : 1) - 1;
616
+ if (left > 0)
617
+ this._catalogTemplates.set(t.catalog, left);
618
+ else
619
+ this._catalogTemplates.delete(t.catalog);
620
+ }
553
621
  _scheduleGc(inst) {
554
622
  var _a, _b;
555
623
  this._cancelGc(inst);