@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.
- package/_testable/index.js +1 -3
- package/client/componentRegistry.js +44 -0
- package/client/componentRegistry.test.js +16 -0
- package/client/moduleBootstrap.js +54 -97
- package/client/moduleBootstrap.test.js +47 -32
- package/client/vite/clientBootstrapPlugin.test.js +2 -2
- package/internal/node/installedPackages.js +2 -7
- package/internal/node/installedPackages.test.js +26 -5
- package/package.json +3 -5
- package/server/actions/actionCatalogue.js +162 -0
- package/server/actions/actionEvents.js +66 -0
- package/server/actions/actionProvider.js +23 -0
- package/server/actions/index.js +3 -6
- package/server/features/defineFeature.js +131 -0
- package/server/features/defineFeature.test.js +238 -0
- package/server/features/index.js +1 -0
- package/server/http/HttpProvider.js +23 -0
- package/server/http/_testable/index.js +0 -1
- package/server/http/capabilityHttpRuntime.js +123 -0
- package/server/http/capabilityHttpRuntime.test.js +161 -0
- package/server/http/directRequestActionExecutor.js +78 -0
- package/server/http/index.js +2 -1
- package/server/http/lib/index.js +1 -13
- package/server/http/lib/routeRegistration.js +9 -25
- package/server/platform/index.js +2 -2
- package/server/platform/installedRuntime.js +122 -0
- package/server/platform/installedRuntime.test.js +103 -0
- package/server/platform/providerRuntime/capabilityProviderLoader.js +100 -0
- package/server/platform/providerRuntime/capabilityProviderLoader.test.js +80 -0
- package/server/platform/providerRuntime/packageCatalog.js +4 -1
- package/server/platform/surfaceRuntime.js +1 -15
- package/server/runtime/BootstrapProvider.js +69 -0
- package/server/runtime/EventProvider.js +74 -0
- package/server/runtime/EventProvider.test.js +32 -0
- package/server/runtime/entityChangeEvents.js +7 -7
- package/server/runtime/entityChangeEvents.test.js +2 -2
- package/server/runtime/fastifyBootstrap.js +0 -40
- package/server/runtime/index.js +2 -3
- package/server/surface/index.js +0 -1
- package/shared/actions/actionDefinitions.js +20 -0
- package/shared/actions/pipeline.js +22 -0
- package/shared/actions/policies.js +1 -1
- package/shared/actions/registry.js +3 -0
- package/shared/capabilities/defineProvider.js +99 -0
- package/shared/capabilities/index.js +2 -0
- package/shared/capabilities/runtime.js +243 -0
- package/shared/capabilities/runtime.test.js +170 -0
- package/test/barrelExposure.test.js +36 -13
- package/test/exportsContract.test.js +8 -3
- package/server/actions/ActionRuntimeServiceProvider.js +0 -304
- package/server/actions/ActionRuntimeServiceProvider.test.js +0 -558
- package/server/container/ContainerCoreServiceProvider.js +0 -27
- package/server/container/index.js +0 -10
- package/server/http/HttpFastifyServiceProvider.js +0 -25
- package/server/http/lib/httpRuntime.js +0 -83
- package/server/http/lib/kernel.js +0 -15
- package/server/http/lib/kernel.test.js +0 -1565
- package/server/http/lib/requestActionExecutor.js +0 -265
- package/server/http/lib/requestScope.js +0 -57
- package/server/kernel/KernelCoreServiceProvider.js +0 -27
- package/server/kernel/index.js +0 -10
- package/server/platform/PlatformServerRuntimeServiceProvider.js +0 -30
- package/server/platform/providerRuntime/providerLoader.js +0 -283
- package/server/platform/providerRuntime.js +0 -146
- package/server/platform/providerRuntime.test.js +0 -214
- package/server/platform/runtime.js +0 -40
- package/server/registries/actionSurfaceSourceRegistry.js +0 -148
- package/server/registries/bootstrapPayloadContributorRegistry.js +0 -51
- package/server/registries/domainEventListenerRegistry.js +0 -58
- package/server/registries/index.js +0 -32
- package/server/registries/primitives.js +0 -63
- package/server/registries/routeVisibilityResolverRegistry.js +0 -84
- package/server/registries/serviceRegistrationRegistry.js +0 -488
- package/server/runtime/ServerRuntimeCoreServiceProvider.js +0 -64
- package/server/runtime/ServerRuntimeCoreServiceProvider.test.js +0 -53
- package/server/runtime/apiRoutePolicyParity.test.js +0 -109
- package/server/runtime/apiRouteRegistration.js +0 -65
- package/server/runtime/bootBootstrapRoutes.js +0 -29
- package/server/runtime/bootBootstrapRoutes.test.js +0 -69
- package/server/runtime/bootstrapContributors.test.js +0 -166
- package/server/runtime/composition.js +0 -142
- package/server/runtime/domainEvents.test.js +0 -114
- package/server/runtime/domainRules.js +0 -50
- package/server/runtime/domainRules.test.js +0 -87
- package/server/runtime/runtimeAssembly.js +0 -113
- package/server/runtime/runtimeKernel.js +0 -55
- package/server/runtime/serviceRegistration.test.js +0 -251
- package/server/support/SupportCoreServiceProvider.js +0 -27
- package/server/surface/SurfaceRoutingServiceProvider.js +0 -27
- package/shared/actions/index.js +0 -2
- package/shared/runtime/application.js +0 -324
- package/shared/runtime/container.js +0 -261
- package/shared/runtime/containerErrors.js +0 -22
- package/shared/runtime/index.js +0 -18
- package/shared/runtime/kernelErrors.js +0 -25
- package/shared/runtime/kernelErrors.test.js +0 -120
- package/shared/runtime/serviceProvider.js +0 -13
- package/shared/support/containerToken.js +0 -8
- package/shared/support/generatedUiContract.js +0 -542
- package/shared/support/generatedUiContract.test.js +0 -218
- package/shared/support/tokens.js +0 -1
- package/shared/support/tokens.test.js +0 -17
|
@@ -1,63 +0,0 @@
|
|
|
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
|
-
};
|
|
@@ -1,84 +0,0 @@
|
|
|
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
|
-
function normalizeRouteVisibilityResolver(entry) {
|
|
8
|
-
if (typeof entry === "function") {
|
|
9
|
-
return {
|
|
10
|
-
resolverId: normalizeText(entry.name) || "anonymous",
|
|
11
|
-
resolve: entry
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
if (!entry || typeof entry !== "object" || Array.isArray(entry) || typeof entry.resolve !== "function") {
|
|
16
|
-
return null;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
return {
|
|
20
|
-
resolverId: normalizeText(entry.resolverId) || "anonymous",
|
|
21
|
-
resolve: entry.resolve
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function resolveRouteVisibilityResolvers(scope) {
|
|
26
|
-
return resolveTaggedEntries(scope, "jskit.runtime.http.visibilityResolvers")
|
|
27
|
-
.map((entry) => normalizeRouteVisibilityResolver(entry))
|
|
28
|
-
.filter(Boolean);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function registerRouteVisibilityResolver(app, token, factory) {
|
|
32
|
-
registerTaggedSingleton(app, token, factory, "jskit.runtime.http.visibilityResolvers", {
|
|
33
|
-
context: "registerRouteVisibilityResolver",
|
|
34
|
-
ErrorType: RouteRegistrationError
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
async function resolveRouteVisibilityContext({
|
|
39
|
-
resolutionScope = null,
|
|
40
|
-
request = null,
|
|
41
|
-
routeVisibility = ROUTE_VISIBILITY_PUBLIC,
|
|
42
|
-
context = {},
|
|
43
|
-
input = {},
|
|
44
|
-
deps = {},
|
|
45
|
-
actionId = "",
|
|
46
|
-
version = null,
|
|
47
|
-
channel = "api"
|
|
48
|
-
} = {}) {
|
|
49
|
-
const normalizedRouteVisibility = normalizeRouteVisibilityToken(routeVisibility);
|
|
50
|
-
const normalizedCoreVisibility = normalizeRouteVisibility(normalizedRouteVisibility);
|
|
51
|
-
const resolvers = resolveRouteVisibilityResolvers(resolutionScope);
|
|
52
|
-
const patch = {};
|
|
53
|
-
|
|
54
|
-
for (const resolver of resolvers) {
|
|
55
|
-
const contribution = await resolver.resolve({
|
|
56
|
-
request,
|
|
57
|
-
visibility: normalizedRouteVisibility,
|
|
58
|
-
context,
|
|
59
|
-
input,
|
|
60
|
-
deps,
|
|
61
|
-
actionId: normalizeText(actionId),
|
|
62
|
-
version: version == null ? null : version,
|
|
63
|
-
channel: normalizeText(channel).toLowerCase() || "api"
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
if (!contribution || typeof contribution !== "object" || Array.isArray(contribution)) {
|
|
67
|
-
continue;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
Object.assign(patch, contribution);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return normalizeVisibilityContext({
|
|
74
|
-
...patch,
|
|
75
|
-
visibility: normalizedRouteVisibility,
|
|
76
|
-
requiresActorScope: patch.requiresActorScope === true || normalizedCoreVisibility === "user"
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export {
|
|
81
|
-
resolveRouteVisibilityResolvers,
|
|
82
|
-
registerRouteVisibilityResolver,
|
|
83
|
-
resolveRouteVisibilityContext
|
|
84
|
-
};
|
|
@@ -1,488 +0,0 @@
|
|
|
1
|
-
import { normalizeObject, normalizePositiveInteger, normalizeText } from "../../shared/support/normalize.js";
|
|
2
|
-
import { isContainerToken } from "../../shared/support/containerToken.js";
|
|
3
|
-
import { createEntityChangePublisher } from "../runtime/entityChangeEvents.js";
|
|
4
|
-
import {
|
|
5
|
-
assertTaggableApp,
|
|
6
|
-
normalizeNestedEntries,
|
|
7
|
-
registerTaggedSingleton,
|
|
8
|
-
resolveTaggedEntries
|
|
9
|
-
} from "./primitives.js";
|
|
10
|
-
|
|
11
|
-
const DEFAULT_REALTIME_AUDIENCE = "event_scope";
|
|
12
|
-
let SERVICE_REGISTRATION_INDEX = 0;
|
|
13
|
-
|
|
14
|
-
function normalizeMethodName(value, { context = "service method" } = {}) {
|
|
15
|
-
const methodName = String(value || "").trim();
|
|
16
|
-
if (!methodName) {
|
|
17
|
-
throw new TypeError(`${context} must be a non-empty string.`);
|
|
18
|
-
}
|
|
19
|
-
return methodName;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function createServiceRegistrationToken() {
|
|
23
|
-
SERVICE_REGISTRATION_INDEX += 1;
|
|
24
|
-
return Symbol(`jskit.runtime.services.registration.${SERVICE_REGISTRATION_INDEX}`);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function normalizeServiceEventType(value, { context = "service event" } = {}) {
|
|
28
|
-
const normalizedType = normalizeText(value).toLowerCase();
|
|
29
|
-
if (normalizedType !== "entity.changed") {
|
|
30
|
-
throw new TypeError(`${context}.type must be "entity.changed".`);
|
|
31
|
-
}
|
|
32
|
-
return normalizedType;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function normalizeServiceEventOperation(value, { context = "service event" } = {}) {
|
|
36
|
-
if (typeof value === "function") {
|
|
37
|
-
return value;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const normalizedOperation = normalizeText(value).toLowerCase();
|
|
41
|
-
if (!normalizedOperation) {
|
|
42
|
-
throw new TypeError(`${context}.operation is required.`);
|
|
43
|
-
}
|
|
44
|
-
if (normalizedOperation !== "created" && normalizedOperation !== "updated" && normalizedOperation !== "deleted") {
|
|
45
|
-
throw new TypeError(`${context}.operation must be one of: created, updated, deleted.`);
|
|
46
|
-
}
|
|
47
|
-
return normalizedOperation;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function normalizeServiceEventEntityId(value) {
|
|
51
|
-
if (typeof value === "function" || typeof value === "string") {
|
|
52
|
-
return value;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const parsed = normalizePositiveInteger(value);
|
|
56
|
-
if (parsed > 0) {
|
|
57
|
-
return parsed;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return null;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function normalizeServiceEventMetaField(value, { context = "service event meta field" } = {}) {
|
|
64
|
-
if (value == null) {
|
|
65
|
-
return null;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (typeof value === "function") {
|
|
69
|
-
return value;
|
|
70
|
-
}
|
|
71
|
-
if (typeof value !== "string") {
|
|
72
|
-
throw new TypeError(`${context} must be a non-empty string or function.`);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const normalized = normalizeText(value);
|
|
76
|
-
if (!normalized) {
|
|
77
|
-
throw new TypeError(`${context} must be a non-empty string or function.`);
|
|
78
|
-
}
|
|
79
|
-
return normalized;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function normalizeRealtimeDispatch(value, { context = "service event.realtime" } = {}) {
|
|
83
|
-
const source = normalizeObject(value);
|
|
84
|
-
if (Object.keys(source).length < 1) {
|
|
85
|
-
return null;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const event = normalizeText(source.event);
|
|
89
|
-
if (!event) {
|
|
90
|
-
throw new TypeError(`${context}.event is required.`);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const payload = typeof source.payload === "function" ? source.payload : null;
|
|
94
|
-
const audience = normalizeRealtimeAudience(source.audience, {
|
|
95
|
-
context: `${context}.audience`
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
return Object.freeze({
|
|
99
|
-
event,
|
|
100
|
-
payload,
|
|
101
|
-
audience
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function normalizeRealtimeAudience(value, { context = "service event.realtime.audience" } = {}) {
|
|
106
|
-
if (typeof value === "undefined") {
|
|
107
|
-
return DEFAULT_REALTIME_AUDIENCE;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if (typeof value === "function") {
|
|
111
|
-
return value;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
if (typeof value === "string") {
|
|
115
|
-
const preset = normalizeText(value).toLowerCase();
|
|
116
|
-
if (!preset) {
|
|
117
|
-
throw new TypeError(`${context} must be a non-empty string, function, or object.`);
|
|
118
|
-
}
|
|
119
|
-
return preset;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
123
|
-
return Object.freeze({
|
|
124
|
-
...value
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
throw new TypeError(`${context} must be a string, function, or object.`);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function normalizeServiceEventSpec(entry, { context = "service event" } = {}) {
|
|
132
|
-
const source = normalizeObject(entry);
|
|
133
|
-
|
|
134
|
-
return Object.freeze({
|
|
135
|
-
type: normalizeServiceEventType(source.type, { context }),
|
|
136
|
-
source: normalizeText(source.source),
|
|
137
|
-
entity: normalizeText(source.entity),
|
|
138
|
-
operation: normalizeServiceEventOperation(source.operation, { context }),
|
|
139
|
-
entityId: normalizeServiceEventEntityId(source.entityId),
|
|
140
|
-
action: normalizeServiceEventMetaField(source.action, { context: `${context}.action` }),
|
|
141
|
-
reason: normalizeServiceEventMetaField(source.reason, { context: `${context}.reason` }),
|
|
142
|
-
realtime: normalizeRealtimeDispatch(source.realtime, { context: `${context}.realtime` })
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function normalizeServiceMetadata(value = {}) {
|
|
147
|
-
const source = normalizeObject(value);
|
|
148
|
-
const unsupportedFields = Object.keys(source).filter((key) => key !== "events");
|
|
149
|
-
if (unsupportedFields.length > 0) {
|
|
150
|
-
throw new TypeError(`service metadata supports only events. Received: ${unsupportedFields.sort().join(", ")}.`);
|
|
151
|
-
}
|
|
152
|
-
const eventsSource = normalizeObject(source.events);
|
|
153
|
-
const events = {};
|
|
154
|
-
|
|
155
|
-
for (const [methodName, eventEntries] of Object.entries(eventsSource)) {
|
|
156
|
-
const normalizedMethodName = normalizeMethodName(methodName, {
|
|
157
|
-
context: "service metadata.events method"
|
|
158
|
-
});
|
|
159
|
-
const normalizedEventEntries = normalizeNestedEntries(eventEntries).map((entry, index) =>
|
|
160
|
-
normalizeServiceEventSpec(entry, {
|
|
161
|
-
context: `service metadata.events.${normalizedMethodName}[${index}]`
|
|
162
|
-
})
|
|
163
|
-
);
|
|
164
|
-
events[normalizedMethodName] = Object.freeze(normalizedEventEntries);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
return Object.freeze({
|
|
168
|
-
events: Object.freeze(events)
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function normalizeServiceEventsForDefinition(serviceDefinition, serviceMetadata) {
|
|
173
|
-
const service = normalizeObject(serviceDefinition);
|
|
174
|
-
const methodNameSet = new Set(
|
|
175
|
-
Object.entries(service)
|
|
176
|
-
.filter(([, value]) => typeof value === "function")
|
|
177
|
-
.map(([name]) => name)
|
|
178
|
-
);
|
|
179
|
-
const declaredEvents = normalizeObject(serviceMetadata.events);
|
|
180
|
-
const normalizedEvents = {};
|
|
181
|
-
|
|
182
|
-
for (const [methodName, events] of Object.entries(declaredEvents)) {
|
|
183
|
-
if (!methodNameSet.has(methodName)) {
|
|
184
|
-
throw new TypeError(`service metadata.events.${methodName} does not match a service method.`);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
const normalizedEntries = normalizeNestedEntries(events).map((entry, index) =>
|
|
188
|
-
normalizeServiceEventSpec(entry, {
|
|
189
|
-
context: `service metadata.events.${methodName}[${index}]`
|
|
190
|
-
})
|
|
191
|
-
);
|
|
192
|
-
|
|
193
|
-
for (const [index, entry] of normalizedEntries.entries()) {
|
|
194
|
-
if (!entry.source || !entry.entity) {
|
|
195
|
-
throw new TypeError(
|
|
196
|
-
`service metadata.events.${methodName}[${index}] requires source and entity for "entity.changed".`
|
|
197
|
-
);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
normalizedEvents[methodName] = Object.freeze(normalizedEntries);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
return Object.freeze(normalizedEvents);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
function resolveMethodOptions(args = []) {
|
|
208
|
-
if (!Array.isArray(args) || args.length < 1) {
|
|
209
|
-
return {};
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
const candidate = args[args.length - 1];
|
|
213
|
-
if (candidate && typeof candidate === "object" && !Array.isArray(candidate)) {
|
|
214
|
-
return candidate;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
return {};
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
function resolveEventOperation(spec, state) {
|
|
221
|
-
if (typeof spec.operation === "function") {
|
|
222
|
-
return spec.operation({
|
|
223
|
-
result: state.result,
|
|
224
|
-
args: state.args,
|
|
225
|
-
options: state.options,
|
|
226
|
-
methodName: state.methodName,
|
|
227
|
-
serviceToken: state.serviceToken
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
return spec.operation;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
function resolveEventEntityId(spec, state) {
|
|
234
|
-
if (typeof spec.entityId === "function") {
|
|
235
|
-
return spec.entityId({
|
|
236
|
-
result: state.result,
|
|
237
|
-
args: state.args,
|
|
238
|
-
options: state.options,
|
|
239
|
-
methodName: state.methodName,
|
|
240
|
-
serviceToken: state.serviceToken
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
if (typeof spec.entityId === "string") {
|
|
245
|
-
return state?.result?.[spec.entityId];
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
if (Number.isInteger(spec.entityId) && spec.entityId > 0) {
|
|
249
|
-
return spec.entityId;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
return state?.result?.id;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
function resolveEventMetaField(value, state) {
|
|
256
|
-
const context = `service metadata.events.${state.methodName}.meta`;
|
|
257
|
-
if (typeof value === "function") {
|
|
258
|
-
const resolved = value({
|
|
259
|
-
result: state.result,
|
|
260
|
-
args: state.args,
|
|
261
|
-
options: state.options,
|
|
262
|
-
methodName: state.methodName,
|
|
263
|
-
serviceToken: state.serviceToken,
|
|
264
|
-
event: state.event
|
|
265
|
-
});
|
|
266
|
-
if (resolved == null) {
|
|
267
|
-
return "";
|
|
268
|
-
}
|
|
269
|
-
if (typeof resolved !== "string") {
|
|
270
|
-
throw new TypeError(`${context} field resolver must return a string.`);
|
|
271
|
-
}
|
|
272
|
-
return normalizeText(resolved);
|
|
273
|
-
}
|
|
274
|
-
return normalizeText(value);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
function resolveEventMeta(spec, state) {
|
|
278
|
-
const meta = {
|
|
279
|
-
service: Object.freeze({
|
|
280
|
-
token: state.serviceToken,
|
|
281
|
-
method: state.methodName
|
|
282
|
-
})
|
|
283
|
-
};
|
|
284
|
-
|
|
285
|
-
const action = resolveEventMetaField(spec.action, {
|
|
286
|
-
...state,
|
|
287
|
-
event: spec
|
|
288
|
-
});
|
|
289
|
-
if (action) {
|
|
290
|
-
meta.action = action;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
const reason = resolveEventMetaField(spec.reason, {
|
|
294
|
-
...state,
|
|
295
|
-
event: spec
|
|
296
|
-
});
|
|
297
|
-
if (reason) {
|
|
298
|
-
meta.reason = reason;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
if (spec.realtime) {
|
|
302
|
-
meta.realtime = spec.realtime.payload
|
|
303
|
-
? Object.freeze({
|
|
304
|
-
event: spec.realtime.event,
|
|
305
|
-
payload: spec.realtime.payload({
|
|
306
|
-
event: state.event,
|
|
307
|
-
result: state.result,
|
|
308
|
-
args: state.args,
|
|
309
|
-
options: state.options,
|
|
310
|
-
methodName: state.methodName,
|
|
311
|
-
serviceToken: state.serviceToken
|
|
312
|
-
})
|
|
313
|
-
})
|
|
314
|
-
: Object.freeze({
|
|
315
|
-
event: spec.realtime.event
|
|
316
|
-
});
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
return Object.freeze(meta);
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
function createServiceMethodEventPublisher(scope, serviceToken, methodName, specs = []) {
|
|
323
|
-
if (!scope || typeof scope.make !== "function" || typeof scope.has !== "function") {
|
|
324
|
-
throw new TypeError("service event publisher requires a scope with has()/make().");
|
|
325
|
-
}
|
|
326
|
-
if (specs.length < 1) {
|
|
327
|
-
return async function publishNoop() {
|
|
328
|
-
return null;
|
|
329
|
-
};
|
|
330
|
-
}
|
|
331
|
-
if (!scope.has("domainEvents")) {
|
|
332
|
-
throw new Error(`app.service(${String(serviceToken)}) requires "domainEvents" binding to emit service events.`);
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
const domainEvents = scope.make("domainEvents");
|
|
336
|
-
const publishers = specs.map((spec) =>
|
|
337
|
-
createEntityChangePublisher({
|
|
338
|
-
domainEvents,
|
|
339
|
-
source: spec.source,
|
|
340
|
-
entity: spec.entity
|
|
341
|
-
})
|
|
342
|
-
);
|
|
343
|
-
|
|
344
|
-
return async function publishServiceMethodEvents(state) {
|
|
345
|
-
for (const [index, spec] of specs.entries()) {
|
|
346
|
-
const publisher = publishers[index];
|
|
347
|
-
const operation = resolveEventOperation(spec, state);
|
|
348
|
-
const entityId = resolveEventEntityId(spec, state);
|
|
349
|
-
const eventMeta = resolveEventMeta(spec, {
|
|
350
|
-
...state,
|
|
351
|
-
event: spec
|
|
352
|
-
});
|
|
353
|
-
await publisher(operation, entityId, state.options, eventMeta);
|
|
354
|
-
}
|
|
355
|
-
return null;
|
|
356
|
-
};
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
function materializeServiceRegistration(scope, registrationSpec) {
|
|
360
|
-
const service = registrationSpec.factory(scope);
|
|
361
|
-
const normalizedService = normalizeObject(service);
|
|
362
|
-
const events = normalizeServiceEventsForDefinition(normalizedService, registrationSpec.metadata);
|
|
363
|
-
const wrappedService = {};
|
|
364
|
-
|
|
365
|
-
for (const [methodName, method] of Object.entries(normalizedService)) {
|
|
366
|
-
if (typeof method !== "function") {
|
|
367
|
-
continue;
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
const methodEvents = events[methodName] || [];
|
|
371
|
-
const eventPublisher = createServiceMethodEventPublisher(
|
|
372
|
-
scope,
|
|
373
|
-
registrationSpec.serviceToken,
|
|
374
|
-
methodName,
|
|
375
|
-
methodEvents
|
|
376
|
-
);
|
|
377
|
-
|
|
378
|
-
wrappedService[methodName] = function registeredServiceMethod(...args) {
|
|
379
|
-
if (methodEvents.length < 1) {
|
|
380
|
-
return method(...args);
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
const options = resolveMethodOptions(args);
|
|
384
|
-
const result = method(...args);
|
|
385
|
-
const publish = (resolvedResult) =>
|
|
386
|
-
eventPublisher({
|
|
387
|
-
result: resolvedResult,
|
|
388
|
-
args,
|
|
389
|
-
options,
|
|
390
|
-
methodName,
|
|
391
|
-
serviceToken: registrationSpec.serviceToken
|
|
392
|
-
}).then(() => resolvedResult);
|
|
393
|
-
|
|
394
|
-
if (result && typeof result.then === "function") {
|
|
395
|
-
return result.then((resolvedResult) => publish(resolvedResult));
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
return publish(result);
|
|
399
|
-
};
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
Object.defineProperty(wrappedService, "serviceEvents", {
|
|
403
|
-
enumerable: false,
|
|
404
|
-
configurable: false,
|
|
405
|
-
writable: false,
|
|
406
|
-
value: events
|
|
407
|
-
});
|
|
408
|
-
Object.defineProperty(wrappedService, "serviceToken", {
|
|
409
|
-
enumerable: false,
|
|
410
|
-
configurable: false,
|
|
411
|
-
writable: false,
|
|
412
|
-
value: registrationSpec.serviceToken
|
|
413
|
-
});
|
|
414
|
-
|
|
415
|
-
return Object.freeze(wrappedService);
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
function normalizeServiceRegistration(value = {}) {
|
|
419
|
-
const source = normalizeObject(value);
|
|
420
|
-
if (!isContainerToken(source.serviceToken)) {
|
|
421
|
-
throw new TypeError("app.service requires a valid service token.");
|
|
422
|
-
}
|
|
423
|
-
if (typeof source.factory !== "function") {
|
|
424
|
-
throw new TypeError("app.service requires a factory function.");
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
return Object.freeze({
|
|
428
|
-
serviceToken: source.serviceToken,
|
|
429
|
-
factory: source.factory,
|
|
430
|
-
metadata: normalizeServiceMetadata(source.metadata)
|
|
431
|
-
});
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
function registerServiceRegistration(app, token, factory) {
|
|
435
|
-
registerTaggedSingleton(app, token, factory, "jskit.runtime.services.registrations", {
|
|
436
|
-
context: "registerServiceRegistration"
|
|
437
|
-
});
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
function resolveServiceRegistrations(scope) {
|
|
441
|
-
return resolveTaggedEntries(scope, "jskit.runtime.services.registrations")
|
|
442
|
-
.map((entry) => normalizeObject(entry))
|
|
443
|
-
.filter((entry) => Object.keys(entry).length > 0)
|
|
444
|
-
.sort((left, right) => String(left.serviceToken || "").localeCompare(String(right.serviceToken || "")));
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
function installServiceRegistrationApi(app) {
|
|
448
|
-
assertTaggableApp(app, {
|
|
449
|
-
context: "installServiceRegistrationApi"
|
|
450
|
-
});
|
|
451
|
-
if (typeof app.service === "function") {
|
|
452
|
-
return;
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
const registerService = function registerService(serviceToken, factory, metadata = {}) {
|
|
456
|
-
const registration = normalizeServiceRegistration({
|
|
457
|
-
serviceToken,
|
|
458
|
-
factory,
|
|
459
|
-
metadata
|
|
460
|
-
});
|
|
461
|
-
|
|
462
|
-
this.singleton(registration.serviceToken, (scope) => materializeServiceRegistration(scope, registration));
|
|
463
|
-
|
|
464
|
-
const registrationToken = createServiceRegistrationToken();
|
|
465
|
-
registerServiceRegistration(this, registrationToken, () =>
|
|
466
|
-
Object.freeze({
|
|
467
|
-
serviceToken: registration.serviceToken,
|
|
468
|
-
metadata: registration.metadata
|
|
469
|
-
})
|
|
470
|
-
);
|
|
471
|
-
|
|
472
|
-
return this;
|
|
473
|
-
};
|
|
474
|
-
|
|
475
|
-
Object.defineProperty(app, "service", {
|
|
476
|
-
configurable: true,
|
|
477
|
-
writable: true,
|
|
478
|
-
value: registerService
|
|
479
|
-
});
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
export {
|
|
483
|
-
normalizeServiceRegistration,
|
|
484
|
-
materializeServiceRegistration,
|
|
485
|
-
registerServiceRegistration,
|
|
486
|
-
resolveServiceRegistrations,
|
|
487
|
-
installServiceRegistrationApi
|
|
488
|
-
};
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import * as apiRouteRegistration from "./apiRouteRegistration.js";
|
|
2
|
-
import * as bootstrapContributors from "../registries/bootstrapPayloadContributorRegistry.js";
|
|
3
|
-
import * as bootstrapRoutes from "./bootBootstrapRoutes.js";
|
|
4
|
-
import * as canonicalJson from "./canonicalJson.js";
|
|
5
|
-
import * as composition from "./composition.js";
|
|
6
|
-
import * as domainEvents from "../registries/domainEventListenerRegistry.js";
|
|
7
|
-
import * as errors from "./errors.js";
|
|
8
|
-
import * as fastifyBootstrap from "./fastifyBootstrap.js";
|
|
9
|
-
import * as integers from "./integers.js";
|
|
10
|
-
import * as pagination from "./pagination.js";
|
|
11
|
-
import * as realtimeNormalization from "./realtimeNormalization.js";
|
|
12
|
-
import * as requestUrl from "./requestUrl.js";
|
|
13
|
-
import * as routeUtils from "./routeUtils.js";
|
|
14
|
-
import * as runtimeAssembly from "./runtimeAssembly.js";
|
|
15
|
-
import * as runtimeKernel from "./runtimeKernel.js";
|
|
16
|
-
import * as serviceAuthorization from "./serviceAuthorization.js";
|
|
17
|
-
import * as serviceRegistration from "../registries/serviceRegistrationRegistry.js";
|
|
18
|
-
import * as entityChangeEvents from "./entityChangeEvents.js";
|
|
19
|
-
import * as securityAudit from "./securityAudit.js";
|
|
20
|
-
|
|
21
|
-
const SERVER_RUNTIME_CORE_API = Object.freeze({
|
|
22
|
-
apiRouteRegistration: Object.freeze({ ...apiRouteRegistration }),
|
|
23
|
-
bootstrapContributors: Object.freeze({ ...bootstrapContributors }),
|
|
24
|
-
bootstrapRoutes: Object.freeze({ ...bootstrapRoutes }),
|
|
25
|
-
canonicalJson: Object.freeze({ ...canonicalJson }),
|
|
26
|
-
composition: Object.freeze({ ...composition }),
|
|
27
|
-
domainEvents: Object.freeze({ ...domainEvents }),
|
|
28
|
-
errors: Object.freeze({ ...errors }),
|
|
29
|
-
fastifyBootstrap: Object.freeze({ ...fastifyBootstrap }),
|
|
30
|
-
integers: Object.freeze({ ...integers }),
|
|
31
|
-
pagination: Object.freeze({ ...pagination }),
|
|
32
|
-
realtimeNormalization: Object.freeze({ ...realtimeNormalization }),
|
|
33
|
-
requestUrl: Object.freeze({ ...requestUrl }),
|
|
34
|
-
routeUtils: Object.freeze({ ...routeUtils }),
|
|
35
|
-
runtimeAssembly: Object.freeze({ ...runtimeAssembly }),
|
|
36
|
-
runtimeKernel: Object.freeze({ ...runtimeKernel }),
|
|
37
|
-
serviceAuthorization: Object.freeze({ ...serviceAuthorization }),
|
|
38
|
-
serviceRegistration: Object.freeze({ ...serviceRegistration }),
|
|
39
|
-
entityChangeEvents: Object.freeze({ ...entityChangeEvents }),
|
|
40
|
-
securityAudit: Object.freeze({ ...securityAudit })
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
class ServerRuntimeCoreServiceProvider {
|
|
44
|
-
static id = "runtime.server";
|
|
45
|
-
|
|
46
|
-
register(app) {
|
|
47
|
-
if (!app || typeof app.singleton !== "function" || typeof app.has !== "function") {
|
|
48
|
-
throw new Error("ServerRuntimeCoreServiceProvider requires application singleton()/has().");
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
serviceRegistration.installServiceRegistrationApi(app);
|
|
52
|
-
|
|
53
|
-
app.singleton("runtime.server", () => SERVER_RUNTIME_CORE_API);
|
|
54
|
-
app.singleton("domainEvents", (scope) => domainEvents.createDomainEvents(scope));
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
boot(app) {
|
|
58
|
-
if (app.has("jskit.http.router")) {
|
|
59
|
-
bootstrapRoutes.bootBootstrapRoutes(app);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export { ServerRuntimeCoreServiceProvider };
|