vitral 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/angular.d.ts +2 -2
  2. package/dist/angular.js +5 -5
  3. package/dist/auto.js +5 -5
  4. package/dist/{chunk-W6322KGP.js → chunk-4Z77XNBR.js} +2 -2
  5. package/dist/{chunk-EMSB2NVX.js → chunk-5JNV45BB.js} +4 -4
  6. package/dist/{chunk-IH3ZT3QU.js → chunk-C3QOBAUE.js} +1 -1
  7. package/dist/chunk-DAESKNQ6.js +207 -0
  8. package/dist/{chunk-BCC25ANK.js → chunk-K6EG7DMO.js} +8 -5
  9. package/dist/{chunk-6EX5KYT5.js → chunk-KU6HF2EW.js} +2 -2
  10. package/dist/{chunk-WD4CDGDZ.js → chunk-KZBH5MPD.js} +12 -11
  11. package/dist/chunk-NZEQGRBL.js +93 -0
  12. package/dist/chunk-PY7P5BF4.js +404 -0
  13. package/dist/cli.js +414 -44
  14. package/dist/dict.d.ts +69 -0
  15. package/dist/dict.js +8 -0
  16. package/dist/edge.d.ts +118 -0
  17. package/dist/edge.js +398 -0
  18. package/dist/element.d.ts +2 -2
  19. package/dist/element.js +5 -5
  20. package/dist/embed.js +2 -2
  21. package/dist/{events-Dkpkw_Sv.d.ts → events-0rxFlWJJ.d.ts} +1 -1
  22. package/dist/{expose-OVWTPHH5.js → expose-IAKYHJXF.js} +3 -3
  23. package/dist/federation.d.ts +1 -1
  24. package/dist/hydrate-YVZDJ2V7.js +168 -0
  25. package/dist/iframe.js +4 -4
  26. package/dist/index.d.ts +4 -4
  27. package/dist/index.js +3 -3
  28. package/dist/otel.d.ts +29 -1
  29. package/dist/otel.js +13 -1
  30. package/dist/preact.d.ts +4 -4
  31. package/dist/preact.js +4 -4
  32. package/dist/prefetch.d.ts +25 -0
  33. package/dist/prefetch.js +178 -0
  34. package/dist/react.d.ts +3 -3
  35. package/dist/react.js +4 -4
  36. package/dist/reframe-LQL4DZYP.js +251 -0
  37. package/dist/routes.d.ts +2 -2
  38. package/dist/routes.js +3 -3
  39. package/dist/{shadow-PVSETNMZ.js → shadow-S4SKHTA7.js} +2 -2
  40. package/dist/single-spa.d.ts +1 -1
  41. package/dist/{slot-UPg6ob9v.d.ts → slot-D49L1rdU.d.ts} +1 -1
  42. package/dist/solid.d.ts +3 -3
  43. package/dist/solid.js +4 -4
  44. package/dist/{sri-M7ZRU57G.js → sri-576GTDIA.js} +2 -1
  45. package/dist/svelte.d.ts +4 -4
  46. package/dist/svelte.js +4 -4
  47. package/dist/testing.d.ts +171 -3
  48. package/dist/testing.js +191 -4
  49. package/dist/{types-CA2p9KHC.d.ts → types-DUtcF3HO.d.ts} +12 -1
  50. package/dist/vitals.d.ts +29 -0
  51. package/dist/vitals.js +152 -0
  52. package/dist/vite.js +28 -422
  53. package/dist/vue.d.ts +2 -2
  54. package/dist/vue.js +4 -4
  55. package/package.json +107 -16
package/dist/angular.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { InjectionToken, Type, ApplicationConfig, Signal, Provider, EnvironmentProviders } from '@angular/core';
2
- import { R as RemoteContext, P as Props, a as RemoteModule, S as Shared, V as VitralConfig } from './types-CA2p9KHC.js';
3
- export { e as events } from './events-Dkpkw_Sv.js';
2
+ import { R as RemoteContext, P as Props, a as RemoteModule, S as Shared, V as VitralConfig } from './types-DUtcF3HO.js';
3
+ export { e as events } from './events-0rxFlWJJ.js';
4
4
  export { n as navigate } from './navigate-BRG1WJUM.js';
5
5
  import './errors-kjBv6IH_.js';
