@opendatalabs/vana-sdk 3.15.0 → 3.17.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.
Files changed (50) hide show
  1. package/README.md +107 -0
  2. package/dist/errors.cjs +94 -2
  3. package/dist/errors.cjs.map +1 -1
  4. package/dist/errors.d.ts +123 -0
  5. package/dist/errors.js +82 -1
  6. package/dist/errors.js.map +1 -1
  7. package/dist/index.browser.d.ts +4 -0
  8. package/dist/index.browser.js +1155 -14
  9. package/dist/index.browser.js.map +4 -4
  10. package/dist/index.node.cjs +1205 -15
  11. package/dist/index.node.cjs.map +4 -4
  12. package/dist/index.node.d.ts +4 -0
  13. package/dist/index.node.js +1155 -14
  14. package/dist/index.node.js.map +4 -4
  15. package/dist/protocol/gateway.cjs +16 -2
  16. package/dist/protocol/gateway.cjs.map +1 -1
  17. package/dist/protocol/gateway.d.ts +2 -0
  18. package/dist/protocol/gateway.js +16 -2
  19. package/dist/protocol/gateway.js.map +1 -1
  20. package/dist/protocol/lineage.cjs +287 -0
  21. package/dist/protocol/lineage.cjs.map +1 -0
  22. package/dist/protocol/lineage.d.ts +228 -0
  23. package/dist/protocol/lineage.js +258 -0
  24. package/dist/protocol/lineage.js.map +1 -0
  25. package/dist/protocol/lineage.test.d.ts +1 -0
  26. package/dist/protocol/personal-server-error-body.cjs +57 -0
  27. package/dist/protocol/personal-server-error-body.cjs.map +1 -0
  28. package/dist/protocol/personal-server-error-body.d.ts +18 -0
  29. package/dist/protocol/personal-server-error-body.js +32 -0
  30. package/dist/protocol/personal-server-error-body.js.map +1 -0
  31. package/dist/protocol/personal-server-write.cjs +623 -0
  32. package/dist/protocol/personal-server-write.cjs.map +1 -0
  33. package/dist/protocol/personal-server-write.d.ts +284 -0
  34. package/dist/protocol/personal-server-write.js +601 -0
  35. package/dist/protocol/personal-server-write.js.map +1 -0
  36. package/dist/protocol/personal-server-write.test.d.ts +1 -0
  37. package/dist/protocol/scope-actions.cjs +185 -0
  38. package/dist/protocol/scope-actions.cjs.map +1 -0
  39. package/dist/protocol/scope-actions.d.ts +145 -0
  40. package/dist/protocol/scope-actions.js +154 -0
  41. package/dist/protocol/scope-actions.js.map +1 -0
  42. package/dist/protocol/scope-actions.test.d.ts +1 -0
  43. package/dist/protocol/write-signer.cjs +67 -0
  44. package/dist/protocol/write-signer.cjs.map +1 -0
  45. package/dist/protocol/write-signer.d.ts +59 -0
  46. package/dist/protocol/write-signer.js +43 -0
  47. package/dist/protocol/write-signer.js.map +1 -0
  48. package/dist/protocol/write-signer.test.d.ts +1 -0
  49. package/dist/tests/mock-personal-server.d.ts +127 -0
  50. package/package.json +1 -1
