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.
Files changed (115) hide show
  1. package/AGENTS.md +209 -0
  2. package/LICENSE +21 -0
  3. package/LLMS.md +6707 -0
  4. package/README.md +300 -0
  5. package/dist/domain/bcs.d.ts +101 -0
  6. package/dist/domain/bcs.d.ts.map +1 -0
  7. package/dist/domain/bcs.js +183 -0
  8. package/dist/domain/bcs.js.map +1 -0
  9. package/dist/domain/errors.d.ts +595 -0
  10. package/dist/domain/errors.d.ts.map +1 -0
  11. package/dist/domain/errors.js +476 -0
  12. package/dist/domain/errors.js.map +1 -0
  13. package/dist/domain/executed.d.ts +325 -0
  14. package/dist/domain/executed.d.ts.map +1 -0
  15. package/dist/domain/executed.js +253 -0
  16. package/dist/domain/executed.js.map +1 -0
  17. package/dist/domain/journal-entry.d.ts +468 -0
  18. package/dist/domain/journal-entry.d.ts.map +1 -0
  19. package/dist/domain/journal-entry.js +63 -0
  20. package/dist/domain/journal-entry.js.map +1 -0
  21. package/dist/domain/schemas.d.ts +950 -0
  22. package/dist/domain/schemas.d.ts.map +1 -0
  23. package/dist/domain/schemas.js +571 -0
  24. package/dist/domain/schemas.js.map +1 -0
  25. package/dist/domain/sui-schema.d.ts +12 -0
  26. package/dist/domain/sui-schema.d.ts.map +1 -0
  27. package/dist/domain/sui-schema.js +12 -0
  28. package/dist/domain/sui-schema.js.map +1 -0
  29. package/dist/extension.d.ts +7 -0
  30. package/dist/extension.d.ts.map +1 -0
  31. package/dist/extension.js +7 -0
  32. package/dist/extension.js.map +1 -0
  33. package/dist/index.d.ts +31 -0
  34. package/dist/index.d.ts.map +1 -0
  35. package/dist/index.js +31 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/internal.d.ts +18 -0
  38. package/dist/internal.d.ts.map +1 -0
  39. package/dist/internal.js +18 -0
  40. package/dist/internal.js.map +1 -0
  41. package/dist/journal.d.ts +51 -0
  42. package/dist/journal.d.ts.map +1 -0
  43. package/dist/journal.js +18 -0
  44. package/dist/journal.js.map +1 -0
  45. package/dist/script.d.ts +7 -0
  46. package/dist/script.d.ts.map +1 -0
  47. package/dist/script.js +7 -0
  48. package/dist/script.js.map +1 -0
  49. package/dist/services/Journal.d.ts +76 -0
  50. package/dist/services/Journal.d.ts.map +1 -0
  51. package/dist/services/Journal.js +70 -0
  52. package/dist/services/Journal.js.map +1 -0
  53. package/dist/services/JournalKeyValueStore.d.ts +73 -0
  54. package/dist/services/JournalKeyValueStore.d.ts.map +1 -0
  55. package/dist/services/JournalKeyValueStore.js +155 -0
  56. package/dist/services/JournalKeyValueStore.js.map +1 -0
  57. package/dist/services/Script.d.ts +210 -0
  58. package/dist/services/Script.d.ts.map +1 -0
  59. package/dist/services/Script.js +441 -0
  60. package/dist/services/Script.js.map +1 -0
  61. package/dist/services/Signer.d.ts +132 -0
  62. package/dist/services/Signer.d.ts.map +1 -0
  63. package/dist/services/Signer.js +158 -0
  64. package/dist/services/Signer.js.map +1 -0
  65. package/dist/services/SubmitConfig.d.ts +175 -0
  66. package/dist/services/SubmitConfig.d.ts.map +1 -0
  67. package/dist/services/SubmitConfig.js +52 -0
  68. package/dist/services/SubmitConfig.js.map +1 -0
  69. package/dist/services/Sui.d.ts +295 -0
  70. package/dist/services/Sui.d.ts.map +1 -0
  71. package/dist/services/Sui.js +414 -0
  72. package/dist/services/Sui.js.map +1 -0
  73. package/dist/services/SuiCore.d.ts +227 -0
  74. package/dist/services/SuiCore.d.ts.map +1 -0
  75. package/dist/services/SuiCore.js +400 -0
  76. package/dist/services/SuiCore.js.map +1 -0
  77. package/dist/services/SuiCoreFake.d.ts +278 -0
  78. package/dist/services/SuiCoreFake.d.ts.map +1 -0
  79. package/dist/services/SuiCoreFake.js +1003 -0
  80. package/dist/services/SuiCoreFake.js.map +1 -0
  81. package/dist/services/SuiExtension.d.ts +208 -0
  82. package/dist/services/SuiExtension.d.ts.map +1 -0
  83. package/dist/services/SuiExtension.js +355 -0
  84. package/dist/services/SuiExtension.js.map +1 -0
  85. package/dist/services/SuiGraphQL.d.ts +98 -0
  86. package/dist/services/SuiGraphQL.d.ts.map +1 -0
  87. package/dist/services/SuiGraphQL.js +118 -0
  88. package/dist/services/SuiGraphQL.js.map +1 -0
  89. package/dist/services/Tx.d.ts +687 -0
  90. package/dist/services/Tx.d.ts.map +1 -0
  91. package/dist/services/Tx.js +1224 -0
  92. package/dist/services/Tx.js.map +1 -0
  93. package/dist/testing.d.ts +88 -0
  94. package/dist/testing.d.ts.map +1 -0
  95. package/dist/testing.js +163 -0
  96. package/dist/testing.js.map +1 -0
  97. package/dist/tx.d.ts +24 -0
  98. package/dist/tx.d.ts.map +1 -0
  99. package/dist/tx.js +24 -0
  100. package/dist/tx.js.map +1 -0
  101. package/docs/extensions.md +1685 -0
  102. package/examples/extension-template/README.md +170 -0
  103. package/examples/extension-template/package.json +44 -0
  104. package/examples/extension-template/scripts/check-package.ts +145 -0
  105. package/examples/extension-template/src/Escrow.ts +445 -0
  106. package/examples/extension-template/src/Platform.ts +136 -0
  107. package/examples/extension-template/src/errors.ts +61 -0
  108. package/examples/extension-template/src/extension.ts +63 -0
  109. package/examples/extension-template/src/index.ts +34 -0
  110. package/examples/extension-template/src/schema.ts +158 -0
  111. package/examples/extension-template/src/upstream.ts +66 -0
  112. package/examples/extension-template/test/escrow.test.ts +559 -0
  113. package/examples/extension-template/tsconfig.build.json +26 -0
  114. package/examples/extension-template/tsconfig.json +35 -0
  115. package/package.json +87 -0
@@ -0,0 +1,7 @@
1
+ /**
2
+ * `sui-effect/extension`: the Promise face of an Effect service.
3
+ *
4
+ * @since 0.1.0
5
+ */
6
+ export { fromService, SuiExtension } from "./services/SuiExtension.js";
7
+ //# sourceMappingURL=extension.js.map
@@ -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"}
@@ -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"}
@@ -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 "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"}
@@ -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"}
@@ -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,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 } from "./services/Script.js";
7
+ //# sourceMappingURL=script.js.map
@@ -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 "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("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 "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,oBAAoB,EAAE;IACzE,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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"}