syncorejs 0.2.1 → 0.2.3

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 (169) hide show
  1. package/README.md +2 -1
  2. package/dist/_vendor/cli/app.d.mts.map +1 -1
  3. package/dist/_vendor/cli/app.mjs +330 -46
  4. package/dist/_vendor/cli/app.mjs.map +1 -1
  5. package/dist/_vendor/cli/context.mjs +27 -9
  6. package/dist/_vendor/cli/context.mjs.map +1 -1
  7. package/dist/_vendor/cli/dev-session.mjs.map +1 -1
  8. package/dist/_vendor/cli/doctor.mjs +513 -46
  9. package/dist/_vendor/cli/doctor.mjs.map +1 -1
  10. package/dist/_vendor/cli/errors.mjs.map +1 -1
  11. package/dist/_vendor/cli/help.mjs.map +1 -1
  12. package/dist/_vendor/cli/index.mjs +9 -2
  13. package/dist/_vendor/cli/index.mjs.map +1 -1
  14. package/dist/_vendor/cli/messages.mjs +5 -4
  15. package/dist/_vendor/cli/messages.mjs.map +1 -1
  16. package/dist/_vendor/cli/preflight.mjs.map +1 -1
  17. package/dist/_vendor/cli/project.mjs +125 -27
  18. package/dist/_vendor/cli/project.mjs.map +1 -1
  19. package/dist/_vendor/cli/render.mjs +57 -9
  20. package/dist/_vendor/cli/render.mjs.map +1 -1
  21. package/dist/_vendor/cli/targets.mjs +4 -3
  22. package/dist/_vendor/cli/targets.mjs.map +1 -1
  23. package/dist/_vendor/core/cli.d.mts +20 -4
  24. package/dist/_vendor/core/cli.d.mts.map +1 -1
  25. package/dist/_vendor/core/cli.mjs +458 -133
  26. package/dist/_vendor/core/cli.mjs.map +1 -1
  27. package/dist/_vendor/core/devtools-auth.mjs +60 -0
  28. package/dist/_vendor/core/devtools-auth.mjs.map +1 -0
  29. package/dist/_vendor/core/index.d.mts +5 -3
  30. package/dist/_vendor/core/index.mjs +22 -2
  31. package/dist/_vendor/core/index.mjs.map +1 -1
  32. package/dist/_vendor/core/runtime/components.d.mts +111 -0
  33. package/dist/_vendor/core/runtime/components.d.mts.map +1 -0
  34. package/dist/_vendor/core/runtime/components.mjs +186 -0
  35. package/dist/_vendor/core/runtime/components.mjs.map +1 -0
  36. package/dist/_vendor/core/runtime/devtools.d.mts +4 -4
  37. package/dist/_vendor/core/runtime/devtools.d.mts.map +1 -1
  38. package/dist/_vendor/core/runtime/devtools.mjs +178 -60
  39. package/dist/_vendor/core/runtime/devtools.mjs.map +1 -1
  40. package/dist/_vendor/core/runtime/functions.d.mts +398 -16
  41. package/dist/_vendor/core/runtime/functions.d.mts.map +1 -1
  42. package/dist/_vendor/core/runtime/functions.mjs +74 -3
  43. package/dist/_vendor/core/runtime/functions.mjs.map +1 -1
  44. package/dist/_vendor/core/runtime/id.d.mts.map +1 -1
  45. package/dist/_vendor/core/runtime/id.mjs.map +1 -1
  46. package/dist/_vendor/core/runtime/internal/engines/devtoolsEngine.mjs +83 -0
  47. package/dist/_vendor/core/runtime/internal/engines/devtoolsEngine.mjs.map +1 -0
  48. package/dist/_vendor/core/runtime/internal/engines/executionEngine.mjs +720 -0
  49. package/dist/_vendor/core/runtime/internal/engines/executionEngine.mjs.map +1 -0
  50. package/dist/_vendor/core/runtime/internal/engines/reactivityEngine.mjs +234 -0
  51. package/dist/_vendor/core/runtime/internal/engines/reactivityEngine.mjs.map +1 -0
  52. package/dist/_vendor/core/runtime/internal/engines/schedulerEngine.mjs +255 -0
  53. package/dist/_vendor/core/runtime/internal/engines/schedulerEngine.mjs.map +1 -0
  54. package/dist/_vendor/core/runtime/internal/engines/schemaEngine.mjs +200 -0
  55. package/dist/_vendor/core/runtime/internal/engines/schemaEngine.mjs.map +1 -0
  56. package/dist/_vendor/core/runtime/internal/engines/shared.mjs +252 -0
  57. package/dist/_vendor/core/runtime/internal/engines/shared.mjs.map +1 -0
  58. package/dist/_vendor/core/runtime/internal/engines/storageEngine.mjs +145 -0
  59. package/dist/_vendor/core/runtime/internal/engines/storageEngine.mjs.map +1 -0
  60. package/dist/_vendor/core/runtime/internal/runtimeKernel.mjs +221 -0
  61. package/dist/_vendor/core/runtime/internal/runtimeKernel.mjs.map +1 -0
  62. package/dist/_vendor/core/runtime/internal/runtimeStatus.mjs +32 -0
  63. package/dist/_vendor/core/runtime/internal/runtimeStatus.mjs.map +1 -0
  64. package/dist/_vendor/core/runtime/internal/systemMeta.mjs +61 -0
  65. package/dist/_vendor/core/runtime/internal/systemMeta.mjs.map +1 -0
  66. package/dist/_vendor/core/runtime/internal/transactionCoordinator.mjs +41 -0
  67. package/dist/_vendor/core/runtime/internal/transactionCoordinator.mjs.map +1 -0
  68. package/dist/_vendor/core/runtime/runtime.d.mts +1187 -202
  69. package/dist/_vendor/core/runtime/runtime.d.mts.map +1 -1
  70. package/dist/_vendor/core/runtime/runtime.mjs +73 -1365
  71. package/dist/_vendor/core/runtime/runtime.mjs.map +1 -1
  72. package/dist/_vendor/core/transport.d.mts +113 -0
  73. package/dist/_vendor/core/transport.d.mts.map +1 -0
  74. package/dist/_vendor/core/transport.mjs +428 -0
  75. package/dist/_vendor/core/transport.mjs.map +1 -0
  76. package/dist/_vendor/devtools-protocol/index.d.ts +187 -4
  77. package/dist/_vendor/devtools-protocol/index.d.ts.map +1 -1
  78. package/dist/_vendor/devtools-protocol/index.js +25 -9
  79. package/dist/_vendor/devtools-protocol/index.js.map +1 -1
  80. package/dist/_vendor/next/config.d.ts +3 -4
  81. package/dist/_vendor/next/config.d.ts.map +1 -1
  82. package/dist/_vendor/next/config.js +37 -19
  83. package/dist/_vendor/next/config.js.map +1 -1
  84. package/dist/_vendor/next/index.d.ts +109 -29
  85. package/dist/_vendor/next/index.d.ts.map +1 -1
  86. package/dist/_vendor/next/index.js +104 -26
  87. package/dist/_vendor/next/index.js.map +1 -1
  88. package/dist/_vendor/platform-expo/index.d.ts +156 -37
  89. package/dist/_vendor/platform-expo/index.d.ts.map +1 -1
  90. package/dist/_vendor/platform-expo/index.js +80 -12
  91. package/dist/_vendor/platform-expo/index.js.map +1 -1
  92. package/dist/_vendor/platform-expo/react.d.ts.map +1 -1
  93. package/dist/_vendor/platform-expo/react.js +11 -10
  94. package/dist/_vendor/platform-expo/react.js.map +1 -1
  95. package/dist/_vendor/platform-expo/web-sqljs-wasm.js +16 -0
  96. package/dist/_vendor/platform-expo/web-sqljs-wasm.js.map +1 -0
  97. package/dist/_vendor/platform-node/index.d.mts +192 -24
  98. package/dist/_vendor/platform-node/index.d.mts.map +1 -1
  99. package/dist/_vendor/platform-node/index.mjs +236 -97
  100. package/dist/_vendor/platform-node/index.mjs.map +1 -1
  101. package/dist/_vendor/platform-node/ipc-react.d.mts.map +1 -1
  102. package/dist/_vendor/platform-node/ipc-react.mjs +15 -2
  103. package/dist/_vendor/platform-node/ipc-react.mjs.map +1 -1
  104. package/dist/_vendor/platform-node/ipc.d.mts +11 -35
  105. package/dist/_vendor/platform-node/ipc.d.mts.map +1 -1
  106. package/dist/_vendor/platform-node/ipc.mjs +3 -273
  107. package/dist/_vendor/platform-node/ipc.mjs.map +1 -1
  108. package/dist/_vendor/platform-web/external-change.d.ts +43 -1
  109. package/dist/_vendor/platform-web/external-change.d.ts.map +1 -1
  110. package/dist/_vendor/platform-web/external-change.js +32 -1
  111. package/dist/_vendor/platform-web/external-change.js.map +1 -1
  112. package/dist/_vendor/platform-web/index.d.ts +323 -51
  113. package/dist/_vendor/platform-web/index.d.ts.map +1 -1
  114. package/dist/_vendor/platform-web/index.js +233 -30
  115. package/dist/_vendor/platform-web/index.js.map +1 -1
  116. package/dist/_vendor/platform-web/indexeddb.d.ts +12 -0
  117. package/dist/_vendor/platform-web/indexeddb.d.ts.map +1 -1
  118. package/dist/_vendor/platform-web/indexeddb.js +10 -0
  119. package/dist/_vendor/platform-web/indexeddb.js.map +1 -1
  120. package/dist/_vendor/platform-web/opfs.d.ts +13 -0
  121. package/dist/_vendor/platform-web/opfs.d.ts.map +1 -1
  122. package/dist/_vendor/platform-web/opfs.js +12 -0
  123. package/dist/_vendor/platform-web/opfs.js.map +1 -1
  124. package/dist/_vendor/platform-web/persistence.d.ts +54 -0
  125. package/dist/_vendor/platform-web/persistence.d.ts.map +1 -1
  126. package/dist/_vendor/platform-web/persistence.js +15 -0
  127. package/dist/_vendor/platform-web/persistence.js.map +1 -1
  128. package/dist/_vendor/platform-web/react.d.ts +1 -2
  129. package/dist/_vendor/platform-web/react.d.ts.map +1 -1
  130. package/dist/_vendor/platform-web/react.js +27 -13
  131. package/dist/_vendor/platform-web/react.js.map +1 -1
  132. package/dist/_vendor/platform-web/sqljs.js +10 -1
  133. package/dist/_vendor/platform-web/sqljs.js.map +1 -1
  134. package/dist/_vendor/platform-web/web-sqljs-wasm.js +8 -0
  135. package/dist/_vendor/platform-web/web-sqljs-wasm.js.map +1 -0
  136. package/dist/_vendor/platform-web/worker.d.ts +71 -44
  137. package/dist/_vendor/platform-web/worker.d.ts.map +1 -1
  138. package/dist/_vendor/platform-web/worker.js +40 -271
  139. package/dist/_vendor/platform-web/worker.js.map +1 -1
  140. package/dist/_vendor/react/index.d.ts +222 -23
  141. package/dist/_vendor/react/index.d.ts.map +1 -1
  142. package/dist/_vendor/react/index.js +476 -63
  143. package/dist/_vendor/react/index.js.map +1 -1
  144. package/dist/_vendor/schema/definition.d.ts +151 -37
  145. package/dist/_vendor/schema/definition.d.ts.map +1 -1
  146. package/dist/_vendor/schema/definition.js +102 -20
  147. package/dist/_vendor/schema/definition.js.map +1 -1
  148. package/dist/_vendor/schema/index.d.ts +4 -4
  149. package/dist/_vendor/schema/index.js +2 -2
  150. package/dist/_vendor/schema/planner.d.ts +19 -2
  151. package/dist/_vendor/schema/planner.d.ts.map +1 -1
  152. package/dist/_vendor/schema/planner.js +79 -3
  153. package/dist/_vendor/schema/planner.js.map +1 -1
  154. package/dist/_vendor/schema/validators.d.ts +279 -83
  155. package/dist/_vendor/schema/validators.d.ts.map +1 -1
  156. package/dist/_vendor/schema/validators.js +330 -38
  157. package/dist/_vendor/schema/validators.js.map +1 -1
  158. package/dist/_vendor/svelte/index.d.ts +245 -19
  159. package/dist/_vendor/svelte/index.d.ts.map +1 -1
  160. package/dist/_vendor/svelte/index.js +443 -20
  161. package/dist/_vendor/svelte/index.js.map +1 -1
  162. package/dist/browser.d.ts.map +1 -1
  163. package/dist/cli.js +3 -1
  164. package/dist/cli.js.map +1 -1
  165. package/dist/components.d.ts +2 -0
  166. package/dist/components.js +2 -0
  167. package/dist/index.d.ts +3 -2
  168. package/dist/index.js +2 -1
  169. package/package.json +29 -21
