summitjs 0.3.0 → 0.4.1
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/AGENTS.md +5 -5
- package/README.md +15 -6
- package/dist/net.cjs +26 -5
- package/dist/net.cjs.map +1 -1
- package/dist/net.d.cts +0 -14
- package/dist/net.d.ts +0 -14
- package/dist/net.js +23 -2
- package/dist/net.js.map +1 -1
- package/dist/summit-net.min.js +3 -0
- package/dist/summit-net.min.js.map +1 -0
- package/dist/summit.cjs +6 -5
- package/dist/summit.cjs.map +1 -1
- package/dist/summit.js +6 -5
- package/dist/summit.js.map +1 -1
- package/dist/summit.min.js +2 -2
- package/dist/summit.min.js.map +1 -1
- package/package.json +4 -4
package/AGENTS.md
CHANGED
|
@@ -6,14 +6,14 @@ small, CSP-safe interpreter evaluates the expressions, so only a fixed,
|
|
|
6
6
|
predictable vocabulary is allowed. This file tells you how to write Summit that
|
|
7
7
|
runs correctly the first time.
|
|
8
8
|
|
|
9
|
-
Full docs as one file: https://velofy.github.io/
|
|
10
|
-
Index: https://velofy.github.io/
|
|
9
|
+
Full docs as one file: https://velofy.github.io/summitjs/llms-full.txt
|
|
10
|
+
Index: https://velofy.github.io/summitjs/llms.txt
|
|
11
11
|
Any page as markdown: append `index.md` to its URL.
|
|
12
12
|
|
|
13
13
|
## Load it
|
|
14
14
|
|
|
15
15
|
```html
|
|
16
|
-
<script src="https://velofy.github.io/
|
|
16
|
+
<script src="https://velofy.github.io/summitjs/summit.min.js" defer></script>
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
## The model
|
|
@@ -98,8 +98,8 @@ prefixed classes (`s-btn`, `s-input`, `s-card`, `s-alert`, `s-tabs`, `s-dialog`,
|
|
|
98
98
|
and more). Theme everything by overriding CSS variables like `--accent`.
|
|
99
99
|
|
|
100
100
|
```html
|
|
101
|
-
<link rel="stylesheet" href="https://velofy.github.io/
|
|
101
|
+
<link rel="stylesheet" href="https://velofy.github.io/summitjs/assets/components.css">
|
|
102
102
|
```
|
|
103
103
|
|
|
104
|
-
See https://velofy.github.io/
|
|
104
|
+
See https://velofy.github.io/summitjs/components/ for every component with live
|
|
105
105
|
demos and copy-paste markup.
|
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<p align="center">
|
|
2
2
|
<picture>
|
|
3
|
-
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/velofy/
|
|
4
|
-
<img alt="Summit.js" src="https://raw.githubusercontent.com/velofy/
|
|
3
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/velofy/summitjs/main/docs/assets/logo-dark.svg">
|
|
4
|
+
<img alt="Summit.js" src="https://raw.githubusercontent.com/velofy/summitjs/main/docs/assets/logo-light.svg" width="104">
|
|
5
5
|
</picture>
|
|
6
6
|
</p>
|
|
7
7
|
|
|
@@ -22,11 +22,11 @@ Summit is designed so that an AI agent can write a working, good-looking fronten
|
|
|
22
22
|
- **HTML-first and local.** Behavior lives on the element it affects, so an agent edits one place and sees the result. No file graph to hold in context.
|
|
23
23
|
- **Predictable vocabulary.** A small, closed set of `s-` directives and `$` magics. There is one obvious way to do most things, which is exactly what a model does best.
|
|
24
24
|
- **Safe by construction.** Expressions are interpreted, never `eval`ed, so generated markup runs under a strict CSP and cannot reach outside its allowlist.
|
|
25
|
-
- **A UI library, included.** Accessible, token-themed components an agent (or a person) drops in and themes with CSS variables. See the [UI Library](https://velofy.github.io/
|
|
26
|
-
- **Built to be read by machines.** The whole framework is discoverable in one fetch: [`llms.txt`](https://velofy.github.io/
|
|
25
|
+
- **A UI library, included.** Accessible, token-themed components an agent (or a person) drops in and themes with CSS variables. See the [UI Library](https://velofy.github.io/summitjs/components/).
|
|
26
|
+
- **Built to be read by machines.** The whole framework is discoverable in one fetch: [`llms.txt`](https://velofy.github.io/summitjs/llms.txt) indexes every page, [`llms-full.txt`](https://velofy.github.io/summitjs/llms-full.txt) is the entire corpus as markdown, any page is available as markdown (append `index.md` to its URL) with a one-click "Copy for AI" button, and [`AGENTS.md`](./AGENTS.md) is a drop-in brief you can hand to your own agent.
|
|
27
27
|
|
|
28
28
|
```html
|
|
29
|
-
<script src="https://velofy.github.io/
|
|
29
|
+
<script src="https://velofy.github.io/summitjs/summit.min.js" defer></script>
|
|
30
30
|
|
|
31
31
|
<div s-data="{ open: false }">
|
|
32
32
|
<button @click="open = !open">Toggle</button>
|
|
@@ -55,7 +55,7 @@ The headline difference is the engine. Summit owns a real signal core, so a chan
|
|
|
55
55
|
Zero-build, drop in a script (auto-starts, exposes `window.Summit`):
|
|
56
56
|
|
|
57
57
|
```html
|
|
58
|
-
<script src="https://velofy.github.io/
|
|
58
|
+
<script src="https://velofy.github.io/summitjs/summit.min.js" defer></script>
|
|
59
59
|
```
|
|
60
60
|
|
|
61
61
|
With a bundler:
|
|
@@ -145,6 +145,8 @@ count.set(5); // logs 10
|
|
|
145
145
|
|
|
146
146
|
Summit ships an optional reactive data layer in `summitjs/net` (about 2.5KB gzip, separate from the core, so the base bundle stays lean). It is axios's essential parts, but a resource's `data`, `error`, `loading`, and `status` are signals, so the DOM updates itself. It also closes fetch's real-world footguns: non-2xx **throws**, out-of-order responses are discarded (latest wins), and in-flight requests **abort on unmount**.
|
|
147
147
|
|
|
148
|
+
With a bundler:
|
|
149
|
+
|
|
148
150
|
```js
|
|
149
151
|
import Summit from "summitjs";
|
|
150
152
|
import { net } from "summitjs/net";
|
|
@@ -152,6 +154,13 @@ Summit.plugin(net); // adds the $fetch magic and s-resource directive
|
|
|
152
154
|
Summit.start();
|
|
153
155
|
```
|
|
154
156
|
|
|
157
|
+
Or drop in a second script, no build step (it registers itself on the global):
|
|
158
|
+
|
|
159
|
+
```html
|
|
160
|
+
<script src="https://velofy.github.io/summitjs/summit.min.js" defer></script>
|
|
161
|
+
<script src="https://velofy.github.io/summitjs/summit-net.min.js" defer></script>
|
|
162
|
+
```
|
|
163
|
+
|
|
155
164
|
```html
|
|
156
165
|
<div s-data="{ users: $fetch.resource('/api/users', { immediate: true }) }">
|
|
157
166
|
<p s-show="users.loading">Loading…</p>
|
package/dist/net.cjs
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
var summitjs = require('summitjs');
|
|
4
4
|
|
|
5
|
+
// src/net/index.ts
|
|
6
|
+
|
|
7
|
+
// src/net/runtime.ts
|
|
8
|
+
var factory;
|
|
9
|
+
function setSignalFactory(fn) {
|
|
10
|
+
factory = fn;
|
|
11
|
+
}
|
|
12
|
+
function signal(initial) {
|
|
13
|
+
if (!factory) {
|
|
14
|
+
throw new Error(
|
|
15
|
+
'summitjs/net: reactivity is not wired up. Import it from "summitjs/net", or load summit-net.min.js after summit.min.js.'
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
return factory(initial);
|
|
19
|
+
}
|
|
20
|
+
|
|
5
21
|
// src/net/client.ts
|
|
6
22
|
var NetError = class extends Error {
|
|
7
23
|
constructor(message, init) {
|
|
@@ -112,11 +128,13 @@ function withParams(url, params) {
|
|
|
112
128
|
if (!suffix) return url;
|
|
113
129
|
return `${url}${url.includes("?") ? "&" : "?"}${suffix}`;
|
|
114
130
|
}
|
|
131
|
+
|
|
132
|
+
// src/net/resource.ts
|
|
115
133
|
function createResource(client, url, options = {}) {
|
|
116
|
-
const dataSig =
|
|
117
|
-
const errorSig =
|
|
118
|
-
const loadingSig =
|
|
119
|
-
const statusSig =
|
|
134
|
+
const dataSig = signal(void 0);
|
|
135
|
+
const errorSig = signal(void 0);
|
|
136
|
+
const loadingSig = signal(false);
|
|
137
|
+
const statusSig = signal("idle");
|
|
120
138
|
const { immediate, onCleanup, ...requestDefaults } = options;
|
|
121
139
|
let seq = 0;
|
|
122
140
|
let controller;
|
|
@@ -227,7 +245,7 @@ function makeResourceDirective(client) {
|
|
|
227
245
|
};
|
|
228
246
|
}
|
|
229
247
|
|
|
230
|
-
// src/net/
|
|
248
|
+
// src/net/registry.ts
|
|
231
249
|
var http = createClient();
|
|
232
250
|
function bindClient(client, ctx) {
|
|
233
251
|
return {
|
|
@@ -249,6 +267,9 @@ function createNet(options) {
|
|
|
249
267
|
return (summit) => register(summit, client);
|
|
250
268
|
}
|
|
251
269
|
|
|
270
|
+
// src/net/index.ts
|
|
271
|
+
setSignalFactory(summitjs.signal);
|
|
272
|
+
|
|
252
273
|
exports.NetError = NetError;
|
|
253
274
|
exports.createClient = createClient;
|
|
254
275
|
exports.createNet = createNet;
|
package/dist/net.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/net/client.ts","../src/net/resource.ts","../src/net/directive.ts","../src/net/index.ts"],"names":["signal"],"mappings":"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","file":"net.cjs","sourcesContent":["/**\n * summitjs/net — the HTTP client.\n *\n * A thin, dependency-free wrapper over `fetch` that fixes fetch's real-world\n * footguns: it throws a structured error on non-2xx, sends and parses JSON\n * automatically, serializes params, supports a baseURL, default headers,\n * per-request timeout, and cancellation. No eval, no codegen, so it stays\n * CSP-safe like the rest of Summit.\n */\n\n/** A structured error thrown for non-2xx responses, timeouts, and aborts. */\nexport class NetError extends Error {\n status: number;\n body: unknown;\n url: string;\n method: string;\n aborted: boolean;\n\n constructor(\n message: string,\n init: { status: number; body: unknown; url: string; method: string; aborted: boolean },\n ) {\n super(message);\n this.name = \"NetError\";\n this.status = init.status;\n this.body = init.body;\n this.url = init.url;\n this.method = init.method;\n this.aborted = init.aborted;\n }\n}\n\nexport type NetParams = Record<string, string | number | boolean | null | undefined>;\n\nexport interface NetRequest {\n method?: string;\n headers?: Record<string, string>;\n params?: NetParams;\n /** A value: objects/arrays are JSON-encoded; strings are sent as-is. */\n body?: unknown;\n /** Abort this request when the given signal aborts. */\n signal?: AbortSignal;\n /** Abort automatically after this many milliseconds. */\n timeout?: number;\n}\n\nexport interface NetClientOptions {\n baseURL?: string;\n /** Static headers, or a function evaluated per request (e.g. for auth tokens). */\n headers?: Record<string, string> | (() => Record<string, string>);\n timeout?: number;\n /** Inspect or replace the request just before it is sent. */\n beforeRequest?: (req: NetInit) => void | NetInit;\n /** Inspect or replace the response before the body is read. */\n afterResponse?: (res: Response) => void | Response;\n /** Called with every NetError (e.g. redirect on 401). Re-throws afterward. */\n onError?: (err: NetError) => void;\n}\n\nexport type NetInit = RequestInit & { url: string };\n\nexport interface NetClient {\n request<T = unknown>(path: string, options?: NetRequest): Promise<T>;\n get<T = unknown>(path: string, options?: NetRequest): Promise<T>;\n post<T = unknown>(path: string, body?: unknown, options?: NetRequest): Promise<T>;\n put<T = unknown>(path: string, body?: unknown, options?: NetRequest): Promise<T>;\n patch<T = unknown>(path: string, body?: unknown, options?: NetRequest): Promise<T>;\n delete<T = unknown>(path: string, options?: NetRequest): Promise<T>;\n /** The client's live defaults; mutate to reconfigure baseURL/headers at runtime. */\n options: NetClientOptions;\n}\n\nexport function createClient(defaults: NetClientOptions = {}): NetClient {\n async function request<T = unknown>(path: string, req: NetRequest = {}): Promise<T> {\n const controller = new AbortController();\n const timeoutMs = req.timeout ?? defaults.timeout;\n const timer = timeoutMs ? setTimeout(() => controller.abort(), timeoutMs) : undefined;\n // Chain an external signal onto our controller so both timeout and the\n // caller's own cancellation abort the same request.\n if (req.signal) {\n if (req.signal.aborted) controller.abort();\n else req.signal.addEventListener(\"abort\", () => controller.abort(), { once: true });\n }\n\n const method = (req.method ?? \"GET\").toUpperCase();\n const url = withParams(joinURL(defaults.baseURL, path), req.params);\n const baseHeaders = typeof defaults.headers === \"function\" ? defaults.headers() : defaults.headers;\n const headers: Record<string, string> = { ...baseHeaders, ...req.headers };\n\n let init: NetInit = { url, method, headers, signal: controller.signal };\n if (req.body !== undefined && method !== \"GET\" && method !== \"HEAD\") {\n if (!hasHeader(headers, \"content-type\") && typeof req.body !== \"string\") {\n headers[\"content-type\"] = \"application/json\";\n }\n init.body = typeof req.body === \"string\" ? req.body : JSON.stringify(req.body);\n }\n\n const patched = defaults.beforeRequest?.(init);\n if (patched) init = patched;\n\n try {\n let res = await fetch(init.url, init);\n const swapped = defaults.afterResponse?.(res);\n if (swapped) res = swapped;\n const body = await readBody(res);\n if (!res.ok) {\n throw new NetError(`Request failed with status ${res.status}`, {\n status: res.status,\n body,\n url: init.url,\n method,\n aborted: false,\n });\n }\n return body as T;\n } catch (err) {\n const e = normalizeError(err, init.url, method);\n defaults.onError?.(e);\n throw e;\n } finally {\n if (timer) clearTimeout(timer);\n }\n }\n\n const client: NetClient = {\n request,\n get: (path, options) => request(path, { ...options, method: \"GET\" }),\n post: (path, body, options) => request(path, { ...options, method: \"POST\", body }),\n put: (path, body, options) => request(path, { ...options, method: \"PUT\", body }),\n patch: (path, body, options) => request(path, { ...options, method: \"PATCH\", body }),\n delete: (path, options) => request(path, { ...options, method: \"DELETE\" }),\n options: defaults,\n };\n return client;\n}\n\nfunction normalizeError(err: unknown, url: string, method: string): NetError {\n if (err instanceof NetError) return err;\n const aborted = (err as { name?: string })?.name === \"AbortError\";\n return new NetError(aborted ? \"Request aborted\" : (err as Error)?.message ?? \"Network request failed\", {\n status: 0,\n body: undefined,\n url,\n method,\n aborted,\n });\n}\n\nasync function readBody(res: Response): Promise<unknown> {\n const type = res.headers.get(\"content-type\") ?? \"\";\n const text = await res.text();\n if (text === \"\") return undefined;\n if (type.includes(\"application/json\") || type.includes(\"+json\")) {\n try {\n return JSON.parse(text);\n } catch {\n return text; // malformed JSON: hand back the raw text rather than throwing\n }\n }\n return text;\n}\n\nfunction hasHeader(headers: Record<string, string>, name: string): boolean {\n const lower = name.toLowerCase();\n return Object.keys(headers).some((k) => k.toLowerCase() === lower);\n}\n\nfunction joinURL(base: string | undefined, path: string): string {\n if (!base || /^[a-z][a-z0-9+.-]*:\\/\\//i.test(path)) return path;\n return `${base.replace(/\\/+$/, \"\")}/${path.replace(/^\\/+/, \"\")}`;\n}\n\nfunction withParams(url: string, params?: NetParams): string {\n if (!params) return url;\n const query = new URLSearchParams();\n for (const [key, value] of Object.entries(params)) {\n if (value != null) query.set(key, String(value));\n }\n const suffix = query.toString();\n if (!suffix) return url;\n return `${url}${url.includes(\"?\") ? \"&\" : \"?\"}${suffix}`;\n}\n","/**\n * summitjs/net — the reactive resource.\n *\n * A resource wraps a request in Summit signals: `data`, `error`, `loading`,\n * and `status` are exposed as auto-unwrapping getters, so reading them inside a\n * directive (`s-text=\"user.data.name\"`, `s-show=\"user.loading\"`) subscribes the\n * DOM to exactly that field. This is the key win over a promise wrapper: the\n * view updates itself.\n *\n * It also closes the footguns the stress test found:\n * - out-of-order responses: a monotonic sequence means the latest wins;\n * - non-2xx: surfaces through `error` (the client already throws);\n * - unmount leaks: `onCleanup` aborts the in-flight request;\n * - unhandled rejections: refetch never rejects — failures land in `error`.\n */\n\n// Import the signal factory from the public entry (not a relative core path) so\n// the net bundle shares Summit's ONE reactivity runtime instead of bundling a\n// second copy — otherwise resource signals would not drive the core's DOM.\nimport { signal } from \"summitjs\";\nimport type { NetClient, NetError, NetRequest } from \"./client.js\";\n\nexport type ResourceStatus = \"idle\" | \"loading\" | \"success\" | \"error\";\n\nexport interface ResourceOptions extends NetRequest {\n /** Fetch immediately on creation. Default true. */\n immediate?: boolean;\n /** Register a teardown callback (Summit passes the element's cleanup here). */\n onCleanup?: (fn: () => void) => void;\n}\n\nexport interface MutateOptions {\n /** Persist the optimistic change; on rejection the previous value is restored. */\n request?: () => Promise<unknown>;\n /** Roll back on a failed request. Default true. */\n rollbackOnError?: boolean;\n}\n\nexport interface Resource<T> {\n readonly data: T | undefined;\n readonly error: NetError | undefined;\n readonly loading: boolean;\n readonly status: ResourceStatus;\n /** Re-run the request (optionally overriding params/headers). Never rejects. */\n refetch(overrides?: NetRequest): Promise<T | undefined>;\n /** Optimistically set data; with `request`, roll back if the request fails. */\n mutate(next: T | ((prev: T | undefined) => T), options?: MutateOptions): void;\n /** Cancel the in-flight request and return to idle. */\n abort(): void;\n}\n\nexport function createResource<T = unknown>(\n client: NetClient,\n url: string | (() => string),\n options: ResourceOptions = {},\n): Resource<T> {\n const dataSig = signal<T | undefined>(undefined);\n const errorSig = signal<NetError | undefined>(undefined);\n const loadingSig = signal(false);\n const statusSig = signal<ResourceStatus>(\"idle\");\n\n const { immediate, onCleanup, ...requestDefaults } = options;\n let seq = 0;\n let controller: AbortController | undefined;\n\n async function refetch(overrides: NetRequest = {}): Promise<T | undefined> {\n const current = ++seq;\n controller?.abort();\n controller = new AbortController();\n loadingSig.set(true);\n statusSig.set(\"loading\");\n errorSig.set(undefined);\n try {\n const target = typeof url === \"function\" ? url() : url;\n const result = await client.request<T>(target, {\n ...requestDefaults,\n ...overrides,\n signal: controller.signal,\n });\n if (current !== seq) return undefined; // a newer request superseded this one\n dataSig.set(result);\n statusSig.set(\"success\");\n return result;\n } catch (err) {\n if (current !== seq) return undefined; // stale failure, ignore\n const e = err as NetError;\n if (e.aborted) {\n statusSig.set(\"idle\");\n return undefined;\n }\n errorSig.set(e);\n statusSig.set(\"error\");\n return undefined;\n } finally {\n if (current === seq) loadingSig.set(false);\n }\n }\n\n const resource: Resource<T> = {\n get data() {\n return dataSig();\n },\n get error() {\n return errorSig();\n },\n get loading() {\n return loadingSig();\n },\n get status() {\n return statusSig();\n },\n refetch,\n mutate(next, mutateOptions = {}) {\n const previous = dataSig.peek();\n dataSig.set((prev) => (typeof next === \"function\" ? (next as (p: T | undefined) => T)(prev) : next));\n if (!mutateOptions.request) return;\n // Persist the optimistic change; roll back and surface the error on failure.\n void Promise.resolve()\n .then(mutateOptions.request)\n .catch((err) => {\n if (mutateOptions.rollbackOnError !== false) dataSig.set(previous);\n errorSig.set(err as NetError);\n });\n },\n abort() {\n seq++;\n controller?.abort();\n controller = undefined;\n loadingSig.set(false);\n if (statusSig.peek() === \"loading\") statusSig.set(\"idle\");\n },\n };\n\n onCleanup?.(() => resource.abort());\n if (immediate !== false) void refetch();\n return resource;\n}\n","/**\n * summitjs/net — the `s-resource` directive.\n *\n * <div s-resource:users=\"{ url: '/api/users', immediate: true }\">\n * <p s-show=\"users.loading\">Loading…</p>\n * <p s-show=\"users.error\" s-text=\"users.error.message\"></p>\n * <template s-for=\"u in users.data || []\" :key=\"u.id\"><li s-text=\"u.name\"></li></template>\n * </div>\n *\n * The config expression is evaluated reactively: when a value it reads changes\n * (e.g. a `params: { q }` bound to state), the resource refetches with the new\n * params, and latest-wins cancellation makes stale responses harmless. An\n * optional `debounce` throttles those refetches for search-as-you-type.\n */\n\nimport type { DirectiveHandler } from \"summitjs\";\nimport { createResource, type Resource } from \"./resource.js\";\nimport type { NetClient, NetParams } from \"./client.js\";\n\ninterface ResourceConfig {\n url?: string;\n params?: NetParams;\n headers?: Record<string, string>;\n method?: string;\n body?: unknown;\n immediate?: boolean;\n debounce?: number;\n timeout?: number;\n}\n\nexport function makeResourceDirective(client: NetClient): DirectiveHandler {\n return (el, meta, utils) => {\n const name = meta.value;\n if (!name) {\n console.warn(\"[summit] s-resource needs a name, e.g. s-resource:users=\\\"{ url: '/api/users' }\\\".\", el);\n return;\n }\n\n const scope = utils.scopes[utils.scopes.length - 1] as Record<string, unknown> | undefined;\n let resource: Resource<unknown> | undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n const readConfig = (): ResourceConfig => (utils.evaluate(meta.expression) || {}) as ResourceConfig;\n\n utils.effect(() => {\n const cfg = readConfig(); // subscribes to reactive params (q, page, …)\n if (!cfg.url) return;\n const req = { params: cfg.params, headers: cfg.headers, method: cfg.method, body: cfg.body, timeout: cfg.timeout };\n\n if (!resource) {\n resource = createResource(client, () => readConfig().url ?? cfg.url!, {\n ...req,\n immediate: cfg.immediate !== false,\n onCleanup: utils.cleanup,\n });\n if (scope) scope[name] = resource;\n return;\n }\n\n // Config changed after creation: refetch with the new request options,\n // debounced when asked. Latest-wins means out-of-order replies are safe.\n const run = () => resource!.refetch(req);\n if (cfg.debounce) {\n if (timer) clearTimeout(timer);\n timer = setTimeout(run, cfg.debounce);\n } else {\n run();\n }\n });\n\n utils.cleanup(() => {\n if (timer) clearTimeout(timer);\n });\n };\n}\n","/**\n * summitjs/net — a tiny reactive data layer for Summit.\n *\n * Optional, shipped as its own entry so the core stays lean. Use it either\n * imperatively (`createClient`, `createResource`) or through Summit by\n * registering the plugin, which adds the `$fetch` magic and the `s-resource`\n * directive:\n *\n * import Summit from \"summitjs\";\n * import { net } from \"summitjs/net\";\n * Summit.plugin(net); // or Summit.plugin(createNet({ baseURL: \"/api\" }))\n * Summit.start();\n */\n\nimport type { SummitGlobal, MagicContext } from \"summitjs\";\nimport { createClient, NetError } from \"./client.js\";\nimport type { NetClient, NetClientOptions, NetRequest, NetParams, NetInit } from \"./client.js\";\nimport { createResource } from \"./resource.js\";\nimport type { Resource, ResourceOptions, ResourceStatus, MutateOptions } from \"./resource.js\";\nimport { makeResourceDirective } from \"./directive.js\";\n\nexport { createClient, createResource, NetError };\nexport type {\n NetClient,\n NetClientOptions,\n NetRequest,\n NetParams,\n NetInit,\n Resource,\n ResourceOptions,\n ResourceStatus,\n MutateOptions,\n};\n\n/** The default client shared by the `$fetch` magic and `s-resource` directive. */\nexport const http: NetClient = createClient();\n\n/** A client bound to an element: resources it creates auto-abort on unmount. */\nexport interface BoundClient extends NetClient {\n resource<T = unknown>(url: string | (() => string), options?: ResourceOptions): Resource<T>;\n}\n\nfunction bindClient(client: NetClient, ctx: MagicContext): BoundClient {\n return {\n ...client,\n resource(url, options = {}) {\n return createResource(client, url, { ...options, onCleanup: options.onCleanup ?? ctx.cleanup });\n },\n };\n}\n\nfunction register(summit: SummitGlobal, client: NetClient): void {\n summit.magic(\"fetch\", (ctx) => bindClient(client, ctx as MagicContext));\n summit.directive(\"resource\", makeResourceDirective(client));\n}\n\n/** Ready-to-use plugin backed by the default client: `Summit.plugin(net)`. */\nexport function net(summit: SummitGlobal): void {\n register(summit, http);\n}\n\n/** A configured plugin: `Summit.plugin(createNet({ baseURL: \"/api\" }))`. */\nexport function createNet(options: NetClientOptions): (summit: SummitGlobal) => void {\n const client = createClient(options);\n return (summit) => register(summit, client);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/net/runtime.ts","../src/net/client.ts","../src/net/resource.ts","../src/net/directive.ts","../src/net/registry.ts","../src/net/index.ts"],"names":["signal"],"mappings":"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","file":"net.cjs","sourcesContent":["/**\n * summitjs/net — signal-factory injection.\n *\n * The net layer must create signals with Summit's ONE reactivity runtime, but\n * it reaches that runtime differently per target: the npm entry imports it from\n * \"summitjs\" (deduped by the consumer's bundler), while the CDN drop-in reads it\n * off the global the core script already installed. Both wire it via\n * setSignalFactory before any resource is created; resource.ts makes its signals\n * through here.\n */\nimport type { Signal } from \"summitjs\";\n\ntype SignalFactory = <T>(initial: T) => Signal<T>;\n\nlet factory: SignalFactory | undefined;\n\nexport function setSignalFactory(fn: SignalFactory): void {\n factory = fn;\n}\n\nexport function signal<T>(initial: T): Signal<T> {\n if (!factory) {\n throw new Error(\n 'summitjs/net: reactivity is not wired up. Import it from \"summitjs/net\", or load summit-net.min.js after summit.min.js.',\n );\n }\n return factory(initial);\n}\n","/**\n * summitjs/net — the HTTP client.\n *\n * A thin, dependency-free wrapper over `fetch` that fixes fetch's real-world\n * footguns: it throws a structured error on non-2xx, sends and parses JSON\n * automatically, serializes params, supports a baseURL, default headers,\n * per-request timeout, and cancellation. No eval, no codegen, so it stays\n * CSP-safe like the rest of Summit.\n */\n\n/** A structured error thrown for non-2xx responses, timeouts, and aborts. */\nexport class NetError extends Error {\n status: number;\n body: unknown;\n url: string;\n method: string;\n aborted: boolean;\n\n constructor(\n message: string,\n init: { status: number; body: unknown; url: string; method: string; aborted: boolean },\n ) {\n super(message);\n this.name = \"NetError\";\n this.status = init.status;\n this.body = init.body;\n this.url = init.url;\n this.method = init.method;\n this.aborted = init.aborted;\n }\n}\n\nexport type NetParams = Record<string, string | number | boolean | null | undefined>;\n\nexport interface NetRequest {\n method?: string;\n headers?: Record<string, string>;\n params?: NetParams;\n /** A value: objects/arrays are JSON-encoded; strings are sent as-is. */\n body?: unknown;\n /** Abort this request when the given signal aborts. */\n signal?: AbortSignal;\n /** Abort automatically after this many milliseconds. */\n timeout?: number;\n}\n\nexport interface NetClientOptions {\n baseURL?: string;\n /** Static headers, or a function evaluated per request (e.g. for auth tokens). */\n headers?: Record<string, string> | (() => Record<string, string>);\n timeout?: number;\n /** Inspect or replace the request just before it is sent. */\n beforeRequest?: (req: NetInit) => void | NetInit;\n /** Inspect or replace the response before the body is read. */\n afterResponse?: (res: Response) => void | Response;\n /** Called with every NetError (e.g. redirect on 401). Re-throws afterward. */\n onError?: (err: NetError) => void;\n}\n\nexport type NetInit = RequestInit & { url: string };\n\nexport interface NetClient {\n request<T = unknown>(path: string, options?: NetRequest): Promise<T>;\n get<T = unknown>(path: string, options?: NetRequest): Promise<T>;\n post<T = unknown>(path: string, body?: unknown, options?: NetRequest): Promise<T>;\n put<T = unknown>(path: string, body?: unknown, options?: NetRequest): Promise<T>;\n patch<T = unknown>(path: string, body?: unknown, options?: NetRequest): Promise<T>;\n delete<T = unknown>(path: string, options?: NetRequest): Promise<T>;\n /** The client's live defaults; mutate to reconfigure baseURL/headers at runtime. */\n options: NetClientOptions;\n}\n\nexport function createClient(defaults: NetClientOptions = {}): NetClient {\n async function request<T = unknown>(path: string, req: NetRequest = {}): Promise<T> {\n const controller = new AbortController();\n const timeoutMs = req.timeout ?? defaults.timeout;\n const timer = timeoutMs ? setTimeout(() => controller.abort(), timeoutMs) : undefined;\n // Chain an external signal onto our controller so both timeout and the\n // caller's own cancellation abort the same request.\n if (req.signal) {\n if (req.signal.aborted) controller.abort();\n else req.signal.addEventListener(\"abort\", () => controller.abort(), { once: true });\n }\n\n const method = (req.method ?? \"GET\").toUpperCase();\n const url = withParams(joinURL(defaults.baseURL, path), req.params);\n const baseHeaders = typeof defaults.headers === \"function\" ? defaults.headers() : defaults.headers;\n const headers: Record<string, string> = { ...baseHeaders, ...req.headers };\n\n let init: NetInit = { url, method, headers, signal: controller.signal };\n if (req.body !== undefined && method !== \"GET\" && method !== \"HEAD\") {\n if (!hasHeader(headers, \"content-type\") && typeof req.body !== \"string\") {\n headers[\"content-type\"] = \"application/json\";\n }\n init.body = typeof req.body === \"string\" ? req.body : JSON.stringify(req.body);\n }\n\n const patched = defaults.beforeRequest?.(init);\n if (patched) init = patched;\n\n try {\n let res = await fetch(init.url, init);\n const swapped = defaults.afterResponse?.(res);\n if (swapped) res = swapped;\n const body = await readBody(res);\n if (!res.ok) {\n throw new NetError(`Request failed with status ${res.status}`, {\n status: res.status,\n body,\n url: init.url,\n method,\n aborted: false,\n });\n }\n return body as T;\n } catch (err) {\n const e = normalizeError(err, init.url, method);\n defaults.onError?.(e);\n throw e;\n } finally {\n if (timer) clearTimeout(timer);\n }\n }\n\n const client: NetClient = {\n request,\n get: (path, options) => request(path, { ...options, method: \"GET\" }),\n post: (path, body, options) => request(path, { ...options, method: \"POST\", body }),\n put: (path, body, options) => request(path, { ...options, method: \"PUT\", body }),\n patch: (path, body, options) => request(path, { ...options, method: \"PATCH\", body }),\n delete: (path, options) => request(path, { ...options, method: \"DELETE\" }),\n options: defaults,\n };\n return client;\n}\n\nfunction normalizeError(err: unknown, url: string, method: string): NetError {\n if (err instanceof NetError) return err;\n const aborted = (err as { name?: string })?.name === \"AbortError\";\n return new NetError(aborted ? \"Request aborted\" : (err as Error)?.message ?? \"Network request failed\", {\n status: 0,\n body: undefined,\n url,\n method,\n aborted,\n });\n}\n\nasync function readBody(res: Response): Promise<unknown> {\n const type = res.headers.get(\"content-type\") ?? \"\";\n const text = await res.text();\n if (text === \"\") return undefined;\n if (type.includes(\"application/json\") || type.includes(\"+json\")) {\n try {\n return JSON.parse(text);\n } catch {\n return text; // malformed JSON: hand back the raw text rather than throwing\n }\n }\n return text;\n}\n\nfunction hasHeader(headers: Record<string, string>, name: string): boolean {\n const lower = name.toLowerCase();\n return Object.keys(headers).some((k) => k.toLowerCase() === lower);\n}\n\nfunction joinURL(base: string | undefined, path: string): string {\n if (!base || /^[a-z][a-z0-9+.-]*:\\/\\//i.test(path)) return path;\n return `${base.replace(/\\/+$/, \"\")}/${path.replace(/^\\/+/, \"\")}`;\n}\n\nfunction withParams(url: string, params?: NetParams): string {\n if (!params) return url;\n const query = new URLSearchParams();\n for (const [key, value] of Object.entries(params)) {\n if (value != null) query.set(key, String(value));\n }\n const suffix = query.toString();\n if (!suffix) return url;\n return `${url}${url.includes(\"?\") ? \"&\" : \"?\"}${suffix}`;\n}\n","/**\n * summitjs/net — the reactive resource.\n *\n * A resource wraps a request in Summit signals: `data`, `error`, `loading`,\n * and `status` are exposed as auto-unwrapping getters, so reading them inside a\n * directive (`s-text=\"user.data.name\"`, `s-show=\"user.loading\"`) subscribes the\n * DOM to exactly that field. This is the key win over a promise wrapper: the\n * view updates itself.\n *\n * It also closes the footguns the stress test found:\n * - out-of-order responses: a monotonic sequence means the latest wins;\n * - non-2xx: surfaces through `error` (the client already throws);\n * - unmount leaks: `onCleanup` aborts the in-flight request;\n * - unhandled rejections: refetch never rejects — failures land in `error`.\n */\n\n// Signals come from the injected runtime factory (see runtime.ts) so the net\n// layer shares Summit's ONE reactivity runtime whether loaded via npm or CDN;\n// a second copy would mean resource signals could not drive the core's DOM.\nimport { signal } from \"./runtime.js\";\nimport type { NetClient, NetError, NetRequest } from \"./client.js\";\n\nexport type ResourceStatus = \"idle\" | \"loading\" | \"success\" | \"error\";\n\nexport interface ResourceOptions extends NetRequest {\n /** Fetch immediately on creation. Default true. */\n immediate?: boolean;\n /** Register a teardown callback (Summit passes the element's cleanup here). */\n onCleanup?: (fn: () => void) => void;\n}\n\nexport interface MutateOptions {\n /** Persist the optimistic change; on rejection the previous value is restored. */\n request?: () => Promise<unknown>;\n /** Roll back on a failed request. Default true. */\n rollbackOnError?: boolean;\n}\n\nexport interface Resource<T> {\n readonly data: T | undefined;\n readonly error: NetError | undefined;\n readonly loading: boolean;\n readonly status: ResourceStatus;\n /** Re-run the request (optionally overriding params/headers). Never rejects. */\n refetch(overrides?: NetRequest): Promise<T | undefined>;\n /** Optimistically set data; with `request`, roll back if the request fails. */\n mutate(next: T | ((prev: T | undefined) => T), options?: MutateOptions): void;\n /** Cancel the in-flight request and return to idle. */\n abort(): void;\n}\n\nexport function createResource<T = unknown>(\n client: NetClient,\n url: string | (() => string),\n options: ResourceOptions = {},\n): Resource<T> {\n const dataSig = signal<T | undefined>(undefined);\n const errorSig = signal<NetError | undefined>(undefined);\n const loadingSig = signal(false);\n const statusSig = signal<ResourceStatus>(\"idle\");\n\n const { immediate, onCleanup, ...requestDefaults } = options;\n let seq = 0;\n let controller: AbortController | undefined;\n\n async function refetch(overrides: NetRequest = {}): Promise<T | undefined> {\n const current = ++seq;\n controller?.abort();\n controller = new AbortController();\n loadingSig.set(true);\n statusSig.set(\"loading\");\n errorSig.set(undefined);\n try {\n const target = typeof url === \"function\" ? url() : url;\n const result = await client.request<T>(target, {\n ...requestDefaults,\n ...overrides,\n signal: controller.signal,\n });\n if (current !== seq) return undefined; // a newer request superseded this one\n dataSig.set(result);\n statusSig.set(\"success\");\n return result;\n } catch (err) {\n if (current !== seq) return undefined; // stale failure, ignore\n const e = err as NetError;\n if (e.aborted) {\n statusSig.set(\"idle\");\n return undefined;\n }\n errorSig.set(e);\n statusSig.set(\"error\");\n return undefined;\n } finally {\n if (current === seq) loadingSig.set(false);\n }\n }\n\n const resource: Resource<T> = {\n get data() {\n return dataSig();\n },\n get error() {\n return errorSig();\n },\n get loading() {\n return loadingSig();\n },\n get status() {\n return statusSig();\n },\n refetch,\n mutate(next, mutateOptions = {}) {\n const previous = dataSig.peek();\n dataSig.set((prev) => (typeof next === \"function\" ? (next as (p: T | undefined) => T)(prev) : next));\n if (!mutateOptions.request) return;\n // Persist the optimistic change; roll back and surface the error on failure.\n void Promise.resolve()\n .then(mutateOptions.request)\n .catch((err) => {\n if (mutateOptions.rollbackOnError !== false) dataSig.set(previous);\n errorSig.set(err as NetError);\n });\n },\n abort() {\n seq++;\n controller?.abort();\n controller = undefined;\n loadingSig.set(false);\n if (statusSig.peek() === \"loading\") statusSig.set(\"idle\");\n },\n };\n\n onCleanup?.(() => resource.abort());\n if (immediate !== false) void refetch();\n return resource;\n}\n","/**\n * summitjs/net — the `s-resource` directive.\n *\n * <div s-resource:users=\"{ url: '/api/users', immediate: true }\">\n * <p s-show=\"users.loading\">Loading…</p>\n * <p s-show=\"users.error\" s-text=\"users.error.message\"></p>\n * <template s-for=\"u in users.data || []\" :key=\"u.id\"><li s-text=\"u.name\"></li></template>\n * </div>\n *\n * The config expression is evaluated reactively: when a value it reads changes\n * (e.g. a `params: { q }` bound to state), the resource refetches with the new\n * params, and latest-wins cancellation makes stale responses harmless. An\n * optional `debounce` throttles those refetches for search-as-you-type.\n */\n\nimport type { DirectiveHandler } from \"summitjs\";\nimport { createResource, type Resource } from \"./resource.js\";\nimport type { NetClient, NetParams } from \"./client.js\";\n\ninterface ResourceConfig {\n url?: string;\n params?: NetParams;\n headers?: Record<string, string>;\n method?: string;\n body?: unknown;\n immediate?: boolean;\n debounce?: number;\n timeout?: number;\n}\n\nexport function makeResourceDirective(client: NetClient): DirectiveHandler {\n return (el, meta, utils) => {\n const name = meta.value;\n if (!name) {\n console.warn(\"[summit] s-resource needs a name, e.g. s-resource:users=\\\"{ url: '/api/users' }\\\".\", el);\n return;\n }\n\n const scope = utils.scopes[utils.scopes.length - 1] as Record<string, unknown> | undefined;\n let resource: Resource<unknown> | undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n const readConfig = (): ResourceConfig => (utils.evaluate(meta.expression) || {}) as ResourceConfig;\n\n utils.effect(() => {\n const cfg = readConfig(); // subscribes to reactive params (q, page, …)\n if (!cfg.url) return;\n const req = { params: cfg.params, headers: cfg.headers, method: cfg.method, body: cfg.body, timeout: cfg.timeout };\n\n if (!resource) {\n resource = createResource(client, () => readConfig().url ?? cfg.url!, {\n ...req,\n immediate: cfg.immediate !== false,\n onCleanup: utils.cleanup,\n });\n if (scope) scope[name] = resource;\n return;\n }\n\n // Config changed after creation: refetch with the new request options,\n // debounced when asked. Latest-wins means out-of-order replies are safe.\n const run = () => resource!.refetch(req);\n if (cfg.debounce) {\n if (timer) clearTimeout(timer);\n timer = setTimeout(run, cfg.debounce);\n } else {\n run();\n }\n });\n\n utils.cleanup(() => {\n if (timer) clearTimeout(timer);\n });\n };\n}\n","/**\n * summitjs/net — plugin registration.\n *\n * Shared by both the npm entry (index.ts) and the CDN drop-in (cdn.ts). It only\n * *type*-imports from \"summitjs\", so it carries no runtime dependency on the\n * core — the CDN build can bundle it into a self-contained IIFE that reads the\n * core off the global instead.\n */\nimport type { SummitGlobal, MagicContext } from \"summitjs\";\nimport { createClient } from \"./client.js\";\nimport type { NetClient } from \"./client.js\";\nimport { createResource } from \"./resource.js\";\nimport type { Resource, ResourceOptions } from \"./resource.js\";\nimport { makeResourceDirective } from \"./directive.js\";\n\n/** The default client shared by the `$fetch` magic and `s-resource` directive. */\nexport const http: NetClient = createClient();\n\n/** A client bound to an element: resources it creates auto-abort on unmount. */\nexport interface BoundClient extends NetClient {\n resource<T = unknown>(url: string | (() => string), options?: ResourceOptions): Resource<T>;\n}\n\nfunction bindClient(client: NetClient, ctx: MagicContext): BoundClient {\n return {\n ...client,\n resource(url, options = {}) {\n return createResource(client, url, { ...options, onCleanup: options.onCleanup ?? ctx.cleanup });\n },\n };\n}\n\nfunction register(summit: SummitGlobal, client: NetClient): void {\n summit.magic(\"fetch\", (ctx) => bindClient(client, ctx as MagicContext));\n summit.directive(\"resource\", makeResourceDirective(client));\n}\n\n/** Ready-to-use plugin backed by the default client: `Summit.plugin(net)`. */\nexport function net(summit: SummitGlobal): void {\n register(summit, http);\n}\n\n/** A configured plugin: `Summit.plugin(createNet({ baseURL: \"/api\" }))`. */\nexport function createNet(options: import(\"./client.js\").NetClientOptions): (summit: SummitGlobal) => void {\n const client = createClient(options);\n return (summit) => register(summit, client);\n}\n","/**\n * summitjs/net — a tiny reactive data layer for Summit (npm entry).\n *\n * Optional, shipped as its own entry so the core stays lean. Use it either\n * imperatively (`createClient`, `createResource`) or through Summit by\n * registering the plugin, which adds the `$fetch` magic and the `s-resource`\n * directive:\n *\n * import Summit from \"summitjs\";\n * import { net } from \"summitjs/net\";\n * Summit.plugin(net); // or Summit.plugin(createNet({ baseURL: \"/api\" }))\n * Summit.start();\n *\n * CDN users load summit-net.min.js after summit.min.js instead; see cdn.ts.\n */\nimport { signal } from \"summitjs\";\nimport { setSignalFactory } from \"./runtime.js\";\n\n// Wire the net layer to the core's reactivity runtime. `summitjs` is external in\n// this build, so a bundler dedupes it to the single installed copy.\nsetSignalFactory(signal);\n\nexport { createClient, NetError } from \"./client.js\";\nexport type { NetClient, NetClientOptions, NetRequest, NetParams, NetInit } from \"./client.js\";\nexport { createResource } from \"./resource.js\";\nexport type { Resource, ResourceOptions, ResourceStatus, MutateOptions } from \"./resource.js\";\nexport { http, net, createNet } from \"./registry.js\";\nexport type { BoundClient } from \"./registry.js\";\n"]}
|
package/dist/net.d.cts
CHANGED
|
@@ -106,20 +106,6 @@ interface Resource<T> {
|
|
|
106
106
|
}
|
|
107
107
|
declare function createResource<T = unknown>(client: NetClient, url: string | (() => string), options?: ResourceOptions): Resource<T>;
|
|
108
108
|
|
|
109
|
-
/**
|
|
110
|
-
* summitjs/net — a tiny reactive data layer for Summit.
|
|
111
|
-
*
|
|
112
|
-
* Optional, shipped as its own entry so the core stays lean. Use it either
|
|
113
|
-
* imperatively (`createClient`, `createResource`) or through Summit by
|
|
114
|
-
* registering the plugin, which adds the `$fetch` magic and the `s-resource`
|
|
115
|
-
* directive:
|
|
116
|
-
*
|
|
117
|
-
* import Summit from "summitjs";
|
|
118
|
-
* import { net } from "summitjs/net";
|
|
119
|
-
* Summit.plugin(net); // or Summit.plugin(createNet({ baseURL: "/api" }))
|
|
120
|
-
* Summit.start();
|
|
121
|
-
*/
|
|
122
|
-
|
|
123
109
|
/** The default client shared by the `$fetch` magic and `s-resource` directive. */
|
|
124
110
|
declare const http: NetClient;
|
|
125
111
|
/** A client bound to an element: resources it creates auto-abort on unmount. */
|
package/dist/net.d.ts
CHANGED
|
@@ -106,20 +106,6 @@ interface Resource<T> {
|
|
|
106
106
|
}
|
|
107
107
|
declare function createResource<T = unknown>(client: NetClient, url: string | (() => string), options?: ResourceOptions): Resource<T>;
|
|
108
108
|
|
|
109
|
-
/**
|
|
110
|
-
* summitjs/net — a tiny reactive data layer for Summit.
|
|
111
|
-
*
|
|
112
|
-
* Optional, shipped as its own entry so the core stays lean. Use it either
|
|
113
|
-
* imperatively (`createClient`, `createResource`) or through Summit by
|
|
114
|
-
* registering the plugin, which adds the `$fetch` magic and the `s-resource`
|
|
115
|
-
* directive:
|
|
116
|
-
*
|
|
117
|
-
* import Summit from "summitjs";
|
|
118
|
-
* import { net } from "summitjs/net";
|
|
119
|
-
* Summit.plugin(net); // or Summit.plugin(createNet({ baseURL: "/api" }))
|
|
120
|
-
* Summit.start();
|
|
121
|
-
*/
|
|
122
|
-
|
|
123
109
|
/** The default client shared by the `$fetch` magic and `s-resource` directive. */
|
|
124
110
|
declare const http: NetClient;
|
|
125
111
|
/** A client bound to an element: resources it creates auto-abort on unmount. */
|
package/dist/net.js
CHANGED
|
@@ -1,4 +1,20 @@
|
|
|
1
|
-
import { signal } from 'summitjs';
|
|
1
|
+
import { signal as signal$1 } from 'summitjs';
|
|
2
|
+
|
|
3
|
+
// src/net/index.ts
|
|
4
|
+
|
|
5
|
+
// src/net/runtime.ts
|
|
6
|
+
var factory;
|
|
7
|
+
function setSignalFactory(fn) {
|
|
8
|
+
factory = fn;
|
|
9
|
+
}
|
|
10
|
+
function signal(initial) {
|
|
11
|
+
if (!factory) {
|
|
12
|
+
throw new Error(
|
|
13
|
+
'summitjs/net: reactivity is not wired up. Import it from "summitjs/net", or load summit-net.min.js after summit.min.js.'
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
return factory(initial);
|
|
17
|
+
}
|
|
2
18
|
|
|
3
19
|
// src/net/client.ts
|
|
4
20
|
var NetError = class extends Error {
|
|
@@ -110,6 +126,8 @@ function withParams(url, params) {
|
|
|
110
126
|
if (!suffix) return url;
|
|
111
127
|
return `${url}${url.includes("?") ? "&" : "?"}${suffix}`;
|
|
112
128
|
}
|
|
129
|
+
|
|
130
|
+
// src/net/resource.ts
|
|
113
131
|
function createResource(client, url, options = {}) {
|
|
114
132
|
const dataSig = signal(void 0);
|
|
115
133
|
const errorSig = signal(void 0);
|
|
@@ -225,7 +243,7 @@ function makeResourceDirective(client) {
|
|
|
225
243
|
};
|
|
226
244
|
}
|
|
227
245
|
|
|
228
|
-
// src/net/
|
|
246
|
+
// src/net/registry.ts
|
|
229
247
|
var http = createClient();
|
|
230
248
|
function bindClient(client, ctx) {
|
|
231
249
|
return {
|
|
@@ -247,6 +265,9 @@ function createNet(options) {
|
|
|
247
265
|
return (summit) => register(summit, client);
|
|
248
266
|
}
|
|
249
267
|
|
|
268
|
+
// src/net/index.ts
|
|
269
|
+
setSignalFactory(signal$1);
|
|
270
|
+
|
|
250
271
|
export { NetError, createClient, createNet, createResource, http, net };
|
|
251
272
|
//# sourceMappingURL=net.js.map
|
|
252
273
|
//# sourceMappingURL=net.js.map
|
package/dist/net.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/net/client.ts","../src/net/resource.ts","../src/net/directive.ts","../src/net/index.ts"],"names":[],"mappings":";;;AAWO,IAAM,QAAA,GAAN,cAAuB,KAAA,CAAM;AAAA,EAOlC,WAAA,CACE,SACA,IAAA,EACA;AACA,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,UAAA;AACZ,IAAA,IAAA,CAAK,SAAS,IAAA,CAAK,MAAA;AACnB,IAAA,IAAA,CAAK,OAAO,IAAA,CAAK,IAAA;AACjB,IAAA,IAAA,CAAK,MAAM,IAAA,CAAK,GAAA;AAChB,IAAA,IAAA,CAAK,SAAS,IAAA,CAAK,MAAA;AACnB,IAAA,IAAA,CAAK,UAAU,IAAA,CAAK,OAAA;AAAA,EACtB;AACF;AA0CO,SAAS,YAAA,CAAa,QAAA,GAA6B,EAAC,EAAc;AACvE,EAAA,eAAe,OAAA,CAAqB,IAAA,EAAc,GAAA,GAAkB,EAAC,EAAe;AAClF,IAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,IAAA,MAAM,SAAA,GAAY,GAAA,CAAI,OAAA,IAAW,QAAA,CAAS,OAAA;AAC1C,IAAA,MAAM,KAAA,GAAQ,YAAY,UAAA,CAAW,MAAM,WAAW,KAAA,EAAM,EAAG,SAAS,CAAA,GAAI,MAAA;AAG5E,IAAA,IAAI,IAAI,MAAA,EAAQ;AACd,MAAA,IAAI,GAAA,CAAI,MAAA,CAAO,OAAA,EAAS,UAAA,CAAW,KAAA,EAAM;AAAA,WACpC,GAAA,CAAI,MAAA,CAAO,gBAAA,CAAiB,OAAA,EAAS,MAAM,UAAA,CAAW,KAAA,EAAM,EAAG,EAAE,IAAA,EAAM,IAAA,EAAM,CAAA;AAAA,IACpF;AAEA,IAAA,MAAM,MAAA,GAAA,CAAU,GAAA,CAAI,MAAA,IAAU,KAAA,EAAO,WAAA,EAAY;AACjD,IAAA,MAAM,GAAA,GAAM,WAAW,OAAA,CAAQ,QAAA,CAAS,SAAS,IAAI,CAAA,EAAG,IAAI,MAAM,CAAA;AAClE,IAAA,MAAM,WAAA,GAAc,OAAO,QAAA,CAAS,OAAA,KAAY,aAAa,QAAA,CAAS,OAAA,KAAY,QAAA,CAAS,OAAA;AAC3F,IAAA,MAAM,UAAkC,EAAE,GAAG,WAAA,EAAa,GAAG,IAAI,OAAA,EAAQ;AAEzE,IAAA,IAAI,OAAgB,EAAE,GAAA,EAAK,QAAQ,OAAA,EAAS,MAAA,EAAQ,WAAW,MAAA,EAAO;AACtE,IAAA,IAAI,IAAI,IAAA,KAAS,MAAA,IAAa,MAAA,KAAW,KAAA,IAAS,WAAW,MAAA,EAAQ;AACnE,MAAA,IAAI,CAAC,UAAU,OAAA,EAAS,cAAc,KAAK,OAAO,GAAA,CAAI,SAAS,QAAA,EAAU;AACvE,QAAA,OAAA,CAAQ,cAAc,CAAA,GAAI,kBAAA;AAAA,MAC5B;AACA,MAAA,IAAA,CAAK,IAAA,GAAO,OAAO,GAAA,CAAI,IAAA,KAAS,QAAA,GAAW,IAAI,IAAA,GAAO,IAAA,CAAK,SAAA,CAAU,GAAA,CAAI,IAAI,CAAA;AAAA,IAC/E;AAEA,IAAA,MAAM,OAAA,GAAU,QAAA,CAAS,aAAA,GAAgB,IAAI,CAAA;AAC7C,IAAA,IAAI,SAAS,IAAA,GAAO,OAAA;AAEpB,IAAA,IAAI;AACF,MAAA,IAAI,GAAA,GAAM,MAAM,KAAA,CAAM,IAAA,CAAK,KAAK,IAAI,CAAA;AACpC,MAAA,MAAM,OAAA,GAAU,QAAA,CAAS,aAAA,GAAgB,GAAG,CAAA;AAC5C,MAAA,IAAI,SAAS,GAAA,GAAM,OAAA;AACnB,MAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,GAAG,CAAA;AAC/B,MAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACX,QAAA,MAAM,IAAI,QAAA,CAAS,CAAA,2BAAA,EAA8B,GAAA,CAAI,MAAM,CAAA,CAAA,EAAI;AAAA,UAC7D,QAAQ,GAAA,CAAI,MAAA;AAAA,UACZ,IAAA;AAAA,UACA,KAAK,IAAA,CAAK,GAAA;AAAA,UACV,MAAA;AAAA,UACA,OAAA,EAAS;AAAA,SACV,CAAA;AAAA,MACH;AACA,MAAA,OAAO,IAAA;AAAA,IACT,SAAS,GAAA,EAAK;AACZ,MAAA,MAAM,CAAA,GAAI,cAAA,CAAe,GAAA,EAAK,IAAA,CAAK,KAAK,MAAM,CAAA;AAC9C,MAAA,QAAA,CAAS,UAAU,CAAC,CAAA;AACpB,MAAA,MAAM,CAAA;AAAA,IACR,CAAA,SAAE;AACA,MAAA,IAAI,KAAA,eAAoB,KAAK,CAAA;AAAA,IAC/B;AAAA,EACF;AAEA,EAAA,MAAM,MAAA,GAAoB;AAAA,IACxB,OAAA;AAAA,IACA,GAAA,EAAK,CAAC,IAAA,EAAM,OAAA,KAAY,OAAA,CAAQ,IAAA,EAAM,EAAE,GAAG,OAAA,EAAS,MAAA,EAAQ,KAAA,EAAO,CAAA;AAAA,IACnE,IAAA,EAAM,CAAC,IAAA,EAAM,IAAA,EAAM,OAAA,KAAY,OAAA,CAAQ,IAAA,EAAM,EAAE,GAAG,OAAA,EAAS,MAAA,EAAQ,MAAA,EAAQ,MAAM,CAAA;AAAA,IACjF,GAAA,EAAK,CAAC,IAAA,EAAM,IAAA,EAAM,OAAA,KAAY,OAAA,CAAQ,IAAA,EAAM,EAAE,GAAG,OAAA,EAAS,MAAA,EAAQ,KAAA,EAAO,MAAM,CAAA;AAAA,IAC/E,KAAA,EAAO,CAAC,IAAA,EAAM,IAAA,EAAM,OAAA,KAAY,OAAA,CAAQ,IAAA,EAAM,EAAE,GAAG,OAAA,EAAS,MAAA,EAAQ,OAAA,EAAS,MAAM,CAAA;AAAA,IACnF,MAAA,EAAQ,CAAC,IAAA,EAAM,OAAA,KAAY,OAAA,CAAQ,IAAA,EAAM,EAAE,GAAG,OAAA,EAAS,MAAA,EAAQ,QAAA,EAAU,CAAA;AAAA,IACzE,OAAA,EAAS;AAAA,GACX;AACA,EAAA,OAAO,MAAA;AACT;AAEA,SAAS,cAAA,CAAe,GAAA,EAAc,GAAA,EAAa,MAAA,EAA0B;AAC3E,EAAA,IAAI,GAAA,YAAe,UAAU,OAAO,GAAA;AACpC,EAAA,MAAM,OAAA,GAAW,KAA2B,IAAA,KAAS,YAAA;AACrD,EAAA,OAAO,IAAI,QAAA,CAAS,OAAA,GAAU,iBAAA,GAAqB,GAAA,EAAe,WAAW,wBAAA,EAA0B;AAAA,IACrG,MAAA,EAAQ,CAAA;AAAA,IACR,IAAA,EAAM,MAAA;AAAA,IACN,GAAA;AAAA,IACA,MAAA;AAAA,IACA;AAAA,GACD,CAAA;AACH;AAEA,eAAe,SAAS,GAAA,EAAiC;AACvD,EAAA,MAAM,IAAA,GAAO,GAAA,CAAI,OAAA,CAAQ,GAAA,CAAI,cAAc,CAAA,IAAK,EAAA;AAChD,EAAA,MAAM,IAAA,GAAO,MAAM,GAAA,CAAI,IAAA,EAAK;AAC5B,EAAA,IAAI,IAAA,KAAS,IAAI,OAAO,MAAA;AACxB,EAAA,IAAI,KAAK,QAAA,CAAS,kBAAkB,KAAK,IAAA,CAAK,QAAA,CAAS,OAAO,CAAA,EAAG;AAC/D,IAAA,IAAI;AACF,MAAA,OAAO,IAAA,CAAK,MAAM,IAAI,CAAA;AAAA,IACxB,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,EACF;AACA,EAAA,OAAO,IAAA;AACT;AAEA,SAAS,SAAA,CAAU,SAAiC,IAAA,EAAuB;AACzE,EAAA,MAAM,KAAA,GAAQ,KAAK,WAAA,EAAY;AAC/B,EAAA,OAAO,MAAA,CAAO,IAAA,CAAK,OAAO,CAAA,CAAE,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,WAAA,EAAY,KAAM,KAAK,CAAA;AACnE;AAEA,SAAS,OAAA,CAAQ,MAA0B,IAAA,EAAsB;AAC/D,EAAA,IAAI,CAAC,IAAA,IAAQ,0BAAA,CAA2B,IAAA,CAAK,IAAI,GAAG,OAAO,IAAA;AAC3D,EAAA,OAAO,CAAA,EAAG,IAAA,CAAK,OAAA,CAAQ,MAAA,EAAQ,EAAE,CAAC,CAAA,CAAA,EAAI,IAAA,CAAK,OAAA,CAAQ,MAAA,EAAQ,EAAE,CAAC,CAAA,CAAA;AAChE;AAEA,SAAS,UAAA,CAAW,KAAa,MAAA,EAA4B;AAC3D,EAAA,IAAI,CAAC,QAAQ,OAAO,GAAA;AACpB,EAAA,MAAM,KAAA,GAAQ,IAAI,eAAA,EAAgB;AAClC,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAA,EAAG;AACjD,IAAA,IAAI,SAAS,IAAA,EAAM,KAAA,CAAM,IAAI,GAAA,EAAK,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,EACjD;AACA,EAAA,MAAM,MAAA,GAAS,MAAM,QAAA,EAAS;AAC9B,EAAA,IAAI,CAAC,QAAQ,OAAO,GAAA;AACpB,EAAA,OAAO,CAAA,EAAG,GAAG,CAAA,EAAG,GAAA,CAAI,QAAA,CAAS,GAAG,CAAA,GAAI,GAAA,GAAM,GAAG,CAAA,EAAG,MAAM,CAAA,CAAA;AACxD;AClIO,SAAS,cAAA,CACd,MAAA,EACA,GAAA,EACA,OAAA,GAA2B,EAAC,EACf;AACb,EAAA,MAAM,OAAA,GAAU,OAAsB,MAAS,CAAA;AAC/C,EAAA,MAAM,QAAA,GAAW,OAA6B,MAAS,CAAA;AACvD,EAAA,MAAM,UAAA,GAAa,OAAO,KAAK,CAAA;AAC/B,EAAA,MAAM,SAAA,GAAY,OAAuB,MAAM,CAAA;AAE/C,EAAA,MAAM,EAAE,SAAA,EAAW,SAAA,EAAW,GAAG,iBAAgB,GAAI,OAAA;AACrD,EAAA,IAAI,GAAA,GAAM,CAAA;AACV,EAAA,IAAI,UAAA;AAEJ,EAAA,eAAe,OAAA,CAAQ,SAAA,GAAwB,EAAC,EAA2B;AACzE,IAAA,MAAM,UAAU,EAAE,GAAA;AAClB,IAAA,UAAA,EAAY,KAAA,EAAM;AAClB,IAAA,UAAA,GAAa,IAAI,eAAA,EAAgB;AACjC,IAAA,UAAA,CAAW,IAAI,IAAI,CAAA;AACnB,IAAA,SAAA,CAAU,IAAI,SAAS,CAAA;AACvB,IAAA,QAAA,CAAS,IAAI,MAAS,CAAA;AACtB,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,OAAO,GAAA,KAAQ,UAAA,GAAa,KAAI,GAAI,GAAA;AACnD,MAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,OAAA,CAAW,MAAA,EAAQ;AAAA,QAC7C,GAAG,eAAA;AAAA,QACH,GAAG,SAAA;AAAA,QACH,QAAQ,UAAA,CAAW;AAAA,OACpB,CAAA;AACD,MAAA,IAAI,OAAA,KAAY,KAAK,OAAO,KAAA,CAAA;AAC5B,MAAA,OAAA,CAAQ,IAAI,MAAM,CAAA;AAClB,MAAA,SAAA,CAAU,IAAI,SAAS,CAAA;AACvB,MAAA,OAAO,MAAA;AAAA,IACT,SAAS,GAAA,EAAK;AACZ,MAAA,IAAI,OAAA,KAAY,KAAK,OAAO,MAAA;AAC5B,MAAA,MAAM,CAAA,GAAI,GAAA;AACV,MAAA,IAAI,EAAE,OAAA,EAAS;AACb,QAAA,SAAA,CAAU,IAAI,MAAM,CAAA;AACpB,QAAA,OAAO,MAAA;AAAA,MACT;AACA,MAAA,QAAA,CAAS,IAAI,CAAC,CAAA;AACd,MAAA,SAAA,CAAU,IAAI,OAAO,CAAA;AACrB,MAAA,OAAO,MAAA;AAAA,IACT,CAAA,SAAE;AACA,MAAA,IAAI,OAAA,KAAY,GAAA,EAAK,UAAA,CAAW,GAAA,CAAI,KAAK,CAAA;AAAA,IAC3C;AAAA,EACF;AAEA,EAAA,MAAM,QAAA,GAAwB;AAAA,IAC5B,IAAI,IAAA,GAAO;AACT,MAAA,OAAO,OAAA,EAAQ;AAAA,IACjB,CAAA;AAAA,IACA,IAAI,KAAA,GAAQ;AACV,MAAA,OAAO,QAAA,EAAS;AAAA,IAClB,CAAA;AAAA,IACA,IAAI,OAAA,GAAU;AACZ,MAAA,OAAO,UAAA,EAAW;AAAA,IACpB,CAAA;AAAA,IACA,IAAI,MAAA,GAAS;AACX,MAAA,OAAO,SAAA,EAAU;AAAA,IACnB,CAAA;AAAA,IACA,OAAA;AAAA,IACA,MAAA,CAAO,IAAA,EAAM,aAAA,GAAgB,EAAC,EAAG;AAC/B,MAAA,MAAM,QAAA,GAAW,QAAQ,IAAA,EAAK;AAC9B,MAAA,OAAA,CAAQ,GAAA,CAAI,CAAC,IAAA,KAAU,OAAO,SAAS,UAAA,GAAc,IAAA,CAAiC,IAAI,CAAA,GAAI,IAAK,CAAA;AACnG,MAAA,IAAI,CAAC,cAAc,OAAA,EAAS;AAE5B,MAAA,KAAK,OAAA,CAAQ,SAAQ,CAClB,IAAA,CAAK,cAAc,OAAO,CAAA,CAC1B,KAAA,CAAM,CAAC,GAAA,KAAQ;AACd,QAAA,IAAI,aAAA,CAAc,eAAA,KAAoB,KAAA,EAAO,OAAA,CAAQ,IAAI,QAAQ,CAAA;AACjE,QAAA,QAAA,CAAS,IAAI,GAAe,CAAA;AAAA,MAC9B,CAAC,CAAA;AAAA,IACL,CAAA;AAAA,IACA,KAAA,GAAQ;AACN,MAAA,GAAA,EAAA;AACA,MAAA,UAAA,EAAY,KAAA,EAAM;AAClB,MAAA,UAAA,GAAa,MAAA;AACb,MAAA,UAAA,CAAW,IAAI,KAAK,CAAA;AACpB,MAAA,IAAI,UAAU,IAAA,EAAK,KAAM,SAAA,EAAW,SAAA,CAAU,IAAI,MAAM,CAAA;AAAA,IAC1D;AAAA,GACF;AAEA,EAAA,SAAA,GAAY,MAAM,QAAA,CAAS,KAAA,EAAO,CAAA;AAClC,EAAA,IAAI,SAAA,KAAc,KAAA,EAAO,KAAK,OAAA,EAAQ;AACtC,EAAA,OAAO,QAAA;AACT;;;AC1GO,SAAS,sBAAsB,MAAA,EAAqC;AACzE,EAAA,OAAO,CAAC,EAAA,EAAI,IAAA,EAAM,KAAA,KAAU;AAC1B,IAAA,MAAM,OAAO,IAAA,CAAK,KAAA;AAClB,IAAA,IAAI,CAAC,IAAA,EAAM;AACT,MAAA,OAAA,CAAQ,IAAA,CAAK,oFAAsF,EAAE,CAAA;AACrG,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,QAAQ,KAAA,CAAM,MAAA,CAAO,KAAA,CAAM,MAAA,CAAO,SAAS,CAAC,CAAA;AAClD,IAAA,IAAI,QAAA;AACJ,IAAA,IAAI,KAAA;AAEJ,IAAA,MAAM,aAAa,MAAuB,KAAA,CAAM,SAAS,IAAA,CAAK,UAAU,KAAK,EAAC;AAE9E,IAAA,KAAA,CAAM,OAAO,MAAM;AACjB,MAAA,MAAM,MAAM,UAAA,EAAW;AACvB,MAAA,IAAI,CAAC,IAAI,GAAA,EAAK;AACd,MAAA,MAAM,MAAM,EAAE,MAAA,EAAQ,GAAA,CAAI,MAAA,EAAQ,SAAS,GAAA,CAAI,OAAA,EAAS,MAAA,EAAQ,GAAA,CAAI,QAAQ,IAAA,EAAM,GAAA,CAAI,IAAA,EAAM,OAAA,EAAS,IAAI,OAAA,EAAQ;AAEjH,MAAA,IAAI,CAAC,QAAA,EAAU;AACb,QAAA,QAAA,GAAW,eAAe,MAAA,EAAQ,MAAM,YAAW,CAAE,GAAA,IAAO,IAAI,GAAA,EAAM;AAAA,UACpE,GAAG,GAAA;AAAA,UACH,SAAA,EAAW,IAAI,SAAA,KAAc,KAAA;AAAA,UAC7B,WAAW,KAAA,CAAM;AAAA,SAClB,CAAA;AACD,QAAA,IAAI,KAAA,EAAO,KAAA,CAAM,IAAI,CAAA,GAAI,QAAA;AACzB,QAAA;AAAA,MACF;AAIA,MAAA,MAAM,GAAA,GAAM,MAAM,QAAA,CAAU,OAAA,CAAQ,GAAG,CAAA;AACvC,MAAA,IAAI,IAAI,QAAA,EAAU;AAChB,QAAA,IAAI,KAAA,eAAoB,KAAK,CAAA;AAC7B,QAAA,KAAA,GAAQ,UAAA,CAAW,GAAA,EAAK,GAAA,CAAI,QAAQ,CAAA;AAAA,MACtC,CAAA,MAAO;AACL,QAAA,GAAA,EAAI;AAAA,MACN;AAAA,IACF,CAAC,CAAA;AAED,IAAA,KAAA,CAAM,QAAQ,MAAM;AAClB,MAAA,IAAI,KAAA,eAAoB,KAAK,CAAA;AAAA,IAC/B,CAAC,CAAA;AAAA,EACH,CAAA;AACF;;;ACvCO,IAAM,OAAkB,YAAA;AAO/B,SAAS,UAAA,CAAW,QAAmB,GAAA,EAAgC;AACrE,EAAA,OAAO;AAAA,IACL,GAAG,MAAA;AAAA,IACH,QAAA,CAAS,GAAA,EAAK,OAAA,GAAU,EAAC,EAAG;AAC1B,MAAA,OAAO,cAAA,CAAe,MAAA,EAAQ,GAAA,EAAK,EAAE,GAAG,OAAA,EAAS,SAAA,EAAW,OAAA,CAAQ,SAAA,IAAa,GAAA,CAAI,OAAA,EAAS,CAAA;AAAA,IAChG;AAAA,GACF;AACF;AAEA,SAAS,QAAA,CAAS,QAAsB,MAAA,EAAyB;AAC/D,EAAA,MAAA,CAAO,MAAM,OAAA,EAAS,CAAC,QAAQ,UAAA,CAAW,MAAA,EAAQ,GAAmB,CAAC,CAAA;AACtE,EAAA,MAAA,CAAO,SAAA,CAAU,UAAA,EAAY,qBAAA,CAAsB,MAAM,CAAC,CAAA;AAC5D;AAGO,SAAS,IAAI,MAAA,EAA4B;AAC9C,EAAA,QAAA,CAAS,QAAQ,IAAI,CAAA;AACvB;AAGO,SAAS,UAAU,OAAA,EAA2D;AACnF,EAAA,MAAM,MAAA,GAAS,aAAa,OAAO,CAAA;AACnC,EAAA,OAAO,CAAC,MAAA,KAAW,QAAA,CAAS,MAAA,EAAQ,MAAM,CAAA;AAC5C","file":"net.js","sourcesContent":["/**\n * summitjs/net — the HTTP client.\n *\n * A thin, dependency-free wrapper over `fetch` that fixes fetch's real-world\n * footguns: it throws a structured error on non-2xx, sends and parses JSON\n * automatically, serializes params, supports a baseURL, default headers,\n * per-request timeout, and cancellation. No eval, no codegen, so it stays\n * CSP-safe like the rest of Summit.\n */\n\n/** A structured error thrown for non-2xx responses, timeouts, and aborts. */\nexport class NetError extends Error {\n status: number;\n body: unknown;\n url: string;\n method: string;\n aborted: boolean;\n\n constructor(\n message: string,\n init: { status: number; body: unknown; url: string; method: string; aborted: boolean },\n ) {\n super(message);\n this.name = \"NetError\";\n this.status = init.status;\n this.body = init.body;\n this.url = init.url;\n this.method = init.method;\n this.aborted = init.aborted;\n }\n}\n\nexport type NetParams = Record<string, string | number | boolean | null | undefined>;\n\nexport interface NetRequest {\n method?: string;\n headers?: Record<string, string>;\n params?: NetParams;\n /** A value: objects/arrays are JSON-encoded; strings are sent as-is. */\n body?: unknown;\n /** Abort this request when the given signal aborts. */\n signal?: AbortSignal;\n /** Abort automatically after this many milliseconds. */\n timeout?: number;\n}\n\nexport interface NetClientOptions {\n baseURL?: string;\n /** Static headers, or a function evaluated per request (e.g. for auth tokens). */\n headers?: Record<string, string> | (() => Record<string, string>);\n timeout?: number;\n /** Inspect or replace the request just before it is sent. */\n beforeRequest?: (req: NetInit) => void | NetInit;\n /** Inspect or replace the response before the body is read. */\n afterResponse?: (res: Response) => void | Response;\n /** Called with every NetError (e.g. redirect on 401). Re-throws afterward. */\n onError?: (err: NetError) => void;\n}\n\nexport type NetInit = RequestInit & { url: string };\n\nexport interface NetClient {\n request<T = unknown>(path: string, options?: NetRequest): Promise<T>;\n get<T = unknown>(path: string, options?: NetRequest): Promise<T>;\n post<T = unknown>(path: string, body?: unknown, options?: NetRequest): Promise<T>;\n put<T = unknown>(path: string, body?: unknown, options?: NetRequest): Promise<T>;\n patch<T = unknown>(path: string, body?: unknown, options?: NetRequest): Promise<T>;\n delete<T = unknown>(path: string, options?: NetRequest): Promise<T>;\n /** The client's live defaults; mutate to reconfigure baseURL/headers at runtime. */\n options: NetClientOptions;\n}\n\nexport function createClient(defaults: NetClientOptions = {}): NetClient {\n async function request<T = unknown>(path: string, req: NetRequest = {}): Promise<T> {\n const controller = new AbortController();\n const timeoutMs = req.timeout ?? defaults.timeout;\n const timer = timeoutMs ? setTimeout(() => controller.abort(), timeoutMs) : undefined;\n // Chain an external signal onto our controller so both timeout and the\n // caller's own cancellation abort the same request.\n if (req.signal) {\n if (req.signal.aborted) controller.abort();\n else req.signal.addEventListener(\"abort\", () => controller.abort(), { once: true });\n }\n\n const method = (req.method ?? \"GET\").toUpperCase();\n const url = withParams(joinURL(defaults.baseURL, path), req.params);\n const baseHeaders = typeof defaults.headers === \"function\" ? defaults.headers() : defaults.headers;\n const headers: Record<string, string> = { ...baseHeaders, ...req.headers };\n\n let init: NetInit = { url, method, headers, signal: controller.signal };\n if (req.body !== undefined && method !== \"GET\" && method !== \"HEAD\") {\n if (!hasHeader(headers, \"content-type\") && typeof req.body !== \"string\") {\n headers[\"content-type\"] = \"application/json\";\n }\n init.body = typeof req.body === \"string\" ? req.body : JSON.stringify(req.body);\n }\n\n const patched = defaults.beforeRequest?.(init);\n if (patched) init = patched;\n\n try {\n let res = await fetch(init.url, init);\n const swapped = defaults.afterResponse?.(res);\n if (swapped) res = swapped;\n const body = await readBody(res);\n if (!res.ok) {\n throw new NetError(`Request failed with status ${res.status}`, {\n status: res.status,\n body,\n url: init.url,\n method,\n aborted: false,\n });\n }\n return body as T;\n } catch (err) {\n const e = normalizeError(err, init.url, method);\n defaults.onError?.(e);\n throw e;\n } finally {\n if (timer) clearTimeout(timer);\n }\n }\n\n const client: NetClient = {\n request,\n get: (path, options) => request(path, { ...options, method: \"GET\" }),\n post: (path, body, options) => request(path, { ...options, method: \"POST\", body }),\n put: (path, body, options) => request(path, { ...options, method: \"PUT\", body }),\n patch: (path, body, options) => request(path, { ...options, method: \"PATCH\", body }),\n delete: (path, options) => request(path, { ...options, method: \"DELETE\" }),\n options: defaults,\n };\n return client;\n}\n\nfunction normalizeError(err: unknown, url: string, method: string): NetError {\n if (err instanceof NetError) return err;\n const aborted = (err as { name?: string })?.name === \"AbortError\";\n return new NetError(aborted ? \"Request aborted\" : (err as Error)?.message ?? \"Network request failed\", {\n status: 0,\n body: undefined,\n url,\n method,\n aborted,\n });\n}\n\nasync function readBody(res: Response): Promise<unknown> {\n const type = res.headers.get(\"content-type\") ?? \"\";\n const text = await res.text();\n if (text === \"\") return undefined;\n if (type.includes(\"application/json\") || type.includes(\"+json\")) {\n try {\n return JSON.parse(text);\n } catch {\n return text; // malformed JSON: hand back the raw text rather than throwing\n }\n }\n return text;\n}\n\nfunction hasHeader(headers: Record<string, string>, name: string): boolean {\n const lower = name.toLowerCase();\n return Object.keys(headers).some((k) => k.toLowerCase() === lower);\n}\n\nfunction joinURL(base: string | undefined, path: string): string {\n if (!base || /^[a-z][a-z0-9+.-]*:\\/\\//i.test(path)) return path;\n return `${base.replace(/\\/+$/, \"\")}/${path.replace(/^\\/+/, \"\")}`;\n}\n\nfunction withParams(url: string, params?: NetParams): string {\n if (!params) return url;\n const query = new URLSearchParams();\n for (const [key, value] of Object.entries(params)) {\n if (value != null) query.set(key, String(value));\n }\n const suffix = query.toString();\n if (!suffix) return url;\n return `${url}${url.includes(\"?\") ? \"&\" : \"?\"}${suffix}`;\n}\n","/**\n * summitjs/net — the reactive resource.\n *\n * A resource wraps a request in Summit signals: `data`, `error`, `loading`,\n * and `status` are exposed as auto-unwrapping getters, so reading them inside a\n * directive (`s-text=\"user.data.name\"`, `s-show=\"user.loading\"`) subscribes the\n * DOM to exactly that field. This is the key win over a promise wrapper: the\n * view updates itself.\n *\n * It also closes the footguns the stress test found:\n * - out-of-order responses: a monotonic sequence means the latest wins;\n * - non-2xx: surfaces through `error` (the client already throws);\n * - unmount leaks: `onCleanup` aborts the in-flight request;\n * - unhandled rejections: refetch never rejects — failures land in `error`.\n */\n\n// Import the signal factory from the public entry (not a relative core path) so\n// the net bundle shares Summit's ONE reactivity runtime instead of bundling a\n// second copy — otherwise resource signals would not drive the core's DOM.\nimport { signal } from \"summitjs\";\nimport type { NetClient, NetError, NetRequest } from \"./client.js\";\n\nexport type ResourceStatus = \"idle\" | \"loading\" | \"success\" | \"error\";\n\nexport interface ResourceOptions extends NetRequest {\n /** Fetch immediately on creation. Default true. */\n immediate?: boolean;\n /** Register a teardown callback (Summit passes the element's cleanup here). */\n onCleanup?: (fn: () => void) => void;\n}\n\nexport interface MutateOptions {\n /** Persist the optimistic change; on rejection the previous value is restored. */\n request?: () => Promise<unknown>;\n /** Roll back on a failed request. Default true. */\n rollbackOnError?: boolean;\n}\n\nexport interface Resource<T> {\n readonly data: T | undefined;\n readonly error: NetError | undefined;\n readonly loading: boolean;\n readonly status: ResourceStatus;\n /** Re-run the request (optionally overriding params/headers). Never rejects. */\n refetch(overrides?: NetRequest): Promise<T | undefined>;\n /** Optimistically set data; with `request`, roll back if the request fails. */\n mutate(next: T | ((prev: T | undefined) => T), options?: MutateOptions): void;\n /** Cancel the in-flight request and return to idle. */\n abort(): void;\n}\n\nexport function createResource<T = unknown>(\n client: NetClient,\n url: string | (() => string),\n options: ResourceOptions = {},\n): Resource<T> {\n const dataSig = signal<T | undefined>(undefined);\n const errorSig = signal<NetError | undefined>(undefined);\n const loadingSig = signal(false);\n const statusSig = signal<ResourceStatus>(\"idle\");\n\n const { immediate, onCleanup, ...requestDefaults } = options;\n let seq = 0;\n let controller: AbortController | undefined;\n\n async function refetch(overrides: NetRequest = {}): Promise<T | undefined> {\n const current = ++seq;\n controller?.abort();\n controller = new AbortController();\n loadingSig.set(true);\n statusSig.set(\"loading\");\n errorSig.set(undefined);\n try {\n const target = typeof url === \"function\" ? url() : url;\n const result = await client.request<T>(target, {\n ...requestDefaults,\n ...overrides,\n signal: controller.signal,\n });\n if (current !== seq) return undefined; // a newer request superseded this one\n dataSig.set(result);\n statusSig.set(\"success\");\n return result;\n } catch (err) {\n if (current !== seq) return undefined; // stale failure, ignore\n const e = err as NetError;\n if (e.aborted) {\n statusSig.set(\"idle\");\n return undefined;\n }\n errorSig.set(e);\n statusSig.set(\"error\");\n return undefined;\n } finally {\n if (current === seq) loadingSig.set(false);\n }\n }\n\n const resource: Resource<T> = {\n get data() {\n return dataSig();\n },\n get error() {\n return errorSig();\n },\n get loading() {\n return loadingSig();\n },\n get status() {\n return statusSig();\n },\n refetch,\n mutate(next, mutateOptions = {}) {\n const previous = dataSig.peek();\n dataSig.set((prev) => (typeof next === \"function\" ? (next as (p: T | undefined) => T)(prev) : next));\n if (!mutateOptions.request) return;\n // Persist the optimistic change; roll back and surface the error on failure.\n void Promise.resolve()\n .then(mutateOptions.request)\n .catch((err) => {\n if (mutateOptions.rollbackOnError !== false) dataSig.set(previous);\n errorSig.set(err as NetError);\n });\n },\n abort() {\n seq++;\n controller?.abort();\n controller = undefined;\n loadingSig.set(false);\n if (statusSig.peek() === \"loading\") statusSig.set(\"idle\");\n },\n };\n\n onCleanup?.(() => resource.abort());\n if (immediate !== false) void refetch();\n return resource;\n}\n","/**\n * summitjs/net — the `s-resource` directive.\n *\n * <div s-resource:users=\"{ url: '/api/users', immediate: true }\">\n * <p s-show=\"users.loading\">Loading…</p>\n * <p s-show=\"users.error\" s-text=\"users.error.message\"></p>\n * <template s-for=\"u in users.data || []\" :key=\"u.id\"><li s-text=\"u.name\"></li></template>\n * </div>\n *\n * The config expression is evaluated reactively: when a value it reads changes\n * (e.g. a `params: { q }` bound to state), the resource refetches with the new\n * params, and latest-wins cancellation makes stale responses harmless. An\n * optional `debounce` throttles those refetches for search-as-you-type.\n */\n\nimport type { DirectiveHandler } from \"summitjs\";\nimport { createResource, type Resource } from \"./resource.js\";\nimport type { NetClient, NetParams } from \"./client.js\";\n\ninterface ResourceConfig {\n url?: string;\n params?: NetParams;\n headers?: Record<string, string>;\n method?: string;\n body?: unknown;\n immediate?: boolean;\n debounce?: number;\n timeout?: number;\n}\n\nexport function makeResourceDirective(client: NetClient): DirectiveHandler {\n return (el, meta, utils) => {\n const name = meta.value;\n if (!name) {\n console.warn(\"[summit] s-resource needs a name, e.g. s-resource:users=\\\"{ url: '/api/users' }\\\".\", el);\n return;\n }\n\n const scope = utils.scopes[utils.scopes.length - 1] as Record<string, unknown> | undefined;\n let resource: Resource<unknown> | undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n const readConfig = (): ResourceConfig => (utils.evaluate(meta.expression) || {}) as ResourceConfig;\n\n utils.effect(() => {\n const cfg = readConfig(); // subscribes to reactive params (q, page, …)\n if (!cfg.url) return;\n const req = { params: cfg.params, headers: cfg.headers, method: cfg.method, body: cfg.body, timeout: cfg.timeout };\n\n if (!resource) {\n resource = createResource(client, () => readConfig().url ?? cfg.url!, {\n ...req,\n immediate: cfg.immediate !== false,\n onCleanup: utils.cleanup,\n });\n if (scope) scope[name] = resource;\n return;\n }\n\n // Config changed after creation: refetch with the new request options,\n // debounced when asked. Latest-wins means out-of-order replies are safe.\n const run = () => resource!.refetch(req);\n if (cfg.debounce) {\n if (timer) clearTimeout(timer);\n timer = setTimeout(run, cfg.debounce);\n } else {\n run();\n }\n });\n\n utils.cleanup(() => {\n if (timer) clearTimeout(timer);\n });\n };\n}\n","/**\n * summitjs/net — a tiny reactive data layer for Summit.\n *\n * Optional, shipped as its own entry so the core stays lean. Use it either\n * imperatively (`createClient`, `createResource`) or through Summit by\n * registering the plugin, which adds the `$fetch` magic and the `s-resource`\n * directive:\n *\n * import Summit from \"summitjs\";\n * import { net } from \"summitjs/net\";\n * Summit.plugin(net); // or Summit.plugin(createNet({ baseURL: \"/api\" }))\n * Summit.start();\n */\n\nimport type { SummitGlobal, MagicContext } from \"summitjs\";\nimport { createClient, NetError } from \"./client.js\";\nimport type { NetClient, NetClientOptions, NetRequest, NetParams, NetInit } from \"./client.js\";\nimport { createResource } from \"./resource.js\";\nimport type { Resource, ResourceOptions, ResourceStatus, MutateOptions } from \"./resource.js\";\nimport { makeResourceDirective } from \"./directive.js\";\n\nexport { createClient, createResource, NetError };\nexport type {\n NetClient,\n NetClientOptions,\n NetRequest,\n NetParams,\n NetInit,\n Resource,\n ResourceOptions,\n ResourceStatus,\n MutateOptions,\n};\n\n/** The default client shared by the `$fetch` magic and `s-resource` directive. */\nexport const http: NetClient = createClient();\n\n/** A client bound to an element: resources it creates auto-abort on unmount. */\nexport interface BoundClient extends NetClient {\n resource<T = unknown>(url: string | (() => string), options?: ResourceOptions): Resource<T>;\n}\n\nfunction bindClient(client: NetClient, ctx: MagicContext): BoundClient {\n return {\n ...client,\n resource(url, options = {}) {\n return createResource(client, url, { ...options, onCleanup: options.onCleanup ?? ctx.cleanup });\n },\n };\n}\n\nfunction register(summit: SummitGlobal, client: NetClient): void {\n summit.magic(\"fetch\", (ctx) => bindClient(client, ctx as MagicContext));\n summit.directive(\"resource\", makeResourceDirective(client));\n}\n\n/** Ready-to-use plugin backed by the default client: `Summit.plugin(net)`. */\nexport function net(summit: SummitGlobal): void {\n register(summit, http);\n}\n\n/** A configured plugin: `Summit.plugin(createNet({ baseURL: \"/api\" }))`. */\nexport function createNet(options: NetClientOptions): (summit: SummitGlobal) => void {\n const client = createClient(options);\n return (summit) => register(summit, client);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/net/runtime.ts","../src/net/client.ts","../src/net/resource.ts","../src/net/directive.ts","../src/net/registry.ts","../src/net/index.ts"],"names":["signal"],"mappings":"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","file":"net.js","sourcesContent":["/**\n * summitjs/net — signal-factory injection.\n *\n * The net layer must create signals with Summit's ONE reactivity runtime, but\n * it reaches that runtime differently per target: the npm entry imports it from\n * \"summitjs\" (deduped by the consumer's bundler), while the CDN drop-in reads it\n * off the global the core script already installed. Both wire it via\n * setSignalFactory before any resource is created; resource.ts makes its signals\n * through here.\n */\nimport type { Signal } from \"summitjs\";\n\ntype SignalFactory = <T>(initial: T) => Signal<T>;\n\nlet factory: SignalFactory | undefined;\n\nexport function setSignalFactory(fn: SignalFactory): void {\n factory = fn;\n}\n\nexport function signal<T>(initial: T): Signal<T> {\n if (!factory) {\n throw new Error(\n 'summitjs/net: reactivity is not wired up. Import it from \"summitjs/net\", or load summit-net.min.js after summit.min.js.',\n );\n }\n return factory(initial);\n}\n","/**\n * summitjs/net — the HTTP client.\n *\n * A thin, dependency-free wrapper over `fetch` that fixes fetch's real-world\n * footguns: it throws a structured error on non-2xx, sends and parses JSON\n * automatically, serializes params, supports a baseURL, default headers,\n * per-request timeout, and cancellation. No eval, no codegen, so it stays\n * CSP-safe like the rest of Summit.\n */\n\n/** A structured error thrown for non-2xx responses, timeouts, and aborts. */\nexport class NetError extends Error {\n status: number;\n body: unknown;\n url: string;\n method: string;\n aborted: boolean;\n\n constructor(\n message: string,\n init: { status: number; body: unknown; url: string; method: string; aborted: boolean },\n ) {\n super(message);\n this.name = \"NetError\";\n this.status = init.status;\n this.body = init.body;\n this.url = init.url;\n this.method = init.method;\n this.aborted = init.aborted;\n }\n}\n\nexport type NetParams = Record<string, string | number | boolean | null | undefined>;\n\nexport interface NetRequest {\n method?: string;\n headers?: Record<string, string>;\n params?: NetParams;\n /** A value: objects/arrays are JSON-encoded; strings are sent as-is. */\n body?: unknown;\n /** Abort this request when the given signal aborts. */\n signal?: AbortSignal;\n /** Abort automatically after this many milliseconds. */\n timeout?: number;\n}\n\nexport interface NetClientOptions {\n baseURL?: string;\n /** Static headers, or a function evaluated per request (e.g. for auth tokens). */\n headers?: Record<string, string> | (() => Record<string, string>);\n timeout?: number;\n /** Inspect or replace the request just before it is sent. */\n beforeRequest?: (req: NetInit) => void | NetInit;\n /** Inspect or replace the response before the body is read. */\n afterResponse?: (res: Response) => void | Response;\n /** Called with every NetError (e.g. redirect on 401). Re-throws afterward. */\n onError?: (err: NetError) => void;\n}\n\nexport type NetInit = RequestInit & { url: string };\n\nexport interface NetClient {\n request<T = unknown>(path: string, options?: NetRequest): Promise<T>;\n get<T = unknown>(path: string, options?: NetRequest): Promise<T>;\n post<T = unknown>(path: string, body?: unknown, options?: NetRequest): Promise<T>;\n put<T = unknown>(path: string, body?: unknown, options?: NetRequest): Promise<T>;\n patch<T = unknown>(path: string, body?: unknown, options?: NetRequest): Promise<T>;\n delete<T = unknown>(path: string, options?: NetRequest): Promise<T>;\n /** The client's live defaults; mutate to reconfigure baseURL/headers at runtime. */\n options: NetClientOptions;\n}\n\nexport function createClient(defaults: NetClientOptions = {}): NetClient {\n async function request<T = unknown>(path: string, req: NetRequest = {}): Promise<T> {\n const controller = new AbortController();\n const timeoutMs = req.timeout ?? defaults.timeout;\n const timer = timeoutMs ? setTimeout(() => controller.abort(), timeoutMs) : undefined;\n // Chain an external signal onto our controller so both timeout and the\n // caller's own cancellation abort the same request.\n if (req.signal) {\n if (req.signal.aborted) controller.abort();\n else req.signal.addEventListener(\"abort\", () => controller.abort(), { once: true });\n }\n\n const method = (req.method ?? \"GET\").toUpperCase();\n const url = withParams(joinURL(defaults.baseURL, path), req.params);\n const baseHeaders = typeof defaults.headers === \"function\" ? defaults.headers() : defaults.headers;\n const headers: Record<string, string> = { ...baseHeaders, ...req.headers };\n\n let init: NetInit = { url, method, headers, signal: controller.signal };\n if (req.body !== undefined && method !== \"GET\" && method !== \"HEAD\") {\n if (!hasHeader(headers, \"content-type\") && typeof req.body !== \"string\") {\n headers[\"content-type\"] = \"application/json\";\n }\n init.body = typeof req.body === \"string\" ? req.body : JSON.stringify(req.body);\n }\n\n const patched = defaults.beforeRequest?.(init);\n if (patched) init = patched;\n\n try {\n let res = await fetch(init.url, init);\n const swapped = defaults.afterResponse?.(res);\n if (swapped) res = swapped;\n const body = await readBody(res);\n if (!res.ok) {\n throw new NetError(`Request failed with status ${res.status}`, {\n status: res.status,\n body,\n url: init.url,\n method,\n aborted: false,\n });\n }\n return body as T;\n } catch (err) {\n const e = normalizeError(err, init.url, method);\n defaults.onError?.(e);\n throw e;\n } finally {\n if (timer) clearTimeout(timer);\n }\n }\n\n const client: NetClient = {\n request,\n get: (path, options) => request(path, { ...options, method: \"GET\" }),\n post: (path, body, options) => request(path, { ...options, method: \"POST\", body }),\n put: (path, body, options) => request(path, { ...options, method: \"PUT\", body }),\n patch: (path, body, options) => request(path, { ...options, method: \"PATCH\", body }),\n delete: (path, options) => request(path, { ...options, method: \"DELETE\" }),\n options: defaults,\n };\n return client;\n}\n\nfunction normalizeError(err: unknown, url: string, method: string): NetError {\n if (err instanceof NetError) return err;\n const aborted = (err as { name?: string })?.name === \"AbortError\";\n return new NetError(aborted ? \"Request aborted\" : (err as Error)?.message ?? \"Network request failed\", {\n status: 0,\n body: undefined,\n url,\n method,\n aborted,\n });\n}\n\nasync function readBody(res: Response): Promise<unknown> {\n const type = res.headers.get(\"content-type\") ?? \"\";\n const text = await res.text();\n if (text === \"\") return undefined;\n if (type.includes(\"application/json\") || type.includes(\"+json\")) {\n try {\n return JSON.parse(text);\n } catch {\n return text; // malformed JSON: hand back the raw text rather than throwing\n }\n }\n return text;\n}\n\nfunction hasHeader(headers: Record<string, string>, name: string): boolean {\n const lower = name.toLowerCase();\n return Object.keys(headers).some((k) => k.toLowerCase() === lower);\n}\n\nfunction joinURL(base: string | undefined, path: string): string {\n if (!base || /^[a-z][a-z0-9+.-]*:\\/\\//i.test(path)) return path;\n return `${base.replace(/\\/+$/, \"\")}/${path.replace(/^\\/+/, \"\")}`;\n}\n\nfunction withParams(url: string, params?: NetParams): string {\n if (!params) return url;\n const query = new URLSearchParams();\n for (const [key, value] of Object.entries(params)) {\n if (value != null) query.set(key, String(value));\n }\n const suffix = query.toString();\n if (!suffix) return url;\n return `${url}${url.includes(\"?\") ? \"&\" : \"?\"}${suffix}`;\n}\n","/**\n * summitjs/net — the reactive resource.\n *\n * A resource wraps a request in Summit signals: `data`, `error`, `loading`,\n * and `status` are exposed as auto-unwrapping getters, so reading them inside a\n * directive (`s-text=\"user.data.name\"`, `s-show=\"user.loading\"`) subscribes the\n * DOM to exactly that field. This is the key win over a promise wrapper: the\n * view updates itself.\n *\n * It also closes the footguns the stress test found:\n * - out-of-order responses: a monotonic sequence means the latest wins;\n * - non-2xx: surfaces through `error` (the client already throws);\n * - unmount leaks: `onCleanup` aborts the in-flight request;\n * - unhandled rejections: refetch never rejects — failures land in `error`.\n */\n\n// Signals come from the injected runtime factory (see runtime.ts) so the net\n// layer shares Summit's ONE reactivity runtime whether loaded via npm or CDN;\n// a second copy would mean resource signals could not drive the core's DOM.\nimport { signal } from \"./runtime.js\";\nimport type { NetClient, NetError, NetRequest } from \"./client.js\";\n\nexport type ResourceStatus = \"idle\" | \"loading\" | \"success\" | \"error\";\n\nexport interface ResourceOptions extends NetRequest {\n /** Fetch immediately on creation. Default true. */\n immediate?: boolean;\n /** Register a teardown callback (Summit passes the element's cleanup here). */\n onCleanup?: (fn: () => void) => void;\n}\n\nexport interface MutateOptions {\n /** Persist the optimistic change; on rejection the previous value is restored. */\n request?: () => Promise<unknown>;\n /** Roll back on a failed request. Default true. */\n rollbackOnError?: boolean;\n}\n\nexport interface Resource<T> {\n readonly data: T | undefined;\n readonly error: NetError | undefined;\n readonly loading: boolean;\n readonly status: ResourceStatus;\n /** Re-run the request (optionally overriding params/headers). Never rejects. */\n refetch(overrides?: NetRequest): Promise<T | undefined>;\n /** Optimistically set data; with `request`, roll back if the request fails. */\n mutate(next: T | ((prev: T | undefined) => T), options?: MutateOptions): void;\n /** Cancel the in-flight request and return to idle. */\n abort(): void;\n}\n\nexport function createResource<T = unknown>(\n client: NetClient,\n url: string | (() => string),\n options: ResourceOptions = {},\n): Resource<T> {\n const dataSig = signal<T | undefined>(undefined);\n const errorSig = signal<NetError | undefined>(undefined);\n const loadingSig = signal(false);\n const statusSig = signal<ResourceStatus>(\"idle\");\n\n const { immediate, onCleanup, ...requestDefaults } = options;\n let seq = 0;\n let controller: AbortController | undefined;\n\n async function refetch(overrides: NetRequest = {}): Promise<T | undefined> {\n const current = ++seq;\n controller?.abort();\n controller = new AbortController();\n loadingSig.set(true);\n statusSig.set(\"loading\");\n errorSig.set(undefined);\n try {\n const target = typeof url === \"function\" ? url() : url;\n const result = await client.request<T>(target, {\n ...requestDefaults,\n ...overrides,\n signal: controller.signal,\n });\n if (current !== seq) return undefined; // a newer request superseded this one\n dataSig.set(result);\n statusSig.set(\"success\");\n return result;\n } catch (err) {\n if (current !== seq) return undefined; // stale failure, ignore\n const e = err as NetError;\n if (e.aborted) {\n statusSig.set(\"idle\");\n return undefined;\n }\n errorSig.set(e);\n statusSig.set(\"error\");\n return undefined;\n } finally {\n if (current === seq) loadingSig.set(false);\n }\n }\n\n const resource: Resource<T> = {\n get data() {\n return dataSig();\n },\n get error() {\n return errorSig();\n },\n get loading() {\n return loadingSig();\n },\n get status() {\n return statusSig();\n },\n refetch,\n mutate(next, mutateOptions = {}) {\n const previous = dataSig.peek();\n dataSig.set((prev) => (typeof next === \"function\" ? (next as (p: T | undefined) => T)(prev) : next));\n if (!mutateOptions.request) return;\n // Persist the optimistic change; roll back and surface the error on failure.\n void Promise.resolve()\n .then(mutateOptions.request)\n .catch((err) => {\n if (mutateOptions.rollbackOnError !== false) dataSig.set(previous);\n errorSig.set(err as NetError);\n });\n },\n abort() {\n seq++;\n controller?.abort();\n controller = undefined;\n loadingSig.set(false);\n if (statusSig.peek() === \"loading\") statusSig.set(\"idle\");\n },\n };\n\n onCleanup?.(() => resource.abort());\n if (immediate !== false) void refetch();\n return resource;\n}\n","/**\n * summitjs/net — the `s-resource` directive.\n *\n * <div s-resource:users=\"{ url: '/api/users', immediate: true }\">\n * <p s-show=\"users.loading\">Loading…</p>\n * <p s-show=\"users.error\" s-text=\"users.error.message\"></p>\n * <template s-for=\"u in users.data || []\" :key=\"u.id\"><li s-text=\"u.name\"></li></template>\n * </div>\n *\n * The config expression is evaluated reactively: when a value it reads changes\n * (e.g. a `params: { q }` bound to state), the resource refetches with the new\n * params, and latest-wins cancellation makes stale responses harmless. An\n * optional `debounce` throttles those refetches for search-as-you-type.\n */\n\nimport type { DirectiveHandler } from \"summitjs\";\nimport { createResource, type Resource } from \"./resource.js\";\nimport type { NetClient, NetParams } from \"./client.js\";\n\ninterface ResourceConfig {\n url?: string;\n params?: NetParams;\n headers?: Record<string, string>;\n method?: string;\n body?: unknown;\n immediate?: boolean;\n debounce?: number;\n timeout?: number;\n}\n\nexport function makeResourceDirective(client: NetClient): DirectiveHandler {\n return (el, meta, utils) => {\n const name = meta.value;\n if (!name) {\n console.warn(\"[summit] s-resource needs a name, e.g. s-resource:users=\\\"{ url: '/api/users' }\\\".\", el);\n return;\n }\n\n const scope = utils.scopes[utils.scopes.length - 1] as Record<string, unknown> | undefined;\n let resource: Resource<unknown> | undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n const readConfig = (): ResourceConfig => (utils.evaluate(meta.expression) || {}) as ResourceConfig;\n\n utils.effect(() => {\n const cfg = readConfig(); // subscribes to reactive params (q, page, …)\n if (!cfg.url) return;\n const req = { params: cfg.params, headers: cfg.headers, method: cfg.method, body: cfg.body, timeout: cfg.timeout };\n\n if (!resource) {\n resource = createResource(client, () => readConfig().url ?? cfg.url!, {\n ...req,\n immediate: cfg.immediate !== false,\n onCleanup: utils.cleanup,\n });\n if (scope) scope[name] = resource;\n return;\n }\n\n // Config changed after creation: refetch with the new request options,\n // debounced when asked. Latest-wins means out-of-order replies are safe.\n const run = () => resource!.refetch(req);\n if (cfg.debounce) {\n if (timer) clearTimeout(timer);\n timer = setTimeout(run, cfg.debounce);\n } else {\n run();\n }\n });\n\n utils.cleanup(() => {\n if (timer) clearTimeout(timer);\n });\n };\n}\n","/**\n * summitjs/net — plugin registration.\n *\n * Shared by both the npm entry (index.ts) and the CDN drop-in (cdn.ts). It only\n * *type*-imports from \"summitjs\", so it carries no runtime dependency on the\n * core — the CDN build can bundle it into a self-contained IIFE that reads the\n * core off the global instead.\n */\nimport type { SummitGlobal, MagicContext } from \"summitjs\";\nimport { createClient } from \"./client.js\";\nimport type { NetClient } from \"./client.js\";\nimport { createResource } from \"./resource.js\";\nimport type { Resource, ResourceOptions } from \"./resource.js\";\nimport { makeResourceDirective } from \"./directive.js\";\n\n/** The default client shared by the `$fetch` magic and `s-resource` directive. */\nexport const http: NetClient = createClient();\n\n/** A client bound to an element: resources it creates auto-abort on unmount. */\nexport interface BoundClient extends NetClient {\n resource<T = unknown>(url: string | (() => string), options?: ResourceOptions): Resource<T>;\n}\n\nfunction bindClient(client: NetClient, ctx: MagicContext): BoundClient {\n return {\n ...client,\n resource(url, options = {}) {\n return createResource(client, url, { ...options, onCleanup: options.onCleanup ?? ctx.cleanup });\n },\n };\n}\n\nfunction register(summit: SummitGlobal, client: NetClient): void {\n summit.magic(\"fetch\", (ctx) => bindClient(client, ctx as MagicContext));\n summit.directive(\"resource\", makeResourceDirective(client));\n}\n\n/** Ready-to-use plugin backed by the default client: `Summit.plugin(net)`. */\nexport function net(summit: SummitGlobal): void {\n register(summit, http);\n}\n\n/** A configured plugin: `Summit.plugin(createNet({ baseURL: \"/api\" }))`. */\nexport function createNet(options: import(\"./client.js\").NetClientOptions): (summit: SummitGlobal) => void {\n const client = createClient(options);\n return (summit) => register(summit, client);\n}\n","/**\n * summitjs/net — a tiny reactive data layer for Summit (npm entry).\n *\n * Optional, shipped as its own entry so the core stays lean. Use it either\n * imperatively (`createClient`, `createResource`) or through Summit by\n * registering the plugin, which adds the `$fetch` magic and the `s-resource`\n * directive:\n *\n * import Summit from \"summitjs\";\n * import { net } from \"summitjs/net\";\n * Summit.plugin(net); // or Summit.plugin(createNet({ baseURL: \"/api\" }))\n * Summit.start();\n *\n * CDN users load summit-net.min.js after summit.min.js instead; see cdn.ts.\n */\nimport { signal } from \"summitjs\";\nimport { setSignalFactory } from \"./runtime.js\";\n\n// Wire the net layer to the core's reactivity runtime. `summitjs` is external in\n// this build, so a bundler dedupes it to the single installed copy.\nsetSignalFactory(signal);\n\nexport { createClient, NetError } from \"./client.js\";\nexport type { NetClient, NetClientOptions, NetRequest, NetParams, NetInit } from \"./client.js\";\nexport { createResource } from \"./resource.js\";\nexport type { Resource, ResourceOptions, ResourceStatus, MutateOptions } from \"./resource.js\";\nexport { http, net, createNet } from \"./registry.js\";\nexport type { BoundClient } from \"./registry.js\";\n"]}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
(function(){'use strict';var N;function C(e){N=e;}function h(e){if(!N)throw new Error('summitjs/net: reactivity is not wired up. Import it from "summitjs/net", or load summit-net.min.js after summit.min.js.');return N(e)}var R=class extends Error{constructor(n,o){super(n),this.name="NetError",this.status=o.status,this.body=o.body,this.url=o.url,this.method=o.method,this.aborted=o.aborted;}};function k(e={}){async function n(t,r={}){let s=new AbortController,i=r.timeout??e.timeout,l=i?setTimeout(()=>s.abort(),i):void 0;r.signal&&(r.signal.aborted?s.abort():r.signal.addEventListener("abort",()=>s.abort(),{once:true}));let m=(r.method??"GET").toUpperCase(),u=q(j(e.baseURL,t),r.params),d={...typeof e.headers=="function"?e.headers():e.headers,...r.headers},f={url:u,method:m,headers:d,signal:s.signal};r.body!==void 0&&m!=="GET"&&m!=="HEAD"&&(!P(d,"content-type")&&typeof r.body!="string"&&(d["content-type"]="application/json"),f.body=typeof r.body=="string"?r.body:JSON.stringify(r.body));let y=e.beforeRequest?.(f);y&&(f=y);try{let a=await fetch(f.url,f),c=e.afterResponse?.(a);c&&(a=c);let b=await E(a);if(!a.ok)throw new R(`Request failed with status ${a.status}`,{status:a.status,body:b,url:f.url,method:m,aborted:!1});return b}catch(a){let c=v(a,f.url,m);throw e.onError?.(c),c}finally{l&&clearTimeout(l);}}return {request:n,get:(t,r)=>n(t,{...r,method:"GET"}),post:(t,r,s)=>n(t,{...s,method:"POST",body:r}),put:(t,r,s)=>n(t,{...s,method:"PUT",body:r}),patch:(t,r,s)=>n(t,{...s,method:"PATCH",body:r}),delete:(t,r)=>n(t,{...r,method:"DELETE"}),options:e}}function v(e,n,o){if(e instanceof R)return e;let t=e?.name==="AbortError";return new R(t?"Request aborted":e?.message??"Network request failed",{status:0,body:void 0,url:n,method:o,aborted:t})}async function E(e){let n=e.headers.get("content-type")??"",o=await e.text();if(o!==""){if(n.includes("application/json")||n.includes("+json"))try{return JSON.parse(o)}catch{return o}return o}}function P(e,n){let o=n.toLowerCase();return Object.keys(e).some(t=>t.toLowerCase()===o)}function j(e,n){return !e||/^[a-z][a-z0-9+.-]*:\/\//i.test(n)?n:`${e.replace(/\/+$/,"")}/${n.replace(/^\/+/,"")}`}function q(e,n){if(!n)return e;let o=new URLSearchParams;for(let[r,s]of Object.entries(n))s!=null&&o.set(r,String(s));let t=o.toString();return t?`${e}${e.includes("?")?"&":"?"}${t}`:e}function T(e,n,o={}){let t=h(void 0),r=h(void 0),s=h(false),i=h("idle"),{immediate:l,onCleanup:m,...u}=o,p=0,d;async function f(a={}){let c=++p;d?.abort(),d=new AbortController,s.set(true),i.set("loading"),r.set(void 0);try{let b=typeof n=="function"?n():n,g=await e.request(b,{...u,...a,signal:d.signal});return c!==p?void 0:(t.set(g),i.set("success"),g)}catch(b){if(c!==p)return;let g=b;if(g.aborted){i.set("idle");return}r.set(g),i.set("error");return}finally{c===p&&s.set(false);}}let y={get data(){return t()},get error(){return r()},get loading(){return s()},get status(){return i()},refetch:f,mutate(a,c={}){let b=t.peek();t.set(g=>typeof a=="function"?a(g):a),c.request&&Promise.resolve().then(c.request).catch(g=>{c.rollbackOnError!==false&&t.set(b),r.set(g);});},abort(){p++,d?.abort(),d=void 0,s.set(false),i.peek()==="loading"&&i.set("idle");}};return m?.(()=>y.abort()),l!==false&&f(),y}function S(e){return (n,o,t)=>{let r=o.value;if(!r){console.warn(`[summit] s-resource needs a name, e.g. s-resource:users="{ url: '/api/users' }".`,n);return}let s=t.scopes[t.scopes.length-1],i,l,m=()=>t.evaluate(o.expression)||{};t.effect(()=>{let u=m();if(!u.url)return;let p={params:u.params,headers:u.headers,method:u.method,body:u.body,timeout:u.timeout};if(!i){i=T(e,()=>m().url??u.url,{...p,immediate:u.immediate!==false,onCleanup:t.cleanup}),s&&(s[r]=i);return}let d=()=>i.refetch(p);u.debounce?(l&&clearTimeout(l),l=setTimeout(d,u.debounce)):d();}),t.cleanup(()=>{l&&clearTimeout(l);});}}var O=k();function G(e,n){return {...e,resource(o,t={}){return T(e,o,{...t,onCleanup:t.onCleanup??n.cleanup})}}}function L(e,n){e.magic("fetch",o=>G(n,o)),e.directive("resource",S(n));}function x(e){L(e,O);}var w=typeof window<"u"?window.Summit:void 0;w&&typeof w.plugin=="function"?(C(w.signal),w.plugin(x)):typeof console<"u"&&console.warn("[summit] summit-net.min.js must load after summit.min.js.");
|
|
2
|
+
})();//# sourceMappingURL=summit-net.min.js.map
|
|
3
|
+
//# sourceMappingURL=summit-net.min.global.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/net/runtime.ts","../src/net/client.ts","../src/net/resource.ts","../src/net/directive.ts","../src/net/registry.ts","../src/net/cdn.ts"],"names":["factory","setSignalFactory","fn","signal","initial","NetError","message","init","createClient","defaults","request","path","req","controller","timeoutMs","timer","method","url","withParams","joinURL","headers","hasHeader","patched","res","swapped","body","readBody","err","e","normalizeError","options","aborted","type","text","name","lower","k","base","params","query","key","value","suffix","createResource","client","dataSig","errorSig","loadingSig","statusSig","immediate","onCleanup","requestDefaults","seq","refetch","overrides","current","target","result","resource","next","mutateOptions","previous","prev","makeResourceDirective","el","meta","utils","scope","readConfig","cfg","run","http","bindClient","ctx","register","summit","net","S"],"mappings":"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","file":"summit-net.min.js","sourcesContent":["/**\n * summitjs/net — signal-factory injection.\n *\n * The net layer must create signals with Summit's ONE reactivity runtime, but\n * it reaches that runtime differently per target: the npm entry imports it from\n * \"summitjs\" (deduped by the consumer's bundler), while the CDN drop-in reads it\n * off the global the core script already installed. Both wire it via\n * setSignalFactory before any resource is created; resource.ts makes its signals\n * through here.\n */\nimport type { Signal } from \"summitjs\";\n\ntype SignalFactory = <T>(initial: T) => Signal<T>;\n\nlet factory: SignalFactory | undefined;\n\nexport function setSignalFactory(fn: SignalFactory): void {\n factory = fn;\n}\n\nexport function signal<T>(initial: T): Signal<T> {\n if (!factory) {\n throw new Error(\n 'summitjs/net: reactivity is not wired up. Import it from \"summitjs/net\", or load summit-net.min.js after summit.min.js.',\n );\n }\n return factory(initial);\n}\n","/**\n * summitjs/net — the HTTP client.\n *\n * A thin, dependency-free wrapper over `fetch` that fixes fetch's real-world\n * footguns: it throws a structured error on non-2xx, sends and parses JSON\n * automatically, serializes params, supports a baseURL, default headers,\n * per-request timeout, and cancellation. No eval, no codegen, so it stays\n * CSP-safe like the rest of Summit.\n */\n\n/** A structured error thrown for non-2xx responses, timeouts, and aborts. */\nexport class NetError extends Error {\n status: number;\n body: unknown;\n url: string;\n method: string;\n aborted: boolean;\n\n constructor(\n message: string,\n init: { status: number; body: unknown; url: string; method: string; aborted: boolean },\n ) {\n super(message);\n this.name = \"NetError\";\n this.status = init.status;\n this.body = init.body;\n this.url = init.url;\n this.method = init.method;\n this.aborted = init.aborted;\n }\n}\n\nexport type NetParams = Record<string, string | number | boolean | null | undefined>;\n\nexport interface NetRequest {\n method?: string;\n headers?: Record<string, string>;\n params?: NetParams;\n /** A value: objects/arrays are JSON-encoded; strings are sent as-is. */\n body?: unknown;\n /** Abort this request when the given signal aborts. */\n signal?: AbortSignal;\n /** Abort automatically after this many milliseconds. */\n timeout?: number;\n}\n\nexport interface NetClientOptions {\n baseURL?: string;\n /** Static headers, or a function evaluated per request (e.g. for auth tokens). */\n headers?: Record<string, string> | (() => Record<string, string>);\n timeout?: number;\n /** Inspect or replace the request just before it is sent. */\n beforeRequest?: (req: NetInit) => void | NetInit;\n /** Inspect or replace the response before the body is read. */\n afterResponse?: (res: Response) => void | Response;\n /** Called with every NetError (e.g. redirect on 401). Re-throws afterward. */\n onError?: (err: NetError) => void;\n}\n\nexport type NetInit = RequestInit & { url: string };\n\nexport interface NetClient {\n request<T = unknown>(path: string, options?: NetRequest): Promise<T>;\n get<T = unknown>(path: string, options?: NetRequest): Promise<T>;\n post<T = unknown>(path: string, body?: unknown, options?: NetRequest): Promise<T>;\n put<T = unknown>(path: string, body?: unknown, options?: NetRequest): Promise<T>;\n patch<T = unknown>(path: string, body?: unknown, options?: NetRequest): Promise<T>;\n delete<T = unknown>(path: string, options?: NetRequest): Promise<T>;\n /** The client's live defaults; mutate to reconfigure baseURL/headers at runtime. */\n options: NetClientOptions;\n}\n\nexport function createClient(defaults: NetClientOptions = {}): NetClient {\n async function request<T = unknown>(path: string, req: NetRequest = {}): Promise<T> {\n const controller = new AbortController();\n const timeoutMs = req.timeout ?? defaults.timeout;\n const timer = timeoutMs ? setTimeout(() => controller.abort(), timeoutMs) : undefined;\n // Chain an external signal onto our controller so both timeout and the\n // caller's own cancellation abort the same request.\n if (req.signal) {\n if (req.signal.aborted) controller.abort();\n else req.signal.addEventListener(\"abort\", () => controller.abort(), { once: true });\n }\n\n const method = (req.method ?? \"GET\").toUpperCase();\n const url = withParams(joinURL(defaults.baseURL, path), req.params);\n const baseHeaders = typeof defaults.headers === \"function\" ? defaults.headers() : defaults.headers;\n const headers: Record<string, string> = { ...baseHeaders, ...req.headers };\n\n let init: NetInit = { url, method, headers, signal: controller.signal };\n if (req.body !== undefined && method !== \"GET\" && method !== \"HEAD\") {\n if (!hasHeader(headers, \"content-type\") && typeof req.body !== \"string\") {\n headers[\"content-type\"] = \"application/json\";\n }\n init.body = typeof req.body === \"string\" ? req.body : JSON.stringify(req.body);\n }\n\n const patched = defaults.beforeRequest?.(init);\n if (patched) init = patched;\n\n try {\n let res = await fetch(init.url, init);\n const swapped = defaults.afterResponse?.(res);\n if (swapped) res = swapped;\n const body = await readBody(res);\n if (!res.ok) {\n throw new NetError(`Request failed with status ${res.status}`, {\n status: res.status,\n body,\n url: init.url,\n method,\n aborted: false,\n });\n }\n return body as T;\n } catch (err) {\n const e = normalizeError(err, init.url, method);\n defaults.onError?.(e);\n throw e;\n } finally {\n if (timer) clearTimeout(timer);\n }\n }\n\n const client: NetClient = {\n request,\n get: (path, options) => request(path, { ...options, method: \"GET\" }),\n post: (path, body, options) => request(path, { ...options, method: \"POST\", body }),\n put: (path, body, options) => request(path, { ...options, method: \"PUT\", body }),\n patch: (path, body, options) => request(path, { ...options, method: \"PATCH\", body }),\n delete: (path, options) => request(path, { ...options, method: \"DELETE\" }),\n options: defaults,\n };\n return client;\n}\n\nfunction normalizeError(err: unknown, url: string, method: string): NetError {\n if (err instanceof NetError) return err;\n const aborted = (err as { name?: string })?.name === \"AbortError\";\n return new NetError(aborted ? \"Request aborted\" : (err as Error)?.message ?? \"Network request failed\", {\n status: 0,\n body: undefined,\n url,\n method,\n aborted,\n });\n}\n\nasync function readBody(res: Response): Promise<unknown> {\n const type = res.headers.get(\"content-type\") ?? \"\";\n const text = await res.text();\n if (text === \"\") return undefined;\n if (type.includes(\"application/json\") || type.includes(\"+json\")) {\n try {\n return JSON.parse(text);\n } catch {\n return text; // malformed JSON: hand back the raw text rather than throwing\n }\n }\n return text;\n}\n\nfunction hasHeader(headers: Record<string, string>, name: string): boolean {\n const lower = name.toLowerCase();\n return Object.keys(headers).some((k) => k.toLowerCase() === lower);\n}\n\nfunction joinURL(base: string | undefined, path: string): string {\n if (!base || /^[a-z][a-z0-9+.-]*:\\/\\//i.test(path)) return path;\n return `${base.replace(/\\/+$/, \"\")}/${path.replace(/^\\/+/, \"\")}`;\n}\n\nfunction withParams(url: string, params?: NetParams): string {\n if (!params) return url;\n const query = new URLSearchParams();\n for (const [key, value] of Object.entries(params)) {\n if (value != null) query.set(key, String(value));\n }\n const suffix = query.toString();\n if (!suffix) return url;\n return `${url}${url.includes(\"?\") ? \"&\" : \"?\"}${suffix}`;\n}\n","/**\n * summitjs/net — the reactive resource.\n *\n * A resource wraps a request in Summit signals: `data`, `error`, `loading`,\n * and `status` are exposed as auto-unwrapping getters, so reading them inside a\n * directive (`s-text=\"user.data.name\"`, `s-show=\"user.loading\"`) subscribes the\n * DOM to exactly that field. This is the key win over a promise wrapper: the\n * view updates itself.\n *\n * It also closes the footguns the stress test found:\n * - out-of-order responses: a monotonic sequence means the latest wins;\n * - non-2xx: surfaces through `error` (the client already throws);\n * - unmount leaks: `onCleanup` aborts the in-flight request;\n * - unhandled rejections: refetch never rejects — failures land in `error`.\n */\n\n// Signals come from the injected runtime factory (see runtime.ts) so the net\n// layer shares Summit's ONE reactivity runtime whether loaded via npm or CDN;\n// a second copy would mean resource signals could not drive the core's DOM.\nimport { signal } from \"./runtime.js\";\nimport type { NetClient, NetError, NetRequest } from \"./client.js\";\n\nexport type ResourceStatus = \"idle\" | \"loading\" | \"success\" | \"error\";\n\nexport interface ResourceOptions extends NetRequest {\n /** Fetch immediately on creation. Default true. */\n immediate?: boolean;\n /** Register a teardown callback (Summit passes the element's cleanup here). */\n onCleanup?: (fn: () => void) => void;\n}\n\nexport interface MutateOptions {\n /** Persist the optimistic change; on rejection the previous value is restored. */\n request?: () => Promise<unknown>;\n /** Roll back on a failed request. Default true. */\n rollbackOnError?: boolean;\n}\n\nexport interface Resource<T> {\n readonly data: T | undefined;\n readonly error: NetError | undefined;\n readonly loading: boolean;\n readonly status: ResourceStatus;\n /** Re-run the request (optionally overriding params/headers). Never rejects. */\n refetch(overrides?: NetRequest): Promise<T | undefined>;\n /** Optimistically set data; with `request`, roll back if the request fails. */\n mutate(next: T | ((prev: T | undefined) => T), options?: MutateOptions): void;\n /** Cancel the in-flight request and return to idle. */\n abort(): void;\n}\n\nexport function createResource<T = unknown>(\n client: NetClient,\n url: string | (() => string),\n options: ResourceOptions = {},\n): Resource<T> {\n const dataSig = signal<T | undefined>(undefined);\n const errorSig = signal<NetError | undefined>(undefined);\n const loadingSig = signal(false);\n const statusSig = signal<ResourceStatus>(\"idle\");\n\n const { immediate, onCleanup, ...requestDefaults } = options;\n let seq = 0;\n let controller: AbortController | undefined;\n\n async function refetch(overrides: NetRequest = {}): Promise<T | undefined> {\n const current = ++seq;\n controller?.abort();\n controller = new AbortController();\n loadingSig.set(true);\n statusSig.set(\"loading\");\n errorSig.set(undefined);\n try {\n const target = typeof url === \"function\" ? url() : url;\n const result = await client.request<T>(target, {\n ...requestDefaults,\n ...overrides,\n signal: controller.signal,\n });\n if (current !== seq) return undefined; // a newer request superseded this one\n dataSig.set(result);\n statusSig.set(\"success\");\n return result;\n } catch (err) {\n if (current !== seq) return undefined; // stale failure, ignore\n const e = err as NetError;\n if (e.aborted) {\n statusSig.set(\"idle\");\n return undefined;\n }\n errorSig.set(e);\n statusSig.set(\"error\");\n return undefined;\n } finally {\n if (current === seq) loadingSig.set(false);\n }\n }\n\n const resource: Resource<T> = {\n get data() {\n return dataSig();\n },\n get error() {\n return errorSig();\n },\n get loading() {\n return loadingSig();\n },\n get status() {\n return statusSig();\n },\n refetch,\n mutate(next, mutateOptions = {}) {\n const previous = dataSig.peek();\n dataSig.set((prev) => (typeof next === \"function\" ? (next as (p: T | undefined) => T)(prev) : next));\n if (!mutateOptions.request) return;\n // Persist the optimistic change; roll back and surface the error on failure.\n void Promise.resolve()\n .then(mutateOptions.request)\n .catch((err) => {\n if (mutateOptions.rollbackOnError !== false) dataSig.set(previous);\n errorSig.set(err as NetError);\n });\n },\n abort() {\n seq++;\n controller?.abort();\n controller = undefined;\n loadingSig.set(false);\n if (statusSig.peek() === \"loading\") statusSig.set(\"idle\");\n },\n };\n\n onCleanup?.(() => resource.abort());\n if (immediate !== false) void refetch();\n return resource;\n}\n","/**\n * summitjs/net — the `s-resource` directive.\n *\n * <div s-resource:users=\"{ url: '/api/users', immediate: true }\">\n * <p s-show=\"users.loading\">Loading…</p>\n * <p s-show=\"users.error\" s-text=\"users.error.message\"></p>\n * <template s-for=\"u in users.data || []\" :key=\"u.id\"><li s-text=\"u.name\"></li></template>\n * </div>\n *\n * The config expression is evaluated reactively: when a value it reads changes\n * (e.g. a `params: { q }` bound to state), the resource refetches with the new\n * params, and latest-wins cancellation makes stale responses harmless. An\n * optional `debounce` throttles those refetches for search-as-you-type.\n */\n\nimport type { DirectiveHandler } from \"summitjs\";\nimport { createResource, type Resource } from \"./resource.js\";\nimport type { NetClient, NetParams } from \"./client.js\";\n\ninterface ResourceConfig {\n url?: string;\n params?: NetParams;\n headers?: Record<string, string>;\n method?: string;\n body?: unknown;\n immediate?: boolean;\n debounce?: number;\n timeout?: number;\n}\n\nexport function makeResourceDirective(client: NetClient): DirectiveHandler {\n return (el, meta, utils) => {\n const name = meta.value;\n if (!name) {\n console.warn(\"[summit] s-resource needs a name, e.g. s-resource:users=\\\"{ url: '/api/users' }\\\".\", el);\n return;\n }\n\n const scope = utils.scopes[utils.scopes.length - 1] as Record<string, unknown> | undefined;\n let resource: Resource<unknown> | undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n const readConfig = (): ResourceConfig => (utils.evaluate(meta.expression) || {}) as ResourceConfig;\n\n utils.effect(() => {\n const cfg = readConfig(); // subscribes to reactive params (q, page, …)\n if (!cfg.url) return;\n const req = { params: cfg.params, headers: cfg.headers, method: cfg.method, body: cfg.body, timeout: cfg.timeout };\n\n if (!resource) {\n resource = createResource(client, () => readConfig().url ?? cfg.url!, {\n ...req,\n immediate: cfg.immediate !== false,\n onCleanup: utils.cleanup,\n });\n if (scope) scope[name] = resource;\n return;\n }\n\n // Config changed after creation: refetch with the new request options,\n // debounced when asked. Latest-wins means out-of-order replies are safe.\n const run = () => resource!.refetch(req);\n if (cfg.debounce) {\n if (timer) clearTimeout(timer);\n timer = setTimeout(run, cfg.debounce);\n } else {\n run();\n }\n });\n\n utils.cleanup(() => {\n if (timer) clearTimeout(timer);\n });\n };\n}\n","/**\n * summitjs/net — plugin registration.\n *\n * Shared by both the npm entry (index.ts) and the CDN drop-in (cdn.ts). It only\n * *type*-imports from \"summitjs\", so it carries no runtime dependency on the\n * core — the CDN build can bundle it into a self-contained IIFE that reads the\n * core off the global instead.\n */\nimport type { SummitGlobal, MagicContext } from \"summitjs\";\nimport { createClient } from \"./client.js\";\nimport type { NetClient } from \"./client.js\";\nimport { createResource } from \"./resource.js\";\nimport type { Resource, ResourceOptions } from \"./resource.js\";\nimport { makeResourceDirective } from \"./directive.js\";\n\n/** The default client shared by the `$fetch` magic and `s-resource` directive. */\nexport const http: NetClient = createClient();\n\n/** A client bound to an element: resources it creates auto-abort on unmount. */\nexport interface BoundClient extends NetClient {\n resource<T = unknown>(url: string | (() => string), options?: ResourceOptions): Resource<T>;\n}\n\nfunction bindClient(client: NetClient, ctx: MagicContext): BoundClient {\n return {\n ...client,\n resource(url, options = {}) {\n return createResource(client, url, { ...options, onCleanup: options.onCleanup ?? ctx.cleanup });\n },\n };\n}\n\nfunction register(summit: SummitGlobal, client: NetClient): void {\n summit.magic(\"fetch\", (ctx) => bindClient(client, ctx as MagicContext));\n summit.directive(\"resource\", makeResourceDirective(client));\n}\n\n/** Ready-to-use plugin backed by the default client: `Summit.plugin(net)`. */\nexport function net(summit: SummitGlobal): void {\n register(summit, http);\n}\n\n/** A configured plugin: `Summit.plugin(createNet({ baseURL: \"/api\" }))`. */\nexport function createNet(options: import(\"./client.js\").NetClientOptions): (summit: SummitGlobal) => void {\n const client = createClient(options);\n return (summit) => register(summit, client);\n}\n","/**\n * summitjs/net — CDN drop-in (self-contained IIFE).\n *\n * For no-build users: load this after the core Summit script and it wires the\n * data layer to the core's reactivity (read off the global) and registers the\n * `$fetch` magic and `s-resource` directive. No module resolution, no second\n * reactivity runtime.\n *\n * <script src=\"https://velofy.github.io/summitjs/summit.min.js\" defer></script>\n * <script src=\"https://velofy.github.io/summitjs/summit-net.min.js\" defer></script>\n */\nimport type { SummitGlobal } from \"summitjs\";\nimport { setSignalFactory } from \"./runtime.js\";\nimport { net } from \"./registry.js\";\n\nconst S = typeof window !== \"undefined\" ? (window as unknown as { Summit?: SummitGlobal }).Summit : undefined;\nif (S && typeof S.plugin === \"function\") {\n setSignalFactory(S.signal);\n S.plugin(net);\n} else if (typeof console !== \"undefined\") {\n console.warn(\"[summit] summit-net.min.js must load after summit.min.js.\");\n}\n"]}
|