@lunora/server 1.0.0-alpha.26 → 1.0.0-alpha.28
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/dist/data-model.d.mts +148 -148
- package/dist/data-model.d.ts +148 -148
- package/dist/index.d.mts +901 -901
- package/dist/index.d.ts +901 -901
- package/dist/index.mjs +1 -1
- package/dist/packem_shared/{mask-DSBtA3qp.mjs → mask-CZuu9WAF.mjs} +17 -0
- package/dist/packem_shared/types.d-C4CMJK8x.d.mts +141 -0
- package/dist/packem_shared/types.d-DdYF8E18.d.ts +141 -0
- package/dist/rls/testing.d.mts +31 -31
- package/dist/rls/testing.d.ts +31 -31
- package/dist/types.d.mts +624 -586
- package/dist/types.d.ts +624 -586
- package/package.json +5 -5
- package/dist/packem_shared/types.d-BB3pjV0m.d.mts +0 -141
- package/dist/packem_shared/types.d-Cxl6ndhm.d.ts +0 -141
package/dist/types.d.ts
CHANGED
|
@@ -6,12 +6,12 @@ type InferArgs<A extends ArgsValidator> = InferValidatorMap<A>;
|
|
|
6
6
|
/** Storage backend for a `.global()` table: D1 (default) or a Postgres/MySQL database via Cloudflare Hyperdrive (PlanetScale, Neon, …). */
|
|
7
7
|
type GlobalBackend = "d1" | "hyperdrive";
|
|
8
8
|
/**
|
|
9
|
-
* Cloudflare Durable Object data-residency jurisdiction declared via
|
|
10
|
-
* `defineSchema(...).jurisdiction("…")`. Restricts where every DO the app
|
|
11
|
-
* reaches runs and persists data (GDPR, FedRAMP, US data residency). Widening
|
|
12
|
-
* union — Cloudflare adds values over time.
|
|
13
|
-
* @see https://developers.cloudflare.com/durable-objects/reference/data-location/
|
|
14
|
-
*/
|
|
9
|
+
* Cloudflare Durable Object data-residency jurisdiction declared via
|
|
10
|
+
* `defineSchema(...).jurisdiction("…")`. Restricts where every DO the app
|
|
11
|
+
* reaches runs and persists data (GDPR, FedRAMP, US data residency). Widening
|
|
12
|
+
* union — Cloudflare adds values over time.
|
|
13
|
+
* @see https://developers.cloudflare.com/durable-objects/reference/data-location/
|
|
14
|
+
*/
|
|
15
15
|
type DurableObjectJurisdiction = "eu" | "fedramp" | "us";
|
|
16
16
|
/** How a table is routed at runtime. */
|
|
17
17
|
type ShardMode = {
|
|
@@ -28,31 +28,31 @@ type ExternalSourceRefresh = "manual" | {
|
|
|
28
28
|
everyMs: number;
|
|
29
29
|
};
|
|
30
30
|
/**
|
|
31
|
-
* Delete-detection mode for external-source ingest (plan 077 / 136).
|
|
32
|
-
*
|
|
33
|
-
* `"full-pull"` (the default) reads the **whole** tenant membership each tick and
|
|
34
|
-
* diffs it, so it observes upstream deletes for free — but costs a full read per tick
|
|
35
|
-
* (the Phase-0 bench put the ceiling at ~10k rows).
|
|
36
|
-
*
|
|
37
|
-
* `"incremental"` pulls **only rows past a durable watermark** (`cursor`), cheap for
|
|
38
|
-
* large low-churn tables above the full-pull cap. Because an absent row then means
|
|
39
|
-
* "unchanged", not "deleted", incremental requires a delete-visibility path: either a
|
|
40
|
-
* `reconcileEveryMs` periodic full-pull sweep, or a `softDeleteColumn` whose
|
|
41
|
-
* tombstones the pull returns. `defineSchema` throws (and the
|
|
42
|
-
* `external_source_incremental_no_delete_path` advisor lint fails the build) when an
|
|
43
|
-
* incremental source declares neither.
|
|
44
|
-
*/
|
|
31
|
+
* Delete-detection mode for external-source ingest (plan 077 / 136).
|
|
32
|
+
*
|
|
33
|
+
* `"full-pull"` (the default) reads the **whole** tenant membership each tick and
|
|
34
|
+
* diffs it, so it observes upstream deletes for free — but costs a full read per tick
|
|
35
|
+
* (the Phase-0 bench put the ceiling at ~10k rows).
|
|
36
|
+
*
|
|
37
|
+
* `"incremental"` pulls **only rows past a durable watermark** (`cursor`), cheap for
|
|
38
|
+
* large low-churn tables above the full-pull cap. Because an absent row then means
|
|
39
|
+
* "unchanged", not "deleted", incremental requires a delete-visibility path: either a
|
|
40
|
+
* `reconcileEveryMs` periodic full-pull sweep, or a `softDeleteColumn` whose
|
|
41
|
+
* tombstones the pull returns. `defineSchema` throws (and the
|
|
42
|
+
* `external_source_incremental_no_delete_path` advisor lint fails the build) when an
|
|
43
|
+
* incremental source declares neither.
|
|
44
|
+
*/
|
|
45
45
|
type ExternalSourceMode = "full-pull" | "incremental";
|
|
46
46
|
/**
|
|
47
|
-
* Incremental-ingest cursor (plan 136): the monotonic watermark column plus the
|
|
48
|
-
* watermark-parameterized pull query. `column` names the field in the pulled rows
|
|
49
|
-
* whose max becomes the next watermark (e.g. `"updated_at"`). `query` is a second
|
|
50
|
-
* SQL that returns only rows changed since the watermark — the watermark binds as
|
|
51
|
-
* the parameter AFTER `tenantBy`'s params (e.g. Postgres
|
|
52
|
-
* `... WHERE tenant_id = $1 AND updated_at >= $2 ORDER BY updated_at`). Prefer `>=`
|
|
53
|
-
* with the idempotent upsert apply so rows sharing the boundary timestamp are never
|
|
54
|
-
* skipped (re-pulling them is a no-op).
|
|
55
|
-
*/
|
|
47
|
+
* Incremental-ingest cursor (plan 136): the monotonic watermark column plus the
|
|
48
|
+
* watermark-parameterized pull query. `column` names the field in the pulled rows
|
|
49
|
+
* whose max becomes the next watermark (e.g. `"updated_at"`). `query` is a second
|
|
50
|
+
* SQL that returns only rows changed since the watermark — the watermark binds as
|
|
51
|
+
* the parameter AFTER `tenantBy`'s params (e.g. Postgres
|
|
52
|
+
* `... WHERE tenant_id = $1 AND updated_at >= $2 ORDER BY updated_at`). Prefer `>=`
|
|
53
|
+
* with the idempotent upsert apply so rows sharing the boundary timestamp are never
|
|
54
|
+
* skipped (re-pulling them is a no-op).
|
|
55
|
+
*/
|
|
56
56
|
interface ExternalSourceCursor {
|
|
57
57
|
/** The monotonic watermark column in the pulled rows; its max advances the stored watermark. */
|
|
58
58
|
column: string;
|
|
@@ -60,14 +60,14 @@ interface ExternalSourceCursor {
|
|
|
60
60
|
query: string;
|
|
61
61
|
}
|
|
62
62
|
/**
|
|
63
|
-
* Config for `.source(...)` (plan 077): declares a table as **materialized from an
|
|
64
|
-
* external Postgres/MySQL behind Cloudflare Hyperdrive**, not written by user
|
|
65
|
-
* mutations. A system-driven poll loop reads the tenant slice and lands it in the
|
|
66
|
-
* DO's SQLite (via the validated CDC writer), after which `defineShape` carries it
|
|
67
|
-
* to clients unchanged. Orthogonal to `shardMode` — a sourced table almost always
|
|
68
|
-
* also `.shardBy()`s, in which case `tenantBy` is the mandatory tenant-isolation
|
|
69
|
-
* boundary (enforced by the `external_source_unscoped` advisor lint).
|
|
70
|
-
*/
|
|
63
|
+
* Config for `.source(...)` (plan 077): declares a table as **materialized from an
|
|
64
|
+
* external Postgres/MySQL behind Cloudflare Hyperdrive**, not written by user
|
|
65
|
+
* mutations. A system-driven poll loop reads the tenant slice and lands it in the
|
|
66
|
+
* DO's SQLite (via the validated CDC writer), after which `defineShape` carries it
|
|
67
|
+
* to clients unchanged. Orthogonal to `shardMode` — a sourced table almost always
|
|
68
|
+
* also `.shardBy()`s, in which case `tenantBy` is the mandatory tenant-isolation
|
|
69
|
+
* boundary (enforced by the `external_source_unscoped` advisor lint).
|
|
70
|
+
*/
|
|
71
71
|
interface ExternalSourceDefinition {
|
|
72
72
|
/** The wrangler Hyperdrive binding name the poll loop reads from. */
|
|
73
73
|
binding: string;
|
|
@@ -84,27 +84,27 @@ interface ExternalSourceDefinition {
|
|
|
84
84
|
/** The full tenant-membership query, with driver-native placeholders (`$1` / `?`). `tenantBy` binds its params. */
|
|
85
85
|
query: string;
|
|
86
86
|
/**
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
87
|
+
* **Incremental delete-visibility (plan 136)**: run a full-pull sweep at most
|
|
88
|
+
* this often (millis) to GC upstream deletes an incremental slice can't see.
|
|
89
|
+
* One of `reconcileEveryMs` / `softDeleteColumn` is required for incremental;
|
|
90
|
+
* rejected on a `"full-pull"` source.
|
|
91
|
+
*/
|
|
92
92
|
reconcileEveryMs?: number;
|
|
93
93
|
/** Poll cadence, or `"manual"`. Omit ⇒ the runtime's size-scaled default. */
|
|
94
94
|
refresh?: ExternalSourceRefresh;
|
|
95
95
|
/**
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
96
|
+
* **Incremental delete-visibility (plan 136)**: the upstream soft-delete
|
|
97
|
+
* tombstone column (e.g. `"deleted_at"`). When set, the incremental pull must
|
|
98
|
+
* return tombstoned rows and the ingest turns each into a local delete — an
|
|
99
|
+
* alternative to `reconcileEveryMs`. Rejected on a `"full-pull"` source.
|
|
100
|
+
*/
|
|
101
101
|
softDeleteColumn?: string;
|
|
102
102
|
/**
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
103
|
+
* **Mandatory under `.shardBy()`**: map this DO's shard key → the query's bound
|
|
104
|
+
* params, so a tenant DO can only ever pull its own rows. An unscoped sourced +
|
|
105
|
+
* sharded table replicates the whole multitenant table into every shard — the
|
|
106
|
+
* `external_source_unscoped` advisor lint fails the build when this is absent.
|
|
107
|
+
*/
|
|
108
108
|
tenantBy?: (shardKey: string) => ReadonlyArray<unknown>;
|
|
109
109
|
}
|
|
110
110
|
interface IndexDefinition {
|
|
@@ -120,18 +120,18 @@ interface SearchIndexDefinition {
|
|
|
120
120
|
/** Reducer applied by an aggregate index. */
|
|
121
121
|
type AggregateOp = "avg" | "count" | "max" | "min" | "sum";
|
|
122
122
|
/**
|
|
123
|
-
* Declared aggregate index — the schema-level seam that lets the runtime keep
|
|
124
|
-
* O(1) counters/sums in step with row writes (via the trigger runner) and
|
|
125
|
-
* route matching reads through them.
|
|
126
|
-
*
|
|
127
|
-
* - `on` — the table whose rows feed the aggregate.
|
|
128
|
-
* - `op` — the reducer. `count` is field-less; the others take `field`.
|
|
129
|
-
* - `field` — the column the reducer applies to (required for non-count ops).
|
|
130
|
-
* - `by` — group keys. When all `where` keys in a read participate in `by`, the
|
|
131
|
-
* reader can answer from the counter table without scanning rows.
|
|
132
|
-
* - `where` — optional static predicate baked into the counter (only the rows
|
|
133
|
-
* matching it ever land in the counter).
|
|
134
|
-
*/
|
|
123
|
+
* Declared aggregate index — the schema-level seam that lets the runtime keep
|
|
124
|
+
* O(1) counters/sums in step with row writes (via the trigger runner) and
|
|
125
|
+
* route matching reads through them.
|
|
126
|
+
*
|
|
127
|
+
* - `on` — the table whose rows feed the aggregate.
|
|
128
|
+
* - `op` — the reducer. `count` is field-less; the others take `field`.
|
|
129
|
+
* - `field` — the column the reducer applies to (required for non-count ops).
|
|
130
|
+
* - `by` — group keys. When all `where` keys in a read participate in `by`, the
|
|
131
|
+
* reader can answer from the counter table without scanning rows.
|
|
132
|
+
* - `where` — optional static predicate baked into the counter (only the rows
|
|
133
|
+
* matching it ever land in the counter).
|
|
134
|
+
*/
|
|
135
135
|
interface AggregateIndexDefinition {
|
|
136
136
|
by?: ReadonlyArray<string>;
|
|
137
137
|
field?: string;
|
|
@@ -141,32 +141,32 @@ interface AggregateIndexDefinition {
|
|
|
141
141
|
where?: Record<string, unknown>;
|
|
142
142
|
}
|
|
143
143
|
/**
|
|
144
|
-
* One ordering key on a `rankIndex.sortBy`: which column to sort by, and the
|
|
145
|
-
* direction. The runtime breaks ties on the row's `_id` ASC so the order is
|
|
146
|
-
* total and `rank()` always returns a deterministic 1-based position.
|
|
147
|
-
*/
|
|
144
|
+
* One ordering key on a `rankIndex.sortBy`: which column to sort by, and the
|
|
145
|
+
* direction. The runtime breaks ties on the row's `_id` ASC so the order is
|
|
146
|
+
* total and `rank()` always returns a deterministic 1-based position.
|
|
147
|
+
*/
|
|
148
148
|
interface RankSortKey {
|
|
149
149
|
direction: "asc" | "desc";
|
|
150
150
|
field: string;
|
|
151
151
|
}
|
|
152
152
|
/**
|
|
153
|
-
* Declared rank index — a sorted companion table per `(partition tuple, sortBy)`
|
|
154
|
-
* maintained by triggers, so:
|
|
155
|
-
*
|
|
156
|
-
* - `rank(row)` returns the row's 1-based position within its partition under
|
|
157
|
-
* the declared `sortBy` order, plus the partition's total row count, in
|
|
158
|
-
* O(log n) lookups against the SQLite btree on the companion table.
|
|
159
|
-
* - `rankPage({ where, take, from })` walks the same companion table to return
|
|
160
|
-
* rows in the declared order — a sorted-pagination accelerator.
|
|
161
|
-
*
|
|
162
|
-
* Fields mirror `AggregateIndexDefinition`:
|
|
163
|
-
*
|
|
164
|
-
* - `on` — the source table whose rows feed the rank.
|
|
165
|
-
* - `sortBy` — ordered keys driving the rank. Required.
|
|
166
|
-
* - `partitionBy` — columns that scope each rank context (e.g. `["channelId"]`
|
|
167
|
-
* to rank within a channel). Omitted ⇒ one global rank across the table.
|
|
168
|
-
* - `where` — static predicate baked into the index; only matching rows enter.
|
|
169
|
-
*/
|
|
153
|
+
* Declared rank index — a sorted companion table per `(partition tuple, sortBy)`
|
|
154
|
+
* maintained by triggers, so:
|
|
155
|
+
*
|
|
156
|
+
* - `rank(row)` returns the row's 1-based position within its partition under
|
|
157
|
+
* the declared `sortBy` order, plus the partition's total row count, in
|
|
158
|
+
* O(log n) lookups against the SQLite btree on the companion table.
|
|
159
|
+
* - `rankPage({ where, take, from })` walks the same companion table to return
|
|
160
|
+
* rows in the declared order — a sorted-pagination accelerator.
|
|
161
|
+
*
|
|
162
|
+
* Fields mirror `AggregateIndexDefinition`:
|
|
163
|
+
*
|
|
164
|
+
* - `on` — the source table whose rows feed the rank.
|
|
165
|
+
* - `sortBy` — ordered keys driving the rank. Required.
|
|
166
|
+
* - `partitionBy` — columns that scope each rank context (e.g. `["channelId"]`
|
|
167
|
+
* to rank within a channel). Omitted ⇒ one global rank across the table.
|
|
168
|
+
* - `where` — static predicate baked into the index; only matching rows enter.
|
|
169
|
+
*/
|
|
170
170
|
interface RankIndexDefinition {
|
|
171
171
|
name: string;
|
|
172
172
|
on: string;
|
|
@@ -177,16 +177,16 @@ interface RankIndexDefinition {
|
|
|
177
177
|
/** FK behavior when a referenced parent row is deleted (mirrors SQL `ON DELETE`). */
|
|
178
178
|
type OnDeleteAction = "cascade" | "restrict" | "set null";
|
|
179
179
|
/**
|
|
180
|
-
* A declared relation between two tables, recorded by `.relations((r) => …)`.
|
|
181
|
-
*
|
|
182
|
-
* - `one` (many-to-one): the FK column `field` lives on **this** table and
|
|
183
|
-
* points at `table`.`references` (default `_id`). Loads a single doc.
|
|
184
|
-
* - `many` (one-to-many): the FK column `field` lives on the **target** table
|
|
185
|
-
* and points back at this table's `references` (default `_id`). Loads an array.
|
|
186
|
-
*
|
|
187
|
-
* `onDelete` is meaningful only on `one`: it is the action applied to the
|
|
188
|
-
* holder rows when the referenced parent row is deleted.
|
|
189
|
-
*/
|
|
180
|
+
* A declared relation between two tables, recorded by `.relations((r) => …)`.
|
|
181
|
+
*
|
|
182
|
+
* - `one` (many-to-one): the FK column `field` lives on **this** table and
|
|
183
|
+
* points at `table`.`references` (default `_id`). Loads a single doc.
|
|
184
|
+
* - `many` (one-to-many): the FK column `field` lives on the **target** table
|
|
185
|
+
* and points back at this table's `references` (default `_id`). Loads an array.
|
|
186
|
+
*
|
|
187
|
+
* `onDelete` is meaningful only on `one`: it is the action applied to the
|
|
188
|
+
* holder rows when the referenced parent row is deleted.
|
|
189
|
+
*/
|
|
190
190
|
interface RelationDefinition {
|
|
191
191
|
field: string;
|
|
192
192
|
kind: "many" | "one";
|
|
@@ -197,15 +197,15 @@ interface RelationDefinition {
|
|
|
197
197
|
/** Distance metric used by a Vectorize index. */
|
|
198
198
|
type VectorMetric = "cosine" | "dot-product" | "euclidean";
|
|
199
199
|
/**
|
|
200
|
-
* Bring-your-own-embedder: a user-supplied fn turning a source string into a
|
|
201
|
-
* numeric vector. The runtime calls it at upsert/query time so the framework
|
|
202
|
-
* never couples to a single embedding provider.
|
|
203
|
-
*/
|
|
200
|
+
* Bring-your-own-embedder: a user-supplied fn turning a source string into a
|
|
201
|
+
* numeric vector. The runtime calls it at upsert/query time so the framework
|
|
202
|
+
* never couples to a single embedding provider.
|
|
203
|
+
*/
|
|
204
204
|
type VectorEmbedder = (input: string) => Promise<ReadonlyArray<number>> | ReadonlyArray<number>;
|
|
205
205
|
/**
|
|
206
|
-
* Vector index declared inline on a table via `.vectorize(field, opts)`
|
|
207
|
-
* (DSL Shape A). The source is always a single column on the owning table.
|
|
208
|
-
*/
|
|
206
|
+
* Vector index declared inline on a table via `.vectorize(field, opts)`
|
|
207
|
+
* (DSL Shape A). The source is always a single column on the owning table.
|
|
208
|
+
*/
|
|
209
209
|
interface TableVectorIndex {
|
|
210
210
|
dimensions: number;
|
|
211
211
|
embed: VectorEmbedder;
|
|
@@ -216,82 +216,82 @@ interface TableVectorIndex {
|
|
|
216
216
|
}
|
|
217
217
|
interface TableDefinition<Shape extends Record<string, Validator> = Record<string, Validator>> {
|
|
218
218
|
/**
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
219
|
+
* Aggregate indexes declared via `.aggregateIndex(name, opts)`. The runtime
|
|
220
|
+
* maintains a counter row per `by` group via the trigger seam, so reads
|
|
221
|
+
* whose `where` keys all participate in the index's `by` set are answered
|
|
222
|
+
* without scanning the underlying table.
|
|
223
|
+
*/
|
|
224
224
|
aggregateIndexes: ReadonlyArray<AggregateIndexDefinition>;
|
|
225
225
|
/**
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
226
|
+
* Set by `.source(...)` (named `externalSource`, not `source`, so the data
|
|
227
|
+
* field doesn't collide with the fluent `.source()` builder method — same
|
|
228
|
+
* convention as `shardBy()`/`shardMode`). When present, the table is
|
|
229
|
+
* materialized from an external Hyperdrive-backed database by a system poll
|
|
230
|
+
* loop rather than user mutations. Implies `isExternallyManaged`.
|
|
231
|
+
*/
|
|
232
232
|
externalSource?: ExternalSourceDefinition;
|
|
233
233
|
indexes: ReadonlyArray<IndexDefinition>;
|
|
234
234
|
/**
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
235
|
+
* `true` when `.externallyManaged()` was called — the table's rows are
|
|
236
|
+
* written outside Lunora's discoverable insert path (an adapter, a
|
|
237
|
+
* migration, or framework middleware), e.g. `@lunora/auth`'s better-auth
|
|
238
|
+
* tables or `@lunora/ratelimit`'s store. Advisor insert-path lints
|
|
239
|
+
* (`table_without_insert`) skip such tables instead of flagging the absent
|
|
240
|
+
* `ctx.db.insert(...)`.
|
|
241
|
+
*/
|
|
242
242
|
isExternallyManaged?: boolean;
|
|
243
243
|
/**
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
244
|
+
* `true` when `.public()` was called — the table opts OUT of secure-by-default
|
|
245
|
+
* RLS. Under a schema marked `.rls("required")`, every table is protected (the
|
|
246
|
+
* DO/D1 write path denies raw, non-RLS `ctx.db` access) UNLESS it is `isPublic`.
|
|
247
|
+
* Has no effect when the schema does not require RLS.
|
|
248
|
+
*/
|
|
249
249
|
isPublic?: boolean;
|
|
250
250
|
/**
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
251
|
+
* Rank indexes declared via `.rankIndex(name, opts)`. The runtime maintains
|
|
252
|
+
* a sorted companion table per declared rank with a btree on
|
|
253
|
+
* `(partition, sortBy)` so `rank(row)` returns the row's 1-based position
|
|
254
|
+
* within its partition in O(log n), and `rankPage()` walks the index for
|
|
255
|
+
* sorted pagination.
|
|
256
|
+
*/
|
|
257
257
|
rankIndexes: ReadonlyArray<RankIndexDefinition>;
|
|
258
258
|
/**
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
259
|
+
* Declared relations keyed by accessor name; empty unless `.relations()`
|
|
260
|
+
* was called. Named `relationMap` (not `relations`) so the fluent
|
|
261
|
+
* `.relations((r) => …)` builder method doesn't collide with this field.
|
|
262
|
+
*/
|
|
263
263
|
relationMap: Record<string, RelationDefinition>;
|
|
264
264
|
searchIndexes: ReadonlyArray<SearchIndexDefinition>;
|
|
265
265
|
shape: Shape;
|
|
266
266
|
shardMode: ShardMode;
|
|
267
267
|
/**
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
268
|
+
* Set by `.softDelete()` (named `softDeleteMode`, not `softDelete`, so the
|
|
269
|
+
* data field doesn't collide with the fluent `.softDelete()` builder method —
|
|
270
|
+
* same convention as `shardBy()`/`shardMode`). When present, the table carries
|
|
271
|
+
* a nullable timestamp column (`field`, default `deletedAt`):
|
|
272
|
+
* `ctx.db.<table>.delete()` flips it instead of physically removing the row,
|
|
273
|
+
* and **list reads** (`findMany`/`findFirst`/`query()`/`count`/`aggregate`/
|
|
274
|
+
* relation loads) hide rows whose `field` is set unless
|
|
275
|
+
* `includeDeleted: true` is passed. By-id `get`/`patch`/`replace` and
|
|
276
|
+
* `restore` are unaffected. Absent ⇒ deletes are physical, as before.
|
|
277
|
+
*/
|
|
278
278
|
softDeleteMode?: {
|
|
279
279
|
field: string;
|
|
280
280
|
};
|
|
281
281
|
/**
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
282
|
+
* Declared lifecycle triggers keyed by accessor name; empty unless
|
|
283
|
+
* `.triggers()` was called. Named `triggerMap` (not `triggers`) so the
|
|
284
|
+
* fluent `.triggers((t) => …)` builder method doesn't collide with this
|
|
285
|
+
* field — same reasoning as {@link TableDefinition.relationMap}.
|
|
286
|
+
*/
|
|
287
287
|
triggerMap: Record<string, TriggerDefinition>;
|
|
288
288
|
vectorIndexes: ReadonlyArray<TableVectorIndex>;
|
|
289
289
|
}
|
|
290
290
|
/**
|
|
291
|
-
* Standalone vector index declared via `defineVectorIndex(...)` (DSL Shape B).
|
|
292
|
-
* Unlike {@link TableVectorIndex}, the source is a `select` function so it can
|
|
293
|
-
* derive the embedded text from any computation (e.g. `title + body`).
|
|
294
|
-
*/
|
|
291
|
+
* Standalone vector index declared via `defineVectorIndex(...)` (DSL Shape B).
|
|
292
|
+
* Unlike {@link TableVectorIndex}, the source is a `select` function so it can
|
|
293
|
+
* derive the embedded text from any computation (e.g. `title + body`).
|
|
294
|
+
*/
|
|
295
295
|
interface VectorIndexDefinition {
|
|
296
296
|
readonly dimensions: number;
|
|
297
297
|
readonly embed: VectorEmbedder;
|
|
@@ -303,40 +303,40 @@ interface VectorIndexDefinition {
|
|
|
303
303
|
}
|
|
304
304
|
interface Schema<T extends Record<string, TableDefinition> = Record<string, TableDefinition>> {
|
|
305
305
|
/**
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
306
|
+
* Secure-by-default RLS mode declared via `.rls("required")`. When
|
|
307
|
+
* `"required"`, every table is protected: the DO/D1 write path denies raw
|
|
308
|
+
* (non-RLS-wrapped) `ctx.db` access at runtime, so a procedure that forgets
|
|
309
|
+
* `.use(rls(...))` fails closed instead of silently exposing the table. A
|
|
310
|
+
* table opts out with `.public()` (→ {@link TableDefinition.isPublic}).
|
|
311
|
+
* Absent ⇒ legacy opt-in behavior (RLS only where a policy is applied).
|
|
312
|
+
*/
|
|
313
313
|
readonly rlsMode?: "required";
|
|
314
314
|
readonly tables: T;
|
|
315
315
|
readonly vectorIndexes: Record<string, VectorIndexDefinition>;
|
|
316
316
|
}
|
|
317
317
|
type FunctionKind = "action" | "mutation" | "query" | "stream";
|
|
318
318
|
/**
|
|
319
|
-
* Call surface a function is exposed on. `public` functions are reachable from
|
|
320
|
-
* clients via the generated `api`; `internal` functions are reachable only
|
|
321
|
-
* server-to-server (`ctx.runQuery`/`runMutation`/`runAction`) and are rejected
|
|
322
|
-
* by the DO's external RPC path. Absence is treated as `public` for
|
|
323
|
-
* back-compat with functions registered before visibility existed.
|
|
324
|
-
*/
|
|
319
|
+
* Call surface a function is exposed on. `public` functions are reachable from
|
|
320
|
+
* clients via the generated `api`; `internal` functions are reachable only
|
|
321
|
+
* server-to-server (`ctx.runQuery`/`runMutation`/`runAction`) and are rejected
|
|
322
|
+
* by the DO's external RPC path. Absence is treated as `public` for
|
|
323
|
+
* back-compat with functions registered before visibility existed.
|
|
324
|
+
*/
|
|
325
325
|
type FunctionVisibility = "internal" | "public";
|
|
326
326
|
/**
|
|
327
|
-
* x402 payment tag attached by the `.x402({ price })` builder modifier. Marks a
|
|
328
|
-
* public procedure as paid: the origin worker answers an unpaid client RPC with
|
|
329
|
-
* HTTP 402, verifies + settles the payment, and only then dispatches to the
|
|
330
|
-
* shard. The runtime reads only `price` from here — the network, recipient, and
|
|
331
|
-
* facilitator live in the worker-level x402 charge config, so `@lunora/runtime`
|
|
332
|
-
* never has to import `@lunora/x402` (and its viem/solana deps).
|
|
333
|
-
*/
|
|
327
|
+
* x402 payment tag attached by the `.x402({ price })` builder modifier. Marks a
|
|
328
|
+
* public procedure as paid: the origin worker answers an unpaid client RPC with
|
|
329
|
+
* HTTP 402, verifies + settles the payment, and only then dispatches to the
|
|
330
|
+
* shard. The runtime reads only `price` from here — the network, recipient, and
|
|
331
|
+
* facilitator live in the worker-level x402 charge config, so `@lunora/runtime`
|
|
332
|
+
* never has to import `@lunora/x402` (and its viem/solana deps).
|
|
333
|
+
*/
|
|
334
334
|
interface X402ProcedureConfig {
|
|
335
335
|
/**
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
336
|
+
* USD-denominated price: a number of dollars (`0.01`) or a decimal string
|
|
337
|
+
* (`"0.01"`, or the `"$0.01"` shorthand). Resolved to the network
|
|
338
|
+
* stablecoin's base units (USDC has 6 decimals) at challenge time.
|
|
339
|
+
*/
|
|
340
340
|
readonly price: number | string;
|
|
341
341
|
}
|
|
342
342
|
interface RegisteredFunction<A extends ArgsValidator, R, Kind extends FunctionKind> {
|
|
@@ -344,18 +344,18 @@ interface RegisteredFunction<A extends ArgsValidator, R, Kind extends FunctionKi
|
|
|
344
344
|
readonly handler: (context: unknown, args: InferArgs<A>) => Promise<R> | R;
|
|
345
345
|
readonly kind: Kind;
|
|
346
346
|
/**
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
347
|
+
* Set on connection-lifecycle hooks (`onConnect` / `onDisconnect`).
|
|
348
|
+
* Marks the function for the generated `LUNORA_LIFECYCLE_HOOKS` manifest so the
|
|
349
|
+
* DO dispatches it on socket connect/disconnect rather than via a client RPC.
|
|
350
|
+
* Absent on ordinary registrations.
|
|
351
|
+
*/
|
|
352
352
|
readonly lifecycle?: LifecycleEventKind;
|
|
353
353
|
readonly visibility?: FunctionVisibility;
|
|
354
354
|
/**
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
355
|
+
* Set by the `.x402({ price })` builder modifier. Marks the procedure as paid
|
|
356
|
+
* so the origin worker gates it behind an x402 402-challenge before dispatch.
|
|
357
|
+
* Absent on unpaid functions.
|
|
358
|
+
*/
|
|
359
359
|
readonly x402?: X402ProcedureConfig;
|
|
360
360
|
}
|
|
361
361
|
type RegisteredQuery<A extends ArgsValidator, R> = RegisteredFunction<A, R, "query">;
|
|
@@ -364,11 +364,11 @@ type RegisteredAction<A extends ArgsValidator, R> = RegisteredFunction<A, R, "ac
|
|
|
364
364
|
/** Which side of the WebSocket lifecycle a hook fires on. */
|
|
365
365
|
type LifecycleEventKind = "connect" | "disconnect";
|
|
366
366
|
/**
|
|
367
|
-
* The event a connection-lifecycle hook receives as its second argument. It is
|
|
368
|
-
* the JSON-serializable payload the DO forwards on socket connect/disconnect;
|
|
369
|
-
* the verified caller identity is also reflected on `ctx.auth` (the hook runs
|
|
370
|
-
* under the connecting user via `resolveIdentity`).
|
|
371
|
-
*/
|
|
367
|
+
* The event a connection-lifecycle hook receives as its second argument. It is
|
|
368
|
+
* the JSON-serializable payload the DO forwards on socket connect/disconnect;
|
|
369
|
+
* the verified caller identity is also reflected on `ctx.auth` (the hook runs
|
|
370
|
+
* under the connecting user via `resolveIdentity`).
|
|
371
|
+
*/
|
|
372
372
|
interface LifecycleEvent {
|
|
373
373
|
/** Stable per-socket id, minted at upgrade and replayed verbatim on disconnect. */
|
|
374
374
|
readonly connectionId: string;
|
|
@@ -380,20 +380,20 @@ interface LifecycleEvent {
|
|
|
380
380
|
readonly userId: string | null;
|
|
381
381
|
}
|
|
382
382
|
/**
|
|
383
|
-
* A registered connection-lifecycle hook — an internal mutation tagged with the
|
|
384
|
-
* lifecycle side it fires on. Produced by `onConnect` / `onDisconnect`.
|
|
385
|
-
*/
|
|
383
|
+
* A registered connection-lifecycle hook — an internal mutation tagged with the
|
|
384
|
+
* lifecycle side it fires on. Produced by `onConnect` / `onDisconnect`.
|
|
385
|
+
*/
|
|
386
386
|
type RegisteredLifecycleHook = RegisteredFunction<Record<string, never>, void, "mutation"> & {
|
|
387
387
|
readonly lifecycle: LifecycleEventKind;
|
|
388
388
|
};
|
|
389
389
|
/**
|
|
390
|
-
* A streaming query registration. Unlike {@link RegisteredFunction} the handler
|
|
391
|
-
* returns an `AsyncIterable<R>` synchronously (it does NOT `Promise<R>`); the
|
|
392
|
-
* runtime drives it frame by frame and forwards each chunk to the caller. The
|
|
393
|
-
* third `signal` argument is wired to the caller's cancel signal so the handler
|
|
394
|
-
* can stop early — break out of the loop or check `signal.aborted` between
|
|
395
|
-
* yields.
|
|
396
|
-
*/
|
|
390
|
+
* A streaming query registration. Unlike {@link RegisteredFunction} the handler
|
|
391
|
+
* returns an `AsyncIterable<R>` synchronously (it does NOT `Promise<R>`); the
|
|
392
|
+
* runtime drives it frame by frame and forwards each chunk to the caller. The
|
|
393
|
+
* third `signal` argument is wired to the caller's cancel signal so the handler
|
|
394
|
+
* can stop early — break out of the loop or check `signal.aborted` between
|
|
395
|
+
* yields.
|
|
396
|
+
*/
|
|
397
397
|
interface RegisteredStream<A extends ArgsValidator, R> {
|
|
398
398
|
readonly args: A;
|
|
399
399
|
readonly handler: (context: unknown, args: InferArgs<A>, signal: AbortSignal) => AsyncIterable<R>;
|
|
@@ -403,10 +403,10 @@ interface RegisteredStream<A extends ArgsValidator, R> {
|
|
|
403
403
|
/** The system tables `ctx.db.system` can read. */
|
|
404
404
|
type SystemTableName = "_scheduled_functions" | "_storage";
|
|
405
405
|
/**
|
|
406
|
-
* A pending scheduled invocation as surfaced by the `_scheduled_functions`
|
|
407
|
-
* system table. Mirrors {@link ScheduledJob} (the `ctx.scheduler` view); the
|
|
408
|
-
* separate name keeps the system-table read surface self-describing.
|
|
409
|
-
*/
|
|
406
|
+
* A pending scheduled invocation as surfaced by the `_scheduled_functions`
|
|
407
|
+
* system table. Mirrors {@link ScheduledJob} (the `ctx.scheduler` view); the
|
|
408
|
+
* separate name keeps the system-table read surface self-describing.
|
|
409
|
+
*/
|
|
410
410
|
interface ScheduledFunctionDoc {
|
|
411
411
|
/** Function arguments the job will be dispatched with. */
|
|
412
412
|
args: Record<string, unknown>;
|
|
@@ -436,55 +436,55 @@ interface SystemQuery<T extends SystemTableName> {
|
|
|
436
436
|
collect: () => Promise<SystemDoc<T>[]>;
|
|
437
437
|
}
|
|
438
438
|
/**
|
|
439
|
-
* Read-only reader over Lunora's system tables (`_scheduled_functions`,
|
|
440
|
-
* `_storage`), exposed as `ctx.db.system`. Mirrors Convex's `ctx.db.system`.
|
|
441
|
-
*
|
|
442
|
-
* **Best-effort and eventually consistent.** Unlike `ctx.db.<table>` — which
|
|
443
|
-
* reads the shard's transactional SQLite snapshot — the data behind these tables
|
|
444
|
-
* lives OUTSIDE the shard (scheduled functions in the `SchedulerDO`, storage
|
|
445
|
-
* objects in R2). Every `collect()` / `get()` reaches across to that source.
|
|
446
|
-
*
|
|
447
|
-
* It is **not part of the mutation transaction snapshot** (no OCC guard, no
|
|
448
|
-
* subscription dependency recorded — reading it inside a mutation does not pin
|
|
449
|
-
* it), and results are **eventually consistent** with writes a mutation just
|
|
450
|
-
* made (e.g. a freshly scheduled job may not appear yet).
|
|
451
|
-
*
|
|
452
|
-
* Read-only by design: mutate scheduled jobs via `ctx.scheduler`, storage
|
|
453
|
-
* objects via `ctx.storage`.
|
|
454
|
-
*/
|
|
439
|
+
* Read-only reader over Lunora's system tables (`_scheduled_functions`,
|
|
440
|
+
* `_storage`), exposed as `ctx.db.system`. Mirrors Convex's `ctx.db.system`.
|
|
441
|
+
*
|
|
442
|
+
* **Best-effort and eventually consistent.** Unlike `ctx.db.<table>` — which
|
|
443
|
+
* reads the shard's transactional SQLite snapshot — the data behind these tables
|
|
444
|
+
* lives OUTSIDE the shard (scheduled functions in the `SchedulerDO`, storage
|
|
445
|
+
* objects in R2). Every `collect()` / `get()` reaches across to that source.
|
|
446
|
+
*
|
|
447
|
+
* It is **not part of the mutation transaction snapshot** (no OCC guard, no
|
|
448
|
+
* subscription dependency recorded — reading it inside a mutation does not pin
|
|
449
|
+
* it), and results are **eventually consistent** with writes a mutation just
|
|
450
|
+
* made (e.g. a freshly scheduled job may not appear yet).
|
|
451
|
+
*
|
|
452
|
+
* Read-only by design: mutate scheduled jobs via `ctx.scheduler`, storage
|
|
453
|
+
* objects via `ctx.storage`.
|
|
454
|
+
*/
|
|
455
455
|
interface SystemDatabaseReader {
|
|
456
456
|
/**
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
457
|
+
* Resolve a single system-table row by id, or `null` when absent.
|
|
458
|
+
* (`_scheduled_functions` → job id; `_storage` → object key.)
|
|
459
|
+
*/
|
|
460
460
|
get: <T extends SystemTableName>(table: T, id: string) => Promise<SystemDoc<T> | null>;
|
|
461
461
|
/**
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
462
|
+
* Begin a read over a system table; call `.collect()` to resolve the full
|
|
463
|
+
* list. No filtering, indexing, or pagination — the backing source is remote
|
|
464
|
+
* and the surface stays deliberately minimal.
|
|
465
|
+
*/
|
|
466
466
|
query: <T extends SystemTableName>(table: T) => SystemQuery<T>;
|
|
467
467
|
}
|
|
468
468
|
/**
|
|
469
|
-
* Read-only handle bound to a table. Used by `query`/`mutation`/`action`. The
|
|
470
|
-
* actual SQL implementation lives in `@lunora/do`; these are signatures only.
|
|
471
|
-
*/
|
|
469
|
+
* Read-only handle bound to a table. Used by `query`/`mutation`/`action`. The
|
|
470
|
+
* actual SQL implementation lives in `@lunora/do`; these are signatures only.
|
|
471
|
+
*/
|
|
472
472
|
interface DatabaseReader {
|
|
473
473
|
get: <T extends string>(id: Id<T>) => Promise<Record<string, unknown> | null>;
|
|
474
474
|
/**
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
475
|
+
* Validate an untrusted `id` string against the structural shape of an id
|
|
476
|
+
* for `tableName`, returning the branded {@link Id} when it is well-formed
|
|
477
|
+
* and `null` otherwise. Pure structural validation — it never reads the
|
|
478
|
+
* database, so a structurally valid id for a row that doesn't exist still
|
|
479
|
+
* returns the branded id (mirrors Convex's `db.normalizeId`).
|
|
480
|
+
*/
|
|
481
481
|
normalizeId: <T extends string>(tableName: T, id: string) => Id<T> | null;
|
|
482
482
|
query: (tableName: string) => TableReader;
|
|
483
483
|
/**
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
484
|
+
* Best-effort, read-only reader over Lunora's system tables
|
|
485
|
+
* (`_scheduled_functions`, `_storage`). Eventually consistent and **not**
|
|
486
|
+
* part of the transaction snapshot — see {@link SystemDatabaseReader}.
|
|
487
|
+
*/
|
|
488
488
|
readonly system: SystemDatabaseReader;
|
|
489
489
|
}
|
|
490
490
|
/** Options for {@link TableReader.paginate} — Convex-compatible page request. */
|
|
@@ -492,14 +492,14 @@ interface PaginationOptions {
|
|
|
492
492
|
/** Opaque cursor from the prior page's `continueCursor`; `null`/omitted starts at the first page. */
|
|
493
493
|
cursor?: null | string;
|
|
494
494
|
/**
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
495
|
+
* Optional inclusive upper bound for reactive pagination. When supplied the
|
|
496
|
+
* page covers the fixed half-open range `(cursor, endCursor]` (ignoring
|
|
497
|
+
* `numItems`): every row strictly after `cursor` up to and including the
|
|
498
|
+
* boundary row `endCursor` encodes. The page's `isDone` is `true` and its
|
|
499
|
+
* `continueCursor` echoes `endCursor`, so the next page keeps starting where
|
|
500
|
+
* this one ends even as rows are inserted/deleted inside the range. Omit (or
|
|
501
|
+
* pass `null`) for the legacy "first `numItems` after `cursor`" behaviour.
|
|
502
|
+
*/
|
|
503
503
|
endCursor?: null | string;
|
|
504
504
|
/** Maximum rows to return for this page. */
|
|
505
505
|
numItems: number;
|
|
@@ -512,46 +512,46 @@ interface PaginationResult<T = Record<string, unknown>> {
|
|
|
512
512
|
isDone: boolean;
|
|
513
513
|
page: T[];
|
|
514
514
|
/**
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
515
|
+
* Reactive-pagination only: the midpoint cursor of a bounded
|
|
516
|
+
* `(cursor, endCursor]` page, used by the client to split an over-grown page
|
|
517
|
+
* into two adjacent ranges. Absent on legacy (open-ended) pages.
|
|
518
|
+
*/
|
|
519
519
|
splitCursor?: null | string;
|
|
520
520
|
}
|
|
521
521
|
/**
|
|
522
|
-
* The fluent `ctx.db.query(table)` reader. Generic over the document type
|
|
523
|
-
* `Row` so the generated `ctx.db` can bind it to `Doc<table>` (the chain and
|
|
524
|
-
* every terminal then resolve typed rows — no `as unknown as Doc<...>` casts).
|
|
525
|
-
* Defaults to the untyped `Record<string, unknown>` shape for the base
|
|
526
|
-
* (schema-agnostic) `@lunora/server` reader.
|
|
527
|
-
*/
|
|
522
|
+
* The fluent `ctx.db.query(table)` reader. Generic over the document type
|
|
523
|
+
* `Row` so the generated `ctx.db` can bind it to `Doc<table>` (the chain and
|
|
524
|
+
* every terminal then resolve typed rows — no `as unknown as Doc<...>` casts).
|
|
525
|
+
* Defaults to the untyped `Record<string, unknown>` shape for the base
|
|
526
|
+
* (schema-agnostic) `@lunora/server` reader.
|
|
527
|
+
*/
|
|
528
528
|
interface TableReader<Row = Record<string, unknown>> {
|
|
529
529
|
collect: () => Promise<Row[]>;
|
|
530
530
|
filter: (predicate: (document: Row) => boolean) => TableReader<Row>;
|
|
531
531
|
first: () => Promise<Row | null>;
|
|
532
532
|
/**
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
533
|
+
* Set the result order. Orders by the active `.withIndex()` (or by
|
|
534
|
+
* `_creationTime` when none is staged), `"asc"` by default; `"desc"`
|
|
535
|
+
* reverses it. Composes with `.withIndex()`, `.filter()`, and every
|
|
536
|
+
* terminal (`collect`/`first`/`take`/`paginate`/`unique`). Mirrors Convex's
|
|
537
|
+
* `.order("asc" | "desc")`.
|
|
538
|
+
*/
|
|
539
539
|
order: (direction: "asc" | "desc") => TableReader<Row>;
|
|
540
540
|
paginate: (options: PaginationOptions) => Promise<PaginationResult<Row>>;
|
|
541
541
|
take: (limit: number) => Promise<Row[]>;
|
|
542
542
|
/**
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
543
|
+
* Return the single matching document. Returns `null` when nothing matches
|
|
544
|
+
* and throws when more than one row matches. Mirrors Convex's `.unique()`.
|
|
545
|
+
*/
|
|
546
546
|
unique: () => Promise<Row | null>;
|
|
547
547
|
withIndex: (indexName: string, range?: (q: IndexRangeBuilder) => IndexRangeBuilder) => TableReader<Row>;
|
|
548
548
|
/**
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
549
|
+
* Restrict the query to a declared `.searchIndex()`. The builder's
|
|
550
|
+
* `.search(field, query)` runs a full-text match against the index's
|
|
551
|
+
* searchable field; `.eq(field, value)` narrows by a declared filter
|
|
552
|
+
* field. Results come back ordered by relevance — pair with `.take(n)`
|
|
553
|
+
* (`.paginate()` is not supported on a search query).
|
|
554
|
+
*/
|
|
555
555
|
withSearchIndex: (indexName: string, search: (q: SearchFilterBuilder) => SearchFilterBuilder) => TableReader<Row>;
|
|
556
556
|
}
|
|
557
557
|
interface IndexRangeBuilder {
|
|
@@ -569,12 +569,12 @@ interface SearchFilterBuilder {
|
|
|
569
569
|
search: (field: string, query: string) => SearchFilterBuilder;
|
|
570
570
|
}
|
|
571
571
|
/**
|
|
572
|
-
* Options shared by the batch-write methods (`insertMany`/`deleteMany`/
|
|
573
|
-
* `patchMany`) — a per-call payload cap. The default cap (500) rejects an
|
|
574
|
-
* oversized call up front so an accidental O(n²) or a payload past the Durable
|
|
575
|
-
* Object request limit fails loudly instead of degrading the mutation. Callers
|
|
576
|
-
* with larger sets should chunk their own loop or raise `limit`.
|
|
577
|
-
*/
|
|
572
|
+
* Options shared by the batch-write methods (`insertMany`/`deleteMany`/
|
|
573
|
+
* `patchMany`) — a per-call payload cap. The default cap (500) rejects an
|
|
574
|
+
* oversized call up front so an accidental O(n²) or a payload past the Durable
|
|
575
|
+
* Object request limit fails loudly instead of degrading the mutation. Callers
|
|
576
|
+
* with larger sets should chunk their own loop or raise `limit`.
|
|
577
|
+
*/
|
|
578
578
|
interface BatchWriteOptions {
|
|
579
579
|
/** Reject the call when the batch size exceeds this value (default 500). */
|
|
580
580
|
limit?: number;
|
|
@@ -582,67 +582,67 @@ interface BatchWriteOptions {
|
|
|
582
582
|
/** Options accepted by {@link DatabaseWriter.insertMany} and the per-table facade. */
|
|
583
583
|
interface InsertManyOptions extends BatchWriteOptions {
|
|
584
584
|
/**
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
585
|
+
* When `true`, a UNIQUE-constraint breach for a row resolves to `null`
|
|
586
|
+
* instead of throwing — the rest of the batch is still inserted. Skipped rows
|
|
587
|
+
* keep their input-order slot with `null` in the returned array. Mirrors
|
|
588
|
+
* better-drizzle's `createMany({ skipDuplicates: true })`.
|
|
589
|
+
*/
|
|
590
590
|
skipDuplicates?: boolean;
|
|
591
591
|
}
|
|
592
592
|
interface DatabaseWriter extends DatabaseReader {
|
|
593
593
|
delete: <T extends string>(id: Id<T>) => Promise<void>;
|
|
594
594
|
/**
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
595
|
+
* Delete many rows by id in one call. Each id is deleted through the full
|
|
596
|
+
* single-row pipeline (triggers + per-row RLS). The returned `deleted` is the
|
|
597
|
+
* number of ids **requested**, not the rows actually removed — an unknown or
|
|
598
|
+
* duplicated id is a silent no-op.
|
|
599
|
+
*
|
|
600
|
+
* **Atomic within a mutation:** the DO wraps a mutation's dispatch in a
|
|
601
|
+
* BEGIN/COMMIT span, so a mid-batch failure (a later RLS denial or handler
|
|
602
|
+
* error) rolls back the whole mutation. (In an action there is no transaction
|
|
603
|
+
* span, so the prior deletes persist; the in-memory test harness mirrors the span.)
|
|
604
|
+
*/
|
|
605
605
|
deleteMany: <T extends string>(ids: ReadonlyArray<Id<T>>, options?: BatchWriteOptions) => Promise<{
|
|
606
606
|
deleted: number;
|
|
607
607
|
}>;
|
|
608
608
|
/**
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
609
|
+
* Delete every row matching `where` in one call. Matching rows are resolved
|
|
610
|
+
* first, then each row is deleted through the single-row delete pipeline
|
|
611
|
+
* (triggers, companion sync, CDC, broadcast) so reactive subscriptions and
|
|
612
|
+
* search/aggregate companions stay correct.
|
|
613
|
+
*
|
|
614
|
+
* **Atomic within a mutation:** the DO wraps a mutation's dispatch in a
|
|
615
|
+
* BEGIN/COMMIT span, so a mid-batch failure rolls back the whole mutation.
|
|
616
|
+
*/
|
|
617
617
|
deleteWhere: (tableName: string, where: Record<string, unknown>, options?: BatchWriteOptions) => Promise<{
|
|
618
618
|
deleted: number;
|
|
619
619
|
}>;
|
|
620
620
|
/**
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
621
|
+
* Insert a document, returning its server id.
|
|
622
|
+
*
|
|
623
|
+
* Pass `options.clientId` (a UUID) to key the row yourself — for an
|
|
624
|
+
* optimistic client that needs the persisted row to match the key it
|
|
625
|
+
* already rendered. It's validated for shape and still subject to the
|
|
626
|
+
* primary-key uniqueness constraint; omit it and the server mints the id.
|
|
627
|
+
*/
|
|
628
628
|
insert: <T extends string>(tableName: T, document: Record<string, unknown>, options?: {
|
|
629
629
|
clientId?: string;
|
|
630
630
|
}) => Promise<Id<T>>;
|
|
631
631
|
/**
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
632
|
+
* Insert many documents into one table in a single call, returning the
|
|
633
|
+
* minted ids in input order. Equivalent to a per-row `insert()` loop — each
|
|
634
|
+
* row gets defaults, validators, triggers, and a per-row RLS check — but the
|
|
635
|
+
* caller pays one round-trip instead of N.
|
|
636
|
+
*
|
|
637
|
+
* Pass `{ skipDuplicates: true }` to turn UNIQUE-constraint breaches into
|
|
638
|
+
* `null` results for that row instead of failing the whole batch; the rest of
|
|
639
|
+
* the batch is still inserted and order is preserved.
|
|
640
|
+
*
|
|
641
|
+
* **Atomic within a mutation:** the DO wraps a mutation's dispatch in a
|
|
642
|
+
* BEGIN/COMMIT span, so a mid-batch failure (an invalid or RLS-denied row)
|
|
643
|
+
* rolls back the whole mutation. (In an action there is no transaction span,
|
|
644
|
+
* so the prior inserts persist; the in-memory test harness mirrors the span.)
|
|
645
|
+
*/
|
|
646
646
|
insertMany: {
|
|
647
647
|
<T extends string>(tableName: T, documents: ReadonlyArray<Record<string, unknown>>, options: BatchWriteOptions & {
|
|
648
648
|
skipDuplicates: true;
|
|
@@ -650,32 +650,32 @@ interface DatabaseWriter extends DatabaseReader {
|
|
|
650
650
|
<T extends string>(tableName: T, documents: ReadonlyArray<Record<string, unknown>>, options?: InsertManyOptions): Promise<Id<T>[]>;
|
|
651
651
|
};
|
|
652
652
|
/**
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
653
|
+
* **Trusted** bulk insert: one multi-row `INSERT` that **skips per-row
|
|
654
|
+
* `.check()` validators and before/after triggers** for throughput on data you
|
|
655
|
+
* control (seed, migration, admin import). Defaults, ids, and every companion
|
|
656
|
+
* (search/aggregate/rank/CDC + live subscriptions) are still applied, so reads
|
|
657
|
+
* stay correct.
|
|
658
|
+
*
|
|
659
|
+
* It is **"unsafe" only in that it bypasses the validation/trigger pipeline** —
|
|
660
|
+
* RLS is **not** bypassed: secure-by-default and the table's insert policy still
|
|
661
|
+
* apply (the framework ships no RLS-bypassing writer). Pass `allowExplicitId` to
|
|
662
|
+
* preserve a supplied `_id` (import). Use only for data you trust; prefer
|
|
663
|
+
* `insertMany` for anything user-supplied.
|
|
664
|
+
*/
|
|
665
665
|
insertManyUnsafe: <T extends string>(tableName: T, documents: ReadonlyArray<Record<string, unknown>>, options?: BatchWriteOptions & {
|
|
666
666
|
allowExplicitId?: boolean;
|
|
667
667
|
}) => Promise<Id<T>[]>;
|
|
668
668
|
patch: <T extends string>(id: Id<T>, patch: Record<string, unknown>) => Promise<void>;
|
|
669
669
|
/**
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
670
|
+
* Patch many rows by id in one call. Each `{ id, patch }` is applied like a
|
|
671
|
+
* single `patch()` (per-row triggers + RLS). Returns the number of rows
|
|
672
|
+
* actually patched.
|
|
673
|
+
*
|
|
674
|
+
* **Atomic within a mutation:** the DO wraps a mutation's dispatch in a
|
|
675
|
+
* BEGIN/COMMIT span, so a mid-batch failure rolls back the whole mutation.
|
|
676
|
+
* (In an action there is no transaction span, so the prior patches persist;
|
|
677
|
+
* the in-memory test harness mirrors the span.)
|
|
678
|
+
*/
|
|
679
679
|
patchMany: <T extends string>(patches: ReadonlyArray<{
|
|
680
680
|
id: Id<T>;
|
|
681
681
|
patch: Record<string, unknown>;
|
|
@@ -683,14 +683,14 @@ interface DatabaseWriter extends DatabaseReader {
|
|
|
683
683
|
patched: number;
|
|
684
684
|
}>;
|
|
685
685
|
/**
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
686
|
+
* Patch every row matching `where` with the same `patch` in one call. The
|
|
687
|
+
* matching rows are resolved first, then each row is updated through the
|
|
688
|
+
* single-row patch pipeline (OCC, triggers, companion sync, CDC, broadcast)
|
|
689
|
+
* so reactive subscriptions and search/aggregate companions stay correct.
|
|
690
|
+
*
|
|
691
|
+
* **Atomic within a mutation:** the DO wraps a mutation's dispatch in a
|
|
692
|
+
* BEGIN/COMMIT span, so a mid-batch failure rolls back the whole mutation.
|
|
693
|
+
*/
|
|
694
694
|
patchWhere: (tableName: string, args: {
|
|
695
695
|
patch: Record<string, unknown>;
|
|
696
696
|
where: Record<string, unknown>;
|
|
@@ -705,11 +705,11 @@ interface AuthState {
|
|
|
705
705
|
readonly userId: string | null;
|
|
706
706
|
}
|
|
707
707
|
/**
|
|
708
|
-
* A pending scheduled invocation as surfaced by {@link Scheduler.list} /
|
|
709
|
-
* {@link Scheduler.get}. A clean public mirror of `@lunora/scheduler`'s internal
|
|
710
|
-
* `ScheduleRecord` — re-declared here so the public ctx surface carries no
|
|
711
|
-
* dependency on the scheduler package's internal types.
|
|
712
|
-
*/
|
|
708
|
+
* A pending scheduled invocation as surfaced by {@link Scheduler.list} /
|
|
709
|
+
* {@link Scheduler.get}. A clean public mirror of `@lunora/scheduler`'s internal
|
|
710
|
+
* `ScheduleRecord` — re-declared here so the public ctx surface carries no
|
|
711
|
+
* dependency on the scheduler package's internal types.
|
|
712
|
+
*/
|
|
713
713
|
interface ScheduledJob {
|
|
714
714
|
args: Record<string, unknown>;
|
|
715
715
|
/** Number of dispatch attempts already made (absent until the first retry). */
|
|
@@ -724,13 +724,13 @@ interface ScheduledJob {
|
|
|
724
724
|
shardKey?: string;
|
|
725
725
|
}
|
|
726
726
|
/**
|
|
727
|
-
* A schedulable durable-workflow reference — the generated `workflows.<name>` /
|
|
728
|
-
* `agents.<name>` object, which carries its `WORKFLOW_*`/`AGENT_*` binding and
|
|
729
|
-
* stable name. Structural mirror of `@lunora/scheduler`'s `WorkflowReference` so
|
|
730
|
-
* `ctx.scheduler` can target a workflow/agent without a dependency on
|
|
731
|
-
* `@lunora/scheduler` / `@lunora/workflow`. A scheduled workflow target starts a
|
|
732
|
-
* fresh instance on fire (the args become its `params`).
|
|
733
|
-
*/
|
|
727
|
+
* A schedulable durable-workflow reference — the generated `workflows.<name>` /
|
|
728
|
+
* `agents.<name>` object, which carries its `WORKFLOW_*`/`AGENT_*` binding and
|
|
729
|
+
* stable name. Structural mirror of `@lunora/scheduler`'s `WorkflowReference` so
|
|
730
|
+
* `ctx.scheduler` can target a workflow/agent without a dependency on
|
|
731
|
+
* `@lunora/scheduler` / `@lunora/workflow`. A scheduled workflow target starts a
|
|
732
|
+
* fresh instance on fire (the args become its `params`).
|
|
733
|
+
*/
|
|
734
734
|
interface SchedulableWorkflowReference {
|
|
735
735
|
/** The `WORKFLOW_*`/`AGENT_*` binding name (present on a generated ref). */
|
|
736
736
|
readonly binding?: string;
|
|
@@ -748,22 +748,22 @@ interface Scheduler {
|
|
|
748
748
|
/** List all pending scheduled jobs. */
|
|
749
749
|
list: () => Promise<ScheduledJob[]>;
|
|
750
750
|
/**
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
751
|
+
* Schedule a one-shot run `delayMs` from now. `target` is a function path
|
|
752
|
+
* (`"ns:fn"`) dispatched as a one-shot, or a generated `workflows.<name>` /
|
|
753
|
+
* `agents.<name>` reference which starts a fresh durable instance on fire
|
|
754
|
+
* (the args become its `params`).
|
|
755
|
+
*/
|
|
756
756
|
runAfter: (delayMs: number, target: SchedulableWorkflowReference | string, args?: Record<string, unknown>) => Promise<string>;
|
|
757
757
|
/** Like {@link Scheduler.runAfter} but fires at an absolute epoch-ms timestamp. */
|
|
758
758
|
runAt: (timestampMs: number, target: SchedulableWorkflowReference | string, args?: Record<string, unknown>) => Promise<string>;
|
|
759
759
|
}
|
|
760
760
|
/**
|
|
761
|
-
* A workflow instance's lifecycle status. Clean public mirror of
|
|
762
|
-
* `@lunora/workflow`'s `WorkflowInstanceStatus` (itself a mirror of Cloudflare's
|
|
763
|
-
* `WorkflowInstanceStatus`) — re-declared here so the ctx surface carries no
|
|
764
|
-
* dependency on the workflow package, exactly as {@link Scheduler} avoids a
|
|
765
|
-
* dependency on `@lunora/scheduler`.
|
|
766
|
-
*/
|
|
761
|
+
* A workflow instance's lifecycle status. Clean public mirror of
|
|
762
|
+
* `@lunora/workflow`'s `WorkflowInstanceStatus` (itself a mirror of Cloudflare's
|
|
763
|
+
* `WorkflowInstanceStatus`) — re-declared here so the ctx surface carries no
|
|
764
|
+
* dependency on the workflow package, exactly as {@link Scheduler} avoids a
|
|
765
|
+
* dependency on `@lunora/scheduler`.
|
|
766
|
+
*/
|
|
767
767
|
type WorkflowInstanceStatus = "complete" | "errored" | "paused" | "queued" | "running" | "terminated" | "unknown" | "waiting" | "waitingForPause";
|
|
768
768
|
/** Result of {@link WorkflowInstance.status}. Mirrors `@lunora/workflow`'s `WorkflowStatusResult`. */
|
|
769
769
|
interface WorkflowStatusResult {
|
|
@@ -800,9 +800,9 @@ interface WorkflowInstance {
|
|
|
800
800
|
terminate: () => Promise<void>;
|
|
801
801
|
}
|
|
802
802
|
/**
|
|
803
|
-
* A typed handle to one declared workflow, addressable from `ctx.workflows`.
|
|
804
|
-
* Mirrors `@lunora/workflow`'s `WorkflowHandle`.
|
|
805
|
-
*/
|
|
803
|
+
* A typed handle to one declared workflow, addressable from `ctx.workflows`.
|
|
804
|
+
* Mirrors `@lunora/workflow`'s `WorkflowHandle`.
|
|
805
|
+
*/
|
|
806
806
|
interface WorkflowHandle<Params = Record<string, unknown>> {
|
|
807
807
|
/** Start a new instance (optionally with an id + params). */
|
|
808
808
|
create: (options?: WorkflowCreateOptions<Params>) => Promise<WorkflowInstance>;
|
|
@@ -812,51 +812,51 @@ interface WorkflowHandle<Params = Record<string, unknown>> {
|
|
|
812
812
|
get: (id: string) => Promise<WorkflowInstance>;
|
|
813
813
|
}
|
|
814
814
|
/**
|
|
815
|
-
* The `ctx.workflows` surface on {@link MutationCtx} / {@link ActionCtx}. Each
|
|
816
|
-
* workflow declared in `lunora/workflows.ts` is reachable by its export name;
|
|
817
|
-
* codegen narrows the `get(name)` overloads to the known workflow names + their
|
|
818
|
-
* inferred param types. Mirrors `@lunora/workflow`'s `Workflows`.
|
|
819
|
-
*/
|
|
815
|
+
* The `ctx.workflows` surface on {@link MutationCtx} / {@link ActionCtx}. Each
|
|
816
|
+
* workflow declared in `lunora/workflows.ts` is reachable by its export name;
|
|
817
|
+
* codegen narrows the `get(name)` overloads to the known workflow names + their
|
|
818
|
+
* inferred param types. Mirrors `@lunora/workflow`'s `Workflows`.
|
|
819
|
+
*/
|
|
820
820
|
interface Workflows {
|
|
821
821
|
/** Resolve the handle for a declared workflow by export name. */
|
|
822
822
|
get: <Params = Record<string, unknown>>(name: string) => WorkflowHandle<Params>;
|
|
823
823
|
}
|
|
824
824
|
/**
|
|
825
|
-
* Programmatic cache purge surface exposed on {@link ActionCtx}. Actions run
|
|
826
|
-
* in the Worker (not the DO), so they can reach the Worker's `ctx.cache.purge`.
|
|
827
|
-
* Queries and mutations do not expose this — they run inside the Durable Object.
|
|
828
|
-
*/
|
|
825
|
+
* Programmatic cache purge surface exposed on {@link ActionCtx}. Actions run
|
|
826
|
+
* in the Worker (not the DO), so they can reach the Worker's `ctx.cache.purge`.
|
|
827
|
+
* Queries and mutations do not expose this — they run inside the Durable Object.
|
|
828
|
+
*/
|
|
829
829
|
interface CachePurge {
|
|
830
830
|
/**
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
831
|
+
* Purge cached responses matching the given tags, or everything when
|
|
832
|
+
* `purgeEverything` is true. Only available in action handlers.
|
|
833
|
+
*/
|
|
834
834
|
purge: (options: {
|
|
835
835
|
purgeEverything?: boolean;
|
|
836
836
|
tags?: string[];
|
|
837
837
|
}) => Promise<unknown>;
|
|
838
838
|
}
|
|
839
839
|
/**
|
|
840
|
-
* Structural projection of workers-types' `SecretsStoreSecret` binding — the
|
|
841
|
-
* per-secret `secrets_store_secrets[]` binding whose `.get()` resolves the
|
|
842
|
-
* secret value (or throws if it does not exist). Mirrored structurally so the
|
|
843
|
-
* runtime resolves it without a workerd type dependency.
|
|
844
|
-
*/
|
|
840
|
+
* Structural projection of workers-types' `SecretsStoreSecret` binding — the
|
|
841
|
+
* per-secret `secrets_store_secrets[]` binding whose `.get()` resolves the
|
|
842
|
+
* secret value (or throws if it does not exist). Mirrored structurally so the
|
|
843
|
+
* runtime resolves it without a workerd type dependency.
|
|
844
|
+
*/
|
|
845
845
|
interface SecretsStoreSecretLike {
|
|
846
846
|
get: () => Promise<string>;
|
|
847
847
|
}
|
|
848
848
|
/**
|
|
849
|
-
* `ctx.secrets` — read account-level secrets bound via Cloudflare Secrets Store.
|
|
850
|
-
* A core built-in (always present on every context, like `ctx.log`): a binding
|
|
851
|
-
* named in wrangler's `secrets_store_secrets[]` is read by its binding name.
|
|
852
|
-
*
|
|
853
|
-
* ```ts
|
|
854
|
-
* const apiKey = await ctx.secrets.get("STRIPE_KEY");
|
|
855
|
-
* ```
|
|
856
|
-
*
|
|
857
|
-
* The lookup is async (the platform fetches and decrypts on first read);
|
|
858
|
-
* reading an undeclared name throws a directed error naming the bound secrets.
|
|
859
|
-
*/
|
|
849
|
+
* `ctx.secrets` — read account-level secrets bound via Cloudflare Secrets Store.
|
|
850
|
+
* A core built-in (always present on every context, like `ctx.log`): a binding
|
|
851
|
+
* named in wrangler's `secrets_store_secrets[]` is read by its binding name.
|
|
852
|
+
*
|
|
853
|
+
* ```ts
|
|
854
|
+
* const apiKey = await ctx.secrets.get("STRIPE_KEY");
|
|
855
|
+
* ```
|
|
856
|
+
*
|
|
857
|
+
* The lookup is async (the platform fetches and decrypts on first read);
|
|
858
|
+
* reading an undeclared name throws a directed error naming the bound secrets.
|
|
859
|
+
*/
|
|
860
860
|
interface Secrets {
|
|
861
861
|
/** Resolve a Secrets Store secret by its wrangler binding name. */
|
|
862
862
|
get: (name: string) => Promise<string>;
|
|
@@ -866,11 +866,11 @@ type TriggerTiming = "after" | "before";
|
|
|
866
866
|
/** The CRUD operation a trigger reacts to. `patch` and `replace` both map to `update`. */
|
|
867
867
|
type TriggerOp = "delete" | "insert" | "update";
|
|
868
868
|
/**
|
|
869
|
-
* A row as observed by a trigger handler: the table's `Shape` (with the same
|
|
870
|
-
* optionality rules as {@link InferArgs}) plus the system columns every stored
|
|
871
|
-
* doc carries.
|
|
872
|
-
*/
|
|
873
|
-
type TriggerRow<Shape extends Record<string, Validator>> = { [K in keyof Shape as undefined extends Infer<Shape[K]> ? K : never]?: Infer<Shape[K]
|
|
869
|
+
* A row as observed by a trigger handler: the table's `Shape` (with the same
|
|
870
|
+
* optionality rules as {@link InferArgs}) plus the system columns every stored
|
|
871
|
+
* doc carries.
|
|
872
|
+
*/
|
|
873
|
+
type TriggerRow<Shape extends Record<string, Validator>> = { [K in keyof Shape as undefined extends Infer<Shape[K]> ? K : never]?: Infer<Shape[K]>; } & { [K in keyof Shape as undefined extends Infer<Shape[K]> ? never : K]: Infer<Shape[K]>; } & {
|
|
874
874
|
readonly _creationTime: number;
|
|
875
875
|
readonly _id: string;
|
|
876
876
|
};
|
|
@@ -882,11 +882,11 @@ interface TriggerInsertEvent<Shape extends Record<string, Validator> = Record<st
|
|
|
882
882
|
readonly table: string;
|
|
883
883
|
}
|
|
884
884
|
/**
|
|
885
|
-
* What an `update` trigger observes: the merged row plus the pre-write row.
|
|
886
|
-
* `previous` is typed as always present (the row must exist to be updated); the
|
|
887
|
-
* runtime supplies it best-effort and only omits it in the unreachable
|
|
888
|
-
* row-vanished-mid-write case.
|
|
889
|
-
*/
|
|
885
|
+
* What an `update` trigger observes: the merged row plus the pre-write row.
|
|
886
|
+
* `previous` is typed as always present (the row must exist to be updated); the
|
|
887
|
+
* runtime supplies it best-effort and only omits it in the unreachable
|
|
888
|
+
* row-vanished-mid-write case.
|
|
889
|
+
*/
|
|
890
890
|
interface TriggerUpdateEvent<Shape extends Record<string, Validator> = Record<string, Validator>> {
|
|
891
891
|
readonly doc: TriggerRow<Shape>;
|
|
892
892
|
readonly id: string;
|
|
@@ -895,10 +895,10 @@ interface TriggerUpdateEvent<Shape extends Record<string, Validator> = Record<st
|
|
|
895
895
|
readonly table: string;
|
|
896
896
|
}
|
|
897
897
|
/**
|
|
898
|
-
* What a `delete` trigger observes: the row about to be (or just) removed.
|
|
899
|
-
* `previous` is typed as always present; the runtime supplies it best-effort
|
|
900
|
-
* and only omits it in the unreachable row-vanished-mid-write case.
|
|
901
|
-
*/
|
|
898
|
+
* What a `delete` trigger observes: the row about to be (or just) removed.
|
|
899
|
+
* `previous` is typed as always present; the runtime supplies it best-effort
|
|
900
|
+
* and only omits it in the unreachable row-vanished-mid-write case.
|
|
901
|
+
*/
|
|
902
902
|
interface TriggerDeleteEvent<Shape extends Record<string, Validator> = Record<string, Validator>> {
|
|
903
903
|
readonly id: string;
|
|
904
904
|
readonly op: "delete";
|
|
@@ -922,10 +922,10 @@ interface TriggerQueryArgs {
|
|
|
922
922
|
with?: Record<string, unknown>;
|
|
923
923
|
}
|
|
924
924
|
/**
|
|
925
|
-
* Args accepted by {@link TriggerDatabase.aggregate} — structural mirror of
|
|
926
|
-
* `@lunora/do`'s `AggregateOptions`, kept local so trigger handlers in
|
|
927
|
-
* `@lunora/server` don't take a hard dep on the DO runtime.
|
|
928
|
-
*/
|
|
925
|
+
* Args accepted by {@link TriggerDatabase.aggregate} — structural mirror of
|
|
926
|
+
* `@lunora/do`'s `AggregateOptions`, kept local so trigger handlers in
|
|
927
|
+
* `@lunora/server` don't take a hard dep on the DO runtime.
|
|
928
|
+
*/
|
|
929
929
|
interface TriggerAggregateOptions {
|
|
930
930
|
baseWhere?: Record<string, unknown>;
|
|
931
931
|
field?: string;
|
|
@@ -970,17 +970,17 @@ interface TriggerRankPageOptions {
|
|
|
970
970
|
where?: Record<string, unknown>;
|
|
971
971
|
}
|
|
972
972
|
/**
|
|
973
|
-
* Portable, table/id-addressed ORM writer handed to trigger handlers via
|
|
974
|
-
* `ctx.db`. Mirrors `@lunora/do`'s runtime `DatabaseWriterLike` surface — it is
|
|
975
|
-
* **not** the generated per-table `ctx.db.<table>` facade (which can't be typed
|
|
976
|
-
* from inside `defineTable`, where the full schema isn't known).
|
|
977
|
-
*
|
|
978
|
-
* `aggregate`/`groupBy`/`count`/`rank`/`rankPage` route through the same
|
|
979
|
-
* trigger-maintained counter and rank tables the user-facing reader uses, so
|
|
980
|
-
* a handler's `ctx.db.<table>.aggregate(...)` observes the just-staged write
|
|
981
|
-
* within the same DO transaction (the counter step happens before the trigger
|
|
982
|
-
* fires).
|
|
983
|
-
*/
|
|
973
|
+
* Portable, table/id-addressed ORM writer handed to trigger handlers via
|
|
974
|
+
* `ctx.db`. Mirrors `@lunora/do`'s runtime `DatabaseWriterLike` surface — it is
|
|
975
|
+
* **not** the generated per-table `ctx.db.<table>` facade (which can't be typed
|
|
976
|
+
* from inside `defineTable`, where the full schema isn't known).
|
|
977
|
+
*
|
|
978
|
+
* `aggregate`/`groupBy`/`count`/`rank`/`rankPage` route through the same
|
|
979
|
+
* trigger-maintained counter and rank tables the user-facing reader uses, so
|
|
980
|
+
* a handler's `ctx.db.<table>.aggregate(...)` observes the just-staged write
|
|
981
|
+
* within the same DO transaction (the counter step happens before the trigger
|
|
982
|
+
* fires).
|
|
983
|
+
*/
|
|
984
984
|
interface TriggerDatabase {
|
|
985
985
|
aggregate: (tableName: string, options: TriggerAggregateOptions) => Promise<null | number>;
|
|
986
986
|
count: (tableName: string, where?: Record<string, unknown>) => Promise<number>;
|
|
@@ -996,10 +996,10 @@ interface TriggerDatabase {
|
|
|
996
996
|
replace: (id: string, document: Record<string, unknown>) => Promise<void>;
|
|
997
997
|
}
|
|
998
998
|
/**
|
|
999
|
-
* Handle injected into every trigger handler. `db` is the portable ORM writer;
|
|
1000
|
-
* `scheduler` enqueues async / cross-shard follow-up work (cross-shard work is
|
|
1001
|
-
* **not** transactional with the firing write).
|
|
1002
|
-
*/
|
|
999
|
+
* Handle injected into every trigger handler. `db` is the portable ORM writer;
|
|
1000
|
+
* `scheduler` enqueues async / cross-shard follow-up work (cross-shard work is
|
|
1001
|
+
* **not** transactional with the firing write).
|
|
1002
|
+
*/
|
|
1003
1003
|
interface TriggerCtx {
|
|
1004
1004
|
readonly db: TriggerDatabase;
|
|
1005
1005
|
readonly scheduler: Scheduler;
|
|
@@ -1007,20 +1007,20 @@ interface TriggerCtx {
|
|
|
1007
1007
|
/** A user-declared trigger handler. Throwing from a `before*` handler aborts the write. */
|
|
1008
1008
|
type TriggerHandler<Event> = (context: TriggerCtx, event: Event) => Promise<void> | void;
|
|
1009
1009
|
/**
|
|
1010
|
-
* A single declared trigger, as stored in {@link TableDefinition.triggerMap}.
|
|
1011
|
-
* The handler's event type is erased to the {@link TriggerEvent} union here; the
|
|
1012
|
-
* per-op {@link TriggerBuilder} methods recover the precise event type for
|
|
1013
|
-
* authors.
|
|
1014
|
-
*/
|
|
1010
|
+
* A single declared trigger, as stored in {@link TableDefinition.triggerMap}.
|
|
1011
|
+
* The handler's event type is erased to the {@link TriggerEvent} union here; the
|
|
1012
|
+
* per-op {@link TriggerBuilder} methods recover the precise event type for
|
|
1013
|
+
* authors.
|
|
1014
|
+
*/
|
|
1015
1015
|
interface TriggerDefinition {
|
|
1016
1016
|
readonly handler: TriggerHandler<TriggerEvent>;
|
|
1017
1017
|
readonly op: TriggerOp;
|
|
1018
1018
|
readonly timing: TriggerTiming;
|
|
1019
1019
|
}
|
|
1020
1020
|
/**
|
|
1021
|
-
* The `t` argument passed to `.triggers((t) => …)`. Each method binds a handler
|
|
1022
|
-
* to one `timing`+`op` pair, typing the event against the table's `Shape`.
|
|
1023
|
-
*/
|
|
1021
|
+
* The `t` argument passed to `.triggers((t) => …)`. Each method binds a handler
|
|
1022
|
+
* to one `timing`+`op` pair, typing the event against the table's `Shape`.
|
|
1023
|
+
*/
|
|
1024
1024
|
interface TriggerBuilder<Shape extends Record<string, Validator> = Record<string, Validator>> {
|
|
1025
1025
|
afterDelete: (handler: TriggerHandler<TriggerDeleteEvent<Shape>>) => TriggerDefinition;
|
|
1026
1026
|
afterInsert: (handler: TriggerHandler<TriggerInsertEvent<Shape>>) => TriggerDefinition;
|
|
@@ -1030,11 +1030,11 @@ interface TriggerBuilder<Shape extends Record<string, Validator> = Record<string
|
|
|
1030
1030
|
beforeUpdate: (handler: TriggerHandler<TriggerUpdateEvent<Shape>>) => TriggerDefinition;
|
|
1031
1031
|
}
|
|
1032
1032
|
/**
|
|
1033
|
-
* Per-file metadata returned by {@link ReadOnlyStorage.getMetadata}. A clean
|
|
1034
|
-
* public mirror of `@lunora/storage`'s `ObjectMetadata` — re-declared here so
|
|
1035
|
-
* the ctx surface carries no dependency on the storage package's types. Matches
|
|
1036
|
-
* the columns Convex surfaces for `ctx.storage.getMetadata` / `_storage`.
|
|
1037
|
-
*/
|
|
1033
|
+
* Per-file metadata returned by {@link ReadOnlyStorage.getMetadata}. A clean
|
|
1034
|
+
* public mirror of `@lunora/storage`'s `ObjectMetadata` — re-declared here so
|
|
1035
|
+
* the ctx surface carries no dependency on the storage package's types. Matches
|
|
1036
|
+
* the columns Convex surfaces for `ctx.storage.getMetadata` / `_storage`.
|
|
1037
|
+
*/
|
|
1038
1038
|
interface StorageMetadata {
|
|
1039
1039
|
/** The object's `Content-Type`, when recorded. */
|
|
1040
1040
|
contentType?: string;
|
|
@@ -1050,31 +1050,31 @@ interface StorageMetadata {
|
|
|
1050
1050
|
uploaded?: number;
|
|
1051
1051
|
}
|
|
1052
1052
|
/**
|
|
1053
|
-
* Read-only projection of `Storage` exposed on `QueryCtx` / `MutationCtx`.
|
|
1054
|
-
*
|
|
1055
|
-
* Queries are pure reads, and mutations run inside a transactional scope —
|
|
1056
|
-
* neither is allowed to perform side-effectful R2 writes (`upload`) or
|
|
1057
|
-
* deletes (`delete`). They can, however, **read** existing objects and
|
|
1058
|
-
* resolve signed URLs (the URL signing itself is HMAC-only — no R2 round
|
|
1059
|
-
* trip), so the read-only surface keeps `download` and `getSignedUrl`. The
|
|
1060
|
-
* full {@link Storage} surface stays on `ActionCtx`.
|
|
1061
|
-
*/
|
|
1053
|
+
* Read-only projection of `Storage` exposed on `QueryCtx` / `MutationCtx`.
|
|
1054
|
+
*
|
|
1055
|
+
* Queries are pure reads, and mutations run inside a transactional scope —
|
|
1056
|
+
* neither is allowed to perform side-effectful R2 writes (`upload`) or
|
|
1057
|
+
* deletes (`delete`). They can, however, **read** existing objects and
|
|
1058
|
+
* resolve signed URLs (the URL signing itself is HMAC-only — no R2 round
|
|
1059
|
+
* trip), so the read-only surface keeps `download` and `getSignedUrl`. The
|
|
1060
|
+
* full {@link Storage} surface stays on `ActionCtx`.
|
|
1061
|
+
*/
|
|
1062
1062
|
interface ReadOnlyStorage<Buckets extends string = string> {
|
|
1063
1063
|
/**
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1064
|
+
* Select a named bucket (declared via `v.storage("name")`). The returned
|
|
1065
|
+
* accessor's operations target that bucket — `ctx.storage.bucket("avatars")
|
|
1066
|
+
* .download(key)`. The bare `ctx.storage` targets the default bucket.
|
|
1067
|
+
*/
|
|
1068
1068
|
bucket: (name: Buckets) => ReadOnlyStorage<Buckets>;
|
|
1069
1069
|
/** The bucket this accessor's operations target (the default for the bare `ctx.storage`). */
|
|
1070
1070
|
readonly bucketName: string;
|
|
1071
1071
|
/** Fetch the body of an existing object. Returns `null` when absent. */
|
|
1072
1072
|
download: (key: string) => Promise<ReadableStream | null>;
|
|
1073
1073
|
/**
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1074
|
+
* Read a file's metadata (size, content-type, sha256, upload time, custom
|
|
1075
|
+
* metadata) without fetching its body. Returns `null` when the object is
|
|
1076
|
+
* absent. Mirrors Convex's `ctx.storage.getMetadata`.
|
|
1077
|
+
*/
|
|
1078
1078
|
getMetadata: (key: string) => Promise<StorageMetadata | null>;
|
|
1079
1079
|
/** Resolve a short-lived signed URL for an existing object. */
|
|
1080
1080
|
getSignedUrl: (key: string, options?: {
|
|
@@ -1088,20 +1088,20 @@ interface Storage<Buckets extends string = string> extends ReadOnlyStorage<Bucke
|
|
|
1088
1088
|
bucket: (name: Buckets) => Storage<Buckets>;
|
|
1089
1089
|
delete: (key: string) => Promise<void>;
|
|
1090
1090
|
/**
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1091
|
+
* Mint a short-lived signed `PUT` URL a client can upload directly to,
|
|
1092
|
+
* optionally pinning the `Content-Type` the uploader must send. Mirrors
|
|
1093
|
+
* Convex's `storage.generateUploadUrl`.
|
|
1094
|
+
*/
|
|
1095
1095
|
generateUploadUrl: (key: string, options?: {
|
|
1096
1096
|
contentType?: string;
|
|
1097
1097
|
expiresInSeconds?: number;
|
|
1098
1098
|
}) => Promise<string>;
|
|
1099
1099
|
/**
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1100
|
+
* Upload `body` to `key` from the server, returning the stored object's key
|
|
1101
|
+
* and etag. Mirrors Convex's `storage.store`. Accepts the same guard fields
|
|
1102
|
+
* as `@lunora/storage`'s `UploadOptions` so `maxSize` /
|
|
1103
|
+
* `allowedContentTypes` enforcement isn't lost behind the Convex-style alias.
|
|
1104
|
+
*/
|
|
1105
1105
|
store: (key: string, body: ReadableStream | ArrayBuffer | Blob, options?: {
|
|
1106
1106
|
allowedContentTypes?: ReadonlyArray<string>;
|
|
1107
1107
|
contentType?: string;
|
|
@@ -1145,84 +1145,122 @@ interface VectorRecord {
|
|
|
1145
1145
|
values: ReadonlyArray<number>;
|
|
1146
1146
|
}
|
|
1147
1147
|
/**
|
|
1148
|
-
* Read-only vector surface exposed on {@link QueryCtx}. Mirrors the read half
|
|
1149
|
-
* of `@lunora/bindings/vectors`' `LunoraVectors` so the live adapter is assignable.
|
|
1150
|
-
*/
|
|
1148
|
+
* Read-only vector surface exposed on {@link QueryCtx}. Mirrors the read half
|
|
1149
|
+
* of `@lunora/bindings/vectors`' `LunoraVectors` so the live adapter is assignable.
|
|
1150
|
+
*/
|
|
1151
1151
|
interface VectorSearchReader {
|
|
1152
1152
|
getByIds: (indexName: string, ids: ReadonlyArray<string>) => Promise<ReadonlyArray<VectorRecord>>;
|
|
1153
1153
|
query: (indexName: string, input: VectorQueryInput) => Promise<VectorMatches>;
|
|
1154
1154
|
}
|
|
1155
1155
|
/**
|
|
1156
|
-
* Mutating vector surface on {@link MutationCtx} / {@link ActionCtx}. `upsert`
|
|
1157
|
-
* is queued post-commit by default; `upsertNow` forces a synchronous write.
|
|
1158
|
-
* `db.delete` on a vectorized table auto-propagates the matching `deleteByIds`.
|
|
1159
|
-
*/
|
|
1156
|
+
* Mutating vector surface on {@link MutationCtx} / {@link ActionCtx}. `upsert`
|
|
1157
|
+
* is queued post-commit by default; `upsertNow` forces a synchronous write.
|
|
1158
|
+
* `db.delete` on a vectorized table auto-propagates the matching `deleteByIds`.
|
|
1159
|
+
*/
|
|
1160
1160
|
interface VectorSearch extends VectorSearchReader {
|
|
1161
1161
|
deleteByIds: (indexName: string, ids: ReadonlyArray<string>) => Promise<void>;
|
|
1162
1162
|
upsert: (indexName: string, input: VectorUpsertInput) => Promise<void>;
|
|
1163
1163
|
upsertNow: (indexName: string, input: VectorUpsertInput) => Promise<void>;
|
|
1164
1164
|
}
|
|
1165
1165
|
/**
|
|
1166
|
-
* Structured
|
|
1167
|
-
*
|
|
1168
|
-
* `ObservabilitySink`'s `onLog`
|
|
1169
|
-
*
|
|
1170
|
-
*
|
|
1171
|
-
*
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
*
|
|
1176
|
-
*
|
|
1177
|
-
*
|
|
1178
|
-
*
|
|
1179
|
-
*
|
|
1180
|
-
*
|
|
1181
|
-
*
|
|
1182
|
-
|
|
1166
|
+
* Structured, filterable key/value fields attached to a log line — the second
|
|
1167
|
+
* argument of a `ctx.log.<level>(message, fields)` call, or the fields bound by
|
|
1168
|
+
* `ctx.log.with(fields)`. They travel to an `ObservabilitySink`'s `onLog` and,
|
|
1169
|
+
* for a network sink, become OTLP log-record attributes a log pipeline (or the
|
|
1170
|
+
* Cloud log viewer) can filter and index on. Primitive values pass through;
|
|
1171
|
+
* objects/arrays are JSON-encoded at the sink boundary.
|
|
1172
|
+
*/
|
|
1173
|
+
type LogFields = Record<string, unknown>;
|
|
1174
|
+
/**
|
|
1175
|
+
* One `ctx.log` severity method. Two call forms:
|
|
1176
|
+
*
|
|
1177
|
+
* - **Structured** — `ctx.log.info("order placed", { orderId, total })`: a
|
|
1178
|
+
* message string plus a `fields` object. The fields are indexed as attributes.
|
|
1179
|
+
* - **Console-style** — `ctx.log.info("state", value, other)`: any number of
|
|
1180
|
+
* values, joined into the display message exactly like `console.log`.
|
|
1181
|
+
*
|
|
1182
|
+
* The structured form is matched when the second argument is a plain object;
|
|
1183
|
+
* otherwise the call is treated as console-style, so existing `console`-shaped
|
|
1184
|
+
* calls keep working unchanged.
|
|
1185
|
+
*/
|
|
1186
|
+
interface LunoraLogMethod {
|
|
1187
|
+
(message: string, fields?: LogFields): void;
|
|
1188
|
+
(...args: unknown[]): void;
|
|
1189
|
+
}
|
|
1190
|
+
/**
|
|
1191
|
+
* Structured logger on every function `ctx`. Each call emits one attributed log
|
|
1192
|
+
* line — tagged with the function path on the server — that flows to an
|
|
1193
|
+
* `ObservabilitySink`'s `onLog` (where you route it in production) and, in
|
|
1194
|
+
* development, to the dev server terminal via the CLI / Vite plugin formatter.
|
|
1195
|
+
* Mirrors the `console` method names so it's a drop-in for `console.log` inside a
|
|
1196
|
+
* handler, but with attribution, structured fields, and a routable transport.
|
|
1197
|
+
*
|
|
1198
|
+
* Six severities spanning the OpenTelemetry ramp: `trace`, `debug`, `info` (and
|
|
1199
|
+
* its `log` alias), `warn`, `error`, `fatal`.
|
|
1200
|
+
*
|
|
1201
|
+
* Two ways to attach structured {@link LogFields}: pass them per call
|
|
1202
|
+
* (`ctx.log.info(message, fields)`) or bind them once with {@link with} for a
|
|
1203
|
+
* child logger that stamps every line. The rendered `message` and the structured
|
|
1204
|
+
* `fields` reach the dev terminal and the platform's Workers Logs; the raw,
|
|
1205
|
+
* un-rendered console-style arguments are preserved ONLY on the in-process
|
|
1206
|
+
* `onLog` sink (which you opt into and control).
|
|
1207
|
+
*
|
|
1208
|
+
* Attribution follows the dispatched function: a log emitted inside an internal
|
|
1209
|
+
* function invoked via `ctx.runQuery`/`runMutation`/`runAction` is attributed to
|
|
1210
|
+
* the outer request entrypoint, since the composed call reuses its context.
|
|
1211
|
+
*/
|
|
1183
1212
|
interface LunoraLogger {
|
|
1184
|
-
readonly debug:
|
|
1185
|
-
readonly error:
|
|
1186
|
-
readonly
|
|
1187
|
-
readonly
|
|
1188
|
-
readonly
|
|
1213
|
+
readonly debug: LunoraLogMethod;
|
|
1214
|
+
readonly error: LunoraLogMethod;
|
|
1215
|
+
readonly fatal: LunoraLogMethod;
|
|
1216
|
+
readonly info: LunoraLogMethod;
|
|
1217
|
+
readonly log: LunoraLogMethod;
|
|
1218
|
+
readonly trace: LunoraLogMethod;
|
|
1219
|
+
readonly warn: LunoraLogMethod;
|
|
1220
|
+
/**
|
|
1221
|
+
* Return a child logger that stamps `fields` onto every line it emits,
|
|
1222
|
+
* merged under any per-call fields (per-call wins on a key clash). Chainable
|
|
1223
|
+
* — `ctx.log.with({ requestId }).with({ step })` accumulates both. Use it to
|
|
1224
|
+
* bind request-scoped context once instead of repeating it per call.
|
|
1225
|
+
*/
|
|
1226
|
+
readonly with: (fields: LogFields) => LunoraLogger;
|
|
1189
1227
|
}
|
|
1190
1228
|
interface QueryCtx {
|
|
1191
1229
|
readonly auth: AuthState;
|
|
1192
1230
|
readonly db: DatabaseReader;
|
|
1193
1231
|
/**
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1232
|
+
* The validated, typed environment. Populated only when the project declares
|
|
1233
|
+
* a `defineEnv(...)` contract in `lunora/env.ts`; codegen then narrows this to
|
|
1234
|
+
* the validated `InferEnv` shape so `ctx.env.STRIPE_KEY` is parsed and
|
|
1235
|
+
* coercion-aware. Absent (optional) without a contract — declare
|
|
1236
|
+
* `lunora/env.ts` to populate and type it.
|
|
1237
|
+
*/
|
|
1200
1238
|
readonly env?: Record<string, unknown>;
|
|
1201
1239
|
/**
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1240
|
+
* The caller's IP for this request — Cloudflare's trusted `CF-Connecting-IP`,
|
|
1241
|
+
* forwarded server-side (never read from a client header). `undefined` when
|
|
1242
|
+
* unknown: a live-subscription re-run, a server-initiated dispatch, or
|
|
1243
|
+
* non-Cloudflare hosting. A convenient rate-limit key for anonymous traffic.
|
|
1244
|
+
*/
|
|
1207
1245
|
readonly ip?: string;
|
|
1208
1246
|
/** Structured, function-attributed logger; see {@link LunoraLogger}. */
|
|
1209
1247
|
readonly log: LunoraLogger;
|
|
1210
1248
|
/**
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1249
|
+
* Wall-clock time (epoch ms) the function began, captured once so the whole
|
|
1250
|
+
* handler sees a single stable value. Query/mutation handlers must be
|
|
1251
|
+
* deterministic — they may be re-run on OCC retry / subscription re-eval — so
|
|
1252
|
+
* read time through `ctx.now` instead of `Date.now()` (the latter is flagged
|
|
1253
|
+
* by the `nondeterministic_query_mutation` advisor). Actions may use `Date.now()`.
|
|
1254
|
+
*/
|
|
1217
1255
|
readonly now: number;
|
|
1218
1256
|
/**
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1257
|
+
* Compose a read-only subquery in-process, reusing this query's read
|
|
1258
|
+
* context (same transaction, same `db`). Executes the referenced query's
|
|
1259
|
+
* handler directly — no fresh DO RPC round-trip — so it observes the exact
|
|
1260
|
+
* same snapshot. A query may only call other queries; there is no
|
|
1261
|
+
* `runMutation` on a `QueryCtx` (writes are not allowed from a query).
|
|
1262
|
+
* Mirrors Convex's `ctx.runQuery`.
|
|
1263
|
+
*/
|
|
1226
1264
|
readonly runQuery: <A extends ArgsValidator, R>(reference: RegisteredQuery<A, R>, args: InferArgs<A>) => Promise<R>;
|
|
1227
1265
|
/** Read account-level secrets from Cloudflare Secrets Store; see {@link Secrets}. */
|
|
1228
1266
|
readonly secrets: Secrets;
|
|
@@ -1233,45 +1271,45 @@ interface MutationCtx {
|
|
|
1233
1271
|
readonly auth: AuthState;
|
|
1234
1272
|
readonly db: DatabaseWriter;
|
|
1235
1273
|
/**
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1274
|
+
* The validated, typed environment. Populated only when the project declares
|
|
1275
|
+
* a `defineEnv(...)` contract in `lunora/env.ts`; codegen then narrows this to
|
|
1276
|
+
* the validated `InferEnv` shape so `ctx.env.STRIPE_KEY` is parsed and
|
|
1277
|
+
* coercion-aware. Absent (optional) without a contract — declare
|
|
1278
|
+
* `lunora/env.ts` to populate and type it.
|
|
1279
|
+
*/
|
|
1242
1280
|
readonly env?: Record<string, unknown>;
|
|
1243
1281
|
/**
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1282
|
+
* The caller's IP for this request — Cloudflare's trusted `CF-Connecting-IP`,
|
|
1283
|
+
* forwarded server-side (never read from a client header). `undefined` when
|
|
1284
|
+
* unknown: a live-subscription re-run, a server-initiated dispatch, or
|
|
1285
|
+
* non-Cloudflare hosting. A convenient rate-limit key for anonymous traffic.
|
|
1286
|
+
*/
|
|
1249
1287
|
readonly ip?: string;
|
|
1250
1288
|
/** Structured, function-attributed logger; see {@link LunoraLogger}. */
|
|
1251
1289
|
readonly log: LunoraLogger;
|
|
1252
1290
|
/**
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1291
|
+
* Wall-clock time (epoch ms) the function began, captured once so the whole
|
|
1292
|
+
* handler sees a single stable value. Mutation handlers must be deterministic
|
|
1293
|
+
* — they may be re-run on OCC retry — so read time through `ctx.now` instead
|
|
1294
|
+
* of `Date.now()` (the latter is flagged by the `nondeterministic_query_mutation`
|
|
1295
|
+
* advisor). Actions may use `Date.now()`.
|
|
1296
|
+
*/
|
|
1259
1297
|
readonly now: number;
|
|
1260
1298
|
/**
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1299
|
+
* Compose a submutation in-process, reusing this mutation's `db` writer.
|
|
1300
|
+
* Executes the referenced mutation's handler directly — no fresh DO RPC —
|
|
1301
|
+
* so its writes apply through the same shard invocation as the enclosing
|
|
1302
|
+
* mutation. Note: writes are not wrapped in a SQL transaction, so a partial
|
|
1303
|
+
* failure does not roll back earlier writes (the same as a top-level
|
|
1304
|
+
* mutation). Mirrors Convex's `ctx.runMutation`.
|
|
1305
|
+
*/
|
|
1268
1306
|
readonly runMutation: <A extends ArgsValidator, R>(reference: RegisteredMutation<A, R>, args: InferArgs<A>) => Promise<R>;
|
|
1269
1307
|
/**
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1308
|
+
* Compose a read-only subquery in-process, reusing this mutation's `db`.
|
|
1309
|
+
* Executes the referenced query's handler directly — no fresh DO RPC — so
|
|
1310
|
+
* it observes this mutation's in-flight writes. Mirrors Convex's
|
|
1311
|
+
* `ctx.runQuery`.
|
|
1312
|
+
*/
|
|
1275
1313
|
readonly runQuery: <A extends ArgsValidator, R>(reference: RegisteredQuery<A, R>, args: InferArgs<A>) => Promise<R>;
|
|
1276
1314
|
readonly scheduler: Scheduler;
|
|
1277
1315
|
/** Read account-level secrets from Cloudflare Secrets Store; see {@link Secrets}. */
|
|
@@ -1284,36 +1322,36 @@ interface MutationCtx {
|
|
|
1284
1322
|
interface ActionCtx {
|
|
1285
1323
|
readonly auth: AuthState;
|
|
1286
1324
|
/**
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1325
|
+
* Programmatic Workers Cache purge; see {@link CachePurge}.
|
|
1326
|
+
* **Action-only** — actions run in the Worker, which has a `cache` binding.
|
|
1327
|
+
* Queries and mutations run inside the Durable Object and do not expose this.
|
|
1328
|
+
* Optional at runtime because Workers Cache is only present when enabled.
|
|
1329
|
+
*/
|
|
1292
1330
|
readonly cache?: CachePurge;
|
|
1293
1331
|
readonly db: DatabaseWriter;
|
|
1294
1332
|
/**
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1333
|
+
* The validated, typed environment. Populated only when the project declares
|
|
1334
|
+
* a `defineEnv(...)` contract in `lunora/env.ts`; codegen then narrows this to
|
|
1335
|
+
* the validated `InferEnv` shape so `ctx.env.STRIPE_KEY` is parsed and
|
|
1336
|
+
* coercion-aware. Absent (optional) without a contract — declare
|
|
1337
|
+
* `lunora/env.ts` to populate and type it.
|
|
1338
|
+
*/
|
|
1301
1339
|
readonly env?: Record<string, unknown>;
|
|
1302
1340
|
readonly fetch: typeof globalThis.fetch;
|
|
1303
1341
|
/**
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1342
|
+
* The caller's IP for this request — Cloudflare's trusted `CF-Connecting-IP`,
|
|
1343
|
+
* forwarded server-side (never read from a client header). `undefined` when
|
|
1344
|
+
* unknown: a live-subscription re-run, a server-initiated dispatch, or
|
|
1345
|
+
* non-Cloudflare hosting. A convenient rate-limit key for anonymous traffic.
|
|
1346
|
+
*/
|
|
1309
1347
|
readonly ip?: string;
|
|
1310
1348
|
/** Structured, function-attributed logger; see {@link LunoraLogger}. */
|
|
1311
1349
|
readonly log: LunoraLogger;
|
|
1312
1350
|
/**
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1351
|
+
* Wall-clock time (epoch ms) the action began, captured once for convenience
|
|
1352
|
+
* and parity with query/mutation `ctx.now`. Actions run exactly once, so they
|
|
1353
|
+
* may also use ambient `Date.now()` freely.
|
|
1354
|
+
*/
|
|
1317
1355
|
readonly now: number;
|
|
1318
1356
|
readonly runAction: <A extends ArgsValidator, R>(reference: RegisteredAction<A, R>, args: InferArgs<A>) => Promise<R>;
|
|
1319
1357
|
readonly runMutation: <A extends ArgsValidator, R>(reference: RegisteredMutation<A, R>, args: InferArgs<A>) => Promise<R>;
|
|
@@ -1327,9 +1365,9 @@ interface ActionCtx {
|
|
|
1327
1365
|
readonly workflows: Workflows;
|
|
1328
1366
|
}
|
|
1329
1367
|
/**
|
|
1330
|
-
* Stand-in returned by codegen so projects can `import { api } from "./_generated/api"`.
|
|
1331
|
-
* The runtime value is opaque; the types are filled in by generated declarations.
|
|
1332
|
-
*/
|
|
1368
|
+
* Stand-in returned by codegen so projects can `import { api } from "./_generated/api"`.
|
|
1369
|
+
* The runtime value is opaque; the types are filled in by generated declarations.
|
|
1370
|
+
*/
|
|
1333
1371
|
type AnyApi = Record<string, Record<string, RegisteredFunction<ArgsValidator, unknown, FunctionKind>>>;
|
|
1334
1372
|
declare const anyApi: AnyApi;
|
|
1335
|
-
export { type ActionCtx, type AggregateIndexDefinition, type AggregateOp, type AnyApi, type ArgsValidator, type AuthState, type CachePurge, type DatabaseReader, type DatabaseWriter, type DurableObjectJurisdiction, type ExternalSourceCursor, type ExternalSourceDefinition, type ExternalSourceMode, type ExternalSourceRefresh, type FunctionKind, type FunctionVisibility, type GlobalBackend, type IndexDefinition, type IndexRangeBuilder, type InferArgs, type LifecycleEvent, type LifecycleEventKind, type LunoraLogger, type MutationCtx, type OnDeleteAction, type PaginationOptions, type PaginationResult, type QueryCtx, type RankIndexDefinition, type RankSortKey, type ReadOnlyStorage, type RegisteredAction, type RegisteredFunction, type RegisteredLifecycleHook, type RegisteredMutation, type RegisteredQuery, type RegisteredStream, type RelationDefinition, type ScheduledFunctionDoc, type ScheduledJob, type Scheduler, type Schema, type SearchFilterBuilder, type SearchIndexDefinition, type Secrets, type SecretsStoreSecretLike, type ShardMode, type Storage, type StorageMetadata, type SystemDatabaseReader, type SystemDoc, type SystemQuery, type SystemTableName, type TableDefinition, type TableReader, type TableVectorIndex, type TriggerAggregateOptions, type TriggerBuilder, type TriggerCtx, type TriggerDatabase, type TriggerDefinition, type TriggerDeleteEvent, type TriggerEvent, type TriggerGroupByEntry, type TriggerGroupByOptions, type TriggerHandler, type TriggerInsertEvent, type TriggerOp, type TriggerQueryArgs, type TriggerQueryPage, type TriggerRankOptions, type TriggerRankPageOptions, type TriggerRankResult, type TriggerRow, type TriggerTiming, type TriggerUpdateEvent, type VectorEmbedder, type VectorIndexDefinition, type VectorMatch, type VectorMatches, type VectorMetric, type VectorQueryInput, type VectorRecord, type VectorSearch, type VectorSearchReader, type VectorUpsertInput, type WorkflowCreateOptions, type WorkflowHandle, type WorkflowInstance, type WorkflowInstanceStatus, type WorkflowStatusResult, type Workflows, type X402ProcedureConfig, anyApi };
|
|
1373
|
+
export { type ActionCtx, type AggregateIndexDefinition, type AggregateOp, type AnyApi, type ArgsValidator, type AuthState, type CachePurge, type DatabaseReader, type DatabaseWriter, type DurableObjectJurisdiction, type ExternalSourceCursor, type ExternalSourceDefinition, type ExternalSourceMode, type ExternalSourceRefresh, type FunctionKind, type FunctionVisibility, type GlobalBackend, type IndexDefinition, type IndexRangeBuilder, type InferArgs, type LifecycleEvent, type LifecycleEventKind, type LogFields, type LunoraLogMethod, type LunoraLogger, type MutationCtx, type OnDeleteAction, type PaginationOptions, type PaginationResult, type QueryCtx, type RankIndexDefinition, type RankSortKey, type ReadOnlyStorage, type RegisteredAction, type RegisteredFunction, type RegisteredLifecycleHook, type RegisteredMutation, type RegisteredQuery, type RegisteredStream, type RelationDefinition, type ScheduledFunctionDoc, type ScheduledJob, type Scheduler, type Schema, type SearchFilterBuilder, type SearchIndexDefinition, type Secrets, type SecretsStoreSecretLike, type ShardMode, type Storage, type StorageMetadata, type SystemDatabaseReader, type SystemDoc, type SystemQuery, type SystemTableName, type TableDefinition, type TableReader, type TableVectorIndex, type TriggerAggregateOptions, type TriggerBuilder, type TriggerCtx, type TriggerDatabase, type TriggerDefinition, type TriggerDeleteEvent, type TriggerEvent, type TriggerGroupByEntry, type TriggerGroupByOptions, type TriggerHandler, type TriggerInsertEvent, type TriggerOp, type TriggerQueryArgs, type TriggerQueryPage, type TriggerRankOptions, type TriggerRankPageOptions, type TriggerRankResult, type TriggerRow, type TriggerTiming, type TriggerUpdateEvent, type VectorEmbedder, type VectorIndexDefinition, type VectorMatch, type VectorMatches, type VectorMetric, type VectorQueryInput, type VectorRecord, type VectorSearch, type VectorSearchReader, type VectorUpsertInput, type WorkflowCreateOptions, type WorkflowHandle, type WorkflowInstance, type WorkflowInstanceStatus, type WorkflowStatusResult, type Workflows, type X402ProcedureConfig, anyApi };
|