withruntime 0.11.2 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BLAXEL.md +2 -2
- package/COMPATIBILITY.md +74 -9
- package/DAYTONA.md +33 -5
- package/E2B.md +85 -24
- package/README.md +11 -10
- package/README.src.md +10 -9
- package/VERCEL.md +43 -5
- package/compatibility-lock.json +58 -49
- package/dist/billing-cli.d.ts +1 -1
- package/dist/billing-cli.js +85 -5
- package/dist/blaxel/errors.d.ts +14 -0
- package/dist/blaxel/errors.js +29 -0
- package/dist/blaxel/index.d.ts +2 -2
- package/dist/blaxel/index.js +4 -4
- package/dist/blaxel/interpreter.d.ts +2 -5
- package/dist/blaxel/sandbox.d.ts +15 -6
- package/dist/blaxel/sandbox.js +64 -8
- package/dist/blaxel/snapshot.d.ts +4 -0
- package/dist/blaxel/snapshot.js +4 -0
- package/dist/cli/logs.d.ts +21 -0
- package/dist/cli/logs.js +188 -0
- package/dist/cli-extras.d.ts +3 -3
- package/dist/cli-extras.js +17 -12
- package/dist/cli-words.d.ts +10 -1
- package/dist/cli-words.js +18 -0
- package/dist/cli.d.ts +22 -3
- package/dist/cli.js +647 -273
- package/dist/client.d.ts +2 -4
- package/dist/client.js +27 -11
- package/dist/cloudflare/errors.d.ts +143 -0
- package/dist/cloudflare/errors.js +167 -0
- package/dist/cloudflare/index.d.ts +175 -6
- package/dist/cloudflare/index.js +416 -26
- package/dist/cloudflare/interpreter.d.ts +119 -0
- package/dist/cloudflare/interpreter.js +223 -0
- package/dist/cloudflare/streams.d.ts +38 -0
- package/dist/cloudflare/streams.js +145 -0
- package/dist/cloudflare/watch.d.ts +9 -0
- package/dist/cloudflare/watch.js +24 -0
- package/dist/codesandbox/index.d.ts +216 -28
- package/dist/codesandbox/index.js +478 -78
- package/dist/compat/core.d.ts +18 -0
- package/dist/compat/core.js +29 -0
- package/dist/compat/files.d.ts +1 -0
- package/dist/compat/files.js +25 -5
- package/dist/credentials.d.ts +3 -0
- package/dist/credentials.js +15 -0
- package/dist/daytona/computer-use.d.ts +13 -4
- package/dist/daytona/computer-use.js +66 -8
- package/dist/daytona/daytona.d.ts +7 -1
- package/dist/daytona/daytona.js +31 -4
- package/dist/daytona/errors.d.ts +11 -0
- package/dist/daytona/errors.js +18 -0
- package/dist/daytona/filesystem.js +2 -1
- package/dist/daytona/image.d.ts +13 -9
- package/dist/daytona/image.js +18 -12
- package/dist/daytona/index.d.ts +1 -1
- package/dist/daytona/sandbox.d.ts +8 -2
- package/dist/daytona/sandbox.js +16 -2
- package/dist/domains/index.d.ts +9 -0
- package/dist/domains/index.js +10 -0
- package/dist/e2b/commands.d.ts +1 -1
- package/dist/e2b/commands.js +6 -7
- package/dist/e2b/errors.d.ts +17 -0
- package/dist/e2b/errors.js +40 -1
- package/dist/e2b/index.d.ts +2 -2
- package/dist/e2b/index.js +1 -1
- package/dist/e2b/sandbox.d.ts +28 -22
- package/dist/e2b/sandbox.js +34 -8
- package/dist/e2b/unsupported.js +1 -1
- package/dist/errors.d.ts +11 -2
- package/dist/errors.js +17 -5
- package/dist/freestyle/index.d.ts +285 -37
- package/dist/freestyle/index.js +656 -144
- package/dist/identity.d.ts +3 -0
- package/dist/identity.js +27 -0
- package/dist/images/index.d.ts +9 -0
- package/dist/images/index.js +10 -0
- package/dist/index.d.ts +3 -1
- package/dist/ips/index.d.ts +9 -0
- package/dist/ips/index.js +10 -0
- package/dist/jobs/index.d.ts +9 -0
- package/dist/jobs/index.js +10 -0
- package/dist/keys.js +2 -0
- package/dist/login.js +2 -0
- package/dist/machine.d.ts +27 -4
- package/dist/machine.js +46 -14
- package/dist/mcp.js +144 -20
- package/dist/modal/index.d.ts +339 -61
- package/dist/modal/index.js +781 -121
- package/dist/network-cli.d.ts +3 -1
- package/dist/network-cli.js +78 -36
- package/dist/networks/index.d.ts +11 -0
- package/dist/networks/index.js +10 -0
- package/dist/ports/index.d.ts +9 -0
- package/dist/ports/index.js +10 -0
- package/dist/product-names.d.ts +52 -0
- package/dist/product-names.js +164 -0
- package/dist/product-renames.d.ts +103 -0
- package/dist/product-renames.js +189 -0
- package/dist/products/api-keys.d.ts +73 -0
- package/dist/products/api-keys.js +23 -0
- package/dist/products/apps.d.ts +157 -0
- package/dist/products/apps.js +60 -0
- package/dist/products/balancers.d.ts +87 -0
- package/dist/products/balancers.js +8 -0
- package/dist/products/billing.d.ts +41 -0
- package/dist/products/billing.js +13 -0
- package/dist/products/browser.d.ts +10 -0
- package/dist/products/browsers.d.ts +71 -0
- package/dist/products/browsers.js +102 -0
- package/dist/products/builds.d.ts +156 -0
- package/dist/products/builds.js +227 -0
- package/dist/products/index.d.ts +5 -16
- package/dist/products/index.js +5 -14
- package/dist/products/jobs.d.ts +19 -2
- package/dist/products/jobs.js +28 -11
- package/dist/products/logs.d.ts +245 -0
- package/dist/products/logs.js +274 -0
- package/dist/products/network-products.d.ts +38 -7
- package/dist/products/network-products.js +31 -18
- package/dist/products/observability.d.ts +8 -2
- package/dist/products/private-network.js +1 -1
- package/dist/products/queues.d.ts +149 -0
- package/dist/products/queues.js +213 -0
- package/dist/products/referrals.d.ts +21 -7
- package/dist/products/referrals.js +3 -3
- package/dist/products/resources.d.ts +54 -0
- package/dist/products/resources.js +122 -0
- package/dist/products/secrets.d.ts +29 -3
- package/dist/products/secrets.js +47 -18
- package/dist/products/signed-requests.d.ts +21 -0
- package/dist/products/signed-requests.js +11 -0
- package/dist/products/streams.d.ts +73 -0
- package/dist/products/streams.js +17 -0
- package/dist/products/switching.d.ts +35 -7
- package/dist/products/switching.js +4 -3
- package/dist/products/volumes.d.ts +6 -0
- package/dist/record-cli.d.ts +23 -0
- package/dist/record-cli.js +308 -0
- package/dist/renamed-products.d.ts +38 -0
- package/dist/renamed-products.js +34 -0
- package/dist/runloop/index.d.ts +9 -2
- package/dist/runloop/index.js +29 -8
- package/dist/runloop/sdk.d.ts +131 -0
- package/dist/runloop/sdk.js +167 -0
- package/dist/sandbox.d.ts +7 -2
- package/dist/sandbox.js +41 -11
- package/dist/sandboxes/index.d.ts +11 -0
- package/dist/sandboxes/index.js +12 -0
- package/dist/snapshots/index.d.ts +9 -0
- package/dist/snapshots/index.js +11 -0
- package/dist/snapshots.d.ts +7 -4
- package/dist/sprites/index.d.ts +343 -31
- package/dist/sprites/index.js +866 -84
- package/dist/ssh.d.ts +1 -1
- package/dist/ssh.js +11 -11
- package/dist/terminal.d.ts +24 -0
- package/dist/terminal.js +32 -0
- package/dist/transport.d.ts +18 -1
- package/dist/transport.js +80 -6
- package/dist/tunnel.js +1 -1
- package/dist/types.d.ts +71 -4
- package/dist/vercel/sandbox.d.ts +1 -1
- package/dist/vercel/sandbox.js +6 -4
- package/dist/vercel/snapshot.js +10 -2
- package/dist/volumes/index.d.ts +9 -0
- package/dist/volumes/index.js +10 -0
- package/dist/wallet.d.ts +4 -1
- package/dist/wallet.js +2 -1
- package/package.json +48 -14
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { privateNetwork } from "./private-network.js";
|
|
2
|
+
import { signedRequests } from "./signed-requests.js";
|
|
2
3
|
const enc = encodeURIComponent;
|
|
3
4
|
/** `runtime.domains`: serve a sandbox's port at your own hostname, with HTTPS. */
|
|
4
5
|
export function domains(t) {
|
|
@@ -43,57 +44,69 @@ export function ports(t) {
|
|
|
43
44
|
}),
|
|
44
45
|
};
|
|
45
46
|
}
|
|
46
|
-
/** `runtime.
|
|
47
|
+
/** `runtime.ips`: a dedicated outbound address every sandbox of the
|
|
47
48
|
* account sends from, for allow-lists. */
|
|
48
|
-
export function
|
|
49
|
+
export function ips(t) {
|
|
49
50
|
return {
|
|
50
|
-
reserve: (input = {}, options) => t.json({ method: "POST", path: "/v1/
|
|
51
|
-
list: async (options) => (await t.json({ method: "GET", path: "/v1/
|
|
52
|
-
.data,
|
|
51
|
+
reserve: (input = {}, options) => t.json({ method: "POST", path: "/v1/ips", body: input, ...options }),
|
|
52
|
+
list: async (options) => (await t.json({ method: "GET", path: "/v1/ips", ...options })).data,
|
|
53
53
|
release: (addressId, options) => t.json({
|
|
54
54
|
method: "DELETE",
|
|
55
|
-
path: `/v1/
|
|
55
|
+
path: `/v1/ips/${enc(addressId)}`,
|
|
56
56
|
...options,
|
|
57
57
|
}),
|
|
58
58
|
};
|
|
59
59
|
}
|
|
60
|
-
/** `runtime.
|
|
61
|
-
export function
|
|
60
|
+
/** `runtime.networks.wireguard`: a WireGuard tunnel from your own network into your sandboxes. */
|
|
61
|
+
export function wireguard(t) {
|
|
62
62
|
return {
|
|
63
|
-
get: (options) => t.json({ method: "GET", path: "/v1/
|
|
63
|
+
get: (options) => t.json({ method: "GET", path: "/v1/networks/wireguard", ...options }),
|
|
64
64
|
/** Idempotent: returns the tunnel you have. */
|
|
65
|
-
create: (input = {}, options) => t.json({ method: "POST", path: "/v1/
|
|
66
|
-
delete: (options) => t.json({
|
|
65
|
+
create: (input = {}, options) => t.json({ method: "POST", path: "/v1/networks/wireguard", body: input, ...options }),
|
|
66
|
+
delete: (options) => t.json({
|
|
67
|
+
method: "DELETE",
|
|
68
|
+
path: "/v1/networks/wireguard",
|
|
69
|
+
...options,
|
|
70
|
+
}),
|
|
67
71
|
/** Leave publicKey out and Runtime generates the pair; the private key is
|
|
68
72
|
* then in `config`, shown once. */
|
|
69
73
|
addPeer: (input, options) => t.json({
|
|
70
74
|
method: "POST",
|
|
71
|
-
path: "/v1/
|
|
75
|
+
path: "/v1/networks/wireguard/peers",
|
|
72
76
|
body: input,
|
|
73
77
|
...options,
|
|
74
78
|
}),
|
|
75
79
|
rotatePeer: (peerId, input = {}, options) => t.json({
|
|
76
80
|
method: "POST",
|
|
77
|
-
path: `/v1/
|
|
81
|
+
path: `/v1/networks/wireguard/peers/${enc(peerId)}:rotate`,
|
|
78
82
|
body: input,
|
|
79
83
|
...options,
|
|
80
84
|
}),
|
|
81
|
-
removePeer: (peerId, options) => t.json({
|
|
85
|
+
removePeer: (peerId, options) => t.json({
|
|
86
|
+
method: "DELETE",
|
|
87
|
+
path: `/v1/networks/wireguard/peers/${enc(peerId)}`,
|
|
88
|
+
...options,
|
|
89
|
+
}),
|
|
82
90
|
};
|
|
83
91
|
}
|
|
84
|
-
/** `runtime.
|
|
92
|
+
/** `runtime.networks`: the networks of the whole account: the WireGuard
|
|
93
|
+
* tunnel in, your sandboxes reaching each other, and the proxy out.
|
|
85
94
|
*
|
|
86
|
-
* await runtime.
|
|
95
|
+
* await runtime.networks.upstreamProxy.set({ url: "http://proxy.example.com:3128" });
|
|
87
96
|
*
|
|
88
97
|
* `upstreamProxy`: after Runtime's own rules allow a connection, the host
|
|
89
98
|
* reaches it with CONNECT through your proxy, so your proxy's controls and
|
|
90
99
|
* logs apply. A proxy that refuses or cannot be reached fails the connection
|
|
91
100
|
* (upstream-proxy-failed); nothing goes around it. Needs a kept top-up. */
|
|
92
|
-
export function
|
|
93
|
-
const path = "/v1/
|
|
101
|
+
export function networks(t) {
|
|
102
|
+
const path = "/v1/networks/upstream-proxy";
|
|
94
103
|
return {
|
|
104
|
+
/** The WireGuard tunnel from your own network into your sandboxes. */
|
|
105
|
+
wireguard: wireguard(t),
|
|
95
106
|
/** Your sandboxes reaching each other by name (`private-network.ts`). */
|
|
96
107
|
private: privateNetwork(t),
|
|
108
|
+
/** Your sandboxes' requests signed as Runtime's agents (`signed-requests.ts`). */
|
|
109
|
+
signedRequests: signedRequests(t),
|
|
97
110
|
upstreamProxy: {
|
|
98
111
|
/** Send outbound connections through your proxy, replacing any set before. */
|
|
99
112
|
set: (input, options) => t.json({
|
|
@@ -35,7 +35,7 @@ export type SandboxMetrics = {
|
|
|
35
35
|
latest: MetricPoint | null;
|
|
36
36
|
points: MetricPoint[];
|
|
37
37
|
};
|
|
38
|
-
export type WebhookEventType = "sandbox.created" | "sandbox.running" | "sandbox.paused" | "sandbox.woken" | "sandbox.stopped" | "sandbox.start_failed" | "sandbox.wake_failed" | "sandbox.host_unreachable" | "sandbox.host_reachable" | "sandbox.recovered" | "sandbox.recovery_failed" | "snapshot.ready" | "snapshot.failed" | "snapshot.deleted" | "volume.ready" | "volume.failed" | "volume.deleted";
|
|
38
|
+
export type WebhookEventType = "sandbox.created" | "sandbox.running" | "sandbox.paused" | "sandbox.woken" | "sandbox.stopped" | "sandbox.start_failed" | "sandbox.wake_failed" | "sandbox.host_unreachable" | "sandbox.host_reachable" | "sandbox.recovered" | "sandbox.recovery_failed" | "sandbox.files.changed" | "sandbox.out_of_memory" | "sandbox.secret_not_applied" | "snapshot.ready" | "snapshot.failed" | "snapshot.deleted" | "volume.ready" | "volume.failed" | "volume.deleted" | "volume.recovered" | "egress_secret.created" | "egress_secret.updated" | "egress_secret.deleted" | "upstream_proxy.set" | "upstream_proxy.removed";
|
|
39
39
|
/** An event as /v1/events lists it and a webhook delivers it. */
|
|
40
40
|
export type RuntimeEvent = {
|
|
41
41
|
id: string;
|
|
@@ -113,7 +113,8 @@ export type UsageExportRow = {
|
|
|
113
113
|
name: string | null;
|
|
114
114
|
kind: string;
|
|
115
115
|
meter: string;
|
|
116
|
-
|
|
116
|
+
/** credit, the trial's hours, or the month's free usage. */
|
|
117
|
+
fundedBy: "credit" | "trial" | "allowance";
|
|
117
118
|
startsAt: string;
|
|
118
119
|
endsAt: string;
|
|
119
120
|
settledAt: string;
|
|
@@ -127,12 +128,17 @@ export type UsageExportRow = {
|
|
|
127
128
|
/** Exact JSON text, preserving integers beyond JavaScript's number range. */
|
|
128
129
|
measurementsJson: string | null;
|
|
129
130
|
evidence: "measured" | "partial" | "missing" | null;
|
|
131
|
+
/** The resource is deleted and every charge it will ever have settled
|
|
132
|
+
* before finalThrough. A volume's backups bill on until retention ends. */
|
|
133
|
+
final: boolean;
|
|
130
134
|
};
|
|
131
135
|
export type UsageExportPage = {
|
|
132
136
|
since: string;
|
|
133
137
|
until: string;
|
|
134
138
|
data: UsageExportRow[];
|
|
135
139
|
nextCursor: string | null;
|
|
140
|
+
/** No row with settledAt before this can still appear or change. */
|
|
141
|
+
finalThrough: string | null;
|
|
136
142
|
/** Concatenate pages in order; only the first page includes the header. */
|
|
137
143
|
csv: string;
|
|
138
144
|
};
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { Page } from "../page.js";
|
|
2
|
+
import type { RequestOptions, Transport } from "../transport.js";
|
|
3
|
+
/** Private until launch.
|
|
4
|
+
* This is the proposed management contract, not an installed API route.
|
|
5
|
+
* SQS clients use the returned sqs URL with their existing client. */
|
|
6
|
+
export type RecordState = "creating" | "ready" | "updating" | "deleting" | "deleted" | "failed";
|
|
7
|
+
export type RecordResource = {
|
|
8
|
+
id: string;
|
|
9
|
+
name: string;
|
|
10
|
+
labels: Record<string, string>;
|
|
11
|
+
state: RecordState;
|
|
12
|
+
createdAt: string;
|
|
13
|
+
error?: {
|
|
14
|
+
code: string;
|
|
15
|
+
message: string;
|
|
16
|
+
hint: string;
|
|
17
|
+
} | null;
|
|
18
|
+
};
|
|
19
|
+
export type QueueSettings = {
|
|
20
|
+
visibilityTimeoutSeconds: number;
|
|
21
|
+
retentionSeconds: number;
|
|
22
|
+
maxMessageBytes: number;
|
|
23
|
+
delaySeconds: number;
|
|
24
|
+
fifo: boolean;
|
|
25
|
+
deadLetter: {
|
|
26
|
+
queue: string;
|
|
27
|
+
maxReceives: number;
|
|
28
|
+
} | null;
|
|
29
|
+
};
|
|
30
|
+
export type Queue = RecordResource & {
|
|
31
|
+
settings: QueueSettings;
|
|
32
|
+
counts: {
|
|
33
|
+
visible: number;
|
|
34
|
+
inFlight: number;
|
|
35
|
+
delayed: number;
|
|
36
|
+
};
|
|
37
|
+
sqs: string;
|
|
38
|
+
};
|
|
39
|
+
export type CreateQueue = {
|
|
40
|
+
name: string;
|
|
41
|
+
labels?: Record<string, string>;
|
|
42
|
+
settings?: Partial<QueueSettings>;
|
|
43
|
+
};
|
|
44
|
+
export type UpdateQueue = Partial<CreateQueue>;
|
|
45
|
+
export type RecordListQuery = {
|
|
46
|
+
name?: string;
|
|
47
|
+
label?: readonly string[];
|
|
48
|
+
state?: readonly RecordState[];
|
|
49
|
+
limit?: number;
|
|
50
|
+
cursor?: string;
|
|
51
|
+
};
|
|
52
|
+
export type RecordWaitOptions = RequestOptions & {
|
|
53
|
+
pollMs?: number;
|
|
54
|
+
};
|
|
55
|
+
export type QueueAttribute = {
|
|
56
|
+
dataType: string;
|
|
57
|
+
stringValue?: string;
|
|
58
|
+
binaryValue?: string;
|
|
59
|
+
};
|
|
60
|
+
export type SendMessage = {
|
|
61
|
+
body: string;
|
|
62
|
+
attributes?: Record<string, QueueAttribute>;
|
|
63
|
+
delaySeconds?: number;
|
|
64
|
+
groupId?: string;
|
|
65
|
+
dedupId?: string;
|
|
66
|
+
};
|
|
67
|
+
export type ReceiveMessages = {
|
|
68
|
+
max?: number;
|
|
69
|
+
waitSeconds?: number;
|
|
70
|
+
visibilitySeconds?: number;
|
|
71
|
+
};
|
|
72
|
+
export type QueueMessage = SendMessage & {
|
|
73
|
+
id: string;
|
|
74
|
+
receiptHandle: string;
|
|
75
|
+
receives: number;
|
|
76
|
+
};
|
|
77
|
+
export type MessageSent = {
|
|
78
|
+
id: string;
|
|
79
|
+
md5: string;
|
|
80
|
+
sequenceNumber?: string;
|
|
81
|
+
};
|
|
82
|
+
export type ReceiptResult = {
|
|
83
|
+
successful: string[];
|
|
84
|
+
failed: {
|
|
85
|
+
receiptHandle: string;
|
|
86
|
+
code: string;
|
|
87
|
+
message: string;
|
|
88
|
+
hint: string;
|
|
89
|
+
}[];
|
|
90
|
+
};
|
|
91
|
+
/** A consume call handles messages sequentially. max counts handler attempts,
|
|
92
|
+
* including failures. Omit max to run until cancelled. timeoutMs bounds each
|
|
93
|
+
* network call, including settlement after cancellation, not the handler.
|
|
94
|
+
* Choose visibilitySeconds longer than the handler's work; delivery is at least once. */
|
|
95
|
+
export type ConsumeOptions = ReceiveMessages & {
|
|
96
|
+
signal?: AbortSignal;
|
|
97
|
+
timeoutMs?: number;
|
|
98
|
+
};
|
|
99
|
+
export type ConsumeResult = {
|
|
100
|
+
processed: number;
|
|
101
|
+
acknowledged: number;
|
|
102
|
+
retried: number;
|
|
103
|
+
aborted: boolean;
|
|
104
|
+
};
|
|
105
|
+
export type QueueHandler = (message: QueueMessage, signal: AbortSignal) => unknown;
|
|
106
|
+
/** Shared only by the private record products. The server owns field defaults
|
|
107
|
+
* and protocol refusals; the SDK preserves their codes and recovery hints. */
|
|
108
|
+
export declare function recordResources<R extends RecordResource, C, U>(t: Transport, plural: "queues" | "streams"): {
|
|
109
|
+
create: (input: C, options?: RequestOptions) => Promise<R>;
|
|
110
|
+
get: (id: string, options?: RequestOptions) => Promise<R>;
|
|
111
|
+
list: (query?: RecordListQuery, options?: RequestOptions) => Promise<Page<R>>;
|
|
112
|
+
update: (id: string, input: U, options?: RequestOptions) => Promise<R>;
|
|
113
|
+
delete: (id: string, options?: RequestOptions) => Promise<R>;
|
|
114
|
+
/** Poll one resource with a deadline across every read, including retries. */
|
|
115
|
+
wait(id: string, target?: "ready" | "deleted", options?: RecordWaitOptions): Promise<R>;
|
|
116
|
+
};
|
|
117
|
+
/** Private until launch. No public Runtime property is installed here. */
|
|
118
|
+
export declare function queues(t: Transport): {
|
|
119
|
+
send: (id: string, input: SendMessage, options?: RequestOptions) => Promise<MessageSent>;
|
|
120
|
+
receive: (id: string, input?: ReceiveMessages, options?: RequestOptions) => Promise<{
|
|
121
|
+
messages: QueueMessage[];
|
|
122
|
+
}>;
|
|
123
|
+
ack: (id: string, input: {
|
|
124
|
+
receiptHandles: string[];
|
|
125
|
+
}, options?: RequestOptions) => Promise<ReceiptResult>;
|
|
126
|
+
retry: (id: string, input: {
|
|
127
|
+
receiptHandle: string;
|
|
128
|
+
visibilitySeconds: number;
|
|
129
|
+
}, options?: RequestOptions) => Promise<{
|
|
130
|
+
receiptHandle: string;
|
|
131
|
+
visibilitySeconds: number;
|
|
132
|
+
}>;
|
|
133
|
+
/** Receive, handle, then acknowledge. No acknowledgement precedes success.
|
|
134
|
+
* A failed or cancelled handler releases its receipt at once. Aborting
|
|
135
|
+
* during receive can lose its reply; that lease expires without an ack.
|
|
136
|
+
* Settlement ignores the cancelled signal and has a bounded deadline.
|
|
137
|
+
* A lost settlement reply is retried once with the same key, even when
|
|
138
|
+
* transport retries were disabled. A remaining failure is thrown. */
|
|
139
|
+
consume(id: string, handler: QueueHandler, options?: ConsumeOptions): Promise<ConsumeResult>;
|
|
140
|
+
/** Purge returns the resource; follow its state with wait(id). */
|
|
141
|
+
purge: (id: string, options?: RequestOptions) => Promise<Queue>;
|
|
142
|
+
create: (input: CreateQueue, options?: RequestOptions) => Promise<Queue>;
|
|
143
|
+
get: (id: string, options?: RequestOptions) => Promise<Queue>;
|
|
144
|
+
list: (query?: RecordListQuery, options?: RequestOptions) => Promise<Page<Queue>>;
|
|
145
|
+
update: (id: string, input: Partial<CreateQueue>, options?: RequestOptions) => Promise<Queue>;
|
|
146
|
+
delete: (id: string, options?: RequestOptions) => Promise<Queue>;
|
|
147
|
+
/** Poll one resource with a deadline across every read, including retries. */
|
|
148
|
+
wait(id: string, target?: "ready" | "deleted", options?: RecordWaitOptions): Promise<Queue>;
|
|
149
|
+
};
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { RuntimeError } from "../errors.js";
|
|
2
|
+
import { Page } from "../page.js";
|
|
3
|
+
import { pause } from "../wait.js";
|
|
4
|
+
/** Stop waiting for user code on abort. Its rejection stays observed even if
|
|
5
|
+
* it finishes later. Handlers should use the signal to stop their own work. */
|
|
6
|
+
function handleMessage(handler, message, signal) {
|
|
7
|
+
return new Promise((resolve) => {
|
|
8
|
+
let finished = false;
|
|
9
|
+
const done = (success) => {
|
|
10
|
+
if (finished)
|
|
11
|
+
return;
|
|
12
|
+
finished = true;
|
|
13
|
+
signal.removeEventListener("abort", abort);
|
|
14
|
+
resolve(success);
|
|
15
|
+
};
|
|
16
|
+
const abort = () => done(false);
|
|
17
|
+
if (signal.aborted) {
|
|
18
|
+
done(false);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
22
|
+
// Observe synchronous throws and late rejections by the same path.
|
|
23
|
+
Promise.resolve()
|
|
24
|
+
.then(() => signal.aborted ? false : Promise.resolve(handler(message, signal)).then(() => true))
|
|
25
|
+
.then(done, () => done(false));
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
/** Shared only by the private record products. The server owns field defaults
|
|
29
|
+
* and protocol refusals; the SDK preserves their codes and recovery hints. */
|
|
30
|
+
export function recordResources(t, plural) {
|
|
31
|
+
const base = `/v1/${plural}`;
|
|
32
|
+
const path = (id) => `${base}/${encodeURIComponent(id)}`;
|
|
33
|
+
const get = (id, options) => t.json({ ...options, method: "GET", path: path(id) });
|
|
34
|
+
const list = async (query = {}, options) => {
|
|
35
|
+
const page = await t.json({
|
|
36
|
+
...options,
|
|
37
|
+
method: "GET",
|
|
38
|
+
path: base,
|
|
39
|
+
query,
|
|
40
|
+
});
|
|
41
|
+
return new Page(page.data, page.nextCursor, (cursor) => list({ ...query, cursor }, options));
|
|
42
|
+
};
|
|
43
|
+
return {
|
|
44
|
+
create: (input, options) => t.json({ ...options, method: "POST", path: base, body: input }),
|
|
45
|
+
get,
|
|
46
|
+
list,
|
|
47
|
+
update: (id, input, options) => t.json({ ...options, method: "POST", path: `${path(id)}:update`, body: input }),
|
|
48
|
+
delete: (id, options) => t.json({ ...options, method: "DELETE", path: path(id) }),
|
|
49
|
+
/** Poll one resource with a deadline across every read, including retries. */
|
|
50
|
+
async wait(id, target = "ready", options = {}) {
|
|
51
|
+
const { pollMs = 250, timeoutMs = 300_000, signal } = options;
|
|
52
|
+
if (!Number.isSafeInteger(pollMs) ||
|
|
53
|
+
pollMs < 0 ||
|
|
54
|
+
!Number.isSafeInteger(timeoutMs) ||
|
|
55
|
+
timeoutMs < 0)
|
|
56
|
+
throw new RuntimeError({
|
|
57
|
+
code: "invalid_request",
|
|
58
|
+
status: 0,
|
|
59
|
+
message: "pollMs and timeoutMs must be nonnegative safe integers.",
|
|
60
|
+
hint: "Use milliseconds; zero timeout disables the deadline.",
|
|
61
|
+
});
|
|
62
|
+
const deadline = new AbortController();
|
|
63
|
+
const combined = signal ? AbortSignal.any([signal, deadline.signal]) : deadline.signal;
|
|
64
|
+
const timer = timeoutMs ? setTimeout(() => deadline.abort(), timeoutMs) : undefined;
|
|
65
|
+
try {
|
|
66
|
+
for (;;) {
|
|
67
|
+
combined.throwIfAborted();
|
|
68
|
+
const current = await get(id, {
|
|
69
|
+
signal: combined,
|
|
70
|
+
timeoutMs: 0,
|
|
71
|
+
});
|
|
72
|
+
combined.throwIfAborted();
|
|
73
|
+
if (current.state === "failed" || (current.state === "deleted" && target !== "deleted"))
|
|
74
|
+
throw new RuntimeError({
|
|
75
|
+
code: current.error?.code ?? "unexpected_state",
|
|
76
|
+
status: 0,
|
|
77
|
+
message: current.error?.message ?? `Resource reached ${current.state}.`,
|
|
78
|
+
hint: current.error?.hint ?? "Read the resource before retrying the change.",
|
|
79
|
+
details: { id, target, state: current.state },
|
|
80
|
+
});
|
|
81
|
+
if (current.state === target)
|
|
82
|
+
return current;
|
|
83
|
+
await pause(pollMs, combined);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch (cause) {
|
|
87
|
+
if (!combined.aborted)
|
|
88
|
+
throw cause;
|
|
89
|
+
throw new RuntimeError({
|
|
90
|
+
code: "timeout",
|
|
91
|
+
status: 0,
|
|
92
|
+
message: "Waiting ended before the target state was observed.",
|
|
93
|
+
hint: "Read the resource to resume; retry a write with its original idempotency key.",
|
|
94
|
+
cause,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
finally {
|
|
98
|
+
clearTimeout(timer);
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/** Private until launch. No public Runtime property is installed here. */
|
|
104
|
+
export function queues(t) {
|
|
105
|
+
const action = (id, verb, body, options) => t.json({
|
|
106
|
+
...options,
|
|
107
|
+
method: "POST",
|
|
108
|
+
path: `/v1/queues/${encodeURIComponent(id)}:${verb}`,
|
|
109
|
+
body,
|
|
110
|
+
});
|
|
111
|
+
return {
|
|
112
|
+
...recordResources(t, "queues"),
|
|
113
|
+
send: (id, input, options) => action(id, "send", input, options),
|
|
114
|
+
receive: (id, input = {}, options) => action(id, "receive", input, options),
|
|
115
|
+
ack: (id, input, options) => action(id, "ack", input, options),
|
|
116
|
+
retry: (id, input, options) => action(id, "retry", input, options),
|
|
117
|
+
/** Receive, handle, then acknowledge. No acknowledgement precedes success.
|
|
118
|
+
* A failed or cancelled handler releases its receipt at once. Aborting
|
|
119
|
+
* during receive can lose its reply; that lease expires without an ack.
|
|
120
|
+
* Settlement ignores the cancelled signal and has a bounded deadline.
|
|
121
|
+
* A lost settlement reply is retried once with the same key, even when
|
|
122
|
+
* transport retries were disabled. A remaining failure is thrown. */
|
|
123
|
+
async consume(id, handler, options = {}) {
|
|
124
|
+
const { max, waitSeconds = 20, visibilitySeconds, timeoutMs = 30_000 } = options;
|
|
125
|
+
for (const [field, value, minimum, maximum] of [
|
|
126
|
+
["max", max, 0, Number.MAX_SAFE_INTEGER],
|
|
127
|
+
["waitSeconds", waitSeconds, 0, 20],
|
|
128
|
+
["visibilitySeconds", visibilitySeconds, 0, 43_200],
|
|
129
|
+
["timeoutMs", timeoutMs, 1, 2_147_483_647],
|
|
130
|
+
]) {
|
|
131
|
+
if (value !== undefined &&
|
|
132
|
+
(!Number.isSafeInteger(value) || value < minimum || value > maximum))
|
|
133
|
+
throw new RuntimeError({
|
|
134
|
+
code: "invalid_request",
|
|
135
|
+
status: 0,
|
|
136
|
+
message: `${field} must be an integer from ${minimum} to ${maximum}.`,
|
|
137
|
+
hint: "Correct the consume option; no message was received.",
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
const signal = options.signal ?? new AbortController().signal;
|
|
141
|
+
const result = { processed: 0, acknowledged: 0, retried: 0, aborted: false };
|
|
142
|
+
const settle = async (verb, body) => {
|
|
143
|
+
const request = { idempotencyKey: crypto.randomUUID(), timeoutMs };
|
|
144
|
+
try {
|
|
145
|
+
return await action(id, verb, body, request);
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
if (!(error instanceof RuntimeError) ||
|
|
149
|
+
!["connection_error", "timeout"].includes(error.code))
|
|
150
|
+
throw error;
|
|
151
|
+
return action(id, verb, body, request);
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
while (!signal.aborted && (max === undefined || result.processed < max)) {
|
|
155
|
+
let received;
|
|
156
|
+
try {
|
|
157
|
+
received = await action(id, "receive", {
|
|
158
|
+
max: 1,
|
|
159
|
+
waitSeconds,
|
|
160
|
+
...(visibilitySeconds === undefined ? {} : { visibilitySeconds }),
|
|
161
|
+
}, { signal, timeoutMs, idempotencyKey: crypto.randomUUID() });
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
if (!signal.aborted)
|
|
165
|
+
throw error;
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
if (received.messages.length > 1)
|
|
169
|
+
throw new RuntimeError({
|
|
170
|
+
code: "invalid_response",
|
|
171
|
+
status: 0,
|
|
172
|
+
message: "Receive returned more than the one message requested.",
|
|
173
|
+
hint: "No message was acknowledged; their visibility leases will expire.",
|
|
174
|
+
});
|
|
175
|
+
const message = received.messages[0];
|
|
176
|
+
if (!message) {
|
|
177
|
+
// Empty short polls must yield to cancellation and avoid a hot loop.
|
|
178
|
+
await pause(100, signal).catch(() => { });
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
let success = false;
|
|
182
|
+
if (!signal.aborted) {
|
|
183
|
+
result.processed++;
|
|
184
|
+
success = await handleMessage(handler, message, signal);
|
|
185
|
+
}
|
|
186
|
+
if (success && !signal.aborted) {
|
|
187
|
+
const ack = await settle("ack", {
|
|
188
|
+
receiptHandles: [message.receiptHandle],
|
|
189
|
+
});
|
|
190
|
+
const failed = ack.failed[0];
|
|
191
|
+
if (failed || !ack.successful.includes(message.receiptHandle))
|
|
192
|
+
throw new RuntimeError({
|
|
193
|
+
code: failed?.code ?? "invalid_response",
|
|
194
|
+
status: 0,
|
|
195
|
+
message: failed?.message ?? "The receipt was not acknowledged.",
|
|
196
|
+
hint: failed?.hint ??
|
|
197
|
+
"Do not assume delivery completed; this message can be delivered again.",
|
|
198
|
+
details: { receiptHandle: message.receiptHandle },
|
|
199
|
+
});
|
|
200
|
+
result.acknowledged++;
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
await settle("retry", { receiptHandle: message.receiptHandle, visibilitySeconds: 0 });
|
|
204
|
+
result.retried++;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
result.aborted = signal.aborted;
|
|
208
|
+
return result;
|
|
209
|
+
},
|
|
210
|
+
/** Purge returns the resource; follow its state with wait(id). */
|
|
211
|
+
purge: (id, options) => action(id, "purge", {}, options),
|
|
212
|
+
};
|
|
213
|
+
}
|
|
@@ -5,13 +5,16 @@ export type ReferralSummary = {
|
|
|
5
5
|
code: string;
|
|
6
6
|
/** Share this: https://withruntime.com/r/<code>. */
|
|
7
7
|
link: string;
|
|
8
|
-
/** The least each side gets
|
|
8
|
+
/** The least each side gets on the referred company's first qualifying
|
|
9
|
+
* payment: a smaller one is matched up to this. */
|
|
9
10
|
rewardMicros: string;
|
|
10
|
-
/** The most each side gets
|
|
11
|
+
/** The most each side gets per referred company, all its matched payments
|
|
12
|
+
* together. */
|
|
11
13
|
maxRewardMicros: string;
|
|
12
|
-
/** The smallest
|
|
14
|
+
/** The smallest payment a referral matches. */
|
|
13
15
|
minPurchaseMicros: string;
|
|
14
|
-
/** The most referral credit you can earn in
|
|
16
|
+
/** The most referral credit, matches and share together, you can earn in
|
|
17
|
+
* one calendar year (UTC). */
|
|
15
18
|
yearlyCapMicros: string;
|
|
16
19
|
enabled: boolean;
|
|
17
20
|
year: number;
|
|
@@ -27,10 +30,21 @@ export type ReferralSummary = {
|
|
|
27
30
|
status: "pending" | "paid" | "refused" | "reversed";
|
|
28
31
|
creditMicros: string;
|
|
29
32
|
} | null;
|
|
33
|
+
/** Referred companies with a matched payment still standing. */
|
|
34
|
+
payingCompanies: number;
|
|
35
|
+
/** Share credit you kept, all time. */
|
|
36
|
+
shareEarnedMicros: string;
|
|
37
|
+
/** Your share of what a referred company spends from credit it paid for,
|
|
38
|
+
* in basis points: 1000 is 10%. */
|
|
39
|
+
shareBasisPoints: number;
|
|
40
|
+
/** How long the share runs from a company's first matched payment. */
|
|
41
|
+
shareMonths: number;
|
|
42
|
+
/** How long after sign-up a referred company's payments are matched. */
|
|
43
|
+
windowDays: number;
|
|
30
44
|
};
|
|
31
|
-
/** `runtime.referrals
|
|
32
|
-
* its first
|
|
33
|
-
*
|
|
45
|
+
/** `runtime.referrals`: a company that signs up with your link has every
|
|
46
|
+
* payment in its first 30 days matched, up to $1,000, and you get the same;
|
|
47
|
+
* then for 12 months you earn 10% of what it spends from credit it paid for. */
|
|
34
48
|
export declare function referrals(t: Transport): {
|
|
35
49
|
get: (options?: RequestOptions) => Promise<ReferralSummary>;
|
|
36
50
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
/** `runtime.referrals
|
|
2
|
-
* its first
|
|
3
|
-
*
|
|
1
|
+
/** `runtime.referrals`: a company that signs up with your link has every
|
|
2
|
+
* payment in its first 30 days matched, up to $1,000, and you get the same;
|
|
3
|
+
* then for 12 months you earn 10% of what it spends from credit it paid for. */
|
|
4
4
|
export function referrals(t) {
|
|
5
5
|
return {
|
|
6
6
|
get: (options) => t.json({ method: "GET", path: "/v1/referrals", ...options }),
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Page } from "../page.js";
|
|
2
|
+
import type { RequestOptions, Transport } from "../transport.js";
|
|
3
|
+
/** The five verbs every resource product shares (CLOUD.md section 4, "One
|
|
4
|
+
* shape for every interface"; packages/cloud/src/api/resource-product.ts),
|
|
5
|
+
* and following one to a state. A product builds its client on these and
|
|
6
|
+
* adds only its own verbs. */
|
|
7
|
+
export type ResourceRecord = {
|
|
8
|
+
id: string;
|
|
9
|
+
state: string;
|
|
10
|
+
error: {
|
|
11
|
+
code: string;
|
|
12
|
+
message: string;
|
|
13
|
+
} | null;
|
|
14
|
+
};
|
|
15
|
+
export type ResourceListQuery<S extends string = string> = {
|
|
16
|
+
name?: string;
|
|
17
|
+
/** Repeated key:value filters; all must match. */
|
|
18
|
+
label?: readonly string[];
|
|
19
|
+
/** Defaults to the product's live states. */
|
|
20
|
+
state?: readonly S[];
|
|
21
|
+
/** 1 to 100; defaults to 50. */
|
|
22
|
+
limit?: number;
|
|
23
|
+
cursor?: string;
|
|
24
|
+
};
|
|
25
|
+
export type ResourceGetOptions<S extends string = string> = RequestOptions & {
|
|
26
|
+
waitFor?: S;
|
|
27
|
+
/** Server wait, 1 to 60 seconds; defaults to 30. Check the returned state. */
|
|
28
|
+
timeoutSeconds?: number;
|
|
29
|
+
};
|
|
30
|
+
export type ResourceWaitOptions = RequestOptions & {
|
|
31
|
+
/** Whole wait, including retries; defaults to 300000 ms. 0 disables it. */
|
|
32
|
+
timeoutMs?: number;
|
|
33
|
+
/** Delay between reads; defaults to 250 ms. */
|
|
34
|
+
pollMs?: number;
|
|
35
|
+
};
|
|
36
|
+
export type ResourceDeleteOptions = ResourceWaitOptions & {
|
|
37
|
+
wait?: boolean;
|
|
38
|
+
};
|
|
39
|
+
export declare const enc: typeof encodeURIComponent;
|
|
40
|
+
/** One deadline covers the write, retries and every read. A timed-out delete
|
|
41
|
+
* can be retried after a process restart with this same key. */
|
|
42
|
+
export declare function following<T>(options: ResourceWaitOptions, work: (request: RequestOptions, pause: () => Promise<void>) => Promise<T>): Promise<T>;
|
|
43
|
+
/** A resource product's client at `/v1/<path>`. A wait ends at its target
|
|
44
|
+
* or at any of `settled`, which answers with the resource's error. */
|
|
45
|
+
export declare function resource<R extends ResourceRecord, C, U, S extends string = R["state"]>(t: Transport, path: string, settled: readonly S[]): {
|
|
46
|
+
base: string;
|
|
47
|
+
/** Defaults belong to the API, so the SDK does not override them. */
|
|
48
|
+
create: (input: C, options?: RequestOptions) => Promise<R>;
|
|
49
|
+
get: (id: string, options?: ResourceGetOptions<S>) => Promise<R>;
|
|
50
|
+
list: (query?: ResourceListQuery<S>, options?: RequestOptions) => Promise<Page<R>>;
|
|
51
|
+
update: (id: string, input: U, options?: RequestOptions) => Promise<R>;
|
|
52
|
+
wait: (id: string, target: S, options?: ResourceWaitOptions) => Promise<R>;
|
|
53
|
+
delete: (id: string, options?: ResourceDeleteOptions) => Promise<R>;
|
|
54
|
+
};
|