@lyeve-labs/client-svelte 0.1.2

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 LyEve Labs
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,207 @@
1
+ # @lyeve/cms-client-svelte
2
+
3
+ Svelte 5 reactive stores for the LyEve Core API. Thin wrapper around
4
+ `@lyeve/cms-client` using Svelte 5 runes (`$state`).
5
+
6
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
7
+ [![Svelte 5](https://img.shields.io/badge/svelte-5-ff3e00.svg)](https://svelte.dev)
8
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.7-3178c6.svg)](https://www.typescriptlang.org)
9
+
10
+ ```bash
11
+ pnpm add @lyeve/cms-client @lyeve/cms-client-svelte
12
+ ```
13
+
14
+ ```svelte
15
+ <script lang="ts">
16
+ import { createCmsClient, createAsyncStore } from '@lyeve/cms-client-svelte';
17
+ import { getSchemas } from '@lyeve/cms-client-rest';
18
+
19
+ const client = createCmsClient({
20
+ baseUrl: 'https://cms.example.com',
21
+ getHeaders: () => ({ Authorization: `Bearer ${token}` }),
22
+ });
23
+
24
+ const schemas = createAsyncStore((client) => getSchemas(client), client);
25
+ </script>
26
+
27
+ {#if schemas.loading}
28
+ <p>Loading...</p>
29
+ {:else if schemas.error}
30
+ <p>Error: {schemas.error.message}</p>
31
+ {:else}
32
+ <ul>
33
+ {#each schemas.data ?? [] as schema}
34
+ <li>{schema.name}</li>
35
+ {/each}
36
+ </ul>
37
+ {/if}
38
+ ```
39
+
40
+ No Provider needed. Create a client, pass it in, done.
41
+
42
+ ---
43
+
44
+ ## What's in the box
45
+
46
+ - **createCmsClient:** factory that returns a configured `HttpClient` with
47
+ base URL and dynamic headers.
48
+ - **createAsyncStore:** reactive async data store built on `$state`. Returns
49
+ `{ data, error, loading, refetch }`. All reactive, no boilerplate.
50
+ - **createAuthStore:** auth state store with reactive `user`, `token`, and
51
+ `isAuthenticated`. Methods to `setUser`, `clear`, and `load` from storage.
52
+ - **Svelte 5 native:** built on runes. No legacy stores, no `writable`.
53
+
54
+ ## Requirements
55
+
56
+ - **Node 20** or newer
57
+ - **Svelte 5.0** or newer
58
+ - **[@lyeve/cms-client](https://www.npmjs.com/package/@lyeve/cms-client)** `>=0.1.0`
59
+
60
+ ## Install
61
+
62
+ ```bash
63
+ pnpm add @lyeve/cms-client @lyeve/cms-client-svelte
64
+ # or npm install @lyeve/cms-client @lyeve/cms-client-svelte
65
+ # or yarn add @lyeve/cms-client @lyeve/cms-client-svelte
66
+ ```
67
+
68
+ ## Use
69
+
70
+ ### Data fetching
71
+
72
+ ```svelte
73
+ <script lang="ts">
74
+ import { createCmsClient, createAsyncStore } from '@lyeve/cms-client-svelte';
75
+ import { getSchemas } from '@lyeve/cms-client-rest';
76
+
77
+ const client = createCmsClient({
78
+ baseUrl: 'https://cms.example.com',
79
+ getHeaders: () => ({ Authorization: `Bearer ${token}` }),
80
+ });
81
+
82
+ const schemas = createAsyncStore((c) => getSchemas(c), client);
83
+ </script>
84
+
85
+ {#if schemas.loading}
86
+ <p>Loading...</p>
87
+ {:else if schemas.error}
88
+ <p>Error: {schemas.error.message}</p>
89
+ {:else}
90
+ <ul>
91
+ {#each schemas.data ?? [] as schema}
92
+ <li>{schema.name}</li>
93
+ {/each}
94
+ </ul>
95
+ {/if}
96
+
97
+ <button onclick={() => schemas.refetch()}>Reload</button>
98
+ ```
99
+
100
+ ### Authentication
101
+
102
+ ```svelte
103
+ <script lang="ts">
104
+ import { createCmsClient, createAuthStore } from '@lyeve/cms-client-svelte';
105
+
106
+ const client = createCmsClient({ baseUrl: 'https://cms.example.com' });
107
+ const auth = createAuthStore(client);
108
+
109
+ function handleLogin(e: SubmitEvent) {
110
+ e.preventDefault();
111
+ const data = new FormData(e.target as HTMLFormElement);
112
+ const credentials = {
113
+ email: data.get('email') as string,
114
+ password: data.get('password') as string,
115
+ };
116
+ // Call your auth endpoint, then store the result:
117
+ const user = { id: '1', email: credentials.email, roles: ['admin'] };
118
+ auth.setUser(user, 'jwt-token');
119
+ }
120
+ </script>
121
+
122
+ {#if !auth.isAuthenticated}
123
+ <form onsubmit={handleLogin}>
124
+ <input type="email" name="email" required />
125
+ <input type="password" name="password" required />
126
+ <button type="submit">Log in</button>
127
+ </form>
128
+ {:else}
129
+ <p>Welcome, {auth.user?.email}</p>
130
+ <button onclick={() => auth.clear()}>Log out</button>
131
+ {/if}
132
+ ```
133
+
134
+ ## API
135
+
136
+ ### createCmsClient(config)
137
+
138
+ ```ts
139
+ function createCmsClient(config: {
140
+ baseUrl?: string;
141
+ getHeaders?: () => Record<string, string>;
142
+ }): HttpClient;
143
+ ```
144
+
145
+ ### createAsyncStore(fetcher, client)
146
+
147
+ ```ts
148
+ function createAsyncStore<T>(
149
+ fetcher: (client: HttpClient) => Promise<T>,
150
+ client: HttpClient,
151
+ ): {
152
+ data: T | null;
153
+ error: Error | null;
154
+ loading: boolean;
155
+ refetch: () => void;
156
+ };
157
+ ```
158
+
159
+ All fields are reactive (`$state` rune).
160
+
161
+ ### createAuthStore(client)
162
+
163
+ ```ts
164
+ function createAuthStore(client: HttpClient): {
165
+ user: { id: string; email: string; roles: string[] } | null;
166
+ token: string | null;
167
+ isAuthenticated: boolean;
168
+ setUser: (user: User, token: string) => void;
169
+ clear: () => void;
170
+ load: () => Promise<void>;
171
+ };
172
+ ```
173
+
174
+ ## Local development
175
+
176
+ ```bash
177
+ pnpm install # install dependencies
178
+ pnpm test # run unit tests
179
+ pnpm check # type-check
180
+ pnpm build # tsup + publint -> dist/
181
+ ```
182
+
183
+ ## Project layout
184
+
185
+ ```
186
+ src/
187
+ index.ts # public API (re-exports)
188
+ index.svelte.ts # Svelte 5 entry point
189
+ runes.ts # createAsyncStore, createAuthStore
190
+ ambient.d.ts # type declarations
191
+ tests/ # vitest test suite
192
+ ```
193
+
194
+ ## Versioning
195
+
196
+ `@lyeve/cms-client-svelte` follows [SemVer](https://semver.org). While under `1.0`,
197
+ breaking changes bump the **minor** version; additive changes bump the **patch**.
198
+ Every release is logged in [`CHANGELOG.md`](CHANGELOG.md).
199
+
200
+ ## Contributing
201
+
202
+ Bug reports and feature requests are welcome. See
203
+ [`CONTRIBUTING.md`](CONTRIBUTING.md) for the development setup and conventions.
204
+
205
+ ## License
206
+
207
+ MIT. See [`LICENSE`](LICENSE).
package/dist/index.cjs ADDED
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ createAsyncStore: () => createAsyncStore,
24
+ createAuthStore: () => createAuthStore,
25
+ createCmsClient: () => createCmsClient,
26
+ createMutation: () => createMutation
27
+ });
28
+ module.exports = __toCommonJS(src_exports);
29
+
30
+ // src/runes.ts
31
+ var import_cms_client = require("@lyeve/cms-client");
32
+ function createCmsClient(config) {
33
+ const base = config.baseUrl ?? "";
34
+ return (0, import_cms_client.createClient)((url, init) => {
35
+ const fullUrl = typeof url === "string" ? `${base}${url}` : url;
36
+ return fetch(fullUrl, {
37
+ ...init,
38
+ headers: { ...init?.headers, ...config.getHeaders?.() }
39
+ });
40
+ });
41
+ }
42
+ function createAsyncStore(fetcher, client) {
43
+ let data = $state(null);
44
+ let error = $state(null);
45
+ let loading = $state(true);
46
+ async function fetch2() {
47
+ loading = true;
48
+ try {
49
+ data = await fetcher(client);
50
+ error = null;
51
+ } catch (e) {
52
+ error = e;
53
+ } finally {
54
+ loading = false;
55
+ }
56
+ }
57
+ fetch2();
58
+ return {
59
+ get data() {
60
+ return data;
61
+ },
62
+ get error() {
63
+ return error;
64
+ },
65
+ get loading() {
66
+ return loading;
67
+ },
68
+ refetch: fetch2
69
+ };
70
+ }
71
+ function createMutation(mutator, client) {
72
+ let data = $state(null);
73
+ let error = $state(null);
74
+ let loading = $state(false);
75
+ async function run(vars) {
76
+ loading = true;
77
+ error = null;
78
+ try {
79
+ const result = await mutator(client, vars);
80
+ data = result;
81
+ return result;
82
+ } catch (e) {
83
+ error = e;
84
+ throw e;
85
+ } finally {
86
+ loading = false;
87
+ }
88
+ }
89
+ return {
90
+ get data() {
91
+ return data;
92
+ },
93
+ get error() {
94
+ return error;
95
+ },
96
+ get loading() {
97
+ return loading;
98
+ },
99
+ run
100
+ };
101
+ }
102
+ function createAuthStore(client) {
103
+ let state = $state({ user: null, token: null });
104
+ return {
105
+ get user() {
106
+ return state.user;
107
+ },
108
+ get token() {
109
+ return state.token;
110
+ },
111
+ get isAuthenticated() {
112
+ return state.token !== null;
113
+ },
114
+ setUser(user, token) {
115
+ state = { user, token };
116
+ },
117
+ clear() {
118
+ state = { user: null, token: null };
119
+ },
120
+ async load() {
121
+ try {
122
+ const user = await client.get("/api/admin/auth/me");
123
+ state = { ...state, user };
124
+ } catch {
125
+ }
126
+ }
127
+ };
128
+ }
129
+ // Annotate the CommonJS export names for ESM import in node:
130
+ 0 && (module.exports = {
131
+ createAsyncStore,
132
+ createAuthStore,
133
+ createCmsClient,
134
+ createMutation
135
+ });
136
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/runes.ts"],"sourcesContent":["// Re-exports for non-Svelte consumers (bundled by tsup).\n// Implementation lives in runes.svelte.ts so Svelte's compiler handles\n// $state runes natively via the \"svelte\" export condition.\nexport {\n\tcreateCmsClient,\n\tcreateAsyncStore,\n\tcreateMutation,\n\tcreateAuthStore,\n\ttype SvelteCmsConfig,\n\ttype AsyncStore,\n\ttype AuthState,\n\ttype AuthStore,\n} from './runes.js';\n","import { createClient, type HttpClient } from '@lyeve/cms-client';\n\n// Client factory\n\nexport interface SvelteCmsConfig {\n\t/** Base URL prepended to every request path. */\n\tbaseUrl?: string;\n\t/**\n\t * Callback returning headers added to every request.\n\t * Called on every request so auth tokens can be refreshed without\n\t * recreating the client.\n\t */\n\tgetHeaders?: () => Record<string, string>;\n}\n\n/**\n * Creates an HttpClient pre-configured with base URL and dynamic request\n * headers. No Provider needed - Svelte callers just pass the client around.\n *\n * @example\n * ```ts\n * const client = createCmsClient({\n * baseUrl: 'https://cms.example.com',\n * getHeaders: () => ({ Authorization: `Bearer ${token}` }),\n * });\n * ```\n */\nexport function createCmsClient(config: SvelteCmsConfig): HttpClient {\n\tconst base = config.baseUrl ?? '';\n\treturn createClient((url, init) => {\n\t\tconst fullUrl = typeof url === 'string' ? `${base}${url}` : url;\n\t\treturn fetch(fullUrl, {\n\t\t\t...init,\n\t\t\theaders: { ...init?.headers, ...config.getHeaders?.() },\n\t\t});\n\t});\n}\n\n// Generic async store\n\nexport interface AsyncStore<T> {\n\treadonly data: T | null;\n\treadonly error: Error | null;\n\treadonly loading: boolean;\n\trefetch: () => void;\n}\n\n/**\n * Reactive async data store using the $state rune.\n * Fires the fetcher immediately and exposes reactive loading/data/error\n * getters that Svelte components can read directly in markup.\n *\n * @example\n * ```svelte\n * <script lang=\"ts\">\n * const schemas = createAsyncStore((c) => getSchemas(c), client);\n * </script>\n * {#if schemas.loading}...{/if}\n * ```\n */\nexport function createAsyncStore<T>(\n\tfetcher: (client: HttpClient) => Promise<T>,\n\tclient: HttpClient,\n): AsyncStore<T> {\n\tlet data = $state<T | null>(null);\n\tlet error = $state<Error | null>(null);\n\tlet loading = $state(true);\n\n\tasync function fetch() {\n\t\tloading = true;\n\t\ttry {\n\t\t\tdata = await fetcher(client);\n\t\t\terror = null;\n\t\t} catch (e) {\n\t\t\terror = e as Error;\n\t\t} finally {\n\t\t\tloading = false;\n\t\t}\n\t}\n\n\tfetch();\n\n\treturn {\n\t\tget data() { return data; },\n\t\tget error() { return error; },\n\t\tget loading() { return loading; },\n\t\trefetch: fetch,\n\t};\n}\n\n/**\n * Reactive mutation primitive using the $state rune.\n * Returns data/error/loading state and a run function that triggers the\n * mutation. Unlike createAsyncStore, the mutation does not fire immediately.\n *\n * @example\n * ```svelte\n * <script lang=\"ts\">\n * const createArticle = createMutation(\n * (c, vars: { title: string }) => createArticle(c, vars),\n * client,\n * );\n * </script>\n * <button onclick={() => createArticle.run({ title: 'Hello' })}>\n * {createArticle.loading ? 'Saving...' : 'Create'}\n * </button>\n * ```\n */\nexport function createMutation<T, V>(\n\tmutator: (client: HttpClient, vars: V) => Promise<T>,\n\tclient: HttpClient,\n) {\n\tlet data = $state<T | null>(null);\n\tlet error = $state<Error | null>(null);\n\tlet loading = $state(false);\n\n\tasync function run(vars: V): Promise<T> {\n\t\tloading = true;\n\t\terror = null;\n\t\ttry {\n\t\t\tconst result = await mutator(client, vars);\n\t\t\tdata = result;\n\t\t\treturn result;\n\t\t} catch (e) {\n\t\t\terror = e as Error;\n\t\t\tthrow e;\n\t\t} finally {\n\t\t\tloading = false;\n\t\t}\n\t}\n\n\treturn {\n\t\tget data() { return data; },\n\t\tget error() { return error; },\n\t\tget loading() { return loading; },\n\t\trun,\n\t};\n}\n\n// Auth store\n\nexport interface AuthState {\n\tuser: { id: string; email: string; roles: string[] } | null;\n\ttoken: string | null;\n}\n\nexport interface AuthStore {\n\treadonly user: AuthState['user'];\n\treadonly token: string | null;\n\treadonly isAuthenticated: boolean;\n\t/** Set the current user and token after a successful login. */\n\tsetUser: (user: AuthState['user'], token: string | null) => void;\n\t/** Clear auth state (e.g. after logout). */\n\tclear: () => void;\n\t/** Try to load the current user from the server using the stored token. */\n\tload: () => Promise<void>;\n}\n\n/**\n * Simple reactive auth store. The caller is responsible for calling\n * {@link AuthStore.setUser} after login and {@link AuthStore.clear} after\n * logout. Use {@link AuthStore.load} on app start to restore a session\n * from an existing cookie.\n */\nexport function createAuthStore(client: HttpClient): AuthStore {\n\tlet state = $state<AuthState>({ user: null, token: null });\n\n\treturn {\n\t\tget user() { return state.user; },\n\t\tget token() { return state.token; },\n\t\tget isAuthenticated() { return state.token !== null; },\n\t\tsetUser(user: AuthState['user'], token: string | null) {\n\t\t\tstate = { user, token };\n\t\t},\n\t\tclear() {\n\t\t\tstate = { user: null, token: null };\n\t\t},\n\t\tasync load() {\n\t\t\ttry {\n\t\t\t\tconst user = await client.get<{ id: string; email: string; roles: string[] }>('/api/admin/auth/me');\n\t\t\t\tstate = { ...state, user };\n\t\t\t} catch { /* not logged in */ }\n\t\t},\n\t};\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,wBAA8C;AA2BvC,SAAS,gBAAgB,QAAqC;AACpE,QAAM,OAAO,OAAO,WAAW;AAC/B,aAAO,gCAAa,CAAC,KAAK,SAAS;AAClC,UAAM,UAAU,OAAO,QAAQ,WAAW,GAAG,IAAI,GAAG,GAAG,KAAK;AAC5D,WAAO,MAAM,SAAS;AAAA,MACrB,GAAG;AAAA,MACH,SAAS,EAAE,GAAG,MAAM,SAAS,GAAG,OAAO,aAAa,EAAE;AAAA,IACvD,CAAC;AAAA,EACF,CAAC;AACF;AAwBO,SAAS,iBACf,SACA,QACgB;AAChB,MAAI,OAAO,OAAiB,IAAI;AAChC,MAAI,QAAQ,OAAqB,IAAI;AACrC,MAAI,UAAU,OAAO,IAAI;AAEzB,iBAAeA,SAAQ;AACtB,cAAU;AACV,QAAI;AACH,aAAO,MAAM,QAAQ,MAAM;AAC3B,cAAQ;AAAA,IACT,SAAS,GAAG;AACX,cAAQ;AAAA,IACT,UAAE;AACD,gBAAU;AAAA,IACX;AAAA,EACD;AAEA,EAAAA,OAAM;AAEN,SAAO;AAAA,IACN,IAAI,OAAO;AAAE,aAAO;AAAA,IAAM;AAAA,IAC1B,IAAI,QAAQ;AAAE,aAAO;AAAA,IAAO;AAAA,IAC5B,IAAI,UAAU;AAAE,aAAO;AAAA,IAAS;AAAA,IAChC,SAASA;AAAA,EACV;AACD;AAoBO,SAAS,eACf,SACA,QACC;AACD,MAAI,OAAO,OAAiB,IAAI;AAChC,MAAI,QAAQ,OAAqB,IAAI;AACrC,MAAI,UAAU,OAAO,KAAK;AAE1B,iBAAe,IAAI,MAAqB;AACvC,cAAU;AACV,YAAQ;AACR,QAAI;AACH,YAAM,SAAS,MAAM,QAAQ,QAAQ,IAAI;AACzC,aAAO;AACP,aAAO;AAAA,IACR,SAAS,GAAG;AACX,cAAQ;AACR,YAAM;AAAA,IACP,UAAE;AACD,gBAAU;AAAA,IACX;AAAA,EACD;AAEA,SAAO;AAAA,IACN,IAAI,OAAO;AAAE,aAAO;AAAA,IAAM;AAAA,IAC1B,IAAI,QAAQ;AAAE,aAAO;AAAA,IAAO;AAAA,IAC5B,IAAI,UAAU;AAAE,aAAO;AAAA,IAAS;AAAA,IAChC;AAAA,EACD;AACD;AA2BO,SAAS,gBAAgB,QAA+B;AAC9D,MAAI,QAAQ,OAAkB,EAAE,MAAM,MAAM,OAAO,KAAK,CAAC;AAEzD,SAAO;AAAA,IACN,IAAI,OAAO;AAAE,aAAO,MAAM;AAAA,IAAM;AAAA,IAChC,IAAI,QAAQ;AAAE,aAAO,MAAM;AAAA,IAAO;AAAA,IAClC,IAAI,kBAAkB;AAAE,aAAO,MAAM,UAAU;AAAA,IAAM;AAAA,IACrD,QAAQ,MAAyB,OAAsB;AACtD,cAAQ,EAAE,MAAM,MAAM;AAAA,IACvB;AAAA,IACA,QAAQ;AACP,cAAQ,EAAE,MAAM,MAAM,OAAO,KAAK;AAAA,IACnC;AAAA,IACA,MAAM,OAAO;AACZ,UAAI;AACH,cAAM,OAAO,MAAM,OAAO,IAAoD,oBAAoB;AAClG,gBAAQ,EAAE,GAAG,OAAO,KAAK;AAAA,MAC1B,QAAQ;AAAA,MAAsB;AAAA,IAC/B;AAAA,EACD;AACD;","names":["fetch"]}
@@ -0,0 +1,97 @@
1
+ import { HttpClient } from '@lyeve/cms-client';
2
+
3
+ interface SvelteCmsConfig {
4
+ /** Base URL prepended to every request path. */
5
+ baseUrl?: string;
6
+ /**
7
+ * Callback returning headers added to every request.
8
+ * Called on every request so auth tokens can be refreshed without
9
+ * recreating the client.
10
+ */
11
+ getHeaders?: () => Record<string, string>;
12
+ }
13
+ /**
14
+ * Creates an HttpClient pre-configured with base URL and dynamic request
15
+ * headers. No Provider needed - Svelte callers just pass the client around.
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * const client = createCmsClient({
20
+ * baseUrl: 'https://cms.example.com',
21
+ * getHeaders: () => ({ Authorization: `Bearer ${token}` }),
22
+ * });
23
+ * ```
24
+ */
25
+ declare function createCmsClient(config: SvelteCmsConfig): HttpClient;
26
+ interface AsyncStore<T> {
27
+ readonly data: T | null;
28
+ readonly error: Error | null;
29
+ readonly loading: boolean;
30
+ refetch: () => void;
31
+ }
32
+ /**
33
+ * Reactive async data store using the $state rune.
34
+ * Fires the fetcher immediately and exposes reactive loading/data/error
35
+ * getters that Svelte components can read directly in markup.
36
+ *
37
+ * @example
38
+ * ```svelte
39
+ * <script lang="ts">
40
+ * const schemas = createAsyncStore((c) => getSchemas(c), client);
41
+ * </script>
42
+ * {#if schemas.loading}...{/if}
43
+ * ```
44
+ */
45
+ declare function createAsyncStore<T>(fetcher: (client: HttpClient) => Promise<T>, client: HttpClient): AsyncStore<T>;
46
+ /**
47
+ * Reactive mutation primitive using the $state rune.
48
+ * Returns data/error/loading state and a run function that triggers the
49
+ * mutation. Unlike createAsyncStore, the mutation does not fire immediately.
50
+ *
51
+ * @example
52
+ * ```svelte
53
+ * <script lang="ts">
54
+ * const createArticle = createMutation(
55
+ * (c, vars: { title: string }) => createArticle(c, vars),
56
+ * client,
57
+ * );
58
+ * </script>
59
+ * <button onclick={() => createArticle.run({ title: 'Hello' })}>
60
+ * {createArticle.loading ? 'Saving...' : 'Create'}
61
+ * </button>
62
+ * ```
63
+ */
64
+ declare function createMutation<T, V>(mutator: (client: HttpClient, vars: V) => Promise<T>, client: HttpClient): {
65
+ readonly data: T | null;
66
+ readonly error: Error | null;
67
+ readonly loading: boolean;
68
+ run: (vars: V) => Promise<T>;
69
+ };
70
+ interface AuthState {
71
+ user: {
72
+ id: string;
73
+ email: string;
74
+ roles: string[];
75
+ } | null;
76
+ token: string | null;
77
+ }
78
+ interface AuthStore {
79
+ readonly user: AuthState['user'];
80
+ readonly token: string | null;
81
+ readonly isAuthenticated: boolean;
82
+ /** Set the current user and token after a successful login. */
83
+ setUser: (user: AuthState['user'], token: string | null) => void;
84
+ /** Clear auth state (e.g. after logout). */
85
+ clear: () => void;
86
+ /** Try to load the current user from the server using the stored token. */
87
+ load: () => Promise<void>;
88
+ }
89
+ /**
90
+ * Simple reactive auth store. The caller is responsible for calling
91
+ * {@link AuthStore.setUser} after login and {@link AuthStore.clear} after
92
+ * logout. Use {@link AuthStore.load} on app start to restore a session
93
+ * from an existing cookie.
94
+ */
95
+ declare function createAuthStore(client: HttpClient): AuthStore;
96
+
97
+ export { type AsyncStore, type AuthState, type AuthStore, type SvelteCmsConfig, createAsyncStore, createAuthStore, createCmsClient, createMutation };
@@ -0,0 +1,97 @@
1
+ import { HttpClient } from '@lyeve/cms-client';
2
+
3
+ interface SvelteCmsConfig {
4
+ /** Base URL prepended to every request path. */
5
+ baseUrl?: string;
6
+ /**
7
+ * Callback returning headers added to every request.
8
+ * Called on every request so auth tokens can be refreshed without
9
+ * recreating the client.
10
+ */
11
+ getHeaders?: () => Record<string, string>;
12
+ }
13
+ /**
14
+ * Creates an HttpClient pre-configured with base URL and dynamic request
15
+ * headers. No Provider needed - Svelte callers just pass the client around.
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * const client = createCmsClient({
20
+ * baseUrl: 'https://cms.example.com',
21
+ * getHeaders: () => ({ Authorization: `Bearer ${token}` }),
22
+ * });
23
+ * ```
24
+ */
25
+ declare function createCmsClient(config: SvelteCmsConfig): HttpClient;
26
+ interface AsyncStore<T> {
27
+ readonly data: T | null;
28
+ readonly error: Error | null;
29
+ readonly loading: boolean;
30
+ refetch: () => void;
31
+ }
32
+ /**
33
+ * Reactive async data store using the $state rune.
34
+ * Fires the fetcher immediately and exposes reactive loading/data/error
35
+ * getters that Svelte components can read directly in markup.
36
+ *
37
+ * @example
38
+ * ```svelte
39
+ * <script lang="ts">
40
+ * const schemas = createAsyncStore((c) => getSchemas(c), client);
41
+ * </script>
42
+ * {#if schemas.loading}...{/if}
43
+ * ```
44
+ */
45
+ declare function createAsyncStore<T>(fetcher: (client: HttpClient) => Promise<T>, client: HttpClient): AsyncStore<T>;
46
+ /**
47
+ * Reactive mutation primitive using the $state rune.
48
+ * Returns data/error/loading state and a run function that triggers the
49
+ * mutation. Unlike createAsyncStore, the mutation does not fire immediately.
50
+ *
51
+ * @example
52
+ * ```svelte
53
+ * <script lang="ts">
54
+ * const createArticle = createMutation(
55
+ * (c, vars: { title: string }) => createArticle(c, vars),
56
+ * client,
57
+ * );
58
+ * </script>
59
+ * <button onclick={() => createArticle.run({ title: 'Hello' })}>
60
+ * {createArticle.loading ? 'Saving...' : 'Create'}
61
+ * </button>
62
+ * ```
63
+ */
64
+ declare function createMutation<T, V>(mutator: (client: HttpClient, vars: V) => Promise<T>, client: HttpClient): {
65
+ readonly data: T | null;
66
+ readonly error: Error | null;
67
+ readonly loading: boolean;
68
+ run: (vars: V) => Promise<T>;
69
+ };
70
+ interface AuthState {
71
+ user: {
72
+ id: string;
73
+ email: string;
74
+ roles: string[];
75
+ } | null;
76
+ token: string | null;
77
+ }
78
+ interface AuthStore {
79
+ readonly user: AuthState['user'];
80
+ readonly token: string | null;
81
+ readonly isAuthenticated: boolean;
82
+ /** Set the current user and token after a successful login. */
83
+ setUser: (user: AuthState['user'], token: string | null) => void;
84
+ /** Clear auth state (e.g. after logout). */
85
+ clear: () => void;
86
+ /** Try to load the current user from the server using the stored token. */
87
+ load: () => Promise<void>;
88
+ }
89
+ /**
90
+ * Simple reactive auth store. The caller is responsible for calling
91
+ * {@link AuthStore.setUser} after login and {@link AuthStore.clear} after
92
+ * logout. Use {@link AuthStore.load} on app start to restore a session
93
+ * from an existing cookie.
94
+ */
95
+ declare function createAuthStore(client: HttpClient): AuthStore;
96
+
97
+ export { type AsyncStore, type AuthState, type AuthStore, type SvelteCmsConfig, createAsyncStore, createAuthStore, createCmsClient, createMutation };
package/dist/index.js ADDED
@@ -0,0 +1,106 @@
1
+ // src/runes.ts
2
+ import { createClient } from "@lyeve/cms-client";
3
+ function createCmsClient(config) {
4
+ const base = config.baseUrl ?? "";
5
+ return createClient((url, init) => {
6
+ const fullUrl = typeof url === "string" ? `${base}${url}` : url;
7
+ return fetch(fullUrl, {
8
+ ...init,
9
+ headers: { ...init?.headers, ...config.getHeaders?.() }
10
+ });
11
+ });
12
+ }
13
+ function createAsyncStore(fetcher, client) {
14
+ let data = $state(null);
15
+ let error = $state(null);
16
+ let loading = $state(true);
17
+ async function fetch2() {
18
+ loading = true;
19
+ try {
20
+ data = await fetcher(client);
21
+ error = null;
22
+ } catch (e) {
23
+ error = e;
24
+ } finally {
25
+ loading = false;
26
+ }
27
+ }
28
+ fetch2();
29
+ return {
30
+ get data() {
31
+ return data;
32
+ },
33
+ get error() {
34
+ return error;
35
+ },
36
+ get loading() {
37
+ return loading;
38
+ },
39
+ refetch: fetch2
40
+ };
41
+ }
42
+ function createMutation(mutator, client) {
43
+ let data = $state(null);
44
+ let error = $state(null);
45
+ let loading = $state(false);
46
+ async function run(vars) {
47
+ loading = true;
48
+ error = null;
49
+ try {
50
+ const result = await mutator(client, vars);
51
+ data = result;
52
+ return result;
53
+ } catch (e) {
54
+ error = e;
55
+ throw e;
56
+ } finally {
57
+ loading = false;
58
+ }
59
+ }
60
+ return {
61
+ get data() {
62
+ return data;
63
+ },
64
+ get error() {
65
+ return error;
66
+ },
67
+ get loading() {
68
+ return loading;
69
+ },
70
+ run
71
+ };
72
+ }
73
+ function createAuthStore(client) {
74
+ let state = $state({ user: null, token: null });
75
+ return {
76
+ get user() {
77
+ return state.user;
78
+ },
79
+ get token() {
80
+ return state.token;
81
+ },
82
+ get isAuthenticated() {
83
+ return state.token !== null;
84
+ },
85
+ setUser(user, token) {
86
+ state = { user, token };
87
+ },
88
+ clear() {
89
+ state = { user: null, token: null };
90
+ },
91
+ async load() {
92
+ try {
93
+ const user = await client.get("/api/admin/auth/me");
94
+ state = { ...state, user };
95
+ } catch {
96
+ }
97
+ }
98
+ };
99
+ }
100
+ export {
101
+ createAsyncStore,
102
+ createAuthStore,
103
+ createCmsClient,
104
+ createMutation
105
+ };
106
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/runes.ts"],"sourcesContent":["import { createClient, type HttpClient } from '@lyeve/cms-client';\n\n// Client factory\n\nexport interface SvelteCmsConfig {\n\t/** Base URL prepended to every request path. */\n\tbaseUrl?: string;\n\t/**\n\t * Callback returning headers added to every request.\n\t * Called on every request so auth tokens can be refreshed without\n\t * recreating the client.\n\t */\n\tgetHeaders?: () => Record<string, string>;\n}\n\n/**\n * Creates an HttpClient pre-configured with base URL and dynamic request\n * headers. No Provider needed - Svelte callers just pass the client around.\n *\n * @example\n * ```ts\n * const client = createCmsClient({\n * baseUrl: 'https://cms.example.com',\n * getHeaders: () => ({ Authorization: `Bearer ${token}` }),\n * });\n * ```\n */\nexport function createCmsClient(config: SvelteCmsConfig): HttpClient {\n\tconst base = config.baseUrl ?? '';\n\treturn createClient((url, init) => {\n\t\tconst fullUrl = typeof url === 'string' ? `${base}${url}` : url;\n\t\treturn fetch(fullUrl, {\n\t\t\t...init,\n\t\t\theaders: { ...init?.headers, ...config.getHeaders?.() },\n\t\t});\n\t});\n}\n\n// Generic async store\n\nexport interface AsyncStore<T> {\n\treadonly data: T | null;\n\treadonly error: Error | null;\n\treadonly loading: boolean;\n\trefetch: () => void;\n}\n\n/**\n * Reactive async data store using the $state rune.\n * Fires the fetcher immediately and exposes reactive loading/data/error\n * getters that Svelte components can read directly in markup.\n *\n * @example\n * ```svelte\n * <script lang=\"ts\">\n * const schemas = createAsyncStore((c) => getSchemas(c), client);\n * </script>\n * {#if schemas.loading}...{/if}\n * ```\n */\nexport function createAsyncStore<T>(\n\tfetcher: (client: HttpClient) => Promise<T>,\n\tclient: HttpClient,\n): AsyncStore<T> {\n\tlet data = $state<T | null>(null);\n\tlet error = $state<Error | null>(null);\n\tlet loading = $state(true);\n\n\tasync function fetch() {\n\t\tloading = true;\n\t\ttry {\n\t\t\tdata = await fetcher(client);\n\t\t\terror = null;\n\t\t} catch (e) {\n\t\t\terror = e as Error;\n\t\t} finally {\n\t\t\tloading = false;\n\t\t}\n\t}\n\n\tfetch();\n\n\treturn {\n\t\tget data() { return data; },\n\t\tget error() { return error; },\n\t\tget loading() { return loading; },\n\t\trefetch: fetch,\n\t};\n}\n\n/**\n * Reactive mutation primitive using the $state rune.\n * Returns data/error/loading state and a run function that triggers the\n * mutation. Unlike createAsyncStore, the mutation does not fire immediately.\n *\n * @example\n * ```svelte\n * <script lang=\"ts\">\n * const createArticle = createMutation(\n * (c, vars: { title: string }) => createArticle(c, vars),\n * client,\n * );\n * </script>\n * <button onclick={() => createArticle.run({ title: 'Hello' })}>\n * {createArticle.loading ? 'Saving...' : 'Create'}\n * </button>\n * ```\n */\nexport function createMutation<T, V>(\n\tmutator: (client: HttpClient, vars: V) => Promise<T>,\n\tclient: HttpClient,\n) {\n\tlet data = $state<T | null>(null);\n\tlet error = $state<Error | null>(null);\n\tlet loading = $state(false);\n\n\tasync function run(vars: V): Promise<T> {\n\t\tloading = true;\n\t\terror = null;\n\t\ttry {\n\t\t\tconst result = await mutator(client, vars);\n\t\t\tdata = result;\n\t\t\treturn result;\n\t\t} catch (e) {\n\t\t\terror = e as Error;\n\t\t\tthrow e;\n\t\t} finally {\n\t\t\tloading = false;\n\t\t}\n\t}\n\n\treturn {\n\t\tget data() { return data; },\n\t\tget error() { return error; },\n\t\tget loading() { return loading; },\n\t\trun,\n\t};\n}\n\n// Auth store\n\nexport interface AuthState {\n\tuser: { id: string; email: string; roles: string[] } | null;\n\ttoken: string | null;\n}\n\nexport interface AuthStore {\n\treadonly user: AuthState['user'];\n\treadonly token: string | null;\n\treadonly isAuthenticated: boolean;\n\t/** Set the current user and token after a successful login. */\n\tsetUser: (user: AuthState['user'], token: string | null) => void;\n\t/** Clear auth state (e.g. after logout). */\n\tclear: () => void;\n\t/** Try to load the current user from the server using the stored token. */\n\tload: () => Promise<void>;\n}\n\n/**\n * Simple reactive auth store. The caller is responsible for calling\n * {@link AuthStore.setUser} after login and {@link AuthStore.clear} after\n * logout. Use {@link AuthStore.load} on app start to restore a session\n * from an existing cookie.\n */\nexport function createAuthStore(client: HttpClient): AuthStore {\n\tlet state = $state<AuthState>({ user: null, token: null });\n\n\treturn {\n\t\tget user() { return state.user; },\n\t\tget token() { return state.token; },\n\t\tget isAuthenticated() { return state.token !== null; },\n\t\tsetUser(user: AuthState['user'], token: string | null) {\n\t\t\tstate = { user, token };\n\t\t},\n\t\tclear() {\n\t\t\tstate = { user: null, token: null };\n\t\t},\n\t\tasync load() {\n\t\t\ttry {\n\t\t\t\tconst user = await client.get<{ id: string; email: string; roles: string[] }>('/api/admin/auth/me');\n\t\t\t\tstate = { ...state, user };\n\t\t\t} catch { /* not logged in */ }\n\t\t},\n\t};\n}\n"],"mappings":";AAAA,SAAS,oBAAqC;AA2BvC,SAAS,gBAAgB,QAAqC;AACpE,QAAM,OAAO,OAAO,WAAW;AAC/B,SAAO,aAAa,CAAC,KAAK,SAAS;AAClC,UAAM,UAAU,OAAO,QAAQ,WAAW,GAAG,IAAI,GAAG,GAAG,KAAK;AAC5D,WAAO,MAAM,SAAS;AAAA,MACrB,GAAG;AAAA,MACH,SAAS,EAAE,GAAG,MAAM,SAAS,GAAG,OAAO,aAAa,EAAE;AAAA,IACvD,CAAC;AAAA,EACF,CAAC;AACF;AAwBO,SAAS,iBACf,SACA,QACgB;AAChB,MAAI,OAAO,OAAiB,IAAI;AAChC,MAAI,QAAQ,OAAqB,IAAI;AACrC,MAAI,UAAU,OAAO,IAAI;AAEzB,iBAAeA,SAAQ;AACtB,cAAU;AACV,QAAI;AACH,aAAO,MAAM,QAAQ,MAAM;AAC3B,cAAQ;AAAA,IACT,SAAS,GAAG;AACX,cAAQ;AAAA,IACT,UAAE;AACD,gBAAU;AAAA,IACX;AAAA,EACD;AAEA,EAAAA,OAAM;AAEN,SAAO;AAAA,IACN,IAAI,OAAO;AAAE,aAAO;AAAA,IAAM;AAAA,IAC1B,IAAI,QAAQ;AAAE,aAAO;AAAA,IAAO;AAAA,IAC5B,IAAI,UAAU;AAAE,aAAO;AAAA,IAAS;AAAA,IAChC,SAASA;AAAA,EACV;AACD;AAoBO,SAAS,eACf,SACA,QACC;AACD,MAAI,OAAO,OAAiB,IAAI;AAChC,MAAI,QAAQ,OAAqB,IAAI;AACrC,MAAI,UAAU,OAAO,KAAK;AAE1B,iBAAe,IAAI,MAAqB;AACvC,cAAU;AACV,YAAQ;AACR,QAAI;AACH,YAAM,SAAS,MAAM,QAAQ,QAAQ,IAAI;AACzC,aAAO;AACP,aAAO;AAAA,IACR,SAAS,GAAG;AACX,cAAQ;AACR,YAAM;AAAA,IACP,UAAE;AACD,gBAAU;AAAA,IACX;AAAA,EACD;AAEA,SAAO;AAAA,IACN,IAAI,OAAO;AAAE,aAAO;AAAA,IAAM;AAAA,IAC1B,IAAI,QAAQ;AAAE,aAAO;AAAA,IAAO;AAAA,IAC5B,IAAI,UAAU;AAAE,aAAO;AAAA,IAAS;AAAA,IAChC;AAAA,EACD;AACD;AA2BO,SAAS,gBAAgB,QAA+B;AAC9D,MAAI,QAAQ,OAAkB,EAAE,MAAM,MAAM,OAAO,KAAK,CAAC;AAEzD,SAAO;AAAA,IACN,IAAI,OAAO;AAAE,aAAO,MAAM;AAAA,IAAM;AAAA,IAChC,IAAI,QAAQ;AAAE,aAAO,MAAM;AAAA,IAAO;AAAA,IAClC,IAAI,kBAAkB;AAAE,aAAO,MAAM,UAAU;AAAA,IAAM;AAAA,IACrD,QAAQ,MAAyB,OAAsB;AACtD,cAAQ,EAAE,MAAM,MAAM;AAAA,IACvB;AAAA,IACA,QAAQ;AACP,cAAQ,EAAE,MAAM,MAAM,OAAO,KAAK;AAAA,IACnC;AAAA,IACA,MAAM,OAAO;AACZ,UAAI;AACH,cAAM,OAAO,MAAM,OAAO,IAAoD,oBAAoB;AAClG,gBAAQ,EAAE,GAAG,OAAO,KAAK;AAAA,MAC1B,QAAQ;AAAA,MAAsB;AAAA,IAC/B;AAAA,EACD;AACD;","names":["fetch"]}
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@lyeve-labs/client-svelte",
3
+ "version": "0.1.2",
4
+ "description": "Svelte 5 reactive stores for the LyEve Core API - thin wrapper around @lyeve/cms-client using $state runes",
5
+ "license": "MIT",
6
+ "author": "LyEve Labs <hello@lyeve.com>",
7
+ "type": "module",
8
+ "engines": {
9
+ "node": ">=20"
10
+ },
11
+ "main": "./dist/index.cjs",
12
+ "module": "./dist/index.js",
13
+ "types": "./dist/index.d.ts",
14
+ "svelte": "./src/index.svelte.ts",
15
+ "exports": {
16
+ ".": {
17
+ "svelte": "./src/index.svelte.ts",
18
+ "types": "./dist/index.d.ts",
19
+ "import": "./dist/index.js",
20
+ "require": "./dist/index.cjs"
21
+ },
22
+ "./package.json": "./package.json"
23
+ },
24
+ "files": [
25
+ "dist",
26
+ "!dist/**/*.test.*",
27
+ "!dist/**/*.spec.*"
28
+ ],
29
+ "sideEffects": false,
30
+ "publishConfig": {
31
+ "access": "public"
32
+ },
33
+ "peerDependencies": {
34
+ "@lyeve-labs/client-svelte": ">=0.1.0",
35
+ "svelte": ">=5.0.0"
36
+ },
37
+ "devDependencies": {
38
+ "@lyeve-labs/client-svelte": "^0.1.0",
39
+ "svelte": "^5.0.0",
40
+ "tsup": "^8.4.0",
41
+ "typescript": "^5.7.0",
42
+ "vitest": "^2.1.0",
43
+ "publint": "^0.3.0",
44
+ "prettier": "^3.4.0"
45
+ },
46
+ "scripts": {
47
+ "dev": "tsup --watch",
48
+ "build": "tsup && publint",
49
+ "check": "tsc --noEmit",
50
+ "test": "vitest run",
51
+ "test:watch": "vitest",
52
+ "format": "prettier --write .",
53
+ "format:check": "prettier --check ."
54
+ }
55
+ }