@open-mercato/cli 0.6.7-develop.6795.1.8a3f27921c → 0.6.7-develop.6814.1.0627c7e9f1
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/.turbo/turbo-build.log +1 -1
- package/build.mjs +1 -1
- package/dist/agentic/guides/module-facts.json +90266 -20749
- package/dist/agentic/guides/modules/ai_assistant.md +170 -60
- package/dist/agentic/guides/modules/api_docs.md +26 -1
- package/dist/agentic/guides/modules/api_keys.md +57 -17
- package/dist/agentic/guides/modules/attachments.md +72 -19
- package/dist/agentic/guides/modules/audit_logs.md +95 -29
- package/dist/agentic/guides/modules/auth.md +218 -83
- package/dist/agentic/guides/modules/business_rules.md +108 -34
- package/dist/agentic/guides/modules/catalog.md +386 -157
- package/dist/agentic/guides/modules/channel_gmail.md +55 -6
- package/dist/agentic/guides/modules/channel_imap.md +55 -6
- package/dist/agentic/guides/modules/checkout.md +254 -129
- package/dist/agentic/guides/modules/communication_channels.md +280 -109
- package/dist/agentic/guides/modules/configs.md +68 -11
- package/dist/agentic/guides/modules/content.md +26 -1
- package/dist/agentic/guides/modules/currencies.md +109 -44
- package/dist/agentic/guides/modules/customer_accounts.md +350 -156
- package/dist/agentic/guides/modules/customers.md +642 -294
- package/dist/agentic/guides/modules/dashboards.md +122 -28
- package/dist/agentic/guides/modules/data_sync.md +117 -37
- package/dist/agentic/guides/modules/design_system.md +36 -2
- package/dist/agentic/guides/modules/dictionaries.md +78 -20
- package/dist/agentic/guides/modules/directory.md +110 -45
- package/dist/agentic/guides/modules/entities.md +95 -38
- package/dist/agentic/guides/modules/events.md +31 -1
- package/dist/agentic/guides/modules/feature_toggles.md +79 -12
- package/dist/agentic/guides/modules/gateway_stripe.md +61 -6
- package/dist/agentic/guides/modules/generators.md +17 -1
- package/dist/agentic/guides/modules/inbox_ops.md +171 -60
- package/dist/agentic/guides/modules/integrations.md +116 -30
- package/dist/agentic/guides/modules/messages.md +187 -66
- package/dist/agentic/guides/modules/notifications.md +64 -9
- package/dist/agentic/guides/modules/onboarding.md +47 -8
- package/dist/agentic/guides/modules/payment_gateways.md +133 -48
- package/dist/agentic/guides/modules/perspectives.md +50 -12
- package/dist/agentic/guides/modules/planner.md +102 -32
- package/dist/agentic/guides/modules/portal.md +58 -15
- package/dist/agentic/guides/modules/progress.md +78 -30
- package/dist/agentic/guides/modules/query_index.md +115 -53
- package/dist/agentic/guides/modules/record_locks.md +178 -59
- package/dist/agentic/guides/modules/resources.md +155 -78
- package/dist/agentic/guides/modules/sales.md +620 -257
- package/dist/agentic/guides/modules/scheduler.md +95 -19
- package/dist/agentic/guides/modules/search.md +124 -20
- package/dist/agentic/guides/modules/security.md +251 -105
- package/dist/agentic/guides/modules/shipping_carriers.md +106 -37
- package/dist/agentic/guides/modules/sso.md +121 -49
- package/dist/agentic/guides/modules/staff.md +314 -126
- package/dist/agentic/guides/modules/storage_s3.md +55 -6
- package/dist/agentic/guides/modules/sync_akeneo.md +54 -4
- package/dist/agentic/guides/modules/sync_excel.md +49 -11
- package/dist/agentic/guides/modules/system_status_overlays.md +33 -5
- package/dist/agentic/guides/modules/translations.md +76 -21
- package/dist/agentic/guides/modules/webhooks.md +166 -82
- package/dist/agentic/guides/modules/wms.md +383 -223
- package/dist/agentic/guides/modules/workflows.md +248 -112
- package/dist/agentic/guides/upstream/AGENTS.md +1 -1
- package/dist/agentic/guides/upstream/manifest.json +2 -2
- package/dist/agentic/shared/AGENTS.md.template +6 -5
- package/dist/agentic/shared/ai/harness/cases.json +3 -0
- package/dist/agentic/shared/ai/lessons/_template.md +16 -0
- package/dist/agentic/shared/ai/lessons.md +26 -3
- package/dist/agentic/shared/ai/skills/om-evolve-harness/SKILL.md +7 -6
- package/dist/agentic/shared/ai/skills/om-system-extension/references/unified-overrides.md +4 -2
- package/dist/agentic/shared/scripts/check-lessons.mjs +178 -0
- package/dist/lib/agentic-setup.js +2 -2
- package/dist/lib/agentic-setup.js.map +2 -2
- package/dist/lib/generators/module-extension-facts.js +652 -24
- package/dist/lib/generators/module-extension-facts.js.map +3 -3
- package/dist/lib/generators/module-fact-sources.js +70 -0
- package/dist/lib/generators/module-fact-sources.js.map +7 -0
- package/dist/lib/generators/module-facts.js +1001 -26
- package/dist/lib/generators/module-facts.js.map +3 -3
- package/dist/lib/generators/module-override-targets.js +688 -0
- package/dist/lib/generators/module-override-targets.js.map +7 -0
- package/dist/lib/testing/integration.js +5 -0
- package/dist/lib/testing/integration.js.map +2 -2
- package/package.json +5 -5
- package/src/lib/__tests__/agentic-setup.ownership.test.ts +6 -0
- package/src/lib/agentic-setup.ts +6 -2
- package/src/lib/generators/__tests__/activation-adapter-coverage.test.ts +92 -0
- package/src/lib/generators/__tests__/module-extension-activations.test.ts +762 -0
- package/src/lib/generators/__tests__/module-extension-facts.test.ts +4 -2
- package/src/lib/generators/__tests__/module-facts.bc-guard.test.ts +37 -3
- package/src/lib/generators/__tests__/module-facts.owned-contracts.test.ts +561 -0
- package/src/lib/generators/__tests__/module-override-targets.roundtrip.test.ts +149 -0
- package/src/lib/generators/__tests__/module-override-targets.test.ts +304 -0
- package/src/lib/generators/module-extension-facts.ts +923 -27
- package/src/lib/generators/module-fact-sources.ts +108 -0
- package/src/lib/generators/module-facts.ts +1360 -25
- package/src/lib/generators/module-override-targets.ts +863 -0
- package/src/lib/testing/__tests__/integration.test.ts +4 -0
- package/src/lib/testing/integration.ts +5 -0
|
@@ -493,7 +493,7 @@ function factRefHost(options) {
|
|
|
493
493
|
...options.scopeContract ? { scopeContract: options.scopeContract } : {},
|
|
494
494
|
bound: true,
|
|
495
495
|
stability: "stable",
|
|
496
|
-
source: { kind: "fact-ref", factSection: options.factSection, factKey: options.id }
|
|
496
|
+
source: { kind: "fact-ref", factSection: options.factSection, factKey: options.factKey ?? options.id }
|
|
497
497
|
};
|
|
498
498
|
}
|
|
499
499
|
function sourceFilesBelow(directory) {
|
|
@@ -527,6 +527,7 @@ function apiExtensionHostIds(moduleId, moduleRoot) {
|
|
|
527
527
|
const cached = activeExtractionCache?.apiExtensionHostIds.get(cacheKey);
|
|
528
528
|
if (cached) return cached;
|
|
529
529
|
const entityIds = /* @__PURE__ */ new Set();
|
|
530
|
+
const entitySourceFiles = {};
|
|
530
531
|
const commandIds = /* @__PURE__ */ new Set();
|
|
531
532
|
const routeIds = /* @__PURE__ */ new Set();
|
|
532
533
|
for (const filePath of sourceFilesBelow(path.join(moduleRoot, "api"))) {
|
|
@@ -545,13 +546,19 @@ function apiExtensionHostIds(moduleId, moduleRoot) {
|
|
|
545
546
|
if (name === "enrichers") {
|
|
546
547
|
const config = staticValue(node.initializer, context);
|
|
547
548
|
const entityId = isStaticObject(config) ? stringValue(config.entityId) : void 0;
|
|
548
|
-
if (entityId)
|
|
549
|
+
if (entityId) {
|
|
550
|
+
entityIds.add(entityId);
|
|
551
|
+
if (!entitySourceFiles[entityId]) entitySourceFiles[entityId] = filePath;
|
|
552
|
+
}
|
|
549
553
|
}
|
|
550
554
|
}
|
|
551
555
|
if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === "validateCrudMutationGuard") {
|
|
552
556
|
const input = node.arguments[1] ? staticValue(node.arguments[1], context) : void 0;
|
|
553
557
|
const entityId = isStaticObject(input) ? stringValue(input.resourceKind) : void 0;
|
|
554
|
-
if (entityId)
|
|
558
|
+
if (entityId) {
|
|
559
|
+
entityIds.add(entityId);
|
|
560
|
+
if (!entitySourceFiles[entityId]) entitySourceFiles[entityId] = filePath;
|
|
561
|
+
}
|
|
555
562
|
}
|
|
556
563
|
node.forEachChild(visit);
|
|
557
564
|
};
|
|
@@ -559,6 +566,7 @@ function apiExtensionHostIds(moduleId, moduleRoot) {
|
|
|
559
566
|
}
|
|
560
567
|
const result = {
|
|
561
568
|
entityIds: [...entityIds].sort((left, right) => left.localeCompare(right)),
|
|
569
|
+
entitySourceFiles,
|
|
562
570
|
commandIds: [...commandIds].sort((left, right) => left.localeCompare(right)),
|
|
563
571
|
routeIds: [...routeIds].sort((left, right) => left.localeCompare(right))
|
|
564
572
|
};
|
|
@@ -580,6 +588,104 @@ function extractKnownCommandIds(moduleId, moduleRoot) {
|
|
|
580
588
|
const apiHosts = apiExtensionHostIds(moduleId, moduleRoot);
|
|
581
589
|
return moduleCommandIds(moduleRoot, apiHosts.commandIds);
|
|
582
590
|
}
|
|
591
|
+
const HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"];
|
|
592
|
+
const API_INTERCEPTOR_BRIDGE_CALLS = {
|
|
593
|
+
runApiInterceptorsBefore: "before",
|
|
594
|
+
runApiInterceptorsAfter: "after"
|
|
595
|
+
};
|
|
596
|
+
function isHttpMethodName(value) {
|
|
597
|
+
return HTTP_METHODS.includes(value);
|
|
598
|
+
}
|
|
599
|
+
function exportedHttpMethods(file) {
|
|
600
|
+
const methods = /* @__PURE__ */ new Set();
|
|
601
|
+
const consider = (name) => {
|
|
602
|
+
if (name && isHttpMethodName(name)) methods.add(name);
|
|
603
|
+
};
|
|
604
|
+
const considerBindingName = (name) => {
|
|
605
|
+
if (ts.isIdentifier(name)) {
|
|
606
|
+
consider(name.text);
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
609
|
+
for (const element of name.elements) {
|
|
610
|
+
if (ts.isBindingElement(element)) considerBindingName(element.name);
|
|
611
|
+
}
|
|
612
|
+
};
|
|
613
|
+
const visit = (node) => {
|
|
614
|
+
if (ts.isVariableStatement(node) && node.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword)) {
|
|
615
|
+
for (const declaration of node.declarationList.declarations) {
|
|
616
|
+
considerBindingName(declaration.name);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
if (ts.isFunctionDeclaration(node) && node.name && node.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword)) {
|
|
620
|
+
consider(node.name.text);
|
|
621
|
+
}
|
|
622
|
+
if (ts.isExportDeclaration(node) && node.exportClause && ts.isNamedExports(node.exportClause)) {
|
|
623
|
+
for (const element of node.exportClause.elements) consider(element.name.text);
|
|
624
|
+
}
|
|
625
|
+
node.forEachChild(visit);
|
|
626
|
+
};
|
|
627
|
+
file.forEachChild(visit);
|
|
628
|
+
return [...methods].sort((left, right) => left.localeCompare(right));
|
|
629
|
+
}
|
|
630
|
+
function extractApiRouteInterceptorBridges(file, context) {
|
|
631
|
+
const exportedMethods = exportedHttpMethods(file);
|
|
632
|
+
const phasesByMethod = /* @__PURE__ */ new Map();
|
|
633
|
+
const addPhase = (method, phase) => {
|
|
634
|
+
const phases = phasesByMethod.get(method) ?? /* @__PURE__ */ new Set();
|
|
635
|
+
phases.add(phase);
|
|
636
|
+
phasesByMethod.set(method, phases);
|
|
637
|
+
};
|
|
638
|
+
const visit = (node) => {
|
|
639
|
+
if (ts.isCallExpression(node) && ts.isIdentifier(node.expression)) {
|
|
640
|
+
const calleeName = node.expression.text;
|
|
641
|
+
if (calleeName === "makeCrudRoute") {
|
|
642
|
+
for (const method of exportedMethods) {
|
|
643
|
+
addPhase(method, "before");
|
|
644
|
+
addPhase(method, "after");
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
const phase = API_INTERCEPTOR_BRIDGE_CALLS[calleeName];
|
|
648
|
+
if (phase) {
|
|
649
|
+
const callOptions = node.arguments[0] ? staticValue(node.arguments[0], context) : void 0;
|
|
650
|
+
const declaredMethod = isStaticObject(callOptions) ? stringValue(callOptions.method) : void 0;
|
|
651
|
+
const methods = declaredMethod && isHttpMethodName(declaredMethod) ? [declaredMethod] : exportedMethods;
|
|
652
|
+
for (const method of methods) addPhase(method, phase);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
node.forEachChild(visit);
|
|
656
|
+
};
|
|
657
|
+
file.forEachChild(visit);
|
|
658
|
+
return [...phasesByMethod.entries()].map(([method, phases]) => ({
|
|
659
|
+
method,
|
|
660
|
+
phases: [...phases].sort((left, right) => left.localeCompare(right))
|
|
661
|
+
})).sort((left, right) => left.method.localeCompare(right.method));
|
|
662
|
+
}
|
|
663
|
+
function extractActivationTargetOwners(options) {
|
|
664
|
+
const apiRoutes = [];
|
|
665
|
+
for (const filePath of sourceFilesBelow(path.join(options.moduleRoot, "api"))) {
|
|
666
|
+
const routeId = apiRouteId(options.moduleId, options.moduleRoot, filePath);
|
|
667
|
+
if (!routeId) continue;
|
|
668
|
+
const file = sourceFile(filePath);
|
|
669
|
+
const bridges = file ? extractApiRouteInterceptorBridges(file, buildStaticContext(file)) : [];
|
|
670
|
+
apiRoutes.push({
|
|
671
|
+
id: routeId,
|
|
672
|
+
source: { sourcePath: portablePath(options.moduleRoot, options.sourceRoot, filePath) },
|
|
673
|
+
bridges
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
const commands = [];
|
|
677
|
+
const seenCommands = /* @__PURE__ */ new Set();
|
|
678
|
+
for (const filePath of sourceFilesBelow(path.join(options.moduleRoot, "commands"))) {
|
|
679
|
+
if (path.basename(filePath).replace(/\.(?:ts|tsx)$/, "") === "interceptors") continue;
|
|
680
|
+
const source = { sourcePath: portablePath(options.moduleRoot, options.sourceRoot, filePath) };
|
|
681
|
+
for (const id of extractCommandIdsFromSource(filePath)) {
|
|
682
|
+
if (seenCommands.has(id)) continue;
|
|
683
|
+
seenCommands.add(id);
|
|
684
|
+
commands.push({ id, source });
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
return { apiRoutes, commands };
|
|
688
|
+
}
|
|
583
689
|
function extractFactRefHosts(options) {
|
|
584
690
|
const hosts = [];
|
|
585
691
|
for (const entity of options.entities) {
|
|
@@ -631,6 +737,7 @@ function extractFactRefHosts(options) {
|
|
|
631
737
|
family: "query-lifecycle",
|
|
632
738
|
capabilities: ["sync-subscriber"],
|
|
633
739
|
factSection: "searchEntities",
|
|
740
|
+
factKey: entityId,
|
|
634
741
|
phases: phase === "querying" ? ["block", "query-transform"] : ["result-transform", "scope-reapply"],
|
|
635
742
|
activation: "caller-opt-in",
|
|
636
743
|
scopeContract: "tenant-and-organization-reapplied-after-result"
|
|
@@ -639,7 +746,8 @@ function extractFactRefHosts(options) {
|
|
|
639
746
|
}
|
|
640
747
|
const apiHosts = apiExtensionHostIds(options.moduleId, options.moduleRoot);
|
|
641
748
|
for (const entityId of apiHosts.entityIds) {
|
|
642
|
-
|
|
749
|
+
const declaringFile = apiHosts.entitySourceFiles[entityId];
|
|
750
|
+
const host = factRefHost({
|
|
643
751
|
key: `api-entity.${entityId}`,
|
|
644
752
|
id: entityId,
|
|
645
753
|
moduleId: options.moduleId,
|
|
@@ -648,7 +756,15 @@ function extractFactRefHosts(options) {
|
|
|
648
756
|
factSection: "apiRoutes",
|
|
649
757
|
activation: "host-opt-in",
|
|
650
758
|
scopeContract: "tenant-and-organization"
|
|
651
|
-
})
|
|
759
|
+
});
|
|
760
|
+
hosts.push(declaringFile ? {
|
|
761
|
+
...host,
|
|
762
|
+
source: {
|
|
763
|
+
kind: "declaration",
|
|
764
|
+
path: portablePath(options.moduleRoot, options.sourceRoot, declaringFile),
|
|
765
|
+
symbol: entityId
|
|
766
|
+
}
|
|
767
|
+
} : host);
|
|
652
768
|
}
|
|
653
769
|
return sortHosts([...new Map(hosts.map((host) => [`${host.family}:${host.id}`, host])).values()]);
|
|
654
770
|
}
|
|
@@ -738,7 +854,8 @@ function extractEnrichers(options) {
|
|
|
738
854
|
const targetEntity = stringValue(entry.targetEntity);
|
|
739
855
|
if (!targetEntity) return null;
|
|
740
856
|
const timeoutMs = numberValue(entry.timeout) ?? 2e3;
|
|
741
|
-
const
|
|
857
|
+
const queryEngineConfig = isStaticObject(entry.queryEngine) ? entry.queryEngine : null;
|
|
858
|
+
const queryEngine = queryEngineConfig && booleanValue(queryEngineConfig.enabled) === true ? queryEngineConfig : null;
|
|
742
859
|
const surfaces = [entry.enrichMany === true ? "list" : null, entry.enrichOne === true ? "detail" : null].filter((value) => value !== null);
|
|
743
860
|
const base = contributionBase(id, sourcePath, id);
|
|
744
861
|
return {
|
|
@@ -879,14 +996,13 @@ function extractSubscribers(options) {
|
|
|
879
996
|
const directory = path.join(options.moduleRoot, "subscribers");
|
|
880
997
|
if (!fs.existsSync(directory)) return [];
|
|
881
998
|
const facts = [];
|
|
882
|
-
for (const
|
|
883
|
-
if (!entry.isFile() || !/\.(?:ts|tsx)$/.test(entry.name) || /\.(?:test|spec)\./.test(entry.name)) continue;
|
|
884
|
-
const filePath = path.join(directory, entry.name);
|
|
999
|
+
for (const filePath of sourceFilesBelow(directory)) {
|
|
885
1000
|
const metadata = readRootObject(filePath, "metadata");
|
|
886
1001
|
if (!metadata) continue;
|
|
887
1002
|
const event = stringValue(metadata.event);
|
|
888
1003
|
if (!event) continue;
|
|
889
|
-
const
|
|
1004
|
+
const relativeStem = path.relative(directory, filePath).replace(/\.tsx?$/, "").split(path.sep).join("/");
|
|
1005
|
+
const subscriberId = stringValue(metadata.id) ?? `${options.moduleId}:${relativeStem}`;
|
|
890
1006
|
const persistent = booleanValue(metadata.persistent) ?? false;
|
|
891
1007
|
const sync = booleanValue(metadata.sync) ?? !persistent;
|
|
892
1008
|
const contribution = contributionBase(subscriberId, portablePath(options.moduleRoot, options.sourceRoot, filePath), "metadata");
|
|
@@ -931,7 +1047,8 @@ function extractNotificationReactions(options) {
|
|
|
931
1047
|
build(entry, sourcePath, index) {
|
|
932
1048
|
const notificationType = stringValue(entry.notificationType);
|
|
933
1049
|
if (!notificationType) return null;
|
|
934
|
-
const
|
|
1050
|
+
const declaredId = stringValue(entry.id);
|
|
1051
|
+
const id = declaredId ?? `${options.moduleId}.notification-handler.${index}`;
|
|
935
1052
|
const contribution = contributionBase(id, sourcePath, "notificationHandlers");
|
|
936
1053
|
return {
|
|
937
1054
|
...contribution,
|
|
@@ -942,7 +1059,8 @@ function extractNotificationReactions(options) {
|
|
|
942
1059
|
details: {
|
|
943
1060
|
transports: ["notification-effect"],
|
|
944
1061
|
hooks: ["useNotificationEffect"],
|
|
945
|
-
audienceScopeContract: "tenant-organization-user-role-and-customer"
|
|
1062
|
+
audienceScopeContract: "tenant-organization-user-role-and-customer",
|
|
1063
|
+
...declaredId ? { overrideKey: declaredId } : {}
|
|
946
1064
|
}
|
|
947
1065
|
};
|
|
948
1066
|
}
|
|
@@ -1007,16 +1125,32 @@ function extractSpecializedRegistries(options) {
|
|
|
1007
1125
|
}
|
|
1008
1126
|
const integrationPath = conventionPath(options.moduleRoot, "integration.ts");
|
|
1009
1127
|
if (integrationPath) {
|
|
1010
|
-
const integration = readRootObject(integrationPath, "integration");
|
|
1011
1128
|
const sourcePath = portablePath(options.moduleRoot, options.sourceRoot, integrationPath);
|
|
1012
|
-
const
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
const
|
|
1019
|
-
|
|
1129
|
+
const addIntegration = (integration, symbol, bundleId) => {
|
|
1130
|
+
const integrationId = stringValue(integration.id);
|
|
1131
|
+
if (integrationId) {
|
|
1132
|
+
add(integrationId, "integration", sourcePath, "integrations", bundleId ? `${symbol}[${bundleId}]` : symbol);
|
|
1133
|
+
}
|
|
1134
|
+
const providerKey = stringValue(integration.providerKey);
|
|
1135
|
+
const category = stringValue(integration.category);
|
|
1136
|
+
const categoryRegistry = category === "payment" ? "payment" : category === "shipping" ? "shipping" : category === "currency" ? "currency" : null;
|
|
1137
|
+
if (providerKey && categoryRegistry) {
|
|
1138
|
+
const specialistRoute = categoryRegistry === "payment" ? "paymentGateways" : categoryRegistry === "shipping" ? "shippingCarriers" : "currencies";
|
|
1139
|
+
add(providerKey, categoryRegistry, sourcePath, specialistRoute, `${symbol}.providerKey`);
|
|
1140
|
+
}
|
|
1141
|
+
};
|
|
1142
|
+
const singular = readRootObject(integrationPath, "integration");
|
|
1143
|
+
if (singular) addIntegration(singular, "integration");
|
|
1144
|
+
for (const integration of readConventionObjectArray(integrationPath, "integrations")) {
|
|
1145
|
+
addIntegration(integration, "integrations");
|
|
1146
|
+
}
|
|
1147
|
+
const bundle = readRootObject(integrationPath, "integrationBundle") ?? readRootObject(integrationPath, "bundle");
|
|
1148
|
+
if (bundle) {
|
|
1149
|
+
const bundleId = stringValue(bundle.id) ?? "bundle";
|
|
1150
|
+
const members = Array.isArray(bundle.integrations) ? bundle.integrations : [];
|
|
1151
|
+
for (const member of members) {
|
|
1152
|
+
if (isStaticObject(member)) addIntegration(member, "integrationBundle", bundleId);
|
|
1153
|
+
}
|
|
1020
1154
|
}
|
|
1021
1155
|
}
|
|
1022
1156
|
const workflowsPath = conventionPath(options.moduleRoot, "workflows.ts");
|
|
@@ -1071,6 +1205,147 @@ function extractSpecializedRegistries(options) {
|
|
|
1071
1205
|
}
|
|
1072
1206
|
return facts;
|
|
1073
1207
|
}
|
|
1208
|
+
const ACTIVATION_ADAPTERS = {
|
|
1209
|
+
"crud-response-enricher": { contributionKinds: ["response-enricher"], mode: "call-site-object" },
|
|
1210
|
+
"query-enricher": { contributionKinds: ["response-enricher"], mode: "call-site-object" },
|
|
1211
|
+
"mutation-guard": { contributionKinds: ["mutation-guard"], mode: "call-site-object" },
|
|
1212
|
+
"api-interceptor-bridge": { contributionKinds: ["api-interceptor"], mode: "host-reference" },
|
|
1213
|
+
"command-interceptor-bridge": { contributionKinds: ["command-interceptor"], mode: "owner-reference" },
|
|
1214
|
+
"widget-injection-consumer": { contributionKinds: ["widget", "data-table", "crud-form"], mode: "host-reference" },
|
|
1215
|
+
"component-extension-consumer": { contributionKinds: ["component-override"], mode: "host-reference" },
|
|
1216
|
+
"dashboard-host-consumer": { contributionKinds: ["widget"], mode: "host-reference" }
|
|
1217
|
+
};
|
|
1218
|
+
const CONTRIBUTION_ACTIVATION_CLASSIFICATION = {
|
|
1219
|
+
"response-enricher": ["crud-response-enricher", "query-enricher"],
|
|
1220
|
+
"mutation-guard": ["mutation-guard"],
|
|
1221
|
+
"api-interceptor": ["api-interceptor-bridge"],
|
|
1222
|
+
"command-interceptor": ["command-interceptor-bridge"],
|
|
1223
|
+
"widget": ["widget-injection-consumer", "dashboard-host-consumer"],
|
|
1224
|
+
"data-table": ["widget-injection-consumer"],
|
|
1225
|
+
"crud-form": ["widget-injection-consumer"],
|
|
1226
|
+
"component-override": ["component-extension-consumer"],
|
|
1227
|
+
"entity-extension": "capability-only",
|
|
1228
|
+
"subscriber": "capability-only",
|
|
1229
|
+
"browser-reaction": "capability-only",
|
|
1230
|
+
"specialized-registry": "capability-only",
|
|
1231
|
+
"module-override": "capability-only"
|
|
1232
|
+
};
|
|
1233
|
+
const ALL_ACTIVATION_KINDS = Object.keys(ACTIVATION_ADAPTERS);
|
|
1234
|
+
const ALL_CONTRIBUTION_KINDS = Object.keys(CONTRIBUTION_ACTIVATION_CLASSIFICATION);
|
|
1235
|
+
const MUTATION_GUARD_BRIDGE_ADAPTERS = {
|
|
1236
|
+
runRouteMutationGuards: { resourceArgument: "input-property" },
|
|
1237
|
+
validateCrudMutationGuard: { resourceArgument: "any-argument" },
|
|
1238
|
+
runMutationGuards: { resourceArgument: "any-argument" }
|
|
1239
|
+
};
|
|
1240
|
+
const MUTATION_GUARD_SOURCE_DIRECTORIES = ["api", "lib"];
|
|
1241
|
+
const MUTATION_GUARD_OPERATIONS = /* @__PURE__ */ new Set(["create", "update", "delete", "custom"]);
|
|
1242
|
+
function toRegistryGuardOperation(operation) {
|
|
1243
|
+
return operation === "custom" ? "update" : operation;
|
|
1244
|
+
}
|
|
1245
|
+
function readMutationGuardBridge(node, context) {
|
|
1246
|
+
const callee = node.expression;
|
|
1247
|
+
const calleeName = ts.isIdentifier(callee) ? callee.text : ts.isPropertyAccessExpression(callee) ? callee.name.text : null;
|
|
1248
|
+
const adapter = calleeName ? MUTATION_GUARD_BRIDGE_ADAPTERS[calleeName] : void 0;
|
|
1249
|
+
if (!adapter) return null;
|
|
1250
|
+
const argumentObjects = node.arguments.map((argument) => staticValue(argument, context)).filter(isStaticObject);
|
|
1251
|
+
const resourceObjects = adapter.resourceArgument === "input-property" ? argumentObjects.flatMap((argumentObject) => isStaticObject(argumentObject.input) ? [argumentObject.input] : []) : argumentObjects;
|
|
1252
|
+
for (const resourceObject of resourceObjects) {
|
|
1253
|
+
const entityId = stringValue(resourceObject.resourceKind) ?? stringValue(resourceObject.entityId);
|
|
1254
|
+
if (!entityId) continue;
|
|
1255
|
+
const declaredOperation = stringValue(resourceObject.operation);
|
|
1256
|
+
const operations = declaredOperation && MUTATION_GUARD_OPERATIONS.has(declaredOperation) ? [toRegistryGuardOperation(declaredOperation)] : [];
|
|
1257
|
+
return { entityId, operations };
|
|
1258
|
+
}
|
|
1259
|
+
return null;
|
|
1260
|
+
}
|
|
1261
|
+
function nodeLine(file, node) {
|
|
1262
|
+
return file.getLineAndCharacterOfPosition(node.getStart(file)).line + 1;
|
|
1263
|
+
}
|
|
1264
|
+
function activationId(host, kind) {
|
|
1265
|
+
const method = host.method ? `:${host.method}` : "";
|
|
1266
|
+
return `${host.kind}:${host.id}${method}:${kind}`;
|
|
1267
|
+
}
|
|
1268
|
+
function sortActivations(activations) {
|
|
1269
|
+
return [...activations].sort((left, right) => left.id.localeCompare(right.id));
|
|
1270
|
+
}
|
|
1271
|
+
function isQueryWithExtensionsCall(node, context) {
|
|
1272
|
+
if (!ts.isCallExpression(node)) return false;
|
|
1273
|
+
const callee = node.expression;
|
|
1274
|
+
const calleeName = ts.isPropertyAccessExpression(callee) ? callee.name.text : ts.isIdentifier(callee) ? callee.text : null;
|
|
1275
|
+
if (calleeName !== "query") return false;
|
|
1276
|
+
const queryOptions = node.arguments[1] ? staticValue(node.arguments[1], context) : void 0;
|
|
1277
|
+
return isStaticObject(queryOptions) && queryOptions.extensions !== void 0;
|
|
1278
|
+
}
|
|
1279
|
+
function extractCallSiteActivations(options) {
|
|
1280
|
+
const activations = [];
|
|
1281
|
+
const byId = /* @__PURE__ */ new Map();
|
|
1282
|
+
const push = (activation) => {
|
|
1283
|
+
const existing = byId.get(activation.id);
|
|
1284
|
+
if (existing) {
|
|
1285
|
+
if (!existing.operations || !activation.operations) {
|
|
1286
|
+
delete existing.operations;
|
|
1287
|
+
} else {
|
|
1288
|
+
existing.operations = [.../* @__PURE__ */ new Set([...existing.operations, ...activation.operations])].sort((left, right) => left.localeCompare(right));
|
|
1289
|
+
}
|
|
1290
|
+
return;
|
|
1291
|
+
}
|
|
1292
|
+
byId.set(activation.id, activation);
|
|
1293
|
+
activations.push(activation);
|
|
1294
|
+
};
|
|
1295
|
+
const scannedFiles = MUTATION_GUARD_SOURCE_DIRECTORIES.flatMap((directory) => sourceFilesBelow(path.join(options.moduleRoot, directory)));
|
|
1296
|
+
for (const filePath of scannedFiles) {
|
|
1297
|
+
const file = sourceFile(filePath);
|
|
1298
|
+
if (!file) continue;
|
|
1299
|
+
const context = buildStaticContext(file);
|
|
1300
|
+
const sourcePath = portablePath(options.moduleRoot, options.sourceRoot, filePath);
|
|
1301
|
+
const visit = (node) => {
|
|
1302
|
+
if (ts.isPropertyAssignment(node) && propertyName(node.name) === "enrichers") {
|
|
1303
|
+
const config = staticValue(node.initializer, context);
|
|
1304
|
+
const entityId = isStaticObject(config) ? stringValue(config.entityId) : void 0;
|
|
1305
|
+
if (entityId) {
|
|
1306
|
+
const host = { kind: "entity", id: entityId, moduleId: options.moduleId };
|
|
1307
|
+
push({
|
|
1308
|
+
id: activationId(host, "crud-response-enricher"),
|
|
1309
|
+
kind: "crud-response-enricher",
|
|
1310
|
+
host,
|
|
1311
|
+
contributionKinds: ["response-enricher"],
|
|
1312
|
+
source: { sourcePath, line: nodeLine(file, node) }
|
|
1313
|
+
});
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
if (isQueryWithExtensionsCall(node, context)) {
|
|
1317
|
+
const entityId = stringValue(staticValue(node.arguments[0], context));
|
|
1318
|
+
if (entityId) {
|
|
1319
|
+
const host = { kind: "entity", id: entityId, moduleId: options.moduleId };
|
|
1320
|
+
push({
|
|
1321
|
+
id: activationId(host, "query-enricher"),
|
|
1322
|
+
kind: "query-enricher",
|
|
1323
|
+
host,
|
|
1324
|
+
contributionKinds: ["response-enricher"],
|
|
1325
|
+
source: { sourcePath, line: nodeLine(file, node) }
|
|
1326
|
+
});
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
if (ts.isCallExpression(node)) {
|
|
1330
|
+
const bridge = readMutationGuardBridge(node, context);
|
|
1331
|
+
if (bridge) {
|
|
1332
|
+
const host = { kind: "entity", id: bridge.entityId, moduleId: options.moduleId };
|
|
1333
|
+
push({
|
|
1334
|
+
id: activationId(host, "mutation-guard"),
|
|
1335
|
+
kind: "mutation-guard",
|
|
1336
|
+
host,
|
|
1337
|
+
contributionKinds: ["mutation-guard"],
|
|
1338
|
+
...bridge.operations.length > 0 ? { operations: bridge.operations } : {},
|
|
1339
|
+
source: { sourcePath, line: nodeLine(file, node) }
|
|
1340
|
+
});
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
node.forEachChild(visit);
|
|
1344
|
+
};
|
|
1345
|
+
file.forEachChild(visit);
|
|
1346
|
+
}
|
|
1347
|
+
return sortActivations(activations);
|
|
1348
|
+
}
|
|
1074
1349
|
function extractModuleExtensionFacts(options) {
|
|
1075
1350
|
const declared = extractDeclaredHosts(options);
|
|
1076
1351
|
const hosts = sortHosts([...declared.hosts, ...extractFactRefHosts(options)]);
|
|
@@ -1087,7 +1362,12 @@ function extractModuleExtensionFacts(options) {
|
|
|
1087
1362
|
...extractComponentOverrides(options),
|
|
1088
1363
|
...extractSpecializedRegistries(options)
|
|
1089
1364
|
]);
|
|
1090
|
-
return {
|
|
1365
|
+
return {
|
|
1366
|
+
hosts,
|
|
1367
|
+
contributions,
|
|
1368
|
+
unresolved: declared.unresolved,
|
|
1369
|
+
activations: extractCallSiteActivations(options)
|
|
1370
|
+
};
|
|
1091
1371
|
}
|
|
1092
1372
|
function patternMatches(pattern, targetId) {
|
|
1093
1373
|
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\\\{[^}]+\\\}/g, "[^:]+").replace(/\*/g, ".*");
|
|
@@ -1128,7 +1408,7 @@ function correlateExtensionTarget(targetFact, options) {
|
|
|
1128
1408
|
return { id: targetFact.id, resolution: "fact-ref", factRef: { factSection: "events", factKey: targetFact.id } };
|
|
1129
1409
|
}
|
|
1130
1410
|
if (options.commandIds?.has(targetFact.id)) {
|
|
1131
|
-
return factTarget(targetFact.id, "
|
|
1411
|
+
return factTarget(targetFact.id, "ownedContracts.command");
|
|
1132
1412
|
}
|
|
1133
1413
|
const route = [...options.apiRoutes].find((apiRoute) => apiRoute === targetFact.id || apiRoute.endsWith(`/${targetFact.id}`));
|
|
1134
1414
|
if (route) return { id: targetFact.id, resolution: "fact-ref", factRef: { factSection: "apiRoutes", factKey: route } };
|
|
@@ -1164,11 +1444,343 @@ function correlateModuleExtensionFacts(options) {
|
|
|
1164
1444
|
result[moduleId] = {
|
|
1165
1445
|
hosts: sortHosts([...surface.hosts]),
|
|
1166
1446
|
contributions: sortContributions(contributions),
|
|
1167
|
-
unresolved: unresolved.sort((left, right) => left.key.localeCompare(right.key))
|
|
1447
|
+
unresolved: unresolved.sort((left, right) => left.key.localeCompare(right.key)),
|
|
1448
|
+
activations: sortActivations([...surface.activations ?? []])
|
|
1449
|
+
};
|
|
1450
|
+
}
|
|
1451
|
+
return result;
|
|
1452
|
+
}
|
|
1453
|
+
function normalizeRouteId(routeId) {
|
|
1454
|
+
return routeId.replace(/^\//, "");
|
|
1455
|
+
}
|
|
1456
|
+
const FRAMEWORK_HOST_OWNER = "framework";
|
|
1457
|
+
const FRAMEWORK_HOST_FAMILIES_BY_ACTIVATION = {
|
|
1458
|
+
"dashboard-host-consumer": ["dashboard"],
|
|
1459
|
+
"widget-injection-consumer": ["menu", "generic", "crud-form", "data-table", "notification", "integration"],
|
|
1460
|
+
"component-extension-consumer": ["component-handle"]
|
|
1461
|
+
};
|
|
1462
|
+
function findFrameworkHost(targetId, activationKind) {
|
|
1463
|
+
const families = FRAMEWORK_HOST_FAMILIES_BY_ACTIVATION[activationKind];
|
|
1464
|
+
if (!families) return null;
|
|
1465
|
+
const candidates = FRAMEWORK_HOSTS.filter((host) => families.includes(host.family));
|
|
1466
|
+
const exact = candidates.find((host) => host.id === targetId);
|
|
1467
|
+
if (exact) return exact;
|
|
1468
|
+
return candidates.find((host) => host.resolution === "pattern" && patternMatches(host.id, targetId)) ?? null;
|
|
1469
|
+
}
|
|
1470
|
+
function operationsIntersect(activationOperations, contributionOperations) {
|
|
1471
|
+
if (!activationOperations || activationOperations.length === 0) return true;
|
|
1472
|
+
if (!contributionOperations || contributionOperations.length === 0) return true;
|
|
1473
|
+
return contributionOperations.some((operation) => activationOperations.includes(operation));
|
|
1474
|
+
}
|
|
1475
|
+
function contributionSourceRef(contribution) {
|
|
1476
|
+
return {
|
|
1477
|
+
sourcePath: contribution.source.path,
|
|
1478
|
+
...contribution.source.symbol ? { exportName: contribution.source.symbol } : {}
|
|
1479
|
+
};
|
|
1480
|
+
}
|
|
1481
|
+
function targetRefKindFor(contribution, targetId) {
|
|
1482
|
+
if (targetId === "*" || targetId.includes("*") || targetId.includes("{")) return "wildcard";
|
|
1483
|
+
switch (contribution.kind) {
|
|
1484
|
+
case "response-enricher":
|
|
1485
|
+
case "mutation-guard":
|
|
1486
|
+
case "entity-extension":
|
|
1487
|
+
return "entity";
|
|
1488
|
+
case "api-interceptor":
|
|
1489
|
+
return "api-route";
|
|
1490
|
+
case "command-interceptor":
|
|
1491
|
+
return "command";
|
|
1492
|
+
case "component-override":
|
|
1493
|
+
return "component";
|
|
1494
|
+
case "subscriber":
|
|
1495
|
+
return "event";
|
|
1496
|
+
case "browser-reaction":
|
|
1497
|
+
return contribution.details.transports.includes("notification-effect") ? "notification" : "event";
|
|
1498
|
+
case "widget":
|
|
1499
|
+
case "data-table":
|
|
1500
|
+
case "crud-form":
|
|
1501
|
+
return "widget-spot";
|
|
1502
|
+
case "specialized-registry":
|
|
1503
|
+
case "module-override":
|
|
1504
|
+
return "module";
|
|
1505
|
+
default:
|
|
1506
|
+
return "module";
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
function correlateIncomingExtensions(options) {
|
|
1510
|
+
const moduleIds = Object.keys(options.surfacesByModule).sort((left, right) => left.localeCompare(right));
|
|
1511
|
+
const apiRouteOwners = options.apiRouteOwners ?? /* @__PURE__ */ new Map();
|
|
1512
|
+
const commandOwners = options.commandOwners ?? /* @__PURE__ */ new Map();
|
|
1513
|
+
const activationIndex = /* @__PURE__ */ new Map();
|
|
1514
|
+
const activationKey = (kind, hostKind, id) => {
|
|
1515
|
+
const normId = hostKind === "api-route" ? normalizeRouteId(id) : id;
|
|
1516
|
+
return JSON.stringify([kind, hostKind, normId]);
|
|
1517
|
+
};
|
|
1518
|
+
const boundHostIndex = /* @__PURE__ */ new Map();
|
|
1519
|
+
for (const moduleId of moduleIds) {
|
|
1520
|
+
const surface = options.surfacesByModule[moduleId];
|
|
1521
|
+
for (const activation of surface.activations ?? []) {
|
|
1522
|
+
const key = activationKey(activation.kind, activation.host.kind, activation.host.id);
|
|
1523
|
+
const bucket = activationIndex.get(key);
|
|
1524
|
+
if (bucket) bucket.push({ moduleId, activation });
|
|
1525
|
+
else activationIndex.set(key, [{ moduleId, activation }]);
|
|
1526
|
+
}
|
|
1527
|
+
for (const host of surface.hosts) {
|
|
1528
|
+
if (!host.bound) continue;
|
|
1529
|
+
if (!boundHostIndex.has(host.id)) boundHostIndex.set(host.id, { moduleId, host });
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
const incomingByModule = {};
|
|
1533
|
+
const resolutionsByModule = {};
|
|
1534
|
+
const syntheticActivations = {};
|
|
1535
|
+
for (const moduleId of moduleIds) {
|
|
1536
|
+
incomingByModule[moduleId] = [];
|
|
1537
|
+
resolutionsByModule[moduleId] = [];
|
|
1538
|
+
syntheticActivations[moduleId] = [];
|
|
1539
|
+
}
|
|
1540
|
+
const registerSyntheticActivation = (ownerModule, activation) => {
|
|
1541
|
+
if (!syntheticActivations[ownerModule]) syntheticActivations[ownerModule] = [];
|
|
1542
|
+
if (!syntheticActivations[ownerModule].some((entry) => entry.id === activation.id)) {
|
|
1543
|
+
syntheticActivations[ownerModule].push(activation);
|
|
1544
|
+
}
|
|
1545
|
+
};
|
|
1546
|
+
const findCallSiteActivations = (contributionKind, activationKinds, targetRef, contributionOperations) => {
|
|
1547
|
+
const matches = [];
|
|
1548
|
+
for (const kind of activationKinds) {
|
|
1549
|
+
if (ACTIVATION_ADAPTERS[kind].mode !== "call-site-object") continue;
|
|
1550
|
+
const bucket = activationIndex.get(activationKey(kind, targetRef.kind, targetRef.id)) ?? [];
|
|
1551
|
+
for (const entry of bucket) {
|
|
1552
|
+
if (!entry.activation.contributionKinds.includes(contributionKind)) continue;
|
|
1553
|
+
if (!operationsIntersect(entry.activation.operations, contributionOperations)) continue;
|
|
1554
|
+
matches.push(entry);
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
return matches.sort((left, right) => left.activation.id.localeCompare(right.activation.id));
|
|
1558
|
+
};
|
|
1559
|
+
for (const contributorModuleId of moduleIds) {
|
|
1560
|
+
const surface = options.surfacesByModule[contributorModuleId];
|
|
1561
|
+
for (const contribution of surface.contributions) {
|
|
1562
|
+
const classification = CONTRIBUTION_ACTIVATION_CLASSIFICATION[contribution.kind];
|
|
1563
|
+
for (const resolvedTarget of contribution.targets) {
|
|
1564
|
+
const targetKind = targetRefKindFor(contribution, resolvedTarget.id);
|
|
1565
|
+
const baseTarget = { kind: targetKind, id: resolvedTarget.id };
|
|
1566
|
+
let resolution;
|
|
1567
|
+
let ownerModule = null;
|
|
1568
|
+
let boundActivationIds = [];
|
|
1569
|
+
if (targetKind === "wildcard" || resolvedTarget.resolution === "pattern") {
|
|
1570
|
+
resolution = "wildcard";
|
|
1571
|
+
} else if (resolvedTarget.resolution === "optional-external") {
|
|
1572
|
+
resolution = "optional-target-missing";
|
|
1573
|
+
} else if (resolvedTarget.resolution === "unresolved") {
|
|
1574
|
+
resolution = "unresolved";
|
|
1575
|
+
} else {
|
|
1576
|
+
const activationKinds = classification === "capability-only" ? [] : classification;
|
|
1577
|
+
const callSiteMatches = findCallSiteActivations(
|
|
1578
|
+
contribution.kind,
|
|
1579
|
+
activationKinds,
|
|
1580
|
+
baseTarget,
|
|
1581
|
+
contribution.operations
|
|
1582
|
+
);
|
|
1583
|
+
if (callSiteMatches.length > 0) {
|
|
1584
|
+
resolution = "bound";
|
|
1585
|
+
ownerModule = callSiteMatches[0].moduleId;
|
|
1586
|
+
boundActivationIds = callSiteMatches.map((entry) => entry.activation.id);
|
|
1587
|
+
} else {
|
|
1588
|
+
const referenceBinding = resolveReferenceBinding({
|
|
1589
|
+
contribution,
|
|
1590
|
+
activationKinds,
|
|
1591
|
+
targetRef: baseTarget,
|
|
1592
|
+
boundHostIndex,
|
|
1593
|
+
apiRouteOwners,
|
|
1594
|
+
commandOwners
|
|
1595
|
+
});
|
|
1596
|
+
if (referenceBinding) {
|
|
1597
|
+
resolution = "bound";
|
|
1598
|
+
const isFrameworkHost = referenceBinding.ownerModule === FRAMEWORK_HOST_OWNER;
|
|
1599
|
+
ownerModule = isFrameworkHost ? null : referenceBinding.ownerModule;
|
|
1600
|
+
const activationOwner = isFrameworkHost ? contributorModuleId : referenceBinding.ownerModule;
|
|
1601
|
+
for (const activation of referenceBinding.activations) {
|
|
1602
|
+
registerSyntheticActivation(activationOwner, activation);
|
|
1603
|
+
}
|
|
1604
|
+
boundActivationIds = referenceBinding.activations.map((activation) => activation.id);
|
|
1605
|
+
} else {
|
|
1606
|
+
const hostOwner = boundHostIndex.get(resolvedTarget.id);
|
|
1607
|
+
if (hostOwner) {
|
|
1608
|
+
resolution = "capability-only";
|
|
1609
|
+
ownerModule = hostOwner.moduleId;
|
|
1610
|
+
} else {
|
|
1611
|
+
resolution = "capability-only";
|
|
1612
|
+
ownerModule = null;
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
const resolutionTarget = ownerModule ? { ...baseTarget, moduleId: ownerModule } : baseTarget;
|
|
1618
|
+
resolutionsByModule[contributorModuleId].push({
|
|
1619
|
+
contributionId: contribution.id,
|
|
1620
|
+
target: resolutionTarget,
|
|
1621
|
+
resolution,
|
|
1622
|
+
activationIds: [...boundActivationIds].sort((left, right) => left.localeCompare(right))
|
|
1623
|
+
});
|
|
1624
|
+
if (ownerModule && ownerModule !== contributorModuleId && (resolution === "bound" || resolution === "capability-only")) {
|
|
1625
|
+
const source = contributionSourceRef(contribution);
|
|
1626
|
+
const incomingTarget = { ...baseTarget, moduleId: ownerModule };
|
|
1627
|
+
if (resolution === "bound" && boundActivationIds.length > 0) {
|
|
1628
|
+
for (const activationRefId of boundActivationIds) {
|
|
1629
|
+
incomingByModule[ownerModule].push({
|
|
1630
|
+
contributionId: contribution.id,
|
|
1631
|
+
contributionKind: contribution.kind,
|
|
1632
|
+
contributorModuleId,
|
|
1633
|
+
target: incomingTarget,
|
|
1634
|
+
activationId: activationRefId,
|
|
1635
|
+
resolution: "bound",
|
|
1636
|
+
source
|
|
1637
|
+
});
|
|
1638
|
+
}
|
|
1639
|
+
} else {
|
|
1640
|
+
incomingByModule[ownerModule].push({
|
|
1641
|
+
contributionId: contribution.id,
|
|
1642
|
+
contributionKind: contribution.kind,
|
|
1643
|
+
contributorModuleId,
|
|
1644
|
+
target: incomingTarget,
|
|
1645
|
+
resolution,
|
|
1646
|
+
source
|
|
1647
|
+
});
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
const result = {};
|
|
1654
|
+
for (const moduleId of moduleIds) {
|
|
1655
|
+
const surface = options.surfacesByModule[moduleId];
|
|
1656
|
+
result[moduleId] = {
|
|
1657
|
+
...surface,
|
|
1658
|
+
activations: sortActivations([...surface.activations ?? [], ...syntheticActivations[moduleId]]),
|
|
1659
|
+
incoming: dedupeIncoming(incomingByModule[moduleId]),
|
|
1660
|
+
contributionResolutions: sortResolutions(resolutionsByModule[moduleId])
|
|
1168
1661
|
};
|
|
1169
1662
|
}
|
|
1170
1663
|
return result;
|
|
1171
1664
|
}
|
|
1665
|
+
function matchApiInterceptorBridges(contribution, owner) {
|
|
1666
|
+
if (contribution.kind !== "api-interceptor") return [];
|
|
1667
|
+
const bridges = owner.bridges ?? [];
|
|
1668
|
+
const declaredMethods = contribution.details.methods ?? [];
|
|
1669
|
+
const declaredPhases = contribution.phases ?? [];
|
|
1670
|
+
const matched = [];
|
|
1671
|
+
for (const bridge of bridges) {
|
|
1672
|
+
if (declaredMethods.length > 0 && !declaredMethods.includes(bridge.method)) continue;
|
|
1673
|
+
const phases = declaredPhases.length > 0 ? bridge.phases.filter((phase) => declaredPhases.includes(phase)) : bridge.phases;
|
|
1674
|
+
if (phases.length === 0) continue;
|
|
1675
|
+
matched.push({ method: bridge.method, phases });
|
|
1676
|
+
}
|
|
1677
|
+
return matched;
|
|
1678
|
+
}
|
|
1679
|
+
function resolveReferenceBinding(input) {
|
|
1680
|
+
for (const kind of input.activationKinds) {
|
|
1681
|
+
const adapter = ACTIVATION_ADAPTERS[kind];
|
|
1682
|
+
if (adapter.mode === "call-site-object") continue;
|
|
1683
|
+
if (!adapter.contributionKinds.includes(input.contribution.kind)) continue;
|
|
1684
|
+
if (adapter.mode === "host-reference" && kind === "api-interceptor-bridge") {
|
|
1685
|
+
const routeId = normalizeRouteId(input.targetRef.id);
|
|
1686
|
+
const owner = input.apiRouteOwners.get(routeId);
|
|
1687
|
+
if (!owner) continue;
|
|
1688
|
+
const matchedBridges = matchApiInterceptorBridges(input.contribution, owner);
|
|
1689
|
+
if (matchedBridges.length === 0) continue;
|
|
1690
|
+
return {
|
|
1691
|
+
ownerModule: owner.moduleId,
|
|
1692
|
+
activations: matchedBridges.map((bridge) => {
|
|
1693
|
+
const host = {
|
|
1694
|
+
kind: "api-route",
|
|
1695
|
+
id: routeId,
|
|
1696
|
+
moduleId: owner.moduleId,
|
|
1697
|
+
method: bridge.method
|
|
1698
|
+
};
|
|
1699
|
+
return {
|
|
1700
|
+
id: activationId(host, kind),
|
|
1701
|
+
kind,
|
|
1702
|
+
host,
|
|
1703
|
+
contributionKinds: ["api-interceptor"],
|
|
1704
|
+
phases: bridge.phases,
|
|
1705
|
+
source: owner.source,
|
|
1706
|
+
bridge: { factSection: "apiRoutes", factKey: `/${routeId}` }
|
|
1707
|
+
};
|
|
1708
|
+
})
|
|
1709
|
+
};
|
|
1710
|
+
}
|
|
1711
|
+
if (adapter.mode === "owner-reference" && kind === "command-interceptor-bridge") {
|
|
1712
|
+
const owner = input.commandOwners.get(input.targetRef.id);
|
|
1713
|
+
if (!owner) continue;
|
|
1714
|
+
const host = { kind: "command", id: input.targetRef.id, moduleId: owner.moduleId };
|
|
1715
|
+
return {
|
|
1716
|
+
ownerModule: owner.moduleId,
|
|
1717
|
+
activations: [{
|
|
1718
|
+
id: activationId(host, kind),
|
|
1719
|
+
kind,
|
|
1720
|
+
host,
|
|
1721
|
+
contributionKinds: ["command-interceptor"],
|
|
1722
|
+
source: owner.source,
|
|
1723
|
+
bridge: { factSection: "ownedContracts.command", factKey: input.targetRef.id }
|
|
1724
|
+
}]
|
|
1725
|
+
};
|
|
1726
|
+
}
|
|
1727
|
+
if (adapter.mode === "host-reference" && (kind === "widget-injection-consumer" || kind === "component-extension-consumer" || kind === "dashboard-host-consumer")) {
|
|
1728
|
+
const frameworkHostFact = input.boundHostIndex.has(input.targetRef.id) ? null : findFrameworkHost(input.targetRef.id, kind);
|
|
1729
|
+
const hostEntry = input.boundHostIndex.get(input.targetRef.id) ?? (frameworkHostFact ? { moduleId: FRAMEWORK_HOST_OWNER, host: frameworkHostFact } : void 0);
|
|
1730
|
+
if (!hostEntry) continue;
|
|
1731
|
+
const hostSource = hostReferenceSource(hostEntry.host);
|
|
1732
|
+
if (!hostSource) continue;
|
|
1733
|
+
const host = { ...input.targetRef, moduleId: hostEntry.moduleId };
|
|
1734
|
+
return {
|
|
1735
|
+
ownerModule: hostEntry.moduleId,
|
|
1736
|
+
activations: [{
|
|
1737
|
+
id: activationId(host, kind),
|
|
1738
|
+
kind,
|
|
1739
|
+
host,
|
|
1740
|
+
contributionKinds: adapter.contributionKinds,
|
|
1741
|
+
source: hostSource,
|
|
1742
|
+
bridge: { factSection: "hosts", factKey: hostEntry.host.key }
|
|
1743
|
+
}]
|
|
1744
|
+
};
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
return null;
|
|
1748
|
+
}
|
|
1749
|
+
function hostReferenceSource(host) {
|
|
1750
|
+
if (host.source.kind === "declaration" || host.source.kind === "framework") {
|
|
1751
|
+
return { sourcePath: host.source.path, exportName: host.source.symbol };
|
|
1752
|
+
}
|
|
1753
|
+
return null;
|
|
1754
|
+
}
|
|
1755
|
+
function incomingIdentity(entry) {
|
|
1756
|
+
const target2 = JSON.stringify([entry.target.kind, entry.target.id]);
|
|
1757
|
+
return JSON.stringify([entry.contributorModuleId, entry.contributionId, target2, entry.activationId ?? entry.resolution]);
|
|
1758
|
+
}
|
|
1759
|
+
function dedupeIncoming(entries) {
|
|
1760
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1761
|
+
const unique = [];
|
|
1762
|
+
for (const entry of entries) {
|
|
1763
|
+
const identity = incomingIdentity(entry);
|
|
1764
|
+
if (seen.has(identity)) continue;
|
|
1765
|
+
seen.add(identity);
|
|
1766
|
+
unique.push(entry);
|
|
1767
|
+
}
|
|
1768
|
+
return unique.sort((left, right) => incomingIdentity(left).localeCompare(incomingIdentity(right)));
|
|
1769
|
+
}
|
|
1770
|
+
function resolutionIdentity(entry) {
|
|
1771
|
+
return JSON.stringify([entry.contributionId, entry.target.kind, entry.target.id, entry.resolution]);
|
|
1772
|
+
}
|
|
1773
|
+
function sortResolutions(entries) {
|
|
1774
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1775
|
+
const unique = [];
|
|
1776
|
+
for (const entry of entries) {
|
|
1777
|
+
const identity = resolutionIdentity(entry);
|
|
1778
|
+
if (seen.has(identity)) continue;
|
|
1779
|
+
seen.add(identity);
|
|
1780
|
+
unique.push(entry);
|
|
1781
|
+
}
|
|
1782
|
+
return unique.sort((left, right) => resolutionIdentity(left).localeCompare(resolutionIdentity(right)));
|
|
1783
|
+
}
|
|
1172
1784
|
function assertNoUnresolvedExtensionTargets(surfacesByModule) {
|
|
1173
1785
|
const unresolved = Object.entries(surfacesByModule).flatMap(
|
|
1174
1786
|
([moduleId, surface]) => surface.unresolved.filter((entry) => entry.reason === "unresolved-first-party-target").map((entry) => `${moduleId}:${entry.key}`)
|
|
@@ -1177,6 +1789,15 @@ function assertNoUnresolvedExtensionTargets(surfacesByModule) {
|
|
|
1177
1789
|
throw new Error(`[module-facts] unresolved first-party extension targets: ${unresolved.join(", ")}`);
|
|
1178
1790
|
}
|
|
1179
1791
|
}
|
|
1792
|
+
function getFrameworkOverrideModes() {
|
|
1793
|
+
const modes = {};
|
|
1794
|
+
for (const host of FRAMEWORK_OVERRIDE_HOSTS) {
|
|
1795
|
+
const dotted = host.key.replace(/^framework\.module-override\./, "");
|
|
1796
|
+
const mode = host.operations?.[0];
|
|
1797
|
+
if (mode === "disable-replace" || mode === "replace" || mode === "additive") modes[dotted] = mode;
|
|
1798
|
+
}
|
|
1799
|
+
return modes;
|
|
1800
|
+
}
|
|
1180
1801
|
function getFrameworkExtensionHosts() {
|
|
1181
1802
|
return sortHosts(allFrameworkHosts().map((host) => ({
|
|
1182
1803
|
...host,
|
|
@@ -1198,13 +1819,20 @@ function renderFrameworkExtensionPointsMarkdown() {
|
|
|
1198
1819
|
].join("\n");
|
|
1199
1820
|
}
|
|
1200
1821
|
export {
|
|
1822
|
+
ACTIVATION_ADAPTERS,
|
|
1823
|
+
ALL_ACTIVATION_KINDS,
|
|
1824
|
+
ALL_CONTRIBUTION_KINDS,
|
|
1825
|
+
CONTRIBUTION_ACTIVATION_CLASSIFICATION,
|
|
1201
1826
|
assertNoUnresolvedExtensionTargets,
|
|
1202
1827
|
correlateExtensionTarget,
|
|
1828
|
+
correlateIncomingExtensions,
|
|
1203
1829
|
correlateModuleExtensionFacts,
|
|
1830
|
+
extractActivationTargetOwners,
|
|
1204
1831
|
extractKnownApiRouteIds,
|
|
1205
1832
|
extractKnownCommandIds,
|
|
1206
1833
|
extractModuleExtensionFacts,
|
|
1207
1834
|
getFrameworkExtensionHosts,
|
|
1835
|
+
getFrameworkOverrideModes,
|
|
1208
1836
|
readConventionObjectArray,
|
|
1209
1837
|
renderFrameworkExtensionPointsMarkdown,
|
|
1210
1838
|
withModuleExtensionFactExtractionCache
|