@jarenjs/db 0.49.2 → 0.66.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/ARCHITECTURE.md +420 -71
  2. package/README.md +711 -79
  3. package/docs/HOSTS.md +269 -0
  4. package/docs/JOBS-FORMAT.md +309 -45
  5. package/docs/LIVE-FORMAT.md +156 -19
  6. package/docs/MIGRATION-FORMAT.md +247 -40
  7. package/docs/MODEL-FORMAT.md +968 -86
  8. package/package.json +21 -8
  9. package/schemas/jaren-migration.draft-07.schema.json +73 -0
  10. package/schemas/jaren-migration.schema.json +73 -0
  11. package/schemas/jaren-model.draft-07.schema.json +224 -162
  12. package/schemas/jaren-model.schema.json +224 -162
  13. package/src/algebra.js +227 -9
  14. package/src/backup.js +161 -0
  15. package/src/cancellation.js +48 -0
  16. package/src/capture.js +255 -44
  17. package/src/cli.js +337 -50
  18. package/src/cursor.js +411 -0
  19. package/src/dag-job.js +154 -21
  20. package/src/ddl.js +125 -11
  21. package/src/dialect.js +267 -112
  22. package/src/dialects/expression-read.js +158 -0
  23. package/src/dialects/postgres.js +618 -0
  24. package/src/dialects/rtree-ddl.js +129 -0
  25. package/src/dialects/sqlite.js +245 -12
  26. package/src/document-files.js +311 -0
  27. package/src/document-steps.js +422 -0
  28. package/src/documents.js +335 -0
  29. package/src/driver.js +503 -69
  30. package/src/drivers/bun.js +37 -1
  31. package/src/drivers/indexeddb-snapshot.js +149 -0
  32. package/src/drivers/node-pool.js +11 -0
  33. package/src/drivers/node-worker-endpoint.js +105 -0
  34. package/src/drivers/node-worker.js +204 -0
  35. package/src/drivers/node.js +41 -7
  36. package/src/drivers/postgres.js +331 -0
  37. package/src/drivers/wasm-oo1.js +97 -0
  38. package/src/drivers/wasm-session.js +67 -0
  39. package/src/drivers/wasm.js +18 -83
  40. package/src/drivers/worker-pool.js +183 -0
  41. package/src/drivers/worker-protocol.js +79 -0
  42. package/src/drivers/worker-queue.js +60 -0
  43. package/src/emit-model.js +14 -0
  44. package/src/emit.js +349 -51
  45. package/src/entity.js +102 -59
  46. package/src/errors.js +430 -2
  47. package/src/expression.js +284 -0
  48. package/src/graph.js +64 -8
  49. package/src/index.js +48 -19
  50. package/src/introspect.js +583 -0
  51. package/src/jobs.js +870 -99
  52. package/src/json-bytes.js +58 -0
  53. package/src/live-time.js +12 -3
  54. package/src/live.js +11 -1
  55. package/src/maintenance.js +175 -0
  56. package/src/migrate.js +606 -333
  57. package/src/model.js +241 -8
  58. package/src/plan.js +1238 -160
  59. package/src/pragmas.js +314 -0
  60. package/src/profile.js +151 -3
  61. package/src/query.js +1748 -312
  62. package/src/residual.js +17 -0
  63. package/src/series.js +12 -4
  64. package/src/store.js +1672 -276
  65. package/src/tracker.js +367 -68
  66. package/src/udf.js +88 -7
  67. package/types/index.d.ts +1246 -32
  68. package/types/node-pool.d.ts +28 -0
  69. package/types/node-worker.d.ts +54 -0
  70. package/types/node.d.ts +72 -3
  71. package/types/postgres.d.ts +46 -0
  72. package/types/typed.d.ts +81 -3
  73. package/types/wasm.d.ts +21 -0
  74. package/dist/types/algebra.d.ts +0 -230
  75. package/dist/types/app.d.ts +0 -49
  76. package/dist/types/capture.d.ts +0 -85
  77. package/dist/types/cli.d.ts +0 -2
  78. package/dist/types/dag-job.d.ts +0 -40
  79. package/dist/types/ddl.d.ts +0 -229
  80. package/dist/types/derive.d.ts +0 -250
  81. package/dist/types/dialect.d.ts +0 -154
  82. package/dist/types/dialects/sqlite.d.ts +0 -9
  83. package/dist/types/driver.d.ts +0 -110
  84. package/dist/types/drivers/bun.d.ts +0 -47
  85. package/dist/types/drivers/node.d.ts +0 -37
  86. package/dist/types/drivers/wasm.d.ts +0 -65
  87. package/dist/types/emit-model.d.ts +0 -44
  88. package/dist/types/emit.d.ts +0 -75
  89. package/dist/types/entity.d.ts +0 -23
  90. package/dist/types/errors.d.ts +0 -170
  91. package/dist/types/graph.d.ts +0 -28
  92. package/dist/types/index.d.ts +0 -37
  93. package/dist/types/jobs.d.ts +0 -140
  94. package/dist/types/knn.d.ts +0 -69
  95. package/dist/types/live-time.d.ts +0 -141
  96. package/dist/types/live.d.ts +0 -64
  97. package/dist/types/migrate.d.ts +0 -170
  98. package/dist/types/model.d.ts +0 -36
  99. package/dist/types/patch-sql.d.ts +0 -37
  100. package/dist/types/plan.d.ts +0 -142
  101. package/dist/types/profile.d.ts +0 -80
  102. package/dist/types/query.d.ts +0 -112
  103. package/dist/types/residual.d.ts +0 -64
  104. package/dist/types/series.d.ts +0 -227
  105. package/dist/types/store.d.ts +0 -60
  106. package/dist/types/tracker.d.ts +0 -43
  107. package/dist/types/typed.d.ts +0 -15
  108. package/dist/types/types.d.ts +0 -26
  109. package/dist/types/udf.d.ts +0 -75
  110. package/dist/types/window.d.ts +0 -52
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@jarenjs/db",
3
3
  "private": false,
