@keystrokehq/hosting 0.1.16 → 0.1.18

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 (35) hide show
  1. package/dist/docker.cjs +24 -1
  2. package/dist/docker.cjs.map +1 -1
  3. package/dist/docker.d.cts +1 -1
  4. package/dist/docker.d.mts +1 -1
  5. package/dist/docker.mjs +24 -1
  6. package/dist/docker.mjs.map +1 -1
  7. package/dist/index.cjs +117 -3
  8. package/dist/index.cjs.map +1 -1
  9. package/dist/index.d.cts +76 -6
  10. package/dist/index.d.cts.map +1 -1
  11. package/dist/index.d.mts +76 -6
  12. package/dist/index.d.mts.map +1 -1
  13. package/dist/index.mjs +111 -4
  14. package/dist/index.mjs.map +1 -1
  15. package/dist/org-artifacts.d.cts +1 -1
  16. package/dist/org-artifacts.d.mts +1 -1
  17. package/dist/{plugin-is5oPt6x.d.cts → plugin-4cuXkf42.d.cts} +2 -2
  18. package/dist/{plugin-is5oPt6x.d.cts.map → plugin-4cuXkf42.d.cts.map} +1 -1
  19. package/dist/{plugin-qEl5Sx4m.d.mts → plugin-eiAXqE7P.d.mts} +2 -2
  20. package/dist/{plugin-qEl5Sx4m.d.mts.map → plugin-eiAXqE7P.d.mts.map} +1 -1
  21. package/dist/runtime-BmfsHLrk.mjs.map +1 -1
  22. package/dist/runtime-CV7pSfMl.cjs.map +1 -1
  23. package/dist/{runtime-B0jF0oMv.d.cts → runtime-Cp906goK.d.cts} +22 -3
  24. package/dist/runtime-Cp906goK.d.cts.map +1 -0
  25. package/dist/{runtime-B0jF0oMv.d.mts → runtime-Cp906goK.d.mts} +22 -3
  26. package/dist/runtime-Cp906goK.d.mts.map +1 -0
  27. package/dist/{wait-for-health-XEzPPQIO.mjs → wait-for-health-B4-AM1NR.mjs} +3 -2
  28. package/dist/wait-for-health-B4-AM1NR.mjs.map +1 -0
  29. package/dist/{wait-for-health-DM55_NQh.cjs → wait-for-health-CObmNF8o.cjs} +3 -2
  30. package/dist/wait-for-health-CObmNF8o.cjs.map +1 -0
  31. package/package.json +2 -2
  32. package/dist/runtime-B0jF0oMv.d.cts.map +0 -1
  33. package/dist/runtime-B0jF0oMv.d.mts.map +0 -1
  34. package/dist/wait-for-health-DM55_NQh.cjs.map +0 -1
  35. package/dist/wait-for-health-XEzPPQIO.mjs.map +0 -1
package/dist/docker.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_wait_for_health = require("./wait-for-health-DM55_NQh.cjs");
2
+ const require_wait_for_health = require("./wait-for-health-CObmNF8o.cjs");
3
3
  let node_fs_promises = require("node:fs/promises");
4
4
  //#region src/docker/create-docker-runtime.ts
5
5
  const MINIO_DOCKER_NETWORK = process.env.MINIO_DOCKER_NETWORK ?? "keystroke";
@@ -67,6 +67,29 @@ function createDockerRuntime(options = {}) {
67
67
  try {
68
68
  await (await resolveDocker(options)).getContainer(input.runtimeId).remove({ force: true });
69
69
  } catch {}
70
+ },
71
+ async sleep(input) {
72
+ const container = (await resolveDocker(options)).getContainer(input.runtimeId);
73
+ const inspect = await container.inspect().catch(() => null);
74
+ if (!inspect) return;
75
+ if (inspect.State.Running) await container.stop({ t: 10 });
76
+ },
77
+ async wake(input) {
78
+ const container = (await resolveDocker(options)).getContainer(input.runtimeId);
79
+ if (!(await container.inspect()).State.Running) await container.start();
80
+ const refreshed = await container.inspect();
81
+ const ports = refreshed.NetworkSettings.Ports ?? {};
82
+ const binding = Object.values(ports).flat().find((entry) => entry?.HostPort)?.HostPort;
83
+ if (!binding) throw new Error(`Failed to resolve host port for container ${input.runtimeId}`);
84
+ const baseUrl = `http://127.0.0.1:${binding}`;
85
+ await require_wait_for_health.waitForHealth(baseUrl, {
86
+ fetchImpl: options.fetchImpl,
87
+ timeoutMs: options.healthTimeoutMs
88
+ });
89
+ return {
90
+ runtimeId: refreshed.Id,
91
+ baseUrl
92
+ };
70
93
  }
71
94
  };
72
95
  }
