@powersync/service-sync-rules 0.39.0 → 0.41.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 (92) hide show
  1. package/dist/HydratedSyncConfig.d.ts +9 -2
  2. package/dist/HydratedSyncConfig.js +12 -1
  3. package/dist/HydratedSyncConfig.js.map +1 -1
  4. package/dist/HydrationState.d.ts +2 -0
  5. package/dist/HydrationState.js.map +1 -1
  6. package/dist/StorageVersion.js +2 -1
  7. package/dist/StorageVersion.js.map +1 -1
  8. package/dist/SyncConfig.d.ts +4 -3
  9. package/dist/SyncConfig.js +10 -11
  10. package/dist/SyncConfig.js.map +1 -1
  11. package/dist/TablePattern.d.ts +2 -0
  12. package/dist/TablePattern.js +18 -1
  13. package/dist/TablePattern.js.map +1 -1
  14. package/dist/compiler/bucket_resolver.d.ts +1 -0
  15. package/dist/compiler/bucket_resolver.js +22 -12
  16. package/dist/compiler/bucket_resolver.js.map +1 -1
  17. package/dist/compiler/compatibility.d.ts +38 -9
  18. package/dist/compiler/compatibility.js +81 -11
  19. package/dist/compiler/compatibility.js.map +1 -1
  20. package/dist/compiler/compiler.d.ts +81 -13
  21. package/dist/compiler/compiler.js +111 -15
  22. package/dist/compiler/compiler.js.map +1 -1
  23. package/dist/compiler/expression.d.ts +35 -12
  24. package/dist/compiler/expression.js +49 -13
  25. package/dist/compiler/expression.js.map +1 -1
  26. package/dist/compiler/filter.d.ts +4 -4
  27. package/dist/compiler/filter.js +7 -6
  28. package/dist/compiler/filter.js.map +1 -1
  29. package/dist/compiler/ir_to_sync_plan.d.ts +4 -2
  30. package/dist/compiler/ir_to_sync_plan.js +47 -37
  31. package/dist/compiler/ir_to_sync_plan.js.map +1 -1
  32. package/dist/compiler/parser.js +3 -3
  33. package/dist/compiler/parser.js.map +1 -1
  34. package/dist/compiler/querier_graph.js +45 -26
  35. package/dist/compiler/querier_graph.js.map +1 -1
  36. package/dist/compiler/rows.d.ts +23 -29
  37. package/dist/compiler/rows.js +83 -59
  38. package/dist/compiler/rows.js.map +1 -1
  39. package/dist/compiler/sqlite.d.ts +3 -2
  40. package/dist/compiler/sqlite.js +33 -3
  41. package/dist/compiler/sqlite.js.map +1 -1
  42. package/dist/compiler/table.d.ts +3 -2
  43. package/dist/compiler/table.js +9 -7
  44. package/dist/compiler/table.js.map +1 -1
  45. package/dist/events/CompiledEventSourceQuery.d.ts +51 -0
  46. package/dist/events/CompiledEventSourceQuery.js +119 -0
  47. package/dist/events/CompiledEventSourceQuery.js.map +1 -0
  48. package/dist/events/EventDescriptor.d.ts +26 -0
  49. package/dist/events/EventDescriptor.js +2 -0
  50. package/dist/events/EventDescriptor.js.map +1 -0
  51. package/dist/from_yaml.js +157 -189
  52. package/dist/from_yaml.js.map +1 -1
  53. package/dist/index.d.ts +2 -2
  54. package/dist/index.js +2 -2
  55. package/dist/index.js.map +1 -1
  56. package/dist/json_schema.js +1 -1
  57. package/dist/json_schema.js.map +1 -1
  58. package/dist/sync_plan/engine/scalar_expression_engine.js +7 -0
  59. package/dist/sync_plan/engine/scalar_expression_engine.js.map +1 -1
  60. package/dist/sync_plan/evaluator/bucket_data_source.js +11 -52
  61. package/dist/sync_plan/evaluator/bucket_data_source.js.map +1 -1
  62. package/dist/sync_plan/evaluator/index.d.ts +2 -7
  63. package/dist/sync_plan/evaluator/index.js +6 -7
  64. package/dist/sync_plan/evaluator/index.js.map +1 -1
  65. package/dist/sync_plan/evaluator/parameter_evaluator.js +11 -7
  66. package/dist/sync_plan/evaluator/parameter_evaluator.js.map +1 -1
  67. package/dist/sync_plan/evaluator/parameter_index_lookup_creator.js +2 -2
  68. package/dist/sync_plan/evaluator/parameter_index_lookup_creator.js.map +1 -1
  69. package/dist/sync_plan/evaluator/row_projection.d.ts +22 -0
  70. package/dist/sync_plan/evaluator/row_projection.js +71 -0
  71. package/dist/sync_plan/evaluator/row_projection.js.map +1 -0
  72. package/dist/sync_plan/evaluator/table_processor_to_sql.d.ts +5 -2
  73. package/dist/sync_plan/evaluator/table_processor_to_sql.js +10 -0
  74. package/dist/sync_plan/evaluator/table_processor_to_sql.js.map +1 -1
  75. package/dist/sync_plan/plan.d.ts +49 -9
  76. package/dist/sync_plan/plan_equality_serialized.d.ts +12 -2
  77. package/dist/sync_plan/plan_equality_serialized.js +56 -0
  78. package/dist/sync_plan/plan_equality_serialized.js.map +1 -1
  79. package/dist/sync_plan/serialize.d.ts +54 -5
  80. package/dist/sync_plan/serialize.js +109 -19
  81. package/dist/sync_plan/serialize.js.map +1 -1
  82. package/dist/yaml_validation.d.ts +73 -0
  83. package/dist/yaml_validation.js +140 -0
  84. package/dist/yaml_validation.js.map +1 -0
  85. package/package.json +1 -1
  86. package/schema/sync_rules.json +3 -2
  87. package/dist/events/SqlEventDescriptor.d.ts +0 -20
  88. package/dist/events/SqlEventDescriptor.js +0 -51
  89. package/dist/events/SqlEventDescriptor.js.map +0 -1
  90. package/dist/events/SqlEventSourceQuery.d.ts +0 -20
  91. package/dist/events/SqlEventSourceQuery.js +0 -125
  92. package/dist/events/SqlEventSourceQuery.js.map +0 -1
