create-partylayer-app 0.3.0 → 0.4.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.
@@ -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,EAgBnC,CAAC;AAEF,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEnD"}
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,EAqBnC,CAAC;AAEF,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEnD"}
package/dist/templates.js CHANGED
@@ -14,6 +14,11 @@ export const TEMPLATES = [
14
14
  label: 'Vue + Nuxt + Pinia',
15
15
  hint: 'Nuxt 3 SSR session, PartyLayer session in a Pinia store',
16
16
  },
17
+ {
18
+ id: 'vanilla',
19
+ label: 'Vanilla TypeScript',
20
+ hint: 'No framework — Vite + the @partylayer/sdk client API',
21
+ },
17
22
  ];
18
23
  export function isTemplateId(value) {
19
24
  return TEMPLATES.some((t) => t.id === value);
@@ -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;IACD;QACE,EAAE,EAAE,UAAU;QACd,KAAK,EAAE,eAAe;QACtB,IAAI,EAAE,mDAAmD;KAC1D;IACD;QACE,EAAE,EAAE,gBAAgB;QACpB,KAAK,EAAE,oBAAoB;QAC3B,IAAI,EAAE,yDAAyD;KAChE;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;IACD;QACE,EAAE,EAAE,gBAAgB;QACpB,KAAK,EAAE,oBAAoB;QAC3B,IAAI,EAAE,yDAAyD;KAChE;IACD;QACE,EAAE,EAAE,SAAS;QACb,KAAK,EAAE,oBAAoB;QAC3B,IAAI,EAAE,sDAAsD;KAC7D;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.0",
3
+ "version": "0.4.1",
4
4
  "description": "Scaffold a PartyLayer dApp (Canton wallet integration) in seconds.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -10,9 +10,10 @@
10
10
  "lint": "next lint"
11
11
  },
