withruntime 0.11.2 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. package/BLAXEL.md +2 -2
  2. package/COMPATIBILITY.md +74 -9
  3. package/DAYTONA.md +33 -5
  4. package/E2B.md +85 -24
  5. package/README.md +9 -8
  6. package/README.src.md +8 -7
  7. package/VERCEL.md +43 -5
  8. package/compatibility-lock.json +58 -49
  9. package/dist/billing-cli.d.ts +1 -1
  10. package/dist/billing-cli.js +77 -1
  11. package/dist/blaxel/errors.d.ts +14 -0
  12. package/dist/blaxel/errors.js +29 -0
  13. package/dist/blaxel/index.d.ts +2 -2
  14. package/dist/blaxel/index.js +4 -4
  15. package/dist/blaxel/interpreter.d.ts +2 -5
  16. package/dist/blaxel/sandbox.d.ts +14 -5
  17. package/dist/blaxel/sandbox.js +63 -7
  18. package/dist/blaxel/snapshot.d.ts +4 -0
  19. package/dist/blaxel/snapshot.js +4 -0
  20. package/dist/cli/logs.d.ts +21 -0
  21. package/dist/cli/logs.js +188 -0
  22. package/dist/cli/logs.test.d.ts +1 -0
  23. package/dist/cli/logs.test.js +272 -0
  24. package/dist/cli-extras.d.ts +3 -3
  25. package/dist/cli-extras.js +17 -12
  26. package/dist/cli-words.d.ts +10 -1
  27. package/dist/cli-words.js +18 -0
  28. package/dist/cli.d.ts +5 -1
  29. package/dist/cli.js +405 -204
  30. package/dist/client.d.ts +2 -4
  31. package/dist/client.js +27 -11
  32. package/dist/cloudflare/errors.d.ts +143 -0
  33. package/dist/cloudflare/errors.js +167 -0
  34. package/dist/cloudflare/index.d.ts +175 -6
  35. package/dist/cloudflare/index.js +416 -26
  36. package/dist/cloudflare/interpreter.d.ts +119 -0
  37. package/dist/cloudflare/interpreter.js +223 -0
  38. package/dist/cloudflare/streams.d.ts +38 -0
  39. package/dist/cloudflare/streams.js +145 -0
  40. package/dist/cloudflare/watch.d.ts +9 -0
  41. package/dist/cloudflare/watch.js +24 -0
  42. package/dist/codesandbox/index.d.ts +216 -28
  43. package/dist/codesandbox/index.js +478 -78
  44. package/dist/compat/core.d.ts +18 -0
  45. package/dist/compat/core.js +29 -0
  46. package/dist/compat/files.d.ts +1 -0
  47. package/dist/compat/files.js +25 -5
  48. package/dist/daytona/computer-use.d.ts +13 -4
  49. package/dist/daytona/computer-use.js +66 -8
  50. package/dist/daytona/daytona.d.ts +6 -0
  51. package/dist/daytona/daytona.js +30 -3
  52. package/dist/daytona/errors.d.ts +11 -0
  53. package/dist/daytona/errors.js +18 -0
  54. package/dist/daytona/filesystem.js +2 -1
  55. package/dist/daytona/image.d.ts +13 -9
  56. package/dist/daytona/image.js +18 -12
  57. package/dist/daytona/index.d.ts +1 -1
  58. package/dist/daytona/sandbox.d.ts +8 -2
  59. package/dist/daytona/sandbox.js +16 -2
  60. package/dist/domains/index.d.ts +9 -0
  61. package/dist/domains/index.js +10 -0
  62. package/dist/e2b/commands.d.ts +1 -1
  63. package/dist/e2b/commands.js +6 -7
  64. package/dist/e2b/errors.d.ts +17 -0
  65. package/dist/e2b/errors.js +40 -1
  66. package/dist/e2b/index.d.ts +2 -2
  67. package/dist/e2b/index.js +1 -1
  68. package/dist/e2b/sandbox.d.ts +27 -21
  69. package/dist/e2b/sandbox.js +34 -8
  70. package/dist/e2b/unsupported.js +1 -1
  71. package/dist/errors.d.ts +10 -1
  72. package/dist/errors.js +16 -4
  73. package/dist/freestyle/index.d.ts +285 -37
  74. package/dist/freestyle/index.js +656 -144
  75. package/dist/identity.d.ts +3 -0
  76. package/dist/identity.js +27 -0
  77. package/dist/images/index.d.ts +9 -0
  78. package/dist/images/index.js +10 -0
  79. package/dist/index.d.ts +1 -0
  80. package/dist/ips/index.d.ts +9 -0
  81. package/dist/ips/index.js +10 -0
  82. package/dist/jobs/index.d.ts +9 -0
  83. package/dist/jobs/index.js +10 -0
  84. package/dist/keys.js +2 -0
  85. package/dist/login.js +2 -0
  86. package/dist/machine.d.ts +12 -3
  87. package/dist/machine.js +20 -3
  88. package/dist/mcp.js +1 -1
  89. package/dist/modal/index.d.ts +339 -61
  90. package/dist/modal/index.js +781 -121
  91. package/dist/network-cli.d.ts +3 -1
  92. package/dist/network-cli.js +78 -36
  93. package/dist/networks/index.d.ts +11 -0
  94. package/dist/networks/index.js +10 -0
  95. package/dist/ports/index.d.ts +9 -0
  96. package/dist/ports/index.js +10 -0
  97. package/dist/product-names.d.ts +52 -0
  98. package/dist/product-names.js +164 -0
  99. package/dist/product-renames.d.ts +103 -0
  100. package/dist/product-renames.js +189 -0
  101. package/dist/products/apps.d.ts +176 -0
  102. package/dist/products/apps.js +165 -0
  103. package/dist/products/billing.d.ts +41 -0
  104. package/dist/products/billing.js +13 -0
  105. package/dist/products/browser.d.ts +10 -0
  106. package/dist/products/browsers.d.ts +71 -0
  107. package/dist/products/browsers.js +102 -0
  108. package/dist/products/builds.d.ts +126 -0
  109. package/dist/products/builds.js +221 -0
  110. package/dist/products/index.d.ts +3 -16
  111. package/dist/products/index.js +3 -14
  112. package/dist/products/jobs.d.ts +17 -0
  113. package/dist/products/jobs.js +27 -10
  114. package/dist/products/logs.d.ts +245 -0
  115. package/dist/products/logs.js +274 -0
  116. package/dist/products/logs.test.d.ts +1 -0
  117. package/dist/products/logs.test.js +343 -0
  118. package/dist/products/network-products.d.ts +38 -7
  119. package/dist/products/network-products.js +31 -18
  120. package/dist/products/observability.d.ts +3 -2
  121. package/dist/products/private-network.js +1 -1
  122. package/dist/products/queues.d.ts +149 -0
  123. package/dist/products/queues.js +213 -0
  124. package/dist/products/referrals.d.ts +21 -7
  125. package/dist/products/referrals.js +3 -3
  126. package/dist/products/secrets.d.ts +7 -3
  127. package/dist/products/secrets.js +39 -18
  128. package/dist/products/signed-requests.d.ts +21 -0
  129. package/dist/products/signed-requests.js +11 -0
  130. package/dist/products/streams.d.ts +73 -0
  131. package/dist/products/streams.js +17 -0
  132. package/dist/products/switching.d.ts +35 -7
  133. package/dist/products/switching.js +4 -3
  134. package/dist/products/volumes.d.ts +3 -0
  135. package/dist/record-cli.d.ts +23 -0
  136. package/dist/record-cli.js +308 -0
  137. package/dist/renamed-products.d.ts +38 -0
  138. package/dist/renamed-products.js +34 -0
  139. package/dist/runloop/index.d.ts +9 -2
  140. package/dist/runloop/index.js +29 -8
  141. package/dist/runloop/sdk.d.ts +131 -0
  142. package/dist/runloop/sdk.js +167 -0
  143. package/dist/sandbox.d.ts +3 -2
  144. package/dist/sandbox.js +14 -9
  145. package/dist/sandboxes/index.d.ts +11 -0
  146. package/dist/sandboxes/index.js +12 -0
  147. package/dist/snapshots/index.d.ts +9 -0
  148. package/dist/snapshots/index.js +11 -0
  149. package/dist/snapshots.d.ts +7 -4
  150. package/dist/sprites/index.d.ts +343 -31
  151. package/dist/sprites/index.js +866 -84
  152. package/dist/ssh.d.ts +1 -1
  153. package/dist/ssh.js +11 -11
  154. package/dist/terminal.d.ts +24 -0
  155. package/dist/terminal.js +32 -0
  156. package/dist/transport.d.ts +15 -1
  157. package/dist/transport.js +56 -3
  158. package/dist/tunnel.js +1 -1
  159. package/dist/types.d.ts +63 -0
  160. package/dist/vercel/sandbox.js +5 -3
  161. package/dist/vercel/snapshot.js +10 -2
  162. package/dist/volumes/index.d.ts +9 -0
  163. package/dist/volumes/index.js +10 -0
  164. package/package.json +48 -2