@@ -0,0 +1,71 @@
1
+ import { filterJsonRow, isJsonValue } from '../../utils.js';
2
+ import { scalarStatementToSql } from '../engine/scalar_expression_engine.js';
3
+ import { resolveRowMetadata, TableProcessorToSqlHelper } from './table_processor_to_sql.js';
4
+ /**
5
+ * Prepares the shared projection/filter portion of bucket and event row evaluators.
6
+ */
7
+ export class PendingRowProjection {
8
+ tablePattern;
9
+ outputs = [];
10
+ numberOfOutputExpressions;
11
+ numberOfParameters;
12
+ evaluatorInputs;
13
+ statement;
14
+ constructor(evaluator, defaultSchema) {
15
+ const translationHelper = new TableProcessorToSqlHelper(evaluator);
16
+ const outputExpressions = [];
17
+ for (const column of evaluator.columns) {
18
+ if (column === 'star') {
19
+ this.outputs.push('star');
20
+ }
21
+ else {
22
+ const expressionIndex = outputExpressions.length;
23
+ outputExpressions.push(translationHelper.mapper.transform(column.expr));
24
+ this.outputs.push({ index: expressionIndex, alias: column.alias });
25
+ }
26
+ }
27
+ this.numberOfOutputExpressions = outputExpressions.length;
28
+ for (const parameter of evaluator.parameters) {
29
+ outputExpressions.push(translationHelper.mapper.transform(parameter.expr));
30
+ }
31
+ this.numberOfParameters = evaluator.parameters.length;
32
+ this.statement = {
33
+ outputs: outputExpressions,
34
+ filters: translationHelper.filterExpressions,
35
+ tableValuedFunctions: translationHelper.tableValuedFunctions
36
+ };
37
+ this.tablePattern = evaluator.sourceTable.toTablePattern(defaultSchema);
38
+ this.evaluatorInputs = translationHelper.mapper.instantiation;
39
+ }
40
+ get debugSql() {
41
+ return scalarStatementToSql(this.statement);
42
+ }
43
+ instantiate(engine) {
44
+ const evaluator = engine.prepareEvaluator(this.statement);
45
+ const pattern = this.tablePattern;
46
+ return (options) => {
47
+ const inputInstantiation = this.evaluatorInputs.map((input) => 'column' in input ? options.record[input.column] : resolveRowMetadata(input, pattern, options.sourceTable));
48
+ const results = [];
49
+ for (const source of evaluator.evaluate(inputInstantiation)) {
50
+ const record = {};
51
+ for (const output of this.outputs) {
52
+ if (output === 'star') {
53
+ Object.assign(record, filterJsonRow(options.record));
54
+ }
55
+ else {
56
+ const value = source[output.index];
57
+ if (isJsonValue(value)) {
58
+ record[output.alias] = value;
59
+ }
60
+ }
61
+ }
62
+ results.push({
63
+ data: record,
64
+ partitionValues: source.slice(this.numberOfOutputExpressions, this.numberOfOutputExpressions + this.numberOfParameters)
65
+ });
66
+ }
67
+ return results;
68
+ };
69
+ }
70
+ }
71
+ //# sourceMappingURL=row_projection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"row_projection.js","sourceRoot":"","sources":["../../../src/sync_plan/evaluator/row_projection.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,5 @@
1
+ import { SourceTableRef } from '../../SourceTableRef.js';
2
+ import { TablePattern } from '../../TablePattern.js';
1
3
  import { TableValuedFunction, TableValuedFunctionOutput } from '../engine/scalar_expression_engine.js';
2
4
  import { SqlExpression } from '../expression.js';
3
5
  import * as plan from '../plan.js';
@@ -9,12 +11,13 @@ import * as plan from '../plan.js';
9
11
  */
10
12
  export declare class TableProcessorToSqlHelper {
11
13
  mapper: {
12
- instantiation: plan.ColumnSqlParameterValue[];
14
+ instantiation: (plan.ColumnSqlParameterValue | plan.RowMetadataSqlValue)[];
13
15
  tableValuedFunctions: Map<plan.TableProcessorTableValuedFunction, TableValuedFunction>;
14
16
  transform(expr: SqlExpression<plan.SqlParameterValue>): SqlExpression<number | TableValuedFunctionOutput>;
15
- transformWithoutTableValued(expr: SqlExpression<plan.ColumnSqlParameterValue>): SqlExpression<number>;
17
+ transformWithoutTableValued(expr: SqlExpression<plan.ColumnSqlParameterValue | plan.RowMetadataSqlValue>): SqlExpression<number>;
16
18
  };
17
19
  readonly filterExpressions: SqlExpression<number | TableValuedFunctionOutput>[];
18
20
  get tableValuedFunctions(): TableValuedFunction[];
19
21
  constructor(source: plan.TableProcessor);
20
22
  }
