syncorejs 0.2.1 → 0.2.2

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 (135) 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 +323 -42
  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/doctor.mjs +513 -46
  8. package/dist/_vendor/cli/doctor.mjs.map +1 -1
  9. package/dist/_vendor/cli/messages.mjs +5 -4
  10. package/dist/_vendor/cli/messages.mjs.map +1 -1
  11. package/dist/_vendor/cli/project.mjs +110 -12
  12. package/dist/_vendor/cli/project.mjs.map +1 -1
  13. package/dist/_vendor/cli/render.mjs +57 -9
  14. package/dist/_vendor/cli/render.mjs.map +1 -1
  15. package/dist/_vendor/cli/targets.mjs +4 -3
  16. package/dist/_vendor/cli/targets.mjs.map +1 -1
  17. package/dist/_vendor/core/cli.d.mts +13 -3
  18. package/dist/_vendor/core/cli.d.mts.map +1 -1
  19. package/dist/_vendor/core/cli.mjs +242 -91
  20. package/dist/_vendor/core/cli.mjs.map +1 -1
  21. package/dist/_vendor/core/devtools-auth.mjs +60 -0
  22. package/dist/_vendor/core/devtools-auth.mjs.map +1 -0
  23. package/dist/_vendor/core/index.d.mts +5 -3
  24. package/dist/_vendor/core/index.mjs +22 -2
  25. package/dist/_vendor/core/index.mjs.map +1 -1
  26. package/dist/_vendor/core/runtime/components.d.mts +111 -0
  27. package/dist/_vendor/core/runtime/components.d.mts.map +1 -0
  28. package/dist/_vendor/core/runtime/components.mjs +186 -0
  29. package/dist/_vendor/core/runtime/components.mjs.map +1 -0
  30. package/dist/_vendor/core/runtime/devtools.d.mts +4 -4
  31. package/dist/_vendor/core/runtime/devtools.d.mts.map +1 -1
  32. package/dist/_vendor/core/runtime/devtools.mjs +52 -41
  33. package/dist/_vendor/core/runtime/devtools.mjs.map +1 -1
  34. package/dist/_vendor/core/runtime/functions.d.mts +10 -10
  35. package/dist/_vendor/core/runtime/functions.d.mts.map +1 -1
  36. package/dist/_vendor/core/runtime/functions.mjs +2 -2
  37. package/dist/_vendor/core/runtime/functions.mjs.map +1 -1
  38. package/dist/_vendor/core/runtime/internal/engines/devtoolsEngine.mjs +77 -0
  39. package/dist/_vendor/core/runtime/internal/engines/devtoolsEngine.mjs.map +1 -0
  40. package/dist/_vendor/core/runtime/internal/engines/executionEngine.mjs +617 -0
  41. package/dist/_vendor/core/runtime/internal/engines/executionEngine.mjs.map +1 -0
  42. package/dist/_vendor/core/runtime/internal/engines/reactivityEngine.mjs +186 -0
  43. package/dist/_vendor/core/runtime/internal/engines/reactivityEngine.mjs.map +1 -0
  44. package/dist/_vendor/core/runtime/internal/engines/schedulerEngine.mjs +220 -0
  45. package/dist/_vendor/core/runtime/internal/engines/schedulerEngine.mjs.map +1 -0
  46. package/dist/_vendor/core/runtime/internal/engines/schemaEngine.mjs +203 -0
  47. package/dist/_vendor/core/runtime/internal/engines/schemaEngine.mjs.map +1 -0
  48. package/dist/_vendor/core/runtime/internal/engines/shared.mjs +177 -0
  49. package/dist/_vendor/core/runtime/internal/engines/shared.mjs.map +1 -0
  50. package/dist/_vendor/core/runtime/internal/engines/storageEngine.mjs +144 -0
  51. package/dist/_vendor/core/runtime/internal/engines/storageEngine.mjs.map +1 -0
  52. package/dist/_vendor/core/runtime/internal/runtimeKernel.mjs +220 -0
  53. package/dist/_vendor/core/runtime/internal/runtimeKernel.mjs.map +1 -0
  54. package/dist/_vendor/core/runtime/internal/runtimeStatus.mjs +32 -0
  55. package/dist/_vendor/core/runtime/internal/runtimeStatus.mjs.map +1 -0
  56. package/dist/_vendor/core/runtime/internal/systemMeta.mjs +61 -0
  57. package/dist/_vendor/core/runtime/internal/systemMeta.mjs.map +1 -0
  58. package/dist/_vendor/core/runtime/internal/transactionCoordinator.mjs +37 -0
  59. package/dist/_vendor/core/runtime/internal/transactionCoordinator.mjs.map +1 -0
  60. package/dist/_vendor/core/runtime/runtime.d.mts +159 -205
  61. package/dist/_vendor/core/runtime/runtime.d.mts.map +1 -1
  62. package/dist/_vendor/core/runtime/runtime.mjs +16 -1371
  63. package/dist/_vendor/core/runtime/runtime.mjs.map +1 -1
  64. package/dist/_vendor/core/transport.d.mts +111 -0
  65. package/dist/_vendor/core/transport.d.mts.map +1 -0
  66. package/dist/_vendor/core/transport.mjs +419 -0
  67. package/dist/_vendor/core/transport.mjs.map +1 -0
  68. package/dist/_vendor/devtools-protocol/index.d.ts +39 -1
  69. package/dist/_vendor/devtools-protocol/index.d.ts.map +1 -1
  70. package/dist/_vendor/devtools-protocol/index.js +25 -9
  71. package/dist/_vendor/devtools-protocol/index.js.map +1 -1
  72. package/dist/_vendor/next/index.d.ts +1 -1
  73. package/dist/_vendor/next/index.d.ts.map +1 -1
  74. package/dist/_vendor/next/index.js +31 -13
  75. package/dist/_vendor/next/index.js.map +1 -1
  76. package/dist/_vendor/platform-expo/index.d.ts +12 -12
  77. package/dist/_vendor/platform-expo/index.d.ts.map +1 -1
  78. package/dist/_vendor/platform-expo/index.js +4 -2
  79. package/dist/_vendor/platform-expo/index.js.map +1 -1
  80. package/dist/_vendor/platform-expo/react.d.ts.map +1 -1
  81. package/dist/_vendor/platform-expo/react.js +11 -10
  82. package/dist/_vendor/platform-expo/react.js.map +1 -1
  83. package/dist/_vendor/platform-node/index.d.mts +23 -19
  84. package/dist/_vendor/platform-node/index.d.mts.map +1 -1
  85. package/dist/_vendor/platform-node/index.mjs +13 -5
  86. package/dist/_vendor/platform-node/index.mjs.map +1 -1
  87. package/dist/_vendor/platform-node/ipc-react.d.mts.map +1 -1
  88. package/dist/_vendor/platform-node/ipc-react.mjs +15 -2
  89. package/dist/_vendor/platform-node/ipc-react.mjs.map +1 -1
  90. package/dist/_vendor/platform-node/ipc.d.mts +11 -35
  91. package/dist/_vendor/platform-node/ipc.d.mts.map +1 -1
  92. package/dist/_vendor/platform-node/ipc.mjs +3 -273
  93. package/dist/_vendor/platform-node/ipc.mjs.map +1 -1
  94. package/dist/_vendor/platform-web/external-change.d.ts +2 -1
  95. package/dist/_vendor/platform-web/external-change.d.ts.map +1 -1
  96. package/dist/_vendor/platform-web/external-change.js +2 -1
  97. package/dist/_vendor/platform-web/external-change.js.map +1 -1
  98. package/dist/_vendor/platform-web/index.d.ts +21 -21
  99. package/dist/_vendor/platform-web/index.d.ts.map +1 -1
  100. package/dist/_vendor/platform-web/index.js +44 -7
  101. package/dist/_vendor/platform-web/index.js.map +1 -1
  102. package/dist/_vendor/platform-web/react.d.ts.map +1 -1
  103. package/dist/_vendor/platform-web/react.js +29 -13
  104. package/dist/_vendor/platform-web/react.js.map +1 -1
  105. package/dist/_vendor/platform-web/worker.d.ts +11 -35
  106. package/dist/_vendor/platform-web/worker.d.ts.map +1 -1
  107. package/dist/_vendor/platform-web/worker.js +3 -267
  108. package/dist/_vendor/platform-web/worker.js.map +1 -1
  109. package/dist/_vendor/react/index.d.ts +36 -20
  110. package/dist/_vendor/react/index.d.ts.map +1 -1
  111. package/dist/_vendor/react/index.js +279 -57
  112. package/dist/_vendor/react/index.js.map +1 -1
  113. package/dist/_vendor/schema/definition.d.ts +48 -63
  114. package/dist/_vendor/schema/definition.d.ts.map +1 -1
  115. package/dist/_vendor/schema/definition.js +22 -39
  116. package/dist/_vendor/schema/definition.js.map +1 -1
  117. package/dist/_vendor/schema/index.d.ts +4 -4
  118. package/dist/_vendor/schema/index.js +2 -2
  119. package/dist/_vendor/schema/planner.d.ts +19 -2
  120. package/dist/_vendor/schema/planner.d.ts.map +1 -1
  121. package/dist/_vendor/schema/planner.js +79 -3
  122. package/dist/_vendor/schema/planner.js.map +1 -1
  123. package/dist/_vendor/schema/validators.d.ts +141 -121
  124. package/dist/_vendor/schema/validators.d.ts.map +1 -1
  125. package/dist/_vendor/schema/validators.js +300 -42
  126. package/dist/_vendor/schema/validators.js.map +1 -1
  127. package/dist/_vendor/svelte/index.d.ts +47 -19
  128. package/dist/_vendor/svelte/index.d.ts.map +1 -1
  129. package/dist/_vendor/svelte/index.js +250 -20
  130. package/dist/_vendor/svelte/index.js.map +1 -1
  131. package/dist/components.d.ts +2 -0
  132. package/dist/components.js +2 -0
  133. package/dist/index.d.ts +3 -2
  134. package/dist/index.js +2 -1
  135. package/package.json +8 -3
