@powersync/service-core 1.21.0 → 1.22.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 (71) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/dist/api/diagnostics.js +1 -1
  3. package/dist/api/diagnostics.js.map +1 -1
  4. package/dist/auth/RemoteJWKSCollector.js +3 -2
  5. package/dist/auth/RemoteJWKSCollector.js.map +1 -1
  6. package/dist/replication/RelationCache.d.ts +9 -2
  7. package/dist/replication/RelationCache.js +21 -2
  8. package/dist/replication/RelationCache.js.map +1 -1
  9. package/dist/routes/configure-fastify.js +3 -1
  10. package/dist/routes/configure-fastify.js.map +1 -1
  11. package/dist/routes/endpoints/admin.js +9 -5
  12. package/dist/routes/endpoints/admin.js.map +1 -1
  13. package/dist/routes/endpoints/sync-rules.js +1 -1
  14. package/dist/routes/endpoints/sync-rules.js.map +1 -1
  15. package/dist/routes/route-register.d.ts +2 -0
  16. package/dist/routes/route-register.js +65 -3
  17. package/dist/routes/route-register.js.map +1 -1
  18. package/dist/storage/BucketStorageBatch.d.ts +29 -0
  19. package/dist/storage/BucketStorageBatch.js.map +1 -1
  20. package/dist/storage/BucketStorageFactory.d.ts +4 -0
  21. package/dist/storage/BucketStorageFactory.js +1 -1
  22. package/dist/storage/BucketStorageFactory.js.map +1 -1
  23. package/dist/storage/PersistedSyncRulesContent.d.ts +3 -3
  24. package/dist/storage/PersistedSyncRulesContent.js +6 -6
  25. package/dist/storage/PersistedSyncRulesContent.js.map +1 -1
  26. package/dist/storage/SourceEntity.d.ts +8 -1
  27. package/dist/storage/SourceTable.d.ts +29 -8
  28. package/dist/storage/SourceTable.js +38 -12
  29. package/dist/storage/SourceTable.js.map +1 -1
  30. package/dist/storage/SyncRulesBucketStorage.d.ts +26 -13
  31. package/dist/storage/SyncRulesBucketStorage.js.map +1 -1
  32. package/dist/sync/BucketChecksumState.d.ts +4 -4
  33. package/dist/sync/BucketChecksumState.js +1 -1
  34. package/dist/sync/BucketChecksumState.js.map +1 -1
  35. package/dist/sync/sync.d.ts +2 -2
  36. package/dist/sync/sync.js.map +1 -1
  37. package/dist/tracing/PerformanceTracer.d.ts +17 -1
  38. package/dist/tracing/PerformanceTracer.js +3 -0
  39. package/dist/tracing/PerformanceTracer.js.map +1 -1
  40. package/dist/util/util-index.d.ts +1 -0
  41. package/dist/util/util-index.js +1 -0
  42. package/dist/util/util-index.js.map +1 -1
  43. package/dist/util/utils.d.ts +5 -0
  44. package/dist/util/utils.js +7 -0
  45. package/dist/util/utils.js.map +1 -1
  46. package/package.json +4 -4
  47. package/src/api/diagnostics.ts +3 -3
  48. package/src/auth/RemoteJWKSCollector.ts +3 -1
  49. package/src/replication/RelationCache.ts +23 -4
  50. package/src/routes/configure-fastify.ts +8 -1
  51. package/src/routes/endpoints/admin.ts +10 -5
  52. package/src/routes/endpoints/sync-rules.ts +1 -1
  53. package/src/routes/route-register.ts +73 -4
  54. package/src/storage/BucketStorageBatch.ts +32 -0
  55. package/src/storage/BucketStorageFactory.ts +6 -1
  56. package/src/storage/PersistedSyncRulesContent.ts +9 -9
  57. package/src/storage/SourceEntity.ts +9 -1
  58. package/src/storage/SourceTable.ts +53 -19
  59. package/src/storage/SyncRulesBucketStorage.ts +28 -15
  60. package/src/sync/BucketChecksumState.ts +5 -5
  61. package/src/sync/sync.ts +3 -3
  62. package/src/tracing/PerformanceTracer.ts +24 -1
  63. package/src/util/util-index.ts +1 -0
  64. package/src/util/utils.ts +8 -0
  65. package/test/src/auth.test.ts +11 -0
  66. package/test/src/diagnostics.test.ts +10 -6
  67. package/test/src/routes/error-handler.integration.test.ts +275 -0
  68. package/test/src/routes/stream.test.ts +15 -4
  69. package/test/src/storage/SourceTable.test.ts +89 -0
  70. package/test/src/sync/BucketChecksumState.test.ts +25 -17
  71. package/tsconfig.tsbuildinfo +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/util/utils.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/util/utils.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