@@ -1 +1 @@
1
- {"version":3,"file":"docker.cjs","names":["resolveRuntimeLaunch","formatDockerEnv","waitForHealth","resolveWorkerRuntimeConfig"],"sources":["../src/docker/create-docker-runtime.ts","../src/docker/plugin.ts","../src/default-plugin.ts"],"sourcesContent":["import { mkdir } from \"node:fs/promises\";\nimport type Docker from \"dockerode\";\n\nimport type { ProjectRuntime } from \"../runtime\";\n\nimport type { HostingOptions } from \"../plugin\";\nimport { formatDockerEnv, resolveRuntimeLaunch } from \"../runtime-env\";\nimport { waitForHealth } from \"../wait-for-health\";\n\nexport type WorkspacesMount = {\n hostPath: string;\n containerPath: string;\n};\n\nexport type DockerPluginOptions = HostingOptions & {\n docker?: Docker;\n /** Bind a shared host workspace dir into dev workers (VM sandbox proxy mount path). */\n workspacesMount?: WorkspacesMount;\n /** Additional Docker bind mounts (`host:container[:options]`). */\n extraBinds?: string[];\n};\n\nconst MINIO_DOCKER_NETWORK = process.env.MINIO_DOCKER_NETWORK ?? \"keystroke\";\n\nasync function resolveDocker(options: DockerPluginOptions): Promise<Docker> {\n if (options.docker) {\n return options.docker;\n }\n const { default: DockerCtor } = await import(\"dockerode\");\n return new DockerCtor();\n}\n\nexport function createDockerRuntime(options: DockerPluginOptions = {}): ProjectRuntime {\n return {\n async start(input) {\n if (input.projects.length === 0) {\n throw new Error(\"Docker org runtime requires at least one project deploy\");\n }\n\n const docker = await resolveDocker(options);\n\n let launch = resolveRuntimeLaunch(\n options,\n input.projects.map((project) => ({\n projectId: project.projectId,\n artifactStorageKey: project.artifactStorageKey,\n artifactId: project.artifactId,\n artifactVersion: project.artifactVersion,\n })),\n input.database,\n input.env,\n );\n\n if (options.workspacesMount) {\n const { hostPath, containerPath } = options.workspacesMount;\n await mkdir(hostPath, { recursive: true });\n launch = {\n ...launch,\n env: {\n ...launch.env,\n KEYSTROKE_WORKSPACES_ROOT: containerPath,\n },\n };\n }\n\n await ensureImage(docker, launch.image);\n await removeExistingContainer(docker, input.organizationId);\n\n const binds = [\n ...(options.workspacesMount\n ? [`${options.workspacesMount.hostPath}:${options.workspacesMount.containerPath}`]\n : []),\n ...(options.extraBinds ?? []),\n ];\n\n const container = await docker.createContainer({\n Image: launch.image,\n name: containerName(input.organizationId),\n Labels: {\n \"keystroke.organization.id\": input.organizationId,\n \"keystroke.project.ids\": input.projects.map((project) => project.projectId).join(\",\"),\n },\n ExposedPorts: {\n [`${launch.port}/tcp`]: {},\n },\n HostConfig: {\n ExtraHosts: [\"host.docker.internal:host-gateway\"],\n PortBindings: {\n [`${launch.port}/tcp`]: [{ HostPort: \"0\" }],\n },\n ...(binds.length > 0 ? { Binds: binds } : {}),\n },\n NetworkingConfig: {\n EndpointsConfig: {\n [MINIO_DOCKER_NETWORK]: {},\n },\n },\n Env: formatDockerEnv(launch.env),\n });\n\n await container.start();\n\n const inspect = await container.inspect();\n const binding = inspect.NetworkSettings.Ports?.[`${launch.port}/tcp`]?.[0];\n if (!binding?.HostPort) {\n throw new Error(\"Failed to resolve org container host port\");\n }\n\n const baseUrl = `http://127.0.0.1:${binding.HostPort}`;\n await waitForHealth(baseUrl, {\n fetchImpl: options.fetchImpl,\n timeoutMs: options.healthTimeoutMs,\n });\n\n return {\n runtimeId: inspect.Id,\n baseUrl,\n };\n },\n\n async destroy(input) {\n try {\n const docker = await resolveDocker(options);\n const container = docker.getContainer(input.runtimeId);\n await container.remove({ force: true });\n } catch {\n // Container already removed or never existed.\n }\n },\n };\n}\n\nfunction containerName(organizationId: string): string {\n return `keystroke-org-${organizationId}`;\n}\n\nasync function removeExistingContainer(docker: Docker, organizationId: string): Promise<void> {\n try {\n const existing = docker.getContainer(containerName(organizationId));\n await existing.stop({ t: 5 }).catch(() => undefined);\n await existing.remove({ force: true });\n } catch {\n // no existing container\n }\n}\n\nasync function ensureImage(docker: Docker, image: string): Promise<void> {\n try {\n await docker.getImage(image).inspect();\n return;\n } catch {\n // pull below\n }\n\n await new Promise<void>((resolvePromise, reject) => {\n docker.pull(image, (error: Error | null, stream: NodeJS.ReadableStream | undefined) => {\n if (error) {\n reject(error);\n return;\n }\n\n if (!stream) {\n reject(new Error(`Failed to pull image ${image}`));\n return;\n }\n\n docker.modem.followProgress(stream, (progressError: Error | null) => {\n if (progressError) {\n reject(progressError);\n return;\n }\n\n resolvePromise();\n });\n });\n });\n}\n","import type { HostingPlugin } from \"../plugin\";\nimport { resolveWorkerRuntimeConfig } from \"../worker-runtime-config\";\n\nimport { createDockerRuntime } from \"./create-docker-runtime\";\nimport type { DockerPluginOptions } from \"./create-docker-runtime\";\n\nexport type { DockerPluginOptions } from \"./create-docker-runtime\";\n\nexport function dockerPlugin(options: DockerPluginOptions = {}): HostingPlugin {\n const env = options.env ?? process.env;\n\n return {\n name: \"docker\",\n workerRuntime: resolveWorkerRuntimeConfig(env),\n createRuntime: () => createDockerRuntime({ ...options, env }),\n };\n}\n","import type { HostingPlugin } from \"./plugin\";\nimport { dockerPlugin, type DockerPluginOptions } from \"./docker/plugin\";\n\n/** Local Docker hosting — default for platform and dev. */\nexport function defaultHostingPlugin(options: DockerPluginOptions = {}): HostingPlugin {\n return dockerPlugin(options);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"docker.cjs","names":["resolveRuntimeLaunch","formatDockerEnv","waitForHealth","resolveWorkerRuntimeConfig"],"sources":["../src/docker/create-docker-runtime.ts","../src/docker/plugin.ts","../src/default-plugin.ts"],"sourcesContent":["import { mkdir } from \"node:fs/promises\";\nimport type Docker from \"dockerode\";\n\nimport type { ProjectRuntime } from \"../runtime\";\n\nimport type { HostingOptions } from \"../plugin\";\nimport { formatDockerEnv, resolveRuntimeLaunch } from \"../runtime-env\";\nimport { waitForHealth } from \"../wait-for-health\";\n\nexport type WorkspacesMount = {\n hostPath: string;\n containerPath: string;\n};\n\nexport type DockerPluginOptions = HostingOptions & {\n docker?: Docker;\n /** Bind a shared host workspace dir into dev workers (VM sandbox proxy mount path). */\n workspacesMount?: WorkspacesMount;\n /** Additional Docker bind mounts (`host:container[:options]`). */\n extraBinds?: string[];\n};\n\nconst MINIO_DOCKER_NETWORK = process.env.MINIO_DOCKER_NETWORK ?? \"keystroke\";\n\nasync function resolveDocker(options: DockerPluginOptions): Promise<Docker> {\n if (options.docker) {\n return options.docker;\n }\n const { default: DockerCtor } = await import(\"dockerode\");\n return new DockerCtor();\n}\n\nexport function createDockerRuntime(options: DockerPluginOptions = {}): ProjectRuntime {\n return {\n async start(input) {\n if (input.projects.length === 0) {\n throw new Error(\"Docker org runtime requires at least one project deploy\");\n }\n\n const docker = await resolveDocker(options);\n\n let launch = resolveRuntimeLaunch(\n options,\n input.projects.map((project) => ({\n projectId: project.projectId,\n artifactStorageKey: project.artifactStorageKey,\n artifactId: project.artifactId,\n artifactVersion: project.artifactVersion,\n })),\n input.database,\n input.env,\n );\n\n if (options.workspacesMount) {\n const { hostPath, containerPath } = options.workspacesMount;\n await mkdir(hostPath, { recursive: true });\n launch = {\n ...launch,\n env: {\n ...launch.env,\n KEYSTROKE_WORKSPACES_ROOT: containerPath,\n },\n };\n }\n\n await ensureImage(docker, launch.image);\n await removeExistingContainer(docker, input.organizationId);\n\n const binds = [\n ...(options.workspacesMount\n ? [`${options.workspacesMount.hostPath}:${options.workspacesMount.containerPath}`]\n : []),\n ...(options.extraBinds ?? []),\n ];\n\n const container = await docker.createContainer({\n Image: launch.image,\n name: containerName(input.organizationId),\n Labels: {\n \"keystroke.organization.id\": input.organizationId,\n \"keystroke.project.ids\": input.projects.map((project) => project.projectId).join(\",\"),\n },\n ExposedPorts: {\n [`${launch.port}/tcp`]: {},\n },\n HostConfig: {\n ExtraHosts: [\"host.docker.internal:host-gateway\"],\n PortBindings: {\n [`${launch.port}/tcp`]: [{ HostPort: \"0\" }],\n },\n ...(binds.length > 0 ? { Binds: binds } : {}),\n },\n NetworkingConfig: {\n EndpointsConfig: {\n [MINIO_DOCKER_NETWORK]: {},\n },\n },\n Env: formatDockerEnv(launch.env),\n });\n\n await container.start();\n\n const inspect = await container.inspect();\n const binding = inspect.NetworkSettings.Ports?.[`${launch.port}/tcp`]?.[0];\n if (!binding?.HostPort) {\n throw new Error(\"Failed to resolve org container host port\");\n }\n\n const baseUrl = `http://127.0.0.1:${binding.HostPort}`;\n await waitForHealth(baseUrl, {\n fetchImpl: options.fetchImpl,\n timeoutMs: options.healthTimeoutMs,\n });\n\n return {\n runtimeId: inspect.Id,\n baseUrl,\n };\n },\n\n async destroy(input) {\n try {\n const docker = await resolveDocker(options);\n const container = docker.getContainer(input.runtimeId);\n await container.remove({ force: true });\n } catch {\n // Container already removed or never existed.\n }\n },\n\n async sleep(input) {\n const docker = await resolveDocker(options);\n const container = docker.getContainer(input.runtimeId);\n const inspect = await container.inspect().catch(() => null);\n if (!inspect) {\n return;\n }\n if (inspect.State.Running) {\n await container.stop({ t: 10 });\n }\n },\n\n async wake(input) {\n const docker = await resolveDocker(options);\n const container = docker.getContainer(input.runtimeId);\n const inspect = await container.inspect();\n if (!inspect.State.Running) {\n await container.start();\n }\n\n const refreshed = await container.inspect();\n const ports = refreshed.NetworkSettings.Ports ?? {};\n const binding = Object.values(ports)\n .flat()\n .find((entry) => entry?.HostPort)?.HostPort;\n if (!binding) {\n throw new Error(`Failed to resolve host port for container ${input.runtimeId}`);\n }\n\n const baseUrl = `http://127.0.0.1:${binding}`;\n await waitForHealth(baseUrl, {\n fetchImpl: options.fetchImpl,\n timeoutMs: options.healthTimeoutMs,\n });\n\n return {\n runtimeId: refreshed.Id,\n baseUrl,\n };\n },\n };\n}\n\nfunction containerName(organizationId: string): string {\n return `keystroke-org-${organizationId}`;\n}\n\nasync function removeExistingContainer(docker: Docker, organizationId: string): Promise<void> {\n try {\n const existing = docker.getContainer(containerName(organizationId));\n await existing.stop({ t: 5 }).catch(() => undefined);\n await existing.remove({ force: true });\n } catch {\n // no existing container\n }\n}\n\nasync function ensureImage(docker: Docker, image: string): Promise<void> {\n try {\n await docker.getImage(image).inspect();\n return;\n } catch {\n // pull below\n }\n\n await new Promise<void>((resolvePromise, reject) => {\n docker.pull(image, (error: Error | null, stream: NodeJS.ReadableStream | undefined) => {\n if (error) {\n reject(error);\n return;\n }\n\n if (!stream) {\n reject(new Error(`Failed to pull image ${image}`));\n return;\n }\n\n docker.modem.followProgress(stream, (progressError: Error | null) => {\n if (progressError) {\n reject(progressError);\n return;\n }\n\n resolvePromise();\n });\n });\n });\n}\n","import type { HostingPlugin } from \"../plugin\";\nimport { resolveWorkerRuntimeConfig } from \"../worker-runtime-config\";\n\nimport { createDockerRuntime } from \"./create-docker-runtime\";\nimport type { DockerPluginOptions } from \"./create-docker-runtime\";\n\nexport type { DockerPluginOptions } from \"./create-docker-runtime\";\n\nexport function dockerPlugin(options: DockerPluginOptions = {}): HostingPlugin {\n const env = options.env ?? process.env;\n\n return {\n name: \"docker\",\n workerRuntime: resolveWorkerRuntimeConfig(env),\n createRuntime: () => createDockerRuntime({ ...options, env }),\n };\n}\n","import type { HostingPlugin } from \"./plugin\";\nimport { dockerPlugin, type DockerPluginOptions } from \"./docker/plugin\";\n\n/** Local Docker hosting — default for platform and dev. */\nexport function defaultHostingPlugin(options: DockerPluginOptions = {}): HostingPlugin {\n return dockerPlugin(options);\n}\n"],"mappings":"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"}
package/dist/docker.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { n as HostingPlugin, t as HostingOptions } from "./plugin-is5oPt6x.cjs";
1
+ import { n as HostingPlugin, t as HostingOptions } from "./plugin-4cuXkf42.cjs";
2
2
  import Docker from "dockerode";
3
3
 
4
4
  //#region src/docker/create-docker-runtime.d.ts
package/dist/docker.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { n as HostingPlugin, t as HostingOptions } from "./plugin-qEl5Sx4m.mjs";
1
+ import { n as HostingPlugin, t as HostingOptions } from "./plugin-eiAXqE7P.mjs";
2
2
  import Docker from "dockerode";
3
3
 
4
4
  //#region src/docker/create-docker-runtime.d.ts
package/dist/docker.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { a as formatDockerEnv, c as resolveRuntimeLaunch, h as resolveWorkerRuntimeConfig, t as waitForHealth } from "./wait-for-health-XEzPPQIO.mjs";
1
+ import { a as formatDockerEnv, c as resolveRuntimeLaunch, h as resolveWorkerRuntimeConfig, t as waitForHealth } from "./wait-for-health-B4-AM1NR.mjs";
2
2
  import { mkdir } from "node:fs/promises";
