@lunora/server 1.0.0-alpha.1 → 1.0.0-alpha.11

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.
Files changed (34) hide show
  1. package/README.md +5 -1
  2. package/__assets__/package-og.svg +1 -1
  3. package/dist/data-model.d.mts +104 -16
  4. package/dist/data-model.d.ts +104 -16
  5. package/dist/index.d.mts +342 -28
  6. package/dist/index.d.ts +342 -28
  7. package/dist/index.mjs +17 -12
  8. package/dist/packem_shared/{LunoraError-DhggBJZF.mjs → LunoraError-DN7Zhhvu.mjs} +4 -1
  9. package/dist/packem_shared/{definePresence-D5LtwGl0.mjs → PRESENCE_DEFAULT_TTL_MS-D8viLY1S.mjs} +4 -4
  10. package/dist/packem_shared/{bindTableFacade-DCuyr46L.mjs → bindOrm-Ce57S3N9.mjs} +58 -1
  11. package/dist/packem_shared/buildRlsReadRegistry-1jexWrb3.mjs +107 -0
  12. package/dist/packem_shared/createSecrets-TsIP9lOa.mjs +55 -0
  13. package/dist/packem_shared/{defineAggregateIndex-DzqxtAyV.mjs → defineAggregateIndex-ZdyU78gh.mjs} +58 -3
  14. package/dist/packem_shared/defineIdentity-B_9YD46A.mjs +34 -0
  15. package/dist/packem_shared/defineMutator-EIXAWhs9.mjs +11 -0
  16. package/dist/packem_shared/defineShape-CJ27Wx7o.mjs +17 -0
  17. package/dist/packem_shared/functions-Di9FUNkf.mjs +5 -0
  18. package/dist/packem_shared/{httpAction-B7FYUEgr.mjs → httpAction-FLwfsePg.mjs} +1 -1
  19. package/dist/packem_shared/{initLunora-CATvPsVt.mjs → initLunora-lxwHTEV3.mjs} +17 -3
  20. package/dist/packem_shared/{mask-CkZJHHMM.mjs → mask-BV_jNzsN.mjs} +2 -2
  21. package/dist/packem_shared/policy-tag-DvpVH2tv.mjs +13 -0
  22. package/dist/packem_shared/{rls-Zhf5wEeJ.mjs → rls-2Jhd0uev.mjs} +22 -4
  23. package/dist/packem_shared/{storageRules-4a30FSpI.mjs → storageRules-Cje6Woea.mjs} +1 -1
  24. package/dist/packem_shared/{types.d-DmvyEMD6.d.mts → types.d-BB3pjV0m.d.mts} +10 -4
  25. package/dist/packem_shared/{types.d-BDY0FYHK.d.ts → types.d-Cxl6ndhm.d.ts} +10 -4
  26. package/dist/rls/testing.d.mts +1 -1
  27. package/dist/rls/testing.d.ts +1 -1
  28. package/dist/rls/testing.mjs +1 -1
  29. package/dist/types.d.mts +130 -2
  30. package/dist/types.d.ts +130 -2
  31. package/package.json +5 -5
  32. /package/dist/packem_shared/{defineEnv-DjFkpkSP.mjs → LunoraEnvError-DjFkpkSP.mjs} +0 -0
  33. /package/dist/packem_shared/{defineSchemaExtension-Ck5_TUO8.mjs → composePluginMiddleware-Ck5_TUO8.mjs} +0 -0
  34. /package/dist/packem_shared/{definePolicy-De67zPDS.mjs → createPolicyDsl-De67zPDS.mjs} +0 -0
package/dist/types.d.mts CHANGED
@@ -5,6 +5,14 @@ type ArgsValidator = ValidatorMap;
5
5
  type InferArgs<A extends ArgsValidator> = InferValidatorMap<A>;
6
6
  /** Storage backend for a `.global()` table: D1 (default) or a Postgres/MySQL database via Cloudflare Hyperdrive (PlanetScale, Neon, …). */
7
7
  type GlobalBackend = "d1" | "hyperdrive";
