@cortexkit/common-auth 0.5.0 → 0.7.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/dist/fs/index.d.ts +2 -1
- package/dist/fs/refresh-file-lock.d.ts +15 -4
- package/dist/fs/refresh-file-lock.js +67 -7
- package/dist/fs/with-lock.d.ts +9 -8
- package/dist/store/errors.d.ts +2 -2
- package/dist/store/identity.d.ts +5 -0
- package/dist/store/identity.js +16 -0
- package/dist/store/index.d.ts +5 -3
- package/dist/store/index.js +2 -1
- package/dist/store/mutate.d.ts +3 -1
- package/dist/store/mutate.js +4 -0
- package/dist/store/pool.d.ts +31 -16
- package/dist/store/pool.js +3 -0
- package/dist/store/provider-state.d.ts +131 -0
- package/dist/store/provider-state.js +223 -0
- package/dist/store/refresh.d.ts +8 -0
- package/dist/store/refresh.js +9 -0
- package/dist/store/rows.d.ts +65 -15
- package/dist/store/rows.js +195 -59
- package/dist/store/schema.d.ts +114 -4
- package/dist/store/schema.js +85 -8
- package/dist/store/torn.d.ts +31 -1
- package/dist/store/torn.js +76 -2
- package/package.json +1 -1
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { assertNotInsideHook, runInsideHook } from './hooks.js';
|
|
2
|
+
import { runOperation, withTransaction } from './mutate.js';
|
|
3
|
+
import { readRow, refusal, requireBound, rowLockSpec, unknownRow, } from './runtime.js';
|
|
4
|
+
import { boundProviderStateDigest, CREDENTIAL_STAMP_KEY, credentialDigest, isCredentialEpoch, isRecord, PROVIDER_STATE_KEY, parseStamp, providerStateDigest, rowLockKey, } from './schema.js';
|
|
5
|
+
/**
|
|
6
|
+
* A provider state as the store will store it: a JSON round trip of it (so
|
|
7
|
+
* its digest is the same once read back from disk), accepted by the codec.
|
|
8
|
+
* Refused before anything is written when the store has no codec, the value
|
|
9
|
+
* is not JSON, or the codec rejects it.
|
|
10
|
+
*/
|
|
11
|
+
export function acceptProviderState(codec, operation, id, value, what = 'the provider state') {
|
|
12
|
+
if (!codec)
|
|
13
|
+
throw refusal(operation, id, 'invalid-input', 'the store was opened without a provider-state codec');
|
|
14
|
+
let normalized;
|
|
15
|
+
try {
|
|
16
|
+
const text = JSON.stringify(value);
|
|
17
|
+
normalized = text === undefined ? undefined : JSON.parse(text);
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
normalized = undefined;
|
|
21
|
+
}
|
|
22
|
+
if (normalized === undefined)
|
|
23
|
+
throw refusal(operation, id, 'invalid-provider-state', `${what} cannot be stored as JSON`);
|
|
24
|
+
if (!codec.validate(normalized))
|
|
25
|
+
throw refusal(operation, id, 'invalid-provider-state', `the provider-state codec rejected ${what}`);
|
|
26
|
+
return normalized;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The write for a value a credential write brings (`add` of a secret the
|
|
30
|
+
* pool holds, `rotate`, a refresh): merged with the row's value on disk by
|
|
31
|
+
* the codec's `merge` when both exist, else the incoming value as is.
|
|
32
|
+
*/
|
|
33
|
+
export function mergedProviderState(codec, operation, id, onDisk, incoming) {
|
|
34
|
+
if (onDisk === undefined || !codec?.merge)
|
|
35
|
+
return { kind: 'set', value: incoming };
|
|
36
|
+
return {
|
|
37
|
+
kind: 'set',
|
|
38
|
+
value: acceptProviderState(codec, operation, id, codec.merge(structuredClone(onDisk), structuredClone(incoming)), 'the merged provider state'),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The provider state a replace leaves on the row, decided before anything is
|
|
43
|
+
* written: whatever the codec's `onReplace` returns (undefined clears it), or
|
|
44
|
+
* without that hook the value handed to `replace`, else nothing. The old
|
|
45
|
+
* credential's value is never kept by default: it describes the account the
|
|
46
|
+
* replaced credential belonged to.
|
|
47
|
+
*/
|
|
48
|
+
export function replacementProviderState(codec, row, credentialEpoch, identity, incoming) {
|
|
49
|
+
const hook = codec?.onReplace;
|
|
50
|
+
const next = hook
|
|
51
|
+
? hook(row.providerState === undefined
|
|
52
|
+
? undefined
|
|
53
|
+
: structuredClone(row.providerState), {
|
|
54
|
+
id: row.id,
|
|
55
|
+
credentialEpoch,
|
|
56
|
+
...(identity !== undefined ? { identity } : {}),
|
|
57
|
+
...(incoming !== undefined
|
|
58
|
+
? { incoming: structuredClone(incoming) }
|
|
59
|
+
: {}),
|
|
60
|
+
})
|
|
61
|
+
: incoming;
|
|
62
|
+
if (next === undefined)
|
|
63
|
+
return { kind: 'clear' };
|
|
64
|
+
if (!hook)
|
|
65
|
+
return { kind: 'set', value: next };
|
|
66
|
+
return {
|
|
67
|
+
kind: 'set',
|
|
68
|
+
value: acceptProviderState(codec, 'replace', row.id, next, 'the provider state onReplace returned'),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* The provider-state digest the stamp of a credential write carries, and the
|
|
73
|
+
* state-file fields it changes. `set` binds the new value; `keep` carries the
|
|
74
|
+
* old stamp's digest forward only when that stamp bound it to this row as it
|
|
75
|
+
* stood before the write (same credential lineage, the epoch being written,
|
|
76
|
+
* the identity recorded before the write); `clear` binds nothing.
|
|
77
|
+
*/
|
|
78
|
+
export function providerStateCoverage(codec, write, prior, priorRow, credentialEpoch) {
|
|
79
|
+
if (write.kind === 'set')
|
|
80
|
+
return providerStateDigest(codec, write.value);
|
|
81
|
+
if (write.kind === 'clear')
|
|
82
|
+
return undefined;
|
|
83
|
+
return boundProviderStateDigest(prior, priorRow?.credential, credentialEpoch, priorRow?.identity);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Changes a row's provider state without touching its credential, in one
|
|
87
|
+
* state-file write under the row lock, the caller's extra locks and the
|
|
88
|
+
* store locks. `fence` is what the caller read the row at: the write is
|
|
89
|
+
* refused (`attribution`, retryable) when the row has since moved to another
|
|
90
|
+
* credential epoch or recorded identity, and (`unknown-row`) once it is
|
|
91
|
+
* removed, so a writer that read the row before a replace or a removal never
|
|
92
|
+
* lands its value on the new credential or brings a removed row's state back.
|
|
93
|
+
*
|
|
94
|
+
* The value is bound by the stamp already beside the credential. When its
|
|
95
|
+
* credential-bound part is unchanged the stamp is left byte for byte as it
|
|
96
|
+
* is; otherwise only the stamp's provider-state digest changes, so the
|
|
97
|
+
* credential's stamp status never moves. With `requireCredentialStamps`, an
|
|
98
|
+
* unbound row refuses (`unbound-credential`) as every other strict path
|
|
99
|
+
* does. Without it, a row whose stamp was not written by this store with this
|
|
100
|
+
* credential at the row's epoch and identity refuses the same way: no stamp
|
|
101
|
+
* could bind the value, so no reader would ever show it. A `rotate` or
|
|
102
|
+
* `replace` stamps such a row.
|
|
103
|
+
*/
|
|
104
|
+
export async function updateProviderStateRow(rt, id, fence, mutator, options = {}) {
|
|
105
|
+
assertNotInsideHook('updateProviderState');
|
|
106
|
+
const { ctx } = rt;
|
|
107
|
+
const operation = 'updateProviderState';
|
|
108
|
+
return runOperation(ctx, operation, id, options.onFailure, async (locks, progress) => {
|
|
109
|
+
const codec = ctx.providerState;
|
|
110
|
+
if (!codec)
|
|
111
|
+
throw refusal(operation, id, 'invalid-input', 'the store was opened without a provider-state codec');
|
|
112
|
+
if (typeof mutator !== 'function')
|
|
113
|
+
throw refusal(operation, id, 'invalid-input', 'a mutator is required');
|
|
114
|
+
const captured = isRecord(fence) ? fence.credentialEpoch : undefined;
|
|
115
|
+
if (!isCredentialEpoch(captured))
|
|
116
|
+
throw refusal(operation, id, 'invalid-input', 'the credential epoch the provider state was read at is required');
|
|
117
|
+
const { row: seen } = await readRow(rt, operation, id);
|
|
118
|
+
await locks.acquire(rowLockSpec(rt, seen));
|
|
119
|
+
for (const extra of options.extraLocks ?? [])
|
|
120
|
+
await locks.acquire(extra);
|
|
121
|
+
return withTransaction(ctx, locks, progress, { operation, rowId: id }, async (tx) => {
|
|
122
|
+
const row = tx.row(id);
|
|
123
|
+
if (!row)
|
|
124
|
+
throw unknownRow(operation, id);
|
|
125
|
+
if (rowLockKey(row) !== rowLockKey(seen))
|
|
126
|
+
throw refusal(operation, id, 'row-key-changed', `row ${id}'s wire identity changed while its lock was being taken`, true);
|
|
127
|
+
if (row.invalid)
|
|
128
|
+
throw refusal(operation, id, 'invalid-row', `row ${id} failed validation`);
|
|
129
|
+
if (!row.credential)
|
|
130
|
+
throw refusal(operation, id, 'no-credential', `row ${id} holds no credential`);
|
|
131
|
+
requireBound(operation, row);
|
|
132
|
+
const epoch = row.credentialEpoch ?? 1;
|
|
133
|
+
if (epoch !== captured || row.identity !== fence.identity)
|
|
134
|
+
throw refusal(operation, id, 'attribution', `the provider state for ${id} was read for a credential or account the row no longer holds`, true);
|
|
135
|
+
const plan = await planProviderStateIn(tx, codec, operation, row, mutator);
|
|
136
|
+
if (plan.kind !== 'changed')
|
|
137
|
+
return {
|
|
138
|
+
id,
|
|
139
|
+
outcome: 'unchanged',
|
|
140
|
+
...(plan.kind === 'unchanged' && plan.value !== undefined
|
|
141
|
+
? { providerState: plan.value }
|
|
142
|
+
: {}),
|
|
143
|
+
};
|
|
144
|
+
tx.setStateAccount(id, plan.account);
|
|
145
|
+
await tx.commitState();
|
|
146
|
+
return plan.value === undefined
|
|
147
|
+
? { id, outcome: 'cleared' }
|
|
148
|
+
: { id, outcome: 'updated', providerState: plan.value };
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Returned by the provider-state mutator of an attributed `disable` or
|
|
154
|
+
* `enable` to decline the whole transition: nothing is written, neither the
|
|
155
|
+
* provider state nor the row's enabled flag, and the call resolves with
|
|
156
|
+
* `declined: true`. A mutator declines when the state it is shown is newer
|
|
157
|
+
* than what its caller saw, such as an eligibility recorded after the
|
|
158
|
+
* request whose refusal is being acted on. It is a value of its own because
|
|
159
|
+
* `undefined` already means "clear the provider state". `Symbol.for` keeps it
|
|
160
|
+
* equal across two copies of this module loaded in one process.
|
|
161
|
+
*/
|
|
162
|
+
export const DECLINE_TRANSITION = Symbol.for('@cortexkit/common-auth/store/decline-transition');
|
|
163
|
+
/**
|
|
164
|
+
* Runs a provider-state mutator for a row loaded under every lock and
|
|
165
|
+
* already checked by the caller (present, valid, inside its attribution
|
|
166
|
+
* fence), and plans the write. Refuses (`no-credential`) a row holding no
|
|
167
|
+
* credential, and (`unbound-credential`) one whose credential carries no
|
|
168
|
+
* stamp of this store at the row's epoch and identity: no stamp could bind
|
|
169
|
+
* the value, so no reader would ever show it. `DECLINE_TRANSITION` is
|
|
170
|
+
* honoured only when `declinable` is set; elsewhere it is not JSON and is
|
|
171
|
+
* refused as such.
|
|
172
|
+
*/
|
|
173
|
+
export async function planProviderStateIn(tx, codec, operation, row, mutator, declinable = false) {
|
|
174
|
+
const id = row.id;
|
|
175
|
+
const credential = row.credential;
|
|
176
|
+
if (!credential)
|
|
177
|
+
throw refusal(operation, id, 'no-credential', `row ${id} holds no credential`);
|
|
178
|
+
const epoch = row.credentialEpoch ?? 1;
|
|
179
|
+
const account = tx.stateAccount(id);
|
|
180
|
+
const rawStamp = account?.[CREDENTIAL_STAMP_KEY];
|
|
181
|
+
const stamp = parseStamp(rawStamp);
|
|
182
|
+
if (!isRecord(rawStamp) ||
|
|
183
|
+
!stamp?.binding ||
|
|
184
|
+
stamp.digest !== credentialDigest(credential) ||
|
|
185
|
+
stamp.credentialEpoch !== epoch ||
|
|
186
|
+
stamp.binding.identity !== row.identity)
|
|
187
|
+
throw refusal(operation, id, 'unbound-credential', `row ${id}'s credential carries no stamp of this store to bind a provider state to (stamp ${row.stamp}); rotate or replace it first`);
|
|
188
|
+
const current = row.providerState === undefined
|
|
189
|
+
? undefined
|
|
190
|
+
: structuredClone(row.providerState);
|
|
191
|
+
const returned = await runInsideHook(operation, () => mutator(current, row));
|
|
192
|
+
if (declinable && returned === DECLINE_TRANSITION)
|
|
193
|
+
return { kind: 'declined' };
|
|
194
|
+
const next = returned === undefined
|
|
195
|
+
? undefined
|
|
196
|
+
: acceptProviderState(codec, operation, id, returned, 'the provider state the mutator returned');
|
|
197
|
+
const held = account !== undefined && Object.hasOwn(account, PROVIDER_STATE_KEY);
|
|
198
|
+
if (next === undefined
|
|
199
|
+
? !held
|
|
200
|
+
: row.providerState !== undefined &&
|
|
201
|
+
JSON.stringify(row.providerState) === JSON.stringify(next))
|
|
202
|
+
return { kind: 'unchanged', ...(next !== undefined ? { value: next } : {}) };
|
|
203
|
+
const nextAccount = { ...account };
|
|
204
|
+
if (next === undefined) {
|
|
205
|
+
delete nextAccount[PROVIDER_STATE_KEY];
|
|
206
|
+
const nextStamp = { ...rawStamp };
|
|
207
|
+
delete nextStamp.providerState;
|
|
208
|
+
nextAccount[CREDENTIAL_STAMP_KEY] = nextStamp;
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
nextAccount[PROVIDER_STATE_KEY] = next;
|
|
212
|
+
const digest = providerStateDigest(codec, next);
|
|
213
|
+
// A change confined to the part the codec does not bind to the
|
|
214
|
+
// credential leaves the stamp exactly as it was.
|
|
215
|
+
if (stamp.providerState !== digest)
|
|
216
|
+
nextAccount[CREDENTIAL_STAMP_KEY] = { ...rawStamp, providerState: digest };
|
|
217
|
+
}
|
|
218
|
+
return {
|
|
219
|
+
kind: 'changed',
|
|
220
|
+
...(next !== undefined ? { value: next } : {}),
|
|
221
|
+
account: nextAccount,
|
|
222
|
+
};
|
|
223
|
+
}
|
package/dist/store/refresh.d.ts
CHANGED
|
@@ -10,6 +10,14 @@ export interface ProviderRefreshResult {
|
|
|
10
10
|
expiresIn?: number;
|
|
11
11
|
/** The account's wire identity, when the provider reports one. */
|
|
12
12
|
identity?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Provider state that changes with the new token (needs the store's
|
|
15
|
+
* provider-state codec). It is merged with the row's value on disk
|
|
16
|
+
* (`ProviderStateCodec.merge`) and written in the same state write as the
|
|
17
|
+
* rotated credential, under the same commit fence. Left out, the row keeps
|
|
18
|
+
* its value.
|
|
19
|
+
*/
|
|
20
|
+
providerState?: unknown;
|
|
13
21
|
}
|
|
14
22
|
export type ProviderRefresh = (credential: OAuthCredential & {
|
|
15
23
|
lastRefreshedAt?: number;
|
package/dist/store/refresh.js
CHANGED
|
@@ -2,6 +2,7 @@ import { PoolOperationError } from './errors.js';
|
|
|
2
2
|
import { assertNotInsideHook, runInsideHook } from './hooks.js';
|
|
3
3
|
import { recordIdentityIn } from './identity.js';
|
|
4
4
|
import { runOperation, withTransaction } from './mutate.js';
|
|
5
|
+
import { acceptProviderState, mergedProviderState } from './provider-state.js';
|
|
5
6
|
import { rotateIn } from './rows.js';
|
|
6
7
|
import { readRow, refusal, requireBound, rowLockSpec, } from './runtime.js';
|
|
7
8
|
import { rotationStamp, rotationStampUntrusted, rowLockKey, } from './schema.js';
|
|
@@ -98,6 +99,9 @@ export async function refreshRow(rt, id, provider, options = {}) {
|
|
|
98
99
|
}
|
|
99
100
|
if (typeof result?.refresh !== 'string' || !result.refresh.trim())
|
|
100
101
|
throw refusal('refresh', id, 'provider', 'the provider returned no refresh token', true);
|
|
102
|
+
const incoming = result.providerState === undefined
|
|
103
|
+
? undefined
|
|
104
|
+
: acceptProviderState(ctx.providerState, 'refresh', id, result.providerState, 'the provider state the provider returned');
|
|
101
105
|
const commit = await withTransaction(ctx, locks, progress, { operation: 'refresh', rowId: id }, async (tx) => {
|
|
102
106
|
const current = tx.row(id);
|
|
103
107
|
const entry = tx.entry(id);
|
|
@@ -142,6 +146,11 @@ export async function refreshRow(rt, id, provider, options = {}) {
|
|
|
142
146
|
const stored = await rotateIn(rt, tx, id, credential, {
|
|
143
147
|
stamp: rotationStamp(prior, now),
|
|
144
148
|
identity: learnt,
|
|
149
|
+
...(incoming !== undefined
|
|
150
|
+
? {
|
|
151
|
+
providerState: mergedProviderState(ctx.providerState, 'refresh', id, current.providerState, incoming),
|
|
152
|
+
}
|
|
153
|
+
: {}),
|
|
145
154
|
});
|
|
146
155
|
let identity = current.identity;
|
|
147
156
|
if (learnt !== undefined) {
|
package/dist/store/rows.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Attribution } from './attribution.js';
|
|
2
2
|
import { PoolOperationError } from './errors.js';
|
|
3
3
|
import { type Transaction } from './mutate.js';
|
|
4
|
+
import { type ProviderStateWrite, type RowTransitionMutator, type UpdateProviderStateResult } from './provider-state.js';
|
|
4
5
|
import type { PoolLockSpec } from './refresh-lock.js';
|
|
5
6
|
import { type StoreRuntime } from './runtime.js';
|
|
6
7
|
import { type CredentialBinding, type PoolCredential, type PoolRow, type RotateCredential, type StoredCredential } from './schema.js';
|
|
@@ -80,6 +81,24 @@ export interface AddInput {
|
|
|
80
81
|
credential: PoolCredential;
|
|
81
82
|
identity?: string;
|
|
82
83
|
label?: string;
|
|
84
|
+
/**
|
|
85
|
+
* Provider state for the credential, written in the same state write as
|
|
86
|
+
* the credential (needs the store's provider-state codec). On an `add`
|
|
87
|
+
* that rotates a row already holding this secret it is merged with the
|
|
88
|
+
* row's value (`ProviderStateCodec.merge`); left out, that row keeps its
|
|
89
|
+
* value.
|
|
90
|
+
*/
|
|
91
|
+
providerState?: unknown;
|
|
92
|
+
}
|
|
93
|
+
/** What `replace` and `rotate` take beside the credential. */
|
|
94
|
+
export interface CredentialWriteInput {
|
|
95
|
+
identity?: string;
|
|
96
|
+
/**
|
|
97
|
+
* Provider state written in the same state write as the credential. For
|
|
98
|
+
* `rotate` it is merged with the row's value; left out, the row keeps its
|
|
99
|
+
* value. For `replace` see `ProviderStateCodec.onReplace`.
|
|
100
|
+
*/
|
|
101
|
+
providerState?: unknown;
|
|
83
102
|
}
|
|
84
103
|
export type AddResult = {
|
|
85
104
|
/** The row holding the credential; an existing row's id on a re-add. */
|
|
@@ -102,39 +121,70 @@ export declare function rotateIn(rt: StoreRuntime, tx: Transaction, id: string,
|
|
|
102
121
|
clearErrors?: boolean;
|
|
103
122
|
binding?: CredentialBinding;
|
|
104
123
|
identity?: string;
|
|
124
|
+
providerState?: ProviderStateWrite;
|
|
105
125
|
}): Promise<StoredCredential>;
|
|
106
126
|
export declare function addRow(rt: StoreRuntime, input: AddInput, options?: RowOperationOptions): Promise<AddResult>;
|
|
107
|
-
export declare function replaceRow(rt: StoreRuntime, id: string, credential: PoolCredential, input?: {
|
|
108
|
-
identity?: string;
|
|
109
|
-
}, options?: RowOperationOptions): Promise<{
|
|
127
|
+
export declare function replaceRow(rt: StoreRuntime, id: string, credential: PoolCredential, input?: CredentialWriteInput, options?: RowOperationOptions): Promise<{
|
|
110
128
|
id: string;
|
|
111
129
|
credential: StoredCredential;
|
|
112
130
|
credentialEpoch: number;
|
|
113
131
|
}>;
|
|
114
|
-
export declare function rotateRow(rt: StoreRuntime, id: string, credential: RotateCredential, input?: {
|
|
115
|
-
identity?: string;
|
|
116
|
-
}, options?: RowOperationOptions): Promise<{
|
|
132
|
+
export declare function rotateRow(rt: StoreRuntime, id: string, credential: RotateCredential, input?: CredentialWriteInput, options?: RowOperationOptions): Promise<{
|
|
117
133
|
id: string;
|
|
118
134
|
credential: StoredCredential;
|
|
119
135
|
}>;
|
|
120
136
|
/**
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
* for a refresh of the row instead of landing during its provider call.
|
|
137
|
+
* Options of `disable` and `enable`. A call that passes neither
|
|
138
|
+
* `attribution` nor `providerState` behaves exactly as it did before 0.7.0.
|
|
124
139
|
*/
|
|
125
|
-
export
|
|
140
|
+
export interface RowTransitionOptions extends RowToggleOptions {
|
|
141
|
+
/**
|
|
142
|
+
* The credential epoch and recorded identity the caller's evidence for the
|
|
143
|
+
* transition was obtained under (as `recordQuota`'s attribution: an
|
|
144
|
+
* identity left out means the row had none). The call is refused
|
|
145
|
+
* (`attribution`, retryable, nothing written) once the row holds another
|
|
146
|
+
* epoch or identity, so a provider's late answer about a replaced
|
|
147
|
+
* credential never disables, or switches back on, the row now holding its
|
|
148
|
+
* successor.
|
|
149
|
+
*/
|
|
150
|
+
attribution?: Attribution;
|
|
151
|
+
/**
|
|
152
|
+
* A provider-state change made in the same transaction as the transition,
|
|
153
|
+
* under the rules of `updateProviderState` (codec validation, the stamp
|
|
154
|
+
* rebound to the value, `unbound-credential` for a row no stamp of this
|
|
155
|
+
* store can bind it to); it requires `attribution`. The value and the
|
|
156
|
+
* enabled flag land together: no reader, and no crash at any write point,
|
|
157
|
+
* shows one without the other. Returning `DECLINE_TRANSITION` declines the
|
|
158
|
+
* whole call and writes nothing.
|
|
159
|
+
*/
|
|
160
|
+
providerState?: RowTransitionMutator;
|
|
161
|
+
}
|
|
162
|
+
export interface RowTransitionResult {
|
|
126
163
|
id: string;
|
|
127
|
-
|
|
164
|
+
/** The provider-state mutator declined: nothing was written. */
|
|
165
|
+
declined?: true;
|
|
166
|
+
/**
|
|
167
|
+
* Set when a provider-state mutator ran and did not decline: what it did
|
|
168
|
+
* to the value, as `updateProviderState` reports it.
|
|
169
|
+
*/
|
|
170
|
+
providerStateOutcome?: UpdateProviderStateResult['outcome'];
|
|
171
|
+
/** The provider state the row now holds, when a mutator ran and left one. */
|
|
172
|
+
providerState?: unknown;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Marks a row disabled with a reason. See `RowTransitionOptions` for the
|
|
176
|
+
* attributed form, which may change the provider state with it.
|
|
177
|
+
*/
|
|
178
|
+
export declare function disableRow(rt: StoreRuntime, id: string, reason: string, options?: RowTransitionOptions): Promise<RowTransitionResult>;
|
|
128
179
|
/**
|
|
129
180
|
* Clears a row's `enabled: false` and its `disabledReason` in one config
|
|
130
181
|
* write. An OAuth row whose recorded identity another enabled OAuth row holds
|
|
131
182
|
* stays disabled and the call refuses (`duplicate-identity`): the same rule
|
|
132
183
|
* that makes `add` store such a row disabled. Enabling a row that is already
|
|
133
|
-
* enabled writes nothing.
|
|
184
|
+
* enabled writes nothing. See `RowTransitionOptions` for the attributed
|
|
185
|
+
* form, which may change the provider state with it.
|
|
134
186
|
*/
|
|
135
|
-
export declare function enableRow(rt: StoreRuntime, id: string, options?:
|
|
136
|
-
id: string;
|
|
137
|
-
}>;
|
|
187
|
+
export declare function enableRow(rt: StoreRuntime, id: string, options?: RowTransitionOptions): Promise<RowTransitionResult>;
|
|
138
188
|
/**
|
|
139
189
|
* Deletes a row: its roster row and per-row entry (quota, epoch; the identity
|
|
140
190
|
* lives in the roster row) in one config write, then its credential and
|