3
3
  //#region src/docker/create-docker-runtime.ts
4
4
  const MINIO_DOCKER_NETWORK = process.env.MINIO_DOCKER_NETWORK ?? "keystroke";
@@ -66,6 +66,29 @@ function createDockerRuntime(options = {}) {
66
66
  try {
67
67
  await (await resolveDocker(options)).getContainer(input.runtimeId).remove({ force: true });
68
68
  } catch {}
69
+ },
70
+ async sleep(input) {
71
+ const container = (await resolveDocker(options)).getContainer(input.runtimeId);
72
+ const inspect = await container.inspect().catch(() => null);
73
+ if (!inspect) return;
74
+ if (inspect.State.Running) await container.stop({ t: 10 });
75
+ },
76
+ async wake(input) {
77
+ const container = (await resolveDocker(options)).getContainer(input.runtimeId);
78
+ if (!(await container.inspect()).State.Running) await container.start();
79
+ const refreshed = await container.inspect();
80
+ const ports = refreshed.NetworkSettings.Ports ?? {};
81
+ const binding = Object.values(ports).flat().find((entry) => entry?.HostPort)?.HostPort;
82
+ if (!binding) throw new Error(`Failed to resolve host port for container ${input.runtimeId}`);
83
+ const baseUrl = `http://127.0.0.1:${binding}`;
84
+ await waitForHealth(baseUrl, {
85
+ fetchImpl: options.fetchImpl,
86
+ timeoutMs: options.healthTimeoutMs
87
+ });
88
+ return {
89
+ runtimeId: refreshed.Id,
90
+ baseUrl
91
+ };
69
92
  }
70
93
  };
71
94
  }
@@ -1 +1 @@
1
- {"version":3,"file":"docker.mjs","names":[],"sources":["../src/docker/create-docker-runtime.ts","../src/docker/plugin.ts","../src/default-plugin.ts"],"sourcesContent":["import { mkdir } from \"node:fs/promises\";\nimport type Docker from \"dockerode\";\n\nimport type { ProjectRuntime } from \"../runtime\";\n\nimport type { HostingOptions } from \"../plugin\";\nimport { formatDockerEnv, resolveRuntimeLaunch } from \"../runtime-env\";\nimport { waitForHealth } from \"../wait-for-health\";\n\nexport type WorkspacesMount = {\n hostPath: string;\n containerPath: string;\n};\n\nexport type DockerPluginOptions = HostingOptions & {\n docker?: Docker;\n /** Bind a shared host workspace dir into dev workers (VM sandbox proxy mount path). */\n workspacesMount?: WorkspacesMount;\n /** Additional Docker bind mounts (`host:container[:options]`). */\n extraBinds?: string[];\n};\n\nconst MINIO_DOCKER_NETWORK = process.env.MINIO_DOCKER_NETWORK ?? \"keystroke\";\n\nasync function resolveDocker(options: DockerPluginOptions): Promise<Docker> {\n if (options.docker) {\n return options.docker;\n }\n const { default: DockerCtor } = await import(\"dockerode\");\n return new DockerCtor();\n}\n\nexport function createDockerRuntime(options: DockerPluginOptions = {}): ProjectRuntime {\n return {\n async start(input) {\n if (input.projects.length === 0) {\n throw new Error(\"Docker org runtime requires at least one project deploy\");\n }\n\n const docker = await resolveDocker(options);\n\n let launch = resolveRuntimeLaunch(\n options,\n input.projects.map((project) => ({\n projectId: project.projectId,\n artifactStorageKey: project.artifactStorageKey,\n artifactId: project.artifactId,\n artifactVersion: project.artifactVersion,\n })),\n input.database,\n input.env,\n );\n\n if (options.workspacesMount) {\n const { hostPath, containerPath } = options.workspacesMount;\n await mkdir(hostPath, { recursive: true });\n launch = {\n ...launch,\n env: {\n ...launch.env,\n KEYSTROKE_WORKSPACES_ROOT: containerPath,\n },\n };\n }\n\n await ensureImage(docker, launch.image);\n await removeExistingContainer(docker, input.organizationId);\n\n const binds = [\n ...(options.workspacesMount\n ? [`${options.workspacesMount.hostPath}:${options.workspacesMount.containerPath}`]\n : []),\n ...(options.extraBinds ?? []),\n ];\n\n const container = await docker.createContainer({\n Image: launch.image,\n name: containerName(input.organizationId),\n Labels: {\n \"keystroke.organization.id\": input.organizationId,\n \"keystroke.project.ids\": input.projects.map((project) => project.projectId).join(\",\"),\n },\n ExposedPorts: {\n [`${launch.port}/tcp`]: {},\n },\n HostConfig: {\n ExtraHosts: [\"host.docker.internal:host-gateway\"],\n PortBindings: {\n [`${launch.port}/tcp`]: [{ HostPort: \"0\" }],\n },\n ...(binds.length > 0 ? { Binds: binds } : {}),\n },\n NetworkingConfig: {\n EndpointsConfig: {\n [MINIO_DOCKER_NETWORK]: {},\n },\n },\n Env: formatDockerEnv(launch.env),\n });\n\n await container.start();\n\n const inspect = await container.inspect();\n const binding = inspect.NetworkSettings.Ports?.[`${launch.port}/tcp`]?.[0];\n if (!binding?.HostPort) {\n throw new Error(\"Failed to resolve org container host port\");\n }\n\n const baseUrl = `http://127.0.0.1:${binding.HostPort}`;\n await waitForHealth(baseUrl, {\n fetchImpl: options.fetchImpl,\n timeoutMs: options.healthTimeoutMs,\n });\n\n return {\n runtimeId: inspect.Id,\n baseUrl,\n };\n },\n\n async destroy(input) {\n try {\n const docker = await resolveDocker(options);\n const container = docker.getContainer(input.runtimeId);\n await container.remove({ force: true });\n } catch {\n // Container already removed or never existed.\n }\n },\n };\n}\n\nfunction containerName(organizationId: string): string {\n return `keystroke-org-${organizationId}`;\n}\n\nasync function removeExistingContainer(docker: Docker, organizationId: string): Promise<void> {\n try {\n const existing = docker.getContainer(containerName(organizationId));\n await existing.stop({ t: 5 }).catch(() => undefined);\n await existing.remove({ force: true });\n } catch {\n // no existing container\n }\n}\n\nasync function ensureImage(docker: Docker, image: string): Promise<void> {\n try {\n await docker.getImage(image).inspect();\n return;\n } catch {\n // pull below\n }\n\n await new Promise<void>((resolvePromise, reject) => {\n docker.pull(image, (error: Error | null, stream: NodeJS.ReadableStream | undefined) => {\n if (error) {\n reject(error);\n return;\n }\n\n if (!stream) {\n reject(new Error(`Failed to pull image ${image}`));\n return;\n }\n\n docker.modem.followProgress(stream, (progressError: Error | null) => {\n if (progressError) {\n reject(progressError);\n return;\n }\n\n resolvePromise();\n });\n });\n });\n}\n","import type { HostingPlugin } from \"../plugin\";\nimport { resolveWorkerRuntimeConfig } from \"../worker-runtime-config\";\n\nimport { createDockerRuntime } from \"./create-docker-runtime\";\nimport type { DockerPluginOptions } from \"./create-docker-runtime\";\n\nexport type { DockerPluginOptions } from \"./create-docker-runtime\";\n\nexport function dockerPlugin(options: DockerPluginOptions = {}): HostingPlugin {\n const env = options.env ?? process.env;\n\n return {\n name: \"docker\",\n workerRuntime: resolveWorkerRuntimeConfig(env),\n createRuntime: () => createDockerRuntime({ ...options, env }),\n };\n}\n","import type { HostingPlugin } from \"./plugin\";\nimport { dockerPlugin, type DockerPluginOptions } from \"./docker/plugin\";\n\n/** Local Docker hosting — default for platform and dev. */\nexport function defaultHostingPlugin(options: DockerPluginOptions = {}): HostingPlugin {\n return dockerPlugin(options);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"docker.mjs","names":[],"sources":["../src/docker/create-docker-runtime.ts","../src/docker/plugin.ts","../src/default-plugin.ts"],"sourcesContent":["import { mkdir } from \"node:fs/promises\";\nimport type Docker from \"dockerode\";\n\nimport type { ProjectRuntime } from \"../runtime\";\n\nimport type { HostingOptions } from \"../plugin\";\nimport { formatDockerEnv, resolveRuntimeLaunch } from \"../runtime-env\";\nimport { waitForHealth } from \"../wait-for-health\";\n\nexport type WorkspacesMount = {\n hostPath: string;\n containerPath: string;\n};\n\nexport type DockerPluginOptions = HostingOptions & {\n docker?: Docker;\n /** Bind a shared host workspace dir into dev workers (VM sandbox proxy mount path). */\n workspacesMount?: WorkspacesMount;\n /** Additional Docker bind mounts (`host:container[:options]`). */\n extraBinds?: string[];\n};\n\nconst MINIO_DOCKER_NETWORK = process.env.MINIO_DOCKER_NETWORK ?? \"keystroke\";\n\nasync function resolveDocker(options: DockerPluginOptions): Promise<Docker> {\n if (options.docker) {\n return options.docker;\n }\n const { default: DockerCtor } = await import(\"dockerode\");\n return new DockerCtor();\n}\n\nexport function createDockerRuntime(options: DockerPluginOptions = {}): ProjectRuntime {\n return {\n async start(input) {\n if (input.projects.length === 0) {\n throw new Error(\"Docker org runtime requires at least one project deploy\");\n }\n\n const docker = await resolveDocker(options);\n\n let launch = resolveRuntimeLaunch(\n options,\n input.projects.map((project) => ({\n projectId: project.projectId,\n artifactStorageKey: project.artifactStorageKey,\n artifactId: project.artifactId,\n artifactVersion: project.artifactVersion,\n })),\n input.database,\n input.env,\n );\n\n if (options.workspacesMount) {\n const { hostPath, containerPath } = options.workspacesMount;\n await mkdir(hostPath, { recursive: true });\n launch = {\n ...launch,\n env: {\n ...launch.env,\n KEYSTROKE_WORKSPACES_ROOT: containerPath,\n },\n };\n }\n\n await ensureImage(docker, launch.image);\n await removeExistingContainer(docker, input.organizationId);\n\n const binds = [\n ...(options.workspacesMount\n ? [`${options.workspacesMount.hostPath}:${options.workspacesMount.containerPath}`]\n : []),\n ...(options.extraBinds ?? []),\n ];\n\n const container = await docker.createContainer({\n Image: launch.image,\n name: containerName(input.organizationId),\n Labels: {\n \"keystroke.organization.id\": input.organizationId,\n \"keystroke.project.ids\": input.projects.map((project) => project.projectId).join(\",\"),\n },\n ExposedPorts: {\n [`${launch.port}/tcp`]: {},\n },\n HostConfig: {\n ExtraHosts: [\"host.docker.internal:host-gateway\"],\n PortBindings: {\n [`${launch.port}/tcp`]: [{ HostPort: \"0\" }],\n },\n ...(binds.length > 0 ? { Binds: binds } : {}),\n },\n NetworkingConfig: {\n EndpointsConfig: {\n [MINIO_DOCKER_NETWORK]: {},\n },\n },\n Env: formatDockerEnv(launch.env),\n });\n\n await container.start();\n\n const inspect = await container.inspect();\n const binding = inspect.NetworkSettings.Ports?.[`${launch.port}/tcp`]?.[0];\n if (!binding?.HostPort) {\n throw new Error(\"Failed to resolve org container host port\");\n }\n\n const baseUrl = `http://127.0.0.1:${binding.HostPort}`;\n await waitForHealth(baseUrl, {\n fetchImpl: options.fetchImpl,\n timeoutMs: options.healthTimeoutMs,\n });\n\n return {\n runtimeId: inspect.Id,\n baseUrl,\n };\n },\n\n async destroy(input) {\n try {\n const docker = await resolveDocker(options);\n const container = docker.getContainer(input.runtimeId);\n await container.remove({ force: true });\n } catch {\n // Container already removed or never existed.\n }\n },\n\n async sleep(input) {\n const docker = await resolveDocker(options);\n const container = docker.getContainer(input.runtimeId);\n const inspect = await container.inspect().catch(() => null);\n if (!inspect) {\n return;\n }\n if (inspect.State.Running) {\n await container.stop({ t: 10 });\n }\n },\n\n async wake(input) {\n const docker = await resolveDocker(options);\n const container = docker.getContainer(input.runtimeId);\n const inspect = await container.inspect();\n if (!inspect.State.Running) {\n await container.start();\n }\n\n const refreshed = await container.inspect();\n const ports = refreshed.NetworkSettings.Ports ?? {};\n const binding = Object.values(ports)\n .flat()\n .find((entry) => entry?.HostPort)?.HostPort;\n if (!binding) {\n throw new Error(`Failed to resolve host port for container ${input.runtimeId}`);\n }\n\n const baseUrl = `http://127.0.0.1:${binding}`;\n await waitForHealth(baseUrl, {\n fetchImpl: options.fetchImpl,\n timeoutMs: options.healthTimeoutMs,\n });\n\n return {\n runtimeId: refreshed.Id,\n baseUrl,\n };\n },\n };\n}\n\nfunction containerName(organizationId: string): string {\n return `keystroke-org-${organizationId}`;\n}\n\nasync function removeExistingContainer(docker: Docker, organizationId: string): Promise<void> {\n try {\n const existing = docker.getContainer(containerName(organizationId));\n await existing.stop({ t: 5 }).catch(() => undefined);\n await existing.remove({ force: true });\n } catch {\n // no existing container\n }\n}\n\nasync function ensureImage(docker: Docker, image: string): Promise<void> {\n try {\n await docker.getImage(image).inspect();\n return;\n } catch {\n // pull below\n }\n\n await new Promise<void>((resolvePromise, reject) => {\n docker.pull(image, (error: Error | null, stream: NodeJS.ReadableStream | undefined) => {\n if (error) {\n reject(error);\n return;\n }\n\n if (!stream) {\n reject(new Error(`Failed to pull image ${image}`));\n return;\n }\n\n docker.modem.followProgress(stream, (progressError: Error | null) => {\n if (progressError) {\n reject(progressError);\n return;\n }\n\n resolvePromise();\n });\n });\n });\n}\n","import type { HostingPlugin } from \"../plugin\";\nimport { resolveWorkerRuntimeConfig } from \"../worker-runtime-config\";\n\nimport { createDockerRuntime } from \"./create-docker-runtime\";\nimport type { DockerPluginOptions } from \"./create-docker-runtime\";\n\nexport type { DockerPluginOptions } from \"./create-docker-runtime\";\n\nexport function dockerPlugin(options: DockerPluginOptions = {}): HostingPlugin {\n const env = options.env ?? process.env;\n\n return {\n name: \"docker\",\n workerRuntime: resolveWorkerRuntimeConfig(env),\n createRuntime: () => createDockerRuntime({ ...options, env }),\n };\n}\n","import type { HostingPlugin } from \"./plugin\";\nimport { dockerPlugin, type DockerPluginOptions } from \"./docker/plugin\";\n\n/** Local Docker hosting — default for platform and dev. */\nexport function defaultHostingPlugin(options: DockerPluginOptions = {}): HostingPlugin {\n return dockerPlugin(options);\n}\n"],"mappings":"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"}
package/dist/index.cjs CHANGED
@@ -1,10 +1,50 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_wait_for_health = require("./wait-for-health-DM55_NQh.cjs");
2
+ const require_wait_for_health = require("./wait-for-health-CObmNF8o.cjs");
3
3
  const require_runtime = require("./runtime-CV7pSfMl.cjs");
