@shapeshift-labs/frontier-lang-compiler 0.2.66 → 0.2.67

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 (47) hide show
  1. package/README.md +6 -2
  2. package/bench/smoke.mjs +15 -1
  3. package/bench/universal-fixture-suite.mjs +183 -0
  4. package/dist/declarations/native-project-admission.d.ts +115 -0
  5. package/dist/declarations/roundtrip-audit.d.ts +177 -0
  6. package/dist/declarations/roundtrip.d.ts +2 -53
  7. package/dist/declarations/semantic-history-records.d.ts +277 -0
  8. package/dist/declarations/semantic-history.d.ts +45 -92
  9. package/dist/declarations/semantic-slice-admission.d.ts +111 -0
  10. package/dist/declarations/semantic-slice.d.ts +36 -1
  11. package/dist/declarations/universal-conversion-plan.d.ts +59 -0
  12. package/dist/declarations/universal-runtime-capabilities.d.ts +171 -0
  13. package/dist/index.d.ts +1 -0
  14. package/dist/index.js +1 -0
  15. package/dist/internal/index-impl/createNativeRoundtripEvidence.js +54 -49
  16. package/dist/internal/index-impl/createSemanticSlice.js +1 -1
  17. package/dist/internal/index-impl/createSemanticSliceAdmissionRecord.js +10 -1
  18. package/dist/internal/index-impl/expandSemanticSliceSelection.js +0 -1
  19. package/dist/internal/index-impl/nativeRoundtripAudit.js +217 -0
  20. package/dist/internal/index-impl/projectImportAdmissionImportEvidence.js +160 -0
  21. package/dist/internal/index-impl/projectImportAdmissionLanguageSummaries.js +247 -0
  22. package/dist/internal/index-impl/projectImportAdmissionRanks.js +52 -0
  23. package/dist/internal/index-impl/projectImportAdmissionSummaries.js +46 -111
  24. package/dist/internal/index-impl/projectImportAdmissionTasks.js +239 -0
  25. package/dist/internal/index-impl/semanticHistoryRecordNormalizers.js +151 -0
  26. package/dist/internal/index-impl/semanticHistoryRecordOverlaps.js +113 -0
  27. package/dist/internal/index-impl/semanticHistoryRecords.js +210 -149
  28. package/dist/internal/index-impl/semanticSliceAdmissionSurface.js +142 -0
  29. package/dist/internal/index-impl/semanticSliceExpectationAssertions.js +100 -0
  30. package/dist/internal/index-impl/semanticSliceExpectationRecords.js +75 -0
  31. package/dist/internal/index-impl/semanticSliceExpectedAssertions.js +5 -2
  32. package/dist/internal/index-impl/testSemanticSlice.js +4 -1
  33. package/dist/language-adapter-package-contracts.js +12 -57
  34. package/dist/language-adapter-package-rows.js +116 -0
  35. package/dist/universal-conversion-plan-summary.js +42 -0
  36. package/dist/universal-conversion-plan.js +46 -40
  37. package/dist/universal-runtime-capabilities.js +92 -0
  38. package/dist/universal-runtime-host-selectors.js +192 -0
  39. package/dist/universal-runtime-profiles.js +109 -0
  40. package/dist/universal-runtime-route-records.js +162 -0
  41. package/examples/js-frontier-rust-workbench-client.mjs +58 -1
  42. package/examples/js-frontier-rust-workbench-convert.mjs +161 -0
  43. package/examples/js-frontier-rust-workbench-route-styles.mjs +126 -0
  44. package/examples/js-frontier-rust-workbench-route.mjs +190 -0
  45. package/examples/js-frontier-rust-workbench-styles.mjs +3 -38
  46. package/examples/js-frontier-rust-workbench.mjs +22 -128
  47. package/package.json +1 -1
@@ -9,6 +9,10 @@ import {
9
9
  normalizeProjectionMatrixTargets
10
10
  } from './coverage-matrix-profiles.js';
11
11
  import { createUniversalCapabilityMatrix } from './universal-capability-matrix.js';
