@prisma/composer-prisma-cloud 0.3.0-dev.8 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/dist/compute-main.mjs +4 -20
  2. package/dist/compute-main.mjs.map +1 -1
  3. package/dist/control.d.mts +1 -1
  4. package/dist/control.mjs +17 -28
  5. package/dist/control.mjs.map +1 -1
  6. package/dist/cron/index.d.mts +26 -33
  7. package/dist/cron/index.d.mts.map +1 -1
  8. package/dist/cron/index.mjs +77 -202
  9. package/dist/cron/index.mjs.map +1 -1
  10. package/dist/cron/scheduler-entrypoint.mjs +114 -133
  11. package/dist/cron/scheduler-entrypoint.mjs.map +1 -1
  12. package/dist/cron/scheduler-service.mjs +76 -200
  13. package/dist/cron/scheduler-service.mjs.map +1 -1
  14. package/dist/email/email-entrypoint.mjs +126 -162
  15. package/dist/email/email-entrypoint.mjs.map +1 -1
  16. package/dist/email/email-service.mjs +79 -230
  17. package/dist/email/email-service.mjs.map +1 -1
  18. package/dist/email/index.d.mts +29 -33
  19. package/dist/email/index.d.mts.map +1 -1
  20. package/dist/email/index.mjs +84 -236
  21. package/dist/email/index.mjs.map +1 -1
  22. package/dist/email/testing.mjs +89 -112
  23. package/dist/email/testing.mjs.map +1 -1
  24. package/dist/index.d.mts +43 -51
  25. package/dist/index.d.mts.map +1 -1
  26. package/dist/index.mjs +51 -47
  27. package/dist/index.mjs.map +1 -1
  28. package/dist/local-target.mjs +1 -1
  29. package/dist/local-target.mjs.map +1 -1
  30. package/dist/{secret-BVGxqHxY-DYDfiULl.mjs → param-BTzY0ne5-CW6ojZ3E.mjs} +4 -45
  31. package/dist/param-BTzY0ne5-CW6ojZ3E.mjs.map +1 -0
  32. package/dist/{preflight-names-CpJUd9ZA-BCU-W3cT.mjs → preflight-names-BPL-qsZF-BfoRgbgb.mjs} +14 -30
  33. package/dist/{preflight-names-CpJUd9ZA-BCU-W3cT.mjs.map → preflight-names-BPL-qsZF-BfoRgbgb.mjs.map} +1 -1
  34. package/dist/{provisioned-edges-Cxy18aoh-CUnaY96r.mjs → provisioned-edges-C1wSO3cx-YC5C19f9.mjs} +3 -3
  35. package/dist/{provisioned-edges-Cxy18aoh-CUnaY96r.mjs.map → provisioned-edges-C1wSO3cx-YC5C19f9.mjs.map} +1 -1
  36. package/dist/secret-rz_ZqNeq-_xd97Jd9.mjs +47 -0
  37. package/dist/secret-rz_ZqNeq-_xd97Jd9.mjs.map +1 -0
  38. package/dist/serializer-CJiOhF2d-DJUjQj1i.mjs +251 -0
  39. package/dist/serializer-CJiOhF2d-DJUjQj1i.mjs.map +1 -0
  40. package/dist/{serializer-BD_6nkr4-DWn5NNTC.d.mts → serializer-Cx5slrV4-xJfH6EWS.d.mts} +2 -2
  41. package/dist/serializer-Cx5slrV4-xJfH6EWS.d.mts.map +1 -0
  42. package/dist/storage/index.d.mts +26 -31
  43. package/dist/storage/index.d.mts.map +1 -1
  44. package/dist/storage/index.mjs +89 -215
  45. package/dist/storage/index.mjs.map +1 -1
  46. package/dist/storage/storage-entrypoint.mjs +122 -147
  47. package/dist/storage/storage-entrypoint.mjs.map +1 -1
  48. package/dist/storage/storage-service.mjs +87 -212
  49. package/dist/storage/storage-service.mjs.map +1 -1
  50. package/dist/streams/index.d.mts +22 -29
  51. package/dist/streams/index.d.mts.map +1 -1
  52. package/dist/streams/index.mjs +88 -213
  53. package/dist/streams/index.mjs.map +1 -1
  54. package/dist/streams/streams-entrypoint.mjs +118 -139
  55. package/dist/streams/streams-entrypoint.mjs.map +1 -1
  56. package/dist/streams/streams-service.mjs +87 -212
  57. package/dist/streams/streams-service.mjs.map +1 -1
  58. package/dist/testing.d.mts +5 -19
  59. package/dist/testing.d.mts.map +1 -1
  60. package/dist/testing.mjs +3 -11
  61. package/dist/testing.mjs.map +1 -1
  62. package/package.json +18 -18
  63. package/dist/secret-BVGxqHxY-DYDfiULl.mjs.map +0 -1
  64. package/dist/serializer-BD_6nkr4-DWn5NNTC.d.mts.map +0 -1
  65. package/dist/serializer-sVE4UAmg-CikRngbR.mjs +0 -475
  66. package/dist/serializer-sVE4UAmg-CikRngbR.mjs.map +0 -1
