@tanstack/db 0.5.33 → 0.6.1
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.
- package/dist/cjs/collection/change-events.cjs.map +1 -1
- package/dist/cjs/collection/change-events.d.cts +3 -2
- package/dist/cjs/collection/changes.cjs +13 -4
- package/dist/cjs/collection/changes.cjs.map +1 -1
- package/dist/cjs/collection/changes.d.cts +10 -1
- package/dist/cjs/collection/cleanup-queue.cjs +89 -0
- package/dist/cjs/collection/cleanup-queue.cjs.map +1 -0
- package/dist/cjs/collection/cleanup-queue.d.cts +30 -0
- package/dist/cjs/collection/events.cjs +14 -0
- package/dist/cjs/collection/events.cjs.map +1 -1
- package/dist/cjs/collection/events.d.cts +39 -1
- package/dist/cjs/collection/index.cjs +66 -28
- package/dist/cjs/collection/index.cjs.map +1 -1
- package/dist/cjs/collection/index.d.cts +49 -36
- package/dist/cjs/collection/indexes.cjs +211 -62
- package/dist/cjs/collection/indexes.cjs.map +1 -1
- package/dist/cjs/collection/indexes.d.cts +27 -17
- package/dist/cjs/collection/lifecycle.cjs +5 -22
- package/dist/cjs/collection/lifecycle.cjs.map +1 -1
- package/dist/cjs/collection/lifecycle.d.cts +0 -1
- package/dist/cjs/collection/mutations.cjs +18 -0
- package/dist/cjs/collection/mutations.cjs.map +1 -1
- package/dist/cjs/collection/mutations.d.cts +1 -0
- package/dist/cjs/collection/state.cjs +381 -53
- package/dist/cjs/collection/state.cjs.map +1 -1
- package/dist/cjs/collection/state.d.cts +65 -1
- package/dist/cjs/collection/subscription.cjs +6 -0
- package/dist/cjs/collection/subscription.cjs.map +1 -1
- package/dist/cjs/collection/subscription.d.cts +4 -0
- package/dist/cjs/collection/sync.cjs +108 -1
- package/dist/cjs/collection/sync.cjs.map +1 -1
- package/dist/cjs/collection/sync.d.cts +2 -0
- package/dist/cjs/collection/transaction-metadata.cjs +5 -0
- package/dist/cjs/collection/transaction-metadata.cjs.map +1 -0
- package/dist/cjs/collection/transaction-metadata.d.cts +1 -0
- package/dist/cjs/errors.cjs +8 -0
- package/dist/cjs/errors.cjs.map +1 -1
- package/dist/cjs/errors.d.cts +3 -0
- package/dist/cjs/index.cjs +22 -4
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +11 -3
- package/dist/cjs/indexes/auto-index.cjs +13 -6
- package/dist/cjs/indexes/auto-index.cjs.map +1 -1
- package/dist/cjs/indexes/base-index.cjs +0 -3
- package/dist/cjs/indexes/base-index.cjs.map +1 -1
- package/dist/cjs/indexes/base-index.d.cts +2 -6
- package/dist/cjs/indexes/basic-index.cjs +361 -0
- package/dist/cjs/indexes/basic-index.cjs.map +1 -0
- package/dist/cjs/indexes/basic-index.d.cts +102 -0
- package/dist/cjs/indexes/btree-index.cjs.map +1 -1
- package/dist/cjs/indexes/btree-index.d.cts +1 -1
- package/dist/cjs/indexes/index-options.d.cts +8 -9
- package/dist/cjs/indexes/index-registry.cjs +89 -0
- package/dist/cjs/indexes/index-registry.cjs.map +1 -0
- package/dist/cjs/indexes/index-registry.d.cts +61 -0
- package/dist/cjs/local-only.cjs +5 -0
- package/dist/cjs/local-only.cjs.map +1 -1
- package/dist/cjs/query/builder/functions.cjs +27 -11
- package/dist/cjs/query/builder/functions.cjs.map +1 -1
- package/dist/cjs/query/builder/functions.d.cts +25 -3
- package/dist/cjs/query/builder/index.cjs +200 -39
- package/dist/cjs/query/builder/index.cjs.map +1 -1
- package/dist/cjs/query/builder/index.d.cts +4 -3
- package/dist/cjs/query/builder/ref-proxy.cjs.map +1 -1
- package/dist/cjs/query/builder/ref-proxy.d.cts +14 -3
- package/dist/cjs/query/builder/types.d.cts +84 -19
- package/dist/cjs/query/compiler/evaluators.cjs +51 -0
- package/dist/cjs/query/compiler/evaluators.cjs.map +1 -1
- package/dist/cjs/query/compiler/group-by.cjs +100 -28
- package/dist/cjs/query/compiler/group-by.cjs.map +1 -1
- package/dist/cjs/query/compiler/group-by.d.cts +4 -2
- package/dist/cjs/query/compiler/index.cjs +283 -11
- package/dist/cjs/query/compiler/index.cjs.map +1 -1
- package/dist/cjs/query/compiler/index.d.cts +30 -2
- package/dist/cjs/query/compiler/order-by.cjs +29 -10
- package/dist/cjs/query/compiler/order-by.cjs.map +1 -1
- package/dist/cjs/query/compiler/order-by.d.cts +1 -1
- package/dist/cjs/query/compiler/select.cjs +8 -0
- package/dist/cjs/query/compiler/select.cjs.map +1 -1
- package/dist/cjs/query/index.d.cts +2 -1
- package/dist/cjs/query/ir.cjs +18 -1
- package/dist/cjs/query/ir.cjs.map +1 -1
- package/dist/cjs/query/ir.d.cts +21 -1
- package/dist/cjs/query/live/collection-config-builder.cjs +501 -5
- package/dist/cjs/query/live/collection-config-builder.cjs.map +1 -1
- package/dist/cjs/query/live/collection-config-builder.d.cts +7 -0
- package/dist/cjs/query/live/types.d.cts +3 -3
- package/dist/cjs/query/live/utils.cjs +43 -3
- package/dist/cjs/query/live/utils.cjs.map +1 -1
- package/dist/cjs/query/live/utils.d.cts +1 -0
- package/dist/cjs/query/live-query-collection.cjs.map +1 -1
- package/dist/cjs/query/live-query-collection.d.cts +9 -6
- package/dist/cjs/query/query-once.cjs.map +1 -1
- package/dist/cjs/query/query-once.d.cts +7 -5
- package/dist/cjs/query/subset-dedupe.cjs +9 -3
- package/dist/cjs/query/subset-dedupe.cjs.map +1 -1
- package/dist/cjs/types.d.cts +42 -8
- package/dist/cjs/utils/array-utils.cjs +27 -0
- package/dist/cjs/utils/array-utils.cjs.map +1 -0
- package/dist/cjs/utils/array-utils.d.cts +16 -0
- package/dist/cjs/utils/comparison.cjs +11 -0
- package/dist/cjs/utils/comparison.cjs.map +1 -1
- package/dist/cjs/utils/index-optimization.cjs +4 -0
- package/dist/cjs/utils/index-optimization.cjs.map +1 -1
- package/dist/cjs/utils.cjs +7 -9
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +6 -1
- package/dist/cjs/virtual-props.cjs +33 -0
- package/dist/cjs/virtual-props.cjs.map +1 -0
- package/dist/cjs/virtual-props.d.cts +196 -0
- package/dist/esm/collection/change-events.d.ts +3 -2
- package/dist/esm/collection/change-events.js.map +1 -1
- package/dist/esm/collection/changes.d.ts +10 -1
- package/dist/esm/collection/changes.js +13 -4
- package/dist/esm/collection/changes.js.map +1 -1
- package/dist/esm/collection/cleanup-queue.d.ts +30 -0
- package/dist/esm/collection/cleanup-queue.js +89 -0
- package/dist/esm/collection/cleanup-queue.js.map +1 -0
- package/dist/esm/collection/events.d.ts +39 -1
- package/dist/esm/collection/events.js +14 -0
- package/dist/esm/collection/events.js.map +1 -1
- package/dist/esm/collection/index.d.ts +49 -36
- package/dist/esm/collection/index.js +67 -29
- package/dist/esm/collection/index.js.map +1 -1
- package/dist/esm/collection/indexes.d.ts +27 -17
- package/dist/esm/collection/indexes.js +211 -62
- package/dist/esm/collection/indexes.js.map +1 -1
- package/dist/esm/collection/lifecycle.d.ts +0 -1
- package/dist/esm/collection/lifecycle.js +5 -22
- package/dist/esm/collection/lifecycle.js.map +1 -1
- package/dist/esm/collection/mutations.d.ts +1 -0
- package/dist/esm/collection/mutations.js +18 -0
- package/dist/esm/collection/mutations.js.map +1 -1
- package/dist/esm/collection/state.d.ts +65 -1
- package/dist/esm/collection/state.js +381 -53
- package/dist/esm/collection/state.js.map +1 -1
- package/dist/esm/collection/subscription.d.ts +4 -0
- package/dist/esm/collection/subscription.js +6 -0
- package/dist/esm/collection/subscription.js.map +1 -1
- package/dist/esm/collection/sync.d.ts +2 -0
- package/dist/esm/collection/sync.js +108 -1
- package/dist/esm/collection/sync.js.map +1 -1
- package/dist/esm/collection/transaction-metadata.d.ts +1 -0
- package/dist/esm/collection/transaction-metadata.js +5 -0
- package/dist/esm/collection/transaction-metadata.js.map +1 -0
- package/dist/esm/errors.d.ts +3 -0
- package/dist/esm/errors.js +8 -0
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/index.d.ts +11 -3
- package/dist/esm/index.js +25 -7
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/indexes/auto-index.js +13 -6
- package/dist/esm/indexes/auto-index.js.map +1 -1
- package/dist/esm/indexes/base-index.d.ts +2 -6
- package/dist/esm/indexes/base-index.js +1 -4
- package/dist/esm/indexes/base-index.js.map +1 -1
- package/dist/esm/indexes/basic-index.d.ts +102 -0
- package/dist/esm/indexes/basic-index.js +361 -0
- package/dist/esm/indexes/basic-index.js.map +1 -0
- package/dist/esm/indexes/btree-index.d.ts +1 -1
- package/dist/esm/indexes/btree-index.js.map +1 -1
- package/dist/esm/indexes/index-options.d.ts +8 -9
- package/dist/esm/indexes/index-registry.d.ts +61 -0
- package/dist/esm/indexes/index-registry.js +89 -0
- package/dist/esm/indexes/index-registry.js.map +1 -0
- package/dist/esm/local-only.js +5 -0
- package/dist/esm/local-only.js.map +1 -1
- package/dist/esm/query/builder/functions.d.ts +25 -3
- package/dist/esm/query/builder/functions.js +27 -11
- package/dist/esm/query/builder/functions.js.map +1 -1
- package/dist/esm/query/builder/index.d.ts +4 -3
- package/dist/esm/query/builder/index.js +201 -40
- package/dist/esm/query/builder/index.js.map +1 -1
- package/dist/esm/query/builder/ref-proxy.d.ts +14 -3
- package/dist/esm/query/builder/ref-proxy.js.map +1 -1
- package/dist/esm/query/builder/types.d.ts +84 -19
- package/dist/esm/query/compiler/evaluators.js +51 -0
- package/dist/esm/query/compiler/evaluators.js.map +1 -1
- package/dist/esm/query/compiler/group-by.d.ts +4 -2
- package/dist/esm/query/compiler/group-by.js +101 -29
- package/dist/esm/query/compiler/group-by.js.map +1 -1
- package/dist/esm/query/compiler/index.d.ts +30 -2
- package/dist/esm/query/compiler/index.js +285 -13
- package/dist/esm/query/compiler/index.js.map +1 -1
- package/dist/esm/query/compiler/order-by.d.ts +1 -1
- package/dist/esm/query/compiler/order-by.js +30 -11
- package/dist/esm/query/compiler/order-by.js.map +1 -1
- package/dist/esm/query/compiler/select.js +8 -0
- package/dist/esm/query/compiler/select.js.map +1 -1
- package/dist/esm/query/index.d.ts +2 -1
- package/dist/esm/query/ir.d.ts +21 -1
- package/dist/esm/query/ir.js +18 -1
- package/dist/esm/query/ir.js.map +1 -1
- package/dist/esm/query/live/collection-config-builder.d.ts +7 -0
- package/dist/esm/query/live/collection-config-builder.js +503 -7
- package/dist/esm/query/live/collection-config-builder.js.map +1 -1
- package/dist/esm/query/live/types.d.ts +3 -3
- package/dist/esm/query/live/utils.d.ts +1 -0
- package/dist/esm/query/live/utils.js +43 -3
- package/dist/esm/query/live/utils.js.map +1 -1
- package/dist/esm/query/live-query-collection.d.ts +9 -6
- package/dist/esm/query/live-query-collection.js.map +1 -1
- package/dist/esm/query/query-once.d.ts +7 -5
- package/dist/esm/query/query-once.js.map +1 -1
- package/dist/esm/query/subset-dedupe.js +9 -3
- package/dist/esm/query/subset-dedupe.js.map +1 -1
- package/dist/esm/types.d.ts +42 -8
- package/dist/esm/utils/array-utils.d.ts +16 -0
- package/dist/esm/utils/array-utils.js +27 -0
- package/dist/esm/utils/array-utils.js.map +1 -0
- package/dist/esm/utils/comparison.js +11 -0
- package/dist/esm/utils/comparison.js.map +1 -1
- package/dist/esm/utils/index-optimization.js +4 -0
- package/dist/esm/utils/index-optimization.js.map +1 -1
- package/dist/esm/utils.d.ts +6 -1
- package/dist/esm/utils.js +7 -9
- package/dist/esm/utils.js.map +1 -1
- package/dist/esm/virtual-props.d.ts +196 -0
- package/dist/esm/virtual-props.js +33 -0
- package/dist/esm/virtual-props.js.map +1 -0
- package/package.json +4 -3
- package/skills/db-core/SKILL.md +4 -2
- package/skills/db-core/collection-setup/SKILL.md +30 -11
- package/skills/db-core/collection-setup/references/electric-adapter.md +1 -1
- package/skills/db-core/collection-setup/references/powersync-adapter.md +4 -0
- package/skills/db-core/collection-setup/references/query-adapter.md +32 -0
- package/skills/db-core/custom-adapter/SKILL.md +58 -9
- package/skills/db-core/live-queries/SKILL.md +162 -2
- package/skills/db-core/mutations-optimistic/SKILL.md +1 -1
- package/skills/db-core/persistence/SKILL.md +241 -0
- package/skills/meta-framework/SKILL.md +1 -1
- package/src/collection/change-events.ts +13 -9
- package/src/collection/changes.ts +30 -7
- package/src/collection/cleanup-queue.ts +105 -0
- package/src/collection/events.ts +65 -0
- package/src/collection/index.ts +110 -45
- package/src/collection/indexes.ts +283 -76
- package/src/collection/lifecycle.ts +5 -26
- package/src/collection/mutations.ts +21 -0
- package/src/collection/state.ts +545 -71
- package/src/collection/subscription.ts +7 -0
- package/src/collection/sync.ts +137 -0
- package/src/collection/transaction-metadata.ts +1 -0
- package/src/errors.ts +9 -0
- package/src/index.ts +46 -3
- package/src/indexes/auto-index.ts +18 -8
- package/src/indexes/base-index.ts +2 -10
- package/src/indexes/basic-index.ts +507 -0
- package/src/indexes/btree-index.ts +1 -1
- package/src/indexes/index-options.ts +17 -37
- package/src/indexes/index-registry.ts +174 -0
- package/src/local-only.ts +7 -0
- package/src/query/builder/functions.ts +84 -7
- package/src/query/builder/index.ts +329 -9
- package/src/query/builder/ref-proxy.ts +22 -4
- package/src/query/builder/types.ts +257 -62
- package/src/query/compiler/evaluators.ts +57 -0
- package/src/query/compiler/group-by.ts +156 -35
- package/src/query/compiler/index.ts +445 -15
- package/src/query/compiler/order-by.ts +51 -12
- package/src/query/compiler/select.ts +9 -0
- package/src/query/index.ts +7 -0
- package/src/query/ir.ts +23 -2
- package/src/query/live/collection-config-builder.ts +809 -9
- package/src/query/live/types.ts +10 -4
- package/src/query/live/utils.ts +64 -3
- package/src/query/live-query-collection.ts +43 -18
- package/src/query/query-once.ts +31 -12
- package/src/query/subset-dedupe.ts +11 -7
- package/src/types.ts +49 -9
- package/src/utils/array-utils.ts +49 -0
- package/src/utils/comparison.ts +14 -0
- package/src/utils/index-optimization.ts +4 -0
- package/src/utils.ts +12 -9
- package/src/virtual-props.ts +282 -0
- package/dist/cjs/indexes/lazy-index.cjs +0 -190
- package/dist/cjs/indexes/lazy-index.cjs.map +0 -1
- package/dist/cjs/indexes/lazy-index.d.cts +0 -96
- package/dist/esm/indexes/lazy-index.d.ts +0 -96
- package/dist/esm/indexes/lazy-index.js +0 -190
- package/dist/esm/indexes/lazy-index.js.map +0 -1
- package/src/indexes/lazy-index.ts +0 -251
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ir.cjs","sources":["../../../src/query/ir.ts"],"sourcesContent":["/*\nThis is the intermediate representation of the query.\n*/\n\nimport type { CompareOptions } from './builder/types'\nimport type { Collection, CollectionImpl } from '../collection/index.js'\nimport type { NamespacedRow } from '../types'\n\nexport interface QueryIR {\n from: From\n select?: Select\n join?: Join\n where?: Array<Where>\n groupBy?: GroupBy\n having?: Array<Having>\n orderBy?: OrderBy\n limit?: Limit\n offset?: Offset\n distinct?: true\n singleResult?: true\n\n // Functional variants\n fnSelect?: (row: NamespacedRow) => any\n fnWhere?: Array<(row: NamespacedRow) => any>\n fnHaving?: Array<(row: NamespacedRow) => any>\n}\n\nexport type From = CollectionRef | QueryRef\n\nexport type Select = {\n [alias: string]: BasicExpression | Aggregate | Select\n}\n\nexport type Join = Array<JoinClause>\n\nexport interface JoinClause {\n from: CollectionRef | QueryRef\n type: `left` | `right` | `inner` | `outer` | `full` | `cross`\n left: BasicExpression\n right: BasicExpression\n}\n\nexport type Where =\n | BasicExpression<boolean>\n | { expression: BasicExpression<boolean>; residual?: boolean }\n\nexport type GroupBy = Array<BasicExpression>\n\nexport type Having = Where\n\nexport type OrderBy = Array<OrderByClause>\n\nexport type OrderByClause = {\n expression: BasicExpression\n compareOptions: CompareOptions\n}\n\nexport type OrderByDirection = `asc` | `desc`\n\nexport type Limit = number\n\nexport type Offset = number\n\n/* Expressions */\n\nabstract class BaseExpression<T = any> {\n public abstract type: string\n /** @internal - Type brand for TypeScript inference */\n declare readonly __returnType: T\n}\n\nexport class CollectionRef extends BaseExpression {\n public type = `collectionRef` as const\n constructor(\n public collection: CollectionImpl,\n public alias: string,\n ) {\n super()\n }\n}\n\nexport class QueryRef extends BaseExpression {\n public type = `queryRef` as const\n constructor(\n public query: QueryIR,\n public alias: string,\n ) {\n super()\n }\n}\n\nexport class PropRef<T = any> extends BaseExpression<T> {\n public type = `ref` as const\n constructor(\n public path: Array<string>, // path to the property in the collection, with the alias as the first element\n ) {\n super()\n }\n}\n\nexport class Value<T = any> extends BaseExpression<T> {\n public type = `val` as const\n constructor(\n public value: T, // any js value\n ) {\n super()\n }\n}\n\nexport class Func<T = any> extends BaseExpression<T> {\n public type = `func` as const\n constructor(\n public name: string, // such as eq, gt, lt, upper, lower, etc.\n public args: Array<BasicExpression>,\n ) {\n super()\n }\n}\n\n// This is the basic expression type that is used in the majority of expression\n// builder callbacks (select, where, groupBy, having, orderBy, etc.)\n// it doesn't include aggregate functions as those are only used in the select clause\nexport type BasicExpression<T = any> = PropRef<T> | Value<T> | Func<T>\n\nexport class Aggregate<T = any> extends BaseExpression<T> {\n public type = `agg` as const\n constructor(\n public name: string, // such as count, avg, sum, min, max, etc.\n public args: Array<BasicExpression>,\n ) {\n super()\n }\n}\n\n/**\n * Runtime helper to detect IR expression-like objects.\n * Prefer this over ad-hoc local implementations to keep behavior consistent.\n */\nexport function isExpressionLike(value: any): boolean {\n return (\n value instanceof Aggregate ||\n value instanceof Func ||\n value instanceof PropRef ||\n value instanceof Value\n )\n}\n\n/**\n * Helper functions for working with Where clauses\n */\n\n/**\n * Extract the expression from a Where clause\n */\nexport function getWhereExpression(where: Where): BasicExpression<boolean> {\n return typeof where === `object` && `expression` in where\n ? where.expression\n : where\n}\n\n/**\n * Extract the expression from a HAVING clause\n * HAVING clauses can contain aggregates, unlike regular WHERE clauses\n */\nexport function getHavingExpression(\n having: Having,\n): BasicExpression | Aggregate {\n return typeof having === `object` && `expression` in having\n ? having.expression\n : having\n}\n\n/**\n * Check if a Where clause is marked as residual\n */\nexport function isResidualWhere(where: Where): boolean {\n return (\n typeof where === `object` &&\n `expression` in where &&\n where.residual === true\n )\n}\n\n/**\n * Create a residual Where clause from an expression\n */\nexport function createResidualWhere(\n expression: BasicExpression<boolean>,\n): Where {\n return { expression, residual: true }\n}\n\nfunction getRefFromAlias(\n query: QueryIR,\n alias: string,\n): CollectionRef | QueryRef | void {\n if (query.from.alias === alias) {\n return query.from\n }\n\n for (const join of query.join || []) {\n if (join.from.alias === alias) {\n return join.from\n }\n }\n}\n\n/**\n * Follows the given reference in a query\n * until its finds the root field the reference points to.\n * @returns The collection, its alias, and the path to the root field in this collection\n */\nexport function followRef(\n query: QueryIR,\n ref: PropRef<any>,\n collection: Collection,\n): { collection: Collection; path: Array<string> } | void {\n if (ref.path.length === 0) {\n return\n }\n\n if (ref.path.length === 1) {\n // This field should be part of this collection\n const field = ref.path[0]!\n // is it part of the select clause?\n if (query.select) {\n const selectedField = query.select[field]\n if (selectedField && selectedField.type === `ref`) {\n return followRef(query, selectedField, collection)\n }\n }\n\n // Either this field is not part of the select clause\n // and thus it must be part of the collection itself\n // or it is part of the select but is not a reference\n // so we can stop here and don't have to follow it\n return { collection, path: [field] }\n }\n\n if (ref.path.length > 1) {\n // This is a nested field\n const [alias, ...rest] = ref.path\n const aliasRef = getRefFromAlias(query, alias!)\n if (!aliasRef) {\n return\n }\n\n if (aliasRef.type === `queryRef`) {\n return followRef(aliasRef.query, new PropRef(rest), collection)\n } else {\n // This is a reference to a collection\n // we can't follow it further\n // so the field must be on the collection itself\n return { collection: aliasRef.collection, path: rest }\n }\n }\n}\n"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"ir.cjs","sources":["../../../src/query/ir.ts"],"sourcesContent":["/*\nThis is the intermediate representation of the query.\n*/\n\nimport type { CompareOptions } from './builder/types'\nimport type { Collection, CollectionImpl } from '../collection/index.js'\nimport type { NamespacedRow } from '../types'\n\nexport interface QueryIR {\n from: From\n select?: Select\n join?: Join\n where?: Array<Where>\n groupBy?: GroupBy\n having?: Array<Having>\n orderBy?: OrderBy\n limit?: Limit\n offset?: Offset\n distinct?: true\n singleResult?: true\n\n // Functional variants\n fnSelect?: (row: NamespacedRow) => any\n fnWhere?: Array<(row: NamespacedRow) => any>\n fnHaving?: Array<(row: NamespacedRow) => any>\n}\n\nexport type IncludesMaterialization = `collection` | `array` | `concat`\n\nexport const INCLUDES_SCALAR_FIELD = `__includes_scalar__`\n\nexport type From = CollectionRef | QueryRef\n\nexport type Select = {\n [alias: string]: BasicExpression | Aggregate | Select | IncludesSubquery\n}\n\nexport type Join = Array<JoinClause>\n\nexport interface JoinClause {\n from: CollectionRef | QueryRef\n type: `left` | `right` | `inner` | `outer` | `full` | `cross`\n left: BasicExpression\n right: BasicExpression\n}\n\nexport type Where =\n | BasicExpression<boolean>\n | { expression: BasicExpression<boolean>; residual?: boolean }\n\nexport type GroupBy = Array<BasicExpression>\n\nexport type Having = Where\n\nexport type OrderBy = Array<OrderByClause>\n\nexport type OrderByClause = {\n expression: BasicExpression\n compareOptions: CompareOptions\n}\n\nexport type OrderByDirection = `asc` | `desc`\n\nexport type Limit = number\n\nexport type Offset = number\n\n/* Expressions */\n\nabstract class BaseExpression<T = any> {\n public abstract type: string\n /** @internal - Type brand for TypeScript inference */\n declare readonly __returnType: T\n}\n\nexport class CollectionRef extends BaseExpression {\n public type = `collectionRef` as const\n constructor(\n public collection: CollectionImpl,\n public alias: string,\n ) {\n super()\n }\n}\n\nexport class QueryRef extends BaseExpression {\n public type = `queryRef` as const\n constructor(\n public query: QueryIR,\n public alias: string,\n ) {\n super()\n }\n}\n\nexport class PropRef<T = any> extends BaseExpression<T> {\n public type = `ref` as const\n constructor(\n public path: Array<string>, // path to the property in the collection, with the alias as the first element\n ) {\n super()\n }\n}\n\nexport class Value<T = any> extends BaseExpression<T> {\n public type = `val` as const\n constructor(\n public value: T, // any js value\n ) {\n super()\n }\n}\n\nexport class Func<T = any> extends BaseExpression<T> {\n public type = `func` as const\n constructor(\n public name: string, // such as eq, gt, lt, upper, lower, etc.\n public args: Array<BasicExpression>,\n ) {\n super()\n }\n}\n\n// This is the basic expression type that is used in the majority of expression\n// builder callbacks (select, where, groupBy, having, orderBy, etc.)\n// it doesn't include aggregate functions as those are only used in the select clause\nexport type BasicExpression<T = any> = PropRef<T> | Value<T> | Func<T>\n\nexport class Aggregate<T = any> extends BaseExpression<T> {\n public type = `agg` as const\n constructor(\n public name: string, // such as count, avg, sum, min, max, etc.\n public args: Array<BasicExpression>,\n ) {\n super()\n }\n}\n\nexport class IncludesSubquery extends BaseExpression {\n public type = `includesSubquery` as const\n constructor(\n public query: QueryIR, // Child query (correlation WHERE removed)\n public correlationField: PropRef, // Parent-side ref (e.g., project.id)\n public childCorrelationField: PropRef, // Child-side ref (e.g., issue.projectId)\n public fieldName: string, // Result field name (e.g., \"issues\")\n public parentFilters?: Array<Where>, // WHERE clauses referencing parent aliases (applied post-join)\n public parentProjection?: Array<PropRef>, // Parent field refs used by parentFilters\n public materialization: IncludesMaterialization = `collection`,\n public scalarField?: string,\n ) {\n super()\n }\n}\n\n/**\n * Runtime helper to detect IR expression-like objects.\n * Prefer this over ad-hoc local implementations to keep behavior consistent.\n */\nexport function isExpressionLike(value: any): boolean {\n return (\n value instanceof Aggregate ||\n value instanceof Func ||\n value instanceof PropRef ||\n value instanceof Value ||\n value instanceof IncludesSubquery\n )\n}\n\n/**\n * Helper functions for working with Where clauses\n */\n\n/**\n * Extract the expression from a Where clause\n */\nexport function getWhereExpression(where: Where): BasicExpression<boolean> {\n return typeof where === `object` && `expression` in where\n ? where.expression\n : where\n}\n\n/**\n * Extract the expression from a HAVING clause\n * HAVING clauses can contain aggregates, unlike regular WHERE clauses\n */\nexport function getHavingExpression(\n having: Having,\n): BasicExpression | Aggregate {\n return typeof having === `object` && `expression` in having\n ? having.expression\n : having\n}\n\n/**\n * Check if a Where clause is marked as residual\n */\nexport function isResidualWhere(where: Where): boolean {\n return (\n typeof where === `object` &&\n `expression` in where &&\n where.residual === true\n )\n}\n\n/**\n * Create a residual Where clause from an expression\n */\nexport function createResidualWhere(\n expression: BasicExpression<boolean>,\n): Where {\n return { expression, residual: true }\n}\n\nfunction getRefFromAlias(\n query: QueryIR,\n alias: string,\n): CollectionRef | QueryRef | void {\n if (query.from.alias === alias) {\n return query.from\n }\n\n for (const join of query.join || []) {\n if (join.from.alias === alias) {\n return join.from\n }\n }\n}\n\n/**\n * Follows the given reference in a query\n * until its finds the root field the reference points to.\n * @returns The collection, its alias, and the path to the root field in this collection\n */\nexport function followRef(\n query: QueryIR,\n ref: PropRef<any>,\n collection: Collection,\n): { collection: Collection; path: Array<string> } | void {\n if (ref.path.length === 0) {\n return\n }\n\n if (ref.path.length === 1) {\n // This field should be part of this collection\n const field = ref.path[0]!\n // is it part of the select clause?\n if (query.select) {\n const selectedField = query.select[field]\n if (selectedField && selectedField.type === `ref`) {\n return followRef(query, selectedField, collection)\n }\n }\n\n // Either this field is not part of the select clause\n // and thus it must be part of the collection itself\n // or it is part of the select but is not a reference\n // so we can stop here and don't have to follow it\n return { collection, path: [field] }\n }\n\n if (ref.path.length > 1) {\n // This is a nested field\n const [alias, ...rest] = ref.path\n const aliasRef = getRefFromAlias(query, alias!)\n if (!aliasRef) {\n return\n }\n\n if (aliasRef.type === `queryRef`) {\n return followRef(aliasRef.query, new PropRef(rest), collection)\n } else {\n // This is a reference to a collection\n // we can't follow it further\n // so the field must be on the collection itself\n return { collection: aliasRef.collection, path: rest }\n }\n }\n}\n"],"names":[],"mappings":";;AA6BO,MAAM,wBAAwB;AAwCrC,MAAe,eAAwB;AAIvC;AAEO,MAAM,sBAAsB,eAAe;AAAA,EAEhD,YACS,YACA,OACP;AACA,UAAA;AAHO,SAAA,aAAA;AACA,SAAA,QAAA;AAHT,SAAO,OAAO;AAAA,EAMd;AACF;AAEO,MAAM,iBAAiB,eAAe;AAAA,EAE3C,YACS,OACA,OACP;AACA,UAAA;AAHO,SAAA,QAAA;AACA,SAAA,QAAA;AAHT,SAAO,OAAO;AAAA,EAMd;AACF;AAEO,MAAM,gBAAyB,eAAkB;AAAA,EAEtD,YACS,MACP;AACA,UAAA;AAFO,SAAA,OAAA;AAFT,SAAO,OAAO;AAAA,EAKd;AACF;AAEO,MAAM,cAAuB,eAAkB;AAAA,EAEpD,YACS,OACP;AACA,UAAA;AAFO,SAAA,QAAA;AAFT,SAAO,OAAO;AAAA,EAKd;AACF;AAEO,MAAM,aAAsB,eAAkB;AAAA,EAEnD,YACS,MACA,MACP;AACA,UAAA;AAHO,SAAA,OAAA;AACA,SAAA,OAAA;AAHT,SAAO,OAAO;AAAA,EAMd;AACF;AAOO,MAAM,kBAA2B,eAAkB;AAAA,EAExD,YACS,MACA,MACP;AACA,UAAA;AAHO,SAAA,OAAA;AACA,SAAA,OAAA;AAHT,SAAO,OAAO;AAAA,EAMd;AACF;AAEO,MAAM,yBAAyB,eAAe;AAAA,EAEnD,YACS,OACA,kBACA,uBACA,WACA,eACA,kBACA,kBAA2C,cAC3C,aACP;AACA,UAAA;AATO,SAAA,QAAA;AACA,SAAA,mBAAA;AACA,SAAA,wBAAA;AACA,SAAA,YAAA;AACA,SAAA,gBAAA;AACA,SAAA,mBAAA;AACA,SAAA,kBAAA;AACA,SAAA,cAAA;AATT,SAAO,OAAO;AAAA,EAYd;AACF;AAMO,SAAS,iBAAiB,OAAqB;AACpD,SACE,iBAAiB,aACjB,iBAAiB,QACjB,iBAAiB,WACjB,iBAAiB,SACjB,iBAAiB;AAErB;AASO,SAAS,mBAAmB,OAAwC;AACzE,SAAO,OAAO,UAAU,YAAY,gBAAgB,QAChD,MAAM,aACN;AACN;AAMO,SAAS,oBACd,QAC6B;AAC7B,SAAO,OAAO,WAAW,YAAY,gBAAgB,SACjD,OAAO,aACP;AACN;AAKO,SAAS,gBAAgB,OAAuB;AACrD,SACE,OAAO,UAAU,YACjB,gBAAgB,SAChB,MAAM,aAAa;AAEvB;AAKO,SAAS,oBACd,YACO;AACP,SAAO,EAAE,YAAY,UAAU,KAAA;AACjC;AAEA,SAAS,gBACP,OACA,OACiC;AACjC,MAAI,MAAM,KAAK,UAAU,OAAO;AAC9B,WAAO,MAAM;AAAA,EACf;AAEA,aAAW,QAAQ,MAAM,QAAQ,CAAA,GAAI;AACnC,QAAI,KAAK,KAAK,UAAU,OAAO;AAC7B,aAAO,KAAK;AAAA,IACd;AAAA,EACF;AACF;AAOO,SAAS,UACd,OACA,KACA,YACwD;AACxD,MAAI,IAAI,KAAK,WAAW,GAAG;AACzB;AAAA,EACF;AAEA,MAAI,IAAI,KAAK,WAAW,GAAG;AAEzB,UAAM,QAAQ,IAAI,KAAK,CAAC;AAExB,QAAI,MAAM,QAAQ;AAChB,YAAM,gBAAgB,MAAM,OAAO,KAAK;AACxC,UAAI,iBAAiB,cAAc,SAAS,OAAO;AACjD,eAAO,UAAU,OAAO,eAAe,UAAU;AAAA,MACnD;AAAA,IACF;AAMA,WAAO,EAAE,YAAY,MAAM,CAAC,KAAK,EAAA;AAAA,EACnC;AAEA,MAAI,IAAI,KAAK,SAAS,GAAG;AAEvB,UAAM,CAAC,OAAO,GAAG,IAAI,IAAI,IAAI;AAC7B,UAAM,WAAW,gBAAgB,OAAO,KAAM;AAC9C,QAAI,CAAC,UAAU;AACb;AAAA,IACF;AAEA,QAAI,SAAS,SAAS,YAAY;AAChC,aAAO,UAAU,SAAS,OAAO,IAAI,QAAQ,IAAI,GAAG,UAAU;AAAA,IAChE,OAAO;AAIL,aAAO,EAAE,YAAY,SAAS,YAAY,MAAM,KAAA;AAAA,IAClD;AAAA,EACF;AACF;;;;;;;;;;;;;;;"}
|
package/dist/cjs/query/ir.d.cts
CHANGED
|
@@ -17,9 +17,11 @@ export interface QueryIR {
|
|
|
17
17
|
fnWhere?: Array<(row: NamespacedRow) => any>;
|
|
18
18
|
fnHaving?: Array<(row: NamespacedRow) => any>;
|
|
19
19
|
}
|
|
20
|
+
export type IncludesMaterialization = `collection` | `array` | `concat`;
|
|
21
|
+
export declare const INCLUDES_SCALAR_FIELD = "__includes_scalar__";
|
|
20
22
|
export type From = CollectionRef | QueryRef;
|
|
21
23
|
export type Select = {
|
|
22
|
-
[alias: string]: BasicExpression | Aggregate | Select;
|
|
24
|
+
[alias: string]: BasicExpression | Aggregate | Select | IncludesSubquery;
|
|
23
25
|
};
|
|
24
26
|
export type Join = Array<JoinClause>;
|
|
25
27
|
export interface JoinClause {
|
|
@@ -84,6 +86,24 @@ export declare class Aggregate<T = any> extends BaseExpression<T> {
|
|
|
84
86
|
constructor(name: string, // such as count, avg, sum, min, max, etc.
|
|
85
87
|
args: Array<BasicExpression>);
|
|
86
88
|
}
|
|
89
|
+
export declare class IncludesSubquery extends BaseExpression {
|
|
90
|
+
query: QueryIR;
|
|
91
|
+
correlationField: PropRef;
|
|
92
|
+
childCorrelationField: PropRef;
|
|
93
|
+
fieldName: string;
|
|
94
|
+
parentFilters?: Array<Where> | undefined;
|
|
95
|
+
parentProjection?: Array<PropRef> | undefined;
|
|
96
|
+
materialization: IncludesMaterialization;
|
|
97
|
+
scalarField?: string | undefined;
|
|
98
|
+
type: "includesSubquery";
|
|
99
|
+
constructor(query: QueryIR, // Child query (correlation WHERE removed)
|
|
100
|
+
correlationField: PropRef, // Parent-side ref (e.g., project.id)
|
|
101
|
+
childCorrelationField: PropRef, // Child-side ref (e.g., issue.projectId)
|
|
102
|
+
fieldName: string, // Result field name (e.g., "issues")
|
|
103
|
+
parentFilters?: Array<Where> | undefined, // WHERE clauses referencing parent aliases (applied post-join)
|
|
104
|
+
parentProjection?: Array<PropRef> | undefined, // Parent field refs used by parentFilters
|
|
105
|
+
materialization?: IncludesMaterialization, scalarField?: string | undefined);
|
|
106
|
+
}
|
|
87
107
|
/**
|
|
88
108
|
* Runtime helper to detect IR expression-like objects.
|
|
89
109
|
* Prefer this over ad-hoc local implementations to keep behavior consistent.
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const dbIvm = require("@tanstack/db-ivm");
|
|
4
4
|
const index = require("../compiler/index.cjs");
|
|
5
|
+
const index$1 = require("../../collection/index.cjs");
|
|
5
6
|
const errors = require("../../errors.cjs");
|
|
6
7
|
const scheduler = require("../../scheduler.cjs");
|
|
7
8
|
const transactions = require("../../transactions.cjs");
|
|
@@ -27,7 +28,10 @@ class CollectionConfigBuilder {
|
|
|
27
28
|
this.lazySources = /* @__PURE__ */ new Set();
|
|
28
29
|
this.optimizableOrderByCollections = {};
|
|
29
30
|
this.id = config.id || `live-query-${++liveQueryCollectionCounter}`;
|
|
30
|
-
this.query = utils.buildQueryFromConfig(
|
|
31
|
+
this.query = utils.buildQueryFromConfig({
|
|
32
|
+
query: config.query,
|
|
33
|
+
requireObjectResult: true
|
|
34
|
+
});
|
|
31
35
|
this.collections = utils.extractCollectionsFromQuery(this.query);
|
|
32
36
|
const collectionAliasesById = utils.extractCollectionAliases(this.query);
|
|
33
37
|
this.collectionByAlias = {};
|
|
@@ -362,6 +366,7 @@ class CollectionConfigBuilder {
|
|
|
362
366
|
this.inputsCache = void 0;
|
|
363
367
|
this.pipelineCache = void 0;
|
|
364
368
|
this.sourceWhereClausesCache = void 0;
|
|
369
|
+
this.includesCache = void 0;
|
|
365
370
|
this.lazySources.clear();
|
|
366
371
|
this.optimizableOrderByCollections = {};
|
|
367
372
|
this.lazySourcesCallbacks = {};
|
|
@@ -399,6 +404,7 @@ class CollectionConfigBuilder {
|
|
|
399
404
|
this.pipelineCache = compilation.pipeline;
|
|
400
405
|
this.sourceWhereClausesCache = compilation.sourceWhereClauses;
|
|
401
406
|
this.compiledAliasToCollectionId = compilation.aliasToCollectionId;
|
|
407
|
+
this.includesCache = compilation.includes;
|
|
402
408
|
const missingAliases = Object.keys(this.compiledAliasToCollectionId).filter(
|
|
403
409
|
(alias) => !Object.hasOwn(this.inputsCache, alias)
|
|
404
410
|
);
|
|
@@ -427,8 +433,14 @@ class CollectionConfigBuilder {
|
|
|
427
433
|
messages.reduce(accumulateChanges, pendingChanges);
|
|
428
434
|
})
|
|
429
435
|
);
|
|
436
|
+
const includesState = this.setupIncludesOutput(
|
|
437
|
+
this.includesCache,
|
|
438
|
+
syncState
|
|
439
|
+
);
|
|
430
440
|
syncState.flushPendingChanges = () => {
|
|
431
|
-
|
|
441
|
+
const hasParentChanges = pendingChanges.size > 0;
|
|
442
|
+
const hasChildChanges = hasPendingIncludesChanges(includesState);
|
|
443
|
+
if (!hasParentChanges && !hasChildChanges) {
|
|
432
444
|
return;
|
|
433
445
|
}
|
|
434
446
|
let changesToApply = pendingChanges;
|
|
@@ -452,10 +464,19 @@ class CollectionConfigBuilder {
|
|
|
452
464
|
}
|
|
453
465
|
changesToApply = merged;
|
|
454
466
|
}
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
467
|
+
if (hasParentChanges) {
|
|
468
|
+
begin();
|
|
469
|
+
changesToApply.forEach(this.applyChanges.bind(this, config));
|
|
470
|
+
commit();
|
|
471
|
+
}
|
|
458
472
|
pendingChanges = /* @__PURE__ */ new Map();
|
|
473
|
+
flushIncludesState(
|
|
474
|
+
includesState,
|
|
475
|
+
config.collection,
|
|
476
|
+
this.id,
|
|
477
|
+
hasParentChanges ? changesToApply : null,
|
|
478
|
+
config
|
|
479
|
+
);
|
|
459
480
|
};
|
|
460
481
|
graph.finalize();
|
|
461
482
|
syncState.graph = graph;
|
|
@@ -463,6 +484,65 @@ class CollectionConfigBuilder {
|
|
|
463
484
|
syncState.pipeline = pipeline;
|
|
464
485
|
return syncState;
|
|
465
486
|
}
|
|
487
|
+
/**
|
|
488
|
+
* Sets up output callbacks for includes child pipelines.
|
|
489
|
+
* Each includes entry gets its own output callback that accumulates child changes,
|
|
490
|
+
* and a child registry that maps correlation key → child Collection.
|
|
491
|
+
*/
|
|
492
|
+
setupIncludesOutput(includesEntries, syncState) {
|
|
493
|
+
if (!includesEntries || includesEntries.length === 0) {
|
|
494
|
+
return [];
|
|
495
|
+
}
|
|
496
|
+
return includesEntries.map((entry) => {
|
|
497
|
+
const state = {
|
|
498
|
+
fieldName: entry.fieldName,
|
|
499
|
+
childCorrelationField: entry.childCorrelationField,
|
|
500
|
+
hasOrderBy: entry.hasOrderBy,
|
|
501
|
+
materialization: entry.materialization,
|
|
502
|
+
scalarField: entry.scalarField,
|
|
503
|
+
childRegistry: /* @__PURE__ */ new Map(),
|
|
504
|
+
pendingChildChanges: /* @__PURE__ */ new Map(),
|
|
505
|
+
correlationToParentKeys: /* @__PURE__ */ new Map()
|
|
506
|
+
};
|
|
507
|
+
entry.pipeline.pipe(
|
|
508
|
+
dbIvm.output((data) => {
|
|
509
|
+
const messages = data.getInner();
|
|
510
|
+
syncState.messagesCount += messages.length;
|
|
511
|
+
for (const [[childKey, tupleData], multiplicity] of messages) {
|
|
512
|
+
const [childResult, _orderByIndex, correlationKey, parentContext] = tupleData;
|
|
513
|
+
const routingKey = computeRoutingKey(correlationKey, parentContext);
|
|
514
|
+
let byChild = state.pendingChildChanges.get(routingKey);
|
|
515
|
+
if (!byChild) {
|
|
516
|
+
byChild = /* @__PURE__ */ new Map();
|
|
517
|
+
state.pendingChildChanges.set(routingKey, byChild);
|
|
518
|
+
}
|
|
519
|
+
const existing = byChild.get(childKey) || {
|
|
520
|
+
deletes: 0,
|
|
521
|
+
inserts: 0,
|
|
522
|
+
value: childResult,
|
|
523
|
+
orderByIndex: _orderByIndex
|
|
524
|
+
};
|
|
525
|
+
if (multiplicity < 0) {
|
|
526
|
+
existing.deletes += Math.abs(multiplicity);
|
|
527
|
+
} else if (multiplicity > 0) {
|
|
528
|
+
existing.inserts += multiplicity;
|
|
529
|
+
existing.value = childResult;
|
|
530
|
+
}
|
|
531
|
+
byChild.set(childKey, existing);
|
|
532
|
+
}
|
|
533
|
+
})
|
|
534
|
+
);
|
|
535
|
+
if (entry.childCompilationResult.includes) {
|
|
536
|
+
state.nestedSetups = setupNestedPipelines(
|
|
537
|
+
entry.childCompilationResult.includes,
|
|
538
|
+
syncState
|
|
539
|
+
);
|
|
540
|
+
state.nestedRoutingIndex = /* @__PURE__ */ new Map();
|
|
541
|
+
state.nestedRoutingReverseIndex = /* @__PURE__ */ new Map();
|
|
542
|
+
}
|
|
543
|
+
return state;
|
|
544
|
+
});
|
|
545
|
+
}
|
|
466
546
|
applyChanges(config, changes, key) {
|
|
467
547
|
const { write, collection } = config;
|
|
468
548
|
const { deletes, inserts, value, orderByIndex } = changes;
|
|
@@ -606,6 +686,422 @@ function createOrderByComparator(orderByIndices) {
|
|
|
606
686
|
return 0;
|
|
607
687
|
};
|
|
608
688
|
}
|
|
689
|
+
function materializesInline(state) {
|
|
690
|
+
return state.materialization !== `collection`;
|
|
691
|
+
}
|
|
692
|
+
function materializeIncludedValue(state, entry) {
|
|
693
|
+
if (!entry) {
|
|
694
|
+
if (state.materialization === `array`) {
|
|
695
|
+
return [];
|
|
696
|
+
}
|
|
697
|
+
if (state.materialization === `concat`) {
|
|
698
|
+
return ``;
|
|
699
|
+
}
|
|
700
|
+
return void 0;
|
|
701
|
+
}
|
|
702
|
+
if (state.materialization === `collection`) {
|
|
703
|
+
return entry.collection;
|
|
704
|
+
}
|
|
705
|
+
const rows = [...entry.collection.toArray];
|
|
706
|
+
const values = state.scalarField ? rows.map((row) => row?.[state.scalarField]) : rows;
|
|
707
|
+
if (state.materialization === `array`) {
|
|
708
|
+
return values;
|
|
709
|
+
}
|
|
710
|
+
return values.map((value) => String(value ?? ``)).join(``);
|
|
711
|
+
}
|
|
712
|
+
function setupNestedPipelines(includes, syncState) {
|
|
713
|
+
return includes.map((entry) => {
|
|
714
|
+
const buffer = /* @__PURE__ */ new Map();
|
|
715
|
+
entry.pipeline.pipe(
|
|
716
|
+
dbIvm.output((data) => {
|
|
717
|
+
const messages = data.getInner();
|
|
718
|
+
syncState.messagesCount += messages.length;
|
|
719
|
+
for (const [[childKey, tupleData], multiplicity] of messages) {
|
|
720
|
+
const [childResult, _orderByIndex, correlationKey, parentContext] = tupleData;
|
|
721
|
+
const routingKey = computeRoutingKey(correlationKey, parentContext);
|
|
722
|
+
let byChild = buffer.get(routingKey);
|
|
723
|
+
if (!byChild) {
|
|
724
|
+
byChild = /* @__PURE__ */ new Map();
|
|
725
|
+
buffer.set(routingKey, byChild);
|
|
726
|
+
}
|
|
727
|
+
const existing = byChild.get(childKey) || {
|
|
728
|
+
deletes: 0,
|
|
729
|
+
inserts: 0,
|
|
730
|
+
value: childResult,
|
|
731
|
+
orderByIndex: _orderByIndex
|
|
732
|
+
};
|
|
733
|
+
if (multiplicity < 0) {
|
|
734
|
+
existing.deletes += Math.abs(multiplicity);
|
|
735
|
+
} else if (multiplicity > 0) {
|
|
736
|
+
existing.inserts += multiplicity;
|
|
737
|
+
existing.value = childResult;
|
|
738
|
+
}
|
|
739
|
+
byChild.set(childKey, existing);
|
|
740
|
+
}
|
|
741
|
+
})
|
|
742
|
+
);
|
|
743
|
+
const setup = {
|
|
744
|
+
compilationResult: entry,
|
|
745
|
+
buffer
|
|
746
|
+
};
|
|
747
|
+
if (entry.childCompilationResult.includes) {
|
|
748
|
+
setup.nestedSetups = setupNestedPipelines(
|
|
749
|
+
entry.childCompilationResult.includes,
|
|
750
|
+
syncState
|
|
751
|
+
);
|
|
752
|
+
}
|
|
753
|
+
return setup;
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
function createPerEntryIncludesStates(setups) {
|
|
757
|
+
return setups.map((setup) => {
|
|
758
|
+
const state = {
|
|
759
|
+
fieldName: setup.compilationResult.fieldName,
|
|
760
|
+
childCorrelationField: setup.compilationResult.childCorrelationField,
|
|
761
|
+
hasOrderBy: setup.compilationResult.hasOrderBy,
|
|
762
|
+
materialization: setup.compilationResult.materialization,
|
|
763
|
+
scalarField: setup.compilationResult.scalarField,
|
|
764
|
+
childRegistry: /* @__PURE__ */ new Map(),
|
|
765
|
+
pendingChildChanges: /* @__PURE__ */ new Map(),
|
|
766
|
+
correlationToParentKeys: /* @__PURE__ */ new Map()
|
|
767
|
+
};
|
|
768
|
+
if (setup.nestedSetups) {
|
|
769
|
+
state.nestedSetups = setup.nestedSetups;
|
|
770
|
+
state.nestedRoutingIndex = /* @__PURE__ */ new Map();
|
|
771
|
+
state.nestedRoutingReverseIndex = /* @__PURE__ */ new Map();
|
|
772
|
+
}
|
|
773
|
+
return state;
|
|
774
|
+
});
|
|
775
|
+
}
|
|
776
|
+
function drainNestedBuffers(state) {
|
|
777
|
+
const dirtyCorrelationKeys = /* @__PURE__ */ new Set();
|
|
778
|
+
if (!state.nestedSetups) return dirtyCorrelationKeys;
|
|
779
|
+
for (let i = 0; i < state.nestedSetups.length; i++) {
|
|
780
|
+
const setup = state.nestedSetups[i];
|
|
781
|
+
const toDelete = [];
|
|
782
|
+
for (const [nestedCorrelationKey, childChanges] of setup.buffer) {
|
|
783
|
+
const parentCorrelationKey = state.nestedRoutingIndex.get(nestedCorrelationKey);
|
|
784
|
+
if (parentCorrelationKey === void 0) {
|
|
785
|
+
continue;
|
|
786
|
+
}
|
|
787
|
+
const entry = state.childRegistry.get(parentCorrelationKey);
|
|
788
|
+
if (!entry || !entry.includesStates) {
|
|
789
|
+
continue;
|
|
790
|
+
}
|
|
791
|
+
const entryState = entry.includesStates[i];
|
|
792
|
+
for (const [childKey, changes] of childChanges) {
|
|
793
|
+
let byChild = entryState.pendingChildChanges.get(nestedCorrelationKey);
|
|
794
|
+
if (!byChild) {
|
|
795
|
+
byChild = /* @__PURE__ */ new Map();
|
|
796
|
+
entryState.pendingChildChanges.set(nestedCorrelationKey, byChild);
|
|
797
|
+
}
|
|
798
|
+
const existing = byChild.get(childKey);
|
|
799
|
+
if (existing) {
|
|
800
|
+
existing.inserts += changes.inserts;
|
|
801
|
+
existing.deletes += changes.deletes;
|
|
802
|
+
if (changes.inserts > 0) {
|
|
803
|
+
existing.value = changes.value;
|
|
804
|
+
if (changes.orderByIndex !== void 0) {
|
|
805
|
+
existing.orderByIndex = changes.orderByIndex;
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
} else {
|
|
809
|
+
byChild.set(childKey, { ...changes });
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
dirtyCorrelationKeys.add(parentCorrelationKey);
|
|
813
|
+
toDelete.push(nestedCorrelationKey);
|
|
814
|
+
}
|
|
815
|
+
for (const key of toDelete) {
|
|
816
|
+
setup.buffer.delete(key);
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
return dirtyCorrelationKeys;
|
|
820
|
+
}
|
|
821
|
+
function updateRoutingIndex(state, correlationKey, childChanges) {
|
|
822
|
+
if (!state.nestedSetups) return;
|
|
823
|
+
for (const setup of state.nestedSetups) {
|
|
824
|
+
for (const [, change] of childChanges) {
|
|
825
|
+
if (change.inserts > 0) {
|
|
826
|
+
const nestedRouting = change.value[index.INCLUDES_ROUTING]?.[setup.compilationResult.fieldName];
|
|
827
|
+
const nestedCorrelationKey = nestedRouting?.correlationKey;
|
|
828
|
+
const nestedParentContext = nestedRouting?.parentContext ?? null;
|
|
829
|
+
const nestedRoutingKey = computeRoutingKey(
|
|
830
|
+
nestedCorrelationKey,
|
|
831
|
+
nestedParentContext
|
|
832
|
+
);
|
|
833
|
+
if (nestedCorrelationKey != null) {
|
|
834
|
+
state.nestedRoutingIndex.set(nestedRoutingKey, correlationKey);
|
|
835
|
+
let reverseSet = state.nestedRoutingReverseIndex.get(correlationKey);
|
|
836
|
+
if (!reverseSet) {
|
|
837
|
+
reverseSet = /* @__PURE__ */ new Set();
|
|
838
|
+
state.nestedRoutingReverseIndex.set(correlationKey, reverseSet);
|
|
839
|
+
}
|
|
840
|
+
reverseSet.add(nestedRoutingKey);
|
|
841
|
+
}
|
|
842
|
+
} else if (change.deletes > 0 && change.inserts === 0) {
|
|
843
|
+
const nestedRouting2 = change.value[index.INCLUDES_ROUTING]?.[setup.compilationResult.fieldName];
|
|
844
|
+
const nestedCorrelationKey = nestedRouting2?.correlationKey;
|
|
845
|
+
const nestedParentContext2 = nestedRouting2?.parentContext ?? null;
|
|
846
|
+
const nestedRoutingKey = computeRoutingKey(
|
|
847
|
+
nestedCorrelationKey,
|
|
848
|
+
nestedParentContext2
|
|
849
|
+
);
|
|
850
|
+
if (nestedCorrelationKey != null) {
|
|
851
|
+
state.nestedRoutingIndex.delete(nestedRoutingKey);
|
|
852
|
+
const reverseSet = state.nestedRoutingReverseIndex.get(correlationKey);
|
|
853
|
+
if (reverseSet) {
|
|
854
|
+
reverseSet.delete(nestedRoutingKey);
|
|
855
|
+
if (reverseSet.size === 0) {
|
|
856
|
+
state.nestedRoutingReverseIndex.delete(correlationKey);
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
function cleanRoutingIndexOnDelete(state, correlationKey) {
|
|
865
|
+
if (!state.nestedRoutingReverseIndex) return;
|
|
866
|
+
const nestedKeys = state.nestedRoutingReverseIndex.get(correlationKey);
|
|
867
|
+
if (nestedKeys) {
|
|
868
|
+
for (const nestedKey of nestedKeys) {
|
|
869
|
+
state.nestedRoutingIndex.delete(nestedKey);
|
|
870
|
+
}
|
|
871
|
+
state.nestedRoutingReverseIndex.delete(correlationKey);
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
function hasNestedBufferChanges(setups) {
|
|
875
|
+
for (const setup of setups) {
|
|
876
|
+
if (setup.buffer.size > 0) return true;
|
|
877
|
+
if (setup.nestedSetups && hasNestedBufferChanges(setup.nestedSetups))
|
|
878
|
+
return true;
|
|
879
|
+
}
|
|
880
|
+
return false;
|
|
881
|
+
}
|
|
882
|
+
function computeRoutingKey(correlationKey, parentContext) {
|
|
883
|
+
if (parentContext == null) return correlationKey;
|
|
884
|
+
return JSON.stringify([correlationKey, parentContext]);
|
|
885
|
+
}
|
|
886
|
+
function createChildCollectionEntry(parentId, fieldName, correlationKey, hasOrderBy, nestedSetups) {
|
|
887
|
+
const resultKeys = /* @__PURE__ */ new WeakMap();
|
|
888
|
+
const orderByIndices = hasOrderBy ? /* @__PURE__ */ new WeakMap() : null;
|
|
889
|
+
let syncMethods = null;
|
|
890
|
+
const compare = orderByIndices ? createOrderByComparator(orderByIndices) : void 0;
|
|
891
|
+
const collection = index$1.createCollection({
|
|
892
|
+
id: `__child-collection:${parentId}-${fieldName}-${dbIvm.serializeValue(correlationKey)}`,
|
|
893
|
+
getKey: (item) => resultKeys.get(item),
|
|
894
|
+
compare,
|
|
895
|
+
sync: {
|
|
896
|
+
rowUpdateMode: `full`,
|
|
897
|
+
sync: (methods) => {
|
|
898
|
+
syncMethods = methods;
|
|
899
|
+
return () => {
|
|
900
|
+
syncMethods = null;
|
|
901
|
+
};
|
|
902
|
+
}
|
|
903
|
+
},
|
|
904
|
+
startSync: true
|
|
905
|
+
});
|
|
906
|
+
const entry = {
|
|
907
|
+
collection,
|
|
908
|
+
get syncMethods() {
|
|
909
|
+
return syncMethods;
|
|
910
|
+
},
|
|
911
|
+
resultKeys,
|
|
912
|
+
orderByIndices
|
|
913
|
+
};
|
|
914
|
+
if (nestedSetups) {
|
|
915
|
+
entry.includesStates = createPerEntryIncludesStates(nestedSetups);
|
|
916
|
+
}
|
|
917
|
+
return entry;
|
|
918
|
+
}
|
|
919
|
+
function flushIncludesState(includesState, parentCollection, parentId, parentChanges, parentSyncMethods) {
|
|
920
|
+
for (const state of includesState) {
|
|
921
|
+
if (parentChanges) {
|
|
922
|
+
for (const [parentKey, changes] of parentChanges) {
|
|
923
|
+
if (changes.inserts > 0) {
|
|
924
|
+
const parentResult = changes.value;
|
|
925
|
+
const routing = parentResult[index.INCLUDES_ROUTING]?.[state.fieldName];
|
|
926
|
+
const correlationKey = routing?.correlationKey;
|
|
927
|
+
const parentContext = routing?.parentContext ?? null;
|
|
928
|
+
const routingKey = computeRoutingKey(correlationKey, parentContext);
|
|
929
|
+
if (correlationKey != null) {
|
|
930
|
+
if (!state.childRegistry.has(routingKey)) {
|
|
931
|
+
const entry = createChildCollectionEntry(
|
|
932
|
+
parentId,
|
|
933
|
+
state.fieldName,
|
|
934
|
+
routingKey,
|
|
935
|
+
state.hasOrderBy,
|
|
936
|
+
state.nestedSetups
|
|
937
|
+
);
|
|
938
|
+
state.childRegistry.set(routingKey, entry);
|
|
939
|
+
}
|
|
940
|
+
let parentKeys = state.correlationToParentKeys.get(routingKey);
|
|
941
|
+
if (!parentKeys) {
|
|
942
|
+
parentKeys = /* @__PURE__ */ new Set();
|
|
943
|
+
state.correlationToParentKeys.set(routingKey, parentKeys);
|
|
944
|
+
}
|
|
945
|
+
parentKeys.add(parentKey);
|
|
946
|
+
const childValue = materializeIncludedValue(
|
|
947
|
+
state,
|
|
948
|
+
state.childRegistry.get(routingKey)
|
|
949
|
+
);
|
|
950
|
+
parentResult[state.fieldName] = childValue;
|
|
951
|
+
const storedParent = parentCollection.get(parentKey);
|
|
952
|
+
if (storedParent && storedParent !== parentResult) {
|
|
953
|
+
storedParent[state.fieldName] = childValue;
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
const affectedCorrelationKeys = materializesInline(state) ? new Set(state.pendingChildChanges.keys()) : null;
|
|
960
|
+
const entriesWithChildChanges = /* @__PURE__ */ new Map();
|
|
961
|
+
if (state.pendingChildChanges.size > 0) {
|
|
962
|
+
for (const [correlationKey, childChanges] of state.pendingChildChanges) {
|
|
963
|
+
let entry = state.childRegistry.get(correlationKey);
|
|
964
|
+
if (!entry) {
|
|
965
|
+
entry = createChildCollectionEntry(
|
|
966
|
+
parentId,
|
|
967
|
+
state.fieldName,
|
|
968
|
+
correlationKey,
|
|
969
|
+
state.hasOrderBy,
|
|
970
|
+
state.nestedSetups
|
|
971
|
+
);
|
|
972
|
+
state.childRegistry.set(correlationKey, entry);
|
|
973
|
+
}
|
|
974
|
+
if (state.materialization === `collection`) {
|
|
975
|
+
attachChildCollectionToParent(
|
|
976
|
+
parentCollection,
|
|
977
|
+
state.fieldName,
|
|
978
|
+
correlationKey,
|
|
979
|
+
state.correlationToParentKeys,
|
|
980
|
+
entry.collection
|
|
981
|
+
);
|
|
982
|
+
}
|
|
983
|
+
if (entry.syncMethods) {
|
|
984
|
+
entry.syncMethods.begin();
|
|
985
|
+
for (const [childKey, change] of childChanges) {
|
|
986
|
+
entry.resultKeys.set(change.value, childKey);
|
|
987
|
+
if (entry.orderByIndices && change.orderByIndex !== void 0) {
|
|
988
|
+
entry.orderByIndices.set(change.value, change.orderByIndex);
|
|
989
|
+
}
|
|
990
|
+
if (change.inserts > 0 && change.deletes === 0) {
|
|
991
|
+
entry.syncMethods.write({ value: change.value, type: `insert` });
|
|
992
|
+
} else if (change.inserts > change.deletes || change.inserts === change.deletes && entry.syncMethods.collection.has(
|
|
993
|
+
entry.syncMethods.collection.getKeyFromItem(change.value)
|
|
994
|
+
)) {
|
|
995
|
+
entry.syncMethods.write({ value: change.value, type: `update` });
|
|
996
|
+
} else if (change.deletes > 0) {
|
|
997
|
+
entry.syncMethods.write({ value: change.value, type: `delete` });
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
entry.syncMethods.commit();
|
|
1001
|
+
}
|
|
1002
|
+
updateRoutingIndex(state, correlationKey, childChanges);
|
|
1003
|
+
entriesWithChildChanges.set(correlationKey, { entry, childChanges });
|
|
1004
|
+
}
|
|
1005
|
+
state.pendingChildChanges.clear();
|
|
1006
|
+
}
|
|
1007
|
+
const dirtyFromBuffers = drainNestedBuffers(state);
|
|
1008
|
+
for (const [, { entry, childChanges }] of entriesWithChildChanges) {
|
|
1009
|
+
if (entry.includesStates) {
|
|
1010
|
+
flushIncludesState(
|
|
1011
|
+
entry.includesStates,
|
|
1012
|
+
entry.collection,
|
|
1013
|
+
entry.collection.id,
|
|
1014
|
+
childChanges,
|
|
1015
|
+
entry.syncMethods
|
|
1016
|
+
);
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
for (const correlationKey of dirtyFromBuffers) {
|
|
1020
|
+
if (entriesWithChildChanges.has(correlationKey)) continue;
|
|
1021
|
+
const entry = state.childRegistry.get(correlationKey);
|
|
1022
|
+
if (entry?.includesStates) {
|
|
1023
|
+
flushIncludesState(
|
|
1024
|
+
entry.includesStates,
|
|
1025
|
+
entry.collection,
|
|
1026
|
+
entry.collection.id,
|
|
1027
|
+
null,
|
|
1028
|
+
entry.syncMethods
|
|
1029
|
+
);
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
const inlineReEmitKeys = materializesInline(state) ? /* @__PURE__ */ new Set([...affectedCorrelationKeys || [], ...dirtyFromBuffers]) : null;
|
|
1033
|
+
if (parentSyncMethods && inlineReEmitKeys && inlineReEmitKeys.size > 0) {
|
|
1034
|
+
const events = [];
|
|
1035
|
+
for (const correlationKey of inlineReEmitKeys) {
|
|
1036
|
+
const parentKeys = state.correlationToParentKeys.get(correlationKey);
|
|
1037
|
+
if (!parentKeys) continue;
|
|
1038
|
+
const entry = state.childRegistry.get(correlationKey);
|
|
1039
|
+
for (const parentKey of parentKeys) {
|
|
1040
|
+
const item = parentCollection.get(parentKey);
|
|
1041
|
+
if (item) {
|
|
1042
|
+
const key = parentSyncMethods.collection.getKeyFromItem(item);
|
|
1043
|
+
const previousValue = { ...item };
|
|
1044
|
+
item[state.fieldName] = materializeIncludedValue(state, entry);
|
|
1045
|
+
events.push({
|
|
1046
|
+
type: `update`,
|
|
1047
|
+
key,
|
|
1048
|
+
value: item,
|
|
1049
|
+
previousValue
|
|
1050
|
+
});
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
if (events.length > 0) {
|
|
1055
|
+
const changesManager = parentCollection._changes;
|
|
1056
|
+
changesManager.emitEvents(events, true);
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
if (parentChanges) {
|
|
1060
|
+
for (const [parentKey, changes] of parentChanges) {
|
|
1061
|
+
if (changes.deletes > 0 && changes.inserts === 0) {
|
|
1062
|
+
const routing = changes.value[index.INCLUDES_ROUTING]?.[state.fieldName];
|
|
1063
|
+
const correlationKey = routing?.correlationKey;
|
|
1064
|
+
const parentContext = routing?.parentContext ?? null;
|
|
1065
|
+
const routingKey = computeRoutingKey(correlationKey, parentContext);
|
|
1066
|
+
if (correlationKey != null) {
|
|
1067
|
+
const parentKeys = state.correlationToParentKeys.get(routingKey);
|
|
1068
|
+
if (parentKeys) {
|
|
1069
|
+
parentKeys.delete(parentKey);
|
|
1070
|
+
if (parentKeys.size === 0) {
|
|
1071
|
+
cleanRoutingIndexOnDelete(state, routingKey);
|
|
1072
|
+
state.childRegistry.delete(routingKey);
|
|
1073
|
+
state.correlationToParentKeys.delete(routingKey);
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
if (parentChanges) {
|
|
1082
|
+
for (const [, changes] of parentChanges) {
|
|
1083
|
+
delete changes.value[index.INCLUDES_ROUTING];
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
function hasPendingIncludesChanges(states) {
|
|
1088
|
+
for (const state of states) {
|
|
1089
|
+
if (state.pendingChildChanges.size > 0) return true;
|
|
1090
|
+
if (state.nestedSetups && hasNestedBufferChanges(state.nestedSetups))
|
|
1091
|
+
return true;
|
|
1092
|
+
}
|
|
1093
|
+
return false;
|
|
1094
|
+
}
|
|
1095
|
+
function attachChildCollectionToParent(parentCollection, fieldName, correlationKey, correlationToParentKeys, childCollection) {
|
|
1096
|
+
const parentKeys = correlationToParentKeys.get(correlationKey);
|
|
1097
|
+
if (!parentKeys) return;
|
|
1098
|
+
for (const parentKey of parentKeys) {
|
|
1099
|
+
const item = parentCollection.get(parentKey);
|
|
1100
|
+
if (item) {
|
|
1101
|
+
item[fieldName] = childCollection;
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
609
1105
|
function accumulateChanges(acc, [[key, tupleData], multiplicity]) {
|
|
610
1106
|
const [value, orderByIndex] = tupleData;
|
|
611
1107
|
const changes = acc.get(key) || {
|