23
+ export declare function resolveRowMetadata(value: plan.RowMetadataSqlValue, pattern: TablePattern, table: SourceTableRef): string;
@@ -25,4 +25,14 @@ export class TableProcessorToSqlHelper {
25
25
  }
26
26
  }
27
27
  }
28
+ export function resolveRowMetadata(value, pattern, table) {
29
+ switch (value.metadata) {
30
+ case 'schema':
31
+ return table.schema;
32
+ case 'table_name':
33
+ return table.name;
34
+ case 'table_suffix':
35
+ return pattern.isWildcard ? pattern.suffix(table.name) : '';
36
+ }
37
+ }
28
38
  //# sourceMappingURL=table_processor_to_sql.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"table_processor_to_sql.js","sourceRoot":"","sources":["../../../src/sync_plan/evaluator/table_processor_to_sql.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,8BAA8B,EAG/B,MAAM,uCAAuC,CAAC;AAI/C;;;;;GAKG;AACH,MAAM,OAAO,yBAAyB;IACpC,MAAM,GAAG,8BAA8B,EAAgC,CAAC;IAC/D,iBAAiB,GAAwD,EAAE,CAAC;IAErF,IAAI,oBAAoB;QACtB,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC,MAAM,EAAE,CAAC,CAAC;IACxD,CAAC;IAED,YAAY,MAA2B;QACrC,yCAAyC;QACzC,KAAK,MAAM,EAAE,IAAI,MAAM,CAAC,oBAAoB,EAAE,CAAC;YAC7C,MAAM,MAAM,GAAwB;gBAClC,IAAI,EAAE,EAAE,CAAC,YAAY;gBACrB,MAAM,EAAE,EAAE,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,2BAA2B,CAAC,CAAC,CAAC,CAAC;aACjF,CAAC;YACF,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QACnD,CAAC;QAED,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACpC,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;CACF"}
