@jskit-ai/kernel 0.1.4

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 (185) hide show
  1. package/README.md +24 -0
  2. package/_testable/index.js +4 -0
  3. package/client/appConfig.js +33 -0
  4. package/client/componentInteraction.js +51 -0
  5. package/client/componentInteraction.test.js +111 -0
  6. package/client/descriptorSections.js +75 -0
  7. package/client/index.d.ts +70 -0
  8. package/client/index.js +3 -0
  9. package/client/logging.js +38 -0
  10. package/client/moduleBootstrap.js +670 -0
  11. package/client/moduleBootstrap.test.js +403 -0
  12. package/client/shellBootstrap.js +233 -0
  13. package/client/shellBootstrap.test.js +185 -0
  14. package/client/shellRouting.js +321 -0
  15. package/client/shellRouting.test.js +113 -0
  16. package/client/vite/clientBootstrapPlugin.js +259 -0
  17. package/client/vite/clientBootstrapPlugin.test.js +563 -0
  18. package/client/vite/index.js +3 -0
  19. package/internal/node/fileSystem.js +21 -0
  20. package/internal/node/installedPackageDescriptor.js +104 -0
  21. package/package.json +43 -0
  22. package/server/actions/ActionRuntimeServiceProvider.js +309 -0
  23. package/server/actions/ActionRuntimeServiceProvider.test.js +551 -0
  24. package/server/actions/index.js +8 -0
  25. package/server/container/ContainerCoreServiceProvider.js +27 -0
  26. package/server/container/index.js +10 -0
  27. package/server/exportPolicy.test.js +68 -0
  28. package/server/http/HttpFastifyServiceProvider.js +25 -0
  29. package/server/http/_testable/index.js +2 -0
  30. package/server/http/index.js +1 -0
  31. package/server/http/lib/controller.js +183 -0
  32. package/server/http/lib/controller.test.js +143 -0
  33. package/server/http/lib/errors.js +12 -0
  34. package/server/http/lib/httpRuntime.js +82 -0
  35. package/server/http/lib/index.js +18 -0
  36. package/server/http/lib/kernel.js +15 -0
  37. package/server/http/lib/kernel.test.js +880 -0
  38. package/server/http/lib/middlewareRuntime.js +149 -0
  39. package/server/http/lib/requestActionExecutor.js +258 -0
  40. package/server/http/lib/requestScope.js +59 -0
  41. package/server/http/lib/routeRegistration.js +165 -0
  42. package/server/http/lib/routeSupport.js +45 -0
  43. package/server/http/lib/routeValidator.js +469 -0
  44. package/server/http/lib/routeValidator.test.js +474 -0
  45. package/server/http/lib/router.js +206 -0
  46. package/server/kernel/KernelCoreServiceProvider.js +27 -0
  47. package/server/kernel/index.js +10 -0
  48. package/server/platform/PlatformServerRuntimeServiceProvider.js +30 -0
  49. package/server/platform/index.js +5 -0
  50. package/server/platform/providerRuntime/descriptorCatalog.js +170 -0
  51. package/server/platform/providerRuntime/helpers.js +45 -0
  52. package/server/platform/providerRuntime/lockfile.js +27 -0
  53. package/server/platform/providerRuntime/providerLoader.js +283 -0
  54. package/server/platform/providerRuntime.js +142 -0
  55. package/server/platform/providerRuntime.test.js +217 -0
  56. package/server/platform/runtime.js +40 -0
  57. package/server/platform/surfaceRuntime.js +150 -0
  58. package/server/platform/surfaceRuntime.test.js +136 -0
  59. package/server/registries/actionSurfaceSourceRegistry.js +150 -0
  60. package/server/registries/bootstrapPayloadContributorRegistry.js +41 -0
  61. package/server/registries/domainEventListenerRegistry.js +61 -0
  62. package/server/registries/index.js +36 -0
  63. package/server/registries/primitives.js +63 -0
  64. package/server/registries/routeVisibilityResolverRegistry.js +87 -0
  65. package/server/registries/serviceRegistrationRegistry.js +431 -0
  66. package/server/runtime/ServerRuntimeCoreServiceProvider.js +65 -0
  67. package/server/runtime/ServerRuntimeCoreServiceProvider.test.js +53 -0
  68. package/server/runtime/apiRoutePolicyParity.test.js +109 -0
  69. package/server/runtime/apiRouteRegistration.js +65 -0
  70. package/server/runtime/bootBootstrapRoutes.js +46 -0
  71. package/server/runtime/bootBootstrapRoutes.test.js +79 -0
  72. package/server/runtime/bootstrapContributors.test.js +114 -0
  73. package/server/runtime/canonicalJson.js +74 -0
  74. package/server/runtime/composition.js +142 -0
  75. package/server/runtime/domainEvents.test.js +114 -0
  76. package/server/runtime/domainRules.js +50 -0
  77. package/server/runtime/domainRules.test.js +87 -0
  78. package/server/runtime/entityChangeEvents.js +182 -0
  79. package/server/runtime/entityChangeEvents.test.js +211 -0
  80. package/server/runtime/errors.js +68 -0
  81. package/server/runtime/errors.test.js +73 -0
  82. package/server/runtime/fastifyBootstrap.js +372 -0
  83. package/server/runtime/fastifyBootstrap.test.js +194 -0
  84. package/server/runtime/index.js +6 -0
  85. package/server/runtime/integers.js +13 -0
  86. package/server/runtime/moduleConfig.js +269 -0
  87. package/server/runtime/moduleConfig.test.js +141 -0
  88. package/server/runtime/pagination.js +13 -0
  89. package/server/runtime/realtimeNormalization.js +21 -0
  90. package/server/runtime/requestUrl.js +38 -0
  91. package/server/runtime/routeUtils.js +20 -0
  92. package/server/runtime/runtimeAssembly.js +113 -0
  93. package/server/runtime/runtimeKernel.js +55 -0
  94. package/server/runtime/securityAudit.js +269 -0
  95. package/server/runtime/securityAudit.test.js +41 -0
  96. package/server/runtime/serviceAuthorization.js +113 -0
  97. package/server/runtime/serviceAuthorization.test.js +100 -0
  98. package/server/runtime/serviceRegistration.test.js +197 -0
  99. package/server/support/SupportCoreServiceProvider.js +25 -0
  100. package/server/support/appConfig.js +37 -0
  101. package/server/support/appConfig.test.js +94 -0
  102. package/server/support/defaultMissingHandler.js +7 -0
  103. package/server/support/index.js +2 -0
  104. package/server/support/routePolicyConfig.js +51 -0
  105. package/server/support/symlinkSafeRequire.js +78 -0
  106. package/server/support/symlinkSafeRequire.test.js +27 -0
  107. package/server/surface/SurfaceRoutingServiceProvider.js +27 -0
  108. package/server/surface/index.js +19 -0
  109. package/shared/actions/actionContributorHelpers.js +34 -0
  110. package/shared/actions/actionContributorHelpers.test.js +16 -0
  111. package/shared/actions/actionDefinitions.js +488 -0
  112. package/shared/actions/actionDefinitions.test.js +212 -0
  113. package/shared/actions/audit.js +7 -0
  114. package/shared/actions/executionContext.js +97 -0
  115. package/shared/actions/executionContext.test.js +66 -0
  116. package/shared/actions/idempotency.js +62 -0
  117. package/shared/actions/index.js +2 -0
  118. package/shared/actions/observability.js +10 -0
  119. package/shared/actions/pipeline.js +287 -0
  120. package/shared/actions/policies.js +342 -0
  121. package/shared/actions/policies.test.js +233 -0
  122. package/shared/actions/registry.js +187 -0
  123. package/shared/actions/registry.test.js +381 -0
  124. package/shared/actions/requestMeta.js +36 -0
  125. package/shared/actions/textNormalization.js +3 -0
  126. package/shared/actions/withActionDefaults.js +34 -0
  127. package/shared/index.js +2 -0
  128. package/shared/runtime/application.js +323 -0
  129. package/shared/runtime/container.js +261 -0
  130. package/shared/runtime/containerErrors.js +22 -0
  131. package/shared/runtime/index.js +18 -0
  132. package/shared/runtime/kernelErrors.js +20 -0
  133. package/shared/runtime/serviceProvider.js +13 -0
  134. package/shared/support/formatDateTime.js +10 -0
  135. package/shared/support/formatDateTime.test.js +15 -0
  136. package/shared/support/index.js +14 -0
  137. package/shared/support/linkPath.js +67 -0
  138. package/shared/support/linkPath.test.js +35 -0
  139. package/shared/support/normalize.js +116 -0
  140. package/shared/support/normalize.test.js +48 -0
  141. package/shared/support/packageDescriptor.test.js +121 -0
  142. package/shared/support/permissions.js +50 -0
  143. package/shared/support/pickOwnProperties.js +17 -0
  144. package/shared/support/pickOwnProperties.test.js +25 -0
  145. package/shared/support/policies.js +11 -0
  146. package/shared/support/queryPath.js +33 -0
  147. package/shared/support/queryPath.test.js +19 -0
  148. package/shared/support/queryResilience.js +34 -0
  149. package/shared/support/queryResilience.test.js +33 -0
  150. package/shared/support/returnToPath.js +153 -0
  151. package/shared/support/returnToPath.test.js +123 -0
  152. package/shared/support/sorting.js +15 -0
  153. package/shared/support/tokens.js +23 -0
  154. package/shared/support/tokens.test.js +17 -0
  155. package/shared/support/visibility.js +56 -0
  156. package/shared/support/visibility.test.js +45 -0
  157. package/shared/surface/apiPaths.js +84 -0
  158. package/shared/surface/escapeRegExp.js +5 -0
  159. package/shared/surface/index.js +6 -0
  160. package/shared/surface/paths.js +273 -0
  161. package/shared/surface/registry.js +135 -0
  162. package/shared/surface/registry.test.js +44 -0
  163. package/shared/surface/runtime.js +357 -0
  164. package/shared/surface/runtime.test.js +319 -0
  165. package/shared/validators/createCursorListValidator.js +42 -0
  166. package/shared/validators/createCursorListValidator.test.js +34 -0
  167. package/shared/validators/cursorPaginationQueryValidator.js +31 -0
  168. package/shared/validators/cursorPaginationQueryValidator.test.js +21 -0
  169. package/shared/validators/index.js +12 -0
  170. package/shared/validators/inputNormalization.js +13 -0
  171. package/shared/validators/mergeObjectSchemas.js +31 -0
  172. package/shared/validators/mergeObjectSchemas.test.js +67 -0
  173. package/shared/validators/mergeValidators.js +89 -0
  174. package/shared/validators/mergeValidators.test.js +116 -0
  175. package/shared/validators/nestValidator.js +53 -0
  176. package/shared/validators/nestValidator.test.js +60 -0
  177. package/shared/validators/recordIdParamsValidator.js +36 -0
  178. package/shared/validators/recordIdParamsValidator.test.js +20 -0
  179. package/shared/validators/resourceRequiredMetadata.js +41 -0
  180. package/shared/validators/resourceRequiredMetadata.test.js +49 -0
  181. package/test/barrelExposure.test.js +106 -0
  182. package/test/dynamicImportPolicy.test.js +89 -0
  183. package/test/exportsContract.test.js +168 -0
  184. package/test/routeInputContractGuard.test.js +78 -0
  185. package/test/surfaceIndependence.test.js +109 -0
