@datafn/svelte 0.0.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/README.md +290 -0
- package/dist/index.cjs +100 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +54 -0
- package/dist/index.d.ts +54 -0
- package/dist/index.js +73 -0
- package/dist/index.js.map +1 -0
- package/package.json +49 -0
package/README.md
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
# @datafn/svelte
|
|
2
|
+
|
|
3
|
+
Svelte bindings for DataFn. Converts DataFn reactive signals into Svelte readable stores for seamless integration with Svelte 3, 4, and 5 components.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @datafn/svelte @datafn/core
|
|
9
|
+
# Peer dependency
|
|
10
|
+
npm install svelte
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- **Seamless Reactivity** — Mutations and sync changes in DataFn automatically update your Svelte components
|
|
16
|
+
- **Automatic Cleanup** — Store subscriptions are cleaned up when components are destroyed
|
|
17
|
+
- **State Properties** — Stores expose `data`, `loading`, `error`, and `refreshing` for complete UI control
|
|
18
|
+
- **Derived Store Support** — Returns standard Svelte `Readable`, composable with `derived` stores
|
|
19
|
+
- **Svelte 3 / 4 / 5 Compatible** — Works with reactive declarations (`$:`) and runes
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
```svelte
|
|
26
|
+
<script lang="ts">
|
|
27
|
+
import { createDatafnClient, IndexedDbStorageAdapter } from "@datafn/client";
|
|
28
|
+
import { toSvelteStore } from "@datafn/svelte";
|
|
29
|
+
|
|
30
|
+
const client = createDatafnClient({
|
|
31
|
+
schema: mySchema,
|
|
32
|
+
clientId: "device-1",
|
|
33
|
+
storage: new IndexedDbStorageAdapter("my-db"),
|
|
34
|
+
sync: { remote: "http://localhost:3000/datafn" },
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Create a DataFn signal (reactive query)
|
|
38
|
+
const signal = client.table("tasks").signal({
|
|
39
|
+
filters: { completed: false },
|
|
40
|
+
sort: ["-createdAt"],
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Convert to Svelte store
|
|
44
|
+
const tasks = toSvelteStore(signal);
|
|
45
|
+
</script>
|
|
46
|
+
|
|
47
|
+
{#if $tasks.loading}
|
|
48
|
+
<p>Loading...</p>
|
|
49
|
+
{:else if $tasks.error}
|
|
50
|
+
<p>Error: {$tasks.error.message}</p>
|
|
51
|
+
{:else}
|
|
52
|
+
{#each $tasks.data || [] as task (task.id)}
|
|
53
|
+
<div>{task.title}</div>
|
|
54
|
+
{/each}
|
|
55
|
+
{/if}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## API
|
|
61
|
+
|
|
62
|
+
### toSvelteStore\<T\>(signal): DatafnSvelteStore\<T\>
|
|
63
|
+
|
|
64
|
+
Converts a DataFn `Signal<T>` into a Svelte `Readable` store that wraps the signal value with state properties.
|
|
65
|
+
|
|
66
|
+
**Parameters:**
|
|
67
|
+
|
|
68
|
+
| Parameter | Type | Description |
|
|
69
|
+
|-----------|------|-------------|
|
|
70
|
+
| `signal` | `DatafnSignal<T>` | A signal from `table.signal()` or `client.kv.signal()` |
|
|
71
|
+
|
|
72
|
+
**Returns:**
|
|
73
|
+
|
|
74
|
+
A `Readable<{ data: T; loading: boolean; error: DatafnError | null; refreshing: boolean }>`.
|
|
75
|
+
|
|
76
|
+
| Property | Type | Description |
|
|
77
|
+
|----------|------|-------------|
|
|
78
|
+
| `data` | `T` | The current signal value (query results, KV value, etc.) |
|
|
79
|
+
| `loading` | `boolean` | `true` while the initial fetch is in progress |
|
|
80
|
+
| `error` | `DatafnError \| null` | Non-null if the last fetch/refresh failed |
|
|
81
|
+
| `refreshing` | `boolean` | `true` while a background refresh is in progress |
|
|
82
|
+
|
|
83
|
+
**Type:**
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
type DatafnSvelteStore<T> = Readable<{
|
|
87
|
+
data: T;
|
|
88
|
+
loading: boolean;
|
|
89
|
+
error: DatafnError | null;
|
|
90
|
+
refreshing: boolean;
|
|
91
|
+
}>;
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Usage Patterns
|
|
97
|
+
|
|
98
|
+
### Table Query Signals
|
|
99
|
+
|
|
100
|
+
```svelte
|
|
101
|
+
<script lang="ts">
|
|
102
|
+
import { toSvelteStore } from "@datafn/svelte";
|
|
103
|
+
import { client } from "./lib/datafn";
|
|
104
|
+
|
|
105
|
+
// All tasks
|
|
106
|
+
const allTasks = toSvelteStore(
|
|
107
|
+
client.table("tasks").signal({ sort: ["-createdAt"] })
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
// Filtered tasks
|
|
111
|
+
const activeTasks = toSvelteStore(
|
|
112
|
+
client.table("tasks").signal({
|
|
113
|
+
filters: { completed: false },
|
|
114
|
+
sort: ["priority", "-createdAt"],
|
|
115
|
+
})
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
// With select and limit
|
|
119
|
+
const recentTasks = toSvelteStore(
|
|
120
|
+
client.table("tasks").signal({
|
|
121
|
+
select: ["id", "title"],
|
|
122
|
+
sort: ["-createdAt"],
|
|
123
|
+
limit: 5,
|
|
124
|
+
})
|
|
125
|
+
);
|
|
126
|
+
</script>
|
|
127
|
+
|
|
128
|
+
<h2>Active Tasks ({($activeTasks.data || []).length})</h2>
|
|
129
|
+
{#each $activeTasks.data || [] as task (task.id)}
|
|
130
|
+
<div>{task.title}</div>
|
|
131
|
+
{/each}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### KV Signal Stores
|
|
135
|
+
|
|
136
|
+
```svelte
|
|
137
|
+
<script lang="ts">
|
|
138
|
+
import { toSvelteStore } from "@datafn/svelte";
|
|
139
|
+
import { client } from "./lib/datafn";
|
|
140
|
+
|
|
141
|
+
// Reactive KV preference
|
|
142
|
+
const theme = toSvelteStore(
|
|
143
|
+
client.kv.signal<string>("pref:theme", { defaultValue: "dark" })
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
async function toggleTheme() {
|
|
147
|
+
const next = $theme.data === "dark" ? "light" : "dark";
|
|
148
|
+
await client.kv.set("pref:theme", next);
|
|
149
|
+
// Store auto-updates — no manual refresh needed
|
|
150
|
+
}
|
|
151
|
+
</script>
|
|
152
|
+
|
|
153
|
+
<button on:click={toggleTheme}>
|
|
154
|
+
Theme: {$theme.data}
|
|
155
|
+
</button>
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Derived Stores
|
|
159
|
+
|
|
160
|
+
Since `toSvelteStore` returns a standard Svelte `Readable`, you can compose it with `derived`:
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
import { derived } from "svelte/store";
|
|
164
|
+
import { toSvelteStore } from "@datafn/svelte";
|
|
165
|
+
|
|
166
|
+
const allTasks = toSvelteStore(client.table("tasks").signal({}));
|
|
167
|
+
|
|
168
|
+
// Compute statistics from the live data
|
|
169
|
+
const stats = derived(allTasks, ($tasks) => {
|
|
170
|
+
const data = $tasks.data || [];
|
|
171
|
+
return {
|
|
172
|
+
total: data.length,
|
|
173
|
+
completed: data.filter((t: any) => t.completed).length,
|
|
174
|
+
active: data.filter((t: any) => !t.completed).length,
|
|
175
|
+
};
|
|
176
|
+
});
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
```svelte
|
|
180
|
+
<p>{$stats.total} tasks — {$stats.active} active, {$stats.completed} done</p>
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Reactive Query Parameters (Svelte 3/4)
|
|
184
|
+
|
|
185
|
+
Re-create the store when a reactive variable changes:
|
|
186
|
+
|
|
187
|
+
```svelte
|
|
188
|
+
<script>
|
|
189
|
+
import { toSvelteStore } from "@datafn/svelte";
|
|
190
|
+
import { client } from "./lib/datafn";
|
|
191
|
+
|
|
192
|
+
export let categoryId;
|
|
193
|
+
|
|
194
|
+
// Re-creates the store whenever categoryId changes
|
|
195
|
+
$: tasks = toSvelteStore(
|
|
196
|
+
client.table("tasks").signal({
|
|
197
|
+
filters: { categoryId },
|
|
198
|
+
sort: ["-createdAt"],
|
|
199
|
+
})
|
|
200
|
+
);
|
|
201
|
+
</script>
|
|
202
|
+
|
|
203
|
+
{#each $tasks.data || [] as task (task.id)}
|
|
204
|
+
<div>{task.title}</div>
|
|
205
|
+
{/each}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Loading & Error States
|
|
209
|
+
|
|
210
|
+
Handle all states for a polished UX:
|
|
211
|
+
|
|
212
|
+
```svelte
|
|
213
|
+
<script>
|
|
214
|
+
import { toSvelteStore } from "@datafn/svelte";
|
|
215
|
+
import { client } from "./lib/datafn";
|
|
216
|
+
|
|
217
|
+
const tasks = toSvelteStore(
|
|
218
|
+
client.table("tasks").signal({ sort: ["-createdAt"] })
|
|
219
|
+
);
|
|
220
|
+
</script>
|
|
221
|
+
|
|
222
|
+
{#if $tasks.loading}
|
|
223
|
+
<div class="skeleton">Loading tasks...</div>
|
|
224
|
+
{:else if $tasks.error}
|
|
225
|
+
<div class="error">
|
|
226
|
+
<p>Failed to load tasks: {$tasks.error.message}</p>
|
|
227
|
+
<code>{$tasks.error.code}</code>
|
|
228
|
+
</div>
|
|
229
|
+
{:else}
|
|
230
|
+
<ul>
|
|
231
|
+
{#each $tasks.data || [] as task (task.id)}
|
|
232
|
+
<li>{task.title}</li>
|
|
233
|
+
{/each}
|
|
234
|
+
</ul>
|
|
235
|
+
|
|
236
|
+
{#if $tasks.refreshing}
|
|
237
|
+
<small>Refreshing...</small>
|
|
238
|
+
{/if}
|
|
239
|
+
{/if}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### Multiple Stores in a Component
|
|
243
|
+
|
|
244
|
+
```svelte
|
|
245
|
+
<script lang="ts">
|
|
246
|
+
import { toSvelteStore } from "@datafn/svelte";
|
|
247
|
+
import { client } from "./lib/datafn";
|
|
248
|
+
|
|
249
|
+
const todos = toSvelteStore(client.table("todos").signal({ sort: ["-createdAt"] }));
|
|
250
|
+
const categories = toSvelteStore(client.table("categories").signal({ sort: ["name"] }));
|
|
251
|
+
const theme = toSvelteStore(client.kv.signal("pref:theme", { defaultValue: "dark" }));
|
|
252
|
+
</script>
|
|
253
|
+
|
|
254
|
+
<main class={$theme.data}>
|
|
255
|
+
<h1>Todos ({($todos.data || []).length})</h1>
|
|
256
|
+
<!-- ... -->
|
|
257
|
+
<aside>
|
|
258
|
+
<h2>Categories ({($categories.data || []).length})</h2>
|
|
259
|
+
<!-- ... -->
|
|
260
|
+
</aside>
|
|
261
|
+
</main>
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## How It Works
|
|
267
|
+
|
|
268
|
+
1. `toSvelteStore(signal)` creates a Svelte `readable` store
|
|
269
|
+
2. On first subscriber, the store subscribes to the DataFn signal
|
|
270
|
+
3. When the signal value changes (mutation, sync, or refresh), the store updates
|
|
271
|
+
4. The store reads `signal.loading`, `signal.error`, and `signal.refreshing` on each update
|
|
272
|
+
5. When the last subscriber unsubscribes (component destroyed), the signal subscription is cleaned up
|
|
273
|
+
|
|
274
|
+
This means:
|
|
275
|
+
- **Lazy**: No data is fetched until a component subscribes
|
|
276
|
+
- **Shared**: Multiple components using the same signal share a single cached query
|
|
277
|
+
- **Auto-updating**: Mutations to the underlying resource trigger automatic signal refresh
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## Exports
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
export { toSvelteStore }
|
|
285
|
+
export type { DatafnSvelteStore }
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## License
|
|
289
|
+
|
|
290
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
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 index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
toSvelteStore: () => toSvelteStore
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(index_exports);
|
|
26
|
+
|
|
27
|
+
// src/toSvelteStore.ts
|
|
28
|
+
var import_store = require("svelte/store");
|
|
29
|
+
function toSvelteStore(signalOrClientRef, signalFactory) {
|
|
30
|
+
if (signalFactory !== void 0) {
|
|
31
|
+
return toSvelteStoreFactory(signalOrClientRef, signalFactory);
|
|
32
|
+
}
|
|
33
|
+
return toSvelteStoreDirect(signalOrClientRef);
|
|
34
|
+
}
|
|
35
|
+
function toSvelteStoreDirect(signal) {
|
|
36
|
+
return (0, import_store.readable)(
|
|
37
|
+
{
|
|
38
|
+
data: signal.get(),
|
|
39
|
+
loading: signal.loading,
|
|
40
|
+
error: signal.error,
|
|
41
|
+
refreshing: signal.refreshing,
|
|
42
|
+
nextCursor: signal.nextCursor ?? null
|
|
43
|
+
},
|
|
44
|
+
(set) => {
|
|
45
|
+
const unsub = signal.subscribe((value) => {
|
|
46
|
+
set({
|
|
47
|
+
data: value,
|
|
48
|
+
loading: signal.loading,
|
|
49
|
+
error: signal.error,
|
|
50
|
+
refreshing: signal.refreshing,
|
|
51
|
+
nextCursor: signal.nextCursor ?? null
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
return () => {
|
|
55
|
+
unsub();
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
function toSvelteStoreFactory(clientRef, signalFactory) {
|
|
61
|
+
return (0, import_store.readable)(
|
|
62
|
+
{ data: void 0, loading: true, error: null, refreshing: false, nextCursor: null },
|
|
63
|
+
(set) => {
|
|
64
|
+
let currentSignal = null;
|
|
65
|
+
let currentUnsubSignal = null;
|
|
66
|
+
const unsubClient = clientRef.subscribe((client) => {
|
|
67
|
+
currentUnsubSignal?.();
|
|
68
|
+
currentSignal?.dispose();
|
|
69
|
+
currentSignal = signalFactory(client);
|
|
70
|
+
set({
|
|
71
|
+
data: currentSignal.get(),
|
|
72
|
+
loading: currentSignal.loading,
|
|
73
|
+
error: currentSignal.error,
|
|
74
|
+
refreshing: currentSignal.refreshing,
|
|
75
|
+
nextCursor: currentSignal.nextCursor ?? null
|
|
76
|
+
});
|
|
77
|
+
currentUnsubSignal = currentSignal.subscribe((value) => {
|
|
78
|
+
set({
|
|
79
|
+
data: value,
|
|
80
|
+
loading: currentSignal.loading,
|
|
81
|
+
error: currentSignal.error,
|
|
82
|
+
refreshing: currentSignal.refreshing,
|
|
83
|
+
nextCursor: currentSignal.nextCursor ?? null
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
return () => {
|
|
88
|
+
unsubClient();
|
|
89
|
+
currentUnsubSignal?.();
|
|
90
|
+
currentSignal?.dispose();
|
|
91
|
+
currentSignal = null;
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
97
|
+
0 && (module.exports = {
|
|
98
|
+
toSvelteStore
|
|
99
|
+
});
|
|
100
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/toSvelteStore.ts"],"sourcesContent":["/**\n * @datafn/svelte public API\n */\n\nexport { toSvelteStore } from \"./toSvelteStore.js\";\nexport type { DatafnSvelteStore, ClientRef } from \"./toSvelteStore.js\";\n","/**\n * Convert DataFn signal to Svelte store\n */\n\nimport type { DatafnSignal, DatafnError } from \"@datafn/core\";\nimport type { Readable } from \"svelte/store\";\nimport { readable } from \"svelte/store\";\n\n/**\n * A client reference that can notify subscribers when the underlying client\n * changes. Compatible with `{ subscribe: client.subscribeClient }` from a\n * unified `createDatafnClient`, or any Svelte writable store holding a client.\n */\nexport type ClientRef<C> = {\n subscribe(fn: (client: C) => void): () => void;\n};\n\n/**\n * Store type that wraps signal value with state properties.\n * Returned by both overloads.\n */\nexport type DatafnSvelteStore<T> = Readable<{\n data: T;\n loading: boolean;\n error: DatafnError | null;\n refreshing: boolean;\n nextCursor?: string | null;\n}>;\n\n/**\n * Convert a lifecycle-aware DataFn signal directly to a Svelte readable store.\n *\n * Signals from `createDatafnClient` are lifecycle-aware — they survive\n * `switchContext()` and rebind automatically. No factory pattern or `clientRef`\n * is needed.\n *\n * ```typescript\n * const todosStore = toSvelteStore(\n * client.todos.signal({ sort: [\"-createdAt\"] })\n * );\n * // Template: {#each $todosStore.data ?? [] as todo}\n * // Also: {#if $todosStore.loading} Loading... {/if}\n * ```\n */\nexport function toSvelteStore<T>(signal: DatafnSignal<T>): DatafnSvelteStore<T>;\n\n/**\n * Convert a DataFn signal factory to a reactive Svelte readable store.\n *\n * Accepts a `clientRef` (anything with a `subscribe(fn)` API) and a\n * `signalFactory` that creates a new signal from the current client. The store\n * automatically disposes the old signal and creates a fresh one whenever the\n * client changes, preventing memory leaks and ensuring signals are always\n * bound to the active client.\n */\nexport function toSvelteStore<T, C>(\n clientRef: ClientRef<C>,\n signalFactory: (client: C) => DatafnSignal<T>,\n): DatafnSvelteStore<T>;\n\nexport function toSvelteStore<T, C = any>(\n signalOrClientRef: DatafnSignal<T> | ClientRef<C>,\n signalFactory?: (client: C) => DatafnSignal<T>,\n): DatafnSvelteStore<T> {\n if (signalFactory !== undefined) {\n return toSvelteStoreFactory(signalOrClientRef as ClientRef<C>, signalFactory);\n }\n return toSvelteStoreDirect(signalOrClientRef as DatafnSignal<T>);\n}\n\n/**\n * Direct signal overload: wraps a DatafnSignal as a Svelte readable store.\n * The store emits `{ data, loading, error, refreshing }` on each update.\n * Does NOT call signal.dispose() on teardown — the signal's lifecycle is\n * managed by the LiveSignalRegistry, allowing multiple stores to share a\n * deduplicated signal safely.\n */\nfunction toSvelteStoreDirect<T>(signal: DatafnSignal<T>): DatafnSvelteStore<T> {\n return readable(\n {\n data: signal.get(),\n loading: signal.loading,\n error: signal.error,\n refreshing: signal.refreshing,\n nextCursor: signal.nextCursor ?? null,\n },\n (set) => {\n const unsub = signal.subscribe((value: T) => {\n set({\n data: value,\n loading: signal.loading,\n error: signal.error,\n refreshing: signal.refreshing,\n nextCursor: signal.nextCursor ?? null,\n });\n });\n return () => {\n unsub();\n };\n },\n );\n}\n\n/**\n * Factory overload: subscribes to clientRef, creates/disposes signal on each\n * client change. Returns a DatafnSvelteStore with { data, loading, error, refreshing }.\n */\nfunction toSvelteStoreFactory<T, C>(\n clientRef: ClientRef<C>,\n signalFactory: (client: C) => DatafnSignal<T>,\n): DatafnSvelteStore<T> {\n return readable(\n { data: undefined as unknown as T, loading: true as boolean, error: null as DatafnError | null, refreshing: false as boolean, nextCursor: null as string | null },\n (set) => {\n let currentSignal: DatafnSignal<T> | null = null;\n let currentUnsubSignal: (() => void) | null = null;\n\n const unsubClient = clientRef.subscribe((client) => {\n // Tear down previous signal\n currentUnsubSignal?.();\n currentSignal?.dispose();\n\n // Create a fresh signal from the new client\n currentSignal = signalFactory(client);\n\n // Emit the initial value immediately\n set({\n data: currentSignal.get(),\n loading: currentSignal.loading,\n error: currentSignal.error,\n refreshing: currentSignal.refreshing,\n nextCursor: currentSignal.nextCursor ?? null,\n });\n\n // Subscribe to future updates\n currentUnsubSignal = currentSignal.subscribe((value: T) => {\n set({\n data: value,\n loading: currentSignal!.loading,\n error: currentSignal!.error,\n refreshing: currentSignal!.refreshing,\n nextCursor: currentSignal!.nextCursor ?? null,\n });\n });\n });\n\n // Cleanup when all Svelte subscribers unsubscribe\n return () => {\n unsubClient();\n currentUnsubSignal?.();\n currentSignal?.dispose();\n currentSignal = null;\n };\n },\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACMA,mBAAyB;AAsDlB,SAAS,cACd,mBACA,eACsB;AACtB,MAAI,kBAAkB,QAAW;AAC/B,WAAO,qBAAqB,mBAAmC,aAAa;AAAA,EAC9E;AACA,SAAO,oBAAoB,iBAAoC;AACjE;AASA,SAAS,oBAAuB,QAA+C;AAC7E,aAAO;AAAA,IACL;AAAA,MACE,MAAM,OAAO,IAAI;AAAA,MACjB,SAAS,OAAO;AAAA,MAChB,OAAO,OAAO;AAAA,MACd,YAAY,OAAO;AAAA,MACnB,YAAY,OAAO,cAAc;AAAA,IACnC;AAAA,IACA,CAAC,QAAQ;AACP,YAAM,QAAQ,OAAO,UAAU,CAAC,UAAa;AAC3C,YAAI;AAAA,UACF,MAAM;AAAA,UACN,SAAS,OAAO;AAAA,UAChB,OAAO,OAAO;AAAA,UACd,YAAY,OAAO;AAAA,UACnB,YAAY,OAAO,cAAc;AAAA,QACnC,CAAC;AAAA,MACH,CAAC;AACD,aAAO,MAAM;AACX,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACF;AAMA,SAAS,qBACP,WACA,eACsB;AACtB,aAAO;AAAA,IACL,EAAE,MAAM,QAA2B,SAAS,MAAiB,OAAO,MAA4B,YAAY,OAAkB,YAAY,KAAsB;AAAA,IAChK,CAAC,QAAQ;AACP,UAAI,gBAAwC;AAC5C,UAAI,qBAA0C;AAE9C,YAAM,cAAc,UAAU,UAAU,CAAC,WAAW;AAElD,6BAAqB;AACrB,uBAAe,QAAQ;AAGvB,wBAAgB,cAAc,MAAM;AAGpC,YAAI;AAAA,UACF,MAAM,cAAc,IAAI;AAAA,UACxB,SAAS,cAAc;AAAA,UACvB,OAAO,cAAc;AAAA,UACrB,YAAY,cAAc;AAAA,UAC1B,YAAY,cAAc,cAAc;AAAA,QAC1C,CAAC;AAGD,6BAAqB,cAAc,UAAU,CAAC,UAAa;AACzD,cAAI;AAAA,YACF,MAAM;AAAA,YACN,SAAS,cAAe;AAAA,YACxB,OAAO,cAAe;AAAA,YACtB,YAAY,cAAe;AAAA,YAC3B,YAAY,cAAe,cAAc;AAAA,UAC3C,CAAC;AAAA,QACH,CAAC;AAAA,MACH,CAAC;AAGD,aAAO,MAAM;AACX,oBAAY;AACZ,6BAAqB;AACrB,uBAAe,QAAQ;AACvB,wBAAgB;AAAA,MAClB;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { DatafnSignal, DatafnError } from '@datafn/core';
|
|
2
|
+
import { Readable } from 'svelte/store';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Convert DataFn signal to Svelte store
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A client reference that can notify subscribers when the underlying client
|
|
10
|
+
* changes. Compatible with `{ subscribe: client.subscribeClient }` from a
|
|
11
|
+
* unified `createDatafnClient`, or any Svelte writable store holding a client.
|
|
12
|
+
*/
|
|
13
|
+
type ClientRef<C> = {
|
|
14
|
+
subscribe(fn: (client: C) => void): () => void;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Store type that wraps signal value with state properties.
|
|
18
|
+
* Returned by both overloads.
|
|
19
|
+
*/
|
|
20
|
+
type DatafnSvelteStore<T> = Readable<{
|
|
21
|
+
data: T;
|
|
22
|
+
loading: boolean;
|
|
23
|
+
error: DatafnError | null;
|
|
24
|
+
refreshing: boolean;
|
|
25
|
+
nextCursor?: string | null;
|
|
26
|
+
}>;
|
|
27
|
+
/**
|
|
28
|
+
* Convert a lifecycle-aware DataFn signal directly to a Svelte readable store.
|
|
29
|
+
*
|
|
30
|
+
* Signals from `createDatafnClient` are lifecycle-aware — they survive
|
|
31
|
+
* `switchContext()` and rebind automatically. No factory pattern or `clientRef`
|
|
32
|
+
* is needed.
|
|
33
|
+
*
|
|
34
|
+
* ```typescript
|
|
35
|
+
* const todosStore = toSvelteStore(
|
|
36
|
+
* client.todos.signal({ sort: ["-createdAt"] })
|
|
37
|
+
* );
|
|
38
|
+
* // Template: {#each $todosStore.data ?? [] as todo}
|
|
39
|
+
* // Also: {#if $todosStore.loading} Loading... {/if}
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
declare function toSvelteStore<T>(signal: DatafnSignal<T>): DatafnSvelteStore<T>;
|
|
43
|
+
/**
|
|
44
|
+
* Convert a DataFn signal factory to a reactive Svelte readable store.
|
|
45
|
+
*
|
|
46
|
+
* Accepts a `clientRef` (anything with a `subscribe(fn)` API) and a
|
|
47
|
+
* `signalFactory` that creates a new signal from the current client. The store
|
|
48
|
+
* automatically disposes the old signal and creates a fresh one whenever the
|
|
49
|
+
* client changes, preventing memory leaks and ensuring signals are always
|
|
50
|
+
* bound to the active client.
|
|
51
|
+
*/
|
|
52
|
+
declare function toSvelteStore<T, C>(clientRef: ClientRef<C>, signalFactory: (client: C) => DatafnSignal<T>): DatafnSvelteStore<T>;
|
|
53
|
+
|
|
54
|
+
export { type ClientRef, type DatafnSvelteStore, toSvelteStore };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { DatafnSignal, DatafnError } from '@datafn/core';
|
|
2
|
+
import { Readable } from 'svelte/store';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Convert DataFn signal to Svelte store
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A client reference that can notify subscribers when the underlying client
|
|
10
|
+
* changes. Compatible with `{ subscribe: client.subscribeClient }` from a
|
|
11
|
+
* unified `createDatafnClient`, or any Svelte writable store holding a client.
|
|
12
|
+
*/
|
|
13
|
+
type ClientRef<C> = {
|
|
14
|
+
subscribe(fn: (client: C) => void): () => void;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Store type that wraps signal value with state properties.
|
|
18
|
+
* Returned by both overloads.
|
|
19
|
+
*/
|
|
20
|
+
type DatafnSvelteStore<T> = Readable<{
|
|
21
|
+
data: T;
|
|
22
|
+
loading: boolean;
|
|
23
|
+
error: DatafnError | null;
|
|
24
|
+
refreshing: boolean;
|
|
25
|
+
nextCursor?: string | null;
|
|
26
|
+
}>;
|
|
27
|
+
/**
|
|
28
|
+
* Convert a lifecycle-aware DataFn signal directly to a Svelte readable store.
|
|
29
|
+
*
|
|
30
|
+
* Signals from `createDatafnClient` are lifecycle-aware — they survive
|
|
31
|
+
* `switchContext()` and rebind automatically. No factory pattern or `clientRef`
|
|
32
|
+
* is needed.
|
|
33
|
+
*
|
|
34
|
+
* ```typescript
|
|
35
|
+
* const todosStore = toSvelteStore(
|
|
36
|
+
* client.todos.signal({ sort: ["-createdAt"] })
|
|
37
|
+
* );
|
|
38
|
+
* // Template: {#each $todosStore.data ?? [] as todo}
|
|
39
|
+
* // Also: {#if $todosStore.loading} Loading... {/if}
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
declare function toSvelteStore<T>(signal: DatafnSignal<T>): DatafnSvelteStore<T>;
|
|
43
|
+
/**
|
|
44
|
+
* Convert a DataFn signal factory to a reactive Svelte readable store.
|
|
45
|
+
*
|
|
46
|
+
* Accepts a `clientRef` (anything with a `subscribe(fn)` API) and a
|
|
47
|
+
* `signalFactory` that creates a new signal from the current client. The store
|
|
48
|
+
* automatically disposes the old signal and creates a fresh one whenever the
|
|
49
|
+
* client changes, preventing memory leaks and ensuring signals are always
|
|
50
|
+
* bound to the active client.
|
|
51
|
+
*/
|
|
52
|
+
declare function toSvelteStore<T, C>(clientRef: ClientRef<C>, signalFactory: (client: C) => DatafnSignal<T>): DatafnSvelteStore<T>;
|
|
53
|
+
|
|
54
|
+
export { type ClientRef, type DatafnSvelteStore, toSvelteStore };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// src/toSvelteStore.ts
|
|
2
|
+
import { readable } from "svelte/store";
|
|
3
|
+
function toSvelteStore(signalOrClientRef, signalFactory) {
|
|
4
|
+
if (signalFactory !== void 0) {
|
|
5
|
+
return toSvelteStoreFactory(signalOrClientRef, signalFactory);
|
|
6
|
+
}
|
|
7
|
+
return toSvelteStoreDirect(signalOrClientRef);
|
|
8
|
+
}
|
|
9
|
+
function toSvelteStoreDirect(signal) {
|
|
10
|
+
return readable(
|
|
11
|
+
{
|
|
12
|
+
data: signal.get(),
|
|
13
|
+
loading: signal.loading,
|
|
14
|
+
error: signal.error,
|
|
15
|
+
refreshing: signal.refreshing,
|
|
16
|
+
nextCursor: signal.nextCursor ?? null
|
|
17
|
+
},
|
|
18
|
+
(set) => {
|
|
19
|
+
const unsub = signal.subscribe((value) => {
|
|
20
|
+
set({
|
|
21
|
+
data: value,
|
|
22
|
+
loading: signal.loading,
|
|
23
|
+
error: signal.error,
|
|
24
|
+
refreshing: signal.refreshing,
|
|
25
|
+
nextCursor: signal.nextCursor ?? null
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
return () => {
|
|
29
|
+
unsub();
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
function toSvelteStoreFactory(clientRef, signalFactory) {
|
|
35
|
+
return readable(
|
|
36
|
+
{ data: void 0, loading: true, error: null, refreshing: false, nextCursor: null },
|
|
37
|
+
(set) => {
|
|
38
|
+
let currentSignal = null;
|
|
39
|
+
let currentUnsubSignal = null;
|
|
40
|
+
const unsubClient = clientRef.subscribe((client) => {
|
|
41
|
+
currentUnsubSignal?.();
|
|
42
|
+
currentSignal?.dispose();
|
|
43
|
+
currentSignal = signalFactory(client);
|
|
44
|
+
set({
|
|
45
|
+
data: currentSignal.get(),
|
|
46
|
+
loading: currentSignal.loading,
|
|
47
|
+
error: currentSignal.error,
|
|
48
|
+
refreshing: currentSignal.refreshing,
|
|
49
|
+
nextCursor: currentSignal.nextCursor ?? null
|
|
50
|
+
});
|
|
51
|
+
currentUnsubSignal = currentSignal.subscribe((value) => {
|
|
52
|
+
set({
|
|
53
|
+
data: value,
|
|
54
|
+
loading: currentSignal.loading,
|
|
55
|
+
error: currentSignal.error,
|
|
56
|
+
refreshing: currentSignal.refreshing,
|
|
57
|
+
nextCursor: currentSignal.nextCursor ?? null
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
return () => {
|
|
62
|
+
unsubClient();
|
|
63
|
+
currentUnsubSignal?.();
|
|
64
|
+
currentSignal?.dispose();
|
|
65
|
+
currentSignal = null;
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
export {
|
|
71
|
+
toSvelteStore
|
|
72
|
+
};
|
|
73
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/toSvelteStore.ts"],"sourcesContent":["/**\n * Convert DataFn signal to Svelte store\n */\n\nimport type { DatafnSignal, DatafnError } from \"@datafn/core\";\nimport type { Readable } from \"svelte/store\";\nimport { readable } from \"svelte/store\";\n\n/**\n * A client reference that can notify subscribers when the underlying client\n * changes. Compatible with `{ subscribe: client.subscribeClient }` from a\n * unified `createDatafnClient`, or any Svelte writable store holding a client.\n */\nexport type ClientRef<C> = {\n subscribe(fn: (client: C) => void): () => void;\n};\n\n/**\n * Store type that wraps signal value with state properties.\n * Returned by both overloads.\n */\nexport type DatafnSvelteStore<T> = Readable<{\n data: T;\n loading: boolean;\n error: DatafnError | null;\n refreshing: boolean;\n nextCursor?: string | null;\n}>;\n\n/**\n * Convert a lifecycle-aware DataFn signal directly to a Svelte readable store.\n *\n * Signals from `createDatafnClient` are lifecycle-aware — they survive\n * `switchContext()` and rebind automatically. No factory pattern or `clientRef`\n * is needed.\n *\n * ```typescript\n * const todosStore = toSvelteStore(\n * client.todos.signal({ sort: [\"-createdAt\"] })\n * );\n * // Template: {#each $todosStore.data ?? [] as todo}\n * // Also: {#if $todosStore.loading} Loading... {/if}\n * ```\n */\nexport function toSvelteStore<T>(signal: DatafnSignal<T>): DatafnSvelteStore<T>;\n\n/**\n * Convert a DataFn signal factory to a reactive Svelte readable store.\n *\n * Accepts a `clientRef` (anything with a `subscribe(fn)` API) and a\n * `signalFactory` that creates a new signal from the current client. The store\n * automatically disposes the old signal and creates a fresh one whenever the\n * client changes, preventing memory leaks and ensuring signals are always\n * bound to the active client.\n */\nexport function toSvelteStore<T, C>(\n clientRef: ClientRef<C>,\n signalFactory: (client: C) => DatafnSignal<T>,\n): DatafnSvelteStore<T>;\n\nexport function toSvelteStore<T, C = any>(\n signalOrClientRef: DatafnSignal<T> | ClientRef<C>,\n signalFactory?: (client: C) => DatafnSignal<T>,\n): DatafnSvelteStore<T> {\n if (signalFactory !== undefined) {\n return toSvelteStoreFactory(signalOrClientRef as ClientRef<C>, signalFactory);\n }\n return toSvelteStoreDirect(signalOrClientRef as DatafnSignal<T>);\n}\n\n/**\n * Direct signal overload: wraps a DatafnSignal as a Svelte readable store.\n * The store emits `{ data, loading, error, refreshing }` on each update.\n * Does NOT call signal.dispose() on teardown — the signal's lifecycle is\n * managed by the LiveSignalRegistry, allowing multiple stores to share a\n * deduplicated signal safely.\n */\nfunction toSvelteStoreDirect<T>(signal: DatafnSignal<T>): DatafnSvelteStore<T> {\n return readable(\n {\n data: signal.get(),\n loading: signal.loading,\n error: signal.error,\n refreshing: signal.refreshing,\n nextCursor: signal.nextCursor ?? null,\n },\n (set) => {\n const unsub = signal.subscribe((value: T) => {\n set({\n data: value,\n loading: signal.loading,\n error: signal.error,\n refreshing: signal.refreshing,\n nextCursor: signal.nextCursor ?? null,\n });\n });\n return () => {\n unsub();\n };\n },\n );\n}\n\n/**\n * Factory overload: subscribes to clientRef, creates/disposes signal on each\n * client change. Returns a DatafnSvelteStore with { data, loading, error, refreshing }.\n */\nfunction toSvelteStoreFactory<T, C>(\n clientRef: ClientRef<C>,\n signalFactory: (client: C) => DatafnSignal<T>,\n): DatafnSvelteStore<T> {\n return readable(\n { data: undefined as unknown as T, loading: true as boolean, error: null as DatafnError | null, refreshing: false as boolean, nextCursor: null as string | null },\n (set) => {\n let currentSignal: DatafnSignal<T> | null = null;\n let currentUnsubSignal: (() => void) | null = null;\n\n const unsubClient = clientRef.subscribe((client) => {\n // Tear down previous signal\n currentUnsubSignal?.();\n currentSignal?.dispose();\n\n // Create a fresh signal from the new client\n currentSignal = signalFactory(client);\n\n // Emit the initial value immediately\n set({\n data: currentSignal.get(),\n loading: currentSignal.loading,\n error: currentSignal.error,\n refreshing: currentSignal.refreshing,\n nextCursor: currentSignal.nextCursor ?? null,\n });\n\n // Subscribe to future updates\n currentUnsubSignal = currentSignal.subscribe((value: T) => {\n set({\n data: value,\n loading: currentSignal!.loading,\n error: currentSignal!.error,\n refreshing: currentSignal!.refreshing,\n nextCursor: currentSignal!.nextCursor ?? null,\n });\n });\n });\n\n // Cleanup when all Svelte subscribers unsubscribe\n return () => {\n unsubClient();\n currentUnsubSignal?.();\n currentSignal?.dispose();\n currentSignal = null;\n };\n },\n );\n}\n"],"mappings":";AAMA,SAAS,gBAAgB;AAsDlB,SAAS,cACd,mBACA,eACsB;AACtB,MAAI,kBAAkB,QAAW;AAC/B,WAAO,qBAAqB,mBAAmC,aAAa;AAAA,EAC9E;AACA,SAAO,oBAAoB,iBAAoC;AACjE;AASA,SAAS,oBAAuB,QAA+C;AAC7E,SAAO;AAAA,IACL;AAAA,MACE,MAAM,OAAO,IAAI;AAAA,MACjB,SAAS,OAAO;AAAA,MAChB,OAAO,OAAO;AAAA,MACd,YAAY,OAAO;AAAA,MACnB,YAAY,OAAO,cAAc;AAAA,IACnC;AAAA,IACA,CAAC,QAAQ;AACP,YAAM,QAAQ,OAAO,UAAU,CAAC,UAAa;AAC3C,YAAI;AAAA,UACF,MAAM;AAAA,UACN,SAAS,OAAO;AAAA,UAChB,OAAO,OAAO;AAAA,UACd,YAAY,OAAO;AAAA,UACnB,YAAY,OAAO,cAAc;AAAA,QACnC,CAAC;AAAA,MACH,CAAC;AACD,aAAO,MAAM;AACX,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACF;AAMA,SAAS,qBACP,WACA,eACsB;AACtB,SAAO;AAAA,IACL,EAAE,MAAM,QAA2B,SAAS,MAAiB,OAAO,MAA4B,YAAY,OAAkB,YAAY,KAAsB;AAAA,IAChK,CAAC,QAAQ;AACP,UAAI,gBAAwC;AAC5C,UAAI,qBAA0C;AAE9C,YAAM,cAAc,UAAU,UAAU,CAAC,WAAW;AAElD,6BAAqB;AACrB,uBAAe,QAAQ;AAGvB,wBAAgB,cAAc,MAAM;AAGpC,YAAI;AAAA,UACF,MAAM,cAAc,IAAI;AAAA,UACxB,SAAS,cAAc;AAAA,UACvB,OAAO,cAAc;AAAA,UACrB,YAAY,cAAc;AAAA,UAC1B,YAAY,cAAc,cAAc;AAAA,QAC1C,CAAC;AAGD,6BAAqB,cAAc,UAAU,CAAC,UAAa;AACzD,cAAI;AAAA,YACF,MAAM;AAAA,YACN,SAAS,cAAe;AAAA,YACxB,OAAO,cAAe;AAAA,YACtB,YAAY,cAAe;AAAA,YAC3B,YAAY,cAAe,cAAc;AAAA,UAC3C,CAAC;AAAA,QACH,CAAC;AAAA,MACH,CAAC;AAGD,aAAO,MAAM;AACX,oBAAY;AACZ,6BAAqB;AACrB,uBAAe,QAAQ;AACvB,wBAAgB;AAAA,MAClB;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@datafn/svelte",
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Svelte adapter for DataFn signals",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsup",
|
|
18
|
+
"test": "vitest run",
|
|
19
|
+
"test:watch": "vitest"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@datafn/core": "*",
|
|
23
|
+
"svelte": "^4.0.0"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"tsup": "^8.0.0",
|
|
27
|
+
"typescript": "^5.3.0",
|
|
28
|
+
"vitest": "^1.0.0"
|
|
29
|
+
},
|
|
30
|
+
"peerDependencies": {
|
|
31
|
+
"svelte": "^3.0.0 || ^4.0.0"
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"dist"
|
|
35
|
+
],
|
|
36
|
+
"author": "21n",
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"bugs": {
|
|
39
|
+
"url": "https://github.com/21nCo/super-functions/issues"
|
|
40
|
+
},
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "git+https://github.com/21nCo/super-functions.git",
|
|
44
|
+
"directory": "datafn/svelte"
|
|
45
|
+
},
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"access": "public"
|
|
48
|
+
}
|
|
49
|
+
}
|