@artblocks/abx-cli 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.
Files changed (189) hide show
  1. package/CHANGELOG.md +161 -0
  2. package/assets/renderer-scaffold/README.md +41 -11
  3. package/assets/renderer-scaffold/foundry.toml +5 -0
  4. package/assets/renderer-scaffold/remappings.txt +1 -1
  5. package/assets/renderer-scaffold/script/DeployHooks.s.sol +24 -0
  6. package/assets/renderer-scaffold/script/Preview.s.sol +99 -0
  7. package/assets/renderer-scaffold/src/MyHooks.sol +20 -0
  8. package/assets/renderer-scaffold/src/MyRenderer.sol +4 -4
  9. package/assets/renderer-scaffold/src/MyTraits.sol +2 -2
  10. package/assets/renderer-scaffold/test/MyRenderer.t.sol +60 -3
  11. package/dist/bin.d.ts +26 -0
  12. package/dist/bin.d.ts.map +1 -0
  13. package/dist/bin.js +63 -0
  14. package/dist/bin.js.map +1 -0
  15. package/dist/capabilities.d.ts +99 -0
  16. package/dist/capabilities.d.ts.map +1 -0
  17. package/dist/capabilities.js +144 -0
  18. package/dist/capabilities.js.map +1 -0
  19. package/dist/commands/auth.d.ts +54 -0
  20. package/dist/commands/auth.d.ts.map +1 -0
  21. package/dist/commands/auth.js +447 -0
  22. package/dist/commands/auth.js.map +1 -0
  23. package/dist/commands/deploy.d.ts +242 -0
  24. package/dist/commands/deploy.d.ts.map +1 -0
  25. package/dist/commands/deploy.js +5429 -0
  26. package/dist/commands/deploy.js.map +1 -0
  27. package/dist/commands/feedback.d.ts +7 -0
  28. package/dist/commands/feedback.d.ts.map +1 -0
  29. package/dist/commands/feedback.js +147 -0
  30. package/dist/commands/feedback.js.map +1 -0
  31. package/dist/commands/maintenance.d.ts +4 -0
  32. package/dist/commands/maintenance.d.ts.map +1 -0
  33. package/dist/commands/maintenance.js +114 -0
  34. package/dist/commands/maintenance.js.map +1 -0
  35. package/dist/commands/project.d.ts +429 -0
  36. package/dist/commands/project.d.ts.map +1 -0
  37. package/dist/commands/project.js +1720 -0
  38. package/dist/commands/project.js.map +1 -0
  39. package/dist/commands/reads.d.ts +85 -0
  40. package/dist/commands/reads.d.ts.map +1 -0
  41. package/dist/commands/reads.js +868 -0
  42. package/dist/commands/reads.js.map +1 -0
  43. package/dist/commands/scaffold.d.ts +89 -0
  44. package/dist/commands/scaffold.d.ts.map +1 -0
  45. package/dist/commands/scaffold.js +738 -0
  46. package/dist/commands/scaffold.js.map +1 -0
  47. package/dist/commands/service.d.ts +67 -0
  48. package/dist/commands/service.d.ts.map +1 -0
  49. package/dist/commands/service.js +748 -0
  50. package/dist/commands/service.js.map +1 -0
  51. package/dist/commands/storage.d.ts +51 -0
  52. package/dist/commands/storage.d.ts.map +1 -0
  53. package/dist/commands/storage.js +399 -0
  54. package/dist/commands/storage.js.map +1 -0
  55. package/dist/commands/submit-app.d.ts +102 -0
  56. package/dist/commands/submit-app.d.ts.map +1 -0
  57. package/dist/commands/submit-app.js +622 -0
  58. package/dist/commands/submit-app.js.map +1 -0
  59. package/dist/config.d.ts +105 -2
  60. package/dist/config.d.ts.map +1 -1
  61. package/dist/config.js +351 -12
  62. package/dist/config.js.map +1 -1
  63. package/dist/conformance.d.ts +31 -0
  64. package/dist/conformance.d.ts.map +1 -0
  65. package/dist/conformance.js +390 -0
  66. package/dist/conformance.js.map +1 -0
  67. package/dist/contract-read-error.d.ts +5 -0
  68. package/dist/contract-read-error.d.ts.map +1 -0
  69. package/dist/contract-read-error.js +37 -0
  70. package/dist/contract-read-error.js.map +1 -0
  71. package/dist/deploy-plan.d.ts +150 -0
  72. package/dist/deploy-plan.d.ts.map +1 -0
  73. package/dist/deploy-plan.js +31 -0
  74. package/dist/deploy-plan.js.map +1 -0
  75. package/dist/deps.d.ts +6 -39
  76. package/dist/deps.d.ts.map +1 -1
  77. package/dist/deps.js +4 -68
  78. package/dist/deps.js.map +1 -1
  79. package/dist/errors.d.ts +20 -0
  80. package/dist/errors.d.ts.map +1 -0
  81. package/dist/errors.js +25 -0
  82. package/dist/errors.js.map +1 -0
  83. package/dist/flag-allowlists.d.ts +53 -0
  84. package/dist/flag-allowlists.d.ts.map +1 -0
  85. package/dist/flag-allowlists.js +184 -0
  86. package/dist/flag-allowlists.js.map +1 -0
  87. package/dist/flags.d.ts +47 -0
  88. package/dist/flags.d.ts.map +1 -1
  89. package/dist/flags.js +125 -1
  90. package/dist/flags.js.map +1 -1
  91. package/dist/jsonout.d.ts +37 -0
  92. package/dist/jsonout.d.ts.map +1 -0
  93. package/dist/jsonout.js +79 -0
  94. package/dist/jsonout.js.map +1 -0
  95. package/dist/kind.d.ts +57 -0
  96. package/dist/kind.d.ts.map +1 -0
  97. package/dist/kind.js +122 -0
  98. package/dist/kind.js.map +1 -0
  99. package/dist/main.js +786 -4838
  100. package/dist/main.js.map +1 -1
  101. package/dist/mintpage.d.ts +17 -2
  102. package/dist/mintpage.d.ts.map +1 -1
  103. package/dist/mintpage.js +241 -54
  104. package/dist/mintpage.js.map +1 -1
  105. package/dist/output.d.ts +179 -0
  106. package/dist/output.d.ts.map +1 -0
  107. package/dist/output.js +780 -0
  108. package/dist/output.js.map +1 -0
  109. package/dist/ownerops.d.ts +381 -57
  110. package/dist/ownerops.d.ts.map +1 -1
  111. package/dist/ownerops.js +2014 -359
  112. package/dist/ownerops.js.map +1 -1
  113. package/dist/preview.d.ts +23 -5
  114. package/dist/preview.d.ts.map +1 -1
  115. package/dist/preview.js +95 -43
  116. package/dist/preview.js.map +1 -1
  117. package/dist/prompt.d.ts +17 -0
  118. package/dist/prompt.d.ts.map +1 -0
  119. package/dist/prompt.js +19 -0
  120. package/dist/prompt.js.map +1 -0
  121. package/dist/provision.d.ts +3 -13
  122. package/dist/provision.d.ts.map +1 -1
  123. package/dist/provision.js +19 -21
  124. package/dist/provision.js.map +1 -1
  125. package/dist/remote.d.ts +157 -52
  126. package/dist/remote.d.ts.map +1 -1
  127. package/dist/remote.js +435 -46
  128. package/dist/remote.js.map +1 -1
  129. package/dist/riskgate.d.ts +62 -0
  130. package/dist/riskgate.d.ts.map +1 -0
  131. package/dist/riskgate.js +234 -0
  132. package/dist/riskgate.js.map +1 -0
  133. package/dist/scaffold.d.ts +12 -0
  134. package/dist/scaffold.d.ts.map +1 -0
  135. package/dist/scaffold.js +56 -0
  136. package/dist/scaffold.js.map +1 -0
  137. package/dist/schema.d.ts +36 -1
  138. package/dist/schema.d.ts.map +1 -1
  139. package/dist/schema.js +121 -26
  140. package/dist/schema.js.map +1 -1
  141. package/dist/script-chunks.d.ts +8 -0
  142. package/dist/script-chunks.d.ts.map +1 -0
  143. package/dist/script-chunks.js +35 -0
  144. package/dist/script-chunks.js.map +1 -0
  145. package/dist/served.d.ts +30 -0
  146. package/dist/served.d.ts.map +1 -0
  147. package/dist/served.js +112 -0
  148. package/dist/served.js.map +1 -0
  149. package/dist/signer.d.ts +13 -0
  150. package/dist/signer.d.ts.map +1 -1
  151. package/dist/signer.js +84 -15
  152. package/dist/signer.js.map +1 -1
  153. package/dist/update-check.d.ts +86 -5
  154. package/dist/update-check.d.ts.map +1 -1
  155. package/dist/update-check.js +161 -20
  156. package/dist/update-check.js.map +1 -1
  157. package/package.json +13 -12
  158. package/skill/SKILL.md +181 -347
  159. package/skill/agents/openai.yaml +4 -0
  160. package/skill/reference/capabilities.md +188 -0
  161. package/skill/reference/code.md +220 -0
  162. package/skill/reference/creator-token.md +94 -0
  163. package/skill/reference/deploy.md +174 -0
  164. package/skill/reference/diagnose.md +178 -0
  165. package/skill/reference/hosting.md +186 -93
  166. package/skill/reference/operate.md +220 -0
  167. package/skill/reference/services.md +121 -0
  168. package/skill/reference/setup.md +154 -36
  169. package/assets/renderer-scaffold/src/interfaces/IAbxFieldRenderer.sol +0 -32
  170. package/assets/renderer-scaffold/src/interfaces/IAbxParams.sol +0 -26
  171. package/dist/inspect.d.ts +0 -48
  172. package/dist/inspect.d.ts.map +0 -1
  173. package/dist/inspect.js +0 -184
  174. package/dist/inspect.js.map +0 -1
  175. package/dist/migrate.d.ts +0 -65
  176. package/dist/migrate.d.ts.map +0 -1
  177. package/dist/migrate.js +0 -180
  178. package/dist/migrate.js.map +0 -1
  179. package/dist/onchain-uri.d.ts +0 -97
  180. package/dist/onchain-uri.d.ts.map +0 -1
  181. package/dist/onchain-uri.js +0 -243
  182. package/dist/onchain-uri.js.map +0 -1
  183. package/dist/upload.d.ts +0 -28
  184. package/dist/upload.d.ts.map +0 -1
  185. package/dist/upload.js +0 -41
  186. package/dist/upload.js.map +0 -1
  187. package/skill/reference/code-projects.md +0 -246
  188. package/skill/reference/operating.md +0 -116
  189. package/skill/reference/troubleshooting.md +0 -28
