@unconfirmed/sui-effect 0.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/AGENTS.md +209 -0
- package/LICENSE +21 -0
- package/LLMS.md +6707 -0
- package/README.md +300 -0
- package/dist/domain/bcs.d.ts +101 -0
- package/dist/domain/bcs.d.ts.map +1 -0
- package/dist/domain/bcs.js +183 -0
- package/dist/domain/bcs.js.map +1 -0
- package/dist/domain/errors.d.ts +595 -0
- package/dist/domain/errors.d.ts.map +1 -0
- package/dist/domain/errors.js +476 -0
- package/dist/domain/errors.js.map +1 -0
- package/dist/domain/executed.d.ts +325 -0
- package/dist/domain/executed.d.ts.map +1 -0
- package/dist/domain/executed.js +253 -0
- package/dist/domain/executed.js.map +1 -0
- package/dist/domain/journal-entry.d.ts +468 -0
- package/dist/domain/journal-entry.d.ts.map +1 -0
- package/dist/domain/journal-entry.js +63 -0
- package/dist/domain/journal-entry.js.map +1 -0
- package/dist/domain/schemas.d.ts +950 -0
- package/dist/domain/schemas.d.ts.map +1 -0
- package/dist/domain/schemas.js +571 -0
- package/dist/domain/schemas.js.map +1 -0
- package/dist/domain/sui-schema.d.ts +12 -0
- package/dist/domain/sui-schema.d.ts.map +1 -0
- package/dist/domain/sui-schema.js +12 -0
- package/dist/domain/sui-schema.js.map +1 -0
- package/dist/extension.d.ts +7 -0
- package/dist/extension.d.ts.map +1 -0
- package/dist/extension.js +7 -0
- package/dist/extension.js.map +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/internal.d.ts +18 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +18 -0
- package/dist/internal.js.map +1 -0
- package/dist/journal.d.ts +51 -0
- package/dist/journal.d.ts.map +1 -0
- package/dist/journal.js +18 -0
- package/dist/journal.js.map +1 -0
- package/dist/script.d.ts +7 -0
- package/dist/script.d.ts.map +1 -0
- package/dist/script.js +7 -0
- package/dist/script.js.map +1 -0
- package/dist/services/Journal.d.ts +76 -0
- package/dist/services/Journal.d.ts.map +1 -0
- package/dist/services/Journal.js +70 -0
- package/dist/services/Journal.js.map +1 -0
- package/dist/services/JournalKeyValueStore.d.ts +73 -0
- package/dist/services/JournalKeyValueStore.d.ts.map +1 -0
- package/dist/services/JournalKeyValueStore.js +155 -0
- package/dist/services/JournalKeyValueStore.js.map +1 -0
- package/dist/services/Script.d.ts +210 -0
- package/dist/services/Script.d.ts.map +1 -0
- package/dist/services/Script.js +441 -0
- package/dist/services/Script.js.map +1 -0
- package/dist/services/Signer.d.ts +132 -0
- package/dist/services/Signer.d.ts.map +1 -0
- package/dist/services/Signer.js +158 -0
- package/dist/services/Signer.js.map +1 -0
- package/dist/services/SubmitConfig.d.ts +175 -0
- package/dist/services/SubmitConfig.d.ts.map +1 -0
- package/dist/services/SubmitConfig.js +52 -0
- package/dist/services/SubmitConfig.js.map +1 -0
- package/dist/services/Sui.d.ts +295 -0
- package/dist/services/Sui.d.ts.map +1 -0
- package/dist/services/Sui.js +414 -0
- package/dist/services/Sui.js.map +1 -0
- package/dist/services/SuiCore.d.ts +227 -0
- package/dist/services/SuiCore.d.ts.map +1 -0
- package/dist/services/SuiCore.js +400 -0
- package/dist/services/SuiCore.js.map +1 -0
- package/dist/services/SuiCoreFake.d.ts +278 -0
- package/dist/services/SuiCoreFake.d.ts.map +1 -0
- package/dist/services/SuiCoreFake.js +1003 -0
- package/dist/services/SuiCoreFake.js.map +1 -0
- package/dist/services/SuiExtension.d.ts +208 -0
- package/dist/services/SuiExtension.d.ts.map +1 -0
- package/dist/services/SuiExtension.js +355 -0
- package/dist/services/SuiExtension.js.map +1 -0
- package/dist/services/SuiGraphQL.d.ts +98 -0
- package/dist/services/SuiGraphQL.d.ts.map +1 -0
- package/dist/services/SuiGraphQL.js +118 -0
- package/dist/services/SuiGraphQL.js.map +1 -0
- package/dist/services/Tx.d.ts +687 -0
- package/dist/services/Tx.d.ts.map +1 -0
- package/dist/services/Tx.js +1224 -0
- package/dist/services/Tx.js.map +1 -0
- package/dist/testing.d.ts +88 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +163 -0
- package/dist/testing.js.map +1 -0
- package/dist/tx.d.ts +24 -0
- package/dist/tx.d.ts.map +1 -0
- package/dist/tx.js +24 -0
- package/dist/tx.js.map +1 -0
- package/docs/extensions.md +1685 -0
- package/examples/extension-template/README.md +170 -0
- package/examples/extension-template/package.json +44 -0
- package/examples/extension-template/scripts/check-package.ts +147 -0
- package/examples/extension-template/src/Escrow.ts +445 -0
- package/examples/extension-template/src/Platform.ts +136 -0
- package/examples/extension-template/src/errors.ts +61 -0
- package/examples/extension-template/src/extension.ts +63 -0
- package/examples/extension-template/src/index.ts +34 -0
- package/examples/extension-template/src/schema.ts +158 -0
- package/examples/extension-template/src/upstream.ts +66 -0
- package/examples/extension-template/test/escrow.test.ts +559 -0
- package/examples/extension-template/tsconfig.build.json +26 -0
- package/examples/extension-template/tsconfig.json +35 -0
- package/package.json +87 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension.js","sourceRoot":"","sources":["../src/extension.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAEL,WAAW,EAEX,YAAY,EAEb,MAAM,4BAA4B,CAAA"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sui-effect: an opinionated Effect v4 layer over `@mysten/sui`.
|
|
3
|
+
*
|
|
4
|
+
* Two tiers: `SuiCore` mirrors the SDK method for method, `Sui` makes the
|
|
5
|
+
* opinionated decisions. Every public function states its error union in words,
|
|
6
|
+
* every failure is a `Schema.TaggedError`, and every boundary decodes through
|
|
7
|
+
* `Schema`.
|
|
8
|
+
*
|
|
9
|
+
* Everything this module exports is public API. The machinery behind it lives
|
|
10
|
+
* in `sui-effect/internal`, which is not in the package `exports` map.
|
|
11
|
+
*
|
|
12
|
+
* @since 0.1.0
|
|
13
|
+
*/
|
|
14
|
+
/** The closed error taxonomy and its four helpers. */
|
|
15
|
+
export { BuildError, DecodeError, ExecutionFailed, ExtensionNotReady, GraphQLUnavailable, type HasOutcome, JournalError, NetworkMismatch, NotApplied, ObjectDeleted, ObjectNotFound, ObjectUnavailable, type Outcome, PolicyDenied, SigningError, SimulationFailed, SubmissionUnknown, SuiError, TransactionNotFound, TransportError, UnexpectedEffects } from "./domain/errors.ts";
|
|
16
|
+
/** Branded schemas and the transport-neutral object model. */
|
|
17
|
+
export { Balance, BalanceChange, Built, ChangedObject, CleverError, CoinType, CommandResult, Digest, DynamicField, DynamicFieldEntry, DynamicFieldName, Event, ExecutionReason, ExecutionStatus, GasCostSummary, chainOf, KNOWN_CHAIN_IDS, KnownNetwork, maxEpochOf, Mist, maxTimestampMsOf, MoveLocation, Network, NotAppliedEvidence, ObjectEnvelope, ObjectId, ObjectRef, ObjectType, Owner, Signature, SignedTransaction, Simulation, StructTag, SuiAddress, type SuiObject, TransactionEffects, TransactionExpiration, UnchangedConsensusObject, Version } from "./domain/schemas.ts";
|
|
18
|
+
/** A transaction that reached the chain, with accessors over its effects. */
|
|
19
|
+
export { type ChangedRef, Executed, objectRefOf, type SdkObjectRef, sdkRefOf } from "./domain/executed.ts";
|
|
20
|
+
/** The opinionated tier. */
|
|
21
|
+
export { type BatchItemError, type GetObjectError, type Recipe, Sui, type SuiLayerOptions, type SuiService } from "./services/Sui.ts";
|
|
22
|
+
/** The mechanical tier. */
|
|
23
|
+
export { defaultGrpcUrl, type ObjectLookupError, type SimulationLookupError, SuiCore, type SuiCoreError, type SuiCoreService, type SuiGrpcLayerOptions, type TransactionLookupError, type VersionedObject } from "./services/SuiCore.ts";
|
|
24
|
+
/** The SDK's GraphQL client as one shared tag. sui-effect wraps no GraphQL API. */
|
|
25
|
+
export { SuiGraphQL } from "./services/SuiGraphQL.ts";
|
|
26
|
+
/**
|
|
27
|
+
* The BCS bridge, namespaced the way the spec spells it:
|
|
28
|
+
* `SuiSchema.bcs(bcsType, expectedType)`.
|
|
29
|
+
*/
|
|
30
|
+
export * as SuiSchema from "./domain/sui-schema.ts";
|
|
31
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,sDAAsD;AACtD,OAAO,EACL,UAAU,EACV,WAAW,EACX,eAAe,EACf,iBAAiB,EACjB,kBAAkB,EAClB,KAAK,UAAU,EACf,YAAY,EACZ,eAAe,EACf,UAAU,EACV,aAAa,EACb,cAAc,EACd,iBAAiB,EACjB,KAAK,OAAO,EACZ,YAAY,EACZ,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,QAAQ,EACR,mBAAmB,EACnB,cAAc,EACd,iBAAiB,EAClB,MAAM,oBAAoB,CAAA;AAE3B,8DAA8D;AAC9D,OAAO,EACL,OAAO,EACP,aAAa,EACb,KAAK,EACL,aAAa,EACb,WAAW,EACX,QAAQ,EACR,aAAa,EACb,MAAM,EACN,YAAY,EACZ,iBAAiB,EACjB,gBAAgB,EAChB,KAAK,EACL,eAAe,EACf,eAAe,EACf,cAAc,EACd,OAAO,EACP,eAAe,EACf,YAAY,EACZ,UAAU,EACV,IAAI,EACJ,gBAAgB,EAChB,YAAY,EACZ,OAAO,EACP,kBAAkB,EAClB,cAAc,EACd,QAAQ,EACR,SAAS,EACT,UAAU,EACV,KAAK,EACL,SAAS,EACT,iBAAiB,EACjB,UAAU,EACV,SAAS,EACT,UAAU,EACV,KAAK,SAAS,EACd,kBAAkB,EAClB,qBAAqB,EACrB,wBAAwB,EACxB,OAAO,EACR,MAAM,qBAAqB,CAAA;AAE5B,6EAA6E;AAC7E,OAAO,EACL,KAAK,UAAU,EACf,QAAQ,EACR,WAAW,EACX,KAAK,YAAY,EACjB,QAAQ,EACT,MAAM,sBAAsB,CAAA;AAE7B,4BAA4B;AAC5B,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,MAAM,EACX,GAAG,EACH,KAAK,eAAe,EACpB,KAAK,UAAU,EAChB,MAAM,mBAAmB,CAAA;AAE1B,2BAA2B;AAC3B,OAAO,EACL,cAAc,EACd,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,EAC1B,OAAO,EACP,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,mBAAmB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,eAAe,EACrB,MAAM,uBAAuB,CAAA;AAE9B,mFAAmF;AACnF,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAA;AAErD;;;GAGG;AACH,OAAO,KAAK,SAAS,MAAM,wBAAwB,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sui-effect: an opinionated Effect v4 layer over `@mysten/sui`.
|
|
3
|
+
*
|
|
4
|
+
* Two tiers: `SuiCore` mirrors the SDK method for method, `Sui` makes the
|
|
5
|
+
* opinionated decisions. Every public function states its error union in words,
|
|
6
|
+
* every failure is a `Schema.TaggedError`, and every boundary decodes through
|
|
7
|
+
* `Schema`.
|
|
8
|
+
*
|
|
9
|
+
* Everything this module exports is public API. The machinery behind it lives
|
|
10
|
+
* in `sui-effect/internal`, which is not in the package `exports` map.
|
|
11
|
+
*
|
|
12
|
+
* @since 0.1.0
|
|
13
|
+
*/
|
|
14
|
+
/** The closed error taxonomy and its four helpers. */
|
|
15
|
+
export { BuildError, DecodeError, ExecutionFailed, ExtensionNotReady, GraphQLUnavailable, JournalError, NetworkMismatch, NotApplied, ObjectDeleted, ObjectNotFound, ObjectUnavailable, PolicyDenied, SigningError, SimulationFailed, SubmissionUnknown, SuiError, TransactionNotFound, TransportError, UnexpectedEffects } from "./domain/errors.js";
|
|
16
|
+
/** Branded schemas and the transport-neutral object model. */
|
|
17
|
+
export { Balance, BalanceChange, Built, ChangedObject, CleverError, CoinType, CommandResult, Digest, DynamicField, DynamicFieldEntry, DynamicFieldName, Event, ExecutionReason, ExecutionStatus, GasCostSummary, chainOf, KNOWN_CHAIN_IDS, KnownNetwork, maxEpochOf, Mist, maxTimestampMsOf, MoveLocation, Network, NotAppliedEvidence, ObjectEnvelope, ObjectId, ObjectRef, ObjectType, Owner, Signature, SignedTransaction, Simulation, StructTag, SuiAddress, TransactionEffects, TransactionExpiration, UnchangedConsensusObject, Version } from "./domain/schemas.js";
|
|
18
|
+
/** A transaction that reached the chain, with accessors over its effects. */
|
|
19
|
+
export { Executed, objectRefOf, sdkRefOf } from "./domain/executed.js";
|
|
20
|
+
/** The opinionated tier. */
|
|
21
|
+
export { Sui } from "./services/Sui.js";
|
|
22
|
+
/** The mechanical tier. */
|
|
23
|
+
export { defaultGrpcUrl, SuiCore } from "./services/SuiCore.js";
|
|
24
|
+
/** The SDK's GraphQL client as one shared tag. sui-effect wraps no GraphQL API. */
|
|
25
|
+
export { SuiGraphQL } from "./services/SuiGraphQL.js";
|
|
26
|
+
/**
|
|
27
|
+
* The BCS bridge, namespaced the way the spec spells it:
|
|
28
|
+
* `SuiSchema.bcs(bcsType, expectedType)`.
|
|
29
|
+
*/
|
|
30
|
+
export * as SuiSchema from "./domain/sui-schema.js";
|
|
31
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,sDAAsD;AACtD,OAAO,EACL,UAAU,EACV,WAAW,EACX,eAAe,EACf,iBAAiB,EACjB,kBAAkB,EAElB,YAAY,EACZ,eAAe,EACf,UAAU,EACV,aAAa,EACb,cAAc,EACd,iBAAiB,EAEjB,YAAY,EACZ,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,QAAQ,EACR,mBAAmB,EACnB,cAAc,EACd,iBAAiB,EAClB,MAAM,oBAAoB,CAAA;AAE3B,8DAA8D;AAC9D,OAAO,EACL,OAAO,EACP,aAAa,EACb,KAAK,EACL,aAAa,EACb,WAAW,EACX,QAAQ,EACR,aAAa,EACb,MAAM,EACN,YAAY,EACZ,iBAAiB,EACjB,gBAAgB,EAChB,KAAK,EACL,eAAe,EACf,eAAe,EACf,cAAc,EACd,OAAO,EACP,eAAe,EACf,YAAY,EACZ,UAAU,EACV,IAAI,EACJ,gBAAgB,EAChB,YAAY,EACZ,OAAO,EACP,kBAAkB,EAClB,cAAc,EACd,QAAQ,EACR,SAAS,EACT,UAAU,EACV,KAAK,EACL,SAAS,EACT,iBAAiB,EACjB,UAAU,EACV,SAAS,EACT,UAAU,EAEV,kBAAkB,EAClB,qBAAqB,EACrB,wBAAwB,EACxB,OAAO,EACR,MAAM,qBAAqB,CAAA;AAE5B,6EAA6E;AAC7E,OAAO,EAEL,QAAQ,EACR,WAAW,EAEX,QAAQ,EACT,MAAM,sBAAsB,CAAA;AAE7B,4BAA4B;AAC5B,OAAO,EAIL,GAAG,EAGJ,MAAM,mBAAmB,CAAA;AAE1B,2BAA2B;AAC3B,OAAO,EACL,cAAc,EAGd,OAAO,EAMR,MAAM,uBAAuB,CAAA;AAE9B,mFAAmF;AACnF,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAA;AAErD;;;GAGG;AACH,OAAO,KAAK,SAAS,MAAM,wBAAwB,CAAA"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internals: everything sui-effect uses to implement itself and everything the
|
|
3
|
+
* tests reach for, none of it part of the public surface.
|
|
4
|
+
*
|
|
5
|
+
* This module is deliberately absent from the package `exports` map. It is
|
|
6
|
+
* importable inside this repository and by nothing else, so an agent reading
|
|
7
|
+
* `src/index.ts` sees exactly the API and nothing that merely happens to be
|
|
8
|
+
* exported. Names here may change in any release.
|
|
9
|
+
*
|
|
10
|
+
* @since 0.1.0
|
|
11
|
+
*/
|
|
12
|
+
export { bcs, decodeContent, expectedTypeOf, typeMatches } from "./domain/bcs.ts";
|
|
13
|
+
export { digestOf, SuiErrorSchema } from "./domain/errors.ts";
|
|
14
|
+
export { EXECUTE_INCLUDE, fromTransactionResult } from "./domain/executed.ts";
|
|
15
|
+
export { executionReasonOf, makeSuiObject } from "./domain/schemas.ts";
|
|
16
|
+
export { OBJECT_INCLUDE, SIMULATE_INCLUDE } from "./services/Sui.ts";
|
|
17
|
+
export { DefectMarker, makeFromClient, mapSdkError, readSchedule, RETRYABLE_GRPC_STATUSES } from "./services/SuiCore.ts";
|
|
18
|
+
//# sourceMappingURL=internal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,GAAG,EAAE,aAAa,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AACjF,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAC7D,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAA;AAC7E,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACtE,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACpE,OAAO,EACL,YAAY,EACZ,cAAc,EACd,WAAW,EACX,YAAY,EACZ,uBAAuB,EACxB,MAAM,uBAAuB,CAAA"}
|
package/dist/internal.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internals: everything sui-effect uses to implement itself and everything the
|
|
3
|
+
* tests reach for, none of it part of the public surface.
|
|
4
|
+
*
|
|
5
|
+
* This module is deliberately absent from the package `exports` map. It is
|
|
6
|
+
* importable inside this repository and by nothing else, so an agent reading
|
|
7
|
+
* `src/index.ts` sees exactly the API and nothing that merely happens to be
|
|
8
|
+
* exported. Names here may change in any release.
|
|
9
|
+
*
|
|
10
|
+
* @since 0.1.0
|
|
11
|
+
*/
|
|
12
|
+
export { bcs, decodeContent, expectedTypeOf, typeMatches } from "./domain/bcs.js";
|
|
13
|
+
export { digestOf, SuiErrorSchema } from "./domain/errors.js";
|
|
14
|
+
export { EXECUTE_INCLUDE, fromTransactionResult } from "./domain/executed.js";
|
|
15
|
+
export { executionReasonOf, makeSuiObject } from "./domain/schemas.js";
|
|
16
|
+
export { OBJECT_INCLUDE, SIMULATE_INCLUDE } from "./services/Sui.js";
|
|
17
|
+
export { DefectMarker, makeFromClient, mapSdkError, readSchedule, RETRYABLE_GRPC_STATUSES } from "./services/SuiCore.js";
|
|
18
|
+
//# sourceMappingURL=internal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"internal.js","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,GAAG,EAAE,aAAa,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AACjF,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAC7D,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAA;AAC7E,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACtE,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACpE,OAAO,EACL,YAAY,EACZ,cAAc,EACd,WAAW,EACX,YAAY,EACZ,uBAAuB,EACxB,MAAM,uBAAuB,CAAA"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `sui-effect/journal`: the durable submission journal.
|
|
3
|
+
*
|
|
4
|
+
* Separate from `sui-effect/tx` because it is the only part of the package
|
|
5
|
+
* that imports `effect/unstable/*` (`KeyValueStore`), and an application that
|
|
6
|
+
* does not need durability should not take that dependency.
|
|
7
|
+
*
|
|
8
|
+
* Building the layer does no network work. Settling what a previous run left
|
|
9
|
+
* behind is `Tx.reconcileAll`, which the application calls when it is ready.
|
|
10
|
+
*
|
|
11
|
+
* The durable constructors are module-level functions rather than statics on
|
|
12
|
+
* `Journal`. Attaching them would mean mutating the one shared reference object
|
|
13
|
+
* at import time, which a bundler told `sideEffects: false` is entitled to drop
|
|
14
|
+
* — and which made `Journal` appear twice, with two different shapes, in the
|
|
15
|
+
* generated documentation. The `Journal` re-exported here is the very same
|
|
16
|
+
* reference `sui-effect/tx` exports, so what a layer provides and what
|
|
17
|
+
* `Tx.submit` reads are the same key.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* import { Layer } from "effect"
|
|
22
|
+
* import { KeyValueStore } from "effect/unstable/persistence"
|
|
23
|
+
* import { layerKeyValueStore } from "@unconfirmed/sui-effect/journal"
|
|
24
|
+
*
|
|
25
|
+
* const layer = layerKeyValueStore({ onUnresolved: "ignore" }).pipe(
|
|
26
|
+
* Layer.provide(KeyValueStore.layerMemory)
|
|
27
|
+
* )
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* @since 0.1.0
|
|
31
|
+
*/
|
|
32
|
+
export type { JournalKeyValueStoreOptions } from "./services/JournalKeyValueStore.ts";
|
|
33
|
+
export type { JournalService } from "./services/Journal.ts";
|
|
34
|
+
/**
|
|
35
|
+
* The same `Journal` reference `sui-effect/tx` exports, unmodified: importing
|
|
36
|
+
* this module changes nothing about it.
|
|
37
|
+
*/
|
|
38
|
+
export { Journal } from "./services/Journal.ts";
|
|
39
|
+
/**
|
|
40
|
+
* The durable journal as a layer over a `KeyValueStore`.
|
|
41
|
+
*
|
|
42
|
+
* Fails with: `JournalError` when the store cannot be read, and — with
|
|
43
|
+
* `onUnresolved: "fail"` — when the store still holds unsettled entries.
|
|
44
|
+
*/
|
|
45
|
+
export { layerKeyValueStore } from "./services/JournalKeyValueStore.ts";
|
|
46
|
+
/**
|
|
47
|
+
* The same implementation over a `KeyValueStore` you already have, for a
|
|
48
|
+
* process that wires the journal itself. Never fails.
|
|
49
|
+
*/
|
|
50
|
+
export { make as makeKeyValueStore } from "./services/JournalKeyValueStore.ts";
|
|
51
|
+
//# sourceMappingURL=journal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"journal.d.ts","sourceRoot":"","sources":["../src/journal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,YAAY,EAAE,2BAA2B,EAAE,MAAM,oCAAoC,CAAA;AACrF,YAAY,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAE3D;;;GAGG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAA;AAE/C;;;;;GAKG;AACH,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAA;AAEvE;;;GAGG;AACH,OAAO,EAAE,IAAI,IAAI,iBAAiB,EAAE,MAAM,oCAAoC,CAAA"}
|
package/dist/journal.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The same `Journal` reference `sui-effect/tx` exports, unmodified: importing
|
|
3
|
+
* this module changes nothing about it.
|
|
4
|
+
*/
|
|
5
|
+
export { Journal } from "./services/Journal.js";
|
|
6
|
+
/**
|
|
7
|
+
* The durable journal as a layer over a `KeyValueStore`.
|
|
8
|
+
*
|
|
9
|
+
* Fails with: `JournalError` when the store cannot be read, and — with
|
|
10
|
+
* `onUnresolved: "fail"` — when the store still holds unsettled entries.
|
|
11
|
+
*/
|
|
12
|
+
export { layerKeyValueStore } from "./services/JournalKeyValueStore.js";
|
|
13
|
+
/**
|
|
14
|
+
* The same implementation over a `KeyValueStore` you already have, for a
|
|
15
|
+
* process that wires the journal itself. Never fails.
|
|
16
|
+
*/
|
|
17
|
+
export { make as makeKeyValueStore } from "./services/JournalKeyValueStore.js";
|
|
18
|
+
//# sourceMappingURL=journal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"journal.js","sourceRoot":"","sources":["../src/journal.ts"],"names":[],"mappings":"AAkCA;;;GAGG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAA;AAE/C;;;;;GAKG;AACH,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAA;AAEvE;;;GAGG;AACH,OAAO,EAAE,IAAI,IAAI,iBAAiB,EAAE,MAAM,oCAAoC,CAAA"}
|
package/dist/script.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `sui-effect/script`: the on-demand script preset.
|
|
3
|
+
*
|
|
4
|
+
* @since 0.1.0
|
|
5
|
+
*/
|
|
6
|
+
export { exitCode, readNetwork, run, Script, ScriptReadOnly, type ScriptReadOnlyService, type ScriptRunOptions, type ScriptService, type SignalSource } from "./services/Script.ts";
|
|
7
|
+
//# sourceMappingURL=script.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"script.d.ts","sourceRoot":"","sources":["../src/script.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EACL,QAAQ,EACR,WAAW,EACX,GAAG,EACH,MAAM,EACN,cAAc,EACd,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,YAAY,EAClB,MAAM,sBAAsB,CAAA"}
|
package/dist/script.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"script.js","sourceRoot":"","sources":["../src/script.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EACL,QAAQ,EACR,WAAW,EACX,GAAG,EACH,MAAM,EACN,cAAc,EAKf,MAAM,sBAAsB,CAAA"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Journal`: what this process has put on the wire.
|
|
3
|
+
*
|
|
4
|
+
* A `Context.Reference` with an in-memory default, so `Journal` never appears
|
|
5
|
+
* in an `R` and a one-shot script works with zero setup. For a script the
|
|
6
|
+
* durable record is the signed bytes inside `SubmissionUnknown`, which
|
|
7
|
+
* `SuiError.describe` prints; a long-lived application replaces the default
|
|
8
|
+
* with the `KeyValueStore` journal in `sui-effect/journal`.
|
|
9
|
+
*
|
|
10
|
+
* @since 0.1.0
|
|
11
|
+
*/
|
|
12
|
+
import { Context, Effect, Layer, Option } from "effect";
|
|
13
|
+
import type { JournalError } from "../domain/errors.ts";
|
|
14
|
+
import { JournalEntry } from "../domain/journal-entry.ts";
|
|
15
|
+
import type { Digest } from "../domain/schemas.ts";
|
|
16
|
+
/** Reading and writing the submission journal. */
|
|
17
|
+
export interface JournalService {
|
|
18
|
+
/**
|
|
19
|
+
* Records one entry, replacing any earlier entry for the same digest.
|
|
20
|
+
*
|
|
21
|
+
* Fails with: `JournalError`.
|
|
22
|
+
*/
|
|
23
|
+
readonly put: (entry: JournalEntry) => Effect.Effect<void, JournalError>;
|
|
24
|
+
/**
|
|
25
|
+
* The entry for a digest, if this journal has one.
|
|
26
|
+
*
|
|
27
|
+
* Fails with: `JournalError`.
|
|
28
|
+
*/
|
|
29
|
+
readonly get: (digest: Digest) => Effect.Effect<Option.Option<JournalEntry>, JournalError>;
|
|
30
|
+
/**
|
|
31
|
+
* Every entry still waiting for an answer (`Signed` and `Unknown`), oldest
|
|
32
|
+
* first. `Tx.reconcileAll` is what a startup does with it.
|
|
33
|
+
*
|
|
34
|
+
* Fails with: `JournalError`.
|
|
35
|
+
*/
|
|
36
|
+
readonly listUnresolved: Effect.Effect<ReadonlyArray<JournalEntry>, JournalError>;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* An in-memory journal over a `Map`. Survives nothing; enough for a one-shot
|
|
40
|
+
* script, a test, and any program whose crash recovery is a human reading
|
|
41
|
+
* `SubmissionUnknown`.
|
|
42
|
+
*/
|
|
43
|
+
export declare const makeMemoryUnsafe: () => JournalService;
|
|
44
|
+
/**
|
|
45
|
+
* The submission journal.
|
|
46
|
+
*
|
|
47
|
+
* Being a `Context.Reference`, it is never in an `R`: `Tx.submit` reads it from
|
|
48
|
+
* context and finds the in-memory default unless something provided another.
|
|
49
|
+
*
|
|
50
|
+
* **The default is process-wide.** A `Context.Reference`'s default value is
|
|
51
|
+
* computed once and cached on the reference itself, so every fiber that does
|
|
52
|
+
* not provide one shares a single `Map` for the life of the process. That is
|
|
53
|
+
* what makes a one-shot script work with zero wiring, and it is also why a
|
|
54
|
+
* test that runs `Tx.submit` or `Tx.run` should provide
|
|
55
|
+
* {@link Journal.layerMemory}: without it, entries from one test are visible to
|
|
56
|
+
* the next, and `listUnresolved` returns other tests' submissions.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```ts
|
|
60
|
+
* import { Effect } from "effect"
|
|
61
|
+
* import { Journal } from "@unconfirmed/sui-effect/tx"
|
|
62
|
+
*
|
|
63
|
+
* const entries = Effect.gen(function*() {
|
|
64
|
+
* const journal = yield* Journal
|
|
65
|
+
* return yield* journal.listUnresolved
|
|
66
|
+
* })
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
export declare const Journal: Context.Reference<JournalService> & {
|
|
70
|
+
/** A fresh in-memory journal, for a test or a process that wants its own. */
|
|
71
|
+
layerMemory: Layer.Layer<never, never, never>;
|
|
72
|
+
/** The in-memory implementation, for building one directly. */
|
|
73
|
+
makeMemoryUnsafe: () => JournalService;
|
|
74
|
+
};
|
|
75
|
+
export { JournalEntry };
|
|
76
|
+
//# sourceMappingURL=Journal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Journal.d.ts","sourceRoot":"","sources":["../../src/services/Journal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,EAAgB,YAAY,EAAE,MAAM,4BAA4B,CAAA;AACvE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAElD,kDAAkD;AAClD,MAAM,WAAW,cAAc;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,YAAY,CAAC,CAAA;IACxE;;;;OAIG;IACH,QAAQ,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,YAAY,CAAC,CAAA;IAC1F;;;;;OAKG;IACH,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,YAAY,CAAC,EAAE,YAAY,CAAC,CAAA;CAClF;AAED;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,QAAO,cAgBnC,CAAA;AAOD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,OAAO;IAClB,6EAA6E;;IAE7E,+DAA+D;4BAnD7B,cAAc;CAqDhD,CAAA;AAEF,OAAO,EAAE,YAAY,EAAE,CAAA"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Journal`: what this process has put on the wire.
|
|
3
|
+
*
|
|
4
|
+
* A `Context.Reference` with an in-memory default, so `Journal` never appears
|
|
5
|
+
* in an `R` and a one-shot script works with zero setup. For a script the
|
|
6
|
+
* durable record is the signed bytes inside `SubmissionUnknown`, which
|
|
7
|
+
* `SuiError.describe` prints; a long-lived application replaces the default
|
|
8
|
+
* with the `KeyValueStore` journal in `sui-effect/journal`.
|
|
9
|
+
*
|
|
10
|
+
* @since 0.1.0
|
|
11
|
+
*/
|
|
12
|
+
import { Context, Effect, Layer, Option } from "effect";
|
|
13
|
+
import { isUnresolved, JournalEntry } from "../domain/journal-entry.js";
|
|
14
|
+
/**
|
|
15
|
+
* An in-memory journal over a `Map`. Survives nothing; enough for a one-shot
|
|
16
|
+
* script, a test, and any program whose crash recovery is a human reading
|
|
17
|
+
* `SubmissionUnknown`.
|
|
18
|
+
*/
|
|
19
|
+
export const makeMemoryUnsafe = () => {
|
|
20
|
+
const entries = new Map();
|
|
21
|
+
return {
|
|
22
|
+
put: Effect.fn("Journal.put")(function* (entry) {
|
|
23
|
+
yield* Effect.sync(() => {
|
|
24
|
+
entries.delete(entry.digest);
|
|
25
|
+
entries.set(entry.digest, entry);
|
|
26
|
+
});
|
|
27
|
+
}),
|
|
28
|
+
get: Effect.fn("Journal.get")(function* (digest) {
|
|
29
|
+
return yield* Effect.sync(() => Option.fromNullishOr(entries.get(digest)));
|
|
30
|
+
}),
|
|
31
|
+
listUnresolved: Effect.sync(() => [...entries.values()].filter(isUnresolved)).pipe(Effect.withSpan("Journal.listUnresolved"))
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
/** The reference itself; {@link Journal} is it with the constructors attached. */
|
|
35
|
+
const JournalRef = Context.Reference("@unconfirmed/sui-effect/Journal", {
|
|
36
|
+
defaultValue: makeMemoryUnsafe
|
|
37
|
+
});
|
|
38
|
+
/**
|
|
39
|
+
* The submission journal.
|
|
40
|
+
*
|
|
41
|
+
* Being a `Context.Reference`, it is never in an `R`: `Tx.submit` reads it from
|
|
42
|
+
* context and finds the in-memory default unless something provided another.
|
|
43
|
+
*
|
|
44
|
+
* **The default is process-wide.** A `Context.Reference`'s default value is
|
|
45
|
+
* computed once and cached on the reference itself, so every fiber that does
|
|
46
|
+
* not provide one shares a single `Map` for the life of the process. That is
|
|
47
|
+
* what makes a one-shot script work with zero wiring, and it is also why a
|
|
48
|
+
* test that runs `Tx.submit` or `Tx.run` should provide
|
|
49
|
+
* {@link Journal.layerMemory}: without it, entries from one test are visible to
|
|
50
|
+
* the next, and `listUnresolved` returns other tests' submissions.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```ts
|
|
54
|
+
* import { Effect } from "effect"
|
|
55
|
+
* import { Journal } from "@unconfirmed/sui-effect/tx"
|
|
56
|
+
*
|
|
57
|
+
* const entries = Effect.gen(function*() {
|
|
58
|
+
* const journal = yield* Journal
|
|
59
|
+
* return yield* journal.listUnresolved
|
|
60
|
+
* })
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export const Journal = Object.assign(JournalRef, {
|
|
64
|
+
/** A fresh in-memory journal, for a test or a process that wants its own. */
|
|
65
|
+
layerMemory: Layer.sync(JournalRef, makeMemoryUnsafe),
|
|
66
|
+
/** The in-memory implementation, for building one directly. */
|
|
67
|
+
makeMemoryUnsafe
|
|
68
|
+
});
|
|
69
|
+
export { JournalEntry };
|
|
70
|
+
//# sourceMappingURL=Journal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Journal.js","sourceRoot":"","sources":["../../src/services/Journal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAEvD,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AA0BvE;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAmB,EAAE;IACnD,MAAM,OAAO,GAAG,IAAI,GAAG,EAAwB,CAAA;IAC/C,OAAO;QACL,GAAG,EAAE,MAAM,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,QAAQ,CAAC,EAAC,KAAmB;YACzD,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;gBACtB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;gBAC5B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;YAClC,CAAC,CAAC,CAAA;QACJ,CAAC,CAAC;QACF,GAAG,EAAE,MAAM,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,QAAQ,CAAC,EAAC,MAAc;YACpD,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;QAC5E,CAAC,CAAC;QACF,cAAc,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAChF,MAAM,CAAC,QAAQ,CAAC,wBAAwB,CAAC,CAC1C;KACF,CAAA;AACH,CAAC,CAAA;AAED,kFAAkF;AAClF,MAAM,UAAU,GAAG,OAAO,CAAC,SAAS,CAAiB,iCAAiC,EAAE;IACtF,YAAY,EAAE,gBAAgB;CAC/B,CAAC,CAAA;AAEF;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE;IAC/C,6EAA6E;IAC7E,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,gBAAgB,CAAC;IACrD,+DAA+D;IAC/D,gBAAgB;CACjB,CAAC,CAAA;AAEF,OAAO,EAAE,YAAY,EAAE,CAAA"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The durable `Journal`, over `KeyValueStore` from `effect/unstable/persistence`.
|
|
3
|
+
*
|
|
4
|
+
* This is the only file in the package that imports `effect/unstable/*`, and it
|
|
5
|
+
* is reachable only through the `sui-effect/journal` subpath, so the core
|
|
6
|
+
* modules stay on stable Effect.
|
|
7
|
+
*
|
|
8
|
+
* Building the layer does no network work: it reads the index of unresolved
|
|
9
|
+
* entries and, when asked to, refuses to start while any are outstanding. What
|
|
10
|
+
* to do about them is `Tx.reconcileAll`, which an application calls explicitly.
|
|
11
|
+
*
|
|
12
|
+
* @since 0.1.0
|
|
13
|
+
*/
|
|
14
|
+
import { Layer } from "effect";
|
|
15
|
+
import { KeyValueStore } from "effect/unstable/persistence";
|
|
16
|
+
import { JournalError } from "../domain/errors.ts";
|
|
17
|
+
import type { JournalService } from "./Journal.ts";
|
|
18
|
+
/** What a durable journal does about entries left over from a previous run. */
|
|
19
|
+
export interface JournalKeyValueStoreOptions {
|
|
20
|
+
/**
|
|
21
|
+
* `"fail"` refuses to build the layer while the store holds unresolved
|
|
22
|
+
* entries, which is what a service that must never silently drop a
|
|
23
|
+
* transaction wants: it forces the operator (or a `Tx.reconcileAll` step
|
|
24
|
+
* ahead of this layer) to settle them first. `"ignore"` builds anyway and
|
|
25
|
+
* leaves them for `Tx.reconcileAll`.
|
|
26
|
+
*/
|
|
27
|
+
readonly onUnresolved: "fail" | "ignore";
|
|
28
|
+
/** A prefix for every key, so several journals can share one store. */
|
|
29
|
+
readonly prefix?: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* A journal backed by a `KeyValueStore`.
|
|
33
|
+
*
|
|
34
|
+
* The store has no key enumeration, so the journal keeps its own index: one
|
|
35
|
+
* key holding the digests of the entries that are still unresolved, rewritten
|
|
36
|
+
* whenever an entry is put. A terminal entry (`Executed`, `Failed`,
|
|
37
|
+
* `NotApplied`) drops its digest from the index and keeps the entry itself, so
|
|
38
|
+
* a later `get` still finds the answer.
|
|
39
|
+
*
|
|
40
|
+
* `put` is serialized by a semaphore of one permit held across **both** writes,
|
|
41
|
+
* because the index write is a read-modify-write over a store that offers no
|
|
42
|
+
* compare-and-set: two concurrent `Tx.run`s from different senders would
|
|
43
|
+
* otherwise each read the same index, each append their own digest, and the
|
|
44
|
+
* second write would drop the first. The semaphore is keyed by the store prefix
|
|
45
|
+
* at module level, so two journals built over one prefix share it.
|
|
46
|
+
*
|
|
47
|
+
* **The order of the two writes depends on which way a crash between them is
|
|
48
|
+
* survivable:**
|
|
49
|
+
*
|
|
50
|
+
* - an **unresolved** entry (`Signed`, `Unknown`) writes the **index first**, so
|
|
51
|
+
* a crash leaves a digest whose entry is missing — `listUnresolved` skips it —
|
|
52
|
+
* rather than an entry nothing points at;
|
|
53
|
+
* - a **terminal** entry (`Executed`, `Failed`, `NotApplied`) writes the
|
|
54
|
+
* **entry first** and only then drops the digest from the index, so a crash
|
|
55
|
+
* leaves the digest still indexed with its terminal answer already stored:
|
|
56
|
+
* `listUnresolved` reads the entry, sees it is resolved, and skips it. Doing
|
|
57
|
+
* it the other way round is how a settled transaction reverted to the stale
|
|
58
|
+
* `Signed` entry that startup recovery could no longer find.
|
|
59
|
+
*
|
|
60
|
+
* Every member fails with `JournalError` and nothing else.
|
|
61
|
+
*/
|
|
62
|
+
export declare const make: (store: KeyValueStore.KeyValueStore, options?: {
|
|
63
|
+
readonly prefix?: string;
|
|
64
|
+
}) => JournalService;
|
|
65
|
+
/**
|
|
66
|
+
* The durable journal as a layer.
|
|
67
|
+
*
|
|
68
|
+
* Fails with: `JournalError` when the store cannot be read, and — with
|
|
69
|
+
* `onUnresolved: "fail"` — when the store still holds entries no one has
|
|
70
|
+
* settled.
|
|
71
|
+
*/
|
|
72
|
+
export declare const layerKeyValueStore: (options: JournalKeyValueStoreOptions) => Layer.Layer<never, JournalError, KeyValueStore.KeyValueStore>;
|
|
73
|
+
//# sourceMappingURL=JournalKeyValueStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"JournalKeyValueStore.d.ts","sourceRoot":"","sources":["../../src/services/JournalKeyValueStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAU,KAAK,EAA6B,MAAM,QAAQ,CAAA;AACjE,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAGlD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAGlD,+EAA+E;AAC/E,MAAM,WAAW,2BAA2B;IAC1C;;;;;;OAMG;IACH,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,QAAQ,CAAA;IACxC,uEAAuE;IACvE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CACzB;AAwCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,eAAO,MAAM,IAAI,GACf,OAAO,aAAa,CAAC,aAAa,EAClC,UAAU;IAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,KACrC,cA8DF,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,GAC7B,SAAS,2BAA2B,KACnC,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,YAAY,EAAE,aAAa,CAAC,aAAa,CAoB5D,CAAA"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The durable `Journal`, over `KeyValueStore` from `effect/unstable/persistence`.
|
|
3
|
+
*
|
|
4
|
+
* This is the only file in the package that imports `effect/unstable/*`, and it
|
|
5
|
+
* is reachable only through the `sui-effect/journal` subpath, so the core
|
|
6
|
+
* modules stay on stable Effect.
|
|
7
|
+
*
|
|
8
|
+
* Building the layer does no network work: it reads the index of unresolved
|
|
9
|
+
* entries and, when asked to, refuses to start while any are outstanding. What
|
|
10
|
+
* to do about them is `Tx.reconcileAll`, which an application calls explicitly.
|
|
11
|
+
*
|
|
12
|
+
* @since 0.1.0
|
|
13
|
+
*/
|
|
14
|
+
import { Effect, Layer, Option, Schema, Semaphore } from "effect";
|
|
15
|
+
import { KeyValueStore } from "effect/unstable/persistence";
|
|
16
|
+
import { JournalError } from "../domain/errors.js";
|
|
17
|
+
import { isUnresolved, JournalEntry } from "../domain/journal-entry.js";
|
|
18
|
+
import { Journal } from "./Journal.js";
|
|
19
|
+
const ENTRY = "entry:";
|
|
20
|
+
const INDEX = "index";
|
|
21
|
+
const Index = Schema.Array(Schema.String);
|
|
22
|
+
const decodeIndex = Schema.decodeUnknownEffect(Schema.fromJsonString(Index));
|
|
23
|
+
const encodeIndex = Schema.encodeEffect(Schema.fromJsonString(Index));
|
|
24
|
+
const decodeEntry = Schema.decodeUnknownEffect(Schema.fromJsonString(JournalEntry));
|
|
25
|
+
const encodeEntry = Schema.encodeEffect(Schema.fromJsonString(JournalEntry));
|
|
26
|
+
const journalError = (cause) => new JournalError({ cause });
|
|
27
|
+
/**
|
|
28
|
+
* The `put` semaphores, one per key prefix, at **module** level.
|
|
29
|
+
*
|
|
30
|
+
* Two `make` calls over the same store and prefix — two layer instances in one
|
|
31
|
+
* process, a test that builds the journal twice — used to get a semaphore each
|
|
32
|
+
* and could interleave their index read-modify-writes, losing one of the two
|
|
33
|
+
* digests. Keying the permit by prefix rather than by instance is what makes
|
|
34
|
+
* the serialization a property of the store's keyspace instead of a property of
|
|
35
|
+
* whoever happened to build the journal.
|
|
36
|
+
*
|
|
37
|
+
* **This is in-process only, and that is a documented limitation.** Two
|
|
38
|
+
* processes over one store (two pods, a script beside the service) still race:
|
|
39
|
+
* `KeyValueStore` offers no compare-and-set, so there is nothing to build a
|
|
40
|
+
* cross-process lock on. Run one writer per store prefix. A storage-level lock
|
|
41
|
+
* is deferred (DESIGN §8, §15).
|
|
42
|
+
*/
|
|
43
|
+
const PUT_LOCKS = new Map();
|
|
44
|
+
const putLock = (prefix) => {
|
|
45
|
+
const existing = PUT_LOCKS.get(prefix);
|
|
46
|
+
if (existing !== undefined)
|
|
47
|
+
return existing;
|
|
48
|
+
const created = Semaphore.makeUnsafe(1);
|
|
49
|
+
PUT_LOCKS.set(prefix, created);
|
|
50
|
+
return created;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* A journal backed by a `KeyValueStore`.
|
|
54
|
+
*
|
|
55
|
+
* The store has no key enumeration, so the journal keeps its own index: one
|
|
56
|
+
* key holding the digests of the entries that are still unresolved, rewritten
|
|
57
|
+
* whenever an entry is put. A terminal entry (`Executed`, `Failed`,
|
|
58
|
+
* `NotApplied`) drops its digest from the index and keeps the entry itself, so
|
|
59
|
+
* a later `get` still finds the answer.
|
|
60
|
+
*
|
|
61
|
+
* `put` is serialized by a semaphore of one permit held across **both** writes,
|
|
62
|
+
* because the index write is a read-modify-write over a store that offers no
|
|
63
|
+
* compare-and-set: two concurrent `Tx.run`s from different senders would
|
|
64
|
+
* otherwise each read the same index, each append their own digest, and the
|
|
65
|
+
* second write would drop the first. The semaphore is keyed by the store prefix
|
|
66
|
+
* at module level, so two journals built over one prefix share it.
|
|
67
|
+
*
|
|
68
|
+
* **The order of the two writes depends on which way a crash between them is
|
|
69
|
+
* survivable:**
|
|
70
|
+
*
|
|
71
|
+
* - an **unresolved** entry (`Signed`, `Unknown`) writes the **index first**, so
|
|
72
|
+
* a crash leaves a digest whose entry is missing — `listUnresolved` skips it —
|
|
73
|
+
* rather than an entry nothing points at;
|
|
74
|
+
* - a **terminal** entry (`Executed`, `Failed`, `NotApplied`) writes the
|
|
75
|
+
* **entry first** and only then drops the digest from the index, so a crash
|
|
76
|
+
* leaves the digest still indexed with its terminal answer already stored:
|
|
77
|
+
* `listUnresolved` reads the entry, sees it is resolved, and skips it. Doing
|
|
78
|
+
* it the other way round is how a settled transaction reverted to the stale
|
|
79
|
+
* `Signed` entry that startup recovery could no longer find.
|
|
80
|
+
*
|
|
81
|
+
* Every member fails with `JournalError` and nothing else.
|
|
82
|
+
*/
|
|
83
|
+
export const make = (store, options) => {
|
|
84
|
+
const kv = options?.prefix === undefined
|
|
85
|
+
? store
|
|
86
|
+
: KeyValueStore.prefix(store, options.prefix);
|
|
87
|
+
const readIndex = Effect.gen(function* () {
|
|
88
|
+
const raw = yield* kv.get(INDEX).pipe(Effect.mapError(journalError));
|
|
89
|
+
if (raw === undefined)
|
|
90
|
+
return [];
|
|
91
|
+
return yield* decodeIndex(raw).pipe(Effect.mapError(journalError));
|
|
92
|
+
});
|
|
93
|
+
const writeIndex = (digests) => encodeIndex(digests).pipe(Effect.mapError(journalError), Effect.flatMap((json) => kv.set(INDEX, json).pipe(Effect.mapError(journalError))));
|
|
94
|
+
const get = Effect.fn("Journal.get")(function* (digest) {
|
|
95
|
+
const raw = yield* kv.get(`${ENTRY}${digest}`).pipe(Effect.mapError(journalError));
|
|
96
|
+
if (raw === undefined)
|
|
97
|
+
return Option.none();
|
|
98
|
+
const entry = yield* decodeEntry(raw).pipe(Effect.mapError(journalError));
|
|
99
|
+
return Option.some(entry);
|
|
100
|
+
});
|
|
101
|
+
// One permit per store prefix, held across the index read-modify-write and
|
|
102
|
+
// the entry write.
|
|
103
|
+
const writes = putLock(options?.prefix ?? "");
|
|
104
|
+
return {
|
|
105
|
+
put: Effect.fn("Journal.put")(function* (entry) {
|
|
106
|
+
yield* Semaphore.withPermits(writes, 1)(Effect.gen(function* () {
|
|
107
|
+
const json = yield* encodeEntry(entry).pipe(Effect.mapError(journalError));
|
|
108
|
+
const writeEntry = kv.set(`${ENTRY}${entry.digest}`, json).pipe(Effect.mapError(journalError));
|
|
109
|
+
const index = yield* readIndex;
|
|
110
|
+
const without = index.filter((digest) => digest !== entry.digest);
|
|
111
|
+
if (isUnresolved(entry)) {
|
|
112
|
+
yield* writeIndex([...without, entry.digest]);
|
|
113
|
+
yield* writeEntry;
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
// Terminal: the answer is durable before the digest stops being
|
|
117
|
+
// indexed, so a crash in between can only leave a resolved entry
|
|
118
|
+
// still listed, which `listUnresolved` filters out.
|
|
119
|
+
yield* writeEntry;
|
|
120
|
+
if (without.length !== index.length)
|
|
121
|
+
yield* writeIndex(without);
|
|
122
|
+
}));
|
|
123
|
+
}),
|
|
124
|
+
get,
|
|
125
|
+
listUnresolved: Effect.gen(function* () {
|
|
126
|
+
const index = yield* readIndex;
|
|
127
|
+
const entries = [];
|
|
128
|
+
for (const digest of index) {
|
|
129
|
+
const entry = yield* get(digest);
|
|
130
|
+
if (Option.isSome(entry) && isUnresolved(entry.value))
|
|
131
|
+
entries.push(entry.value);
|
|
132
|
+
}
|
|
133
|
+
return entries;
|
|
134
|
+
}).pipe(Effect.withSpan("Journal.listUnresolved"))
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
/**
|
|
138
|
+
* The durable journal as a layer.
|
|
139
|
+
*
|
|
140
|
+
* Fails with: `JournalError` when the store cannot be read, and — with
|
|
141
|
+
* `onUnresolved: "fail"` — when the store still holds entries no one has
|
|
142
|
+
* settled.
|
|
143
|
+
*/
|
|
144
|
+
export const layerKeyValueStore = (options) => Layer.effect(Journal, Effect.gen(function* () {
|
|
145
|
+
const store = yield* KeyValueStore.KeyValueStore;
|
|
146
|
+
const journal = make(store, options.prefix === undefined ? {} : { prefix: options.prefix });
|
|
147
|
+
const unresolved = yield* journal.listUnresolved;
|
|
148
|
+
if (options.onUnresolved === "fail" && unresolved.length > 0) {
|
|
149
|
+
return yield* new JournalError({
|
|
150
|
+
cause: `the journal holds ${unresolved.length} unresolved submission(s): ${unresolved.map((entry) => entry.digest).join(", ")}. Settle them with Tx.reconcileAll, or build the layer with { onUnresolved: "ignore" }.`
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
return journal;
|
|
154
|
+
}));
|
|
155
|
+
//# sourceMappingURL=JournalKeyValueStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"JournalKeyValueStore.js","sourceRoot":"","sources":["../../src/services/JournalKeyValueStore.ts"],"names":[],"mappings":"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"}
|