@weave-framework/data 0.2.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 Aidas Josas
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.
@@ -0,0 +1,139 @@
1
+ /**
2
+ * @weave-framework/data — signal-native async data. Zero third-party deps (native `fetch`).
3
+ *
4
+ * `resource(source, fetcher)` turns an async fetch into reactive `{ data, loading,
5
+ * error }` signals: it refetches whenever `source` changes and cancels the
6
+ * in-flight request (AbortController) on change or unmount — so you never wire up
7
+ * loading flags or race-condition guards by hand. `createClient()` is an optional
8
+ * thin wrapper over `fetch` (base URL, default headers, JSON, error hook) whose
9
+ * methods drop straight into a resource fetcher.
10
+ *
11
+ * This is deliberately NOT an Angular-style HttpClient (no RxJS) — just the few
12
+ * primitives an app actually needs, built on signals. It does ship a small
13
+ * functional-interceptor chain (`(req, next) => Promise<Response>`), the zero-RxJS
14
+ * analog of Angular's `HttpInterceptorFn`, for auth/logging/retry/caching.
15
+ */
16
+ /** Extra info handed to a fetcher: an abort signal + whether this is a manual refetch. */
17
+ export interface FetchInfo {
18
+ signal: AbortSignal;
19
+ refetching: boolean;
20
+ }
21
+ /** Produces the data for a resource. Gets the (non-null) source value + abort info. */
22
+ export type Fetcher<S, T> = (value: S, info: FetchInfo) => Promise<T> | T;
23
+ export interface ResourceOptions<T> {
24
+ /** Seed value before the first fetch resolves (data() returns this meanwhile). */
25
+ initialValue?: T;
26
+ }
27
+ /** Reactive view of an async value. All accessors are signals — call them to read. */
28
+ export interface Resource<T> {
29
+ /** Latest resolved value, or the initial/undefined value while pending. Reactive. */
30
+ data: () => T | undefined;
31
+ /** True while a fetch is in flight. Reactive. */
32
+ loading: () => boolean;
33
+ /** The last rejection, or undefined. Cleared at the start of each fetch. Reactive. */
34
+ error: () => unknown;
35
+ /** Re-run the fetcher with the current source (info.refetching = true). */
36
+ refetch: () => void;
37
+ /** Set `data` directly without fetching (optimistic update / cache write). */
38
+ mutate: (value: T | undefined) => void;
39
+ }
40
+ /** A source value of `undefined | null | false` means "not ready" — the fetcher is skipped. */
41
+ type SourceValue<S> = S | undefined | null | false;
42
+ export declare function resource<T>(fetcher: Fetcher<true, T>, options?: ResourceOptions<T>): Resource<T>;
43
+ export declare function resource<S, T>(source: () => SourceValue<S>, fetcher: Fetcher<S, T>, options?: ResourceOptions<T>): Resource<T>;
44
+ /** Reactive state for an async action (a form submit / mutation). */
45
+ export interface Action<I, T> {
46
+ /** Run the action with `input`. Resolves/rejects with the action's own result. */
47
+ run: (input: I) => Promise<T>;
48
+ /** True while a run is in flight. Reactive. */
49
+ pending: () => boolean;
50
+ /** The last rejection, or undefined. Cleared at the start of each run. Reactive. */
51
+ error: () => unknown;
52
+ /** The latest resolved result, or undefined. Reactive. */
53
+ result: () => T | undefined;
54
+ }
55
+ /**
56
+ * Wrap an async action (submit / mutation) with reactive `pending`/`error`/`result`
57
+ * — the write-side counterpart to `resource` (which is for reads). The Weave analog
58
+ * of React's `useActionState`. If runs overlap, only the **latest** updates the
59
+ * signals (a stale slow run can't clobber a newer one); every caller still gets its
60
+ * own promise result back from `run`.
61
+ */
62
+ export declare function action<I = void, T = unknown>(fn: (input: I) => Promise<T> | T): Action<I, T>;
63
+ /** A base value with optimistic updates folded in until the real value reconciles. */
64
+ export interface Optimistic<T, U> {
65
+ /** `base` with every in-flight optimistic update applied via the reducer. Reactive. */
66
+ value: () => T;
67
+ /** Push an optimistic update; cleared automatically when `base` next changes. */
68
+ add: (optimistic: U) => void;
69
+ }
70
+ /**
71
+ * Show an optimistic value over `base` while a mutation is in flight, reconciling
72
+ * automatically when the real `base` changes — the Weave analog of React's
73
+ * `useOptimistic`. `reduce` folds each pending update onto the current value
74
+ * (default: replace). Must be called inside an owner (component `setup`) so its
75
+ * internal watcher disposes on unmount.
76
+ */
77
+ export declare function optimistic<T, U = T>(base: () => T, reduce?: (current: T, optimistic: U) => T): Optimistic<T, U>;
78
+ /** Thrown when a response has a non-2xx status. Carries the raw `Response`. */
79
+ export declare class HttpError extends Error {
80
+ status: number;
81
+ statusText: string;
82
+ response: Response;
83
+ constructor(status: number, statusText: string, response: Response);
84
+ }
85
+ /**
86
+ * A mutable request descriptor passed down the interceptor chain. Mutate it
87
+ * in place (`req.headers.set(…)`) or hand `next` a fresh one — both work.
88
+ */
89
+ export interface WeaveRequest {
90
+ /** Fully-resolved URL (baseUrl + path + query string). */
91
+ url: string;
92
+ method: string;
93
+ /** Mutable headers — set/append before calling `next`. */
94
+ headers: Headers;
95
+ body: BodyInit | null;
96
+ /** The remaining `RequestInit` fields (signal, credentials, mode, …). */
97
+ init: RequestInit;
98
+ }
99
+ /** Performs the request (the next interceptor, or finally the real `fetch`). */
100
+ export type RequestHandler = (req: WeaveRequest) => Promise<Response>;
101
+ /**
102
+ * A functional interceptor — the zero-RxJS analog of Angular's `HttpInterceptorFn`.
103
+ * Wrap `next(req)` to read/replace the request, inspect or retry the response, or
104
+ * short-circuit (return a `Response` without calling `next`). Interceptors run in
105
+ * the order given; the first is outermost.
106
+ */
107
+ export type Interceptor = (req: WeaveRequest, next: RequestHandler) => Promise<Response>;
108
+ export interface ClientOptions {
109
+ /** Prepended to every request path. */
110
+ baseUrl?: string;
111
+ /** Default headers, or a function called per request (e.g. for a fresh auth token). */
112
+ headers?: HeadersInit | (() => HeadersInit);
113
+ /** Called with any thrown error before it is re-thrown (logging / global handling). */
114
+ onError?: (error: unknown) => void;
115
+ /** Functional interceptor chain (auth/logging/retry/cache). First = outermost. */
116
+ interceptors?: Interceptor[];
117
+ /** Override the fetch implementation (tests / SSR). Defaults to global `fetch`. */
118
+ fetch?: typeof fetch;
119
+ }
120
+ export interface RequestOptions extends Omit<RequestInit, 'body' | 'method'> {
121
+ /** Body shortcut: JSON-stringified, with `Content-Type: application/json` set. */
122
+ json?: unknown;
123
+ /** Raw body (use instead of `json` for FormData / text / blobs). */
124
+ body?: BodyInit | null;
125
+ /** Query parameters appended to the URL. */
126
+ params?: Record<string, string | number | boolean>;
127
+ }
128
+ export interface Client {
129
+ request<T = unknown>(method: string, path: string, opts?: RequestOptions): Promise<T>;
130
+ get<T = unknown>(path: string, opts?: RequestOptions): Promise<T>;
131
+ post<T = unknown>(path: string, body?: unknown, opts?: RequestOptions): Promise<T>;
132
+ put<T = unknown>(path: string, body?: unknown, opts?: RequestOptions): Promise<T>;
133
+ patch<T = unknown>(path: string, body?: unknown, opts?: RequestOptions): Promise<T>;
134
+ delete<T = unknown>(path: string, opts?: RequestOptions): Promise<T>;
135
+ }
136
+ /** Create a small fetch client. Its methods are ready-made resource fetchers. */
137
+ export declare function createClient(options?: ClientOptions): Client;
138
+ export {};
139
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js ADDED
@@ -0,0 +1,224 @@
1
+ /**
2
+ * @weave-framework/data — signal-native async data. Zero third-party deps (native `fetch`).
3
+ *
4
+ * `resource(source, fetcher)` turns an async fetch into reactive `{ data, loading,
5
+ * error }` signals: it refetches whenever `source` changes and cancels the
6
+ * in-flight request (AbortController) on change or unmount — so you never wire up
7
+ * loading flags or race-condition guards by hand. `createClient()` is an optional
8
+ * thin wrapper over `fetch` (base URL, default headers, JSON, error hook) whose
9
+ * methods drop straight into a resource fetcher.
10
+ *
11
+ * This is deliberately NOT an Angular-style HttpClient (no RxJS) — just the few
12
+ * primitives an app actually needs, built on signals. It does ship a small
13
+ * functional-interceptor chain (`(req, next) => Promise<Response>`), the zero-RxJS
14
+ * analog of Angular's `HttpInterceptorFn`, for auth/logging/retry/caching.
15
+ */
16
+ import { signal, effect, batch, onCleanup, watch } from '@weave-framework/runtime';
17
+ export function resource(a, b, c) {
18
+ // Two shapes: resource(fetcher, opts?) or resource(source, fetcher, opts?).
19
+ let source;
20
+ let fetcher;
21
+ let options;
22
+ if (typeof b === 'function') {
23
+ source = a;
24
+ fetcher = b;
25
+ options = c ?? {};
26
+ }
27
+ else {
28
+ source = () => true;
29
+ fetcher = a;
30
+ options = b ?? {};
31
+ }
32
+ const data = signal(options.initialValue);
33
+ const error = signal(undefined);
34
+ const loading = signal(false);
35
+ // A plain counter signal the effect subscribes to, so refetch() can re-trigger
36
+ // it even when `source` is unchanged.
37
+ const trigger = signal(0);
38
+ let pendingRefetch = false;
39
+ function load(value, refetching) {
40
+ const controller = new AbortController();
41
+ let cancelled = false;
42
+ // Registered on the owning effect's computation, so the next re-run (source
43
+ // change / refetch) or unmount aborts this in-flight request.
44
+ onCleanup(() => {
45
+ cancelled = true;
46
+ controller.abort();
47
+ });
48
+ batch(() => {
49
+ loading.set(true);
50
+ error.set(() => undefined);
51
+ });
52
+ // Defer the fetcher to a microtask so it never tracks signals in this effect.
53
+ Promise.resolve()
54
+ .then(() => fetcher(value, { signal: controller.signal, refetching }))
55
+ .then((result) => {
56
+ if (cancelled)
57
+ return;
58
+ batch(() => {
59
+ data.set(() => result);
60
+ loading.set(false);
61
+ });
62
+ }, (err) => {
63
+ // An abort is an intentional cancel, not an error to surface.
64
+ if (cancelled || (err && err.name === 'AbortError'))
65
+ return;
66
+ batch(() => {
67
+ error.set(() => err);
68
+ loading.set(false);
69
+ });
70
+ });
71
+ }
72
+ effect(() => {
73
+ trigger(); // subscribe so refetch() re-runs this
74
+ const value = source();
75
+ const refetching = pendingRefetch;
76
+ pendingRefetch = false;
77
+ if (value === undefined || value === null || value === false) {
78
+ loading.set(false); // not ready — keep last data, ensure not stuck loading
79
+ return;
80
+ }
81
+ load(value, refetching);
82
+ });
83
+ return {
84
+ data: () => data(),
85
+ loading: () => loading(),
86
+ error: () => error(),
87
+ refetch: () => {
88
+ pendingRefetch = true;
89
+ trigger.set((n) => n + 1);
90
+ },
91
+ mutate: (value) => data.set(() => value),
92
+ };
93
+ }
94
+ /**
95
+ * Wrap an async action (submit / mutation) with reactive `pending`/`error`/`result`
96
+ * — the write-side counterpart to `resource` (which is for reads). The Weave analog
97
+ * of React's `useActionState`. If runs overlap, only the **latest** updates the
98
+ * signals (a stale slow run can't clobber a newer one); every caller still gets its
99
+ * own promise result back from `run`.
100
+ */
101
+ export function action(fn) {
102
+ const pending = signal(false);
103
+ const error = signal(undefined);
104
+ const result = signal(undefined);
105
+ let runId = 0;
106
+ async function run(input) {
107
+ const id = ++runId;
108
+ batch(() => {
109
+ pending.set(true);
110
+ error.set(() => undefined);
111
+ });
112
+ try {
113
+ const value = await fn(input);
114
+ if (id === runId) {
115
+ batch(() => {
116
+ result.set(() => value);
117
+ pending.set(false);
118
+ });
119
+ }
120
+ return value;
121
+ }
122
+ catch (err) {
123
+ if (id === runId) {
124
+ batch(() => {
125
+ error.set(() => err);
126
+ pending.set(false);
127
+ });
128
+ }
129
+ throw err;
130
+ }
131
+ }
132
+ return {
133
+ run,
134
+ pending: () => pending(),
135
+ error: () => error(),
136
+ result: () => result(),
137
+ };
138
+ }
139
+ /**
140
+ * Show an optimistic value over `base` while a mutation is in flight, reconciling
141
+ * automatically when the real `base` changes — the Weave analog of React's
142
+ * `useOptimistic`. `reduce` folds each pending update onto the current value
143
+ * (default: replace). Must be called inside an owner (component `setup`) so its
144
+ * internal watcher disposes on unmount.
145
+ */
146
+ export function optimistic(base, reduce = (_, u) => u) {
147
+ const pending = signal([]);
148
+ // When the real base changes (the mutation's result landed), drop the overlay.
149
+ // `watch` fires on change only, never on creation — so the seed value is kept.
150
+ watch(base, () => {
151
+ pending.set([]);
152
+ });
153
+ return {
154
+ value: () => pending().reduce((acc, u) => reduce(acc, u), base()),
155
+ add: (u) => pending.set((list) => [...list, u]),
156
+ };
157
+ }
158
+ /* ──────────────────────────── createClient ──────────────────────────── */
159
+ /** Thrown when a response has a non-2xx status. Carries the raw `Response`. */
160
+ export class HttpError extends Error {
161
+ status;
162
+ statusText;
163
+ response;
164
+ constructor(status, statusText, response) {
165
+ super(`HTTP ${status} ${statusText}`);
166
+ this.status = status;
167
+ this.statusText = statusText;
168
+ this.response = response;
169
+ this.name = 'HttpError';
170
+ }
171
+ }
172
+ /** Create a small fetch client. Its methods are ready-made resource fetchers. */
173
+ export function createClient(options = {}) {
174
+ const doFetch = options.fetch ?? globalThis.fetch.bind(globalThis);
175
+ const base = options.baseUrl ?? '';
176
+ // The terminal handler actually hits the network; interceptors wrap around it
177
+ // (composed once, outermost-first). reduceRight folds the chain so the first
178
+ // interceptor sees `next` = the rest of the chain ending in `doFetch`.
179
+ const terminal = (req) => doFetch(req.url, { method: req.method, headers: req.headers, body: req.body, ...req.init });
180
+ const handler = (options.interceptors ?? []).reduceRight((next, interceptor) => (req) => interceptor(req, next), terminal);
181
+ async function request(method, path, opts = {}) {
182
+ const { json, params, headers, body, ...rest } = opts;
183
+ let url = base + path;
184
+ if (params) {
185
+ const qs = new URLSearchParams();
186
+ for (const k in params)
187
+ qs.set(k, String(params[k]));
188
+ url += (url.includes('?') ? '&' : '?') + qs.toString();
189
+ }
190
+ const defaults = typeof options.headers === 'function' ? options.headers() : options.headers;
191
+ const h = new Headers(defaults);
192
+ if (headers)
193
+ new Headers(headers).forEach((v, k) => h.set(k, v));
194
+ let finalBody = body ?? null;
195
+ if (json !== undefined) {
196
+ finalBody = JSON.stringify(json);
197
+ if (!h.has('Content-Type'))
198
+ h.set('Content-Type', 'application/json');
199
+ }
200
+ const req = { url, method, headers: h, body: finalBody, init: rest };
201
+ try {
202
+ // The chain returns the raw Response (including non-2xx) so an interceptor
203
+ // can inspect/retry it; the ok-check + parse stay here, outside the chain.
204
+ const res = await handler(req);
205
+ if (!res.ok)
206
+ throw new HttpError(res.status, res.statusText, res);
207
+ const ct = res.headers.get('content-type') ?? '';
208
+ return (ct.includes('application/json') ? await res.json() : await res.text());
209
+ }
210
+ catch (err) {
211
+ options.onError?.(err);
212
+ throw err;
213
+ }
214
+ }
215
+ return {
216
+ request,
217
+ get: (path, opts) => request('GET', path, opts),
218
+ post: (path, body, opts) => request('POST', path, { ...opts, json: body }),
219
+ put: (path, body, opts) => request('PUT', path, { ...opts, json: body }),
220
+ patch: (path, body, opts) => request('PATCH', path, { ...opts, json: body }),
221
+ delete: (path, opts) => request('DELETE', path, opts),
222
+ };
223
+ }
224
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@weave-framework/data",
3
+ "version": "0.2.0",
4
+ "description": "Weave data — signal-native async resources + a tiny fetch client. Zero third-party deps.",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "main": "./dist/index.js",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "engines": {
20
+ "node": ">=18"
21
+ },
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/weave-framework/weave.git",
25
+ "directory": "packages/data"
26
+ },
27
+ "publishConfig": {
28
+ "access": "public"
29
+ },
30
+ "dependencies": {
31
+ "@weave-framework/runtime": "0.2.0"
32
+ },
33
+ "license": "MIT"
34
+ }