@prisma/composer 0.1.0-dev.14 → 0.1.0-dev.15

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.
@@ -37,17 +37,25 @@ declare function contract<Fns extends Record<string, (input: any) => Promise<any
37
37
  declare const RPC_ACCEPTED_KEYS_ENV = "COMPOSER_RPC_ACCEPTED_KEYS";
38
38
  type AnyRunnable = RunnableServiceNode<any, any, any>;
39
39
  type CmpOf<C> = C extends Contract<string, infer Cmp> ? Cmp : never;
40
- type HandlerFor<Fn, LoadedDeps> = Fn extends ((input: infer I) => Promise<infer O>) ? (input: I, deps: LoadedDeps) => Promise<O> : never;
40
+ /** A handler's optional third argument. Handlers may ignore it. */
41
+ interface RpcHandlerContext {
42
+ /** The call's idempotency key (same across its retries), or `undefined` for a keyless caller that opted out of dedup. */
43
+ readonly idempotencyKey: string | undefined;
44
+ }
45
+ type HandlerFor<Fn, LoadedDeps> = Fn extends ((input: infer I) => Promise<infer O>) ? (input: I, deps: LoadedDeps, ctx: RpcHandlerContext) => Promise<O> : never;
41
46
  /** Every exposed port's methods, turned into a handler map typed off S's own `expose` and `load()`. */
42
47
  type Handlers<S extends AnyRunnable> = { [Port in keyof NonNullable<S['expose']>]: { [M in keyof CmpOf<NonNullable<S['expose']>[Port]>]: HandlerFor<CmpOf<NonNullable<S['expose']>[Port]>[M], ReturnType<S['load']>>; }; };
43
48
  /**
44
- * Routes `POST /rpc/<method>`: parses JSON, validates input, calls the
45
- * handler with `service.load()`'s deps, validates the output, and responds
46
- * JSON. An unknown method or invalid input is a 4xx; a handler (or output
47
- * validation) failure is a 5xx either way the process does not crash.
48
- * `load()` is called exactly once, here, before the handler ever runs.
49
+ * Routes `POST /rpc/<method>`: checks the service key, requires an
50
+ * Idempotency-Key, single-flights/replays through `IdempotencyStore`, and
51
+ * per call parses JSON within the body cap, validates input, calls the
52
+ * handler with `service.load()`'s deps plus `{ idempotencyKey }`, validates
53
+ * the output, and responds JSON. A handler or output-validation failure
54
+ * masks its message behind a generic 500 and logs the real error; an
55
+ * unknown method or invalid input is a 4xx. `load()` is called exactly
56
+ * once, here, before the handler ever runs.
49
57
  */
50
58
  declare function serve<S extends AnyRunnable, H extends Handlers<S>>(service: S, handlers: H): (req: Request) => Promise<Response>;
51
59
  //#endregion
52
- export { type Client, type Handlers, RPC_ACCEPTED_KEYS_ENV, RPC_PEER_KEY, type Transport, contract, makeClient, perBindingToken, rpc, serve };
60
+ export { type Client, type Handlers, RPC_ACCEPTED_KEYS_ENV, RPC_PEER_KEY, type RpcHandlerContext, type Transport, contract, makeClient, perBindingToken, rpc, serve };
53
61
  //# sourceMappingURL=service-rpc.d.mts.map
@@ -2,15 +2,20 @@ import { t as blindCast } from "./casts-Ci5rYYaR.mjs";
2
2
  import { i as dependency, p as provisionNeed } from "./graph-BmrUEdo9-6Oq1hSmS.mjs";
3
3
  import { l as string } from "./dist-B0axxnBf.mjs";
4
4
  //#region ../../0-framework/2-authoring/service-rpc/dist/index.mjs