package/CHANGELOG.md ADDED
@@ -0,0 +1,161 @@
1
+ # @artblocks/abx-cli
2
+
3
+ ## 0.1.0-alpha.41
4
+
5
+ ### Minor Changes
6
+
7
+ - c068a0f: `abx artifacts <address> [--token <id>]` reads a token's `artifacts` manifest directly, instead of
8
+ fetching and parsing the whole served tokenURI document. Reports every entry plus every
9
+ producer-registered effect row — current AND stale — each labeled against the token's active
10
+ `inputsHash`, so a param change that re-addressed a render is diagnosable rather than silently
11
+ absent. `--remote <name|url>` reads what a hosted resolver actually reports (the surface that owns a
12
+ hosted project's real artifact set), mirroring the existing `abx verify` / `abx verify --remote`
13
+ split. Neither "not registered here" nor "not registered on that remote" fails the command — both
14
+ return a stable `{surface, registered, available, reason, entries, effects}` JSON shape.
15
+ `@artblocks/abx-token-api` exports the underlying `tokenArtifacts()` read for direct SDK/server use.
16
+ - c068a0f: `abx attach` now distinguishes the on-chain field write from off-chain SERVING — the write can
17
+ succeed while nothing is able to serve it. It prints the canonical, directly-fetchable URL for every
18
+ attached key (`{base}/{chainId}/{address}[/<id>]/data/<key>`) so nobody hand-assembles the route, and
19
+ probes whether a resolver actually answers for the project right now, warning when none does (with
20
+ no resolver base baked in at all, or with one baked in that isn't currently reachable). `--collection`
21
+ now correctly reads `contractURIBase` (it previously always read `tokenURIBase`, even for a
22
+ collection-scope attach).
23
+ - c068a0f: Local data-directory resolution (`.abx-self-host` / `ABX_DATA_DIR`) is now centralized in one SDK
24
+ resolver (`resolveDataDir` from `@artblocks/abx-sdk/node`) instead of five independent copies of the
25
+ same fallback across the CLI, indexer, and storage packages — the setup that could silently split a
26
+ project's SQLite projection from its managed Arweave key into two different directories.
27
+
28
+ A handful of READ commands (`status`, `state`, `verify`, `doctor`, `capabilities`, `tokens`,
29
+ `tokenuri`, `contracturi`, `inspect`, `minter show`) now search upward from the current directory,
30
+ git-style, for an already-existing `.abx-self-host` before falling back to creating one at cwd —
31
+ bounded at the home directory, a `.git` repository root, or the filesystem root, and never crossing
32
+ into an unrelated directory tree. Running one of these from a project subdirectory (e.g. a
33
+ `contracts/` folder) now finds the same node a run from the project root would, instead of quietly
34
+ reporting an empty one. `abx status`'s existing `data: <path>` line — and a new one-line notice —
35
+ name the directory whenever it was found this way. Every WRITE command is unaffected: it always
36
+ resolves strictly to the current directory, exactly as before, so nothing is ever created somewhere
37
+ you didn't `cd` into. `ABX_DATA_DIR`, when set, still always wins outright and disables the search
38
+ entirely.
39
+
40
+ - c068a0f: Every `deploy` / `deploy-series` / `deploy-code` `--json` emit — dry-run and real send, across every
41
+ product lane and `deploy-code --resume` — now carries a versioned, structured `plan` object alongside
42
+ the existing fields: wallet transaction/approval count, expected signer/owner/royalty/minter/payee
43
+ roles, custody and URI-resolution choices, the mint plan, a cost estimate where one is computed, every
44
+ warning that run raised, and (code lanes) the per-surface thumbnail/traits/postParams disposition plus
45
+ dependency-registry info. A field a lane has no answer for is reported as `null`, never silently
46
+ absent. The human dry-run/confirm output is unchanged, byte for byte.
47
+ - c068a0f: `deploy-code --copies --code-dir <dir>` now works: an EditionCode project can point `code` at a
48
+ hosted directory build (index.html + assets uploaded to ipfs/arweave/cloud), the same as the
49
+ existing SeriesCode (721) lane.
50
+ - c068a0f: `deploy-code --copies --image-base <url>` now works: an EditionCode project can bake a
51
+ deterministic per-id off-chain still (`{base}/{id}.png`), the same field write as the existing
52
+ SeriesCode (721) lane, mutually exclusive with an on-chain `--image-renderer`. The effect runner's
53
+ render sweep excludes an id with zero live copies (no mint-time seed drawn yet), mirroring how a
54
+ burned 721 token is already excluded.
55
+ - c068a0f: `deploy-code --resume <address>` now works against an EditionCode (`--copies`) target, not just
56
+ SeriesCode. The mint leg diffs per id against that id's own `totalSupply(id)` — pass the same
57
+ content flags the original deploy used, plus `--mint-count`/`--mint-amount` if it premint any ids.
58
+ - c068a0f: `reconstructProject()` and `reconstructIncremental()` accept `toBlock: 'safe' | 'finalized'` in
59
+ addition to a literal block number and the existing `'latest'` default. The tag is resolved to a
60
+ concrete inclusive block number (via the new `resolveBlockTag()`) before any `eth_getLogs` scan
61
+ starts, and that number — never the tag — is what lands in the persisted `ProjectState.toBlock`. An
62
+ RPC that doesn't support the requested tag throws the new `BlockTagUnavailableError` instead of
63
+ silently falling back to `latest`. `SelfHostIndexer#reindex` gained a matching `blockTag` option.
64
+ Existing callers (the `'latest'`/unset default) are unaffected.
65
+
66
+ `abx index --to-block safe|finalized` stops a re-index at a reorg-safe boundary.
67
+
68
+ - c068a0f: The Solidity renderer scaffold (`abx scaffold renderer` / `abx scaffold solidity`) now ships
69
+ `script/Preview.s.sol`: a Forge script that runs the actual `IAbxFieldRenderer.render` interface
70
+ against representative token id / seed / PostParam inputs (all overridable via env vars) and writes
71
+ the SVG/JSON output to `preview-out/` for human inspection — `forge test` proves the renderer
72
+ behaves, but never let you look at what it drew. Reverts are surfaced clearly; the script is explicit
73
+ that its gas output is a local-EVM sanity check, not a production gas measurement.
74
+ - c068a0f: Add `abx replace-script <address> --script <file>` — a safe, first-class way to replace an UNLOCKED
75
+ code project's on-chain program (`SeriesCode`/`EditionCode`, before `abx lock-script`). It refuses
76
+ outright on a locked script or a non-code target, diffs the replacement by content against what's
77
+ on-chain (an index that already matches is never re-sent), handles a smaller replacement by queuing
78
+ the trailing `removeLastScriptChunk` calls it needs, folds every write and remove into ONE atomic
79
+ transaction (a revert can never leave a half-applied script), and reads the completed script back to
80
+ verify it reassembles exactly to the file before reporting success. Supports `--dry-run` and every
81
+ signing lane.
82
+
83
+ The SDK gains the primitives this is built on: `prepareSetScriptChunk`/`prepareRemoveLastScriptChunk`
84
+ (no `prepare*` wrapper existed for `OnChainScript.sol`'s writers before this — every caller hand-
85
+ encoded `encodeFunctionData`), plus `planScriptReplace`/`verifyScriptReplace`, which take a small
86
+ reader interface (mirroring `resume.ts`'s own reader seam) so the diff and post-write verification
87
+ are testable without a chain.
88
+
89
+ - c068a0f: SQLite maintenance for existing and long-running self-hosted stores (issue #113). `SqliteStore`
90
+ gains `autoVacuumMode`/`vacuumStats` (detection), `vacuumConvert` (a one-time, explicit full
91
+ `VACUUM` for a store that predates incremental auto-vacuum), and `runIncrementalVacuum` (a bounded
92
+ reclaim pass). `SelfHostIndexer.startVacuumMaintenance` runs that bounded reclaim automatically
93
+ between chain-watch ticks — never inline with a request. The new `abx vacuum [status|convert|
94
+ incremental]` CLI command exposes both halves to an operator. Also fixes a pragma-ordering bug that
95
+ left EVERY store — not just pre-existing ones — stuck at `auto_vacuum='none'` despite the earlier
96
+ incremental auto-vacuum fix (`PRAGMA auto_vacuum` must precede `PRAGMA journal_mode = WAL`, not just
97
+ the schema's first `CREATE TABLE`).
98
+ - c068a0f: `abx state` now reports every irreversible lock a collection can carry — token/contract URI, script,
99
+ dependencies, param hooks, and the standard `METADATA_FIELD` set — for both ERC-721 and ERC-1155
100
+ families, not just `paramHooks.locked`. Each lock is a distinct `true` (frozen) / `false` (open) /
101
+ `null` (unread — never collapsed into `false`) reading, in both human and `--json` output, alongside
102
+ the exact owner command that freezes it.
103
+ - c068a0f: `abx verify --json` gains a sibling `availability` field (render/serve-readiness: `available` /
104
+ `partial` / `unavailable` / `unknown`), kept separate from the integrity-only `ok`/`contentIntegrity`
105
+ that the command's exit status still reflects exactly as before. A locator-committed `image` field
106
+ (ipfs/arweave/url/url-template) this command doesn't re-fetch now reports as a `verified: null`
107
+ content check instead of silently reading as no commitment at all. Fixes a side-bug: `abx verify
108
+ --remote <r> --json` used to route around `--json` entirely and emit no payload — it now emits the
109
+ same shape as the local lane, plus a `"not-checked"` `contentIntegrity` state for when byte
110
+ integrity genuinely wasn't asked (no credential, rejected token, or an older resolver).
111
+ - c068a0f: Additive for SDK consumers — a new exported function, nothing existing changed shape or behavior.
112
+
113
+ `abx verify` now compares a collection's actual `tokenURIRenderer` and `animation_url` generator
114
+ pointers against the current canonical singletons for its chain, reporting each as current or
115
+ not-current (never treating an older-but-working deployment as broken). Adds the SDK helper
116
+ `isCurrentGenerator` alongside the existing `isCurrentRenderer` probe.
117
+
118
+ ### Patch Changes
119
+
120
+ - c068a0f: `abx storage upload <path>` now stats the path before reading it, so a directory, a missing file, or
121
+ an unreadable file gets an actionable error instead of a raw Node errno (`EISDIR: illegal operation
122
+ on a directory, read`, a bare `ENOENT` stack). The directory message points at the commands that
123
+ actually upload folder content — `abx deploy-series --dir` and `abx deploy-code --code-dir` — since
124
+ `storage upload` only ever handles a single file.
125
+ - c068a0f: `abx submit-app` no longer fails silently on RPC catch-up: the mint's token id is now recoverable
126
+ even under `--json` if metadata configuration can't proceed, the first metadata write after a fresh
127
+ mint retries a few times with backoff before giving up, and a caller who exhausts that bound gets an
128
+ explicit message naming the token id and confirming a re-run picks up exactly where it left off
129
+ (never a duplicate mint — the collection is already `claimed`). `--json`'s `withJson` helper also now
130
+ flushes whatever payload a command already `emit()`-ed before a later step throws, instead of leaving
131
+ `--json` callers with nothing on stdout when a command fails partway through.
132
+ - Updated dependencies [c068a0f]
133
+ - Updated dependencies [c068a0f]
134
+ - Updated dependencies [c068a0f]
135
+ - Updated dependencies [c068a0f]
136
+ - Updated dependencies [c068a0f]
137
+ - Updated dependencies [c068a0f]
138
+ - Updated dependencies [c068a0f]
139
+ - Updated dependencies [c068a0f]
140
+ - Updated dependencies [c068a0f]
141
+ - Updated dependencies [c068a0f]
142
+ - Updated dependencies [c068a0f]
143
+ - @artblocks/abx-token-api@0.1.0-alpha.34
144
+ - @artblocks/abx-sdk@0.1.0-alpha.31
145
+ - @artblocks/abx-indexer@0.1.0-alpha.32
146
+ - @artblocks/abx-storage@0.1.0-alpha.31
147
+
148
+ ## 0.1.0-alpha.40
149
+
150
+ ### Patch Changes
151
+
152
+ - 64248d9: Establish the public source baseline and refresh dependency constraints.
153
+ - Updated dependencies [64248d9]
154
+ - @artblocks/abx-indexer@0.1.0-alpha.31
155
+ - @artblocks/abx-sdk@0.1.0-alpha.30
156
+ - @artblocks/abx-storage@0.1.0-alpha.30
157
+ - @artblocks/abx-token-api@0.1.0-alpha.33
158
+
159
+ ## Unreleased
160
+
161
+ Public release history begins here.
@@ -1,9 +1,10 @@
1
- # ABX in-chain renderer scaffold
1
+ # ABX Solidity extension workspace
2
2
 
3
- A ready-to-build Foundry project for the **fully on-chain Solidity art lane** the artwork is a
4
- smart contract that computes an SVG (and traits) from the token's `seed` and collector-set
5
- PostParams. No JavaScript, no browser, no bucket, no resolver, no effect runner: once the renderer
6
- is deployed and wired, `tokenURI` name, image, traits resolves from chain forever.
3
+ A ready-to-build Foundry project for ABX's actual Solidity extension surfaces. Each role remains a
4
+ separate deployable contract: image/traits rendering, configure-time validation, transfer lifecycle,
5
+ and read-time augmentation. Use only the roles your project needs. There is intentionally no
6
+ "minter hook": minting is an external-minter capability, while the three PostParam hooks are
7
+ configure, transfer and augment.
7
8
 
8
9
  `abx` does **not** compile or deploy Solidity for you; you build, test, and deploy this project with
9
10
  Foundry, then hand the deployed address(es) to `abx deploy-code`, which verifies they have code.
@@ -11,16 +12,21 @@ Foundry, then hand the deployed address(es) to `abx deploy-code`, which verifies
11
12
  ## What's here
12
13
 
13
14
  - `src/MyRenderer.sol` — the **image** renderer: seed → geometry, a `palette` HexColor PostParam →
14
- tint. **Fork the geometry/palette math for your own art.**
15
+ tint. **Fork the geometry/palette math for your own work.**
15
16
  - `src/MyTraits.sol` — the **attributes** renderer: reads the *same* seed math so traits agree with
16
17
  the image by construction.
17
- - `src/interfaces/`the two ABX interfaces you implement/read (`IAbxFieldRenderer`, `IAbxParams`).
18
- Read `IAbxFieldRenderer.sol` first: it documents **the five invariants** (above all, `render()`
19
- must NEVER revert a revert bricks the whole `tokenURI`, including the collection surface
20
- `tokenId == type(uint256).max`).
18
+ - `src/MyHooks.sol`small wrappers around one reference implementation for each PostParam hook
19
+ role. Fork a role independently; do not merge their authority just because they share a workspace.
20
+ - `abx-contracts~2.0.0` — the exact-pinned canonical interfaces, 721/1155 fixed-price minters,
21
+ reference hooks, token implementations and real-clone test harness. The scaffold carries no
22
+ hand-copied ABX interface.
21
23
  - `test/MyRenderer.t.sol` — proves `render()` never reverts (incl. no-seed, no-palette, the
22
24
  collection surface, and a fuzz over every seed/tokenId) and that image ↔ traits stay coherent.
23
- - `script/Deploy.s.sol` deploys both renderers and prints the addresses + the next command.
25
+ - `script/Deploy.s.sol` deploys renderers; `script/DeployHooks.s.sol` deploys the three hook examples
26
+ after a token address exists.
27
+ - `script/Preview.s.sol` — a **local preview**, not a test: runs `render()` with representative
28
+ inputs and writes the actual output to `preview-out/` so you can look at it (open the `.svg` in a
29
+ browser). `forge test` proves render() behaves; this script is the only way to see what it drew.
24
30
 
25
31
  ## Build, test, deploy
26
32
 
@@ -29,10 +35,23 @@ forge soldeer install # fetch solady + forge-std (pinned in foundry.tom
29
35
  forge build
30
36
  forge test # MUST pass — especially the never-revert cases
31
37
 
38
+ # look at what the renderer actually produces (writes preview-out/image.svg + attributes.json):
39
+ forge script script/Preview.s.sol
40
+ # override the representative token id / seed / palette without editing the script:
41
+ PREVIEW_TOKEN_ID=42 PREVIEW_SEED=0x00...abc PREVIEW_PALETTE=0x00...ff3366 forge script script/Preview.s.sol
42
+
32
43
  # deploy to your testnet, then copy the printed addresses:
33
44
  forge script script/Deploy.s.sol --rpc-url <your-rpc-url> --private-key <key> --broadcast
45
+
46
+ # optional PostParam hooks, deployed separately after the token exists:
47
+ forge script script/DeployHooks.s.sol --sig "run(address,uint256)" <token> 1024 \
48
+ --rpc-url <your-rpc-url> --private-key <key> --broadcast
34
49
  ```
35
50
 
51
+ `script/Preview.s.sol`'s gas numbers are a local-EVM sanity check, not a production gas measurement —
52
+ see the disclaimer in that file. For real gas numbers, deploy (above) and read `tokenURI` on the
53
+ actual chain you're targeting.
54
+
36
55
  ## Wire it into a drop
37
56
 
38
57
  ```bash
@@ -44,6 +63,17 @@ abx deploy-code \
44
63
  --name "Your Collection" --symbol SYM [--max N] --sign --for <your wallet>
45
64
  ```
46
65
 
66
+ Then, only if using the hook examples:
67
+
68
+ ```bash
69
+ abx set-param-hooks <token> --configure <configure> --augment <augment> --transfer <transfer>
70
+ ```
71
+
72
+ The transfer hook is a veto surface and also runs on mint. The reference counts only real
73
+ non-zero-to-non-zero moves and pins `msg.sender` to the token. On ERC-1155 it counts copies moved per
74
+ id; it never pretends a multi-copy id has one current owner. Freeze the selected hook addresses with
75
+ `abx lock-param-hooks` when the project is ready to make that authority immutable.
76
+
47
77
  - `--onchain-uri` (no `--script`/`--code-dir`) is the in-chain lane — a small Solidity SVG is a
48
78
  great fit for an on-chain `tokenURI` (unlike a 200KB JS bundle).
49
79
  - `--schema palette:HexColor:TokenOwner` declares the collector param your renderer reads. **Omit it
@@ -9,7 +9,12 @@ evm_version = "paris" # broad multi-chain compatibility (no PUSH0/cancun
9
9
  optimizer = true
10
10
  optimizer_runs = 1_000_000 # a renderer is deployed once and called forever → optimize runtime
11
11
 
12
+ # script/Preview.s.sol writes rendered output here for human inspection (vm.writeFileBinary).
13
+ # Foundry denies filesystem writes by default; this is the narrowest grant that lets it work.
14
+ fs_permissions = [{ access = "read-write", path = "./preview-out" }]
15
+
12
16
  # Dependencies are fetched with soldeer (`forge soldeer install`), pinned here.
13
17
  [dependencies]
18
+ abx-contracts = "2.0.0"
14
19
  solady = "0.1.26"
15
20
  forge-std = "1.16.1"
@@ -1,3 +1,3 @@
1
1
  solady/=dependencies/solady-0.1.26/src/
2
2
  forge-std/=dependencies/forge-std-1.16.1/src/
3
- abx/=src/interfaces/
3
+ abx-contracts/=dependencies/abx-contracts-2.0.0/
@@ -0,0 +1,24 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity 0.8.28;
3
+
4
+ import {Script, console2} from "forge-std/Script.sol";
5
+ import {MyConfigureHook, MyTransferHook, MyAugmentHook} from "../src/MyHooks.sol";
6
+
7
+ /// Deploy only the hook roles this project needs. TOKEN must already be deployed because every
8
+ /// write-time hook is pinned to its caller; this avoids a reusable hook accidentally trusting any
9
+ /// contract that knows its selector.
10
+ contract DeployHooks is Script {
11
+ function run(address token, uint256 maxDataLength) external {
12
+ vm.startBroadcast();
13
+ MyConfigureHook configure = new MyConfigureHook(token, maxDataLength);
14
+ MyTransferHook transfer = new MyTransferHook(token);
15
+ MyAugmentHook augment = new MyAugmentHook(transfer);
16
+ vm.stopBroadcast();
17
+ console2.log("configure hook:", address(configure));
18
+ console2.log("augment hook :", address(augment));
19
+ console2.log("transfer hook :", address(transfer));
20
+ console2.log(
21
+ "Next: abx set-param-hooks <token> --configure <configure> --augment <augment> --transfer <transfer>"
22
+ );
23
+ }
24
+ }
@@ -0,0 +1,99 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.20;
3
+
4
+ import {Script, console2} from "forge-std/Script.sol";
5
+ import {IAbxFieldRenderer} from "abx-contracts/src/uri/IAbxFieldRenderer.sol";
6
+ import {MyRenderer} from "../src/MyRenderer.sol";
7
+ import {MyTraits} from "../src/MyTraits.sol";
8
+ import {MockParams} from "../test/MyRenderer.t.sol";
9
+
10
+ /// @notice A human-inspection harness, not a test. `forge test` is what proves render() behaves
11
+ /// (never reverts, traits agree with the image, etc) — this script exists ONLY because
12
+ /// assertions don't let a person actually LOOK at the SVG/JSON a renderer produces. It
13
+ /// calls the exact same `IAbxFieldRenderer.render(token, tokenId, field)` interface the
14
+ /// deployed ABX token calls at read time, against a `MockParams` standing in for the
15
+ /// token (the same stand-in `test/MyRenderer.t.sol` uses), and writes the raw output to
16
+ /// disk so it can be opened in a browser or editor.
17
+ ///
18
+ /// GAS DISCLAIMER — read before trusting any number this script prints: a local
19
+ /// `forge script` run executes against an in-memory EVM with no real chain's calldata
20
+ /// pricing, contract-size/warm-storage state, or congestion. The gas this script reports
21
+ /// (and even a real `--broadcast` dry run) is NOT a production gas measurement. If you
22
+ /// need real numbers, deploy with `script/Deploy.s.sol` and read `tokenURI` on the actual
23
+ /// chain you're targeting.
24
+ ///
25
+ /// Usage (from this scaffold's directory, after `forge soldeer install`):
26
+ /// forge script script/Preview.s.sol
27
+ /// Every input has a representative default and is overridable via env vars, so you can preview
28
+ /// a specific token without editing this file:
29
+ /// PREVIEW_TOKEN_ID=42 PREVIEW_SEED=0x00112233445566778899aabbccddeeff00112233445566778899aabbccddee \
30
+ /// PREVIEW_PALETTE=0xff3366 forge script script/Preview.s.sol
31
+ /// Set PREVIEW_TOKEN_ID to `115792089237316195423570985008687907853269984665640564039457584007913129639935`
32
+ /// (type(uint256).max) to preview the collection surface (contractURI) instead of a token.
33
+ /// Output lands in `preview-out/` at the repo root of this scaffold — open `preview-out/image.svg`
34
+ /// in a browser, `preview-out/attributes.json` in an editor.
35
+ contract Preview is Script {
36
+ function run() external {
37
+ uint256 tokenId = vm.envOr("PREVIEW_TOKEN_ID", uint256(7));
38
+ bytes32 seed = vm.envOr("PREVIEW_SEED", bytes32(uint256(0x1234)));
39
+ // `palette` is a HexColor PostParam: the RGB value packed into the low 3 bytes.
40
+ bytes32 palette = vm.envOr("PREVIEW_PALETTE", bytes32(uint256(0xff3366)));
41
+
42
+ // vm.writeFileBinary does not create parent directories; make sure ours exists.
43
+ vm.createDir("preview-out", true);
44
+
45
+ MyRenderer image = new MyRenderer();
46
+ MyTraits traits = new MyTraits();
47
+ MockParams params = new MockParams();
48
+ params.setToken(tokenId, "seed", seed);
49
+ params.setToken(tokenId, "palette", palette);
50
+
51
+ console2.log("== NOT a gas measurement (see script comment) ==");
52
+ console2.log("== preview inputs ==");
53
+ console2.log("tokenId:", tokenId);
54
+ console2.log("seed :", vm.toString(seed));
55
+ console2.log("palette:", vm.toString(palette));
56
+
57
+ _renderAndDump(image, address(params), tokenId, "image", "image", "preview-out/image");
58
+ _renderAndDump(traits, address(params), tokenId, "attributes", "attributes", "preview-out/attributes");
59
+ }
60
+
61
+ /// @dev Runs the ACTUAL renderer interface — the same call the resolver/on-chain metadata
62
+ /// renderer makes — and surfaces a revert instead of swallowing it, so a miswired field
63
+ /// or a broken invariant shows up here instead of silently bricking `tokenURI` later.
64
+ function _renderAndDump(
65
+ IAbxFieldRenderer renderer,
66
+ address token,
67
+ uint256 tokenId,
68
+ bytes32 field,
69
+ string memory fieldLabel,
70
+ string memory pathNoExt
71
+ ) private {
72
+ console2.log(string.concat("== field: ", fieldLabel, " =="));
73
+ try renderer.render(token, tokenId, field) returns (string memory contentType, bytes memory data) {
74
+ string memory path = string.concat(pathNoExt, ".", _extFor(contentType));
75
+ vm.writeFileBinary(path, data);
76
+ console2.log("contentType:", contentType);
77
+ console2.log("bytes :", data.length);
78
+ console2.log("written to :", path);
79
+ } catch Error(string memory reason) {
80
+ console2.log("REVERTED:", reason);
81
+ } catch Panic(uint256 code) {
82
+ console2.log("REVERTED, panic code:", code);
83
+ } catch (bytes memory lowLevelData) {
84
+ // A custom error (e.g. MyRenderer.UnsupportedField) has no ABI-decoded reason string —
85
+ // this is the raw revert data, selector first, so you can tell which error fired.
86
+ console2.log("REVERTED, raw data:", vm.toString(lowLevelData));
87
+ }
88
+ }
89
+
90
+ /// @dev Extension purely for local viewing convenience — not part of the on-chain contract.
91
+ function _extFor(string memory contentType) private pure returns (string memory) {
92
+ bytes32 ct = keccak256(bytes(contentType));
93
+ if (ct == keccak256("image/svg+xml")) return "svg";
94
+ if (ct == keccak256("application/json")) return "json";
95
+ if (ct == keccak256("text/html")) return "html";
96
+ if (ct == keccak256("text/plain")) return "txt";
97
+ return "bin";
98
+ }
99
+ }
@@ -0,0 +1,20 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity 0.8.28;
3
+
4
+ import {MaxDataLengthConfigureHook} from "abx-contracts/src/reference/MaxDataLengthConfigureHook.sol";
5
+ import {TransferCounterHook} from "abx-contracts/src/reference/TransferCounterHook.sol";
6
+ import {TransferCountAugmentHook} from "abx-contracts/src/reference/TransferCountAugmentHook.sol";
7
+
8
+ /// Fork these thin wrappers when your project needs different policy. Keeping each lifecycle role
9
+ /// in its own contract means a project only deploys and grants the authority it actually uses.
10
+ contract MyConfigureHook is MaxDataLengthConfigureHook {
11
+ constructor(address token, uint256 maxDataLength) MaxDataLengthConfigureHook(token, maxDataLength) {}
12
+ }
13
+
14
+ contract MyTransferHook is TransferCounterHook {
15
+ constructor(address token) TransferCounterHook(token) {}
16
+ }
17
+
18
+ contract MyAugmentHook is TransferCountAugmentHook {
19
+ constructor(TransferCounterHook counter) TransferCountAugmentHook(counter) {}
20
+ }
@@ -2,8 +2,8 @@
2
2
  pragma solidity ^0.8.20;
3
3
 
4
4
  import {LibString} from "solady/utils/LibString.sol";
5
- import {IAbxFieldRenderer} from "abx/IAbxFieldRenderer.sol";
6
- import {IAbxParams} from "abx/IAbxParams.sol";
5
+ import {IAbxFieldRenderer} from "abx-contracts/src/uri/IAbxFieldRenderer.sol";
6
+ import {IAbxParams} from "abx-contracts/src/extensions/params/IAbxParams.sol";
7
7
 
8
8
  /// @title MyRenderer — a fully on-chain generative image, tinted by a collector PostParam
9
9
  /// @notice FORK THIS. It renders the `image` field as an `image/svg+xml` document computed
@@ -19,7 +19,7 @@ import {IAbxParams} from "abx/IAbxParams.sol";
19
19
  /// (declare the `palette` schema, or the param is fixed at the default and collectors
20
20
  /// can't set it.) Pair with MyTraits.sol so image AND traits read the SAME seed math.
21
21
  ///
22
- /// Swap the geometry/palette math below for your own art. Keep the FIVE INVARIANTS in
22
+ /// Swap the geometry/palette math below for your own work. Keep the FIVE INVARIANTS in
23
23
  /// IAbxFieldRenderer — above all, NEVER revert (a revert bricks the whole tokenURI).
24
24
  contract MyRenderer is IAbxFieldRenderer {
25
25
  using LibString for uint256;
@@ -53,7 +53,7 @@ contract MyRenderer is IAbxFieldRenderer {
53
53
  return (CONTENT_TYPE, bytes(_svg(seed, palette)));
54
54
  }
55
55
 
56
- // ── the art: seed → geometry, palette → tint (replace with your own) ──────────
56
+ // ── the work: seed → geometry, palette → tint (replace with your own) ─────────
57
57
 
58
58
  function _svg(bytes32 seed, string memory palette) private pure returns (string memory) {
59
59
  uint256 rings = 3 + (uint8(seed[0]) % 6); // 3..8 rings (MUST match MyTraits' math)
@@ -2,8 +2,8 @@
2
2
  pragma solidity ^0.8.20;
3
3
 
4
4
  import {LibString} from "solady/utils/LibString.sol";
5
- import {IAbxFieldRenderer} from "abx/IAbxFieldRenderer.sol";
6
- import {IAbxParams} from "abx/IAbxParams.sol";
5
+ import {IAbxFieldRenderer} from "abx-contracts/src/uri/IAbxFieldRenderer.sol";
6
+ import {IAbxParams} from "abx-contracts/src/extensions/params/IAbxParams.sol";
7
7
 
8
8
  /// @title MyTraits — on-chain marketplace traits, coherent with MyRenderer
9
9
  /// @notice FORK THIS. Renders the `attributes` field as an `application/json` array a marketplace
@@ -4,7 +4,7 @@ pragma solidity ^0.8.20;
4
4
  import {Test} from "forge-std/Test.sol";
5
5
  import {MyRenderer} from "../src/MyRenderer.sol";
6
6
  import {MyTraits} from "../src/MyTraits.sol";
7
- import {IAbxParams} from "abx/IAbxParams.sol";
7
+ import {IAbxParams} from "abx-contracts/src/extensions/params/IAbxParams.sol";
8
8
 
9
9
  /// A tiny stand-in for the ABX contract's param surface, so the renderers can be tested in
10
10
  /// isolation — set a seed / palette (or don't) and assert the renderer behaves.
@@ -13,16 +13,47 @@ contract MockParams is IAbxParams {
13
13
  mapping(uint256 => mapping(bytes32 => bool)) private ts;
14
14
  mapping(bytes32 => bytes32) private cv;
15
15
  mapping(bytes32 => bool) private cs;
16
+ // `Bytes`/`String` params: the bytes32 holds keccak(content), the content lives here.
17
+ mapping(uint256 => mapping(bytes32 => bytes)) private td;
18
+ mapping(bytes32 => bytes) private cd;
16
19
 
17
20
  function setToken(uint256 id, bytes32 key, bytes32 val) external { tv[id][key] = val; ts[id][key] = true; }
18
21
  function setContract(bytes32 key, bytes32 val) external { cv[key] = val; cs[key] = true; }
19
22
 
23
+ /// Set a payload-typed (`Bytes`/`String`) param the way the real contract does: the scalar slot
24
+ /// carries the keccak COMMITMENT and `valueIsHash` is true, so a renderer that reads the bytes32
25
+ /// gets a hash — the data reader is the only way to the content.
26
+ function setTokenData(uint256 id, bytes32 key, bytes memory content) external {
27
+ td[id][key] = content;
28
+ tv[id][key] = keccak256(content);
29
+ ts[id][key] = true;
30
+ }
31
+ function setContractData(bytes32 key, bytes memory content) external {
32
+ cd[key] = content;
33
+ cv[key] = keccak256(content);
34
+ cs[key] = true;
35
+ }
36
+
20
37
  function tokenParam(uint256 id, bytes32 key) external view returns (bytes32, bool, bool) {
21
- return (tv[id][key], false, ts[id][key]);
38
+ return (tv[id][key], td[id][key].length > 0, ts[id][key]);
22
39
  }
23
40
  function contractParam(bytes32 key) external view returns (bytes32, bool, bool) {
24
- return (cv[key], false, cs[key]);
41
+ return (cv[key], cd[key].length > 0, cs[key]);
42
+ }
43
+ function tokenParamData(uint256 id, bytes32 key) external view returns (bytes memory) {
44
+ return td[id][key];
45
+ }
46
+ function contractParamData(bytes32 key) external view returns (bytes memory) {
47
+ return cd[key];
25
48
  }
49
+ function tokenParamKeys(uint256) external pure returns (bytes32[] memory) { return new bytes32[](0); }
50
+ function tokenParamKeysPaged(uint256, uint256, uint256)
51
+ external pure returns (bytes32[] memory keys, uint256 total)
52
+ { return (new bytes32[](0), 0); }
53
+ function contractParamKeys() external pure returns (bytes32[] memory) { return new bytes32[](0); }
54
+ function contractParamKeysPaged(uint256, uint256)
55
+ external pure returns (bytes32[] memory keys, uint256 total)
56
+ { return (new bytes32[](0), 0); }
26
57
  }
27
58
 
28
59
  contract MyRendererTest is Test {
@@ -107,4 +138,30 @@ contract MyRendererTest is Test {
107
138
  (, bytes memory data) = img.render(address(params), tokenId, IMAGE);
108
139
  assertTrue(data.length > 0);
109
140
  }
141
+
142
+ /// READING A PAYLOAD PARAM (`Bytes`/`String`) — the pattern for carrying an actual work
143
+ /// payload on-chain. The scalar reader hands you a keccak COMMITMENT with `valueIsHash == true`;
144
+ /// the content only comes from `tokenParamData`. If your renderer reads a `Bytes` param through
145
+ /// `tokenParam` it will draw from a hash and produce garbage, silently — hence this test.
146
+ function test_bytesParam_readViaDataReader() public {
147
+ bytes memory grid = hex"00112233445566778899aabbccddeeff";
148
+ params.setTokenData(0, "grid", grid);
149
+
150
+ // The scalar surface: a commitment, explicitly flagged as one — NOT the content.
151
+ (bytes32 value, bool valueIsHash, bool isSet) = IAbxParams(address(params)).tokenParam(0, "grid");
152
+ assertTrue(isSet);
153
+ assertTrue(valueIsHash, "a Bytes param reports valueIsHash: read the data instead");
154
+ assertEq(value, keccak256(grid));
155
+
156
+ // The data surface: the real bytes, verifiable against that commitment.
157
+ bytes memory got = IAbxParams(address(params)).tokenParamData(0, "grid");
158
+ assertEq(got, grid);
159
+ assertEq(keccak256(got), value, "content must match the on-chain commitment");
160
+ }
161
+
162
+ /// An unset payload param returns empty bytes — your "use a default" signal, never a revert.
163
+ function test_bytesParam_unsetIsEmpty() public view {
164
+ assertEq(IAbxParams(address(params)).tokenParamData(0, "grid").length, 0);
165
+ assertEq(IAbxParams(address(params)).contractParamData("grid").length, 0);
166
+ }
110
167
  }
package/dist/bin.d.ts ADDED
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * abx — the published binary's entry point, and nothing else.
4
+ *
5
+ * This file exists for one reason: **ESM links the entire static import graph before any module
6
+ * body runs.** `main.ts` reaches `@artblocks/abx-indexer` → `node:sqlite` through `output.ts` and
7
+ * `config.ts`, so on a Node that cannot load `node:sqlite` the process dies during *linking* —
8
+ * before a single line of our code executes. A guard at the top of `main.ts` could never fire. The
9
+ * only place a check can run first is a module that imports nothing statically and reaches the rest
10
+ * of the CLI through a dynamic `import()`.
11
+ *
12
+ * What that bought before this file existed: `npm i -g @artblocks/abx-cli && abx doctor` on Node
13
+ * 22.5 printed a raw `ERR_UNKNOWN_BUILTIN_MODULE: No such built-in module: node:sqlite` stack trace.
14
+ * No mention of abx, Node versions, or what to do — on the first command we tell every user to run.
15
+ *
16
+ * We probe the *capability*, not the version number. `node:sqlite` shipped in v22.5.0 behind
17
+ * `--experimental-sqlite` and lost the flag in v22.13.0 / v23.4.0, so an old Node run with
18
+ * `NODE_OPTIONS=--experimental-sqlite` genuinely works — refusing it on a version comparison would
19
+ * be a false negative on a setup the reporter proved out. `engines` states what we *support*; this
20
+ * guard blocks only what is actually broken.
21
+ *
22
+ * KEEP THIS FILE IMPORT-FREE. A single static `import` of anything in the CLI reintroduces the
23
+ * crash it exists to prevent.
24
+ */
25
+ export {};
26
+ //# sourceMappingURL=bin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;GAsBG"}
package/dist/bin.js ADDED
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * abx — the published binary's entry point, and nothing else.
4
+ *
5
+ * This file exists for one reason: **ESM links the entire static import graph before any module
6
+ * body runs.** `main.ts` reaches `@artblocks/abx-indexer` → `node:sqlite` through `output.ts` and
7
+ * `config.ts`, so on a Node that cannot load `node:sqlite` the process dies during *linking* —
8
+ * before a single line of our code executes. A guard at the top of `main.ts` could never fire. The
9
+ * only place a check can run first is a module that imports nothing statically and reaches the rest
10
+ * of the CLI through a dynamic `import()`.
11
+ *
12
+ * What that bought before this file existed: `npm i -g @artblocks/abx-cli && abx doctor` on Node
13
+ * 22.5 printed a raw `ERR_UNKNOWN_BUILTIN_MODULE: No such built-in module: node:sqlite` stack trace.
14
+ * No mention of abx, Node versions, or what to do — on the first command we tell every user to run.
15
+ *
16
+ * We probe the *capability*, not the version number. `node:sqlite` shipped in v22.5.0 behind
17
+ * `--experimental-sqlite` and lost the flag in v22.13.0 / v23.4.0, so an old Node run with
18
+ * `NODE_OPTIONS=--experimental-sqlite` genuinely works — refusing it on a version comparison would
19
+ * be a false negative on a setup the reporter proved out. `engines` states what we *support*; this
20
+ * guard blocks only what is actually broken.
21
+ *
22
+ * KEEP THIS FILE IMPORT-FREE. A single static `import` of anything in the CLI reintroduces the
23
+ * crash it exists to prevent.
24
+ */
25
+ /** Unflagged `node:sqlite`: v22.13.0 on the 22.x line, v23.4.0 on 23.x (nodejs/node#55890). */
26
+ const NODE_FLOOR = '22.13.0';
27
+ const color = !process.env.NO_COLOR && process.stderr.isTTY;
28
+ const red = (s) => (color ? `\x1b[31m${s}\x1b[0m` : s);
29
+ const dim = (s) => (color ? `\x1b[2m${s}\x1b[0m` : s);
30
+ const green = (s) => (color ? `\x1b[32m${s}\x1b[0m` : s);
31
+ /**
32
+ * True when this Node has `node:sqlite` but only behind `--experimental-sqlite` — i.e. the
33
+ * v22.5.0–v22.12.x window (and v23.0–v23.3, which is EOL and not worth naming). Only then is the
34
+ * NODE_OPTIONS escape hatch real; below v22.5.0 the module does not exist at any flag.
35
+ */
36
+ function flagWouldWork(version) {
37
+ const [major, minor] = version.split('.').map(Number);
38
+ if (major === 22)
39
+ return minor >= 5 && minor < 13;
40
+ return major === 23;
41
+ }
42
+ function nodeSqliteMessage(version) {
43
+ const escape = flagWouldWork(version)
44
+ ? `\n Need to stay on this Node? It has SQLite, just flag-gated — but abx is untested there:\n ${green(`NODE_OPTIONS=--experimental-sqlite abx ${process.argv[2] ?? 'doctor'}`)}\n`
45
+ : '';
46
+ return (`\n${red('✗')} abx needs Node's built-in SQLite (${dim('node:sqlite')}), and this Node can't load it.\n\n` +
47
+ ` you have ${red(`v${version}`)}\n` +
48
+ ` abx needs ${green(`v${NODE_FLOOR}`)} or newer\n\n` +
49
+ ` Upgrade Node, then re-run:\n` +
50
+ ` ${green('nvm install 22 && nvm use 22')} ${dim('# or: brew upgrade node')}\n` +
51
+ ` ${green('abx doctor')}\n` +
52
+ escape);
53
+ }
54
+ try {
55
+ await import('node:sqlite');
56
+ }
57
+ catch {
58
+ process.stderr.write(nodeSqliteMessage(process.versions.node));
59
+ process.exit(1);
60
+ }
61
+ await import('./main.js');
62
+ export {};
63
+ //# sourceMappingURL=bin.js.map