@@ -66,6 +66,14 @@ function blindCast(input) {
66
66
  * data objects. A node's `extension` + `type` form its deploy-time registry key (ADR-0017).
67
67
  */
68
68
  const NODE = Symbol.for("prisma:node");
69
+ const SECRET_NEED = blindCast(Symbol.for("prisma:secret-need"));
70
+ /** Declares a secret NEED. Nameless — the platform name is bound at the root via `envSecret`. */
71
+ function secret() {
72
+ return Object.freeze({
73
+ [SECRET_NEED]: true,
74
+ kind: "secret"
75
+ });
76
+ }
69
77
  const PROVISION_NEED = blindCast(Symbol.for("prisma:provision-need"));
70
78
  /** Builds an opaque provisioning need — the declaring package's own brand plus whatever payload its provisioner reads back. */
71
79
  function provisionNeed(brand, payload) {
@@ -104,6 +112,11 @@ function freezeParams(params) {
104
112
  for (const [name, param] of Object.entries(params)) frozen[name] = Object.freeze({ ...param });
105
113
  return Object.freeze(frozen);
106
114
  }
115
+ function freezeSecrets(secrets) {
116
+ const frozen = {};
117
+ for (const [name, need] of Object.entries(secrets)) frozen[name] = Object.freeze({ ...need });
118
+ return blindCast(Object.freeze(frozen));
119
+ }
107
120
  /** A frozen shallow copy that keeps the caller's declared type. */
108
121
  function frozenShallowCopy(obj) {
109
122
  return blindCast(Object.freeze({ ...obj }));
@@ -157,8 +170,7 @@ function resource(def) {
157
170
  }
158
171
  /**
159
172
  * Constructs a branded, frozen Service node — declarations only (inputs,
160
- * params, the input schema, build adapter, and the ports it exposes). Pure;
161
- * carries no runtime behavior.
173
+ * params, build adapter, and the ports it exposes). Pure; carries no runtime behavior.
162
174
  */
163
175
  function service$1(def) {
164
176
  requireName(def.name, "service");
@@ -166,7 +178,8 @@ function service$1(def) {
166
178
  requireType(def.type, "service");
167
179
  requireConfigKeySegmentNames(Object.keys(def.inputs), "input", "service");
168
180
  requireConfigKeySegmentNames(Object.keys(def.params), "param", "service");
169
- if (def.input !== void 0 && typeof def.input["~standard"]?.validate !== "function") throw new Error("service() `input` must be a Standard Schema (an object with a \"~standard\".validate function) — see https://standardschema.dev (ADR-0042).");
181
+ requireConfigKeySegmentNames(Object.keys(def.secrets ?? {}), "secret", "service");
182
+ 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.`);
170
183
  return Object.freeze({
171
184
  [NODE]: true,
172
185
  kind: "service",
@@ -175,7 +188,7 @@ function service$1(def) {
175
188
  type: def.type,
176
189
  inputs: frozenShallowCopy(def.inputs),
177
190
  params: freezeParams(def.params),
178
- inputSchema: blindCast(def.input),
191
+ secretSlots: freezeSecrets(def.secrets ?? blindCast({})),
179
192
  build: Object.freeze({ ...def.build }),
180
193
  expose: def.expose !== void 0 ? frozenShallowCopy(def.expose) : void 0
181
194
  });
@@ -208,16 +221,11 @@ function dependency(def) {
208
221
  * `JSON.stringify`, and `console.log`/`util.inspect` all print `[REDACTED]`, so
209
222
  * a secret can't leak through an accidental log or serialization.
210
223
  *
211
- * Every box carries a brand property keyed by the registered symbol
212
- * `Symbol.for('prisma:secret-box')`, which is what `isSecretString` checks. A
213
- * symbol key is invisible to `JSON.stringify` and `Object.keys`, so the box's
214
- * public shape still matches the platform's own `secrecy` type
215
- * (pdp-control-plane).
224
+ * Shape matches the platform's own `secrecy` type (pdp-control-plane). The class
225
+ * is nominal enough on its own — no phantom brand.
216
226
  */
217
227
  const REDACTED = "[REDACTED]";
218
- const SECRET_BOX = blindCast(Symbol.for("prisma:secret-box"));
219
228
  var SecretBox = class {
220
- [SECRET_BOX] = true;
221
229
  #value;
222
230
  constructor(value) {
223
231
  this.#value = value;
@@ -242,14 +250,6 @@ var SecretBox = class {
242
250
  return REDACTED;
243
251
  }
244
252
  };
245
- /**
246
- * True for a redacting secret box — what a schema's secret leaf checks
247
- * (ADR-0042). Reads the brand rather than `instanceof`, so a box from a
248
- * duplicated module copy in a bundle counts and a look-alike does not.
249
- */
250
- function isSecretString(value) {
251
- return typeof value === "object" && value !== null && blindCast(value)[SECRET_BOX] === true;
252
- }
253
253
  function scalarSchema(name, check) {
254
254
  return { "~standard": {
255
255
  version: 1,
@@ -275,6 +275,10 @@ function string$1(opts = {}) {
275
275
  function number$1(opts = {}) {
276
276
  return withFacets(numberSchema, opts);
277
277
  }
278
+ /** A param over any caller-supplied Standard Schema — a structured `jobs`, say. */
279
+ function param(schema, opts = {}) {
280
+ return withFacets(schema, opts);
281
+ }
278
282
  /**
279
283
  * Synchronous hydrate — what the node's `load()` uses so
280
284
  * `const { db } = service.load()` reads without `await`. Requires every
@@ -291,6 +295,24 @@ function hydrateSync(root, config) {
291
295
  }
292
296
  return deps;
293
297
  }
298
+ /**
299
+ * Wraps each of a service's resolved secret values in a redacting `SecretBox`
300
+ * — what the node's `secrets()` accessor returns (ADR-0021, sibling to
301
+ * `load()`/`config()`). The RESOLUTION of a secret's value (the boot
302
+ * double-lookup that reads the platform var the pointer names) is the target
303
+ * pack's job; core is handed the already-resolved strings and only boxes them,
304
+ * so a secret is redacted by TYPE from here on. A declared slot missing from
305
+ * `values` is a target contract violation, named loudly.
306
+ */
307
+ function hydrateSecrets(root, values) {
308
+ const boxed = {};
309
+ for (const slot of Object.keys(root.secretSlots)) {
310
+ const value = values[slot];
311
+ if (value === void 0) throw new Error(`secret slot "${slot}" has no resolved value — the target must resolve every declared secret before hydrateSecrets().`);
312
+ boxed[slot] = new SecretBox(value);
313
+ }
314
+ return blindCast(boxed);
315
+ }
294
316
  /** Bounded jittered backoff for retrying a dropped call. `maxRetries` is retries after the first attempt. */
295
317
  const RETRY = {
296
318
  initialDelayMs: 250,
@@ -11670,6 +11692,13 @@ function smtpAttempt(deliveryUrl, credential) {
11670
11692
  function createSmtpDelivery(opts) {
11671
11693
  return withRetryPolicy(smtpAttempt(opts.deliveryUrl, opts.credential), opts.retryPolicy);
11672
11694
  }
11695
+ const nodeBuild = (opts) => ({
11696
+ extension: "@prisma/composer/node",
11697
+ type: "node",
11698
+ module: opts.module,
11699
+ entry: opts.entry,
11700
+ ...opts.dir === void 0 ? {} : { dir: opts.dir }
11701
+ });
11673
11702
  const liftArray = (data) => Array.isArray(data) ? data : [data];
11674
11703
  /**
11675
11704
  * Splits an array into two arrays based on the result of a predicate
@@ -18789,28 +18818,6 @@ ark.schema;
18789
18818
  ark.define;
18790
18819
  ark.declare;
18791
18820
  /**
18792
- * ArkType spellings of the framework's schema leaves. Opt-in: the framework
18793
- * accepts any Standard Schema library (ADR-0042) and nothing outside this
18794
- * module imports arktype, so an app that uses Zod never loads it.
18795
- */
18796
- /**
18797
- * A schema leaf that accepts only a redacting `SecretString` box, so
18798
- * `input().<field>.expose()` type-checks and binding a plain literal there
18799
- * fails the deploy (ADR-0042).
18800
- *
18801
- * ```ts
18802
- * input: type({ signingKey: secretString() })
18803
- * ```
18804
- */
18805
- const secretString = () => type("unknown").narrow((value, ctx) => isSecretString(value) || ctx.mustBe("a SecretString box — bind this field to a secret"));
18806
- const nodeBuild = (opts) => ({
18807
- extension: "@prisma/composer/node",
18808
- type: "node",
18809
- module: opts.module,
18810
- entry: opts.entry,
18811
- ...opts.dir === void 0 ? {} : { dir: opts.dir }
18812
- });
18813
- /**
18814
18821
  * Walks a node's own params, then each dependency input's connection params —
18815
18822
  * the same enumeration order `configOf` uses, but carrying the raw
18816
18823
  * `ConfigParam` (with its `serialize`/`deserialize`) instead of a pure-data
@@ -18899,7 +18906,7 @@ function coerceEnvSourcedParam(raw, d, key) {
18899
18906
  /**
18900
18907
  * Boot: read each declared param from env by its key, reverse the param's own
18901
18908
  * serialization (missing/invalid fails loudly), assemble the typed Config.
18902
- * The input document rides its own channel (readInput), not this one.
18909
+ * Secrets ride a separate channel (deserializeSecrets), not this one.
18903
18910
  */
18904
18911
  const deserialize = (node, address) => {
18905
18912
  const service = {};
@@ -18936,84 +18943,40 @@ const stash = (node, config) => {
18936
18943
  process.env[configKey("", d)] = encode(d.owner, value);
18937
18944
  }
18938
18945
  };
18939
- /** The input document's own row name; its full key is COMPOSER_<addr>_INPUT. */
18940
- const INPUT_KEY_NAME = "INPUT";
18941
- /** The input document row's key for a service address. */
18942
- const inputKey = (address) => configKey(address, {
18946
+ /** The pointer-row key for a secret slot: COMPOSER_<addr>_<slot> (secrets are service-level). */
18947
+ const secretKey = (address, slot) => configKey(address, {
18943
18948
  owner: "service",
18944
- name: INPUT_KEY_NAME
18949
+ name: slot
18945
18950
  });
18946
- const SECRET_MARKER = "$secret";
18947
- const ESCAPED_KEY = /^\$\$+secret$/;
18948
- /** True for the framework's secret pointer: `{ "$secret": "<PLATFORM_VAR>" }` and nothing else. */
18949
- function isSecretPointer(value) {
18950
- return typeof value === "object" && value !== null && !Array.isArray(value) && Object.keys(value).length === 1 && SECRET_MARKER in value && typeof value[SECRET_MARKER] === "string";
18951
- }
18952
- /** A plain data object (not an array, not a class instance) — the only object shape a binding/document may nest. */
18953
- function isPlainObject(value) {
18954
- if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
18955
- const proto = Object.getPrototypeOf(value);
18956
- return proto === Object.prototype || proto === null;
18957
- }
18958
18951
  /**
18959
- * Boot: read the one input document row (address-free after run()'s
18960
- * re-stash), swap each `$secret` pointer for a redacting box over the named
18961
- * platform var, unescape reserved keys, validate with the service's own
18962
- * schema, and return the typed object. A missing row is a loud failure naming
18963
- * the env var — a deployed environment always writes it; a local harness must
18964
- * supply it (set the row to the serialized input document).
18952
+ * Boot: resolve every secret slot to its value by double-lookup read the
18953
+ * pointer key (the platform NAME), then read that platform var. A missing
18954
+ * pointer or a missing/empty platform value is a loud failure naming both keys.
18955
+ * Returns a plain Record for core's `hydrateSecrets` to box.
18965
18956
  */
18966
- function readInput(node, address) {
18967
- const schema = node.inputSchema;
18968
- if (schema === void 0) throw new Error(`input() called on service "${node.name}", which declares no input schema — declare \`input: <schema>\` on compute() to use it (ADR-0042).`);
18969
- const key = inputKey(address);
18970
- const raw = process.env[key];
18971
- if (raw === void 0 || raw === "") throw new Error(`this service's input is not available (env ${key} is unset) — a deployed environment writes it automatically; a local harness must supply it like any other config value (set ${key} to the serialized input document).`);
18972
- let parsed;
18973
- try {
18974
- parsed = JSON.parse(raw);
18975
- } catch (cause) {
18976
- const message = cause instanceof Error ? cause.message : String(cause);
18977
- throw new Error(`this service's input document is not valid JSON (env ${key}): ${message} — a deployed environment writes it automatically; a local harness must supply the serialized input document (set ${key} to it).`);
18978
- }
18979
- const hydrated = hydrateInputDocument(parsed, key);
18980
- try {
18981
- return standardValidateSync(schema, hydrated);
18982
- } catch (cause) {
18983
- const message = cause instanceof Error ? cause.message : String(cause);
18984
- throw new Error(`invalid input document (env ${key}): ${message}`);
18985
- }
18986
- }
18987
- /** Reverses the document encoding: `$secret` pointers become redacting boxes over the named platform vars; escaped reserved keys drop one "$". */
18988
- function hydrateInputDocument(value, key) {
18989
- if (isSecretPointer(value)) {
18990
- const name = value[SECRET_MARKER];
18991
- const secret = process.env[name];
18992
- if (secret === void 0 || secret === "") throw new Error(`secret input is not provisioned (env ${key} → ${name}): the platform var "${name}" is unset or empty.`);
18993
- return new SecretBox(secret);
18994
- }
18995
- if (Array.isArray(value)) return value.map((member) => hydrateInputDocument(member, key));
18996
- if (isPlainObject(value)) {
18997
- const out = {};
18998
- for (const [k, member] of Object.entries(value)) {
18999
- const read = ESCAPED_KEY.test(k) ? k.slice(1) : k;
19000
- out[read] = hydrateInputDocument(member, key);
19001
- }
19002
- return out;
19003
- }
19004
- return value;
19005
- }
18957
+ const deserializeSecrets = (node, address) => {
18958
+ const values = {};
18959
+ for (const slot of Object.keys(node.secretSlots)) {
18960
+ const key = secretKey(address, slot);
18961
+ const name = process.env[key];
18962
+ if (name === void 0 || name === "") throw new Error(`missing secret pointer for slot "${slot}" (env ${key}) — the deploy did not write it.`);
18963
+ const value = process.env[name];
18964
+ if (value === void 0 || value === "") throw new Error(`secret "${slot}" is not provisioned (env ${key} → ${name}): the platform var "${name}" is unset or empty.`);
18965
+ values[slot] = value;
18966
+ }
18967
+ return values;
18968
+ };
19006
18969
  /**
19007
- * run()'s setup step for the input document: re-emit the row under its
19008
- * address-free key, so the address-free `readInput` reads identically. The
19009
- * document carries pointers, never values — the values stay only in their
19010
- * platform vars.
18970
+ * run()'s setup step for secrets: re-emit each slot's pointer NAME under its
18971
+ * address-free key, so the address-free `deserializeSecrets` double-looks-up
18972
+ * identically. Never the value — the value stays only in the platform var.
19011
18973
  */
19012
- const stashInput = (node, address) => {
19013
- if (node.inputSchema === void 0) return;
19014
- const raw = process.env[inputKey(address)];
19015
- if (raw === void 0) return;
19016
- process.env[inputKey("")] = raw;
18974
+ const stashSecrets = (node, address) => {
18975
+ for (const slot of Object.keys(node.secretSlots)) {
18976
+ const name = process.env[secretKey(address, slot)];
18977
+ if (name === void 0) continue;
18978
+ process.env[secretKey("", slot)] = name;
18979
+ }
19017
18980
  };
19018
18981
  /**
19019
18982
  * Boot: for each reserved provider param, read its address-scoped row through
@@ -19194,24 +19157,20 @@ Object.freeze({
19194
19157
  });
19195
19158
  const reservedParams = { port: number$1({ default: 3e3 }) };
19196
19159
  /**
19197
- * A Prisma Compute service — declarations only (deps + one input schema +
19198
- * build + the ports it exposes), no descriptor. The reserved `port` param
19199
- * rides its own channel (ADR-0042 leaves it untouched); a dependency named
19200
- * like it fails at authoring.
19160
+ * A Prisma Compute service — declarations only (deps + params + build + the
19161
+ * ports it exposes), no descriptor. `params` merges with the reserved
19162
+ * `ReservedParams` (`port`); a user param whose name collides with a reserved
19163
+ * one fails at authoring, the same way a colliding dependency name does.
19201
19164
  *
19202
19165
  * · run(address, boot) — the process controller: deserialize the platform
19203
19166
  * environment (keyed off `address`, the extension's ONE env read) into a
19204
- * typed Config, re-emit it (and the input document row) under address-free
19205
- * process-local stash keys, then call boot() to start the app's entry.
19206
- * · load() / input() — called from inside the app's entry: read the stash;
19207
- * load() hydrates + memoizes the deps, input() parses the input document,
19208
- * replaces each `$secret` pointer with a redacting box over the named
19209
- * platform var, validates with the declared schema, and memoizes (ADR-0042).
19167
+ * typed Config, re-emit it under address-free process-local stash keys,
19168
+ * then call boot() to start the app's entry.
19169
+ * · load() / config() — called from inside the app's entry: read the stash;
19170
+ * load() hydrates + memoizes the deps, config() returns the typed params.
19171
+ * Separate accessors so a dep and a param never share a namespace (ADR-0021).
19210
19172
  * · origin() — this service's platform-assigned public origin, read from the
19211
19173
  * stash `run()` populates; memoized per process.
19212
- * · port() — this service's resolved reserved port (the value `run()` routes
19213
- * to and exports as PORT), read from that same stash via #processConfig();
19214
- * defaults to 3000 when the platform binds none.
19215
19174
  *
19216
19175
  * The underlying node carries `extension: '@prisma/composer-prisma-cloud'` —
19217
19176
  * the control-plane registry key `prisma-composer deploy` resolves through the
@@ -19221,7 +19180,8 @@ const reservedParams = { port: number$1({ default: 3e3 }) };
19221
19180
  var ComputeService = class {
19222
19181
  #resolved;
19223
19182
  #loadedDeps;
19224
- #loadedInput;
19183
+ #loadedParams;
19184
+ #loadedSecrets;
19225
19185
  #origin;
19226
19186
  constructor(node) {
19227
19187
  Object.assign(this, node);
@@ -19234,7 +19194,7 @@ var ComputeService = class {
19234
19194
  const config = deserialize(this, address);
19235
19195
  stash(this, config);
19236
19196
  stashProviderParams(RESERVED_PROVIDER_PARAMS, address);
19237
- stashInput(this, address);
19197
+ stashSecrets(this, address);
19238
19198
  const port = config.service["port"];
19239
19199
  if (typeof port === "number") process.env["PORT"] = String(port);
19240
19200
  return boot();
@@ -19243,9 +19203,13 @@ var ComputeService = class {
19243
19203
  if (this.#loadedDeps === void 0) this.#loadedDeps = blindCast(hydrateSync(this, this.#processConfig()));
19244
19204
  return this.#loadedDeps;
19245
19205
  }
19246
- input() {
19247
- if (this.#loadedInput === void 0) this.#loadedInput = blindCast(readInput(this, ""));
19248
- return this.#loadedInput;
19206
+ config() {
19207
+ if (this.#loadedParams === void 0) this.#loadedParams = blindCast(this.#processConfig().service);
19208
+ return this.#loadedParams;
19209
+ }
19210
+ secrets() {
19211
+ if (this.#loadedSecrets === void 0) this.#loadedSecrets = blindCast(hydrateSecrets(this, deserializeSecrets(this, "")));
19212
+ return this.#loadedSecrets;
19249
19213
  }
19250
19214
  /** This service's platform-assigned public origin — read from the stash
19251
19215
  * run() populates and memoized per process. Throws if called before run()
@@ -19254,24 +19218,26 @@ var ComputeService = class {
19254
19218
  this.#origin ??= readOrigin();
19255
19219
  return this.#origin;
19256
19220
  }
19257
- /** This service's resolved reserved port — the value run() routes to and
19258
- * exports as PORT — read the same way load() reads the stash. Defaults to
19259
- * 3000 when the platform binds none. */
19260
- port() {
19261
- const port = this.#processConfig().service["port"];
19262
- if (typeof port !== "number") throw new Error(`service "${this.name}" resolved a non-numeric port — the reserved port param is number({ default: 3000 }), so a stashed config always carries a number here.`);
19263
- return port;
19264
- }
19265
19221
  };
19266
19222
  const compute = (def) => {
19267
- for (const reserved of Object.keys(reservedParams)) if (reserved in def.deps) throw new Error(`compute(): dependency "${reserved}" collides with the reserved service param of the same name — rename the dependency.`);
19223
+ const userParams = def.params ?? blindCast({});
19224
+ for (const reserved of Object.keys(reservedParams)) {
19225
+ if (reserved in def.deps) throw new Error(`compute(): dependency "${reserved}" collides with the reserved service param of the same name — rename the dependency.`);
19226
+ if (reserved in userParams) throw new Error(`compute(): param "${reserved}" collides with the reserved service param of the same name — rename the param.`);
19227
+ }
19228
+ for (const name of Object.keys(userParams)) if (name.toUpperCase() === "ORIGIN") throw new Error(`compute(): param "${name}" collides with the framework-written origin row — rename the param.`);
19229
+ for (const name of Object.keys(def.secrets ?? {})) if (name.toUpperCase() === "ORIGIN") throw new Error(`compute(): secret "${name}" collides with the framework-written origin row — rename the secret.`);
19230
+ const params = blindCast({
19231
+ ...userParams,
19232
+ ...reservedParams
19233
+ });
19268
19234
  const instance = new ComputeService(service$1({
19269
19235
  name: def.name,
19270
19236
  extension: "@prisma/composer-prisma-cloud",
19271
19237
  type: "compute",
19272
19238
  inputs: def.deps,
19273
- params: reservedParams,
19274
- ...def.input !== void 0 ? { input: def.input } : {},
19239
+ params,
19240
+ ...def.secrets !== void 0 ? { secrets: def.secrets } : {},
19275
19241
  build: def.build,
19276
19242
  ...def.expose !== void 0 ? { expose: def.expose } : {}
19277
19243
  }));
@@ -19371,27 +19337,25 @@ const emailOutboxContract = contract({
19371
19337
  });
19372
19338
  /**
19373
19339
  * The email compute service (spec §"Service definition"): a Postgres `db`
19374
- * dependency, ONE input schema — `deliveryMode`/`deliveryUrl`/`from` plus the
19375
- * `deliveryCredential` secret leaf (a `SecretString`-typed field, ADR-0042)
19376
- * and the two exposed ports (`send`, `outbox`). The module (`email-module.ts`)
19377
- * binds the input at provision: forwarded boundary params/secret as leaves,
19378
- * `deliveryUrl` as a literal. Build/entry mechanics copied from storage's
19379
- * service file (`storage/src/storage-service.ts`), not re-derived:
19380
- * `build.module` points at this file's own built output so the deploy
19381
- * bootstrap can re-import it as `main` and call `main.run(address, boot)`;
19382
- * `entry` resolves the sibling entrypoint pass in the same dist directory.
19340
+ * dependency, the `deliveryMode`/`deliveryUrl`/`from` params, the
19341
+ * `deliveryCredential` secret, and the two exposed ports (`send`, `outbox`).
19342
+ * Build/entry mechanics copied from storage's service file
19343
+ * (`storage/src/storage-service.ts`), not re-derived: `build.module` points
19344
+ * at this file's own built output so the deploy bootstrap can re-import it
19345
+ * as `main` and call `main.run(address, boot)`; `entry` resolves the
19346
+ * sibling entrypoint pass in the same dist directory.
19383
19347
  */
19384
- const emailInputSchema = type({
19385
- deliveryMode: "'resend'|'smtp'|'none'",
19386
- deliveryUrl: "string",
19387
- from: "string",
19388
- deliveryCredential: secretString()
19389
- });
19390
- function emailService() {
19348
+ const deliveryModeSchema = type("'resend'|'smtp'|'none'");
19349
+ function emailService(opts) {
19391
19350
  return compute({
19392
19351
  name: "email",
19393
19352
  deps: { db: postgres() },
19394
- input: emailInputSchema,
19353
+ params: {
19354
+ deliveryMode: param(deliveryModeSchema),
19355
+ deliveryUrl: string$1({ default: opts?.deliveryUrl ?? "https://api.resend.com" }),
19356
+ from: string$1()
19357
+ },
19358
+ secrets: { deliveryCredential: secret() },
19395
19359
  expose: {
19396
19360
  send: emailSendContract,
19397
19361
  outbox: emailOutboxContract
@@ -19740,8 +19704,8 @@ function checkDeliveryUrl(deliveryMode, deliveryUrl) {
19740
19704
  }
19741
19705
  const service = emailService();
19742
19706
  const { db } = service.load();
19743
- const { deliveryMode, deliveryUrl, from, deliveryCredential } = service.input();
19744
- const port = service.port();
19707
+ const { deliveryMode, deliveryUrl, from, port } = service.config();
19708
+ const { deliveryCredential } = service.secrets();
19745
19709
  const deliveryUrlError = checkDeliveryUrl(deliveryMode, deliveryUrl);
19746
19710
  if (deliveryUrlError !== null) throw new Error(deliveryUrlError);
19747
19711
  const handlers = createHandlers({