@@ -0,0 +1,623 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var personal_server_write_exports = {};
20
+ __export(personal_server_write_exports, {
21
+ LINEAGE_FIELD: () => LINEAGE_FIELD,
22
+ LINEAGE_KEY: () => LINEAGE_KEY,
23
+ MAX_LINEAGE_SOURCES: () => MAX_LINEAGE_SOURCES,
24
+ RESERVED_WRITE_KEYS: () => RESERVED_WRITE_KEYS,
25
+ WRITER_ATTRIBUTION_KEY: () => WRITER_ATTRIBUTION_KEY,
26
+ WRITE_CONTENT_DISPOSITION_HEADER: () => WRITE_CONTENT_DISPOSITION_HEADER,
27
+ WRITE_FILENAME_HEADER: () => WRITE_FILENAME_HEADER,
28
+ WRITE_METADATA_HEADER: () => WRITE_METADATA_HEADER,
29
+ WRITE_SESSION_PATH: () => WRITE_SESSION_PATH,
30
+ WRITE_SIGNATURE_HEADER: () => WRITE_SIGNATURE_HEADER,
31
+ binaryWriteSignedBytes: () => binaryWriteSignedBytes,
32
+ encodeWriteMetadataHeader: () => encodeWriteMetadataHeader,
33
+ normalizeBinaryMimeType: () => normalizeBinaryMimeType,
34
+ openWriteSession: () => openWriteSession,
35
+ parseWriteMetadataHeader: () => parseWriteMetadataHeader,
36
+ sessionCoversScope: () => sessionCoversScope,
37
+ writeData: () => writeData,
38
+ writePersonalServerData: () => writePersonalServerData
39
+ });
40
+ module.exports = __toCommonJS(personal_server_write_exports);
41
+ var import_sha2 = require("@noble/hashes/sha2");
42
+ var import_viem = require("viem");
43
+ var import_zod = require("zod");
44
+ var import_web3_signed_builder = require("../auth/web3-signed-builder");
45
+ var import_errors = require("../errors");
46
+ var import_encoding = require("../utils/encoding");
47
+ var import_data_file = require("./data-file");
48
+ var import_lineage = require("./lineage");
49
+ var import_personal_server_error_body = require("./personal-server-error-body");
50
+ var import_scopes = require("./scopes");
51
+ var import_write_signer = require("./write-signer");
52
+ const WRITE_SESSION_PATH = "/v1/write/session";
53
+ const WRITE_SIGNATURE_HEADER = "X-Vana-Write-Signature";
54
+ const WRITE_METADATA_HEADER = "X-Vana-Metadata";
55
+ const LINEAGE_FIELD = "lineage";
56
+ const MAX_LINEAGE_SOURCES = 256;
57
+ const WRITE_FILENAME_HEADER = "X-Filename";
58
+ const WRITE_CONTENT_DISPOSITION_HEADER = "Content-Disposition";
59
+ const WRITER_ATTRIBUTION_KEY = "$writtenBy";
60
+ const LINEAGE_KEY = "$lineage";
61
+ const RESERVED_WRITE_KEYS = [
62
+ WRITER_ATTRIBUTION_KEY,
63
+ LINEAGE_KEY
64
+ ];
65
+ const WriteDataResultSchema = import_data_file.IngestResponseSchema.extend({
66
+ // Present when the write carried lineage: the validated, lowercased ids.
67
+ lineage: import_zod.z.object({ sources: import_zod.z.array(import_zod.z.string()) }).optional()
68
+ });
69
+ const WriteSessionResponseSchema = import_zod.z.object({
70
+ access_token: import_zod.z.string().min(1),
71
+ token_type: import_zod.z.string(),
72
+ expires_in: import_zod.z.number().nonnegative(),
73
+ scope: import_zod.z.string()
74
+ });
75
+ function normalizeBaseUrl(url) {
76
+ return url.replace(/\/+$/, "");
77
+ }
78
+ function resolveFetch(fetchFn) {
79
+ const resolved = fetchFn ?? globalThis.fetch;
80
+ if (resolved === void 0) {
81
+ throw new import_errors.WriteRequestError("No fetch implementation available");
82
+ }
83
+ return resolved;
84
+ }
85
+ function dataPath(scope) {
86
+ return `/v1/data/${encodeURIComponent(scope)}`;
87
+ }
88
+ function errorMessage(err) {
89
+ return err instanceof Error ? err.message : String(err);
90
+ }
91
+ function finiteOr(value, fallback) {
92
+ return typeof value === "number" && Number.isFinite(value) ? value : fallback;
93
+ }
94
+ function sleep(ms) {
95
+ return new Promise((resolve) => setTimeout(resolve, ms));
96
+ }
97
+ const issuedProofIats = /* @__PURE__ */ new Map();
98
+ const issuedProofBuckets = /* @__PURE__ */ new Map();
99
+ let issuedProofIatsPrunedAtSec = 0;
100
+ const WEB3_SIGNED_PROOF_LIFETIME_SECONDS = 300;
101
+ const WEB3_SIGNED_CLOCK_SKEW_SECONDS = 60;
102
+ const PROOF_IAT_RETENTION_SECONDS = WEB3_SIGNED_PROOF_LIFETIME_SECONDS + WEB3_SIGNED_CLOCK_SKEW_SECONDS;
103
+ const PROOF_IAT_MAX_AHEAD_SECONDS = 30;
104
+ function pruneIssuedProofIats(nowSec) {
105
+ if (issuedProofIatsPrunedAtSec === nowSec) return;
106
+ issuedProofIatsPrunedAtSec = nowSec;
107
+ const cutoff = nowSec - PROOF_IAT_RETENTION_SECONDS;
108
+ for (const [sec, keys] of issuedProofBuckets) {
109
+ if (sec >= cutoff) continue;
110
+ for (const key of keys) issuedProofIats.delete(key);
111
+ issuedProofBuckets.delete(sec);
112
+ }
113
+ }
114
+ function setIssuedProofIat(key, iat, previous) {
115
+ if (previous !== void 0) {
116
+ const bucket2 = issuedProofBuckets.get(previous);
117
+ bucket2?.delete(key);
118
+ if (bucket2?.size === 0) issuedProofBuckets.delete(previous);
119
+ }
120
+ issuedProofIats.set(key, iat);
121
+ let bucket = issuedProofBuckets.get(iat);
122
+ if (bucket === void 0) {
123
+ bucket = /* @__PURE__ */ new Set();
124
+ issuedProofBuckets.set(iat, bucket);
125
+ }
126
+ bucket.add(key);
127
+ }
128
+ function nextProofIat(proofKey) {
129
+ const nowSec = Math.floor(Date.now() / 1e3);
130
+ pruneIssuedProofIats(nowSec);
131
+ const last = issuedProofIats.get(proofKey);
132
+ const iat = last === void 0 ? nowSec : Math.max(nowSec, last + 1);
133
+ setIssuedProofIat(proofKey, iat, last);
134
+ const waitSec = iat - nowSec - PROOF_IAT_MAX_AHEAD_SECONDS;
135
+ if (waitSec <= 0) return Promise.resolve(iat);
136
+ return sleep(waitSec * 1e3).then(() => iat);
137
+ }
138
+ function proofKeyFor(parts) {
139
+ return (0, import_viem.bytesToHex)(
140
+ (0, import_sha2.sha256)(
141
+ new TextEncoder().encode(
142
+ JSON.stringify([
143
+ parts.aud,
144
+ parts.method,
145
+ parts.uri,
146
+ parts.grantId,
147
+ parts.signedBytes ? (0, import_viem.bytesToHex)((0, import_sha2.sha256)(parts.signedBytes)) : ""
148
+ ])
149
+ )
150
+ )
151
+ );
152
+ }
153
+ async function sendWithFreshProof(label, fetchFn, options, proofKey, build) {
154
+ const attempts = Math.max(1, Math.floor(finiteOr(options?.attempts, 3)));
155
+ let delayMs = Math.max(0, finiteOr(options?.initialDelayMs, 1e3));
156
+ let lastError;
157
+ for (let attempt = 0; attempt < attempts; attempt++) {
158
+ const { url, init } = await build(await nextProofIat(proofKey));
159
+ try {
160
+ return await fetchFn(url, init);
161
+ } catch (err) {
162
+ lastError = err;
163
+ }
164
+ if (attempt < attempts - 1) {
165
+ await sleep(delayMs);
166
+ delayMs *= 2;
167
+ }
168
+ }
169
+ throw new import_errors.WriteTransportError(
170
+ `${label} failed after ${attempts} attempt(s): ${errorMessage(lastError)}`,
171
+ attempts,
172
+ lastError
173
+ );
174
+ }
175
+ async function openWriteSession(params) {
176
+ const fetchFn = resolveFetch(params.fetch);
177
+ const personalServerUrl = normalizeBaseUrl(params.personalServerUrl);
178
+ const audience = params.audience ?? personalServerUrl;
179
+ const signer = (0, import_write_signer.resolveWriteSigner)(params.signer, { account: params.account });
180
+ const response = await sendWithFreshProof(
181
+ "Write session handshake",
182
+ fetchFn,
183
+ params.retry,
184
+ proofKeyFor({
185
+ aud: audience,
186
+ method: "POST",
187
+ uri: WRITE_SESSION_PATH,
188
+ grantId: params.grantId
189
+ }),
190
+ async (iat) => {
191
+ const headers = new Headers(params.headers);
192
+ headers.set(
193
+ "Authorization",
194
+ await (0, import_web3_signed_builder.buildWeb3SignedHeader)({
195
+ signMessage: signer.signMessage,
196
+ aud: audience,
197
+ method: "POST",
198
+ uri: WRITE_SESSION_PATH,
199
+ grantId: params.grantId,
200
+ iat
201
+ })
202
+ );
203
+ return {
204
+ url: `${personalServerUrl}${WRITE_SESSION_PATH}`,
205
+ init: { method: "POST", headers }
206
+ };
207
+ }
208
+ );
209
+ const mintedAt = Date.now();
210
+ if (!response.ok) {
211
+ const { errorCode, message, details } = await (0, import_personal_server_error_body.readPersonalServerErrorBody)(response);
212
+ throw new import_errors.WriteSessionError(
213
+ message ?? `Write session handshake failed: ${response.status} ${response.statusText}`,
214
+ response.status,
215
+ errorCode,
216
+ details
217
+ );
218
+ }
219
+ let body;
220
+ try {
221
+ body = await response.json();
222
+ } catch (err) {
223
+ throw new import_errors.WriteSessionError(
224
+ "Write session response is not JSON",
225
+ response.status,
226
+ null,
227
+ { cause: errorMessage(err) }
228
+ );
229
+ }
230
+ const parsed = WriteSessionResponseSchema.safeParse(body);
231
+ if (!parsed.success) {
232
+ throw new import_errors.WriteSessionError(
233
+ "Write session response is not a session",
234
+ response.status,
235
+ null,
236
+ { issues: parsed.error.issues }
237
+ );
238
+ }
239
+ if (parsed.data.token_type.toLowerCase() !== "bearer") {
240
+ throw new import_errors.WriteSessionError(
241
+ `Write session token type is not Bearer: ${parsed.data.token_type}`,
242
+ response.status
243
+ );
244
+ }
245
+ return {
246
+ personalServerUrl,
247
+ audience,
248
+ grantId: params.grantId,
249
+ accessToken: parsed.data.access_token,
250
+ expiresAt: mintedAt + parsed.data.expires_in * 1e3,
251
+ writeScopes: parsed.data.scope.split(" ").filter((s) => s.length > 0),
252
+ signer
253
+ };
254
+ }
255
+ function isWriteSession(value) {
256
+ if (!(0, import_personal_server_error_body.isRecord)(value)) return false;
257
+ const signer = value.signer;
258
+ return typeof value.personalServerUrl === "string" && typeof value.audience === "string" && typeof value.grantId === "string" && typeof value.accessToken === "string" && value.accessToken.length > 0 && typeof value.expiresAt === "number" && Number.isFinite(value.expiresAt) && Array.isArray(value.writeScopes) && value.writeScopes.every((scope) => typeof scope === "string") && (0, import_personal_server_error_body.isRecord)(signer) && typeof signer.signMessage === "function";
259
+ }
260
+ function sessionCoversScope(session, scope) {
261
+ if (!(0, import_personal_server_error_body.isRecord)(session) || !Array.isArray(session.writeScopes) || !session.writeScopes.every((pattern) => typeof pattern === "string")) {
262
+ throw new import_errors.WriteRequestError("session must come from openWriteSession");
263
+ }
264
+ return session.writeScopes.some(
265
+ (pattern) => (0, import_scopes.scopeMatchesPattern)(scope, pattern)
266
+ );
267
+ }
268
+ function normalizeBinaryMimeType(contentType) {
269
+ if (!contentType) return "application/octet-stream";
270
+ return contentType.split(";")[0].trim() || "application/octet-stream";
271
+ }
272
+ function parseWriteMetadataHeader(value) {
273
+ if (value === null) return void 0;
274
+ const trimmed = value.trim();
275
+ if (trimmed === "") return void 0;
276
+ try {
277
+ return JSON.parse(trimmed);
278
+ } catch {
279
+ return value;
280
+ }
281
+ }
282
+ function encodeWriteMetadataHeader(metadata) {
283
+ let json;
284
+ try {
285
+ json = JSON.stringify(metadata);
286
+ } catch (err) {
287
+ throw new import_errors.WriteRequestError(
288
+ `metadata is not JSON-serialisable: ${errorMessage(err)}`
289
+ );
290
+ }
291
+ if (typeof json !== "string") {
292
+ throw new import_errors.WriteRequestError("metadata must serialise to JSON");
293
+ }
294
+ return json.replace(
295
+ /[\u007f-\uffff]/g,
296
+ (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, "0")}`
297
+ );
298
+ }
299
+ function binaryWriteSignedBytes(input) {
300
+ const metadata = parseWriteMetadataHeader(input.metadataHeader ?? null);
301
+ const record = {
302
+ $binary: true,
303
+ mimeType: normalizeBinaryMimeType(input.contentType),
304
+ ...input.filename ? { filename: input.filename } : {},
305
+ sizeBytes: input.bytes.length,
306
+ contentHash: (0, import_viem.bytesToHex)((0, import_sha2.sha256)(input.bytes)),
307
+ encoding: "base64",
308
+ content: (0, import_encoding.toBase64)(input.bytes),
309
+ ...metadata !== void 0 ? { metadata } : {}
310
+ };
311
+ return new TextEncoder().encode(JSON.stringify(record));
312
+ }
313
+ const PRINTABLE_ASCII = /^[\x20-\x7e]*$/;
314
+ function setFilenameHeader(headers, filename) {
315
+ if (PRINTABLE_ASCII.test(filename)) {
316
+ headers.set(WRITE_FILENAME_HEADER, filename);
317
+ return;
318
+ }
319
+ headers.set(
320
+ WRITE_CONTENT_DISPOSITION_HEADER,
321
+ `attachment; filename*=UTF-8''${encodeURIComponent(filename)}`
322
+ );
323
+ }
324
+ function assertNoReservedKeys(value, where) {
325
+ for (const key of RESERVED_WRITE_KEYS) {
326
+ if (Object.prototype.hasOwnProperty.call(value, key)) {
327
+ throw new import_errors.WriteRequestError(
328
+ `${where} must not contain the reserved ${key} key; the Personal Server stamps it`,
329
+ { key }
330
+ );
331
+ }
332
+ }
333
+ }
334
+ function isLineagePair(value) {
335
+ return (0, import_personal_server_error_body.isRecord)(value) && typeof value.ownerAddress === "string" && typeof value.scope === "string";
336
+ }
337
+ function normalizeLineage(lineage, derivedScope) {
338
+ if (!Array.isArray(lineage)) {
339
+ throw new import_errors.WriteRequestError("lineage must be an array of data point ids");
340
+ }
341
+ if (lineage.length > MAX_LINEAGE_SOURCES) {
342
+ throw new import_errors.WriteRequestError(
343
+ `lineage lists ${lineage.length} sources; the maximum is ${MAX_LINEAGE_SOURCES}`,
344
+ { max: MAX_LINEAGE_SOURCES, count: lineage.length }
345
+ );
346
+ }
347
+ const seen = /* @__PURE__ */ new Set();
348
+ const sources = [];
349
+ const sourceScopes = [];
350
+ const ownIds = /* @__PURE__ */ new Set();
351
+ for (const entry of lineage) {
352
+ let id;
353
+ if (isLineagePair(entry)) {
354
+ if (!(0, import_viem.isAddress)(entry.ownerAddress, { strict: false })) {
355
+ throw new import_errors.WriteRequestError(
356
+ "lineage source ownerAddress must be an EVM address",
357
+ { ownerAddress: entry.ownerAddress }
358
+ );
359
+ }
360
+ if (entry.scope.length === 0) {
361
+ throw new import_errors.WriteRequestError("lineage source scope is required");
362
+ }
363
+ id = (0, import_lineage.deriveDataPointId)(entry.ownerAddress, entry.scope);
364
+ sourceScopes.push(entry.scope);
365
+ ownIds.add((0, import_lineage.deriveDataPointId)(entry.ownerAddress, derivedScope));
366
+ } else if ((0, import_lineage.isDataPointId)(entry)) {
367
+ id = entry;
368
+ } else {
369
+ throw new import_errors.WriteRequestError(
370
+ "lineage entries must be 32-byte hex data point ids or { ownerAddress, scope } pairs",
371
+ { entry }
372
+ );
373
+ }
374
+ const normalized = id.toLowerCase();
375
+ if (seen.has(normalized)) {
376
+ throw new import_errors.WriteRequestError("lineage must not repeat a data point id", {
377
+ dataPointId: normalized
378
+ });
379
+ }
380
+ seen.add(normalized);
381
+ sources.push(normalized);
382
+ }
383
+ for (const own of ownIds) {
384
+ if (seen.has(own)) {
385
+ throw new import_errors.WriteRequestError(
386
+ "lineage must not cite the record's own data point",
387
+ { dataPointId: own, scope: derivedScope }
388
+ );
389
+ }
390
+ }
391
+ (0, import_lineage.assertDerivedScopeNaming)(derivedScope, sourceScopes);
392
+ return sources;
393
+ }
394
+ function assertNoLineageField(value, where) {
395
+ if (Object.prototype.hasOwnProperty.call(value, LINEAGE_FIELD)) {
396
+ throw new import_errors.WriteRequestError(
397
+ `${where}.${LINEAGE_FIELD} is reserved; pass sources through the lineage option`
398
+ );
399
+ }
400
+ }
401
+ function buildMetadataHeader(metadata, sources) {
402
+ if (metadata !== void 0) {
403
+ if (!(0, import_personal_server_error_body.isRecord)(metadata)) {
404
+ throw new import_errors.WriteRequestError("metadata must be a plain object");
405
+ }
406
+ assertNoReservedKeys(metadata, "metadata");
407
+ assertNoLineageField(metadata, "metadata");
408
+ }
409
+ if (metadata === void 0 && sources === void 0) return void 0;
410
+ return encodeWriteMetadataHeader({
411
+ ...metadata ?? {},
412
+ ...sources !== void 0 ? { [LINEAGE_FIELD]: sources } : {}
413
+ });
414
+ }
415
+ function prepareWrite(params) {
416
+ if (params.binary !== void 0 && params.data !== void 0) {
417
+ throw new import_errors.WriteRequestError("Pass either data or binary, not both");
418
+ }
419
+ const rawLineage = params.lineage;
420
+ const sources = rawLineage === void 0 || rawLineage === null ? void 0 : normalizeLineage(rawLineage, params.scope);
421
+ if (params.binary !== void 0) {
422
+ const { bytes, contentType, filename } = params.binary;
423
+ if (!(bytes instanceof Uint8Array)) {
424
+ throw new import_errors.WriteRequestError("binary.bytes must be a Uint8Array");
425
+ }
426
+ if (typeof contentType !== "string" || contentType.trim() === "") {
427
+ throw new import_errors.WriteRequestError("binary.contentType is required");
428
+ }
429
+ if (filename !== void 0) {
430
+ if (typeof filename !== "string") {
431
+ throw new import_errors.WriteRequestError("binary.filename must be a string");
432
+ }
433
+ if (filename !== filename.trim()) {
434
+ throw new import_errors.WriteRequestError(
435
+ "binary.filename must not have leading or trailing whitespace"
436
+ );
437
+ }
438
+ }
439
+ const metadataHeader = buildMetadataHeader(params.metadata, sources);
440
+ return {
441
+ body: bytes,
442
+ signedBytes: binaryWriteSignedBytes({
443
+ bytes,
444
+ contentType,
445
+ filename,
446
+ metadataHeader
447
+ }),
448
+ contentType,
449
+ filename,
450
+ metadataHeader
451
+ };
452
+ }
453
+ if (params.data === void 0) {
454
+ throw new import_errors.WriteRequestError("Pass data (a JSON object) or binary");
455
+ }
456
+ if (!(0, import_personal_server_error_body.isRecord)(params.data)) {
457
+ throw new import_errors.WriteRequestError("data must be a plain JSON object");
458
+ }
459
+ if (params.metadata !== void 0) {
460
+ throw new import_errors.WriteRequestError(
461
+ "metadata applies to binary writes; put fields to store inside data"
462
+ );
463
+ }
464
+ assertNoReservedKeys(params.data, "data");
465
+ assertNoLineageField(params.data, "data");
466
+ const record = sources === void 0 ? params.data : { ...params.data, [LINEAGE_FIELD]: sources };
467
+ let text;
468
+ try {
469
+ text = JSON.stringify(record);
470
+ } catch (err) {
471
+ throw new import_errors.WriteRequestError(
472
+ `data is not JSON-serialisable: ${errorMessage(err)}`
473
+ );
474
+ }
475
+ if (typeof text !== "string" || !text.startsWith("{")) {
476
+ throw new import_errors.WriteRequestError("data must serialise to a JSON object");
477
+ }
478
+ const body = new TextEncoder().encode(text);
479
+ return {
480
+ body,
481
+ signedBytes: body,
482
+ contentType: "application/json"
483
+ };
484
+ }
485
+ async function writeErrorFromResponse(response) {
486
+ const { errorCode, message, details } = await (0, import_personal_server_error_body.readPersonalServerErrorBody)(response);
487
+ const text = message ?? `Personal Server write failed: ${response.status} ${response.statusText}`;
488
+ if (response.status === 422 || errorCode?.startsWith("LINEAGE_")) {
489
+ return new import_errors.WriteLineageError(text, response.status, errorCode, details);
490
+ }
491
+ switch (response.status) {
492
+ case 401:
493
+ return new import_errors.WriteUnauthorizedError(text, errorCode, details);
494
+ case 403:
495
+ return new import_errors.WriteForbiddenError(text, errorCode, details);
496
+ case 409:
497
+ return new import_errors.WriteConflictError(text, errorCode, details);
498
+ default:
499
+ return new import_errors.WriteRejectedError(text, response.status, errorCode, details);
500
+ }
501
+ }
502
+ async function writeData(params) {
503
+ const { session } = params;
504
+ if (!isWriteSession(session)) {
505
+ throw new import_errors.WriteRequestError("session must come from openWriteSession");
506
+ }
507
+ const fetchFn = resolveFetch(params.fetch);
508
+ if (typeof params.scope !== "string" || params.scope.length === 0) {
509
+ throw new import_errors.WriteRequestError("scope is required");
510
+ }
511
+ const prepared = prepareWrite(params);
512
+ if (Date.now() >= session.expiresAt) {
513
+ throw new import_errors.WriteSessionExpiredError(
514
+ "Write session has expired; open a new session",
515
+ { grantId: session.grantId, expiresAt: session.expiresAt }
516
+ );
517
+ }
518
+ const path = dataPath(params.scope);
519
+ const response = await sendWithFreshProof(
520
+ `Write to ${params.scope}`,
521
+ fetchFn,
522
+ params.retry,
523
+ proofKeyFor({
524
+ aud: session.audience,
525
+ method: "POST",
526
+ uri: path,
527
+ grantId: session.grantId,
528
+ signedBytes: prepared.signedBytes
529
+ }),
530
+ async (iat) => {
531
+ const headers = new Headers(params.headers);
532
+ headers.set("Content-Type", prepared.contentType);
533
+ headers.set("Authorization", `Bearer ${session.accessToken}`);
534
+ if (prepared.filename) {
535
+ setFilenameHeader(headers, prepared.filename);
536
+ }
537
+ if (prepared.metadataHeader !== void 0) {
538
+ headers.set(WRITE_METADATA_HEADER, prepared.metadataHeader);
539
+ }
540
+ headers.set(
541
+ WRITE_SIGNATURE_HEADER,
542
+ await (0, import_web3_signed_builder.buildWeb3SignedHeader)({
543
+ signMessage: session.signer.signMessage,
544
+ aud: session.audience,
545
+ method: "POST",
546
+ uri: path,
547
+ body: prepared.signedBytes,
548
+ grantId: session.grantId,
549
+ iat
550
+ })
551
+ );
552
+ return {
553
+ url: `${session.personalServerUrl}${path}`,
554
+ init: {
555
+ method: "POST",
556
+ headers,
557
+ body: prepared.body
558
+ }
559
+ };
560
+ }
561
+ );
562
+ if (!response.ok) {
563
+ throw await writeErrorFromResponse(response);
564
+ }
565
+ let body;
566
+ try {
567
+ body = await response.json();
568
+ } catch (err) {
569
+ throw new import_errors.WriteRejectedError(
570
+ "Personal Server write response is not JSON",
571
+ response.status,
572
+ null,
573
+ { cause: errorMessage(err) }
574
+ );
575
+ }
576
+ const parsed = WriteDataResultSchema.safeParse(body);
577
+ if (!parsed.success) {
578
+ throw new import_errors.WriteRejectedError(
579
+ "Personal Server write response is not an ingest result",
580
+ response.status,
581
+ null,
582
+ { issues: parsed.error.issues }
583
+ );
584
+ }
585
+ return parsed.data;
586
+ }
587
+ async function writePersonalServerData(params) {
588
+ const session = await openWriteSession({
589
+ personalServerUrl: params.personalServerUrl,
590
+ signer: params.signer,
591
+ grantId: params.grantId,
592
+ account: params.account,
593
+ audience: params.audience,
594
+ fetch: params.fetch,
595
+ headers: params.headers,
596
+ retry: params.retry
597
+ });
598
+ const writeParams = { ...params, session };
599
+ const result = await writeData(writeParams);
600
+ return { ...result, session };
601
+ }
602
+ // Annotate the CommonJS export names for ESM import in node:
603
+ 0 && (module.exports = {
604
+ LINEAGE_FIELD,
605
+ LINEAGE_KEY,
606
+ MAX_LINEAGE_SOURCES,
607
+ RESERVED_WRITE_KEYS,
608
+ WRITER_ATTRIBUTION_KEY,
609
+ WRITE_CONTENT_DISPOSITION_HEADER,
610
+ WRITE_FILENAME_HEADER,
611
+ WRITE_METADATA_HEADER,
612
+ WRITE_SESSION_PATH,
613
+ WRITE_SIGNATURE_HEADER,
614
+ binaryWriteSignedBytes,
615
+ encodeWriteMetadataHeader,
616
+ normalizeBinaryMimeType,
617
+ openWriteSession,
618
+ parseWriteMetadataHeader,
619
+ sessionCoversScope,
620
+ writeData,
621
+ writePersonalServerData
622
+ });
623
+ //# sourceMappingURL=personal-server-write.cjs.map