@jskit-ai/kernel 0.1.159 → 0.1.161

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 (102) hide show
  1. package/_testable/index.js +1 -3
  2. package/client/componentRegistry.js +44 -0
  3. package/client/componentRegistry.test.js +16 -0
  4. package/client/moduleBootstrap.js +54 -97
  5. package/client/moduleBootstrap.test.js +47 -32
  6. package/client/vite/clientBootstrapPlugin.test.js +2 -2
  7. package/internal/node/installedPackages.js +2 -7
  8. package/internal/node/installedPackages.test.js +26 -5
  9. package/package.json +3 -5
  10. package/server/actions/actionCatalogue.js +162 -0
  11. package/server/actions/actionEvents.js +66 -0
  12. package/server/actions/actionProvider.js +23 -0
  13. package/server/actions/index.js +3 -6
  14. package/server/features/defineFeature.js +131 -0
  15. package/server/features/defineFeature.test.js +238 -0
  16. package/server/features/index.js +1 -0
  17. package/server/http/HttpProvider.js +23 -0
  18. package/server/http/_testable/index.js +0 -1
  19. package/server/http/capabilityHttpRuntime.js +123 -0
  20. package/server/http/capabilityHttpRuntime.test.js +161 -0
  21. package/server/http/directRequestActionExecutor.js +78 -0
  22. package/server/http/index.js +2 -1
  23. package/server/http/lib/index.js +1 -13
  24. package/server/http/lib/routeRegistration.js +9 -25
  25. package/server/platform/index.js +2 -2
  26. package/server/platform/installedRuntime.js +122 -0
  27. package/server/platform/installedRuntime.test.js +103 -0
  28. package/server/platform/providerRuntime/capabilityProviderLoader.js +100 -0
  29. package/server/platform/providerRuntime/capabilityProviderLoader.test.js +80 -0
  30. package/server/platform/providerRuntime/packageCatalog.js +4 -1
  31. package/server/platform/surfaceRuntime.js +1 -15
  32. package/server/runtime/BootstrapProvider.js +69 -0
  33. package/server/runtime/EventProvider.js +74 -0
  34. package/server/runtime/EventProvider.test.js +32 -0
  35. package/server/runtime/entityChangeEvents.js +7 -7
  36. package/server/runtime/entityChangeEvents.test.js +2 -2
  37. package/server/runtime/fastifyBootstrap.js +0 -40
  38. package/server/runtime/index.js +2 -3
  39. package/server/surface/index.js +0 -1
  40. package/shared/actions/actionDefinitions.js +20 -0
  41. package/shared/actions/pipeline.js +22 -0
  42. package/shared/actions/policies.js +1 -1
  43. package/shared/actions/registry.js +3 -0
  44. package/shared/capabilities/defineProvider.js +99 -0
  45. package/shared/capabilities/index.js +2 -0
  46. package/shared/capabilities/runtime.js +243 -0
  47. package/shared/capabilities/runtime.test.js +170 -0
  48. package/test/barrelExposure.test.js +36 -13
  49. package/test/exportsContract.test.js +8 -3
  50. package/server/actions/ActionRuntimeServiceProvider.js +0 -304
  51. package/server/actions/ActionRuntimeServiceProvider.test.js +0 -558
  52. package/server/container/ContainerCoreServiceProvider.js +0 -27
  53. package/server/container/index.js +0 -10
  54. package/server/http/HttpFastifyServiceProvider.js +0 -25
  55. package/server/http/lib/httpRuntime.js +0 -83
  56. package/server/http/lib/kernel.js +0 -15
  57. package/server/http/lib/kernel.test.js +0 -1565
  58. package/server/http/lib/requestActionExecutor.js +0 -265
  59. package/server/http/lib/requestScope.js +0 -57
  60. package/server/kernel/KernelCoreServiceProvider.js +0 -27
  61. package/server/kernel/index.js +0 -10
  62. package/server/platform/PlatformServerRuntimeServiceProvider.js +0 -30
  63. package/server/platform/providerRuntime/providerLoader.js +0 -283
  64. package/server/platform/providerRuntime.js +0 -146
  65. package/server/platform/providerRuntime.test.js +0 -214
  66. package/server/platform/runtime.js +0 -40
  67. package/server/registries/actionSurfaceSourceRegistry.js +0 -148
  68. package/server/registries/bootstrapPayloadContributorRegistry.js +0 -51
  69. package/server/registries/domainEventListenerRegistry.js +0 -58
  70. package/server/registries/index.js +0 -32
  71. package/server/registries/primitives.js +0 -63
  72. package/server/registries/routeVisibilityResolverRegistry.js +0 -84
  73. package/server/registries/serviceRegistrationRegistry.js +0 -488
  74. package/server/runtime/ServerRuntimeCoreServiceProvider.js +0 -64
  75. package/server/runtime/ServerRuntimeCoreServiceProvider.test.js +0 -53
  76. package/server/runtime/apiRoutePolicyParity.test.js +0 -109
  77. package/server/runtime/apiRouteRegistration.js +0 -65
  78. package/server/runtime/bootBootstrapRoutes.js +0 -29
  79. package/server/runtime/bootBootstrapRoutes.test.js +0 -69
  80. package/server/runtime/bootstrapContributors.test.js +0 -166
  81. package/server/runtime/composition.js +0 -142
  82. package/server/runtime/domainEvents.test.js +0 -114
  83. package/server/runtime/domainRules.js +0 -50
  84. package/server/runtime/domainRules.test.js +0 -87
  85. package/server/runtime/runtimeAssembly.js +0 -113
  86. package/server/runtime/runtimeKernel.js +0 -55
  87. package/server/runtime/serviceRegistration.test.js +0 -251
  88. package/server/support/SupportCoreServiceProvider.js +0 -27
  89. package/server/surface/SurfaceRoutingServiceProvider.js +0 -27
  90. package/shared/actions/index.js +0 -2
  91. package/shared/runtime/application.js +0 -324
  92. package/shared/runtime/container.js +0 -261
  93. package/shared/runtime/containerErrors.js +0 -22
  94. package/shared/runtime/index.js +0 -18
  95. package/shared/runtime/kernelErrors.js +0 -25
  96. package/shared/runtime/kernelErrors.test.js +0 -120
  97. package/shared/runtime/serviceProvider.js +0 -13
  98. package/shared/support/containerToken.js +0 -8
  99. package/shared/support/generatedUiContract.js +0 -542
  100. package/shared/support/generatedUiContract.test.js +0 -218
  101. package/shared/support/tokens.js +0 -1
  102. package/shared/support/tokens.test.js +0 -17
