@fabricorg/platform 0.1.0 → 0.1.2

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 (55) hide show
  1. package/dist/actions/index.d.ts +3 -1
  2. package/dist/actions/index.js +1 -57
  3. package/dist/actions/index.js.map +1 -1
  4. package/dist/adapters/index.js +1 -99
  5. package/dist/adapters/index.js.map +1 -1
  6. package/dist/chunk-3A6FU3KV.js +67 -0
  7. package/dist/chunk-3A6FU3KV.js.map +1 -0
  8. package/dist/chunk-3F6FKQZA.js +89 -0
  9. package/dist/chunk-3F6FKQZA.js.map +1 -0
  10. package/dist/chunk-3ZW5USXF.js +101 -0
  11. package/dist/chunk-3ZW5USXF.js.map +1 -0
  12. package/dist/chunk-4YQOGV7H.js +55 -0
  13. package/dist/chunk-4YQOGV7H.js.map +1 -0
  14. package/dist/chunk-6FMW4NF6.js +59 -0
  15. package/dist/chunk-6FMW4NF6.js.map +1 -0
  16. package/dist/chunk-I35B576X.js +30 -0
  17. package/dist/chunk-I35B576X.js.map +1 -0
  18. package/dist/chunk-IFMZPLM4.js +27 -0
  19. package/dist/chunk-IFMZPLM4.js.map +1 -0
  20. package/dist/chunk-JSTGOT6P.js +181 -0
  21. package/dist/chunk-JSTGOT6P.js.map +1 -0
  22. package/dist/chunk-KOC4A7A4.js +544 -0
  23. package/dist/chunk-KOC4A7A4.js.map +1 -0
  24. package/dist/chunk-N24NLCZ6.js +6 -0
  25. package/dist/chunk-N24NLCZ6.js.map +1 -0
  26. package/dist/chunk-OZLZHISS.js +3 -0
  27. package/dist/chunk-OZLZHISS.js.map +1 -0
  28. package/dist/chunk-PXMKCAQI.js +187 -0
  29. package/dist/chunk-PXMKCAQI.js.map +1 -0
  30. package/dist/chunk-RALMUSOX.js +55 -0
  31. package/dist/chunk-RALMUSOX.js.map +1 -0
  32. package/dist/displays/index.js +1 -87
  33. package/dist/displays/index.js.map +1 -1
  34. package/dist/events/index.js +1 -53
  35. package/dist/events/index.js.map +1 -1
  36. package/dist/evidence/index.js +1 -1
  37. package/dist/ids/index.js +1 -53
  38. package/dist/ids/index.js.map +1 -1
  39. package/dist/index.js +13 -1355
  40. package/dist/index.js.map +1 -1
  41. package/dist/modules/index.js +7 -373
  42. package/dist/modules/index.js.map +1 -1
  43. package/dist/objects/index.js +1 -28
  44. package/dist/objects/index.js.map +1 -1
  45. package/dist/policies/index.js +1 -542
  46. package/dist/policies/index.js.map +1 -1
  47. package/dist/privacy/index.js +1 -4
  48. package/dist/privacy/index.js.map +1 -1
  49. package/dist/projections/index.js +1 -179
  50. package/dist/projections/index.js.map +1 -1
  51. package/dist/state-machines/index.js +1 -65
  52. package/dist/state-machines/index.js.map +1 -1
  53. package/dist/testing/index.js +2 -56
  54. package/dist/testing/index.js.map +1 -1
  55. package/package.json +111 -112
