@opendatalabs/vana-sdk 3.23.0 → 4.0.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 +57 -0
- package/dist/auth/errors.cjs +1 -1
- package/dist/auth/web3-signed.cjs +1 -1
- package/dist/crypto/envelope/job-stream.test.d.ts +1 -0
- package/dist/crypto/envelope/job.cjs +593 -38
- package/dist/crypto/envelope/job.cjs.map +1 -1
- package/dist/crypto/envelope/job.d.ts +44 -27
- package/dist/crypto/envelope/job.js +589 -37
- package/dist/crypto/envelope/job.js.map +1 -1
- package/dist/direct/access-request-client.cjs +1 -1
- package/dist/direct/connect-flow.cjs +43 -1
- package/dist/direct/connect-flow.cjs.map +1 -1
- package/dist/direct/connect-flow.d.ts +12 -0
- package/dist/direct/connect-flow.js +43 -1
- package/dist/direct/connect-flow.js.map +1 -1
- package/dist/direct/controller.cjs +1 -1
- package/dist/direct/errors.cjs +1 -1
- package/dist/direct/escrow-payment.cjs.map +1 -1
- package/dist/direct/escrow-payment.d.ts +2 -2
- package/dist/direct/escrow-payment.js.map +1 -1
- package/dist/direct/personal-server-read.cjs +2 -2
- package/dist/direct/use-direct-vana-connect.cjs +2 -1
- package/dist/direct/use-direct-vana-connect.cjs.map +1 -1
- package/dist/direct/use-direct-vana-connect.d.ts +3 -1
- package/dist/direct/use-direct-vana-connect.js +2 -1
- package/dist/direct/use-direct-vana-connect.js.map +1 -1
- package/dist/error-entry-points.test.d.ts +1 -0
- package/dist/errors.cjs +7 -0
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.ts +10 -0
- package/dist/errors.js +6 -0
- package/dist/errors.js.map +1 -1
- package/dist/index.browser.d.ts +4 -4
- package/dist/index.browser.js +882 -518
- package/dist/index.browser.js.map +4 -4
- package/dist/index.node.cjs +1035 -705
- package/dist/index.node.cjs.map +4 -4
- package/dist/index.node.d.ts +4 -4
- package/dist/index.node.js +989 -567
- package/dist/index.node.js.map +4 -4
- package/dist/protocol/data-point-deletion.cjs +1 -1
- package/dist/protocol/derivative-questions.cjs +1 -1
- package/dist/protocol/derivative-status.cjs +1 -1
- package/dist/protocol/eip712.cjs +28 -2
- package/dist/protocol/eip712.cjs.map +1 -1
- package/dist/protocol/eip712.d.ts +70 -0
- package/dist/protocol/eip712.js +24 -1
- package/dist/protocol/eip712.js.map +1 -1
- package/dist/protocol/escrow.cjs +154 -2
- package/dist/protocol/escrow.cjs.map +1 -1
- package/dist/protocol/escrow.d.ts +145 -2
- package/dist/protocol/escrow.js +152 -1
- package/dist/protocol/escrow.js.map +1 -1
- package/dist/protocol/fixtures/moksha-identity.json +20 -0
- package/dist/protocol/gateway.cjs +1 -1
- package/dist/protocol/gateway.cjs.map +1 -1
- package/dist/protocol/gateway.d.ts +8 -38
- package/dist/protocol/gateway.js.map +1 -1
- package/dist/protocol/identity.cjs +7 -2
- package/dist/protocol/identity.cjs.map +1 -1
- package/dist/protocol/identity.js +7 -2
- package/dist/protocol/identity.js.map +1 -1
- package/dist/protocol/jobs-client.cjs +73 -21
- package/dist/protocol/jobs-client.cjs.map +1 -1
- package/dist/protocol/jobs-client.d.ts +7 -5
- package/dist/protocol/jobs-client.js +74 -21
- package/dist/protocol/jobs-client.js.map +1 -1
- package/dist/protocol/jobs.cjs +0 -3
- package/dist/protocol/jobs.cjs.map +1 -1
- package/dist/protocol/jobs.d.ts +21 -13
- package/dist/protocol/jobs.js +0 -2
- package/dist/protocol/jobs.js.map +1 -1
- package/dist/protocol/lineage.cjs +1 -1
- package/dist/protocol/personal-server-data.cjs +1 -1
- package/dist/protocol/personal-server-write.cjs +1 -1
- package/dist/protocol/write-request.cjs +1 -1
- package/dist/protocol/write-signer.cjs +1 -1
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js.map +1 -1
- package/dist/session-relay/client.cjs +1 -1
- package/dist/session-relay/errors.cjs +1 -1
- package/dist/session-relay/index.cjs +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/protocol/jobs-client.ts"],"sourcesContent":["/**\n * Builder-side client for encrypted Gateway jobs.\n *\n * @remarks\n * This Node-only client fetches an owner's sealed enclave identity, signs and\n * seals raw-read work, submits it to the Gateway, polls builder-visible state,\n * and decrypts the bound result with the builder private key.\n *\n * @category Protocol\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport {\n isAddress,\n isAddressEqual,\n isHex,\n type Address,\n type Hex,\n type LocalAccount,\n} from \"viem\";\nimport { privateKeyToAccount, publicKeyToAddress } from \"viem/accounts\";\nimport {\n buildWeb3SignedHeader,\n computeBodyHash,\n} from \"../auth/web3-signed-builder\";\nimport type { ECIESProvider } from \"../crypto/ecies/interface\";\nimport { NodeECIESUint8Provider } from \"../crypto/ecies/node\";\nimport {\n canonicalJobRequestBytes,\n openJobResult,\n sealJobRequest,\n} from \"../crypto/envelope/job\";\nimport {\n BuilderUnknownError,\n GrantInvalidError,\n JobIdTakenError,\n JobNotFoundError,\n JobRejectedError,\n JobRequestTooLargeError,\n JobTimeoutError,\n JobTransportError,\n OwnerNotReadyError,\n type JobGatewayErrorCode,\n type JobsClientError,\n} from \"../errors\";\nimport { userPsId, type IdentityResponse } from \"./identity\";\nimport {\n CLAIM_POLL_FLOOR_MS,\n DEFAULT_JOB_DEADLINE_SECONDS,\n JOB_OPERATIONS,\n JOB_PROTOCOL_VERSION,\n JOB_STATES,\n MAX_INLINE_RESULT_BYTES,\n MAX_JOB_DEADLINE_SECONDS,\n MAX_WAIT_SECONDS,\n type JobRequest,\n type JobResult,\n type JobOperation,\n type JobState,\n type JobStatus,\n type JobSubmission,\n} from \"./jobs\";\nimport { resolveWriteSigner, type WriteSignerSource } from \"./write-signer\";\n\nconst IDENTITY_PATH = \"/v1/identity\";\nconst JOBS_PATH = \"/v1/jobs\";\nconst JOB_EXECUTE_PATH = \"/v1/jobs/execute\";\nconst AUTHORIZATION_HEADER = \"Authorization\";\nconst CONTENT_TYPE_HEADER = \"Content-Type\";\nconst JSON_CONTENT_TYPE = \"application/json\";\nconst GET_METHOD = \"GET\";\nconst POST_METHOD = \"POST\";\nconst RAW_READ_OPERATION = \"raw_read\";\nconst SEALED_IDENTITY_STATE = \"sealed\";\nconst HTTP_OK = 200;\nconst HTTP_ACCEPTED = 202;\nconst HTTP_UNAUTHORIZED = 401;\nconst HTTP_FORBIDDEN = 403;\nconst HTTP_NOT_FOUND = 404;\nconst HTTP_CONFLICT = 409;\nconst HTTP_PAYLOAD_TOO_LARGE = 413;\nconst MILLISECONDS_PER_SECOND = 1_000;\nconst DEFAULT_JOB_TIMEOUT_MS = 120_000;\nconst DEFAULT_JOB_POLL_MS = CLAIM_POLL_FLOOR_MS;\nconst TERMINAL_JOB_STATES: ReadonlySet<JobState> = new Set([\n \"completed\",\n \"failed\",\n \"expired\",\n \"cancelled\",\n]);\n\n/** A viem local account that exposes the public key required by job requests. */\nexport type JobsBuilderAccount = Extract<LocalAccount, WriteSignerSource>;\n\n/** Configuration captured by {@link createJobsClient}. */\nexport interface JobsClientOptions {\n /** Gateway base URL; its origin is the Web3Signed audience. */\n gatewayUrl: string;\n /** Vana chain ID used by the owner identity lookup. */\n chainId: number;\n /** Raw builder private key, used for signing and result decryption. */\n builderPrivateKey?: Hex;\n /**\n * viem local account used for signing. Its public key supports submission,\n * but without `builderPrivateKey` this client cannot decrypt results:\n * `openResult` and `readRaw` reject while submit/status/wait remain usable.\n */\n builderAccount?: JobsBuilderAccount;\n /** HTTP implementation; defaults to `globalThis.fetch`. */\n fetch?: typeof fetch;\n /** ECIES implementation; defaults to the Node provider. */\n ecies?: ECIESProvider;\n /** Clock used when constructing a job deadline. */\n now?: () => Date;\n}\n\n/** Parameters for one encrypted raw-read submission. */\nexport interface SubmitRawReadParams {\n /** Owner whose enclave executes the read. */\n owner: Address;\n /** Owner-issued builder grant. */\n grantId: Hex;\n /** Data scope to read. */\n scope: string;\n /** Exact record version to require, or `null` for an unpinned read. */\n pinnedVersion?: string | null;\n /** Requested deadline offset, clamped to the protocol maximum. */\n deadlineSeconds?: number;\n /** Gateway inline-wait seconds, clamped to `0..MAX_WAIT_SECONDS`. */\n wait?: number;\n}\n\n/** Result of submitting an encrypted raw-read job. */\nexport interface SubmitRawReadResult {\n /** Client-generated job UUID. */\n jobId: string;\n /** Current Gateway state. */\n state: JobState;\n /** Full status when the Gateway returned an inline 200 response. */\n job?: JobStatus;\n}\n\n/** Polling controls for {@link JobsClient.waitForJob}. */\nexport interface WaitForJobOptions {\n /** Total polling budget in milliseconds. */\n timeoutMs?: number;\n /** Delay between reads; values below `CLAIM_POLL_FLOOR_MS` are raised. */\n pollMs?: number;\n}\n\n/** Expected plaintext bindings for {@link JobsClient.openResult}. */\nexport interface OpenJobResultOptions {\n /** Bind the plaintext to this job id. */\n expect: {\n jobId: string;\n scope?: string;\n version?: string | null;\n };\n}\n\n/** Parameters for the submit, wait, and decrypt convenience flow. */\nexport interface ReadRawParams extends SubmitRawReadParams, WaitForJobOptions {}\n\n/** Builder operations exposed by {@link createJobsClient}. */\nexport interface JobsClient {\n /**\n * Encrypt and submit a raw-read job.\n *\n * @param params - Owner, grant, scope, deadline, and inline-wait controls.\n * @returns The job id and current state, plus inline status on HTTP 200.\n * @throws {OwnerNotReadyError} When the owner identity is not sealed.\n * @throws {JobsClientError} When the Gateway or transport rejects the call.\n */\n submitRawRead(params: SubmitRawReadParams): Promise<SubmitRawReadResult>;\n\n /**\n * Read one builder-visible job status.\n *\n * @param jobId - Client-generated job UUID.\n * @returns The current job status.\n * @throws {JobNotFoundError} When the job is absent or belongs to another builder.\n * @throws {JobsClientError} For other Gateway or transport failures.\n */\n getJob(jobId: string): Promise<JobStatus>;\n\n /**\n * Poll until a job completes, fails, expires, or is cancelled.\n *\n * @param jobId - Job UUID to poll.\n * @param options - Timeout and poll cadence.\n * @returns The terminal job status.\n * @throws {JobTimeoutError} When the caller or job deadline is exhausted.\n */\n waitForJob(jobId: string, options?: WaitForJobOptions): Promise<JobStatus>;\n\n /**\n * Decrypt and validate an inline job result.\n *\n * @param job - Terminal job status containing `resultCiphertext`.\n * @param options - Expected plaintext job, scope, and version bindings.\n * @returns The validated plaintext job result.\n * @remarks Decryption requires `builderPrivateKey`; `builderAccount` alone\n * only supports submission, status reads, and waiting.\n * @throws {JobRejectedError} When ciphertext or a raw private key is unavailable.\n * @throws {JobEnvelopeError} When decrypted protocol fields or bindings differ.\n */\n openResult(job: JobStatus, options: OpenJobResultOptions): Promise<JobResult>;\n\n /**\n * Submit, wait for, and decrypt one raw read.\n *\n * @param params - Raw-read request plus polling controls.\n * @returns The decrypted, binding-checked job result.\n * @remarks This convenience flow decrypts the result and therefore requires\n * `builderPrivateKey`; a client configured only with `builderAccount`\n * cannot call `readRaw`.\n * @throws {JobsClientError} When submission, polling, or decryption setup fails.\n * @throws {JobEnvelopeError} When decrypted result bindings differ.\n *\n * @example\n * ```typescript\n * const result = await client.readRaw({\n * owner,\n * grantId,\n * scope: \"profile.email\",\n * wait: 25,\n * });\n * ```\n */\n readRaw(params: ReadRawParams): Promise<JobResult>;\n}\n\ninterface ResolvedBuilder {\n address: Address;\n publicKey: Hex;\n signMessage: ReturnType<typeof resolveWriteSigner>[\"signMessage\"];\n}\n\ninterface GatewayErrorBody {\n code: JobGatewayErrorCode | null;\n message: string;\n reason?: string;\n details?: Record<string, unknown>;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction normalizeGateway(gatewayUrl: string): {\n baseUrl: string;\n audience: string;\n} {\n try {\n const url = new URL(gatewayUrl);\n if (url.pathname !== \"/\" || url.search !== \"\" || url.hash !== \"\") {\n throw new JobRejectedError(\n \"gatewayUrl must be an origin without a path, query, or fragment\",\n );\n }\n return {\n baseUrl: url.origin,\n audience: url.origin,\n };\n } catch (error) {\n if (error instanceof JobRejectedError) throw error;\n throw new JobRejectedError(\n \"gatewayUrl must be an absolute URL\",\n undefined,\n null,\n {\n cause: error instanceof Error ? error.message : String(error),\n },\n );\n }\n}\n\nfunction resolveFetch(fetchFn?: typeof fetch): typeof fetch {\n const resolved = fetchFn ?? globalThis.fetch;\n if (resolved === undefined) {\n throw new JobRejectedError(\"No fetch implementation available\");\n }\n return resolved;\n}\n\nfunction sourcePublicKey(source: WriteSignerSource): Hex | undefined {\n if (isRecord(source) && typeof source[\"publicKey\"] === \"string\") {\n return source[\"publicKey\"] as Hex;\n }\n return undefined;\n}\n\nfunction resolveBuilder(options: JobsClientOptions): {\n builder: ResolvedBuilder;\n privateKey?: Hex;\n} {\n if (options.builderPrivateKey && options.builderAccount) {\n throw new JobRejectedError(\n \"Provide builderPrivateKey or builderAccount, not both\",\n );\n }\n let source: WriteSignerSource | undefined;\n try {\n source = options.builderPrivateKey\n ? privateKeyToAccount(options.builderPrivateKey)\n : options.builderAccount;\n } catch (error) {\n throw new JobRejectedError(\"Invalid builder private key\", undefined, null, {\n cause: error instanceof Error ? error.message : String(error),\n });\n }\n if (!source) {\n throw new JobRejectedError(\n \"builderPrivateKey or builderAccount is required\",\n );\n }\n let signer: ReturnType<typeof resolveWriteSigner>;\n try {\n signer = resolveWriteSigner(source);\n } catch (error) {\n throw new JobRejectedError(\"Invalid builder signer\", undefined, null, {\n cause: error instanceof Error ? error.message : String(error),\n });\n }\n const publicKey = sourcePublicKey(source);\n if (!signer.address || !publicKey) {\n throw new JobRejectedError(\n \"Builder signer must expose both address and publicKey\",\n );\n }\n return {\n builder: {\n address: signer.address,\n publicKey,\n signMessage: signer.signMessage,\n },\n ...(options.builderPrivateKey\n ? { privateKey: options.builderPrivateKey }\n : {}),\n };\n}\n\nfunction clampInteger(\n value: number | undefined,\n fallback: number,\n minimum: number,\n maximum: number,\n field: string,\n): number {\n const resolved = value ?? fallback;\n if (!Number.isFinite(resolved)) {\n throw new JobRejectedError(`${field} must be a finite number`);\n }\n return Math.min(maximum, Math.max(minimum, Math.trunc(resolved)));\n}\n\nasync function responseBody(response: Response): Promise<unknown> {\n try {\n return await response.json();\n } catch {\n return null;\n }\n}\n\nfunction gatewayErrorBody(body: unknown, fallback: string): GatewayErrorBody {\n if (!isRecord(body)) return { code: null, message: fallback };\n const code = typeof body[\"code\"] === \"string\" ? body[\"code\"] : null;\n const message =\n typeof body[\"error\"] === \"string\"\n ? body[\"error\"]\n : typeof body[\"message\"] === \"string\"\n ? body[\"message\"]\n : fallback;\n const reason =\n typeof body[\"reason\"] === \"string\" ? body[\"reason\"] : undefined;\n const details = isRecord(body[\"details\"]) ? body[\"details\"] : undefined;\n return {\n code,\n message,\n ...(reason ? { reason } : {}),\n ...(details ? { details } : {}),\n };\n}\n\nasync function rejectedResponse(\n response: Response,\n fallback: string,\n): Promise<JobsClientError> {\n const parsed = gatewayErrorBody(await responseBody(response), fallback);\n const details =\n response.status === HTTP_UNAUTHORIZED && parsed.reason\n ? { ...parsed.details, reason: parsed.reason }\n : parsed.details;\n const message =\n response.status === HTTP_UNAUTHORIZED && parsed.reason\n ? `${parsed.message}: ${parsed.reason}`\n : parsed.message;\n if (response.status === HTTP_FORBIDDEN) {\n if (parsed.code === \"BUILDER_UNKNOWN\") {\n return new BuilderUnknownError(parsed.message, details);\n }\n if (parsed.code === \"GRANT_INVALID\") {\n return new GrantInvalidError(parsed.message, details);\n }\n if (parsed.code === \"OWNER_NOT_READY\") {\n return new OwnerNotReadyError(parsed.message, details);\n }\n }\n if (response.status === HTTP_NOT_FOUND) {\n return new JobNotFoundError(parsed.message, details);\n }\n if (response.status === HTTP_CONFLICT && parsed.code === \"JOB_ID_TAKEN\") {\n return new JobIdTakenError(parsed.message, details);\n }\n if (response.status === HTTP_PAYLOAD_TOO_LARGE) {\n return new JobRequestTooLargeError(parsed.message, parsed.code, details);\n }\n return new JobRejectedError(message, response.status, parsed.code, details);\n}\n\nfunction requireStatus(value: unknown, status: number): JobStatus {\n if (\n !isRecord(value) ||\n !isNonEmptyString(value[\"jobId\"]) ||\n typeof value[\"state\"] !== \"string\" ||\n !JOB_STATES.includes(value[\"state\"] as JobState) ||\n !isNonEmptyString(value[\"owner\"]) ||\n !isNonEmptyString(value[\"grantId\"]) ||\n !isNonEmptyString(value[\"scope\"]) ||\n !isNonEmptyString(value[\"operation\"]) ||\n !JOB_OPERATIONS.includes(value[\"operation\"] as JobOperation)\n ) {\n throw new JobRejectedError(\n \"Gateway response did not contain a job status\",\n status,\n );\n }\n return value as unknown as JobStatus;\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === \"string\" && value.length > 0;\n}\n\nfunction hasMatchingIdentityKey(value: Record<string, unknown>): boolean {\n const address = value[\"address\"];\n const publicKey = value[\"publicKey\"];\n if (\n typeof address !== \"string\" ||\n !isAddress(address) ||\n typeof publicKey !== \"string\" ||\n !isHex(publicKey)\n ) {\n return false;\n }\n try {\n return (\n publicKeyToAddress(publicKey).toLowerCase() === address.toLowerCase()\n );\n } catch {\n return false;\n }\n}\n\nfunction hasExpectedIdentity(\n value: Record<string, unknown>,\n owner: Address,\n chainId: number,\n): boolean {\n const identityOwner = value[\"ownerAddress\"];\n const identityChainId = value[\"chainId\"];\n const identityUserPsId = value[\"userPsId\"];\n if (\n !hasMatchingIdentityKey(value) ||\n typeof identityOwner !== \"string\" ||\n !isAddress(identityOwner) ||\n typeof identityChainId !== \"number\" ||\n typeof identityUserPsId !== \"string\" ||\n !isHex(identityUserPsId)\n ) {\n return false;\n }\n return (\n isAddressEqual(identityOwner, owner) &&\n identityChainId === chainId &&\n identityUserPsId.toLowerCase() === userPsId(chainId, owner).toLowerCase()\n );\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction requireFiniteDuration(value: number, field: string): number {\n if (!Number.isFinite(value)) {\n throw new JobRejectedError(`${field} must be a finite number`);\n }\n return Math.max(0, value);\n}\n\n/**\n * Create a reusable Node builder client for encrypted Gateway jobs.\n *\n * @param options - Gateway, chain, builder signer, and injectable adapters.\n * @returns A client bound to the configured builder and Gateway.\n * @throws {JobRejectedError} When configuration or signer shape is invalid.\n *\n * @remarks\n * This client is Node-only. `builderPrivateKey` supports the complete flow;\n * `builderAccount` alone supports submit/status/wait but cannot decrypt, so\n * `openResult` and `readRaw` require the raw private key.\n *\n * @example\n * ```typescript\n * const client = createJobsClient({\n * gatewayUrl: \"https://gateway.example.com\",\n * chainId: 14800,\n * builderPrivateKey: process.env.BUILDER_PRIVATE_KEY as Hex,\n * });\n * const result = await client.readRaw({ owner, grantId, scope: \"profile\" });\n * ```\n */\nexport function createJobsClient(options: JobsClientOptions): JobsClient {\n const { baseUrl, audience } = normalizeGateway(options.gatewayUrl);\n const fetchFn = resolveFetch(options.fetch);\n const { builder, privateKey } = resolveBuilder(options);\n const ecies = options.ecies ?? new NodeECIESUint8Provider();\n const now = options.now ?? (() => new Date());\n\n async function request(\n url: string,\n init: RequestInit,\n description: string,\n ): Promise<Response> {\n try {\n return await fetchFn(url, init);\n } catch (error) {\n throw new JobTransportError(\n `${description} failed: ${error instanceof Error ? error.message : String(error)}`,\n error,\n );\n }\n }\n\n async function getIdentity(\n owner: Address,\n ): Promise<IdentityResponse[\"identity\"]> {\n const query = new URLSearchParams({\n owner,\n chainId: String(options.chainId),\n });\n const response = await request(\n `${baseUrl}${IDENTITY_PATH}?${query.toString()}`,\n { method: GET_METHOD },\n \"Owner identity lookup\",\n );\n if (response.status === HTTP_NOT_FOUND) {\n const parsed = gatewayErrorBody(\n await responseBody(response),\n \"Owner identity not found\",\n );\n throw new OwnerNotReadyError(parsed.message, parsed.details);\n }\n if (!response.ok) {\n throw await rejectedResponse(response, \"Owner identity lookup rejected\");\n }\n const body = await responseBody(response);\n if (\n !isRecord(body) ||\n body[\"state\"] !== SEALED_IDENTITY_STATE ||\n !isRecord(body[\"identity\"])\n ) {\n throw new OwnerNotReadyError(\"Owner identity is not sealed\", {\n state: isRecord(body) ? body[\"state\"] : undefined,\n });\n }\n if (!hasExpectedIdentity(body[\"identity\"], owner, options.chainId)) {\n throw new JobRejectedError(\n \"Gateway returned a malformed or mismatched owner identity\",\n response.status,\n );\n }\n // Full TDX/KMS evidence verification awaits provisioned trust anchors and is intentionally outside this driver-matching client.\n return body[\"identity\"] as unknown as IdentityResponse[\"identity\"];\n }\n\n const client: JobsClient = {\n async submitRawRead(params) {\n const identity = await getIdentity(params.owner);\n const deadlineSeconds = clampInteger(\n params.deadlineSeconds,\n DEFAULT_JOB_DEADLINE_SECONDS,\n 1,\n MAX_JOB_DEADLINE_SECONDS,\n \"deadlineSeconds\",\n );\n const wait = clampInteger(params.wait, 0, 0, MAX_WAIT_SECONDS, \"wait\");\n const jobId = randomUUID();\n const nowMs = now().getTime();\n if (!Number.isFinite(nowMs)) {\n throw new JobRejectedError(\"now must return a valid Date\");\n }\n const deadline = new Date(\n nowMs + deadlineSeconds * MILLISECONDS_PER_SECOND,\n ).toISOString();\n const requestBody: JobRequest = {\n v: JOB_PROTOCOL_VERSION,\n jobId,\n owner: params.owner,\n builder: builder.address,\n builderPublicKey: builder.publicKey,\n grantId: params.grantId,\n scope: params.scope,\n operation: RAW_READ_OPERATION,\n pinnedVersion: params.pinnedVersion ?? null,\n deadline,\n };\n const auth = await buildWeb3SignedHeader({\n signMessage: builder.signMessage,\n aud: audience,\n method: POST_METHOD,\n uri: JOB_EXECUTE_PATH,\n bodyHash: computeBodyHash(canonicalJobRequestBytes(requestBody)),\n nonce: randomUUID(),\n });\n const requestCiphertext = await sealJobRequest(\n { request: requestBody, auth },\n identity.publicKey,\n ecies,\n );\n const submission: JobSubmission = {\n owner: requestBody.owner,\n grantId: requestBody.grantId,\n scope: requestBody.scope,\n operation: requestBody.operation,\n idempotencyKey: randomUUID(),\n jobId,\n deadline,\n requestCiphertext,\n };\n const bodyBytes = new TextEncoder().encode(JSON.stringify(submission));\n const authorization = await buildWeb3SignedHeader({\n signMessage: builder.signMessage,\n aud: audience,\n method: POST_METHOD,\n uri: JOBS_PATH,\n body: bodyBytes,\n nonce: randomUUID(),\n });\n const response = await request(\n `${baseUrl}${JOBS_PATH}?wait=${wait}`,\n {\n method: POST_METHOD,\n headers: {\n [AUTHORIZATION_HEADER]: authorization,\n [CONTENT_TYPE_HEADER]: JSON_CONTENT_TYPE,\n },\n body: bodyBytes,\n },\n \"Job submission\",\n );\n if (!response.ok) {\n throw await rejectedResponse(response, \"Job submission rejected\");\n }\n const body = await responseBody(response);\n if (response.status === HTTP_OK && isRecord(body)) {\n const job = requireStatus(body[\"job\"], response.status);\n if (job.jobId !== jobId) {\n throw new JobRejectedError(\n \"Gateway returned a job id that does not match the submission\",\n response.status,\n );\n }\n return { jobId, state: job.state, job };\n }\n if (\n response.status === HTTP_ACCEPTED &&\n isRecord(body) &&\n body[\"jobId\"] === jobId &&\n typeof body[\"state\"] === \"string\" &&\n JOB_STATES.includes(body[\"state\"] as JobState)\n ) {\n return { jobId, state: body[\"state\"] as JobState };\n }\n throw new JobRejectedError(\n \"Gateway returned an undocumented job submission response\",\n response.status,\n );\n },\n\n async getJob(jobId) {\n const path = `${JOBS_PATH}/${encodeURIComponent(jobId)}`;\n const authorization = await buildWeb3SignedHeader({\n signMessage: builder.signMessage,\n aud: audience,\n method: GET_METHOD,\n uri: path,\n nonce: randomUUID(),\n });\n const response = await request(\n `${baseUrl}${path}`,\n {\n method: GET_METHOD,\n headers: { [AUTHORIZATION_HEADER]: authorization },\n },\n \"Job status read\",\n );\n if (!response.ok) {\n throw await rejectedResponse(response, \"Job status read rejected\");\n }\n const body = await responseBody(response);\n return requireStatus(\n isRecord(body) ? body[\"job\"] : undefined,\n response.status,\n );\n },\n\n async waitForJob(jobId, waitOptions = {}) {\n const timeoutMs = requireFiniteDuration(\n waitOptions.timeoutMs ?? DEFAULT_JOB_TIMEOUT_MS,\n \"timeoutMs\",\n );\n const pollMs = Math.max(\n CLAIM_POLL_FLOOR_MS,\n requireFiniteDuration(\n waitOptions.pollMs ?? DEFAULT_JOB_POLL_MS,\n \"pollMs\",\n ),\n );\n const startedAt = Date.now();\n const callerDeadline = startedAt + timeoutMs;\n let latest: JobStatus | undefined;\n for (;;) {\n if (Date.now() >= callerDeadline) {\n throw new JobTimeoutError(\n `Timed out waiting for terminal job ${jobId}`,\n {\n jobId,\n timeoutMs,\n state: latest?.state,\n },\n );\n }\n latest = await client.getJob(jobId);\n if (TERMINAL_JOB_STATES.has(latest.state)) return latest;\n const remaining = callerDeadline - Date.now();\n if (remaining <= 0) {\n throw new JobTimeoutError(\n `Timed out waiting for terminal job ${jobId}`,\n {\n jobId,\n timeoutMs,\n state: latest.state,\n },\n );\n }\n await sleep(Math.min(pollMs, remaining));\n }\n },\n\n async openResult(job, openOptions) {\n if (!job.resultCiphertext) {\n throw new JobRejectedError(\n \"Job status does not include inline resultCiphertext\",\n undefined,\n null,\n {\n jobId: job.jobId,\n state: job.state,\n resultHandle: job.resultHandle,\n maxInlineResultBytes: MAX_INLINE_RESULT_BYTES,\n },\n );\n }\n if (!privateKey) {\n throw new JobRejectedError(\n \"openResult requires createJobsClient({ builderPrivateKey })\",\n );\n }\n return openJobResult(\n job.resultCiphertext,\n privateKey,\n ecies,\n openOptions.expect,\n );\n },\n\n async readRaw(params) {\n const submitted = await client.submitRawRead(params);\n const job =\n submitted.job && TERMINAL_JOB_STATES.has(submitted.job.state)\n ? submitted.job\n : await client.waitForJob(submitted.jobId, {\n timeoutMs: params.timeoutMs,\n pollMs: params.pollMs,\n });\n return client.openResult(job, {\n expect: {\n jobId: submitted.jobId,\n scope: params.scope,\n ...(params.pinnedVersion !== undefined &&\n params.pinnedVersion !== null\n ? { version: params.pinnedVersion }\n : {}),\n },\n });\n },\n };\n\n return client;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/protocol/jobs-client.ts"],"sourcesContent":["/**\n * Builder-side client for encrypted Gateway jobs.\n *\n * @remarks\n * This Node-only client fetches an owner's sealed enclave identity, signs and\n * seals raw-read work, submits it to the Gateway, polls builder-visible state,\n * and decrypts the bound result with the builder private key.\n *\n * @category Protocol\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport { sha256 } from \"@noble/hashes/sha2\";\nimport {\n bytesToHex,\n isAddress,\n isAddressEqual,\n isHex,\n type Address,\n type Hex,\n type LocalAccount,\n} from \"viem\";\nimport { privateKeyToAccount, publicKeyToAddress } from \"viem/accounts\";\nimport {\n buildWeb3SignedHeader,\n computeBodyHash,\n} from \"../auth/web3-signed-builder\";\nimport type { ECIESProvider } from \"../crypto/ecies/interface\";\nimport { NodeECIESUint8Provider } from \"../crypto/ecies/node\";\nimport {\n canonicalJobRequestBytes,\n openJobResult,\n sealJobRequest,\n} from \"../crypto/envelope/job\";\nimport {\n BuilderUnknownError,\n GrantInvalidError,\n JobIdTakenError,\n JobNotFoundError,\n JobRejectedError,\n JobResultIntegrityError,\n JobRequestTooLargeError,\n JobTimeoutError,\n JobTransportError,\n OwnerNotReadyError,\n type JobGatewayErrorCode,\n type JobsClientError,\n} from \"../errors\";\nimport { userPsId, type IdentityResponse } from \"./identity\";\nimport {\n CLAIM_POLL_FLOOR_MS,\n DEFAULT_JOB_DEADLINE_SECONDS,\n JOB_OPERATIONS,\n JOB_PROTOCOL_VERSION,\n JOB_STATES,\n MAX_JOB_DEADLINE_SECONDS,\n MAX_WAIT_SECONDS,\n type JobRequest,\n type JobResult,\n type JobOperation,\n type JobState,\n type JobStatus,\n type JobSubmission,\n type ResultHandle,\n} from \"./jobs\";\nimport { resolveWriteSigner, type WriteSignerSource } from \"./write-signer\";\n\nconst IDENTITY_PATH = \"/v1/identity\";\nconst JOBS_PATH = \"/v1/jobs\";\nconst JOB_EXECUTE_PATH = \"/v1/jobs/execute\";\nconst AUTHORIZATION_HEADER = \"Authorization\";\nconst CONTENT_TYPE_HEADER = \"Content-Type\";\nconst JSON_CONTENT_TYPE = \"application/json\";\nconst GET_METHOD = \"GET\";\nconst POST_METHOD = \"POST\";\nconst RAW_READ_OPERATION = \"raw_read\";\nconst SEALED_IDENTITY_STATE = \"sealed\";\nconst HTTP_OK = 200;\nconst HTTP_ACCEPTED = 202;\nconst HTTP_UNAUTHORIZED = 401;\nconst HTTP_FORBIDDEN = 403;\nconst HTTP_NOT_FOUND = 404;\nconst HTTP_CONFLICT = 409;\nconst HTTP_PAYLOAD_TOO_LARGE = 413;\nconst MILLISECONDS_PER_SECOND = 1_000;\nconst DEFAULT_JOB_TIMEOUT_MS = 120_000;\nconst DEFAULT_JOB_POLL_MS = CLAIM_POLL_FLOOR_MS;\nconst TERMINAL_JOB_STATES: ReadonlySet<JobState> = new Set([\n \"completed\",\n \"failed\",\n \"expired\",\n \"cancelled\",\n]);\n\n/** A viem local account that exposes the public key required by job requests. */\nexport type JobsBuilderAccount = Extract<LocalAccount, WriteSignerSource>;\n\n/** Configuration captured by {@link createJobsClient}. */\nexport interface JobsClientOptions {\n /** Gateway base URL; its origin is the Web3Signed audience. */\n gatewayUrl: string;\n /** Vana chain ID used by the owner identity lookup. */\n chainId: number;\n /** Raw builder private key, used for signing and result decryption. */\n builderPrivateKey?: Hex;\n /**\n * viem local account used for signing. Its public key supports submission,\n * but without `builderPrivateKey` this client cannot decrypt results:\n * `openResult` and `readRaw` reject while submit/status/wait remain usable.\n */\n builderAccount?: JobsBuilderAccount;\n /** HTTP implementation; defaults to `globalThis.fetch`. */\n fetch?: typeof fetch;\n /** ECIES implementation; defaults to the Node provider. */\n ecies?: ECIESProvider;\n /** Clock used when constructing a job deadline. */\n now?: () => Date;\n}\n\n/** Parameters for one encrypted raw-read submission. */\nexport interface SubmitRawReadParams {\n /** Owner whose enclave executes the read. */\n owner: Address;\n /** Owner-issued builder grant. */\n grantId: Hex;\n /** Data scope to read. */\n scope: string;\n /** Exact record version to require, or `null` for an unpinned read. */\n pinnedVersion?: string | null;\n /** Requested deadline offset, clamped to the protocol maximum. */\n deadlineSeconds?: number;\n /** Gateway inline-wait seconds, clamped to `0..MAX_WAIT_SECONDS`. */\n wait?: number;\n}\n\n/** Result of submitting an encrypted raw-read job. */\nexport interface SubmitRawReadResult {\n /** Client-generated job UUID. */\n jobId: string;\n /** Current Gateway state. */\n state: JobState;\n /** Full status when the Gateway returned an inline 200 response. */\n job?: JobStatus;\n}\n\n/** Polling controls for {@link JobsClient.waitForJob}. */\nexport interface WaitForJobOptions {\n /** Total polling budget in milliseconds. */\n timeoutMs?: number;\n /** Delay between reads; values below `CLAIM_POLL_FLOOR_MS` are raised. */\n pollMs?: number;\n}\n\n/** Expected plaintext bindings for {@link JobsClient.openResult}. */\nexport interface OpenJobResultOptions {\n /** Bind the plaintext to this job id. */\n expect: {\n jobId: string;\n scope?: string;\n version?: string | null;\n };\n}\n\n/** Parameters for the submit, wait, and decrypt convenience flow. */\nexport interface ReadRawParams extends SubmitRawReadParams, WaitForJobOptions {}\n\n/** Builder operations exposed by {@link createJobsClient}. */\nexport interface JobsClient {\n /**\n * Encrypt and submit a raw-read job.\n *\n * @param params - Owner, grant, scope, deadline, and inline-wait controls.\n * @returns The job id and current state, plus inline status on HTTP 200.\n * @throws {OwnerNotReadyError} When the owner identity is not sealed.\n * @throws {JobsClientError} When the Gateway or transport rejects the call.\n */\n submitRawRead(params: SubmitRawReadParams): Promise<SubmitRawReadResult>;\n\n /**\n * Read one builder-visible job status.\n *\n * @param jobId - Client-generated job UUID.\n * @returns The current job status.\n * @throws {JobNotFoundError} When the job is absent or belongs to another builder.\n * @throws {JobsClientError} For other Gateway or transport failures.\n */\n getJob(jobId: string): Promise<JobStatus>;\n\n /**\n * Poll until a job completes, fails, expires, or is cancelled.\n *\n * @param jobId - Job UUID to poll.\n * @param options - Timeout and poll cadence.\n * @returns The terminal job status.\n * @throws {JobTimeoutError} When the caller or job deadline is exhausted.\n */\n waitForJob(jobId: string, options?: WaitForJobOptions): Promise<JobStatus>;\n\n /**\n * Fetch, decrypt, and validate a job result from object storage.\n *\n * @param handle - Object-storage location and integrity metadata.\n * @param options - Expected plaintext job, scope, and version bindings.\n * @returns The validated plaintext job result.\n * @remarks Decryption requires `builderPrivateKey`; `builderAccount` alone\n * only supports submission, status reads, and waiting.\n * @throws {JobRejectedError} When a raw private key is unavailable.\n * @throws {JobTransportError} When fetching the result object fails.\n * @throws {JobResultIntegrityError} When the fetched bytes mismatch the handle.\n * @throws {JobEnvelopeError} When decrypted protocol fields or bindings differ.\n */\n openResult(\n handle: ResultHandle,\n options: OpenJobResultOptions,\n ): Promise<JobResult>;\n\n /**\n * Submit, wait for, and decrypt one raw read.\n *\n * @param params - Raw-read request plus polling controls.\n * @returns The decrypted, binding-checked job result.\n * @remarks This convenience flow decrypts the result and therefore requires\n * `builderPrivateKey`; a client configured only with `builderAccount`\n * cannot call `readRaw`.\n * @throws {JobsClientError} When submission, polling, or decryption setup fails.\n * @throws {JobEnvelopeError} When decrypted result bindings differ.\n *\n * @example\n * ```typescript\n * const result = await client.readRaw({\n * owner,\n * grantId,\n * scope: \"profile.email\",\n * wait: 25,\n * });\n * ```\n */\n readRaw(params: ReadRawParams): Promise<JobResult>;\n}\n\ninterface ResolvedBuilder {\n address: Address;\n publicKey: Hex;\n signMessage: ReturnType<typeof resolveWriteSigner>[\"signMessage\"];\n}\n\ninterface GatewayErrorBody {\n code: JobGatewayErrorCode | null;\n message: string;\n reason?: string;\n details?: Record<string, unknown>;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction normalizeGateway(gatewayUrl: string): {\n baseUrl: string;\n audience: string;\n} {\n try {\n const url = new URL(gatewayUrl);\n if (url.pathname !== \"/\" || url.search !== \"\" || url.hash !== \"\") {\n throw new JobRejectedError(\n \"gatewayUrl must be an origin without a path, query, or fragment\",\n );\n }\n return {\n baseUrl: url.origin,\n audience: url.origin,\n };\n } catch (error) {\n if (error instanceof JobRejectedError) throw error;\n throw new JobRejectedError(\n \"gatewayUrl must be an absolute URL\",\n undefined,\n null,\n {\n cause: error instanceof Error ? error.message : String(error),\n },\n );\n }\n}\n\nfunction resolveFetch(fetchFn?: typeof fetch): typeof fetch {\n const resolved = fetchFn ?? globalThis.fetch;\n if (resolved === undefined) {\n throw new JobRejectedError(\"No fetch implementation available\");\n }\n return resolved;\n}\n\nfunction sourcePublicKey(source: WriteSignerSource): Hex | undefined {\n if (isRecord(source) && typeof source[\"publicKey\"] === \"string\") {\n return source[\"publicKey\"] as Hex;\n }\n return undefined;\n}\n\nfunction resolveBuilder(options: JobsClientOptions): {\n builder: ResolvedBuilder;\n privateKey?: Hex;\n} {\n if (options.builderPrivateKey && options.builderAccount) {\n throw new JobRejectedError(\n \"Provide builderPrivateKey or builderAccount, not both\",\n );\n }\n let source: WriteSignerSource | undefined;\n try {\n source = options.builderPrivateKey\n ? privateKeyToAccount(options.builderPrivateKey)\n : options.builderAccount;\n } catch (error) {\n throw new JobRejectedError(\"Invalid builder private key\", undefined, null, {\n cause: error instanceof Error ? error.message : String(error),\n });\n }\n if (!source) {\n throw new JobRejectedError(\n \"builderPrivateKey or builderAccount is required\",\n );\n }\n let signer: ReturnType<typeof resolveWriteSigner>;\n try {\n signer = resolveWriteSigner(source);\n } catch (error) {\n throw new JobRejectedError(\"Invalid builder signer\", undefined, null, {\n cause: error instanceof Error ? error.message : String(error),\n });\n }\n const publicKey = sourcePublicKey(source);\n if (!signer.address || !publicKey) {\n throw new JobRejectedError(\n \"Builder signer must expose both address and publicKey\",\n );\n }\n return {\n builder: {\n address: signer.address,\n publicKey,\n signMessage: signer.signMessage,\n },\n ...(options.builderPrivateKey\n ? { privateKey: options.builderPrivateKey }\n : {}),\n };\n}\n\nfunction clampInteger(\n value: number | undefined,\n fallback: number,\n minimum: number,\n maximum: number,\n field: string,\n): number {\n const resolved = value ?? fallback;\n if (!Number.isFinite(resolved)) {\n throw new JobRejectedError(`${field} must be a finite number`);\n }\n return Math.min(maximum, Math.max(minimum, Math.trunc(resolved)));\n}\n\nasync function responseBody(response: Response): Promise<unknown> {\n try {\n return await response.json();\n } catch {\n return null;\n }\n}\n\nfunction gatewayErrorBody(body: unknown, fallback: string): GatewayErrorBody {\n if (!isRecord(body)) return { code: null, message: fallback };\n const code = typeof body[\"code\"] === \"string\" ? body[\"code\"] : null;\n const message =\n typeof body[\"error\"] === \"string\"\n ? body[\"error\"]\n : typeof body[\"message\"] === \"string\"\n ? body[\"message\"]\n : fallback;\n const reason =\n typeof body[\"reason\"] === \"string\" ? body[\"reason\"] : undefined;\n const details = isRecord(body[\"details\"]) ? body[\"details\"] : undefined;\n return {\n code,\n message,\n ...(reason ? { reason } : {}),\n ...(details ? { details } : {}),\n };\n}\n\nasync function rejectedResponse(\n response: Response,\n fallback: string,\n): Promise<JobsClientError> {\n const parsed = gatewayErrorBody(await responseBody(response), fallback);\n const details =\n response.status === HTTP_UNAUTHORIZED && parsed.reason\n ? { ...parsed.details, reason: parsed.reason }\n : parsed.details;\n const message =\n response.status === HTTP_UNAUTHORIZED && parsed.reason\n ? `${parsed.message}: ${parsed.reason}`\n : parsed.message;\n if (response.status === HTTP_FORBIDDEN) {\n if (parsed.code === \"BUILDER_UNKNOWN\") {\n return new BuilderUnknownError(parsed.message, details);\n }\n if (parsed.code === \"GRANT_INVALID\") {\n return new GrantInvalidError(parsed.message, details);\n }\n if (parsed.code === \"OWNER_NOT_READY\") {\n return new OwnerNotReadyError(parsed.message, details);\n }\n }\n if (response.status === HTTP_NOT_FOUND) {\n return new JobNotFoundError(parsed.message, details);\n }\n if (response.status === HTTP_CONFLICT && parsed.code === \"JOB_ID_TAKEN\") {\n return new JobIdTakenError(parsed.message, details);\n }\n if (response.status === HTTP_PAYLOAD_TOO_LARGE) {\n return new JobRequestTooLargeError(parsed.message, parsed.code, details);\n }\n return new JobRejectedError(message, response.status, parsed.code, details);\n}\n\nfunction requireStatus(value: unknown, status: number): JobStatus {\n if (\n !isRecord(value) ||\n !isNonEmptyString(value[\"jobId\"]) ||\n typeof value[\"state\"] !== \"string\" ||\n !JOB_STATES.includes(value[\"state\"] as JobState) ||\n !isNonEmptyString(value[\"owner\"]) ||\n !isNonEmptyString(value[\"grantId\"]) ||\n !isNonEmptyString(value[\"scope\"]) ||\n !isNonEmptyString(value[\"operation\"]) ||\n !JOB_OPERATIONS.includes(value[\"operation\"] as JobOperation) ||\n (value[\"state\"] === \"completed\" && !isResultHandle(value[\"result\"]))\n ) {\n throw new JobRejectedError(\n \"Gateway response did not contain a job status\",\n status,\n );\n }\n return value as unknown as JobStatus;\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === \"string\" && value.length > 0;\n}\n\nfunction isResultHandle(value: unknown): value is ResultHandle {\n if (\n !isRecord(value) ||\n !isNonEmptyString(value[\"objectKey\"]) ||\n !isNonEmptyString(value[\"url\"]) ||\n typeof value[\"size\"] !== \"number\" ||\n !Number.isInteger(value[\"size\"]) ||\n value[\"size\"] < 0 ||\n typeof value[\"hash\"] !== \"string\" ||\n !/^0x[0-9a-fA-F]{64}$/.test(value[\"hash\"]) ||\n typeof value[\"expiresAt\"] !== \"string\" ||\n Number.isNaN(Date.parse(value[\"expiresAt\"]))\n ) {\n return false;\n }\n try {\n const url = new URL(value[\"url\"]);\n return url.protocol === \"https:\" || url.protocol === \"http:\";\n } catch {\n return false;\n }\n}\n\nfunction hasMatchingIdentityKey(value: Record<string, unknown>): boolean {\n const address = value[\"address\"];\n const publicKey = value[\"publicKey\"];\n if (\n typeof address !== \"string\" ||\n !isAddress(address) ||\n typeof publicKey !== \"string\" ||\n !isHex(publicKey)\n ) {\n return false;\n }\n try {\n return (\n publicKeyToAddress(publicKey).toLowerCase() === address.toLowerCase()\n );\n } catch {\n return false;\n }\n}\n\nfunction hasExpectedIdentity(\n value: Record<string, unknown>,\n owner: Address,\n chainId: number,\n): boolean {\n const identityOwner = value[\"ownerAddress\"];\n const identityChainId = value[\"chainId\"];\n const identityUserPsId = value[\"userPsId\"];\n if (\n !hasMatchingIdentityKey(value) ||\n typeof identityOwner !== \"string\" ||\n !isAddress(identityOwner) ||\n typeof identityChainId !== \"number\" ||\n typeof identityUserPsId !== \"string\" ||\n !isHex(identityUserPsId)\n ) {\n return false;\n }\n return (\n isAddressEqual(identityOwner, owner) &&\n identityChainId === chainId &&\n identityUserPsId.toLowerCase() === userPsId(chainId, owner).toLowerCase()\n );\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction requireFiniteDuration(value: number, field: string): number {\n if (!Number.isFinite(value)) {\n throw new JobRejectedError(`${field} must be a finite number`);\n }\n return Math.max(0, value);\n}\n\n/**\n * Create a reusable Node builder client for encrypted Gateway jobs.\n *\n * @param options - Gateway, chain, builder signer, and injectable adapters.\n * @returns A client bound to the configured builder and Gateway.\n * @throws {JobRejectedError} When configuration or signer shape is invalid.\n *\n * @remarks\n * This client is Node-only. `builderPrivateKey` supports the complete flow;\n * `builderAccount` alone supports submit/status/wait but cannot decrypt, so\n * `openResult` and `readRaw` require the raw private key.\n *\n * @example\n * ```typescript\n * const client = createJobsClient({\n * gatewayUrl: \"https://gateway.example.com\",\n * chainId: 14800,\n * builderPrivateKey: process.env.BUILDER_PRIVATE_KEY as Hex,\n * });\n * const result = await client.readRaw({ owner, grantId, scope: \"profile\" });\n * ```\n */\nexport function createJobsClient(options: JobsClientOptions): JobsClient {\n const { baseUrl, audience } = normalizeGateway(options.gatewayUrl);\n const fetchFn = resolveFetch(options.fetch);\n const { builder, privateKey } = resolveBuilder(options);\n const ecies = options.ecies ?? new NodeECIESUint8Provider();\n const now = options.now ?? (() => new Date());\n\n async function request(\n url: string,\n init: RequestInit,\n description: string,\n ): Promise<Response> {\n try {\n return await fetchFn(url, init);\n } catch (error) {\n throw new JobTransportError(\n `${description} failed: ${error instanceof Error ? error.message : String(error)}`,\n error,\n );\n }\n }\n\n async function getIdentity(\n owner: Address,\n ): Promise<IdentityResponse[\"identity\"]> {\n const query = new URLSearchParams({\n owner,\n chainId: String(options.chainId),\n });\n const response = await request(\n `${baseUrl}${IDENTITY_PATH}?${query.toString()}`,\n { method: GET_METHOD },\n \"Owner identity lookup\",\n );\n if (response.status === HTTP_NOT_FOUND) {\n const parsed = gatewayErrorBody(\n await responseBody(response),\n \"Owner identity not found\",\n );\n throw new OwnerNotReadyError(parsed.message, parsed.details);\n }\n if (!response.ok) {\n throw await rejectedResponse(response, \"Owner identity lookup rejected\");\n }\n const body = await responseBody(response);\n if (\n !isRecord(body) ||\n body[\"state\"] !== SEALED_IDENTITY_STATE ||\n !isRecord(body[\"identity\"])\n ) {\n throw new OwnerNotReadyError(\"Owner identity is not sealed\", {\n state: isRecord(body) ? body[\"state\"] : undefined,\n });\n }\n if (!hasExpectedIdentity(body[\"identity\"], owner, options.chainId)) {\n throw new JobRejectedError(\n \"Gateway returned a malformed or mismatched owner identity\",\n response.status,\n );\n }\n // Full TDX/KMS evidence verification awaits provisioned trust anchors and is intentionally outside this driver-matching client.\n return body[\"identity\"] as unknown as IdentityResponse[\"identity\"];\n }\n\n const client: JobsClient = {\n async submitRawRead(params) {\n const identity = await getIdentity(params.owner);\n const deadlineSeconds = clampInteger(\n params.deadlineSeconds,\n DEFAULT_JOB_DEADLINE_SECONDS,\n 1,\n MAX_JOB_DEADLINE_SECONDS,\n \"deadlineSeconds\",\n );\n const wait = clampInteger(params.wait, 0, 0, MAX_WAIT_SECONDS, \"wait\");\n const jobId = randomUUID();\n const nowMs = now().getTime();\n if (!Number.isFinite(nowMs)) {\n throw new JobRejectedError(\"now must return a valid Date\");\n }\n const deadline = new Date(\n nowMs + deadlineSeconds * MILLISECONDS_PER_SECOND,\n ).toISOString();\n const requestBody: JobRequest = {\n v: JOB_PROTOCOL_VERSION,\n jobId,\n owner: params.owner,\n builder: builder.address,\n builderPublicKey: builder.publicKey,\n grantId: params.grantId,\n scope: params.scope,\n operation: RAW_READ_OPERATION,\n pinnedVersion: params.pinnedVersion ?? null,\n deadline,\n };\n const auth = await buildWeb3SignedHeader({\n signMessage: builder.signMessage,\n aud: audience,\n method: POST_METHOD,\n uri: JOB_EXECUTE_PATH,\n bodyHash: computeBodyHash(canonicalJobRequestBytes(requestBody)),\n nonce: randomUUID(),\n });\n const requestCiphertext = await sealJobRequest(\n { request: requestBody, auth },\n identity.publicKey,\n ecies,\n );\n const submission: JobSubmission = {\n owner: requestBody.owner,\n grantId: requestBody.grantId,\n scope: requestBody.scope,\n operation: requestBody.operation,\n idempotencyKey: randomUUID(),\n jobId,\n deadline,\n requestCiphertext,\n };\n const bodyBytes = new TextEncoder().encode(JSON.stringify(submission));\n const authorization = await buildWeb3SignedHeader({\n signMessage: builder.signMessage,\n aud: audience,\n method: POST_METHOD,\n uri: JOBS_PATH,\n body: bodyBytes,\n nonce: randomUUID(),\n });\n const response = await request(\n `${baseUrl}${JOBS_PATH}?wait=${wait}`,\n {\n method: POST_METHOD,\n headers: {\n [AUTHORIZATION_HEADER]: authorization,\n [CONTENT_TYPE_HEADER]: JSON_CONTENT_TYPE,\n },\n body: bodyBytes,\n },\n \"Job submission\",\n );\n if (!response.ok) {\n throw await rejectedResponse(response, \"Job submission rejected\");\n }\n const body = await responseBody(response);\n if (response.status === HTTP_OK && isRecord(body)) {\n const job = requireStatus(body[\"job\"], response.status);\n if (job.jobId !== jobId) {\n throw new JobRejectedError(\n \"Gateway returned a job id that does not match the submission\",\n response.status,\n );\n }\n return { jobId, state: job.state, job };\n }\n if (\n response.status === HTTP_ACCEPTED &&\n isRecord(body) &&\n body[\"jobId\"] === jobId &&\n typeof body[\"state\"] === \"string\" &&\n JOB_STATES.includes(body[\"state\"] as JobState)\n ) {\n return { jobId, state: body[\"state\"] as JobState };\n }\n throw new JobRejectedError(\n \"Gateway returned an undocumented job submission response\",\n response.status,\n );\n },\n\n async getJob(jobId) {\n const path = `${JOBS_PATH}/${encodeURIComponent(jobId)}`;\n const authorization = await buildWeb3SignedHeader({\n signMessage: builder.signMessage,\n aud: audience,\n method: GET_METHOD,\n uri: path,\n nonce: randomUUID(),\n });\n const response = await request(\n `${baseUrl}${path}`,\n {\n method: GET_METHOD,\n headers: { [AUTHORIZATION_HEADER]: authorization },\n },\n \"Job status read\",\n );\n if (!response.ok) {\n throw await rejectedResponse(response, \"Job status read rejected\");\n }\n const body = await responseBody(response);\n return requireStatus(\n isRecord(body) ? body[\"job\"] : undefined,\n response.status,\n );\n },\n\n async waitForJob(jobId, waitOptions = {}) {\n const timeoutMs = requireFiniteDuration(\n waitOptions.timeoutMs ?? DEFAULT_JOB_TIMEOUT_MS,\n \"timeoutMs\",\n );\n const pollMs = Math.max(\n CLAIM_POLL_FLOOR_MS,\n requireFiniteDuration(\n waitOptions.pollMs ?? DEFAULT_JOB_POLL_MS,\n \"pollMs\",\n ),\n );\n const startedAt = Date.now();\n const callerDeadline = startedAt + timeoutMs;\n let latest: JobStatus | undefined;\n for (;;) {\n if (Date.now() >= callerDeadline) {\n throw new JobTimeoutError(\n `Timed out waiting for terminal job ${jobId}`,\n {\n jobId,\n timeoutMs,\n state: latest?.state,\n },\n );\n }\n latest = await client.getJob(jobId);\n if (TERMINAL_JOB_STATES.has(latest.state)) return latest;\n const remaining = callerDeadline - Date.now();\n if (remaining <= 0) {\n throw new JobTimeoutError(\n `Timed out waiting for terminal job ${jobId}`,\n {\n jobId,\n timeoutMs,\n state: latest.state,\n },\n );\n }\n await sleep(Math.min(pollMs, remaining));\n }\n },\n\n async openResult(handle, openOptions) {\n if (!privateKey) {\n throw new JobRejectedError(\n \"openResult requires createJobsClient({ builderPrivateKey })\",\n );\n }\n const response = await request(\n handle.url,\n { method: GET_METHOD },\n \"Job result fetch\",\n );\n if (!response.ok) {\n throw await rejectedResponse(response, \"Job result fetch rejected\");\n }\n let bytes: Uint8Array;\n try {\n bytes = new Uint8Array(await response.arrayBuffer());\n } catch (error) {\n throw new JobTransportError(\n `Job result fetch failed: ${error instanceof Error ? error.message : String(error)}`,\n error,\n );\n }\n const actualHash = bytesToHex(sha256(bytes));\n if (actualHash.toLowerCase() !== handle.hash.toLowerCase()) {\n throw new JobResultIntegrityError(\n \"Fetched job result hash does not match its handle\",\n {\n objectKey: handle.objectKey,\n expectedHash: handle.hash,\n actualHash,\n },\n );\n }\n if (bytes.length !== handle.size) {\n throw new JobResultIntegrityError(\n \"Fetched job result size does not match its handle\",\n {\n objectKey: handle.objectKey,\n expectedSize: handle.size,\n actualSize: bytes.length,\n },\n );\n }\n return openJobResult(bytes, privateKey, ecies, openOptions.expect);\n },\n\n async readRaw(params) {\n const submitted = await client.submitRawRead(params);\n const job =\n submitted.job && TERMINAL_JOB_STATES.has(submitted.job.state)\n ? submitted.job\n : await client.waitForJob(submitted.jobId, {\n timeoutMs: params.timeoutMs,\n pollMs: params.pollMs,\n });\n if (job.state !== \"completed\") {\n throw new JobRejectedError(\n `Job ${job.jobId} ended in state ${job.state}${job.failureReason ? `: ${job.failureReason}` : \"\"}`,\n undefined,\n null,\n {\n jobId: job.jobId,\n state: job.state,\n ...(job.failureReason ? { failureReason: job.failureReason } : {}),\n },\n );\n }\n if (!job.result) {\n throw new JobRejectedError(\n \"Completed job status does not include a result handle\",\n undefined,\n null,\n { jobId: job.jobId, state: job.state },\n );\n }\n return client.openResult(job.result, {\n expect: {\n jobId: submitted.jobId,\n scope: params.scope,\n ...(params.pinnedVersion !== undefined &&\n params.pinnedVersion !== null\n ? { version: params.pinnedVersion }\n : {}),\n },\n });\n },\n };\n\n return client;\n}\n"],"mappings":"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","names":[]}
|
package/dist/protocol/jobs.cjs
CHANGED
|
@@ -25,7 +25,6 @@ __export(jobs_exports, {
|
|
|
25
25
|
JOB_PROTOCOL_VERSION: () => JOB_PROTOCOL_VERSION,
|
|
26
26
|
JOB_STATES: () => JOB_STATES,
|
|
27
27
|
MAX_ATTEMPTS: () => MAX_ATTEMPTS,
|
|
28
|
-
MAX_INLINE_RESULT_BYTES: () => MAX_INLINE_RESULT_BYTES,
|
|
29
28
|
MAX_JOB_DEADLINE_SECONDS: () => MAX_JOB_DEADLINE_SECONDS,
|
|
30
29
|
MAX_LEASE_SECONDS: () => MAX_LEASE_SECONDS,
|
|
31
30
|
MAX_WAIT_SECONDS: () => MAX_WAIT_SECONDS
|
|
@@ -47,7 +46,6 @@ const MAX_LEASE_SECONDS = 300;
|
|
|
47
46
|
const MAX_ATTEMPTS = 3;
|
|
48
47
|
const MAX_WAIT_SECONDS = 25;
|
|
49
48
|
const CLAIM_POLL_FLOOR_MS = 1e3;
|
|
50
|
-
const MAX_INLINE_RESULT_BYTES = 1048576;
|
|
51
49
|
const DEFAULT_JOB_DEADLINE_SECONDS = 600;
|
|
52
50
|
const MAX_JOB_DEADLINE_SECONDS = 3600;
|
|
53
51
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -59,7 +57,6 @@ const MAX_JOB_DEADLINE_SECONDS = 3600;
|
|
|
59
57
|
JOB_PROTOCOL_VERSION,
|
|
60
58
|
JOB_STATES,
|
|
61
59
|
MAX_ATTEMPTS,
|
|
62
|
-
MAX_INLINE_RESULT_BYTES,
|
|
63
60
|
MAX_JOB_DEADLINE_SECONDS,
|
|
64
61
|
MAX_LEASE_SECONDS,
|
|
65
62
|
MAX_WAIT_SECONDS
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/protocol/jobs.ts"],"sourcesContent":["/**\n * Job queue protocol types for encrypted Personal Server execution.\n *\n * Builder -> GW -> Agent -> Sandbox\n * Builder -> GW: submit an enclave-encrypted request.\n * GW -> Agent: claim work with a fenced lease and sealed owner identity.\n * Agent -> Sandbox: decrypt, wake the owner sandbox, and execute privately.\n * Sandbox -> Agent: encrypt the result to the builder's public key.\n * Agent -> GW: complete with
|
|
1
|
+
{"version":3,"sources":["../../src/protocol/jobs.ts"],"sourcesContent":["/**\n * Job queue protocol types for encrypted Personal Server execution.\n *\n * Builder -> GW -> Agent -> Sandbox\n * Builder -> GW: submit an enclave-encrypted request.\n * GW -> Agent: claim work with a fenced lease and sealed owner identity.\n * Agent -> Sandbox: decrypt, wake the owner sandbox, and execute privately.\n * Sandbox -> Agent: encrypt the result to the builder's public key.\n * Agent -> GW: store the sealed result and complete with its object key, hash, and size.\n * GW -> Builder: return inline or polled status with an object-storage handle.\n * Builder: decrypt and verify the result's job, scope, and version bindings.\n * Full flow: personal-server-ts `docs/260903-jobs-contract.md`, section 1.\n *\n * @category Protocol\n */\n\nimport type { Address, Hex } from \"viem\";\nimport type { SealedEnvelope } from \"./identity\";\n\nexport const JOB_PROTOCOL_VERSION = 1;\nexport const JOB_OPERATIONS = [\"raw_read\", \"inference\"] as const;\nexport type JobOperation = (typeof JOB_OPERATIONS)[number];\nexport const JOB_STATES = [\n \"queued\",\n \"claimed\",\n \"running\",\n \"completed\",\n \"failed\",\n \"expired\",\n \"cancelled\",\n] as const;\nexport type JobState = (typeof JOB_STATES)[number];\n/** Payment lifecycle recorded for a queued job. */\nexport type PaymentState = \"none\" | \"reserved\" | \"settled\";\nexport const DEFAULT_LEASE_SECONDS = 30;\nexport const MAX_LEASE_SECONDS = 300;\nexport const MAX_ATTEMPTS = 3;\nexport const MAX_WAIT_SECONDS = 25;\nexport const CLAIM_POLL_FLOOR_MS = 1000;\nexport const DEFAULT_JOB_DEADLINE_SECONDS = 600;\nexport const MAX_JOB_DEADLINE_SECONDS = 3600;\n\n/** Inner plaintext of the request box (ECIES to the enclave publicKey). */\nexport interface JobRequest {\n v: 1;\n jobId: string;\n owner: Address;\n builder: Address;\n builderPublicKey: Hex;\n grantId: Hex;\n scope: string;\n operation: JobOperation;\n pinnedVersion: string | null;\n deadline: string; /* ISO */\n}\n/** Plaintext of the request box; the Gateway never sees it. */\nexport interface JobRequestEnvelope {\n request: JobRequest;\n /**\n * `Web3Signed <b64>.<sig>` by the builder: `aud` = Gateway origin,\n * `uri` = `/v1/jobs/execute`,\n * `bodyHash` = `sha256(canonicalJobRequestBytes(request))`.\n */\n auth: string;\n}\n/** Outer body of POST /v1/jobs (signed Web3Signed by the builder). */\nexport interface JobSubmission {\n owner: Address;\n grantId: Hex;\n scope: string;\n operation: JobOperation;\n idempotencyKey: string;\n /** Client UUID, echoed in `JobRequest.jobId`. */\n jobId: string;\n deadline?: string;\n /** Base64 ECIES from `sealJobRequest`. */\n requestCiphertext: string;\n}\n/** Where a completed job's sealed result lives. Bytes never transit the Gateway. */\nexport interface ResultHandle {\n /** Object key in vana-storage, `jobresults/{chainId}/{jobId}`. */\n objectKey: string;\n /** Absolute URL the builder GETs. The Gateway builds it from its storage origin. */\n url: string;\n /** Byte length of the sealed object. */\n size: number;\n /** sha256 of the sealed bytes, 0x-prefixed. */\n hash: Hex;\n /** Logical expiry. After this the Gateway stops serving the handle. */\n expiresAt: string;\n}\n/** Response from `GET /v1/jobs/:id`. */\nexport interface JobStatus {\n jobId: string;\n state: JobState;\n operation: JobOperation;\n owner: Address;\n grantId: Hex;\n scope: string;\n pinnedVersion: string | null;\n attempt: number;\n price: string;\n payer: \"builder\";\n paymentState: PaymentState;\n createdAt: string;\n claimedAt: string | null;\n completedAt: string | null;\n failureReason: string | null;\n /** Present only when `state === \"completed\"`. */\n result?: ResultHandle;\n}\n/** Request body for `POST /v1/jobs/claim`. */\nexport interface ClaimRequest {\n leaseSeconds?: number;\n capacity?: number;\n}\n/** Claimed job and owner identity returned by `POST /v1/jobs/claim`. */\nexport interface ClaimResponse {\n job: {\n jobId: string;\n owner: Address;\n builder: Address;\n grantId: Hex;\n scope: string;\n operation: JobOperation;\n pinnedVersion: string | null;\n requestCiphertext: string;\n attempt: number;\n deadlineAt: string | null;\n claimExpiresAt: string;\n fencingToken: number; /* = attempt; every node write echoes it */\n };\n identity: {\n userPsId: Hex;\n epoch: number;\n enclaveAddress: Address;\n enclavePublicKey: Hex;\n sealedEnvelope: SealedEnvelope;\n };\n}\n/** Request body for `POST /v1/jobs/:id/heartbeat`. */\nexport interface HeartbeatRequest {\n leaseSeconds?: number;\n fencingToken: number;\n}\n/** Request body for `POST /v1/jobs/:id/complete`. */\nexport interface CompleteRequest {\n fencingToken: number;\n resultHash: Hex;\n resultSize: number;\n /** `jobresults/{chainId}/{jobId}`. */\n resultObjectKey: string;\n}\n/** Request body for `POST /v1/jobs/:id/fail`. */\nexport interface FailRequest {\n fencingToken: number;\n reason: string; /* <= 1024, fail.ts:16 */\n}\n/** Successful response from a fenced job write endpoint. */\nexport interface FencedResponse {\n success: true;\n jobId: string;\n state: JobState;\n claimExpiresAt: string | null;\n}\n/** Inner plaintext of the result box (ECIES to builderPublicKey). */\nexport interface JobResult {\n v: 1;\n jobId: string;\n scope: string;\n version: string | null;\n contentType: string;\n /** Raw result bytes; callers decode text explicitly when appropriate. */\n body: Uint8Array;\n}\n/** Admission lifecycle of a registered TEE node. */\nexport type TeeNodeState = \"pending\" | \"admitted\" | \"draining\" | \"removed\";\n/** Request body for `POST /v1/tee-nodes`. */\nexport interface TeeNodeRegistration {\n nodeId: string;\n appId: Hex;\n composeHash: Hex;\n publicUrl: string;\n capacity: number;\n secret: string;\n}\n/** Request body for `POST /v1/tee-nodes/:id/heartbeat`. */\nexport interface TeeNodeHeartbeat {\n composeHash: Hex;\n instanceId: string;\n activeSandboxes: number;\n capacity: number;\n}\n/** Public registration and capacity state for a TEE node. */\nexport interface TeeNode {\n nodeId: string;\n appId: Hex;\n composeHash: Hex;\n publicUrl: string;\n state: TeeNodeState;\n capacity: number;\n activeSandboxes: number;\n lastHeartbeatAt: string | null;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmBO,MAAM,uBAAuB;AAC7B,MAAM,iBAAiB,CAAC,YAAY,WAAW;AAE/C,MAAM,aAAa;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAIO,MAAM,wBAAwB;AAC9B,MAAM,oBAAoB;AAC1B,MAAM,eAAe;AACrB,MAAM,mBAAmB;AACzB,MAAM,sBAAsB;AAC5B,MAAM,+BAA+B;AACrC,MAAM,2BAA2B;","names":[]}
|
package/dist/protocol/jobs.d.ts
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
* GW -> Agent: claim work with a fenced lease and sealed owner identity.
|
|
7
7
|
* Agent -> Sandbox: decrypt, wake the owner sandbox, and execute privately.
|
|
8
8
|
* Sandbox -> Agent: encrypt the result to the builder's public key.
|
|
9
|
-
* Agent -> GW: complete with
|
|
10
|
-
* GW -> Builder: return inline or polled status
|
|
9
|
+
* Agent -> GW: store the sealed result and complete with its object key, hash, and size.
|
|
10
|
+
* GW -> Builder: return inline or polled status with an object-storage handle.
|
|
11
11
|
* Builder: decrypt and verify the result's job, scope, and version bindings.
|
|
12
12
|
* Full flow: personal-server-ts `docs/260903-jobs-contract.md`, section 1.
|
|
13
13
|
*
|
|
@@ -27,7 +27,6 @@ export declare const MAX_LEASE_SECONDS = 300;
|
|
|
27
27
|
export declare const MAX_ATTEMPTS = 3;
|
|
28
28
|
export declare const MAX_WAIT_SECONDS = 25;
|
|
29
29
|
export declare const CLAIM_POLL_FLOOR_MS = 1000;
|
|
30
|
-
export declare const MAX_INLINE_RESULT_BYTES = 1048576;
|
|
31
30
|
export declare const DEFAULT_JOB_DEADLINE_SECONDS = 600;
|
|
32
31
|
export declare const MAX_JOB_DEADLINE_SECONDS = 3600;
|
|
33
32
|
/** Inner plaintext of the request box (ECIES to the enclave publicKey). */
|
|
@@ -66,6 +65,19 @@ export interface JobSubmission {
|
|
|
66
65
|
/** Base64 ECIES from `sealJobRequest`. */
|
|
67
66
|
requestCiphertext: string;
|
|
68
67
|
}
|
|
68
|
+
/** Where a completed job's sealed result lives. Bytes never transit the Gateway. */
|
|
69
|
+
export interface ResultHandle {
|
|
70
|
+
/** Object key in vana-storage, `jobresults/{chainId}/{jobId}`. */
|
|
71
|
+
objectKey: string;
|
|
72
|
+
/** Absolute URL the builder GETs. The Gateway builds it from its storage origin. */
|
|
73
|
+
url: string;
|
|
74
|
+
/** Byte length of the sealed object. */
|
|
75
|
+
size: number;
|
|
76
|
+
/** sha256 of the sealed bytes, 0x-prefixed. */
|
|
77
|
+
hash: Hex;
|
|
78
|
+
/** Logical expiry. After this the Gateway stops serving the handle. */
|
|
79
|
+
expiresAt: string;
|
|
80
|
+
}
|
|
69
81
|
/** Response from `GET /v1/jobs/:id`. */
|
|
70
82
|
export interface JobStatus {
|
|
71
83
|
jobId: string;
|
|
@@ -83,11 +95,8 @@ export interface JobStatus {
|
|
|
83
95
|
claimedAt: string | null;
|
|
84
96
|
completedAt: string | null;
|
|
85
97
|
failureReason: string | null;
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
resultHash?: Hex;
|
|
89
|
-
resultSize?: number;
|
|
90
|
-
resultExpiresAt?: string;
|
|
98
|
+
/** Present only when `state === "completed"`. */
|
|
99
|
+
result?: ResultHandle;
|
|
91
100
|
}
|
|
92
101
|
/** Request body for `POST /v1/jobs/claim`. */
|
|
93
102
|
export interface ClaimRequest {
|
|
@@ -128,10 +137,8 @@ export interface CompleteRequest {
|
|
|
128
137
|
fencingToken: number;
|
|
129
138
|
resultHash: Hex;
|
|
130
139
|
resultSize: number;
|
|
131
|
-
/**
|
|
132
|
-
|
|
133
|
-
/** v1.1, R2. */
|
|
134
|
-
resultHandle?: string;
|
|
140
|
+
/** `jobresults/{chainId}/{jobId}`. */
|
|
141
|
+
resultObjectKey: string;
|
|
135
142
|
}
|
|
136
143
|
/** Request body for `POST /v1/jobs/:id/fail`. */
|
|
137
144
|
export interface FailRequest {
|
|
@@ -152,7 +159,8 @@ export interface JobResult {
|
|
|
152
159
|
scope: string;
|
|
153
160
|
version: string | null;
|
|
154
161
|
contentType: string;
|
|
155
|
-
|
|
162
|
+
/** Raw result bytes; callers decode text explicitly when appropriate. */
|
|
163
|
+
body: Uint8Array;
|
|
156
164
|
}
|
|
157
165
|
/** Admission lifecycle of a registered TEE node. */
|
|
158
166
|
export type TeeNodeState = "pending" | "admitted" | "draining" | "removed";
|
package/dist/protocol/jobs.js
CHANGED
|
@@ -14,7 +14,6 @@ const MAX_LEASE_SECONDS = 300;
|
|
|
14
14
|
const MAX_ATTEMPTS = 3;
|
|
15
15
|
const MAX_WAIT_SECONDS = 25;
|
|
16
16
|
const CLAIM_POLL_FLOOR_MS = 1e3;
|
|
17
|
-
const MAX_INLINE_RESULT_BYTES = 1048576;
|
|
18
17
|
const DEFAULT_JOB_DEADLINE_SECONDS = 600;
|
|
19
18
|
const MAX_JOB_DEADLINE_SECONDS = 3600;
|
|
20
19
|
export {
|
|
@@ -25,7 +24,6 @@ export {
|
|
|
25
24
|
JOB_PROTOCOL_VERSION,
|
|
26
25
|
JOB_STATES,
|
|
27
26
|
MAX_ATTEMPTS,
|
|
28
|
-
MAX_INLINE_RESULT_BYTES,
|
|
29
27
|
MAX_JOB_DEADLINE_SECONDS,
|
|
30
28
|
MAX_LEASE_SECONDS,
|
|
31
29
|
MAX_WAIT_SECONDS
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/protocol/jobs.ts"],"sourcesContent":["/**\n * Job queue protocol types for encrypted Personal Server execution.\n *\n * Builder -> GW -> Agent -> Sandbox\n * Builder -> GW: submit an enclave-encrypted request.\n * GW -> Agent: claim work with a fenced lease and sealed owner identity.\n * Agent -> Sandbox: decrypt, wake the owner sandbox, and execute privately.\n * Sandbox -> Agent: encrypt the result to the builder's public key.\n * Agent -> GW: complete with
|
|
1
|
+
{"version":3,"sources":["../../src/protocol/jobs.ts"],"sourcesContent":["/**\n * Job queue protocol types for encrypted Personal Server execution.\n *\n * Builder -> GW -> Agent -> Sandbox\n * Builder -> GW: submit an enclave-encrypted request.\n * GW -> Agent: claim work with a fenced lease and sealed owner identity.\n * Agent -> Sandbox: decrypt, wake the owner sandbox, and execute privately.\n * Sandbox -> Agent: encrypt the result to the builder's public key.\n * Agent -> GW: store the sealed result and complete with its object key, hash, and size.\n * GW -> Builder: return inline or polled status with an object-storage handle.\n * Builder: decrypt and verify the result's job, scope, and version bindings.\n * Full flow: personal-server-ts `docs/260903-jobs-contract.md`, section 1.\n *\n * @category Protocol\n */\n\nimport type { Address, Hex } from \"viem\";\nimport type { SealedEnvelope } from \"./identity\";\n\nexport const JOB_PROTOCOL_VERSION = 1;\nexport const JOB_OPERATIONS = [\"raw_read\", \"inference\"] as const;\nexport type JobOperation = (typeof JOB_OPERATIONS)[number];\nexport const JOB_STATES = [\n \"queued\",\n \"claimed\",\n \"running\",\n \"completed\",\n \"failed\",\n \"expired\",\n \"cancelled\",\n] as const;\nexport type JobState = (typeof JOB_STATES)[number];\n/** Payment lifecycle recorded for a queued job. */\nexport type PaymentState = \"none\" | \"reserved\" | \"settled\";\nexport const DEFAULT_LEASE_SECONDS = 30;\nexport const MAX_LEASE_SECONDS = 300;\nexport const MAX_ATTEMPTS = 3;\nexport const MAX_WAIT_SECONDS = 25;\nexport const CLAIM_POLL_FLOOR_MS = 1000;\nexport const DEFAULT_JOB_DEADLINE_SECONDS = 600;\nexport const MAX_JOB_DEADLINE_SECONDS = 3600;\n\n/** Inner plaintext of the request box (ECIES to the enclave publicKey). */\nexport interface JobRequest {\n v: 1;\n jobId: string;\n owner: Address;\n builder: Address;\n builderPublicKey: Hex;\n grantId: Hex;\n scope: string;\n operation: JobOperation;\n pinnedVersion: string | null;\n deadline: string; /* ISO */\n}\n/** Plaintext of the request box; the Gateway never sees it. */\nexport interface JobRequestEnvelope {\n request: JobRequest;\n /**\n * `Web3Signed <b64>.<sig>` by the builder: `aud` = Gateway origin,\n * `uri` = `/v1/jobs/execute`,\n * `bodyHash` = `sha256(canonicalJobRequestBytes(request))`.\n */\n auth: string;\n}\n/** Outer body of POST /v1/jobs (signed Web3Signed by the builder). */\nexport interface JobSubmission {\n owner: Address;\n grantId: Hex;\n scope: string;\n operation: JobOperation;\n idempotencyKey: string;\n /** Client UUID, echoed in `JobRequest.jobId`. */\n jobId: string;\n deadline?: string;\n /** Base64 ECIES from `sealJobRequest`. */\n requestCiphertext: string;\n}\n/** Where a completed job's sealed result lives. Bytes never transit the Gateway. */\nexport interface ResultHandle {\n /** Object key in vana-storage, `jobresults/{chainId}/{jobId}`. */\n objectKey: string;\n /** Absolute URL the builder GETs. The Gateway builds it from its storage origin. */\n url: string;\n /** Byte length of the sealed object. */\n size: number;\n /** sha256 of the sealed bytes, 0x-prefixed. */\n hash: Hex;\n /** Logical expiry. After this the Gateway stops serving the handle. */\n expiresAt: string;\n}\n/** Response from `GET /v1/jobs/:id`. */\nexport interface JobStatus {\n jobId: string;\n state: JobState;\n operation: JobOperation;\n owner: Address;\n grantId: Hex;\n scope: string;\n pinnedVersion: string | null;\n attempt: number;\n price: string;\n payer: \"builder\";\n paymentState: PaymentState;\n createdAt: string;\n claimedAt: string | null;\n completedAt: string | null;\n failureReason: string | null;\n /** Present only when `state === \"completed\"`. */\n result?: ResultHandle;\n}\n/** Request body for `POST /v1/jobs/claim`. */\nexport interface ClaimRequest {\n leaseSeconds?: number;\n capacity?: number;\n}\n/** Claimed job and owner identity returned by `POST /v1/jobs/claim`. */\nexport interface ClaimResponse {\n job: {\n jobId: string;\n owner: Address;\n builder: Address;\n grantId: Hex;\n scope: string;\n operation: JobOperation;\n pinnedVersion: string | null;\n requestCiphertext: string;\n attempt: number;\n deadlineAt: string | null;\n claimExpiresAt: string;\n fencingToken: number; /* = attempt; every node write echoes it */\n };\n identity: {\n userPsId: Hex;\n epoch: number;\n enclaveAddress: Address;\n enclavePublicKey: Hex;\n sealedEnvelope: SealedEnvelope;\n };\n}\n/** Request body for `POST /v1/jobs/:id/heartbeat`. */\nexport interface HeartbeatRequest {\n leaseSeconds?: number;\n fencingToken: number;\n}\n/** Request body for `POST /v1/jobs/:id/complete`. */\nexport interface CompleteRequest {\n fencingToken: number;\n resultHash: Hex;\n resultSize: number;\n /** `jobresults/{chainId}/{jobId}`. */\n resultObjectKey: string;\n}\n/** Request body for `POST /v1/jobs/:id/fail`. */\nexport interface FailRequest {\n fencingToken: number;\n reason: string; /* <= 1024, fail.ts:16 */\n}\n/** Successful response from a fenced job write endpoint. */\nexport interface FencedResponse {\n success: true;\n jobId: string;\n state: JobState;\n claimExpiresAt: string | null;\n}\n/** Inner plaintext of the result box (ECIES to builderPublicKey). */\nexport interface JobResult {\n v: 1;\n jobId: string;\n scope: string;\n version: string | null;\n contentType: string;\n /** Raw result bytes; callers decode text explicitly when appropriate. */\n body: Uint8Array;\n}\n/** Admission lifecycle of a registered TEE node. */\nexport type TeeNodeState = \"pending\" | \"admitted\" | \"draining\" | \"removed\";\n/** Request body for `POST /v1/tee-nodes`. */\nexport interface TeeNodeRegistration {\n nodeId: string;\n appId: Hex;\n composeHash: Hex;\n publicUrl: string;\n capacity: number;\n secret: string;\n}\n/** Request body for `POST /v1/tee-nodes/:id/heartbeat`. */\nexport interface TeeNodeHeartbeat {\n composeHash: Hex;\n instanceId: string;\n activeSandboxes: number;\n capacity: number;\n}\n/** Public registration and capacity state for a TEE node. */\nexport interface TeeNode {\n nodeId: string;\n appId: Hex;\n composeHash: Hex;\n publicUrl: string;\n state: TeeNodeState;\n capacity: number;\n activeSandboxes: number;\n lastHeartbeatAt: string | null;\n}\n"],"mappings":"AAmBO,MAAM,uBAAuB;AAC7B,MAAM,iBAAiB,CAAC,YAAY,WAAW;AAE/C,MAAM,aAAa;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAIO,MAAM,wBAAwB;AAC9B,MAAM,oBAAoB;AAC1B,MAAM,eAAe;AACrB,MAAM,mBAAmB;AACzB,MAAM,sBAAsB;AAC5B,MAAM,+BAA+B;AACrC,MAAM,2BAA2B;","names":[]}
|
|
@@ -38,7 +38,7 @@ module.exports = __toCommonJS(lineage_exports);
|
|
|
38
38
|
var import_viem = require("viem");
|
|
39
39
|
var import_zod = require("zod");
|
|
40
40
|
var import_web3_signed_builder = require("../auth/web3-signed-builder");
|
|
41
|
-
var import_errors = require("../errors");
|
|
41
|
+
var import_errors = require("../errors.cjs");
|
|
42
42
|
var import_personal_server_error_body = require("./personal-server-error-body");
|
|
43
43
|
var import_write_signer = require("./write-signer");
|
|
44
44
|
const DATA_POINT_ID_PATTERN = /^0x[0-9a-fA-F]{64}$/;
|
|
@@ -24,7 +24,7 @@ __export(personal_server_data_exports, {
|
|
|
24
24
|
});
|
|
25
25
|
module.exports = __toCommonJS(personal_server_data_exports);
|
|
26
26
|
var import_web3_signed_builder = require("../auth/web3-signed-builder");
|
|
27
|
-
var import_errors = require("../errors");
|
|
27
|
+
var import_errors = require("../errors.cjs");
|
|
28
28
|
var import_data_file = require("./data-file");
|
|
29
29
|
var import_data_point_deletion = require("./data-point-deletion");
|
|
30
30
|
var import_response_body = require("../utils/response-body");
|
|
@@ -42,7 +42,7 @@ var import_sha2 = require("@noble/hashes/sha2");
|
|
|
42
42
|
var import_viem = require("viem");
|
|
43
43
|
var import_zod = require("zod");
|
|
44
44
|
var import_web3_signed_builder = require("../auth/web3-signed-builder");
|
|
45
|
-
var import_errors = require("../errors");
|
|
45
|
+
var import_errors = require("../errors.cjs");
|
|
46
46
|
var import_encoding = require("../utils/encoding");
|
|
47
47
|
var import_data_file = require("./data-file");
|
|
48
48
|
var import_lineage = require("./lineage");
|
|
@@ -31,7 +31,7 @@ __export(write_request_exports, {
|
|
|
31
31
|
module.exports = __toCommonJS(write_request_exports);
|
|
32
32
|
var import_sha2 = require("@noble/hashes/sha2");
|
|
33
33
|
var import_viem = require("viem");
|
|
34
|
-
var import_errors = require("../errors");
|
|
34
|
+
var import_errors = require("../errors.cjs");
|
|
35
35
|
function normalizeBaseUrl(url) {
|
|
36
36
|
return url.replace(/\/+$/, "");
|
|
37
37
|
}
|
|
@@ -21,7 +21,7 @@ __export(write_signer_exports, {
|
|
|
21
21
|
resolveWriteSigner: () => resolveWriteSigner
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(write_signer_exports);
|
|
24
|
-
var import_errors = require("../errors");
|
|
24
|
+
var import_errors = require("../errors.cjs");
|
|
25
25
|
function isRecord(value) {
|
|
26
26
|
return value !== null && typeof value === "object";
|
|
27
27
|
}
|
package/dist/react.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/react.ts"],"sourcesContent":["/**\n * React entry point for the Vana SDK direct Data Portability flow.\n *\n * @remarks\n * Exposes {@link useDirectVanaConnect} (and the underlying framework-agnostic\n * connect-flow store) for browser apps. This entry point is browser-safe and\n * imports nothing Node-only. `react` is a peer dependency.\n *\n * @example\n * ```tsx\n * \"use client\";\n * import { useDirectVanaConnect } from \"@opendatalabs/vana-sdk/react\";\n *\n * export function ConnectNotesButton() {\n * const connect = useDirectVanaConnect({\n * createRequest: () => fetch(\"/api/vana/request\", { method: \"POST\" }).then((r) => r.json()),\n * getStatus: (id) => fetch(`/api/vana/status?requestId=${id}`).then((r) => r.json()),\n * readResult: (id) => fetch(`/api/vana/data?requestId=${id}`).then((r) => r.json()),\n * });\n * const { state, start } = connect;\n * return (\n * <div>\n * <button disabled={state.type !== \"idle\"} onClick={start} type=\"button\">\n * {state.type === \"idle\" ? \"Connect Apple Notes\" : \"Connecting...\"}\n * </button>\n * {state.type === \"awaiting_approval\" && (\n * // Fallback link: if the browser blocked the approval popup, the user\n * // can still open it manually instead of the flow silently hanging.\n * <a href={state.request.approvalUrl} target=\"_blank\" rel=\"noreferrer\">\n * {state.popupBlocked ? \"Popup blocked — open approval\" : \"Open approval\"}\n * </a>\n * )}\n * {state.type === \"ready_to_open\" && (\n * // Mobile deep Direct: render the HTTPS continuation as a primary tap.\n * // The SDK keeps polling in this tab; if the tab is lost, restart.\n * <a href={state.mobileContinuationUrl} target=\"_blank\" rel=\"noreferrer\">\n * Open Vana\n * </a>\n * )}\n * </div>\n * );\n * }\n * ```\n *\n * @category Direct\n * @module react\n */\n\nexport {\n useDirectVanaConnect,\n type UseDirectVanaConnectOptions,\n type UseDirectVanaConnectResult,\n} from \"./direct/use-direct-vana-connect\";\n\n// Framework-agnostic store (usable without React).\nexport {\n createDirectConnectFlow,\n type ConnectWindow,\n type DirectBrowserPlatform,\n type DirectBrowserPlatformPolicy,\n type DirectConnectFlow,\n type DirectConnectState,\n type DirectConnectOptions,\n type DirectConnectTransports,\n} from \"./direct/connect-flow\";\n\n// Shared types useful when typing the transports.\nexport type {\n ForegroundDelivery,\n AccessRequest,\n AccessRequestStatus,\n AccessRequestStatusValue,\n ApprovedDataResult,\n} from \"./direct/types\";\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgDA,qCAIO;AAGP,
|
|
1
|
+
{"version":3,"sources":["../src/react.ts"],"sourcesContent":["/**\n * React entry point for the Vana SDK direct Data Portability flow.\n *\n * @remarks\n * Exposes {@link useDirectVanaConnect} (and the underlying framework-agnostic\n * connect-flow store) for browser apps. This entry point is browser-safe and\n * imports nothing Node-only. `react` is a peer dependency.\n *\n * @example\n * ```tsx\n * \"use client\";\n * import { useDirectVanaConnect } from \"@opendatalabs/vana-sdk/react\";\n *\n * export function ConnectNotesButton() {\n * const connect = useDirectVanaConnect({\n * createRequest: () => fetch(\"/api/vana/request\", { method: \"POST\" }).then((r) => r.json()),\n * getStatus: (id) => fetch(`/api/vana/status?requestId=${id}`).then((r) => r.json()),\n * readResult: (id) => fetch(`/api/vana/data?requestId=${id}`).then((r) => r.json()),\n * });\n * const { state, start } = connect;\n * return (\n * <div>\n * <button disabled={state.type !== \"idle\"} onClick={start} type=\"button\">\n * {state.type === \"idle\" ? \"Connect Apple Notes\" : \"Connecting...\"}\n * </button>\n * {state.type === \"awaiting_approval\" && (\n * // Fallback link: if the browser blocked the approval popup, the user\n * // can still open it manually instead of the flow silently hanging.\n * <a href={state.request.approvalUrl} target=\"_blank\" rel=\"noreferrer\">\n * {state.popupBlocked ? \"Popup blocked — open approval\" : \"Open approval\"}\n * </a>\n * )}\n * {state.type === \"ready_to_open\" && (\n * // Mobile deep Direct: render the HTTPS continuation as a primary tap.\n * // The SDK keeps polling in this tab; if the tab is lost, restart.\n * <a href={state.mobileContinuationUrl} target=\"_blank\" rel=\"noreferrer\">\n * Open Vana\n * </a>\n * )}\n * </div>\n * );\n * }\n * ```\n *\n * @category Direct\n * @module react\n */\n\nexport {\n useDirectVanaConnect,\n type UseDirectVanaConnectOptions,\n type UseDirectVanaConnectResult,\n} from \"./direct/use-direct-vana-connect\";\n\n// Framework-agnostic store (usable without React).\nexport {\n createDirectConnectFlow,\n type ConnectWindow,\n type DirectBrowserPlatform,\n type DirectBrowserPlatformPolicy,\n type DirectConnectFlow,\n type DirectConnectRetryOutcome,\n type DirectConnectState,\n type DirectConnectOptions,\n type DirectConnectTransports,\n} from \"./direct/connect-flow\";\n\n// Shared types useful when typing the transports.\nexport type {\n ForegroundDelivery,\n AccessRequest,\n AccessRequestStatus,\n AccessRequestStatusValue,\n ApprovedDataResult,\n} from \"./direct/types\";\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgDA,qCAIO;AAGP,0BAUO;","names":[]}
|
package/dist/react.d.ts
CHANGED
|
@@ -46,5 +46,5 @@
|
|
|
46
46
|
* @module react
|
|
47
47
|
*/
|
|
48
48
|
export { useDirectVanaConnect, type UseDirectVanaConnectOptions, type UseDirectVanaConnectResult, } from "./direct/use-direct-vana-connect.js";
|
|
49
|
-
export { createDirectConnectFlow, type ConnectWindow, type DirectBrowserPlatform, type DirectBrowserPlatformPolicy, type DirectConnectFlow, type DirectConnectState, type DirectConnectOptions, type DirectConnectTransports, } from "./direct/connect-flow.js";
|
|
49
|
+
export { createDirectConnectFlow, type ConnectWindow, type DirectBrowserPlatform, type DirectBrowserPlatformPolicy, type DirectConnectFlow, type DirectConnectRetryOutcome, type DirectConnectState, type DirectConnectOptions, type DirectConnectTransports, } from "./direct/connect-flow.js";
|
|
50
50
|
export type { ForegroundDelivery, AccessRequest, AccessRequestStatus, AccessRequestStatusValue, ApprovedDataResult, } from "./direct/types.js";
|
package/dist/react.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/react.ts"],"sourcesContent":["/**\n * React entry point for the Vana SDK direct Data Portability flow.\n *\n * @remarks\n * Exposes {@link useDirectVanaConnect} (and the underlying framework-agnostic\n * connect-flow store) for browser apps. This entry point is browser-safe and\n * imports nothing Node-only. `react` is a peer dependency.\n *\n * @example\n * ```tsx\n * \"use client\";\n * import { useDirectVanaConnect } from \"@opendatalabs/vana-sdk/react\";\n *\n * export function ConnectNotesButton() {\n * const connect = useDirectVanaConnect({\n * createRequest: () => fetch(\"/api/vana/request\", { method: \"POST\" }).then((r) => r.json()),\n * getStatus: (id) => fetch(`/api/vana/status?requestId=${id}`).then((r) => r.json()),\n * readResult: (id) => fetch(`/api/vana/data?requestId=${id}`).then((r) => r.json()),\n * });\n * const { state, start } = connect;\n * return (\n * <div>\n * <button disabled={state.type !== \"idle\"} onClick={start} type=\"button\">\n * {state.type === \"idle\" ? \"Connect Apple Notes\" : \"Connecting...\"}\n * </button>\n * {state.type === \"awaiting_approval\" && (\n * // Fallback link: if the browser blocked the approval popup, the user\n * // can still open it manually instead of the flow silently hanging.\n * <a href={state.request.approvalUrl} target=\"_blank\" rel=\"noreferrer\">\n * {state.popupBlocked ? \"Popup blocked — open approval\" : \"Open approval\"}\n * </a>\n * )}\n * {state.type === \"ready_to_open\" && (\n * // Mobile deep Direct: render the HTTPS continuation as a primary tap.\n * // The SDK keeps polling in this tab; if the tab is lost, restart.\n * <a href={state.mobileContinuationUrl} target=\"_blank\" rel=\"noreferrer\">\n * Open Vana\n * </a>\n * )}\n * </div>\n * );\n * }\n * ```\n *\n * @category Direct\n * @module react\n */\n\nexport {\n useDirectVanaConnect,\n type UseDirectVanaConnectOptions,\n type UseDirectVanaConnectResult,\n} from \"./direct/use-direct-vana-connect\";\n\n// Framework-agnostic store (usable without React).\nexport {\n createDirectConnectFlow,\n type ConnectWindow,\n type DirectBrowserPlatform,\n type DirectBrowserPlatformPolicy,\n type DirectConnectFlow,\n type DirectConnectState,\n type DirectConnectOptions,\n type DirectConnectTransports,\n} from \"./direct/connect-flow\";\n\n// Shared types useful when typing the transports.\nexport type {\n ForegroundDelivery,\n AccessRequest,\n AccessRequestStatus,\n AccessRequestStatusValue,\n ApprovedDataResult,\n} from \"./direct/types\";\n"],"mappings":"AAgDA;AAAA,EACE;AAAA,OAGK;AAGP;AAAA,EACE;AAAA,
|
|
1
|
+
{"version":3,"sources":["../src/react.ts"],"sourcesContent":["/**\n * React entry point for the Vana SDK direct Data Portability flow.\n *\n * @remarks\n * Exposes {@link useDirectVanaConnect} (and the underlying framework-agnostic\n * connect-flow store) for browser apps. This entry point is browser-safe and\n * imports nothing Node-only. `react` is a peer dependency.\n *\n * @example\n * ```tsx\n * \"use client\";\n * import { useDirectVanaConnect } from \"@opendatalabs/vana-sdk/react\";\n *\n * export function ConnectNotesButton() {\n * const connect = useDirectVanaConnect({\n * createRequest: () => fetch(\"/api/vana/request\", { method: \"POST\" }).then((r) => r.json()),\n * getStatus: (id) => fetch(`/api/vana/status?requestId=${id}`).then((r) => r.json()),\n * readResult: (id) => fetch(`/api/vana/data?requestId=${id}`).then((r) => r.json()),\n * });\n * const { state, start } = connect;\n * return (\n * <div>\n * <button disabled={state.type !== \"idle\"} onClick={start} type=\"button\">\n * {state.type === \"idle\" ? \"Connect Apple Notes\" : \"Connecting...\"}\n * </button>\n * {state.type === \"awaiting_approval\" && (\n * // Fallback link: if the browser blocked the approval popup, the user\n * // can still open it manually instead of the flow silently hanging.\n * <a href={state.request.approvalUrl} target=\"_blank\" rel=\"noreferrer\">\n * {state.popupBlocked ? \"Popup blocked — open approval\" : \"Open approval\"}\n * </a>\n * )}\n * {state.type === \"ready_to_open\" && (\n * // Mobile deep Direct: render the HTTPS continuation as a primary tap.\n * // The SDK keeps polling in this tab; if the tab is lost, restart.\n * <a href={state.mobileContinuationUrl} target=\"_blank\" rel=\"noreferrer\">\n * Open Vana\n * </a>\n * )}\n * </div>\n * );\n * }\n * ```\n *\n * @category Direct\n * @module react\n */\n\nexport {\n useDirectVanaConnect,\n type UseDirectVanaConnectOptions,\n type UseDirectVanaConnectResult,\n} from \"./direct/use-direct-vana-connect\";\n\n// Framework-agnostic store (usable without React).\nexport {\n createDirectConnectFlow,\n type ConnectWindow,\n type DirectBrowserPlatform,\n type DirectBrowserPlatformPolicy,\n type DirectConnectFlow,\n type DirectConnectRetryOutcome,\n type DirectConnectState,\n type DirectConnectOptions,\n type DirectConnectTransports,\n} from \"./direct/connect-flow\";\n\n// Shared types useful when typing the transports.\nexport type {\n ForegroundDelivery,\n AccessRequest,\n AccessRequestStatus,\n AccessRequestStatusValue,\n ApprovedDataResult,\n} from \"./direct/types\";\n"],"mappings":"AAgDA;AAAA,EACE;AAAA,OAGK;AAGP;AAAA,EACE;AAAA,OASK;","names":[]}
|
|
@@ -23,7 +23,7 @@ __export(client_exports, {
|
|
|
23
23
|
});
|
|
24
24
|
module.exports = __toCommonJS(client_exports);
|
|
25
25
|
var import_endpoints = require("./endpoints");
|
|
26
|
-
var import_errors = require("./errors");
|
|
26
|
+
var import_errors = require("./errors.cjs");
|
|
27
27
|
var import_signing = require("./signing");
|
|
28
28
|
const DEFAULT_POLL_INTERVAL_MS = 2e3;
|
|
29
29
|
const DEFAULT_POLL_TIMEOUT_MS = 15 * 6e4;
|
|
@@ -21,7 +21,7 @@ __export(errors_exports, {
|
|
|
21
21
|
SessionRelayError: () => SessionRelayError
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(errors_exports);
|
|
24
|
-
var import_errors = require("../errors");
|
|
24
|
+
var import_errors = require("../errors.cjs");
|
|
25
25
|
class SessionRelayError extends import_errors.VanaError {
|
|
26
26
|
constructor(message, details = {}) {
|
|
27
27
|
super(message, details.relayErrorCode ?? "SESSION_RELAY_ERROR");
|
|
@@ -29,7 +29,7 @@ __export(session_relay_exports, {
|
|
|
29
29
|
});
|
|
30
30
|
module.exports = __toCommonJS(session_relay_exports);
|
|
31
31
|
var import_endpoints = require("./endpoints");
|
|
32
|
-
var import_errors = require("./errors");
|
|
32
|
+
var import_errors = require("./errors.cjs");
|
|
33
33
|
var import_signing = require("./signing");
|
|
34
34
|
var import_client = require("./client");
|
|
35
35
|
// Annotate the CommonJS export names for ESM import in node:
|