4
4
  //#region src/runtime-constants.ts
5
5
  /** Timeout for a single project runtime `/health` probe. */
6
6
  const RUNTIME_PING_TIMEOUT_MS = 15e3;
7
7
  //#endregion
8
+ //#region src/runtime-control.ts
9
+ const PROJECT_SERVER_CONTROL = {
10
+ load: "/control/projects/load",
11
+ promote: "/control/projects/promote",
12
+ unload: "/control/projects/unload",
13
+ port: require_wait_for_health.PROJECT_SERVER_PORT
14
+ };
15
+ async function postControl(baseUrl, path, body, options = {}) {
16
+ const timeoutMs = options.timeoutMs ?? 15e3;
17
+ const headers = { "content-type": "application/json" };
18
+ if (options.workerToken) headers.authorization = `Bearer ${options.workerToken}`;
19
+ const response = await (options.fetchImpl ?? fetch)(new URL(path, baseUrl), {
20
+ method: "POST",
21
+ headers,
22
+ body: JSON.stringify(body),
23
+ signal: AbortSignal.timeout(timeoutMs)
24
+ });
25
+ const data = await response.json().catch(() => ({}));
26
+ if (!response.ok || data.ok === false) throw new Error(typeof data.error === "string" ? data.error : `Runtime control ${path} failed (${response.status})`);
27
+ return data;
28
+ }
29
+ async function loadProjectOnRuntime(baseUrl, input, options = {}) {
30
+ await postControl(baseUrl, PROJECT_SERVER_CONTROL.load, input, options);
31
+ }
32
+ async function promoteProjectOnRuntime(baseUrl, input, options = {}) {
33
+ await postControl(baseUrl, PROJECT_SERVER_CONTROL.promote, input, options);
34
+ }
35
+ async function unloadProjectOnRuntime(baseUrl, input, options = {}) {
36
+ await postControl(baseUrl, PROJECT_SERVER_CONTROL.unload, input, options);
37
+ }
38
+ /** True when the org runtime answers /health. */
39
+ async function pingRuntimeHealth(baseUrl, options = {}) {
40
+ const timeoutMs = options.timeoutMs ?? 15e3;
41
+ try {
42
+ return (await (options.fetchImpl ?? fetch)(new URL("/health", baseUrl), { signal: AbortSignal.timeout(timeoutMs) })).ok;
43
+ } catch {
44
+ return false;
45
+ }
46
+ }
47
+ //#endregion
8
48
  //#region src/ping-project.ts