5
- async function standardValidate(schema, value) {
6
- const result = await schema["~standard"].validate(value);
7
- if (result.issues !== void 0) throw new Error(`Schema validation failed: ${result.issues.map((issue) => issue.message).join("; ")}`);
8
- return result.value;
5
+ /** Bounded jittered backoff for retrying a dropped call. `maxRetries` is retries after the first attempt. */
6
+ const RETRY = {
7
+ initialDelayMs: 250,
8
+ multiplier: 2,
9
+ maxDelayMs: 5e3,
10
+ maxRetries: 5
11
+ };
12
+ const IDEMPOTENCY_KEY_HEADER$1 = "Idempotency-Key";
13
+ function sleep(ms) {
14
+ return new Promise((resolve) => setTimeout(resolve, ms));
9
15
  }
10
- /** `<base>/rpc/<method>`, preserving a base URL's own path (e.g. a mount point). */
11
- function methodUrl(base, method) {
12
- const normalizedBase = base.endsWith("/") ? base : `${base}/`;
13
- return new URL(`rpc/${method}`, normalizedBase).toString();
16
+ /** Whether a non-OK response is safe to retry: 429 or any 5xx, never another 4xx. */
17
+ function isRetryableStatus(status) {
18
+ return status === 429 || status >= 500;
14
19
  }
15
20
  /** The server's `{ error }` body, if the response has one — undefined otherwise. */
16
21
  async function errorDetail(res) {
@@ -21,22 +26,56 @@ async function errorDetail(res) {
21
26
  return;
22
27
  }
23
28
  }
29
+ /** `<base>/rpc/<method>`, preserving a base URL's own path (e.g. a mount point). */
30
+ function methodUrl(base, method) {
31
+ const normalizedBase = base.endsWith("/") ? base : `${base}/`;
32
+ return new URL(`rpc/${method}`, normalizedBase).toString();
33
+ }
34
+ /**
35
+ * Sends one call over `send`, retrying a thrown error, 429, or 5xx with
36
+ * full-jitter backoff. `buildRequest` runs per attempt but carries the same
37
+ * idempotency key each time — only the transport call repeats, not the key.
38
+ */
39
+ async function callWithRetry(send, buildRequest, method) {
40
+ let delay = RETRY.initialDelayMs;
41
+ let retries = 0;
42
+ for (;;) {
43
+ let res;
44
+ try {
45
+ res = await send(buildRequest());
46
+ } catch (err) {
47
+ if (retries >= RETRY.maxRetries) throw err;
48
+ retries += 1;
49
+ await sleep(Math.random() * delay);
50
+ delay = Math.min(delay * RETRY.multiplier, RETRY.maxDelayMs);
51
+ continue;
52
+ }
53
+ if (res.ok) return res.json();
54
+ if (!isRetryableStatus(res.status) || retries >= RETRY.maxRetries) {
55
+ const detail = await errorDetail(res);
56
+ throw new Error(`RPC call "${method}" failed: ${res.status} ${res.statusText}` + (detail !== void 0 ? ` — ${detail}` : ""));
57
+ }
58
+ retries += 1;
59
+ await sleep(Math.random() * delay);
60
+ delay = Math.min(delay * RETRY.multiplier, RETRY.maxDelayMs);
61
+ }
62
+ }
24
63
  function makeClient(contract, url, opts) {
25
64
  const send = opts?.fetch ?? fetch;
26
- const headers = { "content-type": "application/json" };
27
- if (opts?.serviceKey !== void 0) headers["Authorization"] = `Bearer ${opts.serviceKey}`;
65
+ const baseHeaders = { "content-type": "application/json" };
66
+ if (opts?.serviceKey !== void 0) baseHeaders["Authorization"] = `Bearer ${opts.serviceKey}`;
28
67
  const client = {};
29
- for (const [method, schemas] of Object.entries(blindCast(contract.__cmp))) client[method] = async (input) => {
30
- const res = await send(new Request(methodUrl(url, method), {
68
+ for (const method of Object.keys(contract.__cmp)) client[method] = async (input) => {
69
+ const idempotencyKey = crypto.randomUUID();
70
+ const body = JSON.stringify(input);
71
+ return callWithRetry(send, () => new Request(methodUrl(url, method), {
31
72
  method: "POST",
32
- headers,
33
- body: JSON.stringify(input)
34
- }));
35
- if (!res.ok) {
36
- const detail = await errorDetail(res);
37
- throw new Error(`RPC call "${method}" failed: ${res.status} ${res.statusText}` + (detail !== void 0 ? ` — ${detail}` : ""));
38
- }
39
- return standardValidate(schemas.output, await res.json());
73
+ headers: {
74
+ ...baseHeaders,
75
+ [IDEMPOTENCY_KEY_HEADER$1]: idempotencyKey
76
+ },
77
+ body
78
+ }), method);
40
79
  };
41
80
  return blindCast(client);
42
81
  }
@@ -72,14 +111,125 @@ function rpc(arg) {
72
111
  function isRpcContract(value) {
73
112
  return typeof value === "object" && value !== null && "kind" in value && value.kind === "rpc" && "__cmp" in value && "satisfies" in value;
74
113
  }
114
+ async function standardValidate(schema, value) {
115
+ const result = await schema["~standard"].validate(value);
116
+ if (result.issues !== void 0) throw new Error(`Schema validation failed: ${result.issues.map((issue) => issue.message).join("; ")}`);
117
+ return result.value;
118
+ }
75
119
  /** The reserved env var the target (slice 2) writes the accepted key set to. */
76
120
  const RPC_ACCEPTED_KEYS_ENV = "COMPOSER_RPC_ACCEPTED_KEYS";
77
- function jsonResponse(body, status = 200) {
78
- return new Response(JSON.stringify(body), {
121
+ function outcome(body, status = 200) {
122
+ return {
79
123
  status,
124
+ bodyText: JSON.stringify(body)
125
+ };
126
+ }
127
+ function toResponse(o) {
128
+ return new Response(o.bodyText, {
129
+ status: o.status,
80
130
  headers: { "content-type": "application/json" }
81
131
  });
82
132
  }
133
+ /** The generic message every caller-facing 500 carries — the real error goes to `console.error` instead. */
134
+ const INTERNAL_ERROR_MESSAGE = "Internal server error";
135
+ /** Request body cap. Internal RPC payloads are small records, not uploads; 1 MiB bounds a slow request's memory. */
136
+ const MAX_BODY_BYTES = 1048576;
137
+ var RequestBodyTooLargeError = class extends Error {};
138
+ /** Reads the body as text, aborting past `maxBytes` of bytes actually read — `content-length` is caller-supplied, so untrusted. */
139
+ async function readBoundedBody(req, maxBytes) {
140
+ const reader = req.body?.getReader();
141
+ if (reader === void 0) return "";
142
+ const decoder = new TextDecoder();
143
+ let text = "";
144
+ let total = 0;
145
+ for (;;) {
146
+ const { done, value } = await reader.read();
147
+ if (done) break;
148
+ total += value.byteLength;
149
+ if (total > maxBytes) {
150
+ await reader.cancel();
151
+ throw new RequestBodyTooLargeError();
152
+ }
153
+ text += decoder.decode(value, { stream: true });
154
+ }
155
+ text += decoder.decode();
156
+ return text;
157
+ }
158
+ /** How long a completed 2xx/4xx answer stays replayable for a repeated key. */
159
+ const REPLAY_TTL_MS = 6e4;
160
+ /**
161
+ * Per-method, per-key deduplication. A duplicate arriving mid-execution
162
+ * single-flights onto the same promise; a completed 2xx/4xx replays for
163
+ * REPLAY_TTL_MS; a 5xx is never kept, since that is what a retry re-executes.
164
+ * Keyed by method first, so a replay can never answer a different method.
165
+ */
166
+ var IdempotencyStore = class {
167
+ byMethod = /* @__PURE__ */ new Map();
168
+ lruOrder = /* @__PURE__ */ new Map();
169
+ async dispatch(method, key, run) {
170
+ const bucket = this.bucketFor(method);
171
+ const existing = bucket.get(key);
172
+ if (existing?.kind === "pending") return existing.promise;
173
+ if (existing?.kind === "completed") {
174
+ if (Date.now() - existing.completedAt < REPLAY_TTL_MS) {
175
+ this.touch(method, key);
176
+ return existing.outcome;
177
+ }
178
+ bucket.delete(key);
179
+ this.lruOrder.delete(this.lruKey(method, key));
180
+ }
181
+ const promise = run();
182
+ bucket.set(key, {
183
+ kind: "pending",
184
+ promise
185
+ });
186
+ let result;
187
+ try {
188
+ result = await promise;
189
+ } catch (err) {
190
+ bucket.delete(key);
191
+ throw err;
192
+ }
193
+ if (result.status >= 500) bucket.delete(key);
194
+ else {
195
+ bucket.set(key, {
196
+ kind: "completed",
197
+ outcome: result,
198
+ completedAt: Date.now()
199
+ });
200
+ this.touch(method, key);
201
+ }
202
+ return result;
203
+ }
204
+ bucketFor(method) {
205
+ let bucket = this.byMethod.get(method);
206
+ if (bucket === void 0) {
207
+ bucket = /* @__PURE__ */ new Map();
208
+ this.byMethod.set(method, bucket);
209
+ }
210
+ return bucket;
211
+ }
212
+ lruKey(method, key) {
213
+ return `${method}\0${key}`;
214
+ }
215
+ /** Marks (method, key) most-recently-used, evicting the oldest completed entry once over the bound. */
216
+ touch(method, key) {
217
+ const lruKey = this.lruKey(method, key);
218
+ this.lruOrder.delete(lruKey);
219
+ this.lruOrder.set(lruKey, {
220
+ method,
221
+ key
222
+ });
223
+ if (this.lruOrder.size > 1e3) {
224
+ const oldestKey = this.lruOrder.keys().next().value;
225
+ const oldest = oldestKey === void 0 ? void 0 : this.lruOrder.get(oldestKey);
226
+ if (oldestKey !== void 0 && oldest !== void 0) {
227
+ this.lruOrder.delete(oldestKey);
228
+ this.byMethod.get(oldest.method)?.delete(oldest.key);
229
+ }
230
+ }
231
+ }
232
+ };
83
233
  /** The provisioned accepted key set, or undefined when the deploy never provisioned one (local/test — enforcement off). */
84
234
  function acceptedKeys() {
85
235
  const raw = process.env[RPC_ACCEPTED_KEYS_ENV];
@@ -116,6 +266,7 @@ function bearerToken(req) {
116
266
  const header = req.headers.get("authorization");
117
267
  return header?.startsWith(BEARER_PREFIX) ? header.slice(7) : "";
118
268
  }
269
+ const IDEMPOTENCY_KEY_HEADER = "Idempotency-Key";
119
270
  /**
120
271
  * Flattens every exposed port's methods into one method → {schemas, handler}
121
272
  * table. RPC dispatch is flat (`/rpc/<method>`), so a method name exposed by
@@ -140,42 +291,67 @@ function methodTable(expose, handlers) {
140
291
  return table;
141
292
  }
142
293
  /**
143
- * Routes `POST /rpc/<method>`: parses JSON, validates input, calls the
144
- * handler with `service.load()`'s deps, validates the output, and responds
145
- * JSON. An unknown method or invalid input is a 4xx; a handler (or output
146
- * validation) failure is a 5xx either way the process does not crash.
147
- * `load()` is called exactly once, here, before the handler ever runs.
294
+ * Routes `POST /rpc/<method>`: checks the service key, requires an
295
+ * Idempotency-Key, single-flights/replays through `IdempotencyStore`, and
296
+ * per call parses JSON within the body cap, validates input, calls the
297
+ * handler with `service.load()`'s deps plus `{ idempotencyKey }`, validates
298
+ * the output, and responds JSON. A handler or output-validation failure
299
+ * masks its message behind a generic 500 and logs the real error; an
300
+ * unknown method or invalid input is a 4xx. `load()` is called exactly
301
+ * once, here, before the handler ever runs.
148
302
  */
149
303
  function serve(service, handlers) {
150
304
  const table = methodTable(service.expose ?? {}, blindCast(handlers));
151
305
  const deps = service.load();
306
+ const idempotency = new IdempotencyStore();
152
307
  return async (req) => {
153
308
  const accepted = acceptedKeys();
154
- if (accepted !== void 0 && !isAcceptedKey(bearerToken(req), accepted)) return jsonResponse({ error: "Unauthorized: missing or invalid service key" }, 401);
309
+ if (accepted !== void 0 && !isAcceptedKey(bearerToken(req), accepted)) return toResponse(outcome({ error: "Unauthorized: missing or invalid service key" }, 401));
155
310
  const { pathname } = new URL(req.url);
156
311
  const methodName = /^\/rpc\/([^/]+)$/.exec(pathname)?.[1];
157
- if (methodName === void 0) return jsonResponse({ error: `Not found: ${pathname}` }, 404);
312
+ if (methodName === void 0) return toResponse(outcome({ error: `Not found: ${pathname}` }, 404));
158
313
  const method = table.get(methodName);
159
- if (method === void 0) return jsonResponse({ error: `Unknown RPC method "${methodName}"` }, 404);
160
- if (req.method !== "POST") return jsonResponse({ error: `Method "${methodName}" requires POST` }, 405);
161
- let body;
162
- try {
163
- body = await req.json();
164
- } catch {
165
- return jsonResponse({ error: "Request body must be JSON" }, 400);
166
- }
167
- let input;
168
- try {
169
- input = await standardValidate(method.input, body);
170
- } catch (err) {
171
- return jsonResponse({ error: err instanceof Error ? err.message : String(err) }, 400);
172
- }
173
- try {
174
- const result = await method.handler(input, deps);
175
- return jsonResponse(await standardValidate(method.output, result));
176
- } catch (err) {
177
- return jsonResponse({ error: err instanceof Error ? err.message : String(err) }, 500);
178
- }
314
+ if (method === void 0) return toResponse(outcome({ error: `Unknown RPC method "${methodName}"` }, 404));
315
+ if (req.method !== "POST") return toResponse(outcome({ error: `Method "${methodName}" requires POST` }, 405));
316
+ const idempotencyKey = req.headers.get(IDEMPOTENCY_KEY_HEADER.toLowerCase()) || void 0;
317
+ const ctx = { idempotencyKey };
318
+ const run = async () => {
319
+ let bodyText;
320
+ try {
321
+ bodyText = await readBoundedBody(req, MAX_BODY_BYTES);
322
+ } catch (err) {
323
+ if (err instanceof RequestBodyTooLargeError) return outcome({ error: `Request body exceeds the ${MAX_BODY_BYTES}-byte limit` }, 413);
324
+ console.error(`serve(): reading the request body for "${methodName}" failed:`, err);
325
+ return outcome({ error: INTERNAL_ERROR_MESSAGE }, 500);
326
+ }
327
+ let body;
328
+ try {
329
+ body = JSON.parse(bodyText);
330
+ } catch {
331
+ return outcome({ error: "Request body must be JSON" }, 400);
332
+ }
333
+ let input;
334
+ try {
335
+ input = await standardValidate(method.input, body);
336
+ } catch (err) {
337
+ return outcome({ error: err instanceof Error ? err.message : String(err) }, 400);
338
+ }
339
+ try {
340
+ const result = await method.handler(input, deps, ctx);
341
+ let output;
342
+ try {
343
+ output = await standardValidate(method.output, result);
344
+ } catch (err) {
345
+ console.error(`serve(): handler for "${methodName}" returned output that failed schema validation — this is a provider bug:`, err);
346
+ return outcome({ error: INTERNAL_ERROR_MESSAGE }, 500);
347
+ }
348
+ return outcome(output);
349
+ } catch (err) {
350
+ console.error(`serve(): handler for "${methodName}" threw:`, err);
351
+ return outcome({ error: INTERNAL_ERROR_MESSAGE }, 500);
352
+ }
353
+ };
354
+ return toResponse(idempotencyKey === void 0 ? await run() : await idempotency.dispatch(methodName, idempotencyKey, run));
179
355
  };
180
356
  }
181
357
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"service-rpc.mjs","names":[],"sources":["../../../0-framework/2-authoring/service-rpc/dist/index.mjs"],"sourcesContent":["import { blindCast } from \"@internal/foundation/casts\";\nimport { dependency, provisionNeed, string } from \"@internal/core\";\n//#region src/standard-schema.ts\nasync function standardValidate(schema, value) {\n\tconst result = await schema[\"~standard\"].validate(value);\n\tif (result.issues !== void 0) throw new Error(`Schema validation failed: ${result.issues.map((issue) => issue.message).join(\"; \")}`);\n\treturn result.value;\n}\n//#endregion\n//#region src/client.ts\n/** `<base>/rpc/<method>`, preserving a base URL's own path (e.g. a mount point). */\nfunction methodUrl(base, method) {\n\tconst normalizedBase = base.endsWith(\"/\") ? base : `${base}/`;\n\treturn new URL(`rpc/${method}`, normalizedBase).toString();\n}\n/** The server's `{ error }` body, if the response has one — undefined otherwise. */\nasync function errorDetail(res) {\n\ttry {\n\t\tconst body = await res.json();\n\t\treturn typeof body === \"object\" && body !== null && \"error\" in body ? String(body.error) : void 0;\n\t} catch {\n\t\treturn;\n\t}\n}\nfunction makeClient(contract, url, opts) {\n\tconst send = opts?.fetch ?? fetch;\n\tconst headers = { \"content-type\": \"application/json\" };\n\tif (opts?.serviceKey !== void 0) headers[\"Authorization\"] = `Bearer ${opts.serviceKey}`;\n\tconst client = {};\n\tfor (const [method, schemas] of Object.entries(blindCast(contract.__cmp))) client[method] = async (input) => {\n\t\tconst res = await send(new Request(methodUrl(url, method), {\n\t\t\tmethod: \"POST\",\n\t\t\theaders,\n\t\t\tbody: JSON.stringify(input)\n\t\t}));\n\t\tif (!res.ok) {\n\t\t\tconst detail = await errorDetail(res);\n\t\t\tthrow new Error(`RPC call \"${method}\" failed: ${res.status} ${res.statusText}` + (detail !== void 0 ? ` — ${detail}` : \"\"));\n\t\t}\n\t\treturn standardValidate(schemas.output, await res.json());\n\t};\n\treturn blindCast(client);\n}\n//#endregion\n//#region src/contract.ts\nfunction contract(fns) {\n\tconst value = {\n\t\tkind: \"rpc\",\n\t\t__cmp: fns,\n\t\tsatisfies: (required) => value === required\n\t};\n\treturn Object.freeze(value);\n}\n//#endregion\n//#region src/rpc.ts\n/** ADR-0031's need brand for RPC's per-binding service key — the target registers a provisioner under this. */\nconst RPC_PEER_KEY = Symbol.for(\"prisma:rpc/per-binding-key\");\n/** The provisioning need `rpc()`'s `serviceKey` param declares (ADR-0030): a shared, unguessable value the target mints per consumer edge. */\nconst perBindingToken = () => provisionNeed(RPC_PEER_KEY);\nfunction rpc(arg) {\n\tif (!isRpcContract(arg)) return arg;\n\treturn dependency({\n\t\ttype: \"rpc\",\n\t\tconnection: {\n\t\t\tparams: {\n\t\t\t\turl: string(),\n\t\t\t\tserviceKey: string({\n\t\t\t\t\toptional: true,\n\t\t\t\t\tprovision: perBindingToken()\n\t\t\t\t})\n\t\t\t},\n\t\t\thydrate: ({ url, serviceKey }) => makeClient(arg, url, { serviceKey })\n\t\t},\n\t\trequired: arg\n\t});\n}\nfunction isRpcContract(value) {\n\treturn typeof value === \"object\" && value !== null && \"kind\" in value && value.kind === \"rpc\" && \"__cmp\" in value && \"satisfies\" in value;\n}\n//#endregion\n//#region src/serve.ts\n/** The reserved env var the target (slice 2) writes the accepted key set to. */\nconst RPC_ACCEPTED_KEYS_ENV = \"COMPOSER_RPC_ACCEPTED_KEYS\";\nfunction jsonResponse(body, status = 200) {\n\treturn new Response(JSON.stringify(body), {\n\t\tstatus,\n\t\theaders: { \"content-type\": \"application/json\" }\n\t});\n}\n/** The provisioned accepted key set, or undefined when the deploy never provisioned one (local/test — enforcement off). */\nfunction acceptedKeys() {\n\tconst raw = process.env[RPC_ACCEPTED_KEYS_ENV];\n\tif (raw === void 0 || raw === \"\") return void 0;\n\tlet parsed;\n\ttry {\n\t\tparsed = JSON.parse(raw);\n\t} catch {\n\t\treturn [];\n\t}\n\treturn Array.isArray(parsed) && parsed.every((key) => typeof key === \"string\") ? parsed : [];\n}\n/**\n* Length-independent constant-time string equality — no early exit on the\n* first mismatched character or on a length difference, so a caller cannot\n* time its way toward a valid key. No `node:crypto`, to keep this module\n* runtime-agnostic.\n*/\nfunction constantTimeEquals(a, b) {\n\tconst length = Math.max(a.length, b.length);\n\tlet diff = a.length ^ b.length;\n\tfor (let i = 0; i < length; i++) diff |= (i < a.length ? a.charCodeAt(i) : 0) ^ (i < b.length ? b.charCodeAt(i) : 0);\n\treturn diff === 0;\n}\n/** Whether `presented` is a member of `accepted` — always compares against every key. */\nfunction isAcceptedKey(presented, accepted) {\n\tlet matched = false;\n\tfor (const key of accepted) matched = constantTimeEquals(presented, key) || matched;\n\treturn matched;\n}\nconst BEARER_PREFIX = \"Bearer \";\n/** The bearer token on `Authorization`, or `''` if the header is missing or malformed. */\nfunction bearerToken(req) {\n\tconst header = req.headers.get(\"authorization\");\n\treturn header?.startsWith(BEARER_PREFIX) ? header.slice(7) : \"\";\n}\n/**\n* Flattens every exposed port's methods into one method → {schemas, handler}\n* table. RPC dispatch is flat (`/rpc/<method>`), so a method name exposed by\n* more than one port is a construction-time error, as is a missing handler.\n*/\nfunction methodTable(expose, handlers) {\n\tconst table = /* @__PURE__ */ new Map();\n\tfor (const [port, contract] of Object.entries(expose)) {\n\t\tconst portHandlers = handlers[port] ?? {};\n\t\tfor (const [method, fn] of Object.entries(contract.__cmp)) {\n\t\t\tif (table.has(method)) throw new Error(`serve(): method \"${method}\" is exposed by more than one port — RPC dispatch is flat (POST /rpc/<method>), so method names must be unique across a service's exposed ports.`);\n\t\t\tconst handler = portHandlers[method];\n\t\t\tif (handler === void 0) throw new Error(`serve(): no handler supplied for exposed method \"${port}.${method}\".`);\n\t\t\tconst { input, output } = blindCast(fn);\n\t\t\ttable.set(method, {\n\t\t\t\tinput,\n\t\t\t\toutput,\n\t\t\t\thandler\n\t\t\t});\n\t\t}\n\t}\n\treturn table;\n}\n/**\n* Routes `POST /rpc/<method>`: parses JSON, validates input, calls the\n* handler with `service.load()`'s deps, validates the output, and responds\n* JSON. An unknown method or invalid input is a 4xx; a handler (or output\n* validation) failure is a 5xx — either way the process does not crash.\n* `load()` is called exactly once, here, before the handler ever runs.\n*/\nfunction serve(service, handlers) {\n\tconst table = methodTable(service.expose ?? {}, blindCast(handlers));\n\tconst deps = service.load();\n\treturn async (req) => {\n\t\tconst accepted = acceptedKeys();\n\t\tif (accepted !== void 0 && !isAcceptedKey(bearerToken(req), accepted)) return jsonResponse({ error: \"Unauthorized: missing or invalid service key\" }, 401);\n\t\tconst { pathname } = new URL(req.url);\n\t\tconst methodName = /^\\/rpc\\/([^/]+)$/.exec(pathname)?.[1];\n\t\tif (methodName === void 0) return jsonResponse({ error: `Not found: ${pathname}` }, 404);\n\t\tconst method = table.get(methodName);\n\t\tif (method === void 0) return jsonResponse({ error: `Unknown RPC method \"${methodName}\"` }, 404);\n\t\tif (req.method !== \"POST\") return jsonResponse({ error: `Method \"${methodName}\" requires POST` }, 405);\n\t\tlet body;\n\t\ttry {\n\t\t\tbody = await req.json();\n\t\t} catch {\n\t\t\treturn jsonResponse({ error: \"Request body must be JSON\" }, 400);\n\t\t}\n\t\tlet input;\n\t\ttry {\n\t\t\tinput = await standardValidate(method.input, body);\n\t\t} catch (err) {\n\t\t\treturn jsonResponse({ error: err instanceof Error ? err.message : String(err) }, 400);\n\t\t}\n\t\ttry {\n\t\t\tconst result = await method.handler(input, deps);\n\t\t\treturn jsonResponse(await standardValidate(method.output, result));\n\t\t} catch (err) {\n\t\t\treturn jsonResponse({ error: err instanceof Error ? err.message : String(err) }, 500);\n\t\t}\n\t};\n}\n//#endregion\nexport { RPC_ACCEPTED_KEYS_ENV, RPC_PEER_KEY, contract, makeClient, perBindingToken, rpc, serve };\n\n//# sourceMappingURL=index.mjs.map"],"mappings":"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"}
1
+ {"version":3,"file":"service-rpc.mjs","names":[],"sources":["../../../0-framework/2-authoring/service-rpc/dist/index.mjs"],"sourcesContent":["import { blindCast } from \"@internal/foundation/casts\";\nimport { dependency, provisionNeed, string } from \"@internal/core\";\n//#region src/client.ts\n/** Bounded jittered backoff for retrying a dropped call. `maxRetries` is retries after the first attempt. */\nconst RETRY = {\n\tinitialDelayMs: 250,\n\tmultiplier: 2,\n\tmaxDelayMs: 5e3,\n\tmaxRetries: 5\n};\nconst IDEMPOTENCY_KEY_HEADER$1 = \"Idempotency-Key\";\nfunction sleep(ms) {\n\treturn new Promise((resolve) => setTimeout(resolve, ms));\n}\n/** Whether a non-OK response is safe to retry: 429 or any 5xx, never another 4xx. */\nfunction isRetryableStatus(status) {\n\treturn status === 429 || status >= 500;\n}\n/** The server's `{ error }` body, if the response has one — undefined otherwise. */\nasync function errorDetail(res) {\n\ttry {\n\t\tconst body = await res.json();\n\t\treturn typeof body === \"object\" && body !== null && \"error\" in body ? String(body.error) : void 0;\n\t} catch {\n\t\treturn;\n\t}\n}\n/** `<base>/rpc/<method>`, preserving a base URL's own path (e.g. a mount point). */\nfunction methodUrl(base, method) {\n\tconst normalizedBase = base.endsWith(\"/\") ? base : `${base}/`;\n\treturn new URL(`rpc/${method}`, normalizedBase).toString();\n}\n/**\n* Sends one call over `send`, retrying a thrown error, 429, or 5xx with\n* full-jitter backoff. `buildRequest` runs per attempt but carries the same\n* idempotency key each time — only the transport call repeats, not the key.\n*/\nasync function callWithRetry(send, buildRequest, method) {\n\tlet delay = RETRY.initialDelayMs;\n\tlet retries = 0;\n\tfor (;;) {\n\t\tlet res;\n\t\ttry {\n\t\t\tres = await send(buildRequest());\n\t\t} catch (err) {\n\t\t\tif (retries >= RETRY.maxRetries) throw err;\n\t\t\tretries += 1;\n\t\t\tawait sleep(Math.random() * delay);\n\t\t\tdelay = Math.min(delay * RETRY.multiplier, RETRY.maxDelayMs);\n\t\t\tcontinue;\n\t\t}\n\t\tif (res.ok) return res.json();\n\t\tif (!isRetryableStatus(res.status) || retries >= RETRY.maxRetries) {\n\t\t\tconst detail = await errorDetail(res);\n\t\t\tthrow new Error(`RPC call \"${method}\" failed: ${res.status} ${res.statusText}` + (detail !== void 0 ? ` — ${detail}` : \"\"));\n\t\t}\n\t\tretries += 1;\n\t\tawait sleep(Math.random() * delay);\n\t\tdelay = Math.min(delay * RETRY.multiplier, RETRY.maxDelayMs);\n\t}\n}\nfunction makeClient(contract, url, opts) {\n\tconst send = opts?.fetch ?? fetch;\n\tconst baseHeaders = { \"content-type\": \"application/json\" };\n\tif (opts?.serviceKey !== void 0) baseHeaders[\"Authorization\"] = `Bearer ${opts.serviceKey}`;\n\tconst client = {};\n\tfor (const method of Object.keys(contract.__cmp)) client[method] = async (input) => {\n\t\tconst idempotencyKey = crypto.randomUUID();\n\t\tconst body = JSON.stringify(input);\n\t\treturn callWithRetry(send, () => new Request(methodUrl(url, method), {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\t...baseHeaders,\n\t\t\t\t[IDEMPOTENCY_KEY_HEADER$1]: idempotencyKey\n\t\t\t},\n\t\t\tbody\n\t\t}), method);\n\t};\n\treturn blindCast(client);\n}\n//#endregion\n//#region src/contract.ts\nfunction contract(fns) {\n\tconst value = {\n\t\tkind: \"rpc\",\n\t\t__cmp: fns,\n\t\tsatisfies: (required) => value === required\n\t};\n\treturn Object.freeze(value);\n}\n//#endregion\n//#region src/rpc.ts\n/** ADR-0031's need brand for RPC's per-binding service key — the target registers a provisioner under this. */\nconst RPC_PEER_KEY = Symbol.for(\"prisma:rpc/per-binding-key\");\n/** The provisioning need `rpc()`'s `serviceKey` param declares (ADR-0030): a shared, unguessable value the target mints per consumer edge. */\nconst perBindingToken = () => provisionNeed(RPC_PEER_KEY);\nfunction rpc(arg) {\n\tif (!isRpcContract(arg)) return arg;\n\treturn dependency({\n\t\ttype: \"rpc\",\n\t\tconnection: {\n\t\t\tparams: {\n\t\t\t\turl: string(),\n\t\t\t\tserviceKey: string({\n\t\t\t\t\toptional: true,\n\t\t\t\t\tprovision: perBindingToken()\n\t\t\t\t})\n\t\t\t},\n\t\t\thydrate: ({ url, serviceKey }) => makeClient(arg, url, { serviceKey })\n\t\t},\n\t\trequired: arg\n\t});\n}\nfunction isRpcContract(value) {\n\treturn typeof value === \"object\" && value !== null && \"kind\" in value && value.kind === \"rpc\" && \"__cmp\" in value && \"satisfies\" in value;\n}\n//#endregion\n//#region src/standard-schema.ts\nasync function standardValidate(schema, value) {\n\tconst result = await schema[\"~standard\"].validate(value);\n\tif (result.issues !== void 0) throw new Error(`Schema validation failed: ${result.issues.map((issue) => issue.message).join(\"; \")}`);\n\treturn result.value;\n}\n//#endregion\n//#region src/serve.ts\n/** The reserved env var the target (slice 2) writes the accepted key set to. */\nconst RPC_ACCEPTED_KEYS_ENV = \"COMPOSER_RPC_ACCEPTED_KEYS\";\nfunction outcome(body, status = 200) {\n\treturn {\n\t\tstatus,\n\t\tbodyText: JSON.stringify(body)\n\t};\n}\nfunction toResponse(o) {\n\treturn new Response(o.bodyText, {\n\t\tstatus: o.status,\n\t\theaders: { \"content-type\": \"application/json\" }\n\t});\n}\n/** The generic message every caller-facing 500 carries — the real error goes to `console.error` instead. */\nconst INTERNAL_ERROR_MESSAGE = \"Internal server error\";\n/** Request body cap. Internal RPC payloads are small records, not uploads; 1 MiB bounds a slow request's memory. */\nconst MAX_BODY_BYTES = 1048576;\nvar RequestBodyTooLargeError = class extends Error {};\n/** Reads the body as text, aborting past `maxBytes` of bytes actually read — `content-length` is caller-supplied, so untrusted. */\nasync function readBoundedBody(req, maxBytes) {\n\tconst reader = req.body?.getReader();\n\tif (reader === void 0) return \"\";\n\tconst decoder = new TextDecoder();\n\tlet text = \"\";\n\tlet total = 0;\n\tfor (;;) {\n\t\tconst { done, value } = await reader.read();\n\t\tif (done) break;\n\t\ttotal += value.byteLength;\n\t\tif (total > maxBytes) {\n\t\t\tawait reader.cancel();\n\t\t\tthrow new RequestBodyTooLargeError();\n\t\t}\n\t\ttext += decoder.decode(value, { stream: true });\n\t}\n\ttext += decoder.decode();\n\treturn text;\n}\n/** How long a completed 2xx/4xx answer stays replayable for a repeated key. */\nconst REPLAY_TTL_MS = 6e4;\n/**\n* Per-method, per-key deduplication. A duplicate arriving mid-execution\n* single-flights onto the same promise; a completed 2xx/4xx replays for\n* REPLAY_TTL_MS; a 5xx is never kept, since that is what a retry re-executes.\n* Keyed by method first, so a replay can never answer a different method.\n*/\nvar IdempotencyStore = class {\n\tbyMethod = /* @__PURE__ */ new Map();\n\tlruOrder = /* @__PURE__ */ new Map();\n\tasync dispatch(method, key, run) {\n\t\tconst bucket = this.bucketFor(method);\n\t\tconst existing = bucket.get(key);\n\t\tif (existing?.kind === \"pending\") return existing.promise;\n\t\tif (existing?.kind === \"completed\") {\n\t\t\tif (Date.now() - existing.completedAt < REPLAY_TTL_MS) {\n\t\t\t\tthis.touch(method, key);\n\t\t\t\treturn existing.outcome;\n\t\t\t}\n\t\t\tbucket.delete(key);\n\t\t\tthis.lruOrder.delete(this.lruKey(method, key));\n\t\t}\n\t\tconst promise = run();\n\t\tbucket.set(key, {\n\t\t\tkind: \"pending\",\n\t\t\tpromise\n\t\t});\n\t\tlet result;\n\t\ttry {\n\t\t\tresult = await promise;\n\t\t} catch (err) {\n\t\t\tbucket.delete(key);\n\t\t\tthrow err;\n\t\t}\n\t\tif (result.status >= 500) bucket.delete(key);\n\t\telse {\n\t\t\tbucket.set(key, {\n\t\t\t\tkind: \"completed\",\n\t\t\t\toutcome: result,\n\t\t\t\tcompletedAt: Date.now()\n\t\t\t});\n\t\t\tthis.touch(method, key);\n\t\t}\n\t\treturn result;\n\t}\n\tbucketFor(method) {\n\t\tlet bucket = this.byMethod.get(method);\n\t\tif (bucket === void 0) {\n\t\t\tbucket = /* @__PURE__ */ new Map();\n\t\t\tthis.byMethod.set(method, bucket);\n\t\t}\n\t\treturn bucket;\n\t}\n\tlruKey(method, key) {\n\t\treturn `${method}\\0${key}`;\n\t}\n\t/** Marks (method, key) most-recently-used, evicting the oldest completed entry once over the bound. */\n\ttouch(method, key) {\n\t\tconst lruKey = this.lruKey(method, key);\n\t\tthis.lruOrder.delete(lruKey);\n\t\tthis.lruOrder.set(lruKey, {\n\t\t\tmethod,\n\t\t\tkey\n\t\t});\n\t\tif (this.lruOrder.size > 1e3) {\n\t\t\tconst oldestKey = this.lruOrder.keys().next().value;\n\t\t\tconst oldest = oldestKey === void 0 ? void 0 : this.lruOrder.get(oldestKey);\n\t\t\tif (oldestKey !== void 0 && oldest !== void 0) {\n\t\t\t\tthis.lruOrder.delete(oldestKey);\n\t\t\t\tthis.byMethod.get(oldest.method)?.delete(oldest.key);\n\t\t\t}\n\t\t}\n\t}\n};\n/** The provisioned accepted key set, or undefined when the deploy never provisioned one (local/test — enforcement off). */\nfunction acceptedKeys() {\n\tconst raw = process.env[RPC_ACCEPTED_KEYS_ENV];\n\tif (raw === void 0 || raw === \"\") return void 0;\n\tlet parsed;\n\ttry {\n\t\tparsed = JSON.parse(raw);\n\t} catch {\n\t\treturn [];\n\t}\n\treturn Array.isArray(parsed) && parsed.every((key) => typeof key === \"string\") ? parsed : [];\n}\n/**\n* Length-independent constant-time string equality — no early exit on the\n* first mismatched character or on a length difference, so a caller cannot\n* time its way toward a valid key. No `node:crypto`, to keep this module\n* runtime-agnostic.\n*/\nfunction constantTimeEquals(a, b) {\n\tconst length = Math.max(a.length, b.length);\n\tlet diff = a.length ^ b.length;\n\tfor (let i = 0; i < length; i++) diff |= (i < a.length ? a.charCodeAt(i) : 0) ^ (i < b.length ? b.charCodeAt(i) : 0);\n\treturn diff === 0;\n}\n/** Whether `presented` is a member of `accepted` — always compares against every key. */\nfunction isAcceptedKey(presented, accepted) {\n\tlet matched = false;\n\tfor (const key of accepted) matched = constantTimeEquals(presented, key) || matched;\n\treturn matched;\n}\nconst BEARER_PREFIX = \"Bearer \";\n/** The bearer token on `Authorization`, or `''` if the header is missing or malformed. */\nfunction bearerToken(req) {\n\tconst header = req.headers.get(\"authorization\");\n\treturn header?.startsWith(BEARER_PREFIX) ? header.slice(7) : \"\";\n}\nconst IDEMPOTENCY_KEY_HEADER = \"Idempotency-Key\";\n/**\n* Flattens every exposed port's methods into one method → {schemas, handler}\n* table. RPC dispatch is flat (`/rpc/<method>`), so a method name exposed by\n* more than one port is a construction-time error, as is a missing handler.\n*/\nfunction methodTable(expose, handlers) {\n\tconst table = /* @__PURE__ */ new Map();\n\tfor (const [port, contract] of Object.entries(expose)) {\n\t\tconst portHandlers = handlers[port] ?? {};\n\t\tfor (const [method, fn] of Object.entries(contract.__cmp)) {\n\t\t\tif (table.has(method)) throw new Error(`serve(): method \"${method}\" is exposed by more than one port — RPC dispatch is flat (POST /rpc/<method>), so method names must be unique across a service's exposed ports.`);\n\t\t\tconst handler = portHandlers[method];\n\t\t\tif (handler === void 0) throw new Error(`serve(): no handler supplied for exposed method \"${port}.${method}\".`);\n\t\t\tconst { input, output } = blindCast(fn);\n\t\t\ttable.set(method, {\n\t\t\t\tinput,\n\t\t\t\toutput,\n\t\t\t\thandler\n\t\t\t});\n\t\t}\n\t}\n\treturn table;\n}\n/**\n* Routes `POST /rpc/<method>`: checks the service key, requires an\n* Idempotency-Key, single-flights/replays through `IdempotencyStore`, and —\n* per call — parses JSON within the body cap, validates input, calls the\n* handler with `service.load()`'s deps plus `{ idempotencyKey }`, validates\n* the output, and responds JSON. A handler or output-validation failure\n* masks its message behind a generic 500 and logs the real error; an\n* unknown method or invalid input is a 4xx. `load()` is called exactly\n* once, here, before the handler ever runs.\n*/\nfunction serve(service, handlers) {\n\tconst table = methodTable(service.expose ?? {}, blindCast(handlers));\n\tconst deps = service.load();\n\tconst idempotency = new IdempotencyStore();\n\treturn async (req) => {\n\t\tconst accepted = acceptedKeys();\n\t\tif (accepted !== void 0 && !isAcceptedKey(bearerToken(req), accepted)) return toResponse(outcome({ error: \"Unauthorized: missing or invalid service key\" }, 401));\n\t\tconst { pathname } = new URL(req.url);\n\t\tconst methodName = /^\\/rpc\\/([^/]+)$/.exec(pathname)?.[1];\n\t\tif (methodName === void 0) return toResponse(outcome({ error: `Not found: ${pathname}` }, 404));\n\t\tconst method = table.get(methodName);\n\t\tif (method === void 0) return toResponse(outcome({ error: `Unknown RPC method \"${methodName}\"` }, 404));\n\t\tif (req.method !== \"POST\") return toResponse(outcome({ error: `Method \"${methodName}\" requires POST` }, 405));\n\t\tconst idempotencyKey = req.headers.get(IDEMPOTENCY_KEY_HEADER.toLowerCase()) || void 0;\n\t\tconst ctx = { idempotencyKey };\n\t\tconst run = async () => {\n\t\t\tlet bodyText;\n\t\t\ttry {\n\t\t\t\tbodyText = await readBoundedBody(req, MAX_BODY_BYTES);\n\t\t\t} catch (err) {\n\t\t\t\tif (err instanceof RequestBodyTooLargeError) return outcome({ error: `Request body exceeds the ${MAX_BODY_BYTES}-byte limit` }, 413);\n\t\t\t\tconsole.error(`serve(): reading the request body for \"${methodName}\" failed:`, err);\n\t\t\t\treturn outcome({ error: INTERNAL_ERROR_MESSAGE }, 500);\n\t\t\t}\n\t\t\tlet body;\n\t\t\ttry {\n\t\t\t\tbody = JSON.parse(bodyText);\n\t\t\t} catch {\n\t\t\t\treturn outcome({ error: \"Request body must be JSON\" }, 400);\n\t\t\t}\n\t\t\tlet input;\n\t\t\ttry {\n\t\t\t\tinput = await standardValidate(method.input, body);\n\t\t\t} catch (err) {\n\t\t\t\treturn outcome({ error: err instanceof Error ? err.message : String(err) }, 400);\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst result = await method.handler(input, deps, ctx);\n\t\t\t\tlet output;\n\t\t\t\ttry {\n\t\t\t\t\toutput = await standardValidate(method.output, result);\n\t\t\t\t} catch (err) {\n\t\t\t\t\tconsole.error(`serve(): handler for \"${methodName}\" returned output that failed schema validation — this is a provider bug:`, err);\n\t\t\t\t\treturn outcome({ error: INTERNAL_ERROR_MESSAGE }, 500);\n\t\t\t\t}\n\t\t\t\treturn outcome(output);\n\t\t\t} catch (err) {\n\t\t\t\tconsole.error(`serve(): handler for \"${methodName}\" threw:`, err);\n\t\t\t\treturn outcome({ error: INTERNAL_ERROR_MESSAGE }, 500);\n\t\t\t}\n\t\t};\n\t\treturn toResponse(idempotencyKey === void 0 ? await run() : await idempotency.dispatch(methodName, idempotencyKey, run));\n\t};\n}\n//#endregion\nexport { RPC_ACCEPTED_KEYS_ENV, RPC_PEER_KEY, contract, makeClient, perBindingToken, rpc, serve };\n\n//# sourceMappingURL=index.mjs.map"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prisma/composer",
3
- "version": "0.1.0-dev.14",
3
+ "version": "0.1.0-dev.15",
4
4
  "type": "module",
5
5
  "description": "Prisma Composer — build a Prisma App by composing Modules. Core authoring, deploy pipeline, the prisma-composer CLI, and the service-rpc/node/nextjs authoring surfaces.",
6
6
  "bin": {
@@ -36,15 +36,15 @@
36
36
  "@prisma/management-api-sdk": "^1.50.0"
37
37
  },
38
38
  "devDependencies": {
39
- "@internal/assemble": "0.1.0-dev.14",
40
- "@internal/cli": "0.1.0-dev.14",
41
- "@internal/core": "0.1.0-dev.14",
42
- "@internal/foundation": "0.1.0-dev.14",
43
- "@internal/lowering": "0.1.0-dev.14",
44
- "@internal/nextjs": "0.1.0-dev.14",
45
- "@internal/node": "0.1.0-dev.14",
46
- "@internal/service-rpc": "0.1.0-dev.14",
47
- "@internal/tsdown-config": "0.1.0-dev.14",
39
+ "@internal/assemble": "0.1.0-dev.15",
40
+ "@internal/cli": "0.1.0-dev.15",
41
+ "@internal/core": "0.1.0-dev.15",
42
+ "@internal/foundation": "0.1.0-dev.15",
43
+ "@internal/lowering": "0.1.0-dev.15",
44
+ "@internal/nextjs": "0.1.0-dev.15",
45
+ "@internal/node": "0.1.0-dev.15",
46
+ "@internal/service-rpc": "0.1.0-dev.15",
47
+ "@internal/tsdown-config": "0.1.0-dev.15",
48
48
  "@types/node": "^25.9.3",
49
49
  "tsdown": "^0.22.7",
50
50
  "typescript": "^6.0.3"