4
- "version": "0.49.2",
4
+ "version": "0.66.1",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
7
7
  "types": "./types/index.d.ts",
@@ -15,6 +15,10 @@
15
15
  "types": "./types/node.d.ts",
16
16
  "default": "./src/drivers/node.js"
17
17
  },
18
+ "./postgres": {
19
+ "types": "./types/postgres.d.ts",
20
+ "default": "./src/drivers/postgres.js"
21
+ },
18
22
  "./bun": {
19
23
  "types": "./types/bun.d.ts",
20
24
  "default": "./src/drivers/bun.js"
@@ -32,17 +36,24 @@
32
36
  "default": "./src/app.js"
33
37
  },
34
38
  "./schemas/*": "./schemas/*",
35
- "./package.json": "./package.json"
39
+ "./package.json": "./package.json",
40
+ "./node-worker": {
41
+ "types": "./types/node-worker.d.ts",
42
+ "default": "./src/drivers/node-worker.js"
43
+ },
44
+ "./node-pool": {
45
+ "types": "./types/node-pool.d.ts",
46
+ "default": "./src/drivers/node-pool.js"
47
+ }
36
48
  },
37
49
  "files": [
38
50
  "types/",
39
- "dist/types/",
40
51
  "src/",
41
52
  "schemas/",
42
53
  "docs/",
43
54
  "ARCHITECTURE.md"
44
55
  ],
45
- "description": "Document storage for the Jaren suite: a model document declares collections as JSON Schemas with indexes; openStore applies DDL through a dialect and gives transactional, schema-validated reads and writes over SQLite on Node, Bun, or an injected wasm handle",
56
+ "description": "Document storage for the Jaren suite: a model document declares collections as JSON Schemas with indexes; openStore applies DDL through a dialect and gives transactional, schema-validated reads and writes over SQLite (Node, Bun, or an injected wasm handle) or PostgreSQL 16+ through an injected client",
46
57
  "author": "joham",
