Add provider-instance identity and auth.json storage for multiple credentials per provider. - Export provider-instance key parsing, validation, and reserved metadata helpers. - Resolve, list, mutate, and select named credential instances atomically in auth storage. - Preserve legacy bare-key hydration and document the auth.json instance contract. - Cover provider instance parsing, storage behavior, and concurrent writes. Files changed: .changeset/fn-8651-provider-instances.md | 7 + docs/secrets.md | 8 + .../src/__tests__/oauth-credential-interop.test.ts | 16 ++ packages/core/src/index.ts | 16 ++ packages/core/src/oauth-credential-interop.ts | 15 +- packages/core/src/provider-instance.test.ts | 24 +++ packages/core/src/provider-instance.ts | 65 +++++++ .../src/__tests__/auth-storage-concurrency.test.ts | 13 ++ .../src/__tests__/auth-storage-instances.test.ts | 75 ++++++++ packages/engine/src/auth-storage.ts | 214 +++++++++++++-------- 10 files changed, 367 insertions(+), 86 deletions(-) Fusion-Task-Id: FN-8651 Fusion-Task-Lineage: cfabe496-60f9-4166-a09f-87ea2028cfd9 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
66 lines
2.9 KiB
TypeScript
66 lines
2.9 KiB
TypeScript
export const DEFAULT_PROVIDER_INSTANCE_ID = "default";
|
|
export const PROVIDER_INSTANCE_ID_MAX_LENGTH = 64;
|
|
export const RESERVED_AUTH_STORAGE_KEYS = ["__fusionDefaultInstances"] as const;
|
|
export const ANTHROPIC_SUBSCRIPTION_PROVIDER_ID = "anthropic-subscription";
|
|
|
|
export type ProviderInstanceRef = { providerId: string; instanceId: string };
|
|
|
|
export function isReservedAuthStorageKey(key: string): boolean {
|
|
return (RESERVED_AUTH_STORAGE_KEYS as readonly string[]).includes(key);
|
|
}
|
|
|
|
export function isDefaultProviderInstance(instanceId: string): boolean {
|
|
return instanceId === DEFAULT_PROVIDER_INSTANCE_ID;
|
|
}
|
|
|
|
export function isValidProviderInstanceId(instanceId: unknown): instanceId is string {
|
|
return typeof instanceId === "string"
|
|
&& instanceId.length > 0
|
|
&& instanceId.length <= PROVIDER_INSTANCE_ID_MAX_LENGTH
|
|
&& !/\s/.test(instanceId)
|
|
&& !instanceId.includes("[")
|
|
&& !instanceId.includes("]");
|
|
}
|
|
|
|
export function assertValidProviderInstanceId(instanceId: unknown): asserts instanceId is string {
|
|
if (!isValidProviderInstanceId(instanceId)) {
|
|
throw new Error(`Invalid provider instance id: ${String(instanceId)}`);
|
|
}
|
|
}
|
|
|
|
export function isValidProviderId(providerId: unknown): providerId is string {
|
|
return isValidProviderInstanceId(providerId) && !isReservedAuthStorageKey(providerId);
|
|
}
|
|
|
|
export function assertValidProviderId(providerId: unknown): asserts providerId is string {
|
|
if (!isValidProviderId(providerId)) {
|
|
throw new Error(`Invalid or reserved provider id: ${String(providerId)}`);
|
|
}
|
|
}
|
|
|
|
/*
|
|
FNXC:ProviderAuth 2026-08-01-04:36:
|
|
FN-8651 keeps auth.json compatible by spelling the default instance as a bare provider id and named instances as provider[instance]. Validation makes format and parse exact inverses so legacy APIs cannot write ambiguous raw keys. The reserved defaults record is never a provider because deleting or overwriting metadata would strand credentials.
|
|
*/
|
|
export function formatProviderInstanceKey(ref: ProviderInstanceRef): string {
|
|
assertValidProviderId(ref.providerId);
|
|
assertValidProviderInstanceId(ref.instanceId);
|
|
return isDefaultProviderInstance(ref.instanceId) ? ref.providerId : `${ref.providerId}[${ref.instanceId}]`;
|
|
}
|
|
|
|
export function parseProviderInstanceKey(key: string): ProviderInstanceRef | undefined {
|
|
if (typeof key !== "string" || key.length === 0) return undefined;
|
|
const open = key.indexOf("[");
|
|
const close = key.indexOf("]");
|
|
const isBare = open === -1 && close === -1;
|
|
const isNamed = open > 0 && close === key.length - 1 && key.indexOf("[", open + 1) === -1 && key.indexOf("]", close + 1) === -1;
|
|
if (!isBare && !isNamed) return undefined;
|
|
const providerId = isBare ? key : key.slice(0, open);
|
|
const instanceId = isBare ? DEFAULT_PROVIDER_INSTANCE_ID : key.slice(open + 1, -1);
|
|
return isValidProviderId(providerId)
|
|
&& isValidProviderInstanceId(instanceId)
|
|
&& (!isNamed || !isDefaultProviderInstance(instanceId))
|
|
? { providerId, instanceId }
|
|
: undefined;
|
|
}
|