9
49
  async function pingProject(baseUrl, options = {}) {
10
50
  const timeoutMs = options.timeoutMs ?? 15e3;
@@ -33,10 +73,14 @@ async function pingProjectTarget(target, options = {}) {
33
73
  }
34
74
  //#endregion
35
75
  //#region src/deploy-status.ts
76
+ const DEFAULT_WAIT_TIMEOUT_MS = 12e4;
77
+ const DEFAULT_WAIT_INTERVAL_MS = 500;
78
+ const REQUEST_TIMEOUT_MS = 2e3;
36
79
  /**
37
80
  * Ask a running org machine which projects bootstrapped and which failed.
38
- * Best-effort: returns undefined on any error so callers can fall back to
39
- * treating every project as healthy (preserving pre-existing behavior).
81
+ * Returns undefined when the endpoint is unreachable, non-OK, times out, or
82
+ * returns an unusable payload callers must treat that as non-affirmative
83
+ * health for every pending project (never promote on silence).
40
84
  */
41
85
  async function fetchDeployStatus(baseUrl, options = {}) {
42
86
  const timeoutMs = options.timeoutMs ?? 15e3;
@@ -51,9 +95,73 @@ async function fetchDeployStatus(baseUrl, options = {}) {
51
95
  return;
52
96
  }
53
97
  }
98
+ /**
99
+ * Poll `/deploy-status` until the real worker returns a usable payload or the
100
+ * deadline expires. Retries only transient unavailability (network errors,
101
+ * non-2xx including early-health 404, timeouts, malformed bodies). Any valid
102
+ * `{ projects: [...] }` response — including empty or explicit failures — is
103
+ * terminal so genuine bootstrap errors surface immediately.
104
+ */
105
+ async function waitForDeployStatus(baseUrl, options = {}) {
106
+ const timeoutMs = options.timeoutMs ?? DEFAULT_WAIT_TIMEOUT_MS;
107
+ const intervalMs = options.intervalMs ?? DEFAULT_WAIT_INTERVAL_MS;
108
+ const deadline = Date.now() + timeoutMs;
109
+ while (Date.now() < deadline) {
110
+ const remainingMs = deadline - Date.now();
111
+ const requestTimeoutMs = Math.min(REQUEST_TIMEOUT_MS, Math.max(1, remainingMs));
112
+ const status = await fetchDeployStatus(baseUrl, {
113
+ fetchImpl: options.fetchImpl,
114
+ timeoutMs: requestTimeoutMs
115
+ });
116
+ if (status) return status;
117
+ if (Date.now() + intervalMs >= deadline) break;
118
+ await sleep(intervalMs);
119
+ }
120
+ }
121
+ function sleep(ms) {
122
+ return new Promise((resolve) => {
123
+ setTimeout(resolve, ms);
124
+ });
125
+ }
126
+ /**
127
+ * Require an explicit `{ projectId, ok: true }` entry for each candidate.
128
+ * Missing entries, `ok: false`, empty `projects`, or an unavailable status
129
+ * response all fail closed for that project.
130
+ */
131
+ function classifyDeployHealth(input) {
132
+ const verdicts = /* @__PURE__ */ new Map();
133
+ if (!input.deployStatus) {
134
+ for (const projectId of input.projectIds) verdicts.set(projectId, {
135
+ ok: false,
136
+ error: "Deploy health status unavailable"
137
+ });
138
+ return verdicts;
139
+ }
140
+ const byId = new Map(input.deployStatus.projects.filter((entry) => typeof entry?.projectId === "string" && entry.projectId.length > 0).map((entry) => [entry.projectId, entry]));
141
+ for (const projectId of input.projectIds) {
142
+ const entry = byId.get(projectId);
143
+ if (!entry) {
144
+ verdicts.set(projectId, {
145
+ ok: false,
146
+ error: "Deploy health status omitted project"
147
+ });
148
+ continue;
149
+ }
150
+ if (entry.ok !== true) {
151
+ verdicts.set(projectId, {
152
+ ok: false,
153
+ error: entry.error?.trim() || "Project failed to start"
154
+ });
155
+ continue;
156
+ }
157
+ verdicts.set(projectId, { ok: true });
158
+ }
159
+ return verdicts;
160
+ }
54
161
  //#endregion
55
162
  exports.DEV_PLATFORM_WORKER_TOKEN = require_wait_for_health.DEV_PLATFORM_WORKER_TOKEN;
56
163
  exports.FORBIDDEN_WORKER_ENV_KEYS = require_wait_for_health.FORBIDDEN_WORKER_ENV_KEYS;
164
+ exports.PROJECT_SERVER_CONTROL = PROJECT_SERVER_CONTROL;
57
165
  exports.PROJECT_SERVER_DEPLOY_STATUS = require_wait_for_health.PROJECT_SERVER_DEPLOY_STATUS;
58
166
  exports.PROJECT_SERVER_FRAMEWORK_NODE_MODULES = require_wait_for_health.PROJECT_SERVER_FRAMEWORK_NODE_MODULES;
59
167
  exports.PROJECT_SERVER_FRAMEWORK_ROOT = require_wait_for_health.PROJECT_SERVER_FRAMEWORK_ROOT;
@@ -65,13 +173,17 @@ exports.RUNTIME_PING_TIMEOUT_MS = RUNTIME_PING_TIMEOUT_MS;
65
173
  exports.WorkerRuntimeConfigError = require_wait_for_health.WorkerRuntimeConfigError;
66
174
  exports.buildRuntimeEnv = require_wait_for_health.buildRuntimeEnv;
67
175
  exports.canPingProjectTarget = canPingProjectTarget;
176
+ exports.classifyDeployHealth = classifyDeployHealth;
68
177
  exports.encodeOrgArtifacts = require_runtime.encodeOrgArtifacts;
69
178
  exports.fetchDeployStatus = fetchDeployStatus;
70
179
  exports.formatDockerEnv = require_wait_for_health.formatDockerEnv;
180
+ exports.loadProjectOnRuntime = loadProjectOnRuntime;
71
181
  exports.mintOrgWorkerToken = require_wait_for_health.mintOrgWorkerToken;
72
182
  exports.parseOrgArtifactsFromEnv = require_runtime.parseOrgArtifactsFromEnv;
73
183
  exports.pingProject = pingProject;
74
184
  exports.pingProjectTarget = pingProjectTarget;
185
+ exports.pingRuntimeHealth = pingRuntimeHealth;
186
+ exports.promoteProjectOnRuntime = promoteProjectOnRuntime;
75
187
  exports.resolvePlatformWorkerToken = require_wait_for_health.resolvePlatformWorkerToken;
76
188
  exports.resolveProjectServerImage = require_wait_for_health.resolveProjectServerImage;
77
189
  exports.resolveRuntimeLaunch = require_wait_for_health.resolveRuntimeLaunch;
@@ -79,7 +191,9 @@ exports.resolveWorkerPlatformUrl = require_wait_for_health.resolveWorkerPlatform
79
191
  exports.resolveWorkerRuntimeConfig = require_wait_for_health.resolveWorkerRuntimeConfig;
80
192
  exports.rewriteLoopbackHost = require_wait_for_health.rewriteLoopbackHost;
81
193
  exports.rewriteLoopbackUrl = require_wait_for_health.rewriteLoopbackUrl;
194
+ exports.unloadProjectOnRuntime = unloadProjectOnRuntime;
82
195
  exports.verifyOrgWorkerToken = require_wait_for_health.verifyOrgWorkerToken;
196
+ exports.waitForDeployStatus = waitForDeployStatus;
83
197
  exports.waitForHealth = require_wait_for_health.waitForHealth;
84
198
 
85
199
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["PROJECT_SERVER_HEALTH","PROJECT_SERVER_DEPLOY_STATUS"],"sources":["../src/runtime-constants.ts","../src/ping-project.ts","../src/ping-project-target.ts","../src/deploy-status.ts"],"sourcesContent":["/** Timeout for a single project runtime `/health` probe. */\nexport const RUNTIME_PING_TIMEOUT_MS = 15_000;\n","import { PROJECT_SERVER_HEALTH } from \"./constants\";\nimport type { HostingPlugin } from \"./plugin\";\nimport { RUNTIME_PING_TIMEOUT_MS } from \"./runtime-constants\";\n\nexport type PingProjectOptions = {\n runtimeId?: string | null;\n fetchImpl?: typeof fetch;\n timeoutMs?: number;\n plugin?: Pick<HostingPlugin, \"pingRequestHeaders\">;\n};\n\nexport async function pingProject(\n baseUrl: string,\n options: PingProjectOptions = {},\n): Promise<boolean> {\n const timeoutMs = options.timeoutMs ?? RUNTIME_PING_TIMEOUT_MS;\n const headers = options.plugin?.pingRequestHeaders?.(options.runtimeId ?? null) ?? {};\n\n try {\n const response = await (options.fetchImpl ?? fetch)(\n new URL(PROJECT_SERVER_HEALTH.path, baseUrl),\n {\n signal: AbortSignal.timeout(timeoutMs),\n headers,\n },\n );\n\n return response.ok;\n } catch {\n return false;\n }\n}\n","import type { HostingPlugin } from \"./plugin\";\nimport type { ProjectPingTarget } from \"./runtime\";\nimport { pingProject, type PingProjectOptions } from \"./ping-project\";\n\nexport function canPingProjectTarget(\n target: ProjectPingTarget,\n plugin?: Pick<HostingPlugin, \"canPingTarget\">,\n): target is { baseUrl: string; runtimeId: string | null } {\n if (plugin?.canPingTarget) {\n return plugin.canPingTarget(target);\n }\n\n return !!target.baseUrl;\n}\n\nexport async function pingProjectTarget(\n target: ProjectPingTarget,\n options: PingProjectOptions & {\n plugin?: Pick<HostingPlugin, \"canPingTarget\" | \"pingRequestHeaders\">;\n } = {},\n): Promise<boolean> {\n if (!canPingProjectTarget(target, options.plugin)) {\n return false;\n }\n\n return pingProject(target.baseUrl, {\n ...options,\n runtimeId: target.runtimeId,\n });\n}\n","import { PROJECT_SERVER_DEPLOY_STATUS } from \"./constants\";\nimport { RUNTIME_PING_TIMEOUT_MS } from \"./runtime-constants\";\n\n/** Per-project bootstrap outcome reported by an org runtime machine. */\nexport type ProjectDeployStatus = {\n projectId: string;\n ok: boolean;\n error?: string;\n};\n\nexport type DeployStatusResponse = {\n projects: ProjectDeployStatus[];\n};\n\nexport type FetchDeployStatusOptions = {\n fetchImpl?: typeof fetch;\n timeoutMs?: number;\n};\n\n/**\n * Ask a running org machine which projects bootstrapped and which failed.\n * Best-effort: returns undefined on any error so callers can fall back to\n * treating every project as healthy (preserving pre-existing behavior).\n */\nexport async function fetchDeployStatus(\n baseUrl: string,\n options: FetchDeployStatusOptions = {},\n): Promise<DeployStatusResponse | undefined> {\n const timeoutMs = options.timeoutMs ?? RUNTIME_PING_TIMEOUT_MS;\n\n try {\n const response = await (options.fetchImpl ?? fetch)(\n new URL(PROJECT_SERVER_DEPLOY_STATUS.path, baseUrl),\n { signal: AbortSignal.timeout(timeoutMs) },\n );\n\n if (!response.ok) {\n return undefined;\n }\n\n const data = (await response.json()) as DeployStatusResponse;\n if (!data || !Array.isArray(data.projects)) {\n return undefined;\n }\n\n return data;\n } catch (error) {\n // Best-effort: fall back to promote-all, but log so a consistently\n // unreachable /deploy-status (port/6PN/version skew) is diagnosable rather\n // than silently re-creating the pre-fix behavior.\n console.warn(`[deploy-status] failed to fetch from ${baseUrl}:`, error);\n return undefined;\n }\n}\n"],"mappings":";;;;;AACA,MAAa,0BAA0B;;;ACUvC,eAAsB,YACpB,SACA,UAA8B,CAAC,GACb;CAClB,MAAM,YAAY,QAAQ,aAAA;CAC1B,MAAM,UAAU,QAAQ,QAAQ,qBAAqB,QAAQ,aAAa,IAAI,KAAK,CAAC;CAEpF,IAAI;EASF,QAAO,OARiB,QAAQ,aAAa,OAC3C,IAAI,IAAIA,wBAAAA,sBAAsB,MAAM,OAAO,GAC3C;GACE,QAAQ,YAAY,QAAQ,SAAS;GACrC;EACF,CACF,GAEgB;CAClB,QAAQ;EACN,OAAO;CACT;AACF;;;AC3BA,SAAgB,qBACd,QACA,QACyD;CACzD,IAAI,QAAQ,eACV,OAAO,OAAO,cAAc,MAAM;CAGpC,OAAO,CAAC,CAAC,OAAO;AAClB;AAEA,eAAsB,kBACpB,QACA,UAEI,CAAC,GACa;CAClB,IAAI,CAAC,qBAAqB,QAAQ,QAAQ,MAAM,GAC9C,OAAO;CAGT,OAAO,YAAY,OAAO,SAAS;EACjC,GAAG;EACH,WAAW,OAAO;CACpB,CAAC;AACH;;;;;;;;ACLA,eAAsB,kBACpB,SACA,UAAoC,CAAC,GACM;CAC3C,MAAM,YAAY,QAAQ,aAAA;CAE1B,IAAI;EACF,MAAM,WAAW,OAAO,QAAQ,aAAa,OAC3C,IAAI,IAAIC,wBAAAA,6BAA6B,MAAM,OAAO,GAClD,EAAE,QAAQ,YAAY,QAAQ,SAAS,EAAE,CAC3C;EAEA,IAAI,CAAC,SAAS,IACZ;EAGF,MAAM,OAAQ,MAAM,SAAS,KAAK;EAClC,IAAI,CAAC,QAAQ,CAAC,MAAM,QAAQ,KAAK,QAAQ,GACvC;EAGF,OAAO;CACT,SAAS,OAAO;EAId,QAAQ,KAAK,wCAAwC,QAAQ,IAAI,KAAK;EACtE;CACF;AACF"}
1
+ {"version":3,"file":"index.cjs","names":["PROJECT_SERVER_PORT","PROJECT_SERVER_HEALTH","PROJECT_SERVER_DEPLOY_STATUS"],"sources":["../src/runtime-constants.ts","../src/runtime-control.ts","../src/ping-project.ts","../src/ping-project-target.ts","../src/deploy-status.ts"],"sourcesContent":["/** Timeout for a single project runtime `/health` probe. */\nexport const RUNTIME_PING_TIMEOUT_MS = 15_000;\n","import { PROJECT_SERVER_PORT } from \"./constants\";\nimport { RUNTIME_PING_TIMEOUT_MS } from \"./runtime-constants\";\n\nexport const PROJECT_SERVER_CONTROL = {\n load: \"/control/projects/load\",\n promote: \"/control/projects/promote\",\n unload: \"/control/projects/unload\",\n port: PROJECT_SERVER_PORT,\n} as const;\n\nexport type LoadProjectOnRuntimeInput = {\n projectId: string;\n artifactId: string;\n artifactVersion: number;\n storageKey: string;\n activate?: boolean;\n};\n\nexport type RuntimeControlOptions = {\n fetchImpl?: typeof fetch;\n timeoutMs?: number;\n /** Bearer token — typically the org's WORKER_INTERNAL_TOKEN. */\n workerToken?: string;\n};\n\nasync function postControl<T>(\n baseUrl: string,\n path: string,\n body: unknown,\n options: RuntimeControlOptions = {},\n): Promise<T> {\n const timeoutMs = options.timeoutMs ?? RUNTIME_PING_TIMEOUT_MS;\n const headers: Record<string, string> = {\n \"content-type\": \"application/json\",\n };\n if (options.workerToken) {\n headers.authorization = `Bearer ${options.workerToken}`;\n }\n\n const response = await (options.fetchImpl ?? fetch)(new URL(path, baseUrl), {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(timeoutMs),\n });\n\n const data = (await response.json().catch(() => ({}))) as T & { ok?: boolean; error?: string };\n if (!response.ok || data.ok === false) {\n throw new Error(\n typeof data.error === \"string\"\n ? data.error\n : `Runtime control ${path} failed (${response.status})`,\n );\n }\n\n return data;\n}\n\nexport async function loadProjectOnRuntime(\n baseUrl: string,\n input: LoadProjectOnRuntimeInput,\n options: RuntimeControlOptions = {},\n): Promise<void> {\n await postControl(baseUrl, PROJECT_SERVER_CONTROL.load, input, options);\n}\n\nexport async function promoteProjectOnRuntime(\n baseUrl: string,\n input: { projectId: string; artifactId: string },\n options: RuntimeControlOptions = {},\n): Promise<void> {\n await postControl(baseUrl, PROJECT_SERVER_CONTROL.promote, input, options);\n}\n\nexport async function unloadProjectOnRuntime(\n baseUrl: string,\n input: { projectId: string; artifactId: string; force?: boolean },\n options: RuntimeControlOptions = {},\n): Promise<void> {\n await postControl(baseUrl, PROJECT_SERVER_CONTROL.unload, input, options);\n}\n\n/** True when the org runtime answers /health. */\nexport async function pingRuntimeHealth(\n baseUrl: string,\n options: RuntimeControlOptions = {},\n): Promise<boolean> {\n const timeoutMs = options.timeoutMs ?? RUNTIME_PING_TIMEOUT_MS;\n try {\n const response = await (options.fetchImpl ?? fetch)(new URL(\"/health\", baseUrl), {\n signal: AbortSignal.timeout(timeoutMs),\n });\n return response.ok;\n } catch {\n return false;\n }\n}\n","import { PROJECT_SERVER_HEALTH } from \"./constants\";\nimport type { HostingPlugin } from \"./plugin\";\nimport { RUNTIME_PING_TIMEOUT_MS } from \"./runtime-constants\";\n\nexport type PingProjectOptions = {\n runtimeId?: string | null;\n fetchImpl?: typeof fetch;\n timeoutMs?: number;\n plugin?: Pick<HostingPlugin, \"pingRequestHeaders\">;\n};\n\nexport async function pingProject(\n baseUrl: string,\n options: PingProjectOptions = {},\n): Promise<boolean> {\n const timeoutMs = options.timeoutMs ?? RUNTIME_PING_TIMEOUT_MS;\n const headers = options.plugin?.pingRequestHeaders?.(options.runtimeId ?? null) ?? {};\n\n try {\n const response = await (options.fetchImpl ?? fetch)(\n new URL(PROJECT_SERVER_HEALTH.path, baseUrl),\n {\n signal: AbortSignal.timeout(timeoutMs),\n headers,\n },\n );\n\n return response.ok;\n } catch {\n return false;\n }\n}\n","import type { HostingPlugin } from \"./plugin\";\nimport type { ProjectPingTarget } from \"./runtime\";\nimport { pingProject, type PingProjectOptions } from \"./ping-project\";\n\nexport function canPingProjectTarget(\n target: ProjectPingTarget,\n plugin?: Pick<HostingPlugin, \"canPingTarget\">,\n): target is { baseUrl: string; runtimeId: string | null } {\n if (plugin?.canPingTarget) {\n return plugin.canPingTarget(target);\n }\n\n return !!target.baseUrl;\n}\n\nexport async function pingProjectTarget(\n target: ProjectPingTarget,\n options: PingProjectOptions & {\n plugin?: Pick<HostingPlugin, \"canPingTarget\" | \"pingRequestHeaders\">;\n } = {},\n): Promise<boolean> {\n if (!canPingProjectTarget(target, options.plugin)) {\n return false;\n }\n\n return pingProject(target.baseUrl, {\n ...options,\n runtimeId: target.runtimeId,\n });\n}\n","import { PROJECT_SERVER_DEPLOY_STATUS } from \"./constants\";\nimport { RUNTIME_PING_TIMEOUT_MS } from \"./runtime-constants\";\n\nconst DEFAULT_WAIT_TIMEOUT_MS = 120_000;\nconst DEFAULT_WAIT_INTERVAL_MS = 500;\nconst REQUEST_TIMEOUT_MS = 2_000;\n\n/** Per-project bootstrap outcome reported by an org runtime machine. */\nexport type ProjectDeployStatus = {\n projectId: string;\n ok: boolean;\n error?: string;\n};\n\nexport type VersionDeployStatus = {\n projectId: string;\n artifactId: string;\n state: \"active\" | \"resident\" | \"indexed\";\n ok: boolean;\n activeJobCount?: number;\n error?: string;\n};\n\nexport type DeployStatusResponse = {\n projects: ProjectDeployStatus[];\n versions?: VersionDeployStatus[];\n};\n\nexport type FetchDeployStatusOptions = {\n fetchImpl?: typeof fetch;\n timeoutMs?: number;\n};\n\nexport type WaitForDeployStatusOptions = {\n fetchImpl?: typeof fetch;\n timeoutMs?: number;\n intervalMs?: number;\n};\n\n/**\n * Ask a running org machine which projects bootstrapped and which failed.\n * Returns undefined when the endpoint is unreachable, non-OK, times out, or\n * returns an unusable payload — callers must treat that as non-affirmative\n * health for every pending project (never promote on silence).\n */\nexport async function fetchDeployStatus(\n baseUrl: string,\n options: FetchDeployStatusOptions = {},\n): Promise<DeployStatusResponse | undefined> {\n const timeoutMs = options.timeoutMs ?? RUNTIME_PING_TIMEOUT_MS;\n\n try {\n const response = await (options.fetchImpl ?? fetch)(\n new URL(PROJECT_SERVER_DEPLOY_STATUS.path, baseUrl),\n { signal: AbortSignal.timeout(timeoutMs) },\n );\n\n if (!response.ok) {\n return undefined;\n }\n\n const data = (await response.json()) as DeployStatusResponse;\n if (!data || !Array.isArray(data.projects)) {\n return undefined;\n }\n\n return data;\n } catch (error) {\n console.warn(`[deploy-status] failed to fetch from ${baseUrl}:`, error);\n return undefined;\n }\n}\n\n/**\n * Poll `/deploy-status` until the real worker returns a usable payload or the\n * deadline expires. Retries only transient unavailability (network errors,\n * non-2xx including early-health 404, timeouts, malformed bodies). Any valid\n * `{ projects: [...] }` response — including empty or explicit failures — is\n * terminal so genuine bootstrap errors surface immediately.\n */\nexport async function waitForDeployStatus(\n baseUrl: string,\n options: WaitForDeployStatusOptions = {},\n): Promise<DeployStatusResponse | undefined> {\n const timeoutMs = options.timeoutMs ?? DEFAULT_WAIT_TIMEOUT_MS;\n const intervalMs = options.intervalMs ?? DEFAULT_WAIT_INTERVAL_MS;\n const deadline = Date.now() + timeoutMs;\n\n while (Date.now() < deadline) {\n const remainingMs = deadline - Date.now();\n const requestTimeoutMs = Math.min(REQUEST_TIMEOUT_MS, Math.max(1, remainingMs));\n const status = await fetchDeployStatus(baseUrl, {\n fetchImpl: options.fetchImpl,\n timeoutMs: requestTimeoutMs,\n });\n if (status) {\n return status;\n }\n\n if (Date.now() + intervalMs >= deadline) {\n break;\n }\n await sleep(intervalMs);\n }\n\n return undefined;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n\nexport type DeployHealthVerdict = { ok: true } | { ok: false; error: string };\n\n/**\n * Require an explicit `{ projectId, ok: true }` entry for each candidate.\n * Missing entries, `ok: false`, empty `projects`, or an unavailable status\n * response all fail closed for that project.\n */\nexport function classifyDeployHealth(input: {\n projectIds: string[];\n deployStatus: DeployStatusResponse | undefined;\n}): Map<string, DeployHealthVerdict> {\n const verdicts = new Map<string, DeployHealthVerdict>();\n\n if (!input.deployStatus) {\n for (const projectId of input.projectIds) {\n verdicts.set(projectId, {\n ok: false,\n error: \"Deploy health status unavailable\",\n });\n }\n return verdicts;\n }\n\n const byId = new Map(\n input.deployStatus.projects\n .filter((entry) => typeof entry?.projectId === \"string\" && entry.projectId.length > 0)\n .map((entry) => [entry.projectId, entry]),\n );\n\n for (const projectId of input.projectIds) {\n const entry = byId.get(projectId);\n if (!entry) {\n verdicts.set(projectId, {\n ok: false,\n error: \"Deploy health status omitted project\",\n });\n continue;\n }\n if (entry.ok !== true) {\n verdicts.set(projectId, {\n ok: false,\n error: entry.error?.trim() || \"Project failed to start\",\n });\n continue;\n }\n verdicts.set(projectId, { ok: true });\n }\n\n return verdicts;\n}\n"],"mappings":"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"}
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { a as ProjectRuntimeDatabase, c as ProjectRuntimeInput, d as encodeOrgArtifacts, f as parseOrgArtifactsFromEnv, i as ProjectRuntime, l as ProjectRuntimeReconcileInput, n as OrgProjectDeploy, o as ProjectRuntimeDestroyInput, r as ProjectPingTarget, s as ProjectRuntimeHosting, t as OrgArtifactSpec, u as ProjectRuntimeResult } from "./runtime-B0jF0oMv.cjs";
2
- import { a as WorkerRuntimeConfig, i as OrganizationHostingResult, n as HostingPlugin, o as WorkerRuntimeConfigError, r as OrganizationHostingInput, s as resolveWorkerRuntimeConfig, t as HostingOptions } from "./plugin-is5oPt6x.cjs";
1
+ import { a as ProjectRuntimeDatabase, c as ProjectRuntimeInput, d as ProjectRuntimeSleepInput, f as ProjectRuntimeWakeInput, i as ProjectRuntime, l as ProjectRuntimeReconcileInput, m as parseOrgArtifactsFromEnv, n as OrgProjectDeploy, o as ProjectRuntimeDestroyInput, p as encodeOrgArtifacts, r as ProjectPingTarget, s as ProjectRuntimeHosting, t as OrgArtifactSpec, u as ProjectRuntimeResult } from "./runtime-Cp906goK.cjs";
2
+ import { a as WorkerRuntimeConfig, i as OrganizationHostingResult, n as HostingPlugin, o as WorkerRuntimeConfigError, r as OrganizationHostingInput, s as resolveWorkerRuntimeConfig, t as HostingOptions } from "./plugin-4cuXkf42.cjs";
3
3
 
