@tiangong-lca/cli 0.0.32 → 0.1.0

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.
Files changed (101) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +33 -12
  3. package/assets/import-smoke/simapro.csv +20 -0
  4. package/bin/tiangong-lca.js +1 -1
  5. package/dist/src/cli.js +103 -65
  6. package/dist/src/cli.js.map +1 -1
  7. package/dist/src/lib/dataset-bilingual.js.map +1 -1
  8. package/dist/src/lib/dataset-classification.js.map +1 -1
  9. package/dist/src/lib/dataset-contract.js.map +1 -1
  10. package/dist/src/lib/dataset-evidence-search.js.map +1 -1
  11. package/dist/src/lib/dataset-import-lca.js +302 -131
  12. package/dist/src/lib/dataset-import-lca.js.map +1 -1
  13. package/dist/src/lib/dataset-maintenance-apply.js +916 -43
  14. package/dist/src/lib/dataset-maintenance-apply.js.map +1 -1
  15. package/dist/src/lib/dataset-maintenance-clear-account.js +10 -4
  16. package/dist/src/lib/dataset-maintenance-clear-account.js.map +1 -1
  17. package/dist/src/lib/dataset-maintenance-contract.js +1 -6
  18. package/dist/src/lib/dataset-maintenance-contract.js.map +1 -1
  19. package/dist/src/lib/dataset-maintenance-derivatives.js +0 -1
  20. package/dist/src/lib/dataset-maintenance-derivatives.js.map +1 -1
  21. package/dist/src/lib/dataset-maintenance-flow-identity-capture.js +1 -0
  22. package/dist/src/lib/dataset-maintenance-flow-identity-capture.js.map +1 -1
  23. package/dist/src/lib/dataset-maintenance-pagination.js.map +1 -1
  24. package/dist/src/lib/dataset-maintenance-protected-before.js.map +1 -1
  25. package/dist/src/lib/dataset-maintenance-protected-contract.js +0 -1
  26. package/dist/src/lib/dataset-maintenance-protected-contract.js.map +1 -1
  27. package/dist/src/lib/dataset-maintenance-protected-freeze.js.map +1 -1
  28. package/dist/src/lib/dataset-maintenance-protected-preparation.js +0 -2
  29. package/dist/src/lib/dataset-maintenance-protected-preparation.js.map +1 -1
  30. package/dist/src/lib/dataset-maintenance-protected-seal.js.map +1 -1
  31. package/dist/src/lib/dataset-maintenance-protected-verify.js +0 -3
  32. package/dist/src/lib/dataset-maintenance-protected-verify.js.map +1 -1
  33. package/dist/src/lib/dataset-maintenance-remote.js +68 -12
  34. package/dist/src/lib/dataset-maintenance-remote.js.map +1 -1
  35. package/dist/src/lib/dataset-maintenance-verify.js +1 -2
  36. package/dist/src/lib/dataset-maintenance-verify.js.map +1 -1
  37. package/dist/src/lib/dataset-remote-refresh.js.map +1 -1
  38. package/dist/src/lib/dataset-remote-verify.js +2 -0
  39. package/dist/src/lib/dataset-remote-verify.js.map +1 -1
  40. package/dist/src/lib/dataset-save-draft-run.js +2 -0
  41. package/dist/src/lib/dataset-save-draft-run.js.map +1 -1
  42. package/dist/src/lib/flow-build-alias-map.js.map +1 -1
  43. package/dist/src/lib/flow-fetch-rows.js.map +1 -1
  44. package/dist/src/lib/flow-get.js.map +1 -1
  45. package/dist/src/lib/flow-governance.js.map +1 -1
  46. package/dist/src/lib/flow-list.js +1 -0
  47. package/dist/src/lib/flow-list.js.map +1 -1
  48. package/dist/src/lib/flow-payload-validation.js.map +1 -1
  49. package/dist/src/lib/flow-publish-version.js +1 -0
  50. package/dist/src/lib/flow-publish-version.js.map +1 -1
  51. package/dist/src/lib/flow-read.js +1 -0
  52. package/dist/src/lib/flow-read.js.map +1 -1
  53. package/dist/src/lib/flow-regen-product.js.map +1 -1
  54. package/dist/src/lib/flow-remediate.js.map +1 -1
  55. package/dist/src/lib/identity-preflight.js +4 -10
  56. package/dist/src/lib/identity-preflight.js.map +1 -1
  57. package/dist/src/lib/lifecyclemodel-bundle-save.js +1 -0
  58. package/dist/src/lib/lifecyclemodel-bundle-save.js.map +1 -1
  59. package/dist/src/lib/lifecyclemodel-orchestrate.js.map +1 -1
  60. package/dist/src/lib/lifecyclemodel-publish-resulting-process.js.map +1 -1
  61. package/dist/src/lib/lifecyclemodel-resulting-process.js.map +1 -1
  62. package/dist/src/lib/lifecyclemodel-save-draft-run.js.map +1 -1
  63. package/dist/src/lib/lifecyclemodel-validate-build.js.map +1 -1
  64. package/dist/src/lib/package-version.js.map +1 -1
  65. package/dist/src/lib/process-auto-build.js.map +1 -1
  66. package/dist/src/lib/process-dedup-review.js +14 -7
  67. package/dist/src/lib/process-dedup-review.js.map +1 -1
  68. package/dist/src/lib/process-flow-build-plan.js +111 -30
  69. package/dist/src/lib/process-flow-build-plan.js.map +1 -1
  70. package/dist/src/lib/process-get.js +1 -0
  71. package/dist/src/lib/process-get.js.map +1 -1
  72. package/dist/src/lib/process-list.js +1 -0
  73. package/dist/src/lib/process-list.js.map +1 -1
  74. package/dist/src/lib/process-payload-validation.js.map +1 -1
  75. package/dist/src/lib/process-qa.js.map +1 -1
  76. package/dist/src/lib/process-refresh-references.js +15 -10
  77. package/dist/src/lib/process-refresh-references.js.map +1 -1
  78. package/dist/src/lib/process-save-draft.js +9 -3
  79. package/dist/src/lib/process-save-draft.js.map +1 -1
  80. package/dist/src/lib/process-scope-statistics.js +11 -4
  81. package/dist/src/lib/process-scope-statistics.js.map +1 -1
  82. package/dist/src/lib/process-verify-rows.js.map +1 -1
  83. package/dist/src/lib/publish.js.map +1 -1
  84. package/dist/src/lib/run.js.map +1 -1
  85. package/dist/src/lib/supabase-client.js +13 -1
  86. package/dist/src/lib/supabase-client.js.map +1 -1
  87. package/dist/src/lib/supabase-data-api-contract.js +328 -0
  88. package/dist/src/lib/supabase-data-api-contract.js.map +1 -0
  89. package/dist/src/lib/supabase-data-api-replay.js +69 -0
  90. package/dist/src/lib/supabase-data-api-replay.js.map +1 -0
  91. package/dist/src/lib/supabase-json-ordered-write.js +2 -0
  92. package/dist/src/lib/supabase-json-ordered-write.js.map +1 -1
  93. package/dist/src/lib/supabase-rest.js +1 -0
  94. package/dist/src/lib/supabase-rest.js.map +1 -1
  95. package/dist/src/lib/tidas-sdk-package-validator.js +5 -5
  96. package/dist/src/lib/tidas-sdk-package-validator.js.map +1 -1
  97. package/dist/src/lib/validation.js.map +1 -1
  98. package/dist/src/main.js.map +1 -1
  99. package/package.json +40 -40
  100. package/dist/scripts/assert-full-coverage.js +0 -77
  101. package/dist/scripts/assert-full-coverage.js.map +0 -1
