@tiangong-lca/cli 0.0.9 → 0.0.11
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 +3 -0
- package/dist/src/cli.js +112 -1
- package/dist/src/cli.js.map +1 -1
- package/dist/src/lib/dataset-curation-queue.js +411 -0
- package/dist/src/lib/dataset-curation-queue.js.map +1 -0
- package/dist/src/lib/process-required-fields.js +14 -1
- package/dist/src/lib/process-required-fields.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { CliError } from './errors.js';
|
|
5
|
+
import { firstNonEmpty, isRecord, materializeDatasetRows, } from './dataset-local.js';
|
|
6
|
+
const DEFAULT_VERSION = 'unversioned';
|
|
7
|
+
export async function runDatasetCurationQueueBuild(options) {
|
|
8
|
+
const outDir = requirePath(options.outDir, '--out-dir');
|
|
9
|
+
const processesPath = requireExistingPath(options.processesPath, '--processes');
|
|
10
|
+
const flowsPath = options.flowsPath ? requireExistingPath(options.flowsPath, '--flows') : null;
|
|
11
|
+
const supportPaths = (options.supportPaths ?? []).map((inputPath) => requireExistingPath(inputPath, '--support'));
|
|
12
|
+
const externalFlowRefPaths = (options.externalFlowRefPaths ?? []).map((inputPath) => requireExistingPath(inputPath, '--external-flow-ref'));
|
|
13
|
+
const processLimit = normalizeProcessLimit(options.processLimit);
|
|
14
|
+
const excludedProcessIds = new Set((options.excludeProcessIds ?? []).map((id) => id.trim()));
|
|
15
|
+
const supportRows = supportPaths.flatMap((inputPath) => readQueueRows(inputPath, 'support'));
|
|
16
|
+
const flowRows = flowsPath ? readQueueRows(flowsPath, 'flow') : [];
|
|
17
|
+
const processRows = readQueueRows(processesPath, 'process')
|
|
18
|
+
.filter((row) => !excludedProcessIds.has(row.id))
|
|
19
|
+
.slice(0, processLimit ?? undefined);
|
|
20
|
+
const externalFlowRefs = externalFlowRefPaths.flatMap((inputPath) => readExternalFlowRefs(inputPath));
|
|
21
|
+
mkdirSync(outDir, { recursive: true });
|
|
22
|
+
const localFlowTasks = new Map();
|
|
23
|
+
const flowRowsById = new Map();
|
|
24
|
+
for (const row of flowRows) {
|
|
25
|
+
const taskId = taskIdFor(row.entityType, row.id, row.version);
|
|
26
|
+
localFlowTasks.set(row.id, taskId);
|
|
27
|
+
flowRowsById.set(row.id, row);
|
|
28
|
+
}
|
|
29
|
+
const externalFlowIds = new Set(externalFlowRefs.map((ref) => ref.id));
|
|
30
|
+
const blockers = [];
|
|
31
|
+
const supportTasks = supportRows.map((row) => buildTask(outDir, row, []));
|
|
32
|
+
const flowTasks = flowRows.map((row) => buildTask(outDir, row, []));
|
|
33
|
+
const processTasks = processRows.map((row) => {
|
|
34
|
+
const refs = extractProcessFlowRefs(row.payload);
|
|
35
|
+
const dependsOn = new Set();
|
|
36
|
+
const missingRefs = [];
|
|
37
|
+
for (const ref of refs) {
|
|
38
|
+
const taskId = localFlowTasks.get(ref.id);
|
|
39
|
+
if (taskId) {
|
|
40
|
+
dependsOn.add(taskId);
|
|
41
|
+
}
|
|
42
|
+
else if (!externalFlowIds.has(ref.id)) {
|
|
43
|
+
missingRefs.push(ref);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (missingRefs.length > 0) {
|
|
47
|
+
blockers.push({
|
|
48
|
+
schema_version: 1,
|
|
49
|
+
code: 'process_flow_reference_unresolved',
|
|
50
|
+
severity: 'blocker',
|
|
51
|
+
entity_type: 'process',
|
|
52
|
+
entity_id: row.id,
|
|
53
|
+
version: row.version,
|
|
54
|
+
message: 'Process references flows that are neither present in local flow rows nor declared as external flow refs.',
|
|
55
|
+
details: { missing_flow_refs: missingRefs },
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
return buildTask(outDir, row, [...dependsOn].sort());
|
|
59
|
+
});
|
|
60
|
+
const tasks = [...supportTasks, ...flowTasks, ...processTasks];
|
|
61
|
+
const taskRows = [
|
|
62
|
+
...supportRows.map((row, index) => ({ row, task: supportTasks[index] })),
|
|
63
|
+
...flowRows.map((row, index) => ({ row, task: flowTasks[index] })),
|
|
64
|
+
...processRows.map((row, index) => ({ row, task: processTasks[index] })),
|
|
65
|
+
];
|
|
66
|
+
for (const { row, task } of taskRows) {
|
|
67
|
+
writeEntityArtifacts({
|
|
68
|
+
row,
|
|
69
|
+
task,
|
|
70
|
+
flowRefs: row.entityType === 'process' ? extractProcessFlowRefs(row.payload) : [],
|
|
71
|
+
externalFlowIds,
|
|
72
|
+
flowRowsById,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
const outputsDir = path.join(outDir, 'outputs');
|
|
76
|
+
mkdirSync(outputsDir, { recursive: true });
|
|
77
|
+
const manifestPath = path.join(outputsDir, 'curation-queue-manifest.json');
|
|
78
|
+
const tasksPath = path.join(outputsDir, 'curation-queue-tasks.jsonl');
|
|
79
|
+
const locksPath = path.join(outputsDir, 'curation-queue-locks.json');
|
|
80
|
+
const blockersPath = path.join(outputsDir, 'curation-queue-blockers.jsonl');
|
|
81
|
+
const inputHashes = buildInputHashes([
|
|
82
|
+
processesPath,
|
|
83
|
+
...(flowsPath ? [flowsPath] : []),
|
|
84
|
+
...supportPaths,
|
|
85
|
+
...externalFlowRefPaths,
|
|
86
|
+
]);
|
|
87
|
+
const locks = {
|
|
88
|
+
schema_version: 1,
|
|
89
|
+
generated_at_utc: new Date().toISOString(),
|
|
90
|
+
locks: tasks.map((task) => ({
|
|
91
|
+
lock_key: task.lock_key,
|
|
92
|
+
task_id: task.task_id,
|
|
93
|
+
entity_type: task.entity_type,
|
|
94
|
+
entity_id: task.entity_id,
|
|
95
|
+
version: task.version,
|
|
96
|
+
status: 'available',
|
|
97
|
+
})),
|
|
98
|
+
};
|
|
99
|
+
const report = {
|
|
100
|
+
schema_version: 1,
|
|
101
|
+
generated_at_utc: new Date().toISOString(),
|
|
102
|
+
status: blockers.length > 0 ? 'blocked' : 'ready',
|
|
103
|
+
out_dir: path.resolve(outDir),
|
|
104
|
+
inputs: {
|
|
105
|
+
processes: path.resolve(processesPath),
|
|
106
|
+
flows: flowsPath ? path.resolve(flowsPath) : null,
|
|
107
|
+
support: supportPaths.map((inputPath) => path.resolve(inputPath)),
|
|
108
|
+
external_flow_refs: externalFlowRefPaths.map((inputPath) => path.resolve(inputPath)),
|
|
109
|
+
},
|
|
110
|
+
counts: {
|
|
111
|
+
support_rows: supportRows.length,
|
|
112
|
+
flow_rows: flowRows.length,
|
|
113
|
+
process_rows: processRows.length,
|
|
114
|
+
external_flow_refs: externalFlowRefs.length,
|
|
115
|
+
tasks: tasks.length,
|
|
116
|
+
blockers: blockers.length,
|
|
117
|
+
},
|
|
118
|
+
hashes: {
|
|
119
|
+
inputs: inputHashes,
|
|
120
|
+
task_order: sha256(tasks.map((task) => task.task_id).join('\n')),
|
|
121
|
+
},
|
|
122
|
+
files: {
|
|
123
|
+
manifest: manifestPath,
|
|
124
|
+
tasks: tasksPath,
|
|
125
|
+
locks: locksPath,
|
|
126
|
+
blockers: blockersPath,
|
|
127
|
+
},
|
|
128
|
+
tasks,
|
|
129
|
+
blockers,
|
|
130
|
+
};
|
|
131
|
+
writeJson(manifestPath, report);
|
|
132
|
+
writeText(tasksPath, jsonLines(tasks));
|
|
133
|
+
writeJson(locksPath, locks);
|
|
134
|
+
writeText(blockersPath, jsonLines(blockers));
|
|
135
|
+
return report;
|
|
136
|
+
}
|
|
137
|
+
function readQueueRows(inputPath, entityType) {
|
|
138
|
+
const rows = materializeDatasetRows(inputPath);
|
|
139
|
+
return rows.map((row) => queueRowFromDatasetRow(inputPath, entityType, row));
|
|
140
|
+
}
|
|
141
|
+
function queueRowFromDatasetRow(inputPath, entityType, row) {
|
|
142
|
+
const tidasIdentity = genericTidasDatasetIdentity(row.payload);
|
|
143
|
+
const id = firstNonEmpty(row.id, row.row.id, row.row.dataset_id, row.row.uuid, tidasIdentity.id);
|
|
144
|
+
if (!id) {
|
|
145
|
+
throw new CliError(`${entityType} row is missing a stable id in ${inputPath} at index ${row.index}.`, {
|
|
146
|
+
code: 'CURATION_QUEUE_ROW_ID_MISSING',
|
|
147
|
+
exitCode: 2,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
entityType,
|
|
152
|
+
sourcePath: inputPath,
|
|
153
|
+
sourceIndex: row.index,
|
|
154
|
+
row: row.row,
|
|
155
|
+
payload: row.payload,
|
|
156
|
+
id,
|
|
157
|
+
version: firstNonEmpty(row.version, row.row.version, tidasIdentity.version) ?? DEFAULT_VERSION,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function genericTidasDatasetIdentity(payload) {
|
|
161
|
+
const root = genericTidasDatasetRoot(payload);
|
|
162
|
+
const information = Object.values(root).find((value) => isRecord(value) && isRecord(value.dataSetInformation));
|
|
163
|
+
const dataSetInformation = isRecord(information) && isRecord(information.dataSetInformation)
|
|
164
|
+
? information.dataSetInformation
|
|
165
|
+
: {};
|
|
166
|
+
const administrativeInformation = isRecord(root.administrativeInformation)
|
|
167
|
+
? root.administrativeInformation
|
|
168
|
+
: {};
|
|
169
|
+
const publicationAndOwnership = isRecord(administrativeInformation.publicationAndOwnership)
|
|
170
|
+
? administrativeInformation.publicationAndOwnership
|
|
171
|
+
: {};
|
|
172
|
+
return {
|
|
173
|
+
id: firstNonEmpty(dataSetInformation['common:UUID']),
|
|
174
|
+
version: firstNonEmpty(publicationAndOwnership['common:dataSetVersion']),
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
function genericTidasDatasetRoot(payload) {
|
|
178
|
+
const datasetRoots = [];
|
|
179
|
+
for (const [key, value] of Object.entries(payload)) {
|
|
180
|
+
if (key.endsWith('DataSet') && isRecord(value)) {
|
|
181
|
+
datasetRoots.push(value);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return datasetRoots.length === 1 ? datasetRoots[0] : payload;
|
|
185
|
+
}
|
|
186
|
+
function buildTask(outDir, row, dependsOn) {
|
|
187
|
+
const taskId = taskIdFor(row.entityType, row.id, row.version);
|
|
188
|
+
const workDir = path.join(outDir, 'entities', entityDirPlural(row.entityType), entityDirName(row.id, row.version));
|
|
189
|
+
return {
|
|
190
|
+
schema_version: 1,
|
|
191
|
+
entity_type: row.entityType,
|
|
192
|
+
task_id: taskId,
|
|
193
|
+
entity_id: row.id,
|
|
194
|
+
version: row.version,
|
|
195
|
+
lock_key: taskId,
|
|
196
|
+
depends_on: dependsOn,
|
|
197
|
+
input_rows_file: path.join(workDir, 'input.jsonl'),
|
|
198
|
+
work_dir: workDir,
|
|
199
|
+
checkpoint_file: path.join(workDir, 'checkpoint.json'),
|
|
200
|
+
run_plan_file: path.join(workDir, 'entity-run-plan.json'),
|
|
201
|
+
closure_file: path.join(workDir, 'closure.json'),
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
function writeEntityArtifacts(options) {
|
|
205
|
+
mkdirSync(path.join(options.task.work_dir, 'checkpoints'), { recursive: true });
|
|
206
|
+
writeText(options.task.input_rows_file, jsonLines([options.row.row]));
|
|
207
|
+
writeJson(options.task.closure_file, {
|
|
208
|
+
schema_version: 1,
|
|
209
|
+
entity_type: options.row.entityType,
|
|
210
|
+
entity_id: options.row.id,
|
|
211
|
+
version: options.row.version,
|
|
212
|
+
source: {
|
|
213
|
+
file: path.resolve(options.row.sourcePath),
|
|
214
|
+
index: options.row.sourceIndex,
|
|
215
|
+
},
|
|
216
|
+
dependencies: buildDependencyClosure(options),
|
|
217
|
+
});
|
|
218
|
+
writeJson(options.task.run_plan_file, buildRunPlan(options.task));
|
|
219
|
+
}
|
|
220
|
+
function buildDependencyClosure(options) {
|
|
221
|
+
if (options.row.entityType !== 'process') {
|
|
222
|
+
return {
|
|
223
|
+
local_tasks: [],
|
|
224
|
+
external_refs: [],
|
|
225
|
+
unresolved_refs: [],
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
return {
|
|
229
|
+
local_tasks: options.flowRefs
|
|
230
|
+
.map((ref) => ({ ref, row: options.flowRowsById.get(ref.id) }))
|
|
231
|
+
.filter((item) => Boolean(item.row))
|
|
232
|
+
.map(({ ref, row }) => ({
|
|
233
|
+
entity_type: 'flow',
|
|
234
|
+
entity_id: ref.id,
|
|
235
|
+
version: row.version,
|
|
236
|
+
task_id: taskIdFor('flow', ref.id, row.version),
|
|
237
|
+
ref_path: ref.path,
|
|
238
|
+
})),
|
|
239
|
+
external_refs: options.flowRefs
|
|
240
|
+
.filter((ref) => !options.flowRowsById.has(ref.id) && options.externalFlowIds.has(ref.id))
|
|
241
|
+
.map((ref) => ({
|
|
242
|
+
entity_type: 'flow',
|
|
243
|
+
entity_id: ref.id,
|
|
244
|
+
version: ref.version,
|
|
245
|
+
ref_path: ref.path,
|
|
246
|
+
})),
|
|
247
|
+
unresolved_refs: options.flowRefs
|
|
248
|
+
.filter((ref) => !options.flowRowsById.has(ref.id) && !options.externalFlowIds.has(ref.id))
|
|
249
|
+
.map((ref) => ({
|
|
250
|
+
entity_type: 'flow',
|
|
251
|
+
entity_id: ref.id,
|
|
252
|
+
version: ref.version,
|
|
253
|
+
ref_path: ref.path,
|
|
254
|
+
})),
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
function buildRunPlan(task) {
|
|
258
|
+
const stagesByType = {
|
|
259
|
+
support: ['identity', 'schema', 'qa_or_profile', 'checkpoint'],
|
|
260
|
+
flow: ['identity', 'name_plan', 'schema', 'qa', 'checkpoint'],
|
|
261
|
+
process: [
|
|
262
|
+
'dependency_closure',
|
|
263
|
+
'reference_refresh',
|
|
264
|
+
'required_fields',
|
|
265
|
+
'schema',
|
|
266
|
+
'qa',
|
|
267
|
+
'curation',
|
|
268
|
+
'remote_dry_run',
|
|
269
|
+
'readback',
|
|
270
|
+
],
|
|
271
|
+
};
|
|
272
|
+
return {
|
|
273
|
+
schema_version: 1,
|
|
274
|
+
task_id: task.task_id,
|
|
275
|
+
entity_type: task.entity_type,
|
|
276
|
+
entity_id: task.entity_id,
|
|
277
|
+
version: task.version,
|
|
278
|
+
input_rows_file: task.input_rows_file,
|
|
279
|
+
checkpoint_file: task.checkpoint_file,
|
|
280
|
+
stages: stagesByType[task.entity_type].map((stage) => ({
|
|
281
|
+
id: stage,
|
|
282
|
+
status: 'pending',
|
|
283
|
+
checkpoint_file: path.join(task.work_dir, 'checkpoints', `${stage}.json`),
|
|
284
|
+
})),
|
|
285
|
+
ai_authoring_policy: {
|
|
286
|
+
output_only: 'structured_patch_or_build_plan',
|
|
287
|
+
deterministic_apply_required: true,
|
|
288
|
+
remote_write_allowed: false,
|
|
289
|
+
},
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
function extractProcessFlowRefs(payload) {
|
|
293
|
+
const refs = new Map();
|
|
294
|
+
scanForFlowRefs(payload, [], refs);
|
|
295
|
+
return [...refs.values()].sort((a, b) => `${a.id}@${a.version ?? ''}`.localeCompare(`${b.id}@${b.version ?? ''}`));
|
|
296
|
+
}
|
|
297
|
+
function scanForFlowRefs(value, pathParts, refs) {
|
|
298
|
+
if (Array.isArray(value)) {
|
|
299
|
+
value.forEach((item, index) => scanForFlowRefs(item, [...pathParts, String(index)], refs));
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
if (!isRecord(value)) {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
const keyPath = pathParts.join('.');
|
|
306
|
+
const keyHint = keyPath.toLowerCase();
|
|
307
|
+
const looksLikeFlowRef = keyHint.includes('referencetoflowdataset') ||
|
|
308
|
+
keyHint.includes('reference_to_flow_dataset') ||
|
|
309
|
+
keyHint.includes('flowdataset') ||
|
|
310
|
+
keyHint.includes('flow_dataset');
|
|
311
|
+
if (looksLikeFlowRef) {
|
|
312
|
+
const id = firstNonEmpty(value['@refObjectId'], value.refObjectId, value.ref_object_id, value.id, value.uuid, value['common:UUID']);
|
|
313
|
+
if (id) {
|
|
314
|
+
const version = firstNonEmpty(value['@version'], value.version, value.dataSetVersion, value['common:dataSetVersion']);
|
|
315
|
+
refs.set(`${id}@${version ?? ''}@${keyPath}`, {
|
|
316
|
+
id,
|
|
317
|
+
version,
|
|
318
|
+
path: keyPath,
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
for (const [key, nested] of Object.entries(value)) {
|
|
323
|
+
scanForFlowRefs(nested, [...pathParts, key], refs);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
function readExternalFlowRefs(inputPath) {
|
|
327
|
+
const rows = materializeDatasetRows(inputPath);
|
|
328
|
+
return rows.map((row) => {
|
|
329
|
+
const id = firstNonEmpty(row.id, row.row.id, row.row.dataset_id, row.row.uuid);
|
|
330
|
+
if (!id) {
|
|
331
|
+
throw new CliError(`External flow ref is missing id in ${inputPath} at index ${row.index}.`, {
|
|
332
|
+
code: 'CURATION_QUEUE_EXTERNAL_FLOW_REF_ID_MISSING',
|
|
333
|
+
exitCode: 2,
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
return {
|
|
337
|
+
id,
|
|
338
|
+
version: firstNonEmpty(row.version, row.row.version) ?? null,
|
|
339
|
+
path: `${inputPath}#${row.index}`,
|
|
340
|
+
};
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
function normalizeProcessLimit(value) {
|
|
344
|
+
if (value === undefined) {
|
|
345
|
+
return null;
|
|
346
|
+
}
|
|
347
|
+
if (!Number.isInteger(value) || value < 1) {
|
|
348
|
+
throw new CliError('--process-limit must be a positive integer.', {
|
|
349
|
+
code: 'CURATION_QUEUE_PROCESS_LIMIT_INVALID',
|
|
350
|
+
exitCode: 2,
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
return value;
|
|
354
|
+
}
|
|
355
|
+
function requirePath(value, flag) {
|
|
356
|
+
const trimmed = value?.trim();
|
|
357
|
+
if (!trimmed) {
|
|
358
|
+
throw new CliError(`${flag} is required.`, {
|
|
359
|
+
code: 'CURATION_QUEUE_REQUIRED_FLAG_MISSING',
|
|
360
|
+
exitCode: 2,
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
return trimmed;
|
|
364
|
+
}
|
|
365
|
+
function requireExistingPath(value, flag) {
|
|
366
|
+
const inputPath = requirePath(value, flag);
|
|
367
|
+
if (!existsSync(inputPath)) {
|
|
368
|
+
throw new CliError(`${flag} file does not exist: ${inputPath}`, {
|
|
369
|
+
code: 'CURATION_QUEUE_INPUT_NOT_FOUND',
|
|
370
|
+
exitCode: 2,
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
return inputPath;
|
|
374
|
+
}
|
|
375
|
+
function entityDirPlural(entityType) {
|
|
376
|
+
return entityType === 'process' ? 'processes' : entityType === 'flow' ? 'flows' : 'supports';
|
|
377
|
+
}
|
|
378
|
+
function entityDirName(id, version) {
|
|
379
|
+
return `${sanitizePathToken(id)}__${sanitizePathToken(version)}`;
|
|
380
|
+
}
|
|
381
|
+
function taskIdFor(entityType, id, version) {
|
|
382
|
+
return `${entityType}:${id}@${version}`;
|
|
383
|
+
}
|
|
384
|
+
function sanitizePathToken(value) {
|
|
385
|
+
return value.replace(/[^A-Za-z0-9._-]+/gu, '_').replace(/^_+|_+$/gu, '') || 'unknown';
|
|
386
|
+
}
|
|
387
|
+
function buildInputHashes(inputPaths) {
|
|
388
|
+
const hashes = {};
|
|
389
|
+
for (const inputPath of inputPaths) {
|
|
390
|
+
hashes[path.resolve(inputPath)] = sha256(readFileSync(inputPath));
|
|
391
|
+
}
|
|
392
|
+
return hashes;
|
|
393
|
+
}
|
|
394
|
+
function sha256(value) {
|
|
395
|
+
return createHash('sha256').update(value).digest('hex');
|
|
396
|
+
}
|
|
397
|
+
function writeJson(filePath, value) {
|
|
398
|
+
mkdirSync(path.dirname(filePath), { recursive: true });
|
|
399
|
+
writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`, 'utf8');
|
|
400
|
+
}
|
|
401
|
+
function writeText(filePath, value) {
|
|
402
|
+
mkdirSync(path.dirname(filePath), { recursive: true });
|
|
403
|
+
writeFileSync(filePath, value, 'utf8');
|
|
404
|
+
}
|
|
405
|
+
function jsonLines(rows) {
|
|
406
|
+
return rows.map((row) => JSON.stringify(row)).join('\n') + (rows.length > 0 ? '\n' : '');
|
|
407
|
+
}
|
|
408
|
+
export const __testInternals = {
|
|
409
|
+
extractProcessFlowRefs,
|
|
410
|
+
};
|
|
411
|
+
//# sourceMappingURL=dataset-curation-queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dataset-curation-queue.js","sourceRoot":"","sources":["../../../src/lib/dataset-curation-queue.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { createHash } from 'node:crypto';\nimport path from 'node:path';\nimport { CliError } from './errors.js';\nimport {\n firstNonEmpty,\n isRecord,\n materializeDatasetRows,\n type DatasetRowInput,\n type JsonObject,\n} from './dataset-local.js';\n\nexport type DatasetCurationQueueEntityType = 'support' | 'flow' | 'process';\n\nexport type RunDatasetCurationQueueBuildOptions = {\n processesPath: string;\n flowsPath?: string;\n supportPaths?: string[];\n externalFlowRefPaths?: string[];\n outDir: string;\n excludeProcessIds?: string[];\n processLimit?: number;\n};\n\nexport type DatasetCurationQueueTask = {\n schema_version: 1;\n entity_type: DatasetCurationQueueEntityType;\n task_id: string;\n entity_id: string;\n version: string;\n lock_key: string;\n depends_on: string[];\n input_rows_file: string;\n work_dir: string;\n checkpoint_file: string;\n run_plan_file: string;\n closure_file: string;\n};\n\ntype DatasetCurationQueueBlocker = {\n schema_version: 1;\n code: string;\n severity: 'blocker';\n entity_type: DatasetCurationQueueEntityType;\n entity_id: string | null;\n version: string | null;\n message: string;\n details?: unknown;\n};\n\nexport type DatasetCurationQueueBuildReport = {\n schema_version: 1;\n generated_at_utc: string;\n status: 'ready' | 'blocked';\n out_dir: string;\n inputs: {\n processes: string;\n flows: string | null;\n support: string[];\n external_flow_refs: string[];\n };\n counts: {\n support_rows: number;\n flow_rows: number;\n process_rows: number;\n external_flow_refs: number;\n tasks: number;\n blockers: number;\n };\n hashes: {\n inputs: Record<string, string>;\n task_order: string;\n };\n files: {\n manifest: string;\n tasks: string;\n locks: string;\n blockers: string;\n };\n tasks: DatasetCurationQueueTask[];\n blockers: DatasetCurationQueueBlocker[];\n};\n\ntype QueueRow = {\n entityType: DatasetCurationQueueEntityType;\n sourcePath: string;\n sourceIndex: number;\n row: JsonObject;\n payload: JsonObject;\n id: string;\n version: string;\n};\n\ntype FlowRef = {\n id: string;\n version: string | null;\n path: string;\n};\n\nconst DEFAULT_VERSION = 'unversioned';\n\nexport async function runDatasetCurationQueueBuild(\n options: RunDatasetCurationQueueBuildOptions,\n): Promise<DatasetCurationQueueBuildReport> {\n const outDir = requirePath(options.outDir, '--out-dir');\n const processesPath = requireExistingPath(options.processesPath, '--processes');\n const flowsPath = options.flowsPath ? requireExistingPath(options.flowsPath, '--flows') : null;\n const supportPaths = (options.supportPaths ?? []).map((inputPath) =>\n requireExistingPath(inputPath, '--support'),\n );\n const externalFlowRefPaths = (options.externalFlowRefPaths ?? []).map((inputPath) =>\n requireExistingPath(inputPath, '--external-flow-ref'),\n );\n const processLimit = normalizeProcessLimit(options.processLimit);\n const excludedProcessIds = new Set((options.excludeProcessIds ?? []).map((id) => id.trim()));\n\n const supportRows = supportPaths.flatMap((inputPath) => readQueueRows(inputPath, 'support'));\n const flowRows = flowsPath ? readQueueRows(flowsPath, 'flow') : [];\n const processRows = readQueueRows(processesPath, 'process')\n .filter((row) => !excludedProcessIds.has(row.id))\n .slice(0, processLimit ?? undefined);\n const externalFlowRefs = externalFlowRefPaths.flatMap((inputPath) =>\n readExternalFlowRefs(inputPath),\n );\n\n mkdirSync(outDir, { recursive: true });\n\n const localFlowTasks = new Map<string, string>();\n const flowRowsById = new Map<string, QueueRow>();\n for (const row of flowRows) {\n const taskId = taskIdFor(row.entityType, row.id, row.version);\n localFlowTasks.set(row.id, taskId);\n flowRowsById.set(row.id, row);\n }\n\n const externalFlowIds = new Set(externalFlowRefs.map((ref) => ref.id));\n const blockers: DatasetCurationQueueBlocker[] = [];\n const supportTasks = supportRows.map((row) => buildTask(outDir, row, []));\n const flowTasks = flowRows.map((row) => buildTask(outDir, row, []));\n const processTasks = processRows.map((row) => {\n const refs = extractProcessFlowRefs(row.payload);\n const dependsOn = new Set<string>();\n const missingRefs: FlowRef[] = [];\n for (const ref of refs) {\n const taskId = localFlowTasks.get(ref.id);\n if (taskId) {\n dependsOn.add(taskId);\n } else if (!externalFlowIds.has(ref.id)) {\n missingRefs.push(ref);\n }\n }\n if (missingRefs.length > 0) {\n blockers.push({\n schema_version: 1,\n code: 'process_flow_reference_unresolved',\n severity: 'blocker',\n entity_type: 'process',\n entity_id: row.id,\n version: row.version,\n message:\n 'Process references flows that are neither present in local flow rows nor declared as external flow refs.',\n details: { missing_flow_refs: missingRefs },\n });\n }\n return buildTask(outDir, row, [...dependsOn].sort());\n });\n const tasks = [...supportTasks, ...flowTasks, ...processTasks];\n const taskRows = [\n ...supportRows.map((row, index) => ({ row, task: supportTasks[index] })),\n ...flowRows.map((row, index) => ({ row, task: flowTasks[index] })),\n ...processRows.map((row, index) => ({ row, task: processTasks[index] })),\n ];\n\n for (const { row, task } of taskRows) {\n writeEntityArtifacts({\n row,\n task,\n flowRefs: row.entityType === 'process' ? extractProcessFlowRefs(row.payload) : [],\n externalFlowIds,\n flowRowsById,\n });\n }\n\n const outputsDir = path.join(outDir, 'outputs');\n mkdirSync(outputsDir, { recursive: true });\n const manifestPath = path.join(outputsDir, 'curation-queue-manifest.json');\n const tasksPath = path.join(outputsDir, 'curation-queue-tasks.jsonl');\n const locksPath = path.join(outputsDir, 'curation-queue-locks.json');\n const blockersPath = path.join(outputsDir, 'curation-queue-blockers.jsonl');\n const inputHashes = buildInputHashes([\n processesPath,\n ...(flowsPath ? [flowsPath] : []),\n ...supportPaths,\n ...externalFlowRefPaths,\n ]);\n const locks = {\n schema_version: 1,\n generated_at_utc: new Date().toISOString(),\n locks: tasks.map((task) => ({\n lock_key: task.lock_key,\n task_id: task.task_id,\n entity_type: task.entity_type,\n entity_id: task.entity_id,\n version: task.version,\n status: 'available',\n })),\n };\n const report: DatasetCurationQueueBuildReport = {\n schema_version: 1,\n generated_at_utc: new Date().toISOString(),\n status: blockers.length > 0 ? 'blocked' : 'ready',\n out_dir: path.resolve(outDir),\n inputs: {\n processes: path.resolve(processesPath),\n flows: flowsPath ? path.resolve(flowsPath) : null,\n support: supportPaths.map((inputPath) => path.resolve(inputPath)),\n external_flow_refs: externalFlowRefPaths.map((inputPath) => path.resolve(inputPath)),\n },\n counts: {\n support_rows: supportRows.length,\n flow_rows: flowRows.length,\n process_rows: processRows.length,\n external_flow_refs: externalFlowRefs.length,\n tasks: tasks.length,\n blockers: blockers.length,\n },\n hashes: {\n inputs: inputHashes,\n task_order: sha256(tasks.map((task) => task.task_id).join('\\n')),\n },\n files: {\n manifest: manifestPath,\n tasks: tasksPath,\n locks: locksPath,\n blockers: blockersPath,\n },\n tasks,\n blockers,\n };\n\n writeJson(manifestPath, report);\n writeText(tasksPath, jsonLines(tasks));\n writeJson(locksPath, locks);\n writeText(blockersPath, jsonLines(blockers));\n return report;\n}\n\nfunction readQueueRows(inputPath: string, entityType: DatasetCurationQueueEntityType): QueueRow[] {\n const rows = materializeDatasetRows(inputPath);\n return rows.map((row) => queueRowFromDatasetRow(inputPath, entityType, row));\n}\n\nfunction queueRowFromDatasetRow(\n inputPath: string,\n entityType: DatasetCurationQueueEntityType,\n row: DatasetRowInput,\n): QueueRow {\n const tidasIdentity = genericTidasDatasetIdentity(row.payload);\n const id = firstNonEmpty(row.id, row.row.id, row.row.dataset_id, row.row.uuid, tidasIdentity.id);\n if (!id) {\n throw new CliError(\n `${entityType} row is missing a stable id in ${inputPath} at index ${row.index}.`,\n {\n code: 'CURATION_QUEUE_ROW_ID_MISSING',\n exitCode: 2,\n },\n );\n }\n return {\n entityType,\n sourcePath: inputPath,\n sourceIndex: row.index,\n row: row.row,\n payload: row.payload,\n id,\n version: firstNonEmpty(row.version, row.row.version, tidasIdentity.version) ?? DEFAULT_VERSION,\n };\n}\n\nfunction genericTidasDatasetIdentity(payload: JsonObject): {\n id: string | null;\n version: string | null;\n} {\n const root = genericTidasDatasetRoot(payload);\n const information = Object.values(root).find(\n (value) => isRecord(value) && isRecord(value.dataSetInformation),\n );\n const dataSetInformation =\n isRecord(information) && isRecord(information.dataSetInformation)\n ? information.dataSetInformation\n : {};\n const administrativeInformation = isRecord(root.administrativeInformation)\n ? root.administrativeInformation\n : {};\n const publicationAndOwnership = isRecord(administrativeInformation.publicationAndOwnership)\n ? administrativeInformation.publicationAndOwnership\n : {};\n\n return {\n id: firstNonEmpty(dataSetInformation['common:UUID']),\n version: firstNonEmpty(publicationAndOwnership['common:dataSetVersion']),\n };\n}\n\nfunction genericTidasDatasetRoot(payload: JsonObject): JsonObject {\n const datasetRoots: JsonObject[] = [];\n for (const [key, value] of Object.entries(payload)) {\n if (key.endsWith('DataSet') && isRecord(value)) {\n datasetRoots.push(value);\n }\n }\n return datasetRoots.length === 1 ? datasetRoots[0] : payload;\n}\n\nfunction buildTask(outDir: string, row: QueueRow, dependsOn: string[]): DatasetCurationQueueTask {\n const taskId = taskIdFor(row.entityType, row.id, row.version);\n const workDir = path.join(\n outDir,\n 'entities',\n entityDirPlural(row.entityType),\n entityDirName(row.id, row.version),\n );\n return {\n schema_version: 1,\n entity_type: row.entityType,\n task_id: taskId,\n entity_id: row.id,\n version: row.version,\n lock_key: taskId,\n depends_on: dependsOn,\n input_rows_file: path.join(workDir, 'input.jsonl'),\n work_dir: workDir,\n checkpoint_file: path.join(workDir, 'checkpoint.json'),\n run_plan_file: path.join(workDir, 'entity-run-plan.json'),\n closure_file: path.join(workDir, 'closure.json'),\n };\n}\n\nfunction writeEntityArtifacts(options: {\n row: QueueRow;\n task: DatasetCurationQueueTask;\n flowRefs: FlowRef[];\n externalFlowIds: Set<string>;\n flowRowsById: Map<string, QueueRow>;\n}): void {\n mkdirSync(path.join(options.task.work_dir, 'checkpoints'), { recursive: true });\n writeText(options.task.input_rows_file, jsonLines([options.row.row]));\n writeJson(options.task.closure_file, {\n schema_version: 1,\n entity_type: options.row.entityType,\n entity_id: options.row.id,\n version: options.row.version,\n source: {\n file: path.resolve(options.row.sourcePath),\n index: options.row.sourceIndex,\n },\n dependencies: buildDependencyClosure(options),\n });\n writeJson(options.task.run_plan_file, buildRunPlan(options.task));\n}\n\nfunction buildDependencyClosure(options: {\n row: QueueRow;\n task: DatasetCurationQueueTask;\n flowRefs: FlowRef[];\n externalFlowIds: Set<string>;\n flowRowsById: Map<string, QueueRow>;\n}): unknown {\n if (options.row.entityType !== 'process') {\n return {\n local_tasks: [],\n external_refs: [],\n unresolved_refs: [],\n };\n }\n return {\n local_tasks: options.flowRefs\n .map((ref) => ({ ref, row: options.flowRowsById.get(ref.id) }))\n .filter((item): item is { ref: FlowRef; row: QueueRow } => Boolean(item.row))\n .map(({ ref, row }) => ({\n entity_type: 'flow',\n entity_id: ref.id,\n version: row.version,\n task_id: taskIdFor('flow', ref.id, row.version),\n ref_path: ref.path,\n })),\n external_refs: options.flowRefs\n .filter((ref) => !options.flowRowsById.has(ref.id) && options.externalFlowIds.has(ref.id))\n .map((ref) => ({\n entity_type: 'flow',\n entity_id: ref.id,\n version: ref.version,\n ref_path: ref.path,\n })),\n unresolved_refs: options.flowRefs\n .filter((ref) => !options.flowRowsById.has(ref.id) && !options.externalFlowIds.has(ref.id))\n .map((ref) => ({\n entity_type: 'flow',\n entity_id: ref.id,\n version: ref.version,\n ref_path: ref.path,\n })),\n };\n}\n\nfunction buildRunPlan(task: DatasetCurationQueueTask): unknown {\n const stagesByType: Record<DatasetCurationQueueEntityType, string[]> = {\n support: ['identity', 'schema', 'qa_or_profile', 'checkpoint'],\n flow: ['identity', 'name_plan', 'schema', 'qa', 'checkpoint'],\n process: [\n 'dependency_closure',\n 'reference_refresh',\n 'required_fields',\n 'schema',\n 'qa',\n 'curation',\n 'remote_dry_run',\n 'readback',\n ],\n };\n return {\n schema_version: 1,\n task_id: task.task_id,\n entity_type: task.entity_type,\n entity_id: task.entity_id,\n version: task.version,\n input_rows_file: task.input_rows_file,\n checkpoint_file: task.checkpoint_file,\n stages: stagesByType[task.entity_type].map((stage) => ({\n id: stage,\n status: 'pending',\n checkpoint_file: path.join(task.work_dir, 'checkpoints', `${stage}.json`),\n })),\n ai_authoring_policy: {\n output_only: 'structured_patch_or_build_plan',\n deterministic_apply_required: true,\n remote_write_allowed: false,\n },\n };\n}\n\nfunction extractProcessFlowRefs(payload: unknown): FlowRef[] {\n const refs = new Map<string, FlowRef>();\n scanForFlowRefs(payload, [], refs);\n return [...refs.values()].sort((a, b) =>\n `${a.id}@${a.version ?? ''}`.localeCompare(`${b.id}@${b.version ?? ''}`),\n );\n}\n\nfunction scanForFlowRefs(value: unknown, pathParts: string[], refs: Map<string, FlowRef>): void {\n if (Array.isArray(value)) {\n value.forEach((item, index) => scanForFlowRefs(item, [...pathParts, String(index)], refs));\n return;\n }\n if (!isRecord(value)) {\n return;\n }\n\n const keyPath = pathParts.join('.');\n const keyHint = keyPath.toLowerCase();\n const looksLikeFlowRef =\n keyHint.includes('referencetoflowdataset') ||\n keyHint.includes('reference_to_flow_dataset') ||\n keyHint.includes('flowdataset') ||\n keyHint.includes('flow_dataset');\n if (looksLikeFlowRef) {\n const id = firstNonEmpty(\n value['@refObjectId'],\n value.refObjectId,\n value.ref_object_id,\n value.id,\n value.uuid,\n value['common:UUID'],\n );\n if (id) {\n const version = firstNonEmpty(\n value['@version'],\n value.version,\n value.dataSetVersion,\n value['common:dataSetVersion'],\n );\n refs.set(`${id}@${version ?? ''}@${keyPath}`, {\n id,\n version,\n path: keyPath,\n });\n }\n }\n\n for (const [key, nested] of Object.entries(value)) {\n scanForFlowRefs(nested, [...pathParts, key], refs);\n }\n}\n\nfunction readExternalFlowRefs(inputPath: string): FlowRef[] {\n const rows = materializeDatasetRows(inputPath);\n return rows.map((row) => {\n const id = firstNonEmpty(row.id, row.row.id, row.row.dataset_id, row.row.uuid);\n if (!id) {\n throw new CliError(`External flow ref is missing id in ${inputPath} at index ${row.index}.`, {\n code: 'CURATION_QUEUE_EXTERNAL_FLOW_REF_ID_MISSING',\n exitCode: 2,\n });\n }\n return {\n id,\n version: firstNonEmpty(row.version, row.row.version) ?? null,\n path: `${inputPath}#${row.index}`,\n };\n });\n}\n\nfunction normalizeProcessLimit(value: number | undefined): number | null {\n if (value === undefined) {\n return null;\n }\n if (!Number.isInteger(value) || value < 1) {\n throw new CliError('--process-limit must be a positive integer.', {\n code: 'CURATION_QUEUE_PROCESS_LIMIT_INVALID',\n exitCode: 2,\n });\n }\n return value;\n}\n\nfunction requirePath(value: string | undefined, flag: string): string {\n const trimmed = value?.trim();\n if (!trimmed) {\n throw new CliError(`${flag} is required.`, {\n code: 'CURATION_QUEUE_REQUIRED_FLAG_MISSING',\n exitCode: 2,\n });\n }\n return trimmed;\n}\n\nfunction requireExistingPath(value: string | undefined, flag: string): string {\n const inputPath = requirePath(value, flag);\n if (!existsSync(inputPath)) {\n throw new CliError(`${flag} file does not exist: ${inputPath}`, {\n code: 'CURATION_QUEUE_INPUT_NOT_FOUND',\n exitCode: 2,\n });\n }\n return inputPath;\n}\n\nfunction entityDirPlural(entityType: DatasetCurationQueueEntityType): string {\n return entityType === 'process' ? 'processes' : entityType === 'flow' ? 'flows' : 'supports';\n}\n\nfunction entityDirName(id: string, version: string): string {\n return `${sanitizePathToken(id)}__${sanitizePathToken(version)}`;\n}\n\nfunction taskIdFor(\n entityType: DatasetCurationQueueEntityType,\n id: string,\n version: string,\n): string {\n return `${entityType}:${id}@${version}`;\n}\n\nfunction sanitizePathToken(value: string): string {\n return value.replace(/[^A-Za-z0-9._-]+/gu, '_').replace(/^_+|_+$/gu, '') || 'unknown';\n}\n\nfunction buildInputHashes(inputPaths: string[]): Record<string, string> {\n const hashes: Record<string, string> = {};\n for (const inputPath of inputPaths) {\n hashes[path.resolve(inputPath)] = sha256(readFileSync(inputPath));\n }\n return hashes;\n}\n\nfunction sha256(value: string | Buffer): string {\n return createHash('sha256').update(value).digest('hex');\n}\n\nfunction writeJson(filePath: string, value: unknown): void {\n mkdirSync(path.dirname(filePath), { recursive: true });\n writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\\n`, 'utf8');\n}\n\nfunction writeText(filePath: string, value: string): void {\n mkdirSync(path.dirname(filePath), { recursive: true });\n writeFileSync(filePath, value, 'utf8');\n}\n\nfunction jsonLines(rows: unknown[]): string {\n return rows.map((row) => JSON.stringify(row)).join('\\n') + (rows.length > 0 ? '\\n' : '');\n}\n\nexport const __testInternals = {\n extractProcessFlowRefs,\n};\n"]}
|
|
@@ -6,6 +6,7 @@ const ANNUAL_SUPPLY_FIELD = 'processDataSet.modellingAndValidation.dataSourcesTr
|
|
|
6
6
|
const ANNUAL_SUPPLY_ROOT_FIELD = 'modellingAndValidation.dataSourcesTreatmentAndRepresentativeness.annualSupplyOrProductionVolume';
|
|
7
7
|
const NUMERIC_TEXT_WITH_SUFFIX_PATTERN = /^[+-]?(\d+(\.\d*)?|\.\d+)([Ee][+-]?\d+)?\s+\S.*$/u;
|
|
8
8
|
const ANNUAL_PERIOD_PATTERN = /(?:\/\s*(?:year|yr|a)\b|\bper\s+(?:year|annum)\b|\/\s*年|每年|年度|年供应|年产)/iu;
|
|
9
|
+
const ANNUAL_UNAVAILABLE_PATTERN = /\b(?:source\s+)?(?:production\s+)?volume\s+(?:unavailable|unknown|not\s+available)\b/iu;
|
|
9
10
|
const CJK_TEXT_PATTERN = /[\p{Script=Han}]/u;
|
|
10
11
|
const NET_CALORIFIC_VALUE_ID = '93a60a56-a3c8-11da-a746-0800200c9a66';
|
|
11
12
|
const DEFAULT_COMPLIANCE_SYSTEM = {
|
|
@@ -78,7 +79,9 @@ function isValidAnnualSupplyVolume(value) {
|
|
|
78
79
|
return (items.length > 0 &&
|
|
79
80
|
items.every((item) => {
|
|
80
81
|
const text = item['#text'].trim();
|
|
81
|
-
return
|
|
82
|
+
return (!ANNUAL_UNAVAILABLE_PATTERN.test(text) &&
|
|
83
|
+
NUMERIC_TEXT_WITH_SUFFIX_PATTERN.test(text) &&
|
|
84
|
+
ANNUAL_PERIOD_PATTERN.test(text));
|
|
82
85
|
}));
|
|
83
86
|
}
|
|
84
87
|
function isValidReview(value) {
|
|
@@ -145,6 +148,16 @@ export function collectProcessRequiredFieldIssues(payload) {
|
|
|
145
148
|
},
|
|
146
149
|
];
|
|
147
150
|
}
|
|
151
|
+
if (items.some((item) => ANNUAL_UNAVAILABLE_PATTERN.test(item['#text'].trim()))) {
|
|
152
|
+
return [
|
|
153
|
+
...issues,
|
|
154
|
+
{
|
|
155
|
+
code: 'annual_supply_or_production_volume_missing',
|
|
156
|
+
message: 'Process payload must include annualSupplyOrProductionVolume as numeric text with a unit or context suffix.',
|
|
157
|
+
path: ANNUAL_SUPPLY_FIELD,
|
|
158
|
+
},
|
|
159
|
+
];
|
|
160
|
+
}
|
|
148
161
|
if (items.every((item) => NUMERIC_TEXT_WITH_SUFFIX_PATTERN.test(item['#text'].trim())) &&
|
|
149
162
|
items.some((item) => !ANNUAL_PERIOD_PATTERN.test(item['#text'].trim()))) {
|
|
150
163
|
return [
|