@@ -1,11 +1,5 @@
1
- import { ensureObjectValidator } from "./validators.js";
1
+ import { describeValidator, deserializeValue, ensureObjectValidator, serializeValue } from "./validators.js";
2
2
  //#region src/definition.ts
3
- /**
4
- * Describes a Syncore table and its indexes.
5
- *
6
- * Create tables with {@link defineTable} and then chain index helpers to make
7
- * queries faster and more expressive.
8
- */
9
3
  var TableDefinition = class {
10
4
  indexes = [];
11
5
  searchIndexes = [];
@@ -14,38 +8,39 @@ var TableDefinition = class {
14
8
  this.validator = validator;
15
9
  this.options = options ?? {};
16
10
  }
17
- /**
18
- * Add a named index for querying a table by one or more fields.
19
- *
20
- * @param name - The index name used from `ctx.db.query(...).withIndex(...)`.
21
- * @param fields - The fields that participate in the index.
22
- * @returns The same table definition for chaining.
23
- */
24
11
  index(name, fields) {
25
12
  this.indexes.push({
26
13
  name,
27
- fields
14
+ fields: [...fields]
28
15
  });
29
16
  return this;
30
17
  }
31
- /**
32
- * Add a search index for text search.
33
- *
34
- * @param name - The search index name used from `withSearchIndex(...)`.
35
- * @param config - The indexed search field and optional filter fields.
36
- * @returns The same table definition for chaining.
37
- */
38
18
  searchIndex(name, config) {
39
19
  this.searchIndexes.push({
40
20
  name,
41
21
  searchField: config.searchField,
42
- filterFields: config.filterFields ?? []
22
+ filterFields: [...config.filterFields ?? []]
43
23
  });
44
24
  return this;
45
25
  }
26
+ parse(value) {
27
+ return this.validator.parse(value);
28
+ }
29
+ serialize(value) {
30
+ return serializeValue(this.validator, value);
31
+ }
32
+ deserialize(value) {
33
+ return deserializeValue(this.validator, value);
34
+ }
35
+ parseAndSerialize(value) {
36
+ return this.serialize(this.parse(value));
37
+ }
38
+ describe() {
39
+ return describeValidator(this.validator);
40
+ }
46
41
  };
