@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,97 @@
1
+ import { normalizeRequestMeta } from "./requestMeta.js";
2
+ import { normalizeLowerText, normalizeText } from "./textNormalization.js";
3
+
4
+ function normalizePermissions(value) {
5
+ const source = Array.isArray(value) ? value : [];
6
+ return Array.from(new Set(source.map((entry) => normalizeText(entry)).filter(Boolean)));
7
+ }
8
+
9
+ function copyRecord(value) {
10
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
11
+ return null;
12
+ }
13
+
14
+ return {
15
+ ...value
16
+ };
17
+ }
18
+
19
+ function normalizeActor(actor) {
20
+ const source = copyRecord(actor);
21
+ if (!source) {
22
+ return null;
23
+ }
24
+
25
+ if (!Object.prototype.hasOwnProperty.call(source, "id")) {
26
+ source.id = null;
27
+ } else if (source.id == null) {
28
+ source.id = null;
29
+ }
30
+
31
+ return source;
32
+ }
33
+
34
+ function normalizeMembership(membership) {
35
+ return copyRecord(membership);
36
+ }
37
+
38
+ function normalizeTimeMeta(timeMeta) {
39
+ const source = timeMeta && typeof timeMeta === "object" ? timeMeta : {};
40
+ const nowValue = source.now instanceof Date ? source.now : new Date();
41
+
42
+ return {
43
+ now: nowValue,
44
+ timezone: normalizeText(source.timezone),
45
+ locale: normalizeText(source.locale)
46
+ };
47
+ }
48
+
49
+ const RESERVED_CONTEXT_KEYS = new Set([
50
+ "actor",
51
+ "membership",
52
+ "permissions",
53
+ "surface",
54
+ "channel",
55
+ "requestMeta",
56
+ "timeMeta"
57
+ ]);
58
+
59
+ function copyPassthroughContextEntries(source = {}) {
60
+ const passthrough = {};
61
+ for (const [key, value] of Object.entries(source)) {
62
+ if (RESERVED_CONTEXT_KEYS.has(key)) {
63
+ continue;
64
+ }
65
+ passthrough[key] = value;
66
+ }
67
+ return passthrough;
68
+ }
69
+
70
+ function normalizeExecutionContext(context = {}) {
71
+ const source = context && typeof context === "object" ? context : {};
72
+ const passthrough = copyPassthroughContextEntries(source);
73
+
74
+ return Object.freeze({
75
+ ...passthrough,
76
+ actor: normalizeActor(source.actor),
77
+ membership: normalizeMembership(source.membership),
78
+ permissions: normalizePermissions(source.permissions),
79
+ surface: normalizeLowerText(source.surface),
80
+ channel: normalizeLowerText(source.channel) || "internal",
81
+ requestMeta: normalizeRequestMeta(source.requestMeta),
82
+ timeMeta: normalizeTimeMeta(source.timeMeta)
83
+ });
84
+ }
85
+
86
+ const __testables = {
87
+ normalizeText,
88
+ normalizeLowerText,
89
+ normalizePermissions,
90
+ copyRecord,
91
+ normalizeActor,
92
+ normalizeMembership,
93
+ normalizeRequestMeta,
94
+ normalizeTimeMeta
95
+ };
96
+
97
+ export { normalizeExecutionContext, __testables };
@@ -0,0 +1,66 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import { normalizeExecutionContext } from "./executionContext.js";
5
+
6
+ test("normalizeExecutionContext preserves non-core context fields", () => {
7
+ const context = normalizeExecutionContext({
8
+ tenant: {
9
+ id: 7,
10
+ slug: "team-alpha",
11
+ ownerUserId: 42
12
+ }
13
+ });
14
+
15
+ assert.equal(context.tenant.ownerUserId, 42);
16
+ });
17
+
18
+ test("normalizeExecutionContext keeps custom requestMeta fields", () => {
19
+ const resolvedWorkspaceContext = {
20
+ workspace: {
21
+ id: 7,
22
+ ownerUserId: 42
23
+ }
24
+ };
25
+
26
+ const context = normalizeExecutionContext({
27
+ requestMeta: {
28
+ resolvedWorkspaceContext
29
+ }
30
+ });
31
+
32
+ assert.deepEqual(context.requestMeta.resolvedWorkspaceContext, resolvedWorkspaceContext);
33
+ });
34
+
35
+ test("normalizeExecutionContext leaves surface empty when missing", () => {
36
+ const context = normalizeExecutionContext({});
37
+ assert.equal(context.surface, "");
38
+ });
39
+
40
+ test("normalizeExecutionContext keeps actor payload generic", () => {
41
+ const context = normalizeExecutionContext({
42
+ actor: {
43
+ id: "user_1",
44
+ email: "UPPER@EXAMPLE.COM",
45
+ roleId: "OWNER",
46
+ customFlag: true
47
+ },
48
+ membership: {
49
+ roleId: "OWNER",
50
+ status: "ACTIVE",
51
+ extra: "x"
52
+ }
53
+ });
54
+
55
+ assert.deepEqual(context.actor, {
56
+ id: "user_1",
57
+ email: "UPPER@EXAMPLE.COM",
58
+ roleId: "OWNER",
59
+ customFlag: true
60
+ });
61
+ assert.deepEqual(context.membership, {
62
+ roleId: "OWNER",
63
+ status: "ACTIVE",
64
+ extra: "x"
65
+ });
66
+ });
@@ -0,0 +1,62 @@
1
+ import { createActionRuntimeError } from "./actionDefinitions.js";
2
+ import { normalizeText } from "./textNormalization.js";
3
+
4
+ function resolveRequestIdempotencyKey(context) {
5
+ return normalizeText(context?.requestMeta?.idempotencyKey || context?.requestMeta?.commandId || "");
6
+ }
7
+
8
+ function resolveActionIdempotencyKey(definition, context) {
9
+ const policy = normalizeText(definition?.idempotency || "none").toLowerCase();
10
+ if (policy === "none") {
11
+ return "";
12
+ }
13
+
14
+ return resolveRequestIdempotencyKey(context);
15
+ }
16
+
17
+ function createNoopIdempotencyAdapter() {
18
+ return {
19
+ async claimOrReplay() {
20
+ return {
21
+ type: "proceed",
22
+ idempotencyReplay: false,
23
+ claim: null,
24
+ replayResult: null
25
+ };
26
+ },
27
+ async markSucceeded() {},
28
+ async markFailed() {}
29
+ };
30
+ }
31
+
32
+ function ensureIdempotencyKeyIfRequired(definition, context, key) {
33
+ const policy = normalizeText(definition?.idempotency || "none").toLowerCase();
34
+ if (policy !== "required") {
35
+ return;
36
+ }
37
+
38
+ if (normalizeText(key)) {
39
+ return;
40
+ }
41
+
42
+ throw createActionRuntimeError(400, "Validation failed.", {
43
+ code: "ACTION_IDEMPOTENCY_KEY_REQUIRED",
44
+ details: {
45
+ fieldErrors: {
46
+ idempotencyKey: "Idempotency key is required for this action."
47
+ }
48
+ }
49
+ });
50
+ }
51
+
52
+ const __testables = {
53
+ normalizeText,
54
+ resolveRequestIdempotencyKey
55
+ };
56
+
57
+ export {
58
+ resolveActionIdempotencyKey,
59
+ ensureIdempotencyKeyIfRequired,
60
+ createNoopIdempotencyAdapter,
61
+ __testables
62
+ };
@@ -0,0 +1,2 @@
1
+ export { normalizeActionDefinition } from "./actionDefinitions.js";
2
+ export { withActionDefaults } from "./withActionDefaults.js";
@@ -0,0 +1,10 @@
1
+ function createNoopObservabilityAdapter() {
2
+ return {
3
+ recordExecutionStart() {},
4
+ recordExecutionFinish() {},
5
+ recordValidationFailure() {},
6
+ recordIdempotentReplay() {}
7
+ };
8
+ }
9
+
10
+ export { createNoopObservabilityAdapter };
@@ -0,0 +1,287 @@
1
+ import {
2
+ ensureActionChannelAllowed,
3
+ ensureActionSurfaceAllowed,
4
+ ensureActionPermissionAllowed,
5
+ normalizeActionInput,
6
+ normalizeActionOutput
7
+ } from "./policies.js";
8
+ import {
9
+ createNoopIdempotencyAdapter,
10
+ resolveActionIdempotencyKey,
11
+ ensureIdempotencyKeyIfRequired
12
+ } from "./idempotency.js";
13
+ import { createNoopAuditAdapter } from "./audit.js";
14
+ import { createNoopObservabilityAdapter } from "./observability.js";
15
+ import { normalizeExecutionContext } from "./executionContext.js";
16
+
17
+ function normalizeOutcomeErrorCode(error) {
18
+ return String(error?.code || "ACTION_EXECUTION_FAILED").trim();
19
+ }
20
+
21
+ function buildActionLogPayload({ definition, context, outcome, durationMs, errorCode, idempotencyReplay }) {
22
+ return {
23
+ action: definition?.id,
24
+ version: definition?.version,
25
+ channel: context?.channel,
26
+ surface: context?.surface,
27
+ requestId: context?.requestMeta?.requestId || null,
28
+ durationMs,
29
+ outcome,
30
+ errorCode: errorCode || null,
31
+ idempotencyReplay: Boolean(idempotencyReplay)
32
+ };
33
+ }
34
+
35
+ async function emitAuditEvent(adapter, payload) {
36
+ if (!adapter || typeof adapter.emitExecution !== "function") {
37
+ return;
38
+ }
39
+
40
+ await adapter.emitExecution(payload);
41
+ }
42
+
43
+ async function executeIdempotencyClaim(adapter, payload) {
44
+ if (!adapter || typeof adapter.claimOrReplay !== "function") {
45
+ return {
46
+ type: "proceed",
47
+ idempotencyReplay: false,
48
+ claim: null,
49
+ replayResult: null
50
+ };
51
+ }
52
+
53
+ const result = await adapter.claimOrReplay(payload);
54
+ if (!result || typeof result !== "object") {
55
+ return {
56
+ type: "proceed",
57
+ idempotencyReplay: false,
58
+ claim: null,
59
+ replayResult: null
60
+ };
61
+ }
62
+
63
+ return {
64
+ type: String(result.type || "proceed"),
65
+ idempotencyReplay: result.idempotencyReplay === true,
66
+ claim: result.claim || null,
67
+ replayResult: Object.hasOwn(result, "replayResult") ? result.replayResult : null
68
+ };
69
+ }
70
+
71
+ async function executeActionPipeline({
72
+ definition,
73
+ input,
74
+ context,
75
+ deps = {},
76
+ idempotencyAdapter,
77
+ auditAdapter,
78
+ observabilityAdapter,
79
+ logger = console
80
+ } = {}) {
81
+ const normalizedContext = normalizeExecutionContext(context);
82
+ const normalizedIdempotencyAdapter = idempotencyAdapter || createNoopIdempotencyAdapter();
83
+ const normalizedAuditAdapter = auditAdapter || createNoopAuditAdapter();
84
+ const normalizedObservabilityAdapter = observabilityAdapter || createNoopObservabilityAdapter();
85
+ const startedAt = Date.now();
86
+
87
+ if (typeof normalizedObservabilityAdapter.recordExecutionStart === "function") {
88
+ normalizedObservabilityAdapter.recordExecutionStart({
89
+ definition,
90
+ context: normalizedContext
91
+ });
92
+ }
93
+
94
+ let idempotencyKey = "";
95
+ let idempotencyClaim = null;
96
+
97
+ try {
98
+ ensureActionChannelAllowed(definition, normalizedContext);
99
+ ensureActionSurfaceAllowed(definition, normalizedContext);
100
+ ensureActionPermissionAllowed(definition, normalizedContext);
101
+
102
+ const normalizedInput = await normalizeActionInput(definition, input, normalizedContext);
103
+
104
+ idempotencyKey = resolveActionIdempotencyKey(definition, normalizedContext);
105
+ ensureIdempotencyKeyIfRequired(definition, normalizedContext, idempotencyKey);
106
+
107
+ const idempotencyPolicy = String(definition?.idempotency || "none").trim().toLowerCase();
108
+ if (idempotencyPolicy !== "none") {
109
+ const claimResult = await executeIdempotencyClaim(normalizedIdempotencyAdapter, {
110
+ definition,
111
+ context: normalizedContext,
112
+ input: normalizedInput,
113
+ idempotencyKey
114
+ });
115
+
116
+ idempotencyClaim = claimResult.claim;
117
+ if (claimResult.type === "replay") {
118
+ if (typeof normalizedObservabilityAdapter.recordIdempotentReplay === "function") {
119
+ normalizedObservabilityAdapter.recordIdempotentReplay({
120
+ definition,
121
+ context: normalizedContext
122
+ });
123
+ }
124
+
125
+ const replayDurationMs = Date.now() - startedAt;
126
+ if (typeof normalizedObservabilityAdapter.recordExecutionFinish === "function") {
127
+ normalizedObservabilityAdapter.recordExecutionFinish({
128
+ definition,
129
+ context: normalizedContext,
130
+ outcome: "replay",
131
+ durationMs: replayDurationMs,
132
+ idempotencyReplay: true
133
+ });
134
+ }
135
+
136
+ await emitAuditEvent(normalizedAuditAdapter, {
137
+ definition,
138
+ context: normalizedContext,
139
+ outcome: "success",
140
+ result: claimResult.replayResult,
141
+ error: null,
142
+ durationMs: replayDurationMs,
143
+ idempotencyReplay: true
144
+ });
145
+
146
+ if (logger && typeof logger.debug === "function") {
147
+ logger.debug(
148
+ buildActionLogPayload({
149
+ definition,
150
+ context: normalizedContext,
151
+ outcome: "replay",
152
+ durationMs: replayDurationMs,
153
+ errorCode: null,
154
+ idempotencyReplay: true
155
+ }),
156
+ "action.execution"
157
+ );
158
+ }
159
+
160
+ return {
161
+ result: claimResult.replayResult,
162
+ idempotencyReplay: true
163
+ };
164
+ }
165
+ }
166
+
167
+ const executionResult = await definition.execute(normalizedInput, normalizedContext, deps);
168
+ const normalizedResult = await normalizeActionOutput(definition, executionResult, normalizedContext);
169
+
170
+ if (idempotencyClaim && typeof normalizedIdempotencyAdapter.markSucceeded === "function") {
171
+ await normalizedIdempotencyAdapter.markSucceeded({
172
+ definition,
173
+ context: normalizedContext,
174
+ input: normalizedInput,
175
+ result: normalizedResult,
176
+ claim: idempotencyClaim,
177
+ idempotencyKey
178
+ });
179
+ }
180
+
181
+ const durationMs = Date.now() - startedAt;
182
+ if (typeof normalizedObservabilityAdapter.recordExecutionFinish === "function") {
183
+ normalizedObservabilityAdapter.recordExecutionFinish({
184
+ definition,
185
+ context: normalizedContext,
186
+ outcome: "success",
187
+ durationMs,
188
+ idempotencyReplay: false
189
+ });
190
+ }
191
+
192
+ await emitAuditEvent(normalizedAuditAdapter, {
193
+ definition,
194
+ context: normalizedContext,
195
+ outcome: "success",
196
+ result: normalizedResult,
197
+ error: null,
198
+ durationMs,
199
+ idempotencyReplay: false
200
+ });
201
+
202
+ if (logger && typeof logger.debug === "function") {
203
+ logger.debug(
204
+ buildActionLogPayload({
205
+ definition,
206
+ context: normalizedContext,
207
+ outcome: "success",
208
+ durationMs,
209
+ errorCode: null,
210
+ idempotencyReplay: false
211
+ }),
212
+ "action.execution"
213
+ );
214
+ }
215
+
216
+ return {
217
+ result: normalizedResult,
218
+ idempotencyReplay: false
219
+ };
220
+ } catch (error) {
221
+ const executionError = error;
222
+
223
+ if (executionError?.code === "ACTION_VALIDATION_FAILED") {
224
+ if (typeof normalizedObservabilityAdapter.recordValidationFailure === "function") {
225
+ normalizedObservabilityAdapter.recordValidationFailure({
226
+ definition,
227
+ context: normalizedContext,
228
+ error: executionError
229
+ });
230
+ }
231
+ }
232
+
233
+ if (idempotencyClaim && typeof normalizedIdempotencyAdapter.markFailed === "function") {
234
+ await normalizedIdempotencyAdapter.markFailed({
235
+ definition,
236
+ context: normalizedContext,
237
+ input,
238
+ error: executionError,
239
+ claim: idempotencyClaim,
240
+ idempotencyKey
241
+ });
242
+ }
243
+
244
+ const durationMs = Date.now() - startedAt;
245
+ const errorCode = normalizeOutcomeErrorCode(executionError);
246
+
247
+ if (typeof normalizedObservabilityAdapter.recordExecutionFinish === "function") {
248
+ normalizedObservabilityAdapter.recordExecutionFinish({
249
+ definition,
250
+ context: normalizedContext,
251
+ outcome: "failure",
252
+ durationMs,
253
+ error: executionError,
254
+ errorCode,
255
+ idempotencyReplay: false
256
+ });
257
+ }
258
+
259
+ await emitAuditEvent(normalizedAuditAdapter, {
260
+ definition,
261
+ context: normalizedContext,
262
+ outcome: "failure",
263
+ result: null,
264
+ error: executionError,
265
+ durationMs,
266
+ idempotencyReplay: false
267
+ });
268
+
269
+ if (logger && typeof logger.debug === "function") {
270
+ logger.debug(
271
+ buildActionLogPayload({
272
+ definition,
273
+ context: normalizedContext,
274
+ outcome: "failure",
275
+ durationMs,
276
+ errorCode,
277
+ idempotencyReplay: false
278
+ }),
279
+ "action.execution"
280
+ );
281
+ }
282
+
283
+ throw executionError;
284
+ }
285
+ }
286
+
287
+ export { executeActionPipeline };