4
4
  //#region src/constants.d.ts
5
5
  declare const PROJECT_SERVER_IMAGE = "keystroke/project-server:local";
@@ -16,9 +16,41 @@ declare const PROJECT_SERVER_DEPLOY_STATUS: {
16
16
  readonly port: 3000;
17
17
  };
18
18
  //#endregion
19
+ //#region src/runtime-control.d.ts
20
+ declare const PROJECT_SERVER_CONTROL: {
21
+ readonly load: "/control/projects/load";
22
+ readonly promote: "/control/projects/promote";
23
+ readonly unload: "/control/projects/unload";
24
+ readonly port: 3000;
25
+ };
26
+ type LoadProjectOnRuntimeInput = {
27
+ projectId: string;
28
+ artifactId: string;
29
+ artifactVersion: number;
30
+ storageKey: string;
31
+ activate?: boolean;
32
+ };
33
+ type RuntimeControlOptions = {
34
+ fetchImpl?: typeof fetch;
35
+ timeoutMs?: number; /** Bearer token — typically the org's WORKER_INTERNAL_TOKEN. */
36
+ workerToken?: string;
37
+ };
38
+ declare function loadProjectOnRuntime(baseUrl: string, input: LoadProjectOnRuntimeInput, options?: RuntimeControlOptions): Promise<void>;
39
+ declare function promoteProjectOnRuntime(baseUrl: string, input: {
40
+ projectId: string;
41
+ artifactId: string;
42
+ }, options?: RuntimeControlOptions): Promise<void>;
43
+ declare function unloadProjectOnRuntime(baseUrl: string, input: {
44
+ projectId: string;
45
+ artifactId: string;
46
+ force?: boolean;
47
+ }, options?: RuntimeControlOptions): Promise<void>;
48
+ /** True when the org runtime answers /health. */
49
+ declare function pingRuntimeHealth(baseUrl: string, options?: RuntimeControlOptions): Promise<boolean>;
50
+ //#endregion
19
51
  //#region src/runtime-env.d.ts
