@ontrails/store 1.0.0-beta.14 → 1.0.0-beta.15
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/.turbo/turbo-lint.log +1 -1
- package/CHANGELOG.md +7 -0
- package/README.md +126 -61
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/internal/signal-identity.d.ts +17 -0
- package/dist/internal/signal-identity.d.ts.map +1 -0
- package/dist/internal/signal-identity.js +81 -0
- package/dist/internal/signal-identity.js.map +1 -0
- package/dist/jsonfile/index.d.ts +3 -0
- package/dist/jsonfile/index.d.ts.map +1 -0
- package/dist/jsonfile/index.js +2 -0
- package/dist/jsonfile/index.js.map +1 -0
- package/dist/jsonfile/runtime.d.ts +19 -0
- package/dist/jsonfile/runtime.d.ts.map +1 -0
- package/dist/jsonfile/runtime.js +403 -0
- package/dist/jsonfile/runtime.js.map +1 -0
- package/dist/jsonfile/types.d.ts +40 -0
- package/dist/jsonfile/types.d.ts.map +1 -0
- package/dist/{drizzle → jsonfile}/types.js.map +1 -1
- package/dist/store.d.ts +8 -18
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +152 -94
- package/dist/store.js.map +1 -1
- package/dist/testing.d.ts +28 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +90 -0
- package/dist/testing.js.map +1 -0
- package/dist/trails/crud.d.ts +38 -0
- package/dist/trails/crud.d.ts.map +1 -0
- package/dist/trails/crud.js +112 -0
- package/dist/trails/crud.js.map +1 -0
- package/dist/trails/index.d.ts +7 -0
- package/dist/trails/index.d.ts.map +1 -0
- package/dist/trails/index.js +4 -0
- package/dist/trails/index.js.map +1 -0
- package/dist/trails/reconcile.d.ts +36 -0
- package/dist/trails/reconcile.d.ts.map +1 -0
- package/dist/trails/reconcile.js +142 -0
- package/dist/trails/reconcile.js.map +1 -0
- package/dist/trails/sync.d.ts +25 -0
- package/dist/trails/sync.d.ts.map +1 -0
- package/dist/trails/sync.js +96 -0
- package/dist/trails/sync.js.map +1 -0
- package/dist/trails/utils.d.ts +51 -0
- package/dist/trails/utils.d.ts.map +1 -0
- package/dist/trails/utils.js +65 -0
- package/dist/trails/utils.js.map +1 -0
- package/dist/types.d.ts +205 -35
- package/dist/types.d.ts.map +1 -1
- package/package.json +6 -9
- package/src/__tests__/crud.test.ts +346 -0
- package/src/__tests__/jsonfile.test.ts +502 -0
- package/src/__tests__/store.test.ts +239 -33
- package/src/__tests__/sync-reconcile.test.ts +646 -0
- package/src/index.ts +12 -8
- package/src/internal/signal-identity.ts +162 -0
- package/src/jsonfile/index.ts +6 -0
- package/src/jsonfile/runtime.ts +702 -0
- package/src/jsonfile/types.ts +50 -0
- package/src/store.ts +296 -134
- package/src/testing.ts +175 -0
- package/src/trails/crud.ts +355 -0
- package/src/trails/index.ts +10 -0
- package/src/trails/reconcile.ts +281 -0
- package/src/trails/sync.ts +251 -0
- package/src/trails/utils.ts +96 -0
- package/src/types.ts +355 -63
- package/tsconfig.tests.json +10 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/drizzle/index.d.ts +0 -3
- package/dist/drizzle/index.d.ts.map +0 -1
- package/dist/drizzle/index.js +0 -2
- package/dist/drizzle/index.js.map +0 -1
- package/dist/drizzle/runtime.d.ts +0 -21
- package/dist/drizzle/runtime.d.ts.map +0 -1
- package/dist/drizzle/runtime.js +0 -458
- package/dist/drizzle/runtime.js.map +0 -1
- package/dist/drizzle/schema.d.ts +0 -15
- package/dist/drizzle/schema.d.ts.map +0 -1
- package/dist/drizzle/schema.js +0 -322
- package/dist/drizzle/schema.js.map +0 -1
- package/dist/drizzle/types.d.ts +0 -40
- package/dist/drizzle/types.d.ts.map +0 -1
- package/src/drizzle/__tests__/drizzle.test.ts +0 -469
- package/src/drizzle/index.ts +0 -17
- package/src/drizzle/runtime.ts +0 -853
- package/src/drizzle/schema.ts +0 -577
- package/src/drizzle/types.ts +0 -70
- /package/dist/{drizzle → jsonfile}/types.js +0 -0
package/.turbo/turbo-lint.log
CHANGED
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Schema-derived persistence for Trails.
|
|
4
4
|
|
|
5
|
-
The root package owns the connector-agnostic `store(...)` declaration.
|
|
5
|
+
The root package owns the connector-agnostic `store(...)` declaration. External connector packages such as `@ontrails/drizzle` bind that declaration to a concrete runtime, and first-party built-ins such as `@ontrails/store/jsonfile` live as opt-in subpaths on the same package.
|
|
6
6
|
|
|
7
7
|
## The two layers
|
|
8
8
|
|
|
@@ -14,13 +14,14 @@ import { store } from '@ontrails/store';
|
|
|
14
14
|
export const db = store({
|
|
15
15
|
gists: {
|
|
16
16
|
schema: gistSchema,
|
|
17
|
-
|
|
17
|
+
identity: 'id',
|
|
18
18
|
generated: ['id', 'createdAt', 'updatedAt'],
|
|
19
|
-
|
|
19
|
+
indexed: ['owner', 'createdAt'],
|
|
20
|
+
versioned: true,
|
|
20
21
|
},
|
|
21
22
|
files: {
|
|
22
23
|
schema: fileSchema,
|
|
23
|
-
|
|
24
|
+
identity: 'id',
|
|
24
25
|
generated: ['id'],
|
|
25
26
|
references: { gistId: 'gists' },
|
|
26
27
|
},
|
|
@@ -33,8 +34,11 @@ This declaration is pure metadata:
|
|
|
33
34
|
- insert schema
|
|
34
35
|
- update schema
|
|
35
36
|
- fixture schema
|
|
37
|
+
- derived change-signal handles (`table.signals.created|updated|removed`)
|
|
38
|
+
- identity field
|
|
36
39
|
- generated-field metadata
|
|
37
|
-
-
|
|
40
|
+
- optional framework-managed version tracking
|
|
41
|
+
- indexed markers
|
|
38
42
|
- references
|
|
39
43
|
|
|
40
44
|
No database connection is opened here. The returned value is the durable authored source of truth.
|
|
@@ -43,12 +47,12 @@ No database connection is opened here. The returned value is the durable authore
|
|
|
43
47
|
|
|
44
48
|
```typescript
|
|
45
49
|
import { store } from '@ontrails/store';
|
|
46
|
-
import { connectDrizzle } from '@ontrails/
|
|
50
|
+
import { connectDrizzle } from '@ontrails/drizzle';
|
|
47
51
|
|
|
48
52
|
const definition = store({
|
|
49
53
|
gists: {
|
|
50
54
|
schema: gistSchema,
|
|
51
|
-
|
|
55
|
+
identity: 'id',
|
|
52
56
|
generated: ['id', 'createdAt', 'updatedAt'],
|
|
53
57
|
},
|
|
54
58
|
});
|
|
@@ -59,11 +63,11 @@ export const db = connectDrizzle(definition, {
|
|
|
59
63
|
});
|
|
60
64
|
```
|
|
61
65
|
|
|
62
|
-
The bound store is a
|
|
66
|
+
The bound store is a resource. Use it directly in trails:
|
|
63
67
|
|
|
64
68
|
```typescript
|
|
65
69
|
export const list = trail('gist.list', {
|
|
66
|
-
|
|
70
|
+
resources: [db],
|
|
67
71
|
intent: 'read',
|
|
68
72
|
blaze: async (_input, ctx) => {
|
|
69
73
|
const conn = db.from(ctx);
|
|
@@ -73,32 +77,76 @@ export const list = trail('gist.list', {
|
|
|
73
77
|
});
|
|
74
78
|
```
|
|
75
79
|
|
|
80
|
+
### Built-in local backend
|
|
81
|
+
|
|
82
|
+
For a zero-extra-package local backend, use the first-party JSON file binding:
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
import { store } from '@ontrails/store';
|
|
86
|
+
import { jsonFile } from '@ontrails/store/jsonfile';
|
|
87
|
+
|
|
88
|
+
const definition = store({
|
|
89
|
+
gists: {
|
|
90
|
+
schema: gistSchema,
|
|
91
|
+
identity: 'id',
|
|
92
|
+
generated: ['id', 'createdAt', 'updatedAt'],
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
export const db = jsonFile(definition, {
|
|
97
|
+
dir: './data',
|
|
98
|
+
});
|
|
99
|
+
```
|
|
100
|
+
|
|
76
101
|
## Typed accessors
|
|
77
102
|
|
|
78
|
-
Every table on a bound connection exposes
|
|
103
|
+
Every writable table on a bound connection exposes the connector-agnostic accessor contract:
|
|
79
104
|
|
|
80
105
|
```typescript
|
|
81
106
|
const conn = db.from(ctx);
|
|
82
107
|
|
|
83
|
-
const created = await conn.gists.
|
|
84
|
-
|
|
108
|
+
const created = await conn.gists.upsert({
|
|
109
|
+
ownerId: 'matt',
|
|
85
110
|
description: 'Hello, Trails',
|
|
86
111
|
});
|
|
87
112
|
|
|
88
113
|
const found = await conn.gists.get(created.id);
|
|
89
|
-
const page = await conn.gists.list({
|
|
90
|
-
const updated = await conn.gists.
|
|
114
|
+
const page = await conn.gists.list({ ownerId: 'matt' }, { limit: 20, offset: 0 });
|
|
115
|
+
const updated = await conn.gists.upsert({
|
|
91
116
|
description: 'Updated description',
|
|
117
|
+
id: created.id,
|
|
118
|
+
ownerId: 'matt',
|
|
92
119
|
});
|
|
93
120
|
const removed = await conn.gists.remove(created.id);
|
|
94
121
|
```
|
|
95
122
|
|
|
96
123
|
Types are derived from the Zod schema:
|
|
97
124
|
|
|
98
|
-
- `
|
|
99
|
-
- `update()` uses the entity schema minus generated fields, then makes it partial
|
|
125
|
+
- `upsert()` uses the fixture/entity shape with generated fields optional
|
|
100
126
|
- `get()` returns `Entity | null`
|
|
101
127
|
- `list()` accepts typed partial filters and pagination options
|
|
128
|
+
- `versioned: true` adds a framework-managed `version` field to returned entities and lets `upsert()` accept an expected `version` for optimistic concurrency
|
|
129
|
+
|
|
130
|
+
Each normalized table also derives typed change signals from the same schema:
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
const createdHandle = definition.tables.gists.signals.created;
|
|
134
|
+
const updatedHandle = definition.tables.gists.signals.updated;
|
|
135
|
+
const removedHandle = definition.tables.gists.signals.removed;
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
These pre-bind handles preserve payload shape, but the canonical signal id materializes only when a connector binds the store to a resource. The bound form is always `resource:table.event`:
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
const created = db.store.tables.gists.signals.created;
|
|
142
|
+
|
|
143
|
+
created.id;
|
|
144
|
+
// "db.main:gists.created"
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Writable bindings fire those canonical scoped signals automatically when you access the resource through `db.from(ctx)` inside a trail context.
|
|
148
|
+
|
|
149
|
+
Tabular connectors such as `@ontrails/drizzle` also expose `insert()` and `update()` as convenience methods when the backend natively distinguishes create and patch operations.
|
|
102
150
|
|
|
103
151
|
## Fixtures and mocks
|
|
104
152
|
|
|
@@ -108,44 +156,43 @@ Fixtures belong on the root definition:
|
|
|
108
156
|
export const db = store({
|
|
109
157
|
gists: {
|
|
110
158
|
schema: gistSchema,
|
|
111
|
-
|
|
159
|
+
identity: 'id',
|
|
112
160
|
generated: ['id', 'createdAt', 'updatedAt'],
|
|
113
161
|
fixtures: [
|
|
114
|
-
{ id: 'g_1',
|
|
162
|
+
{ id: 'g_1', ownerId: 'matt', description: 'Seed gist' },
|
|
115
163
|
],
|
|
116
164
|
},
|
|
117
165
|
});
|
|
118
166
|
```
|
|
119
167
|
|
|
120
|
-
When a connector binds the store, those fixtures feed the
|
|
168
|
+
When a connector binds the store, those fixtures feed the resource mock automatically. Connector options can also add or override seed data for tests.
|
|
121
169
|
|
|
122
|
-
That means `testAll(app)` can auto-resolve connector-bound store
|
|
170
|
+
That means `testAll(app)` can auto-resolve connector-bound store resources without extra ceremony, as long as the resource is registered in the topo.
|
|
123
171
|
|
|
124
172
|
## Read-only bindings
|
|
125
173
|
|
|
126
|
-
Use the Drizzle connector's read-only
|
|
174
|
+
Use the Drizzle connector's read-only binding when a trail should inspect persisted state without exposing writes:
|
|
127
175
|
|
|
128
176
|
```typescript
|
|
129
|
-
import { connectReadOnlyDrizzle
|
|
177
|
+
import { connectReadOnlyDrizzle } from '@ontrails/drizzle';
|
|
130
178
|
|
|
131
179
|
const analytics = connectReadOnlyDrizzle(definition, {
|
|
132
180
|
id: 'analytics.db',
|
|
133
181
|
url: './data/analytics.sqlite',
|
|
134
182
|
});
|
|
183
|
+
```
|
|
135
184
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
{ id: 'audit.db', url: './data/audit.sqlite' }
|
|
145
|
-
);
|
|
185
|
+
Read-only bindings expose `get()`, `list()`, and `query()`, but not `upsert()`, `remove()`, `insert()`, or `update()`.
|
|
186
|
+
|
|
187
|
+
## Accessor contract testing
|
|
188
|
+
|
|
189
|
+
Connectors can reuse the shared writable-accessor contract tests from `@ontrails/store/testing`:
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
import { createStoreAccessorContractCases } from '@ontrails/store/testing';
|
|
146
193
|
```
|
|
147
194
|
|
|
148
|
-
|
|
195
|
+
That helper provides reusable cases for the baseline `get()`, `list()`, `upsert()`, and `remove()` behavior so connector suites only need to wrap them with their normal `test(...)` calls and add backend-specific coverage on top.
|
|
149
196
|
|
|
150
197
|
## Drizzle escape hatch
|
|
151
198
|
|
|
@@ -163,51 +210,69 @@ const rows = await conn.query(({ drizzle, tables }) =>
|
|
|
163
210
|
|
|
164
211
|
This keeps the default happy path derived and typed, while still giving you full access to the underlying connector when the CRUD accessors are not enough.
|
|
165
212
|
|
|
166
|
-
## Connector
|
|
213
|
+
## Connector binding
|
|
167
214
|
|
|
168
|
-
`@ontrails/
|
|
215
|
+
`@ontrails/drizzle` keeps the durable `store(...)` declaration in
|
|
216
|
+
`@ontrails/store` and binds it to a concrete runtime:
|
|
169
217
|
|
|
170
218
|
```typescript
|
|
171
|
-
import {
|
|
172
|
-
|
|
173
|
-
export const writable = store(
|
|
174
|
-
{
|
|
175
|
-
gists: {
|
|
176
|
-
schema: gistSchema,
|
|
177
|
-
primaryKey: 'id',
|
|
178
|
-
generated: ['id', 'createdAt', 'updatedAt'],
|
|
179
|
-
},
|
|
180
|
-
},
|
|
181
|
-
{ url: ':memory:' }
|
|
182
|
-
);
|
|
219
|
+
import { connectDrizzle, connectReadOnlyDrizzle } from '@ontrails/drizzle';
|
|
220
|
+
import { store } from '@ontrails/store';
|
|
183
221
|
|
|
184
|
-
|
|
185
|
-
{
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
generated: ['id', 'createdAt', 'updatedAt'],
|
|
190
|
-
},
|
|
222
|
+
const definition = store({
|
|
223
|
+
gists: {
|
|
224
|
+
schema: gistSchema,
|
|
225
|
+
identity: 'id',
|
|
226
|
+
generated: ['id', 'createdAt', 'updatedAt'],
|
|
191
227
|
},
|
|
192
|
-
|
|
193
|
-
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
export const writable = connectDrizzle(definition, { url: ':memory:' });
|
|
231
|
+
|
|
232
|
+
export const readonly = connectReadOnlyDrizzle(definition, {
|
|
233
|
+
url: './data/gists.sqlite',
|
|
234
|
+
});
|
|
194
235
|
```
|
|
195
236
|
|
|
196
|
-
|
|
237
|
+
The root package still owns the authored persistence model; connector packages
|
|
238
|
+
project that model into runnable resources.
|
|
197
239
|
|
|
198
240
|
## Schema export for external tooling
|
|
199
241
|
|
|
200
|
-
If you need the raw derived Drizzle tables for tooling such as `drizzle-kit`,
|
|
242
|
+
If you need the raw derived Drizzle tables for tooling such as `drizzle-kit`,
|
|
243
|
+
read them from the bound resource's `tables` field:
|
|
201
244
|
|
|
202
245
|
```typescript
|
|
203
|
-
import {
|
|
246
|
+
import { connectDrizzle } from '@ontrails/drizzle';
|
|
247
|
+
import { store } from '@ontrails/store';
|
|
248
|
+
|
|
249
|
+
const definition = store({
|
|
250
|
+
gists: {
|
|
251
|
+
schema: gistSchema,
|
|
252
|
+
identity: 'id',
|
|
253
|
+
generated: ['id', 'createdAt', 'updatedAt'],
|
|
254
|
+
},
|
|
255
|
+
});
|
|
204
256
|
|
|
205
|
-
const
|
|
257
|
+
const db = connectDrizzle(definition, { url: ':memory:' });
|
|
258
|
+
const schema = db.tables;
|
|
206
259
|
```
|
|
207
260
|
|
|
208
261
|
## Installation
|
|
209
262
|
|
|
210
263
|
```bash
|
|
211
264
|
bun add @ontrails/store zod
|
|
212
|
-
bun add drizzle-orm
|
|
213
265
|
```
|
|
266
|
+
|
|
267
|
+
Add Drizzle only when you want the external SQLite/ORM connector:
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
bun add @ontrails/drizzle
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## Migration
|
|
274
|
+
|
|
275
|
+
The Drizzle binding now lives in `@ontrails/drizzle`.
|
|
276
|
+
|
|
277
|
+
- Replace `import { ... } from '@ontrails/store/drizzle'` with `import { ... } from '@ontrails/drizzle'`
|
|
278
|
+
- Keep connector-agnostic store declarations on `@ontrails/store`
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export type { AnyStoreDefinition, AnyStoreTable, EntityOf, FiltersOf, FixtureInputOf, FixtureOf, FixturesOfInput, GeneratedFieldsOfInput, GeneratedKeysOf, IndexFieldsOfInput,
|
|
1
|
+
export { store, versionFieldName } from './store.js';
|
|
2
|
+
export type { AnyStoreDefinition, AnyStoreTable, EntityOf, FiltersOf, FixtureInputOf, FixtureOf, FixturesOfInput, GeneratedFieldsOfInput, GeneratedKeysOf, IdentityFieldOfInput, IdentityOf, IndexFieldsOfInput, IndexedFieldsOfInput, InsertOf, ReadOnlyStoreConnection, StoreAccessor, ReadOnlyStoreTableAccessor, ReferencesOfInput, StoreAccessMode, StoreConnection, StoreConnectorOptions, StoreDefinition, StoreFieldKey, StoreFixtureInput, StoreFixtureRow, StoreIdentifierOf, StoreKind, StoreListOptions, StoreMockSeed, StoreObjectSchema, StoreOptions, StoreSearchDefinition, StoreTable, StoreTableSignals, StoreTableAccessor, StoreTableConnection, StoreTableInput, StoreTablesInput, UpsertOf, UpdateOf, } from './types.js';
|
|
3
3
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACrD,YAAY,EACV,kBAAkB,EAClB,aAAa,EACb,QAAQ,EACR,SAAS,EACT,cAAc,EACd,SAAS,EACT,eAAe,EACf,sBAAsB,EACtB,eAAe,EACf,oBAAoB,EACpB,UAAU,EACV,kBAAkB,EAClB,oBAAoB,EACpB,QAAQ,EACR,uBAAuB,EACvB,aAAa,EACb,0BAA0B,EAC1B,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,qBAAqB,EACrB,eAAe,EACf,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,iBAAiB,EACjB,SAAS,EACT,gBAAgB,EAChB,aAAa,EACb,iBAAiB,EACjB,YAAY,EACZ,qBAAqB,EACrB,UAAU,EACV,iBAAiB,EACjB,kBAAkB,EAClB,oBAAoB,EACpB,eAAe,EACf,gBAAgB,EAChB,QAAQ,EACR,QAAQ,GACT,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { store, versionFieldName } from './store.js';
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { z } from 'zod';
|
|
2
|
+
import type { AnyStoreDefinition, StoreTableSignals } from '../types.js';
|
|
3
|
+
type StoreSignalEvent = 'created' | 'removed' | 'updated';
|
|
4
|
+
export declare const createStoreTableSignals: <TPayload>(tableName: string, payload: z.ZodType<TPayload>) => StoreTableSignals<TPayload>;
|
|
5
|
+
export declare const composeStoreSignalId: (scope: string, tableName: string, event: StoreSignalEvent) => string;
|
|
6
|
+
/**
|
|
7
|
+
* Verifies that a resource id is safe to compose into a scoped signal id.
|
|
8
|
+
*
|
|
9
|
+
* Scoped signal ids are matched by the `SCOPED_SIGNAL_ID` pattern in
|
|
10
|
+
* `@ontrails/core` (`^[^:\s]+:[^:.\s]+(?:\.[^:.\s]+)+$`). A resource id used
|
|
11
|
+
* as the scope segment must therefore be a non-empty string that contains
|
|
12
|
+
* neither `":"` nor any whitespace.
|
|
13
|
+
*/
|
|
14
|
+
export declare const isValidResourceId: (resourceId: string) => boolean;
|
|
15
|
+
export declare const bindStoreDefinition: <TStore extends AnyStoreDefinition>(definition: TStore, scope: string) => TStore;
|
|
16
|
+
export type { StoreSignalEvent };
|
|
17
|
+
//# sourceMappingURL=signal-identity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signal-identity.d.ts","sourceRoot":"","sources":["../../src/internal/signal-identity.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B,OAAO,KAAK,EACV,kBAAkB,EAElB,iBAAiB,EAClB,MAAM,aAAa,CAAC;AAMrB,KAAK,gBAAgB,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;AAsC1D,eAAO,MAAM,uBAAuB,GAAI,QAAQ,EAC9C,WAAW,MAAM,EACjB,SAAS,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,KAC3B,iBAAiB,CAAC,QAAQ,CAKzB,CAAC;AAEL,eAAO,MAAM,oBAAoB,GAC/B,OAAO,MAAM,EACb,WAAW,MAAM,EACjB,OAAO,gBAAgB,KACtB,MAA0C,CAAC;AAkC9C;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,GAAI,YAAY,MAAM,KAAG,OACuB,CAAC;AAU/E,eAAO,MAAM,mBAAmB,GAAI,MAAM,SAAS,kBAAkB,EACnE,YAAY,MAAM,EAClB,OAAO,MAAM,KACZ,MAiCF,CAAC;AAEF,YAAY,EAAE,gBAAgB,EAAE,CAAC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { signal, ValidationError } from '@ontrails/core';
|
|
2
|
+
import { attachLateBoundSignalRef, cloneSignalWithId, } from '@ontrails/core/internal/signal-ref';
|
|
3
|
+
const createStoreSignalDescription = (tableName, event) => {
|
|
4
|
+
switch (event) {
|
|
5
|
+
case 'created': {
|
|
6
|
+
return `Fired after a "${tableName}" entity is created.`;
|
|
7
|
+
}
|
|
8
|
+
case 'removed': {
|
|
9
|
+
return `Fired after a "${tableName}" entity is removed.`;
|
|
10
|
+
}
|
|
11
|
+
case 'updated': {
|
|
12
|
+
return `Fired after a "${tableName}" entity is updated.`;
|
|
13
|
+
}
|
|
14
|
+
default: {
|
|
15
|
+
throw new Error(`Unsupported store signal event: ${event}`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
const createStoreSignal = (tableName, event, payload) => attachLateBoundSignalRef(signal(`${tableName}.${event}`, {
|
|
20
|
+
description: createStoreSignalDescription(tableName, event),
|
|
21
|
+
payload,
|
|
22
|
+
}), {
|
|
23
|
+
kind: 'store-derived',
|
|
24
|
+
token: Bun.randomUUIDv7(),
|
|
25
|
+
});
|
|
26
|
+
export const createStoreTableSignals = (tableName, payload) => Object.freeze({
|
|
27
|
+
created: createStoreSignal(tableName, 'created', payload),
|
|
28
|
+
removed: createStoreSignal(tableName, 'removed', payload),
|
|
29
|
+
updated: createStoreSignal(tableName, 'updated', payload),
|
|
30
|
+
});
|
|
31
|
+
export const composeStoreSignalId = (scope, tableName, event) => `${scope}:${tableName}.${event}`;
|
|
32
|
+
const bindTableSignals = (scope, table) => Object.freeze({
|
|
33
|
+
created: cloneSignalWithId(table.signals.created, composeStoreSignalId(scope, table.name, 'created')),
|
|
34
|
+
removed: cloneSignalWithId(table.signals.removed, composeStoreSignalId(scope, table.name, 'removed')),
|
|
35
|
+
updated: cloneSignalWithId(table.signals.updated, composeStoreSignalId(scope, table.name, 'updated')),
|
|
36
|
+
});
|
|
37
|
+
const collectStoreSignals = (normalized, tableNames) => Object.freeze(tableNames.flatMap((name) => {
|
|
38
|
+
const table = normalized[name];
|
|
39
|
+
return table === undefined
|
|
40
|
+
? []
|
|
41
|
+
: [table.signals.created, table.signals.updated, table.signals.removed];
|
|
42
|
+
}));
|
|
43
|
+
/**
|
|
44
|
+
* Verifies that a resource id is safe to compose into a scoped signal id.
|
|
45
|
+
*
|
|
46
|
+
* Scoped signal ids are matched by the `SCOPED_SIGNAL_ID` pattern in
|
|
47
|
+
* `@ontrails/core` (`^[^:\s]+:[^:.\s]+(?:\.[^:.\s]+)+$`). A resource id used
|
|
48
|
+
* as the scope segment must therefore be a non-empty string that contains
|
|
49
|
+
* neither `":"` nor any whitespace.
|
|
50
|
+
*/
|
|
51
|
+
export const isValidResourceId = (resourceId) => resourceId.length > 0 && !resourceId.includes(':') && !/\s/.test(resourceId);
|
|
52
|
+
const assertValidScope = (scope) => {
|
|
53
|
+
if (!isValidResourceId(scope)) {
|
|
54
|
+
throw new ValidationError(`Store resource id "${scope}" is invalid: must be a non-empty string with no ":" characters and no whitespace.`);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
export const bindStoreDefinition = (definition, scope) => {
|
|
58
|
+
assertValidScope(scope);
|
|
59
|
+
const tableNames = definition.tableNames;
|
|
60
|
+
const tables = {};
|
|
61
|
+
for (const tableName of tableNames) {
|
|
62
|
+
const table = definition.tables[tableName];
|
|
63
|
+
if (table === undefined) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
tables[tableName] = Object.freeze({
|
|
67
|
+
...table,
|
|
68
|
+
signals: bindTableSignals(scope, table),
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
const get = 'get' in definition && typeof definition.get === 'function'
|
|
72
|
+
? (name) => tables[name]
|
|
73
|
+
: undefined;
|
|
74
|
+
return Object.freeze({
|
|
75
|
+
...definition,
|
|
76
|
+
...(get ? { get } : {}),
|
|
77
|
+
signals: collectStoreSignals(tables, tableNames),
|
|
78
|
+
tables: Object.freeze(tables),
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
//# sourceMappingURL=signal-identity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signal-identity.js","sourceRoot":"","sources":["../../src/internal/signal-identity.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/jsonfile/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,QAAQ,IAAI,KAAK,EAAE,MAAM,cAAc,CAAC;AAC5E,YAAY,EACV,kBAAkB,EAClB,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/jsonfile/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,QAAQ,IAAI,KAAK,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { AnyStoreDefinition } from '../types.js';
|
|
2
|
+
import type { JsonFileConnection, JsonFileStoreOptions, JsonFileStoreResource } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Connect to a JSON-file-backed store.
|
|
5
|
+
*
|
|
6
|
+
* Creates one `<tableName>.json` file per table in the target directory.
|
|
7
|
+
* Each file holds a JSON array of entities. The entire dataset is loaded
|
|
8
|
+
* into memory on connect and flushed to disk on every write.
|
|
9
|
+
*/
|
|
10
|
+
export declare const connectJsonFile: <TStore extends AnyStoreDefinition>(definition: TStore, options: JsonFileStoreOptions<TStore>) => Promise<JsonFileConnection<TStore["tables"]>>;
|
|
11
|
+
/**
|
|
12
|
+
* Create a JSON-file-backed store resource.
|
|
13
|
+
*
|
|
14
|
+
* Returns a `Resource` that can be registered in a topo and resolved from
|
|
15
|
+
* trail context via `db.from(ctx)`.
|
|
16
|
+
*/
|
|
17
|
+
export declare const jsonFile: <TStore extends AnyStoreDefinition>(definition: TStore, options: JsonFileStoreOptions<TStore>) => JsonFileStoreResource<TStore["tables"]>;
|
|
18
|
+
export { jsonFile as store };
|
|
19
|
+
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/jsonfile/runtime.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,kBAAkB,EASnB,MAAM,aAAa,CAAC;AAIrB,OAAO,KAAK,EACV,kBAAkB,EAClB,oBAAoB,EACpB,qBAAqB,EACtB,MAAM,YAAY,CAAC;AAglBpB;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,GAAU,MAAM,SAAS,kBAAkB,EACrE,YAAY,MAAM,EAClB,SAAS,oBAAoB,CAAC,MAAM,CAAC,KACpC,OAAO,CAAC,kBAAkB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAmB9C,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,GAAI,MAAM,SAAS,kBAAkB,EACxD,YAAY,MAAM,EAClB,SAAS,oBAAoB,CAAC,MAAM,CAAC,KACpC,qBAAqB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAgDxC,CAAC;AAEF,OAAO,EAAE,QAAQ,IAAI,KAAK,EAAE,CAAC"}
|