@tiinex/core 0.3.0 → 0.5.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.
- package/package.json +8 -6
- package/src/public/index.js +21 -0
- package/src/public/node.js +1 -0
- package/src/release/plan.mjs +18 -5
- package/src/release/run.mjs +7 -5
- package/src/tooling/portable/adapters/cli/cli.command-input.js +3 -0
- package/src/tooling/portable/adapters/cli/cli.common-output.js +23 -0
- package/src/tooling/portable/adapters/cli/cli.handoff-manufacture.js +3 -1
- package/src/tooling/portable/adapters/cli/cli.help.js +18 -2
- package/src/tooling/portable/adapters/cli/cli.material-policy.js +0 -1
- package/src/tooling/portable/adapters/cli/cli.operator-bridge.js +0 -13
- package/src/tooling/portable/adapters/cli/cli.run.js +1 -1
- package/src/tooling/portable/adapters/cli/cli.source-frontier-comparison.js +50 -0
- package/src/tooling/portable/adapters/node/handoff.manufacture.js +14 -0
- package/src/tooling/portable/adapters/node/handoff.manufacture.packageParent.js +80 -16
- package/src/tooling/portable/adapters/node/handoff.manufacture.requirements.js +44 -11
- package/src/tooling/portable/adapters/node/handoff.manufacture.scope.js +96 -1
- package/src/tooling/portable/adapters/node/sourceFrontierComparison.js +194 -0
- package/src/tooling/portable/comparison/sourceFrontierComparison.js +501 -0
- package/src/tooling/portable/grounding/grounding.readiness.js +8 -4
- package/src/tooling/portable/grounding/grounding.readiness.support.js +63 -1
- package/src/tooling/portable/handoff/contextAudit.js +21 -1
- package/src/tooling/portable/handoff/materialClosure.descriptor.js +1 -1
- package/src/tooling/portable/handoff/materialClosure.materials.js +1 -1
- package/src/tooling/portable/handoff/recipientV2.artifacts.js +1 -1
- package/src/tooling/portable/handoff/recipientV2.inspect.helpers.js +6 -1
- package/src/tooling/portable/handoff/recipientV2.packageV1.build.js +93 -19
- package/src/tooling/portable/handoff/recipientV2.packageV1.contract.js +30 -1
- package/src/tooling/portable/handoff/recipientV2.packageV1.inspect.helpers.js +8 -2
- package/src/tooling/portable/handoff/recipientV2.packageV1.inspect.js +122 -10
- package/src/tooling/portable/handoff/recipientV2.packageV1.js +2 -1
- package/src/tooling/portable/handoff/recipientV2.packageV1.secure.js +87 -0
- package/src/tooling/portable/handoff/recipientV2.packageV1.shared.js +9 -1
- package/src/tooling/portable/handoff/recipientV2.topology.js +2 -2
- package/src/tooling/portable/handoff/recipientV2.topology.materials.js +9 -1
- package/src/tooling/portable/handoff/transportEnvelopeV1.js +76 -0
- package/src/tooling/portable/index.js +4 -0
- package/src/tooling/portable/operation.catalog.js +8 -0
- package/src/tooling/portable/operation.catalog.package.js +0 -8
- package/src/transport/secureTransportV1.js +339 -0
- package/src/tooling/portable/handoff/sourceFrontierComparison.js +0 -186
|
@@ -21,7 +21,7 @@ export async function projectManufacturingRequirements({ handoff, workspaceId, h
|
|
|
21
21
|
if (!runtime) throw new Error(`portable.handoff-manufacture.route.workspace-unresolved:${routeWorkspaceId}`);
|
|
22
22
|
const routeMarkdown = routeWorkspaceId === workspaceId && routePath === handoffPath
|
|
23
23
|
? handoff.markdown
|
|
24
|
-
: await
|
|
24
|
+
: await readWorkspaceRuntimeText(runtime, routePath, 'portable.handoff-manufacture.route.path');
|
|
25
25
|
const projected = routeWorkspaceId === workspaceId && routePath === handoffPath
|
|
26
26
|
? primary
|
|
27
27
|
: projectHandoffMaterialRequirements({ id: routePath, path: routePath, semanticStatus: 'unknown', markdown: routeMarkdown });
|
|
@@ -75,7 +75,7 @@ export async function resolveWorkspaceRequirementMaterials(requirements, workspa
|
|
|
75
75
|
const targetRuntime = workspaceRuntimeById.get(targetWorkspaceId);
|
|
76
76
|
const entry = targetRuntime ? entryFromEnumeration(targetRuntime.enumeration, targetPath) : null;
|
|
77
77
|
if (entry) {
|
|
78
|
-
out.push(materialCandidateFromWorkspaceEntry(requirement, targetWorkspaceId, targetPath, entry, targetRuntime.enumeration));
|
|
78
|
+
out.push(materialCandidateFromWorkspaceEntry(requirement, targetWorkspaceId, targetPath, entry, targetRuntime.enumeration, targetRuntime));
|
|
79
79
|
continue;
|
|
80
80
|
}
|
|
81
81
|
}
|
|
@@ -85,20 +85,28 @@ export async function resolveWorkspaceRequirementMaterials(requirements, workspa
|
|
|
85
85
|
if (workspaceQualified) {
|
|
86
86
|
const targetRuntime = workspaceRuntimeById.get(workspaceQualified.workspaceId);
|
|
87
87
|
const entry = targetRuntime ? entryFromEnumeration(targetRuntime.enumeration, workspaceQualified.path) : null;
|
|
88
|
-
if (entry) out.push(materialCandidateFromWorkspaceEntry(requirement, workspaceQualified.workspaceId, workspaceQualified.path, entry, targetRuntime.enumeration));
|
|
88
|
+
if (entry) out.push(materialCandidateFromWorkspaceEntry(requirement, workspaceQualified.workspaceId, workspaceQualified.path, entry, targetRuntime.enumeration, targetRuntime));
|
|
89
89
|
continue;
|
|
90
90
|
}
|
|
91
91
|
if (isExternalReference(target) || target.startsWith('#')) continue;
|
|
92
92
|
const routeWorkspaceId = String(requirement.routeWorkspaceId || [...workspaceRuntimeById.keys()][0] || '');
|
|
93
93
|
const runtime = workspaceRuntimeById.get(routeWorkspaceId);
|
|
94
94
|
if (!runtime) continue;
|
|
95
|
+
if (!runtime.root) {
|
|
96
|
+
const relative = resolveRelativeWorkspaceTarget(String(requirement.routePath || ''), target);
|
|
97
|
+
if (!relative) continue;
|
|
98
|
+
const entry = entryFromEnumeration(runtime.enumeration, relative);
|
|
99
|
+
if (!entry) continue;
|
|
100
|
+
out.push(materialCandidateFromWorkspaceEntry(requirement, routeWorkspaceId, relative, entry, runtime.enumeration, runtime));
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
95
103
|
const handoffDir = path.dirname(path.resolve(runtime.root, String(requirement.routePath || '')));
|
|
96
104
|
const absolute = path.resolve(handoffDir, decodeURIComponent(target.split('#')[0]));
|
|
97
105
|
if (!inside(runtime.root, absolute)) continue;
|
|
98
106
|
const relative = normalizeRelativePath(path.relative(runtime.root, absolute));
|
|
99
107
|
const entry = entryFromEnumeration(runtime.enumeration, relative);
|
|
100
108
|
if (!entry) continue;
|
|
101
|
-
out.push(materialCandidateFromWorkspaceEntry(requirement, routeWorkspaceId, relative, entry, runtime.enumeration));
|
|
109
|
+
out.push(materialCandidateFromWorkspaceEntry(requirement, routeWorkspaceId, relative, entry, runtime.enumeration, runtime));
|
|
102
110
|
}
|
|
103
111
|
return out;
|
|
104
112
|
}
|
|
@@ -207,7 +215,7 @@ async function resolveDerivedDependencyMaterial(requirement, sourceMaterial, wor
|
|
|
207
215
|
if (requirement.targetWorkspaceId && requirement.targetPath) {
|
|
208
216
|
const runtime = workspaceRuntimeById.get(String(requirement.targetWorkspaceId));
|
|
209
217
|
const entry = runtime ? entryFromEnumeration(runtime.enumeration, requirement.targetPath) : null;
|
|
210
|
-
if (entry) return materialCandidateFromWorkspaceEntry(requirement, requirement.targetWorkspaceId, requirement.targetPath, entry, runtime.enumeration);
|
|
218
|
+
if (entry) return materialCandidateFromWorkspaceEntry(requirement, requirement.targetWorkspaceId, requirement.targetPath, entry, runtime.enumeration, runtime);
|
|
211
219
|
}
|
|
212
220
|
const target = String(requirement.reference?.target || '');
|
|
213
221
|
const sourcePath = String(sourceMaterial.provenance?.sourcePath || '').trim();
|
|
@@ -291,7 +299,19 @@ export function entryFromEnumeration(enumeration = {}, relative = '') {
|
|
|
291
299
|
return matches.length === 1 ? matches[0] : null;
|
|
292
300
|
}
|
|
293
301
|
|
|
294
|
-
export function materialCandidateFromWorkspaceEntry(requirement, workspaceId, relative, entry, enumeration) {
|
|
302
|
+
export function materialCandidateFromWorkspaceEntry(requirement, workspaceId, relative, entry, enumeration, runtime = {}) {
|
|
303
|
+
const packageParentProvider = String(runtime.provider || '').startsWith('qualified-package-parent-workspace');
|
|
304
|
+
const materializationSource = enumeration?.materialization?.source || {};
|
|
305
|
+
const provenance = packageParentProvider
|
|
306
|
+
? Object.freeze({
|
|
307
|
+
workspaceId,
|
|
308
|
+
path: relative,
|
|
309
|
+
boundary: '.',
|
|
310
|
+
parentPackagePath: String(materializationSource.parentPackagePath || ''),
|
|
311
|
+
parentPackageSha256: String(materializationSource.parentPackageSha256 || ''),
|
|
312
|
+
parentWorkspaceArchivePath: String(materializationSource.parentWorkspaceArchivePath || '')
|
|
313
|
+
})
|
|
314
|
+
: Object.freeze({ workspaceId, path: relative, boundary: '.' });
|
|
295
315
|
return Object.freeze({
|
|
296
316
|
requirementId: requirement.id,
|
|
297
317
|
referenceTarget: String(requirement.reference?.target || requirement.referenceTarget || ''),
|
|
@@ -300,10 +320,14 @@ export function materialCandidateFromWorkspaceEntry(requirement, workspaceId, re
|
|
|
300
320
|
bytes: entry.bytes,
|
|
301
321
|
sha256: entry.sha256,
|
|
302
322
|
mediaType: entry.mediaType,
|
|
303
|
-
providerId: 'node-workspace-enumerator',
|
|
304
|
-
providerKind: 'qualified-local-workspace',
|
|
305
|
-
provenance
|
|
306
|
-
authority: Object.freeze({
|
|
323
|
+
providerId: packageParentProvider ? 'package-parent-workspace-enumerator' : 'node-workspace-enumerator',
|
|
324
|
+
providerKind: packageParentProvider ? 'qualified-package-parent-workspace' : 'qualified-local-workspace',
|
|
325
|
+
provenance,
|
|
326
|
+
authority: Object.freeze({
|
|
327
|
+
localIdentityQualified: true,
|
|
328
|
+
packageParentWorkspaceQualified: packageParentProvider,
|
|
329
|
+
completenessEvidenceFingerprint: enumeration.evidence.entriesFingerprint
|
|
330
|
+
})
|
|
307
331
|
});
|
|
308
332
|
}
|
|
309
333
|
|
|
@@ -316,7 +340,7 @@ function materialCandidateFromWorkspaceBinding(requirement, binding, workspaceRu
|
|
|
316
340
|
const runtime = workspaceRuntimeById.get(workspaceId);
|
|
317
341
|
const entry = runtime ? entryFromEnumeration(runtime.enumeration, relative) : null;
|
|
318
342
|
if (!entry) return null;
|
|
319
|
-
return materialCandidateFromWorkspaceEntry(requirement, workspaceId, relative, entry, runtime.enumeration);
|
|
343
|
+
return materialCandidateFromWorkspaceEntry(requirement, workspaceId, relative, entry, runtime.enumeration, runtime);
|
|
320
344
|
}
|
|
321
345
|
|
|
322
346
|
async function materialCandidateFromBinding(requirement, binding, workspaceRoot) {
|
|
@@ -343,6 +367,15 @@ async function readWorkspaceText(root, relative, code) {
|
|
|
343
367
|
return readFile(absolute, 'utf8');
|
|
344
368
|
}
|
|
345
369
|
|
|
370
|
+
async function readWorkspaceRuntimeText(runtime, relative, code) {
|
|
371
|
+
if (runtime?.root) return readWorkspaceText(runtime.root, relative, code);
|
|
372
|
+
const entry = entryFromEnumeration(runtime?.enumeration, relative);
|
|
373
|
+
if (!entry) throw new Error(`${code}.unresolved`);
|
|
374
|
+
const markdown = decodeUtf8(entry.data);
|
|
375
|
+
if (!markdown) throw new Error(`${code}.non-text`);
|
|
376
|
+
return markdown;
|
|
377
|
+
}
|
|
378
|
+
|
|
346
379
|
|
|
347
380
|
export function normalizeRelativePath(value = '') { return String(value || '').replace(/\\/g, '/').replace(/^\/+/, '').split('/').filter((part) => part && part !== '.').join('/'); }
|
|
348
381
|
function inside(root, absolute) { const relative = path.relative(root, absolute); return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative)); }
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { parseArtifactMarkdown } from '../../../../artifacts/artifact.parse.js';
|
|
2
|
+
import { parseWorkspaceQualifiedReference } from '../../handoff/workspaceQualifiedReference.js';
|
|
2
3
|
import { safeWorkspaceToken } from './handoff.manufacture.multiRoot.js';
|
|
3
4
|
import {
|
|
4
5
|
decodeUtf8,
|
|
@@ -136,7 +137,7 @@ export function expandBoundedParentBoundaryClosure(input = {}) {
|
|
|
136
137
|
fields: Object.freeze({ RouteWorkspace: workspaceId, RoutePath: sourcePath, TargetWorkspace: workspaceId, TargetPath: parentPath, Reference: reference })
|
|
137
138
|
});
|
|
138
139
|
dependencies.push(requirement);
|
|
139
|
-
materials.push(materialCandidateFromWorkspaceEntry(requirement, workspaceId, parentPath, parentEntry, runtime.enumeration));
|
|
140
|
+
materials.push(materialCandidateFromWorkspaceEntry(requirement, workspaceId, parentPath, parentEntry, runtime.enumeration, runtime));
|
|
140
141
|
}
|
|
141
142
|
}
|
|
142
143
|
}
|
|
@@ -145,3 +146,97 @@ export function expandBoundedParentBoundaryClosure(input = {}) {
|
|
|
145
146
|
materials: Object.freeze(materials)
|
|
146
147
|
});
|
|
147
148
|
}
|
|
149
|
+
|
|
150
|
+
export function expandRouteParentBoundaryClosure(input = {}) {
|
|
151
|
+
const requirements = input.requirements || {};
|
|
152
|
+
const dependencies = [...(requirements.dependencies || [])];
|
|
153
|
+
const materials = [...(input.materials || [])];
|
|
154
|
+
const materializationByWorkspaceId = new Map((input.workspaceMaterializations || []).map((item) => [String(item.id || item.workspaceId || ''), item]));
|
|
155
|
+
const seenRequirementIds = new Set(dependencies.map((item) => String(item.id || '')));
|
|
156
|
+
const seenRouteTargets = new Set(dependencies.map((item) => `${String(item.routeWorkspaceId || '')}\0${normalizeRelativePath(item.routePath || '')}\0${String(item.targetWorkspaceId || '')}\0${normalizeRelativePath(item.targetPath || '')}`));
|
|
157
|
+
|
|
158
|
+
for (const route of input.routeSpecs || []) {
|
|
159
|
+
const routeWorkspaceId = String(route.workspaceId || '').trim();
|
|
160
|
+
const routePath = normalizeRelativePath(route.path || route.workspaceRelativePath || '');
|
|
161
|
+
if (!routeWorkspaceId || !routePath) continue;
|
|
162
|
+
let currentWorkspaceId = routeWorkspaceId;
|
|
163
|
+
let currentPath = routePath;
|
|
164
|
+
const visited = new Set();
|
|
165
|
+
for (let depth = 0; depth < 256; depth += 1) {
|
|
166
|
+
const visitKey = `${currentWorkspaceId}\0${currentPath}`;
|
|
167
|
+
if (visited.has(visitKey)) break;
|
|
168
|
+
visited.add(visitKey);
|
|
169
|
+
const runtime = input.workspaceRuntimeById?.get(currentWorkspaceId);
|
|
170
|
+
const currentEntry = runtime ? entryFromEnumeration(runtime.enumeration, currentPath) : null;
|
|
171
|
+
if (!currentEntry) break;
|
|
172
|
+
const markdown = decodeUtf8(currentEntry.data);
|
|
173
|
+
if (!markdown) break;
|
|
174
|
+
let parent;
|
|
175
|
+
try { parent = parseArtifactMarkdown(markdown).envelope?.parent || {}; } catch { break; }
|
|
176
|
+
const reference = String(parent.trace || (parent.originEntries || []).find((item) => String(item?.label || '').trim() === 'relative')?.target || '').trim();
|
|
177
|
+
if (!reference) break;
|
|
178
|
+
|
|
179
|
+
let targetWorkspaceId = currentWorkspaceId;
|
|
180
|
+
let targetPath = '';
|
|
181
|
+
const qualified = parseWorkspaceQualifiedReference(reference);
|
|
182
|
+
if (qualified) {
|
|
183
|
+
targetWorkspaceId = qualified.workspaceId;
|
|
184
|
+
targetPath = normalizeRelativePath(qualified.path);
|
|
185
|
+
} else {
|
|
186
|
+
if (isExternalReference(reference) || reference.startsWith('#')) break;
|
|
187
|
+
targetPath = resolveRelativeWorkspaceTarget(currentPath, reference);
|
|
188
|
+
}
|
|
189
|
+
if (!targetWorkspaceId || !targetPath) break;
|
|
190
|
+
|
|
191
|
+
const targetRuntime = input.workspaceRuntimeById?.get(targetWorkspaceId);
|
|
192
|
+
const targetEntry = targetRuntime ? entryFromEnumeration(targetRuntime.enumeration, targetPath) : null;
|
|
193
|
+
const targetMaterialization = materializationByWorkspaceId.get(targetWorkspaceId);
|
|
194
|
+
const targetAlreadyCarried = workspaceMaterializationIncludesPath(targetMaterialization, targetPath);
|
|
195
|
+
const routeTargetKey = `${routeWorkspaceId}\0${routePath}\0${targetWorkspaceId}\0${targetPath}`;
|
|
196
|
+
if (!targetAlreadyCarried && !seenRouteTargets.has(routeTargetKey)) {
|
|
197
|
+
seenRouteTargets.add(routeTargetKey);
|
|
198
|
+
const id = `route-parent:${safeWorkspaceToken(routeWorkspaceId)}:${sha256Text(`${routePath}\0${currentWorkspaceId}\0${currentPath}\0${targetWorkspaceId}\0${targetPath}`).slice(0, 20)}`;
|
|
199
|
+
if (!seenRequirementIds.has(id)) {
|
|
200
|
+
seenRequirementIds.add(id);
|
|
201
|
+
const requirement = Object.freeze({
|
|
202
|
+
id,
|
|
203
|
+
name: `Route Parent boundary for ${currentWorkspaceId}:${currentPath}`,
|
|
204
|
+
classification: 'parent-boundary',
|
|
205
|
+
material: 'exact detached Parent continuity dependency',
|
|
206
|
+
purpose: 'preserve selected Handoff route continuity across a Workspace carriage boundary without requiring complete unrelated Workspace carriage',
|
|
207
|
+
availability: 'declared',
|
|
208
|
+
materialReference: reference,
|
|
209
|
+
reference: Object.freeze({ form: qualified ? 'parent-workspace-qualified-target' : 'parent-relative-target', raw: reference, label: '', target: reference, exactTargetDeclared: true }),
|
|
210
|
+
routeWorkspaceId,
|
|
211
|
+
routePath,
|
|
212
|
+
sourceWorkspaceId: currentWorkspaceId,
|
|
213
|
+
sourcePath: currentPath,
|
|
214
|
+
targetWorkspaceId,
|
|
215
|
+
targetPath,
|
|
216
|
+
sourceRequirementId: '',
|
|
217
|
+
fields: Object.freeze({ RouteWorkspace: routeWorkspaceId, RoutePath: routePath, SourceWorkspace: currentWorkspaceId, SourcePath: currentPath, TargetWorkspace: targetWorkspaceId, TargetPath: targetPath, Reference: reference })
|
|
218
|
+
});
|
|
219
|
+
dependencies.push(requirement);
|
|
220
|
+
if (targetEntry) materials.push(materialCandidateFromWorkspaceEntry(requirement, targetWorkspaceId, targetPath, targetEntry, targetRuntime.enumeration, targetRuntime));
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (!targetEntry) break;
|
|
224
|
+
currentWorkspaceId = targetWorkspaceId;
|
|
225
|
+
currentPath = targetPath;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return Object.freeze({
|
|
230
|
+
requirements: Object.freeze({ ...requirements, dependencies: Object.freeze(dependencies), counts: Object.freeze({ ...(requirements.counts || {}), dependencies: dependencies.length }) }),
|
|
231
|
+
materials: Object.freeze(materials)
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function workspaceMaterializationIncludesPath(materialization = null, targetPath = '') {
|
|
236
|
+
if (!materialization) return false;
|
|
237
|
+
const state = String(materialization.state || materialization.materialization || '');
|
|
238
|
+
if (state === 'complete') return true;
|
|
239
|
+
if (state !== 'bounded') return false;
|
|
240
|
+
const normalized = normalizeRelativePath(targetPath);
|
|
241
|
+
return (materialization.includedEntries || materialization.entries || []).some((entry) => normalizeRelativePath(entry.path || '') === normalized);
|
|
242
|
+
}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { enumerateNodeWorkspace } from './handoff.manufacture.enumeration.js';
|
|
3
|
+
import { loadNodePortableInput } from '../../input/node.input.js';
|
|
4
|
+
import { inspectRecipientFacingV2Topology } from '../../handoff/recipientV2.inspect.js';
|
|
5
|
+
import { handoffWorkspaceProviderForId, listHandoffWorkspaceEntries } from '../../handoff/workspaceByteProvider.js';
|
|
6
|
+
import { safeWorkspaceToken } from './handoff.manufacture.multiRoot.js';
|
|
7
|
+
import { portableFinding } from '../../findings.js';
|
|
8
|
+
import {
|
|
9
|
+
PORTABLE_SOURCE_FRONTIER_SCHEMA_ID,
|
|
10
|
+
compareOrReconcilePortableSourceFrontiers,
|
|
11
|
+
createPortableSourceFrontier
|
|
12
|
+
} from '../../comparison/sourceFrontierComparison.js';
|
|
13
|
+
|
|
14
|
+
export const PORTABLE_NODE_SOURCE_FRONTIER_INPUT_SCHEMA_ID = 'tiinex.portable.node-source-frontier-input.v1';
|
|
15
|
+
|
|
16
|
+
export async function prepareNodeSourceFrontierComparisonInput(input = {}, options = {}) {
|
|
17
|
+
if (input.base || input.incoming || input.current) {
|
|
18
|
+
if (!input.base || !input.incoming || !input.current) return { base: invalidFrontier('base', 'three-way-input-incomplete'), incoming: invalidFrontier('incoming', 'three-way-input-incomplete'), current: invalidFrontier('current', 'three-way-input-incomplete') };
|
|
19
|
+
const [base, incoming, current] = await Promise.all([
|
|
20
|
+
prepareNodeSourceFrontier(input.base, { ...options, side: 'base' }),
|
|
21
|
+
prepareNodeSourceFrontier(input.incoming, { ...options, side: 'incoming' }),
|
|
22
|
+
prepareNodeSourceFrontier(input.current, { ...options, side: 'current' })
|
|
23
|
+
]);
|
|
24
|
+
return Object.freeze({ base, incoming, current });
|
|
25
|
+
}
|
|
26
|
+
const [left, right] = await Promise.all([
|
|
27
|
+
prepareNodeSourceFrontier(input.left, { ...options, side: 'left' }),
|
|
28
|
+
prepareNodeSourceFrontier(input.right, { ...options, side: 'right' })
|
|
29
|
+
]);
|
|
30
|
+
return Object.freeze({ left, right });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function compareNodeSourceFrontiers(input = {}, options = {}) {
|
|
34
|
+
const normalized = await prepareNodeSourceFrontierComparisonInput(input, options);
|
|
35
|
+
return compareOrReconcilePortableSourceFrontiers(normalized);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export async function prepareNodeSourceFrontier(descriptor = {}, options = {}) {
|
|
39
|
+
if (descriptor?.schema === PORTABLE_SOURCE_FRONTIER_SCHEMA_ID) return createPortableSourceFrontier(descriptor);
|
|
40
|
+
const kind = String(descriptor?.kind || '').trim().toLowerCase();
|
|
41
|
+
if (!kind) return invalidFrontier(options.side || '', 'input-kind-required', portableFinding('error', 'portable.source-frontier.node.kind-required', 'Node source-frontier input requires an explicit kind; filesystem/package type is never guessed.', { side: options.side || '' }));
|
|
42
|
+
if (kind === 'local-workspace') return frontierFromLocalWorkspace(descriptor, options);
|
|
43
|
+
if (kind === 'local-frontier') return frontierFromLocalWorkspaceSet(descriptor, options);
|
|
44
|
+
if (kind === 'handoff-package') return frontierFromHandoffPackage(descriptor, options);
|
|
45
|
+
if (kind === 'qualified-workspace-provider') return frontierFromQualifiedProvider(descriptor, options);
|
|
46
|
+
if (kind === 'normalized-frontier') return createPortableSourceFrontier(descriptor.frontier || descriptor.value || {});
|
|
47
|
+
return invalidFrontier(options.side || '', 'input-kind-unsupported', portableFinding('error', 'portable.source-frontier.node.kind-unsupported', 'Node source-frontier input kind is unsupported.', { kind, side: options.side || '' }));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function frontierFromLocalWorkspace(descriptor, options) {
|
|
51
|
+
const rootText = String(descriptor.root || descriptor.path || descriptor.workspaceRoot || '').trim();
|
|
52
|
+
if (!rootText) return invalidFrontier(options.side || '', 'local-workspace-root-required', portableFinding('error', 'portable.source-frontier.node.local-root-required', 'Local Workspace comparison input requires an explicit root path.', { side: options.side || '' }));
|
|
53
|
+
const root = path.resolve(rootText);
|
|
54
|
+
const workspaceId = safeWorkspaceToken(descriptor.workspaceId || descriptor.id || path.basename(root) || 'workspace');
|
|
55
|
+
try {
|
|
56
|
+
const enumeration = await enumerateNodeWorkspace(root, enumerationOptions(descriptor, options, workspaceId));
|
|
57
|
+
if (enumeration.status !== 'qualified-complete') return invalidFrontier(options.side || '', `local-enumeration-${enumeration.status}`, portableFinding('error', 'portable.source-frontier.node.local-enumeration-unqualified', 'Local Workspace source enumeration did not qualify as complete under the shared manufacture snapshot contract.', { workspaceId, status: enumeration.status }));
|
|
58
|
+
return createPortableSourceFrontier({
|
|
59
|
+
id: descriptor.label || `${options.side || 'input'}:${workspaceId}`,
|
|
60
|
+
source: { kind: 'local-workspace', ref: root, snapshotContract: 'shared-handoff-manufacture-node-enumeration-v1' },
|
|
61
|
+
workspaces: [workspaceFromEnumeration(workspaceId, enumeration, root)],
|
|
62
|
+
boundary: 'Local source is enumerated by the same deterministic Node Workspace enumerator used by Handoff manufacture; excluded directories and symlink policy therefore remain shared.'
|
|
63
|
+
});
|
|
64
|
+
} catch (error) {
|
|
65
|
+
return createPortableSourceFrontier({ id: descriptor.label || options.side || '', state: 'qualification-error', source: { kind: 'local-workspace', ref: root }, workspaces: [{ workspaceId, state: 'unavailable', reason: 'local-workspace-read-failed', source: { kind: 'local-workspace', ref: root } }], findings: [portableFinding('error', 'portable.source-frontier.node.local-read-failed', 'Local Workspace source could not be enumerated.', { workspaceId, ref: root, detail: String(error?.message || error || '') })] });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function frontierFromLocalWorkspaceSet(descriptor, options) {
|
|
70
|
+
const specs = Array.isArray(descriptor.workspaces) ? descriptor.workspaces : [];
|
|
71
|
+
if (!specs.length) return invalidFrontier(options.side || '', 'local-frontier-workspaces-required', portableFinding('error', 'portable.source-frontier.node.local-frontier-empty', 'Local multi-Workspace frontier requires at least one explicit Workspace root binding.', { side: options.side || '' }));
|
|
72
|
+
const results = await Promise.all(specs.map(async (spec, index) => {
|
|
73
|
+
const rootText = String(spec.root || spec.path || spec.workspaceRoot || '').trim();
|
|
74
|
+
if (!rootText) return { workspaceId: String(spec.workspaceId || spec.id || `workspace-${index + 1}`), state: 'unavailable', reason: 'local-workspace-root-required', source: { kind: 'local-workspace' }, finding: portableFinding('error', 'portable.source-frontier.node.local-root-required', 'Local frontier Workspace binding requires an explicit root path.', { index }) };
|
|
75
|
+
const root = path.resolve(rootText);
|
|
76
|
+
const workspaceId = safeWorkspaceToken(spec.workspaceId || spec.id || '');
|
|
77
|
+
if (!spec.workspaceId && !spec.id) return { workspaceId: `unresolved-${index + 1}`, state: 'qualification-error', reason: 'workspace-id-required', source: { kind: 'local-workspace', ref: root }, finding: portableFinding('error', 'portable.source-frontier.node.local-frontier-id-required', 'Every local multi-Workspace root requires an explicit Workspace id.', { ref: root }) };
|
|
78
|
+
try {
|
|
79
|
+
const enumeration = await enumerateNodeWorkspace(root, enumerationOptions({ ...descriptor, ...spec }, options, workspaceId));
|
|
80
|
+
if (enumeration.status !== 'qualified-complete') return { workspaceId, state: 'qualification-error', reason: `enumeration-${enumeration.status}`, source: { kind: 'local-workspace', ref: root }, finding: portableFinding('error', 'portable.source-frontier.node.local-enumeration-unqualified', 'Local frontier Workspace did not qualify as a complete manufacture-equivalent snapshot.', { workspaceId, status: enumeration.status }) };
|
|
81
|
+
return workspaceFromEnumeration(workspaceId, enumeration, root);
|
|
82
|
+
} catch (error) {
|
|
83
|
+
return { workspaceId, state: 'unavailable', reason: 'local-workspace-read-failed', source: { kind: 'local-workspace', ref: root }, finding: portableFinding('error', 'portable.source-frontier.node.local-read-failed', 'Local frontier Workspace source could not be enumerated.', { workspaceId, ref: root, detail: String(error?.message || error || '') }) };
|
|
84
|
+
}
|
|
85
|
+
}));
|
|
86
|
+
const findings = results.map((item) => item.finding).filter(Boolean);
|
|
87
|
+
return createPortableSourceFrontier({
|
|
88
|
+
id: descriptor.label || options.side || 'local-frontier',
|
|
89
|
+
state: findings.some((item) => item.severity === 'error') ? 'qualification-error' : 'qualified',
|
|
90
|
+
source: { kind: 'local-frontier', workspaceCount: specs.length, snapshotContract: 'shared-handoff-manufacture-node-enumeration-v1' },
|
|
91
|
+
workspaces: results.map(({ finding, ...workspace }) => workspace),
|
|
92
|
+
findings,
|
|
93
|
+
boundary: 'Explicit local multi-Workspace roots, each enumerated by the shared Handoff-manufacture Workspace snapshot contract. Workspace ids are never inferred across the multi-root boundary.'
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function frontierFromHandoffPackage(descriptor, options) {
|
|
98
|
+
const packagePathText = String(descriptor.path || descriptor.packagePath || descriptor.root || '').trim();
|
|
99
|
+
if (!packagePathText) return invalidFrontier(options.side || '', 'handoff-package-path-required', portableFinding('error', 'portable.source-frontier.node.package-path-required', 'Handoff-package comparison input requires an explicit package path.', { side: options.side || '' }));
|
|
100
|
+
const packagePath = path.resolve(packagePathText);
|
|
101
|
+
let material;
|
|
102
|
+
try {
|
|
103
|
+
material = await loadNodePortableInput([packagePath], { maxFiles: descriptor.maxCarrierFiles || options.maxCarrierFiles || 10000, maxTextBytes: descriptor.maxTextBytes || options.maxTextBytes || 16 * 1024 * 1024 });
|
|
104
|
+
} catch (error) {
|
|
105
|
+
return invalidFrontier(options.side || '', 'handoff-package-read-failed', portableFinding('error', 'portable.source-frontier.node.package-read-failed', 'Handoff package could not be read by current trusted Core Tooling.', { ref: packagePath, detail: String(error?.message || error || '') }));
|
|
106
|
+
}
|
|
107
|
+
const loaderErrors = (material.findings || []).filter((item) => item.severity === 'error');
|
|
108
|
+
const inspection = inspectRecipientFacingV2Topology(material);
|
|
109
|
+
if (loaderErrors.length || inspection.status !== 'valid') {
|
|
110
|
+
return createPortableSourceFrontier({
|
|
111
|
+
id: descriptor.label || options.side || '', state: 'qualification-error', source: { kind: 'handoff-package', ref: packagePath, qualification: inspection.status || 'invalid' }, workspaces: [],
|
|
112
|
+
findings: [...loaderErrors, ...(inspection.findings || []).filter((item) => item.severity === 'error').map((item) => portableFinding('error', item.code || 'portable.source-frontier.package-unqualified', item.message || 'Handoff package qualification failed.', { ref: item.path || packagePath }))],
|
|
113
|
+
boundary: 'Received Handoff package failed current trusted Core package qualification; no Workspace source paths are projected for comparison.'
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const availableIds = new Set([...(inspection.workspaces || []).map((item) => String(item.workspaceId || '')), ...(inspection.sealedWorkspaces || []).map((item) => String(item.workspaceId || ''))].filter(Boolean));
|
|
118
|
+
const selectors = normalizeSelectors(descriptor.workspaceIds || descriptor.select || descriptor.workspaces);
|
|
119
|
+
const ids = selectors.length ? selectors : [...availableIds].sort();
|
|
120
|
+
const openedById = new Map((descriptor.openedWorkspaces || []).map((item) => [String(item?.workspaceId || ''), item]).filter(([id]) => id));
|
|
121
|
+
const sealedById = new Map((inspection.sealedWorkspaces || []).map((item) => [String(item.workspaceId || ''), item]).filter(([id]) => id));
|
|
122
|
+
const workspaces = ids.map((workspaceId) => {
|
|
123
|
+
if (!availableIds.has(workspaceId)) return { workspaceId, state: 'unavailable', reason: 'workspace-not-carried', source: { kind: 'handoff-package', ref: packagePath } };
|
|
124
|
+
const sealedBinding = sealedById.get(workspaceId) || null;
|
|
125
|
+
const opened = openedById.get(workspaceId) || null;
|
|
126
|
+
if (sealedBinding && authorizedOpenMatchesSealedBinding(opened, sealedBinding, workspaceId)) {
|
|
127
|
+
const normalized = workspaceFromProvider(workspaceId, opened.provider, { kind: 'authorized-opened-workspace-provider', packageRef: packagePath });
|
|
128
|
+
if (normalized.state === 'qualified') return normalized;
|
|
129
|
+
}
|
|
130
|
+
if (sealedBinding) return { workspaceId, state: 'locked', qualification: 'locked-qualified-sealed-binding', reason: opened ? 'authorized-open-result-does-not-match-sealed-binding' : 'password-sealed-workspace-not-opened', source: { kind: 'handoff-package', ref: packagePath, binding: 'password-sealed-workspace-byte-tree' } };
|
|
131
|
+
return workspaceFromProvider(workspaceId, inspection.workspaceByteProvider, { kind: 'qualified-handoff-package-workspace', packageRef: packagePath });
|
|
132
|
+
});
|
|
133
|
+
return createPortableSourceFrontier({
|
|
134
|
+
id: descriptor.label || options.side || path.basename(packagePath),
|
|
135
|
+
source: { kind: 'handoff-package', ref: packagePath, qualification: 'qualified-current-tooling', format: String(inspection.format || '') },
|
|
136
|
+
workspaces,
|
|
137
|
+
boundary: 'Handoff inputs are inspected by the receiver current trusted Core Tooling. Clear qualified Workspace providers expose exact path/hash source; sealed bindings remain locked unless caller supplies an already-authorized qualified opened provider.'
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function authorizedOpenMatchesSealedBinding(opened, sealedBinding, workspaceId) {
|
|
142
|
+
if (!opened || opened.state !== 'opened-qualified' || !opened.provider) return false;
|
|
143
|
+
if (String(opened.workspaceId || '') !== String(workspaceId || '')) return false;
|
|
144
|
+
if (String(opened.workspaceArtifactPath || '') !== String(sealedBinding.workspaceArtifactPath || '')) return false;
|
|
145
|
+
if (String(opened.workspaceArtifactSha256 || '').toLowerCase() !== String(sealedBinding.workspaceArtifactSha256 || '').toLowerCase()) return false;
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function frontierFromQualifiedProvider(descriptor, options) {
|
|
150
|
+
const provider = descriptor.provider || descriptor.value || null;
|
|
151
|
+
const providerIds = Array.isArray(provider?.workspaces) ? provider.workspaces.map((item) => String(item?.id || '')).filter(Boolean) : [];
|
|
152
|
+
const selectors = normalizeSelectors(descriptor.workspaceIds || descriptor.select || descriptor.workspaces);
|
|
153
|
+
const ids = selectors.length ? selectors : providerIds.sort();
|
|
154
|
+
if (!provider || !ids.length) return invalidFrontier(options.side || '', 'provider-unavailable', portableFinding('error', 'portable.source-frontier.node.provider-unavailable', 'Qualified Workspace provider input must expose at least one explicit or discoverable Workspace id.', { side: options.side || '' }));
|
|
155
|
+
return createPortableSourceFrontier({ id: descriptor.label || options.side || 'qualified-provider', source: { kind: 'qualified-workspace-provider' }, workspaces: ids.map((id) => workspaceFromProvider(id, provider, { kind: 'qualified-workspace-provider' })) });
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function workspaceFromEnumeration(workspaceId, enumeration, root) {
|
|
159
|
+
return {
|
|
160
|
+
workspaceId, state: 'qualified', qualification: 'qualified-complete-manufacture-enumeration', source: { kind: 'local-workspace', ref: root },
|
|
161
|
+
entries: (enumeration.materialization?.includedEntries || []).map((entry) => ({ path: entry.path, bytes: entry.bytes, sha256: entry.sha256 })),
|
|
162
|
+
evidence: { ...(enumeration.evidence || {}), sourceSelection: 'enumerateNodeWorkspace' }
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function workspaceFromProvider(workspaceId, provider, source) {
|
|
167
|
+
const workspace = handoffWorkspaceProviderForId(provider, workspaceId);
|
|
168
|
+
if (!workspace || workspace.state !== 'qualified') return { workspaceId, state: workspace?.state === 'locked' ? 'locked' : 'qualification-error', qualification: String(workspace?.state || 'provider-unresolved'), reason: 'qualified-workspace-provider-unresolved', source };
|
|
169
|
+
const entries = listHandoffWorkspaceEntries(provider, workspaceId);
|
|
170
|
+
return {
|
|
171
|
+
workspaceId, state: 'qualified', qualification: 'qualified-exact-package-provider', source,
|
|
172
|
+
entries: entries.map((entry) => ({ path: entry.path || entry.innerPath, bytes: entry.bytes, sha256: entry.sha256 })),
|
|
173
|
+
evidence: { providerSchema: String(provider.schema || ''), providerState: String(provider.status || ''), workspaceMode: String(workspace.mode || ''), coverage: String(workspace.materialization?.materialization || workspace.binding?.coverage || '') }
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function enumerationOptions(descriptor, options, workspaceId) {
|
|
178
|
+
return {
|
|
179
|
+
workspaceId,
|
|
180
|
+
workspaceTitle: String(descriptor.workspaceTitle || descriptor.title || workspaceId),
|
|
181
|
+
sourceMetadata: descriptor.sourceMetadata || {},
|
|
182
|
+
excludeDirectories: descriptor.excludeDirectories || options.excludeDirectories,
|
|
183
|
+
maxFiles: descriptor.maxFiles || options.maxFiles
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function normalizeSelectors(value) {
|
|
188
|
+
const list = Array.isArray(value) ? value : value ? String(value).split(',') : [];
|
|
189
|
+
return [...new Set(list.map((item) => typeof item === 'string' ? item.trim() : String(item?.workspaceId || item?.id || '').trim()).filter(Boolean))].sort();
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function invalidFrontier(id, reason, finding = null) {
|
|
193
|
+
return createPortableSourceFrontier({ id, state: 'qualification-error', source: { kind: 'unavailable', reason }, workspaces: [], findings: [finding || portableFinding('error', 'portable.source-frontier.node.input-invalid', 'Node source-frontier input is invalid.', { reason })] });
|
|
194
|
+
}
|