@runnee/sdk 0.0.0-stage → 0.1.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/CHANGELOG.md +9 -0
- package/CONTRIBUTING.md +29 -0
- package/LICENSE +21 -0
- package/README.md +76 -2
- package/RELEASING.md +32 -0
- package/SECURITY.md +11 -0
- package/lib/cjs/contracts.d.ts +1279 -0
- package/lib/cjs/contracts.js +1391 -0
- package/lib/cjs/errors.d.ts +34 -0
- package/lib/cjs/errors.js +49 -0
- package/lib/cjs/http.d.ts +25 -0
- package/lib/cjs/http.js +260 -0
- package/lib/cjs/index.d.ts +57 -0
- package/lib/cjs/index.js +86 -0
- package/lib/cjs/package.json +2 -0
- package/lib/cjs/types.d.ts +304 -0
- package/lib/cjs/types.js +2 -0
- package/lib/cjs/validation.d.ts +3 -0
- package/lib/cjs/validation.js +102 -0
- package/lib/esm/contracts.d.ts +1279 -0
- package/lib/esm/contracts.js +1388 -0
- package/lib/esm/errors.d.ts +34 -0
- package/lib/esm/errors.js +34 -0
- package/lib/esm/http.d.ts +25 -0
- package/lib/esm/http.js +256 -0
- package/lib/esm/index.d.ts +57 -0
- package/lib/esm/index.js +68 -0
- package/lib/esm/types.d.ts +304 -0
- package/lib/esm/types.js +1 -0
- package/lib/esm/validation.d.ts +3 -0
- package/lib/esm/validation.js +98 -0
- package/openapi.json +7932 -0
- package/package.json +64 -4
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
export type Error = {
|
|
2
|
+
statusCode?: number;
|
|
3
|
+
message?: string | string[];
|
|
4
|
+
error?: string;
|
|
5
|
+
code?: string;
|
|
6
|
+
[key: string]: unknown;
|
|
7
|
+
};
|
|
8
|
+
export type ExecuteRequest = {
|
|
9
|
+
code: string;
|
|
10
|
+
mode?: 'dry-run' | 'live';
|
|
11
|
+
event?: {
|
|
12
|
+
[key: string]: unknown;
|
|
13
|
+
};
|
|
14
|
+
retainSource?: boolean;
|
|
15
|
+
scriptName?: string;
|
|
16
|
+
secretNames?: string[];
|
|
17
|
+
scriptId?: string;
|
|
18
|
+
};
|
|
19
|
+
export type Diagnostic = {
|
|
20
|
+
code?: string;
|
|
21
|
+
message: string;
|
|
22
|
+
line?: number;
|
|
23
|
+
column?: number;
|
|
24
|
+
filename?: string;
|
|
25
|
+
snippet?: string;
|
|
26
|
+
[key: string]: unknown;
|
|
27
|
+
};
|
|
28
|
+
export type Timings = {
|
|
29
|
+
[key: string]: number;
|
|
30
|
+
};
|
|
31
|
+
export type ExecutionResult = {
|
|
32
|
+
durationMs: number;
|
|
33
|
+
instructions: number;
|
|
34
|
+
returnValue?: unknown;
|
|
35
|
+
replies: {
|
|
36
|
+
count: number;
|
|
37
|
+
lastPayload?: unknown;
|
|
38
|
+
};
|
|
39
|
+
counters?: {
|
|
40
|
+
dbOps: number;
|
|
41
|
+
httpCalls: number;
|
|
42
|
+
};
|
|
43
|
+
explain?: {
|
|
44
|
+
[key: string]: unknown;
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
export type ExecuteResponse = {
|
|
48
|
+
success: boolean;
|
|
49
|
+
mocked?: boolean;
|
|
50
|
+
error?: Diagnostic;
|
|
51
|
+
executionResult?: ExecutionResult;
|
|
52
|
+
replies?: unknown[];
|
|
53
|
+
metrics?: {
|
|
54
|
+
[key: string]: unknown;
|
|
55
|
+
};
|
|
56
|
+
telemetryRecorded?: boolean;
|
|
57
|
+
executionId?: string | null;
|
|
58
|
+
replayed?: boolean;
|
|
59
|
+
originalResultOmitted?: boolean;
|
|
60
|
+
[key: string]: unknown;
|
|
61
|
+
};
|
|
62
|
+
export type Policy = {
|
|
63
|
+
[key: string]: unknown;
|
|
64
|
+
};
|
|
65
|
+
export type Execution = {
|
|
66
|
+
id: string;
|
|
67
|
+
scriptName?: string | null;
|
|
68
|
+
mode: string;
|
|
69
|
+
status: string;
|
|
70
|
+
durationMs: number;
|
|
71
|
+
instructionsUsed: number;
|
|
72
|
+
errorCode?: string | null;
|
|
73
|
+
httpCalls?: number;
|
|
74
|
+
dbOps?: number;
|
|
75
|
+
errorMessage?: string | null;
|
|
76
|
+
hasSource?: boolean;
|
|
77
|
+
timings?: Timings;
|
|
78
|
+
createdAt: string;
|
|
79
|
+
expiresAt?: string;
|
|
80
|
+
[key: string]: unknown;
|
|
81
|
+
};
|
|
82
|
+
export type ExecutionDetail = {
|
|
83
|
+
id: string;
|
|
84
|
+
scriptName?: string | null;
|
|
85
|
+
mode: string;
|
|
86
|
+
status: string;
|
|
87
|
+
durationMs: number;
|
|
88
|
+
instructionsUsed: number;
|
|
89
|
+
errorCode?: string | null;
|
|
90
|
+
httpCalls?: number;
|
|
91
|
+
dbOps?: number;
|
|
92
|
+
errorMessage?: string | null;
|
|
93
|
+
hasSource?: boolean;
|
|
94
|
+
timings?: Timings;
|
|
95
|
+
createdAt: string;
|
|
96
|
+
expiresAt?: string;
|
|
97
|
+
sourceCode?: string | null;
|
|
98
|
+
outputPreview?: string | null;
|
|
99
|
+
[key: string]: unknown;
|
|
100
|
+
};
|
|
101
|
+
export type ExecutionPage = {
|
|
102
|
+
items: Execution[];
|
|
103
|
+
nextCursor: string | null;
|
|
104
|
+
policy: Policy;
|
|
105
|
+
};
|
|
106
|
+
export type Script = {
|
|
107
|
+
id: string;
|
|
108
|
+
name: string;
|
|
109
|
+
currentVersion: number;
|
|
110
|
+
createdAt: string;
|
|
111
|
+
updatedAt: string;
|
|
112
|
+
};
|
|
113
|
+
export type ScriptDetail = {
|
|
114
|
+
id: string;
|
|
115
|
+
name: string;
|
|
116
|
+
currentVersion: number;
|
|
117
|
+
createdAt: string;
|
|
118
|
+
updatedAt: string;
|
|
119
|
+
code: string;
|
|
120
|
+
versions: {
|
|
121
|
+
version: number;
|
|
122
|
+
createdAt: string;
|
|
123
|
+
bytes: number;
|
|
124
|
+
}[];
|
|
125
|
+
[key: string]: unknown;
|
|
126
|
+
};
|
|
127
|
+
export type ScriptList = {
|
|
128
|
+
items: Script[];
|
|
129
|
+
policy: Policy;
|
|
130
|
+
};
|
|
131
|
+
export type ScriptCreate = {
|
|
132
|
+
name: string;
|
|
133
|
+
code: string;
|
|
134
|
+
};
|
|
135
|
+
export type ScriptUpdate = {
|
|
136
|
+
name: string;
|
|
137
|
+
code: string;
|
|
138
|
+
expectedVersion: number;
|
|
139
|
+
};
|
|
140
|
+
export type ScriptRestore = {
|
|
141
|
+
version: number;
|
|
142
|
+
expectedVersion: number;
|
|
143
|
+
};
|
|
144
|
+
export type ScriptVersion = {
|
|
145
|
+
version: number;
|
|
146
|
+
code: string;
|
|
147
|
+
createdAt: string;
|
|
148
|
+
};
|
|
149
|
+
export type Deleted = {
|
|
150
|
+
deleted: boolean;
|
|
151
|
+
};
|
|
152
|
+
export type Storage = {
|
|
153
|
+
policy: Policy;
|
|
154
|
+
history: {
|
|
155
|
+
count: number;
|
|
156
|
+
bytes: number;
|
|
157
|
+
};
|
|
158
|
+
scripts: {
|
|
159
|
+
count: number;
|
|
160
|
+
versions: number;
|
|
161
|
+
bytes: number;
|
|
162
|
+
};
|
|
163
|
+
};
|
|
164
|
+
export type Secret = {
|
|
165
|
+
name: string;
|
|
166
|
+
allowInline: boolean;
|
|
167
|
+
scriptIds: string[];
|
|
168
|
+
version: number;
|
|
169
|
+
createdAt: string;
|
|
170
|
+
updatedAt: string;
|
|
171
|
+
};
|
|
172
|
+
export type SecretList = {
|
|
173
|
+
items: Secret[];
|
|
174
|
+
};
|
|
175
|
+
export type SecretCreate = {
|
|
176
|
+
name: string;
|
|
177
|
+
value: string;
|
|
178
|
+
allowInline?: boolean;
|
|
179
|
+
scriptIds?: string[];
|
|
180
|
+
};
|
|
181
|
+
export type SecretUpdate = {
|
|
182
|
+
value?: string;
|
|
183
|
+
allowInline?: boolean;
|
|
184
|
+
scriptIds?: string[];
|
|
185
|
+
};
|
|
186
|
+
export type SandboxLimits = {
|
|
187
|
+
timeoutMs: number;
|
|
188
|
+
maxInstructions: number;
|
|
189
|
+
maxMemoryMb: number;
|
|
190
|
+
};
|
|
191
|
+
export type Sandbox = {
|
|
192
|
+
plan: string;
|
|
193
|
+
configurable: boolean;
|
|
194
|
+
limits: SandboxLimits;
|
|
195
|
+
defaults: SandboxLimits;
|
|
196
|
+
maxLimits: SandboxLimits;
|
|
197
|
+
};
|
|
198
|
+
export type Plan = {
|
|
199
|
+
id: string;
|
|
200
|
+
configurable?: boolean;
|
|
201
|
+
limits: SandboxLimits;
|
|
202
|
+
maxLimits?: SandboxLimits;
|
|
203
|
+
name: string;
|
|
204
|
+
monthlyUsd?: number | null;
|
|
205
|
+
yearlyMonthlyUsd?: number | null;
|
|
206
|
+
usageUsdPer1k?: number;
|
|
207
|
+
executionsPerMonth: number | null;
|
|
208
|
+
timeoutMs?: number;
|
|
209
|
+
features?: string[];
|
|
210
|
+
[key: string]: unknown;
|
|
211
|
+
};
|
|
212
|
+
export type Plans = {
|
|
213
|
+
configured: boolean;
|
|
214
|
+
plans: Plan[];
|
|
215
|
+
};
|
|
216
|
+
export type BillingStatus = {
|
|
217
|
+
configured: boolean;
|
|
218
|
+
plan: Plan;
|
|
219
|
+
subscription: {
|
|
220
|
+
status?: string;
|
|
221
|
+
interval?: string | null;
|
|
222
|
+
currentPeriodEnd?: string | null;
|
|
223
|
+
cancelAtPeriodEnd?: boolean;
|
|
224
|
+
hasCustomer?: boolean;
|
|
225
|
+
[key: string]: unknown;
|
|
226
|
+
} | null;
|
|
227
|
+
usage: {
|
|
228
|
+
executions: number;
|
|
229
|
+
limit: number | null;
|
|
230
|
+
resetsAt: string;
|
|
231
|
+
};
|
|
232
|
+
};
|
|
233
|
+
export type UsageSummary = {
|
|
234
|
+
windowDays: number;
|
|
235
|
+
totalExecutions: number;
|
|
236
|
+
successfulExecutions: number;
|
|
237
|
+
failedExecutions: number;
|
|
238
|
+
averageDurationMs: number;
|
|
239
|
+
latest: {
|
|
240
|
+
[key: string]: unknown;
|
|
241
|
+
}[];
|
|
242
|
+
};
|
|
243
|
+
export type UsageHistory = {
|
|
244
|
+
days: number;
|
|
245
|
+
total: number;
|
|
246
|
+
success: number;
|
|
247
|
+
failed: number;
|
|
248
|
+
successRate: number;
|
|
249
|
+
daily: {
|
|
250
|
+
date: string;
|
|
251
|
+
total: number;
|
|
252
|
+
success: number;
|
|
253
|
+
failed: number;
|
|
254
|
+
avgDurationMs: number;
|
|
255
|
+
}[];
|
|
256
|
+
topScripts: {
|
|
257
|
+
name: string;
|
|
258
|
+
count: number;
|
|
259
|
+
}[];
|
|
260
|
+
topScriptsSample: {
|
|
261
|
+
days: number;
|
|
262
|
+
limit: number;
|
|
263
|
+
};
|
|
264
|
+
apiKeys: {
|
|
265
|
+
active: number;
|
|
266
|
+
lastUsedAt: string | null;
|
|
267
|
+
};
|
|
268
|
+
};
|
|
269
|
+
export type TelemetryExecution = {
|
|
270
|
+
scriptName?: string;
|
|
271
|
+
mode?: string;
|
|
272
|
+
status: string;
|
|
273
|
+
durationMs: number;
|
|
274
|
+
instructionsUsed: number;
|
|
275
|
+
memoryUsageKb?: number;
|
|
276
|
+
errorCode?: string;
|
|
277
|
+
metadata?: {
|
|
278
|
+
[key: string]: unknown;
|
|
279
|
+
};
|
|
280
|
+
};
|
|
281
|
+
export type TelemetryEvent = {
|
|
282
|
+
eventName: string;
|
|
283
|
+
pagePath?: string;
|
|
284
|
+
sessionId?: string;
|
|
285
|
+
fingerprintHash?: string;
|
|
286
|
+
properties?: {
|
|
287
|
+
[key: string]: string | number | boolean | null;
|
|
288
|
+
};
|
|
289
|
+
};
|
|
290
|
+
export type TelemetryRecorded = {
|
|
291
|
+
[key: string]: unknown;
|
|
292
|
+
} | string | null;
|
|
293
|
+
export type Accepted = {
|
|
294
|
+
accepted: boolean;
|
|
295
|
+
};
|
|
296
|
+
export type Health = {
|
|
297
|
+
status: 'operational' | 'degraded';
|
|
298
|
+
timestamp: string;
|
|
299
|
+
services: {
|
|
300
|
+
id: string;
|
|
301
|
+
name: string;
|
|
302
|
+
status: 'operational' | 'degraded';
|
|
303
|
+
}[];
|
|
304
|
+
};
|
package/lib/esm/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { contracts } from './contracts.js';
|
|
2
|
+
import { RunneeResponseError, RunneeValidationError } from './errors.js';
|
|
3
|
+
const schemas = contracts.schemas;
|
|
4
|
+
function valid(value, s, depth = 0) {
|
|
5
|
+
if (depth > 40)
|
|
6
|
+
return false;
|
|
7
|
+
if (s.$ref)
|
|
8
|
+
return valid(value, schemas[s.$ref.split('/').pop()], depth + 1);
|
|
9
|
+
if (s.anyOf)
|
|
10
|
+
return s.anyOf.some((sub) => valid(value, sub, depth + 1));
|
|
11
|
+
if (s.enum && !s.enum.includes(value))
|
|
12
|
+
return false;
|
|
13
|
+
if (s.type === 'null')
|
|
14
|
+
return value === null;
|
|
15
|
+
if (s.type === 'boolean')
|
|
16
|
+
return typeof value === 'boolean';
|
|
17
|
+
if (s.type === 'number' || s.type === 'integer')
|
|
18
|
+
return (typeof value === 'number' &&
|
|
19
|
+
Number.isFinite(value) &&
|
|
20
|
+
(s.type !== 'integer' || Number.isSafeInteger(value)) &&
|
|
21
|
+
(s.minimum === undefined || value >= s.minimum) &&
|
|
22
|
+
(s.maximum === undefined || value <= s.maximum));
|
|
23
|
+
if (s.type === 'string')
|
|
24
|
+
return (typeof value === 'string' &&
|
|
25
|
+
(s.minLength === undefined || value.length >= s.minLength) &&
|
|
26
|
+
(s.maxLength === undefined || value.length <= s.maxLength) &&
|
|
27
|
+
(!s.pattern || new RegExp(s.pattern).test(value)) &&
|
|
28
|
+
(s.format !== 'uuid' || /^[a-f\d]{8}-(?:[a-f\d]{4}-){3}[a-f\d]{12}$/i.test(value)));
|
|
29
|
+
if (s.type === 'array')
|
|
30
|
+
return (Array.isArray(value) &&
|
|
31
|
+
(s.minItems === undefined || value.length >= s.minItems) &&
|
|
32
|
+
(s.maxItems === undefined || value.length <= s.maxItems) &&
|
|
33
|
+
(!s.uniqueItems || new Set(value.map((v) => JSON.stringify(v))).size === value.length) &&
|
|
34
|
+
value.every((v) => valid(v, s.items ?? {}, depth + 1)));
|
|
35
|
+
if (s.type === 'object') {
|
|
36
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
37
|
+
return false;
|
|
38
|
+
const record = value;
|
|
39
|
+
return ((!s.minProperties || Object.keys(record).length >= s.minProperties) &&
|
|
40
|
+
(s.required ?? []).every((k) => Object.hasOwn(record, k)) &&
|
|
41
|
+
Object.entries(record).every(([k, v]) => Object.hasOwn(s.properties ?? {}, k)
|
|
42
|
+
? valid(v, s.properties[k], depth + 1)
|
|
43
|
+
: s.additionalProperties === false
|
|
44
|
+
? false
|
|
45
|
+
: typeof s.additionalProperties === 'object'
|
|
46
|
+
? valid(v, s.additionalProperties, depth + 1)
|
|
47
|
+
: true));
|
|
48
|
+
}
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
export function validate(value, name, response, requestId) {
|
|
52
|
+
if (!schemas[name] || !valid(value, schemas[name])) {
|
|
53
|
+
const ErrorClass = response ? RunneeResponseError : RunneeValidationError;
|
|
54
|
+
throw new ErrorClass(response
|
|
55
|
+
? 'Runnee returned an unexpected response.'
|
|
56
|
+
: 'Request does not match the Runnee API contract.', { requestId });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/** Reject non-JSON inputs before I/O; serialization failures must never look retryable. */
|
|
60
|
+
export function encodeRequest(value) {
|
|
61
|
+
const ancestors = new Set();
|
|
62
|
+
let count = 0, characters = 0;
|
|
63
|
+
const walk = (v, depth = 0) => {
|
|
64
|
+
if (++count > 10000 || depth > 20)
|
|
65
|
+
throw new RunneeValidationError('Request JSON exceeds the nesting or value limit.');
|
|
66
|
+
if (v === null || typeof v === 'boolean')
|
|
67
|
+
return;
|
|
68
|
+
if (typeof v === 'string') {
|
|
69
|
+
characters += v.length;
|
|
70
|
+
if (characters > 102400)
|
|
71
|
+
throw new RunneeValidationError('Request JSON is too large.');
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (typeof v === 'number' && Number.isFinite(v))
|
|
75
|
+
return;
|
|
76
|
+
if (v &&
|
|
77
|
+
typeof v === 'object' &&
|
|
78
|
+
(Array.isArray(v) ||
|
|
79
|
+
Object.getPrototypeOf(v) === Object.prototype ||
|
|
80
|
+
Object.getPrototypeOf(v) === null)) {
|
|
81
|
+
if (ancestors.has(v))
|
|
82
|
+
throw new RunneeValidationError('Request JSON contains a cycle.');
|
|
83
|
+
ancestors.add(v);
|
|
84
|
+
for (const [key, entry] of Object.entries(v)) {
|
|
85
|
+
characters += key.length;
|
|
86
|
+
walk(entry, depth + 1);
|
|
87
|
+
}
|
|
88
|
+
ancestors.delete(v);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
throw new RunneeValidationError('Request data must contain only JSON values.');
|
|
92
|
+
};
|
|
93
|
+
walk(value);
|
|
94
|
+
const encoded = JSON.stringify(value);
|
|
95
|
+
if (new TextEncoder().encode(encoded).byteLength > 102400)
|
|
96
|
+
throw new RunneeValidationError('Request JSON exceeds 100 KiB.');
|
|
97
|
+
return encoded;
|
|
98
|
+
}
|