47
42
  function defineTable(validator) {
48
- return new TableDefinition(ensureObjectValidator(validator));
43
+ return new TableDefinition(isValidatorLike(validator) ? validator : ensureObjectValidator(validator));
49
44
  }
50
45
  var SyncoreSchema = class {
51
46
  constructor(tables) {
@@ -60,24 +55,12 @@ var SyncoreSchema = class {
60
55
  return Object.keys(this.tables);
61
56
  }
62
57
  };
63
- /**
64
- * Define the tables that make up your Syncore app.
65
- *
66
- * The returned schema is used by runtimes, code generation, and type inference.
67
- *
68
- * @example
69
- * ```ts
70
- * export default defineSchema({
71
- * tasks: defineTable({
72
- * text: v.string(),
73
- * done: v.boolean()
74
- * })
75
- * });
76
- * ```
77
- */
78
58
  function defineSchema(tables) {
79
59
  return new SyncoreSchema(tables);
80
60
  }
61
+ function isValidatorLike(value) {
62
+ return typeof value.parse === "function";
63
+ }
81
64
  //#endregion
82
65
  export { SyncoreSchema, TableDefinition, defineSchema, defineTable };
83
66
 
@@ -1 +1 @@
1
- {"version":3,"file":"definition.js","names":[],"sources":["../src/definition.ts"],"sourcesContent":["import {\n ensureObjectValidator,\n type Infer,\n type ObjectValidatorShape,\n type Validator\n} from \"./validators.js\";\nimport type { ObjectValidator } from \"./validators.js\";\n\nexport interface IndexDefinition {\n name: string;\n fields: string[];\n}\n\nexport interface SearchIndexDefinition {\n name: string;\n searchField: string;\n filterFields: string[];\n}\n\nexport interface TableDefinitionOptions {\n tableName?: string;\n}\n\nexport interface TableDocumentSystemFields {\n _id: string;\n _creationTime: number;\n}\n\n/**\n * Describes a Syncore table and its indexes.\n *\n * Create tables with {@link defineTable} and then chain index helpers to make\n * queries faster and more expressive.\n */\nexport class TableDefinition<TValidator extends Validator<unknown>> {\n readonly indexes: IndexDefinition[] = [];\n readonly searchIndexes: SearchIndexDefinition[] = [];\n readonly options: TableDefinitionOptions;\n\n constructor(\n public readonly validator: TValidator,\n options?: TableDefinitionOptions\n ) {\n this.options = options ?? {};\n }\n\n /**\n * Add a named index for querying a table by one or more fields.\n *\n * @param name - The index name used from `ctx.db.query(...).withIndex(...)`.\n * @param fields - The fields that participate in the index.\n * @returns The same table definition for chaining.\n */\n index(name: string, fields: string[]): this {\n this.indexes.push({ name, fields });\n return this;\n }\n\n /**\n * Add a search index for text search.\n *\n * @param name - The search index name used from `withSearchIndex(...)`.\n * @param config - The indexed search field and optional filter fields.\n * @returns The same table definition for chaining.\n */\n searchIndex(\n name: string,\n config: { searchField: string; filterFields?: string[] }\n ): this {\n this.searchIndexes.push({\n name,\n searchField: config.searchField,\n filterFields: config.filterFields ?? []\n });\n return this;\n }\n}\n\nexport type AnyTableDefinition = TableDefinition<Validator<unknown>>;\n\nexport type InferDocument<TTable extends AnyTableDefinition> = Infer<\n TTable[\"validator\"]\n> &\n TableDocumentSystemFields;\n\nexport type InferTableInput<TTable extends AnyTableDefinition> = Omit<\n InferDocument<TTable>,\n keyof TableDocumentSystemFields\n>;\n\n/**\n * Define a table in a Syncore schema.\n *\n * Pass an object of validators describing the document fields stored in the\n * table. Chain `.index(...)` or `.searchIndex(...)` to add query helpers.\n *\n * @example\n * ```ts\n * const tasks = defineTable({\n * text: v.string(),\n * done: v.boolean()\n * }).index(\"by_done\", [\"done\"]);\n * ```\n */\nexport function defineTable<TShape extends ObjectValidatorShape>(\n validator: TShape\n): TableDefinition<ObjectValidator<TShape>>;\nexport function defineTable<TValidator extends Validator<unknown>>(\n validator: TValidator\n): TableDefinition<TValidator>;\nexport function defineTable<TShape extends ObjectValidatorShape>(\n validator: TShape | Validator<unknown>\n): TableDefinition<Validator<unknown>> {\n return new TableDefinition(ensureObjectValidator(validator));\n}\n\nexport interface SyncoreSchemaDefinition {\n [tableName: string]: AnyTableDefinition;\n}\n\nexport class SyncoreSchema<TTables extends SyncoreSchemaDefinition> {\n constructor(public readonly tables: TTables) {}\n\n getTable<TTableName extends Extract<keyof TTables, string>>(\n tableName: TTableName\n ): TTables[TTableName] {\n const table = this.tables[tableName];\n if (!table) {\n throw new Error(`Unknown table \"${tableName}\".`);\n }\n return table;\n }\n\n tableNames(): Array<Extract<keyof TTables, string>> {\n return Object.keys(this.tables) as Array<Extract<keyof TTables, string>>;\n }\n}\n\n/**\n * Define the tables that make up your Syncore app.\n *\n * The returned schema is used by runtimes, code generation, and type inference.\n *\n * @example\n * ```ts\n * export default defineSchema({\n * tasks: defineTable({\n * text: v.string(),\n * done: v.boolean()\n * })\n * });\n * ```\n */\nexport function defineSchema<TTables extends SyncoreSchemaDefinition>(\n tables: TTables\n): SyncoreSchema<TTables> {\n return new SyncoreSchema(tables);\n}\n"],"mappings":";;;;;;;;AAkCA,IAAa,kBAAb,MAAoE;CAClE,UAAsC,EAAE;CACxC,gBAAkD,EAAE;CACpD;CAEA,YACE,WACA,SACA;AAFgB,OAAA,YAAA;AAGhB,OAAK,UAAU,WAAW,EAAE;;;;;;;;;CAU9B,MAAM,MAAc,QAAwB;AAC1C,OAAK,QAAQ,KAAK;GAAE;GAAM;GAAQ,CAAC;AACnC,SAAO;;;;;;;;;CAUT,YACE,MACA,QACM;AACN,OAAK,cAAc,KAAK;GACtB;GACA,aAAa,OAAO;GACpB,cAAc,OAAO,gBAAgB,EAAE;GACxC,CAAC;AACF,SAAO;;;AAoCX,SAAgB,YACd,WACqC;AACrC,QAAO,IAAI,gBAAgB,sBAAsB,UAAU,CAAC;;AAO9D,IAAa,gBAAb,MAAoE;CAClE,YAAY,QAAiC;AAAjB,OAAA,SAAA;;CAE5B,SACE,WACqB;EACrB,MAAM,QAAQ,KAAK,OAAO;AAC1B,MAAI,CAAC,MACH,OAAM,IAAI,MAAM,kBAAkB,UAAU,IAAI;AAElD,SAAO;;CAGT,aAAoD;AAClD,SAAO,OAAO,KAAK,KAAK,OAAO;;;;;;;;;;;;;;;;;;AAmBnC,SAAgB,aACd,QACwB;AACxB,QAAO,IAAI,cAAc,OAAO"}
1
+ {"version":3,"file":"definition.js","names":[],"sources":["../src/definition.ts"],"sourcesContent":["import {\n describeValidator,\n deserializeValue,\n ensureObjectValidator,\n serializeValue,\n type FieldPaths,\n type Infer,\n type InferStorage,\n type ObjectValidator,\n type ObjectValidatorShape,\n type Validator\n} from \"./validators.js\";\n\ntype Expand<T> = { [TKey in keyof T]: T[TKey] } & {};\n\nexport interface IndexDefinition {\n name: string;\n fields: string[];\n}\n\nexport interface SearchIndexDefinition {\n name: string;\n searchField: string;\n filterFields: string[];\n}\n\nexport interface TableDefinitionOptions {\n tableName?: string;\n componentPath?: string;\n componentName?: string;\n}\n\nexport interface TableDocumentSystemFields {\n _id: string;\n _creationTime: number;\n}\n\nexport type GenericTableIndexes = Record<string, readonly string[]>;\n\nexport type GenericTableSearchIndexes = Record<\n string,\n {\n searchField: string;\n filterFields: string;\n }\n>;\n\nexport class TableDefinition<\n TValidator extends Validator<Record<string, unknown>, Record<string, unknown>, string>,\n TIndexes = Record<never, never>,\n TSearchIndexes = Record<never, never>\n> {\n readonly indexes: IndexDefinition[] = [];\n readonly searchIndexes: SearchIndexDefinition[] = [];\n readonly options: TableDefinitionOptions;\n\n declare readonly document: Infer<TValidator>;\n declare readonly storageDocument: InferStorage<TValidator>;\n declare readonly fieldPaths: FieldPaths<TValidator>;\n declare readonly indexesByName: TIndexes;\n declare readonly searchIndexesByName: TSearchIndexes;\n\n constructor(\n public readonly validator: TValidator,\n options?: TableDefinitionOptions\n ) {\n this.options = options ?? {};\n }\n\n index<\n const TIndexName extends string,\n TFirstField extends FieldPaths<TValidator>,\n TRestFields extends FieldPaths<TValidator>[]\n >(\n name: TIndexName,\n fields: [TFirstField, ...TRestFields]\n ): TableDefinition<\n TValidator,\n Expand<TIndexes & Record<TIndexName, readonly [TFirstField, ...TRestFields]>>,\n TSearchIndexes\n > {\n this.indexes.push({\n name,\n fields: [...fields]\n });\n return this as unknown as TableDefinition<\n TValidator,\n Expand<TIndexes & Record<TIndexName, readonly [TFirstField, ...TRestFields]>>,\n TSearchIndexes\n >;\n }\n\n searchIndex<\n const TIndexName extends string,\n TSearchField extends FieldPaths<TValidator>,\n TFilterField extends FieldPaths<TValidator> = never\n >(\n name: TIndexName,\n config: {\n searchField: TSearchField;\n filterFields?: TFilterField[];\n }\n ): TableDefinition<\n TValidator,\n TIndexes,\n Expand<\n TSearchIndexes &\n Record<\n TIndexName,\n {\n searchField: TSearchField;\n filterFields: TFilterField;\n }\n >\n >\n > {\n this.searchIndexes.push({\n name,\n searchField: config.searchField,\n filterFields: [...(config.filterFields ?? [])]\n });\n return this as unknown as TableDefinition<\n TValidator,\n TIndexes,\n Expand<\n TSearchIndexes &\n Record<\n TIndexName,\n {\n searchField: TSearchField;\n filterFields: TFilterField;\n }\n >\n >\n >;\n }\n\n parse(value: unknown): Infer<TValidator> {\n return this.validator.parse(value) as Infer<TValidator>;\n }\n\n serialize(value: Infer<TValidator>): InferStorage<TValidator> {\n return serializeValue(this.validator, value) as InferStorage<TValidator>;\n }\n\n deserialize(value: unknown): Infer<TValidator> {\n return deserializeValue(this.validator, value) as Infer<TValidator>;\n }\n\n parseAndSerialize(value: unknown): InferStorage<TValidator> {\n return this.serialize(this.parse(value));\n }\n\n describe() {\n return describeValidator(this.validator);\n }\n}\n\nexport type AnyTableDefinition = TableDefinition<\n Validator<Record<string, unknown>, Record<string, unknown>, string>,\n GenericTableIndexes,\n GenericTableSearchIndexes\n>;\n\nexport type InferDocument<TTable extends AnyTableDefinition> = Infer<\n TTable[\"validator\"]\n> &\n TableDocumentSystemFields;\n\nexport type InferTableInput<TTable extends AnyTableDefinition> = Infer<\n TTable[\"validator\"]\n>;\n\nexport type TableFieldPaths<TTable> = TTable extends TableDefinition<\n infer TValidator,\n unknown,\n unknown\n>\n ? FieldPaths<TValidator>\n : never;\n\nexport type TableIndexes<TTable> = TTable extends TableDefinition<\n Validator<Record<string, unknown>, Record<string, unknown>, string>,\n infer TIndexes,\n unknown\n>\n ? TIndexes\n : never;\n\nexport type TableSearchIndexes<TTable> = TTable extends TableDefinition<\n Validator<Record<string, unknown>, Record<string, unknown>, string>,\n unknown,\n infer TSearchIndexes\n>\n ? TSearchIndexes\n : never;\n\nexport type TableIndexNames<TTable> = Extract<\n keyof TableIndexes<TTable>,\n string\n>;\n\nexport type TableSearchIndexNames<TTable> = Extract<\n keyof TableSearchIndexes<TTable>,\n string\n>;\n\nexport type TableIndexFields<\n TTable,\n TIndexName extends TableIndexNames<TTable>\n> = TableIndexes<TTable>[TIndexName];\n\nexport type TableSearchIndexConfig<\n TTable,\n TIndexName extends TableSearchIndexNames<TTable>\n> = TableSearchIndexes<TTable>[TIndexName];\n\nexport type TableFieldDefinitionSummary = {\n name: string;\n validator: ReturnType<AnyTableDefinition[\"describe\"]>;\n storage: ReturnType<AnyTableDefinition[\"describe\"]>;\n optional: boolean;\n};\n\nexport function defineTable<const TShape extends ObjectValidatorShape>(\n validator: TShape\n): TableDefinition<ObjectValidator<TShape>>;\nexport function defineTable<\n TValidator extends Validator<Record<string, unknown>, Record<string, unknown>, string>\n>(validator: TValidator): TableDefinition<TValidator>;\nexport function defineTable<const TShape extends ObjectValidatorShape>(\n validator:\n | TShape\n | Validator<Record<string, unknown>, Record<string, unknown>, string>\n): TableDefinition<\n Validator<Record<string, unknown>, Record<string, unknown>, string>\n> {\n const normalized: Validator<Record<string, unknown>, Record<string, unknown>, string> =\n isValidatorLike(validator)\n ? validator\n : ensureObjectValidator(validator);\n return new TableDefinition(normalized);\n}\n\nexport interface SyncoreSchemaDefinition {\n [tableName: string]: AnyTableDefinition;\n}\n\nexport class SyncoreSchema<TTables> {\n constructor(public readonly tables: TTables) {}\n\n getTable<TTableName extends Extract<keyof TTables, string>>(\n tableName: TTableName\n ): TTables[TTableName] {\n const tables = this.tables as Record<string, unknown>;\n const table = tables[tableName];\n if (!table) {\n throw new Error(`Unknown table \"${tableName}\".`);\n }\n return table as TTables[TTableName];\n }\n\n tableNames(): Array<Extract<keyof TTables, string>> {\n return Object.keys(this.tables as Record<string, unknown>) as Array<\n Extract<keyof TTables, string>\n >;\n }\n}\n\nexport function defineSchema<const TTables extends SyncoreSchemaDefinition>(\n tables: TTables\n): SyncoreSchema<TTables> {\n return new SyncoreSchema(tables);\n}\n\nfunction isValidatorLike(\n value: Validator<Record<string, unknown>, Record<string, unknown>, string> | ObjectValidatorShape\n): value is Validator<Record<string, unknown>, Record<string, unknown>, string> {\n return typeof (value as Validator<unknown, unknown, string>).parse === \"function\";\n}\n"],"mappings":";;AA+CA,IAAa,kBAAb,MAIE;CACA,UAAsC,EAAE;CACxC,gBAAkD,EAAE;CACpD;CAQA,YACE,WACA,SACA;AAFgB,OAAA,YAAA;AAGhB,OAAK,UAAU,WAAW,EAAE;;CAG9B,MAKE,MACA,QAKA;AACA,OAAK,QAAQ,KAAK;GAChB;GACA,QAAQ,CAAC,GAAG,OAAO;GACpB,CAAC;AACF,SAAO;;CAOT,YAKE,MACA,QAiBA;AACA,OAAK,cAAc,KAAK;GACtB;GACA,aAAa,OAAO;GACpB,cAAc,CAAC,GAAI,OAAO,gBAAgB,EAAE,CAAE;GAC/C,CAAC;AACF,SAAO;;CAgBT,MAAM,OAAmC;AACvC,SAAO,KAAK,UAAU,MAAM,MAAM;;CAGpC,UAAU,OAAoD;AAC5D,SAAO,eAAe,KAAK,WAAW,MAAM;;CAG9C,YAAY,OAAmC;AAC7C,SAAO,iBAAiB,KAAK,WAAW,MAAM;;CAGhD,kBAAkB,OAA0C;AAC1D,SAAO,KAAK,UAAU,KAAK,MAAM,MAAM,CAAC;;CAG1C,WAAW;AACT,SAAO,kBAAkB,KAAK,UAAU;;;AA4E5C,SAAgB,YACd,WAKA;AAKA,QAAO,IAAI,gBAHT,gBAAgB,UAAU,GACxB,YACA,sBAAsB,UAAU,CACE;;AAOxC,IAAa,gBAAb,MAAoC;CAClC,YAAY,QAAiC;AAAjB,OAAA,SAAA;;CAE5B,SACE,WACqB;EAErB,MAAM,QADS,KAAK,OACC;AACrB,MAAI,CAAC,MACH,OAAM,IAAI,MAAM,kBAAkB,UAAU,IAAI;AAElD,SAAO;;CAGT,aAAoD;AAClD,SAAO,OAAO,KAAK,KAAK,OAAkC;;;AAM9D,SAAgB,aACd,QACwB;AACxB,QAAO,IAAI,cAAc,OAAO;;AAGlC,SAAS,gBACP,OAC8E;AAC9E,QAAO,OAAQ,MAA8C,UAAU"}
@@ -1,4 +1,4 @@
1
- import { AnyValidator, ArrayValidator, BooleanValidator, IdValidator, Infer, LiteralValidator, NullValidator, NumberValidator, ObjectValidator, ObjectValidatorShape, OptionalValidator, StringValidator, Validator, ValidatorBuilderApi, ValidatorDescription, ValidatorKind, ValidatorMap, describeValidator, ensureObjectValidator, v } from "./validators.js";
2
- import { AnyTableDefinition, IndexDefinition, InferDocument, InferTableInput, SearchIndexDefinition, SyncoreSchema, SyncoreSchemaDefinition, TableDefinition, TableDefinitionOptions, TableDocumentSystemFields, defineSchema, defineTable } from "./definition.js";
3
- import { SchemaMigrationPlan, SchemaSnapshot, TableSnapshot, createSchemaSnapshot, diffSchemaSnapshots, parseSchemaSnapshot, renderCreateIndexStatement, renderCreateSearchIndexStatement, renderCreateTableStatement, renderMigrationSql, searchIndexTableName } from "./planner.js";
4
- export { AnyTableDefinition, AnyValidator, ArrayValidator, BooleanValidator, IdValidator, IndexDefinition, Infer, InferDocument, InferTableInput, LiteralValidator, NullValidator, NumberValidator, ObjectValidator, ObjectValidatorShape, OptionalValidator, SchemaMigrationPlan, SchemaSnapshot, SearchIndexDefinition, StringValidator, SyncoreSchema, SyncoreSchemaDefinition, TableDefinition, TableDefinitionOptions, TableDocumentSystemFields, TableSnapshot, Validator, ValidatorBuilderApi, ValidatorDescription, ValidatorKind, ValidatorMap, createSchemaSnapshot, defineSchema, defineTable, describeValidator, diffSchemaSnapshots, ensureObjectValidator, parseSchemaSnapshot, renderCreateIndexStatement, renderCreateSearchIndexStatement, renderCreateTableStatement, renderMigrationSql, searchIndexTableName, v };
1
+ import { AnyValidator, ArrayValidator, BooleanValidator, CodecValidator, EnumValidator, FieldPaths, IdValidator, Infer, InferStorage, JoinFieldPaths, LiteralValidator, NullValidator, NumberValidator, ObjectValidator, ObjectValidatorShape, OptionalValidator, RecordValidator, StringValidator, UnionValidator, Validator, ValidatorBuilderApi, ValidatorDescription, ValidatorKind, ValidatorMap, describeValidator, deserializeValue, ensureObjectValidator, isValidator, s, serializeValue } from "./validators.js";
2
+ import { AnyTableDefinition, GenericTableIndexes, GenericTableSearchIndexes, IndexDefinition, InferDocument, InferTableInput, SearchIndexDefinition, SyncoreSchema, SyncoreSchemaDefinition, TableDefinition, TableDefinitionOptions, TableDocumentSystemFields, TableFieldDefinitionSummary, TableFieldPaths, TableIndexFields, TableIndexNames, TableIndexes, TableSearchIndexConfig, TableSearchIndexNames, TableSearchIndexes, defineSchema, defineTable } from "./definition.js";
3
+ import { SchemaMigrationPlan, SchemaSnapshot, TableFieldSnapshot, TableSnapshot, createSchemaSnapshot, diffSchemaSnapshots, parseSchemaSnapshot, renderCreateIndexStatement, renderCreateSearchIndexStatement, renderCreateTableStatement, renderMigrationSql, searchIndexTableName } from "./planner.js";
4
+ export { AnyTableDefinition, AnyValidator, ArrayValidator, BooleanValidator, CodecValidator, EnumValidator, FieldPaths, GenericTableIndexes, GenericTableSearchIndexes, IdValidator, IndexDefinition, Infer, InferDocument, InferStorage, InferTableInput, JoinFieldPaths, LiteralValidator, NullValidator, NumberValidator, ObjectValidator, ObjectValidatorShape, OptionalValidator, RecordValidator, SchemaMigrationPlan, SchemaSnapshot, SearchIndexDefinition, StringValidator, SyncoreSchema, SyncoreSchemaDefinition, TableDefinition, TableDefinitionOptions, TableDocumentSystemFields, TableFieldDefinitionSummary, TableFieldPaths, TableFieldSnapshot, TableIndexFields, TableIndexNames, TableIndexes, TableSearchIndexConfig, TableSearchIndexNames, TableSearchIndexes, TableSnapshot, UnionValidator, Validator, ValidatorBuilderApi, ValidatorDescription, ValidatorKind, ValidatorMap, createSchemaSnapshot, defineSchema, defineTable, describeValidator, deserializeValue, diffSchemaSnapshots, ensureObjectValidator, isValidator, parseSchemaSnapshot, renderCreateIndexStatement, renderCreateSearchIndexStatement, renderCreateTableStatement, renderMigrationSql, s, searchIndexTableName, serializeValue };
@@ -1,4 +1,4 @@
1
- import { AnyValidator, ArrayValidator, BooleanValidator, IdValidator, LiteralValidator, NullValidator, NumberValidator, ObjectValidator, OptionalValidator, StringValidator, describeValidator, ensureObjectValidator, v } from "./validators.js";
1
+ import { AnyValidator, ArrayValidator, BooleanValidator, CodecValidator, EnumValidator, IdValidator, LiteralValidator, NullValidator, NumberValidator, ObjectValidator, OptionalValidator, RecordValidator, StringValidator, UnionValidator, describeValidator, deserializeValue, ensureObjectValidator, isValidator, s, serializeValue } from "./validators.js";
2
2
  import { SyncoreSchema, TableDefinition, defineSchema, defineTable } from "./definition.js";
3
3
  import { createSchemaSnapshot, diffSchemaSnapshots, parseSchemaSnapshot, renderCreateIndexStatement, renderCreateSearchIndexStatement, renderCreateTableStatement, renderMigrationSql, searchIndexTableName } from "./planner.js";
4
- export { AnyValidator, ArrayValidator, BooleanValidator, IdValidator, LiteralValidator, NullValidator, NumberValidator, ObjectValidator, OptionalValidator, StringValidator, SyncoreSchema, TableDefinition, createSchemaSnapshot, defineSchema, defineTable, describeValidator, diffSchemaSnapshots, ensureObjectValidator, parseSchemaSnapshot, renderCreateIndexStatement, renderCreateSearchIndexStatement, renderCreateTableStatement, renderMigrationSql, searchIndexTableName, v };
4
+ export { AnyValidator, ArrayValidator, BooleanValidator, CodecValidator, EnumValidator, IdValidator, LiteralValidator, NullValidator, NumberValidator, ObjectValidator, OptionalValidator, RecordValidator, StringValidator, SyncoreSchema, TableDefinition, UnionValidator, createSchemaSnapshot, defineSchema, defineTable, describeValidator, deserializeValue, diffSchemaSnapshots, ensureObjectValidator, isValidator, parseSchemaSnapshot, renderCreateIndexStatement, renderCreateSearchIndexStatement, renderCreateTableStatement, renderMigrationSql, s, searchIndexTableName, serializeValue };
@@ -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,oBAAA;EACT,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;EACR,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,mBAAA,EACN,OAAA;EAAY,KAAA;AAAA;AAAA,iBAiCE,mBAAA,CAAoB,MAAA,WAAiB,cAAA;AAAA,iBAwErC,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"}
@@ -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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"}