6
6
 
package/dist/angular.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  defineVitralElement
3
- } from "./chunk-BCC25ANK.js";
4
- import "./chunk-W6322KGP.js";
3
+ } from "./chunk-K6EG7DMO.js";
4
+ import "./chunk-4Z77XNBR.js";
5
5
  import {
6
6
  defineRemote,
7
7
  events,
@@ -9,11 +9,11 @@ import {
9
9
  navigate,
10
10
  same,
11
11
  shared
12
- } from "./chunk-WD4CDGDZ.js";
12
+ } from "./chunk-KZBH5MPD.js";
13
13
  import {
14
14
  configure
15
- } from "./chunk-EMSB2NVX.js";
16
- import "./chunk-6EX5KYT5.js";
15
+ } from "./chunk-5JNV45BB.js";
16
+ import "./chunk-KU6HF2EW.js";
17
17
  import "./chunk-TW7KYJQ6.js";
18
18
  import "./chunk-463H6B56.js";
19
19
  import "./chunk-GNPFWISJ.js";
package/dist/auto.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  defineVitralElement
3
- } from "./chunk-BCC25ANK.js";
4
- import "./chunk-W6322KGP.js";
5
- import "./chunk-WD4CDGDZ.js";
6
- import "./chunk-EMSB2NVX.js";
7
- import "./chunk-6EX5KYT5.js";
3
+ } from "./chunk-K6EG7DMO.js";
4
+ import "./chunk-4Z77XNBR.js";
5
+ import "./chunk-KZBH5MPD.js";
6
+ import "./chunk-5JNV45BB.js";
7
+ import "./chunk-KU6HF2EW.js";
8
8
  import "./chunk-TW7KYJQ6.js";
9
9
  import "./chunk-463H6B56.js";
10
10
  import "./chunk-GNPFWISJ.js";
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  createSlot
3
- } from "./chunk-WD4CDGDZ.js";
3
+ } from "./chunk-KZBH5MPD.js";
4
4
  import {
5
5
  preload,
6
6
  registerSrc
7
- } from "./chunk-EMSB2NVX.js";
7
+ } from "./chunk-5JNV45BB.js";
8
8
 
9
9
  // src/core/host-slot.ts
