SOURCE / PINNED RELEASE
Made of little things.
Powder Tool V600Billion
- Release
- 142767edcab8…
- Author-recorded commit
- 6d92971effd0…
- License
- LICENSE
- Author’s source reference
- nostr://npub1fllw8kw0thjj55wds0uugcnp5kej2nfxd36eruq39d56wwz8r44q5q78wj/wss%3A%2F%2Fgit.napplet.soy%2F/powder-toy
Archive hash verified: ed7d6a8ea7083197…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
/**
* Put the built napplet where the Hangar serves it, in a nappelin.com checkout.
*
* npm run build
* npm run dock -- [path to nappelin.com] (default: ../nappelin.com, or NAPPELIN_DIR)
*
* Copies dist/index.html and dist/.nip5a-manifest.json to
* apps/hangar/public/napplets/powder-toy/ there, with the GPL-3.0 as
* LICENSE.txt (the Hangar's card links it), and writes provenance.json next
* to them: this repository and commit, what went in from upstream, and that the
* napplet declares no roles or intents. Refuses a working tree with changes, so
* the commit in provenance.json is the source of the bytes (--allow-dirty for a
* local try-out marks the commit "-dirty"). Pinning is a separate, reviewed step
* in nappelin.com:
* cd apps/hangar && node scripts/pin-napplet.mjs powder-toy
*/
import { execFileSync } from 'node:child_process';
import { createHash } from 'node:crypto';
import { copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
import { join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { PUBLIC_SOURCE } from './public-source.mjs';
import { upstream } from './upstream.mjs';
const SOURCE = 'https://github.com/BIMbeamFLX/powder-toy-napplet';
const repo = fileURLToPath(new URL('../', import.meta.url));
const dist = join(repo, 'dist');
const args = process.argv.slice(2);
const allowDirty = args.includes('--allow-dirty');
const nappelin = resolve(args.find((arg) => !arg.startsWith('--')) ?? process.env.NAPPELIN_DIR ?? join(repo, '..', 'nappelin.com'));
const hangar = join(nappelin, 'apps', 'hangar');
const dock = join(hangar, 'public', 'napplets', 'powder-toy');
const sha256 = (bytes) => createHash('sha256').update(bytes).digest('hex');
const git = (...command) => execFileSync('git', command, { cwd: repo, encoding: 'utf8' }).trim();
if (!existsSync(join(hangar, 'src', 'catalog.ts'))) {
throw new Error(`${nappelin} is not a nappelin.com checkout (no apps/hangar/src/catalog.ts). Pass its path: npm run dock -- <path>`);
}
const dirty = git('status', '--porcelain') !== '';
if (dirty && !allowDirty) throw new Error('the working tree has changes; commit them first, so provenance.json names the source (or --allow-dirty)');
const commit = git('rev-parse', 'HEAD') + (dirty ? '-dirty' : '');
if (!existsSync(dist)) throw new Error('no dist/ yet: npm run wasm && npm run build');
const files = readdirSync(dist).sort();
const expected = ['.nip5a-manifest.json', 'index.html'];
if (JSON.stringify(files) !== JSON.stringify(expected)) {
throw new Error(`dist/ should hold exactly ${expected.join(' and ')}, found: ${files.join(', ')}`);
}
const html = readFileSync(join(dist, 'index.html'));
if (html.length > 10 * 1024 * 1024) throw new Error(`index.html is ${html.length} bytes, over the 10 MB napplet limit`);
if (/<script\b[^>]*\bsrc=/i.test(html.toString('utf8'))) throw new Error('index.html still references an external script');
const manifest = JSON.parse(readFileSync(join(dist, '.nip5a-manifest.json'), 'utf8'));
const pathTag = manifest.tags.find((tag) => tag[0] === 'path' && tag[1] === '/index.html');
if (!pathTag || pathTag[2] !== sha256(html)) throw new Error('the manifest does not pin dist/index.html');
mkdirSync(dock, { recursive: true });
for (const file of expected) copyFileSync(join(dist, file), join(dock, file));
copyFileSync(join(repo, 'LICENSE'), join(dock, 'LICENSE.txt'));
writeFileSync(join(dock, 'provenance.json'), JSON.stringify({
sha256: sha256(html),
source: SOURCE,
publicSource: PUBLIC_SOURCE,
sourceCommit: commit,
build: 'npm ci && npm run wasm && npm run build',
upstream: {
source: upstream.source,
tag: upstream.tag,
commit: upstream.commit,
emsdk: upstream.build.emsdk,
meson: upstream.build.meson,
patches: upstream.build.patches,
files: upstream.build.files,
},
adaptation: 'Upstream source built for the web without threads (scripts/build-wasm.sh, patches/), inlined into one file '
+ 'with a boot script: /powder on napplet storage instead of IndexedDB, the online features answered over Nostr.',
license: upstream.license,
archetypes: [],
intents: [],
}, null, 2) + '\n');
console.log(`docked ${html.length} bytes (${(html.length / 1048576).toFixed(2)} MiB), sha256 ${sha256(html)}, commit ${commit}`);
console.log(` -> ${dock}`);
console.log('pin it there: cd apps/hangar && node scripts/pin-napplet.mjs powder-toy');
