@lunora/server 1.0.0-alpha.17 → 1.0.0-alpha.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -0
- package/dist/data-model.d.mts +55 -17
- package/dist/data-model.d.ts +55 -17
- package/dist/index.d.mts +109 -20
- package/dist/index.d.ts +109 -20
- package/dist/index.mjs +5 -5
- package/dist/packem_shared/{bindOrm-CNXKgfUa.mjs → bindOrm-Bfgl7f-Q.mjs} +34 -18
- package/dist/packem_shared/{buildRlsReadRegistry-DFBFLydB.mjs → buildRlsReadRegistry-D54vUQe4.mjs} +1 -1
- package/dist/packem_shared/{httpAction-C_L82dxR.mjs → httpAction-DCXoYPIk.mjs} +31 -11
- package/dist/packem_shared/{mask-Dc8G5Gl7.mjs → mask-BT1YgRbF.mjs} +1 -1
- package/dist/packem_shared/{rls-3sCJIH6F.mjs → rls-B_ZWgslr.mjs} +24 -1
- package/dist/rls/testing.mjs +1 -1
- package/dist/types.d.mts +75 -4
- package/dist/types.d.ts +75 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -99,6 +99,52 @@ The builder chain is `<builder>.input(validators).<kind>(handler)`, plus `.use(m
|
|
|
99
99
|
|
|
100
100
|
> This README covers the basics. For the full API, options, and guides, see the **[documentation](https://lunora.sh/docs/packages/server)**.
|
|
101
101
|
|
|
102
|
+
### Caching with Workers Cache
|
|
103
|
+
|
|
104
|
+
Lunora supports Cloudflare Workers Cache for HTTP actions (`httpRoute`). RPC queries and mutations are `POST /_lunora/rpc` and are not cacheable at the edge by design.
|
|
105
|
+
|
|
106
|
+
**Enable Workers Cache** in `wrangler.jsonc`:
|
|
107
|
+
|
|
108
|
+
```jsonc
|
|
109
|
+
{
|
|
110
|
+
"cache": { "enabled": true },
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The dev server and CLI automatically bump `compatibility_date` to the minimum required when cache is enabled — you do not need to set it manually.
|
|
115
|
+
|
|
116
|
+
**Set cache headers declaratively** on an `httpRoute`:
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
import { httpRoute } from "./_generated/server";
|
|
120
|
+
|
|
121
|
+
export const getProduct = httpRoute
|
|
122
|
+
.get("/api/products/:id")
|
|
123
|
+
.params({ id: v.string() })
|
|
124
|
+
.cacheControl("public, max-age=300, stale-while-revalidate=3600")
|
|
125
|
+
.cacheTag("products")
|
|
126
|
+
.handler(async ({ ctx, params }) => {
|
|
127
|
+
return { id: params.id, name: "Widget" };
|
|
128
|
+
});
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**Purge cache by tag** from an action handler:
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
import { action } from "./_generated/server";
|
|
135
|
+
|
|
136
|
+
export const refreshProducts = action.action(async ({ ctx }) => {
|
|
137
|
+
if (!ctx.cache) {
|
|
138
|
+
throw new Error("Workers Cache is not enabled in wrangler.jsonc");
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
await ctx.cache.purge({ tags: ["products"] });
|
|
142
|
+
return { ok: true };
|
|
143
|
+
});
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
The `ctx.cache.purge` API accepts `{ tags?: string[]; purgeEverything?: boolean }`. Only action handlers expose `ctx.cache`; queries and mutations run inside the Durable Object and do not have access to the Worker-level cache binding.
|
|
147
|
+
|
|
102
148
|
## Related
|
|
103
149
|
|
|
104
150
|
- [`@lunora/values`](https://www.npmjs.com/package/@lunora/values) — the `v.*` validators re-exported here.
|
package/dist/data-model.d.mts
CHANGED
|
@@ -345,12 +345,24 @@ interface TableWriterFacade<DM, IM extends Record<keyof DM, object>, REL extends
|
|
|
345
345
|
* {@link TableWriterFacade.hardDelete} to force physical removal.
|
|
346
346
|
*/
|
|
347
347
|
delete: (id: Id<string & T>) => Promise<void>;
|
|
348
|
-
/**
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
348
|
+
/**
|
|
349
|
+
* Delete many rows in this table. Pass an array of ids (requested count is
|
|
350
|
+
* returned; unknown ids are no-ops) or `{ where }` to delete matching rows
|
|
351
|
+
* (actual removed count is returned). Atomic within a mutation.
|
|
352
|
+
*/
|
|
353
|
+
deleteMany: {
|
|
354
|
+
(ids: ReadonlyArray<Id<string & T>>, options?: {
|
|
355
|
+
limit?: number;
|
|
356
|
+
}): Promise<{
|
|
357
|
+
deleted: number;
|
|
358
|
+
}>;
|
|
359
|
+
(args: {
|
|
360
|
+
limit?: number;
|
|
361
|
+
where: Partial<DM[T]>;
|
|
362
|
+
}): Promise<{
|
|
363
|
+
deleted: number;
|
|
364
|
+
}>;
|
|
365
|
+
};
|
|
354
366
|
/** Physically remove a row (and physically cascade `onDelete`), bypassing `.softDelete()`. Same as `delete()` on a non-soft table. */
|
|
355
367
|
hardDelete: (id: Id<string & T>) => Promise<void>;
|
|
356
368
|
/**
|
|
@@ -366,18 +378,44 @@ interface TableWriterFacade<DM, IM extends Record<keyof DM, object>, REL extends
|
|
|
366
378
|
skipDuplicates?: boolean;
|
|
367
379
|
}): Promise<Id<string & T>>;
|
|
368
380
|
};
|
|
369
|
-
/**
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
381
|
+
/**
|
|
382
|
+
* Insert many documents into this table in one call, returning the minted ids
|
|
383
|
+
* in input order. With `{ skipDuplicates: true }`, UNIQUE breaches resolve to
|
|
384
|
+
* `null` for that row instead of failing the batch. Atomic within a mutation.
|
|
385
|
+
*/
|
|
386
|
+
insertMany: {
|
|
387
|
+
(values: ReadonlyArray<IM[T]>, options: {
|
|
388
|
+
limit?: number;
|
|
389
|
+
skipDuplicates: true;
|
|
390
|
+
}): Promise<(Id<string & T> | null)[]>;
|
|
391
|
+
(values: ReadonlyArray<IM[T]>, options?: {
|
|
392
|
+
limit?: number;
|
|
393
|
+
skipDuplicates?: boolean;
|
|
394
|
+
}): Promise<Id<string & T>[]>;
|
|
395
|
+
};
|
|
373
396
|
patch: (id: Id<string & T>, values: Partial<IM[T]>) => Promise<void>;
|
|
374
|
-
/**
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
397
|
+
/**
|
|
398
|
+
* Patch many rows in this table. Pass an array of `{ id, values }` or
|
|
399
|
+
* `{ where, values }` to patch matching rows with the same values. Returns
|
|
400
|
+
* the actual patched count. Atomic within a mutation.
|
|
401
|
+
*/
|
|
402
|
+
patchMany: {
|
|
403
|
+
(patches: ReadonlyArray<{
|
|
404
|
+
id: Id<string & T>;
|
|
405
|
+
values: Partial<IM[T]>;
|
|
406
|
+
}>, options?: {
|
|
407
|
+
limit?: number;
|
|
408
|
+
}): Promise<{
|
|
409
|
+
patched: number;
|
|
410
|
+
}>;
|
|
411
|
+
(args: {
|
|
412
|
+
limit?: number;
|
|
413
|
+
values: Partial<IM[T]>;
|
|
414
|
+
where: Partial<DM[T]>;
|
|
415
|
+
}): Promise<{
|
|
416
|
+
patched: number;
|
|
417
|
+
}>;
|
|
418
|
+
};
|
|
381
419
|
replace: (id: Id<string & T>, values: IM[T]) => Promise<void>;
|
|
382
420
|
/** Un-soft-delete a row by id: clears the `.softDelete()` marker so list reads see it again. Throws on a non-soft table. */
|
|
383
421
|
restore: (id: Id<string & T>) => Promise<void>;
|
package/dist/data-model.d.ts
CHANGED
|
@@ -345,12 +345,24 @@ interface TableWriterFacade<DM, IM extends Record<keyof DM, object>, REL extends
|
|
|
345
345
|
* {@link TableWriterFacade.hardDelete} to force physical removal.
|
|
346
346
|
*/
|
|
347
347
|
delete: (id: Id<string & T>) => Promise<void>;
|
|
348
|
-
/**
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
348
|
+
/**
|
|
349
|
+
* Delete many rows in this table. Pass an array of ids (requested count is
|
|
350
|
+
* returned; unknown ids are no-ops) or `{ where }` to delete matching rows
|
|
351
|
+
* (actual removed count is returned). Atomic within a mutation.
|
|
352
|
+
*/
|
|
353
|
+
deleteMany: {
|
|
354
|
+
(ids: ReadonlyArray<Id<string & T>>, options?: {
|
|
355
|
+
limit?: number;
|
|
356
|
+
}): Promise<{
|
|
357
|
+
deleted: number;
|
|
358
|
+
}>;
|
|
359
|
+
(args: {
|
|
360
|
+
limit?: number;
|
|
361
|
+
where: Partial<DM[T]>;
|
|
362
|
+
}): Promise<{
|
|
363
|
+
deleted: number;
|
|
364
|
+
}>;
|
|
365
|
+
};
|
|
354
366
|
/** Physically remove a row (and physically cascade `onDelete`), bypassing `.softDelete()`. Same as `delete()` on a non-soft table. */
|
|
355
367
|
hardDelete: (id: Id<string & T>) => Promise<void>;
|
|
356
368
|
/**
|
|
@@ -366,18 +378,44 @@ interface TableWriterFacade<DM, IM extends Record<keyof DM, object>, REL extends
|
|
|
366
378
|
skipDuplicates?: boolean;
|
|
367
379
|
}): Promise<Id<string & T>>;
|
|
368
380
|
};
|
|
369
|
-
/**
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
381
|
+
/**
|
|
382
|
+
* Insert many documents into this table in one call, returning the minted ids
|
|
383
|
+
* in input order. With `{ skipDuplicates: true }`, UNIQUE breaches resolve to
|
|
384
|
+
* `null` for that row instead of failing the batch. Atomic within a mutation.
|
|
385
|
+
*/
|
|
386
|
+
insertMany: {
|
|
387
|
+
(values: ReadonlyArray<IM[T]>, options: {
|
|
388
|
+
limit?: number;
|
|
389
|
+
skipDuplicates: true;
|
|
390
|
+
}): Promise<(Id<string & T> | null)[]>;
|
|
391
|
+
(values: ReadonlyArray<IM[T]>, options?: {
|
|
392
|
+
limit?: number;
|
|
393
|
+
skipDuplicates?: boolean;
|
|
394
|
+
}): Promise<Id<string & T>[]>;
|
|
395
|
+
};
|
|
373
396
|
patch: (id: Id<string & T>, values: Partial<IM[T]>) => Promise<void>;
|
|
374
|
-
/**
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
397
|
+
/**
|
|
398
|
+
* Patch many rows in this table. Pass an array of `{ id, values }` or
|
|
399
|
+
* `{ where, values }` to patch matching rows with the same values. Returns
|
|
400
|
+
* the actual patched count. Atomic within a mutation.
|
|
401
|
+
*/
|
|
402
|
+
patchMany: {
|
|
403
|
+
(patches: ReadonlyArray<{
|
|
404
|
+
id: Id<string & T>;
|
|
405
|
+
values: Partial<IM[T]>;
|
|
406
|
+
}>, options?: {
|
|
407
|
+
limit?: number;
|
|
408
|
+
}): Promise<{
|
|
409
|
+
patched: number;
|
|
410
|
+
}>;
|
|
411
|
+
(args: {
|
|
412
|
+
limit?: number;
|
|
413
|
+
values: Partial<IM[T]>;
|
|
414
|
+
where: Partial<DM[T]>;
|
|
415
|
+
}): Promise<{
|
|
416
|
+
patched: number;
|
|
417
|
+
}>;
|
|
418
|
+
};
|
|
381
419
|
replace: (id: Id<string & T>, values: IM[T]) => Promise<void>;
|
|
382
420
|
/** Un-soft-delete a row by id: clears the `.softDelete()` marker so list reads see it again. Throws on a non-soft table. */
|
|
383
421
|
restore: (id: Id<string & T>) => Promise<void>;
|
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Validator, Infer, ValidatorMap, InferValidatorMap, v } from '@lunora/values';
|
|
2
2
|
export { type ColumnValidator, type Id, type Infer, ValidationError, type Validator, type ValidatorKind, v } from '@lunora/values';
|
|
3
3
|
import { ArgsValidator, InferArgs, RegisteredAction, ActionCtx, MutationCtx, RegisteredMutation, QueryCtx, RegisteredQuery, RegisteredStream, FunctionKind, Secrets, LifecycleEvent, RegisteredLifecycleHook, TableDefinition, RegisteredFunction, VectorIndexDefinition, Schema, AggregateOp, DurableObjectJurisdiction, RelationDefinition, GlobalBackend, OnDeleteAction, ExternalSourceDefinition, TriggerBuilder, TriggerDefinition, VectorEmbedder, VectorMetric, AggregateIndexDefinition, RankIndexDefinition } from "./types.mjs";
|
|
4
|
-
export { type AnyApi, type AuthState, type DatabaseReader, type DatabaseWriter, type FunctionVisibility, type IndexDefinition, type IndexRangeBuilder, type LifecycleEventKind, type LunoraLogger, type PaginationOptions, type PaginationResult, type RankSortKey, type ReadOnlyStorage, type ScheduledFunctionDoc, type ScheduledJob, type Scheduler, type SearchFilterBuilder, type SearchIndexDefinition, type ShardMode, type Storage, type StorageMetadata, type SystemDatabaseReader, type SystemDoc, type SystemQuery, type SystemTableName, type TableReader, type TableVectorIndex, type TriggerAggregateOptions, type TriggerCtx, type TriggerDatabase, 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 VectorMatch, type VectorMatches, type VectorQueryInput, type VectorRecord, type VectorSearch, type VectorSearchReader, type VectorUpsertInput, type WorkflowCreateOptions, type WorkflowHandle, type WorkflowInstance, type WorkflowInstanceStatus, type WorkflowStatusResult, type Workflows, anyApi } from "./types.mjs";
|
|
4
|
+
export { type AnyApi, type AuthState, type CachePurge, type DatabaseReader, type DatabaseWriter, type FunctionVisibility, type IndexDefinition, type IndexRangeBuilder, type LifecycleEventKind, type LunoraLogger, type PaginationOptions, type PaginationResult, type RankSortKey, type ReadOnlyStorage, type ScheduledFunctionDoc, type ScheduledJob, type Scheduler, type SearchFilterBuilder, type SearchIndexDefinition, type ShardMode, type Storage, type StorageMetadata, type SystemDatabaseReader, type SystemDoc, type SystemQuery, type SystemTableName, type TableReader, type TableVectorIndex, type TriggerAggregateOptions, type TriggerCtx, type TriggerDatabase, 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 VectorMatch, type VectorMatches, type VectorQueryInput, type VectorRecord, type VectorSearch, type VectorSearchReader, type VectorUpsertInput, type WorkflowCreateOptions, type WorkflowHandle, type WorkflowInstance, type WorkflowInstanceStatus, type WorkflowStatusResult, type Workflows, anyApi } from "./types.mjs";
|
|
5
5
|
import { LunoraError as LunoraError$1, LunoraErrorCode } from '@lunora/errors';
|
|
6
6
|
export type { LunoraErrorCode } from '@lunora/errors';
|
|
7
7
|
import { Context, Hono } from 'hono';
|
|
@@ -299,6 +299,11 @@ interface FacadeWriterLike {
|
|
|
299
299
|
}, expectedTable?: string): Promise<{
|
|
300
300
|
deleted: number;
|
|
301
301
|
}>;
|
|
302
|
+
deleteWhere?(tableName: string, where: Record<string, unknown>, options?: {
|
|
303
|
+
limit?: number;
|
|
304
|
+
}): Promise<{
|
|
305
|
+
deleted: number;
|
|
306
|
+
}>;
|
|
302
307
|
findFirst(tableName: string, args?: unknown): Promise<unknown>;
|
|
303
308
|
findFirstOrThrow(tableName: string, args?: unknown): Promise<unknown>;
|
|
304
309
|
findMany(tableName: string, args?: unknown): Promise<unknown>;
|
|
@@ -307,14 +312,25 @@ interface FacadeWriterLike {
|
|
|
307
312
|
insert(tableName: string, document: Record<string, unknown>): Promise<string>;
|
|
308
313
|
insertMany?(tableName: string, documents: ReadonlyArray<Record<string, unknown>>, options?: {
|
|
309
314
|
limit?: number;
|
|
310
|
-
|
|
315
|
+
skipDuplicates?: boolean;
|
|
316
|
+
}): Promise<(string | null)[]>;
|
|
311
317
|
patch(id: string, patch: Record<string, unknown>, expectedTable?: string): Promise<void>;
|
|
312
318
|
patchMany?(patches: ReadonlyArray<{
|
|
313
319
|
id: string;
|
|
314
320
|
patch: Record<string, unknown>;
|
|
315
321
|
}>, options?: {
|
|
316
322
|
limit?: number;
|
|
317
|
-
}, expectedTable?: string): Promise<
|
|
323
|
+
}, expectedTable?: string): Promise<{
|
|
324
|
+
patched: number;
|
|
325
|
+
}>;
|
|
326
|
+
patchWhere?(tableName: string, args: {
|
|
327
|
+
patch: Record<string, unknown>;
|
|
328
|
+
where: Record<string, unknown>;
|
|
329
|
+
}, options?: {
|
|
330
|
+
limit?: number;
|
|
331
|
+
}): Promise<{
|
|
332
|
+
patched: number;
|
|
333
|
+
}>;
|
|
318
334
|
query(tableName: string): {
|
|
319
335
|
withSearchIndex(indexName: string, search: (q: unknown) => unknown): unknown;
|
|
320
336
|
};
|
|
@@ -328,11 +344,19 @@ interface FacadeEntry {
|
|
|
328
344
|
aggregate: (options: unknown) => Promise<unknown>;
|
|
329
345
|
count: (where?: unknown) => Promise<number>;
|
|
330
346
|
delete: (id: string) => Promise<void>;
|
|
331
|
-
deleteMany:
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
347
|
+
deleteMany: {
|
|
348
|
+
(ids: ReadonlyArray<string>, options?: {
|
|
349
|
+
limit?: number;
|
|
350
|
+
}): Promise<{
|
|
351
|
+
deleted: number;
|
|
352
|
+
}>;
|
|
353
|
+
(args: {
|
|
354
|
+
limit?: number;
|
|
355
|
+
where: Record<string, unknown>;
|
|
356
|
+
}): Promise<{
|
|
357
|
+
deleted: number;
|
|
358
|
+
}>;
|
|
359
|
+
};
|
|
336
360
|
/** `true` when at least one row matches `where` (or any row exists when omitted). Honors RLS like `findFirst`. */
|
|
337
361
|
exists: (where?: unknown) => Promise<boolean>;
|
|
338
362
|
findFirst: (args?: unknown) => Promise<unknown>;
|
|
@@ -343,16 +367,34 @@ interface FacadeEntry {
|
|
|
343
367
|
/** Physically remove a row (and physically cascade), bypassing `.softDelete()`. */
|
|
344
368
|
hardDelete: (id: string) => Promise<void>;
|
|
345
369
|
insert: (document: Record<string, unknown>, options?: FacadeInsertOptions) => Promise<null | string>;
|
|
370
|
+
/**
|
|
371
|
+
* Insert many documents into this table in one call. With
|
|
372
|
+
* `{ skipDuplicates: true }`, UNIQUE breaches resolve to `null` for that row
|
|
373
|
+
* instead of failing the batch. The typed facade narrows the return to
|
|
374
|
+
* `Id<T>[]` when skipDuplicates is not requested.
|
|
375
|
+
*/
|
|
346
376
|
insertMany: (documents: ReadonlyArray<Record<string, unknown>>, options?: {
|
|
347
377
|
limit?: number;
|
|
348
|
-
|
|
378
|
+
skipDuplicates?: boolean;
|
|
379
|
+
}) => Promise<(string | null)[]>;
|
|
349
380
|
patch: (id: string, patch: Record<string, unknown>) => Promise<void>;
|
|
350
|
-
patchMany:
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
381
|
+
patchMany: {
|
|
382
|
+
(patches: ReadonlyArray<{
|
|
383
|
+
id: string;
|
|
384
|
+
values: Record<string, unknown>;
|
|
385
|
+
}>, options?: {
|
|
386
|
+
limit?: number;
|
|
387
|
+
}): Promise<{
|
|
388
|
+
patched: number;
|
|
389
|
+
}>;
|
|
390
|
+
(args: {
|
|
391
|
+
limit?: number;
|
|
392
|
+
values: Record<string, unknown>;
|
|
393
|
+
where: Record<string, unknown>;
|
|
394
|
+
}): Promise<{
|
|
395
|
+
patched: number;
|
|
396
|
+
}>;
|
|
397
|
+
};
|
|
356
398
|
rank: (indexName: string, options: unknown) => Promise<unknown>;
|
|
357
399
|
rankPage: (indexName: string, options?: unknown) => Promise<unknown>;
|
|
358
400
|
replace: (id: string, document: Record<string, unknown>) => Promise<void>;
|
|
@@ -435,7 +477,7 @@ type HttpMethod = "DELETE" | "GET" | "HEAD" | "OPTIONS" | "PATCH" | "POST" | "PU
|
|
|
435
477
|
* `storage` surface — reach the data layer through `runQuery` / `runMutation` /
|
|
436
478
|
* `runAction`, which forward to the owning shard.
|
|
437
479
|
*/
|
|
438
|
-
type HttpActionCtx = Pick<ActionCtx, "auth" | "fetch" | "runAction" | "runMutation" | "runQuery">;
|
|
480
|
+
type HttpActionCtx = Pick<ActionCtx, "auth" | "cache" | "fetch" | "runAction" | "runMutation" | "runQuery">;
|
|
439
481
|
/** A raw handler wrapped by {@link httpAction}. Receives the raw request, returns the raw response. */
|
|
440
482
|
type HttpActionHandler = (context: HttpActionCtx, request: Request) => Promise<Response> | Response;
|
|
441
483
|
/**
|
|
@@ -519,6 +561,16 @@ interface HttpStreamHandlerOptions<SearchParams extends ArgsValidator, Params ex
|
|
|
519
561
|
*/
|
|
520
562
|
interface HttpRouteBuilder<SearchParams extends ArgsValidator, Body extends ArgsValidator, Params extends ArgsValidator, Output = undefined> {
|
|
521
563
|
body: <B extends ArgsValidator>(validators: B) => HttpRouteBuilder<SearchParams, B & Body, Params, Output>;
|
|
564
|
+
/**
|
|
565
|
+
* Attach a `Cache-Control` header to the response. Only meaningful when
|
|
566
|
+
* Workers Cache is enabled in `wrangler.jsonc` (`"cache": { "enabled": true }`).
|
|
567
|
+
*/
|
|
568
|
+
cacheControl: (value: string) => HttpRouteBuilder<SearchParams, Body, Params, Output>;
|
|
569
|
+
/**
|
|
570
|
+
* Attach a `Cache-Tag` header to the response for tag-based purging via
|
|
571
|
+
* `ctx.cache.purge({ tags: [...] })`.
|
|
572
|
+
*/
|
|
573
|
+
cacheTag: (value: string) => HttpRouteBuilder<SearchParams, Body, Params, Output>;
|
|
522
574
|
handler: [Output] extends [undefined] ? <R>(handler: (options: HttpRouteHandlerOptions<SearchParams, Body, Params>) => Promise<R> | R) => LunoraRouteHandler : (handler: (options: HttpRouteHandlerOptions<SearchParams, Body, Params>) => Output | Promise<Output>) => LunoraRouteHandler;
|
|
523
575
|
output: <V extends Validator>(validator: V) => HttpRouteBuilder<SearchParams, Body, Params, Infer<V>>;
|
|
524
576
|
params: <P extends ArgsValidator>(validators: P) => HttpRouteBuilder<SearchParams, Body, P & Params, Output>;
|
|
@@ -533,6 +585,11 @@ interface HttpRouteBuilder<SearchParams extends ArgsValidator, Body extends Args
|
|
|
533
585
|
* handler's yielded type.
|
|
534
586
|
*/
|
|
535
587
|
stream: <R>(handler: (options: HttpStreamHandlerOptions<SearchParams, Params>) => AsyncGenerator<R, void, void> | AsyncIterable<R>) => LunoraRouteHandler;
|
|
588
|
+
/**
|
|
589
|
+
* Attach a `Vary` header to the response so Cloudflare stores separate
|
|
590
|
+
* cached variants per distinct value of the listed request headers.
|
|
591
|
+
*/
|
|
592
|
+
vary: (value: string) => HttpRouteBuilder<SearchParams, Body, Params, Output>;
|
|
536
593
|
}
|
|
537
594
|
/** Opens a fresh {@link HttpRouteBuilder}. The `path` documents intent; hono owns the actual routing at mount. */
|
|
538
595
|
type HttpRouteFactory = (path: string) => HttpRouteBuilder<EmptyArgs, EmptyArgs, EmptyArgs>;
|
|
@@ -828,6 +885,11 @@ interface MaskDatabase {
|
|
|
828
885
|
}) => Promise<{
|
|
829
886
|
deleted: number;
|
|
830
887
|
}>;
|
|
888
|
+
deleteWhere?: (tableName: string, where: Record<string, unknown>, options?: {
|
|
889
|
+
limit?: number;
|
|
890
|
+
}) => Promise<{
|
|
891
|
+
deleted: number;
|
|
892
|
+
}>;
|
|
831
893
|
findFirst: (tableName: string, args?: QueryArgs$1) => Promise<Record<string, unknown> | null>;
|
|
832
894
|
findFirstOrThrow: (tableName: string, args?: QueryArgs$1) => Promise<Record<string, unknown>>;
|
|
833
895
|
findMany: (tableName: string, args?: QueryArgs$1) => Promise<QueryPage$1>;
|
|
@@ -839,7 +901,8 @@ interface MaskDatabase {
|
|
|
839
901
|
insert: (tableName: string, document: Record<string, unknown>) => Promise<string>;
|
|
840
902
|
insertMany: (tableName: string, documents: ReadonlyArray<Record<string, unknown>>, options?: {
|
|
841
903
|
limit?: number;
|
|
842
|
-
|
|
904
|
+
skipDuplicates?: boolean;
|
|
905
|
+
}) => Promise<(string | null)[]>;
|
|
843
906
|
lookupById?: (id: string, expectedTable?: string) => Promise<null | {
|
|
844
907
|
row: Record<string, unknown>;
|
|
845
908
|
tableName: string;
|
|
@@ -850,7 +913,17 @@ interface MaskDatabase {
|
|
|
850
913
|
patch: Record<string, unknown>;
|
|
851
914
|
}>, options?: {
|
|
852
915
|
limit?: number;
|
|
853
|
-
}) => Promise<
|
|
916
|
+
}) => Promise<{
|
|
917
|
+
patched: number;
|
|
918
|
+
}>;
|
|
919
|
+
patchWhere?: (tableName: string, args: {
|
|
920
|
+
patch: Record<string, unknown>;
|
|
921
|
+
where: Record<string, unknown>;
|
|
922
|
+
}, options?: {
|
|
923
|
+
limit?: number;
|
|
924
|
+
}) => Promise<{
|
|
925
|
+
patched: number;
|
|
926
|
+
}>;
|
|
854
927
|
query: (tableName: string) => TableReaderLike$1;
|
|
855
928
|
rank: (tableName: string, indexName: string, options: unknown) => Promise<null | {
|
|
856
929
|
position: number;
|
|
@@ -1722,6 +1795,11 @@ interface DatabaseWriterLike {
|
|
|
1722
1795
|
}, expectedTable?: string) => Promise<{
|
|
1723
1796
|
deleted: number;
|
|
1724
1797
|
}>;
|
|
1798
|
+
deleteWhere?: (tableName: string, where: WhereInput, options?: {
|
|
1799
|
+
limit?: number;
|
|
1800
|
+
}) => Promise<{
|
|
1801
|
+
deleted: number;
|
|
1802
|
+
}>;
|
|
1725
1803
|
findFirst: (tableName: string, args?: QueryArgs) => Promise<Record<string, unknown> | null>;
|
|
1726
1804
|
findFirstOrThrow: (tableName: string, args?: QueryArgs) => Promise<Record<string, unknown>>;
|
|
1727
1805
|
findMany: (tableName: string, args?: QueryArgs) => Promise<QueryPage>;
|
|
@@ -1738,7 +1816,8 @@ interface DatabaseWriterLike {
|
|
|
1738
1816
|
insert: (tableName: string, document: Record<string, unknown>) => Promise<string>;
|
|
1739
1817
|
insertMany: (tableName: string, documents: ReadonlyArray<Record<string, unknown>>, options?: {
|
|
1740
1818
|
limit?: number;
|
|
1741
|
-
|
|
1819
|
+
skipDuplicates?: boolean;
|
|
1820
|
+
}) => Promise<(string | null)[]>;
|
|
1742
1821
|
insertManyUnsafe: (tableName: string, documents: ReadonlyArray<Record<string, unknown>>, options?: {
|
|
1743
1822
|
allowExplicitId?: boolean;
|
|
1744
1823
|
limit?: number;
|
|
@@ -1761,7 +1840,17 @@ interface DatabaseWriterLike {
|
|
|
1761
1840
|
patch: Record<string, unknown>;
|
|
1762
1841
|
}>, options?: {
|
|
1763
1842
|
limit?: number;
|
|
1764
|
-
}, expectedTable?: string) => Promise<
|
|
1843
|
+
}, expectedTable?: string) => Promise<{
|
|
1844
|
+
patched: number;
|
|
1845
|
+
}>;
|
|
1846
|
+
patchWhere?: (tableName: string, args: {
|
|
1847
|
+
patch: Record<string, unknown>;
|
|
1848
|
+
where: WhereInput;
|
|
1849
|
+
}, options?: {
|
|
1850
|
+
limit?: number;
|
|
1851
|
+
}) => Promise<{
|
|
1852
|
+
patched: number;
|
|
1853
|
+
}>;
|
|
1765
1854
|
query: (tableName: string) => TableReaderLike;
|
|
1766
1855
|
/**
|
|
1767
1856
|
* Rank a row within its partition. A position is a count-of-rows-before, so
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Validator, Infer, ValidatorMap, InferValidatorMap, v } from '@lunora/values';
|
|
2
2
|
export { type ColumnValidator, type Id, type Infer, ValidationError, type Validator, type ValidatorKind, v } from '@lunora/values';
|
|
3
3
|
import { ArgsValidator, InferArgs, RegisteredAction, ActionCtx, MutationCtx, RegisteredMutation, QueryCtx, RegisteredQuery, RegisteredStream, FunctionKind, Secrets, LifecycleEvent, RegisteredLifecycleHook, TableDefinition, RegisteredFunction, VectorIndexDefinition, Schema, AggregateOp, DurableObjectJurisdiction, RelationDefinition, GlobalBackend, OnDeleteAction, ExternalSourceDefinition, TriggerBuilder, TriggerDefinition, VectorEmbedder, VectorMetric, AggregateIndexDefinition, RankIndexDefinition } from "./types.js";
|
|
4
|
-
export { type AnyApi, type AuthState, type DatabaseReader, type DatabaseWriter, type FunctionVisibility, type IndexDefinition, type IndexRangeBuilder, type LifecycleEventKind, type LunoraLogger, type PaginationOptions, type PaginationResult, type RankSortKey, type ReadOnlyStorage, type ScheduledFunctionDoc, type ScheduledJob, type Scheduler, type SearchFilterBuilder, type SearchIndexDefinition, type ShardMode, type Storage, type StorageMetadata, type SystemDatabaseReader, type SystemDoc, type SystemQuery, type SystemTableName, type TableReader, type TableVectorIndex, type TriggerAggregateOptions, type TriggerCtx, type TriggerDatabase, 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 VectorMatch, type VectorMatches, type VectorQueryInput, type VectorRecord, type VectorSearch, type VectorSearchReader, type VectorUpsertInput, type WorkflowCreateOptions, type WorkflowHandle, type WorkflowInstance, type WorkflowInstanceStatus, type WorkflowStatusResult, type Workflows, anyApi } from "./types.js";
|
|
4
|
+
export { type AnyApi, type AuthState, type CachePurge, type DatabaseReader, type DatabaseWriter, type FunctionVisibility, type IndexDefinition, type IndexRangeBuilder, type LifecycleEventKind, type LunoraLogger, type PaginationOptions, type PaginationResult, type RankSortKey, type ReadOnlyStorage, type ScheduledFunctionDoc, type ScheduledJob, type Scheduler, type SearchFilterBuilder, type SearchIndexDefinition, type ShardMode, type Storage, type StorageMetadata, type SystemDatabaseReader, type SystemDoc, type SystemQuery, type SystemTableName, type TableReader, type TableVectorIndex, type TriggerAggregateOptions, type TriggerCtx, type TriggerDatabase, 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 VectorMatch, type VectorMatches, type VectorQueryInput, type VectorRecord, type VectorSearch, type VectorSearchReader, type VectorUpsertInput, type WorkflowCreateOptions, type WorkflowHandle, type WorkflowInstance, type WorkflowInstanceStatus, type WorkflowStatusResult, type Workflows, anyApi } from "./types.js";
|
|
5
5
|
import { LunoraError as LunoraError$1, LunoraErrorCode } from '@lunora/errors';
|
|
6
6
|
export type { LunoraErrorCode } from '@lunora/errors';
|
|
7
7
|
import { Context, Hono } from 'hono';
|
|
@@ -299,6 +299,11 @@ interface FacadeWriterLike {
|
|
|
299
299
|
}, expectedTable?: string): Promise<{
|
|
300
300
|
deleted: number;
|
|
301
301
|
}>;
|
|
302
|
+
deleteWhere?(tableName: string, where: Record<string, unknown>, options?: {
|
|
303
|
+
limit?: number;
|
|
304
|
+
}): Promise<{
|
|
305
|
+
deleted: number;
|
|
306
|
+
}>;
|
|
302
307
|
findFirst(tableName: string, args?: unknown): Promise<unknown>;
|
|
303
308
|
findFirstOrThrow(tableName: string, args?: unknown): Promise<unknown>;
|
|
304
309
|
findMany(tableName: string, args?: unknown): Promise<unknown>;
|
|
@@ -307,14 +312,25 @@ interface FacadeWriterLike {
|
|
|
307
312
|
insert(tableName: string, document: Record<string, unknown>): Promise<string>;
|
|
308
313
|
insertMany?(tableName: string, documents: ReadonlyArray<Record<string, unknown>>, options?: {
|
|
309
314
|
limit?: number;
|
|
310
|
-
|
|
315
|
+
skipDuplicates?: boolean;
|
|
316
|
+
}): Promise<(string | null)[]>;
|
|
311
317
|
patch(id: string, patch: Record<string, unknown>, expectedTable?: string): Promise<void>;
|
|
312
318
|
patchMany?(patches: ReadonlyArray<{
|
|
313
319
|
id: string;
|
|
314
320
|
patch: Record<string, unknown>;
|
|
315
321
|
}>, options?: {
|
|
316
322
|
limit?: number;
|
|
317
|
-
}, expectedTable?: string): Promise<
|
|
323
|
+
}, expectedTable?: string): Promise<{
|
|
324
|
+
patched: number;
|
|
325
|
+
}>;
|
|
326
|
+
patchWhere?(tableName: string, args: {
|
|
327
|
+
patch: Record<string, unknown>;
|
|
328
|
+
where: Record<string, unknown>;
|
|
329
|
+
}, options?: {
|
|
330
|
+
limit?: number;
|
|
331
|
+
}): Promise<{
|
|
332
|
+
patched: number;
|
|
333
|
+
}>;
|
|
318
334
|
query(tableName: string): {
|
|
319
335
|
withSearchIndex(indexName: string, search: (q: unknown) => unknown): unknown;
|
|
320
336
|
};
|
|
@@ -328,11 +344,19 @@ interface FacadeEntry {
|
|
|
328
344
|
aggregate: (options: unknown) => Promise<unknown>;
|
|
329
345
|
count: (where?: unknown) => Promise<number>;
|
|
330
346
|
delete: (id: string) => Promise<void>;
|
|
331
|
-
deleteMany:
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
347
|
+
deleteMany: {
|
|
348
|
+
(ids: ReadonlyArray<string>, options?: {
|
|
349
|
+
limit?: number;
|
|
350
|
+
}): Promise<{
|
|
351
|
+
deleted: number;
|
|
352
|
+
}>;
|
|
353
|
+
(args: {
|
|
354
|
+
limit?: number;
|
|
355
|
+
where: Record<string, unknown>;
|
|
356
|
+
}): Promise<{
|
|
357
|
+
deleted: number;
|
|
358
|
+
}>;
|
|
359
|
+
};
|
|
336
360
|
/** `true` when at least one row matches `where` (or any row exists when omitted). Honors RLS like `findFirst`. */
|
|
337
361
|
exists: (where?: unknown) => Promise<boolean>;
|
|
338
362
|
findFirst: (args?: unknown) => Promise<unknown>;
|
|
@@ -343,16 +367,34 @@ interface FacadeEntry {
|
|
|
343
367
|
/** Physically remove a row (and physically cascade), bypassing `.softDelete()`. */
|
|
344
368
|
hardDelete: (id: string) => Promise<void>;
|
|
345
369
|
insert: (document: Record<string, unknown>, options?: FacadeInsertOptions) => Promise<null | string>;
|
|
370
|
+
/**
|
|
371
|
+
* Insert many documents into this table in one call. With
|
|
372
|
+
* `{ skipDuplicates: true }`, UNIQUE breaches resolve to `null` for that row
|
|
373
|
+
* instead of failing the batch. The typed facade narrows the return to
|
|
374
|
+
* `Id<T>[]` when skipDuplicates is not requested.
|
|
375
|
+
*/
|
|
346
376
|
insertMany: (documents: ReadonlyArray<Record<string, unknown>>, options?: {
|
|
347
377
|
limit?: number;
|
|
348
|
-
|
|
378
|
+
skipDuplicates?: boolean;
|
|
379
|
+
}) => Promise<(string | null)[]>;
|
|
349
380
|
patch: (id: string, patch: Record<string, unknown>) => Promise<void>;
|
|
350
|
-
patchMany:
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
381
|
+
patchMany: {
|
|
382
|
+
(patches: ReadonlyArray<{
|
|
383
|
+
id: string;
|
|
384
|
+
values: Record<string, unknown>;
|
|
385
|
+
}>, options?: {
|
|
386
|
+
limit?: number;
|
|
387
|
+
}): Promise<{
|
|
388
|
+
patched: number;
|
|
389
|
+
}>;
|
|
390
|
+
(args: {
|
|
391
|
+
limit?: number;
|
|
392
|
+
values: Record<string, unknown>;
|
|
393
|
+
where: Record<string, unknown>;
|
|
394
|
+
}): Promise<{
|
|
395
|
+
patched: number;
|
|
396
|
+
}>;
|
|
397
|
+
};
|
|
356
398
|
rank: (indexName: string, options: unknown) => Promise<unknown>;
|
|
357
399
|
rankPage: (indexName: string, options?: unknown) => Promise<unknown>;
|
|
358
400
|
replace: (id: string, document: Record<string, unknown>) => Promise<void>;
|
|
@@ -435,7 +477,7 @@ type HttpMethod = "DELETE" | "GET" | "HEAD" | "OPTIONS" | "PATCH" | "POST" | "PU
|
|
|
435
477
|
* `storage` surface — reach the data layer through `runQuery` / `runMutation` /
|
|
436
478
|
* `runAction`, which forward to the owning shard.
|
|
437
479
|
*/
|
|
438
|
-
type HttpActionCtx = Pick<ActionCtx, "auth" | "fetch" | "runAction" | "runMutation" | "runQuery">;
|
|
480
|
+
type HttpActionCtx = Pick<ActionCtx, "auth" | "cache" | "fetch" | "runAction" | "runMutation" | "runQuery">;
|
|
439
481
|
/** A raw handler wrapped by {@link httpAction}. Receives the raw request, returns the raw response. */
|
|
440
482
|
type HttpActionHandler = (context: HttpActionCtx, request: Request) => Promise<Response> | Response;
|
|
441
483
|
/**
|
|
@@ -519,6 +561,16 @@ interface HttpStreamHandlerOptions<SearchParams extends ArgsValidator, Params ex
|
|
|
519
561
|
*/
|
|
520
562
|
interface HttpRouteBuilder<SearchParams extends ArgsValidator, Body extends ArgsValidator, Params extends ArgsValidator, Output = undefined> {
|
|
521
563
|
body: <B extends ArgsValidator>(validators: B) => HttpRouteBuilder<SearchParams, B & Body, Params, Output>;
|
|
564
|
+
/**
|
|
565
|
+
* Attach a `Cache-Control` header to the response. Only meaningful when
|
|
566
|
+
* Workers Cache is enabled in `wrangler.jsonc` (`"cache": { "enabled": true }`).
|
|
567
|
+
*/
|
|
568
|
+
cacheControl: (value: string) => HttpRouteBuilder<SearchParams, Body, Params, Output>;
|
|
569
|
+
/**
|
|
570
|
+
* Attach a `Cache-Tag` header to the response for tag-based purging via
|
|
571
|
+
* `ctx.cache.purge({ tags: [...] })`.
|
|
572
|
+
*/
|
|
573
|
+
cacheTag: (value: string) => HttpRouteBuilder<SearchParams, Body, Params, Output>;
|
|
522
574
|
handler: [Output] extends [undefined] ? <R>(handler: (options: HttpRouteHandlerOptions<SearchParams, Body, Params>) => Promise<R> | R) => LunoraRouteHandler : (handler: (options: HttpRouteHandlerOptions<SearchParams, Body, Params>) => Output | Promise<Output>) => LunoraRouteHandler;
|
|
523
575
|
output: <V extends Validator>(validator: V) => HttpRouteBuilder<SearchParams, Body, Params, Infer<V>>;
|
|
524
576
|
params: <P extends ArgsValidator>(validators: P) => HttpRouteBuilder<SearchParams, Body, P & Params, Output>;
|
|
@@ -533,6 +585,11 @@ interface HttpRouteBuilder<SearchParams extends ArgsValidator, Body extends Args
|
|
|
533
585
|
* handler's yielded type.
|
|
534
586
|
*/
|
|
535
587
|
stream: <R>(handler: (options: HttpStreamHandlerOptions<SearchParams, Params>) => AsyncGenerator<R, void, void> | AsyncIterable<R>) => LunoraRouteHandler;
|
|
588
|
+
/**
|
|
589
|
+
* Attach a `Vary` header to the response so Cloudflare stores separate
|
|
590
|
+
* cached variants per distinct value of the listed request headers.
|
|
591
|
+
*/
|
|
592
|
+
vary: (value: string) => HttpRouteBuilder<SearchParams, Body, Params, Output>;
|
|
536
593
|
}
|
|
537
594
|
/** Opens a fresh {@link HttpRouteBuilder}. The `path` documents intent; hono owns the actual routing at mount. */
|
|
538
595
|
type HttpRouteFactory = (path: string) => HttpRouteBuilder<EmptyArgs, EmptyArgs, EmptyArgs>;
|
|
@@ -828,6 +885,11 @@ interface MaskDatabase {
|
|
|
828
885
|
}) => Promise<{
|
|
829
886
|
deleted: number;
|
|
830
887
|
}>;
|
|
888
|
+
deleteWhere?: (tableName: string, where: Record<string, unknown>, options?: {
|
|
889
|
+
limit?: number;
|
|
890
|
+
}) => Promise<{
|
|
891
|
+
deleted: number;
|
|
892
|
+
}>;
|
|
831
893
|
findFirst: (tableName: string, args?: QueryArgs$1) => Promise<Record<string, unknown> | null>;
|
|
832
894
|
findFirstOrThrow: (tableName: string, args?: QueryArgs$1) => Promise<Record<string, unknown>>;
|
|
833
895
|
findMany: (tableName: string, args?: QueryArgs$1) => Promise<QueryPage$1>;
|
|
@@ -839,7 +901,8 @@ interface MaskDatabase {
|
|
|
839
901
|
insert: (tableName: string, document: Record<string, unknown>) => Promise<string>;
|
|
840
902
|
insertMany: (tableName: string, documents: ReadonlyArray<Record<string, unknown>>, options?: {
|
|
841
903
|
limit?: number;
|
|
842
|
-
|
|
904
|
+
skipDuplicates?: boolean;
|
|
905
|
+
}) => Promise<(string | null)[]>;
|
|
843
906
|
lookupById?: (id: string, expectedTable?: string) => Promise<null | {
|
|
844
907
|
row: Record<string, unknown>;
|
|
845
908
|
tableName: string;
|
|
@@ -850,7 +913,17 @@ interface MaskDatabase {
|
|
|
850
913
|
patch: Record<string, unknown>;
|
|
851
914
|
}>, options?: {
|
|
852
915
|
limit?: number;
|
|
853
|
-
}) => Promise<
|
|
916
|
+
}) => Promise<{
|
|
917
|
+
patched: number;
|
|
918
|
+
}>;
|
|
919
|
+
patchWhere?: (tableName: string, args: {
|
|
920
|
+
patch: Record<string, unknown>;
|
|
921
|
+
where: Record<string, unknown>;
|
|
922
|
+
}, options?: {
|
|
923
|
+
limit?: number;
|
|
924
|
+
}) => Promise<{
|
|
925
|
+
patched: number;
|
|
926
|
+
}>;
|
|
854
927
|
query: (tableName: string) => TableReaderLike$1;
|
|
855
928
|
rank: (tableName: string, indexName: string, options: unknown) => Promise<null | {
|
|
856
929
|
position: number;
|
|
@@ -1722,6 +1795,11 @@ interface DatabaseWriterLike {
|
|
|
1722
1795
|
}, expectedTable?: string) => Promise<{
|
|
1723
1796
|
deleted: number;
|
|
1724
1797
|
}>;
|
|
1798
|
+
deleteWhere?: (tableName: string, where: WhereInput, options?: {
|
|
1799
|
+
limit?: number;
|
|
1800
|
+
}) => Promise<{
|
|
1801
|
+
deleted: number;
|
|
1802
|
+
}>;
|
|
1725
1803
|
findFirst: (tableName: string, args?: QueryArgs) => Promise<Record<string, unknown> | null>;
|
|
1726
1804
|
findFirstOrThrow: (tableName: string, args?: QueryArgs) => Promise<Record<string, unknown>>;
|
|
1727
1805
|
findMany: (tableName: string, args?: QueryArgs) => Promise<QueryPage>;
|
|
@@ -1738,7 +1816,8 @@ interface DatabaseWriterLike {
|
|
|
1738
1816
|
insert: (tableName: string, document: Record<string, unknown>) => Promise<string>;
|
|
1739
1817
|
insertMany: (tableName: string, documents: ReadonlyArray<Record<string, unknown>>, options?: {
|
|
1740
1818
|
limit?: number;
|
|
1741
|
-
|
|
1819
|
+
skipDuplicates?: boolean;
|
|
1820
|
+
}) => Promise<(string | null)[]>;
|
|
1742
1821
|
insertManyUnsafe: (tableName: string, documents: ReadonlyArray<Record<string, unknown>>, options?: {
|
|
1743
1822
|
allowExplicitId?: boolean;
|
|
1744
1823
|
limit?: number;
|
|
@@ -1761,7 +1840,17 @@ interface DatabaseWriterLike {
|
|
|
1761
1840
|
patch: Record<string, unknown>;
|
|
1762
1841
|
}>, options?: {
|
|
1763
1842
|
limit?: number;
|
|
1764
|
-
}, expectedTable?: string) => Promise<
|
|
1843
|
+
}, expectedTable?: string) => Promise<{
|
|
1844
|
+
patched: number;
|
|
1845
|
+
}>;
|
|
1846
|
+
patchWhere?: (tableName: string, args: {
|
|
1847
|
+
patch: Record<string, unknown>;
|
|
1848
|
+
where: WhereInput;
|
|
1849
|
+
}, options?: {
|
|
1850
|
+
limit?: number;
|
|
1851
|
+
}) => Promise<{
|
|
1852
|
+
patched: number;
|
|
1853
|
+
}>;
|
|
1765
1854
|
query: (tableName: string) => TableReaderLike;
|
|
1766
1855
|
/**
|
|
1767
1856
|
* Rank a row within its partition. A position is a count-of-rows-before, so
|
package/dist/index.mjs
CHANGED
|
@@ -3,8 +3,8 @@ export { initLunora } from './packem_shared/initLunora-sQUqaejx.mjs';
|
|
|
3
3
|
export { createSecrets } from './packem_shared/createSecrets-DwaR2rNG.mjs';
|
|
4
4
|
export { LunoraEnvError, defineEnv, redactSecrets } from './packem_shared/LunoraEnvError-BGmd1Qs0.mjs';
|
|
5
5
|
export { LunoraError } from './packem_shared/LunoraError-WbxmrpxR.mjs';
|
|
6
|
-
export { bindOrm, bindTableFacade } from './packem_shared/bindOrm-
|
|
7
|
-
export { httpAction, httpRoute, httpRouter, isSafeHeaderValue, serveStorageObject } from './packem_shared/httpAction-
|
|
6
|
+
export { bindOrm, bindTableFacade } from './packem_shared/bindOrm-Bfgl7f-Q.mjs';
|
|
7
|
+
export { httpAction, httpRoute, httpRouter, isSafeHeaderValue, serveStorageObject } from './packem_shared/httpAction-DCXoYPIk.mjs';
|
|
8
8
|
export { defineIdentity } from './packem_shared/defineIdentity-DiX4zM9x.mjs';
|
|
9
9
|
export { onConnect, onDisconnect } from './packem_shared/onConnect-CIPXKPyw.mjs';
|
|
10
10
|
export { defineMigration } from './packem_shared/defineMigration-Hx01yIht.mjs';
|
|
@@ -17,11 +17,11 @@ export { defineShape } from './packem_shared/defineShape-C5scNOrf.mjs';
|
|
|
17
17
|
export { anyApi } from './types.mjs';
|
|
18
18
|
export { cronJobs } from '@lunora/scheduler';
|
|
19
19
|
export { ValidationError, v } from '@lunora/values';
|
|
20
|
-
export { buildRlsReadRegistry, composeShapeReadWhere } from './packem_shared/buildRlsReadRegistry-
|
|
20
|
+
export { buildRlsReadRegistry, composeShapeReadWhere } from './packem_shared/buildRlsReadRegistry-D54vUQe4.mjs';
|
|
21
21
|
export { createPolicyDsl, definePermission, definePolicies, definePolicy, defineRole } from './packem_shared/createPolicyDsl-By3QB4he.mjs';
|
|
22
22
|
export { defineStorageRule, defineStorageRules } from './packem_shared/defineStorageRule-B5nL4Z1P.mjs';
|
|
23
|
-
export { mask } from './packem_shared/mask-
|
|
24
|
-
export { rls } from './packem_shared/rls-
|
|
23
|
+
export { mask } from './packem_shared/mask-BT1YgRbF.mjs';
|
|
24
|
+
export { rls } from './packem_shared/rls-B_ZWgslr.mjs';
|
|
25
25
|
export { storageRules } from './packem_shared/storageRules-6QxzDOcx.mjs';
|
|
26
26
|
|
|
27
27
|
const VERSION = "0.0.0";
|
|
@@ -44,15 +44,24 @@ const bindTableFacade = (writer, tableName) => {
|
|
|
44
44
|
count: (where) => writer.count(tableName, where),
|
|
45
45
|
delete: (id) => writer.delete(id, tableName),
|
|
46
46
|
// `deleteMany`/`patchMany` forward the bound `tableName` as `expectedTable`
|
|
47
|
-
// (threaded through the writer + the RLS middleware's
|
|
48
|
-
// batched id is scoped to this table — the same IDOR
|
|
49
|
-
// `delete`/`patch` apply.
|
|
50
|
-
// the writer's `{
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
47
|
+
// for id-based calls (threaded through the writer + the RLS middleware's
|
|
48
|
+
// per-id gate) so every batched id is scoped to this table — the same IDOR
|
|
49
|
+
// guard the single-row `delete`/`patch` apply. The where-based form routes
|
|
50
|
+
// through the structural writer's `deleteMany(tableName, { where })`.
|
|
51
|
+
// `patchMany` maps the facade's `values` payload to the writer's
|
|
52
|
+
// `{ id, patch }` shape.
|
|
53
|
+
deleteMany: (first, options) => {
|
|
54
|
+
if (Array.isArray(first)) {
|
|
55
|
+
if (writer.deleteMany === void 0) {
|
|
56
|
+
throw new LunoraError$1("INTERNAL", `ctx.db.${tableName}.deleteMany is unavailable: this writer has no batch delete`);
|
|
57
|
+
}
|
|
58
|
+
return writer.deleteMany(first, options, tableName);
|
|
54
59
|
}
|
|
55
|
-
|
|
60
|
+
if (writer.deleteWhere === void 0) {
|
|
61
|
+
throw new LunoraError$1("INTERNAL", `ctx.db.${tableName}.deleteMany({ where }) is unavailable: this writer has no where-based delete`);
|
|
62
|
+
}
|
|
63
|
+
const whereArgs = first;
|
|
64
|
+
return writer.deleteWhere(tableName, whereArgs.where, { limit: whereArgs.limit });
|
|
56
65
|
},
|
|
57
66
|
// `exists` reuses `findFirst` (RLS-filtered, indexed when a `.withIndex`-able
|
|
58
67
|
// `where` is supplied) and only asks whether a row came back — no count scan.
|
|
@@ -72,17 +81,24 @@ const bindTableFacade = (writer, tableName) => {
|
|
|
72
81
|
return writer.insertMany(tableName, documents, options);
|
|
73
82
|
},
|
|
74
83
|
patch: (id, patch) => writer.patch(id, patch, tableName),
|
|
75
|
-
patchMany: (
|
|
76
|
-
if (
|
|
77
|
-
|
|
84
|
+
patchMany: (first, options) => {
|
|
85
|
+
if (Array.isArray(first)) {
|
|
86
|
+
if (writer.patchMany === void 0) {
|
|
87
|
+
throw new LunoraError$1("INTERNAL", `ctx.db.${tableName}.patchMany is unavailable: this writer has no batch patch`);
|
|
88
|
+
}
|
|
89
|
+
return writer.patchMany(
|
|
90
|
+
first.map((entry) => {
|
|
91
|
+
return { id: entry.id, patch: entry.values };
|
|
92
|
+
}),
|
|
93
|
+
options,
|
|
94
|
+
tableName
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
if (writer.patchWhere === void 0) {
|
|
98
|
+
throw new LunoraError$1("INTERNAL", `ctx.db.${tableName}.patchMany({ where, values }) is unavailable: this writer has no where-based patch`);
|
|
78
99
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
return { id: entry.id, patch: entry.values };
|
|
82
|
-
}),
|
|
83
|
-
options,
|
|
84
|
-
tableName
|
|
85
|
-
);
|
|
100
|
+
const whereArgs = first;
|
|
101
|
+
return writer.patchWhere(tableName, { patch: whereArgs.values, where: whereArgs.where }, { limit: whereArgs.limit });
|
|
86
102
|
},
|
|
87
103
|
rank: (indexName, options) => writer.rank(tableName, indexName, options),
|
|
88
104
|
rankPage: (indexName, options) => writer.rankPage(tableName, indexName, options),
|
package/dist/packem_shared/{buildRlsReadRegistry-DFBFLydB.mjs → buildRlsReadRegistry-D54vUQe4.mjs}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { indexRolePermissions, computeReadBaseWhere, permissionName } from './rls-
|
|
1
|
+
import { indexRolePermissions, computeReadBaseWhere, permissionName } from './rls-B_ZWgslr.mjs';
|
|
2
2
|
import { r as readRlsTag } from './policy-tag-DvpVH2tv.mjs';
|
|
3
3
|
|
|
4
4
|
const FALSE_PREDICATE = { OR: [] };
|
|
@@ -126,7 +126,21 @@ const buildRouteHandler = (state, userHandler) => async (c) => {
|
|
|
126
126
|
const body = Object.keys(state.body).length > 0 ? await parseBody(state.body, c) : {};
|
|
127
127
|
const result = await userHandler({ body, ctx: context, params, searchParams });
|
|
128
128
|
const payload = state.output ? applyOutput(state.output, result) : result;
|
|
129
|
-
|
|
129
|
+
const headers = {};
|
|
130
|
+
if (state.cacheControl) {
|
|
131
|
+
headers["cache-control"] = state.cacheControl;
|
|
132
|
+
}
|
|
133
|
+
if (state.cacheTag) {
|
|
134
|
+
headers["cache-tag"] = state.cacheTag;
|
|
135
|
+
}
|
|
136
|
+
if (state.vary) {
|
|
137
|
+
headers.vary = state.vary;
|
|
138
|
+
}
|
|
139
|
+
const hasCacheHeaders = Object.keys(headers).length > 0;
|
|
140
|
+
if (payload === void 0) {
|
|
141
|
+
return new Response(null, { headers: hasCacheHeaders ? headers : void 0, status: 204 });
|
|
142
|
+
}
|
|
143
|
+
return Response.json(payload, { headers: hasCacheHeaders ? headers : void 0 });
|
|
130
144
|
} catch (error) {
|
|
131
145
|
return errorResponse(error);
|
|
132
146
|
}
|
|
@@ -202,25 +216,31 @@ const buildStreamHandler = (state, userHandler) => (
|
|
|
202
216
|
}
|
|
203
217
|
}
|
|
204
218
|
});
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
219
|
+
const headers = {
|
|
220
|
+
// SSE responses must stay uncacheable so proxies don't buffer or
|
|
221
|
+
// coalesce live frames. `cacheControl()` is intentionally ignored
|
|
222
|
+
// for stream() routes; `cacheTag`/`vary` are also omitted because
|
|
223
|
+
// they only make sense alongside a cacheable response.
|
|
224
|
+
"cache-control": "no-cache, no-transform",
|
|
225
|
+
"content-type": "text/event-stream; charset=utf-8",
|
|
226
|
+
// Hint to proxies (including Cloudflare's own buffering layer)
|
|
227
|
+
// that this response must not be coalesced.
|
|
228
|
+
"x-accel-buffering": "no"
|
|
229
|
+
};
|
|
230
|
+
return new Response(stream, { headers });
|
|
214
231
|
}
|
|
215
232
|
);
|
|
216
233
|
const makeRouteBuilder = (state) => {
|
|
217
234
|
return {
|
|
218
235
|
body: (validators) => makeRouteBuilder({ ...state, body: { ...state.body, ...validators } }),
|
|
236
|
+
cacheControl: (value) => makeRouteBuilder({ ...state, cacheControl: value }),
|
|
237
|
+
cacheTag: (value) => makeRouteBuilder({ ...state, cacheTag: value }),
|
|
219
238
|
handler: (userHandler) => buildRouteHandler(state, userHandler),
|
|
220
239
|
output: (validator) => makeRouteBuilder({ ...state, output: validator }),
|
|
221
240
|
params: (validators) => makeRouteBuilder({ ...state, params: { ...state.params, ...validators } }),
|
|
222
241
|
searchParams: (validators) => makeRouteBuilder({ ...state, searchParams: { ...state.searchParams, ...validators } }),
|
|
223
|
-
stream: (userHandler) => buildStreamHandler(state, userHandler)
|
|
242
|
+
stream: (userHandler) => buildStreamHandler(state, userHandler),
|
|
243
|
+
vary: (value) => makeRouteBuilder({ ...state, vary: value })
|
|
224
244
|
};
|
|
225
245
|
};
|
|
226
246
|
const makeRouteFactory = (method) => (path) => makeRouteBuilder({ body: {}, method, params: {}, path, searchParams: {} });
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { LunoraError } from './LunoraError-WbxmrpxR.mjs';
|
|
2
|
-
import { bindTableFacade, bindOrm } from './bindOrm-
|
|
2
|
+
import { bindTableFacade, bindOrm } from './bindOrm-Bfgl7f-Q.mjs';
|
|
3
3
|
|
|
4
4
|
const permissionName = (permission) => typeof permission === "string" ? permission : permission.name;
|
|
5
5
|
const indexRolePermissions = (roles) => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { LunoraError } from './LunoraError-WbxmrpxR.mjs';
|
|
2
|
-
import { bindTableFacade, bindOrm } from './bindOrm-
|
|
2
|
+
import { bindTableFacade, bindOrm } from './bindOrm-Bfgl7f-Q.mjs';
|
|
3
3
|
import { t as tagRlsMiddleware } from './policy-tag-DvpVH2tv.mjs';
|
|
4
4
|
|
|
5
5
|
const DEFAULT_BATCH_LIMIT = 500;
|
|
@@ -333,6 +333,16 @@ const wrapDatabase = (base, raw, perTable, context) => {
|
|
|
333
333
|
}
|
|
334
334
|
return { deleted: ids.length };
|
|
335
335
|
},
|
|
336
|
+
async deleteWhere(tableName, where, options) {
|
|
337
|
+
const { baseWhere } = readBase(tableName);
|
|
338
|
+
const resolved = await route(tableName).findMany(tableName, {
|
|
339
|
+
baseWhere: mergeBaseWhere(where, baseWhere),
|
|
340
|
+
relationBaseWhere: relationReadFilter
|
|
341
|
+
});
|
|
342
|
+
const ids = resolved.page.map((row) => String(row["_id"]));
|
|
343
|
+
assertBatchLimit(ids.length, options?.limit, "deleteWhere");
|
|
344
|
+
return wrapped.deleteMany(ids, options);
|
|
345
|
+
},
|
|
336
346
|
async findFirst(tableName, args) {
|
|
337
347
|
const { baseWhere } = readBase(tableName);
|
|
338
348
|
return route(tableName).findFirst(tableName, {
|
|
@@ -436,6 +446,19 @@ const wrapDatabase = (base, raw, perTable, context) => {
|
|
|
436
446
|
expectedTable
|
|
437
447
|
);
|
|
438
448
|
}
|
|
449
|
+
return { patched: patches.length };
|
|
450
|
+
},
|
|
451
|
+
async patchWhere(tableName, args, options) {
|
|
452
|
+
const { baseWhere } = readBase(tableName);
|
|
453
|
+
const resolved = await route(tableName).findMany(tableName, {
|
|
454
|
+
baseWhere: mergeBaseWhere(args.where, baseWhere),
|
|
455
|
+
relationBaseWhere: relationReadFilter
|
|
456
|
+
});
|
|
457
|
+
const patches = resolved.page.map((row) => {
|
|
458
|
+
return { id: String(row["_id"]), patch: args.patch };
|
|
459
|
+
});
|
|
460
|
+
assertBatchLimit(patches.length, options?.limit, "patchWhere");
|
|
461
|
+
return wrapped.patchMany(patches, options);
|
|
439
462
|
},
|
|
440
463
|
query(tableName) {
|
|
441
464
|
const { baseWhere } = readBase(tableName);
|
package/dist/rls/testing.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { indexRolePermissions, computeReadBaseWhere, matchesWhere, evaluateWrite, permissionName } from '../packem_shared/rls-
|
|
1
|
+
import { indexRolePermissions, computeReadBaseWhere, matchesWhere, evaluateWrite, permissionName } from '../packem_shared/rls-B_ZWgslr.mjs';
|
|
2
2
|
|
|
3
3
|
const expectPolicy = (policies, options = {}) => {
|
|
4
4
|
const rolePermissions = indexRolePermissions(options.roles);
|
package/dist/types.d.mts
CHANGED
|
@@ -518,6 +518,16 @@ interface BatchWriteOptions {
|
|
|
518
518
|
/** Reject the call when the batch size exceeds this value (default 500). */
|
|
519
519
|
limit?: number;
|
|
520
520
|
}
|
|
521
|
+
/** Options accepted by {@link DatabaseWriter.insertMany} and the per-table facade. */
|
|
522
|
+
interface InsertManyOptions extends BatchWriteOptions {
|
|
523
|
+
/**
|
|
524
|
+
* When `true`, a UNIQUE-constraint breach for a row resolves to `null`
|
|
525
|
+
* instead of throwing — the rest of the batch is still inserted. Skipped rows
|
|
526
|
+
* keep their input-order slot with `null` in the returned array. Mirrors
|
|
527
|
+
* better-drizzle's `createMany({ skipDuplicates: true })`.
|
|
528
|
+
*/
|
|
529
|
+
skipDuplicates?: boolean;
|
|
530
|
+
}
|
|
521
531
|
interface DatabaseWriter extends DatabaseReader {
|
|
522
532
|
delete: <T extends string>(id: Id<T>) => Promise<void>;
|
|
523
533
|
/**
|
|
@@ -535,6 +545,18 @@ interface DatabaseWriter extends DatabaseReader {
|
|
|
535
545
|
deleted: number;
|
|
536
546
|
}>;
|
|
537
547
|
/**
|
|
548
|
+
* Delete every row matching `where` in one call. Matching rows are resolved
|
|
549
|
+
* first, then each row is deleted through the single-row delete pipeline
|
|
550
|
+
* (triggers, companion sync, CDC, broadcast) so reactive subscriptions and
|
|
551
|
+
* search/aggregate companions stay correct.
|
|
552
|
+
*
|
|
553
|
+
* **Atomic within a mutation:** the DO wraps a mutation's dispatch in a
|
|
554
|
+
* BEGIN/COMMIT span, so a mid-batch failure rolls back the whole mutation.
|
|
555
|
+
*/
|
|
556
|
+
deleteWhere: (tableName: string, where: Record<string, unknown>, options?: BatchWriteOptions) => Promise<{
|
|
557
|
+
deleted: number;
|
|
558
|
+
}>;
|
|
559
|
+
/**
|
|
538
560
|
* Insert a document, returning its server id.
|
|
539
561
|
*
|
|
540
562
|
* Pass `options.clientId` (a UUID) to key the row yourself — for an
|
|
@@ -551,12 +573,21 @@ interface DatabaseWriter extends DatabaseReader {
|
|
|
551
573
|
* row gets defaults, validators, triggers, and a per-row RLS check — but the
|
|
552
574
|
* caller pays one round-trip instead of N.
|
|
553
575
|
*
|
|
576
|
+
* Pass `{ skipDuplicates: true }` to turn UNIQUE-constraint breaches into
|
|
577
|
+
* `null` results for that row instead of failing the whole batch; the rest of
|
|
578
|
+
* the batch is still inserted and order is preserved.
|
|
579
|
+
*
|
|
554
580
|
* **Atomic within a mutation:** the DO wraps a mutation's dispatch in a
|
|
555
581
|
* BEGIN/COMMIT span, so a mid-batch failure (an invalid or RLS-denied row)
|
|
556
582
|
* rolls back the whole mutation. (In an action there is no transaction span,
|
|
557
583
|
* so the prior inserts persist; the in-memory test harness mirrors the span.)
|
|
558
584
|
*/
|
|
559
|
-
insertMany:
|
|
585
|
+
insertMany: {
|
|
586
|
+
<T extends string>(tableName: T, documents: ReadonlyArray<Record<string, unknown>>, options: BatchWriteOptions & {
|
|
587
|
+
skipDuplicates: true;
|
|
588
|
+
}): Promise<(Id<T> | null)[]>;
|
|
589
|
+
<T extends string>(tableName: T, documents: ReadonlyArray<Record<string, unknown>>, options?: InsertManyOptions): Promise<Id<T>[]>;
|
|
590
|
+
};
|
|
560
591
|
/**
|
|
561
592
|
* **Trusted** bulk insert: one multi-row `INSERT` that **skips per-row
|
|
562
593
|
* `.check()` validators and before/after triggers** for throughput on data you
|
|
@@ -576,7 +607,8 @@ interface DatabaseWriter extends DatabaseReader {
|
|
|
576
607
|
patch: <T extends string>(id: Id<T>, patch: Record<string, unknown>) => Promise<void>;
|
|
577
608
|
/**
|
|
578
609
|
* Patch many rows by id in one call. Each `{ id, patch }` is applied like a
|
|
579
|
-
* single `patch()` (per-row triggers + RLS).
|
|
610
|
+
* single `patch()` (per-row triggers + RLS). Returns the number of rows
|
|
611
|
+
* actually patched.
|
|
580
612
|
*
|
|
581
613
|
* **Atomic within a mutation:** the DO wraps a mutation's dispatch in a
|
|
582
614
|
* BEGIN/COMMIT span, so a mid-batch failure rolls back the whole mutation.
|
|
@@ -586,7 +618,24 @@ interface DatabaseWriter extends DatabaseReader {
|
|
|
586
618
|
patchMany: <T extends string>(patches: ReadonlyArray<{
|
|
587
619
|
id: Id<T>;
|
|
588
620
|
patch: Record<string, unknown>;
|
|
589
|
-
}>, options?: BatchWriteOptions) => Promise<
|
|
621
|
+
}>, options?: BatchWriteOptions) => Promise<{
|
|
622
|
+
patched: number;
|
|
623
|
+
}>;
|
|
624
|
+
/**
|
|
625
|
+
* Patch every row matching `where` with the same `patch` in one call. The
|
|
626
|
+
* matching rows are resolved first, then each row is updated through the
|
|
627
|
+
* single-row patch pipeline (OCC, triggers, companion sync, CDC, broadcast)
|
|
628
|
+
* so reactive subscriptions and search/aggregate companions stay correct.
|
|
629
|
+
*
|
|
630
|
+
* **Atomic within a mutation:** the DO wraps a mutation's dispatch in a
|
|
631
|
+
* BEGIN/COMMIT span, so a mid-batch failure rolls back the whole mutation.
|
|
632
|
+
*/
|
|
633
|
+
patchWhere: (tableName: string, args: {
|
|
634
|
+
patch: Record<string, unknown>;
|
|
635
|
+
where: Record<string, unknown>;
|
|
636
|
+
}, options?: BatchWriteOptions) => Promise<{
|
|
637
|
+
patched: number;
|
|
638
|
+
}>;
|
|
590
639
|
replace: <T extends string>(id: Id<T>, document: Record<string, unknown>) => Promise<void>;
|
|
591
640
|
}
|
|
592
641
|
/** Authenticated identity surfaced into every context. */
|
|
@@ -690,6 +739,21 @@ interface Workflows {
|
|
|
690
739
|
get: <Params = Record<string, unknown>>(name: string) => WorkflowHandle<Params>;
|
|
691
740
|
}
|
|
692
741
|
/**
|
|
742
|
+
* Programmatic cache purge surface exposed on {@link ActionCtx}. Actions run
|
|
743
|
+
* in the Worker (not the DO), so they can reach the Worker's `ctx.cache.purge`.
|
|
744
|
+
* Queries and mutations do not expose this — they run inside the Durable Object.
|
|
745
|
+
*/
|
|
746
|
+
interface CachePurge {
|
|
747
|
+
/**
|
|
748
|
+
* Purge cached responses matching the given tags, or everything when
|
|
749
|
+
* `purgeEverything` is true. Only available in action handlers.
|
|
750
|
+
*/
|
|
751
|
+
purge: (options: {
|
|
752
|
+
purgeEverything?: boolean;
|
|
753
|
+
tags?: string[];
|
|
754
|
+
}) => Promise<unknown>;
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
693
757
|
* Structural projection of workers-types' `SecretsStoreSecret` binding — the
|
|
694
758
|
* per-secret `secrets_store_secrets[]` binding whose `.get()` resolves the
|
|
695
759
|
* secret value (or throws if it does not exist). Mirrored structurally so the
|
|
@@ -1136,6 +1200,13 @@ interface MutationCtx {
|
|
|
1136
1200
|
}
|
|
1137
1201
|
interface ActionCtx {
|
|
1138
1202
|
readonly auth: AuthState;
|
|
1203
|
+
/**
|
|
1204
|
+
* Programmatic Workers Cache purge; see {@link CachePurge}.
|
|
1205
|
+
* **Action-only** — actions run in the Worker, which has a `cache` binding.
|
|
1206
|
+
* Queries and mutations run inside the Durable Object and do not expose this.
|
|
1207
|
+
* Optional at runtime because Workers Cache is only present when enabled.
|
|
1208
|
+
*/
|
|
1209
|
+
readonly cache?: CachePurge;
|
|
1139
1210
|
readonly db: DatabaseWriter;
|
|
1140
1211
|
/**
|
|
1141
1212
|
* The validated, typed environment. Populated only when the project declares
|
|
@@ -1178,4 +1249,4 @@ interface ActionCtx {
|
|
|
1178
1249
|
*/
|
|
1179
1250
|
type AnyApi = Record<string, Record<string, RegisteredFunction<ArgsValidator, unknown, FunctionKind>>>;
|
|
1180
1251
|
declare const anyApi: AnyApi;
|
|
1181
|
-
export { type ActionCtx, type AggregateIndexDefinition, type AggregateOp, type AnyApi, type ArgsValidator, type AuthState, type DatabaseReader, type DatabaseWriter, type DurableObjectJurisdiction, 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, anyApi };
|
|
1252
|
+
export { type ActionCtx, type AggregateIndexDefinition, type AggregateOp, type AnyApi, type ArgsValidator, type AuthState, type CachePurge, type DatabaseReader, type DatabaseWriter, type DurableObjectJurisdiction, 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, anyApi };
|
package/dist/types.d.ts
CHANGED
|
@@ -518,6 +518,16 @@ interface BatchWriteOptions {
|
|
|
518
518
|
/** Reject the call when the batch size exceeds this value (default 500). */
|
|
519
519
|
limit?: number;
|
|
520
520
|
}
|
|
521
|
+
/** Options accepted by {@link DatabaseWriter.insertMany} and the per-table facade. */
|
|
522
|
+
interface InsertManyOptions extends BatchWriteOptions {
|
|
523
|
+
/**
|
|
524
|
+
* When `true`, a UNIQUE-constraint breach for a row resolves to `null`
|
|
525
|
+
* instead of throwing — the rest of the batch is still inserted. Skipped rows
|
|
526
|
+
* keep their input-order slot with `null` in the returned array. Mirrors
|
|
527
|
+
* better-drizzle's `createMany({ skipDuplicates: true })`.
|
|
528
|
+
*/
|
|
529
|
+
skipDuplicates?: boolean;
|
|
530
|
+
}
|
|
521
531
|
interface DatabaseWriter extends DatabaseReader {
|
|
522
532
|
delete: <T extends string>(id: Id<T>) => Promise<void>;
|
|
523
533
|
/**
|
|
@@ -535,6 +545,18 @@ interface DatabaseWriter extends DatabaseReader {
|
|
|
535
545
|
deleted: number;
|
|
536
546
|
}>;
|
|
537
547
|
/**
|
|
548
|
+
* Delete every row matching `where` in one call. Matching rows are resolved
|
|
549
|
+
* first, then each row is deleted through the single-row delete pipeline
|
|
550
|
+
* (triggers, companion sync, CDC, broadcast) so reactive subscriptions and
|
|
551
|
+
* search/aggregate companions stay correct.
|
|
552
|
+
*
|
|
553
|
+
* **Atomic within a mutation:** the DO wraps a mutation's dispatch in a
|
|
554
|
+
* BEGIN/COMMIT span, so a mid-batch failure rolls back the whole mutation.
|
|
555
|
+
*/
|
|
556
|
+
deleteWhere: (tableName: string, where: Record<string, unknown>, options?: BatchWriteOptions) => Promise<{
|
|
557
|
+
deleted: number;
|
|
558
|
+
}>;
|
|
559
|
+
/**
|
|
538
560
|
* Insert a document, returning its server id.
|
|
539
561
|
*
|
|
540
562
|
* Pass `options.clientId` (a UUID) to key the row yourself — for an
|
|
@@ -551,12 +573,21 @@ interface DatabaseWriter extends DatabaseReader {
|
|
|
551
573
|
* row gets defaults, validators, triggers, and a per-row RLS check — but the
|
|
552
574
|
* caller pays one round-trip instead of N.
|
|
553
575
|
*
|
|
576
|
+
* Pass `{ skipDuplicates: true }` to turn UNIQUE-constraint breaches into
|
|
577
|
+
* `null` results for that row instead of failing the whole batch; the rest of
|
|
578
|
+
* the batch is still inserted and order is preserved.
|
|
579
|
+
*
|
|
554
580
|
* **Atomic within a mutation:** the DO wraps a mutation's dispatch in a
|
|
555
581
|
* BEGIN/COMMIT span, so a mid-batch failure (an invalid or RLS-denied row)
|
|
556
582
|
* rolls back the whole mutation. (In an action there is no transaction span,
|
|
557
583
|
* so the prior inserts persist; the in-memory test harness mirrors the span.)
|
|
558
584
|
*/
|
|
559
|
-
insertMany:
|
|
585
|
+
insertMany: {
|
|
586
|
+
<T extends string>(tableName: T, documents: ReadonlyArray<Record<string, unknown>>, options: BatchWriteOptions & {
|
|
587
|
+
skipDuplicates: true;
|
|
588
|
+
}): Promise<(Id<T> | null)[]>;
|
|
589
|
+
<T extends string>(tableName: T, documents: ReadonlyArray<Record<string, unknown>>, options?: InsertManyOptions): Promise<Id<T>[]>;
|
|
590
|
+
};
|
|
560
591
|
/**
|
|
561
592
|
* **Trusted** bulk insert: one multi-row `INSERT` that **skips per-row
|
|
562
593
|
* `.check()` validators and before/after triggers** for throughput on data you
|
|
@@ -576,7 +607,8 @@ interface DatabaseWriter extends DatabaseReader {
|
|
|
576
607
|
patch: <T extends string>(id: Id<T>, patch: Record<string, unknown>) => Promise<void>;
|
|
577
608
|
/**
|
|
578
609
|
* Patch many rows by id in one call. Each `{ id, patch }` is applied like a
|
|
579
|
-
* single `patch()` (per-row triggers + RLS).
|
|
610
|
+
* single `patch()` (per-row triggers + RLS). Returns the number of rows
|
|
611
|
+
* actually patched.
|
|
580
612
|
*
|
|
581
613
|
* **Atomic within a mutation:** the DO wraps a mutation's dispatch in a
|
|
582
614
|
* BEGIN/COMMIT span, so a mid-batch failure rolls back the whole mutation.
|
|
@@ -586,7 +618,24 @@ interface DatabaseWriter extends DatabaseReader {
|
|
|
586
618
|
patchMany: <T extends string>(patches: ReadonlyArray<{
|
|
587
619
|
id: Id<T>;
|
|
588
620
|
patch: Record<string, unknown>;
|
|
589
|
-
}>, options?: BatchWriteOptions) => Promise<
|
|
621
|
+
}>, options?: BatchWriteOptions) => Promise<{
|
|
622
|
+
patched: number;
|
|
623
|
+
}>;
|
|
624
|
+
/**
|
|
625
|
+
* Patch every row matching `where` with the same `patch` in one call. The
|
|
626
|
+
* matching rows are resolved first, then each row is updated through the
|
|
627
|
+
* single-row patch pipeline (OCC, triggers, companion sync, CDC, broadcast)
|
|
628
|
+
* so reactive subscriptions and search/aggregate companions stay correct.
|
|
629
|
+
*
|
|
630
|
+
* **Atomic within a mutation:** the DO wraps a mutation's dispatch in a
|
|
631
|
+
* BEGIN/COMMIT span, so a mid-batch failure rolls back the whole mutation.
|
|
632
|
+
*/
|
|
633
|
+
patchWhere: (tableName: string, args: {
|
|
634
|
+
patch: Record<string, unknown>;
|
|
635
|
+
where: Record<string, unknown>;
|
|
636
|
+
}, options?: BatchWriteOptions) => Promise<{
|
|
637
|
+
patched: number;
|
|
638
|
+
}>;
|
|
590
639
|
replace: <T extends string>(id: Id<T>, document: Record<string, unknown>) => Promise<void>;
|
|
591
640
|
}
|
|
592
641
|
/** Authenticated identity surfaced into every context. */
|
|
@@ -690,6 +739,21 @@ interface Workflows {
|
|
|
690
739
|
get: <Params = Record<string, unknown>>(name: string) => WorkflowHandle<Params>;
|
|
691
740
|
}
|
|
692
741
|
/**
|
|
742
|
+
* Programmatic cache purge surface exposed on {@link ActionCtx}. Actions run
|
|
743
|
+
* in the Worker (not the DO), so they can reach the Worker's `ctx.cache.purge`.
|
|
744
|
+
* Queries and mutations do not expose this — they run inside the Durable Object.
|
|
745
|
+
*/
|
|
746
|
+
interface CachePurge {
|
|
747
|
+
/**
|
|
748
|
+
* Purge cached responses matching the given tags, or everything when
|
|
749
|
+
* `purgeEverything` is true. Only available in action handlers.
|
|
750
|
+
*/
|
|
751
|
+
purge: (options: {
|
|
752
|
+
purgeEverything?: boolean;
|
|
753
|
+
tags?: string[];
|
|
754
|
+
}) => Promise<unknown>;
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
693
757
|
* Structural projection of workers-types' `SecretsStoreSecret` binding — the
|
|
694
758
|
* per-secret `secrets_store_secrets[]` binding whose `.get()` resolves the
|
|
695
759
|
* secret value (or throws if it does not exist). Mirrored structurally so the
|
|
@@ -1136,6 +1200,13 @@ interface MutationCtx {
|
|
|
1136
1200
|
}
|
|
1137
1201
|
interface ActionCtx {
|
|
1138
1202
|
readonly auth: AuthState;
|
|
1203
|
+
/**
|
|
1204
|
+
* Programmatic Workers Cache purge; see {@link CachePurge}.
|
|
1205
|
+
* **Action-only** — actions run in the Worker, which has a `cache` binding.
|
|
1206
|
+
* Queries and mutations run inside the Durable Object and do not expose this.
|
|
1207
|
+
* Optional at runtime because Workers Cache is only present when enabled.
|
|
1208
|
+
*/
|
|
1209
|
+
readonly cache?: CachePurge;
|
|
1139
1210
|
readonly db: DatabaseWriter;
|
|
1140
1211
|
/**
|
|
1141
1212
|
* The validated, typed environment. Populated only when the project declares
|
|
@@ -1178,4 +1249,4 @@ interface ActionCtx {
|
|
|
1178
1249
|
*/
|
|
1179
1250
|
type AnyApi = Record<string, Record<string, RegisteredFunction<ArgsValidator, unknown, FunctionKind>>>;
|
|
1180
1251
|
declare const anyApi: AnyApi;
|
|
1181
|
-
export { type ActionCtx, type AggregateIndexDefinition, type AggregateOp, type AnyApi, type ArgsValidator, type AuthState, type DatabaseReader, type DatabaseWriter, type DurableObjectJurisdiction, 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, anyApi };
|
|
1252
|
+
export { type ActionCtx, type AggregateIndexDefinition, type AggregateOp, type AnyApi, type ArgsValidator, type AuthState, type CachePurge, type DatabaseReader, type DatabaseWriter, type DurableObjectJurisdiction, 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, anyApi };
|