@artblocks/abx-storage 0.1.0-alpha.4 → 0.1.0-alpha.41
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/CHANGELOG.md +69 -0
- package/dist/arweave.d.ts +45 -44
- package/dist/arweave.d.ts.map +1 -1
- package/dist/arweave.js +98 -155
- package/dist/arweave.js.map +1 -1
- package/dist/backend.d.ts +1 -4
- package/dist/backend.d.ts.map +1 -1
- package/dist/backend.js +4 -33
- package/dist/backend.js.map +1 -1
- package/dist/content-index.d.ts.map +1 -1
- package/dist/content-index.js +3 -1
- package/dist/content-index.js.map +1 -1
- package/dist/content-plan.d.ts +78 -0
- package/dist/content-plan.d.ts.map +1 -0
- package/dist/content-plan.js +68 -0
- package/dist/content-plan.js.map +1 -0
- package/dist/fs.d.ts.map +1 -1
- package/dist/fs.js +3 -1
- package/dist/fs.js.map +1 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/ipfs.d.ts +17 -1
- package/dist/ipfs.d.ts.map +1 -1
- package/dist/ipfs.js +60 -2
- package/dist/ipfs.js.map +1 -1
- package/dist/migrate.d.ts +37 -0
- package/dist/migrate.d.ts.map +1 -0
- package/dist/migrate.js +55 -0
- package/dist/migrate.js.map +1 -0
- package/dist/probe.d.ts +66 -0
- package/dist/probe.d.ts.map +1 -0
- package/dist/probe.js +155 -0
- package/dist/probe.js.map +1 -0
- package/dist/readiness.d.ts +158 -0
- package/dist/readiness.d.ts.map +1 -0
- package/dist/readiness.js +183 -0
- package/dist/readiness.js.map +1 -0
- package/dist/upload-readiness.d.ts +89 -0
- package/dist/upload-readiness.d.ts.map +1 -0
- package/dist/upload-readiness.js +85 -0
- package/dist/upload-readiness.js.map +1 -0
- package/dist/upload.d.ts +28 -0
- package/dist/upload.d.ts.map +1 -0
- package/dist/upload.js +41 -0
- package/dist/upload.js.map +1 -0
- package/package.json +17 -9
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { ResolveStorageOptions } from './resolve.js';
|
|
2
|
+
import { type ArweaveConfig } from './arweave.js';
|
|
3
|
+
/**
|
|
4
|
+
* Deploy-time storage readiness — what a REAL upload needs, decided from local facts (and, for the
|
|
5
|
+
* best-effort USD estimate, one price-API call) so "dry-run passes, then the real deploy fails at
|
|
6
|
+
* upload" can't happen. Three questions, each pure aside from the one noted network call:
|
|
7
|
+
*
|
|
8
|
+
* - {@link planTurboUpload} — is THIS ONE file free (Turbo's 100 KiB tier) or chargeable?
|
|
9
|
+
* - {@link assessStorageReadiness} — across every backend, what does a real upload of `sizes` need
|
|
10
|
+
* (Arweave credits, a Pinata JWT, a cloud public base)?
|
|
11
|
+
* - {@link assessTurboFunds} — does the resolved Turbo identity have ENOUGH prepaid credits for
|
|
12
|
+
* `sizes`, and (when it's short) does the creator's own wallet already hold some?
|
|
13
|
+
*
|
|
14
|
+
* None of this mints or spends anything — it only INTERROGATES the resolved provider. Identity
|
|
15
|
+
* creation is the caller's job (the CLI's managed-key flow owns the key file location; see
|
|
16
|
+
* `arweave-identity.ts` / `ArweaveConfig.ensureJwk`), so `assessTurboFunds` expects a config that
|
|
17
|
+
* already has one.
|
|
18
|
+
*/
|
|
19
|
+
/** Whether `opts` resolves to Arweave via the Turbo (prepaid-credits) provider — the only provider
|
|
20
|
+
* with a free tier and a funds balance to check; `http-bundler` skips every readiness/funds
|
|
21
|
+
* question below (it has its own auth, not a balance this package knows how to read). */
|
|
22
|
+
export declare function isTurboArweave(opts: ResolveStorageOptions): boolean;
|
|
23
|
+
export interface TurboUploadPlan {
|
|
24
|
+
/** Whether this upload is over Turbo's 100 KiB free tier (so it draws on prepaid credits). */
|
|
25
|
+
chargeable: boolean;
|
|
26
|
+
/** `size` in KB, formatted to 1 decimal — for the free-vs-credit readout. */
|
|
27
|
+
sizeKb: string;
|
|
28
|
+
}
|
|
29
|
+
/** The free-vs-credit decision for ONE Turbo upload of `size` bytes — from the local byte size
|
|
30
|
+
* alone, so it works under `--dry-run` too. Null when `opts` doesn't resolve to Turbo-Arweave
|
|
31
|
+
* (nothing to decide). */
|
|
32
|
+
export declare function planTurboUpload(opts: ResolveStorageOptions, size: number): TurboUploadPlan | null;
|
|
33
|
+
/** Per-backend readiness facts for a real upload of `sizes` (per-file byte counts). Each variant
|
|
34
|
+
* carries only what its backend needs checked; a backend with nothing to check (`fs`, or anything
|
|
35
|
+
* this package doesn't know about) reports `'other'` — a literal, not the resolved id, so the
|
|
36
|
+
* union stays a real discriminated union (a plain `string` member would swallow the other
|
|
37
|
+
* branches on narrowing) — callers that branch on `backend` never needed the id anyway. */
|
|
38
|
+
export type StorageReadinessReport = {
|
|
39
|
+
backend: 'arweave';
|
|
40
|
+
overCount: number;
|
|
41
|
+
totalCount: number;
|
|
42
|
+
totalKb: number;
|
|
43
|
+
usd: number | null;
|
|
44
|
+
} | {
|
|
45
|
+
backend: 'ipfs';
|
|
46
|
+
pinataMissingJwt: boolean;
|
|
47
|
+
} | {
|
|
48
|
+
backend: 'cloud';
|
|
49
|
+
missingPublicBase: boolean;
|
|
50
|
+
} | {
|
|
51
|
+
backend: 'other';
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* What a REAL deploy against `opts` needs, from `sizes` (per-file byte sizes) alone — Arweave
|
|
55
|
+
* credit needs (plus a best-effort USD estimate for the chargeable bytes), a missing Pinata JWT, or
|
|
56
|
+
* a missing cloud public base. The one network call (Turbo's price API) is best-effort: a failure
|
|
57
|
+
* there degrades `usd` to `null` rather than failing the whole check.
|
|
58
|
+
*/
|
|
59
|
+
export declare function assessStorageReadiness(opts: ResolveStorageOptions, sizes: number[]): Promise<StorageReadinessReport>;
|
|
60
|
+
export interface TurboFundsCheck {
|
|
61
|
+
/** True when the resolved identity does NOT have enough prepaid credits for `sizes`. */
|
|
62
|
+
short: boolean;
|
|
63
|
+
address: string;
|
|
64
|
+
/** Whether the identity is an EVM one (`.env` key or a remote wallet) rather than the CLI-managed
|
|
65
|
+
* Arweave key — decides which recovery message applies. */
|
|
66
|
+
isEth: boolean;
|
|
67
|
+
haveWinc: number;
|
|
68
|
+
needWinc: number;
|
|
69
|
+
/** How many of `sizes` are over the free tier (what this shortfall is actually for). */
|
|
70
|
+
chargeableCount: number;
|
|
71
|
+
/** Set only when checked (short, a non-eth identity, and a wallet address was given): the
|
|
72
|
+
* creator's OWN wallet's Turbo balance, queryable by address with no key — so "spend those
|
|
73
|
+
* instead of topping up again" can be recommended before ever proposing a top-up. Null if that
|
|
74
|
+
* wallet has never had a Turbo balance. */
|
|
75
|
+
walletCredits?: {
|
|
76
|
+
winc: number;
|
|
77
|
+
credits: string;
|
|
78
|
+
} | null;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* The pre-upload funds decision for a Turbo (Arweave) upload of `sizes` (per-file byte sizes):
|
|
82
|
+
* chargeable bytes → the Winston cost → compare against the identity's prepaid balance. `cfg` must
|
|
83
|
+
* already have a resolved identity (`jwk`/`ethSignerKey`/`remoteEth`) — this module only
|
|
84
|
+
* INTERROGATES the resolved provider, never mints one (see the module doc). Returns `null` when
|
|
85
|
+
* nothing is chargeable (every file is free) or the provider can't be reached right now — a
|
|
86
|
+
* best-effort check never false-blocks; the upload itself will surface any real error.
|
|
87
|
+
*/
|
|
88
|
+
export declare function assessTurboFunds(cfg: ArweaveConfig, sizes: number[], walletAddr?: string): Promise<TurboFundsCheck | null>;
|
|
89
|
+
//# sourceMappingURL=upload-readiness.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload-readiness.d.ts","sourceRoot":"","sources":["../src/upload-readiness.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,cAAc,CAAC;AACxD,OAAO,EAAyG,KAAK,aAAa,EAAC,MAAM,cAAc,CAAC;AAExJ;;;;;;;;;;;;;;;GAeG;AAEH;;0FAE0F;AAC1F,wBAAgB,cAAc,CAAC,IAAI,EAAE,qBAAqB,GAAG,OAAO,CAEnE;AAED,MAAM,WAAW,eAAe;IAC9B,8FAA8F;IAC9F,UAAU,EAAE,OAAO,CAAC;IACpB,6EAA6E;IAC7E,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;2BAE2B;AAC3B,wBAAgB,eAAe,CAAC,IAAI,EAAE,qBAAqB,EAAE,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAGjG;AAED;;;;4FAI4F;AAC5F,MAAM,MAAM,sBAAsB,GAC9B;IAAC,OAAO,EAAE,SAAS,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;CAAC,GAChG;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,gBAAgB,EAAE,OAAO,CAAA;CAAC,GAC5C;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,iBAAiB,EAAE,OAAO,CAAA;CAAC,GAC9C;IAAC,OAAO,EAAE,OAAO,CAAA;CAAC,CAAC;AAEvB;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAAC,IAAI,EAAE,qBAAqB,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAc1H;AAED,MAAM,WAAW,eAAe;IAC9B,wFAAwF;IACxF,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB;gEAC4D;IAC5D,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,wFAAwF;IACxF,eAAe,EAAE,MAAM,CAAC;IACxB;;;gDAG4C;IAC5C,aAAa,CAAC,EAAE;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAC,GAAG,IAAI,CAAC;CACxD;AAED;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAuBhI"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { ARWEAVE_FREE_UPLOAD_LIMIT, arweaveFunding, turboBalanceForAddress, turboUploadCostUsd, turboUploadWinc } from './arweave.js';
|
|
2
|
+
/**
|
|
3
|
+
* Deploy-time storage readiness — what a REAL upload needs, decided from local facts (and, for the
|
|
4
|
+
* best-effort USD estimate, one price-API call) so "dry-run passes, then the real deploy fails at
|
|
5
|
+
* upload" can't happen. Three questions, each pure aside from the one noted network call:
|
|
6
|
+
*
|
|
7
|
+
* - {@link planTurboUpload} — is THIS ONE file free (Turbo's 100 KiB tier) or chargeable?
|
|
8
|
+
* - {@link assessStorageReadiness} — across every backend, what does a real upload of `sizes` need
|
|
9
|
+
* (Arweave credits, a Pinata JWT, a cloud public base)?
|
|
10
|
+
* - {@link assessTurboFunds} — does the resolved Turbo identity have ENOUGH prepaid credits for
|
|
11
|
+
* `sizes`, and (when it's short) does the creator's own wallet already hold some?
|
|
12
|
+
*
|
|
13
|
+
* None of this mints or spends anything — it only INTERROGATES the resolved provider. Identity
|
|
14
|
+
* creation is the caller's job (the CLI's managed-key flow owns the key file location; see
|
|
15
|
+
* `arweave-identity.ts` / `ArweaveConfig.ensureJwk`), so `assessTurboFunds` expects a config that
|
|
16
|
+
* already has one.
|
|
17
|
+
*/
|
|
18
|
+
/** Whether `opts` resolves to Arweave via the Turbo (prepaid-credits) provider — the only provider
|
|
19
|
+
* with a free tier and a funds balance to check; `http-bundler` skips every readiness/funds
|
|
20
|
+
* question below (it has its own auth, not a balance this package knows how to read). */
|
|
21
|
+
export function isTurboArweave(opts) {
|
|
22
|
+
return opts.backend === 'arweave' && (opts.arweave?.provider ?? 'turbo') === 'turbo';
|
|
23
|
+
}
|
|
24
|
+
/** The free-vs-credit decision for ONE Turbo upload of `size` bytes — from the local byte size
|
|
25
|
+
* alone, so it works under `--dry-run` too. Null when `opts` doesn't resolve to Turbo-Arweave
|
|
26
|
+
* (nothing to decide). */
|
|
27
|
+
export function planTurboUpload(opts, size) {
|
|
28
|
+
if (!isTurboArweave(opts))
|
|
29
|
+
return null;
|
|
30
|
+
return { chargeable: size >= ARWEAVE_FREE_UPLOAD_LIMIT, sizeKb: (size / 1024).toFixed(1) };
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* What a REAL deploy against `opts` needs, from `sizes` (per-file byte sizes) alone — Arweave
|
|
34
|
+
* credit needs (plus a best-effort USD estimate for the chargeable bytes), a missing Pinata JWT, or
|
|
35
|
+
* a missing cloud public base. The one network call (Turbo's price API) is best-effort: a failure
|
|
36
|
+
* there degrades `usd` to `null` rather than failing the whole check.
|
|
37
|
+
*/
|
|
38
|
+
export async function assessStorageReadiness(opts, sizes) {
|
|
39
|
+
if (opts.backend === 'arweave') {
|
|
40
|
+
const chargeable = sizes.filter((s) => s >= ARWEAVE_FREE_UPLOAD_LIMIT);
|
|
41
|
+
const usd = chargeable.length ? await turboUploadCostUsd(chargeable.reduce((a, b) => a + b, 0)) : null;
|
|
42
|
+
const totalKb = Math.round(sizes.reduce((a, b) => a + b, 0) / 1024);
|
|
43
|
+
return { backend: 'arweave', overCount: chargeable.length, totalCount: sizes.length, totalKb, usd };
|
|
44
|
+
}
|
|
45
|
+
if (opts.backend === 'ipfs') {
|
|
46
|
+
return { backend: 'ipfs', pinataMissingJwt: (opts.ipfs?.mode ?? 'pinata') === 'pinata' && !opts.ipfs?.pinataJwt };
|
|
47
|
+
}
|
|
48
|
+
if (opts.backend === 'cloud') {
|
|
49
|
+
return { backend: 'cloud', missingPublicBase: !opts.cloud?.publicBase };
|
|
50
|
+
}
|
|
51
|
+
return { backend: 'other' };
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The pre-upload funds decision for a Turbo (Arweave) upload of `sizes` (per-file byte sizes):
|
|
55
|
+
* chargeable bytes → the Winston cost → compare against the identity's prepaid balance. `cfg` must
|
|
56
|
+
* already have a resolved identity (`jwk`/`ethSignerKey`/`remoteEth`) — this module only
|
|
57
|
+
* INTERROGATES the resolved provider, never mints one (see the module doc). Returns `null` when
|
|
58
|
+
* nothing is chargeable (every file is free) or the provider can't be reached right now — a
|
|
59
|
+
* best-effort check never false-blocks; the upload itself will surface any real error.
|
|
60
|
+
*/
|
|
61
|
+
export async function assessTurboFunds(cfg, sizes, walletAddr) {
|
|
62
|
+
const chargeable = sizes.filter((s) => s >= ARWEAVE_FREE_UPLOAD_LIMIT);
|
|
63
|
+
if (chargeable.length === 0)
|
|
64
|
+
return null; // all free
|
|
65
|
+
const funding = await arweaveFunding(cfg);
|
|
66
|
+
let address;
|
|
67
|
+
let haveWinc;
|
|
68
|
+
try {
|
|
69
|
+
address = await funding.address();
|
|
70
|
+
haveWinc = Number((await funding.balance()).winc) || 0;
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return null; // can't reach Turbo to check — don't false-block; the upload will surface any real error
|
|
74
|
+
}
|
|
75
|
+
const needWinc = (await turboUploadWinc(chargeable.reduce((a, b) => a + b, 0))) ?? 0;
|
|
76
|
+
const short = needWinc > 0 ? haveWinc < needWinc : haveWinc <= 0;
|
|
77
|
+
const isEth = !!(cfg.ethSignerKey || cfg.remoteEth);
|
|
78
|
+
let walletCredits;
|
|
79
|
+
if (short && !isEth && walletAddr) {
|
|
80
|
+
const wb = await turboBalanceForAddress(walletAddr, 'ethereum');
|
|
81
|
+
walletCredits = wb ? { winc: Number(wb.winc) || 0, credits: wb.credits } : null;
|
|
82
|
+
}
|
|
83
|
+
return { short, address, isEth, haveWinc, needWinc, chargeableCount: chargeable.length, walletCredits };
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=upload-readiness.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload-readiness.js","sourceRoot":"","sources":["../src/upload-readiness.ts"],"names":[],"mappings":"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"}
|
package/dist/upload.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `abx storage upload` core — upload ONE file and return a public locator that PRESERVES the
|
|
3
|
+
* filename, so the declared mimeType survives when the locator is later attached (`abx attach`).
|
|
4
|
+
* The on-chain metadata field has no MIME slot: the URL extension IS the type declaration, so a
|
|
5
|
+
* bare content-addressed locator (`ar://<txid>`, `<gw>/ipfs/<cid>`) — which has no extension —
|
|
6
|
+
* would make the attached artifact `application/octet-stream`. Extracted from main.ts so the
|
|
7
|
+
* capability-based branch (the exact bug this guards) is unit-testable with fake backends.
|
|
8
|
+
*/
|
|
9
|
+
import type { StorageBackend, StoredContent } from './backend.js';
|
|
10
|
+
export interface UploadResult {
|
|
11
|
+
/** A fetchable public URL for the file. */
|
|
12
|
+
locator: string;
|
|
13
|
+
/** True when `locator` carries the filename (extension → correct declared type on attach). */
|
|
14
|
+
filenamePreserved: boolean;
|
|
15
|
+
/** Set when we had to fall back to a bare (extension-less) locator — the caller warns. */
|
|
16
|
+
fallbackReason?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Store `content` under `name` and return the best available public locator. Preference order:
|
|
20
|
+
* 1. path-addressed host (cloud/S3/R2): the object key IS the filename → `<publicBase>/<name>`.
|
|
21
|
+
* 2. content-addressed host with directory support (ipfs/arweave): wrap the single file in a
|
|
22
|
+
* one-entry directory → `<base>/<name>` (filename preserved).
|
|
23
|
+
* 3. bare content-addressed locator (e.g. kubo IPFS with no dir-add): `<gw>/ipfs/<cid>` — no
|
|
24
|
+
* extension; `filenamePreserved: false` so the caller can warn about the octet-stream type.
|
|
25
|
+
* Throws for a backend that can't produce any public URL (e.g. `fs`).
|
|
26
|
+
*/
|
|
27
|
+
export declare function uploadAndLocate(backend: StorageBackend, name: string, content: StoredContent): Promise<UploadResult>;
|
|
28
|
+
//# sourceMappingURL=upload.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../src/upload.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAC,cAAc,EAAE,aAAa,EAAC,MAAM,cAAc,CAAC;AAGhE,MAAM,WAAW,YAAY;IAC3B,2CAA2C;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,8FAA8F;IAC9F,iBAAiB,EAAE,OAAO,CAAC;IAC3B,0FAA0F;IAC1F,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAID;;;;;;;;GAQG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAyB1H"}
|
package/dist/upload.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { hashContent } from './backend.js';
|
|
2
|
+
const trimSlash = (s) => s.replace(/\/+$/, '');
|
|
3
|
+
/**
|
|
4
|
+
* Store `content` under `name` and return the best available public locator. Preference order:
|
|
5
|
+
* 1. path-addressed host (cloud/S3/R2): the object key IS the filename → `<publicBase>/<name>`.
|
|
6
|
+
* 2. content-addressed host with directory support (ipfs/arweave): wrap the single file in a
|
|
7
|
+
* one-entry directory → `<base>/<name>` (filename preserved).
|
|
8
|
+
* 3. bare content-addressed locator (e.g. kubo IPFS with no dir-add): `<gw>/ipfs/<cid>` — no
|
|
9
|
+
* extension; `filenamePreserved: false` so the caller can warn about the octet-stream type.
|
|
10
|
+
* Throws for a backend that can't produce any public URL (e.g. `fs`).
|
|
11
|
+
*/
|
|
12
|
+
export async function uploadAndLocate(backend, name, content) {
|
|
13
|
+
if (backend.publicBase && backend.putObject) {
|
|
14
|
+
await backend.putObject(name, content);
|
|
15
|
+
return { locator: `${trimSlash(backend.publicBase)}/${name}`, filenamePreserved: true };
|
|
16
|
+
}
|
|
17
|
+
if (backend.putDirectory && backend.locator) {
|
|
18
|
+
try {
|
|
19
|
+
const { base } = await backend.putDirectory([{ name, bytes: content.bytes, contentType: content.contentType }]);
|
|
20
|
+
return { locator: `${trimSlash(base)}/${name}`, filenamePreserved: true };
|
|
21
|
+
}
|
|
22
|
+
catch (e) {
|
|
23
|
+
const hash = hashContent(content.bytes);
|
|
24
|
+
await backend.put(hash, content);
|
|
25
|
+
const loc = await backend.locator(hash);
|
|
26
|
+
if (!loc)
|
|
27
|
+
throw e;
|
|
28
|
+
return { locator: loc, filenamePreserved: false, fallbackReason: e.message };
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (backend.locator) {
|
|
32
|
+
const hash = hashContent(content.bytes);
|
|
33
|
+
await backend.put(hash, content);
|
|
34
|
+
const loc = await backend.locator(hash);
|
|
35
|
+
if (!loc)
|
|
36
|
+
throw new Error(`backend '${backend.id}' stored the file but returned no durable locator`);
|
|
37
|
+
return { locator: loc, filenamePreserved: false };
|
|
38
|
+
}
|
|
39
|
+
throw new Error(`backend '${backend.id}' can't produce a public URL for an attached file — use ipfs, arweave, or cloud (with a public base).`);
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=upload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload.js","sourceRoot":"","sources":["../src/upload.ts"],"names":[],"mappings":"AASA,OAAO,EAAC,WAAW,EAAC,MAAM,cAAc,CAAC;AAWzC,MAAM,SAAS,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAEvD;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,OAAuB,EAAE,IAAY,EAAE,OAAsB;IACjG,IAAI,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;QAC5C,MAAM,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACvC,OAAO,EAAC,OAAO,EAAE,GAAG,SAAS,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,IAAI,EAAE,EAAE,iBAAiB,EAAE,IAAI,EAAC,CAAC;IACxF,CAAC;IACD,IAAI,OAAO,CAAC,YAAY,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QAC5C,IAAI,CAAC;YACH,MAAM,EAAC,IAAI,EAAC,GAAG,MAAM,OAAO,CAAC,YAAY,CAAC,CAAC,EAAC,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAC,CAAC,CAAC,CAAC;YAC5G,OAAO,EAAC,OAAO,EAAE,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,EAAE,iBAAiB,EAAE,IAAI,EAAC,CAAC;QAC1E,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YACxC,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YACjC,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YACxC,IAAI,CAAC,GAAG;gBAAE,MAAM,CAAC,CAAC;YAClB,OAAO,EAAC,OAAO,EAAE,GAAG,EAAE,iBAAiB,EAAE,KAAK,EAAE,cAAc,EAAG,CAAW,CAAC,OAAO,EAAC,CAAC;QACxF,CAAC;IACH,CAAC;IACD,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QACxC,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACjC,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,YAAY,OAAO,CAAC,EAAE,mDAAmD,CAAC,CAAC;QACrG,OAAO,EAAC,OAAO,EAAE,GAAG,EAAE,iBAAiB,EAAE,KAAK,EAAC,CAAC;IAClD,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,YAAY,OAAO,CAAC,EAAE,uGAAuG,CAAC,CAAC;AACjJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@artblocks/abx-storage",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.41",
|
|
4
4
|
"license": "MIT",
|
|
5
|
-
"description": "
|
|
5
|
+
"description": "Storage adapters for ABX content using filesystem, S3-compatible, IPFS, or Arweave backends.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./dist/index.js",
|
|
8
8
|
"exports": {
|
|
@@ -13,18 +13,19 @@
|
|
|
13
13
|
},
|
|
14
14
|
"files": [
|
|
15
15
|
"dist",
|
|
16
|
+
"CHANGELOG.md",
|
|
16
17
|
"LICENSE"
|
|
17
18
|
],
|
|
18
19
|
"sideEffects": false,
|
|
19
20
|
"engines": {
|
|
20
|
-
"node": ">=22.
|
|
21
|
+
"node": ">=22.13.0"
|
|
21
22
|
},
|
|
22
23
|
"repository": {
|
|
23
24
|
"type": "git",
|
|
24
25
|
"url": "git+https://github.com/ArtBlocks/abx.git",
|
|
25
26
|
"directory": "packages/storage"
|
|
26
27
|
},
|
|
27
|
-
"homepage": "https://
|
|
28
|
+
"homepage": "https://docs.abx.io",
|
|
28
29
|
"bugs": "https://github.com/ArtBlocks/abx/issues",
|
|
29
30
|
"keywords": [
|
|
30
31
|
"abx",
|
|
@@ -38,12 +39,19 @@
|
|
|
38
39
|
"access": "public"
|
|
39
40
|
},
|
|
40
41
|
"dependencies": {
|
|
41
|
-
"viem": "^2.
|
|
42
|
-
"@artblocks/abx-sdk": "0.1.0-alpha.
|
|
42
|
+
"viem": "^2.56.5",
|
|
43
|
+
"@artblocks/abx-sdk": "0.1.0-alpha.41"
|
|
43
44
|
},
|
|
44
|
-
"
|
|
45
|
-
"@
|
|
46
|
-
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"@artblocks/abx-storage-arweave": ">=0.1.0-alpha.0"
|
|
47
|
+
},
|
|
48
|
+
"peerDependenciesMeta": {
|
|
49
|
+
"@artblocks/abx-storage-arweave": {
|
|
50
|
+
"optional": true
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@artblocks/abx-storage-arweave": "0.1.0-alpha.5"
|
|
47
55
|
},
|
|
48
56
|
"scripts": {
|
|
49
57
|
"build": "tsc -b tsconfig.build.json"
|