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: PLACEHOLDER_STORE_ID, resource: `images/${file}` });
126
- const meta = capsuleResourceUris({ storeId: PLACEHOLDER_STORE_ID, resource: `metadata/${metaName}` });
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: PLACEHOLDER_STORE_ID, resource: `licenses/${file}` });
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.2.4",
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
- # Mint the NFTs on-chain from collection.json + the generated items.json (wallet-signed):
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: PLACEHOLDER_STORE_ID, resource: `licenses/${file}` });
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: PLACEHOLDER_STORE_ID, resource: `images/${file}` });
382
- const meta = capsuleResourceUris({ storeId: PLACEHOLDER_STORE_ID, resource: `metadata/${metaName}` });
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 r = generateMetadata(ROOT);
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
- console.log("Next: `npm run validate`, then mint with `digstore collection mint --collection collection.json --manifest items.json`.");
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);