@ferricstore/ferricstore 0.11.11 → 0.12.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 +16 -1
- package/dist/durability-DlDCsdlo.d.cts +16 -0
- package/dist/durability-DplL0SbW.d.ts +16 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -162
- package/dist/index.d.ts +5 -162
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/internal-lEEDZpPH.d.cts +4 -0
- package/dist/internal-lEEDZpPH.d.ts +4 -0
- package/dist/langgraph.cjs +1278 -0
- package/dist/langgraph.cjs.map +1 -0
- package/dist/langgraph.d.cts +148 -0
- package/dist/langgraph.d.ts +148 -0
- package/dist/langgraph.js +1252 -0
- package/dist/langgraph.js.map +1 -0
- package/dist/openai-agents.cjs +580 -0
- package/dist/openai-agents.cjs.map +1 -0
- package/dist/openai-agents.d.cts +44 -0
- package/dist/openai-agents.d.ts +44 -0
- package/dist/openai-agents.js +555 -0
- package/dist/openai-agents.js.map +1 -0
- package/dist/outcomes-BbFDp3AH.d.ts +160 -0
- package/dist/outcomes-DmBwnq0Y.d.cts +160 -0
- package/docs/agent-frameworks.md +159 -0
- package/docs/api/assets/highlight.css +12 -12
- package/docs/api/classes/ClaimHydrationError.html +2 -2
- package/docs/api/classes/ConnectionClosedError.html +2 -2
- package/docs/api/classes/FerricStoreError.html +2 -2
- package/docs/api/classes/FlowAlreadyExistsError.html +2 -2
- package/docs/api/classes/FlowBatchError.html +2 -2
- package/docs/api/classes/FlowNotFoundError.html +2 -2
- package/docs/api/classes/FlowQueryError.html +2 -2
- package/docs/api/classes/FlowWrongStateError.html +2 -2
- package/docs/api/classes/HTTPTransportError.html +2 -2
- package/docs/api/classes/InvalidCommandError.html +2 -2
- package/docs/api/classes/LeaseRenewalError.html +2 -2
- package/docs/api/classes/LockHeldError.html +2 -2
- package/docs/api/classes/LockNotOwnedError.html +2 -2
- package/docs/api/classes/OverloadedError.html +2 -2
- package/docs/api/classes/QueueCompletionError.html +2 -2
- package/docs/api/classes/RequestTimeoutError.html +2 -2
- package/docs/api/classes/RerouteError.html +2 -2
- package/docs/api/classes/StaleLeaseError.html +2 -2
- package/docs/api/classes/StalePolicyGenerationError.html +2 -2
- package/docs/api/index.html +34 -24
- package/docs/api/media/agent-frameworks.md +159 -0
- package/docs/api/media/langgraph.ts +23 -0
- package/docs/api/media/openai-agents-session.ts +13 -0
- package/docs/api/variables/FERRICSTORE_SDK_VERSION.html +1 -1
- package/package.json +41 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/openai-agents.ts","../src/agent-persistence/durability.ts","../src/errors.ts","../src/agent-persistence/snapshot.ts"],"sourcesContent":["import { createHash, randomUUID } from \"node:crypto\";\n\nimport type {\n AgentInputItem,\n Session,\n SessionHistoryRewriteArgs,\n SessionHistoryRewriteAwareSession,\n SessionHistoryTransaction,\n SessionHistoryTransactionArgs,\n SessionHistoryTransactionAwareSession\n} from \"@openai/agents\";\n\nimport {\n normalizeKeyPrefix,\n type FerricStoreCommandClient,\n type FerricStoreLockOptions,\n withMutationLocks\n} from \"./agent-persistence/durability.js\";\nimport {\n cloneSnapshot,\n decodeSnapshot,\n encodeSnapshot,\n snapshotDigest,\n snapshotsEqual\n} from \"./agent-persistence/snapshot.js\";\n\nconst SESSION_FORMAT_VERSION = 1;\nconst SESSION_STATE_FIELD = \"state\";\n\ninterface StoredSessionState {\n readonly formatVersion: typeof SESSION_FORMAT_VERSION;\n readonly items: AgentInputItem[];\n readonly operations: Record<string, string>;\n readonly sessionId: string;\n}\n\nexport interface FerricStoreSessionOptions extends FerricStoreLockOptions {\n /** Existing conversation identifier. A random UUID is created when omitted. */\n sessionId?: string;\n /** Items used only when this session has not yet been persisted. */\n initialItems?: AgentInputItem[];\n /** FerricStore key prefix. Defaults to `openai:agents:session`. */\n keyPrefix?: string;\n}\n\n/**\n * Durable OpenAI Agents SDK conversation history backed by FerricStore.\n *\n * Every mutation is serialized by an ownership-checked, renewable FerricStore\n * lock. History transactions and their operation receipts are persisted in one\n * atomic hash-field write, implementing the SDK's retry-safe transaction\n * capability in addition to its base Session contract.\n */\nexport class FerricStoreSession implements\n Session,\n SessionHistoryRewriteAwareSession,\n SessionHistoryTransactionAwareSession {\n readonly client: FerricStoreCommandClient;\n readonly sessionId: string;\n readonly keyPrefix: string;\n private readonly initialItems: AgentInputItem[];\n private readonly lockOptions: FerricStoreLockOptions;\n private readonly sessionKey: string;\n private readonly lockKey: string;\n\n constructor(client: FerricStoreCommandClient, options: FerricStoreSessionOptions = {}) {\n this.client = client;\n this.sessionId = options.sessionId ?? randomUUID();\n if (typeof this.sessionId !== \"string\" || this.sessionId.trim().length === 0) {\n throw new TypeError(\"sessionId must be a non-empty string\");\n }\n this.keyPrefix = normalizeKeyPrefix(options.keyPrefix ?? \"openai:agents:session\", \"openai:agents:session\");\n this.initialItems = snapshotItems(options.initialItems ?? [], \"initialItems\");\n this.lockOptions = {\n lockRetryMs: options.lockRetryMs,\n lockTtlMs: options.lockTtlMs,\n lockWaitMs: options.lockWaitMs\n };\n const digest = createHash(\"sha256\").update(this.sessionId, \"utf8\").digest(\"hex\");\n this.sessionKey = `${this.keyPrefix}:{oais:${digest}}:session`;\n this.lockKey = `${this.keyPrefix}:{oais:${digest}}:mutation-lock`;\n }\n\n async getSessionId(): Promise<string> {\n await this.mutate(async (state) => state);\n return this.sessionId;\n }\n\n async getItems(limit?: number): Promise<AgentInputItem[]> {\n if (limit != null && limit <= 0) return [];\n if (limit != null && !Number.isSafeInteger(limit)) {\n throw new TypeError(\"limit must be a safe integer\");\n }\n const state = await this.readState();\n const items = limit == null ? state.items : state.items.slice(Math.max(state.items.length - limit, 0));\n return cloneSnapshot(items);\n }\n\n async addItems(items: AgentInputItem[]): Promise<void> {\n if (items.length === 0) return;\n const additions = snapshotItems(items, \"items\");\n await this.mutate(async (state) => ({\n ...state,\n items: [...state.items, ...additions]\n }));\n }\n\n async replaceHistoryWithCompaction(items: AgentInputItem[]): Promise<void> {\n const replacement = snapshotItems(items, \"items\");\n await this.mutate(async (state) => ({ ...state, items: replacement }));\n }\n\n async popItem(): Promise<AgentInputItem | undefined> {\n let popped: AgentInputItem | undefined;\n await this.mutate(async (state) => {\n popped = state.items.at(-1);\n return popped == null ? state : { ...state, items: state.items.slice(0, -1) };\n });\n return popped == null ? undefined : cloneSnapshot(popped);\n }\n\n async clearSession(): Promise<void> {\n await this.mutate(async (state) => ({ ...state, items: [], operations: {} }));\n }\n\n async applyHistoryMutations(args: SessionHistoryRewriteArgs): Promise<void> {\n if (args == null || !Array.isArray(args.mutations)) {\n throw new TypeError(\"session history mutations are invalid\");\n }\n if (args.mutations.length === 0) return;\n const mutations = cloneSnapshot(args.mutations);\n await this.mutate(async (state) => {\n let items = cloneSnapshot(state.items);\n for (const mutation of mutations) {\n if (mutation.type !== \"replace_function_call\") {\n throw new TypeError(\"unsupported session history mutation\");\n }\n const replacement = snapshotItem(mutation.replacement, \"mutation replacement\");\n let keptReplacement = false;\n const next: AgentInputItem[] = [];\n for (const item of items) {\n if (item.type === \"function_call\" && item.callId === mutation.callId) {\n if (!keptReplacement) {\n next.push(replacement);\n keptReplacement = true;\n }\n } else {\n next.push(item);\n }\n }\n items = next;\n }\n return { ...state, items };\n });\n }\n\n async applyHistoryTransaction(args: SessionHistoryTransactionArgs): Promise<void> {\n const { operationId, transaction } = snapshotTransactionArgs(args);\n const digest = snapshotDigest(transaction);\n await this.mutate(async (state) => {\n const existing = Object.getOwnPropertyDescriptor(state.operations, operationId)?.value as unknown;\n if (existing != null) {\n if (typeof existing !== \"string\") throw new Error(\"corrupt session history operation receipt\");\n if (existing !== digest) {\n throw new Error(\"session history operation was already applied with a different transaction\");\n }\n return state;\n }\n\n let items: AgentInputItem[];\n if (transaction.type === \"append_items\") {\n items = [...state.items, ...transaction.items];\n } else {\n const suffixStart = state.items.length - transaction.expectedSuffix.length;\n const actualSuffix = suffixStart < 0 ? [] : state.items.slice(suffixStart);\n if (suffixStart < 0 || !snapshotsEqual(actualSuffix, transaction.expectedSuffix)) {\n throw new Error(\"session history suffix no longer matches the transaction precondition\");\n }\n items = [...state.items.slice(0, suffixStart), ...transaction.replacement];\n }\n return {\n ...state,\n items,\n operations: { ...state.operations, [operationId]: digest }\n };\n });\n }\n\n private async mutate(\n operation: (state: StoredSessionState) => Promise<StoredSessionState>\n ): Promise<void> {\n await withMutationLocks(this.client, [this.lockKey], async () => {\n const current = await this.readState();\n const next = await operation(current);\n await this.client.command(\"HSET\", this.sessionKey, SESSION_STATE_FIELD, encodeSnapshot(next));\n }, this.lockOptions);\n }\n\n private async readState(): Promise<StoredSessionState> {\n const value = await this.client.command(\"HGET\", this.sessionKey, SESSION_STATE_FIELD);\n if (value == null) return this.emptyState();\n const state = decodeSnapshot<StoredSessionState>(value, \"OpenAI Agents session state\");\n if (\n state == null ||\n typeof state !== \"object\" ||\n state.formatVersion !== SESSION_FORMAT_VERSION ||\n state.sessionId !== this.sessionId ||\n !Array.isArray(state.items) ||\n state.operations == null ||\n typeof state.operations !== \"object\" ||\n Array.isArray(state.operations) ||\n Object.values(state.operations).some((digest) => typeof digest !== \"string\")\n ) {\n throw new Error(\"unsupported or corrupt FerricStore OpenAI Agents session state\");\n }\n return state;\n }\n\n private emptyState(): StoredSessionState {\n return {\n formatVersion: SESSION_FORMAT_VERSION,\n items: cloneSnapshot(this.initialItems),\n operations: {},\n sessionId: this.sessionId\n };\n }\n}\n\nfunction snapshotTransactionArgs(args: SessionHistoryTransactionArgs): {\n operationId: string;\n transaction: SessionHistoryTransaction;\n} {\n if (args == null || typeof args !== \"object\") throw new TypeError(\"session history transaction is invalid\");\n if (typeof args.operationId !== \"string\" || args.operationId.trim().length === 0) {\n throw new TypeError(\"session history transaction operationId must be a non-empty string\");\n }\n const transaction = cloneSnapshot(args.transaction);\n if (transaction == null || typeof transaction !== \"object\") {\n throw new TypeError(\"session history transaction must be an object\");\n }\n if (transaction.type === \"append_items\") {\n if (!Array.isArray(transaction.items)) throw new TypeError(\"session history append items are invalid\");\n return {\n operationId: args.operationId,\n transaction: { type: \"append_items\", items: snapshotItems(transaction.items, \"transaction items\") }\n };\n }\n if (transaction.type === \"replace_suffix\") {\n if (!Array.isArray(transaction.expectedSuffix) || !Array.isArray(transaction.replacement)) {\n throw new TypeError(\"session history suffix transaction is invalid\");\n }\n return {\n operationId: args.operationId,\n transaction: {\n type: \"replace_suffix\",\n expectedSuffix: snapshotItems(transaction.expectedSuffix, \"transaction expectedSuffix\"),\n replacement: snapshotItems(transaction.replacement, \"transaction replacement\")\n }\n };\n }\n throw new TypeError(\"unsupported session history transaction type\");\n}\n\nfunction snapshotItems(items: AgentInputItem[], name: string): AgentInputItem[] {\n if (!Array.isArray(items)) throw new TypeError(`${name} must be an array`);\n return items.map((item) => snapshotItem(item, name));\n}\n\nfunction snapshotItem(item: AgentInputItem, name: string): AgentInputItem {\n if (item == null || typeof item !== \"object\" || Array.isArray(item)) {\n throw new TypeError(`${name} contains an invalid agent item`);\n }\n return cloneSnapshot(item);\n}\n\nexport type {\n AgentInputItem,\n Session,\n SessionHistoryRewriteAwareSession,\n SessionHistoryTransactionAwareSession\n} from \"@openai/agents\";\n","import { randomUUID } from \"node:crypto\";\nimport { setTimeout as delay } from \"node:timers/promises\";\n\nimport { LockHeldError } from \"../errors.js\";\nimport type { Command, CommandArgument } from \"../internal.js\";\n\nexport interface FerricStoreCommandClient {\n command(...args: CommandArgument[]): Promise<unknown>;\n pipeline?(commands: readonly Command[]): Promise<unknown[]>;\n}\n\nexport interface FerricStoreLockOptions {\n /** Lease duration for adapter mutation locks. Defaults to five minutes. */\n lockTtlMs?: number;\n /** Maximum time to wait for a contended mutation lock. Defaults to 30 seconds. */\n lockWaitMs?: number;\n /** Delay between lock acquisition attempts. Defaults to 10 milliseconds. */\n lockRetryMs?: number;\n}\n\ninterface RequiredLockOptions {\n readonly lockRetryMs: number;\n readonly lockTtlMs: number;\n readonly lockWaitMs: number;\n}\n\nconst DEFAULT_LOCK_OPTIONS: RequiredLockOptions = {\n lockRetryMs: 10,\n lockTtlMs: 300_000,\n lockWaitMs: 30_000\n};\n\nexport function normalizeKeyPrefix(value: string, defaultValue: string): string {\n const prefix = value.length === 0 ? defaultValue : value;\n if (prefix.includes(\"\\0\")) throw new TypeError(\"keyPrefix must not contain NUL bytes\");\n const normalized = prefix.replace(/:+$/u, \"\");\n if (normalized.length === 0) throw new TypeError(\"keyPrefix must contain a character other than ':'\");\n return normalized;\n}\n\nexport function positiveInteger(value: number | undefined, fallback: number, name: string): number {\n const normalized = value ?? fallback;\n if (!Number.isSafeInteger(normalized) || normalized <= 0) {\n throw new TypeError(`${name} must be a positive safe integer`);\n }\n return normalized;\n}\n\nexport function nonNegativeInteger(value: number | undefined, fallback: number, name: string): number {\n const normalized = value ?? fallback;\n if (!Number.isSafeInteger(normalized) || normalized < 0) {\n throw new TypeError(`${name} must be a non-negative safe integer`);\n }\n return normalized;\n}\n\nexport function textResponse(value: unknown, name: string): string {\n if (typeof value === \"string\") return value;\n if (Buffer.isBuffer(value) || value instanceof Uint8Array) return Buffer.from(value).toString(\"utf8\");\n throw new TypeError(`FerricStore returned an invalid ${name}`);\n}\n\nexport function arrayResponse(value: unknown, name: string): unknown[] {\n if (!Array.isArray(value)) throw new TypeError(`FerricStore returned an invalid ${name}`);\n return value;\n}\n\nexport function integerResponse(value: unknown, name: string): number {\n const parsed = typeof value === \"number\" ? value : Number(textResponse(value, name));\n if (!Number.isSafeInteger(parsed)) throw new TypeError(`FerricStore returned an invalid ${name}`);\n return parsed;\n}\n\nexport async function executeCommands(\n client: FerricStoreCommandClient,\n commands: readonly Command[]\n): Promise<unknown[]> {\n if (commands.length === 0) return [];\n if (client.pipeline != null) return await client.pipeline(commands);\n return await Promise.all(commands.map(async (command) => await client.command(...command)));\n}\n\nexport async function withMutationLocks<T>(\n client: FerricStoreCommandClient,\n keys: readonly string[],\n operation: () => Promise<T>,\n options: FerricStoreLockOptions = {}\n): Promise<T> {\n const orderedKeys = [...new Set(keys)].sort();\n if (orderedKeys.length === 0) return await operation();\n\n const normalized: RequiredLockOptions = {\n lockRetryMs: positiveInteger(options.lockRetryMs, DEFAULT_LOCK_OPTIONS.lockRetryMs, \"lockRetryMs\"),\n lockTtlMs: positiveInteger(options.lockTtlMs, DEFAULT_LOCK_OPTIONS.lockTtlMs, \"lockTtlMs\"),\n lockWaitMs: nonNegativeInteger(options.lockWaitMs, DEFAULT_LOCK_OPTIONS.lockWaitMs, \"lockWaitMs\")\n };\n const owner = randomUUID();\n const acquired: string[] = [];\n const deadline = performance.now() + normalized.lockWaitMs;\n let primaryError: unknown;\n let heartbeatError: unknown;\n let releaseError: unknown;\n let result: T | undefined;\n let operationCompleted = false;\n const heartbeatAbort = new AbortController();\n\n try {\n for (const key of orderedKeys) {\n while (!(await tryAcquireLock(client, key, owner, normalized.lockTtlMs))) {\n if (performance.now() >= deadline) {\n throw new Error(`timed out acquiring FerricStore lock ${JSON.stringify(key)}`);\n }\n await delay(normalized.lockRetryMs);\n }\n acquired.push(key);\n }\n\n const heartbeat = renewLocks(\n client,\n acquired,\n owner,\n normalized.lockTtlMs,\n heartbeatAbort.signal,\n (error) => {\n heartbeatError ??= error;\n }\n );\n try {\n result = await operation();\n operationCompleted = true;\n } catch (error) {\n primaryError = error;\n } finally {\n heartbeatAbort.abort();\n await heartbeat;\n }\n } catch (error) {\n primaryError ??= error;\n } finally {\n heartbeatAbort.abort();\n for (const key of acquired.reverse()) {\n try {\n await client.command(\"UNLOCK\", key, owner);\n } catch (error) {\n releaseError ??= error;\n }\n }\n }\n if (primaryError != null) throw errorObject(primaryError);\n if (heartbeatError != null) throw errorObject(heartbeatError);\n if (releaseError != null) throw errorObject(releaseError);\n if (!operationCompleted) throw new Error(\"FerricStore mutation did not complete\");\n return result as T;\n}\n\nasync function tryAcquireLock(\n client: FerricStoreCommandClient,\n key: string,\n owner: string,\n ttlMs: number\n): Promise<boolean> {\n try {\n const response = await client.command(\"LOCK\", key, owner, ttlMs);\n return response === true || response === \"OK\" || Buffer.isBuffer(response) && response.equals(Buffer.from(\"OK\"));\n } catch (error) {\n if (error instanceof LockHeldError) return false;\n throw error;\n }\n}\n\nasync function renewLocks(\n client: FerricStoreCommandClient,\n keys: readonly string[],\n owner: string,\n ttlMs: number,\n signal: AbortSignal,\n onError: (error: unknown) => void\n): Promise<void> {\n const intervalMs = Math.max(Math.floor(ttlMs / 3), 10);\n const retryMs = Math.min(Math.max(Math.floor(intervalMs / 10), 10), 1_000);\n const lastExtended = new Map(keys.map((key) => [key, performance.now()]));\n let waitMs = intervalMs;\n while (!signal.aborted) {\n try {\n await delay(waitMs, undefined, { signal });\n } catch (error) {\n if (signal.aborted) return;\n onError(error);\n return;\n }\n const now = performance.now();\n let retry = false;\n for (const key of keys) {\n try {\n const response = await client.command(\"EXTEND\", key, owner, ttlMs);\n if (integerResponse(response, \"EXTEND response\") !== 1) {\n onError(new Error(`lost FerricStore lock ${JSON.stringify(key)} while mutating data`));\n return;\n }\n lastExtended.set(key, now);\n } catch (error) {\n if (now - (lastExtended.get(key) ?? 0) >= ttlMs) {\n onError(new Error(`lost FerricStore lock ${JSON.stringify(key)} while mutating data`, { cause: error }));\n return;\n }\n retry = true;\n }\n }\n waitMs = retry ? retryMs : intervalMs;\n }\n}\n\nfunction errorObject(value: unknown): Error {\n return value instanceof Error ? value : new Error(\"FerricStore mutation failed\", { cause: value });\n}\n","export class FerricStoreError extends Error {\n readonly code: string = \"ferricstore_error\";\n readonly raw: unknown;\n readonly retryable: boolean | undefined;\n readonly safeToRetry: boolean | undefined;\n readonly retryAfterMs: number | undefined;\n\n constructor(message: string, options: {\n raw?: unknown;\n cause?: unknown;\n retryable?: boolean;\n safeToRetry?: boolean;\n retryAfterMs?: number;\n } = {}) {\n super(message, { cause: options.cause });\n this.name = new.target.name;\n this.raw = options.raw;\n this.retryable = options.retryable ?? structuredBooleanField(options.raw, \"retryable\");\n this.safeToRetry = options.safeToRetry ?? structuredBooleanField(options.raw, \"safe_to_retry\");\n this.retryAfterMs = options.retryAfterMs ?? structuredIntegerField(options.raw, \"retry_after_ms\");\n }\n}\n\nexport class HTTPTransportError extends FerricStoreError {\n override readonly code = \"http_transport\";\n readonly statusCode: number | undefined;\n\n constructor(message: string, options: ConstructorParameters<typeof FerricStoreError>[1] & {\n statusCode?: number;\n } = {}) {\n super(message, options);\n this.statusCode = options.statusCode;\n }\n}\n\nexport type RequestDisposition = \"unsent\" | \"possibly_sent\";\n/** @deprecated Use RequestDisposition; retained for source compatibility. */\nexport type ConnectionRequestDisposition = RequestDisposition;\n\n/** Connection closure annotated with whether the current request may have reached the server. */\nexport class ConnectionClosedError extends FerricStoreError {\n override readonly code = \"connection_closed\";\n readonly requestDisposition: RequestDisposition;\n\n constructor(\n requestDisposition: RequestDisposition,\n options: { raw?: unknown; cause?: unknown; message?: string } = {}\n ) {\n super(\n options.message ?? (requestDisposition === \"unsent\"\n ? \"FerricStore connection is closed\"\n : \"FerricStore connection closed\"),\n options\n );\n this.requestDisposition = requestDisposition;\n }\n}\n\n/** Request timeout annotated with whether the request may have reached the server. */\nexport class RequestTimeoutError extends FerricStoreError {\n override readonly code = \"request_timeout\";\n readonly requestDisposition: RequestDisposition;\n readonly timeoutMs: number;\n\n constructor(\n timeoutMs: number,\n requestDisposition: RequestDisposition,\n options: { raw?: unknown; cause?: unknown } = {}\n ) {\n super(`FerricStore request timed out after ${timeoutMs}ms`, options);\n this.requestDisposition = requestDisposition;\n this.timeoutMs = timeoutMs;\n }\n}\n\nexport class FlowNotFoundError extends FerricStoreError {\n override readonly code = \"flow_not_found\";\n}\n\nexport class FlowWrongStateError extends FerricStoreError {\n override readonly code = \"flow_wrong_state\";\n}\n\nexport class StaleLeaseError extends FerricStoreError {\n override readonly code = \"stale_lease\";\n}\n\n/** FLOW.POLICY.SET expected_generation did not match the stored generation. */\nexport class StalePolicyGenerationError extends FerricStoreError {\n override readonly code = \"stale_policy_generation\";\n}\n\nexport class FlowAlreadyExistsError extends FerricStoreError {\n override readonly code = \"flow_already_exists\";\n}\n\nexport class LockHeldError extends FerricStoreError {\n override readonly code = \"lock_held\";\n}\n\nexport class LockNotOwnedError extends FerricStoreError {\n override readonly code = \"lock_not_owned\";\n}\n\nexport class InvalidCommandError extends FerricStoreError {\n override readonly code = \"invalid_command\";\n}\n\nexport class OverloadedError extends FerricStoreError {\n override readonly code = \"overloaded\";\n readonly reason: string | undefined;\n\n constructor(\n message: string,\n options: {\n raw?: unknown;\n cause?: unknown;\n retryAfterMs?: number;\n reason?: string;\n retryable?: boolean;\n safeToRetry?: boolean;\n } = {}\n ) {\n const local = options.raw == null;\n super(message, {\n ...options,\n retryable: options.retryable ?? (local ? true : undefined),\n safeToRetry: options.safeToRetry ?? (local ? true : undefined)\n });\n this.reason = options.reason;\n }\n}\n\n/** The contacted endpoint cannot serve this route and topology should be refreshed. */\nexport class RerouteError extends FerricStoreError {\n override readonly code = \"reroute\";\n}\n\nconst OVERLOAD_CODES = new Set([\n \"backpressure\",\n \"busy\",\n \"flow_control_window_exhausted\",\n \"lane_queue_full\",\n \"overloaded\"\n]);\n\nexport function classifyServerError(\n message: string,\n raw?: unknown,\n cause?: unknown,\n status?: number | string\n): FerricStoreError {\n const lower = message.toLowerCase();\n const structuredCode = structuredStringField(raw, \"code\");\n const code = structuredCode?.toLowerCase();\n const retry = {\n retryable: structuredBooleanField(raw, \"retryable\"),\n safeToRetry: structuredBooleanField(raw, \"safe_to_retry\"),\n retryAfterMs: structuredIntegerField(raw, \"retry_after_ms\") ?? intField(lower, \"retry_after_ms\")\n };\n\n if (isRerouteStatus(status) || code === \"reroute\") {\n return new RerouteError(message, { cause, raw, ...definedRetryMetadata(retry) });\n }\n if (isBusyStatus(status) || isOverloadCode(structuredCode) || overloadMessage(lower)) {\n return new OverloadedError(message, {\n cause,\n raw,\n ...definedRetryMetadata(retry),\n reason: structuredStringField(raw, \"reason\") ?? structuredCode ?? stringField(lower, \"reason\"),\n retryAfterMs: retry.retryAfterMs\n });\n }\n if (code === \"flow_already_exists\" || (lower.includes(\"flow\") && lower.includes(\"already exists\"))) {\n return new FlowAlreadyExistsError(message, { cause, raw });\n }\n if (lower.includes(\"flow wrong state\") || code === \"flow_wrong_state\") {\n return new FlowWrongStateError(message, { cause, raw });\n }\n if (\n code === \"stale_lease\"\n || code === \"stale_flow_lease\"\n || lower.includes(\"stale flow lease\")\n || lower.includes(\"stale lease\")\n || lower.includes(\"stale token\")\n ) {\n return new StaleLeaseError(message, { cause, raw });\n }\n if (\n code === \"stale_generation\"\n || code === \"stale_policy_generation\"\n || code === \"stale_flow_policy_generation\"\n || lower.includes(\"stale flow policy generation\")\n || lower.includes(\"stale policy generation\")\n ) {\n return new StalePolicyGenerationError(message, { cause, raw });\n }\n if (\n code === \"flow_not_found\"\n || (lower.includes(\"flow\") && (lower.includes(\"not found\") || lower.includes(\"does not exist\")))\n ) {\n return new FlowNotFoundError(message, { cause, raw });\n }\n if (code === \"lock_held\" || lower.includes(\"lock is held\") || lower.includes(\"held by another owner\")) {\n return new LockHeldError(message, { cause, raw });\n }\n if (code === \"lock_not_owned\" || lower.includes(\"not the lock owner\") || lower.includes(\"caller is not the lock owner\")) {\n return new LockNotOwnedError(message, { cause, raw });\n }\n if (code === \"invalid_command\" || lower.includes(\"wrong number of arguments\") || lower.includes(\"syntax error\")) {\n return new InvalidCommandError(message, { cause, raw });\n }\n\n return new FerricStoreError(message, { cause, raw, ...definedRetryMetadata(retry) });\n}\n\nexport function mapException(error: unknown): unknown {\n if (error instanceof FerricStoreError) {\n return error;\n }\n\n if (!(error instanceof Error)) {\n return error;\n }\n\n const message = error.message;\n const serverLike =\n error.name === \"ResponseError\" ||\n message.startsWith(\"ERR \") ||\n message.startsWith(\"WRONGTYPE \") ||\n message.startsWith(\"DISTLOCK \");\n\n if (!serverLike) {\n return error;\n }\n\n return classifyServerError(message, error, error);\n}\n\nfunction intField(message: string, name: string): number | undefined {\n const match = new RegExp(`\\\\b${name}=([0-9]+)\\\\b`).exec(message);\n return match?.[1] == null ? undefined : nonNegativeSafeIntegerText(match[1]);\n}\n\nfunction isBusyStatus(status: number | string | undefined): boolean {\n return status === 4 || (typeof status === \"string\" && (status === \"4\" || status.toLowerCase() === \"busy\"));\n}\n\nfunction isRerouteStatus(status: number | string | undefined): boolean {\n return status === 5 || (typeof status === \"string\" && (status === \"5\" || status.toLowerCase() === \"reroute\"));\n}\n\nfunction isOverloadCode(code: string | undefined): boolean {\n return code != null && OVERLOAD_CODES.has(code.toLowerCase());\n}\n\nfunction overloadMessage(message: string): boolean {\n return /\\boverloaded\\b/u.test(message) || /(?:^|\\s)busy(?:\\s|:|$)/u.test(message);\n}\n\nfunction structuredIntegerField(raw: unknown, name: string): number | undefined {\n const value = structuredField(raw, name);\n if (typeof value === \"number\") {\n return Number.isSafeInteger(value) && value >= 0 ? value : undefined;\n }\n if (typeof value === \"bigint\") {\n return value >= 0n && value <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(value) : undefined;\n }\n const text = binaryText(value);\n return text == null ? undefined : nonNegativeSafeIntegerText(text);\n}\n\nfunction structuredBooleanField(raw: unknown, name: string): boolean | undefined {\n const value = structuredField(raw, name);\n if (typeof value === \"boolean\") return value;\n const text = binaryText(value)?.toLowerCase();\n if (text === \"true\" || text === \"1\") return true;\n if (text === \"false\" || text === \"0\") return false;\n return undefined;\n}\n\nfunction definedRetryMetadata(metadata: {\n readonly retryable?: boolean;\n readonly safeToRetry?: boolean;\n readonly retryAfterMs?: number;\n}): { retryable?: boolean; safeToRetry?: boolean; retryAfterMs?: number } {\n return {\n ...(metadata.retryable == null ? {} : { retryable: metadata.retryable }),\n ...(metadata.safeToRetry == null ? {} : { safeToRetry: metadata.safeToRetry }),\n ...(metadata.retryAfterMs == null ? {} : { retryAfterMs: metadata.retryAfterMs })\n };\n}\n\nfunction nonNegativeSafeIntegerText(value: string): number | undefined {\n if (!/^[0-9]+$/u.test(value)) return undefined;\n const parsed = Number.parseInt(value, 10);\n return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : undefined;\n}\n\nfunction structuredStringField(raw: unknown, name: string): string | undefined {\n return binaryText(structuredField(raw, name));\n}\n\nfunction structuredField(raw: unknown, name: string): unknown {\n if (raw instanceof Map) {\n if (raw.has(name)) return raw.get(name);\n for (const [key, value] of raw.entries()) {\n if (binaryText(key) === name) return value;\n }\n return undefined;\n }\n if (typeof raw === \"object\" && raw != null && Object.hasOwn(raw, name)) {\n return (raw as Record<string, unknown>)[name];\n }\n return undefined;\n}\n\nfunction binaryText(value: unknown): string | undefined {\n if (typeof value === \"string\") return value;\n if (Buffer.isBuffer(value) || value instanceof Uint8Array) return Buffer.from(value).toString(\"utf8\");\n return undefined;\n}\n\nfunction stringField(message: string, name: string): string | undefined {\n const match = new RegExp(`\\\\b${name}=([a-z0-9_:-]+)\\\\b`).exec(message);\n return match?.[1];\n}\n","import { createHash } from \"node:crypto\";\n\ntype EncodedSnapshot =\n | [\"array\", EncodedSnapshot[]]\n | [\"binary\", string]\n | [\"boolean\", boolean]\n | [\"null\"]\n | [\"number\", number | \"NaN\" | \"+Infinity\" | \"-Infinity\" | \"-0\"]\n | [\"object\", [string, EncodedSnapshot][]]\n | [\"string\", string]\n | [\"undefined\"];\n\nexport function encodeSnapshot(value: unknown): Buffer {\n return Buffer.from(JSON.stringify(snapshot(value, new WeakSet())), \"utf8\");\n}\n\nexport function decodeSnapshot<T>(value: unknown, name: string): T {\n const bytes = typeof value === \"string\" ? Buffer.from(value, \"utf8\") : Buffer.from(asBytes(value, name));\n let encoded: unknown;\n try {\n encoded = JSON.parse(bytes.toString(\"utf8\"));\n } catch (error) {\n throw new Error(`FerricStore returned an invalid ${name}`, { cause: error });\n }\n return restore(encoded, name) as T;\n}\n\nexport function snapshotDigest(value: unknown): string {\n return createHash(\"sha256\").update(encodeSnapshot(value)).digest(\"hex\");\n}\n\nexport function cloneSnapshot<T>(value: T): T {\n return decodeSnapshot<T>(encodeSnapshot(value), \"snapshot\");\n}\n\nexport function snapshotsEqual(left: unknown, right: unknown): boolean {\n return encodeSnapshot(left).equals(encodeSnapshot(right));\n}\n\nfunction snapshot(value: unknown, ancestors: WeakSet<object>): EncodedSnapshot {\n if (value === null) return [\"null\"];\n if (value === undefined) return [\"undefined\"];\n if (typeof value === \"string\") return [\"string\", value];\n if (typeof value === \"boolean\") return [\"boolean\", value];\n if (typeof value === \"number\") {\n if (Object.is(value, -0)) return [\"number\", \"-0\"];\n if (Number.isNaN(value)) return [\"number\", \"NaN\"];\n if (value === Infinity) return [\"number\", \"+Infinity\"];\n if (value === -Infinity) return [\"number\", \"-Infinity\"];\n return [\"number\", value];\n }\n if (typeof value !== \"object\") throw new TypeError(\"session history contains unsupported data\");\n if (value instanceof Uint8Array) {\n const keys = Reflect.ownKeys(value);\n if (\n keys.length !== value.length ||\n keys.some((key) => typeof key !== \"string\" || !isArrayIndex(key, value.length))\n ) {\n throw new TypeError(\"session history binary data contains custom properties\");\n }\n return [\"binary\", Buffer.from(value).toString(\"base64\")];\n }\n if (ancestors.has(value)) throw new TypeError(\"session history contains cyclic data\");\n ancestors.add(value);\n try {\n if (Array.isArray(value)) {\n const keys = Reflect.ownKeys(value);\n if (\n keys.length !== value.length + 1 ||\n keys.some((key) => typeof key !== \"string\" || key !== \"length\" && !isArrayIndex(key, value.length))\n ) {\n throw new TypeError(\"session history contains a sparse or customized array\");\n }\n const items: EncodedSnapshot[] = [];\n for (let index = 0; index < value.length; index += 1) {\n const descriptor = Object.getOwnPropertyDescriptor(value, String(index));\n if (descriptor == null || !descriptor.enumerable || !(\"value\" in descriptor)) {\n throw new TypeError(\"session history contains an unsupported array item\");\n }\n items.push(snapshot(descriptor.value as unknown, ancestors));\n }\n return [\"array\", items];\n }\n const prototype: unknown = Object.getPrototypeOf(value);\n if (prototype !== Object.prototype && prototype !== null) {\n throw new TypeError(\"session history contains an unsupported object\");\n }\n const keys = Reflect.ownKeys(value);\n if (keys.some((key) => typeof key !== \"string\")) {\n throw new TypeError(\"session history contains a symbol property\");\n }\n const entries: [string, EncodedSnapshot][] = [];\n for (const key of (keys as string[]).sort((left, right) => left.localeCompare(right))) {\n const descriptor = Object.getOwnPropertyDescriptor(value, key);\n if (descriptor == null || !descriptor.enumerable || !(\"value\" in descriptor)) {\n throw new TypeError(\"session history contains an unsupported property\");\n }\n entries.push([key, snapshot(descriptor.value as unknown, ancestors)]);\n }\n return [\"object\", entries];\n } finally {\n ancestors.delete(value);\n }\n}\n\nfunction restore(value: unknown, name: string): unknown {\n if (!Array.isArray(value) || typeof value[0] !== \"string\") throw new TypeError(`invalid ${name}`);\n switch (value[0]) {\n case \"null\": return null;\n case \"undefined\": return undefined;\n case \"string\": return requireValueType(value[1], \"string\", name);\n case \"boolean\": return requireValueType(value[1], \"boolean\", name);\n case \"binary\": return Buffer.from(requireValueType(value[1], \"string\", name), \"base64\");\n case \"number\": return restoreNumber(value[1], name);\n case \"array\": {\n if (!Array.isArray(value[1])) throw new TypeError(`invalid ${name}`);\n return value[1].map((item) => restore(item, name));\n }\n case \"object\": {\n if (!Array.isArray(value[1])) throw new TypeError(`invalid ${name}`);\n const result: Record<string, unknown> = {};\n for (const entry of value[1]) {\n if (!Array.isArray(entry) || entry.length !== 2 || typeof entry[0] !== \"string\") {\n throw new TypeError(`invalid ${name}`);\n }\n Object.defineProperty(result, entry[0], {\n configurable: true,\n enumerable: true,\n value: restore(entry[1], name),\n writable: true\n });\n }\n return result;\n }\n default: throw new TypeError(`invalid ${name}`);\n }\n}\n\nfunction restoreNumber(value: unknown, name: string): number {\n if (typeof value === \"number\") return value;\n if (value === \"NaN\") return Number.NaN;\n if (value === \"+Infinity\") return Infinity;\n if (value === \"-Infinity\") return -Infinity;\n if (value === \"-0\") return -0;\n throw new TypeError(`invalid ${name}`);\n}\n\nfunction requireValueType<T extends \"boolean\" | \"string\">(\n value: unknown,\n type: T,\n name: string\n): T extends \"string\" ? string : boolean {\n if (typeof value !== type) throw new TypeError(`invalid ${name}`);\n return value as T extends \"string\" ? string : boolean;\n}\n\nfunction asBytes(value: unknown, name: string): Uint8Array {\n if (Buffer.isBuffer(value) || value instanceof Uint8Array) return value;\n throw new TypeError(`FerricStore returned a non-binary ${name}`);\n}\n\nfunction isArrayIndex(key: string, length: number): boolean {\n if (!/^(?:0|[1-9]\\d*)$/u.test(key)) return false;\n const index = Number(key);\n return Number.isSafeInteger(index) && index >= 0 && index < length;\n}\n"],"mappings":"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","names":["createHash","randomUUID","keys","randomUUID","createHash"]}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { C as CommandArgument } from './internal-lEEDZpPH.js';
|
|
2
|
+
|
|
3
|
+
type MaxActiveMs = number | "infinity";
|
|
4
|
+
/** An exact Flow fencing token; bigint is used beyond JavaScript's safe integer range. */
|
|
5
|
+
type FencingToken = number | bigint;
|
|
6
|
+
interface ChildSpec {
|
|
7
|
+
id: string;
|
|
8
|
+
type: string;
|
|
9
|
+
payload?: unknown;
|
|
10
|
+
partitionKey?: string;
|
|
11
|
+
values?: Record<string, unknown>;
|
|
12
|
+
valueRefs?: Record<string, string>;
|
|
13
|
+
}
|
|
14
|
+
type StateMetaValue = string | number | boolean | Buffer;
|
|
15
|
+
type StateMeta = Record<string, StateMetaValue>;
|
|
16
|
+
interface CreateItem {
|
|
17
|
+
id: string;
|
|
18
|
+
payload?: unknown;
|
|
19
|
+
partitionKey?: string;
|
|
20
|
+
attributes?: Record<string, CommandArgument>;
|
|
21
|
+
values?: Record<string, unknown>;
|
|
22
|
+
valueRefs?: Record<string, string>;
|
|
23
|
+
stateMeta?: StateMeta;
|
|
24
|
+
}
|
|
25
|
+
/** @internal */
|
|
26
|
+
declare const CLAIMED_ITEM_WIRE: unique symbol;
|
|
27
|
+
/** @internal */
|
|
28
|
+
interface ClaimedItemWire {
|
|
29
|
+
id: Buffer;
|
|
30
|
+
partitionKey?: Buffer | null;
|
|
31
|
+
leaseToken: Buffer;
|
|
32
|
+
fencingToken: FencingToken;
|
|
33
|
+
}
|
|
34
|
+
interface ClaimedItem<TPayload = unknown> {
|
|
35
|
+
id: string;
|
|
36
|
+
leaseToken: Buffer;
|
|
37
|
+
fencingToken: FencingToken;
|
|
38
|
+
partitionKey?: string;
|
|
39
|
+
/** Present when supplied by a full response or known compact-claim context. */
|
|
40
|
+
type?: string;
|
|
41
|
+
state: string;
|
|
42
|
+
runState?: string;
|
|
43
|
+
payload?: TPayload | null;
|
|
44
|
+
attributes?: Record<string, unknown>;
|
|
45
|
+
/** @internal */
|
|
46
|
+
[CLAIMED_ITEM_WIRE]?: ClaimedItemWire;
|
|
47
|
+
}
|
|
48
|
+
interface FencedItem {
|
|
49
|
+
id: string;
|
|
50
|
+
fencingToken: FencingToken;
|
|
51
|
+
leaseToken?: Buffer;
|
|
52
|
+
partitionKey?: string;
|
|
53
|
+
}
|
|
54
|
+
interface RateLimitResult {
|
|
55
|
+
status: string;
|
|
56
|
+
count: number;
|
|
57
|
+
remaining: number;
|
|
58
|
+
resetMs: number;
|
|
59
|
+
allowed: boolean;
|
|
60
|
+
}
|
|
61
|
+
interface KeyInfo {
|
|
62
|
+
type: string;
|
|
63
|
+
valueSize: number;
|
|
64
|
+
ttlMs: number;
|
|
65
|
+
hotCacheStatus: string;
|
|
66
|
+
lastWriteShard: number;
|
|
67
|
+
raw: Record<string, unknown>;
|
|
68
|
+
}
|
|
69
|
+
interface FetchOrComputeHitResult<T = unknown> {
|
|
70
|
+
readonly computeMode: "hit";
|
|
71
|
+
readonly hit: true;
|
|
72
|
+
readonly shouldCompute: false;
|
|
73
|
+
readonly status: "hit";
|
|
74
|
+
readonly value: T | null;
|
|
75
|
+
}
|
|
76
|
+
interface FetchOrComputeComputeResult {
|
|
77
|
+
/** Opaque application hint echoed for the process elected to compute. */
|
|
78
|
+
readonly computeHint: Buffer;
|
|
79
|
+
readonly computeMode: "fenced";
|
|
80
|
+
/** Fencing token required when publishing the computed result or error. */
|
|
81
|
+
readonly computeToken: Buffer;
|
|
82
|
+
readonly hit: false;
|
|
83
|
+
readonly shouldCompute: true;
|
|
84
|
+
readonly status: "compute";
|
|
85
|
+
}
|
|
86
|
+
type FetchOrComputeFencedResult = FetchOrComputeComputeResult;
|
|
87
|
+
type FetchOrComputeResult<T = unknown> = FetchOrComputeHitResult<T> | FetchOrComputeComputeResult;
|
|
88
|
+
interface FlowMaxActiveFailure {
|
|
89
|
+
readonly maxActiveMs: number;
|
|
90
|
+
readonly reason: "max_active_ms";
|
|
91
|
+
}
|
|
92
|
+
interface FlowRecord<TPayload = unknown> {
|
|
93
|
+
id: string;
|
|
94
|
+
type: string;
|
|
95
|
+
state: string;
|
|
96
|
+
partitionKey: string;
|
|
97
|
+
runState?: string;
|
|
98
|
+
payload?: TPayload | null;
|
|
99
|
+
leaseToken: Buffer;
|
|
100
|
+
fencingToken: FencingToken;
|
|
101
|
+
version: number;
|
|
102
|
+
parentFlowId?: string;
|
|
103
|
+
rootFlowId?: string;
|
|
104
|
+
correlationId?: string;
|
|
105
|
+
maxActiveMs?: number;
|
|
106
|
+
error?: unknown;
|
|
107
|
+
failureReason?: string;
|
|
108
|
+
valueRefs?: Record<string, unknown>;
|
|
109
|
+
values?: Record<string, unknown>;
|
|
110
|
+
valueSizes?: Record<string, unknown>;
|
|
111
|
+
valueOmitted?: Record<string, unknown>;
|
|
112
|
+
valueMissing?: Record<string, unknown>;
|
|
113
|
+
attributes?: Record<string, unknown>;
|
|
114
|
+
stateMeta?: Record<string, unknown>;
|
|
115
|
+
indexedStateMeta?: string;
|
|
116
|
+
raw?: unknown;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
type Outcome = TransitionOutcome | CompleteOutcome | RetryOutcome | FailOutcome;
|
|
120
|
+
interface NamedValueMutation {
|
|
121
|
+
values?: Record<string, unknown>;
|
|
122
|
+
valueRefs?: Record<string, string>;
|
|
123
|
+
dropValues?: string[];
|
|
124
|
+
overrideValues?: string[];
|
|
125
|
+
attributesMerge?: Record<string, CommandArgument>;
|
|
126
|
+
attributesDelete?: string[];
|
|
127
|
+
stateMeta?: StateMeta;
|
|
128
|
+
}
|
|
129
|
+
interface TransitionOutcome extends NamedValueMutation {
|
|
130
|
+
readonly kind: "transition";
|
|
131
|
+
readonly toState: string;
|
|
132
|
+
readonly payload?: unknown;
|
|
133
|
+
readonly priority?: number;
|
|
134
|
+
readonly runAtMs?: number;
|
|
135
|
+
}
|
|
136
|
+
interface CompleteOutcome extends NamedValueMutation {
|
|
137
|
+
readonly kind: "complete";
|
|
138
|
+
readonly payload?: unknown;
|
|
139
|
+
readonly result?: unknown;
|
|
140
|
+
readonly ttlMs?: number;
|
|
141
|
+
}
|
|
142
|
+
interface RetryOutcome extends NamedValueMutation {
|
|
143
|
+
readonly kind: "retry";
|
|
144
|
+
readonly error?: unknown;
|
|
145
|
+
readonly payload?: unknown;
|
|
146
|
+
readonly runAtMs?: number;
|
|
147
|
+
}
|
|
148
|
+
interface FailOutcome extends NamedValueMutation {
|
|
149
|
+
readonly kind: "fail";
|
|
150
|
+
readonly error?: unknown;
|
|
151
|
+
readonly payload?: unknown;
|
|
152
|
+
readonly ttlMs?: number;
|
|
153
|
+
}
|
|
154
|
+
declare function transition(toState: string, options?: Omit<TransitionOutcome, "kind" | "toState">): TransitionOutcome;
|
|
155
|
+
declare function complete(options?: Omit<CompleteOutcome, "kind">): CompleteOutcome;
|
|
156
|
+
declare function retry(options?: Omit<RetryOutcome, "kind">): RetryOutcome;
|
|
157
|
+
declare function fail(options?: Omit<FailOutcome, "kind">): FailOutcome;
|
|
158
|
+
declare function isOutcome(value: unknown): value is Outcome;
|
|
159
|
+
|
|
160
|
+
export { type ClaimedItem as C, type FencingToken as F, type KeyInfo as K, type MaxActiveMs as M, type NamedValueMutation as N, type Outcome as O, type RateLimitResult as R, type StateMeta as S, type TransitionOutcome as T, type FlowRecord as a, type FetchOrComputeResult as b, type ChildSpec as c, type FencedItem as d, type CreateItem as e, type CompleteOutcome as f, type FailOutcome as g, type FetchOrComputeComputeResult as h, type FetchOrComputeFencedResult as i, type FetchOrComputeHitResult as j, type FlowMaxActiveFailure as k, type RetryOutcome as l, type StateMetaValue as m, complete as n, fail as o, isOutcome as p, retry as r, transition as t };
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { C as CommandArgument } from './internal-lEEDZpPH.cjs';
|
|
2
|
+
|
|
3
|
+
type MaxActiveMs = number | "infinity";
|
|
4
|
+
/** An exact Flow fencing token; bigint is used beyond JavaScript's safe integer range. */
|
|
5
|
+
type FencingToken = number | bigint;
|
|
6
|
+
interface ChildSpec {
|
|
7
|
+
id: string;
|
|
8
|
+
type: string;
|
|
9
|
+
payload?: unknown;
|
|
10
|
+
partitionKey?: string;
|
|
11
|
+
values?: Record<string, unknown>;
|
|
12
|
+
valueRefs?: Record<string, string>;
|
|
13
|
+
}
|
|
14
|
+
type StateMetaValue = string | number | boolean | Buffer;
|
|
15
|
+
type StateMeta = Record<string, StateMetaValue>;
|
|
16
|
+
interface CreateItem {
|
|
17
|
+
id: string;
|
|
18
|
+
payload?: unknown;
|
|
19
|
+
partitionKey?: string;
|
|
20
|
+
attributes?: Record<string, CommandArgument>;
|
|
21
|
+
values?: Record<string, unknown>;
|
|
22
|
+
valueRefs?: Record<string, string>;
|
|
23
|
+
stateMeta?: StateMeta;
|
|
24
|
+
}
|
|
25
|
+
/** @internal */
|
|
26
|
+
declare const CLAIMED_ITEM_WIRE: unique symbol;
|
|
27
|
+
/** @internal */
|
|
28
|
+
interface ClaimedItemWire {
|
|
29
|
+
id: Buffer;
|
|
30
|
+
partitionKey?: Buffer | null;
|
|
31
|
+
leaseToken: Buffer;
|
|
32
|
+
fencingToken: FencingToken;
|
|
33
|
+
}
|
|
34
|
+
interface ClaimedItem<TPayload = unknown> {
|
|
35
|
+
id: string;
|
|
36
|
+
leaseToken: Buffer;
|
|
37
|
+
fencingToken: FencingToken;
|
|
38
|
+
partitionKey?: string;
|
|
39
|
+
/** Present when supplied by a full response or known compact-claim context. */
|
|
40
|
+
type?: string;
|
|
41
|
+
state: string;
|
|
42
|
+
runState?: string;
|
|
43
|
+
payload?: TPayload | null;
|
|
44
|
+
attributes?: Record<string, unknown>;
|
|
45
|
+
/** @internal */
|
|
46
|
+
[CLAIMED_ITEM_WIRE]?: ClaimedItemWire;
|
|
47
|
+
}
|
|
48
|
+
interface FencedItem {
|
|
49
|
+
id: string;
|
|
50
|
+
fencingToken: FencingToken;
|
|
51
|
+
leaseToken?: Buffer;
|
|
52
|
+
partitionKey?: string;
|
|
53
|
+
}
|
|
54
|
+
interface RateLimitResult {
|
|
55
|
+
status: string;
|
|
56
|
+
count: number;
|
|
57
|
+
remaining: number;
|
|
58
|
+
resetMs: number;
|
|
59
|
+
allowed: boolean;
|
|
60
|
+
}
|
|
61
|
+
interface KeyInfo {
|
|
62
|
+
type: string;
|
|
63
|
+
valueSize: number;
|
|
64
|
+
ttlMs: number;
|
|
65
|
+
hotCacheStatus: string;
|
|
66
|
+
lastWriteShard: number;
|
|
67
|
+
raw: Record<string, unknown>;
|
|
68
|
+
}
|
|
69
|
+
interface FetchOrComputeHitResult<T = unknown> {
|
|
70
|
+
readonly computeMode: "hit";
|
|
71
|
+
readonly hit: true;
|
|
72
|
+
readonly shouldCompute: false;
|
|
73
|
+
readonly status: "hit";
|
|
74
|
+
readonly value: T | null;
|
|
75
|
+
}
|
|
76
|
+
interface FetchOrComputeComputeResult {
|
|
77
|
+
/** Opaque application hint echoed for the process elected to compute. */
|
|
78
|
+
readonly computeHint: Buffer;
|
|
79
|
+
readonly computeMode: "fenced";
|
|
80
|
+
/** Fencing token required when publishing the computed result or error. */
|
|
81
|
+
readonly computeToken: Buffer;
|
|
82
|
+
readonly hit: false;
|
|
83
|
+
readonly shouldCompute: true;
|
|
84
|
+
readonly status: "compute";
|
|
85
|
+
}
|
|
86
|
+
type FetchOrComputeFencedResult = FetchOrComputeComputeResult;
|
|
87
|
+
type FetchOrComputeResult<T = unknown> = FetchOrComputeHitResult<T> | FetchOrComputeComputeResult;
|
|
88
|
+
interface FlowMaxActiveFailure {
|
|
89
|
+
readonly maxActiveMs: number;
|
|
90
|
+
readonly reason: "max_active_ms";
|
|
91
|
+
}
|
|
92
|
+
interface FlowRecord<TPayload = unknown> {
|
|
93
|
+
id: string;
|
|
94
|
+
type: string;
|
|
95
|
+
state: string;
|
|
96
|
+
partitionKey: string;
|
|
97
|
+
runState?: string;
|
|
98
|
+
payload?: TPayload | null;
|
|
99
|
+
leaseToken: Buffer;
|
|
100
|
+
fencingToken: FencingToken;
|
|
101
|
+
version: number;
|
|
102
|
+
parentFlowId?: string;
|
|
103
|
+
rootFlowId?: string;
|
|
104
|
+
correlationId?: string;
|
|
105
|
+
maxActiveMs?: number;
|
|
106
|
+
error?: unknown;
|
|
107
|
+
failureReason?: string;
|
|
108
|
+
valueRefs?: Record<string, unknown>;
|
|
109
|
+
values?: Record<string, unknown>;
|
|
110
|
+
valueSizes?: Record<string, unknown>;
|
|
111
|
+
valueOmitted?: Record<string, unknown>;
|
|
112
|
+
valueMissing?: Record<string, unknown>;
|
|
113
|
+
attributes?: Record<string, unknown>;
|
|
114
|
+
stateMeta?: Record<string, unknown>;
|
|
115
|
+
indexedStateMeta?: string;
|
|
116
|
+
raw?: unknown;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
type Outcome = TransitionOutcome | CompleteOutcome | RetryOutcome | FailOutcome;
|
|
120
|
+
interface NamedValueMutation {
|
|
121
|
+
values?: Record<string, unknown>;
|
|
122
|
+
valueRefs?: Record<string, string>;
|
|
123
|
+
dropValues?: string[];
|
|
124
|
+
overrideValues?: string[];
|
|
125
|
+
attributesMerge?: Record<string, CommandArgument>;
|
|
126
|
+
attributesDelete?: string[];
|
|
127
|
+
stateMeta?: StateMeta;
|
|
128
|
+
}
|
|
129
|
+
interface TransitionOutcome extends NamedValueMutation {
|
|
130
|
+
readonly kind: "transition";
|
|
131
|
+
readonly toState: string;
|
|
132
|
+
readonly payload?: unknown;
|
|
133
|
+
readonly priority?: number;
|
|
134
|
+
readonly runAtMs?: number;
|
|
135
|
+
}
|
|
136
|
+
interface CompleteOutcome extends NamedValueMutation {
|
|
137
|
+
readonly kind: "complete";
|
|
138
|
+
readonly payload?: unknown;
|
|
139
|
+
readonly result?: unknown;
|
|
140
|
+
readonly ttlMs?: number;
|
|
141
|
+
}
|
|
142
|
+
interface RetryOutcome extends NamedValueMutation {
|
|
143
|
+
readonly kind: "retry";
|
|
144
|
+
readonly error?: unknown;
|
|
145
|
+
readonly payload?: unknown;
|
|
146
|
+
readonly runAtMs?: number;
|
|
147
|
+
}
|
|
148
|
+
interface FailOutcome extends NamedValueMutation {
|
|
149
|
+
readonly kind: "fail";
|
|
150
|
+
readonly error?: unknown;
|
|
151
|
+
readonly payload?: unknown;
|
|
152
|
+
readonly ttlMs?: number;
|
|
153
|
+
}
|
|
154
|
+
declare function transition(toState: string, options?: Omit<TransitionOutcome, "kind" | "toState">): TransitionOutcome;
|
|
155
|
+
declare function complete(options?: Omit<CompleteOutcome, "kind">): CompleteOutcome;
|
|
156
|
+
declare function retry(options?: Omit<RetryOutcome, "kind">): RetryOutcome;
|
|
157
|
+
declare function fail(options?: Omit<FailOutcome, "kind">): FailOutcome;
|
|
158
|
+
declare function isOutcome(value: unknown): value is Outcome;
|
|
159
|
+
|
|
160
|
+
export { type ClaimedItem as C, type FencingToken as F, type KeyInfo as K, type MaxActiveMs as M, type NamedValueMutation as N, type Outcome as O, type RateLimitResult as R, type StateMeta as S, type TransitionOutcome as T, type FlowRecord as a, type FetchOrComputeResult as b, type ChildSpec as c, type FencedItem as d, type CreateItem as e, type CompleteOutcome as f, type FailOutcome as g, type FetchOrComputeComputeResult as h, type FetchOrComputeFencedResult as i, type FetchOrComputeHitResult as j, type FlowMaxActiveFailure as k, type RetryOutcome as l, type StateMetaValue as m, complete as n, fail as o, isOutcome as p, retry as r, transition as t };
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# Agent framework persistence
|
|
2
|
+
|
|
3
|
+
FerricStore's TypeScript package has optional adapters for LangGraph.js and the
|
|
4
|
+
OpenAI Agents SDK. They live in separate package entry points, so the base SDK
|
|
5
|
+
does not load either framework.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
For LangGraph.js:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install @ferricstore/ferricstore @langchain/langgraph @langchain/core
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
For the OpenAI Agents SDK:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @ferricstore/ferricstore @openai/agents
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Both adapters accept the normal `FerricStoreClient`. Their serialization is
|
|
22
|
+
independent of the client's configured codec.
|
|
23
|
+
|
|
24
|
+
## LangGraph.js checkpoints
|
|
25
|
+
|
|
26
|
+
`FerricStoreSaver` implements LangGraph's `BaseCheckpointSaver` contract:
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
import { Annotation, END, START, StateGraph } from "@langchain/langgraph";
|
|
30
|
+
import { FerricStoreClient } from "@ferricstore/ferricstore";
|
|
31
|
+
import { FerricStoreSaver } from "@ferricstore/ferricstore/langgraph";
|
|
32
|
+
|
|
33
|
+
const client = await FerricStoreClient.fromUrl("ferric://127.0.0.1:6388");
|
|
34
|
+
const saver = new FerricStoreSaver(client);
|
|
35
|
+
|
|
36
|
+
const State = Annotation.Root({ count: Annotation<number>() });
|
|
37
|
+
const graph = new StateGraph(State)
|
|
38
|
+
.addNode("increment", ({ count }) => ({ count: count + 1 }))
|
|
39
|
+
.addEdge(START, "increment")
|
|
40
|
+
.addEdge("increment", END)
|
|
41
|
+
.compile({ checkpointer: saver });
|
|
42
|
+
|
|
43
|
+
await graph.invoke(
|
|
44
|
+
{ count: 0 },
|
|
45
|
+
{ configurable: { thread_id: "agent-42" } }
|
|
46
|
+
);
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The saver supports named checkpoint namespaces, latest and exact reads,
|
|
50
|
+
ordered and filtered listing, parent chains, pending writes, retry-safe write
|
|
51
|
+
indexes, global listing, and complete thread deletion. It uses LangGraph's
|
|
52
|
+
serializer, so framework-specific values round-trip correctly.
|
|
53
|
+
|
|
54
|
+
Checkpoint mutations are serialized per thread with renewable,
|
|
55
|
+
ownership-checked locks. Indexes are published before the final checkpoint
|
|
56
|
+
record; readers validate each record and skip incomplete entries. This makes a
|
|
57
|
+
process failure during publication invisible and a retry safe.
|
|
58
|
+
|
|
59
|
+
## LangGraph.js long-term memory
|
|
60
|
+
|
|
61
|
+
`FerricStoreStore` implements `BaseStore`:
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import { FerricStoreStore } from "@ferricstore/ferricstore/langgraph";
|
|
65
|
+
|
|
66
|
+
const store = new FerricStoreStore(client);
|
|
67
|
+
|
|
68
|
+
await store.put(["users", "u-42"], "preferences", {
|
|
69
|
+
language: "en",
|
|
70
|
+
notifications: true
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const memories = await store.search(["users", "u-42"], {
|
|
74
|
+
filter: { notifications: true }
|
|
75
|
+
});
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
It supports hierarchical namespaces, atomic per-item mutation ordering,
|
|
79
|
+
batched operations, exact and comparison filters (`$eq`, `$ne`, `$gt`, `$gte`,
|
|
80
|
+
`$lt`, `$lte`, `$in`, `$nin`), ordered pagination, namespace listing, updates,
|
|
81
|
+
and deletion.
|
|
82
|
+
Semantic `query` search currently throws a clear error because no vector index
|
|
83
|
+
is configured; it never silently returns unranked data.
|
|
84
|
+
|
|
85
|
+
## Run LangGraph inside FerricFlow
|
|
86
|
+
|
|
87
|
+
The checkpointer makes graph steps resumable. `LangGraphFlow` adds the durable
|
|
88
|
+
outer lifecycle: leases and fencing, retries, scheduled work, signals,
|
|
89
|
+
approvals, workflow history, and terminal state.
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
import { LangGraphFlow } from "@ferricstore/ferricstore/langgraph";
|
|
93
|
+
|
|
94
|
+
const agentFlow = new LangGraphFlow(graph, {
|
|
95
|
+
interruptState: "waiting_for_approval"
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
workflow.state("running", agentFlow.handler.bind(agentFlow));
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
By default, the bridge derives a stable LangGraph `thread_id` from the Flow
|
|
102
|
+
type, partition, and ID. It sends the Flow payload on the first invocation and
|
|
103
|
+
uses `null` input when a checkpoint already exists. LangGraph runtime context
|
|
104
|
+
includes the active `WorkflowContext`. Completed graphs become `complete()`
|
|
105
|
+
outcomes; interrupts can transition to a chosen Flow state or use a custom
|
|
106
|
+
outcome mapper. Call `resume(flowContext, value)` from a handler to send a
|
|
107
|
+
LangGraph `Command({ resume: value })`.
|
|
108
|
+
|
|
109
|
+
The graph checkpointer and FerricFlow solve different layers and are intended
|
|
110
|
+
to be used together:
|
|
111
|
+
|
|
112
|
+
```text
|
|
113
|
+
FerricFlow durable run lifecycle
|
|
114
|
+
↓
|
|
115
|
+
LangGraphFlow invocation bridge
|
|
116
|
+
↓
|
|
117
|
+
LangGraph graph + FerricStoreSaver
|
|
118
|
+
↓
|
|
119
|
+
FerricStore
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## OpenAI Agents SDK Session
|
|
123
|
+
|
|
124
|
+
`FerricStoreSession` implements the base `Session` contract plus the optional
|
|
125
|
+
history rewrite and atomic transaction capabilities used by the current
|
|
126
|
+
OpenAI Agents SDK:
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
import { Agent, run } from "@openai/agents";
|
|
130
|
+
import { FerricStoreSession } from "@ferricstore/ferricstore/openai-agents";
|
|
131
|
+
|
|
132
|
+
const session = new FerricStoreSession(client, {
|
|
133
|
+
sessionId: "customer-42"
|
|
134
|
+
});
|
|
135
|
+
const agent = new Agent({ name: "Support", instructions: "Be helpful." });
|
|
136
|
+
|
|
137
|
+
await run(agent, "Where is my order?", { session });
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
The adapter provides chronological reads with tail limits, append, pop,
|
|
141
|
+
clear, compaction replacement, function-call history rewrites, and atomic
|
|
142
|
+
`append_items` / `replace_suffix` transactions. A transaction stores its
|
|
143
|
+
operation ID and history mutation in one atomic record. Repeating the same
|
|
144
|
+
operation is a no-op; reusing its ID for different content or replacing a
|
|
145
|
+
non-matching suffix fails without changing history.
|
|
146
|
+
|
|
147
|
+
All session mutations use a renewable FerricStore lock. Reads see either the
|
|
148
|
+
old or new complete session record, never a partial history. `clearSession()`
|
|
149
|
+
also clears transaction receipts. Session persistence stores conversation
|
|
150
|
+
history; put the overall agent run in FerricFlow when it also needs durable
|
|
151
|
+
leases, retries, timers, signals, or multi-step business state.
|
|
152
|
+
|
|
153
|
+
## Operational options
|
|
154
|
+
|
|
155
|
+
All three adapters accept `keyPrefix`, `lockTtlMs`, `lockWaitMs`, and
|
|
156
|
+
`lockRetryMs`. The saver and store also accept `scanCount`; the saver accepts a
|
|
157
|
+
custom LangGraph serializer. Defaults are suitable for ordinary use. Give
|
|
158
|
+
different applications or environments different prefixes when they share a
|
|
159
|
+
FerricStore deployment.
|
|
@@ -5,18 +5,18 @@
|
|
|
5
5
|
--dark-hl-1: #D4D4D4;
|
|
6
6
|
--light-hl-2: #A31515;
|
|
7
7
|
--dark-hl-2: #CE9178;
|
|
8
|
-
--light-hl-3: #
|
|
9
|
-
--dark-hl-3: #
|
|
10
|
-
--light-hl-4: #
|
|
11
|
-
--dark-hl-4: #
|
|
12
|
-
--light-hl-5: #
|
|
13
|
-
--dark-hl-5: #
|
|
14
|
-
--light-hl-6: #
|
|
15
|
-
--dark-hl-6: #
|
|
16
|
-
--light-hl-7: #
|
|
17
|
-
--dark-hl-7: #
|
|
18
|
-
--light-hl-8: #
|
|
19
|
-
--dark-hl-8: #
|
|
8
|
+
--light-hl-3: #008000;
|
|
9
|
+
--dark-hl-3: #6A9955;
|
|
10
|
+
--light-hl-4: #AF00DB;
|
|
11
|
+
--dark-hl-4: #C586C0;
|
|
12
|
+
--light-hl-5: #001080;
|
|
13
|
+
--dark-hl-5: #9CDCFE;
|
|
14
|
+
--light-hl-6: #0000FF;
|
|
15
|
+
--dark-hl-6: #569CD6;
|
|
16
|
+
--light-hl-7: #0070C1;
|
|
17
|
+
--dark-hl-7: #4FC1FF;
|
|
18
|
+
--light-hl-8: #EE0000;
|
|
19
|
+
--dark-hl-8: #D7BA7D;
|
|
20
20
|
--light-hl-9: #098658;
|
|
21
21
|
--dark-hl-9: #B5CEA8;
|
|
22
22
|
--light-hl-10: #267F99;
|