create-dig-app 0.2.4 → 0.3.1
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,21 @@ 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
|
+
// An empty / whitespace-only storeId falls back to the placeholder — never emit an empty-store-id
|
|
104
|
+
// URI (`urn:dig:chia:/…`). The CLI (dig-nft.mjs) rejects an explicitly-empty `--store-id` louder;
|
|
105
|
+
// here we coerce defensively so a programmatic caller can't produce a broken URN. (#1065)
|
|
106
|
+
const storeId = opts.storeId && String(opts.storeId).trim() ? opts.storeId : PLACEHOLDER_STORE_ID;
|
|
96
107
|
const collection = loadCollection(root);
|
|
97
108
|
const imageFiles = listImages(root);
|
|
98
109
|
const items = resolveItems(root, imageFiles);
|
|
@@ -101,7 +112,7 @@ export function generateMetadata(root) {
|
|
|
101
112
|
mkdirSync(metadataDir, { recursive: true });
|
|
102
113
|
|
|
103
114
|
// If a license file already exists in licenses/, fold its URI + hash into each item's media.
|
|
104
|
-
const license = readGeneratedLicense(root, collection);
|
|
115
|
+
const license = readGeneratedLicense(root, collection, storeId);
|
|
105
116
|
|
|
106
117
|
const mds = generateItemMetadata(collection, items);
|
|
107
118
|
const manifest = [];
|
|
@@ -122,8 +133,8 @@ export function generateMetadata(root) {
|
|
|
122
133
|
writeFileSync(join(metadataDir, metaName), metaJson + "\n");
|
|
123
134
|
const metadataHash = metadataHashHex(md);
|
|
124
135
|
|
|
125
|
-
const data = capsuleResourceUris({ storeId
|
|
126
|
-
const meta = capsuleResourceUris({ storeId
|
|
136
|
+
const data = capsuleResourceUris({ storeId, resource: `images/${file}` });
|
|
137
|
+
const meta = capsuleResourceUris({ storeId, resource: `metadata/${metaName}` });
|
|
127
138
|
|
|
128
139
|
const media = {
|
|
129
140
|
data_uris: data.uris,
|
|
@@ -148,14 +159,14 @@ export function generateMetadata(root) {
|
|
|
148
159
|
}
|
|
149
160
|
|
|
150
161
|
/** Read an already-generated license file (if any) and return its uris + hash, else null. */
|
|
151
|
-
function readGeneratedLicense(root, collection) {
|
|
162
|
+
function readGeneratedLicense(root, collection, storeId = PLACEHOLDER_STORE_ID) {
|
|
152
163
|
const id = collection?.license;
|
|
153
164
|
if (!id || !LICENSES[id]) return null;
|
|
154
165
|
const file = licenseFileName(id);
|
|
155
166
|
const path = join(root, "licenses", file);
|
|
156
167
|
if (!existsSync(path)) return null;
|
|
157
168
|
const bytes = readFileSync(path);
|
|
158
|
-
const { uris } = capsuleResourceUris({ storeId
|
|
169
|
+
const { uris } = capsuleResourceUris({ storeId, resource: `licenses/${file}` });
|
|
159
170
|
return { id, file, hash: sha256Hex(bytes), uris };
|
|
160
171
|
}
|
|
161
172
|
|
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.1",
|
|
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
|
```
|
|
@@ -346,24 +346,26 @@ 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
|
+
// Empty/whitespace storeId falls back to the placeholder — never an empty-store-id URI. (#1065)
|
|
362
|
+
const storeId = opts.storeId && String(opts.storeId).trim() ? opts.storeId : PLACEHOLDER_STORE_ID;
|
|
361
363
|
const collection = loadCollection(root);
|
|
362
364
|
const imageFiles = listImages(root);
|
|
363
365
|
const items = resolveItems(root, imageFiles);
|
|
364
366
|
const metadataDir = join(root, "metadata");
|
|
365
367
|
mkdirSync(metadataDir, { recursive: true });
|
|
366
|
-
const license = readGeneratedLicense(root, collection);
|
|
368
|
+
const license = readGeneratedLicense(root, collection, storeId);
|
|
367
369
|
const mds = generateItemMetadata(collection, items);
|
|
368
370
|
const manifest = [];
|
|
369
371
|
for (let i = 0; i < items.length; i++) {
|
|
@@ -378,8 +380,8 @@ function generateMetadata(root) {
|
|
|
378
380
|
const metaName = `${stem}.json`;
|
|
379
381
|
writeFileSync(join(metadataDir, metaName), canonicalJson(md) + "\n");
|
|
380
382
|
const metadataHash = metadataHashHex(md);
|
|
381
|
-
const data = capsuleResourceUris({ storeId
|
|
382
|
-
const meta = capsuleResourceUris({ storeId
|
|
383
|
+
const data = capsuleResourceUris({ storeId, resource: `images/${file}` });
|
|
384
|
+
const meta = capsuleResourceUris({ storeId, resource: `metadata/${metaName}` });
|
|
383
385
|
manifest.push({
|
|
384
386
|
name: md.name,
|
|
385
387
|
description: item.description || undefined,
|
|
@@ -461,13 +463,40 @@ function validateProject(root) {
|
|
|
461
463
|
|
|
462
464
|
// ── CLI dispatch ──────────────────────────────────────────────────────────────────────────────────
|
|
463
465
|
|
|
466
|
+
/** Read a `--flag value` / `--flag=value` option from an argv slice; undefined if absent. */
|
|
467
|
+
function flagValue(args, name) {
|
|
468
|
+
for (let i = 0; i < args.length; i++) {
|
|
469
|
+
if (args[i] === name) return args[i + 1];
|
|
470
|
+
if (args[i].startsWith(`${name}=`)) return args[i].slice(name.length + 1);
|
|
471
|
+
}
|
|
472
|
+
return undefined;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/** True if `name` appears in the argv slice as a flag (with or without a value). */
|
|
476
|
+
function flagPresent(args, name) {
|
|
477
|
+
return args.some((a) => a === name || a.startsWith(`${name}=`));
|
|
478
|
+
}
|
|
479
|
+
|
|
464
480
|
function main() {
|
|
465
481
|
const cmd = process.argv[2];
|
|
482
|
+
const args = process.argv.slice(3);
|
|
466
483
|
try {
|
|
467
484
|
if (cmd === "metadata") {
|
|
468
|
-
const
|
|
485
|
+
const storeId = flagValue(args, "--store-id");
|
|
486
|
+
if (flagPresent(args, "--store-id") && !(storeId && storeId.trim())) {
|
|
487
|
+
console.error(
|
|
488
|
+
"Error: --store-id was given but empty. Omit it for the pre-publish placeholder pass, " +
|
|
489
|
+
"or pass the real store id from `digstore deploy` (e.g. --store-id <id>).",
|
|
490
|
+
);
|
|
491
|
+
process.exit(2);
|
|
492
|
+
}
|
|
493
|
+
const r = generateMetadata(ROOT, storeId ? { storeId } : {});
|
|
469
494
|
console.log(`Generated ${r.count} CHIP-0007 metadata file(s) → metadata/ and the items.json manifest.`);
|
|
470
|
-
|
|
495
|
+
if (storeId) {
|
|
496
|
+
console.log(`Baked store id ${storeId} into every URI. Next: \`npm run validate\`, then \`digstore collection mint --collection collection.json --manifest items.json\`.`);
|
|
497
|
+
} else {
|
|
498
|
+
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`.");
|
|
499
|
+
}
|
|
471
500
|
} else if (cmd === "license") {
|
|
472
501
|
const r = generateLicense(ROOT);
|
|
473
502
|
console.log(`Wrote licenses/${r.file} (sha256 ${r.hash}).`);
|
|
@@ -476,8 +505,9 @@ function main() {
|
|
|
476
505
|
const r = validateProject(ROOT);
|
|
477
506
|
console.log(`OK — ${r.checked} item(s) valid: CHIP-0007 schema + data/metadata/license hashes agree with the real bytes.`);
|
|
478
507
|
} else {
|
|
479
|
-
console.error("Usage: node scripts/dig-nft.mjs <metadata|license|validate>");
|
|
508
|
+
console.error("Usage: node scripts/dig-nft.mjs <metadata|license|validate> [--store-id <id>]");
|
|
480
509
|
console.error(" metadata generate CHIP-0007 metadata/*.json + items.json from images/ + collection.json");
|
|
510
|
+
console.error(" --store-id <id> bake the REAL published store id into URIs (after `digstore deploy`)");
|
|
481
511
|
console.error(" license write the license chosen in collection.json into licenses/");
|
|
482
512
|
console.error(" validate re-verify schema + URI/hash agreement before minting");
|
|
483
513
|
process.exit(2);
|