12
12
  "dependencies": {
13
- "@partylayer/react": "^0.9.1",
14
- "@partylayer/sdk": "^0.10.0",
15
- "@partylayer/session": "^1.1.0",
13
+ "@partylayer/react": "^2.1.0",
14
+ "@partylayer/sdk": "^0.15.0",
15
+ "@partylayer/session": "^1.1.4",
16
+ "@tanstack/react-query": "^5.0.0",
16
17
  "next": "^14.2.5",
17
18
  "react": "^18.3.1",
18
19
  "react-dom": "^18.3.1"
@@ -1,7 +1,7 @@
1
1
  import { getServerSession, truncateParty } from '@/lib/session';
2
2
  import { ConnectButton } from '@/components/ConnectButton';
3
3
 
4
- // Reading cookies() opts this route into dynamic (per-request) rendering — which
4
+ // Reading cookies() opts this route into dynamic (per-request) rendering, which
5
5
  // is exactly what SSR session needs: the server reads the session cookie on
6
6
  // every request and renders the connected state into the initial HTML.
7
7
  export default function Home() {
@@ -11,7 +11,7 @@ export default function Home() {
11
11
  return (
12
12
  <main className="app">
13
13
  <h1>{{PROJECT_NAME}}</h1>
14
- <p className="subtitle">A PartyLayer dApp on Canton — connect any verified wallet.</p>
14
+ <p className="subtitle">A PartyLayer dApp on Canton: connect any verified wallet.</p>
15
15
 
16
16
  {/* SERVER-RENDERED connected state (Option A: server owns the display). This
17
17
  appears in the initial HTML from the cookie, before any client JS, so
@@ -22,11 +22,11 @@ export default function Home() {
22
22
  </p>
23
23
  ) : (
24
24
  <p className="session" data-testid="server-cta">
25
- Not connected — connect a wallet to continue.
25
+ Not connected: connect a wallet to continue.
26
26
  </p>
27
27
  )}
28
28
 
29
- {/* Interactive client island — reconciles silently with the live provider. */}
29
+ {/* Interactive client island: reconciles silently with the live provider. */}
30
30
  <ConnectButton />
31
31
  </main>
32
32
  );
@@ -1,19 +1,29 @@
1
1
  'use client';
2
2
 
3
- import { useMemo, type ReactNode } from 'react';
3
+ import { useMemo, useState, type ReactNode } from 'react';
4
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
4
5
  import { PartyLayerKit } from '@partylayer/react';
5
6
  import { createCookieStorage } from '@partylayer/session';
6
7
 
7
8
  /**
8
9
  * 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.
10
+ * (synchronous, flash-free), the SAME cookie the server reads in lib/session.ts,
11
+ * so server HTML and client hydration agree.
12
+ *
13
+ * The QueryClient is created with useState so each browser client gets one stable
14
+ * instance (never shared across server requests), the SSR-safe pattern for the
15
+ * App Router. PartyLayer's data hooks (@partylayer/react/query) read this client;
16
+ * the base session surface works without it, but it is set up so the query hooks
17
+ * are ready when you reach for them.
11
18
  */
12
19
  export function Providers({ children }: { children: ReactNode }) {
13
20
  const storage = useMemo(() => createCookieStorage(), []);
21
+ const [queryClient] = useState(() => new QueryClient());
14
22
  return (
15
- <PartyLayerKit network="devnet" appName="{{PROJECT_NAME}}" sessionOptions={{ storage }}>
16
- {children}
17
- </PartyLayerKit>
23
+ <QueryClientProvider client={queryClient}>
24
+ <PartyLayerKit network="devnet" appName="{{PROJECT_NAME}}" sessionOptions={{ storage }}>
25
+ {children}
26
+ </PartyLayerKit>
27
+ </QueryClientProvider>
18
28
  );
19
29
  }
@@ -9,8 +9,9 @@
9
9
  "preview": "vite preview"
10
10
  },
11
11
  "dependencies": {
12
- "@partylayer/react": "^0.9.1",
13
- "@partylayer/sdk": "^0.10.0",
12
+ "@partylayer/react": "^2.1.0",
13
+ "@partylayer/sdk": "^0.15.0",
14
+ "@tanstack/react-query": "^5.0.0",
14
15
  "react": "^18.3.1",
15
16
  "react-dom": "^18.3.1"
16
17
  },
@@ -12,7 +12,7 @@ export default function App() {
12
12
  <PartyLayerKit network="devnet" appName="{{PROJECT_NAME}}">
13
13
  <main className="app">
14
14
  <h1>{{PROJECT_NAME}}</h1>
15
- <p className="subtitle">A PartyLayer dApp on Canton — connect any verified wallet.</p>
15
+ <p className="subtitle">A PartyLayer dApp on Canton: connect any verified wallet.</p>
16
16
  <ConnectButton />
17
17
  <SessionPanel />
18
18
  </main>
@@ -1,10 +1,20 @@
1
1
  import { StrictMode } from 'react';
2
2
  import { createRoot } from 'react-dom/client';
3
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
3
4
  import App from './App';
4
5
  import './index.css';
5
6
 
7
+ // PartyLayer's data hooks (the `@partylayer/react/query` entrypoint:
8
+ // useDamlContract, useChoice, the cost hooks) are built on TanStack Query, so
9
+ // they need a QueryClient in context. Create one here and wrap the app. The base
10
+ // session surface (PartyLayerKit, ConnectButton, useAccount) works without it,
11
+ // but having it set up means the query hooks are ready when you reach for them.
12
+ const queryClient = new QueryClient();
13
+
6
14
  createRoot(document.getElementById('root')!).render(
7
15
  <StrictMode>
8
- <App />
16
+ <QueryClientProvider client={queryClient}>
17
+ <App />
18
+ </QueryClientProvider>
9
19
  </StrictMode>,
10
20
  );
@@ -0,0 +1,36 @@
1
+ # {{PROJECT_NAME}}
2
+
3
+ A [PartyLayer](https://partylayer.xyz) dApp — vanilla TypeScript + Vite, no framework.
4
+
5
+ ## Develop
6
+
7
+ ```bash
8
+ npm run dev
9
+ ```
10
+
11
+ ## How it's wired
12
+
13
+ ```ts
14
+ import { createPartyLayer } from '@partylayer/sdk';
15
+
16
+ const client = createPartyLayer({ network: 'devnet', app: { name: '{{PROJECT_NAME}}' } });
17
+
18
+ const wallets = await client.listWallets(); // verified wallets from the registry
19
+ const session = await client.connect({ walletId }); // connect to one
20
+ client.on('session:connected', () => { /* … */ }); // react to session changes
21
+ client.on('session:disconnected', () => { /* … */ });
22
+ const active = await client.getActiveSession(); // restore on load
23
+ ```
24
+
25
+ `createPartyLayer` is the dApp connect API — no framework bindings needed. See `src/main.ts` for the full hand-rolled connect UI. Switch networks by changing `network` to `"testnet"` or `"mainnet"`.
26
+
27
+ ## Build
28
+
29
+ ```bash
30
+ npm run build && npm run preview
31
+ ```
32
+
33
+ ## Docs
34
+
35
+ - [PartyLayer docs](https://partylayer.xyz/docs/introduction)
36
+ - [@partylayer/sdk](https://www.npmjs.com/package/@partylayer/sdk)
@@ -0,0 +1,7 @@
1
+ node_modules
2
+ dist
3
+ *.local
4
+ .DS_Store
5
+ .env
6
+ .env.*
7
+ !.env.example
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "{{PROJECT_NAME}}",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "tsc && vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "dependencies": {
12
+ "@partylayer/sdk": "^0.15.0"
13
+ },
14
+ "devDependencies": {
15
+ "typescript": "^5.6.2",
16
+ "vite": "^5.4.2"
17
+ }
18
+ }
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>{{PROJECT_NAME}}</title>
7
+ </head>
8
+ <body>
9
+ <div id="app"></div>
10
+ <script type="module" src="/src/main.ts"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,77 @@
1
+ import { createPartyLayer, type Session } from '@partylayer/sdk';
2
+ import './style.css';
3
+
4
+ // The PartyLayer client — the dApp connect API (listWallets / connect / session).
5
+ // All built-in wallet adapters are auto-registered.
6
+ const client = createPartyLayer({ network: 'devnet', app: { name: '{{PROJECT_NAME}}' } });
7
+
8
+ const app = document.querySelector<HTMLDivElement>('#app')!;
9
+ app.innerHTML = `
10
+ <main class="app">
11
+ <h1>{{PROJECT_NAME}}</h1>
12
+ <p class="subtitle">A PartyLayer dApp on Canton — vanilla TypeScript, no framework.</p>
13
+ <div id="status" class="status"></div>
14
+ <button id="connect" class="btn">Connect Wallet</button>
15
+ <ul id="wallets" class="wallets" hidden></ul>
16
+ </main>
17
+ `;
18
+
19
+ const statusEl = document.querySelector<HTMLDivElement>('#status')!;
20
+ const connectBtn = document.querySelector<HTMLButtonElement>('#connect')!;
21
+ const walletsEl = document.querySelector<HTMLUListElement>('#wallets')!;
22
+
23
+ const truncateParty = (p: string) => (p.length <= 16 ? p : `${p.slice(0, 10)}…${p.slice(-4)}`);
24
+
25
+ function renderConnected(session: Session) {
26
+ walletsEl.hidden = true;
27
+ connectBtn.hidden = true;
28
+ statusEl.innerHTML = `Connected as <code>${truncateParty(session.partyId)}</code>`;
29
+ const disconnect = document.createElement('button');
30
+ disconnect.className = 'btn';
31
+ disconnect.textContent = 'Disconnect';
32
+ disconnect.addEventListener('click', () => void client.disconnect());
33
+ statusEl.appendChild(disconnect);
34
+ }
35
+
36
+ function renderDisconnected() {
37
+ statusEl.textContent = 'Not connected — connect a wallet to continue.';
38
+ connectBtn.hidden = false;
39
+ walletsEl.hidden = true;
40
+ }
41
+
42
+ // Connect → list the verified wallets from the registry → pick one → connect.
43
+ connectBtn.addEventListener('click', async () => {
44
+ const wallets = await client.listWallets();
45
+ walletsEl.innerHTML = wallets
46
+ .map(
47
+ (w, i) => `<li>
48
+ <button class="wallet" data-idx="${i}">
49
+ ${w.icons?.md ? `<img src="${w.icons.md}" alt="" width="24" height="24" />` : ''}
50
+ <span>${w.name}</span>
51
+ </button>
52
+ </li>`,
53
+ )
54
+ .join('');
55
+ walletsEl.hidden = false;
56
+ walletsEl.querySelectorAll<HTMLButtonElement>('.wallet').forEach((btn) => {
57
+ btn.addEventListener('click', async () => {
58
+ // Pass the WalletInfo's walletId (a branded WalletId), not a raw string.
59
+ const wallet = wallets[Number(btn.dataset.idx)];
60
+ try {
61
+ const session = await client.connect({ walletId: wallet.walletId });
62
+ renderConnected(session);
63
+ } catch (err) {
64
+ statusEl.textContent = `Connect failed: ${(err as Error).message}`;
65
+ }
66
+ });
67
+ });
68
+ });
69
+
70
+ // Keep the UI in sync with external session changes (multi-tab, expiry, restore).
71
+ client.on('session:connected', () => {
72
+ void client.getActiveSession().then((s) => s && renderConnected(s));
73
+ });
74
+ client.on('session:disconnected', () => renderDisconnected());
75
+
76
+ // Initial state — restore an existing session if there is one.
77
+ void client.getActiveSession().then((s) => (s ? renderConnected(s) : renderDisconnected()));
@@ -0,0 +1,45 @@
1
+ :root {
2
+ font-family: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
3
+ color-scheme: light dark;
4
+ background: #0b0f1a;
5
+ color: rgba(255, 255, 255, 0.92);
6
+ }
7
+ * { box-sizing: border-box; }
8
+ body { margin: 0; }
9
+ .app {
10
+ min-height: 100vh;
11
+ display: flex;
12
+ flex-direction: column;
13
+ align-items: center;
14
+ justify-content: center;
15
+ gap: 16px;
16
+ padding: 24px;
17
+ text-align: center;
18
+ }
19
+ .app h1 { font-size: 2.5rem; margin: 0; }
20
+ .subtitle { margin: 0 0 8px; color: rgba(255, 255, 255, 0.6); max-width: 32rem; }
21
+ .status { display: flex; align-items: center; gap: 12px; }
22
+ .status code { background: rgba(255, 255, 255, 0.08); padding: 2px 8px; border-radius: 6px; }
23
+ .btn {
24
+ padding: 10px 18px;
25
+ border-radius: 8px;
26
+ border: 1px solid rgba(255, 255, 255, 0.2);
27
+ background: #ffcc00;
28
+ color: #0b0f1a;
29
+ font-weight: 600;
30
+ cursor: pointer;
31
+ }
32
+ .wallets { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 8px; width: 100%; max-width: 20rem; }
33
+ .wallet {
34
+ width: 100%;
35
+ display: flex;
36
+ align-items: center;
37
+ gap: 10px;
38
+ padding: 10px 14px;
39
+ border-radius: 8px;
40
+ border: 1px solid rgba(255, 255, 255, 0.15);
41
+ background: rgba(255, 255, 255, 0.04);
42
+ color: inherit;
43
+ cursor: pointer;
44
+ }
45
+ .wallet img { border-radius: 6px; }
@@ -0,0 +1,19 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "useDefineForClassFields": true,
5
+ "module": "ESNext",
6
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
7
+ "skipLibCheck": true,
8
+ "moduleResolution": "bundler",
9
+ "allowImportingTsExtensions": true,
10
+ "resolveJsonModule": true,
11
+ "isolatedModules": true,
12
+ "noEmit": true,
13
+ "strict": true,
14
+ "noUnusedLocals": true,
15
+ "noUnusedParameters": true,
16
+ "noFallthroughCasesInSwitch": true
17
+ },
18
+ "include": ["src"]
19
+ }
@@ -0,0 +1,4 @@
1
+ import { defineConfig } from 'vite';
2
+
3
+ // https://vite.dev/config/
4
+ export default defineConfig({});
@@ -10,10 +10,11 @@
10
10
  "postinstall": "nuxt prepare"
11
11
  },
12
12
  "dependencies": {
13
- "@partylayer/sdk": "^0.10.0",
14
- "@partylayer/session": "^1.1.0",
15
- "@partylayer/vue": "^0.1.3",
13
+ "@partylayer/sdk": "^0.15.0",
14
+ "@partylayer/session": "^1.1.4",
15
+ "@partylayer/vue": "^1.0.1",
16
16
  "@pinia/nuxt": "^0.5.5",
17
+ "@tanstack/vue-query": "^5.0.0",
17
18
  "pinia": "^2.2.0",
18
19
  "vue": "^3.5.0"
19
20
  },
@@ -15,14 +15,14 @@ const sessionStore = useSessionStore();
15
15
  <template>
16
16
  <main class="app">
17
17
  <h1>{{PROJECT_NAME}}</h1>
18
- <p class="subtitle">A PartyLayer dApp on Canton — Nuxt 3 + Pinia, SSR session.</p>
18
+ <p class="subtitle">A PartyLayer dApp on Canton: Nuxt 3 + Pinia, SSR session.</p>
19
19
 
20
20
  <!-- SERVER-RENDERED party from the cookie (initial HTML, no flash) -->
21
21
  <p v-if="serverParty" class="session">
22
22
  Connected as <code data-testid="server-party">{{ truncateParty(serverParty) }}</code>
23
23
  </p>
24
24
  <p v-else class="session" data-testid="server-cta">
25
- Not connected — connect a wallet to continue.
25
+ Not connected: connect a wallet to continue.
26
26
  </p>
27
27
 
28
28
  <!-- Pinia-backed interactive session (reads the Pinia store, not the composables) -->
@@ -1,14 +1,22 @@
1
+ import { VueQueryPlugin } from '@tanstack/vue-query';
1
2
  import { createPartyLayer } from '@partylayer/sdk';
2
3
  import { createPartyLayerSession } from '@partylayer/vue';
3
4
  import { createCookieStorage } from '@partylayer/session';
4
5
 
5
6
  /**
6
7
  * Client-only plugin: create the PartyLayer client and provide the session store
7
- * app-wide. cookieStorage on the client uses document.cookie — the SAME cookie
8
+ * app-wide. cookieStorage on the client uses document.cookie, the SAME cookie
8
9
  * the server reads for SSR (lib/session.ts). The connected/wallet UI is
9
10
  * client-side; the server-rendered party comes from the cookie directly.
11
+ *
12
+ * VueQueryPlugin supplies the QueryClient that PartyLayer's data composables
13
+ * (useDamlContract, useChoice, the cost composables) build on. It is registered
14
+ * client-side here, so there is no SSR query state to dehydrate; the base session
15
+ * composables work without it, but it is set up so the query composables are
16
+ * ready when you reach for them. For SSR query prefetch, see the Nuxt SSR guide.
10
17
  */
11
18
  export default defineNuxtPlugin((nuxtApp) => {
19
+ nuxtApp.vueApp.use(VueQueryPlugin);
12
20
  const client = createPartyLayer({ network: 'devnet', app: { name: '{{PROJECT_NAME}}' } });
13
21
  nuxtApp.vueApp.use(
14
22
  createPartyLayerSession({