@lunora/server 1.0.0-alpha.2 → 1.0.0-alpha.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +6 -0
- package/README.md +51 -1
- package/__assets__/package-og.svg +1 -1
- package/dist/data-model.d.mts +158 -32
- package/dist/data-model.d.ts +158 -32
- package/dist/index.d.mts +459 -117
- package/dist/index.d.ts +459 -117
- package/dist/index.mjs +20 -15
- package/dist/packem_shared/{LunoraEnvError-DjFkpkSP.mjs → LunoraEnvError-BGmd1Qs0.mjs} +4 -4
- package/dist/packem_shared/LunoraError-WbxmrpxR.mjs +9 -0
- package/dist/packem_shared/{PRESENCE_DEFAULT_TTL_MS-BZYd5-uo.mjs → PRESENCE_DEFAULT_TTL_MS-BtPCiNLW.mjs} +4 -4
- package/dist/packem_shared/bindOrm-Bfgl7f-Q.mjs +146 -0
- package/dist/packem_shared/buildRlsReadRegistry-D54vUQe4.mjs +107 -0
- package/dist/packem_shared/{composePluginMiddleware-Ck5_TUO8.mjs → composePluginMiddleware-CcTj1_v2.mjs} +9 -6
- package/dist/packem_shared/{createPolicyDsl-De67zPDS.mjs → createPolicyDsl-By3QB4he.mjs} +4 -1
- package/dist/packem_shared/createSecrets-DwaR2rNG.mjs +58 -0
- package/dist/packem_shared/{defineAggregateIndex-DxSso0rH.mjs → defineAggregateIndex-znq4ygKQ.mjs} +69 -10
- package/dist/packem_shared/defineIdentity-DiX4zM9x.mjs +35 -0
- package/dist/packem_shared/{defineMigration-CAJLr6fx.mjs → defineMigration-Hx01yIht.mjs} +3 -1
- package/dist/packem_shared/defineMutator-EIXAWhs9.mjs +11 -0
- package/dist/packem_shared/defineShape-C5scNOrf.mjs +18 -0
- package/dist/packem_shared/{defineStorageRule-qu0mpilX.mjs → defineStorageRule-B5nL4Z1P.mjs} +4 -1
- package/dist/packem_shared/functions-Di9FUNkf.mjs +5 -0
- package/dist/packem_shared/{httpAction-B7FYUEgr.mjs → httpAction-DCXoYPIk.mjs} +42 -27
- package/dist/packem_shared/{initLunora-CATvPsVt.mjs → initLunora-sQUqaejx.mjs} +18 -4
- package/dist/packem_shared/{mask-Jc84C_hK.mjs → mask-BT1YgRbF.mjs} +89 -9
- package/dist/packem_shared/policy-tag-DvpVH2tv.mjs +13 -0
- package/dist/packem_shared/{protectPublic-BjFkQ_Or.mjs → protectPublic-BlcGpiRc.mjs} +1 -1
- package/dist/packem_shared/{rls-BDKRbMCA.mjs → rls-B_ZWgslr.mjs} +51 -7
- package/dist/packem_shared/{run-middleware-CYQOuoV6.mjs → run-middleware-I6EiQfxL.mjs} +3 -1
- package/dist/packem_shared/{storageRules-4a30FSpI.mjs → storageRules-6QxzDOcx.mjs} +1 -1
- package/dist/packem_shared/{types.d-DmvyEMD6.d.mts → types.d-BB3pjV0m.d.mts} +10 -4
- package/dist/packem_shared/{types.d-BDY0FYHK.d.ts → types.d-Cxl6ndhm.d.ts} +10 -4
- package/dist/rls/testing.d.mts +1 -1
- package/dist/rls/testing.d.ts +1 -1
- package/dist/rls/testing.mjs +1 -1
- package/dist/types.d.mts +206 -5
- package/dist/types.d.ts +206 -5
- package/package.json +6 -5
- package/dist/packem_shared/LunoraError-DhggBJZF.mjs +0 -51
- package/dist/packem_shared/bindOrm-DCuyr46L.mjs +0 -71
package/dist/index.d.mts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { Validator, Infer, v } from '@lunora/values';
|
|
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
|
-
import { ArgsValidator, InferArgs, RegisteredAction, ActionCtx, MutationCtx, RegisteredMutation, QueryCtx, RegisteredQuery, RegisteredStream, FunctionKind, LifecycleEvent, RegisteredLifecycleHook, TableDefinition, RegisteredFunction, VectorIndexDefinition, Schema, AggregateOp, RelationDefinition, GlobalBackend, OnDeleteAction, 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";
|
|
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 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
|
+
import { LunoraError as LunoraError$1, LunoraErrorCode } from '@lunora/errors';
|
|
6
|
+
export type { LunoraErrorCode } from '@lunora/errors';
|
|
5
7
|
import { Context, Hono } from 'hono';
|
|
6
|
-
import { b as Permission, R as Role, T as TypedDefinePolicyInput, a as Policy, D as DefinePolicyInput, W as WhereInput, c as RlsOptions } from "./packem_shared/types.d-
|
|
7
|
-
export type { d as PolicyContext, e as PolicyDecision, f as PolicyDecisionOf, P as PolicyOperation } from "./packem_shared/types.d-
|
|
8
|
+
import { b as Permission, R as Role, T as TypedDefinePolicyInput, a as Policy, D as DefinePolicyInput, W as WhereInput, c as RlsOptions } from "./packem_shared/types.d-BB3pjV0m.mjs";
|
|
9
|
+
export type { d as PolicyContext, e as PolicyDecision, f as PolicyDecisionOf, P as PolicyOperation } from "./packem_shared/types.d-BB3pjV0m.mjs";
|
|
8
10
|
export { type CronJob, type CronJobsBuilder, type CronScheduleKind, type DailySchedule, type IntervalSchedule, type MonthlySchedule, type WeeklySchedule, cronJobs } from '@lunora/scheduler';
|
|
9
11
|
import "./data-model.mjs";
|
|
10
12
|
/**
|
|
@@ -187,6 +189,13 @@ declare const initLunora: {
|
|
|
187
189
|
dataModel: <DataModel>() => DataModelInit<DataModel>;
|
|
188
190
|
};
|
|
189
191
|
/**
|
|
192
|
+
* Build the `ctx.secrets` reader from the worker `env`. `get(name)` resolves
|
|
193
|
+
* `env[name].get()` — the `secrets_store_secrets[]` binding of that name. An
|
|
194
|
+
* absent or non-Secrets-Store binding throws a directed error pointing at the
|
|
195
|
+
* wrangler config; the lookup is lazy, so an unused secret never resolves.
|
|
196
|
+
*/
|
|
197
|
+
declare const createSecrets: (env: Record<string, unknown>) => Secrets;
|
|
198
|
+
/**
|
|
190
199
|
* Redact secrets from a free-form message. Masks, in order: any quoted value
|
|
191
200
|
* whose contents look like a credential (so a value surfaced as `received string
|
|
192
201
|
* "sk_live_…"` is masked even though the surrounding text is not a token); a
|
|
@@ -219,8 +228,7 @@ interface EnvKeyFailure {
|
|
|
219
228
|
*
|
|
220
229
|
* Named export only (no default) per the repo export convention.
|
|
221
230
|
*/
|
|
222
|
-
declare class LunoraEnvError extends
|
|
223
|
-
override readonly name = "LunoraEnvError";
|
|
231
|
+
declare class LunoraEnvError extends LunoraError$1 {
|
|
224
232
|
readonly failures: ReadonlyArray<EnvKeyFailure>;
|
|
225
233
|
constructor(failures: ReadonlyArray<EnvKeyFailure>);
|
|
226
234
|
}
|
|
@@ -270,75 +278,9 @@ interface EnvAccessor<S extends EnvShape> {
|
|
|
270
278
|
* invalid — lazily on first access of that key, or eagerly via `config.parse(env)`.
|
|
271
279
|
*/
|
|
272
280
|
declare const defineEnv: <S extends EnvShape>(shape: S) => EnvAccessor<S>;
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
* The runtime's structural error mapper keys off `name === "LunoraError"` plus
|
|
277
|
-
* the numeric `status`, so throwing one of these from a handler or middleware
|
|
278
|
-
* yields the right RPC/HTTP status without any further wiring. `code` carries
|
|
279
|
-
* the machine-readable reason for clients.
|
|
280
|
-
*/
|
|
281
|
-
declare const CODE_STATUS: {
|
|
282
|
-
readonly BAD_REQUEST: 400;
|
|
283
|
-
readonly CONFLICT: 409;
|
|
284
|
-
/**
|
|
285
|
-
* `count()` invoked against a table whose context carries an active RLS
|
|
286
|
-
* policy. The operation itself is unsupported in an RLS-restricted reader
|
|
287
|
-
* (kitcn's documented constraint) — the request is well-formed and the
|
|
288
|
-
* caller is authorized, so this is a 422 (semantic conflict) rather than a
|
|
289
|
-
* 403 (policy denial).
|
|
290
|
-
*/
|
|
291
|
-
readonly COUNT_RLS_UNSUPPORTED: 422;
|
|
292
|
-
readonly FORBIDDEN: 403;
|
|
293
|
-
readonly INTERNAL_SERVER_ERROR: 500;
|
|
294
|
-
/**
|
|
295
|
-
* An analytical reduction (`aggregate` / `groupBy`) was invoked over a
|
|
296
|
-
* column that the procedure's `mask()` middleware redacts. A masked column
|
|
297
|
-
* can't be summed, averaged, or grouped without leaking the very values the
|
|
298
|
-
* mask hides (a group key *is* the raw value; an aggregate is computed from
|
|
299
|
-
* it), so the operation fails closed. The request is well-formed and the
|
|
300
|
-
* caller is authorized — this is a 422 (semantic conflict), mirroring
|
|
301
|
-
* `COUNT_RLS_UNSUPPORTED`.
|
|
302
|
-
*/
|
|
303
|
-
readonly MASK_UNSUPPORTED: 422;
|
|
304
|
-
readonly NOT_FOUND: 404;
|
|
305
|
-
readonly NOT_IMPLEMENTED: 501;
|
|
306
|
-
/**
|
|
307
|
-
* A write policy's `when` returned a relation-crossing predicate
|
|
308
|
-
* (`some`/`none`/`every`/`is`/`isNot`). The in-memory write-policy evaluator
|
|
309
|
-
* has no child fetcher and cannot resolve a relation node, so the policy is
|
|
310
|
-
* unsupported as written. Relation predicates are valid in *read* policies
|
|
311
|
-
* and query `where` clauses (the pre-resolver handles them there). The
|
|
312
|
-
* request is well-formed; this is a 422 (semantic conflict), mirroring the
|
|
313
|
-
* sibling `*_UNSUPPORTED` codes.
|
|
314
|
-
*/
|
|
315
|
-
readonly RELATION_PREDICATE_UNSUPPORTED: 422;
|
|
316
|
-
readonly TOO_MANY_REQUESTS: 429;
|
|
317
|
-
readonly UNAUTHORIZED: 401;
|
|
318
|
-
readonly UNPROCESSABLE: 422;
|
|
319
|
-
};
|
|
320
|
-
type LunoraErrorCode = keyof typeof CODE_STATUS;
|
|
321
|
-
declare class LunoraError extends Error {
|
|
322
|
-
override readonly name = "LunoraError";
|
|
323
|
-
readonly code: LunoraErrorCode;
|
|
324
|
-
readonly status: number;
|
|
325
|
-
constructor(code: LunoraErrorCode, message?: string);
|
|
326
|
-
}
|
|
327
|
-
/**
|
|
328
|
-
* The per-table `ctx.db` accessor (the `ctx.db.messages.findMany(...)` form) and
|
|
329
|
-
* the kitcn-style `ctx.orm` namespace, as plain runtime helpers. This is the ONE
|
|
330
|
-
* source of truth for the facade shape, shared by two callers so they can never
|
|
331
|
-
* drift (a drift here is security-relevant — a facade accessor the RLS
|
|
332
|
-
* middleware forgot to re-bind would read around policy). `@lunora/codegen`
|
|
333
|
-
* emits `ctx.db`/`ctx.orm` by calling these over the raw shard writer (and the
|
|
334
|
-
* D1 `globalDb` writer for `.global()` tables); the RLS middleware re-binds the
|
|
335
|
-
* policy tables by calling them over the policy-enforcing wrapped writer.
|
|
336
|
-
*
|
|
337
|
-
* `bindTableFacade(writer, table)` pins `tableName` on the structural writer so
|
|
338
|
-
* callers address rows by id (`get`/`delete`/`patch`/`replace`) or by the bound
|
|
339
|
-
* table (everything else). The binding is identical regardless of which writer
|
|
340
|
-
* is passed — that's the whole point.
|
|
341
|
-
*/
|
|
281
|
+
declare class LunoraError extends LunoraError$1 {
|
|
282
|
+
constructor(code: LunoraErrorCode, message?: string, data?: unknown);
|
|
283
|
+
}
|
|
342
284
|
/**
|
|
343
285
|
* Minimal structural writer the facade binds over. Declared with **method**
|
|
344
286
|
* syntax (not arrow properties) so a more-specifically-typed writer — both
|
|
@@ -349,12 +291,19 @@ declare class LunoraError extends Error {
|
|
|
349
291
|
interface FacadeWriterLike {
|
|
350
292
|
aggregate(tableName: string, options: unknown): Promise<unknown>;
|
|
351
293
|
count(tableName: string, where?: unknown): Promise<number>;
|
|
352
|
-
delete(id: string, expectedTable?: string
|
|
294
|
+
delete(id: string, expectedTable?: string, options?: {
|
|
295
|
+
hard?: boolean;
|
|
296
|
+
}): Promise<void>;
|
|
353
297
|
deleteMany?(ids: ReadonlyArray<string>, options?: {
|
|
354
298
|
limit?: number;
|
|
355
299
|
}, expectedTable?: string): Promise<{
|
|
356
300
|
deleted: number;
|
|
357
301
|
}>;
|
|
302
|
+
deleteWhere?(tableName: string, where: Record<string, unknown>, options?: {
|
|
303
|
+
limit?: number;
|
|
304
|
+
}): Promise<{
|
|
305
|
+
deleted: number;
|
|
306
|
+
}>;
|
|
358
307
|
findFirst(tableName: string, args?: unknown): Promise<unknown>;
|
|
359
308
|
findFirstOrThrow(tableName: string, args?: unknown): Promise<unknown>;
|
|
360
309
|
findMany(tableName: string, args?: unknown): Promise<unknown>;
|
|
@@ -363,52 +312,133 @@ interface FacadeWriterLike {
|
|
|
363
312
|
insert(tableName: string, document: Record<string, unknown>): Promise<string>;
|
|
364
313
|
insertMany?(tableName: string, documents: ReadonlyArray<Record<string, unknown>>, options?: {
|
|
365
314
|
limit?: number;
|
|
366
|
-
|
|
315
|
+
skipDuplicates?: boolean;
|
|
316
|
+
}): Promise<(string | null)[]>;
|
|
367
317
|
patch(id: string, patch: Record<string, unknown>, expectedTable?: string): Promise<void>;
|
|
368
318
|
patchMany?(patches: ReadonlyArray<{
|
|
369
319
|
id: string;
|
|
370
320
|
patch: Record<string, unknown>;
|
|
371
321
|
}>, options?: {
|
|
372
322
|
limit?: number;
|
|
373
|
-
}, 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
|
+
}>;
|
|
374
334
|
query(tableName: string): {
|
|
375
335
|
withSearchIndex(indexName: string, search: (q: unknown) => unknown): unknown;
|
|
376
336
|
};
|
|
377
337
|
rank(tableName: string, indexName: string, options: unknown): Promise<unknown>;
|
|
378
338
|
rankPage(tableName: string, indexName: string, options?: unknown): Promise<unknown>;
|
|
379
339
|
replace(id: string, document: Record<string, unknown>, expectedTable?: string): Promise<void>;
|
|
340
|
+
restore?(id: string, expectedTable?: string): Promise<void>;
|
|
380
341
|
}
|
|
381
342
|
/** The per-table accessor object returned for the `ctx.db` table form. */
|
|
382
343
|
interface FacadeEntry {
|
|
383
344
|
aggregate: (options: unknown) => Promise<unknown>;
|
|
384
345
|
count: (where?: unknown) => Promise<number>;
|
|
385
346
|
delete: (id: string) => Promise<void>;
|
|
386
|
-
deleteMany:
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
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
|
+
};
|
|
360
|
+
/** `true` when at least one row matches `where` (or any row exists when omitted). Honors RLS like `findFirst`. */
|
|
361
|
+
exists: (where?: unknown) => Promise<boolean>;
|
|
391
362
|
findFirst: (args?: unknown) => Promise<unknown>;
|
|
392
363
|
findFirstOrThrow: (args?: unknown) => Promise<unknown>;
|
|
393
364
|
findMany: (args?: unknown) => Promise<unknown>;
|
|
394
365
|
get: (id: string) => Promise<unknown>;
|
|
395
366
|
groupBy: (options: unknown) => Promise<unknown>;
|
|
396
|
-
|
|
367
|
+
/** Physically remove a row (and physically cascade), bypassing `.softDelete()`. */
|
|
368
|
+
hardDelete: (id: string) => Promise<void>;
|
|
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
|
+
*/
|
|
397
376
|
insertMany: (documents: ReadonlyArray<Record<string, unknown>>, options?: {
|
|
398
377
|
limit?: number;
|
|
399
|
-
|
|
378
|
+
skipDuplicates?: boolean;
|
|
379
|
+
}) => Promise<(string | null)[]>;
|
|
400
380
|
patch: (id: string, patch: Record<string, unknown>) => Promise<void>;
|
|
401
|
-
patchMany:
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
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
|
+
};
|
|
407
398
|
rank: (indexName: string, options: unknown) => Promise<unknown>;
|
|
408
399
|
rankPage: (indexName: string, options?: unknown) => Promise<unknown>;
|
|
409
400
|
replace: (id: string, document: Record<string, unknown>) => Promise<void>;
|
|
401
|
+
/** Un-soft-delete a row: clears the `.softDelete()` marker (by-id, so it reaches a row list reads hide). */
|
|
402
|
+
restore: (id: string) => Promise<void>;
|
|
403
|
+
/** Insert when no row matches `target`, else patch the match. Composes `findFirst` + `insert`/`patch`, so RLS applies to each step. */
|
|
404
|
+
upsert: (args: UpsertArgs) => Promise<UpsertResult>;
|
|
405
|
+
/** Sequential `upsert` over many rows sharing one `target`; returns one result per input row in order. */
|
|
406
|
+
upsertMany: (args: UpsertManyArgs) => Promise<UpsertResult[]>;
|
|
410
407
|
withSearchIndex: (indexName: string, search: (q: unknown) => unknown) => unknown;
|
|
411
408
|
}
|
|
409
|
+
/** Options accepted by the per-table `insert` accessor. */
|
|
410
|
+
interface FacadeInsertOptions {
|
|
411
|
+
/**
|
|
412
|
+
* When `true`, a UNIQUE-constraint breach is swallowed: the insert becomes a
|
|
413
|
+
* silent no-op and resolves to `null` instead of throwing a `CONFLICT`. Any
|
|
414
|
+
* other error still propagates. Mirrors better-drizzle's `create({ skipDuplicates })`.
|
|
415
|
+
*/
|
|
416
|
+
skipDuplicates?: boolean;
|
|
417
|
+
}
|
|
418
|
+
/** The conflict target for `upsert`/`upsertMany`: one field name or a tuple of them. */
|
|
419
|
+
type UpsertTarget = ReadonlyArray<string> | string;
|
|
420
|
+
/** Argument to the per-table `upsert` accessor. */
|
|
421
|
+
interface UpsertArgs {
|
|
422
|
+
/** Document inserted when no existing row matches the `target`. */
|
|
423
|
+
create: Record<string, unknown>;
|
|
424
|
+
/** Field(s) — typically a `.unique()` column or unique index — used to look up an existing row. */
|
|
425
|
+
target: UpsertTarget;
|
|
426
|
+
/** Patch applied when an existing row matches the `target`. Defaults to `create`. */
|
|
427
|
+
update?: Record<string, unknown>;
|
|
428
|
+
}
|
|
429
|
+
/** Result of an `upsert`: the row's id and whether it was freshly inserted (`true`) or updated (`false`). */
|
|
430
|
+
interface UpsertResult {
|
|
431
|
+
created: boolean;
|
|
432
|
+
id: string;
|
|
433
|
+
}
|
|
434
|
+
/** Argument to the per-table `upsertMany` accessor — a shared `target` plus per-row create/update payloads. */
|
|
435
|
+
interface UpsertManyArgs {
|
|
436
|
+
rows: ReadonlyArray<{
|
|
437
|
+
create: Record<string, unknown>;
|
|
438
|
+
update?: Record<string, unknown>;
|
|
439
|
+
}>;
|
|
440
|
+
target: UpsertTarget;
|
|
441
|
+
}
|
|
412
442
|
/**
|
|
413
443
|
* Bind a structural writer to one table, producing its `ctx.db` table accessor.
|
|
414
444
|
*
|
|
@@ -426,7 +456,7 @@ declare const bindTableFacade: (writer: FacadeWriterLike, tableName: string) =>
|
|
|
426
456
|
interface OrmLike {
|
|
427
457
|
delete: (table: string, id: string) => Promise<void>;
|
|
428
458
|
insert: (table: string) => {
|
|
429
|
-
values: (document: Record<string, unknown>) => Promise<string>;
|
|
459
|
+
values: (document: Record<string, unknown>) => Promise<null | string>;
|
|
430
460
|
};
|
|
431
461
|
query: Record<string, FacadeEntry>;
|
|
432
462
|
replace: (table: string, id: string) => {
|
|
@@ -447,7 +477,7 @@ type HttpMethod = "DELETE" | "GET" | "HEAD" | "OPTIONS" | "PATCH" | "POST" | "PU
|
|
|
447
477
|
* `storage` surface — reach the data layer through `runQuery` / `runMutation` /
|
|
448
478
|
* `runAction`, which forward to the owning shard.
|
|
449
479
|
*/
|
|
450
|
-
type HttpActionCtx = Pick<ActionCtx, "auth" | "fetch" | "runAction" | "runMutation" | "runQuery">;
|
|
480
|
+
type HttpActionCtx = Pick<ActionCtx, "auth" | "cache" | "fetch" | "runAction" | "runMutation" | "runQuery">;
|
|
451
481
|
/** A raw handler wrapped by {@link httpAction}. Receives the raw request, returns the raw response. */
|
|
452
482
|
type HttpActionHandler = (context: HttpActionCtx, request: Request) => Promise<Response> | Response;
|
|
453
483
|
/**
|
|
@@ -531,6 +561,16 @@ interface HttpStreamHandlerOptions<SearchParams extends ArgsValidator, Params ex
|
|
|
531
561
|
*/
|
|
532
562
|
interface HttpRouteBuilder<SearchParams extends ArgsValidator, Body extends ArgsValidator, Params extends ArgsValidator, Output = undefined> {
|
|
533
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>;
|
|
534
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;
|
|
535
575
|
output: <V extends Validator>(validator: V) => HttpRouteBuilder<SearchParams, Body, Params, Infer<V>>;
|
|
536
576
|
params: <P extends ArgsValidator>(validators: P) => HttpRouteBuilder<SearchParams, Body, P & Params, Output>;
|
|
@@ -545,6 +585,11 @@ interface HttpRouteBuilder<SearchParams extends ArgsValidator, Body extends Args
|
|
|
545
585
|
* handler's yielded type.
|
|
546
586
|
*/
|
|
547
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>;
|
|
548
593
|
}
|
|
549
594
|
/** Opens a fresh {@link HttpRouteBuilder}. The `path` documents intent; hono owns the actual routing at mount. */
|
|
550
595
|
type HttpRouteFactory = (path: string) => HttpRouteBuilder<EmptyArgs, EmptyArgs, EmptyArgs>;
|
|
@@ -616,6 +661,15 @@ interface ContextWithStorage {
|
|
|
616
661
|
storage: StorageDownloader;
|
|
617
662
|
}
|
|
618
663
|
/**
|
|
664
|
+
* True when `value` is safe to use as an HTTP header field-value: no CR, LF, or
|
|
665
|
+
* NUL. Guards against response-header injection / `Headers`-construction throws
|
|
666
|
+
* when reflecting attacker-influenced object metadata (e.g. a stored
|
|
667
|
+
* `Content-Type`). Exported (see the `export {}` at the file end) so an `httpAction`
|
|
668
|
+
* handler can guard a request-derived header value before writing it — the fix the
|
|
669
|
+
* `http_action_response_header_injection` advisor lint points to.
|
|
670
|
+
*/
|
|
671
|
+
declare const isSafeHeaderValue: (value: string) => boolean;
|
|
672
|
+
/**
|
|
619
673
|
* Stream a stored object as an HTTP {@link Response} from an `httpAction`
|
|
620
674
|
* handler, with correct `Content-Type`, `ETag`, and `Accept-Ranges: bytes`.
|
|
621
675
|
* Honors a single-range `Range` request → **206 Partial Content** with
|
|
@@ -631,6 +685,76 @@ interface ContextWithStorage {
|
|
|
631
685
|
* client then ranges against R2/CDN directly with no Worker hop.
|
|
632
686
|
*/
|
|
633
687
|
declare const serveStorageObject: (context: ContextWithStorage, key: string, request: Request) => Promise<Response>;
|
|
688
|
+
/**
|
|
689
|
+
* What the worker does with a resolver's identity when it fails contract
|
|
690
|
+
* validation (a forged / malformed claim set arriving from an untrusted token).
|
|
691
|
+
* `"anonymous"` (default, safe) treats the request as anonymous, so the bad
|
|
692
|
+
* identity never reaches a policy as a valid identity (`ctx.auth.userId`
|
|
693
|
+
* becomes `undefined`). `"reject"` fails the request closed (a `401`) — use
|
|
694
|
+
* when a malformed credential should be a hard error, not a silent downgrade.
|
|
695
|
+
*/
|
|
696
|
+
type IdentityRejectMode = "anonymous" | "reject";
|
|
697
|
+
/** Options for {@link defineIdentity}. */
|
|
698
|
+
interface DefineIdentityOptions {
|
|
699
|
+
/**
|
|
700
|
+
* How to handle a resolver identity that violates the contract at the trust
|
|
701
|
+
* boundary. Defaults to `"anonymous"` (a forged claim set is downgraded to
|
|
702
|
+
* anonymous rather than flowing in as an unchecked cast).
|
|
703
|
+
*/
|
|
704
|
+
readonly onInvalid?: IdentityRejectMode;
|
|
705
|
+
}
|
|
706
|
+
/** Result of validating a candidate identity against the contract. */
|
|
707
|
+
type IdentityValidation = {
|
|
708
|
+
ok: true;
|
|
709
|
+
} | {
|
|
710
|
+
error: string;
|
|
711
|
+
ok: false;
|
|
712
|
+
};
|
|
713
|
+
/**
|
|
714
|
+
* A declared identity claim contract. Carries the codegen discovery brand, the
|
|
715
|
+
* declared claim validators, the reject policy, and a runtime `validate`. The
|
|
716
|
+
* `TClaims` type parameter is the inferred claim shape (always extending
|
|
717
|
+
* `{ userId: string }`); it is phantom (no runtime field) and exists so
|
|
718
|
+
* `@lunora/codegen` and {@link InferIdentity} can recover the type.
|
|
719
|
+
*/
|
|
720
|
+
interface IdentityContract<TClaims extends {
|
|
721
|
+
userId: string;
|
|
722
|
+
} = {
|
|
723
|
+
userId: string;
|
|
724
|
+
}> {
|
|
725
|
+
/**
|
|
726
|
+
* Phantom carrier for the inferred claim type. Never populated at runtime
|
|
727
|
+
* (`undefined`); present only so the type flows to codegen / {@link InferIdentity}.
|
|
728
|
+
*/
|
|
729
|
+
readonly __claimType?: TClaims;
|
|
730
|
+
readonly __lunoraIdentity: true;
|
|
731
|
+
/** The declared claim validators (a `@lunora/values` validator map). */
|
|
732
|
+
readonly claims: ValidatorMap;
|
|
733
|
+
/** Reject policy applied at the trust boundary. See {@link IdentityRejectMode}. */
|
|
734
|
+
readonly onInvalid: IdentityRejectMode;
|
|
735
|
+
/**
|
|
736
|
+
* Validate a resolver's returned identity against the declared claims. On
|
|
737
|
+
* success the caller keeps the original identity untouched (so undeclared
|
|
738
|
+
* claims are forwarded verbatim, preserving today's behaviour); on failure
|
|
739
|
+
* the worker applies the `onInvalid` policy.
|
|
740
|
+
*/
|
|
741
|
+
validate: (identity: Record<string, unknown>) => IdentityValidation;
|
|
742
|
+
}
|
|
743
|
+
/** Recover the declared claim type from a {@link defineIdentity} contract. */
|
|
744
|
+
type InferIdentity<T> = T extends IdentityContract<infer TClaims> ? TClaims : never;
|
|
745
|
+
/**
|
|
746
|
+
* Declare the identity claim contract. `claims` is a `@lunora/values` validator
|
|
747
|
+
* map whose inferred type must extend `{ userId: string }` — if it does not
|
|
748
|
+
* (e.g. `userId` is missing or not a required string), the argument type
|
|
749
|
+
* collapses to `never` and the call fails to typecheck.
|
|
750
|
+
* @example
|
|
751
|
+
* export const identity = defineIdentity({ userId: v.string(), tenantId: v.optional(v.string()), scopes: v.optional(v.array(v.string())) });
|
|
752
|
+
*/
|
|
753
|
+
declare const defineIdentity: <A extends ValidatorMap>(claims: InferValidatorMap<A> extends {
|
|
754
|
+
userId: string;
|
|
755
|
+
} ? A : never, options?: DefineIdentityOptions) => IdentityContract<InferValidatorMap<A> & {
|
|
756
|
+
userId: string;
|
|
757
|
+
}>;
|
|
634
758
|
/** Handler for a connection-lifecycle hook. */
|
|
635
759
|
type LifecycleHandler = (context: MutationCtx, event: LifecycleEvent) => Promise<void> | void;
|
|
636
760
|
/** Register a hook that fires once when a client's WebSocket connects. */
|
|
@@ -761,6 +885,11 @@ interface MaskDatabase {
|
|
|
761
885
|
}) => Promise<{
|
|
762
886
|
deleted: number;
|
|
763
887
|
}>;
|
|
888
|
+
deleteWhere?: (tableName: string, where: Record<string, unknown>, options?: {
|
|
889
|
+
limit?: number;
|
|
890
|
+
}) => Promise<{
|
|
891
|
+
deleted: number;
|
|
892
|
+
}>;
|
|
764
893
|
findFirst: (tableName: string, args?: QueryArgs$1) => Promise<Record<string, unknown> | null>;
|
|
765
894
|
findFirstOrThrow: (tableName: string, args?: QueryArgs$1) => Promise<Record<string, unknown>>;
|
|
766
895
|
findMany: (tableName: string, args?: QueryArgs$1) => Promise<QueryPage$1>;
|
|
@@ -772,7 +901,8 @@ interface MaskDatabase {
|
|
|
772
901
|
insert: (tableName: string, document: Record<string, unknown>) => Promise<string>;
|
|
773
902
|
insertMany: (tableName: string, documents: ReadonlyArray<Record<string, unknown>>, options?: {
|
|
774
903
|
limit?: number;
|
|
775
|
-
|
|
904
|
+
skipDuplicates?: boolean;
|
|
905
|
+
}) => Promise<(string | null)[]>;
|
|
776
906
|
lookupById?: (id: string, expectedTable?: string) => Promise<null | {
|
|
777
907
|
row: Record<string, unknown>;
|
|
778
908
|
tableName: string;
|
|
@@ -783,7 +913,17 @@ interface MaskDatabase {
|
|
|
783
913
|
patch: Record<string, unknown>;
|
|
784
914
|
}>, options?: {
|
|
785
915
|
limit?: number;
|
|
786
|
-
}) => 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
|
+
}>;
|
|
787
927
|
query: (tableName: string) => TableReaderLike$1;
|
|
788
928
|
rank: (tableName: string, indexName: string, options: unknown) => Promise<null | {
|
|
789
929
|
position: number;
|
|
@@ -815,21 +955,6 @@ interface MaskContextIn {
|
|
|
815
955
|
* includes this middleware — opt-in, never global (the same invariant as RLS).
|
|
816
956
|
*/
|
|
817
957
|
declare const mask: <Context extends MaskContextIn = MaskContextIn>(policies: MaskPolicies<Context>, options?: MaskOptions<Context>) => Middleware<Context, Context>;
|
|
818
|
-
/**
|
|
819
|
-
* Online data-migration authoring API.
|
|
820
|
-
*
|
|
821
|
-
* `defineMigration` declares a per-document backfill over one table: `up`
|
|
822
|
-
* transforms every existing row, `down` (optional) reverses it. Unlike the D1
|
|
823
|
-
* SQL schema migrations in `@lunora/d1`, these run *inside each shard's*
|
|
824
|
-
* Durable Object against live documents, in keyset batches, and are resumable —
|
|
825
|
-
* the per-shard runner in `@lunora/do` tracks progress in a reserved
|
|
826
|
-
* `__lunora_migrations` table so an interrupted run picks up where it stopped.
|
|
827
|
-
*
|
|
828
|
-
* The returned object carries a `__lunoraMigration` brand so codegen can
|
|
829
|
-
* discover declarations through the type checker (mirroring the procedure
|
|
830
|
-
* builder's `__lunoraProcedure` brand) and emit them into a `LUNORA_MIGRATIONS`
|
|
831
|
-
* registry the DO and CLI look migrations up by id.
|
|
832
|
-
*/
|
|
833
958
|
/** A document handed to a migration transform: the stored row including `_id`/`_creationTime`. */
|
|
834
959
|
type MigrationDocument = Record<string, unknown>;
|
|
835
960
|
/**
|
|
@@ -858,6 +983,55 @@ interface RegisteredMigration extends MigrationDefinition {
|
|
|
858
983
|
/** Declare an online data migration. See the module docs for runtime semantics. */
|
|
859
984
|
declare const defineMigration: (definition: MigrationDefinition) => RegisteredMigration;
|
|
860
985
|
/**
|
|
986
|
+
* A mutator declaration. `server` is authoritative; `client` is the optimistic
|
|
987
|
+
* twin (optional — omit it to let the optimistic write fall through to the
|
|
988
|
+
* server round-trip with no local preview). Both receive the same validated
|
|
989
|
+
* `args`.
|
|
990
|
+
*/
|
|
991
|
+
interface MutatorDefinition<Args extends ValidatorMap = ValidatorMap, ServerContext = MutationCtx, ClientTx = unknown, R = unknown> {
|
|
992
|
+
/**
|
|
993
|
+
* Validator for the mutator's arguments. Validated on the DO before `server`
|
|
994
|
+
* runs and (when present) on the client before `client` runs, so both impls
|
|
995
|
+
* see the same parsed shape. Omit for a parameterless mutator.
|
|
996
|
+
*/
|
|
997
|
+
readonly args?: Args;
|
|
998
|
+
/**
|
|
999
|
+
* Optimistic client implementation. Runs in a TanStack DB transaction
|
|
1000
|
+
* against the local collections; its writes are applied immediately and
|
|
1001
|
+
* automatically rolled back / rebased as the authoritative result syncs
|
|
1002
|
+
* back. Pure and side-effect-free beyond the local store. Omit to skip the
|
|
1003
|
+
* local preview.
|
|
1004
|
+
*/
|
|
1005
|
+
readonly client?: (tx: ClientTx, args: InferValidatorMap<Args>) => Promise<void> | void;
|
|
1006
|
+
/**
|
|
1007
|
+
* Authoritative server implementation. Runs inside the shard DO with a full
|
|
1008
|
+
* {@link MutationContext} (`ctx.db` writer); its writes append to `__cdc_log`
|
|
1009
|
+
* and poke back to subscribers. This is the source of truth — the client
|
|
1010
|
+
* impl is only a prediction of it.
|
|
1011
|
+
*/
|
|
1012
|
+
readonly server: (context: ServerContext, args: InferValidatorMap<Args>) => Promise<R> | R;
|
|
1013
|
+
}
|
|
1014
|
+
/**
|
|
1015
|
+
* A {@link MutatorDefinition} plus the codegen discovery marker and a
|
|
1016
|
+
* dispatch-shaped `handler` (validates `args`, then runs `server`) so the DO
|
|
1017
|
+
* invokes a mutator exactly like a registered procedure.
|
|
1018
|
+
*/
|
|
1019
|
+
interface RegisteredMutator<Args extends ValidatorMap = ValidatorMap, ServerContext = MutationCtx, ClientTx = unknown, R = unknown> extends MutatorDefinition<Args, ServerContext, ClientTx, R> {
|
|
1020
|
+
readonly __lunoraMutator: true;
|
|
1021
|
+
/** Validate `rawArgs`, then run the authoritative `server` impl. Used by the DO push path. */
|
|
1022
|
+
readonly handler: (context: ServerContext, rawArgs: Record<string, unknown>) => Promise<R>;
|
|
1023
|
+
/**
|
|
1024
|
+
* Marks the dispatch kind so codegen can register the mutator in the same
|
|
1025
|
+
* `LUNORA_FUNCTIONS` table queries/mutations use — the DO's `handleRpc`
|
|
1026
|
+
* reads `kind === "mutation"` to wrap the authoritative `server` impl in the
|
|
1027
|
+
* shard's BEGIN/COMMIT span (all-or-nothing writes), exactly like an
|
|
1028
|
+
* ordinary `mutation`.
|
|
1029
|
+
*/
|
|
1030
|
+
readonly kind: "mutation";
|
|
1031
|
+
}
|
|
1032
|
+
/** Declare a custom mutator. See the module docs for runtime semantics. */
|
|
1033
|
+
declare const defineMutator: <Args extends ValidatorMap = ValidatorMap, ServerContext = MutationCtx, ClientTx = unknown, R = unknown>(definition: MutatorDefinition<Args, ServerContext, ClientTx, R>) => RegisteredMutator<Args, ServerContext, ClientTx, R>;
|
|
1034
|
+
/**
|
|
861
1035
|
* The prefixed tables a single plugin `P` contributes, or an empty map when it
|
|
862
1036
|
* ships no schema extension. Mirrors {@link PrefixedTables} at the plugin level
|
|
863
1037
|
* so {@link InstalledTables} can fold a tuple of plugins.
|
|
@@ -1168,6 +1342,35 @@ interface TableBuilder<Shape extends Record<string, Validator> = Record<string,
|
|
|
1168
1342
|
}) => TableBuilder<Shape>;
|
|
1169
1343
|
/** Route storage by the named field — one DO per distinct value. */
|
|
1170
1344
|
shardBy: (field: keyof Shape & string) => TableBuilder<Shape>;
|
|
1345
|
+
/**
|
|
1346
|
+
* Turn on soft delete. Adds a nullable timestamp column (`options.field`,
|
|
1347
|
+
* default `deletedAt`) and changes `ctx.db.<table>.delete()` to **set** it
|
|
1348
|
+
* instead of removing the row; `onDelete: "cascade"` children are recursively
|
|
1349
|
+
* soft-deleted too. **List reads** (`findMany`/`findFirst`/`query()`/`count`/
|
|
1350
|
+
* `aggregate`/relation loads) then hide soft-deleted rows unless they pass
|
|
1351
|
+
* `includeDeleted: true`; by-id `get`/`patch`/`replace` and the new
|
|
1352
|
+
* `restore()` still address the row directly. `hardDelete()` physically
|
|
1353
|
+
* removes it (cascading as a real delete). Note: `includeDeleted` is a read
|
|
1354
|
+
* scope, not access control — anyone who can run the read can set it; a unique
|
|
1355
|
+
* index still rejects a new row that collides with a soft-deleted one (the row
|
|
1356
|
+
* physically persists).
|
|
1357
|
+
*/
|
|
1358
|
+
softDelete: (options?: {
|
|
1359
|
+
field?: string;
|
|
1360
|
+
}) => TableBuilder<Shape>;
|
|
1361
|
+
/**
|
|
1362
|
+
* Materialize this table from an external Postgres/MySQL behind Cloudflare
|
|
1363
|
+
* Hyperdrive (plan 077). A system-driven poll loop reads the tenant slice
|
|
1364
|
+
* (`query`, with params bound from `tenantBy`) and lands it in the DO's SQLite,
|
|
1365
|
+
* after which `defineShape` carries it to clients unchanged. Implies
|
|
1366
|
+
* `.externallyManaged()` (rows come from the ingest loop, not user mutations).
|
|
1367
|
+
*
|
|
1368
|
+
* Orthogonal to `.shardBy()` — combine them for per-tenant DOs. **Under
|
|
1369
|
+
* `.shardBy()` `tenantBy` is mandatory** (the tenant-isolation boundary); the
|
|
1370
|
+
* `external_source_unscoped` advisor lint fails the build when it is absent, and
|
|
1371
|
+
* `external_source_on_global` rejects combining `.source()` with `.global()`.
|
|
1372
|
+
*/
|
|
1373
|
+
source: (definition: ExternalSourceDefinition) => TableBuilder<Shape>;
|
|
1171
1374
|
/** Declare named lifecycle triggers fired inline within the write path. */
|
|
1172
1375
|
triggers: (build: (t: TriggerBuilder<Shape>) => Record<string, TriggerDefinition>) => TableBuilder<Shape>;
|
|
1173
1376
|
/** Declare a vector index over a single text field on this table. */
|
|
@@ -1190,7 +1393,7 @@ interface VectorIndexOptions {
|
|
|
1190
1393
|
* Build a table definition. Returned object is both the table definition (for
|
|
1191
1394
|
* `defineSchema`) and a fluent builder for indexes + sharding metadata.
|
|
1192
1395
|
*/
|
|
1193
|
-
declare const defineTable: <Shape extends Record<string, Validator>>(
|
|
1396
|
+
declare const defineTable: <Shape extends Record<string, Validator>>(inputShape: Shape) => TableBuilder<Shape>;
|
|
1194
1397
|
/**
|
|
1195
1398
|
* Declare a standalone vector index (DSL Shape B). Pass the returned value in
|
|
1196
1399
|
* the `vectorIndexes` map of {@link defineSchema} when the source is derived
|
|
@@ -1262,6 +1465,28 @@ type ExtendableSchema<T extends Record<string, TableDefinition>> = {
|
|
|
1262
1465
|
readonly key: Key;
|
|
1263
1466
|
}) => ExtendableSchema<PrefixedTables<X, Key> & T>;
|
|
1264
1467
|
/**
|
|
1468
|
+
* Pin every Durable Object the app reaches — shards, fan-out, subscriptions,
|
|
1469
|
+
* the scheduler, and `ctx.containers` — to a Cloudflare data-residency
|
|
1470
|
+
* jurisdiction (`"eu"`, `"us"`, `"fedramp"`). Codegen reads this off the
|
|
1471
|
+
* schema and emits it into the generated worker's `createWorker({ jurisdiction })`
|
|
1472
|
+
* (and `ctx.scheduler` / `ctx.containers`). Non-mutating: returns a fresh
|
|
1473
|
+
* `ExtendableSchema`, so it composes with `.rls(...)` / `.extend(...)` in any order.
|
|
1474
|
+
*
|
|
1475
|
+
* ⚠️ **Set this once, before your first deploy — changing or removing it
|
|
1476
|
+
* strands data.** A Durable Object name maps to a *different* ID in each
|
|
1477
|
+
* jurisdiction, so toggling this on an existing app makes every shard, scheduler
|
|
1478
|
+
* job, and session DO resolve to a NEW, empty DO; the previous data stays in the
|
|
1479
|
+
* old jurisdiction's DOs and is no longer reachable. There is no in-place
|
|
1480
|
+
* migration — you would have to export from the old jurisdiction and import
|
|
1481
|
+
* into the new one.
|
|
1482
|
+
*
|
|
1483
|
+
* Note: this pins **DO-backed** state only. D1-backed state — `.global()`
|
|
1484
|
+
* tables and `@lunora/auth` sessions alike — is governed by D1's own location
|
|
1485
|
+
* settings, not this option.
|
|
1486
|
+
* @see https://developers.cloudflare.com/durable-objects/reference/data-location/
|
|
1487
|
+
*/
|
|
1488
|
+
jurisdiction: (jurisdiction: DurableObjectJurisdiction) => ExtendableSchema<T>;
|
|
1489
|
+
/**
|
|
1265
1490
|
* Turn on secure-by-default RLS for the whole schema. Every table is then
|
|
1266
1491
|
* protected — the DO/D1 write path denies raw, non-RLS `ctx.db` access, so a
|
|
1267
1492
|
* procedure that forgets `.use(rls(...))` fails closed. Opt a table out with
|
|
@@ -1433,7 +1658,7 @@ declare const definePolicy: <Context = unknown>(input: DefinePolicyInput<Context
|
|
|
1433
1658
|
* the compile-time surface narrows, so a policy authored either way is
|
|
1434
1659
|
* discovered identically by the `rls()` chain.
|
|
1435
1660
|
*/
|
|
1436
|
-
declare const createPolicyDsl: <DM, REL extends Record<keyof DM, object>>() => <T extends keyof DM, Context = unknown>(input: TypedDefinePolicyInput<DM, REL, T, Context>) => Policy<Context>;
|
|
1661
|
+
declare const createPolicyDsl: <DM, REL extends Record<keyof DM, object>, Identity = Record<string, unknown>>() => <T extends keyof DM, Context = unknown>(input: TypedDefinePolicyInput<DM, REL, T, Context, Identity>) => Policy<Context>;
|
|
1437
1662
|
/**
|
|
1438
1663
|
* Declare a named permission a policy can check with `ctx.auth.can(...)`. Grant
|
|
1439
1664
|
* it to a role through `defineRole`'s `permissions`, register those roles with
|
|
@@ -1562,12 +1787,19 @@ interface DatabaseWriterLike {
|
|
|
1562
1787
|
*/
|
|
1563
1788
|
aggregate: (tableName: string, options: AggregateArgs) => Promise<null | number>;
|
|
1564
1789
|
count: (tableName: string, whereOrArgs?: CountArgs | WhereInput) => Promise<number>;
|
|
1565
|
-
delete: (id: string, expectedTable?: string
|
|
1790
|
+
delete: (id: string, expectedTable?: string, options?: {
|
|
1791
|
+
hard?: boolean;
|
|
1792
|
+
}) => Promise<void>;
|
|
1566
1793
|
deleteMany: (ids: ReadonlyArray<string>, options?: {
|
|
1567
1794
|
limit?: number;
|
|
1568
1795
|
}, expectedTable?: string) => Promise<{
|
|
1569
1796
|
deleted: number;
|
|
1570
1797
|
}>;
|
|
1798
|
+
deleteWhere?: (tableName: string, where: WhereInput, options?: {
|
|
1799
|
+
limit?: number;
|
|
1800
|
+
}) => Promise<{
|
|
1801
|
+
deleted: number;
|
|
1802
|
+
}>;
|
|
1571
1803
|
findFirst: (tableName: string, args?: QueryArgs) => Promise<Record<string, unknown> | null>;
|
|
1572
1804
|
findFirstOrThrow: (tableName: string, args?: QueryArgs) => Promise<Record<string, unknown>>;
|
|
1573
1805
|
findMany: (tableName: string, args?: QueryArgs) => Promise<QueryPage>;
|
|
@@ -1584,7 +1816,8 @@ interface DatabaseWriterLike {
|
|
|
1584
1816
|
insert: (tableName: string, document: Record<string, unknown>) => Promise<string>;
|
|
1585
1817
|
insertMany: (tableName: string, documents: ReadonlyArray<Record<string, unknown>>, options?: {
|
|
1586
1818
|
limit?: number;
|
|
1587
|
-
|
|
1819
|
+
skipDuplicates?: boolean;
|
|
1820
|
+
}) => Promise<(string | null)[]>;
|
|
1588
1821
|
insertManyUnsafe: (tableName: string, documents: ReadonlyArray<Record<string, unknown>>, options?: {
|
|
1589
1822
|
allowExplicitId?: boolean;
|
|
1590
1823
|
limit?: number;
|
|
@@ -1607,7 +1840,17 @@ interface DatabaseWriterLike {
|
|
|
1607
1840
|
patch: Record<string, unknown>;
|
|
1608
1841
|
}>, options?: {
|
|
1609
1842
|
limit?: number;
|
|
1610
|
-
}, 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
|
+
}>;
|
|
1611
1854
|
query: (tableName: string) => TableReaderLike;
|
|
1612
1855
|
/**
|
|
1613
1856
|
* Rank a row within its partition. A position is a count-of-rows-before, so
|
|
@@ -1633,6 +1876,7 @@ interface DatabaseWriterLike {
|
|
|
1633
1876
|
*/
|
|
1634
1877
|
rankPage: (tableName: string, indexName: string, options?: RankPageArgs) => Promise<QueryPage>;
|
|
1635
1878
|
replace: (id: string, document: Record<string, unknown>, expectedTable?: string) => Promise<void>;
|
|
1879
|
+
restore?: (id: string, expectedTable?: string) => Promise<void>;
|
|
1636
1880
|
}
|
|
1637
1881
|
/**
|
|
1638
1882
|
* What a procedure's `ctx.db` must structurally satisfy for the middleware
|
|
@@ -1656,6 +1900,104 @@ interface RlsContextIn {
|
|
|
1656
1900
|
}
|
|
1657
1901
|
declare const rls: <Context extends RlsContextIn = RlsContextIn>(policies: ReadonlyArray<Policy<Context>>, options?: RlsOptions) => Middleware<Context, Context>;
|
|
1658
1902
|
/**
|
|
1903
|
+
* One `rls()` tag's read policies for a table, paired with the role→permission
|
|
1904
|
+
* grants of that SAME middleware. Keeping the role map per-group is what lets a
|
|
1905
|
+
* policy's `auth.can(...)` resolve against its own middleware's roles — never a
|
|
1906
|
+
* permission registered on a different `rls()` step.
|
|
1907
|
+
*/
|
|
1908
|
+
interface ScopedReadPolicies {
|
|
1909
|
+
readonly policies: ReadonlyArray<Policy>;
|
|
1910
|
+
readonly rolePermissions: ReadonlyMap<string, ReadonlySet<string>>;
|
|
1911
|
+
}
|
|
1912
|
+
/** Table-indexed read-policy groups, each scoped to the roles of the rls() middleware that declared it. */
|
|
1913
|
+
interface RlsReadRegistry {
|
|
1914
|
+
readonly byTable: ReadonlyMap<string, ReadonlyArray<ScopedReadPolicies>>;
|
|
1915
|
+
}
|
|
1916
|
+
/** The trusted, server-resolved facts a shape's RLS evaluation runs under. */
|
|
1917
|
+
interface ShapeReadWhereRequest {
|
|
1918
|
+
/** The shape ctx (the procedure context a policy `when` reads as `ctx`). */
|
|
1919
|
+
readonly ctx: unknown;
|
|
1920
|
+
/** Resolved identity claims (the socket's verified identity), or `null` when anonymous. */
|
|
1921
|
+
readonly identity: Record<string, unknown> | null;
|
|
1922
|
+
/** `true` when the schema is `.rls("required")` — gates the fail-closed branch. */
|
|
1923
|
+
readonly rlsRequired: boolean;
|
|
1924
|
+
/** Role labels the request carries (drives `auth.can(...)`). */
|
|
1925
|
+
readonly roles: ReadonlyArray<string>;
|
|
1926
|
+
/** The shape's own predicate (`where(ctx, args)`). */
|
|
1927
|
+
readonly shapeWhere: WhereInput;
|
|
1928
|
+
/** Logical table the shape replicates. */
|
|
1929
|
+
readonly table: string;
|
|
1930
|
+
/** `true` when the table is `.public()` (exempt from `.rls("required")` denial). */
|
|
1931
|
+
readonly tablePublic: boolean;
|
|
1932
|
+
/** Verified user id, or `null` when anonymous. */
|
|
1933
|
+
readonly userId: null | string;
|
|
1934
|
+
}
|
|
1935
|
+
/**
|
|
1936
|
+
* Build the read-policy registry from the registered functions (pass
|
|
1937
|
+
* `Object.values(LUNORA_FUNCTIONS)`). Only `on: "read"` policies are collected,
|
|
1938
|
+
* grouped per `rls()` middleware so each group keeps its own role→permission map
|
|
1939
|
+
* (a `(table, when)` pair is de-duplicated within a tag). A tag reused across
|
|
1940
|
+
* several procedures (a shared `const guard = rls(...)`) is folded once. This
|
|
1941
|
+
* mirrors the request-time `rls()` path exactly: a policy's `auth.can(...)`
|
|
1942
|
+
* resolves against the roles of the middleware that declared it, never a union.
|
|
1943
|
+
*/
|
|
1944
|
+
declare const buildRlsReadRegistry: (functions: Iterable<unknown>) => RlsReadRegistry;
|
|
1945
|
+
/**
|
|
1946
|
+
* Compute the effective `where` a shape replicates: the table's RLS read
|
|
1947
|
+
* base-where AND the shape's own predicate. Returns the shape predicate
|
|
1948
|
+
* unchanged for a table with no read policy (a `.public()` or non-RLS table),
|
|
1949
|
+
* and the FALSE sentinel (replicate nothing) when a `.rls("required")` schema
|
|
1950
|
+
* exposes a protected, policy-less table.
|
|
1951
|
+
*/
|
|
1952
|
+
declare const composeShapeReadWhere: (registry: RlsReadRegistry, request: ShapeReadWhereRequest) => WhereInput;
|
|
1953
|
+
/**
|
|
1954
|
+
* A shape declaration. `where` receives the trusted procedure context and the
|
|
1955
|
+
* validated client args and returns the same {@link WhereInput} shape the RLS
|
|
1956
|
+
* DSL uses, so the DO can AND-merge it with the table's read base-where via the
|
|
1957
|
+
* existing where-compiler (zero second predicate implementation).
|
|
1958
|
+
*/
|
|
1959
|
+
interface ShapeDefinition<Args extends ValidatorMap = ValidatorMap, Context = QueryCtx> {
|
|
1960
|
+
/**
|
|
1961
|
+
* Validator for the client-supplied shape parameters. Validated on the DO
|
|
1962
|
+
* before `where` runs, so a malformed `args` envelope is rejected at the
|
|
1963
|
+
* subscription boundary rather than silently widening the partition. Omit
|
|
1964
|
+
* for a parameterless shape.
|
|
1965
|
+
*/
|
|
1966
|
+
readonly args?: Args;
|
|
1967
|
+
/**
|
|
1968
|
+
* Project the replicated rows to these columns (the system columns `_id` and
|
|
1969
|
+
* `_creationTime` are always included). Omit to replicate every column. An
|
|
1970
|
+
* empty array is rejected — it would replicate no data, which is never the
|
|
1971
|
+
* intent.
|
|
1972
|
+
*/
|
|
1973
|
+
readonly columns?: ReadonlyArray<string>;
|
|
1974
|
+
/** Logical table this shape replicates a partition of. */
|
|
1975
|
+
readonly table: string;
|
|
1976
|
+
/**
|
|
1977
|
+
* Predicate selecting the rows this shape replicates. AND-composed with the
|
|
1978
|
+
* table's RLS read base-where on the DO. Runs server-side with a trusted
|
|
1979
|
+
* `ctx` (identity/auth the client can't forge) and the validated client
|
|
1980
|
+
* `args`; returns a {@link WhereInput} using the same operator set as the
|
|
1981
|
+
* SQL compiler (`eq`/`in`/`lt`/… + `AND`/`OR`/`NOT`).
|
|
1982
|
+
*/
|
|
1983
|
+
readonly where: (context: Context, args: InferValidatorMap<Args>) => WhereInput;
|
|
1984
|
+
}
|
|
1985
|
+
/** A {@link ShapeDefinition} plus the codegen discovery marker and a dispatch-shaped `compileWhere`. */
|
|
1986
|
+
interface RegisteredShape<Args extends ValidatorMap = ValidatorMap, Context = QueryCtx> extends ShapeDefinition<Args, Context> {
|
|
1987
|
+
readonly __lunoraShape: true;
|
|
1988
|
+
/**
|
|
1989
|
+
* Validate `rawArgs`, then evaluate `where` under the trusted `ctx` and
|
|
1990
|
+
* return its {@link WhereInput}. Used by the generated DO's `resolveShape`
|
|
1991
|
+
* override: `ctx` is erased to `unknown` at this dispatch boundary (the DO
|
|
1992
|
+
* builds it from the socket's verified identity and hands it back as the
|
|
1993
|
+
* concrete {@link QueryContext} the predicate expects), exactly like
|
|
1994
|
+
* `RegisteredLunoraFunction.handler` erases its context.
|
|
1995
|
+
*/
|
|
1996
|
+
readonly compileWhere: (context: unknown, rawArgs: Record<string, unknown>) => WhereInput;
|
|
1997
|
+
}
|
|
1998
|
+
/** Declare a replication shape. See the module docs for runtime semantics. */
|
|
1999
|
+
declare const defineShape: <Args extends ValidatorMap = ValidatorMap, Context = QueryCtx>(definition: ShapeDefinition<Args, Context>) => RegisteredShape<Args, Context>;
|
|
2000
|
+
/**
|
|
1659
2001
|
* Operations a storage rule can gate. `read` covers `download` / `getMetadata`
|
|
1660
2002
|
* / `getSignedUrl` / `getUrl`; `write` covers `store` / `generateUploadUrl`;
|
|
1661
2003
|
* `delete` is `delete`; `list` is a prefix listing (governed via the file
|
|
@@ -1738,4 +2080,4 @@ interface StorageContextIn {
|
|
|
1738
2080
|
}
|
|
1739
2081
|
declare const storageRules: <Context extends StorageContextIn = StorageContextIn>(rules: ReadonlyArray<StorageRule<Context>>, options?: StorageRulesOptions) => Middleware<Context, Context>;
|
|
1740
2082
|
declare const VERSION = "0.0.0";
|
|
1741
|
-
export { type ActionBuilder, type ActionCtx, type AggregateIndexDefinition, type AggregateIndexOptions, type AggregateOp, type ArgsValidator, type Component, type ComponentFunctions, type CreateOptions, type DataModelInit, type DefineComponentOptions, type DefinePluginOptions, type DefinePolicyInput, type DefinePresenceOptions, type DefineStorageRuleInput, type EmptyArgs, type EnvAccessor, type EnvKeyFailure, type EnvShape, type ExtendableSchema, type FacadeEntry, type FacadeWriterLike, type FunctionKind, type HttpActionCtx, type HttpActionHandler, type HttpMethod, type HttpRoute, type HttpRouteBuilder, type HttpRouteFactory, type HttpRouteHandlerOptions, type HttpStreamHandlerOptions, type InferArgs, type InferEnv, type InlineAggregateIndexOptions, type InlineRankIndexOptions, type InternalActionBuilder, type InternalMutationBuilder, type InternalQueryBuilder, type LifecycleEvent, type LifecycleHandler, type LunoraBuilders, LunoraEnvError, LunoraError, type
|
|
2083
|
+
export { type ActionBuilder, type ActionCtx, type AggregateIndexDefinition, type AggregateIndexOptions, type AggregateOp, type ArgsValidator, type Component, type ComponentFunctions, type CreateOptions, type DataModelInit, type DefineComponentOptions, type DefineIdentityOptions, type DefinePluginOptions, type DefinePolicyInput, type DefinePresenceOptions, type DefineStorageRuleInput, type DurableObjectJurisdiction, type EmptyArgs, type EnvAccessor, type EnvKeyFailure, type EnvShape, type ExtendableSchema, type FacadeEntry, type FacadeWriterLike, type FunctionKind, type HttpActionCtx, type HttpActionHandler, type HttpMethod, type HttpRoute, type HttpRouteBuilder, type HttpRouteFactory, type HttpRouteHandlerOptions, type HttpStreamHandlerOptions, type IdentityContract, type IdentityRejectMode, type IdentityValidation, type InferArgs, type InferEnv, type InferIdentity, type InlineAggregateIndexOptions, type InlineRankIndexOptions, type InternalActionBuilder, type InternalMutationBuilder, type InternalQueryBuilder, type LifecycleEvent, type LifecycleHandler, type LunoraBuilders, LunoraEnvError, LunoraError, type LunoraHttpApp, type LunoraHttpEnv, type LunoraRouteHandler, type ManyRelation, type MaskColumns, type MaskContext, type MaskFn, type MaskOptions, type MaskPolicies, type MaskStrategy, type Middleware, type MiddlewareNext, type MigrationDefinition, type MigrationDocument, type MigrationTransform, type MutationBuilder, type MutationCtx, type MutatorDefinition, type OnDeleteAction, type OneRelation, type OrmLike, DEFAULT_TTL_MS as PRESENCE_DEFAULT_TTL_MS, PRESENCE_TABLE, type Permission, type Plugin, type Policy, type PrefixedTables, type PresenceComponent, type PresenceFunctions, type PresenceMember, type ProtectPublicOptions, type QueryBuilder, type QueryCtx, type RankIndexDefinition, type RankIndexOptions, type RegisteredAction, type RegisteredFunction, type RegisteredLifecycleHook, type RegisteredMigration, type RegisteredMutation, type RegisteredMutator, type RegisteredQuery, type RegisteredShape, type RegisteredStream, type RelationBuilder, type RelationDefinition, type RlsOptions, type RlsReadRegistry, type Role, type Schema, type SchemaExtension, type ShapeDefinition, type ShapeReadWhereRequest, type StorageOperation, type StorageRule, type StorageRuleContext, type StorageRuleDecision, type StorageRulesOptions, type TableBuilder, type TableDefinition, type TerminalKind, type TriggerBuilder, type TriggerDefinition, type TypedDefinePolicyInput, VERSION, type VectorEmbedder, type VectorIndexDefinition, type VectorIndexOptions, type VectorMetric, type VectorizeOptions, type WhereInput, asBucketStorage, bindOrm, bindTableFacade, buildRlsReadRegistry, composePluginMiddleware, composeShapeReadWhere, createPolicyDsl, createSecrets, defineAggregateIndex, defineComponent, defineEnv, defineIdentity, defineMigration, defineMutator, definePermission, definePlugin, definePolicies, definePolicy, definePresence, defineRankIndex, defineRole, defineSchema, defineSchemaExtension, defineShape, defineStorageRule, defineStorageRules, defineTable, defineVectorIndex, httpAction, httpRoute, httpRouter, initLunora, installPlugins, isSafeHeaderValue, mask, mergeSchemaExtension, onConnect, onDisconnect, presenceExtension, protectPublic, redactSecrets, rls, serveStorageObject, storageRules };
|