12
+ import {
13
+ createUniversalRuntimeCapabilityMatrix,
14
+ runtimeRouteForConversion
15
+ } from './universal-runtime-capabilities.js';
12
16
  import {
13
17
  conversionMergeRefs,
14
18
  importsForConversionLanguage
@@ -17,6 +21,7 @@ import {
17
21
  conversionMergeScore,
18
22
  conversionScoreComponents
19
23
  } from './universal-conversion-plan-scoring.js';
24
+ import { conversionPlanSummary } from './universal-conversion-plan-summary.js';
20
25
 
21
26
  export function createUniversalConversionPlan(input = {}, context = {}) {
22
27
  const generatedAt = input.generatedAt ?? Date.now();
@@ -25,12 +30,21 @@ export function createUniversalConversionPlan(input = {}, context = {}) {
25
30
  ? input.universalCapabilityMatrix
26
31
  : createUniversalCapabilityMatrix({ ...input, generatedAt }, context);
27
32
  const targets = conversionTargets(input, matrix, context);
33
+ const runtimeMatrix = input.universalRuntimeCapabilityMatrix?.kind === 'frontier.lang.universalRuntimeCapabilityMatrix'
34
+ ? input.universalRuntimeCapabilityMatrix
35
+ : createUniversalRuntimeCapabilityMatrix({
36
+ ...input,
37
+ generatedAt,
38
+ sourceLanguages: matrix.languages,
39
+ targets
40
+ }, context);
28
41
  const evidence = input.evidence ?? [];
29
42
  const routes = (matrix.languages ?? []).flatMap((language) => targets.map((target) => conversionRoute(language, target, {
30
43
  evidence,
31
44
  generatedAt,
32
45
  imports: input.imports ?? [],
33
- matrix
46
+ matrix,
47
+ runtimeMatrix
34
48
  }, id)));
35
49
  return {
36
50
  kind: 'frontier.lang.universalConversionPlan',
@@ -41,6 +55,7 @@ export function createUniversalConversionPlan(input = {}, context = {}) {
41
55
  summary: conversionPlanSummary(routes),
42
56
  matrices: {
43
57
  universalCapability: matrix,
58
+ runtimeCapabilities: runtimeMatrix,
44
59
  projectionReadiness: matrix.matrices?.projectionReadiness,
45
60
  projectionTargets: matrix.matrices?.projectionTargets
46
61
  },
@@ -90,6 +105,8 @@ function conversionRoute(language, target, input, planId) {
90
105
  const sourceTarget = nativeLanguageCompileTarget(language.language, language.aliases) ?? normalizeNativeLanguageId(language.language);
91
106
  const targetCell = (language.projection?.targets ?? []).find((entry) => entry.target === target);
92
107
  const readinessCell = projectionReadinessCell(input.matrix, language, target);
108
+ const runtimeRoute = runtimeRouteForConversion(input.runtimeMatrix, language, target);
109
+ const runtime = conversionRuntime(runtimeRoute);
93
110
  const mode = conversionMode(language, target, sourceTarget, targetCell);
94
111
  const blockers = conversionBlockers(language, targetCell, mode);
95
112
  const review = conversionReviewReasons(language, targetCell, mode);
@@ -115,6 +132,8 @@ function conversionRoute(language, target, input, planId) {
115
132
  adapterKind: targetCell?.adapterKind,
116
133
  sourceProjection: language.projection?.sourceProjection,
117
134
  projectionReadiness: readinessCell,
135
+ runtime,
136
+ runtimeAdapterRequirements: runtime.adapterRequirements,
118
137
  evidence: conversionEvidence(language, targetCell),
119
138
  missingEvidence: conversionMissingEvidence(language, targetCell, mode),
120
139
  blockers,
@@ -172,6 +191,32 @@ function conversionReviewReasons(language, targetCell, mode) {
172
191
  ]);
173
192
  }
174
193
 
194
+ function conversionRuntime(runtimeRoute) {
195
+ if (!runtimeRoute) {
196
+ return {
197
+ requiredCapabilities: [],
198
+ satisfiedCapabilities: [],
199
+ adapterRequirements: [],
200
+ missingCapabilities: [],
201
+ readiness: 'ready',
202
+ blockers: [],
203
+ review: []
204
+ };
205
+ }
206
+ return {
207
+ routeId: runtimeRoute.id,
208
+ source: runtimeRoute.source,
209
+ target: runtimeRoute.target,
210
+ requiredCapabilities: runtimeRoute.requiredCapabilities,
211
+ satisfiedCapabilities: runtimeRoute.satisfiedCapabilities,
212
+ adapterRequirements: runtimeRoute.adapterRequirements,
213
+ missingCapabilities: runtimeRoute.missingCapabilities,
214
+ readiness: runtimeRoute.readiness,
215
+ blockers: runtimeRoute.blockers,
216
+ review: runtimeRoute.review
217
+ };
218
+ }
219
+
175
220
  function projectionReadinessCell(matrix, language, target) {
176
221
  const ids = uniqueStrings([language.language, ...(language.aliases ?? [])].map(normalizeNativeLanguageId));
177
222
  return (matrix.matrices?.projectionReadiness?.languages ?? [])
@@ -238,42 +283,3 @@ function conversionTasks(language, target, mode, blockers, review) {
238
283
  ...(blockers.length || review.length ? [`collect proof, replay, or oracle evidence for ${language.language} to ${target}`] : [])
239
284
  ]);
240
285
  }
241
-
242
- function conversionPlanSummary(routes) {
243
- const summary = {
244
- routes: routes.length,
245
- byMode: {},
246
- byReadiness: {},
247
- byAdmissionAction: {},
248
- readyRoutes: 0,
249
- reviewRoutes: 0,
250
- blockedRoutes: 0,
251
- preserveSourceRoutes: 0,
252
- targetAdapterRoutes: 0,
253
- stubOnlyRoutes: 0,
254
- semanticIndexOnlyRoutes: 0,
255
- missingEvidence: 0,
256
- blockers: 0,
257
- reviewReasons: 0,
258
- autoMergeClaims: 0,
259
- semanticEquivalenceClaims: 0
260
- };
261
- for (const route of routes) {
262
- summary.byMode[route.mode] = (summary.byMode[route.mode] ?? 0) + 1;
263
- summary.byReadiness[route.readiness] = (summary.byReadiness[route.readiness] ?? 0) + 1;
264
- summary.byAdmissionAction[route.admissionAction] = (summary.byAdmissionAction[route.admissionAction] ?? 0) + 1;
265
- if (route.readiness === 'ready') summary.readyRoutes += 1;
266
- if (route.readiness === 'needs-review' || route.readiness === 'ready-with-losses') summary.reviewRoutes += 1;
267
- if (route.readiness === 'blocked') summary.blockedRoutes += 1;
268
- if (route.mode === 'preserve-source') summary.preserveSourceRoutes += 1;
269
- if (route.mode === 'target-adapter') summary.targetAdapterRoutes += 1;
270
- if (route.mode === 'stub-only') summary.stubOnlyRoutes += 1;
271
- if (route.mode === 'semantic-index-only') summary.semanticIndexOnlyRoutes += 1;
272
- summary.missingEvidence += route.missingEvidence.length;
273
- summary.blockers += route.blockers.length;
274
- summary.reviewReasons += route.review.length;
275
- if (route.autoMergeClaim) summary.autoMergeClaims += 1;
276
- if (route.semanticEquivalenceClaim) summary.semanticEquivalenceClaims += 1;
277
- }
278
- return summary;
279
- }
@@ -0,0 +1,92 @@
1
+ import {
2
+ normalizeNativeLanguageId,
3
+ uniqueStrings
4
+ } from './native-import-utils.js';
5
+ import {
6
+ normalizeProjectionMatrixTargets
7
+ } from './coverage-matrix-profiles.js';
8
+ import {
9
+ UniversalRuntimeCapabilityKinds,
10
+ UniversalRuntimeHostProfiles
11
+ } from './universal-runtime-profiles.js';
12
+ import {
13
+ normalizeRuntimeCapabilityKind,
14
+ normalizeRuntimeHostProfiles,
15
+ normalizeRuntimeId,
16
+ normalizeRuntimeRequirements,
17
+ runtimeSourceHosts,
18
+ runtimeTargetHosts
19
+ } from './universal-runtime-host-selectors.js';
20
+ import {
21
+ runtimeCapabilityMatrixSummary,
22
+ runtimeRoute
23
+ } from './universal-runtime-route-records.js';
24
+
25
+ export {
26
+ UniversalRuntimeCapabilityKinds,
27
+ UniversalRuntimeHostProfiles
28
+ } from './universal-runtime-profiles.js';
29
+
30
+ export function createUniversalRuntimeCapabilityMatrix(input = {}, context = {}) {
31
+ const generatedAt = input.generatedAt ?? Date.now();
32
+ const hostProfiles = normalizeRuntimeHostProfiles(input.hostProfiles ?? input.runtimeHosts ?? UniversalRuntimeHostProfiles);
33
+ const sourceHosts = runtimeSourceHosts(input, hostProfiles);
34
+ const targetHosts = runtimeTargetHosts(input, hostProfiles, context);
35
+ const requirements = normalizeRuntimeRequirements(input, hostProfiles);
36
+ const routes = sourceHosts.flatMap((sourceHost) => targetHosts.map((targetHost) => runtimeRoute(sourceHost, targetHost, {
37
+ generatedAt,
38
+ requirements
39
+ })));
40
+ return {
41
+ kind: 'frontier.lang.universalRuntimeCapabilityMatrix',
42
+ version: 1,
43
+ generatedAt,
44
+ hostProfiles,
45
+ routes,
46
+ summary: runtimeCapabilityMatrixSummary(routes),
47
+ metadata: {
48
+ capabilityKinds: [...UniversalRuntimeCapabilityKinds],
49
+ sourceHosts: sourceHosts.map((host) => host.id),
50
+ targetHosts: targetHosts.map((host) => host.id),
51
+ note: 'Runtime capability coverage is declarative host evidence for adapter planning. It does not import, polyfill, or execute host APIs.'
52
+ }
53
+ };
54
+ }
55
+
56
+ export function queryUniversalRuntimeCapabilityMatrix(matrixOrInput = {}, query = {}, context = {}) {
57
+ const matrix = matrixOrInput?.kind === 'frontier.lang.universalRuntimeCapabilityMatrix'
58
+ ? matrixOrInput
59
+ : createUniversalRuntimeCapabilityMatrix(matrixOrInput, context);
60
+ const sourceLanguage = normalizeNativeLanguageId(query.sourceLanguage ?? query.language);
61
+ const target = normalizeProjectionMatrixTargets(query.target ? [query.target] : [])[0];
62
+ const sourceRuntime = normalizeRuntimeId(query.sourceRuntime ?? query.runtime);
63
+ const targetRuntime = normalizeRuntimeId(query.targetRuntime);
64
+ const capability = normalizeRuntimeCapabilityKind(query.capability);
65
+ const routes = (matrix.routes ?? []).filter((route) => {
66
+ if (sourceLanguage && !route.source.languageIds.includes(sourceLanguage)) return false;
67
+ if (target && route.target.target !== target) return false;
68
+ if (sourceRuntime && route.source.runtime !== sourceRuntime && route.source.id !== sourceRuntime) return false;
69
+ if (targetRuntime && route.target.runtime !== targetRuntime && route.target.id !== targetRuntime) return false;
70
+ if (capability && !route.requiredCapabilities.includes(capability)) return false;
71
+ if (query.requiresAdapter === true && route.adapterRequirements.length === 0) return false;
72
+ if (query.requiresAdapter === false && route.adapterRequirements.length > 0) return false;
73
+ return true;
74
+ });
75
+ return {
76
+ kind: 'frontier.lang.universalRuntimeCapabilityMatrixQuery',
77
+ version: 1,
78
+ found: routes.length > 0,
79
+ routes,
80
+ bestRoute: routes[0],
81
+ reasons: routes.length ? [] : [`No runtime capability route matched source=${query.sourceLanguage ?? query.language ?? '*'} target=${query.target ?? '*'}.`]
82
+ };
83
+ }
84
+
85
+ export function runtimeRouteForConversion(runtimeMatrix, language, target) {
86
+ const languageIds = uniqueStrings([language?.language, ...(language?.aliases ?? [])].map(normalizeNativeLanguageId));
87
+ const normalizedTarget = normalizeProjectionMatrixTargets([target])[0] ?? String(target ?? '');
88
+ return (runtimeMatrix?.routes ?? []).find((route) => {
89
+ if (normalizedTarget && route.target.target !== normalizedTarget) return false;
90
+ return route.source.languageIds.some((id) => languageIds.includes(id));
91
+ });
92
+ }
@@ -0,0 +1,192 @@
1
+ import {
2
+ normalizeNativeLanguageId,
3
+ uniqueStrings
4
+ } from './native-import-utils.js';
5
+ import {
6
+ nativeLanguageCompileTarget,
7
+ normalizeProjectionMatrixTargets
8
+ } from './coverage-matrix-profiles.js';
9
+
10
+ export function normalizeRuntimeHostProfiles(profiles) {
11
+ return (profiles ?? []).map((profile) => {
12
+ const language = normalizeNativeLanguageId(profile.language);
13
+ const runtime = normalizeRuntimeId(profile.runtime ?? profile.host ?? 'host');
14
+ const id = profile.id ?? `${language}:${runtime}`;
15
+ const aliases = uniqueStrings((profile.aliases ?? []).map(normalizeNativeLanguageId));
16
+ const capabilities = Object.fromEntries(Object.entries(profile.capabilities ?? {})
17
+ .map(([kind, value]) => {
18
+ const normalizedKind = normalizeRuntimeCapabilityKind(value?.kind ?? kind);
19
+ return [normalizedKind, {
20
+ kind: normalizedKind,
21
+ support: normalizeCapabilitySupport(value?.support),
22
+ binding: String(value?.binding ?? `${id}.${normalizedKind}`),
23
+ notes: uniqueStrings(value?.notes ?? [])
24
+ }];
25
+ }));
26
+ return {
27
+ id,
28
+ language,
29
+ aliases,
30
+ languageIds: uniqueStrings([language, ...aliases]),
31
+ runtime,
32
+ host: String(profile.host ?? runtime),
33
+ target: normalizeProjectionMatrixTargets([profile.target ?? nativeLanguageCompileTarget(language, aliases) ?? language])[0] ?? language,
34
+ capabilities,
35
+ notes: uniqueStrings(profile.notes ?? [])
36
+ };
37
+ });
38
+ }
39
+
40
+ export function runtimeSourceHosts(input, hostProfiles) {
41
+ const explicit = normalizeHostSelectors(input.sourceHosts ?? input.sourceRuntimeHosts, hostProfiles);
42
+ if (explicit.length) return explicit;
43
+ const languages = uniqueStrings([
44
+ ...(input.sourceLanguages ?? input.languages ?? []).map((entry) => entry?.language ?? entry),
45
+ ...(input.imports ?? []).map((entry) => entry?.language ?? entry?.nativeAst?.language ?? entry?.nativeSource?.language),
46
+ ...runtimeRequirementRecords(input).map((entry) => entry.sourceLanguage ?? entry.language)
47
+ ].map(normalizeNativeLanguageId));
48
+ return (languages.length ? languages : ['javascript'])
49
+ .map((language) => defaultHostForLanguage(language, hostProfiles, input.sourceRuntimes?.[language] ?? input.sourceRuntime))
50
+ .filter(Boolean);
51
+ }
52
+
53
+ export function runtimeTargetHosts(input, hostProfiles, context) {
54
+ const explicit = normalizeHostSelectors(input.targetHosts ?? input.targetRuntimeHosts, hostProfiles);
55
+ if (explicit.length) return explicit;
56
+ const targets = normalizeProjectionMatrixTargets(input.targets ?? context.compileTargets ?? []);
57
+ const targetList = targets.length ? targets : ['javascript'];
58
+ return targetList.map((target) => defaultHostForTarget(target, hostProfiles, input.targetRuntimes?.[target] ?? input.targetRuntime));
59
+ }
60
+
61
+ function normalizeHostSelectors(value, hostProfiles) {
62
+ if (!value) return [];
63
+ if (Array.isArray(value)) return value.map((entry) => resolveHostSelector(entry, hostProfiles)).filter(Boolean);
64
+ if (typeof value === 'object') return Object.values(value).map((entry) => resolveHostSelector(entry, hostProfiles)).filter(Boolean);
65
+ return [resolveHostSelector(value, hostProfiles)].filter(Boolean);
66
+ }
67
+
68
+ function resolveHostSelector(value, hostProfiles) {
69
+ if (!value) return undefined;
70
+ if (typeof value === 'object') return normalizeRuntimeHostProfiles([value])[0];
71
+ const normalized = normalizeRuntimeId(value);
72
+ return hostProfiles.find((profile) => profile.id === normalized)
73
+ ?? hostProfiles.find((profile) => `${profile.language}:${profile.runtime}` === normalized)
74
+ ?? hostProfiles.find((profile) => profile.language === normalized)
75
+ ?? genericHostProfile(normalized, normalized);
76
+ }
77
+
78
+ function defaultHostForLanguage(language, hostProfiles, runtime) {
79
+ const normalizedLanguage = normalizeNativeLanguageId(language);
80
+ const normalizedRuntime = normalizeRuntimeId(runtime);
81
+ return hostProfiles.find((profile) => profile.language === normalizedLanguage && (!normalizedRuntime || profile.runtime === normalizedRuntime || profile.id === normalizedRuntime))
82
+ ?? hostProfiles.find((profile) => profile.languageIds.includes(normalizedLanguage))
83
+ ?? genericHostProfile(`${normalizedLanguage}:host`, normalizedLanguage);
84
+ }
85
+
86
+ function defaultHostForTarget(target, hostProfiles, runtime) {
87
+ const normalizedTarget = normalizeProjectionMatrixTargets([target])[0] ?? normalizeNativeLanguageId(target);
88
+ const normalizedRuntime = normalizeRuntimeId(runtime);
89
+ return hostProfiles.find((profile) => profile.target === normalizedTarget && (!normalizedRuntime || profile.runtime === normalizedRuntime || profile.id === normalizedRuntime))
90
+ ?? hostProfiles.find((profile) => profile.language === normalizedTarget)
91
+ ?? genericHostProfile(`${normalizedTarget}:host`, normalizedTarget);
92
+ }
93
+
94
+ function genericHostProfile(id, language) {
95
+ const normalizedLanguage = normalizeNativeLanguageId(language);
96
+ const normalizedId = String(id || `${normalizedLanguage}:host`);
97
+ return {
98
+ id: normalizedId,
99
+ language: normalizedLanguage,
100
+ aliases: [],
101
+ languageIds: [normalizedLanguage],
102
+ runtime: normalizedId.includes(':') ? normalizedId.split(':').at(-1) : 'host',
103
+ host: 'host',
104
+ target: normalizedLanguage,
105
+ capabilities: {},
106
+ notes: ['No built-in runtime host profile was available; callers may provide hostProfiles for stronger adapter planning.']
107
+ };
108
+ }
109
+
110
+ export function normalizeRuntimeRequirements(input, hostProfiles) {
111
+ const records = [
112
+ ...runtimeRequirementRecords(input),
113
+ ...runtimeRequirementRecordsFromImports(input.imports)
114
+ ];
115
+ return records.flatMap((record) => normalizeRuntimeRequirementRecord(record, hostProfiles));
116
+ }
117
+
118
+ function runtimeRequirementRecords(input) {
119
+ if (!input) return [];
120
+ const value = input.runtimeRequirements ?? input.requiredRuntimeCapabilities ?? input.effects ?? [];
121
+ if (Array.isArray(value)) return value;
122
+ if (typeof value === 'object') {
123
+ return Object.entries(value).map(([key, capabilities]) => ({
124
+ sourceLanguage: key,
125
+ capabilities
126
+ }));
127
+ }
128
+ return [value];
129
+ }
130
+
131
+ function runtimeRequirementRecordsFromImports(imports) {
132
+ return (imports ?? []).flatMap((imported) => {
133
+ const value = imported?.runtimeRequirements
134
+ ?? imported?.metadata?.runtimeRequirements
135
+ ?? imported?.metadata?.runtimeCapabilities
136
+ ?? imported?.metadata?.effects;
137
+ const records = Array.isArray(value) ? value : value ? [value] : [];
138
+ return records.map((record) => typeof record === 'string'
139
+ ? { sourceLanguage: imported?.language ?? imported?.nativeAst?.language, capabilities: [record] }
140
+ : { sourceLanguage: imported?.language ?? imported?.nativeAst?.language, ...record });
141
+ });
142
+ }
143
+
144
+ function normalizeRuntimeRequirementRecord(record, hostProfiles) {
145
+ if (!record) return [];
146
+ if (typeof record === 'string') return [{ capability: normalizeRuntimeCapabilityKind(record) }].filter((entry) => entry.capability);
147
+ const capabilities = uniqueStrings([
148
+ ...(Array.isArray(record.capabilities) ? record.capabilities : []),
149
+ ...(Array.isArray(record.requiredCapabilities) ? record.requiredCapabilities : []),
150
+ record.capability,
151
+ record.kind
152
+ ].map(normalizeRuntimeCapabilityKind).filter(Boolean));
153
+ const sourceHost = resolveHostSelector(record.sourceHost ?? record.sourceRuntimeHost, hostProfiles);
154
+ const targetHost = resolveHostSelector(record.targetHost ?? record.targetRuntimeHost, hostProfiles);
155
+ return capabilities.map((capability) => ({
156
+ capability,
157
+ sourceLanguage: normalizeNativeLanguageId(record.sourceLanguage ?? record.language ?? sourceHost?.language),
158
+ sourceRuntime: normalizeRuntimeId(record.sourceRuntime ?? record.runtime ?? sourceHost?.runtime),
159
+ sourceHostId: sourceHost?.id,
160
+ target: normalizeProjectionMatrixTargets(record.target ? [record.target] : [])[0],
161
+ targetRuntime: normalizeRuntimeId(record.targetRuntime ?? targetHost?.runtime),
162
+ targetHostId: targetHost?.id,
163
+ reason: record.reason,
164
+ evidenceIds: uniqueStrings(record.evidenceIds ?? [])
165
+ }));
166
+ }
167
+
168
+ export function normalizeRuntimeCapabilityKind(value) {
169
+ if (!value) return '';
170
+ const text = String(value).trim().toLowerCase();
171
+ if (text === 'http' || text === 'network' || text === 'networking') return 'fetch';
172
+ if (text === 'timer' || text === 'clock' || text === 'scheduler') return 'timers';
173
+ if (text === 'localstorage' || text === 'local-storage' || text === 'persistence') return 'storage';
174
+ if (text === 'file' || text === 'files' || text === 'fs') return 'filesystem';
175
+ if (text === 'threads' || text === 'workers' || text === 'worker') return 'threading';
176
+ if (text === 'browser-dom' || text === 'document') return 'dom';
177
+ if (text === 'promise' || text === 'promises' || text === 'await') return 'async';
178
+ if (text === 'native-ffi' || text === 'abi') return 'ffi';
179
+ return text;
180
+ }
181
+
182
+ export function normalizeRuntimeId(value) {
183
+ if (!value) return '';
184
+ return String(value).trim().toLowerCase();
185
+ }
186
+
187
+ function normalizeCapabilitySupport(value) {
188
+ const support = String(value ?? 'native').trim().toLowerCase();
189
+ if (support === 'native' || support === 'adapter' || support === 'unavailable') return support;
190
+ if (support === 'unsupported' || support === 'missing' || support === 'none') return 'unavailable';
191
+ return support;
192
+ }
@@ -0,0 +1,109 @@
1
+ export const UniversalRuntimeCapabilityKinds = Object.freeze([
2
+ 'fetch',
3
+ 'timers',
4
+ 'storage',
5
+ 'filesystem',
6
+ 'threading',
7
+ 'dom',
8
+ 'async',
9
+ 'ffi'
10
+ ]);
11
+
12
+ export const UniversalRuntimeHostProfiles = Object.freeze([
13
+ runtimeHostProfile('javascript:web', 'javascript', 'web', 'browser', 'javascript', {
14
+ fetch: runtimeCapability('fetch', 'native', 'web.fetch'),
15
+ timers: runtimeCapability('timers', 'native', 'web.timers'),
16
+ storage: runtimeCapability('storage', 'native', 'web.storage'),
17
+ filesystem: runtimeCapability('filesystem', 'unavailable', 'web.no-filesystem'),
18
+ threading: runtimeCapability('threading', 'adapter', 'web.workers'),
19
+ dom: runtimeCapability('dom', 'native', 'web.dom'),
20
+ async: runtimeCapability('async', 'native', 'web.promise-event-loop'),
21
+ ffi: runtimeCapability('ffi', 'unavailable', 'web.no-native-ffi')
22
+ }, ['js', 'jsx']),
23
+ runtimeHostProfile('typescript:web', 'typescript', 'web', 'browser', 'typescript', {
24
+ fetch: runtimeCapability('fetch', 'native', 'web.fetch'),
25
+ timers: runtimeCapability('timers', 'native', 'web.timers'),
26
+ storage: runtimeCapability('storage', 'native', 'web.storage'),
27
+ filesystem: runtimeCapability('filesystem', 'unavailable', 'web.no-filesystem'),
28
+ threading: runtimeCapability('threading', 'adapter', 'web.workers'),
29
+ dom: runtimeCapability('dom', 'native', 'web.dom'),
30
+ async: runtimeCapability('async', 'native', 'web.promise-event-loop'),
31
+ ffi: runtimeCapability('ffi', 'unavailable', 'web.no-native-ffi')
32
+ }, ['ts', 'tsx']),
33
+ runtimeHostProfile('javascript:node', 'javascript', 'node', 'node', 'javascript', {
34
+ fetch: runtimeCapability('fetch', 'native', 'node.fetch'),
35
+ timers: runtimeCapability('timers', 'native', 'node.timers'),
36
+ storage: runtimeCapability('storage', 'adapter', 'node.storage-adapter'),
37
+ filesystem: runtimeCapability('filesystem', 'native', 'node.fs'),
38
+ threading: runtimeCapability('threading', 'adapter', 'node.worker-threads'),
39
+ dom: runtimeCapability('dom', 'unavailable', 'node.no-dom'),
40
+ async: runtimeCapability('async', 'native', 'node.promise-event-loop'),
41
+ ffi: runtimeCapability('ffi', 'adapter', 'node.native-addon')
42
+ }, ['nodejs']),
43
+ runtimeHostProfile('rust:cli', 'rust', 'cli', 'native-cli', 'rust', {
44
+ fetch: runtimeCapability('fetch', 'adapter', 'rust.http-client'),
45
+ timers: runtimeCapability('timers', 'native', 'rust.timer'),
46
+ storage: runtimeCapability('storage', 'adapter', 'rust.filesystem-storage'),
47
+ filesystem: runtimeCapability('filesystem', 'native', 'rust.std-fs'),
48
+ threading: runtimeCapability('threading', 'native', 'rust.std-thread'),
49
+ dom: runtimeCapability('dom', 'unavailable', 'rust.no-dom'),
50
+ async: runtimeCapability('async', 'adapter', 'rust.async-runtime'),
51
+ ffi: runtimeCapability('ffi', 'native', 'rust.ffi')
52
+ }, ['rs']),
53
+ runtimeHostProfile('python:cli', 'python', 'cli', 'native-cli', 'python', {
54
+ fetch: runtimeCapability('fetch', 'adapter', 'python.http-client'),
55
+ timers: runtimeCapability('timers', 'native', 'python.timers'),
56
+ storage: runtimeCapability('storage', 'adapter', 'python.persistence'),
57
+ filesystem: runtimeCapability('filesystem', 'native', 'python.filesystem'),
58
+ threading: runtimeCapability('threading', 'native', 'python.threading'),
59
+ dom: runtimeCapability('dom', 'unavailable', 'python.no-dom'),
60
+ async: runtimeCapability('async', 'native', 'python.asyncio'),
61
+ ffi: runtimeCapability('ffi', 'native', 'python.ctypes')
62
+ }, ['py']),
63
+ runtimeHostProfile('c:cli', 'c', 'cli', 'native-cli', 'c', {
64
+ fetch: runtimeCapability('fetch', 'adapter', 'c.http-client'),
65
+ timers: runtimeCapability('timers', 'native', 'c.timers'),
66
+ storage: runtimeCapability('storage', 'adapter', 'c.persistence'),
67
+ filesystem: runtimeCapability('filesystem', 'native', 'c.filesystem'),
68
+ threading: runtimeCapability('threading', 'native', 'c.threads'),
69
+ dom: runtimeCapability('dom', 'unavailable', 'c.no-dom'),
70
+ async: runtimeCapability('async', 'adapter', 'c.event-loop'),
71
+ ffi: runtimeCapability('ffi', 'native', 'c.abi')
72
+ }, ['h']),
73
+ runtimeHostProfile('cpp:cli', 'cpp', 'cli', 'native-cli', 'cpp', {
74
+ fetch: runtimeCapability('fetch', 'adapter', 'cpp.http-client'),
75
+ timers: runtimeCapability('timers', 'native', 'cpp.timers'),
76
+ storage: runtimeCapability('storage', 'adapter', 'cpp.persistence'),
77
+ filesystem: runtimeCapability('filesystem', 'native', 'cpp.filesystem'),
78
+ threading: runtimeCapability('threading', 'native', 'cpp.thread'),
79
+ dom: runtimeCapability('dom', 'unavailable', 'cpp.no-dom'),
80
+ async: runtimeCapability('async', 'adapter', 'cpp.async-runtime'),
81
+ ffi: runtimeCapability('ffi', 'native', 'cpp.abi')
82
+ }, ['cc', 'cxx', 'hpp'])
83
+ ]);
84
+
85
+ function runtimeHostProfile(id, language, runtime, host, target, capabilities, aliases = []) {
86
+ return Object.freeze({
87
+ id,
88
+ language,
89
+ aliases,
90
+ runtime,
91
+ host,
92
+ target,
93
+ capabilities: freezeCapabilityMap(capabilities),
94
+ notes: []
95
+ });
96
+ }
97
+
98
+ function runtimeCapability(kind, support, binding, notes = []) {
99
+ return Object.freeze({
100
+ kind,
101
+ support,
102
+ binding,
103
+ notes
104
+ });
105
+ }
106
+
107
+ function freezeCapabilityMap(capabilities) {
108
+ return Object.freeze(Object.fromEntries(Object.entries(capabilities).map(([key, value]) => [key, Object.freeze(value)])));
109
+ }