@askrjs/askr 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +874 -0
- package/README.md +61 -29
- package/capabilities.json +19 -9
- package/dist/actions/index.js +56 -12
- package/dist/benchmark.js +10 -15
- package/dist/boot/index.d.ts +3 -2
- package/dist/boot/index.js +1 -1
- package/dist/boot-DaoTYoCw.js +3989 -0
- package/dist/browser-activity-CB8e-V4f.js +220 -0
- package/dist/components/index.d.ts +8 -1
- package/dist/components/index.js +1 -43
- package/dist/{aria-hKjThHcp.js → compose-ref-DWzdbxsD.js} +41 -4
- package/dist/control/index.d.ts +2 -0
- package/dist/control/index.js +1 -1
- package/dist/control-nfpT2fbK.js +209 -0
- package/dist/core.d.ts +5 -9
- package/dist/csp-nonce-DwGw1A6Q.js +35 -0
- package/dist/data/index.js +3 -3
- package/dist/{data-2edW0jhF.js → data-BCwClJiF.js} +283 -94
- package/dist/data-runtime-C1bFAlba.js +242 -0
- package/dist/{deferred-CbgNhf67.js → deferred-bEEHHvic.js} +42 -20
- package/dist/domains/actions.d.ts +8 -3
- package/dist/domains/component.d.ts +1 -107
- package/dist/domains/context.d.ts +0 -15
- package/dist/domains/control.d.ts +36 -249
- package/dist/domains/data.d.ts +31 -13
- package/dist/domains/diagnostics.d.ts +1 -2
- package/dist/domains/lifecycle.d.ts +2 -5
- package/dist/domains/refs.d.ts +0 -2
- package/dist/domains/routing.d.ts +43 -8
- package/dist/domains/scheduler.d.ts +1 -76
- package/dist/domains/server.d.ts +5 -2
- package/dist/domains/state.d.ts +2 -5
- package/dist/domains/telemetry.d.ts +0 -2
- package/dist/elements.d.ts +214 -51
- package/dist/error-boundary-Bpwofwps.js +293 -0
- package/dist/foundations/icon/index.d.ts +0 -58
- package/dist/foundations/icon/index.js +2 -2
- package/dist/foundations/index.js +2 -1
- package/dist/foundations/interactions/index.js +1 -1
- package/dist/foundations/state/index.d.ts +3 -3
- package/dist/foundations/state/index.js +5 -3
- package/dist/foundations/structures/index.js +2 -1
- package/dist/foundations/utilities/index.d.ts +2 -0
- package/dist/foundations/utilities/index.js +1 -2
- package/dist/fx/index.d.ts +31 -58
- package/dist/fx/index.js +313 -195
- package/dist/hooks-BimKStr4.js +202 -0
- package/dist/index.d.ts +11 -74
- package/dist/index.js +8 -447
- package/dist/instance-BAnfmXOR.js +988 -0
- package/dist/{interactions-CTeACdT_.js → interactions-D2BoREb-.js} +3 -2
- package/dist/jsx-dev-runtime.d.ts +41 -18
- package/dist/jsx-dev-runtime.js +1 -6
- package/dist/{jsx-runtime-CPJXAOBF.js → jsx-runtime-C4idqHY0.js} +18 -7
- package/dist/jsx-runtime.js +1 -1
- package/dist/jsx.d.ts +61 -37
- package/dist/{manifest-D1P3UNWV.js → manifest-CW9gyNBY.js} +3 -3
- package/dist/{navigate-prC5V8XV.js → navigate-CP_0Zy5U.js} +454 -87
- package/dist/navigation.d.ts +12 -3
- package/dist/peer-types.d.ts +8 -0
- package/dist/portal-DuOKfnLa.js +349 -0
- package/dist/promise-Dz8iIHAm.js +16 -0
- package/dist/query-registry-WMCN0BJL.js +320 -0
- package/dist/readable-DFWOsTac.js +21 -0
- package/dist/{render-context-B7ohlh6t.js → render-context-DGHnEveC.js} +27 -11
- package/dist/render-sync-IC5lMFpq.js +1240 -0
- package/dist/resolution-Cx82-xjW.js +1244 -0
- package/dist/resource-CLjB7RBQ.js +377 -0
- package/dist/resources/index.d.ts +21 -11
- package/dist/resources/index.js +45 -244
- package/dist/router/index.d.ts +14 -5
- package/dist/router/index.js +22 -53
- package/dist/scope-CwpVSHVf.js +55 -0
- package/dist/{shared-kU8yvryp.js → shared-O2ZnmYAf.js} +1 -7
- package/dist/{snapshot-source-An4iKajG.js → snapshot-Cjx4N988.js} +6 -1
- package/dist/ssg/index.d.ts +1 -2
- package/dist/ssg/index.js +5 -13
- package/dist/ssr/index.d.ts +26 -28
- package/dist/ssr/index.js +4 -4
- package/dist/{ssr-CiNm6Vzl.js → ssr-BJ8ug7zH.js} +133 -122
- package/dist/ssr-DoV-vJ-5.js +229 -0
- package/dist/state-DWO1KzkX.js +232 -0
- package/dist/structures-D4dDMqHX.js +231 -0
- package/dist/testing/index.d.ts +8 -4
- package/dist/testing/index.js +29 -34
- package/dist/url-CnbfpX0K.js +185 -0
- package/dist/utilities.d.ts +48 -2
- package/dist/verify-hydration-DpVaDnqz.js +152 -0
- package/package.json +19 -9
- package/dist/access-CEA2pOry.js +0 -1046
- package/dist/activity-oy2YFTFF.js +0 -201
- package/dist/boot-BMBMWUGI.js +0 -1084
- package/dist/capabilities-Bbf0mlp9.js +0 -42
- package/dist/compose-ref-v0qGlHoN.js +0 -26
- package/dist/control-BR_mrX__.js +0 -173
- package/dist/control-C-ApnFs1.js +0 -20
- package/dist/csp-nonce-BfFWcd0w.js +0 -173
- package/dist/data-runtime-CxoY98lX.js +0 -149
- package/dist/default-portal-runtime-DvMvWJVp.js +0 -20
- package/dist/dom-renderer.d.ts +0 -77
- package/dist/domains/renderer.d.ts +0 -127
- package/dist/eager-control.d.ts +0 -8
- package/dist/effect-C3Zsc1k6.js +0 -292
- package/dist/for-state-BDykeOJL.js +0 -1753
- package/dist/instance-VAFYVJOa.js +0 -1027
- package/dist/notify-DxxuDPeY.js +0 -266
- package/dist/portal-w3oQ6oQU.js +0 -29
- package/dist/query-registry-LrYpQsDJ.js +0 -83
- package/dist/render-resolved-BC0W7_5b.js +0 -1123
- package/dist/renderer-CT0HeY58.js +0 -8623
- package/dist/resolution-3DskUkjG.js +0 -577
- package/dist/resource-18-sFRl9.js +0 -344
- package/dist/route-matching-CUzZ31gT.js +0 -551
- package/dist/router-internal-7nuNEHfG.js +0 -36
- package/dist/scope-Cq_iYbTR.js +0 -403
- package/dist/selector-BSPVkjKP.js +0 -311
- package/dist/ssr-EPjwJH5R.js +0 -29
- package/dist/state-BXJfudq1.js +0 -91
- package/dist/structures-CjEzfj4h.js +0 -654
- package/dist/timer-C7uqnJVt.js +0 -85
- package/dist/verify-hydration-ikaxotf7.js +0 -27
- package/dist/vnode-D24p6IW2.js +0 -7
package/dist/testing/index.d.ts
CHANGED
|
@@ -77,9 +77,9 @@ declare function mount(
|
|
|
77
77
|
declare function renderRoute(
|
|
78
78
|
options: RouteRenderOptions
|
|
79
79
|
): Promise<RenderResult>;
|
|
80
|
-
/** Dispatch
|
|
80
|
+
/** Dispatch one bubbling `click` MouseEvent (no pointerdown/mousedown/mouseup sequence). */
|
|
81
81
|
declare function click(element: Element): boolean;
|
|
82
|
-
/**
|
|
82
|
+
/** Append `text` to a text control's value one character at a time, emitting an input event for each. */
|
|
83
83
|
declare function type(
|
|
84
84
|
element: HTMLInputElement | HTMLTextAreaElement,
|
|
85
85
|
text: string
|
|
@@ -94,7 +94,9 @@ interface QueryTestRegistry {
|
|
|
94
94
|
clear(): void;
|
|
95
95
|
}
|
|
96
96
|
/** Create a keyed query fixture registry for a test render runtime. */
|
|
97
|
-
declare function createQueryTestRegistry(
|
|
97
|
+
declare function createQueryTestRegistry(
|
|
98
|
+
runtime?: DataRuntime
|
|
99
|
+
): QueryTestRegistry;
|
|
98
100
|
/** Keyed mutation fixture registry returned by {@link createMutationTestRegistry}. */
|
|
99
101
|
interface MutationTestRegistry {
|
|
100
102
|
readonly runtime: DataRuntime;
|
|
@@ -103,7 +105,9 @@ interface MutationTestRegistry {
|
|
|
103
105
|
clear(): void;
|
|
104
106
|
}
|
|
105
107
|
/** Create a keyed mutation fixture registry for a test render runtime. */
|
|
106
|
-
declare function createMutationTestRegistry(
|
|
108
|
+
declare function createMutationTestRegistry(
|
|
109
|
+
runtime?: DataRuntime
|
|
110
|
+
): MutationTestRegistry;
|
|
107
111
|
/** Initial state for {@link mutationState}; exactly one of `pending`/`error`/`result` may be set. */
|
|
108
112
|
type MutationFixtureInitial<TResult> = {
|
|
109
113
|
pending?: boolean;
|
package/dist/testing/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { a as
|
|
5
|
-
import {
|
|
6
|
-
import { a as
|
|
7
|
-
import {
|
|
1
|
+
import { y as flushSync } from "../instance-BAnfmXOR.js";
|
|
2
|
+
import { n as createSource, s as notify, u as readSource } from "../hooks-BimKStr4.js";
|
|
3
|
+
import { v as matchRoute } from "../resolution-Cx82-xjW.js";
|
|
4
|
+
import { a as cleanupApp, n as createSPA, r as createIsland } from "../boot-DaoTYoCw.js";
|
|
5
|
+
import { h as addInvalidationListener, t as createDataRuntime, u as resolveDataRuntimeState } from "../data-runtime-C1bFAlba.js";
|
|
6
|
+
import { a as loadingQueryState, i as freshQueryState, o as refreshingQueryState, r as errorQueryState, s as staleQueryState } from "../data-BCwClJiF.js";
|
|
7
|
+
import { i as normalizeAsyncDataError } from "../shared-O2ZnmYAf.js";
|
|
8
8
|
//#region src/testing/render.ts
|
|
9
9
|
const renderStates = /* @__PURE__ */ new WeakMap();
|
|
10
10
|
const eventConstructorNames = {
|
|
@@ -99,7 +99,7 @@ function isEventForTarget(target, value) {
|
|
|
99
99
|
}
|
|
100
100
|
/** Synchronously flush the runtime's scheduled work (renders, effects). */
|
|
101
101
|
function flush() {
|
|
102
|
-
|
|
102
|
+
flushSync();
|
|
103
103
|
}
|
|
104
104
|
/** Dispatch an event (constructed from a type string, or given directly) on `target`. */
|
|
105
105
|
function dispatch(target, event, init = {}) {
|
|
@@ -191,12 +191,12 @@ async function renderRoute(options) {
|
|
|
191
191
|
}
|
|
192
192
|
//#endregion
|
|
193
193
|
//#region src/testing/interactions.ts
|
|
194
|
-
/** Dispatch
|
|
194
|
+
/** Dispatch one bubbling `click` MouseEvent (no pointerdown/mousedown/mouseup sequence). */
|
|
195
195
|
function click(element) {
|
|
196
196
|
if (!element || typeof element.dispatchEvent !== "function") throw new TypeError("@askrjs/askr/testing click requires an Element.");
|
|
197
197
|
return dispatch(element, "click");
|
|
198
198
|
}
|
|
199
|
-
/**
|
|
199
|
+
/** Append `text` to a text control's value one character at a time, emitting an input event for each. */
|
|
200
200
|
function type(element, text) {
|
|
201
201
|
if (!element || typeof element.dispatchEvent !== "function") throw new TypeError("@askrjs/askr/testing type requires a text control.");
|
|
202
202
|
for (const character of text) {
|
|
@@ -215,40 +215,40 @@ function submit(form) {
|
|
|
215
215
|
//#endregion
|
|
216
216
|
//#region src/testing/registries.ts
|
|
217
217
|
/** Create a keyed query fixture registry for a test render runtime. */
|
|
218
|
-
function createQueryTestRegistry() {
|
|
219
|
-
const
|
|
218
|
+
function createQueryTestRegistry(runtime = createDataRuntime()) {
|
|
219
|
+
const overrides = resolveDataRuntimeState(runtime).queryTestOverrides;
|
|
220
220
|
return {
|
|
221
221
|
runtime,
|
|
222
222
|
set(key, query) {
|
|
223
223
|
if (typeof key !== "string" || key.length === 0) throw new TypeError("@askrjs/askr/testing query registry keys must be non-empty strings.");
|
|
224
|
-
|
|
224
|
+
overrides.set(key, query);
|
|
225
225
|
},
|
|
226
226
|
delete(key) {
|
|
227
|
-
|
|
227
|
+
overrides.delete(key);
|
|
228
228
|
},
|
|
229
229
|
clear() {
|
|
230
|
-
|
|
230
|
+
overrides.clear();
|
|
231
231
|
}
|
|
232
232
|
};
|
|
233
233
|
}
|
|
234
234
|
/** Create a keyed mutation fixture registry for a test render runtime. */
|
|
235
|
-
function createMutationTestRegistry() {
|
|
236
|
-
const
|
|
235
|
+
function createMutationTestRegistry(runtime = createDataRuntime()) {
|
|
236
|
+
const overrides = resolveDataRuntimeState(runtime).mutationTestOverrides;
|
|
237
237
|
return {
|
|
238
238
|
runtime,
|
|
239
239
|
set(key, mutation) {
|
|
240
240
|
if (typeof key !== "string" || key.length === 0) throw new TypeError("@askrjs/askr/testing mutation registry keys must be non-empty strings.");
|
|
241
|
-
const previous =
|
|
241
|
+
const previous = overrides.get(key);
|
|
242
242
|
if (previous && previous !== mutation) previous.reset();
|
|
243
|
-
|
|
243
|
+
overrides.set(key, mutation);
|
|
244
244
|
},
|
|
245
245
|
delete(key) {
|
|
246
|
-
|
|
247
|
-
|
|
246
|
+
overrides.get(key)?.reset();
|
|
247
|
+
overrides.delete(key);
|
|
248
248
|
},
|
|
249
249
|
clear() {
|
|
250
|
-
for (const mutation of
|
|
251
|
-
|
|
250
|
+
for (const mutation of overrides.values()) mutation.reset();
|
|
251
|
+
overrides.clear();
|
|
252
252
|
}
|
|
253
253
|
};
|
|
254
254
|
}
|
|
@@ -256,7 +256,7 @@ function createMutationTestRegistry() {
|
|
|
256
256
|
//#region src/testing/mutation-fixtures.ts
|
|
257
257
|
var MutableMutationFixture = class {
|
|
258
258
|
constructor(initial = {}) {
|
|
259
|
-
this.source =
|
|
259
|
+
this.source = createSource();
|
|
260
260
|
this.receivedInputs = [];
|
|
261
261
|
this.active = null;
|
|
262
262
|
const hasResult = Object.prototype.hasOwnProperty.call(initial, "result");
|
|
@@ -281,19 +281,19 @@ var MutableMutationFixture = class {
|
|
|
281
281
|
};
|
|
282
282
|
}
|
|
283
283
|
get status() {
|
|
284
|
-
|
|
284
|
+
readSource(this.source);
|
|
285
285
|
return this.state.status;
|
|
286
286
|
}
|
|
287
287
|
get pending() {
|
|
288
|
-
|
|
288
|
+
readSource(this.source);
|
|
289
289
|
return this.state.status === "pending";
|
|
290
290
|
}
|
|
291
291
|
get error() {
|
|
292
|
-
|
|
292
|
+
readSource(this.source);
|
|
293
293
|
return this.state.error;
|
|
294
294
|
}
|
|
295
295
|
get result() {
|
|
296
|
-
|
|
296
|
+
readSource(this.source);
|
|
297
297
|
return this.state.result;
|
|
298
298
|
}
|
|
299
299
|
get inputs() {
|
|
@@ -357,7 +357,7 @@ var MutableMutationFixture = class {
|
|
|
357
357
|
}
|
|
358
358
|
setState(state) {
|
|
359
359
|
this.state = state;
|
|
360
|
-
|
|
360
|
+
notify(this.source);
|
|
361
361
|
}
|
|
362
362
|
};
|
|
363
363
|
function createMutationFixture(initial = {}) {
|
|
@@ -458,11 +458,6 @@ function createInvalidationRecorder() {
|
|
|
458
458
|
}
|
|
459
459
|
//#endregion
|
|
460
460
|
//#region src/testing/routes.ts
|
|
461
|
-
/** Match `path` against a route registry for tests, without mounting the app. */
|
|
462
|
-
function matchRoute(path, options) {
|
|
463
|
-
if (!options?.registry) throw new TypeError("matchRoute requires options.registry.");
|
|
464
|
-
return computeRouteActivityMatches(path, { registry: options.registry })[0] ?? null;
|
|
465
|
-
}
|
|
466
461
|
function getRoutePatternRecords(options) {
|
|
467
462
|
if (!options?.registry) throw new TypeError("getRouteWarnings requires options.registry.");
|
|
468
463
|
return options.registry.manifest.records.map((record) => ({
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { F as logger, I as isDevelopmentEnvironment } from "./instance-BAnfmXOR.js";
|
|
2
|
+
//#region src/common/attr-names.ts
|
|
3
|
+
/**
|
|
4
|
+
* JSX prop name to rendered attribute name, shared by the DOM renderer and SSR.
|
|
5
|
+
*
|
|
6
|
+
* The DOM renderer additionally lowercases the result for non-SVG elements,
|
|
7
|
+
* using the element's real namespace. SSR renders to text and has no element to
|
|
8
|
+
* ask, so every camelCase prop that does not name a real camelCase attribute is
|
|
9
|
+
* mapped explicitly here — otherwise SSR emits `tabIndex` where the DOM renderer
|
|
10
|
+
* writes `tabindex`, or `strokeDasharray`, which browsers ignore. Every mapped
|
|
11
|
+
* name is lowercase in both HTML and SVG, so the mapping is
|
|
12
|
+
* namespace-independent. SVG attributes that really are camelCase (`viewBox`,
|
|
13
|
+
* `gradientUnits`, `preserveAspectRatio`, ...) are not listed and pass through.
|
|
14
|
+
*
|
|
15
|
+
* Custom elements (a tag name containing `-`) define their own attributes, so
|
|
16
|
+
* they only get this base table and never the SVG table below.
|
|
17
|
+
*/
|
|
18
|
+
const PUBLIC_ATTRIBUTE_NAME_MAP = {
|
|
19
|
+
className: "class",
|
|
20
|
+
htmlFor: "for",
|
|
21
|
+
autoComplete: "autocomplete",
|
|
22
|
+
colSpan: "colspan",
|
|
23
|
+
contentEditable: "contenteditable",
|
|
24
|
+
enterKeyHint: "enterkeyhint",
|
|
25
|
+
inputMode: "inputmode",
|
|
26
|
+
maxLength: "maxlength",
|
|
27
|
+
minLength: "minlength",
|
|
28
|
+
noValidate: "novalidate",
|
|
29
|
+
readOnly: "readonly",
|
|
30
|
+
rowSpan: "rowspan",
|
|
31
|
+
spellCheck: "spellcheck",
|
|
32
|
+
tabIndex: "tabindex",
|
|
33
|
+
writingSuggestions: "writingsuggestions",
|
|
34
|
+
strokeWidth: "stroke-width",
|
|
35
|
+
strokeLinecap: "stroke-linecap",
|
|
36
|
+
strokeLinejoin: "stroke-linejoin",
|
|
37
|
+
fillRule: "fill-rule",
|
|
38
|
+
clipRule: "clip-rule"
|
|
39
|
+
};
|
|
40
|
+
const SVG_ATTRIBUTE_NAME_MAP = {};
|
|
41
|
+
/**
|
|
42
|
+
* SVG presentation attributes are hyphenated, and the XML-namespaced ones use
|
|
43
|
+
* a prefix; JSX spells both in camelCase (`strokeDasharray`, `xlinkHref`).
|
|
44
|
+
* Listing the rendered names and deriving the prop names keeps this table
|
|
45
|
+
* small in the client bundle.
|
|
46
|
+
*/
|
|
47
|
+
for (const attributeName of "alignment-baseline baseline-shift clip-path clip-rule color-interpolation color-interpolation-filters color-rendering dominant-baseline fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight image-rendering letter-spacing lighting-color marker-end marker-mid marker-start mask-type paint-order pointer-events shape-rendering stop-color stop-opacity stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-overflow text-rendering transform-origin unicode-bidi vector-effect white-space word-spacing writing-mode xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xml:lang xml:space xmlns:xlink".split(" ")) SVG_ATTRIBUTE_NAME_MAP[attributeName.replace(/[-:]([a-z])/g, (_, char) => char.toUpperCase())] = attributeName;
|
|
48
|
+
function isCustomElementName(tagName) {
|
|
49
|
+
return tagName.includes("-");
|
|
50
|
+
}
|
|
51
|
+
function getPublicAttributeName(propName, customElement = false) {
|
|
52
|
+
if (propName.startsWith("attr:")) return propName.slice(5);
|
|
53
|
+
return PUBLIC_ATTRIBUTE_NAME_MAP[propName] ?? (customElement ? void 0 : SVG_ATTRIBUTE_NAME_MAP[propName]) ?? propName;
|
|
54
|
+
}
|
|
55
|
+
const ATTRIBUTE_PREFIX_NAMESPACES = {
|
|
56
|
+
xlink: "http://www.w3.org/1999/xlink",
|
|
57
|
+
xml: "http://www.w3.org/XML/1998/namespace",
|
|
58
|
+
xmlns: "http://www.w3.org/2000/xmlns/"
|
|
59
|
+
};
|
|
60
|
+
const SVG_NAMESPACE = "http://www.w3.org/2000/svg";
|
|
61
|
+
const MATHML_NAMESPACE = "http://www.w3.org/1998/Math/MathML";
|
|
62
|
+
/**
|
|
63
|
+
* The XML namespace of a prefixed attribute name such as `xlink:href` on an
|
|
64
|
+
* element in `elementNamespace`, or `null` for ordinary attributes. The HTML
|
|
65
|
+
* parser puts these attributes in their namespaces only on SVG and MathML
|
|
66
|
+
* elements when it reads SSR markup; on HTML elements `xml:lang` stays a plain
|
|
67
|
+
* attribute. The DOM renderer must do the same so both sides produce the same
|
|
68
|
+
* element.
|
|
69
|
+
*/
|
|
70
|
+
function attributeNamespace(elementNamespace, attributeName) {
|
|
71
|
+
if (elementNamespace !== SVG_NAMESPACE && elementNamespace !== MATHML_NAMESPACE) return null;
|
|
72
|
+
const colon = attributeName.indexOf(":");
|
|
73
|
+
if (colon === -1) return null;
|
|
74
|
+
return ATTRIBUTE_PREFIX_NAMESPACES[attributeName.slice(0, colon)] ?? null;
|
|
75
|
+
}
|
|
76
|
+
//#endregion
|
|
77
|
+
//#region src/common/url.ts
|
|
78
|
+
const SAFE_URL_SCHEMES = /* @__PURE__ */ new Set([
|
|
79
|
+
"http",
|
|
80
|
+
"https",
|
|
81
|
+
"mailto",
|
|
82
|
+
"sms",
|
|
83
|
+
"tel"
|
|
84
|
+
]);
|
|
85
|
+
const SCRIPT_URL_SCHEMES = /* @__PURE__ */ new Set(["javascript", "vbscript"]);
|
|
86
|
+
const URL_SCHEME_RE = /^([a-z][a-z0-9+.-]*):/i;
|
|
87
|
+
function urlScheme(value) {
|
|
88
|
+
const compact = value.trim().replace(/[\u0000-\u0020\u007f-\u009f]/g, "");
|
|
89
|
+
return URL_SCHEME_RE.exec(compact)?.[1]?.toLowerCase();
|
|
90
|
+
}
|
|
91
|
+
const EXPLICIT_HTTP_URL_RE = /^[\u0000-\u0020]*https?:/i;
|
|
92
|
+
const NAVIGATION_BASE = "http://askr.invalid/";
|
|
93
|
+
/**
|
|
94
|
+
* Resolve a navigation or redirect target against `base`. Only a target
|
|
95
|
+
* written with an explicit `http:`/`https:` scheme may leave the base origin.
|
|
96
|
+
* A path-like string that URL parsing resolves to another host
|
|
97
|
+
* (`//evil.example`, `/\\evil.example`, `\\\\evil.example`) throws a
|
|
98
|
+
* `TypeError`, so an app path can never become an open redirect. So does a
|
|
99
|
+
* same-origin URL whose pathname starts with `//` (`/.//evil.example`,
|
|
100
|
+
* `/%2e//evil.example`): its root-relative form `//evil.example` would name
|
|
101
|
+
* another host. No window is needed: the check holds for any base origin.
|
|
102
|
+
*/
|
|
103
|
+
function resolveNavigationUrl(target, base = NAVIGATION_BASE) {
|
|
104
|
+
const url = new URL(target, base);
|
|
105
|
+
const sameOrigin = url.origin === new URL(base).origin;
|
|
106
|
+
if (!sameOrigin && !EXPLICIT_HTTP_URL_RE.test(target) || sameOrigin && url.pathname.startsWith("//")) throw new TypeError(`Navigation target ${JSON.stringify(target)} resolves to another origin without an explicit http: or https: scheme.`);
|
|
107
|
+
return url;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* The public form of a navigation target: root-relative for the base origin,
|
|
111
|
+
* absolute for an explicit URL on another origin.
|
|
112
|
+
*/
|
|
113
|
+
function formatNavigationUrl(url, base = NAVIGATION_BASE) {
|
|
114
|
+
return url.origin === new URL(base).origin ? `${url.pathname}${url.search}${url.hash}` : url.href;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Reject a scheme-less href (`/\\evil.example`, `//evil.example`) that the
|
|
118
|
+
* browser would follow to another origin. Hrefs with a scheme are checked by
|
|
119
|
+
* {@link isSafeHref}.
|
|
120
|
+
*/
|
|
121
|
+
function assertPathHrefStaysOnOrigin(value) {
|
|
122
|
+
const scheme = urlScheme(value);
|
|
123
|
+
if (scheme === void 0 || scheme === "http" || scheme === "https") resolveNavigationUrl(value);
|
|
124
|
+
}
|
|
125
|
+
function isSafeHref(value) {
|
|
126
|
+
const scheme = urlScheme(value);
|
|
127
|
+
return scheme === void 0 || SAFE_URL_SCHEMES.has(scheme);
|
|
128
|
+
}
|
|
129
|
+
/** Whether a resource URL avoids script-executing schemes; data:, blob: and custom schemes stay allowed. */
|
|
130
|
+
function isSafeResourceUrl(value) {
|
|
131
|
+
const scheme = urlScheme(value);
|
|
132
|
+
return scheme === void 0 || !SCRIPT_URL_SCHEMES.has(scheme);
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Attribute names (lowercased) that can carry a browser-navigable/executable
|
|
136
|
+
* URL and must be checked against isSafeHref. Shared between the client
|
|
137
|
+
* renderer (renderer/attributes.ts) and SSR (ssr/attrs.ts) so their unsafe-
|
|
138
|
+
* URL handling can't drift.
|
|
139
|
+
*/
|
|
140
|
+
const UNSAFE_URL_SCHEME_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
141
|
+
"href",
|
|
142
|
+
"formaction",
|
|
143
|
+
"action",
|
|
144
|
+
"xlink:href"
|
|
145
|
+
]);
|
|
146
|
+
/**
|
|
147
|
+
* Resource attributes (lowercased) that execute script-scheme URLs, such as
|
|
148
|
+
* `<iframe src>` and `<object data>`. They legitimately carry data:, blob: and
|
|
149
|
+
* other non-navigable URLs, so only script schemes are rejected, not the
|
|
150
|
+
* navigation allowlist.
|
|
151
|
+
*/
|
|
152
|
+
const SCRIPT_URL_RESOURCE_ATTRIBUTES = /* @__PURE__ */ new Set(["src", "data"]);
|
|
153
|
+
/** Accepts the JSX prop name (`xlinkHref`) or the rendered attribute name. */
|
|
154
|
+
function isUnsafeUrlAttribute(key, value) {
|
|
155
|
+
const name = getPublicAttributeName(key).toLowerCase();
|
|
156
|
+
if (UNSAFE_URL_SCHEME_ATTRIBUTES.has(name)) return !isSafeHref(String(value));
|
|
157
|
+
if (SCRIPT_URL_RESOURCE_ATTRIBUTES.has(name)) return !isSafeResourceUrl(String(value));
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
const WARNED_URL_PREVIEW_LENGTH = 80;
|
|
161
|
+
const WARNED_URL_LIMIT = 256;
|
|
162
|
+
const warnedUrls = /* @__PURE__ */ new Set();
|
|
163
|
+
const MISLEADING_PREVIEW_CHARS_RE = /[\u202A-\u202E\u2066-\u2069\u2028\u2029]/g;
|
|
164
|
+
function warnUnsafeUrl(name, text) {
|
|
165
|
+
const key = `${name}=${text}`;
|
|
166
|
+
if (warnedUrls.has(key)) return;
|
|
167
|
+
if (warnedUrls.size >= WARNED_URL_LIMIT) warnedUrls.clear();
|
|
168
|
+
warnedUrls.add(key);
|
|
169
|
+
const preview = (text.length > WARNED_URL_PREVIEW_LENGTH ? `${text.slice(0, WARNED_URL_PREVIEW_LENGTH)}...` : text).replace(MISLEADING_PREVIEW_CHARS_RE, "?");
|
|
170
|
+
const allowed = SCRIPT_URL_RESOURCE_ATTRIBUTES.has(name) ? "script-executing schemes are never rendered" : `only relative URLs and ${[...SAFE_URL_SCHEMES].join(", ")} are allowed`;
|
|
171
|
+
logger.warn(`[Askr] Omitted ${name}=${JSON.stringify(preview)}: the "${urlScheme(text)}:" scheme is blocked (${allowed}).`);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* {@link isUnsafeUrlAttribute} for a value about to be rendered: in
|
|
175
|
+
* development, a blocked value logs a warning naming the attribute and scheme,
|
|
176
|
+
* once per attribute and value, so a stripped custom-scheme link (`vscode:`,
|
|
177
|
+
* `slack:`) is not silent.
|
|
178
|
+
*/
|
|
179
|
+
function rejectUnsafeUrlAttribute(key, value) {
|
|
180
|
+
if (!isUnsafeUrlAttribute(key, value)) return false;
|
|
181
|
+
if (isDevelopmentEnvironment()) warnUnsafeUrl(getPublicAttributeName(key).toLowerCase(), String(value));
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
//#endregion
|
|
185
|
+
export { isSafeHref as a, resolveNavigationUrl as c, isCustomElementName as d, formatNavigationUrl as i, attributeNamespace as l, UNSAFE_URL_SCHEME_ATTRIBUTES as n, isUnsafeUrlAttribute as o, assertPathHrefStaysOnOrigin as r, rejectUnsafeUrlAttribute as s, SCRIPT_URL_RESOURCE_ATTRIBUTES as t, getPublicAttributeName as u };
|
package/dist/utilities.d.ts
CHANGED
|
@@ -63,14 +63,59 @@ declare function composeHandlers<A extends readonly unknown[]>(
|
|
|
63
63
|
second?: (...args: A) => void,
|
|
64
64
|
options?: ComposeHandlersOptions
|
|
65
65
|
): (...args: A) => void;
|
|
66
|
+
type IsAny<T> = 0 extends 1 & T ? true : false;
|
|
67
|
+
type HasIndexSignature<T> = string extends keyof T
|
|
68
|
+
? true
|
|
69
|
+
: number extends keyof T
|
|
70
|
+
? true
|
|
71
|
+
: false;
|
|
72
|
+
type RequiredKeys<T> = {
|
|
73
|
+
[K in keyof T]-?: {} extends Pick<T, K> ? never : K;
|
|
74
|
+
}[keyof T];
|
|
75
|
+
|
|
76
|
+
/** A base value, or the injected one where the base may be `undefined`. */
|
|
77
|
+
type MergedValue<TBase, TInjected> =
|
|
78
|
+
IsAny<TBase> extends true
|
|
79
|
+
? TBase
|
|
80
|
+
: [TBase] extends [undefined]
|
|
81
|
+
? TInjected
|
|
82
|
+
: undefined extends TBase
|
|
83
|
+
? Exclude<TBase, undefined> | TInjected
|
|
84
|
+
: TBase;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* The props {@link mergeProps} returns: base keys win unless `undefined`, and
|
|
88
|
+
* a base key is required when the injected props always supply it. Props
|
|
89
|
+
* with an index signature fall back to the intersection of both sides.
|
|
90
|
+
*/
|
|
91
|
+
type MergedProps<TBase extends object, TInjected extends object> =
|
|
92
|
+
HasIndexSignature<TBase> extends true
|
|
93
|
+
? TInjected & TBase
|
|
94
|
+
: HasIndexSignature<TInjected> extends true
|
|
95
|
+
? TInjected & TBase
|
|
96
|
+
: Omit<TInjected, keyof TBase> & {
|
|
97
|
+
[
|
|
98
|
+
K in keyof TBase as K extends RequiredKeys<TInjected> ? K : never
|
|
99
|
+
]-?: MergedValue<TBase[K], TInjected[K & keyof TInjected]>;
|
|
100
|
+
} & {
|
|
101
|
+
[
|
|
102
|
+
K in keyof TBase as K extends RequiredKeys<TInjected> ? never : K
|
|
103
|
+
]: MergedValue<
|
|
104
|
+
TBase[K],
|
|
105
|
+
K extends keyof TInjected ? TInjected[K] : undefined
|
|
106
|
+
>;
|
|
107
|
+
};
|
|
108
|
+
|
|
66
109
|
/**
|
|
67
110
|
* Merge `base` props over `injected` props: non-handler keys in `base` win,
|
|
68
|
-
* and matching event handlers are composed (`injected` runs first).
|
|
111
|
+
* and matching event handlers are composed (`injected` runs first). `base`
|
|
112
|
+
* values that are `undefined` are treated as absent and never overwrite an
|
|
113
|
+
* injected value; use `null` to clear one explicitly.
|
|
69
114
|
*/
|
|
70
115
|
declare function mergeProps<TBase extends object, TInjected extends object>(
|
|
71
116
|
base: TBase,
|
|
72
117
|
injected: TInjected
|
|
73
|
-
): TInjected
|
|
118
|
+
): MergedProps<TBase, TInjected>;
|
|
74
119
|
/**
|
|
75
120
|
* Tiny aria helpers
|
|
76
121
|
*/
|
|
@@ -124,6 +169,7 @@ export {
|
|
|
124
169
|
DefaultPreventable,
|
|
125
170
|
formatId,
|
|
126
171
|
mergeProps,
|
|
172
|
+
MergedProps,
|
|
127
173
|
PropagationStoppable,
|
|
128
174
|
ariaDisabled,
|
|
129
175
|
ComposeHandlersOptions,
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { r as validateCspNonce } from "./csp-nonce-DwGw1A6Q.js";
|
|
2
|
+
import { g as withHydrationVerificationRender, v as getHydrationRenderUrl } from "./render-context-DGHnEveC.js";
|
|
3
|
+
import { C as removeRouteBasePath, S as normalizeRouteBasePath, f as currentAuth, n as getRouteRenderContext, y as resolveRouteFromRoutes } from "./resolution-Cx82-xjW.js";
|
|
4
|
+
import { t as SSR_RENDER_DATA_ATTR } from "./ssr-DoV-vJ-5.js";
|
|
5
|
+
import { i as StringSink, s as createRenderContext, t as renderSSRRouteAppToSink } from "./render-sync-IC5lMFpq.js";
|
|
6
|
+
//#region src/ssr/render-resolved.ts
|
|
7
|
+
function sameRouteParams(left, right) {
|
|
8
|
+
if (!left || !right) return !left && !right;
|
|
9
|
+
const leftKeys = Object.keys(left);
|
|
10
|
+
const rightKeys = Object.keys(right);
|
|
11
|
+
if (leftKeys.length !== rightKeys.length) return false;
|
|
12
|
+
for (const key of leftKeys) if (left[key] !== right[key]) return false;
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
/** @internal Render a resolved route handler, with the request-local auth context used by hydration verification. */
|
|
16
|
+
function renderResolvedForHydrationSync(opts, authContext) {
|
|
17
|
+
const { url, registry, handler, params, options } = opts;
|
|
18
|
+
const routes = registry.routes;
|
|
19
|
+
const requestUrl = new URL(url, "http://localhost");
|
|
20
|
+
const logicalTarget = removeRouteBasePath(`${requestUrl.pathname}${requestUrl.search}${requestUrl.hash}`, normalizeRouteBasePath(registry.manifest.basePath));
|
|
21
|
+
const matchedIndex = routes.findIndex((route) => {
|
|
22
|
+
if (logicalTarget === void 0) return false;
|
|
23
|
+
const resolved = resolveRouteFromRoutes(new URL(logicalTarget, "http://localhost").pathname, [route]);
|
|
24
|
+
return resolved !== null && resolved.handler === route.handler && sameRouteParams(resolved.params, params ?? {});
|
|
25
|
+
});
|
|
26
|
+
if (matchedIndex < 0) throw new Error(`renderResolvedForHydrationSync: no route found for url: ${url}`);
|
|
27
|
+
const effectiveRoutes = routes.map((route, index) => index === matchedIndex ? {
|
|
28
|
+
...route,
|
|
29
|
+
handler
|
|
30
|
+
} : route);
|
|
31
|
+
const matchedRoute = routes[matchedIndex];
|
|
32
|
+
const cspNonce = validateCspNonce(options?.cspNonce);
|
|
33
|
+
const ctx = createRenderContext(options?.seed, {
|
|
34
|
+
url,
|
|
35
|
+
basePath: registry.manifest.basePath,
|
|
36
|
+
data: options?.data,
|
|
37
|
+
params,
|
|
38
|
+
routes: effectiveRoutes,
|
|
39
|
+
routeAuth: registry.manifest.auth,
|
|
40
|
+
authContext,
|
|
41
|
+
dataRuntime: options?.dataRuntime,
|
|
42
|
+
envelope: options?.envelope,
|
|
43
|
+
cspNonce
|
|
44
|
+
});
|
|
45
|
+
const sink = new StringSink();
|
|
46
|
+
renderSSRRouteAppToSink({
|
|
47
|
+
route: {
|
|
48
|
+
...matchedRoute,
|
|
49
|
+
handler
|
|
50
|
+
},
|
|
51
|
+
params: params ?? {},
|
|
52
|
+
data: options?.data,
|
|
53
|
+
ctx,
|
|
54
|
+
sink
|
|
55
|
+
});
|
|
56
|
+
sink.end();
|
|
57
|
+
return sink.toString();
|
|
58
|
+
}
|
|
59
|
+
//#endregion
|
|
60
|
+
//#region src/ssr/verify-hydration.ts
|
|
61
|
+
const SSR_STYLE_REGISTRY_SELECTOR = "style[data-askr-style-registry]";
|
|
62
|
+
const RENDERER_BOOKKEEPING_ATTRS = [
|
|
63
|
+
"data-skip-hydrate",
|
|
64
|
+
"data-key",
|
|
65
|
+
"data-askr-key-kind"
|
|
66
|
+
];
|
|
67
|
+
/**
|
|
68
|
+
* Parsed declarations in a canonical order. Each parsed longhand is unique, so
|
|
69
|
+
* order carries no meaning here, but the DOM renderer may reorder properties
|
|
70
|
+
* it rewrites.
|
|
71
|
+
*/
|
|
72
|
+
function normalizeStyleDeclarations(style) {
|
|
73
|
+
const declarations = [];
|
|
74
|
+
for (let index = 0; index < style.length; index += 1) {
|
|
75
|
+
const name = style.item(index);
|
|
76
|
+
const priority = style.getPropertyPriority(name);
|
|
77
|
+
declarations.push(`${name}: ${style.getPropertyValue(name)}${priority ? ` !${priority}` : ""};`);
|
|
78
|
+
}
|
|
79
|
+
return declarations.sort().join(" ");
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Serialize markup in a form that is stable across the SSR serializer and the
|
|
83
|
+
* DOM renderer: transport carriers, comments (range anchors) and the
|
|
84
|
+
* key and `data-skip-hydrate` bookkeeping the renderers add are dropped, and style
|
|
85
|
+
* attributes are re-serialized through the CSSOM so `color:red;` and the
|
|
86
|
+
* DOM's `color: red;` compare equal.
|
|
87
|
+
*/
|
|
88
|
+
function normalizeHydrationHtml(html) {
|
|
89
|
+
const template = document.createElement("template");
|
|
90
|
+
template.innerHTML = html;
|
|
91
|
+
for (const carrier of Array.from(template.content.querySelectorAll(`script[${SSR_RENDER_DATA_ATTR}],${SSR_STYLE_REGISTRY_SELECTOR}`))) carrier.remove();
|
|
92
|
+
for (const name of RENDERER_BOOKKEEPING_ATTRS) for (const element of Array.from(template.content.querySelectorAll(`[${name}]`))) element.removeAttribute(name);
|
|
93
|
+
for (const element of Array.from(template.content.querySelectorAll("[style]"))) {
|
|
94
|
+
const style = element.style;
|
|
95
|
+
if (!style) continue;
|
|
96
|
+
const cssText = normalizeStyleDeclarations(style);
|
|
97
|
+
if (cssText) element.setAttribute("style", cssText);
|
|
98
|
+
else element.removeAttribute("style");
|
|
99
|
+
}
|
|
100
|
+
const comments = document.createTreeWalker(template.content, NodeFilter.SHOW_COMMENT);
|
|
101
|
+
const found = [];
|
|
102
|
+
while (comments.nextNode()) found.push(comments.currentNode);
|
|
103
|
+
for (const comment of found) comment.parentNode?.removeChild(comment);
|
|
104
|
+
return template.innerHTML;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Whether the client renders the same URL the server markup was rendered
|
|
108
|
+
* for. A static page opened with a client-only query or hash is verified
|
|
109
|
+
* against its queryless render URL, then hydration applies the browser URL on
|
|
110
|
+
* purpose, so its client output legitimately differs.
|
|
111
|
+
*/
|
|
112
|
+
function clientRendersServerUrl(url, envelope) {
|
|
113
|
+
const live = new URL(url, "http://localhost");
|
|
114
|
+
const renderUrl = getHydrationRenderUrl(envelope);
|
|
115
|
+
const rendered = new URL(renderUrl ?? live.pathname, "http://localhost");
|
|
116
|
+
return rendered.search === live.search && rendered.hash === live.hash;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Capture the server markup under `root` before the client renderer touches
|
|
120
|
+
* it, normalized for {@link verifyClientHydrationMarkup}. Returns `null` when
|
|
121
|
+
* the client output is not expected to reproduce it because the page is
|
|
122
|
+
* hydrated at a different URL than it was rendered for.
|
|
123
|
+
*/
|
|
124
|
+
function captureServerHydrationMarkup(root, url, envelope) {
|
|
125
|
+
const html = root.innerHTML;
|
|
126
|
+
if (!clientRendersServerUrl(url, envelope)) return null;
|
|
127
|
+
return normalizeHydrationHtml(html);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Whether client markup left by hydration (`clientHtml`) matches the server
|
|
131
|
+
* markup captured before it ran. Hydration reconciles the server DOM in place,
|
|
132
|
+
* so an SSR/client renderer divergence shows up here even when a fresh server
|
|
133
|
+
* render reproduces the server HTML exactly.
|
|
134
|
+
*/
|
|
135
|
+
function verifyClientHydrationMarkup(clientHtml, serverMarkup) {
|
|
136
|
+
return normalizeHydrationHtml(clientHtml) === serverMarkup;
|
|
137
|
+
}
|
|
138
|
+
function verifyHydrationSyncForUrl(opts) {
|
|
139
|
+
const { root, url, registry, resolved, options } = opts;
|
|
140
|
+
const verificationUrl = getHydrationRenderUrl(options?.envelope) ?? new URL(url, "http://localhost").pathname;
|
|
141
|
+
const authContext = getRouteRenderContext(resolved)?.auth ?? currentAuth();
|
|
142
|
+
const expected = withHydrationVerificationRender(() => renderResolvedForHydrationSync({
|
|
143
|
+
url: verificationUrl,
|
|
144
|
+
registry,
|
|
145
|
+
handler: resolved.handler,
|
|
146
|
+
params: resolved.params,
|
|
147
|
+
options
|
|
148
|
+
}, authContext));
|
|
149
|
+
return normalizeHydrationHtml(root.innerHTML) === normalizeHydrationHtml(expected);
|
|
150
|
+
}
|
|
151
|
+
//#endregion
|
|
152
|
+
export { captureServerHydrationMarkup, verifyClientHydrationMarkup, verifyHydrationSyncForUrl };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@askrjs/askr",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "A TypeScript UI runtime with getter-based state, fine-grained bindings, transactional commits with rollback, and one route graph for SPA, SSR, and SSG.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"askr",
|
|
7
7
|
"framework",
|
|
@@ -128,7 +128,7 @@
|
|
|
128
128
|
"dev": "vp dev",
|
|
129
129
|
"fmt": "vp fmt .",
|
|
130
130
|
"lint": "vp lint src tests test-utils benches && npm run typecheck",
|
|
131
|
-
"typecheck": "tsc --noEmit --pretty false",
|
|
131
|
+
"typecheck": "tsc --noEmit --pretty false && tsc --noEmit --pretty false -p test-utils && tsc --noEmit --pretty false -p benches && tsc --noEmit --pretty false -p tests",
|
|
132
132
|
"test": "npm run test:unit && npm run test:checks && npm run test:jsdom && npm run test:browser",
|
|
133
133
|
"test:browser": "vp test run -c vitest.test.browser.config.ts tests/browser",
|
|
134
134
|
"test:checks": "vp test run -c vitest.test.checks.config.ts",
|
|
@@ -146,22 +146,32 @@
|
|
|
146
146
|
"prepack": "npm run build",
|
|
147
147
|
"prepublishOnly": "npm run check"
|
|
148
148
|
},
|
|
149
|
-
"dependencies": {
|
|
150
|
-
"@askrjs/auth": ">=0.3.0 <0.4.0",
|
|
151
|
-
"@askrjs/schema": ">=0.3.0 <0.4.0"
|
|
152
|
-
},
|
|
153
149
|
"devDependencies": {
|
|
154
|
-
"@
|
|
150
|
+
"@askrjs/auth": ">=0.4.0 <0.5.0",
|
|
151
|
+
"@askrjs/schema": ">=0.4.0 <0.5.0",
|
|
152
|
+
"@types/node": "^26.6.2",
|
|
155
153
|
"@typescript/typescript6": "^6.0.2",
|
|
156
154
|
"@vitest/browser-playwright": "4.1.11",
|
|
157
155
|
"cross-env": "^10.1.0",
|
|
158
|
-
"jsdom": "^30.
|
|
156
|
+
"jsdom": "^30.1.1",
|
|
159
157
|
"playwright": "^1.63.0",
|
|
160
158
|
"publint": "^0.3.24",
|
|
161
159
|
"tsd": "^0.33.0",
|
|
162
160
|
"typescript": "^7.0.2",
|
|
163
161
|
"vite-plus": "^0.3.1"
|
|
164
162
|
},
|
|
163
|
+
"peerDependencies": {
|
|
164
|
+
"@askrjs/auth": ">=0.4.0 <0.5.0",
|
|
165
|
+
"@askrjs/schema": ">=0.4.0 <0.5.0"
|
|
166
|
+
},
|
|
167
|
+
"peerDependenciesMeta": {
|
|
168
|
+
"@askrjs/auth": {
|
|
169
|
+
"optional": true
|
|
170
|
+
},
|
|
171
|
+
"@askrjs/schema": {
|
|
172
|
+
"optional": true
|
|
173
|
+
}
|
|
174
|
+
},
|
|
165
175
|
"tsd": {
|
|
166
176
|
"compilerOptions": {
|
|
167
177
|
"composite": false,
|