47
58
  "repository": {
48
59
  "type": "git",
@@ -63,7 +74,9 @@
63
74
  "sqlite",
64
75
  "document-store",
65
76
  "json-schema",
66
- "database"
77
+ "database",
78
+ "postgres",
79
+ "postgresql"
67
80
  ],
68
81
  "scripts": {
69
82
  "build": "npm run build:types",
@@ -71,9 +84,9 @@
71
84
  "prepack": "npm run build:types"
72
85
  },
73
86
  "dependencies": {
74
- "@jarenjs/core": "^0.49.2",
75
- "@jarenjs/json": "^0.49.2",
76
- "@jarenjs/validate": "^0.49.2"
87
+ "@jarenjs/core": "^0.66.1",
88
+ "@jarenjs/json": "^0.66.1",
89
+ "@jarenjs/validate": "^0.66.1"
77
90
  },
78
91
  "bin": {
79
92
  "jaren-db": "./src/cli.js"
@@ -58,6 +58,12 @@
58
58
  },
59
59
  {
60
60
  "$ref": "#/definitions/deriveStep"
61
+ },
62
+ {
63
+ "$ref": "#/definitions/sqlStep"
64
+ },
65
+ {
66
+ "$ref": "#/definitions/rebuildStep"
61
67
  }
62
68
  ]
63
69
  },
@@ -216,6 +222,73 @@
216
222
  "columns"
217
223
  ],
218
224
  "additionalProperties": false
225
+ },
226
+ "sqlStep": {
227
+ "description": "A DATA step spelled directly as one SQL statement (MIGRATION-FORMAT §9.4): the planner's fold of a dropped column into the document, the move of a document member into its new column, an R*Tree backfill. Executed exactly as a ddl step; distinct in kind so a reviewer reads intent and a dry run shows it as data.",
228
+ "type": "object",
229
+ "properties": {
230
+ "kind": {
231
+ "const": "sql"
232
+ },
233
+ "sql": {
234
+ "type": "string",
235
+ "minLength": 1
236
+ },
237
+ "note": {
238
+ "type": "string"
239
+ }
240
+ },
241
+ "required": [
242
+ "kind",
243
+ "sql"
244
+ ],
245
+ "additionalProperties": false
246
+ },
247
+ "rebuildStep": {
248
+ "description": "The ALTER TABLE procedure SQLite lacks (MIGRATION-FORMAT §10), self-contained: create the target shape under `<table>__rebuild`, copy the rows through the rendered column mapping, drop the old table, rename, recreate the indexes, then `PRAGMA foreign_key_check` inside the transaction.",
249
+ "type": "object",
250
+ "properties": {
251
+ "kind": {
252
+ "const": "rebuild"
253
+ },
254
+ "table": {
255
+ "type": "string",
256
+ "pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
257
+ },
258
+ "create": {
259
+ "description": "The CREATE statements of the temporary table.",
260
+ "type": "array",
261
+ "minItems": 1,
262
+ "items": {
263
+ "type": "string",
264
+ "minLength": 1
265
+ }
266
+ },
267
+ "copy": {
268
+ "description": "The INSERT … SELECT that carries every row across the column mapping.",
269
+ "type": "string",
270
+ "minLength": 1
271
+ },
272
+ "indexes": {
273
+ "description": "The index statements recreated under the final name.",
274
+ "type": "array",
275
+ "items": {
276
+ "type": "string",
277
+ "minLength": 1
278
+ }
279
+ },
280
+ "note": {
281
+ "type": "string"
282
+ }
283
+ },
284
+ "required": [
285
+ "kind",
286
+ "table",
287
+ "create",
288
+ "copy",
289
+ "indexes"
290
+ ],
291
+ "additionalProperties": false
219
292
  }
220
293
  }
221
294
  }
@@ -58,6 +58,12 @@
58
58
  },
