@jarenjs/db 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +397 -0
- package/README.md +218 -0
- package/dist/types/algebra.d.ts +133 -0
- package/dist/types/app.d.ts +49 -0
- package/dist/types/capture.d.ts +85 -0
- package/dist/types/cli.d.ts +2 -0
- package/dist/types/dag-job.d.ts +40 -0
- package/dist/types/ddl.d.ts +170 -0
- package/dist/types/dialect.d.ts +130 -0
- package/dist/types/dialects/sqlite.d.ts +9 -0
- package/dist/types/driver.d.ts +128 -0
- package/dist/types/drivers/bun.d.ts +47 -0
- package/dist/types/drivers/node.d.ts +37 -0
- package/dist/types/drivers/wasm.d.ts +65 -0
- package/dist/types/emit-model.d.ts +44 -0
- package/dist/types/emit.d.ts +72 -0
- package/dist/types/entity.d.ts +23 -0
- package/dist/types/errors.d.ts +165 -0
- package/dist/types/graph.d.ts +28 -0
- package/dist/types/index.d.ts +35 -0
- package/dist/types/jobs.d.ts +134 -0
- package/dist/types/live.d.ts +62 -0
- package/dist/types/migrate.d.ts +163 -0
- package/dist/types/model.d.ts +36 -0
- package/dist/types/patch-sql.d.ts +37 -0
- package/dist/types/plan.d.ts +119 -0
- package/dist/types/profile.d.ts +80 -0
- package/dist/types/query.d.ts +100 -0
- package/dist/types/residual.d.ts +50 -0
- package/dist/types/store.d.ts +53 -0
- package/dist/types/tracker.d.ts +43 -0
- package/dist/types/typed.d.ts +15 -0
- package/dist/types/types.d.ts +26 -0
- package/dist/types/udf.d.ts +70 -0
- package/dist/types/window.d.ts +52 -0
- package/docs/JOBS-FORMAT.md +218 -0
- package/docs/LIVE-FORMAT.md +348 -0
- package/docs/MIGRATION-FORMAT.md +302 -0
- package/docs/MODEL-FORMAT.md +928 -0
- package/package.json +81 -0
- package/schemas/jaren-migration.draft-07.schema.json +144 -0
- package/schemas/jaren-migration.schema.json +144 -0
- package/schemas/jaren-model.draft-07.schema.json +149 -0
- package/schemas/jaren-model.schema.json +149 -0
- package/src/algebra.js +105 -0
- package/src/app.js +108 -0
- package/src/capture.js +584 -0
- package/src/cli.js +264 -0
- package/src/dag-job.js +86 -0
- package/src/ddl.js +588 -0
- package/src/dialect.js +297 -0
- package/src/dialects/sqlite.js +175 -0
- package/src/driver.js +419 -0
- package/src/drivers/bun.js +101 -0
- package/src/drivers/node.js +93 -0
- package/src/drivers/wasm.js +178 -0
- package/src/emit-model.js +208 -0
- package/src/emit.js +393 -0
- package/src/entity.js +367 -0
- package/src/errors.js +173 -0
- package/src/graph.js +101 -0
- package/src/index.js +64 -0
- package/src/jobs.js +507 -0
- package/src/live.js +899 -0
- package/src/migrate.js +1411 -0
- package/src/model.js +476 -0
- package/src/patch-sql.js +150 -0
- package/src/plan.js +1038 -0
- package/src/profile.js +131 -0
- package/src/query.js +1010 -0
- package/src/residual.js +91 -0
- package/src/store.js +1422 -0
- package/src/tracker.js +776 -0
- package/src/typed.js +19 -0
- package/src/types.js +36 -0
- package/src/udf.js +132 -0
- package/src/window.js +125 -0
- package/types/app.d.ts +36 -0
- package/types/bun.d.ts +9 -0
- package/types/index.d.ts +592 -0
- package/types/node.d.ts +15 -0
- package/types/typed.d.ts +108 -0
- package/types/wasm.d.ts +5 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
//@ts-check
|
|
2
|
+
/**
|
|
3
|
+
* @file The wasm binding: an INJECTED handle behind the driver
|
|
4
|
+
* contract. This module imports no runtime builtin at all — the host
|
|
5
|
+
* (a browser, a worker) supplies the SQLite build, and this driver
|
|
6
|
+
* only adapts it. Every handle method may return a value or a promise;
|
|
7
|
+
* a main-thread OPFS-backed build is asynchronous and that is exactly
|
|
8
|
+
* why the public store surface is.
|
|
9
|
+
*
|
|
10
|
+
* The injected contract:
|
|
11
|
+
*
|
|
12
|
+
* handle = {
|
|
13
|
+
* open(path, options) -> raw | Promise<raw>,
|
|
14
|
+
* synchronous?: boolean, // default false
|
|
15
|
+
* declares?: { userFunctions?, deterministicIndexableFunctions?,
|
|
16
|
+
* sessions? } // default all false
|
|
17
|
+
* }
|
|
18
|
+
* raw = { exec(sql), prepare(sql) -> { run, get, all, iterate? },
|
|
19
|
+
* close(), registerFunction?, registerAggregate?, session? }
|
|
20
|
+
*
|
|
21
|
+
* Capability truth still comes from the probe: whatever the handle
|
|
22
|
+
* declares is intersected with what the loaded library actually
|
|
23
|
+
* compiled in.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { chain, openConnection } from '../driver.js';
|
|
27
|
+
import { sqliteDialect } from '../dialects/sqlite.js';
|
|
28
|
+
import { DbCompileError } from '../errors.js';
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Adapt an already-constructed `sqlite3.oo1` database (the official
|
|
32
|
+
* SQLite wasm build's object API) into the raw contract. The oo1 API
|
|
33
|
+
* is SYNCHRONOUS — wasm SQLite computes in place and the SAH-pool
|
|
34
|
+
* OPFS VFS does synchronous I/O inside a dedicated worker — which is
|
|
35
|
+
* exactly what keeps journal capture, live queries and the job queue
|
|
36
|
+
* working unchanged in a browser.
|
|
37
|
+
*
|
|
38
|
+
* Statements are REUSED by the store's prepared caches: every
|
|
39
|
+
* operation ends in `reset()`, never `finalize()`. oo1 user functions
|
|
40
|
+
* receive a context pointer first — stripped here — and register
|
|
41
|
+
* variadic (`arity: -1`), matching the engine's fragment shapes.
|
|
42
|
+
* @param {any} sqlite3 - the loaded sqlite3 module (for `capi`)
|
|
43
|
+
* @param {any} db - an `sqlite3.oo1.DB`-shaped database
|
|
44
|
+
* @returns {any} the raw binding for {@link openConnection}
|
|
45
|
+
*/
|
|
46
|
+
export function adaptOo1Database(sqlite3, db) {
|
|
47
|
+
return {
|
|
48
|
+
/** @param {string} sql */
|
|
49
|
+
exec: (sql) => {
|
|
50
|
+
db.exec(sql);
|
|
51
|
+
},
|
|
52
|
+
/** @param {string} sql */
|
|
53
|
+
prepare: (sql) => {
|
|
54
|
+
const statement = db.prepare(sql);
|
|
55
|
+
const bind = (params) => {
|
|
56
|
+
statement.reset();
|
|
57
|
+
if (params.length > 0) statement.bind(params);
|
|
58
|
+
};
|
|
59
|
+
return {
|
|
60
|
+
run: (params = []) => {
|
|
61
|
+
bind(params);
|
|
62
|
+
statement.step();
|
|
63
|
+
statement.reset();
|
|
64
|
+
return {
|
|
65
|
+
changes: db.changes(),
|
|
66
|
+
lastInsertRowid: Number(
|
|
67
|
+
sqlite3.capi.sqlite3_last_insert_rowid(db)),
|
|
68
|
+
};
|
|
69
|
+
},
|
|
70
|
+
get: (params = []) => {
|
|
71
|
+
bind(params);
|
|
72
|
+
const row = statement.step() ? statement.get({}) : undefined;
|
|
73
|
+
statement.reset();
|
|
74
|
+
return row;
|
|
75
|
+
},
|
|
76
|
+
all: (params = []) => {
|
|
77
|
+
bind(params);
|
|
78
|
+
const rows = [];
|
|
79
|
+
while (statement.step()) rows.push(statement.get({}));
|
|
80
|
+
statement.reset();
|
|
81
|
+
return rows;
|
|
82
|
+
},
|
|
83
|
+
iterate: (params = []) => {
|
|
84
|
+
bind(params);
|
|
85
|
+
return {
|
|
86
|
+
next() {
|
|
87
|
+
if (statement.step()) return { done: false, value: statement.get({}) };
|
|
88
|
+
statement.reset();
|
|
89
|
+
return { done: true, value: undefined };
|
|
90
|
+
},
|
|
91
|
+
return(value) {
|
|
92
|
+
statement.reset();
|
|
93
|
+
return { done: true, value };
|
|
94
|
+
},
|
|
95
|
+
[Symbol.iterator]() { return this; },
|
|
96
|
+
};
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
},
|
|
100
|
+
close: () => db.close(),
|
|
101
|
+
registerFunction: (name, options, fn) => db.createFunction(name,
|
|
102
|
+
(_context, ...args) => fn(...args),
|
|
103
|
+
{ deterministic: options?.deterministic === true, arity: -1 }),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Build the injected HANDLE from a loaded sqlite3 module — the D6
|
|
109
|
+
* recipe: the host loads the wasm build and picks the database class
|
|
110
|
+
* (`sqlite3.oo1.DB` for `:memory:`, the SAH-pool util's `OpfsSAHPoolDb`
|
|
111
|
+
* for OPFS persistence), and this package only adapts it.
|
|
112
|
+
*
|
|
113
|
+
* `sessions` is deliberately NOT declared even though the canonical
|
|
114
|
+
* wasm build compiles `ENABLE_SESSION`: this adapter does not yet map
|
|
115
|
+
* the session C API, so capture runs in the journal mode — stated in
|
|
116
|
+
* the capability matrix, adapting it is a roadmap item.
|
|
117
|
+
* @param {any} sqlite3 - the loaded sqlite3 module
|
|
118
|
+
* @param {{ DbClass?: any }} [handleOptions] - the database class to
|
|
119
|
+
* construct (default `sqlite3.oo1.DB`)
|
|
120
|
+
* @returns {any} a handle for {@link wasmDriver}
|
|
121
|
+
*/
|
|
122
|
+
export function sqlite3Handle(sqlite3, handleOptions) {
|
|
123
|
+
if (sqlite3 === null || typeof sqlite3 !== 'object'
|
|
124
|
+
|| typeof sqlite3?.oo1?.DB !== 'function') {
|
|
125
|
+
throw new DbCompileError('JD0003',
|
|
126
|
+
'sqlite3Handle needs a loaded sqlite3 module exposing oo1.DB');
|
|
127
|
+
}
|
|
128
|
+
const DbClass = handleOptions?.DbClass ?? sqlite3.oo1.DB;
|
|
129
|
+
return {
|
|
130
|
+
synchronous: true,
|
|
131
|
+
declares: {
|
|
132
|
+
userFunctions: true,
|
|
133
|
+
deterministicIndexableFunctions: true,
|
|
134
|
+
sessions: false,
|
|
135
|
+
},
|
|
136
|
+
/**
|
|
137
|
+
* @param {string} path
|
|
138
|
+
* @param {{ readOnly?: boolean }} [options]
|
|
139
|
+
*/
|
|
140
|
+
open: (path, options) => adaptOo1Database(sqlite3,
|
|
141
|
+
new DbClass(path ?? ':memory:', options?.readOnly === true ? 'r' : 'c')),
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* The wasm driver over an injected handle.
|
|
147
|
+
* @param {any} handle - The host-supplied SQLite handle (see the file
|
|
148
|
+
* header for the contract)
|
|
149
|
+
* @returns {any}
|
|
150
|
+
*/
|
|
151
|
+
export function wasmDriver(handle) {
|
|
152
|
+
if (handle === null || typeof handle !== 'object'
|
|
153
|
+
|| typeof handle.open !== 'function') {
|
|
154
|
+
throw new DbCompileError('JD0003',
|
|
155
|
+
'wasmDriver needs an injected handle exposing open(path, options)');
|
|
156
|
+
}
|
|
157
|
+
return Object.freeze({
|
|
158
|
+
name: 'wasm-sqlite',
|
|
159
|
+
dialect: sqliteDialect,
|
|
160
|
+
/**
|
|
161
|
+
* @param {string} path
|
|
162
|
+
* @param {any} [options]
|
|
163
|
+
* @returns {any}
|
|
164
|
+
*/
|
|
165
|
+
open: (path, options) => chain(handle.open(path, options), (raw) =>
|
|
166
|
+
openConnection(raw, {
|
|
167
|
+
dialect: sqliteDialect,
|
|
168
|
+
synchronous: handle.synchronous === true,
|
|
169
|
+
declared: {
|
|
170
|
+
sessions: handle.declares?.sessions === true,
|
|
171
|
+
userFunctions: handle.declares?.userFunctions === true,
|
|
172
|
+
deterministicIndexableFunctions:
|
|
173
|
+
handle.declares?.deterministicIndexableFunctions === true,
|
|
174
|
+
aggregateFunctions: handle.declares?.aggregateFunctions === true,
|
|
175
|
+
},
|
|
176
|
+
})),
|
|
177
|
+
});
|
|
178
|
+
}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
//@ts-check
|
|
2
|
+
/**
|
|
3
|
+
* @file Entity types from models (D17): build an EMIT MODEL DOCUMENT
|
|
4
|
+
* — the published contract from EMIT-FORMAT.md — that renders entity
|
|
5
|
+
* interfaces, input variants and the typed-store metadata from the
|
|
6
|
+
* same model document the runtime validates against.
|
|
7
|
+
*
|
|
8
|
+
* The seam (EMIT-FORMAT §4.1, recorded here as the order demanded):
|
|
9
|
+
* the schema compiler is INJECTED (`options.compile` is
|
|
10
|
+
* `compileEmitModel` from `@jarenjs/emit`), called with
|
|
11
|
+
* `extensions: ['x-entity']` so the vocabulary rides the member nodes
|
|
12
|
+
* verbatim; this module then post-processes the MODEL DOCUMENT —
|
|
13
|
+
* replacing relation member types with references, flipping
|
|
14
|
+
* optionality, adding declarations — and never renders a character of
|
|
15
|
+
* TypeScript itself. Emit stays database-free, db stays
|
|
16
|
+
* renderer-free, and the injection keeps `@jarenjs/emit` out of db's
|
|
17
|
+
* dependency graph (the generator script wires the two).
|
|
18
|
+
*
|
|
19
|
+
* What the artifact says, deliberately:
|
|
20
|
+
* - entity interfaces are CLOSED objects (excess-property checking is
|
|
21
|
+
* the point of generated types; the runtime validator stays
|
|
22
|
+
* authoritative for what a database accepts);
|
|
23
|
+
* - relation members are optional references — present only when a
|
|
24
|
+
* graph load included them; `date-time`/`date` strings carry the
|
|
25
|
+
* `DateTime` brand so the linq date operators light up;
|
|
26
|
+
* - the `<Name>Input` variant makes defaulted and generated members
|
|
27
|
+
* optional, drops to-one/to-many relation members (`create`/`add`
|
|
28
|
+
* refuse them), and types many-to-many members as key-or-document
|
|
29
|
+
* arrays (what `add()` accepts);
|
|
30
|
+
* - `EntityMetaMap` carries doc/input/key/relations per entity — the
|
|
31
|
+
* generic typed-store surface (`@jarenjs/db/typed`) binds to it.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
import { normalizeEntities } from './model.js';
|
|
35
|
+
|
|
36
|
+
const primitive = (name) => ({ kind: 'primitive', primitive: name });
|
|
37
|
+
const ref = (name) => ({ kind: 'ref', ref: name });
|
|
38
|
+
const arrayOf = (items) => ({ kind: 'array', items });
|
|
39
|
+
const unionOf = (options) => ({ kind: 'union', options });
|
|
40
|
+
const literal = (value) => ({ kind: 'literal', value });
|
|
41
|
+
const member = (name, type, required, doc = []) =>
|
|
42
|
+
({ kind: 'member', name, type, required, constraints: [], doc });
|
|
43
|
+
const objectOf = (members) => ({ kind: 'object', members });
|
|
44
|
+
const declaration = (name, type, doc = []) =>
|
|
45
|
+
({ kind: 'declaration', name, type, constraints: [], doc });
|
|
46
|
+
|
|
47
|
+
/** The `DateTime` brand: structurally the linq brand, generated
|
|
48
|
+
* inline so the artifact stands alone. */
|
|
49
|
+
const DATE_TIME_DECLARATION = declaration('DateTime', {
|
|
50
|
+
kind: 'intersection',
|
|
51
|
+
parts: [primitive('string'), objectOf([
|
|
52
|
+
member('__jarenTag', literal('date-time'), true),
|
|
53
|
+
])],
|
|
54
|
+
}, ['An RFC 3339 string branded for the date operators;',
|
|
55
|
+
'structurally identical to the @jarenjs/linq brand.']);
|
|
56
|
+
|
|
57
|
+
const keyTypeOf = (entity) => {
|
|
58
|
+
const parts = entity.keys.map((key) =>
|
|
59
|
+
primitive(entity.properties.get(key).type === 'string' ? 'string' : 'number'));
|
|
60
|
+
if (parts.length === 1) return parts[0];
|
|
61
|
+
return objectOf(entity.keys.map((key, i) => member(key, parts[i], true)));
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Build the emit-model document for a model's entities.
|
|
66
|
+
* @param {any} model - a jaren-model document with `entities`
|
|
67
|
+
* @param {{ compile: (schema: any, options?: any) => any,
|
|
68
|
+
* source?: string, reserved?: string[] }} options - `compile` is
|
|
69
|
+
* `compileEmitModel` (injected; see the header)
|
|
70
|
+
* @returns {any} an EMIT-FORMAT `0.1` model document
|
|
71
|
+
*/
|
|
72
|
+
export function entityEmitModel(model, options) {
|
|
73
|
+
const compile = options?.compile;
|
|
74
|
+
if (typeof compile !== 'function')
|
|
75
|
+
throw new TypeError("entityEmitModel needs { compile: compileEmitModel } injected");
|
|
76
|
+
const entities = normalizeEntities(model);
|
|
77
|
+
const entityNames = [...entities.keys()];
|
|
78
|
+
|
|
79
|
+
// every entity name, input name and the brand are reserved up front
|
|
80
|
+
// so nested-shape hints can never steal them
|
|
81
|
+
const taken = [
|
|
82
|
+
...(options?.reserved ?? []),
|
|
83
|
+
'DateTime', 'Entities', 'EntityInputs', 'EntityMetaMap',
|
|
84
|
+
...entityNames,
|
|
85
|
+
...entityNames.map((name) => `${name}Input`),
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
/** @type {any[]} */
|
|
89
|
+
const declarations = [];
|
|
90
|
+
let usesDateTime = false;
|
|
91
|
+
|
|
92
|
+
for (const entity of entities.values()) {
|
|
93
|
+
const compiled = compile(entity.schema, {
|
|
94
|
+
name: entity.name,
|
|
95
|
+
extensions: ['x-entity'],
|
|
96
|
+
openObjects: 'closed',
|
|
97
|
+
reserved: taken.filter((name) => name !== entity.name),
|
|
98
|
+
});
|
|
99
|
+
for (const compiledDeclaration of compiled.declarations) {
|
|
100
|
+
if (!taken.includes(compiledDeclaration.name))
|
|
101
|
+
taken.push(compiledDeclaration.name);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const root = compiled.declarations
|
|
105
|
+
.find((candidate) => candidate.name === entity.name);
|
|
106
|
+
const others = compiled.declarations
|
|
107
|
+
.filter((candidate) => candidate !== root);
|
|
108
|
+
|
|
109
|
+
// post-process the entity's own members through the vocabulary
|
|
110
|
+
for (const memberNode of root.type.members) {
|
|
111
|
+
const property = entity.properties.get(memberNode.name);
|
|
112
|
+
if (property === undefined) continue;
|
|
113
|
+
if (property.relation !== undefined) {
|
|
114
|
+
// the seam is load-bearing: a relation member is DETECTED by
|
|
115
|
+
// the preserved keyword, resolved through the normalized model
|
|
116
|
+
if (memberNode.extensions?.['x-entity']?.relation === undefined) continue;
|
|
117
|
+
const relation = property.relation;
|
|
118
|
+
const target = ref(relation.to);
|
|
119
|
+
memberNode.type = relation.kind === 'oneToOne' ? target : arrayOf(target);
|
|
120
|
+
memberNode.required = false;
|
|
121
|
+
memberNode.doc = [
|
|
122
|
+
`The ${relation.kind} relation to ${relation.to}; present only`,
|
|
123
|
+
'when a graph load included it.'];
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
if (property.type === 'string'
|
|
127
|
+
&& (property.format === 'date-time' || property.format === 'date')) {
|
|
128
|
+
memberNode.type = ref('DateTime');
|
|
129
|
+
usesDateTime = true;
|
|
130
|
+
}
|
|
131
|
+
if (property.version) {
|
|
132
|
+
memberNode.doc = ['The optimistic-concurrency token (§11.5):',
|
|
133
|
+
'engine-owned, bumped on every successful write.'];
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// the input variant: defaulted/generated members optional, to-one
|
|
138
|
+
// and to-many members gone, many-to-many as key-or-document arrays
|
|
139
|
+
const inputMembers = [];
|
|
140
|
+
for (const memberNode of root.type.members) {
|
|
141
|
+
const property = entity.properties.get(memberNode.name);
|
|
142
|
+
if (property?.relation !== undefined) {
|
|
143
|
+
if (property.relation.kind !== 'manyToMany') continue;
|
|
144
|
+
const target = entities.get(property.relation.to);
|
|
145
|
+
inputMembers.push(member(memberNode.name,
|
|
146
|
+
arrayOf(unionOf([keyTypeOf(target), ref(property.relation.to)])),
|
|
147
|
+
false,
|
|
148
|
+
['Membership rows to attach: target keys or documents.']));
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
const inputMember = {
|
|
152
|
+
...memberNode,
|
|
153
|
+
// a DateTime read is a plain string write: the brand
|
|
154
|
+
// discriminates expressions, never blocks a caller's literal
|
|
155
|
+
type: memberNode.type.ref === 'DateTime' ? primitive('string') : memberNode.type,
|
|
156
|
+
required: property?.default !== undefined
|
|
157
|
+
? false
|
|
158
|
+
: memberNode.required,
|
|
159
|
+
};
|
|
160
|
+
if (property?.default !== undefined) {
|
|
161
|
+
inputMember.doc = [`Defaulted (${JSON.stringify(property.default)});`,
|
|
162
|
+
'optional for a caller, present after the write.'];
|
|
163
|
+
}
|
|
164
|
+
inputMembers.push(inputMember);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
declarations.push(...others, root,
|
|
168
|
+
declaration(`${entity.name}Input`, objectOf(inputMembers),
|
|
169
|
+
[`What create()/add() accept for ${entity.name}: defaulted and`,
|
|
170
|
+
'generated members are optional; relation projections are not',
|
|
171
|
+
'writable (many-to-many membership arrays are).']));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// the metadata the generic typed store binds to
|
|
175
|
+
const metaFor = (entity) => objectOf([
|
|
176
|
+
member('doc', ref(entity.name), true),
|
|
177
|
+
member('input', ref(`${entity.name}Input`), true),
|
|
178
|
+
member('key', keyTypeOf(entity), true),
|
|
179
|
+
member('relations', objectOf(
|
|
180
|
+
[...entity.properties.values()]
|
|
181
|
+
.filter((property) => property.relation !== undefined)
|
|
182
|
+
.map((property) => member(property.name, objectOf([
|
|
183
|
+
member('entity', literal(property.relation.to), true),
|
|
184
|
+
member('doc', ref(property.relation.to), true),
|
|
185
|
+
member('many', literal(property.relation.kind !== 'oneToOne'), true),
|
|
186
|
+
]), true))), true),
|
|
187
|
+
]);
|
|
188
|
+
declarations.push(
|
|
189
|
+
declaration('Entities',
|
|
190
|
+
objectOf(entityNames.map((name) => member(name, ref(name), true))),
|
|
191
|
+
['Entity name to document shape.']),
|
|
192
|
+
declaration('EntityInputs',
|
|
193
|
+
objectOf(entityNames.map((name) => member(name, ref(`${name}Input`), true))),
|
|
194
|
+
['Entity name to input shape.']),
|
|
195
|
+
declaration('EntityMetaMap',
|
|
196
|
+
objectOf(entityNames.map((name) => member(name, metaFor(entities.get(name)), true))),
|
|
197
|
+
['The typed-store binding: pass this to TypedStore<…> from',
|
|
198
|
+
"'@jarenjs/db/typed'."]));
|
|
199
|
+
|
|
200
|
+
if (usesDateTime) declarations.unshift(DATE_TIME_DECLARATION);
|
|
201
|
+
|
|
202
|
+
return {
|
|
203
|
+
$emit: '0.1',
|
|
204
|
+
source: options?.source ?? null,
|
|
205
|
+
root: null,
|
|
206
|
+
declarations,
|
|
207
|
+
};
|
|
208
|
+
}
|