@cortexkit/common-auth 0.2.0 → 0.2.2
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/store/errors.d.ts +1 -1
- package/dist/store/index.d.ts +1 -1
- package/dist/store/mutate.d.ts +13 -0
- package/dist/store/mutate.js +45 -0
- package/dist/store/pool.d.ts +12 -1
- package/dist/store/pool.js +4 -1
- package/dist/store/rows.d.ts +7 -0
- package/dist/store/rows.js +8 -0
- package/package.json +1 -1
package/dist/store/errors.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { StoredCredential } from './schema.js';
|
|
2
2
|
/** Every library operation that can fail, as named in the failure value. */
|
|
3
|
-
export type PoolOperation = 'add' | 'replace' | 'rotate' | 'disable' | 'recordIdentity' | 'refresh' | 'pull';
|
|
3
|
+
export type PoolOperation = 'initialize' | 'add' | 'replace' | 'rotate' | 'disable' | 'recordIdentity' | 'refresh' | 'pull';
|
|
4
4
|
/**
|
|
5
5
|
* How far an operation got before it failed.
|
|
6
6
|
*
|
package/dist/store/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export type { PoolFailureKind, PoolFailurePhase, PoolOperation, } from './errors
|
|
|
3
3
|
export { PoolOperationError, PoolReentryError } from './errors.js';
|
|
4
4
|
export type { PoolLogger } from './hooks.js';
|
|
5
5
|
export { countUnknownIdentityRows, DUPLICATE_IDENTITY_REASON, } from './identity.js';
|
|
6
|
-
export type { HoldPoint, WriteStep } from './mutate.js';
|
|
6
|
+
export type { HoldPoint, InitializeOutcome, WriteStep } from './mutate.js';
|
|
7
7
|
export type { OpenPoolStoreOptions, PoolLoad, PoolStore, } from './pool.js';
|
|
8
8
|
export { openPoolStore } from './pool.js';
|
|
9
9
|
export type { PullHook, PullRequest } from './pull.js';
|
package/dist/store/mutate.d.ts
CHANGED
|
@@ -92,6 +92,19 @@ export declare class Transaction {
|
|
|
92
92
|
commitState(committed?: StoredCredential): Promise<void>;
|
|
93
93
|
private write;
|
|
94
94
|
}
|
|
95
|
+
/** What `initializePool` did. */
|
|
96
|
+
export type InitializeOutcome = 'initialized' | 'already-ready';
|
|
97
|
+
/**
|
|
98
|
+
* Adds the pool key to a config that holds a legacy roster without one, under
|
|
99
|
+
* the store-lock list, in one config write: `commonAuthPool` with the schema
|
|
100
|
+
* version and no row entries, the legacy roster and every other top-level key
|
|
101
|
+
* kept, except the keys named in `dropKeys`. The state file is not touched.
|
|
102
|
+
* This is the only write the store makes to a pending-migration config; it is
|
|
103
|
+
* where a plugin's migration starts, and every later row goes through the
|
|
104
|
+
* ordinary operations. A config that is already a pool is left alone; a load
|
|
105
|
+
* error refuses.
|
|
106
|
+
*/
|
|
107
|
+
export declare function initializePool(ctx: StoreContext, dropKeys: readonly string[]): Promise<InitializeOutcome>;
|
|
95
108
|
/**
|
|
96
109
|
* Runs `fn` under the store-lock list. The pool must be ready: a pending
|
|
97
110
|
* migration or a load error refuses before anything is written.
|
package/dist/store/mutate.js
CHANGED
|
@@ -195,6 +195,51 @@ export class Transaction {
|
|
|
195
195
|
await this.ctx.onStep?.(`after-${file}-write`, this.info);
|
|
196
196
|
}
|
|
197
197
|
}
|
|
198
|
+
/**
|
|
199
|
+
* Adds the pool key to a config that holds a legacy roster without one, under
|
|
200
|
+
* the store-lock list, in one config write: `commonAuthPool` with the schema
|
|
201
|
+
* version and no row entries, the legacy roster and every other top-level key
|
|
202
|
+
* kept, except the keys named in `dropKeys`. The state file is not touched.
|
|
203
|
+
* This is the only write the store makes to a pending-migration config; it is
|
|
204
|
+
* where a plugin's migration starts, and every later row goes through the
|
|
205
|
+
* ordinary operations. A config that is already a pool is left alone; a load
|
|
206
|
+
* error refuses.
|
|
207
|
+
*/
|
|
208
|
+
export async function initializePool(ctx, dropKeys) {
|
|
209
|
+
const locks = new LockStack(ctx.lockDefaults, ctx.lockEnv);
|
|
210
|
+
const progress = { writes: 0 };
|
|
211
|
+
try {
|
|
212
|
+
for (const spec of ctx.storeLocks)
|
|
213
|
+
await locks.acquire(spec);
|
|
214
|
+
const result = await readPool(ctx);
|
|
215
|
+
if (result.status === 'error')
|
|
216
|
+
throw notReadyError(result, 'initialize', undefined);
|
|
217
|
+
if (result.status === 'ready')
|
|
218
|
+
return 'already-ready';
|
|
219
|
+
const next = {};
|
|
220
|
+
for (const [key, value] of Object.entries(result.config))
|
|
221
|
+
if (!dropKeys.includes(key))
|
|
222
|
+
next[key] = value;
|
|
223
|
+
next.version = LEGACY_STORE_VERSION;
|
|
224
|
+
next[POOL_KEY] = { schemaVersion: POOL_SCHEMA_VERSION, [POOL_ROWS_KEY]: {} };
|
|
225
|
+
const info = { operation: 'initialize', rowId: undefined };
|
|
226
|
+
await writeJsonAtomic(ctx.configPath, next, {
|
|
227
|
+
beforeRename: async () => {
|
|
228
|
+
await ctx.onStep?.('before-config-write', info);
|
|
229
|
+
await locks.assertAll();
|
|
230
|
+
},
|
|
231
|
+
});
|
|
232
|
+
progress.writes++;
|
|
233
|
+
await ctx.onStep?.('after-config-write', info);
|
|
234
|
+
return 'initialized';
|
|
235
|
+
}
|
|
236
|
+
catch (error) {
|
|
237
|
+
throw toFailure(error, 'initialize', undefined, progress);
|
|
238
|
+
}
|
|
239
|
+
finally {
|
|
240
|
+
await locks.releaseAll();
|
|
241
|
+
}
|
|
242
|
+
}
|
|
198
243
|
/**
|
|
199
244
|
* Runs `fn` under the store-lock list. The pool must be ready: a pending
|
|
200
245
|
* migration or a load error refuses before anything is written.
|
package/dist/store/pool.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type Attribution } from './attribution.js';
|
|
2
2
|
import type { PoolOperationError } from './errors.js';
|
|
3
3
|
import type { PoolLogger } from './hooks.js';
|
|
4
|
-
import { type HoldPoint, type StoreContext } from './mutate.js';
|
|
4
|
+
import { type HoldPoint, type InitializeOutcome, type StoreContext } from './mutate.js';
|
|
5
5
|
import { type PullHook } from './pull.js';
|
|
6
6
|
import { type ProviderRefresh, type RefreshOptions, type RefreshOutcome } from './refresh.js';
|
|
7
7
|
import { type LockEnvironment, type PoolLockOptions, type PoolLockSpec } from './refresh-lock.js';
|
|
@@ -55,6 +55,17 @@ export interface PoolStore {
|
|
|
55
55
|
load(): Promise<PoolLoad>;
|
|
56
56
|
/** Reads the pool without firing anything. */
|
|
57
57
|
read(): Promise<PoolLoad>;
|
|
58
|
+
/**
|
|
59
|
+
* Turns a pending-migration config into an empty pool (the pool key with no
|
|
60
|
+
* row entries) in one locked config write, dropping the named top-level
|
|
61
|
+
* keys and keeping the rest. The start of a plugin's migration; a ready
|
|
62
|
+
* pool is left alone. Failures carry operation `initialize`.
|
|
63
|
+
*/
|
|
64
|
+
initialize(input?: {
|
|
65
|
+
dropKeys?: readonly string[];
|
|
66
|
+
}): Promise<{
|
|
67
|
+
status: InitializeOutcome;
|
|
68
|
+
}>;
|
|
58
69
|
add(input: AddInput, options?: RowOperationOptions): Promise<AddResult>;
|
|
59
70
|
replace(id: string, credential: PoolCredential, input?: {
|
|
60
71
|
identity?: string;
|
package/dist/store/pool.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { recordQuota } from './attribution.js';
|
|
2
|
-
import { readPool } from './mutate.js';
|
|
2
|
+
import { initializePool, readPool, } from './mutate.js';
|
|
3
3
|
import { PullScheduler } from './pull.js';
|
|
4
4
|
import { refreshRow, } from './refresh.js';
|
|
5
5
|
import { POOL_LOCK_DEFAULTS, } from './refresh-lock.js';
|
|
@@ -85,6 +85,9 @@ export function openPoolStore(options) {
|
|
|
85
85
|
async read() {
|
|
86
86
|
return toLoad(await readPool(ctx));
|
|
87
87
|
},
|
|
88
|
+
async initialize(input = {}) {
|
|
89
|
+
return { status: await initializePool(ctx, input.dropKeys ?? []) };
|
|
90
|
+
},
|
|
88
91
|
add: (input, callOptions) => addRow(rt, input, callOptions),
|
|
89
92
|
replace: (id, credential, input, callOptions) => replaceRow(rt, id, credential, input, callOptions),
|
|
90
93
|
rotate: (id, credential, input, callOptions) => rotateRow(rt, id, credential, input, callOptions),
|
package/dist/store/rows.d.ts
CHANGED
|
@@ -9,6 +9,13 @@ export interface RowOperationOptions {
|
|
|
9
9
|
onFailure?: FailureHook;
|
|
10
10
|
/** The provider-wide lock, when an operation may change identity keying. */
|
|
11
11
|
providerLock?: PoolLockSpec;
|
|
12
|
+
/**
|
|
13
|
+
* Further locks taken after the row lock and the provider-wide lock, in
|
|
14
|
+
* this order, before the store locks: the same place `refresh` takes its
|
|
15
|
+
* extra locks, so a caller holding legacy locks around a row write and a
|
|
16
|
+
* refresh of that row acquire them in one order and cannot deadlock.
|
|
17
|
+
*/
|
|
18
|
+
extraLocks?: readonly PoolLockSpec[];
|
|
12
19
|
}
|
|
13
20
|
export interface AddInput {
|
|
14
21
|
id: string;
|
package/dist/store/rows.js
CHANGED
|
@@ -78,6 +78,8 @@ export async function addRow(rt, input, options = {}) {
|
|
|
78
78
|
await locks.acquire(rowLockSpec(rt, { id, identity }));
|
|
79
79
|
if (credential.type === 'oauth')
|
|
80
80
|
await locks.acquire(options.providerLock ?? rt.providerLock);
|
|
81
|
+
for (const extra of options.extraLocks ?? [])
|
|
82
|
+
await locks.acquire(extra);
|
|
81
83
|
return withTransaction(ctx, locks, progress, { operation: 'add', rowId: id }, async (tx) => {
|
|
82
84
|
const rows = tx.rows();
|
|
83
85
|
const fingerprint = fingerprintOf(credential);
|
|
@@ -150,6 +152,8 @@ export async function replaceRow(rt, id, credential, input = {}, options = {}) {
|
|
|
150
152
|
await locks.acquire(rowLockSpec(rt, seen));
|
|
151
153
|
if (credential.type === 'oauth')
|
|
152
154
|
await locks.acquire(options.providerLock ?? rt.providerLock);
|
|
155
|
+
for (const extra of options.extraLocks ?? [])
|
|
156
|
+
await locks.acquire(extra);
|
|
153
157
|
return withTransaction(ctx, locks, progress, { operation: 'replace', rowId: id }, async (tx) => {
|
|
154
158
|
const row = requireUsableRow('replace', id, tx.row(id), credential);
|
|
155
159
|
if (rowLockKey(row) !== rowLockKey(seen))
|
|
@@ -197,6 +201,8 @@ export async function rotateRow(rt, id, credential, input = {}, options = {}) {
|
|
|
197
201
|
await locks.acquire(rowLockSpec(rt, seen));
|
|
198
202
|
if (input.identity !== undefined && credential.type === 'oauth')
|
|
199
203
|
await locks.acquire(options.providerLock ?? rt.providerLock);
|
|
204
|
+
for (const extra of options.extraLocks ?? [])
|
|
205
|
+
await locks.acquire(extra);
|
|
200
206
|
return withTransaction(ctx, locks, progress, { operation: 'rotate', rowId: id }, async (tx) => {
|
|
201
207
|
const row = requireUsableRow('rotate', id, tx.row(id), credential);
|
|
202
208
|
if (rowLockKey(row) !== rowLockKey(seen))
|
|
@@ -238,6 +244,8 @@ export async function recordRowIdentity(rt, id, identity, options = {}) {
|
|
|
238
244
|
const { row: seen } = await readRow(rt, 'recordIdentity', id);
|
|
239
245
|
await locks.acquire(rowLockSpec(rt, seen));
|
|
240
246
|
await locks.acquire(options.providerLock ?? rt.providerLock);
|
|
247
|
+
for (const extra of options.extraLocks ?? [])
|
|
248
|
+
await locks.acquire(extra);
|
|
241
249
|
return withTransaction(rt.ctx, locks, progress, { operation: 'recordIdentity', rowId: id }, async (tx) => {
|
|
242
250
|
const row = requireUsableRow('recordIdentity', id, tx.row(id));
|
|
243
251
|
if (rowLockKey(row) !== rowLockKey(seen))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cortexkit/common-auth",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Shared plumbing for the CortexKit auth plugins: loopback RPC, file locks and atomic writes, logger, sidebar state file, TUI preferences and TUI build.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|