@beignet/core 0.0.25 → 0.0.27

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 (35) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +49 -5
  3. package/dist/ports/rate-limit.d.ts.map +1 -1
  4. package/dist/ports/rate-limit.js +12 -0
  5. package/dist/ports/rate-limit.js.map +1 -1
  6. package/dist/server/fixtures/run-service-context-script.fixture.d.ts +12 -0
  7. package/dist/server/fixtures/run-service-context-script.fixture.d.ts.map +1 -0
  8. package/dist/server/fixtures/run-service-context-script.fixture.js +36 -0
  9. package/dist/server/fixtures/run-service-context-script.fixture.js.map +1 -0
  10. package/dist/server/hooks/rate-limit.d.ts +17 -3
  11. package/dist/server/hooks/rate-limit.d.ts.map +1 -1
  12. package/dist/server/hooks/rate-limit.js +40 -4
  13. package/dist/server/hooks/rate-limit.js.map +1 -1
  14. package/dist/server/http.d.ts +12 -0
  15. package/dist/server/http.d.ts.map +1 -1
  16. package/dist/server/request-context.d.ts +9 -0
  17. package/dist/server/request-context.d.ts.map +1 -1
  18. package/dist/server/request-context.js +11 -0
  19. package/dist/server/request-context.js.map +1 -1
  20. package/dist/server/server.d.ts +19 -0
  21. package/dist/server/server.d.ts.map +1 -1
  22. package/dist/server/server.js +36 -1
  23. package/dist/server/server.js.map +1 -1
  24. package/dist/tasks/index.d.ts +22 -0
  25. package/dist/tasks/index.d.ts.map +1 -1
  26. package/dist/tasks/index.js.map +1 -1
  27. package/package.json +1 -1
  28. package/skills/app-architecture/SKILL.md +13 -3
  29. package/src/ports/rate-limit.ts +12 -0
  30. package/src/server/fixtures/run-service-context-script.fixture.ts +45 -0
  31. package/src/server/hooks/rate-limit.ts +59 -7
  32. package/src/server/http.ts +10 -0
  33. package/src/server/request-context.ts +15 -0
  34. package/src/server/server.ts +68 -0
  35. package/src/tasks/index.ts +23 -0
@@ -71,6 +71,7 @@ import {
71
71
  enterActiveRequestContext,
72
72
  readContextActor,
73
73
  readContextTenant,
74
+ runWithActiveRequestContext,
74
75
  setActiveRequestIdentity,
75
76
  } from "./request-context.js";
76
77
  import type {
@@ -428,10 +429,31 @@ export interface ServerInstance<
428
429
  * tasks, and background work.
429
430
  *
430
431
  * Requires `context.service` to be declared in `createServer(...)`.
432
+ *
433
+ * Only call this from long-lived runtimes such as servers, workers, and
434
+ * `bun test`. It enters the ambient correlation context with
435
+ * `AsyncLocalStorage.enterWith`, and resuming that frame across top-level
436
+ * await crashes Bun 1.3.x in plain scripts. Seeds and one-off scripts must
437
+ * use `runServiceContext(...)` instead.
431
438
  */
432
439
  createServiceContext: (
433
440
  ...args: ServiceContextInputArgs<ServiceInput>
434
441
  ) => Promise<Ctx>;
442
+ /**
443
+ * Build a service context and run `fn` inside a scoped ambient correlation
444
+ * frame, returning its result.
445
+ *
446
+ * This is the script-safe counterpart to `createServiceContext(...)`: the
447
+ * ambient frame is entered with `AsyncLocalStorage.run`, so plain scripts
448
+ * such as seeds and one-off maintenance work stay safe under top-level
449
+ * await. Requires `context.service` to be declared in `createServer(...)`.
450
+ */
451
+ runServiceContext: <T>(
452
+ ...args: [
453
+ ...ServiceContextInputArgs<ServiceInput>,
454
+ fn: (ctx: Ctx) => T | Promise<T>,
455
+ ]
456
+ ) => Promise<T>;
435
457
  /**
436
458
  * Contract configs registered through the `routes` option.
437
459
  */
@@ -2032,6 +2054,11 @@ export async function createServer<
2032
2054
  ];
