@fabricorg/platform-host 2.0.0 → 2.0.1
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/CHANGELOG.md +20 -8
- package/README.md +16 -7
- package/dist/index.cjs +60 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -2
- package/dist/index.d.ts +12 -2
- package/dist/index.js +60 -3
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,28 @@
|
|
|
1
1
|
# @fabricorg/platform-host
|
|
2
2
|
|
|
3
|
+
## 2.0.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Close final uplift audit gaps: align Host 2.x documentation and migration evidence, surface every
|
|
8
|
+
compiler/generator API to LLM consumers, make memory outbox egress directly usable, sanitize relay
|
|
9
|
+
failures, validate complete archetype state maps, support recursive JSON Schema generation, reject
|
|
10
|
+
cross-platform path traversal, synchronize artifact version stamps, and include package licenses.
|
|
11
|
+
- Require explicit domain snapshot/restore support before memory stores advertise atomic outbox
|
|
12
|
+
transactions, buffer Host lifecycle commits until success, fail missing transaction capabilities
|
|
13
|
+
before handlers run, sanitize relay failures, and exclude sensitive Host lifecycle events from bus
|
|
14
|
+
egress by default.
|
|
15
|
+
- Prevalidate buffered memory transactions so failed commits cannot append partial event or outbox rows.
|
|
16
|
+
- Qualify result-validation rollback documentation by the configured domain transaction provider.
|
|
17
|
+
- Updated dependencies
|
|
18
|
+
- @fabricorg/platform@0.11.1
|
|
19
|
+
|
|
3
20
|
## 2.0.0
|
|
4
21
|
|
|
5
|
-
###
|
|
22
|
+
### Major Changes
|
|
6
23
|
|
|
24
|
+
- Require `@fabricorg/platform@^0.11.0` and its manifest v2/result-schema contract. Applications
|
|
25
|
+
upgrading from Host 1.x must upgrade Platform in the same deployment and regenerate manifest consumers.
|
|
7
26
|
- Add the optional durable event outbox, PostgreSQL and memory adapters, and at-least-once relay.
|
|
8
27
|
- Add manifest v2 typed entity contracts, v1 reader normalization, optional action result schemas, and
|
|
9
28
|
Host enforcement of result validation before events and adapters.
|
|
@@ -14,13 +33,6 @@
|
|
|
14
33
|
- Updated dependencies
|
|
15
34
|
- @fabricorg/platform@0.11.0
|
|
16
35
|
|
|
17
|
-
## Unreleased
|
|
18
|
-
|
|
19
|
-
- Validate optional action result schemas immediately after handler execution and before events or
|
|
20
|
-
adapters. Invalid results roll back the handler transaction and persist a sanitized failure.
|
|
21
|
-
- Add optional memory and PostgreSQL outbox capabilities with atomic canonical-event records,
|
|
22
|
-
lease-based at-least-once relay, retry, replay, and dead-letter state.
|
|
23
|
-
|
|
24
36
|
## 1.0.0
|
|
25
37
|
|
|
26
38
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
The canonical host for the `@fabricorg/platform` mutation pipeline.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
pnpm add @fabricorg/platform@^0.
|
|
6
|
+
pnpm add @fabricorg/platform@^0.11.0 @fabricorg/platform-host@^2.0.0
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
## AI agent integration boundary
|
|
@@ -15,7 +15,7 @@ registration, and submit stable idempotent commands. The gateway derives tenant
|
|
|
15
15
|
calls `submitAction()`; an agent must never call handlers, adapters, workflow internals, or database
|
|
16
16
|
writes directly.
|
|
17
17
|
|
|
18
|
-
See the [Platform Host
|
|
18
|
+
See the [Platform Host 2.x agent integration
|
|
19
19
|
guide](https://platform.fabric.pro/docs/platform/reference/platform-host) for application wiring,
|
|
20
20
|
execution-time authorization, the PostgreSQL transaction binder, recovery semantics, and the external
|
|
21
21
|
gateway contract.
|
|
@@ -101,10 +101,11 @@ Stores without this additive capability retain the legacy boundary for compatibi
|
|
|
101
101
|
claim atomic domain-write/event persistence.
|
|
102
102
|
|
|
103
103
|
Actions can optionally declare `resultSchema`. The Host validates public handler result data after
|
|
104
|
-
the handler and before canonical event append or adapter execution. Invalid results
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
through an
|
|
104
|
+
the handler and before canonical event append or adapter execution. Invalid results fail the invocation,
|
|
105
|
+
emit no domain/completion event, invoke no adapter, and persist only a sanitized validation failure.
|
|
106
|
+
Domain writes roll back only when the configured store transaction provider includes those writes.
|
|
107
|
+
This validates the handler result contract; it does not observe arbitrary writes through an
|
|
108
|
+
application-owned `TDb`.
|
|
108
109
|
|
|
109
110
|
## Agent HITL
|
|
110
111
|
|
|
@@ -177,7 +178,7 @@ explain which contract and provider adapter governed a mutation after dependenci
|
|
|
177
178
|
const host = createGovernedActionHost({
|
|
178
179
|
// ...
|
|
179
180
|
runtimeEvidence: {
|
|
180
|
-
hostPackageVersion: "
|
|
181
|
+
hostPackageVersion: "2.0.1",
|
|
181
182
|
policyRulesetVersion: "gtm-rules.v8",
|
|
182
183
|
providerBridge: { name: "@fabric-harness/databricks", version: "1" },
|
|
183
184
|
},
|
|
@@ -196,3 +197,11 @@ the proven event transaction. `runOutboxRelayCycle()` leases and publishes recor
|
|
|
196
197
|
and dead-letter handling. Delivery is at least once: consumers deduplicate on immutable
|
|
197
198
|
`eventId`. Payload classification is metadata, not a redaction mechanism; event payloads must
|
|
198
199
|
already be audit-safe.
|
|
200
|
+
|
|
201
|
+
Adapter and compliance lifecycle events remain canonical but are excluded from bus egress by default.
|
|
202
|
+
Use `shouldPublish` to opt them in only when the application has made their payloads safe for that bus.
|
|
203
|
+
|
|
204
|
+
For deterministic local tests, `MemoryPlatformHostStore` enables governed Host outbox egress only
|
|
205
|
+
when its constructor receives a `MemoryPlatformHostTransactionProvider` with domain `snapshot` and
|
|
206
|
+
`restore` functions. Without that explicit rollback seam, `transactionalOutbox` is false and Host
|
|
207
|
+
construction rejects outbox configuration. PostgreSQL remains the production implementation.
|
package/dist/index.cjs
CHANGED
|
@@ -328,6 +328,9 @@ function createGovernedActionHost(options) {
|
|
|
328
328
|
let domainEvents = [];
|
|
329
329
|
try {
|
|
330
330
|
const runHandler = async (db, transaction) => {
|
|
331
|
+
if (options.outbox && !transaction?.appendEventWithOutbox) {
|
|
332
|
+
throw new Error("Outbox egress requires transactionWithEvents to provide appendEventWithOutbox before handler execution.");
|
|
333
|
+
}
|
|
331
334
|
const handlerResult = action.handler ? await action.handler(
|
|
332
335
|
{
|
|
333
336
|
actionInvocationId,
|
|
@@ -694,6 +697,12 @@ function createGovernedActionHost(options) {
|
|
|
694
697
|
...invocation.causationId ? { causationId: invocation.causationId } : {}
|
|
695
698
|
};
|
|
696
699
|
if (options.outbox) {
|
|
700
|
+
const hostLifecycleEvent = (/* @__PURE__ */ new Set(["AdapterInvocationStarted", "AdapterInvocationSucceeded", "AdapterInvocationFailed", "ComplianceBlocked"])).has(envelope.eventType);
|
|
701
|
+
const shouldPublish = options.outbox.shouldPublish?.(envelope) ?? !hostLifecycleEvent;
|
|
702
|
+
if (!shouldPublish) {
|
|
703
|
+
await (transaction ?? options.store).appendEvent(envelope);
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
697
706
|
const traceContext = options.outbox.traceContext?.(envelope);
|
|
698
707
|
const metadata = {
|
|
699
708
|
producerModuleVersion: options.outbox.producerModuleVersion(invocation.actionId, invocation.actionVersion),
|
|
@@ -849,13 +858,13 @@ async function runOutboxRelayCycle(options) {
|
|
|
849
858
|
continue;
|
|
850
859
|
}
|
|
851
860
|
result.published += 1;
|
|
852
|
-
} catch
|
|
861
|
+
} catch {
|
|
853
862
|
const deadLetter = record.attemptCount >= maxAttempts;
|
|
854
863
|
try {
|
|
855
864
|
await options.store.markOutboxFailed({
|
|
856
865
|
id: record.id,
|
|
857
866
|
workerId: options.workerId,
|
|
858
|
-
error:
|
|
867
|
+
error: "Event publisher failed",
|
|
859
868
|
availableAt: new Date(now().getTime() + (options.retryDelayMs?.(record.attemptCount) ?? 1e3)),
|
|
860
869
|
deadLetter
|
|
861
870
|
});
|
|
@@ -876,10 +885,14 @@ function cloneOutboxRecord(record) {
|
|
|
876
885
|
|
|
877
886
|
// src/memory-store.ts
|
|
878
887
|
var MemoryPlatformHostStore = class {
|
|
879
|
-
constructor(db) {
|
|
888
|
+
constructor(db, transactionProvider) {
|
|
880
889
|
this.db = db;
|
|
890
|
+
this.transactionalOutbox = transactionProvider !== void 0;
|
|
891
|
+
if (transactionProvider) this.transactionWithEvents = (run) => this.runTransactionWithEvents(run, transactionProvider);
|
|
881
892
|
}
|
|
882
893
|
db;
|
|
894
|
+
transactionalOutbox;
|
|
895
|
+
transactionTail = Promise.resolve();
|
|
883
896
|
invocations = [];
|
|
884
897
|
policyEvaluations = [];
|
|
885
898
|
adapterInvocations = [];
|
|
@@ -891,6 +904,50 @@ var MemoryPlatformHostStore = class {
|
|
|
891
904
|
async transaction(run) {
|
|
892
905
|
return run(this.db);
|
|
893
906
|
}
|
|
907
|
+
async runTransactionWithEvents(run, transactionProvider) {
|
|
908
|
+
let release;
|
|
909
|
+
const previous = this.transactionTail;
|
|
910
|
+
this.transactionTail = new Promise((resolve) => {
|
|
911
|
+
release = resolve;
|
|
912
|
+
});
|
|
913
|
+
await previous;
|
|
914
|
+
let domainSnapshot;
|
|
915
|
+
let snapshotCreated = false;
|
|
916
|
+
try {
|
|
917
|
+
domainSnapshot = transactionProvider.snapshot(this.db);
|
|
918
|
+
snapshotCreated = true;
|
|
919
|
+
const pendingEvents = [];
|
|
920
|
+
const pendingUpdates = [];
|
|
921
|
+
const appendPending = async (event, metadata) => {
|
|
922
|
+
if (this.events.some((candidate) => candidate.id === event.id) || pendingEvents.some((candidate) => candidate.event.id === event.id)) return;
|
|
923
|
+
pendingEvents.push({ event, ...metadata ? { metadata } : {} });
|
|
924
|
+
};
|
|
925
|
+
const result = await run({
|
|
926
|
+
db: this.db,
|
|
927
|
+
appendEvent: (event) => appendPending(event),
|
|
928
|
+
appendEventWithOutbox: (event, metadata) => appendPending(event, metadata),
|
|
929
|
+
nextEventSequence: async (tenantId, spaceId) => (await this.listEvents(tenantId, spaceId)).length + pendingEvents.filter((candidate) => candidate.event.tenantId === tenantId && candidate.event.spaceId === spaceId).length + 1,
|
|
930
|
+
listEvents: async (tenantId, spaceId) => [...await this.listEvents(tenantId, spaceId), ...pendingEvents.map((candidate) => candidate.event).filter((event) => event.tenantId === tenantId && event.spaceId === spaceId)],
|
|
931
|
+
updateActionInvocation: async (id, tenantId, spaceId, patch) => {
|
|
932
|
+
pendingUpdates.push({ id, tenantId, spaceId, patch });
|
|
933
|
+
}
|
|
934
|
+
});
|
|
935
|
+
for (const update of pendingUpdates) {
|
|
936
|
+
if (!await this.getActionInvocation(update.id, update.tenantId, update.spaceId)) throw new Error(`ActionInvocation not found: ${update.id}`);
|
|
937
|
+
}
|
|
938
|
+
for (const pending of pendingEvents) {
|
|
939
|
+
if (pending.metadata) await this.appendEventWithOutbox(pending.event, pending.metadata);
|
|
940
|
+
else await this.appendEvent(pending.event);
|
|
941
|
+
}
|
|
942
|
+
for (const update of pendingUpdates) await this.updateActionInvocation(update.id, update.tenantId, update.spaceId, update.patch);
|
|
943
|
+
return result;
|
|
944
|
+
} catch (error) {
|
|
945
|
+
if (snapshotCreated) transactionProvider.restore(this.db, domainSnapshot);
|
|
946
|
+
throw error;
|
|
947
|
+
} finally {
|
|
948
|
+
release();
|
|
949
|
+
}
|
|
950
|
+
}
|
|
894
951
|
async createActionInvocation(input) {
|
|
895
952
|
if (input.idempotencyKey) {
|
|
896
953
|
const existing = this.invocations.find(
|