@@ -1,181 +1,3 @@
1
- // projections/index.ts
2
- async function replayEvents({
3
- events,
4
- scope,
5
- initialState,
6
- snapshot,
7
- applyEvent
8
- }) {
9
- const warnings = [];
10
- const scopedEvents = filterEventsByScope(await collectEvents(events), scope, warnings);
11
- const orderedEvents = sortEvents(scopedEvents, isSubjectScoped(scope));
12
- const uniqueEvents = removeDuplicateEvents(orderedEvents, warnings);
13
- const resumableEvents = applyCursor(uniqueEvents, snapshot, isSubjectScoped(scope), warnings);
14
- let state = snapshot?.snapshotData ?? initialState;
15
- const appliedEvents = [];
16
- let eventCursor = snapshot?.eventCursor ?? null;
17
- let eventSequence = snapshot?.eventSequence ?? 0;
18
- for (const event of resumableEvents) {
19
- state = await applyEvent(state, event);
20
- appliedEvents.push(event);
21
- eventCursor = event.id;
22
- eventSequence = event.sequence;
23
- }
24
- return {
25
- state,
26
- appliedEvents,
27
- warnings,
28
- eventCursor,
29
- eventSequence
30
- };
31
- }
32
- async function collectEvents(events) {
33
- const collected = [];
34
- if (Symbol.asyncIterator in events) {
35
- for await (const event of events) {
36
- collected.push(event);
37
- }
38
- return collected;
39
- }
40
- for (const event of events) {
41
- collected.push(event);
42
- }
43
- return collected;
44
- }
45
- function filterEventsByScope(events, scope, warnings) {
46
- if (scope.subjectType && !scope.subjectId || !scope.subjectType && scope.subjectId) {
47
- warnings.push({
48
- code: "subject_scope_incomplete",
49
- message: "Both subjectType and subjectId are required for subject-scoped replay.",
50
- subjectType: scope.subjectType,
51
- subjectId: scope.subjectId
52
- });
53
- }
54
- return events.filter((event) => {
55
- if (event.tenantId !== scope.tenantId || event.spaceId !== scope.spaceId) {
56
- return false;
57
- }
58
- if (scope.subjectType && event.subjectType !== scope.subjectType) {
59
- return false;
60
- }
61
- if (scope.subjectId && event.subjectId !== scope.subjectId) {
62
- return false;
63
- }
64
- return true;
65
- });
66
- }
67
- function removeDuplicateEvents(events, warnings) {
68
- const seenEventIds = /* @__PURE__ */ new Set();
69
- const uniqueEvents = [];
70
- for (const event of events) {
71
- if (seenEventIds.has(event.id)) {
72
- warnings.push({
73
- code: "duplicate_event",
74
- message: `Duplicate event ${event.id} ignored during replay.`,
75
- eventId: event.id,
76
- subjectType: event.subjectType,
77
- subjectId: event.subjectId
78
- });
79
- continue;
80
- }
81
- seenEventIds.add(event.id);
82
- uniqueEvents.push(event);
83
- }
84
- recordMissingSequenceWarnings(uniqueEvents, warnings);
85
- return uniqueEvents;
86
- }
87
- function recordMissingSequenceWarnings(events, warnings) {
88
- const lastSequenceBySubject = /* @__PURE__ */ new Map();
89
- for (const event of events) {
90
- const subjectKey = `${event.subjectType}:${event.subjectId}`;
91
- const previousSequence = lastSequenceBySubject.get(subjectKey);
92
- const expectedSequence = previousSequence === void 0 ? event.sequence : previousSequence + 1;
93
- if (previousSequence !== void 0 && event.sequence > expectedSequence) {
94
- warnings.push({
95
- code: "missing_sequence",
96
- message: `Missing event sequence for ${subjectKey}: expected ${expectedSequence}, received ${event.sequence}.`,
97
- eventId: event.id,
98
- subjectType: event.subjectType,
99
- subjectId: event.subjectId,
100
- expectedSequence,
101
- actualSequence: event.sequence
102
- });
103
- }
104
- lastSequenceBySubject.set(subjectKey, Math.max(previousSequence ?? event.sequence, event.sequence));
105
- }
106
- }
107
- function applyCursor(events, snapshot, subjectScoped, warnings) {
108
- const eventCursor = snapshot?.eventCursor;
109
- if (eventCursor) {
110
- const cursorIndex = events.findIndex((event) => event.id === eventCursor);
111
- if (cursorIndex >= 0) {
112
- return events.slice(cursorIndex + 1);
113
- }
114
- warnings.push({
115
- code: "cursor_not_found",
116
- message: `Snapshot cursor ${eventCursor} was not found in the scoped event stream.`,
117
- eventId: eventCursor
118
- });
119
- }
120
- if (subjectScoped && snapshot?.eventSequence !== void 0 && snapshot.eventSequence !== null) {
121
- return events.filter((event) => event.sequence > snapshot.eventSequence);
122
- }
123
- return events;
124
- }
125
- function sortEvents(events, subjectScoped) {
126
- return [...events].sort(
127
- (first, second) => subjectScoped ? compareSubjectEvents(first, second) : compareGlobalEvents(first, second)
128
- );
129
- }
130
- function compareSubjectEvents(first, second) {
131
- return compareValues(
132
- [first.sequence, toTimestamp(first.recordedAt), toTimestamp(first.occurredAt), first.id],
133
- [second.sequence, toTimestamp(second.recordedAt), toTimestamp(second.occurredAt), second.id]
134
- );
135
- }
136
- function compareGlobalEvents(first, second) {
137
- return compareValues(
138
- [
139
- toTimestamp(first.recordedAt),
140
- toTimestamp(first.occurredAt),
141
- first.actionInvocationId,
142
- first.correlationId,
143
- first.subjectType,
144
- first.subjectId,
145
- first.sequence,
146
- first.id
147
- ],
148
- [
149
- toTimestamp(second.recordedAt),
150
- toTimestamp(second.occurredAt),
151
- second.actionInvocationId,
152
- second.correlationId,
153
- second.subjectType,
154
- second.subjectId,
155
- second.sequence,
156
- second.id
157
- ]
158
- );
159
- }
160
- function compareValues(firstValues, secondValues) {
161
- for (const [index, firstValue] of firstValues.entries()) {
162
- const secondValue = secondValues[index];
163
- if (firstValue < secondValue) {
164
- return -1;
165
- }
166
- if (firstValue > secondValue) {
167
- return 1;
168
- }
169
- }
170
- return 0;
171
- }
172
- function isSubjectScoped(scope) {
173
- return Boolean(scope.subjectType && scope.subjectId);
174
- }
175
- function toTimestamp(value) {
176
- return value instanceof Date ? value.getTime() : new Date(value).getTime();
177
- }
178
-
179
- export { replayEvents };
1
+ export { replayEvents } from '../chunk-JSTGOT6P.js';
180
2
  //# sourceMappingURL=index.js.map