@@ -0,0 +1,150 @@
1
+ import { KERNEL_TOKENS } from "../../shared/support/tokens.js";
2
+ import { assertTaggableApp } from "./primitives.js";
3
+
4
+ const ACTION_SURFACE_SOURCE_REGISTRY_TOKEN = Symbol.for("jskit.runtime.actions.surfaceSourceRegistry");
5
+ const ACTION_SURFACE_SOURCE_NAME_PATTERN = /^[a-z][a-z0-9_.-]*$/;
6
+
7
+ function normalizeSurfaceIdValue(value) {
8
+ return String(value || "").trim().toLowerCase();
9
+ }
10
+
11
+ function normalizeSurfaceSourceName(sourceName, { context = "action surface source" } = {}) {
12
+ const normalizedSourceName = normalizeSurfaceIdValue(sourceName);
13
+ if (!ACTION_SURFACE_SOURCE_NAME_PATTERN.test(normalizedSourceName)) {
14
+ throw new Error(`${context} must match ${ACTION_SURFACE_SOURCE_NAME_PATTERN.toString()}.`);
15
+ }
16
+
17
+ return normalizedSourceName;
18
+ }
19
+
20
+ function resolveSurfaceRuntime(scope) {
21
+ if (!scope || typeof scope.has !== "function" || typeof scope.make !== "function") {
22
+ throw new Error("Action definition materialization requires scope.has()/make().");
23
+ }
24
+
25
+ if (!scope.has(KERNEL_TOKENS.SurfaceRuntime)) {
26
+ throw new Error("Action definition surfacesFrom requires KERNEL_TOKENS.SurfaceRuntime.");
27
+ }
28
+
29
+ return scope.make(KERNEL_TOKENS.SurfaceRuntime);
30
+ }
31
+
32
+ function resolveEnabledSurfaceIds(surfaceRuntime) {
33
+ return Array.isArray(surfaceRuntime?.listEnabledSurfaceIds?.()) ? surfaceRuntime.listEnabledSurfaceIds() : [];
34
+ }
35
+
36
+ function normalizeSurfaceIdList(surfaceIds, { context = "action.surfacesFrom", surfaceRuntime = null } = {}) {
37
+ const sourceSurfaceIds = Array.isArray(surfaceIds) ? surfaceIds : [];
38
+ const enabledSurfaceIds = new Set(
39
+ resolveEnabledSurfaceIds(surfaceRuntime).map((entry) => normalizeSurfaceIdValue(entry)).filter(Boolean)
40
+ );
41
+ const seen = new Set();
42
+ const normalized = [];
43
+
44
+ for (const sourceSurfaceId of sourceSurfaceIds) {
45
+ const normalizedSurfaceId = normalizeSurfaceIdValue(sourceSurfaceId);
46
+ if (!normalizedSurfaceId || seen.has(normalizedSurfaceId)) {
47
+ continue;
48
+ }
49
+
50
+ if (!enabledSurfaceIds.has(normalizedSurfaceId)) {
51
+ throw new Error(`${context} resolved non-enabled surface "${normalizedSurfaceId}".`);
52
+ }
53
+
54
+ seen.add(normalizedSurfaceId);
55
+ normalized.push(normalizedSurfaceId);
56
+ }
57
+
58
+ return Object.freeze(normalized);
59
+ }
60
+
61
+ function createActionSurfaceSourceRegistry() {
62
+ const sourceResolvers = new Map();
63
+
64
+ const register = (sourceName, resolver, { allowOverride = false } = {}) => {
65
+ const normalizedSourceName = normalizeSurfaceSourceName(sourceName);
66
+ if (typeof resolver !== "function") {
67
+ throw new Error(`action surface source "${normalizedSourceName}" resolver must be a function.`);
68
+ }
69
+ if (!allowOverride && sourceResolvers.has(normalizedSourceName)) {
70
+ throw new Error(`action surface source "${normalizedSourceName}" is already registered.`);
71
+ }
72
+ sourceResolvers.set(normalizedSourceName, resolver);
73
+ };
74
+
75
+ register("enabled", ({ surfaceRuntime }) => resolveEnabledSurfaceIds(surfaceRuntime));
76
+
77
+ return Object.freeze({
78
+ register,
79
+ resolve(scope, sourceName, { context = "action.surfacesFrom" } = {}) {
80
+ const normalizedSourceName = normalizeSurfaceSourceName(sourceName, { context });
81
+ const resolver = sourceResolvers.get(normalizedSourceName);
82
+ if (typeof resolver !== "function") {
83
+ throw new Error(
84
+ `${context} references unknown surface source "${normalizedSourceName}". Register it via app.actionSurfaceSource().`
85
+ );
86
+ }
87
+
88
+ const surfaceRuntime = resolveSurfaceRuntime(scope);
89
+ const resolvedSurfaceIds = resolver({
90
+ scope,
91
+ sourceName: normalizedSourceName,
92
+ surfaceRuntime
93
+ });
94
+
95
+ return normalizeSurfaceIdList(resolvedSurfaceIds, {
96
+ context,
97
+ surfaceRuntime
98
+ });
99
+ }
100
+ });
101
+ }
102
+
103
+ function resolveActionSurfaceSourceRegistry(scope) {
104
+ if (!scope || typeof scope.has !== "function" || typeof scope.make !== "function") {
105
+ throw new Error("Action surface source resolution requires scope.has()/make().");
106
+ }
107
+ if (!scope.has(ACTION_SURFACE_SOURCE_REGISTRY_TOKEN)) {
108
+ throw new Error("Action surface source registry is not registered.");
109
+ }
110
+ return scope.make(ACTION_SURFACE_SOURCE_REGISTRY_TOKEN);
111
+ }
112
+
113
+ function resolveActionSurfaceSourceIds(scope, sourceName, { context = "action.surfacesFrom" } = {}) {
114
+ const sourceRegistry = resolveActionSurfaceSourceRegistry(scope);
115
+ return sourceRegistry.resolve(scope, sourceName, { context });
116
+ }
117
+
118
+ function installActionSurfaceSourceRegistrationApi(app) {
119
+ if (typeof app.actionSurfaceSource === "function") {
120
+ return;
121
+ }
122
+
123
+ const registerActionSurfaceSource = function registerActionSurfaceSource(sourceName, resolver) {
124
+ const sourceRegistry = resolveActionSurfaceSourceRegistry(this);
125
+ sourceRegistry.register(sourceName, resolver);
126
+ return this;
127
+ };
128
+
129
+ Object.defineProperty(app, "actionSurfaceSource", {
130
+ configurable: true,
131
+ writable: true,
132
+ value: registerActionSurfaceSource
133
+ });
134
+ }
135
+
136
+ function ensureActionSurfaceSourceRegistry(app) {
137
+ assertTaggableApp(app, {
138
+ context: "ensureActionSurfaceSourceRegistry"
139
+ });
140
+ if (typeof app.has !== "function") {
141
+ throw new Error("ensureActionSurfaceSourceRegistry requires app.has().");
142
+ }
143
+
144
+ if (!app.has(ACTION_SURFACE_SOURCE_REGISTRY_TOKEN)) {
145
+ app.singleton(ACTION_SURFACE_SOURCE_REGISTRY_TOKEN, () => createActionSurfaceSourceRegistry());
146
+ }
147
+ installActionSurfaceSourceRegistrationApi(app);
148
+ }
149
+
150
+ export { ensureActionSurfaceSourceRegistry, resolveActionSurfaceSourceIds };
@@ -0,0 +1,41 @@
1
+ import { normalizeObject } from "../../shared/support/normalize.js";
2
+ import { normalizeContributorEntry, registerTaggedSingleton, resolveTaggedEntries } from "./primitives.js";
3
+
4
+ const BOOTSTRAP_PAYLOAD_CONTRIBUTOR_TAG = Symbol.for("jskit.runtime.bootstrap.payloadContributors");
5
+
6
+ function registerBootstrapPayloadContributor(app, token, factory) {
7
+ registerTaggedSingleton(app, token, factory, BOOTSTRAP_PAYLOAD_CONTRIBUTOR_TAG, {
8
+ context: "registerBootstrapPayloadContributor"
9
+ });
10
+ }
11
+
12
+ function resolveBootstrapPayloadContributors(scope) {
13
+ return resolveTaggedEntries(scope, BOOTSTRAP_PAYLOAD_CONTRIBUTOR_TAG)
14
+ .map((entry) => normalizeContributorEntry(entry))
15
+ .filter(Boolean);
16
+ }
17
+
18
+ async function resolveBootstrapPayload(scope, context = {}) {
19
+ const contributors = resolveBootstrapPayloadContributors(scope);
20
+ let payload = {};
21
+
22
+ for (const contributor of contributors) {
23
+ const contribution = await contributor.contribute({
24
+ ...normalizeObject(context),
25
+ payload: Object.freeze({ ...payload })
26
+ });
27
+ payload = {
28
+ ...payload,
29
+ ...normalizeObject(contribution)
30
+ };
31
+ }
32
+
33
+ return payload;
34
+ }
35
+
36
+ export {
37
+ BOOTSTRAP_PAYLOAD_CONTRIBUTOR_TAG,
38
+ registerBootstrapPayloadContributor,
39
+ resolveBootstrapPayloadContributors,
40
+ resolveBootstrapPayload
41
+ };
@@ -0,0 +1,61 @@
1
+ import { normalizeObject } from "../../shared/support/normalize.js";
2
+ import { registerTaggedSingleton, resolveTaggedEntries } from "./primitives.js";
3
+
4
+ const DOMAIN_EVENT_LISTENER_TAG = Symbol.for("jskit.runtime.domainEvent.listeners");
5
+
6
+ function normalizeDomainEventListener(entry) {
7
+ if (typeof entry === "function") {
8
+ return {
9
+ listenerId: String(entry.name || "anonymous"),
10
+ matches: null,
11
+ handle: entry
12
+ };
13
+ }
14
+
15
+ if (entry && typeof entry === "object" && typeof entry.handle === "function") {
16
+ return {
17
+ ...entry,
18
+ listenerId: String(entry.listenerId || "anonymous"),
19
+ matches: typeof entry.matches === "function" ? entry.matches : null
20
+ };
21
+ }
22
+
23
+ return null;
24
+ }
25
+
26
+ function registerDomainEventListener(app, token, factory) {
27
+ registerTaggedSingleton(app, token, factory, DOMAIN_EVENT_LISTENER_TAG, {
28
+ context: "registerDomainEventListener"
29
+ });
30
+ }
31
+
32
+ function resolveDomainEventListeners(scope) {
33
+ return resolveTaggedEntries(scope, DOMAIN_EVENT_LISTENER_TAG)
34
+ .map((entry) => normalizeDomainEventListener(entry))
35
+ .filter(Boolean);
36
+ }
37
+
38
+ function createDomainEvents(scope) {
39
+ return Object.freeze({
40
+ async publish(event = {}) {
41
+ const payload = normalizeObject(event);
42
+ const listeners = resolveDomainEventListeners(scope);
43
+
44
+ for (const listener of listeners) {
45
+ if (listener.matches && listener.matches(payload) !== true) {
46
+ continue;
47
+ }
48
+ await listener.handle(payload);
49
+ }
50
+
51
+ return null;
52
+ }
53
+ });
54
+ }
55
+
56
+ export {
57
+ DOMAIN_EVENT_LISTENER_TAG,
58
+ registerDomainEventListener,
59
+ resolveDomainEventListeners,
60
+ createDomainEvents
61
+ };
@@ -0,0 +1,36 @@
1
+ export {
2
+ BOOTSTRAP_PAYLOAD_CONTRIBUTOR_TAG,
3
+ registerBootstrapPayloadContributor,
4
+ resolveBootstrapPayloadContributors,
5
+ resolveBootstrapPayload
6
+ } from "./bootstrapPayloadContributorRegistry.js";
7
+ export {
8
+ DOMAIN_EVENT_LISTENER_TAG,
9
+ registerDomainEventListener,
10
+ resolveDomainEventListeners,
11
+ createDomainEvents
12
+ } from "./domainEventListenerRegistry.js";
13
+ export {
14
+ SERVICE_REGISTRATION_TAG,
15
+ normalizeServiceRegistration,
16
+ materializeServiceRegistration,
17
+ registerServiceRegistration,
18
+ resolveServiceRegistrations,
19
+ installServiceRegistrationApi
20
+ } from "./serviceRegistrationRegistry.js";
21
+ export {
22
+ ROUTE_VISIBILITY_RESOLVER_TAG,
23
+ resolveRouteVisibilityResolvers,
24
+ registerRouteVisibilityResolver,
25
+ resolveRouteVisibilityContext
26
+ } from "./routeVisibilityResolverRegistry.js";
27
+ export {
28
+ ensureActionSurfaceSourceRegistry,
29
+ resolveActionSurfaceSourceIds
30
+ } from "./actionSurfaceSourceRegistry.js";
31
+ export {
32
+ normalizeNestedEntries,
33
+ assertTaggableApp,
34
+ registerTaggedSingleton,
35
+ resolveTaggedEntries
36
+ } from "./primitives.js";
@@ -0,0 +1,63 @@
1
+ function normalizeNestedEntries(value) {
2
+ const queue = Array.isArray(value) ? [...value] : [value];
3
+ const entries = [];
4
+
5
+ while (queue.length > 0) {
6
+ const entry = queue.shift();
7
+ if (Array.isArray(entry)) {
8
+ queue.push(...entry);
9
+ continue;
10
+ }
11
+ if (entry == null) {
12
+ continue;
13
+ }
14
+ entries.push(entry);
15
+ }
16
+
17
+ return entries;
18
+ }
19
+
20
+ function assertTaggableApp(app, { context = "registry", ErrorType = Error } = {}) {
21
+ if (!app || typeof app.singleton !== "function" || typeof app.tag !== "function") {
22
+ throw new ErrorType(`${context} requires application singleton()/tag().`);
23
+ }
24
+ }
25
+
26
+ function registerTaggedSingleton(app, token, factory, tag, { context = "registry", ErrorType = Error } = {}) {
27
+ assertTaggableApp(app, { context, ErrorType });
28
+ app.singleton(token, factory);
29
+ app.tag(token, tag);
30
+ }
31
+
32
+ function resolveTaggedEntries(scope, tag) {
33
+ if (!scope || typeof scope.resolveTag !== "function") {
34
+ return [];
35
+ }
36
+ return normalizeNestedEntries(scope.resolveTag(tag));
37
+ }
38
+
39
+ function normalizeContributorEntry(entry) {
40
+ if (typeof entry === "function") {
41
+ return {
42
+ contributorId: String(entry.name || "anonymous"),
43
+ contribute: entry
44
+ };
45
+ }
46
+
47
+ if (entry && typeof entry === "object" && typeof entry.contribute === "function") {
48
+ return {
49
+ ...entry,
50
+ contributorId: String(entry.contributorId || "anonymous")
51
+ };
52
+ }
53
+
54
+ return null;
55
+ }
56
+
57
+ export {
58
+ normalizeNestedEntries,
59
+ assertTaggableApp,
60
+ registerTaggedSingleton,
61
+ resolveTaggedEntries,
62
+ normalizeContributorEntry
63
+ };
@@ -0,0 +1,87 @@
1
+ import { normalizeRouteVisibility, normalizeRouteVisibilityToken, normalizeVisibilityContext } from "../../shared/support/visibility.js";
2
+ import { normalizeText } from "../../shared/support/normalize.js";
3
+ import { ROUTE_VISIBILITY_PUBLIC } from "../../shared/support/policies.js";
4
+ import { RouteRegistrationError } from "../http/lib/errors.js";
5
+ import { registerTaggedSingleton, resolveTaggedEntries } from "./primitives.js";
6
+
7
+ const ROUTE_VISIBILITY_RESOLVER_TAG = Symbol.for("jskit.runtime.http.visibilityResolvers");
8
+
9
+ function normalizeRouteVisibilityResolver(entry) {
10
+ if (typeof entry === "function") {
11
+ return {
12
+ resolverId: normalizeText(entry.name) || "anonymous",
13
+ resolve: entry
14
+ };
15
+ }
16
+
17
+ if (!entry || typeof entry !== "object" || Array.isArray(entry) || typeof entry.resolve !== "function") {
18
+ return null;
19
+ }
20
+
21
+ return {
22
+ resolverId: normalizeText(entry.resolverId) || "anonymous",
23
+ resolve: entry.resolve
24
+ };
25
+ }
26
+
27
+ function resolveRouteVisibilityResolvers(scope) {
28
+ return resolveTaggedEntries(scope, ROUTE_VISIBILITY_RESOLVER_TAG)
29
+ .map((entry) => normalizeRouteVisibilityResolver(entry))
30
+ .filter(Boolean);
31
+ }
32
+
33
+ function registerRouteVisibilityResolver(app, token, factory) {
34
+ registerTaggedSingleton(app, token, factory, ROUTE_VISIBILITY_RESOLVER_TAG, {
35
+ context: "registerRouteVisibilityResolver",
36
+ ErrorType: RouteRegistrationError
37
+ });
38
+ }
39
+
40
+ async function resolveRouteVisibilityContext({
41
+ resolutionScope = null,
42
+ request = null,
43
+ routeVisibility = ROUTE_VISIBILITY_PUBLIC,
44
+ context = {},
45
+ input = {},
46
+ deps = {},
47
+ actionId = "",
48
+ version = null,
49
+ channel = "api"
50
+ } = {}) {
51
+ const normalizedRouteVisibility = normalizeRouteVisibilityToken(routeVisibility);
52
+ const normalizedCoreVisibility = normalizeRouteVisibility(normalizedRouteVisibility);
53
+ const resolvers = resolveRouteVisibilityResolvers(resolutionScope);
54
+ const patch = {};
55
+
56
+ for (const resolver of resolvers) {
57
+ const contribution = await resolver.resolve({
58
+ request,
59
+ visibility: normalizedRouteVisibility,
60
+ context,
61
+ input,
62
+ deps,
63
+ actionId: normalizeText(actionId),
64
+ version: version == null ? null : version,
65
+ channel: normalizeText(channel).toLowerCase() || "api"
66
+ });
67
+
68
+ if (!contribution || typeof contribution !== "object" || Array.isArray(contribution)) {
69
+ continue;
70
+ }
71
+
72
+ Object.assign(patch, contribution);
73
+ }
74
+
75
+ return normalizeVisibilityContext({
76
+ ...patch,
77
+ visibility: normalizedRouteVisibility,
78
+ requiresActorScope: patch.requiresActorScope === true || normalizedCoreVisibility === "user"
79
+ });
80
+ }
81
+
82
+ export {
83
+ ROUTE_VISIBILITY_RESOLVER_TAG,
84
+ resolveRouteVisibilityResolvers,
85
+ registerRouteVisibilityResolver,
86
+ resolveRouteVisibilityContext
87
+ };