@go-go-scope/adapter-svelte 2.7.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 thelinuxlich (Alisson Cavalcante Agiani)
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,73 @@
1
+ # @go-go-scope/adapter-svelte
2
+
3
+ Svelte 5 runes integration for go-go-scope. Provides reactive stores that work with Svelte's `$` prefix.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @go-go-scope/adapter-svelte
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```svelte
14
+ <script>
15
+ import { createScope, createTask, createChannel } from '@go-go-scope/adapter-svelte';
16
+
17
+ // Create a reactive scope
18
+ const scope = createScope({ name: 'my-component' });
19
+
20
+ // Execute tasks with reactive state
21
+ const task = createTask(async () => {
22
+ const response = await fetch('/api/data');
23
+ return response.json();
24
+ }, { immediate: true });
25
+
26
+ // Use channels for communication
27
+ const channel = createChannel<string>();
28
+
29
+ async function sendMessage() {
30
+ await channel.send('Hello!');
31
+ }
32
+ </script>
33
+
34
+ <div>
35
+ {#if $task.isLoading}
36
+ <p>Loading...</p>
37
+ {:else if $task.error}
38
+ <p>Error: {$task.error.message}</p>
39
+ {:else}
40
+ <p>Data: {$task.data}</p>
41
+ {/if}
42
+
43
+ <button onclick={sendMessage}>Send Message</button>
44
+ <p>Latest: {$channel.latest}</p>
45
+ </div>
46
+ ```
47
+
48
+ ## API
49
+
50
+ ### `createScope(options?)`
51
+ Creates a reactive scope that auto-disposes on component destroy.
52
+
53
+ ### `createTask(factory, options?)`
54
+ Creates a reactive task store with `data`, `error`, `isLoading`, `isReady`, and `execute()`.
55
+
56
+ ### `createParallel(factories, options?)`
57
+ Executes tasks in parallel with reactive progress tracking.
58
+
59
+ ### `createChannel(options?)`
60
+ Creates a reactive channel for Go-style communication.
61
+
62
+ ### `createBroadcast()`
63
+ Creates a reactive broadcast channel for pub/sub patterns.
64
+
65
+ ### `createPolling(factory, options)`
66
+ Creates a reactive polling mechanism.
67
+
68
+ ### `createStore(initialValue)`
69
+ Creates a simple reactive store compatible with Svelte's `$` prefix.
70
+
71
+ ## License
72
+
73
+ MIT
@@ -0,0 +1,280 @@
1
+ import * as svelte_store from 'svelte/store';
2
+ import { Readable } from 'svelte/store';
3
+ import { Result, Scope } from 'go-go-scope';
4
+ export { BroadcastChannel, Channel, Result, Scope } from 'go-go-scope';
5
+
6
+ /**
7
+ * Options for creating a scope
8
+ */
9
+ interface CreateScopeOptions {
10
+ /** Scope name for debugging */
11
+ name?: string;
12
+ /** Timeout in milliseconds */
13
+ timeout?: number;
14
+ }
15
+ /**
16
+ * Reactive scope with automatic cleanup
17
+ */
18
+ interface ReactiveScope extends Scope {
19
+ /** Whether the scope is currently active */
20
+ readonly isActive: Readable<boolean>;
21
+ }
22
+ /**
23
+ * Create a reactive scope that automatically disposes on component destroy.
24
+ *
25
+ * @example
26
+ * ```svelte
27
+ * <script>
28
+ * const s = createScope({ name: "my-component" });
29
+ *
30
+ * // Scope is automatically disposed when component is destroyed
31
+ * const [err, result] = await s.task(() => fetchData());
32
+ * </script>
33
+ * ```
34
+ */
35
+ declare function createScope(options?: CreateScopeOptions): ReactiveScope;
36
+ /**
37
+ * Options for creating a task
38
+ */
39
+ interface CreateTaskOptions<T> {
40
+ /** Task name for debugging */
41
+ name?: string;
42
+ /** Whether to execute immediately */
43
+ immediate?: boolean;
44
+ /** Timeout in milliseconds */
45
+ timeout?: number;
46
+ /** Retry options */
47
+ retry?: {
48
+ maxRetries?: number;
49
+ delay?: number;
50
+ };
51
+ /** Initial data value */
52
+ initialData?: T;
53
+ }
54
+ /**
55
+ * Reactive task state
56
+ */
57
+ interface TaskState<T> {
58
+ /** Current data (undefined if not loaded) */
59
+ readonly data: Readable<T | undefined>;
60
+ /** Error if task failed */
61
+ readonly error: Readable<Error | undefined>;
62
+ /** Whether task is currently running */
63
+ readonly isLoading: Readable<boolean>;
64
+ /** Whether task has been executed */
65
+ readonly isReady: Readable<boolean>;
66
+ /** Execute the task */
67
+ readonly execute: () => Promise<Result<Error, T>>;
68
+ }
69
+ /**
70
+ * Create a reactive task that integrates with Svelte's store system.
71
+ * Returns a store-like object that can be used with `$` prefix.
72
+ *
73
+ * @example
74
+ * ```svelte
75
+ * <script>
76
+ * const task = createTask(
77
+ * async () => fetchUser(userId),
78
+ * { immediate: true }
79
+ * );
80
+ * </script>
81
+ *
82
+ * {#if $task.isLoading}
83
+ * <p>Loading...</p>
84
+ * {:else if $task.error}
85
+ * <p>Error: {$task.error.message}</p>
86
+ * {:else}
87
+ * <p>{$task.data.name}</p>
88
+ * {/if}
89
+ * ```
90
+ */
91
+ declare function createTask<T>(factory: () => Promise<T>, options?: CreateTaskOptions<T>): TaskState<T>;
92
+ /**
93
+ * Options for parallel execution
94
+ */
95
+ interface CreateParallelOptions {
96
+ /** Concurrency limit */
97
+ concurrency?: number;
98
+ /** Whether to execute immediately */
99
+ immediate?: boolean;
100
+ }
101
+ /**
102
+ * Reactive parallel task state
103
+ */
104
+ interface ParallelState<T> {
105
+ /** Results from all tasks */
106
+ readonly results: Readable<(T | undefined)[]>;
107
+ /** Errors from failed tasks */
108
+ readonly errors: Readable<(Error | undefined)[]>;
109
+ /** Whether any task is running */
110
+ readonly isLoading: Readable<boolean>;
111
+ /** Progress percentage (0-100) */
112
+ readonly progress: Readable<number>;
113
+ /** Execute all tasks */
114
+ readonly execute: () => Promise<Result<Error, T>[]>;
115
+ }
116
+ /**
117
+ * Create reactive parallel task execution.
118
+ *
119
+ * @example
120
+ * ```svelte
121
+ * <script>
122
+ * const parallel = createParallel(
123
+ * urls.map(url => () => fetch(url).then(r => r.json())),
124
+ * { concurrency: 3, immediate: true }
125
+ * );
126
+ * </script>
127
+ *
128
+ * <progress value={$parallel.progress} max="100" />
129
+ * ```
130
+ */
131
+ declare function createParallel<T>(factories: (() => Promise<T>)[], options?: CreateParallelOptions): ParallelState<T>;
132
+ /**
133
+ * Options for creating a channel
134
+ */
135
+ interface CreateChannelOptions {
136
+ /** Buffer size */
137
+ bufferSize?: number;
138
+ /** Maximum history to keep */
139
+ historySize?: number;
140
+ }
141
+ /**
142
+ * Reactive channel state
143
+ */
144
+ interface ChannelState<T> {
145
+ /** Latest received value */
146
+ readonly latest: Readable<T | undefined>;
147
+ /** History of values */
148
+ readonly history: Readable<readonly T[]>;
149
+ /** Whether channel is closed */
150
+ readonly isClosed: Readable<boolean>;
151
+ /** Send a value */
152
+ readonly send: (value: T) => Promise<void>;
153
+ /** Close the channel */
154
+ readonly close: () => void;
155
+ }
156
+ /**
157
+ * Create a reactive channel for Go-style communication.
158
+ *
159
+ * @example
160
+ * ```svelte
161
+ * <script>
162
+ * const ch = createChannel<string>();
163
+ *
164
+ * async function sendMessage() {
165
+ * await ch.send("Hello!");
166
+ * }
167
+ * </script>
168
+ *
169
+ * <p>Latest: {$ch.latest}</p>
170
+ * <button onclick={sendMessage}>Send</button>
171
+ * ```
172
+ */
173
+ declare function createChannel<T>(options?: CreateChannelOptions): ChannelState<T>;
174
+ /**
175
+ * Reactive broadcast state
176
+ */
177
+ interface BroadcastState<T> {
178
+ /** Latest broadcasted value */
179
+ readonly latest: Readable<T | undefined>;
180
+ /** Subscribe to broadcasts */
181
+ readonly subscribe: (callback: (value: T) => void) => {
182
+ unsubscribe: () => void;
183
+ };
184
+ /** Broadcast a value */
185
+ readonly broadcast: (value: T) => void;
186
+ }
187
+ /**
188
+ * Create a reactive broadcast channel.
189
+ *
190
+ * @example
191
+ * ```svelte
192
+ * <script>
193
+ * const bus = createBroadcast<string>();
194
+ *
195
+ * // Subscribe
196
+ * bus.subscribe(msg => console.log(msg));
197
+ *
198
+ * function send() {
199
+ * bus.broadcast("Hello!");
200
+ * }
201
+ * </script>
202
+ *
203
+ * <p>Latest: {$bus.latest}</p>
204
+ * ```
205
+ */
206
+ declare function createBroadcast<T>(): BroadcastState<T>;
207
+ /**
208
+ * Options for polling
209
+ */
210
+ interface CreatePollingOptions {
211
+ /** Interval in milliseconds */
212
+ interval: number;
213
+ /** Whether to start immediately */
214
+ immediate?: boolean;
215
+ /** Continue when tab is hidden */
216
+ continueOnHidden?: boolean;
217
+ }
218
+ /**
219
+ * Reactive polling state
220
+ */
221
+ interface PollingState<T> {
222
+ /** Latest data */
223
+ readonly data: Readable<T | undefined>;
224
+ /** Error if polling failed */
225
+ readonly error: Readable<Error | undefined>;
226
+ /** Whether currently polling */
227
+ readonly isPolling: Readable<boolean>;
228
+ /** Number of polls completed */
229
+ readonly pollCount: Readable<number>;
230
+ /** Start polling */
231
+ readonly start: () => void;
232
+ /** Stop polling */
233
+ readonly stop: () => void;
234
+ }
235
+ /**
236
+ * Create a reactive polling mechanism.
237
+ *
238
+ * @example
239
+ * ```svelte
240
+ * <script>
241
+ * const poller = createPolling(
242
+ * async () => fetchLatestData(),
243
+ * { interval: 5000, immediate: true }
244
+ * );
245
+ * </script>
246
+ *
247
+ * {#if $poller.data}
248
+ * <p>{$poller.data}</p>
249
+ * {/if}
250
+ * <button onclick={() => $poller.stop()}>Stop</button>
251
+ * ```
252
+ */
253
+ declare function createPolling<T>(factory: () => Promise<T>, options: CreatePollingOptions): PollingState<T>;
254
+ /**
255
+ * Create a reactive value that can be subscribed to (Svelte store contract).
256
+ * Compatible with Svelte 5's `$` prefix.
257
+ *
258
+ * @example
259
+ * ```svelte
260
+ * <script>
261
+ * const count = createStore(0);
262
+ *
263
+ * function increment() {
264
+ * $count++;
265
+ * }
266
+ * </script>
267
+ *
268
+ * <button onclick={increment}>
269
+ * Count: {$count}
270
+ * </button>
271
+ * ```
272
+ */
273
+ declare function createStore<T>(initialValue: T): {
274
+ subscribe: (this: void, run: svelte_store.Subscriber<T>, invalidate?: () => void) => svelte_store.Unsubscriber;
275
+ set: (this: void, value: T) => void;
276
+ update: (this: void, updater: svelte_store.Updater<T>) => void;
277
+ };
278
+
279
+ export { createBroadcast, createChannel, createParallel, createPolling, createScope, createStore, createTask };
280
+ export type { BroadcastState, ChannelState, CreateChannelOptions, CreateParallelOptions, CreatePollingOptions, CreateScopeOptions, CreateTaskOptions, ParallelState, PollingState, ReactiveScope, TaskState };
package/dist/index.mjs ADDED
@@ -0,0 +1,222 @@
1
+ import { scope, BroadcastChannel } from 'go-go-scope';
2
+ import { onDestroy } from 'svelte';
3
+ import { writable } from 'svelte/store';
4
+
5
+ function createScope(options = {}) {
6
+ const isActive = writable(true);
7
+ const s = scope({
8
+ name: options.name,
9
+ timeout: options.timeout
10
+ });
11
+ onDestroy(() => {
12
+ isActive.set(false);
13
+ s[Symbol.asyncDispose]().catch(() => {
14
+ });
15
+ });
16
+ return Object.assign(s, {
17
+ isActive: { subscribe: isActive.subscribe }
18
+ });
19
+ }
20
+ function createTask(factory, options = {}) {
21
+ const s = createScope({ name: options.name ?? "createTask" });
22
+ const data = writable(options.initialData);
23
+ const error = writable(void 0);
24
+ const isLoading = writable(false);
25
+ const isReady = writable(false);
26
+ const execute = async () => {
27
+ isLoading.set(true);
28
+ error.set(void 0);
29
+ try {
30
+ const [err, result] = await s.task(
31
+ async () => await factory(),
32
+ {
33
+ timeout: options.timeout,
34
+ retry: options.retry
35
+ }
36
+ );
37
+ if (err) {
38
+ error.set(err instanceof Error ? err : new Error(String(err)));
39
+ data.set(void 0);
40
+ return [err, void 0];
41
+ }
42
+ data.set(result);
43
+ isReady.set(true);
44
+ return [void 0, result];
45
+ } finally {
46
+ isLoading.set(false);
47
+ }
48
+ };
49
+ if (options.immediate) {
50
+ execute();
51
+ }
52
+ return {
53
+ data: { subscribe: data.subscribe },
54
+ error: { subscribe: error.subscribe },
55
+ isLoading: { subscribe: isLoading.subscribe },
56
+ isReady: { subscribe: isReady.subscribe },
57
+ execute
58
+ };
59
+ }
60
+ function createParallel(factories, options = {}) {
61
+ const s = createScope({ name: "createParallel" });
62
+ const results = writable([]);
63
+ const errors = writable([]);
64
+ const isLoading = writable(false);
65
+ const progress = writable(0);
66
+ const execute = async () => {
67
+ isLoading.set(true);
68
+ progress.set(0);
69
+ results.set(new Array(factories.length).fill(void 0));
70
+ errors.set(new Array(factories.length).fill(void 0));
71
+ try {
72
+ const taskFactories = factories.map((factory, index) => {
73
+ return async () => {
74
+ const [err, result] = await s.task(async () => await factory());
75
+ if (err) {
76
+ errors.update((e) => {
77
+ e[index] = err instanceof Error ? err : new Error(String(err));
78
+ return e;
79
+ });
80
+ } else {
81
+ results.update((r) => {
82
+ r[index] = result;
83
+ return r;
84
+ });
85
+ }
86
+ progress.set(Math.round((index + 1) / factories.length * 100));
87
+ return [err, result];
88
+ };
89
+ });
90
+ return await s.parallel(taskFactories, {
91
+ concurrency: options.concurrency
92
+ });
93
+ } finally {
94
+ isLoading.set(false);
95
+ }
96
+ };
97
+ if (options.immediate) {
98
+ execute();
99
+ }
100
+ return {
101
+ results: { subscribe: results.subscribe },
102
+ errors: { subscribe: errors.subscribe },
103
+ isLoading: { subscribe: isLoading.subscribe },
104
+ progress: { subscribe: progress.subscribe },
105
+ execute
106
+ };
107
+ }
108
+ function createChannel(options = {}) {
109
+ const s = createScope({ name: "createChannel" });
110
+ const ch = s.channel(options.bufferSize ?? 0);
111
+ const latest = writable(void 0);
112
+ const history = writable([]);
113
+ const isClosed = writable(false);
114
+ const receiveLoop = async () => {
115
+ for await (const value of ch) {
116
+ latest.set(value);
117
+ history.update((h) => [...h.slice(-(options.historySize ?? 100)), value]);
118
+ }
119
+ isClosed.set(true);
120
+ };
121
+ receiveLoop();
122
+ return {
123
+ latest: { subscribe: latest.subscribe },
124
+ history: { subscribe: history.subscribe },
125
+ isClosed: { subscribe: isClosed.subscribe },
126
+ send: async (value) => {
127
+ await ch.send(value);
128
+ },
129
+ close: () => {
130
+ ch.close();
131
+ }
132
+ };
133
+ }
134
+ function createBroadcast() {
135
+ const bc = new BroadcastChannel();
136
+ const latest = writable(void 0);
137
+ return {
138
+ latest: { subscribe: latest.subscribe },
139
+ subscribe: (callback) => {
140
+ (async () => {
141
+ for await (const value of bc.subscribe()) {
142
+ latest.set(value);
143
+ callback(value);
144
+ }
145
+ })();
146
+ return {
147
+ unsubscribe: () => bc.close()
148
+ };
149
+ },
150
+ broadcast: async (value) => {
151
+ await bc.send(value);
152
+ latest.set(value);
153
+ }
154
+ };
155
+ }
156
+ function createPolling(factory, options) {
157
+ const s = createScope({ name: "createPolling" });
158
+ const data = writable(void 0);
159
+ const error = writable(void 0);
160
+ const isPolling = writable(false);
161
+ const pollCount = writable(0);
162
+ const poller = s.poll(
163
+ async () => {
164
+ try {
165
+ const result = await factory();
166
+ return { success: true, value: result };
167
+ } catch (e) {
168
+ return { success: false, error: e };
169
+ }
170
+ },
171
+ (result) => {
172
+ pollCount.update((c) => c + 1);
173
+ if (result.success) {
174
+ data.set(result.value);
175
+ } else {
176
+ error.set(result.error instanceof Error ? result.error : new Error(String(result.error)));
177
+ }
178
+ },
179
+ { interval: options.interval, immediate: options.immediate }
180
+ );
181
+ const updateStatus = () => {
182
+ const status = poller.status();
183
+ isPolling.set(status.running);
184
+ };
185
+ if (!options.continueOnHidden && typeof document !== "undefined") {
186
+ const handler = () => {
187
+ if (document.hidden) {
188
+ poller.stop();
189
+ updateStatus();
190
+ } else if (options.immediate !== false) {
191
+ poller.start();
192
+ updateStatus();
193
+ }
194
+ };
195
+ document.addEventListener("visibilitychange", handler);
196
+ }
197
+ updateStatus();
198
+ return {
199
+ data: { subscribe: data.subscribe },
200
+ error: { subscribe: error.subscribe },
201
+ isPolling: { subscribe: isPolling.subscribe },
202
+ pollCount: { subscribe: pollCount.subscribe },
203
+ start: () => {
204
+ poller.start();
205
+ updateStatus();
206
+ },
207
+ stop: () => {
208
+ poller.stop();
209
+ updateStatus();
210
+ }
211
+ };
212
+ }
213
+ function createStore(initialValue) {
214
+ const { subscribe, set, update } = writable(initialValue);
215
+ return {
216
+ subscribe,
217
+ set,
218
+ update
219
+ };
220
+ }
221
+
222
+ export { createBroadcast, createChannel, createParallel, createPolling, createScope, createStore, createTask };
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@go-go-scope/adapter-svelte",
3
+ "version": "2.7.0",
4
+ "description": "Svelte 5 runes integration for go-go-scope",
5
+ "type": "module",
6
+ "main": "./dist/index.mjs",
7
+ "types": "./dist/index.d.mts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.mts",
11
+ "default": "./dist/index.mjs"
12
+ }
13
+ },
14
+ "keywords": [
15
+ "go-go-scope",
16
+ "svelte",
17
+ "svelte5",
18
+ "runes",
19
+ "reactivity"
20
+ ],
21
+ "engines": {
22
+ "node": ">=24.0.0"
23
+ },
24
+ "author": "thelinuxlich",
25
+ "license": "MIT",
26
+ "peerDependencies": {
27
+ "svelte": "^5.0.0",
28
+ "go-go-scope": "2.7.0"
29
+ },
30
+ "devDependencies": {
31
+ "@biomejs/biome": "^2.4.4",
32
+ "@sveltejs/vite-plugin-svelte": "^4.0.0",
33
+ "@testing-library/svelte": "^5.2.0",
34
+ "@types/node": "^24",
35
+ "jsdom": "^24.0.0",
36
+ "pkgroll": "^2.26.3",
37
+ "svelte": "^5.0.0",
38
+ "typescript": "^5.9.3",
39
+ "vitest": "^4.0.18",
40
+ "go-go-scope": "2.7.0"
41
+ },
42
+ "scripts": {
43
+ "build": "pkgroll --clean-dist",
44
+ "lint": "biome check --write src/",
45
+ "test": "vitest run",
46
+ "clean": "rm -rf dist",
47
+ "typecheck": "tsc --noEmit"
48
+ }
49
+ }