181
3
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../projections/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["export interface ReplayableAssetEvent<Payload = unknown> {\n\tid: string;\n\ttenantId: string;\n\tspaceId: string;\n\teventType: string;\n\tsubjectType: string;\n\tsubjectId: string;\n\tpayload: Payload;\n\tsequence: number;\n\toccurredAt: Date | string;\n\trecordedAt: Date | string;\n\tactionInvocationId: string;\n\tcorrelationId: string;\n}\n\nexport interface ReplayScope {\n\ttenantId: string;\n\tspaceId: string;\n\tsubjectType?: string;\n\tsubjectId?: string;\n}\n\nexport interface ReplaySnapshot<State = unknown> {\n\tsnapshotData: State;\n\teventCursor?: string | null;\n\teventSequence?: number | null;\n}\n\nexport type ReplayWarningCode =\n\t| \"cursor_not_found\"\n\t| \"duplicate_event\"\n\t| \"missing_sequence\"\n\t| \"subject_scope_incomplete\";\n\nexport interface ReplayWarning {\n\tcode: ReplayWarningCode;\n\tmessage: string;\n\teventId?: string;\n\tsubjectType?: string;\n\tsubjectId?: string;\n\texpectedSequence?: number;\n\tactualSequence?: number;\n}\n\nexport interface ReplayEventsOptions<State, Event extends ReplayableAssetEvent = ReplayableAssetEvent> {\n\tevents: Iterable<Event> | AsyncIterable<Event>;\n\tscope: ReplayScope;\n\tinitialState: State;\n\tsnapshot?: ReplaySnapshot<State> | null;\n\tapplyEvent: (state: State, event: Event) => State | Promise<State>;\n}\n\nexport interface ReplayEventsResult<State, Event extends ReplayableAssetEvent = ReplayableAssetEvent> {\n\tstate: State;\n\tappliedEvents: Event[];\n\twarnings: ReplayWarning[];\n\teventCursor: string | null;\n\teventSequence: number;\n}\n\nexport async function replayEvents<State, Event extends ReplayableAssetEvent = ReplayableAssetEvent>({\n\tevents,\n\tscope,\n\tinitialState,\n\tsnapshot,\n\tapplyEvent,\n}: ReplayEventsOptions<State, Event>): Promise<ReplayEventsResult<State, Event>> {\n\tconst warnings: ReplayWarning[] = [];\n\tconst scopedEvents = filterEventsByScope(await collectEvents(events), scope, warnings);\n\tconst orderedEvents = sortEvents(scopedEvents, isSubjectScoped(scope));\n\tconst uniqueEvents = removeDuplicateEvents(orderedEvents, warnings);\n\tconst resumableEvents = applyCursor(uniqueEvents, snapshot, isSubjectScoped(scope), warnings);\n\n\tlet state = snapshot?.snapshotData ?? initialState;\n\tconst appliedEvents: Event[] = [];\n\tlet eventCursor = snapshot?.eventCursor ?? null;\n\tlet eventSequence = snapshot?.eventSequence ?? 0;\n\n\tfor (const event of resumableEvents) {\n\t\tstate = await applyEvent(state, event);\n\t\tappliedEvents.push(event);\n\t\teventCursor = event.id;\n\t\teventSequence = event.sequence;\n\t}\n\n\treturn {\n\t\tstate,\n\t\tappliedEvents,\n\t\twarnings,\n\t\teventCursor,\n\t\teventSequence,\n\t};\n}\n\nasync function collectEvents<Event extends ReplayableAssetEvent>(\n\tevents: Iterable<Event> | AsyncIterable<Event>,\n): Promise<Event[]> {\n\tconst collected: Event[] = [];\n\n\tif (Symbol.asyncIterator in events) {\n\t\tfor await (const event of events) {\n\t\t\tcollected.push(event);\n\t\t}\n\n\t\treturn collected;\n\t}\n\n\tfor (const event of events) {\n\t\tcollected.push(event);\n\t}\n\n\treturn collected;\n}\n\nfunction filterEventsByScope<Event extends ReplayableAssetEvent>(\n\tevents: Event[],\n\tscope: ReplayScope,\n\twarnings: ReplayWarning[],\n): Event[] {\n\tif ((scope.subjectType && !scope.subjectId) || (!scope.subjectType && scope.subjectId)) {\n\t\twarnings.push({\n\t\t\tcode: \"subject_scope_incomplete\",\n\t\t\tmessage: \"Both subjectType and subjectId are required for subject-scoped replay.\",\n\t\t\tsubjectType: scope.subjectType,\n\t\t\tsubjectId: scope.subjectId,\n\t\t});\n\t}\n\n\treturn events.filter((event) => {\n\t\tif (event.tenantId !== scope.tenantId || event.spaceId !== scope.spaceId) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (scope.subjectType && event.subjectType !== scope.subjectType) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (scope.subjectId && event.subjectId !== scope.subjectId) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t});\n}\n\nfunction removeDuplicateEvents<Event extends ReplayableAssetEvent>(\n\tevents: Event[],\n\twarnings: ReplayWarning[],\n): Event[] {\n\tconst seenEventIds = new Set<string>();\n\tconst uniqueEvents: Event[] = [];\n\n\tfor (const event of events) {\n\t\tif (seenEventIds.has(event.id)) {\n\t\t\twarnings.push({\n\t\t\t\tcode: \"duplicate_event\",\n\t\t\t\tmessage: `Duplicate event ${event.id} ignored during replay.`,\n\t\t\t\teventId: event.id,\n\t\t\t\tsubjectType: event.subjectType,\n\t\t\t\tsubjectId: event.subjectId,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\n\t\tseenEventIds.add(event.id);\n\t\tuniqueEvents.push(event);\n\t}\n\n\trecordMissingSequenceWarnings(uniqueEvents, warnings);\n\n\treturn uniqueEvents;\n}\n\nfunction recordMissingSequenceWarnings<Event extends ReplayableAssetEvent>(\n\tevents: Event[],\n\twarnings: ReplayWarning[],\n): void {\n\tconst lastSequenceBySubject = new Map<string, number>();\n\n\tfor (const event of events) {\n\t\tconst subjectKey = `${event.subjectType}:${event.subjectId}`;\n\t\tconst previousSequence = lastSequenceBySubject.get(subjectKey);\n\t\tconst expectedSequence = previousSequence === undefined ? event.sequence : previousSequence + 1;\n\n\t\tif (previousSequence !== undefined && event.sequence > expectedSequence) {\n\t\t\twarnings.push({\n\t\t\t\tcode: \"missing_sequence\",\n\t\t\t\tmessage: `Missing event sequence for ${subjectKey}: expected ${expectedSequence}, received ${event.sequence}.`,\n\t\t\t\teventId: event.id,\n\t\t\t\tsubjectType: event.subjectType,\n\t\t\t\tsubjectId: event.subjectId,\n\t\t\t\texpectedSequence,\n\t\t\t\tactualSequence: event.sequence,\n\t\t\t});\n\t\t}\n\n\t\tlastSequenceBySubject.set(subjectKey, Math.max(previousSequence ?? event.sequence, event.sequence));\n\t}\n}\n\nfunction applyCursor<Event extends ReplayableAssetEvent>(\n\tevents: Event[],\n\tsnapshot: ReplaySnapshot | null | undefined,\n\tsubjectScoped: boolean,\n\twarnings: ReplayWarning[],\n): Event[] {\n\tconst eventCursor = snapshot?.eventCursor;\n\n\tif (eventCursor) {\n\t\tconst cursorIndex = events.findIndex((event) => event.id === eventCursor);\n\n\t\tif (cursorIndex >= 0) {\n\t\t\treturn events.slice(cursorIndex + 1);\n\t\t}\n\n\t\twarnings.push({\n\t\t\tcode: \"cursor_not_found\",\n\t\t\tmessage: `Snapshot cursor ${eventCursor} was not found in the scoped event stream.`,\n\t\t\teventId: eventCursor,\n\t\t});\n\t}\n\n\tif (subjectScoped && snapshot?.eventSequence !== undefined && snapshot.eventSequence !== null) {\n\t\treturn events.filter((event) => event.sequence > snapshot.eventSequence!);\n\t}\n\n\treturn events;\n}\n\nfunction sortEvents<Event extends ReplayableAssetEvent>(events: Event[], subjectScoped: boolean): Event[] {\n\treturn [...events].sort((first, second) =>\n\t\tsubjectScoped ? compareSubjectEvents(first, second) : compareGlobalEvents(first, second),\n\t);\n}\n\nfunction compareSubjectEvents(first: ReplayableAssetEvent, second: ReplayableAssetEvent): number {\n\treturn compareValues(\n\t\t[first.sequence, toTimestamp(first.recordedAt), toTimestamp(first.occurredAt), first.id],\n\t\t[second.sequence, toTimestamp(second.recordedAt), toTimestamp(second.occurredAt), second.id],\n\t);\n}\n\nfunction compareGlobalEvents(first: ReplayableAssetEvent, second: ReplayableAssetEvent): number {\n\treturn compareValues(\n\t\t[\n\t\t\ttoTimestamp(first.recordedAt),\n\t\t\ttoTimestamp(first.occurredAt),\n\t\t\tfirst.actionInvocationId,\n\t\t\tfirst.correlationId,\n\t\t\tfirst.subjectType,\n\t\t\tfirst.subjectId,\n\t\t\tfirst.sequence,\n\t\t\tfirst.id,\n\t\t],\n\t\t[\n\t\t\ttoTimestamp(second.recordedAt),\n\t\t\ttoTimestamp(second.occurredAt),\n\t\t\tsecond.actionInvocationId,\n\t\t\tsecond.correlationId,\n\t\t\tsecond.subjectType,\n\t\t\tsecond.subjectId,\n\t\t\tsecond.sequence,\n\t\t\tsecond.id,\n\t\t],\n\t);\n}\n\nfunction compareValues(firstValues: Array<number | string>, secondValues: Array<number | string>): number {\n\tfor (const [index, firstValue] of firstValues.entries()) {\n\t\tconst secondValue = secondValues[index];\n\n\t\tif (firstValue < secondValue) {\n\t\t\treturn -1;\n\t\t}\n\n\t\tif (firstValue > secondValue) {\n\t\t\treturn 1;\n\t\t}\n\t}\n\n\treturn 0;\n}\n\nfunction isSubjectScoped(scope: ReplayScope): boolean {\n\treturn Boolean(scope.subjectType && scope.subjectId);\n}\n\nfunction toTimestamp(value: Date | string): number {\n\treturn value instanceof Date ? value.getTime() : new Date(value).getTime();\n}\n"]}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
@@ -1,67 +1,3 @@
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 };
1
+ export { getAllowedTransitions, getEntityStateClass, registerStateMachine, registerStateMachineIfMissing, resolveStateMachine, validateTransition } from '../chunk-3A6FU3KV.js';
66
2
  //# sourceMappingURL=index.js.map