10
10
  var noop = () => {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  prefetchCss
3
- } from "./chunk-6EX5KYT5.js";
3
+ } from "./chunk-KU6HF2EW.js";
4
4
  import {
5
5
  absolute,
6
6
  addLink,
@@ -79,7 +79,7 @@ function loadRemote(name) {
79
79
  return load(name).then((r) => r.module);
80
80
  }
81
81
  function loadModule(name) {
82
- return import("./expose-OVWTPHH5.js").then((m) => m.resolveModule(name, (n) => loadFile(n))).catch(fail);
82
+ return import("./expose-IAKYHJXF.js").then((m) => m.resolveModule(name, (n) => loadFile(n))).catch(fail);
83
83
  }
84
84
  function preload(name) {
85
85
  load(name).catch(() => {
@@ -88,12 +88,12 @@ function preload(name) {
88
88
  var paths;
89
89
  function load(name, isolate, priority) {
90
90
  const file = (n) => loadFile(n, isolate, priority);
91
- return (protocol().entry(name) || !name.includes("/") ? file(name).then((r) => mountable(name, r)) : import("./expose-OVWTPHH5.js").then((m) => m.resolveTarget(name, file))).catch(fail);
91
+ return (protocol().entry(name) || !name.includes("/") ? file(name).then((r) => mountable(name, r)) : import("./expose-IAKYHJXF.js").then((m) => m.resolveTarget(name, file))).catch(fail);
92
92
  }
93
93
  function loadFile(name, isolate, priority) {
94
94
  const p = protocol();
95
95
  return p.load(name, async () => {
96
- paths ??= import("./sri-M7ZRU57G.js").catch(() => null);
96
+ paths ??= import("./sri-576GTDIA.js").catch(() => null);
97
97
  let resolved;
98
98
  try {
99
99
  await p.pending();
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  mountable
3
- } from "./chunk-EMSB2NVX.js";
3
+ } from "./chunk-5JNV45BB.js";
4
4
  import {
5
5
  RemoteError
6
6
  } from "./chunk-463H6B56.js";
@@ -0,0 +1,207 @@
1
+ // src/harness.ts
2
+ var CONTRACT_SCHEMA = "https://joaogabriel15.github.io/vitral/schema/vitral-contract.json";
3
+ function encode(value, seen = /* @__PURE__ */ new Set()) {
4
+ if (typeof value === "function") return { $fn: true };
5
+ if (value === void 0 || value === null || typeof value !== "object") {
6
+ return typeof value === "bigint" || typeof value === "symbol" ? { $unknown: typeof value } : value;
7
+ }
8
+ if (value instanceof Date) return { $date: value.toISOString() };
9
+ if (seen.has(value)) return { $unknown: "circular" };
10
+ const proto = Object.getPrototypeOf(value);
11
+ if (!Array.isArray(value) && proto !== Object.prototype && proto !== null) return { $unknown: proto?.constructor?.name ?? "Object" };
12
+ seen.add(value);
13
+ try {
14
+ if (Array.isArray(value)) return value.map((v) => encode(v, seen) ?? null);
15
+ const out = {};
16
+ for (const [k, v] of Object.entries(value)) if (v !== void 0) out[k] = encode(v, seen);
17
+ return out;
18
+ } finally {
19
+ seen.delete(value);
20
+ }
21
+ }
22
+ function decode(value, fn = () => () => void 0, path = "") {
23
+ if (!value || typeof value !== "object") return value;
24
+ if (Array.isArray(value)) return value.map((v2, i) => decode(v2, fn, `${path}[${i}]`));
25
+ const v = value;
26
+ if (v.$fn === true) return fn(path);
27
+ if (typeof v.$date === "string") return new Date(v.$date);
28
+ if (typeof v.$unknown === "string") return void 0;
29
+ return Object.fromEntries(Object.entries(v).map(([k, x]) => [k, decode(x, fn, path ? `${path}.${k}` : k)]));
30
+ }
31
+ async function harness(mod, options) {
32
+ const remote = typeof mod.mount === "function" ? mod : mod.default;
33
+ if (typeof remote?.mount !== "function") throw new Error("[vitral] o m\xF3dulo n\xE3o exporta mount()");
34
+ const name = options.name ?? "remote";
35
+ const target = options.target ?? document.body.appendChild(document.createElement("div"));
36
+ const shadowRoot = options.shadow ? target.attachShadow({ mode: "open" }) : null;
37
+ const el = document.createElement("div");
38
+ el.setAttribute("data-vitral-root", name);
39
+ (shadowRoot ?? target).appendChild(el);
40
+ const portal = shadowRoot ? shadowRoot.appendChild(document.createElement("div")) : document.body;
41
+ const errors = [];
42
+ const emitted = [];
43
+ const navigations = [];
44
+ const handlers = /* @__PURE__ */ new Map();
45
+ const last = new Map(Object.entries(options.last ?? {}));
46
+ const dispatch = (type, payload) => {
47
+ last.set(type, payload);
48
+ for (const h of [...handlers.get(type) ?? []]) {
49
+ try {
50
+ h(payload);
51
+ } catch (e) {
52
+ errors.push(e);
53
+ }
54
+ }
55
+ };
56
+ const events = {
57
+ emit(type, payload) {
58
+ emitted.push({ type, payload });
59
+ dispatch(type, payload);
60
+ },
61
+ on(type, handler) {
62
+ let set = handlers.get(type);
63
+ if (!set) handlers.set(type, set = /* @__PURE__ */ new Set());
64
+ set.add(handler);
65
+ return () => set.delete(handler);
66
+ },
67
+ last: (type) => last.get(type)
68
+ };
69
+ const activate = /* @__PURE__ */ new Set();
70
+ const deactivate = /* @__PURE__ */ new Set();
71
+ const hook = (set) => (fn) => (set.add(fn), () => set.delete(fn));
72
+ const ctx = {
73
+ name,
74
+ baseUrl: typeof location !== "undefined" ? new URL(".", location.href).href : "http://localhost/",
75
+ events,
76
+ shadowRoot,
77
+ portalContainer: portal,
78
+ customElements,
79
+ reportError: (e) => errors.push(e),
80
+ navigate: (url, opts) => navigations.push({ url, options: opts }),
81
+ protocol: 1,
82
+ onActivate: hook(activate),
83
+ onDeactivate: hook(deactivate),
84
+ shared: memoryShared(options.shared ?? {}),
85
+ ...options.ctx
86
+ };
87
+ const mounted = await remote.mount(el, options.props, ctx);
88
+ let alive = true;
89
+ return {
90
+ el,
91
+ ctx,
92
+ events: emitted,
93
+ errors,
94
+ navigations,
95
+ listens: () => [...handlers].filter(([, s]) => s.size).map(([t]) => t),
96
+ emit: dispatch,
97
+ update(props) {
98
+ if (alive) mounted.update?.(props);
99
+ },
100
+ activate: () => activate.forEach((f) => f()),
101
+ deactivate: () => deactivate.forEach((f) => f()),
102
+ unmount() {
103
+ if (!alive) return;
104
+ alive = false;
105
+ try {
106
+ mounted.unmount();
107
+ } finally {
108
+ el.remove();
109
+ if (!options.target) target.remove();
110
+ }
111
+ }
112
+ };
113
+ }
114
+ function memoryShared(initial) {
115
+ const values = new Map(Object.entries(initial));
116
+ const subs = /* @__PURE__ */ new Map();
117
+ const handle = (key) => {
118
+ const set = (v) => {
119
+ values.set(key, v);
120
+ for (const fn of [...subs.get(key) ?? []]) fn(v);
121
+ };
122
+ return {
123
+ key,
124
+ get: () => values.get(key),
125
+ set,
126
+ update: (fn) => set(fn(values.get(key))),
127
+ subscribe(fn) {
128
+ fn(values.get(key));
129
+ let s = subs.get(key);
130
+ if (!s) subs.set(key, s = /* @__PURE__ */ new Set());
131
+ s.add(fn);
132
+ return () => s.delete(fn);
133
+ }
134
+ };
135
+ };
136
+ const api = handle;
137
+ api.provide = ((key, value) => {
138
+ if (!values.has(key)) values.set(key, value);
139
+ return handle(key);
140
+ });
141
+ api.batch = (fn) => fn();
142
+ return api;
143
+ }
144
+ var message = (e) => String(e?.message ?? e);
145
+ async function verify(contract, mod, options = {}) {
146
+ const failures = [];
147
+ const warnings = [];
148
+ const pause = () => new Promise((r) => setTimeout(r, options.wait ?? 0));
149
+ const cases = contract.cases ?? [];
150
+ for (const [i, c] of cases.entries()) {
151
+ const at = `caso ${i + 1}`;
152
+ const fail = (msg) => failures.push(`${at}: ${msg}`);
153
+ let h;
154
+ try {
155
+ h = await harness(mod, {
156
+ name: contract.remote,
157
+ props: decode(c.props),
158
+ shared: decode(c.shared ?? {}),
159
+ last: decode(c.last ?? {})
160
+ });
161
+ } catch (e) {
162
+ fail(`erro no mount: ${message(e)}`);
163
+ continue;
164
+ }
165
+ await pause();
166
+ for (const [j, u] of (c.updates ?? []).entries()) {
167
+ try {
168
+ h.update(decode(u));
169
+ } catch (e) {
170
+ fail(`erro no update ${j + 1}: ${message(e)}`);
171
+ }
172
+ }
173
+ if (c.updates?.length) await pause();
174
+ const deaf = /* @__PURE__ */ new Set();
175
+ for (const input of c.inputs ?? []) {
176
+ if (!h.listens().includes(input.type)) {
177
+ if (!deaf.has(input.type)) warnings.push(`${at}: o host emite "${input.type}" e o remote n\xE3o escuta mais`);
178
+ deaf.add(input.type);
179
+ continue;
180
+ }
181
+ h.emit(input.type, decode(input.payload));
182
+ }
183
+ await pause();
184
+ const seen = new Set(h.events.map((e) => e.type));
185
+ for (const e of c.emits ?? []) {
186
+ if (seen.has(e.type)) continue;
187
+ if (e.phase !== "later") fail(`o host escuta "${e.type}" e o remote n\xE3o emitiu (na grava\xE7\xE3o saiu no ${e.phase})`);
188
+ else if (!options.sources) warnings.push(`${at}: "${e.type}" sai depois do mount; sem o c\xF3digo do remote, n\xE3o deu para conferir`);
189
+ else if (!options.sources.some((s) => s.includes(e.type))) fail(`o host escuta "${e.type}" e o c\xF3digo do remote n\xE3o tem mais esse evento`);
190
+ }
191
+ try {
192
+ h.unmount();
193
+ } catch (e) {
194
+ fail(`erro no unmount: ${message(e)}`);
195
+ }
196
+ for (const e of h.errors) fail(`erro reportado: ${message(e)}`);
197
+ }
198
+ if (!cases.length) warnings.push("contrato sem casos");
199
+ return { cases: cases.length, failures, warnings };
200
+ }
201
+
202
+ export {
203
+ CONTRACT_SCHEMA,
204
+ encode,
205
+ harness,
206
+ verify
207
+ };
@@ -1,16 +1,16 @@
1
1
  import {
2
2
  hostSlot,
3
3
  timings
4
- } from "./chunk-W6322KGP.js";
4
+ } from "./chunk-4Z77XNBR.js";
5
5
  import {
6
6
  defineRemote,
7
7
  memoSelect,
8
8
  same,
9
9
  shared
10
- } from "./chunk-WD4CDGDZ.js";
10
+ } from "./chunk-KZBH5MPD.js";
11
11
  import {
12
12
  registerSrc
13
- } from "./chunk-EMSB2NVX.js";
13
+ } from "./chunk-5JNV45BB.js";
14
14
 
15
15
  // src/element.ts
16
16
  var Base = typeof HTMLElement === "undefined" ? class {
@@ -40,7 +40,7 @@ var VitralRemoteElement = class extends Base {
40
40
  if (!name) return;
41
41
  if (this.hasAttribute("props") && !Object.keys(this.#props).length) this.#props = parseProps(this.getAttribute("props"));
42
42
  const keep = this.getAttribute("keep-alive");
43
- this.#slot = hostSlot(this, {
43
+ const options = {
44
44
  name,
45
45
  props: this.#props,
46
46
  isolate: this.getAttribute("isolate") || void 0,
@@ -66,7 +66,10 @@ var VitralRemoteElement = class extends Base {
66
66
  if (status === "ready") this.dispatchEvent(new CustomEvent("vitral-ready", { bubbles: true, detail: { timings: timings(name) } }));
67
67
  if (status === "error") this.dispatchEvent(new CustomEvent("vitral-error", { bubbles: true, detail: { error } }));
68
68
  }
69
- });
69
+ };
70
+ const ssr = true ? this.querySelector(":scope>[data-vitral-ssr]") : null;
71
+ if (ssr) import("./hydrate-YVZDJ2V7.js").then((m) => this.isConnected && !this.#slot && (this.#slot = m.hydrateSlot(this, ssr, { ...options, props: this.#props })));
72
+ else this.#slot = hostSlot(this, options);
70
73
  }
71
74
  /**
72
75
  * Nome para montar. `src` em atributo é registrado sem entrar na allowlist:
@@ -23,11 +23,11 @@ function fetchCssText(href, integrity, priority) {
23
23
  return p;
24
24
  }
25
25
  function prefetchCss(r, shadow, priority = "auto") {
26
- if (shadow && adopts()) import("./shadow-PVSETNMZ.js").then((m) => m.prefetch(r, priority), noop);
26
+ if (shadow && adopts()) import("./shadow-S4SKHTA7.js").then((m) => m.prefetch(r, priority), noop);
27
27
  else for (const href of r.css) if (!document.querySelector(`link[href="${href}"]`)) addLink("preload", href, { as: "style", integrity: r.sri?.[href] ?? "", fetchPriority: priority });
28
28
  }
29
29
  function injectStyles(r, shadow, make, styles) {
30
- return shadow ? import("./shadow-PVSETNMZ.js").then((m) => m.attachShadowRoot(shadow, make, r, protocol().settings().styles, styles)) : injectCss(r.css, null, r.sri).then(() => [noop]);
30
+ return shadow ? import("./shadow-S4SKHTA7.js").then((m) => m.attachShadowRoot(shadow, make, r, protocol().settings().styles, styles)) : injectCss(r.css, null, r.sri).then(() => [noop]);
31
31
  }
32
32
  function injectCss(urls, root, sri = {}) {
33
33
  const owner = root ?? document;
@@ -3,10 +3,10 @@ import {
3
3
  loadFile,
4
4
  registerSrc,
5
5
  resolveEntry
6
- } from "./chunk-EMSB2NVX.js";
6
+ } from "./chunk-5JNV45BB.js";
7
7
  import {
8
8
  injectStyles
9
- } from "./chunk-6EX5KYT5.js";
9
+ } from "./chunk-KU6HF2EW.js";
10
10
  import {
11
11
  metric
12
12
  } from "./chunk-TW7KYJQ6.js";
@@ -119,7 +119,7 @@ var noop = () => {
119
119
  async function checkUpdates() {
120
120
  const p = protocol();
121
121
  const found = [];
122
- const { splitName } = await import("./expose-OVWTPHH5.js");
122
+ const { splitName } = await import("./expose-IAKYHJXF.js");
123
123
  const remotes = /* @__PURE__ */ new Map();
124
124
  for (const name of live.keys()) {
125
125
  const root = splitName(name)[0];
@@ -163,14 +163,11 @@ function setFrameMounter(mounter) {
163
163
  async function mountOnce(target, name, props, options) {
164
164
  const p = protocol();
165
165
  await p.pending();
166
- if ((options.isolate ?? registered(name)?.isolate ?? p.settings().isolate) === "iframe") {
167
- if (!frames) throw report(new RemoteError("MOUNT_FAILED", name, 'isolate="iframe" precisa de enableIframe(), de vitral/iframe'));
168
- return frames(target, name, props, options).catch(fail);
169
- }
170
- const { module: mod, ...resolved } = await load(name, options.isolate, options.priority);
166
+ const isolate = options.isolate ?? registered(name)?.isolate ?? p.settings().isolate;
167
+ if (isolate === "iframe") return (frames ?? (await import("./reframe-LQL4DZYP.js")).noFrames)(target, name, props, options).catch(fail);
168
+ const { module: mod, ...resolved } = await (isolate == "reframe" ? (await import("./reframe-LQL4DZYP.js")).load : load)(name, isolate, options.priority);
171
169
  const untrack = track(name, resolved);
172
170
  ctxHooks.watch?.(name);
173
- const isolate = options.isolate ?? resolved.isolate ?? protocol().settings().isolate;
174
171
  const label = resolved.name ?? name;
175
172
  const make = (options2) => {
176
173
  const el2 = document.createElement("div", options2);
@@ -182,7 +179,7 @@ async function mountOnce(target, name, props, options) {
182
179
  let t = performance.now();
183
180
  const [release, portal = document.body, shadowRoot = null, registry = customElements, el = target.appendChild(make())] = await injectStyles(
184
181
  resolved,
185
- isolate === "shadow" && target,
182
+ isolate > "q" && target,
186
183
  make,
187
184
  options.styles
188
185
  );
@@ -203,7 +200,7 @@ async function mountOnce(target, name, props, options) {
203
200
  else console.error(err);
204
201
  },
205
202
  navigate,
206
- protocol: protocol().protocol,
203
+ protocol: p.protocol,
207
204
  onActivate: on("vitral:activate"),
208
205
  onDeactivate: on("vitral:deactivate")
209
206
  };
@@ -611,13 +608,17 @@ function serializeShared(values) {
611
608
 
612
609
  export {
613
610
  events,
611
+ scopedEvents,
614
612
  navigate,
615
613
  track,
616
614
  refresh,
615
+ refreshable,
617
616
  checkUpdates,
618
617
  watchUpdates,
619
618
  mountRemote,
620
619
  setFrameMounter,
620
+ mountOnce,
621
+ registered,
621
622
  createSlot,
622
623
  defineRemote,
623
624
  html,
@@ -0,0 +1,93 @@
1
+ // src/dict.ts
2
+ var VARY = "Accept-Encoding, Available-Dictionary";
3
+ var HASH = /-[\w-]{8}(\.[a-z]+)$/;
4
+ var ESPECIAIS = /[\\+*?:{}()]/g;
5
+ function dictMatch(pathname) {
6
+ const i = pathname.lastIndexOf("/") + 1;
7
+ const dir = pathname.slice(0, i).replace(ESPECIAIS, "\\$&");
8
+ const name = pathname.slice(i);
9
+ const m = HASH.exec(name);
10
+ if (!m) return dir + name.replace(ESPECIAIS, "\\$&");
11
+ return dir + name.slice(0, m.index).replace(ESPECIAIS, "\\$&") + "-*" + m[1];
12
+ }
13
+ function accepted(header) {
14
+ const out = /* @__PURE__ */ new Set();
15
+ for (const part of (header ?? "").split(",")) {
16
+ const [name, ...params] = part.trim().toLowerCase().split(";");
17
+ if (!name || params.some((p) => /^\s*q\s*=\s*0(\.0*)?\s*$/.test(p))) continue;
18
+ out.add(name.trim());
19
+ }
20
+ return out;
21
+ }
22
+ function createDict(o) {
23
+ const base = (o.base ?? "/").replace(/\/?$/, "/");
24
+ const cacheControl = o.cacheControl ?? "public, max-age=31536000, immutable";
25
+ const lookup = (pathname) => {
26
+ if (!pathname.startsWith(base)) return void 0;
27
+ let rel;
28
+ try {
29
+ rel = decodeURIComponent(pathname.slice(base.length));
30
+ } catch {
31
+ return void 0;
32
+ }
33
+ return Object.prototype.hasOwnProperty.call(o.index.files, rel) ? o.index.files[rel] : void 0;
34
+ };
35
+ const headers = (pathname) => lookup(pathname) ? { "Use-As-Dictionary": `match="${dictMatch(pathname)}"`, Vary: VARY } : void 0;
36
+ const choose = (pathname, method, available, encoding) => {
37
+ if (method && method !== "GET" && method !== "HEAD") return void 0;
38
+ const file = lookup(pathname);
39
+ const hash = available?.trim();
40
+ if (!file || !hash || !Object.prototype.hasOwnProperty.call(file.deltas, hash)) return void 0;
41
+ const variant = file.deltas[hash];
42
+ const ok = accepted(encoding);
43
+ const enc = variant.dcz && ok.has("dcz") ? "dcz" : variant.dcb && ok.has("dcb") ? "dcb" : void 0;
44
+ if (!enc) return void 0;
45
+ return {
46
+ path: variant[enc],
47
+ head: {
48
+ "Content-Type": file.type,
49
+ "Content-Encoding": enc,
50
+ "Cache-Control": cacheControl,
51
+ "Use-As-Dictionary": `match="${dictMatch(pathname)}"`,
52
+ Vary: VARY
53
+ }
54
+ };
55
+ };
56
+ return {
57
+ headers,
58
+ async fetch(request) {
59
+ const url = new URL(request.url);
60
+ const hit = choose(url.pathname, request.method, request.headers.get("available-dictionary"), request.headers.get("accept-encoding"));
61
+ if (!hit) return void 0;
62
+ const body = await o.read(hit.path);
63
+ if (body == null) return void 0;
64
+ const init = { headers: hit.head, encodeBody: "manual" };
65
+ return new Response(request.method === "HEAD" ? null : body, init);
66
+ },
67
+ node(req, res, next) {
68
+ const pathname = new URL(req.url ?? "/", "http://x").pathname;
69
+ const one = (v) => Array.isArray(v) ? v.join(",") : v;
70
+ const hit = choose(pathname, req.method, one(req.headers["available-dictionary"]), one(req.headers["accept-encoding"]));
71
+ if (!hit) {
72
+ const h = headers(pathname);
73
+ if (h) for (const [k, v] of Object.entries(h)) res.setHeader(k, v);
74
+ return next();
75
+ }
76
+ Promise.resolve(o.read(hit.path)).then(async (body) => {
77
+ if (body == null) {
78
+ const h = headers(pathname);
79
+ for (const [k, v] of Object.entries(h)) res.setHeader(k, v);
80
+ return next();
81
+ }
82
+ const bytes = typeof body === "string" || body instanceof Uint8Array ? body : new Uint8Array(await new Response(body).arrayBuffer());
83
+ res.writeHead(200, hit.head);
84
+ res.end(req.method === "HEAD" ? void 0 : bytes);
85
+ }).catch(next);
86
+ }
87
+ };
88
+ }
89
+
90
+ export {
91
+ dictMatch,
92
+ createDict
93
+ };