create-partylayer-app 0.1.0 → 0.2.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/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +111 -0
- package/dist/index.js.map +1 -0
- package/dist/prompts.d.ts +20 -0
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +65 -0
- package/dist/prompts.js.map +1 -0
- package/dist/scaffold.d.ts +2 -0
- package/dist/scaffold.d.ts.map +1 -0
- package/dist/scaffold.js +42 -0
- package/dist/scaffold.js.map +1 -0
- package/dist/templates.d.ts +15 -0
- package/dist/templates.d.ts.map +1 -0
- package/dist/templates.js +16 -0
- package/dist/templates.js.map +1 -0
- package/package.json +2 -2
- package/templates/next-ssr/README.md +28 -0
- package/templates/next-ssr/_gitignore +10 -0
- package/templates/next-ssr/_package.json +26 -0
- package/templates/next-ssr/app/globals.css +42 -0
- package/templates/next-ssr/app/layout.tsx +18 -0
- package/templates/next-ssr/app/page.tsx +33 -0
- package/templates/next-ssr/app/providers.tsx +19 -0
- package/templates/next-ssr/components/ConnectButton.tsx +8 -0
- package/templates/next-ssr/lib/session.ts +41 -0
- package/templates/next-ssr/next.config.mjs +4 -0
- package/templates/next-ssr/tsconfig.json +23 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* create-partylayer-app — scaffold a PartyLayer dApp.
|
|
4
|
+
*
|
|
5
|
+
* npm create partylayer-app@latest
|
|
6
|
+
* pnpm create partylayer-app
|
|
7
|
+
* yarn create partylayer-app
|
|
8
|
+
*
|
|
9
|
+
* Flags (skip the matching prompt; pass all for a non-interactive run):
|
|
10
|
+
* <dir> target directory (positional)
|
|
11
|
+
* --template, -t template id (e.g. react-vite)
|
|
12
|
+
* --pm npm | pnpm | yarn
|
|
13
|
+
* --no-install skip dependency install
|
|
14
|
+
* --no-git skip git init
|
|
15
|
+
* --help, -h show this help
|
|
16
|
+
*/
|
|
17
|
+
import { spawnSync } from 'node:child_process';
|
|
18
|
+
import { existsSync, readdirSync } from 'node:fs';
|
|
19
|
+
import { dirname, join, resolve } from 'node:path';
|
|
20
|
+
import { fileURLToPath } from 'node:url';
|
|
21
|
+
import * as p from '@clack/prompts';
|
|
22
|
+
import mri from 'mri';
|
|
23
|
+
import pc from 'picocolors';
|
|
24
|
+
import { runPrompts } from './prompts.js';
|
|
25
|
+
import { scaffold } from './scaffold.js';
|
|
26
|
+
import { isTemplateId, TEMPLATES } from './templates.js';
|
|
27
|
+
const HELP = `${pc.bold('create-partylayer-app')} — scaffold a PartyLayer dApp
|
|
28
|
+
|
|
29
|
+
${pc.bold('Usage')}
|
|
30
|
+
npm create partylayer-app@latest [dir] [options]
|
|
31
|
+
|
|
32
|
+
${pc.bold('Options')}
|
|
33
|
+
-t, --template <id> ${TEMPLATES.map((t) => t.id).join(', ')}
|
|
34
|
+
--pm <manager> npm | pnpm | yarn
|
|
35
|
+
--no-install skip dependency install
|
|
36
|
+
--no-git skip git init
|
|
37
|
+
-h, --help show this help
|
|
38
|
+
`;
|
|
39
|
+
async function main() {
|
|
40
|
+
const argv = mri(process.argv.slice(2), {
|
|
41
|
+
boolean: ['install', 'git', 'help'],
|
|
42
|
+
default: { install: true, git: true, help: false },
|
|
43
|
+
alias: { t: 'template', h: 'help' },
|
|
44
|
+
});
|
|
45
|
+
if (argv.help) {
|
|
46
|
+
console.log(HELP);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
p.intro(pc.bgYellow(pc.black(' create-partylayer-app ')));
|
|
50
|
+
const flagTemplate = typeof argv.template === 'string' ? argv.template : undefined;
|
|
51
|
+
if (flagTemplate && !isTemplateId(flagTemplate)) {
|
|
52
|
+
p.cancel(`Unknown template "${flagTemplate}". Available: ${TEMPLATES.map((t) => t.id).join(', ')}`);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
const flagPm = typeof argv.pm === 'string' ? argv.pm : undefined;
|
|
56
|
+
if (flagPm && !['npm', 'pnpm', 'yarn'].includes(flagPm)) {
|
|
57
|
+
p.cancel(`Unknown package manager "${flagPm}". Use npm, pnpm, or yarn.`);
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
const opts = await runPrompts({
|
|
61
|
+
projectDir: typeof argv._[0] === 'string' ? argv._[0] : undefined,
|
|
62
|
+
template: flagTemplate,
|
|
63
|
+
pm: flagPm,
|
|
64
|
+
install: argv.install,
|
|
65
|
+
git: argv.git,
|
|
66
|
+
});
|
|
67
|
+
const targetDir = resolve(process.cwd(), opts.projectDir);
|
|
68
|
+
if (existsSync(targetDir) && readdirSync(targetDir).length > 0) {
|
|
69
|
+
p.cancel(`Target directory "${opts.projectDir}" already exists and is not empty.`);
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
const templatesRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..', 'templates');
|
|
73
|
+
const templateDir = join(templatesRoot, opts.template);
|
|
74
|
+
const s = p.spinner();
|
|
75
|
+
s.start(`Scaffolding ${pc.cyan(opts.projectName)} (${opts.template})`);
|
|
76
|
+
await scaffold(templateDir, targetDir, { PROJECT_NAME: opts.projectName });
|
|
77
|
+
s.stop(`Created ${pc.cyan(opts.projectDir)}`);
|
|
78
|
+
if (opts.git) {
|
|
79
|
+
const git = spawnSync('git', ['init', '-q'], { cwd: targetDir, stdio: 'ignore' });
|
|
80
|
+
if (git.status === 0) {
|
|
81
|
+
spawnSync('git', ['add', '-A'], { cwd: targetDir, stdio: 'ignore' });
|
|
82
|
+
spawnSync('git', ['commit', '-q', '-m', 'Initial commit from create-partylayer-app'], {
|
|
83
|
+
cwd: targetDir,
|
|
84
|
+
stdio: 'ignore',
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (opts.install) {
|
|
89
|
+
const inst = p.spinner();
|
|
90
|
+
inst.start(`Installing dependencies with ${opts.pm}`);
|
|
91
|
+
const res = spawnSync(opts.pm, ['install'], { cwd: targetDir, stdio: 'ignore' });
|
|
92
|
+
if (res.status === 0) {
|
|
93
|
+
inst.stop('Dependencies installed');
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
inst.stop(pc.yellow(`Install skipped/failed — run "${opts.pm} install" yourself.`));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const steps = [
|
|
100
|
+
`cd ${opts.projectDir}`,
|
|
101
|
+
...(opts.install ? [] : [`${opts.pm} install`]),
|
|
102
|
+
`${opts.pm} run dev`,
|
|
103
|
+
];
|
|
104
|
+
p.note(steps.join('\n'), 'Next steps');
|
|
105
|
+
p.outro(`${pc.green('Done.')} Open the app and click Connect Wallet.`);
|
|
106
|
+
}
|
|
107
|
+
main().catch((err) => {
|
|
108
|
+
p.cancel(String(err instanceof Error ? err.message : err));
|
|
109
|
+
process.exit(1);
|
|
110
|
+
});
|
|
111
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type PackageManager = 'npm' | 'pnpm' | 'yarn';
|
|
2
|
+
export interface ScaffoldOptions {
|
|
3
|
+
/** Target directory (relative to cwd), e.g. "my-app". */
|
|
4
|
+
projectDir: string;
|
|
5
|
+
/** Project name baked into package.json + app config (derived from dir). */
|
|
6
|
+
projectName: string;
|
|
7
|
+
template: string;
|
|
8
|
+
pm: PackageManager;
|
|
9
|
+
install: boolean;
|
|
10
|
+
git: boolean;
|
|
11
|
+
}
|
|
12
|
+
export interface InitialOptions {
|
|
13
|
+
projectDir?: string;
|
|
14
|
+
template?: string;
|
|
15
|
+
pm?: PackageManager;
|
|
16
|
+
install: boolean;
|
|
17
|
+
git: boolean;
|
|
18
|
+
}
|
|
19
|
+
export declare function runPrompts(initial: InitialOptions): Promise<ScaffoldOptions>;
|
|
20
|
+
//# sourceMappingURL=prompts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;AAErD,MAAM,WAAW,eAAe;IAC9B,yDAAyD;IACzD,UAAU,EAAE,MAAM,CAAC;IACnB,4EAA4E;IAC5E,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,cAAc,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,OAAO,CAAC;CACd;AAED,MAAM,WAAW,cAAc;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,EAAE,CAAC,EAAE,cAAc,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,OAAO,CAAC;CACd;AAmBD,wBAAsB,UAAU,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC,CA0ClF"}
|
package/dist/prompts.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive prompt flow (project name → template → package manager → options).
|
|
3
|
+
* Every field is prompted ONLY if it wasn't supplied via a CLI flag, so passing
|
|
4
|
+
* all flags makes the CLI fully non-interactive (safe in CI / non-TTY).
|
|
5
|
+
*/
|
|
6
|
+
import * as p from '@clack/prompts';
|
|
7
|
+
import { TEMPLATES } from './templates.js';
|
|
8
|
+
const PM_VALUES = ['npm', 'pnpm', 'yarn'];
|
|
9
|
+
/** A valid, install-safe directory/package name. */
|
|
10
|
+
function sanitizeName(input) {
|
|
11
|
+
return input
|
|
12
|
+
.trim()
|
|
13
|
+
.toLowerCase()
|
|
14
|
+
.replace(/[^a-z0-9._-]+/g, '-')
|
|
15
|
+
.replace(/^[-_.]+|[-_.]+$/g, '')
|
|
16
|
+
|| 'partylayer-app';
|
|
17
|
+
}
|
|
18
|
+
function bail() {
|
|
19
|
+
p.cancel('Scaffolding cancelled.');
|
|
20
|
+
process.exit(0);
|
|
21
|
+
}
|
|
22
|
+
export async function runPrompts(initial) {
|
|
23
|
+
let projectDir = initial.projectDir;
|
|
24
|
+
if (!projectDir) {
|
|
25
|
+
const res = await p.text({
|
|
26
|
+
message: 'Project directory?',
|
|
27
|
+
placeholder: 'my-partylayer-app',
|
|
28
|
+
defaultValue: 'my-partylayer-app',
|
|
29
|
+
});
|
|
30
|
+
if (p.isCancel(res))
|
|
31
|
+
bail();
|
|
32
|
+
projectDir = res || 'my-partylayer-app';
|
|
33
|
+
}
|
|
34
|
+
let template = initial.template;
|
|
35
|
+
if (!template) {
|
|
36
|
+
const res = await p.select({
|
|
37
|
+
message: 'Which template?',
|
|
38
|
+
options: TEMPLATES.map((t) => ({ value: t.id, label: t.label, hint: t.hint })),
|
|
39
|
+
initialValue: TEMPLATES[0].id,
|
|
40
|
+
});
|
|
41
|
+
if (p.isCancel(res))
|
|
42
|
+
bail();
|
|
43
|
+
template = res;
|
|
44
|
+
}
|
|
45
|
+
let pm = initial.pm;
|
|
46
|
+
if (!pm) {
|
|
47
|
+
const res = await p.select({
|
|
48
|
+
message: 'Package manager?',
|
|
49
|
+
options: PM_VALUES.map((v) => ({ value: v, label: v })),
|
|
50
|
+
initialValue: 'npm',
|
|
51
|
+
});
|
|
52
|
+
if (p.isCancel(res))
|
|
53
|
+
bail();
|
|
54
|
+
pm = res;
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
projectDir,
|
|
58
|
+
projectName: sanitizeName(projectDir.split('/').pop() || projectDir),
|
|
59
|
+
template,
|
|
60
|
+
pm,
|
|
61
|
+
install: initial.install,
|
|
62
|
+
git: initial.git,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=prompts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompts.js","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,CAAC,MAAM,gBAAgB,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAuB3C,MAAM,SAAS,GAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AAE5D,oDAAoD;AACpD,SAAS,YAAY,CAAC,KAAa;IACjC,OAAO,KAAK;SACT,IAAI,EAAE;SACN,WAAW,EAAE;SACb,OAAO,CAAC,gBAAgB,EAAE,GAAG,CAAC;SAC9B,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC;WAC7B,gBAAgB,CAAC;AACxB,CAAC;AAED,SAAS,IAAI;IACX,CAAC,CAAC,MAAM,CAAC,wBAAwB,CAAC,CAAC;IACnC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAAuB;IACtD,IAAI,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IACpC,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC;YACvB,OAAO,EAAE,oBAAoB;YAC7B,WAAW,EAAE,mBAAmB;YAChC,YAAY,EAAE,mBAAmB;SAClC,CAAC,CAAC;QACH,IAAI,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,IAAI,EAAE,CAAC;QAC5B,UAAU,GAAI,GAAc,IAAI,mBAAmB,CAAC;IACtD,CAAC;IAED,IAAI,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IAChC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,MAAM,CAAC;YACzB,OAAO,EAAE,iBAAiB;YAC1B,OAAO,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;YAC9E,YAAY,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE;SAC9B,CAAC,CAAC;QACH,IAAI,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,IAAI,EAAE,CAAC;QAC5B,QAAQ,GAAG,GAAa,CAAC;IAC3B,CAAC;IAED,IAAI,EAAE,GAAG,OAAO,CAAC,EAAE,CAAC;IACpB,IAAI,CAAC,EAAE,EAAE,CAAC;QACR,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,MAAM,CAAC;YACzB,OAAO,EAAE,kBAAkB;YAC3B,OAAO,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;YACvD,YAAY,EAAE,KAAuB;SACtC,CAAC,CAAC;QACH,IAAI,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,IAAI,EAAE,CAAC;QAC5B,EAAE,GAAG,GAAqB,CAAC;IAC7B,CAAC;IAED,OAAO;QACL,UAAU;QACV,WAAW,EAAE,YAAY,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,UAAU,CAAC;QACpE,QAAQ;QACR,EAAE;QACF,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,GAAG,EAAE,OAAO,CAAC,GAAG;KACjB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"AA2BA,wBAAsB,QAAQ,CAC5B,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC3B,OAAO,CAAC,IAAI,CAAC,CAEf"}
|
package/dist/scaffold.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copy a template directory into the target, applying:
|
|
3
|
+
* - file renames: `_package.json` → `package.json`, and any other `_x` → `.x`
|
|
4
|
+
* (so a published `.gitignore` isn't rewritten to `.npmignore` by npm, and
|
|
5
|
+
* `templates/<id>/_package.json` is NOT picked up by the monorepo's
|
|
6
|
+
* `packages/*` workspace glob);
|
|
7
|
+
* - placeholder substitution: `{{VAR}}` → vars[VAR].
|
|
8
|
+
*/
|
|
9
|
+
import { mkdir, readdir, readFile, writeFile } from 'node:fs/promises';
|
|
10
|
+
import { join } from 'node:path';
|
|
11
|
+
const RENAME = {
|
|
12
|
+
'_package.json': 'package.json',
|
|
13
|
+
};
|
|
14
|
+
function targetName(name) {
|
|
15
|
+
if (RENAME[name])
|
|
16
|
+
return RENAME[name];
|
|
17
|
+
if (name.startsWith('_'))
|
|
18
|
+
return '.' + name.slice(1);
|
|
19
|
+
return name;
|
|
20
|
+
}
|
|
21
|
+
function substitute(content, vars) {
|
|
22
|
+
return content.replace(/\{\{(\w+)\}\}/g, (_match, key) => Object.prototype.hasOwnProperty.call(vars, key) ? vars[key] : `{{${key}}}`);
|
|
23
|
+
}
|
|
24
|
+
export async function scaffold(templateDir, targetDir, vars) {
|
|
25
|
+
await copyDir(templateDir, targetDir, vars);
|
|
26
|
+
}
|
|
27
|
+
async function copyDir(src, dest, vars) {
|
|
28
|
+
await mkdir(dest, { recursive: true });
|
|
29
|
+
const entries = await readdir(src, { withFileTypes: true });
|
|
30
|
+
for (const entry of entries) {
|
|
31
|
+
const from = join(src, entry.name);
|
|
32
|
+
const to = join(dest, targetName(entry.name));
|
|
33
|
+
if (entry.isDirectory()) {
|
|
34
|
+
await copyDir(from, to, vars);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
const content = await readFile(from, 'utf8');
|
|
38
|
+
await writeFile(to, substitute(content, vars));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=scaffold.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACvE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,MAAM,GAA2B;IACrC,eAAe,EAAE,cAAc;CAChC,CAAC;AAEF,SAAS,UAAU,CAAC,IAAY;IAC9B,IAAI,MAAM,CAAC,IAAI,CAAC;QAAE,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC;IACtC,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACrD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,UAAU,CAAC,OAAe,EAAE,IAA4B;IAC/D,OAAO,OAAO,CAAC,OAAO,CAAC,gBAAgB,EAAE,CAAC,MAAM,EAAE,GAAW,EAAE,EAAE,CAC/D,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,CAC3E,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,WAAmB,EACnB,SAAiB,EACjB,IAA4B;IAE5B,MAAM,OAAO,CAAC,WAAW,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;AAC9C,CAAC;AAED,KAAK,UAAU,OAAO,CACpB,GAAW,EACX,IAAY,EACZ,IAA4B;IAE5B,MAAM,KAAK,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACvC,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5D,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACnC,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QAC9C,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACxB,MAAM,OAAO,CAAC,IAAI,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC;QAChC,CAAC;aAAM,CAAC;YACN,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YAC7C,MAAM,SAAS,CAAC,EAAE,EAAE,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;QACjD,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template registry. Each entry maps to a directory under `templates/<id>/`.
|
|
3
|
+
* New templates (next-ssr, vue-nuxt-pinia, vanilla) are added here as they land.
|
|
4
|
+
*/
|
|
5
|
+
export interface TemplateMeta {
|
|
6
|
+
/** Directory name under templates/ and the --template flag value. */
|
|
7
|
+
id: string;
|
|
8
|
+
/** Human label shown in the picker. */
|
|
9
|
+
label: string;
|
|
10
|
+
/** One-line hint shown next to the label. */
|
|
11
|
+
hint: string;
|
|
12
|
+
}
|
|
13
|
+
export declare const TEMPLATES: TemplateMeta[];
|
|
14
|
+
export declare function isTemplateId(value: string): boolean;
|
|
15
|
+
//# sourceMappingURL=templates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,qEAAqE;IACrE,EAAE,EAAE,MAAM,CAAC;IACX,uCAAuC;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC;CACd;AAED,eAAO,MAAM,SAAS,EAAE,YAAY,EAWnC,CAAC;AAEF,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEnD"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export const TEMPLATES = [
|
|
2
|
+
{
|
|
3
|
+
id: 'react-vite',
|
|
4
|
+
label: 'React + Vite',
|
|
5
|
+
hint: 'React 18, Vite, zero-config PartyLayerKit',
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
id: 'next-ssr',
|
|
9
|
+
label: 'Next.js (SSR)',
|
|
10
|
+
hint: 'App Router, server-side session via cookieStorage',
|
|
11
|
+
},
|
|
12
|
+
];
|
|
13
|
+
export function isTemplateId(value) {
|
|
14
|
+
return TEMPLATES.some((t) => t.id === value);
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=templates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"templates.js","sourceRoot":"","sources":["../src/templates.ts"],"names":[],"mappings":"AAaA,MAAM,CAAC,MAAM,SAAS,GAAmB;IACvC;QACE,EAAE,EAAE,YAAY;QAChB,KAAK,EAAE,cAAc;QACrB,IAAI,EAAE,2CAA2C;KAClD;IACD;QACE,EAAE,EAAE,UAAU;QACd,KAAK,EAAE,eAAe;QACtB,IAAI,EAAE,mDAAmD;KAC1D;CACF,CAAC;AAEF,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,CAAC;AAC/C,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-partylayer-app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Scaffold a PartyLayer dApp (Canton wallet integration) in seconds.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -45,6 +45,6 @@
|
|
|
45
45
|
],
|
|
46
46
|
"scripts": {
|
|
47
47
|
"build": "tsc",
|
|
48
|
-
"clean": "rm -rf dist"
|
|
48
|
+
"clean": "rm -rf dist *.tsbuildinfo"
|
|
49
49
|
}
|
|
50
50
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# {{PROJECT_NAME}}
|
|
2
|
+
|
|
3
|
+
A [PartyLayer](https://partylayer.xyz) dApp — Next.js App Router with **SSR session**.
|
|
4
|
+
|
|
5
|
+
## Develop
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm run dev
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## How SSR session works
|
|
12
|
+
|
|
13
|
+
- **Server** ([lib/session.ts](lib/session.ts)) reads the session cookie via `next/headers` `cookies()` + `@partylayer/session`'s `createCookieStorage` → the connected party is rendered in the **initial HTML** (no disconnected→connected flash). `app/page.tsx` is a Server Component that gates on `getServerSession()`.
|
|
14
|
+
- **Client** ([app/providers.tsx](app/providers.tsx)) wraps the app in `PartyLayerKit` with `sessionOptions={{ storage: createCookieStorage() }}` — `document.cookie` (synchronous), the **same** cookie the server reads.
|
|
15
|
+
- `next/headers` is imported only in your app (`lib/session.ts`), never in `@partylayer/session` — the library stays framework-agnostic.
|
|
16
|
+
|
|
17
|
+
Switch networks by changing `network` in `app/providers.tsx` and `appName` accordingly.
|
|
18
|
+
|
|
19
|
+
## Build
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm run build && npm start
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Docs
|
|
26
|
+
|
|
27
|
+
- [PartyLayer docs](https://partylayer.xyz/docs/introduction)
|
|
28
|
+
- [@partylayer/session — createCookieStorage](https://www.npmjs.com/package/@partylayer/session)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{PROJECT_NAME}}",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "next dev",
|
|
8
|
+
"build": "next build",
|
|
9
|
+
"start": "next start",
|
|
10
|
+
"lint": "next lint"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@partylayer/react": "^0.9.1",
|
|
14
|
+
"@partylayer/sdk": "^0.10.0",
|
|
15
|
+
"@partylayer/session": "^1.1.0",
|
|
16
|
+
"next": "^14.2.5",
|
|
17
|
+
"react": "^18.3.1",
|
|
18
|
+
"react-dom": "^18.3.1"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/node": "^20.11.0",
|
|
22
|
+
"@types/react": "^18.3.3",
|
|
23
|
+
"@types/react-dom": "^18.3.0",
|
|
24
|
+
"typescript": "^5.6.2"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
color-scheme: light dark;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
* {
|
|
6
|
+
box-sizing: border-box;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
body {
|
|
10
|
+
margin: 0;
|
|
11
|
+
font-family: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
|
|
12
|
+
background: #0b0f1a;
|
|
13
|
+
color: rgba(255, 255, 255, 0.92);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.app {
|
|
17
|
+
min-height: 100vh;
|
|
18
|
+
display: flex;
|
|
19
|
+
flex-direction: column;
|
|
20
|
+
align-items: center;
|
|
21
|
+
justify-content: center;
|
|
22
|
+
gap: 16px;
|
|
23
|
+
padding: 24px;
|
|
24
|
+
text-align: center;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.app h1 {
|
|
28
|
+
font-size: 2.5rem;
|
|
29
|
+
margin: 0;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.subtitle {
|
|
33
|
+
margin: 0 0 8px;
|
|
34
|
+
color: rgba(255, 255, 255, 0.6);
|
|
35
|
+
max-width: 32rem;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.session code {
|
|
39
|
+
background: rgba(255, 255, 255, 0.08);
|
|
40
|
+
padding: 2px 8px;
|
|
41
|
+
border-radius: 6px;
|
|
42
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import { Providers } from './providers';
|
|
3
|
+
import './globals.css';
|
|
4
|
+
|
|
5
|
+
export const metadata = {
|
|
6
|
+
title: '{{PROJECT_NAME}}',
|
|
7
|
+
description: 'A PartyLayer dApp on Canton (Next.js App Router + SSR session).',
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export default function RootLayout({ children }: { children: ReactNode }) {
|
|
11
|
+
return (
|
|
12
|
+
<html lang="en">
|
|
13
|
+
<body>
|
|
14
|
+
<Providers>{children}</Providers>
|
|
15
|
+
</body>
|
|
16
|
+
</html>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { getServerSession, truncateParty } from '@/lib/session';
|
|
2
|
+
import { ConnectButton } from '@/components/ConnectButton';
|
|
3
|
+
|
|
4
|
+
// Reading cookies() opts this route into dynamic (per-request) rendering — which
|
|
5
|
+
// is exactly what SSR session needs: the server reads the session cookie on
|
|
6
|
+
// every request and renders the connected state into the initial HTML.
|
|
7
|
+
export default function Home() {
|
|
8
|
+
const session = getServerSession();
|
|
9
|
+
const party = session?.account?.partyId ?? null;
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<main className="app">
|
|
13
|
+
<h1>{{PROJECT_NAME}}</h1>
|
|
14
|
+
<p className="subtitle">A PartyLayer dApp on Canton — connect any verified wallet.</p>
|
|
15
|
+
|
|
16
|
+
{/* SERVER-RENDERED connected state (Option A: server owns the display). This
|
|
17
|
+
appears in the initial HTML from the cookie, before any client JS, so
|
|
18
|
+
there is no disconnected→connected flash. */}
|
|
19
|
+
{party ? (
|
|
20
|
+
<p className="session">
|
|
21
|
+
Connected as <code data-testid="server-party">{truncateParty(party)}</code>
|
|
22
|
+
</p>
|
|
23
|
+
) : (
|
|
24
|
+
<p className="session" data-testid="server-cta">
|
|
25
|
+
Not connected — connect a wallet to continue.
|
|
26
|
+
</p>
|
|
27
|
+
)}
|
|
28
|
+
|
|
29
|
+
{/* Interactive client island — reconciles silently with the live provider. */}
|
|
30
|
+
<ConnectButton />
|
|
31
|
+
</main>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useMemo, type ReactNode } from 'react';
|
|
4
|
+
import { PartyLayerKit } from '@partylayer/react';
|
|
5
|
+
import { createCookieStorage } from '@partylayer/session';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Client provider boundary. cookieStorage on the client uses document.cookie
|
|
9
|
+
* (synchronous → flash-free), the SAME cookie the server reads in lib/session.ts
|
|
10
|
+
* — so server HTML and client hydration agree.
|
|
11
|
+
*/
|
|
12
|
+
export function Providers({ children }: { children: ReactNode }) {
|
|
13
|
+
const storage = useMemo(() => createCookieStorage(), []);
|
|
14
|
+
return (
|
|
15
|
+
<PartyLayerKit network="devnet" appName="{{PROJECT_NAME}}" sessionOptions={{ storage }}>
|
|
16
|
+
{children}
|
|
17
|
+
</PartyLayerKit>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { cookies } from 'next/headers';
|
|
2
|
+
import {
|
|
3
|
+
createCookieStorage,
|
|
4
|
+
decodeSessionEnvelope,
|
|
5
|
+
type CookieAdapter,
|
|
6
|
+
type PersistedSessionSnapshot,
|
|
7
|
+
} from '@partylayer/session';
|
|
8
|
+
|
|
9
|
+
// next/headers is imported HERE (the app), never inside @partylayer/session —
|
|
10
|
+
// the library stays framework-agnostic. This adapter wraps Next's read-only
|
|
11
|
+
// request cookies into the CookieAdapter shape createCookieStorage expects.
|
|
12
|
+
function nextCookieAdapter(): CookieAdapter {
|
|
13
|
+
const jar = cookies();
|
|
14
|
+
return {
|
|
15
|
+
get: (name) => jar.get(name)?.value ?? null,
|
|
16
|
+
// Server Components can only READ cookies; writes happen client-side
|
|
17
|
+
// (document.cookie) or in a Route Handler / Server Action.
|
|
18
|
+
set: () => {},
|
|
19
|
+
remove: () => {},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Read the persisted session ON THE SERVER (for SSR). Returns the decoded
|
|
25
|
+
* snapshot (party, network, …) or null. This is what lets a Server Component
|
|
26
|
+
* render the connected state in the initial HTML — before any client JS.
|
|
27
|
+
*/
|
|
28
|
+
export function getServerSession(): PersistedSessionSnapshot | null {
|
|
29
|
+
const storage = createCookieStorage({ adapter: nextCookieAdapter() });
|
|
30
|
+
// Cookie reads are synchronous; the SessionStorage interface types getItem as
|
|
31
|
+
// string | null | Promise, so narrow to a string (null/absent → no session).
|
|
32
|
+
const raw = storage.getItem('pl_session');
|
|
33
|
+
if (typeof raw !== 'string') return null;
|
|
34
|
+
return decodeSessionEnvelope(raw);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Pure, server-safe party-id truncation (avoids importing a client module). */
|
|
38
|
+
export function truncateParty(partyId: string): string {
|
|
39
|
+
if (partyId.length <= 16) return partyId;
|
|
40
|
+
return `${partyId.slice(0, 10)}…${partyId.slice(-4)}`;
|
|
41
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2017",
|
|
4
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
|
5
|
+
"allowJs": true,
|
|
6
|
+
"skipLibCheck": true,
|
|
7
|
+
"strict": true,
|
|
8
|
+
"noEmit": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"module": "esnext",
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"isolatedModules": true,
|
|
14
|
+
"jsx": "preserve",
|
|
15
|
+
"incremental": true,
|
|
16
|
+
"plugins": [{ "name": "next" }],
|
|
17
|
+
"paths": {
|
|
18
|
+
"@/*": ["./*"]
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
22
|
+
"exclude": ["node_modules"]
|
|
23
|
+
}
|