@cortexkit/antigravity-auth-core 2.2.1 → 3.0.1
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 +24 -2
- package/dist/abort-forwarding.d.ts +3 -0
- package/dist/abort-forwarding.d.ts.map +1 -0
- package/dist/abort-forwarding.js +19 -0
- package/dist/abort-forwarding.js.map +1 -0
- package/dist/account-identity.d.ts +52 -0
- package/dist/account-identity.d.ts.map +1 -0
- package/dist/account-identity.js +66 -0
- package/dist/account-identity.js.map +1 -0
- package/dist/account-manager.d.ts +228 -43
- package/dist/account-manager.d.ts.map +1 -1
- package/dist/account-manager.js +942 -903
- package/dist/account-manager.js.map +1 -1
- package/dist/account-migration.d.ts +309 -0
- package/dist/account-migration.d.ts.map +1 -0
- package/dist/account-migration.js +2284 -0
- package/dist/account-migration.js.map +1 -0
- package/dist/account-repository-codecs.d.ts +82 -0
- package/dist/account-repository-codecs.d.ts.map +1 -0
- package/dist/account-repository-codecs.js +609 -0
- package/dist/account-repository-codecs.js.map +1 -0
- package/dist/account-repository-types.d.ts +831 -0
- package/dist/account-repository-types.d.ts.map +1 -0
- package/dist/account-repository-types.js +137 -0
- package/dist/account-repository-types.js.map +1 -0
- package/dist/account-repository.d.ts +442 -0
- package/dist/account-repository.d.ts.map +1 -0
- package/dist/account-repository.js +2253 -0
- package/dist/account-repository.js.map +1 -0
- package/dist/account-selector.d.ts +470 -0
- package/dist/account-selector.d.ts.map +1 -0
- package/dist/account-selector.js +1383 -0
- package/dist/account-selector.js.map +1 -0
- package/dist/account-storage.d.ts.map +1 -1
- package/dist/account-storage.js +5 -0
- package/dist/account-storage.js.map +1 -1
- package/dist/agy-request-metadata.d.ts.map +1 -1
- package/dist/agy-request-metadata.js +8 -21
- package/dist/agy-request-metadata.js.map +1 -1
- package/dist/agy-transport.d.ts.map +1 -1
- package/dist/agy-transport.js +21 -35
- package/dist/agy-transport.js.map +1 -1
- package/dist/antigravity-command-menu.d.ts +171 -0
- package/dist/antigravity-command-menu.d.ts.map +1 -0
- package/dist/antigravity-command-menu.js +523 -0
- package/dist/antigravity-command-menu.js.map +1 -0
- package/dist/atomic-write.d.ts +18 -1
- package/dist/atomic-write.d.ts.map +1 -1
- package/dist/atomic-write.js +15 -3
- package/dist/atomic-write.js.map +1 -1
- package/dist/common-auth-embedded/NOTICE.txt +21 -0
- package/dist/common-auth-embedded/auth-menu/accounts.d.ts +97 -0
- package/dist/common-auth-embedded/auth-menu/accounts.js +387 -0
- package/dist/common-auth-embedded/auth-menu/ansi.d.ts +26 -0
- package/dist/common-auth-embedded/auth-menu/ansi.js +98 -0
- package/dist/common-auth-embedded/auth-menu/confirm.d.ts +7 -0
- package/dist/common-auth-embedded/auth-menu/confirm.js +21 -0
- package/dist/common-auth-embedded/auth-menu/doctor.d.ts +52 -0
- package/dist/common-auth-embedded/auth-menu/doctor.js +81 -0
- package/dist/common-auth-embedded/auth-menu/index.d.ts +17 -0
- package/dist/common-auth-embedded/auth-menu/index.js +12 -0
- package/dist/common-auth-embedded/auth-menu/login.d.ts +55 -0
- package/dist/common-auth-embedded/auth-menu/login.js +83 -0
- package/dist/common-auth-embedded/auth-menu/menu.d.ts +59 -0
- package/dist/common-auth-embedded/auth-menu/menu.js +73 -0
- package/dist/common-auth-embedded/auth-menu/opencode-v1.d.ts +49 -0
- package/dist/common-auth-embedded/auth-menu/opencode-v1.js +47 -0
- package/dist/common-auth-embedded/auth-menu/select.d.ts +21 -0
- package/dist/common-auth-embedded/auth-menu/select.js +159 -0
- package/dist/common-auth-embedded/auth-menu/terminal.d.ts +43 -0
- package/dist/common-auth-embedded/auth-menu/terminal.js +19 -0
- package/dist/common-auth-embedded/claustrum/consumer.d.ts +102 -0
- package/dist/common-auth-embedded/claustrum/consumer.js +298 -0
- package/dist/common-auth-embedded/claustrum/custody.d.ts +175 -0
- package/dist/common-auth-embedded/claustrum/custody.js +354 -0
- package/dist/common-auth-embedded/claustrum/enrollment.d.ts +158 -0
- package/dist/common-auth-embedded/claustrum/enrollment.js +759 -0
- package/dist/common-auth-embedded/claustrum/errors.d.ts +16 -0
- package/dist/common-auth-embedded/claustrum/errors.js +8 -0
- package/dist/common-auth-embedded/claustrum/host-slot.d.ts +39 -0
- package/dist/common-auth-embedded/claustrum/host-slot.js +72 -0
- package/dist/common-auth-embedded/claustrum/index.d.ts +18 -0
- package/dist/common-auth-embedded/claustrum/index.js +22 -0
- package/dist/common-auth-embedded/claustrum/interlock.d.ts +27 -0
- package/dist/common-auth-embedded/claustrum/interlock.js +29 -0
- package/dist/common-auth-embedded/claustrum/roster.d.ts +208 -0
- package/dist/common-auth-embedded/claustrum/roster.js +536 -0
- package/dist/common-auth-embedded/commands/builtins.d.ts +71 -0
- package/dist/common-auth-embedded/commands/builtins.js +534 -0
- package/dist/common-auth-embedded/commands/index.d.ts +10 -0
- package/dist/common-auth-embedded/commands/index.js +5 -0
- package/dist/common-auth-embedded/commands/menu.d.ts +61 -0
- package/dist/common-auth-embedded/commands/menu.js +300 -0
- package/dist/common-auth-embedded/commands/model.d.ts +238 -0
- package/dist/common-auth-embedded/commands/model.js +16 -0
- package/dist/common-auth-embedded/commands/pi.d.ts +23 -0
- package/dist/common-auth-embedded/commands/pi.js +206 -0
- package/dist/common-auth-embedded/commands/seam.d.ts +72 -0
- package/dist/common-auth-embedded/commands/seam.js +301 -0
- package/dist/common-auth-embedded/fs/atomic-write.d.ts +20 -0
- package/dist/common-auth-embedded/fs/atomic-write.js +80 -0
- package/dist/common-auth-embedded/fs/index.d.ts +7 -0
- package/dist/common-auth-embedded/fs/index.js +6 -0
- package/dist/common-auth-embedded/fs/lock-constants.d.ts +14 -0
- package/dist/common-auth-embedded/fs/lock-constants.js +14 -0
- package/dist/common-auth-embedded/fs/refresh-file-lock.d.ts +33 -0
- package/dist/common-auth-embedded/fs/refresh-file-lock.js +501 -0
- package/dist/common-auth-embedded/fs/with-lock.d.ts +33 -0
- package/dist/common-auth-embedded/fs/with-lock.js +48 -0
- package/dist/common-auth-embedded/logger/capture-sink.d.ts +14 -0
- package/dist/common-auth-embedded/logger/capture-sink.js +13 -0
- package/dist/common-auth-embedded/logger/engine.d.ts +100 -0
- package/dist/common-auth-embedded/logger/engine.js +246 -0
- package/dist/common-auth-embedded/logger/index.d.ts +6 -0
- package/dist/common-auth-embedded/logger/index.js +3 -0
- package/dist/common-auth-embedded/logger/redact.d.ts +11 -0
- package/dist/common-auth-embedded/logger/redact.js +83 -0
- package/dist/common-auth-embedded/pi-slot/index.d.ts +3 -0
- package/dist/common-auth-embedded/pi-slot/index.js +2 -0
- package/dist/common-auth-embedded/pi-slot/lock.js +212 -0
- package/dist/common-auth-embedded/pi-slot/slot.d.ts +49 -0
- package/dist/common-auth-embedded/pi-slot/slot.js +383 -0
- package/dist/common-auth-embedded/quota/codec.d.ts +11 -0
- package/dist/common-auth-embedded/quota/codec.js +12 -0
- package/dist/common-auth-embedded/quota/format.d.ts +46 -0
- package/dist/common-auth-embedded/quota/format.js +138 -0
- package/dist/common-auth-embedded/quota/index.d.ts +10 -0
- package/dist/common-auth-embedded/quota/index.js +5 -0
- package/dist/common-auth-embedded/quota/map.d.ts +63 -0
- package/dist/common-auth-embedded/quota/map.js +105 -0
- package/dist/common-auth-embedded/quota/merge.d.ts +48 -0
- package/dist/common-auth-embedded/quota/merge.js +162 -0
- package/dist/common-auth-embedded/quota/projection.d.ts +64 -0
- package/dist/common-auth-embedded/quota/projection.js +134 -0
- package/dist/common-auth-embedded/routing/admission.d.ts +93 -0
- package/dist/common-auth-embedded/routing/admission.js +142 -0
- package/dist/common-auth-embedded/routing/index.d.ts +8 -0
- package/dist/common-auth-embedded/routing/index.js +4 -0
- package/dist/common-auth-embedded/routing/ordered.d.ts +47 -0
- package/dist/common-auth-embedded/routing/ordered.js +58 -0
- package/dist/common-auth-embedded/routing/pins.d.ts +22 -0
- package/dist/common-auth-embedded/routing/pins.js +34 -0
- package/dist/common-auth-embedded/routing/sticky.d.ts +158 -0
- package/dist/common-auth-embedded/routing/sticky.js +339 -0
- package/dist/common-auth-embedded/source-output.json +1090 -0
- package/dist/common-auth-embedded/store/attribution.d.ts +28 -0
- package/dist/common-auth-embedded/store/attribution.js +60 -0
- package/dist/common-auth-embedded/store/errors.d.ts +55 -0
- package/dist/common-auth-embedded/store/errors.js +38 -0
- package/dist/common-auth-embedded/store/hooks.d.ts +12 -0
- package/dist/common-auth-embedded/store/hooks.js +34 -0
- package/dist/common-auth-embedded/store/identity.d.ts +39 -0
- package/dist/common-auth-embedded/store/identity.js +79 -0
- package/dist/common-auth-embedded/store/index.d.ts +21 -0
- package/dist/common-auth-embedded/store/index.js +7 -0
- package/dist/common-auth-embedded/store/mutate.d.ts +189 -0
- package/dist/common-auth-embedded/store/mutate.js +500 -0
- package/dist/common-auth-embedded/store/pool.d.ts +196 -0
- package/dist/common-auth-embedded/store/pool.js +120 -0
- package/dist/common-auth-embedded/store/provider-state.d.ts +131 -0
- package/dist/common-auth-embedded/store/provider-state.js +226 -0
- package/dist/common-auth-embedded/store/publication.d.ts +47 -0
- package/dist/common-auth-embedded/store/publication.js +304 -0
- package/dist/common-auth-embedded/store/pull.d.ts +36 -0
- package/dist/common-auth-embedded/store/pull.js +143 -0
- package/dist/common-auth-embedded/store/refresh-lock.d.ts +63 -0
- package/dist/common-auth-embedded/store/refresh-lock.js +136 -0
- package/dist/common-auth-embedded/store/refresh.d.ts +72 -0
- package/dist/common-auth-embedded/store/refresh.js +225 -0
- package/dist/common-auth-embedded/store/reserved.js +13 -0
- package/dist/common-auth-embedded/store/rows.d.ts +313 -0
- package/dist/common-auth-embedded/store/rows.js +1258 -0
- package/dist/common-auth-embedded/store/runtime.d.ts +36 -0
- package/dist/common-auth-embedded/store/runtime.js +68 -0
- package/dist/common-auth-embedded/store/schema.d.ts +443 -0
- package/dist/common-auth-embedded/store/schema.js +754 -0
- package/dist/common-auth-embedded/store/settings.d.ts +63 -0
- package/dist/common-auth-embedded/store/settings.js +120 -0
- package/dist/common-auth-embedded/store/torn.d.ts +78 -0
- package/dist/common-auth-embedded/store/torn.js +294 -0
- package/dist/common-auth-embedded/util/any-signal.js +42 -0
- package/dist/common-auth-runtime.d.ts +73 -0
- package/dist/common-auth-runtime.d.ts.map +1 -0
- package/dist/common-auth-runtime.js +65 -0
- package/dist/common-auth-runtime.js.map +1 -0
- package/dist/credential-redaction.d.ts +13 -0
- package/dist/credential-redaction.d.ts.map +1 -0
- package/dist/credential-redaction.js +230 -0
- package/dist/credential-redaction.js.map +1 -0
- package/dist/fetch-timeout.d.ts +4 -2
- package/dist/fetch-timeout.d.ts.map +1 -1
- package/dist/fetch-timeout.js +121 -14
- package/dist/fetch-timeout.js.map +1 -1
- package/dist/fingerprint.d.ts +3 -3
- package/dist/fingerprint.d.ts.map +1 -1
- package/dist/fingerprint.js +4 -4
- package/dist/fingerprint.js.map +1 -1
- package/dist/index.d.ts +16 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -1
- package/dist/model-registry.d.ts +16 -0
- package/dist/model-registry.d.ts.map +1 -1
- package/dist/model-registry.js +242 -30
- package/dist/model-registry.js.map +1 -1
- package/dist/quota-manager.d.ts.map +1 -1
- package/dist/quota-manager.js +35 -10
- package/dist/quota-manager.js.map +1 -1
- package/dist/quota-presentation.d.ts +10 -0
- package/dist/quota-presentation.d.ts.map +1 -0
- package/dist/quota-presentation.js +60 -0
- package/dist/quota-presentation.js.map +1 -0
- package/dist/quota-types.d.ts +2 -0
- package/dist/quota-types.d.ts.map +1 -1
- package/dist/rotation.d.ts +8 -0
- package/dist/rotation.d.ts.map +1 -1
- package/dist/rotation.js +24 -0
- package/dist/rotation.js.map +1 -1
- package/dist/transform/agy-generation.d.ts +3 -0
- package/dist/transform/agy-generation.d.ts.map +1 -0
- package/dist/transform/agy-generation.js +28 -0
- package/dist/transform/agy-generation.js.map +1 -0
- package/dist/transform/claude.d.ts +7 -3
- package/dist/transform/claude.d.ts.map +1 -1
- package/dist/transform/claude.js +8 -2
- package/dist/transform/claude.js.map +1 -1
- package/dist/transform/index.d.ts +1 -0
- package/dist/transform/index.d.ts.map +1 -1
- package/dist/transform/index.js +1 -0
- package/dist/transform/index.js.map +1 -1
- package/dist/transform/model-resolver.d.ts +2 -1
- package/dist/transform/model-resolver.d.ts.map +1 -1
- package/dist/transform/model-resolver.js +46 -5
- package/dist/transform/model-resolver.js.map +1 -1
- package/dist/transform/types.d.ts +1 -1
- package/dist/transform/types.d.ts.map +1 -1
- package/dist/vault-account-source.d.ts +183 -0
- package/dist/vault-account-source.d.ts.map +1 -0
- package/dist/vault-account-source.js +296 -0
- package/dist/vault-account-source.js.map +1 -0
- package/dist/vault-enrollment-authority.d.ts +16 -0
- package/dist/vault-enrollment-authority.d.ts.map +1 -0
- package/dist/vault-enrollment-authority.js +19 -0
- package/dist/vault-enrollment-authority.js.map +1 -0
- package/dist/vault-family.d.ts +11 -0
- package/dist/vault-family.d.ts.map +1 -0
- package/dist/vault-family.js +24 -0
- package/dist/vault-family.js.map +1 -0
- package/dist/vault-provider-state.d.ts +162 -0
- package/dist/vault-provider-state.d.ts.map +1 -0
- package/dist/vault-provider-state.js +422 -0
- package/dist/vault-provider-state.js.map +1 -0
- package/package.json +39 -2
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { PoolOperationError } from './errors.js';
|
|
2
|
+
import type { PoolLockSpec } from './refresh-lock.js';
|
|
3
|
+
import type { StoreRuntime } from './runtime.js';
|
|
4
|
+
/**
|
|
5
|
+
* Top-level config keys the pool owns: the legacy `version`, the legacy
|
|
6
|
+
* roster (`accounts`) and the pool's own key. A settings write never reads
|
|
7
|
+
* them into the settings it hands out and refuses a result that sets them,
|
|
8
|
+
* so the roster and the per-row entries change only through row operations.
|
|
9
|
+
*/
|
|
10
|
+
export declare const POOL_OWNED_KEYS: readonly string[];
|
|
11
|
+
/** The plugin's settings: every top-level config key except the pool-owned ones. */
|
|
12
|
+
export type PoolSettings = Record<string, unknown>;
|
|
13
|
+
export type SettingsRead = {
|
|
14
|
+
status: 'ready';
|
|
15
|
+
settings: PoolSettings;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The config still holds a legacy roster with no pool key. Its settings
|
|
19
|
+
* read the same way; `updateSettings` refuses until the pool is initialized.
|
|
20
|
+
*/
|
|
21
|
+
| {
|
|
22
|
+
status: 'pending-migration';
|
|
23
|
+
settings: PoolSettings;
|
|
24
|
+
} | {
|
|
25
|
+
status: 'error';
|
|
26
|
+
file: 'config' | 'state';
|
|
27
|
+
reason: string;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Receives a private copy of the current settings and either edits it in
|
|
31
|
+
* place (returning nothing) or returns the complete next settings object.
|
|
32
|
+
* It runs under the store locks, so it must not call back into the store:
|
|
33
|
+
* a store operation called from inside it is refused (`PoolReentryError`).
|
|
34
|
+
*/
|
|
35
|
+
export type SettingsMutator = (settings: PoolSettings) => PoolSettings | undefined | Promise<PoolSettings | undefined>;
|
|
36
|
+
/**
|
|
37
|
+
* Options of `updateSettings`. It names no row, so its failure hook is
|
|
38
|
+
* handed only the failure; like `reorder` it takes the extra locks, then the
|
|
39
|
+
* store locks, and no row or provider-wide lock.
|
|
40
|
+
*/
|
|
41
|
+
export interface UpdateSettingsOptions {
|
|
42
|
+
/** Called once, awaited, on every non-success path, before the extra locks release. */
|
|
43
|
+
onFailure?: (error: PoolOperationError) => void | Promise<void>;
|
|
44
|
+
/** Locks taken, in this order, before the store locks. */
|
|
45
|
+
extraLocks?: readonly PoolLockSpec[];
|
|
46
|
+
}
|
|
47
|
+
export type UpdateSettingsResult = {
|
|
48
|
+
/** The settings now on disk. */
|
|
49
|
+
settings: PoolSettings;
|
|
50
|
+
/** `unchanged` when the mutator left the settings as they were; nothing was written. */
|
|
51
|
+
outcome: 'updated' | 'unchanged';
|
|
52
|
+
};
|
|
53
|
+
/** Reads the settings without taking a lock or writing anything. */
|
|
54
|
+
export declare function readPoolSettings(rt: StoreRuntime): Promise<SettingsRead>;
|
|
55
|
+
/**
|
|
56
|
+
* One locked read-modify-write of the plugin's settings in the config file
|
|
57
|
+
* the pool lives in. The pool must be ready (a pending migration or a load
|
|
58
|
+
* error refuses before the mutator runs). A mutator result that sets a
|
|
59
|
+
* pool-owned key refuses (`invalid-input`) with nothing written; a result
|
|
60
|
+
* equal to the current settings writes nothing. The state file is never
|
|
61
|
+
* touched, and the pool-owned keys are written back exactly as read.
|
|
62
|
+
*/
|
|
63
|
+
export declare function updatePoolSettings(rt: StoreRuntime, mutator: SettingsMutator, options?: UpdateSettingsOptions): Promise<UpdateSettingsResult>;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { isDeepStrictEqual } from 'node:util';
|
|
2
|
+
import { writeJsonAtomic } from '../fs/atomic-write.js';
|
|
3
|
+
import { PoolOperationError } from './errors.js';
|
|
4
|
+
import { assertNotInsideHook, runInsideHook } from './hooks.js';
|
|
5
|
+
import { notReadyError, readPool, runOperation } from './mutate.js';
|
|
6
|
+
import { isRecord, POOL_KEY } from './schema.js';
|
|
7
|
+
/**
|
|
8
|
+
* Top-level config keys the pool owns: the legacy `version`, the legacy
|
|
9
|
+
* roster (`accounts`) and the pool's own key. A settings write never reads
|
|
10
|
+
* them into the settings it hands out and refuses a result that sets them,
|
|
11
|
+
* so the roster and the per-row entries change only through row operations.
|
|
12
|
+
*/
|
|
13
|
+
export const POOL_OWNED_KEYS = Object.freeze([
|
|
14
|
+
'version',
|
|
15
|
+
'accounts',
|
|
16
|
+
POOL_KEY,
|
|
17
|
+
]);
|
|
18
|
+
function settingsOf(config) {
|
|
19
|
+
const settings = {};
|
|
20
|
+
for (const [key, value] of Object.entries(config))
|
|
21
|
+
if (!POOL_OWNED_KEYS.includes(key))
|
|
22
|
+
defineKey(settings, key, value);
|
|
23
|
+
return settings;
|
|
24
|
+
}
|
|
25
|
+
/** Sets a key as an own data property, so a `__proto__` key stays a plain key. */
|
|
26
|
+
function defineKey(target, key, value) {
|
|
27
|
+
Object.defineProperty(target, key, {
|
|
28
|
+
value,
|
|
29
|
+
enumerable: true,
|
|
30
|
+
writable: true,
|
|
31
|
+
configurable: true,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The next config: the pool-owned keys exactly as read, every settings key
|
|
36
|
+
* from `next` in its existing position, settings keys `next` dropped left
|
|
37
|
+
* out, and new settings keys appended.
|
|
38
|
+
*/
|
|
39
|
+
function composeConfig(config, next) {
|
|
40
|
+
const out = {};
|
|
41
|
+
for (const [key, value] of Object.entries(config)) {
|
|
42
|
+
if (POOL_OWNED_KEYS.includes(key))
|
|
43
|
+
defineKey(out, key, value);
|
|
44
|
+
else if (Object.hasOwn(next, key))
|
|
45
|
+
defineKey(out, key, next[key]);
|
|
46
|
+
}
|
|
47
|
+
for (const [key, value] of Object.entries(next))
|
|
48
|
+
if (!Object.hasOwn(config, key))
|
|
49
|
+
defineKey(out, key, value);
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
function settingsRefusal(message) {
|
|
53
|
+
return new PoolOperationError({
|
|
54
|
+
operation: 'updateSettings',
|
|
55
|
+
phase: 'before-first-write',
|
|
56
|
+
retryable: false,
|
|
57
|
+
kind: 'invalid-input',
|
|
58
|
+
message,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
/** Reads the settings without taking a lock or writing anything. */
|
|
62
|
+
export async function readPoolSettings(rt) {
|
|
63
|
+
const result = await readPool(rt.ctx);
|
|
64
|
+
if (result.status === 'error')
|
|
65
|
+
return result;
|
|
66
|
+
return { status: result.status, settings: settingsOf(result.config) };
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* One locked read-modify-write of the plugin's settings in the config file
|
|
70
|
+
* the pool lives in. The pool must be ready (a pending migration or a load
|
|
71
|
+
* error refuses before the mutator runs). A mutator result that sets a
|
|
72
|
+
* pool-owned key refuses (`invalid-input`) with nothing written; a result
|
|
73
|
+
* equal to the current settings writes nothing. The state file is never
|
|
74
|
+
* touched, and the pool-owned keys are written back exactly as read.
|
|
75
|
+
*/
|
|
76
|
+
export async function updatePoolSettings(rt, mutator, options = {}) {
|
|
77
|
+
assertNotInsideHook('updateSettings');
|
|
78
|
+
const { ctx } = rt;
|
|
79
|
+
const onFailure = options.onFailure;
|
|
80
|
+
return runOperation(ctx, 'updateSettings', undefined, onFailure && ((_rowId, error) => onFailure(error)), async (locks, progress) => {
|
|
81
|
+
for (const extra of options.extraLocks ?? [])
|
|
82
|
+
await locks.acquire(extra);
|
|
83
|
+
// `mark` is where the store locks start on the lock stack. They are
|
|
84
|
+
// released at the end of this block, so `onFailure` runs holding only
|
|
85
|
+
// the caller's extra locks, as it does for every other store operation.
|
|
86
|
+
const mark = locks.held.length;
|
|
87
|
+
try {
|
|
88
|
+
for (const spec of ctx.storeLocks)
|
|
89
|
+
await locks.acquire(spec);
|
|
90
|
+
const result = await readPool(ctx);
|
|
91
|
+
if (result.status !== 'ready')
|
|
92
|
+
throw notReadyError(result, 'updateSettings', undefined);
|
|
93
|
+
const current = settingsOf(result.config);
|
|
94
|
+
const draft = structuredClone(current);
|
|
95
|
+
const returned = await runInsideHook('updateSettings', () => mutator(draft));
|
|
96
|
+
const next = returned === undefined ? draft : returned;
|
|
97
|
+
if (!isRecord(next))
|
|
98
|
+
throw settingsRefusal('the settings mutator must produce an object');
|
|
99
|
+
const owned = POOL_OWNED_KEYS.filter((key) => Object.hasOwn(next, key));
|
|
100
|
+
if (owned.length > 0)
|
|
101
|
+
throw settingsRefusal(`settings cannot set the pool-owned key(s) ${owned.join(', ')}`);
|
|
102
|
+
if (isDeepStrictEqual(next, current))
|
|
103
|
+
return { settings: current, outcome: 'unchanged' };
|
|
104
|
+
const config = composeConfig(result.config, next);
|
|
105
|
+
const info = { operation: 'updateSettings', rowId: undefined };
|
|
106
|
+
await writeJsonAtomic(ctx.configPath, config, {
|
|
107
|
+
beforeRename: async () => {
|
|
108
|
+
await ctx.onStep?.('before-config-write', info);
|
|
109
|
+
await locks.assertAll();
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
progress.writes++;
|
|
113
|
+
await ctx.onStep?.('after-config-write', info);
|
|
114
|
+
return { settings: settingsOf(config), outcome: 'updated' };
|
|
115
|
+
}
|
|
116
|
+
finally {
|
|
117
|
+
await locks.releaseTo(mark);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { type RowEditor } from './identity.js';
|
|
2
|
+
import { type CredentialBinding, type CredentialStamp, type PoolRow, type ProviderStateCodec, type QuotaCodec } from './schema.js';
|
|
3
|
+
/** Key, inside a credential stamp, of an attributed enable or disable. */
|
|
4
|
+
export declare const TRANSITION_STAMP_KEY = "transition";
|
|
5
|
+
/**
|
|
6
|
+
* Key, inside a per-row config entry, of the mark of the last transition the
|
|
7
|
+
* config carries out. Older readers ignore it.
|
|
8
|
+
*/
|
|
9
|
+
export declare const TRANSITION_MARK_KEY = "transitionMark";
|
|
10
|
+
/**
|
|
11
|
+
* An attributed enable or disable as its state write records it: `mark` is
|
|
12
|
+
* unique to that write, `enabled` is the flag it sets, and `reason` the
|
|
13
|
+
* disabled reason (a disable's only).
|
|
14
|
+
*/
|
|
15
|
+
export interface StampedTransition {
|
|
16
|
+
mark: string;
|
|
17
|
+
enabled: boolean;
|
|
18
|
+
reason?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Carries a transition out in a config being edited: the row's enabled flag
|
|
22
|
+
* and reason as the transition says, and its mark recorded in the entry (a
|
|
23
|
+
* row without an entry gets one at epoch 1, as `disableIn` gives it). An
|
|
24
|
+
* enable then applies the duplicate-identity rule, as every write that
|
|
25
|
+
* enables an OAuth row with a known identity does: the earlier row in roster
|
|
26
|
+
* order keeps the identity.
|
|
27
|
+
*/
|
|
28
|
+
export declare function applyTransition(editor: RowEditor, id: string, transition: StampedTransition): void;
|
|
29
|
+
/** How a row left between the two writes of an operation is completed. */
|
|
30
|
+
export type TornCompletion = {
|
|
31
|
+
kind: 'replace';
|
|
32
|
+
stamp: CredentialStamp & {
|
|
33
|
+
binding: CredentialBinding;
|
|
34
|
+
};
|
|
35
|
+
} | {
|
|
36
|
+
kind: 'identity';
|
|
37
|
+
identity: string;
|
|
38
|
+
} | {
|
|
39
|
+
kind: 'transition';
|
|
40
|
+
transition: StampedTransition;
|
|
41
|
+
};
|
|
42
|
+
/** Rows left between the two writes of an operation, by row id. */
|
|
43
|
+
export declare function tornStamps(config: Record<string, unknown>, state: Record<string, unknown>, codec: QuotaCodec, options?: {
|
|
44
|
+
requireCredentialStamps?: boolean;
|
|
45
|
+
}): Map<string, TornCompletion>;
|
|
46
|
+
/**
|
|
47
|
+
* The config half of a replacement: the row's entry moves to the new epoch,
|
|
48
|
+
* loses its quota and needs a first reading; its roster row gets the new
|
|
49
|
+
* identity (or loses the old one) and, for an API key, the new endpoint.
|
|
50
|
+
* Disabling later rows that share the new identity is left to the caller
|
|
51
|
+
* (`disableIdentityDuplicates`), so several rows can be bound first.
|
|
52
|
+
*/
|
|
53
|
+
export declare function bindReplacement(editor: RowEditor, id: string, credentialEpoch: number, binding: CredentialBinding): void;
|
|
54
|
+
/**
|
|
55
|
+
* The config with every torn row completed as its interrupted write would
|
|
56
|
+
* have left it, and the ids completed. The config passed in is not modified;
|
|
57
|
+
* when nothing is torn it is returned as is.
|
|
58
|
+
*/
|
|
59
|
+
export declare function completeTornRows(config: Record<string, unknown>, state: Record<string, unknown>, codec: QuotaCodec, options?: {
|
|
60
|
+
requireCredentialStamps?: boolean;
|
|
61
|
+
}): {
|
|
62
|
+
config: Record<string, unknown>;
|
|
63
|
+
torn: string[];
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* The rows every reader gets. A torn row is shown completed (the identity,
|
|
67
|
+
* endpoint and epoch its stamp names, beside the credential it stamps), is
|
|
68
|
+
* marked `torn` and is never a candidate; every other row is as on disk.
|
|
69
|
+
* With `requireCredentialStamps`, a row whose stamp is not `bound` is marked
|
|
70
|
+
* `unbound` and is never a candidate either. A torn row is shown with the
|
|
71
|
+
* stamp of the interrupted write, which binds the completed row, so it is not
|
|
72
|
+
* unbound; once a store write puts its completion on disk it is no longer
|
|
73
|
+
* torn and is a candidate again like any other row.
|
|
74
|
+
*/
|
|
75
|
+
export declare function loadRows(config: Record<string, unknown>, state: Record<string, unknown>, codec: QuotaCodec, options?: {
|
|
76
|
+
requireCredentialStamps?: boolean;
|
|
77
|
+
providerState?: ProviderStateCodec;
|
|
78
|
+
}): PoolRow[];
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { disableIdentityDuplicates, disableIn, enableIn, IDENTITY_CONTRADICTED_REASON_PREFIX, recordIdentityIn, } from './identity.js';
|
|
2
|
+
import { buildRawRows, CREDENTIAL_STAMP_KEY, credentialDigest, dispatchDigest, entryIn, isRecord, parseStamp, rosterRowIn, setEntryIn, } from './schema.js';
|
|
3
|
+
/*
|
|
4
|
+
* A replace changes both files: the state file gets the new credential and
|
|
5
|
+
* the config gets the new epoch, identity or endpoint. It writes the state
|
|
6
|
+
* file first, stamping the credential with the epoch it belongs to and the
|
|
7
|
+
* binding (identity, endpoint) the config is about to get. A crash between
|
|
8
|
+
* the two writes therefore leaves a credential whose stamp is ahead of the
|
|
9
|
+
* config's epoch, and that is how every reader tells such a torn row from a
|
|
10
|
+
* whole one. The stamp holds everything the config write would have written,
|
|
11
|
+
* so a torn row is completed forward: readers are shown the completed row
|
|
12
|
+
* (never a candidate until it is on disk) and the next store write on it
|
|
13
|
+
* writes the config to match.
|
|
14
|
+
*
|
|
15
|
+
* A stamp counts only beside the credential it was written with (its digest
|
|
16
|
+
* matches): a writer that does not know about stamps may put another
|
|
17
|
+
* credential beside an old one, and that stamp then says nothing. A stamp at
|
|
18
|
+
* or behind the config's epoch is never torn as a replace: this store's own
|
|
19
|
+
* writes leave the state file ahead of the config, never behind it.
|
|
20
|
+
*
|
|
21
|
+
* Only a replace's stamp is ever completed as a replace. Since 0.4.4 every
|
|
22
|
+
* stamp carries a binding, so a replace's says so itself (`replace: true`); a
|
|
23
|
+
* stamp written by 0.4.3 or earlier has no dispatch digest, and those
|
|
24
|
+
* versions wrote a binding only on replace, so such a stamp with a binding is
|
|
25
|
+
* a replace's. A stamp from any other write that sits ahead of the config was
|
|
26
|
+
* not left by a crash (those writes keep the row's epoch), and completing it
|
|
27
|
+
* would rewrite the row's identity and drop its quota on a foreign writer's
|
|
28
|
+
* say-so. With `requireCredentialStamps` a 0.4.3-shaped replace stamp is not
|
|
29
|
+
* completed either: it proves nothing about the token sent, so the strict
|
|
30
|
+
* store leaves the row as it is on disk (`legacy`, unbound) rather than act
|
|
31
|
+
* on it; a store without the option completes it as before.
|
|
32
|
+
*
|
|
33
|
+
* A strict store also completes a replace only when its stamp describes what
|
|
34
|
+
* the completed row would send: the stamp's dispatch digest must be that of
|
|
35
|
+
* the credential beside it, with an API key moved to the endpoint the binding
|
|
36
|
+
* names. The lineage digest alone covers the refresh token or API key, so
|
|
37
|
+
* without this check another writer that changed only the access token, the
|
|
38
|
+
* expiry or the endpoint would still have the strict store rewrite the row's
|
|
39
|
+
* epoch, identity and quota before refusing the row as unbound. A store
|
|
40
|
+
* without the option never compared dispatch digests here and still does not.
|
|
41
|
+
*
|
|
42
|
+
* A write that gives a row its first identity (`recordIdentity`, or a
|
|
43
|
+
* `rotate` or refresh that learns one) follows the same order: the stamp
|
|
44
|
+
* naming the identity first, then the config recording it. A crash between
|
|
45
|
+
* leaves a stamp at the row's epoch, matching the credential beside it
|
|
46
|
+
* exactly (digest and dispatch digest), that names an identity the config
|
|
47
|
+
* does not record; that is completed forward the same way, by recording the
|
|
48
|
+
* identity.
|
|
49
|
+
*
|
|
50
|
+
* An attributed `disable` or `enable` that also changes the provider state
|
|
51
|
+
* follows the same order. Its state write carries the new value and, in the
|
|
52
|
+
* stamp, the transition (`transition: {mark, enabled, reason?}`, see
|
|
53
|
+
* `StampedTransition`); its config write then flips the row and records the
|
|
54
|
+
* transition's mark in the row's entry (`transitionMark`). A stamp bound to
|
|
55
|
+
* the row (this credential, epoch and identity, the binding that also shows
|
|
56
|
+
* the provider state beside it) whose transition mark the entry does not
|
|
57
|
+
* record is such a write stopped between its two writes, and is completed
|
|
58
|
+
* forward the same way: readers are shown the row disabled or enabled as the
|
|
59
|
+
* transition says, beside the value written with it. Once the config records
|
|
60
|
+
* the mark the transition says nothing more, so a later `enable` or
|
|
61
|
+
* `disable` of the row is never undone by it; a credential write drops it
|
|
62
|
+
* with the rest of the old stamp.
|
|
63
|
+
*/
|
|
64
|
+
/** Key, inside a credential stamp, of an attributed enable or disable. */
|
|
65
|
+
export const TRANSITION_STAMP_KEY = 'transition';
|
|
66
|
+
/**
|
|
67
|
+
* Key, inside a per-row config entry, of the mark of the last transition the
|
|
68
|
+
* config carries out. Older readers ignore it.
|
|
69
|
+
*/
|
|
70
|
+
export const TRANSITION_MARK_KEY = 'transitionMark';
|
|
71
|
+
/** The well-formed transition in a raw stamp, or undefined. */
|
|
72
|
+
function stampedTransition(rawStamp) {
|
|
73
|
+
if (!isRecord(rawStamp))
|
|
74
|
+
return undefined;
|
|
75
|
+
const raw = rawStamp[TRANSITION_STAMP_KEY];
|
|
76
|
+
if (!isRecord(raw))
|
|
77
|
+
return undefined;
|
|
78
|
+
if (typeof raw.mark !== 'string' || raw.mark.length === 0)
|
|
79
|
+
return undefined;
|
|
80
|
+
if (raw.enabled === true)
|
|
81
|
+
return { mark: raw.mark, enabled: true };
|
|
82
|
+
if (raw.enabled === false && typeof raw.reason === 'string')
|
|
83
|
+
return { mark: raw.mark, enabled: false, reason: raw.reason };
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Carries a transition out in a config being edited: the row's enabled flag
|
|
88
|
+
* and reason as the transition says, and its mark recorded in the entry (a
|
|
89
|
+
* row without an entry gets one at epoch 1, as `disableIn` gives it). An
|
|
90
|
+
* enable then applies the duplicate-identity rule, as every write that
|
|
91
|
+
* enables an OAuth row with a known identity does: the earlier row in roster
|
|
92
|
+
* order keeps the identity.
|
|
93
|
+
*/
|
|
94
|
+
export function applyTransition(editor, id, transition) {
|
|
95
|
+
if (transition.enabled)
|
|
96
|
+
enableIn(editor, id);
|
|
97
|
+
else
|
|
98
|
+
disableIn(editor, id, transition.reason ?? '');
|
|
99
|
+
if (!editor.rosterRow(id))
|
|
100
|
+
return;
|
|
101
|
+
const entry = editor.entry(id) ?? {
|
|
102
|
+
credentialEpoch: 1,
|
|
103
|
+
needsFirstReading: true,
|
|
104
|
+
};
|
|
105
|
+
editor.setEntry(id, { ...entry, [TRANSITION_MARK_KEY]: transition.mark });
|
|
106
|
+
if (!transition.enabled)
|
|
107
|
+
return;
|
|
108
|
+
const row = editor.rows().find((candidate) => candidate.id === id);
|
|
109
|
+
if (row?.type === 'oauth' && row.identity !== undefined)
|
|
110
|
+
disableIdentityDuplicates(editor, row.identity);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* The credential a torn replace leaves once its config write lands: the
|
|
114
|
+
* credential loaded beside the stamp, with the endpoint the stamp's binding
|
|
115
|
+
* names. Only an API key's endpoint lives in the config (an OAuth credential
|
|
116
|
+
* is entirely in the state file, which the replace has already written). A
|
|
117
|
+
* replace that moves a key to a new endpoint leaves the old `baseURL` and
|
|
118
|
+
* `authHeader` in the config until its config write, so the binding's
|
|
119
|
+
* `baseURL` and `authHeader` are the ones the replacement will be sent with.
|
|
120
|
+
*/
|
|
121
|
+
function projectedReplacement(credential, binding) {
|
|
122
|
+
if (credential.type !== 'api')
|
|
123
|
+
return credential;
|
|
124
|
+
return {
|
|
125
|
+
...credential,
|
|
126
|
+
...(binding.baseURL !== undefined ? { baseURL: binding.baseURL } : {}),
|
|
127
|
+
...(binding.authHeader !== undefined
|
|
128
|
+
? { authHeader: binding.authHeader }
|
|
129
|
+
: {}),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/** Whether a well-formed stamp was written by a replace. */
|
|
133
|
+
function isReplaceStamp(stamp) {
|
|
134
|
+
if (!stamp.binding)
|
|
135
|
+
return false;
|
|
136
|
+
return stamp.replace === true || stamp.dispatch === undefined;
|
|
137
|
+
}
|
|
138
|
+
/** Rows left between the two writes of an operation, by row id. */
|
|
139
|
+
export function tornStamps(config, state, codec, options = {}) {
|
|
140
|
+
const accounts = isRecord(state.accounts) ? state.accounts : {};
|
|
141
|
+
const torn = new Map();
|
|
142
|
+
for (const row of buildRawRows(config, state, codec)) {
|
|
143
|
+
if (row.invalid || row.staged || !row.credential)
|
|
144
|
+
continue;
|
|
145
|
+
const account = Object.hasOwn(accounts, row.id)
|
|
146
|
+
? accounts[row.id]
|
|
147
|
+
: undefined;
|
|
148
|
+
if (!isRecord(account))
|
|
149
|
+
continue;
|
|
150
|
+
const stamp = parseStamp(account[CREDENTIAL_STAMP_KEY]);
|
|
151
|
+
if (!stamp?.binding)
|
|
152
|
+
continue;
|
|
153
|
+
if (stamp.digest !== credentialDigest(row.credential))
|
|
154
|
+
continue;
|
|
155
|
+
// A row without an entry is at epoch 1, as everywhere else.
|
|
156
|
+
const epoch = row.credentialEpoch ?? 1;
|
|
157
|
+
if (stamp.credentialEpoch > epoch) {
|
|
158
|
+
if (!isReplaceStamp(stamp))
|
|
159
|
+
continue;
|
|
160
|
+
if (options.requireCredentialStamps) {
|
|
161
|
+
if (stamp.dispatch === undefined)
|
|
162
|
+
continue;
|
|
163
|
+
// `digest` covers only the refresh token or API key, so another
|
|
164
|
+
// writer may still have changed the access token, the expiry or the
|
|
165
|
+
// endpoint the stamp names. `dispatch` covers all of what a request
|
|
166
|
+
// sends; completing the replace when it disagrees would move the
|
|
167
|
+
// row's epoch, identity and quota on a stamp that does not describe
|
|
168
|
+
// the credential. So the replacement as it would be sent (the loaded
|
|
169
|
+
// credential with the binding's endpoint) must have the stamp's
|
|
170
|
+
// dispatch digest; otherwise the row stays as on disk (unbound).
|
|
171
|
+
if (stamp.dispatch !==
|
|
172
|
+
dispatchDigest(projectedReplacement(row.credential, stamp.binding)))
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
torn.set(row.id, {
|
|
176
|
+
kind: 'replace',
|
|
177
|
+
stamp: { ...stamp, binding: stamp.binding },
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
else if (stamp.credentialEpoch === epoch &&
|
|
181
|
+
stamp.dispatch !== undefined &&
|
|
182
|
+
stamp.dispatch === dispatchDigest(row.credential) &&
|
|
183
|
+
stamp.binding.identity !== undefined &&
|
|
184
|
+
row.identity === undefined) {
|
|
185
|
+
torn.set(row.id, { kind: 'identity', identity: stamp.binding.identity });
|
|
186
|
+
}
|
|
187
|
+
else if (stamp.credentialEpoch === epoch &&
|
|
188
|
+
stamp.binding.identity === row.identity) {
|
|
189
|
+
// The lineage digest alone misses an access-token or expiry edit. A
|
|
190
|
+
// strict store must not repair the config from a transition whose stamp
|
|
191
|
+
// no longer describes the credential; the row stays unbound instead.
|
|
192
|
+
if (options.requireCredentialStamps &&
|
|
193
|
+
stamp.dispatch !== dispatchDigest(row.credential))
|
|
194
|
+
continue;
|
|
195
|
+
const transition = stampedTransition(account[CREDENTIAL_STAMP_KEY]);
|
|
196
|
+
if (transition &&
|
|
197
|
+
entryIn(config, row.id)?.[TRANSITION_MARK_KEY] !== transition.mark)
|
|
198
|
+
torn.set(row.id, { kind: 'transition', transition });
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return torn;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* The config half of a replacement: the row's entry moves to the new epoch,
|
|
205
|
+
* loses its quota and needs a first reading; its roster row gets the new
|
|
206
|
+
* identity (or loses the old one) and, for an API key, the new endpoint.
|
|
207
|
+
* Disabling later rows that share the new identity is left to the caller
|
|
208
|
+
* (`disableIdentityDuplicates`), so several rows can be bound first.
|
|
209
|
+
*/
|
|
210
|
+
export function bindReplacement(editor, id, credentialEpoch, binding) {
|
|
211
|
+
const entry = editor.entry(id) ?? {};
|
|
212
|
+
const next = {
|
|
213
|
+
...entry,
|
|
214
|
+
credentialEpoch,
|
|
215
|
+
needsFirstReading: true,
|
|
216
|
+
};
|
|
217
|
+
delete next.quota;
|
|
218
|
+
// An explicit identity supplied with a replacement is the adapter's validated
|
|
219
|
+
// account binding. Clear its quarantine on completion, including crash recovery,
|
|
220
|
+
// but leave the row disabled until a separate enable.
|
|
221
|
+
if (binding.identity !== undefined &&
|
|
222
|
+
typeof next.disabledReason === 'string' &&
|
|
223
|
+
next.disabledReason.startsWith(IDENTITY_CONTRADICTED_REASON_PREFIX))
|
|
224
|
+
delete next.disabledReason;
|
|
225
|
+
editor.setEntry(id, next);
|
|
226
|
+
const raw = editor.rosterRow(id);
|
|
227
|
+
if (!raw)
|
|
228
|
+
return;
|
|
229
|
+
if (binding.identity !== undefined)
|
|
230
|
+
raw.accountId = binding.identity;
|
|
231
|
+
else
|
|
232
|
+
delete raw.accountId;
|
|
233
|
+
if (binding.baseURL !== undefined)
|
|
234
|
+
raw.baseURL = binding.baseURL;
|
|
235
|
+
if (binding.authHeader !== undefined)
|
|
236
|
+
raw.authHeader = binding.authHeader;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* The config with every torn row completed as its interrupted write would
|
|
240
|
+
* have left it, and the ids completed. The config passed in is not modified;
|
|
241
|
+
* when nothing is torn it is returned as is.
|
|
242
|
+
*/
|
|
243
|
+
export function completeTornRows(config, state, codec, options = {}) {
|
|
244
|
+
const completions = tornStamps(config, state, codec, options);
|
|
245
|
+
if (completions.size === 0)
|
|
246
|
+
return { config, torn: [] };
|
|
247
|
+
const next = structuredClone(config);
|
|
248
|
+
const editor = {
|
|
249
|
+
rows: () => buildRawRows(next, state, codec),
|
|
250
|
+
rosterRow: (id) => rosterRowIn(next, id),
|
|
251
|
+
entry: (id) => entryIn(next, id),
|
|
252
|
+
setEntry: (id, entry) => setEntryIn(next, id, entry),
|
|
253
|
+
};
|
|
254
|
+
for (const [id, completion] of completions)
|
|
255
|
+
if (completion.kind === 'replace')
|
|
256
|
+
bindReplacement(editor, id, completion.stamp.credentialEpoch, completion.stamp.binding);
|
|
257
|
+
for (const [id, completion] of completions) {
|
|
258
|
+
if (completion.kind === 'identity')
|
|
259
|
+
recordIdentityIn(editor, id, completion.identity);
|
|
260
|
+
else if (completion.kind === 'transition')
|
|
261
|
+
applyTransition(editor, id, completion.transition);
|
|
262
|
+
else if (completion.stamp.binding.identity !== undefined)
|
|
263
|
+
disableIdentityDuplicates(editor, completion.stamp.binding.identity);
|
|
264
|
+
}
|
|
265
|
+
return { config: next, torn: [...completions.keys()] };
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* The rows every reader gets. A torn row is shown completed (the identity,
|
|
269
|
+
* endpoint and epoch its stamp names, beside the credential it stamps), is
|
|
270
|
+
* marked `torn` and is never a candidate; every other row is as on disk.
|
|
271
|
+
* With `requireCredentialStamps`, a row whose stamp is not `bound` is marked
|
|
272
|
+
* `unbound` and is never a candidate either. A torn row is shown with the
|
|
273
|
+
* stamp of the interrupted write, which binds the completed row, so it is not
|
|
274
|
+
* unbound; once a store write puts its completion on disk it is no longer
|
|
275
|
+
* torn and is a candidate again like any other row.
|
|
276
|
+
*/
|
|
277
|
+
export function loadRows(config, state, codec, options = {}) {
|
|
278
|
+
const { config: whole, torn } = completeTornRows(config, state, codec, options);
|
|
279
|
+
// A torn replace wrote its provider state beside the new credential, under
|
|
280
|
+
// the stamp naming the new epoch, so the completed row shows the new
|
|
281
|
+
// credential with the state written for it, never with the old one.
|
|
282
|
+
const rows = buildRawRows(whole, state, codec, options.providerState);
|
|
283
|
+
for (const row of rows) {
|
|
284
|
+
if (torn.includes(row.id)) {
|
|
285
|
+
row.torn = true;
|
|
286
|
+
row.candidate = false;
|
|
287
|
+
}
|
|
288
|
+
if (options.requireCredentialStamps && row.stamp !== 'bound') {
|
|
289
|
+
row.unbound = true;
|
|
290
|
+
row.candidate = false;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return rows;
|
|
294
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Combine abort signals without relying on `AbortSignal.any`, which is missing
|
|
3
|
+
* from early Node 20 releases. Call `dispose` when the operation using the
|
|
4
|
+
* combined signal settles so long-lived source signals do not retain listeners.
|
|
5
|
+
*/
|
|
6
|
+
export function combineAbortSignals(signals) {
|
|
7
|
+
const controller = new AbortController();
|
|
8
|
+
const listeners = [];
|
|
9
|
+
let disposed = false;
|
|
10
|
+
const dispose = () => {
|
|
11
|
+
if (disposed)
|
|
12
|
+
return;
|
|
13
|
+
disposed = true;
|
|
14
|
+
for (const { source, listener } of listeners) {
|
|
15
|
+
source.removeEventListener('abort', listener);
|
|
16
|
+
}
|
|
17
|
+
listeners.length = 0;
|
|
18
|
+
};
|
|
19
|
+
const abort = (reason) => {
|
|
20
|
+
if (disposed || controller.signal.aborted)
|
|
21
|
+
return;
|
|
22
|
+
controller.abort(reason);
|
|
23
|
+
dispose();
|
|
24
|
+
};
|
|
25
|
+
const alreadyAborted = signals.find((signal) => signal.aborted);
|
|
26
|
+
if (alreadyAborted) {
|
|
27
|
+
controller.abort(alreadyAborted.reason);
|
|
28
|
+
return { signal: controller.signal, dispose };
|
|
29
|
+
}
|
|
30
|
+
for (const source of signals) {
|
|
31
|
+
if (controller.signal.aborted)
|
|
32
|
+
break;
|
|
33
|
+
const listener = () => abort(source.reason);
|
|
34
|
+
listeners.push({ source, listener });
|
|
35
|
+
source.addEventListener('abort', listener, { once: true });
|
|
36
|
+
// Cover a source that aborts after the initial check but before its listener
|
|
37
|
+
// is installed.
|
|
38
|
+
if (source.aborted)
|
|
39
|
+
abort(source.reason);
|
|
40
|
+
}
|
|
41
|
+
return { signal: controller.signal, dispose };
|
|
42
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed, lazily loaded bindings to the public entries of @cortexkit/common-auth
|
|
3
|
+
* that this package ships inside itself.
|
|
4
|
+
*
|
|
5
|
+
* The library's public `./store`, `./fs`, `./routing`, `./quota`,
|
|
6
|
+
* `./commands`, `./auth-menu`, `./claustrum` and `./logger` entries are copied
|
|
7
|
+
* byte for byte from the verified library archive into `common-auth-embedded/`
|
|
8
|
+
* by the repository's single common-auth emitter; `source-output.json` there
|
|
9
|
+
* records the archive and every file hash. Nothing here re-implements or wraps
|
|
10
|
+
* library behaviour: each type is the genuine module namespace type, and each
|
|
11
|
+
* loader returns the genuine module.
|
|
12
|
+
*
|
|
13
|
+
* The loaders use dynamic imports so that importing the package root does not
|
|
14
|
+
* evaluate the store, the command menu or the vault client. A loaded module is
|
|
15
|
+
* cached for the life of the process; a failed load is not cached, so a later
|
|
16
|
+
* call can retry it. `./claustrum` additionally needs the
|
|
17
|
+
* `@cortexkit/claustrum-client` dependency, which is only resolved when
|
|
18
|
+
* `loadCommonAuthClaustrum` is called.
|
|
19
|
+
*
|
|
20
|
+
* Static consumers can import the same modules through this package's
|
|
21
|
+
* `./common-auth/<entry>` subpath exports instead.
|
|
22
|
+
*/
|
|
23
|
+
/** The library's public `./store` entry: the lease-backed pool store. */
|
|
24
|
+
export type CommonAuthStoreModule = typeof import('./common-auth-embedded/store/index.js');
|
|
25
|
+
/** The library's public `./fs` entry: file leases and atomic writes. */
|
|
26
|
+
export type CommonAuthFsModule = typeof import('./common-auth-embedded/fs/index.js');
|
|
27
|
+
/** The library's public `./routing` entry: admission and route selection. */
|
|
28
|
+
export type CommonAuthRoutingModule = typeof import('./common-auth-embedded/routing/index.js');
|
|
29
|
+
/** The library's public `./quota` entry: quota maps, merge and projection. */
|
|
30
|
+
export type CommonAuthQuotaModule = typeof import('./common-auth-embedded/quota/index.js');
|
|
31
|
+
/** The library's public `./commands` entry: the shared command menu. */
|
|
32
|
+
export type CommonAuthCommandsModule = typeof import('./common-auth-embedded/commands/index.js');
|
|
33
|
+
/** The library's public `./auth-menu` entry: the terminal account menu. */
|
|
34
|
+
export type CommonAuthAuthMenuModule = typeof import('./common-auth-embedded/auth-menu/index.js');
|
|
35
|
+
/** The library's public `./claustrum` entry: vault credential custody. */
|
|
36
|
+
export type CommonAuthClaustrumModule = typeof import('./common-auth-embedded/claustrum/index.js');
|
|
37
|
+
/** The library's public `./logger` entry: the redacting logger engine. */
|
|
38
|
+
export type CommonAuthLoggerModule = typeof import('./common-auth-embedded/logger/index.js');
|
|
39
|
+
/**
|
|
40
|
+
* The `./store` and `./fs` entries of one library copy, as an account
|
|
41
|
+
* repository is built on them. Both come from the same embedded copy, so the
|
|
42
|
+
* store's own locks and the repository's file leases share one implementation.
|
|
43
|
+
*/
|
|
44
|
+
export interface CommonAuthStoreModules {
|
|
45
|
+
readonly store: CommonAuthStoreModule;
|
|
46
|
+
readonly fs: CommonAuthFsModule;
|
|
47
|
+
}
|
|
48
|
+
/** Loads the library's public `./store` entry. */
|
|
49
|
+
export declare const loadCommonAuthStore: () => Promise<CommonAuthStoreModule>;
|
|
50
|
+
/** Loads the library's public `./fs` entry. */
|
|
51
|
+
export declare const loadCommonAuthFs: () => Promise<CommonAuthFsModule>;
|
|
52
|
+
/** Loads the library's public `./routing` entry. */
|
|
53
|
+
export declare const loadCommonAuthRouting: () => Promise<CommonAuthRoutingModule>;
|
|
54
|
+
/** Loads the library's public `./quota` entry. */
|
|
55
|
+
export declare const loadCommonAuthQuota: () => Promise<CommonAuthQuotaModule>;
|
|
56
|
+
/** Loads the library's public `./commands` entry. */
|
|
57
|
+
export declare const loadCommonAuthCommands: () => Promise<CommonAuthCommandsModule>;
|
|
58
|
+
/** Loads the library's public `./auth-menu` entry. */
|
|
59
|
+
export declare const loadCommonAuthAuthMenu: () => Promise<CommonAuthAuthMenuModule>;
|
|
60
|
+
/**
|
|
61
|
+
* Loads the library's public `./claustrum` entry. This also loads the
|
|
62
|
+
* installed `@cortexkit/claustrum-client`; a missing client rejects here, not
|
|
63
|
+
* when the package root is imported.
|
|
64
|
+
*/
|
|
65
|
+
export declare const loadCommonAuthClaustrum: () => Promise<CommonAuthClaustrumModule>;
|
|
66
|
+
/** Loads the library's public `./logger` entry. */
|
|
67
|
+
export declare const loadCommonAuthLogger: () => Promise<CommonAuthLoggerModule>;
|
|
68
|
+
/** Loads the `./store` and `./fs` entries an account repository needs. */
|
|
69
|
+
export declare function loadCommonAuthStoreModules(): Promise<CommonAuthStoreModules>;
|
|
70
|
+
export type CommonAuthPiSlotModule = typeof import('./common-auth-embedded/pi-slot/index.js');
|
|
71
|
+
/** Load common-auth's Pi auth.json editor outside UI bundles; its lock coordinates with Pi's own login writers. */
|
|
72
|
+
export declare const loadCommonAuthPiSlot: () => Promise<CommonAuthPiSlotModule>;
|
|
73
|
+
//# sourceMappingURL=common-auth-runtime.d.ts.map
|