1
+ {"version":3,"file":"table_processor_to_sql.js","sourceRoot":"","sources":["../../../src/sync_plan/evaluator/table_processor_to_sql.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,8BAA8B,EAG/B,MAAM,uCAAuC,CAAC;AAI/C;;;;;GAKG;AACH,MAAM,OAAO,yBAAyB;IACpC,MAAM,GAAG,8BAA8B,EAA2D,CAAC;IAC1F,iBAAiB,GAAwD,EAAE,CAAC;IAErF,IAAI,oBAAoB;QACtB,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC,MAAM,EAAE,CAAC,CAAC;IACxD,CAAC;IAED,YAAY,MAA2B;QACrC,yCAAyC;QACzC,KAAK,MAAM,EAAE,IAAI,MAAM,CAAC,oBAAoB,EAAE,CAAC;YAC7C,MAAM,MAAM,GAAwB;gBAClC,IAAI,EAAE,EAAE,CAAC,YAAY;gBACrB,MAAM,EAAE,EAAE,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,2BAA2B,CAAC,CAAC,CAAC,CAAC;aACjF,CAAC;YACF,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QACnD,CAAC;QAED,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACpC,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;CACF;AAED,MAAM,UAAU,kBAAkB,CAChC,KAA+B,EAC/B,OAAqB,EACrB,KAAqB;IAErB,QAAQ,KAAK,CAAC,QAAQ,EAAE,CAAC;QACvB,KAAK,QAAQ;YACX,OAAO,KAAK,CAAC,MAAM,CAAC;QACtB,KAAK,YAAY;YACf,OAAO,KAAK,CAAC,IAAI,CAAC;QACpB,KAAK,cAAc;YACjB,OAAO,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAChE,CAAC;AACH,CAAC"}
@@ -24,6 +24,7 @@ export interface SyncPlan {
24
24
  buckets: StreamBucketDataSource[];
25
25
  parameterIndexes: StreamParameterIndexLookupCreator[];
26
26
  streams: CompiledSyncStream[];
27
+ events: CompiledEventDescriptor[];
27
28
  }
28
29
  /**
29
30
  * Something that processes a source row if it matches filters.
@@ -88,10 +89,10 @@ export interface TableProcessorTableValuedFunctionOutput {
88
89
  /**
89
90
  * External data that can be used in expressions for {@link TableProcessor}s.
90
91
  *
91
- * This allows references to the row in the table being processed, and references to outputs of joined
92
- * {@link TableProcessor#tableValuedFunctions}.
92
+ * This allows references to the row in the table being processed, metadata about the row's source table, and
93
+ * references to outputs of joined {@link TableProcessor#tableValuedFunctions}.
93
94
  */
94
- export type TableProcessorData = ColumnSqlParameterValue | TableProcessorTableValuedFunctionOutput;
95
+ export type TableProcessorData = ColumnSqlParameterValue | RowMetadataSqlValue | TableProcessorTableValuedFunctionOutput;
95
96
  /**
96
97
  * A scalar partition key evaluates to a single value in the source or parameter row. For instance, a stream definition
97
98
  * like `SELECT * FROM users WHERE id = auth.user_id()` would generate a scalar key to partition users by the `id`
@@ -107,22 +108,52 @@ export interface PartitionKey {
107
108
  * duplicate bucket data but can still share the actual processing logic between multiple streams (to avoid evaluating
108
109
  * the same filters and expressions multiple times).
109
110
  */
110
- export interface StreamDataSource extends TableProcessor {
111
+ export interface RowProjection extends TableProcessor {
112
+ /**
113
+ * Output columns produced when a matching source row is evaluated.
114
+ */
115
+ columns: ColumnSource[];
116
+ }
117
+ export interface StreamDataSource extends RowProjection {
111
118
  /**
112
119
  * The name of the output table for evaluated rows.
113
120
  *
114
121
  * If null, the name of the table being evaluated should be used instead.
115
122
  */
116
123
  outputTableName?: string;
117
- /**
118
- * Output columns describing the row to store in buckets.
119
- */
120
- columns: ColumnSource[];
121
124
  }
122
125
  export type ColumnSource = 'star' | {
123
126
  expr: SqlExpression<TableProcessorData>;
124
127
  alias: string;
125
128
  };
129
+ /**
130
+ * A named replication event compiled from `event_definitions`.
131
+ */
132
+ export interface CompiledEventDescriptor {
133
+ name: string;
134
+ sourceQueries: CompiledEventSourceQuery[];
135
+ }
136
+ /**
137
+ * A single payload query for an event.
138
+ *
139
+ * Event payload queries are restricted to one physical source table. A query can have multiple variants after its
140
+ * filter has been normalized to disjunctive normal form. Evaluation stops after the first matching variant, which may
141
+ * itself produce multiple payload rows, for example through a table-valued expansion.
142
+ */
143
+ export interface CompiledEventSourceQuery {
144
+ /**
145
+ * Original SQL retained as a compatibility mirror for services using the legacy event evaluator. Compiled event
146
+ * evaluation uses the remaining fields.
147
+ */
148
+ sql: string;
149
+ sourceTable: ImplicitSchemaTablePattern;
150
+ variants: EventRowEvaluator[];
151
+ }
152
+ /**
153
+ * A row projection used by one normalized filter variant of an event payload query.
154
+ */
155
+ export interface EventRowEvaluator extends RowProjection {
156
+ }
126
157
  /**
127
158
  * A mapping describing how {@link StreamDataSource}s are combined into buckets.
128
159
  *
@@ -197,6 +228,15 @@ export interface StreamQuerier {
197
228
  export interface ColumnSqlParameterValue {
198
229
  column: string;
199
230
  }
231
+ export type RowMetadataKind = 'schema' | 'table_name' | 'table_suffix';
232
+ /**
233
+ * A value that resolves to metadata about the source table of the row being processed: the schema containing the
234
+ * table (`schema()`), the name of the table (`table_name()`) or, for wildcard table patterns, the matched
235
+ * suffix of the table name (`table_suffix()`).
236
+ */
237
+ export interface RowMetadataSqlValue {
238
+ metadata: RowMetadataKind;
239
+ }
200
240
  export type ConnectionParameterSource = 'auth' | 'subscription' | 'connection';
201
241
  /**
202
242
  * A value that resolves to either the current subscription parameters, the JWT of the connecting user or global
@@ -205,7 +245,7 @@ export type ConnectionParameterSource = 'auth' | 'subscription' | 'connection';
205
245
  export interface RequestSqlParameterValue {
206
246
  request: ConnectionParameterSource;
207
247
  }
208
- export type SqlParameterValue = ColumnSqlParameterValue | RequestSqlParameterValue | TableProcessorTableValuedFunctionOutput;
248
+ export type SqlParameterValue = ColumnSqlParameterValue | RowMetadataSqlValue | RequestSqlParameterValue | TableProcessorTableValuedFunctionOutput;
209
249
  /**
210
250
  * A lookup returning multiple rows when instantiated.
211
251
  */
@@ -1,5 +1,5 @@
1
- import { Equality } from '../compiler/equality.js';
2
- import { SerializedBucketDataSource, SerializedDataSource, SerializedParameterIndexLookupCreator } from './serialize.js';
1
+ import type { Equality } from '../compiler/equality.js';
2
+ import type { SerializedBucketDataSource, SerializedDataSource, SerializedEventDescriptor, SerializedParameterIndexLookupCreator } from './serialize.js';
3
3
  export interface SerializedBucketDataSourceWithDataSources {
4
4
  bucket: SerializedBucketDataSource;
5
5
  dataSources: readonly SerializedDataSource[];
@@ -13,6 +13,16 @@ export interface SerializedBucketDataSourceWithDataSources {
13
13
  * These are only considered equal if the lookupName remains the same, so may be affected by changing order of queries.
14
14
  */
15
15
  export declare const serializedStreamParameterIndexLookupCreatorEquality: Equality<SerializedParameterIndexLookupCreator>;
16
+ /**
17
+ * Equality for persisted replication events.
18
+ *
19
+ * Raw SQL is a rolling-upgrade compatibility mirror and cached evaluator hashes are not equality checks, so neither is
20
+ * part of event behavior. Payload queries are unordered because runtime evaluates every query matching the source
21
+ * table and treats the resulting payloads as an unordered collection. Normalized variants are unordered because they
22
+ * share a projection and only determine whether a row matches. The remaining serialized plan is a stable,
23
+ * self-contained behavior representation: Expression ASTs include both their shape and external-data bindings.
24
+ */
25
+ export declare const serializedEventDefinitionEquality: Equality<SerializedEventDescriptor>;
16
26
  /**
17
27
  * SerializedBucketDataSource is not safe to compare _directly_, since it contains index references to SerializedDataSource
18
28
  * in the serialized sync plan. However, each SerializedDataSource is self-contained and safe to compare directly.
@@ -7,6 +7,23 @@
7
7
  * These are only considered equal if the lookupName remains the same, so may be affected by changing order of queries.
8
8
  */
9
9
  export const serializedStreamParameterIndexLookupCreatorEquality = jsonEquality();
10
+ /**
11
+ * Equality for persisted replication events.
12
+ *
13
+ * Raw SQL is a rolling-upgrade compatibility mirror and cached evaluator hashes are not equality checks, so neither is
14
+ * part of event behavior. Payload queries are unordered because runtime evaluates every query matching the source
15
+ * table and treats the resulting payloads as an unordered collection. Normalized variants are unordered because they
16
+ * share a projection and only determine whether a row matches. The remaining serialized plan is a stable,
17
+ * self-contained behavior representation: Expression ASTs include both their shape and external-data bindings.
18
+ */
19
+ export const serializedEventDefinitionEquality = {
20
+ hash(hasher, value) {
21
+ hasher.addString(persistedJson(eventIdentity(value)));
22
+ },
23
+ equals(a, b) {
24
+ return a === b || persistedJson(eventIdentity(a)) == persistedJson(eventIdentity(b));
25
+ }
26
+ };
10
27
  /**
11
28
  * SerializedBucketDataSource is not safe to compare _directly_, since it contains index references to SerializedDataSource
12
29
  * in the serialized sync plan. However, each SerializedDataSource is self-contained and safe to compare directly.
@@ -33,6 +50,45 @@ function bucketIdentity(value) {
33
50
  sources: bucket.sources.map((index) => JSON.stringify(dataSources[index])).sort()
34
51
  };
35
52
  }
53
+ /**
54
+ * Builds a normalized identity for comparing serialized event descriptors from persisted sync plans.
55
+ *
56
+ * Raw SQL is excluded, while source queries and variants are sorted because their order does not affect event
57
+ * behavior.
58
+ */
59
+ function eventIdentity(event) {
60
+ return {
61
+ name: event.name,
62
+ sourceQueries: event.sourceQueries
63
+ .map((query) => persistedJson({
64
+ table: query.table,
65
+ variants: query.variants.map((variant) => persistedJson(eventRowEvaluatorIdentity(variant))).sort()
66
+ }))
67
+ .sort()
68
+ };
69
+ }
70
+ /**
71
+ * Selects the behavioral fields used when comparing row evaluators within serialized event descriptors.
72
+ *
73
+ * The cached evaluator hash is deliberately omitted. Ordering inside an evaluator is retained conservatively so
74
+ * changes to projected column order or the compiler's filter-expression order invalidate persisted compatibility.
75
+ */
76
+ function eventRowEvaluatorIdentity(evaluator) {
77
+ return {
78
+ table: evaluator.table,
79
+ tableValuedFunctions: evaluator.tableValuedFunctions,
80
+ filters: evaluator.filters,
81
+ partitionBy: evaluator.partitionBy,
82
+ columns: evaluator.columns
83
+ };
84
+ }
85
+ /**
86
+ * MongoDB persists undefined object properties as null. Treat the two forms as the same so a plan compares equal
87
+ * before and after it has been stored and loaded again.
88
+ */
89
+ function persistedJson(value) {
90
+ return JSON.stringify(value, (_key, item) => (item === undefined ? null : item));
91
+ }
36
92
  function jsonEquality() {
37
93
  return {
38
94
  hash(hasher, value) {
@@ -1 +1 @@
1
- {"version":3,"file":"plan_equality_serialized.js","sourceRoot":"","sources":["../../src/sync_plan/plan_equality_serialized.ts"],"names":[],"mappings":"AAYA;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,mDAAmD,GAC9D,YAAY,EAAyC,CAAC;AAExD;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,wCAAwC,GAAwD;IAC3G,IAAI,CAAC,MAAM,EAAE,KAAK;QAChB,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC1D,CAAC;IACD,MAAM,CAAC,CAAC,EAAE,CAAC;QACT,OAAO,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3F,CAAC;CACF,CAAC;AAEF,SAAS,cAAc,CAAC,KAAgD;IACtE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,KAAK,CAAC;IAEtC,OAAO;QACL,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,6GAA6G;QAC7G,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE;KAClF,CAAC;AACJ,CAAC;AAED,SAAS,YAAY;IACnB,OAAO;QACL,IAAI,CAAC,MAAM,EAAE,KAAK;YAChB,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;QAC1C,CAAC;QACD,MAAM,CAAC,CAAC,EAAE,CAAC;YACT,OAAO,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;QAC3D,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"plan_equality_serialized.js","sourceRoot":"","sources":["../../src/sync_plan/plan_equality_serialized.ts"],"names":[],"mappings":"AAcA;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,mDAAmD,GAC9D,YAAY,EAAyC,CAAC;AAExD;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,iCAAiC,GAAwC;IACpF,IAAI,CAAC,MAAM,EAAE,KAAK;QAChB,MAAM,CAAC,SAAS,CAAC,aAAa,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACxD,CAAC;IACD,MAAM,CAAC,CAAC,EAAE,CAAC;QACT,OAAO,CAAC,KAAK,CAAC,IAAI,aAAa,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI,aAAa,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;IACvF,CAAC;CACF,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,wCAAwC,GAAwD;IAC3G,IAAI,CAAC,MAAM,EAAE,KAAK;QAChB,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC1D,CAAC;IACD,MAAM,CAAC,CAAC,EAAE,CAAC;QACT,OAAO,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3F,CAAC;CACF,CAAC;AAEF,SAAS,cAAc,CAAC,KAAgD;IACtE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,KAAK,CAAC;IAEtC,OAAO;QACL,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,6GAA6G;QAC7G,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE;KAClF,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAS,aAAa,CAAC,KAAgC;IACrD,OAAO;QACL,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,aAAa,EAAE,KAAK,CAAC,aAAa;aAC/B,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CACb,aAAa,CAAC;YACZ,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,aAAa,CAAC,yBAAyB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE;SACpG,CAAC,CACH;aACA,IAAI,EAAE;KACV,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAS,yBAAyB,CAAC,SAAsC;IACvE,OAAO;QACL,KAAK,EAAE,SAAS,CAAC,KAAK;QACtB,oBAAoB,EAAE,SAAS,CAAC,oBAAoB;QACpD,OAAO,EAAE,SAAS,CAAC,OAAO;QAC1B,WAAW,EAAE,SAAS,CAAC,WAAW;QAClC,OAAO,EAAE,SAAS,CAAC,OAAO;KAC3B,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAS,aAAa,CAAC,KAAc;IACnC,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;AACnF,CAAC;AAED,SAAS,YAAY;IACnB,OAAO;QACL,IAAI,CAAC,MAAM,EAAE,KAAK;YAChB,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;QAC1C,CAAC;QACD,MAAM,CAAC,CAAC,EAAE,CAAC;YACT,OAAO,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;QAC3D,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import { ParameterLookupDefinitionId } from '../HydrationState.js';
2
2
  import { SqlExpression } from './expression.js';
3
- import { ColumnSqlParameterValue, RequestSqlParameterValue, StreamOptions, SyncPlan, TableProcessorTableValuedFunction } from './plan.js';
3
+ import { ColumnSqlParameterValue, RequestSqlParameterValue, RowMetadataSqlValue, StreamOptions, SyncPlan, TableProcessorTableValuedFunction } from './plan.js';
4
4
  /**
5
5
  * Serializes a sync plan into a simple JSON object.
6
6
  *
@@ -8,14 +8,40 @@ import { ColumnSqlParameterValue, RequestSqlParameterValue, StreamOptions, SyncP
8
8
  * queriers to bucket creators. To represent this efficiently, we assign numbers to referenced elements while
9
9
  * serializing instead of duplicating definitions.
10
10
  */
11
- export declare function serializeSyncPlan(plan: SyncPlan): SerializedSyncPlanV1;
11
+ export declare function serializeSyncPlan(plan: SyncPlan): SerializedSyncPlan;
12
12
  export declare function deserializeSyncPlan(serialized: unknown): SyncPlan;
13
- export interface SerializedSyncPlanV1 {
14
- version: number;
13
+ /**
14
+ * Changes to {@link SerializedSyncPlan} require a version bump when older services would interpret the plan
15
+ * incorrectly. Optional additive fields are only safe without a bump when older readers can ignore them while another
16
+ * persisted representation preserves equivalent behavior.
17
+ *
18
+ * Compiled `events` are an explicit additive exception: service-core continues to persist raw event SQL alongside the
19
+ * plan for the legacy evaluator. Older readers ignore `events` and use that legacy mirror. Removing the mirror or
20
+ * relying on compiled-only event semantics will require a version bump.
21
+ *
22
+ * ### Version 2
23
+ *
24
+ * - Add {@link RowMetadataSqlValue} to data for row and parameter evaluators, exposing the exact table and schema name
25
+ * when matching on wildcard table patterns.
26
+ * The deserialization logic can remain the same for v1 and v2.
27
+ *
28
+ * ### Version 1
29
+ *
30
+ * - Initial version
31
+ */
32
+ export type SerializedSyncPlanVersion = 1 | 2;
33
+ export declare const maxSupportedSyncPlanVersion: SerializedSyncPlanVersion;
34
+ export interface SerializedSyncPlan {
35
+ version: SerializedSyncPlanVersion;
15
36
  dataSources: SerializedDataSource[];
16
37
  buckets: SerializedBucketDataSource[];
17
38
  parameterIndexes: SerializedParameterIndexLookupCreator[];
18
39
  streams: SerializedStream[];
40
+ /**
41
+ * Optional additive compiled event definitions. Older readers safely ignore this because service-core dual-writes
42
+ * equivalent raw SQL in its legacy `eventDescriptors` field.
43
+ */
44
+ events?: SerializedEventDescriptor[];
19
45
  }
20
46
  export interface SerializedBucketDataSource {
21
47
  hash: number;
@@ -31,7 +57,7 @@ export interface SerializedTableProcessorTableValuedFunctionOutput {
31
57
  function: number;
32
58
  outputName: string;
33
59
  }
34
- export type SerializedTableProcessorData = ColumnSqlParameterValue | SerializedTableProcessorTableValuedFunctionOutput;
60
+ export type SerializedTableProcessorData = ColumnSqlParameterValue | RowMetadataSqlValue | SerializedTableProcessorTableValuedFunctionOutput;
35
61
  export interface SerializedPartitionKey {
36
62
  expr: SqlExpression<SerializedTableProcessorData>;
37
63
  }
@@ -48,6 +74,29 @@ export interface SerializedDataSource {
48
74
  tableValuedFunctions: TableProcessorTableValuedFunction[];
49
75
  partitionBy: SerializedPartitionKey[];
50
76
  }
77
+ export interface SerializedEventDescriptor {
78
+ name: string;
79
+ sourceQueries: SerializedEventSourceQuery[];
80
+ }
81
+ export interface SerializedEventSourceQuery {
82
+ /** Raw SQL retained for the legacy compatibility mirror. */
83
+ sql: string;
84
+ table: SerializedTablePattern;
85
+ variants: SerializedEventRowEvaluator[];
86
+ }
87
+ export interface SerializedEventRowEvaluator {
88
+ table: SerializedTablePattern;
89
+ /**
90
+ * The compiler's structural hash, retained only for round-trip symmetry with data sources (which reuse the same
91
+ * projection shape). It is not part of event identity (the compiler model compares behavior directly), and events
92
+ * are never deduplicated by it at runtime.
93
+ */
94
+ hash: number;
95
+ columns: SerializedColumnSource[];
96
+ filters: SqlExpression<SerializedTableProcessorData>[];
97
+ tableValuedFunctions: TableProcessorTableValuedFunction[];
98
+ partitionBy: SerializedPartitionKey[];
99
+ }
51
100
  export interface SerializedParameterIndexLookupCreator {
52
101
  table: SerializedTablePattern;
53
102
  hash: number;
@@ -1,19 +1,10 @@
1
1
  import { ImplicitSchemaTablePattern, TablePattern } from '../TablePattern.js';
2
2
  import { MapSourceVisitor, visitExpr } from './expression_visitor.js';
3
- /**
4
- * Serializes a sync plan into a simple JSON object.
5
- *
6
- * While {@link SyncPlan}s are already serializable for the most part, it contains a graph of references from e.g.
7
- * queriers to bucket creators. To represent this efficiently, we assign numbers to referenced elements while
8
- * serializing instead of duplicating definitions.
9
- */
10
- export function serializeSyncPlan(plan) {
11
- const dataSourceIndex = new Map();
12
- const bucketIndex = new Map();
13
- const parameterIndex = new Map();
14
- const expandingLookups = new Map();
3
+ function createTableProcessorSerializer() {
15
4
  const addedTableValuedFunctions = new Map();
5
+ let usesRowMetadataSqlValue = false;
16
6
  const replaceFunctionReferenceWithIndex = new MapSourceVisitor((value) => {
7
+ usesRowMetadataSqlValue ||= 'metadata' in value;
17
8
  if ('function' in value) {
18
9
  return { function: addedTableValuedFunctions.get(value.function), outputName: value.outputName };
19
10
  }
@@ -34,7 +25,13 @@ export function serializeSyncPlan(plan) {
34
25
  function serializeTableValued(source) {
35
26
  return source.tableValuedFunctions.map((fn, i) => {
36
27
  addedTableValuedFunctions.set(fn, i);
37
- return fn;
28
+ return {
29
+ functionName: fn.functionName,
30
+ functionInputs: fn.functionInputs.map(
31
+ // Since we don't support table-valued functions as inputs to other table-valued functions, this doesn't
32
+ // change expressions. It ensures we track row metadata use in any input, though.
33
+ (s) => serializeTableProcessorDataExpr(s))
34
+ };
38
35
  });
39
36
  }
40
37
  function translateParameters(source) {
@@ -42,6 +39,56 @@ export function serializeSyncPlan(plan) {
42
39
  return { expr: serializeTableProcessorDataExpr(key.expr) };
43
40
  });
44
41
  }
42
+ function serializeEventRowEvaluator(source) {
43
+ return {
44
+ hash: source.hashCode,
45
+ table: serializeTablePattern(source.sourceTable),
46
+ tableValuedFunctions: serializeTableValued(source),
47
+ filters: source.filters.map(serializeTableProcessorDataExpr),
48
+ partitionBy: translateParameters(source),
49
+ columns: source.columns.map((column) => {
50
+ if (column == 'star') {
51
+ return 'star';
52
+ }
53
+ return { expr: serializeTableProcessorDataExpr(column.expr), alias: column.alias };
54
+ })
55
+ };
56
+ }
57
+ function serializeEventDefinition(event) {
58
+ return {
59
+ name: event.name,
60
+ sourceQueries: event.sourceQueries.map((query) => ({
61
+ sql: query.sql,
62
+ table: serializeTablePattern(query.sourceTable),
63
+ variants: query.variants.map(serializeEventRowEvaluator)
64
+ }))
65
+ };
66
+ }
67
+ return {
68
+ get usesRowMetadataSqlValue() {
69
+ return usesRowMetadataSqlValue;
70
+ },
71
+ serializeTableProcessorDataExpr,
72
+ serializeTablePattern,
73
+ serializeTableValued,
74
+ translateParameters,
75
+ serializeEventDefinition
76
+ };
77
+ }
78
+ /**
79
+ * Serializes a sync plan into a simple JSON object.
80
+ *
81
+ * While {@link SyncPlan}s are already serializable for the most part, it contains a graph of references from e.g.
82
+ * queriers to bucket creators. To represent this efficiently, we assign numbers to referenced elements while
83
+ * serializing instead of duplicating definitions.
84
+ */
85
+ export function serializeSyncPlan(plan) {
86
+ const dataSourceIndex = new Map();
87
+ const bucketIndex = new Map();
88
+ const parameterIndex = new Map();
89
+ const expandingLookups = new Map();
90
+ const tableProcessorSerializer = createTableProcessorSerializer();
91
+ const { serializeTableProcessorDataExpr, serializeTablePattern, serializeTableValued, translateParameters } = tableProcessorSerializer;
45
92
  function serializeDataSources() {
46
93
  return plan.dataSources.map((source, i) => {
47
94
  dataSourceIndex.set(source, i);
@@ -72,7 +119,7 @@ export function serializeSyncPlan(plan) {
72
119
  tableValuedFunctions: serializeTableValued(source),
73
120
  filters: source.filters.map(serializeTableProcessorDataExpr),
74
121
  partitionBy: translateParameters(source),
75
- output: source.outputs.map((out) => visitExpr(replaceFunctionReferenceWithIndex, out, null)),
122
+ output: source.outputs.map(serializeTableProcessorDataExpr),
76
123
  lookupScope: source.defaultLookupScope
77
124
  };
78
125
  });
@@ -124,8 +171,8 @@ export function serializeSyncPlan(plan) {
124
171
  sourceInstantiation: source.sourceInstantiation.map(serializeParameterValue)
125
172
  };
126
173
  }
127
- return {
128
- version: 1,
174
+ const events = plan.events.map(tableProcessorSerializer.serializeEventDefinition);
175
+ const serialized = {
129
176
  dataSources: serializeDataSources(),
130
177
  buckets: plan.buckets.map((bkt, index) => {
131
178
  bucketIndex.set(bkt, index);
@@ -139,13 +186,24 @@ export function serializeSyncPlan(plan) {
139
186
  streams: plan.streams.map((s) => ({
140
187
  stream: s.stream,
141
188
  queriers: s.queriers.map(serializeStreamQuerier)
142
- }))
189
+ })),
190
+ version: tableProcessorSerializer.usesRowMetadataSqlValue ? 2 : 1
143
191
  };
192
+ // Compiled events are intentionally additive to plan versions 1 and 2. The service also persists their raw SQL in
193
+ // the legacy eventDescriptors field, so older readers can ignore this field and retain equivalent event behavior.
194
+ if (events.length != 0) {
195
+ serialized.events = events;
196
+ }
197
+ return serialized;
144
198
  }
145
199
  export function deserializeSyncPlan(serialized) {
146
- if (serialized.version != 1) {
200
+ const { version } = serialized;
201
+ if (version < 1) {
147
202
  throw new Error('Unknown sync plan version passed to deserializeSyncPlan()');
148
203
  }
204
+ if (version > maxSupportedSyncPlanVersion) {
205
+ throw new Error(`Encountered a sync plan with version ${version}, the maximum supported version is ${maxSupportedSyncPlanVersion}. This can happen when the PowerSync service version is downgraded after deploying Sync Streams, consider upgrading or re-deploying.`);
206
+ }
149
207
  function deserializeTablePattern(pattern) {
150
208
  if (pattern.schema) {
151
209
  return new TablePattern(`${pattern.connection}.${pattern.schema}`, pattern.table);
@@ -210,6 +268,36 @@ export function deserializeSyncPlan(serialized) {
210
268
  defaultLookupScope: source.lookupScope
211
269
  };
212
270
  });
271
+ function deserializeEventRowEvaluator(source) {
272
+ const functions = (tableValuedFunctionsInScope = source.tableValuedFunctions);
273
+ return {
274
+ hashCode: source.hash,
275
+ sourceTable: deserializeTablePattern(source.table),
276
+ tableValuedFunctions: functions,
277
+ filters: source.filters.map(deserializeTableProcessorDataExpr),
278
+ parameters: deserializeParameters(source.partitionBy),
279
+ columns: source.columns.map((column) => {
280
+ if (column == 'star') {
281
+ return 'star';
282
+ }
283
+ return { expr: deserializeTableProcessorDataExpr(column.expr), alias: column.alias };
284
+ })
285
+ };
286
+ }
287
+ if (plan.events != null && !Array.isArray(plan.events)) {
288
+ throw new Error('Compiled sync plan events must be an array.');
289
+ }
290
+ const serializedEvents = plan.events ?? [];
291
+ const events = serializedEvents.map((event) => {
292
+ return {
293
+ name: event.name,
294
+ sourceQueries: event.sourceQueries.map((query) => ({
295
+ sql: query.sql,
296
+ sourceTable: deserializeTablePattern(query.table),
297
+ variants: query.variants.map(deserializeEventRowEvaluator)
298
+ }))
299
+ };
300
+ });
213
301
  function deserializeParameterValue(stages, value) {
214
302
  switch (value.type) {
215
303
  case 'request':
@@ -268,7 +356,9 @@ export function deserializeSyncPlan(serialized) {
268
356
  dataSources,
269
357
  buckets,
270
358
  parameterIndexes,
271
- streams
359
+ streams,
360
+ events
272
361
  };
273
362
  }
363
+ export const maxSupportedSyncPlanVersion = 2;
274
364
  //# sourceMappingURL=serialize.js.map