@tiangong-lca/cli 0.0.6 → 0.0.8
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 +154 -30
- package/dist/src/cli.js +2235 -149
- package/dist/src/cli.js.map +1 -1
- package/dist/src/lib/dataset-author.js +100 -0
- package/dist/src/lib/dataset-author.js.map +1 -0
- package/dist/src/lib/dataset-bilingual.js +545 -0
- package/dist/src/lib/dataset-bilingual.js.map +1 -0
- package/dist/src/lib/dataset-contract.js +350 -0
- package/dist/src/lib/dataset-contract.js.map +1 -0
- package/dist/src/lib/dataset-evidence-search.js +636 -0
- package/dist/src/lib/dataset-evidence-search.js.map +1 -0
- package/dist/src/lib/dataset-import-lca.js +171 -0
- package/dist/src/lib/dataset-import-lca.js.map +1 -0
- package/dist/src/lib/dataset-local.js +231 -0
- package/dist/src/lib/dataset-local.js.map +1 -0
- package/dist/src/lib/dataset-references-rewrite.js +214 -0
- package/dist/src/lib/dataset-references-rewrite.js.map +1 -0
- package/dist/src/lib/dataset-remote-refresh.js +166 -0
- package/dist/src/lib/dataset-remote-refresh.js.map +1 -0
- package/dist/src/lib/dataset-remote-verify.js +543 -0
- package/dist/src/lib/dataset-remote-verify.js.map +1 -0
- package/dist/src/lib/dataset-validate.js +247 -0
- package/dist/src/lib/dataset-validate.js.map +1 -0
- package/dist/src/lib/flow-payload-validation.js +51 -0
- package/dist/src/lib/flow-payload-validation.js.map +1 -0
- package/dist/src/lib/flow-publish-reviewed-data.js +16 -0
- package/dist/src/lib/flow-publish-reviewed-data.js.map +1 -1
- package/dist/src/lib/flow-publish-version.js +182 -12
- package/dist/src/lib/flow-publish-version.js.map +1 -1
- package/dist/src/lib/flow-regen-product.js +2 -10
- package/dist/src/lib/flow-regen-product.js.map +1 -1
- package/dist/src/lib/flow-remediate.js +4 -8
- package/dist/src/lib/flow-remediate.js.map +1 -1
- package/dist/src/lib/identity-preflight.js +1021 -0
- package/dist/src/lib/identity-preflight.js.map +1 -0
- package/dist/src/lib/lifecyclemodel-auto-build.js +11 -8
- package/dist/src/lib/lifecyclemodel-auto-build.js.map +1 -1
- package/dist/src/lib/lifecyclemodel-graph.js +248 -0
- package/dist/src/lib/lifecyclemodel-graph.js.map +1 -0
- package/dist/src/lib/lifecyclemodel-publish-build.js +1 -1
- package/dist/src/lib/lifecyclemodel-publish-build.js.map +1 -1
- package/dist/src/lib/lifecyclemodel-save-draft-run.js +245 -0
- package/dist/src/lib/lifecyclemodel-save-draft-run.js.map +1 -0
- package/dist/src/lib/lifecyclemodel-validate-build.js +1 -1
- package/dist/src/lib/lifecyclemodel-validate-build.js.map +1 -1
- package/dist/src/lib/process-auto-build.js +158 -8
- package/dist/src/lib/process-auto-build.js.map +1 -1
- package/dist/src/lib/process-batch-build.js +6 -3
- package/dist/src/lib/process-batch-build.js.map +1 -1
- package/dist/src/lib/process-dedup-review.js +921 -0
- package/dist/src/lib/process-dedup-review.js.map +1 -0
- package/dist/src/lib/process-flow-build-plan.js +1071 -0
- package/dist/src/lib/process-flow-build-plan.js.map +1 -0
- package/dist/src/lib/process-payload-validation.js +57 -0
- package/dist/src/lib/process-payload-validation.js.map +1 -0
- package/dist/src/lib/process-publish-build.js +126 -10
- package/dist/src/lib/process-publish-build.js.map +1 -1
- package/dist/src/lib/process-refresh-references.js +1037 -0
- package/dist/src/lib/process-refresh-references.js.map +1 -0
- package/dist/src/lib/process-required-fields.js +810 -0
- package/dist/src/lib/process-required-fields.js.map +1 -0
- package/dist/src/lib/process-resume-build.js +4 -6
- package/dist/src/lib/process-resume-build.js.map +1 -1
- package/dist/src/lib/process-save-draft-run.js +35 -13
- package/dist/src/lib/process-save-draft-run.js.map +1 -1
- package/dist/src/lib/process-scope-statistics.js +859 -0
- package/dist/src/lib/process-scope-statistics.js.map +1 -0
- package/dist/src/lib/process-verify-rows.js +250 -0
- package/dist/src/lib/process-verify-rows.js.map +1 -0
- package/dist/src/lib/publish.js +101 -1
- package/dist/src/lib/publish.js.map +1 -1
- package/dist/src/lib/remote.js +4 -4
- package/dist/src/lib/remote.js.map +1 -1
- package/dist/src/lib/review-flow.js +58 -0
- package/dist/src/lib/review-flow.js.map +1 -1
- package/dist/src/lib/review-lifecyclemodel.js +2 -2
- package/dist/src/lib/review-lifecyclemodel.js.map +1 -1
- package/dist/src/lib/review-process.js +150 -2
- package/dist/src/lib/review-process.js.map +1 -1
- package/dist/src/lib/runtime-rulesets.js +283 -0
- package/dist/src/lib/runtime-rulesets.js.map +1 -0
- package/dist/src/lib/tidas-sdk-package-validator.js +28 -9
- package/dist/src/lib/tidas-sdk-package-validator.js.map +1 -1
- package/dist/src/lib/tidas-sdk-validation.js +96 -0
- package/dist/src/lib/tidas-sdk-validation.js.map +1 -0
- package/dist/src/lib/user-api-key.js +1 -1
- package/dist/src/lib/user-api-key.js.map +1 -1
- package/package.json +5 -5
- /package/bin/{tiangong.d.ts → tiangong-lca.d.ts} +0 -0
- /package/bin/{tiangong.js → tiangong-lca.js} +0 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { writeJsonArtifact, writeJsonLinesArtifact } from './artifacts.js';
|
|
3
|
+
import { CliError } from './errors.js';
|
|
4
|
+
import { cloneJson, firstNonEmpty, isRecord, materializeDatasetRows, unwrapDatasetPayload, } from './dataset-local.js';
|
|
5
|
+
import { runLifecyclemodelSaveDraft, } from './lifecyclemodel-save-draft-run.js';
|
|
6
|
+
import { runProcessSaveDraft } from './process-save-draft-run.js';
|
|
7
|
+
function parseReference(value, label) {
|
|
8
|
+
const match = /^(flow):([^@\s]+)(?:@([^@\s]+))?$/u.exec(value.trim());
|
|
9
|
+
if (!match) {
|
|
10
|
+
throw new CliError(`Expected ${label} reference like flow:<id>@<version>.`, {
|
|
11
|
+
code: 'DATASET_REFERENCE_INVALID',
|
|
12
|
+
exitCode: 2,
|
|
13
|
+
details: value,
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
kind: 'flow',
|
|
18
|
+
id: match[2],
|
|
19
|
+
version: match[3] ?? null,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function normalizeTypes(values) {
|
|
23
|
+
const raw = values && values.length > 0 ? values.flatMap((value) => value.split(',')) : [];
|
|
24
|
+
const normalized = raw
|
|
25
|
+
.map((value) => value.trim().toLowerCase())
|
|
26
|
+
.filter(Boolean)
|
|
27
|
+
.map((value) => {
|
|
28
|
+
if (value === 'process' || value === 'processes') {
|
|
29
|
+
return 'process';
|
|
30
|
+
}
|
|
31
|
+
if (value === 'lifecyclemodel' ||
|
|
32
|
+
value === 'lifecyclemodels' ||
|
|
33
|
+
value === 'model' ||
|
|
34
|
+
value === 'models') {
|
|
35
|
+
return 'lifecyclemodel';
|
|
36
|
+
}
|
|
37
|
+
throw new CliError('Expected --type or --types to contain process and/or lifecyclemodel.', {
|
|
38
|
+
code: 'DATASET_REFERENCE_TYPES_INVALID',
|
|
39
|
+
exitCode: 2,
|
|
40
|
+
details: value,
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
return normalized.length > 0 ? [...new Set(normalized)] : ['process', 'lifecyclemodel'];
|
|
44
|
+
}
|
|
45
|
+
function joinPath(base, key) {
|
|
46
|
+
return base ? `${base}.${String(key)}` : String(key);
|
|
47
|
+
}
|
|
48
|
+
function rewriteProcessReferences(value, from, to, pathPrefix, change) {
|
|
49
|
+
if (Array.isArray(value)) {
|
|
50
|
+
value.forEach((item, index) => rewriteProcessReferences(item, from, to, joinPath(pathPrefix, index), change));
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (!isRecord(value)) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const reference = value.referenceToFlowDataSet;
|
|
57
|
+
if (isRecord(reference) && reference['@refObjectId'] === from.id) {
|
|
58
|
+
const versionMatches = !from.version || reference['@version'] === from.version;
|
|
59
|
+
if (versionMatches) {
|
|
60
|
+
if (reference['@refObjectId'] !== to.id) {
|
|
61
|
+
change(joinPath(pathPrefix, 'referenceToFlowDataSet.@refObjectId'), '@refObjectId', reference['@refObjectId'], to.id);
|
|
62
|
+
reference['@refObjectId'] = to.id;
|
|
63
|
+
}
|
|
64
|
+
if (to.version && reference['@version'] !== to.version) {
|
|
65
|
+
change(joinPath(pathPrefix, 'referenceToFlowDataSet.@version'), '@version', typeof reference['@version'] === 'string' ? reference['@version'] : null, to.version);
|
|
66
|
+
reference['@version'] = to.version;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
Object.entries(value).forEach(([key, child]) => rewriteProcessReferences(child, from, to, joinPath(pathPrefix, key), change));
|
|
71
|
+
}
|
|
72
|
+
function rewriteLifecyclemodelReferences(value, from, to, pathPrefix, change) {
|
|
73
|
+
if (Array.isArray(value)) {
|
|
74
|
+
value.forEach((item, index) => rewriteLifecyclemodelReferences(item, from, to, joinPath(pathPrefix, index), change));
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (!isRecord(value)) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (value['@flowUUID'] === from.id && value['@flowUUID'] !== to.id) {
|
|
81
|
+
change(joinPath(pathPrefix, '@flowUUID'), '@flowUUID', value['@flowUUID'], to.id);
|
|
82
|
+
value['@flowUUID'] = to.id;
|
|
83
|
+
}
|
|
84
|
+
Object.entries(value).forEach(([key, child]) => rewriteLifecyclemodelReferences(child, from, to, joinPath(pathPrefix, key), change));
|
|
85
|
+
}
|
|
86
|
+
function buildFiles(outDir) {
|
|
87
|
+
return {
|
|
88
|
+
patched_rows: path.join(outDir, 'outputs', 'patched-rows.jsonl'),
|
|
89
|
+
rewrite_plan: path.join(outDir, 'outputs', 'rewrite-plan.json'),
|
|
90
|
+
summary: path.join(outDir, 'outputs', 'summary.json'),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
export async function runDatasetReferencesRewrite(options) {
|
|
94
|
+
if (!options.outDir) {
|
|
95
|
+
throw new CliError('Missing required --out-dir value.', {
|
|
96
|
+
code: 'DATASET_REFERENCES_REWRITE_OUT_DIR_REQUIRED',
|
|
97
|
+
exitCode: 2,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
const from = parseReference(options.from, '--from');
|
|
101
|
+
const to = parseReference(options.to, '--to');
|
|
102
|
+
const types = normalizeTypes(options.types);
|
|
103
|
+
const rows = materializeDatasetRows(options.inputPath, options.rawInput);
|
|
104
|
+
const outDir = path.resolve(options.outDir);
|
|
105
|
+
const files = buildFiles(outDir);
|
|
106
|
+
const changes = [];
|
|
107
|
+
const patchedRows = [];
|
|
108
|
+
const processRows = [];
|
|
109
|
+
const lifecyclemodelRows = [];
|
|
110
|
+
rows.forEach((row) => {
|
|
111
|
+
const patched = cloneJson(row.row);
|
|
112
|
+
if (row.kind && types.includes(row.kind)) {
|
|
113
|
+
const payload = unwrapDatasetPayload(patched);
|
|
114
|
+
const addChange = (pathValue, field, before, after) => {
|
|
115
|
+
changes.push({
|
|
116
|
+
row_index: row.index,
|
|
117
|
+
dataset_type: row.kind,
|
|
118
|
+
dataset_id: row.id,
|
|
119
|
+
dataset_version: row.version,
|
|
120
|
+
path: pathValue,
|
|
121
|
+
field,
|
|
122
|
+
before,
|
|
123
|
+
after,
|
|
124
|
+
});
|
|
125
|
+
};
|
|
126
|
+
const beforeCount = changes.length;
|
|
127
|
+
if (row.kind === 'process') {
|
|
128
|
+
rewriteProcessReferences(payload, from, to, '', addChange);
|
|
129
|
+
}
|
|
130
|
+
else if (row.kind === 'lifecyclemodel') {
|
|
131
|
+
rewriteLifecyclemodelReferences(payload, from, to, '', addChange);
|
|
132
|
+
}
|
|
133
|
+
if (changes.length > beforeCount) {
|
|
134
|
+
if (row.kind === 'process') {
|
|
135
|
+
processRows.push(patched);
|
|
136
|
+
}
|
|
137
|
+
else if (row.kind === 'lifecyclemodel') {
|
|
138
|
+
lifecyclemodelRows.push(patched);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
patchedRows.push(patched);
|
|
143
|
+
});
|
|
144
|
+
writeJsonLinesArtifact(files.patched_rows, patchedRows);
|
|
145
|
+
writeJsonArtifact(files.rewrite_plan, {
|
|
146
|
+
from,
|
|
147
|
+
to,
|
|
148
|
+
filters: {
|
|
149
|
+
types,
|
|
150
|
+
scope: firstNonEmpty(options.scope),
|
|
151
|
+
},
|
|
152
|
+
changes,
|
|
153
|
+
});
|
|
154
|
+
const commit = options.commit === true;
|
|
155
|
+
const processSaveDraftImpl = options.runProcessSaveDraftImpl ?? runProcessSaveDraft;
|
|
156
|
+
const lifecyclemodelSaveDraftImpl = options.runLifecyclemodelSaveDraftImpl ?? runLifecyclemodelSaveDraft;
|
|
157
|
+
const processReport = commit && processRows.length > 0
|
|
158
|
+
? await processSaveDraftImpl({
|
|
159
|
+
inputPath: files.patched_rows,
|
|
160
|
+
rawInput: processRows,
|
|
161
|
+
outDir: path.join(outDir, 'process-save-draft'),
|
|
162
|
+
commit: true,
|
|
163
|
+
env: options.env,
|
|
164
|
+
fetchImpl: options.fetchImpl,
|
|
165
|
+
})
|
|
166
|
+
: null;
|
|
167
|
+
const lifecyclemodelReport = commit && lifecyclemodelRows.length > 0
|
|
168
|
+
? await lifecyclemodelSaveDraftImpl({
|
|
169
|
+
inputPath: files.patched_rows,
|
|
170
|
+
rawInput: lifecyclemodelRows,
|
|
171
|
+
outDir: path.join(outDir, 'lifecyclemodel-save-draft'),
|
|
172
|
+
commit: true,
|
|
173
|
+
env: options.env,
|
|
174
|
+
fetchImpl: options.fetchImpl,
|
|
175
|
+
})
|
|
176
|
+
: null;
|
|
177
|
+
const commitFailed = processReport?.status === 'completed_with_failures' ||
|
|
178
|
+
lifecyclemodelReport?.status === 'completed_with_failures';
|
|
179
|
+
const report = {
|
|
180
|
+
generated_at_utc: (options.now ?? new Date()).toISOString(),
|
|
181
|
+
input_path: path.resolve(options.inputPath),
|
|
182
|
+
out_dir: outDir,
|
|
183
|
+
mode: commit ? 'commit' : 'dry_run',
|
|
184
|
+
status: commitFailed ? 'completed_with_failures' : 'completed',
|
|
185
|
+
from,
|
|
186
|
+
to,
|
|
187
|
+
filters: {
|
|
188
|
+
types,
|
|
189
|
+
scope: firstNonEmpty(options.scope),
|
|
190
|
+
},
|
|
191
|
+
counts: {
|
|
192
|
+
input_rows: rows.length,
|
|
193
|
+
patched_rows: processRows.length + lifecyclemodelRows.length,
|
|
194
|
+
changes: changes.length,
|
|
195
|
+
process_rows: processRows.length,
|
|
196
|
+
lifecyclemodel_rows: lifecyclemodelRows.length,
|
|
197
|
+
},
|
|
198
|
+
files,
|
|
199
|
+
changes,
|
|
200
|
+
commit_reports: {
|
|
201
|
+
processes: processReport,
|
|
202
|
+
lifecyclemodels: lifecyclemodelReport,
|
|
203
|
+
},
|
|
204
|
+
};
|
|
205
|
+
writeJsonArtifact(files.summary, report);
|
|
206
|
+
return report;
|
|
207
|
+
}
|
|
208
|
+
export const __testInternals = {
|
|
209
|
+
normalizeTypes,
|
|
210
|
+
parseReference,
|
|
211
|
+
rewriteLifecyclemodelReferences,
|
|
212
|
+
rewriteProcessReferences,
|
|
213
|
+
};
|
|
214
|
+
//# sourceMappingURL=dataset-references-rewrite.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dataset-references-rewrite.js","sourceRoot":"","sources":["../../../src/lib/dataset-references-rewrite.ts"],"names":[],"mappings":"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","sourcesContent":["import path from 'node:path';\nimport { writeJsonArtifact, writeJsonLinesArtifact } from './artifacts.js';\nimport { CliError } from './errors.js';\nimport type { FetchLike } from './http.js';\nimport {\n cloneJson,\n firstNonEmpty,\n isRecord,\n materializeDatasetRows,\n unwrapDatasetPayload,\n type DatasetKind,\n type JsonObject,\n} from './dataset-local.js';\nimport {\n runLifecyclemodelSaveDraft,\n type LifecyclemodelSaveDraftReport,\n} from './lifecyclemodel-save-draft-run.js';\nimport { runProcessSaveDraft, type ProcessSaveDraftReport } from './process-save-draft-run.js';\n\ntype ReferenceKind = 'flow';\n\ntype ParsedReference = {\n kind: ReferenceKind;\n id: string;\n version: string | null;\n};\n\nexport type DatasetReferencesRewriteChange = {\n row_index: number;\n dataset_type: DatasetKind | null;\n dataset_id: string | null;\n dataset_version: string | null;\n path: string;\n field: '@refObjectId' | '@version' | '@flowUUID';\n before: string | null;\n after: string | null;\n};\n\nexport type DatasetReferencesRewriteReport = {\n generated_at_utc: string;\n input_path: string;\n out_dir: string;\n mode: 'dry_run' | 'commit';\n status: 'completed' | 'completed_with_failures';\n from: ParsedReference;\n to: ParsedReference;\n filters: {\n types: DatasetKind[];\n scope: string | null;\n };\n counts: {\n input_rows: number;\n patched_rows: number;\n changes: number;\n process_rows: number;\n lifecyclemodel_rows: number;\n };\n files: {\n patched_rows: string;\n rewrite_plan: string;\n summary: string;\n };\n changes: DatasetReferencesRewriteChange[];\n commit_reports: {\n processes: ProcessSaveDraftReport | null;\n lifecyclemodels: LifecyclemodelSaveDraftReport | null;\n };\n};\n\nexport type RunDatasetReferencesRewriteOptions = {\n inputPath: string;\n outDir: string;\n from: string;\n to: string;\n types?: string[] | null;\n scope?: string | null;\n commit?: boolean | null;\n rawInput?: unknown;\n env?: NodeJS.ProcessEnv;\n fetchImpl?: FetchLike;\n now?: Date;\n runProcessSaveDraftImpl?: typeof runProcessSaveDraft;\n runLifecyclemodelSaveDraftImpl?: typeof runLifecyclemodelSaveDraft;\n};\n\nfunction parseReference(value: string, label: string): ParsedReference {\n const match = /^(flow):([^@\\s]+)(?:@([^@\\s]+))?$/u.exec(value.trim());\n if (!match) {\n throw new CliError(`Expected ${label} reference like flow:<id>@<version>.`, {\n code: 'DATASET_REFERENCE_INVALID',\n exitCode: 2,\n details: value,\n });\n }\n return {\n kind: 'flow',\n id: match[2],\n version: match[3] ?? null,\n };\n}\n\nfunction normalizeTypes(values: string[] | null | undefined): DatasetKind[] {\n const raw = values && values.length > 0 ? values.flatMap((value) => value.split(',')) : [];\n const normalized = raw\n .map((value) => value.trim().toLowerCase())\n .filter(Boolean)\n .map((value) => {\n if (value === 'process' || value === 'processes') {\n return 'process' as const;\n }\n if (\n value === 'lifecyclemodel' ||\n value === 'lifecyclemodels' ||\n value === 'model' ||\n value === 'models'\n ) {\n return 'lifecyclemodel' as const;\n }\n throw new CliError('Expected --type or --types to contain process and/or lifecyclemodel.', {\n code: 'DATASET_REFERENCE_TYPES_INVALID',\n exitCode: 2,\n details: value,\n });\n });\n return normalized.length > 0 ? [...new Set(normalized)] : ['process', 'lifecyclemodel'];\n}\n\nfunction joinPath(base: string, key: string | number): string {\n return base ? `${base}.${String(key)}` : String(key);\n}\n\nfunction rewriteProcessReferences(\n value: unknown,\n from: ParsedReference,\n to: ParsedReference,\n pathPrefix: string,\n change: (\n path: string,\n field: '@refObjectId' | '@version',\n before: string | null,\n after: string | null,\n ) => void,\n): void {\n if (Array.isArray(value)) {\n value.forEach((item, index) =>\n rewriteProcessReferences(item, from, to, joinPath(pathPrefix, index), change),\n );\n return;\n }\n if (!isRecord(value)) {\n return;\n }\n\n const reference = value.referenceToFlowDataSet;\n if (isRecord(reference) && reference['@refObjectId'] === from.id) {\n const versionMatches = !from.version || reference['@version'] === from.version;\n if (versionMatches) {\n if (reference['@refObjectId'] !== to.id) {\n change(\n joinPath(pathPrefix, 'referenceToFlowDataSet.@refObjectId'),\n '@refObjectId',\n reference['@refObjectId'],\n to.id,\n );\n reference['@refObjectId'] = to.id;\n }\n if (to.version && reference['@version'] !== to.version) {\n change(\n joinPath(pathPrefix, 'referenceToFlowDataSet.@version'),\n '@version',\n typeof reference['@version'] === 'string' ? reference['@version'] : null,\n to.version,\n );\n reference['@version'] = to.version;\n }\n }\n }\n\n Object.entries(value).forEach(([key, child]) =>\n rewriteProcessReferences(child, from, to, joinPath(pathPrefix, key), change),\n );\n}\n\nfunction rewriteLifecyclemodelReferences(\n value: unknown,\n from: ParsedReference,\n to: ParsedReference,\n pathPrefix: string,\n change: (path: string, field: '@flowUUID', before: string | null, after: string | null) => void,\n): void {\n if (Array.isArray(value)) {\n value.forEach((item, index) =>\n rewriteLifecyclemodelReferences(item, from, to, joinPath(pathPrefix, index), change),\n );\n return;\n }\n if (!isRecord(value)) {\n return;\n }\n\n if (value['@flowUUID'] === from.id && value['@flowUUID'] !== to.id) {\n change(joinPath(pathPrefix, '@flowUUID'), '@flowUUID', value['@flowUUID'], to.id);\n value['@flowUUID'] = to.id;\n }\n\n Object.entries(value).forEach(([key, child]) =>\n rewriteLifecyclemodelReferences(child, from, to, joinPath(pathPrefix, key), change),\n );\n}\n\nfunction buildFiles(outDir: string): DatasetReferencesRewriteReport['files'] {\n return {\n patched_rows: path.join(outDir, 'outputs', 'patched-rows.jsonl'),\n rewrite_plan: path.join(outDir, 'outputs', 'rewrite-plan.json'),\n summary: path.join(outDir, 'outputs', 'summary.json'),\n };\n}\n\nexport async function runDatasetReferencesRewrite(\n options: RunDatasetReferencesRewriteOptions,\n): Promise<DatasetReferencesRewriteReport> {\n if (!options.outDir) {\n throw new CliError('Missing required --out-dir value.', {\n code: 'DATASET_REFERENCES_REWRITE_OUT_DIR_REQUIRED',\n exitCode: 2,\n });\n }\n\n const from = parseReference(options.from, '--from');\n const to = parseReference(options.to, '--to');\n\n const types = normalizeTypes(options.types);\n const rows = materializeDatasetRows(options.inputPath, options.rawInput);\n const outDir = path.resolve(options.outDir);\n const files = buildFiles(outDir);\n const changes: DatasetReferencesRewriteChange[] = [];\n const patchedRows: JsonObject[] = [];\n const processRows: JsonObject[] = [];\n const lifecyclemodelRows: JsonObject[] = [];\n\n rows.forEach((row) => {\n const patched = cloneJson(row.row);\n if (row.kind && types.includes(row.kind)) {\n const payload = unwrapDatasetPayload(patched);\n const addChange = (\n pathValue: string,\n field: '@refObjectId' | '@version' | '@flowUUID',\n before: string | null,\n after: string | null,\n ): void => {\n changes.push({\n row_index: row.index,\n dataset_type: row.kind,\n dataset_id: row.id,\n dataset_version: row.version,\n path: pathValue,\n field,\n before,\n after,\n });\n };\n const beforeCount = changes.length;\n if (row.kind === 'process') {\n rewriteProcessReferences(payload, from, to, '', addChange);\n } else if (row.kind === 'lifecyclemodel') {\n rewriteLifecyclemodelReferences(payload, from, to, '', addChange);\n }\n if (changes.length > beforeCount) {\n if (row.kind === 'process') {\n processRows.push(patched);\n } else if (row.kind === 'lifecyclemodel') {\n lifecyclemodelRows.push(patched);\n }\n }\n }\n patchedRows.push(patched);\n });\n\n writeJsonLinesArtifact(files.patched_rows, patchedRows);\n writeJsonArtifact(files.rewrite_plan, {\n from,\n to,\n filters: {\n types,\n scope: firstNonEmpty(options.scope),\n },\n changes,\n });\n\n const commit = options.commit === true;\n const processSaveDraftImpl = options.runProcessSaveDraftImpl ?? runProcessSaveDraft;\n const lifecyclemodelSaveDraftImpl =\n options.runLifecyclemodelSaveDraftImpl ?? runLifecyclemodelSaveDraft;\n const processReport =\n commit && processRows.length > 0\n ? await processSaveDraftImpl({\n inputPath: files.patched_rows,\n rawInput: processRows,\n outDir: path.join(outDir, 'process-save-draft'),\n commit: true,\n env: options.env,\n fetchImpl: options.fetchImpl,\n })\n : null;\n const lifecyclemodelReport =\n commit && lifecyclemodelRows.length > 0\n ? await lifecyclemodelSaveDraftImpl({\n inputPath: files.patched_rows,\n rawInput: lifecyclemodelRows,\n outDir: path.join(outDir, 'lifecyclemodel-save-draft'),\n commit: true,\n env: options.env,\n fetchImpl: options.fetchImpl,\n })\n : null;\n\n const commitFailed =\n processReport?.status === 'completed_with_failures' ||\n lifecyclemodelReport?.status === 'completed_with_failures';\n const report: DatasetReferencesRewriteReport = {\n generated_at_utc: (options.now ?? new Date()).toISOString(),\n input_path: path.resolve(options.inputPath),\n out_dir: outDir,\n mode: commit ? 'commit' : 'dry_run',\n status: commitFailed ? 'completed_with_failures' : 'completed',\n from,\n to,\n filters: {\n types,\n scope: firstNonEmpty(options.scope),\n },\n counts: {\n input_rows: rows.length,\n patched_rows: processRows.length + lifecyclemodelRows.length,\n changes: changes.length,\n process_rows: processRows.length,\n lifecyclemodel_rows: lifecyclemodelRows.length,\n },\n files,\n changes,\n commit_reports: {\n processes: processReport,\n lifecyclemodels: lifecyclemodelReport,\n },\n };\n writeJsonArtifact(files.summary, report);\n return report;\n}\n\nexport const __testInternals = {\n normalizeTypes,\n parseReference,\n rewriteLifecyclemodelReferences,\n rewriteProcessReferences,\n};\n"]}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { writeJsonArtifact, writeJsonLinesArtifact } from './artifacts.js';
|
|
4
|
+
import { CliError } from './errors.js';
|
|
5
|
+
import { cloneJson, isRecord, readDatasetRowsInput, unwrapDatasetPayload, } from './dataset-local.js';
|
|
6
|
+
import { runDatasetRemoteVerify, } from './dataset-remote-verify.js';
|
|
7
|
+
function requireNonEmpty(value, label, code) {
|
|
8
|
+
const normalized = value.trim();
|
|
9
|
+
if (!normalized) {
|
|
10
|
+
throw new CliError(`Missing required ${label} value.`, {
|
|
11
|
+
code,
|
|
12
|
+
exitCode: 2,
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
return normalized;
|
|
16
|
+
}
|
|
17
|
+
function nowIso(now = new Date()) {
|
|
18
|
+
return now.toISOString();
|
|
19
|
+
}
|
|
20
|
+
function readJsonl(filePath) {
|
|
21
|
+
return readFileSync(filePath, 'utf8')
|
|
22
|
+
.split(/\r?\n/u)
|
|
23
|
+
.map((line) => line.trim())
|
|
24
|
+
.filter(Boolean)
|
|
25
|
+
.map((line) => JSON.parse(line))
|
|
26
|
+
.filter(isRecord);
|
|
27
|
+
}
|
|
28
|
+
function decodePointerSegment(segment) {
|
|
29
|
+
return segment.replace(/~1/gu, '/').replace(/~0/gu, '~');
|
|
30
|
+
}
|
|
31
|
+
function pointerSegments(pointerPath) {
|
|
32
|
+
return pointerPath
|
|
33
|
+
.split('/')
|
|
34
|
+
.filter(Boolean)
|
|
35
|
+
.map((segment) => decodePointerSegment(segment));
|
|
36
|
+
}
|
|
37
|
+
function valueAtPointer(root, pointerPath) {
|
|
38
|
+
let current = root;
|
|
39
|
+
for (const segment of pointerSegments(pointerPath)) {
|
|
40
|
+
if (Array.isArray(current)) {
|
|
41
|
+
const index = Number(segment);
|
|
42
|
+
current = Number.isInteger(index) ? current[index] : undefined;
|
|
43
|
+
}
|
|
44
|
+
else if (isRecord(current)) {
|
|
45
|
+
current = current[segment];
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return current;
|
|
52
|
+
}
|
|
53
|
+
function referenceNodeForPatch(payload, check) {
|
|
54
|
+
if (check.path.endsWith('/@flowUUID')) {
|
|
55
|
+
const parentPath = check.path.slice(0, -'/@flowUUID'.length);
|
|
56
|
+
const parent = valueAtPointer(payload, parentPath);
|
|
57
|
+
return isRecord(parent) ? parent : null;
|
|
58
|
+
}
|
|
59
|
+
const node = valueAtPointer(payload, check.path);
|
|
60
|
+
return isRecord(node) ? node : null;
|
|
61
|
+
}
|
|
62
|
+
function refreshableCheck(check) {
|
|
63
|
+
return (check.role === 'reference' &&
|
|
64
|
+
Boolean(check.latest_version) &&
|
|
65
|
+
['missing_version', 'version_missing', 'version_outdated'].includes(check.status));
|
|
66
|
+
}
|
|
67
|
+
function applyRemoteRefreshPatches(rows, checks) {
|
|
68
|
+
const patchedRows = cloneJson(rows);
|
|
69
|
+
const patches = [];
|
|
70
|
+
for (const check of checks.filter(refreshableCheck)) {
|
|
71
|
+
const row = patchedRows[check.row_index];
|
|
72
|
+
if (!row || !check.latest_version) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const payload = unwrapDatasetPayload(row);
|
|
76
|
+
const node = referenceNodeForPatch(payload, check);
|
|
77
|
+
if (!node) {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
const before = typeof node['@version'] === 'string' ? node['@version'] : null;
|
|
81
|
+
if (before === check.latest_version) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
node['@version'] = check.latest_version;
|
|
85
|
+
patches.push({
|
|
86
|
+
row_index: check.row_index,
|
|
87
|
+
role: check.role,
|
|
88
|
+
table: check.table,
|
|
89
|
+
id: check.id,
|
|
90
|
+
from_version: before,
|
|
91
|
+
to_version: check.latest_version,
|
|
92
|
+
path: check.path,
|
|
93
|
+
status_before: check.status,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
return { rows: patchedRows, patches };
|
|
97
|
+
}
|
|
98
|
+
function buildFiles(outPath, outDir) {
|
|
99
|
+
const resolved = path.resolve(outDir);
|
|
100
|
+
return {
|
|
101
|
+
output_rows: path.resolve(outPath),
|
|
102
|
+
report: path.join(resolved, 'outputs', 'remote-refresh-report.json'),
|
|
103
|
+
patches: path.join(resolved, 'outputs', 'remote-refresh-patches.jsonl'),
|
|
104
|
+
pre_verification_report: path.join(resolved, 'pre-refresh-verify', 'outputs', 'remote-verification-report.json'),
|
|
105
|
+
post_verification_report: path.join(resolved, 'post-refresh-verify', 'outputs', 'remote-verification-report.json'),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
export async function runDatasetRemoteRefresh(options) {
|
|
109
|
+
const inputPath = path.resolve(requireNonEmpty(options.inputPath, '--input', 'DATASET_REMOTE_REFRESH_INPUT_REQUIRED'));
|
|
110
|
+
const outPath = path.resolve(requireNonEmpty(options.outPath, '--out', 'DATASET_REMOTE_REFRESH_OUT_REQUIRED'));
|
|
111
|
+
const outDir = path.resolve(requireNonEmpty(options.outDir, '--out-dir', 'DATASET_REMOTE_REFRESH_OUT_DIR_REQUIRED'));
|
|
112
|
+
const runVerify = options.runDatasetRemoteVerifyImpl ?? runDatasetRemoteVerify;
|
|
113
|
+
const rows = readDatasetRowsInput(inputPath, options.rawInput);
|
|
114
|
+
const files = buildFiles(outPath, outDir);
|
|
115
|
+
const preReport = await runVerify({
|
|
116
|
+
inputPath,
|
|
117
|
+
outDir: path.join(outDir, 'pre-refresh-verify'),
|
|
118
|
+
rawInput: rows,
|
|
119
|
+
env: options.env,
|
|
120
|
+
fetchImpl: options.fetchImpl,
|
|
121
|
+
timeoutMs: options.timeoutMs,
|
|
122
|
+
now: options.now,
|
|
123
|
+
rootPolicy: options.rootPolicy,
|
|
124
|
+
});
|
|
125
|
+
const checks = readJsonl(path.join(outDir, 'pre-refresh-verify', 'outputs', 'remote-verification.jsonl'));
|
|
126
|
+
const { rows: patchedRows, patches } = applyRemoteRefreshPatches(rows, checks);
|
|
127
|
+
writeJsonLinesArtifact(outPath, patchedRows);
|
|
128
|
+
writeJsonLinesArtifact(files.patches, patches);
|
|
129
|
+
const postReport = await runVerify({
|
|
130
|
+
inputPath: outPath,
|
|
131
|
+
outDir: path.join(outDir, 'post-refresh-verify'),
|
|
132
|
+
rawInput: patchedRows,
|
|
133
|
+
env: options.env,
|
|
134
|
+
fetchImpl: options.fetchImpl,
|
|
135
|
+
timeoutMs: options.timeoutMs,
|
|
136
|
+
now: options.now,
|
|
137
|
+
rootPolicy: options.rootPolicy,
|
|
138
|
+
});
|
|
139
|
+
const report = {
|
|
140
|
+
schema_version: 1,
|
|
141
|
+
generated_at_utc: nowIso(options.now),
|
|
142
|
+
status: postReport.blockers.length > 0 ? 'completed_with_blockers' : 'completed',
|
|
143
|
+
root_policy: options.rootPolicy ?? 'existing',
|
|
144
|
+
input_path: inputPath,
|
|
145
|
+
out_path: outPath,
|
|
146
|
+
out_dir: outDir,
|
|
147
|
+
counts: {
|
|
148
|
+
rows: rows.length,
|
|
149
|
+
pre_refresh_blockers: preReport.blockers.length,
|
|
150
|
+
refreshable_references: checks.filter(refreshableCheck).length,
|
|
151
|
+
patched_references: patches.length,
|
|
152
|
+
post_refresh_blockers: postReport.blockers.length,
|
|
153
|
+
},
|
|
154
|
+
remaining_blockers: postReport.blockers,
|
|
155
|
+
files,
|
|
156
|
+
};
|
|
157
|
+
writeJsonArtifact(files.report, report);
|
|
158
|
+
return report;
|
|
159
|
+
}
|
|
160
|
+
export const __testInternals = {
|
|
161
|
+
applyRemoteRefreshPatches,
|
|
162
|
+
pointerSegments,
|
|
163
|
+
refreshableCheck,
|
|
164
|
+
valueAtPointer,
|
|
165
|
+
};
|
|
166
|
+
//# sourceMappingURL=dataset-remote-refresh.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dataset-remote-refresh.js","sourceRoot":"","sources":["../../../src/lib/dataset-remote-refresh.ts"],"names":[],"mappings":"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","sourcesContent":["import path from 'node:path';\nimport { readFileSync } from 'node:fs';\nimport { writeJsonArtifact, writeJsonLinesArtifact } from './artifacts.js';\nimport { CliError } from './errors.js';\nimport type { FetchLike } from './http.js';\nimport {\n cloneJson,\n isRecord,\n readDatasetRowsInput,\n unwrapDatasetPayload,\n type JsonObject,\n} from './dataset-local.js';\nimport {\n runDatasetRemoteVerify,\n type DatasetRemoteVerificationReport,\n type RemoteVerificationCheck,\n type RunDatasetRemoteVerifyOptions,\n} from './dataset-remote-verify.js';\n\nexport type DatasetRemoteRefreshPatch = {\n row_index: number;\n role: string;\n table: string | null;\n id: string | null;\n from_version: string | null;\n to_version: string;\n path: string;\n status_before: string;\n};\n\nexport type DatasetRemoteRefreshReport = {\n schema_version: 1;\n generated_at_utc: string;\n status: 'completed' | 'completed_with_blockers';\n root_policy: NonNullable<RunDatasetRemoteVerifyOptions['rootPolicy']>;\n input_path: string;\n out_path: string;\n out_dir: string;\n counts: {\n rows: number;\n pre_refresh_blockers: number;\n refreshable_references: number;\n patched_references: number;\n post_refresh_blockers: number;\n };\n remaining_blockers: DatasetRemoteVerificationReport['blockers'];\n files: {\n output_rows: string;\n report: string;\n patches: string;\n pre_verification_report: string;\n post_verification_report: string;\n };\n};\n\nexport type RunDatasetRemoteRefreshOptions = {\n inputPath: string;\n outPath: string;\n outDir: string;\n rawInput?: unknown;\n env?: NodeJS.ProcessEnv;\n fetchImpl?: FetchLike;\n timeoutMs?: number;\n now?: Date;\n rootPolicy?: RunDatasetRemoteVerifyOptions['rootPolicy'];\n runDatasetRemoteVerifyImpl?: (\n options: RunDatasetRemoteVerifyOptions,\n ) => Promise<DatasetRemoteVerificationReport>;\n};\n\nfunction requireNonEmpty(value: string, label: string, code: string): string {\n const normalized = value.trim();\n if (!normalized) {\n throw new CliError(`Missing required ${label} value.`, {\n code,\n exitCode: 2,\n });\n }\n return normalized;\n}\n\nfunction nowIso(now: Date = new Date()): string {\n return now.toISOString();\n}\n\nfunction readJsonl(filePath: string): JsonObject[] {\n return readFileSync(filePath, 'utf8')\n .split(/\\r?\\n/u)\n .map((line) => line.trim())\n .filter(Boolean)\n .map((line) => JSON.parse(line) as unknown)\n .filter(isRecord);\n}\n\nfunction decodePointerSegment(segment: string): string {\n return segment.replace(/~1/gu, '/').replace(/~0/gu, '~');\n}\n\nfunction pointerSegments(pointerPath: string): string[] {\n return pointerPath\n .split('/')\n .filter(Boolean)\n .map((segment) => decodePointerSegment(segment));\n}\n\nfunction valueAtPointer(root: unknown, pointerPath: string): unknown {\n let current = root;\n for (const segment of pointerSegments(pointerPath)) {\n if (Array.isArray(current)) {\n const index = Number(segment);\n current = Number.isInteger(index) ? current[index] : undefined;\n } else if (isRecord(current)) {\n current = current[segment];\n } else {\n return undefined;\n }\n }\n return current;\n}\n\nfunction referenceNodeForPatch(\n payload: JsonObject,\n check: RemoteVerificationCheck,\n): JsonObject | null {\n if (check.path.endsWith('/@flowUUID')) {\n const parentPath = check.path.slice(0, -'/@flowUUID'.length);\n const parent = valueAtPointer(payload, parentPath);\n return isRecord(parent) ? parent : null;\n }\n const node = valueAtPointer(payload, check.path);\n return isRecord(node) ? node : null;\n}\n\nfunction refreshableCheck(check: RemoteVerificationCheck): boolean {\n return (\n check.role === 'reference' &&\n Boolean(check.latest_version) &&\n ['missing_version', 'version_missing', 'version_outdated'].includes(check.status)\n );\n}\n\nfunction applyRemoteRefreshPatches(\n rows: JsonObject[],\n checks: RemoteVerificationCheck[],\n): { rows: JsonObject[]; patches: DatasetRemoteRefreshPatch[] } {\n const patchedRows = cloneJson(rows);\n const patches: DatasetRemoteRefreshPatch[] = [];\n\n for (const check of checks.filter(refreshableCheck)) {\n const row = patchedRows[check.row_index];\n if (!row || !check.latest_version) {\n continue;\n }\n const payload = unwrapDatasetPayload(row);\n const node = referenceNodeForPatch(payload, check);\n if (!node) {\n continue;\n }\n const before = typeof node['@version'] === 'string' ? node['@version'] : null;\n if (before === check.latest_version) {\n continue;\n }\n node['@version'] = check.latest_version;\n patches.push({\n row_index: check.row_index,\n role: check.role,\n table: check.table,\n id: check.id,\n from_version: before,\n to_version: check.latest_version,\n path: check.path,\n status_before: check.status,\n });\n }\n\n return { rows: patchedRows, patches };\n}\n\nfunction buildFiles(outPath: string, outDir: string): DatasetRemoteRefreshReport['files'] {\n const resolved = path.resolve(outDir);\n return {\n output_rows: path.resolve(outPath),\n report: path.join(resolved, 'outputs', 'remote-refresh-report.json'),\n patches: path.join(resolved, 'outputs', 'remote-refresh-patches.jsonl'),\n pre_verification_report: path.join(\n resolved,\n 'pre-refresh-verify',\n 'outputs',\n 'remote-verification-report.json',\n ),\n post_verification_report: path.join(\n resolved,\n 'post-refresh-verify',\n 'outputs',\n 'remote-verification-report.json',\n ),\n };\n}\n\nexport async function runDatasetRemoteRefresh(\n options: RunDatasetRemoteRefreshOptions,\n): Promise<DatasetRemoteRefreshReport> {\n const inputPath = path.resolve(\n requireNonEmpty(options.inputPath, '--input', 'DATASET_REMOTE_REFRESH_INPUT_REQUIRED'),\n );\n const outPath = path.resolve(\n requireNonEmpty(options.outPath, '--out', 'DATASET_REMOTE_REFRESH_OUT_REQUIRED'),\n );\n const outDir = path.resolve(\n requireNonEmpty(options.outDir, '--out-dir', 'DATASET_REMOTE_REFRESH_OUT_DIR_REQUIRED'),\n );\n const runVerify = options.runDatasetRemoteVerifyImpl ?? runDatasetRemoteVerify;\n const rows = readDatasetRowsInput(inputPath, options.rawInput);\n const files = buildFiles(outPath, outDir);\n\n const preReport = await runVerify({\n inputPath,\n outDir: path.join(outDir, 'pre-refresh-verify'),\n rawInput: rows,\n env: options.env,\n fetchImpl: options.fetchImpl,\n timeoutMs: options.timeoutMs,\n now: options.now,\n rootPolicy: options.rootPolicy,\n });\n const checks = readJsonl(\n path.join(outDir, 'pre-refresh-verify', 'outputs', 'remote-verification.jsonl'),\n ) as RemoteVerificationCheck[];\n const { rows: patchedRows, patches } = applyRemoteRefreshPatches(rows, checks);\n writeJsonLinesArtifact(outPath, patchedRows);\n writeJsonLinesArtifact(files.patches, patches);\n\n const postReport = await runVerify({\n inputPath: outPath,\n outDir: path.join(outDir, 'post-refresh-verify'),\n rawInput: patchedRows,\n env: options.env,\n fetchImpl: options.fetchImpl,\n timeoutMs: options.timeoutMs,\n now: options.now,\n rootPolicy: options.rootPolicy,\n });\n\n const report: DatasetRemoteRefreshReport = {\n schema_version: 1,\n generated_at_utc: nowIso(options.now),\n status: postReport.blockers.length > 0 ? 'completed_with_blockers' : 'completed',\n root_policy: options.rootPolicy ?? 'existing',\n input_path: inputPath,\n out_path: outPath,\n out_dir: outDir,\n counts: {\n rows: rows.length,\n pre_refresh_blockers: preReport.blockers.length,\n refreshable_references: checks.filter(refreshableCheck).length,\n patched_references: patches.length,\n post_refresh_blockers: postReport.blockers.length,\n },\n remaining_blockers: postReport.blockers,\n files,\n };\n writeJsonArtifact(files.report, report);\n\n return report;\n}\n\nexport const __testInternals = {\n applyRemoteRefreshPatches,\n pointerSegments,\n refreshableCheck,\n valueAtPointer,\n};\n"]}
|