@prisma-next-idb/client-idb 0.1.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/LICENSE +21 -0
- package/dist/client-auto.d.mts +95 -0
- package/dist/client-auto.d.mts.map +1 -0
- package/dist/client-auto.mjs +197 -0
- package/dist/client-auto.mjs.map +1 -0
- package/dist/client.d.mts +2 -0
- package/dist/client.mjs +2 -0
- package/dist/idb-client-BJ6OoA2W.mjs +54 -0
- package/dist/idb-client-BJ6OoA2W.mjs.map +1 -0
- package/dist/idb-client-CiyW5_TQ.d.mts +52 -0
- package/dist/idb-client-CiyW5_TQ.d.mts.map +1 -0
- package/dist/idb-orm-B5H6xka-.d.mts +702 -0
- package/dist/idb-orm-B5H6xka-.d.mts.map +1 -0
- package/dist/idb-orm-NHIZ3oNt.mjs +1526 -0
- package/dist/idb-orm-NHIZ3oNt.mjs.map +1 -0
- package/dist/orm.d.mts +55 -0
- package/dist/orm.d.mts.map +1 -0
- package/dist/orm.mjs +28 -0
- package/dist/orm.mjs.map +1 -0
- package/package.json +57 -0
|
@@ -0,0 +1,702 @@
|
|
|
1
|
+
import { AsyncIterableResult } from "@prisma-next/framework-components/runtime";
|
|
2
|
+
import { IdbFieldFilter, IdbFilterExpr, IdbNullCheckExpr, IdbQueryPlan } from "@prisma-next-idb/adapter-idb/runtime";
|
|
3
|
+
import { Contract, ContractModelBase, ContractReferenceRelation } from "@prisma-next/contract/types";
|
|
4
|
+
import { ExtractIdbFieldInputTypes, ExtractIdbFieldOutputTypes, IdbStorage } from "@prisma-next-idb/target-idb/pack";
|
|
5
|
+
|
|
6
|
+
//#region src/core/types.d.ts
|
|
7
|
+
/**
|
|
8
|
+
* Extract the model map from a contract by traversing the domain namespace.
|
|
9
|
+
*
|
|
10
|
+
* 0.14.0: `Contract` dropped the 2nd `TModels` type parameter; models are now
|
|
11
|
+
* accessed via `domain.namespaces[ns].models`. For a single-namespace contract
|
|
12
|
+
* (the only IDB case) this resolves to the typed model map; falls back to
|
|
13
|
+
* `Record<string, ContractModelBase>` for un-typed/multi-namespace inputs.
|
|
14
|
+
*/
|
|
15
|
+
type ModelsOf<TContract> = TContract extends {
|
|
16
|
+
readonly domain: {
|
|
17
|
+
readonly namespaces: infer NS;
|
|
18
|
+
};
|
|
19
|
+
} ? NS[keyof NS] extends {
|
|
20
|
+
readonly models: infer M;
|
|
21
|
+
} ? M extends Record<string, ContractModelBase> ? M : Record<string, ContractModelBase> : Record<string, ContractModelBase> : Record<string, ContractModelBase>;
|
|
22
|
+
/**
|
|
23
|
+
* A `Contract` narrowed to the IDB storage shape. Accepts any contract whose
|
|
24
|
+
* `storage` block includes an `stores` record, regardless of the model set or
|
|
25
|
+
* whether type maps are attached.
|
|
26
|
+
*/
|
|
27
|
+
type IdbContract = Contract<IdbStorage>;
|
|
28
|
+
/**
|
|
29
|
+
* Resolve the output row type for a model from the contract's type maps.
|
|
30
|
+
*
|
|
31
|
+
* When `fieldOutputTypes` is parameterised (the emitted contract carries
|
|
32
|
+
* `IdbContractWithTypeMaps<Base, IdbTypeMaps<Codecs, FieldOutputTypes, ...>>`),
|
|
33
|
+
* each field gets the exact TypeScript type emitted by the family (e.g. `Date`
|
|
34
|
+
* for `idb/date@1`).
|
|
35
|
+
*
|
|
36
|
+
* Falls back to `Record<string, unknown>` when type maps are absent (plain
|
|
37
|
+
* `IdbContract` or when the model name is not in the type maps).
|
|
38
|
+
*/
|
|
39
|
+
type NsFieldOutputTypes<TContract> = ExtractIdbFieldOutputTypes<TContract>[keyof ExtractIdbFieldOutputTypes<TContract>];
|
|
40
|
+
type ResolvedOutputRow<TContract, ModelName extends string> = string extends keyof ExtractIdbFieldOutputTypes<TContract> ? Record<string, unknown> : NsFieldOutputTypes<TContract> extends Record<string, unknown> ? ModelName extends keyof NsFieldOutputTypes<TContract> ? { -readonly [K in keyof NsFieldOutputTypes<TContract>[ModelName]]: NsFieldOutputTypes<TContract>[ModelName][K] } : Record<string, unknown> : Record<string, unknown>;
|
|
41
|
+
/**
|
|
42
|
+
* Resolve the input row type for a model from the contract's type maps.
|
|
43
|
+
*
|
|
44
|
+
* Mirrors `ResolvedOutputRow` but uses `fieldInputTypes` — the input types
|
|
45
|
+
* used for `create()`, `where()`, and mutation payloads.
|
|
46
|
+
*/
|
|
47
|
+
type NsFieldInputTypes<TContract> = ExtractIdbFieldInputTypes<TContract>[keyof ExtractIdbFieldInputTypes<TContract>];
|
|
48
|
+
type ResolvedInputRow<TContract, ModelName extends string> = string extends keyof ExtractIdbFieldInputTypes<TContract> ? Record<string, unknown> : NsFieldInputTypes<TContract> extends Record<string, unknown> ? ModelName extends keyof NsFieldInputTypes<TContract> ? { -readonly [K in keyof NsFieldInputTypes<TContract>[ModelName]]: NsFieldInputTypes<TContract>[ModelName][K] } : Record<string, unknown> : Record<string, unknown>;
|
|
49
|
+
/** The full TypeScript row shape returned by `all()`, `first()`, and `create()`. */
|
|
50
|
+
type DefaultModelRow<TContract, ModelName extends string> = ResolvedOutputRow<TContract, ModelName>;
|
|
51
|
+
/**
|
|
52
|
+
* Partial equality filter applied as an in-memory predicate during cursor scans.
|
|
53
|
+
*
|
|
54
|
+
* All fields are optional — only provided fields are checked. Values use the
|
|
55
|
+
* output types (from `fieldOutputTypes`) since the values are compared against
|
|
56
|
+
* decoded row data.
|
|
57
|
+
*/
|
|
58
|
+
type WhereFilter<TContract, ModelName extends string> = { readonly [K in keyof DefaultModelRow<TContract, ModelName>]?: DefaultModelRow<TContract, ModelName>[K] };
|
|
59
|
+
/**
|
|
60
|
+
* The literal `keyPath` string for a model, extracted from
|
|
61
|
+
* `contract.models[ModelName].storage.keyPath`.
|
|
62
|
+
*
|
|
63
|
+
* Used at the type level to exclude the key field from `CreateInput` and to
|
|
64
|
+
* narrow the `findUnique` / `delete` key parameter type.
|
|
65
|
+
*/
|
|
66
|
+
type ModelKeyPath<TContract, ModelName extends string> = ModelName extends keyof ModelsOf<TContract> ? ModelsOf<TContract>[ModelName] extends {
|
|
67
|
+
storage: {
|
|
68
|
+
keyPath: infer P;
|
|
69
|
+
};
|
|
70
|
+
} ? P extends string ? P : never : never : never;
|
|
71
|
+
/**
|
|
72
|
+
* TypeScript type of the primary key field for a given model.
|
|
73
|
+
*
|
|
74
|
+
* When the output row type has a field matching `ModelKeyPath`, that field's
|
|
75
|
+
* type is used. Otherwise falls back to `IDBValidKey` (the DOM union of valid
|
|
76
|
+
* IDB key types).
|
|
77
|
+
*/
|
|
78
|
+
type KeyType<TContract, ModelName extends string> = ModelKeyPath<TContract, ModelName> extends keyof ResolvedOutputRow<TContract, ModelName> ? ResolvedOutputRow<TContract, ModelName>[ModelKeyPath<TContract, ModelName>] : IDBValidKey;
|
|
79
|
+
/** The keyPath field when it exists in the resolved input row (may be absent for models with no typed maps). */
|
|
80
|
+
type KeyPathField<TContract, ModelName extends string> = ModelKeyPath<TContract, ModelName> & keyof ResolvedInputRow<TContract, ModelName>;
|
|
81
|
+
/**
|
|
82
|
+
* Input shape for `create()`: the full input row with the primary key field
|
|
83
|
+
* made optional (IDB can generate keys via `autoIncrement`, or clients may
|
|
84
|
+
* omit the key when using `cuid()` / `uuid()` at the application layer).
|
|
85
|
+
*/
|
|
86
|
+
type CreateInput<TContract, ModelName extends string> = Omit<ResolvedInputRow<TContract, ModelName>, ModelKeyPath<TContract, ModelName>> & Partial<Pick<ResolvedInputRow<TContract, ModelName>, KeyPathField<TContract, ModelName>>>;
|
|
87
|
+
/** Extract the relations record for a model. */
|
|
88
|
+
type ModelRelations<TContract, ModelName extends string> = ModelName extends keyof ModelsOf<TContract> ? ModelsOf<TContract>[ModelName] extends {
|
|
89
|
+
relations: infer R;
|
|
90
|
+
} ? R extends Record<string, unknown> ? R : Record<string, never> : Record<string, never> : Record<string, never>;
|
|
91
|
+
/**
|
|
92
|
+
* Union of relation keys on a model that are `ContractReferenceRelation`s
|
|
93
|
+
* (i.e. cross-store joins, not embedded documents).
|
|
94
|
+
*
|
|
95
|
+
* Used to constrain the `include()` method's `relation` parameter to only
|
|
96
|
+
* valid reference relation names.
|
|
97
|
+
*/
|
|
98
|
+
type ReferenceRelKeys<TContract, ModelName extends string> = { [K in keyof ModelRelations<TContract, ModelName>]: ModelRelations<TContract, ModelName>[K] extends ContractReferenceRelation ? K : never }[keyof ModelRelations<TContract, ModelName>] & string;
|
|
99
|
+
/**
|
|
100
|
+
* TypeScript row type for an included relation.
|
|
101
|
+
*
|
|
102
|
+
* - `1:N` cardinality → `RelatedRow[]`
|
|
103
|
+
* - `N:1` or `1:1` cardinality → `RelatedRow | null`
|
|
104
|
+
*/
|
|
105
|
+
type RelationRowType<TContract, ModelName extends string, RelKey extends string> = RelKey extends keyof ModelRelations<TContract, ModelName> ? ModelRelations<TContract, ModelName>[RelKey] extends ContractReferenceRelation ? ModelRelations<TContract, ModelName>[RelKey] extends {
|
|
106
|
+
to: {
|
|
107
|
+
model: infer To extends string;
|
|
108
|
+
};
|
|
109
|
+
cardinality: infer C;
|
|
110
|
+
} ? C extends "1:N" ? DefaultModelRow<TContract, To>[] : DefaultModelRow<TContract, To> | null : never : never : never;
|
|
111
|
+
/**
|
|
112
|
+
* Per-relation include marker tracked at the type level.
|
|
113
|
+
*
|
|
114
|
+
* - `true` — the relation is loaded as rows (array for `1:N`, single/null
|
|
115
|
+
* otherwise), optionally refined by a `where`/`orderBy`/`take` callback.
|
|
116
|
+
* - `"scalar"` — the relation is reduced to a `count()` (Phase 6.5), so the
|
|
117
|
+
* row field becomes a `number` instead of related rows.
|
|
118
|
+
*/
|
|
119
|
+
type IncludeMarker = true | "scalar";
|
|
120
|
+
/** Which relations are included in the current accessor chain. */
|
|
121
|
+
type IncludeSpec<TContract, ModelName extends string> = Partial<Record<ReferenceRelKeys<TContract, ModelName>, IncludeMarker>>;
|
|
122
|
+
/** Empty include spec — no relations included. */
|
|
123
|
+
type NoIncludes = Record<never, never>;
|
|
124
|
+
/**
|
|
125
|
+
* The relation fields contributed by a set of `.include()` calls.
|
|
126
|
+
*
|
|
127
|
+
* A key is added only when its `TIncludes` marker is set; the field type is
|
|
128
|
+
* `number` for a scalar `count()` include and the cardinality-shaped related
|
|
129
|
+
* row(s) otherwise. Split out from {@link IncludedRow} so {@link SelectedRow}
|
|
130
|
+
* can re-use it on top of a projected (picked) scalar base.
|
|
131
|
+
*/
|
|
132
|
+
type IncludeFields<TContract, ModelName extends string, TIncludes extends IncludeSpec<TContract, ModelName>> = { -readonly [K in keyof TIncludes & string as TIncludes[K] extends IncludeMarker ? K : never]: TIncludes[K] extends "scalar" ? number : RelationRowType<TContract, ModelName, K> };
|
|
133
|
+
/**
|
|
134
|
+
* A row type that merges the base model row with any included relation fields.
|
|
135
|
+
*
|
|
136
|
+
* The extra fields are only added when the corresponding key in `TIncludes` is
|
|
137
|
+
* set, so the type stays narrow until `.include()` is called.
|
|
138
|
+
*/
|
|
139
|
+
type IncludedRow<TContract, ModelName extends string, TIncludes extends IncludeSpec<TContract, ModelName>> = DefaultModelRow<TContract, ModelName> & IncludeFields<TContract, ModelName, TIncludes>;
|
|
140
|
+
/**
|
|
141
|
+
* The row type after an optional `.select()` projection.
|
|
142
|
+
*
|
|
143
|
+
* When `TSelected` is `never` (no `.select()` call) the full {@link IncludedRow}
|
|
144
|
+
* is returned. Otherwise the scalar base is narrowed to the picked fields, with
|
|
145
|
+
* any included relation fields preserved (mirrors the vendor `select()` which
|
|
146
|
+
* keeps relations and narrows only scalar columns).
|
|
147
|
+
*/
|
|
148
|
+
type SelectedRow<TContract, ModelName extends string, TIncludes extends IncludeSpec<TContract, ModelName>, TSelected extends string> = [TSelected] extends [never] ? IncludedRow<TContract, ModelName, TIncludes> : Pick<DefaultModelRow<TContract, ModelName>, TSelected & keyof DefaultModelRow<TContract, ModelName>> & IncludeFields<TContract, ModelName, TIncludes>;
|
|
149
|
+
/**
|
|
150
|
+
* Partial update shape for `update()`, `updateAll()`, `updateCount()`, and the
|
|
151
|
+
* `update` arm of `upsert()`. All fields are optional — only provided fields
|
|
152
|
+
* are shallow-merged onto the existing record.
|
|
153
|
+
*/
|
|
154
|
+
type PatchInput<TContract, ModelName extends string> = Partial<DefaultModelRow<TContract, ModelName>>;
|
|
155
|
+
/** Extracts the `to` model name for a named relation on a model. */
|
|
156
|
+
type RelatedModelOf<TContract, ModelName extends string, RelName extends string> = ModelName extends keyof ModelsOf<TContract> ? ModelsOf<TContract>[ModelName] extends {
|
|
157
|
+
relations: Record<RelName, {
|
|
158
|
+
to: {
|
|
159
|
+
model: infer To extends string;
|
|
160
|
+
};
|
|
161
|
+
}>;
|
|
162
|
+
} ? To : string : string;
|
|
163
|
+
/** Nested-create descriptor: insert one or more related records. */
|
|
164
|
+
interface RelationMutationCreate<TContract, ModelName extends string> {
|
|
165
|
+
readonly kind: "create";
|
|
166
|
+
readonly data: readonly CreateInput<TContract, ModelName>[];
|
|
167
|
+
}
|
|
168
|
+
/** Nested-connect descriptor: link existing records to the parent via FK update. */
|
|
169
|
+
interface RelationMutationConnect {
|
|
170
|
+
readonly kind: "connect";
|
|
171
|
+
readonly criteria: readonly Record<string, unknown>[];
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Nested-disconnect descriptor: unlink related records by setting FK to null.
|
|
175
|
+
* With no criteria, disconnects all child records from this parent.
|
|
176
|
+
*/
|
|
177
|
+
interface RelationMutationDisconnect {
|
|
178
|
+
readonly kind: "disconnect";
|
|
179
|
+
readonly criteria?: readonly Record<string, unknown>[];
|
|
180
|
+
}
|
|
181
|
+
/** Discriminated union of all nested relation mutation descriptors. */
|
|
182
|
+
type IdbRelationMutation<TContract, ModelName extends string> = RelationMutationCreate<TContract, ModelName> | RelationMutationConnect | RelationMutationDisconnect;
|
|
183
|
+
/** Relation mutator object passed to the user's relation callback. */
|
|
184
|
+
interface IdbRelationMutator<TContract, ModelName extends string> {
|
|
185
|
+
create(data: CreateInput<TContract, ModelName> | readonly CreateInput<TContract, ModelName>[]): RelationMutationCreate<TContract, ModelName>;
|
|
186
|
+
connect(criteria: Record<string, unknown> | readonly Record<string, unknown>[]): RelationMutationConnect;
|
|
187
|
+
disconnect(criteria?: readonly Record<string, unknown>[]): RelationMutationDisconnect;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Maps each reference relation key to an optional mutation callback.
|
|
191
|
+
*
|
|
192
|
+
* When `ReferenceRelKeys` widens to `string` (a loosely-typed `IdbContract`
|
|
193
|
+
* with no emitted type maps), a naive mapped type would become an index
|
|
194
|
+
* signature `{ [k: string]: callback }` that incorrectly forces *every* field —
|
|
195
|
+
* scalars included — to be a relation callback, so even `create({ name: "x" })`
|
|
196
|
+
* would fail to type-check. The `string extends …` guard detects that case and
|
|
197
|
+
* contributes no constraint (`& unknown` is identity), leaving plain scalar
|
|
198
|
+
* payloads valid. Precisely-typed contracts (the emitted `contract.d.ts`)
|
|
199
|
+
* resolve `ReferenceRelKeys` to a finite union and get the full callback typing.
|
|
200
|
+
*/
|
|
201
|
+
type RelationMutationFields<TContract, ModelName extends string> = string extends ReferenceRelKeys<TContract, ModelName> ? unknown : Partial<{ [K in ReferenceRelKeys<TContract, ModelName>]: (mutator: IdbRelationMutator<TContract, RelatedModelOf<TContract, ModelName, K>>) => IdbRelationMutation<TContract, RelatedModelOf<TContract, ModelName, K>> }>;
|
|
202
|
+
/**
|
|
203
|
+
* The `localFields` of all N:1 relations on a model — the FK fields that are
|
|
204
|
+
* owned by this model and can be supplied via a relation callback instead of
|
|
205
|
+
* as a scalar value. Resolves to `never` for loosely-typed contracts where
|
|
206
|
+
* cardinality is not preserved as a literal (e.g. `defineContract` in tests).
|
|
207
|
+
*
|
|
208
|
+
* Mirrors `ChildForeignKeyFieldNames` from `sql-orm-client`, simplified: instead
|
|
209
|
+
* of crawling all models for relations pointing *to* this one, we look at this
|
|
210
|
+
* model's own N:1 relations directly — same field set, one model.
|
|
211
|
+
*/
|
|
212
|
+
type N1LocalFieldNames<TContract, ModelName extends string> = { [K in keyof ModelRelations<TContract, ModelName>]: ModelRelations<TContract, ModelName>[K] extends {
|
|
213
|
+
readonly cardinality: "N:1";
|
|
214
|
+
readonly on: {
|
|
215
|
+
readonly localFields: infer Fields extends readonly string[];
|
|
216
|
+
};
|
|
217
|
+
} ? Fields[number] : never }[keyof ModelRelations<TContract, ModelName>] & string;
|
|
218
|
+
/**
|
|
219
|
+
* Like `CreateInput` but with N:1 FK fields made optional.
|
|
220
|
+
*
|
|
221
|
+
* An N:1 FK field (e.g. `authorId` on `Post`) can be supplied either as a
|
|
222
|
+
* scalar value or via a relation callback (`author: (rel) => rel.connect({id})`).
|
|
223
|
+
* Making it optional here lets callers omit it when using the callback form —
|
|
224
|
+
* the executor populates it from the related record before inserting.
|
|
225
|
+
*
|
|
226
|
+
* Mirrors `NestedCreateInput` from `sql-orm-client`.
|
|
227
|
+
*/
|
|
228
|
+
type NestedCreateInput<TContract, ModelName extends string> = Omit<CreateInput<TContract, ModelName>, N1LocalFieldNames<TContract, ModelName>> & Partial<Pick<CreateInput<TContract, ModelName>, N1LocalFieldNames<TContract, ModelName> & keyof CreateInput<TContract, ModelName>>>;
|
|
229
|
+
/**
|
|
230
|
+
* Input shape for `create()` with optional relation callbacks.
|
|
231
|
+
* N:1 FK fields (e.g. `authorId`) are optional when using a relation callback.
|
|
232
|
+
* Relation fields accept a callback `(rel) => rel.create([...])` / `rel.connect(...)`.
|
|
233
|
+
*/
|
|
234
|
+
type MutationCreateInput<TContract, ModelName extends string> = NestedCreateInput<TContract, ModelName> & RelationMutationFields<TContract, ModelName>;
|
|
235
|
+
/**
|
|
236
|
+
* Input shape for `update()` with optional relation callbacks.
|
|
237
|
+
* All scalar fields are optional (shallow merge); relation fields accept
|
|
238
|
+
* `connect` or `disconnect` callbacks.
|
|
239
|
+
*/
|
|
240
|
+
type MutationUpdateInput<TContract, ModelName extends string> = PatchInput<TContract, ModelName> & RelationMutationFields<TContract, ModelName>;
|
|
241
|
+
/** Sort direction for `orderBy()`. */
|
|
242
|
+
type SortDirection = "asc" | "desc";
|
|
243
|
+
/** Partial sort spec: field name → direction. */
|
|
244
|
+
type OrderBySpec<TContract, ModelName extends string> = Partial<Record<string & keyof DefaultModelRow<TContract, ModelName>, SortDirection>>;
|
|
245
|
+
/** The five aggregation functions, matching the vendor `AggregateFn`. */
|
|
246
|
+
type AggregateFn = "count" | "sum" | "avg" | "min" | "max";
|
|
247
|
+
/**
|
|
248
|
+
* Fields eligible for numeric aggregation (`sum`/`avg`/`min`/`max`).
|
|
249
|
+
*
|
|
250
|
+
* For an emitted (precisely-typed) contract this narrows to the fields whose
|
|
251
|
+
* output type is assignable to `number`. For a loosely-typed `IdbContract`
|
|
252
|
+
* (no type maps — `DefaultModelRow` is `Record<string, unknown>`) the row key
|
|
253
|
+
* set widens to `string`, so we allow any field name rather than collapsing to
|
|
254
|
+
* `never`. Mirrors `NumericFieldNames` from `sql-orm-client`, trait-free.
|
|
255
|
+
*/
|
|
256
|
+
type NumericFieldNames<TContract, ModelName extends string> = string extends keyof DefaultModelRow<TContract, ModelName> ? string : { [K in keyof DefaultModelRow<TContract, ModelName> & string]: NonNullable<DefaultModelRow<TContract, ModelName>[K]> extends number ? K : never }[keyof DefaultModelRow<TContract, ModelName> & string];
|
|
257
|
+
declare const idbAggregateResultBrand: unique symbol;
|
|
258
|
+
/**
|
|
259
|
+
* A single aggregation selector produced by the {@link IdbAggregateBuilder}.
|
|
260
|
+
*
|
|
261
|
+
* The phantom `Result` brand carries the per-selector result type so
|
|
262
|
+
* {@link IdbAggregateResult} can map each alias back to its value type.
|
|
263
|
+
* Mirrors the vendor `AggregateSelector`.
|
|
264
|
+
*/
|
|
265
|
+
interface IdbAggregateSelector<Result> {
|
|
266
|
+
readonly kind: "aggregate";
|
|
267
|
+
readonly fn: AggregateFn;
|
|
268
|
+
readonly field?: string;
|
|
269
|
+
readonly [idbAggregateResultBrand]?: Result;
|
|
270
|
+
}
|
|
271
|
+
/** A map of result aliases → aggregation selectors (the `aggregate()` spec). */
|
|
272
|
+
type IdbAggregateSpec = Record<string, IdbAggregateSelector<unknown>>;
|
|
273
|
+
/** The result row shape for an {@link IdbAggregateSpec}: alias → value type. */
|
|
274
|
+
type IdbAggregateResult<Spec extends IdbAggregateSpec> = { [K in keyof Spec]: Spec[K] extends IdbAggregateSelector<infer Result> ? Result : never };
|
|
275
|
+
/**
|
|
276
|
+
* The builder handed to an `.aggregate(agg => …)` callback. `count()` is always
|
|
277
|
+
* available; the field reducers are constrained to {@link NumericFieldNames}.
|
|
278
|
+
* Mirrors the vendor `AggregateBuilder`, minus the SQL column mapping.
|
|
279
|
+
*/
|
|
280
|
+
interface IdbAggregateBuilder<TContract, ModelName extends string> {
|
|
281
|
+
count(): IdbAggregateSelector<number>;
|
|
282
|
+
sum<F extends NumericFieldNames<TContract, ModelName>>(field: F): IdbAggregateSelector<number | null>;
|
|
283
|
+
avg<F extends NumericFieldNames<TContract, ModelName>>(field: F): IdbAggregateSelector<number | null>;
|
|
284
|
+
min<F extends NumericFieldNames<TContract, ModelName>>(field: F): IdbAggregateSelector<number | null>;
|
|
285
|
+
max<F extends NumericFieldNames<TContract, ModelName>>(field: F): IdbAggregateSelector<number | null>;
|
|
286
|
+
}
|
|
287
|
+
//#endregion
|
|
288
|
+
//#region src/core/executor.d.ts
|
|
289
|
+
/**
|
|
290
|
+
* Thin executor interface for the IDB ORM client.
|
|
291
|
+
*
|
|
292
|
+
* Any object with a compatible `execute()` method satisfies this interface,
|
|
293
|
+
* including `IdbRuntime` from `@prisma-next-idb/runtime-idb`. The separation
|
|
294
|
+
* avoids a direct dependency on `runtime-idb`, keeping `client-idb` composable
|
|
295
|
+
* and independently testable.
|
|
296
|
+
*
|
|
297
|
+
* @example
|
|
298
|
+
* ```ts
|
|
299
|
+
* import { createIdbRuntime } from "@prisma-next-idb/runtime-idb/runtime";
|
|
300
|
+
* const runtime = createIdbRuntime({ adapter, driver });
|
|
301
|
+
* const client = idbOrm({ contract, executor: runtime }); // runtime satisfies IdbQueryExecutor
|
|
302
|
+
* ```
|
|
303
|
+
*/
|
|
304
|
+
interface IdbQueryExecutor {
|
|
305
|
+
execute<Row>(plan: IdbQueryPlan<Row>): AsyncIterableResult<Row>;
|
|
306
|
+
}
|
|
307
|
+
//#endregion
|
|
308
|
+
//#region src/core/model-accessor.d.ts
|
|
309
|
+
/**
|
|
310
|
+
* Operator surface available on every field. All return frozen AST nodes
|
|
311
|
+
* — they are values, not statements, so chaining and logical combinators
|
|
312
|
+
* compose naturally.
|
|
313
|
+
*/
|
|
314
|
+
interface IdbFieldAccessor<T> {
|
|
315
|
+
eq(value: T): IdbFieldFilter;
|
|
316
|
+
neq(value: T): IdbFieldFilter;
|
|
317
|
+
gt(value: T): IdbFieldFilter;
|
|
318
|
+
lt(value: T): IdbFieldFilter;
|
|
319
|
+
gte(value: T): IdbFieldFilter;
|
|
320
|
+
lte(value: T): IdbFieldFilter;
|
|
321
|
+
in(values: ReadonlyArray<T>): IdbFieldFilter;
|
|
322
|
+
notIn(values: ReadonlyArray<T>): IdbFieldFilter;
|
|
323
|
+
contains(sub: string): IdbFieldFilter;
|
|
324
|
+
startsWith(sub: string): IdbFieldFilter;
|
|
325
|
+
endsWith(sub: string): IdbFieldFilter;
|
|
326
|
+
isNull(): IdbNullCheckExpr;
|
|
327
|
+
isNotNull(): IdbNullCheckExpr;
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* The accessor object handed to a `where(fn)` callback. Each key resolves
|
|
331
|
+
* to an {@link IdbFieldAccessor} typed against the field's output type.
|
|
332
|
+
*
|
|
333
|
+
* The Proxy makes every string key resolve to an accessor at runtime; the
|
|
334
|
+
* type narrows the visible surface to the model's declared fields so the
|
|
335
|
+
* developer gets autocomplete.
|
|
336
|
+
*/
|
|
337
|
+
type IdbModelAccessor<TContract, ModelName extends string> = { readonly [K in keyof DefaultModelRow<TContract, ModelName>]-?: IdbFieldAccessor<DefaultModelRow<TContract, ModelName>[K]> };
|
|
338
|
+
//#endregion
|
|
339
|
+
//#region src/core/store-state.d.ts
|
|
340
|
+
/**
|
|
341
|
+
* One entry in {@link IdbAccessorState.includes} — describes how a single
|
|
342
|
+
* `.include()`d relation should be materialised.
|
|
343
|
+
*
|
|
344
|
+
* - `collection`: load the related rows, applying the refined `state`
|
|
345
|
+
* (its `filters` / `orderBy` / `skip` / `take`) to the child scan.
|
|
346
|
+
* `state` is `emptyAccessorState()` for an unrefined `.include(rel)`.
|
|
347
|
+
* - `scalar`: reduce the related rows to a single number (Phase 6.5 supports
|
|
348
|
+
* `count`). The `state.filters` still constrain which children are counted.
|
|
349
|
+
*
|
|
350
|
+
* Mirrors the `IncludeExpr` / `IncludeScalar` split from
|
|
351
|
+
* `vendor/prisma-next/.../sql-orm-client`, collapsed to the two shapes IDB
|
|
352
|
+
* needs (no SQL column mapping, no `combine()` branches).
|
|
353
|
+
*/
|
|
354
|
+
type IncludeEntry = {
|
|
355
|
+
readonly kind: "collection";
|
|
356
|
+
readonly state: IdbAccessorState;
|
|
357
|
+
} | {
|
|
358
|
+
readonly kind: "scalar";
|
|
359
|
+
readonly fn: "count";
|
|
360
|
+
readonly state: IdbAccessorState;
|
|
361
|
+
};
|
|
362
|
+
/**
|
|
363
|
+
* Marker returned by the child accessor's `.count()` terminal when it is
|
|
364
|
+
* called inside an `include()` refinement callback. Carries the refined
|
|
365
|
+
* child `state` so the relation loader counts only the matching children.
|
|
366
|
+
*
|
|
367
|
+
* Detected by {@link isIncludeScalar}; mirrors `IncludeScalar` from the
|
|
368
|
+
* vendor `sql-orm-client/include-descriptors.ts`.
|
|
369
|
+
*/
|
|
370
|
+
interface IdbIncludeScalar {
|
|
371
|
+
readonly kind: "includeScalar";
|
|
372
|
+
readonly fn: "count";
|
|
373
|
+
readonly state: IdbAccessorState;
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Immutable state carried by each {@link IdbStoreAccessorImpl} node in the
|
|
377
|
+
* builder chain.
|
|
378
|
+
*
|
|
379
|
+
* Every mutating method (`.where()`, `.take()`, etc.) returns a cloned
|
|
380
|
+
* instance with an updated state rather than mutating in place, making the
|
|
381
|
+
* accessor safe to reuse across multiple query branches.
|
|
382
|
+
*/
|
|
383
|
+
interface IdbAccessorState {
|
|
384
|
+
/**
|
|
385
|
+
* Accumulated filter expressions. Multiple `.where()` calls compose
|
|
386
|
+
* with AND semantics — the planner wraps them in an `andExpr` when
|
|
387
|
+
* lowering. Each entry is already either an `IdbFieldFilter`, a
|
|
388
|
+
* combinator, or a `null-check` node — never raw shorthand records.
|
|
389
|
+
*/
|
|
390
|
+
readonly filters: ReadonlyArray<IdbFilterExpr>;
|
|
391
|
+
/** Sort spec: field → direction. Applied as an in-memory comparator. */
|
|
392
|
+
readonly orderBy?: Record<string, "asc" | "desc">;
|
|
393
|
+
/** OFFSET (number of rows to skip). */
|
|
394
|
+
readonly skip?: number;
|
|
395
|
+
/** LIMIT (maximum number of rows to return). */
|
|
396
|
+
readonly take?: number;
|
|
397
|
+
/**
|
|
398
|
+
* Relations that have been `.include()`d — keys are relation names, values
|
|
399
|
+
* describe how to materialise each (collection vs scalar count, plus any
|
|
400
|
+
* refinement state). See {@link IncludeEntry}.
|
|
401
|
+
*/
|
|
402
|
+
readonly includes: Record<string, IncludeEntry>;
|
|
403
|
+
/**
|
|
404
|
+
* Fields kept by `.select()`. `undefined` means "all fields". When set,
|
|
405
|
+
* the materialised rows are projected down to these fields (plus any
|
|
406
|
+
* included relation keys) after the scan and relation loads complete.
|
|
407
|
+
*/
|
|
408
|
+
readonly selectedFields?: ReadonlyArray<string>;
|
|
409
|
+
}
|
|
410
|
+
//#endregion
|
|
411
|
+
//#region src/core/grouped-accessor.d.ts
|
|
412
|
+
/**
|
|
413
|
+
* One grouped-aggregate result row: the group-key fields picked from the model
|
|
414
|
+
* row, intersected with the aggregate aliases.
|
|
415
|
+
*/
|
|
416
|
+
type GroupedResultRow<TContract, ModelName extends string, Fields extends readonly string[], Spec extends IdbAggregateSpec> = Pick<DefaultModelRow<TContract, ModelName>, Fields[number] & keyof DefaultModelRow<TContract, ModelName>> & IdbAggregateResult<Spec>;
|
|
417
|
+
/**
|
|
418
|
+
* Grouped-aggregate builder returned by `accessor.groupBy(...)`.
|
|
419
|
+
*
|
|
420
|
+
* Port of the vendor `GroupedCollection` (`sql-orm-client/grouped-collection.ts`),
|
|
421
|
+
* but purely in-memory: there is no SQL compilation, so `aggregate()`
|
|
422
|
+
* materialises the matching rows, partitions them by the group-key fields, and
|
|
423
|
+
* reduces each partition with the requested selectors.
|
|
424
|
+
*/
|
|
425
|
+
interface IdbGroupedAccessor<TContract, ModelName extends string, Fields extends readonly string[]> {
|
|
426
|
+
/**
|
|
427
|
+
* Reduce each group to one row of `{ ...groupKeyFields, ...aggregates }`.
|
|
428
|
+
*
|
|
429
|
+
* @example
|
|
430
|
+
* ```ts
|
|
431
|
+
* const byUser = await db.posts
|
|
432
|
+
* .where({ published: true })
|
|
433
|
+
* .groupBy("authorId")
|
|
434
|
+
* .aggregate((agg) => ({ count: agg.count(), totalViews: agg.sum("views") }));
|
|
435
|
+
* // [{ authorId: "u1", count: 3, totalViews: 120 }, ...]
|
|
436
|
+
* ```
|
|
437
|
+
*/
|
|
438
|
+
aggregate<Spec extends IdbAggregateSpec>(fn: (agg: IdbAggregateBuilder<TContract, ModelName>) => Spec): Promise<Array<GroupedResultRow<TContract, ModelName, Fields, Spec>>>;
|
|
439
|
+
}
|
|
440
|
+
//#endregion
|
|
441
|
+
//#region src/core/store-accessor.d.ts
|
|
442
|
+
/** Callback form of `.where(fn)` — receives the typed model accessor proxy. */
|
|
443
|
+
type WhereCallback<TContract, ModelName extends string> = (m: IdbModelAccessor<TContract, ModelName>) => IdbFilterExpr;
|
|
444
|
+
/** Tuple of one-or-more field names of a model (for `select()` / `groupBy()`). */
|
|
445
|
+
type FieldTuple<TContract, ModelName extends string> = readonly [keyof DefaultModelRow<TContract, ModelName> & string, ...(keyof DefaultModelRow<TContract, ModelName> & string)[]];
|
|
446
|
+
/**
|
|
447
|
+
* The child accessor handed to an `include()` refinement callback.
|
|
448
|
+
*
|
|
449
|
+
* Exposes the chainable narrowing methods (`where` / `orderBy` / `take` /
|
|
450
|
+
* `skip`) plus the scalar `count()` reducer. Mirrors the vendor
|
|
451
|
+
* `IncludeRefinementCollection`: chainable methods return the same refinement
|
|
452
|
+
* surface so `count()` stays reachable after a `where()`, and `count()` returns
|
|
453
|
+
* an {@link IdbIncludeScalar} marker rather than the async terminal `count()`
|
|
454
|
+
* found on the top-level accessor.
|
|
455
|
+
*/
|
|
456
|
+
interface IdbIncludeRefinementAccessor<TContract, ModelName extends string> {
|
|
457
|
+
where(filter: WhereFilter<TContract, ModelName> | WhereCallback<TContract, ModelName>): IdbIncludeRefinementAccessor<TContract, ModelName>;
|
|
458
|
+
orderBy(spec: OrderBySpec<TContract, ModelName>): IdbIncludeRefinementAccessor<TContract, ModelName>;
|
|
459
|
+
take(n: number): IdbIncludeRefinementAccessor<TContract, ModelName>;
|
|
460
|
+
skip(n: number): IdbIncludeRefinementAccessor<TContract, ModelName>;
|
|
461
|
+
count(): IdbIncludeScalar;
|
|
462
|
+
}
|
|
463
|
+
/** Refinement callback type for a given relation key `K`. */
|
|
464
|
+
type IncludeRefineFn<TContract, ModelName extends string, K extends string, R> = (collection: IdbIncludeRefinementAccessor<TContract, RelatedModelOf<TContract, ModelName, K>>) => R;
|
|
465
|
+
/**
|
|
466
|
+
* Immutable query-builder for a single IDB object store.
|
|
467
|
+
*
|
|
468
|
+
* Each method that narrows the query (`.where()`, `.take()`, etc.) returns a
|
|
469
|
+
* new, independent accessor instance — the original is never mutated. This
|
|
470
|
+
* mirrors the `MongoCollection` pattern from `@prisma-next/2-mongo-family`.
|
|
471
|
+
*
|
|
472
|
+
* @template TContract - The full IDB contract (with or without type maps).
|
|
473
|
+
* @template ModelName - The model (store) this accessor targets.
|
|
474
|
+
* @template TIncludes - Tracks which relations have been included via
|
|
475
|
+
* `.include()` calls, so the return type widens progressively.
|
|
476
|
+
* @template TSelected - Field names kept by `.select()`. `never` (the
|
|
477
|
+
* default) means "all fields"; otherwise the row narrows to these fields
|
|
478
|
+
* (plus any included relations).
|
|
479
|
+
*/
|
|
480
|
+
interface IdbStoreAccessor<TContract, ModelName extends string, TIncludes extends IncludeSpec<TContract, ModelName> = NoIncludes, TSelected extends string = never> {
|
|
481
|
+
/**
|
|
482
|
+
* Add a filter (ANDed with any previous `.where()` calls).
|
|
483
|
+
*
|
|
484
|
+
* Two forms:
|
|
485
|
+
*
|
|
486
|
+
* - **Shorthand**: `where({ field: value })` — multi-key shorthand
|
|
487
|
+
* objects compose as AND. `null` values become null-checks rather
|
|
488
|
+
* than literal-null equalities so absent fields match.
|
|
489
|
+
* - **Callback**: `where((m) => m.field.op(value))` — receives the
|
|
490
|
+
* typed model accessor proxy and returns an `IdbFilterExpr` built
|
|
491
|
+
* via the operator surface. Combinators (`and`, `or`, `not` from
|
|
492
|
+
* `@prisma-next-idb/client-idb/orm`) compose nodes.
|
|
493
|
+
*/
|
|
494
|
+
where(filter: WhereFilter<TContract, ModelName> | WhereCallback<TContract, ModelName>): IdbStoreAccessor<TContract, ModelName, TIncludes, TSelected>;
|
|
495
|
+
/** Set the sort order. Replaces any previous `.orderBy()` call. */
|
|
496
|
+
orderBy(spec: OrderBySpec<TContract, ModelName>): IdbStoreAccessor<TContract, ModelName, TIncludes, TSelected>;
|
|
497
|
+
/** Limit the number of rows returned. */
|
|
498
|
+
take(n: number): IdbStoreAccessor<TContract, ModelName, TIncludes, TSelected>;
|
|
499
|
+
/** Skip the first `n` rows (OFFSET). */
|
|
500
|
+
skip(n: number): IdbStoreAccessor<TContract, ModelName, TIncludes, TSelected>;
|
|
501
|
+
/**
|
|
502
|
+
* Include a reference relation in the returned rows.
|
|
503
|
+
*
|
|
504
|
+
* The relation is loaded via a single batch cursor scan after the main
|
|
505
|
+
* query — O(1) round trips to IDB per included relation regardless of
|
|
506
|
+
* the number of parent rows. The return type gains the relation field
|
|
507
|
+
* automatically.
|
|
508
|
+
*
|
|
509
|
+
* An optional refinement callback narrows the loaded relation:
|
|
510
|
+
*
|
|
511
|
+
* - return the (chained) collection to apply `where` / `orderBy` /
|
|
512
|
+
* `take` / `skip` to the related rows (per-parent for `1:N`);
|
|
513
|
+
* - return `collection.count()` to reduce a to-many relation to the
|
|
514
|
+
* number of matching children (the field becomes a `number`).
|
|
515
|
+
*
|
|
516
|
+
* @example
|
|
517
|
+
* ```ts
|
|
518
|
+
* db.users.include("posts", (posts) => posts.where({ published: true }).take(5))
|
|
519
|
+
* db.users.include("posts", (posts) => posts.count())
|
|
520
|
+
* ```
|
|
521
|
+
*/
|
|
522
|
+
include<K extends ReferenceRelKeys<TContract, ModelName>>(relation: K, refineFn?: IncludeRefineFn<TContract, ModelName, K, IdbIncludeRefinementAccessor<TContract, RelatedModelOf<TContract, ModelName, K>>>): IdbStoreAccessor<TContract, ModelName, TIncludes & Record<K, true>, TSelected>;
|
|
523
|
+
include<K extends ReferenceRelKeys<TContract, ModelName>>(relation: K, refineFn: IncludeRefineFn<TContract, ModelName, K, IdbIncludeScalar>): IdbStoreAccessor<TContract, ModelName, TIncludes & Record<K, "scalar">, TSelected>;
|
|
524
|
+
/**
|
|
525
|
+
* Project the row down to a subset of scalar fields. Any previously
|
|
526
|
+
* `.include()`d relations are preserved on the result; only the scalar
|
|
527
|
+
* fields are narrowed.
|
|
528
|
+
*
|
|
529
|
+
* @example
|
|
530
|
+
* ```ts
|
|
531
|
+
* const summaries = await db.users.select("id", "email").all();
|
|
532
|
+
* // typeof summaries[number] === { id: string; email: string }
|
|
533
|
+
* ```
|
|
534
|
+
*/
|
|
535
|
+
select<Fields extends FieldTuple<TContract, ModelName>>(...fields: Fields): IdbStoreAccessor<TContract, ModelName, TIncludes, Fields[number]>;
|
|
536
|
+
/** Return all matching rows as an async iterable (also awaitable as `Row[]`). */
|
|
537
|
+
all(): AsyncIterableResult<SelectedRow<TContract, ModelName, TIncludes, TSelected>>;
|
|
538
|
+
/** Return the first matching row, or `null` if none match. */
|
|
539
|
+
first(): Promise<SelectedRow<TContract, ModelName, TIncludes, TSelected> | null>;
|
|
540
|
+
/**
|
|
541
|
+
* Run an in-memory aggregate (count/sum/avg/min/max) over the rows matching
|
|
542
|
+
* the accumulated `.where()` filter. Returns one result object keyed by the
|
|
543
|
+
* aliases supplied in the spec.
|
|
544
|
+
*
|
|
545
|
+
* @example
|
|
546
|
+
* ```ts
|
|
547
|
+
* const stats = await db.posts.where({ published: true }).aggregate((agg) => ({
|
|
548
|
+
* total: agg.count(),
|
|
549
|
+
* avgViews: agg.avg("views"),
|
|
550
|
+
* }));
|
|
551
|
+
* ```
|
|
552
|
+
*/
|
|
553
|
+
aggregate<Spec extends IdbAggregateSpec>(fn: (agg: IdbAggregateBuilder<TContract, ModelName>) => Spec): Promise<IdbAggregateResult<Spec>>;
|
|
554
|
+
/**
|
|
555
|
+
* Switch to grouped-aggregate mode. The returned {@link IdbGroupedAccessor}'s
|
|
556
|
+
* `.aggregate(...)` terminal produces one row per group with the chosen key
|
|
557
|
+
* fields plus the requested aggregates.
|
|
558
|
+
*
|
|
559
|
+
* @example
|
|
560
|
+
* ```ts
|
|
561
|
+
* const byUser = await db.posts
|
|
562
|
+
* .where({ published: true })
|
|
563
|
+
* .groupBy("authorId")
|
|
564
|
+
* .aggregate((agg) => ({ count: agg.count(), totalViews: agg.sum("views") }));
|
|
565
|
+
* ```
|
|
566
|
+
*/
|
|
567
|
+
groupBy<Fields extends FieldTuple<TContract, ModelName>>(...fields: Fields): IdbGroupedAccessor<TContract, ModelName, Fields>;
|
|
568
|
+
/**
|
|
569
|
+
* Insert a record into the store and return the stored row.
|
|
570
|
+
*
|
|
571
|
+
* The primary key field is optional in `data` — pass it to use a
|
|
572
|
+
* client-generated ID (`cuid`, `uuid`) or omit it for auto-increment stores.
|
|
573
|
+
*
|
|
574
|
+
* Relation fields accept a mutation callback:
|
|
575
|
+
* `posts: (rel) => rel.create([...])` or `author: (rel) => rel.connect({ id })`.
|
|
576
|
+
* When any relation callback is present, all writes are wrapped in a single
|
|
577
|
+
* multi-store IDB transaction (requires IdbRuntime, not a plain executor).
|
|
578
|
+
*/
|
|
579
|
+
create(data: MutationCreateInput<TContract, ModelName>): Promise<DefaultModelRow<TContract, ModelName>>;
|
|
580
|
+
/** Look up a single row by primary key. Returns `null` if not found. */
|
|
581
|
+
findUnique(key: KeyType<TContract, ModelName>): Promise<DefaultModelRow<TContract, ModelName> | null>;
|
|
582
|
+
/** Delete the row with the given primary key. */
|
|
583
|
+
delete(key: KeyType<TContract, ModelName>): Promise<void>;
|
|
584
|
+
/**
|
|
585
|
+
* Update the first row matching the accumulated `.where()` filter.
|
|
586
|
+
* Returns the merged row, or `null` if no row matches.
|
|
587
|
+
*
|
|
588
|
+
* Relation fields accept `connect` or `disconnect` callbacks. When any
|
|
589
|
+
* relation callback is present, all writes run in a single multi-store
|
|
590
|
+
* IDB transaction (requires IdbRuntime).
|
|
591
|
+
*/
|
|
592
|
+
update(patch: MutationUpdateInput<TContract, ModelName>): Promise<DefaultModelRow<TContract, ModelName> | null>;
|
|
593
|
+
/**
|
|
594
|
+
* Update all rows matching the accumulated `.where()` filter and return
|
|
595
|
+
* them as an `AsyncIterableResult` (also awaitable as `Row[]`).
|
|
596
|
+
*/
|
|
597
|
+
updateAll(patch: PatchInput<TContract, ModelName>): AsyncIterableResult<DefaultModelRow<TContract, ModelName>>;
|
|
598
|
+
/**
|
|
599
|
+
* Update all rows matching the accumulated `.where()` filter.
|
|
600
|
+
* Returns the count of updated rows.
|
|
601
|
+
*/
|
|
602
|
+
updateCount(patch: PatchInput<TContract, ModelName>): Promise<number>;
|
|
603
|
+
/**
|
|
604
|
+
* Insert or update a single record.
|
|
605
|
+
*
|
|
606
|
+
* - If a row matching `where` exists: shallow-merge `update` onto it and
|
|
607
|
+
* return the merged row.
|
|
608
|
+
* - If no matching row exists: insert `create` and return it.
|
|
609
|
+
*/
|
|
610
|
+
upsert(args: {
|
|
611
|
+
create: CreateInput<TContract, ModelName>;
|
|
612
|
+
update: PatchInput<TContract, ModelName>;
|
|
613
|
+
where: WhereFilter<TContract, ModelName>;
|
|
614
|
+
}): Promise<DefaultModelRow<TContract, ModelName>>;
|
|
615
|
+
/**
|
|
616
|
+
* Insert multiple records in a single atomic transaction.
|
|
617
|
+
* Returns all inserted rows as an `AsyncIterableResult`.
|
|
618
|
+
*/
|
|
619
|
+
createAll(data: CreateInput<TContract, ModelName>[]): AsyncIterableResult<DefaultModelRow<TContract, ModelName>>;
|
|
620
|
+
/**
|
|
621
|
+
* Insert multiple records in a single atomic transaction.
|
|
622
|
+
* Returns the count of inserted rows.
|
|
623
|
+
*/
|
|
624
|
+
createCount(data: CreateInput<TContract, ModelName>[]): Promise<number>;
|
|
625
|
+
/**
|
|
626
|
+
* Delete all rows matching the accumulated `.where()` filter.
|
|
627
|
+
* Returns the deleted rows as an `AsyncIterableResult`.
|
|
628
|
+
*/
|
|
629
|
+
deleteAll(): AsyncIterableResult<DefaultModelRow<TContract, ModelName>>;
|
|
630
|
+
/**
|
|
631
|
+
* Delete all rows matching the accumulated `.where()` filter.
|
|
632
|
+
* Returns the count of deleted rows.
|
|
633
|
+
*/
|
|
634
|
+
deleteCount(): Promise<number>;
|
|
635
|
+
/**
|
|
636
|
+
* Count all rows matching the accumulated `.where()` filter.
|
|
637
|
+
* With no filter, counts all rows in the store.
|
|
638
|
+
*
|
|
639
|
+
* **Note — `skip`/`take` are respected**: unlike Prisma's SQL `count()`,
|
|
640
|
+
* which ignores pagination, this implementation reuses the same cursor-scan
|
|
641
|
+
* plan as `all()`. That means `where(...).take(5).count()` returns at most 5,
|
|
642
|
+
* not the total number of matching rows. Use `where(...).count()` without
|
|
643
|
+
* `take`/`skip` when you need an unbounded total.
|
|
644
|
+
*/
|
|
645
|
+
count(): Promise<number>;
|
|
646
|
+
}
|
|
647
|
+
//#endregion
|
|
648
|
+
//#region src/core/idb-orm.d.ts
|
|
649
|
+
/**
|
|
650
|
+
* The ORM client object returned by {@link idbOrm}.
|
|
651
|
+
*
|
|
652
|
+
* Keys are the root keys from `contract.roots` (e.g. `"users"`, `"posts"`).
|
|
653
|
+
* Each value is an {@link IdbStoreAccessor} targeting the corresponding model.
|
|
654
|
+
*
|
|
655
|
+
* @example
|
|
656
|
+
* ```ts
|
|
657
|
+
* const db = idbOrm({ contract, executor: runtime });
|
|
658
|
+
* const users = await db.users.all(); // IdbStoreAccessor<..., "User">
|
|
659
|
+
* ```
|
|
660
|
+
*/
|
|
661
|
+
type IdbOrmClient<TContract extends Contract<IdbStorage>> = { readonly [K in string & keyof TContract["roots"]]: TContract["roots"][K] extends {
|
|
662
|
+
model: infer ModelName extends string;
|
|
663
|
+
} ? IdbStoreAccessor<TContract, ModelName> : never };
|
|
664
|
+
/**
|
|
665
|
+
* Options for {@link idbOrm}.
|
|
666
|
+
*/
|
|
667
|
+
interface IdbOrmOptions<TContract extends IdbContract> {
|
|
668
|
+
/** The resolved IDB contract (with or without attached type maps). */
|
|
669
|
+
readonly contract: TContract;
|
|
670
|
+
/**
|
|
671
|
+
* The query executor.
|
|
672
|
+
*
|
|
673
|
+
* Any object with a compatible `execute()` signature satisfies this —
|
|
674
|
+
* most commonly an `IdbRuntime` created via `createIdbRuntime()`.
|
|
675
|
+
*/
|
|
676
|
+
readonly executor: IdbQueryExecutor;
|
|
677
|
+
}
|
|
678
|
+
/**
|
|
679
|
+
* Create a typed IDB ORM client from a contract and executor.
|
|
680
|
+
*
|
|
681
|
+
* The client exposes one `IdbStoreAccessor` per entry in `contract.roots`.
|
|
682
|
+
* Only roots-declared stores are accessible at the top level — other stores
|
|
683
|
+
* can be reached via `.include()` on any accessor.
|
|
684
|
+
*
|
|
685
|
+
* @example
|
|
686
|
+
* ```ts
|
|
687
|
+
* import { idbOrm } from "@prisma-next-idb/client-idb/orm";
|
|
688
|
+
* import { createIdbRuntime } from "@prisma-next-idb/runtime-idb/runtime";
|
|
689
|
+
* import contract from "./prisma/idb-contract";
|
|
690
|
+
*
|
|
691
|
+
* const runtime = createIdbRuntime({ adapter, driver });
|
|
692
|
+
* const db = idbOrm({ contract, executor: runtime });
|
|
693
|
+
*
|
|
694
|
+
* // Typed query builder:
|
|
695
|
+
* const alice = await db.users.where({ email: "alice@example.com" }).first();
|
|
696
|
+
* const postsWithAuthor = await db.posts.include("author").all();
|
|
697
|
+
* ```
|
|
698
|
+
*/
|
|
699
|
+
declare function idbOrm<TContract extends IdbContract>(options: IdbOrmOptions<TContract>): IdbOrmClient<TContract>;
|
|
700
|
+
//#endregion
|
|
701
|
+
export { MutationUpdateInput as A, SelectedRow as B, IncludeFields as C, KeyType as D, IncludedRow as E, ReferenceRelKeys as F, WhereFilter as H, RelatedModelOf as I, RelationMutationConnect as L, NumericFieldNames as M, OrderBySpec as N, ModelKeyPath as O, PatchInput as P, RelationMutationCreate as R, IdbRelationMutator as S, IncludeSpec as T, SortDirection as V, IdbAggregateResult as _, IdbStoreAccessor as a, IdbContract as b, IdbGroupedAccessor as c, IdbModelAccessor as d, IdbQueryExecutor as f, IdbAggregateBuilder as g, DefaultModelRow as h, IdbIncludeRefinementAccessor as i, NoIncludes as j, MutationCreateInput as k, IdbIncludeScalar as l, CreateInput as m, IdbOrmOptions as n, WhereCallback as o, AggregateFn as p, idbOrm as r, GroupedResultRow as s, IdbOrmClient as t, IdbFieldAccessor as u, IdbAggregateSelector as v, IncludeMarker as w, IdbRelationMutation as x, IdbAggregateSpec as y, RelationMutationDisconnect as z };
|
|
702
|
+
//# sourceMappingURL=idb-orm-B5H6xka-.d.mts.map
|