@manyducks.co/dolla 0.69.3 → 0.69.4
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/lib/classes/EventEmitter.d.ts +44 -0
- package/lib/index.js +274 -272
- package/lib/index.js.map +4 -4
- package/lib/nodes/text.d.ts +1 -1
- package/lib/routing.d.ts +79 -0
- package/lib/routing.test.d.ts +1 -0
- package/lib/state.d.ts +1 -1
- package/lib/store.d.ts +1 -1
- package/lib/typeChecking.d.ts +191 -0
- package/lib/types.d.ts +1 -1
- package/package.json +8 -9
- package/tests/state.test.js +52 -52
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
type EventListeners<E extends EventMap> = {
|
|
2
|
+
[K in keyof E]?: EventCallback<E, K>[];
|
|
3
|
+
};
|
|
4
|
+
export type EventCallback<E extends EventMap, K extends keyof E> = (event: EmittedEvent<E, K>) => void;
|
|
5
|
+
/**
|
|
6
|
+
* A map of event names and data values that their listener callbacks take.
|
|
7
|
+
*/
|
|
8
|
+
export interface EventMap {
|
|
9
|
+
[name: string]: any;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* A hub for subscribing to and emitting events. A good pattern when you want to notify several parts of your app
|
|
13
|
+
* at once when a condition changes in a central location. This is a similar pattern to Readable and Writable as far as
|
|
14
|
+
* observability goes, but with the added ability to emit multiple event types each with their own separate listeners.
|
|
15
|
+
*/
|
|
16
|
+
export declare class EventEmitter<E extends EventMap = EventMap> {
|
|
17
|
+
listeners: EventListeners<E>;
|
|
18
|
+
/**
|
|
19
|
+
* Emit an event.
|
|
20
|
+
*/
|
|
21
|
+
emit<K extends keyof E>(name: K, data: E[K]): void;
|
|
22
|
+
/**
|
|
23
|
+
* Listen for an event. The callback will be called whenever that event is emitted.
|
|
24
|
+
* Returns a function that will cancel this listener when called.
|
|
25
|
+
*/
|
|
26
|
+
on<K extends keyof EventListeners<E>>(name: K, callback: EventCallback<E, K>): () => void;
|
|
27
|
+
/**
|
|
28
|
+
* Listen for the next emitted event. The callback will be called once the next time that event is emitted,
|
|
29
|
+
* and then never again. Returns a function that will cancel this listener when called.
|
|
30
|
+
*/
|
|
31
|
+
once<K extends keyof EventListeners<E>>(name: K, callback: EventCallback<E, K>): () => void;
|
|
32
|
+
/**
|
|
33
|
+
* Cancel a listener by passing the callback that was originally used to register it.
|
|
34
|
+
*/
|
|
35
|
+
off<K extends keyof EventListeners<E>>(name: K, callback: EventCallback<E, K>): void;
|
|
36
|
+
}
|
|
37
|
+
declare class EmittedEvent<E extends EventMap, K extends keyof E> {
|
|
38
|
+
/**
|
|
39
|
+
* Data object emitted with this event.
|
|
40
|
+
*/
|
|
41
|
+
data: E[K];
|
|
42
|
+
constructor(data: E[K]);
|
|
43
|
+
}
|
|
44
|
+
export {};
|
package/lib/index.js
CHANGED
|
@@ -29,7 +29,7 @@ var require_lib = __commonJS({
|
|
|
29
29
|
"node_modules/simple-color-hash/lib/index.js"(exports, module) {
|
|
30
30
|
"use strict";
|
|
31
31
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
32
|
-
var _slicedToArray = function() {
|
|
32
|
+
var _slicedToArray = /* @__PURE__ */ function() {
|
|
33
33
|
function a(a2, b) {
|
|
34
34
|
var c = [], d = true, e = false, f = void 0;
|
|
35
35
|
try {
|
|
@@ -88,7 +88,55 @@ var require_lib = __commonJS({
|
|
|
88
88
|
}
|
|
89
89
|
});
|
|
90
90
|
|
|
91
|
-
//
|
|
91
|
+
// src/classes/CrashCollector.ts
|
|
92
|
+
var CrashCollector = class {
|
|
93
|
+
#errors = [];
|
|
94
|
+
#errorCallbacks = [];
|
|
95
|
+
/**
|
|
96
|
+
* Registers a callback to receive all errors that pass through the CrashCollector.
|
|
97
|
+
* Returns a function that cancels this listener when called.
|
|
98
|
+
*/
|
|
99
|
+
onError(callback) {
|
|
100
|
+
this.#errorCallbacks.push(callback);
|
|
101
|
+
return () => {
|
|
102
|
+
this.#errorCallbacks.splice(this.#errorCallbacks.indexOf(callback), 1);
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Reports an unrecoverable error that requires crashing the whole app.
|
|
107
|
+
*/
|
|
108
|
+
crash({ error, componentName }) {
|
|
109
|
+
const ctx = {
|
|
110
|
+
error,
|
|
111
|
+
severity: "crash",
|
|
112
|
+
componentName: componentName ?? "anonymous component"
|
|
113
|
+
};
|
|
114
|
+
this.#errors.push(ctx);
|
|
115
|
+
for (const callback of this.#errorCallbacks) {
|
|
116
|
+
callback(ctx);
|
|
117
|
+
}
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Reports a recoverable error.
|
|
122
|
+
*/
|
|
123
|
+
error({ error, componentName }) {
|
|
124
|
+
const ctx = {
|
|
125
|
+
error,
|
|
126
|
+
severity: "error",
|
|
127
|
+
componentName: componentName ?? "anonymous component"
|
|
128
|
+
};
|
|
129
|
+
this.#errors.push(ctx);
|
|
130
|
+
for (const callback of this.#errorCallbacks) {
|
|
131
|
+
callback(ctx);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
// src/classes/DebugHub.ts
|
|
137
|
+
var import_simple_color_hash = __toESM(require_lib(), 1);
|
|
138
|
+
|
|
139
|
+
// src/typeChecking.ts
|
|
92
140
|
function typeOf(value) {
|
|
93
141
|
if (value === void 0) {
|
|
94
142
|
return "undefined";
|
|
@@ -209,274 +257,7 @@ function formatError(value, message) {
|
|
|
209
257
|
return message.replaceAll("%t", typeName).replaceAll("%v", valueString);
|
|
210
258
|
}
|
|
211
259
|
|
|
212
|
-
// node_modules/@borf/bedrock/lib/routing.js
|
|
213
|
-
var FragTypes;
|
|
214
|
-
(function(FragTypes2) {
|
|
215
|
-
FragTypes2[FragTypes2["Literal"] = 1] = "Literal";
|
|
216
|
-
FragTypes2[FragTypes2["Param"] = 2] = "Param";
|
|
217
|
-
FragTypes2[FragTypes2["Wildcard"] = 3] = "Wildcard";
|
|
218
|
-
FragTypes2[FragTypes2["NumericParam"] = 4] = "NumericParam";
|
|
219
|
-
})(FragTypes || (FragTypes = {}));
|
|
220
|
-
function splitPath(path) {
|
|
221
|
-
assertString(path, "Expected `path` to be a string. Got type: %t, value: %v");
|
|
222
|
-
return path.split("/").map((f) => f.trim()).filter((f) => f !== "");
|
|
223
|
-
}
|
|
224
|
-
function joinPath(parts) {
|
|
225
|
-
assertArrayOf((part) => isFunction(part?.toString), parts, "Expected `parts` to be an array of objects with a .toString() method. Got type: %t, value: %v");
|
|
226
|
-
parts = parts.filter((x) => x).flatMap(String);
|
|
227
|
-
let joined = parts.shift()?.toString();
|
|
228
|
-
if (joined) {
|
|
229
|
-
for (const part of parts.map((p) => p.toString())) {
|
|
230
|
-
if (part.startsWith(".")) {
|
|
231
|
-
joined = resolvePath(joined, part);
|
|
232
|
-
} else if (joined[joined.length - 1] !== "/") {
|
|
233
|
-
if (part[0] !== "/") {
|
|
234
|
-
joined += "/" + part;
|
|
235
|
-
} else {
|
|
236
|
-
joined += part;
|
|
237
|
-
}
|
|
238
|
-
} else {
|
|
239
|
-
if (part[0] === "/") {
|
|
240
|
-
joined += part.slice(1);
|
|
241
|
-
} else {
|
|
242
|
-
joined += part;
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
if (joined && joined !== "/" && joined.endsWith("/")) {
|
|
247
|
-
joined = joined.slice(0, joined.length - 1);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
return joined ?? "";
|
|
251
|
-
}
|
|
252
|
-
function resolvePath(base, part) {
|
|
253
|
-
assertString(base, "Expected `base` to be a string. Got type: %t, value: %v");
|
|
254
|
-
if (part == null) {
|
|
255
|
-
part = base;
|
|
256
|
-
base = "";
|
|
257
|
-
}
|
|
258
|
-
if (part.startsWith("/")) {
|
|
259
|
-
return part;
|
|
260
|
-
}
|
|
261
|
-
let resolved = base;
|
|
262
|
-
while (true) {
|
|
263
|
-
if (part.startsWith("..")) {
|
|
264
|
-
for (let i = resolved.length; i > 0; --i) {
|
|
265
|
-
if (resolved[i] === "/" || i === 0) {
|
|
266
|
-
resolved = resolved.slice(0, i);
|
|
267
|
-
part = part.replace(/^\.\.\/?/, "");
|
|
268
|
-
break;
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
} else if (part.startsWith(".")) {
|
|
272
|
-
part = part.replace(/^\.\/?/, "");
|
|
273
|
-
} else {
|
|
274
|
-
break;
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
return joinPath([resolved, part]);
|
|
278
|
-
}
|
|
279
|
-
function parseQueryParams(query) {
|
|
280
|
-
if (!query)
|
|
281
|
-
return {};
|
|
282
|
-
const entries = query.split("&").filter((x) => x.trim() !== "").map((entry) => {
|
|
283
|
-
const [key, value] = entry.split("=").map((x) => x.trim());
|
|
284
|
-
if (value.toLowerCase() === "true") {
|
|
285
|
-
return [key, true];
|
|
286
|
-
}
|
|
287
|
-
if (value.toLowerCase() === "false") {
|
|
288
|
-
return [key, false];
|
|
289
|
-
}
|
|
290
|
-
if (!isNaN(Number(value))) {
|
|
291
|
-
return [key, Number(value)];
|
|
292
|
-
}
|
|
293
|
-
return [key, value];
|
|
294
|
-
});
|
|
295
|
-
return Object.fromEntries(entries);
|
|
296
|
-
}
|
|
297
|
-
function matchRoutes(routes, url, options = {}) {
|
|
298
|
-
const [path, query] = url.split("?");
|
|
299
|
-
const parts = splitPath(path);
|
|
300
|
-
routes:
|
|
301
|
-
for (const route of routes) {
|
|
302
|
-
const { fragments } = route;
|
|
303
|
-
const hasWildcard = fragments[fragments.length - 1]?.type === FragTypes.Wildcard;
|
|
304
|
-
if (!hasWildcard && fragments.length !== parts.length) {
|
|
305
|
-
continue routes;
|
|
306
|
-
}
|
|
307
|
-
if (options.willMatch && !options.willMatch(route)) {
|
|
308
|
-
continue routes;
|
|
309
|
-
}
|
|
310
|
-
const matched = [];
|
|
311
|
-
fragments:
|
|
312
|
-
for (let i = 0; i < fragments.length; i++) {
|
|
313
|
-
const part = parts[i];
|
|
314
|
-
const frag = fragments[i];
|
|
315
|
-
if (part == null && frag.type !== FragTypes.Wildcard) {
|
|
316
|
-
continue routes;
|
|
317
|
-
}
|
|
318
|
-
switch (frag.type) {
|
|
319
|
-
case FragTypes.Literal:
|
|
320
|
-
if (frag.name.toLowerCase() === part.toLowerCase()) {
|
|
321
|
-
matched.push(frag);
|
|
322
|
-
break;
|
|
323
|
-
} else {
|
|
324
|
-
continue routes;
|
|
325
|
-
}
|
|
326
|
-
case FragTypes.Param:
|
|
327
|
-
matched.push({ ...frag, value: part });
|
|
328
|
-
break;
|
|
329
|
-
case FragTypes.Wildcard:
|
|
330
|
-
matched.push({ ...frag, value: parts.slice(i).join("/") });
|
|
331
|
-
break fragments;
|
|
332
|
-
case FragTypes.NumericParam:
|
|
333
|
-
if (!isNaN(Number(part))) {
|
|
334
|
-
matched.push({ ...frag, value: Number(part) });
|
|
335
|
-
break;
|
|
336
|
-
} else {
|
|
337
|
-
continue routes;
|
|
338
|
-
}
|
|
339
|
-
default:
|
|
340
|
-
throw new Error(`Unknown fragment type: ${frag.type}`);
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
const params = /* @__PURE__ */ Object.create(null);
|
|
344
|
-
for (const frag of matched) {
|
|
345
|
-
if (frag.type === FragTypes.Param) {
|
|
346
|
-
params[frag.name] = decodeURIComponent(frag.value);
|
|
347
|
-
}
|
|
348
|
-
if (frag.type === FragTypes.NumericParam) {
|
|
349
|
-
params[frag.name] = frag.value;
|
|
350
|
-
}
|
|
351
|
-
if (frag.type === FragTypes.Wildcard) {
|
|
352
|
-
params.wildcard = "/" + decodeURIComponent(frag.value);
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
return {
|
|
356
|
-
path: "/" + matched.map((f) => f.value).join("/"),
|
|
357
|
-
pattern: "/" + fragments.map((f) => {
|
|
358
|
-
if (f.type === FragTypes.Param) {
|
|
359
|
-
return `{${f.name}}`;
|
|
360
|
-
}
|
|
361
|
-
if (f.type === FragTypes.NumericParam) {
|
|
362
|
-
return `{#${f.name}}`;
|
|
363
|
-
}
|
|
364
|
-
return f.name;
|
|
365
|
-
}).join("/"),
|
|
366
|
-
params,
|
|
367
|
-
query: parseQueryParams(query),
|
|
368
|
-
meta: route.meta
|
|
369
|
-
};
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
function sortRoutes(routes) {
|
|
373
|
-
const withoutParams = [];
|
|
374
|
-
const withNumericParams = [];
|
|
375
|
-
const withParams = [];
|
|
376
|
-
const wildcard = [];
|
|
377
|
-
for (const route of routes) {
|
|
378
|
-
const { fragments } = route;
|
|
379
|
-
if (fragments.some((f) => f.type === FragTypes.Wildcard)) {
|
|
380
|
-
wildcard.push(route);
|
|
381
|
-
} else if (fragments.some((f) => f.type === FragTypes.NumericParam)) {
|
|
382
|
-
withNumericParams.push(route);
|
|
383
|
-
} else if (fragments.some((f) => f.type === FragTypes.Param)) {
|
|
384
|
-
withParams.push(route);
|
|
385
|
-
} else {
|
|
386
|
-
withoutParams.push(route);
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
const bySizeDesc = (a, b) => {
|
|
390
|
-
if (a.fragments.length > b.fragments.length) {
|
|
391
|
-
return -1;
|
|
392
|
-
} else {
|
|
393
|
-
return 1;
|
|
394
|
-
}
|
|
395
|
-
};
|
|
396
|
-
withoutParams.sort(bySizeDesc);
|
|
397
|
-
withNumericParams.sort(bySizeDesc);
|
|
398
|
-
withParams.sort(bySizeDesc);
|
|
399
|
-
wildcard.sort(bySizeDesc);
|
|
400
|
-
return [...withoutParams, ...withNumericParams, ...withParams, ...wildcard];
|
|
401
|
-
}
|
|
402
|
-
function patternToFragments(pattern) {
|
|
403
|
-
const parts = splitPath(pattern);
|
|
404
|
-
const fragments = [];
|
|
405
|
-
for (let i = 0; i < parts.length; i++) {
|
|
406
|
-
const part = parts[i];
|
|
407
|
-
if (part === "*") {
|
|
408
|
-
if (i !== parts.length - 1) {
|
|
409
|
-
throw new Error(`Wildcard must be at the end of a pattern. Received: ${pattern}`);
|
|
410
|
-
}
|
|
411
|
-
fragments.push({
|
|
412
|
-
type: FragTypes.Wildcard,
|
|
413
|
-
name: "*",
|
|
414
|
-
value: null
|
|
415
|
-
});
|
|
416
|
-
} else if (part.at(0) === "{" && part.at(-1) === "}") {
|
|
417
|
-
fragments.push({
|
|
418
|
-
type: part[1] === "#" ? FragTypes.NumericParam : FragTypes.Param,
|
|
419
|
-
name: part[1] === "#" ? part.slice(2, -1) : part.slice(1, -1),
|
|
420
|
-
value: null
|
|
421
|
-
});
|
|
422
|
-
} else {
|
|
423
|
-
fragments.push({
|
|
424
|
-
type: FragTypes.Literal,
|
|
425
|
-
name: part,
|
|
426
|
-
value: part
|
|
427
|
-
});
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
return fragments;
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
// src/classes/CrashCollector.ts
|
|
434
|
-
var CrashCollector = class {
|
|
435
|
-
#errors = [];
|
|
436
|
-
#errorCallbacks = [];
|
|
437
|
-
/**
|
|
438
|
-
* Registers a callback to receive all errors that pass through the CrashCollector.
|
|
439
|
-
* Returns a function that cancels this listener when called.
|
|
440
|
-
*/
|
|
441
|
-
onError(callback) {
|
|
442
|
-
this.#errorCallbacks.push(callback);
|
|
443
|
-
return () => {
|
|
444
|
-
this.#errorCallbacks.splice(this.#errorCallbacks.indexOf(callback), 1);
|
|
445
|
-
};
|
|
446
|
-
}
|
|
447
|
-
/**
|
|
448
|
-
* Reports an unrecoverable error that requires crashing the whole app.
|
|
449
|
-
*/
|
|
450
|
-
crash({ error, componentName }) {
|
|
451
|
-
const ctx = {
|
|
452
|
-
error,
|
|
453
|
-
severity: "crash",
|
|
454
|
-
componentName: componentName ?? "anonymous component"
|
|
455
|
-
};
|
|
456
|
-
this.#errors.push(ctx);
|
|
457
|
-
for (const callback of this.#errorCallbacks) {
|
|
458
|
-
callback(ctx);
|
|
459
|
-
}
|
|
460
|
-
throw error;
|
|
461
|
-
}
|
|
462
|
-
/**
|
|
463
|
-
* Reports a recoverable error.
|
|
464
|
-
*/
|
|
465
|
-
error({ error, componentName }) {
|
|
466
|
-
const ctx = {
|
|
467
|
-
error,
|
|
468
|
-
severity: "error",
|
|
469
|
-
componentName: componentName ?? "anonymous component"
|
|
470
|
-
};
|
|
471
|
-
this.#errors.push(ctx);
|
|
472
|
-
for (const callback of this.#errorCallbacks) {
|
|
473
|
-
callback(ctx);
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
};
|
|
477
|
-
|
|
478
260
|
// src/classes/DebugHub.ts
|
|
479
|
-
var import_simple_color_hash = __toESM(require_lib(), 1);
|
|
480
261
|
var DebugHub = class {
|
|
481
262
|
#filter = "*,-dolla/*";
|
|
482
263
|
#matcher;
|
|
@@ -686,7 +467,9 @@ function $$(initialValue, config) {
|
|
|
686
467
|
}
|
|
687
468
|
function $(...args) {
|
|
688
469
|
if (args.length > 1) {
|
|
689
|
-
|
|
470
|
+
const callback = args.pop();
|
|
471
|
+
const readables = args.flat().map(readable);
|
|
472
|
+
return computed(...readables, callback);
|
|
690
473
|
} else {
|
|
691
474
|
return readable(args[0]);
|
|
692
475
|
}
|
|
@@ -715,7 +498,6 @@ function computed(...args) {
|
|
|
715
498
|
if (typeof compute !== "function") {
|
|
716
499
|
throw new TypeError(`Final argument must be a function. Got ${typeOf(compute)}: ${compute}`);
|
|
717
500
|
}
|
|
718
|
-
args = args.flat().map(readable);
|
|
719
501
|
if (args.length < 1) {
|
|
720
502
|
throw new Error(`Must pass at least one value before the callback function.`);
|
|
721
503
|
}
|
|
@@ -881,7 +663,7 @@ function proxy(source, config) {
|
|
|
881
663
|
}
|
|
882
664
|
function observe(...args) {
|
|
883
665
|
const callback = args.pop();
|
|
884
|
-
const readables = args.flat();
|
|
666
|
+
const readables = args.flat().map(readable);
|
|
885
667
|
if (readables.length === 0) {
|
|
886
668
|
throw new TypeError(`Expected at least one readable.`);
|
|
887
669
|
}
|
|
@@ -3146,6 +2928,226 @@ function parsePath(path) {
|
|
|
3146
2928
|
return parsedPath;
|
|
3147
2929
|
}
|
|
3148
2930
|
|
|
2931
|
+
// src/routing.ts
|
|
2932
|
+
function splitPath(path) {
|
|
2933
|
+
assertString(path, "Expected `path` to be a string. Got type: %t, value: %v");
|
|
2934
|
+
return path.split("/").map((f) => f.trim()).filter((f) => f !== "");
|
|
2935
|
+
}
|
|
2936
|
+
function joinPath(parts) {
|
|
2937
|
+
assertArrayOf(
|
|
2938
|
+
(part) => isFunction(part?.toString),
|
|
2939
|
+
parts,
|
|
2940
|
+
"Expected `parts` to be an array of objects with a .toString() method. Got type: %t, value: %v"
|
|
2941
|
+
);
|
|
2942
|
+
parts = parts.filter((x) => x).flatMap(String);
|
|
2943
|
+
let joined = parts.shift()?.toString();
|
|
2944
|
+
if (joined) {
|
|
2945
|
+
for (const part of parts.map((p) => p.toString())) {
|
|
2946
|
+
if (part.startsWith(".")) {
|
|
2947
|
+
joined = resolvePath(joined, part);
|
|
2948
|
+
} else if (joined[joined.length - 1] !== "/") {
|
|
2949
|
+
if (part[0] !== "/") {
|
|
2950
|
+
joined += "/" + part;
|
|
2951
|
+
} else {
|
|
2952
|
+
joined += part;
|
|
2953
|
+
}
|
|
2954
|
+
} else {
|
|
2955
|
+
if (part[0] === "/") {
|
|
2956
|
+
joined += part.slice(1);
|
|
2957
|
+
} else {
|
|
2958
|
+
joined += part;
|
|
2959
|
+
}
|
|
2960
|
+
}
|
|
2961
|
+
}
|
|
2962
|
+
if (joined && joined !== "/" && joined.endsWith("/")) {
|
|
2963
|
+
joined = joined.slice(0, joined.length - 1);
|
|
2964
|
+
}
|
|
2965
|
+
}
|
|
2966
|
+
return joined ?? "";
|
|
2967
|
+
}
|
|
2968
|
+
function resolvePath(base, part) {
|
|
2969
|
+
assertString(base, "Expected `base` to be a string. Got type: %t, value: %v");
|
|
2970
|
+
if (part == null) {
|
|
2971
|
+
part = base;
|
|
2972
|
+
base = "";
|
|
2973
|
+
}
|
|
2974
|
+
if (part.startsWith("/")) {
|
|
2975
|
+
return part;
|
|
2976
|
+
}
|
|
2977
|
+
let resolved = base;
|
|
2978
|
+
while (true) {
|
|
2979
|
+
if (part.startsWith("..")) {
|
|
2980
|
+
for (let i = resolved.length; i > 0; --i) {
|
|
2981
|
+
if (resolved[i] === "/" || i === 0) {
|
|
2982
|
+
resolved = resolved.slice(0, i);
|
|
2983
|
+
part = part.replace(/^\.\.\/?/, "");
|
|
2984
|
+
break;
|
|
2985
|
+
}
|
|
2986
|
+
}
|
|
2987
|
+
} else if (part.startsWith(".")) {
|
|
2988
|
+
part = part.replace(/^\.\/?/, "");
|
|
2989
|
+
} else {
|
|
2990
|
+
break;
|
|
2991
|
+
}
|
|
2992
|
+
}
|
|
2993
|
+
return joinPath([resolved, part]);
|
|
2994
|
+
}
|
|
2995
|
+
function parseQueryParams(query) {
|
|
2996
|
+
if (!query)
|
|
2997
|
+
return {};
|
|
2998
|
+
const entries = query.split("&").filter((x) => x.trim() !== "").map((entry) => {
|
|
2999
|
+
const [key, value] = entry.split("=").map((x) => x.trim());
|
|
3000
|
+
if (value.toLowerCase() === "true") {
|
|
3001
|
+
return [key, true];
|
|
3002
|
+
}
|
|
3003
|
+
if (value.toLowerCase() === "false") {
|
|
3004
|
+
return [key, false];
|
|
3005
|
+
}
|
|
3006
|
+
if (!isNaN(Number(value))) {
|
|
3007
|
+
return [key, Number(value)];
|
|
3008
|
+
}
|
|
3009
|
+
return [key, value];
|
|
3010
|
+
});
|
|
3011
|
+
return Object.fromEntries(entries);
|
|
3012
|
+
}
|
|
3013
|
+
function matchRoutes(routes, url, options = {}) {
|
|
3014
|
+
const [path, query] = url.split("?");
|
|
3015
|
+
const parts = splitPath(path);
|
|
3016
|
+
routes:
|
|
3017
|
+
for (const route of routes) {
|
|
3018
|
+
const { fragments } = route;
|
|
3019
|
+
const hasWildcard = fragments[fragments.length - 1]?.type === 3 /* Wildcard */;
|
|
3020
|
+
if (!hasWildcard && fragments.length !== parts.length) {
|
|
3021
|
+
continue routes;
|
|
3022
|
+
}
|
|
3023
|
+
if (options.willMatch && !options.willMatch(route)) {
|
|
3024
|
+
continue routes;
|
|
3025
|
+
}
|
|
3026
|
+
const matched = [];
|
|
3027
|
+
fragments:
|
|
3028
|
+
for (let i = 0; i < fragments.length; i++) {
|
|
3029
|
+
const part = parts[i];
|
|
3030
|
+
const frag = fragments[i];
|
|
3031
|
+
if (part == null && frag.type !== 3 /* Wildcard */) {
|
|
3032
|
+
continue routes;
|
|
3033
|
+
}
|
|
3034
|
+
switch (frag.type) {
|
|
3035
|
+
case 1 /* Literal */:
|
|
3036
|
+
if (frag.name.toLowerCase() === part.toLowerCase()) {
|
|
3037
|
+
matched.push(frag);
|
|
3038
|
+
break;
|
|
3039
|
+
} else {
|
|
3040
|
+
continue routes;
|
|
3041
|
+
}
|
|
3042
|
+
case 2 /* Param */:
|
|
3043
|
+
matched.push({ ...frag, value: part });
|
|
3044
|
+
break;
|
|
3045
|
+
case 3 /* Wildcard */:
|
|
3046
|
+
matched.push({ ...frag, value: parts.slice(i).join("/") });
|
|
3047
|
+
break fragments;
|
|
3048
|
+
case 4 /* NumericParam */:
|
|
3049
|
+
if (!isNaN(Number(part))) {
|
|
3050
|
+
matched.push({ ...frag, value: Number(part) });
|
|
3051
|
+
break;
|
|
3052
|
+
} else {
|
|
3053
|
+
continue routes;
|
|
3054
|
+
}
|
|
3055
|
+
default:
|
|
3056
|
+
throw new Error(`Unknown fragment type: ${frag.type}`);
|
|
3057
|
+
}
|
|
3058
|
+
}
|
|
3059
|
+
const params = /* @__PURE__ */ Object.create(null);
|
|
3060
|
+
for (const frag of matched) {
|
|
3061
|
+
if (frag.type === 2 /* Param */) {
|
|
3062
|
+
params[frag.name] = decodeURIComponent(frag.value);
|
|
3063
|
+
}
|
|
3064
|
+
if (frag.type === 4 /* NumericParam */) {
|
|
3065
|
+
params[frag.name] = frag.value;
|
|
3066
|
+
}
|
|
3067
|
+
if (frag.type === 3 /* Wildcard */) {
|
|
3068
|
+
params.wildcard = "/" + decodeURIComponent(frag.value);
|
|
3069
|
+
}
|
|
3070
|
+
}
|
|
3071
|
+
return {
|
|
3072
|
+
path: "/" + matched.map((f) => f.value).join("/"),
|
|
3073
|
+
pattern: "/" + fragments.map((f) => {
|
|
3074
|
+
if (f.type === 2 /* Param */) {
|
|
3075
|
+
return `{${f.name}}`;
|
|
3076
|
+
}
|
|
3077
|
+
if (f.type === 4 /* NumericParam */) {
|
|
3078
|
+
return `{#${f.name}}`;
|
|
3079
|
+
}
|
|
3080
|
+
return f.name;
|
|
3081
|
+
}).join("/"),
|
|
3082
|
+
params,
|
|
3083
|
+
query: parseQueryParams(query),
|
|
3084
|
+
meta: route.meta
|
|
3085
|
+
};
|
|
3086
|
+
}
|
|
3087
|
+
}
|
|
3088
|
+
function sortRoutes(routes) {
|
|
3089
|
+
const withoutParams = [];
|
|
3090
|
+
const withNumericParams = [];
|
|
3091
|
+
const withParams = [];
|
|
3092
|
+
const wildcard = [];
|
|
3093
|
+
for (const route of routes) {
|
|
3094
|
+
const { fragments } = route;
|
|
3095
|
+
if (fragments.some((f) => f.type === 3 /* Wildcard */)) {
|
|
3096
|
+
wildcard.push(route);
|
|
3097
|
+
} else if (fragments.some((f) => f.type === 4 /* NumericParam */)) {
|
|
3098
|
+
withNumericParams.push(route);
|
|
3099
|
+
} else if (fragments.some((f) => f.type === 2 /* Param */)) {
|
|
3100
|
+
withParams.push(route);
|
|
3101
|
+
} else {
|
|
3102
|
+
withoutParams.push(route);
|
|
3103
|
+
}
|
|
3104
|
+
}
|
|
3105
|
+
const bySizeDesc = (a, b) => {
|
|
3106
|
+
if (a.fragments.length > b.fragments.length) {
|
|
3107
|
+
return -1;
|
|
3108
|
+
} else {
|
|
3109
|
+
return 1;
|
|
3110
|
+
}
|
|
3111
|
+
};
|
|
3112
|
+
withoutParams.sort(bySizeDesc);
|
|
3113
|
+
withNumericParams.sort(bySizeDesc);
|
|
3114
|
+
withParams.sort(bySizeDesc);
|
|
3115
|
+
wildcard.sort(bySizeDesc);
|
|
3116
|
+
return [...withoutParams, ...withNumericParams, ...withParams, ...wildcard];
|
|
3117
|
+
}
|
|
3118
|
+
function patternToFragments(pattern) {
|
|
3119
|
+
const parts = splitPath(pattern);
|
|
3120
|
+
const fragments = [];
|
|
3121
|
+
for (let i = 0; i < parts.length; i++) {
|
|
3122
|
+
const part = parts[i];
|
|
3123
|
+
if (part === "*") {
|
|
3124
|
+
if (i !== parts.length - 1) {
|
|
3125
|
+
throw new Error(
|
|
3126
|
+
`Wildcard must be at the end of a pattern. Received: ${pattern}`
|
|
3127
|
+
);
|
|
3128
|
+
}
|
|
3129
|
+
fragments.push({
|
|
3130
|
+
type: 3 /* Wildcard */,
|
|
3131
|
+
name: "*",
|
|
3132
|
+
value: null
|
|
3133
|
+
});
|
|
3134
|
+
} else if (part.at(0) === "{" && part.at(-1) === "}") {
|
|
3135
|
+
fragments.push({
|
|
3136
|
+
type: part[1] === "#" ? 4 /* NumericParam */ : 2 /* Param */,
|
|
3137
|
+
name: part[1] === "#" ? part.slice(2, -1) : part.slice(1, -1),
|
|
3138
|
+
value: null
|
|
3139
|
+
});
|
|
3140
|
+
} else {
|
|
3141
|
+
fragments.push({
|
|
3142
|
+
type: 1 /* Literal */,
|
|
3143
|
+
name: part,
|
|
3144
|
+
value: part
|
|
3145
|
+
});
|
|
3146
|
+
}
|
|
3147
|
+
}
|
|
3148
|
+
return fragments;
|
|
3149
|
+
}
|
|
3150
|
+
|
|
3149
3151
|
// src/stores/router.ts
|
|
3150
3152
|
var DefaultView = (_, ctx) => ctx.outlet();
|
|
3151
3153
|
function RouterStore(ctx) {
|