@lunora/server 1.0.0-alpha.1 → 1.0.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/__assets__/package-og.svg +1 -1
- package/dist/data-model.d.mts +104 -16
- package/dist/data-model.d.ts +104 -16
- package/dist/index.d.mts +70 -20
- package/dist/index.d.ts +70 -20
- package/dist/index.mjs +8 -8
- package/dist/packem_shared/{definePresence-D5LtwGl0.mjs → PRESENCE_DEFAULT_TTL_MS-UQuUI5sV.mjs} +2 -2
- package/dist/packem_shared/{bindTableFacade-DCuyr46L.mjs → bindOrm-Ce57S3N9.mjs} +58 -1
- package/dist/packem_shared/{defineAggregateIndex-DzqxtAyV.mjs → defineAggregateIndex-B20MIOmj.mjs} +16 -3
- package/dist/packem_shared/{mask-CkZJHHMM.mjs → mask-eCUYOwhd.mjs} +1 -1
- package/dist/packem_shared/{rls-Zhf5wEeJ.mjs → rls-Bi9HiyDC.mjs} +18 -2
- package/dist/rls/testing.mjs +1 -1
- package/dist/types.d.mts +36 -0
- package/dist/types.d.ts +36 -0
- package/package.json +2 -2
- /package/dist/packem_shared/{defineEnv-DjFkpkSP.mjs → LunoraEnvError-DjFkpkSP.mjs} +0 -0
- /package/dist/packem_shared/{defineSchemaExtension-Ck5_TUO8.mjs → composePluginMiddleware-Ck5_TUO8.mjs} +0 -0
- /package/dist/packem_shared/{definePolicy-De67zPDS.mjs → createPolicyDsl-De67zPDS.mjs} +0 -0
package/dist/data-model.d.mts
CHANGED
|
@@ -49,6 +49,13 @@ interface QueryArgs<TDocument> {
|
|
|
49
49
|
cursor?: null | string;
|
|
50
50
|
limit?: number;
|
|
51
51
|
orderBy?: OrderBy<TDocument>[];
|
|
52
|
+
/**
|
|
53
|
+
* Project each returned row down to these columns (plus the system fields
|
|
54
|
+
* `_id`/`_creationTime`, always retained). Trims wire payload for wide rows;
|
|
55
|
+
* relations requested via `with` are still attached. Reactivity is unaffected —
|
|
56
|
+
* the engine still reads the whole row to track dependencies.
|
|
57
|
+
*/
|
|
58
|
+
select?: ReadonlyArray<keyof TDocument & string>;
|
|
52
59
|
where?: Where<TDocument>;
|
|
53
60
|
}
|
|
54
61
|
/**
|
|
@@ -97,6 +104,12 @@ type WhereOf<DM, REL extends Record<keyof DM, object>, T extends keyof DM> = Rel
|
|
|
97
104
|
/** {@link QueryArgs} with the relation-aware {@link WhereOf} `where` typing. */
|
|
98
105
|
interface QueryArgsOf<DM, REL extends Record<keyof DM, object>, T extends keyof DM> {
|
|
99
106
|
cursor?: null | string;
|
|
107
|
+
/**
|
|
108
|
+
* Include soft-deleted rows (`.softDelete()` tables only). Default hides them;
|
|
109
|
+
* `true` returns deleted rows alongside live ones. No effect on a table
|
|
110
|
+
* without `.softDelete()`.
|
|
111
|
+
*/
|
|
112
|
+
includeDeleted?: boolean;
|
|
100
113
|
limit?: number;
|
|
101
114
|
orderBy?: OrderBy<DM[T]>[];
|
|
102
115
|
where?: WhereOf<DM, REL, T>;
|
|
@@ -110,11 +123,15 @@ interface QueryPage<TDocument> {
|
|
|
110
123
|
type NestedWithArgument<WK> = WK extends {
|
|
111
124
|
with: infer NW;
|
|
112
125
|
} ? NW : {};
|
|
126
|
+
/** The nested `select` tuple inside a relation's with-value, or `undefined` (no projection). */
|
|
127
|
+
type NestedSelectArgument<WK> = WK extends {
|
|
128
|
+
select: infer S;
|
|
129
|
+
} ? S : undefined;
|
|
113
130
|
/**
|
|
114
131
|
* The `with` argument for table `T`: each relation can be `true` (load with no
|
|
115
|
-
* refinements) or an object. `many` relations accept `where`/`orderBy`/`limit
|
|
116
|
-
* plus a nested `with`; `one` relations accept
|
|
117
|
-
* reserved `_count` key requests per-relation aggregate counts.
|
|
132
|
+
* refinements) or an object. `many` relations accept `where`/`orderBy`/`limit`/
|
|
133
|
+
* `select` plus a nested `with`; `one` relations accept `select` + a nested
|
|
134
|
+
* `with`. The reserved `_count` key requests per-relation aggregate counts.
|
|
118
135
|
*/
|
|
119
136
|
type WithArg<DM, REL extends Record<keyof DM, object>, T extends keyof DM> = { [K in keyof REL[T]]?: REL[T][K] extends {
|
|
120
137
|
__relationKind: "many";
|
|
@@ -125,18 +142,24 @@ type WithArg<DM, REL extends Record<keyof DM, object>, T extends keyof DM> = { [
|
|
|
125
142
|
__relationKind: "one";
|
|
126
143
|
__target: infer Target extends keyof DM;
|
|
127
144
|
} ? boolean | {
|
|
145
|
+
select?: ReadonlyArray<keyof DM[Target] & string>;
|
|
128
146
|
with?: WithArg<DM, REL, Target>;
|
|
129
147
|
} : never } & {
|
|
130
148
|
_count?: { [K in keyof REL[T]]?: true };
|
|
131
149
|
};
|
|
132
|
-
/**
|
|
150
|
+
/**
|
|
151
|
+
* Resolve a single relation descriptor + its with-value to the loaded type,
|
|
152
|
+
* threading the nested `select` tuple into the projected child shape (the 5th
|
|
153
|
+
* `LoadWith` arg) so `with: { author: { select: ["name"] } }` narrows the loaded
|
|
154
|
+
* `author` to the selected columns + system fields.
|
|
155
|
+
*/
|
|
133
156
|
type LoadRelation<DM, REL extends Record<keyof DM, object>, R, WK> = R extends {
|
|
134
157
|
__relationKind: "one";
|
|
135
158
|
__target: infer Target extends keyof DM;
|
|
136
|
-
} ? LoadWith<DM, REL, Target, NestedWithArgument<WK>> | null : R extends {
|
|
159
|
+
} ? LoadWith<DM, REL, Target, NestedWithArgument<WK>, NestedSelectArgument<WK>> | null : R extends {
|
|
137
160
|
__relationKind: "many";
|
|
138
161
|
__target: infer Target extends keyof DM;
|
|
139
|
-
} ? LoadWith<DM, REL, Target, NestedWithArgument<WK>>[] : never;
|
|
162
|
+
} ? LoadWith<DM, REL, Target, NestedWithArgument<WK>, NestedSelectArgument<WK>>[] : never;
|
|
140
163
|
/** The relation keys of `W` that were actually requested (not `false`/`undefined`). */
|
|
141
164
|
type LoadedRelations<DM, REL extends Record<keyof DM, object>, T extends keyof DM, W> = { [K in keyof W as K extends keyof REL[T] ? (W[K] extends false | undefined ? never : K) : never]: K extends keyof REL[T] ? LoadRelation<DM, REL, REL[T][K], W[K]> : never };
|
|
142
165
|
/** The `_count` projection of `W`, if any. */
|
|
@@ -145,8 +168,20 @@ type LoadedCount<W> = W extends {
|
|
|
145
168
|
} ? {
|
|
146
169
|
_count: { [K in keyof C]: number };
|
|
147
170
|
} : {};
|
|
148
|
-
/**
|
|
149
|
-
type
|
|
171
|
+
/** System columns a `select` projection always retains, so cursors and by-id reuse keep working. */
|
|
172
|
+
type SelectAlwaysKeep<DM, T extends keyof DM> = ("_creationTime" | "_id") & keyof DM[T];
|
|
173
|
+
/**
|
|
174
|
+
* `DM[T]` narrowed to the columns named by a `select` tuple `S` (plus the system
|
|
175
|
+
* fields). `undefined` (the default — no `select`) keeps the full document.
|
|
176
|
+
*/
|
|
177
|
+
type ProjectDoc<DM, T extends keyof DM, S> = S extends ReadonlyArray<infer K> ? (K extends keyof DM[T] ? Pick<DM[T], (K & keyof DM[T]) | SelectAlwaysKeep<DM, T>> : DM[T]) : DM[T];
|
|
178
|
+
/**
|
|
179
|
+
* `Doc<T>` narrowed to exactly the relations requested in the with-arg `W` and,
|
|
180
|
+
* when a `select` tuple `S` is supplied, to its projected columns. `S` defaults
|
|
181
|
+
* to `undefined` so the 4-argument form (the codegen-emitted callers) keeps the
|
|
182
|
+
* full document.
|
|
183
|
+
*/
|
|
184
|
+
type LoadWith<DM, REL extends Record<keyof DM, object>, T extends keyof DM, W, S = undefined> = LoadedCount<W> & LoadedRelations<DM, REL, T, W> & ProjectDoc<DM, T, S>;
|
|
150
185
|
/** Reducer applied by an aggregate (`avg`/`count`/`max`/`min`/`sum`). */
|
|
151
186
|
type AggregateOp = "avg" | "count" | "max" | "min" | "sum";
|
|
152
187
|
/**
|
|
@@ -260,15 +295,20 @@ interface TableReaderFacade<DM, REL extends Record<keyof DM, object>, RANK exten
|
|
|
260
295
|
* uses to inject `baseWhere` and `restrictsCounts`.
|
|
261
296
|
*/
|
|
262
297
|
count: (where?: RestrictableQueryOptionsOf<DM, REL, T> | WhereOf<DM, REL, T>) => Promise<number>;
|
|
263
|
-
|
|
298
|
+
/** `true` when at least one row matches `where` (any row when omitted). RLS-filtered exactly like `findFirst`. */
|
|
299
|
+
exists: (where?: WhereOf<DM, REL, T>) => Promise<boolean>;
|
|
300
|
+
findFirst: <W extends WithArg<DM, REL, T> = {}, S extends ReadonlyArray<keyof DM[T] & string> | undefined = undefined>(args?: QueryArgsOf<DM, REL, T> & {
|
|
301
|
+
select?: S;
|
|
264
302
|
with?: W;
|
|
265
|
-
}) => Promise<LoadWith<DM, REL, T, W> | null>;
|
|
266
|
-
findFirstOrThrow: <W extends WithArg<DM, REL, T> = {}>(args?: QueryArgsOf<DM, REL, T> & {
|
|
303
|
+
}) => Promise<LoadWith<DM, REL, T, W, S> | null>;
|
|
304
|
+
findFirstOrThrow: <W extends WithArg<DM, REL, T> = {}, S extends ReadonlyArray<keyof DM[T] & string> | undefined = undefined>(args?: QueryArgsOf<DM, REL, T> & {
|
|
305
|
+
select?: S;
|
|
267
306
|
with?: W;
|
|
268
|
-
}) => Promise<LoadWith<DM, REL, T, W>>;
|
|
269
|
-
findMany: <W extends WithArg<DM, REL, T> = {}>(args?: QueryArgsOf<DM, REL, T> & {
|
|
307
|
+
}) => Promise<LoadWith<DM, REL, T, W, S>>;
|
|
308
|
+
findMany: <W extends WithArg<DM, REL, T> = {}, S extends ReadonlyArray<keyof DM[T] & string> | undefined = undefined>(args?: QueryArgsOf<DM, REL, T> & {
|
|
309
|
+
select?: S;
|
|
270
310
|
with?: W;
|
|
271
|
-
}) => Promise<QueryPage<LoadWith<DM, REL, T, W>>>;
|
|
311
|
+
}) => Promise<QueryPage<LoadWith<DM, REL, T, W, S>>>;
|
|
272
312
|
get: (id: Id<string & T>) => Promise<DM[T] | null>;
|
|
273
313
|
/**
|
|
274
314
|
* Group rows by the named keys and apply `agg` per group (defaults to
|
|
@@ -299,6 +339,11 @@ interface TableReaderFacade<DM, REL extends Record<keyof DM, object>, RANK exten
|
|
|
299
339
|
}
|
|
300
340
|
/** Read-write typed table accessor exposed on `MutationCtx.db.<table>` / `ActionCtx.db.<table>`. */
|
|
301
341
|
interface TableWriterFacade<DM, IM extends Record<keyof DM, object>, REL extends Record<keyof DM, object>, RANK extends Record<keyof DM, string>, SEARCH extends Record<keyof DM, string>, T extends keyof DM> extends TableReaderFacade<DM, REL, RANK, SEARCH, T> {
|
|
342
|
+
/**
|
|
343
|
+
* Delete a row by id. On a `.softDelete()` table this flips the marker column
|
|
344
|
+
* (and cascades as a soft delete) instead of removing the row; use
|
|
345
|
+
* {@link TableWriterFacade.hardDelete} to force physical removal.
|
|
346
|
+
*/
|
|
302
347
|
delete: (id: Id<string & T>) => Promise<void>;
|
|
303
348
|
/** Delete many rows in this table by id in one call; returns the *requested* id count (unknown ids are no-ops). Atomic within a mutation (a throw rolls the mutation back); an action has no transaction span. */
|
|
304
349
|
deleteMany: (ids: ReadonlyArray<Id<string & T>>, options?: {
|
|
@@ -306,7 +351,21 @@ interface TableWriterFacade<DM, IM extends Record<keyof DM, object>, REL extends
|
|
|
306
351
|
}) => Promise<{
|
|
307
352
|
deleted: number;
|
|
308
353
|
}>;
|
|
309
|
-
|
|
354
|
+
/** Physically remove a row (and physically cascade `onDelete`), bypassing `.softDelete()`. Same as `delete()` on a non-soft table. */
|
|
355
|
+
hardDelete: (id: Id<string & T>) => Promise<void>;
|
|
356
|
+
/**
|
|
357
|
+
* Insert a document, returning its minted id. With `{ skipDuplicates: true }`
|
|
358
|
+
* a UNIQUE-constraint breach resolves to `null` (the row already exists)
|
|
359
|
+
* instead of throwing — the return type widens to `Id | null` on that overload.
|
|
360
|
+
*/
|
|
361
|
+
insert: {
|
|
362
|
+
(values: IM[T], options: {
|
|
363
|
+
skipDuplicates: true;
|
|
364
|
+
}): Promise<Id<string & T> | null>;
|
|
365
|
+
(values: IM[T], options?: {
|
|
366
|
+
skipDuplicates?: boolean;
|
|
367
|
+
}): Promise<Id<string & T>>;
|
|
368
|
+
};
|
|
310
369
|
/** Insert many documents into this table in one call, returning the minted ids in input order. Atomic within a mutation (a throw rolls the mutation back); an action has no transaction span. */
|
|
311
370
|
insertMany: (values: ReadonlyArray<IM[T]>, options?: {
|
|
312
371
|
limit?: number;
|
|
@@ -320,9 +379,38 @@ interface TableWriterFacade<DM, IM extends Record<keyof DM, object>, REL extends
|
|
|
320
379
|
limit?: number;
|
|
321
380
|
}) => Promise<void>;
|
|
322
381
|
replace: (id: Id<string & T>, values: IM[T]) => Promise<void>;
|
|
382
|
+
/** Un-soft-delete a row by id: clears the `.softDelete()` marker so list reads see it again. Throws on a non-soft table. */
|
|
383
|
+
restore: (id: Id<string & T>) => Promise<void>;
|
|
384
|
+
/**
|
|
385
|
+
* Insert when no existing row matches `target`, otherwise patch the match with
|
|
386
|
+
* `update` (defaulting to `create`). `target` names a `.unique()` column (or a
|
|
387
|
+
* tuple) used to look it up. Returns the row id and whether it was `created`.
|
|
388
|
+
* Composes `findFirst` + `insert`/`patch`, so RLS gates each step.
|
|
389
|
+
*/
|
|
390
|
+
upsert: (args: {
|
|
391
|
+
create: IM[T];
|
|
392
|
+
target: UpsertTargetOf<DM, T>;
|
|
393
|
+
update?: Partial<IM[T]>;
|
|
394
|
+
}) => Promise<{
|
|
395
|
+
created: boolean;
|
|
396
|
+
id: Id<string & T>;
|
|
397
|
+
}>;
|
|
398
|
+
/** Sequential {@link TableWriterFacade.upsert} over many rows sharing one `target`; one result per input row, in order. */
|
|
399
|
+
upsertMany: (args: {
|
|
400
|
+
rows: ReadonlyArray<{
|
|
401
|
+
create: IM[T];
|
|
402
|
+
update?: Partial<IM[T]>;
|
|
403
|
+
}>;
|
|
404
|
+
target: UpsertTargetOf<DM, T>;
|
|
405
|
+
}) => Promise<{
|
|
406
|
+
created: boolean;
|
|
407
|
+
id: Id<string & T>;
|
|
408
|
+
}[]>;
|
|
323
409
|
}
|
|
410
|
+
/** Conflict target for `upsert`/`upsertMany`: one column of table `T`, or a tuple of them. */
|
|
411
|
+
type UpsertTargetOf<DM, T extends keyof DM> = ReadonlyArray<keyof DM[T] & string> | (keyof DM[T] & string);
|
|
324
412
|
/** Per-table read facade — `ctx.db.<table>` on a `QueryCtx`. */
|
|
325
413
|
type DatabaseReaderFacade<DM, REL extends Record<keyof DM, object>, RANK extends Record<keyof DM, string>, SEARCH extends Record<keyof DM, string>> = { readonly [T in keyof DM]: TableReaderFacade<DM, REL, RANK, SEARCH, T> };
|
|
326
414
|
/** Per-table read-write facade — `ctx.db.<table>` on a `MutationCtx` / `ActionCtx`. */
|
|
327
415
|
type DatabaseWriterFacade<DM, IM extends Record<keyof DM, object>, REL extends Record<keyof DM, object>, RANK extends Record<keyof DM, string>, SEARCH extends Record<keyof DM, string>> = { readonly [T in keyof DM]: TableWriterFacade<DM, IM, REL, RANK, SEARCH, T> };
|
|
328
|
-
export { AggregateOp, DatabaseReaderFacade, DatabaseWriterFacade, GroupByEntry, Id, LoadWith, ManyRelationWhere, OneRelationWhere, OrderBy, QueryArgs, QueryArgsOf, QueryPage, RankPage, RankResult, RestrictableQueryOptions, RestrictableQueryOptionsOf, SearchFilterBuilder, SearchReader, TableAggregateOptions, TableAggregateOptionsOf, TableGroupByOptions, TableGroupByOptionsOf, TableRankOptions, TableRankPageOptions, TableReaderFacade, TableWriterFacade, Where, WhereOf, WhereOperators, WithArg };
|
|
416
|
+
export { AggregateOp, DatabaseReaderFacade, DatabaseWriterFacade, GroupByEntry, Id, LoadWith, ManyRelationWhere, OneRelationWhere, OrderBy, QueryArgs, QueryArgsOf, QueryPage, RankPage, RankResult, RestrictableQueryOptions, RestrictableQueryOptionsOf, SearchFilterBuilder, SearchReader, TableAggregateOptions, TableAggregateOptionsOf, TableGroupByOptions, TableGroupByOptionsOf, TableRankOptions, TableRankPageOptions, TableReaderFacade, TableWriterFacade, UpsertTargetOf, Where, WhereOf, WhereOperators, WithArg };
|
package/dist/data-model.d.ts
CHANGED
|
@@ -49,6 +49,13 @@ interface QueryArgs<TDocument> {
|
|
|
49
49
|
cursor?: null | string;
|
|
50
50
|
limit?: number;
|
|
51
51
|
orderBy?: OrderBy<TDocument>[];
|
|
52
|
+
/**
|
|
53
|
+
* Project each returned row down to these columns (plus the system fields
|
|
54
|
+
* `_id`/`_creationTime`, always retained). Trims wire payload for wide rows;
|
|
55
|
+
* relations requested via `with` are still attached. Reactivity is unaffected —
|
|
56
|
+
* the engine still reads the whole row to track dependencies.
|
|
57
|
+
*/
|
|
58
|
+
select?: ReadonlyArray<keyof TDocument & string>;
|
|
52
59
|
where?: Where<TDocument>;
|
|
53
60
|
}
|
|
54
61
|
/**
|
|
@@ -97,6 +104,12 @@ type WhereOf<DM, REL extends Record<keyof DM, object>, T extends keyof DM> = Rel
|
|
|
97
104
|
/** {@link QueryArgs} with the relation-aware {@link WhereOf} `where` typing. */
|
|
98
105
|
interface QueryArgsOf<DM, REL extends Record<keyof DM, object>, T extends keyof DM> {
|
|
99
106
|
cursor?: null | string;
|
|
107
|
+
/**
|
|
108
|
+
* Include soft-deleted rows (`.softDelete()` tables only). Default hides them;
|
|
109
|
+
* `true` returns deleted rows alongside live ones. No effect on a table
|
|
110
|
+
* without `.softDelete()`.
|
|
111
|
+
*/
|
|
112
|
+
includeDeleted?: boolean;
|
|
100
113
|
limit?: number;
|
|
101
114
|
orderBy?: OrderBy<DM[T]>[];
|
|
102
115
|
where?: WhereOf<DM, REL, T>;
|
|
@@ -110,11 +123,15 @@ interface QueryPage<TDocument> {
|
|
|
110
123
|
type NestedWithArgument<WK> = WK extends {
|
|
111
124
|
with: infer NW;
|
|
112
125
|
} ? NW : {};
|
|
126
|
+
/** The nested `select` tuple inside a relation's with-value, or `undefined` (no projection). */
|
|
127
|
+
type NestedSelectArgument<WK> = WK extends {
|
|
128
|
+
select: infer S;
|
|
129
|
+
} ? S : undefined;
|
|
113
130
|
/**
|
|
114
131
|
* The `with` argument for table `T`: each relation can be `true` (load with no
|
|
115
|
-
* refinements) or an object. `many` relations accept `where`/`orderBy`/`limit
|
|
116
|
-
* plus a nested `with`; `one` relations accept
|
|
117
|
-
* reserved `_count` key requests per-relation aggregate counts.
|
|
132
|
+
* refinements) or an object. `many` relations accept `where`/`orderBy`/`limit`/
|
|
133
|
+
* `select` plus a nested `with`; `one` relations accept `select` + a nested
|
|
134
|
+
* `with`. The reserved `_count` key requests per-relation aggregate counts.
|
|
118
135
|
*/
|
|
119
136
|
type WithArg<DM, REL extends Record<keyof DM, object>, T extends keyof DM> = { [K in keyof REL[T]]?: REL[T][K] extends {
|
|
120
137
|
__relationKind: "many";
|
|
@@ -125,18 +142,24 @@ type WithArg<DM, REL extends Record<keyof DM, object>, T extends keyof DM> = { [
|
|
|
125
142
|
__relationKind: "one";
|
|
126
143
|
__target: infer Target extends keyof DM;
|
|
127
144
|
} ? boolean | {
|
|
145
|
+
select?: ReadonlyArray<keyof DM[Target] & string>;
|
|
128
146
|
with?: WithArg<DM, REL, Target>;
|
|
129
147
|
} : never } & {
|
|
130
148
|
_count?: { [K in keyof REL[T]]?: true };
|
|
131
149
|
};
|
|
132
|
-
/**
|
|
150
|
+
/**
|
|
151
|
+
* Resolve a single relation descriptor + its with-value to the loaded type,
|
|
152
|
+
* threading the nested `select` tuple into the projected child shape (the 5th
|
|
153
|
+
* `LoadWith` arg) so `with: { author: { select: ["name"] } }` narrows the loaded
|
|
154
|
+
* `author` to the selected columns + system fields.
|
|
155
|
+
*/
|
|
133
156
|
type LoadRelation<DM, REL extends Record<keyof DM, object>, R, WK> = R extends {
|
|
134
157
|
__relationKind: "one";
|
|
135
158
|
__target: infer Target extends keyof DM;
|
|
136
|
-
} ? LoadWith<DM, REL, Target, NestedWithArgument<WK>> | null : R extends {
|
|
159
|
+
} ? LoadWith<DM, REL, Target, NestedWithArgument<WK>, NestedSelectArgument<WK>> | null : R extends {
|
|
137
160
|
__relationKind: "many";
|
|
138
161
|
__target: infer Target extends keyof DM;
|
|
139
|
-
} ? LoadWith<DM, REL, Target, NestedWithArgument<WK>>[] : never;
|
|
162
|
+
} ? LoadWith<DM, REL, Target, NestedWithArgument<WK>, NestedSelectArgument<WK>>[] : never;
|
|
140
163
|
/** The relation keys of `W` that were actually requested (not `false`/`undefined`). */
|
|
141
164
|
type LoadedRelations<DM, REL extends Record<keyof DM, object>, T extends keyof DM, W> = { [K in keyof W as K extends keyof REL[T] ? (W[K] extends false | undefined ? never : K) : never]: K extends keyof REL[T] ? LoadRelation<DM, REL, REL[T][K], W[K]> : never };
|
|
142
165
|
/** The `_count` projection of `W`, if any. */
|
|
@@ -145,8 +168,20 @@ type LoadedCount<W> = W extends {
|
|
|
145
168
|
} ? {
|
|
146
169
|
_count: { [K in keyof C]: number };
|
|
147
170
|
} : {};
|
|
148
|
-
/**
|
|
149
|
-
type
|
|
171
|
+
/** System columns a `select` projection always retains, so cursors and by-id reuse keep working. */
|
|
172
|
+
type SelectAlwaysKeep<DM, T extends keyof DM> = ("_creationTime" | "_id") & keyof DM[T];
|
|
173
|
+
/**
|
|
174
|
+
* `DM[T]` narrowed to the columns named by a `select` tuple `S` (plus the system
|
|
175
|
+
* fields). `undefined` (the default — no `select`) keeps the full document.
|
|
176
|
+
*/
|
|
177
|
+
type ProjectDoc<DM, T extends keyof DM, S> = S extends ReadonlyArray<infer K> ? (K extends keyof DM[T] ? Pick<DM[T], (K & keyof DM[T]) | SelectAlwaysKeep<DM, T>> : DM[T]) : DM[T];
|
|
178
|
+
/**
|
|
179
|
+
* `Doc<T>` narrowed to exactly the relations requested in the with-arg `W` and,
|
|
180
|
+
* when a `select` tuple `S` is supplied, to its projected columns. `S` defaults
|
|
181
|
+
* to `undefined` so the 4-argument form (the codegen-emitted callers) keeps the
|
|
182
|
+
* full document.
|
|
183
|
+
*/
|
|
184
|
+
type LoadWith<DM, REL extends Record<keyof DM, object>, T extends keyof DM, W, S = undefined> = LoadedCount<W> & LoadedRelations<DM, REL, T, W> & ProjectDoc<DM, T, S>;
|
|
150
185
|
/** Reducer applied by an aggregate (`avg`/`count`/`max`/`min`/`sum`). */
|
|
151
186
|
type AggregateOp = "avg" | "count" | "max" | "min" | "sum";
|
|
152
187
|
/**
|
|
@@ -260,15 +295,20 @@ interface TableReaderFacade<DM, REL extends Record<keyof DM, object>, RANK exten
|
|
|
260
295
|
* uses to inject `baseWhere` and `restrictsCounts`.
|
|
261
296
|
*/
|
|
262
297
|
count: (where?: RestrictableQueryOptionsOf<DM, REL, T> | WhereOf<DM, REL, T>) => Promise<number>;
|
|
263
|
-
|
|
298
|
+
/** `true` when at least one row matches `where` (any row when omitted). RLS-filtered exactly like `findFirst`. */
|
|
299
|
+
exists: (where?: WhereOf<DM, REL, T>) => Promise<boolean>;
|
|
300
|
+
findFirst: <W extends WithArg<DM, REL, T> = {}, S extends ReadonlyArray<keyof DM[T] & string> | undefined = undefined>(args?: QueryArgsOf<DM, REL, T> & {
|
|
301
|
+
select?: S;
|
|
264
302
|
with?: W;
|
|
265
|
-
}) => Promise<LoadWith<DM, REL, T, W> | null>;
|
|
266
|
-
findFirstOrThrow: <W extends WithArg<DM, REL, T> = {}>(args?: QueryArgsOf<DM, REL, T> & {
|
|
303
|
+
}) => Promise<LoadWith<DM, REL, T, W, S> | null>;
|
|
304
|
+
findFirstOrThrow: <W extends WithArg<DM, REL, T> = {}, S extends ReadonlyArray<keyof DM[T] & string> | undefined = undefined>(args?: QueryArgsOf<DM, REL, T> & {
|
|
305
|
+
select?: S;
|
|
267
306
|
with?: W;
|
|
268
|
-
}) => Promise<LoadWith<DM, REL, T, W>>;
|
|
269
|
-
findMany: <W extends WithArg<DM, REL, T> = {}>(args?: QueryArgsOf<DM, REL, T> & {
|
|
307
|
+
}) => Promise<LoadWith<DM, REL, T, W, S>>;
|
|
308
|
+
findMany: <W extends WithArg<DM, REL, T> = {}, S extends ReadonlyArray<keyof DM[T] & string> | undefined = undefined>(args?: QueryArgsOf<DM, REL, T> & {
|
|
309
|
+
select?: S;
|
|
270
310
|
with?: W;
|
|
271
|
-
}) => Promise<QueryPage<LoadWith<DM, REL, T, W>>>;
|
|
311
|
+
}) => Promise<QueryPage<LoadWith<DM, REL, T, W, S>>>;
|
|
272
312
|
get: (id: Id<string & T>) => Promise<DM[T] | null>;
|
|
273
313
|
/**
|
|
274
314
|
* Group rows by the named keys and apply `agg` per group (defaults to
|
|
@@ -299,6 +339,11 @@ interface TableReaderFacade<DM, REL extends Record<keyof DM, object>, RANK exten
|
|
|
299
339
|
}
|
|
300
340
|
/** Read-write typed table accessor exposed on `MutationCtx.db.<table>` / `ActionCtx.db.<table>`. */
|
|
301
341
|
interface TableWriterFacade<DM, IM extends Record<keyof DM, object>, REL extends Record<keyof DM, object>, RANK extends Record<keyof DM, string>, SEARCH extends Record<keyof DM, string>, T extends keyof DM> extends TableReaderFacade<DM, REL, RANK, SEARCH, T> {
|
|
342
|
+
/**
|
|
343
|
+
* Delete a row by id. On a `.softDelete()` table this flips the marker column
|
|
344
|
+
* (and cascades as a soft delete) instead of removing the row; use
|
|
345
|
+
* {@link TableWriterFacade.hardDelete} to force physical removal.
|
|
346
|
+
*/
|
|
302
347
|
delete: (id: Id<string & T>) => Promise<void>;
|
|
303
348
|
/** Delete many rows in this table by id in one call; returns the *requested* id count (unknown ids are no-ops). Atomic within a mutation (a throw rolls the mutation back); an action has no transaction span. */
|
|
304
349
|
deleteMany: (ids: ReadonlyArray<Id<string & T>>, options?: {
|
|
@@ -306,7 +351,21 @@ interface TableWriterFacade<DM, IM extends Record<keyof DM, object>, REL extends
|
|
|
306
351
|
}) => Promise<{
|
|
307
352
|
deleted: number;
|
|
308
353
|
}>;
|
|
309
|
-
|
|
354
|
+
/** Physically remove a row (and physically cascade `onDelete`), bypassing `.softDelete()`. Same as `delete()` on a non-soft table. */
|
|
355
|
+
hardDelete: (id: Id<string & T>) => Promise<void>;
|
|
356
|
+
/**
|
|
357
|
+
* Insert a document, returning its minted id. With `{ skipDuplicates: true }`
|
|
358
|
+
* a UNIQUE-constraint breach resolves to `null` (the row already exists)
|
|
359
|
+
* instead of throwing — the return type widens to `Id | null` on that overload.
|
|
360
|
+
*/
|
|
361
|
+
insert: {
|
|
362
|
+
(values: IM[T], options: {
|
|
363
|
+
skipDuplicates: true;
|
|
364
|
+
}): Promise<Id<string & T> | null>;
|
|
365
|
+
(values: IM[T], options?: {
|
|
366
|
+
skipDuplicates?: boolean;
|
|
367
|
+
}): Promise<Id<string & T>>;
|
|
368
|
+
};
|
|
310
369
|
/** Insert many documents into this table in one call, returning the minted ids in input order. Atomic within a mutation (a throw rolls the mutation back); an action has no transaction span. */
|
|
311
370
|
insertMany: (values: ReadonlyArray<IM[T]>, options?: {
|
|
312
371
|
limit?: number;
|
|
@@ -320,9 +379,38 @@ interface TableWriterFacade<DM, IM extends Record<keyof DM, object>, REL extends
|
|
|
320
379
|
limit?: number;
|
|
321
380
|
}) => Promise<void>;
|
|
322
381
|
replace: (id: Id<string & T>, values: IM[T]) => Promise<void>;
|
|
382
|
+
/** Un-soft-delete a row by id: clears the `.softDelete()` marker so list reads see it again. Throws on a non-soft table. */
|
|
383
|
+
restore: (id: Id<string & T>) => Promise<void>;
|
|
384
|
+
/**
|
|
385
|
+
* Insert when no existing row matches `target`, otherwise patch the match with
|
|
386
|
+
* `update` (defaulting to `create`). `target` names a `.unique()` column (or a
|
|
387
|
+
* tuple) used to look it up. Returns the row id and whether it was `created`.
|
|
388
|
+
* Composes `findFirst` + `insert`/`patch`, so RLS gates each step.
|
|
389
|
+
*/
|
|
390
|
+
upsert: (args: {
|
|
391
|
+
create: IM[T];
|
|
392
|
+
target: UpsertTargetOf<DM, T>;
|
|
393
|
+
update?: Partial<IM[T]>;
|
|
394
|
+
}) => Promise<{
|
|
395
|
+
created: boolean;
|
|
396
|
+
id: Id<string & T>;
|
|
397
|
+
}>;
|
|
398
|
+
/** Sequential {@link TableWriterFacade.upsert} over many rows sharing one `target`; one result per input row, in order. */
|
|
399
|
+
upsertMany: (args: {
|
|
400
|
+
rows: ReadonlyArray<{
|
|
401
|
+
create: IM[T];
|
|
402
|
+
update?: Partial<IM[T]>;
|
|
403
|
+
}>;
|
|
404
|
+
target: UpsertTargetOf<DM, T>;
|
|
405
|
+
}) => Promise<{
|
|
406
|
+
created: boolean;
|
|
407
|
+
id: Id<string & T>;
|
|
408
|
+
}[]>;
|
|
323
409
|
}
|
|
410
|
+
/** Conflict target for `upsert`/`upsertMany`: one column of table `T`, or a tuple of them. */
|
|
411
|
+
type UpsertTargetOf<DM, T extends keyof DM> = ReadonlyArray<keyof DM[T] & string> | (keyof DM[T] & string);
|
|
324
412
|
/** Per-table read facade — `ctx.db.<table>` on a `QueryCtx`. */
|
|
325
413
|
type DatabaseReaderFacade<DM, REL extends Record<keyof DM, object>, RANK extends Record<keyof DM, string>, SEARCH extends Record<keyof DM, string>> = { readonly [T in keyof DM]: TableReaderFacade<DM, REL, RANK, SEARCH, T> };
|
|
326
414
|
/** Per-table read-write facade — `ctx.db.<table>` on a `MutationCtx` / `ActionCtx`. */
|
|
327
415
|
type DatabaseWriterFacade<DM, IM extends Record<keyof DM, object>, REL extends Record<keyof DM, object>, RANK extends Record<keyof DM, string>, SEARCH extends Record<keyof DM, string>> = { readonly [T in keyof DM]: TableWriterFacade<DM, IM, REL, RANK, SEARCH, T> };
|
|
328
|
-
export { AggregateOp, DatabaseReaderFacade, DatabaseWriterFacade, GroupByEntry, Id, LoadWith, ManyRelationWhere, OneRelationWhere, OrderBy, QueryArgs, QueryArgsOf, QueryPage, RankPage, RankResult, RestrictableQueryOptions, RestrictableQueryOptionsOf, SearchFilterBuilder, SearchReader, TableAggregateOptions, TableAggregateOptionsOf, TableGroupByOptions, TableGroupByOptionsOf, TableRankOptions, TableRankPageOptions, TableReaderFacade, TableWriterFacade, Where, WhereOf, WhereOperators, WithArg };
|
|
416
|
+
export { AggregateOp, DatabaseReaderFacade, DatabaseWriterFacade, GroupByEntry, Id, LoadWith, ManyRelationWhere, OneRelationWhere, OrderBy, QueryArgs, QueryArgsOf, QueryPage, RankPage, RankResult, RestrictableQueryOptions, RestrictableQueryOptionsOf, SearchFilterBuilder, SearchReader, TableAggregateOptions, TableAggregateOptionsOf, TableGroupByOptions, TableGroupByOptionsOf, TableRankOptions, TableRankPageOptions, TableReaderFacade, TableWriterFacade, UpsertTargetOf, Where, WhereOf, WhereOperators, WithArg };
|
package/dist/index.d.mts
CHANGED
|
@@ -325,21 +325,6 @@ declare class LunoraError extends Error {
|
|
|
325
325
|
constructor(code: LunoraErrorCode, message?: string);
|
|
326
326
|
}
|
|
327
327
|
/**
|
|
328
|
-
* The per-table `ctx.db` accessor (the `ctx.db.messages.findMany(...)` form) and
|
|
329
|
-
* the kitcn-style `ctx.orm` namespace, as plain runtime helpers. This is the ONE
|
|
330
|
-
* source of truth for the facade shape, shared by two callers so they can never
|
|
331
|
-
* drift (a drift here is security-relevant — a facade accessor the RLS
|
|
332
|
-
* middleware forgot to re-bind would read around policy). `@lunora/codegen`
|
|
333
|
-
* emits `ctx.db`/`ctx.orm` by calling these over the raw shard writer (and the
|
|
334
|
-
* D1 `globalDb` writer for `.global()` tables); the RLS middleware re-binds the
|
|
335
|
-
* policy tables by calling them over the policy-enforcing wrapped writer.
|
|
336
|
-
*
|
|
337
|
-
* `bindTableFacade(writer, table)` pins `tableName` on the structural writer so
|
|
338
|
-
* callers address rows by id (`get`/`delete`/`patch`/`replace`) or by the bound
|
|
339
|
-
* table (everything else). The binding is identical regardless of which writer
|
|
340
|
-
* is passed — that's the whole point.
|
|
341
|
-
*/
|
|
342
|
-
/**
|
|
343
328
|
* Minimal structural writer the facade binds over. Declared with **method**
|
|
344
329
|
* syntax (not arrow properties) so a more-specifically-typed writer — both
|
|
345
330
|
* `@lunora/do`'s `DatabaseWriterLike` and the RLS middleware's wrapped writer —
|
|
@@ -349,7 +334,9 @@ declare class LunoraError extends Error {
|
|
|
349
334
|
interface FacadeWriterLike {
|
|
350
335
|
aggregate(tableName: string, options: unknown): Promise<unknown>;
|
|
351
336
|
count(tableName: string, where?: unknown): Promise<number>;
|
|
352
|
-
delete(id: string, expectedTable?: string
|
|
337
|
+
delete(id: string, expectedTable?: string, options?: {
|
|
338
|
+
hard?: boolean;
|
|
339
|
+
}): Promise<void>;
|
|
353
340
|
deleteMany?(ids: ReadonlyArray<string>, options?: {
|
|
354
341
|
limit?: number;
|
|
355
342
|
}, expectedTable?: string): Promise<{
|
|
@@ -377,6 +364,7 @@ interface FacadeWriterLike {
|
|
|
377
364
|
rank(tableName: string, indexName: string, options: unknown): Promise<unknown>;
|
|
378
365
|
rankPage(tableName: string, indexName: string, options?: unknown): Promise<unknown>;
|
|
379
366
|
replace(id: string, document: Record<string, unknown>, expectedTable?: string): Promise<void>;
|
|
367
|
+
restore?(id: string, expectedTable?: string): Promise<void>;
|
|
380
368
|
}
|
|
381
369
|
/** The per-table accessor object returned for the `ctx.db` table form. */
|
|
382
370
|
interface FacadeEntry {
|
|
@@ -388,12 +376,16 @@ interface FacadeEntry {
|
|
|
388
376
|
}) => Promise<{
|
|
389
377
|
deleted: number;
|
|
390
378
|
}>;
|
|
379
|
+
/** `true` when at least one row matches `where` (or any row exists when omitted). Honors RLS like `findFirst`. */
|
|
380
|
+
exists: (where?: unknown) => Promise<boolean>;
|
|
391
381
|
findFirst: (args?: unknown) => Promise<unknown>;
|
|
392
382
|
findFirstOrThrow: (args?: unknown) => Promise<unknown>;
|
|
393
383
|
findMany: (args?: unknown) => Promise<unknown>;
|
|
394
384
|
get: (id: string) => Promise<unknown>;
|
|
395
385
|
groupBy: (options: unknown) => Promise<unknown>;
|
|
396
|
-
|
|
386
|
+
/** Physically remove a row (and physically cascade), bypassing `.softDelete()`. */
|
|
387
|
+
hardDelete: (id: string) => Promise<void>;
|
|
388
|
+
insert: (document: Record<string, unknown>, options?: FacadeInsertOptions) => Promise<null | string>;
|
|
397
389
|
insertMany: (documents: ReadonlyArray<Record<string, unknown>>, options?: {
|
|
398
390
|
limit?: number;
|
|
399
391
|
}) => Promise<string[]>;
|
|
@@ -407,8 +399,47 @@ interface FacadeEntry {
|
|
|
407
399
|
rank: (indexName: string, options: unknown) => Promise<unknown>;
|
|
408
400
|
rankPage: (indexName: string, options?: unknown) => Promise<unknown>;
|
|
409
401
|
replace: (id: string, document: Record<string, unknown>) => Promise<void>;
|
|
402
|
+
/** Un-soft-delete a row: clears the `.softDelete()` marker (by-id, so it reaches a row list reads hide). */
|
|
403
|
+
restore: (id: string) => Promise<void>;
|
|
404
|
+
/** Insert when no row matches `target`, else patch the match. Composes `findFirst` + `insert`/`patch`, so RLS applies to each step. */
|
|
405
|
+
upsert: (args: UpsertArgs) => Promise<UpsertResult>;
|
|
406
|
+
/** Sequential `upsert` over many rows sharing one `target`; returns one result per input row in order. */
|
|
407
|
+
upsertMany: (args: UpsertManyArgs) => Promise<UpsertResult[]>;
|
|
410
408
|
withSearchIndex: (indexName: string, search: (q: unknown) => unknown) => unknown;
|
|
411
409
|
}
|
|
410
|
+
/** Options accepted by the per-table `insert` accessor. */
|
|
411
|
+
interface FacadeInsertOptions {
|
|
412
|
+
/**
|
|
413
|
+
* When `true`, a UNIQUE-constraint breach is swallowed: the insert becomes a
|
|
414
|
+
* silent no-op and resolves to `null` instead of throwing a `CONFLICT`. Any
|
|
415
|
+
* other error still propagates. Mirrors better-drizzle's `create({ skipDuplicates })`.
|
|
416
|
+
*/
|
|
417
|
+
skipDuplicates?: boolean;
|
|
418
|
+
}
|
|
419
|
+
/** The conflict target for `upsert`/`upsertMany`: one field name or a tuple of them. */
|
|
420
|
+
type UpsertTarget = ReadonlyArray<string> | string;
|
|
421
|
+
/** Argument to the per-table `upsert` accessor. */
|
|
422
|
+
interface UpsertArgs {
|
|
423
|
+
/** Document inserted when no existing row matches the `target`. */
|
|
424
|
+
create: Record<string, unknown>;
|
|
425
|
+
/** Field(s) — typically a `.unique()` column or unique index — used to look up an existing row. */
|
|
426
|
+
target: UpsertTarget;
|
|
427
|
+
/** Patch applied when an existing row matches the `target`. Defaults to `create`. */
|
|
428
|
+
update?: Record<string, unknown>;
|
|
429
|
+
}
|
|
430
|
+
/** Result of an `upsert`: the row's id and whether it was freshly inserted (`true`) or updated (`false`). */
|
|
431
|
+
interface UpsertResult {
|
|
432
|
+
created: boolean;
|
|
433
|
+
id: string;
|
|
434
|
+
}
|
|
435
|
+
/** Argument to the per-table `upsertMany` accessor — a shared `target` plus per-row create/update payloads. */
|
|
436
|
+
interface UpsertManyArgs {
|
|
437
|
+
rows: ReadonlyArray<{
|
|
438
|
+
create: Record<string, unknown>;
|
|
439
|
+
update?: Record<string, unknown>;
|
|
440
|
+
}>;
|
|
441
|
+
target: UpsertTarget;
|
|
442
|
+
}
|
|
412
443
|
/**
|
|
413
444
|
* Bind a structural writer to one table, producing its `ctx.db` table accessor.
|
|
414
445
|
*
|
|
@@ -426,7 +457,7 @@ declare const bindTableFacade: (writer: FacadeWriterLike, tableName: string) =>
|
|
|
426
457
|
interface OrmLike {
|
|
427
458
|
delete: (table: string, id: string) => Promise<void>;
|
|
428
459
|
insert: (table: string) => {
|
|
429
|
-
values: (document: Record<string, unknown>) => Promise<string>;
|
|
460
|
+
values: (document: Record<string, unknown>) => Promise<null | string>;
|
|
430
461
|
};
|
|
431
462
|
query: Record<string, FacadeEntry>;
|
|
432
463
|
replace: (table: string, id: string) => {
|
|
@@ -1168,6 +1199,22 @@ interface TableBuilder<Shape extends Record<string, Validator> = Record<string,
|
|
|
1168
1199
|
}) => TableBuilder<Shape>;
|
|
1169
1200
|
/** Route storage by the named field — one DO per distinct value. */
|
|
1170
1201
|
shardBy: (field: keyof Shape & string) => TableBuilder<Shape>;
|
|
1202
|
+
/**
|
|
1203
|
+
* Turn on soft delete. Adds a nullable timestamp column (`options.field`,
|
|
1204
|
+
* default `deletedAt`) and changes `ctx.db.<table>.delete()` to **set** it
|
|
1205
|
+
* instead of removing the row; `onDelete: "cascade"` children are recursively
|
|
1206
|
+
* soft-deleted too. **List reads** (`findMany`/`findFirst`/`query()`/`count`/
|
|
1207
|
+
* `aggregate`/relation loads) then hide soft-deleted rows unless they pass
|
|
1208
|
+
* `includeDeleted: true`; by-id `get`/`patch`/`replace` and the new
|
|
1209
|
+
* `restore()` still address the row directly. `hardDelete()` physically
|
|
1210
|
+
* removes it (cascading as a real delete). Note: `includeDeleted` is a read
|
|
1211
|
+
* scope, not access control — anyone who can run the read can set it; a unique
|
|
1212
|
+
* index still rejects a new row that collides with a soft-deleted one (the row
|
|
1213
|
+
* physically persists).
|
|
1214
|
+
*/
|
|
1215
|
+
softDelete: (options?: {
|
|
1216
|
+
field?: string;
|
|
1217
|
+
}) => TableBuilder<Shape>;
|
|
1171
1218
|
/** Declare named lifecycle triggers fired inline within the write path. */
|
|
1172
1219
|
triggers: (build: (t: TriggerBuilder<Shape>) => Record<string, TriggerDefinition>) => TableBuilder<Shape>;
|
|
1173
1220
|
/** Declare a vector index over a single text field on this table. */
|
|
@@ -1190,7 +1237,7 @@ interface VectorIndexOptions {
|
|
|
1190
1237
|
* Build a table definition. Returned object is both the table definition (for
|
|
1191
1238
|
* `defineSchema`) and a fluent builder for indexes + sharding metadata.
|
|
1192
1239
|
*/
|
|
1193
|
-
declare const defineTable: <Shape extends Record<string, Validator>>(
|
|
1240
|
+
declare const defineTable: <Shape extends Record<string, Validator>>(inputShape: Shape) => TableBuilder<Shape>;
|
|
1194
1241
|
/**
|
|
1195
1242
|
* Declare a standalone vector index (DSL Shape B). Pass the returned value in
|
|
1196
1243
|
* the `vectorIndexes` map of {@link defineSchema} when the source is derived
|
|
@@ -1562,7 +1609,9 @@ interface DatabaseWriterLike {
|
|
|
1562
1609
|
*/
|
|
1563
1610
|
aggregate: (tableName: string, options: AggregateArgs) => Promise<null | number>;
|
|
1564
1611
|
count: (tableName: string, whereOrArgs?: CountArgs | WhereInput) => Promise<number>;
|
|
1565
|
-
delete: (id: string, expectedTable?: string
|
|
1612
|
+
delete: (id: string, expectedTable?: string, options?: {
|
|
1613
|
+
hard?: boolean;
|
|
1614
|
+
}) => Promise<void>;
|
|
1566
1615
|
deleteMany: (ids: ReadonlyArray<string>, options?: {
|
|
1567
1616
|
limit?: number;
|
|
1568
1617
|
}, expectedTable?: string) => Promise<{
|
|
@@ -1633,6 +1682,7 @@ interface DatabaseWriterLike {
|
|
|
1633
1682
|
*/
|
|
1634
1683
|
rankPage: (tableName: string, indexName: string, options?: RankPageArgs) => Promise<QueryPage>;
|
|
1635
1684
|
replace: (id: string, document: Record<string, unknown>, expectedTable?: string) => Promise<void>;
|
|
1685
|
+
restore?: (id: string, expectedTable?: string) => Promise<void>;
|
|
1636
1686
|
}
|
|
1637
1687
|
/**
|
|
1638
1688
|
* What a procedure's `ctx.db` must structurally satisfy for the middleware
|