deepline 0.1.10 → 0.1.12
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 +4 -4
- package/dist/cli/index.js +509 -353
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +513 -358
- package/dist/cli/index.mjs.map +1 -1
- package/dist/index.d.mts +250 -305
- package/dist/index.d.ts +250 -305
- package/dist/index.js +174 -286
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +174 -285
- package/dist/index.mjs.map +1 -1
- package/dist/repo/apps/play-runner-workers/src/coordinator-entry.ts +23 -13
- package/dist/repo/apps/play-runner-workers/src/entry.ts +581 -1220
- package/dist/repo/sdk/src/cli/commands/play.ts +381 -247
- package/dist/repo/sdk/src/cli/commands/tools.ts +1 -1
- package/dist/repo/sdk/src/cli/dataset-stats.ts +86 -12
- package/dist/repo/sdk/src/client.ts +54 -51
- package/dist/repo/sdk/src/index.ts +7 -16
- package/dist/repo/sdk/src/play.ts +122 -135
- package/dist/repo/sdk/src/plays/bundle-play-file.ts +6 -3
- package/dist/repo/sdk/src/tool-output.ts +0 -111
- package/dist/repo/sdk/src/types.ts +2 -0
- package/dist/repo/sdk/src/version.ts +1 -1
- package/dist/repo/sdk/src/worker-play-entry.ts +3 -0
- package/dist/repo/shared_libs/play-runtime/context.ts +510 -267
- package/dist/repo/shared_libs/play-runtime/csv-rename.ts +180 -0
- package/dist/repo/shared_libs/play-runtime/ctx-types.ts +13 -1
- package/dist/repo/shared_libs/play-runtime/tool-result.ts +139 -114
- package/dist/repo/shared_libs/plays/bundling/index.ts +68 -5
- package/dist/repo/shared_libs/plays/compiler-manifest.ts +1 -1
- package/dist/repo/shared_libs/plays/dataset.ts +1 -1
- package/dist/repo/shared_libs/plays/runtime-validation.ts +8 -28
- package/package.json +1 -1
- package/dist/repo/apps/play-runner-workers/src/runtime/tool-result.ts +0 -184
|
@@ -99,6 +99,15 @@ export type PlayBundlingAdapter = {
|
|
|
99
99
|
warnAboutNonDevelopmentBundling?(filePath: string): void;
|
|
100
100
|
};
|
|
101
101
|
|
|
102
|
+
function assertValidExportName(exportName: string): void {
|
|
103
|
+
if (exportName === 'default') return;
|
|
104
|
+
if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(exportName)) {
|
|
105
|
+
throw new Error(
|
|
106
|
+
`Invalid play export name "${exportName}". Named prebuilt exports must be valid JavaScript identifiers.`,
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
102
111
|
export type BundledPlayFileSuccess = {
|
|
103
112
|
success: true;
|
|
104
113
|
artifact: PlayBundleArtifact;
|
|
@@ -409,6 +418,32 @@ function workersPlayEntryAliasPlugin(playFilePath: string): Plugin {
|
|
|
409
418
|
};
|
|
410
419
|
}
|
|
411
420
|
|
|
421
|
+
function workersNamedPlayEntryAliasPlugin(
|
|
422
|
+
playFilePath: string,
|
|
423
|
+
exportName: string,
|
|
424
|
+
): Plugin {
|
|
425
|
+
return {
|
|
426
|
+
name: 'deepline-workers-named-play-entry-alias',
|
|
427
|
+
setup(buildContext) {
|
|
428
|
+
buildContext.onResolve(
|
|
429
|
+
{ filter: new RegExp(`^${WORKERS_PLAY_ENTRY_VIRTUAL}$`) },
|
|
430
|
+
() => ({
|
|
431
|
+
path: `${playFilePath}.${exportName}.entry.ts`,
|
|
432
|
+
namespace: 'deepline-named-play-entry',
|
|
433
|
+
}),
|
|
434
|
+
);
|
|
435
|
+
buildContext.onLoad(
|
|
436
|
+
{ filter: /.*/, namespace: 'deepline-named-play-entry' },
|
|
437
|
+
() => ({
|
|
438
|
+
contents: `export { ${exportName} as default } from ${JSON.stringify(playFilePath)};\n`,
|
|
439
|
+
loader: 'ts',
|
|
440
|
+
resolveDir: dirname(playFilePath),
|
|
441
|
+
}),
|
|
442
|
+
);
|
|
443
|
+
},
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
|
|
412
447
|
/**
|
|
413
448
|
* Cloudflare Workers' `nodejs_compat` flag covers most node builtins (path,
|
|
414
449
|
* crypto, buffer, async_hooks, ...) but NOT `node:fs` / `node:fs/promises` /
|
|
@@ -1068,6 +1103,7 @@ export type BundlePlayFileOptions = {
|
|
|
1068
1103
|
* kind is cached independently on disk.
|
|
1069
1104
|
*/
|
|
1070
1105
|
target?: PlayArtifactKind;
|
|
1106
|
+
exportName?: string;
|
|
1071
1107
|
};
|
|
1072
1108
|
|
|
1073
1109
|
export type BundlePlayFileCoreOptions = BundlePlayFileOptions & {
|
|
@@ -1084,11 +1120,25 @@ async function runEsbuildForCjsNode(
|
|
|
1084
1120
|
entryFile: string,
|
|
1085
1121
|
importedPlayDependencies: ImportedPlayDependency[],
|
|
1086
1122
|
adapter: PlayBundlingAdapter,
|
|
1123
|
+
exportName: string,
|
|
1087
1124
|
): Promise<EsbuildBundleOutput | string[]> {
|
|
1088
1125
|
const sdkAliasPlugin = localSdkAliasPlugin(adapter);
|
|
1089
1126
|
const playProxyPlugin = importedPlayProxyPlugin(importedPlayDependencies);
|
|
1127
|
+
const namedExportShim =
|
|
1128
|
+
exportName === 'default'
|
|
1129
|
+
? null
|
|
1130
|
+
: `export { ${exportName} as default } from ${JSON.stringify(entryFile)};\n`;
|
|
1090
1131
|
const result = await build({
|
|
1091
|
-
|
|
1132
|
+
...(namedExportShim
|
|
1133
|
+
? {
|
|
1134
|
+
stdin: {
|
|
1135
|
+
contents: namedExportShim,
|
|
1136
|
+
resolveDir: dirname(entryFile),
|
|
1137
|
+
sourcefile: `${basename(entryFile)}.${exportName}.entry.ts`,
|
|
1138
|
+
loader: 'ts' as const,
|
|
1139
|
+
},
|
|
1140
|
+
}
|
|
1141
|
+
: { entryPoints: [entryFile] }),
|
|
1092
1142
|
absWorkingDir: adapter.projectRoot,
|
|
1093
1143
|
bundle: true,
|
|
1094
1144
|
format: 'cjs',
|
|
@@ -1121,10 +1171,14 @@ async function runEsbuildForEsmWorkers(
|
|
|
1121
1171
|
playEntryFile: string,
|
|
1122
1172
|
importedPlayDependencies: ImportedPlayDependency[],
|
|
1123
1173
|
adapter: PlayBundlingAdapter,
|
|
1174
|
+
exportName: string,
|
|
1124
1175
|
): Promise<EsbuildBundleOutput | string[]> {
|
|
1125
1176
|
const sdkAliasPlugin = localSdkAliasPlugin(adapter, { workersRuntime: true });
|
|
1126
1177
|
const playProxyPlugin = importedPlayProxyPlugin(importedPlayDependencies);
|
|
1127
|
-
const playEntryAlias =
|
|
1178
|
+
const playEntryAlias =
|
|
1179
|
+
exportName === 'default'
|
|
1180
|
+
? workersPlayEntryAliasPlugin(playEntryFile)
|
|
1181
|
+
: workersNamedPlayEntryAliasPlugin(playEntryFile, exportName);
|
|
1128
1182
|
const result = await build({
|
|
1129
1183
|
// Entry is the Workers harness; it imports the play via the virtual
|
|
1130
1184
|
// `deepline-play-entry` alias resolved by workersPlayEntryAliasPlugin.
|
|
@@ -1199,11 +1253,16 @@ export async function bundlePlayFile(
|
|
|
1199
1253
|
): Promise<BundledPlayFileResult> {
|
|
1200
1254
|
const adapter = options.adapter;
|
|
1201
1255
|
const target: PlayArtifactKind = options.target ?? PLAY_ARTIFACT_KINDS.cjsNode20;
|
|
1256
|
+
const exportName = options.exportName?.trim() || 'default';
|
|
1257
|
+
assertValidExportName(exportName);
|
|
1202
1258
|
const absolutePath = await normalizeLocalPath(filePath);
|
|
1203
1259
|
adapter.warnAboutNonDevelopmentBundling?.(absolutePath);
|
|
1204
1260
|
|
|
1205
1261
|
try {
|
|
1206
1262
|
const analysis = await analyzeSourceGraph(absolutePath, adapter);
|
|
1263
|
+
analysis.graphHash = sha256(
|
|
1264
|
+
`${analysis.graphHash}\nentry-export:${exportName}`,
|
|
1265
|
+
);
|
|
1207
1266
|
// For esm_workers builds, the harness source files (entry.ts +
|
|
1208
1267
|
// peer DO/coordinator types it imports) are bundled INTO every play
|
|
1209
1268
|
// artifact. So any harness edit must produce a different graphHash so
|
|
@@ -1275,8 +1334,8 @@ export async function bundlePlayFile(
|
|
|
1275
1334
|
}
|
|
1276
1335
|
const buildOutcome =
|
|
1277
1336
|
target === PLAY_ARTIFACT_KINDS.esmWorkers
|
|
1278
|
-
? await runEsbuildForEsmWorkers(absolutePath, analysis.importedPlayDependencies, adapter)
|
|
1279
|
-
: await runEsbuildForCjsNode(absolutePath, analysis.importedPlayDependencies, adapter);
|
|
1337
|
+
? await runEsbuildForEsmWorkers(absolutePath, analysis.importedPlayDependencies, adapter, exportName)
|
|
1338
|
+
: await runEsbuildForCjsNode(absolutePath, analysis.importedPlayDependencies, adapter, exportName);
|
|
1280
1339
|
if (Array.isArray(buildOutcome)) {
|
|
1281
1340
|
return {
|
|
1282
1341
|
success: false,
|
|
@@ -1287,7 +1346,11 @@ export async function bundlePlayFile(
|
|
|
1287
1346
|
const { bundledCode, sourceMapText, outputExtension } = buildOutcome;
|
|
1288
1347
|
|
|
1289
1348
|
const normalizedSourceMap = normalizeSourceMapForRuntime(sourceMapText);
|
|
1290
|
-
const
|
|
1349
|
+
const virtualBaseName =
|
|
1350
|
+
exportName === 'default'
|
|
1351
|
+
? basename(absolutePath).replace(/\.[^.]+$/, '')
|
|
1352
|
+
: `${basename(absolutePath).replace(/\.[^.]+$/, '')}.${exportName}`;
|
|
1353
|
+
const virtualFilename = `/virtual/deepline-plays/${analysis.graphHash}/${virtualBaseName}.${outputExtension}`;
|
|
1291
1354
|
const executableCode = `${bundledCode}\n//# sourceMappingURL=${basename(virtualFilename)}.map\n`;
|
|
1292
1355
|
const bundleSizeError = getBundleSizeError(
|
|
1293
1356
|
absolutePath,
|
|
@@ -255,7 +255,7 @@ export function createPlayDataset<T>(
|
|
|
255
255
|
metadata?.datasetId ??
|
|
256
256
|
`${metadata?.kind ?? 'map'}:${metadata?.tableNamespace ?? metadata?.sourceLabel ?? 'inline'}`,
|
|
257
257
|
count: materializedRows.length,
|
|
258
|
-
previewRows: materializedRows.slice(0,
|
|
258
|
+
previewRows: materializedRows.slice(0, 5),
|
|
259
259
|
sourceLabel: metadata?.sourceLabel ?? null,
|
|
260
260
|
tableNamespace: metadata?.tableNamespace ?? null,
|
|
261
261
|
resolvers: {
|
|
@@ -233,14 +233,14 @@ function extractValidatedMapTableNamespace(
|
|
|
233
233
|
const rowsArgument = node.arguments[1];
|
|
234
234
|
if (!keyArgument) {
|
|
235
235
|
errors.push(
|
|
236
|
-
'ctx.map() requires a string literal map key as the first argument, e.g. ctx.map("leads", rows
|
|
236
|
+
'ctx.map() requires a string literal map key as the first argument, e.g. ctx.map("leads", rows).step("company", row => row.domain).run({ key: "lead_id" }).',
|
|
237
237
|
);
|
|
238
238
|
return null;
|
|
239
239
|
}
|
|
240
240
|
|
|
241
241
|
if (!rowsArgument) {
|
|
242
242
|
errors.push(
|
|
243
|
-
'ctx.map() requires rows as the second argument, e.g. ctx.map("leads", rows
|
|
243
|
+
'ctx.map() requires rows as the second argument, e.g. ctx.map("leads", rows).step("company", row => row.domain).run({ key: "lead_id" }).',
|
|
244
244
|
);
|
|
245
245
|
return null;
|
|
246
246
|
}
|
|
@@ -264,7 +264,7 @@ function extractValidatedMapTableNamespace(
|
|
|
264
264
|
} catch (error) {
|
|
265
265
|
errors.push(
|
|
266
266
|
error instanceof Error
|
|
267
|
-
? `${error.message} Example: ctx.map("leads", rows
|
|
267
|
+
? `${error.message} Example: ctx.map("leads", rows).step("company", row => row.domain).run({ key: "lead_id", description: "..." }).`
|
|
268
268
|
: `ctx.map() key must normalize to <= ${MAP_KEY_NAMESPACE_MAX_LENGTH} characters.`,
|
|
269
269
|
);
|
|
270
270
|
return null;
|
|
@@ -272,7 +272,7 @@ function extractValidatedMapTableNamespace(
|
|
|
272
272
|
|
|
273
273
|
if (rowsArgument.type === 'ObjectExpression') {
|
|
274
274
|
errors.push(
|
|
275
|
-
'ctx.map() key must not be an object. Use ctx.map("leads", rows
|
|
275
|
+
'ctx.map() key must not be an object. Use ctx.map("leads", rows).step(...).run({ key: "lead_id" }).',
|
|
276
276
|
);
|
|
277
277
|
return null;
|
|
278
278
|
}
|
|
@@ -285,36 +285,16 @@ function extractValidatedMapTableNamespace(
|
|
|
285
285
|
}
|
|
286
286
|
|
|
287
287
|
const optionsArgument = node.arguments[2];
|
|
288
|
-
if (optionsArgument
|
|
289
|
-
errors.push(
|
|
288
|
+
if (optionsArgument) {
|
|
289
|
+
errors.push(
|
|
290
|
+
'ctx.map() accepts only a map key and rows. Add steps with .step(...) and pass row identity options to .run({ key: "lead_id" }).',
|
|
291
|
+
);
|
|
290
292
|
return null;
|
|
291
293
|
}
|
|
292
294
|
|
|
293
295
|
return keyArgument.value.trim();
|
|
294
296
|
}
|
|
295
297
|
|
|
296
|
-
function isMapDefinitionOptionsNode(node: acorn.Node): boolean {
|
|
297
|
-
if (node.type !== 'ObjectExpression') return false;
|
|
298
|
-
const properties = (
|
|
299
|
-
node as acorn.Node & {
|
|
300
|
-
properties: acorn.Node[];
|
|
301
|
-
}
|
|
302
|
-
).properties;
|
|
303
|
-
return properties.every((property) => {
|
|
304
|
-
if (property.type !== 'Property') return false;
|
|
305
|
-
const key = (property as acorn.Node & { key: acorn.Node }).key as
|
|
306
|
-
| (acorn.Node & { type: 'Identifier'; name: string })
|
|
307
|
-
| (acorn.Node & { type: 'Literal'; value: unknown });
|
|
308
|
-
const name =
|
|
309
|
-
key.type === 'Identifier'
|
|
310
|
-
? key.name
|
|
311
|
-
: key.type === 'Literal' && typeof key.value === 'string'
|
|
312
|
-
? key.value
|
|
313
|
-
: null;
|
|
314
|
-
return name === 'key' || name === 'staleAfterSeconds';
|
|
315
|
-
});
|
|
316
|
-
}
|
|
317
|
-
|
|
318
298
|
function isCtxMapCall(node: acorn.Node): node is acorn.CallExpression {
|
|
319
299
|
if (node.type !== 'CallExpression') {
|
|
320
300
|
return false;
|
package/package.json
CHANGED
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Per-play Worker tool result wrapper.
|
|
3
|
-
*
|
|
4
|
-
* User play code expects ergonomic getters like `result.getEmail()`. Keep this
|
|
5
|
-
* local and dependency-free so those getters do not pull the shared runtime
|
|
6
|
-
* module graph into every dynamic Worker.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
export type ToolResultMetadataInput = {
|
|
10
|
-
toolId: string;
|
|
11
|
-
resultIdentityGetters?: Record<string, readonly string[]>;
|
|
12
|
-
listExtractorPaths?: readonly string[];
|
|
13
|
-
listIdentityGetters?: Record<string, readonly string[]>;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
type ToolResultExecutionMetadata = {
|
|
17
|
-
idempotent: true;
|
|
18
|
-
cached: boolean;
|
|
19
|
-
source: 'live' | 'checkpoint' | 'cache';
|
|
20
|
-
cacheKey?: string;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
export type ToolExecuteResult<TResult = unknown> = {
|
|
24
|
-
status: string;
|
|
25
|
-
result: TResult;
|
|
26
|
-
_metadata: {
|
|
27
|
-
toolId: string;
|
|
28
|
-
execution: ToolResultExecutionMetadata;
|
|
29
|
-
targets: Record<string, { value: unknown; path: string }>;
|
|
30
|
-
lists: Record<string, { path: string; count: number | null; keys: Record<string, string> }>;
|
|
31
|
-
};
|
|
32
|
-
get<T = unknown>(target: string): T | null;
|
|
33
|
-
getEmail(): string | null;
|
|
34
|
-
getPhone(): string | null;
|
|
35
|
-
getLinkedin(): string | null;
|
|
36
|
-
list<T = Record<string, unknown>>(name?: string): T[] | null;
|
|
37
|
-
listPick<const TKeys extends readonly string[]>(
|
|
38
|
-
keys: TKeys,
|
|
39
|
-
name?: string,
|
|
40
|
-
): Array<Record<TKeys[number], unknown>> | null;
|
|
41
|
-
listKeys(name?: string): Record<string, string>;
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
type PathSegment = string | number;
|
|
45
|
-
|
|
46
|
-
export function createToolExecuteResult<TResult = unknown>(input: {
|
|
47
|
-
status: string;
|
|
48
|
-
result: TResult;
|
|
49
|
-
metadata: ToolResultMetadataInput;
|
|
50
|
-
execution: ToolResultExecutionMetadata;
|
|
51
|
-
}): ToolExecuteResult<TResult> {
|
|
52
|
-
const targets: ToolExecuteResult<TResult>['_metadata']['targets'] = {};
|
|
53
|
-
for (const [target, paths] of Object.entries(
|
|
54
|
-
input.metadata.resultIdentityGetters ?? {},
|
|
55
|
-
)) {
|
|
56
|
-
const path = paths
|
|
57
|
-
.map((rawPath) => String(rawPath || '').trim().replace(/^result\./, ''))
|
|
58
|
-
.find((candidate) => candidate && getAtPath(input.result, candidate) != null);
|
|
59
|
-
if (path) {
|
|
60
|
-
targets[target] = { value: getAtPath(input.result, path), path };
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
if (Object.keys(targets).length === 0 && isRecordLike(input.result)) {
|
|
64
|
-
for (const target of ['email', 'phone', 'linkedin', 'domain', 'status']) {
|
|
65
|
-
if (target in input.result && input.result[target] != null) {
|
|
66
|
-
targets[target] = { value: input.result[target], path: target };
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const lists: ToolExecuteResult<TResult>['_metadata']['lists'] = {};
|
|
72
|
-
for (const rawPath of input.metadata.listExtractorPaths ?? []) {
|
|
73
|
-
const path = String(rawPath || '').trim().replace(/^result\./, '');
|
|
74
|
-
const rows = normalizeRows(getAtPath(input.result, path));
|
|
75
|
-
if (!path || !rows) continue;
|
|
76
|
-
const name = path.split('.').filter(Boolean).at(-1)?.replace(/\[\d+\]$/, '') || path;
|
|
77
|
-
const keys = Object.fromEntries(
|
|
78
|
-
Object.keys(rows[0] ?? {}).map((key) => [key, key]),
|
|
79
|
-
);
|
|
80
|
-
lists[name] = { path, count: rows.length, keys };
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return {
|
|
84
|
-
status: input.status,
|
|
85
|
-
result: input.result,
|
|
86
|
-
_metadata: {
|
|
87
|
-
toolId: input.metadata.toolId,
|
|
88
|
-
execution: input.execution,
|
|
89
|
-
targets,
|
|
90
|
-
lists,
|
|
91
|
-
},
|
|
92
|
-
get<T = unknown>(target: string): T | null {
|
|
93
|
-
return (this._metadata.targets[target]?.value as T | undefined) ?? null;
|
|
94
|
-
},
|
|
95
|
-
getEmail(): string | null {
|
|
96
|
-
const value = this.get('email');
|
|
97
|
-
return typeof value === 'string' && value.trim() ? value : null;
|
|
98
|
-
},
|
|
99
|
-
getPhone(): string | null {
|
|
100
|
-
const value = this.get('phone');
|
|
101
|
-
return typeof value === 'string' && value.trim() ? value : null;
|
|
102
|
-
},
|
|
103
|
-
getLinkedin(): string | null {
|
|
104
|
-
const value = this.get('linkedin');
|
|
105
|
-
return typeof value === 'string' && value.trim() ? value : null;
|
|
106
|
-
},
|
|
107
|
-
list<T = Record<string, unknown>>(name?: string): T[] | null {
|
|
108
|
-
const entryName = name ?? Object.keys(this._metadata.lists)[0];
|
|
109
|
-
if (!entryName) return null;
|
|
110
|
-
const list = this._metadata.lists[entryName];
|
|
111
|
-
return list ? (normalizeRows(getAtPath(this.result, list.path)) as T[] | null) : null;
|
|
112
|
-
},
|
|
113
|
-
listPick<const TKeys extends readonly string[]>(
|
|
114
|
-
keys: TKeys,
|
|
115
|
-
name?: string,
|
|
116
|
-
): Array<Record<TKeys[number], unknown>> | null {
|
|
117
|
-
const entryName = name ?? Object.keys(this._metadata.lists)[0];
|
|
118
|
-
if (!entryName) return null;
|
|
119
|
-
const listMetadata = this._metadata.lists[entryName];
|
|
120
|
-
if (!listMetadata) return null;
|
|
121
|
-
const rows = normalizeRows(getAtPath(this.result, listMetadata.path));
|
|
122
|
-
if (!rows) return null;
|
|
123
|
-
return rows.map((row) => {
|
|
124
|
-
const picked: Record<string, unknown> = {};
|
|
125
|
-
for (const key of keys) {
|
|
126
|
-
const path = listMetadata.keys[key];
|
|
127
|
-
picked[key] = path ? getAtPath(row, path) ?? null : null;
|
|
128
|
-
}
|
|
129
|
-
return picked as Record<TKeys[number], unknown>;
|
|
130
|
-
});
|
|
131
|
-
},
|
|
132
|
-
listKeys(name?: string): Record<string, string> {
|
|
133
|
-
const entryName = name ?? Object.keys(this._metadata.lists)[0];
|
|
134
|
-
return entryName ? (this._metadata.lists[entryName]?.keys ?? {}) : {};
|
|
135
|
-
},
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
export function isToolExecuteResult(value: unknown): value is ToolExecuteResult {
|
|
140
|
-
return (
|
|
141
|
-
isRecordLike(value) &&
|
|
142
|
-
typeof value.status === 'string' &&
|
|
143
|
-
isRecordLike(value._metadata) &&
|
|
144
|
-
'result' in value
|
|
145
|
-
);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
function parseToolPath(path: string): PathSegment[] {
|
|
149
|
-
const segments: PathSegment[] = [];
|
|
150
|
-
for (const rawPart of path.split('.').filter(Boolean)) {
|
|
151
|
-
const bracketPattern = /([^\[\]]+)|\[(\d+)\]/g;
|
|
152
|
-
let matched = false;
|
|
153
|
-
for (const match of rawPart.matchAll(bracketPattern)) {
|
|
154
|
-
matched = true;
|
|
155
|
-
if (match[1]) segments.push(match[1]);
|
|
156
|
-
else if (match[2]) segments.push(Number(match[2]));
|
|
157
|
-
}
|
|
158
|
-
if (!matched) segments.push(rawPart);
|
|
159
|
-
}
|
|
160
|
-
return segments;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
function getAtPath(root: unknown, path: string): unknown {
|
|
164
|
-
let current = root;
|
|
165
|
-
for (const segment of parseToolPath(path)) {
|
|
166
|
-
if (typeof segment === 'number') {
|
|
167
|
-
if (!Array.isArray(current)) return undefined;
|
|
168
|
-
current = current[segment];
|
|
169
|
-
} else {
|
|
170
|
-
if (!isRecordLike(current)) return undefined;
|
|
171
|
-
current = current[segment];
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
return current;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
function normalizeRows(value: unknown): Record<string, unknown>[] | null {
|
|
178
|
-
if (!Array.isArray(value)) return null;
|
|
179
|
-
return value.map((entry) => (isRecordLike(entry) ? entry : { value: entry }));
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
function isRecordLike(value: unknown): value is Record<string, unknown> {
|
|
183
|
-
return value != null && typeof value === 'object' && !Array.isArray(value);
|
|
184
|
-
}
|