@midnight-ntwrk/wallet-sdk-runtime 1.0.0-beta.10
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/dist/Runtime.d.ts +51 -0
- package/dist/Runtime.js +172 -0
- package/dist/WalletBuilder.d.ts +45 -0
- package/dist/WalletBuilder.js +112 -0
- package/dist/abstractions/StateChange.d.ts +167 -0
- package/dist/abstractions/StateChange.js +27 -0
- package/dist/abstractions/Variant.d.ts +57 -0
- package/dist/abstractions/Variant.js +7 -0
- package/dist/abstractions/VariantBuilder.d.ts +39 -0
- package/dist/abstractions/VariantBuilder.js +1 -0
- package/dist/abstractions/VersionChangeType.d.ts +67 -0
- package/dist/abstractions/VersionChangeType.js +23 -0
- package/dist/abstractions/WalletLike.d.ts +45 -0
- package/dist/abstractions/WalletLike.js +1 -0
- package/dist/abstractions/WalletRuntimeError.d.ts +9 -0
- package/dist/abstractions/WalletRuntimeError.js +15 -0
- package/dist/abstractions/index.d.ts +6 -0
- package/dist/abstractions/index.js +18 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +14 -0
- package/dist/testing/utils.d.ts +16 -0
- package/dist/testing/utils.js +33 -0
- package/dist/testing/variants.d.ts +58 -0
- package/dist/testing/variants.js +149 -0
- package/package.json +57 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Effect, Scope, Stream, SynchronizedRef } from 'effect';
|
|
2
|
+
import { ProtocolState, ProtocolVersion } from '@midnight-ntwrk/wallet-sdk-abstractions';
|
|
3
|
+
import { Variant, WalletRuntimeError } from './abstractions/index.js';
|
|
4
|
+
import { HList, Poly } from '@midnight-ntwrk/wallet-sdk-utilities';
|
|
5
|
+
/**
|
|
6
|
+
* The {@link Runtime} service type.
|
|
7
|
+
*/
|
|
8
|
+
export interface Runtime<Variants extends Variant.AnyVersionedVariantArray> {
|
|
9
|
+
readonly stateChanges: Stream.Stream<ProtocolState.ProtocolState<Variant.StateOf<HList.Each<Variants>>>, WalletRuntimeError>;
|
|
10
|
+
readonly progress: Effect.Effect<Progress>;
|
|
11
|
+
readonly currentVariant: Effect.Effect<EachRunningVariant<Variants>>;
|
|
12
|
+
dispatch<TResult, E = never>(impl: Poly.PolyFunction<Variant.RunningVariantOf<HList.Each<Variants>>, Effect.Effect<TResult, E>>): Effect.Effect<TResult, WalletRuntimeError | E>;
|
|
13
|
+
}
|
|
14
|
+
export type RunningVariant<TVariant extends Variant.AnyVersionedVariant, TRest extends Variant.AnyVersionedVariantArray> = Poly.WithTagFrom<TVariant['variant']> & {
|
|
15
|
+
variant: TVariant;
|
|
16
|
+
runningVariant: Variant.RunningVariantOf<TVariant>;
|
|
17
|
+
initialState: Variant.StateOf<TVariant>;
|
|
18
|
+
variantScope: Scope.CloseableScope;
|
|
19
|
+
currentStateRef: SynchronizedRef.SynchronizedRef<Variant.StateOf<TVariant>>;
|
|
20
|
+
restVariants: TRest;
|
|
21
|
+
initProtocolVersion: ProtocolVersion.ProtocolVersion;
|
|
22
|
+
validVersionRange: ProtocolVersion.ProtocolVersion.Range;
|
|
23
|
+
nextProtocolVersion: ProtocolVersion.ProtocolVersion | null;
|
|
24
|
+
};
|
|
25
|
+
type EachRunningVariant<TAll extends Variant.AnyVersionedVariantArray> = TAll extends [
|
|
26
|
+
infer THead extends Variant.AnyVersionedVariant,
|
|
27
|
+
...infer TRest extends Variant.AnyVersionedVariantArray
|
|
28
|
+
] ? RunningVariant<THead, TRest> | EachRunningVariant<TRest> : never;
|
|
29
|
+
/**
|
|
30
|
+
* A type that represents the reported progress of a variant expressed in terms of gaps to reaching synced
|
|
31
|
+
* progress in application site and data source site
|
|
32
|
+
*/
|
|
33
|
+
type Progress = {
|
|
34
|
+
readonly sourceGap: bigint;
|
|
35
|
+
readonly applyGap: bigint;
|
|
36
|
+
};
|
|
37
|
+
export type InitRuntimeHeadArgs<Variants extends Variant.AnyVersionedVariantArray> = {
|
|
38
|
+
variants: Variants;
|
|
39
|
+
state: Variant.StateOf<HList.Head<Variants>>;
|
|
40
|
+
};
|
|
41
|
+
export declare const initHead: <Variants extends Variant.AnyVersionedVariantArray>(initArgs: InitRuntimeHeadArgs<Variants>) => Effect.Effect<Runtime<Variants>, WalletRuntimeError, Scope.Scope>;
|
|
42
|
+
export type InitRuntimeArgs<Variants extends Variant.AnyVersionedVariantArray, InitTag extends string | symbol> = {
|
|
43
|
+
variants: Variants;
|
|
44
|
+
tag: InitTag;
|
|
45
|
+
state: Variant.StateOf<HList.Find<Variants, {
|
|
46
|
+
variant: Poly.WithTag<InitTag>;
|
|
47
|
+
}>>;
|
|
48
|
+
};
|
|
49
|
+
export declare const init: <Variants extends Variant.AnyVersionedVariantArray, InitTag extends string | symbol>(initArgs: InitRuntimeArgs<Variants, InitTag>) => Effect.Effect<Runtime<Variants>, WalletRuntimeError, Scope.Scope>;
|
|
50
|
+
export declare const dispatch: <Variants extends Variant.AnyVersionedVariantArray, TResult, E = never>(runtime: Runtime<Variants>, impl: Poly.PolyFunction<Variant.RunningVariantOf<HList.Each<Variants>>, Effect.Effect<TResult, E>>) => Effect.Effect<TResult, WalletRuntimeError | E>;
|
|
51
|
+
export {};
|
package/dist/Runtime.js
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
// This file is part of MIDNIGHT-WALLET-SDK.
|
|
2
|
+
// Copyright (C) 2025 Midnight Foundation
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
// You may not use this file except in compliance with the License.
|
|
6
|
+
// You may obtain a copy of the License at
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
// See the License for the specific language governing permissions and
|
|
12
|
+
// limitations under the License.
|
|
13
|
+
import { Effect, Either, Exit, Option, Scope, Stream, SubscriptionRef, SynchronizedRef } from 'effect';
|
|
14
|
+
import { ProtocolVersion } from '@midnight-ntwrk/wallet-sdk-abstractions';
|
|
15
|
+
import { StateChange, VersionChangeType, WalletRuntimeError } from './abstractions/index.js';
|
|
16
|
+
import { EitherOps, HList, Poly } from '@midnight-ntwrk/wallet-sdk-utilities';
|
|
17
|
+
export const initHead = (initArgs) => {
|
|
18
|
+
const headVariant = HList.head(initArgs.variants);
|
|
19
|
+
return init({
|
|
20
|
+
variants: initArgs.variants,
|
|
21
|
+
tag: Poly.getTag(headVariant.variant),
|
|
22
|
+
state: initArgs.state,
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
export const init = (initArgs) => {
|
|
26
|
+
//Rewritten from generators to better track type issues reported
|
|
27
|
+
return Effect.Do.pipe(Effect.bind('initiatedFirstVariant', () => initVariant(initArgs)), Effect.bind('currentStateRef', ({ initiatedFirstVariant }) => initiatedFirstVariant.currentStateRef.get.pipe(Effect.flatMap((state) => SubscriptionRef.make(Either.right({ version: initiatedFirstVariant.initProtocolVersion, state }))))), Effect.bind('progressRef', () => SynchronizedRef.make({ applyGap: 0n, sourceGap: 0n })), Effect.bind('currentVariantRef', ({ initiatedFirstVariant }) => Effect.acquireRelease(SynchronizedRef.make(initiatedFirstVariant), (ref, exit) => Effect.gen(function* () {
|
|
28
|
+
// This is needed to properly close variant scope when whole runtime closes
|
|
29
|
+
// Otherwise variant would be running in the background
|
|
30
|
+
const runningVariant = yield* SynchronizedRef.get(ref);
|
|
31
|
+
yield* Scope.close(runningVariant.variantScope, exit);
|
|
32
|
+
}))), Effect.bind('runningStream', ({ initiatedFirstVariant, currentStateRef, progressRef, currentVariantRef }) => {
|
|
33
|
+
return runVariantStream(initiatedFirstVariant, currentStateRef, progressRef, currentVariantRef).pipe(Effect.catchAll((error) => {
|
|
34
|
+
return SubscriptionRef.set(currentStateRef, Either.left(error));
|
|
35
|
+
}), Effect.forkScoped);
|
|
36
|
+
}), Effect.flatMap(({ currentStateRef, progressRef, currentVariantRef }) => {
|
|
37
|
+
return Effect.gen(function* () {
|
|
38
|
+
const changesStream = yield* currentStateRef.changes.pipe(Stream.mapEffect((value) => EitherOps.toEffect(value)), Stream.share({ capacity: 'unbounded', replay: 1 }));
|
|
39
|
+
const runtime = {
|
|
40
|
+
stateChanges: changesStream,
|
|
41
|
+
progress: progressRef.get,
|
|
42
|
+
currentVariant: currentVariantRef.get,
|
|
43
|
+
dispatch: (impl) => dispatch(runtime, impl),
|
|
44
|
+
};
|
|
45
|
+
return runtime;
|
|
46
|
+
});
|
|
47
|
+
}));
|
|
48
|
+
};
|
|
49
|
+
export const dispatch = (runtime, impl) => {
|
|
50
|
+
return runtime.currentVariant.pipe(Effect.flatMap((current) => Poly.dispatch(current.runningVariant, impl)));
|
|
51
|
+
};
|
|
52
|
+
const migrateToNextVariant = (migrateArgs) => {
|
|
53
|
+
return Effect.gen(function* () {
|
|
54
|
+
const [headVersionedVariant] = migrateArgs.variants;
|
|
55
|
+
if (!headVersionedVariant) {
|
|
56
|
+
yield* Effect.fail(new WalletRuntimeError({ message: 'No variant to init' }));
|
|
57
|
+
}
|
|
58
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- It seems that TS is defaulting to the constraint provided for a generic type with its inference, which includes any
|
|
59
|
+
const newState = yield* headVersionedVariant.variant.migrateState(migrateArgs.state);
|
|
60
|
+
return yield* initHeadVariant({
|
|
61
|
+
variants: migrateArgs.variants,
|
|
62
|
+
state: newState,
|
|
63
|
+
initProtocolVersion: migrateArgs.initProtocolVersion,
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
// Arguments are gathered to a separate type because presence of HList.Find is crashing TS compiler ¯\_(ツ)_/¯
|
|
68
|
+
const initVariant = (init) => {
|
|
69
|
+
return Effect.gen(function* () {
|
|
70
|
+
const index = init.variants.findIndex((variant) => Poly.getTag(variant.variant) === init.tag);
|
|
71
|
+
const theRest = init.variants.toSpliced(0, index);
|
|
72
|
+
//These casts are terrible, but they allow to call the initHeadVariant
|
|
73
|
+
return yield* initHeadVariant({
|
|
74
|
+
variants: theRest,
|
|
75
|
+
state: init.state,
|
|
76
|
+
initProtocolVersion: undefined,
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
// Following pattern from `initVariant` for consistency
|
|
81
|
+
const initHeadVariant = (init) => {
|
|
82
|
+
return Effect.gen(function* () {
|
|
83
|
+
const [anyHeadVersionedVariant, maybeNextVersionedVariant] = init.variants;
|
|
84
|
+
if (!anyHeadVersionedVariant) {
|
|
85
|
+
yield* Effect.fail(new WalletRuntimeError({ message: 'No variant to init' }));
|
|
86
|
+
}
|
|
87
|
+
const headVersionedVariant = anyHeadVersionedVariant;
|
|
88
|
+
const actualInitProtocolVersion = init.initProtocolVersion ?? headVersionedVariant.sinceVersion;
|
|
89
|
+
const nextActivationVersion = maybeNextVersionedVariant
|
|
90
|
+
? maybeNextVersionedVariant.sinceVersion
|
|
91
|
+
: ProtocolVersion.MaxSupportedVersion;
|
|
92
|
+
const validVersionRange = ProtocolVersion.makeRange(headVersionedVariant.sinceVersion, nextActivationVersion);
|
|
93
|
+
const stateRef = yield* SubscriptionRef.make(init.state);
|
|
94
|
+
const variantScope = yield* Scope.make();
|
|
95
|
+
const runningVariant = yield* headVersionedVariant.variant
|
|
96
|
+
.start({ stateRef })
|
|
97
|
+
.pipe(Effect.provideService(Scope.Scope, variantScope));
|
|
98
|
+
//This type declaration helps with setting right properties...
|
|
99
|
+
const out = {
|
|
100
|
+
__polyTag__: headVersionedVariant.variant.__polyTag__,
|
|
101
|
+
variant: headVersionedVariant,
|
|
102
|
+
initialState: init.state,
|
|
103
|
+
runningVariant: runningVariant,
|
|
104
|
+
currentStateRef: stateRef,
|
|
105
|
+
restVariants: HList.tail(init.variants),
|
|
106
|
+
initProtocolVersion: actualInitProtocolVersion,
|
|
107
|
+
validVersionRange,
|
|
108
|
+
nextProtocolVersion: maybeNextVersionedVariant ? maybeNextVersionedVariant.sinceVersion : null,
|
|
109
|
+
variantScope,
|
|
110
|
+
};
|
|
111
|
+
// ...while this type casting makes things bearable in the rest of the code (TS's type inference is great, but still limited)
|
|
112
|
+
return out;
|
|
113
|
+
});
|
|
114
|
+
};
|
|
115
|
+
const runVariantStream = (initiatedVariant, stateRef, progressRef, currentVariantRef) => {
|
|
116
|
+
const initialAcc = {
|
|
117
|
+
protocolVersion: initiatedVariant.initProtocolVersion,
|
|
118
|
+
shouldInitChange: false,
|
|
119
|
+
followEffect: Effect.void,
|
|
120
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
121
|
+
lastState: initiatedVariant.initialState,
|
|
122
|
+
};
|
|
123
|
+
return initiatedVariant.runningVariant.state.pipe(Stream.scanEffect(initialAcc, (accumulator, change) => {
|
|
124
|
+
return StateChange.match(change, {
|
|
125
|
+
State: ({ state }) => {
|
|
126
|
+
return SubscriptionRef.set(stateRef, Either.right({ version: accumulator.protocolVersion, state })).pipe(Effect.as({ ...accumulator, lastState: state }));
|
|
127
|
+
},
|
|
128
|
+
ProgressUpdate: (progress) => {
|
|
129
|
+
return SynchronizedRef.set(progressRef, progress).pipe(Effect.as(accumulator));
|
|
130
|
+
},
|
|
131
|
+
VersionChange: ({ change }) => {
|
|
132
|
+
const newProtocolVersion = VersionChangeType.match(change, {
|
|
133
|
+
Version: ({ version }) => version,
|
|
134
|
+
Next: () => initiatedVariant.nextProtocolVersion,
|
|
135
|
+
});
|
|
136
|
+
if (newProtocolVersion != null &&
|
|
137
|
+
!ProtocolVersion.withinRange(newProtocolVersion, initiatedVariant.validVersionRange)) {
|
|
138
|
+
return Effect.succeed({
|
|
139
|
+
...accumulator,
|
|
140
|
+
protocolVersion: newProtocolVersion,
|
|
141
|
+
shouldInitChange: true,
|
|
142
|
+
followEffect: Effect.gen(function* () {
|
|
143
|
+
yield* Scope.close(initiatedVariant.variantScope, Exit.void);
|
|
144
|
+
const newInitiatedVariant = yield* migrateToNextVariant({
|
|
145
|
+
variants: initiatedVariant.restVariants,
|
|
146
|
+
state: accumulator.lastState,
|
|
147
|
+
initProtocolVersion: newProtocolVersion,
|
|
148
|
+
});
|
|
149
|
+
yield* SynchronizedRef.set(currentVariantRef, newInitiatedVariant);
|
|
150
|
+
return yield* runVariantStream(newInitiatedVariant, stateRef, progressRef, currentVariantRef);
|
|
151
|
+
}),
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
return Effect.succeed({
|
|
156
|
+
...accumulator,
|
|
157
|
+
protocolVersion: newProtocolVersion ?? accumulator.protocolVersion,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
}), Stream.filter((streamAcc) => streamAcc.shouldInitChange), Stream.runHead, Effect.flatMap((streamAccOption) => {
|
|
163
|
+
return Option.match(streamAccOption, {
|
|
164
|
+
onSome: (value) => {
|
|
165
|
+
return value.followEffect;
|
|
166
|
+
},
|
|
167
|
+
onNone: () => {
|
|
168
|
+
return Effect.void;
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
}));
|
|
172
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Types } from 'effect';
|
|
2
|
+
import { ProtocolVersion } from '@midnight-ntwrk/wallet-sdk-abstractions';
|
|
3
|
+
import { VariantBuilder, WalletLike } from './abstractions/index.js';
|
|
4
|
+
import { HList } from '@midnight-ntwrk/wallet-sdk-utilities';
|
|
5
|
+
/**
|
|
6
|
+
* Builds a wallet-like implementation from a collection of wallet-like variants, each specific
|
|
7
|
+
* to a given version of the Midnight protocol.
|
|
8
|
+
*
|
|
9
|
+
* @typeParam TBuilders The sequence of variant builders that will manage the wallet state
|
|
10
|
+
*/
|
|
11
|
+
export declare class WalletBuilder<TBuilders extends VariantBuilder.AnyVersionedVariantBuilder[]> {
|
|
12
|
+
#private;
|
|
13
|
+
private constructor();
|
|
14
|
+
static init(): WalletBuilder<[]>;
|
|
15
|
+
/**
|
|
16
|
+
* Ensures that the built wallet uses a given variant.
|
|
17
|
+
*
|
|
18
|
+
* @param sinceVersion The Midnight protocol version that the variant should operate from.
|
|
19
|
+
* @param variantBuilder A {@link VariantBuilder} that builds the variant.
|
|
20
|
+
* @returns A new {@link WalletBuilder} that uses the variant that will be built from `variantBuilder`.
|
|
21
|
+
*/
|
|
22
|
+
withVariant<TBuilder extends VariantBuilder.AnyVariantBuilder>(sinceVersion: ProtocolVersion.ProtocolVersion, variantBuilder: TBuilder): WalletBuilder<HList.Append<TBuilders, VariantBuilder.VersionedVariantBuilder<TBuilder>>>;
|
|
23
|
+
/**
|
|
24
|
+
* Builds a wallet like implementation.
|
|
25
|
+
*/
|
|
26
|
+
build(...[maybeConfiguration]: WalletBuilder.BuildArguments<TBuilders>): WalletLike.BaseWalletClass<VariantBuilder.VersionedVariantsOf<TBuilders>, WalletBuilder.FullConfiguration<TBuilders>>;
|
|
27
|
+
}
|
|
28
|
+
export declare namespace WalletBuilder {
|
|
29
|
+
/**
|
|
30
|
+
* The internal build state of {@link WalletBuilder}.
|
|
31
|
+
*
|
|
32
|
+
* @remarks
|
|
33
|
+
* Represents the collection of configured variants and their configuration.
|
|
34
|
+
*/
|
|
35
|
+
type BuildState<TBuilders extends VariantBuilder.AnyVersionedVariantBuilder[]> = {
|
|
36
|
+
readonly variants: TBuilders;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Allows properly expressing no need for configuration if an empty one needs to be provided
|
|
40
|
+
*/
|
|
41
|
+
type BuildArguments<TBuilders extends VariantBuilder.AnyVersionedVariantBuilder[]> = VoidIfEmpty<FullConfiguration<TBuilders>> extends undefined ? [] : [FullConfiguration<TBuilders>];
|
|
42
|
+
type FullConfiguration<TBuilders extends VariantBuilder.AnyVersionedVariantBuilder[]> = Types.UnionToIntersection<Configurations<TBuilders>>;
|
|
43
|
+
type VoidIfEmpty<TObject> = keyof TObject extends never ? undefined : TObject;
|
|
44
|
+
type Configurations<TBuilders extends VariantBuilder.AnyVersionedVariantBuilder[]> = VariantBuilder.ConfigurationOf<HList.Each<TBuilders>>;
|
|
45
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// This file is part of MIDNIGHT-WALLET-SDK.
|
|
2
|
+
// Copyright (C) 2025 Midnight Foundation
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
// You may not use this file except in compliance with the License.
|
|
6
|
+
// You may obtain a copy of the License at
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
// See the License for the specific language governing permissions and
|
|
12
|
+
// limitations under the License.
|
|
13
|
+
import { Effect, Exit, Scope } from 'effect';
|
|
14
|
+
import * as rx from 'rxjs';
|
|
15
|
+
import { ProtocolVersion } from '@midnight-ntwrk/wallet-sdk-abstractions';
|
|
16
|
+
import { Variant, WalletRuntimeError } from './abstractions/index.js';
|
|
17
|
+
import { ObservableOps, HList } from '@midnight-ntwrk/wallet-sdk-utilities';
|
|
18
|
+
import * as Runtime from './Runtime.js';
|
|
19
|
+
/**
|
|
20
|
+
* Builds a wallet-like implementation from a collection of wallet-like variants, each specific
|
|
21
|
+
* to a given version of the Midnight protocol.
|
|
22
|
+
*
|
|
23
|
+
* @typeParam TBuilders The sequence of variant builders that will manage the wallet state
|
|
24
|
+
*/
|
|
25
|
+
export class WalletBuilder {
|
|
26
|
+
constructor(buildState) {
|
|
27
|
+
this.#buildState = buildState;
|
|
28
|
+
}
|
|
29
|
+
static init() {
|
|
30
|
+
return new WalletBuilder({
|
|
31
|
+
variants: [],
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
#buildState;
|
|
35
|
+
/**
|
|
36
|
+
* Ensures that the built wallet uses a given variant.
|
|
37
|
+
*
|
|
38
|
+
* @param sinceVersion The Midnight protocol version that the variant should operate from.
|
|
39
|
+
* @param variantBuilder A {@link VariantBuilder} that builds the variant.
|
|
40
|
+
* @returns A new {@link WalletBuilder} that uses the variant that will be built from `variantBuilder`.
|
|
41
|
+
*/
|
|
42
|
+
withVariant(sinceVersion, variantBuilder) {
|
|
43
|
+
const { sinceVersion: previousVersion } = this.#buildState.variants.at(-1) ?? {
|
|
44
|
+
sinceVersion: ProtocolVersion.ProtocolVersion(-1n),
|
|
45
|
+
};
|
|
46
|
+
if (sinceVersion <= previousVersion) {
|
|
47
|
+
throw new Error('ProtocolMismatch: sinceVersion is prior to previously registered version');
|
|
48
|
+
}
|
|
49
|
+
const newBuilder = { sinceVersion, variantBuilder };
|
|
50
|
+
return new WalletBuilder({
|
|
51
|
+
variants: HList.append(this.#buildState.variants, newBuilder),
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Builds a wallet like implementation.
|
|
56
|
+
*/
|
|
57
|
+
build(...[maybeConfiguration]) {
|
|
58
|
+
if (this.#buildState.variants.length == 0) {
|
|
59
|
+
throw new WalletRuntimeError({ message: 'Empty variants list' });
|
|
60
|
+
}
|
|
61
|
+
const variants = this.#buildState.variants.map(({ sinceVersion, variantBuilder }) => ({
|
|
62
|
+
sinceVersion,
|
|
63
|
+
variant: variantBuilder.build(maybeConfiguration ?? {}),
|
|
64
|
+
}));
|
|
65
|
+
return class BaseWallet {
|
|
66
|
+
static configuration = (maybeConfiguration ??
|
|
67
|
+
{});
|
|
68
|
+
static allVariants() {
|
|
69
|
+
return variants;
|
|
70
|
+
}
|
|
71
|
+
static allVariantsRecord() {
|
|
72
|
+
return Variant.makeVersionedRecord(BaseWallet.allVariants());
|
|
73
|
+
}
|
|
74
|
+
static startEmpty(WalletClass) {
|
|
75
|
+
return Effect.gen(this, function* () {
|
|
76
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
77
|
+
const initialState = yield* HList.head(BaseWallet.allVariants()).variant.migrateState(null);
|
|
78
|
+
return BaseWallet.startFirst(WalletClass, initialState);
|
|
79
|
+
}).pipe(Effect.runSync);
|
|
80
|
+
}
|
|
81
|
+
static startFirst(WalletClass, state) {
|
|
82
|
+
return Effect.gen(this, function* () {
|
|
83
|
+
const scope = yield* Scope.make();
|
|
84
|
+
const runtime = yield* Runtime.initHead({ variants, state }).pipe(Effect.provideService(Scope.Scope, scope));
|
|
85
|
+
return new WalletClass(runtime, scope);
|
|
86
|
+
}).pipe(Effect.runSync);
|
|
87
|
+
}
|
|
88
|
+
static start(WalletClass, tag, state) {
|
|
89
|
+
return Effect.gen(this, function* () {
|
|
90
|
+
const scope = yield* Scope.make();
|
|
91
|
+
const runtime = yield* Runtime.init({ variants, tag, state }).pipe(Effect.provideService(Scope.Scope, scope));
|
|
92
|
+
return new WalletClass(runtime, scope);
|
|
93
|
+
}).pipe(Effect.runSync);
|
|
94
|
+
}
|
|
95
|
+
runtime;
|
|
96
|
+
runtimeScope;
|
|
97
|
+
rawState;
|
|
98
|
+
get syncComplete() {
|
|
99
|
+
const { sourceGap, applyGap } = Effect.runSync(this.runtime.progress);
|
|
100
|
+
return sourceGap === 0n && applyGap === 0n;
|
|
101
|
+
}
|
|
102
|
+
constructor(runtime, runtimeScope) {
|
|
103
|
+
this.runtime = runtime;
|
|
104
|
+
this.runtimeScope = runtimeScope;
|
|
105
|
+
this.rawState = ObservableOps.fromStream(runtime.stateChanges).pipe(rx.shareReplay({ refCount: true, bufferSize: 1 }));
|
|
106
|
+
}
|
|
107
|
+
stop() {
|
|
108
|
+
return Scope.close(this.runtimeScope, Exit.void).pipe(Effect.runPromise);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { Data } from 'effect';
|
|
2
|
+
import { VersionChangeType } from './VersionChangeType.js';
|
|
3
|
+
/**
|
|
4
|
+
* A tagged enum data type that represents the state changes across wallet implementation variants.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* A variant can report changes in state using the {@link StateChange.State} enum variant. The
|
|
8
|
+
* {@link StateChange.ProgressUpdate} and {@link StateChange.VersionChange} enum variants should be used when a
|
|
9
|
+
* variant needs to report a sync progress update, or a detected change in protocol version respectively.
|
|
10
|
+
*/
|
|
11
|
+
export type StateChange<TState> = Data.TaggedEnum<{
|
|
12
|
+
/** A change in state. */
|
|
13
|
+
State: {
|
|
14
|
+
readonly state: TState;
|
|
15
|
+
};
|
|
16
|
+
/** A change in reported progress. */
|
|
17
|
+
ProgressUpdate: {
|
|
18
|
+
/**
|
|
19
|
+
* The number of blocks that remain for the underlying datasource to process in order to be fully synchronized.
|
|
20
|
+
*/
|
|
21
|
+
readonly sourceGap: bigint;
|
|
22
|
+
/**
|
|
23
|
+
* The number of blocks that remain for the variant to apply in order to be fully synchronized.
|
|
24
|
+
*/
|
|
25
|
+
readonly applyGap: bigint;
|
|
26
|
+
};
|
|
27
|
+
/** A change in Midnight protocol version. */
|
|
28
|
+
VersionChange: {
|
|
29
|
+
readonly change: VersionChangeType;
|
|
30
|
+
};
|
|
31
|
+
}>;
|
|
32
|
+
/**
|
|
33
|
+
* A type predicate that determines if a given value is a {@link StateChange.State} enum variant.
|
|
34
|
+
*/
|
|
35
|
+
export declare const isState: {
|
|
36
|
+
<T extends {
|
|
37
|
+
readonly _tag: "State";
|
|
38
|
+
readonly state: any;
|
|
39
|
+
} | {
|
|
40
|
+
readonly _tag: "ProgressUpdate";
|
|
41
|
+
readonly sourceGap: bigint;
|
|
42
|
+
readonly applyGap: bigint;
|
|
43
|
+
} | {
|
|
44
|
+
readonly _tag: "VersionChange";
|
|
45
|
+
readonly change: VersionChangeType;
|
|
46
|
+
}>(u: T): u is T & {
|
|
47
|
+
readonly _tag: "State";
|
|
48
|
+
};
|
|
49
|
+
(u: unknown): u is {
|
|
50
|
+
readonly _tag: "State";
|
|
51
|
+
readonly state: unknown;
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* A type predicate that determines if a given value is a {@link StateChange.ProgressUpdate} enum variant.
|
|
56
|
+
*/
|
|
57
|
+
export declare const isProgressUpdate: {
|
|
58
|
+
<T extends {
|
|
59
|
+
readonly _tag: "State";
|
|
60
|
+
readonly state: any;
|
|
61
|
+
} | {
|
|
62
|
+
readonly _tag: "ProgressUpdate";
|
|
63
|
+
readonly sourceGap: bigint;
|
|
64
|
+
readonly applyGap: bigint;
|
|
65
|
+
} | {
|
|
66
|
+
readonly _tag: "VersionChange";
|
|
67
|
+
readonly change: VersionChangeType;
|
|
68
|
+
}>(u: T): u is T & {
|
|
69
|
+
readonly _tag: "ProgressUpdate";
|
|
70
|
+
};
|
|
71
|
+
(u: unknown): u is {
|
|
72
|
+
readonly _tag: "ProgressUpdate";
|
|
73
|
+
readonly sourceGap: bigint;
|
|
74
|
+
readonly applyGap: bigint;
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* A type predicate that determines if a given value is a {@link StateChange.VersionChange} enum variant.
|
|
79
|
+
*/
|
|
80
|
+
export declare const isVersionChange: {
|
|
81
|
+
<T extends {
|
|
82
|
+
readonly _tag: "State";
|
|
83
|
+
readonly state: any;
|
|
84
|
+
} | {
|
|
85
|
+
readonly _tag: "ProgressUpdate";
|
|
86
|
+
readonly sourceGap: bigint;
|
|
87
|
+
readonly applyGap: bigint;
|
|
88
|
+
} | {
|
|
89
|
+
readonly _tag: "VersionChange";
|
|
90
|
+
readonly change: VersionChangeType;
|
|
91
|
+
}>(u: T): u is T & {
|
|
92
|
+
readonly _tag: "VersionChange";
|
|
93
|
+
};
|
|
94
|
+
(u: unknown): u is {
|
|
95
|
+
readonly _tag: "VersionChange";
|
|
96
|
+
readonly change: VersionChangeType;
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
export declare const match: {
|
|
100
|
+
<A, B, C, D, Cases extends {
|
|
101
|
+
readonly State: (args: {
|
|
102
|
+
readonly _tag: "State";
|
|
103
|
+
readonly state: A;
|
|
104
|
+
}) => any;
|
|
105
|
+
readonly ProgressUpdate: (args: {
|
|
106
|
+
readonly _tag: "ProgressUpdate";
|
|
107
|
+
readonly sourceGap: bigint;
|
|
108
|
+
readonly applyGap: bigint;
|
|
109
|
+
}) => any;
|
|
110
|
+
readonly VersionChange: (args: {
|
|
111
|
+
readonly _tag: "VersionChange";
|
|
112
|
+
readonly change: VersionChangeType;
|
|
113
|
+
}) => any;
|
|
114
|
+
}>(cases: Cases & { [K in Exclude<keyof Cases, "State" | "ProgressUpdate" | "VersionChange">]: never; }): (self: {
|
|
115
|
+
readonly _tag: "State";
|
|
116
|
+
readonly state: A;
|
|
117
|
+
} | {
|
|
118
|
+
readonly _tag: "ProgressUpdate";
|
|
119
|
+
readonly sourceGap: bigint;
|
|
120
|
+
readonly applyGap: bigint;
|
|
121
|
+
} | {
|
|
122
|
+
readonly _tag: "VersionChange";
|
|
123
|
+
readonly change: VersionChangeType;
|
|
124
|
+
}) => import("effect/Unify").Unify<ReturnType<Cases["State" | "ProgressUpdate" | "VersionChange"]>>;
|
|
125
|
+
<A, B, C, D, Cases extends {
|
|
126
|
+
readonly State: (args: {
|
|
127
|
+
readonly _tag: "State";
|
|
128
|
+
readonly state: A;
|
|
129
|
+
}) => any;
|
|
130
|
+
readonly ProgressUpdate: (args: {
|
|
131
|
+
readonly _tag: "ProgressUpdate";
|
|
132
|
+
readonly sourceGap: bigint;
|
|
133
|
+
readonly applyGap: bigint;
|
|
134
|
+
}) => any;
|
|
135
|
+
readonly VersionChange: (args: {
|
|
136
|
+
readonly _tag: "VersionChange";
|
|
137
|
+
readonly change: VersionChangeType;
|
|
138
|
+
}) => any;
|
|
139
|
+
}>(self: {
|
|
140
|
+
readonly _tag: "State";
|
|
141
|
+
readonly state: A;
|
|
142
|
+
} | {
|
|
143
|
+
readonly _tag: "ProgressUpdate";
|
|
144
|
+
readonly sourceGap: bigint;
|
|
145
|
+
readonly applyGap: bigint;
|
|
146
|
+
} | {
|
|
147
|
+
readonly _tag: "VersionChange";
|
|
148
|
+
readonly change: VersionChangeType;
|
|
149
|
+
}, cases: Cases & { [K in Exclude<keyof Cases, "State" | "ProgressUpdate" | "VersionChange">]: never; }): import("effect/Unify").Unify<ReturnType<Cases["State" | "ProgressUpdate" | "VersionChange"]>>;
|
|
150
|
+
}, State: <A>(args: {
|
|
151
|
+
readonly state: A;
|
|
152
|
+
}) => {
|
|
153
|
+
readonly _tag: "State";
|
|
154
|
+
readonly state: A;
|
|
155
|
+
}, ProgressUpdate: <A>(args: {
|
|
156
|
+
readonly sourceGap: bigint;
|
|
157
|
+
readonly applyGap: bigint;
|
|
158
|
+
}) => {
|
|
159
|
+
readonly _tag: "ProgressUpdate";
|
|
160
|
+
readonly sourceGap: bigint;
|
|
161
|
+
readonly applyGap: bigint;
|
|
162
|
+
}, VersionChange: <A>(args: {
|
|
163
|
+
readonly change: VersionChangeType;
|
|
164
|
+
}) => {
|
|
165
|
+
readonly _tag: "VersionChange";
|
|
166
|
+
readonly change: VersionChangeType;
|
|
167
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// This file is part of MIDNIGHT-WALLET-SDK.
|
|
2
|
+
// Copyright (C) 2025 Midnight Foundation
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
// You may not use this file except in compliance with the License.
|
|
6
|
+
// You may obtain a copy of the License at
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
// See the License for the specific language governing permissions and
|
|
12
|
+
// limitations under the License.
|
|
13
|
+
import { Data } from 'effect';
|
|
14
|
+
const StateChange = Data.taggedEnum();
|
|
15
|
+
/**
|
|
16
|
+
* A type predicate that determines if a given value is a {@link StateChange.State} enum variant.
|
|
17
|
+
*/
|
|
18
|
+
export const isState = StateChange.$is('State');
|
|
19
|
+
/**
|
|
20
|
+
* A type predicate that determines if a given value is a {@link StateChange.ProgressUpdate} enum variant.
|
|
21
|
+
*/
|
|
22
|
+
export const isProgressUpdate = StateChange.$is('ProgressUpdate');
|
|
23
|
+
/**
|
|
24
|
+
* A type predicate that determines if a given value is a {@link StateChange.VersionChange} enum variant.
|
|
25
|
+
*/
|
|
26
|
+
export const isVersionChange = StateChange.$is('VersionChange');
|
|
27
|
+
export const { $match: match, State, ProgressUpdate, VersionChange } = StateChange;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Scope, SubscriptionRef } from 'effect';
|
|
2
|
+
import type { Effect } from 'effect/Effect';
|
|
3
|
+
import type { Stream } from 'effect/Stream';
|
|
4
|
+
import { Poly } from '@midnight-ntwrk/wallet-sdk-utilities';
|
|
5
|
+
import type { ProtocolVersion } from '@midnight-ntwrk/wallet-sdk-abstractions';
|
|
6
|
+
import { WalletRuntimeError } from './WalletRuntimeError.js';
|
|
7
|
+
import * as StateChange from './StateChange.js';
|
|
8
|
+
export interface VariantContext<TState> {
|
|
9
|
+
stateRef: SubscriptionRef.SubscriptionRef<TState>;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Encapsulates a variant of a wallet implementation.
|
|
13
|
+
*
|
|
14
|
+
* @typeParam TTState The type of state that the variant will operate over.
|
|
15
|
+
* @typeParam TPreviousState The type of state that the variant can migrate from.
|
|
16
|
+
* @typeParam TDomain The variant-specific functionality
|
|
17
|
+
*/
|
|
18
|
+
export type Variant<TTag extends string | symbol, TState, TPreviousState, TRunning extends RunningVariant<TTag, TState>> = Poly.WithTag<TTag> & {
|
|
19
|
+
start(context: VariantContext<TState>): Effect<TRunning, WalletRuntimeError, Scope.Scope>;
|
|
20
|
+
migrateState(previousState: TPreviousState): Effect<TState>;
|
|
21
|
+
};
|
|
22
|
+
export type RunningVariant<TTag extends symbol | string, TState> = Poly.WithTag<TTag> & {
|
|
23
|
+
state: Stream<StateChange.StateChange<TState>, WalletRuntimeError>;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* A utility type that represents any {@link Variant}.
|
|
27
|
+
*/
|
|
28
|
+
export type AnyVariant = Variant<string | symbol, any, any, AnyRunningVariant>;
|
|
29
|
+
export type AnyRunningVariant = RunningVariant<string | symbol, any>;
|
|
30
|
+
export type RunningVariantOf<T> = T extends VersionedVariant<infer V> ? RunningVariantOf<V> : T extends Variant<string | symbol, any, any, infer Running> ? Running : never;
|
|
31
|
+
export type StateOf<T> = T extends Variant<any, infer S, any, AnyRunningVariant> ? S : T extends VersionedVariant<infer V> ? StateOf<V> : never;
|
|
32
|
+
export type PreviousStateOf<T> = T extends VersionedVariant<infer V> ? PreviousStateOf<V> : T extends Variant<string | symbol, unknown, infer S, any> ? S : never;
|
|
33
|
+
/**
|
|
34
|
+
* An array of {@link Variant} instances.
|
|
35
|
+
*/
|
|
36
|
+
export type AnyVariantArray = AnyVariant[];
|
|
37
|
+
/**
|
|
38
|
+
* A type that associates a {@link Variant} with a given version of the Midnight protocol.
|
|
39
|
+
*/
|
|
40
|
+
export type VersionedVariant<T extends AnyVariant> = Readonly<{
|
|
41
|
+
sinceVersion: ProtocolVersion.ProtocolVersion;
|
|
42
|
+
variant: T;
|
|
43
|
+
}>;
|
|
44
|
+
export type AnyVersionedVariant = VersionedVariant<AnyVariant>;
|
|
45
|
+
/**
|
|
46
|
+
* An ordered array of types that associates a {@link Variant} with a given version of the Midnight protocol.
|
|
47
|
+
*
|
|
48
|
+
* @remarks
|
|
49
|
+
* The expected order of the variants will be ascending on `sinceVersion`.
|
|
50
|
+
*/
|
|
51
|
+
export type AnyVersionedVariantArray = AnyVersionedVariant[];
|
|
52
|
+
export type VariantTag<T> = T extends VersionedVariant<infer V> ? VariantTag<V> : T extends Poly.WithTag<infer Tag> ? Tag : never;
|
|
53
|
+
export type VariantRecord<Variants> = Variants extends [infer THead, ...infer TRest] ? {
|
|
54
|
+
readonly [K in VariantTag<THead>]: THead;
|
|
55
|
+
} & VariantRecord<TRest> : Variants extends [] ? object : never;
|
|
56
|
+
export declare const getVersionedVariantTag: <Variant extends AnyVariant>(v: VersionedVariant<Variant>) => VariantTag<Variant>;
|
|
57
|
+
export declare const makeVersionedRecord: <Variants extends AnyVersionedVariantArray>(variants: Variants) => VariantRecord<Variants>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Poly } from '@midnight-ntwrk/wallet-sdk-utilities';
|
|
2
|
+
export const getVersionedVariantTag = (v) => Poly.getTag(v.variant);
|
|
3
|
+
export const makeVersionedRecord = (variants) => {
|
|
4
|
+
return variants.reduce((acc, variant) => {
|
|
5
|
+
return { ...acc, [getVersionedVariantTag(variant)]: variant };
|
|
6
|
+
}, {});
|
|
7
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { ProtocolVersion } from '@midnight-ntwrk/wallet-sdk-abstractions';
|
|
2
|
+
import { AnyVariant, VersionedVariant } from './Variant.js';
|
|
3
|
+
/**
|
|
4
|
+
* Builds a target {@link Variant} object from internal build state.
|
|
5
|
+
*
|
|
6
|
+
* @typeParam TTState The type of state that the variant will operate over.
|
|
7
|
+
* @typeParam TPreviousState The type of state that the variant can migrate from.
|
|
8
|
+
* @typeParam TConfiguration A type representing the configuration required by the variant.
|
|
9
|
+
*/
|
|
10
|
+
export interface VariantBuilder<TVariant extends AnyVariant, TConfiguration extends object = object> {
|
|
11
|
+
/**
|
|
12
|
+
* Builds the target variant object from the internal build state.
|
|
13
|
+
*
|
|
14
|
+
* @param configuration The configuration to use when building the target variant.
|
|
15
|
+
*
|
|
16
|
+
* @returns An instance of {@link Variant} that operates over `TState`.
|
|
17
|
+
*/
|
|
18
|
+
build(configuration: TConfiguration): TVariant;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Base type that represents variant configuration.
|
|
22
|
+
*/
|
|
23
|
+
export type AnyBuilderConfiguration = object;
|
|
24
|
+
/**
|
|
25
|
+
* A utility type that represents any {@link VariantBuilder}.
|
|
26
|
+
*/
|
|
27
|
+
export type AnyVariantBuilder = VariantBuilder<AnyVariant, AnyBuilderConfiguration>;
|
|
28
|
+
export type VariantOf<T> = T extends VersionedVariantBuilder<infer TBuilder> ? VariantOf<TBuilder> : T extends VariantBuilder<infer TVariant, object> ? TVariant : never;
|
|
29
|
+
export type VersionedVariantBuilder<TBuilder extends AnyVariantBuilder> = Readonly<{
|
|
30
|
+
sinceVersion: ProtocolVersion.ProtocolVersion;
|
|
31
|
+
variantBuilder: TBuilder;
|
|
32
|
+
}>;
|
|
33
|
+
export type VariantsOf<T> = T extends [infer THead, ...infer TRest] ? [VariantOf<THead>, ...VariantsOf<TRest>] : T extends [] ? [] : never;
|
|
34
|
+
export type VersionedVariantsOf<T> = T extends [infer THead, ...infer Rest] ? [VersionedVariant<VariantOf<THead>>, ...VersionedVariantsOf<Rest>] : T extends [] ? [] : never;
|
|
35
|
+
export type ConfigurationOf<T> = T extends VariantBuilder<any, infer Config> ? Config : T extends VersionedVariantBuilder<infer Builder> ? ConfigurationOf<Builder> : never;
|
|
36
|
+
/**
|
|
37
|
+
* A type that associates a {@link VariantBuilder} with a given version of the Midnight protocol.
|
|
38
|
+
*/
|
|
39
|
+
export type AnyVersionedVariantBuilder = VersionedVariantBuilder<AnyVariantBuilder>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Data } from 'effect';
|
|
2
|
+
import { ProtocolVersion } from '@midnight-ntwrk/wallet-sdk-abstractions';
|
|
3
|
+
/**
|
|
4
|
+
* A tagged enum data type that represents a change in Midnight protocol versions.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* A specific protocol version can be specified using the {@link VersionChangeType.Version} enum variant. It has a
|
|
8
|
+
* `version` property that accepts a {@link ProtocolVersion} value for a known protocol version.
|
|
9
|
+
* For use cases where a specific protocol version cannot be given, the {@link VersionChangeType.Next} enum variant
|
|
10
|
+
* can be used. Its use is context specific.
|
|
11
|
+
*/
|
|
12
|
+
export type VersionChangeType = Data.TaggedEnum<{
|
|
13
|
+
/** A change to a particular protocol version. */
|
|
14
|
+
Version: {
|
|
15
|
+
readonly version: ProtocolVersion.ProtocolVersion;
|
|
16
|
+
};
|
|
17
|
+
/** A change to the 'next' protocol version. Particularly useful in testing */
|
|
18
|
+
Next: {};
|
|
19
|
+
}>;
|
|
20
|
+
/**
|
|
21
|
+
* A type predicate that determines if a given value is a {@link VersionChangeType.Version} enum variant.
|
|
22
|
+
*/
|
|
23
|
+
export declare const isVersion: (u: unknown) => u is {
|
|
24
|
+
readonly _tag: "Version";
|
|
25
|
+
readonly version: ProtocolVersion.ProtocolVersion;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* A type predicate that determines if a given value is a {@link VersionChangeType.Next} enum variant.
|
|
29
|
+
*/
|
|
30
|
+
export declare const isNext: (u: unknown) => u is {
|
|
31
|
+
readonly _tag: "Next";
|
|
32
|
+
};
|
|
33
|
+
export declare const match: {
|
|
34
|
+
<const Cases extends {
|
|
35
|
+
readonly Version: (args: {
|
|
36
|
+
readonly _tag: "Version";
|
|
37
|
+
readonly version: ProtocolVersion.ProtocolVersion;
|
|
38
|
+
}) => any;
|
|
39
|
+
readonly Next: (args: {
|
|
40
|
+
readonly _tag: "Next";
|
|
41
|
+
}) => any;
|
|
42
|
+
}>(cases: Cases & { [K in Exclude<keyof Cases, "Version" | "Next">]: never; }): (value: {
|
|
43
|
+
readonly _tag: "Version";
|
|
44
|
+
readonly version: ProtocolVersion.ProtocolVersion;
|
|
45
|
+
} | {
|
|
46
|
+
readonly _tag: "Next";
|
|
47
|
+
}) => import("effect/Unify").Unify<ReturnType<Cases["Version" | "Next"]>>;
|
|
48
|
+
<const Cases extends {
|
|
49
|
+
readonly Version: (args: {
|
|
50
|
+
readonly _tag: "Version";
|
|
51
|
+
readonly version: ProtocolVersion.ProtocolVersion;
|
|
52
|
+
}) => any;
|
|
53
|
+
readonly Next: (args: {
|
|
54
|
+
readonly _tag: "Next";
|
|
55
|
+
}) => any;
|
|
56
|
+
}>(value: {
|
|
57
|
+
readonly _tag: "Version";
|
|
58
|
+
readonly version: ProtocolVersion.ProtocolVersion;
|
|
59
|
+
} | {
|
|
60
|
+
readonly _tag: "Next";
|
|
61
|
+
}, cases: Cases & { [K in Exclude<keyof Cases, "Version" | "Next">]: never; }): import("effect/Unify").Unify<ReturnType<Cases["Version" | "Next"]>>;
|
|
62
|
+
}, Version: Data.Case.Constructor<{
|
|
63
|
+
readonly _tag: "Version";
|
|
64
|
+
readonly version: ProtocolVersion.ProtocolVersion;
|
|
65
|
+
}, "_tag">, Next: Data.Case.Constructor<{
|
|
66
|
+
readonly _tag: "Next";
|
|
67
|
+
}, "_tag">;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// This file is part of MIDNIGHT-WALLET-SDK.
|
|
2
|
+
// Copyright (C) 2025 Midnight Foundation
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
// You may not use this file except in compliance with the License.
|
|
6
|
+
// You may obtain a copy of the License at
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
// See the License for the specific language governing permissions and
|
|
12
|
+
// limitations under the License.
|
|
13
|
+
import { Data } from 'effect';
|
|
14
|
+
const VersionChangeType = Data.taggedEnum();
|
|
15
|
+
/**
|
|
16
|
+
* A type predicate that determines if a given value is a {@link VersionChangeType.Version} enum variant.
|
|
17
|
+
*/
|
|
18
|
+
export const isVersion = VersionChangeType.$is('Version');
|
|
19
|
+
/**
|
|
20
|
+
* A type predicate that determines if a given value is a {@link VersionChangeType.Next} enum variant.
|
|
21
|
+
*/
|
|
22
|
+
export const isNext = VersionChangeType.$is('Next');
|
|
23
|
+
export const { $match: match, Version, Next } = VersionChangeType;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Scope } from 'effect';
|
|
2
|
+
import { Observable } from 'rxjs';
|
|
3
|
+
import { Runtime } from '../Runtime.js';
|
|
4
|
+
import { AnyVersionedVariantArray, StateOf, VariantRecord } from './Variant.js';
|
|
5
|
+
import { HList, Poly } from '@midnight-ntwrk/wallet-sdk-utilities';
|
|
6
|
+
import { ProtocolState } from '@midnight-ntwrk/wallet-sdk-abstractions';
|
|
7
|
+
/**
|
|
8
|
+
* Defines the static portion of base wallet class definition
|
|
9
|
+
*/
|
|
10
|
+
export interface BaseWalletClass<TVariants extends AnyVersionedVariantArray, TConfiguration = object> {
|
|
11
|
+
readonly configuration: Readonly<TConfiguration>;
|
|
12
|
+
new (runtime: Runtime<TVariants>, scope: Scope.CloseableScope): WalletLike<TVariants>;
|
|
13
|
+
allVariants(): TVariants;
|
|
14
|
+
allVariantsRecord(): VariantRecord<TVariants>;
|
|
15
|
+
startEmpty<T extends WalletClassLike<TVariants, any>>(walletClass: T): WalletOf<T>;
|
|
16
|
+
startFirst<T extends WalletClassLike<TVariants, any>>(walletClass: T, state: StateOf<HList.Head<TVariants>>): WalletOf<T>;
|
|
17
|
+
start<T extends WalletClassLike<TVariants, any>, Tag extends string | symbol>(walletClass: T, tag: Tag, state: StateOf<HList.Find<TVariants, {
|
|
18
|
+
variant: Poly.WithTag<Tag>;
|
|
19
|
+
}>>): WalletOf<T>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Defines the static portion of wallet-like definition
|
|
23
|
+
*/
|
|
24
|
+
export interface WalletClassLike<TVariants extends AnyVersionedVariantArray, TWallet extends WalletLike<TVariants>> extends BaseWalletClass<TVariants> {
|
|
25
|
+
new (runtime: Runtime<TVariants>, scope: Scope.CloseableScope): TWallet;
|
|
26
|
+
}
|
|
27
|
+
export type AnyWalletClass<Variants extends AnyVersionedVariantArray> = WalletClassLike<Variants, WalletLike<Variants>>;
|
|
28
|
+
export type WalletOf<T> = T extends WalletClassLike<any, infer TWallet> ? TWallet : never;
|
|
29
|
+
/**
|
|
30
|
+
* Defines a base wallet-like implementation.
|
|
31
|
+
*
|
|
32
|
+
* @typeParam TVariants Underlying variants
|
|
33
|
+
*/
|
|
34
|
+
export interface WalletLike<TVariants extends AnyVersionedVariantArray> {
|
|
35
|
+
readonly runtime: Runtime<TVariants>;
|
|
36
|
+
readonly runtimeScope: Scope.CloseableScope;
|
|
37
|
+
/**
|
|
38
|
+
* A stream of state changes over any amount of time that have been processed by the wallet.
|
|
39
|
+
*/
|
|
40
|
+
readonly rawState: Observable<ProtocolState.ProtocolState<StateOf<HList.Each<TVariants>>>>;
|
|
41
|
+
/**
|
|
42
|
+
* Stops the wallet
|
|
43
|
+
*/
|
|
44
|
+
stop(): Promise<void>;
|
|
45
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
declare const WalletRuntimeError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
|
|
2
|
+
readonly _tag: "WalletRuntimeError";
|
|
3
|
+
} & Readonly<A>;
|
|
4
|
+
export declare class WalletRuntimeError extends WalletRuntimeError_base<{
|
|
5
|
+
message: string;
|
|
6
|
+
cause?: unknown;
|
|
7
|
+
}> {
|
|
8
|
+
}
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// This file is part of MIDNIGHT-WALLET-SDK.
|
|
2
|
+
// Copyright (C) 2025 Midnight Foundation
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
// You may not use this file except in compliance with the License.
|
|
6
|
+
// You may obtain a copy of the License at
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
// See the License for the specific language governing permissions and
|
|
12
|
+
// limitations under the License.
|
|
13
|
+
import { Data } from 'effect';
|
|
14
|
+
export class WalletRuntimeError extends Data.TaggedError('WalletRuntimeError') {
|
|
15
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export * as Variant from './Variant.js';
|
|
2
|
+
export * as VariantBuilder from './VariantBuilder.js';
|
|
3
|
+
export * as WalletLike from './WalletLike.js';
|
|
4
|
+
export * from './WalletRuntimeError.js';
|
|
5
|
+
export * as StateChange from './StateChange.js';
|
|
6
|
+
export * as VersionChangeType from './VersionChangeType.js';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// This file is part of MIDNIGHT-WALLET-SDK.
|
|
2
|
+
// Copyright (C) 2025 Midnight Foundation
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
// You may not use this file except in compliance with the License.
|
|
6
|
+
// You may obtain a copy of the License at
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
// See the License for the specific language governing permissions and
|
|
12
|
+
// limitations under the License.
|
|
13
|
+
export * as Variant from './Variant.js';
|
|
14
|
+
export * as VariantBuilder from './VariantBuilder.js';
|
|
15
|
+
export * as WalletLike from './WalletLike.js';
|
|
16
|
+
export * from './WalletRuntimeError.js';
|
|
17
|
+
export * as StateChange from './StateChange.js';
|
|
18
|
+
export * as VersionChangeType from './VersionChangeType.js';
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// This file is part of MIDNIGHT-WALLET-SDK.
|
|
2
|
+
// Copyright (C) 2025 Midnight Foundation
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
// You may not use this file except in compliance with the License.
|
|
6
|
+
// You may obtain a copy of the License at
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
// See the License for the specific language governing permissions and
|
|
12
|
+
// limitations under the License.
|
|
13
|
+
export * from './WalletBuilder.js';
|
|
14
|
+
export * as Runtime from './Runtime.js';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ProtocolState } from '@midnight-ntwrk/wallet-sdk-abstractions';
|
|
2
|
+
import { Chunk } from 'effect';
|
|
3
|
+
import { Observable, OperatorFunction } from 'rxjs';
|
|
4
|
+
/**
|
|
5
|
+
* Utility function that takes state values from an RxJS observable until it completes or errors.
|
|
6
|
+
*
|
|
7
|
+
* @param observable The RxJS observable from which state values should be read.
|
|
8
|
+
* @param onErrCallback An optional callback to invoke if an error is encountered reading a state value.
|
|
9
|
+
* @returns A `Promise` that resolves with an array of state values that were received before encountering
|
|
10
|
+
* any error.
|
|
11
|
+
*
|
|
12
|
+
* @internal
|
|
13
|
+
*/
|
|
14
|
+
export declare const toProtocolStateArray: <T>(observable: Observable<ProtocolState.ProtocolState<T>>, onErrCallback?: (err: unknown) => void) => Promise<ProtocolState.ProtocolState<T>[]>;
|
|
15
|
+
export declare const reduceToChunk: <T>() => OperatorFunction<T, Chunk.Chunk<T>>;
|
|
16
|
+
export declare const isRange: (values: Chunk.Chunk<number>) => boolean;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Chunk } from 'effect';
|
|
2
|
+
import { reduce } from 'rxjs';
|
|
3
|
+
/**
|
|
4
|
+
* Utility function that takes state values from an RxJS observable until it completes or errors.
|
|
5
|
+
*
|
|
6
|
+
* @param observable The RxJS observable from which state values should be read.
|
|
7
|
+
* @param onErrCallback An optional callback to invoke if an error is encountered reading a state value.
|
|
8
|
+
* @returns A `Promise` that resolves with an array of state values that were received before encountering
|
|
9
|
+
* any error.
|
|
10
|
+
*
|
|
11
|
+
* @internal
|
|
12
|
+
*/
|
|
13
|
+
export const toProtocolStateArray = (observable, onErrCallback) => new Promise((resolve) => {
|
|
14
|
+
const receivedStates = [];
|
|
15
|
+
observable.subscribe({
|
|
16
|
+
next(value) {
|
|
17
|
+
receivedStates.push(value);
|
|
18
|
+
},
|
|
19
|
+
complete() {
|
|
20
|
+
resolve(receivedStates);
|
|
21
|
+
},
|
|
22
|
+
error(err) {
|
|
23
|
+
onErrCallback?.call(undefined, err);
|
|
24
|
+
resolve(receivedStates);
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
export const reduceToChunk = () => reduce((chunk, value) => Chunk.append(chunk, value), Chunk.empty());
|
|
29
|
+
export const isRange = (values) => {
|
|
30
|
+
const firstDropped = Chunk.drop(values, 1);
|
|
31
|
+
const lastDropped = Chunk.dropRight(values, 1);
|
|
32
|
+
return Chunk.zip(lastDropped, firstDropped).pipe(Chunk.every(([l, r]) => r == l + 1));
|
|
33
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Variant, VariantBuilder, VersionChangeType, WalletRuntimeError } from '../abstractions/index.js';
|
|
2
|
+
import { Effect, Scope } from 'effect';
|
|
3
|
+
export type RangeConfig = {
|
|
4
|
+
min: number;
|
|
5
|
+
max: number;
|
|
6
|
+
};
|
|
7
|
+
export declare const Numeric: "NumericRange";
|
|
8
|
+
export declare class NumericRange implements Variant.Variant<typeof Numeric, number, null, Variant.RunningVariant<typeof Numeric, number>> {
|
|
9
|
+
#private;
|
|
10
|
+
__polyTag__: typeof Numeric;
|
|
11
|
+
protected configuration: RangeConfig;
|
|
12
|
+
protected yieldCount: number;
|
|
13
|
+
protected throwError: boolean;
|
|
14
|
+
constructor(configuration: RangeConfig, yieldCount: number, throwError: boolean);
|
|
15
|
+
get currentState(): number;
|
|
16
|
+
start(context: Variant.VariantContext<number>): Effect.Effect<Variant.RunningVariant<typeof Numeric, number>>;
|
|
17
|
+
migrateState(): Effect.Effect<number>;
|
|
18
|
+
}
|
|
19
|
+
export declare class NumericRangeBuilder implements VariantBuilder.VariantBuilder<NumericRange, RangeConfig> {
|
|
20
|
+
private readonly yieldCount;
|
|
21
|
+
private readonly throwError;
|
|
22
|
+
constructor(yieldCount?: number, throwError?: boolean);
|
|
23
|
+
build(configuration: RangeConfig): NumericRange;
|
|
24
|
+
}
|
|
25
|
+
export type RangeMultiplierConfig = RangeConfig & {
|
|
26
|
+
multiplier: number;
|
|
27
|
+
};
|
|
28
|
+
export declare const NumericMultiplier = "NumericMultiplier";
|
|
29
|
+
export declare class NumericRangeMultiplier implements Variant.Variant<typeof NumericMultiplier, number, number, Variant.RunningVariant<typeof NumericMultiplier, number>> {
|
|
30
|
+
#private;
|
|
31
|
+
__polyTag__: typeof NumericMultiplier;
|
|
32
|
+
protected configuration: RangeMultiplierConfig;
|
|
33
|
+
constructor(configuration: RangeMultiplierConfig);
|
|
34
|
+
get currentState(): number;
|
|
35
|
+
start(context: Variant.VariantContext<number>): Effect.Effect<Variant.RunningVariant<typeof NumericMultiplier, number>>;
|
|
36
|
+
migrateState(state: number): Effect.Effect<number>;
|
|
37
|
+
}
|
|
38
|
+
export declare class NumericRangeMultiplierBuilder implements VariantBuilder.VariantBuilder<NumericRangeMultiplier, RangeMultiplierConfig> {
|
|
39
|
+
build(configuration: RangeMultiplierConfig): NumericRangeMultiplier;
|
|
40
|
+
}
|
|
41
|
+
export type InterceptingRunningVariant<TTag extends string | symbol, TState> = Variant.RunningVariant<TTag, TState> & {
|
|
42
|
+
emitProtocolVersionChange: (change: VersionChangeType.VersionChangeType) => Effect.Effect<void>;
|
|
43
|
+
};
|
|
44
|
+
export declare class InterceptingVariant<TTag extends string | symbol, TState> implements Variant.Variant<TTag, TState, TState, InterceptingRunningVariant<TTag, TState>> {
|
|
45
|
+
__polyTag__: TTag;
|
|
46
|
+
constructor(tag: TTag);
|
|
47
|
+
migrateState(previousState: TState): Effect.Effect<TState>;
|
|
48
|
+
start(context: Variant.VariantContext<TState>): Effect.Effect<InterceptingRunningVariant<TTag, TState>, WalletRuntimeError, Scope.Scope>;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Builder of an intercepting variant
|
|
52
|
+
* It allows removing the possibility of race conditions by requiring an explicit gesture to migrate to a next/specific protocol version
|
|
53
|
+
*/
|
|
54
|
+
export declare class InterceptingVariantBuilder<TTag extends string | symbol, TState> implements VariantBuilder.VariantBuilder<InterceptingVariant<TTag, TState>, object> {
|
|
55
|
+
tag: TTag;
|
|
56
|
+
constructor(tag: TTag);
|
|
57
|
+
build(): InterceptingVariant<TTag, TState>;
|
|
58
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
// This file is part of MIDNIGHT-WALLET-SDK.
|
|
2
|
+
// Copyright (C) 2025 Midnight Foundation
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
// You may not use this file except in compliance with the License.
|
|
6
|
+
// You may obtain a copy of the License at
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
// See the License for the specific language governing permissions and
|
|
12
|
+
// limitations under the License.
|
|
13
|
+
import { StateChange, VersionChangeType, WalletRuntimeError } from '../abstractions/index.js';
|
|
14
|
+
import { Effect, Option, PubSub, Stream } from 'effect';
|
|
15
|
+
export const Numeric = 'NumericRange';
|
|
16
|
+
export class NumericRange {
|
|
17
|
+
__polyTag__ = Numeric;
|
|
18
|
+
#state = 0;
|
|
19
|
+
configuration;
|
|
20
|
+
yieldCount;
|
|
21
|
+
throwError;
|
|
22
|
+
constructor(configuration, yieldCount, throwError) {
|
|
23
|
+
this.throwError = throwError;
|
|
24
|
+
this.yieldCount = yieldCount;
|
|
25
|
+
this.configuration = configuration;
|
|
26
|
+
}
|
|
27
|
+
get currentState() {
|
|
28
|
+
return this.#state;
|
|
29
|
+
}
|
|
30
|
+
start(context) {
|
|
31
|
+
const max = this.configuration.max ?? 10;
|
|
32
|
+
return context.stateRef.get.pipe(Effect.flatMap((state) => {
|
|
33
|
+
return Effect.sync(() => {
|
|
34
|
+
this.#state = state;
|
|
35
|
+
});
|
|
36
|
+
}), Effect.map(() => ({
|
|
37
|
+
__polyTag__: Numeric,
|
|
38
|
+
state: Stream.fromAsyncIterable(
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
40
|
+
(async function* (self) {
|
|
41
|
+
for (let value = self.#state; value <= max; value++) {
|
|
42
|
+
self.#state = value;
|
|
43
|
+
yield StateChange.State({ state: value });
|
|
44
|
+
if (--self.yieldCount === 0) {
|
|
45
|
+
if (self.throwError) {
|
|
46
|
+
throw new Error('NumericRange: forced break');
|
|
47
|
+
}
|
|
48
|
+
yield StateChange.VersionChange({ change: VersionChangeType.Next() });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
})(this), (e) => new WalletRuntimeError({ message: 'NumericRange error', cause: e })),
|
|
52
|
+
})));
|
|
53
|
+
}
|
|
54
|
+
migrateState() {
|
|
55
|
+
return Effect.succeed(0);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export class NumericRangeBuilder {
|
|
59
|
+
yieldCount;
|
|
60
|
+
throwError;
|
|
61
|
+
constructor(yieldCount = 10, throwError = false) {
|
|
62
|
+
this.throwError = throwError;
|
|
63
|
+
this.yieldCount = yieldCount;
|
|
64
|
+
}
|
|
65
|
+
build(configuration) {
|
|
66
|
+
return new NumericRange(configuration, this.yieldCount, this.throwError);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
export const NumericMultiplier = 'NumericMultiplier';
|
|
70
|
+
export class NumericRangeMultiplier {
|
|
71
|
+
__polyTag__ = NumericMultiplier;
|
|
72
|
+
#state = 0;
|
|
73
|
+
configuration;
|
|
74
|
+
constructor(configuration) {
|
|
75
|
+
this.configuration = configuration;
|
|
76
|
+
}
|
|
77
|
+
get currentState() {
|
|
78
|
+
return this.#state;
|
|
79
|
+
}
|
|
80
|
+
start(context) {
|
|
81
|
+
return context.stateRef.get.pipe(Effect.flatMap((state) => {
|
|
82
|
+
return Effect.sync(() => {
|
|
83
|
+
this.#state = state;
|
|
84
|
+
});
|
|
85
|
+
}), Effect.map(() => {
|
|
86
|
+
const max = this.configuration.max ?? 10;
|
|
87
|
+
return {
|
|
88
|
+
__polyTag__: NumericMultiplier,
|
|
89
|
+
state: Stream.fromIterable((function* (self) {
|
|
90
|
+
for (let value = self.#state; value <= max; value++) {
|
|
91
|
+
self.#state = value;
|
|
92
|
+
yield StateChange.State({ state: value * self.configuration.multiplier });
|
|
93
|
+
}
|
|
94
|
+
return Option.none();
|
|
95
|
+
})(this)),
|
|
96
|
+
};
|
|
97
|
+
}));
|
|
98
|
+
}
|
|
99
|
+
migrateState(state) {
|
|
100
|
+
return Effect.succeed(state + 1);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
export class NumericRangeMultiplierBuilder {
|
|
104
|
+
build(configuration) {
|
|
105
|
+
return new NumericRangeMultiplier(configuration);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
export class InterceptingVariant {
|
|
109
|
+
__polyTag__;
|
|
110
|
+
constructor(tag) {
|
|
111
|
+
this.__polyTag__ = tag;
|
|
112
|
+
}
|
|
113
|
+
migrateState(previousState) {
|
|
114
|
+
return Effect.succeed(previousState);
|
|
115
|
+
}
|
|
116
|
+
start(context) {
|
|
117
|
+
const tag = this.__polyTag__;
|
|
118
|
+
return Effect.gen(this, function* () {
|
|
119
|
+
const pubsub = yield* PubSub.bounded({
|
|
120
|
+
capacity: 1,
|
|
121
|
+
replay: 1,
|
|
122
|
+
});
|
|
123
|
+
const state = yield* context.stateRef.get;
|
|
124
|
+
yield* PubSub.publish(pubsub, StateChange.State({ state }));
|
|
125
|
+
return {
|
|
126
|
+
__polyTag__: tag,
|
|
127
|
+
state: Stream.fromPubSub(pubsub, {
|
|
128
|
+
shutdown: true,
|
|
129
|
+
}),
|
|
130
|
+
emitProtocolVersionChange: (change) => {
|
|
131
|
+
return PubSub.publish(pubsub, StateChange.VersionChange({ change }));
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Builder of an intercepting variant
|
|
139
|
+
* It allows removing the possibility of race conditions by requiring an explicit gesture to migrate to a next/specific protocol version
|
|
140
|
+
*/
|
|
141
|
+
export class InterceptingVariantBuilder {
|
|
142
|
+
tag;
|
|
143
|
+
constructor(tag) {
|
|
144
|
+
this.tag = tag;
|
|
145
|
+
}
|
|
146
|
+
build() {
|
|
147
|
+
return new InterceptingVariant(this.tag);
|
|
148
|
+
}
|
|
149
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@midnight-ntwrk/wallet-sdk-runtime",
|
|
3
|
+
"description": "Runtime for the wallet variants",
|
|
4
|
+
"version": "1.0.0-beta.10",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"author": "Midnight Foundation",
|
|
10
|
+
"license": "Apache-2.0",
|
|
11
|
+
"publishConfig": {
|
|
12
|
+
"registry": "https://npm.pkg.github.com/"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist/"
|
|
16
|
+
],
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/midnight-ntwrk/artifacts.git"
|
|
20
|
+
},
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"import": "./dist/index.js"
|
|
25
|
+
},
|
|
26
|
+
"./abstractions": {
|
|
27
|
+
"types": "./dist/abstractions/index.d.ts",
|
|
28
|
+
"import": "./dist/abstractions/index.js"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@midnight-ntwrk/wallet-sdk-abstractions": "1.0.0-beta.10",
|
|
33
|
+
"@midnight-ntwrk/wallet-sdk-utilities": "1.0.0-beta.9",
|
|
34
|
+
"effect": "^3.17.3",
|
|
35
|
+
"rxjs": "^7.5"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"typecheck": "tsc -b ./tsconfig.json --noEmit",
|
|
39
|
+
"test": "vitest run",
|
|
40
|
+
"lint": "eslint --max-warnings 0",
|
|
41
|
+
"format": "prettier --write \"**/*.{ts,js,json,yaml,yml,md}\"",
|
|
42
|
+
"format:check": "prettier --check \"**/*.{ts,js,json,yaml,yml,md}\"",
|
|
43
|
+
"dist": "tsc -b ./tsconfig.build.json",
|
|
44
|
+
"dist:publish": "tsc -b ./tsconfig.publish.json",
|
|
45
|
+
"clean": "rimraf --glob dist 'tsconfig.*.tsbuildinfo' && date +%s > .clean-timestamp",
|
|
46
|
+
"publint": "publint --strict"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"eslint": "^9.32.0",
|
|
50
|
+
"fast-check": "^4.2.0",
|
|
51
|
+
"prettier": "^3.7.0",
|
|
52
|
+
"publint": "~0.3.14",
|
|
53
|
+
"rimraf": "^6.0.1",
|
|
54
|
+
"typescript": "^5.9.3",
|
|
55
|
+
"vitest": "^4.0.16"
|
|
56
|
+
}
|
|
57
|
+
}
|