@@ -2,9 +2,20 @@ import { ValidatorDescription } from "./validators.js";
2
2
  import { SearchIndexDefinition, SyncoreSchema, SyncoreSchemaDefinition } from "./definition.js";
3
3
 
4
4
  //#region src/planner.d.ts
5
+ interface TableFieldSnapshot {
6
+ name: string;
7
+ validator: ValidatorDescription;
8
+ storage: ValidatorDescription;
9
+ optional: boolean;
10
+ }
5
11
  interface TableSnapshot {
6
12
  name: string;
13
+ displayName?: string;
14
+ componentPath?: string;
15
+ componentName?: string;
7
16
  validator: ValidatorDescription;
17
+ fieldPaths: string[];
18
+ fields: TableFieldSnapshot[];
8
19
  indexes: Array<{
9
20
  name: string;
10
21
  fields: string[];
@@ -16,13 +27,19 @@ interface TableSnapshot {
16
27
  }>;
17
28
  }
18
29
  interface SchemaSnapshot {
19
- version: 1;
30
+ formatVersion: 3;
31
+ plannerVersion: 2;
32
+ runtimeVersion?: string;
20
33
  tables: TableSnapshot[];
21
34
  hash: string;
22
35
  }
23
36
  interface SchemaMigrationPlan {
37
+ formatVersion: 3;
38
+ plannerVersion: 2;
24
39
  previousHash: string | null;
25
40
  nextHash: string;
41
+ fromSchemaHash: string | null;
42
+ toSchemaHash: string;
26
43
  statements: string[];
27
44
  warnings: string[];
28
45
  destructiveChanges: string[];
@@ -38,5 +55,5 @@ declare function renderCreateIndexStatement(tableName: string, indexName: string
38
55
  declare function renderCreateSearchIndexStatement(tableName: string, searchIndex: SearchIndexDefinition | TableSnapshot["searchIndexes"][number]): string;
39
56
  declare function searchIndexTableName(tableName: string, indexName: string): string;
40
57
  //#endregion
41
- export { SchemaMigrationPlan, SchemaSnapshot, TableSnapshot, createSchemaSnapshot, diffSchemaSnapshots, parseSchemaSnapshot, renderCreateIndexStatement, renderCreateSearchIndexStatement, renderCreateTableStatement, renderMigrationSql, searchIndexTableName };
58
+ export { SchemaMigrationPlan, SchemaSnapshot, TableFieldSnapshot, TableSnapshot, createSchemaSnapshot, diffSchemaSnapshots, parseSchemaSnapshot, renderCreateIndexStatement, renderCreateSearchIndexStatement, renderCreateTableStatement, renderMigrationSql, searchIndexTableName };
42
59
  //# sourceMappingURL=planner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"planner.d.ts","names":[],"sources":["../src/planner.ts"],"mappings":";;;;UAUiB,aAAA;EACf,IAAA;EACA,SAAA,EAAW,oBAAA;EACX,OAAA,EAAS,KAAA;IACP,IAAA;IACA,MAAA;EAAA;EAEF,aAAA,EAAe,KAAA;IACb,IAAA;IACA,WAAA;IACA,YAAA;EAAA;AAAA;AAAA,UAIa,cAAA;EACf,OAAA;EACA,MAAA,EAAQ,aAAA;EACR,IAAA;AAAA;AAAA,UAGe,mBAAA;EACf,YAAA;EACA,QAAA;EACA,UAAA;EACA,QAAA;EACA,kBAAA;AAAA;AAAA,iBAGc,oBAAA,iBAAqC,uBAAA,CAAA,CACnD,MAAA,EAAQ,aAAA,CAAc,OAAA,IACrB,cAAA;AAAA,iBAoCa,mBAAA,CACd,gBAAA,EAAkB,cAAA,qBAClB,YAAA,EAAc,cAAA,GACb,mBAAA;AAAA,iBAqGa,kBAAA,CACd,IAAA,EAAM,mBAAA,EACN,OAAA;EAAY,KAAA;AAAA;AAAA,iBA+BE,mBAAA,CAAoB,MAAA,WAAiB,cAAA;AAAA,iBAQrC,0BAAA,CAA2B,SAAA;AAAA,iBAS3B,0BAAA,CACd,SAAA,UACA,SAAA,UACA,MAAA;AAAA,iBAUc,gCAAA,CACd,SAAA,UACA,WAAA,EAAa,qBAAA,GAAwB,aAAA;AAAA,iBAOvB,oBAAA,CAAqB,SAAA,UAAmB,SAAA"}
1
+ {"version":3,"file":"planner.d.ts","names":[],"sources":["../src/planner.ts"],"mappings":";;;;UAUiB,kBAAA;EACf,IAAA;EACA,SAAA,EAAW,oBAAA;EACX,OAAA,EAAS,oBAAoB;EAC7B,QAAA;AAAA;AAAA,UAGe,aAAA;EACf,IAAA;EACA,WAAA;EACA,aAAA;EACA,aAAA;EACA,SAAA,EAAW,oBAAA;EACX,UAAA;EACA,MAAA,EAAQ,kBAAA;EACR,OAAA,EAAS,KAAA;IACP,IAAA;IACA,MAAA;EAAA;EAEF,aAAA,EAAe,KAAA;IACb,IAAA;IACA,WAAA;IACA,YAAA;EAAA;AAAA;AAAA,UAIa,cAAA;EACf,aAAA;EACA,cAAA;EACA,cAAA;EACA,MAAA,EAAQ,aAAa;EACrB,IAAA;AAAA;AAAA,UAGe,mBAAA;EACf,aAAA;EACA,cAAA;EACA,YAAA;EACA,QAAA;EACA,cAAA;EACA,YAAA;EACA,UAAA;EACA,QAAA;EACA,kBAAA;AAAA;AAAA,iBAGc,oBAAA,iBAAqC,uBAAA,CAAA,CACnD,MAAA,EAAQ,aAAA,CAAc,OAAA,IACrB,cAAA;AAAA,iBA+Ca,mBAAA,CACd,gBAAA,EAAkB,cAAA,qBAClB,YAAA,EAAc,cAAA,GACb,mBAAA;AAAA,iBAyGa,kBAAA,CACd,IAAA,EAAM,mBAAmB,EACzB,OAAA;EAAY,KAAA;AAAA;AAAA,iBAiCE,mBAAA,CAAoB,MAAA,WAAiB,cAAc;AAAA,iBAwEnD,0BAAA,CAA2B,SAAiB;AAAA,iBAS5C,0BAAA,CACd,SAAA,UACA,SAAA,UACA,MAAA;AAAA,iBAUc,gCAAA,CACd,SAAA,UACA,WAAA,EAAa,qBAAA,GAAwB,aAAa;AAAA,iBAOpC,oBAAA,CAAqB,SAAA,UAAmB,SAAiB"}
@@ -2,12 +2,19 @@ import { describeValidator } from "./validators.js";
2
2
  //#region src/planner.ts
3
3
  function createSchemaSnapshot(schema) {
4
4
  const base = {
5
- version: 1,
5
+ formatVersion: 3,
6
+ plannerVersion: 2,
6
7
  tables: schema.tableNames().sort((left, right) => left.localeCompare(right)).map((tableName) => {
7
8
  const table = schema.getTable(tableName);
9
+ const validator = describeValidator(table.validator);
8
10
  return {
9
11
  name: tableName,
10
- validator: describeValidator(table.validator),
12
+ ...table.options.tableName ? { displayName: table.options.tableName } : {},
13
+ ...table.options.componentPath ? { componentPath: table.options.componentPath } : {},
14
+ ...table.options.componentName ? { componentName: table.options.componentName } : {},
15
+ validator,
16
+ fieldPaths: extractFieldPaths(validator),
17
+ fields: extractTopLevelFields(validator),
11
18
  indexes: table.indexes.map((index) => ({
12
19
  name: index.name,
13
20
  fields: [...index.fields]
@@ -65,8 +72,12 @@ function diffSchemaSnapshots(previousSnapshot, nextSnapshot) {
65
72
  }
66
73
  for (const previousTable of previousSnapshot?.tables ?? []) if (!nextTables.has(previousTable.name)) destructiveChanges.push(`Table "${previousTable.name}" was removed and requires a manual migration.`);
67
74
  return {
75
+ formatVersion: 3,
76
+ plannerVersion: 2,
68
77
  previousHash: previousSnapshot?.hash ?? null,
69
78
  nextHash: nextSnapshot.hash,
79
+ fromSchemaHash: previousSnapshot?.hash ?? null,
80
+ toSchemaHash: nextSnapshot.hash,
70
81
  statements,
71
82
  warnings,
72
83
  destructiveChanges
@@ -75,6 +86,8 @@ function diffSchemaSnapshots(previousSnapshot, nextSnapshot) {
75
86
  function renderMigrationSql(plan, options) {
76
87
  const lines = [];
77
88
  lines.push(`-- ${options?.title ?? "Syncore migration"}`);
89
+ lines.push(`-- format-version: ${plan.formatVersion}`);
90
+ lines.push(`-- planner-version: ${plan.plannerVersion}`);
78
91
  lines.push(`-- previous: ${plan.previousHash ?? "none"}`);
79
92
  lines.push(`-- next: ${plan.nextHash}`);
80
93
  for (const warning of plan.warnings) lines.push(`-- warning: ${warning}`);
@@ -90,8 +103,32 @@ function renderMigrationSql(plan, options) {
90
103
  }
91
104
  function parseSchemaSnapshot(source) {
92
105
  const parsed = JSON.parse(source);
106
+ if ("formatVersion" in parsed && parsed.formatVersion === 3) {
107
+ if (parsed.plannerVersion !== 2 || !Array.isArray(parsed.tables) || typeof parsed.hash !== "string") throw new Error("Invalid schema snapshot file.");
108
+ return parsed;
109
+ }
110
+ if ("formatVersion" in parsed && parsed.formatVersion === 2) return {
111
+ formatVersion: 3,
112
+ plannerVersion: 2,
113
+ ...parsed.runtimeVersion ? { runtimeVersion: parsed.runtimeVersion } : {},
114
+ tables: parsed.tables.map((table) => ({
115
+ ...table,
116
+ fieldPaths: extractFieldPaths(table.validator),
117
+ fields: extractTopLevelFields(table.validator)
118
+ })),
119
+ hash: parsed.hash
120
+ };
93
121
  if (parsed.version !== 1 || !Array.isArray(parsed.tables) || typeof parsed.hash !== "string") throw new Error("Invalid schema snapshot file.");
94
- return parsed;
122
+ return {
123
+ formatVersion: 3,
124
+ plannerVersion: 2,
125
+ tables: parsed.tables.map((table) => ({
126
+ ...table,
127
+ fieldPaths: table.fieldPaths ?? extractFieldPaths(table.validator),
128
+ fields: table.fields ?? extractTopLevelFields(table.validator)
129
+ })),
130
+ hash: parsed.hash
131
+ };
95
132
  }
96
133
  function renderCreateTableStatement(tableName) {
97
134
  return `
@@ -125,6 +162,45 @@ function sortValue(value) {
125
162
  if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, nested]) => [key, sortValue(nested)]));
126
163
  return value;
127
164
  }
165
+ function extractFieldPaths(description, prefix = "") {
166
+ switch (description.kind) {
167
+ case "object": return Object.entries(description.shape).flatMap(([key, entry]) => {
168
+ const path = prefix ? `${prefix}.${key}` : key;
169
+ const nested = extractFieldPaths(normalizeObjectFieldEntry(entry).validator, path);
170
+ return nested.length > 0 ? [path, ...nested] : [path];
171
+ });
172
+ case "optional": return extractFieldPaths(description.inner, prefix);
173
+ case "codec": return extractFieldPaths(description.value, prefix);
174
+ case "union": return [...new Set(description.members.flatMap((member) => extractFieldPaths(member, prefix)))];
175
+ default: return [];
176
+ }
177
+ }
178
+ function extractTopLevelFields(description) {
179
+ if (description.kind !== "object") return [];
180
+ return Object.entries(description.shape).map(([name, entry]) => {
181
+ const normalizedEntry = normalizeObjectFieldEntry(entry);
182
+ return {
183
+ name,
184
+ validator: normalizedEntry.validator,
185
+ storage: normalizedEntry.validator.kind === "codec" ? normalizedEntry.validator.storage : normalizedEntry.validator,
186
+ optional: normalizedEntry.optional
187
+ };
188
+ }).sort((left, right) => left.name.localeCompare(right.name));
189
+ }
190
+ function normalizeObjectFieldEntry(entry) {
191
+ if ("validator" in entry) return {
192
+ validator: entry.validator,
193
+ optional: entry.optional ?? false
194
+ };
195
+ if (entry.kind === "optional") return {
196
+ validator: entry.inner,
197
+ optional: true
198
+ };
199
+ return {
200
+ validator: entry,
201
+ optional: false
202
+ };
203
+ }
128
204
  //#endregion
129
205
  export { createSchemaSnapshot, diffSchemaSnapshots, parseSchemaSnapshot, renderCreateIndexStatement, renderCreateSearchIndexStatement, renderCreateTableStatement, renderMigrationSql, searchIndexTableName };
130
206
 
@@ -1 +1 @@
1
- {"version":3,"file":"planner.js","names":[],"sources":["../src/planner.ts"],"sourcesContent":["import {\n type SearchIndexDefinition,\n type SyncoreSchemaDefinition,\n type SyncoreSchema\n} from \"./definition.js\";\nimport {\n describeValidator,\n type ValidatorDescription\n} from \"./validators.js\";\n\nexport interface TableSnapshot {\n name: string;\n validator: ValidatorDescription;\n indexes: Array<{\n name: string;\n fields: string[];\n }>;\n searchIndexes: Array<{\n name: string;\n searchField: string;\n filterFields: string[];\n }>;\n}\n\nexport interface SchemaSnapshot {\n version: 1;\n tables: TableSnapshot[];\n hash: string;\n}\n\nexport interface SchemaMigrationPlan {\n previousHash: string | null;\n nextHash: string;\n statements: string[];\n warnings: string[];\n destructiveChanges: string[];\n}\n\nexport function createSchemaSnapshot<TTables extends SyncoreSchemaDefinition>(\n schema: SyncoreSchema<TTables>\n): SchemaSnapshot {\n const tables = schema\n .tableNames()\n .sort((left, right) => left.localeCompare(right))\n .map((tableName) => {\n const table = schema.getTable(tableName);\n return {\n name: tableName,\n validator: describeValidator(table.validator),\n indexes: table.indexes\n .map((index) => ({\n name: index.name,\n fields: [...index.fields]\n }))\n .sort((left, right) => left.name.localeCompare(right.name)),\n searchIndexes: table.searchIndexes\n .map((index) => ({\n name: index.name,\n searchField: index.searchField,\n filterFields: [...index.filterFields]\n }))\n .sort((left, right) => left.name.localeCompare(right.name))\n };\n });\n\n const base = {\n version: 1 as const,\n tables\n };\n\n return {\n ...base,\n hash: createSchemaHash(base)\n };\n}\n\nexport function diffSchemaSnapshots(\n previousSnapshot: SchemaSnapshot | null | undefined,\n nextSnapshot: SchemaSnapshot\n): SchemaMigrationPlan {\n const statements: string[] = [];\n const warnings: string[] = [];\n const destructiveChanges: string[] = [];\n\n const previousTables = new Map(\n (previousSnapshot?.tables ?? []).map((table) => [table.name, table])\n );\n const nextTables = new Map(nextSnapshot.tables.map((table) => [table.name, table]));\n\n for (const table of nextSnapshot.tables) {\n const previousTable = previousTables.get(table.name);\n if (!previousTable) {\n statements.push(renderCreateTableStatement(table.name));\n for (const index of table.indexes) {\n statements.push(renderCreateIndexStatement(table.name, index.name, index.fields));\n }\n for (const searchIndex of table.searchIndexes) {\n statements.push(renderCreateSearchIndexStatement(table.name, searchIndex));\n }\n continue;\n }\n\n if (stableStringify(previousTable.validator) !== stableStringify(table.validator)) {\n warnings.push(\n `Validator changed for table \"${table.name}\". Existing rows are not rewritten automatically.`\n );\n }\n\n const previousIndexes = new Map(\n previousTable.indexes.map((index) => [index.name, index])\n );\n const nextIndexes = new Map(table.indexes.map((index) => [index.name, index]));\n\n for (const index of table.indexes) {\n const previousIndex = previousIndexes.get(index.name);\n if (!previousIndex) {\n statements.push(renderCreateIndexStatement(table.name, index.name, index.fields));\n continue;\n }\n if (stableStringify(previousIndex.fields) !== stableStringify(index.fields)) {\n destructiveChanges.push(\n `Index \"${table.name}.${index.name}\" changed fields and requires a manual migration.`\n );\n }\n }\n\n for (const previousIndex of previousTable.indexes) {\n if (!nextIndexes.has(previousIndex.name)) {\n destructiveChanges.push(\n `Index \"${table.name}.${previousIndex.name}\" was removed and requires a manual migration.`\n );\n }\n }\n\n const previousSearchIndexes = new Map(\n previousTable.searchIndexes.map((index) => [index.name, index])\n );\n const nextSearchIndexes = new Map(\n table.searchIndexes.map((index) => [index.name, index])\n );\n\n for (const searchIndex of table.searchIndexes) {\n const previousSearchIndex = previousSearchIndexes.get(searchIndex.name);\n if (!previousSearchIndex) {\n statements.push(renderCreateSearchIndexStatement(table.name, searchIndex));\n continue;\n }\n if (stableStringify(previousSearchIndex) !== stableStringify(searchIndex)) {\n destructiveChanges.push(\n `Search index \"${table.name}.${searchIndex.name}\" changed and requires a manual migration.`\n );\n }\n }\n\n for (const previousSearchIndex of previousTable.searchIndexes) {\n if (!nextSearchIndexes.has(previousSearchIndex.name)) {\n destructiveChanges.push(\n `Search index \"${table.name}.${previousSearchIndex.name}\" was removed and requires a manual migration.`\n );\n }\n }\n }\n\n for (const previousTable of previousSnapshot?.tables ?? []) {\n if (!nextTables.has(previousTable.name)) {\n destructiveChanges.push(\n `Table \"${previousTable.name}\" was removed and requires a manual migration.`\n );\n }\n }\n\n return {\n previousHash: previousSnapshot?.hash ?? null,\n nextHash: nextSnapshot.hash,\n statements,\n warnings,\n destructiveChanges\n };\n}\n\nexport function renderMigrationSql(\n plan: SchemaMigrationPlan,\n options?: { title?: string }\n): string {\n const lines: string[] = [];\n\n lines.push(`-- ${options?.title ?? \"Syncore migration\"}`);\n lines.push(`-- previous: ${plan.previousHash ?? \"none\"}`);\n lines.push(`-- next: ${plan.nextHash}`);\n\n for (const warning of plan.warnings) {\n lines.push(`-- warning: ${warning}`);\n }\n\n if (plan.destructiveChanges.length > 0) {\n for (const destructiveChange of plan.destructiveChanges) {\n lines.push(`-- destructive: ${destructiveChange}`);\n }\n }\n\n if (plan.statements.length > 0) {\n lines.push(\"\");\n for (const statement of plan.statements) {\n lines.push(statement);\n }\n } else {\n lines.push(\"\");\n lines.push(\"-- no-op\");\n }\n\n return `${lines.join(\"\\n\")}\\n`;\n}\n\nexport function parseSchemaSnapshot(source: string): SchemaSnapshot {\n const parsed = JSON.parse(source) as SchemaSnapshot;\n if (parsed.version !== 1 || !Array.isArray(parsed.tables) || typeof parsed.hash !== \"string\") {\n throw new Error(\"Invalid schema snapshot file.\");\n }\n return parsed;\n}\n\nexport function renderCreateTableStatement(tableName: string): string {\n return `\nCREATE TABLE IF NOT EXISTS ${quoteIdentifier(tableName)} (\n _id TEXT PRIMARY KEY,\n _creationTime INTEGER NOT NULL,\n _json TEXT NOT NULL\n);`.trim();\n}\n\nexport function renderCreateIndexStatement(\n tableName: string,\n indexName: string,\n fields: string[]\n): string {\n const expressions = fields\n .map((field) => `json_extract(_json, '$.${field}')`)\n .join(\", \");\n return `CREATE INDEX IF NOT EXISTS ${quoteIdentifier(\n `idx_${tableName}_${indexName}`\n )} ON ${quoteIdentifier(tableName)} (${expressions});`;\n}\n\nexport function renderCreateSearchIndexStatement(\n tableName: string,\n searchIndex: SearchIndexDefinition | TableSnapshot[\"searchIndexes\"][number]\n): string {\n return `CREATE VIRTUAL TABLE IF NOT EXISTS ${quoteIdentifier(\n searchIndexTableName(tableName, searchIndex.name)\n )} USING fts5(_id UNINDEXED, search_value);`;\n}\n\nexport function searchIndexTableName(tableName: string, indexName: string): string {\n return `fts_${tableName}_${indexName}`;\n}\n\nfunction createSchemaHash(value: Omit<SchemaSnapshot, \"hash\">): string {\n return stableStringify(value);\n}\n\nfunction quoteIdentifier(identifier: string): string {\n return `\"${identifier.replaceAll('\"', '\"\"')}\"`;\n}\n\nfunction stableStringify(value: unknown): string {\n return JSON.stringify(sortValue(value));\n}\n\nfunction sortValue(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map(sortValue);\n }\n if (value && typeof value === \"object\") {\n return Object.fromEntries(\n Object.entries(value as Record<string, unknown>)\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([key, nested]) => [key, sortValue(nested)])\n );\n }\n return value;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"planner.js","names":[],"sources":["../src/planner.ts"],"sourcesContent":["import {\n type SearchIndexDefinition,\n type SyncoreSchemaDefinition,\n type SyncoreSchema\n} from \"./definition.js\";\nimport {\n describeValidator,\n type ValidatorDescription\n} from \"./validators.js\";\n\nexport interface TableFieldSnapshot {\n name: string;\n validator: ValidatorDescription;\n storage: ValidatorDescription;\n optional: boolean;\n}\n\nexport interface TableSnapshot {\n name: string;\n displayName?: string;\n componentPath?: string;\n componentName?: string;\n validator: ValidatorDescription;\n fieldPaths: string[];\n fields: TableFieldSnapshot[];\n indexes: Array<{\n name: string;\n fields: string[];\n }>;\n searchIndexes: Array<{\n name: string;\n searchField: string;\n filterFields: string[];\n }>;\n}\n\nexport interface SchemaSnapshot {\n formatVersion: 3;\n plannerVersion: 2;\n runtimeVersion?: string;\n tables: TableSnapshot[];\n hash: string;\n}\n\nexport interface SchemaMigrationPlan {\n formatVersion: 3;\n plannerVersion: 2;\n previousHash: string | null;\n nextHash: string;\n fromSchemaHash: string | null;\n toSchemaHash: string;\n statements: string[];\n warnings: string[];\n destructiveChanges: string[];\n}\n\nexport function createSchemaSnapshot<TTables extends SyncoreSchemaDefinition>(\n schema: SyncoreSchema<TTables>\n): SchemaSnapshot {\n const tables = schema\n .tableNames()\n .sort((left, right) => left.localeCompare(right))\n .map((tableName) => {\n const table = schema.getTable(tableName);\n const validator = describeValidator(table.validator);\n return {\n name: tableName,\n ...(table.options.tableName ? { displayName: table.options.tableName } : {}),\n ...(table.options.componentPath\n ? { componentPath: table.options.componentPath }\n : {}),\n ...(table.options.componentName\n ? { componentName: table.options.componentName }\n : {}),\n validator,\n fieldPaths: extractFieldPaths(validator),\n fields: extractTopLevelFields(validator),\n indexes: table.indexes\n .map((index) => ({\n name: index.name,\n fields: [...index.fields]\n }))\n .sort((left, right) => left.name.localeCompare(right.name)),\n searchIndexes: table.searchIndexes\n .map((index) => ({\n name: index.name,\n searchField: index.searchField,\n filterFields: [...index.filterFields]\n }))\n .sort((left, right) => left.name.localeCompare(right.name))\n };\n });\n\n const base = {\n formatVersion: 3 as const,\n plannerVersion: 2 as const,\n tables\n };\n\n return {\n ...base,\n hash: createSchemaHash(base)\n };\n}\n\nexport function diffSchemaSnapshots(\n previousSnapshot: SchemaSnapshot | null | undefined,\n nextSnapshot: SchemaSnapshot\n): SchemaMigrationPlan {\n const statements: string[] = [];\n const warnings: string[] = [];\n const destructiveChanges: string[] = [];\n\n const previousTables = new Map(\n (previousSnapshot?.tables ?? []).map((table) => [table.name, table])\n );\n const nextTables = new Map(nextSnapshot.tables.map((table) => [table.name, table]));\n\n for (const table of nextSnapshot.tables) {\n const previousTable = previousTables.get(table.name);\n if (!previousTable) {\n statements.push(renderCreateTableStatement(table.name));\n for (const index of table.indexes) {\n statements.push(renderCreateIndexStatement(table.name, index.name, index.fields));\n }\n for (const searchIndex of table.searchIndexes) {\n statements.push(renderCreateSearchIndexStatement(table.name, searchIndex));\n }\n continue;\n }\n\n if (stableStringify(previousTable.validator) !== stableStringify(table.validator)) {\n warnings.push(\n `Validator changed for table \"${table.name}\". Existing rows are not rewritten automatically.`\n );\n }\n\n const previousIndexes = new Map(\n previousTable.indexes.map((index) => [index.name, index])\n );\n const nextIndexes = new Map(table.indexes.map((index) => [index.name, index]));\n\n for (const index of table.indexes) {\n const previousIndex = previousIndexes.get(index.name);\n if (!previousIndex) {\n statements.push(renderCreateIndexStatement(table.name, index.name, index.fields));\n continue;\n }\n if (stableStringify(previousIndex.fields) !== stableStringify(index.fields)) {\n destructiveChanges.push(\n `Index \"${table.name}.${index.name}\" changed fields and requires a manual migration.`\n );\n }\n }\n\n for (const previousIndex of previousTable.indexes) {\n if (!nextIndexes.has(previousIndex.name)) {\n destructiveChanges.push(\n `Index \"${table.name}.${previousIndex.name}\" was removed and requires a manual migration.`\n );\n }\n }\n\n const previousSearchIndexes = new Map(\n previousTable.searchIndexes.map((index) => [index.name, index])\n );\n const nextSearchIndexes = new Map(\n table.searchIndexes.map((index) => [index.name, index])\n );\n\n for (const searchIndex of table.searchIndexes) {\n const previousSearchIndex = previousSearchIndexes.get(searchIndex.name);\n if (!previousSearchIndex) {\n statements.push(renderCreateSearchIndexStatement(table.name, searchIndex));\n continue;\n }\n if (stableStringify(previousSearchIndex) !== stableStringify(searchIndex)) {\n destructiveChanges.push(\n `Search index \"${table.name}.${searchIndex.name}\" changed and requires a manual migration.`\n );\n }\n }\n\n for (const previousSearchIndex of previousTable.searchIndexes) {\n if (!nextSearchIndexes.has(previousSearchIndex.name)) {\n destructiveChanges.push(\n `Search index \"${table.name}.${previousSearchIndex.name}\" was removed and requires a manual migration.`\n );\n }\n }\n }\n\n for (const previousTable of previousSnapshot?.tables ?? []) {\n if (!nextTables.has(previousTable.name)) {\n destructiveChanges.push(\n `Table \"${previousTable.name}\" was removed and requires a manual migration.`\n );\n }\n }\n\n return {\n formatVersion: 3,\n plannerVersion: 2,\n previousHash: previousSnapshot?.hash ?? null,\n nextHash: nextSnapshot.hash,\n fromSchemaHash: previousSnapshot?.hash ?? null,\n toSchemaHash: nextSnapshot.hash,\n statements,\n warnings,\n destructiveChanges\n };\n}\n\nexport function renderMigrationSql(\n plan: SchemaMigrationPlan,\n options?: { title?: string }\n): string {\n const lines: string[] = [];\n\n lines.push(`-- ${options?.title ?? \"Syncore migration\"}`);\n lines.push(`-- format-version: ${plan.formatVersion}`);\n lines.push(`-- planner-version: ${plan.plannerVersion}`);\n lines.push(`-- previous: ${plan.previousHash ?? \"none\"}`);\n lines.push(`-- next: ${plan.nextHash}`);\n\n for (const warning of plan.warnings) {\n lines.push(`-- warning: ${warning}`);\n }\n\n if (plan.destructiveChanges.length > 0) {\n for (const destructiveChange of plan.destructiveChanges) {\n lines.push(`-- destructive: ${destructiveChange}`);\n }\n }\n\n if (plan.statements.length > 0) {\n lines.push(\"\");\n for (const statement of plan.statements) {\n lines.push(statement);\n }\n } else {\n lines.push(\"\");\n lines.push(\"-- no-op\");\n }\n\n return `${lines.join(\"\\n\")}\\n`;\n}\n\nexport function parseSchemaSnapshot(source: string): SchemaSnapshot {\n const parsed = JSON.parse(source) as\n | SchemaSnapshot\n | {\n formatVersion: 2;\n plannerVersion: 1;\n runtimeVersion?: string;\n tables: Array<{\n name: string;\n displayName?: string;\n componentPath?: string;\n componentName?: string;\n validator: ValidatorDescription;\n indexes: Array<{ name: string; fields: string[] }>;\n searchIndexes: Array<{\n name: string;\n searchField: string;\n filterFields: string[];\n }>;\n }>;\n hash: string;\n }\n | {\n version: 1;\n tables: TableSnapshot[];\n hash: string;\n };\n\n if (\"formatVersion\" in parsed && parsed.formatVersion === 3) {\n if (\n parsed.plannerVersion !== 2 ||\n !Array.isArray(parsed.tables) ||\n typeof parsed.hash !== \"string\"\n ) {\n throw new Error(\"Invalid schema snapshot file.\");\n }\n return parsed;\n }\n\n if (\"formatVersion\" in parsed && parsed.formatVersion === 2) {\n return {\n formatVersion: 3,\n plannerVersion: 2,\n ...(parsed.runtimeVersion ? { runtimeVersion: parsed.runtimeVersion } : {}),\n tables: parsed.tables.map((table) => ({\n ...table,\n fieldPaths: extractFieldPaths(table.validator),\n fields: extractTopLevelFields(table.validator)\n })),\n hash: parsed.hash\n };\n }\n\n if (\n parsed.version !== 1 ||\n !Array.isArray(parsed.tables) ||\n typeof parsed.hash !== \"string\"\n ) {\n throw new Error(\"Invalid schema snapshot file.\");\n }\n return {\n formatVersion: 3,\n plannerVersion: 2,\n tables: parsed.tables.map((table) => ({\n ...table,\n fieldPaths: table.fieldPaths ?? extractFieldPaths(table.validator),\n fields: table.fields ?? extractTopLevelFields(table.validator)\n })),\n hash: parsed.hash\n };\n}\n\nexport function renderCreateTableStatement(tableName: string): string {\n return `\nCREATE TABLE IF NOT EXISTS ${quoteIdentifier(tableName)} (\n _id TEXT PRIMARY KEY,\n _creationTime INTEGER NOT NULL,\n _json TEXT NOT NULL\n);`.trim();\n}\n\nexport function renderCreateIndexStatement(\n tableName: string,\n indexName: string,\n fields: string[]\n): string {\n const expressions = fields\n .map((field) => `json_extract(_json, '$.${field}')`)\n .join(\", \");\n return `CREATE INDEX IF NOT EXISTS ${quoteIdentifier(\n `idx_${tableName}_${indexName}`\n )} ON ${quoteIdentifier(tableName)} (${expressions});`;\n}\n\nexport function renderCreateSearchIndexStatement(\n tableName: string,\n searchIndex: SearchIndexDefinition | TableSnapshot[\"searchIndexes\"][number]\n): string {\n return `CREATE VIRTUAL TABLE IF NOT EXISTS ${quoteIdentifier(\n searchIndexTableName(tableName, searchIndex.name)\n )} USING fts5(_id UNINDEXED, search_value);`;\n}\n\nexport function searchIndexTableName(tableName: string, indexName: string): string {\n return `fts_${tableName}_${indexName}`;\n}\n\nfunction createSchemaHash(\n value: Omit<SchemaSnapshot, \"hash\" | \"runtimeVersion\"> & {\n runtimeVersion?: string;\n }\n): string {\n return stableStringify(value);\n}\n\nfunction quoteIdentifier(identifier: string): string {\n return `\"${identifier.replaceAll('\"', '\"\"')}\"`;\n}\n\nfunction stableStringify(value: unknown): string {\n return JSON.stringify(sortValue(value));\n}\n\nfunction sortValue(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map(sortValue);\n }\n if (value && typeof value === \"object\") {\n return Object.fromEntries(\n Object.entries(value as Record<string, unknown>)\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([key, nested]) => [key, sortValue(nested)])\n );\n }\n return value;\n}\n\nfunction extractFieldPaths(\n description: ValidatorDescription,\n prefix = \"\"\n): string[] {\n switch (description.kind) {\n case \"object\":\n return Object.entries(description.shape).flatMap(([key, entry]) => {\n const path = prefix ? `${prefix}.${key}` : key;\n const normalizedEntry = normalizeObjectFieldEntry(entry);\n const nested = extractFieldPaths(normalizedEntry.validator, path);\n return nested.length > 0 ? [path, ...nested] : [path];\n });\n case \"optional\":\n return extractFieldPaths(description.inner, prefix);\n case \"codec\":\n return extractFieldPaths(description.value, prefix);\n case \"union\":\n return [...new Set(description.members.flatMap((member) => extractFieldPaths(member, prefix)))];\n default:\n return [];\n }\n}\n\nfunction extractTopLevelFields(\n description: ValidatorDescription\n): TableFieldSnapshot[] {\n if (description.kind !== \"object\") {\n return [];\n }\n return Object.entries(description.shape)\n .map(([name, entry]) => {\n const normalizedEntry = normalizeObjectFieldEntry(entry);\n return {\n name,\n validator: normalizedEntry.validator,\n storage:\n normalizedEntry.validator.kind === \"codec\"\n ? normalizedEntry.validator.storage\n : normalizedEntry.validator,\n optional: normalizedEntry.optional\n };\n })\n .sort((left, right) => left.name.localeCompare(right.name));\n}\n\nfunction normalizeObjectFieldEntry(\n entry:\n | ValidatorDescription\n | {\n validator: ValidatorDescription;\n optional?: boolean;\n }\n): {\n validator: ValidatorDescription;\n optional: boolean;\n} {\n if (\"validator\" in entry) {\n return {\n validator: entry.validator,\n optional: entry.optional ?? false\n };\n }\n if (entry.kind === \"optional\") {\n return {\n validator: entry.inner,\n optional: true\n };\n }\n return {\n validator: entry,\n optional: false\n };\n}\n"],"mappings":"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"}