59
59
  {
60
60
  "$ref": "#/$defs/deriveStep"
61
+ },
62
+ {
63
+ "$ref": "#/$defs/sqlStep"
64
+ },
65
+ {
66
+ "$ref": "#/$defs/rebuildStep"
61
67
  }
62
68
  ]
63
69
  },
@@ -216,6 +222,73 @@
216
222
  "columns"
217
223
  ],
218
224
  "additionalProperties": false
225
+ },
226
+ "sqlStep": {
227
+ "description": "A DATA step spelled directly as one SQL statement (MIGRATION-FORMAT §9.4): the planner's fold of a dropped column into the document, the move of a document member into its new column, an R*Tree backfill. Executed exactly as a ddl step; distinct in kind so a reviewer reads intent and a dry run shows it as data.",
228
+ "type": "object",
229
+ "properties": {
230
+ "kind": {
231
+ "const": "sql"
232
+ },
233
+ "sql": {
234
+ "type": "string",
235
+ "minLength": 1
236
+ },
237
+ "note": {
238
+ "type": "string"
239
+ }
240
+ },
241
+ "required": [
242
+ "kind",
243
+ "sql"
244
+ ],
245
+ "additionalProperties": false
246
+ },
247
+ "rebuildStep": {
248
+ "description": "The ALTER TABLE procedure SQLite lacks (MIGRATION-FORMAT §10), self-contained: create the target shape under `<table>__rebuild`, copy the rows through the rendered column mapping, drop the old table, rename, recreate the indexes, then `PRAGMA foreign_key_check` inside the transaction.",
249
+ "type": "object",
250
+ "properties": {
251
+ "kind": {
252
+ "const": "rebuild"
253
+ },
254
+ "table": {
255
+ "type": "string",
256
+ "pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
257
+ },
258
+ "create": {
259
+ "description": "The CREATE statements of the temporary table.",
260
+ "type": "array",
261
+ "minItems": 1,
262
+ "items": {
263
+ "type": "string",
264
+ "minLength": 1
265
+ }
266
+ },
267
+ "copy": {
268
+ "description": "The INSERT … SELECT that carries every row across the column mapping.",
269
+ "type": "string",
270
+ "minLength": 1
271
+ },
272
+ "indexes": {
273
+ "description": "The index statements recreated under the final name.",
274
+ "type": "array",
275
+ "items": {
276
+ "type": "string",
277
+ "minLength": 1
278
+ }
279
+ },
280
+ "note": {
281
+ "type": "string"
282
+ }
283
+ },
284
+ "required": [
285
+ "kind",
286
+ "table",
287
+ "create",
288
+ "copy",
289
+ "indexes"
290
+ ],
291
+ "additionalProperties": false
219
292
  }
220
293
  }
221
294
  }