8
+ /**
9
+ * Cloudflare Durable Object data-residency jurisdiction declared via
10
+ * `defineSchema(...).jurisdiction("…")`. Restricts where every DO the app
11
+ * reaches runs and persists data (GDPR, FedRAMP, US data residency). Widening
12
+ * union — Cloudflare adds values over time.
13
+ * @see https://developers.cloudflare.com/durable-objects/reference/data-location/
14
+ */
15
+ type DurableObjectJurisdiction = "eu" | "fedramp" | "us";
8
16
  /** How a table is routed at runtime. */
9
17
  type ShardMode = {
10
18
  backend?: GlobalBackend;
@@ -15,6 +23,51 @@ type ShardMode = {
15
23
  } | {
16
24
  kind: "root";
17
25
  };
26
+ /** Poll cadence for a sourced table — `"manual"` (pull only on an explicit trigger) or a fixed interval. */
27
+ type ExternalSourceRefresh = "manual" | {
28
+ everyMs: number;
29
+ };
30
+ /**
31
+ * Delete-detection mode for external-source ingest. `"full-pull"` (default) reads
32
+ * the whole tenant membership each tick and diffs it, so it observes upstream
33
+ * deletes; `"incremental"` pulls only changed rows (cheap) and is blind to deletes
34
+ * unless paired with a soft-delete column or a `reconcileEveryMs` full-pull sweep.
35
+ */
36
+ type ExternalSourceMode = "full-pull" | "incremental";
37
+ /**
38
+ * Config for `.source(...)` (plan 077): declares a table as **materialized from an
39
+ * external Postgres/MySQL behind Cloudflare Hyperdrive**, not written by user
40
+ * mutations. A system-driven poll loop reads the tenant slice and lands it in the
41
+ * DO's SQLite (via the validated CDC writer), after which `defineShape` carries it
42
+ * to clients unchanged. Orthogonal to `shardMode` — a sourced table almost always
43
+ * also `.shardBy()`s, in which case `tenantBy` is the mandatory tenant-isolation
44
+ * boundary (enforced by the `external_source_unscoped` advisor lint).
45
+ */
46
+ interface ExternalSourceDefinition {
47
+ /** The wrangler Hyperdrive binding name the poll loop reads from. */
48
+ binding: string;
49
+ /** Project the materialized rows to these columns (passed to the membership diff). Omit ⇒ the full mapped document. */
50
+ columns?: ReadonlyArray<string>;
51
+ /** Column whose value becomes the Lunora `_id`. Defaults to `"id"`. */
52
+ idColumn?: string;
53
+ /** Transform an external row into the stored document body. Omit ⇒ every selected column except `idColumn` is copied. */
54
+ map?: (row: Record<string, unknown>) => Record<string, unknown>;
55
+ /** Delete-detection mode. Defaults to `"full-pull"`. */
56
+ mode?: ExternalSourceMode;
57
+ /** The full tenant-membership query, with driver-native placeholders (`$1` / `?`). `tenantBy` binds its params. */
58
+ query: string;
59
+ /** `"incremental"` only: run a full-pull reconcile this often to garbage-collect tombstones the incremental path can't see. */
60
+ reconcileEveryMs?: number;
61
+ /** Poll cadence, or `"manual"`. Omit ⇒ the runtime's size-scaled default. */
62
+ refresh?: ExternalSourceRefresh;
63
+ /**
64
+ * **Mandatory under `.shardBy()`**: map this DO's shard key → the query's bound
65
+ * params, so a tenant DO can only ever pull its own rows. An unscoped sourced +
66
+ * sharded table replicates the whole multitenant table into every shard — the
67
+ * `external_source_unscoped` advisor lint fails the build when this is absent.
68
+ */
69
+ tenantBy?: (shardKey: string) => ReadonlyArray<unknown>;
70
+ }
18
71
  interface IndexDefinition {
19
72
  fields: ReadonlyArray<string>;
20
73
  name: string;
@@ -130,6 +183,14 @@ interface TableDefinition<Shape extends Record<string, Validator> = Record<strin
130
183
  * without scanning the underlying table.
131
184
  */
132
185
  aggregateIndexes: ReadonlyArray<AggregateIndexDefinition>;
186
+ /**
187
+ * Set by `.source(...)` (named `externalSource`, not `source`, so the data
188
+ * field doesn't collide with the fluent `.source()` builder method — same
189
+ * convention as `shardBy()`/`shardMode`). When present, the table is
190
+ * materialized from an external Hyperdrive-backed database by a system poll
191
+ * loop rather than user mutations. Implies `isExternallyManaged`.
192
+ */
193
+ externalSource?: ExternalSourceDefinition;
133
194
  indexes: ReadonlyArray<IndexDefinition>;
134
195
  /**
135
196
  * `true` when `.externallyManaged()` was called — the table's rows are
@@ -165,6 +226,20 @@ interface TableDefinition<Shape extends Record<string, Validator> = Record<strin
165
226
  shape: Shape;
166
227
  shardMode: ShardMode;
167
228
  /**
229
+ * Set by `.softDelete()` (named `softDeleteMode`, not `softDelete`, so the
230
+ * data field doesn't collide with the fluent `.softDelete()` builder method —
231
+ * same convention as `shardBy()`/`shardMode`). When present, the table carries
232
+ * a nullable timestamp column (`field`, default `deletedAt`):
233
+ * `ctx.db.&lt;table>.delete()` flips it instead of physically removing the row,
234
+ * and **list reads** (`findMany`/`findFirst`/`query()`/`count`/`aggregate`/
235
+ * relation loads) hide rows whose `field` is set unless
236
+ * `includeDeleted: true` is passed. By-id `get`/`patch`/`replace` and
237
+ * `restore` are unaffected. Absent ⇒ deletes are physical, as before.
238
+ */
239
+ softDeleteMode?: {
240
+ field: string;
241
+ };
242
+ /**
168
243
  * Declared lifecycle triggers keyed by accessor name; empty unless
169
244
  * `.triggers()` was called. Named `triggerMap` (not `triggers`) so the
170
245
  * fluent `.triggers((t) => …)` builder method doesn't collide with this
@@ -614,6 +689,31 @@ interface Workflows {
614
689
  /** Resolve the handle for a declared workflow by export name. */
615
690
  get: <Params = Record<string, unknown>>(name: string) => WorkflowHandle<Params>;
616
691
  }
692
+ /**
693
+ * Structural projection of workers-types' `SecretsStoreSecret` binding — the
694
+ * per-secret `secrets_store_secrets[]` binding whose `.get()` resolves the
695
+ * secret value (or throws if it does not exist). Mirrored structurally so the
696
+ * runtime resolves it without a workerd type dependency.
697
+ */
698
+ interface SecretsStoreSecretLike {
699
+ get: () => Promise<string>;
700
+ }
701
+ /**
702
+ * `ctx.secrets` — read account-level secrets bound via Cloudflare Secrets Store.
703
+ * A core built-in (always present on every context, like `ctx.log`): a binding
704
+ * named in wrangler's `secrets_store_secrets[]` is read by its binding name.
705
+ *
706
+ * ```ts
707
+ * const apiKey = await ctx.secrets.get("STRIPE_KEY");
708
+ * ```
709
+ *
710
+ * The lookup is async (the platform fetches and decrypts on first read);
711
+ * reading an undeclared name throws a directed error naming the bound secrets.
712
+ */
713
+ interface Secrets {
714
+ /** Resolve a Secrets Store secret by its wrangler binding name. */
715
+ get: (name: string) => Promise<string>;
716
+ }
617
717
  /** Lifecycle phase relative to the SQL write. */
618
718
  type TriggerTiming = "after" | "before";
619
719
  /** The CRUD operation a trigger reacts to. `patch` and `replace` both map to `update`. */
@@ -899,7 +999,7 @@ interface VectorRecord {
899
999
  }
900
1000
  /**
901
1001
  * Read-only vector surface exposed on {@link QueryCtx}. Mirrors the read half
902
- * of `@lunora/vectors`' `LunoraVectors` so the live adapter is assignable.
1002
+ * of `@lunora/bindings/vectors`' `LunoraVectors` so the live adapter is assignable.
903
1003
  */
904
1004
  interface VectorSearchReader {
905
1005
  getByIds: (indexName: string, ids: ReadonlyArray<string>) => Promise<ReadonlyArray<VectorRecord>>;
@@ -953,6 +1053,14 @@ interface QueryCtx {
953
1053
  /** Structured, function-attributed logger; see {@link LunoraLogger}. */
954
1054
  readonly log: LunoraLogger;
955
1055
  /**
1056
+ * Wall-clock time (epoch ms) the function began, captured once so the whole
1057
+ * handler sees a single stable value. Query/mutation handlers must be
1058
+ * deterministic — they may be re-run on OCC retry / subscription re-eval — so
1059
+ * read time through `ctx.now` instead of `Date.now()` (the latter is flagged
1060
+ * by the `nondeterministic_query_mutation` advisor). Actions may use `Date.now()`.
1061
+ */
1062
+ readonly now: number;
1063
+ /**
956
1064
  * Compose a read-only subquery in-process, reusing this query's read
957
1065
  * context (same transaction, same `db`). Executes the referenced query's
958
1066
  * handler directly — no fresh DO RPC round-trip — so it observes the exact
@@ -961,6 +1069,8 @@ interface QueryCtx {
961
1069
  * Mirrors Convex's `ctx.runQuery`.
962
1070
  */
963
1071
  readonly runQuery: <A extends ArgsValidator, R>(reference: RegisteredQuery<A, R>, args: InferArgs<A>) => Promise<R>;
1072
+ /** Read account-level secrets from Cloudflare Secrets Store; see {@link Secrets}. */
1073
+ readonly secrets: Secrets;
964
1074
  readonly storage: ReadOnlyStorage;
965
1075
  readonly vectors: VectorSearchReader;
966
1076
  }
@@ -977,6 +1087,14 @@ interface MutationCtx {
977
1087
  /** Structured, function-attributed logger; see {@link LunoraLogger}. */
978
1088
  readonly log: LunoraLogger;
979
1089
  /**
1090
+ * Wall-clock time (epoch ms) the function began, captured once so the whole
1091
+ * handler sees a single stable value. Mutation handlers must be deterministic
1092
+ * — they may be re-run on OCC retry — so read time through `ctx.now` instead
1093
+ * of `Date.now()` (the latter is flagged by the `nondeterministic_query_mutation`
1094
+ * advisor). Actions may use `Date.now()`.
1095
+ */
1096
+ readonly now: number;
1097
+ /**
980
1098
  * Compose a submutation in-process, reusing this mutation's `db` writer.
981
1099
  * Executes the referenced mutation's handler directly — no fresh DO RPC —
982
1100
  * so its writes apply through the same shard invocation as the enclosing
@@ -993,6 +1111,8 @@ interface MutationCtx {
993
1111
  */
994
1112
  readonly runQuery: <A extends ArgsValidator, R>(reference: RegisteredQuery<A, R>, args: InferArgs<A>) => Promise<R>;
995
1113
  readonly scheduler: Scheduler;
1114
+ /** Read account-level secrets from Cloudflare Secrets Store; see {@link Secrets}. */
1115
+ readonly secrets: Secrets;
996
1116
  readonly storage: ReadOnlyStorage;
997
1117
  readonly vectors: VectorSearch;
998
1118
  /** Start / resume / inspect durable workflows; see {@link Workflows}. */
@@ -1011,10 +1131,18 @@ interface ActionCtx {
1011
1131
  readonly ip?: string;
1012
1132
  /** Structured, function-attributed logger; see {@link LunoraLogger}. */
1013
1133
  readonly log: LunoraLogger;
1134
+ /**
1135
+ * Wall-clock time (epoch ms) the action began, captured once for convenience
1136
+ * and parity with query/mutation `ctx.now`. Actions run exactly once, so they
1137
+ * may also use ambient `Date.now()` freely.
1138
+ */
1139
+ readonly now: number;
1014
1140
  readonly runAction: <A extends ArgsValidator, R>(reference: RegisteredAction<A, R>, args: InferArgs<A>) => Promise<R>;
1015
1141
  readonly runMutation: <A extends ArgsValidator, R>(reference: RegisteredMutation<A, R>, args: InferArgs<A>) => Promise<R>;
1016
1142
  readonly runQuery: <A extends ArgsValidator, R>(reference: RegisteredQuery<A, R>, args: InferArgs<A>) => Promise<R>;
1017
1143
  readonly scheduler: Scheduler;
1144
+ /** Read account-level secrets from Cloudflare Secrets Store; see {@link Secrets}. */
1145
+ readonly secrets: Secrets;
1018
1146
  readonly storage: Storage;
1019
1147
  readonly vectors: VectorSearch;
1020
1148
  /** Start / resume / inspect durable workflows; see {@link Workflows}. */
@@ -1026,4 +1154,4 @@ interface ActionCtx {
1026
1154
  */
1027
1155
  type AnyApi = Record<string, Record<string, RegisteredFunction<ArgsValidator, unknown, FunctionKind>>>;
1028
1156
  declare const anyApi: AnyApi;
1029
- export { type ActionCtx, type AggregateIndexDefinition, type AggregateOp, type AnyApi, type ArgsValidator, type AuthState, type DatabaseReader, type DatabaseWriter, 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 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 };
1157
+ 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 };
package/dist/types.d.ts CHANGED
@@ -5,6 +5,14 @@ type ArgsValidator = ValidatorMap;
5
5
  type InferArgs<A extends ArgsValidator> = InferValidatorMap<A>;
6
6
  /** Storage backend for a `.global()` table: D1 (default) or a Postgres/MySQL database via Cloudflare Hyperdrive (PlanetScale, Neon, …). */
7
7
  type GlobalBackend = "d1" | "hyperdrive";
8
+ /**
9
+ * Cloudflare Durable Object data-residency jurisdiction declared via
10
+ * `defineSchema(...).jurisdiction("…")`. Restricts where every DO the app
11
+ * reaches runs and persists data (GDPR, FedRAMP, US data residency). Widening
12
+ * union — Cloudflare adds values over time.
13
+ * @see https://developers.cloudflare.com/durable-objects/reference/data-location/
14
+ */
15
+ type DurableObjectJurisdiction = "eu" | "fedramp" | "us";
8
16
  /** How a table is routed at runtime. */
9
17
  type ShardMode = {
10
18
  backend?: GlobalBackend;
@@ -15,6 +23,51 @@ type ShardMode = {
15
23
  } | {
16
24
  kind: "root";
17
25
  };
26
+ /** Poll cadence for a sourced table — `"manual"` (pull only on an explicit trigger) or a fixed interval. */
27
+ type ExternalSourceRefresh = "manual" | {
28
+ everyMs: number;
29
+ };
30
+ /**
31
+ * Delete-detection mode for external-source ingest. `"full-pull"` (default) reads
32
+ * the whole tenant membership each tick and diffs it, so it observes upstream
33
+ * deletes; `"incremental"` pulls only changed rows (cheap) and is blind to deletes
34
+ * unless paired with a soft-delete column or a `reconcileEveryMs` full-pull sweep.
35
+ */
36
+ type ExternalSourceMode = "full-pull" | "incremental";
37
+ /**
38
+ * Config for `.source(...)` (plan 077): declares a table as **materialized from an
39
+ * external Postgres/MySQL behind Cloudflare Hyperdrive**, not written by user
40
+ * mutations. A system-driven poll loop reads the tenant slice and lands it in the
41
+ * DO's SQLite (via the validated CDC writer), after which `defineShape` carries it
42
+ * to clients unchanged. Orthogonal to `shardMode` — a sourced table almost always
43
+ * also `.shardBy()`s, in which case `tenantBy` is the mandatory tenant-isolation
44
+ * boundary (enforced by the `external_source_unscoped` advisor lint).
45
+ */
46
+ interface ExternalSourceDefinition {
47
+ /** The wrangler Hyperdrive binding name the poll loop reads from. */
48
+ binding: string;
49
+ /** Project the materialized rows to these columns (passed to the membership diff). Omit ⇒ the full mapped document. */
50
+ columns?: ReadonlyArray<string>;
51
+ /** Column whose value becomes the Lunora `_id`. Defaults to `"id"`. */
52
+ idColumn?: string;
53
+ /** Transform an external row into the stored document body. Omit ⇒ every selected column except `idColumn` is copied. */
54
+ map?: (row: Record<string, unknown>) => Record<string, unknown>;
55
+ /** Delete-detection mode. Defaults to `"full-pull"`. */
56
+ mode?: ExternalSourceMode;
57
+ /** The full tenant-membership query, with driver-native placeholders (`$1` / `?`). `tenantBy` binds its params. */
58
+ query: string;
59
+ /** `"incremental"` only: run a full-pull reconcile this often to garbage-collect tombstones the incremental path can't see. */
60
+ reconcileEveryMs?: number;
61
+ /** Poll cadence, or `"manual"`. Omit ⇒ the runtime's size-scaled default. */
62
+ refresh?: ExternalSourceRefresh;
63
+ /**
64
+ * **Mandatory under `.shardBy()`**: map this DO's shard key → the query's bound
65
+ * params, so a tenant DO can only ever pull its own rows. An unscoped sourced +
66
+ * sharded table replicates the whole multitenant table into every shard — the
67
+ * `external_source_unscoped` advisor lint fails the build when this is absent.
68
+ */
69
+ tenantBy?: (shardKey: string) => ReadonlyArray<unknown>;
70
+ }
18
71
  interface IndexDefinition {
19
72
  fields: ReadonlyArray<string>;
20
73
  name: string;
@@ -130,6 +183,14 @@ interface TableDefinition<Shape extends Record<string, Validator> = Record<strin
130
183
  * without scanning the underlying table.
131
184
  */
132
185
  aggregateIndexes: ReadonlyArray<AggregateIndexDefinition>;
186
+ /**
187
+ * Set by `.source(...)` (named `externalSource`, not `source`, so the data
188
+ * field doesn't collide with the fluent `.source()` builder method — same
189
+ * convention as `shardBy()`/`shardMode`). When present, the table is
190
+ * materialized from an external Hyperdrive-backed database by a system poll
191
+ * loop rather than user mutations. Implies `isExternallyManaged`.
192
+ */
193
+ externalSource?: ExternalSourceDefinition;
133
194
  indexes: ReadonlyArray<IndexDefinition>;
134
195
  /**
135
196
  * `true` when `.externallyManaged()` was called — the table's rows are
@@ -165,6 +226,20 @@ interface TableDefinition<Shape extends Record<string, Validator> = Record<strin
165
226
  shape: Shape;
166
227
  shardMode: ShardMode;
167
228
  /**
229
+ * Set by `.softDelete()` (named `softDeleteMode`, not `softDelete`, so the
230
+ * data field doesn't collide with the fluent `.softDelete()` builder method —
231
+ * same convention as `shardBy()`/`shardMode`). When present, the table carries
232
+ * a nullable timestamp column (`field`, default `deletedAt`):
233
+ * `ctx.db.&lt;table>.delete()` flips it instead of physically removing the row,
234
+ * and **list reads** (`findMany`/`findFirst`/`query()`/`count`/`aggregate`/
235
+ * relation loads) hide rows whose `field` is set unless
236
+ * `includeDeleted: true` is passed. By-id `get`/`patch`/`replace` and
237
+ * `restore` are unaffected. Absent ⇒ deletes are physical, as before.
238
+ */
239
+ softDeleteMode?: {
240
+ field: string;
241
+ };
242
+ /**
168
243
  * Declared lifecycle triggers keyed by accessor name; empty unless
169
244
  * `.triggers()` was called. Named `triggerMap` (not `triggers`) so the
170
245
  * fluent `.triggers((t) => …)` builder method doesn't collide with this
@@ -614,6 +689,31 @@ interface Workflows {
614
689
  /** Resolve the handle for a declared workflow by export name. */
615
690
  get: <Params = Record<string, unknown>>(name: string) => WorkflowHandle<Params>;
616
691
  }
692
+ /**
693
+ * Structural projection of workers-types' `SecretsStoreSecret` binding — the
694
+ * per-secret `secrets_store_secrets[]` binding whose `.get()` resolves the
695
+ * secret value (or throws if it does not exist). Mirrored structurally so the
696
+ * runtime resolves it without a workerd type dependency.
697
+ */
698
+ interface SecretsStoreSecretLike {
699
+ get: () => Promise<string>;
700
+ }
701
+ /**
702
+ * `ctx.secrets` — read account-level secrets bound via Cloudflare Secrets Store.
703
+ * A core built-in (always present on every context, like `ctx.log`): a binding
704
+ * named in wrangler's `secrets_store_secrets[]` is read by its binding name.
705
+ *
706
+ * ```ts
707
+ * const apiKey = await ctx.secrets.get("STRIPE_KEY");
708
+ * ```
709
+ *
710
+ * The lookup is async (the platform fetches and decrypts on first read);
711
+ * reading an undeclared name throws a directed error naming the bound secrets.
712
+ */
713
+ interface Secrets {
714
+ /** Resolve a Secrets Store secret by its wrangler binding name. */
715
+ get: (name: string) => Promise<string>;
716
+ }
617
717
  /** Lifecycle phase relative to the SQL write. */
618
718
  type TriggerTiming = "after" | "before";
619
719
  /** The CRUD operation a trigger reacts to. `patch` and `replace` both map to `update`. */
@@ -899,7 +999,7 @@ interface VectorRecord {
899
999
  }
900
1000
  /**
901
1001
  * Read-only vector surface exposed on {@link QueryCtx}. Mirrors the read half
902
- * of `@lunora/vectors`' `LunoraVectors` so the live adapter is assignable.
1002
+ * of `@lunora/bindings/vectors`' `LunoraVectors` so the live adapter is assignable.
903
1003
  */
904
1004
  interface VectorSearchReader {
905
1005
  getByIds: (indexName: string, ids: ReadonlyArray<string>) => Promise<ReadonlyArray<VectorRecord>>;
@@ -953,6 +1053,14 @@ interface QueryCtx {
953
1053
  /** Structured, function-attributed logger; see {@link LunoraLogger}. */
954
1054
  readonly log: LunoraLogger;
955
1055
  /**
1056
+ * Wall-clock time (epoch ms) the function began, captured once so the whole
1057
+ * handler sees a single stable value. Query/mutation handlers must be
1058
+ * deterministic — they may be re-run on OCC retry / subscription re-eval — so
1059
+ * read time through `ctx.now` instead of `Date.now()` (the latter is flagged
1060
+ * by the `nondeterministic_query_mutation` advisor). Actions may use `Date.now()`.
1061
+ */
1062
+ readonly now: number;
1063
+ /**
956
1064
  * Compose a read-only subquery in-process, reusing this query's read
957
1065
  * context (same transaction, same `db`). Executes the referenced query's
958
1066
  * handler directly — no fresh DO RPC round-trip — so it observes the exact
@@ -961,6 +1069,8 @@ interface QueryCtx {
961
1069
  * Mirrors Convex's `ctx.runQuery`.
962
1070
  */
963
1071
  readonly runQuery: <A extends ArgsValidator, R>(reference: RegisteredQuery<A, R>, args: InferArgs<A>) => Promise<R>;
1072
+ /** Read account-level secrets from Cloudflare Secrets Store; see {@link Secrets}. */
1073
+ readonly secrets: Secrets;
964
1074
  readonly storage: ReadOnlyStorage;
965
1075
  readonly vectors: VectorSearchReader;
966
1076
  }
@@ -977,6 +1087,14 @@ interface MutationCtx {
977
1087
  /** Structured, function-attributed logger; see {@link LunoraLogger}. */
978
1088
  readonly log: LunoraLogger;
979
1089
  /**
1090
+ * Wall-clock time (epoch ms) the function began, captured once so the whole
1091
+ * handler sees a single stable value. Mutation handlers must be deterministic
1092
+ * — they may be re-run on OCC retry — so read time through `ctx.now` instead
1093
+ * of `Date.now()` (the latter is flagged by the `nondeterministic_query_mutation`
1094
+ * advisor). Actions may use `Date.now()`.
1095
+ */
1096
+ readonly now: number;
1097
+ /**
980
1098
  * Compose a submutation in-process, reusing this mutation's `db` writer.
981
1099
  * Executes the referenced mutation's handler directly — no fresh DO RPC —
982
1100
  * so its writes apply through the same shard invocation as the enclosing
@@ -993,6 +1111,8 @@ interface MutationCtx {
993
1111
  */
994
1112
  readonly runQuery: <A extends ArgsValidator, R>(reference: RegisteredQuery<A, R>, args: InferArgs<A>) => Promise<R>;
995
1113
  readonly scheduler: Scheduler;
1114
+ /** Read account-level secrets from Cloudflare Secrets Store; see {@link Secrets}. */
1115
+ readonly secrets: Secrets;
996
1116
  readonly storage: ReadOnlyStorage;
997
1117
  readonly vectors: VectorSearch;
998
1118
  /** Start / resume / inspect durable workflows; see {@link Workflows}. */
@@ -1011,10 +1131,18 @@ interface ActionCtx {
1011
1131
  readonly ip?: string;
1012
1132
  /** Structured, function-attributed logger; see {@link LunoraLogger}. */
1013
1133
  readonly log: LunoraLogger;
1134
+ /**
1135
+ * Wall-clock time (epoch ms) the action began, captured once for convenience
1136
+ * and parity with query/mutation `ctx.now`. Actions run exactly once, so they
1137
+ * may also use ambient `Date.now()` freely.
1138
+ */
1139
+ readonly now: number;
1014
1140
  readonly runAction: <A extends ArgsValidator, R>(reference: RegisteredAction<A, R>, args: InferArgs<A>) => Promise<R>;
1015
1141
  readonly runMutation: <A extends ArgsValidator, R>(reference: RegisteredMutation<A, R>, args: InferArgs<A>) => Promise<R>;
1016
1142
  readonly runQuery: <A extends ArgsValidator, R>(reference: RegisteredQuery<A, R>, args: InferArgs<A>) => Promise<R>;
1017
1143
  readonly scheduler: Scheduler;
1144
+ /** Read account-level secrets from Cloudflare Secrets Store; see {@link Secrets}. */
1145
+ readonly secrets: Secrets;
1018
1146
  readonly storage: Storage;
1019
1147
  readonly vectors: VectorSearch;
1020
1148
  /** Start / resume / inspect durable workflows; see {@link Workflows}. */
@@ -1026,4 +1154,4 @@ interface ActionCtx {
1026
1154
  */
1027
1155
  type AnyApi = Record<string, Record<string, RegisteredFunction<ArgsValidator, unknown, FunctionKind>>>;
1028
1156
  declare const anyApi: AnyApi;
1029
- export { type ActionCtx, type AggregateIndexDefinition, type AggregateOp, type AnyApi, type ArgsValidator, type AuthState, type DatabaseReader, type DatabaseWriter, 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 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 };
1157
+ 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 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/server",
3
- "version": "1.0.0-alpha.1",
3
+ "version": "1.0.0-alpha.11",
4
4
  "description": "Server primitives for Lunora: defineSchema, defineTable, query, mutation, and action",
5
5
  "keywords": [
6
6
  "backend",
@@ -25,7 +25,7 @@
25
25
  "directory": "packages/server"
26
26
  },
27
27
  "files": [
28
- "dist",
28
+ "./dist",
29
29
  "README.md",
30
30
  "LICENSE.md",
31
31
  "__assets__"
@@ -62,10 +62,10 @@
62
62
  "access": "public"
63
63
  },
64
64
  "dependencies": {
65
- "@lunora/scheduler": "1.0.0-alpha.1",
66
- "@lunora/values": "1.0.0-alpha.1",
65
+ "@lunora/scheduler": "1.0.0-alpha.4",
66
+ "@lunora/values": "1.0.0-alpha.3",
67
67
  "drizzle-orm": "^0.45.2",
68
- "hono": "^4.12.26"
68
+ "hono": "^4.12.27"
69
69
  },
70
70
  "engines": {
71
71
  "node": "^22.15.0 || >=24.11.0"