@interop/wallet-core 0.14.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -4
- package/dist/clients/index.d.ts +32 -0
- package/dist/clients/index.d.ts.map +1 -0
- package/dist/clients/index.js +28 -0
- package/dist/clients/index.js.map +1 -0
- package/dist/clients/listing.d.ts +103 -0
- package/dist/clients/listing.d.ts.map +1 -0
- package/dist/clients/listing.js +86 -0
- package/dist/clients/listing.js.map +1 -0
- package/dist/clients/policy.d.ts +79 -0
- package/dist/clients/policy.d.ts.map +1 -0
- package/dist/clients/policy.js +55 -0
- package/dist/clients/policy.js.map +1 -0
- package/dist/clients/revocation.d.ts +156 -0
- package/dist/clients/revocation.d.ts.map +1 -0
- package/dist/clients/revocation.js +142 -0
- package/dist/clients/revocation.js.map +1 -0
- package/dist/clients/rosterPolicy.d.ts +124 -0
- package/dist/clients/rosterPolicy.d.ts.map +1 -0
- package/dist/clients/rosterPolicy.js +125 -0
- package/dist/clients/rosterPolicy.js.map +1 -0
- package/dist/enrollment/connectCode.d.ts +29 -0
- package/dist/enrollment/connectCode.d.ts.map +1 -0
- package/dist/enrollment/connectCode.js +31 -0
- package/dist/enrollment/connectCode.js.map +1 -0
- package/dist/enrollment/enrollment.d.ts.map +1 -1
- package/dist/enrollment/enrollment.js +1 -5
- package/dist/enrollment/enrollment.js.map +1 -1
- package/dist/enrollment/index.d.ts +3 -0
- package/dist/enrollment/index.d.ts.map +1 -1
- package/dist/enrollment/index.js +3 -0
- package/dist/enrollment/index.js.map +1 -1
- package/dist/keys/clientKeyRecord.d.ts +124 -0
- package/dist/keys/clientKeyRecord.d.ts.map +1 -0
- package/dist/keys/clientKeyRecord.js +265 -0
- package/dist/keys/clientKeyRecord.js.map +1 -0
- package/dist/keys/index.d.ts +5 -0
- package/dist/keys/index.d.ts.map +1 -1
- package/dist/keys/index.js +4 -0
- package/dist/keys/index.js.map +1 -1
- package/dist/request/index.d.ts +4 -0
- package/dist/request/index.d.ts.map +1 -1
- package/dist/request/index.js +4 -0
- package/dist/request/index.js.map +1 -1
- package/dist/request/walletInput.d.ts +93 -0
- package/dist/request/walletInput.d.ts.map +1 -0
- package/dist/request/walletInput.js +182 -0
- package/dist/request/walletInput.js.map +1 -0
- package/dist/sync/contactsConflict.d.ts +76 -0
- package/dist/sync/contactsConflict.d.ts.map +1 -0
- package/dist/sync/contactsConflict.js +95 -0
- package/dist/sync/contactsConflict.js.map +1 -0
- package/dist/sync/index.d.ts +5 -0
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/sync/index.js +4 -0
- package/dist/sync/index.js.map +1 -1
- package/dist/webvh/didWebvh.d.ts +6 -0
- package/dist/webvh/didWebvh.d.ts.map +1 -1
- package/dist/webvh/didWebvh.js +16 -0
- package/dist/webvh/didWebvh.js.map +1 -1
- package/dist/webvh/index.d.ts +5 -2
- package/dist/webvh/index.d.ts.map +1 -1
- package/dist/webvh/index.js +4 -1
- package/dist/webvh/index.js.map +1 -1
- package/dist/webvh/listClients.d.ts +58 -0
- package/dist/webvh/listClients.d.ts.map +1 -1
- package/dist/webvh/listClients.js +54 -0
- package/dist/webvh/listClients.js.map +1 -1
- package/dist/webvh/revokeClient.d.ts +6 -0
- package/dist/webvh/revokeClient.d.ts.map +1 -1
- package/dist/webvh/revokeClient.js +6 -0
- package/dist/webvh/revokeClient.js.map +1 -1
- package/package.json +15 -2
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { revokeWebvhClient } from '../webvh/index.js';
|
|
2
|
+
import { cascadeCollectionsToPuk, readPukRoster, rosterRecipientKid, rotatePukRoster } from '../keys/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Resolves the collection ids the fan-out covers, whether they are a fixed set
|
|
5
|
+
* or a listing the app performs.
|
|
6
|
+
*
|
|
7
|
+
* @param options {object}
|
|
8
|
+
* @param options.collections {CascadeCollections}
|
|
9
|
+
* @returns {Promise<string[]>}
|
|
10
|
+
*/
|
|
11
|
+
async function collectionIdsOf({ collections }) {
|
|
12
|
+
const { collectionIds } = collections;
|
|
13
|
+
return typeof collectionIds === 'function'
|
|
14
|
+
? await collectionIds()
|
|
15
|
+
: collectionIds;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Runs the whole revocation cascade for one enrolled wallet client. See the
|
|
19
|
+
* module doc for the order and the convergence story. Throws before touching
|
|
20
|
+
* anything on a call that must not proceed (revoking this client itself, a
|
|
21
|
+
* client whose active update key was not attributed); once the document edit
|
|
22
|
+
* lands, a thrown later stage leaves durable state a naive re-run -- or the
|
|
23
|
+
* login-time sweep -- converges from.
|
|
24
|
+
*
|
|
25
|
+
* The document edit's `StagedCommitmentAmbiguousError` passes through
|
|
26
|
+
* unwrapped, so a surface can re-word it ("disconnect this wallet from the
|
|
27
|
+
* other one instead"). Match it on `err.name ===
|
|
28
|
+
* 'StagedCommitmentAmbiguousError'` -- a stable contract on that class --
|
|
29
|
+
* rather than on `instanceof`, which does not survive a linked or duplicated
|
|
30
|
+
* copy of this package.
|
|
31
|
+
*
|
|
32
|
+
* @param options {object}
|
|
33
|
+
* @param options.idStore {WebvhIdStore} the account's `id` collection store
|
|
34
|
+
* @param options.updateKeys {ClientWebvhUpdateKeys} THIS client's did:webvh
|
|
35
|
+
* update-key seeds, which sign the document edit
|
|
36
|
+
* @param options.revokedClient {RevokedClientKeys} the revoked client's
|
|
37
|
+
* public halves (its two verification-method multibases and its active
|
|
38
|
+
* update key)
|
|
39
|
+
* @param [options.knownLatentHashes] {string[]} the standing recovery codes'
|
|
40
|
+
* update-key hashes, so the document edit can tell the revoked client's
|
|
41
|
+
* staged commitment apart from a latent recovery commitment (the one
|
|
42
|
+
* ambiguous log shape). Best-effort on the caller's side: an unreadable
|
|
43
|
+
* registry simply omits them
|
|
44
|
+
* @param [options.ownSigningKeyMultibase] {string} this client's own signing
|
|
45
|
+
* key; supplied, self-revocation is refused up front by the rule the surface
|
|
46
|
+
* should name, rather than by the update-key check inside the edit
|
|
47
|
+
* @param options.rosterStore {EncryptionDescriptorStore} the
|
|
48
|
+
* `key-map/puk.json` roster store
|
|
49
|
+
* @param [options.puk] {Puk} this client's cached per-user key
|
|
50
|
+
* @param options.clientKeyAgreementKey {IKeyAgreementKey} this client's own
|
|
51
|
+
* (identity) key-agreement key -- its roster entry
|
|
52
|
+
* @param [options.pinnedEpochId] {string} the locally pinned latest-seen
|
|
53
|
+
* roster epoch
|
|
54
|
+
* @param [options.onPukAdopted] {Function} persists a rotated key: called
|
|
55
|
+
* with `{ puk, latestEpochId, descriptor }` after the roster read and BEFORE
|
|
56
|
+
* the fan-out. The key and the epoch pin must persist atomically
|
|
57
|
+
* @param options.collections {CascadeCollections} the fan-out's work
|
|
58
|
+
* @param [options.remintRecoveryDelegations] {Function} `({ document }) =>
|
|
59
|
+
* Promise<{ reminted, skipped }>` -- the recovery-delegation re-mint stage,
|
|
60
|
+
* for a wallet that issues recovery codes
|
|
61
|
+
* @param [options.onRotationAdopted] {Function} `({ puk }) => Promise<void>`
|
|
62
|
+
* -- the live-session adoption of a rotated key, run last so the session
|
|
63
|
+
* keeps operating without a re-login
|
|
64
|
+
* @returns {Promise<ClientRevocationResult>}
|
|
65
|
+
*/
|
|
66
|
+
export async function revokeAccountClient({ idStore, updateKeys, revokedClient, knownLatentHashes, ownSigningKeyMultibase, rosterStore, puk, clientKeyAgreementKey, pinnedEpochId, onPukAdopted, collections, remintRecoveryDelegations, onRotationAdopted }) {
|
|
67
|
+
if (ownSigningKeyMultibase &&
|
|
68
|
+
ownSigningKeyMultibase === revokedClient.signingKeyMultibase) {
|
|
69
|
+
throw new Error('This wallet cannot disconnect itself; use another enrolled wallet ' +
|
|
70
|
+
'client (or a recovery code) instead.');
|
|
71
|
+
}
|
|
72
|
+
if (!revokedClient.updateKeyMultibase) {
|
|
73
|
+
throw new Error("This wallet's update key could not be attributed from the account " +
|
|
74
|
+
'log, so it cannot be disconnected from here.');
|
|
75
|
+
}
|
|
76
|
+
// 1. The document edit -- the pull axis everywhere, first. It resolves the
|
|
77
|
+
// document as it now stands, which is what stage 2 resolves its remaining
|
|
78
|
+
// recipients from.
|
|
79
|
+
const { doc } = await revokeWebvhClient({
|
|
80
|
+
idStore,
|
|
81
|
+
updateKeys,
|
|
82
|
+
revokedClient,
|
|
83
|
+
...(knownLatentHashes ? { knownLatentHashes } : {})
|
|
84
|
+
});
|
|
85
|
+
// 2. The roster rotation, recipients resolved from that same document.
|
|
86
|
+
await rotatePukRoster({
|
|
87
|
+
store: rosterStore,
|
|
88
|
+
document: doc,
|
|
89
|
+
retireRecipientId: rosterRecipientKid(revokedClient)
|
|
90
|
+
});
|
|
91
|
+
const read = await readPukRoster({
|
|
92
|
+
store: rosterStore,
|
|
93
|
+
...(puk ? { puk } : {}),
|
|
94
|
+
clientKeyAgreementKey,
|
|
95
|
+
pinnedEpochId
|
|
96
|
+
});
|
|
97
|
+
if (!read) {
|
|
98
|
+
// No roster to rotate (an account whose collections are not encrypted
|
|
99
|
+
// yet). The document edit has already landed, so the client IS
|
|
100
|
+
// disconnected -- a completed cascade with nothing rotated, not a failure.
|
|
101
|
+
return {
|
|
102
|
+
rotated: false,
|
|
103
|
+
collections: { outcomes: {}, failed: [] },
|
|
104
|
+
document: doc
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
if (read.rotated) {
|
|
108
|
+
await onPukAdopted?.({
|
|
109
|
+
puk: read.puk,
|
|
110
|
+
latestEpochId: read.latestEpochId,
|
|
111
|
+
descriptor: read.descriptor
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
// 3. The collection fan-out, in parallel -- run even when this call found
|
|
115
|
+
// the roster already rotated (a re-run after a crash), because the staleness
|
|
116
|
+
// rule finds exactly the stranded collections.
|
|
117
|
+
const cascade = await cascadeCollectionsToPuk({
|
|
118
|
+
collectionIds: await collectionIdsOf({ collections }),
|
|
119
|
+
storeFor: collections.storeFor,
|
|
120
|
+
...(collections.isEncrypted
|
|
121
|
+
? { isEncrypted: collections.isEncrypted }
|
|
122
|
+
: {}),
|
|
123
|
+
rosterDescriptor: read.descriptor,
|
|
124
|
+
clientKeyAgreementKey,
|
|
125
|
+
puk: read.puk
|
|
126
|
+
});
|
|
127
|
+
// 4. The recovery re-mints, while the registry is still readable under the
|
|
128
|
+
// session's pre-adoption vault keys.
|
|
129
|
+
const recovery = await remintRecoveryDelegations?.({ document: doc });
|
|
130
|
+
if (read.rotated) {
|
|
131
|
+
await onRotationAdopted?.({ puk: read.puk });
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
rotated: read.rotated,
|
|
135
|
+
collections: cascade,
|
|
136
|
+
document: doc,
|
|
137
|
+
puk: read.puk,
|
|
138
|
+
rosterDescriptor: read.descriptor,
|
|
139
|
+
...(recovery ? { recovery } : {})
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=revocation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"revocation.js","sourceRoot":"","sources":["../../src/clients/revocation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2026 Interop Alliance. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* The login-time wrap-set roster policy: what a wallet does with
|
|
6
|
+
* `key-map/puk.json` when a session starts, and how it decides between
|
|
7
|
+
* refusing the session and carrying on offline.
|
|
8
|
+
*
|
|
9
|
+
* Two steps, in this order:
|
|
10
|
+
*
|
|
11
|
+
* - {@link checkPukRosterAtLogin} -- one direct read of the roster, before any
|
|
12
|
+
* storage client or cipher is built (a stale key would silently fail to
|
|
13
|
+
* decrypt everything written since another client rotated it). The served
|
|
14
|
+
* roster is authenticated and checked against the locally pinned latest-seen
|
|
15
|
+
* epoch either way.
|
|
16
|
+
*
|
|
17
|
+
* **Failure semantics.** The three roster refusals -- a rolled-back or
|
|
18
|
+
* replayed roster, an epoch configuration that fails authentication, and a
|
|
19
|
+
* current epoch this client cannot unwrap -- rethrow and refuse the session:
|
|
20
|
+
* they are the same continuity class as a substituted account pointer, and
|
|
21
|
+
* proceeding would mean acting under a key nobody vouched for. Anything else
|
|
22
|
+
* (an unreachable server, a transport hiccup) is warned about and resolves
|
|
23
|
+
* `null`, so an offline start keeps working from the cached key.
|
|
24
|
+
*
|
|
25
|
+
* - {@link convergePukRosterToAccount} -- the roster stage of the
|
|
26
|
+
* cascade-completion sweep, which the collection fan-out then runs behind. A
|
|
27
|
+
* revocation torn between its document edit and its roster rotation leaves
|
|
28
|
+
* the roster wrapping the CURRENT key to a client the document no longer
|
|
29
|
+
* keys -- durable and silent, since the revoked client's document edit will
|
|
30
|
+
* never be re-run -- so it is finished here. Strictly best-effort: a log
|
|
31
|
+
* that cannot be fetched or verified (offline, an unpromoted account) leaves
|
|
32
|
+
* the start's own roster read in place, and the next start tries again.
|
|
33
|
+
*
|
|
34
|
+
* **Once per session.** A converged roster stays converged, and the engine
|
|
35
|
+
* restart (or storage-cipher rebuild) that adopts a rotation must not
|
|
36
|
+
* converge again. The guard is the caller's, because only the app knows what
|
|
37
|
+
* a session is.
|
|
38
|
+
*
|
|
39
|
+
* Adoption is a callback in both: persisting a key, pinning an epoch, swapping
|
|
40
|
+
* a live session's vault keys, and rebuilding ciphers are app-side side
|
|
41
|
+
* effects. What is NOT app-side is the ordering -- the key and the epoch pin
|
|
42
|
+
* persist atomically (the pin must never advance without the key that
|
|
43
|
+
* authenticated the roster it advanced to), and the adoption lands BEFORE the
|
|
44
|
+
* collection fan-out runs against it.
|
|
45
|
+
*/
|
|
46
|
+
import type { IKeyAgreementKey } from '@interop/data-integrity-core';
|
|
47
|
+
import type { CollectionEncryption } from '@interop/was-client';
|
|
48
|
+
import type { EncryptionDescriptorStore } from '@interop/was-client/edv';
|
|
49
|
+
import { type Puk, type PukRosterReadResult } from '../keys/index.js';
|
|
50
|
+
import type { AccountLogPointer } from './listing.js';
|
|
51
|
+
/**
|
|
52
|
+
* What an adopted per-user key carries: the key itself, the roster epoch to
|
|
53
|
+
* pin as latest-seen, and the descriptor it was read from.
|
|
54
|
+
*/
|
|
55
|
+
export interface AdoptedPuk {
|
|
56
|
+
puk: Puk;
|
|
57
|
+
latestEpochId: string;
|
|
58
|
+
descriptor: CollectionEncryption;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* The login-time roster read. See the module doc for the failure semantics.
|
|
62
|
+
*
|
|
63
|
+
* @param options {object}
|
|
64
|
+
* @param options.store {EncryptionDescriptorStore} the roster's descriptor
|
|
65
|
+
* store
|
|
66
|
+
* @param [options.puk] {Puk} this client's cached per-user key; omitted (a
|
|
67
|
+
* freshly enrolled client's first read) the key is always taken from the
|
|
68
|
+
* roster
|
|
69
|
+
* @param options.clientKeyAgreementKey {IKeyAgreementKey} this client's own
|
|
70
|
+
* (identity) key-agreement key -- its roster entry
|
|
71
|
+
* @param [options.pinnedEpochId] {string} the locally pinned latest-seen
|
|
72
|
+
* roster epoch
|
|
73
|
+
* @param [options.onRosterRead] {Function} called with the
|
|
74
|
+
* {@link AdoptedPuk} of every successful read (whether or not it rotated),
|
|
75
|
+
* so the epoch pin advances to the epoch just authenticated; the key and the
|
|
76
|
+
* pin must persist atomically
|
|
77
|
+
* @returns {Promise<PukRosterReadResult | null>} the read, or `null` when
|
|
78
|
+
* the account has no roster yet or the server could not be reached
|
|
79
|
+
*/
|
|
80
|
+
export declare function checkPukRosterAtLogin({ store, puk, clientKeyAgreementKey, pinnedEpochId, onRosterRead }: {
|
|
81
|
+
store: EncryptionDescriptorStore;
|
|
82
|
+
puk?: Puk;
|
|
83
|
+
clientKeyAgreementKey: IKeyAgreementKey;
|
|
84
|
+
pinnedEpochId?: string | null;
|
|
85
|
+
onRosterRead?: (adopted: AdoptedPuk) => Promise<void>;
|
|
86
|
+
}): Promise<PukRosterReadResult | null>;
|
|
87
|
+
/**
|
|
88
|
+
* Converges the roster onto the account's locally verified did:webvh document,
|
|
89
|
+
* adopting a rotation the ordinary way (a roster re-read) and handing back the
|
|
90
|
+
* key and descriptor the collection fan-out should run against.
|
|
91
|
+
*
|
|
92
|
+
* Best-effort throughout: any failure resolves to the unchanged input, since
|
|
93
|
+
* the fan-out is still worth running on the key this start already has.
|
|
94
|
+
*
|
|
95
|
+
* @param options {object}
|
|
96
|
+
* @param options.pointer {AccountLogPointer} where the account log lives
|
|
97
|
+
* @param options.store {EncryptionDescriptorStore} the roster's descriptor
|
|
98
|
+
* store
|
|
99
|
+
* @param options.puk {Puk} the start's current per-user key
|
|
100
|
+
* @param options.descriptor {CollectionEncryption} the start's roster read
|
|
101
|
+
* @param options.clientKeyAgreementKey {IKeyAgreementKey} this client's own
|
|
102
|
+
* (identity) key-agreement key
|
|
103
|
+
* @param [options.pinnedEpochId] {string} the locally pinned latest-seen
|
|
104
|
+
* roster epoch
|
|
105
|
+
* @param [options.onPukAdopted] {Function} called with the
|
|
106
|
+
* {@link AdoptedPuk} of a rotation, before the fan-out runs
|
|
107
|
+
* @returns {Promise<object>} whether the roster rotated on this call, the
|
|
108
|
+
* stale recipient kids found, and the key + descriptor to fan out with
|
|
109
|
+
*/
|
|
110
|
+
export declare function convergePukRosterToAccount({ pointer, store, puk, descriptor, clientKeyAgreementKey, pinnedEpochId, onPukAdopted }: {
|
|
111
|
+
pointer: AccountLogPointer;
|
|
112
|
+
store: EncryptionDescriptorStore;
|
|
113
|
+
puk: Puk;
|
|
114
|
+
descriptor: CollectionEncryption;
|
|
115
|
+
clientKeyAgreementKey: IKeyAgreementKey;
|
|
116
|
+
pinnedEpochId?: string | null;
|
|
117
|
+
onPukAdopted?: (adopted: AdoptedPuk) => Promise<void>;
|
|
118
|
+
}): Promise<{
|
|
119
|
+
rotated: boolean;
|
|
120
|
+
staleRecipientIds: string[];
|
|
121
|
+
puk: Puk;
|
|
122
|
+
descriptor: CollectionEncryption;
|
|
123
|
+
}>;
|
|
124
|
+
//# sourceMappingURL=rosterPolicy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rosterPolicy.d.ts","sourceRoot":"","sources":["../../src/clients/rosterPolicy.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAA;AACpE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAC/D,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAA;AACxE,OAAO,EAML,KAAK,GAAG,EACR,KAAK,mBAAmB,EACzB,MAAM,kBAAkB,CAAA;AAEzB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAErD;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,GAAG,CAAA;IACR,aAAa,EAAE,MAAM,CAAA;IACrB,UAAU,EAAE,oBAAoB,CAAA;CACjC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,qBAAqB,CAAC,EAC1C,KAAK,EACL,GAAG,EACH,qBAAqB,EACrB,aAAa,EACb,YAAY,EACb,EAAE;IACD,KAAK,EAAE,yBAAyB,CAAA;IAChC,GAAG,CAAC,EAAE,GAAG,CAAA;IACT,qBAAqB,EAAE,gBAAgB,CAAA;IACvC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CACtD,GAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAkCtC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,0BAA0B,CAAC,EAC/C,OAAO,EACP,KAAK,EACL,GAAG,EACH,UAAU,EACV,qBAAqB,EACrB,aAAa,EACb,YAAY,EACb,EAAE;IACD,OAAO,EAAE,iBAAiB,CAAA;IAC1B,KAAK,EAAE,yBAAyB,CAAA;IAChC,GAAG,EAAE,GAAG,CAAA;IACR,UAAU,EAAE,oBAAoB,CAAA;IAChC,qBAAqB,EAAE,gBAAgB,CAAA;IACvC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CACtD,GAAG,OAAO,CAAC;IACV,OAAO,EAAE,OAAO,CAAA;IAChB,iBAAiB,EAAE,MAAM,EAAE,CAAA;IAC3B,GAAG,EAAE,GAAG,CAAA;IACR,UAAU,EAAE,oBAAoB,CAAA;CACjC,CAAC,CAmDD"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { convergePukRosterToDocument, PukRosterContinuityError, PukRosterIntegrityError, PukRosterUnwrapError, readPukRoster } from '../keys/index.js';
|
|
2
|
+
import { verifyAccountLog } from '../webvh/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* The login-time roster read. See the module doc for the failure semantics.
|
|
5
|
+
*
|
|
6
|
+
* @param options {object}
|
|
7
|
+
* @param options.store {EncryptionDescriptorStore} the roster's descriptor
|
|
8
|
+
* store
|
|
9
|
+
* @param [options.puk] {Puk} this client's cached per-user key; omitted (a
|
|
10
|
+
* freshly enrolled client's first read) the key is always taken from the
|
|
11
|
+
* roster
|
|
12
|
+
* @param options.clientKeyAgreementKey {IKeyAgreementKey} this client's own
|
|
13
|
+
* (identity) key-agreement key -- its roster entry
|
|
14
|
+
* @param [options.pinnedEpochId] {string} the locally pinned latest-seen
|
|
15
|
+
* roster epoch
|
|
16
|
+
* @param [options.onRosterRead] {Function} called with the
|
|
17
|
+
* {@link AdoptedPuk} of every successful read (whether or not it rotated),
|
|
18
|
+
* so the epoch pin advances to the epoch just authenticated; the key and the
|
|
19
|
+
* pin must persist atomically
|
|
20
|
+
* @returns {Promise<PukRosterReadResult | null>} the read, or `null` when
|
|
21
|
+
* the account has no roster yet or the server could not be reached
|
|
22
|
+
*/
|
|
23
|
+
export async function checkPukRosterAtLogin({ store, puk, clientKeyAgreementKey, pinnedEpochId, onRosterRead }) {
|
|
24
|
+
try {
|
|
25
|
+
const read = await readPukRoster({
|
|
26
|
+
store,
|
|
27
|
+
...(puk ? { puk } : {}),
|
|
28
|
+
clientKeyAgreementKey,
|
|
29
|
+
pinnedEpochId
|
|
30
|
+
});
|
|
31
|
+
if (!read) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
await onRosterRead?.({
|
|
35
|
+
puk: read.puk,
|
|
36
|
+
latestEpochId: read.latestEpochId,
|
|
37
|
+
descriptor: read.descriptor
|
|
38
|
+
});
|
|
39
|
+
return read;
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
if (err instanceof PukRosterContinuityError ||
|
|
43
|
+
err instanceof PukRosterIntegrityError ||
|
|
44
|
+
err instanceof PukRosterUnwrapError) {
|
|
45
|
+
throw err;
|
|
46
|
+
}
|
|
47
|
+
// An unreachable server (or any transport hiccup) must not lock the user
|
|
48
|
+
// out of an offline start: the cached key stays authoritative.
|
|
49
|
+
console.warn('The wrap-set roster check failed; continuing with the cached ' +
|
|
50
|
+
'per-user key:', err);
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Converges the roster onto the account's locally verified did:webvh document,
|
|
56
|
+
* adopting a rotation the ordinary way (a roster re-read) and handing back the
|
|
57
|
+
* key and descriptor the collection fan-out should run against.
|
|
58
|
+
*
|
|
59
|
+
* Best-effort throughout: any failure resolves to the unchanged input, since
|
|
60
|
+
* the fan-out is still worth running on the key this start already has.
|
|
61
|
+
*
|
|
62
|
+
* @param options {object}
|
|
63
|
+
* @param options.pointer {AccountLogPointer} where the account log lives
|
|
64
|
+
* @param options.store {EncryptionDescriptorStore} the roster's descriptor
|
|
65
|
+
* store
|
|
66
|
+
* @param options.puk {Puk} the start's current per-user key
|
|
67
|
+
* @param options.descriptor {CollectionEncryption} the start's roster read
|
|
68
|
+
* @param options.clientKeyAgreementKey {IKeyAgreementKey} this client's own
|
|
69
|
+
* (identity) key-agreement key
|
|
70
|
+
* @param [options.pinnedEpochId] {string} the locally pinned latest-seen
|
|
71
|
+
* roster epoch
|
|
72
|
+
* @param [options.onPukAdopted] {Function} called with the
|
|
73
|
+
* {@link AdoptedPuk} of a rotation, before the fan-out runs
|
|
74
|
+
* @returns {Promise<object>} whether the roster rotated on this call, the
|
|
75
|
+
* stale recipient kids found, and the key + descriptor to fan out with
|
|
76
|
+
*/
|
|
77
|
+
export async function convergePukRosterToAccount({ pointer, store, puk, descriptor, clientKeyAgreementKey, pinnedEpochId, onPukAdopted }) {
|
|
78
|
+
const unchanged = {
|
|
79
|
+
rotated: false,
|
|
80
|
+
staleRecipientIds: [],
|
|
81
|
+
puk,
|
|
82
|
+
descriptor
|
|
83
|
+
};
|
|
84
|
+
try {
|
|
85
|
+
const { doc } = await verifyAccountLog(pointer);
|
|
86
|
+
const { rotated, staleRecipientIds } = await convergePukRosterToDocument({
|
|
87
|
+
store,
|
|
88
|
+
document: doc,
|
|
89
|
+
descriptor
|
|
90
|
+
});
|
|
91
|
+
if (!rotated) {
|
|
92
|
+
return unchanged;
|
|
93
|
+
}
|
|
94
|
+
console.warn('The wrap-set roster still wrapped the current key to ' +
|
|
95
|
+
`${staleRecipientIds.length} recipient(s) the account document no ` +
|
|
96
|
+
'longer keys; the rotation has been completed.');
|
|
97
|
+
// Rotated: the fresh key is only readable from the roster, so re-read it
|
|
98
|
+
// and adopt it before the collection fan-out runs against it.
|
|
99
|
+
const read = await readPukRoster({
|
|
100
|
+
store,
|
|
101
|
+
puk,
|
|
102
|
+
clientKeyAgreementKey,
|
|
103
|
+
pinnedEpochId
|
|
104
|
+
});
|
|
105
|
+
if (!read) {
|
|
106
|
+
return unchanged;
|
|
107
|
+
}
|
|
108
|
+
await onPukAdopted?.({
|
|
109
|
+
puk: read.puk,
|
|
110
|
+
latestEpochId: read.latestEpochId,
|
|
111
|
+
descriptor: read.descriptor
|
|
112
|
+
});
|
|
113
|
+
return {
|
|
114
|
+
rotated: true,
|
|
115
|
+
staleRecipientIds,
|
|
116
|
+
puk: read.puk,
|
|
117
|
+
descriptor: read.descriptor
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
console.warn('Could not converge the wrap-set roster onto the account document:', err);
|
|
122
|
+
return unchanged;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=rosterPolicy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rosterPolicy.js","sourceRoot":"","sources":["../../src/clients/rosterPolicy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2026 Interop Alliance. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* The connect-code grammar, on its own: the scheme-like prefix a wallet
|
|
6
|
+
* connect code carries and the predicate that recognizes one.
|
|
7
|
+
*
|
|
8
|
+
* It is a leaf module deliberately. Recognizing a connect code is something a
|
|
9
|
+
* paste box or a QR scanner does long before any ceremony machinery is
|
|
10
|
+
* involved, so the input classifier can depend on this without pulling in the
|
|
11
|
+
* enrollment ceremony's whole graph -- and there is still exactly one spelling
|
|
12
|
+
* of the prefix in the codebase.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* The connect-code prefix; the payload after it is base64url(JSON) of an
|
|
16
|
+
* enrollment request plus a `v` version stamp.
|
|
17
|
+
*/
|
|
18
|
+
export declare const CONNECT_CODE_PREFIX = "freewallet-connect:";
|
|
19
|
+
/**
|
|
20
|
+
* Whether some pasted or scanned text is a wallet connect code. A prefix
|
|
21
|
+
* check only -- whether the payload is well-formed (and of a supported
|
|
22
|
+
* version) is `parseEnrollmentRequest`'s answer, which is what surfaces a
|
|
23
|
+
* clear refusal for a code that is recognizable but unusable.
|
|
24
|
+
*
|
|
25
|
+
* @param text {string}
|
|
26
|
+
* @returns {boolean}
|
|
27
|
+
*/
|
|
28
|
+
export declare function isConnectCode(text: string): boolean;
|
|
29
|
+
//# sourceMappingURL=connectCode.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connectCode.d.ts","sourceRoot":"","sources":["../../src/enrollment/connectCode.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;GASG;AAEH;;;GAGG;AACH,eAAO,MAAM,mBAAmB,wBAAwB,CAAA;AAExD;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEnD"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2026 Interop Alliance. All rights reserved.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* The connect-code grammar, on its own: the scheme-like prefix a wallet
|
|
6
|
+
* connect code carries and the predicate that recognizes one.
|
|
7
|
+
*
|
|
8
|
+
* It is a leaf module deliberately. Recognizing a connect code is something a
|
|
9
|
+
* paste box or a QR scanner does long before any ceremony machinery is
|
|
10
|
+
* involved, so the input classifier can depend on this without pulling in the
|
|
11
|
+
* enrollment ceremony's whole graph -- and there is still exactly one spelling
|
|
12
|
+
* of the prefix in the codebase.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* The connect-code prefix; the payload after it is base64url(JSON) of an
|
|
16
|
+
* enrollment request plus a `v` version stamp.
|
|
17
|
+
*/
|
|
18
|
+
export const CONNECT_CODE_PREFIX = 'freewallet-connect:';
|
|
19
|
+
/**
|
|
20
|
+
* Whether some pasted or scanned text is a wallet connect code. A prefix
|
|
21
|
+
* check only -- whether the payload is well-formed (and of a supported
|
|
22
|
+
* version) is `parseEnrollmentRequest`'s answer, which is what surfaces a
|
|
23
|
+
* clear refusal for a code that is recognizable but unusable.
|
|
24
|
+
*
|
|
25
|
+
* @param text {string}
|
|
26
|
+
* @returns {boolean}
|
|
27
|
+
*/
|
|
28
|
+
export function isConnectCode(text) {
|
|
29
|
+
return text.trim().startsWith(CONNECT_CODE_PREFIX);
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=connectCode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connectCode.js","sourceRoot":"","sources":["../../src/enrollment/connectCode.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;GASG;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,qBAAqB,CAAA;AAExD;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,mBAAmB,CAAC,CAAA;AACpD,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"enrollment.d.ts","sourceRoot":"","sources":["../../src/enrollment/enrollment.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAA;AACpE,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAA;AAQxE,OAAO,KAAK,EACV,qBAAqB,EACrB,mBAAmB,EACnB,YAAY,EACb,MAAM,sBAAsB,CAAA;AAU7B,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;
|
|
1
|
+
{"version":3,"file":"enrollment.d.ts","sourceRoot":"","sources":["../../src/enrollment/enrollment.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAA;AACpE,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAA;AAQxE,OAAO,KAAK,EACV,qBAAqB,EACrB,mBAAmB,EACnB,YAAY,EACb,MAAM,sBAAsB,CAAA;AAU7B,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAQ1D;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,GAAG,mBAAmB,CAAA;AAEnD;;;;GAIG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;gBAE7C,OAAO,SAC+C;CAKzD;AASD;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,OAAO,EACR,EAAE;IACD,OAAO,EAAE,iBAAiB,CAAA;CAC3B,GAAG,MAAM,CAKT;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,IAAI,EACL,EAAE;IACD,IAAI,EAAE,MAAM,CAAA;CACb,GAAG,iBAAiB,CAwDpB;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,OAAO,EACR,EAAE;IACD,OAAO,EAAE,iBAAiB,CAAA;CAC3B,GAAG,MAAM,CAET;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,OAAO,EACR,EAAE;IACD,OAAO,EAAE,iBAAiB,CAAA;CAC3B,GAAG,MAAM,CAKT;AAED;;;;;;;;;GASG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC;IACrD,UAAU,EAAE,UAAU,CAAA;IACtB,eAAe,EAAE,qBAAqB,CAAA;IACtC,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;CAClB,CAAC,CA2BD;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,iBAAiB,CAAC,EACtC,OAAO,EACP,eAAe,EACf,qBAAqB,EACrB,cAAc,EACd,OAAO,EACR,EAAE;IACD,OAAO,EAAE,iBAAiB,CAAA;IAC1B,eAAe,EAAE,qBAAqB,CAAA;IACtC,qBAAqB,EAAE,gBAAgB,CAAA;IACvC,cAAc,EAAE,yBAAyB,CAAA;IACzC,OAAO,EAAE,YAAY,CAAA;CACtB,GAAG,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,mBAAmB,EAAE,MAAM,CAAA;CAAE,CAAC,CAsB3E;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,sBAAsB,CAAC,EAC3C,UAAU,EACV,eAAe,EACf,OAAO,EACR,EAAE;IACD,UAAU,EAAE,UAAU,CAAA;IACtB,eAAe,EAAE,qBAAqB,CAAA;IACtC,OAAO,EAAE,cAAc,CAAA;CACxB,GAAG,OAAO,CAAC;IAAE,GAAG,EAAE,GAAG,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,CAAC,CA8D/C"}
|
|
@@ -6,11 +6,7 @@ import { AccountLogMissingError, verifyAccountLog } from '../webvh/verifyLog.js'
|
|
|
6
6
|
import { addPukRosterRecipient } from '../keys/pukRoster.js';
|
|
7
7
|
import { readPukRoster, rosterRecipientKid } from '../keys/pukRoster.js';
|
|
8
8
|
import { pukRosterDescriptorStore } from '../keys/rosterStore.js';
|
|
9
|
-
|
|
10
|
-
* The connect-code prefix; the payload after it is base64url(JSON) of an
|
|
11
|
-
* `EnrollmentRequest` plus a `v` version stamp.
|
|
12
|
-
*/
|
|
13
|
-
const CONNECT_CODE_PREFIX = 'freewallet-connect:';
|
|
9
|
+
import { CONNECT_CODE_PREFIX } from './connectCode.js';
|
|
14
10
|
/**
|
|
15
11
|
* The connect-code payload version this build mints and accepts.
|
|
16
12
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"enrollment.js","sourceRoot":"","sources":["../../src/enrollment/enrollment.ts"],"names":[],"mappings":"AA+BA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,EACL,iBAAiB,EACjB,yBAAyB,EACzB,kBAAkB,EACnB,MAAM,sBAAsB,CAAA;AAM7B,OAAO,EACL,yBAAyB,EACzB,UAAU,EACV,eAAe,EAChB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAChF,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAA;AAC5D,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACxE,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAA;
|
|
1
|
+
{"version":3,"file":"enrollment.js","sourceRoot":"","sources":["../../src/enrollment/enrollment.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
* ceremony -- connecting another wallet client to an existing account with
|
|
7
7
|
* only public halves travelling, point to point.
|
|
8
8
|
*
|
|
9
|
+
* - `CONNECT_CODE_PREFIX` / `isConnectCode` -- the connect-code grammar, on
|
|
10
|
+
* its own so an input classifier can recognize a code without the ceremony.
|
|
9
11
|
* - `mintEnrollmentRequest` / `encodeEnrollmentRequest` /
|
|
10
12
|
* `parseEnrollmentRequest` / `enrollmentClientDid` /
|
|
11
13
|
* `enrollmentRecipientKid` -- the connect-code channel layer.
|
|
@@ -17,4 +19,5 @@
|
|
|
17
19
|
*/
|
|
18
20
|
export { approveEnrollment, completeEnrollmentCore, encodeEnrollmentRequest, EnrollmentPendingError, enrollmentClientDid, enrollmentRecipientKid, mintEnrollmentRequest, parseEnrollmentRequest } from './enrollment.js';
|
|
19
21
|
export type { EnrollmentRequest } from './enrollment.js';
|
|
22
|
+
export { CONNECT_CODE_PREFIX, isConnectCode } from './connectCode.js';
|
|
20
23
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/enrollment/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/enrollment/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,uBAAuB,EACvB,sBAAsB,EACtB,mBAAmB,EACnB,sBAAsB,EACtB,qBAAqB,EACrB,sBAAsB,EACvB,MAAM,iBAAiB,CAAA;AACxB,YAAY,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AACxD,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA"}
|
package/dist/enrollment/index.js
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
* ceremony -- connecting another wallet client to an existing account with
|
|
7
7
|
* only public halves travelling, point to point.
|
|
8
8
|
*
|
|
9
|
+
* - `CONNECT_CODE_PREFIX` / `isConnectCode` -- the connect-code grammar, on
|
|
10
|
+
* its own so an input classifier can recognize a code without the ceremony.
|
|
9
11
|
* - `mintEnrollmentRequest` / `encodeEnrollmentRequest` /
|
|
10
12
|
* `parseEnrollmentRequest` / `enrollmentClientDid` /
|
|
11
13
|
* `enrollmentRecipientKid` -- the connect-code channel layer.
|
|
@@ -16,4 +18,5 @@
|
|
|
16
18
|
* the key set stays with the caller's own unlock layer.
|
|
17
19
|
*/
|
|
18
20
|
export { approveEnrollment, completeEnrollmentCore, encodeEnrollmentRequest, EnrollmentPendingError, enrollmentClientDid, enrollmentRecipientKid, mintEnrollmentRequest, parseEnrollmentRequest } from './enrollment.js';
|
|
21
|
+
export { CONNECT_CODE_PREFIX, isConnectCode } from './connectCode.js';
|
|
19
22
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/enrollment/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/enrollment/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,uBAAuB,EACvB,sBAAsB,EACtB,mBAAmB,EACnB,sBAAsB,EACtB,qBAAqB,EACrB,sBAAsB,EACvB,MAAM,iBAAiB,CAAA;AAExB,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA"}
|