@joinbankroll/create-app 0.1.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/README.md +35 -0
- package/dist/index.js +136 -0
- package/package.json +41 -0
package/README.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# @joinbankroll/create-app
|
|
2
|
+
|
|
3
|
+
Scaffold a [Built for Bankroll](https://joinbankroll.com/build) app.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm create @joinbankroll/app@latest my-app
|
|
7
|
+
cd my-app
|
|
8
|
+
npm run bankroll
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
`npm run bankroll` raises a public tunnel and prints a QR code. Scan it and your
|
|
12
|
+
app opens inside Bankroll on your phone, which is the only place it runs — the
|
|
13
|
+
host holds the user's money, identity, and location, and refuses any origin that
|
|
14
|
+
is not public HTTPS.
|
|
15
|
+
|
|
16
|
+
## What it does
|
|
17
|
+
|
|
18
|
+
Downloads [bankroll-starter](https://github.com/inplayinnovation/bankroll-starter),
|
|
19
|
+
writes `.env.local`, installs dependencies, and makes the first commit.
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
-t, --template <ref> template branch or tag (default: main)
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## What you get
|
|
26
|
+
|
|
27
|
+
An app with its own git history and no upstream remote, the same as
|
|
28
|
+
`create-next-app`. There is nothing to merge back later: everything that is not
|
|
29
|
+
your app comes from `@joinbankroll/sdk` and `bankroll-cli`, and updates with
|
|
30
|
+
`npm update`.
|
|
31
|
+
|
|
32
|
+
`.env.local` holds configuration only — `STORE=fs` and your app's name. The
|
|
33
|
+
signing key belongs to `bankroll dev`, which keeps it in
|
|
34
|
+
`~/.config/bankroll/keypair.json` and injects it into the dev server's
|
|
35
|
+
environment, so no secret is ever written into your project.
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { spawnSync } from "child_process";
|
|
5
|
+
import { existsSync, mkdirSync, readdirSync, rmSync, writeFileSync } from "fs";
|
|
6
|
+
import { basename, resolve } from "path";
|
|
7
|
+
import { createInterface } from "readline/promises";
|
|
8
|
+
var TEMPLATE_REPO = "inplayinnovation/bankroll-starter";
|
|
9
|
+
var DEFAULT_REF = "main";
|
|
10
|
+
var DOWNLOAD_TIMEOUT_MS = 6e4;
|
|
11
|
+
var ENV_FILE = ".env.local";
|
|
12
|
+
var IGNORABLE = /* @__PURE__ */ new Set([".DS_Store", ".git", ".gitkeep", ".idea", ".vscode", "Thumbs.db"]);
|
|
13
|
+
function parse(argv) {
|
|
14
|
+
const args = { ref: DEFAULT_REF, help: false };
|
|
15
|
+
for (let i = 0; i < argv.length; i++) {
|
|
16
|
+
const arg = argv[i];
|
|
17
|
+
if (arg === "--help" || arg === "-h") args.help = true;
|
|
18
|
+
else if (arg === "--template" || arg === "-t") args.ref = argv[++i] ?? DEFAULT_REF;
|
|
19
|
+
else if (arg?.startsWith("-")) throw new Error(`Unknown option ${arg}`);
|
|
20
|
+
else if (arg && !args.directory) args.directory = arg;
|
|
21
|
+
}
|
|
22
|
+
return args;
|
|
23
|
+
}
|
|
24
|
+
function usage() {
|
|
25
|
+
console.log(`
|
|
26
|
+
Create a Built for Bankroll app.
|
|
27
|
+
|
|
28
|
+
npm create @joinbankroll/app@latest my-app
|
|
29
|
+
|
|
30
|
+
Options
|
|
31
|
+
-t, --template <ref> template branch or tag (default: ${DEFAULT_REF})
|
|
32
|
+
-h, --help show this
|
|
33
|
+
`);
|
|
34
|
+
}
|
|
35
|
+
async function ask(question, fallback) {
|
|
36
|
+
if (!process.stdin.isTTY) return fallback;
|
|
37
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
38
|
+
try {
|
|
39
|
+
return (await rl.question(` ${question} (${fallback}): `)).trim() || fallback;
|
|
40
|
+
} finally {
|
|
41
|
+
rl.close();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function run(command, args, cwd) {
|
|
45
|
+
const result = spawnSync(command, args, { cwd, stdio: "inherit" });
|
|
46
|
+
if (result.error) throw result.error;
|
|
47
|
+
if (result.status !== 0) throw new Error(`${command} ${args.join(" ")} failed`);
|
|
48
|
+
}
|
|
49
|
+
function tryRun(command, args, cwd) {
|
|
50
|
+
const result = spawnSync(command, args, { cwd, stdio: "ignore" });
|
|
51
|
+
return !result.error && result.status === 0;
|
|
52
|
+
}
|
|
53
|
+
async function download(ref, into) {
|
|
54
|
+
const url = `https://codeload.github.com/${TEMPLATE_REPO}/tar.gz/${ref}`;
|
|
55
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS) });
|
|
56
|
+
if (!response.ok) {
|
|
57
|
+
throw new Error(
|
|
58
|
+
response.status === 404 ? `No template named "${ref}" \u2014 check --template.` : `Could not download the template (${response.status}).`
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
const archive = resolve(into, ".template.tar.gz");
|
|
62
|
+
writeFileSync(archive, Buffer.from(await response.arrayBuffer()));
|
|
63
|
+
try {
|
|
64
|
+
run("tar", ["-xzf", archive, "--strip-components=1"], into);
|
|
65
|
+
} finally {
|
|
66
|
+
rmSync(archive, { force: true });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async function main() {
|
|
70
|
+
const args = parse(process.argv.slice(2));
|
|
71
|
+
if (args.help) return usage();
|
|
72
|
+
const directory = args.directory ?? await ask("Directory", "my-bankroll-app");
|
|
73
|
+
const target = resolve(process.cwd(), directory);
|
|
74
|
+
let created = false;
|
|
75
|
+
if (existsSync(target)) {
|
|
76
|
+
const existing = readdirSync(target).filter((entry) => !IGNORABLE.has(entry));
|
|
77
|
+
if (existing.length > 0) throw new Error(`${directory} is not empty.`);
|
|
78
|
+
} else {
|
|
79
|
+
mkdirSync(target, { recursive: true });
|
|
80
|
+
created = true;
|
|
81
|
+
}
|
|
82
|
+
const name = await ask("App name", basename(target));
|
|
83
|
+
console.log(`
|
|
84
|
+
Downloading the template\u2026`);
|
|
85
|
+
try {
|
|
86
|
+
await download(args.ref, target);
|
|
87
|
+
} catch (error) {
|
|
88
|
+
if (created && readdirSync(target).length === 0) rmSync(target, { recursive: true, force: true });
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
writeFileSync(
|
|
92
|
+
resolve(target, ENV_FILE),
|
|
93
|
+
[
|
|
94
|
+
"# Written by create-bankroll-app. Gitignored, so none of it reaches a",
|
|
95
|
+
"# deployment.",
|
|
96
|
+
"",
|
|
97
|
+
"# Store state in local files rather than Vercel Blob, so this app runs",
|
|
98
|
+
"# without a Vercel account. A deployment has no STORE and uses Blob.",
|
|
99
|
+
"STORE=fs",
|
|
100
|
+
"",
|
|
101
|
+
"# Shown when someone connects the app.",
|
|
102
|
+
`BANKROLL_APP_NAME=${name}`,
|
|
103
|
+
""
|
|
104
|
+
].join("\n")
|
|
105
|
+
);
|
|
106
|
+
console.log(` Installing dependencies\u2026
|
|
107
|
+
`);
|
|
108
|
+
run("npm", ["install"], target);
|
|
109
|
+
if (tryRun("git", ["init", "-q"], target)) {
|
|
110
|
+
tryRun("git", ["add", "-A"], target);
|
|
111
|
+
tryRun("git", ["commit", "-qm", "Initial commit from create-bankroll-app"], target);
|
|
112
|
+
}
|
|
113
|
+
console.log(`
|
|
114
|
+
${name} is ready.
|
|
115
|
+
|
|
116
|
+
cd ${directory}
|
|
117
|
+
npm run bankroll tunnel + QR \u2014 scan it to open the app inside Bankroll
|
|
118
|
+
|
|
119
|
+
Your app runs inside Bankroll, so a phone is how you see it. Everything that
|
|
120
|
+
is not your app comes from @joinbankroll/sdk and updates with npm update.
|
|
121
|
+
`);
|
|
122
|
+
}
|
|
123
|
+
if (process.argv[1] && import.meta.url.endsWith(basename(process.argv[1]))) {
|
|
124
|
+
try {
|
|
125
|
+
await main();
|
|
126
|
+
} catch (error) {
|
|
127
|
+
console.error(`
|
|
128
|
+
${error instanceof Error ? error.message : String(error)}
|
|
129
|
+
`);
|
|
130
|
+
process.exit(1);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
export {
|
|
134
|
+
IGNORABLE,
|
|
135
|
+
parse
|
|
136
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@joinbankroll/create-app",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Create a Built for Bankroll app",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/inplayinnovation/create-bankroll-app.git"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://joinbankroll.com/build",
|
|
11
|
+
"type": "module",
|
|
12
|
+
"bin": {
|
|
13
|
+
"create-bankroll-app": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"engines": {
|
|
19
|
+
"node": ">=20.6"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"bankroll",
|
|
23
|
+
"solana",
|
|
24
|
+
"create"
|
|
25
|
+
],
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/node": "^24.0.0",
|
|
28
|
+
"tsup": "^8.0.0",
|
|
29
|
+
"typescript": "^5.9.3",
|
|
30
|
+
"vitest": "^3.0.0"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "tsup",
|
|
34
|
+
"prepack": "npm run build",
|
|
35
|
+
"test": "vitest run",
|
|
36
|
+
"typecheck": "tsc --noEmit"
|
|
37
|
+
},
|
|
38
|
+
"publishConfig": {
|
|
39
|
+
"access": "public"
|
|
40
|
+
}
|
|
41
|
+
}
|