@tiinex/core 0.10.0 → 0.12.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 +5 -5
- package/src/tooling/portable/adapters/cli/cli.common-author.js +91 -23
- package/src/tooling/portable/adapters/node/handoff.manufacture.bootstrap.js +37 -1
- package/src/tooling/portable/adapters/node/handoff.manufacture.js +10 -0
- package/src/tooling/portable/adapters/node/handoff.manufacture.runtimeSource.js +82 -0
- package/src/tooling/portable/adapters/node/workspaceCarrier.manufacture.js +7 -1
- package/src/tooling/portable/schema/bootstrap/canonical.pack.js +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiinex/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Shared host-neutral Tiinex implementation core for artifacts, schemas, validation, lineage, grounding, Handoffs, provenance and deterministic workflows.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": true,
|
|
@@ -167,12 +167,12 @@
|
|
|
167
167
|
"type": "git",
|
|
168
168
|
"url": "git+https://github.com/Tiinex/core.git"
|
|
169
169
|
},
|
|
170
|
-
"gitHead": "
|
|
170
|
+
"gitHead": "a63df8c4736b3267ac0aefa769b1262f45b78dac",
|
|
171
171
|
"tiinexRelease": {
|
|
172
172
|
"policy": "tiinex.master-npm-release.v1",
|
|
173
|
-
"sourceCommit": "
|
|
174
|
-
"sourceTree": "
|
|
173
|
+
"sourceCommit": "a63df8c4736b3267ac0aefa769b1262f45b78dac",
|
|
174
|
+
"sourceTree": "d502ba11f95bbaf29fb7b86015df3d7b63bc82eb",
|
|
175
175
|
"repository": "Tiinex/core",
|
|
176
|
-
"previousVersion": "0.
|
|
176
|
+
"previousVersion": "0.11.0"
|
|
177
177
|
}
|
|
178
178
|
}
|
|
@@ -8,6 +8,7 @@ import { sha256Hex, utf8Bytes } from '../../../../export/package.bytes.js';
|
|
|
8
8
|
import { resolveSchemaModule } from '../../../../schemas/resolver.js';
|
|
9
9
|
import { loadNodePortableInput } from '../../input/node.input.js';
|
|
10
10
|
import { runPortableOperation } from '../../operation.catalog.js';
|
|
11
|
+
import { markPortableBootstrapCanonicalSource } from '../../providers/schema.bootstrap.provenance.js';
|
|
11
12
|
import { allocateContinuationPath, allocateDirectoryArtifactPath } from '../../../../transitions/record.transitions.js';
|
|
12
13
|
import { classifyParentRecoveryReference } from '../../../../lineage/parentRecoveryReference.js';
|
|
13
14
|
|
|
@@ -36,7 +37,7 @@ export async function runCommonAuthorCli(parsed = {}, runtime = {}) {
|
|
|
36
37
|
const parentPath = parentReference ? (parentSource ? path.resolve(parentSource) : safeWorkspaceTarget(workspaceRoot, parentReference)) : '';
|
|
37
38
|
const artifactRelativePath = requestedArtifactRelativePath || await allocateArtifactRelativePath({ workspaceRoot, targetDirectory, parentRelativePath: parentReference, schemaId, title });
|
|
38
39
|
const artifactPath = safeWorkspaceTarget(workspaceRoot, artifactRelativePath);
|
|
39
|
-
const parentRecord = parentPath ? await parentRecordFromArtifact(parentPath, parentReference, { workspaceRoot, childRelativePath: artifactRelativePath }) : {};
|
|
40
|
+
const parentRecord = parentPath ? await parentRecordFromArtifact(parentPath, parentReference, { workspaceRoot, childRelativePath: artifactRelativePath, runtime }) : {};
|
|
40
41
|
const transitionType = String(flags.transition || defaultTransition(schemaId, Boolean(parentPath))).trim();
|
|
41
42
|
const contract = buildArtifactCreationContract({ schemaId, transitionType });
|
|
42
43
|
const summary = String(flags.summary || title).trim();
|
|
@@ -128,7 +129,7 @@ async function parentRecordFromArtifact(parentPath, parentRelativePath, context
|
|
|
128
129
|
if (self.state !== 'verified') throw new Error(`portable.cli.author.parent.integrity.${self.reason || self.state}`);
|
|
129
130
|
const schemaReferenceAuthority = schemaTarget
|
|
130
131
|
? exactDeclaredSchemaReferenceAuthority(schemaId, schemaTarget)
|
|
131
|
-
: await
|
|
132
|
+
: await recoverQualifiedRuntimeSchemaReferenceAuthority(schemaId, context.runtime || {});
|
|
132
133
|
if (!schemaReferenceAuthority) throw new Error('portable.cli.author.parent.schema-authority.required');
|
|
133
134
|
return Object.freeze({
|
|
134
135
|
id: parentRelativePath,
|
|
@@ -159,48 +160,115 @@ function exactDeclaredSchemaReferenceAuthority(schemaId, schemaTarget) {
|
|
|
159
160
|
});
|
|
160
161
|
}
|
|
161
162
|
|
|
162
|
-
async function
|
|
163
|
-
const workspaceRoot = path.resolve(String(context.workspaceRoot || '.'));
|
|
164
|
-
const childRelativePath = normalizeWorkspaceRelativePath(context.childRelativePath || '');
|
|
163
|
+
export async function recoverQualifiedRuntimeSchemaReferenceAuthority(schemaId, runtime = {}) {
|
|
165
164
|
const resolution = resolveSchemaModule({ schemaId });
|
|
166
165
|
const module = resolution?.fallbackUsed ? null : resolution?.module || null;
|
|
167
166
|
const source = module?.schemaSource || null;
|
|
168
167
|
const qualification = typeof source?.qualify === 'function' ? source.qualify() : null;
|
|
169
168
|
const materialIdentity = qualification?.materialIdentity || {};
|
|
170
|
-
const bundledPath = normalizeWorkspaceRelativePath(source?.bundledPath || '');
|
|
171
169
|
const expectedSha256 = String(materialIdentity.sha256 || qualification?.checksum || '').trim().toLowerCase();
|
|
172
170
|
if (!module || String(module.id || '') !== schemaId) return null;
|
|
173
171
|
if (qualification?.state !== 'qualified' || materialIdentity?.state !== 'qualified' || String(materialIdentity.schemaId || '') !== schemaId) return null;
|
|
174
|
-
if (!
|
|
172
|
+
if (!expectedSha256) return null;
|
|
175
173
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
174
|
+
const runtimeMaterial = await loadQualifiedRuntimeSchemaMaterial(schemaId, runtime);
|
|
175
|
+
if (!runtimeMaterial) return null;
|
|
176
|
+
const markdown = String(runtimeMaterial.markdown || '');
|
|
177
|
+
const observedSha256 = markdown ? sha256Hex(utf8Bytes(markdown)).toLowerCase() : '';
|
|
178
|
+
const observedBytes = markdown ? utf8Bytes(markdown).byteLength : 0;
|
|
179
|
+
if (!observedSha256 || observedSha256 !== expectedSha256) return null;
|
|
180
|
+
if (Number(materialIdentity.bytes || 0) > 0 && observedBytes !== Number(materialIdentity.bytes)) return null;
|
|
181
|
+
if (!exactRuntimeSchemaSourceIdentity(runtimeMaterial.source || {}, materialIdentity)) return null;
|
|
180
182
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
catch { return null; }
|
|
184
|
-
if (String(localParsed.envelope?.current?.schema?.id || '').trim() !== schemaId) return null;
|
|
185
|
-
|
|
186
|
-
const childDir = path.posix.dirname(childRelativePath);
|
|
187
|
-
const preferredTarget = path.posix.relative(childDir === '.' ? '' : childDir, bundledPath);
|
|
188
|
-
if (!preferredTarget || path.posix.isAbsolute(preferredTarget)) return null;
|
|
183
|
+
const preferredTarget = durableRuntimeSchemaTarget(runtimeMaterial.source || {}, materialIdentity, runtime.defaultSchemaSource || {});
|
|
184
|
+
if (!preferredTarget) return null;
|
|
189
185
|
return Object.freeze({
|
|
190
186
|
schemaId,
|
|
191
187
|
exactTargets: Object.freeze([preferredTarget]),
|
|
192
188
|
preferredTarget,
|
|
193
189
|
resolutionState: 'qualified',
|
|
194
|
-
targetAuthority: 'qualified-
|
|
190
|
+
targetAuthority: 'qualified-runtime-canonical-schema-material',
|
|
195
191
|
resolutionEvidence: Object.freeze({
|
|
196
192
|
state: 'qualified',
|
|
197
|
-
kind: '
|
|
198
|
-
|
|
199
|
-
|
|
193
|
+
kind: 'runtime-canonical-schema-byte-match',
|
|
194
|
+
target: preferredTarget,
|
|
195
|
+
materialIdentity: Object.freeze({
|
|
196
|
+
state: 'qualified',
|
|
197
|
+
schemaId,
|
|
198
|
+
sha256: observedSha256,
|
|
199
|
+
bytes: observedBytes,
|
|
200
|
+
sourceRepository: String(materialIdentity.sourceRepository || ''),
|
|
201
|
+
sourceCommit: String(materialIdentity.sourceCommit || ''),
|
|
202
|
+
sourcePath: String(materialIdentity.sourcePath || ''),
|
|
203
|
+
sourceBlobSha: String(materialIdentity.sourceBlobSha || '')
|
|
204
|
+
})
|
|
200
205
|
})
|
|
201
206
|
});
|
|
202
207
|
}
|
|
203
208
|
|
|
209
|
+
async function loadQualifiedRuntimeSchemaMaterial(schemaId, runtime = {}) {
|
|
210
|
+
const targets = normalizeRuntimePaths(runtime.defaultSchemaMaterialPaths);
|
|
211
|
+
if (!targets.length) return null;
|
|
212
|
+
const loaded = await loadNodePortableInput(targets);
|
|
213
|
+
if ((loaded.findings || []).some((finding) => finding?.severity === 'error')) return null;
|
|
214
|
+
const decorated = decorateRuntimeSchemaMaterial(loaded, runtime.defaultSchemaSource || {});
|
|
215
|
+
const resolved = await runPortableOperation('resolve-schema-material', { ...decorated, schemaId }, {});
|
|
216
|
+
if (resolved?.status !== 'resolved' || !resolved?.material) return null;
|
|
217
|
+
const material = resolved.material;
|
|
218
|
+
if (String(material.schemaId || '') !== schemaId) return null;
|
|
219
|
+
if (material.qualification?.sourceQualified !== true || material.qualification?.representationIntegrity !== 'verified') return null;
|
|
220
|
+
return material;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function decorateRuntimeSchemaMaterial(material = {}, source = {}) {
|
|
224
|
+
const repository = String(source.repository || '');
|
|
225
|
+
const commit = String(source.commit || source.ref || '');
|
|
226
|
+
const sourcePathPrefix = String(source.sourcePathPrefix || '.topics/.schemas').replace(/\/$/, '');
|
|
227
|
+
if (!repository || !commit || !sourcePathPrefix) return material;
|
|
228
|
+
return Object.freeze({
|
|
229
|
+
...material,
|
|
230
|
+
files: Object.freeze((material.files || []).map((file) => Object.freeze({
|
|
231
|
+
...file,
|
|
232
|
+
sourceMode: 'portable-bootstrap-canonical-schema',
|
|
233
|
+
source: markPortableBootstrapCanonicalSource({
|
|
234
|
+
providerId: 'bootstrap-canonical-schema-pack',
|
|
235
|
+
repository,
|
|
236
|
+
ref: commit,
|
|
237
|
+
commit,
|
|
238
|
+
path: `${sourcePathPrefix}/${file.path}`,
|
|
239
|
+
authority: 'canonical-core',
|
|
240
|
+
qualification: 'bundled-byte-bound-canonical-snapshot',
|
|
241
|
+
remoteFetch: false,
|
|
242
|
+
cached: false
|
|
243
|
+
})
|
|
244
|
+
})))
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function exactRuntimeSchemaSourceIdentity(source = {}, materialIdentity = {}) {
|
|
249
|
+
const expected = {
|
|
250
|
+
repository: String(materialIdentity.sourceRepository || ''),
|
|
251
|
+
commit: String(materialIdentity.sourceCommit || ''),
|
|
252
|
+
path: String(materialIdentity.sourcePath || '')
|
|
253
|
+
};
|
|
254
|
+
const observed = {
|
|
255
|
+
repository: String(source.repository || ''),
|
|
256
|
+
commit: String(source.commit || source.ref || ''),
|
|
257
|
+
path: String(source.path || '')
|
|
258
|
+
};
|
|
259
|
+
if (!expected.repository || !expected.commit || !expected.path) return false;
|
|
260
|
+
return expected.repository === observed.repository && expected.commit === observed.commit && normalizeWorkspaceRelativePath(expected.path) === normalizeWorkspaceRelativePath(observed.path);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function durableRuntimeSchemaTarget(source = {}, materialIdentity = {}, runtimeSource = {}) {
|
|
264
|
+
const explicitTarget = String(runtimeSource.referenceTarget || '').trim();
|
|
265
|
+
if (explicitTarget) return explicitTarget;
|
|
266
|
+
const workspaceId = String(runtimeSource.workspaceId || '').trim();
|
|
267
|
+
const sourcePath = normalizeWorkspaceRelativePath(materialIdentity.sourcePath || source.path || '');
|
|
268
|
+
if (workspaceId && /^[A-Za-z0-9._-]+$/.test(workspaceId) && sourcePath) return `${workspaceId}::${sourcePath}`;
|
|
269
|
+
return '';
|
|
270
|
+
}
|
|
271
|
+
|
|
204
272
|
async function allocateArtifactRelativePath({ workspaceRoot, targetDirectory, parentRelativePath, schemaId, title } = {}) {
|
|
205
273
|
const directory = normalizeWorkspaceRelativePath(targetDirectory || (parentRelativePath ? path.posix.dirname(parentRelativePath) : '.topics')) || '.topics';
|
|
206
274
|
const existingPaths = await directoryArtifactPaths(workspaceRoot, directory);
|
|
@@ -14,6 +14,7 @@ export async function buildToolingBootstrapTransportFiles(input = {}) {
|
|
|
14
14
|
const delivery = normalizeDelivery(input.delivery);
|
|
15
15
|
const runtimeRoot = path.resolve(String(input.runtimeRoot || DEFAULT_RUNTIME_ROOT));
|
|
16
16
|
const runtime = await enumerateRuntimeDependencyGraph(runtimeRoot, { maxFiles: input.maxFiles });
|
|
17
|
+
const runtimeIdentity = runtimeIdentityFromEnumeration(runtime);
|
|
17
18
|
const manifest = Object.freeze({
|
|
18
19
|
schema: PORTABLE_TOOLING_BOOTSTRAP_MANIFEST_SCHEMA_ID,
|
|
19
20
|
version: 1,
|
|
@@ -30,7 +31,42 @@ export async function buildToolingBootstrapTransportFiles(input = {}) {
|
|
|
30
31
|
const summary = Object.freeze({ schema: 'tiinex.portable.tooling-bootstrap.summary.v1', delivery, manifestSha256, representationSha256: runtime.representationSha256, runtimeFiles: runtime.entries.length, runtimeBytes: runtime.totalBytes, status: delivery === 'embedded' ? 'embedded-qualified' : 'persistent-identity-verified', persistentVerification });
|
|
31
32
|
const files = [transportFile('tiinex.bootstrap/manifest.json', manifestBytes, 'tooling-bootstrap-manifest', 'portable-tooling-bootstrap-control')];
|
|
32
33
|
if (delivery === 'embedded') for (const entry of runtime.entries) files.push(transportFile(`tiinex.bootstrap/runtime/${entry.path}`, entry.data, 'tooling-bootstrap-runtime', 'portable-tooling-bootstrap-runtime'));
|
|
33
|
-
return Object.freeze({ manifest, summary, files: Object.freeze(files) });
|
|
34
|
+
return Object.freeze({ manifest, summary, files: Object.freeze(files), runtimeIdentity });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function buildToolingBootstrapRuntimeIdentity(input = {}) {
|
|
38
|
+
const runtimeRoot = path.resolve(String(input.runtimeRoot || DEFAULT_RUNTIME_ROOT));
|
|
39
|
+
const runtime = await enumerateRuntimeDependencyGraph(runtimeRoot, { maxFiles: input.maxFiles });
|
|
40
|
+
return runtimeIdentityFromEnumeration(runtime);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
function runtimeIdentityFromEnumeration(runtime = {}) {
|
|
45
|
+
const runtimeEntries = runtime.entries || [];
|
|
46
|
+
const packageEntry = runtimeEntries.find((entry) => String(entry?.path || '') === 'package.json');
|
|
47
|
+
let packageName = '';
|
|
48
|
+
let packageVersion = '';
|
|
49
|
+
if (packageEntry?.data) {
|
|
50
|
+
try {
|
|
51
|
+
const parsed = JSON.parse(new TextDecoder().decode(packageEntry.data));
|
|
52
|
+
packageName = String(parsed?.name || '').trim();
|
|
53
|
+
packageVersion = String(parsed?.version || '').trim();
|
|
54
|
+
} catch {}
|
|
55
|
+
}
|
|
56
|
+
const shaFor = (entryPath) => String(runtimeEntries.find((entry) => String(entry?.path || '') === entryPath)?.sha256 || '');
|
|
57
|
+
const sourceEntries = runtimeEntries.filter((entry) => String(entry?.path || '') !== 'package.json');
|
|
58
|
+
const sourceRepresentationSha256 = sha256Text(stableJson(sourceEntries.map(({ path: entryPath, bytes, sha256 }) => ({ path: `runtime/${entryPath}`, bytes, sha256 }))));
|
|
59
|
+
return Object.freeze({
|
|
60
|
+
schema: 'tiinex.portable.tooling-runtime-identity.v1',
|
|
61
|
+
packageName,
|
|
62
|
+
packageVersion,
|
|
63
|
+
representationSha256: String(runtime.representationSha256 || ''),
|
|
64
|
+
sourceRepresentationSha256,
|
|
65
|
+
runtimeFiles: Number(runtimeEntries.length),
|
|
66
|
+
runtimeBytes: Number(runtime.totalBytes || 0),
|
|
67
|
+
entrypointSha256: shaFor('tools/tiinex-portable.mjs'),
|
|
68
|
+
enumerationPolicySha256: shaFor('src/tooling/portable/adapters/node/handoff.manufacture.enumeration.js')
|
|
69
|
+
});
|
|
34
70
|
}
|
|
35
71
|
|
|
36
72
|
function verifyExpectedPersistentBootstrap(expected, manifest, manifestSha256) {
|
|
@@ -2,6 +2,7 @@ import { readFile } from 'node:fs/promises';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { inferWorkspaceTitle, normalizeAdditionalWorkspaceDescriptors, normalizeTransportRoute, safeWorkspaceToken } from './handoff.manufacture.multiRoot.js';
|
|
4
4
|
import { buildToolingBootstrapTransportFiles, PORTABLE_TOOLING_BOOTSTRAP_MANIFEST_SCHEMA_ID } from './handoff.manufacture.bootstrap.js';
|
|
5
|
+
import { qualifyToolingRuntimeSourceAlignment } from './handoff.manufacture.runtimeSource.js';
|
|
5
6
|
import { normalizeHandoffCarrierLineage } from '../../handoff/carrierLineage.js';
|
|
6
7
|
import { normalizeHandoffCarrierProfile } from '../../handoff/carrierProfile.js';
|
|
7
8
|
import { enumerateNodeWorkspace, PORTABLE_NODE_WORKSPACE_ENUMERATION_SCHEMA_ID } from './handoff.manufacture.enumeration.js';
|
|
@@ -153,6 +154,14 @@ export async function prepareNodeHandoffManufacturingInput(input = {}, options =
|
|
|
153
154
|
const toolingBootstrapResult = await toolingBootstrapPromise;
|
|
154
155
|
if (toolingBootstrapResult.error) throw toolingBootstrapResult.error;
|
|
155
156
|
const toolingBootstrap = toolingBootstrapResult.value;
|
|
157
|
+
const runtimeSourceAlignment = await qualifyToolingRuntimeSourceAlignment({
|
|
158
|
+
runtimeIdentity: toolingBootstrap.runtimeIdentity,
|
|
159
|
+
localWorkspaces: [
|
|
160
|
+
Object.freeze({ id: workspaceId, root: workspaceRoot, materialization: primaryMaterialization }),
|
|
161
|
+
...additionalEnumerations.map(({ id, root, enumerated }) => Object.freeze({ id, root, materialization: enumerated.materialization }))
|
|
162
|
+
],
|
|
163
|
+
maxFiles: input.bootstrapMaxFiles || options.bootstrapMaxFiles
|
|
164
|
+
});
|
|
156
165
|
const orientationBootstrap = input.transportBootstrapContent
|
|
157
166
|
? Object.freeze({ present: true, path: String(input.transportBootstrapPath || 'tiinex.package/bootstrap.md'), content: String(input.transportBootstrapContent), mediaType: 'text/markdown' })
|
|
158
167
|
: Object.freeze({ present: false });
|
|
@@ -175,6 +184,7 @@ export async function prepareNodeHandoffManufacturingInput(input = {}, options =
|
|
|
175
184
|
enumeration: enumeration.evidence,
|
|
176
185
|
workspaceEnumerations: Object.freeze(workspaceEnumerations),
|
|
177
186
|
toolingBootstrap: toolingBootstrap.summary,
|
|
187
|
+
runtimeSourceAlignment,
|
|
178
188
|
packageParentWorkspaceReuse: Object.freeze({
|
|
179
189
|
state: String(packageParentReuse.state || ''),
|
|
180
190
|
providerState: String(packageParentReuse.providerState || ''),
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { buildToolingBootstrapRuntimeIdentity } from './handoff.manufacture.bootstrap.js';
|
|
2
|
+
|
|
3
|
+
export const PORTABLE_TOOLING_RUNTIME_SOURCE_ALIGNMENT_SCHEMA_ID = 'tiinex.portable.tooling-runtime-source-alignment.v1';
|
|
4
|
+
const CORE_PACKAGE_NAME = '@tiinex/core';
|
|
5
|
+
|
|
6
|
+
export async function qualifyToolingRuntimeSourceAlignment(input = {}) {
|
|
7
|
+
const runtimeIdentity = normalizeIdentity(input.runtimeIdentity);
|
|
8
|
+
if (!runtimeIdentity.sourceRepresentationSha256) throw new Error('portable.tooling-bootstrap.runtime-source.runtime-identity-required');
|
|
9
|
+
const candidates = [];
|
|
10
|
+
for (const item of input.localWorkspaces || input.workspaces || []) {
|
|
11
|
+
const root = String(item?.root || '').trim();
|
|
12
|
+
if (!root) continue;
|
|
13
|
+
const materialization = item?.materialization || item?.enumeration?.materialization || null;
|
|
14
|
+
const packageIdentity = packageIdentityFromMaterialization(materialization);
|
|
15
|
+
if (packageIdentity.name !== CORE_PACKAGE_NAME) continue;
|
|
16
|
+
candidates.push(Object.freeze({
|
|
17
|
+
workspaceId: String(item?.id || materialization?.id || '').trim(),
|
|
18
|
+
root,
|
|
19
|
+
packageIdentity
|
|
20
|
+
}));
|
|
21
|
+
}
|
|
22
|
+
if (!candidates.length) return Object.freeze({
|
|
23
|
+
schema: PORTABLE_TOOLING_RUNTIME_SOURCE_ALIGNMENT_SCHEMA_ID,
|
|
24
|
+
state: 'not-observed',
|
|
25
|
+
coreWorkspaceCount: 0,
|
|
26
|
+
runtime: runtimeIdentity,
|
|
27
|
+
source: null,
|
|
28
|
+
boundary: 'No local Workspace with package identity @tiinex/core was selected, so exact runtime/source equality cannot be asserted from carried source.'
|
|
29
|
+
});
|
|
30
|
+
if (candidates.length !== 1) throw new Error(`portable.tooling-bootstrap.runtime-source.core-workspace-ambiguous:${candidates.map((item) => item.workspaceId || 'unlabeled').join(',')}`);
|
|
31
|
+
|
|
32
|
+
const candidate = candidates[0];
|
|
33
|
+
const sourceIdentity = normalizeIdentity(await buildToolingBootstrapRuntimeIdentity({
|
|
34
|
+
runtimeRoot: candidate.root,
|
|
35
|
+
maxFiles: input.maxFiles
|
|
36
|
+
}));
|
|
37
|
+
const evidence = Object.freeze({
|
|
38
|
+
schema: PORTABLE_TOOLING_RUNTIME_SOURCE_ALIGNMENT_SCHEMA_ID,
|
|
39
|
+
state: sourceIdentity.sourceRepresentationSha256 === runtimeIdentity.sourceRepresentationSha256 ? 'qualified-exact-match' : 'mismatch',
|
|
40
|
+
coreWorkspaceCount: 1,
|
|
41
|
+
workspaceId: candidate.workspaceId,
|
|
42
|
+
runtime: runtimeIdentity,
|
|
43
|
+
source: sourceIdentity,
|
|
44
|
+
boundary: 'Exact Tooling runtime source/data equality is required when a local @tiinex/core Workspace is carried. Release-normalized package.json metadata may differ, but matching package names or versions alone are insufficient.'
|
|
45
|
+
});
|
|
46
|
+
if (evidence.state !== 'qualified-exact-match') {
|
|
47
|
+
throw new Error([
|
|
48
|
+
'portable.tooling-bootstrap.runtime-source.mismatch',
|
|
49
|
+
`workspace=${candidate.workspaceId || 'unlabeled'}`,
|
|
50
|
+
`runtimeVersion=${runtimeIdentity.packageVersion || 'unknown'}`,
|
|
51
|
+
`sourceVersion=${sourceIdentity.packageVersion || candidate.packageIdentity.version || 'unknown'}`,
|
|
52
|
+
`runtimeSource=${runtimeIdentity.sourceRepresentationSha256}`,
|
|
53
|
+
`sourceSource=${sourceIdentity.sourceRepresentationSha256}`
|
|
54
|
+
].join(':'));
|
|
55
|
+
}
|
|
56
|
+
return evidence;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function packageIdentityFromMaterialization(materialization) {
|
|
60
|
+
const entry = (materialization?.entries || []).find((item) => String(item?.path || '') === 'package.json');
|
|
61
|
+
if (!entry?.data) return Object.freeze({ name: '', version: '' });
|
|
62
|
+
try {
|
|
63
|
+
const parsed = JSON.parse(new TextDecoder().decode(entry.data));
|
|
64
|
+
return Object.freeze({ name: String(parsed?.name || '').trim(), version: String(parsed?.version || '').trim() });
|
|
65
|
+
} catch {
|
|
66
|
+
return Object.freeze({ name: '', version: '' });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function normalizeIdentity(value = {}) {
|
|
71
|
+
return Object.freeze({
|
|
72
|
+
schema: String(value?.schema || 'tiinex.portable.tooling-runtime-identity.v1'),
|
|
73
|
+
packageName: String(value?.packageName || ''),
|
|
74
|
+
packageVersion: String(value?.packageVersion || ''),
|
|
75
|
+
representationSha256: String(value?.representationSha256 || ''),
|
|
76
|
+
sourceRepresentationSha256: String(value?.sourceRepresentationSha256 || ''),
|
|
77
|
+
runtimeFiles: Number(value?.runtimeFiles || 0),
|
|
78
|
+
runtimeBytes: Number(value?.runtimeBytes || 0),
|
|
79
|
+
entrypointSha256: String(value?.entrypointSha256 || ''),
|
|
80
|
+
enumerationPolicySha256: String(value?.enumerationPolicySha256 || '')
|
|
81
|
+
});
|
|
82
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { enumerateNodeWorkspace } from './handoff.manufacture.enumeration.js';
|
|
3
3
|
import { buildToolingBootstrapTransportFiles } from './handoff.manufacture.bootstrap.js';
|
|
4
|
+
import { qualifyToolingRuntimeSourceAlignment } from './handoff.manufacture.runtimeSource.js';
|
|
4
5
|
import { inferWorkspaceTitle, normalizeAdditionalWorkspaceDescriptors, safeWorkspaceToken } from './handoff.manufacture.multiRoot.js';
|
|
5
6
|
import { normalizeWorkspaceTargetBindings } from './handoff.manufacture.scope.js';
|
|
6
7
|
import { normalizeHandoffCarrierLineage } from '../../handoff/carrierLineage.js';
|
|
@@ -47,6 +48,11 @@ export async function prepareNodeWorkspaceCarrierManufacturingInput(input = {},
|
|
|
47
48
|
expected: input.expectedToolingBootstrap || null,
|
|
48
49
|
maxFiles: input.bootstrapMaxFiles || options.bootstrapMaxFiles
|
|
49
50
|
});
|
|
51
|
+
const runtimeSourceAlignment = await qualifyToolingRuntimeSourceAlignment({
|
|
52
|
+
runtimeIdentity: toolingBootstrap.runtimeIdentity,
|
|
53
|
+
localWorkspaces: enumerations.map((item) => Object.freeze({ id: item.materialization.id, root: item.root, materialization: item.materialization })),
|
|
54
|
+
maxFiles: input.bootstrapMaxFiles || options.bootstrapMaxFiles
|
|
55
|
+
});
|
|
50
56
|
return Object.freeze({
|
|
51
57
|
carrierMode: 'workspace',
|
|
52
58
|
createdAt: String(input.createdAt || ''),
|
|
@@ -56,7 +62,7 @@ export async function prepareNodeWorkspaceCarrierManufacturingInput(input = {},
|
|
|
56
62
|
carrierLineage: normalizeHandoffCarrierLineage(input.carrierLineage || null),
|
|
57
63
|
carrierProfile: normalizeHandoffCarrierProfile(input.carrierProfile || null),
|
|
58
64
|
toolingBootstrap: toolingBootstrap.summary,
|
|
59
|
-
manufacturingEvidence: Object.freeze({ workspaceEnumerations: Object.freeze(enumerations.map((item) => Object.freeze({ id: item.materialization.id, root: item.root, evidence: item.evidence }))), toolingBootstrap: toolingBootstrap.summary }),
|
|
65
|
+
manufacturingEvidence: Object.freeze({ workspaceEnumerations: Object.freeze(enumerations.map((item) => Object.freeze({ id: item.materialization.id, root: item.root, evidence: item.evidence }))), toolingBootstrap: toolingBootstrap.summary, runtimeSourceAlignment }),
|
|
60
66
|
verifyRoundtrip: input.verifyRoundtrip !== false
|
|
61
67
|
});
|
|
62
68
|
}
|
|
@@ -6,6 +6,7 @@ export const PORTABLE_CANONICAL_BOOTSTRAP_ROOT = fileURLToPath(new URL(`./docs-$
|
|
|
6
6
|
export const portableCanonicalBootstrapRuntime = Object.freeze({
|
|
7
7
|
defaultSchemaMaterialPaths: Object.freeze([PORTABLE_CANONICAL_BOOTSTRAP_ROOT]),
|
|
8
8
|
defaultSchemaSource: Object.freeze({
|
|
9
|
+
workspaceId: 'docs',
|
|
9
10
|
repository: 'Tiinex/docs',
|
|
10
11
|
commit: PORTABLE_CANONICAL_BOOTSTRAP_DOCS_COMMIT,
|
|
11
12
|
sourcePathPrefix: '.topics/.schemas'
|