@@ -1,265 +0,0 @@
1
- import { normalizeObject, normalizeText } from "../../../shared/support/normalize.js";
2
- import { normalizeRouteVisibilityToken } from "../../../shared/support/visibility.js";
3
- import { resolveActionContextContributors } from "../../actions/ActionRuntimeServiceProvider.js";
4
- import { resolveRouteVisibilityContext } from "../../registries/routeVisibilityResolverRegistry.js";
5
- import { resolveDefaultSurfaceId } from "../../support/appConfig.js";
6
- import { RouteRegistrationError } from "./errors.js";
7
- import { normalizeRequestScopeProperty } from "./requestScope.js";
8
-
9
- function normalizeRequestActionExecutorProperty(value) {
10
- const normalized = String(value || "").trim();
11
- return normalized || "executeAction";
12
- }
13
-
14
- function resolveSurfaceFromRequest(request, explicitSurface = "", defaultSurfaceId = "") {
15
- const normalizedExplicitSurface = normalizeText(explicitSurface).toLowerCase();
16
- if (normalizedExplicitSurface) {
17
- return normalizedExplicitSurface;
18
- }
19
-
20
- const normalizedRouteSurface = normalizeText(request?.routeOptions?.config?.surface).toLowerCase();
21
- if (normalizedRouteSurface) {
22
- return normalizedRouteSurface;
23
- }
24
-
25
- const normalizedRequestSurface = normalizeText(request?.surface).toLowerCase();
26
- if (normalizedRequestSurface) {
27
- return normalizedRequestSurface;
28
- }
29
-
30
- return resolveDefaultSurfaceId(null, {
31
- defaultSurfaceId
32
- });
33
- }
34
-
35
- function buildActionExecutionContext({ request = null, context = {}, channel = "api", defaultSurfaceId = "" } = {}) {
36
- const source = normalizeObject(context);
37
- const sourceRequestMeta = normalizeObject(source.requestMeta);
38
-
39
- return {
40
- ...source,
41
- channel: normalizeText(source.channel || channel).toLowerCase() || "api",
42
- surface: resolveSurfaceFromRequest(request, source.surface, defaultSurfaceId),
43
- requestMeta: {
44
- ...sourceRequestMeta,
45
- request
46
- }
47
- };
48
- }
49
-
50
- function applyActionContextContributionDefaults(targetContext, contribution) {
51
- const patch = normalizeObject(contribution);
52
- if (Object.keys(patch).length < 1) {
53
- return targetContext;
54
- }
55
-
56
- for (const [key, value] of Object.entries(patch)) {
57
- if (key === "requestMeta") {
58
- continue;
59
- }
60
- if (Object.prototype.hasOwnProperty.call(targetContext, key)) {
61
- continue;
62
- }
63
- targetContext[key] = value;
64
- }
65
-
66
- if (Object.prototype.hasOwnProperty.call(patch, "requestMeta")) {
67
- const targetRequestMeta = normalizeObject(targetContext.requestMeta);
68
- const patchRequestMeta = normalizeObject(patch.requestMeta);
69
- for (const [key, value] of Object.entries(patchRequestMeta)) {
70
- if (Object.prototype.hasOwnProperty.call(targetRequestMeta, key)) {
71
- continue;
72
- }
73
- targetRequestMeta[key] = value;
74
- }
75
- targetContext.requestMeta = targetRequestMeta;
76
- }
77
-
78
- return targetContext;
79
- }
80
-
81
- async function enrichActionExecutionContext({
82
- resolutionScope = null,
83
- request = null,
84
- actionId = "",
85
- version = null,
86
- definition = null,
87
- input = {},
88
- deps = {},
89
- channel = "api",
90
- baseContext = {}
91
- } = {}) {
92
- const contributors = resolveActionContextContributors(resolutionScope);
93
- if (contributors.length < 1) {
94
- return baseContext;
95
- }
96
-
97
- const normalizedActionId = normalizeText(actionId);
98
- const normalizedChannel = normalizeText(channel).toLowerCase() || "api";
99
- const normalizedInput = normalizeObject(input);
100
- const normalizedDeps = normalizeObject(deps);
101
- const mutableContext = normalizeObject(baseContext);
102
-
103
- for (const contributor of contributors) {
104
- if (!contributor || typeof contributor.contribute !== "function") {
105
- continue;
106
- }
107
-
108
- const contribution = await contributor.contribute({
109
- request,
110
- actionId: normalizedActionId,
111
- version: version == null ? null : version,
112
- definition,
113
- input: normalizedInput,
114
- deps: normalizedDeps,
115
- channel: normalizedChannel,
116
- surface: mutableContext.surface,
117
- context: { ...mutableContext }
118
- });
119
-
120
- applyActionContextContributionDefaults(mutableContext, contribution);
121
- }
122
-
123
- return mutableContext;
124
- }
125
-
126
- function resolveActionExecutorScope({ app = null, request = null, requestScopeProperty = "scope" } = {}) {
127
- const normalizedScopeProperty = normalizeRequestScopeProperty(requestScopeProperty);
128
- const requestScope =
129
- request && typeof request === "object" && request[normalizedScopeProperty] && typeof request[normalizedScopeProperty] === "object"
130
- ? request[normalizedScopeProperty]
131
- : null;
132
-
133
- if (requestScope && typeof requestScope.make === "function") {
134
- return requestScope;
135
- }
136
-
137
- if (app && typeof app.make === "function") {
138
- return app;
139
- }
140
-
141
- return null;
142
- }
143
-
144
- function resolveRouteVisibilityFromRequestAndPayload(request, payload = {}) {
145
- const routeConfig =
146
- request?.routeOptions?.config && typeof request.routeOptions.config === "object" ? request.routeOptions.config : null;
147
- if (routeConfig && Object.prototype.hasOwnProperty.call(routeConfig, "visibility")) {
148
- return normalizeRouteVisibilityToken(routeConfig.visibility);
149
- }
150
-
151
- return normalizeRouteVisibilityToken(payload.visibility);
152
- }
153
-
154
- function attachRequestActionExecutor({
155
- app = null,
156
- request = null,
157
- requestScopeProperty = "scope",
158
- requestActionExecutorProperty = "executeAction",
159
- actionExecutorToken = "actionExecutor",
160
- defaultChannel = "api",
161
- defaultSurfaceId = ""
162
- } = {}) {
163
- if (!request || typeof request !== "object") {
164
- return null;
165
- }
166
-
167
- const normalizedProperty = normalizeRequestActionExecutorProperty(requestActionExecutorProperty);
168
- if (typeof request[normalizedProperty] === "function") {
169
- return request[normalizedProperty];
170
- }
171
-
172
- const normalizedActionExecutorToken = normalizeText(actionExecutorToken) || "actionExecutor";
173
- const normalizedDefaultChannel = normalizeText(defaultChannel).toLowerCase() || "api";
174
- const initialResolutionScope = resolveActionExecutorScope({
175
- app,
176
- request,
177
- requestScopeProperty
178
- });
179
-
180
- if (!initialResolutionScope || typeof initialResolutionScope.has !== "function" || typeof initialResolutionScope.make !== "function") {
181
- return null;
182
- }
183
-
184
- const executeAction = async (payload = {}) => {
185
- const source = normalizeObject(payload);
186
- const normalizedInput = normalizeObject(source.input);
187
- const normalizedDeps = normalizeObject(source.deps);
188
- const normalizedChannel = normalizeText(source.channel || normalizedDefaultChannel).toLowerCase() || normalizedDefaultChannel;
189
- const resolutionScope = resolveActionExecutorScope({
190
- app,
191
- request,
192
- requestScopeProperty
193
- });
194
-
195
- if (!resolutionScope || typeof resolutionScope.has !== "function" || typeof resolutionScope.make !== "function") {
196
- throw new RouteRegistrationError("request.executeAction requires a container scope with has()/make().");
197
- }
198
- if (!resolutionScope.has(normalizedActionExecutorToken)) {
199
- throw new RouteRegistrationError(`request.executeAction requires "${normalizedActionExecutorToken}" binding.`);
200
- }
201
-
202
- const actionExecutor = resolutionScope.make(normalizedActionExecutorToken);
203
- if (!actionExecutor || typeof actionExecutor.execute !== "function") {
204
- throw new RouteRegistrationError(`"${normalizedActionExecutorToken}" must provide execute().`);
205
- }
206
- const definition =
207
- typeof actionExecutor.getDefinition === "function"
208
- ? actionExecutor.getDefinition(source.actionId, source.version == null ? null : source.version)
209
- : null;
210
-
211
- const baseContext = buildActionExecutionContext({
212
- request,
213
- context: normalizeObject(source.context),
214
- channel: normalizedChannel,
215
- defaultSurfaceId
216
- });
217
- const executionContext = await enrichActionExecutionContext({
218
- resolutionScope,
219
- request,
220
- actionId: source.actionId,
221
- version: source.version == null ? null : source.version,
222
- definition,
223
- input: normalizedInput,
224
- deps: normalizedDeps,
225
- channel: normalizedChannel,
226
- baseContext
227
- });
228
- const visibilityContext = await resolveRouteVisibilityContext({
229
- resolutionScope,
230
- request,
231
- routeVisibility: resolveRouteVisibilityFromRequestAndPayload(request, source),
232
- context: executionContext,
233
- input: normalizedInput,
234
- deps: normalizedDeps,
235
- actionId: source.actionId,
236
- version: source.version == null ? null : source.version,
237
- channel: normalizedChannel
238
- });
239
- executionContext.visibilityContext = visibilityContext;
240
- executionContext.requestMeta = {
241
- ...normalizeObject(executionContext.requestMeta),
242
- visibilityContext,
243
- routeVisibility: visibilityContext.visibility
244
- };
245
-
246
- return actionExecutor.execute({
247
- actionId: source.actionId,
248
- version: source.version == null ? null : source.version,
249
- input: normalizedInput,
250
- context: executionContext,
251
- deps: normalizedDeps
252
- });
253
- };
254
-
255
- Object.defineProperty(request, normalizedProperty, {
256
- configurable: true,
257
- enumerable: false,
258
- writable: true,
259
- value: executeAction
260
- });
261
-
262
- return request[normalizedProperty];
263
- }
264
-
265
- export { buildActionExecutionContext, attachRequestActionExecutor };
@@ -1,57 +0,0 @@
1
- function resolveRequestRuntimeId({ request = null, requestIdResolver = null } = {}) {
2
- if (typeof requestIdResolver === "function") {
3
- const resolvedByResolver = String(requestIdResolver(request) || "").trim();
4
- if (resolvedByResolver) {
5
- return resolvedByResolver;
6
- }
7
- }
8
-
9
- const resolvedFromRequest = String(request?.id || "").trim();
10
- if (resolvedFromRequest) {
11
- return resolvedFromRequest;
12
- }
13
-
14
- return `req-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
15
- }
16
-
17
- function normalizeRequestScopeProperty(value) {
18
- const normalized = String(value || "").trim();
19
- return normalized || "scope";
20
- }
21
-
22
- function attachRequestScope({
23
- app = null,
24
- request = null,
25
- reply = null,
26
- requestScopeProperty = "scope",
27
- requestScopeIdPrefix = "http",
28
- requestIdResolver = null
29
- } = {}) {
30
- if (!app || typeof app.createScope !== "function") {
31
- return null;
32
- }
33
-
34
- const runtimeRequestId = resolveRequestRuntimeId({
35
- request,
36
- requestIdResolver
37
- });
38
-
39
- const scopePrefix = String(requestScopeIdPrefix || "").trim() || "http";
40
- const scope = app.createScope(`${scopePrefix}:${runtimeRequestId}`);
41
- if (!scope || typeof scope.instance !== "function") {
42
- return null;
43
- }
44
-
45
- scope.instance("jskit.http.request", request);
46
- scope.instance("jskit.http.reply", reply);
47
- scope.instance("jskit.http.requestId", runtimeRequestId);
48
- scope.instance("jskit.http.requestScope", scope);
49
-
50
- if (request && typeof request === "object") {
51
- request[normalizeRequestScopeProperty(requestScopeProperty)] = scope;
52
- }
53
-
54
- return scope;
55
- }
56
-
57
- export { normalizeRequestScopeProperty, attachRequestScope };
@@ -1,27 +0,0 @@
1
- import { Application, createApplication, createProviderClass } from "../../shared/runtime/application.js";
2
- import { ServiceProvider } from "../../shared/runtime/serviceProvider.js";
3
- import * as errors from "../../shared/runtime/kernelErrors.js";
4
-
5
- const KERNEL_CORE_API = Object.freeze({
6
- Application,
7
- createApplication,
8
- createProviderClass,
9
- ServiceProvider,
10
- errors: Object.freeze({ ...errors })
11
- });
12
-
13
- class KernelCoreServiceProvider {
14
- static id = "runtime.kernel";
15
-
16
- register(app) {
17
- if (!app || typeof app.singleton !== "function") {
18
- throw new Error("KernelCoreServiceProvider requires application singleton().");
19
- }
20
-
21
- app.singleton("runtime.kernel", () => KERNEL_CORE_API);
22
- }
23
-
24
- boot() {}
25
- }
26
-
27
- export { KernelCoreServiceProvider };
@@ -1,10 +0,0 @@
1
- export { Application, createApplication, createProviderClass } from "../../shared/runtime/application.js";
2
- export { ServiceProvider } from "../../shared/runtime/serviceProvider.js";
3
- export {
4
- KernelError,
5
- ProviderNormalizationError,
6
- DuplicateProviderError,
7
- ProviderStartOrderError,
8
- ProviderLifecycleError
9
- } from "../../shared/runtime/kernelErrors.js";
10
- export { KernelCoreServiceProvider } from "./KernelCoreServiceProvider.js";
@@ -1,30 +0,0 @@
1
- import { createProviderRuntimeApp, createProviderRuntimeFromApp } from "./providerRuntime.js";
2
- import {
3
- createPlatformRuntimeBundle,
4
- createServerRuntime,
5
- createServerRuntimeWithPlatformBundle
6
- } from "./runtime.js";
7
-
8
- const PLATFORM_SERVER_RUNTIME_API = Object.freeze({
9
- createPlatformRuntimeBundle,
10
- createServerRuntime,
11
- createServerRuntimeWithPlatformBundle,
12
- createProviderRuntimeApp,
13
- createProviderRuntimeFromApp
14
- });
15
-
16
- class PlatformServerRuntimeServiceProvider {
17
- static id = "runtime.platform-server";
18
-
19
- register(app) {
20
- if (!app || typeof app.singleton !== "function") {
21
- throw new Error("PlatformServerRuntimeServiceProvider requires application singleton().");
22
- }
23
-
24
- app.singleton("runtime.platform-server", () => PLATFORM_SERVER_RUNTIME_API);
25
- }
26
-
27
- boot() {}
28
- }
29
-
30
- export { PlatformServerRuntimeServiceProvider };
@@ -1,283 +0,0 @@
1
- import { readdir } from "node:fs/promises";
2
- import path from "node:path";
3
- import { pathToFileURL } from "node:url";
4
- import {
5
- isIdentifier,
6
- createWildcardMatcher,
7
- normalizeRelativePath,
8
- fileExists,
9
- isInsidePackageRoot,
10
- toAbsoluteSortedUniquePaths
11
- } from "./helpers.js";
12
-
13
- function normalizeServerProviderDefinitions(packageMetadata, packageId) {
14
- const runtime =
15
- packageMetadata && typeof packageMetadata.runtime === "object" && packageMetadata.runtime && !Array.isArray(packageMetadata.runtime)
16
- ? packageMetadata.runtime
17
- : {};
18
- const server =
19
- runtime && typeof runtime.server === "object" && runtime.server && !Array.isArray(runtime.server)
20
- ? runtime.server
21
- : null;
22
- if (!server) {
23
- return Object.freeze([]);
24
- }
25
-
26
- const providers = Array.isArray(server.providers) ? server.providers : [];
27
- const normalizedProviders = [];
28
-
29
- for (const providerDefinition of providers) {
30
- const entry = providerDefinition && typeof providerDefinition === "object" ? providerDefinition : {};
31
- const providerEntrypoint = String(entry.entrypoint || "").trim();
32
- const providerExport = String(entry.export || "").trim();
33
- const discoverConfig =
34
- entry.discover && typeof entry.discover === "object" && !Array.isArray(entry.discover) ? entry.discover : null;
35
-
36
- if (discoverConfig) {
37
- if (providerEntrypoint || providerExport) {
38
- throw new Error(
39
- `Package ${packageId} runtime.server.providers[] discover entries cannot include entrypoint/export fields.`
40
- );
41
- }
42
-
43
- const discoverDir = String(discoverConfig.dir || "").trim();
44
- if (!discoverDir) {
45
- throw new Error(`Package ${packageId} runtime.server.providers[] discover.dir is required.`);
46
- }
47
- if (discoverDir.startsWith("/") || discoverDir.startsWith("//")) {
48
- throw new Error(`Package ${packageId} runtime.server.providers[] discover.dir must be relative.`);
49
- }
50
- if (discoverDir.includes("*")) {
51
- throw new Error(`Package ${packageId} runtime.server.providers[] discover.dir cannot contain wildcard "*".`);
52
- }
53
-
54
- const discoverPattern = String(discoverConfig.pattern || "*Provider.js").trim() || "*Provider.js";
55
- if (discoverPattern.includes(path.sep) || discoverPattern.includes("/")) {
56
- throw new Error(
57
- `Package ${packageId} runtime.server.providers[] discover.pattern must match filenames only.`
58
- );
59
- }
60
- if (!createWildcardMatcher(discoverPattern)) {
61
- throw new Error(`Package ${packageId} runtime.server.providers[] discover.pattern is invalid.`);
62
- }
63
-
64
- const discoverRecursive = discoverConfig.recursive === true;
65
- normalizedProviders.push(
66
- Object.freeze({
67
- type: "discover",
68
- discoverDir,
69
- discoverPattern,
70
- discoverRecursive
71
- })
72
- );
73
- continue;
74
- }
75
-
76
- if (!providerEntrypoint) {
77
- throw new Error(`Package ${packageId} runtime.server.providers[] entrypoint is required.`);
78
- }
79
- if (!providerExport) {
80
- throw new Error(`Package ${packageId} runtime.server.providers[] export is required for ${providerEntrypoint}.`);
81
- }
82
- if (!isIdentifier(providerExport)) {
83
- throw new Error(`Package ${packageId} runtime.server.providers[] export is invalid: ${providerExport}`);
84
- }
85
-
86
- normalizedProviders.push(
87
- Object.freeze({
88
- type: "explicit",
89
- providerEntrypoint,
90
- providerExport
91
- })
92
- );
93
- }
94
-
95
- return Object.freeze(normalizedProviders);
96
- }
97
-
98
- function isProviderDefinition(value) {
99
- if (typeof value !== "function") {
100
- return false;
101
- }
102
- const prototype = value.prototype && typeof value.prototype === "object" ? value.prototype : null;
103
- const hasLifecycleMethods = Boolean(
104
- (prototype && typeof prototype.register === "function") ||
105
- (prototype && typeof prototype.boot === "function") ||
106
- (prototype && typeof prototype.shutdown === "function")
107
- );
108
- const rawId = value.id;
109
- const hasProviderId = rawId !== undefined && rawId !== null && String(rawId).trim().length > 0;
110
- return hasLifecycleMethods && hasProviderId;
111
- }
112
-
113
- function normalizeProviderExportValue(value, { packageId, label }) {
114
- const output = [];
115
- const queue = Array.isArray(value) ? [...value] : [value];
116
- while (queue.length > 0) {
117
- const candidate = queue.shift();
118
- if (!candidate) {
119
- continue;
120
- }
121
- if (Array.isArray(candidate)) {
122
- queue.push(...candidate);
123
- continue;
124
- }
125
- if (isProviderDefinition(candidate)) {
126
- output.push(candidate);
127
- continue;
128
- }
129
- throw new Error(
130
- `Package ${packageId} ${label} must export a provider class/function or an array of provider classes/functions.`
131
- );
132
- }
133
- return output;
134
- }
135
-
136
- function resolveProviderClassesFromModule(moduleNamespace, { packageId, providerExport }) {
137
- const namespace = moduleNamespace && typeof moduleNamespace === "object" ? moduleNamespace : {};
138
- if (providerExport) {
139
- if (!Object.prototype.hasOwnProperty.call(namespace, providerExport)) {
140
- throw new Error(`Package ${packageId} provider export "${providerExport}" was not found.`);
141
- }
142
- return normalizeProviderExportValue(namespace[providerExport], {
143
- packageId,
144
- label: `provider export "${providerExport}"`
145
- });
146
- }
147
-
148
- const discovered = [];
149
- for (const [exportName, exportValue] of Object.entries(namespace)) {
150
- if (!isProviderDefinition(exportValue)) {
151
- continue;
152
- }
153
- discovered.push(...normalizeProviderExportValue(exportValue, { packageId, label: `export "${exportName}"` }));
154
- }
155
-
156
- if (discovered.length < 1) {
157
- throw new Error(
158
- `Package ${packageId} provider entrypoint did not export any provider classes/functions.`
159
- );
160
- }
161
-
162
- return discovered;
163
- }
164
-
165
- async function collectDiscoveredProviderModulePaths({ packageEntry, providerDefinition }) {
166
- const discoverRoot = path.resolve(packageEntry.packageRoot, providerDefinition.discoverDir);
167
- if (!isInsidePackageRoot(packageEntry.packageRoot, discoverRoot)) {
168
- throw new Error(
169
- `Package ${packageEntry.packageId} runtime.server.providers[] discover.dir escapes package root: ${providerDefinition.discoverDir}`
170
- );
171
- }
172
- if (!(await fileExists(discoverRoot))) {
173
- return Object.freeze([]);
174
- }
175
-
176
- const patternMatcher = createWildcardMatcher(providerDefinition.discoverPattern);
177
- if (!(patternMatcher instanceof RegExp)) {
178
- throw new Error(
179
- `Package ${packageEntry.packageId} runtime.server.providers[] discover.pattern is invalid: ${providerDefinition.discoverPattern}`
180
- );
181
- }
182
- const collectedPaths = [];
183
-
184
- async function walk(currentPath) {
185
- const entries = await readdir(currentPath, { withFileTypes: true });
186
- entries.sort((left, right) => left.name.localeCompare(right.name));
187
-
188
- for (const entry of entries) {
189
- const entryName = String(entry?.name || "");
190
- if (!entryName || entryName.startsWith(".")) {
191
- continue;
192
- }
193
-
194
- const absoluteEntryPath = path.join(currentPath, entryName);
195
- if (entry.isDirectory()) {
196
- if (entryName === "node_modules") {
197
- continue;
198
- }
199
- if (providerDefinition.discoverRecursive === true) {
200
- await walk(absoluteEntryPath);
201
- }
202
- continue;
203
- }
204
-
205
- if (!entry.isFile()) {
206
- continue;
207
- }
208
-
209
- if (patternMatcher.test(entryName)) {
210
- collectedPaths.push(absoluteEntryPath);
211
- }
212
- }
213
- }
214
-
215
- await walk(discoverRoot);
216
- return toAbsoluteSortedUniquePaths(collectedPaths);
217
- }
218
-
219
- function registerProviderClass({ providerClass, sourceId, seenProviderIds, orderedProviderClasses }) {
220
- const providerId = String(providerClass.id || providerClass.name || "<anonymous-provider>").trim();
221
- if (seenProviderIds.has(providerId)) {
222
- const existingSourceId = seenProviderIds.get(providerId);
223
- throw new Error(`Provider id "${providerId}" is duplicated between ${existingSourceId} and ${sourceId}.`);
224
- }
225
-
226
- seenProviderIds.set(providerId, sourceId);
227
- orderedProviderClasses.push(providerClass);
228
- }
229
-
230
- async function loadPackageProviders({ packageEntry }) {
231
- const providerDefinitions = normalizeServerProviderDefinitions(packageEntry.packageMetadata, packageEntry.packageId);
232
- if (providerDefinitions.length < 1) {
233
- return Object.freeze([]);
234
- }
235
-
236
- const providerClasses = [];
237
- for (const providerDefinition of providerDefinitions) {
238
- if (providerDefinition.type === "discover") {
239
- const discoveredProviderPaths = await collectDiscoveredProviderModulePaths({
240
- packageEntry,
241
- providerDefinition
242
- });
243
-
244
- for (const discoveredProviderPath of discoveredProviderPaths) {
245
- const providerModule = await import(pathToFileURL(discoveredProviderPath).href);
246
- providerClasses.push(
247
- ...resolveProviderClassesFromModule(providerModule, {
248
- packageId: `${packageEntry.packageId} (${normalizeRelativePath(
249
- packageEntry.packageRoot,
250
- discoveredProviderPath
251
- )})`,
252
- providerExport: ""
253
- })
254
- );
255
- }
256
- continue;
257
- }
258
-
259
- const providerModulePath = path.resolve(packageEntry.packageRoot, providerDefinition.providerEntrypoint);
260
- if (!isInsidePackageRoot(packageEntry.packageRoot, providerModulePath)) {
261
- throw new Error(
262
- `Package ${packageEntry.packageId} runtime.server.providers[] entrypoint escapes package root: ${providerDefinition.providerEntrypoint}`
263
- );
264
- }
265
- if (!(await fileExists(providerModulePath))) {
266
- throw new Error(
267
- `Package ${packageEntry.packageId} runtime.server.providers[] entrypoint not found: ${providerDefinition.providerEntrypoint}`
268
- );
269
- }
270
-
271
- const providerModule = await import(pathToFileURL(providerModulePath).href);
272
- providerClasses.push(
273
- ...resolveProviderClassesFromModule(providerModule, {
274
- packageId: packageEntry.packageId,
275
- providerExport: providerDefinition.providerExport
276
- })
277
- );
278
- }
279
-
280
- return Object.freeze(providerClasses);
281
- }
282
-
283
- export { loadPackageProviders, registerProviderClass };