create-partylayer-app 0.1.1 → 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/templates.d.ts.map +1 -1
- package/dist/templates.js +5 -0
- package/dist/templates.js.map +1 -1
- package/package.json +1 -1
- 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/templates.d.ts.map
CHANGED
|
@@ -1 +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,
|
|
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"}
|
package/dist/templates.js
CHANGED
|
@@ -4,6 +4,11 @@ export const TEMPLATES = [
|
|
|
4
4
|
label: 'React + Vite',
|
|
5
5
|
hint: 'React 18, Vite, zero-config PartyLayerKit',
|
|
6
6
|
},
|
|
7
|
+
{
|
|
8
|
+
id: 'next-ssr',
|
|
9
|
+
label: 'Next.js (SSR)',
|
|
10
|
+
hint: 'App Router, server-side session via cookieStorage',
|
|
11
|
+
},
|
|
7
12
|
];
|
|
8
13
|
export function isTemplateId(value) {
|
|
9
14
|
return TEMPLATES.some((t) => t.id === value);
|
package/dist/templates.js.map
CHANGED
|
@@ -1 +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;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"}
|
|
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
|
@@ -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
|
+
}
|