@prisma/composer-prisma-cloud 0.2.0-dev.1 → 0.2.0-dev.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist/control.d.mts.map +1 -0
  2. package/dist/control.mjs +34 -20
  3. package/dist/control.mjs.map +1 -1
  4. package/dist/cron/index.d.mts.map +1 -0
  5. package/dist/cron/scheduler-entrypoint.mjs +15 -11
  6. package/dist/cron/scheduler-entrypoint.mjs.map +1 -1
  7. package/dist/email/email-entrypoint.mjs +19738 -0
  8. package/dist/email/email-entrypoint.mjs.map +1 -0
  9. package/dist/email/email-service.mjs +566 -0
  10. package/dist/email/email-service.mjs.map +1 -0
  11. package/dist/email/index.d.mts +282 -0
  12. package/dist/email/index.d.mts.map +1 -0
  13. package/dist/email/index.mjs +653 -0
  14. package/dist/email/index.mjs.map +1 -0
  15. package/dist/email/testing.d.mts +22 -0
  16. package/dist/email/testing.d.mts.map +1 -0
  17. package/dist/email/testing.mjs +1321 -0
  18. package/dist/email/testing.mjs.map +1 -0
  19. package/dist/index.d.mts.map +1 -0
  20. package/dist/{prisma-next-DlU01kXJ-2sP5mQb6.mjs → prisma-next-BvBwGCIt-CSauMX4l.mjs} +19 -3
  21. package/dist/prisma-next-BvBwGCIt-CSauMX4l.mjs.map +1 -0
  22. package/dist/prisma-next.d.mts +14 -5
  23. package/dist/prisma-next.d.mts.map +1 -0
  24. package/dist/prisma-next.mjs +1 -1
  25. package/dist/serializer-Cx5slrV4-xJfH6EWS.d.mts.map +1 -0
  26. package/dist/storage/index.d.mts.map +1 -0
  27. package/dist/storage/storage-entrypoint.mjs +19 -32
  28. package/dist/storage/storage-entrypoint.mjs.map +1 -1
  29. package/dist/storage/testing.d.mts +7 -6
  30. package/dist/storage/testing.d.mts.map +1 -0
  31. package/dist/storage/testing.mjs +4 -21
  32. package/dist/storage/testing.mjs.map +1 -1
  33. package/dist/streams/index.d.mts.map +1 -0
  34. package/dist/streams/streams-entrypoint.mjs +15 -11
  35. package/dist/streams/streams-entrypoint.mjs.map +1 -1
  36. package/dist/streams/testing.d.mts.map +1 -0
  37. package/dist/testing.d.mts.map +1 -0
  38. package/package.json +28 -20
  39. package/src/exports/control.ts +1 -0
  40. package/src/exports/cron.ts +1 -0
  41. package/src/exports/email-testing.ts +1 -0
  42. package/src/exports/email.ts +1 -0
  43. package/src/exports/index.ts +1 -0
  44. package/src/exports/prisma-next.ts +1 -0
  45. package/src/exports/storage-testing.ts +1 -0
  46. package/src/exports/storage.ts +1 -0
  47. package/src/exports/streams-testing.ts +1 -0
  48. package/src/exports/streams.ts +1 -0
  49. package/src/exports/testing.ts +1 -0
  50. package/dist/prisma-next-DlU01kXJ-2sP5mQb6.mjs.map +0 -1
@@ -1,4 +1,5 @@
1
1
  //#region ../../1-prisma-cloud/2-shared-modules/storage/dist/testing.d.mts
2
+ //#region ../../0-lowering/s3-protocol/dist/index.d.mts
2
3
  //#region src/store.d.ts
3
4
  /**
4
5
  * The minimal object store the protocol handler drives — the seam between the
@@ -52,6 +53,12 @@ interface ObjectStore {
52
53
  list(bucket: string, opts?: ListOptions): Promise<ListResult>;
53
54
  }
54
55
  //#endregion
56
+ //#region src/sigv4.d.ts
57
+ interface Credentials {
58
+ readonly accessKeyId: string;
59
+ readonly secretAccessKey: string;
60
+ }
61
+ //#endregion
55
62
  //#region src/pg-store.d.ts
56
63
  /**
57
64
  * Connect (FT-5219 posture: `max: 1`, short `idleTimeout`), apply the schema
@@ -59,12 +66,6 @@ interface ObjectStore {
59
66
  */
60
67
  declare function createPgStore(url: string): Promise<ObjectStore>;
61
68
  //#endregion
62
- //#region src/sigv4.d.ts
63
- interface Credentials {
64
- readonly accessKeyId: string;
65
- readonly secretAccessKey: string;
66
- }
67
- //#endregion
68
69
  //#region src/storage-server.d.ts
