@tiaforge-ui/sinter 0.2.0 → 0.3.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/README.md +3 -2
- package/dist/cache.js +9 -2
- package/dist/check/index.js +2 -1
- package/dist/check/shadow.d.ts +1 -1
- package/dist/check/shadow.js +18 -4
- package/dist/compiler/check-unit.d.ts +14 -2
- package/dist/compiler/compile.js +120 -13
- package/dist/compiler/diagnostic-docs.js +13 -3
- package/dist/compiler/diagnostics.d.ts +2 -0
- package/dist/compiler/diagnostics.js +2 -0
- package/dist/compiler/expr.d.ts +16 -0
- package/dist/compiler/expr.js +56 -4
- package/dist/compiler/vocabulary.d.ts +1 -0
- package/dist/compiler/vocabulary.js +6 -1
- package/dist/ir.d.ts +4 -0
- package/dist/ir.js +21 -3
- package/dist/listbox.d.ts +3 -0
- package/dist/listbox.js +66 -0
- package/dist/paginate.js +11 -4
- package/dist/persist.js +4 -1
- package/dist/runtime/app.d.ts +9 -0
- package/dist/runtime/app.js +96 -27
- package/dist/runtime/config.d.ts +8 -1
- package/dist/runtime/config.js +9 -2
- package/dist/runtime/helpers.js +1 -1
- package/dist/runtime/request.d.ts +1 -1
- package/dist/runtime/request.js +10 -3
- package/dist/runtime/store.js +2 -1
- package/dist/sse.js +6 -3
- package/dist/testing/index.js +2 -1
- package/docs/diagnostics.md +23 -3
- package/html-custom-data.json +29 -1
- package/llms.txt +8 -3
- package/package.json +3 -1
package/dist/runtime/config.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
// comes only from configure() in JavaScript, never from the DOM.
|
|
3
3
|
import { defaultEnv } from './env.js';
|
|
4
4
|
export const config = {
|
|
5
|
-
request: [], sessionProbe: null, onError: null, locale: null, urlSchemes: [], origins: [], transport: null,
|
|
5
|
+
request: [], sessionProbe: null, onError: null, locale: null, urlSchemes: [], origins: [], transport: null,
|
|
6
|
+
confirm: (question) => globalThis.confirm(question), env: defaultEnv,
|
|
6
7
|
};
|
|
7
8
|
export function configure(options) {
|
|
8
9
|
const { env, ...rest } = options;
|
|
@@ -14,10 +15,16 @@ export function configure(options) {
|
|
|
14
15
|
export class SinterError extends Error {
|
|
15
16
|
status;
|
|
16
17
|
body;
|
|
17
|
-
|
|
18
|
+
detail;
|
|
19
|
+
/**
|
|
20
|
+
* @param detail what went wrong in words: the server's message or text, or a thrown error's
|
|
21
|
+
* message; null when there is none (`message` then falls back to the HTTP status)
|
|
22
|
+
*/
|
|
23
|
+
constructor(status, body, message, detail = null) {
|
|
18
24
|
super(message);
|
|
19
25
|
this.status = status;
|
|
20
26
|
this.body = body;
|
|
27
|
+
this.detail = detail;
|
|
21
28
|
this.name = 'SinterError';
|
|
22
29
|
}
|
|
23
30
|
}
|
package/dist/runtime/helpers.js
CHANGED
|
@@ -44,7 +44,7 @@ export function number(v, opts) {
|
|
|
44
44
|
const BYTE_UNITS = ['byte', 'kilobyte', 'megabyte', 'gigabyte', 'terabyte'];
|
|
45
45
|
export function bytes(v) {
|
|
46
46
|
let n = Number(v);
|
|
47
|
-
if (v == null || Number.isNaN(n))
|
|
47
|
+
if (v == null || v === '' || Number.isNaN(n))
|
|
48
48
|
return '';
|
|
49
49
|
let i = 0;
|
|
50
50
|
while (Math.abs(n) >= 1024 && i < BYTE_UNITS.length - 1) {
|
|
@@ -3,7 +3,7 @@ import type { Env } from './env.js';
|
|
|
3
3
|
export declare const TIMEOUT = 30000;
|
|
4
4
|
export interface SendInit {
|
|
5
5
|
method?: string;
|
|
6
|
-
/** JSON-serialized unless FormData */
|
|
6
|
+
/** JSON-serialized unless FormData or URLSearchParams (sent form-encoded) */
|
|
7
7
|
body?: unknown;
|
|
8
8
|
headers?: Record<string, string>;
|
|
9
9
|
signal?: AbortSignal | undefined;
|
package/dist/runtime/request.js
CHANGED
|
@@ -44,14 +44,18 @@ export function createRequestLayer(env, chain) {
|
|
|
44
44
|
let body = init.body;
|
|
45
45
|
if (body == null || method == 'GET')
|
|
46
46
|
body = undefined;
|
|
47
|
+
else if (body instanceof URLSearchParams) {
|
|
48
|
+
body = body.toString();
|
|
49
|
+
headers.set('Content-Type', 'application/x-www-form-urlencoded;charset=UTF-8');
|
|
50
|
+
}
|
|
47
51
|
else if (!(body instanceof FormData)) {
|
|
48
52
|
body = JSON.stringify(body);
|
|
49
53
|
headers.set('Content-Type', 'application/json');
|
|
50
54
|
}
|
|
51
55
|
headers.has('Accept') || headers.set('Accept', 'application/json');
|
|
52
56
|
// mutations outlive the page: a navigation right after a click must not cancel the write
|
|
53
|
-
// (keepalive bodies are limited to 64
|
|
54
|
-
const keepalive = method != 'GET' && method != 'HEAD' && (body == null || (typeof body == 'string' && body
|
|
57
|
+
// (keepalive bodies are limited to 64 KiB; larger ones and uploads go without it)
|
|
58
|
+
const keepalive = method != 'GET' && method != 'HEAD' && (body == null || (typeof body == 'string' && bytes(body) < 60_000));
|
|
55
59
|
inflight++;
|
|
56
60
|
try {
|
|
57
61
|
const res = await run(new Request(target, { method, headers, body: body ?? null, credentials: 'same-origin', signal: init.signal ?? null, keepalive }));
|
|
@@ -71,12 +75,15 @@ export function createRequestLayer(env, chain) {
|
|
|
71
75
|
const res = await raw(url, init), body = await parseBody(res);
|
|
72
76
|
if (!res.ok) {
|
|
73
77
|
const b = body, m = typeof body == 'string' ? body.length < 200 && body.trim() : b?.message ?? b?.error;
|
|
74
|
-
|
|
78
|
+
const detail = typeof m == 'string' && m ? m : null;
|
|
79
|
+
throw new SinterError(res.status, body, detail ?? `${res.status} ${res.statusText}`.trim(), detail);
|
|
75
80
|
}
|
|
76
81
|
return body;
|
|
77
82
|
}
|
|
78
83
|
return { send, raw, get inflight() { return inflight; } };
|
|
79
84
|
}
|
|
85
|
+
/** UTF-8 size of a string; a short one cannot reach the limit (at most 3 bytes per UTF-16 unit). */
|
|
86
|
+
const bytes = (s) => (s.length < 20_000 ? s.length : new TextEncoder().encode(s).length);
|
|
80
87
|
/** 204 or empty → null; JSON content type → parsed; anything else → text. */
|
|
81
88
|
export async function parseBody(res) {
|
|
82
89
|
const text = res.status == 204 ? '' : await res.text();
|
package/dist/runtime/store.js
CHANGED
|
@@ -160,7 +160,8 @@ export function createResource(name, o) {
|
|
|
160
160
|
}
|
|
161
161
|
};
|
|
162
162
|
function visible() {
|
|
163
|
-
|
|
163
|
+
// a locale change re-sorts strings: a view sorted for another locale is stale
|
|
164
|
+
if (view && !(coll && field && o.locale?.() !== sortedFor))
|
|
164
165
|
return view;
|
|
165
166
|
if (!coll)
|
|
166
167
|
return (view = { l: [], get: () => undefined, s: over ? over.s : single });
|
package/dist/sse.js
CHANGED
|
@@ -199,10 +199,13 @@ class Connection {
|
|
|
199
199
|
const sse = {
|
|
200
200
|
name: 'sse',
|
|
201
201
|
install(app) {
|
|
202
|
-
// connect after mount has created resources and loaders
|
|
202
|
+
// connect after mount has created resources and loaders, unless the app is gone by then
|
|
203
|
+
let alive = true;
|
|
204
|
+
app.addCleanup(() => { alive = false; });
|
|
203
205
|
app.env.queueMicrotask(() => {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
+
if (alive)
|
|
207
|
+
for (const def of app.ir.syncs)
|
|
208
|
+
new Connection(app, def).start();
|
|
206
209
|
});
|
|
207
210
|
},
|
|
208
211
|
};
|
package/dist/testing/index.js
CHANGED
|
@@ -13,8 +13,9 @@ import optimistic from '../optimistic.js';
|
|
|
13
13
|
import paginate from '../paginate.js';
|
|
14
14
|
import each from '../each.js';
|
|
15
15
|
import persist from '../persist.js';
|
|
16
|
+
import listbox from '../listbox.js';
|
|
16
17
|
import sse from '../sse.js';
|
|
17
|
-
const FEATURES = { dev, sse, optimistic, paginate, persist, cache, each };
|
|
18
|
+
const FEATURES = { dev, sse, optimistic, paginate, persist, cache, each, listbox };
|
|
18
19
|
function isResponseSpec(v) {
|
|
19
20
|
if (!v || typeof v !== 'object' || Array.isArray(v))
|
|
20
21
|
return false;
|
package/docs/diagnostics.md
CHANGED
|
@@ -88,7 +88,7 @@ Error, for example:
|
|
|
88
88
|
|
|
89
89
|
> `m.is_read = true` mutates a resource. Use `patch('messages', m, {is_read: true})`
|
|
90
90
|
|
|
91
|
-
Resource data is read-only in expressions: it mirrors the server, and a direct write would be lost on the next refresh or event.
|
|
91
|
+
Resource data is read-only in expressions: it mirrors the server, and a direct write (an assignment, a mutating method such as `push()`, or `Object.assign()` on an item) would be lost on the next refresh or event.
|
|
92
92
|
|
|
93
93
|
**Fix:** Use the `patch()` call from the message, or copy the item with `structuredClone(c)` to edit it in state.
|
|
94
94
|
|
|
@@ -108,9 +108,9 @@ Error, for example:
|
|
|
108
108
|
|
|
109
109
|
> `onclick` cannot be bound with `data-sn-attr`; use `data-sn-on-click`
|
|
110
110
|
|
|
111
|
-
The binding could run script or inject markup from data: event-handler attributes, `style`, `srcdoc`, `innerHTML`, bindings on `<script>` and similar elements, URL templates that do not start with a literal path
|
|
111
|
+
The binding could run script or inject markup from data: event-handler attributes, `style`, `srcdoc`, `innerHTML`, bindings on `<script>` and similar elements, URL templates that do not start with a literal path (or with a literal origin that lacks its `/` before a value), persisted password fields, or `constructor`, `__proto__` and `prototype` named in an expression.
|
|
112
112
|
|
|
113
|
-
**Fix:** Use the safe equivalent the message names: `data-sn-on-*` for events, `data-sn-style` for styles, a literal `/path` at the start of a URL template.
|
|
113
|
+
**Fix:** Use the safe equivalent the message names: `data-sn-on-*` for events, `data-sn-style` for styles, a literal `/path` (or `https://host/`) at the start of a URL template.
|
|
114
114
|
|
|
115
115
|
## SN208 type-error
|
|
116
116
|
|
|
@@ -122,6 +122,16 @@ TypeScript found a type error in an expression. Reported by `sinter check`, `sin
|
|
|
122
122
|
|
|
123
123
|
**Fix:** Fix the expression, or the type: `type` on `<sn-resource>`, the OpenAPI spec, or a JSDoc cast in `data-sn-state`.
|
|
124
124
|
|
|
125
|
+
## SN209 name-conflict
|
|
126
|
+
|
|
127
|
+
Error, for example:
|
|
128
|
+
|
|
129
|
+
> `s` is already defined here by `data-sn-let`; use another name
|
|
130
|
+
|
|
131
|
+
A `data-sn-let` name is inlined wherever it is read, so it cannot share a name with a state variable or loop variable in its scope, or be redeclared inside it. A resource cannot take the name of a store function (`refresh`, `upsert`, `patch`, `remove`, `page`, `showModal`, `close`, `reset`, `emit`).
|
|
132
|
+
|
|
133
|
+
**Fix:** Rename the let, the state or loop variable, or the resource.
|
|
134
|
+
|
|
125
135
|
## SN301 unknown-resource
|
|
126
136
|
|
|
127
137
|
Error, for example:
|
|
@@ -202,6 +212,16 @@ The resource matched an operation in the OpenAPI spec, but its `items` or `total
|
|
|
202
212
|
|
|
203
213
|
**Fix:** Fix the path in `items` or `total`, or the spec if the API changed.
|
|
204
214
|
|
|
215
|
+
## SN309 unknown-dialog
|
|
216
|
+
|
|
217
|
+
Error, for example:
|
|
218
|
+
|
|
219
|
+
> no `<dialog id="compose-dialg">` on this page. Did you mean `compose-dialog`?
|
|
220
|
+
|
|
221
|
+
`showModal(id)` and `close(id)` name a `<dialog>` by its literal `id`, which must be on the page or one of its partials.
|
|
222
|
+
|
|
223
|
+
**Fix:** Fix the id, or add `id` to the `<dialog>`. `close()` without an argument closes the dialog around the element.
|
|
224
|
+
|
|
205
225
|
## SN310 unknown-action
|
|
206
226
|
|
|
207
227
|
Error, for example:
|
package/html-custom-data.json
CHANGED
|
@@ -118,6 +118,10 @@
|
|
|
118
118
|
"name": "data-sn-state",
|
|
119
119
|
"description": "declares local reactive state for the element and its subtree (object literal)"
|
|
120
120
|
},
|
|
121
|
+
{
|
|
122
|
+
"name": "data-sn-let",
|
|
123
|
+
"description": "derived names for the element and its subtree, computed where they are read; later names may use earlier ones (object literal {name: expression})"
|
|
124
|
+
},
|
|
121
125
|
{
|
|
122
126
|
"name": "data-sn-text",
|
|
123
127
|
"description": "sets textContent; never HTML (value expression)"
|
|
@@ -162,6 +166,10 @@
|
|
|
162
166
|
"name": "data-sn-else",
|
|
163
167
|
"description": "on a <template> directly after an if template"
|
|
164
168
|
},
|
|
169
|
+
{
|
|
170
|
+
"name": "data-sn-listbox",
|
|
171
|
+
"description": "on a text input: arrow keys mark a [role=option] of that list (empty: the nearest [role=listbox] around the input), Enter or Tab clicks it, Escape fires `dismiss` (id of the suggestion list, or empty)"
|
|
172
|
+
},
|
|
165
173
|
{
|
|
166
174
|
"name": "data-sn-persist",
|
|
167
175
|
"description": "persists the model or state in localStorage under sn: (storage key)"
|
|
@@ -216,7 +224,15 @@
|
|
|
216
224
|
},
|
|
217
225
|
{
|
|
218
226
|
"name": "data-sn-catch",
|
|
219
|
-
"description": "runs after an error; $error = {status, body, message} (handler expression)"
|
|
227
|
+
"description": "runs after an error; $error = {status, body, message, detail} (handler expression)"
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
"name": "data-sn-confirm",
|
|
231
|
+
"description": "asks this question before the action runs (configure({ confirm }), window.confirm by default); no question when falsy (value expression)"
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
"name": "data-sn-encoding",
|
|
235
|
+
"description": "form: the body (data-sn-body, arrays as repeated keys, or the form) goes as application/x-www-form-urlencoded (json | form)"
|
|
220
236
|
},
|
|
221
237
|
{
|
|
222
238
|
"name": "data-sn-path",
|
|
@@ -245,6 +261,18 @@
|
|
|
245
261
|
{
|
|
246
262
|
"name": "data-sn-on-keydown",
|
|
247
263
|
"description": "plain event handler, no request; e.g. data-sn-on-click"
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
"name": "data-sn-debounce-input",
|
|
267
|
+
"description": "delays only that event's data-sn-on-<event> handler (it runs with the last event); e.g. data-sn-debounce-input"
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
"name": "data-sn-debounce-keyup",
|
|
271
|
+
"description": "delays only that event's data-sn-on-<event> handler (it runs with the last event); e.g. data-sn-debounce-input"
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
"name": "data-sn-debounce-scroll",
|
|
275
|
+
"description": "delays only that event's data-sn-on-<event> handler (it runs with the last event); e.g. data-sn-debounce-input"
|
|
248
276
|
}
|
|
249
277
|
]
|
|
250
278
|
}
|
package/llms.txt
CHANGED
|
@@ -7,6 +7,7 @@ Package: @tiaforge-ui/sinter. Every directive is a `data-sn-*` attribute or an `
|
|
|
7
7
|
## Attributes
|
|
8
8
|
|
|
9
9
|
- `data-sn-state` (object literal): declares local reactive state for the element and its subtree
|
|
10
|
+
- `data-sn-let` (object literal {name: expression}): derived names for the element and its subtree, computed where they are read; later names may use earlier ones
|
|
10
11
|
- `data-sn-text` (value expression): sets textContent; never HTML
|
|
11
12
|
- `data-sn-attr` (object literal {href: …}): literal keys; null/false removes, true sets empty; URL values are checked
|
|
12
13
|
- `data-sn-class` (object literal {unread: …}): toggles each class
|
|
@@ -18,6 +19,7 @@ Package: @tiaforge-ui/sinter. Every directive is a `data-sn-*` attribute or an `
|
|
|
18
19
|
- `data-sn-key` (value expression): row identity; defaults to the resource key or object identity
|
|
19
20
|
- `data-sn-if` (value expression): renders a <template> while truthy
|
|
20
21
|
- `data-sn-else`: on a <template> directly after an if template
|
|
22
|
+
- `data-sn-listbox` (id of the suggestion list, or empty): on a text input: arrow keys mark a [role=option] of that list (empty: the nearest [role=listbox] around the input), Enter or Tab clicks it, Escape fires `dismiss`
|
|
21
23
|
- `data-sn-persist` (storage key): persists the model or state in localStorage under sn:
|
|
22
24
|
- `data-sn-get` (URL template): GET action; latest wins
|
|
23
25
|
- `data-sn-post` (URL template): POST action
|
|
@@ -31,10 +33,13 @@ Package: @tiaforge-ui/sinter. Every directive is a `data-sn-*` attribute or an `
|
|
|
31
33
|
- `data-sn-concurrency` (block | queue | latest): mutations only; default block for forms, queue otherwise
|
|
32
34
|
- `data-sn-optimistic` (handler expression): patch/upsert/remove calls applied before the request, rolled back on failure
|
|
33
35
|
- `data-sn-then` (handler expression): runs after a 2xx response; $result = parsed body
|
|
34
|
-
- `data-sn-catch` (handler expression): runs after an error; $error = {status, body, message}
|
|
36
|
+
- `data-sn-catch` (handler expression): runs after an error; $error = {status, body, message, detail}
|
|
37
|
+
- `data-sn-confirm` (value expression): asks this question before the action runs (configure({ confirm }), window.confirm by default); no question when falsy
|
|
38
|
+
- `data-sn-encoding` (json | form): form: the body (data-sn-body, arrays as repeated keys, or the form) goes as application/x-www-form-urlencoded
|
|
35
39
|
- `data-sn-path` (URL patterns /a/:b …): on <html>: the path shapes this page is served for; `:name` segments appear read-only in $url
|
|
36
40
|
- `data-sn-data` (resource name): on <script type="application/json"> in <head>: initial data for a resource
|
|
37
41
|
- `data-sn-on-<event>` (handler expression): plain event handler, no request; e.g. data-sn-on-click
|
|
42
|
+
- `data-sn-debounce-<event>` (milliseconds): delays only that event's data-sn-on-<event> handler (it runs with the last event); e.g. data-sn-debounce-input
|
|
38
43
|
|
|
39
44
|
## Elements
|
|
40
45
|
|
|
@@ -60,11 +65,11 @@ Package: @tiaforge-ui/sinter. Every directive is a `data-sn-*` attribute or an `
|
|
|
60
65
|
|
|
61
66
|
## Store functions
|
|
62
67
|
|
|
63
|
-
`refresh()`, `upsert()`, `patch()`, `remove()`, `page()`, `close()`, `reset()`, `emit()`: `refresh('name')`, `upsert('name', item)`, `patch('name', item, {…})`, `remove('name', item)`, `page('name', 'next' | 'prev' | 'first' | 'last' | n)`, `close()` the nearest dialog, `reset()` the nearest form, `emit('event', detail)`.
|
|
68
|
+
`refresh()`, `upsert()`, `patch()`, `remove()`, `page()`, `showModal()`, `close()`, `reset()`, `emit()`: `refresh('name')`, `upsert('name', item)`, `patch('name', item, {…})`, `remove('name', item)`, `page('name', 'next' | 'prev' | 'first' | 'last' | n)`, `showModal('id')` opens a dialog by its literal id, `close()` the nearest dialog or `close('id')` that one, `reset()` the nearest form, `emit('event', detail)`.
|
|
64
69
|
|
|
65
70
|
## Diagnostics
|
|
66
71
|
|
|
67
|
-
`SN101 unknown-attribute`, `SN102 wrong-element`, `SN103 missing-key`, `SN104 unsupported-model`, `SN201 unknown-identifier`, `SN202 forbidden-global`, `SN203 side-effect`, `SN204 optimistic-scope`, `SN205 resource-mutation`, `SN206 unknown-namespace-member`, `SN207 unsafe-sink`, `SN208 type-error`, `SN301 unknown-resource`, `SN302 duplicate-resource`, `SN303 unknown-meta-field`, `SN304 keyset-jump`, `SN305 include-not-found`, `SN306 include-cycle`, `SN307 openapi-invalid`, `SN308 unknown-schema-field`, `SN310 unknown-action`, `SN401 parse-error`, `SN501 unused-resource`, `SN502 non-reactive-value`, `SN503 data-bound-id`, `SN504 untyped-resource`, `SN505 unknown-query-param`, `SN506 untyped-event`.
|
|
72
|
+
`SN101 unknown-attribute`, `SN102 wrong-element`, `SN103 missing-key`, `SN104 unsupported-model`, `SN201 unknown-identifier`, `SN202 forbidden-global`, `SN203 side-effect`, `SN204 optimistic-scope`, `SN205 resource-mutation`, `SN206 unknown-namespace-member`, `SN207 unsafe-sink`, `SN208 type-error`, `SN209 name-conflict`, `SN301 unknown-resource`, `SN302 duplicate-resource`, `SN303 unknown-meta-field`, `SN304 keyset-jump`, `SN305 include-not-found`, `SN306 include-cycle`, `SN307 openapi-invalid`, `SN308 unknown-schema-field`, `SN309 unknown-dialog`, `SN310 unknown-action`, `SN401 parse-error`, `SN501 unused-resource`, `SN502 non-reactive-value`, `SN503 data-bound-id`, `SN504 untyped-resource`, `SN505 unknown-query-param`, `SN506 untyped-event`.
|
|
68
73
|
|
|
69
74
|
## Recipes
|
|
70
75
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiaforge-ui/sinter",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Bind JSON APIs to plain HTML: keyed resources, SSE sync, template bindings and optimistic mutations, compiled at build time.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Tia Heinrich",
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"./paginate": "./dist/paginate.js",
|
|
39
39
|
"./each": "./dist/each.js",
|
|
40
40
|
"./persist": "./dist/persist.js",
|
|
41
|
+
"./listbox": "./dist/listbox.js",
|
|
41
42
|
"./cache": "./dist/cache.js",
|
|
42
43
|
"./compiler": "./dist/compiler/index.js",
|
|
43
44
|
"./vite": "./dist/vite.js",
|
|
@@ -61,6 +62,7 @@
|
|
|
61
62
|
"bench": "vitest run --config bench/runtime/vitest.config.ts",
|
|
62
63
|
"size": "npm run build && node scripts/size.mjs",
|
|
63
64
|
"docs": "UPDATE_DOCS=1 vitest run test/docs.test.ts",
|
|
65
|
+
"release": "node scripts/release.mjs",
|
|
64
66
|
"prepublishOnly": "rm -rf dist && npm run build && npm test"
|
|
65
67
|
},
|
|
66
68
|
"dependencies": {
|