@@ -0,0 +1,103 @@
1
+ /** CLOUD.md section 4: an old product word keeps working for ALIAS_DAYS after
2
+ * its rename ships, with a warning naming the new word, and then fails with
3
+ * `name_retired` and a hint. One implementation for the API's paths, the MCP's
4
+ * tool names, the CLI's commands and the SDK's properties; each reads the table in
5
+ * product-names.ts. This file imports nothing else, so the CLI can carry it. */
6
+ import { type ProductName } from "./product-names.js";
7
+ export type Surface = "api" | "mcp" | "cli" | "sdk";
8
+ export type RetiredName = {
9
+ readonly code: "name_retired";
10
+ readonly status: 410;
11
+ readonly message: string;
12
+ readonly hint: string;
13
+ };
14
+ export type RenamedField = {
15
+ readonly name: "pot" | "potLimits";
16
+ readonly now: "startupIncluded" | "startupLimits";
17
+ readonly where: "GET /v1/usage";
18
+ readonly retires: string | null;
19
+ };
20
+ /** Response fields follow the same 30-day rule as product names. */
21
+ export declare const RENAMED_FIELDS: readonly RenamedField[];
22
+ /** CLOUD.md section 4: warn while a response field's old name works, then
23
+ * fail with the same code, status and hint as a retired product name. */
24
+ export declare function fieldUse(name: RenamedField["name"], now?: Date, table?: readonly RenamedField[]): {
25
+ kind: "renamed";
26
+ warning: string;
27
+ } | {
28
+ kind: "retired";
29
+ error: RetiredName;
30
+ };
31
+ export type NameUse =
32
+ /** The product's own word, or in the CLI its singular shortcut. */
33
+ {
34
+ readonly kind: "current";
35
+ readonly product: ProductName;
36
+ }
37
+ /** An old word, still served: say `warning` to the caller. */
38
+ | {
39
+ readonly kind: "renamed";
40
+ readonly product: ProductName;
41
+ readonly old: string;
42
+ /** The new words, `networks/wireguard` for `tunnel`. */
43
+ readonly to: string;
44
+ readonly retires: string | null;
45
+ readonly warning: string;
46
+ }
47
+ /** An old word past its date. */
48
+ | {
49
+ readonly kind: "retired";
50
+ readonly product: ProductName;
51
+ readonly old: string;
52
+ readonly to: string;
53
+ readonly error: RetiredName;
54
+ };
55
+ /** The new words for an old one: the product, then where inside it. */
56
+ export declare function newWords(product: string, under?: string): string;
57
+ /** The property path of product words in the JavaScript SDK:
58
+ * `networks/wireguard` is `networks.wireguard`, `web-analytics` is
59
+ * `webAnalytics`. */
60
+ export declare function sdkPath(words: string): string[];
61
+ /** Whether `retires` (a UTC date) has come at `now`. */
62
+ export declare function isRetired(retires: string | null, now: Date): boolean;
63
+ /** What one word means on one surface at `now`, or undefined when it names no
64
+ * product. The CLI takes a product's singular silently, unless the word is
65
+ * still another product's live old name (`tunnel` names the private network
66
+ * until it retires, then incoming tunnels). */
67
+ export declare function resolveWord(word: string, surface: Surface, now?: Date, table?: readonly ProductName[]): NameUse | undefined;
68
+ /** An API path under an old word, rewritten to the new one: `/v1/addresses/x`
69
+ * is `/v1/ips/x`. Undefined when the path's first word is not an old name. */
70
+ export declare function renamePath(pathname: string, now?: Date, table?: readonly ProductName[]): {
71
+ readonly path: string;
72
+ readonly use: Exclude<NameUse, {
73
+ kind: "current";
74
+ }>;
75
+ } | undefined;
76
+ /** An MCP tool under an old word, renamed: `runtime_sandbox_exec` is
77
+ * `runtime_sandboxes_exec`. The longest old word wins, so `runtime_web_x`
78
+ * never takes a shorter old name's place. */
79
+ export declare function renameTool(tool: string, now?: Date, table?: readonly ProductName[]): {
80
+ readonly tool: string;
81
+ readonly use: Exclude<NameUse, {
82
+ kind: "current";
83
+ }>;
84
+ } | undefined;
85
+ /** The headers an answer under an old path carries: `Deprecation` (RFC 9745)
86
+ * from the day the rename shipped, `Sunset` (RFC 8594) on the day it stops,
87
+ * a `Link` to the new path, and `Warning` with the sentence the SDK and CLI
88
+ * print. */
89
+ export declare function renamedHeaders(renamed: {
90
+ readonly path: string;
91
+ readonly use: NameUse;
92
+ }): Record<string, string>;
93
+ /** The sentence inside a `Warning: 299` header, for the SDK and CLI to print;
94
+ * undefined for any other header. */
95
+ export declare function warningText(header: string | null): string | undefined;
96
+ /** A CLI command line's product words, in their plural form: `sandbox ls` is
97
+ * `sandboxes ls` silently, and `tunnel create` is `networks wireguard create`
98
+ * with the warning to print. Undefined when the first word names no product,
99
+ * so a top-level command (`login`, `ls`) passes through. */
100
+ export declare function cliWords(args: readonly string[], now?: Date, table?: readonly ProductName[]): {
101
+ readonly args: string[];
102
+ readonly use: NameUse;
103
+ } | undefined;
@@ -0,0 +1,189 @@
1
+ /** CLOUD.md section 4: an old product word keeps working for ALIAS_DAYS after
2
+ * its rename ships, with a warning naming the new word, and then fails with
3
+ * `name_retired` and a hint. One implementation for the API's paths, the MCP's
4
+ * tool names, the CLI's commands and the SDK's properties; each reads the table in
5
+ * product-names.ts. This file imports nothing else, so the CLI can carry it. */
6
+ import { ALIAS_DAYS, PRODUCT_NAMES, wordOf } from "./product-names.js";
7
+ /** CLOUD.md section 4: the deploy shipping this batch sets its retirement
8
+ * date to the deploy day plus ALIAS_DAYS, as product-names.ts does. */
9
+ const RENAME_OF_8_OCTOBER = null;
10
+ /** Response fields follow the same 30-day rule as product names. */
11
+ export const RENAMED_FIELDS = [
12
+ { name: "pot", now: "startupIncluded", where: "GET /v1/usage", retires: RENAME_OF_8_OCTOBER },
13
+ { name: "potLimits", now: "startupLimits", where: "GET /v1/usage", retires: RENAME_OF_8_OCTOBER },
14
+ ];
15
+ /** CLOUD.md section 4: warn while a response field's old name works, then
16
+ * fail with the same code, status and hint as a retired product name. */
17
+ export function fieldUse(name, now = new Date(), table = RENAMED_FIELDS) {
18
+ const field = table.find((field) => field.name === name);
19
+ if (!field)
20
+ throw new Error(`Unknown renamed response field: ${name}`);
21
+ const from = `usage.${field.name}`;
22
+ const to = `usage.${field.now}`;
23
+ if (isRetired(field.retires, now))
24
+ return {
25
+ kind: "retired",
26
+ error: {
27
+ code: RETIRED[0],
28
+ status: RETIRED[1],
29
+ message: `${from} was renamed ${to} and stopped working on ${field.retires}.`,
30
+ hint: `Use ${to}.`,
31
+ },
32
+ };
33
+ const by = field.retires === null ? "" : ` It stops working on ${field.retires}.`;
34
+ return { kind: "renamed", warning: `${from} is now ${to}.${by}` };
35
+ }
36
+ /** The new words for an old one: the product, then where inside it. */
37
+ export function newWords(product, under) {
38
+ return under === undefined ? product : `${product}/${under}`;
39
+ }
40
+ /** How each surface writes product words (`networks/wireguard`), for
41
+ * messages. */
42
+ function spelled(words, surface) {
43
+ if (surface === "api")
44
+ return `/v1/${words}`;
45
+ if (surface === "mcp")
46
+ return `runtime_${words.replaceAll(/[-/]/g, "_")}`;
47
+ if (surface === "sdk")
48
+ return `runtime.${sdkPath(words).join(".")}`;
49
+ return `runtime ${words.replaceAll("/", " ")}`;
50
+ }
51
+ /** The property path of product words in the JavaScript SDK:
52
+ * `networks/wireguard` is `networks.wireguard`, `web-analytics` is
53
+ * `webAnalytics`. */
54
+ export function sdkPath(words) {
55
+ return words
56
+ .split("/")
57
+ .map((word) => word.replaceAll(/[-_]([a-z])/g, (_, c) => c.toUpperCase()));
58
+ }
59
+ /** Whether `retires` (a UTC date) has come at `now`. */
60
+ export function isRetired(retires, now) {
61
+ return retires !== null && now.getTime() >= Date.parse(`${retires}T00:00:00Z`);
62
+ }
63
+ /** The code and status of an old word after its date (api.md's table). */
64
+ const RETIRED = ["name_retired", 410];
65
+ /** What one word means on one surface at `now`, or undefined when it names no
66
+ * product. The CLI takes a product's singular silently, unless the word is
67
+ * still another product's live old name (`tunnel` names the private network
68
+ * until it retires, then incoming tunnels). */
69
+ export function resolveWord(word, surface, now = new Date(), table = PRODUCT_NAMES) {
70
+ const asked = wordOf(word);
71
+ const own = table.find((product) => product.name === asked);
72
+ if (own)
73
+ return { kind: "current", product: own };
74
+ let retired;
75
+ for (const product of table) {
76
+ for (const old of product.old) {
77
+ if (wordOf(old.name) !== asked)
78
+ continue;
79
+ if (surface === "cli" && product.singular === asked)
80
+ return { kind: "current", product };
81
+ const to = newWords(product.name, old.under);
82
+ const target = spelled(to, surface);
83
+ if (!isRetired(old.retires, now)) {
84
+ const by = old.retires === null ? "" : ` It stops working on ${old.retires}.`;
85
+ return {
86
+ kind: "renamed",
87
+ product,
88
+ old: asked,
89
+ to,
90
+ retires: old.retires,
91
+ warning: `${spelled(asked, surface)} is now ${target}.${by}`,
92
+ };
93
+ }
94
+ retired = {
95
+ kind: "retired",
96
+ product,
97
+ old: asked,
98
+ to,
99
+ error: {
100
+ code: RETIRED[0],
101
+ status: RETIRED[1],
102
+ message: `${spelled(asked, surface)} was renamed ${target} and stopped working on ${old.retires}.`,
103
+ hint: `Use ${target}.`,
104
+ },
105
+ };
106
+ }
107
+ }
108
+ if (surface === "cli") {
109
+ const shortcut = table.find((product) => product.singular === asked);
110
+ if (shortcut)
111
+ return { kind: "current", product: shortcut };
112
+ }
113
+ return retired;
114
+ }
115
+ /** An API path under an old word, rewritten to the new one: `/v1/addresses/x`
116
+ * is `/v1/ips/x`. Undefined when the path's first word is not an old name. */
117
+ export function renamePath(pathname, now = new Date(), table = PRODUCT_NAMES) {
118
+ const found = /^\/v1\/([a-z][a-z0-9_-]*)(?=\/|:|$)/.exec(pathname);
119
+ if (!found)
120
+ return undefined;
121
+ const use = resolveWord(found[1], "api", now, table);
122
+ if (!use || use.kind === "current")
123
+ return undefined;
124
+ return { path: `/v1/${use.to}${pathname.slice(found[0].length)}`, use };
125
+ }
126
+ /** An MCP tool under an old word, renamed: `runtime_sandbox_exec` is
127
+ * `runtime_sandboxes_exec`. The longest old word wins, so `runtime_web_x`
128
+ * never takes a shorter old name's place. */
129
+ export function renameTool(tool, now = new Date(), table = PRODUCT_NAMES) {
130
+ if (!tool.startsWith("runtime_"))
131
+ return undefined;
132
+ const rest = tool.slice("runtime_".length);
133
+ let best;
134
+ for (const product of table) {
135
+ for (const old of product.old) {
136
+ const word = wordOf(old.name).replaceAll("-", "_");
137
+ if (rest !== word && !rest.startsWith(`${word}_`))
138
+ continue;
139
+ if (best && best.word.length >= word.length)
140
+ continue;
141
+ const use = resolveWord(word, "mcp", now, table);
142
+ if (use && use.kind !== "current")
143
+ best = { word, use };
144
+ }
145
+ }
146
+ if (!best)
147
+ return undefined;
148
+ const renamed = `${spelled(best.use.to, "mcp")}${rest.slice(best.word.length)}`;
149
+ return { tool: renamed, use: best.use };
150
+ }
151
+ /** The headers an answer under an old path carries: `Deprecation` (RFC 9745)
152
+ * from the day the rename shipped, `Sunset` (RFC 8594) on the day it stops,
153
+ * a `Link` to the new path, and `Warning` with the sentence the SDK and CLI
154
+ * print. */
155
+ export function renamedHeaders(renamed) {
156
+ const { use } = renamed;
157
+ if (use.kind !== "renamed")
158
+ return {};
159
+ const headers = {
160
+ warning: `299 runtime "${use.warning.replaceAll('"', "'")}"`,
161
+ link: `<${renamed.path}>; rel="successor-version"`,
162
+ };
163
+ if (use.retires !== null) {
164
+ const ends = Date.parse(`${use.retires}T00:00:00Z`);
165
+ headers.deprecation = `@${Math.floor((ends - ALIAS_DAYS * 86_400_000) / 1000)}`;
166
+ headers.sunset = new Date(ends).toUTCString();
167
+ }
168
+ return headers;
169
+ }
170
+ /** The sentence inside a `Warning: 299` header, for the SDK and CLI to print;
171
+ * undefined for any other header. */
172
+ export function warningText(header) {
173
+ const found = header === null ? null : /^299 \S+ "(.*)"$/.exec(header);
174
+ return found ? found[1] : undefined;
175
+ }
176
+ /** A CLI command line's product words, in their plural form: `sandbox ls` is
177
+ * `sandboxes ls` silently, and `tunnel create` is `networks wireguard create`
178
+ * with the warning to print. Undefined when the first word names no product,
179
+ * so a top-level command (`login`, `ls`) passes through. */
180
+ export function cliWords(args, now = new Date(), table = PRODUCT_NAMES) {
181
+ const [first, ...rest] = args;
182
+ if (first === undefined)
183
+ return undefined;
184
+ const use = resolveWord(first, "cli", now, table);
185
+ if (!use)
186
+ return undefined;
187
+ const words = use.kind === "current" ? use.product.name : use.to;
188
+ return { args: [...words.split("/"), ...rest], use };
189
+ }
@@ -0,0 +1,176 @@
1
+ import { Page } from "../page.js";
2
+ import type { RequestOptions, Transport } from "../transport.js";
3
+ /** Private until launch. A deployment's completion follows its own record,
4
+ * not the app's state. */
5
+ export type AppState = "waiting" | "live" | "failed" | "deleting" | "deleted";
6
+ export type AppSettings = {
7
+ minCopies: number;
8
+ maxCopies: number;
9
+ concurrency: number;
10
+ spares: number;
11
+ vcpu: number;
12
+ memoryMiB: number;
13
+ port: number;
14
+ healthPath?: string;
15
+ };
16
+ export type App = AppSettings & {
17
+ id: string;
18
+ name: string | null;
19
+ labels: Record<string, string>;
20
+ state: AppState;
21
+ createdAt: number;
22
+ updatedAt: number;
23
+ deletedAt: number | null;
24
+ error: {
25
+ code: string;
26
+ message: string;
27
+ } | null;
28
+ liveVersion: number | null;
29
+ previousVersion: number | null;
30
+ };
31
+ export type RuntimeFunction = Omit<App, "minCopies"> & {
32
+ minCopies: 0;
33
+ };
34
+ export type CreateFunction = {
35
+ /** Up to 128 letters, digits, dots, dashes or underscores; alphanumeric first. */
36
+ name?: string;
37
+ /** At most 32 entries, each value at most 256 characters. */
38
+ labels?: Record<string, string>;
39
+ /** Required, 1 to 1000. Bounds the number of awake copies. */
40
+ maxCopies: number;
41
+ /** Required, 1 to 16. */
42
+ vcpu: number;
43
+ /** Required, 128 to 65536 MiB. */
44
+ memoryMiB: number;
45
+ /** 1 to 10000; defaults to 100. */
46
+ concurrency?: number;
47
+ /** 0 to 16; defaults to 2. */
48
+ spares?: number;
49
+ /** 1 to 65535; defaults to 8080. */
50
+ port?: number;
51
+ /** Starts with /, no whitespace, at most 256 characters.
52
+ * Omit to check whether the port accepts connections. */
53
+ healthPath?: string;
54
+ /** Only Apps can keep copies awake. Omit this field, including zero. */
55
+ minCopies?: never;
56
+ };
57
+ export type CreateApp = Omit<CreateFunction, "minCopies"> & {
58
+ /** Defaults to 0. No greater than maxCopies. */
59
+ minCopies?: number;
60
+ };
61
+ export type UpdateApp = Partial<AppSettings> & {
62
+ name?: string | null;
63
+ labels?: Record<string, string>;
64
+ };
65
+ export type UpdateFunction = Omit<UpdateApp, "minCopies"> & {
66
+ minCopies?: never;
67
+ };
68
+ export type DeployState = "building" | "warming" | "switching" | "live" | "failed" | "superseded";
69
+ export type AppDeploy = {
70
+ id: string;
71
+ version: number;
72
+ url: string;
73
+ } & ({
74
+ state: "building" | "warming" | "switching" | "live";
75
+ error?: never;
76
+ } | {
77
+ state: "failed" | "superseded";
78
+ error: {
79
+ code: "deploy_build_failed" | "deploy_health_failed" | "deploy_superseded";
80
+ message: string;
81
+ hint: string;
82
+ lastOutput: string;
83
+ };
84
+ });
85
+ export type DeployApp = ({
86
+ image: string;
87
+ build?: never;
88
+ } | {
89
+ build: string;
90
+ image?: never;
91
+ }) & {
92
+ env?: Record<string, string>;
93
+ port?: number;
94
+ healthPath?: string;
95
+ /** Return live, failed or superseded, including its URL/version or failure details. */
96
+ wait?: boolean;
97
+ };
98
+ export type AppVersion = {
99
+ version: number;
100
+ live: boolean;
101
+ previous: boolean;
102
+ saved: boolean;
103
+ };
104
+ export type AppCopy = {
105
+ id: string;
106
+ version: number;
107
+ region: string;
108
+ state: "starting" | "serving" | "draining" | "frozen" | "parked";
109
+ since: number;
110
+ };
111
+ export type DeployGetOptions = RequestOptions & {
112
+ waitFor?: DeployState;
113
+ timeoutSeconds?: number;
114
+ };
115
+ export type AppListQuery = {
116
+ name?: string;
117
+ /** Repeated key:value filters; all must match. */
118
+ label?: readonly string[];
119
+ /** Defaults to waiting, live and failed. */
120
+ state?: readonly AppState[];
121
+ /** 1 to 100; defaults to 50. */
122
+ limit?: number;
123
+ cursor?: string;
124
+ };
125
+ export type AppGetOptions = RequestOptions & {
126
+ waitFor?: AppState;
127
+ /** Server wait, 1 to 60 seconds; defaults to 30. Check the returned state. */
128
+ timeoutSeconds?: number;
129
+ };
130
+ export type AppWaitOptions = RequestOptions & {
131
+ /** Whole wait, including retries; defaults to 300000 ms. 0 disables it. */
132
+ timeoutMs?: number;
133
+ /** Delay between reads; defaults to 250 ms. */
134
+ pollMs?: number;
135
+ };
136
+ export type AppDeleteOptions = AppWaitOptions & {
137
+ wait?: boolean;
138
+ };
139
+ export declare function apps(t: Transport): {
140
+ /** Create a resource waiting for its first version. Defaults belong to
141
+ * the API, so the SDK does not override them. */
142
+ create: (input: CreateApp, options?: RequestOptions) => Promise<App>;
143
+ get: (id: string, options?: AppGetOptions) => Promise<App>;
144
+ list: (query?: AppListQuery, options?: RequestOptions) => Promise<Page<App>>;
145
+ deploy: (id: string, input: DeployApp, options?: AppWaitOptions) => Promise<AppDeploy>;
146
+ getDeploy: (id: string, deployId: string, options?: DeployGetOptions) => Promise<AppDeploy>;
147
+ /** Resume by deploy id after a process restart, without creating another version. */
148
+ waitDeploy: (id: string, deployId: string, options?: AppWaitOptions) => Promise<AppDeploy>;
149
+ rollback: (id: string, options?: AppDeleteOptions) => Promise<AppDeploy>;
150
+ versions: (id: string, options?: RequestOptions) => Promise<AppVersion[]>;
151
+ copies: (id: string, options?: RequestOptions) => Promise<AppCopy[]>;
152
+ update: (id: string, input: UpdateApp, options?: RequestOptions) => Promise<App>;
153
+ /** Follow resource state, not a deployment: an
154
+ * older live version can survive a failed rollout of a newer version. */
155
+ wait: (id: string, target: "live" | "deleted", options?: AppWaitOptions) => Promise<App>;
156
+ delete: (id: string, options?: AppDeleteOptions) => Promise<App>;
157
+ };
158
+ export declare function functions(t: Transport): {
159
+ /** Create a resource waiting for its first version. Defaults belong to
160
+ * the API, so the SDK does not override them. */
161
+ create: (input: CreateFunction, options?: RequestOptions) => Promise<RuntimeFunction>;
162
+ get: (id: string, options?: AppGetOptions) => Promise<RuntimeFunction>;
163
+ list: (query?: AppListQuery, options?: RequestOptions) => Promise<Page<RuntimeFunction>>;
164
+ deploy: (id: string, input: DeployApp, options?: AppWaitOptions) => Promise<AppDeploy>;
165
+ getDeploy: (id: string, deployId: string, options?: DeployGetOptions) => Promise<AppDeploy>;
166
+ /** Resume by deploy id after a process restart, without creating another version. */
167
+ waitDeploy: (id: string, deployId: string, options?: AppWaitOptions) => Promise<AppDeploy>;
168
+ rollback: (id: string, options?: AppDeleteOptions) => Promise<AppDeploy>;
169
+ versions: (id: string, options?: RequestOptions) => Promise<AppVersion[]>;
170
+ copies: (id: string, options?: RequestOptions) => Promise<AppCopy[]>;
171
+ update: (id: string, input: UpdateFunction, options?: RequestOptions) => Promise<RuntimeFunction>;
172
+ /** Follow resource state, not a deployment: an
173
+ * older live version can survive a failed rollout of a newer version. */
174
+ wait: (id: string, target: "live" | "deleted", options?: AppWaitOptions) => Promise<RuntimeFunction>;
175
+ delete: (id: string, options?: AppDeleteOptions) => Promise<RuntimeFunction>;
176
+ };
@@ -0,0 +1,165 @@
1
+ import { RuntimeError } from "../errors.js";
2
+ import { Page } from "../page.js";
3
+ import { pause } from "../wait.js";
4
+ const enc = encodeURIComponent;
5
+ function invalid(field) {
6
+ return new RuntimeError({
7
+ code: "invalid_request",
8
+ status: 0,
9
+ message: `${field} must be a finite, nonnegative number.`,
10
+ hint: "Fix the option and call again; nothing was sent.",
11
+ details: { field },
12
+ });
13
+ }
14
+ /** One deadline covers the write, retries and every read. A timed-out delete
15
+ * can be retried after a process restart with this same key. */
16
+ async function following(options, work) {
17
+ const { pollMs = 250, timeoutMs = 300_000, signal, ...rest } = options;
18
+ if (!Number.isFinite(pollMs) || pollMs < 0)
19
+ throw invalid("pollMs");
20
+ if (!Number.isFinite(timeoutMs) || timeoutMs < 0)
21
+ throw invalid("timeoutMs");
22
+ const deadline = new AbortController();
23
+ const combined = signal ? AbortSignal.any([signal, deadline.signal]) : deadline.signal;
24
+ const timer = timeoutMs > 0 ? setTimeout(() => deadline.abort(), timeoutMs) : undefined;
25
+ const request = { ...rest, timeoutMs: 0, signal: combined };
26
+ const poll = () => pause(pollMs, combined);
27
+ try {
28
+ combined.throwIfAborted();
29
+ const result = await work(request, poll);
30
+ combined.throwIfAborted();
31
+ return result;
32
+ }
33
+ catch (cause) {
34
+ if (!combined.aborted) {
35
+ // A read failure still belongs to the write that started this wait.
36
+ // Keep its key so a caller can recover after losing the reply.
37
+ if (cause instanceof RuntimeError && rest.idempotencyKey && !cause.idempotencyKey)
38
+ throw new RuntimeError({
39
+ message: cause.message,
40
+ code: cause.code,
41
+ status: cause.status,
42
+ requestId: cause.requestId,
43
+ hint: cause.hint,
44
+ details: cause.details,
45
+ retryAfterMs: cause.retryAfterMs,
46
+ idempotencyKey: rest.idempotencyKey,
47
+ cause,
48
+ });
49
+ throw cause;
50
+ }
51
+ throw new RuntimeError({
52
+ code: "timeout",
53
+ status: 0,
54
+ message: "Waiting was cancelled or reached its deadline; the resource may still change.",
55
+ hint: "Read the resource to resume waiting. Retry a write with the same idempotencyKey.",
56
+ ...(rest.idempotencyKey === undefined ? {} : { idempotencyKey: rest.idempotencyKey }),
57
+ cause,
58
+ });
59
+ }
60
+ finally {
61
+ clearTimeout(timer);
62
+ }
63
+ }
64
+ function copies(t, plural) {
65
+ const base = `/v1/${plural}`;
66
+ const get = (id, options = {}) => {
67
+ const { waitFor, timeoutSeconds, ...request } = options;
68
+ return t.json({
69
+ method: "GET",
70
+ path: `${base}/${enc(id)}`,
71
+ query: { waitFor, timeoutSeconds },
72
+ ...request,
73
+ });
74
+ };
75
+ const list = async (query = {}, options) => {
76
+ const page = await t.json({
77
+ method: "GET",
78
+ path: base,
79
+ query,
80
+ ...options,
81
+ });
82
+ return new Page(page.data, page.nextCursor, (cursor) => list({ ...query, cursor }, options));
83
+ };
84
+ const follow = async (id, target, request, pause, initial) => {
85
+ let record = initial ?? (await get(id, request));
86
+ for (;;) {
87
+ if (record.state === target)
88
+ return record;
89
+ if (["failed", "deleted", "live"].includes(record.state))
90
+ throw new RuntimeError({
91
+ code: record.error?.code ?? "unexpected_state",
92
+ status: 0,
93
+ message: record.error?.message ?? `Expected ${target}, but ${id} reached ${record.state}.`,
94
+ hint: "Read the resource and inspect its error before starting another change.",
95
+ details: { id, state: record.state, target, resource: record },
96
+ ...(request.idempotencyKey === undefined
97
+ ? {}
98
+ : { idempotencyKey: request.idempotencyKey }),
99
+ });
100
+ await pause();
101
+ record = await get(id, request);
102
+ }
103
+ };
104
+ const getDeploy = (id, deployId, options = {}) => {
105
+ const { waitFor, timeoutSeconds, ...request } = options;
106
+ return t.json({
107
+ method: "GET",
108
+ path: `${base}/${enc(id)}/deploys/${enc(deployId)}`,
109
+ query: { waitFor, timeoutSeconds },
110
+ ...request,
111
+ });
112
+ };
113
+ const followDeploy = async (id, record, request, pause) => {
114
+ while (!["live", "failed", "superseded"].includes(record.state)) {
115
+ record = await getDeploy(id, record.id, { ...request, waitFor: "live", timeoutSeconds: 60 });
116
+ if (!["live", "failed", "superseded"].includes(record.state))
117
+ await pause();
118
+ }
119
+ return record;
120
+ };
121
+ const writeDeploy = (id, verb, body, wait, options) => {
122
+ const { pollMs, ...request } = options;
123
+ const write = (rest) => t.json({ method: "POST", path: `${base}/${enc(id)}/${verb}`, body, ...rest });
124
+ if (!wait)
125
+ return write(request);
126
+ return following({ ...request, pollMs, idempotencyKey: request.idempotencyKey ?? crypto.randomUUID() }, async (rest, pause) => followDeploy(id, await write(rest), rest, pause));
127
+ };
128
+ return {
129
+ /** Create a resource waiting for its first version. Defaults belong to
130
+ * the API, so the SDK does not override them. */
131
+ create: (input, options) => t.json({ method: "POST", path: base, body: input, ...options }),
132
+ get,
133
+ list,
134
+ deploy: (id, input, options = {}) => {
135
+ const { wait = false, ...body } = input;
136
+ return writeDeploy(id, "deploys", body, wait, options);
137
+ },
138
+ getDeploy,
139
+ /** Resume by deploy id after a process restart, without creating another version. */
140
+ waitDeploy: (id, deployId, options = {}) => following(options, async (request, pause) => followDeploy(id, await getDeploy(id, deployId, request), request, pause)),
141
+ rollback: (id, options = {}) => {
142
+ const { wait = false, ...request } = options;
143
+ return writeDeploy(id, "rollback", {}, wait, request);
144
+ },
145
+ versions: (id, options) => t.json({ method: "GET", path: `${base}/${enc(id)}/versions`, ...options }),
146
+ copies: (id, options) => t.json({ method: "GET", path: `${base}/${enc(id)}/copies`, ...options }),
147
+ update: (id, input, options) => t.json({ method: "POST", path: `${base}/${enc(id)}:update`, body: input, ...options }),
148
+ /** Follow resource state, not a deployment: an
149
+ * older live version can survive a failed rollout of a newer version. */
150
+ wait: (id, target, options = {}) => following(options, (request, pause) => follow(id, target, request, pause)),
151
+ delete: (id, options = {}) => {
152
+ const { wait = false, pollMs, ...request } = options;
153
+ const remove = (rest) => t.json({ method: "DELETE", path: `${base}/${enc(id)}`, ...rest });
154
+ if (!wait)
155
+ return remove(request);
156
+ return following({ ...request, pollMs, idempotencyKey: request.idempotencyKey ?? crypto.randomUUID() }, async (rest, pause) => follow(id, "deleted", rest, pause, await remove(rest)));
157
+ },
158
+ };
159
+ }
160
+ export function apps(t) {
161
+ return copies(t, "apps");
162
+ }
163
+ export function functions(t) {
164
+ return copies(t, "functions");
165
+ }