deepline 0.1.243 → 0.1.244
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/README.md +1 -1
- package/dist/bundling-sources/sdk/src/plays/bundle-play-file.ts +5 -25
- package/dist/bundling-sources/sdk/src/release.ts +23 -21
- package/dist/bundling-sources/sdk/src/types.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/backend.ts +2 -21
- package/dist/bundling-sources/shared_libs/play-runtime/governor/governor.ts +5 -6
- package/dist/bundling-sources/shared_libs/play-runtime/governor/policy.ts +6 -13
- package/dist/bundling-sources/shared_libs/play-runtime/governor/rate-state-backend.ts +9 -26
- package/dist/bundling-sources/shared_libs/play-runtime/internal-step-ids.ts +1 -2
- package/dist/bundling-sources/shared_libs/play-runtime/map-row-identity.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/output-size-limits.ts +191 -29
- package/dist/bundling-sources/shared_libs/play-runtime/play-latency-trace.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/providers.ts +1 -36
- package/dist/bundling-sources/shared_libs/play-runtime/run-ledger.ts +2 -3
- package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/index.ts +0 -7
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +1 -12
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-pg-driver-pg.ts +2 -3
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-pg-driver.ts +5 -15
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +6 -21
- package/dist/bundling-sources/shared_libs/play-runtime/tool-result.ts +3 -4
- package/dist/bundling-sources/shared_libs/plays/artifact-kind-guard.ts +16 -29
- package/dist/bundling-sources/shared_libs/plays/artifact-types.ts +4 -8
- package/dist/bundling-sources/shared_libs/plays/bundling/index.ts +21 -712
- package/dist/bundling-sources/shared_libs/plays/bundling/limits.ts +1 -18
- package/dist/cli/index.js +29 -71
- package/dist/cli/index.mjs +29 -71
- package/dist/{compiler-manifest-CYcwzSOJ.d.ts → compiler-manifest-DlE7dnRm.d.mts} +2 -8
- package/dist/{compiler-manifest-CYcwzSOJ.d.mts → compiler-manifest-DlE7dnRm.d.ts} +2 -8
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +24 -22
- package/dist/index.mjs +24 -22
- package/dist/plays/bundle-play-file.d.mts +7 -22
- package/dist/plays/bundle-play-file.d.ts +7 -22
- package/dist/plays/bundle-play-file.mjs +26 -614
- package/package.json +2 -2
- package/dist/bundling-sources/apps/play-runner-workers/src/coordinator-entry.ts +0 -4504
- package/dist/bundling-sources/apps/play-runner-workers/src/dedup-do.ts +0 -1386
- package/dist/bundling-sources/apps/play-runner-workers/src/durable-object-deploy-handoff.ts +0 -30
- package/dist/bundling-sources/apps/play-runner-workers/src/entry.ts +0 -8385
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/README.md +0 -21
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/batching.ts +0 -251
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/coordinator-progress.ts +0 -289
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/csv-rows.ts +0 -85
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/customer-console.ts +0 -23
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/dataset-handles.ts +0 -528
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/harness-receipt-store.ts +0 -229
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/ledger-event-batches.ts +0 -81
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/live-progress.ts +0 -22
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/map-chunk-plan.ts +0 -920
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/map-latency-profile.ts +0 -90
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/output-datasets.ts +0 -475
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/receipts.ts +0 -570
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/result-dataset-persistence.ts +0 -50
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/row-isolation.ts +0 -5
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/run-work-dispatcher.ts +0 -519
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/tool-batch.ts +0 -107
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/tool-dispatch.ts +0 -2541
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/tool-http-errors.ts +0 -7
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/tool-receipts.ts +0 -227
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/work-budget.ts +0 -130
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/worker-platform-budget.ts +0 -39
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/workflow-preview.ts +0 -26
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime-api-retry.ts +0 -23
- package/dist/bundling-sources/apps/play-runner-workers/src/workflow-instance-create.ts +0 -47
- package/dist/bundling-sources/apps/play-runner-workers/src/workflow-retry-state.ts +0 -167
- package/dist/bundling-sources/apps/play-runner-workers/src/workflow-retry.ts +0 -91
- package/dist/bundling-sources/sdk/src/plays/harness-stub.ts +0 -329
- package/dist/bundling-sources/sdk/src/worker-play-entry.ts +0 -240
- package/dist/bundling-sources/shared_libs/play-runtime/governor/coordinator-rate-state-backend.ts +0 -252
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-pg-driver-neon-serverless.ts +0 -217
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
type ToolTrace = {
|
|
2
|
-
phase: string;
|
|
3
|
-
ms?: number;
|
|
4
|
-
extra?: Record<string, unknown>;
|
|
5
|
-
};
|
|
6
|
-
|
|
7
|
-
type Totals = Record<string, number>;
|
|
8
|
-
|
|
9
|
-
function add(totals: Totals, key: string, ms: number | undefined): void {
|
|
10
|
-
if (typeof ms !== 'number' || !Number.isFinite(ms) || ms < 0) return;
|
|
11
|
-
totals[key] = (totals[key] ?? 0) + ms;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Opt-in map profiler. It aggregates in memory and produces one terminal
|
|
16
|
-
* trace, so normal runs do not pay per-row/per-tool telemetry cost.
|
|
17
|
-
*/
|
|
18
|
-
export class MapLatencyProfile {
|
|
19
|
-
private readonly phaseWorkMs: Totals = {};
|
|
20
|
-
private readonly toolWorkMs: Totals = {};
|
|
21
|
-
private readonly counts: Totals = {};
|
|
22
|
-
|
|
23
|
-
constructor(
|
|
24
|
-
readonly enabled: boolean,
|
|
25
|
-
private readonly mapName: string,
|
|
26
|
-
private readonly startedAtMs: number,
|
|
27
|
-
) {}
|
|
28
|
-
|
|
29
|
-
recordPhase(phase: string, ms: number, count = 1): void {
|
|
30
|
-
if (!this.enabled) return;
|
|
31
|
-
add(this.phaseWorkMs, phase, ms);
|
|
32
|
-
this.counts[`${phase}_count`] =
|
|
33
|
-
(this.counts[`${phase}_count`] ?? 0) + count;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
recordRowQueue(ms: number): void {
|
|
37
|
-
this.recordPhase('row_slot_queue', ms);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
recordRowExecution(ms: number): void {
|
|
41
|
-
this.recordPhase('row_execution', ms);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
recordToolTrace(trace: ToolTrace): void {
|
|
45
|
-
if (!this.enabled) return;
|
|
46
|
-
switch (trace.phase) {
|
|
47
|
-
case 'runner.tool.request':
|
|
48
|
-
add(this.toolWorkMs, 'request_end_to_end', trace.ms);
|
|
49
|
-
this.counts.tool_requests = (this.counts.tool_requests ?? 0) + 1;
|
|
50
|
-
break;
|
|
51
|
-
case 'runner.tool.drain':
|
|
52
|
-
add(this.toolWorkMs, 'drain', trace.ms);
|
|
53
|
-
this.counts.tool_drains = (this.counts.tool_drains ?? 0) + 1;
|
|
54
|
-
break;
|
|
55
|
-
case 'runner.tool.group':
|
|
56
|
-
add(this.toolWorkMs, 'group', trace.ms);
|
|
57
|
-
this.counts.tool_groups = (this.counts.tool_groups ?? 0) + 1;
|
|
58
|
-
break;
|
|
59
|
-
case 'runner.tool.receipt_prepare':
|
|
60
|
-
add(this.toolWorkMs, 'receipt_prepare', trace.ms);
|
|
61
|
-
break;
|
|
62
|
-
case 'runner.tool.admission':
|
|
63
|
-
add(this.toolWorkMs, 'admission_wait', trace.ms);
|
|
64
|
-
break;
|
|
65
|
-
case 'runner.tool.provider':
|
|
66
|
-
add(this.toolWorkMs, 'provider_execute', trace.ms);
|
|
67
|
-
break;
|
|
68
|
-
case 'runner.tool.receipt_complete':
|
|
69
|
-
add(this.toolWorkMs, 'receipt_complete', trace.ms);
|
|
70
|
-
break;
|
|
71
|
-
default:
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
summary(endedAtMs: number, input: { chunks: number; rows: number }) {
|
|
77
|
-
return {
|
|
78
|
-
mapName: this.mapName,
|
|
79
|
-
mapWallMs: Math.max(0, endedAtMs - this.startedAtMs),
|
|
80
|
-
chunks: input.chunks,
|
|
81
|
-
rows: input.rows,
|
|
82
|
-
// These are summed work durations. They can exceed mapWallMs when rows
|
|
83
|
-
// or provider calls overlap; the terminal trace must not mislabel them
|
|
84
|
-
// as a wall-clock decomposition.
|
|
85
|
-
phaseWorkMs: this.phaseWorkMs,
|
|
86
|
-
toolWorkMs: this.toolWorkMs,
|
|
87
|
-
counts: this.counts,
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
}
|
|
@@ -1,475 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
normalizePlayNameForSheet,
|
|
3
|
-
normalizeTableNamespace,
|
|
4
|
-
POSTGRES_IDENTIFIER_MAX_LENGTH,
|
|
5
|
-
sha256Hex,
|
|
6
|
-
} from '../../../../shared_libs/plays/row-identity';
|
|
7
|
-
import {
|
|
8
|
-
createDeferredPlayDataset,
|
|
9
|
-
isPlayDataset,
|
|
10
|
-
isSerializedPlayDataset,
|
|
11
|
-
type SerializedPlayDataset,
|
|
12
|
-
} from '../../../../shared_libs/plays/dataset';
|
|
13
|
-
|
|
14
|
-
export type OutputDatasetRow = Record<string, unknown>;
|
|
15
|
-
|
|
16
|
-
export type PromotedOutputDataset = {
|
|
17
|
-
path: string;
|
|
18
|
-
tableNamespace: string;
|
|
19
|
-
rows: OutputDatasetRow[];
|
|
20
|
-
handle: unknown;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
export type ProjectedResultDatasetHandle = {
|
|
24
|
-
path: string;
|
|
25
|
-
tableNamespace: string;
|
|
26
|
-
datasetKind: 'csv' | 'map' | null;
|
|
27
|
-
handle: unknown;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
type ProjectTerminalResultOptions = {
|
|
31
|
-
playName?: string | null;
|
|
32
|
-
reservedTableNamespaces?: Iterable<string>;
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
type DatasetFactory = (input: {
|
|
36
|
-
path: string;
|
|
37
|
-
tableNamespace: string;
|
|
38
|
-
rows: OutputDatasetRow[];
|
|
39
|
-
}) => unknown;
|
|
40
|
-
|
|
41
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
42
|
-
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function isDatasetLike(value: unknown): boolean {
|
|
46
|
-
if (!isRecord(value)) return false;
|
|
47
|
-
return (
|
|
48
|
-
typeof value.datasetId === 'string' &&
|
|
49
|
-
typeof value.count === 'function' &&
|
|
50
|
-
typeof value.peek === 'function' &&
|
|
51
|
-
typeof value.toJSON === 'function'
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function datasetMetadata(value: unknown): Record<string, unknown> | null {
|
|
56
|
-
if (!isDatasetLike(value)) return null;
|
|
57
|
-
try {
|
|
58
|
-
const metadata = (value as { toJSON(): unknown }).toJSON();
|
|
59
|
-
return isRecord(metadata) ? metadata : null;
|
|
60
|
-
} catch {
|
|
61
|
-
return null;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function datasetCountFromMetadata(
|
|
66
|
-
metadata: Record<string, unknown> | null,
|
|
67
|
-
): number {
|
|
68
|
-
return typeof metadata?.count === 'number' && Number.isFinite(metadata.count)
|
|
69
|
-
? Math.max(0, Math.floor(metadata.count))
|
|
70
|
-
: 0;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function datasetPreviewFromMetadata(
|
|
74
|
-
metadata: Record<string, unknown> | null,
|
|
75
|
-
): OutputDatasetRow[] {
|
|
76
|
-
return Array.isArray(metadata?.preview)
|
|
77
|
-
? metadata.preview.filter(isRecord).map((row) => ({ ...row }))
|
|
78
|
-
: [];
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function isRowArray(
|
|
82
|
-
value: unknown,
|
|
83
|
-
path: readonly string[],
|
|
84
|
-
): value is OutputDatasetRow[] {
|
|
85
|
-
if (!Array.isArray(value)) return false;
|
|
86
|
-
if (value.length === 0) return isLikelyRowOutputPath(path);
|
|
87
|
-
return value.every(isRecord);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const NON_ROW_ARRAY_OUTPUT_KEYS = new Set([
|
|
91
|
-
'actions',
|
|
92
|
-
'columns',
|
|
93
|
-
'errors',
|
|
94
|
-
'logs',
|
|
95
|
-
'messages',
|
|
96
|
-
'next',
|
|
97
|
-
'preview',
|
|
98
|
-
'render',
|
|
99
|
-
'steps',
|
|
100
|
-
'warnings',
|
|
101
|
-
]);
|
|
102
|
-
|
|
103
|
-
function isLikelyRowOutputPath(path: readonly string[]): boolean {
|
|
104
|
-
if (path.length === 0) return true;
|
|
105
|
-
let leaf = '';
|
|
106
|
-
try {
|
|
107
|
-
leaf = normalizeTableNamespace(path.at(-1) ?? '');
|
|
108
|
-
} catch {
|
|
109
|
-
return false;
|
|
110
|
-
}
|
|
111
|
-
if (NON_ROW_ARRAY_OUTPUT_KEYS.has(leaf)) return false;
|
|
112
|
-
return /(^|_)(rows?|results?|records?|items?|leads?|people|contacts?|companies|accounts?)(_|$)/.test(
|
|
113
|
-
leaf,
|
|
114
|
-
);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function tableNamespaceForPath(path: readonly string[]): string {
|
|
118
|
-
const raw = path.length > 0 ? path.join('_') : 'rows';
|
|
119
|
-
try {
|
|
120
|
-
return normalizeTableNamespace(raw);
|
|
121
|
-
} catch {
|
|
122
|
-
const hash = sha256Hex(raw).slice(0, 10);
|
|
123
|
-
const leaf = path.at(-1) ?? 'rows';
|
|
124
|
-
let prefix = 'rows';
|
|
125
|
-
try {
|
|
126
|
-
prefix = normalizeTableNamespace(leaf).slice(0, 52) || 'rows';
|
|
127
|
-
} catch {
|
|
128
|
-
prefix = 'rows';
|
|
129
|
-
}
|
|
130
|
-
return normalizeTableNamespace(`${prefix}_${hash}`);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const MIN_GENERATED_RESULT_NAMESPACE_LENGTH = 9;
|
|
135
|
-
|
|
136
|
-
function maxTableNamespaceLengthForPlay(playName: string | null | undefined) {
|
|
137
|
-
if (!playName) {
|
|
138
|
-
return POSTGRES_IDENTIFIER_MAX_LENGTH;
|
|
139
|
-
}
|
|
140
|
-
let playNameLength: number;
|
|
141
|
-
try {
|
|
142
|
-
playNameLength = normalizePlayNameForSheet(playName).length;
|
|
143
|
-
} catch {
|
|
144
|
-
throw new Error(
|
|
145
|
-
`Play name ${JSON.stringify(
|
|
146
|
-
playName,
|
|
147
|
-
)} cannot be used to create generated result dataset table names. ` +
|
|
148
|
-
'Shorten the play name or use only identifier-safe characters.',
|
|
149
|
-
);
|
|
150
|
-
}
|
|
151
|
-
const maxLength = POSTGRES_IDENTIFIER_MAX_LENGTH - playNameLength - 1;
|
|
152
|
-
if (maxLength < MIN_GENERATED_RESULT_NAMESPACE_LENGTH) {
|
|
153
|
-
throw new Error(
|
|
154
|
-
`Play name ${JSON.stringify(
|
|
155
|
-
playName,
|
|
156
|
-
)} leaves only ${maxLength} characters for generated result dataset table names. ` +
|
|
157
|
-
`Shorten the play name so result datasets have at least ${MIN_GENERATED_RESULT_NAMESPACE_LENGTH} characters of namespace budget.`,
|
|
158
|
-
);
|
|
159
|
-
}
|
|
160
|
-
return maxLength;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
function boundTableNamespace(
|
|
164
|
-
value: string,
|
|
165
|
-
hashInput: string,
|
|
166
|
-
maxLength: number,
|
|
167
|
-
): string {
|
|
168
|
-
const normalized = normalizeTableNamespace(value);
|
|
169
|
-
if (normalized.length <= maxLength) {
|
|
170
|
-
return normalized;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
const hash = sha256Hex(hashInput).slice(0, 10);
|
|
174
|
-
if (maxLength <= 12) {
|
|
175
|
-
return normalizeTableNamespace(`r${hash}`.slice(0, maxLength));
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const suffix = `_${hash}`;
|
|
179
|
-
const prefix = normalized.slice(0, maxLength - suffix.length) || 'r';
|
|
180
|
-
return normalizeTableNamespace(`${prefix}${suffix}`);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
function resultTableNamespaceForPath(
|
|
184
|
-
path: readonly string[],
|
|
185
|
-
maxLength: number,
|
|
186
|
-
): string {
|
|
187
|
-
const base = tableNamespaceForPath(path);
|
|
188
|
-
const raw = `result_${base}`;
|
|
189
|
-
const hashInput = path.join('.') || 'rows';
|
|
190
|
-
try {
|
|
191
|
-
return boundTableNamespace(raw, hashInput, maxLength);
|
|
192
|
-
} catch {
|
|
193
|
-
const hash = sha256Hex(path.join('.')).slice(0, 10);
|
|
194
|
-
return boundTableNamespace(
|
|
195
|
-
`result_${base.slice(0, 45)}_${hash}`,
|
|
196
|
-
hashInput,
|
|
197
|
-
maxLength,
|
|
198
|
-
);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
function seedUsedNamespaces(
|
|
203
|
-
namespaces: Iterable<string> | undefined,
|
|
204
|
-
): Set<string> {
|
|
205
|
-
const usedNamespaces = new Set<string>();
|
|
206
|
-
for (const namespace of namespaces ?? []) {
|
|
207
|
-
try {
|
|
208
|
-
usedNamespaces.add(normalizeTableNamespace(namespace));
|
|
209
|
-
} catch {
|
|
210
|
-
// Ignore malformed historical metadata; result datasets still get a
|
|
211
|
-
// result-prefixed namespace and local collision disambiguation.
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
return usedNamespaces;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
function disambiguateTableNamespace(
|
|
218
|
-
tableNamespace: string,
|
|
219
|
-
path: readonly string[],
|
|
220
|
-
usedNamespaces: Set<string>,
|
|
221
|
-
maxLength: number,
|
|
222
|
-
): string {
|
|
223
|
-
if (!usedNamespaces.has(tableNamespace)) {
|
|
224
|
-
usedNamespaces.add(tableNamespace);
|
|
225
|
-
return tableNamespace;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
const hash = sha256Hex(path.join('.')).slice(0, 10);
|
|
229
|
-
let candidate = boundTableNamespace(
|
|
230
|
-
`${tableNamespace}_${hash}`,
|
|
231
|
-
`${path.join('.')}:collision`,
|
|
232
|
-
maxLength,
|
|
233
|
-
);
|
|
234
|
-
let suffix = 2;
|
|
235
|
-
while (usedNamespaces.has(candidate)) {
|
|
236
|
-
candidate = boundTableNamespace(
|
|
237
|
-
`${tableNamespace}_${hash}_${suffix}`,
|
|
238
|
-
`${path.join('.')}:collision:${suffix}`,
|
|
239
|
-
maxLength,
|
|
240
|
-
);
|
|
241
|
-
suffix += 1;
|
|
242
|
-
}
|
|
243
|
-
usedNamespaces.add(candidate);
|
|
244
|
-
return candidate;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
function collectExistingDatasetTableNamespaces(value: unknown): Set<string> {
|
|
248
|
-
const namespaces = new Set<string>();
|
|
249
|
-
const seen = new WeakSet<object>();
|
|
250
|
-
|
|
251
|
-
const addNamespace = (namespace: unknown) => {
|
|
252
|
-
if (typeof namespace !== 'string' || !namespace) return;
|
|
253
|
-
try {
|
|
254
|
-
namespaces.add(normalizeTableNamespace(namespace));
|
|
255
|
-
} catch {
|
|
256
|
-
// Historical malformed namespaces should not block generated result
|
|
257
|
-
// names. The original handle/envelope remains unchanged.
|
|
258
|
-
}
|
|
259
|
-
};
|
|
260
|
-
|
|
261
|
-
const visit = (candidate: unknown, depth: number): void => {
|
|
262
|
-
if (depth > 20 || candidate == null || typeof candidate !== 'object') {
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
265
|
-
if (seen.has(candidate)) return;
|
|
266
|
-
seen.add(candidate);
|
|
267
|
-
if (isSerializedPlayDataset(candidate)) {
|
|
268
|
-
addNamespace(candidate.tableNamespace);
|
|
269
|
-
return;
|
|
270
|
-
}
|
|
271
|
-
if (isDatasetLike(candidate)) {
|
|
272
|
-
addNamespace(datasetMetadata(candidate)?.tableNamespace);
|
|
273
|
-
return;
|
|
274
|
-
}
|
|
275
|
-
if (Array.isArray(candidate)) {
|
|
276
|
-
for (const entry of candidate) visit(entry, depth + 1);
|
|
277
|
-
return;
|
|
278
|
-
}
|
|
279
|
-
for (const child of Object.values(candidate as Record<string, unknown>)) {
|
|
280
|
-
visit(child, depth + 1);
|
|
281
|
-
}
|
|
282
|
-
};
|
|
283
|
-
|
|
284
|
-
visit(value, 0);
|
|
285
|
-
return namespaces;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
export function projectTerminalResultDatasets(
|
|
289
|
-
value: unknown,
|
|
290
|
-
createDataset: DatasetFactory,
|
|
291
|
-
options: ProjectTerminalResultOptions = {},
|
|
292
|
-
): {
|
|
293
|
-
result: unknown;
|
|
294
|
-
datasets: PromotedOutputDataset[];
|
|
295
|
-
handles: ProjectedResultDatasetHandle[];
|
|
296
|
-
} {
|
|
297
|
-
const datasets: PromotedOutputDataset[] = [];
|
|
298
|
-
const handles: ProjectedResultDatasetHandle[] = [];
|
|
299
|
-
const seen = new WeakMap<object, unknown>();
|
|
300
|
-
const seenHandles = new WeakSet<object>();
|
|
301
|
-
const projectedHandles = new WeakMap<object, unknown>();
|
|
302
|
-
const usedNamespaces = seedUsedNamespaces(options.reservedTableNamespaces);
|
|
303
|
-
for (const namespace of collectExistingDatasetTableNamespaces(value)) {
|
|
304
|
-
usedNamespaces.add(namespace);
|
|
305
|
-
}
|
|
306
|
-
const maxTableNamespaceLength = maxTableNamespaceLengthForPlay(
|
|
307
|
-
options.playName,
|
|
308
|
-
);
|
|
309
|
-
|
|
310
|
-
const projectHandle = (handle: unknown, path: readonly string[]): unknown => {
|
|
311
|
-
if (!isRecord(handle)) return handle;
|
|
312
|
-
const existingProjection = projectedHandles.get(handle);
|
|
313
|
-
if (existingProjection) return existingProjection;
|
|
314
|
-
const metadata = datasetMetadata(handle);
|
|
315
|
-
let projectedHandle: unknown = handle;
|
|
316
|
-
let tableNamespace =
|
|
317
|
-
typeof metadata?.tableNamespace === 'string' && metadata.tableNamespace
|
|
318
|
-
? metadata.tableNamespace
|
|
319
|
-
: null;
|
|
320
|
-
if (!tableNamespace && isPlayDataset<OutputDatasetRow>(handle)) {
|
|
321
|
-
const resolvedPath = path.length > 0 ? path : ['rows'];
|
|
322
|
-
tableNamespace = disambiguateTableNamespace(
|
|
323
|
-
resultTableNamespaceForPath(resolvedPath, maxTableNamespaceLength),
|
|
324
|
-
resolvedPath,
|
|
325
|
-
usedNamespaces,
|
|
326
|
-
maxTableNamespaceLength,
|
|
327
|
-
);
|
|
328
|
-
projectedHandle = createDeferredPlayDataset({
|
|
329
|
-
datasetKind: 'csv',
|
|
330
|
-
datasetId:
|
|
331
|
-
typeof metadata?.datasetId === 'string'
|
|
332
|
-
? metadata.datasetId
|
|
333
|
-
: `csv:${tableNamespace}`,
|
|
334
|
-
count: datasetCountFromMetadata(metadata),
|
|
335
|
-
previewRows: datasetPreviewFromMetadata(metadata),
|
|
336
|
-
sourceLabel:
|
|
337
|
-
typeof metadata?.sourceLabel === 'string'
|
|
338
|
-
? metadata.sourceLabel
|
|
339
|
-
: null,
|
|
340
|
-
tableNamespace,
|
|
341
|
-
resolvers: {
|
|
342
|
-
count: () => handle.count(),
|
|
343
|
-
peek: (limit) => handle.peek(limit),
|
|
344
|
-
materialize: (limit) => handle.materialize(limit),
|
|
345
|
-
iterate: () =>
|
|
346
|
-
({
|
|
347
|
-
async *[Symbol.asyncIterator]() {
|
|
348
|
-
yield* handle;
|
|
349
|
-
},
|
|
350
|
-
}) as AsyncIterable<OutputDatasetRow>,
|
|
351
|
-
},
|
|
352
|
-
});
|
|
353
|
-
}
|
|
354
|
-
projectedHandles.set(handle, projectedHandle);
|
|
355
|
-
if (!tableNamespace || seenHandles.has(projectedHandle as object)) {
|
|
356
|
-
return projectedHandle;
|
|
357
|
-
}
|
|
358
|
-
try {
|
|
359
|
-
usedNamespaces.add(normalizeTableNamespace(tableNamespace));
|
|
360
|
-
} catch {
|
|
361
|
-
// Existing dataset handles can carry historical namespaces. Keep the
|
|
362
|
-
// handle, but do not let a malformed namespace poison new result names.
|
|
363
|
-
}
|
|
364
|
-
seenHandles.add(projectedHandle as object);
|
|
365
|
-
const projectedMetadata =
|
|
366
|
-
projectedHandle === handle ? metadata : datasetMetadata(projectedHandle);
|
|
367
|
-
const datasetKind =
|
|
368
|
-
projectedMetadata?.datasetKind === 'csv' ||
|
|
369
|
-
projectedMetadata?.datasetKind === 'map'
|
|
370
|
-
? projectedMetadata.datasetKind
|
|
371
|
-
: null;
|
|
372
|
-
handles.push({
|
|
373
|
-
path: path.length > 0 ? path.join('.') : 'rows',
|
|
374
|
-
tableNamespace,
|
|
375
|
-
datasetKind,
|
|
376
|
-
handle: projectedHandle,
|
|
377
|
-
});
|
|
378
|
-
return projectedHandle;
|
|
379
|
-
};
|
|
380
|
-
|
|
381
|
-
const promoteRows = (
|
|
382
|
-
rows: OutputDatasetRow[],
|
|
383
|
-
path: readonly string[],
|
|
384
|
-
): unknown => {
|
|
385
|
-
const resolvedPath = path.length > 0 ? path.join('.') : 'rows';
|
|
386
|
-
const tableNamespace = disambiguateTableNamespace(
|
|
387
|
-
resultTableNamespaceForPath(path, maxTableNamespaceLength),
|
|
388
|
-
path,
|
|
389
|
-
usedNamespaces,
|
|
390
|
-
maxTableNamespaceLength,
|
|
391
|
-
);
|
|
392
|
-
const handle = createDataset({
|
|
393
|
-
path: resolvedPath,
|
|
394
|
-
tableNamespace,
|
|
395
|
-
rows,
|
|
396
|
-
});
|
|
397
|
-
datasets.push({
|
|
398
|
-
path: resolvedPath,
|
|
399
|
-
tableNamespace,
|
|
400
|
-
rows,
|
|
401
|
-
handle,
|
|
402
|
-
});
|
|
403
|
-
projectHandle(handle, path);
|
|
404
|
-
return handle;
|
|
405
|
-
};
|
|
406
|
-
|
|
407
|
-
const visit = (
|
|
408
|
-
candidate: unknown,
|
|
409
|
-
path: string[],
|
|
410
|
-
depth: number,
|
|
411
|
-
): unknown => {
|
|
412
|
-
if (depth > 20 || candidate == null) return candidate;
|
|
413
|
-
if (isDatasetLike(candidate)) {
|
|
414
|
-
return projectHandle(candidate, path);
|
|
415
|
-
}
|
|
416
|
-
if (isSerializedPlayDataset(candidate)) {
|
|
417
|
-
return candidate;
|
|
418
|
-
}
|
|
419
|
-
if (isLikelyRowOutputPath(path) && isRowArray(candidate, path)) {
|
|
420
|
-
return promoteRows(candidate, path.length > 0 ? path : ['rows']);
|
|
421
|
-
}
|
|
422
|
-
if (Array.isArray(candidate)) {
|
|
423
|
-
return candidate.map((entry, index) =>
|
|
424
|
-
visit(entry, [...path, String(index)], depth + 1),
|
|
425
|
-
);
|
|
426
|
-
}
|
|
427
|
-
if (!isRecord(candidate)) return candidate;
|
|
428
|
-
const cached = seen.get(candidate);
|
|
429
|
-
if (cached) return cached;
|
|
430
|
-
const out: Record<string, unknown> = {};
|
|
431
|
-
seen.set(candidate, out);
|
|
432
|
-
for (const [key, child] of Object.entries(candidate)) {
|
|
433
|
-
out[key] = visit(child, [...path, key], depth + 1);
|
|
434
|
-
}
|
|
435
|
-
return out;
|
|
436
|
-
};
|
|
437
|
-
|
|
438
|
-
return {
|
|
439
|
-
result: visit(value, [], 0),
|
|
440
|
-
datasets,
|
|
441
|
-
handles,
|
|
442
|
-
};
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
type SerializedDatasetHydrator = (
|
|
446
|
-
dataset: SerializedPlayDataset<Record<string, unknown>>,
|
|
447
|
-
) => unknown;
|
|
448
|
-
|
|
449
|
-
export function hydrateSerializedResultDatasets(
|
|
450
|
-
value: unknown,
|
|
451
|
-
hydrateDataset: SerializedDatasetHydrator,
|
|
452
|
-
): unknown {
|
|
453
|
-
const seen = new WeakMap<object, unknown>();
|
|
454
|
-
|
|
455
|
-
function visit(candidate: unknown, depth: number): unknown {
|
|
456
|
-
if (depth > 20 || candidate == null) return candidate;
|
|
457
|
-
if (isSerializedPlayDataset<Record<string, unknown>>(candidate)) {
|
|
458
|
-
return hydrateDataset(candidate);
|
|
459
|
-
}
|
|
460
|
-
if (Array.isArray(candidate)) {
|
|
461
|
-
return candidate.map((entry) => visit(entry, depth + 1));
|
|
462
|
-
}
|
|
463
|
-
if (!isRecord(candidate)) return candidate;
|
|
464
|
-
const cached = seen.get(candidate);
|
|
465
|
-
if (cached) return cached;
|
|
466
|
-
const out: Record<string, unknown> = {};
|
|
467
|
-
seen.set(candidate, out);
|
|
468
|
-
for (const [key, child] of Object.entries(candidate)) {
|
|
469
|
-
out[key] = visit(child, depth + 1);
|
|
470
|
-
}
|
|
471
|
-
return out;
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
return visit(value, 0);
|
|
475
|
-
}
|