@@ -1,111 +1,271 @@
1
1
  import { existsSync, readFileSync } from 'node:fs';
2
2
  import path from 'node:path';
3
- import { fileURLToPath } from 'node:url';
4
3
  import { spawnSync } from 'node:child_process';
5
- import { writeJsonArtifact } from './artifacts.js';
6
4
  import { CliError } from './errors.js';
7
- export function runDatasetImportLcaConvert(options) {
5
+ const OPERATION_REPORT_SCHEMA = 'tidas.operation-report.v1';
6
+ const INVOCATION_CONTEXT_SCHEMA = 'tidas.invocation-context.v1';
7
+ const IMPORT_REPORT_SCHEMA = 'tidas.import-execution-report.v1';
8
+ const SUPPORTED_TIDAS_VERSION = /^0\.2\.\d+$/u;
9
+ const EXIT_CODES = {
10
+ success: 0,
11
+ 'data-issues': 2,
12
+ usage: 64,
13
+ unavailable: 69,
14
+ internal: 70,
15
+ io: 74,
16
+ cancelled: 130,
17
+ };
18
+ const OPERATION_STATUSES = ['succeeded', 'completed-with-issues', 'failed', 'cancelled'];
19
+ const COMPLETENESS_VALUES = ['complete', 'partial', 'not-started'];
20
+ const SOURCE_FORMATS = [
21
+ 'ecospold1',
22
+ 'ecospold2',
23
+ 'simapro-csv',
24
+ 'openlca-jsonld',
25
+ 'openlca-process-xlsx',
26
+ 'ilcd',
27
+ ];
28
+ export async function runDatasetImportLcaConvert(options) {
8
29
  const inputPath = requireInputPath(options.inputPath);
9
30
  const outputDir = requireOutputDir(options.outputDir);
31
+ const fromFormat = normalizeSourceFormat(options.fromFormat);
10
32
  const target = normalizeTarget(options.target);
11
- const fromFormat = options.fromFormat?.trim() || 'auto';
12
- const pythonBin = options.pythonBin?.trim() || 'python3';
13
- const tidasToolsRoot = resolveTidasToolsRoot(options.tidasToolsDir, options.env ?? process.env);
14
- const processBundlesDir = options.processBundlesDir
15
- ? path.resolve(options.processBundlesDir)
16
- : path.join(outputDir, 'process-bundles');
17
- if (options.processBundles === false && options.processBundlesDir?.trim()) {
18
- throw new CliError('--process-bundles-dir cannot be used with --no-process-bundles.', {
19
- code: 'DATASET_IMPORT_LCA_PROCESS_BUNDLES_INVALID',
20
- exitCode: 2,
21
- });
22
- }
23
- const reportPath = path.resolve(options.reportPath ?? path.join(outputDir, 'conversion-report.json'));
24
- const commandArgs = [
25
- '-m',
26
- 'tidas_tools.import_lca.cli',
27
- '--input',
33
+ const env = options.env ?? process.env;
34
+ const executable = resolveTidasBinary(options.tidasBin, env);
35
+ const cwd = path.resolve(options.cwd ?? process.cwd());
36
+ const reportPath = options.reportPath?.trim() ? path.resolve(options.reportPath) : null;
37
+ const configPath = options.tidasConfig?.trim() ? path.resolve(options.tidasConfig) : null;
38
+ const spawnImpl = [spawnSync, options.spawnImpl].filter(Boolean).at(-1);
39
+ assertSupportedPlatform(options.platform ?? process.platform, options.arch ?? process.arch);
40
+ requirePositiveInteger('--max-entry-mib', options.maxEntryMib);
41
+ requirePositiveInteger('--memory-budget-mib', options.memoryBudgetMib);
42
+ requirePositiveInteger('--queue-capacity', options.queueCapacity);
43
+ const versionArgs = ['version', '--format', 'json', '--progress', 'never'];
44
+ const versionRun = runTidas(spawnImpl, executable, versionArgs, cwd, env);
45
+ const versionReport = parseOperationReport(readProcessReport(versionRun, null), 'version');
46
+ assertExitContract(versionRun, versionReport);
47
+ const version = readCompatibleVersion(versionReport);
48
+ const commandArgs = buildImportArgs({
28
49
  inputPath,
29
- '--output-dir',
30
50
  outputDir,
31
- '--from-format',
32
51
  fromFormat,
33
- '--target',
34
52
  target,
35
- '--report',
36
53
  reportPath,
37
- '--language',
38
- options.language?.trim() || 'en',
39
- '--validation-jobs',
40
- String(options.validationJobs ?? 1),
41
- ];
42
- if (options.mappingDir) {
43
- commandArgs.push('--mapping-dir', path.resolve(options.mappingDir));
44
- }
45
- if (options.failOnWarning) {
46
- commandArgs.push('--fail-on-warning');
47
- }
48
- // tidas-tools >= 0.0.28 enables process bundles by default and only accepts
49
- // --no-process-bundles / --process-bundles-dir; a bare --process-bundles flag
50
- // gets prefix-matched by argparse to --process-bundles-dir and aborts.
51
- if (!options.detectOnly && options.processBundles === false) {
52
- commandArgs.push('--no-process-bundles');
53
- }
54
- if (!options.detectOnly && options.processBundlesDir?.trim()) {
55
- commandArgs.push('--process-bundles-dir', processBundlesDir);
56
- }
57
- if (options.detectOnly) {
58
- commandArgs.push('--detect-only');
59
- }
60
- const run = (options.spawnImpl ?? spawnSync)(pythonBin, commandArgs, {
61
- cwd: tidasToolsRoot,
62
- env: {
63
- ...(options.env ?? process.env),
64
- PYTHONPATH: buildPythonPath(tidasToolsRoot, options.env ?? process.env),
65
- },
66
- encoding: 'utf8',
54
+ configPath,
55
+ writeMapping: Boolean(options.writeMapping),
56
+ processBundles: options.processBundles !== false,
57
+ failOnWarning: Boolean(options.failOnWarning),
58
+ maxEntryMib: options.maxEntryMib,
59
+ memoryBudgetMib: options.memoryBudgetMib,
60
+ queueCapacity: options.queueCapacity,
67
61
  });
68
- const conversionReport = readOptionalJson(reportPath);
69
- // Mapping CSV is opt-in in tidas-tools >= 0.0.28 (mapping.csv.gz) and bundle
70
- // output depends on converter defaults, so report what is actually on disk.
71
- const mappingCsv = firstExistingPath([
72
- path.join(outputDir, 'mapping.csv.gz'),
73
- path.join(outputDir, 'mapping.csv'),
74
- ]);
75
- const processBundlesIndexPath = path.join(processBundlesDir, 'index.json');
76
- const files = {
77
- report: path.join(outputDir, 'outputs', 'import-lca-report.json'),
78
- conversion_report: reportPath,
79
- tidas_dir: options.detectOnly ? null : path.join(outputDir, 'tidas'),
80
- ilcd_dir: !options.detectOnly && (target === 'ilcd' || target === 'both')
81
- ? path.join(outputDir, 'ilcd')
82
- : null,
83
- mapping_csv: mappingCsv,
84
- process_bundles_dir: existsSync(processBundlesDir) ? processBundlesDir : null,
85
- process_bundles_index: existsSync(processBundlesIndexPath) ? processBundlesIndexPath : null,
86
- };
87
- const report = {
88
- schema_version: 1,
89
- status: run.status === 0 ? 'completed' : 'blocked',
62
+ const importRun = runTidas(spawnImpl, executable, commandArgs, cwd, env);
63
+ const operationReport = parseOperationReport(readProcessReport(importRun, reportPath), 'import');
64
+ assertExitContract(importRun, operationReport);
65
+ assertImportContract(operationReport);
66
+ return {
67
+ schema_version: 'tiangong-lca.dataset-import-lca-report.v2',
68
+ status: operationReport.status,
69
+ exit_class: operationReport.exit_class,
70
+ exit_code: EXIT_CODES[operationReport.exit_class],
90
71
  generated_at_utc: (options.now ?? new Date()).toISOString(),
91
72
  input_path: inputPath,
92
73
  output_dir: outputDir,
93
74
  from_format: fromFormat,
94
75
  target,
95
- detect_only: Boolean(options.detectOnly),
76
+ tidas: {
77
+ executable,
78
+ version,
79
+ operation_report_schema: OPERATION_REPORT_SCHEMA,
80
+ import_report_schema: IMPORT_REPORT_SCHEMA,
81
+ },
96
82
  command: {
97
- executable: pythonBin,
98
83
  args: commandArgs,
99
- cwd: tidasToolsRoot,
100
- exit_code: run.status,
101
- stdout: run.stdout ?? '',
102
- stderr: run.stderr ?? '',
84
+ cwd,
85
+ stderr: importRun.stderr,
86
+ },
87
+ operation_report: operationReport,
88
+ files: {
89
+ report: reportPath,
90
+ native_import_report: path.join(outputDir, 'import-report.json'),
91
+ issues: path.join(outputDir, 'issues.jsonl'),
92
+ tidas_dir: operationReport.completeness === 'complete' && (target === 'tidas' || target === 'both')
93
+ ? path.join(outputDir, 'tidas')
94
+ : null,
95
+ ilcd_dir: operationReport.completeness === 'complete' && (target === 'ilcd' || target === 'both')
96
+ ? path.join(outputDir, 'ilcd')
97
+ : null,
98
+ mapping_csv: operationReport.completeness === 'complete' && options.writeMapping
99
+ ? path.join(outputDir, 'mapping.csv.gz')
100
+ : null,
101
+ process_bundles_dir: operationReport.completeness === 'complete' && options.processBundles !== false
102
+ ? path.join(outputDir, 'process-bundles')
103
+ : null,
103
104
  },
104
- conversion_report: conversionReport,
105
- files,
106
105
  };
107
- writeJsonArtifact(files.report, report);
108
- return report;
106
+ }
107
+ function buildImportArgs(options) {
108
+ const args = [
109
+ 'import',
110
+ options.inputPath,
111
+ '--output',
112
+ options.outputDir,
113
+ '--target',
114
+ options.target,
115
+ '--format',
116
+ 'json',
117
+ '--progress',
118
+ 'never',
119
+ ];
120
+ if (options.fromFormat !== 'auto') {
121
+ args.push('--from-format', options.fromFormat);
122
+ }
123
+ if (options.reportPath) {
124
+ args.push('--report', options.reportPath);
125
+ }
126
+ if (options.configPath) {
127
+ args.push('--config', options.configPath);
128
+ }
129
+ if (options.writeMapping) {
130
+ args.push('--write-mapping');
131
+ }
132
+ if (!options.processBundles) {
133
+ args.push('--no-process-bundles');
134
+ }
135
+ if (options.failOnWarning) {
136
+ args.push('--fail-on-warning');
137
+ }
138
+ if (options.maxEntryMib !== undefined) {
139
+ args.push('--max-entry-mib', String(options.maxEntryMib));
140
+ }
141
+ if (options.memoryBudgetMib !== undefined) {
142
+ args.push('--memory-budget-mib', String(options.memoryBudgetMib));
143
+ }
144
+ if (options.queueCapacity !== undefined) {
145
+ args.push('--queue-capacity', String(options.queueCapacity));
146
+ }
147
+ return args;
148
+ }
149
+ function runTidas(spawnImpl, executable, args, cwd, env) {
150
+ const run = spawnImpl(executable, args, {
151
+ cwd,
152
+ env,
153
+ encoding: 'utf8',
154
+ windowsHide: true,
155
+ });
156
+ if (run.error) {
157
+ throw new CliError(`Could not execute the Rust tidas binary: ${run.error.message}`, {
158
+ code: 'DATASET_IMPORT_LCA_TIDAS_EXEC_FAILED',
159
+ exitCode: 69,
160
+ details: { executable },
161
+ });
162
+ }
163
+ return {
164
+ status: run.status,
165
+ signal: run.signal,
166
+ stdout: run.stdout ?? '',
167
+ stderr: run.stderr ?? '',
168
+ };
169
+ }
170
+ function readProcessReport(run, reportPath) {
171
+ const cancelled = run.status === 130 || run.signal === 'SIGINT';
172
+ if (reportPath) {
173
+ if (!existsSync(reportPath)) {
174
+ if (cancelled) {
175
+ throw new CliError('tidas import was cancelled before a complete machine report was available.', {
176
+ code: 'DATASET_IMPORT_LCA_CANCELLED',
177
+ exitCode: 130,
178
+ details: { signal: run.signal, stderr: run.stderr },
179
+ });
180
+ }
181
+ throw new CliError(`tidas did not write the requested machine report: ${reportPath}`, {
182
+ code: 'DATASET_IMPORT_LCA_TIDAS_REPORT_MISSING',
183
+ exitCode: 70,
184
+ });
185
+ }
186
+ return readFileSync(reportPath, 'utf8');
187
+ }
188
+ if (!run.stdout.trim()) {
189
+ throw new CliError(cancelled
190
+ ? 'tidas import was cancelled before a complete machine report was available.'
191
+ : 'tidas did not emit a machine-readable operation report.', {
192
+ code: cancelled
193
+ ? 'DATASET_IMPORT_LCA_CANCELLED'
194
+ : 'DATASET_IMPORT_LCA_TIDAS_REPORT_MISSING',
195
+ exitCode: cancelled ? 130 : 70,
196
+ details: { signal: run.signal, stderr: run.stderr },
197
+ });
198
+ }
199
+ return run.stdout;
200
+ }
201
+ function parseOperationReport(text, command) {
202
+ let value;
203
+ try {
204
+ value = JSON.parse(text);
205
+ }
206
+ catch (error) {
207
+ throw new CliError('tidas emitted invalid JSON instead of its machine operation report.', {
208
+ code: 'DATASET_IMPORT_LCA_TIDAS_REPORT_INVALID',
209
+ exitCode: 70,
210
+ details: String(error),
211
+ });
212
+ }
213
+ if (!isRecord(value) ||
214
+ value.schema_version !== OPERATION_REPORT_SCHEMA ||
215
+ value.command !== command ||
216
+ !includesValue(OPERATION_STATUSES, value.status) ||
217
+ !isExitClass(value.exit_class) ||
218
+ !includesValue(COMPLETENESS_VALUES, value.completeness) ||
219
+ !isRecord(value.invocation) ||
220
+ value.invocation.schema_version !== INVOCATION_CONTEXT_SCHEMA ||
221
+ value.invocation.input_policy !== 'explicit-path-or-dash' ||
222
+ !['stdout', 'file'].includes(String(value.invocation.report_destination)) ||
223
+ value.invocation.diagnostic_destination !== 'stderr' ||
224
+ !isRecord(value.summary) ||
225
+ !Array.isArray(value.diagnostics) ||
226
+ !Array.isArray(value.artifacts) ||
227
+ !Array.isArray(value.next_actions)) {
228
+ throw new CliError(`Incompatible tidas ${command} machine contract; expected ${OPERATION_REPORT_SCHEMA}.`, {
229
+ code: 'DATASET_IMPORT_LCA_TIDAS_CONTRACT_INCOMPATIBLE',
230
+ exitCode: 69,
231
+ });
232
+ }
233
+ return value;
234
+ }
235
+ function assertExitContract(run, report) {
236
+ const expected = EXIT_CODES[report.exit_class];
237
+ if (run.status !== expected) {
238
+ throw new CliError(`tidas exit status ${String(run.status)} disagrees with report exit class ${report.exit_class} (${expected}).`, {
239
+ code: 'DATASET_IMPORT_LCA_TIDAS_EXIT_MISMATCH',
240
+ exitCode: 70,
241
+ });
242
+ }
243
+ }
244
+ function readCompatibleVersion(report) {
245
+ if (report.status !== 'succeeded' ||
246
+ report.exit_class !== 'success' ||
247
+ report.completeness !== 'complete' ||
248
+ report.summary.operation_report_schema !== OPERATION_REPORT_SCHEMA ||
249
+ typeof report.summary.binary_version !== 'string' ||
250
+ !SUPPORTED_TIDAS_VERSION.test(report.summary.binary_version)) {
251
+ throw new CliError('Incompatible tidas binary: dataset import requires a stable contract-compatible 0.2.x release.', {
252
+ code: 'DATASET_IMPORT_LCA_TIDAS_VERSION_INCOMPATIBLE',
253
+ exitCode: 69,
254
+ details: { binary_version: report.summary.binary_version ?? null },
255
+ });
256
+ }
257
+ return report.summary.binary_version;
258
+ }
259
+ function assertImportContract(report) {
260
+ if (report.status === 'succeeded' || report.status === 'completed-with-issues') {
261
+ const importSummary = report.summary.import;
262
+ if (!isRecord(importSummary) || importSummary.schema_version !== IMPORT_REPORT_SCHEMA) {
263
+ throw new CliError(`Incompatible tidas import summary; expected ${IMPORT_REPORT_SCHEMA}.`, {
264
+ code: 'DATASET_IMPORT_LCA_TIDAS_IMPORT_CONTRACT_INCOMPATIBLE',
265
+ exitCode: 69,
266
+ });
267
+ }
268
+ }
109
269
  }
110
270
  function requireInputPath(value) {
111
271
  if (!value?.trim()) {
@@ -132,6 +292,16 @@ function requireOutputDir(value) {
132
292
  }
133
293
  return path.resolve(value);
134
294
  }
295
+ function normalizeSourceFormat(value) {
296
+ const format = value?.trim() || 'auto';
297
+ if (format === 'auto' || includesValue(SOURCE_FORMATS, format)) {
298
+ return format;
299
+ }
300
+ throw new CliError(`--from-format must be auto or one of: ${SOURCE_FORMATS.join(', ')}.`, {
301
+ code: 'DATASET_IMPORT_LCA_SOURCE_FORMAT_INVALID',
302
+ exitCode: 2,
303
+ });
304
+ }
135
305
  function normalizeTarget(value) {
136
306
  const target = value?.trim() || 'tidas';
137
307
  if (target === 'tidas' || target === 'ilcd' || target === 'both') {
@@ -142,60 +312,61 @@ function normalizeTarget(value) {
142
312
  exitCode: 2,
143
313
  });
144
314
  }
145
- function resolveTidasToolsRoot(explicitValue, env, defaultCandidate) {
146
- const cliRepoRoot = resolveCliRepoRoot();
147
- if (explicitValue?.trim()) {
148
- const resolved = path.resolve(explicitValue);
149
- if (existsSync(path.join(resolved, 'src/tidas_tools/import_lca/cli.py'))) {
150
- return resolved;
151
- }
152
- throw new CliError('Could not resolve a tidas-tools checkout for dataset import.', {
153
- code: 'DATASET_IMPORT_LCA_TIDAS_TOOLS_NOT_FOUND',
154
- exitCode: 2,
155
- details: { candidates: [explicitValue] },
156
- });
157
- }
158
- const candidates = [
159
- env.TIDAS_TOOLS_DIR,
160
- env.TIDAS_TOOLS_PATH,
161
- defaultCandidate ?? path.resolve(cliRepoRoot, '../tidas-tools'),
162
- ].filter((candidate) => Boolean(candidate?.trim()));
163
- for (const candidate of candidates) {
315
+ function resolveTidasBinary(explicitValue, env) {
316
+ const candidate = explicitValue?.trim() || env.TIDAS_BIN?.trim() || 'tidas';
317
+ if (path.isAbsolute(candidate) || candidate.includes('/') || candidate.includes('\\')) {
164
318
  const resolved = path.resolve(candidate);
165
- if (existsSync(path.join(resolved, 'src/tidas_tools/import_lca/cli.py'))) {
166
- return resolved;
319
+ if (!existsSync(resolved)) {
320
+ throw new CliError(`Rust tidas binary not found: ${resolved}`, {
321
+ code: 'DATASET_IMPORT_LCA_TIDAS_NOT_FOUND',
322
+ exitCode: 69,
323
+ });
167
324
  }
325
+ return resolved;
168
326
  }
169
- throw new CliError('Could not resolve a tidas-tools checkout for dataset import.', {
170
- code: 'DATASET_IMPORT_LCA_TIDAS_TOOLS_NOT_FOUND',
171
- exitCode: 2,
172
- details: { candidates },
173
- });
327
+ return candidate;
174
328
  }
175
- function resolveCliRepoRoot(candidatesOverride) {
176
- const moduleDir = path.dirname(fileURLToPath(import.meta.url));
177
- const candidates = candidatesOverride ?? [
178
- path.resolve(moduleDir, '../..'),
179
- path.resolve(moduleDir, '../../..'),
180
- ];
181
- return (candidates.find((candidate) => existsSync(path.join(candidate, 'package.json')) &&
182
- existsSync(path.join(candidate, 'src/cli.ts'))) ?? candidates[0]);
329
+ function assertSupportedPlatform(platform, arch) {
330
+ const supported = (platform === 'linux' && (arch === 'x64' || arch === 'arm64')) ||
331
+ (platform === 'darwin' && (arch === 'x64' || arch === 'arm64')) ||
332
+ (platform === 'win32' && arch === 'x64');
333
+ if (!supported) {
334
+ throw new CliError(`No supported Rust tidas artifact exists for ${platform}/${arch}.`, {
335
+ code: 'DATASET_IMPORT_LCA_PLATFORM_UNSUPPORTED',
336
+ exitCode: 69,
337
+ details: {
338
+ supported: ['linux/x64', 'linux/arm64', 'darwin/x64', 'darwin/arm64', 'win32/x64'],
339
+ },
340
+ });
341
+ }
183
342
  }
184
- function buildPythonPath(tidasToolsRoot, env) {
185
- return [path.join(tidasToolsRoot, 'src'), env.PYTHONPATH].filter(Boolean).join(path.delimiter);
343
+ function requirePositiveInteger(flag, value) {
344
+ if (value !== undefined && (!Number.isSafeInteger(value) || value <= 0)) {
345
+ throw new CliError(`${flag} must be a positive integer.`, {
346
+ code: 'DATASET_IMPORT_LCA_RUNTIME_BOUND_INVALID',
347
+ exitCode: 2,
348
+ });
349
+ }
186
350
  }
187
- function firstExistingPath(candidates) {
188
- return candidates.find((candidate) => existsSync(candidate)) ?? null;
351
+ function isRecord(value) {
352
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
189
353
  }
190
- function readOptionalJson(filePath) {
191
- if (!existsSync(filePath)) {
192
- return null;
193
- }
194
- return JSON.parse(readFileSync(filePath, 'utf8'));
354
+ function includesValue(values, value) {
355
+ return typeof value === 'string' && values.includes(value);
356
+ }
357
+ function isExitClass(value) {
358
+ return typeof value === 'string' && Object.hasOwn(EXIT_CODES, value);
195
359
  }
196
360
  export const __testInternals = {
361
+ assertImportContract,
362
+ assertSupportedPlatform,
363
+ buildImportArgs,
364
+ normalizeSourceFormat,
197
365
  normalizeTarget,
198
- resolveCliRepoRoot,
199
- resolveTidasToolsRoot,
366
+ parseOperationReport,
367
+ readCompatibleVersion,
368
+ readProcessReport,
369
+ requirePositiveInteger,
370
+ resolveTidasBinary,
200
371
  };
201
372
  //# sourceMappingURL=dataset-import-lca.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"dataset-import-lca.js","sourceRoot":"","sources":["../../../src/lib/dataset-import-lca.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { spawnSync, type SpawnSyncReturns } from 'node:child_process';\nimport { writeJsonArtifact } from './artifacts.js';\nimport { CliError } from './errors.js';\n\nexport type DatasetImportLcaTarget = 'tidas' | 'ilcd' | 'both';\n\nexport type DatasetImportLcaReport = {\n schema_version: 1;\n status: 'completed' | 'blocked';\n generated_at_utc: string;\n input_path: string;\n output_dir: string;\n from_format: string;\n target: DatasetImportLcaTarget;\n detect_only: boolean;\n command: {\n executable: string;\n args: string[];\n cwd: string;\n exit_code: number | null;\n stdout: string;\n stderr: string;\n };\n conversion_report: unknown | null;\n files: {\n report: string;\n conversion_report: string;\n tidas_dir: string | null;\n ilcd_dir: string | null;\n mapping_csv: string | null;\n process_bundles_dir: string | null;\n process_bundles_index: string | null;\n };\n};\n\nexport type RunDatasetImportLcaConvertOptions = {\n inputPath: string;\n outputDir: string;\n fromFormat?: string | undefined;\n target?: string | undefined;\n language?: string | undefined;\n reportPath?: string | undefined;\n mappingDir?: string | undefined;\n failOnWarning?: boolean | undefined;\n validationJobs?: number | undefined;\n processBundles?: boolean | undefined;\n processBundlesDir?: string | undefined;\n detectOnly?: boolean | undefined;\n pythonBin?: string | undefined;\n tidasToolsDir?: string | undefined;\n env?: NodeJS.ProcessEnv | undefined;\n now?: Date | undefined;\n spawnImpl?: typeof spawnSync | undefined;\n};\n\nexport function runDatasetImportLcaConvert(\n options: RunDatasetImportLcaConvertOptions,\n): DatasetImportLcaReport {\n const inputPath = requireInputPath(options.inputPath);\n const outputDir = requireOutputDir(options.outputDir);\n const target = normalizeTarget(options.target);\n const fromFormat = options.fromFormat?.trim() || 'auto';\n const pythonBin = options.pythonBin?.trim() || 'python3';\n const tidasToolsRoot = resolveTidasToolsRoot(options.tidasToolsDir, options.env ?? process.env);\n const processBundlesDir = options.processBundlesDir\n ? path.resolve(options.processBundlesDir)\n : path.join(outputDir, 'process-bundles');\n if (options.processBundles === false && options.processBundlesDir?.trim()) {\n throw new CliError('--process-bundles-dir cannot be used with --no-process-bundles.', {\n code: 'DATASET_IMPORT_LCA_PROCESS_BUNDLES_INVALID',\n exitCode: 2,\n });\n }\n const reportPath = path.resolve(\n options.reportPath ?? path.join(outputDir, 'conversion-report.json'),\n );\n const commandArgs = [\n '-m',\n 'tidas_tools.import_lca.cli',\n '--input',\n inputPath,\n '--output-dir',\n outputDir,\n '--from-format',\n fromFormat,\n '--target',\n target,\n '--report',\n reportPath,\n '--language',\n options.language?.trim() || 'en',\n '--validation-jobs',\n String(options.validationJobs ?? 1),\n ];\n if (options.mappingDir) {\n commandArgs.push('--mapping-dir', path.resolve(options.mappingDir));\n }\n if (options.failOnWarning) {\n commandArgs.push('--fail-on-warning');\n }\n // tidas-tools >= 0.0.28 enables process bundles by default and only accepts\n // --no-process-bundles / --process-bundles-dir; a bare --process-bundles flag\n // gets prefix-matched by argparse to --process-bundles-dir and aborts.\n if (!options.detectOnly && options.processBundles === false) {\n commandArgs.push('--no-process-bundles');\n }\n if (!options.detectOnly && options.processBundlesDir?.trim()) {\n commandArgs.push('--process-bundles-dir', processBundlesDir);\n }\n if (options.detectOnly) {\n commandArgs.push('--detect-only');\n }\n\n const run = (options.spawnImpl ?? spawnSync)(pythonBin, commandArgs, {\n cwd: tidasToolsRoot,\n env: {\n ...(options.env ?? process.env),\n PYTHONPATH: buildPythonPath(tidasToolsRoot, options.env ?? process.env),\n },\n encoding: 'utf8',\n }) as SpawnSyncReturns<string>;\n const conversionReport = readOptionalJson(reportPath);\n // Mapping CSV is opt-in in tidas-tools >= 0.0.28 (mapping.csv.gz) and bundle\n // output depends on converter defaults, so report what is actually on disk.\n const mappingCsv = firstExistingPath([\n path.join(outputDir, 'mapping.csv.gz'),\n path.join(outputDir, 'mapping.csv'),\n ]);\n const processBundlesIndexPath = path.join(processBundlesDir, 'index.json');\n const files = {\n report: path.join(outputDir, 'outputs', 'import-lca-report.json'),\n conversion_report: reportPath,\n tidas_dir: options.detectOnly ? null : path.join(outputDir, 'tidas'),\n ilcd_dir:\n !options.detectOnly && (target === 'ilcd' || target === 'both')\n ? path.join(outputDir, 'ilcd')\n : null,\n mapping_csv: mappingCsv,\n process_bundles_dir: existsSync(processBundlesDir) ? processBundlesDir : null,\n process_bundles_index: existsSync(processBundlesIndexPath) ? processBundlesIndexPath : null,\n };\n const report: DatasetImportLcaReport = {\n schema_version: 1,\n status: run.status === 0 ? 'completed' : 'blocked',\n generated_at_utc: (options.now ?? new Date()).toISOString(),\n input_path: inputPath,\n output_dir: outputDir,\n from_format: fromFormat,\n target,\n detect_only: Boolean(options.detectOnly),\n command: {\n executable: pythonBin,\n args: commandArgs,\n cwd: tidasToolsRoot,\n exit_code: run.status,\n stdout: run.stdout ?? '',\n stderr: run.stderr ?? '',\n },\n conversion_report: conversionReport,\n files,\n };\n\n writeJsonArtifact(files.report, report);\n return report;\n}\n\nfunction requireInputPath(value: string): string {\n if (!value?.trim()) {\n throw new CliError('Missing required --input value.', {\n code: 'DATASET_IMPORT_LCA_INPUT_REQUIRED',\n exitCode: 2,\n });\n }\n const resolved = path.resolve(value);\n if (!existsSync(resolved)) {\n throw new CliError(`Input path not found: ${resolved}`, {\n code: 'DATASET_IMPORT_LCA_INPUT_NOT_FOUND',\n exitCode: 2,\n });\n }\n return resolved;\n}\n\nfunction requireOutputDir(value: string): string {\n if (!value?.trim()) {\n throw new CliError('Missing required --output-dir value.', {\n code: 'DATASET_IMPORT_LCA_OUTPUT_DIR_REQUIRED',\n exitCode: 2,\n });\n }\n return path.resolve(value);\n}\n\nfunction normalizeTarget(value: string | undefined): DatasetImportLcaTarget {\n const target = value?.trim() || 'tidas';\n if (target === 'tidas' || target === 'ilcd' || target === 'both') {\n return target;\n }\n throw new CliError(\"--target must be 'tidas', 'ilcd', or 'both'.\", {\n code: 'DATASET_IMPORT_LCA_TARGET_INVALID',\n exitCode: 2,\n });\n}\n\nfunction resolveTidasToolsRoot(\n explicitValue: string | undefined,\n env: NodeJS.ProcessEnv,\n defaultCandidate?: string,\n): string {\n const cliRepoRoot = resolveCliRepoRoot();\n if (explicitValue?.trim()) {\n const resolved = path.resolve(explicitValue);\n if (existsSync(path.join(resolved, 'src/tidas_tools/import_lca/cli.py'))) {\n return resolved;\n }\n throw new CliError('Could not resolve a tidas-tools checkout for dataset import.', {\n code: 'DATASET_IMPORT_LCA_TIDAS_TOOLS_NOT_FOUND',\n exitCode: 2,\n details: { candidates: [explicitValue] },\n });\n }\n\n const candidates = [\n env.TIDAS_TOOLS_DIR,\n env.TIDAS_TOOLS_PATH,\n defaultCandidate ?? path.resolve(cliRepoRoot, '../tidas-tools'),\n ].filter((candidate): candidate is string => Boolean(candidate?.trim()));\n\n for (const candidate of candidates) {\n const resolved = path.resolve(candidate);\n if (existsSync(path.join(resolved, 'src/tidas_tools/import_lca/cli.py'))) {\n return resolved;\n }\n }\n\n throw new CliError('Could not resolve a tidas-tools checkout for dataset import.', {\n code: 'DATASET_IMPORT_LCA_TIDAS_TOOLS_NOT_FOUND',\n exitCode: 2,\n details: { candidates },\n });\n}\n\nfunction resolveCliRepoRoot(candidatesOverride?: string[]): string {\n const moduleDir = path.dirname(fileURLToPath(import.meta.url));\n const candidates = candidatesOverride ?? [\n path.resolve(moduleDir, '../..'),\n path.resolve(moduleDir, '../../..'),\n ];\n return (\n candidates.find(\n (candidate) =>\n existsSync(path.join(candidate, 'package.json')) &&\n existsSync(path.join(candidate, 'src/cli.ts')),\n ) ?? candidates[0]\n );\n}\n\nfunction buildPythonPath(tidasToolsRoot: string, env: NodeJS.ProcessEnv): string {\n return [path.join(tidasToolsRoot, 'src'), env.PYTHONPATH].filter(Boolean).join(path.delimiter);\n}\n\nfunction firstExistingPath(candidates: string[]): string | null {\n return candidates.find((candidate) => existsSync(candidate)) ?? null;\n}\n\nfunction readOptionalJson(filePath: string): unknown | null {\n if (!existsSync(filePath)) {\n return null;\n }\n return JSON.parse(readFileSync(filePath, 'utf8'));\n}\n\nexport const __testInternals = {\n normalizeTarget,\n resolveCliRepoRoot,\n resolveTidasToolsRoot,\n};\n"]}
1
+ {"version":3,"file":"dataset-import-lca.js","sourceRoot":"","sources":["../../../src/lib/dataset-import-lca.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\nimport path from 'node:path';\nimport { spawnSync, type SpawnSyncReturns } from 'node:child_process';\nimport { CliError } from './errors.js';\n\nconst OPERATION_REPORT_SCHEMA = 'tidas.operation-report.v1';\nconst INVOCATION_CONTEXT_SCHEMA = 'tidas.invocation-context.v1';\nconst IMPORT_REPORT_SCHEMA = 'tidas.import-execution-report.v1';\nconst SUPPORTED_TIDAS_VERSION = /^0\\.2\\.\\d+$/u;\n\nconst EXIT_CODES = {\n success: 0,\n 'data-issues': 2,\n usage: 64,\n unavailable: 69,\n internal: 70,\n io: 74,\n cancelled: 130,\n} as const;\n\nconst OPERATION_STATUSES = ['succeeded', 'completed-with-issues', 'failed', 'cancelled'] as const;\nconst COMPLETENESS_VALUES = ['complete', 'partial', 'not-started'] as const;\nconst SOURCE_FORMATS = [\n 'ecospold1',\n 'ecospold2',\n 'simapro-csv',\n 'openlca-jsonld',\n 'openlca-process-xlsx',\n 'ilcd',\n] as const;\n\ntype TidasExitClass = keyof typeof EXIT_CODES;\ntype TidasOperationStatus = (typeof OPERATION_STATUSES)[number];\ntype TidasCompleteness = (typeof COMPLETENESS_VALUES)[number];\nexport type DatasetImportLcaTarget = 'tidas' | 'ilcd' | 'both';\nexport type DatasetImportLcaSourceFormat = (typeof SOURCE_FORMATS)[number];\n\ntype TidasOperationReport = {\n schema_version: typeof OPERATION_REPORT_SCHEMA;\n command: 'version' | 'import';\n status: TidasOperationStatus;\n exit_class: TidasExitClass;\n completeness: TidasCompleteness;\n invocation: {\n schema_version: typeof INVOCATION_CONTEXT_SCHEMA;\n input_policy: 'explicit-path-or-dash';\n report_destination: 'stdout' | 'file';\n diagnostic_destination: 'stderr';\n [key: string]: unknown;\n };\n summary: Record<string, unknown>;\n diagnostics: unknown[];\n artifacts: unknown[];\n next_actions: unknown[];\n};\n\nexport type DatasetImportLcaReport = {\n schema_version: 'tiangong-lca.dataset-import-lca-report.v2';\n status: TidasOperationStatus;\n exit_class: TidasExitClass;\n exit_code: number;\n generated_at_utc: string;\n input_path: string;\n output_dir: string;\n from_format: DatasetImportLcaSourceFormat | 'auto';\n target: DatasetImportLcaTarget;\n tidas: {\n executable: string;\n version: string;\n operation_report_schema: typeof OPERATION_REPORT_SCHEMA;\n import_report_schema: typeof IMPORT_REPORT_SCHEMA;\n };\n command: {\n args: string[];\n cwd: string;\n stderr: string;\n };\n operation_report: TidasOperationReport;\n files: {\n report: string | null;\n native_import_report: string;\n issues: string;\n tidas_dir: string | null;\n ilcd_dir: string | null;\n mapping_csv: string | null;\n process_bundles_dir: string | null;\n };\n};\n\nexport type TidasProcessResult = {\n status: number | null;\n signal: NodeJS.Signals | null;\n stdout: string;\n stderr: string;\n error?: Error | undefined;\n};\n\nexport type RunDatasetImportLcaConvertOptions = {\n inputPath: string;\n outputDir: string;\n fromFormat?: string | undefined;\n target?: string | undefined;\n reportPath?: string | undefined;\n writeMapping?: boolean | undefined;\n processBundles?: boolean | undefined;\n failOnWarning?: boolean | undefined;\n maxEntryMib?: number | undefined;\n memoryBudgetMib?: number | undefined;\n queueCapacity?: number | undefined;\n tidasBin?: string | undefined;\n tidasConfig?: string | undefined;\n env?: NodeJS.ProcessEnv | undefined;\n cwd?: string | undefined;\n platform?: NodeJS.Platform | undefined;\n arch?: string | undefined;\n now?: Date | undefined;\n spawnImpl?: typeof spawnSync | undefined;\n};\n\nexport async function runDatasetImportLcaConvert(\n options: RunDatasetImportLcaConvertOptions,\n): Promise<DatasetImportLcaReport> {\n const inputPath = requireInputPath(options.inputPath);\n const outputDir = requireOutputDir(options.outputDir);\n const fromFormat = normalizeSourceFormat(options.fromFormat);\n const target = normalizeTarget(options.target);\n const env = options.env ?? process.env;\n const executable = resolveTidasBinary(options.tidasBin, env);\n const cwd = path.resolve(options.cwd ?? process.cwd());\n const reportPath = options.reportPath?.trim() ? path.resolve(options.reportPath) : null;\n const configPath = options.tidasConfig?.trim() ? path.resolve(options.tidasConfig) : null;\n const spawnImpl = [spawnSync, options.spawnImpl].filter(Boolean).at(-1) as typeof spawnSync;\n\n assertSupportedPlatform(options.platform ?? process.platform, options.arch ?? process.arch);\n requirePositiveInteger('--max-entry-mib', options.maxEntryMib);\n requirePositiveInteger('--memory-budget-mib', options.memoryBudgetMib);\n requirePositiveInteger('--queue-capacity', options.queueCapacity);\n\n const versionArgs = ['version', '--format', 'json', '--progress', 'never'];\n const versionRun = runTidas(spawnImpl, executable, versionArgs, cwd, env);\n const versionReport = parseOperationReport(readProcessReport(versionRun, null), 'version');\n assertExitContract(versionRun, versionReport);\n const version = readCompatibleVersion(versionReport);\n\n const commandArgs = buildImportArgs({\n inputPath,\n outputDir,\n fromFormat,\n target,\n reportPath,\n configPath,\n writeMapping: Boolean(options.writeMapping),\n processBundles: options.processBundles !== false,\n failOnWarning: Boolean(options.failOnWarning),\n maxEntryMib: options.maxEntryMib,\n memoryBudgetMib: options.memoryBudgetMib,\n queueCapacity: options.queueCapacity,\n });\n const importRun = runTidas(spawnImpl, executable, commandArgs, cwd, env);\n const operationReport = parseOperationReport(readProcessReport(importRun, reportPath), 'import');\n assertExitContract(importRun, operationReport);\n assertImportContract(operationReport);\n\n return {\n schema_version: 'tiangong-lca.dataset-import-lca-report.v2',\n status: operationReport.status,\n exit_class: operationReport.exit_class,\n exit_code: EXIT_CODES[operationReport.exit_class],\n generated_at_utc: (options.now ?? new Date()).toISOString(),\n input_path: inputPath,\n output_dir: outputDir,\n from_format: fromFormat,\n target,\n tidas: {\n executable,\n version,\n operation_report_schema: OPERATION_REPORT_SCHEMA,\n import_report_schema: IMPORT_REPORT_SCHEMA,\n },\n command: {\n args: commandArgs,\n cwd,\n stderr: importRun.stderr,\n },\n operation_report: operationReport,\n files: {\n report: reportPath,\n native_import_report: path.join(outputDir, 'import-report.json'),\n issues: path.join(outputDir, 'issues.jsonl'),\n tidas_dir:\n operationReport.completeness === 'complete' && (target === 'tidas' || target === 'both')\n ? path.join(outputDir, 'tidas')\n : null,\n ilcd_dir:\n operationReport.completeness === 'complete' && (target === 'ilcd' || target === 'both')\n ? path.join(outputDir, 'ilcd')\n : null,\n mapping_csv:\n operationReport.completeness === 'complete' && options.writeMapping\n ? path.join(outputDir, 'mapping.csv.gz')\n : null,\n process_bundles_dir:\n operationReport.completeness === 'complete' && options.processBundles !== false\n ? path.join(outputDir, 'process-bundles')\n : null,\n },\n };\n}\n\nfunction buildImportArgs(options: {\n inputPath: string;\n outputDir: string;\n fromFormat: DatasetImportLcaSourceFormat | 'auto';\n target: DatasetImportLcaTarget;\n reportPath: string | null;\n configPath: string | null;\n writeMapping: boolean;\n processBundles: boolean;\n failOnWarning: boolean;\n maxEntryMib: number | undefined;\n memoryBudgetMib: number | undefined;\n queueCapacity: number | undefined;\n}): string[] {\n const args = [\n 'import',\n options.inputPath,\n '--output',\n options.outputDir,\n '--target',\n options.target,\n '--format',\n 'json',\n '--progress',\n 'never',\n ];\n if (options.fromFormat !== 'auto') {\n args.push('--from-format', options.fromFormat);\n }\n if (options.reportPath) {\n args.push('--report', options.reportPath);\n }\n if (options.configPath) {\n args.push('--config', options.configPath);\n }\n if (options.writeMapping) {\n args.push('--write-mapping');\n }\n if (!options.processBundles) {\n args.push('--no-process-bundles');\n }\n if (options.failOnWarning) {\n args.push('--fail-on-warning');\n }\n if (options.maxEntryMib !== undefined) {\n args.push('--max-entry-mib', String(options.maxEntryMib));\n }\n if (options.memoryBudgetMib !== undefined) {\n args.push('--memory-budget-mib', String(options.memoryBudgetMib));\n }\n if (options.queueCapacity !== undefined) {\n args.push('--queue-capacity', String(options.queueCapacity));\n }\n return args;\n}\n\nfunction runTidas(\n spawnImpl: typeof spawnSync,\n executable: string,\n args: string[],\n cwd: string,\n env: NodeJS.ProcessEnv,\n): TidasProcessResult {\n const run = spawnImpl(executable, args, {\n cwd,\n env,\n encoding: 'utf8',\n windowsHide: true,\n }) as SpawnSyncReturns<string>;\n if (run.error) {\n throw new CliError(`Could not execute the Rust tidas binary: ${run.error.message}`, {\n code: 'DATASET_IMPORT_LCA_TIDAS_EXEC_FAILED',\n exitCode: 69,\n details: { executable },\n });\n }\n return {\n status: run.status,\n signal: run.signal,\n stdout: run.stdout ?? '',\n stderr: run.stderr ?? '',\n };\n}\n\nfunction readProcessReport(run: TidasProcessResult, reportPath: string | null): string {\n const cancelled = run.status === 130 || run.signal === 'SIGINT';\n if (reportPath) {\n if (!existsSync(reportPath)) {\n if (cancelled) {\n throw new CliError(\n 'tidas import was cancelled before a complete machine report was available.',\n {\n code: 'DATASET_IMPORT_LCA_CANCELLED',\n exitCode: 130,\n details: { signal: run.signal, stderr: run.stderr },\n },\n );\n }\n throw new CliError(`tidas did not write the requested machine report: ${reportPath}`, {\n code: 'DATASET_IMPORT_LCA_TIDAS_REPORT_MISSING',\n exitCode: 70,\n });\n }\n return readFileSync(reportPath, 'utf8');\n }\n if (!run.stdout.trim()) {\n throw new CliError(\n cancelled\n ? 'tidas import was cancelled before a complete machine report was available.'\n : 'tidas did not emit a machine-readable operation report.',\n {\n code: cancelled\n ? 'DATASET_IMPORT_LCA_CANCELLED'\n : 'DATASET_IMPORT_LCA_TIDAS_REPORT_MISSING',\n exitCode: cancelled ? 130 : 70,\n details: { signal: run.signal, stderr: run.stderr },\n },\n );\n }\n return run.stdout;\n}\n\nfunction parseOperationReport(text: string, command: 'version' | 'import'): TidasOperationReport {\n let value: unknown;\n try {\n value = JSON.parse(text);\n } catch (error) {\n throw new CliError('tidas emitted invalid JSON instead of its machine operation report.', {\n code: 'DATASET_IMPORT_LCA_TIDAS_REPORT_INVALID',\n exitCode: 70,\n details: String(error),\n });\n }\n if (\n !isRecord(value) ||\n value.schema_version !== OPERATION_REPORT_SCHEMA ||\n value.command !== command ||\n !includesValue(OPERATION_STATUSES, value.status) ||\n !isExitClass(value.exit_class) ||\n !includesValue(COMPLETENESS_VALUES, value.completeness) ||\n !isRecord(value.invocation) ||\n value.invocation.schema_version !== INVOCATION_CONTEXT_SCHEMA ||\n value.invocation.input_policy !== 'explicit-path-or-dash' ||\n !['stdout', 'file'].includes(String(value.invocation.report_destination)) ||\n value.invocation.diagnostic_destination !== 'stderr' ||\n !isRecord(value.summary) ||\n !Array.isArray(value.diagnostics) ||\n !Array.isArray(value.artifacts) ||\n !Array.isArray(value.next_actions)\n ) {\n throw new CliError(\n `Incompatible tidas ${command} machine contract; expected ${OPERATION_REPORT_SCHEMA}.`,\n {\n code: 'DATASET_IMPORT_LCA_TIDAS_CONTRACT_INCOMPATIBLE',\n exitCode: 69,\n },\n );\n }\n return value as TidasOperationReport;\n}\n\nfunction assertExitContract(run: TidasProcessResult, report: TidasOperationReport): void {\n const expected = EXIT_CODES[report.exit_class];\n if (run.status !== expected) {\n throw new CliError(\n `tidas exit status ${String(run.status)} disagrees with report exit class ${report.exit_class} (${expected}).`,\n {\n code: 'DATASET_IMPORT_LCA_TIDAS_EXIT_MISMATCH',\n exitCode: 70,\n },\n );\n }\n}\n\nfunction readCompatibleVersion(report: TidasOperationReport): string {\n if (\n report.status !== 'succeeded' ||\n report.exit_class !== 'success' ||\n report.completeness !== 'complete' ||\n report.summary.operation_report_schema !== OPERATION_REPORT_SCHEMA ||\n typeof report.summary.binary_version !== 'string' ||\n !SUPPORTED_TIDAS_VERSION.test(report.summary.binary_version)\n ) {\n throw new CliError(\n 'Incompatible tidas binary: dataset import requires a stable contract-compatible 0.2.x release.',\n {\n code: 'DATASET_IMPORT_LCA_TIDAS_VERSION_INCOMPATIBLE',\n exitCode: 69,\n details: { binary_version: report.summary.binary_version ?? null },\n },\n );\n }\n return report.summary.binary_version;\n}\n\nfunction assertImportContract(report: TidasOperationReport): void {\n if (report.status === 'succeeded' || report.status === 'completed-with-issues') {\n const importSummary = report.summary.import;\n if (!isRecord(importSummary) || importSummary.schema_version !== IMPORT_REPORT_SCHEMA) {\n throw new CliError(`Incompatible tidas import summary; expected ${IMPORT_REPORT_SCHEMA}.`, {\n code: 'DATASET_IMPORT_LCA_TIDAS_IMPORT_CONTRACT_INCOMPATIBLE',\n exitCode: 69,\n });\n }\n }\n}\n\nfunction requireInputPath(value: string): string {\n if (!value?.trim()) {\n throw new CliError('Missing required --input value.', {\n code: 'DATASET_IMPORT_LCA_INPUT_REQUIRED',\n exitCode: 2,\n });\n }\n const resolved = path.resolve(value);\n if (!existsSync(resolved)) {\n throw new CliError(`Input path not found: ${resolved}`, {\n code: 'DATASET_IMPORT_LCA_INPUT_NOT_FOUND',\n exitCode: 2,\n });\n }\n return resolved;\n}\n\nfunction requireOutputDir(value: string): string {\n if (!value?.trim()) {\n throw new CliError('Missing required --output-dir value.', {\n code: 'DATASET_IMPORT_LCA_OUTPUT_DIR_REQUIRED',\n exitCode: 2,\n });\n }\n return path.resolve(value);\n}\n\nfunction normalizeSourceFormat(value: string | undefined): DatasetImportLcaSourceFormat | 'auto' {\n const format = value?.trim() || 'auto';\n if (format === 'auto' || includesValue(SOURCE_FORMATS, format)) {\n return format;\n }\n throw new CliError(`--from-format must be auto or one of: ${SOURCE_FORMATS.join(', ')}.`, {\n code: 'DATASET_IMPORT_LCA_SOURCE_FORMAT_INVALID',\n exitCode: 2,\n });\n}\n\nfunction normalizeTarget(value: string | undefined): DatasetImportLcaTarget {\n const target = value?.trim() || 'tidas';\n if (target === 'tidas' || target === 'ilcd' || target === 'both') {\n return target;\n }\n throw new CliError(\"--target must be 'tidas', 'ilcd', or 'both'.\", {\n code: 'DATASET_IMPORT_LCA_TARGET_INVALID',\n exitCode: 2,\n });\n}\n\nfunction resolveTidasBinary(explicitValue: string | undefined, env: NodeJS.ProcessEnv): string {\n const candidate = explicitValue?.trim() || env.TIDAS_BIN?.trim() || 'tidas';\n if (path.isAbsolute(candidate) || candidate.includes('/') || candidate.includes('\\\\')) {\n const resolved = path.resolve(candidate);\n if (!existsSync(resolved)) {\n throw new CliError(`Rust tidas binary not found: ${resolved}`, {\n code: 'DATASET_IMPORT_LCA_TIDAS_NOT_FOUND',\n exitCode: 69,\n });\n }\n return resolved;\n }\n return candidate;\n}\n\nfunction assertSupportedPlatform(platform: NodeJS.Platform, arch: string): void {\n const supported =\n (platform === 'linux' && (arch === 'x64' || arch === 'arm64')) ||\n (platform === 'darwin' && (arch === 'x64' || arch === 'arm64')) ||\n (platform === 'win32' && arch === 'x64');\n if (!supported) {\n throw new CliError(`No supported Rust tidas artifact exists for ${platform}/${arch}.`, {\n code: 'DATASET_IMPORT_LCA_PLATFORM_UNSUPPORTED',\n exitCode: 69,\n details: {\n supported: ['linux/x64', 'linux/arm64', 'darwin/x64', 'darwin/arm64', 'win32/x64'],\n },\n });\n }\n}\n\nfunction requirePositiveInteger(flag: string, value: number | undefined): void {\n if (value !== undefined && (!Number.isSafeInteger(value) || value <= 0)) {\n throw new CliError(`${flag} must be a positive integer.`, {\n code: 'DATASET_IMPORT_LCA_RUNTIME_BOUND_INVALID',\n exitCode: 2,\n });\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction includesValue<T extends readonly string[]>(values: T, value: unknown): value is T[number] {\n return typeof value === 'string' && values.includes(value);\n}\n\nfunction isExitClass(value: unknown): value is TidasExitClass {\n return typeof value === 'string' && Object.hasOwn(EXIT_CODES, value);\n}\n\nexport const __testInternals = {\n assertImportContract,\n assertSupportedPlatform,\n buildImportArgs,\n normalizeSourceFormat,\n normalizeTarget,\n parseOperationReport,\n readCompatibleVersion,\n readProcessReport,\n requirePositiveInteger,\n resolveTidasBinary,\n};\n"]}