@fabricorg/platform-host 8.0.0 → 8.1.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/CHANGELOG.md +67 -0
- package/README.md +95 -0
- package/dist/index.cjs +576 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +106 -0
- package/dist/index.d.ts +106 -0
- package/dist/index.js +575 -35
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -554,6 +554,13 @@ export interface PlatformHostStore<TDb> {
|
|
|
554
554
|
* optional only for compatibility with existing stores and direct callers.
|
|
555
555
|
*/
|
|
556
556
|
updateAdapterInvocation(id: string, patch: Partial<Pick<AdapterInvocationRecord, "status" | "output" | "error" | "attempt" | "updatedAt" | "reconciliation">>, scope?: AdapterInvocationScope): Promise<void>;
|
|
557
|
+
/**
|
|
558
|
+
* First-write-wins: an event ID the store already holds, in any tenant or space, is a no-op
|
|
559
|
+
* that keeps the first event. Do not throw, replace or merge it, regardless of
|
|
560
|
+
* the later payload, sequence or timestamps. Host re-appends lifecycle events
|
|
561
|
+
* under stable IDs on retry/recovery with fresh sequences and timestamps and
|
|
562
|
+
* potentially different payloads.
|
|
563
|
+
*/
|
|
557
564
|
appendEvent(event: AssetEventEnvelope): Promise<void>;
|
|
558
565
|
nextEventSequence(tenantId: string, spaceId: string): Promise<number>;
|
|
559
566
|
/**
|
|
@@ -588,7 +595,22 @@ export interface PlatformHostMutationTransaction<TDb> {
|
|
|
588
595
|
* Required by serialized saga parent lifecycle transitions.
|
|
589
596
|
*/
|
|
590
597
|
getEvent?(id: string, tenantId: string, spaceId: string): Promise<AssetEventEnvelope | undefined>;
|
|
598
|
+
/**
|
|
599
|
+
* First-write-wins: an event ID the store already holds, in any tenant or space, is a no-op
|
|
600
|
+
* that keeps the first event. Do not throw, replace or merge it, regardless of
|
|
601
|
+
* the later payload, sequence or timestamps. Host re-appends lifecycle events
|
|
602
|
+
* under stable IDs on retry/recovery with fresh sequences and timestamps and
|
|
603
|
+
* potentially different payloads.
|
|
604
|
+
*/
|
|
591
605
|
appendEvent(event: AssetEventEnvelope): Promise<void>;
|
|
606
|
+
/**
|
|
607
|
+
* First-write-wins: an event ID the store already holds, in any tenant or space, is a no-op
|
|
608
|
+
* that keeps the first event and adds no outbox row, even when the
|
|
609
|
+
* first append had none. Do not throw,
|
|
610
|
+
* replace or merge it, regardless of the later payload, sequence or timestamps.
|
|
611
|
+
* Host re-appends lifecycle events under stable IDs on retry/recovery with fresh
|
|
612
|
+
* sequences and timestamps and potentially different payloads.
|
|
613
|
+
*/
|
|
592
614
|
appendEventWithOutbox?(event: AssetEventEnvelope, metadata: OutboxEventMetadata): Promise<void>;
|
|
593
615
|
nextEventSequence(tenantId: string, spaceId: string): Promise<number>;
|
|
594
616
|
listEvents(tenantId: string, spaceId: string): Promise<AssetEventEnvelope[]>;
|
|
@@ -706,6 +728,14 @@ export interface ClaimOutboxInput {
|
|
|
706
728
|
export interface OutboxPlatformHostStore<TDb> extends PlatformHostStore<TDb> {
|
|
707
729
|
/** True only when transactionWithEvents supplies appendEventWithOutbox in the same unit of work. */
|
|
708
730
|
readonly transactionalOutbox?: boolean;
|
|
731
|
+
/**
|
|
732
|
+
* First-write-wins: an event ID the store already holds, in any tenant or space, is a no-op
|
|
733
|
+
* that keeps the first event and adds no outbox row, even when the
|
|
734
|
+
* first append had none. Do not throw,
|
|
735
|
+
* replace or merge it, regardless of the later payload, sequence or timestamps.
|
|
736
|
+
* Host re-appends lifecycle events under stable IDs on retry/recovery with fresh
|
|
737
|
+
* sequences and timestamps and potentially different payloads.
|
|
738
|
+
*/
|
|
709
739
|
appendEventWithOutbox(event: AssetEventEnvelope, metadata: OutboxEventMetadata): Promise<void>;
|
|
710
740
|
claimOutbox(input: ClaimOutboxInput): Promise<OutboxRecord[]>;
|
|
711
741
|
markOutboxPublished(id: string, workerId: string, publishedAt: Date, leaseToken?: number): Promise<void>;
|
|
@@ -1766,4 +1796,80 @@ export declare function tenantIsolationChecks(): readonly TenantIsolationCheck[]
|
|
|
1766
1796
|
/** Run every check, returning each result rather than throwing on the first. */
|
|
1767
1797
|
export declare function runTenantIsolationChecks(subject: TenantIsolationSubject): Promise<TenantIsolationResult>;
|
|
1768
1798
|
//#endregion
|
|
1799
|
+
//#region src/event-append-conformance.d.ts
|
|
1800
|
+
export interface EventAppendSubject<TDb = unknown> {
|
|
1801
|
+
store: Pick<PlatformHostStore<TDb>, "appendEvent"> & Pick<RecoverablePlatformHostStore<TDb>, "listEvents"> & Partial<Pick<OutboxPlatformHostStore<TDb>, "appendEventWithOutbox" | "listOutbox" | "claimOutbox" | "markOutboxPublished" | "markOutboxFailed">> & Partial<Pick<AtomicMutationPlatformHostStore<TDb>, "transactionWithEvents">>;
|
|
1802
|
+
tenantId: string;
|
|
1803
|
+
/** Optional: restore a known state before a check; probes tolerate accumulated state. */
|
|
1804
|
+
reset?(): Promise<void> | void;
|
|
1805
|
+
}
|
|
1806
|
+
export interface EventAppendCheck {
|
|
1807
|
+
id: "fabric.store.event-append-first-write-wins.v1" | "fabric.store.event-append-outbox-first-write-wins.v1" | "fabric.store.event-append-outbox-after-plain.v1" | "fabric.store.event-append-transaction-first-write-wins.v1" | "fabric.store.event-append-transaction-outbox-first-write-wins.v1" | "fabric.store.event-append-transaction-outbox-after-plain.v1";
|
|
1808
|
+
run<TDb>(subject: EventAppendSubject<TDb>): Promise<string[]>;
|
|
1809
|
+
}
|
|
1810
|
+
export interface EventAppendResult {
|
|
1811
|
+
passed: boolean;
|
|
1812
|
+
checks: Array<{
|
|
1813
|
+
id: EventAppendCheck["id"];
|
|
1814
|
+
status: "passed" | "failed" | "skipped";
|
|
1815
|
+
evidence: string[];
|
|
1816
|
+
error?: string;
|
|
1817
|
+
}>;
|
|
1818
|
+
}
|
|
1819
|
+
/**
|
|
1820
|
+
* Six checks detecting common first-write-wins violations through plain,
|
|
1821
|
+
* outbox and transaction appends, including outbox-after-plain and committed
|
|
1822
|
+
* duplicates: throwing, replacing, merging, keeping both events, selecting the
|
|
1823
|
+
* earlier-ordered or later-ordered duplicate, adding outbox rows, and resetting
|
|
1824
|
+
* or rewriting existing outbox rows in the probed states. They are not exhaustive.
|
|
1825
|
+
* Duplicates have both earlier and later ordering fields and vary the remaining
|
|
1826
|
+
* envelope and outbox metadata fields. Whole-record snapshots compare duplicates
|
|
1827
|
+
* while pending, leased by the probe, retry-pending, and settled as published.
|
|
1828
|
+
* Retry-pending accepts a cleared or expired lease, the probe owner retained
|
|
1829
|
+
* without an expiry, or an expiry within five seconds of the probe's clock,
|
|
1830
|
+
* which is waited out before the row is claimed again; attempt counts and
|
|
1831
|
+
* error formatting are not prescribed.
|
|
1832
|
+
* Initial comparisons accept store-specific fields and fields the store omits
|
|
1833
|
+
* or returns as null; transaction verification runs after commit without requiring
|
|
1834
|
+
* read-your-writes. Each committed duplicate transaction also appends a fresh plain
|
|
1835
|
+
* control event whose presence is verified after commit.
|
|
1836
|
+
* Successful outbox-creating checks leave the probe row
|
|
1837
|
+
* published although nothing was sent; its event-append-probe space identifies
|
|
1838
|
+
* it. Absence checks write a control row that cleanup dead-letters. These rows can
|
|
1839
|
+
* be requeued by operator tooling; exclude probe spaces from real publication.
|
|
1840
|
+
*
|
|
1841
|
+
* Intended for test or staging stores with outbox relays paused for the probe
|
|
1842
|
+
* tenant. Event and availability inputs use whole seconds a few seconds in the
|
|
1843
|
+
* past; claims and publication markers use real current time. Checks leave
|
|
1844
|
+
* append-only events in fresh event-append-probe spaces. Final cleanup
|
|
1845
|
+
* attempts to dead-letter visible probe rows still pending with
|
|
1846
|
+
* "event append probe; never published".
|
|
1847
|
+
* Claims use real current time, a pending-probe-row limit, and a 30-second lease.
|
|
1848
|
+
* A filter-ignoring claim can lease foreign rows and make whatever writes the
|
|
1849
|
+
* claim itself makes (such as incrementing attempts). Checks write nothing to
|
|
1850
|
+
* foreign rows and stop claiming for that run (or that standalone check).
|
|
1851
|
+
* After a filter violation, cleanup can dead-letter only probe rows returned by
|
|
1852
|
+
* that claim, using the probe worker and claim tokens without further claims.
|
|
1853
|
+
* Failures list visible probe rows still pending and instruct manual dead-lettering.
|
|
1854
|
+
* Later outbox checks in the run fail as skipped because of the earlier filter
|
|
1855
|
+
* violation. Leases lapse within 30 seconds
|
|
1856
|
+
* of the claim if the store honours the lease duration.
|
|
1857
|
+
*
|
|
1858
|
+
* Missing append capabilities skip; missing listOutbox, claimOutbox,
|
|
1859
|
+
* markOutboxPublished or markOutboxFailed fails verification/cleanup.
|
|
1860
|
+
* Known limits: rules comparing ordering fields in mixed directions, store-wide
|
|
1861
|
+
* uniqueness across spaces or tenants, the dead-letter state, claims that
|
|
1862
|
+
* lease foreign rows without returning them, buffered merges into fields the
|
|
1863
|
+
* store never returns, and duplicate handling that only holds within one
|
|
1864
|
+
* process. Stores requiring gapless sequences, or unique keys over fields the
|
|
1865
|
+
* control events share with the probe event, can fail falsely.
|
|
1866
|
+
*/
|
|
1867
|
+
export declare function eventAppendChecks(): readonly EventAppendCheck[];
|
|
1868
|
+
/**
|
|
1869
|
+
* Run eventAppendChecks and collect results instead of stopping at the first
|
|
1870
|
+
* failure. Use test or staging stores with relays paused for the probe tenant;
|
|
1871
|
+
* see eventAppendChecks for probe publication markers and cleanup side effects.
|
|
1872
|
+
*/
|
|
1873
|
+
export declare function runEventAppendChecks<TDb>(subject: EventAppendSubject<TDb>): Promise<EventAppendResult>;
|
|
1874
|
+
//#endregion
|
|
1769
1875
|
export { canonicalJson };
|
package/dist/index.d.ts
CHANGED
|
@@ -554,6 +554,13 @@ export interface PlatformHostStore<TDb> {
|
|
|
554
554
|
* optional only for compatibility with existing stores and direct callers.
|
|
555
555
|
*/
|
|
556
556
|
updateAdapterInvocation(id: string, patch: Partial<Pick<AdapterInvocationRecord, "status" | "output" | "error" | "attempt" | "updatedAt" | "reconciliation">>, scope?: AdapterInvocationScope): Promise<void>;
|
|
557
|
+
/**
|
|
558
|
+
* First-write-wins: an event ID the store already holds, in any tenant or space, is a no-op
|
|
559
|
+
* that keeps the first event. Do not throw, replace or merge it, regardless of
|
|
560
|
+
* the later payload, sequence or timestamps. Host re-appends lifecycle events
|
|
561
|
+
* under stable IDs on retry/recovery with fresh sequences and timestamps and
|
|
562
|
+
* potentially different payloads.
|
|
563
|
+
*/
|
|
557
564
|
appendEvent(event: AssetEventEnvelope): Promise<void>;
|
|
558
565
|
nextEventSequence(tenantId: string, spaceId: string): Promise<number>;
|
|
559
566
|
/**
|
|
@@ -588,7 +595,22 @@ export interface PlatformHostMutationTransaction<TDb> {
|
|
|
588
595
|
* Required by serialized saga parent lifecycle transitions.
|
|
589
596
|
*/
|
|
590
597
|
getEvent?(id: string, tenantId: string, spaceId: string): Promise<AssetEventEnvelope | undefined>;
|
|
598
|
+
/**
|
|
599
|
+
* First-write-wins: an event ID the store already holds, in any tenant or space, is a no-op
|
|
600
|
+
* that keeps the first event. Do not throw, replace or merge it, regardless of
|
|
601
|
+
* the later payload, sequence or timestamps. Host re-appends lifecycle events
|
|
602
|
+
* under stable IDs on retry/recovery with fresh sequences and timestamps and
|
|
603
|
+
* potentially different payloads.
|
|
604
|
+
*/
|
|
591
605
|
appendEvent(event: AssetEventEnvelope): Promise<void>;
|
|
606
|
+
/**
|
|
607
|
+
* First-write-wins: an event ID the store already holds, in any tenant or space, is a no-op
|
|
608
|
+
* that keeps the first event and adds no outbox row, even when the
|
|
609
|
+
* first append had none. Do not throw,
|
|
610
|
+
* replace or merge it, regardless of the later payload, sequence or timestamps.
|
|
611
|
+
* Host re-appends lifecycle events under stable IDs on retry/recovery with fresh
|
|
612
|
+
* sequences and timestamps and potentially different payloads.
|
|
613
|
+
*/
|
|
592
614
|
appendEventWithOutbox?(event: AssetEventEnvelope, metadata: OutboxEventMetadata): Promise<void>;
|
|
593
615
|
nextEventSequence(tenantId: string, spaceId: string): Promise<number>;
|
|
594
616
|
listEvents(tenantId: string, spaceId: string): Promise<AssetEventEnvelope[]>;
|
|
@@ -706,6 +728,14 @@ export interface ClaimOutboxInput {
|
|
|
706
728
|
export interface OutboxPlatformHostStore<TDb> extends PlatformHostStore<TDb> {
|
|
707
729
|
/** True only when transactionWithEvents supplies appendEventWithOutbox in the same unit of work. */
|
|
708
730
|
readonly transactionalOutbox?: boolean;
|
|
731
|
+
/**
|
|
732
|
+
* First-write-wins: an event ID the store already holds, in any tenant or space, is a no-op
|
|
733
|
+
* that keeps the first event and adds no outbox row, even when the
|
|
734
|
+
* first append had none. Do not throw,
|
|
735
|
+
* replace or merge it, regardless of the later payload, sequence or timestamps.
|
|
736
|
+
* Host re-appends lifecycle events under stable IDs on retry/recovery with fresh
|
|
737
|
+
* sequences and timestamps and potentially different payloads.
|
|
738
|
+
*/
|
|
709
739
|
appendEventWithOutbox(event: AssetEventEnvelope, metadata: OutboxEventMetadata): Promise<void>;
|
|
710
740
|
claimOutbox(input: ClaimOutboxInput): Promise<OutboxRecord[]>;
|
|
711
741
|
markOutboxPublished(id: string, workerId: string, publishedAt: Date, leaseToken?: number): Promise<void>;
|
|
@@ -1766,4 +1796,80 @@ export declare function tenantIsolationChecks(): readonly TenantIsolationCheck[]
|
|
|
1766
1796
|
/** Run every check, returning each result rather than throwing on the first. */
|
|
1767
1797
|
export declare function runTenantIsolationChecks(subject: TenantIsolationSubject): Promise<TenantIsolationResult>;
|
|
1768
1798
|
//#endregion
|
|
1799
|
+
//#region src/event-append-conformance.d.ts
|
|
1800
|
+
export interface EventAppendSubject<TDb = unknown> {
|
|
1801
|
+
store: Pick<PlatformHostStore<TDb>, "appendEvent"> & Pick<RecoverablePlatformHostStore<TDb>, "listEvents"> & Partial<Pick<OutboxPlatformHostStore<TDb>, "appendEventWithOutbox" | "listOutbox" | "claimOutbox" | "markOutboxPublished" | "markOutboxFailed">> & Partial<Pick<AtomicMutationPlatformHostStore<TDb>, "transactionWithEvents">>;
|
|
1802
|
+
tenantId: string;
|
|
1803
|
+
/** Optional: restore a known state before a check; probes tolerate accumulated state. */
|
|
1804
|
+
reset?(): Promise<void> | void;
|
|
1805
|
+
}
|
|
1806
|
+
export interface EventAppendCheck {
|
|
1807
|
+
id: "fabric.store.event-append-first-write-wins.v1" | "fabric.store.event-append-outbox-first-write-wins.v1" | "fabric.store.event-append-outbox-after-plain.v1" | "fabric.store.event-append-transaction-first-write-wins.v1" | "fabric.store.event-append-transaction-outbox-first-write-wins.v1" | "fabric.store.event-append-transaction-outbox-after-plain.v1";
|
|
1808
|
+
run<TDb>(subject: EventAppendSubject<TDb>): Promise<string[]>;
|
|
1809
|
+
}
|
|
1810
|
+
export interface EventAppendResult {
|
|
1811
|
+
passed: boolean;
|
|
1812
|
+
checks: Array<{
|
|
1813
|
+
id: EventAppendCheck["id"];
|
|
1814
|
+
status: "passed" | "failed" | "skipped";
|
|
1815
|
+
evidence: string[];
|
|
1816
|
+
error?: string;
|
|
1817
|
+
}>;
|
|
1818
|
+
}
|
|
1819
|
+
/**
|
|
1820
|
+
* Six checks detecting common first-write-wins violations through plain,
|
|
1821
|
+
* outbox and transaction appends, including outbox-after-plain and committed
|
|
1822
|
+
* duplicates: throwing, replacing, merging, keeping both events, selecting the
|
|
1823
|
+
* earlier-ordered or later-ordered duplicate, adding outbox rows, and resetting
|
|
1824
|
+
* or rewriting existing outbox rows in the probed states. They are not exhaustive.
|
|
1825
|
+
* Duplicates have both earlier and later ordering fields and vary the remaining
|
|
1826
|
+
* envelope and outbox metadata fields. Whole-record snapshots compare duplicates
|
|
1827
|
+
* while pending, leased by the probe, retry-pending, and settled as published.
|
|
1828
|
+
* Retry-pending accepts a cleared or expired lease, the probe owner retained
|
|
1829
|
+
* without an expiry, or an expiry within five seconds of the probe's clock,
|
|
1830
|
+
* which is waited out before the row is claimed again; attempt counts and
|
|
1831
|
+
* error formatting are not prescribed.
|
|
1832
|
+
* Initial comparisons accept store-specific fields and fields the store omits
|
|
1833
|
+
* or returns as null; transaction verification runs after commit without requiring
|
|
1834
|
+
* read-your-writes. Each committed duplicate transaction also appends a fresh plain
|
|
1835
|
+
* control event whose presence is verified after commit.
|
|
1836
|
+
* Successful outbox-creating checks leave the probe row
|
|
1837
|
+
* published although nothing was sent; its event-append-probe space identifies
|
|
1838
|
+
* it. Absence checks write a control row that cleanup dead-letters. These rows can
|
|
1839
|
+
* be requeued by operator tooling; exclude probe spaces from real publication.
|
|
1840
|
+
*
|
|
1841
|
+
* Intended for test or staging stores with outbox relays paused for the probe
|
|
1842
|
+
* tenant. Event and availability inputs use whole seconds a few seconds in the
|
|
1843
|
+
* past; claims and publication markers use real current time. Checks leave
|
|
1844
|
+
* append-only events in fresh event-append-probe spaces. Final cleanup
|
|
1845
|
+
* attempts to dead-letter visible probe rows still pending with
|
|
1846
|
+
* "event append probe; never published".
|
|
1847
|
+
* Claims use real current time, a pending-probe-row limit, and a 30-second lease.
|
|
1848
|
+
* A filter-ignoring claim can lease foreign rows and make whatever writes the
|
|
1849
|
+
* claim itself makes (such as incrementing attempts). Checks write nothing to
|
|
1850
|
+
* foreign rows and stop claiming for that run (or that standalone check).
|
|
1851
|
+
* After a filter violation, cleanup can dead-letter only probe rows returned by
|
|
1852
|
+
* that claim, using the probe worker and claim tokens without further claims.
|
|
1853
|
+
* Failures list visible probe rows still pending and instruct manual dead-lettering.
|
|
1854
|
+
* Later outbox checks in the run fail as skipped because of the earlier filter
|
|
1855
|
+
* violation. Leases lapse within 30 seconds
|
|
1856
|
+
* of the claim if the store honours the lease duration.
|
|
1857
|
+
*
|
|
1858
|
+
* Missing append capabilities skip; missing listOutbox, claimOutbox,
|
|
1859
|
+
* markOutboxPublished or markOutboxFailed fails verification/cleanup.
|
|
1860
|
+
* Known limits: rules comparing ordering fields in mixed directions, store-wide
|
|
1861
|
+
* uniqueness across spaces or tenants, the dead-letter state, claims that
|
|
1862
|
+
* lease foreign rows without returning them, buffered merges into fields the
|
|
1863
|
+
* store never returns, and duplicate handling that only holds within one
|
|
1864
|
+
* process. Stores requiring gapless sequences, or unique keys over fields the
|
|
1865
|
+
* control events share with the probe event, can fail falsely.
|
|
1866
|
+
*/
|
|
1867
|
+
export declare function eventAppendChecks(): readonly EventAppendCheck[];
|
|
1868
|
+
/**
|
|
1869
|
+
* Run eventAppendChecks and collect results instead of stopping at the first
|
|
1870
|
+
* failure. Use test or staging stores with relays paused for the probe tenant;
|
|
1871
|
+
* see eventAppendChecks for probe publication markers and cleanup side effects.
|
|
1872
|
+
*/
|
|
1873
|
+
export declare function runEventAppendChecks<TDb>(subject: EventAppendSubject<TDb>): Promise<EventAppendResult>;
|
|
1874
|
+
//#endregion
|
|
1769
1875
|
export { canonicalJson };
|