@@ -1,176 +1,238 @@
1
1
  {
2
- "$schema": "http://json-schema.org/draft-07/schema#",
3
- "$id": "https://jarenjs.dev/schemas/jaren-model/draft-07",
4
- "title": "Jaren model document",
5
- "description": "A jaren-model document declares the collections of a store: each collection is a JSON Schema for its documents, a key declaration (an RFC 6901 pointer for caller-supplied keys, or null with a declared identity for store-allocated ones), and the indexes over singular JSONPath expressions. A model declares collections (the storage subset: every property JSONB, no relations), entities (the relational subset: keys, typed columns, relations via the x-entity vocabulary), or both; vocabulary is added to THIS format, never as a second format.",
6
- "type": "object",
7
- "properties": {
8
- "$model": {
9
- "description": "The model format version.",
10
- "const": "0.1"
11
- },
12
- "collections": {
13
- "description": "Collection name (an identifier) to collection declaration.",
14
- "type": "object",
15
- "minProperties": 1,
16
- "propertyNames": {
17
- "pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
18
- },
19
- "additionalProperties": {
20
- "$ref": "#/definitions/collection"
21
- }
22
- },
23
- "entities": {
24
- "description": "Entity name (an identifier) to entity declaration. The x-entity vocabulary lives INSIDE each entity schema's property subschemas and is documented in MODEL-FORMAT.md §9; stripping it leaves a plain JSON Schema.",
25
- "type": "object",
26
- "minProperties": 1,
27
- "propertyNames": {
28
- "pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
29
- },
30
- "additionalProperties": {
31
- "$ref": "#/definitions/entity"
32
- }
33
- }
34
- },
35
- "required": [
36
- "$model"
37
- ],
38
- "additionalProperties": false,
39
- "definitions": {
40
- "collection": {
41
- "description": "One collection: a document schema, the key declaration, and the declared indexes. A collection without a key pointer MUST declare how keys are allocated; allocation is declared, never guessed.",
42
- "type": "object",
43
- "properties": {
44
- "schema": {
45
- "description": "The JSON Schema every stored document validates against (through the injected hook). Also the type source for indexed paths.",
46
- "type": "object"
47
- },
48
- "key": {
49
- "description": "Where the caller-supplied key lives in the document, as an RFC 6901 pointer selecting at least one member — or null when the store allocates.",
50
- "type": [
51
- "string",
52
- "null"
53
- ],
54
- "pattern": "^/"
55
- },
56
- "identity": {
57
- "description": "How keys come to be: 'caller' (the default when a key pointer is declared), or — only with \"key\": null — 'uuid' (crypto.randomUUID) or 'integer' (database-allocated).",
58
- "enum": [
59
- "caller",
60
- "uuid",
61
- "integer"
62
- ]
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://jarenjs.dev/schemas/jaren-model/draft-07",
4
+ "title": "Jaren model document",
5
+ "description": "A jaren-model document declares the collections of a store: each collection is a JSON Schema for its documents, a key declaration (an RFC 6901 pointer for caller-supplied keys, or null with a declared identity for store-allocated ones), and the indexes over singular JSONPath expressions. A model declares collections (the storage subset: every property JSONB, no relations), entities (the relational subset: keys, typed columns, relations via the x-entity vocabulary), or both; vocabulary is added to THIS format, never as a second format.",
6
+ "type": "object",
7
+ "properties": {
8
+ "$model": {
9
+ "description": "The model format version.",
10
+ "const": "0.1"
63
11
  },
64
- "indexes": {
65
- "description": "Declared indexes; each singular path becomes a generated column plus an index.",
66
- "type": "array",
67
- "items": {
68
- "$ref": "#/definitions/index"
69
- }
12
+ "collections": {
13
+ "description": "Collection name (an identifier) to collection declaration.",
14
+ "type": "object",
15
+ "minProperties": 1,
16
+ "propertyNames": {
17
+ "pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
18
+ },
19
+ "additionalProperties": {
20
+ "$ref": "#/definitions/collection"
21
+ }
70
22
  },
71
- "x-rename": {
72
- "description": "Migration hint: this collection was previously named the given identifier. Read by the migration planner (a rename cannot be inferred from a diff); ignored by the store.",
73
- "type": "string",
74
- "pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
23
+ "entities": {
24
+ "description": "Entity name (an identifier) to entity declaration. The x-entity vocabulary lives INSIDE each entity schema's property subschemas and is documented in MODEL-FORMAT.md \u00a79; stripping it leaves a plain JSON Schema.",
25
+ "type": "object",
26
+ "minProperties": 1,
27
+ "propertyNames": {
28
+ "pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
29
+ },
30
+ "additionalProperties": {
31
+ "$ref": "#/definitions/entity"
32
+ }
75
33
  }
76
- },
77
- "required": [
78
- "schema"
79
- ],
80
- "additionalProperties": false
81
34
  },
82
- "index": {
83
- "description": "One index over a singular JSONPath expression (or a composite over several). A non-singular path is rejected at open (JD0004). A derived index computes indexable scalars from the selected member instead of indexing it directly.",
84
- "type": "object",
85
- "properties": {
86
- "name": {
87
- "description": "The index name, unique within its collection.",
88
- "type": "string",
89
- "pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
90
- },
91
- "path": {
92
- "description": "A singular JSONPath expression into the stored document ($.email), or a non-empty array of them for a composite index.",
93
- "oneOf": [
94
- {
95
- "type": "string",
96
- "minLength": 1
35
+ "required": [
36
+ "$model"
37
+ ],
38
+ "additionalProperties": false,
39
+ "definitions": {
40
+ "collection": {
41
+ "description": "One collection: a document schema, the key declaration, and the declared indexes. A collection without a key pointer MUST declare how keys are allocated; allocation is declared, never guessed.",
42
+ "type": "object",
43
+ "properties": {
44
+ "schema": {
45
+ "description": "The JSON Schema every stored document validates against (through the injected hook). Also the type source for indexed paths.",
46
+ "type": "object"
47
+ },
48
+ "key": {
49
+ "description": "Where the caller-supplied key lives in the document, as an RFC 6901 pointer selecting at least one member \u2014 or null when the store allocates.",
50
+ "type": [
51
+ "string",
52
+ "null"
53
+ ],
54
+ "pattern": "^/"
55
+ },
56
+ "identity": {
57
+ "description": "How keys come to be: 'caller' (the default when a key pointer is declared), or \u2014 only with \"key\": null \u2014 'uuid' (crypto.randomUUID) or 'integer' (database-allocated).",
58
+ "enum": [
59
+ "caller",
60
+ "uuid",
61
+ "integer"
62
+ ]
63
+ },
64
+ "indexes": {
65
+ "description": "Declared indexes; each singular path becomes a generated column plus an index.",
66
+ "type": "array",
67
+ "items": {
68
+ "$ref": "#/definitions/index"
69
+ }
70
+ },
71
+ "x-rename": {
72
+ "description": "Migration hint: this collection was previously named the given identifier. Read by the migration planner (a rename cannot be inferred from a diff); ignored by the store.",
73
+ "type": "string",
74
+ "pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
75
+ }
97
76
  },
98
- {
99
- "type": "array",
100
- "items": {
101
- "type": "string",
102
- "minLength": 1
103
- },
104
- "minItems": 1
105
- }
106
- ]
107
- },
108
- "unique": {
109
- "description": "Whether the index enforces uniqueness. Defaults to false.",
110
- "type": "boolean"
111
- },
112
- "derive": {
113
- "description": "Derive columns from the selected member instead of indexing it: 'geohash' (one TEXT cell column, precision required), 'bbox' (four REAL columns — west, south, east, north), or 'vector' (one packed BLOB column holding the l2-normalized Float32 form of an array of numbers, dims required; stored on every driver, with no B-tree over it — a fetch-and-rank plan reads it whole). A derived index is never unique.",
114
- "enum": [
115
- "geohash",
116
- "bbox",
117
- "vector"
118
- ]
77
+ "required": [
78
+ "schema"
79
+ ],
80
+ "additionalProperties": false
119
81
  },
120
- "precision": {
121
- "description": "Geohash cell length in characters, 1-12. Required beside derive: 'geohash' and refused anywhere else there is no safe default, because the right cell size depends on the query radius, which the model cannot know.",
122
- "type": "integer",
123
- "minimum": 1,
124
- "maximum": 12
82
+ "index": {
83
+ "description": "One index over a singular JSONPath expression (or a composite over several). A non-singular path is rejected at open (JD0004). A derived index computes indexable scalars from the selected member instead of indexing it directly. An index may instead declare an expression: a closed computation over members, scalars and host-declared deterministic functions.",
84
+ "type": "object",
85
+ "properties": {
86
+ "name": {
87
+ "description": "The index name, unique within its collection.",
88
+ "type": "string",
89
+ "pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
90
+ },
91
+ "path": {
92
+ "description": "A singular JSONPath expression into the stored document ($.email), or a non-empty array of them for a composite index.",
93
+ "oneOf": [
94
+ {
95
+ "type": "string",
96
+ "minLength": 1
97
+ },
98
+ {
99
+ "type": "array",
100
+ "items": {
101
+ "type": "string",
102
+ "minLength": 1
103
+ },
104
+ "minItems": 1
105
+ }
106
+ ]
107
+ },
108
+ "expression": {
109
+ "description": "Index a computed value rather than a member: a closed expression over declared members, JSON scalars and functions the host declared deterministic. Mutually exclusive with path and derive (JD0004) \u2014 an expression names the members it reads itself.",
110
+ "allOf": [
111
+ {
112
+ "$ref": "#/definitions/expression"
113
+ }
114
+ ]
115
+ },
116
+ "unique": {
117
+ "description": "Whether the index enforces uniqueness. Defaults to false.",
118
+ "type": "boolean"
119
+ },
120
+ "derive": {
121
+ "description": "Derive columns from the selected member instead of indexing it: 'geohash' (one TEXT cell column, precision required), 'bbox' (four REAL columns \u2014 west, south, east, north), or 'vector' (one packed BLOB column holding the l2-normalized Float32 form of an array of numbers, dims required; stored on every driver, with no B-tree over it \u2014 a fetch-and-rank plan reads it whole). A derived index is never unique.",
122
+ "enum": [
123
+ "geohash",
124
+ "bbox",
125
+ "vector"
126
+ ]
127
+ },
128
+ "precision": {
129
+ "description": "Geohash cell length in characters, 1-12. Required beside derive: 'geohash' and refused anywhere else \u2014 there is no safe default, because the right cell size depends on the query radius, which the model cannot know.",
130
+ "type": "integer",
131
+ "minimum": 1,
132
+ "maximum": 12
133
+ },
134
+ "dims": {
135
+ "description": "The width of a derive: 'vector' column in components, 1-8192. Required beside derive: 'vector' and refused anywhere else: the width is the column's identity \u2014 two widths over one path are two columns \u2014 and a member that is not an array of exactly this many finite numbers stores NULL in the column (the document itself still stores). Constrain the member with minItems/maxItems equal to dims in the collection's schema so a wrong-width vector is refused at the write instead.",
136
+ "type": "integer",
137
+ "minimum": 1,
138
+ "maximum": 8192
139
+ },
140
+ "physical": {
141
+ "description": "The shape a derive: 'bbox' index takes on disk: 'columns' (the default) is four generated columns under one B-tree; 'rtree' keeps the same four columns and adds a SQLite R*Tree virtual table beside the collection, kept in sync by declared triggers, with no B-tree over the columns. The logical meaning of derive: 'bbox' is identical either way. Refused on any other index kind, and on a driver without the module it falls back to 'columns' and says so through explain().prefilters[].via.",
142
+ "enum": [
143
+ "columns",
144
+ "rtree"
145
+ ]
146
+ }
147
+ },
148
+ "required": [
149
+ "name"
150
+ ],
151
+ "additionalProperties": false
125
152
  },
126
- "dims": {
127
- "description": "The width of a derive: 'vector' column in components, 1-8192. Required beside derive: 'vector' and refused anywhere else: the width is the column's identity two widths over one path are two columns — and a member that is not an array of exactly this many finite numbers stores NULL in the column (the document itself still stores). Constrain the member with minItems/maxItems equal to dims in the collection's schema so a wrong-width vector is refused at the write instead.",
128
- "type": "integer",
129
- "minimum": 1,
130
- "maximum": 8192
153
+ "entity": {
154
+ "description": "One entity: an object schema whose top-level properties map to columns per the hybrid rule (scalars become typed columns, nested shapes stay JSONB); keys, indexes, defaults and relations are declared with x-entity inside the schema.",
155
+ "type": "object",
156
+ "properties": {
157
+ "schema": {
158
+ "type": "object"
159
+ },
160
+ "x-rename": {
161
+ "type": "string",
162
+ "description": "Declares this entity as the rename of the named from-model entity (MIGRATION-FORMAT \u00a79): a rename is declared, never inferred."
163
+ }
164
+ },
165
+ "required": [
166
+ "schema"
167
+ ],
168
+ "additionalProperties": false
131
169
  },
132
- "physical": {
133
- "description": "The shape a derive: 'bbox' index takes on disk: 'columns' (the default) is four generated columns under one B-tree; 'rtree' keeps the same four columns and adds a SQLite R*Tree virtual table beside the collection, kept in sync by declared triggers, with no B-tree over the columns. The logical meaning of derive: 'bbox' is identical either way. Refused on any other index kind, and on a driver without the module it falls back to 'columns' and says so through explain().prefilters[].via.",
134
- "enum": [
135
- "columns",
136
- "rtree"
137
- ]
170
+ "expression": {
171
+ "description": "One node of an index expression: a member path, a JSON scalar, or a call to a function the host declared. Exactly one of the three, and nothing else \u2014 an index expression is never SQL text, and the function it names is resolved for arity and determinism at open (JD0004).",
172
+ "type": "object",
173
+ "oneOf": [
174
+ {
175
+ "required": [
176
+ "member"
177
+ ],
178
+ "properties": {
179
+ "member": {
180
+ "description": "A singular JSONPath expression into the stored document ($.email).",
181
+ "type": "string",
182
+ "minLength": 1
183
+ }
184
+ },
185
+ "additionalProperties": false
186
+ },
187
+ {
188
+ "required": [
189
+ "value"
190
+ ],
191
+ "properties": {
192
+ "value": {
193
+ "description": "A JSON scalar. A null or a compound has no place in an index expression.",
194
+ "type": [
195
+ "string",
196
+ "number",
197
+ "boolean"
198
+ ]
199
+ }
200
+ },
201
+ "additionalProperties": false
202
+ },
203
+ {
204
+ "required": [
205
+ "call"
206
+ ],
207
+ "properties": {
208
+ "call": {
209
+ "description": "The name of a function the host declared through openStore({ expressions }). The store refuses a name it was not given, a wrong arity, and one not declared deterministic \u2014 before any DDL.",
210
+ "type": "string",
211
+ "pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
212
+ },
213
+ "args": {
214
+ "description": "The arguments, in order. Order is significant: sub(a, b) and sub(b, a) are different expressions and different columns.",
215
+ "type": "array",
216
+ "items": {
217
+ "$ref": "#/definitions/expression"
218
+ }
219
+ }
220
+ },
221
+ "additionalProperties": false
222
+ }
223
+ ]
138
224
  }
139
- },
140
- "required": [
141
- "name",
142
- "path"
143
- ],
144
- "additionalProperties": false
145
225
  },
146
- "entity": {
147
- "description": "One entity: an object schema whose top-level properties map to columns per the hybrid rule (scalars become typed columns, nested shapes stay JSONB); keys, indexes, defaults and relations are declared with x-entity inside the schema.",
148
- "type": "object",
149
- "properties": {
150
- "schema": {
151
- "type": "object"
226
+ "anyOf": [
227
+ {
228
+ "required": [
229
+ "collections"
230
+ ]
152
231
  },
153
- "x-rename": {
154
- "type": "string",
155
- "description": "Declares this entity as the rename of the named from-model entity (MIGRATION-FORMAT §9): a rename is declared, never inferred."
232
+ {
233
+ "required": [
234
+ "entities"
235
+ ]
156
236
  }
157
- },
158
- "required": [
159
- "schema"
160
- ],
161
- "additionalProperties": false
162
- }
163
- },
164
- "anyOf": [
165
- {
166
- "required": [
167
- "collections"
168
- ]
169
- },
170
- {
171
- "required": [
172
- "entities"
173
- ]
174
- }
175
- ]
237
+ ]
176
238
  }