@void/platform 0.26.0 → 0.26.1

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/README.md CHANGED
@@ -1,12 +1,32 @@
1
1
  # @void/platform
2
2
 
3
- Runtime artifacts and contracts used by the Void CLI to install a team platform in a Cloudflare account.
3
+ Packaged runtime for [Void Platform](https://void.cloud/guide/self-hosted-platform), a shared deployment environment for your team or customers in your Cloudflare account. The Void CLI uses this package to install and maintain the platform.
4
4
 
5
- Install the `void` package and follow [Install a Void Platform](https://void.cloud/guide/self-hosted-platform). Developers connect to the platform with `void connect <url>` and deploy through the same CLI.
5
+ Your users can build and deploy apps without setting up separate Cloudflare accounts. Administrators control access, set resource limits, and inspect deployments and usage.
6
+
7
+ ## Getting Started
8
+
9
+ Install the `void` CLI and start the guided platform setup:
10
+
11
+ ```sh
12
+ npm install -D void
13
+ npx void platform install
14
+ ```
15
+
16
+ Once the platform is installed, developers connect and deploy:
17
+
18
+ ```sh
19
+ npx void connect https://api.example.com
20
+ npx void deploy
21
+ ```
22
+
23
+ The platform is open source. Use it as provided, contribute improvements, or fork it to integrate your systems and customize deployment workflows.
6
24
 
7
25
  ## Documentation
8
26
 
9
- - [Installation](https://void.cloud/guide/self-hosted-platform)
27
+ - [Installation and prerequisites](https://void.cloud/guide/self-hosted-platform)
10
28
  - [Administration](https://void.cloud/guide/platform-administration)
11
29
  - [Maintenance and recovery](https://void.cloud/guide/platform/installation/maintenance)
12
30
  - [Platform development](https://void.cloud/guide/platform-development)
31
+
32
+ Read the [Void documentation](https://void.cloud) online or use the Markdown guides and references in `node_modules/void/docs/`, included with the `void` package.
@@ -0,0 +1,19 @@
1
+ //#region src/browser-page.d.ts
2
+ /** Shared, dependency-free presentation for browser onboarding and authentication pages. */
3
+ export declare const browserPageLogo: string;
4
+ export declare const browserPageStyles: string;
5
+ export declare function escapeBrowserHtml(value: string): string;
6
+ export declare function browserPageIdentity(identity: {
7
+ login: string;
8
+ issuer: string;
9
+ subject: string;
10
+ }): string;
11
+ export declare function browserPageStatus(status: "success" | "error"): string;
12
+ /** content is trusted markup; escape every dynamic value before composing it. */
13
+ export declare function renderBrowserPage({ title, platformName, content }: {
14
+ title: string;
15
+ platformName?: string;
16
+ content: string;
17
+ }): string;
18
+ export declare function browserPageLead(title: string, description: string): string;
19
+ //#endregion
@@ -0,0 +1,90 @@
1
+ //#region src/browser-page.ts
2
+ /** Shared, dependency-free presentation for browser onboarding and authentication pages. */
3
+ const browserPageLogo = "<svg role=\"img\" aria-label=\"Void\" viewBox=\"0 0 270 58\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n<circle cx=\"262\" cy=\"48.96\" r=\"8\" fill=\"currentColor\"/>\n<path d=\"M20.72 0.959999L35.52 41.28L50.08 0.959999H70L47.92 56.96H22.32L0.24 0.959999H20.72ZM106.542 57.92C82.9419 57.92 70.6219 48 70.6219 28.96C70.6219 9.92 82.9419 1.90735e-06 106.542 1.90735e-06C130.142 1.90735e-06 142.542 9.92 142.542 28.96C142.542 48 130.142 57.92 106.542 57.92ZM106.542 44.08C117.662 44.08 122.862 39.36 122.862 28.96C122.862 18.48 117.662 13.76 106.542 13.76C95.4219 13.76 90.2219 18.48 90.2219 28.96C90.2219 39.36 95.4219 44.08 106.542 44.08ZM152.265 56.96V0.959999H171.465V56.96H152.265ZM184.687 56.96V0.959999H219.887C239.327 0.959999 250.447 9.84 250.447 28.96C250.447 48.16 239.247 56.96 219.807 56.96H184.687ZM230.767 28.96C230.767 18.48 226.287 14.56 217.167 14.56H203.887V43.36H217.167C226.287 43.36 230.767 39.44 230.767 28.96Z\" fill=\"currentColor\"/>\n</svg>";
4
+ const browserPageStyles = `
5
+ :root { color-scheme: light dark; }
6
+ html[data-theme="light"] { color-scheme: light; }
7
+ html[data-theme="dark"] { color-scheme: dark; }
8
+ .void-auth-page {
9
+ --auth-radius-inner: .375rem; --auth-radius-element: .625rem; --auth-radius-container: .75rem;
10
+ --auth-body: light-dark(#f1f1f1, #1b1b1b); --auth-card: light-dark(#fff, #1b1b1b);
11
+ --auth-text: light-dark(#000, #fff); --auth-secondary: light-dark(#474747, #9e9e9e);
12
+ --auth-border: light-dark(#0000000f, #ffffff1a); --auth-accent: light-dark(#1b1b1b, #f1f1f1); --auth-on-accent: light-dark(#fff, #1b1b1b);
13
+ --auth-success: light-dark(#176e45, #79dba9); --auth-success-bg: light-dark(#e7f4ec, #173a29);
14
+ --auth-error: light-dark(#b42332, #ff9aab); --auth-error-bg: light-dark(#fcebed, #421d26);
15
+ box-sizing: border-box; min-height: 100svh; margin: 0;
16
+ padding: clamp(40px, 10vh, 100px) 16px 48px; background: var(--auth-body); color: var(--auth-text);
17
+ font: 14px/1.55 Figtree, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
18
+ }
19
+ @supports (color: AccentColor) { .void-auth-page { --auth-accent: AccentColor; --auth-on-accent: AccentColorText; } }
20
+ /* Match the neutral administrator dashboard's radii, including its rounded fallback. */
21
+ @supports (corner-shape: squircle) {
22
+ .void-auth-page { --auth-radius-inner: .75rem; --auth-radius-element: 1.25rem; --auth-radius-container: 1.5rem; }
23
+ .void-auth-page, .void-auth-page *, .void-auth-page *::before, .void-auth-page *::after { corner-shape: squircle; }
24
+ .void-auth-page .void-auth-status { corner-shape: round; }
25
+ }
26
+ .void-auth-page *, .void-auth-page *::before, .void-auth-page *::after { box-sizing: border-box; }
27
+ .void-auth-page .void-auth-container { width: 100%; max-width: 448px; margin: 0 auto; }
28
+ .void-auth-page .void-auth-brand { display: flex; align-items: center; gap: 12px; margin: 0 4px 24px; color: var(--auth-secondary); overflow-wrap: anywhere; }
29
+ .void-auth-page .void-auth-logo { display: flex; flex: 0 0 70px; color: var(--auth-text); }
30
+ .void-auth-page .void-auth-logo svg { display: block; width: 70px; height: auto; }
31
+ .void-auth-page .void-auth-divider { width: 1px; height: 18px; background: var(--auth-border); flex-shrink: 0; }
32
+ .void-auth-page .void-auth-card { display: grid; gap: 24px; min-width: 0; padding: 24px; border: 1px solid var(--auth-border); border-radius: var(--auth-radius-container); background: var(--auth-card); }
33
+ .void-auth-page .void-auth-lead { display: grid; gap: 10px; }
34
+ .void-auth-page .void-auth-section { display: grid; gap: 12px; min-width: 0; }
35
+ .void-auth-page h1 { margin: 0; font-family: inherit; font-size: 26px; font-weight: 600; line-height: 1.2; letter-spacing: -.5px; color: var(--auth-text); overflow-wrap: anywhere; }
36
+ .void-auth-page h2 { margin: 0; font-family: inherit; font-size: 18px; font-weight: 600; line-height: 1.3; color: var(--auth-text); overflow-wrap: anywhere; }
37
+ .void-auth-page p { margin: 0; font: inherit; color: var(--auth-secondary); overflow-wrap: anywhere; }
38
+ .void-auth-page strong { font-weight: 600; color: var(--auth-text); }
39
+ .void-auth-page a:not(.void-auth-button) { color: var(--auth-text); text-underline-offset: 3px; }
40
+ .void-auth-page .void-auth-actions { display: grid; gap: 12px; }
41
+ .void-auth-page .void-auth-button { display: flex; align-items: center; justify-content: center; width: 100%; min-height: 44px; padding: 10px 16px; border: 1px solid var(--auth-border); border-radius: var(--auth-radius-element); background: var(--auth-body); color: var(--auth-text); font: inherit; font-weight: 550; line-height: 1.4; text-align: center; text-decoration: none; overflow-wrap: anywhere; cursor: pointer; }
42
+ .void-auth-page .void-auth-button-primary { background: var(--auth-accent); color: var(--auth-on-accent); border-color: var(--auth-accent); }
43
+ .void-auth-page .void-auth-button:hover { filter: brightness(.92); }
44
+ .void-auth-page :is(a, button, input):focus-visible { outline: 2px solid var(--auth-accent); outline-offset: 3px; }
45
+ .void-auth-page .void-auth-form { display: grid; gap: 10px; }
46
+ .void-auth-page label { font-weight: 550; }
47
+ .void-auth-page input:not([type=hidden]) { width: 100%; min-width: 0; height: 44px; padding: 0 12px; border: 1px solid var(--auth-border); border-radius: var(--auth-radius-element); background: var(--auth-body); color: var(--auth-text); font: inherit; }
48
+ .void-auth-page .void-auth-form button { margin-top: 8px; }
49
+ .void-auth-page .void-auth-identity { margin: 0; padding: 8px 14px; border: 1px solid var(--auth-border); border-radius: var(--auth-radius-inner); background: var(--auth-body); }
50
+ .void-auth-page .void-auth-identity > div { display: grid; grid-template-columns: 80px minmax(0, 1fr); gap: 16px; padding: 10px 0; }
51
+ .void-auth-page .void-auth-identity > div + div { border-top: 1px solid var(--auth-border); }
52
+ .void-auth-page dt { color: var(--auth-secondary); font-size: 13px; }
53
+ .void-auth-page dd { margin: 0; overflow-wrap: anywhere; }
54
+ .void-auth-page .void-auth-note { font-size: 13px; color: var(--auth-secondary); }
55
+ .void-auth-page pre { margin: 0; padding: 14px; border: 1px solid var(--auth-border); border-radius: var(--auth-radius-container); background: var(--auth-body); white-space: pre-wrap; overflow-wrap: anywhere; }
56
+ .void-auth-page code { font: 13px/1.6 ui-monospace, "SF Mono", Monaco, Consolas, monospace; }
57
+ .void-auth-page p code { padding: 2px 7px; background: light-dark(#e5e5e5, #343434); color: var(--auth-text); border-radius: 24px; corner-shape: squircle; white-space: nowrap; }
58
+ .void-auth-page .void-auth-status { display: grid; place-items: center; width: 40px; height: 40px; border-radius: 50%; font-size: 22px; font-weight: 650; }
59
+ .void-auth-page .void-auth-status-success { color: var(--auth-success); background: var(--auth-success-bg); }
60
+ .void-auth-page .void-auth-status-error { color: var(--auth-error); background: var(--auth-error-bg); }
61
+ @media (max-width: 500px) { .void-auth-page h1 { font-size: 24px; } }
62
+ `;
63
+ function escapeBrowserHtml(value) {
64
+ return value.replace(/[&<>"']/g, (character) => ({
65
+ "&": "&amp;",
66
+ "<": "&lt;",
67
+ ">": "&gt;",
68
+ "\"": "&quot;",
69
+ "'": "&#39;"
70
+ })[character]);
71
+ }
72
+ function browserPageIdentity(identity) {
73
+ return "<dl class=\"void-auth-identity\">" + [
74
+ ["Account", identity.login],
75
+ ["Provider", identity.issuer],
76
+ ["Identity", identity.subject]
77
+ ].map(([label, value]) => "<div><dt>" + label + "</dt><dd>" + escapeBrowserHtml(value) + "</dd></div>").join("") + "</dl>";
78
+ }
79
+ function browserPageStatus(status) {
80
+ return "<div class=\"void-auth-status void-auth-status-" + status + "\" aria-label=\"" + (status === "success" ? "Success" : "Error") + "\">" + (status === "success" ? "✓" : "!") + "</div>";
81
+ }
82
+ /** content is trusted markup; escape every dynamic value before composing it. */
83
+ function renderBrowserPage({ title, platformName = "Void", content }) {
84
+ return "<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><meta name=\"color-scheme\" content=\"light dark\"><title>" + escapeBrowserHtml(title + " · " + platformName + (/\bvoid\b/i.test(platformName) ? "" : " · Void")) + "</title><style>body{margin:0}" + browserPageStyles + "</style></head><body><main class=\"void-auth-page\"><div class=\"void-auth-container\"><div class=\"void-auth-brand\"><span class=\"void-auth-logo\"><svg role=\"img\" aria-label=\"Void\" viewBox=\"0 0 270 58\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n<circle cx=\"262\" cy=\"48.96\" r=\"8\" fill=\"currentColor\"/>\n<path d=\"M20.72 0.959999L35.52 41.28L50.08 0.959999H70L47.92 56.96H22.32L0.24 0.959999H20.72ZM106.542 57.92C82.9419 57.92 70.6219 48 70.6219 28.96C70.6219 9.92 82.9419 1.90735e-06 106.542 1.90735e-06C130.142 1.90735e-06 142.542 9.92 142.542 28.96C142.542 48 130.142 57.92 106.542 57.92ZM106.542 44.08C117.662 44.08 122.862 39.36 122.862 28.96C122.862 18.48 117.662 13.76 106.542 13.76C95.4219 13.76 90.2219 18.48 90.2219 28.96C90.2219 39.36 95.4219 44.08 106.542 44.08ZM152.265 56.96V0.959999H171.465V56.96H152.265ZM184.687 56.96V0.959999H219.887C239.327 0.959999 250.447 9.84 250.447 28.96C250.447 48.16 239.247 56.96 219.807 56.96H184.687ZM230.767 28.96C230.767 18.48 226.287 14.56 217.167 14.56H203.887V43.36H217.167C226.287 43.36 230.767 39.44 230.767 28.96Z\" fill=\"currentColor\"/>\n</svg></span><span class=\"void-auth-divider\" aria-hidden=\"true\"></span><span>" + escapeBrowserHtml(platformName) + "</span></div><section class=\"void-auth-card\" aria-labelledby=\"void-auth-title\">" + content + "</section></div></main></body></html>";
85
+ }
86
+ function browserPageLead(title, description) {
87
+ return "<div class=\"void-auth-lead\"><h1 id=\"void-auth-title\">" + escapeBrowserHtml(title) + "</h1><p>" + escapeBrowserHtml(description) + "</p></div>";
88
+ }
89
+ //#endregion
90
+ export { browserPageIdentity, browserPageLead, browserPageLogo, browserPageStatus, browserPageStyles, escapeBrowserHtml, renderBrowserPage };
@@ -45,22 +45,17 @@ function encodeEmailEnvelope(envelope) {
45
45
  }
46
46
  async function readEmailEnvelope(stream, deadlineMs = 15e3) {
47
47
  if (!stream) throw new Error("missing email envelope");
48
- const reader = stream.getReader();
49
48
  const limit = 10616836;
50
- let bytes = /* @__PURE__ */ new Uint8Array(1024);
51
- let total = 0;
52
49
  const deadline = Date.now() + deadlineMs;
53
- try {
50
+ const { bytes, total } = await withReader(stream, async (reader) => {
51
+ let bytes = /* @__PURE__ */ new Uint8Array(1024);
52
+ let total = 0;
54
53
  for (;;) {
55
- const remaining = deadline - Date.now();
56
- if (remaining <= 0) throw new Error("email envelope timeout");
57
- let timer;
58
- const chunk = await Promise.race([reader.read(), new Promise((_, reject) => {
59
- timer = setTimeout(() => reject(/* @__PURE__ */ new Error("email envelope timeout")), remaining);
60
- })]).finally(() => {
61
- if (timer !== void 0) clearTimeout(timer);
62
- });
63
- if (chunk.done) break;
54
+ const chunk = await readBeforeDeadline(reader, deadline, "email envelope timeout");
55
+ if (chunk.done) return {
56
+ bytes,
57
+ total
58
+ };
64
59
  const nextTotal = total + chunk.value.byteLength;
65
60
  if (nextTotal > limit) throw new Error("email envelope too large");
66
61
  if (nextTotal > bytes.byteLength) {
@@ -73,14 +68,7 @@ async function readEmailEnvelope(stream, deadlineMs = 15e3) {
73
68
  bytes.set(chunk.value, total);
74
69
  total = nextTotal;
75
70
  }
76
- } catch (error) {
77
- reader.cancel().catch(() => {});
78
- throw error;
79
- } finally {
80
- try {
81
- reader.releaseLock();
82
- } catch {}
83
- }
71
+ });
84
72
  if (total < 4) throw new Error("truncated email envelope");
85
73
  const metadataLength = new DataView(bytes.buffer).getUint32(0);
86
74
  if (metadataLength > 131072 || total < 4 + metadataLength) throw new Error("invalid email envelope");
@@ -187,40 +175,30 @@ function encodeEmailActions(actions) {
187
175
  }
188
176
  /** Read each frame with fixed ceilings before parsing metadata or allocating a reply. */
189
177
  async function readEmailActions(stream, deadlineMs = 15e3) {
190
- const reader = stream.getReader();
191
- const actions = [];
192
- let stash = /* @__PURE__ */ new Uint8Array(0);
193
- let total = 0;
194
- const deadline = Date.now() + deadlineMs;
195
- async function readChunk() {
196
- const remaining = deadline - Date.now();
197
- if (remaining <= 0) throw new Error("email response timeout");
198
- let timer;
199
- return Promise.race([reader.read(), new Promise((_, reject) => {
200
- timer = setTimeout(() => reject(/* @__PURE__ */ new Error("email response timeout")), remaining);
201
- })]).finally(() => {
202
- if (timer !== void 0) clearTimeout(timer);
203
- });
204
- }
205
- async function exact(length) {
206
- const result = new Uint8Array(length);
207
- let offset = 0;
208
- while (offset < length) {
209
- if (stash.byteLength === 0) {
210
- const chunk = await readChunk();
211
- if (chunk.done) throw new Error("truncated email response");
212
- total += chunk.value.byteLength;
213
- if (total > 8388608) throw new Error("email response too large");
214
- stash = chunk.value;
178
+ return withReader(stream, async (reader) => {
179
+ const actions = [];
180
+ let stash = /* @__PURE__ */ new Uint8Array(0);
181
+ let total = 0;
182
+ const deadline = Date.now() + deadlineMs;
183
+ const readChunk = () => readBeforeDeadline(reader, deadline, "email response timeout");
184
+ async function exact(length) {
185
+ const result = new Uint8Array(length);
186
+ let offset = 0;
187
+ while (offset < length) {
188
+ if (stash.byteLength === 0) {
189
+ const chunk = await readChunk();
190
+ if (chunk.done) throw new Error("truncated email response");
191
+ total += chunk.value.byteLength;
192
+ if (total > 8388608) throw new Error("email response too large");
193
+ stash = chunk.value;
194
+ }
195
+ const used = Math.min(stash.byteLength, length - offset);
196
+ result.set(stash.subarray(0, used), offset);
197
+ stash = stash.subarray(used);
198
+ offset += used;
215
199
  }
216
- const used = Math.min(stash.byteLength, length - offset);
217
- result.set(stash.subarray(0, used), offset);
218
- stash = stash.subarray(used);
219
- offset += used;
200
+ return result;
220
201
  }
221
- return result;
222
- }
223
- try {
224
202
  for (;;) {
225
203
  const head = await exact(5);
226
204
  const length = new DataView(head.buffer).getUint32(0);
@@ -265,6 +243,22 @@ async function readEmailActions(stream, deadlineMs = 15e3) {
265
243
  });
266
244
  } else throw new Error("unknown email action");
267
245
  }
246
+ });
247
+ }
248
+ async function readBeforeDeadline(reader, deadline, timeoutMessage) {
249
+ const remaining = deadline - Date.now();
250
+ if (remaining <= 0) throw new Error(timeoutMessage);
251
+ let timer;
252
+ return Promise.race([reader.read(), new Promise((_, reject) => {
253
+ timer = setTimeout(() => reject(new Error(timeoutMessage)), remaining);
254
+ })]).finally(() => {
255
+ if (timer !== void 0) clearTimeout(timer);
256
+ });
257
+ }
258
+ async function withReader(stream, read) {
259
+ const reader = stream.getReader();
260
+ try {
261
+ return await read(reader);
268
262
  } catch (error) {
269
263
  reader.cancel().catch(() => {});
270
264
  throw error;
package/dist/index.mjs CHANGED
@@ -411,7 +411,6 @@ function createPlatformDeploymentManifest(input) {
411
411
  for (const [key, value] of Object.entries(manifest.controlPlane)) {
412
412
  if (key === "dashboardUrl") continue;
413
413
  if (value !== null) normalizeApplicationDomain(value);
414
- if (value === "") throw new Error(`controlPlane.${key} must be a hostname or null.`);
415
414
  }
416
415
  if (manifest.controlPlane.dashboardUrl) {
417
416
  const hostname = new URL(manifest.controlPlane.dashboardUrl).hostname;
@@ -480,7 +479,7 @@ function parsePlatformDeploymentManifest(value) {
480
479
  },
481
480
  capabilities,
482
481
  ...value.emailSharedZoneId !== void 0 ? { emailSharedZoneId: requiredString$1(value.emailSharedZoneId, "emailSharedZoneId") } : {},
483
- ...value.emailSenderDomain !== void 0 ? { emailSenderDomain: normalizeApplicationDomain(requiredString$1(value.emailSenderDomain, "emailSenderDomain")) } : {},
482
+ ...value.emailSenderDomain !== void 0 ? { emailSenderDomain: requiredString$1(value.emailSenderDomain, "emailSenderDomain") } : {},
484
483
  controlPlane: {
485
484
  apiHostname: readHostname("apiHostname"),
486
485
  dashboardHostname: readHostname("dashboardHostname"),
@@ -649,16 +648,7 @@ function createPlatformResourceOwnershipMarker(input) {
649
648
  function parsePlatformResourceOwnershipMarker(value) {
650
649
  assertRecord(value, "Platform resource ownership marker");
651
650
  if (value.schemaVersion !== 1) throw new Error(`Unsupported platform resource ownership schema version: ${String(value.schemaVersion)}.`);
652
- if (!isResourceKind(value.resourceKind)) throw new Error("Invalid ownership marker resource kind.");
653
- return createPlatformResourceOwnershipMarker({
654
- accountId: requiredString(value.accountId, "ownership accountId"),
655
- installationId: requiredString(value.installationId, "ownership installationId"),
656
- ownershipNonce: requiredString(value.ownershipNonce, "ownership ownershipNonce"),
657
- resourceId: requiredString(value.resourceId, "ownership resourceId"),
658
- resourceKey: requiredString(value.resourceKey, "ownership resourceKey"),
659
- resourceKind: value.resourceKind,
660
- resourceName: requiredString(value.resourceName, "ownership resourceName")
661
- });
651
+ return createPlatformResourceOwnershipMarker(value);
662
652
  }
663
653
  function createPlatformInstallationManifest(input) {
664
654
  const createdAt = assertTimestamp(input.createdAt, "createdAt");
@@ -782,7 +772,7 @@ function parsePlatformInstallationManifest(value) {
782
772
  ownershipNonce: requiredString(value.ownershipNonce, "ownershipNonce"),
783
773
  platformVersion: requiredString(value.platformVersion, "platformVersion"),
784
774
  safetyProtocols: parseSafetyProtocols(value.safetyProtocols),
785
- usageProtocolVersion: parseUsageProtocolVersion$1(value.usageProtocolVersion)
775
+ usageProtocolVersion: parseUsageProtocolVersion(value.usageProtocolVersion)
786
776
  });
787
777
  const runtime = parsePlatformRuntimeProvenance(value.runtime);
788
778
  let pendingDomain;
@@ -856,7 +846,7 @@ function parseAuthenticationResources(value) {
856
846
  if (new Set(resources.map((resource) => `${resource.accountId}:${resource.kind}:${resource.id}`)).size !== resources.length) throw new Error("Duplicate authentication resource identity.");
857
847
  return resources;
858
848
  }
859
- function parseUsageProtocolVersion$1(value) {
849
+ function parseUsageProtocolVersion(value) {
860
850
  if (value === void 0 || value === 0) return 0;
861
851
  if (value === 1) return 1;
862
852
  if (value === 2) return 2;
@@ -977,7 +967,7 @@ function createPlatformDiscoveryDocument(input) {
977
967
  const application = readPlatformApplication({ PLATFORM_APPLICATION: JSON.stringify(input.applicationRouting) });
978
968
  if (!application.workersDev || application.baseDomain !== input.applicationDomain || application.workersDev.installationId !== input.installationId) throw new Error("Application routing must match the discovery domain and installation identity.");
979
969
  }
980
- if (input.usageProtocolVersion !== void 0 && input.usageProtocolVersion !== 0 && input.usageProtocolVersion !== 1 && input.usageProtocolVersion !== 2) throw new Error("usageProtocolVersion must be 0, 1, or 2.");
970
+ const usageProtocolVersion = parseUsageProtocolVersion(input.usageProtocolVersion);
981
971
  const authentication = input.authentication ? parseAuthentication(input.authentication) : void 0;
982
972
  if (!authentication && input.authenticationProviders.some((provider) => provider !== "github" && provider !== "google")) throw new Error("Named login methods require authentication discovery descriptors.");
983
973
  if (new Set(input.authenticationProviders).size !== input.authenticationProviders.length) throw new Error("Authentication provider IDs must be unique.");
@@ -996,7 +986,7 @@ function createPlatformDiscoveryDocument(input) {
996
986
  platformVersion: input.platformVersion.trim(),
997
987
  proxyUrl: normalizeOrigin(input.proxyUrl, "proxyUrl"),
998
988
  schemaVersion: authentication ? 3 : input.applicationRouting?.workersDev ? 2 : 1,
999
- usageProtocolVersion: input.usageProtocolVersion ?? 0
989
+ usageProtocolVersion
1000
990
  };
1001
991
  }
1002
992
  function parsePlatformDiscoveryDocument(value) {
@@ -1038,7 +1028,7 @@ function parsePlatformDiscoveryDocument(value) {
1038
1028
  return createPlatformDiscoveryDocument({
1039
1029
  apiUrl,
1040
1030
  applicationDomain: string("applicationDomain"),
1041
- ...document.applicationRouting !== void 0 ? { applicationRouting: readPlatformApplication({ PLATFORM_APPLICATION: JSON.stringify(document.applicationRouting ?? null) }) } : {},
1031
+ ...document.applicationRouting !== void 0 ? { applicationRouting: readPlatformApplication({ PLATFORM_APPLICATION: JSON.stringify(document.applicationRouting) }) } : {},
1042
1032
  authenticationProviders: providers,
1043
1033
  ...authentication ? { authentication } : {},
1044
1034
  capabilities: parsedCapabilities,
@@ -1062,12 +1052,6 @@ function parseAuthentication(value) {
1062
1052
  ...authentication.setupPending === true ? { setupPending: true } : {}
1063
1053
  };
1064
1054
  }
1065
- function parseUsageProtocolVersion(value) {
1066
- if (value === void 0 || value === 0) return 0;
1067
- if (value === 1) return 1;
1068
- if (value === 2) return 2;
1069
- throw new Error("usageProtocolVersion must be 0, 1, or 2.");
1070
- }
1071
1055
  //#endregion
1072
1056
  //#region src/worker-config.ts
1073
1057
  function createPlatformWorkerConfigs(input) {
@@ -138,14 +138,16 @@ function resolvePlatformRuntime(customRuntimeDirectory, baseDirectory = process.
138
138
  }
139
139
  const manifestPath = join(directory, "manifest.json");
140
140
  let manifestContents;
141
+ let manifest;
141
142
  try {
142
143
  manifestContents = readFileSync(manifestPath);
144
+ manifest = parsePlatformRuntimeManifest(JSON.parse(manifestContents.toString("utf8")));
143
145
  } catch (error) {
144
146
  throw new Error(`Void Platform deployment artifacts are missing or invalid at ${manifestPath}: ${error instanceof Error ? error.message : String(error)}`);
145
147
  }
146
148
  return {
147
149
  directory,
148
- manifest: readPlatformRuntimeManifest(directory),
150
+ manifest,
149
151
  manifestSha256: createHash("sha256").update(manifestContents).digest("hex"),
150
152
  source
151
153
  };