67
3
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../state-machines/engine.ts"],"names":[],"mappings":"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","file":"index.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"]}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
@@ -1,58 +1,4 @@
1
- // adapters/index.ts
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
-
34
- // testing/index.ts
35
- function createFakeActionContext(options = {}) {
36
- return {
37
- actionInvocationId: options.actionInvocationId ?? "act_test",
38
- tenantId: options.tenantId ?? "tenant-test",
39
- spaceId: options.spaceId ?? "space-test",
40
- actorId: options.actorId ?? "actor-test",
41
- actorType: options.actorType ?? "system",
42
- correlationId: options.correlationId ?? "corr-test",
43
- causationId: options.causationId,
44
- db: options.db ?? {},
45
- services: options.services ?? {}
46
- };
47
- }
48
- function createInMemoryAdapterRegistry() {
49
- return new AdapterRegistry();
50
- }
51
- function createDeterministicIdFactory(prefix = "test") {
52
- let sequence = 0;
53
- return () => `${prefix}_${String(++sequence).padStart(6, "0")}`;
54
- }
55
-
56
- export { createDeterministicIdFactory, createFakeActionContext, createInMemoryAdapterRegistry };
1
+ export { createDeterministicIdFactory, createFakeActionContext, createInMemoryAdapterRegistry } from '../chunk-IFMZPLM4.js';
2
+ import '../chunk-3ZW5USXF.js';
57
3
  //# sourceMappingURL=index.js.map