2033
2055
  const contracts = options.routes ? contractsFromRoutes(options.routes) : [];
2034
2056
 
2057
+ // Fail startup on hook misconfiguration before provider setup runs.
2058
+ for (const hook of hooks) {
2059
+ hook.validate?.({ contracts });
2060
+ }
2061
+
2035
2062
  const resolvedContext = resolveServerContext<Ctx, FinalPorts, ServiceInput>(
2036
2063
  options.context,
2037
2064
  );
@@ -2089,6 +2116,46 @@ export async function createServer<
2089
2116
  ambient.tenant = readContextTenant(ctx);
2090
2117
  return ctx;
2091
2118
  };
2119
+ const runServiceContext = async <T>(
2120
+ ...args: [
2121
+ ...ServiceContextInputArgs<ServiceInput>,
2122
+ fn: (ctx: Ctx) => T | Promise<T>,
2123
+ ]
2124
+ ): Promise<T> => {
2125
+ const serviceFactory = resolvedContext.service;
2126
+ if (!serviceFactory) {
2127
+ throw new Error(
2128
+ "Define context.service in createServer(...) to create service contexts.",
2129
+ );
2130
+ }
2131
+
2132
+ const fn = args[args.length - 1] as (ctx: Ctx) => T | Promise<T>;
2133
+ const input = (args.length > 1 ? args[0] : undefined) as ServiceInput;
2134
+ const { requestId, trace } = instrumentation.createServiceCorrelation();
2135
+ const ambient: ActiveRequestContext = {
2136
+ requestId,
2137
+ traceId: trace.traceId,
2138
+ spanId: trace.spanId,
2139
+ parentSpanId: trace.parentSpanId,
2140
+ traceparent: trace.traceparent,
2141
+ };
2142
+ // AsyncLocalStorage.run scopes the ambient frame to this callback, so the
2143
+ // caller's continuation never resumes through an enterWith frame — the
2144
+ // pattern that crashes Bun 1.3.x in plain scripts under top-level await.
2145
+ return runWithActiveRequestContext(ambient, async () => {
2146
+ const ctx = finalizeContext(
2147
+ await serviceFactory({
2148
+ ports: finalPorts,
2149
+ input,
2150
+ requestId,
2151
+ trace,
2152
+ }),
2153
+ );
2154
+ ambient.actor = readContextActor(ctx);
2155
+ ambient.tenant = readContextTenant(ctx);
2156
+ return await fn(ctx);
2157
+ });
2158
+ };
2092
2159
  const contextRuntime = {
2093
2160
  createRequestContext,
2094
2161
  finalizeContext,
@@ -2426,6 +2493,7 @@ export async function createServer<
2426
2493
  },
2427
2494
  createRequestContext: (req) => createRequestContext(req),
2428
2495
  createServiceContext,
2496
+ runServiceContext,
2429
2497
  contracts,
2430
2498
  stop,
2431
2499
  ports: finalPorts,
@@ -121,6 +121,29 @@ export interface RunTaskOptions<Ctx> {
121
121
  ctx: Ctx;
122
122
  }
123
123
 
124
+ /**
125
+ * Arguments `beignet task run` passes to the app's `createTaskContext` and
126
+ * `stopTaskContext` exports in `server/tasks.ts`.
127
+ */
128
+ export interface TaskRunContextArgs<T extends TaskDef = TaskDef> {
129
+ /**
130
+ * Task definition being run.
131
+ */
132
+ task: T;
133
+ /**
134
+ * Stable task name being run.
135
+ */
136
+ taskName: string;
137
+ /**
138
+ * Schema-parsed task input.
139
+ */
140
+ input: unknown;
141
+ /**
142
+ * Tenant id or slug from `--tenant`, resolved by the app.
143
+ */
144
+ tenant?: string;
145
+ }
146
+
124
147
  /**
125
148
  * Context-bound operational task helper factory.
126
149
  */