@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.
@@ -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
- // node_modules/@borf/bedrock/lib/typeChecking.js
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
- return computed(...args);
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) {