69
70
  interface StorageServer {
70
71
  /** The externally reachable base URL of the running server. */
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.d.mts","names":[],"sources":["../../../../1-prisma-cloud/2-shared-modules/storage/dist/testing.d.mts"],"mappings":";;;;;;;;;UAQU;WACC;;UAED;WACC;;WAEA;;UAED;;WAEC,OAAO;WACP;WACA;;WAEA;;UAED;WACC;WACA;WACA;;UAED;WACC;WACA;WACA;;UAED;WACC;WACA;WACA;;UAED;EACR,IAAI,gBAAgB,aAAa,OAAO,YAAY;IAClD;MACE,QAAQ;;EAEZ,IAAI,gBAAgB,aAAa;IAC/B,QAAQ;MACN,QAAQ;;EAEZ,KAAK,gBAAgB,cAAc,QAAQ;;EAE3C,OAAO,gBAAgB,cAAc;EACrC,KAAK,gBAAgB,OAAO,cAAc,QAAQ;;;;UAI1C;WACC;WACA;;;;;;;;iBAQM,cAAc,cAAc,QAAQ;;;UAG3C;;WAEC;EACT;;UAEQ;WACC,OAAO;WACP,aAAa;;WAEb;WACA;WACA;;iBAEM,mBAAmB,MAAM,uBAAuB"}
@@ -66,7 +66,7 @@ function retryTransientConnect(acquire, opts = {}) {
66
66
  * Runtime engine code (`bun` SQL + `node:crypto`); NOT re-exported from the
67
67
  * authoring barrel.
68
68
  */
69
- const DEFAULT_MAX_KEYS$1 = 1e3;
69
+ const DEFAULT_MAX_KEYS = 1e3;
70
70
  function etagOf(bytes) {
71
71
  return `"${createHash("sha256").update(bytes).digest("hex")}"`;
72
72
  }
@@ -129,7 +129,7 @@ var PgObjectStore = class {
129
129
  }
130
130
  async list(bucket, opts = {}) {
131
131
  const prefix = opts.prefix ?? "";
132
- const maxKeys = opts.maxKeys ?? DEFAULT_MAX_KEYS$1;
132
+ const maxKeys = opts.maxKeys ?? DEFAULT_MAX_KEYS;
133
133
  const token = opts.continuationToken;
134
134
  const limit = maxKeys + 1;
135
135
  const keys = (token === void 0 ? await this.sql`select key from objects
@@ -170,13 +170,6 @@ async function createPgStore(url) {
170
170
  )`);
171
171
  return new PgObjectStore(sql);
172
172
  }
173
- /**
174
- * AWS SigV4 verification for the S3 wire protocol (spec § 2 auth). The payload
175
- * hash comes from the client — `x-amz-content-sha256` (a real hash or
176
- * `UNSIGNED-PAYLOAD`) for header auth, `UNSIGNED-PAYLOAD` for presign — and is
177
- * never re-hashed; the verifier trusts what was signed, like a real S3 endpoint.
178
- * Runtime engine code (`node:crypto`); not re-exported from the authoring barrel.
179
- */
180
173
  const ALGORITHM = "AWS4-HMAC-SHA256";
181
174
  const UNSIGNED_PAYLOAD = "UNSIGNED-PAYLOAD";
182
175
  function sha256Hex(data) {
@@ -391,12 +384,12 @@ function listXml(bucket, prefix, maxKeys, result) {
391
384
  const next = result.isTruncated && result.nextContinuationToken !== void 0 ? `<NextContinuationToken>${xmlEscape(result.nextContinuationToken)}</NextContinuationToken>` : "";
392
385
  return `<?xml version="1.0" encoding="UTF-8"?><ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Name>${xmlEscape(bucket)}</Name><Prefix>${xmlEscape(prefix)}</Prefix><KeyCount>${result.keys.length}</KeyCount><MaxKeys>${maxKeys}</MaxKeys><IsTruncated>${result.isTruncated}</IsTruncated>` + contents + next + "</ListBucketResult>";
393
386
  }
394
- const DEFAULT_MAX_KEYS = 1e3;
387
+ const DEFAULT_MAX_KEYS$1 = 1e3;
395
388
  async function handleList(store, bucket, url) {
396
389
  const prefix = url.searchParams.get("prefix") ?? "";
397
390
  const continuationToken = url.searchParams.get("continuation-token");
398
391
  const maxKeysRaw = url.searchParams.get("max-keys");
399
- const maxKeys = maxKeysRaw !== null && Number.isFinite(Number(maxKeysRaw)) ? Number(maxKeysRaw) : DEFAULT_MAX_KEYS;
392
+ const maxKeys = maxKeysRaw !== null && Number.isFinite(Number(maxKeysRaw)) ? Number(maxKeysRaw) : DEFAULT_MAX_KEYS$1;
400
393
  const result = await store.list(bucket, {
401
394
  prefix,
402
395
  maxKeys,
@@ -490,16 +483,6 @@ function createS3Handler(opts) {
490
483
  }
491
484
  };
492
485
  }
493
- /**
494
- * Boots the S3 wire protocol on `Bun.serve` — the D2 handler over any
495
- * `ObjectStore`. Binds all interfaces (Compute routes external HTTP to the VM,
496
- * so a loopback-only listener would be unreachable). Installs the FT-5219
497
- * process guards so an idle Bun.SQL connection close surfaces as a logged
498
- * error instead of crash-looping the process on scale-to-zero.
499
- *
500
- * Runtime engine code; NOT re-exported from the authoring barrel. The D4
501
- * entrypoint reads deps via `load()` and calls this.
502
- */
503
486
  let guardsInstalled = false;
504
487
  /** FT-5219: keep the process alive when Bun.SQL surfaces an idle-close as an unawaited async error. Installed once. */
505
488
  function installProcessGuards() {
@@ -1 +1 @@
1
- {"version":3,"file":"testing.mjs","names":[],"sources":["../../../../1-prisma-cloud/2-shared-modules/storage/dist/testing.mjs"],"sourcesContent":["import { createHash, createHmac, timingSafeEqual } from \"node:crypto\";\nimport { SQL } from \"bun\";\n//#region ../../1-extensions/target/dist/pg-connection-CadPZuEK.mjs\n/** Connection resilience helpers for Prisma Postgres cold-starts (FT-5226); no heavy imports (no `effect`/`alchemy`/`pg`), so the deploy lowerings, the pnPostgres runtime client, and bun-runnable services (the storage store, via the pure `@internal/prisma-cloud/connection` subpath) all share one implementation. */\n/** Network-level socket failures node-postgres surfaces as `err.code`. */\nconst TRANSIENT_CODES = /* @__PURE__ */ new Set([\n\t\"ECONNREFUSED\",\n\t\"ECONNRESET\",\n\t\"ETIMEDOUT\",\n\t\"EPIPE\",\n\t\"ENOTFOUND\",\n\t\"EAI_AGAIN\"\n]);\n/** Connection-establishment failure messages (no useful `err.code`). */\nconst TRANSIENT_MESSAGE_FRAGMENTS = [\n\t\"upstream database\",\n\t\"connection terminated\",\n\t\"connection refused\",\n\t\"terminating connection\",\n\t\"server closed the connection\",\n\t\"connection timeout\",\n\t\"timeout expired\"\n];\n/** Whether an error is a transient connection failure worth retrying, as opposed to a real query error that must surface at once. */\nfunction isTransientConnectionError(error) {\n\tif (typeof error !== \"object\" || error === null) return false;\n\tconst code = \"code\" in error && typeof error.code === \"string\" ? error.code : void 0;\n\tif (code !== void 0 && TRANSIENT_CODES.has(code)) return true;\n\tconst message = \"message\" in error && typeof error.message === \"string\" ? error.message.toLowerCase() : \"\";\n\treturn TRANSIENT_MESSAGE_FRAGMENTS.some((fragment) => message.includes(fragment));\n}\n/**\n* Retries an operation past a transient connection failure, bounded (default\n* ~1 min). `shouldRetry` decides what's transient — defaults to retrying\n* everything; the runtime client passes {@link isTransientConnectionError}.\n*/\nasync function withConnectionRetry(operation, opts = {}) {\n\tconst attempts = opts.attempts ?? 12;\n\tconst delayMs = opts.delayMs ?? 5e3;\n\tconst sleep = opts.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));\n\tconst shouldRetry = opts.shouldRetry ?? (() => true);\n\tlet lastError;\n\tfor (let attempt = 1; attempt <= attempts; attempt++) try {\n\t\treturn await operation();\n\t} catch (error) {\n\t\tif (!shouldRetry(error)) throw error;\n\t\tlastError = error;\n\t\tif (attempt < attempts) await sleep(delayMs);\n\t}\n\tthrow lastError;\n}\n/** Retries acquiring a connection past a transient cold-start; {@link withConnectionRetry} with {@link isTransientConnectionError} fixed as the predicate. */\nfunction retryTransientConnect(acquire, opts = {}) {\n\treturn withConnectionRetry(acquire, {\n\t\t...opts,\n\t\tshouldRetry: isTransientConnectionError\n\t});\n}\n//#endregion\n//#region src/pg-store.ts\n/**\n* The `ObjectStore` over Postgres `bytea` (spec § 3): one `objects` table,\n* single-row-per-object. Ranged reads use SQL `substring` so a range request\n* never detoasts the whole object. The schema is applied idempotently at init\n* behind a bounded connection retry — the first connect to a freshly\n* provisioned Postgres is rejected while the upstream is cold (FT-5226).\n*\n* Runtime engine code (`bun` SQL + `node:crypto`); NOT re-exported from the\n* authoring barrel.\n*/\nconst DEFAULT_MAX_KEYS$1 = 1e3;\nfunction etagOf(bytes) {\n\treturn `\"${createHash(\"sha256\").update(bytes).digest(\"hex\")}\"`;\n}\n/** bytea comes back as a Node Buffer (a Uint8Array). Fail closed on anything else rather than returning wrong bytes. */\nfunction toBytes(value) {\n\tif (value instanceof Uint8Array) return value;\n\tthrow new TypeError(`expected bytea to decode as Uint8Array, got ${typeof value}`);\n}\n/** One row → GetResult (both the whole-object and ranged queries alias the payload as `bytes`; `size` is the bigint total). */\nfunction toGetResult(row) {\n\treturn {\n\t\tbytes: toBytes(row.bytes),\n\t\tetag: row.etag,\n\t\tcontentType: row.content_type,\n\t\tsize: Number(row.size)\n\t};\n}\nvar PgObjectStore = class {\n\tsql;\n\tconstructor(sql) {\n\t\tthis.sql = sql;\n\t}\n\tasync put(bucket, key, bytes, opts = {}) {\n\t\tconst etag = etagOf(bytes);\n\t\tconst contentType = opts.contentType ?? \"application/octet-stream\";\n\t\tawait this.sql`\n insert into objects (bucket, key, bytes, size, etag, content_type)\n values (${bucket}, ${key}, ${bytes}, ${bytes.byteLength}, ${etag}, ${contentType})\n on conflict (bucket, key) do update set\n bytes = excluded.bytes, size = excluded.size,\n etag = excluded.etag, content_type = excluded.content_type`;\n\t\treturn { etag };\n\t}\n\tasync get(bucket, key, opts = {}) {\n\t\tconst range = opts.range;\n\t\tif (range) {\n\t\t\tconst from = range.start + 1;\n\t\t\tconst row = (range.end === void 0 ? await this.sql`select substring(bytes from ${from}) as bytes, size, etag, content_type\n from objects where bucket = ${bucket} and key = ${key}` : await this.sql`select substring(bytes from ${from} for ${range.end - range.start + 1}) as bytes,\n size, etag, content_type\n from objects where bucket = ${bucket} and key = ${key}`)[0];\n\t\t\treturn row === void 0 ? null : toGetResult(row);\n\t\t}\n\t\tconst row = (await this.sql`select bytes, size, etag, content_type\n from objects where bucket = ${bucket} and key = ${key}`)[0];\n\t\treturn row === void 0 ? null : toGetResult(row);\n\t}\n\tasync head(bucket, key) {\n\t\tconst row = (await this.sql`select size, etag, content_type\n from objects where bucket = ${bucket} and key = ${key}`)[0];\n\t\tif (row === void 0) return null;\n\t\treturn {\n\t\t\tetag: row.etag,\n\t\t\tsize: Number(row.size),\n\t\t\tcontentType: row.content_type\n\t\t};\n\t}\n\tasync delete(bucket, key) {\n\t\tawait this.sql`delete from objects where bucket = ${bucket} and key = ${key}`;\n\t}\n\tasync list(bucket, opts = {}) {\n\t\tconst prefix = opts.prefix ?? \"\";\n\t\tconst maxKeys = opts.maxKeys ?? DEFAULT_MAX_KEYS$1;\n\t\tconst token = opts.continuationToken;\n\t\tconst limit = maxKeys + 1;\n\t\tconst keys = (token === void 0 ? await this.sql`select key from objects\n where bucket = ${bucket} and starts_with(key, ${prefix})\n order by key limit ${limit}` : await this.sql`select key from objects\n where bucket = ${bucket} and starts_with(key, ${prefix}) and key > ${token}\n order by key limit ${limit}`).map((r) => r.key);\n\t\tconst isTruncated = keys.length > maxKeys;\n\t\tconst page = isTruncated ? keys.slice(0, maxKeys) : keys;\n\t\tconst last = page.at(-1);\n\t\treturn {\n\t\t\tkeys: page,\n\t\t\tisTruncated,\n\t\t\t...isTruncated && last !== void 0 ? { nextContinuationToken: last } : {}\n\t\t};\n\t}\n};\n/**\n* Connect (FT-5219 posture: `max: 1`, short `idleTimeout`), apply the schema\n* idempotently behind the cold-start retry, and return the store.\n*/\nasync function createPgStore(url) {\n\tconst sql = new SQL({\n\t\turl,\n\t\tmax: 1,\n\t\tidleTimeout: 10\n\t});\n\tawait retryTransientConnect(() => sql`\n create table if not exists objects (\n bucket text not null,\n key text not null,\n bytes bytea not null,\n size bigint not null,\n etag text not null,\n content_type text not null,\n created_at timestamptz not null default now(),\n primary key (bucket, key)\n )`);\n\treturn new PgObjectStore(sql);\n}\n//#endregion\n//#region src/sigv4.ts\n/**\n* AWS SigV4 verification for the S3 wire protocol (spec § 2 auth). The payload\n* hash comes from the client — `x-amz-content-sha256` (a real hash or\n* `UNSIGNED-PAYLOAD`) for header auth, `UNSIGNED-PAYLOAD` for presign — and is\n* never re-hashed; the verifier trusts what was signed, like a real S3 endpoint.\n* Runtime engine code (`node:crypto`); not re-exported from the authoring barrel.\n*/\nconst ALGORITHM = \"AWS4-HMAC-SHA256\";\nconst UNSIGNED_PAYLOAD = \"UNSIGNED-PAYLOAD\";\nfunction sha256Hex(data) {\n\treturn createHash(\"sha256\").update(data).digest(\"hex\");\n}\nfunction hmac(key, data) {\n\treturn createHmac(\"sha256\", key).update(data).digest();\n}\n/** AWS canonical URI encoding: every byte except the unreserved set is %XX. */\nfunction awsUriEncode(value) {\n\treturn encodeURIComponent(value).replace(/[!'()*]/g, (ch) => `%${ch.charCodeAt(0).toString(16).toUpperCase()}`).replace(/%7E/g, \"~\");\n}\nfunction parseCredential(credential) {\n\tconst parts = credential.split(\"/\");\n\tif (parts.length !== 5 || parts[4] !== \"aws4_request\") return null;\n\tconst [accessKeyId, date, region, service] = parts;\n\tif (!accessKeyId || !date || !region || !service) return null;\n\treturn {\n\t\taccessKeyId,\n\t\tdate,\n\t\tregion,\n\t\tservice\n\t};\n}\nfunction signingKey(secret, scope) {\n\treturn hmac(hmac(hmac(hmac(`AWS4${secret}`, scope.date), scope.region), scope.service), \"aws4_request\");\n}\nfunction canonicalHeaders(url, req, signedHeaders) {\n\treturn signedHeaders.map((name) => {\n\t\treturn `${name}:${(name === \"host\" ? url.host : req.headers.get(name) ?? \"\").trim().replace(/\\s+/g, \" \")}\\n`;\n\t}).join(\"\");\n}\nfunction canonicalQuery(url, exclude) {\n\tconst entries = [];\n\tfor (const [key, value] of url.searchParams.entries()) {\n\t\tif (exclude !== void 0 && key === exclude) continue;\n\t\tentries.push([awsUriEncode(key), awsUriEncode(value)]);\n\t}\n\tconst cmp = (a, b) => a < b ? -1 : a > b ? 1 : 0;\n\tentries.sort(([ak, av], [bk, bv]) => cmp(ak, bk) || cmp(av, bv));\n\treturn entries.map(([k, v]) => `${k}=${v}`).join(\"&\");\n}\nfunction stringToSign(amzDate, scope, canonicalRequest) {\n\tconst scopeString = `${scope.date}/${scope.region}/${scope.service}/aws4_request`;\n\treturn [\n\t\tALGORITHM,\n\t\tamzDate,\n\t\tscopeString,\n\t\tsha256Hex(canonicalRequest)\n\t].join(\"\\n\");\n}\nfunction signatureMatches(expected, provided) {\n\tconst a = Buffer.from(expected, \"hex\");\n\tconst b = Buffer.from(provided, \"hex\");\n\treturn a.length === b.length && a.length > 0 && timingSafeEqual(a, b);\n}\n/** `YYYYMMDDTHHMMSSZ` → epoch ms, or null when malformed. */\nfunction parseAmzDate(amzDate) {\n\tconst match = /^(\\d{4})(\\d{2})(\\d{2})T(\\d{2})(\\d{2})(\\d{2})Z$/.exec(amzDate);\n\tif (!match) return null;\n\tconst [, y, mo, d, h, mi, s] = match;\n\treturn Date.UTC(Number(y), Number(mo) - 1, Number(d), Number(h), Number(mi), Number(s));\n}\nfunction parseAuthorizationHeader(header) {\n\tif (!header.startsWith(`${ALGORITHM} `)) return null;\n\tconst rest = header.slice(17);\n\tconst fields = /* @__PURE__ */ new Map();\n\tfor (const part of rest.split(\",\")) {\n\t\tconst eq = part.indexOf(\"=\");\n\t\tif (eq === -1) continue;\n\t\tfields.set(part.slice(0, eq).trim(), part.slice(eq + 1).trim());\n\t}\n\tconst credential = fields.get(\"Credential\");\n\tconst signedHeaders = fields.get(\"SignedHeaders\");\n\tconst signature = fields.get(\"Signature\");\n\tif (!credential || !signedHeaders || !signature) return null;\n\treturn {\n\t\tcredential,\n\t\tsignedHeaders: signedHeaders.split(\";\"),\n\t\tsignature\n\t};\n}\n/** The one signing core both auth forms share: check the access key, rebuild the canonical request, derive the key, compare in constant time. */\nfunction verifySignature(req, url, credentials, params) {\n\tif (params.scope.accessKeyId !== credentials.accessKeyId) return {\n\t\tok: false,\n\t\treason: \"unknown access key\"\n\t};\n\tconst canonicalRequest = [\n\t\treq.method,\n\t\turl.pathname,\n\t\tcanonicalQuery(url, params.excludeQuery),\n\t\tcanonicalHeaders(url, req, params.signedHeaders),\n\t\tparams.signedHeaders.join(\";\"),\n\t\tparams.payloadHash\n\t].join(\"\\n\");\n\treturn signatureMatches(hmac(signingKey(credentials.secretAccessKey, params.scope), stringToSign(params.amzDate, params.scope, canonicalRequest)).toString(\"hex\"), params.signature) ? { ok: true } : {\n\t\tok: false,\n\t\treason: \"signature mismatch\"\n\t};\n}\nfunction verifyHeader(req, url, credentials) {\n\tconst auth = parseAuthorizationHeader(req.headers.get(\"authorization\") ?? \"\");\n\tif (!auth) return {\n\t\tok: false,\n\t\treason: \"malformed Authorization header\"\n\t};\n\tconst scope = parseCredential(auth.credential);\n\tif (!scope) return {\n\t\tok: false,\n\t\treason: \"malformed credential scope\"\n\t};\n\tconst amzDate = req.headers.get(\"x-amz-date\");\n\tif (!amzDate) return {\n\t\tok: false,\n\t\treason: \"missing x-amz-date\"\n\t};\n\tconst payloadHash = req.headers.get(\"x-amz-content-sha256\");\n\tif (!payloadHash) return {\n\t\tok: false,\n\t\treason: \"missing x-amz-content-sha256\"\n\t};\n\treturn verifySignature(req, url, credentials, {\n\t\tscope,\n\t\tamzDate,\n\t\tsignedHeaders: auth.signedHeaders,\n\t\tpayloadHash,\n\t\tsignature: auth.signature\n\t});\n}\nfunction verifyPresigned(req, url, credentials, now) {\n\tconst q = url.searchParams;\n\tif (q.get(\"X-Amz-Algorithm\") !== ALGORITHM) return {\n\t\tok: false,\n\t\treason: \"unsupported presign algorithm\"\n\t};\n\tconst credentialRaw = q.get(\"X-Amz-Credential\");\n\tconst amzDate = q.get(\"X-Amz-Date\");\n\tconst expiresRaw = q.get(\"X-Amz-Expires\");\n\tconst signedHeadersRaw = q.get(\"X-Amz-SignedHeaders\");\n\tconst signature = q.get(\"X-Amz-Signature\");\n\tif (!credentialRaw || !amzDate || !expiresRaw || !signedHeadersRaw || !signature) return {\n\t\tok: false,\n\t\treason: \"incomplete presign parameters\"\n\t};\n\tconst scope = parseCredential(credentialRaw);\n\tif (!scope) return {\n\t\tok: false,\n\t\treason: \"malformed credential scope\"\n\t};\n\tconst signedAt = parseAmzDate(amzDate);\n\tconst expires = Number(expiresRaw);\n\tif (signedAt === null || !Number.isFinite(expires)) return {\n\t\tok: false,\n\t\treason: \"malformed presign date\"\n\t};\n\tif (now.getTime() > signedAt + expires * 1e3) return {\n\t\tok: false,\n\t\treason: \"presign expired\"\n\t};\n\treturn verifySignature(req, url, credentials, {\n\t\tscope,\n\t\tamzDate,\n\t\tsignedHeaders: signedHeadersRaw.split(\";\"),\n\t\tpayloadHash: UNSIGNED_PAYLOAD,\n\t\tsignature,\n\t\texcludeQuery: \"X-Amz-Signature\"\n\t});\n}\n/**\n* Verify a request's SigV4 signature against a single credential pair. Picks\n* the presigned form when `X-Amz-Signature` is present, otherwise the\n* `Authorization`-header form. `now` is injectable for deterministic\n* expiry tests.\n*/\nfunction verifyRequest(req, credentials, now = /* @__PURE__ */ new Date()) {\n\tconst url = new URL(req.url);\n\tif (url.searchParams.has(\"X-Amz-Signature\")) return verifyPresigned(req, url, credentials, now);\n\tif (req.headers.has(\"authorization\")) return verifyHeader(req, url, credentials);\n\treturn {\n\t\tok: false,\n\t\treason: \"unsigned request\"\n\t};\n}\n//#endregion\n//#region src/handler.ts\nconst DEFAULT_CONTENT_TYPE = \"application/octet-stream\";\n/** Path-style: `/{bucket}/{key…}`. Each segment is percent-decoded. */\nfunction parseTarget(url) {\n\tconst segments = url.pathname.split(\"/\").filter((s) => s.length > 0);\n\tif (segments.length === 0) return null;\n\tconst [bucket, ...keyParts] = segments;\n\treturn {\n\t\tbucket: decodeURIComponent(bucket ?? \"\"),\n\t\tkey: keyParts.map(decodeURIComponent).join(\"/\")\n\t};\n}\n/** `bytes=a-b` (inclusive) or `bytes=a-` (open-ended). Null when absent/malformed. */\nfunction parseRange(header) {\n\tif (!header) return null;\n\tconst match = /^bytes=(\\d+)-(\\d*)$/.exec(header.trim());\n\tif (!match) return null;\n\tconst start = Number(match[1]);\n\treturn match[2] ? {\n\t\tstart,\n\t\tend: Number(match[2])\n\t} : { start };\n}\nfunction xmlEscape(value) {\n\treturn value.replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\").replace(/\"/g, \"&quot;\").replace(/'/g, \"&apos;\");\n}\nfunction listXml(bucket, prefix, maxKeys, result) {\n\tconst contents = result.keys.map((k) => `<Contents><Key>${xmlEscape(k)}</Key></Contents>`).join(\"\");\n\tconst next = result.isTruncated && result.nextContinuationToken !== void 0 ? `<NextContinuationToken>${xmlEscape(result.nextContinuationToken)}</NextContinuationToken>` : \"\";\n\treturn `<?xml version=\"1.0\" encoding=\"UTF-8\"?><ListBucketResult xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\"><Name>${xmlEscape(bucket)}</Name><Prefix>${xmlEscape(prefix)}</Prefix><KeyCount>${result.keys.length}</KeyCount><MaxKeys>${maxKeys}</MaxKeys><IsTruncated>${result.isTruncated}</IsTruncated>` + contents + next + \"</ListBucketResult>\";\n}\nconst DEFAULT_MAX_KEYS = 1e3;\nasync function handleList(store, bucket, url) {\n\tconst prefix = url.searchParams.get(\"prefix\") ?? \"\";\n\tconst continuationToken = url.searchParams.get(\"continuation-token\");\n\tconst maxKeysRaw = url.searchParams.get(\"max-keys\");\n\tconst maxKeys = maxKeysRaw !== null && Number.isFinite(Number(maxKeysRaw)) ? Number(maxKeysRaw) : DEFAULT_MAX_KEYS;\n\tconst result = await store.list(bucket, {\n\t\tprefix,\n\t\tmaxKeys,\n\t\t...continuationToken !== null ? { continuationToken } : {}\n\t});\n\treturn new Response(listXml(bucket, prefix, maxKeys, result), {\n\t\tstatus: 200,\n\t\theaders: { \"content-type\": \"application/xml\" }\n\t});\n}\n/** aws-chunked / flexible-checksum PUTs frame the body as chunks + a trailer (signalled by `x-amz-content-sha256: STREAMING-…` or `content-encoding: aws-chunked`); the seed signature still verifies, so reject them (501) rather than store the raw framing as the object bytes. Decoding is out of scope. */\nfunction isStreamingPut(req) {\n\tconst contentSha = req.headers.get(\"x-amz-content-sha256\") ?? \"\";\n\tconst contentEncoding = req.headers.get(\"content-encoding\") ?? \"\";\n\treturn contentSha.startsWith(\"STREAMING-\") || contentEncoding.split(\",\").some((e) => e.trim() === \"aws-chunked\");\n}\nasync function handlePut(store, t, req) {\n\tif (isStreamingPut(req)) return new Response(\"aws-chunked / flexible checksums not supported; set requestChecksumCalculation: 'WHEN_REQUIRED'\", { status: 501 });\n\tconst body = new Uint8Array(await req.arrayBuffer());\n\tconst contentType = req.headers.get(\"content-type\") ?? DEFAULT_CONTENT_TYPE;\n\tconst { etag } = await store.put(t.bucket, t.key, body, { contentType });\n\treturn new Response(null, {\n\t\tstatus: 200,\n\t\theaders: { etag }\n\t});\n}\n/** The etag/content-type/content-length/accept-ranges headers GET and HEAD share — `contentLength` is the slice length for GET, the total object size for HEAD. */\nfunction metaHeaders(meta) {\n\treturn new Headers({\n\t\tetag: meta.etag,\n\t\t\"content-type\": meta.contentType,\n\t\t\"content-length\": String(meta.contentLength),\n\t\t\"accept-ranges\": \"bytes\"\n\t});\n}\nasync function handleGet(store, t, req) {\n\tconst range = parseRange(req.headers.get(\"range\"));\n\tconst object = await store.get(t.bucket, t.key, range ? { range } : void 0);\n\tif (!object) return new Response(null, { status: 404 });\n\tconst headers = metaHeaders({\n\t\tetag: object.etag,\n\t\tcontentType: object.contentType,\n\t\tcontentLength: object.bytes.byteLength\n\t});\n\tif (!range) return new Response(object.bytes, {\n\t\tstatus: 200,\n\t\theaders\n\t});\n\tif (range.start >= object.size && object.size > 0) return new Response(null, {\n\t\tstatus: 416,\n\t\theaders: { \"content-range\": `bytes */${object.size}` }\n\t});\n\tconst end = range.end === void 0 ? object.size - 1 : Math.min(range.end, object.size - 1);\n\theaders.set(\"content-range\", `bytes ${range.start}-${end}/${object.size}`);\n\treturn new Response(object.bytes, {\n\t\tstatus: 206,\n\t\theaders\n\t});\n}\nasync function handleHead(store, t) {\n\tconst meta = await store.head(t.bucket, t.key);\n\tif (!meta) return new Response(null, { status: 404 });\n\treturn new Response(null, {\n\t\tstatus: 200,\n\t\theaders: metaHeaders({\n\t\t\tetag: meta.etag,\n\t\t\tcontentType: meta.contentType,\n\t\t\tcontentLength: meta.size\n\t\t})\n\t});\n}\nasync function handleDelete(store, t) {\n\tawait store.delete(t.bucket, t.key);\n\treturn new Response(null, { status: 204 });\n}\nfunction createS3Handler(opts) {\n\tconst { store, credentials } = opts;\n\treturn async (req) => {\n\t\tif (!verifyRequest(req, credentials).ok) return new Response(null, { status: 403 });\n\t\tconst url = new URL(req.url);\n\t\tconst target = parseTarget(url);\n\t\tif (!target) return new Response(null, { status: 400 });\n\t\tif (req.method === \"GET\" && url.searchParams.get(\"list-type\") === \"2\" && target.key === \"\") return handleList(store, target.bucket, url);\n\t\tif (target.key === \"\") return new Response(null, { status: 400 });\n\t\tswitch (req.method) {\n\t\t\tcase \"PUT\": return handlePut(store, target, req);\n\t\t\tcase \"GET\": return handleGet(store, target, req);\n\t\t\tcase \"HEAD\": return handleHead(store, target);\n\t\t\tcase \"DELETE\": return handleDelete(store, target);\n\t\t\tdefault: return new Response(null, { status: 405 });\n\t\t}\n\t};\n}\n//#endregion\n//#region src/storage-server.ts\n/**\n* Boots the S3 wire protocol on `Bun.serve` — the D2 handler over any\n* `ObjectStore`. Binds all interfaces (Compute routes external HTTP to the VM,\n* so a loopback-only listener would be unreachable). Installs the FT-5219\n* process guards so an idle Bun.SQL connection close surfaces as a logged\n* error instead of crash-looping the process on scale-to-zero.\n*\n* Runtime engine code; NOT re-exported from the authoring barrel. The D4\n* entrypoint reads deps via `load()` and calls this.\n*/\nlet guardsInstalled = false;\n/** FT-5219: keep the process alive when Bun.SQL surfaces an idle-close as an unawaited async error. Installed once. */\nfunction installProcessGuards() {\n\tif (guardsInstalled) return;\n\tguardsInstalled = true;\n\tprocess.on(\"uncaughtException\", (err) => console.error(\"uncaughtException\", err));\n\tprocess.on(\"unhandledRejection\", (err) => console.error(\"unhandledRejection\", err));\n}\nfunction startStorageServer(opts) {\n\tinstallProcessGuards();\n\tconst handler = createS3Handler({\n\t\tstore: opts.store,\n\t\tcredentials: opts.credentials\n\t});\n\tconst hostname = opts.hostname ?? \"0.0.0.0\";\n\tconst server = Bun.serve({\n\t\tport: opts.port,\n\t\thostname,\n\t\tfetch: (req) => handler(req)\n\t});\n\treturn {\n\t\turl: `http://${hostname === \"0.0.0.0\" ? \"127.0.0.1\" : hostname}:${server.port}`,\n\t\tstop: () => server.stop(true)\n\t};\n}\n//#endregion\nexport { createPgStore, startStorageServer };\n\n//# sourceMappingURL=testing.mjs.map"],"mappings":"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"}
1
+ {"version":3,"file":"testing.mjs","names":[],"sources":["../../../../1-prisma-cloud/2-shared-modules/storage/dist/testing.mjs"],"sourcesContent":["import { createHash, createHmac, timingSafeEqual } from \"node:crypto\";\nimport { SQL } from \"bun\";\n//#region ../../1-extensions/target/dist/pg-connection-CadPZuEK.mjs\n/** Connection resilience helpers for Prisma Postgres cold-starts (FT-5226); no heavy imports (no `effect`/`alchemy`/`pg`), so the deploy lowerings, the pnPostgres runtime client, and bun-runnable services (the storage store, via the pure `@internal/prisma-cloud/connection` subpath) all share one implementation. */\n/** Network-level socket failures node-postgres surfaces as `err.code`. */\nconst TRANSIENT_CODES = /* @__PURE__ */ new Set([\n\t\"ECONNREFUSED\",\n\t\"ECONNRESET\",\n\t\"ETIMEDOUT\",\n\t\"EPIPE\",\n\t\"ENOTFOUND\",\n\t\"EAI_AGAIN\"\n]);\n/** Connection-establishment failure messages (no useful `err.code`). */\nconst TRANSIENT_MESSAGE_FRAGMENTS = [\n\t\"upstream database\",\n\t\"connection terminated\",\n\t\"connection refused\",\n\t\"terminating connection\",\n\t\"server closed the connection\",\n\t\"connection timeout\",\n\t\"timeout expired\"\n];\n/** Whether an error is a transient connection failure worth retrying, as opposed to a real query error that must surface at once. */\nfunction isTransientConnectionError(error) {\n\tif (typeof error !== \"object\" || error === null) return false;\n\tconst code = \"code\" in error && typeof error.code === \"string\" ? error.code : void 0;\n\tif (code !== void 0 && TRANSIENT_CODES.has(code)) return true;\n\tconst message = \"message\" in error && typeof error.message === \"string\" ? error.message.toLowerCase() : \"\";\n\treturn TRANSIENT_MESSAGE_FRAGMENTS.some((fragment) => message.includes(fragment));\n}\n/**\n* Retries an operation past a transient connection failure, bounded (default\n* ~1 min). `shouldRetry` decides what's transient — defaults to retrying\n* everything; the runtime client passes {@link isTransientConnectionError}.\n*/\nasync function withConnectionRetry(operation, opts = {}) {\n\tconst attempts = opts.attempts ?? 12;\n\tconst delayMs = opts.delayMs ?? 5e3;\n\tconst sleep = opts.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));\n\tconst shouldRetry = opts.shouldRetry ?? (() => true);\n\tlet lastError;\n\tfor (let attempt = 1; attempt <= attempts; attempt++) try {\n\t\treturn await operation();\n\t} catch (error) {\n\t\tif (!shouldRetry(error)) throw error;\n\t\tlastError = error;\n\t\tif (attempt < attempts) await sleep(delayMs);\n\t}\n\tthrow lastError;\n}\n/** Retries acquiring a connection past a transient cold-start; {@link withConnectionRetry} with {@link isTransientConnectionError} fixed as the predicate. */\nfunction retryTransientConnect(acquire, opts = {}) {\n\treturn withConnectionRetry(acquire, {\n\t\t...opts,\n\t\tshouldRetry: isTransientConnectionError\n\t});\n}\n//#endregion\n//#region src/pg-store.ts\n/**\n* The `ObjectStore` over Postgres `bytea` (spec § 3): one `objects` table,\n* single-row-per-object. Ranged reads use SQL `substring` so a range request\n* never detoasts the whole object. The schema is applied idempotently at init\n* behind a bounded connection retry — the first connect to a freshly\n* provisioned Postgres is rejected while the upstream is cold (FT-5226).\n*\n* Runtime engine code (`bun` SQL + `node:crypto`); NOT re-exported from the\n* authoring barrel.\n*/\nconst DEFAULT_MAX_KEYS = 1e3;\nfunction etagOf(bytes) {\n\treturn `\"${createHash(\"sha256\").update(bytes).digest(\"hex\")}\"`;\n}\n/** bytea comes back as a Node Buffer (a Uint8Array). Fail closed on anything else rather than returning wrong bytes. */\nfunction toBytes(value) {\n\tif (value instanceof Uint8Array) return value;\n\tthrow new TypeError(`expected bytea to decode as Uint8Array, got ${typeof value}`);\n}\n/** One row → GetResult (both the whole-object and ranged queries alias the payload as `bytes`; `size` is the bigint total). */\nfunction toGetResult(row) {\n\treturn {\n\t\tbytes: toBytes(row.bytes),\n\t\tetag: row.etag,\n\t\tcontentType: row.content_type,\n\t\tsize: Number(row.size)\n\t};\n}\nvar PgObjectStore = class {\n\tsql;\n\tconstructor(sql) {\n\t\tthis.sql = sql;\n\t}\n\tasync put(bucket, key, bytes, opts = {}) {\n\t\tconst etag = etagOf(bytes);\n\t\tconst contentType = opts.contentType ?? \"application/octet-stream\";\n\t\tawait this.sql`\n insert into objects (bucket, key, bytes, size, etag, content_type)\n values (${bucket}, ${key}, ${bytes}, ${bytes.byteLength}, ${etag}, ${contentType})\n on conflict (bucket, key) do update set\n bytes = excluded.bytes, size = excluded.size,\n etag = excluded.etag, content_type = excluded.content_type`;\n\t\treturn { etag };\n\t}\n\tasync get(bucket, key, opts = {}) {\n\t\tconst range = opts.range;\n\t\tif (range) {\n\t\t\tconst from = range.start + 1;\n\t\t\tconst row = (range.end === void 0 ? await this.sql`select substring(bytes from ${from}) as bytes, size, etag, content_type\n from objects where bucket = ${bucket} and key = ${key}` : await this.sql`select substring(bytes from ${from} for ${range.end - range.start + 1}) as bytes,\n size, etag, content_type\n from objects where bucket = ${bucket} and key = ${key}`)[0];\n\t\t\treturn row === void 0 ? null : toGetResult(row);\n\t\t}\n\t\tconst row = (await this.sql`select bytes, size, etag, content_type\n from objects where bucket = ${bucket} and key = ${key}`)[0];\n\t\treturn row === void 0 ? null : toGetResult(row);\n\t}\n\tasync head(bucket, key) {\n\t\tconst row = (await this.sql`select size, etag, content_type\n from objects where bucket = ${bucket} and key = ${key}`)[0];\n\t\tif (row === void 0) return null;\n\t\treturn {\n\t\t\tetag: row.etag,\n\t\t\tsize: Number(row.size),\n\t\t\tcontentType: row.content_type\n\t\t};\n\t}\n\tasync delete(bucket, key) {\n\t\tawait this.sql`delete from objects where bucket = ${bucket} and key = ${key}`;\n\t}\n\tasync list(bucket, opts = {}) {\n\t\tconst prefix = opts.prefix ?? \"\";\n\t\tconst maxKeys = opts.maxKeys ?? DEFAULT_MAX_KEYS;\n\t\tconst token = opts.continuationToken;\n\t\tconst limit = maxKeys + 1;\n\t\tconst keys = (token === void 0 ? await this.sql`select key from objects\n where bucket = ${bucket} and starts_with(key, ${prefix})\n order by key limit ${limit}` : await this.sql`select key from objects\n where bucket = ${bucket} and starts_with(key, ${prefix}) and key > ${token}\n order by key limit ${limit}`).map((r) => r.key);\n\t\tconst isTruncated = keys.length > maxKeys;\n\t\tconst page = isTruncated ? keys.slice(0, maxKeys) : keys;\n\t\tconst last = page.at(-1);\n\t\treturn {\n\t\t\tkeys: page,\n\t\t\tisTruncated,\n\t\t\t...isTruncated && last !== void 0 ? { nextContinuationToken: last } : {}\n\t\t};\n\t}\n};\n/**\n* Connect (FT-5219 posture: `max: 1`, short `idleTimeout`), apply the schema\n* idempotently behind the cold-start retry, and return the store.\n*/\nasync function createPgStore(url) {\n\tconst sql = new SQL({\n\t\turl,\n\t\tmax: 1,\n\t\tidleTimeout: 10\n\t});\n\tawait retryTransientConnect(() => sql`\n create table if not exists objects (\n bucket text not null,\n key text not null,\n bytes bytea not null,\n size bigint not null,\n etag text not null,\n content_type text not null,\n created_at timestamptz not null default now(),\n primary key (bucket, key)\n )`);\n\treturn new PgObjectStore(sql);\n}\n//#endregion\n//#region ../../0-lowering/s3-protocol/dist/index.mjs\nconst ALGORITHM = \"AWS4-HMAC-SHA256\";\nconst UNSIGNED_PAYLOAD = \"UNSIGNED-PAYLOAD\";\nfunction sha256Hex(data) {\n\treturn createHash(\"sha256\").update(data).digest(\"hex\");\n}\nfunction hmac(key, data) {\n\treturn createHmac(\"sha256\", key).update(data).digest();\n}\n/** AWS canonical URI encoding: every byte except the unreserved set is %XX. */\nfunction awsUriEncode(value) {\n\treturn encodeURIComponent(value).replace(/[!'()*]/g, (ch) => `%${ch.charCodeAt(0).toString(16).toUpperCase()}`).replace(/%7E/g, \"~\");\n}\nfunction parseCredential(credential) {\n\tconst parts = credential.split(\"/\");\n\tif (parts.length !== 5 || parts[4] !== \"aws4_request\") return null;\n\tconst [accessKeyId, date, region, service] = parts;\n\tif (!accessKeyId || !date || !region || !service) return null;\n\treturn {\n\t\taccessKeyId,\n\t\tdate,\n\t\tregion,\n\t\tservice\n\t};\n}\nfunction signingKey(secret, scope) {\n\treturn hmac(hmac(hmac(hmac(`AWS4${secret}`, scope.date), scope.region), scope.service), \"aws4_request\");\n}\nfunction canonicalHeaders(url, req, signedHeaders) {\n\treturn signedHeaders.map((name) => {\n\t\treturn `${name}:${(name === \"host\" ? url.host : req.headers.get(name) ?? \"\").trim().replace(/\\s+/g, \" \")}\\n`;\n\t}).join(\"\");\n}\nfunction canonicalQuery(url, exclude) {\n\tconst entries = [];\n\tfor (const [key, value] of url.searchParams.entries()) {\n\t\tif (exclude !== void 0 && key === exclude) continue;\n\t\tentries.push([awsUriEncode(key), awsUriEncode(value)]);\n\t}\n\tconst cmp = (a, b) => a < b ? -1 : a > b ? 1 : 0;\n\tentries.sort(([ak, av], [bk, bv]) => cmp(ak, bk) || cmp(av, bv));\n\treturn entries.map(([k, v]) => `${k}=${v}`).join(\"&\");\n}\nfunction stringToSign(amzDate, scope, canonicalRequest) {\n\tconst scopeString = `${scope.date}/${scope.region}/${scope.service}/aws4_request`;\n\treturn [\n\t\tALGORITHM,\n\t\tamzDate,\n\t\tscopeString,\n\t\tsha256Hex(canonicalRequest)\n\t].join(\"\\n\");\n}\nfunction signatureMatches(expected, provided) {\n\tconst a = Buffer.from(expected, \"hex\");\n\tconst b = Buffer.from(provided, \"hex\");\n\treturn a.length === b.length && a.length > 0 && timingSafeEqual(a, b);\n}\n/** `YYYYMMDDTHHMMSSZ` → epoch ms, or null when malformed. */\nfunction parseAmzDate(amzDate) {\n\tconst match = /^(\\d{4})(\\d{2})(\\d{2})T(\\d{2})(\\d{2})(\\d{2})Z$/.exec(amzDate);\n\tif (!match) return null;\n\tconst [, y, mo, d, h, mi, s] = match;\n\treturn Date.UTC(Number(y), Number(mo) - 1, Number(d), Number(h), Number(mi), Number(s));\n}\nfunction parseAuthorizationHeader(header) {\n\tif (!header.startsWith(`${ALGORITHM} `)) return null;\n\tconst rest = header.slice(17);\n\tconst fields = /* @__PURE__ */ new Map();\n\tfor (const part of rest.split(\",\")) {\n\t\tconst eq = part.indexOf(\"=\");\n\t\tif (eq === -1) continue;\n\t\tfields.set(part.slice(0, eq).trim(), part.slice(eq + 1).trim());\n\t}\n\tconst credential = fields.get(\"Credential\");\n\tconst signedHeaders = fields.get(\"SignedHeaders\");\n\tconst signature = fields.get(\"Signature\");\n\tif (!credential || !signedHeaders || !signature) return null;\n\treturn {\n\t\tcredential,\n\t\tsignedHeaders: signedHeaders.split(\";\"),\n\t\tsignature\n\t};\n}\n/** The one signing core both auth forms share: check the access key, rebuild the canonical request, derive the key, compare in constant time. */\nfunction verifySignature(req, url, credentials, params) {\n\tif (params.scope.accessKeyId !== credentials.accessKeyId) return {\n\t\tok: false,\n\t\treason: \"unknown access key\"\n\t};\n\tconst canonicalRequest = [\n\t\treq.method,\n\t\turl.pathname,\n\t\tcanonicalQuery(url, params.excludeQuery),\n\t\tcanonicalHeaders(url, req, params.signedHeaders),\n\t\tparams.signedHeaders.join(\";\"),\n\t\tparams.payloadHash\n\t].join(\"\\n\");\n\treturn signatureMatches(hmac(signingKey(credentials.secretAccessKey, params.scope), stringToSign(params.amzDate, params.scope, canonicalRequest)).toString(\"hex\"), params.signature) ? { ok: true } : {\n\t\tok: false,\n\t\treason: \"signature mismatch\"\n\t};\n}\nfunction verifyHeader(req, url, credentials) {\n\tconst auth = parseAuthorizationHeader(req.headers.get(\"authorization\") ?? \"\");\n\tif (!auth) return {\n\t\tok: false,\n\t\treason: \"malformed Authorization header\"\n\t};\n\tconst scope = parseCredential(auth.credential);\n\tif (!scope) return {\n\t\tok: false,\n\t\treason: \"malformed credential scope\"\n\t};\n\tconst amzDate = req.headers.get(\"x-amz-date\");\n\tif (!amzDate) return {\n\t\tok: false,\n\t\treason: \"missing x-amz-date\"\n\t};\n\tconst payloadHash = req.headers.get(\"x-amz-content-sha256\");\n\tif (!payloadHash) return {\n\t\tok: false,\n\t\treason: \"missing x-amz-content-sha256\"\n\t};\n\treturn verifySignature(req, url, credentials, {\n\t\tscope,\n\t\tamzDate,\n\t\tsignedHeaders: auth.signedHeaders,\n\t\tpayloadHash,\n\t\tsignature: auth.signature\n\t});\n}\nfunction verifyPresigned(req, url, credentials, now) {\n\tconst q = url.searchParams;\n\tif (q.get(\"X-Amz-Algorithm\") !== ALGORITHM) return {\n\t\tok: false,\n\t\treason: \"unsupported presign algorithm\"\n\t};\n\tconst credentialRaw = q.get(\"X-Amz-Credential\");\n\tconst amzDate = q.get(\"X-Amz-Date\");\n\tconst expiresRaw = q.get(\"X-Amz-Expires\");\n\tconst signedHeadersRaw = q.get(\"X-Amz-SignedHeaders\");\n\tconst signature = q.get(\"X-Amz-Signature\");\n\tif (!credentialRaw || !amzDate || !expiresRaw || !signedHeadersRaw || !signature) return {\n\t\tok: false,\n\t\treason: \"incomplete presign parameters\"\n\t};\n\tconst scope = parseCredential(credentialRaw);\n\tif (!scope) return {\n\t\tok: false,\n\t\treason: \"malformed credential scope\"\n\t};\n\tconst signedAt = parseAmzDate(amzDate);\n\tconst expires = Number(expiresRaw);\n\tif (signedAt === null || !Number.isFinite(expires)) return {\n\t\tok: false,\n\t\treason: \"malformed presign date\"\n\t};\n\tif (now.getTime() > signedAt + expires * 1e3) return {\n\t\tok: false,\n\t\treason: \"presign expired\"\n\t};\n\treturn verifySignature(req, url, credentials, {\n\t\tscope,\n\t\tamzDate,\n\t\tsignedHeaders: signedHeadersRaw.split(\";\"),\n\t\tpayloadHash: UNSIGNED_PAYLOAD,\n\t\tsignature,\n\t\texcludeQuery: \"X-Amz-Signature\"\n\t});\n}\n/**\n* Verify a request's SigV4 signature against a single credential pair. Picks\n* the presigned form when `X-Amz-Signature` is present, otherwise the\n* `Authorization`-header form. `now` is injectable for deterministic\n* expiry tests.\n*/\nfunction verifyRequest(req, credentials, now = /* @__PURE__ */ new Date()) {\n\tconst url = new URL(req.url);\n\tif (url.searchParams.has(\"X-Amz-Signature\")) return verifyPresigned(req, url, credentials, now);\n\tif (req.headers.has(\"authorization\")) return verifyHeader(req, url, credentials);\n\treturn {\n\t\tok: false,\n\t\treason: \"unsigned request\"\n\t};\n}\nconst DEFAULT_CONTENT_TYPE = \"application/octet-stream\";\n/** Path-style: `/{bucket}/{key…}`. Each segment is percent-decoded. */\nfunction parseTarget(url) {\n\tconst segments = url.pathname.split(\"/\").filter((s) => s.length > 0);\n\tif (segments.length === 0) return null;\n\tconst [bucket, ...keyParts] = segments;\n\treturn {\n\t\tbucket: decodeURIComponent(bucket ?? \"\"),\n\t\tkey: keyParts.map(decodeURIComponent).join(\"/\")\n\t};\n}\n/** `bytes=a-b` (inclusive) or `bytes=a-` (open-ended). Null when absent/malformed. */\nfunction parseRange(header) {\n\tif (!header) return null;\n\tconst match = /^bytes=(\\d+)-(\\d*)$/.exec(header.trim());\n\tif (!match) return null;\n\tconst start = Number(match[1]);\n\treturn match[2] ? {\n\t\tstart,\n\t\tend: Number(match[2])\n\t} : { start };\n}\nfunction xmlEscape(value) {\n\treturn value.replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\").replace(/\"/g, \"&quot;\").replace(/'/g, \"&apos;\");\n}\nfunction listXml(bucket, prefix, maxKeys, result) {\n\tconst contents = result.keys.map((k) => `<Contents><Key>${xmlEscape(k)}</Key></Contents>`).join(\"\");\n\tconst next = result.isTruncated && result.nextContinuationToken !== void 0 ? `<NextContinuationToken>${xmlEscape(result.nextContinuationToken)}</NextContinuationToken>` : \"\";\n\treturn `<?xml version=\"1.0\" encoding=\"UTF-8\"?><ListBucketResult xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\"><Name>${xmlEscape(bucket)}</Name><Prefix>${xmlEscape(prefix)}</Prefix><KeyCount>${result.keys.length}</KeyCount><MaxKeys>${maxKeys}</MaxKeys><IsTruncated>${result.isTruncated}</IsTruncated>` + contents + next + \"</ListBucketResult>\";\n}\nconst DEFAULT_MAX_KEYS$1 = 1e3;\nasync function handleList(store, bucket, url) {\n\tconst prefix = url.searchParams.get(\"prefix\") ?? \"\";\n\tconst continuationToken = url.searchParams.get(\"continuation-token\");\n\tconst maxKeysRaw = url.searchParams.get(\"max-keys\");\n\tconst maxKeys = maxKeysRaw !== null && Number.isFinite(Number(maxKeysRaw)) ? Number(maxKeysRaw) : DEFAULT_MAX_KEYS$1;\n\tconst result = await store.list(bucket, {\n\t\tprefix,\n\t\tmaxKeys,\n\t\t...continuationToken !== null ? { continuationToken } : {}\n\t});\n\treturn new Response(listXml(bucket, prefix, maxKeys, result), {\n\t\tstatus: 200,\n\t\theaders: { \"content-type\": \"application/xml\" }\n\t});\n}\n/** aws-chunked / flexible-checksum PUTs frame the body as chunks + a trailer (signalled by `x-amz-content-sha256: STREAMING-…` or `content-encoding: aws-chunked`); the seed signature still verifies, so reject them (501) rather than store the raw framing as the object bytes. Decoding is out of scope. */\nfunction isStreamingPut(req) {\n\tconst contentSha = req.headers.get(\"x-amz-content-sha256\") ?? \"\";\n\tconst contentEncoding = req.headers.get(\"content-encoding\") ?? \"\";\n\treturn contentSha.startsWith(\"STREAMING-\") || contentEncoding.split(\",\").some((e) => e.trim() === \"aws-chunked\");\n}\nasync function handlePut(store, t, req) {\n\tif (isStreamingPut(req)) return new Response(\"aws-chunked / flexible checksums not supported; set requestChecksumCalculation: 'WHEN_REQUIRED'\", { status: 501 });\n\tconst body = new Uint8Array(await req.arrayBuffer());\n\tconst contentType = req.headers.get(\"content-type\") ?? DEFAULT_CONTENT_TYPE;\n\tconst { etag } = await store.put(t.bucket, t.key, body, { contentType });\n\treturn new Response(null, {\n\t\tstatus: 200,\n\t\theaders: { etag }\n\t});\n}\n/** The etag/content-type/content-length/accept-ranges headers GET and HEAD share — `contentLength` is the slice length for GET, the total object size for HEAD. */\nfunction metaHeaders(meta) {\n\treturn new Headers({\n\t\tetag: meta.etag,\n\t\t\"content-type\": meta.contentType,\n\t\t\"content-length\": String(meta.contentLength),\n\t\t\"accept-ranges\": \"bytes\"\n\t});\n}\nasync function handleGet(store, t, req) {\n\tconst range = parseRange(req.headers.get(\"range\"));\n\tconst object = await store.get(t.bucket, t.key, range ? { range } : void 0);\n\tif (!object) return new Response(null, { status: 404 });\n\tconst headers = metaHeaders({\n\t\tetag: object.etag,\n\t\tcontentType: object.contentType,\n\t\tcontentLength: object.bytes.byteLength\n\t});\n\tif (!range) return new Response(object.bytes, {\n\t\tstatus: 200,\n\t\theaders\n\t});\n\tif (range.start >= object.size && object.size > 0) return new Response(null, {\n\t\tstatus: 416,\n\t\theaders: { \"content-range\": `bytes */${object.size}` }\n\t});\n\tconst end = range.end === void 0 ? object.size - 1 : Math.min(range.end, object.size - 1);\n\theaders.set(\"content-range\", `bytes ${range.start}-${end}/${object.size}`);\n\treturn new Response(object.bytes, {\n\t\tstatus: 206,\n\t\theaders\n\t});\n}\nasync function handleHead(store, t) {\n\tconst meta = await store.head(t.bucket, t.key);\n\tif (!meta) return new Response(null, { status: 404 });\n\treturn new Response(null, {\n\t\tstatus: 200,\n\t\theaders: metaHeaders({\n\t\t\tetag: meta.etag,\n\t\t\tcontentType: meta.contentType,\n\t\t\tcontentLength: meta.size\n\t\t})\n\t});\n}\nasync function handleDelete(store, t) {\n\tawait store.delete(t.bucket, t.key);\n\treturn new Response(null, { status: 204 });\n}\nfunction createS3Handler(opts) {\n\tconst { store, credentials } = opts;\n\treturn async (req) => {\n\t\tif (!verifyRequest(req, credentials).ok) return new Response(null, { status: 403 });\n\t\tconst url = new URL(req.url);\n\t\tconst target = parseTarget(url);\n\t\tif (!target) return new Response(null, { status: 400 });\n\t\tif (req.method === \"GET\" && url.searchParams.get(\"list-type\") === \"2\" && target.key === \"\") return handleList(store, target.bucket, url);\n\t\tif (target.key === \"\") return new Response(null, { status: 400 });\n\t\tswitch (req.method) {\n\t\t\tcase \"PUT\": return handlePut(store, target, req);\n\t\t\tcase \"GET\": return handleGet(store, target, req);\n\t\t\tcase \"HEAD\": return handleHead(store, target);\n\t\t\tcase \"DELETE\": return handleDelete(store, target);\n\t\t\tdefault: return new Response(null, { status: 405 });\n\t\t}\n\t};\n}\n//#endregion\n//#region src/storage-server.ts\nlet guardsInstalled = false;\n/** FT-5219: keep the process alive when Bun.SQL surfaces an idle-close as an unawaited async error. Installed once. */\nfunction installProcessGuards() {\n\tif (guardsInstalled) return;\n\tguardsInstalled = true;\n\tprocess.on(\"uncaughtException\", (err) => console.error(\"uncaughtException\", err));\n\tprocess.on(\"unhandledRejection\", (err) => console.error(\"unhandledRejection\", err));\n}\nfunction startStorageServer(opts) {\n\tinstallProcessGuards();\n\tconst handler = createS3Handler({\n\t\tstore: opts.store,\n\t\tcredentials: opts.credentials\n\t});\n\tconst hostname = opts.hostname ?? \"0.0.0.0\";\n\tconst server = Bun.serve({\n\t\tport: opts.port,\n\t\thostname,\n\t\tfetch: (req) => handler(req)\n\t});\n\treturn {\n\t\turl: `http://${hostname === \"0.0.0.0\" ? \"127.0.0.1\" : hostname}:${server.port}`,\n\t\tstop: () => server.stop(true)\n\t};\n}\n//#endregion\nexport { createPgStore, startStorageServer };\n\n//# sourceMappingURL=testing.mjs.map"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../../../1-prisma-cloud/1-extensions/target/dist/index.d.mts","../../../../1-prisma-cloud/2-shared-modules/storage/dist/storage-service-CVf6j3HC.d.mts","../../../../1-prisma-cloud/2-shared-modules/streams/dist/streams-service-DjbzOToP.d.mts","../../../../1-prisma-cloud/2-shared-modules/streams/dist/index.d.mts"],"mappings":";;;;;cA0Cc;WACH,+BAA+B,4CAA4C;;KAEjF,wBAAwB;;;;;;;;;;;;;;;;;;;;;;cAsBf,eAAe,UAAU,MAAM,UAAU,QAAQ,UAAU,QAAQ,UAAU,oBAAoB,oBAAoB,GAAG,IAAI,gBAAgB,GAAG;;YAEjJ;WACD;WACA;WACA;WACA;WACA,QAAQ;WACR,QAAQ,IAAI;WACZ,aAAa;WACb,OAAO;WACP,QAAQ;EACjB,YAAY,MAAM,YAAY,GAAG,IAAI,gBAAgB,GAAG;EACxD,IAAI,iBAAiB,YAAY,mBAAmB;EACpD,QAAQ,aAAa;EACrB,UAAU,OAAO,IAAI;EACrB,WAAW,aAAa;;;;EAIxB;;;;;UCrFQ;WACC;WACA;WACA;WACA;;cAEG,YAAY,eAAe;KACpC,oBAAoB;;;;UCPf;WACC;WACA;;;;;;;;;UASD;WACC;;;iBAGM,aAAa;KACzB,aAAa,eAAe;;;;;;;iBAOhB,gBAAgB,UAAU,YAAY,MAAM,IAAI,oBAAoB;;KAEhF,gBAAgB,UAAU,aAAa,cAAc,oBAAoB;;KAEzE,cAAc,UAAU,0BAA0B,WAAW,IAAI;;;;;;;;;iBASrD,eAAe,UAAU,YAAY,UAAU,oBAAoB,KAAK,cAAc,cAAc,IAAI,oBAAoB;iBAC5H,kBAAkB,cAAc,eAAe,oBAAoB;;;;UAI1E,kBAAkB;WACjB,iBAAiB;;WAEjB;;;UAGD,kBAAkB;WACjB,iBAAiB;WACjB;WACA;;;;;;;;;cASG;mBACK;mBACA;mBACA;mBACA;EACjB,YAAY,QAAQ;;EAEpB,OAAO,eAAe;UACd;;EAER,OAAO,eAAe;;;;;;;EAOtB,OAAO,cAAc,iBAAiB;;EAEtC,KAAK,aAAa,cAAc;IAC9B;MACE,QAAQ,kBAAkB;;;;;;;EAO9B,KAAK,aAAa,cAAc;IAC9B;IACA;IACA,SAAS;MACP,QAAQ,kBAAkB;;;;;;;;;;;;;;;;cAgBlB;mBACK;mBACA;UACT;EACR,YAAY,cAAc,WAAW;UAC7B;UACA;;;;;;;EAOR,OAAO,iBAAiB;;EAExB,KAAK,aAAa;IAChB;MACE,QAAQ,kBAAkB;;;;;;EAM9B,KAAK,aAAa;IAChB;IACA;IACA,SAAS;MACP,QAAQ,kBAAkB;;;;iBAIf,kBAId;EAHD,gCAAgC,cADjB,mCAC8F,eAAnC;GACzE;EACD,kCAAkC,oBAAoB;GACrD;;;;iBC5Ic,QAAQ;EACvB;IACE;EACF,OAAO,cAAc,UAAU;;EAE/B,SAAS,oBAAoB;GAC5B"}
@@ -111,15 +111,19 @@ function requireExtension(extension, factory) {
111
111
  if (typeof extension !== "string" || extension.length === 0) throw new Error(`${factory}() requires a non-empty extension (the authoring extension's package name).`);
112
112
  }
113
113
  /**
114
- * Config keys join address/input/param names with "_" and uppercase an
115
- * underscore inside a name would collide with that separator (e.g. param
116
- * "db_url" vs input "db"'s param "url" both hitting env key "DB_URL").
114
+ * Core's grammar for every name that becomes a config-key segment
115
+ * addresses, input/param/secret names: ASCII letters and digits only.
116
+ * Conservative by design so any target medium POSIX env-var keys
117
+ * included — can uppercase and "_"-join segments without escaping.
117
118
  */
118
- function requireNoUnderscoreName(name, kind, factory) {
119
- if (name.includes("_")) throw new Error(`${factory}() ${kind} name "${name}" may not contain "_" — config keys join names with "_" as the separator (e.g. an input "db"'s param "url" becomes env key "DB_URL"), so an underscore inside a name would collide with that separator.`);
119
+ function isConfigKeySegment(name) {
120
+ return /^[A-Za-z0-9]+$/.test(name);
120
121
  }
121
- function requireNoUnderscoreNames(names, kind, factory) {
122
- for (const name of names) requireNoUnderscoreName(name, kind, factory);
122
+ function requireConfigKeySegmentName(name, kind, factory) {
123
+ if (!isConfigKeySegment(name)) throw new Error(`${factory}() ${kind} name "${name}" must contain only ASCII letters and digits ([A-Za-z0-9]) — declared names derive deterministic config keys, uppercased and joined with "_" (an input "db"'s param "url" becomes config key "DB_URL"), so an underscore inside a name collides with that separator and any other character has no place in a config key. "${name}" would put "${name.toUpperCase()}" inside the derived key.`);
124
+ }
125
+ function requireConfigKeySegmentNames(names, kind, factory) {
126
+ for (const name of names) requireConfigKeySegmentName(name, kind, factory);
123
127
  }
124
128
  function freezeParams(params) {
125
129
  const frozen = {};
@@ -190,9 +194,9 @@ function service$1(def) {
190
194
  requireName(def.name, "service");
191
195
  requireExtension(def.extension, "service");
192
196
  requireType(def.type, "service");
193
- requireNoUnderscoreNames(Object.keys(def.inputs), "input", "service");
194
- requireNoUnderscoreNames(Object.keys(def.params), "param", "service");
195
- requireNoUnderscoreNames(Object.keys(def.secrets ?? {}), "secret", "service");
197
+ requireConfigKeySegmentNames(Object.keys(def.inputs), "input", "service");
198
+ requireConfigKeySegmentNames(Object.keys(def.params), "param", "service");
199
+ requireConfigKeySegmentNames(Object.keys(def.secrets ?? {}), "secret", "service");
196
200
  for (const slot of Object.keys(def.secrets ?? {})) if (Object.hasOwn(def.params, slot)) throw new Error(`service() secret slot "${slot}" collides with a param of the same name — a secret slot and a service param derive the same config key (COMPOSER_<addr>_${slot.toUpperCase()}); rename one.`);
197
201
  return Object.freeze({
198
202
  [NODE]: true,
@@ -214,7 +218,7 @@ function service$1(def) {
214
218
  */
215
219
  function dependency(def) {
216
220
  requireType(def.type, "dependency");
217
- requireNoUnderscoreNames(Object.keys(def.connection.params), "param", "dependency");
221
+ requireConfigKeySegmentNames(Object.keys(def.connection.params), "param", "dependency");
218
222
  const connection = Object.freeze({
219
223
  params: freezeParams(def.connection.params),
220
224
  hydrate: def.connection.hydrate