create-dig-app 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/nft-cli.js
CHANGED
|
@@ -89,10 +89,18 @@ function resolveItems(root, imageFiles) {
|
|
|
89
89
|
* metadata_hash from the canonical metadata JSON; if a license has been generated, wires its URI +
|
|
90
90
|
* hash in too.
|
|
91
91
|
*
|
|
92
|
+
* The store id defaults to {@link PLACEHOLDER_STORE_ID} for the initial content-prep pass (before
|
|
93
|
+
* the capsule is published). After `digstore deploy` returns the real store id, re-run with
|
|
94
|
+
* `{ storeId }` (CLI `--store-id <id>`) to bake the REAL id into every generated URI so the minted
|
|
95
|
+
* NFT points at the published capsule — otherwise the on-chain `data_uris`/`metadata_uris`/
|
|
96
|
+
* `license_uris` keep the literal placeholder permanently (there is no separate substitution step).
|
|
97
|
+
*
|
|
92
98
|
* @param {string} root Project directory.
|
|
99
|
+
* @param {{storeId?:string}} [opts] `storeId` — the real published store id; omit for the placeholder.
|
|
93
100
|
* @returns {{count:number, manifestPath:string, metadataDir:string}}
|
|
94
101
|
*/
|
|
95
|
-
export function generateMetadata(root) {
|
|
102
|
+
export function generateMetadata(root, opts = {}) {
|
|
103
|
+
const storeId = opts.storeId ?? PLACEHOLDER_STORE_ID;
|
|
96
104
|
const collection = loadCollection(root);
|
|
97
105
|
const imageFiles = listImages(root);
|
|
98
106
|
const items = resolveItems(root, imageFiles);
|
|
@@ -101,7 +109,7 @@ export function generateMetadata(root) {
|
|
|
101
109
|
mkdirSync(metadataDir, { recursive: true });
|
|
102
110
|
|
|
103
111
|
// If a license file already exists in licenses/, fold its URI + hash into each item's media.
|
|
104
|
-
const license = readGeneratedLicense(root, collection);
|
|
112
|
+
const license = readGeneratedLicense(root, collection, storeId);
|
|
105
113
|
|
|
106
114
|
const mds = generateItemMetadata(collection, items);
|
|
107
115
|
const manifest = [];
|
|
@@ -122,8 +130,8 @@ export function generateMetadata(root) {
|
|
|
122
130
|
writeFileSync(join(metadataDir, metaName), metaJson + "\n");
|
|
123
131
|
const metadataHash = metadataHashHex(md);
|
|
124
132
|
|
|
125
|
-
const data = capsuleResourceUris({ storeId
|
|
126
|
-
const meta = capsuleResourceUris({ storeId
|
|
133
|
+
const data = capsuleResourceUris({ storeId, resource: `images/${file}` });
|
|
134
|
+
const meta = capsuleResourceUris({ storeId, resource: `metadata/${metaName}` });
|
|
127
135
|
|
|
128
136
|
const media = {
|
|
129
137
|
data_uris: data.uris,
|
|
@@ -148,14 +156,14 @@ export function generateMetadata(root) {
|
|
|
148
156
|
}
|
|
149
157
|
|
|
150
158
|
/** Read an already-generated license file (if any) and return its uris + hash, else null. */
|
|
151
|
-
function readGeneratedLicense(root, collection) {
|
|
159
|
+
function readGeneratedLicense(root, collection, storeId = PLACEHOLDER_STORE_ID) {
|
|
152
160
|
const id = collection?.license;
|
|
153
161
|
if (!id || !LICENSES[id]) return null;
|
|
154
162
|
const file = licenseFileName(id);
|
|
155
163
|
const path = join(root, "licenses", file);
|
|
156
164
|
if (!existsSync(path)) return null;
|
|
157
165
|
const bytes = readFileSync(path);
|
|
158
|
-
const { uris } = capsuleResourceUris({ storeId
|
|
166
|
+
const { uris } = capsuleResourceUris({ storeId, resource: `licenses/${file}` });
|
|
159
167
|
return { id, file, hash: sha256Hex(bytes), uris };
|
|
160
168
|
}
|
|
161
169
|
|
|
@@ -237,7 +245,7 @@ export function validateProject(root) {
|
|
|
237
245
|
throw new Error(`item "${item.name}": metadata_hash does not match any metadata/*.json`);
|
|
238
246
|
}
|
|
239
247
|
}
|
|
240
|
-
// data_hash agrees with the real image bytes (resolve the image path from the
|
|
248
|
+
// data_hash agrees with the real image bytes (resolve the image path from the URN's resource tail).
|
|
241
249
|
const imgFile = resourceTail(media.data_uris?.[0]);
|
|
242
250
|
if (imgFile && media.data_hash != null) {
|
|
243
251
|
const imgPath = join(root, imgFile);
|
|
@@ -257,9 +265,14 @@ export function validateProject(root) {
|
|
|
257
265
|
return { ok: true, checked };
|
|
258
266
|
}
|
|
259
267
|
|
|
260
|
-
/**
|
|
268
|
+
/**
|
|
269
|
+
* Extract the resource path from a capsule URI — the canonical bare URN
|
|
270
|
+
* `urn:dig:chia:<storeId>:<root>/<resource>` or the `https://<host>/<resource>` gateway fallback.
|
|
271
|
+
* Strip an optional `scheme://authority` prefix, then the resource is everything after the first `/`.
|
|
272
|
+
*/
|
|
261
273
|
function resourceTail(uri) {
|
|
262
274
|
if (!uri || typeof uri !== "string") return null;
|
|
263
|
-
const
|
|
264
|
-
|
|
275
|
+
const afterAuthority = uri.includes("://") ? uri.slice(uri.indexOf("://") + 3) : uri;
|
|
276
|
+
const i = afterAuthority.indexOf("/");
|
|
277
|
+
return i >= 0 ? afterAuthority.slice(i + 1) : null;
|
|
265
278
|
}
|
package/lib/nft-metadata.js
CHANGED
|
@@ -364,14 +364,18 @@ export function itemsFromImages(fileNames) {
|
|
|
364
364
|
});
|
|
365
365
|
}
|
|
366
366
|
|
|
367
|
-
// ── Capsule resource URIs (
|
|
367
|
+
// ── Capsule resource URIs (canonical URN first, https gateway fallback) ─────────────────────────────
|
|
368
368
|
|
|
369
369
|
/** The HTTPS gateway host for decrypted capsule resources (mirrors the hub's CAPSULE_HTTPS_GATEWAY). */
|
|
370
370
|
export const CAPSULE_HTTPS_GATEWAY = "usercontent.dig.net";
|
|
371
371
|
|
|
372
372
|
/**
|
|
373
|
-
* Build the CHIP-0007 media URIs for a resource inside a capsule: the canonical
|
|
373
|
+
* Build the CHIP-0007 media URIs for a resource inside a capsule: the canonical bare URN first,
|
|
374
374
|
* then the https gateway fallback (the order the hub + digstore use).
|
|
375
|
+
*
|
|
376
|
+
* The URN is the bare, root-pinned `urn:dig:chia:<storeId>:<root>/<resource>` form — NEVER a
|
|
377
|
+
* `dig://`-prefixed URN (the `dig://urn:dig:chia:…` double-scheme is the #686 bug). `dig://` is not
|
|
378
|
+
* a content/resource scheme; the dig-urn-resolver consumes the bare URN and decides where to fetch.
|
|
375
379
|
* @param {{storeId:string,root?:string,resource:string}} args
|
|
376
380
|
* @returns {{urn:string,https:string,uris:string[]}}
|
|
377
381
|
*/
|
|
@@ -379,7 +383,7 @@ export function capsuleResourceUris({ storeId, root, resource }) {
|
|
|
379
383
|
const sid = String(storeId ?? "").trim();
|
|
380
384
|
const r = String(root ?? "").trim();
|
|
381
385
|
const res = String(resource ?? "").trim().replace(/^\/+/, "");
|
|
382
|
-
const urn = `
|
|
386
|
+
const urn = `urn:dig:chia:${sid}${r ? `:${r}` : ""}/${res}`;
|
|
383
387
|
const https = `https://${sid}.${CAPSULE_HTTPS_GATEWAY}/${res}`;
|
|
384
388
|
return { urn, https, uris: [urn, https] };
|
|
385
389
|
}
|
package/lib/templates.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// `__PLACEHOLDER__` token substitution (see substitute.js). The metadata here drives:
|
|
5
5
|
// - the interactive picker + `--template` validation (templateNames / resolveTemplate),
|
|
6
6
|
// - the dig.toml the scaffolder writes (outputDir + buildCommand, which the SDK adapter and
|
|
7
|
-
// `digstore deploy` both read — see modules/dig-sdk/src/adapters/dig-toml.ts),
|
|
7
|
+
// `digstore deploy` both read — see modules/dx/dig-sdk/src/adapters/dig-toml.ts),
|
|
8
8
|
// - which templates wire @dignetwork/dig-sdk (wallet) vs stay dependency-light static sites.
|
|
9
9
|
//
|
|
10
10
|
// Templates are kept MINIMAL but real: each one `npm install`s and builds, and none bundles a heavy
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-dig-app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Scaffold a wallet-wired, deployable DIG Network app — free, no mint. The JS front door for building dapps, frontends, and NFT collections on Chia.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "DIG Network",
|
|
@@ -86,10 +86,17 @@ Everything shared by all items lives here:
|
|
|
86
86
|
## Mint it
|
|
87
87
|
|
|
88
88
|
```sh
|
|
89
|
-
# Publish the art + metadata + licenses as ONE capsule (this is the only deploy that spends $DIG)
|
|
89
|
+
# Publish the art + metadata + licenses as ONE capsule (this is the only deploy that spends $DIG).
|
|
90
|
+
# The deploy prints your capsule's real store id — copy it for the next step:
|
|
90
91
|
digstore deploy
|
|
91
92
|
|
|
92
|
-
#
|
|
93
|
+
# Bake the REAL store id into every metadata/item URI. The first `npm run generate` used a
|
|
94
|
+
# placeholder (STORE_ID_AFTER_PUBLISH) because the store id isn't known until you deploy. Skip this
|
|
95
|
+
# and the minted NFT points at the placeholder FOREVER — there is no later substitution step:
|
|
96
|
+
npm run generate:metadata -- --store-id <store-id-from-deploy>
|
|
97
|
+
npm run validate
|
|
98
|
+
|
|
99
|
+
# Mint the NFTs on-chain from collection.json + the finalized items.json (wallet-signed):
|
|
93
100
|
digstore collection create --name "__DISPLAY_NAME__" # one-time: register the collection on-chain
|
|
94
101
|
digstore collection mint --collection collection.json --manifest items.json --did <your-did>
|
|
95
102
|
```
|
|
@@ -215,7 +215,7 @@ function capsuleResourceUris({ storeId, root, resource }) {
|
|
|
215
215
|
const sid = String(storeId ?? "").trim();
|
|
216
216
|
const r = String(root ?? "").trim();
|
|
217
217
|
const res = String(resource ?? "").trim().replace(/^\/+/, "");
|
|
218
|
-
const urn = `
|
|
218
|
+
const urn = `urn:dig:chia:${sid}${r ? `:${r}` : ""}/${res}`;
|
|
219
219
|
const https = `https://${sid}.${CAPSULE_HTTPS_GATEWAY}/${res}`;
|
|
220
220
|
return { urn, https, uris: [urn, https] };
|
|
221
221
|
}
|
|
@@ -346,24 +346,25 @@ function resolveItems(root, imageFiles) {
|
|
|
346
346
|
return items.map((item, i) => ({ ...item, file: item.file || imageFiles[i] }));
|
|
347
347
|
}
|
|
348
348
|
|
|
349
|
-
function readGeneratedLicense(root, collection) {
|
|
349
|
+
function readGeneratedLicense(root, collection, storeId = PLACEHOLDER_STORE_ID) {
|
|
350
350
|
const id = collection?.license;
|
|
351
351
|
if (!id || !LICENSES[id]) return null;
|
|
352
352
|
const file = licenseFileName(id);
|
|
353
353
|
const path = join(root, "licenses", file);
|
|
354
354
|
if (!existsSync(path)) return null;
|
|
355
355
|
const bytes = readFileSync(path);
|
|
356
|
-
const { uris } = capsuleResourceUris({ storeId
|
|
356
|
+
const { uris } = capsuleResourceUris({ storeId, resource: `licenses/${file}` });
|
|
357
357
|
return { id, file, hash: sha256Hex(bytes), uris };
|
|
358
358
|
}
|
|
359
359
|
|
|
360
|
-
function generateMetadata(root) {
|
|
360
|
+
function generateMetadata(root, opts = {}) {
|
|
361
|
+
const storeId = opts.storeId ?? PLACEHOLDER_STORE_ID;
|
|
361
362
|
const collection = loadCollection(root);
|
|
362
363
|
const imageFiles = listImages(root);
|
|
363
364
|
const items = resolveItems(root, imageFiles);
|
|
364
365
|
const metadataDir = join(root, "metadata");
|
|
365
366
|
mkdirSync(metadataDir, { recursive: true });
|
|
366
|
-
const license = readGeneratedLicense(root, collection);
|
|
367
|
+
const license = readGeneratedLicense(root, collection, storeId);
|
|
367
368
|
const mds = generateItemMetadata(collection, items);
|
|
368
369
|
const manifest = [];
|
|
369
370
|
for (let i = 0; i < items.length; i++) {
|
|
@@ -378,8 +379,8 @@ function generateMetadata(root) {
|
|
|
378
379
|
const metaName = `${stem}.json`;
|
|
379
380
|
writeFileSync(join(metadataDir, metaName), canonicalJson(md) + "\n");
|
|
380
381
|
const metadataHash = metadataHashHex(md);
|
|
381
|
-
const data = capsuleResourceUris({ storeId
|
|
382
|
-
const meta = capsuleResourceUris({ storeId
|
|
382
|
+
const data = capsuleResourceUris({ storeId, resource: `images/${file}` });
|
|
383
|
+
const meta = capsuleResourceUris({ storeId, resource: `metadata/${metaName}` });
|
|
383
384
|
manifest.push({
|
|
384
385
|
name: md.name,
|
|
385
386
|
description: item.description || undefined,
|
|
@@ -461,13 +462,28 @@ function validateProject(root) {
|
|
|
461
462
|
|
|
462
463
|
// ── CLI dispatch ──────────────────────────────────────────────────────────────────────────────────
|
|
463
464
|
|
|
465
|
+
/** Read a `--flag value` / `--flag=value` option from an argv slice; undefined if absent. */
|
|
466
|
+
function flagValue(args, name) {
|
|
467
|
+
for (let i = 0; i < args.length; i++) {
|
|
468
|
+
if (args[i] === name) return args[i + 1];
|
|
469
|
+
if (args[i].startsWith(`${name}=`)) return args[i].slice(name.length + 1);
|
|
470
|
+
}
|
|
471
|
+
return undefined;
|
|
472
|
+
}
|
|
473
|
+
|
|
464
474
|
function main() {
|
|
465
475
|
const cmd = process.argv[2];
|
|
476
|
+
const args = process.argv.slice(3);
|
|
466
477
|
try {
|
|
467
478
|
if (cmd === "metadata") {
|
|
468
|
-
const
|
|
479
|
+
const storeId = flagValue(args, "--store-id");
|
|
480
|
+
const r = generateMetadata(ROOT, storeId ? { storeId } : {});
|
|
469
481
|
console.log(`Generated ${r.count} CHIP-0007 metadata file(s) → metadata/ and the items.json manifest.`);
|
|
470
|
-
|
|
482
|
+
if (storeId) {
|
|
483
|
+
console.log(`Baked store id ${storeId} into every URI. Next: \`npm run validate\`, then \`digstore collection mint --collection collection.json --manifest items.json\`.`);
|
|
484
|
+
} else {
|
|
485
|
+
console.log("Used the placeholder store id. After `digstore deploy` gives you the real store id, re-run `npm run generate:metadata -- --store-id <id>` to bake it in — otherwise the minted NFT keeps the placeholder. Then `npm run validate` and `digstore collection mint`.");
|
|
486
|
+
}
|
|
471
487
|
} else if (cmd === "license") {
|
|
472
488
|
const r = generateLicense(ROOT);
|
|
473
489
|
console.log(`Wrote licenses/${r.file} (sha256 ${r.hash}).`);
|
|
@@ -476,8 +492,9 @@ function main() {
|
|
|
476
492
|
const r = validateProject(ROOT);
|
|
477
493
|
console.log(`OK — ${r.checked} item(s) valid: CHIP-0007 schema + data/metadata/license hashes agree with the real bytes.`);
|
|
478
494
|
} else {
|
|
479
|
-
console.error("Usage: node scripts/dig-nft.mjs <metadata|license|validate>");
|
|
495
|
+
console.error("Usage: node scripts/dig-nft.mjs <metadata|license|validate> [--store-id <id>]");
|
|
480
496
|
console.error(" metadata generate CHIP-0007 metadata/*.json + items.json from images/ + collection.json");
|
|
497
|
+
console.error(" --store-id <id> bake the REAL published store id into URIs (after `digstore deploy`)");
|
|
481
498
|
console.error(" license write the license chosen in collection.json into licenses/");
|
|
482
499
|
console.error(" validate re-verify schema + URI/hash agreement before minting");
|
|
483
500
|
process.exit(2);
|