@lunora/server 1.0.0-alpha.53 → 1.0.0-alpha.55

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -70,6 +70,17 @@ interface QueryBuilder<Context, Args extends ArgsValidator, Output = undefined>
70
70
  */
71
71
  expose: (config: ExposeConfig) => QueryBuilder<Context, Args, Output>;
72
72
  input: <A extends ArgsValidator>(validators: A) => QueryBuilder<Context, A & Args, Output>;
73
+ /**
74
+ * Attach static, per-procedure metadata. Merges across calls, is readable
75
+ * from middleware as `ctx.meta`, and is stamped onto the registration as
76
+ * `fn.meta` so codegen and other tooling can enumerate it.
77
+ *
78
+ * The point is policy that is DATA rather than a call: `.meta({ rateLimit:
79
+ * "pins/create" })` can be walked to generate a rate-limit registry or docs,
80
+ * where the same policy expressed only as `.use(rateLimit("pins/create"))`
81
+ * can only be executed. Mirrors tRPC's `.meta()`.
82
+ */
83
+ meta: (value: Record<string, unknown>) => QueryBuilder<Context, Args, Output>;
73
84
  output: <V extends Validator>(validator: V) => QueryBuilder<Context, Args, Infer<V>>;
74
85
  query: [Output] extends [undefined] ? <R>(handler: (options: {
75
86
  args: InferArgs<Args>;
@@ -110,6 +121,17 @@ interface MutationBuilder<Context, Args extends ArgsValidator, Output = undefine
110
121
  */
111
122
  expose: (config: ExposeConfig) => MutationBuilder<Context, Args, Output>;
112
123
  input: <A extends ArgsValidator>(validators: A) => MutationBuilder<Context, A & Args, Output>;
124
+ /**
125
+ * Attach static, per-procedure metadata. Merges across calls, is readable
126
+ * from middleware as `ctx.meta`, and is stamped onto the registration as
127
+ * `fn.meta` so codegen and other tooling can enumerate it.
128
+ *
129
+ * The point is policy that is DATA rather than a call: `.meta({ rateLimit:
130
+ * "pins/create" })` can be walked to generate a rate-limit registry or docs,
131
+ * where the same policy expressed only as `.use(rateLimit("pins/create"))`
132
+ * can only be executed. Mirrors tRPC's `.meta()`.
133
+ */
134
+ meta: (value: Record<string, unknown>) => MutationBuilder<Context, Args, Output>;
113
135
  mutation: [Output] extends [undefined] ? <R>(handler: (options: {
114
136
  args: InferArgs<Args>;
115
137
  ctx: Context;
@@ -144,6 +166,17 @@ interface ActionBuilder<Context, Args extends ArgsValidator, Output = undefined>
144
166
  */
145
167
  expose: (config: ExposeConfig) => ActionBuilder<Context, Args, Output>;
146
168
  input: <A extends ArgsValidator>(validators: A) => ActionBuilder<Context, A & Args, Output>;
169
+ /**
170
+ * Attach static, per-procedure metadata. Merges across calls, is readable
171
+ * from middleware as `ctx.meta`, and is stamped onto the registration as
172
+ * `fn.meta` so codegen and other tooling can enumerate it.
173
+ *
174
+ * The point is policy that is DATA rather than a call: `.meta({ rateLimit:
175
+ * "pins/create" })` can be walked to generate a rate-limit registry or docs,
176
+ * where the same policy expressed only as `.use(rateLimit("pins/create"))`
177
+ * can only be executed. Mirrors tRPC's `.meta()`.
178
+ */
179
+ meta: (value: Record<string, unknown>) => ActionBuilder<Context, Args, Output>;
147
180
  output: <V extends Validator>(validator: V) => ActionBuilder<Context, Args, Infer<V>>;
148
181
  use: <ContextOut>(middleware: Middleware<Context, ContextOut>) => ActionBuilder<ContextOut, Args, Output>;
149
182
  /**
@@ -164,6 +197,17 @@ interface InternalQueryBuilder<Context, Args extends ArgsValidator, Output = und
164
197
  readonly __lunoraProcedure: "query";
165
198
  readonly __lunoraVisibility: "internal";
166
199
  input: <A extends ArgsValidator>(validators: A) => InternalQueryBuilder<Context, A & Args, Output>;
200
+ /**
201
+ * Attach static, per-procedure metadata. Merges across calls, is readable
202
+ * from middleware as `ctx.meta`, and is stamped onto the registration as
203
+ * `fn.meta` so codegen and other tooling can enumerate it.
204
+ *
205
+ * The point is policy that is DATA rather than a call: `.meta({ rateLimit:
206
+ * "pins/create" })` can be walked to generate a rate-limit registry or docs,
207
+ * where the same policy expressed only as `.use(rateLimit("pins/create"))`
208
+ * can only be executed. Mirrors tRPC's `.meta()`.
209
+ */
210
+ meta: (value: Record<string, unknown>) => InternalQueryBuilder<Context, Args, Output>;
167
211
  output: <V extends Validator>(validator: V) => InternalQueryBuilder<Context, Args, Infer<V>>;
168
212
  query: [Output] extends [undefined] ? <R>(handler: (options: {
169
213
  args: InferArgs<Args>;
@@ -184,6 +228,17 @@ interface InternalMutationBuilder<Context, Args extends ArgsValidator, Output =
184
228
  readonly __lunoraProcedure: "mutation";
185
229
  readonly __lunoraVisibility: "internal";
186
230
  input: <A extends ArgsValidator>(validators: A) => InternalMutationBuilder<Context, A & Args, Output>;
231
+ /**
232
+ * Attach static, per-procedure metadata. Merges across calls, is readable
233
+ * from middleware as `ctx.meta`, and is stamped onto the registration as
234
+ * `fn.meta` so codegen and other tooling can enumerate it.
235
+ *
236
+ * The point is policy that is DATA rather than a call: `.meta({ rateLimit:
237
+ * "pins/create" })` can be walked to generate a rate-limit registry or docs,
238
+ * where the same policy expressed only as `.use(rateLimit("pins/create"))`
239
+ * can only be executed. Mirrors tRPC's `.meta()`.
240
+ */
241
+ meta: (value: Record<string, unknown>) => InternalMutationBuilder<Context, Args, Output>;
187
242
  mutation: [Output] extends [undefined] ? <R>(handler: (options: {
188
243
  args: InferArgs<Args>;
189
244
  ctx: Context;
@@ -205,6 +260,17 @@ interface InternalActionBuilder<Context, Args extends ArgsValidator, Output = un
205
260
  ctx: Context;
206
261
  }) => Output | Promise<Output>) => RegisteredAction<Args, Output>;
207
262
  input: <A extends ArgsValidator>(validators: A) => InternalActionBuilder<Context, A & Args, Output>;
263
+ /**
264
+ * Attach static, per-procedure metadata. Merges across calls, is readable
265
+ * from middleware as `ctx.meta`, and is stamped onto the registration as
266
+ * `fn.meta` so codegen and other tooling can enumerate it.
267
+ *
268
+ * The point is policy that is DATA rather than a call: `.meta({ rateLimit:
269
+ * "pins/create" })` can be walked to generate a rate-limit registry or docs,
270
+ * where the same policy expressed only as `.use(rateLimit("pins/create"))`
271
+ * can only be executed. Mirrors tRPC's `.meta()`.
272
+ */
273
+ meta: (value: Record<string, unknown>) => InternalActionBuilder<Context, Args, Output>;
208
274
  output: <V extends Validator>(validator: V) => InternalActionBuilder<Context, Args, Infer<V>>;
209
275
  use: <ContextOut>(middleware: Middleware<Context, ContextOut>) => InternalActionBuilder<ContextOut, Args, Output>;
210
276
  }
@@ -517,11 +583,19 @@ type HttpMethod = "DELETE" | "GET" | "HEAD" | "OPTIONS" | "PATCH" | "POST" | "PU
517
583
  /**
518
584
  * Context handed to an HTTP action handler. A narrower view of {@link ActionContext}:
519
585
  * HTTP actions run in the worker (the "action runtime"), separate from the
520
- * transactional store, so there is no direct `db` / `vectors` / `scheduler` /
521
- * `storage` surface — reach the data layer through `runQuery` / `runMutation` /
586
+ * transactional store, so there is no direct `db` / `vectors` / `storage`
587
+ * surface — reach the data layer through `runQuery` / `runMutation` /
522
588
  * `runAction`, which forward to the owning shard.
523
- */
524
- type HttpActionCtx = Pick<ActionCtx, "auth" | "cache" | "fetch" | "runAction" | "runMutation" | "runQuery">;
589
+ *
590
+ * `scheduler` IS present (it talks to the scheduler DO, not the shard) but is
591
+ * optional: it exists only when the app declared `.scheduler(...)` on the
592
+ * generated app builder. "Receive webhook → enqueue the real work → return 200"
593
+ * is what HTTP actions are for, so omitting it forced every app to hand-roll a
594
+ * hop through a mutation plus a closed allow-list of target strings.
595
+ */
596
+ type HttpActionCtx = Pick<ActionCtx, "auth" | "cache" | "fetch" | "runAction" | "runMutation" | "runQuery"> & {
597
+ readonly scheduler?: ActionCtx["scheduler"];
598
+ };
525
599
  /** A raw handler wrapped by {@link httpAction}. Receives the raw request, returns the raw response. */
526
600
  type HttpActionHandler = (context: HttpActionCtx, request: Request) => Promise<Response> | Response;
527
601
  /**
package/dist/index.d.ts CHANGED
@@ -70,6 +70,17 @@ interface QueryBuilder<Context, Args extends ArgsValidator, Output = undefined>
70
70
  */
71
71
  expose: (config: ExposeConfig) => QueryBuilder<Context, Args, Output>;
72
72
  input: <A extends ArgsValidator>(validators: A) => QueryBuilder<Context, A & Args, Output>;
73
+ /**
74
+ * Attach static, per-procedure metadata. Merges across calls, is readable
75
+ * from middleware as `ctx.meta`, and is stamped onto the registration as
76
+ * `fn.meta` so codegen and other tooling can enumerate it.
77
+ *
78
+ * The point is policy that is DATA rather than a call: `.meta({ rateLimit:
79
+ * "pins/create" })` can be walked to generate a rate-limit registry or docs,
80
+ * where the same policy expressed only as `.use(rateLimit("pins/create"))`
81
+ * can only be executed. Mirrors tRPC's `.meta()`.
82
+ */
83
+ meta: (value: Record<string, unknown>) => QueryBuilder<Context, Args, Output>;
73
84
  output: <V extends Validator>(validator: V) => QueryBuilder<Context, Args, Infer<V>>;
74
85
  query: [Output] extends [undefined] ? <R>(handler: (options: {
75
86
  args: InferArgs<Args>;
@@ -110,6 +121,17 @@ interface MutationBuilder<Context, Args extends ArgsValidator, Output = undefine
110
121
  */
111
122
  expose: (config: ExposeConfig) => MutationBuilder<Context, Args, Output>;
112
123
  input: <A extends ArgsValidator>(validators: A) => MutationBuilder<Context, A & Args, Output>;
124
+ /**
125
+ * Attach static, per-procedure metadata. Merges across calls, is readable
126
+ * from middleware as `ctx.meta`, and is stamped onto the registration as
127
+ * `fn.meta` so codegen and other tooling can enumerate it.
128
+ *
129
+ * The point is policy that is DATA rather than a call: `.meta({ rateLimit:
130
+ * "pins/create" })` can be walked to generate a rate-limit registry or docs,
131
+ * where the same policy expressed only as `.use(rateLimit("pins/create"))`
132
+ * can only be executed. Mirrors tRPC's `.meta()`.
133
+ */
134
+ meta: (value: Record<string, unknown>) => MutationBuilder<Context, Args, Output>;
113
135
  mutation: [Output] extends [undefined] ? <R>(handler: (options: {
114
136
  args: InferArgs<Args>;
115
137
  ctx: Context;
@@ -144,6 +166,17 @@ interface ActionBuilder<Context, Args extends ArgsValidator, Output = undefined>
144
166
  */
145
167
  expose: (config: ExposeConfig) => ActionBuilder<Context, Args, Output>;
146
168
  input: <A extends ArgsValidator>(validators: A) => ActionBuilder<Context, A & Args, Output>;
169
+ /**
170
+ * Attach static, per-procedure metadata. Merges across calls, is readable
171
+ * from middleware as `ctx.meta`, and is stamped onto the registration as
172
+ * `fn.meta` so codegen and other tooling can enumerate it.
173
+ *
174
+ * The point is policy that is DATA rather than a call: `.meta({ rateLimit:
175
+ * "pins/create" })` can be walked to generate a rate-limit registry or docs,
176
+ * where the same policy expressed only as `.use(rateLimit("pins/create"))`
177
+ * can only be executed. Mirrors tRPC's `.meta()`.
178
+ */
179
+ meta: (value: Record<string, unknown>) => ActionBuilder<Context, Args, Output>;
147
180
  output: <V extends Validator>(validator: V) => ActionBuilder<Context, Args, Infer<V>>;
148
181
  use: <ContextOut>(middleware: Middleware<Context, ContextOut>) => ActionBuilder<ContextOut, Args, Output>;
149
182
  /**
@@ -164,6 +197,17 @@ interface InternalQueryBuilder<Context, Args extends ArgsValidator, Output = und
164
197
  readonly __lunoraProcedure: "query";
165
198
  readonly __lunoraVisibility: "internal";
166
199
  input: <A extends ArgsValidator>(validators: A) => InternalQueryBuilder<Context, A & Args, Output>;
200
+ /**
201
+ * Attach static, per-procedure metadata. Merges across calls, is readable
202
+ * from middleware as `ctx.meta`, and is stamped onto the registration as
203
+ * `fn.meta` so codegen and other tooling can enumerate it.
204
+ *
205
+ * The point is policy that is DATA rather than a call: `.meta({ rateLimit:
206
+ * "pins/create" })` can be walked to generate a rate-limit registry or docs,
207
+ * where the same policy expressed only as `.use(rateLimit("pins/create"))`
208
+ * can only be executed. Mirrors tRPC's `.meta()`.
209
+ */
210
+ meta: (value: Record<string, unknown>) => InternalQueryBuilder<Context, Args, Output>;
167
211
  output: <V extends Validator>(validator: V) => InternalQueryBuilder<Context, Args, Infer<V>>;
168
212
  query: [Output] extends [undefined] ? <R>(handler: (options: {
169
213
  args: InferArgs<Args>;
@@ -184,6 +228,17 @@ interface InternalMutationBuilder<Context, Args extends ArgsValidator, Output =
184
228
  readonly __lunoraProcedure: "mutation";
185
229
  readonly __lunoraVisibility: "internal";
186
230
  input: <A extends ArgsValidator>(validators: A) => InternalMutationBuilder<Context, A & Args, Output>;
231
+ /**
232
+ * Attach static, per-procedure metadata. Merges across calls, is readable
233
+ * from middleware as `ctx.meta`, and is stamped onto the registration as
234
+ * `fn.meta` so codegen and other tooling can enumerate it.
235
+ *
236
+ * The point is policy that is DATA rather than a call: `.meta({ rateLimit:
237
+ * "pins/create" })` can be walked to generate a rate-limit registry or docs,
238
+ * where the same policy expressed only as `.use(rateLimit("pins/create"))`
239
+ * can only be executed. Mirrors tRPC's `.meta()`.
240
+ */
241
+ meta: (value: Record<string, unknown>) => InternalMutationBuilder<Context, Args, Output>;
187
242
  mutation: [Output] extends [undefined] ? <R>(handler: (options: {
188
243
  args: InferArgs<Args>;
189
244
  ctx: Context;
@@ -205,6 +260,17 @@ interface InternalActionBuilder<Context, Args extends ArgsValidator, Output = un
205
260
  ctx: Context;
206
261
  }) => Output | Promise<Output>) => RegisteredAction<Args, Output>;
207
262
  input: <A extends ArgsValidator>(validators: A) => InternalActionBuilder<Context, A & Args, Output>;
263
+ /**
264
+ * Attach static, per-procedure metadata. Merges across calls, is readable
265
+ * from middleware as `ctx.meta`, and is stamped onto the registration as
266
+ * `fn.meta` so codegen and other tooling can enumerate it.
267
+ *
268
+ * The point is policy that is DATA rather than a call: `.meta({ rateLimit:
269
+ * "pins/create" })` can be walked to generate a rate-limit registry or docs,
270
+ * where the same policy expressed only as `.use(rateLimit("pins/create"))`
271
+ * can only be executed. Mirrors tRPC's `.meta()`.
272
+ */
273
+ meta: (value: Record<string, unknown>) => InternalActionBuilder<Context, Args, Output>;
208
274
  output: <V extends Validator>(validator: V) => InternalActionBuilder<Context, Args, Infer<V>>;
209
275
  use: <ContextOut>(middleware: Middleware<Context, ContextOut>) => InternalActionBuilder<ContextOut, Args, Output>;
210
276
  }
@@ -517,11 +583,19 @@ type HttpMethod = "DELETE" | "GET" | "HEAD" | "OPTIONS" | "PATCH" | "POST" | "PU
517
583
  /**
518
584
  * Context handed to an HTTP action handler. A narrower view of {@link ActionContext}:
519
585
  * HTTP actions run in the worker (the "action runtime"), separate from the
520
- * transactional store, so there is no direct `db` / `vectors` / `scheduler` /
521
- * `storage` surface — reach the data layer through `runQuery` / `runMutation` /
586
+ * transactional store, so there is no direct `db` / `vectors` / `storage`
587
+ * surface — reach the data layer through `runQuery` / `runMutation` /
522
588
  * `runAction`, which forward to the owning shard.
523
- */
524
- type HttpActionCtx = Pick<ActionCtx, "auth" | "cache" | "fetch" | "runAction" | "runMutation" | "runQuery">;
589
+ *
590
+ * `scheduler` IS present (it talks to the scheduler DO, not the shard) but is
591
+ * optional: it exists only when the app declared `.scheduler(...)` on the
592
+ * generated app builder. "Receive webhook → enqueue the real work → return 200"
593
+ * is what HTTP actions are for, so omitting it forced every app to hand-roll a
594
+ * hop through a mutation plus a closed allow-list of target strings.
595
+ */
596
+ type HttpActionCtx = Pick<ActionCtx, "auth" | "cache" | "fetch" | "runAction" | "runMutation" | "runQuery"> & {
597
+ readonly scheduler?: ActionCtx["scheduler"];
598
+ };
525
599
  /** A raw handler wrapped by {@link httpAction}. Receives the raw request, returns the raw response. */
526
600
  type HttpActionHandler = (context: HttpActionCtx, request: Request) => Promise<Response> | Response;
527
601
  /**
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{default as t}from"./packem_shared/asBucketStorage-1pFfH-Tn.mjs";import{initLunora as i}from"./packem_shared/initLunora-CHh1EmR4.mjs";import{createSecrets as p}from"./packem_shared/createSecrets-CgVPiW2C.mjs";import{LunoraEnvError as m,defineEnv as d,redactSecrets as x}from"./packem_shared/LunoraEnvError-CgpI2Mm_.mjs";import{LunoraError as c}from"./packem_shared/LunoraError-LVhdU0Lo.mjs";import{bindOrm as E,bindTableFacade as u}from"./packem_shared/bindOrm-Bp9hsM2q.mjs";import{httpAction as g,httpRoute as R,httpRouter as L,isSafeHeaderValue as P,serveStorageObject as h}from"./packem_shared/httpAction-C14NuF3V.mjs";import{defineIdentity as I}from"./packem_shared/defineIdentity-B7gfAgxx.mjs";import{onConnect as b,onDisconnect as y}from"./packem_shared/onConnect-CEtRmUpJ.mjs";import{DEFAULT_LIMIT as _,DEFAULT_MAX_LIMIT as D,clampLimit as v,defineListArgs as C}from"./packem_shared/DEFAULT_LIMIT-yHJ5O96W.mjs";import{defineMigration as V}from"./packem_shared/defineMigration-Bfpwxv2f.mjs";import{defineMutator as N}from"./packem_shared/defineMutator-BgpQ-xUo.mjs";import{composePluginMiddleware as U,defineComponent as w,definePlugin as B,defineSchemaExtension as W,installPlugins as j,mergeSchemaExtension as H}from"./packem_shared/composePluginMiddleware-COr09CXA.mjs";import{PRESENCE_DEFAULT_TTL_MS as X,PRESENCE_TABLE as q,definePresence as z,presenceExtension as G}from"./packem_shared/PRESENCE_DEFAULT_TTL_MS-D50xfQ1n.mjs";import{protectPublic as Q}from"./packem_shared/protectPublic-BhKewPqm.mjs";import{defineAggregateIndex as Z,defineRankIndex as $,defineSchema as ee,defineTable as oe,defineVectorIndex as re}from"./packem_shared/defineAggregateIndex-_gWNmkQZ.mjs";import{defineShape as ne}from"./packem_shared/defineShape-Ds8uNqzX.mjs";import{anyApi as fe}from"./types.mjs";import{cronJobs as ae}from"@lunora/scheduler";import{ValidationError as de,v as xe}from"@lunora/values";import{allowAll as ce,deny as le,isDeny as Ee,toWhereInput as ue}from"./packem_shared/allowAll-BnyNbJZT.mjs";import{buildRlsReadRegistry as ge,composeShapeReadWhere as Re}from"./packem_shared/buildRlsReadRegistry-WdiqSj87.mjs";import{createPolicyDsl as Pe,definePermission as he,definePolicies as Ae,definePolicy as Ie,defineRole as Te}from"./packem_shared/createPolicyDsl-sV1swpkD.mjs";import{defineStorageRule as ye,defineStorageRules as Me}from"./packem_shared/defineStorageRule-BDu01PUn.mjs";import{mask as De}from"./packem_shared/mask-C6Bi78qj.mjs";import{rls as Ce}from"./packem_shared/rls-_iVsPvhX.mjs";import{storageRules as Ve}from"./packem_shared/storageRules-BptPZbi8.mjs";const e="0.0.0";export{_ as DEFAULT_LIMIT,D as DEFAULT_MAX_LIMIT,m as LunoraEnvError,c as LunoraError,X as PRESENCE_DEFAULT_TTL_MS,q as PRESENCE_TABLE,e as VERSION,de as ValidationError,ce as allowAll,fe as anyApi,t as asBucketStorage,E as bindOrm,u as bindTableFacade,ge as buildRlsReadRegistry,v as clampLimit,U as composePluginMiddleware,Re as composeShapeReadWhere,Pe as createPolicyDsl,p as createSecrets,ae as cronJobs,Z as defineAggregateIndex,w as defineComponent,d as defineEnv,I as defineIdentity,C as defineListArgs,V as defineMigration,N as defineMutator,he as definePermission,B as definePlugin,Ae as definePolicies,Ie as definePolicy,z as definePresence,$ as defineRankIndex,Te as defineRole,ee as defineSchema,W as defineSchemaExtension,ne as defineShape,ye as defineStorageRule,Me as defineStorageRules,oe as defineTable,re as defineVectorIndex,le as deny,g as httpAction,R as httpRoute,L as httpRouter,i as initLunora,j as installPlugins,Ee as isDeny,P as isSafeHeaderValue,De as mask,H as mergeSchemaExtension,b as onConnect,y as onDisconnect,G as presenceExtension,Q as protectPublic,x as redactSecrets,Ce as rls,h as serveStorageObject,Ve as storageRules,ue as toWhereInput,xe as v};
1
+ import{default as t}from"./packem_shared/asBucketStorage-1pFfH-Tn.mjs";import{initLunora as i}from"./packem_shared/initLunora-QNqOuG0A.mjs";import{createSecrets as p}from"./packem_shared/createSecrets-CgVPiW2C.mjs";import{LunoraEnvError as m,defineEnv as d,redactSecrets as x}from"./packem_shared/LunoraEnvError-CgpI2Mm_.mjs";import{LunoraError as c}from"./packem_shared/LunoraError-LVhdU0Lo.mjs";import{bindOrm as E,bindTableFacade as u}from"./packem_shared/bindOrm-Bp9hsM2q.mjs";import{httpAction as g,httpRoute as R,httpRouter as L,isSafeHeaderValue as P,serveStorageObject as h}from"./packem_shared/httpAction-C14NuF3V.mjs";import{defineIdentity as I}from"./packem_shared/defineIdentity-B7gfAgxx.mjs";import{onConnect as b,onDisconnect as y}from"./packem_shared/onConnect-CEtRmUpJ.mjs";import{DEFAULT_LIMIT as _,DEFAULT_MAX_LIMIT as D,clampLimit as v,defineListArgs as C}from"./packem_shared/DEFAULT_LIMIT-yHJ5O96W.mjs";import{defineMigration as V}from"./packem_shared/defineMigration-Bfpwxv2f.mjs";import{defineMutator as N}from"./packem_shared/defineMutator-BgpQ-xUo.mjs";import{composePluginMiddleware as U,defineComponent as w,definePlugin as B,defineSchemaExtension as W,installPlugins as j,mergeSchemaExtension as H}from"./packem_shared/composePluginMiddleware-COr09CXA.mjs";import{PRESENCE_DEFAULT_TTL_MS as X,PRESENCE_TABLE as q,definePresence as z,presenceExtension as G}from"./packem_shared/PRESENCE_DEFAULT_TTL_MS-C9RX3Cl6.mjs";import{protectPublic as Q}from"./packem_shared/protectPublic-BhKewPqm.mjs";import{defineAggregateIndex as Z,defineRankIndex as $,defineSchema as ee,defineTable as oe,defineVectorIndex as re}from"./packem_shared/defineAggregateIndex-_gWNmkQZ.mjs";import{defineShape as ne}from"./packem_shared/defineShape-Ds8uNqzX.mjs";import{anyApi as fe}from"./types.mjs";import{cronJobs as ae}from"@lunora/scheduler";import{ValidationError as de,v as xe}from"@lunora/values";import{allowAll as ce,deny as le,isDeny as Ee,toWhereInput as ue}from"./packem_shared/allowAll-BnyNbJZT.mjs";import{buildRlsReadRegistry as ge,composeShapeReadWhere as Re}from"./packem_shared/buildRlsReadRegistry-WdiqSj87.mjs";import{createPolicyDsl as Pe,definePermission as he,definePolicies as Ae,definePolicy as Ie,defineRole as Te}from"./packem_shared/createPolicyDsl-sV1swpkD.mjs";import{defineStorageRule as ye,defineStorageRules as Me}from"./packem_shared/defineStorageRule-BDu01PUn.mjs";import{mask as De}from"./packem_shared/mask-C6Bi78qj.mjs";import{rls as Ce}from"./packem_shared/rls-_iVsPvhX.mjs";import{storageRules as Ve}from"./packem_shared/storageRules-BptPZbi8.mjs";const e="0.0.0";export{_ as DEFAULT_LIMIT,D as DEFAULT_MAX_LIMIT,m as LunoraEnvError,c as LunoraError,X as PRESENCE_DEFAULT_TTL_MS,q as PRESENCE_TABLE,e as VERSION,de as ValidationError,ce as allowAll,fe as anyApi,t as asBucketStorage,E as bindOrm,u as bindTableFacade,ge as buildRlsReadRegistry,v as clampLimit,U as composePluginMiddleware,Re as composeShapeReadWhere,Pe as createPolicyDsl,p as createSecrets,ae as cronJobs,Z as defineAggregateIndex,w as defineComponent,d as defineEnv,I as defineIdentity,C as defineListArgs,V as defineMigration,N as defineMutator,he as definePermission,B as definePlugin,Ae as definePolicies,Ie as definePolicy,z as definePresence,$ as defineRankIndex,Te as defineRole,ee as defineSchema,W as defineSchemaExtension,ne as defineShape,ye as defineStorageRule,Me as defineStorageRules,oe as defineTable,re as defineVectorIndex,le as deny,g as httpAction,R as httpRoute,L as httpRouter,i as initLunora,j as installPlugins,Ee as isDeny,P as isSafeHeaderValue,De as mask,H as mergeSchemaExtension,b as onConnect,y as onDisconnect,G as presenceExtension,Q as protectPublic,x as redactSecrets,Ce as rls,h as serveStorageObject,Ve as storageRules,ue as toWhereInput,xe as v};
@@ -1 +1 @@
1
- import{v as o}from"@lunora/values";import{initLunora as E}from"./initLunora-CHh1EmR4.mjs";import{LunoraError as p}from"./LunoraError-LVhdU0Lo.mjs";import{onDisconnect as q}from"./onConnect-CEtRmUpJ.mjs";import{defineSchemaExtension as v,defineComponent as R}from"./composePluginMiddleware-COr09CXA.mjs";import{defineTable as _}from"./defineAggregateIndex-_gWNmkQZ.mjs";const D=3e4,y=4096,l="presence",h="present",c=`${l}_${h}`,M=v(l,{tables:{[h]:_({data:o.optional(o.record(o.string(),o.any())),lastSeen:o.number(),roomId:o.string(),sessionId:o.string(),userId:o.optional(o.string())}).index("byRoomSession",["roomId","sessionId"]).index("byRoom",["roomId"])}}),{mutation:b,query:T}=E.dataModel().create(),$=(u={})=>{const m=u.ttlMs??D,f=Math.max(0,Math.min(u.disconnectGraceMs??0,m)),w=b.input({data:o.optional(o.record(o.string(),o.any())),roomId:o.string(),sessionId:o.string()}).mutation(async({args:t,ctx:s})=>{const r=Date.now(),i=s.auth.userId??void 0;if(t.data!==void 0&&new TextEncoder().encode(JSON.stringify(t.data)).length>y)throw new p("BAD_REQUEST",`presence data exceeds the ${String(y)}-byte limit`);const e=await s.db.query(c).withIndex("byRoomSession",n=>n.eq("roomId",t.roomId).eq("sessionId",t.sessionId)).first();if(e&&(e.userId??void 0)!==i)throw new p("FORBIDDEN","presence heartbeat denied: this (roomId, sessionId) is held by another identity");const a={lastSeen:r,roomId:t.roomId,sessionId:t.sessionId,...t.data===void 0?{}:{data:t.data},...i===void 0?{}:{userId:i}};return await(e?s.db.patch(e._id,a):s.db.insert(c,a)),{lastSeen:r}}),S=T.input({roomId:o.string()}).query(async({args:t,ctx:s})=>{const r=Date.now()-m,i=(await s.db.query(c).withIndex("byRoom",n=>n.eq("roomId",t.roomId)).collect()).filter(n=>n.lastSeen>r).toSorted((n,d)=>d.lastSeen-n.lastSeen),e=new Set,a=[];for(const n of i){const d=n.userId;if(d!==void 0){if(e.has(d))continue;e.add(d)}const I={lastSeen:n.lastSeen,roomId:n.roomId};d!==void 0&&(I.userId=d),n.data!==void 0&&(I.data=n.data),a.push(I)}return a}),g={...b.input({roomId:o.string()}).mutation(async({args:t,ctx:s})=>{const r=Date.now()-m,i=await s.db.query(c).withIndex("byRoom",e=>e.eq("roomId",t.roomId)).filter(e=>e.lastSeen<=r).collect();return await Promise.all(i.map(e=>s.db.delete(e._id))),{deleted:i.length}}),visibility:"internal"},x=q(async(t,s)=>{const r=s.context?.roomId,i=s.context?.sessionId;if(typeof r!="string"||typeof i!="string")return;const e=await t.db.query(c).withIndex("byRoomSession",d=>d.eq("roomId",r).eq("sessionId",i)).first();if(!e)return;const a=s.userId??void 0;if((e.userId??void 0)!==a)return;if(f===0){await t.db.delete(e._id);return}const n=Math.min(e.lastSeen,Date.now()+f-m);await t.db.patch(e._id,{lastSeen:n})});return R(l,{extension:M,functions:{disconnect:x,heartbeat:w,listPresent:S,sweep:g}})};export{D as PRESENCE_DEFAULT_TTL_MS,c as PRESENCE_TABLE,$ as definePresence,M as presenceExtension};
1
+ import{v as o}from"@lunora/values";import{initLunora as E}from"./initLunora-QNqOuG0A.mjs";import{LunoraError as p}from"./LunoraError-LVhdU0Lo.mjs";import{onDisconnect as q}from"./onConnect-CEtRmUpJ.mjs";import{defineSchemaExtension as v,defineComponent as R}from"./composePluginMiddleware-COr09CXA.mjs";import{defineTable as _}from"./defineAggregateIndex-_gWNmkQZ.mjs";const D=3e4,y=4096,l="presence",h="present",c=`${l}_${h}`,M=v(l,{tables:{[h]:_({data:o.optional(o.record(o.string(),o.any())),lastSeen:o.number(),roomId:o.string(),sessionId:o.string(),userId:o.optional(o.string())}).index("byRoomSession",["roomId","sessionId"]).index("byRoom",["roomId"])}}),{mutation:b,query:T}=E.dataModel().create(),F=(u={})=>{const m=u.ttlMs??D,f=Math.max(0,Math.min(u.disconnectGraceMs??0,m)),w=b.input({data:o.optional(o.record(o.string(),o.any())),roomId:o.string(),sessionId:o.string()}).mutation(async({args:t,ctx:s})=>{const r=Date.now(),i=s.auth.userId??void 0;if(t.data!==void 0&&new TextEncoder().encode(JSON.stringify(t.data)).length>y)throw new p("BAD_REQUEST",`presence data exceeds the ${String(y)}-byte limit`);const e=await s.db.query(c).withIndex("byRoomSession",n=>n.eq("roomId",t.roomId).eq("sessionId",t.sessionId)).first();if(e&&(e.userId??void 0)!==i)throw new p("FORBIDDEN","presence heartbeat denied: this (roomId, sessionId) is held by another identity");const a={lastSeen:r,roomId:t.roomId,sessionId:t.sessionId,...t.data===void 0?{}:{data:t.data},...i===void 0?{}:{userId:i}};return await(e?s.db.patch(e._id,a):s.db.insert(c,a)),{lastSeen:r}}),S=T.input({roomId:o.string()}).query(async({args:t,ctx:s})=>{const r=Date.now()-m,i=(await s.db.query(c).withIndex("byRoom",n=>n.eq("roomId",t.roomId)).collect()).filter(n=>n.lastSeen>r).toSorted((n,d)=>d.lastSeen-n.lastSeen),e=new Set,a=[];for(const n of i){const d=n.userId;if(d!==void 0){if(e.has(d))continue;e.add(d)}const I={lastSeen:n.lastSeen,roomId:n.roomId};d!==void 0&&(I.userId=d),n.data!==void 0&&(I.data=n.data),a.push(I)}return a}),g={...b.input({roomId:o.string()}).mutation(async({args:t,ctx:s})=>{const r=Date.now()-m,i=await s.db.query(c).withIndex("byRoom",e=>e.eq("roomId",t.roomId)).filter(e=>e.lastSeen<=r).collect();return await Promise.all(i.map(e=>s.db.delete(e._id))),{deleted:i.length}}),visibility:"internal"},x=q(async(t,s)=>{const r=s.context?.roomId,i=s.context?.sessionId;if(typeof r!="string"||typeof i!="string")return;const e=await t.db.query(c).withIndex("byRoomSession",d=>d.eq("roomId",r).eq("sessionId",i)).first();if(!e)return;const a=s.userId??void 0;if((e.userId??void 0)!==a)return;if(f===0){await t.db.delete(e._id);return}const n=Math.min(e.lastSeen,Date.now()+f-m);await t.db.patch(e._id,{lastSeen:n})});return R(l,{extension:M,functions:{disconnect:x,heartbeat:w,listPresent:S,sweep:g}})};export{D as PRESENCE_DEFAULT_TTL_MS,c as PRESENCE_TABLE,F as definePresence,M as presenceExtension};
@@ -0,0 +1 @@
1
+ import{s as c}from"./functions-CDC08CWY.mjs";import{l as p}from"./policy-tag-Dprt9JWo.mjs";import{l as x}from"./run-middleware-BeEEqmdE.mjs";const w=(t,e)=>e===void 0||typeof t!="object"||t===null?t:Object.assign(Object.create(Object.getPrototypeOf(t)),t,{meta:e}),g=(t,e)=>x(t,e,r=>r),y=(t,e,r,a,i)=>async(o,m)=>{const n=c(t,m),d=await g(e,w(o,i)),l=await r({args:n,ctx:d});return a?a.parse(l):l},f=(t,e,r)=>(a,i,o)=>{const m=c(t,i);return(async function*(){const n=await g(e,a),d=r({args:m,ctx:n,signal:o})[Symbol.asyncIterator]();try{for(;;){if(o.aborted)return;const l=await d.next();if(l.done||o.aborted)return;yield l.value}}finally{await d.return?.()}})()},u=t=>{const e=[];for(const r of t){const a=p(r);a&&e.push(a)}return e.length>0?{tags:e}:void 0},s=(t,e,r)=>({__lunoraProcedure:t,...r?{__lunoraVisibility:r}:{},input:a=>s(t,{...e,args:{...e.args,...a}},r),[t]:a=>{const i=u(e.middlewares);return{args:e.args,...e.expose?{expose:e.expose}:{},handler:y(e.args,e.middlewares,a,e.output,e.meta),kind:t,...e.meta?{meta:e.meta}:{},...i?{rls:i}:{},...r?{visibility:r}:{},...e.x402?{x402:e.x402}:{}}},meta:a=>s(t,{...e,meta:{...e.meta,...a}},r),output:a=>s(t,{...e,output:a},r),...t==="query"?{stream:a=>{const i=u(e.middlewares);return{args:e.args,...e.expose?{expose:e.expose}:{},handler:f(e.args,e.middlewares,a),kind:"stream",...i?{rls:i}:{},...r?{visibility:r}:{},...e.x402?{x402:e.x402}:{}}}}:{},use:a=>s(t,{...e,middlewares:[...e.middlewares,a]},r),...r?{}:{expose:a=>s(t,{...e,expose:a},r)},...r?{}:{x402:a=>s(t,{...e,x402:a},r)}}),j={dataModel:()=>({create:t=>({action:s("action",{args:{},middlewares:[]}),internalAction:s("action",{args:{},middlewares:[]},"internal"),internalMutation:s("mutation",{args:{},middlewares:[]},"internal"),internalQuery:s("query",{args:{},middlewares:[]},"internal"),mutation:s("mutation",{args:{},middlewares:[]}),query:s("query",{args:{},middlewares:[]})})})};export{j as initLunora};
package/dist/types.d.mts CHANGED
@@ -552,6 +552,12 @@ interface RegisteredFunction<A extends ArgsValidator, R, Kind extends FunctionKi
552
552
  * Absent on ordinary registrations.
553
553
  */
554
554
  readonly lifecycle?: LifecycleEventKind;
555
+ /**
556
+ * Static per-procedure metadata declared with `.meta(...)`. Present so
557
+ * middleware (via `ctx.meta`) and tooling can read the same object; absent
558
+ * when the chain never called `.meta()`.
559
+ */
560
+ readonly meta?: Record<string, unknown>;
555
561
  readonly visibility?: FunctionVisibility;
556
562
  /**
557
563
  * Set by the `.x402({ price })` builder modifier. Marks the procedure as paid
@@ -563,6 +569,48 @@ interface RegisteredFunction<A extends ArgsValidator, R, Kind extends FunctionKi
563
569
  type RegisteredQuery<A extends ArgsValidator, R> = RegisteredFunction<A, R, "query">;
564
570
  type RegisteredMutation<A extends ArgsValidator, R> = RegisteredFunction<A, R, "mutation">;
565
571
  type RegisteredAction<A extends ArgsValidator, R> = RegisteredFunction<A, R, "action">;
572
+ /**
573
+ * Structural mirror of `@lunora/client`'s `FunctionReference` — the handle the
574
+ * generated `api` / `internal` objects hand you, carrying `&lt;file>:&lt;function>`
575
+ * in `__lunoraRef`. Redeclared here so `@lunora/server` needs no dependency on
576
+ * the client package, exactly as {@link Scheduler} avoids one on
577
+ * `@lunora/scheduler`. `RegisteredFunction` has no `__lunoraRef`, so the two
578
+ * shapes never overlap.
579
+ */
580
+ interface FunctionHandle<Kind extends "action" | "mutation" | "query" | "stream", Args, Return> {
581
+ /** Phantom marker carrying the type parameters; never present at runtime. */
582
+ readonly __lunoraPhantom?: {
583
+ args: Args;
584
+ kind: Kind;
585
+ returns: Return;
586
+ };
587
+ readonly __lunoraRef: string;
588
+ }
589
+ /**
590
+ * `ctx.runQuery` — overloaded, see the note above.
591
+ *
592
+ * A single generic signature over the reference would be nicer (TS will not
593
+ * contextually type a parameter against a multi-signature type, so a hand-built
594
+ * ctx object must annotate its `(reference, args)` explicitly — see
595
+ * `@lunora/testing`'s harness). It does not work: a concrete
596
+ * `RegisteredQuery&lt;{…}, number>` is not assignable to a
597
+ * `RegisteredFunction&lt;ArgsValidator, …>` constraint, because `handler`'s args
598
+ * are in a contravariant position. Two inference sites it is.
599
+ */
600
+ interface RunQuery {
601
+ <A extends ArgsValidator, R>(reference: RegisteredQuery<A, R>, args: InferArgs<A>): Promise<R>;
602
+ <Args, R>(reference: FunctionHandle<"query", Args, R>, args: Args): Promise<R>;
603
+ }
604
+ /** `ctx.runMutation` — overloaded for the same reason as {@link RunQuery}. */
605
+ interface RunMutation {
606
+ <A extends ArgsValidator, R>(reference: RegisteredMutation<A, R>, args: InferArgs<A>): Promise<R>;
607
+ <Args, R>(reference: FunctionHandle<"mutation", Args, R>, args: Args): Promise<R>;
608
+ }
609
+ /** `ctx.runAction` — overloaded for the same reason as {@link RunQuery}. */
610
+ interface RunAction {
611
+ <A extends ArgsValidator, R>(reference: RegisteredAction<A, R>, args: InferArgs<A>): Promise<R>;
612
+ <Args, R>(reference: FunctionHandle<"action", Args, R>, args: Args): Promise<R>;
613
+ }
566
614
  /** Which side of the WebSocket lifecycle a hook fires on. */
567
615
  type LifecycleEventKind = "connect" | "disconnect";
568
616
  /**
@@ -752,6 +800,27 @@ interface PaginationResult<T = Record<string, unknown>> {
752
800
  * (schema-agnostic) `@lunora/server` reader.
753
801
  */
754
802
  interface TableReader<Row = Record<string, unknown>> {
803
+ /**
804
+ * Iterate rows lazily: `for await (const row of ctx.db.query("t").withIndex(…))`.
805
+ *
806
+ * Pages through the result set behind the scenes and yields row by row, so
807
+ * a consumer that stops early stops the reads too. `.collect()` is still the
808
+ * right terminal when you want the whole set; this exists for the cases
809
+ * where you cannot know up front how far you need to read.
810
+ *
811
+ * That is what merged/ordered index streams need. Reimplementing Convex's
812
+ * `convex-helpers/server/stream` in userland previously meant materialising
813
+ * each branch with a bounded `.take(1024)` before merging, so asking a
814
+ * merged stream for ONE row read up to 1,024 rows per branch. The k-way merge itself is application code and stays there — only
815
+ * the laziness had to come from the database layer.
816
+ *
817
+ * Iteration pages through `.paginate()`, so it follows the same order —
818
+ * which is `.collect()`'s order whenever the sort key is unique. Under a
819
+ * TIED sort key (an unindexed read whose rows share `_creationTime`) the
820
+ * two can disagree, because the tie-break is left to SQLite. Read through
821
+ * an index when order matters, exactly as you would for `.paginate()`.
822
+ */
823
+ [Symbol.asyncIterator]: () => AsyncIterator<Row>;
755
824
  collect: () => Promise<Row[]>;
756
825
  filter: (predicate: (document: Row) => boolean) => TableReader<Row>;
757
826
  first: () => Promise<Row | null>;
@@ -1803,6 +1872,13 @@ interface QueryCtx {
1803
1872
  /** Structured, function-attributed logger; see {@link LunoraLogger}. */
1804
1873
  readonly log: LunoraLogger;
1805
1874
  /** Application counters, gauges, and histograms; see {@link LunoraMetrics}. */
1875
+ /**
1876
+ * Static metadata declared on this procedure with `.meta(...)`, merged
1877
+ * across calls. Present so middleware can read the policy it is meant to
1878
+ * enforce (`ctx.meta.rateLimit`, …) instead of having it hard-wired at each
1879
+ * `.use()` site; absent when the procedure never called `.meta()`.
1880
+ */
1881
+ readonly meta?: Record<string, unknown>;
1806
1882
  readonly metrics: LunoraMetrics;
1807
1883
  /**
1808
1884
  * Wall-clock time (epoch ms) the function began, captured once so the whole
@@ -1820,7 +1896,7 @@ interface QueryCtx {
1820
1896
  * `runMutation` on a `QueryCtx` (writes are not allowed from a query).
1821
1897
  * Mirrors Convex's `ctx.runQuery`.
1822
1898
  */
1823
- readonly runQuery: <A extends ArgsValidator, R>(reference: RegisteredQuery<A, R>, args: InferArgs<A>) => Promise<R>;
1899
+ readonly runQuery: RunQuery;
1824
1900
  /** Read account-level secrets from Cloudflare Secrets Store; see {@link Secrets}. */
1825
1901
  readonly secrets: Secrets;
1826
1902
  /** Attach facts to THIS request's span — the wide event; see {@link LunoraWideEvent}. */
@@ -1851,6 +1927,13 @@ interface MutationCtx {
1851
1927
  /** Structured, function-attributed logger; see {@link LunoraLogger}. */
1852
1928
  readonly log: LunoraLogger;
1853
1929
  /** Application counters, gauges, and histograms; see {@link LunoraMetrics}. */
1930
+ /**
1931
+ * Static metadata declared on this procedure with `.meta(...)`, merged
1932
+ * across calls. Present so middleware can read the policy it is meant to
1933
+ * enforce (`ctx.meta.rateLimit`, …) instead of having it hard-wired at each
1934
+ * `.use()` site; absent when the procedure never called `.meta()`.
1935
+ */
1936
+ readonly meta?: Record<string, unknown>;
1854
1937
  readonly metrics: LunoraMetrics;
1855
1938
  /**
1856
1939
  * Wall-clock time (epoch ms) the function began, captured once so the whole
@@ -1868,14 +1951,14 @@ interface MutationCtx {
1868
1951
  * failure does not roll back earlier writes (the same as a top-level
1869
1952
  * mutation). Mirrors Convex's `ctx.runMutation`.
1870
1953
  */
1871
- readonly runMutation: <A extends ArgsValidator, R>(reference: RegisteredMutation<A, R>, args: InferArgs<A>) => Promise<R>;
1954
+ readonly runMutation: RunMutation;
1872
1955
  /**
1873
1956
  * Compose a read-only subquery in-process, reusing this mutation's `db`.
1874
1957
  * Executes the referenced query's handler directly — no fresh DO RPC — so
1875
1958
  * it observes this mutation's in-flight writes. Mirrors Convex's
1876
1959
  * `ctx.runQuery`.
1877
1960
  */
1878
- readonly runQuery: <A extends ArgsValidator, R>(reference: RegisteredQuery<A, R>, args: InferArgs<A>) => Promise<R>;
1961
+ readonly runQuery: RunQuery;
1879
1962
  readonly scheduler: Scheduler;
1880
1963
  /** Read account-level secrets from Cloudflare Secrets Store; see {@link Secrets}. */
1881
1964
  readonly secrets: Secrets;
@@ -1917,6 +2000,13 @@ interface ActionCtx {
1917
2000
  /** Structured, function-attributed logger; see {@link LunoraLogger}. */
1918
2001
  readonly log: LunoraLogger;
1919
2002
  /** Application counters, gauges, and histograms; see {@link LunoraMetrics}. */
2003
+ /**
2004
+ * Static metadata declared on this procedure with `.meta(...)`, merged
2005
+ * across calls. Present so middleware can read the policy it is meant to
2006
+ * enforce (`ctx.meta.rateLimit`, …) instead of having it hard-wired at each
2007
+ * `.use()` site; absent when the procedure never called `.meta()`.
2008
+ */
2009
+ readonly meta?: Record<string, unknown>;
1920
2010
  readonly metrics: LunoraMetrics;
1921
2011
  /**
1922
2012
  * Wall-clock time (epoch ms) the action began, captured once for convenience
@@ -1924,9 +2014,9 @@ interface ActionCtx {
1924
2014
  * may also use ambient `Date.now()` freely.
1925
2015
  */
1926
2016
  readonly now: number;
1927
- readonly runAction: <A extends ArgsValidator, R>(reference: RegisteredAction<A, R>, args: InferArgs<A>) => Promise<R>;
1928
- readonly runMutation: <A extends ArgsValidator, R>(reference: RegisteredMutation<A, R>, args: InferArgs<A>) => Promise<R>;
1929
- readonly runQuery: <A extends ArgsValidator, R>(reference: RegisteredQuery<A, R>, args: InferArgs<A>) => Promise<R>;
2017
+ readonly runAction: RunAction;
2018
+ readonly runMutation: RunMutation;
2019
+ readonly runQuery: RunQuery;
1930
2020
  readonly scheduler: Scheduler;
1931
2021
  /** Read account-level secrets from Cloudflare Secrets Store; see {@link Secrets}. */
1932
2022
  readonly secrets: Secrets;
package/dist/types.d.ts CHANGED
@@ -552,6 +552,12 @@ interface RegisteredFunction<A extends ArgsValidator, R, Kind extends FunctionKi
552
552
  * Absent on ordinary registrations.
553
553
  */
554
554
  readonly lifecycle?: LifecycleEventKind;
555
+ /**
556
+ * Static per-procedure metadata declared with `.meta(...)`. Present so
557
+ * middleware (via `ctx.meta`) and tooling can read the same object; absent
558
+ * when the chain never called `.meta()`.
559
+ */
560
+ readonly meta?: Record<string, unknown>;
555
561
  readonly visibility?: FunctionVisibility;
556
562
  /**
557
563
  * Set by the `.x402({ price })` builder modifier. Marks the procedure as paid
@@ -563,6 +569,48 @@ interface RegisteredFunction<A extends ArgsValidator, R, Kind extends FunctionKi
563
569
  type RegisteredQuery<A extends ArgsValidator, R> = RegisteredFunction<A, R, "query">;
564
570
  type RegisteredMutation<A extends ArgsValidator, R> = RegisteredFunction<A, R, "mutation">;
565
571
  type RegisteredAction<A extends ArgsValidator, R> = RegisteredFunction<A, R, "action">;
572
+ /**
573
+ * Structural mirror of `@lunora/client`'s `FunctionReference` — the handle the
574
+ * generated `api` / `internal` objects hand you, carrying `&lt;file>:&lt;function>`
575
+ * in `__lunoraRef`. Redeclared here so `@lunora/server` needs no dependency on
576
+ * the client package, exactly as {@link Scheduler} avoids one on
577
+ * `@lunora/scheduler`. `RegisteredFunction` has no `__lunoraRef`, so the two
578
+ * shapes never overlap.
579
+ */
580
+ interface FunctionHandle<Kind extends "action" | "mutation" | "query" | "stream", Args, Return> {
581
+ /** Phantom marker carrying the type parameters; never present at runtime. */
582
+ readonly __lunoraPhantom?: {
583
+ args: Args;
584
+ kind: Kind;
585
+ returns: Return;
586
+ };
587
+ readonly __lunoraRef: string;
588
+ }
589
+ /**
590
+ * `ctx.runQuery` — overloaded, see the note above.
591
+ *
592
+ * A single generic signature over the reference would be nicer (TS will not
593
+ * contextually type a parameter against a multi-signature type, so a hand-built
594
+ * ctx object must annotate its `(reference, args)` explicitly — see
595
+ * `@lunora/testing`'s harness). It does not work: a concrete
596
+ * `RegisteredQuery&lt;{…}, number>` is not assignable to a
597
+ * `RegisteredFunction&lt;ArgsValidator, …>` constraint, because `handler`'s args
598
+ * are in a contravariant position. Two inference sites it is.
599
+ */
600
+ interface RunQuery {
601
+ <A extends ArgsValidator, R>(reference: RegisteredQuery<A, R>, args: InferArgs<A>): Promise<R>;
602
+ <Args, R>(reference: FunctionHandle<"query", Args, R>, args: Args): Promise<R>;
603
+ }
604
+ /** `ctx.runMutation` — overloaded for the same reason as {@link RunQuery}. */
605
+ interface RunMutation {
606
+ <A extends ArgsValidator, R>(reference: RegisteredMutation<A, R>, args: InferArgs<A>): Promise<R>;
607
+ <Args, R>(reference: FunctionHandle<"mutation", Args, R>, args: Args): Promise<R>;
608
+ }
609
+ /** `ctx.runAction` — overloaded for the same reason as {@link RunQuery}. */
610
+ interface RunAction {
611
+ <A extends ArgsValidator, R>(reference: RegisteredAction<A, R>, args: InferArgs<A>): Promise<R>;
612
+ <Args, R>(reference: FunctionHandle<"action", Args, R>, args: Args): Promise<R>;
613
+ }
566
614
  /** Which side of the WebSocket lifecycle a hook fires on. */
567
615
  type LifecycleEventKind = "connect" | "disconnect";
568
616
  /**
@@ -752,6 +800,27 @@ interface PaginationResult<T = Record<string, unknown>> {
752
800
  * (schema-agnostic) `@lunora/server` reader.
753
801
  */
754
802
  interface TableReader<Row = Record<string, unknown>> {
803
+ /**
804
+ * Iterate rows lazily: `for await (const row of ctx.db.query("t").withIndex(…))`.
805
+ *
806
+ * Pages through the result set behind the scenes and yields row by row, so
807
+ * a consumer that stops early stops the reads too. `.collect()` is still the
808
+ * right terminal when you want the whole set; this exists for the cases
809
+ * where you cannot know up front how far you need to read.
810
+ *
811
+ * That is what merged/ordered index streams need. Reimplementing Convex's
812
+ * `convex-helpers/server/stream` in userland previously meant materialising
813
+ * each branch with a bounded `.take(1024)` before merging, so asking a
814
+ * merged stream for ONE row read up to 1,024 rows per branch. The k-way merge itself is application code and stays there — only
815
+ * the laziness had to come from the database layer.
816
+ *
817
+ * Iteration pages through `.paginate()`, so it follows the same order —
818
+ * which is `.collect()`'s order whenever the sort key is unique. Under a
819
+ * TIED sort key (an unindexed read whose rows share `_creationTime`) the
820
+ * two can disagree, because the tie-break is left to SQLite. Read through
821
+ * an index when order matters, exactly as you would for `.paginate()`.
822
+ */
823
+ [Symbol.asyncIterator]: () => AsyncIterator<Row>;
755
824
  collect: () => Promise<Row[]>;
756
825
  filter: (predicate: (document: Row) => boolean) => TableReader<Row>;
757
826
  first: () => Promise<Row | null>;
@@ -1803,6 +1872,13 @@ interface QueryCtx {
1803
1872
  /** Structured, function-attributed logger; see {@link LunoraLogger}. */
1804
1873
  readonly log: LunoraLogger;
1805
1874
  /** Application counters, gauges, and histograms; see {@link LunoraMetrics}. */
1875
+ /**
1876
+ * Static metadata declared on this procedure with `.meta(...)`, merged
1877
+ * across calls. Present so middleware can read the policy it is meant to
1878
+ * enforce (`ctx.meta.rateLimit`, …) instead of having it hard-wired at each
1879
+ * `.use()` site; absent when the procedure never called `.meta()`.
1880
+ */
1881
+ readonly meta?: Record<string, unknown>;
1806
1882
  readonly metrics: LunoraMetrics;
1807
1883
  /**
1808
1884
  * Wall-clock time (epoch ms) the function began, captured once so the whole
@@ -1820,7 +1896,7 @@ interface QueryCtx {
1820
1896
  * `runMutation` on a `QueryCtx` (writes are not allowed from a query).
1821
1897
  * Mirrors Convex's `ctx.runQuery`.
1822
1898
  */
1823
- readonly runQuery: <A extends ArgsValidator, R>(reference: RegisteredQuery<A, R>, args: InferArgs<A>) => Promise<R>;
1899
+ readonly runQuery: RunQuery;
1824
1900
  /** Read account-level secrets from Cloudflare Secrets Store; see {@link Secrets}. */
1825
1901
  readonly secrets: Secrets;
1826
1902
  /** Attach facts to THIS request's span — the wide event; see {@link LunoraWideEvent}. */
@@ -1851,6 +1927,13 @@ interface MutationCtx {
1851
1927
  /** Structured, function-attributed logger; see {@link LunoraLogger}. */
1852
1928
  readonly log: LunoraLogger;
1853
1929
  /** Application counters, gauges, and histograms; see {@link LunoraMetrics}. */
1930
+ /**
1931
+ * Static metadata declared on this procedure with `.meta(...)`, merged
1932
+ * across calls. Present so middleware can read the policy it is meant to
1933
+ * enforce (`ctx.meta.rateLimit`, …) instead of having it hard-wired at each
1934
+ * `.use()` site; absent when the procedure never called `.meta()`.
1935
+ */
1936
+ readonly meta?: Record<string, unknown>;
1854
1937
  readonly metrics: LunoraMetrics;
1855
1938
  /**
1856
1939
  * Wall-clock time (epoch ms) the function began, captured once so the whole
@@ -1868,14 +1951,14 @@ interface MutationCtx {
1868
1951
  * failure does not roll back earlier writes (the same as a top-level
1869
1952
  * mutation). Mirrors Convex's `ctx.runMutation`.
1870
1953
  */
1871
- readonly runMutation: <A extends ArgsValidator, R>(reference: RegisteredMutation<A, R>, args: InferArgs<A>) => Promise<R>;
1954
+ readonly runMutation: RunMutation;
1872
1955
  /**
1873
1956
  * Compose a read-only subquery in-process, reusing this mutation's `db`.
1874
1957
  * Executes the referenced query's handler directly — no fresh DO RPC — so
1875
1958
  * it observes this mutation's in-flight writes. Mirrors Convex's
1876
1959
  * `ctx.runQuery`.
1877
1960
  */
1878
- readonly runQuery: <A extends ArgsValidator, R>(reference: RegisteredQuery<A, R>, args: InferArgs<A>) => Promise<R>;
1961
+ readonly runQuery: RunQuery;
1879
1962
  readonly scheduler: Scheduler;
1880
1963
  /** Read account-level secrets from Cloudflare Secrets Store; see {@link Secrets}. */
1881
1964
  readonly secrets: Secrets;
@@ -1917,6 +2000,13 @@ interface ActionCtx {
1917
2000
  /** Structured, function-attributed logger; see {@link LunoraLogger}. */
1918
2001
  readonly log: LunoraLogger;
1919
2002
  /** Application counters, gauges, and histograms; see {@link LunoraMetrics}. */
2003
+ /**
2004
+ * Static metadata declared on this procedure with `.meta(...)`, merged
2005
+ * across calls. Present so middleware can read the policy it is meant to
2006
+ * enforce (`ctx.meta.rateLimit`, …) instead of having it hard-wired at each
2007
+ * `.use()` site; absent when the procedure never called `.meta()`.
2008
+ */
2009
+ readonly meta?: Record<string, unknown>;
1920
2010
  readonly metrics: LunoraMetrics;
1921
2011
  /**
1922
2012
  * Wall-clock time (epoch ms) the action began, captured once for convenience
@@ -1924,9 +2014,9 @@ interface ActionCtx {
1924
2014
  * may also use ambient `Date.now()` freely.
1925
2015
  */
1926
2016
  readonly now: number;
1927
- readonly runAction: <A extends ArgsValidator, R>(reference: RegisteredAction<A, R>, args: InferArgs<A>) => Promise<R>;
1928
- readonly runMutation: <A extends ArgsValidator, R>(reference: RegisteredMutation<A, R>, args: InferArgs<A>) => Promise<R>;
1929
- readonly runQuery: <A extends ArgsValidator, R>(reference: RegisteredQuery<A, R>, args: InferArgs<A>) => Promise<R>;
2017
+ readonly runAction: RunAction;
2018
+ readonly runMutation: RunMutation;
2019
+ readonly runQuery: RunQuery;
1930
2020
  readonly scheduler: Scheduler;
1931
2021
  /** Read account-level secrets from Cloudflare Secrets Store; see {@link Secrets}. */
1932
2022
  readonly secrets: Secrets;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/server",
3
- "version": "1.0.0-alpha.53",
3
+ "version": "1.0.0-alpha.55",
4
4
  "description": "Server primitives for Lunora: defineSchema, defineTable, query, mutation, and action",
5
5
  "keywords": [
6
6
  "backend",
@@ -66,9 +66,9 @@
66
66
  "access": "public"
67
67
  },
68
68
  "dependencies": {
69
- "@lunora/errors": "1.0.0-alpha.9",
70
- "@lunora/scheduler": "1.0.0-alpha.14",
71
- "@lunora/values": "1.0.0-alpha.12",
69
+ "@lunora/errors": "1.0.0-alpha.10",
70
+ "@lunora/scheduler": "1.0.0-alpha.16",
71
+ "@lunora/values": "1.0.0-alpha.13",
72
72
  "drizzle-orm": "^0.45.2",
73
73
  "hono": "^4.12.32"
74
74
  },
@@ -1 +0,0 @@
1
- import{s as c}from"./functions-CDC08CWY.mjs";import{l as g}from"./policy-tag-Dprt9JWo.mjs";import{l as p}from"./run-middleware-BeEEqmdE.mjs";const x=(r,e)=>p(r,e,a=>a),w=(r,e,a,t)=>async(i,o)=>{const d=c(r,o),n=await x(e,i),l=await a({args:d,ctx:n});return t?t.parse(l):l},y=(r,e,a)=>(t,i,o)=>{const d=c(r,i);return(async function*(){const n=await x(e,t),l=a({args:d,ctx:n,signal:o})[Symbol.asyncIterator]();try{for(;;){if(o.aborted)return;const u=await l.next();if(u.done||o.aborted)return;yield u.value}}finally{await l.return?.()}})()},m=r=>{const e=[];for(const a of r){const t=g(a);t&&e.push(t)}return e.length>0?{tags:e}:void 0},s=(r,e,a)=>({__lunoraProcedure:r,...a?{__lunoraVisibility:a}:{},input:t=>s(r,{...e,args:{...e.args,...t}},a),[r]:t=>{const i=m(e.middlewares);return{args:e.args,...e.expose?{expose:e.expose}:{},handler:w(e.args,e.middlewares,t,e.output),kind:r,...i?{rls:i}:{},...a?{visibility:a}:{},...e.x402?{x402:e.x402}:{}}},output:t=>s(r,{...e,output:t},a),...r==="query"?{stream:t=>{const i=m(e.middlewares);return{args:e.args,...e.expose?{expose:e.expose}:{},handler:y(e.args,e.middlewares,t),kind:"stream",...i?{rls:i}:{},...a?{visibility:a}:{},...e.x402?{x402:e.x402}:{}}}}:{},use:t=>s(r,{...e,middlewares:[...e.middlewares,t]},a),...a?{}:{expose:t=>s(r,{...e,expose:t},a)},...a?{}:{x402:t=>s(r,{...e,x402:t},a)}}),q={dataModel:()=>({create:r=>({action:s("action",{args:{},middlewares:[]}),internalAction:s("action",{args:{},middlewares:[]},"internal"),internalMutation:s("mutation",{args:{},middlewares:[]},"internal"),internalQuery:s("query",{args:{},middlewares:[]},"internal"),mutation:s("mutation",{args:{},middlewares:[]}),query:s("query",{args:{},middlewares:[]})})})};export{q as initLunora};