@partylayer/vue 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/LICENSE +21 -0
- package/README.md +97 -0
- package/dist/index.d.mts +119 -0
- package/dist/index.d.ts +119 -0
- package/dist/index.js +149 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +141 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +58 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 PartyLayer
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# @partylayer/vue
|
|
2
|
+
|
|
3
|
+
Vue 3 composables for PartyLayer sessions — thin reactive bindings over the
|
|
4
|
+
framework-agnostic [`@partylayer/session`](../session) store. Mirrors
|
|
5
|
+
[`@partylayer/react`](../react)'s session API.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @partylayer/vue @partylayer/session vue
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
Provide a store near the root, then read it from any descendant composable.
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
// main.ts — plugin form
|
|
19
|
+
import { createApp } from 'vue';
|
|
20
|
+
import { createPartyLayerSession } from '@partylayer/vue';
|
|
21
|
+
import App from './App.vue';
|
|
22
|
+
|
|
23
|
+
createApp(App)
|
|
24
|
+
.use(createPartyLayerSession({ provider, persistSnapshot: true, broadcast: true }))
|
|
25
|
+
.mount('#app');
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
```vue
|
|
29
|
+
<!-- or in a root setup() -->
|
|
30
|
+
<script setup lang="ts">
|
|
31
|
+
import { provideSessionStore, useSession, useAccount } from '@partylayer/vue';
|
|
32
|
+
|
|
33
|
+
provideSessionStore({ provider /* any CIP0103Provider */, reconnect: { /* … */ } });
|
|
34
|
+
|
|
35
|
+
const { status, account, networkId, isConnected, connect, disconnect, on } = useSession();
|
|
36
|
+
const { party, chain } = useAccount();
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<template>
|
|
40
|
+
<button v-if="!isConnected" @click="connect()">Connect</button>
|
|
41
|
+
<div v-else>{{ party }} · {{ networkId }} <button @click="disconnect()">Disconnect</button></div>
|
|
42
|
+
</template>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Side-effects on session transitions:
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import { useAccountEffect } from '@partylayer/vue';
|
|
49
|
+
|
|
50
|
+
useAccountEffect({
|
|
51
|
+
onConnect: ({ account }) => {/* … */},
|
|
52
|
+
onDisconnect: () => {/* … */},
|
|
53
|
+
onPartyChanged: ({ previous, current }) => {/* invalidate caches */},
|
|
54
|
+
});
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Composables
|
|
58
|
+
|
|
59
|
+
- **`useSession()`** → reactive `SessionState` (`status`, `account`, `accounts`,
|
|
60
|
+
`networkId`, `lastError`, `isConnected`/`isConnecting`/`isReconnecting`/
|
|
61
|
+
`isDisconnected`) plus actions `connect` / `disconnect` / `restore` and the
|
|
62
|
+
narrowed event subscription `on(event, handler)`.
|
|
63
|
+
- **`useAccount()`** → reactive `{ party, address, account, accounts, status,
|
|
64
|
+
isConnected, …, networkId, chain, lastError }`.
|
|
65
|
+
- **`useAccountEffect({ onConnect, onDisconnect, onPartyChanged })`** →
|
|
66
|
+
fire-and-forget side-effects; auto-cleans on scope teardown.
|
|
67
|
+
|
|
68
|
+
## Provisioning
|
|
69
|
+
|
|
70
|
+
- **`provideSessionStore(config)`** — core; call in a `setup()`.
|
|
71
|
+
- **`createPartyLayerSession(config)`** — a thin Vue plugin (`app.use(...)`)
|
|
72
|
+
over the same provide (single source of truth).
|
|
73
|
+
|
|
74
|
+
`config` is **either** a pre-built `SessionStore` **or**
|
|
75
|
+
`{ provider: CIP0103Provider } & SessionStoreOptions`.
|
|
76
|
+
|
|
77
|
+
**Ownership rule:** when built from config, this layer owns the lifecycle —
|
|
78
|
+
`init()` runs **client-only** (on mount), `destroy()` on scope/app teardown. A
|
|
79
|
+
**pre-built** store's lifecycle belongs to you; it is never `init()`/`destroy()`d
|
|
80
|
+
here.
|
|
81
|
+
|
|
82
|
+
SSR-safe: the store is constructed without DOM access and `init()` is deferred to
|
|
83
|
+
the client; with no provided store, composables report a disconnected session and
|
|
84
|
+
the actions are no-ops.
|
|
85
|
+
|
|
86
|
+
## React ↔ Vue parity (deviations flagged)
|
|
87
|
+
|
|
88
|
+
| `@partylayer/react` | `@partylayer/vue` | Deviation |
|
|
89
|
+
|---|---|---|
|
|
90
|
+
| `useSession()` → plain values | `useSession()` → **Vue refs** (`ComputedRef`) | **Returns refs** — access `.value` (or auto-unwrap in templates); destructuring keeps reactivity |
|
|
91
|
+
| `useAccount()` → plain values | `useAccount()` → **Vue refs** | same — refs, not plain values |
|
|
92
|
+
| `useAccountEffect({…})` | `useAccountEffect({…})` | identical handler shape; Vue cleans up via `onScopeDispose` |
|
|
93
|
+
| `<PartyLayerProvider>` / `<PartyLayerKit>` (components) | **`provideSessionStore()`** / **`createPartyLayerSession()` plugin** | Vue uses provide-inject / a plugin, not a wrapper component |
|
|
94
|
+
| `useClientSession()` (legacy SDK getter) | — | **not ported** (Vue has no legacy SDK-session layer) |
|
|
95
|
+
|
|
96
|
+
`connect(params?)` mirrors the store's own signature exactly
|
|
97
|
+
(`params?: Record<string, unknown>`).
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { InjectionKey, Plugin, ComputedRef } from 'vue';
|
|
2
|
+
import { SessionStore, SessionStoreOptions, SessionAccount, SessionStatus, SessionState, SessionEvent } from '@partylayer/session';
|
|
3
|
+
import { CIP0103Provider } from '@partylayer/core';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Session store provisioning for Vue — `provideSessionStore` (the core) and a
|
|
7
|
+
* thin `createPartyLayerSession` plugin that wraps the same provide, so there is
|
|
8
|
+
* a single source of truth (one injection key, one store-resolution path).
|
|
9
|
+
*
|
|
10
|
+
* Ownership rule:
|
|
11
|
+
* - Built from config → THIS layer owns the lifecycle: `init()` runs
|
|
12
|
+
* client-only (`onMounted`), `destroy()` on scope/app teardown.
|
|
13
|
+
* - A PRE-BUILT store → its lifecycle belongs to the caller; we NEVER
|
|
14
|
+
* `init()`/`destroy()` it.
|
|
15
|
+
*
|
|
16
|
+
* SSR-safe: the store is constructed without DOM access; `init()` is deferred to
|
|
17
|
+
* `onMounted` (client only), so server rendering never touches the wallet.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/** Injection key for the shared session store (or `null` when none provided). */
|
|
21
|
+
declare const SESSION_STORE_KEY: InjectionKey<SessionStore | null>;
|
|
22
|
+
/** Either a pre-built store, or config to build one (provider + store options). */
|
|
23
|
+
type ProvideSessionConfig = SessionStore | ({
|
|
24
|
+
provider: CIP0103Provider;
|
|
25
|
+
} & Partial<SessionStoreOptions>);
|
|
26
|
+
/**
|
|
27
|
+
* Provide a session store to descendant composables. Call in a component
|
|
28
|
+
* `setup()` (e.g. your root). Returns the store for direct use if needed.
|
|
29
|
+
*
|
|
30
|
+
* When built from config, lifecycle is owned here (client-only `init()` on
|
|
31
|
+
* mount, `destroy()` on scope teardown). A pre-built store is left untouched.
|
|
32
|
+
*/
|
|
33
|
+
declare function provideSessionStore(config: ProvideSessionConfig): SessionStore;
|
|
34
|
+
/**
|
|
35
|
+
* Vue plugin form — a THIN wrapper over the same provide/resolution, for
|
|
36
|
+
* `app.use(...)`. Owned stores `init()` on the client at install and `destroy()`
|
|
37
|
+
* when the app unmounts.
|
|
38
|
+
*/
|
|
39
|
+
declare function createPartyLayerSession(config: ProvideSessionConfig): Plugin;
|
|
40
|
+
/** Internal: read the provided store, or `null` if none (SSR / outside provider). */
|
|
41
|
+
declare function injectSessionStore(): SessionStore | null;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Vue composables over the `@partylayer/session` store. Wagmi/React parity:
|
|
45
|
+
* `useSession` (reactive state + actions), `useAccount`, `useAccountEffect`.
|
|
46
|
+
*
|
|
47
|
+
* Each composable derives individual `ComputedRef`s from ONE internal
|
|
48
|
+
* `shallowRef<SessionState>` fed by a single `store.subscribe`, cleaned up via
|
|
49
|
+
* `onScopeDispose` (no leak after unmount). Returning refs — not a `reactive()`
|
|
50
|
+
* object — means destructuring keeps reactivity.
|
|
51
|
+
*
|
|
52
|
+
* SSR-safe: with no provided store the refs report a disconnected session and
|
|
53
|
+
* the actions are no-ops; nothing touches `window`.
|
|
54
|
+
*/
|
|
55
|
+
|
|
56
|
+
/** wagmi-parity chain handle derived from the CAIP-2 networkId. */
|
|
57
|
+
interface SessionChain {
|
|
58
|
+
id: string;
|
|
59
|
+
}
|
|
60
|
+
interface UseSessionReturn {
|
|
61
|
+
status: ComputedRef<SessionStatus>;
|
|
62
|
+
account: ComputedRef<SessionAccount | null>;
|
|
63
|
+
accounts: ComputedRef<readonly SessionAccount[]>;
|
|
64
|
+
networkId: ComputedRef<string | null>;
|
|
65
|
+
lastError: ComputedRef<Error | null>;
|
|
66
|
+
isConnected: ComputedRef<boolean>;
|
|
67
|
+
isConnecting: ComputedRef<boolean>;
|
|
68
|
+
isReconnecting: ComputedRef<boolean>;
|
|
69
|
+
isDisconnected: ComputedRef<boolean>;
|
|
70
|
+
/** Connect via the store (mirrors the store's own signature exactly). */
|
|
71
|
+
connect(params?: Record<string, unknown>): Promise<SessionState>;
|
|
72
|
+
disconnect(): Promise<void>;
|
|
73
|
+
restore(): Promise<SessionState>;
|
|
74
|
+
/** Subscribe to a structured resilience/sync event (narrowed by `event`). */
|
|
75
|
+
on<T extends SessionEvent['type']>(event: T, handler: (event: Extract<SessionEvent, {
|
|
76
|
+
type: T;
|
|
77
|
+
}>) => void): () => void;
|
|
78
|
+
}
|
|
79
|
+
/** Reactive session state + bound actions. */
|
|
80
|
+
declare function useSession(): UseSessionReturn;
|
|
81
|
+
interface UseAccountReturn {
|
|
82
|
+
party: ComputedRef<string | null>;
|
|
83
|
+
/** wagmi-parity alias of `party`. */
|
|
84
|
+
address: ComputedRef<string | null>;
|
|
85
|
+
account: ComputedRef<SessionAccount | null>;
|
|
86
|
+
accounts: ComputedRef<readonly SessionAccount[]>;
|
|
87
|
+
status: ComputedRef<SessionStatus>;
|
|
88
|
+
isConnected: ComputedRef<boolean>;
|
|
89
|
+
isConnecting: ComputedRef<boolean>;
|
|
90
|
+
isReconnecting: ComputedRef<boolean>;
|
|
91
|
+
isDisconnected: ComputedRef<boolean>;
|
|
92
|
+
networkId: ComputedRef<string | null>;
|
|
93
|
+
chain: ComputedRef<SessionChain | null>;
|
|
94
|
+
lastError: ComputedRef<Error | null>;
|
|
95
|
+
}
|
|
96
|
+
/** Reactive active-account view (wagmi parity). */
|
|
97
|
+
declare function useAccount(): UseAccountReturn;
|
|
98
|
+
interface UseAccountEffectParameters {
|
|
99
|
+
/** Fired on a transition INTO `connected` (once the account is available). */
|
|
100
|
+
onConnect?: (data: {
|
|
101
|
+
account: SessionAccount | null;
|
|
102
|
+
accounts: readonly SessionAccount[];
|
|
103
|
+
networkId: string | null;
|
|
104
|
+
}) => void;
|
|
105
|
+
/** Fired on a transition `connected → disconnected`. */
|
|
106
|
+
onDisconnect?: () => void;
|
|
107
|
+
/** Fired when the active PRIMARY party changes (a switch, not a reorder). */
|
|
108
|
+
onPartyChanged?: (data: {
|
|
109
|
+
previous: string | null;
|
|
110
|
+
current: string | null;
|
|
111
|
+
}) => void;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Fire side-effects on session transitions — no render churn. Auto-cleans on
|
|
115
|
+
* scope teardown (`onScopeDispose`). No-op when no store is provided.
|
|
116
|
+
*/
|
|
117
|
+
declare function useAccountEffect(parameters?: UseAccountEffectParameters): void;
|
|
118
|
+
|
|
119
|
+
export { type ProvideSessionConfig, SESSION_STORE_KEY, type SessionChain, type UseAccountEffectParameters, type UseAccountReturn, type UseSessionReturn, createPartyLayerSession, injectSessionStore, provideSessionStore, useAccount, useAccountEffect, useSession };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { InjectionKey, Plugin, ComputedRef } from 'vue';
|
|
2
|
+
import { SessionStore, SessionStoreOptions, SessionAccount, SessionStatus, SessionState, SessionEvent } from '@partylayer/session';
|
|
3
|
+
import { CIP0103Provider } from '@partylayer/core';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Session store provisioning for Vue — `provideSessionStore` (the core) and a
|
|
7
|
+
* thin `createPartyLayerSession` plugin that wraps the same provide, so there is
|
|
8
|
+
* a single source of truth (one injection key, one store-resolution path).
|
|
9
|
+
*
|
|
10
|
+
* Ownership rule:
|
|
11
|
+
* - Built from config → THIS layer owns the lifecycle: `init()` runs
|
|
12
|
+
* client-only (`onMounted`), `destroy()` on scope/app teardown.
|
|
13
|
+
* - A PRE-BUILT store → its lifecycle belongs to the caller; we NEVER
|
|
14
|
+
* `init()`/`destroy()` it.
|
|
15
|
+
*
|
|
16
|
+
* SSR-safe: the store is constructed without DOM access; `init()` is deferred to
|
|
17
|
+
* `onMounted` (client only), so server rendering never touches the wallet.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/** Injection key for the shared session store (or `null` when none provided). */
|
|
21
|
+
declare const SESSION_STORE_KEY: InjectionKey<SessionStore | null>;
|
|
22
|
+
/** Either a pre-built store, or config to build one (provider + store options). */
|
|
23
|
+
type ProvideSessionConfig = SessionStore | ({
|
|
24
|
+
provider: CIP0103Provider;
|
|
25
|
+
} & Partial<SessionStoreOptions>);
|
|
26
|
+
/**
|
|
27
|
+
* Provide a session store to descendant composables. Call in a component
|
|
28
|
+
* `setup()` (e.g. your root). Returns the store for direct use if needed.
|
|
29
|
+
*
|
|
30
|
+
* When built from config, lifecycle is owned here (client-only `init()` on
|
|
31
|
+
* mount, `destroy()` on scope teardown). A pre-built store is left untouched.
|
|
32
|
+
*/
|
|
33
|
+
declare function provideSessionStore(config: ProvideSessionConfig): SessionStore;
|
|
34
|
+
/**
|
|
35
|
+
* Vue plugin form — a THIN wrapper over the same provide/resolution, for
|
|
36
|
+
* `app.use(...)`. Owned stores `init()` on the client at install and `destroy()`
|
|
37
|
+
* when the app unmounts.
|
|
38
|
+
*/
|
|
39
|
+
declare function createPartyLayerSession(config: ProvideSessionConfig): Plugin;
|
|
40
|
+
/** Internal: read the provided store, or `null` if none (SSR / outside provider). */
|
|
41
|
+
declare function injectSessionStore(): SessionStore | null;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Vue composables over the `@partylayer/session` store. Wagmi/React parity:
|
|
45
|
+
* `useSession` (reactive state + actions), `useAccount`, `useAccountEffect`.
|
|
46
|
+
*
|
|
47
|
+
* Each composable derives individual `ComputedRef`s from ONE internal
|
|
48
|
+
* `shallowRef<SessionState>` fed by a single `store.subscribe`, cleaned up via
|
|
49
|
+
* `onScopeDispose` (no leak after unmount). Returning refs — not a `reactive()`
|
|
50
|
+
* object — means destructuring keeps reactivity.
|
|
51
|
+
*
|
|
52
|
+
* SSR-safe: with no provided store the refs report a disconnected session and
|
|
53
|
+
* the actions are no-ops; nothing touches `window`.
|
|
54
|
+
*/
|
|
55
|
+
|
|
56
|
+
/** wagmi-parity chain handle derived from the CAIP-2 networkId. */
|
|
57
|
+
interface SessionChain {
|
|
58
|
+
id: string;
|
|
59
|
+
}
|
|
60
|
+
interface UseSessionReturn {
|
|
61
|
+
status: ComputedRef<SessionStatus>;
|
|
62
|
+
account: ComputedRef<SessionAccount | null>;
|
|
63
|
+
accounts: ComputedRef<readonly SessionAccount[]>;
|
|
64
|
+
networkId: ComputedRef<string | null>;
|
|
65
|
+
lastError: ComputedRef<Error | null>;
|
|
66
|
+
isConnected: ComputedRef<boolean>;
|
|
67
|
+
isConnecting: ComputedRef<boolean>;
|
|
68
|
+
isReconnecting: ComputedRef<boolean>;
|
|
69
|
+
isDisconnected: ComputedRef<boolean>;
|
|
70
|
+
/** Connect via the store (mirrors the store's own signature exactly). */
|
|
71
|
+
connect(params?: Record<string, unknown>): Promise<SessionState>;
|
|
72
|
+
disconnect(): Promise<void>;
|
|
73
|
+
restore(): Promise<SessionState>;
|
|
74
|
+
/** Subscribe to a structured resilience/sync event (narrowed by `event`). */
|
|
75
|
+
on<T extends SessionEvent['type']>(event: T, handler: (event: Extract<SessionEvent, {
|
|
76
|
+
type: T;
|
|
77
|
+
}>) => void): () => void;
|
|
78
|
+
}
|
|
79
|
+
/** Reactive session state + bound actions. */
|
|
80
|
+
declare function useSession(): UseSessionReturn;
|
|
81
|
+
interface UseAccountReturn {
|
|
82
|
+
party: ComputedRef<string | null>;
|
|
83
|
+
/** wagmi-parity alias of `party`. */
|
|
84
|
+
address: ComputedRef<string | null>;
|
|
85
|
+
account: ComputedRef<SessionAccount | null>;
|
|
86
|
+
accounts: ComputedRef<readonly SessionAccount[]>;
|
|
87
|
+
status: ComputedRef<SessionStatus>;
|
|
88
|
+
isConnected: ComputedRef<boolean>;
|
|
89
|
+
isConnecting: ComputedRef<boolean>;
|
|
90
|
+
isReconnecting: ComputedRef<boolean>;
|
|
91
|
+
isDisconnected: ComputedRef<boolean>;
|
|
92
|
+
networkId: ComputedRef<string | null>;
|
|
93
|
+
chain: ComputedRef<SessionChain | null>;
|
|
94
|
+
lastError: ComputedRef<Error | null>;
|
|
95
|
+
}
|
|
96
|
+
/** Reactive active-account view (wagmi parity). */
|
|
97
|
+
declare function useAccount(): UseAccountReturn;
|
|
98
|
+
interface UseAccountEffectParameters {
|
|
99
|
+
/** Fired on a transition INTO `connected` (once the account is available). */
|
|
100
|
+
onConnect?: (data: {
|
|
101
|
+
account: SessionAccount | null;
|
|
102
|
+
accounts: readonly SessionAccount[];
|
|
103
|
+
networkId: string | null;
|
|
104
|
+
}) => void;
|
|
105
|
+
/** Fired on a transition `connected → disconnected`. */
|
|
106
|
+
onDisconnect?: () => void;
|
|
107
|
+
/** Fired when the active PRIMARY party changes (a switch, not a reorder). */
|
|
108
|
+
onPartyChanged?: (data: {
|
|
109
|
+
previous: string | null;
|
|
110
|
+
current: string | null;
|
|
111
|
+
}) => void;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Fire side-effects on session transitions — no render churn. Auto-cleans on
|
|
115
|
+
* scope teardown (`onScopeDispose`). No-op when no store is provided.
|
|
116
|
+
*/
|
|
117
|
+
declare function useAccountEffect(parameters?: UseAccountEffectParameters): void;
|
|
118
|
+
|
|
119
|
+
export { type ProvideSessionConfig, SESSION_STORE_KEY, type SessionChain, type UseAccountEffectParameters, type UseAccountReturn, type UseSessionReturn, createPartyLayerSession, injectSessionStore, provideSessionStore, useAccount, useAccountEffect, useSession };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var vue = require('vue');
|
|
4
|
+
var session = require('@partylayer/session');
|
|
5
|
+
|
|
6
|
+
// src/provide.ts
|
|
7
|
+
var SESSION_STORE_KEY = /* @__PURE__ */ Symbol("partylayer.session.store");
|
|
8
|
+
function isStore(config) {
|
|
9
|
+
return typeof config.getSnapshot === "function";
|
|
10
|
+
}
|
|
11
|
+
function resolveStore(config) {
|
|
12
|
+
if (isStore(config)) return { store: config, owned: false };
|
|
13
|
+
const { provider, ...options } = config;
|
|
14
|
+
return { store: session.createSessionStore(provider, options), owned: true };
|
|
15
|
+
}
|
|
16
|
+
function provideSessionStore(config) {
|
|
17
|
+
const { store, owned } = resolveStore(config);
|
|
18
|
+
vue.provide(SESSION_STORE_KEY, store);
|
|
19
|
+
if (owned) {
|
|
20
|
+
vue.onMounted(() => {
|
|
21
|
+
void store.init();
|
|
22
|
+
});
|
|
23
|
+
vue.onScopeDispose(() => {
|
|
24
|
+
store.destroy();
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
return store;
|
|
28
|
+
}
|
|
29
|
+
function createPartyLayerSession(config) {
|
|
30
|
+
return {
|
|
31
|
+
install(app) {
|
|
32
|
+
const { store, owned } = resolveStore(config);
|
|
33
|
+
app.provide(SESSION_STORE_KEY, store);
|
|
34
|
+
if (owned) {
|
|
35
|
+
if (typeof window !== "undefined") void store.init();
|
|
36
|
+
const unmount = app.unmount.bind(app);
|
|
37
|
+
app.unmount = () => {
|
|
38
|
+
try {
|
|
39
|
+
store.destroy();
|
|
40
|
+
} finally {
|
|
41
|
+
unmount();
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function injectSessionStore() {
|
|
49
|
+
return vue.inject(SESSION_STORE_KEY, null);
|
|
50
|
+
}
|
|
51
|
+
var DISCONNECTED = {
|
|
52
|
+
status: "disconnected",
|
|
53
|
+
account: null,
|
|
54
|
+
accounts: [],
|
|
55
|
+
networkId: null,
|
|
56
|
+
lastError: null
|
|
57
|
+
};
|
|
58
|
+
function useSessionState() {
|
|
59
|
+
const store = injectSessionStore();
|
|
60
|
+
const state = vue.shallowRef(store ? store.getSnapshot() : DISCONNECTED);
|
|
61
|
+
if (store) {
|
|
62
|
+
const unsubscribe = store.subscribe(() => {
|
|
63
|
+
state.value = store.getSnapshot();
|
|
64
|
+
});
|
|
65
|
+
vue.onScopeDispose(unsubscribe);
|
|
66
|
+
}
|
|
67
|
+
return { store, state };
|
|
68
|
+
}
|
|
69
|
+
function useSession() {
|
|
70
|
+
const { store, state } = useSessionState();
|
|
71
|
+
return {
|
|
72
|
+
status: vue.computed(() => state.value.status),
|
|
73
|
+
account: vue.computed(() => state.value.account),
|
|
74
|
+
accounts: vue.computed(() => state.value.accounts),
|
|
75
|
+
networkId: vue.computed(() => state.value.networkId),
|
|
76
|
+
lastError: vue.computed(() => state.value.lastError),
|
|
77
|
+
isConnected: vue.computed(() => state.value.status === "connected"),
|
|
78
|
+
isConnecting: vue.computed(() => state.value.status === "connecting"),
|
|
79
|
+
isReconnecting: vue.computed(() => state.value.status === "reconnecting"),
|
|
80
|
+
isDisconnected: vue.computed(() => state.value.status === "disconnected"),
|
|
81
|
+
connect: (params) => store ? store.connect(params) : Promise.resolve(DISCONNECTED),
|
|
82
|
+
disconnect: () => store ? store.disconnect() : Promise.resolve(),
|
|
83
|
+
restore: () => store ? store.restore() : Promise.resolve(DISCONNECTED),
|
|
84
|
+
on: (event, handler) => store ? store.on(event, handler) : () => {
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function useAccount() {
|
|
89
|
+
const { state } = useSessionState();
|
|
90
|
+
const party = vue.computed(() => state.value.account?.partyId ?? null);
|
|
91
|
+
return {
|
|
92
|
+
party,
|
|
93
|
+
address: party,
|
|
94
|
+
account: vue.computed(() => state.value.account),
|
|
95
|
+
accounts: vue.computed(() => state.value.accounts),
|
|
96
|
+
status: vue.computed(() => state.value.status),
|
|
97
|
+
isConnected: vue.computed(() => state.value.status === "connected"),
|
|
98
|
+
isConnecting: vue.computed(() => state.value.status === "connecting"),
|
|
99
|
+
isReconnecting: vue.computed(() => state.value.status === "reconnecting"),
|
|
100
|
+
isDisconnected: vue.computed(() => state.value.status === "disconnected"),
|
|
101
|
+
networkId: vue.computed(() => state.value.networkId),
|
|
102
|
+
chain: vue.computed(() => state.value.networkId ? { id: state.value.networkId } : null),
|
|
103
|
+
lastError: vue.computed(() => state.value.lastError)
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
function useAccountEffect(parameters = {}) {
|
|
107
|
+
const store = injectSessionStore();
|
|
108
|
+
if (!store) return;
|
|
109
|
+
let prev = store.getSnapshot().status;
|
|
110
|
+
let firedConnect = false;
|
|
111
|
+
const unsubscribe = store.subscribe(() => {
|
|
112
|
+
const next = store.getSnapshot();
|
|
113
|
+
const was = prev;
|
|
114
|
+
const now = next.status;
|
|
115
|
+
prev = now;
|
|
116
|
+
if (now === "connected") {
|
|
117
|
+
if (!firedConnect && next.account) {
|
|
118
|
+
firedConnect = true;
|
|
119
|
+
parameters.onConnect?.({
|
|
120
|
+
account: next.account,
|
|
121
|
+
accounts: next.accounts,
|
|
122
|
+
networkId: next.networkId
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
} else if (now === "disconnected" && (was === "connected" || firedConnect)) {
|
|
126
|
+
firedConnect = false;
|
|
127
|
+
parameters.onDisconnect?.();
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
const unsubscribeParty = store.on("party:changed", (e) => {
|
|
131
|
+
if (e.type === "party:changed") {
|
|
132
|
+
parameters.onPartyChanged?.({ previous: e.previous, current: e.current });
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
vue.onScopeDispose(() => {
|
|
136
|
+
unsubscribe();
|
|
137
|
+
unsubscribeParty();
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
exports.SESSION_STORE_KEY = SESSION_STORE_KEY;
|
|
142
|
+
exports.createPartyLayerSession = createPartyLayerSession;
|
|
143
|
+
exports.injectSessionStore = injectSessionStore;
|
|
144
|
+
exports.provideSessionStore = provideSessionStore;
|
|
145
|
+
exports.useAccount = useAccount;
|
|
146
|
+
exports.useAccountEffect = useAccountEffect;
|
|
147
|
+
exports.useSession = useSession;
|
|
148
|
+
//# sourceMappingURL=index.js.map
|
|
149
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/provide.ts","../src/composables.ts"],"names":["createSessionStore","provide","onMounted","onScopeDispose","inject","shallowRef","computed"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Session store provisioning for Vue — `provideSessionStore` (the core) and a\n * thin `createPartyLayerSession` plugin that wraps the same provide, so there is\n * a single source of truth (one injection key, one store-resolution path).\n *\n * Ownership rule:\n * - Built from config → THIS layer owns the lifecycle: `init()` runs\n * client-only (`onMounted`), `destroy()` on scope/app teardown.\n * - A PRE-BUILT store → its lifecycle belongs to the caller; we NEVER\n * `init()`/`destroy()` it.\n *\n * SSR-safe: the store is constructed without DOM access; `init()` is deferred to\n * `onMounted` (client only), so server rendering never touches the wallet.\n */\nimport { inject, onMounted, onScopeDispose, provide, type App, type InjectionKey, type Plugin } from 'vue';\nimport { createSessionStore, type SessionStore, type SessionStoreOptions } from '@partylayer/session';\nimport type { CIP0103Provider } from '@partylayer/core';\n\n/** Injection key for the shared session store (or `null` when none provided). */\nexport const SESSION_STORE_KEY: InjectionKey<SessionStore | null> = Symbol('partylayer.session.store');\n\n/** Either a pre-built store, or config to build one (provider + store options). */\nexport type ProvideSessionConfig =\n | SessionStore\n | ({ provider: CIP0103Provider } & Partial<SessionStoreOptions>);\n\nfunction isStore(config: ProvideSessionConfig): config is SessionStore {\n return typeof (config as SessionStore).getSnapshot === 'function';\n}\n\n/** Resolve a config into a store + whether THIS layer owns its lifecycle. */\nfunction resolveStore(config: ProvideSessionConfig): { store: SessionStore; owned: boolean } {\n if (isStore(config)) return { store: config, owned: false };\n const { provider, ...options } = config;\n return { store: createSessionStore(provider, options), owned: true };\n}\n\n/**\n * Provide a session store to descendant composables. Call in a component\n * `setup()` (e.g. your root). Returns the store for direct use if needed.\n *\n * When built from config, lifecycle is owned here (client-only `init()` on\n * mount, `destroy()` on scope teardown). A pre-built store is left untouched.\n */\nexport function provideSessionStore(config: ProvideSessionConfig): SessionStore {\n const { store, owned } = resolveStore(config);\n provide(SESSION_STORE_KEY, store);\n if (owned) {\n // Client-only restore/reconnect; never runs during SSR.\n onMounted(() => {\n void store.init();\n });\n onScopeDispose(() => {\n store.destroy();\n });\n }\n return store;\n}\n\n/**\n * Vue plugin form — a THIN wrapper over the same provide/resolution, for\n * `app.use(...)`. Owned stores `init()` on the client at install and `destroy()`\n * when the app unmounts.\n */\nexport function createPartyLayerSession(config: ProvideSessionConfig): Plugin {\n return {\n install(app: App) {\n const { store, owned } = resolveStore(config);\n app.provide(SESSION_STORE_KEY, store);\n if (owned) {\n if (typeof window !== 'undefined') void store.init(); // client only (SSR-safe)\n const unmount = app.unmount.bind(app);\n app.unmount = () => {\n try {\n store.destroy();\n } finally {\n unmount();\n }\n };\n }\n },\n };\n}\n\n/** Internal: read the provided store, or `null` if none (SSR / outside provider). */\nexport function injectSessionStore(): SessionStore | null {\n return inject(SESSION_STORE_KEY, null);\n}\n","/**\n * Vue composables over the `@partylayer/session` store. Wagmi/React parity:\n * `useSession` (reactive state + actions), `useAccount`, `useAccountEffect`.\n *\n * Each composable derives individual `ComputedRef`s from ONE internal\n * `shallowRef<SessionState>` fed by a single `store.subscribe`, cleaned up via\n * `onScopeDispose` (no leak after unmount). Returning refs — not a `reactive()`\n * object — means destructuring keeps reactivity.\n *\n * SSR-safe: with no provided store the refs report a disconnected session and\n * the actions are no-ops; nothing touches `window`.\n */\nimport { computed, onScopeDispose, shallowRef, type ComputedRef } from 'vue';\nimport type {\n SessionAccount,\n SessionEvent,\n SessionState,\n SessionStatus,\n} from '@partylayer/session';\nimport { injectSessionStore } from './provide';\n\nconst DISCONNECTED: SessionState = {\n status: 'disconnected',\n account: null,\n accounts: [],\n networkId: null,\n lastError: null,\n};\n\n/** Subscribe ONCE to the provided store; returns a reactive snapshot ref. */\nfunction useSessionState() {\n const store = injectSessionStore();\n const state = shallowRef<SessionState>(store ? store.getSnapshot() : DISCONNECTED);\n if (store) {\n const unsubscribe = store.subscribe(() => {\n state.value = store.getSnapshot();\n });\n onScopeDispose(unsubscribe);\n }\n return { store, state };\n}\n\n/** wagmi-parity chain handle derived from the CAIP-2 networkId. */\nexport interface SessionChain {\n id: string;\n}\n\nexport interface UseSessionReturn {\n status: ComputedRef<SessionStatus>;\n account: ComputedRef<SessionAccount | null>;\n accounts: ComputedRef<readonly SessionAccount[]>;\n networkId: ComputedRef<string | null>;\n lastError: ComputedRef<Error | null>;\n isConnected: ComputedRef<boolean>;\n isConnecting: ComputedRef<boolean>;\n isReconnecting: ComputedRef<boolean>;\n isDisconnected: ComputedRef<boolean>;\n /** Connect via the store (mirrors the store's own signature exactly). */\n connect(params?: Record<string, unknown>): Promise<SessionState>;\n disconnect(): Promise<void>;\n restore(): Promise<SessionState>;\n /** Subscribe to a structured resilience/sync event (narrowed by `event`). */\n on<T extends SessionEvent['type']>(\n event: T,\n handler: (event: Extract<SessionEvent, { type: T }>) => void,\n ): () => void;\n}\n\n/** Reactive session state + bound actions. */\nexport function useSession(): UseSessionReturn {\n const { store, state } = useSessionState();\n return {\n status: computed(() => state.value.status),\n account: computed(() => state.value.account),\n accounts: computed(() => state.value.accounts),\n networkId: computed(() => state.value.networkId),\n lastError: computed(() => state.value.lastError),\n isConnected: computed(() => state.value.status === 'connected'),\n isConnecting: computed(() => state.value.status === 'connecting'),\n isReconnecting: computed(() => state.value.status === 'reconnecting'),\n isDisconnected: computed(() => state.value.status === 'disconnected'),\n connect: (params?: Record<string, unknown>) =>\n store ? store.connect(params) : Promise.resolve(DISCONNECTED),\n disconnect: () => (store ? store.disconnect() : Promise.resolve()),\n restore: () => (store ? store.restore() : Promise.resolve(DISCONNECTED)),\n on: <T extends SessionEvent['type']>(\n event: T,\n handler: (event: Extract<SessionEvent, { type: T }>) => void,\n ): (() => void) => (store ? store.on(event, handler as (e: SessionEvent) => void) : () => {}),\n };\n}\n\nexport interface UseAccountReturn {\n party: ComputedRef<string | null>;\n /** wagmi-parity alias of `party`. */\n address: ComputedRef<string | null>;\n account: ComputedRef<SessionAccount | null>;\n accounts: ComputedRef<readonly SessionAccount[]>;\n status: ComputedRef<SessionStatus>;\n isConnected: ComputedRef<boolean>;\n isConnecting: ComputedRef<boolean>;\n isReconnecting: ComputedRef<boolean>;\n isDisconnected: ComputedRef<boolean>;\n networkId: ComputedRef<string | null>;\n chain: ComputedRef<SessionChain | null>;\n lastError: ComputedRef<Error | null>;\n}\n\n/** Reactive active-account view (wagmi parity). */\nexport function useAccount(): UseAccountReturn {\n const { state } = useSessionState();\n const party = computed(() => state.value.account?.partyId ?? null);\n return {\n party,\n address: party,\n account: computed(() => state.value.account),\n accounts: computed(() => state.value.accounts),\n status: computed(() => state.value.status),\n isConnected: computed(() => state.value.status === 'connected'),\n isConnecting: computed(() => state.value.status === 'connecting'),\n isReconnecting: computed(() => state.value.status === 'reconnecting'),\n isDisconnected: computed(() => state.value.status === 'disconnected'),\n networkId: computed(() => state.value.networkId),\n chain: computed(() => (state.value.networkId ? { id: state.value.networkId } : null)),\n lastError: computed(() => state.value.lastError),\n };\n}\n\nexport interface UseAccountEffectParameters {\n /** Fired on a transition INTO `connected` (once the account is available). */\n onConnect?: (data: {\n account: SessionAccount | null;\n accounts: readonly SessionAccount[];\n networkId: string | null;\n }) => void;\n /** Fired on a transition `connected → disconnected`. */\n onDisconnect?: () => void;\n /** Fired when the active PRIMARY party changes (a switch, not a reorder). */\n onPartyChanged?: (data: { previous: string | null; current: string | null }) => void;\n}\n\n/**\n * Fire side-effects on session transitions — no render churn. Auto-cleans on\n * scope teardown (`onScopeDispose`). No-op when no store is provided.\n */\nexport function useAccountEffect(parameters: UseAccountEffectParameters = {}): void {\n const store = injectSessionStore();\n if (!store) return;\n\n let prev: SessionStatus = store.getSnapshot().status;\n // statusChanged(connected) can arrive BEFORE accountsChanged, so fire\n // onConnect once per session on the tick the account becomes available.\n let firedConnect = false;\n\n const unsubscribe = store.subscribe(() => {\n const next = store.getSnapshot();\n const was = prev;\n const now = next.status;\n prev = now;\n if (now === 'connected') {\n if (!firedConnect && next.account) {\n firedConnect = true;\n parameters.onConnect?.({\n account: next.account,\n accounts: next.accounts,\n networkId: next.networkId,\n });\n }\n } else if (now === 'disconnected' && (was === 'connected' || firedConnect)) {\n firedConnect = false;\n parameters.onDisconnect?.();\n }\n });\n\n const unsubscribeParty = store.on('party:changed', (e) => {\n if (e.type === 'party:changed') {\n parameters.onPartyChanged?.({ previous: e.previous, current: e.current });\n }\n });\n\n onScopeDispose(() => {\n unsubscribe();\n unsubscribeParty();\n });\n}\n"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { provide, onMounted, onScopeDispose, inject, computed, shallowRef } from 'vue';
|
|
2
|
+
import { createSessionStore } from '@partylayer/session';
|
|
3
|
+
|
|
4
|
+
// src/provide.ts
|
|
5
|
+
var SESSION_STORE_KEY = /* @__PURE__ */ Symbol("partylayer.session.store");
|
|
6
|
+
function isStore(config) {
|
|
7
|
+
return typeof config.getSnapshot === "function";
|
|
8
|
+
}
|
|
9
|
+
function resolveStore(config) {
|
|
10
|
+
if (isStore(config)) return { store: config, owned: false };
|
|
11
|
+
const { provider, ...options } = config;
|
|
12
|
+
return { store: createSessionStore(provider, options), owned: true };
|
|
13
|
+
}
|
|
14
|
+
function provideSessionStore(config) {
|
|
15
|
+
const { store, owned } = resolveStore(config);
|
|
16
|
+
provide(SESSION_STORE_KEY, store);
|
|
17
|
+
if (owned) {
|
|
18
|
+
onMounted(() => {
|
|
19
|
+
void store.init();
|
|
20
|
+
});
|
|
21
|
+
onScopeDispose(() => {
|
|
22
|
+
store.destroy();
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
return store;
|
|
26
|
+
}
|
|
27
|
+
function createPartyLayerSession(config) {
|
|
28
|
+
return {
|
|
29
|
+
install(app) {
|
|
30
|
+
const { store, owned } = resolveStore(config);
|
|
31
|
+
app.provide(SESSION_STORE_KEY, store);
|
|
32
|
+
if (owned) {
|
|
33
|
+
if (typeof window !== "undefined") void store.init();
|
|
34
|
+
const unmount = app.unmount.bind(app);
|
|
35
|
+
app.unmount = () => {
|
|
36
|
+
try {
|
|
37
|
+
store.destroy();
|
|
38
|
+
} finally {
|
|
39
|
+
unmount();
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function injectSessionStore() {
|
|
47
|
+
return inject(SESSION_STORE_KEY, null);
|
|
48
|
+
}
|
|
49
|
+
var DISCONNECTED = {
|
|
50
|
+
status: "disconnected",
|
|
51
|
+
account: null,
|
|
52
|
+
accounts: [],
|
|
53
|
+
networkId: null,
|
|
54
|
+
lastError: null
|
|
55
|
+
};
|
|
56
|
+
function useSessionState() {
|
|
57
|
+
const store = injectSessionStore();
|
|
58
|
+
const state = shallowRef(store ? store.getSnapshot() : DISCONNECTED);
|
|
59
|
+
if (store) {
|
|
60
|
+
const unsubscribe = store.subscribe(() => {
|
|
61
|
+
state.value = store.getSnapshot();
|
|
62
|
+
});
|
|
63
|
+
onScopeDispose(unsubscribe);
|
|
64
|
+
}
|
|
65
|
+
return { store, state };
|
|
66
|
+
}
|
|
67
|
+
function useSession() {
|
|
68
|
+
const { store, state } = useSessionState();
|
|
69
|
+
return {
|
|
70
|
+
status: computed(() => state.value.status),
|
|
71
|
+
account: computed(() => state.value.account),
|
|
72
|
+
accounts: computed(() => state.value.accounts),
|
|
73
|
+
networkId: computed(() => state.value.networkId),
|
|
74
|
+
lastError: computed(() => state.value.lastError),
|
|
75
|
+
isConnected: computed(() => state.value.status === "connected"),
|
|
76
|
+
isConnecting: computed(() => state.value.status === "connecting"),
|
|
77
|
+
isReconnecting: computed(() => state.value.status === "reconnecting"),
|
|
78
|
+
isDisconnected: computed(() => state.value.status === "disconnected"),
|
|
79
|
+
connect: (params) => store ? store.connect(params) : Promise.resolve(DISCONNECTED),
|
|
80
|
+
disconnect: () => store ? store.disconnect() : Promise.resolve(),
|
|
81
|
+
restore: () => store ? store.restore() : Promise.resolve(DISCONNECTED),
|
|
82
|
+
on: (event, handler) => store ? store.on(event, handler) : () => {
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function useAccount() {
|
|
87
|
+
const { state } = useSessionState();
|
|
88
|
+
const party = computed(() => state.value.account?.partyId ?? null);
|
|
89
|
+
return {
|
|
90
|
+
party,
|
|
91
|
+
address: party,
|
|
92
|
+
account: computed(() => state.value.account),
|
|
93
|
+
accounts: computed(() => state.value.accounts),
|
|
94
|
+
status: computed(() => state.value.status),
|
|
95
|
+
isConnected: computed(() => state.value.status === "connected"),
|
|
96
|
+
isConnecting: computed(() => state.value.status === "connecting"),
|
|
97
|
+
isReconnecting: computed(() => state.value.status === "reconnecting"),
|
|
98
|
+
isDisconnected: computed(() => state.value.status === "disconnected"),
|
|
99
|
+
networkId: computed(() => state.value.networkId),
|
|
100
|
+
chain: computed(() => state.value.networkId ? { id: state.value.networkId } : null),
|
|
101
|
+
lastError: computed(() => state.value.lastError)
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function useAccountEffect(parameters = {}) {
|
|
105
|
+
const store = injectSessionStore();
|
|
106
|
+
if (!store) return;
|
|
107
|
+
let prev = store.getSnapshot().status;
|
|
108
|
+
let firedConnect = false;
|
|
109
|
+
const unsubscribe = store.subscribe(() => {
|
|
110
|
+
const next = store.getSnapshot();
|
|
111
|
+
const was = prev;
|
|
112
|
+
const now = next.status;
|
|
113
|
+
prev = now;
|
|
114
|
+
if (now === "connected") {
|
|
115
|
+
if (!firedConnect && next.account) {
|
|
116
|
+
firedConnect = true;
|
|
117
|
+
parameters.onConnect?.({
|
|
118
|
+
account: next.account,
|
|
119
|
+
accounts: next.accounts,
|
|
120
|
+
networkId: next.networkId
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
} else if (now === "disconnected" && (was === "connected" || firedConnect)) {
|
|
124
|
+
firedConnect = false;
|
|
125
|
+
parameters.onDisconnect?.();
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
const unsubscribeParty = store.on("party:changed", (e) => {
|
|
129
|
+
if (e.type === "party:changed") {
|
|
130
|
+
parameters.onPartyChanged?.({ previous: e.previous, current: e.current });
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
onScopeDispose(() => {
|
|
134
|
+
unsubscribe();
|
|
135
|
+
unsubscribeParty();
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export { SESSION_STORE_KEY, createPartyLayerSession, injectSessionStore, provideSessionStore, useAccount, useAccountEffect, useSession };
|
|
140
|
+
//# sourceMappingURL=index.mjs.map
|
|
141
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/provide.ts","../src/composables.ts"],"names":["onScopeDispose"],"mappings":";;;;AAmBO,IAAM,iBAAA,0BAA8D,0BAA0B;AAOrG,SAAS,QAAQ,MAAA,EAAsD;AACrE,EAAA,OAAO,OAAQ,OAAwB,WAAA,KAAgB,UAAA;AACzD;AAGA,SAAS,aAAa,MAAA,EAAuE;AAC3F,EAAA,IAAI,OAAA,CAAQ,MAAM,CAAA,EAAG,OAAO,EAAE,KAAA,EAAO,MAAA,EAAQ,OAAO,KAAA,EAAM;AAC1D,EAAA,MAAM,EAAE,QAAA,EAAU,GAAG,OAAA,EAAQ,GAAI,MAAA;AACjC,EAAA,OAAO,EAAE,KAAA,EAAO,kBAAA,CAAmB,UAAU,OAAO,CAAA,EAAG,OAAO,IAAA,EAAK;AACrE;AASO,SAAS,oBAAoB,MAAA,EAA4C;AAC9E,EAAA,MAAM,EAAE,KAAA,EAAO,KAAA,EAAM,GAAI,aAAa,MAAM,CAAA;AAC5C,EAAA,OAAA,CAAQ,mBAAmB,KAAK,CAAA;AAChC,EAAA,IAAI,KAAA,EAAO;AAET,IAAA,SAAA,CAAU,MAAM;AACd,MAAA,KAAK,MAAM,IAAA,EAAK;AAAA,IAClB,CAAC,CAAA;AACD,IAAA,cAAA,CAAe,MAAM;AACnB,MAAA,KAAA,CAAM,OAAA,EAAQ;AAAA,IAChB,CAAC,CAAA;AAAA,EACH;AACA,EAAA,OAAO,KAAA;AACT;AAOO,SAAS,wBAAwB,MAAA,EAAsC;AAC5E,EAAA,OAAO;AAAA,IACL,QAAQ,GAAA,EAAU;AAChB,MAAA,MAAM,EAAE,KAAA,EAAO,KAAA,EAAM,GAAI,aAAa,MAAM,CAAA;AAC5C,MAAA,GAAA,CAAI,OAAA,CAAQ,mBAAmB,KAAK,CAAA;AACpC,MAAA,IAAI,KAAA,EAAO;AACT,QAAA,IAAI,OAAO,MAAA,KAAW,WAAA,EAAa,KAAK,MAAM,IAAA,EAAK;AACnD,QAAA,MAAM,OAAA,GAAU,GAAA,CAAI,OAAA,CAAQ,IAAA,CAAK,GAAG,CAAA;AACpC,QAAA,GAAA,CAAI,UAAU,MAAM;AAClB,UAAA,IAAI;AACF,YAAA,KAAA,CAAM,OAAA,EAAQ;AAAA,UAChB,CAAA,SAAE;AACA,YAAA,OAAA,EAAQ;AAAA,UACV;AAAA,QACF,CAAA;AAAA,MACF;AAAA,IACF;AAAA,GACF;AACF;AAGO,SAAS,kBAAA,GAA0C;AACxD,EAAA,OAAO,MAAA,CAAO,mBAAmB,IAAI,CAAA;AACvC;AClEA,IAAM,YAAA,GAA6B;AAAA,EACjC,MAAA,EAAQ,cAAA;AAAA,EACR,OAAA,EAAS,IAAA;AAAA,EACT,UAAU,EAAC;AAAA,EACX,SAAA,EAAW,IAAA;AAAA,EACX,SAAA,EAAW;AACb,CAAA;AAGA,SAAS,eAAA,GAAkB;AACzB,EAAA,MAAM,QAAQ,kBAAA,EAAmB;AACjC,EAAA,MAAM,QAAQ,UAAA,CAAyB,KAAA,GAAQ,KAAA,CAAM,WAAA,KAAgB,YAAY,CAAA;AACjF,EAAA,IAAI,KAAA,EAAO;AACT,IAAA,MAAM,WAAA,GAAc,KAAA,CAAM,SAAA,CAAU,MAAM;AACxC,MAAA,KAAA,CAAM,KAAA,GAAQ,MAAM,WAAA,EAAY;AAAA,IAClC,CAAC,CAAA;AACD,IAAAA,eAAe,WAAW,CAAA;AAAA,EAC5B;AACA,EAAA,OAAO,EAAE,OAAO,KAAA,EAAM;AACxB;AA6BO,SAAS,UAAA,GAA+B;AAC7C,EAAA,MAAM,EAAE,KAAA,EAAO,KAAA,EAAM,GAAI,eAAA,EAAgB;AACzC,EAAA,OAAO;AAAA,IACL,MAAA,EAAQ,QAAA,CAAS,MAAM,KAAA,CAAM,MAAM,MAAM,CAAA;AAAA,IACzC,OAAA,EAAS,QAAA,CAAS,MAAM,KAAA,CAAM,MAAM,OAAO,CAAA;AAAA,IAC3C,QAAA,EAAU,QAAA,CAAS,MAAM,KAAA,CAAM,MAAM,QAAQ,CAAA;AAAA,IAC7C,SAAA,EAAW,QAAA,CAAS,MAAM,KAAA,CAAM,MAAM,SAAS,CAAA;AAAA,IAC/C,SAAA,EAAW,QAAA,CAAS,MAAM,KAAA,CAAM,MAAM,SAAS,CAAA;AAAA,IAC/C,aAAa,QAAA,CAAS,MAAM,KAAA,CAAM,KAAA,CAAM,WAAW,WAAW,CAAA;AAAA,IAC9D,cAAc,QAAA,CAAS,MAAM,KAAA,CAAM,KAAA,CAAM,WAAW,YAAY,CAAA;AAAA,IAChE,gBAAgB,QAAA,CAAS,MAAM,KAAA,CAAM,KAAA,CAAM,WAAW,cAAc,CAAA;AAAA,IACpE,gBAAgB,QAAA,CAAS,MAAM,KAAA,CAAM,KAAA,CAAM,WAAW,cAAc,CAAA;AAAA,IACpE,OAAA,EAAS,CAAC,MAAA,KACR,KAAA,GAAQ,KAAA,CAAM,QAAQ,MAAM,CAAA,GAAI,OAAA,CAAQ,OAAA,CAAQ,YAAY,CAAA;AAAA,IAC9D,YAAY,MAAO,KAAA,GAAQ,MAAM,UAAA,EAAW,GAAI,QAAQ,OAAA,EAAQ;AAAA,IAChE,OAAA,EAAS,MAAO,KAAA,GAAQ,KAAA,CAAM,SAAQ,GAAI,OAAA,CAAQ,QAAQ,YAAY,CAAA;AAAA,IACtE,EAAA,EAAI,CACF,KAAA,EACA,OAAA,KACkB,KAAA,GAAQ,MAAM,EAAA,CAAG,KAAA,EAAO,OAAoC,CAAA,GAAI,MAAM;AAAA,IAAC;AAAA,GAC7F;AACF;AAmBO,SAAS,UAAA,GAA+B;AAC7C,EAAA,MAAM,EAAE,KAAA,EAAM,GAAI,eAAA,EAAgB;AAClC,EAAA,MAAM,QAAQ,QAAA,CAAS,MAAM,MAAM,KAAA,CAAM,OAAA,EAAS,WAAW,IAAI,CAAA;AACjE,EAAA,OAAO;AAAA,IACL,KAAA;AAAA,IACA,OAAA,EAAS,KAAA;AAAA,IACT,OAAA,EAAS,QAAA,CAAS,MAAM,KAAA,CAAM,MAAM,OAAO,CAAA;AAAA,IAC3C,QAAA,EAAU,QAAA,CAAS,MAAM,KAAA,CAAM,MAAM,QAAQ,CAAA;AAAA,IAC7C,MAAA,EAAQ,QAAA,CAAS,MAAM,KAAA,CAAM,MAAM,MAAM,CAAA;AAAA,IACzC,aAAa,QAAA,CAAS,MAAM,KAAA,CAAM,KAAA,CAAM,WAAW,WAAW,CAAA;AAAA,IAC9D,cAAc,QAAA,CAAS,MAAM,KAAA,CAAM,KAAA,CAAM,WAAW,YAAY,CAAA;AAAA,IAChE,gBAAgB,QAAA,CAAS,MAAM,KAAA,CAAM,KAAA,CAAM,WAAW,cAAc,CAAA;AAAA,IACpE,gBAAgB,QAAA,CAAS,MAAM,KAAA,CAAM,KAAA,CAAM,WAAW,cAAc,CAAA;AAAA,IACpE,SAAA,EAAW,QAAA,CAAS,MAAM,KAAA,CAAM,MAAM,SAAS,CAAA;AAAA,IAC/C,KAAA,EAAO,QAAA,CAAS,MAAO,KAAA,CAAM,KAAA,CAAM,SAAA,GAAY,EAAE,EAAA,EAAI,KAAA,CAAM,KAAA,CAAM,SAAA,EAAU,GAAI,IAAK,CAAA;AAAA,IACpF,SAAA,EAAW,QAAA,CAAS,MAAM,KAAA,CAAM,MAAM,SAAS;AAAA,GACjD;AACF;AAmBO,SAAS,gBAAA,CAAiB,UAAA,GAAyC,EAAC,EAAS;AAClF,EAAA,MAAM,QAAQ,kBAAA,EAAmB;AACjC,EAAA,IAAI,CAAC,KAAA,EAAO;AAEZ,EAAA,IAAI,IAAA,GAAsB,KAAA,CAAM,WAAA,EAAY,CAAE,MAAA;AAG9C,EAAA,IAAI,YAAA,GAAe,KAAA;AAEnB,EAAA,MAAM,WAAA,GAAc,KAAA,CAAM,SAAA,CAAU,MAAM;AACxC,IAAA,MAAM,IAAA,GAAO,MAAM,WAAA,EAAY;AAC/B,IAAA,MAAM,GAAA,GAAM,IAAA;AACZ,IAAA,MAAM,MAAM,IAAA,CAAK,MAAA;AACjB,IAAA,IAAA,GAAO,GAAA;AACP,IAAA,IAAI,QAAQ,WAAA,EAAa;AACvB,MAAA,IAAI,CAAC,YAAA,IAAgB,IAAA,CAAK,OAAA,EAAS;AACjC,QAAA,YAAA,GAAe,IAAA;AACf,QAAA,UAAA,CAAW,SAAA,GAAY;AAAA,UACrB,SAAS,IAAA,CAAK,OAAA;AAAA,UACd,UAAU,IAAA,CAAK,QAAA;AAAA,UACf,WAAW,IAAA,CAAK;AAAA,SACjB,CAAA;AAAA,MACH;AAAA,IACF,CAAA,MAAA,IAAW,GAAA,KAAQ,cAAA,KAAmB,GAAA,KAAQ,eAAe,YAAA,CAAA,EAAe;AAC1E,MAAA,YAAA,GAAe,KAAA;AACf,MAAA,UAAA,CAAW,YAAA,IAAe;AAAA,IAC5B;AAAA,EACF,CAAC,CAAA;AAED,EAAA,MAAM,gBAAA,GAAmB,KAAA,CAAM,EAAA,CAAG,eAAA,EAAiB,CAAC,CAAA,KAAM;AACxD,IAAA,IAAI,CAAA,CAAE,SAAS,eAAA,EAAiB;AAC9B,MAAA,UAAA,CAAW,cAAA,GAAiB,EAAE,QAAA,EAAU,CAAA,CAAE,UAAU,OAAA,EAAS,CAAA,CAAE,SAAS,CAAA;AAAA,IAC1E;AAAA,EACF,CAAC,CAAA;AAED,EAAAA,eAAe,MAAM;AACnB,IAAA,WAAA,EAAY;AACZ,IAAA,gBAAA,EAAiB;AAAA,EACnB,CAAC,CAAA;AACH","file":"index.mjs","sourcesContent":["/**\n * Session store provisioning for Vue — `provideSessionStore` (the core) and a\n * thin `createPartyLayerSession` plugin that wraps the same provide, so there is\n * a single source of truth (one injection key, one store-resolution path).\n *\n * Ownership rule:\n * - Built from config → THIS layer owns the lifecycle: `init()` runs\n * client-only (`onMounted`), `destroy()` on scope/app teardown.\n * - A PRE-BUILT store → its lifecycle belongs to the caller; we NEVER\n * `init()`/`destroy()` it.\n *\n * SSR-safe: the store is constructed without DOM access; `init()` is deferred to\n * `onMounted` (client only), so server rendering never touches the wallet.\n */\nimport { inject, onMounted, onScopeDispose, provide, type App, type InjectionKey, type Plugin } from 'vue';\nimport { createSessionStore, type SessionStore, type SessionStoreOptions } from '@partylayer/session';\nimport type { CIP0103Provider } from '@partylayer/core';\n\n/** Injection key for the shared session store (or `null` when none provided). */\nexport const SESSION_STORE_KEY: InjectionKey<SessionStore | null> = Symbol('partylayer.session.store');\n\n/** Either a pre-built store, or config to build one (provider + store options). */\nexport type ProvideSessionConfig =\n | SessionStore\n | ({ provider: CIP0103Provider } & Partial<SessionStoreOptions>);\n\nfunction isStore(config: ProvideSessionConfig): config is SessionStore {\n return typeof (config as SessionStore).getSnapshot === 'function';\n}\n\n/** Resolve a config into a store + whether THIS layer owns its lifecycle. */\nfunction resolveStore(config: ProvideSessionConfig): { store: SessionStore; owned: boolean } {\n if (isStore(config)) return { store: config, owned: false };\n const { provider, ...options } = config;\n return { store: createSessionStore(provider, options), owned: true };\n}\n\n/**\n * Provide a session store to descendant composables. Call in a component\n * `setup()` (e.g. your root). Returns the store for direct use if needed.\n *\n * When built from config, lifecycle is owned here (client-only `init()` on\n * mount, `destroy()` on scope teardown). A pre-built store is left untouched.\n */\nexport function provideSessionStore(config: ProvideSessionConfig): SessionStore {\n const { store, owned } = resolveStore(config);\n provide(SESSION_STORE_KEY, store);\n if (owned) {\n // Client-only restore/reconnect; never runs during SSR.\n onMounted(() => {\n void store.init();\n });\n onScopeDispose(() => {\n store.destroy();\n });\n }\n return store;\n}\n\n/**\n * Vue plugin form — a THIN wrapper over the same provide/resolution, for\n * `app.use(...)`. Owned stores `init()` on the client at install and `destroy()`\n * when the app unmounts.\n */\nexport function createPartyLayerSession(config: ProvideSessionConfig): Plugin {\n return {\n install(app: App) {\n const { store, owned } = resolveStore(config);\n app.provide(SESSION_STORE_KEY, store);\n if (owned) {\n if (typeof window !== 'undefined') void store.init(); // client only (SSR-safe)\n const unmount = app.unmount.bind(app);\n app.unmount = () => {\n try {\n store.destroy();\n } finally {\n unmount();\n }\n };\n }\n },\n };\n}\n\n/** Internal: read the provided store, or `null` if none (SSR / outside provider). */\nexport function injectSessionStore(): SessionStore | null {\n return inject(SESSION_STORE_KEY, null);\n}\n","/**\n * Vue composables over the `@partylayer/session` store. Wagmi/React parity:\n * `useSession` (reactive state + actions), `useAccount`, `useAccountEffect`.\n *\n * Each composable derives individual `ComputedRef`s from ONE internal\n * `shallowRef<SessionState>` fed by a single `store.subscribe`, cleaned up via\n * `onScopeDispose` (no leak after unmount). Returning refs — not a `reactive()`\n * object — means destructuring keeps reactivity.\n *\n * SSR-safe: with no provided store the refs report a disconnected session and\n * the actions are no-ops; nothing touches `window`.\n */\nimport { computed, onScopeDispose, shallowRef, type ComputedRef } from 'vue';\nimport type {\n SessionAccount,\n SessionEvent,\n SessionState,\n SessionStatus,\n} from '@partylayer/session';\nimport { injectSessionStore } from './provide';\n\nconst DISCONNECTED: SessionState = {\n status: 'disconnected',\n account: null,\n accounts: [],\n networkId: null,\n lastError: null,\n};\n\n/** Subscribe ONCE to the provided store; returns a reactive snapshot ref. */\nfunction useSessionState() {\n const store = injectSessionStore();\n const state = shallowRef<SessionState>(store ? store.getSnapshot() : DISCONNECTED);\n if (store) {\n const unsubscribe = store.subscribe(() => {\n state.value = store.getSnapshot();\n });\n onScopeDispose(unsubscribe);\n }\n return { store, state };\n}\n\n/** wagmi-parity chain handle derived from the CAIP-2 networkId. */\nexport interface SessionChain {\n id: string;\n}\n\nexport interface UseSessionReturn {\n status: ComputedRef<SessionStatus>;\n account: ComputedRef<SessionAccount | null>;\n accounts: ComputedRef<readonly SessionAccount[]>;\n networkId: ComputedRef<string | null>;\n lastError: ComputedRef<Error | null>;\n isConnected: ComputedRef<boolean>;\n isConnecting: ComputedRef<boolean>;\n isReconnecting: ComputedRef<boolean>;\n isDisconnected: ComputedRef<boolean>;\n /** Connect via the store (mirrors the store's own signature exactly). */\n connect(params?: Record<string, unknown>): Promise<SessionState>;\n disconnect(): Promise<void>;\n restore(): Promise<SessionState>;\n /** Subscribe to a structured resilience/sync event (narrowed by `event`). */\n on<T extends SessionEvent['type']>(\n event: T,\n handler: (event: Extract<SessionEvent, { type: T }>) => void,\n ): () => void;\n}\n\n/** Reactive session state + bound actions. */\nexport function useSession(): UseSessionReturn {\n const { store, state } = useSessionState();\n return {\n status: computed(() => state.value.status),\n account: computed(() => state.value.account),\n accounts: computed(() => state.value.accounts),\n networkId: computed(() => state.value.networkId),\n lastError: computed(() => state.value.lastError),\n isConnected: computed(() => state.value.status === 'connected'),\n isConnecting: computed(() => state.value.status === 'connecting'),\n isReconnecting: computed(() => state.value.status === 'reconnecting'),\n isDisconnected: computed(() => state.value.status === 'disconnected'),\n connect: (params?: Record<string, unknown>) =>\n store ? store.connect(params) : Promise.resolve(DISCONNECTED),\n disconnect: () => (store ? store.disconnect() : Promise.resolve()),\n restore: () => (store ? store.restore() : Promise.resolve(DISCONNECTED)),\n on: <T extends SessionEvent['type']>(\n event: T,\n handler: (event: Extract<SessionEvent, { type: T }>) => void,\n ): (() => void) => (store ? store.on(event, handler as (e: SessionEvent) => void) : () => {}),\n };\n}\n\nexport interface UseAccountReturn {\n party: ComputedRef<string | null>;\n /** wagmi-parity alias of `party`. */\n address: ComputedRef<string | null>;\n account: ComputedRef<SessionAccount | null>;\n accounts: ComputedRef<readonly SessionAccount[]>;\n status: ComputedRef<SessionStatus>;\n isConnected: ComputedRef<boolean>;\n isConnecting: ComputedRef<boolean>;\n isReconnecting: ComputedRef<boolean>;\n isDisconnected: ComputedRef<boolean>;\n networkId: ComputedRef<string | null>;\n chain: ComputedRef<SessionChain | null>;\n lastError: ComputedRef<Error | null>;\n}\n\n/** Reactive active-account view (wagmi parity). */\nexport function useAccount(): UseAccountReturn {\n const { state } = useSessionState();\n const party = computed(() => state.value.account?.partyId ?? null);\n return {\n party,\n address: party,\n account: computed(() => state.value.account),\n accounts: computed(() => state.value.accounts),\n status: computed(() => state.value.status),\n isConnected: computed(() => state.value.status === 'connected'),\n isConnecting: computed(() => state.value.status === 'connecting'),\n isReconnecting: computed(() => state.value.status === 'reconnecting'),\n isDisconnected: computed(() => state.value.status === 'disconnected'),\n networkId: computed(() => state.value.networkId),\n chain: computed(() => (state.value.networkId ? { id: state.value.networkId } : null)),\n lastError: computed(() => state.value.lastError),\n };\n}\n\nexport interface UseAccountEffectParameters {\n /** Fired on a transition INTO `connected` (once the account is available). */\n onConnect?: (data: {\n account: SessionAccount | null;\n accounts: readonly SessionAccount[];\n networkId: string | null;\n }) => void;\n /** Fired on a transition `connected → disconnected`. */\n onDisconnect?: () => void;\n /** Fired when the active PRIMARY party changes (a switch, not a reorder). */\n onPartyChanged?: (data: { previous: string | null; current: string | null }) => void;\n}\n\n/**\n * Fire side-effects on session transitions — no render churn. Auto-cleans on\n * scope teardown (`onScopeDispose`). No-op when no store is provided.\n */\nexport function useAccountEffect(parameters: UseAccountEffectParameters = {}): void {\n const store = injectSessionStore();\n if (!store) return;\n\n let prev: SessionStatus = store.getSnapshot().status;\n // statusChanged(connected) can arrive BEFORE accountsChanged, so fire\n // onConnect once per session on the tick the account becomes available.\n let firedConnect = false;\n\n const unsubscribe = store.subscribe(() => {\n const next = store.getSnapshot();\n const was = prev;\n const now = next.status;\n prev = now;\n if (now === 'connected') {\n if (!firedConnect && next.account) {\n firedConnect = true;\n parameters.onConnect?.({\n account: next.account,\n accounts: next.accounts,\n networkId: next.networkId,\n });\n }\n } else if (now === 'disconnected' && (was === 'connected' || firedConnect)) {\n firedConnect = false;\n parameters.onDisconnect?.();\n }\n });\n\n const unsubscribeParty = store.on('party:changed', (e) => {\n if (e.type === 'party:changed') {\n parameters.onPartyChanged?.({ previous: e.previous, current: e.current });\n }\n });\n\n onScopeDispose(() => {\n unsubscribe();\n unsubscribeParty();\n });\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@partylayer/vue",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Vue 3 composables for PartyLayer sessions",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"keywords": [
|
|
19
|
+
"canton",
|
|
20
|
+
"wallet",
|
|
21
|
+
"vue",
|
|
22
|
+
"composables"
|
|
23
|
+
],
|
|
24
|
+
"author": "PartyLayer Contributors",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "https://github.com/PartyLayer/PartyLayer.git",
|
|
29
|
+
"directory": "packages/vue"
|
|
30
|
+
},
|
|
31
|
+
"homepage": "https://partylayer.xyz",
|
|
32
|
+
"bugs": {
|
|
33
|
+
"url": "https://github.com/PartyLayer/PartyLayer/issues"
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"vue": ">=3.4.0"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@partylayer/core": "^0.5.0",
|
|
40
|
+
"@partylayer/session": "^1.0.0"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@types/node": "^20.11.0",
|
|
44
|
+
"@vue/test-utils": "^2.4.6",
|
|
45
|
+
"happy-dom": "^14.12.0",
|
|
46
|
+
"typescript": "^5.3.3",
|
|
47
|
+
"vitest": "^1.2.0",
|
|
48
|
+
"vue": "^3.4.0",
|
|
49
|
+
"@partylayer/testing": "^0.1.2"
|
|
50
|
+
},
|
|
51
|
+
"scripts": {
|
|
52
|
+
"build": "tsup",
|
|
53
|
+
"clean": "rm -rf dist",
|
|
54
|
+
"lint": "eslint src --ext .ts",
|
|
55
|
+
"typecheck": "tsc --noEmit",
|
|
56
|
+
"test": "vitest run --passWithNoTests"
|
|
57
|
+
}
|
|
58
|
+
}
|