@prisma/composer-prisma-cloud 0.1.0 → 0.2.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.
- package/dist/control.d.mts +52 -38
- package/dist/control.mjs +1050 -326
- package/dist/control.mjs.map +1 -1
- package/dist/cron/index.d.mts +9 -6
- package/dist/cron/index.mjs +114 -5
- package/dist/cron/index.mjs.map +1 -1
- package/dist/cron/scheduler-entrypoint.mjs +281 -111
- package/dist/cron/scheduler-entrypoint.mjs.map +1 -1
- package/dist/cron/scheduler-service.mjs +114 -5
- package/dist/cron/scheduler-service.mjs.map +1 -1
- package/dist/index.d.mts +118 -23
- package/dist/index.mjs +44 -2
- package/dist/index.mjs.map +1 -1
- package/dist/{prisma-next-COrwlg3N.mjs → prisma-next-DlU01kXJ-2sP5mQb6.mjs} +3 -3
- package/dist/prisma-next-DlU01kXJ-2sP5mQb6.mjs.map +1 -0
- package/dist/prisma-next.d.mts +1 -2
- package/dist/prisma-next.mjs +1 -1
- package/dist/provisioned-edges-DIQAR4q4-Bn9op-JG.mjs +151 -0
- package/dist/provisioned-edges-DIQAR4q4-Bn9op-JG.mjs.map +1 -0
- package/dist/{serializer-C2CsA7xm-29Eg2Tjl.mjs → serializer-CX4VYdf_-KKGoAxfx.mjs} +61 -7
- package/dist/serializer-CX4VYdf_-KKGoAxfx.mjs.map +1 -0
- package/dist/serializer-Cx5slrV4-xJfH6EWS.d.mts +36 -0
- package/dist/storage/index.d.mts +5 -16
- package/dist/storage/index.mjs +114 -3
- package/dist/storage/index.mjs.map +1 -1
- package/dist/storage/storage-entrypoint.mjs +7242 -12
- package/dist/storage/storage-entrypoint.mjs.map +1 -1
- package/dist/storage/storage-service.mjs +114 -3
- package/dist/storage/storage-service.mjs.map +1 -1
- package/dist/storage/testing.d.mts +7 -4
- package/dist/storage/testing.mjs.map +1 -1
- package/dist/streams/index.d.mts +250 -0
- package/dist/streams/index.mjs +4058 -0
- package/dist/streams/index.mjs.map +1 -0
- package/dist/streams/streams-entrypoint.mjs +48095 -0
- package/dist/streams/streams-entrypoint.mjs.map +1 -0
- package/dist/streams/streams-service.mjs +811 -0
- package/dist/streams/streams-service.mjs.map +1 -0
- package/dist/streams/testing.d.mts +33 -0
- package/dist/streams/testing.mjs +31335 -0
- package/dist/streams/testing.mjs.map +1 -0
- package/dist/testing.d.mts +1 -2
- package/dist/testing.mjs +1 -1
- package/dist/testing.mjs.map +1 -1
- package/package.json +27 -22
- package/dist/prisma-next-COrwlg3N.mjs.map +0 -1
- package/dist/serializer-C2CsA7xm-29Eg2Tjl.mjs.map +0 -1
|
@@ -42,6 +42,15 @@ function blindCast(input) {
|
|
|
42
42
|
* data objects. A node's `extension` + `type` form its deploy-time registry key (ADR-0017).
|
|
43
43
|
*/
|
|
44
44
|
const NODE = Symbol.for("prisma:node");
|
|
45
|
+
const PROVISION_NEED = blindCast(Symbol.for("prisma:provision-need"));
|
|
46
|
+
/** Builds an opaque provisioning need — the declaring package's own brand plus whatever payload its provisioner reads back. */
|
|
47
|
+
function provisionNeed(brand, payload) {
|
|
48
|
+
return blindCast(Object.freeze({
|
|
49
|
+
[PROVISION_NEED]: true,
|
|
50
|
+
brand,
|
|
51
|
+
payload
|
|
52
|
+
}));
|
|
53
|
+
}
|
|
45
54
|
function requireType(type, factory) {
|
|
46
55
|
if (typeof type !== "string" || type.length === 0) throw new Error(`${factory}() requires a non-empty node type.`);
|
|
47
56
|
}
|
|
@@ -87,7 +96,7 @@ function service$1(def) {
|
|
|
87
96
|
requireNoUnderscoreNames(Object.keys(def.inputs), "input", "service");
|
|
88
97
|
requireNoUnderscoreNames(Object.keys(def.params), "param", "service");
|
|
89
98
|
requireNoUnderscoreNames(Object.keys(def.secrets ?? {}), "secret", "service");
|
|
90
|
-
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 (
|
|
99
|
+
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.`);
|
|
91
100
|
return Object.freeze({
|
|
92
101
|
[NODE]: true,
|
|
93
102
|
kind: "service",
|
|
@@ -171,7 +180,8 @@ function withFacets(schema, opts) {
|
|
|
171
180
|
return {
|
|
172
181
|
schema,
|
|
173
182
|
...opts.optional !== void 0 ? { optional: opts.optional } : {},
|
|
174
|
-
...opts.default !== void 0 ? { default: opts.default } : {}
|
|
183
|
+
...opts.default !== void 0 ? { default: opts.default } : {},
|
|
184
|
+
...opts.provision !== void 0 ? { provision: opts.provision } : {}
|
|
175
185
|
};
|
|
176
186
|
}
|
|
177
187
|
/** A string-valued param. */
|
|
@@ -224,7 +234,8 @@ const nodeBuild = (opts) => ({
|
|
|
224
234
|
extension: "@prisma/composer/node",
|
|
225
235
|
type: "node",
|
|
226
236
|
module: opts.module,
|
|
227
|
-
entry: opts.entry
|
|
237
|
+
entry: opts.entry,
|
|
238
|
+
...opts.dir === void 0 ? {} : { dir: opts.dir }
|
|
228
239
|
});
|
|
229
240
|
/**
|
|
230
241
|
* Walks a node's own params, then each dependency input's connection params —
|
|
@@ -254,7 +265,7 @@ const configKey = (address, d) => {
|
|
|
254
265
|
const segments = address.split(".").filter((s) => s.length > 0);
|
|
255
266
|
const owner = d.owner === "service" ? [] : [d.owner.input];
|
|
256
267
|
return [
|
|
257
|
-
"
|
|
268
|
+
"COMPOSER",
|
|
258
269
|
...segments,
|
|
259
270
|
...owner,
|
|
260
271
|
d.name
|
|
@@ -273,12 +284,18 @@ function encode(owner, value) {
|
|
|
273
284
|
function decode(owner, raw) {
|
|
274
285
|
return owner === "service" ? JSON.parse(raw) : raw;
|
|
275
286
|
}
|
|
287
|
+
const PARAM_POINTER_PREFIX = "@composer-param-pointer:";
|
|
288
|
+
/** True iff `raw` is a param pointer row (as opposed to a JSON-encoded literal). */
|
|
289
|
+
const isParamPointerRow = (raw) => raw.startsWith(PARAM_POINTER_PREFIX);
|
|
290
|
+
/** Reverses `encodeParamPointer`: the platform var NAME a pointer row points to. */
|
|
291
|
+
const decodeParamPointer = (raw) => raw.slice(24);
|
|
276
292
|
function coerce(raw, d, key) {
|
|
277
293
|
if (!(raw !== void 0 && raw !== "")) {
|
|
278
294
|
if (d.param.default !== void 0) return d.param.default;
|
|
279
295
|
if (d.param.optional === true) return void 0;
|
|
280
296
|
throw new Error(`missing required config param "${d.name}" (env ${key})`);
|
|
281
297
|
}
|
|
298
|
+
if (d.owner === "service" && isParamPointerRow(raw)) return coerceEnvSourcedParam(raw, d, key);
|
|
282
299
|
try {
|
|
283
300
|
return standardValidateSync(d.param.schema, decode(d.owner, raw));
|
|
284
301
|
} catch (cause) {
|
|
@@ -287,6 +304,26 @@ function coerce(raw, d, key) {
|
|
|
287
304
|
}
|
|
288
305
|
}
|
|
289
306
|
/**
|
|
307
|
+
* Boot resolution for an env-sourced param: double-lookup (pointer → platform
|
|
308
|
+
* var), then the param's own schema on the raw string — no JSON decode, and
|
|
309
|
+
* no redaction (it's config, not a secret). An UNSET platform var is a loud
|
|
310
|
+
* boot failure naming both the param and the platform var; an EMPTY string is
|
|
311
|
+
* not special-cased here — it reaches the schema like any other value, so it
|
|
312
|
+
* passes iff the schema accepts it (deliberately unlike a literal param's own
|
|
313
|
+
* ""-means-absent rule, and unlike a secret's non-empty requirement).
|
|
314
|
+
*/
|
|
315
|
+
function coerceEnvSourcedParam(raw, d, key) {
|
|
316
|
+
const platformVar = decodeParamPointer(raw);
|
|
317
|
+
const value = process.env[platformVar];
|
|
318
|
+
if (value === void 0) throw new Error(`env-sourced config param "${d.name}" (env ${key} → ${platformVar}) is unset: the platform variable "${platformVar}" was not injected — the deploy did not provision it.`);
|
|
319
|
+
try {
|
|
320
|
+
return standardValidateSync(d.param.schema, value);
|
|
321
|
+
} catch (cause) {
|
|
322
|
+
const message = cause instanceof Error ? cause.message : String(cause);
|
|
323
|
+
throw new Error(`invalid value for env-sourced config param "${d.name}" (env ${key} → ${platformVar}): ${message}`);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
290
327
|
* Boot: read each declared param from env by its key, reverse the param's own
|
|
291
328
|
* serialization (missing/invalid fails loudly), assemble the typed Config.
|
|
292
329
|
* Secrets ride a separate channel (deserializeSecrets), not this one.
|
|
@@ -326,7 +363,7 @@ const stash = (node, config) => {
|
|
|
326
363
|
process.env[configKey("", d)] = encode(d.owner, value);
|
|
327
364
|
}
|
|
328
365
|
};
|
|
329
|
-
/** The pointer-row key for a secret slot:
|
|
366
|
+
/** The pointer-row key for a secret slot: COMPOSER_<addr>_<slot> (secrets are service-level). */
|
|
330
367
|
const secretKey = (address, slot) => configKey(address, {
|
|
331
368
|
owner: "service",
|
|
332
369
|
name: slot
|
|
@@ -361,6 +398,32 @@ const stashSecrets = (node, address) => {
|
|
|
361
398
|
process.env[secretKey("", slot)] = name;
|
|
362
399
|
}
|
|
363
400
|
};
|
|
401
|
+
/**
|
|
402
|
+
* Boot: for each reserved provider param, read its address-scoped row through
|
|
403
|
+
* the same `coerce` a declared param uses (JSON-decode, schema-validate), and
|
|
404
|
+
* re-emit it address-free — `stash`'s counterpart for this separate
|
|
405
|
+
* declaration space. A param is declared optional here unconditionally: an
|
|
406
|
+
* absent row means "never provisioned" (local dev, tests, a provider with no
|
|
407
|
+
* registered value for this deploy), never a boot failure, so nothing is
|
|
408
|
+
* stashed and the runtime reader that owns this slot falls back to its own
|
|
409
|
+
* pass-through behavior.
|
|
410
|
+
*/
|
|
411
|
+
function stashProviderParams(entries, address) {
|
|
412
|
+
for (const entry of entries) {
|
|
413
|
+
const d = {
|
|
414
|
+
owner: "service",
|
|
415
|
+
name: entry.name,
|
|
416
|
+
param: {
|
|
417
|
+
schema: entry.schema,
|
|
418
|
+
optional: true
|
|
419
|
+
}
|
|
420
|
+
};
|
|
421
|
+
const key = configKey(address, d);
|
|
422
|
+
const value = coerce(process.env[key], d, key);
|
|
423
|
+
if (value === void 0) continue;
|
|
424
|
+
process.env[configKey("", d)] = encode("service", value);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
364
427
|
/** Synchronous Standard Schema validation — see the matching note in core's `config.ts`. */
|
|
365
428
|
function standardValidateSync(schema, value) {
|
|
366
429
|
const result = schema["~standard"].validate(value);
|
|
@@ -368,101 +431,20 @@ function standardValidateSync(schema, value) {
|
|
|
368
431
|
if (result.issues !== void 0) throw new Error(`config param validation failed: ${result.issues.map((issue) => issue.message).join("; ")}`);
|
|
369
432
|
return result.value;
|
|
370
433
|
}
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
* then call boot() to start the app's entry.
|
|
382
|
-
* · load() / config() — called from inside the app's entry: read the stash;
|
|
383
|
-
* load() hydrates + memoizes the deps, config() returns the typed params.
|
|
384
|
-
* Separate accessors so a dep and a param never share a namespace (ADR-0021).
|
|
385
|
-
*
|
|
386
|
-
* `service()`'s underlying node carries `extension: '@prisma/composer-prisma-cloud'` —
|
|
387
|
-
* the control-plane registry key `prisma-composer deploy` resolves through the
|
|
388
|
-
* app's `prisma-composer.config.ts` (ADR-0017). This module loads nothing at
|
|
389
|
-
* deploy time; nodes are pure data.
|
|
390
|
-
*/
|
|
391
|
-
const compute = (def) => {
|
|
392
|
-
const userParams = def.params ?? blindCast({});
|
|
393
|
-
for (const reserved of Object.keys(reservedParams)) {
|
|
394
|
-
if (reserved in def.deps) throw new Error(`compute(): dependency "${reserved}" collides with the reserved service param of the same name — rename the dependency.`);
|
|
395
|
-
if (reserved in userParams) throw new Error(`compute(): param "${reserved}" collides with the reserved service param of the same name — rename the param.`);
|
|
396
|
-
}
|
|
397
|
-
const params = blindCast({
|
|
398
|
-
...userParams,
|
|
399
|
-
...reservedParams
|
|
400
|
-
});
|
|
401
|
-
const node = service$1({
|
|
402
|
-
name: def.name,
|
|
403
|
-
extension: "@prisma/composer-prisma-cloud",
|
|
404
|
-
type: "compute",
|
|
405
|
-
inputs: def.deps,
|
|
406
|
-
params,
|
|
407
|
-
...def.secrets !== void 0 ? { secrets: def.secrets } : {},
|
|
408
|
-
build: def.build,
|
|
409
|
-
...def.expose !== void 0 ? { expose: def.expose } : {}
|
|
410
|
-
});
|
|
411
|
-
let resolved;
|
|
412
|
-
let loadedDeps;
|
|
413
|
-
let loadedParams;
|
|
414
|
-
let loadedSecrets;
|
|
415
|
-
function processConfig() {
|
|
416
|
-
if (resolved === void 0) resolved = deserialize(node, "");
|
|
417
|
-
return resolved;
|
|
418
|
-
}
|
|
419
|
-
const runnable = {
|
|
420
|
-
...node,
|
|
421
|
-
async run(address, boot) {
|
|
422
|
-
const config = deserialize(node, address);
|
|
423
|
-
stash(node, config);
|
|
424
|
-
stashSecrets(node, address);
|
|
425
|
-
const port = config.service["port"];
|
|
426
|
-
if (typeof port === "number") process.env["PORT"] = String(port);
|
|
427
|
-
return boot();
|
|
428
|
-
},
|
|
429
|
-
load() {
|
|
430
|
-
if (loadedDeps === void 0) loadedDeps = blindCast(hydrateSync(node, processConfig()));
|
|
431
|
-
return loadedDeps;
|
|
432
|
-
},
|
|
433
|
-
config() {
|
|
434
|
-
if (loadedParams === void 0) loadedParams = blindCast(processConfig().service);
|
|
435
|
-
return loadedParams;
|
|
436
|
-
},
|
|
437
|
-
secrets() {
|
|
438
|
-
if (loadedSecrets === void 0) loadedSecrets = blindCast(hydrateSecrets(node, deserializeSecrets(node, "")));
|
|
439
|
-
return loadedSecrets;
|
|
440
|
-
}
|
|
441
|
-
};
|
|
442
|
-
return Object.freeze(blindCast(runnable));
|
|
443
|
-
};
|
|
444
|
-
Object.freeze({
|
|
445
|
-
kind: "postgres",
|
|
446
|
-
__cmp: { url: "" },
|
|
447
|
-
satisfies: (required) => required.kind === "postgres"
|
|
448
|
-
});
|
|
449
|
-
Object.freeze({
|
|
450
|
-
kind: "credentials",
|
|
451
|
-
__cmp: {
|
|
452
|
-
accessKeyId: "",
|
|
453
|
-
secretAccessKey: ""
|
|
454
|
-
},
|
|
455
|
-
satisfies: (required) => required.kind === "credentials"
|
|
456
|
-
});
|
|
457
|
-
async function standardValidate(schema, value) {
|
|
458
|
-
const result = await schema["~standard"].validate(value);
|
|
459
|
-
if (result.issues !== void 0) throw new Error(`Schema validation failed: ${result.issues.map((issue) => issue.message).join("; ")}`);
|
|
460
|
-
return result.value;
|
|
434
|
+
/** Bounded jittered backoff for retrying a dropped call. `maxRetries` is retries after the first attempt. */
|
|
435
|
+
const RETRY = {
|
|
436
|
+
initialDelayMs: 250,
|
|
437
|
+
multiplier: 2,
|
|
438
|
+
maxDelayMs: 5e3,
|
|
439
|
+
maxRetries: 5
|
|
440
|
+
};
|
|
441
|
+
const IDEMPOTENCY_KEY_HEADER$1 = "Idempotency-Key";
|
|
442
|
+
function sleep(ms) {
|
|
443
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
461
444
|
}
|
|
462
|
-
/**
|
|
463
|
-
function
|
|
464
|
-
|
|
465
|
-
return new URL(`rpc/${method}`, normalizedBase).toString();
|
|
445
|
+
/** Whether a non-OK response is safe to retry: 429 or any 5xx, never another 4xx. */
|
|
446
|
+
function isRetryableStatus(status) {
|
|
447
|
+
return status === 429 || status >= 500;
|
|
466
448
|
}
|
|
467
449
|
/** The server's `{ error }` body, if the response has one — undefined otherwise. */
|
|
468
450
|
async function errorDetail(res) {
|
|
@@ -473,20 +455,56 @@ async function errorDetail(res) {
|
|
|
473
455
|
return;
|
|
474
456
|
}
|
|
475
457
|
}
|
|
458
|
+
/** `<base>/rpc/<method>`, preserving a base URL's own path (e.g. a mount point). */
|
|
459
|
+
function methodUrl(base, method) {
|
|
460
|
+
const normalizedBase = base.endsWith("/") ? base : `${base}/`;
|
|
461
|
+
return new URL(`rpc/${method}`, normalizedBase).toString();
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Sends one call over `send`, retrying a thrown error, 429, or 5xx with
|
|
465
|
+
* full-jitter backoff. `buildRequest` runs per attempt but carries the same
|
|
466
|
+
* idempotency key each time — only the transport call repeats, not the key.
|
|
467
|
+
*/
|
|
468
|
+
async function callWithRetry(send, buildRequest, method) {
|
|
469
|
+
let delay = RETRY.initialDelayMs;
|
|
470
|
+
let retries = 0;
|
|
471
|
+
for (;;) {
|
|
472
|
+
let res;
|
|
473
|
+
try {
|
|
474
|
+
res = await send(buildRequest());
|
|
475
|
+
} catch (err) {
|
|
476
|
+
if (retries >= RETRY.maxRetries) throw err;
|
|
477
|
+
retries += 1;
|
|
478
|
+
await sleep(Math.random() * delay);
|
|
479
|
+
delay = Math.min(delay * RETRY.multiplier, RETRY.maxDelayMs);
|
|
480
|
+
continue;
|
|
481
|
+
}
|
|
482
|
+
if (res.ok) return res.json();
|
|
483
|
+
if (!isRetryableStatus(res.status) || retries >= RETRY.maxRetries) {
|
|
484
|
+
const detail = await errorDetail(res);
|
|
485
|
+
throw new Error(`RPC call "${method}" failed: ${res.status} ${res.statusText}` + (detail !== void 0 ? ` — ${detail}` : ""));
|
|
486
|
+
}
|
|
487
|
+
retries += 1;
|
|
488
|
+
await sleep(Math.random() * delay);
|
|
489
|
+
delay = Math.min(delay * RETRY.multiplier, RETRY.maxDelayMs);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
476
492
|
function makeClient(contract, url, opts) {
|
|
477
493
|
const send = opts?.fetch ?? fetch;
|
|
494
|
+
const baseHeaders = { "content-type": "application/json" };
|
|
495
|
+
if (opts?.serviceKey !== void 0) baseHeaders["Authorization"] = `Bearer ${opts.serviceKey}`;
|
|
478
496
|
const client = {};
|
|
479
|
-
for (const
|
|
480
|
-
const
|
|
497
|
+
for (const method of Object.keys(contract.__cmp)) client[method] = async (input) => {
|
|
498
|
+
const idempotencyKey = crypto.randomUUID();
|
|
499
|
+
const body = JSON.stringify(input);
|
|
500
|
+
return callWithRetry(send, () => new Request(methodUrl(url, method), {
|
|
481
501
|
method: "POST",
|
|
482
|
-
headers: {
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
}
|
|
489
|
-
return standardValidate(schemas.output, await res.json());
|
|
502
|
+
headers: {
|
|
503
|
+
...baseHeaders,
|
|
504
|
+
[IDEMPOTENCY_KEY_HEADER$1]: idempotencyKey
|
|
505
|
+
},
|
|
506
|
+
body
|
|
507
|
+
}), method);
|
|
490
508
|
};
|
|
491
509
|
return blindCast(client);
|
|
492
510
|
}
|
|
@@ -498,13 +516,23 @@ function contract(fns) {
|
|
|
498
516
|
};
|
|
499
517
|
return Object.freeze(value);
|
|
500
518
|
}
|
|
519
|
+
/** ADR-0031's need brand for RPC's per-binding service key — the target registers a provisioner under this. */
|
|
520
|
+
const RPC_PEER_KEY = Symbol.for("prisma:rpc/per-binding-key");
|
|
521
|
+
/** The provisioning need `rpc()`'s `serviceKey` param declares (ADR-0030): a shared, unguessable value the target mints per consumer edge. */
|
|
522
|
+
const perBindingToken = () => provisionNeed(RPC_PEER_KEY);
|
|
501
523
|
function rpc(arg) {
|
|
502
524
|
if (!isRpcContract(arg)) return arg;
|
|
503
525
|
return dependency({
|
|
504
526
|
type: "rpc",
|
|
505
527
|
connection: {
|
|
506
|
-
params: {
|
|
507
|
-
|
|
528
|
+
params: {
|
|
529
|
+
url: string$1(),
|
|
530
|
+
serviceKey: string$1({
|
|
531
|
+
optional: true,
|
|
532
|
+
provision: perBindingToken()
|
|
533
|
+
})
|
|
534
|
+
},
|
|
535
|
+
hydrate: ({ url, serviceKey }) => makeClient(arg, url, { serviceKey })
|
|
508
536
|
},
|
|
509
537
|
required: arg
|
|
510
538
|
});
|
|
@@ -7631,6 +7659,148 @@ ark.schema;
|
|
|
7631
7659
|
ark.define;
|
|
7632
7660
|
ark.declare;
|
|
7633
7661
|
/**
|
|
7662
|
+
* RPC's reserved provider param (ADR-0030/ADR-0031): the declaration —
|
|
7663
|
+
* name + schema + brand — for the accepted-keys set a provider stores, shared
|
|
7664
|
+
* by `control.ts` (which registers the deploy-side `value(refs)` that mints
|
|
7665
|
+
* and aggregates it — see its `rpcAcceptedKeysValue`) and `compute.ts` (which
|
|
7666
|
+
* validates and stashes it at boot), so writer and reader cannot drift.
|
|
7667
|
+
* Finding the edges themselves is `provisioned-edges.ts`'s generic,
|
|
7668
|
+
* brand-blind scan — RPC is not special-cased anywhere in this target.
|
|
7669
|
+
*
|
|
7670
|
+
* This module is reachable from the RUNTIME/authoring side — it must never
|
|
7671
|
+
* import `@internal/lowering` or `effect`, or those tokens leak into a user
|
|
7672
|
+
* service's bundle (the deploy-side `value(refs)` lives in control.ts, the
|
|
7673
|
+
* control-plane-only entry).
|
|
7674
|
+
*/
|
|
7675
|
+
/**
|
|
7676
|
+
* The reserved provider param for RPC's accepted-keys set: the var name is
|
|
7677
|
+
* `RPC_ACCEPTED_KEYS`, derived through `configKey` at both ends
|
|
7678
|
+
* (`configKey(address, …)` at deploy, `configKey('', …)` at boot — the
|
|
7679
|
+
* address-free form is `@internal/service-rpc`'s `RPC_ACCEPTED_KEYS_ENV`). `brand` is
|
|
7680
|
+
* `RPC_PEER_KEY`, the same brand `perBindingToken()`'s need carries — control.ts
|
|
7681
|
+
* looks its `value(refs)` up by this field.
|
|
7682
|
+
*/
|
|
7683
|
+
const RPC_ACCEPTED_KEYS_PARAM = {
|
|
7684
|
+
name: "RPC_ACCEPTED_KEYS",
|
|
7685
|
+
schema: type("string[]"),
|
|
7686
|
+
brand: RPC_PEER_KEY
|
|
7687
|
+
};
|
|
7688
|
+
/** ADR-0031's need brand for the streams module's bearer key — control.ts registers the provisioner under this. */
|
|
7689
|
+
const STREAMS_API_KEY = Symbol.for("prisma:streams/api-key");
|
|
7690
|
+
/**
|
|
7691
|
+
* The reserved provider param for the streams bearer key: the var name is
|
|
7692
|
+
* `STREAMS_API_KEY`. `brand` is `STREAMS_API_KEY` itself (the same symbol
|
|
7693
|
+
* `streamsApiKeyNeed()`'s need carries) — control.ts looks its `value(refs)`
|
|
7694
|
+
* up by this field.
|
|
7695
|
+
*/
|
|
7696
|
+
const STREAMS_API_KEY_PARAM = {
|
|
7697
|
+
name: "STREAMS_API_KEY",
|
|
7698
|
+
schema: type("string"),
|
|
7699
|
+
brand: STREAMS_API_KEY
|
|
7700
|
+
};
|
|
7701
|
+
configKey("", {
|
|
7702
|
+
owner: "service",
|
|
7703
|
+
name: STREAMS_API_KEY_PARAM.name
|
|
7704
|
+
});
|
|
7705
|
+
const RESERVED_PROVIDER_PARAMS = [RPC_ACCEPTED_KEYS_PARAM, STREAMS_API_KEY_PARAM];
|
|
7706
|
+
Object.freeze({
|
|
7707
|
+
kind: "s3",
|
|
7708
|
+
__cmp: {
|
|
7709
|
+
url: "",
|
|
7710
|
+
bucket: "",
|
|
7711
|
+
accessKeyId: "",
|
|
7712
|
+
secretAccessKey: ""
|
|
7713
|
+
},
|
|
7714
|
+
satisfies: (required) => required.kind === "s3"
|
|
7715
|
+
});
|
|
7716
|
+
const reservedParams = { port: number$1({ default: 3e3 }) };
|
|
7717
|
+
/**
|
|
7718
|
+
* A Prisma Compute service — declarations only (deps + params + build + the
|
|
7719
|
+
* ports it exposes), no descriptor. `params` merges with the reserved
|
|
7720
|
+
* `ReservedParams` (`port`); a user param whose name collides with a reserved
|
|
7721
|
+
* one fails at authoring, the same way a colliding dependency name does.
|
|
7722
|
+
* Returns the extension's runnable/loadable node:
|
|
7723
|
+
* · run(address, boot) — the process controller: deserialize the platform
|
|
7724
|
+
* environment (keyed off `address`, the extension's ONE env read) into a
|
|
7725
|
+
* typed Config, re-emit it under address-free process-local stash keys,
|
|
7726
|
+
* then call boot() to start the app's entry.
|
|
7727
|
+
* · load() / config() — called from inside the app's entry: read the stash;
|
|
7728
|
+
* load() hydrates + memoizes the deps, config() returns the typed params.
|
|
7729
|
+
* Separate accessors so a dep and a param never share a namespace (ADR-0021).
|
|
7730
|
+
*
|
|
7731
|
+
* `service()`'s underlying node carries `extension: '@prisma/composer-prisma-cloud'` —
|
|
7732
|
+
* the control-plane registry key `prisma-composer deploy` resolves through the
|
|
7733
|
+
* app's `prisma-composer.config.ts` (ADR-0017). This module loads nothing at
|
|
7734
|
+
* deploy time; nodes are pure data.
|
|
7735
|
+
*/
|
|
7736
|
+
const compute = (def) => {
|
|
7737
|
+
const userParams = def.params ?? blindCast({});
|
|
7738
|
+
for (const reserved of Object.keys(reservedParams)) {
|
|
7739
|
+
if (reserved in def.deps) throw new Error(`compute(): dependency "${reserved}" collides with the reserved service param of the same name — rename the dependency.`);
|
|
7740
|
+
if (reserved in userParams) throw new Error(`compute(): param "${reserved}" collides with the reserved service param of the same name — rename the param.`);
|
|
7741
|
+
}
|
|
7742
|
+
const params = blindCast({
|
|
7743
|
+
...userParams,
|
|
7744
|
+
...reservedParams
|
|
7745
|
+
});
|
|
7746
|
+
const node = service$1({
|
|
7747
|
+
name: def.name,
|
|
7748
|
+
extension: "@prisma/composer-prisma-cloud",
|
|
7749
|
+
type: "compute",
|
|
7750
|
+
inputs: def.deps,
|
|
7751
|
+
params,
|
|
7752
|
+
...def.secrets !== void 0 ? { secrets: def.secrets } : {},
|
|
7753
|
+
build: def.build,
|
|
7754
|
+
...def.expose !== void 0 ? { expose: def.expose } : {}
|
|
7755
|
+
});
|
|
7756
|
+
let resolved;
|
|
7757
|
+
let loadedDeps;
|
|
7758
|
+
let loadedParams;
|
|
7759
|
+
let loadedSecrets;
|
|
7760
|
+
function processConfig() {
|
|
7761
|
+
if (resolved === void 0) resolved = deserialize(node, "");
|
|
7762
|
+
return resolved;
|
|
7763
|
+
}
|
|
7764
|
+
const runnable = {
|
|
7765
|
+
...node,
|
|
7766
|
+
async run(address, boot) {
|
|
7767
|
+
const config = deserialize(node, address);
|
|
7768
|
+
stash(node, config);
|
|
7769
|
+
stashProviderParams(RESERVED_PROVIDER_PARAMS, address);
|
|
7770
|
+
stashSecrets(node, address);
|
|
7771
|
+
const port = config.service["port"];
|
|
7772
|
+
if (typeof port === "number") process.env["PORT"] = String(port);
|
|
7773
|
+
return boot();
|
|
7774
|
+
},
|
|
7775
|
+
load() {
|
|
7776
|
+
if (loadedDeps === void 0) loadedDeps = blindCast(hydrateSync(node, processConfig()));
|
|
7777
|
+
return loadedDeps;
|
|
7778
|
+
},
|
|
7779
|
+
config() {
|
|
7780
|
+
if (loadedParams === void 0) loadedParams = blindCast(processConfig().service);
|
|
7781
|
+
return loadedParams;
|
|
7782
|
+
},
|
|
7783
|
+
secrets() {
|
|
7784
|
+
if (loadedSecrets === void 0) loadedSecrets = blindCast(hydrateSecrets(node, deserializeSecrets(node, "")));
|
|
7785
|
+
return loadedSecrets;
|
|
7786
|
+
}
|
|
7787
|
+
};
|
|
7788
|
+
return Object.freeze(blindCast(runnable));
|
|
7789
|
+
};
|
|
7790
|
+
Object.freeze({
|
|
7791
|
+
kind: "postgres",
|
|
7792
|
+
__cmp: { url: "" },
|
|
7793
|
+
satisfies: (required) => required.kind === "postgres"
|
|
7794
|
+
});
|
|
7795
|
+
Object.freeze({
|
|
7796
|
+
kind: "credentials",
|
|
7797
|
+
__cmp: {
|
|
7798
|
+
accessKeyId: "",
|
|
7799
|
+
secretAccessKey: ""
|
|
7800
|
+
},
|
|
7801
|
+
satisfies: (required) => required.kind === "credentials"
|
|
7802
|
+
});
|
|
7803
|
+
/**
|
|
7634
7804
|
* The one call edge between the scheduler and the app's runner: `trigger(jobId)`.
|
|
7635
7805
|
* The scheduler depends on it (`rpc(triggerContract)`); the runner exposes it
|
|
7636
7806
|
* (`expose: { trigger: triggerContract }`). `jobId` travels as data through this
|