@pramen/server 0.0.48 → 0.0.50

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 (66) hide show
  1. package/dist/auth.d.ts +4 -3
  2. package/dist/cli.js +17 -30
  3. package/dist/dev.d.ts +1 -0
  4. package/dist/dev.js +9 -0
  5. package/dist/durable-object.d.ts +1 -0
  6. package/dist/durable-object.js +13 -6
  7. package/dist/index.d.ts +7 -5
  8. package/dist/index.js +6 -2
  9. package/dist/pramen.d.ts +4 -2
  10. package/dist/runtime/acl.d.ts +14 -5
  11. package/dist/runtime/acl.js +1 -5
  12. package/dist/runtime/db.d.ts +3 -2
  13. package/dist/runtime/dev-token.d.ts +4 -0
  14. package/dist/runtime/dev-token.js +34 -0
  15. package/dist/runtime/dispatch.d.ts +3 -1
  16. package/dist/runtime/dispatch.js +2 -0
  17. package/dist/runtime/driver.d.ts +8 -5
  18. package/dist/runtime/errors.d.ts +6 -0
  19. package/dist/runtime/errors.js +8 -0
  20. package/dist/runtime/mail.d.ts +2 -1
  21. package/dist/runtime/protocol.d.ts +4 -3
  22. package/dist/runtime/queue-consumer.d.ts +4 -2
  23. package/dist/runtime/queue.d.ts +3 -2
  24. package/dist/runtime/read-engine.d.ts +11 -9
  25. package/dist/runtime/read-engine.js +4 -1
  26. package/dist/runtime/registry.d.ts +4 -1
  27. package/dist/runtime/registry.js +0 -3
  28. package/dist/runtime/schema-diff.d.ts +6 -6
  29. package/dist/runtime/schema-diff.js +4 -4
  30. package/dist/runtime/storage.d.ts +4 -4
  31. package/dist/runtime/storage.js +8 -63
  32. package/dist/runtime/token.d.ts +22 -0
  33. package/dist/runtime/token.js +88 -0
  34. package/dist/sdk/acl.d.ts +18 -11
  35. package/dist/sdk/handlers.d.ts +16 -3
  36. package/dist/sdk/infer.d.ts +17 -0
  37. package/dist/worker.d.ts +2 -1
  38. package/dist/worker.js +20 -10
  39. package/package.json +13 -3
  40. package/src/auth.ts +7 -6
  41. package/src/cli.ts +22 -36
  42. package/src/dev.ts +10 -0
  43. package/src/durable-object.ts +18 -9
  44. package/src/index.ts +14 -4
  45. package/src/pramen.ts +4 -2
  46. package/src/runtime/acl.ts +45 -31
  47. package/src/runtime/db.ts +14 -12
  48. package/src/runtime/dev-token.ts +36 -0
  49. package/src/runtime/dispatch.ts +7 -3
  50. package/src/runtime/driver.ts +11 -7
  51. package/src/runtime/errors.ts +9 -0
  52. package/src/runtime/mail.ts +2 -1
  53. package/src/runtime/migrate.ts +2 -1
  54. package/src/runtime/outbox.ts +2 -1
  55. package/src/runtime/protocol.ts +5 -3
  56. package/src/runtime/queue-consumer.ts +4 -2
  57. package/src/runtime/queue.ts +4 -2
  58. package/src/runtime/read-engine.ts +27 -21
  59. package/src/runtime/registry.ts +5 -1
  60. package/src/runtime/schema-diff.ts +14 -14
  61. package/src/runtime/storage.ts +14 -62
  62. package/src/runtime/token.ts +94 -0
  63. package/src/sdk/acl.ts +29 -14
  64. package/src/sdk/handlers.ts +17 -3
  65. package/src/sdk/infer.ts +21 -0
  66. package/src/worker.ts +24 -11
package/src/worker.ts CHANGED
@@ -6,6 +6,8 @@
6
6
 
7
7
  import { authorizeTenant, HmacStrategy, isAdmin, JwksStrategy, resolveIdentity, type VerifyOptions, type VerifyStrategy } from "./auth";
8
8
  import { dispatch, tasksFacade, bindTasks } from "./runtime/dispatch";
9
+ import type { EnvBag } from "./sdk/handlers";
10
+ import type { JsonValue } from "./sdk/infer";
9
11
  import { ensureOutbox, drainOutbox, listTasks } from "./runtime/outbox";
10
12
  import { createMail } from "./runtime/mail";
11
13
  import { createQueue, type QueueProducerBinding } from "./runtime/queue";
@@ -23,6 +25,11 @@ import type { HandlerContext } from "./sdk/handlers";
23
25
  import { DEFAULT_PARTITION, partitionsOf } from "./sdk/schema";
24
26
  import type { PramenApp } from "./pramen";
25
27
 
