@manyducks.co/dolla 2.0.0-alpha.35 → 2.0.0-alpha.37

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/dist/index.js CHANGED
@@ -1,262 +1,336 @@
1
- var Ke = Object.defineProperty;
2
- var _e = (i) => {
3
- throw TypeError(i);
4
- };
5
- var Qe = (i, e, t) => e in i ? Ke(i, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : i[e] = t;
6
- var p = (i, e, t) => Qe(i, typeof e != "symbol" ? e + "" : e, t), pe = (i, e, t) => e.has(i) || _e("Cannot " + t);
7
- var a = (i, e, t) => (pe(i, e, "read from private field"), t ? t.call(i) : e.get(i)), h = (i, e, t) => e.has(i) ? _e("Cannot add the same private member more than once") : e instanceof WeakSet ? e.add(i) : e.set(i, t), d = (i, e, t, r) => (pe(i, e, "write to private field"), r ? r.call(i, t) : e.set(i, t), t), f = (i, e, t) => (pe(i, e, "access private method"), t);
8
- var Se = (i, e, t, r) => ({
9
- set _(n) {
10
- d(i, e, n, t);
11
- },
12
- get _() {
13
- return a(i, e, r);
14
- }
15
- });
16
- import { I as Je, a as Ue, b as Xe, i as Q, c as je, d as Ce, e as C, g as E, s as Me, f as Ye, h as k, j as G, P as De, k as Ze, t as xe, l as Te, m as et, p as tt, n as Ne, S as rt, o as Pe, q as Fe, r as st, u as ce, v as he, V as nt, w as at, x as it } from "./markup-BWJWLvDF.js";
17
- import { y as Ct, D as Dt, A as It, C as Vt, z as qt, B as Gt } from "./markup-BWJWLvDF.js";
18
- function Ft(i) {
19
- function e() {
20
- if (arguments.length === 1)
21
- i = arguments[0];
22
- else if (arguments.length > 1)
1
+ import { a as assertArrayOf, b as assertString, i as isFunction, I as IS_ROUTER, c as atom, d as compose, g as get, s as shallowEqual, e as assertObject, f as isString, h as isObject, P as Passthrough, j as deepEqual, t as typeOf, k as cond, l as html, m as createMatcher, S as Store, n as StoreError, o as assertInstanceOf, p as createMarkup, V as View, q as groupElements, r as constructMarkup, u as noOp, v as okhash } from './markup-Px0heVon.js';
2
+ export { w as effect, B as list, y as peek, A as portal, x as set, z as strictEqual } from './markup-Px0heVon.js';
3
+
4
+ function ref(value) {
5
+ return function() {
6
+ if (arguments.length === 1) {
7
+ value = arguments[0];
8
+ } else if (arguments.length > 1) {
23
9
  throw new Error(`Too many arguments. Expected 0 or 1. Got: ${arguments.length}`);
24
- return i;
25
- }
26
- return e[Je] = !0, e;
10
+ }
11
+ return value;
12
+ };
27
13
  }
28
- function X(i) {
29
- return Ue(i, "Expected `path` to be a string. Got type: %t, value: %v"), i.split("/").map((e) => e.trim()).filter((e) => e !== "");
14
+
15
+ function splitPath(path) {
16
+ assertString(path, "Expected `path` to be a string. Got type: %t, value: %v");
17
+ return path.split("/").map((f) => f.trim()).filter((f) => f !== "");
30
18
  }
31
- function A(i) {
32
- var t;
33
- Xe(
34
- (r) => Q(r == null ? void 0 : r.toString),
35
- i,
19
+ function joinPath(parts) {
20
+ assertArrayOf(
21
+ (part) => isFunction(part?.toString),
22
+ parts,
36
23
  "Expected `parts` to be an array of objects with a .toString() method. Got type: %t, value: %v"
37
- ), i = i.filter((r) => r).flatMap(String);
38
- let e = (t = i.shift()) == null ? void 0 : t.toString();
39
- if (e) {
40
- for (const r of i.map((n) => n.toString()))
41
- r.startsWith(".") ? e = ue(e, r) : e[e.length - 1] !== "/" ? r[0] !== "/" ? e += "/" + r : e += r : r[0] === "/" ? e += r.slice(1) : e += r;
42
- e && e !== "/" && e.endsWith("/") && (e = e.slice(0, e.length - 1));
43
- }
44
- return e ?? "";
24
+ );
25
+ parts = parts.filter((x) => x).flatMap(String);
26
+ let joined = parts.shift()?.toString();
27
+ if (joined) {
28
+ for (const part of parts.map((p) => p.toString())) {
29
+ if (part.startsWith(".")) {
30
+ joined = resolvePath(joined, part);
31
+ } else if (joined[joined.length - 1] !== "/") {
32
+ if (part[0] !== "/") {
33
+ joined += "/" + part;
34
+ } else {
35
+ joined += part;
36
+ }
37
+ } else {
38
+ if (part[0] === "/") {
39
+ joined += part.slice(1);
40
+ } else {
41
+ joined += part;
42
+ }
43
+ }
44
+ }
45
+ if (joined && joined !== "/" && joined.endsWith("/")) {
46
+ joined = joined.slice(0, joined.length - 1);
47
+ }
48
+ }
49
+ return joined ?? "";
45
50
  }
46
- function ue(i, e) {
47
- if (Ue(i, "Expected `base` to be a string. Got type: %t, value: %v"), e == null && (e = i, i = ""), e.startsWith("/"))
48
- return e;
49
- let t = i;
50
- for (; ; )
51
- if (e.startsWith("..")) {
52
- for (let r = t.length; r > 0; --r)
53
- if (t[r] === "/" || r === 0) {
54
- t = t.slice(0, r), e = e.replace(/^\.\.\/?/, "");
51
+ function resolvePath(base, part) {
52
+ assertString(base, "Expected `base` to be a string. Got type: %t, value: %v");
53
+ if (part == null) {
54
+ part = base;
55
+ base = "";
56
+ }
57
+ if (part.startsWith("/")) {
58
+ return part;
59
+ }
60
+ let resolved = base;
61
+ while (true) {
62
+ if (part.startsWith("..")) {
63
+ for (let i = resolved.length; i > 0; --i) {
64
+ if (resolved[i] === "/" || i === 0) {
65
+ resolved = resolved.slice(0, i);
66
+ part = part.replace(/^\.\.\/?/, "");
55
67
  break;
56
68
  }
57
- } else if (e.startsWith("."))
58
- e = e.replace(/^\.\/?/, "");
59
- else
69
+ }
70
+ } else if (part.startsWith(".")) {
71
+ part = part.replace(/^\.\/?/, "");
72
+ } else {
60
73
  break;
61
- return A([t, e]);
74
+ }
75
+ }
76
+ return joinPath([resolved, part]);
62
77
  }
63
- function ot(i) {
64
- if (!i) return {};
65
- i.startsWith("?") && (i = i.slice(1));
66
- const e = i.split("&").filter((t) => t.trim() !== "").map((t) => {
67
- const [r, n] = t.split("=").map((s) => s.trim());
68
- return n.toLowerCase() === "true" ? [r, !0] : n.toLowerCase() === "false" ? [r, !1] : isNaN(Number(n)) ? [r, n] : [r, Number(n)];
78
+ function parseQueryParams(query) {
79
+ if (!query) return {};
80
+ if (query.startsWith("?")) {
81
+ query = query.slice(1);
82
+ }
83
+ const entries = query.split("&").filter((x) => x.trim() !== "").map((entry) => {
84
+ const [key, value] = entry.split("=").map((x) => x.trim());
85
+ if (value.toLowerCase() === "true") {
86
+ return [key, true];
87
+ }
88
+ if (value.toLowerCase() === "false") {
89
+ return [key, false];
90
+ }
91
+ if (!isNaN(Number(value))) {
92
+ return [key, Number(value)];
93
+ }
94
+ return [key, value];
69
95
  });
70
- return Object.fromEntries(e);
96
+ return Object.fromEntries(entries);
71
97
  }
72
- function Ie(i, e, t = {}) {
73
- var o;
74
- const [r, n] = e.split("?"), s = X(r);
75
- e: for (const u of i) {
76
- const { fragments: l } = u;
77
- if (!(((o = l[l.length - 1]) == null ? void 0 : o.type) === 3) && l.length !== s.length || t.willMatch && !t.willMatch(u))
78
- continue e;
79
- const $ = [];
80
- t: for (let c = 0; c < l.length; c++) {
81
- const T = s[c], _ = l[c];
82
- if (T == null && _.type !== 3)
83
- continue e;
84
- switch (_.type) {
85
- case 1:
86
- if (_.name.toLowerCase() === T.toLowerCase()) {
87
- $.push(_);
98
+ function matchRoutes(routes, url, options = {}) {
99
+ const [path, query] = url.split("?");
100
+ const parts = splitPath(path);
101
+ routes: for (const route of routes) {
102
+ const { fragments } = route;
103
+ const hasWildcard = fragments[fragments.length - 1]?.type === 3 /* Wildcard */;
104
+ if (!hasWildcard && fragments.length !== parts.length) {
105
+ continue routes;
106
+ }
107
+ if (options.willMatch && !options.willMatch(route)) {
108
+ continue routes;
109
+ }
110
+ const matched = [];
111
+ fragments: for (let i = 0; i < fragments.length; i++) {
112
+ const part = parts[i];
113
+ const frag = fragments[i];
114
+ if (part == null && frag.type !== 3 /* Wildcard */) {
115
+ continue routes;
116
+ }
117
+ switch (frag.type) {
118
+ case 1 /* Literal */:
119
+ if (frag.name.toLowerCase() === part.toLowerCase()) {
120
+ matched.push(frag);
88
121
  break;
89
- } else
90
- continue e;
91
- case 2:
92
- $.push({ ..._, value: T });
93
- break;
94
- case 3:
95
- $.push({ ..._, value: s.slice(c).join("/") });
96
- break t;
97
- case 4:
98
- if (isNaN(Number(T)))
99
- continue e;
100
- $.push({ ..._, value: Number(T) });
122
+ } else {
123
+ continue routes;
124
+ }
125
+ case 2 /* Param */:
126
+ matched.push({ ...frag, value: part });
101
127
  break;
128
+ case 3 /* Wildcard */:
129
+ matched.push({ ...frag, value: parts.slice(i).join("/") });
130
+ break fragments;
131
+ case 4 /* NumericParam */:
132
+ if (!isNaN(Number(part))) {
133
+ matched.push({ ...frag, value: Number(part) });
134
+ break;
135
+ } else {
136
+ continue routes;
137
+ }
102
138
  default:
103
- throw new Error(`Unknown fragment type: ${_.type}`);
139
+ throw new Error(`Unknown fragment type: ${frag.type}`);
140
+ }
141
+ }
142
+ const params = {};
143
+ for (const frag of matched) {
144
+ if (frag.type === 2 /* Param */) {
145
+ params[frag.name] = decodeURIComponent(frag.value);
146
+ }
147
+ if (frag.type === 4 /* NumericParam */) {
148
+ params[frag.name] = frag.value;
149
+ }
150
+ if (frag.type === 3 /* Wildcard */) {
151
+ params.wildcard = "/" + decodeURIComponent(frag.value);
104
152
  }
105
153
  }
106
- const R = {};
107
- for (const c of $)
108
- c.type === 2 && (R[c.name] = decodeURIComponent(c.value)), c.type === 4 && (R[c.name] = c.value), c.type === 3 && (R.wildcard = "/" + decodeURIComponent(c.value));
109
154
  return {
110
- path: "/" + $.map((c) => c.value).join("/"),
111
- pattern: "/" + l.map((c) => c.type === 2 ? `{${c.name}}` : c.type === 4 ? `{#${c.name}}` : c.name).join("/"),
112
- params: R,
113
- query: ot(n),
114
- meta: u.meta
155
+ path: "/" + matched.map((f) => f.value).join("/"),
156
+ pattern: "/" + fragments.map((f) => {
157
+ if (f.type === 2 /* Param */) {
158
+ return `{${f.name}}`;
159
+ }
160
+ if (f.type === 4 /* NumericParam */) {
161
+ return `{#${f.name}}`;
162
+ }
163
+ return f.name;
164
+ }).join("/"),
165
+ params,
166
+ query: parseQueryParams(query),
167
+ meta: route.meta
115
168
  };
116
169
  }
117
170
  }
118
- function lt(i) {
119
- const e = [], t = [], r = [], n = [];
120
- for (const o of i) {
121
- const { fragments: u } = o;
122
- u.some(
123
- (l) => l.type === 3
124
- /* Wildcard */
125
- ) ? n.push(o) : u.some(
126
- (l) => l.type === 4
127
- /* NumericParam */
128
- ) ? t.push(o) : u.some(
129
- (l) => l.type === 2
130
- /* Param */
131
- ) ? r.push(o) : e.push(o);
132
- }
133
- const s = (o, u) => o.fragments.length > u.fragments.length ? -1 : 1;
134
- return e.sort(s), t.sort(s), r.sort(s), n.sort(s), [...e, ...t, ...r, ...n];
171
+ function sortRoutes(routes) {
172
+ const withoutParams = [];
173
+ const withNumericParams = [];
174
+ const withParams = [];
175
+ const wildcard = [];
176
+ for (const route of routes) {
177
+ const { fragments } = route;
178
+ if (fragments.some((f) => f.type === 3 /* Wildcard */)) {
179
+ wildcard.push(route);
180
+ } else if (fragments.some((f) => f.type === 4 /* NumericParam */)) {
181
+ withNumericParams.push(route);
182
+ } else if (fragments.some((f) => f.type === 2 /* Param */)) {
183
+ withParams.push(route);
184
+ } else {
185
+ withoutParams.push(route);
186
+ }
187
+ }
188
+ const bySizeDesc = (a, b) => {
189
+ if (a.fragments.length > b.fragments.length) {
190
+ return -1;
191
+ } else {
192
+ return 1;
193
+ }
194
+ };
195
+ withoutParams.sort(bySizeDesc);
196
+ withNumericParams.sort(bySizeDesc);
197
+ withParams.sort(bySizeDesc);
198
+ wildcard.sort(bySizeDesc);
199
+ return [...withoutParams, ...withNumericParams, ...withParams, ...wildcard];
135
200
  }
136
- function ct(i) {
137
- const e = X(i), t = [];
138
- for (let r = 0; r < e.length; r++) {
139
- const n = e[r];
140
- if (n === "*") {
141
- if (r !== e.length - 1)
142
- throw new Error(`Wildcard must be at the end of a pattern. Received: ${i}`);
143
- t.push({
144
- type: 3,
201
+ function patternToFragments(pattern) {
202
+ const parts = splitPath(pattern);
203
+ const fragments = [];
204
+ for (let i = 0; i < parts.length; i++) {
205
+ const part = parts[i];
206
+ if (part === "*") {
207
+ if (i !== parts.length - 1) {
208
+ throw new Error(`Wildcard must be at the end of a pattern. Received: ${pattern}`);
209
+ }
210
+ fragments.push({
211
+ type: 3 /* Wildcard */,
145
212
  name: "*",
146
213
  value: null
147
214
  });
148
- } else n.at(0) === "{" && n.at(-1) === "}" ? t.push({
149
- type: n[1] === "#" ? 4 : 2,
150
- name: n[1] === "#" ? n.slice(2, -1) : n.slice(1, -1),
151
- value: null
152
- }) : t.push({
153
- type: 1,
154
- name: n,
155
- value: n
156
- });
215
+ } else if (part.at(0) === "{" && part.at(-1) === "}") {
216
+ fragments.push({
217
+ type: part[1] === "#" ? 4 /* NumericParam */ : 2 /* Param */,
218
+ name: part[1] === "#" ? part.slice(2, -1) : part.slice(1, -1),
219
+ value: null
220
+ });
221
+ } else {
222
+ fragments.push({
223
+ type: 1 /* Literal */,
224
+ name: part,
225
+ value: part
226
+ });
227
+ }
157
228
  }
158
- return t;
229
+ return fragments;
159
230
  }
160
- function Lt(i) {
161
- return new ft(i);
231
+
232
+ function createRouter(options) {
233
+ return new Router(options);
162
234
  }
163
- const Ve = Symbol.for("DollaRouterMountMethod"), qe = Symbol.for("DollaRouterUnmountMethod");
164
- function ge(i) {
165
- return (i == null ? void 0 : i[je]) === !0;
235
+ const ROUTER_MOUNT = Symbol.for("DollaRouterMountMethod");
236
+ const ROUTER_UNMOUNT = Symbol.for("DollaRouterUnmountMethod");
237
+ function _isRouter(value) {
238
+ return value?.[IS_ROUTER] === true;
166
239
  }
167
- async function ht(i, e) {
168
- return i[Ve](e);
240
+ async function _mountRouter(router, dolla) {
241
+ return router[ROUTER_MOUNT](dolla);
169
242
  }
170
- async function ut(i) {
171
- return i[qe]();
243
+ async function _unmountRouter(router) {
244
+ return router[ROUTER_UNMOUNT]();
172
245
  }
173
- var Oe, H, x, fe, N, z, Y, D, I, P, m, ye, Ge, We, J, Ae, be, ve;
174
- class ft {
175
- constructor(e) {
176
- h(this, m);
177
- p(this, Oe, !0);
178
- h(this, H);
179
- h(this, x);
180
- h(this, fe, 0);
181
- h(this, N, []);
182
- h(this, z, []);
183
- h(this, Y, !1);
184
- /**
185
- * Use hash routing when true. Configured in router options.
186
- */
187
- h(this, D, !1);
188
- // Callbacks that need to be called on unmount.
189
- h(this, I, []);
190
- /**
191
- * The current match object.
192
- */
193
- h(this, P, Ce());
194
- /**
195
- * The currently matched route pattern, if any.
196
- */
197
- p(this, "pattern", C(() => {
198
- var e;
199
- return (e = E(a(this, P))) == null ? void 0 : e.pattern;
200
- }));
201
- /**
202
- * The current URL path.
203
- */
204
- p(this, "path", C(() => {
205
- var e;
206
- return ((e = E(a(this, P))) == null ? void 0 : e.path) ?? window.location.pathname;
207
- }));
208
- /**
209
- * The current named path params.
210
- */
211
- p(this, "params", C(() => {
212
- var e;
213
- return ((e = E(a(this, P))) == null ? void 0 : e.params) ?? {};
214
- }, { equals: Me }));
215
- /**
216
- * The current query params. Changes to this object will be reflected in the URL.
217
- */
218
- p(this, "query", C(() => {
219
- var e;
220
- return ((e = E(a(this, P))) == null ? void 0 : e.query) ?? {};
221
- }, { equals: Me }));
222
- Ye(e, "Options must be an object. Got: %t"), e.hash && d(this, D, !0), d(this, z, lt(
223
- e.routes.flatMap((t) => f(this, m, ve).call(this, t)).map((t) => ({
224
- pattern: t.pattern,
225
- meta: t.meta,
226
- fragments: ct(t.pattern)
246
+ class Router {
247
+ [IS_ROUTER] = true;
248
+ #dolla;
249
+ #logger;
250
+ #layerId = 0;
251
+ #activeLayers = [];
252
+ #routes = [];
253
+ #isMounted = false;
254
+ /**
255
+ * Use hash routing when true. Configured in router options.
256
+ */
257
+ #hash = false;
258
+ // Callbacks that need to be called on unmount.
259
+ #unsubscribers = [];
260
+ /**
261
+ * The current match object.
262
+ */
263
+ #match = atom();
264
+ /**
265
+ * The currently matched route pattern, if any.
266
+ */
267
+ pattern = compose(() => get(this.#match)?.pattern);
268
+ /**
269
+ * The current URL path.
270
+ */
271
+ path = compose(() => get(this.#match)?.path ?? window.location.pathname);
272
+ /**
273
+ * The current named path params.
274
+ */
275
+ params = compose(() => get(this.#match)?.params ?? {}, { equals: shallowEqual });
276
+ /**
277
+ * The current query params. Changes to this object will be reflected in the URL.
278
+ */
279
+ query = compose(() => get(this.#match)?.query ?? {}, { equals: shallowEqual });
280
+ constructor(options) {
281
+ assertObject(options, "Options must be an object. Got: %t");
282
+ if (options.hash) {
283
+ this.#hash = true;
284
+ }
285
+ this.#routes = sortRoutes(
286
+ options.routes.flatMap((route) => this.#prepareRoute(route)).map((route) => ({
287
+ pattern: route.pattern,
288
+ meta: route.meta,
289
+ fragments: patternToFragments(route.pattern)
227
290
  }))
228
- )), gt(a(this, z));
291
+ );
292
+ assertValidRedirects(this.#routes);
229
293
  }
230
- async [(Oe = je, Ve)](e) {
231
- d(this, H, e), d(this, x, e.createLogger("Dolla.router"));
232
- const t = () => {
233
- f(this, m, J).call(this);
294
+ async [ROUTER_MOUNT](dolla) {
295
+ this.#dolla = dolla;
296
+ this.#logger = dolla.createLogger("Dolla.router");
297
+ const onPopState = () => {
298
+ this.#updateRoute();
234
299
  };
235
- window.addEventListener("popstate", t), a(this, I).push(() => window.removeEventListener("popstate", t));
236
- const r = e.getRootElement();
237
- a(this, I).push(
238
- wt(r, (n) => {
239
- let s = n.getAttribute("href");
240
- a(this, x).info("intercepted click on <a> tag", n), /^https?:\/\/|^\//.test(s) || (s = A([window.location.pathname, s])), f(this, m, ye).call(this, s);
300
+ window.addEventListener("popstate", onPopState);
301
+ this.#unsubscribers.push(() => window.removeEventListener("popstate", onPopState));
302
+ const rootElement = dolla.getRootElement();
303
+ this.#unsubscribers.push(
304
+ catchLinks(rootElement, (anchor) => {
305
+ let href = anchor.getAttribute("href");
306
+ this.#logger.info("intercepted click on <a> tag", anchor);
307
+ if (!/^https?:\/\/|^\//.test(href)) {
308
+ href = joinPath([window.location.pathname, href]);
309
+ }
310
+ this.#push(href);
241
311
  })
242
- ), a(this, x).info("will intercept clicks on <a> tags within root element", r), d(this, Y, !0), await f(this, m, J).call(this);
312
+ );
313
+ this.#logger.info("will intercept clicks on <a> tags within root element", rootElement);
314
+ this.#isMounted = true;
315
+ await this.#updateRoute();
243
316
  }
244
- async [qe]() {
245
- for (const e of a(this, I))
246
- e();
247
- d(this, I, []);
317
+ async [ROUTER_UNMOUNT]() {
318
+ for (const callback of this.#unsubscribers) {
319
+ callback();
320
+ }
321
+ this.#unsubscribers = [];
248
322
  }
249
323
  /**
250
324
  * Navigate backward. Pass a number of steps to hit the back button that many times.
251
325
  */
252
- back(e = 1) {
253
- window.history.go(-e);
326
+ back(steps = 1) {
327
+ window.history.go(-steps);
254
328
  }
255
329
  /**
256
330
  * Navigate forward. Pass a number of steps to hit the forward button that many times.
257
331
  */
258
- forward(e = 1) {
259
- window.history.go(e);
332
+ forward(steps = 1) {
333
+ window.history.go(steps);
260
334
  }
261
335
  /**
262
336
  * Navigates to another route.
@@ -265,499 +339,741 @@ class ft {
265
339
  * router.go("/login"); // navigate to `/login`
266
340
  * router.go["/users", 215], { replace: true }); // replace current history entry with `/users/215`
267
341
  */
268
- go(e, t = {}) {
269
- if (a(this, H) == null)
270
- throw new Error("Routa methods won't work until you register it: Dolla.use(Routa, { /* ...options */ })");
271
- let r;
272
- Array.isArray(e) ? r = A(e) : r = e.toString(), r = ue(window.location.pathname, r), t.preserveQuery && (r += window.location.search), t.replace ? f(this, m, Ge).call(this, r) : f(this, m, ye).call(this, r);
342
+ go(path, options = {}) {
343
+ let joined;
344
+ if (Array.isArray(path)) {
345
+ joined = joinPath(path);
346
+ } else {
347
+ joined = path.toString();
348
+ }
349
+ joined = resolvePath(window.location.pathname, joined);
350
+ if (options.preserveQuery) {
351
+ joined += window.location.search;
352
+ }
353
+ if (options.replace) {
354
+ this.#replace(joined);
355
+ } else {
356
+ this.#push(joined);
357
+ }
273
358
  }
274
- }
275
- H = new WeakMap(), x = new WeakMap(), fe = new WeakMap(), N = new WeakMap(), z = new WeakMap(), Y = new WeakMap(), D = new WeakMap(), I = new WeakMap(), P = new WeakMap(), m = new WeakSet(), ye = function(e, t) {
276
- var r;
277
- (r = a(this, x)) == null || r.info("(push)", e), window.history.pushState(t, "", a(this, D) ? "/#" + e : e), f(this, m, J).call(this, e);
278
- }, Ge = function(e, t) {
279
- var r;
280
- (r = a(this, x)) == null || r.info("(replace)", e), window.history.replaceState(t, "", a(this, D) ? "/#" + e : e), f(this, m, J).call(this, e);
281
- }, We = function() {
282
- return a(this, D) ? new URL(window.location.hash.slice(1), window.location.origin) : new URL(window.location.pathname, window.location.origin);
283
- }, J = async function(e) {
284
- var u;
285
- const t = a(this, x), r = (u = a(this, H)) == null ? void 0 : u.getRootView(), n = e ? new URL(e, window.location.origin) : f(this, m, We).call(this), { match: s, journey: o } = await f(this, m, be).call(this, n);
286
- for (const l of o)
287
- switch (l.kind) {
288
- case "match":
289
- t == null || t.info(`📍 ${l.message}`);
290
- break;
291
- case "redirect":
292
- t == null || t.info(`↩️ ${l.message}`);
293
- break;
294
- case "miss":
295
- t == null || t.info(`💀 ${l.message}`);
296
- break;
297
- }
298
- if (s) {
299
- const l = this.pattern.value;
300
- a(this, P).value = s, r && s.pattern !== l && f(this, m, Ae).call(this, r, s);
301
- } else
302
- a(this, Y) && t.crash(new yt(`Failed to match route '${n.pathname}'`));
303
- return { match: s, journey: o };
304
- }, /**
305
- * Takes a matched route and mounts it.
306
- */
307
- Ae = function(e, t) {
308
- const r = t.meta.layers;
309
- for (let n = 0; n < r.length; n++) {
310
- const s = r[n], o = a(this, N)[n];
311
- if ((o == null ? void 0 : o.id) !== s.id) {
312
- d(this, N, a(this, N).slice(0, n)), o == null || o.view.unmount();
313
- const u = a(this, N).at(-1), w = ((u == null ? void 0 : u.view) ?? e).setChildView(s.view);
314
- a(this, N).push({ id: s.id, view: w });
315
- }
316
- }
317
- }, be = async function(e, t = []) {
318
- const r = Ie(a(this, z), e.pathname);
319
- if (!r)
320
- return {
321
- match: null,
322
- journey: [...t, { kind: "miss", message: `no match for '${e.pathname}'` }]
323
- };
324
- let n = r.meta.redirect;
325
- if (r.meta.beforeMatch && await r.meta.beforeMatch({
326
- // TODO: Allow setting context variables from here? Would apply to the context of the matched view.
327
- redirect: (s) => {
328
- n = s;
329
- }
330
- }), n != null) {
331
- let s;
332
- if (k(n))
333
- s = pt(n, r.params);
334
- else if (Q(n)) {
335
- const o = {
336
- path: r.path,
337
- pattern: r.pattern,
338
- params: r.params,
339
- query: r.query
359
+ #push(href, state) {
360
+ this.#logger?.info("(push)", href);
361
+ window.history.pushState(state, "", this.#hash ? "/#" + href : href);
362
+ this.#updateRoute(href);
363
+ }
364
+ #replace(href, state) {
365
+ this.#logger?.info("(replace)", href);
366
+ window.history.replaceState(state, "", this.#hash ? "/#" + href : href);
367
+ this.#updateRoute(href);
368
+ }
369
+ #getCurrentURL() {
370
+ if (this.#hash) {
371
+ return new URL(window.location.hash.slice(1), window.location.origin);
372
+ } else {
373
+ return new URL(window.location.pathname, window.location.origin);
374
+ }
375
+ }
376
+ /**
377
+ * Run when the location changes. Diffs and mounts new routes and updates
378
+ * the $path, $route, $params and $query states accordingly.
379
+ */
380
+ async #updateRoute(href) {
381
+ const logger = this.#logger;
382
+ const rootView = this.#dolla?.getRootView();
383
+ const url = href ? new URL(href, window.location.origin) : this.#getCurrentURL();
384
+ const { match, journey } = await this.#resolveRoute(url);
385
+ if (match) {
386
+ const oldPattern = this.pattern.value;
387
+ this.#match.value = match;
388
+ if (rootView && match.pattern !== oldPattern) {
389
+ this.#mountRoute(rootView, match);
390
+ }
391
+ } else {
392
+ if (this.#isMounted) {
393
+ logger.crash(new NoRouteError(`Failed to match route '${url.pathname}'`));
394
+ }
395
+ }
396
+ return { match, journey };
397
+ }
398
+ /**
399
+ * Takes a matched route and mounts it.
400
+ */
401
+ #mountRoute(rootView, match) {
402
+ const layers = match.meta.layers;
403
+ for (let i = 0; i < layers.length; i++) {
404
+ const matchedLayer = layers[i];
405
+ const activeLayer = this.#activeLayers[i];
406
+ if (activeLayer?.id !== matchedLayer.id) {
407
+ this.#activeLayers = this.#activeLayers.slice(0, i);
408
+ activeLayer?.view.unmount();
409
+ const parentLayer = this.#activeLayers.at(-1);
410
+ const parent2 = parentLayer?.view ?? rootView;
411
+ const view = parent2.setChildView(matchedLayer.view);
412
+ this.#activeLayers.push({ id: matchedLayer.id, view });
413
+ }
414
+ }
415
+ }
416
+ /**
417
+ * Takes a URL and finds a match, following redirects.
418
+ */
419
+ async #resolveRoute(url, journey = []) {
420
+ const match = matchRoutes(this.#routes, url.pathname);
421
+ if (!match) {
422
+ return {
423
+ match: null,
424
+ journey: [...journey, { kind: "miss", message: `no match for '${url.pathname}'` }]
340
425
  };
341
- if (s = await n(o), !k(s))
342
- throw new Error("Redirect function must return a path to redirect to.");
343
- s.startsWith("/") || (s = ue(r.path, s));
344
- } else
345
- throw new TypeError("Redirect must either be a path string or a function.");
346
- return f(this, m, be).call(this, new URL(s, window.location.origin), [
347
- ...t,
348
- { kind: "redirect", message: `redirecting '${r.path}' -> '${s}'` }
349
- ]);
350
- } else
351
- return { match: r, journey: [...t, { kind: "match", message: `matched route '${r.path}'` }] };
352
- }, /**
353
- * Parses a route definition object into a set of matchable routes.
354
- *
355
- * @param route - Route config object.
356
- * @param layers - Array of parent layers. Passed when this function calls itself on nested routes.
357
- */
358
- ve = function(e, t = [], r = []) {
359
- if (!G(e) || !k(e.path))
360
- throw new TypeError(`Route configs must be objects with a 'path' string property. Got: ${e}`);
361
- if (e.redirect && e.routes)
362
- throw new Error("Route cannot have both a 'redirect' and nested 'routes'.");
363
- if (e.redirect && e.view)
364
- throw new Error("Route cannot have both a 'redirect' and a 'view'.");
365
- if (!e.view && !e.routes && !e.redirect)
366
- throw new Error("Route must have a 'view', a 'redirect', or a set of nested 'routes'.");
367
- let n = [];
368
- for (const l of t)
369
- n.push(...X(l.path));
370
- n.push(...X(e.path)), n[n.length - 1] === "*" && n.pop();
371
- const s = [];
372
- if (e.redirect) {
373
- let l = e.redirect;
374
- return k(l) && (l = ue(A(n), l), l.startsWith("/") || (l = "/" + l)), s.push({
375
- pattern: "/" + A([...n, ...X(e.path)]),
376
- meta: {
377
- redirect: l
426
+ }
427
+ let redirect = match.meta.redirect;
428
+ if (match.meta.beforeMatch) {
429
+ await match.meta.beforeMatch({
430
+ // TODO: Allow setting context variables from here? Would apply to the context of the matched view.
431
+ redirect: (path) => {
432
+ redirect = path;
433
+ }
434
+ });
435
+ }
436
+ if (redirect != null) {
437
+ let path;
438
+ if (isString(redirect)) {
439
+ path = replaceParams(redirect, match.params);
440
+ } else if (isFunction(redirect)) {
441
+ const redirectContext = {
442
+ path: match.path,
443
+ pattern: match.pattern,
444
+ params: match.params,
445
+ query: match.query
446
+ };
447
+ path = await redirect(redirectContext);
448
+ if (!isString(path)) {
449
+ throw new Error(`Redirect function must return a path to redirect to.`);
450
+ }
451
+ if (!path.startsWith("/")) {
452
+ path = resolvePath(match.path, path);
453
+ }
454
+ } else {
455
+ throw new TypeError(`Redirect must either be a path string or a function.`);
456
+ }
457
+ return this.#resolveRoute(new URL(path, window.location.origin), [
458
+ ...journey,
459
+ { kind: "redirect", message: `redirecting '${match.path}' -> '${path}'` }
460
+ ]);
461
+ } else {
462
+ return { match, journey: [...journey, { kind: "match", message: `matched route '${match.path}'` }] };
463
+ }
464
+ }
465
+ /**
466
+ * Parses a route definition object into a set of matchable routes.
467
+ *
468
+ * @param route - Route config object.
469
+ * @param layers - Array of parent layers. Passed when this function calls itself on nested routes.
470
+ */
471
+ #prepareRoute(route, parents = [], layers = []) {
472
+ if (!isObject(route) || !isString(route.path)) {
473
+ throw new TypeError(`Route configs must be objects with a 'path' string property. Got: ${route}`);
474
+ }
475
+ if (route.redirect && route.routes) {
476
+ throw new Error(`Route cannot have both a 'redirect' and nested 'routes'.`);
477
+ } else if (route.redirect && route.view) {
478
+ throw new Error(`Route cannot have both a 'redirect' and a 'view'.`);
479
+ } else if (!route.view && !route.routes && !route.redirect) {
480
+ throw new Error(`Route must have a 'view', a 'redirect', or a set of nested 'routes'.`);
481
+ }
482
+ let parts = [];
483
+ for (const parent2 of parents) {
484
+ parts.push(...splitPath(parent2.path));
485
+ }
486
+ parts.push(...splitPath(route.path));
487
+ if (parts[parts.length - 1] === "*") {
488
+ parts.pop();
489
+ }
490
+ const routes = [];
491
+ if (route.redirect) {
492
+ let redirect = route.redirect;
493
+ if (isString(redirect)) {
494
+ redirect = resolvePath(joinPath(parts), redirect);
495
+ if (!redirect.startsWith("/")) {
496
+ redirect = "/" + redirect;
497
+ }
378
498
  }
379
- }), s;
380
- }
381
- let o = De;
382
- if (Q(e.view))
383
- o = e.view;
384
- else if (e.view)
385
- throw new TypeError(`Route '${e.path}' expected a view function or undefined. Got: ${e.view}`);
386
- const u = { id: Se(this, fe)._++, view: o };
387
- if (e.routes)
388
- for (const l of e.routes)
389
- s.push(...f(this, m, ve).call(this, l, [...t, e], [...r, u]));
390
- else
391
- s.push({
392
- pattern: parent ? A([...t.map((l) => l.path), e.path]) : e.path,
393
- meta: {
394
- pattern: e.path,
395
- layers: [...r, u],
396
- beforeMatch: e.beforeMatch
499
+ routes.push({
500
+ pattern: "/" + joinPath([...parts, ...splitPath(route.path)]),
501
+ meta: {
502
+ redirect
503
+ }
504
+ });
505
+ return routes;
506
+ }
507
+ let view = Passthrough;
508
+ if (isFunction(route.view)) {
509
+ view = route.view;
510
+ } else if (route.view) {
511
+ throw new TypeError(`Route '${route.path}' expected a view function or undefined. Got: ${route.view}`);
512
+ }
513
+ const layer = { id: this.#layerId++, view };
514
+ if (route.routes) {
515
+ for (const subroute of route.routes) {
516
+ routes.push(...this.#prepareRoute(subroute, [...parents, route], [...layers, layer]));
397
517
  }
398
- });
399
- return s;
400
- };
401
- const dt = /(noopener|noreferrer) (noopener|noreferrer)/, mt = /^[\w-_]+:/;
402
- function wt(i, e, t = window) {
403
- function r(s) {
404
- return !s || s === i ? null : s.localName !== "a" || s.href === void 0 ? r(s.parentNode) : s;
405
- }
406
- function n(s) {
407
- if (s.button && s.button !== 0 || s.ctrlKey || s.metaKey || s.altKey || s.shiftKey || s.defaultPrevented)
518
+ } else {
519
+ routes.push({
520
+ pattern: parent ? joinPath([...parents.map((p) => p.path), route.path]) : route.path,
521
+ meta: {
522
+ pattern: route.path,
523
+ layers: [...layers, layer],
524
+ beforeMatch: route.beforeMatch
525
+ }
526
+ });
527
+ }
528
+ return routes;
529
+ }
530
+ }
531
+ const safeExternalLink = /(noopener|noreferrer) (noopener|noreferrer)/;
532
+ const protocolLink = /^[\w-_]+:/;
533
+ function catchLinks(root, callback, _window = window) {
534
+ function traverse(node) {
535
+ if (!node || node === root) {
536
+ return null;
537
+ }
538
+ if (node.localName !== "a" || node.href === void 0) {
539
+ return traverse(node.parentNode);
540
+ }
541
+ return node;
542
+ }
543
+ function handler(e) {
544
+ if (e.button && e.button !== 0 || e.ctrlKey || e.metaKey || e.altKey || e.shiftKey || e.defaultPrevented) {
408
545
  return;
409
- const o = r(s.target);
410
- o && (t.location.protocol !== o.protocol || t.location.hostname !== o.hostname || t.location.port !== o.port || o.hasAttribute("data-router-ignore") || o.hasAttribute("download") || o.getAttribute("target") === "_blank" && dt.test(o.getAttribute("rel")) || mt.test(o.getAttribute("href")) || (s.preventDefault(), e(o)));
546
+ }
547
+ const anchor = traverse(e.target);
548
+ if (!anchor) {
549
+ return;
550
+ }
551
+ if (_window.location.protocol !== anchor.protocol || _window.location.hostname !== anchor.hostname || _window.location.port !== anchor.port || anchor.hasAttribute("data-router-ignore") || anchor.hasAttribute("download") || anchor.getAttribute("target") === "_blank" && safeExternalLink.test(anchor.getAttribute("rel")) || protocolLink.test(anchor.getAttribute("href"))) {
552
+ return;
553
+ }
554
+ e.preventDefault();
555
+ callback(anchor);
411
556
  }
412
- return i.addEventListener("click", n), function() {
413
- i.removeEventListener("click", n);
557
+ root.addEventListener("click", handler);
558
+ return function cancel() {
559
+ root.removeEventListener("click", handler);
414
560
  };
415
561
  }
416
- function pt(i, e) {
417
- for (const t in e) {
418
- const r = e[t].toString();
419
- i = i.replace(`{${t}}`, r).replace(`{#${t}}`, r);
562
+ function replaceParams(path, params) {
563
+ for (const key in params) {
564
+ const value = params[key].toString();
565
+ path = path.replace(`{${key}}`, value).replace(`{#${key}}`, value);
420
566
  }
421
- return i;
567
+ return path;
422
568
  }
423
- function gt(i) {
424
- for (const e of i)
425
- if (e.meta.redirect) {
426
- let t;
427
- if (!Q(e.meta.redirect)) if (k(e.meta.redirect)) {
428
- if (t = e.meta.redirect, !Ie(i, t, {
429
- willMatch(n) {
430
- return n !== e;
569
+ function assertValidRedirects(routes) {
570
+ for (const route of routes) {
571
+ if (route.meta.redirect) {
572
+ let redirectPath;
573
+ if (isFunction(route.meta.redirect)) ; else if (isString(route.meta.redirect)) {
574
+ redirectPath = route.meta.redirect;
575
+ const match = matchRoutes(routes, redirectPath, {
576
+ willMatch(r) {
577
+ return r !== route;
431
578
  }
432
- }))
433
- throw new Error(`Found a redirect to an undefined URL. From '${e.pattern}' to '${e.meta.redirect}'`);
434
- } else
435
- throw new TypeError(`Expected a string or redirect function. Got: ${e.meta.redirect}`);
579
+ });
580
+ if (!match) {
581
+ throw new Error(`Found a redirect to an undefined URL. From '${route.pattern}' to '${route.meta.redirect}'`);
582
+ }
583
+ } else {
584
+ throw new TypeError(`Expected a string or redirect function. Got: ${route.meta.redirect}`);
585
+ }
436
586
  }
587
+ }
437
588
  }
438
- class yt extends Error {
589
+ class NoRouteError extends Error {
439
590
  }
440
- var V, de, S, M;
441
- class bt {
442
- // #dolla: Dolla;
443
- // #logger: Logger;
444
- constructor() {
445
- h(this, S);
446
- h(this, V, []);
447
- h(this, de, vt());
448
- }
591
+
592
+ class HTTP {
593
+ #middleware = [];
594
+ #fetch = getDefaultFetch();
449
595
  /**
450
596
  * Adds a new middleware that will apply to subsequent requests.
451
597
  * Returns a function to remove this middleware.
452
598
  *
453
599
  * @param middleware - A middleware function that will intercept requests.
454
600
  */
455
- use(e) {
456
- return a(this, V).push(e), () => {
457
- a(this, V).splice(a(this, V).indexOf(e), 1);
601
+ use(fn) {
602
+ this.#middleware.push(fn);
603
+ return () => {
604
+ this.#middleware.splice(this.#middleware.indexOf(fn), 1);
458
605
  };
459
606
  }
460
- async get(e, t) {
461
- return f(this, S, M).call(this, "get", e, t);
607
+ async get(uri, options) {
608
+ return this.#request("get", uri, options);
462
609
  }
463
- async put(e, t) {
464
- return f(this, S, M).call(this, "put", e, t);
610
+ async put(uri, options) {
611
+ return this.#request("put", uri, options);
465
612
  }
466
- async patch(e, t) {
467
- return f(this, S, M).call(this, "patch", e, t);
613
+ async patch(uri, options) {
614
+ return this.#request("patch", uri, options);
468
615
  }
469
- async post(e, t) {
470
- return f(this, S, M).call(this, "post", e, t);
616
+ async post(uri, options) {
617
+ return this.#request("post", uri, options);
471
618
  }
472
- async delete(e, t) {
473
- return f(this, S, M).call(this, "delete", e, t);
619
+ async delete(uri, options) {
620
+ return this.#request("delete", uri, options);
474
621
  }
475
- async head(e, t) {
476
- return f(this, S, M).call(this, "head", e, t);
622
+ async head(uri, options) {
623
+ return this.#request("head", uri, options);
477
624
  }
478
- async options(e, t) {
479
- return f(this, S, M).call(this, "options", e, t);
625
+ async options(uri, options) {
626
+ return this.#request("options", uri, options);
480
627
  }
481
- async trace(e, t) {
482
- return f(this, S, M).call(this, "trace", e, t);
628
+ async trace(uri, options) {
629
+ return this.#request("trace", uri, options);
630
+ }
631
+ async #request(method, uri, options) {
632
+ const runner = new Runner({
633
+ ...options,
634
+ method,
635
+ uri,
636
+ middleware: this.#middleware,
637
+ fetch: this.#fetch
638
+ });
639
+ return runner.fetch();
483
640
  }
484
641
  }
485
- V = new WeakMap(), de = new WeakMap(), S = new WeakSet(), M = async function(e, t, r) {
486
- return new kt({
487
- ...r,
488
- method: e,
489
- uri: t,
490
- middleware: a(this, V),
491
- fetch: a(this, de)
492
- }).fetch();
493
- };
494
- function vt() {
495
- if (typeof window < "u" && window.fetch)
642
+ function getDefaultFetch() {
643
+ if (typeof window !== "undefined" && window.fetch) {
496
644
  return window.fetch.bind(window);
497
- if (typeof global < "u" && global.fetch)
645
+ }
646
+ if (typeof global !== "undefined" && global.fetch) {
498
647
  return global.fetch.bind(global);
648
+ }
499
649
  throw new Error("Running in neither browser nor node. Please run this app in one of the supported environments.");
500
650
  }
501
- class $t extends Error {
502
- constructor(t) {
503
- const { status: r, statusText: n, method: s, url: o } = t, u = `${r} ${n}: Request failed (${s.toUpperCase()} ${o.toString()})`;
504
- super(u);
505
- p(this, "response");
506
- this.response = t;
651
+ class HTTPResponseError extends Error {
652
+ response;
653
+ constructor(response) {
654
+ const { status, statusText, method, url } = response;
655
+ const message = `${status} ${statusText}: Request failed (${method.toUpperCase()} ${url.toString()})`;
656
+ super(message);
657
+ this.response = response;
507
658
  }
508
659
  }
509
- class Et {
510
- constructor(e) {
511
- p(this, "method");
512
- p(this, "url");
513
- p(this, "headers", new Headers());
514
- p(this, "body");
515
- this.method = e.method, this.body = e.body, e.uri.startsWith("http") ? this.url = new URL(e.uri) : this.url = new URL(e.uri, window.location.origin), this._applyHeaders(e.headers), this._applyQueryParams(e.query);
516
- }
660
+ class Request {
661
+ method;
662
+ url;
663
+ headers = new Headers();
664
+ body;
517
665
  get isSameOrigin() {
518
666
  return this.url.origin === window.location.origin;
519
667
  }
520
- _applyHeaders(e) {
521
- if (e != null)
522
- if (e instanceof Map || e instanceof Headers)
523
- e.forEach((t, r) => {
524
- this.headers.set(r, t);
525
- });
526
- else if (G(e))
527
- for (const t in e) {
528
- const r = e[t];
529
- r instanceof Date ? this.headers.set(t, r.toISOString()) : r != null && this.headers.set(t, String(r));
668
+ constructor(config) {
669
+ this.method = config.method;
670
+ this.body = config.body;
671
+ if (config.uri.startsWith("http")) {
672
+ this.url = new URL(config.uri);
673
+ } else {
674
+ this.url = new URL(config.uri, window.location.origin);
675
+ }
676
+ this._applyHeaders(config.headers);
677
+ this._applyQueryParams(config.query);
678
+ }
679
+ _applyHeaders(headers) {
680
+ if (headers == null) return;
681
+ if (headers instanceof Map || headers instanceof Headers) {
682
+ headers.forEach((value, key) => {
683
+ this.headers.set(key, value);
684
+ });
685
+ } else if (isObject(headers)) {
686
+ for (const name in headers) {
687
+ const value = headers[name];
688
+ if (value instanceof Date) {
689
+ this.headers.set(name, value.toISOString());
690
+ } else if (value != null) {
691
+ this.headers.set(name, String(value));
530
692
  }
531
- else
532
- throw new TypeError(`Unknown headers type. Got: ${e}`);
533
- }
534
- _applyQueryParams(e) {
535
- if (e != null)
536
- if (e instanceof Map || e instanceof URLSearchParams)
537
- e.forEach((t, r) => {
538
- this.url.searchParams.set(r, t);
539
- });
540
- else if (G(e))
541
- for (const t in e) {
542
- const r = e[t];
543
- r instanceof Date ? this.url.searchParams.set(t, r.toISOString()) : r != null && this.url.searchParams.set(t, String(r));
693
+ }
694
+ } else {
695
+ throw new TypeError(`Unknown headers type. Got: ${headers}`);
696
+ }
697
+ }
698
+ _applyQueryParams(query) {
699
+ if (query == null) return;
700
+ if (query instanceof Map || query instanceof URLSearchParams) {
701
+ query.forEach((value, key) => {
702
+ this.url.searchParams.set(key, value);
703
+ });
704
+ } else if (isObject(query)) {
705
+ for (const name in query) {
706
+ const value = query[name];
707
+ if (value instanceof Date) {
708
+ this.url.searchParams.set(name, value.toISOString());
709
+ } else if (value != null) {
710
+ this.url.searchParams.set(name, String(value));
544
711
  }
545
- else
546
- throw new TypeError(`Unknown query params type. Got: ${e}`);
712
+ }
713
+ } else {
714
+ throw new TypeError(`Unknown query params type. Got: ${query}`);
715
+ }
547
716
  }
548
717
  }
549
- class kt {
550
- constructor(e) {
551
- p(this, "_middleware");
552
- p(this, "_fetch");
553
- p(this, "_request");
554
- p(this, "_response");
555
- this._middleware = e.middleware, this._fetch = e.fetch, this._request = new Et(e);
718
+ class Runner {
719
+ _middleware;
720
+ _fetch;
721
+ _request;
722
+ _response;
723
+ constructor(config) {
724
+ this._middleware = config.middleware;
725
+ this._fetch = config.fetch;
726
+ this._request = new Request(config);
556
727
  }
557
728
  async fetch() {
558
729
  if (this._middleware.length > 0) {
559
- const e = (t = 0) => {
560
- const r = this._middleware[t], n = this._middleware[t + 1] ? e(t + 1) : this._handler.bind(this);
561
- return async () => r(this._request, async () => (await n(), this._response));
730
+ const mount = (index = 0) => {
731
+ const current = this._middleware[index];
732
+ const next = this._middleware[index + 1] ? mount(index + 1) : this._handler.bind(this);
733
+ return async () => current(this._request, async () => {
734
+ await next();
735
+ return this._response;
736
+ });
562
737
  };
563
- await e()();
564
- } else
738
+ await mount()();
739
+ } else {
565
740
  await this._handler();
566
- if (this._response.status < 200 || this._response.status >= 400)
567
- throw new $t(this._response);
741
+ }
742
+ if (this._response.status < 200 || this._response.status >= 400) {
743
+ throw new HTTPResponseError(this._response);
744
+ }
568
745
  return this._response;
569
746
  }
570
747
  // This is the function that performs the actual request after the final middleware.
571
748
  async _handler() {
572
- let e;
573
- const t = this._request;
574
- !t.headers.has("content-type") && G(t.body) ? (t.headers.set("content-type", "application/json"), e = JSON.stringify(t.body)) : e = t.body;
575
- const r = await this._fetch(t.url.toString(), {
576
- method: t.method,
577
- headers: t.headers,
578
- body: e
579
- }), n = r.headers.get("content-type");
580
- let s;
581
- n != null && n.includes("application/json") ? s = await r.json() : n != null && n.includes("application/x-www-form-urlencoded") ? s = await r.formData() : s = await r.text(), this._response = {
582
- method: t.method,
583
- url: t.url,
584
- status: r.status,
585
- statusText: r.statusText,
586
- headers: r.headers,
587
- body: s
749
+ let reqBody;
750
+ const req = this._request;
751
+ if (!req.headers.has("content-type") && isObject(req.body)) {
752
+ req.headers.set("content-type", "application/json");
753
+ reqBody = JSON.stringify(req.body);
754
+ } else {
755
+ reqBody = req.body;
756
+ }
757
+ const fetched = await this._fetch(req.url.toString(), {
758
+ method: req.method,
759
+ headers: req.headers,
760
+ body: reqBody
761
+ });
762
+ const contentType = fetched.headers.get("content-type");
763
+ let body;
764
+ if (contentType?.includes("application/json")) {
765
+ body = await fetched.json();
766
+ } else if (contentType?.includes("application/x-www-form-urlencoded")) {
767
+ body = await fetched.formData();
768
+ } else {
769
+ body = await fetched.text();
770
+ }
771
+ this._response = {
772
+ method: req.method,
773
+ url: req.url,
774
+ status: fetched.status,
775
+ statusText: fetched.statusText,
776
+ headers: fetched.headers,
777
+ body
588
778
  };
589
779
  }
590
780
  }
591
- var me, B, W, $e, He;
592
- class Rt {
593
- constructor(e, t) {
594
- h(this, W);
595
- p(this, "dolla");
596
- p(this, "config");
597
- h(this, me, !1);
598
- h(this, B, /* @__PURE__ */ new Map());
599
- this.config = e, this.dolla = t;
781
+
782
+ class Translation {
783
+ dolla;
784
+ config;
785
+ #isLoaded = false;
786
+ #templates = /* @__PURE__ */ new Map();
787
+ constructor(config, dolla) {
788
+ this.config = config;
789
+ this.dolla = dolla;
600
790
  }
601
791
  async load() {
602
- let e;
603
- if (!a(this, me)) {
604
- if (G(this.config.strings))
605
- e = this.config.strings;
606
- else if (Q(this.config.fetch)) {
607
- if (e = await this.config.fetch(), !G(e))
608
- throw new Error(`Fetch function did not return an object of language strings: ${e}`);
609
- } else if (k(this.config.path)) {
610
- const t = await fetch(this.config.path);
611
- if (t.ok) {
612
- const r = await t.json();
613
- if (G(r))
614
- e = r;
615
- else
792
+ let strings;
793
+ if (!this.#isLoaded) {
794
+ if (isObject(this.config.strings)) {
795
+ strings = this.config.strings;
796
+ } else if (isFunction(this.config.fetch)) {
797
+ strings = await this.config.fetch();
798
+ if (!isObject(strings)) {
799
+ throw new Error(`Fetch function did not return an object of language strings: ${strings}`);
800
+ }
801
+ } else if (isString(this.config.path)) {
802
+ const res = await fetch(this.config.path);
803
+ if (res.ok) {
804
+ const body = await res.json();
805
+ if (isObject(body)) {
806
+ strings = body;
807
+ } else {
616
808
  throw new Error(
617
- `Language path '${this.config.path}' did not return an object of language strings: ${r}`
809
+ `Language path '${this.config.path}' did not return an object of language strings: ${body}`
618
810
  );
619
- } else
620
- throw new Error("HTTP request failed.");
811
+ }
812
+ } else {
813
+ throw new Error(`HTTP request failed.`);
814
+ }
815
+ }
816
+ }
817
+ if (strings) {
818
+ const entries = this.#compile(strings);
819
+ for (const entry of entries) {
820
+ this.#templates.set(entry[0], entry[1]);
621
821
  }
822
+ } else {
823
+ throw new Error(`Language could not be loaded.`);
622
824
  }
623
- if (e) {
624
- const t = f(this, W, $e).call(this, e);
625
- for (const r of t)
626
- a(this, B).set(r[0], r[1]);
627
- } else
628
- throw new Error("Language could not be loaded.");
629
825
  }
630
- getTemplate(e) {
631
- return a(this, B).get(e) ?? {
632
- segments: [{ type: 0, text: `[MISSING: ${e}]` }]
826
+ getTemplate(selector) {
827
+ return this.#templates.get(selector) ?? {
828
+ segments: [{ type: 0 /* Static */, text: `[MISSING: ${selector}]` }]
633
829
  };
634
830
  }
635
- hasTemplate(e) {
636
- return a(this, B).has(e);
831
+ hasTemplate(selector) {
832
+ return this.#templates.has(selector);
637
833
  }
638
- }
639
- me = new WeakMap(), B = new WeakMap(), W = new WeakSet(), $e = function(e, t = []) {
640
- const r = [];
641
- for (const n in e)
642
- switch (xe(e[n])) {
643
- case "string":
644
- r.push([[...t, n].join("."), f(this, W, He).call(this, e[n])]);
645
- break;
646
- case "object":
647
- r.push(...f(this, W, $e).call(this, e[n], [...t, n]));
648
- break;
649
- default:
650
- throw new Error(
651
- `Expected to find a string or object at ${[...t, n].join(".")}. Got: ${xe(e[n])}`
652
- );
834
+ #compile(strings, path = []) {
835
+ const entries = [];
836
+ for (const key in strings) {
837
+ switch (typeOf(strings[key])) {
838
+ case "string":
839
+ entries.push([[...path, key].join("."), this.#parseTemplate(strings[key])]);
840
+ break;
841
+ case "object":
842
+ entries.push(...this.#compile(strings[key], [...path, key]));
843
+ break;
844
+ default:
845
+ throw new Error(
846
+ `Expected to find a string or object at ${[...path, key].join(".")}. Got: ${typeOf(strings[key])}`
847
+ );
848
+ }
653
849
  }
654
- return r;
655
- }, He = function(e) {
656
- let t;
657
- ((c) => {
658
- c[c.Static = 0] = "Static", c[c.ValueName = 1] = "ValueName", c[c.FormatName = 2] = "FormatName", c[c.FormatOptionName = 3] = "FormatOptionName", c[c.FormatOptionValue = 4] = "FormatOptionValue", c[c.FormatOptionEnd = 5] = "FormatOptionEnd";
659
- })(t || (t = {}));
660
- const r = {
661
- segments: []
662
- };
663
- let n = "", s = 0, o = 0, u, l, w;
664
- const $ = () => {
665
- u = {
666
- type: 1,
667
- name: "",
668
- formats: []
850
+ return entries;
851
+ }
852
+ #parseTemplate(template) {
853
+ let Loc;
854
+ ((Loc2) => {
855
+ Loc2[Loc2["Static"] = 0] = "Static";
856
+ Loc2[Loc2["ValueName"] = 1] = "ValueName";
857
+ Loc2[Loc2["FormatName"] = 2] = "FormatName";
858
+ Loc2[Loc2["FormatOptionName"] = 3] = "FormatOptionName";
859
+ Loc2[Loc2["FormatOptionValue"] = 4] = "FormatOptionValue";
860
+ Loc2[Loc2["FormatOptionEnd"] = 5] = "FormatOptionEnd";
861
+ })(Loc || (Loc = {}));
862
+ const parsed = {
863
+ segments: []
669
864
  };
670
- }, R = () => {
671
- l = {
672
- name: "",
673
- options: {}
865
+ let buffer = "";
866
+ let i = 0;
867
+ let loc = 0 /* Static */;
868
+ let segment;
869
+ let format;
870
+ let formatOptionName;
871
+ const startSegment = () => {
872
+ segment = {
873
+ type: 1 /* Variable */,
874
+ name: "",
875
+ formats: []
876
+ };
674
877
  };
675
- };
676
- for (; s < e.length; ) {
677
- if (o !== 0 && e[s] === " ") {
678
- s++;
679
- continue;
680
- }
681
- switch (o) {
682
- case 0:
683
- e[s] === "{" && e[s + 1] === "{" ? (o = 1, s += 2, n.length > 0 && (r.segments.push({ type: 0, text: n }), n = ""), $()) : (n += e[s], s++);
684
- break;
685
- case 1:
686
- e[s] === "|" ? (o = 2, s += 1, u.name = n, n = "", R()) : e[s] === "}" && e[s + 1] === "}" ? (o = 0, s += 2, u.name = n, n = "", r.segments.push(u)) : (n += e[s], s++);
687
- break;
688
- case 2:
689
- e[s] === "(" ? (o = 3, s += 1, l.name = n, n = "") : e[s] === "}" && e[s + 1] === "}" ? (o = 0, s += 2, u.formats.push(l), r.segments.push(u)) : (n += e[s], s++);
690
- break;
691
- case 3:
692
- e[s] === ")" || (e[s] === ":" ? (o = 4, s += 1, w = n, n = "") : e[s] === "}" && e[s + 1] === "}" || (n += e[s], s++));
693
- break;
694
- case 4:
695
- e[s] === ")" ? (o = 5, s += 1, l.options[w] = n, n = "", u.formats.push(l)) : e[s] === "," ? (o = 3, s += 1, l.options[w] = n, n = "") : e[s] === "}" && e[s + 1] === "}" || (n += e[s], s++);
696
- break;
697
- case 5:
698
- e[s] === "|" ? (o = 2, s += 1, R()) : e[s] === "}" && e[s + 1] === "}" && (o = 0, s += 2, r.segments.push(u));
699
- break;
700
- }
701
- }
702
- return o === 0 && n.length > 0 && r.segments.push({ type: 0, text: n }), r;
703
- };
704
- var Z, F, y, ee, te, re, v, b, ze, Ee, ke, Re, Be;
705
- class _t {
706
- constructor(e) {
707
- h(this, b);
708
- h(this, Z);
709
- h(this, F);
710
- h(this, y, /* @__PURE__ */ new Map());
711
- h(this, ee, []);
712
- h(this, te, /* @__PURE__ */ new Map());
713
- h(this, re, "auto");
714
- h(this, v, Ce(""));
715
- d(this, Z, e), d(this, F, e.createLogger("Dolla.i18n")), this.addFormat("number", (t, r, n) => f(this, b, Ee).call(this, Number(r), n)), this.addFormat("datetime", (t, r, n) => f(this, b, ke).call(this, r, n)), this.addFormat("list", (t, r, n) => f(this, b, Re).call(this, r, n)), e.beforeMount(async () => {
716
- a(this, y).size > 0 && await this.setLocale(a(this, re));
717
- });
878
+ const startFormat = () => {
879
+ format = {
880
+ name: "",
881
+ options: {}
882
+ };
883
+ };
884
+ while (i < template.length) {
885
+ if (loc !== 0 /* Static */ && template[i] === " ") {
886
+ i++;
887
+ continue;
888
+ }
889
+ switch (loc) {
890
+ case 0 /* Static */:
891
+ if (template[i] === "{" && template[i + 1] === "{") {
892
+ loc = 1 /* ValueName */;
893
+ i += 2;
894
+ if (buffer.length > 0) {
895
+ parsed.segments.push({ type: 0 /* Static */, text: buffer });
896
+ buffer = "";
897
+ }
898
+ startSegment();
899
+ } else {
900
+ buffer += template[i];
901
+ i++;
902
+ }
903
+ break;
904
+ case 1 /* ValueName */:
905
+ if (template[i] === "|") {
906
+ loc = 2 /* FormatName */;
907
+ i += 1;
908
+ segment.name = buffer;
909
+ buffer = "";
910
+ startFormat();
911
+ } else if (template[i] === "}" && template[i + 1] === "}") {
912
+ loc = 0 /* Static */;
913
+ i += 2;
914
+ segment.name = buffer;
915
+ buffer = "";
916
+ parsed.segments.push(segment);
917
+ } else {
918
+ buffer += template[i];
919
+ i++;
920
+ }
921
+ break;
922
+ case 2 /* FormatName */:
923
+ if (template[i] === "(") {
924
+ loc = 3 /* FormatOptionName */;
925
+ i += 1;
926
+ format.name = buffer;
927
+ buffer = "";
928
+ } else if (template[i] === "}" && template[i + 1] === "}") {
929
+ loc = 0 /* Static */;
930
+ i += 2;
931
+ segment.formats.push(format);
932
+ parsed.segments.push(segment);
933
+ } else {
934
+ buffer += template[i];
935
+ i++;
936
+ }
937
+ break;
938
+ case 3 /* FormatOptionName */:
939
+ if (template[i] === ")") ; else if (template[i] === ":") {
940
+ loc = 4 /* FormatOptionValue */;
941
+ i += 1;
942
+ formatOptionName = buffer;
943
+ buffer = "";
944
+ } else if (template[i] === "}" && template[i + 1] === "}") ; else {
945
+ buffer += template[i];
946
+ i++;
947
+ }
948
+ break;
949
+ case 4 /* FormatOptionValue */:
950
+ if (template[i] === ")") {
951
+ loc = 5 /* FormatOptionEnd */;
952
+ i += 1;
953
+ format.options[formatOptionName] = buffer;
954
+ buffer = "";
955
+ segment.formats.push(format);
956
+ } else if (template[i] === ",") {
957
+ loc = 3 /* FormatOptionName */;
958
+ i += 1;
959
+ format.options[formatOptionName] = buffer;
960
+ buffer = "";
961
+ } else if (template[i] === "}" && template[i + 1] === "}") ; else {
962
+ buffer += template[i];
963
+ i++;
964
+ }
965
+ break;
966
+ case 5 /* FormatOptionEnd */:
967
+ if (template[i] === "|") {
968
+ loc = 2 /* FormatName */;
969
+ i += 1;
970
+ startFormat();
971
+ } else if (template[i] === "}" && template[i + 1] === "}") {
972
+ loc = 0 /* Static */;
973
+ i += 2;
974
+ parsed.segments.push(segment);
975
+ } else ;
976
+ break;
977
+ }
978
+ }
979
+ if (loc === 0 /* Static */ && buffer.length > 0) {
980
+ parsed.segments.push({ type: 0 /* Static */, text: buffer });
981
+ }
982
+ return parsed;
718
983
  }
984
+ }
985
+ class I18n {
986
+ #dolla;
987
+ #logger;
988
+ #translations = /* @__PURE__ */ new Map();
989
+ #cache = [];
990
+ #formats = /* @__PURE__ */ new Map();
991
+ #initialLocale = "auto";
992
+ #locale = atom("");
719
993
  get locale() {
720
- return a(this, v);
994
+ return this.#locale;
995
+ }
996
+ constructor(dolla) {
997
+ this.#dolla = dolla;
998
+ this.#logger = dolla.createLogger("Dolla.i18n");
999
+ this.addFormat("number", (_, value, options) => {
1000
+ return this.#formatNumber(Number(value), options);
1001
+ });
1002
+ this.addFormat("datetime", (_, value, options) => {
1003
+ return this.#formatDateTime(value, options);
1004
+ });
1005
+ this.addFormat("list", (_, value, options) => {
1006
+ return this.#formatList(value, options);
1007
+ });
1008
+ dolla.beforeMount(async () => {
1009
+ if (this.#translations.size > 0) {
1010
+ await this.setLocale(this.#initialLocale);
1011
+ }
1012
+ });
721
1013
  }
722
1014
  get locales() {
723
- return [...a(this, y).keys()];
724
- }
725
- setup(e) {
726
- if (e.translations.forEach((t) => {
727
- a(this, y).set(t.locale, new Rt(t, a(this, Z)));
728
- }), e.locale && e.locale !== "auto") {
729
- if (!e.translations.some((r) => r.locale === e.locale))
730
- throw new Error(`Initial locale '${e.locale}' is not registered in the locales array.`);
731
- d(this, re, e.locale);
732
- }
733
- a(this, F).info(
734
- `${a(this, y).size} language${a(this, y).size === 1 ? "" : "s"} supported: '${[...a(this, y).keys()].join("', '")}'`
1015
+ return [...this.#translations.keys()];
1016
+ }
1017
+ setup(options) {
1018
+ options.translations.forEach((entry) => {
1019
+ this.#translations.set(entry.locale, new Translation(entry, this.#dolla));
1020
+ });
1021
+ if (options.locale && options.locale !== "auto") {
1022
+ const isRegistered = options.translations.some((entry) => entry.locale === options.locale);
1023
+ if (!isRegistered) {
1024
+ throw new Error(`Initial locale '${options.locale}' is not registered in the locales array.`);
1025
+ }
1026
+ this.#initialLocale = options.locale;
1027
+ }
1028
+ this.#logger.info(
1029
+ `${this.#translations.size} language${this.#translations.size === 1 ? "" : "s"} supported: '${[...this.#translations.keys()].join("', '")}'`
735
1030
  );
736
1031
  }
737
- async setLocale(e) {
738
- var n;
739
- let t;
740
- if (e === "auto") {
741
- let s = [];
742
- if (typeof navigator < "u") {
743
- const o = navigator;
744
- ((n = o.languages) == null ? void 0 : n.length) > 0 ? s.push(...o.languages) : o.language ? s.push(o.language) : o.browserLanguage ? s.push(o.browserLanguage) : o.userLanguage && s.push(o.userLanguage);
1032
+ async setLocale(name) {
1033
+ let realName;
1034
+ if (name === "auto") {
1035
+ let names = [];
1036
+ if (typeof navigator !== "undefined") {
1037
+ const nav = navigator;
1038
+ if (nav.languages?.length > 0) {
1039
+ names.push(...nav.languages);
1040
+ } else if (nav.language) {
1041
+ names.push(nav.language);
1042
+ } else if (nav.browserLanguage) {
1043
+ names.push(nav.browserLanguage);
1044
+ } else if (nav.userLanguage) {
1045
+ names.push(nav.userLanguage);
1046
+ }
1047
+ }
1048
+ for (const name2 of names) {
1049
+ if (this.#translations.has(name2)) {
1050
+ realName = name2;
1051
+ }
1052
+ }
1053
+ } else {
1054
+ if (this.#translations.has(name)) {
1055
+ realName = name;
745
1056
  }
746
- for (const o of s)
747
- a(this, y).has(o) && (t = o);
748
- } else
749
- a(this, y).has(e) && (t = e);
750
- if (t == null) {
751
- const s = a(this, y).keys().next().value;
752
- s && (t = s);
753
- }
754
- if (!t || !a(this, y).has(t))
755
- throw new Error(`Locale '${e}' has no translation.`);
756
- const r = a(this, y).get(t);
1057
+ }
1058
+ if (realName == null) {
1059
+ const firstLanguage = this.#translations.keys().next().value;
1060
+ if (firstLanguage) {
1061
+ realName = firstLanguage;
1062
+ }
1063
+ }
1064
+ if (!realName || !this.#translations.has(realName)) {
1065
+ throw new Error(`Locale '${name}' has no translation.`);
1066
+ }
1067
+ const translation = this.#translations.get(realName);
757
1068
  try {
758
- await r.load(), d(this, ee, []), a(this, v).value = t, a(this, F).info("set language to " + t);
759
- } catch (s) {
760
- s instanceof Error && a(this, F).crash(s);
1069
+ await translation.load();
1070
+ this.#cache = [];
1071
+ this.#locale.value = realName;
1072
+ this.#logger.info("set language to " + realName);
1073
+ } catch (error) {
1074
+ if (error instanceof Error) {
1075
+ this.#logger.crash(error);
1076
+ }
761
1077
  }
762
1078
  }
763
1079
  /**
@@ -769,18 +1085,71 @@ class _t {
769
1085
  * @example
770
1086
  * const $value = t("your.key.here", { count: 5 });
771
1087
  */
772
- t(e, t) {
773
- if (this === void 0)
1088
+ t(selector, options) {
1089
+ if (this === void 0) {
774
1090
  throw new Error(
775
1091
  `The 't' function cannot be destructured. If you need a standalone version you can import it like so: 'import { t } from "@manyducks.co/dolla"'`
776
1092
  );
777
- return C(() => {
778
- const r = {};
779
- for (const n in t)
780
- r[n] = E(t[n]);
781
- return f(this, b, ze).call(this, E(a(this, v)), e, r);
1093
+ }
1094
+ return compose(() => {
1095
+ const values = {};
1096
+ for (const key in options) {
1097
+ values[key] = get(options[key]);
1098
+ }
1099
+ return this.#getValue(get(this.#locale), selector, values);
782
1100
  });
783
1101
  }
1102
+ #getValue(locale, selector, options) {
1103
+ const cached = this.#getCached(selector, options);
1104
+ if (cached) return cached;
1105
+ const translation = this.#translations.get(locale);
1106
+ if (options.context != null) {
1107
+ selector += "_" + options.context;
1108
+ }
1109
+ if (options.count != null) {
1110
+ if (options.ordinal) {
1111
+ const exact = `${selector}_ordinal_(=${options.count})`;
1112
+ if (translation.hasTemplate(exact)) {
1113
+ selector = exact;
1114
+ } else {
1115
+ selector += "_ordinal_" + new Intl.PluralRules(locale, { type: "ordinal" }).select(options.count);
1116
+ }
1117
+ } else {
1118
+ const exact = `${selector}_(=${options.count})`;
1119
+ if (translation.hasTemplate(exact)) {
1120
+ selector = exact;
1121
+ } else {
1122
+ selector += "_" + new Intl.PluralRules(locale).select(options.count);
1123
+ }
1124
+ }
1125
+ }
1126
+ const template = translation.getTemplate(selector);
1127
+ let output = "";
1128
+ for (const segment of template.segments) {
1129
+ if (segment.type === 0 /* Static */) {
1130
+ output += segment.text;
1131
+ } else if (segment.type === 1 /* Variable */) {
1132
+ let value = resolve(options, segment.name);
1133
+ const formats = options.formatOverrides?.[segment.name] ?? [...segment.formats];
1134
+ if (segment.name === "count" && formats.length === 0) {
1135
+ formats.push({ name: "number", options: {} });
1136
+ }
1137
+ for (const format of formats) {
1138
+ const fn = this.#formats.get(format.name);
1139
+ if (fn == null) {
1140
+ const error = new Error(
1141
+ `Failed to load format '${format.name}' when processing '${selector}', template: ${template}`
1142
+ );
1143
+ this.#logger.crash(error);
1144
+ throw error;
1145
+ }
1146
+ value = fn(locale, value, format.options);
1147
+ }
1148
+ output += value;
1149
+ }
1150
+ }
1151
+ return output;
1152
+ }
784
1153
  /**
785
1154
  * Add a custom format callback.
786
1155
  *
@@ -795,8 +1164,8 @@ class _t {
795
1164
  *
796
1165
  * t("greeting", {name: "world"}); // State<"Hello, WORLD!">
797
1166
  */
798
- addFormat(e, t) {
799
- a(this, te).set(e, t);
1167
+ addFormat(name, callback) {
1168
+ this.#formats.set(name, callback);
800
1169
  }
801
1170
  /**
802
1171
  * Creates an `Intl.Collator` configured for the current locale.
@@ -804,16 +1173,22 @@ class _t {
804
1173
  *
805
1174
  * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Collator/Collator#options
806
1175
  */
807
- collator(e) {
808
- return new Intl.Collator(a(this, v).value, e);
1176
+ collator(options) {
1177
+ return new Intl.Collator(this.#locale.value, options);
809
1178
  }
810
1179
  /**
811
1180
  * Formats a number for the current locale. Uses `Intl.NumberFormat` under the hood.
812
1181
  *
813
1182
  * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat#options
814
1183
  */
815
- number(e, t) {
816
- return C(() => (E(a(this, v)), f(this, b, Ee).call(this, E(e), t)));
1184
+ number(count, options) {
1185
+ return compose(() => {
1186
+ get(this.#locale);
1187
+ return this.#formatNumber(get(count), options);
1188
+ });
1189
+ }
1190
+ #formatNumber(count, options) {
1191
+ return new Intl.NumberFormat(this.#locale.value, options).format(count);
817
1192
  }
818
1193
  /**
819
1194
  * Formats a date for the current locale. Uses `Intl.DateTimeFormat` under the hood.
@@ -824,8 +1199,14 @@ class _t {
824
1199
  * const date = new Date();
825
1200
  * const $formatted = Dolla.i18n.dateTime(date, { dateFormat: "short" });
826
1201
  */
827
- dateTime(e, t) {
828
- return C(() => (E(a(this, v)), f(this, b, ke).call(this, E(e), t)));
1202
+ dateTime(date, options) {
1203
+ return compose(() => {
1204
+ get(this.#locale);
1205
+ return this.#formatDateTime(get(date), options);
1206
+ });
1207
+ }
1208
+ #formatDateTime(date, options) {
1209
+ return new Intl.DateTimeFormat(this.#locale.value, options).format(isString(date) ? new Date(date) : date);
829
1210
  }
830
1211
  /**
831
1212
  * Formats a list for the current locale. Uses `Intl.ListFormat` under the hood.
@@ -836,69 +1217,41 @@ class _t {
836
1217
  * const list = new Date();
837
1218
  * const $formatted = Dolla.i18n.list(list, { });
838
1219
  */
839
- list(e, t) {
840
- return C(() => (E(a(this, v)), f(this, b, Re).call(this, E(e), t)));
1220
+ list(list, options) {
1221
+ return compose(() => {
1222
+ get(this.#locale);
1223
+ return this.#formatList(get(list), options);
1224
+ });
1225
+ }
1226
+ #formatList(list, options) {
1227
+ return new Intl.ListFormat(this.#locale.value, options).format(list);
1228
+ }
1229
+ // relativeTime(): State<string> {
1230
+ // }
1231
+ #getCached(key, values) {
1232
+ for (const entry of this.#cache) {
1233
+ if (entry[0] === key && deepEqual(entry[1], values)) {
1234
+ return entry[2];
1235
+ }
1236
+ }
841
1237
  }
842
1238
  }
843
- Z = new WeakMap(), F = new WeakMap(), y = new WeakMap(), ee = new WeakMap(), te = new WeakMap(), re = new WeakMap(), v = new WeakMap(), b = new WeakSet(), ze = function(e, t, r) {
844
- var l;
845
- const n = f(this, b, Be).call(this, t, r);
846
- if (n) return n;
847
- const s = a(this, y).get(e);
848
- if (r.context != null && (t += "_" + r.context), r.count != null)
849
- if (r.ordinal) {
850
- const w = `${t}_ordinal_(=${r.count})`;
851
- s.hasTemplate(w) ? t = w : t += "_ordinal_" + new Intl.PluralRules(e, { type: "ordinal" }).select(r.count);
1239
+ function resolve(object, key) {
1240
+ const parsed = String(key).split(/[\.\[\]]/).filter((part) => part.trim() !== "");
1241
+ let value = object;
1242
+ while (parsed.length > 0) {
1243
+ const part = parsed.shift();
1244
+ if (value != null) {
1245
+ value = value[part];
852
1246
  } else {
853
- const w = `${t}_(=${r.count})`;
854
- s.hasTemplate(w) ? t = w : t += "_" + new Intl.PluralRules(e).select(r.count);
855
- }
856
- const o = s.getTemplate(t);
857
- let u = "";
858
- for (const w of o.segments)
859
- if (w.type === 0)
860
- u += w.text;
861
- else if (w.type === 1) {
862
- let $ = St(r, w.name);
863
- const R = ((l = r.formatOverrides) == null ? void 0 : l[w.name]) ?? [...w.formats];
864
- w.name === "count" && R.length === 0 && R.push({ name: "number", options: {} });
865
- for (const c of R) {
866
- const T = a(this, te).get(c.name);
867
- if (T == null) {
868
- const _ = new Error(
869
- `Failed to load format '${c.name}' when processing '${t}', template: ${o}`
870
- );
871
- throw a(this, F).crash(_), _;
872
- }
873
- $ = T(e, $, c.options);
874
- }
875
- u += $;
876
- }
877
- return u;
878
- }, Ee = function(e, t) {
879
- return new Intl.NumberFormat(a(this, v).value, t).format(e);
880
- }, ke = function(e, t) {
881
- return new Intl.DateTimeFormat(a(this, v).value, t).format(k(e) ? new Date(e) : e);
882
- }, Re = function(e, t) {
883
- return new Intl.ListFormat(a(this, v).value, t).format(e);
884
- }, // relativeTime(): State<string> {
885
- // }
886
- Be = function(e, t) {
887
- for (const r of a(this, ee))
888
- if (r[0] === e && Ze(r[1], t))
889
- return r[2];
890
- };
891
- function St(i, e) {
892
- const t = String(e).split(/[\.\[\]]/).filter((n) => n.trim() !== "");
893
- let r = i;
894
- for (; t.length > 0; ) {
895
- const n = t.shift();
896
- r != null ? r = r[n] : r = void 0;
897
- }
898
- return r;
1247
+ value = void 0;
1248
+ }
1249
+ }
1250
+ return value;
899
1251
  }
900
- function Mt(i) {
901
- return Te`
1252
+
1253
+ function DefaultCrashView(props) {
1254
+ return html`
902
1255
  <div
903
1256
  style=${{
904
1257
  backgroundColor: "#880000",
@@ -911,8 +1264,8 @@ function Mt(i) {
911
1264
  >
912
1265
  <h1 style=${{ marginBottom: "0.5rem" }}>The app has crashed</h1>
913
1266
  <p style=${{ marginBottom: "0.25rem" }}>
914
- <span style=${{ fontFamily: "monospace" }}>${i.loggerName}</span>
915
- ${et(i.uid, Te`<span style=${{ fontFamily: "monospace", opacity: 0.5 }}> [uid: ${i.uid}]</span>`)}
1267
+ <span style=${{ fontFamily: "monospace" }}>${props.loggerName}</span>
1268
+ ${cond(props.uid, html`<span style=${{ fontFamily: "monospace", opacity: 0.5 }}> [uid: ${props.uid}]</span>`)}
916
1269
  ${" "}says:
917
1270
  </p>
918
1271
  <blockquote
@@ -935,290 +1288,276 @@ function Mt(i) {
935
1288
  fontWeight: "bold"
936
1289
  }}
937
1290
  >
938
- ${i.error.name}
1291
+ ${props.error.name}
939
1292
  </span>
940
- ${i.error.message}
1293
+ ${props.error.message}
941
1294
  </blockquote>
942
1295
 
943
1296
  <p>Please see the browser console for details.</p>
944
1297
  </div>
945
1298
  `;
946
1299
  }
947
- var L, se, O, q, ne, K, ae, ie, oe, le, U, g, j, we;
948
- class xt {
1300
+
1301
+ class Dolla {
1302
+ // TODO: Take these off the global Dolla object.
1303
+ http;
1304
+ i18n;
1305
+ #isMounted = false;
1306
+ #env = "production";
1307
+ #rootElement;
1308
+ #rootView;
1309
+ #crashView = DefaultCrashView;
1310
+ #router;
1311
+ #beforeMountCallbacks = [];
1312
+ #onMountCallbacks = [];
1313
+ #beforeUnmountCallbacks = [];
1314
+ #onUnmountCallbacks = [];
1315
+ #rootElementContext = {
1316
+ root: this,
1317
+ stores: /* @__PURE__ */ new Map(),
1318
+ viewName: "Dolla"
1319
+ };
1320
+ #loggles = {
1321
+ info: "development",
1322
+ log: "development",
1323
+ warn: "development",
1324
+ error: true
1325
+ };
1326
+ #match = createMatcher("*,-Dolla.*");
1327
+ // Registration functions for modules.
1328
+ // All modules will be registered before mount.
1329
+ #modules = [];
949
1330
  constructor() {
950
- // TODO: Take these off the global Dolla object.
951
- p(this, "http");
952
- p(this, "i18n");
953
- h(this, L, !1);
954
- h(this, se, "production");
955
- h(this, O);
956
- h(this, q);
957
- h(this, ne, Mt);
958
- h(this, K);
959
- h(this, ae, []);
960
- h(this, ie, []);
961
- h(this, oe, []);
962
- h(this, le, []);
963
- h(this, U, {
964
- root: this,
965
- data: {},
966
- emitter: new tt(),
967
- stores: /* @__PURE__ */ new Map(),
968
- viewName: "Dolla"
969
- });
970
- h(this, g, {
971
- info: "development",
972
- log: "development",
973
- warn: "development",
974
- error: !0
975
- });
976
- h(this, j, Ne("*,-Dolla.*"));
977
- // Registration functions for modules.
978
- // All modules will be registered before mount.
979
- h(this, we, []);
980
- this.http = new bt(), this.i18n = new _t(this);
1331
+ this.http = new HTTP();
1332
+ this.i18n = new I18n(this);
981
1333
  }
982
1334
  /**
983
1335
  * True when the app is connected to a DOM node and displayed to the user.
984
1336
  */
985
1337
  get isMounted() {
986
- return a(this, L);
1338
+ return this.#isMounted;
987
1339
  }
988
1340
  /**
989
1341
  * Get the current environment that this app is running in.
990
1342
  * Environment affects which log messages will print and how much debugging info is included in the DOM.
991
1343
  */
992
1344
  getEnv() {
993
- return a(this, se);
1345
+ return this.#env;
994
1346
  }
995
1347
  /**
996
1348
  * Sets the environment that this app is running in.
997
1349
  * Environment affects which log messages will print and how much debugging info is included in the DOM.
998
1350
  */
999
- setEnv(e) {
1000
- d(this, se, e);
1351
+ setEnv(value) {
1352
+ this.#env = value;
1001
1353
  }
1002
1354
  /**
1003
1355
  * Sets the view that will be shown when the `crash` method is called on any logger.
1004
1356
  * When a crash is reported the app will be unmounted and replaced with this crash page.
1005
1357
  */
1006
- setCrashView(e) {
1007
- d(this, ne, e);
1358
+ setCrashView(view) {
1359
+ this.#crashView = view;
1008
1360
  }
1009
1361
  /**
1010
1362
  * Returns the HTMLElement Dolla is mounted to. This will return undefined until Dolla.mount() is called.
1011
1363
  */
1012
1364
  getRootElement() {
1013
- return a(this, O);
1365
+ return this.#rootElement;
1014
1366
  }
1015
1367
  /**
1016
1368
  * Returns the top level view Dolla is rendering inside the root element. This will return undefined until Dolla.mount() is called.
1017
1369
  */
1018
1370
  getRootView() {
1019
- return a(this, q);
1020
- }
1021
- provide(e, t) {
1022
- const r = new rt(e, t);
1023
- if (r.attach(a(this, U)))
1024
- return r.value;
1025
- {
1026
- let s = e.name ? `'${e.name}'` : "this store";
1027
- return console.warn(`An instance of ${s} was already attached to this context.`), this.get(e);
1371
+ return this.#rootView;
1372
+ }
1373
+ provide(store, options) {
1374
+ const instance = new Store(store, options);
1375
+ const attached = instance.attach(this.#rootElementContext);
1376
+ if (!attached) {
1377
+ let name = store.name ? `'${store.name}'` : "this store";
1378
+ console.warn(`An instance of ${name} was already attached to this context.`);
1379
+ return this.get(store);
1380
+ } else {
1381
+ return instance.value;
1028
1382
  }
1029
1383
  }
1030
1384
  /**
1031
1385
  * Gets the nearest instance of a store. Throws an error if the store isn't provided higher in the tree.
1032
1386
  */
1033
- get(e) {
1034
- if (Q(e)) {
1035
- const t = a(this, U).stores.get(e);
1036
- if (t == null)
1037
- throw new Pe("Store not found on this context.");
1038
- return t.value;
1039
- } else
1040
- throw new Pe("Invalid store.");
1041
- }
1042
- async mount(e, t) {
1043
- if (a(this, L))
1044
- throw new Error("Dolla is already mounted.");
1045
- if (k(e)) {
1046
- const s = document.querySelector(e);
1047
- Fe(HTMLElement, s, `Selector '${e}' did not match any element.`), d(this, O, s);
1048
- } else
1049
- Fe(HTMLElement, e, "Expected an HTML element or a selector string. Got type: %t, value: %v"), d(this, O, e);
1050
- ge(t) && d(this, K, t);
1051
- const r = ge(t) ? De : t, n = st(r);
1052
- d(this, q, this.constructView(n.type, n.props)), await Promise.all(a(this, we).map((s) => s())), ge(t) && await ht(t, this), await Promise.all(a(this, ae).map((s) => s())), a(this, q).mount(a(this, O)), d(this, L, !0);
1053
- for (const s of a(this, U).stores.values())
1054
- s.handleMount();
1055
- for (const s of a(this, ie))
1056
- s();
1387
+ get(store) {
1388
+ if (isFunction(store)) {
1389
+ const instance = this.#rootElementContext.stores.get(store);
1390
+ if (instance == null) {
1391
+ throw new StoreError(`Store not found on this context.`);
1392
+ } else {
1393
+ return instance.value;
1394
+ }
1395
+ } else {
1396
+ throw new StoreError(`Invalid store.`);
1397
+ }
1398
+ }
1399
+ async mount(target, root) {
1400
+ if (this.#isMounted) {
1401
+ throw new Error(`Dolla is already mounted.`);
1402
+ }
1403
+ if (isString(target)) {
1404
+ const match = document.querySelector(target);
1405
+ assertInstanceOf(HTMLElement, match, `Selector '${target}' did not match any element.`);
1406
+ this.#rootElement = match;
1407
+ } else {
1408
+ assertInstanceOf(HTMLElement, target, "Expected an HTML element or a selector string. Got type: %t, value: %v");
1409
+ this.#rootElement = target;
1410
+ }
1411
+ if (_isRouter(root)) {
1412
+ this.#router = root;
1413
+ }
1414
+ const view = _isRouter(root) ? Passthrough : root;
1415
+ const rootViewMarkup = createMarkup(view);
1416
+ this.#rootView = this.constructView(rootViewMarkup.type, rootViewMarkup.props);
1417
+ await Promise.all(this.#modules.map((register) => register()));
1418
+ if (_isRouter(root)) {
1419
+ await _mountRouter(root, this);
1420
+ }
1421
+ await Promise.all(this.#beforeMountCallbacks.map((callback) => callback()));
1422
+ this.#rootView.mount(this.#rootElement);
1423
+ this.#isMounted = true;
1424
+ for (const store of this.#rootElementContext.stores.values()) {
1425
+ store.handleMount();
1426
+ }
1427
+ for (const callback of this.#onMountCallbacks) {
1428
+ callback();
1429
+ }
1057
1430
  }
1058
1431
  async unmount() {
1059
- var e;
1060
- if (a(this, L)) {
1061
- await Promise.all(a(this, oe).map((t) => t())), (e = a(this, q)) == null || e.unmount(!1), a(this, K) && await ut(a(this, K)), d(this, L, !1);
1062
- for (const t of a(this, le))
1063
- t();
1432
+ if (!this.#isMounted) return;
1433
+ await Promise.all(this.#beforeUnmountCallbacks.map((callback) => callback()));
1434
+ this.#rootView?.unmount(false);
1435
+ if (this.#router) {
1436
+ await _unmountRouter(this.#router);
1437
+ }
1438
+ this.#isMounted = false;
1439
+ for (const callback of this.#onUnmountCallbacks) {
1440
+ callback();
1064
1441
  }
1065
1442
  }
1066
1443
  /**
1067
1444
  * Registers a `callback` to run after `Dolla.mount` is called, before the app is mounted. If `callback` returns a Promise,
1068
1445
  * it will be awaited before mounting finishes. Use this to perform initial setup before the app is displayed to the user.
1069
1446
  */
1070
- beforeMount(e) {
1071
- a(this, ae).push(e);
1447
+ beforeMount(callback) {
1448
+ this.#beforeMountCallbacks.push(callback);
1072
1449
  }
1073
1450
  /**
1074
1451
  * Registers a `callback` to run after the app is mounted.
1075
1452
  */
1076
- onMount(e) {
1077
- a(this, ie).push(e);
1453
+ onMount(callback) {
1454
+ this.#onMountCallbacks.push(callback);
1078
1455
  }
1079
1456
  /**
1080
1457
  * Registers a `callback` to run after `Dolla.unmount` is called, before the app is unmounted. If `callback` returns a Promise,
1081
1458
  * it will be awaited before unmounting finishes. Use this to perform cleanup.
1082
1459
  */
1083
- beforeUnmount(e) {
1084
- a(this, oe).push(e);
1460
+ beforeUnmount(callback) {
1461
+ this.#beforeUnmountCallbacks.push(callback);
1085
1462
  }
1086
1463
  /**
1087
1464
  * Registers a `callback` to run after the app is unmounted.
1088
1465
  */
1089
- onUnmount(e) {
1090
- a(this, le).push(e);
1466
+ onUnmount(callback) {
1467
+ this.#onUnmountCallbacks.push(callback);
1091
1468
  }
1092
1469
  /**
1093
1470
  * Update log type toggles. Values that are not passed will remain unchanged.
1094
1471
  */
1095
- setLoggles(e) {
1096
- for (const t in e) {
1097
- const r = e[t];
1098
- r && (a(this, g)[t] = r);
1472
+ setLoggles(options) {
1473
+ for (const key in options) {
1474
+ const value = options[key];
1475
+ if (value) {
1476
+ this.#loggles[key] = value;
1477
+ }
1099
1478
  }
1100
1479
  }
1101
- setLogFilter(e) {
1102
- d(this, j, Ne(e));
1103
- }
1104
- createLogger(e, t) {
1105
- const r = (t == null ? void 0 : t.console) ?? Tt(), n = this;
1480
+ setLogFilter(filter) {
1481
+ this.#match = createMatcher(filter);
1482
+ }
1483
+ createLogger(name, options) {
1484
+ const _console = options?.console ?? getDefaultConsole();
1485
+ const self = this;
1486
+ const loggles = this.#loggles;
1487
+ const bind = (method) => {
1488
+ if (loggles[method] === false || isString(loggles[method]) && loggles[method] !== self.getEnv() || !this.#match(name)) {
1489
+ return noOp;
1490
+ } else {
1491
+ let label = `%c${name}`;
1492
+ if (options?.uid) {
1493
+ label += ` %c[uid: %c${options.uid}%c]`;
1494
+ } else {
1495
+ label += `%c%c%c`;
1496
+ }
1497
+ return _console[method].bind(
1498
+ _console,
1499
+ label,
1500
+ `color:${okhash(label)};font-weight:bold`,
1501
+ `color:#777`,
1502
+ `color:#aaa`,
1503
+ `color:#777`
1504
+ );
1505
+ }
1506
+ };
1106
1507
  return {
1107
- setName(s) {
1108
- return e = s, this;
1508
+ setName(newName) {
1509
+ name = newName;
1510
+ return this;
1109
1511
  },
1110
1512
  get info() {
1111
- var s;
1112
- if (a(n, g).info === !1 || k(a(n, g).info) && a(n, g).info !== n.getEnv() || !a(s = n, j).call(s, e))
1113
- return ce;
1114
- {
1115
- let o = `%c${e}`;
1116
- return t != null && t.uid ? o += ` %c[uid: %c${t.uid}%c]` : o += "%c%c%c", r.info.bind(
1117
- r,
1118
- o,
1119
- `color:${he(o)};font-weight:bold`,
1120
- "color:#777",
1121
- "color:#aaa",
1122
- "color:#777"
1123
- );
1124
- }
1513
+ return bind("info");
1125
1514
  },
1126
1515
  get log() {
1127
- var s;
1128
- if (a(n, g).log === !1 || k(a(n, g).log) && a(n, g).log !== n.getEnv() || !a(s = n, j).call(s, e))
1129
- return ce;
1130
- {
1131
- let o = `%c${e}`;
1132
- return t != null && t.uid ? o += ` %c[uid: %c${t.uid}%c]` : o += "%c%c%c", r.log.bind(
1133
- r,
1134
- o,
1135
- `color:${he(o)};font-weight:bold`,
1136
- "color:#777",
1137
- "color:#aaa",
1138
- "color:#777"
1139
- );
1140
- }
1516
+ return bind("log");
1141
1517
  },
1142
1518
  get warn() {
1143
- var s;
1144
- if (a(n, g).warn === !1 || k(a(n, g).warn) && a(n, g).warn !== n.getEnv() || !a(s = n, j).call(s, e))
1145
- return ce;
1146
- {
1147
- let o = `%c${e}`;
1148
- return t != null && t.uid ? o += ` %c[uid: %c${t.uid}%c]` : o += "%c%c%c", r.warn.bind(
1149
- r,
1150
- o,
1151
- `color:${he(o)};font-weight:bold`,
1152
- "color:#777",
1153
- "color:#aaa",
1154
- "color:#777"
1155
- );
1156
- }
1519
+ return bind("warn");
1157
1520
  },
1158
1521
  get error() {
1159
- var s;
1160
- if (a(n, g).error === !1 || k(a(n, g).error) && a(n, g).error !== n.getEnv() || !a(s = n, j).call(s, e))
1161
- return ce;
1162
- {
1163
- let o = `%c${e}`;
1164
- return t != null && t.uid ? o += ` %c[uid: %c${t.uid}%c]` : o += "%c%c%c", r.error.bind(
1165
- r,
1166
- o,
1167
- `color:${he(o)};font-weight:bold`,
1168
- "color:#777",
1169
- "color:#aaa",
1170
- "color:#777"
1171
- );
1172
- }
1522
+ return bind("error");
1173
1523
  },
1174
- crash(s) {
1175
- n.isMounted && (n.unmount(), n.constructView(a(n, ne), {
1176
- error: s,
1177
- loggerName: e,
1178
- uid: t == null ? void 0 : t.uid
1179
- }).mount(a(n, O)));
1524
+ crash(error) {
1525
+ if (self.isMounted) {
1526
+ self.unmount();
1527
+ const crashPage = self.constructView(self.#crashView, {
1528
+ error,
1529
+ loggerName: name,
1530
+ uid: options?.uid
1531
+ });
1532
+ crashPage.mount(self.#rootElement);
1533
+ }
1180
1534
  }
1181
1535
  };
1182
1536
  }
1183
1537
  /**
1184
1538
  *
1185
1539
  */
1186
- constructView(e, t, r = []) {
1187
- return new nt(a(this, U), e, t, r);
1540
+ constructView(view, props, children = []) {
1541
+ return new View(this.#rootElementContext, view, props, children);
1188
1542
  }
1189
1543
  /**
1190
1544
  *
1191
1545
  */
1192
- constructMarkup(e) {
1193
- return at(it(a(this, U), e));
1546
+ constructMarkup(markup) {
1547
+ return groupElements(constructMarkup(this.#rootElementContext, markup));
1194
1548
  }
1195
1549
  }
1196
- L = new WeakMap(), se = new WeakMap(), O = new WeakMap(), q = new WeakMap(), ne = new WeakMap(), K = new WeakMap(), ae = new WeakMap(), ie = new WeakMap(), oe = new WeakMap(), le = new WeakMap(), U = new WeakMap(), g = new WeakMap(), j = new WeakMap(), we = new WeakMap();
1197
- function Tt() {
1198
- if (typeof window < "u" && window.console)
1550
+ function getDefaultConsole() {
1551
+ if (typeof window !== "undefined" && window.console) {
1199
1552
  return window.console;
1200
- if (typeof global < "u" && global.console)
1553
+ }
1554
+ if (typeof global !== "undefined" && global.console) {
1201
1555
  return global.console;
1556
+ }
1202
1557
  }
1203
- const Le = new xt(), Ot = Le.i18n.t.bind(Le.i18n);
1204
- export {
1205
- Ce as atom,
1206
- C as compose,
1207
- et as cond,
1208
- st as createMarkup,
1209
- Lt as createRouter,
1210
- Ze as deepEqual,
1211
- Le as default,
1212
- Ct as effect,
1213
- E as get,
1214
- Te as html,
1215
- Dt as list,
1216
- It as peek,
1217
- Vt as portal,
1218
- Ft as ref,
1219
- qt as set,
1220
- Me as shallowEqual,
1221
- Gt as strictEqual,
1222
- Ot as t
1223
- };
1558
+
1559
+ const dolla = new Dolla();
1560
+ const t = dolla.i18n.t.bind(dolla.i18n);
1561
+
1562
+ export { atom, compose, cond, createMarkup, createRouter, deepEqual, dolla as default, get, html, ref, shallowEqual, t };
1224
1563
  //# sourceMappingURL=index.js.map