deepline 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +212 -54
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +198 -40
- package/dist/cli/index.mjs.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/repo/apps/play-runner-workers/src/coordinator-entry.ts +3256 -0
- package/dist/repo/apps/play-runner-workers/src/dedup-do.ts +710 -0
- package/dist/repo/apps/play-runner-workers/src/entry.ts +5070 -0
- package/dist/repo/apps/play-runner-workers/src/runtime/README.md +21 -0
- package/dist/repo/apps/play-runner-workers/src/runtime/batching.ts +177 -0
- package/dist/repo/apps/play-runner-workers/src/runtime/execution-plan.ts +52 -0
- package/dist/repo/apps/play-runner-workers/src/runtime/tool-batch.ts +100 -0
- package/dist/repo/apps/play-runner-workers/src/runtime/tool-result.ts +184 -0
- package/dist/repo/sdk/src/cli/commands/auth.ts +482 -0
- package/dist/repo/sdk/src/cli/commands/billing.ts +188 -0
- package/dist/repo/sdk/src/cli/commands/csv.ts +123 -0
- package/dist/repo/sdk/src/cli/commands/db.ts +119 -0
- package/dist/repo/sdk/src/cli/commands/feedback.ts +40 -0
- package/dist/repo/sdk/src/cli/commands/org.ts +117 -0
- package/dist/repo/sdk/src/cli/commands/play.ts +3200 -0
- package/dist/repo/sdk/src/cli/commands/tools.ts +687 -0
- package/dist/repo/sdk/src/cli/dataset-stats.ts +341 -0
- package/dist/repo/sdk/src/cli/index.ts +138 -0
- package/dist/repo/sdk/src/cli/progress.ts +135 -0
- package/dist/repo/sdk/src/cli/trace.ts +61 -0
- package/dist/repo/sdk/src/cli/utils.ts +145 -0
- package/dist/repo/sdk/src/client.ts +1188 -0
- package/dist/repo/sdk/src/compat.ts +77 -0
- package/dist/repo/sdk/src/config.ts +285 -0
- package/dist/repo/sdk/src/errors.ts +125 -0
- package/dist/repo/sdk/src/http.ts +391 -0
- package/dist/repo/sdk/src/index.ts +139 -0
- package/dist/repo/sdk/src/play.ts +1330 -0
- package/dist/repo/sdk/src/plays/bundle-play-file.ts +133 -0
- package/dist/repo/sdk/src/plays/harness-stub.ts +210 -0
- package/dist/repo/sdk/src/plays/local-file-discovery.ts +326 -0
- package/dist/repo/sdk/src/tool-output.ts +489 -0
- package/dist/repo/sdk/src/types.ts +669 -0
- package/dist/repo/sdk/src/version.ts +2 -0
- package/dist/repo/sdk/src/worker-play-entry.ts +286 -0
- package/dist/repo/shared_libs/observability/node-tracing.ts +129 -0
- package/dist/repo/shared_libs/observability/tracing.ts +98 -0
- package/dist/repo/shared_libs/play-runtime/backend.ts +139 -0
- package/dist/repo/shared_libs/play-runtime/batch-runtime.ts +182 -0
- package/dist/repo/shared_libs/play-runtime/batching-types.ts +91 -0
- package/dist/repo/shared_libs/play-runtime/context.ts +3999 -0
- package/dist/repo/shared_libs/play-runtime/coordinator-headers.ts +78 -0
- package/dist/repo/shared_libs/play-runtime/ctx-contract.ts +250 -0
- package/dist/repo/shared_libs/play-runtime/ctx-types.ts +713 -0
- package/dist/repo/shared_libs/play-runtime/dataset-id.ts +10 -0
- package/dist/repo/shared_libs/play-runtime/db-session-crypto.ts +304 -0
- package/dist/repo/shared_libs/play-runtime/db-session.ts +462 -0
- package/dist/repo/shared_libs/play-runtime/dedup-backend.ts +0 -0
- package/dist/repo/shared_libs/play-runtime/default-batch-strategies.ts +124 -0
- package/dist/repo/shared_libs/play-runtime/execution-plan.ts +262 -0
- package/dist/repo/shared_libs/play-runtime/live-events.ts +214 -0
- package/dist/repo/shared_libs/play-runtime/live-state-contract.ts +50 -0
- package/dist/repo/shared_libs/play-runtime/map-execution-frame.ts +114 -0
- package/dist/repo/shared_libs/play-runtime/map-row-identity.ts +158 -0
- package/dist/repo/shared_libs/play-runtime/profiles.ts +90 -0
- package/dist/repo/shared_libs/play-runtime/progress-emitter.ts +172 -0
- package/dist/repo/shared_libs/play-runtime/protocol.ts +121 -0
- package/dist/repo/shared_libs/play-runtime/public-play-contract.ts +42 -0
- package/dist/repo/shared_libs/play-runtime/result-normalization.ts +33 -0
- package/dist/repo/shared_libs/play-runtime/runtime-actions.ts +208 -0
- package/dist/repo/shared_libs/play-runtime/runtime-api.ts +1873 -0
- package/dist/repo/shared_libs/play-runtime/runtime-constraints.ts +2 -0
- package/dist/repo/shared_libs/play-runtime/runtime-pg-driver-neon-serverless.ts +201 -0
- package/dist/repo/shared_libs/play-runtime/runtime-pg-driver-pg.ts +48 -0
- package/dist/repo/shared_libs/play-runtime/runtime-pg-driver.ts +84 -0
- package/dist/repo/shared_libs/play-runtime/scheduler-backend.ts +174 -0
- package/dist/repo/shared_libs/play-runtime/static-pipeline-types.ts +147 -0
- package/dist/repo/shared_libs/play-runtime/suspension.ts +68 -0
- package/dist/repo/shared_libs/play-runtime/tool-batch-executor.ts +146 -0
- package/dist/repo/shared_libs/play-runtime/tool-result.ts +387 -0
- package/dist/repo/shared_libs/play-runtime/tracing.ts +31 -0
- package/dist/repo/shared_libs/play-runtime/waterfall-replay.ts +75 -0
- package/dist/repo/shared_libs/play-runtime/worker-api-types.ts +140 -0
- package/dist/repo/shared_libs/plays/artifact-transport.ts +14 -0
- package/dist/repo/shared_libs/plays/artifact-types.ts +49 -0
- package/dist/repo/shared_libs/plays/bundling/index.ts +1346 -0
- package/dist/repo/shared_libs/plays/compiler-manifest.ts +186 -0
- package/dist/repo/shared_libs/plays/contracts.ts +51 -0
- package/dist/repo/shared_libs/plays/dataset.ts +308 -0
- package/dist/repo/shared_libs/plays/definition.ts +264 -0
- package/dist/repo/shared_libs/plays/file-refs.ts +11 -0
- package/dist/repo/shared_libs/plays/rate-limit-scheduler.ts +206 -0
- package/dist/repo/shared_libs/plays/resolve-static-pipeline.ts +164 -0
- package/dist/repo/shared_libs/plays/row-identity.ts +302 -0
- package/dist/repo/shared_libs/plays/runtime-validation.ts +415 -0
- package/dist/repo/shared_libs/plays/static-pipeline.ts +560 -0
- package/dist/repo/shared_libs/temporal/constants.ts +39 -0
- package/dist/repo/shared_libs/temporal/preview-config.ts +153 -0
- package/package.json +4 -4
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
EncryptedPostgresUrl,
|
|
3
|
+
PostgresUrlEncryptionRequest,
|
|
4
|
+
} from './db-session-crypto';
|
|
5
|
+
|
|
6
|
+
export const DB_SESSION_DEFAULT_TTL_SECONDS = 10 * 60;
|
|
7
|
+
export const DB_SESSION_MAX_TTL_SECONDS = 30 * 60;
|
|
8
|
+
|
|
9
|
+
export const DB_SESSION_OPERATIONS = [
|
|
10
|
+
'rows.read',
|
|
11
|
+
'rows.append',
|
|
12
|
+
'rows.upsert',
|
|
13
|
+
'rows.replace',
|
|
14
|
+
] as const;
|
|
15
|
+
export type DbSessionOperation = (typeof DB_SESSION_OPERATIONS)[number];
|
|
16
|
+
|
|
17
|
+
export const DB_LOGICAL_TABLES = [
|
|
18
|
+
'sheet_rows',
|
|
19
|
+
'dataset_rows',
|
|
20
|
+
'play_output',
|
|
21
|
+
] as const;
|
|
22
|
+
export type DbLogicalTable = (typeof DB_LOGICAL_TABLES)[number];
|
|
23
|
+
|
|
24
|
+
export type DbSessionTarget = {
|
|
25
|
+
orgId: string;
|
|
26
|
+
tableNamespace: string;
|
|
27
|
+
logicalTable: DbLogicalTable;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export type DbSessionLimits = {
|
|
31
|
+
maxRows?: number;
|
|
32
|
+
maxBytes?: number;
|
|
33
|
+
maxRequests?: number;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export type CreateDbSessionRequest = {
|
|
37
|
+
playName: string;
|
|
38
|
+
target: Omit<DbSessionTarget, 'orgId'>;
|
|
39
|
+
operations: DbSessionOperation[];
|
|
40
|
+
limits?: DbSessionLimits;
|
|
41
|
+
sheetContract?: unknown | null;
|
|
42
|
+
ttlSeconds?: number;
|
|
43
|
+
userEmail?: string | null;
|
|
44
|
+
postgresUrlEncryption?: PostgresUrlEncryptionRequest | null;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export type CreateDbSessionResponse = {
|
|
48
|
+
sessionId: string;
|
|
49
|
+
postgresUrl?: string;
|
|
50
|
+
encryptedPostgresUrl?: EncryptedPostgresUrl;
|
|
51
|
+
postgres?: {
|
|
52
|
+
schema: string;
|
|
53
|
+
sheetTable: string;
|
|
54
|
+
eventTable: string;
|
|
55
|
+
summaryTable: string;
|
|
56
|
+
columnSummaryTable: string;
|
|
57
|
+
};
|
|
58
|
+
expiresAt: string;
|
|
59
|
+
playName: string;
|
|
60
|
+
target: DbSessionTarget;
|
|
61
|
+
operations: DbSessionOperation[];
|
|
62
|
+
limits: DbSessionLimits;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export type PreloadedRuntimeDbSession = {
|
|
66
|
+
tableNamespace: string;
|
|
67
|
+
logicalTable: DbLogicalTable;
|
|
68
|
+
operations: DbSessionOperation[];
|
|
69
|
+
limits?: DbSessionLimits;
|
|
70
|
+
session: CreateDbSessionResponse;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export type RowsWriteDisposition = 'completed' | 'accepted';
|
|
74
|
+
|
|
75
|
+
export type RowsWriteResponse = {
|
|
76
|
+
disposition: RowsWriteDisposition;
|
|
77
|
+
writtenRows?: number;
|
|
78
|
+
jobId?: string;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
type SafeParseSuccess<T> = { success: true; data: T };
|
|
82
|
+
type SafeParseFailure = { success: false; error: Error };
|
|
83
|
+
type SafeParseResult<T> = SafeParseSuccess<T> | SafeParseFailure;
|
|
84
|
+
|
|
85
|
+
type RuntimeSchema<T> = {
|
|
86
|
+
parse(value: unknown): T;
|
|
87
|
+
safeParse(value: unknown): SafeParseResult<T>;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
function createSchema<T>(parse: (value: unknown) => T): RuntimeSchema<T> {
|
|
91
|
+
return {
|
|
92
|
+
parse,
|
|
93
|
+
safeParse(value) {
|
|
94
|
+
try {
|
|
95
|
+
return { success: true, data: parse(value) };
|
|
96
|
+
} catch (error) {
|
|
97
|
+
return {
|
|
98
|
+
success: false,
|
|
99
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
107
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function parseString(value: unknown, path: string): string {
|
|
111
|
+
if (typeof value !== 'string' || value.length === 0) {
|
|
112
|
+
throw new Error(`${path} must be a non-empty string.`);
|
|
113
|
+
}
|
|
114
|
+
return value;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function parseOptionalString(value: unknown, path: string): string | undefined {
|
|
118
|
+
if (value === undefined) return undefined;
|
|
119
|
+
return parseString(value, path);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function parseLiteral<T extends string>(
|
|
123
|
+
value: unknown,
|
|
124
|
+
expected: T,
|
|
125
|
+
path: string,
|
|
126
|
+
): T {
|
|
127
|
+
if (value !== expected) {
|
|
128
|
+
throw new Error(`${path} must be ${expected}.`);
|
|
129
|
+
}
|
|
130
|
+
return expected;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function parseNullableString(
|
|
134
|
+
value: unknown,
|
|
135
|
+
path: string,
|
|
136
|
+
): string | null | undefined {
|
|
137
|
+
if (value === undefined) return undefined;
|
|
138
|
+
if (value === null) return null;
|
|
139
|
+
return parseString(value, path);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function parsePositiveInteger(value: unknown, path: string): number {
|
|
143
|
+
if (
|
|
144
|
+
typeof value !== 'number' ||
|
|
145
|
+
!Number.isInteger(value) ||
|
|
146
|
+
value <= 0
|
|
147
|
+
) {
|
|
148
|
+
throw new Error(`${path} must be a positive integer.`);
|
|
149
|
+
}
|
|
150
|
+
return value;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function parseNonnegativeInteger(
|
|
154
|
+
value: unknown,
|
|
155
|
+
path: string,
|
|
156
|
+
): number {
|
|
157
|
+
if (
|
|
158
|
+
typeof value !== 'number' ||
|
|
159
|
+
!Number.isInteger(value) ||
|
|
160
|
+
value < 0
|
|
161
|
+
) {
|
|
162
|
+
throw new Error(`${path} must be a nonnegative integer.`);
|
|
163
|
+
}
|
|
164
|
+
return value;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function parseEnum<T extends readonly string[]>(
|
|
168
|
+
value: unknown,
|
|
169
|
+
allowed: T,
|
|
170
|
+
path: string,
|
|
171
|
+
): T[number] {
|
|
172
|
+
if (
|
|
173
|
+
typeof value === 'string' &&
|
|
174
|
+
(allowed as readonly string[]).includes(value)
|
|
175
|
+
) {
|
|
176
|
+
return value as T[number];
|
|
177
|
+
}
|
|
178
|
+
throw new Error(`${path} must be one of: ${allowed.join(', ')}.`);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function parseArray<T>(
|
|
182
|
+
value: unknown,
|
|
183
|
+
path: string,
|
|
184
|
+
parseItem: (item: unknown, itemPath: string) => T,
|
|
185
|
+
): T[] {
|
|
186
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
187
|
+
throw new Error(`${path} must be a non-empty array.`);
|
|
188
|
+
}
|
|
189
|
+
return value.map((item, index) => parseItem(item, `${path}[${index}]`));
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function parseLimits(value: unknown, path: string): DbSessionLimits {
|
|
193
|
+
if (!isRecord(value)) {
|
|
194
|
+
throw new Error(`${path} must be an object.`);
|
|
195
|
+
}
|
|
196
|
+
return {
|
|
197
|
+
...(value.maxRows !== undefined
|
|
198
|
+
? { maxRows: parsePositiveInteger(value.maxRows, `${path}.maxRows`) }
|
|
199
|
+
: {}),
|
|
200
|
+
...(value.maxBytes !== undefined
|
|
201
|
+
? { maxBytes: parsePositiveInteger(value.maxBytes, `${path}.maxBytes`) }
|
|
202
|
+
: {}),
|
|
203
|
+
...(value.maxRequests !== undefined
|
|
204
|
+
? {
|
|
205
|
+
maxRequests: parsePositiveInteger(
|
|
206
|
+
value.maxRequests,
|
|
207
|
+
`${path}.maxRequests`,
|
|
208
|
+
),
|
|
209
|
+
}
|
|
210
|
+
: {}),
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function parseOperation(value: unknown, path: string): DbSessionOperation {
|
|
215
|
+
return parseEnum(value, DB_SESSION_OPERATIONS, path);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function parseLogicalTable(value: unknown, path: string): DbLogicalTable {
|
|
219
|
+
return parseEnum(value, DB_LOGICAL_TABLES, path);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function parseTarget(value: unknown, path: string): DbSessionTarget {
|
|
223
|
+
if (!isRecord(value)) {
|
|
224
|
+
throw new Error(`${path} must be an object.`);
|
|
225
|
+
}
|
|
226
|
+
return {
|
|
227
|
+
orgId: parseString(value.orgId, `${path}.orgId`),
|
|
228
|
+
tableNamespace: parseString(value.tableNamespace, `${path}.tableNamespace`),
|
|
229
|
+
logicalTable: parseLogicalTable(value.logicalTable, `${path}.logicalTable`),
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function parseRequestTarget(
|
|
234
|
+
value: unknown,
|
|
235
|
+
path: string,
|
|
236
|
+
): Omit<DbSessionTarget, 'orgId'> {
|
|
237
|
+
if (!isRecord(value)) {
|
|
238
|
+
throw new Error(`${path} must be an object.`);
|
|
239
|
+
}
|
|
240
|
+
return {
|
|
241
|
+
tableNamespace: parseString(value.tableNamespace, `${path}.tableNamespace`),
|
|
242
|
+
logicalTable: parseLogicalTable(value.logicalTable, `${path}.logicalTable`),
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function parsePostgresMetadata(
|
|
247
|
+
value: unknown,
|
|
248
|
+
path: string,
|
|
249
|
+
): CreateDbSessionResponse['postgres'] {
|
|
250
|
+
if (value === undefined) return undefined;
|
|
251
|
+
if (!isRecord(value)) {
|
|
252
|
+
throw new Error(`${path} must be an object.`);
|
|
253
|
+
}
|
|
254
|
+
return {
|
|
255
|
+
schema: parseString(value.schema, `${path}.schema`),
|
|
256
|
+
sheetTable: parseString(value.sheetTable, `${path}.sheetTable`),
|
|
257
|
+
eventTable: parseString(value.eventTable, `${path}.eventTable`),
|
|
258
|
+
summaryTable: parseString(value.summaryTable, `${path}.summaryTable`),
|
|
259
|
+
columnSummaryTable: parseString(
|
|
260
|
+
value.columnSummaryTable,
|
|
261
|
+
`${path}.columnSummaryTable`,
|
|
262
|
+
),
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function parseEncryptedPostgresUrl(
|
|
267
|
+
value: unknown,
|
|
268
|
+
path: string,
|
|
269
|
+
): EncryptedPostgresUrl {
|
|
270
|
+
if (!isRecord(value)) {
|
|
271
|
+
throw new Error(`${path} must be an object.`);
|
|
272
|
+
}
|
|
273
|
+
const alg = parseString(value.alg, `${path}.alg`);
|
|
274
|
+
if (alg === 'A256GCM') {
|
|
275
|
+
return {
|
|
276
|
+
alg,
|
|
277
|
+
kid: parseLiteral(
|
|
278
|
+
value.kid,
|
|
279
|
+
'deepline-runtime-db-session-url:v1',
|
|
280
|
+
`${path}.kid`,
|
|
281
|
+
),
|
|
282
|
+
iv: parseString(value.iv, `${path}.iv`),
|
|
283
|
+
ciphertext: parseString(value.ciphertext, `${path}.ciphertext`),
|
|
284
|
+
tag: parseString(value.tag, `${path}.tag`),
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
if (alg === 'RSA-OAEP-256+A256GCM') {
|
|
288
|
+
return {
|
|
289
|
+
alg,
|
|
290
|
+
kid: parseLiteral(
|
|
291
|
+
value.kid,
|
|
292
|
+
'deepline-runtime-db-session-url:v2',
|
|
293
|
+
`${path}.kid`,
|
|
294
|
+
),
|
|
295
|
+
wrappedKey: parseString(value.wrappedKey, `${path}.wrappedKey`),
|
|
296
|
+
iv: parseString(value.iv, `${path}.iv`),
|
|
297
|
+
ciphertext: parseString(value.ciphertext, `${path}.ciphertext`),
|
|
298
|
+
tag: parseString(value.tag, `${path}.tag`),
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
throw new Error(`${path}.alg must be a supported encryption algorithm.`);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function parsePostgresUrlEncryptionRequest(
|
|
305
|
+
value: unknown,
|
|
306
|
+
path: string,
|
|
307
|
+
): PostgresUrlEncryptionRequest {
|
|
308
|
+
if (!isRecord(value)) {
|
|
309
|
+
throw new Error(`${path} must be an object.`);
|
|
310
|
+
}
|
|
311
|
+
const alg = parseLiteral(
|
|
312
|
+
value.alg,
|
|
313
|
+
'RSA-OAEP-256+A256GCM',
|
|
314
|
+
`${path}.alg`,
|
|
315
|
+
);
|
|
316
|
+
const publicKeyJwkValue = value.publicKeyJwk;
|
|
317
|
+
if (!isRecord(publicKeyJwkValue)) {
|
|
318
|
+
throw new Error(`${path}.publicKeyJwk must be an object.`);
|
|
319
|
+
}
|
|
320
|
+
const publicKeyJwk = { ...publicKeyJwkValue } as unknown as JsonWebKey;
|
|
321
|
+
if (publicKeyJwk.kty !== 'RSA') {
|
|
322
|
+
throw new Error(`${path}.publicKeyJwk.kty must be RSA.`);
|
|
323
|
+
}
|
|
324
|
+
if (
|
|
325
|
+
typeof publicKeyJwk.n !== 'string' ||
|
|
326
|
+
typeof publicKeyJwk.e !== 'string'
|
|
327
|
+
) {
|
|
328
|
+
throw new Error(`${path}.publicKeyJwk must include RSA n and e values.`);
|
|
329
|
+
}
|
|
330
|
+
return {
|
|
331
|
+
alg,
|
|
332
|
+
publicKeyJwk,
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
export const dbSessionOperationSchema = createSchema<DbSessionOperation>(
|
|
337
|
+
(value) => parseOperation(value, 'operation'),
|
|
338
|
+
);
|
|
339
|
+
|
|
340
|
+
export const dbLogicalTableSchema = createSchema<DbLogicalTable>(
|
|
341
|
+
(value) => parseLogicalTable(value, 'logicalTable'),
|
|
342
|
+
);
|
|
343
|
+
|
|
344
|
+
export const dbSessionTargetSchema = createSchema<DbSessionTarget>(
|
|
345
|
+
(value) => parseTarget(value, 'target'),
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
export const dbSessionLimitsSchema = createSchema<DbSessionLimits>(
|
|
349
|
+
(value) => parseLimits(value, 'limits'),
|
|
350
|
+
);
|
|
351
|
+
|
|
352
|
+
export const createDbSessionRequestSchema = createSchema<CreateDbSessionRequest>(
|
|
353
|
+
(value) => {
|
|
354
|
+
if (!isRecord(value)) {
|
|
355
|
+
throw new Error('Create DB session request must be an object.');
|
|
356
|
+
}
|
|
357
|
+
const ttlSeconds =
|
|
358
|
+
value.ttlSeconds === undefined
|
|
359
|
+
? undefined
|
|
360
|
+
: parsePositiveInteger(value.ttlSeconds, 'ttlSeconds');
|
|
361
|
+
if (
|
|
362
|
+
ttlSeconds !== undefined &&
|
|
363
|
+
ttlSeconds > DB_SESSION_MAX_TTL_SECONDS
|
|
364
|
+
) {
|
|
365
|
+
throw new Error(
|
|
366
|
+
`ttlSeconds must be <= ${DB_SESSION_MAX_TTL_SECONDS}.`,
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
return {
|
|
370
|
+
playName: parseString(value.playName, 'playName'),
|
|
371
|
+
target: parseRequestTarget(value.target, 'target'),
|
|
372
|
+
operations: parseArray(
|
|
373
|
+
value.operations,
|
|
374
|
+
'operations',
|
|
375
|
+
parseOperation,
|
|
376
|
+
),
|
|
377
|
+
...(value.limits !== undefined
|
|
378
|
+
? { limits: parseLimits(value.limits, 'limits') }
|
|
379
|
+
: {}),
|
|
380
|
+
...(value.sheetContract !== undefined
|
|
381
|
+
? { sheetContract: value.sheetContract }
|
|
382
|
+
: {}),
|
|
383
|
+
...(ttlSeconds !== undefined ? { ttlSeconds } : {}),
|
|
384
|
+
...(value.userEmail !== undefined
|
|
385
|
+
? { userEmail: parseNullableString(value.userEmail, 'userEmail') }
|
|
386
|
+
: {}),
|
|
387
|
+
...(value.postgresUrlEncryption !== undefined
|
|
388
|
+
? {
|
|
389
|
+
postgresUrlEncryption:
|
|
390
|
+
value.postgresUrlEncryption === null
|
|
391
|
+
? null
|
|
392
|
+
: parsePostgresUrlEncryptionRequest(
|
|
393
|
+
value.postgresUrlEncryption,
|
|
394
|
+
'postgresUrlEncryption',
|
|
395
|
+
),
|
|
396
|
+
}
|
|
397
|
+
: {}),
|
|
398
|
+
};
|
|
399
|
+
},
|
|
400
|
+
);
|
|
401
|
+
|
|
402
|
+
export const createDbSessionResponseSchema =
|
|
403
|
+
createSchema<CreateDbSessionResponse>((value) => {
|
|
404
|
+
if (!isRecord(value)) {
|
|
405
|
+
throw new Error('Create DB session response must be an object.');
|
|
406
|
+
}
|
|
407
|
+
return {
|
|
408
|
+
sessionId: parseString(value.sessionId, 'sessionId'),
|
|
409
|
+
...(value.postgresUrl !== undefined
|
|
410
|
+
? { postgresUrl: parseOptionalString(value.postgresUrl, 'postgresUrl') }
|
|
411
|
+
: {}),
|
|
412
|
+
...(value.encryptedPostgresUrl !== undefined
|
|
413
|
+
? {
|
|
414
|
+
encryptedPostgresUrl: parseEncryptedPostgresUrl(
|
|
415
|
+
value.encryptedPostgresUrl,
|
|
416
|
+
'encryptedPostgresUrl',
|
|
417
|
+
),
|
|
418
|
+
}
|
|
419
|
+
: {}),
|
|
420
|
+
...(value.postgres !== undefined
|
|
421
|
+
? { postgres: parsePostgresMetadata(value.postgres, 'postgres') }
|
|
422
|
+
: {}),
|
|
423
|
+
expiresAt: parseString(value.expiresAt, 'expiresAt'),
|
|
424
|
+
playName: parseString(value.playName, 'playName'),
|
|
425
|
+
target: parseTarget(value.target, 'target'),
|
|
426
|
+
operations: parseArray(
|
|
427
|
+
value.operations,
|
|
428
|
+
'operations',
|
|
429
|
+
parseOperation,
|
|
430
|
+
),
|
|
431
|
+
limits:
|
|
432
|
+
value.limits === undefined
|
|
433
|
+
? {}
|
|
434
|
+
: parseLimits(value.limits, 'limits'),
|
|
435
|
+
};
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
export const rowsWriteResponseSchema = createSchema<RowsWriteResponse>(
|
|
439
|
+
(value) => {
|
|
440
|
+
if (!isRecord(value)) {
|
|
441
|
+
throw new Error('Rows write response must be an object.');
|
|
442
|
+
}
|
|
443
|
+
return {
|
|
444
|
+
disposition: parseEnum(
|
|
445
|
+
value.disposition,
|
|
446
|
+
['completed', 'accepted'] as const,
|
|
447
|
+
'disposition',
|
|
448
|
+
),
|
|
449
|
+
...(value.writtenRows !== undefined
|
|
450
|
+
? {
|
|
451
|
+
writtenRows: parseNonnegativeInteger(
|
|
452
|
+
value.writtenRows,
|
|
453
|
+
'writtenRows',
|
|
454
|
+
),
|
|
455
|
+
}
|
|
456
|
+
: {}),
|
|
457
|
+
...(value.jobId !== undefined
|
|
458
|
+
? { jobId: parseOptionalString(value.jobId, 'jobId') }
|
|
459
|
+
: {}),
|
|
460
|
+
};
|
|
461
|
+
},
|
|
462
|
+
);
|
|
Binary file
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import {
|
|
2
|
+
defineBatchStrategyMap,
|
|
3
|
+
type AnyBatchOperationStrategy,
|
|
4
|
+
type BatchOperationStrategy,
|
|
5
|
+
} from './batching-types';
|
|
6
|
+
|
|
7
|
+
type TestRateLimitPayload = {
|
|
8
|
+
key: string;
|
|
9
|
+
stage?: string;
|
|
10
|
+
lead_id?: string;
|
|
11
|
+
row_number?: number;
|
|
12
|
+
simulated_delay_ms?: number;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
type TestRateLimitBatchPayload = {
|
|
16
|
+
key: string;
|
|
17
|
+
stage?: string;
|
|
18
|
+
simulated_delay_ms?: number;
|
|
19
|
+
items: Array<{
|
|
20
|
+
itemKey: string;
|
|
21
|
+
payload: TestRateLimitPayload;
|
|
22
|
+
}>;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
type TestRateLimitBatchResult = {
|
|
26
|
+
status: 'completed';
|
|
27
|
+
key: string;
|
|
28
|
+
stage?: string;
|
|
29
|
+
batch: true;
|
|
30
|
+
batch_size: number;
|
|
31
|
+
items: Array<{
|
|
32
|
+
itemKey: string;
|
|
33
|
+
result: {
|
|
34
|
+
status: 'completed';
|
|
35
|
+
key: string;
|
|
36
|
+
batch: false;
|
|
37
|
+
lead_id: string | null;
|
|
38
|
+
row_number: number | null;
|
|
39
|
+
};
|
|
40
|
+
}>;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const testRateLimitBatchStrategy: BatchOperationStrategy<
|
|
44
|
+
TestRateLimitPayload,
|
|
45
|
+
TestRateLimitBatchPayload,
|
|
46
|
+
TestRateLimitBatchResult
|
|
47
|
+
> = {
|
|
48
|
+
sourceOperation: 'test_rate_limit',
|
|
49
|
+
batchOperation: 'test_batch_rate_limit',
|
|
50
|
+
kind: 'identifier_batch',
|
|
51
|
+
maxBatchSize: 200,
|
|
52
|
+
canBatchWith(left, right) {
|
|
53
|
+
return String(left.stage || '') === String(right.stage || '');
|
|
54
|
+
},
|
|
55
|
+
toBucketKey(payload) {
|
|
56
|
+
return `test_batch_rate_limit:${String(payload.stage || '')}`;
|
|
57
|
+
},
|
|
58
|
+
toItemKey(payload) {
|
|
59
|
+
return String(payload.lead_id || payload.row_number || payload.key || '');
|
|
60
|
+
},
|
|
61
|
+
compile(payloads) {
|
|
62
|
+
const stage = String(payloads[0]?.stage || '');
|
|
63
|
+
const simulatedDelayMs =
|
|
64
|
+
typeof payloads[0]?.simulated_delay_ms === 'number'
|
|
65
|
+
? payloads[0].simulated_delay_ms
|
|
66
|
+
: undefined;
|
|
67
|
+
const items = payloads.map((payload, index) => ({
|
|
68
|
+
itemKey: String(payload.lead_id || payload.row_number || `row_${index}`),
|
|
69
|
+
payload,
|
|
70
|
+
}));
|
|
71
|
+
return {
|
|
72
|
+
batchOperation: 'test_batch_rate_limit',
|
|
73
|
+
batchPayload: {
|
|
74
|
+
key: 'batch',
|
|
75
|
+
...(stage ? { stage } : {}),
|
|
76
|
+
...(simulatedDelayMs !== undefined
|
|
77
|
+
? { simulated_delay_ms: simulatedDelayMs }
|
|
78
|
+
: {}),
|
|
79
|
+
items,
|
|
80
|
+
},
|
|
81
|
+
items,
|
|
82
|
+
};
|
|
83
|
+
},
|
|
84
|
+
splitResult(fullResult, compiled) {
|
|
85
|
+
const container =
|
|
86
|
+
fullResult && typeof fullResult === 'object'
|
|
87
|
+
? (fullResult as { data?: { items?: unknown[] }; items?: unknown[] })
|
|
88
|
+
: {};
|
|
89
|
+
const nestedItems =
|
|
90
|
+
container.data && typeof container.data === 'object'
|
|
91
|
+
? container.data.items
|
|
92
|
+
: undefined;
|
|
93
|
+
const resultItems = Array.isArray(container.items)
|
|
94
|
+
? (container.items as Array<{ itemKey?: string; result?: unknown }>)
|
|
95
|
+
: Array.isArray(nestedItems)
|
|
96
|
+
? (nestedItems as Array<{ itemKey?: string; result?: unknown }>)
|
|
97
|
+
: [];
|
|
98
|
+
return compiled.items.map((item, index) => ({
|
|
99
|
+
itemKey: item.itemKey,
|
|
100
|
+
result:
|
|
101
|
+
index < resultItems.length
|
|
102
|
+
? (resultItems[index]?.result ?? null)
|
|
103
|
+
: null,
|
|
104
|
+
rawResult:
|
|
105
|
+
index < resultItems.length
|
|
106
|
+
? (resultItems[index]?.result ?? null)
|
|
107
|
+
: null,
|
|
108
|
+
}));
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
export const DEFAULT_PLAY_RUNTIME_BATCH_STRATEGIES =
|
|
113
|
+
defineBatchStrategyMap({
|
|
114
|
+
test_rate_limit: testRateLimitBatchStrategy,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
export function getDefaultPlayRuntimeBatchStrategy(
|
|
118
|
+
operation: string | null | undefined,
|
|
119
|
+
): AnyBatchOperationStrategy | null {
|
|
120
|
+
if (!operation) {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
return DEFAULT_PLAY_RUNTIME_BATCH_STRATEGIES[operation] ?? null;
|
|
124
|
+
}
|