28
+ /** Widen the closed `Env` interface to the open `EnvBag` handlers and services see.
29
+ * Spreading yields an anonymous object type, which TypeScript gives an implicit index
30
+ * signature — so this needs no type assertion. */
31
+ const envBag = (env: Env): EnvBag => ({ ...env });
32
+
26
33
  export interface Env {
27
34
  PRAMEN: DurableObjectNamespace;
28
35
  /** Project KV — tenant registry (`tenant:` keys) + handler ctx.kv (`app:` keys). */
@@ -149,7 +156,7 @@ function partitionStubFor(env: Env, tenant: string, partition: string = DEFAULT_
149
156
  * DO's JSON response (`{ ok, result }` / `{ ok: false, … }`). */
150
157
  export async function callPrivileged(
151
158
  env: Env,
152
- opts: { name: string; input?: unknown; tenant?: string; roles?: string[]; partition?: string },
159
+ opts: { name: string; input?: JsonValue; tenant?: string; roles?: string[]; partition?: string },
153
160
  ): Promise<Response> {
154
161
  const tenant = opts.tenant ?? "main";
155
162
  const partition = opts.partition ?? DEFAULT_PARTITION;
@@ -235,7 +242,10 @@ export function makeWorker(app: PramenApp) {
235
242
  const files = createFiles({ tenant: "main", secret: filesSecret(env), adapter: new R2Adapter(env.FILES) });
236
243
  const db = new Db(driver, { acl: d1Acl, identity, system: true, schema: app.schema, suppressTriggers: true }, app.schema);
237
244
  const kv = new Kv(env.KV);
238
- return { db, kv, files, env: env as unknown as Record<string, unknown>, identity, tasks: tasksFacade(driver), mail: createMail(env as unknown as Record<string, unknown>, kv), queue: createQueue(env as unknown as Record<string, unknown>) };
245
+ const bag = envBag(env);
246
+ // The D1 store is not per-tenant addressed (one shared database, no DO), so the
247
+ // task context runs as the default tenant — matching the `files` scope just above.
248
+ return { db, kv, files, env: bag, identity, tenant: "main", store: "d1", tasks: tasksFacade(driver), mail: createMail(bag, kv), queue: createQueue(bag) };
239
249
  };
240
250
 
241
251
  /** Drain the D1 outbox in the Worker (no DO/alarm on this path) — called by the
@@ -281,7 +291,7 @@ export function makeWorker(app: PramenApp) {
281
291
  for (const r of app.routes ?? []) {
282
292
  if (request.method === r.method && url.pathname === r.path) {
283
293
  const routeCtx = { callPrivileged: (opts: Parameters<typeof callPrivileged>[1]) => callPrivileged(env, opts) };
284
- return r.handler(request, env as unknown as Record<string, unknown>, routeCtx);
294
+ return r.handler(request, envBag(env), routeCtx);
285
295
  }
286
296
  }
287
297
 
@@ -473,8 +483,9 @@ export function makeWorker(app: PramenApp) {
473
483
  }
474
484
  // (isLive is excluded by useD1Store — live always routes to the DO below.)
475
485
  const name = url.pathname.replace(/^\/rpc\//, "");
476
- let input: unknown;
477
- if (request.method === "POST") input = await request.json().catch(() => undefined);
486
+ // The RPC body is JSON — parse it into the domain type once, here at the boundary.
487
+ let input: JsonValue = null;
488
+ if (request.method === "POST") input = ((await request.json().catch(() => null)) ?? null) as JsonValue;
478
489
 
479
490
  // Pick where the D1 session may start its first read. A mutation ALWAYS pins the
480
491
  // primary (`first-primary` is a superset of read-your-writes) so a read-modify-write
@@ -489,10 +500,12 @@ export function makeWorker(app: PramenApp) {
489
500
 
490
501
  const driver = new D1Driver(env.DB, { start });
491
502
  const files = createFiles({ tenant, secret: filesSecret(env), adapter: new R2Adapter(env.FILES) });
492
- const envBag = env as unknown as Record<string, unknown>;
503
+ const bag = envBag(env);
493
504
  try {
494
505
  await ensureD1Migrated(driver, env.PRAMEN_ALLOW_DESTRUCTIVE === "true");
495
- const { result, enqueued } = await dispatch(app.handlers, app.schema, driver, new Kv(env.KV), files, envBag, { acl: d1Acl, identity }, name, input);
506
+ // `tenant` matters here: a handler minting a tenant-scoped capability (a signed
507
+ // preview link) would otherwise stamp it "main" while reading acme's rows.
508
+ const { result, enqueued } = await dispatch(app.handlers, app.schema, driver, new Kv(env.KV), files, bag, { acl: d1Acl, identity, tenant, store: "d1" }, name, input);
496
509
  // Kick an immediate drain in the request tail when this handler enqueued tasks
497
510
  // (e.g. sendMagicLinkEmail). Without this, tasks wait for the next Cron trigger
498
511
  // — up to a full minute. `waitUntil` lets the response return now while the
@@ -567,13 +580,13 @@ export function makeWorker(app: PramenApp) {
567
580
  // handler (ACK on success / RETRY on throw, per message). A consumer is Worker-level
568
581
  // (no tenant DO): its ctx carries env/kv/mail/queue + callPrivileged to reach a DO.
569
582
  async queue(batch: QueueBatch, env: Env): Promise<void> {
570
- const envBag = env as unknown as Record<string, unknown>;
583
+ const bag = envBag(env);
571
584
  const kv = new Kv(env.KV);
572
585
  const ctx: QueueContext = {
573
- env: envBag,
586
+ env: bag,
574
587
  kv,
575
- mail: createMail(envBag, kv),
576
- queue: createQueue(envBag),
588
+ mail: createMail(bag, kv),
589
+ queue: createQueue(bag),
577
590
  callPrivileged: (opts) => callPrivileged(env, opts),
578
591
  };
579
592
  await dispatchQueueBatch(app.queues ?? {}, ctx, batch);