@artblocks/abx-storage 0.1.0-alpha.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.
@@ -0,0 +1,458 @@
1
+ import { privateKeyToAccount } from 'viem/accounts';
2
+ import { ContentIndex } from './content-index.js';
3
+ import { arweaveAddress } from './arweave-identity.js';
4
+ /**
5
+ * Best-effort USD estimate for a Turbo (Arweave) upload of `bytes`, from Turbo's PUBLIC price API
6
+ * (no key, no SDK). Returns null on ANY failure (offline, timeout, API change) — callers treat it
7
+ * as a bonus and carry on. Rough by design: linear in bytes, current rates.
8
+ */
9
+ export async function turboUploadCostUsd(bytes) {
10
+ if (!Number.isFinite(bytes) || bytes <= 0)
11
+ return null;
12
+ const base = 'https://payment.ardrive.io/v1';
13
+ const ctrl = new AbortController();
14
+ const timer = setTimeout(() => ctrl.abort(), 4000);
15
+ try {
16
+ const [priceRes, ratesRes] = await Promise.all([
17
+ fetch(`${base}/price/bytes/${Math.floor(bytes)}`, { signal: ctrl.signal }),
18
+ fetch(`${base}/rates`, { signal: ctrl.signal }),
19
+ ]);
20
+ if (!priceRes.ok || !ratesRes.ok)
21
+ return null;
22
+ const price = (await priceRes.json());
23
+ const rates = (await ratesRes.json());
24
+ const uploadWinc = Number(price.winc);
25
+ const refWinc = Number(rates.winc);
26
+ const usdPerRef = rates.fiat?.usd;
27
+ if (!uploadWinc || !refWinc || !usdPerRef)
28
+ return null;
29
+ return (uploadWinc / refWinc) * usdPerRef;
30
+ }
31
+ catch {
32
+ return null;
33
+ }
34
+ finally {
35
+ clearTimeout(timer);
36
+ }
37
+ }
38
+ /**
39
+ * The Winston-credit (winc) cost of a Turbo upload of `bytes`, from the PUBLIC price API (no key,
40
+ * no SDK). Returns null on any failure — callers treat null as "couldn't check". For the pre-upload
41
+ * funds guard: compare against the identity's balance winc. 1 credit = 1e12 winc.
42
+ */
43
+ export async function turboUploadWinc(bytes) {
44
+ if (!Number.isFinite(bytes) || bytes <= 0)
45
+ return null;
46
+ const ctrl = new AbortController();
47
+ const timer = setTimeout(() => ctrl.abort(), 4000);
48
+ try {
49
+ const res = await fetch(`https://payment.ardrive.io/v1/price/bytes/${Math.floor(bytes)}`, { signal: ctrl.signal });
50
+ if (!res.ok)
51
+ return null;
52
+ const price = (await res.json());
53
+ return Number(price.winc) || null;
54
+ }
55
+ catch {
56
+ return null;
57
+ }
58
+ finally {
59
+ clearTimeout(timer);
60
+ }
61
+ }
62
+ /**
63
+ * The Turbo credit balance of ANY address, from the PUBLIC payment API — **no key, no signature**.
64
+ * So the CLI can check whether a creator's *Ethereum wallet* holds Turbo credits (to recommend
65
+ * `--storage-signer eth`) before ever suggesting a top-up. `token` is the address's chain
66
+ * (`ethereum` for an EVM wallet, `arweave` for a JWK address). Returns null on any failure
67
+ * (offline, unknown address) — callers treat it as "couldn't check". 1 credit = 1e12 winc.
68
+ */
69
+ export async function turboBalanceForAddress(address, token = 'ethereum') {
70
+ if (!address)
71
+ return null;
72
+ const ctrl = new AbortController();
73
+ const timer = setTimeout(() => ctrl.abort(), 4000);
74
+ try {
75
+ const res = await fetch(`https://payment.ardrive.io/v1/account/balance/${token}?address=${encodeURIComponent(address)}`, { signal: ctrl.signal });
76
+ if (!res.ok)
77
+ return null; // 404 = address has never had a Turbo balance
78
+ const winc = ((await res.json()).winc ?? '').toString();
79
+ if (!/^\d+$/.test(winc))
80
+ return null;
81
+ return { winc, credits: wincToCredits(winc) };
82
+ }
83
+ catch {
84
+ return null;
85
+ }
86
+ finally {
87
+ clearTimeout(timer);
88
+ }
89
+ }
90
+ /** Turbo's free-upload ceiling: data items under 100 KiB upload for free — no credits, no prior
91
+ * top-up, no setup. Most single 1/1 SVGs/PNGs fall under it. */
92
+ export const ARWEAVE_FREE_UPLOAD_LIMIT = 100 * 1024;
93
+ /** The Turbo identity a config selects (precedence: remote → eth key → arweave jwk), or null if
94
+ * none is available yet (e.g. Turbo before its managed key is created). */
95
+ export function turboIdentity(cfg) {
96
+ if (cfg.remoteEth)
97
+ return { kind: 'ethereum-remote', ...cfg.remoteEth };
98
+ if (cfg.ethSignerKey)
99
+ return { kind: 'ethereum', privateKey: cfg.ethSignerKey };
100
+ if (cfg.jwk)
101
+ return { kind: 'arweave', jwk: cfg.jwk };
102
+ return null;
103
+ }
104
+ /**
105
+ * The Turbo identity for an operation — {@link turboIdentity}, but when it would be null and this is
106
+ * a WRITE (`opts.mint`), lazily mint the managed key via `cfg.ensureJwk` (creating + persisting it).
107
+ * This is the ONLY implicit key-creation path, and it's gated on `mint` so reads/probes never mint.
108
+ * Pure aside from the injected `ensureJwk` side effect — the write/read distinction is the caller's.
109
+ */
110
+ export function resolveTurboIdentity(cfg, opts = {}) {
111
+ const existing = turboIdentity(cfg);
112
+ if (existing)
113
+ return existing;
114
+ if (opts.mint && cfg.ensureJwk)
115
+ return { kind: 'arweave', jwk: cfg.ensureJwk() };
116
+ return null;
117
+ }
118
+ /** Whether an identity is an Ethereum one (local key or remote wallet) — its address is `0x…`. */
119
+ export function isEthIdentity(id) {
120
+ return id.kind === 'ethereum' || id.kind === 'ethereum-remote';
121
+ }
122
+ const withHexPrefix = (k) => (k.startsWith('0x') ? k : `0x${k}`);
123
+ const stripHexPrefix = (k) => (k.startsWith('0x') ? k.slice(2) : k);
124
+ /** The credit-holding / signing address for an identity — Arweave `base64url(sha256(n))` or an
125
+ * EVM `0x…`. Pure derivation, no network. Its balance is what `abx storage balance` reports. */
126
+ export function turboIdentityAddress(id) {
127
+ switch (id.kind) {
128
+ case 'arweave':
129
+ return arweaveAddress(id.jwk);
130
+ case 'ethereum':
131
+ return privateKeyToAccount(withHexPrefix(id.privateKey)).address;
132
+ case 'ethereum-remote':
133
+ return id.address;
134
+ }
135
+ }
136
+ const trim = (u) => u.replace(/\/+$/, '');
137
+ /** Coerce a Turbo `uploadFile` response to text. It's normally an object, but the idempotent
138
+ * "already uploaded" path returns a plain string (which JSON/stream layers may surface as a
139
+ * char-indexed object) — Object.values(...).join('') recovers it either way. */
140
+ function turboResponseText(res) {
141
+ if (typeof res === 'string')
142
+ return res;
143
+ if (res && typeof res === 'object') {
144
+ if ('id' in res && res.id)
145
+ return String(res.id);
146
+ try {
147
+ return Object.values(res).join('');
148
+ }
149
+ catch {
150
+ return '';
151
+ }
152
+ }
153
+ return '';
154
+ }
155
+ /**
156
+ * The Arweave data-item id from a Turbo `uploadFile` response. Normally `res.id`; but Turbo
157
+ * **deduplicates identical bytes** — a re-upload of the same data item (same signer + tags + bytes →
158
+ * same deterministic id) returns a plain-text `"Data item with ID <id> has already been uploaded to
159
+ * this service!"` instead of the usual JSON. Both are success: the bytes ARE on Turbo. We extract
160
+ * the 43-char base64url txid so idempotent re-uploads (retries, redeploys, duplicate images in a
161
+ * series) resolve to the real locator instead of throwing. Returns null if no id is present.
162
+ */
163
+ export function turboUploadId(res) {
164
+ if (res && typeof res === 'object' && 'id' in res && res.id) {
165
+ return String(res.id);
166
+ }
167
+ const m = turboResponseText(res).match(/\bID\s+([A-Za-z0-9_-]{43})\b/);
168
+ return m ? m[1] : null;
169
+ }
170
+ /** Optional deps, loaded lazily via indirect specifiers so they're not required to install/build. */
171
+ const TURBO_SDK_PACKAGE = '@ardrive/turbo-sdk';
172
+ const ARBUNDLES_PACKAGE = '@dha-team/arbundles';
173
+ const WINC_PER_CREDIT = 1e12;
174
+ const wincToCredits = (winc) => {
175
+ const n = Number(winc) / WINC_PER_CREDIT;
176
+ return n.toFixed(6).replace(/\.?0+$/, '') || '0';
177
+ };
178
+ /** Dep-free uploader for token-authenticated managed Arweave upload endpoints. */
179
+ export class HttpBundlerUploader {
180
+ cfg;
181
+ constructor(cfg) {
182
+ this.cfg = cfg;
183
+ }
184
+ async upload(bytes, contentType) {
185
+ if (!this.cfg.uploadUrl) {
186
+ throw new Error('Arweave http-bundler needs an upload endpoint — pass `--backend arweave --provider http-bundler --upload-url <bundler>` (or set ABX_ARWEAVE_UPLOAD_URL), or use the default `turbo` provider.');
187
+ }
188
+ const headers = { 'content-type': contentType };
189
+ if (this.cfg.token)
190
+ headers.authorization = `Bearer ${this.cfg.token}`;
191
+ const res = await fetch(this.cfg.uploadUrl, { method: 'POST', headers, body: bytes });
192
+ if (!res.ok)
193
+ throw new Error(`Arweave upload failed (${res.status}): ${await res.text()}`);
194
+ const json = (await res.json());
195
+ const id = json.id ?? json.txId ?? json.transactionId ?? json.arweaveTxId;
196
+ if (!id)
197
+ throw new Error('Arweave upload returned no transaction id');
198
+ return { id: String(id) };
199
+ }
200
+ }
201
+ /**
202
+ * ArDrive Turbo uploader — the recommended Arweave path. Lazy-loads the optional
203
+ * `@ardrive/turbo-sdk` only when an upload / balance / top-up actually runs (the retrieve-only
204
+ * resolver never touches it). The signing {@link TurboIdentity} is multi-chain: the CLI-managed
205
+ * Arweave JWK (default), an `.env` EVM key, or a remote browser wallet — each both signs uploads
206
+ * and holds the prepaid credits at its own address.
207
+ */
208
+ export class TurboUploader {
209
+ identity;
210
+ constructor(identity) {
211
+ this.identity = identity;
212
+ if (identity.kind === 'arweave' && !identity.jwk?.n) {
213
+ throw new Error('Turbo needs an Arweave identity key (JWK). The CLI manages one at `.abx-self-host/arweave-key.json` — or set ARWEAVE_JWK, or use `--provider http-bundler`.');
214
+ }
215
+ if (identity.kind === 'ethereum' && !identity.privateKey) {
216
+ throw new Error('Turbo Ethereum identity needs a private key.');
217
+ }
218
+ }
219
+ async sdk() {
220
+ try {
221
+ // Indirect specifier: keeps the OPTIONAL dep out of the type graph so the package
222
+ // type-checks and runs without it installed (the retrieve-only resolver path never calls this).
223
+ return await import(/* @vite-ignore */ TURBO_SDK_PACKAGE);
224
+ }
225
+ catch {
226
+ throw new Error("The Turbo provider needs the optional '@ardrive/turbo-sdk' package — install it (`pnpm add @ardrive/turbo-sdk`), or switch with `--backend arweave --provider http-bundler --upload-url <bundler>`.");
227
+ }
228
+ }
229
+ /** arbundles (the signing lib Turbo uses) — for `InjectedEthereumSigner`, which turbo-sdk does NOT
230
+ * re-export. Optional dep, loaded only on the remote-wallet upload path. */
231
+ async arbundles() {
232
+ try {
233
+ return await import(/* @vite-ignore */ ARBUNDLES_PACKAGE);
234
+ }
235
+ catch {
236
+ throw new Error("The browser-wallet Arweave upload lane needs the optional '@dha-team/arbundles' package — install it (`pnpm add @dha-team/arbundles`), or use `--storage-signer eth` with a key in .env, or the managed Arweave key.");
237
+ }
238
+ }
239
+ clientPromise;
240
+ client() {
241
+ return (this.clientPromise ??= this.buildClient());
242
+ }
243
+ /** Authenticate a Turbo client for the selected identity. Arweave uses the JWK directly; an EVM
244
+ * identity uses an arbundles `EthereumSigner` (local key) or `InjectedEthereumSigner` (remote
245
+ * wallet) — the SDK re-exports both, so no direct arbundles dependency. */
246
+ async buildClient() {
247
+ const m = await this.sdk();
248
+ const id = this.identity;
249
+ if (id.kind === 'arweave') {
250
+ return m.TurboFactory.authenticated({ privateKey: id.jwk, token: 'arweave' });
251
+ }
252
+ if (id.kind === 'ethereum') {
253
+ const signer = new m.EthereumSigner(stripHexPrefix(id.privateKey));
254
+ return m.TurboFactory.authenticated({ signer, token: 'ethereum' });
255
+ }
256
+ // ethereum-remote: drive arbundles' own InjectedEthereumSigner via a provider shim that
257
+ // round-trips each signMessage to the browser wallet. arbundles calls `signer.signMessage(msg)`
258
+ // with a STRING challenge (setPublicKey → pubkey recovery, one prompt) and a Uint8Array per data
259
+ // item, and expects back a `0x…` hex signature (it does `sig.slice(2)`). ethers'
260
+ // signMessage(string) ≡ signMessage(utf8Bytes(string)), so normalizing to bytes is faithful.
261
+ // NOTE: turbo-sdk re-exports EthereumSigner but NOT InjectedEthereumSigner — import it from
262
+ // arbundles (the same package turbo signs with) directly.
263
+ const { InjectedEthereumSigner } = await this.arbundles();
264
+ const signMessage = id.signMessage;
265
+ const provider = {
266
+ getSigner: () => ({
267
+ signMessage: (message) => signMessage(typeof message === 'string' ? new TextEncoder().encode(message) : Uint8Array.from(message)),
268
+ }),
269
+ };
270
+ const signer = new InjectedEthereumSigner(provider);
271
+ await signer.setPublicKey();
272
+ return m.TurboFactory.authenticated({ signer, token: 'ethereum' });
273
+ }
274
+ async upload(bytes, contentType) {
275
+ const turbo = await this.client();
276
+ const { Readable } = await import('node:stream');
277
+ const buf = Buffer.from(bytes);
278
+ const res = await turbo.uploadFile({
279
+ fileStreamFactory: () => Readable.from(buf),
280
+ fileSizeFactory: () => buf.byteLength,
281
+ dataItemOpts: { tags: [{ name: 'Content-Type', value: contentType }] },
282
+ });
283
+ const id = turboUploadId(res);
284
+ if (!id)
285
+ throw new Error(`Turbo upload returned no transaction id (response: ${turboResponseText(res).slice(0, 200)})`);
286
+ return { id };
287
+ }
288
+ funding = {
289
+ address: async () => turboIdentityAddress(this.identity),
290
+ balance: async () => {
291
+ const turbo = await this.client();
292
+ const { winc } = (await turbo.getBalance());
293
+ return { winc: String(winc), credits: wincToCredits(winc) };
294
+ },
295
+ topup: async ({ usd }) => {
296
+ const [turbo, m] = await Promise.all([this.client(), this.sdk()]);
297
+ const session = (await turbo.createCheckoutSession({ amount: m.USD(usd), owner: turboIdentityAddress(this.identity) }));
298
+ if (!session?.url)
299
+ throw new Error('Turbo top-up returned no checkout URL');
300
+ return { url: String(session.url), winc: String(session.winc ?? '') };
301
+ },
302
+ };
303
+ }
304
+ /** The upload route for a config: explicit `provider`, else `http-bundler` when an `uploadUrl`
305
+ * is set, else the recommended `turbo`. */
306
+ export function arweaveProvider(cfg) {
307
+ return cfg.provider ?? (cfg.uploadUrl ? 'http-bundler' : 'turbo');
308
+ }
309
+ function buildUploader(cfg, opts = {}) {
310
+ if (arweaveProvider(cfg) === 'http-bundler') {
311
+ return new HttpBundlerUploader({ uploadUrl: cfg.uploadUrl, token: cfg.token });
312
+ }
313
+ // On a WRITE (opts.mint), a managed default with no key yet is minted here (cfg.ensureJwk); on a
314
+ // read/probe (no mint) an absent identity stays null → the throw below. So the managed default
315
+ // "just works" for an upload without a prior setup step, and reads never create a key.
316
+ const id = resolveTurboIdentity(cfg, opts);
317
+ if (!id) {
318
+ throw new Error('Arweave via Turbo needs an identity. Run an upload through the CLI (it creates a managed key at `.abx-self-host/arweave-key.json`), set ARWEAVE_JWK, sign with an EVM key/wallet (`--storage-signer eth`), or use `--provider http-bundler --upload-url <bundler>`.');
319
+ }
320
+ return new TurboUploader(id);
321
+ }
322
+ /**
323
+ * The funding capability for a config's provider, or throw a clear reason if that provider has
324
+ * none — capability negotiation, so `abx storage topup`/`balance` dispatch through one verb and
325
+ * a crypto-only provider says "fund the wallet directly" rather than pretending.
326
+ */
327
+ export function arweaveFunding(cfg) {
328
+ const f = buildUploader(cfg).funding;
329
+ if (!f)
330
+ throw new Error(`The '${arweaveProvider(cfg)}' Arweave provider has no prepaid balance — fund its wallet directly.`);
331
+ return f;
332
+ }
333
+ export class ArweaveBackend {
334
+ cfg;
335
+ id = 'arweave';
336
+ index;
337
+ uploaderInstance;
338
+ injectedUploader;
339
+ constructor(cfg, dataDir, uploader) {
340
+ this.cfg = cfg;
341
+ this.index = new ContentIndex(dataDir);
342
+ this.injectedUploader = uploader;
343
+ }
344
+ /** Built lazily on first use — retrieval (get/locator/health, the resolver path) needs only the
345
+ * gateway, never an upload identity or the Turbo SDK. `mint` (passed by the write ops) lets a
346
+ * managed default create its key here; reads/probes pass false so they never mint. */
347
+ uploader(mint = false) {
348
+ return (this.uploaderInstance ??= this.injectedUploader ?? buildUploader(this.cfg, { mint }));
349
+ }
350
+ /** Funding capability of the active provider, or null if it has none (e.g. http-bundler) or
351
+ * can't yet be built (e.g. Turbo without an identity key). The CLI uses {@link arweaveFunding}
352
+ * directly for an actionable error; this accessor is the safe boolean-ish capability probe. */
353
+ funding() {
354
+ try {
355
+ return this.uploader(false).funding ?? null;
356
+ }
357
+ catch {
358
+ return null;
359
+ }
360
+ }
361
+ async put(hash, content) {
362
+ const { id } = await this.uploader(true).upload(content.bytes, content.contentType);
363
+ this.index.set(hash, { pointer: id, contentType: content.contentType });
364
+ }
365
+ async get(hash) {
366
+ const entry = this.index.get(hash);
367
+ if (!entry)
368
+ return null;
369
+ const res = await fetch(`${trim(this.cfg.gateway)}/${entry.pointer}`);
370
+ if (res.status === 404)
371
+ return null;
372
+ if (!res.ok)
373
+ throw new Error(`Arweave gateway GET failed (${res.status})`);
374
+ return { bytes: new Uint8Array(await res.arrayBuffer()), contentType: entry.contentType };
375
+ }
376
+ async has(hash) {
377
+ return this.index.has(hash);
378
+ }
379
+ /** The gateway HTTPS URL for the bytes (`<gateway>/<txid>`) — the production-safe form an NFT
380
+ * `image` needs (a raw `ar://` doesn't render in browsers/most wallets). The txid is permanent,
381
+ * so the bytes resolve through any Arweave gateway and verify against the on-chain keccak256;
382
+ * we serve a concrete reachable one. Null until the bytes are uploaded. */
383
+ async locator(hash) {
384
+ const entry = this.index.get(hash);
385
+ return entry ? `${trim(this.cfg.gateway)}/${entry.pointer}` : null;
386
+ }
387
+ /**
388
+ * Upload a directory of files as an Arweave **path manifest**: each file is uploaded to its own
389
+ * txid, then a manifest (`arweave/paths`) mapping `name → txid` is uploaded, and its txid is the
390
+ * directory root. `<gateway>/<manifestTxid>/0.png` then resolves. The manifest txid is a
391
+ * permanent, content-addressed root → integrity for the whole set, no per-file keccak needed.
392
+ */
393
+ async putDirectory(entries) {
394
+ const up = this.uploader(true);
395
+ const paths = {};
396
+ for (const e of entries) {
397
+ const { id } = await up.upload(e.bytes, e.contentType);
398
+ paths[e.name] = { id };
399
+ }
400
+ const manifest = {
401
+ manifest: 'arweave/paths',
402
+ version: '0.1.0',
403
+ index: entries[0] ? { path: entries[0].name } : undefined,
404
+ paths,
405
+ };
406
+ const { id: manifestId } = await up.upload(new TextEncoder().encode(JSON.stringify(manifest)), 'application/x.arweave-manifest+json');
407
+ return { base: `${trim(this.cfg.gateway)}/${manifestId}` };
408
+ }
409
+ async health() {
410
+ const provider = arweaveProvider(this.cfg);
411
+ try {
412
+ const res = await fetch(trim(this.cfg.gateway), { method: 'HEAD' });
413
+ const reachable = res.status < 500;
414
+ let detail = `provider ${provider} · gateway ${this.cfg.gateway}`;
415
+ if (provider === 'http-bundler') {
416
+ if (!this.cfg.uploadUrl) {
417
+ return { ok: false, detail: `${detail} · no upload endpoint — set --upload-url <bundler> (or use --provider turbo)` };
418
+ }
419
+ detail += ` · upload ${this.cfg.uploadUrl}${this.cfg.token ? ' (token set)' : ' (no token)'}`;
420
+ }
421
+ else {
422
+ const id = turboIdentity(this.cfg);
423
+ detail += id
424
+ ? ` · identity ${isEthIdentity(id) ? 'eth' : 'arweave'} ${turboIdentityAddress(id)}`
425
+ : ' · identity created on first upload (free under 100 KiB)';
426
+ }
427
+ return { ok: reachable, detail };
428
+ }
429
+ catch (e) {
430
+ return { ok: false, detail: e.message };
431
+ }
432
+ }
433
+ }
434
+ /** Build an Arweave config from the environment (non-secret values; the token/JWK/eth-key are
435
+ * secrets). `ABX_ARWEAVE_ETH_SIGNER_KEY` selects an EVM Turbo identity (the CLI usually sets this
436
+ * from the resolved signing key instead). */
437
+ export function arweaveConfigFromEnv() {
438
+ return {
439
+ gateway: process.env.ABX_ARWEAVE_GATEWAY ?? 'https://arweave.net',
440
+ provider: process.env.ABX_ARWEAVE_PROVIDER,
441
+ uploadUrl: process.env.ABX_ARWEAVE_UPLOAD_URL,
442
+ token: process.env.ARWEAVE_UPLOAD_TOKEN,
443
+ jwk: parseJwkEnv(process.env.ARWEAVE_JWK),
444
+ ethSignerKey: process.env.ABX_ARWEAVE_ETH_SIGNER_KEY,
445
+ };
446
+ }
447
+ function parseJwkEnv(raw) {
448
+ if (!raw)
449
+ return undefined;
450
+ const text = raw.trim().startsWith('{') ? raw : Buffer.from(raw, 'base64').toString('utf8');
451
+ try {
452
+ return JSON.parse(text);
453
+ }
454
+ catch {
455
+ throw new Error('ARWEAVE_JWK is not valid JSON (or base64 of JSON).');
456
+ }
457
+ }
458
+ //# sourceMappingURL=arweave.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"arweave.js","sourceRoot":"","sources":["../src/arweave.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,87 @@
1
+ import { type Hex } from 'viem';
2
+ /**
3
+ * Byte custody for ABX content.
4
+ *
5
+ * The protocol's central asymmetry: **state replays from chain, but source
6
+ * bytes do not.** A token's content commitment is an on-chain `keccak256` of the
7
+ * bytes that *are* the work; this service holds those bytes and serves them.
8
+ *
9
+ * Content is **addressed by its commitment hash** — `put(hash, …)` stores under
10
+ * the same `keccak256` the contract committed, and `get(hash)` returns it. So the
11
+ * pointer lives on-chain (the commitment), storage stays off-chain, and the bytes
12
+ * are both locatable (by hash) and verifiable (re-hash, compare) with no trust in
13
+ * any server. Custody is *switchable, never lapsable*: move backends freely
14
+ * (re-put, verify, drop the old), but never let the bytes go.
15
+ */
16
+ export interface StoredContent {
17
+ bytes: Uint8Array;
18
+ /** MIME type, e.g. `image/png` — served back verbatim. */
19
+ contentType: string;
20
+ }
21
+ /** One file in a directory upload — `name` is the path under the directory (e.g. `0.png`). */
22
+ export interface DirEntry {
23
+ name: string;
24
+ bytes: Uint8Array;
25
+ contentType: string;
26
+ }
27
+ export interface StorageBackend {
28
+ /** Short backend id, e.g. `fs` · `s3` · `ipfs` · `arweave`. */
29
+ readonly id: string;
30
+ /** Store bytes under their commitment hash. Idempotent (content-addressed). */
31
+ put(hash: Hex, content: StoredContent): Promise<void>;
32
+ /** Fetch bytes previously stored under this hash, or null if this backend lacks them. */
33
+ get(hash: Hex): Promise<StoredContent | null>;
34
+ /** Whether this backend currently holds content for the hash. */
35
+ has(hash: Hex): Promise<boolean>;
36
+ /** Optional liveness + credentials check for `abx doctor`. */
37
+ health?(): Promise<{
38
+ ok: boolean;
39
+ detail?: string;
40
+ }>;
41
+ /**
42
+ * Optional content-addressed locator for the hash — a gateway-agnostic URI a client can
43
+ * resolve WITHOUT this server (e.g. `ipfs://<cid>`, `ar://<txid>`). Backends that only
44
+ * serve bytes through this node (fs, cloud) omit it, and the resolver falls back to its
45
+ * own `/…/image` URL. Lets the served metadata point the heavy asset straight at a
46
+ * durable, peer-to-peer locator while the node serves only thin JSON.
47
+ */
48
+ locator?(hash: Hex): Promise<string | null>;
49
+ /**
50
+ * Optional: upload a whole directory of files in ONE shot and return the **gateway base URL**
51
+ * for it (no trailing slash) — an IPFS directory CID (`<gateway>/ipfs/<cid>`) or an Arweave
52
+ * path manifest (`<gateway>/<manifestTxid>`). A file named `0.png` is then reachable at
53
+ * `<base>/0.png`. This is what powers the O(1) directory pattern: N files → one on-chain
54
+ * `url-template` (`<base>/{id}.<ext>`), no per-token on-chain data. Content-addressed roots
55
+ * (CID / manifest txid) give integrity without a per-file keccak anchor. Backends that serve
56
+ * only through this node (fs, cloud) omit it.
57
+ */
58
+ putDirectory?(entries: DirEntry[]): Promise<{
59
+ base: string;
60
+ }>;
61
+ /**
62
+ * The public READ base for this backend (no trailing slash) such that `<publicBase>/<key>`
63
+ * fetches an object written by {@link putObject}, or undefined if objects are only reachable
64
+ * through this node. Present only on **mutable, path-addressed** hosts (S3/R2/CDN) — the ones
65
+ * that can serve a STABLE per-token URL an on-chain `url-template` names. Content-addressed
66
+ * backends (ipfs/arweave) omit it: their locator changes with the bytes, so they can't back a
67
+ * deterministic `/{id}` path without rewriting chain state on every update.
68
+ */
69
+ readonly publicBase?: string;
70
+ /**
71
+ * Optional: write bytes at a CALLER-CHOSEN key (NOT content-addressed), overwriting in place.
72
+ * This is the deterministic-thumbnail primitive: the effect runner writes token N's still to the
73
+ * exact key an on-chain `image` `url-template` points at (`<publicBase>/<key>`), overwriting on
74
+ * re-render — so the on-chain pointer is stable while the pixels update, with no chain write and
75
+ * no metadata resolver. Only mutable path-addressed backends (cloud) implement it.
76
+ */
77
+ putObject?(key: string, content: StoredContent): Promise<void>;
78
+ /** Optional: read bytes previously written by {@link putObject} at `key`, or null if absent. */
79
+ getObject?(key: string): Promise<StoredContent | null>;
80
+ }
81
+ /** keccak256 of arbitrary content bytes — the value committed on-chain (the image `keccak256` field). */
82
+ export declare function hashContent(bytes: Uint8Array): Hex;
83
+ /** Best-effort MIME type from a file path — a *labeled* extension-map fallback (the data plane's
84
+ * declared-type rule permits it for locator forms; bytes are never sniffed). Unknown → the
85
+ * honest floor, `application/octet-stream`. */
86
+ export declare function contentTypeFromPath(path: string): string;
87
+ //# sourceMappingURL=backend.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,GAAG,EAAC,MAAM,MAAM,CAAC;AAEzC;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,UAAU,CAAC;IAClB,0DAA0D;IAC1D,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,8FAA8F;AAC9F,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,UAAU,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,+EAA+E;IAC/E,GAAG,CAAC,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,yFAAyF;IACzF,GAAG,CAAC,IAAI,EAAE,GAAG,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IAC9C,iEAAiE;IACjE,GAAG,CAAC,IAAI,EAAE,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACjC,8DAA8D;IAC9D,MAAM,CAAC,IAAI,OAAO,CAAC;QAAC,EAAE,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAC,CAAC,CAAC;IACnD;;;;;;OAMG;IACH,OAAO,CAAC,CAAC,IAAI,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC5C;;;;;;;;OAQG;IACH,YAAY,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAC,CAAC,CAAC;IAE5D;;;;;;;OAOG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAE7B;;;;;;OAMG;IACH,SAAS,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE/D,gGAAgG;IAChG,SAAS,CAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;CACxD;AAED,yGAAyG;AACzG,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,GAAG,CAElD;AAED;;gDAEgD;AAChD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CA6BxD"}
@@ -0,0 +1,39 @@
1
+ import { keccak256 } from 'viem';
2
+ /** keccak256 of arbitrary content bytes — the value committed on-chain (the image `keccak256` field). */
3
+ export function hashContent(bytes) {
4
+ return keccak256(bytes);
5
+ }
6
+ /** Best-effort MIME type from a file path — a *labeled* extension-map fallback (the data plane's
7
+ * declared-type rule permits it for locator forms; bytes are never sniffed). Unknown → the
8
+ * honest floor, `application/octet-stream`. */
9
+ export function contentTypeFromPath(path) {
10
+ const ext = path.toLowerCase().slice(path.lastIndexOf('.') + 1);
11
+ const map = {
12
+ png: 'image/png',
13
+ jpg: 'image/jpeg',
14
+ jpeg: 'image/jpeg',
15
+ gif: 'image/gif',
16
+ webp: 'image/webp',
17
+ svg: 'image/svg+xml',
18
+ avif: 'image/avif',
19
+ tiff: 'image/tiff',
20
+ tif: 'image/tiff',
21
+ psd: 'image/vnd.adobe.photoshop',
22
+ mp4: 'video/mp4',
23
+ webm: 'video/webm',
24
+ html: 'text/html; charset=utf-8',
25
+ json: 'application/json',
26
+ glb: 'model/gltf-binary',
27
+ gltf: 'model/gltf+json',
28
+ mp3: 'audio/mpeg',
29
+ wav: 'audio/wav',
30
+ flac: 'audio/flac',
31
+ pdf: 'application/pdf',
32
+ txt: 'text/plain; charset=utf-8',
33
+ md: 'text/markdown; charset=utf-8',
34
+ csv: 'text/csv; charset=utf-8',
35
+ zip: 'application/zip',
36
+ };
37
+ return map[ext] ?? 'application/octet-stream';
38
+ }
39
+ //# sourceMappingURL=backend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backend.js","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAW,MAAM,MAAM,CAAC;AAkFzC,yGAAyG;AACzG,MAAM,UAAU,WAAW,CAAC,KAAiB;IAC3C,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC;AAC1B,CAAC;AAED;;gDAEgD;AAChD,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IAChE,MAAM,GAAG,GAA2B;QAClC,GAAG,EAAE,WAAW;QAChB,GAAG,EAAE,YAAY;QACjB,IAAI,EAAE,YAAY;QAClB,GAAG,EAAE,WAAW;QAChB,IAAI,EAAE,YAAY;QAClB,GAAG,EAAE,eAAe;QACpB,IAAI,EAAE,YAAY;QAClB,IAAI,EAAE,YAAY;QAClB,GAAG,EAAE,YAAY;QACjB,GAAG,EAAE,2BAA2B;QAChC,GAAG,EAAE,WAAW;QAChB,IAAI,EAAE,YAAY;QAClB,IAAI,EAAE,0BAA0B;QAChC,IAAI,EAAE,kBAAkB;QACxB,GAAG,EAAE,mBAAmB;QACxB,IAAI,EAAE,iBAAiB;QACvB,GAAG,EAAE,YAAY;QACjB,GAAG,EAAE,WAAW;QAChB,IAAI,EAAE,YAAY;QAClB,GAAG,EAAE,iBAAiB;QACtB,GAAG,EAAE,2BAA2B;QAChC,EAAE,EAAE,8BAA8B;QAClC,GAAG,EAAE,yBAAyB;QAC9B,GAAG,EAAE,iBAAiB;KACvB,CAAC;IACF,OAAO,GAAG,CAAC,GAAG,CAAC,IAAI,0BAA0B,CAAC;AAChD,CAAC"}