@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
|
@@ -22,12 +22,13 @@ __export(jobs_client_exports, {
|
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(jobs_client_exports);
|
|
24
24
|
var import_node_crypto = require("node:crypto");
|
|
25
|
+
var import_sha2 = require("@noble/hashes/sha2");
|
|
25
26
|
var import_viem = require("viem");
|
|
26
27
|
var import_accounts = require("viem/accounts");
|
|
27
28
|
var import_web3_signed_builder = require("../auth/web3-signed-builder");
|
|
28
29
|
var import_node = require("../crypto/ecies/node");
|
|
29
30
|
var import_job = require("../crypto/envelope/job");
|
|
30
|
-
var import_errors = require("../errors");
|
|
31
|
+
var import_errors = require("../errors.cjs");
|
|
31
32
|
var import_identity = require("./identity");
|
|
32
33
|
var import_jobs = require("./jobs");
|
|
33
34
|
var import_write_signer = require("./write-signer");
|
|
@@ -193,7 +194,7 @@ async function rejectedResponse(response, fallback) {
|
|
|
193
194
|
return new import_errors.JobRejectedError(message, response.status, parsed.code, details);
|
|
194
195
|
}
|
|
195
196
|
function requireStatus(value, status) {
|
|
196
|
-
if (!isRecord(value) || !isNonEmptyString(value["jobId"]) || typeof value["state"] !== "string" || !import_jobs.JOB_STATES.includes(value["state"]) || !isNonEmptyString(value["owner"]) || !isNonEmptyString(value["grantId"]) || !isNonEmptyString(value["scope"]) || !isNonEmptyString(value["operation"]) || !import_jobs.JOB_OPERATIONS.includes(value["operation"])) {
|
|
197
|
+
if (!isRecord(value) || !isNonEmptyString(value["jobId"]) || typeof value["state"] !== "string" || !import_jobs.JOB_STATES.includes(value["state"]) || !isNonEmptyString(value["owner"]) || !isNonEmptyString(value["grantId"]) || !isNonEmptyString(value["scope"]) || !isNonEmptyString(value["operation"]) || !import_jobs.JOB_OPERATIONS.includes(value["operation"]) || value["state"] === "completed" && !isResultHandle(value["result"])) {
|
|
197
198
|
throw new import_errors.JobRejectedError(
|
|
198
199
|
"Gateway response did not contain a job status",
|
|
199
200
|
status
|
|
@@ -204,6 +205,17 @@ function requireStatus(value, status) {
|
|
|
204
205
|
function isNonEmptyString(value) {
|
|
205
206
|
return typeof value === "string" && value.length > 0;
|
|
206
207
|
}
|
|
208
|
+
function isResultHandle(value) {
|
|
209
|
+
if (!isRecord(value) || !isNonEmptyString(value["objectKey"]) || !isNonEmptyString(value["url"]) || typeof value["size"] !== "number" || !Number.isInteger(value["size"]) || value["size"] < 0 || typeof value["hash"] !== "string" || !/^0x[0-9a-fA-F]{64}$/.test(value["hash"]) || typeof value["expiresAt"] !== "string" || Number.isNaN(Date.parse(value["expiresAt"]))) {
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
try {
|
|
213
|
+
const url = new URL(value["url"]);
|
|
214
|
+
return url.protocol === "https:" || url.protocol === "http:";
|
|
215
|
+
} catch {
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
207
219
|
function hasMatchingIdentityKey(value) {
|
|
208
220
|
const address = value["address"];
|
|
209
221
|
const publicKey = value["publicKey"];
|
|
@@ -449,31 +461,51 @@ function createJobsClient(options) {
|
|
|
449
461
|
await sleep(Math.min(pollMs, remaining));
|
|
450
462
|
}
|
|
451
463
|
},
|
|
452
|
-
async openResult(
|
|
453
|
-
if (!
|
|
464
|
+
async openResult(handle, openOptions) {
|
|
465
|
+
if (!privateKey) {
|
|
454
466
|
throw new import_errors.JobRejectedError(
|
|
455
|
-
"
|
|
456
|
-
|
|
457
|
-
|
|
467
|
+
"openResult requires createJobsClient({ builderPrivateKey })"
|
|
468
|
+
);
|
|
469
|
+
}
|
|
470
|
+
const response = await request(
|
|
471
|
+
handle.url,
|
|
472
|
+
{ method: GET_METHOD },
|
|
473
|
+
"Job result fetch"
|
|
474
|
+
);
|
|
475
|
+
if (!response.ok) {
|
|
476
|
+
throw await rejectedResponse(response, "Job result fetch rejected");
|
|
477
|
+
}
|
|
478
|
+
let bytes;
|
|
479
|
+
try {
|
|
480
|
+
bytes = new Uint8Array(await response.arrayBuffer());
|
|
481
|
+
} catch (error) {
|
|
482
|
+
throw new import_errors.JobTransportError(
|
|
483
|
+
`Job result fetch failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
484
|
+
error
|
|
485
|
+
);
|
|
486
|
+
}
|
|
487
|
+
const actualHash = (0, import_viem.bytesToHex)((0, import_sha2.sha256)(bytes));
|
|
488
|
+
if (actualHash.toLowerCase() !== handle.hash.toLowerCase()) {
|
|
489
|
+
throw new import_errors.JobResultIntegrityError(
|
|
490
|
+
"Fetched job result hash does not match its handle",
|
|
458
491
|
{
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
maxInlineResultBytes: import_jobs.MAX_INLINE_RESULT_BYTES
|
|
492
|
+
objectKey: handle.objectKey,
|
|
493
|
+
expectedHash: handle.hash,
|
|
494
|
+
actualHash
|
|
463
495
|
}
|
|
464
496
|
);
|
|
465
497
|
}
|
|
466
|
-
if (
|
|
467
|
-
throw new import_errors.
|
|
468
|
-
"
|
|
498
|
+
if (bytes.length !== handle.size) {
|
|
499
|
+
throw new import_errors.JobResultIntegrityError(
|
|
500
|
+
"Fetched job result size does not match its handle",
|
|
501
|
+
{
|
|
502
|
+
objectKey: handle.objectKey,
|
|
503
|
+
expectedSize: handle.size,
|
|
504
|
+
actualSize: bytes.length
|
|
505
|
+
}
|
|
469
506
|
);
|
|
470
507
|
}
|
|
471
|
-
return (0, import_job.openJobResult)(
|
|
472
|
-
job.resultCiphertext,
|
|
473
|
-
privateKey,
|
|
474
|
-
ecies,
|
|
475
|
-
openOptions.expect
|
|
476
|
-
);
|
|
508
|
+
return (0, import_job.openJobResult)(bytes, privateKey, ecies, openOptions.expect);
|
|
477
509
|
},
|
|
478
510
|
async readRaw(params) {
|
|
479
511
|
const submitted = await client.submitRawRead(params);
|
|
@@ -481,7 +513,27 @@ function createJobsClient(options) {
|
|
|
481
513
|
timeoutMs: params.timeoutMs,
|
|
482
514
|
pollMs: params.pollMs
|
|
483
515
|
});
|
|
484
|
-
|
|
516
|
+
if (job.state !== "completed") {
|
|
517
|
+
throw new import_errors.JobRejectedError(
|
|
518
|
+
`Job ${job.jobId} ended in state ${job.state}${job.failureReason ? `: ${job.failureReason}` : ""}`,
|
|
519
|
+
void 0,
|
|
520
|
+
null,
|
|
521
|
+
{
|
|
522
|
+
jobId: job.jobId,
|
|
523
|
+
state: job.state,
|
|
524
|
+
...job.failureReason ? { failureReason: job.failureReason } : {}
|
|
525
|
+
}
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
if (!job.result) {
|
|
529
|
+
throw new import_errors.JobRejectedError(
|
|
530
|
+
"Completed job status does not include a result handle",
|
|
531
|
+
void 0,
|
|
532
|
+
null,
|
|
533
|
+
{ jobId: job.jobId, state: job.state }
|
|
534
|
+
);
|
|
535
|
+
}
|
|
536
|
+
return client.openResult(job.result, {
|
|
485
537
|
expect: {
|
|
486
538
|
jobId: submitted.jobId,
|
|
487
539
|
scope: params.scope,
|
|
@@ -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":[]}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { type Address, type Hex, type LocalAccount } from "viem";
|
|
12
12
|
import type { ECIESProvider } from "../crypto/ecies/interface.js";
|
|
13
|
-
import { type JobResult, type JobState, type JobStatus } from "./jobs.js";
|
|
13
|
+
import { type JobResult, type JobState, type JobStatus, type ResultHandle } from "./jobs.js";
|
|
14
14
|
import { type WriteSignerSource } from "./write-signer.js";
|
|
15
15
|
/** A viem local account that exposes the public key required by job requests. */
|
|
16
16
|
export type JobsBuilderAccount = Extract<LocalAccount, WriteSignerSource>;
|
|
@@ -108,17 +108,19 @@ export interface JobsClient {
|
|
|
108
108
|
*/
|
|
109
109
|
waitForJob(jobId: string, options?: WaitForJobOptions): Promise<JobStatus>;
|
|
110
110
|
/**
|
|
111
|
-
*
|
|
111
|
+
* Fetch, decrypt, and validate a job result from object storage.
|
|
112
112
|
*
|
|
113
|
-
* @param
|
|
113
|
+
* @param handle - Object-storage location and integrity metadata.
|
|
114
114
|
* @param options - Expected plaintext job, scope, and version bindings.
|
|
115
115
|
* @returns The validated plaintext job result.
|
|
116
116
|
* @remarks Decryption requires `builderPrivateKey`; `builderAccount` alone
|
|
117
117
|
* only supports submission, status reads, and waiting.
|
|
118
|
-
* @throws {JobRejectedError} When
|
|
118
|
+
* @throws {JobRejectedError} When a raw private key is unavailable.
|
|
119
|
+
* @throws {JobTransportError} When fetching the result object fails.
|
|
120
|
+
* @throws {JobResultIntegrityError} When the fetched bytes mismatch the handle.
|
|
119
121
|
* @throws {JobEnvelopeError} When decrypted protocol fields or bindings differ.
|
|
120
122
|
*/
|
|
121
|
-
openResult(
|
|
123
|
+
openResult(handle: ResultHandle, options: OpenJobResultOptions): Promise<JobResult>;
|
|
122
124
|
/**
|
|
123
125
|
* Submit, wait for, and decrypt one raw read.
|
|
124
126
|
*
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { sha256 } from "@noble/hashes/sha2";
|
|
2
3
|
import {
|
|
4
|
+
bytesToHex,
|
|
3
5
|
isAddress,
|
|
4
6
|
isAddressEqual,
|
|
5
7
|
isHex
|
|
@@ -21,6 +23,7 @@ import {
|
|
|
21
23
|
JobIdTakenError,
|
|
22
24
|
JobNotFoundError,
|
|
23
25
|
JobRejectedError,
|
|
26
|
+
JobResultIntegrityError,
|
|
24
27
|
JobRequestTooLargeError,
|
|
25
28
|
JobTimeoutError,
|
|
26
29
|
JobTransportError,
|
|
@@ -33,7 +36,6 @@ import {
|
|
|
33
36
|
JOB_OPERATIONS,
|
|
34
37
|
JOB_PROTOCOL_VERSION,
|
|
35
38
|
JOB_STATES,
|
|
36
|
-
MAX_INLINE_RESULT_BYTES,
|
|
37
39
|
MAX_JOB_DEADLINE_SECONDS,
|
|
38
40
|
MAX_WAIT_SECONDS
|
|
39
41
|
} from "./jobs.js";
|
|
@@ -200,7 +202,7 @@ async function rejectedResponse(response, fallback) {
|
|
|
200
202
|
return new JobRejectedError(message, response.status, parsed.code, details);
|
|
201
203
|
}
|
|
202
204
|
function requireStatus(value, status) {
|
|
203
|
-
if (!isRecord(value) || !isNonEmptyString(value["jobId"]) || typeof value["state"] !== "string" || !JOB_STATES.includes(value["state"]) || !isNonEmptyString(value["owner"]) || !isNonEmptyString(value["grantId"]) || !isNonEmptyString(value["scope"]) || !isNonEmptyString(value["operation"]) || !JOB_OPERATIONS.includes(value["operation"])) {
|
|
205
|
+
if (!isRecord(value) || !isNonEmptyString(value["jobId"]) || typeof value["state"] !== "string" || !JOB_STATES.includes(value["state"]) || !isNonEmptyString(value["owner"]) || !isNonEmptyString(value["grantId"]) || !isNonEmptyString(value["scope"]) || !isNonEmptyString(value["operation"]) || !JOB_OPERATIONS.includes(value["operation"]) || value["state"] === "completed" && !isResultHandle(value["result"])) {
|
|
204
206
|
throw new JobRejectedError(
|
|
205
207
|
"Gateway response did not contain a job status",
|
|
206
208
|
status
|
|
@@ -211,6 +213,17 @@ function requireStatus(value, status) {
|
|
|
211
213
|
function isNonEmptyString(value) {
|
|
212
214
|
return typeof value === "string" && value.length > 0;
|
|
213
215
|
}
|
|
216
|
+
function isResultHandle(value) {
|
|
217
|
+
if (!isRecord(value) || !isNonEmptyString(value["objectKey"]) || !isNonEmptyString(value["url"]) || typeof value["size"] !== "number" || !Number.isInteger(value["size"]) || value["size"] < 0 || typeof value["hash"] !== "string" || !/^0x[0-9a-fA-F]{64}$/.test(value["hash"]) || typeof value["expiresAt"] !== "string" || Number.isNaN(Date.parse(value["expiresAt"]))) {
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
try {
|
|
221
|
+
const url = new URL(value["url"]);
|
|
222
|
+
return url.protocol === "https:" || url.protocol === "http:";
|
|
223
|
+
} catch {
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
214
227
|
function hasMatchingIdentityKey(value) {
|
|
215
228
|
const address = value["address"];
|
|
216
229
|
const publicKey = value["publicKey"];
|
|
@@ -456,31 +469,51 @@ function createJobsClient(options) {
|
|
|
456
469
|
await sleep(Math.min(pollMs, remaining));
|
|
457
470
|
}
|
|
458
471
|
},
|
|
459
|
-
async openResult(
|
|
460
|
-
if (!
|
|
472
|
+
async openResult(handle, openOptions) {
|
|
473
|
+
if (!privateKey) {
|
|
461
474
|
throw new JobRejectedError(
|
|
462
|
-
"
|
|
463
|
-
|
|
464
|
-
|
|
475
|
+
"openResult requires createJobsClient({ builderPrivateKey })"
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
const response = await request(
|
|
479
|
+
handle.url,
|
|
480
|
+
{ method: GET_METHOD },
|
|
481
|
+
"Job result fetch"
|
|
482
|
+
);
|
|
483
|
+
if (!response.ok) {
|
|
484
|
+
throw await rejectedResponse(response, "Job result fetch rejected");
|
|
485
|
+
}
|
|
486
|
+
let bytes;
|
|
487
|
+
try {
|
|
488
|
+
bytes = new Uint8Array(await response.arrayBuffer());
|
|
489
|
+
} catch (error) {
|
|
490
|
+
throw new JobTransportError(
|
|
491
|
+
`Job result fetch failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
492
|
+
error
|
|
493
|
+
);
|
|
494
|
+
}
|
|
495
|
+
const actualHash = bytesToHex(sha256(bytes));
|
|
496
|
+
if (actualHash.toLowerCase() !== handle.hash.toLowerCase()) {
|
|
497
|
+
throw new JobResultIntegrityError(
|
|
498
|
+
"Fetched job result hash does not match its handle",
|
|
465
499
|
{
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
maxInlineResultBytes: MAX_INLINE_RESULT_BYTES
|
|
500
|
+
objectKey: handle.objectKey,
|
|
501
|
+
expectedHash: handle.hash,
|
|
502
|
+
actualHash
|
|
470
503
|
}
|
|
471
504
|
);
|
|
472
505
|
}
|
|
473
|
-
if (
|
|
474
|
-
throw new
|
|
475
|
-
"
|
|
506
|
+
if (bytes.length !== handle.size) {
|
|
507
|
+
throw new JobResultIntegrityError(
|
|
508
|
+
"Fetched job result size does not match its handle",
|
|
509
|
+
{
|
|
510
|
+
objectKey: handle.objectKey,
|
|
511
|
+
expectedSize: handle.size,
|
|
512
|
+
actualSize: bytes.length
|
|
513
|
+
}
|
|
476
514
|
);
|
|
477
515
|
}
|
|
478
|
-
return openJobResult(
|
|
479
|
-
job.resultCiphertext,
|
|
480
|
-
privateKey,
|
|
481
|
-
ecies,
|
|
482
|
-
openOptions.expect
|
|
483
|
-
);
|
|
516
|
+
return openJobResult(bytes, privateKey, ecies, openOptions.expect);
|
|
484
517
|
},
|
|
485
518
|
async readRaw(params) {
|
|
486
519
|
const submitted = await client.submitRawRead(params);
|
|
@@ -488,7 +521,27 @@ function createJobsClient(options) {
|
|
|
488
521
|
timeoutMs: params.timeoutMs,
|
|
489
522
|
pollMs: params.pollMs
|
|
490
523
|
});
|
|
491
|
-
|
|
524
|
+
if (job.state !== "completed") {
|
|
525
|
+
throw new JobRejectedError(
|
|
526
|
+
`Job ${job.jobId} ended in state ${job.state}${job.failureReason ? `: ${job.failureReason}` : ""}`,
|
|
527
|
+
void 0,
|
|
528
|
+
null,
|
|
529
|
+
{
|
|
530
|
+
jobId: job.jobId,
|
|
531
|
+
state: job.state,
|
|
532
|
+
...job.failureReason ? { failureReason: job.failureReason } : {}
|
|
533
|
+
}
|
|
534
|
+
);
|
|
535
|
+
}
|
|
536
|
+
if (!job.result) {
|
|
537
|
+
throw new JobRejectedError(
|
|
538
|
+
"Completed job status does not include a result handle",
|
|
539
|
+
void 0,
|
|
540
|
+
null,
|
|
541
|
+
{ jobId: job.jobId, state: job.state }
|
|
542
|
+
);
|
|
543
|
+
}
|
|
544
|
+
return client.openResult(job.result, {
|
|
492
545
|
expect: {
|
|
493
546
|
jobId: submitted.jobId,
|
|
494
547
|
scope: params.scope,
|