20
52
  /** Env keys that must never be forwarded into untrusted org workers. */
21
- declare const FORBIDDEN_WORKER_ENV_KEYS: readonly ["BETTER_AUTH_SECRET", "DATABASE_URL", "POSTHOG_API_KEY", "POSTGRES_PASSWORD", "PLATFORM_WORKER_TOKEN", "STRIPE_SECRET_KEY", "CREDENTIAL_ENCRYPTION_KEY"];
53
+ declare const FORBIDDEN_WORKER_ENV_KEYS: readonly ["BETTER_AUTH_SECRET", "DATABASE_URL", "POSTHOG_API_KEY", "POSTGRES_PASSWORD", "PLATFORM_WORKER_TOKEN", "STRIPE_SECRET_KEY", "CREDENTIAL_ENCRYPTION_KEY", "TURNSTILE_SECRET"];
22
54
  /** Shared dev token when `PLATFORM_WORKER_TOKEN` is unset (non-production only). */
23
55
  declare const DEV_PLATFORM_WORKER_TOKEN = "dev-platform-worker-token";
24
56
  type RuntimeLaunchSpec = {
@@ -93,19 +125,57 @@ type ProjectDeployStatus = {
93
125
  ok: boolean;
94
126
  error?: string;
95
127
  };
128
+ type VersionDeployStatus = {
129
+ projectId: string;
130
+ artifactId: string;
131
+ state: "active" | "resident" | "indexed";
132
+ ok: boolean;
133
+ activeJobCount?: number;
134
+ error?: string;
135
+ };
96
136
  type DeployStatusResponse = {
97
137
  projects: ProjectDeployStatus[];
138
+ versions?: VersionDeployStatus[];
98
139
  };
99
140
  type FetchDeployStatusOptions = {
100
141
  fetchImpl?: typeof fetch;
101
142
  timeoutMs?: number;
102
143
  };
144
+ type WaitForDeployStatusOptions = {
145
+ fetchImpl?: typeof fetch;
146
+ timeoutMs?: number;
147
+ intervalMs?: number;
148
+ };
103
149
  /**
104
150
  * Ask a running org machine which projects bootstrapped and which failed.
105
- * Best-effort: returns undefined on any error so callers can fall back to
106
- * treating every project as healthy (preserving pre-existing behavior).
151
+ * Returns undefined when the endpoint is unreachable, non-OK, times out, or
152
+ * returns an unusable payload callers must treat that as non-affirmative
153
+ * health for every pending project (never promote on silence).
107
154
  */
108
155
  declare function fetchDeployStatus(baseUrl: string, options?: FetchDeployStatusOptions): Promise<DeployStatusResponse | undefined>;
156
+ /**
157
+ * Poll `/deploy-status` until the real worker returns a usable payload or the
158
+ * deadline expires. Retries only transient unavailability (network errors,
159
+ * non-2xx including early-health 404, timeouts, malformed bodies). Any valid
160
+ * `{ projects: [...] }` response — including empty or explicit failures — is
161
+ * terminal so genuine bootstrap errors surface immediately.
162
+ */
163
+ declare function waitForDeployStatus(baseUrl: string, options?: WaitForDeployStatusOptions): Promise<DeployStatusResponse | undefined>;
164
+ type DeployHealthVerdict = {
165
+ ok: true;
166
+ } | {
167
+ ok: false;
168
+ error: string;
169
+ };
170
+ /**
171
+ * Require an explicit `{ projectId, ok: true }` entry for each candidate.
172
+ * Missing entries, `ok: false`, empty `projects`, or an unavailable status
173
+ * response all fail closed for that project.
174
+ */
175
+ declare function classifyDeployHealth(input: {
176
+ projectIds: string[];
177
+ deployStatus: DeployStatusResponse | undefined;
178
+ }): Map<string, DeployHealthVerdict>;
109
179
  //#endregion
110
- export { DEV_PLATFORM_WORKER_TOKEN, type DeployStatusResponse, FORBIDDEN_WORKER_ENV_KEYS, type FetchDeployStatusOptions, type HostingOptions, type HostingPlugin, type OrgArtifactSpec, type OrgProjectDeploy, type OrganizationHostingInput, type OrganizationHostingResult, PROJECT_SERVER_DEPLOY_STATUS, PROJECT_SERVER_FRAMEWORK_NODE_MODULES, PROJECT_SERVER_FRAMEWORK_ROOT, PROJECT_SERVER_HEALTH, PROJECT_SERVER_IMAGE, PROJECT_SERVER_PORT, PROJECT_SERVER_ROOT, type PingProjectOptions, type ProjectDeployStatus, type ProjectPingTarget, type ProjectRuntime, type ProjectRuntimeDatabase, type ProjectRuntimeDestroyInput, type ProjectRuntimeHosting, type ProjectRuntimeInput, type ProjectRuntimeReconcileInput, type ProjectRuntimeResult, RUNTIME_PING_TIMEOUT_MS, type RuntimeLaunchSpec, type VerifiedOrgWorkerToken, type WaitForHealthOptions, type WorkerRuntimeConfig, WorkerRuntimeConfigError, buildRuntimeEnv, canPingProjectTarget, encodeOrgArtifacts, fetchDeployStatus, formatDockerEnv, mintOrgWorkerToken, parseOrgArtifactsFromEnv, pingProject, pingProjectTarget, resolvePlatformWorkerToken, resolveProjectServerImage, resolveRuntimeLaunch, resolveWorkerPlatformUrl, resolveWorkerRuntimeConfig, rewriteLoopbackHost, rewriteLoopbackUrl, verifyOrgWorkerToken, waitForHealth };
180
+ export { DEV_PLATFORM_WORKER_TOKEN, type DeployHealthVerdict, type DeployStatusResponse, FORBIDDEN_WORKER_ENV_KEYS, type FetchDeployStatusOptions, type HostingOptions, type HostingPlugin, type LoadProjectOnRuntimeInput, type OrgArtifactSpec, type OrgProjectDeploy, type OrganizationHostingInput, type OrganizationHostingResult, PROJECT_SERVER_CONTROL, PROJECT_SERVER_DEPLOY_STATUS, PROJECT_SERVER_FRAMEWORK_NODE_MODULES, PROJECT_SERVER_FRAMEWORK_ROOT, PROJECT_SERVER_HEALTH, PROJECT_SERVER_IMAGE, PROJECT_SERVER_PORT, PROJECT_SERVER_ROOT, type PingProjectOptions, type ProjectDeployStatus, type ProjectPingTarget, type ProjectRuntime, type ProjectRuntimeDatabase, type ProjectRuntimeDestroyInput, type ProjectRuntimeHosting, type ProjectRuntimeInput, type ProjectRuntimeReconcileInput, type ProjectRuntimeResult, type ProjectRuntimeSleepInput, type ProjectRuntimeWakeInput, RUNTIME_PING_TIMEOUT_MS, type RuntimeControlOptions, type RuntimeLaunchSpec, type VerifiedOrgWorkerToken, type VersionDeployStatus, type WaitForDeployStatusOptions, type WaitForHealthOptions, type WorkerRuntimeConfig, WorkerRuntimeConfigError, buildRuntimeEnv, canPingProjectTarget, classifyDeployHealth, encodeOrgArtifacts, fetchDeployStatus, formatDockerEnv, loadProjectOnRuntime, mintOrgWorkerToken, parseOrgArtifactsFromEnv, pingProject, pingProjectTarget, pingRuntimeHealth, promoteProjectOnRuntime, resolvePlatformWorkerToken, resolveProjectServerImage, resolveRuntimeLaunch, resolveWorkerPlatformUrl, resolveWorkerRuntimeConfig, rewriteLoopbackHost, rewriteLoopbackUrl, unloadProjectOnRuntime, verifyOrgWorkerToken, waitForDeployStatus, waitForHealth };
111
181
  //# sourceMappingURL=index.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/constants.ts","../src/runtime-env.ts","../src/org-worker-token.ts","../src/wait-for-health.ts","../src/runtime-constants.ts","../src/ping-project.ts","../src/ping-project-target.ts","../src/deploy-status.ts"],"mappings":";;;;cAAa,oBAAA;AAAA,cAEA,mBAAA;AAAA,cAEA,mBAAA;AAAA,cAEA,6BAAA;AAAA,cAEA,qCAAA;AAAA,cAEA,qBAAA;EAAA,SAGH,IAAA;EAAA,SAAA,IAAA;AAAA;AAAA,cAEG,4BAAA;EAAA,SAGH,IAAA;EAAA,SAAA,IAAA;AAAA;;;AAlBV;AAAA,cCyCa,yBAAA;;cAmBA,yBAAA;AAAA,KAED,iBAAA;EACV,KAAA;EACA,GAAA,EAAK,MAAA;EACL,IAAA;EACA,MAAA,SAAe,qBAAqB;AAAA;AD9DtC;;;;AAAA,iBCqEgB,oBAAA,CACd,OAAA,EAAS,cAAA,EACT,SAAA,EAAW,eAAA,IACX,QAAA,EAAU,sBAAA,EACV,QAAA,GAAW,MAAA,mBACV,iBAAA;;iBAsCa,eAAA,CACd,MAAA,EAAQ,MAAA,CAAO,UAAA,EACf,SAAA,EAAW,eAAA,IACX,QAAA,EAAU,sBAAA,EACV,QAAA,GAAW,MAAA,mBACV,MAAA;AAAA,iBA2Da,0BAAA,CACd,MAAA,GAAQ,MAAA,CAAO,UAAwB;;iBAezB,wBAAA,CACd,MAAA,GAAQ,MAAA,CAAO,UAAwB;;iBAczB,mBAAA,CAAoB,IAAY;AAAA,iBAQhC,kBAAA,CAAmB,GAAW;;iBAW9B,eAAA,CAAgB,GAA2B,EAAtB,MAAM;AAAA,iBAI3B,yBAAA,CACd,GAAA,GAAK,MAAA,CAAO,UAAwB,EACpC,QAAA;;;;;;;AD5OF;iBEWgB,kBAAA,CAAmB,MAAA,UAAgB,cAAsB;AAAA,KAI7D,sBAAA;EACV,cAAc;AAAA;AAAA,iBAGA,oBAAA,CACd,MAAA,sBACA,KAAA,uBACC,sBAAsB;;;KCjBb,oBAAA;EACV,SAAA;EACA,UAAA;EACA,SAAA,UAAmB,KAAK;AAAA;AAAA,iBAGJ,aAAA,CACpB,OAAA,UACA,OAAA,GAAS,oBAAA,GACR,OAAO;;;;cCbG,uBAAA;;;KCGD,kBAAA;EACV,SAAA;EACA,SAAA,UAAmB,KAAA;EACnB,SAAA;EACA,MAAA,GAAS,IAAA,CAAK,aAAA;AAAA;AAAA,iBAGM,WAAA,CACpB,OAAA,UACA,OAAA,GAAS,kBAAA,GACR,OAAO;;;iBCVM,oBAAA,CACd,MAAA,EAAQ,iBAAA,EACR,MAAA,GAAS,IAAA,CAAK,aAAA,qBACb,MAAA;EAAY,OAAA;EAAiB,SAAA;AAAA;AAAA,iBAQV,iBAAA,CACpB,MAAA,EAAQ,iBAAA,EACR,OAAA,GAAS,kBAAA;EACP,MAAA,GAAS,IAAA,CAAK,aAAA;AAAA,IAEf,OAAA;;;;KChBS,mBAAA;EACV,SAAA;EACA,EAAA;EACA,KAAA;AAAA;AAAA,KAGU,oBAAA;EACV,QAAA,EAAU,mBAAmB;AAAA;AAAA,KAGnB,wBAAA;EACV,SAAA,UAAmB,KAAK;EACxB,SAAA;AAAA;APd8B;AAEhC;;;;AAFgC,iBOsBV,iBAAA,CACpB,OAAA,UACA,OAAA,GAAS,wBAAA,GACR,OAAA,CAAQ,oBAAA"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/constants.ts","../src/runtime-control.ts","../src/runtime-env.ts","../src/org-worker-token.ts","../src/wait-for-health.ts","../src/runtime-constants.ts","../src/ping-project.ts","../src/ping-project-target.ts","../src/deploy-status.ts"],"mappings":"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"}