8
- "version": "1.21.0",
8
+ "version": "1.22.0",
9
9
  "main": "dist/index.js",
10
10
  "license": "FSL-1.1-ALv2",
11
11
  "type": "module",
@@ -33,10 +33,10 @@
33
33
  "uuid": "^14.0.0",
34
34
  "winston": "^3.13.0",
35
35
  "yaml": "^2.8.3",
36
- "@powersync/lib-services-framework": "0.9.4",
36
+ "@powersync/lib-services-framework": "0.9.5",
37
37
  "@powersync/service-jsonbig": "0.17.13",
38
- "@powersync/service-rsocket-router": "0.2.20",
39
- "@powersync/service-sync-rules": "0.36.0",
38
+ "@powersync/service-rsocket-router": "0.2.21",
39
+ "@powersync/service-sync-rules": "0.37.0",
40
40
  "@powersync/service-types": "0.15.2"
41
41
  },
42
42
  "devDependencies": {
@@ -1,5 +1,5 @@
1
1
  import { logger } from '@powersync/lib-services-framework';
2
- import { DEFAULT_TAG, SourceTableInterface, SyncConfigWithErrors } from '@powersync/service-sync-rules';
2
+ import { DEFAULT_TAG, SourceTableRef, SyncConfigWithErrors } from '@powersync/service-sync-rules';
3
3
  import { ReplicationError, SyncRulesStatus, TableInfo } from '@powersync/service-types';
4
4
 
5
5
  import * as storage from '../storage/storage-index.js';
@@ -46,7 +46,7 @@ export async function getSyncRulesStatus(
46
46
  let persisted: storage.PersistedSyncRules;
47
47
  try {
48
48
  persisted = sync_rules.parsed(apiHandler.getParseSyncRulesOptions());
49
- parsed = persisted.sync_rules;
49
+ parsed = persisted.syncConfigWithErrors;
50
50
  } catch (e) {
51
51
  return {
52
52
  content: include_content ? sync_rules.sync_rules_content : undefined,
@@ -116,7 +116,7 @@ export async function getSyncRulesStatus(
116
116
  errors: [{ level: 'fatal', message: 'connection failed', ts: now }]
117
117
  };
118
118
  } else {
119
- const source: SourceTableInterface = {
119
+ const source: SourceTableRef = {
120
120
  connectionTag: tag,
121
121
  schema: pattern.schema,
122
122
  name: pattern.tablePattern
@@ -5,6 +5,7 @@ import fetch from 'node-fetch';
5
5
  import {
6
6
  AuthorizationError,
7
7
  ErrorCode,
8
+ hostnameFromSocketAddress,
8
9
  LookupOptions,
9
10
  makeHostnameLookupFunction,
10
11
  ServiceAssertionError,
@@ -140,7 +141,8 @@ export class RemoteJWKSCollector implements KeyCollector {
140
141
  */
141
142
  resolveAgent(): http.Agent | https.Agent {
142
143
  const lookupOptions = this.options?.lookupOptions ?? { reject_ip_ranges: [] };
143
- const lookup = makeHostnameLookupFunction(this.url.hostname, lookupOptions);
144
+ const hostname = hostnameFromSocketAddress(this.url.hostname);
145
+ const lookup = makeHostnameLookupFunction(hostname, lookupOptions);
144
146
 
145
147
  const options: http.AgentOptions = {
146
148
  lookup
@@ -1,24 +1,43 @@
1
1
  import { SourceTable } from '../storage/SourceTable.js';
2
2
 
3
3
  export class RelationCache<T> {
4
- private cache = new Map<string | number, SourceTable>();
4
+ private cache = new Map<string | number, SourceTable[]>();
5
5
  private idFunction: (item: T | SourceTable) => string | number;
6
6
 
7
7
  constructor(idFunction: (item: T | SourceTable) => string | number) {
8
8
  this.idFunction = idFunction;
9
9
  }
10
10
 
11
+ /**
12
+ * Update a single table in-place - use when the snapshot status of the table has changed.
13
+ */
11
14
  update(table: SourceTable) {
12
15
  const id = this.idFunction(table);
13
- this.cache.set(id, table);
16
+ const existing = this.cache.get(id) ?? [];
17
+ const replacementIndex = existing.findIndex((candidate) => candidate.id == table.id);
18
+ if (replacementIndex == -1) {
19
+ this.cache.set(id, [...existing, table]);
20
+ } else {
21
+ const next = [...existing];
22
+ next[replacementIndex] = table;
23
+ this.cache.set(id, next);
24
+ }
14
25
  }
15
26
 
16
- get(source: T | SourceTable): SourceTable | undefined {
27
+ /**
28
+ * Set the full set of tables for a specific reference.
29
+ */
30
+ updateAll(ref: T, tables: SourceTable[]) {
31
+ const id = this.idFunction(ref);
32
+ this.cache.set(id, tables);
33
+ }
34
+
35
+ getAll(source: T | SourceTable): SourceTable[] | undefined {
17
36
  const id = this.idFunction(source);
18
37
  return this.cache.get(id);
19
38
  }
20
39
 
21
- delete(source: T | SourceTable): boolean {
40
+ delete(source: T): boolean {
22
41
  const id = this.idFunction(source);
23
42
  return this.cache.delete(id);
24
43
  }
@@ -1,6 +1,10 @@
1
1
  import type fastify from 'fastify';
2
2
 
3
- import { registerFastifyNotFoundHandler, registerFastifyRoutes } from './route-register.js';
3
+ import {
4
+ registerFastifyErrorHandler,
5
+ registerFastifyNotFoundHandler,
6
+ registerFastifyRoutes
7
+ } from './route-register.js';
4
8
 
5
9
  import * as system from '../system/system-index.js';
6
10
 
@@ -66,6 +70,9 @@ export function configureFastifyServer(server: fastify.FastifyInstance, options:
66
70
  };
67
71
  };
68
72
 
73
+ // Set on the outer server so both child scopes inherit.
74
+ registerFastifyErrorHandler(server);
75
+
69
76
  /**
70
77
  * Fastify creates an encapsulated context for each `.register` call.
71
78
  * Creating a separate context here to separate the concurrency limits for Admin APIs
@@ -1,5 +1,7 @@
1
+ import * as sqlite from 'node:sqlite';
2
+
1
3
  import { ErrorCode, errors, router, schema } from '@powersync/lib-services-framework';
2
- import { SourceSchema, SqlSyncRules, StaticSchema } from '@powersync/service-sync-rules';
4
+ import { nodeSqlite, SourceSchema, SqlSyncRules, StaticSchema } from '@powersync/service-sync-rules';
3
5
  import { internal_routes } from '@powersync/service-types';
4
6
 
5
7
  import { DEFAULT_HYDRATION_STATE } from '@powersync/service-sync-rules';
@@ -136,7 +138,7 @@ export const reprocess = routeDefinition({
136
138
  // 2. If the source does not set the storage version, this will update it do the current version.
137
139
  // We can consider tweaking this behavior in the future.
138
140
  const new_rules = await activeBucketStorage.updateSyncRules(
139
- storage.updateSyncRulesFromYaml(active.sync_rules.config.content, {
141
+ storage.updateSyncRulesFromYaml(active.syncConfigWithErrors.config.content, {
140
142
  // This sync config already passed validation. But if the config is not valid anymore due
141
143
  // to a service change, we do want to report the error here.
142
144
  validate: true
@@ -176,13 +178,16 @@ class FakeSyncRulesContentForValidation extends storage.PersistedSyncRulesConten
176
178
  parsed(options: storage.ParseSyncRulesOptions): storage.PersistedSyncRules {
177
179
  return {
178
180
  ...this,
179
- sync_rules: SqlSyncRules.fromYaml(this.sync_rules_content, {
181
+ syncConfigWithErrors: SqlSyncRules.fromYaml(this.sync_rules_content, {
180
182
  ...this.apiHandler.getParseSyncRulesOptions(),
181
183
  schema: this.schema
182
184
  }),
183
185
  hydrationState: DEFAULT_HYDRATION_STATE,
184
- hydratedSyncRules() {
185
- return this.sync_rules.config.hydrate({ hydrationState: DEFAULT_HYDRATION_STATE });
186
+ hydratedSyncConfig() {
187
+ return this.syncConfigWithErrors.config.hydrate({
188
+ hydrationState: DEFAULT_HYDRATION_STATE,
189
+ sqlite: nodeSqlite(sqlite)
190
+ });
186
191
  }
187
192
  };
188
193
  }
@@ -167,7 +167,7 @@ export const reprocessSyncRules = routeDefinition({
167
167
  }
168
168
 
169
169
  const new_rules = await activeBucketStorage.updateSyncRules(
170
- updateSyncRulesFromYaml(sync_rules.sync_rules.config.content, {
170
+ updateSyncRulesFromYaml(sync_rules.syncConfigWithErrors.config.content, {
171
171
  // This sync config already passed validation. But if the rules are not valid anymore due
172
172
  // to a service change, we do want to report the error here.
173
173
  validate: true
@@ -1,7 +1,8 @@
1
1
  import type fastify from 'fastify';
2
+ import * as zlib from 'node:zlib';
2
3
  import * as uuid from 'uuid';
3
4
 
4
- import { errors, HTTPMethod, logger, RouteNotFound, router, ServiceError } from '@powersync/lib-services-framework';
5
+ import { errors, HTTPMethod, logger, RouteNotFound, router } from '@powersync/lib-services-framework';
5
6
  import { FastifyReply } from 'fastify';
6
7
  import { Context, ContextProvider, RequestEndpoint, RequestEndpointHandlerPayload } from './router.js';
7
8
 
@@ -105,18 +106,86 @@ export function registerFastifyNotFoundHandler(app: fastify.FastifyInstance) {
105
106
  });
106
107
  }
107
108
 
108
- function serviceErrorToResponse(error: ServiceError): router.RouterResponse {
109
+ /** Registers a custom error handler that emits service-error JSON honouring any negotiated `Content-Encoding`. */
110
+ export function registerFastifyErrorHandler(app: fastify.FastifyInstance) {
111
+ app.setErrorHandler<Error>(async (error, _request, reply) => {
112
+ if (reply.raw.headersSent) {
113
+ // Headers already flushed - reply.code/header would throw ERR_HTTP_HEADERS_SENT.
114
+ reply.raw.destroy(error);
115
+ return;
116
+ }
117
+
118
+ const { status, data } = fastifyErrorToResponse(error);
119
+ if (status >= 500) {
120
+ logger.error('Request failed', error);
121
+ } else {
122
+ logger.warn(`Request failed: ${error.message}`, {
123
+ status,
124
+ code: data.error.code
125
+ });
126
+ }
127
+
128
+ const json = JSON.stringify(data);
129
+
130
+ // Fastify's default handler leaves `content-encoding` intact when it rewrites the body.
131
+ const encoding = reply.getHeader('content-encoding');
132
+ let body: string | Buffer = json;
133
+ if (encoding === 'gzip') {
134
+ body = zlib.gzipSync(json);
135
+ } else if (encoding === 'zstd') {
136
+ body = zlib.zstdCompressSync(json);
137
+ } else {
138
+ reply.removeHeader('content-encoding');
139
+ }
140
+
141
+ reply
142
+ .code(status)
143
+ .header('content-type', 'application/json; charset=utf-8')
144
+ .header('content-length', Buffer.byteLength(body))
145
+ .send(body);
146
+ });
147
+ }
148
+
149
+ type ErrorResponseData = { error: errors.ErrorData };
150
+
151
+ function serviceErrorToResponse(serviceError: errors.ServiceError): router.RouterResponse<ErrorResponseData> {
109
152
  return new router.RouterResponse({
110
- status: error.errorData.status || 500,
153
+ status: serviceError.errorData.status || 500,
111
154
  headers: {
112
155
  'Content-Type': 'application/json'
113
156
  },
114
157
  data: {
115
- error: error.errorData
158
+ error: serviceError.errorData
116
159
  }
117
160
  });
118
161
  }
119
162
 
163
+ function fastifyErrorToResponse(error: any): router.RouterResponse<ErrorResponseData> {
164
+ // Preserve 4xx status from Fastify built-ins (validation, invalid JSON body, etc.) instead of collapsing to 500.
165
+ if (
166
+ typeof error?.statusCode === 'number' &&
167
+ error.statusCode >= 400 &&
168
+ error.statusCode < 500 &&
169
+ typeof error.code === 'string'
170
+ ) {
171
+ return new router.RouterResponse({
172
+ status: error.statusCode,
173
+ headers: {
174
+ 'Content-Type': 'application/json'
175
+ },
176
+ data: {
177
+ error: {
178
+ name: error.name,
179
+ code: error.code,
180
+ status: error.statusCode,
181
+ description: error.message
182
+ }
183
+ }
184
+ });
185
+ }
186
+ return serviceErrorToResponse(errors.asServiceError(error));
187
+ }
188
+
120
189
  async function respond(reply: FastifyReply, response: router.RouterResponse) {
121
190
  Object.keys(response.headers).forEach((key) => {
122
191
  reply.header(key, response.headers[key]);
@@ -5,6 +5,7 @@ import { InternalOpId } from '../util/utils.js';
5
5
  import { ReplicationEventPayload } from './ReplicationEventPayload.js';
6
6
  import { SourceTable, TableSnapshotStatus } from './SourceTable.js';
7
7
  import { BatchedCustomWriteCheckpointOptions } from './storage-index.js';
8
+ import { ResolveTablesOptions, ResolveTablesResult } from './SyncRulesBucketStorage.js';
8
9
 
9
10
  export const DEFAULT_BUCKET_BATCH_COMMIT_OPTIONS: ResolvedBucketBatchCommitOptions = {
10
11
  createEmptyCheckpoints: true,
@@ -22,6 +23,11 @@ export interface BucketStorageBatch extends ObserverClient<BucketBatchStorageLis
22
23
  */
23
24
  last_flushed_op: InternalOpId | null;
24
25
 
26
+ /**
27
+ * True for snapshot batches that should skip rows already present in current_data.
28
+ */
29
+ readonly skipExistingRows: boolean;
30
+
25
31
  /**
26
32
  * Save an op, and potentially flush.
27
33
  *
@@ -91,10 +97,36 @@ export interface BucketStorageBatch extends ObserverClient<BucketBatchStorageLis
91
97
 
92
98
  markTableSnapshotDone(tables: SourceTable[], no_checkpoint_before_lsn?: string): Promise<SourceTable[]>;
93
99
  markTableSnapshotRequired(table: SourceTable): Promise<void>;
100
+
101
+ /**
102
+ * Mark the full replication snapshot as done without validating individual source table snapshot state.
103
+ *
104
+ * This is primarily intended for storage tests and setup helpers that manually construct storage state.
105
+ * Replicators should use `markSnapshotDone()` instead, because that validates that all known source tables
106
+ * have completed snapshotting before allowing checkpoints to be unblocked.
107
+ */
94
108
  markAllSnapshotDone(no_checkpoint_before_lsn: string): Promise<void>;
95
109
 
110
+ /**
111
+ * Mark the full replication snapshot as done after validating that all known source tables have completed snapshotting.
112
+ *
113
+ * Replicators should use this method when completing an initial snapshot. The validation prevents races where
114
+ * new source tables are marked as requiring a snapshot while global snapshot finalization is running.
115
+ *
116
+ * If `throwOnConflict` is false, this will instead return early without throwing if there are source tables that still require snapshotting.
117
+ * Use that only in cases where concurrency is expected, and can automatically retry/continue.
118
+ */
119
+ markSnapshotDone(no_checkpoint_before_lsn: string, options?: { throwOnConflict?: boolean }): Promise<void>;
120
+
96
121
  updateTableProgress(table: SourceTable, progress: Partial<TableSnapshotStatus>): Promise<SourceTable>;
97
122
 
123
+ /**
124
+ * Get the current status for an existing source table without creating or resolving a replacement table.
125
+ */
126
+ getSourceTableStatus(table: SourceTable): Promise<SourceTable | null>;
127
+
128
+ resolveTables(options: ResolveTablesOptions): Promise<ResolveTablesResult>;
129
+
98
130
  /**
99
131
  * Queues the creation of a custom Write Checkpoint. This will be persisted after operations are flushed.
100
132
  */
@@ -170,6 +170,11 @@ export interface UpdateSyncRulesOptions {
170
170
  };
171
171
  lock?: boolean;
172
172
  storageVersion?: number;
173
+
174
+ /**
175
+ * Only relevant if the result is used. This does not affect the persisted config.
176
+ */
177
+ defaultSchema?: string;
173
178
  }
174
179
 
175
180
  export interface SerializedSyncPlan {
@@ -196,7 +201,7 @@ export function updateSyncRulesFromYaml(
196
201
  const config = SqlSyncRules.fromYaml(content, {
197
202
  // No schema-based validation at this point
198
203
  schema: undefined,
199
- defaultSchema: 'not_applicable', // Not needed for validation
204
+ defaultSchema: options?.defaultSchema ?? 'not_applicable', // Not needed for validation
200
205
  throwOnError: options?.validate ?? false
201
206
  });
202
207
 
@@ -5,9 +5,9 @@ import {
5
5
  DEFAULT_HYDRATION_STATE,
6
6
  deserializeSyncPlan,
7
7
  ErrorLocation,
8
- HydratedSyncRules,
8
+ HydratedSyncConfig,
9
9
  HydrationState,
10
- javaScriptExpressionEngine,
10
+ nodeSqlite,
11
11
  PrecompiledSyncConfig,
12
12
  SqlEventDescriptor,
13
13
  SqlSyncRules,
@@ -15,6 +15,7 @@ import {
15
15
  versionedHydrationState,
16
16
  YamlError
17
17
  } from '@powersync/service-sync-rules';
18
+ import * as sqlite from 'node:sqlite';
18
19
  import { SerializedSyncPlan, UpdateSyncRulesOptions } from './BucketStorageFactory.js';
19
20
  import { ReplicationLock } from './ReplicationLock.js';
20
21
  import { STORAGE_VERSION_CONFIG, StorageVersionConfig } from './StorageVersionConfig.js';
@@ -101,7 +102,6 @@ export abstract class PersistedSyncRulesContent implements PersistedSyncRulesCon
101
102
 
102
103
  const precompiled = new PrecompiledSyncConfig(plan, compatibility, eventDefinitions, {
103
104
  defaultSchema: options.defaultSchema,
104
- engine: javaScriptExpressionEngine(compatibility),
105
105
  sourceText: this.sync_rules_content
106
106
  });
107
107
 
@@ -141,10 +141,10 @@ export abstract class PersistedSyncRulesContent implements PersistedSyncRulesCon
141
141
  return {
142
142
  id: this.id,
143
143
  slot_name: this.slot_name,
144
- sync_rules: config,
144
+ syncConfigWithErrors: config,
145
145
  hydrationState,
146
- hydratedSyncRules: () => {
147
- return config.config.hydrate({ hydrationState });
146
+ hydratedSyncConfig: () => {
147
+ return config.config.hydrate({ hydrationState, sqlite: nodeSqlite(sqlite) });
148
148
  }
149
149
  };
150
150
  }
@@ -153,7 +153,7 @@ export abstract class PersistedSyncRulesContent implements PersistedSyncRulesCon
153
153
  // defaultSchema is not relevant for the parsed version here
154
154
  const parsed = this.parsed({ defaultSchema: 'not_applicable' });
155
155
  return {
156
- config: { yaml: this.sync_rules_content, plan: this.compiled_plan, parsed: parsed.sync_rules },
156
+ config: { yaml: this.sync_rules_content, plan: this.compiled_plan, parsed: parsed.syncConfigWithErrors },
157
157
  ...options
158
158
  };
159
159
  }
@@ -163,12 +163,12 @@ export abstract class PersistedSyncRulesContent implements PersistedSyncRulesCon
163
163
 
164
164
  export interface PersistedSyncRules {
165
165
  readonly id: number;
166
- readonly sync_rules: SyncConfigWithErrors;
166
+ readonly syncConfigWithErrors: SyncConfigWithErrors;
167
167
  readonly slot_name: string;
168
168
  /**
169
169
  * For testing only.
170
170
  */
171
171
  readonly hydrationState: HydrationState;
172
172
 
173
- hydratedSyncRules(): HydratedSyncRules;
173
+ hydratedSyncConfig(): HydratedSyncConfig;
174
174
  }
@@ -1,3 +1,5 @@
1
+ import { SourceTableRef } from '@powersync/service-sync-rules';
2
+
1
3
  export interface ColumnDescriptor {
2
4
  name: string;
3
5
  /**
@@ -10,7 +12,7 @@ export interface ColumnDescriptor {
10
12
  typeId?: number;
11
13
  }
12
14
 
13
- export interface SourceEntityDescriptor {
15
+ export interface SourceEntityDescriptor extends SourceTableRef {
14
16
  /**
15
17
  * The internal id of the source entity structure in the database.
16
18
  * If undefined, the schema and name are used as the identifier.
@@ -23,4 +25,10 @@ export interface SourceEntityDescriptor {
23
25
  * The columns that are used to uniquely identify a record in the source entity.
24
26
  */
25
27
  replicaIdColumns: ColumnDescriptor[];
28
+ /**
29
+ * Whether the source always sends complete row data with each operation (e.g. Postgres REPLICA
30
+ * IDENTITY FULL). When true, no current_data copy is needed. Undefined means the source does not
31
+ * report this, in which case we default to keeping a copy.
32
+ */
33
+ sendsCompleteRows?: boolean;
26
34
  }
@@ -1,7 +1,12 @@
1
- import { DEFAULT_TAG } from '@powersync/service-sync-rules';
1
+ import {
2
+ BucketDataSource,
3
+ DEFAULT_TAG,
4
+ ParameterIndexLookupCreator,
5
+ SourceTableRef
6
+ } from '@powersync/service-sync-rules';
2
7
  import { bson } from '../index.js';
3
8
  import * as util from '../util/util-index.js';
4
- import { ColumnDescriptor, SourceEntityDescriptor } from './SourceEntity.js';
9
+ import { ColumnDescriptor } from './SourceEntity.js';
5
10
 
6
11
  /**
7
12
  * Format of the id depends on the bucket storage module. It should be consistent within the module.
@@ -10,12 +15,12 @@ export type SourceTableId = string | bson.ObjectId;
10
15
 
11
16
  export interface SourceTableOptions {
12
17
  id: SourceTableId;
13
- connectionTag: string;
18
+ ref: SourceTableRef;
14
19
  objectId: number | string | undefined;
15
- schema: string;
16
- name: string;
17
20
  replicaIdColumns: ColumnDescriptor[];
18
21
  snapshotComplete: boolean;
22
+ bucketDataSources: BucketDataSource[];
23
+ parameterLookupSources: ParameterIndexLookupCreator[];
19
24
  }
20
25
 
21
26
  export interface TableSnapshotStatus {
@@ -24,7 +29,13 @@ export interface TableSnapshotStatus {
24
29
  lastKey: Uint8Array | null;
25
30
  }
26
31
 
27
- export class SourceTable implements SourceEntityDescriptor {
32
+ /**
33
+ * Represents a resolved source table.
34
+ *
35
+ * There could be multiple of these for the same SourceTableRef.
36
+ * For that reason, we do not implement the SourceTableRef interface, to ensure that the two are not used interchangably.
37
+ */
38
+ export class SourceTable {
28
39
  static readonly DEFAULT_TAG = DEFAULT_TAG;
29
40
 
30
41
  /**
@@ -54,6 +65,19 @@ export class SourceTable implements SourceEntityDescriptor {
54
65
  */
55
66
  public syncEvent = true;
56
67
 
68
+ /**
69
+ * True if raw data should be stored in current_data collection.
70
+ *
71
+ * This is needed when the source sends partial row data (e.g. TOAST values).
72
+ * When REPLICA IDENTITY FULL is configured, complete rows are always sent,
73
+ * so we don't need to store raw data.
74
+ *
75
+ * This value is resolved externally based on table configuration.
76
+ *
77
+ * Defaults to true for tests (conservative approach).
78
+ */
79
+ public storeCurrentData = true;
80
+
57
81
  /**
58
82
  * Always undefined if snapshotComplete = true.
59
83
  *
@@ -71,31 +95,39 @@ export class SourceTable implements SourceEntityDescriptor {
71
95
  return this.options.id;
72
96
  }
73
97
 
74
- get connectionTag() {
75
- return this.options.connectionTag;
76
- }
77
-
78
98
  get objectId() {
79
99
  return this.options.objectId;
80
100
  }
81
101
 
82
102
  get schema() {
83
- return this.options.schema;
103
+ return this.options.ref.schema;
84
104
  }
85
105
  get name() {
86
- return this.options.name;
106
+ return this.options.ref.name;
107
+ }
108
+
109
+ get ref() {
110
+ return this.options.ref;
87
111
  }
88
112
 
89
113
  get replicaIdColumns() {
90
114
  return this.options.replicaIdColumns;
91
115
  }
92
116
 
117
+ get bucketDataSources() {
118
+ return this.options.bucketDataSources;
119
+ }
120
+
121
+ get parameterLookupSources() {
122
+ return this.options.parameterLookupSources;
123
+ }
124
+
93
125
  /**
94
- * Sanitized name of the entity in the format of "{schema}.{entity name}"
95
- * Suitable for safe use in Postgres queries.
126
+ * Sanitized name of the entity in the format of "{schema}.{entity name}".
127
+ * Suitable for safe use in Postgres queries.
96
128
  */
97
129
  get qualifiedName() {
98
- return `${util.escapeIdentifier(this.schema)}.${util.escapeIdentifier(this.name)}`;
130
+ return util.qualifiedName(this.ref);
99
131
  }
100
132
 
101
133
  get syncAny() {
@@ -108,15 +140,17 @@ export class SourceTable implements SourceEntityDescriptor {
108
140
  clone() {
109
141
  const copy = new SourceTable({
110
142
  id: this.id,
111
- connectionTag: this.connectionTag,
143
+ ref: this.options.ref,
112
144
  objectId: this.objectId,
113
- schema: this.schema,
114
- name: this.name,
115
145
  replicaIdColumns: this.replicaIdColumns,
116
- snapshotComplete: this.snapshotComplete
146
+ snapshotComplete: this.snapshotComplete,
147
+ bucketDataSources: this.bucketDataSources,
148
+ parameterLookupSources: this.parameterLookupSources
117
149
  });
118
150
  copy.syncData = this.syncData;
119
151
  copy.syncParameters = this.syncParameters;
152
+ copy.syncEvent = this.syncEvent;
153
+ copy.storeCurrentData = this.storeCurrentData;
120
154
  copy.snapshotStatus = this.snapshotStatus;
121
155
  return copy;
122
156
  }