@fabricorg/platform 0.1.1 → 0.2.0
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/dist/actions/index.cjs +48 -0
- package/dist/actions/index.cjs.map +1 -0
- package/dist/actions/index.d.cts +116 -0
- package/dist/actions/index.js +1 -57
- package/dist/actions/index.js.map +1 -1
- package/dist/adapters/index.cjs +28 -0
- package/dist/adapters/index.cjs.map +1 -0
- package/dist/adapters/index.d.cts +83 -0
- package/dist/adapters/index.js +1 -99
- package/dist/adapters/index.js.map +1 -1
- package/dist/chunk-3A6FU3KV.js +67 -0
- package/dist/chunk-3A6FU3KV.js.map +1 -0
- package/dist/chunk-3F6FKQZA.js +89 -0
- package/dist/chunk-3F6FKQZA.js.map +1 -0
- package/dist/chunk-3ZW5USXF.js +101 -0
- package/dist/chunk-3ZW5USXF.js.map +1 -0
- package/dist/chunk-4YQOGV7H.js +55 -0
- package/dist/chunk-4YQOGV7H.js.map +1 -0
- package/dist/chunk-6FMW4NF6.js +59 -0
- package/dist/chunk-6FMW4NF6.js.map +1 -0
- package/dist/chunk-7KJHGAZY.cjs +99 -0
- package/dist/chunk-7KJHGAZY.cjs.map +1 -0
- package/dist/chunk-GB2PSZ33.cjs +74 -0
- package/dist/chunk-GB2PSZ33.cjs.map +1 -0
- package/dist/chunk-I35B576X.js +30 -0
- package/dist/chunk-I35B576X.js.map +1 -0
- package/dist/chunk-IFMZPLM4.js +27 -0
- package/dist/chunk-IFMZPLM4.js.map +1 -0
- package/dist/chunk-JAXDEK5Z.cjs +63 -0
- package/dist/chunk-JAXDEK5Z.cjs.map +1 -0
- package/dist/chunk-JSTGOT6P.js +181 -0
- package/dist/chunk-JSTGOT6P.js.map +1 -0
- package/dist/chunk-KOC4A7A4.js +544 -0
- package/dist/chunk-KOC4A7A4.js.map +1 -0
- package/dist/chunk-KQEBNUZI.cjs +70 -0
- package/dist/chunk-KQEBNUZI.cjs.map +1 -0
- package/dist/chunk-LHCSVEFT.cjs +8 -0
- package/dist/chunk-LHCSVEFT.cjs.map +1 -0
- package/dist/chunk-LYX73U3P.cjs +195 -0
- package/dist/chunk-LYX73U3P.cjs.map +1 -0
- package/dist/chunk-LZSE6QGQ.cjs +60 -0
- package/dist/chunk-LZSE6QGQ.cjs.map +1 -0
- package/dist/chunk-N24NLCZ6.js +6 -0
- package/dist/chunk-N24NLCZ6.js.map +1 -0
- package/dist/chunk-NPVIFZQV.cjs +107 -0
- package/dist/chunk-NPVIFZQV.cjs.map +1 -0
- package/dist/chunk-OZLZHISS.js +3 -0
- package/dist/chunk-OZLZHISS.js.map +1 -0
- package/dist/chunk-PG5LYTUX.cjs +4 -0
- package/dist/chunk-PG5LYTUX.cjs.map +1 -0
- package/dist/chunk-PXMKCAQI.js +187 -0
- package/dist/chunk-PXMKCAQI.js.map +1 -0
- package/dist/chunk-Q3WYIWCN.cjs +183 -0
- package/dist/chunk-Q3WYIWCN.cjs.map +1 -0
- package/dist/chunk-QETQGAXJ.cjs +35 -0
- package/dist/chunk-QETQGAXJ.cjs.map +1 -0
- package/dist/chunk-RALMUSOX.js +55 -0
- package/dist/chunk-RALMUSOX.js.map +1 -0
- package/dist/chunk-SDXDHVTV.cjs +555 -0
- package/dist/chunk-SDXDHVTV.cjs.map +1 -0
- package/dist/chunk-YZKUEC2J.cjs +31 -0
- package/dist/chunk-YZKUEC2J.cjs.map +1 -0
- package/dist/displays/index.cjs +24 -0
- package/dist/displays/index.cjs.map +1 -0
- package/dist/displays/index.d.cts +34 -0
- package/dist/displays/index.js +1 -87
- package/dist/displays/index.js.map +1 -1
- package/dist/events/index.cjs +36 -0
- package/dist/events/index.cjs.map +1 -0
- package/dist/events/index.d.cts +35 -0
- package/dist/events/index.js +1 -53
- package/dist/events/index.js.map +1 -1
- package/dist/evidence/index.cjs +6 -0
- package/dist/evidence/index.cjs.map +1 -0
- package/dist/evidence/index.d.cts +43 -0
- package/dist/evidence/index.js +1 -1
- package/dist/ids/index.cjs +24 -0
- package/dist/ids/index.cjs.map +1 -0
- package/dist/ids/index.d.cts +19 -0
- package/dist/ids/index.js +1 -53
- package/dist/ids/index.js.map +1 -1
- package/dist/index.cjs +268 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +13 -0
- package/dist/index.js +13 -1355
- package/dist/index.js.map +1 -1
- package/dist/modules/index.cjs +42 -0
- package/dist/modules/index.cjs.map +1 -0
- package/dist/modules/index.d.cts +73 -0
- package/dist/modules/index.js +7 -373
- package/dist/modules/index.js.map +1 -1
- package/dist/objects/index.cjs +24 -0
- package/dist/objects/index.cjs.map +1 -0
- package/dist/objects/index.d.cts +7 -0
- package/dist/objects/index.js +1 -28
- package/dist/objects/index.js.map +1 -1
- package/dist/policies/index.cjs +48 -0
- package/dist/policies/index.cjs.map +1 -0
- package/dist/policies/index.d.cts +143 -0
- package/dist/policies/index.js +1 -542
- package/dist/policies/index.js.map +1 -1
- package/dist/privacy/index.cjs +12 -0
- package/dist/privacy/index.cjs.map +1 -0
- package/dist/privacy/index.d.cts +77 -0
- package/dist/privacy/index.js +1 -4
- package/dist/privacy/index.js.map +1 -1
- package/dist/projections/index.cjs +12 -0
- package/dist/projections/index.cjs.map +1 -0
- package/dist/projections/index.d.cts +52 -0
- package/dist/projections/index.js +1 -179
- package/dist/projections/index.js.map +1 -1
- package/dist/state-machines/index.cjs +32 -0
- package/dist/state-machines/index.cjs.map +1 -0
- package/dist/state-machines/index.d.cts +60 -0
- package/dist/state-machines/index.js +1 -65
- package/dist/state-machines/index.js.map +1 -1
- package/dist/testing/index.cjs +21 -0
- package/dist/testing/index.cjs.map +1 -0
- package/dist/testing/index.d.cts +19 -0
- package/dist/testing/index.js +2 -56
- package/dist/testing/index.js.map +1 -1
- package/package.json +196 -111
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkKQEBNUZI_cjs = require('../chunk-KQEBNUZI.cjs');
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
Object.defineProperty(exports, "ACTOR_TYPES", {
|
|
8
|
+
enumerable: true,
|
|
9
|
+
get: function () { return chunkKQEBNUZI_cjs.ACTOR_TYPES; }
|
|
10
|
+
});
|
|
11
|
+
Object.defineProperty(exports, "assertActionId", {
|
|
12
|
+
enumerable: true,
|
|
13
|
+
get: function () { return chunkKQEBNUZI_cjs.assertActionId; }
|
|
14
|
+
});
|
|
15
|
+
Object.defineProperty(exports, "assertActorType", {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
get: function () { return chunkKQEBNUZI_cjs.assertActorType; }
|
|
18
|
+
});
|
|
19
|
+
Object.defineProperty(exports, "getRegisteredActionIds", {
|
|
20
|
+
enumerable: true,
|
|
21
|
+
get: function () { return chunkKQEBNUZI_cjs.getRegisteredActionIds; }
|
|
22
|
+
});
|
|
23
|
+
Object.defineProperty(exports, "isActorType", {
|
|
24
|
+
enumerable: true,
|
|
25
|
+
get: function () { return chunkKQEBNUZI_cjs.isActorType; }
|
|
26
|
+
});
|
|
27
|
+
Object.defineProperty(exports, "isRegisteredActionId", {
|
|
28
|
+
enumerable: true,
|
|
29
|
+
get: function () { return chunkKQEBNUZI_cjs.isRegisteredActionId; }
|
|
30
|
+
});
|
|
31
|
+
Object.defineProperty(exports, "registerAction", {
|
|
32
|
+
enumerable: true,
|
|
33
|
+
get: function () { return chunkKQEBNUZI_cjs.registerAction; }
|
|
34
|
+
});
|
|
35
|
+
Object.defineProperty(exports, "registerActionIfMissing", {
|
|
36
|
+
enumerable: true,
|
|
37
|
+
get: function () { return chunkKQEBNUZI_cjs.registerActionIfMissing; }
|
|
38
|
+
});
|
|
39
|
+
Object.defineProperty(exports, "resolveAction", {
|
|
40
|
+
enumerable: true,
|
|
41
|
+
get: function () { return chunkKQEBNUZI_cjs.resolveAction; }
|
|
42
|
+
});
|
|
43
|
+
Object.defineProperty(exports, "resolveRegisteredAction", {
|
|
44
|
+
enumerable: true,
|
|
45
|
+
get: function () { return chunkKQEBNUZI_cjs.resolveRegisteredAction; }
|
|
46
|
+
});
|
|
47
|
+
//# sourceMappingURL=index.cjs.map
|
|
48
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"index.cjs"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
type ActionId = `${string}.${string}`;
|
|
2
|
+
interface SchemaParseIssue {
|
|
3
|
+
path: PropertyKey[];
|
|
4
|
+
message: string;
|
|
5
|
+
}
|
|
6
|
+
type SchemaParseResult<T = unknown> = {
|
|
7
|
+
success: true;
|
|
8
|
+
data: T;
|
|
9
|
+
} | {
|
|
10
|
+
success: false;
|
|
11
|
+
error: {
|
|
12
|
+
issues: SchemaParseIssue[];
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
interface SchemaValidator<T = unknown> {
|
|
16
|
+
parse(input: unknown): T;
|
|
17
|
+
safeParse(input: unknown): SchemaParseResult<T>;
|
|
18
|
+
}
|
|
19
|
+
type ActorType = "natural_person" | "agent" | "system" | "service_account" | "external_system" | "integration";
|
|
20
|
+
declare const ACTOR_TYPES: readonly ActorType[];
|
|
21
|
+
declare function isActorType(value: string): value is ActorType;
|
|
22
|
+
declare function assertActorType(value: string): asserts value is ActorType;
|
|
23
|
+
type ActionStatus = "pending" | "running" | "completed" | "failed" | "blocked_by_policy" | "waiting_for_approval" | "validation_failed";
|
|
24
|
+
interface FabricRuntimeServices {
|
|
25
|
+
readonly [serviceName: string]: unknown;
|
|
26
|
+
}
|
|
27
|
+
interface ActionContext<TDb = unknown> {
|
|
28
|
+
actionInvocationId: string;
|
|
29
|
+
tenantId: string;
|
|
30
|
+
spaceId: string;
|
|
31
|
+
actorId: string;
|
|
32
|
+
actorType: ActorType;
|
|
33
|
+
correlationId: string;
|
|
34
|
+
causationId?: string;
|
|
35
|
+
db: TDb;
|
|
36
|
+
services?: FabricRuntimeServices;
|
|
37
|
+
}
|
|
38
|
+
interface ActionResult {
|
|
39
|
+
success: boolean;
|
|
40
|
+
data?: Record<string, unknown>;
|
|
41
|
+
error?: string;
|
|
42
|
+
}
|
|
43
|
+
type ActionHandler<TDb = unknown> = (ctx: ActionContext<TDb>, params: unknown) => Promise<ActionResult>;
|
|
44
|
+
interface AdapterStepRetryPolicy {
|
|
45
|
+
idempotent: boolean;
|
|
46
|
+
maxAttempts?: number;
|
|
47
|
+
initialDelayMs?: number;
|
|
48
|
+
backoffMultiplier?: number;
|
|
49
|
+
maxDelayMs?: number;
|
|
50
|
+
}
|
|
51
|
+
interface AdapterStep {
|
|
52
|
+
adapterType: string;
|
|
53
|
+
operation: string;
|
|
54
|
+
retryPolicy?: AdapterStepRetryPolicy;
|
|
55
|
+
getInput: (params: unknown, handlerResult: Record<string, unknown>) => Record<string, unknown> | undefined;
|
|
56
|
+
}
|
|
57
|
+
interface ActionStateMachineBinding {
|
|
58
|
+
entityType: string;
|
|
59
|
+
targetState: string | ((params: unknown) => string);
|
|
60
|
+
getEntityId: (params: unknown) => string | undefined;
|
|
61
|
+
}
|
|
62
|
+
interface ActionDefinition<TDb = unknown> {
|
|
63
|
+
actionId: ActionId;
|
|
64
|
+
namespace: string;
|
|
65
|
+
version: number;
|
|
66
|
+
schema: SchemaValidator;
|
|
67
|
+
handler?: ActionHandler<TDb>;
|
|
68
|
+
requiredRoles?: string[];
|
|
69
|
+
requiredPermissions?: string[];
|
|
70
|
+
policies?: `${string}.v${number}`[];
|
|
71
|
+
emitsEvents: string[];
|
|
72
|
+
idempotent: boolean;
|
|
73
|
+
mutatesDomain: boolean;
|
|
74
|
+
stateMachine?: ActionStateMachineBinding;
|
|
75
|
+
adapterSteps?: AdapterStep[];
|
|
76
|
+
/**
|
|
77
|
+
* If "saga", the action is a multi-step orchestration. The worker dispatches
|
|
78
|
+
* to a registered saga implementation instead of running `handler` directly.
|
|
79
|
+
*/
|
|
80
|
+
kind?: "atomic" | "saga";
|
|
81
|
+
}
|
|
82
|
+
interface SagaContext {
|
|
83
|
+
actionInvocationId: string;
|
|
84
|
+
tenantId: string;
|
|
85
|
+
spaceId: string;
|
|
86
|
+
parameters: unknown;
|
|
87
|
+
sleep?: (delayMs: number) => Promise<void>;
|
|
88
|
+
query?: <T = unknown>(model: string, operation: string, args: unknown) => Promise<T>;
|
|
89
|
+
}
|
|
90
|
+
interface SagaChildActionInput {
|
|
91
|
+
suffix: string;
|
|
92
|
+
actionId: string;
|
|
93
|
+
parameters: Record<string, unknown>;
|
|
94
|
+
}
|
|
95
|
+
type SagaRunChild = (input: SagaChildActionInput) => Promise<Record<string, unknown>>;
|
|
96
|
+
interface SagaCompletionEvent {
|
|
97
|
+
eventType: string;
|
|
98
|
+
subjectType: string;
|
|
99
|
+
subjectId: string;
|
|
100
|
+
payload: unknown;
|
|
101
|
+
eventSchemaVersion?: number;
|
|
102
|
+
}
|
|
103
|
+
interface SagaCompletion {
|
|
104
|
+
resultData: Record<string, unknown>;
|
|
105
|
+
event?: SagaCompletionEvent;
|
|
106
|
+
}
|
|
107
|
+
type SagaImplementation = (ctx: SagaContext, runChild: SagaRunChild) => Promise<SagaCompletion>;
|
|
108
|
+
declare function assertActionId(actionId: string): asserts actionId is ActionId;
|
|
109
|
+
declare function registerAction(definition: ActionDefinition): void;
|
|
110
|
+
declare function resolveAction(actionId: ActionId): ActionDefinition | undefined;
|
|
111
|
+
declare function isRegisteredActionId(actionId: string): actionId is ActionId;
|
|
112
|
+
declare function resolveRegisteredAction(actionId: string): ActionDefinition | undefined;
|
|
113
|
+
declare function getRegisteredActionIds(): ActionId[];
|
|
114
|
+
declare function registerActionIfMissing(definition: ActionDefinition): void;
|
|
115
|
+
|
|
116
|
+
export { ACTOR_TYPES, type ActionContext, type ActionDefinition, type ActionHandler, type ActionId, type ActionResult, type ActionStateMachineBinding, type ActionStatus, type ActorType, type AdapterStep, type AdapterStepRetryPolicy, type FabricRuntimeServices, type SagaChildActionInput, type SagaCompletion, type SagaCompletionEvent, type SagaContext, type SagaImplementation, type SagaRunChild, type SchemaParseIssue, type SchemaParseResult, type SchemaValidator, assertActionId, assertActorType, getRegisteredActionIds, isActorType, isRegisteredActionId, registerAction, registerActionIfMissing, resolveAction, resolveRegisteredAction };
|
package/dist/actions/index.js
CHANGED
|
@@ -1,59 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
var ACTOR_TYPES = [
|
|
3
|
-
"natural_person",
|
|
4
|
-
"agent",
|
|
5
|
-
"system",
|
|
6
|
-
"service_account",
|
|
7
|
-
"external_system",
|
|
8
|
-
"integration"
|
|
9
|
-
];
|
|
10
|
-
function isActorType(value) {
|
|
11
|
-
return ACTOR_TYPES.includes(value);
|
|
12
|
-
}
|
|
13
|
-
function assertActorType(value) {
|
|
14
|
-
if (!isActorType(value)) {
|
|
15
|
-
throw new Error(`Invalid actorType: ${value}`);
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
var registry = /* @__PURE__ */ new Map();
|
|
19
|
-
function assertActionId(actionId) {
|
|
20
|
-
if (!/^[a-z][a-z0-9-]*\.[a-z][a-z0-9_]*$/.test(actionId)) {
|
|
21
|
-
throw new Error(`Invalid action ID: ${actionId}`);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
function registerAction(definition) {
|
|
25
|
-
assertActionId(definition.actionId);
|
|
26
|
-
if (registry.has(definition.actionId)) {
|
|
27
|
-
throw new Error(`Action "${definition.actionId}" is already registered`);
|
|
28
|
-
}
|
|
29
|
-
if (definition.mutatesDomain && definition.emitsEvents.length === 0) {
|
|
30
|
-
throw new Error(
|
|
31
|
-
`Action "${definition.actionId}" mutates domain but emits no events. Every mutating action must emit at least one AssetEvent.`
|
|
32
|
-
);
|
|
33
|
-
}
|
|
34
|
-
registry.set(definition.actionId, definition);
|
|
35
|
-
}
|
|
36
|
-
function resolveAction(actionId) {
|
|
37
|
-
return registry.get(actionId);
|
|
38
|
-
}
|
|
39
|
-
function isRegisteredActionId(actionId) {
|
|
40
|
-
return registry.has(actionId);
|
|
41
|
-
}
|
|
42
|
-
function resolveRegisteredAction(actionId) {
|
|
43
|
-
if (!isRegisteredActionId(actionId)) {
|
|
44
|
-
return void 0;
|
|
45
|
-
}
|
|
46
|
-
return resolveAction(actionId);
|
|
47
|
-
}
|
|
48
|
-
function getRegisteredActionIds() {
|
|
49
|
-
return Array.from(registry.keys());
|
|
50
|
-
}
|
|
51
|
-
function registerActionIfMissing(definition) {
|
|
52
|
-
if (!registry.has(definition.actionId)) {
|
|
53
|
-
registerAction(definition);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export { ACTOR_TYPES, assertActionId, assertActorType, getRegisteredActionIds, isActorType, isRegisteredActionId, registerAction, registerActionIfMissing, resolveAction, resolveRegisteredAction };
|
|
1
|
+
export { ACTOR_TYPES, assertActionId, assertActorType, getRegisteredActionIds, isActorType, isRegisteredActionId, registerAction, registerActionIfMissing, resolveAction, resolveRegisteredAction } from '../chunk-6FMW4NF6.js';
|
|
58
2
|
//# sourceMappingURL=index.js.map
|
|
59
3
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkNPVIFZQV_cjs = require('../chunk-NPVIFZQV.cjs');
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
Object.defineProperty(exports, "AdapterRegistry", {
|
|
8
|
+
enumerable: true,
|
|
9
|
+
get: function () { return chunkNPVIFZQV_cjs.AdapterRegistry; }
|
|
10
|
+
});
|
|
11
|
+
Object.defineProperty(exports, "MissingAdapterRegistrationError", {
|
|
12
|
+
enumerable: true,
|
|
13
|
+
get: function () { return chunkNPVIFZQV_cjs.MissingAdapterRegistrationError; }
|
|
14
|
+
});
|
|
15
|
+
Object.defineProperty(exports, "executeWithAdapterRetry", {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
get: function () { return chunkNPVIFZQV_cjs.executeWithAdapterRetry; }
|
|
18
|
+
});
|
|
19
|
+
Object.defineProperty(exports, "getRetryDelayMs", {
|
|
20
|
+
enumerable: true,
|
|
21
|
+
get: function () { return chunkNPVIFZQV_cjs.getRetryDelayMs; }
|
|
22
|
+
});
|
|
23
|
+
Object.defineProperty(exports, "resolveAdapterRetryPolicy", {
|
|
24
|
+
enumerable: true,
|
|
25
|
+
get: function () { return chunkNPVIFZQV_cjs.resolveAdapterRetryPolicy; }
|
|
26
|
+
});
|
|
27
|
+
//# sourceMappingURL=index.cjs.map
|
|
28
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"index.cjs"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
interface AdapterExecutionContext {
|
|
2
|
+
tenantId: string;
|
|
3
|
+
spaceId: string;
|
|
4
|
+
actionInvocationId: string;
|
|
5
|
+
adapterInvocationId: string;
|
|
6
|
+
correlationId: string;
|
|
7
|
+
causationId?: string;
|
|
8
|
+
attempt: number;
|
|
9
|
+
maxAttempts: number;
|
|
10
|
+
}
|
|
11
|
+
interface AdapterExecutionResult {
|
|
12
|
+
success: boolean;
|
|
13
|
+
correlationId?: string;
|
|
14
|
+
error?: string;
|
|
15
|
+
}
|
|
16
|
+
type AdapterExecutor = (input: Record<string, unknown>, context: AdapterExecutionContext) => Promise<AdapterExecutionResult>;
|
|
17
|
+
interface AdapterRetryPolicy {
|
|
18
|
+
/** External calls must explicitly opt into retry by being idempotent. */
|
|
19
|
+
idempotent: boolean;
|
|
20
|
+
/** Total attempts including the first call. Non-idempotent policies are forced to 1. */
|
|
21
|
+
maxAttempts?: number;
|
|
22
|
+
/** First backoff delay in milliseconds. */
|
|
23
|
+
initialDelayMs?: number;
|
|
24
|
+
/** Exponential multiplier applied after each failed attempt. */
|
|
25
|
+
backoffMultiplier?: number;
|
|
26
|
+
/** Upper bound for any single retry delay in milliseconds. */
|
|
27
|
+
maxDelayMs?: number;
|
|
28
|
+
}
|
|
29
|
+
interface ResolvedAdapterRetryPolicy {
|
|
30
|
+
idempotent: boolean;
|
|
31
|
+
maxAttempts: number;
|
|
32
|
+
initialDelayMs: number;
|
|
33
|
+
backoffMultiplier: number;
|
|
34
|
+
maxDelayMs: number;
|
|
35
|
+
}
|
|
36
|
+
interface AdapterCircuitBreakerConfig {
|
|
37
|
+
mode: "monitor" | "enforce";
|
|
38
|
+
failureThreshold: number;
|
|
39
|
+
windowMs: number;
|
|
40
|
+
cooldownMs: number;
|
|
41
|
+
minimumSamples?: number;
|
|
42
|
+
}
|
|
43
|
+
interface AdapterImplementation {
|
|
44
|
+
adapterType: string;
|
|
45
|
+
operation: string;
|
|
46
|
+
vendor: string;
|
|
47
|
+
/** Whether this adapter operation can be safely retried by default. */
|
|
48
|
+
idempotent: boolean;
|
|
49
|
+
retryPolicy?: AdapterRetryPolicy;
|
|
50
|
+
/** Defaults to monitor-only; enforce mode must be explicitly configured. */
|
|
51
|
+
circuitBreaker?: AdapterCircuitBreakerConfig;
|
|
52
|
+
execute: AdapterExecutor;
|
|
53
|
+
}
|
|
54
|
+
interface AdapterAttempt<T> {
|
|
55
|
+
attempt: number;
|
|
56
|
+
result?: T;
|
|
57
|
+
error?: string;
|
|
58
|
+
willRetry: boolean;
|
|
59
|
+
nextDelayMs?: number;
|
|
60
|
+
}
|
|
61
|
+
interface ExecuteWithRetryOptions<T> {
|
|
62
|
+
policy?: AdapterRetryPolicy;
|
|
63
|
+
defaultIdempotent: boolean;
|
|
64
|
+
execute: (attempt: number, maxAttempts: number) => Promise<T>;
|
|
65
|
+
isSuccessful: (result: T) => boolean;
|
|
66
|
+
getError: (result: T) => string | undefined;
|
|
67
|
+
sleep?: (delayMs: number) => Promise<void>;
|
|
68
|
+
onAttempt?: (attempt: AdapterAttempt<T>) => Promise<void> | void;
|
|
69
|
+
}
|
|
70
|
+
declare class MissingAdapterRegistrationError extends Error {
|
|
71
|
+
constructor(adapterType: string, operation: string);
|
|
72
|
+
}
|
|
73
|
+
declare class AdapterRegistry {
|
|
74
|
+
private readonly adapters;
|
|
75
|
+
register(adapter: AdapterImplementation): void;
|
|
76
|
+
resolve(adapterType: string, operation: string): AdapterImplementation | undefined;
|
|
77
|
+
require(adapterType: string, operation: string): AdapterImplementation;
|
|
78
|
+
}
|
|
79
|
+
declare function resolveAdapterRetryPolicy(policy: AdapterRetryPolicy | undefined, defaultIdempotent: boolean): ResolvedAdapterRetryPolicy;
|
|
80
|
+
declare function getRetryDelayMs(attempt: number, policy: ResolvedAdapterRetryPolicy): number;
|
|
81
|
+
declare function executeWithAdapterRetry<T>(options: ExecuteWithRetryOptions<T>): Promise<T>;
|
|
82
|
+
|
|
83
|
+
export { type AdapterAttempt, type AdapterCircuitBreakerConfig, type AdapterExecutionContext, type AdapterExecutionResult, type AdapterExecutor, type AdapterImplementation, AdapterRegistry, type AdapterRetryPolicy, type ExecuteWithRetryOptions, MissingAdapterRegistrationError, type ResolvedAdapterRetryPolicy, executeWithAdapterRetry, getRetryDelayMs, resolveAdapterRetryPolicy };
|
package/dist/adapters/index.js
CHANGED
|
@@ -1,101 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
function adapterKey(adapterType, operation) {
|
|
3
|
-
return `${adapterType}:${operation}`;
|
|
4
|
-
}
|
|
5
|
-
var MissingAdapterRegistrationError = class extends Error {
|
|
6
|
-
constructor(adapterType, operation) {
|
|
7
|
-
super(`No adapter registered for adapterType "${adapterType}" and operation "${operation}"`);
|
|
8
|
-
this.name = "MissingAdapterRegistrationError";
|
|
9
|
-
}
|
|
10
|
-
};
|
|
11
|
-
var AdapterRegistry = class {
|
|
12
|
-
adapters = /* @__PURE__ */ new Map();
|
|
13
|
-
register(adapter) {
|
|
14
|
-
const key = adapterKey(adapter.adapterType, adapter.operation);
|
|
15
|
-
if (this.adapters.has(key)) {
|
|
16
|
-
throw new Error(
|
|
17
|
-
`Adapter already registered for adapterType "${adapter.adapterType}" and operation "${adapter.operation}"`
|
|
18
|
-
);
|
|
19
|
-
}
|
|
20
|
-
this.adapters.set(key, adapter);
|
|
21
|
-
}
|
|
22
|
-
resolve(adapterType, operation) {
|
|
23
|
-
return this.adapters.get(adapterKey(adapterType, operation));
|
|
24
|
-
}
|
|
25
|
-
require(adapterType, operation) {
|
|
26
|
-
const adapter = this.resolve(adapterType, operation);
|
|
27
|
-
if (!adapter) {
|
|
28
|
-
throw new MissingAdapterRegistrationError(adapterType, operation);
|
|
29
|
-
}
|
|
30
|
-
return adapter;
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
var DEFAULT_RETRY_POLICY = {
|
|
34
|
-
maxAttempts: 3,
|
|
35
|
-
initialDelayMs: 100,
|
|
36
|
-
backoffMultiplier: 2,
|
|
37
|
-
maxDelayMs: 1e3
|
|
38
|
-
};
|
|
39
|
-
var NO_RETRY_POLICY = {
|
|
40
|
-
idempotent: false,
|
|
41
|
-
maxAttempts: 1,
|
|
42
|
-
initialDelayMs: 0,
|
|
43
|
-
backoffMultiplier: 1,
|
|
44
|
-
maxDelayMs: 0
|
|
45
|
-
};
|
|
46
|
-
function resolveAdapterRetryPolicy(policy, defaultIdempotent) {
|
|
47
|
-
const idempotent = policy?.idempotent ?? defaultIdempotent;
|
|
48
|
-
if (!idempotent) {
|
|
49
|
-
return NO_RETRY_POLICY;
|
|
50
|
-
}
|
|
51
|
-
return {
|
|
52
|
-
idempotent: true,
|
|
53
|
-
maxAttempts: Math.max(1, Math.min(policy?.maxAttempts ?? DEFAULT_RETRY_POLICY.maxAttempts, 5)),
|
|
54
|
-
initialDelayMs: Math.max(0, policy?.initialDelayMs ?? DEFAULT_RETRY_POLICY.initialDelayMs),
|
|
55
|
-
backoffMultiplier: Math.max(
|
|
56
|
-
1,
|
|
57
|
-
policy?.backoffMultiplier ?? DEFAULT_RETRY_POLICY.backoffMultiplier
|
|
58
|
-
),
|
|
59
|
-
maxDelayMs: Math.max(0, policy?.maxDelayMs ?? DEFAULT_RETRY_POLICY.maxDelayMs)
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
function getRetryDelayMs(attempt, policy) {
|
|
63
|
-
const exponentialDelay = policy.initialDelayMs * policy.backoffMultiplier ** Math.max(0, attempt - 1);
|
|
64
|
-
return Math.min(exponentialDelay, policy.maxDelayMs);
|
|
65
|
-
}
|
|
66
|
-
async function executeWithAdapterRetry(options) {
|
|
67
|
-
const policy = resolveAdapterRetryPolicy(options.policy, options.defaultIdempotent);
|
|
68
|
-
const sleep = options.sleep ?? ((delayMs) => new Promise((resolve) => setTimeout(resolve, delayMs)));
|
|
69
|
-
let lastError;
|
|
70
|
-
for (let attempt = 1; attempt <= policy.maxAttempts; attempt++) {
|
|
71
|
-
try {
|
|
72
|
-
const result = await options.execute(attempt, policy.maxAttempts);
|
|
73
|
-
if (options.isSuccessful(result)) {
|
|
74
|
-
await options.onAttempt?.({ attempt, result, willRetry: false });
|
|
75
|
-
return result;
|
|
76
|
-
}
|
|
77
|
-
lastError = options.getError(result) ?? "Adapter returned unsuccessful result";
|
|
78
|
-
const willRetry = policy.idempotent && attempt < policy.maxAttempts;
|
|
79
|
-
const nextDelayMs = willRetry ? getRetryDelayMs(attempt, policy) : void 0;
|
|
80
|
-
await options.onAttempt?.({ attempt, result, error: lastError, willRetry, nextDelayMs });
|
|
81
|
-
if (!willRetry) {
|
|
82
|
-
return result;
|
|
83
|
-
}
|
|
84
|
-
await sleep(nextDelayMs ?? 0);
|
|
85
|
-
} catch (error) {
|
|
86
|
-
lastError = error instanceof Error ? error.message : String(error);
|
|
87
|
-
const willRetry = policy.idempotent && attempt < policy.maxAttempts;
|
|
88
|
-
const nextDelayMs = willRetry ? getRetryDelayMs(attempt, policy) : void 0;
|
|
89
|
-
await options.onAttempt?.({ attempt, error: lastError, willRetry, nextDelayMs });
|
|
90
|
-
if (!willRetry) {
|
|
91
|
-
throw error;
|
|
92
|
-
}
|
|
93
|
-
await sleep(nextDelayMs ?? 0);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
throw new Error(lastError ?? "Adapter retry exhausted");
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export { AdapterRegistry, MissingAdapterRegistrationError, executeWithAdapterRetry, getRetryDelayMs, resolveAdapterRetryPolicy };
|
|
1
|
+
export { AdapterRegistry, MissingAdapterRegistrationError, executeWithAdapterRetry, getRetryDelayMs, resolveAdapterRetryPolicy } from '../chunk-3ZW5USXF.js';
|
|
100
2
|
//# sourceMappingURL=index.js.map
|
|
101
3
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../adapters/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["export interface AdapterExecutionContext {\n\ttenantId: string;\n\tspaceId: string;\n\tactionInvocationId: string;\n\tadapterInvocationId: string;\n\tcorrelationId: string;\n\tcausationId?: string;\n\tattempt: number;\n\tmaxAttempts: number;\n}\n\nexport interface AdapterExecutionResult {\n\tsuccess: boolean;\n\tcorrelationId?: string;\n\terror?: string;\n}\n\nexport type AdapterExecutor = (\n\tinput: Record<string, unknown>,\n\tcontext: AdapterExecutionContext,\n) => Promise<AdapterExecutionResult>;\n\nexport interface AdapterRetryPolicy {\n\t/** External calls must explicitly opt into retry by being idempotent. */\n\tidempotent: boolean;\n\t/** Total attempts including the first call. Non-idempotent policies are forced to 1. */\n\tmaxAttempts?: number;\n\t/** First backoff delay in milliseconds. */\n\tinitialDelayMs?: number;\n\t/** Exponential multiplier applied after each failed attempt. */\n\tbackoffMultiplier?: number;\n\t/** Upper bound for any single retry delay in milliseconds. */\n\tmaxDelayMs?: number;\n}\n\nexport interface ResolvedAdapterRetryPolicy {\n\tidempotent: boolean;\n\tmaxAttempts: number;\n\tinitialDelayMs: number;\n\tbackoffMultiplier: number;\n\tmaxDelayMs: number;\n}\n\nexport interface AdapterCircuitBreakerConfig {\n\tmode: \"monitor\" | \"enforce\";\n\tfailureThreshold: number;\n\twindowMs: number;\n\tcooldownMs: number;\n\tminimumSamples?: number;\n}\n\nexport interface AdapterImplementation {\n\tadapterType: string;\n\toperation: string;\n\tvendor: string;\n\t/** Whether this adapter operation can be safely retried by default. */\n\tidempotent: boolean;\n\tretryPolicy?: AdapterRetryPolicy;\n\t/** Defaults to monitor-only; enforce mode must be explicitly configured. */\n\tcircuitBreaker?: AdapterCircuitBreakerConfig;\n\texecute: AdapterExecutor;\n}\n\nexport interface AdapterAttempt<T> {\n\tattempt: number;\n\tresult?: T;\n\terror?: string;\n\twillRetry: boolean;\n\tnextDelayMs?: number;\n}\n\nexport interface ExecuteWithRetryOptions<T> {\n\tpolicy?: AdapterRetryPolicy;\n\tdefaultIdempotent: boolean;\n\texecute: (attempt: number, maxAttempts: number) => Promise<T>;\n\tisSuccessful: (result: T) => boolean;\n\tgetError: (result: T) => string | undefined;\n\tsleep?: (delayMs: number) => Promise<void>;\n\tonAttempt?: (attempt: AdapterAttempt<T>) => Promise<void> | void;\n}\n\nfunction adapterKey(adapterType: string, operation: string): string {\n\treturn `${adapterType}:${operation}`;\n}\n\nexport class MissingAdapterRegistrationError extends Error {\n\tconstructor(adapterType: string, operation: string) {\n\t\tsuper(`No adapter registered for adapterType \"${adapterType}\" and operation \"${operation}\"`);\n\t\tthis.name = \"MissingAdapterRegistrationError\";\n\t}\n}\n\nexport class AdapterRegistry {\n\tprivate readonly adapters = new Map<string, AdapterImplementation>();\n\n\tregister(adapter: AdapterImplementation): void {\n\t\tconst key = adapterKey(adapter.adapterType, adapter.operation);\n\t\tif (this.adapters.has(key)) {\n\t\t\tthrow new Error(\n\t\t\t\t`Adapter already registered for adapterType \"${adapter.adapterType}\" and operation \"${adapter.operation}\"`,\n\t\t\t);\n\t\t}\n\n\t\tthis.adapters.set(key, adapter);\n\t}\n\n\tresolve(adapterType: string, operation: string): AdapterImplementation | undefined {\n\t\treturn this.adapters.get(adapterKey(adapterType, operation));\n\t}\n\n\trequire(adapterType: string, operation: string): AdapterImplementation {\n\t\tconst adapter = this.resolve(adapterType, operation);\n\t\tif (!adapter) {\n\t\t\tthrow new MissingAdapterRegistrationError(adapterType, operation);\n\t\t}\n\n\t\treturn adapter;\n\t}\n}\n\nconst DEFAULT_RETRY_POLICY: ResolvedAdapterRetryPolicy = {\n\tidempotent: true,\n\tmaxAttempts: 3,\n\tinitialDelayMs: 100,\n\tbackoffMultiplier: 2,\n\tmaxDelayMs: 1_000,\n};\n\nconst NO_RETRY_POLICY: ResolvedAdapterRetryPolicy = {\n\tidempotent: false,\n\tmaxAttempts: 1,\n\tinitialDelayMs: 0,\n\tbackoffMultiplier: 1,\n\tmaxDelayMs: 0,\n};\n\nexport function resolveAdapterRetryPolicy(\n\tpolicy: AdapterRetryPolicy | undefined,\n\tdefaultIdempotent: boolean,\n): ResolvedAdapterRetryPolicy {\n\tconst idempotent = policy?.idempotent ?? defaultIdempotent;\n\n\tif (!idempotent) {\n\t\treturn NO_RETRY_POLICY;\n\t}\n\n\treturn {\n\t\tidempotent: true,\n\t\tmaxAttempts: Math.max(1, Math.min(policy?.maxAttempts ?? DEFAULT_RETRY_POLICY.maxAttempts, 5)),\n\t\tinitialDelayMs: Math.max(0, policy?.initialDelayMs ?? DEFAULT_RETRY_POLICY.initialDelayMs),\n\t\tbackoffMultiplier: Math.max(\n\t\t\t1,\n\t\t\tpolicy?.backoffMultiplier ?? DEFAULT_RETRY_POLICY.backoffMultiplier,\n\t\t),\n\t\tmaxDelayMs: Math.max(0, policy?.maxDelayMs ?? DEFAULT_RETRY_POLICY.maxDelayMs),\n\t};\n}\n\nexport function getRetryDelayMs(attempt: number, policy: ResolvedAdapterRetryPolicy): number {\n\tconst exponentialDelay =\n\t\tpolicy.initialDelayMs * policy.backoffMultiplier ** Math.max(0, attempt - 1);\n\n\treturn Math.min(exponentialDelay, policy.maxDelayMs);\n}\n\nexport async function executeWithAdapterRetry<T>(options: ExecuteWithRetryOptions<T>): Promise<T> {\n\tconst policy = resolveAdapterRetryPolicy(options.policy, options.defaultIdempotent);\n\tconst sleep =\n\t\toptions.sleep ?? ((delayMs) => new Promise((resolve) => setTimeout(resolve, delayMs)));\n\tlet lastError: string | undefined;\n\n\tfor (let attempt = 1; attempt <= policy.maxAttempts; attempt++) {\n\t\ttry {\n\t\t\tconst result = await options.execute(attempt, policy.maxAttempts);\n\t\t\tif (options.isSuccessful(result)) {\n\t\t\t\tawait options.onAttempt?.({ attempt, result, willRetry: false });\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tlastError = options.getError(result) ?? \"Adapter returned unsuccessful result\";\n\t\t\tconst willRetry = policy.idempotent && attempt < policy.maxAttempts;\n\t\t\tconst nextDelayMs = willRetry ? getRetryDelayMs(attempt, policy) : undefined;\n\t\t\tawait options.onAttempt?.({ attempt, result, error: lastError, willRetry, nextDelayMs });\n\n\t\t\tif (!willRetry) {\n\t\t\t\treturn result;\n\t\t\t}\n\n\t\t\tawait sleep(nextDelayMs ?? 0);\n\t\t} catch (error) {\n\t\t\tlastError = error instanceof Error ? error.message : String(error);\n\t\t\tconst willRetry = policy.idempotent && attempt < policy.maxAttempts;\n\t\t\tconst nextDelayMs = willRetry ? getRetryDelayMs(attempt, policy) : undefined;\n\t\t\tawait options.onAttempt?.({ attempt, error: lastError, willRetry, nextDelayMs });\n\n\t\t\tif (!willRetry) {\n\t\t\t\tthrow error;\n\t\t\t}\n\n\t\t\tawait sleep(nextDelayMs ?? 0);\n\t\t}\n\t}\n\n\tthrow new Error(lastError ?? \"Adapter retry exhausted\");\n}\n"]}
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// state-machines/engine.ts
|
|
2
|
+
var registry = /* @__PURE__ */ new Map();
|
|
3
|
+
function registerStateMachine(def) {
|
|
4
|
+
if (registry.has(def.entityType)) {
|
|
5
|
+
throw new Error(`State machine for entity type "${def.entityType}" is already registered.`);
|
|
6
|
+
}
|
|
7
|
+
registry.set(def.entityType, def);
|
|
8
|
+
}
|
|
9
|
+
function registerStateMachineIfMissing(def) {
|
|
10
|
+
if (!registry.has(def.entityType)) {
|
|
11
|
+
registerStateMachine(def);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function resolveStateMachine(entityType) {
|
|
15
|
+
return registry.get(entityType);
|
|
16
|
+
}
|
|
17
|
+
function validateTransition(entityType, from, to, actionId) {
|
|
18
|
+
const sm = registry.get(entityType);
|
|
19
|
+
if (!sm) {
|
|
20
|
+
return {
|
|
21
|
+
valid: false,
|
|
22
|
+
error: `No state machine registered for entity type "${entityType}".`
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
const fromDef = sm.states[from];
|
|
26
|
+
if (!fromDef) {
|
|
27
|
+
return {
|
|
28
|
+
valid: false,
|
|
29
|
+
error: `State "${from}" does not exist in state machine for entity type "${entityType}".`
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
const toDef = sm.states[to];
|
|
33
|
+
if (!toDef) {
|
|
34
|
+
return {
|
|
35
|
+
valid: false,
|
|
36
|
+
error: `State "${to}" does not exist in state machine for entity type "${entityType}".`
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
const transitionExists = sm.transitions.some(
|
|
40
|
+
(t) => t.from === from && t.to === to && t.causedByAction === actionId
|
|
41
|
+
);
|
|
42
|
+
if (!transitionExists) {
|
|
43
|
+
return {
|
|
44
|
+
valid: false,
|
|
45
|
+
error: `Transition from "${from}" to "${to}" via action "${actionId}" is not defined for entity type "${entityType}".`
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
return { valid: true };
|
|
49
|
+
}
|
|
50
|
+
function getAllowedTransitions(entityType, from) {
|
|
51
|
+
const sm = registry.get(entityType);
|
|
52
|
+
if (!sm) {
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
return sm.transitions.filter((t) => t.from === from).map((t) => ({ to: t.to, causedByAction: t.causedByAction }));
|
|
56
|
+
}
|
|
57
|
+
function getEntityStateClass(entityType, state) {
|
|
58
|
+
const sm = registry.get(entityType);
|
|
59
|
+
if (!sm) {
|
|
60
|
+
return void 0;
|
|
61
|
+
}
|
|
62
|
+
return sm.states[state]?.stateClass;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export { getAllowedTransitions, getEntityStateClass, registerStateMachine, registerStateMachineIfMissing, resolveStateMachine, validateTransition };
|
|
66
|
+
//# sourceMappingURL=chunk-3A6FU3KV.js.map
|
|
67
|
+
//# sourceMappingURL=chunk-3A6FU3KV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../state-machines/engine.ts"],"names":[],"mappings":"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","file":"chunk-3A6FU3KV.js","sourcesContent":["import type { StateClass, StateDefinition } from \"./index\";\n\nexport interface StateMachineDefinition {\n\tentityType: string;\n\tstates: Record<string, StateDefinition>;\n\ttransitions: Array<{\n\t\tfrom: string;\n\t\tto: string;\n\t\tcausedByAction: string;\n\t\tguard?: (ctx: TransitionContext) => boolean | Promise<boolean>;\n\t}>;\n}\n\nexport type StateMachineDatabaseClient = any;\n\nexport interface TransitionContext<TDb = StateMachineDatabaseClient> {\n\tentity: Record<string, unknown>;\n\tactionInvocationId: string;\n\tactorId: string;\n\tparameters: unknown;\n\tdb: TDb;\n}\n\nconst registry = new Map<string, StateMachineDefinition>();\n\n/**\n * Register a state-machine definition for an entity type.\n * Throws if a definition for this entity type is already registered.\n */\nexport function registerStateMachine(def: StateMachineDefinition): void {\n\tif (registry.has(def.entityType)) {\n\t\tthrow new Error(`State machine for entity type \"${def.entityType}\" is already registered.`);\n\t}\n\tregistry.set(def.entityType, def);\n}\n\n/**\n * Register a state-machine definition only if one for this entity type\n * is not already registered. Idempotent — safe to call multiple times.\n */\nexport function registerStateMachineIfMissing(def: StateMachineDefinition): void {\n\tif (!registry.has(def.entityType)) {\n\t\tregisterStateMachine(def);\n\t}\n}\n\n/**\n * Resolve a previously registered state-machine definition by entity type.\n */\nexport function resolveStateMachine(entityType: string): StateMachineDefinition | undefined {\n\treturn registry.get(entityType);\n}\n\n/**\n * Validate whether a transition is allowed for the given entity type,\n * from state, to state, and action ID.\n */\nexport function validateTransition(\n\tentityType: string,\n\tfrom: string,\n\tto: string,\n\tactionId: string,\n): { valid: boolean; error?: string } {\n\tconst sm = registry.get(entityType);\n\tif (!sm) {\n\t\treturn {\n\t\t\tvalid: false,\n\t\t\terror: `No state machine registered for entity type \"${entityType}\".`,\n\t\t};\n\t}\n\n\tconst fromDef = sm.states[from];\n\tif (!fromDef) {\n\t\treturn {\n\t\t\tvalid: false,\n\t\t\terror: `State \"${from}\" does not exist in state machine for entity type \"${entityType}\".`,\n\t\t};\n\t}\n\n\tconst toDef = sm.states[to];\n\tif (!toDef) {\n\t\treturn {\n\t\t\tvalid: false,\n\t\t\terror: `State \"${to}\" does not exist in state machine for entity type \"${entityType}\".`,\n\t\t};\n\t}\n\n\tconst transitionExists = sm.transitions.some(\n\t\t(t) => t.from === from && t.to === to && t.causedByAction === actionId,\n\t);\n\tif (!transitionExists) {\n\t\treturn {\n\t\t\tvalid: false,\n\t\t\terror: `Transition from \"${from}\" to \"${to}\" via action \"${actionId}\" is not defined for entity type \"${entityType}\".`,\n\t\t};\n\t}\n\n\treturn { valid: true };\n}\n\n/**\n * Get all allowed transitions from a given state for an entity type.\n */\nexport function getAllowedTransitions(\n\tentityType: string,\n\tfrom: string,\n): Array<{ to: string; causedByAction: string }> {\n\tconst sm = registry.get(entityType);\n\tif (!sm) {\n\t\treturn [];\n\t}\n\treturn sm.transitions\n\t\t.filter((t) => t.from === from)\n\t\t.map((t) => ({ to: t.to, causedByAction: t.causedByAction }));\n}\n\n/**\n * Get the StateClass of a given state for an entity type.\n */\nexport function getEntityStateClass(entityType: string, state: string): StateClass | undefined {\n\tconst sm = registry.get(entityType);\n\tif (!sm) {\n\t\treturn undefined;\n\t}\n\treturn sm.states[state]?.stateClass;\n}\n"]}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import assert from 'assert';
|
|
2
|
+
|
|
3
|
+
// displays/index.ts
|
|
4
|
+
var registry = /* @__PURE__ */ new Map();
|
|
5
|
+
function registerDisplayRenderer(eventType, renderer) {
|
|
6
|
+
registry.set(eventType, renderer);
|
|
7
|
+
}
|
|
8
|
+
function resolveDisplayRenderer(eventType) {
|
|
9
|
+
return registry.get(eventType);
|
|
10
|
+
}
|
|
11
|
+
function denormalizeDisplaySnapshot(payload) {
|
|
12
|
+
if (payload !== null && typeof payload === "object" && "_displaySnapshot" in payload && payload._displaySnapshot !== null && typeof payload._displaySnapshot === "object" && "title" in payload._displaySnapshot) {
|
|
13
|
+
return payload._displaySnapshot;
|
|
14
|
+
}
|
|
15
|
+
if (payload !== null && typeof payload === "object" && "_projectionSnapshot" in payload && payload._projectionSnapshot !== null && typeof payload._projectionSnapshot === "object" && "display" in payload._projectionSnapshot && payload._projectionSnapshot.display !== null && typeof payload._projectionSnapshot.display === "object" && "title" in payload._projectionSnapshot.display) {
|
|
16
|
+
const snapshot = payload._projectionSnapshot;
|
|
17
|
+
return {
|
|
18
|
+
...snapshot.display,
|
|
19
|
+
_cachedDisplay: {
|
|
20
|
+
source: "projection_snapshot",
|
|
21
|
+
cursor: snapshot.eventCursor,
|
|
22
|
+
checksum: snapshot.checksum,
|
|
23
|
+
generatedAt: snapshot.updatedAt
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
return void 0;
|
|
28
|
+
}
|
|
29
|
+
async function renderEventDisplay(eventType, payload, ctx) {
|
|
30
|
+
const snapshot = denormalizeDisplaySnapshot(payload);
|
|
31
|
+
if (snapshot) {
|
|
32
|
+
return snapshot;
|
|
33
|
+
}
|
|
34
|
+
const renderer = resolveDisplayRenderer(eventType);
|
|
35
|
+
if (!renderer) {
|
|
36
|
+
return {
|
|
37
|
+
title: eventType,
|
|
38
|
+
subtitle: "No display renderer registered for this event type."
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
return await renderer(payload, ctx);
|
|
42
|
+
}
|
|
43
|
+
async function _runInlineTests() {
|
|
44
|
+
const testEventType = "__TestEvent__";
|
|
45
|
+
registerDisplayRenderer(testEventType, (payload) => ({
|
|
46
|
+
title: `Test: ${payload.name}`,
|
|
47
|
+
subtitle: "registered renderer"
|
|
48
|
+
}));
|
|
49
|
+
const renderer = resolveDisplayRenderer(testEventType);
|
|
50
|
+
assert.ok(renderer, "resolveDisplayRenderer should return the registered renderer");
|
|
51
|
+
const result = await renderEventDisplay(testEventType, { name: "Alice" });
|
|
52
|
+
assert.strictEqual(result.title, "Test: Alice");
|
|
53
|
+
assert.strictEqual(result.subtitle, "registered renderer");
|
|
54
|
+
const snapshot = {
|
|
55
|
+
title: "Snapshot Title",
|
|
56
|
+
subtitle: "snapshot subtitle",
|
|
57
|
+
stateLabel: "archived",
|
|
58
|
+
primaryFacts: [{ label: "ID", value: "123" }]
|
|
59
|
+
};
|
|
60
|
+
const snapshotResult = await renderEventDisplay(testEventType, {
|
|
61
|
+
_displaySnapshot: snapshot
|
|
62
|
+
});
|
|
63
|
+
assert.deepStrictEqual(snapshotResult, snapshot);
|
|
64
|
+
const fallback = await renderEventDisplay("__UnknownEvent__", {});
|
|
65
|
+
assert.strictEqual(fallback.title, "__UnknownEvent__");
|
|
66
|
+
assert.strictEqual(fallback.subtitle, "No display renderer registered for this event type.");
|
|
67
|
+
registerDisplayRenderer("__AsyncEvent__", async (_payload, ctx) => {
|
|
68
|
+
return {
|
|
69
|
+
title: `Async: ${ctx?.tenantId ?? "no-ctx"}`
|
|
70
|
+
};
|
|
71
|
+
});
|
|
72
|
+
const asyncResult = await renderEventDisplay(
|
|
73
|
+
"__AsyncEvent__",
|
|
74
|
+
{},
|
|
75
|
+
{ tenantId: "t-1", spaceId: "s-1", db: {} }
|
|
76
|
+
);
|
|
77
|
+
assert.strictEqual(asyncResult.title, "Async: t-1");
|
|
78
|
+
console.log("\u2705 display engine inline tests passed");
|
|
79
|
+
}
|
|
80
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
81
|
+
_runInlineTests().catch((err) => {
|
|
82
|
+
console.error("\u274C display engine inline tests failed", err);
|
|
83
|
+
process.exit(1);
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export { denormalizeDisplaySnapshot, registerDisplayRenderer, renderEventDisplay, resolveDisplayRenderer };
|
|
88
|
+
//# sourceMappingURL=chunk-3F6FKQZA.js.map
|
|
89
|
+
//# sourceMappingURL=chunk-3F6FKQZA.js.map
|