vitral 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +357 -2
- package/dist/angular.d.ts +38 -0
- package/dist/angular.js +90 -0
- package/dist/auto.d.ts +2 -0
- package/dist/auto.js +12 -0
- package/dist/chunk-2XF5BKV3.js +190 -0
- package/dist/chunk-6Y3VMWHH.js +181 -0
- package/dist/chunk-DXORSF4X.js +42 -0
- package/dist/chunk-GNPFWISJ.js +80 -0
- package/dist/chunk-HP6QWU6K.js +51 -0
- package/dist/chunk-NCONW6WS.js +606 -0
- package/dist/chunk-PX7PBCCT.js +89 -0
- package/dist/chunk-S5MZT5IT.js +20 -0
- package/dist/chunk-SFL6LTP3.js +56 -0
- package/dist/chunk-UTYAX6MU.js +152 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +88 -0
- package/dist/dedupe-IWFIXRWG.js +81 -0
- package/dist/devtools.d.ts +12 -0
- package/dist/devtools.js +86 -0
- package/dist/element.d.ts +88 -0
- package/dist/element.js +18 -0
- package/dist/errors-KKB8FGCz.d.ts +11 -0
- package/dist/events-DUdjhfps.d.ts +6 -0
- package/dist/expose-53BEJB34.js +14 -0
- package/dist/host-slot-Cz6EiGsB.d.ts +6 -0
- package/dist/iframe.d.ts +28 -0
- package/dist/iframe.js +236 -0
- package/dist/index.d.ts +186 -0
- package/dist/index.js +65 -0
- package/dist/navigate-BRG1WJUM.d.ts +9 -0
- package/dist/overrides-NZAIUGK5.js +15 -0
- package/dist/preact.d.ts +45 -0
- package/dist/preact.js +71 -0
- package/dist/react.d.ts +63 -0
- package/dist/react.js +167 -0
- package/dist/routes.d.ts +56 -0
- package/dist/routes.js +143 -0
- package/dist/shadow-MYRJQLLY.js +72 -0
- package/dist/slot-pB276oPt.d.ts +36 -0
- package/dist/solid.d.ts +47 -0
- package/dist/solid.js +83 -0
- package/dist/sri-BVKZNX5Q.js +114 -0
- package/dist/state-BDdCwc21.d.ts +4 -0
- package/dist/svelte.d.ts +43 -0
- package/dist/svelte.js +42 -0
- package/dist/testing.d.ts +28 -0
- package/dist/testing.js +52 -0
- package/dist/types-BV-hUrms.d.ts +265 -0
- package/dist/vite.d.ts +78 -0
- package/dist/vite.js +786 -0
- package/dist/vue.d.ts +133 -0
- package/dist/vue.js +148 -0
- package/package.json +331 -3
package/dist/iframe.js
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import {
|
|
2
|
+
splitName
|
|
3
|
+
} from "./chunk-HP6QWU6K.js";
|
|
4
|
+
import {
|
|
5
|
+
verifyIntegrity
|
|
6
|
+
} from "./chunk-S5MZT5IT.js";
|
|
7
|
+
import {
|
|
8
|
+
events,
|
|
9
|
+
navigate,
|
|
10
|
+
setFrameMounter,
|
|
11
|
+
track
|
|
12
|
+
} from "./chunk-NCONW6WS.js";
|
|
13
|
+
import {
|
|
14
|
+
resolveEntry
|
|
15
|
+
} from "./chunk-2XF5BKV3.js";
|
|
16
|
+
import "./chunk-SFL6LTP3.js";
|
|
17
|
+
import {
|
|
18
|
+
RemoteError,
|
|
19
|
+
fetchBytes,
|
|
20
|
+
withRetry,
|
|
21
|
+
withTimeout
|
|
22
|
+
} from "./chunk-PX7PBCCT.js";
|
|
23
|
+
import {
|
|
24
|
+
protocol
|
|
25
|
+
} from "./chunk-GNPFWISJ.js";
|
|
26
|
+
|
|
27
|
+
// src/iframe-frame.ts
|
|
28
|
+
var FRAME_RUNTIME = `function (w, load) {
|
|
29
|
+
var parent = w.parent, doc = w.document, handlers = {}, last = {}, channel = {}, mounted = null, stage = 'LOAD_FAILED'
|
|
30
|
+
function post(type, data) { parent.postMessage({ vitral: 1, type: type, data: data }, '*') }
|
|
31
|
+
function call(fn, arg) { try { fn(arg) } catch (e) { w.console.error(e) } }
|
|
32
|
+
var events = {
|
|
33
|
+
emit: function (type, payload) { post('emit', { type: type, payload: payload }) },
|
|
34
|
+
on: function (type, fn) {
|
|
35
|
+
if (!handlers[type]) { handlers[type] = []; post('on', { type: type }) }
|
|
36
|
+
handlers[type].push(fn)
|
|
37
|
+
return function () { handlers[type] = handlers[type].filter(function (h) { return h !== fn }) }
|
|
38
|
+
},
|
|
39
|
+
last: function (type) { return last[type] }
|
|
40
|
+
}
|
|
41
|
+
function start(d) {
|
|
42
|
+
var root = doc.createElement('div')
|
|
43
|
+
root.setAttribute('data-vitral-root', d.name)
|
|
44
|
+
if (d.scope) root.setAttribute('data-vitral-scope', d.scope)
|
|
45
|
+
doc.body.appendChild(root)
|
|
46
|
+
// Ponto de extens\xE3o: uma ponte nova registra handlers em channel[tipo] e
|
|
47
|
+
// acrescenta campos aqui (a Fase 2 acrescenta ctx.shared).
|
|
48
|
+
var ctx = {
|
|
49
|
+
name: d.name, baseUrl: d.baseUrl, events: events, shadowRoot: null, protocol: 1,
|
|
50
|
+
reportError: function (e) { post('report', { message: String((e && e.message) || e) }) },
|
|
51
|
+
navigate: function (url, o) { post('navigate', { url: String(url), replace: !!(o && o.replace) }) }
|
|
52
|
+
}
|
|
53
|
+
Promise.all(d.css.map(function (href) {
|
|
54
|
+
return new Promise(function (done) {
|
|
55
|
+
var link = doc.createElement('link')
|
|
56
|
+
link.rel = 'stylesheet'; link.href = href; link.crossOrigin = 'anonymous'
|
|
57
|
+
link.onload = link.onerror = done
|
|
58
|
+
doc.head.appendChild(link)
|
|
59
|
+
})
|
|
60
|
+
})).then(function () {
|
|
61
|
+
return load(d.code ? w.URL.createObjectURL(new w.Blob([d.code], { type: 'text/javascript' })) : d.url)
|
|
62
|
+
}).then(function (ns) {
|
|
63
|
+
if (!d.key) return ns
|
|
64
|
+
var table = ns.exposes, part = table && table[d.key]
|
|
65
|
+
if (!part) { stage = 'NOT_EXPOSED'; throw new Error(d.name + ' n\xE3o exp\xF5e ' + d.key) }
|
|
66
|
+
return typeof part === 'function' ? part() : part
|
|
67
|
+
}).then(function (ns) {
|
|
68
|
+
var mod = (ns && ns.default) || ns
|
|
69
|
+
if (!mod || typeof mod.mount !== 'function') { stage = 'INVALID_MODULE'; throw new Error('o m\xF3dulo n\xE3o exporta mount()') }
|
|
70
|
+
stage = 'MOUNT_FAILED'
|
|
71
|
+
return mod.mount(root, d.props, ctx)
|
|
72
|
+
}).then(function (handle) {
|
|
73
|
+
mounted = handle
|
|
74
|
+
post('mounted')
|
|
75
|
+
var size = function () { post('size', { height: Math.ceil(doc.body.getBoundingClientRect().height) }) }
|
|
76
|
+
size()
|
|
77
|
+
if (w.ResizeObserver) new w.ResizeObserver(size).observe(doc.body)
|
|
78
|
+
}, function (e) {
|
|
79
|
+
post('error', { code: stage, message: String((e && e.message) || e) })
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
w.addEventListener('message', function (e) {
|
|
83
|
+
var m = e.data
|
|
84
|
+
if (e.source !== parent || !m || m.vitral !== 1) return
|
|
85
|
+
var d = m.data
|
|
86
|
+
if (m.type === 'mount') start(d)
|
|
87
|
+
else if (m.type === 'update') { if (mounted && mounted.update) mounted.update(d) }
|
|
88
|
+
else if (m.type === 'unmount') { if (mounted) mounted.unmount(); mounted = null }
|
|
89
|
+
else if (m.type === 'last') last[d.type] = d.payload
|
|
90
|
+
else if (m.type === 'event') {
|
|
91
|
+
last[d.type] = d.payload
|
|
92
|
+
;(handlers[d.type] || []).slice().forEach(function (h) { call(h, d.payload) })
|
|
93
|
+
} else (channel[m.type] || []).forEach(function (h) { call(h, d) })
|
|
94
|
+
})
|
|
95
|
+
post('ready')
|
|
96
|
+
}`;
|
|
97
|
+
var FRAME_SCRIPT = `(${FRAME_RUNTIME})(window, function (u) { return import(u) })`;
|
|
98
|
+
var FRAME_HTML = `<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0}body{display:flow-root}</style></head><body><script>${FRAME_SCRIPT}</script></body></html>`;
|
|
99
|
+
|
|
100
|
+
// src/iframe.ts
|
|
101
|
+
var CODES = ["LOAD_FAILED", "NOT_EXPOSED", "INVALID_MODULE", "MOUNT_FAILED"];
|
|
102
|
+
function enableIframe(options = {}) {
|
|
103
|
+
const sandbox = options.sandbox ?? "allow-scripts";
|
|
104
|
+
if (/\ballow-same-origin\b/.test(sandbox) && /\ballow-scripts\b/.test(sandbox)) {
|
|
105
|
+
console.warn("[vitral] sandbox com allow-scripts e allow-same-origin n\xE3o isola o remote da p\xE1gina.");
|
|
106
|
+
}
|
|
107
|
+
setFrameMounter((target, name, props, mountOptions) => mountFrame(target, name, props, mountOptions, sandbox, options.bridges ?? []));
|
|
108
|
+
}
|
|
109
|
+
async function mountFrame(target, name, props, options, sandbox, bridges) {
|
|
110
|
+
const p = protocol();
|
|
111
|
+
const { timeout, retries } = p.settings();
|
|
112
|
+
const [remote, key] = splitName(name);
|
|
113
|
+
const entry = p.entry(remote);
|
|
114
|
+
if (!entry) throw new RemoteError("NOT_REGISTERED", name, "remote n\xE3o registrado. Use configure({ remotes }).");
|
|
115
|
+
if (!entry.url) throw new RemoteError("MOUNT_FAILED", name, "remote local (module) n\xE3o roda em iframe: registre a URL dele");
|
|
116
|
+
const { integrity, ...resolved } = await resolveEntry(remote, entry);
|
|
117
|
+
const code = integrity ? await withRetry(retries, async () => {
|
|
118
|
+
const bytes = await fetchBytes(resolved.entry, timeout, name);
|
|
119
|
+
await verifyIntegrity(name, bytes, integrity);
|
|
120
|
+
return bytes;
|
|
121
|
+
}) : void 0;
|
|
122
|
+
track(name, resolved.entry);
|
|
123
|
+
const baseUrl = /^https?:/.test(resolved.entry) ? new URL(".", resolved.entry).href : resolved.entry;
|
|
124
|
+
const frame = document.createElement("iframe");
|
|
125
|
+
frame.setAttribute("sandbox", sandbox);
|
|
126
|
+
frame.setAttribute("data-vitral-root", name);
|
|
127
|
+
frame.title = name;
|
|
128
|
+
frame.style.cssText = "display:block;width:100%;height:0;border:0";
|
|
129
|
+
frame.srcdoc = FRAME_HTML;
|
|
130
|
+
let warned = false;
|
|
131
|
+
const post = (type, data) => {
|
|
132
|
+
const win = frame.contentWindow;
|
|
133
|
+
if (!win) return;
|
|
134
|
+
try {
|
|
135
|
+
win.postMessage({ vitral: 1, type, data }, "*");
|
|
136
|
+
} catch {
|
|
137
|
+
if (!warned) console.warn(`[vitral] ${name}: props e eventos no modo iframe precisam ser serializ\xE1veis; o que n\xE3o \xE9 foi descartado.`);
|
|
138
|
+
warned = true;
|
|
139
|
+
win.postMessage({ vitral: 1, type, data: JSON.parse(JSON.stringify(data ?? null)) }, "*");
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
const subs = /* @__PURE__ */ new Map();
|
|
143
|
+
const channel = /* @__PURE__ */ new Map();
|
|
144
|
+
const port = {
|
|
145
|
+
post,
|
|
146
|
+
on(type, handler) {
|
|
147
|
+
if (!channel.has(type)) channel.set(type, /* @__PURE__ */ new Set());
|
|
148
|
+
channel.get(type).add(handler);
|
|
149
|
+
return () => channel.get(type)?.delete(handler);
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
let settle = null;
|
|
153
|
+
const ready = new Promise((ok, fail) => settle = { ok, fail });
|
|
154
|
+
const onMessage = (e) => {
|
|
155
|
+
const m = e.data;
|
|
156
|
+
if (e.source !== frame.contentWindow || !m || m.vitral !== 1) return;
|
|
157
|
+
const d = m.data;
|
|
158
|
+
switch (m.type) {
|
|
159
|
+
case "ready":
|
|
160
|
+
if (settle) post("mount", { name, key, code, url: resolved.entry, css: resolved.css, scope: resolved.scope, baseUrl, props });
|
|
161
|
+
break;
|
|
162
|
+
case "mounted":
|
|
163
|
+
settle?.ok();
|
|
164
|
+
settle = null;
|
|
165
|
+
break;
|
|
166
|
+
case "error":
|
|
167
|
+
settle?.fail(new RemoteError(CODES.includes(d?.code) ? d.code : "MOUNT_FAILED", name, String(d?.message)));
|
|
168
|
+
settle = null;
|
|
169
|
+
break;
|
|
170
|
+
case "size":
|
|
171
|
+
frame.style.height = `${Math.max(0, Number(d?.height) || 0)}px`;
|
|
172
|
+
break;
|
|
173
|
+
case "emit":
|
|
174
|
+
events.emit(String(d?.type), d?.payload);
|
|
175
|
+
break;
|
|
176
|
+
case "on": {
|
|
177
|
+
const type = String(d?.type);
|
|
178
|
+
if (subs.has(type)) break;
|
|
179
|
+
subs.set(type, events.on(type, (payload) => post("event", { type, payload })));
|
|
180
|
+
const value = events.last(type);
|
|
181
|
+
if (value !== void 0) post("last", { type, payload: value });
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
case "navigate": {
|
|
185
|
+
const url = new URL(String(d?.url), location.href);
|
|
186
|
+
if (url.origin === location.origin) navigate(url.href, { replace: !!d?.replace });
|
|
187
|
+
else console.warn(`[vitral] ${name}: navega\xE7\xE3o para outra origem bloqueada (${url.origin})`);
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
case "report": {
|
|
191
|
+
const err = new RemoteError("RENDER_FAILED", name, String(d?.message));
|
|
192
|
+
if (options.onError) options.onError(err);
|
|
193
|
+
else console.error(err);
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
default:
|
|
197
|
+
for (const h of [...channel.get(m.type) ?? []]) h(d);
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
const disposers = [];
|
|
201
|
+
const cleanup = () => {
|
|
202
|
+
removeEventListener("message", onMessage);
|
|
203
|
+
for (const off of subs.values()) off();
|
|
204
|
+
for (const off of disposers) off();
|
|
205
|
+
subs.clear();
|
|
206
|
+
frame.remove();
|
|
207
|
+
};
|
|
208
|
+
addEventListener("message", onMessage);
|
|
209
|
+
for (const bridge of bridges) {
|
|
210
|
+
const off = bridge(port, { name });
|
|
211
|
+
if (off) disposers.push(off);
|
|
212
|
+
}
|
|
213
|
+
target.appendChild(frame);
|
|
214
|
+
try {
|
|
215
|
+
await withTimeout(timeout, name, ready);
|
|
216
|
+
} catch (e) {
|
|
217
|
+
cleanup();
|
|
218
|
+
throw e;
|
|
219
|
+
}
|
|
220
|
+
let alive = true;
|
|
221
|
+
return {
|
|
222
|
+
update(next) {
|
|
223
|
+
if (alive) post("update", next);
|
|
224
|
+
},
|
|
225
|
+
unmount() {
|
|
226
|
+
if (!alive) return;
|
|
227
|
+
alive = false;
|
|
228
|
+
post("unmount");
|
|
229
|
+
cleanup();
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
export {
|
|
234
|
+
FRAME_SCRIPT,
|
|
235
|
+
enableIframe
|
|
236
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { V as VitralConfig, c as RemoteEntry, P as Props, a as RemoteModule, I as Isolation, b as StyleOptions, M as MountedRemote, E as EventBus, R as RemoteContext, d as SharedValue } from './types-BV-hUrms.js';
|
|
2
|
+
export { C as CheckedProps, H as Handler, e as PropsOf, f as ProvideOptions, g as ProvidedValue, h as RemoteManifest, i as RemoteMetric, j as Remotes, S as Shared, k as SharedApi, l as SharedModules, m as SharedType, n as batch, p as provide, s as shared } from './types-BV-hUrms.js';
|
|
3
|
+
import { R as RemoteError } from './errors-KKB8FGCz.js';
|
|
4
|
+
export { a as RemoteErrorCode } from './errors-KKB8FGCz.js';
|
|
5
|
+
export { R as RemoteSlot, a as SlotOptions, S as SlotStatus, c as createSlot } from './slot-pB276oPt.js';
|
|
6
|
+
export { e as events } from './events-DUdjhfps.js';
|
|
7
|
+
export { n as navigate } from './navigate-BRG1WJUM.js';
|
|
8
|
+
export { r as __reset } from './state-BDdCwc21.js';
|
|
9
|
+
|
|
10
|
+
/** Configura a lib. Pode ser chamada mais de uma vez; as opções são mescladas. */
|
|
11
|
+
declare function configure(config: VitralConfig): void;
|
|
12
|
+
/** Registra (ou substitui) um remote. */
|
|
13
|
+
declare function register(name: string, entry: string | RemoteEntry): void;
|
|
14
|
+
/**
|
|
15
|
+
* Registra a URL de um `src` e devolve o nome para montar. Sem `name`, o nome
|
|
16
|
+
* é a própria URL (e o nome do manifesto vira um apelido). Com `trusted`, a
|
|
17
|
+
* origem entra na allowlist mesmo com `allowedOrigins`: vale para URL escrita
|
|
18
|
+
* no código do host, nunca para URL vinda de dados.
|
|
19
|
+
*/
|
|
20
|
+
declare function registerSrc(src: string, name?: string, trusted?: boolean): string;
|
|
21
|
+
|
|
22
|
+
/** Uma chave do store vista de fora (devtools, SSR, ponte do iframe). */
|
|
23
|
+
interface SharedEntry {
|
|
24
|
+
key: string;
|
|
25
|
+
value: unknown;
|
|
26
|
+
/** `false` quando só o dono altera. */
|
|
27
|
+
writable: boolean;
|
|
28
|
+
/** Quem é o dono (nome do remote; vazio para o host ou sem nome). */
|
|
29
|
+
owner?: string;
|
|
30
|
+
/** Quem fez a última mudança. */
|
|
31
|
+
by?: string;
|
|
32
|
+
/** Quantas assinaturas ativas a chave tem. */
|
|
33
|
+
subscribers: number;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Store guardado no protocolo global (`globalThis[Symbol.for('vitral')].shared()`).
|
|
37
|
+
* Faz parte do protocolo v1: só cresce com métodos novos.
|
|
38
|
+
*/
|
|
39
|
+
interface SharedStore {
|
|
40
|
+
/** Valor atual; `undefined` se a chave ainda não tem valor. */
|
|
41
|
+
get(key: string): unknown;
|
|
42
|
+
/**
|
|
43
|
+
* Troca o valor. Passa se `token` é o do dono ou se a chave aceita escrita.
|
|
44
|
+
* Devolve `false` quando a escrita foi bloqueada (`writable: false`).
|
|
45
|
+
*/
|
|
46
|
+
set(key: string, value: unknown, token?: object, by?: string): boolean;
|
|
47
|
+
/** Chama `fn(valor)` a cada mudança (não na hora). Devolve a função que cancela. */
|
|
48
|
+
subscribe(key: string, fn: (value: unknown) => void): () => void;
|
|
49
|
+
/**
|
|
50
|
+
* Torna-se dono de uma chave sem dono e grava `value` (um valor vindo do
|
|
51
|
+
* SSR ainda não reivindicado é mantido). Devolve o token do dono, ou
|
|
52
|
+
* `undefined` se a chave já tem dono.
|
|
53
|
+
*/
|
|
54
|
+
own(key: string, value: unknown, writable: boolean, by?: string): object | undefined;
|
|
55
|
+
/** O dono larga a chave. O valor fica e a chave volta a aceitar escrita. */
|
|
56
|
+
release(key: string, token: object): void;
|
|
57
|
+
/** Roda `fn` e notifica cada chave alterada uma vez só, no fim. */
|
|
58
|
+
batch<R>(fn: () => R): R;
|
|
59
|
+
/** Chaves com valor e seus metadados. */
|
|
60
|
+
list(): SharedEntry[];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Baixa (uma vez só) e devolve o módulo de um remote. */
|
|
64
|
+
declare function loadRemote<P extends Props = Props>(name: string): Promise<RemoteModule<P>>;
|
|
65
|
+
/**
|
|
66
|
+
* Carrega um módulo sem UI que um remote expõe (funções, stores, constantes),
|
|
67
|
+
* com a mesma verificação de SRI e de origem dos componentes.
|
|
68
|
+
*
|
|
69
|
+
* const api = await loadModule<typeof import('./tipos/carrinho-api')>('carrinho/api')
|
|
70
|
+
*
|
|
71
|
+
* Sem `/`, devolve os exports do arquivo principal do remote.
|
|
72
|
+
*/
|
|
73
|
+
declare function loadModule<T = Record<string, unknown>>(name: string): Promise<T>;
|
|
74
|
+
/** Começa a baixar um remote (JS e CSS) sem montar (ex.: no hover de um link). */
|
|
75
|
+
declare function preload(name: string): void;
|
|
76
|
+
|
|
77
|
+
interface MountOptions {
|
|
78
|
+
isolate?: Isolation;
|
|
79
|
+
/** `'high'` para o remote que aparece logo ao abrir a página: vira `fetchpriority` nos downloads. */
|
|
80
|
+
priority?: RequestPriority;
|
|
81
|
+
/** Estilos do host no Shadow DOM (somados aos do `configure`). */
|
|
82
|
+
styles?: StyleOptions;
|
|
83
|
+
/** Chamado quando o remote quebra depois de montado (erro de render, handler etc.). */
|
|
84
|
+
onError?: (error: RemoteError) => void;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Monta um remote dentro de `target`. Devolve um handle com `update` e
|
|
88
|
+
* `unmount` que funciona igual para qualquer framework. Com `refresh(nome)`,
|
|
89
|
+
* o mesmo handle passa a apontar para a versão nova.
|
|
90
|
+
*/
|
|
91
|
+
declare function mountRemote<P extends Props = Props>(target: HTMLElement, name: string, props: P, options?: MountOptions): Promise<MountedRemote<P>>;
|
|
92
|
+
|
|
93
|
+
/** Declara um remote. É só uma função identidade com tipos. */
|
|
94
|
+
declare function defineRemote<P extends Props = Props>(remote: RemoteModule<P>): RemoteModule<P>;
|
|
95
|
+
|
|
96
|
+
declare function createEventBus(): EventBus;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Troca as montagens de `name` (e das partes, `name/…`) pela versão que o
|
|
100
|
+
* manifesto aponta agora, sem recarregar a página. Rejeita se a versão nova
|
|
101
|
+
* não carregar; nesse caso nada muda na tela.
|
|
102
|
+
*/
|
|
103
|
+
declare function refresh(name: string): Promise<void>;
|
|
104
|
+
/**
|
|
105
|
+
* Revalida os manifestos dos remotes montados e emite
|
|
106
|
+
* `vitral:update-available` com `{ name, entry }` para cada deploy novo.
|
|
107
|
+
* Devolve os nomes com versão nova. Falhas de rede são ignoradas.
|
|
108
|
+
*/
|
|
109
|
+
declare function checkUpdates(): Promise<string[]>;
|
|
110
|
+
/**
|
|
111
|
+
* Revalida os manifestos a cada navegação (`popstate`, que `navigate()` também
|
|
112
|
+
* dispara, e a Navigation API onde existe). Devolve a função que desliga.
|
|
113
|
+
*/
|
|
114
|
+
declare function watchUpdates(): () => void;
|
|
115
|
+
|
|
116
|
+
declare const TEMPLATE: unique symbol;
|
|
117
|
+
interface Template {
|
|
118
|
+
readonly [TEMPLATE]: true;
|
|
119
|
+
readonly strings: readonly string[];
|
|
120
|
+
readonly values: readonly unknown[];
|
|
121
|
+
}
|
|
122
|
+
/** Template HTML seguro: tudo que é interpolado é escapado, menos outro `html`. */
|
|
123
|
+
declare function html(strings: TemplateStringsArray, ...values: unknown[]): Template;
|
|
124
|
+
/** Marca uma string como HTML confiável (não é escapada). Use com cuidado. */
|
|
125
|
+
declare function unsafeHTML(markup: string): Template;
|
|
126
|
+
/** Renderiza `view` dentro de `el`, ligando os listeners declarados com `on<evento>=${fn}`. */
|
|
127
|
+
declare function render(view: Template | string | Node, el: Element): void;
|
|
128
|
+
type View = Template | string | Node | null | undefined;
|
|
129
|
+
type RenderFn<P extends Props, S extends object> = (props: P, state: S, ctx: RemoteContext) => View;
|
|
130
|
+
interface ComponentOptions<P extends Props, S extends object> {
|
|
131
|
+
/** Estado local. Alterar qualquer campo renderiza de novo. */
|
|
132
|
+
state?: S | (() => S);
|
|
133
|
+
render: RenderFn<P, S>;
|
|
134
|
+
/** Roda depois do primeiro render. Pode devolver uma função de limpeza. */
|
|
135
|
+
mounted?(el: HTMLElement, state: S, ctx: RemoteContext): void | (() => void);
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Cria um remote a partir de uma função que devolve HTML. Aceita só a função
|
|
139
|
+
* (`component((props) => html\`...\`)`) ou `{ state, render, mounted }`.
|
|
140
|
+
*/
|
|
141
|
+
declare function component<P extends Props = Props, S extends object = Record<string, never>>(options: RenderFn<P, S> | ComponentOptions<P, S>): RemoteModule<P>;
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Extras opcionais do contexto compartilhado: guardar no storage, espelhar
|
|
145
|
+
* entre abas e levar os valores do SSR para o cliente. Ficam fora do store
|
|
146
|
+
* para só entrar no bundle de quem usa.
|
|
147
|
+
*/
|
|
148
|
+
|
|
149
|
+
interface PersistOptions {
|
|
150
|
+
/** `'local'` (padrão) ou `'session'`. */
|
|
151
|
+
storage?: 'local' | 'session';
|
|
152
|
+
/**
|
|
153
|
+
* Versão do formato do valor. Faz parte da chave do storage
|
|
154
|
+
* (`vitral:<chave>:<versão>`): ao mudar o formato, suba a versão e o valor
|
|
155
|
+
* antigo é ignorado. Padrão `1`.
|
|
156
|
+
*/
|
|
157
|
+
version?: string | number;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Restaura o valor guardado (se houver) e guarda cada mudança. Valores que
|
|
161
|
+
* não viram JSON (funções, por exemplo) não são guardados. Sem storage
|
|
162
|
+
* (servidor, modo privado bloqueado), não faz nada.
|
|
163
|
+
*
|
|
164
|
+
* const carrinho = persist(provide('carrinho', []), { version: 2 })
|
|
165
|
+
*/
|
|
166
|
+
declare function persist<V extends SharedValue<any>>(value: V, options?: PersistOptions): V;
|
|
167
|
+
/**
|
|
168
|
+
* Espelha as mudanças entre abas da mesma origem (`BroadcastChannel`). Chame
|
|
169
|
+
* nas duas abas, de preferência no dono da chave: assim a mudança que chega
|
|
170
|
+
* passa mesmo com `writable: false`. Só dados que o `structuredClone` aceita
|
|
171
|
+
* atravessam. Junto com `persist`, uma aba nova começa com o último valor.
|
|
172
|
+
*
|
|
173
|
+
* const carrinho = syncTabs(persist(provide('carrinho', [])))
|
|
174
|
+
*/
|
|
175
|
+
declare function syncTabs<V extends SharedValue<any>>(value: V): V;
|
|
176
|
+
/**
|
|
177
|
+
* SSR: transforma os valores de uma requisição no conteúdo do
|
|
178
|
+
* `<script type="application/json" data-vitral-shared>`, que o store do
|
|
179
|
+
* cliente lê antes dos remotes montarem. Funções e `undefined` ficam de fora;
|
|
180
|
+
* `<` é escapado para o JSON não fechar o `<script>`.
|
|
181
|
+
*
|
|
182
|
+
* `<script type="application/json" data-vitral-shared>${serializeShared({ usuario })}</script>`
|
|
183
|
+
*/
|
|
184
|
+
declare function serializeShared(values: Record<string, unknown>): string;
|
|
185
|
+
|
|
186
|
+
export { type ComponentOptions, EventBus, Isolation, type MountOptions, MountedRemote, type PersistOptions, Props, RemoteContext, RemoteEntry, RemoteError, RemoteModule, type RenderFn, type SharedEntry, type SharedStore, SharedValue, StyleOptions, type Template, type View, VitralConfig, checkUpdates, component, configure, createEventBus, defineRemote, html, loadModule, loadRemote, mountRemote, persist, preload, refresh, register, registerSrc, render, serializeShared, syncTabs, unsafeHTML, watchUpdates };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import {
|
|
2
|
+
batch,
|
|
3
|
+
checkUpdates,
|
|
4
|
+
component,
|
|
5
|
+
createSlot,
|
|
6
|
+
defineRemote,
|
|
7
|
+
events,
|
|
8
|
+
html,
|
|
9
|
+
mountRemote,
|
|
10
|
+
navigate,
|
|
11
|
+
persist,
|
|
12
|
+
provide,
|
|
13
|
+
refresh,
|
|
14
|
+
render,
|
|
15
|
+
serializeShared,
|
|
16
|
+
shared,
|
|
17
|
+
syncTabs,
|
|
18
|
+
unsafeHTML,
|
|
19
|
+
watchUpdates
|
|
20
|
+
} from "./chunk-NCONW6WS.js";
|
|
21
|
+
import {
|
|
22
|
+
configure,
|
|
23
|
+
loadModule,
|
|
24
|
+
loadRemote,
|
|
25
|
+
preload,
|
|
26
|
+
register,
|
|
27
|
+
registerSrc
|
|
28
|
+
} from "./chunk-2XF5BKV3.js";
|
|
29
|
+
import "./chunk-SFL6LTP3.js";
|
|
30
|
+
import {
|
|
31
|
+
RemoteError
|
|
32
|
+
} from "./chunk-PX7PBCCT.js";
|
|
33
|
+
import {
|
|
34
|
+
createEventBus,
|
|
35
|
+
resetProtocol
|
|
36
|
+
} from "./chunk-GNPFWISJ.js";
|
|
37
|
+
export {
|
|
38
|
+
RemoteError,
|
|
39
|
+
resetProtocol as __reset,
|
|
40
|
+
batch,
|
|
41
|
+
checkUpdates,
|
|
42
|
+
component,
|
|
43
|
+
configure,
|
|
44
|
+
createEventBus,
|
|
45
|
+
createSlot,
|
|
46
|
+
defineRemote,
|
|
47
|
+
events,
|
|
48
|
+
html,
|
|
49
|
+
loadModule,
|
|
50
|
+
loadRemote,
|
|
51
|
+
mountRemote,
|
|
52
|
+
navigate,
|
|
53
|
+
persist,
|
|
54
|
+
preload,
|
|
55
|
+
provide,
|
|
56
|
+
refresh,
|
|
57
|
+
register,
|
|
58
|
+
registerSrc,
|
|
59
|
+
render,
|
|
60
|
+
serializeShared,
|
|
61
|
+
shared,
|
|
62
|
+
syncTabs,
|
|
63
|
+
unsafeHTML,
|
|
64
|
+
watchUpdates
|
|
65
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Navega mantendo todos os roteadores em sincronia (React Router, Vue Router,
|
|
3
|
+
* Next, Nuxt…): atualiza o histórico e dispara `popstate`, que todos escutam.
|
|
4
|
+
*/
|
|
5
|
+
declare function navigate(url: string, options?: {
|
|
6
|
+
replace?: boolean;
|
|
7
|
+
}): void;
|
|
8
|
+
|
|
9
|
+
export { navigate as n };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {
|
|
2
|
+
OVERRIDES_KEY,
|
|
3
|
+
manifestUrl,
|
|
4
|
+
overrideFor,
|
|
5
|
+
overridesEnabled,
|
|
6
|
+
storedOverrides
|
|
7
|
+
} from "./chunk-DXORSF4X.js";
|
|
8
|
+
import "./chunk-GNPFWISJ.js";
|
|
9
|
+
export {
|
|
10
|
+
OVERRIDES_KEY,
|
|
11
|
+
manifestUrl,
|
|
12
|
+
overrideFor,
|
|
13
|
+
overridesEnabled,
|
|
14
|
+
storedOverrides
|
|
15
|
+
};
|
package/dist/preact.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import * as preact from 'preact';
|
|
2
|
+
import { VNode, ComponentType } from 'preact';
|
|
3
|
+
import { P as Props, I as Isolation, C as CheckedProps, a as RemoteModule, S as Shared } from './types-BV-hUrms.js';
|
|
4
|
+
export { R as RemoteContext } from './types-BV-hUrms.js';
|
|
5
|
+
import { R as RemoteError } from './errors-KKB8FGCz.js';
|
|
6
|
+
import { S as SlotStatus } from './slot-pB276oPt.js';
|
|
7
|
+
export { e as events } from './events-DUdjhfps.js';
|
|
8
|
+
export { n as navigate } from './navigate-BRG1WJUM.js';
|
|
9
|
+
import { L as Loading, P as Prefetch } from './host-slot-Cz6EiGsB.js';
|
|
10
|
+
|
|
11
|
+
/** Transforma um componente Preact em um remote. O contexto chega na prop `vitral`. */
|
|
12
|
+
declare function preactRemote<P extends Props>(Component: ComponentType<any>): RemoteModule<P>;
|
|
13
|
+
interface RemoteProps<P extends Props = Props> {
|
|
14
|
+
name?: string;
|
|
15
|
+
/** URL do `vitral.json`. Dispensa `configure()`. */
|
|
16
|
+
src?: string;
|
|
17
|
+
props?: P;
|
|
18
|
+
isolate?: Isolation;
|
|
19
|
+
fallback?: VNode | string | null;
|
|
20
|
+
error?: VNode | string | ((error: RemoteError, retry: () => void) => VNode | string | null) | null;
|
|
21
|
+
class?: string;
|
|
22
|
+
/** `'lazy'`: monta perto de aparecer na tela; `'idle'`: com o navegador ocioso. */
|
|
23
|
+
loading?: Loading;
|
|
24
|
+
/** Baixa antes de montar: `'hover'`, `'visible'` ou `'idle'`. */
|
|
25
|
+
prefetch?: Prefetch;
|
|
26
|
+
/** `'high'` para o remote que aparece logo ao abrir a página. */
|
|
27
|
+
priority?: RequestPriority;
|
|
28
|
+
/** Ao sair da página, guarda o remote montado (LRU, padrão 5) e na volta só atualiza as props. */
|
|
29
|
+
keepAlive?: boolean | number;
|
|
30
|
+
}
|
|
31
|
+
declare function Remote<P extends Props = Props, N extends string = string>({ name, src, props, isolate, fallback, error, loading, prefetch, priority, keepAlive, ...rest }: RemoteProps<P> & CheckedProps<N>): VNode<preact.ClassAttributes<HTMLElement> & {
|
|
32
|
+
'data-vitral': string | undefined;
|
|
33
|
+
'data-vitral-status': SlotStatus;
|
|
34
|
+
class: (RemoteProps<P> & CheckedProps<N>)["class"] | undefined;
|
|
35
|
+
}>;
|
|
36
|
+
/**
|
|
37
|
+
* Lê uma chave compartilhada (`provide`/`shared`) e re-renderiza só quando
|
|
38
|
+
* ela muda. Com seletor, só quando o resultado do seletor muda (`Object.is`).
|
|
39
|
+
*
|
|
40
|
+
* const nome = useShared('usuario', (u) => u?.nome)
|
|
41
|
+
*/
|
|
42
|
+
declare function useShared<K extends keyof Shared, S = Shared[K] | undefined>(key: K, select?: (value: Shared[K] | undefined) => S): S;
|
|
43
|
+
declare function useShared<T = unknown, S = T | undefined>(key: string, select?: (value: T | undefined) => S): S;
|
|
44
|
+
|
|
45
|
+
export { Remote, type RemoteProps, preactRemote, useShared };
|
package/dist/preact.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import {
|
|
2
|
+
hostSlot
|
|
3
|
+
} from "./chunk-UTYAX6MU.js";
|
|
4
|
+
import {
|
|
5
|
+
defineRemote,
|
|
6
|
+
events,
|
|
7
|
+
memoSelect,
|
|
8
|
+
navigate,
|
|
9
|
+
same,
|
|
10
|
+
shared
|
|
11
|
+
} from "./chunk-NCONW6WS.js";
|
|
12
|
+
import "./chunk-2XF5BKV3.js";
|
|
13
|
+
import "./chunk-SFL6LTP3.js";
|
|
14
|
+
import "./chunk-PX7PBCCT.js";
|
|
15
|
+
import "./chunk-GNPFWISJ.js";
|
|
16
|
+
|
|
17
|
+
// src/preact.ts
|
|
18
|
+
import { h, render } from "preact";
|
|
19
|
+
import { useEffect, useMemo, useRef, useState } from "preact/hooks";
|
|
20
|
+
function preactRemote(Component) {
|
|
21
|
+
return defineRemote({
|
|
22
|
+
mount(el, props, ctx) {
|
|
23
|
+
const draw = (p) => render(h(Component, { ...p, vitral: ctx }), el);
|
|
24
|
+
draw(props);
|
|
25
|
+
return { update: draw, unmount: () => render(null, el) };
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
function Remote({ name, src, props, isolate, fallback = null, error = null, loading, prefetch, priority, keepAlive, ...rest }) {
|
|
30
|
+
const ref = useRef(null);
|
|
31
|
+
const slot = useRef(null);
|
|
32
|
+
const [state, setState] = useState({ status: "loading", error: null });
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
slot.current = hostSlot(ref.current, { name, src, props: props ?? {}, isolate, loading, prefetch, priority, keepAlive, onStatus: (status, error2) => setState({ status, error: error2 }) });
|
|
35
|
+
return () => slot.current?.destroy();
|
|
36
|
+
}, []);
|
|
37
|
+
useEffect(() => slot.current?.set({ name, src, isolate }), [name, src, isolate]);
|
|
38
|
+
useEffect(() => slot.current?.update(props ?? {}), [props]);
|
|
39
|
+
const retry = () => slot.current?.retry();
|
|
40
|
+
return h("div", { "data-vitral": name ?? src, "data-vitral-status": state.status, class: rest.class }, [
|
|
41
|
+
state.status === "loading" ? fallback : null,
|
|
42
|
+
state.status === "error" && state.error ? typeof error === "function" ? error(state.error, retry) : error : null,
|
|
43
|
+
// O Preact nunca coloca filhos neste div: ele pertence ao remote.
|
|
44
|
+
h("div", { ref })
|
|
45
|
+
]);
|
|
46
|
+
}
|
|
47
|
+
function useShared(key, select = same) {
|
|
48
|
+
const handle = useMemo(() => shared(key), [key]);
|
|
49
|
+
const [pick] = useState(memoSelect);
|
|
50
|
+
const [, rerender] = useState(0);
|
|
51
|
+
const value = pick(handle.get(), select);
|
|
52
|
+
const latest = useRef({ value, select });
|
|
53
|
+
latest.current = { value, select };
|
|
54
|
+
useEffect(
|
|
55
|
+
() => (
|
|
56
|
+
// subscribe chama na hora: pega mudanças entre o render e o efeito.
|
|
57
|
+
handle.subscribe((v) => {
|
|
58
|
+
if (!Object.is(pick(v, latest.current.select), latest.current.value)) rerender((n) => n + 1);
|
|
59
|
+
})
|
|
60
|
+
),
|
|
61
|
+
[handle]
|
|
62
|
+
);
|
|
63
|
+
return value;
|
|
64
|
+
}
|
|
65
|
+
export {
|
|
66
|
+
Remote,
|
|
67
|
+
events,
|
|
68
|
+
navigate,
|
|
69
|
+
preactRemote,
|
|
70
|
+
useShared
|
|
71
|
+
};
|