58
4
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../adapters/index.ts","../../testing/index.ts"],"names":[],"mappings":";AAiFA,SAAS,UAAA,CAAW,aAAqB,SAAA,EAA2B;AACnE,EAAA,OAAO,CAAA,EAAG,WAAW,CAAA,CAAA,EAAI,SAAS,CAAA,CAAA;AACnC;AAEO,IAAM,+BAAA,GAAN,cAA8C,KAAA,CAAM;AAAA,EAC1D,WAAA,CAAY,aAAqB,SAAA,EAAmB;AACnD,IAAA,KAAA,CAAM,CAAA,uCAAA,EAA0C,WAAW,CAAA,iBAAA,EAAoB,SAAS,CAAA,CAAA,CAAG,CAAA;AAC3F,IAAA,IAAA,CAAK,IAAA,GAAO,iCAAA;AAAA,EACb;AACD,CAAA;AAEO,IAAM,kBAAN,MAAsB;AAAA,EACX,QAAA,uBAAe,GAAA,EAAmC;AAAA,EAEnE,SAAS,OAAA,EAAsC;AAC9C,IAAA,MAAM,GAAA,GAAM,UAAA,CAAW,OAAA,CAAQ,WAAA,EAAa,QAAQ,SAAS,CAAA;AAC7D,IAAA,IAAI,IAAA,CAAK,QAAA,CAAS,GAAA,CAAI,GAAG,CAAA,EAAG;AAC3B,MAAA,MAAM,IAAI,KAAA;AAAA,QACT,CAAA,4CAAA,EAA+C,OAAA,CAAQ,WAAW,CAAA,iBAAA,EAAoB,QAAQ,SAAS,CAAA,CAAA;AAAA,OACxG;AAAA,IACD;AAEA,IAAA,IAAA,CAAK,QAAA,CAAS,GAAA,CAAI,GAAA,EAAK,OAAO,CAAA;AAAA,EAC/B;AAAA,EAEA,OAAA,CAAQ,aAAqB,SAAA,EAAsD;AAClF,IAAA,OAAO,KAAK,QAAA,CAAS,GAAA,CAAI,UAAA,CAAW,WAAA,EAAa,SAAS,CAAC,CAAA;AAAA,EAC5D;AAAA,EAEA,OAAA,CAAQ,aAAqB,SAAA,EAA0C;AACtE,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,OAAA,CAAQ,WAAA,EAAa,SAAS,CAAA;AACnD,IAAA,IAAI,CAAC,OAAA,EAAS;AACb,MAAA,MAAM,IAAI,+BAAA,CAAgC,WAAA,EAAa,SAAS,CAAA;AAAA,IACjE;AAEA,IAAA,OAAO,OAAA;AAAA,EACR;AACD,CAAA;;;ACvGO,SAAS,uBAAA,CACf,OAAA,GAAyC,EAAC,EACrB;AACrB,EAAA,OAAO;AAAA,IACN,kBAAA,EAAoB,QAAQ,kBAAA,IAAsB,UAAA;AAAA,IAClD,QAAA,EAAU,QAAQ,QAAA,IAAY,aAAA;AAAA,IAC9B,OAAA,EAAS,QAAQ,OAAA,IAAW,YAAA;AAAA,IAC5B,OAAA,EAAS,QAAQ,OAAA,IAAW,YAAA;AAAA,IAC5B,SAAA,EAAW,QAAQ,SAAA,IAAa,QAAA;AAAA,IAChC,aAAA,EAAe,QAAQ,aAAA,IAAiB,WAAA;AAAA,IACxC,aAAa,OAAA,CAAQ,WAAA;AAAA,IACrB,EAAA,EAAI,OAAA,CAAQ,EAAA,IAAO,EAAC;AAAA,IACpB,QAAA,EAAU,OAAA,CAAQ,QAAA,IAAY;AAAC,GAChC;AACD;AAEO,SAAS,6BAAA,GAAiD;AAChE,EAAA,OAAO,IAAI,eAAA,EAAgB;AAC5B;AAEO,SAAS,4BAAA,CAA6B,SAAS,MAAA,EAAsB;AAC3E,EAAA,IAAI,QAAA,GAAW,CAAA;AACf,EAAA,OAAO,MAAM,CAAA,EAAG,MAAM,CAAA,CAAA,EAAI,MAAA,CAAO,EAAE,QAAQ,CAAA,CAAE,QAAA,CAAS,CAAA,EAAG,GAAG,CAAC,CAAA,CAAA;AAC9D","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","import { AdapterRegistry } from \"../adapters\";\nimport type { ActionContext, ActorType } from \"../actions\";\n\nexport interface FakeActionContextOptions<TDb = Record<string, never>> {\n\tactionInvocationId?: string;\n\ttenantId?: string;\n\tspaceId?: string;\n\tactorId?: string;\n\tactorType?: ActorType;\n\tcorrelationId?: string;\n\tcausationId?: string;\n\tdb?: TDb;\n\tservices?: Record<string, unknown>;\n}\n\nexport function createFakeActionContext<TDb = Record<string, never>>(\n\toptions: FakeActionContextOptions<TDb> = {},\n): ActionContext<TDb> {\n\treturn {\n\t\tactionInvocationId: options.actionInvocationId ?? \"act_test\",\n\t\ttenantId: options.tenantId ?? \"tenant-test\",\n\t\tspaceId: options.spaceId ?? \"space-test\",\n\t\tactorId: options.actorId ?? \"actor-test\",\n\t\tactorType: options.actorType ?? \"system\",\n\t\tcorrelationId: options.correlationId ?? \"corr-test\",\n\t\tcausationId: options.causationId,\n\t\tdb: options.db ?? ({} as TDb),\n\t\tservices: options.services ?? {},\n\t};\n}\n\nexport function createInMemoryAdapterRegistry(): AdapterRegistry {\n\treturn new AdapterRegistry();\n}\n\nexport function createDeterministicIdFactory(prefix = \"test\"): () => string {\n\tlet sequence = 0;\n\treturn () => `${prefix}_${String(++sequence).padStart(6, \"0\")}`;\n}\n"]}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
package/package.json CHANGED
@@ -1,113 +1,112 @@
1
1
  {
2
- "name": "@fabricorg/platform",
3
- "version": "0.1.0",
4
- "description": "Generic, vertical-agnostic runtime for ontology-based business applications. Provides actions, modules, events, policies, state machines, adapters, displays, projections, privacy, and evidence contracts. Zero runtime dependencies; portable across any database client and any vertical (lending, insurance, healthcare, legal, etc.).",
5
- "keywords": [
6
- "fabric",
7
- "ontology",
8
- "action-runtime",
9
- "event-sourcing",
10
- "saga",
11
- "state-machine",
12
- "policy-engine",
13
- "adapter-registry"
14
- ],
15
- "license": "MIT",
16
- "author": "Fabric",
17
- "repository": {
18
- "type": "git",
19
- "url": "https://github.com/Fabric-Pro/fabric-platform.git",
20
- "directory": "packages/platform"
21
- },
22
- "homepage": "https://github.com/Fabric-Pro/fabric-platform#readme",
23
- "bugs": {
24
- "url": "https://github.com/Fabric-Pro/fabric-platform/issues"
25
- },
26
- "type": "module",
27
- "sideEffects": false,
28
- "main": "./dist/index.js",
29
- "types": "./dist/index.d.ts",
30
- "exports": {
31
- ".": {
32
- "types": "./dist/index.d.ts",
33
- "import": "./dist/index.js"
34
- },
35
- "./actions": {
36
- "types": "./dist/actions/index.d.ts",
37
- "import": "./dist/actions/index.js"
38
- },
39
- "./adapters": {
40
- "types": "./dist/adapters/index.d.ts",
41
- "import": "./dist/adapters/index.js"
42
- },
43
- "./displays": {
44
- "types": "./dist/displays/index.d.ts",
45
- "import": "./dist/displays/index.js"
46
- },
47
- "./events": {
48
- "types": "./dist/events/index.d.ts",
49
- "import": "./dist/events/index.js"
50
- },
51
- "./evidence": {
52
- "types": "./dist/evidence/index.d.ts",
53
- "import": "./dist/evidence/index.js"
54
- },
55
- "./ids": {
56
- "types": "./dist/ids/index.d.ts",
57
- "import": "./dist/ids/index.js"
58
- },
59
- "./modules": {
60
- "types": "./dist/modules/index.d.ts",
61
- "import": "./dist/modules/index.js"
62
- },
63
- "./objects": {
64
- "types": "./dist/objects/index.d.ts",
65
- "import": "./dist/objects/index.js"
66
- },
67
- "./policies": {
68
- "types": "./dist/policies/index.d.ts",
69
- "import": "./dist/policies/index.js"
70
- },
71
- "./privacy": {
72
- "types": "./dist/privacy/index.d.ts",
73
- "import": "./dist/privacy/index.js"
74
- },
75
- "./projections": {
76
- "types": "./dist/projections/index.d.ts",
77
- "import": "./dist/projections/index.js"
78
- },
79
- "./state-machines": {
80
- "types": "./dist/state-machines/index.d.ts",
81
- "import": "./dist/state-machines/index.js"
82
- },
83
- "./testing": {
84
- "types": "./dist/testing/index.d.ts",
85
- "import": "./dist/testing/index.js"
86
- }
87
- },
88
- "files": [
89
- "dist",
90
- "README.md",
91
- "LICENSE"
92
- ],
93
- "scripts": {
94
- "clean": "rm -rf dist tsconfig.tsbuildinfo",
95
- "build": "tsup",
96
- "type-check": "tsc --noEmit",
97
- "test": "vitest run --passWithNoTests",
98
- "prepublishOnly": "pnpm clean && pnpm build && pnpm test"
99
- },
100
- "dependencies": {},
101
- "devDependencies": {
102
- "@types/node": "^22.10.0",
103
- "tsup": "^8.5.0",
104
- "typescript": "^5.7.3",
105
- "vitest": "^4.1.5"
106
- },
107
- "engines": {
108
- "node": ">=20"
109
- },
110
- "publishConfig": {
111
- "access": "public"
112
- }
113
- }
2
+ "name": "@fabricorg/platform",
3
+ "version": "0.1.2",
4
+ "description": "Generic, vertical-agnostic runtime for ontology-based business applications. Provides actions, modules, events, policies, state machines, adapters, displays, projections, privacy, and evidence contracts. Zero runtime dependencies; portable across any database client and any vertical (lending, insurance, healthcare, legal, etc.).",
5
+ "keywords": [
6
+ "fabric",
7
+ "ontology",
8
+ "action-runtime",
9
+ "event-sourcing",
10
+ "saga",
11
+ "state-machine",
12
+ "policy-engine",
13
+ "adapter-registry"
14
+ ],
15
+ "license": "MIT",
16
+ "author": "Fabric",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://github.com/Fabric-Pro/fabric-platform.git",
20
+ "directory": "packages/platform"
21
+ },
22
+ "homepage": "https://github.com/Fabric-Pro/fabric-platform#readme",
23
+ "bugs": {
24
+ "url": "https://github.com/Fabric-Pro/fabric-platform/issues"
25
+ },
26
+ "type": "module",
27
+ "sideEffects": false,
28
+ "main": "./dist/index.js",
29
+ "types": "./dist/index.d.ts",
30
+ "exports": {
31
+ ".": {
32
+ "types": "./dist/index.d.ts",
33
+ "import": "./dist/index.js"
34
+ },
35
+ "./actions": {
36
+ "types": "./dist/actions/index.d.ts",
37
+ "import": "./dist/actions/index.js"
38
+ },
39
+ "./adapters": {
40
+ "types": "./dist/adapters/index.d.ts",
41
+ "import": "./dist/adapters/index.js"
42
+ },
43
+ "./displays": {
44
+ "types": "./dist/displays/index.d.ts",
45
+ "import": "./dist/displays/index.js"
46
+ },
47
+ "./events": {
48
+ "types": "./dist/events/index.d.ts",
49
+ "import": "./dist/events/index.js"
50
+ },
51
+ "./evidence": {
52
+ "types": "./dist/evidence/index.d.ts",
53
+ "import": "./dist/evidence/index.js"
54
+ },
55
+ "./ids": {
56
+ "types": "./dist/ids/index.d.ts",
57
+ "import": "./dist/ids/index.js"
58
+ },
59
+ "./modules": {
60
+ "types": "./dist/modules/index.d.ts",
61
+ "import": "./dist/modules/index.js"
62
+ },
63
+ "./objects": {
64
+ "types": "./dist/objects/index.d.ts",
65
+ "import": "./dist/objects/index.js"
66
+ },
67
+ "./policies": {
68
+ "types": "./dist/policies/index.d.ts",
69
+ "import": "./dist/policies/index.js"
70
+ },
71
+ "./privacy": {
72
+ "types": "./dist/privacy/index.d.ts",
73
+ "import": "./dist/privacy/index.js"
74
+ },
75
+ "./projections": {
76
+ "types": "./dist/projections/index.d.ts",
77
+ "import": "./dist/projections/index.js"
78
+ },
79
+ "./state-machines": {
80
+ "types": "./dist/state-machines/index.d.ts",
81
+ "import": "./dist/state-machines/index.js"
82
+ },
83
+ "./testing": {
84
+ "types": "./dist/testing/index.d.ts",
85
+ "import": "./dist/testing/index.js"
86
+ }
87
+ },
88
+ "files": [
89
+ "dist",
90
+ "README.md",
91
+ "LICENSE"
92
+ ],
93
+ "dependencies": {},
94
+ "devDependencies": {
95
+ "@types/node": "^22.10.0",
96
+ "tsup": "^8.5.0",
97
+ "typescript": "^5.7.3",
98
+ "vitest": "^4.1.5"
99
+ },
100
+ "engines": {
101
+ "node": ">=20"
102
+ },
103
+ "publishConfig": {
104
+ "access": "public"
105
+ },
106
+ "scripts": {
107
+ "clean": "rm -rf dist tsconfig.tsbuildinfo",
108
+ "build": "tsup",
109
+ "type-check": "tsc --noEmit",
110
+ "test": "vitest run --passWithNoTests"
111
+ }
112
+ }