@openelement/core 0.41.0-alpha.1
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 +36 -0
- package/package.json +124 -0
- package/src/binding-activation.d.ts +2 -0
- package/src/binding-activation.js +254 -0
- package/src/binding-descriptor.d.ts +79 -0
- package/src/binding-descriptor.js +9 -0
- package/src/context.d.ts +32 -0
- package/src/context.js +76 -0
- package/src/csr.d.ts +13 -0
- package/src/csr.js +14 -0
- package/src/dsd-hydration-events.d.ts +2 -0
- package/src/dsd-hydration-events.js +13 -0
- package/src/dsd-hydration.d.ts +40 -0
- package/src/dsd-hydration.js +48 -0
- package/src/errors.d.ts +54 -0
- package/src/errors.js +113 -0
- package/src/event-hydration.d.ts +12 -0
- package/src/event-hydration.js +118 -0
- package/src/event-marker.d.ts +7 -0
- package/src/event-marker.js +54 -0
- package/src/html-escape.d.ts +29 -0
- package/src/html-escape.js +126 -0
- package/src/hydrate.d.ts +15 -0
- package/src/hydrate.js +15 -0
- package/src/index.d.ts +58 -0
- package/src/index.js +46 -0
- package/src/island-transform.d.ts +14 -0
- package/src/island-transform.js +60 -0
- package/src/island.d.ts +59 -0
- package/src/island.js +342 -0
- package/src/isr-runtime.d.ts +28 -0
- package/src/isr-runtime.js +99 -0
- package/src/isr.d.ts +22 -0
- package/src/isr.js +41 -0
- package/src/jsx-render-dom.d.ts +22 -0
- package/src/jsx-render-dom.js +376 -0
- package/src/jsx-runtime.d.ts +58 -0
- package/src/jsx-runtime.js +99 -0
- package/src/jsx-types.d.ts +46 -0
- package/src/logger.d.ts +15 -0
- package/src/logger.js +24 -0
- package/src/prop.d.ts +24 -0
- package/src/prop.js +160 -0
- package/src/registry.d.ts +17 -0
- package/src/registry.js +219 -0
- package/src/render-dsd-stream.d.ts +46 -0
- package/src/render-dsd-stream.js +86 -0
- package/src/render-dsd.d.ts +27 -0
- package/src/render-dsd.js +315 -0
- package/src/render-ir.d.ts +32 -0
- package/src/render-ir.js +245 -0
- package/src/runtime.d.ts +9 -0
- package/src/runtime.js +16 -0
- package/src/security.d.ts +1 -0
- package/src/security.js +40 -0
- package/src/signal-context.d.ts +15 -0
- package/src/signal-context.js +59 -0
- package/src/static.d.ts +35 -0
- package/src/static.js +34 -0
- package/src/style-sheet.d.ts +9 -0
- package/src/style-sheet.js +56 -0
- package/src/tag-utils.d.ts +11 -0
- package/src/tag-utils.js +28 -0
- package/src/vnode.d.ts +15 -0
- package/src/vnode.js +31 -0
package/src/island.js
ADDED
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
import { ERROR_PREFIX } from '@openelement/core';
|
|
2
|
+
import { formatError } from './errors.js';
|
|
3
|
+
/**
|
|
4
|
+
* @openelement/core - defineIsland() wrapper
|
|
5
|
+
*
|
|
6
|
+
* v0.6.2: defineIsland() wraps any Custom Element class to provide:
|
|
7
|
+
* - Automatic registration via customElements.define()
|
|
8
|
+
* - Hydration strategy support (load, idle, visible, only)
|
|
9
|
+
* - __island / __tagName / __layer metadata markers
|
|
10
|
+
* - data-ssr-props restoration on client upgrade
|
|
11
|
+
* - DSD opt-out via `dsd: false` (Pure Island / Layer 3)
|
|
12
|
+
*
|
|
13
|
+
* Framework-agnostic: works with Lit, vanilla Custom Elements,
|
|
14
|
+
* FAST, or any Web Component library. bindSsrProps() sets props
|
|
15
|
+
* directly; adapters handle framework-specific update triggers.
|
|
16
|
+
*
|
|
17
|
+
* v0.29.1: defineCustomElement helper inlined from custom-element.ts.
|
|
18
|
+
*/ import { createLogger } from './logger.js';
|
|
19
|
+
/** WeakSet to track elements that have already had SSR props bound (idempotent). */ const ssrPropsBoundSet = new WeakSet();
|
|
20
|
+
const log = createLogger('core');
|
|
21
|
+
/**
|
|
22
|
+
* SSR-safe custom element registration helper.
|
|
23
|
+
* v0.29.1: Merged from custom-element.ts.
|
|
24
|
+
*/ export function defineCustomElement(tag, ctor) {
|
|
25
|
+
if (typeof globalThis.customElements === 'undefined') return;
|
|
26
|
+
if (!globalThis.customElements.get(tag)) {
|
|
27
|
+
globalThis.customElements.define(tag, ctor);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const VALID_STRATEGIES = new Set([
|
|
31
|
+
'load',
|
|
32
|
+
'idle',
|
|
33
|
+
'visible',
|
|
34
|
+
'only'
|
|
35
|
+
]);
|
|
36
|
+
// Module-level store of active visibility strategy timeout IDs.
|
|
37
|
+
// Used for test cleanup - tests can call _clearAllVisibilityTimeouts()
|
|
38
|
+
// to prevent timer leaks.
|
|
39
|
+
const _visibilityTimeouts = new Set();
|
|
40
|
+
const _islandMeta = new WeakMap();
|
|
41
|
+
export function getIslandMeta(ctor) {
|
|
42
|
+
return _islandMeta.get(ctor);
|
|
43
|
+
}
|
|
44
|
+
/** Clear all active visibility strategy timeouts (for test cleanup). */ export function _clearAllVisibilityTimeouts() {
|
|
45
|
+
for (const id of _visibilityTimeouts){
|
|
46
|
+
clearTimeout(id);
|
|
47
|
+
}
|
|
48
|
+
_visibilityTimeouts.clear();
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Get the value of the data-ssr-props attribute from a host element.
|
|
52
|
+
* Used to reconstruct SSR props on client upgrade.
|
|
53
|
+
*
|
|
54
|
+
* @param el - The custom element host element
|
|
55
|
+
* @returns Parsed props object, or null if no data-ssr-props attribute
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```ts
|
|
59
|
+
* connectedCallback() {
|
|
60
|
+
* super.connectedCallback();
|
|
61
|
+
* const props = getSsrProps(this);
|
|
62
|
+
* if (props) {
|
|
63
|
+
* this.count = props.count ?? 0;
|
|
64
|
+
* }
|
|
65
|
+
* }
|
|
66
|
+
* ```
|
|
67
|
+
*/ export function getSsrProps(el) {
|
|
68
|
+
const raw = el.getAttribute('data-ssr-props');
|
|
69
|
+
if (!raw) return null;
|
|
70
|
+
try {
|
|
71
|
+
return JSON.parse(raw);
|
|
72
|
+
} catch {
|
|
73
|
+
log.warn(`Failed to parse data-ssr-props on <${el.tagName.toLowerCase()}>`);
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Apply SSR props from data-ssr-props to a component instance.
|
|
79
|
+
* This restores server-rendered property values to the
|
|
80
|
+
* client-side component on upgrade, ensuring consistency between
|
|
81
|
+
* SSR and client state.
|
|
82
|
+
*
|
|
83
|
+
* v0.6.2: Framework-agnostic. No Lit-specific detection.
|
|
84
|
+
* Props are set directly on the instance. DSD hydration and VNode event
|
|
85
|
+
* markers are handled at the component level via DsdElement.
|
|
86
|
+
*
|
|
87
|
+
* v0.14.3: Prototype pollution fix - filters dangerous keys
|
|
88
|
+
* (__proto__, constructor, prototype) from parsed SSR props.
|
|
89
|
+
*
|
|
90
|
+
* @param el - The upgraded custom element
|
|
91
|
+
*/ /** Keys that could cause prototype pollution if assigned to an object instance.
|
|
92
|
+
* v0.14.7: Extended to cover all Object.prototype methods that could be
|
|
93
|
+
* exploited via arbitrary property assignment (C-03 fix).
|
|
94
|
+
*/ import { DANGEROUS_KEYS } from './security.js';
|
|
95
|
+
export function bindSsrProps(el) {
|
|
96
|
+
const props = getSsrProps(el);
|
|
97
|
+
if (!props) return;
|
|
98
|
+
for (const [key, value] of Object.entries(props)){
|
|
99
|
+
// v0.14.3: Prevent prototype pollution - skip dangerous keys
|
|
100
|
+
if (DANGEROUS_KEYS.has(key)) {
|
|
101
|
+
log.warn(`Skipping dangerous key "${key}" in data-ssr-props on <${el.tagName.toLowerCase()}>`);
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
el[key] = value;
|
|
106
|
+
} catch (e) {
|
|
107
|
+
// Some properties may be read-only - safe to skip, but log for debuggability
|
|
108
|
+
log.debug(`Cannot set read-only property "${key}" on <${el.tagName.toLowerCase()}>: ${formatError(e)}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Create an IntersectionObserver-based upgrade strategy.
|
|
114
|
+
* v0.6': Uses querySelectorAll to observe ALL instances of the tag,
|
|
115
|
+
* not just the first one. Per WHATWG IntersectionObserver spec.
|
|
116
|
+
*/ function createVisibleStrategy(tagName, registerFn) {
|
|
117
|
+
let registered = false;
|
|
118
|
+
const observer = new IntersectionObserver((entries)=>{
|
|
119
|
+
for (const entry of entries){
|
|
120
|
+
if (entry.isIntersecting) {
|
|
121
|
+
observer.disconnect();
|
|
122
|
+
if (!registered) {
|
|
123
|
+
registered = true;
|
|
124
|
+
clearTimeout(timeoutId);
|
|
125
|
+
_visibilityTimeouts.delete(timeoutId);
|
|
126
|
+
registerFn();
|
|
127
|
+
}
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}, {
|
|
132
|
+
rootMargin: '200px'
|
|
133
|
+
});
|
|
134
|
+
// We need to wait for elements to be in the DOM first
|
|
135
|
+
const observeAll = ()=>{
|
|
136
|
+
const elements = document.querySelectorAll(tagName);
|
|
137
|
+
if (elements.length > 0) {
|
|
138
|
+
elements.forEach((el)=>observer.observe(el));
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
return false;
|
|
142
|
+
};
|
|
143
|
+
const mo = new MutationObserver((_mutations, mutObs)=>{
|
|
144
|
+
if (observeAll()) {
|
|
145
|
+
mutObs.disconnect();
|
|
146
|
+
} else {
|
|
147
|
+
// v0.14.5: If elements were removed from DOM, disconnect all observers
|
|
148
|
+
const elements = document.querySelectorAll(tagName);
|
|
149
|
+
if (elements.length === 0 && !registered) {
|
|
150
|
+
mutObs.disconnect();
|
|
151
|
+
observer.disconnect();
|
|
152
|
+
clearTimeout(timeoutId);
|
|
153
|
+
_visibilityTimeouts.delete(timeoutId);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
// v0.14.3: Timeout guard - if the target element never appears
|
|
158
|
+
// (e.g., route changed after island was registered), disconnect
|
|
159
|
+
// both observers after 30 seconds to prevent memory/perf leaks.
|
|
160
|
+
const VISIBILITY_TIMEOUT = 30_000; // 30s
|
|
161
|
+
const timeoutId = setTimeout(()=>{
|
|
162
|
+
_visibilityTimeouts.delete(timeoutId);
|
|
163
|
+
if (!registered) {
|
|
164
|
+
mo.disconnect();
|
|
165
|
+
observer.disconnect();
|
|
166
|
+
log.debug(`Visibility strategy for <${tagName}> timed out after ${VISIBILITY_TIMEOUT}ms`);
|
|
167
|
+
}
|
|
168
|
+
}, VISIBILITY_TIMEOUT);
|
|
169
|
+
_visibilityTimeouts.add(timeoutId);
|
|
170
|
+
// Start observing after DOM content loaded
|
|
171
|
+
if (document.readyState === 'loading') {
|
|
172
|
+
document.addEventListener('DOMContentLoaded', ()=>{
|
|
173
|
+
if (!observeAll()) {
|
|
174
|
+
mo.observe(document.body, {
|
|
175
|
+
childList: true,
|
|
176
|
+
subtree: true
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
} else {
|
|
181
|
+
if (!observeAll()) {
|
|
182
|
+
mo.observe(document.body, {
|
|
183
|
+
childList: true,
|
|
184
|
+
subtree: true
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Create an idle (requestIdleCallback-based) hydration strategy.
|
|
191
|
+
* v0.6': Improved fallback chain:
|
|
192
|
+
* 1. requestIdleCallback (optimal, progressive)
|
|
193
|
+
* 2. requestAnimationFrame (next frame, good for interaction)
|
|
194
|
+
* 3. setTimeout(fn, 50) (final fallback, shorter than old 200ms)
|
|
195
|
+
*/ function createIdleStrategy(registerFn) {
|
|
196
|
+
const g = globalThis;
|
|
197
|
+
if (typeof g.requestIdleCallback === 'function') {
|
|
198
|
+
g.requestIdleCallback(registerFn);
|
|
199
|
+
} else if (typeof g.requestAnimationFrame === 'function') {
|
|
200
|
+
g.requestAnimationFrame(()=>registerFn());
|
|
201
|
+
} else {
|
|
202
|
+
setTimeout(registerFn, 50);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Wrap a component class as a openElement Island.
|
|
207
|
+
*
|
|
208
|
+
* Handles:
|
|
209
|
+
* - Automatic customElements.define() registration
|
|
210
|
+
* - Strategy-based upgrade timing
|
|
211
|
+
* - data-ssr-props binding (open:bind)
|
|
212
|
+
* - __island / __tagName export markers
|
|
213
|
+
* - __layer metadata (dsd-static, dsd-interactive, or pure-island)
|
|
214
|
+
* - Idempotent registration (safe for SSR with multiple routes)
|
|
215
|
+
*
|
|
216
|
+
* v0.6.2: Added `dsd` option. When false, the island is a Pure Island
|
|
217
|
+
* (Layer 3) - no DSD template is emitted, framework fully owns shadow root.
|
|
218
|
+
*
|
|
219
|
+
* @param tagName - Custom element tag name (must contain hyphen)
|
|
220
|
+
* @param componentClass - Custom Element constructor (framework-agnostic)
|
|
221
|
+
* @param options - Island options
|
|
222
|
+
* @returns The component class (for chaining / re-export)
|
|
223
|
+
*
|
|
224
|
+
* @example
|
|
225
|
+
* ```ts
|
|
226
|
+
* // Basic usage (DSD enabled by default)
|
|
227
|
+
* export default defineIsland('my-counter', MyCounter);
|
|
228
|
+
*
|
|
229
|
+
* // Pure Island - no DSD, full framework reactivity
|
|
230
|
+
* export default defineIsland('my-counter', MyCounter, { dsd: false });
|
|
231
|
+
*
|
|
232
|
+
* // With visible strategy (IntersectionObserver)
|
|
233
|
+
* export default defineIsland('my-counter', MyCounter, { strategy: 'visible' });
|
|
234
|
+
*
|
|
235
|
+
* // With load strategy (immediate upgrade)
|
|
236
|
+
* export default defineIsland('my-counter', MyCounter, { strategy: 'load' });
|
|
237
|
+
* ```
|
|
238
|
+
*/ export function defineIsland(tagName, componentClass, options = {}) {
|
|
239
|
+
const strategy = options.strategy || 'idle';
|
|
240
|
+
if (!VALID_STRATEGIES.has(strategy)) {
|
|
241
|
+
throw new Error(`${ERROR_PREFIX} Invalid island hydration strategy "${String(strategy)}". ` + 'Use one of: load, idle, visible, only.');
|
|
242
|
+
}
|
|
243
|
+
const useDsd = strategy === 'only' ? false : options.dsd !== false; // default true
|
|
244
|
+
const useSsr = strategy === 'only' ? false : options.ssr !== false; // default true
|
|
245
|
+
// Validate tag name per WHATWG Custom Element name rules
|
|
246
|
+
// https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name
|
|
247
|
+
if (!tagName || !tagName.includes('-')) {
|
|
248
|
+
throw new Error(`${ERROR_PREFIX} defineIsland() requires a hyphenated tag name, got "${tagName}". ` + 'Custom Element names must contain a hyphen per the HTML spec.');
|
|
249
|
+
}
|
|
250
|
+
// WHATWG: must start with lowercase letter, only lowercase/digits/hyphens,
|
|
251
|
+
// must not start with a reserved prefix, no uppercase
|
|
252
|
+
if (!/^[a-z][a-z0-9]*(-[a-z0-9]+)*$/.test(tagName)) {
|
|
253
|
+
throw new Error(`${ERROR_PREFIX} defineIsland() tag name "${tagName}" is not a valid custom element name. ` + 'Must start with a lowercase ASCII letter, contain only lowercase ASCII ' + 'letters, digits, and hyphens, and not use reserved names.');
|
|
254
|
+
}
|
|
255
|
+
// Reserved names per WHATWG (partial list)
|
|
256
|
+
const reservedPrefixes = [
|
|
257
|
+
'annotation-',
|
|
258
|
+
'color-profile',
|
|
259
|
+
'font-face',
|
|
260
|
+
'font-face-',
|
|
261
|
+
'missing-glyph'
|
|
262
|
+
];
|
|
263
|
+
for (const prefix of reservedPrefixes){
|
|
264
|
+
if (tagName.startsWith(prefix)) {
|
|
265
|
+
throw new Error(`${ERROR_PREFIX} defineIsland() tag name "${tagName}" uses a reserved prefix "${prefix}".`);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
_islandMeta.set(componentClass, {
|
|
269
|
+
isIsland: true,
|
|
270
|
+
tagName,
|
|
271
|
+
layer: useDsd ? 'dsd-interactive' : 'pure-island',
|
|
272
|
+
ssr: useSsr,
|
|
273
|
+
dsd: useDsd
|
|
274
|
+
});
|
|
275
|
+
// v0.6': Mixin pattern for connectedCallback - replaces monkey-patch.
|
|
276
|
+
// Instead of modifying the prototype directly, we create a wrapper
|
|
277
|
+
// that calls the original callback + auto-binds SSR props.
|
|
278
|
+
// This is safer than monkey-patching because it doesn't interfere
|
|
279
|
+
// with Lit's own connectedCallback chain.
|
|
280
|
+
//
|
|
281
|
+
// v0.14.3: Added __ssrPropsBound idempotency guard to prevent
|
|
282
|
+
// double bindSsrProps() calls when a subclass island inherits from a
|
|
283
|
+
// parent island (both registered via defineIsland()). Without this guard,
|
|
284
|
+
// the parent's wrapped connectedCallback and the subclass's both
|
|
285
|
+
// call bindSsrProps on the same element.
|
|
286
|
+
const origConnected = componentClass.prototype.connectedCallback;
|
|
287
|
+
if (!componentClass.prototype.__openIslandWrapped) {
|
|
288
|
+
componentClass.prototype.__openIslandWrapped = true;
|
|
289
|
+
componentClass.prototype.connectedCallback = function() {
|
|
290
|
+
// Call original connectedCallback first (super.connectedCallback)
|
|
291
|
+
if (typeof origConnected === 'function') {
|
|
292
|
+
origConnected.call(this);
|
|
293
|
+
}
|
|
294
|
+
// Auto-bind SSR props on upgrade (idempotent - only once per element)
|
|
295
|
+
if (this.hasAttribute('data-ssr-props') && !ssrPropsBoundSet.has(this)) {
|
|
296
|
+
ssrPropsBoundSet.add(this);
|
|
297
|
+
Promise.resolve().then(()=>bindSsrProps(this));
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
// Define a registration function that's idempotent
|
|
302
|
+
const register = ()=>{
|
|
303
|
+
const registry = globalThis.customElements;
|
|
304
|
+
if (!registry) return;
|
|
305
|
+
if (!registry.get(tagName)) {
|
|
306
|
+
try {
|
|
307
|
+
registry.define(tagName, componentClass);
|
|
308
|
+
} catch (e) {
|
|
309
|
+
// Already defined - safe to ignore in SSR contexts
|
|
310
|
+
log.debug(`customElements.define("${tagName}") skipped: ${formatError(e)}`);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
// SSR guard: browser-specific strategy scheduling is a no-op during SSR.
|
|
315
|
+
// IntersectionObserver, MutationObserver etc. are browser-only APIs.
|
|
316
|
+
// During SSR we just define the custom element and let the generated
|
|
317
|
+
// client entry handle strategy dispatch in the browser.
|
|
318
|
+
const isBrowser = typeof IntersectionObserver !== 'undefined';
|
|
319
|
+
if (isBrowser) {
|
|
320
|
+
switch(strategy){
|
|
321
|
+
case 'load':
|
|
322
|
+
case 'only':
|
|
323
|
+
register();
|
|
324
|
+
break;
|
|
325
|
+
case 'idle':
|
|
326
|
+
createIdleStrategy(register);
|
|
327
|
+
break;
|
|
328
|
+
case 'visible':
|
|
329
|
+
createVisibleStrategy(tagName, register);
|
|
330
|
+
break;
|
|
331
|
+
}
|
|
332
|
+
} else {
|
|
333
|
+
// SSR path: define the element idempotently, strategy runs on client.
|
|
334
|
+
register();
|
|
335
|
+
}
|
|
336
|
+
return componentClass;
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Exports the `island` function as default for convenience imports.
|
|
340
|
+
* Tree-shakable: bundlers can eliminate unused named exports from the same module.
|
|
341
|
+
*/ export default defineIsland;
|
|
342
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImZpbGU6Ly8vaG9tZS9ydW5uZXIvd29yay9vcGVuZWxlbWVudC9vcGVuZWxlbWVudC9wYWNrYWdlcy9jb3JlL3NyYy9pc2xhbmQudHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgRVJST1JfUFJFRklYIH0gZnJvbSAnQG9wZW5lbGVtZW50L2NvcmUnO1xuaW1wb3J0IHsgZm9ybWF0RXJyb3IgfSBmcm9tICcuL2Vycm9ycy5qcyc7XG4vKipcbiAqIEBvcGVuZWxlbWVudC9jb3JlIC0gZGVmaW5lSXNsYW5kKCkgd3JhcHBlclxuICpcbiAqIHYwLjYuMjogZGVmaW5lSXNsYW5kKCkgd3JhcHMgYW55IEN1c3RvbSBFbGVtZW50IGNsYXNzIHRvIHByb3ZpZGU6XG4gKiAgIC0gQXV0b21hdGljIHJlZ2lzdHJhdGlvbiB2aWEgY3VzdG9tRWxlbWVudHMuZGVmaW5lKClcbiAqICAgLSBIeWRyYXRpb24gc3RyYXRlZ3kgc3VwcG9ydCAobG9hZCwgaWRsZSwgdmlzaWJsZSwgb25seSlcbiAqICAgLSBfX2lzbGFuZCAvIF9fdGFnTmFtZSAvIF9fbGF5ZXIgbWV0YWRhdGEgbWFya2Vyc1xuICogICAtIGRhdGEtc3NyLXByb3BzIHJlc3RvcmF0aW9uIG9uIGNsaWVudCB1cGdyYWRlXG4gKiAgIC0gRFNEIG9wdC1vdXQgdmlhIGBkc2Q6IGZhbHNlYCAoUHVyZSBJc2xhbmQgLyBMYXllciAzKVxuICpcbiAqIEZyYW1ld29yay1hZ25vc3RpYzogd29ya3Mgd2l0aCBMaXQsIHZhbmlsbGEgQ3VzdG9tIEVsZW1lbnRzLFxuICogRkFTVCwgb3IgYW55IFdlYiBDb21wb25lbnQgbGlicmFyeS4gYmluZFNzclByb3BzKCkgc2V0cyBwcm9wc1xuICogZGlyZWN0bHk7IGFkYXB0ZXJzIGhhbmRsZSBmcmFtZXdvcmstc3BlY2lmaWMgdXBkYXRlIHRyaWdnZXJzLlxuICpcbiAqIHYwLjI5LjE6IGRlZmluZUN1c3RvbUVsZW1lbnQgaGVscGVyIGlubGluZWQgZnJvbSBjdXN0b20tZWxlbWVudC50cy5cbiAqL1xuXG5pbXBvcnQgeyBjcmVhdGVMb2dnZXIgfSBmcm9tICcuL2xvZ2dlci5qcyc7XG5pbXBvcnQgdHlwZSB7IEh5ZHJhdGlvblN0cmF0ZWd5IH0gZnJvbSAnQG9wZW5lbGVtZW50L3Byb3RvY29sL2ZyYW1ld29yayc7XG5pbXBvcnQgdHlwZSB7IElzbGFuZE1ldGEsIElzbGFuZE9wdGlvbnMgfSBmcm9tICdAb3BlbmVsZW1lbnQvcHJvdG9jb2wvaXNsYW5kJztcbmV4cG9ydCB0eXBlIHsgSXNsYW5kTWV0YSwgSXNsYW5kT3B0aW9ucyB9O1xuXG4vKiogV2Vha1NldCB0byB0cmFjayBlbGVtZW50cyB0aGF0IGhhdmUgYWxyZWFkeSBoYWQgU1NSIHByb3BzIGJvdW5kIChpZGVtcG90ZW50KS4gKi9cbmNvbnN0IHNzclByb3BzQm91bmRTZXQgPSBuZXcgV2Vha1NldDxIVE1MRWxlbWVudD4oKTtcbmNvbnN0IGxvZyA9IGNyZWF0ZUxvZ2dlcignY29yZScpO1xuXG4vKipcbiAqIFNTUi1zYWZlIGN1c3RvbSBlbGVtZW50IHJlZ2lzdHJhdGlvbiBoZWxwZXIuXG4gKiB2MC4yOS4xOiBNZXJnZWQgZnJvbSBjdXN0b20tZWxlbWVudC50cy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGRlZmluZUN1c3RvbUVsZW1lbnQoXG4gIHRhZzogc3RyaW5nLFxuICBjdG9yOiBDdXN0b21FbGVtZW50Q29uc3RydWN0b3IsXG4pOiB2b2lkIHtcbiAgaWYgKHR5cGVvZiBnbG9iYWxUaGlzLmN1c3RvbUVsZW1lbnRzID09PSAndW5kZWZpbmVkJykgcmV0dXJuO1xuICBpZiAoIWdsb2JhbFRoaXMuY3VzdG9tRWxlbWVudHMuZ2V0KHRhZykpIHtcbiAgICBnbG9iYWxUaGlzLmN1c3RvbUVsZW1lbnRzLmRlZmluZSh0YWcsIGN0b3IpO1xuICB9XG59XG5cbmNvbnN0IFZBTElEX1NUUkFURUdJRVMgPSBuZXcgU2V0PEh5ZHJhdGlvblN0cmF0ZWd5PihbJ2xvYWQnLCAnaWRsZScsICd2aXNpYmxlJywgJ29ubHknXSk7XG5cbi8vIE1vZHVsZS1sZXZlbCBzdG9yZSBvZiBhY3RpdmUgdmlzaWJpbGl0eSBzdHJhdGVneSB0aW1lb3V0IElEcy5cbi8vIFVzZWQgZm9yIHRlc3QgY2xlYW51cCAtIHRlc3RzIGNhbiBjYWxsIF9jbGVhckFsbFZpc2liaWxpdHlUaW1lb3V0cygpXG4vLyB0byBwcmV2ZW50IHRpbWVyIGxlYWtzLlxuY29uc3QgX3Zpc2liaWxpdHlUaW1lb3V0cyA9IG5ldyBTZXQ8UmV0dXJuVHlwZTx0eXBlb2Ygc2V0VGltZW91dD4+KCk7XG5cbmNvbnN0IF9pc2xhbmRNZXRhID0gbmV3IFdlYWtNYXA8Q3VzdG9tRWxlbWVudENvbnN0cnVjdG9yLCBJc2xhbmRNZXRhPigpO1xuXG5leHBvcnQgZnVuY3Rpb24gZ2V0SXNsYW5kTWV0YShjdG9yOiBDdXN0b21FbGVtZW50Q29uc3RydWN0b3IpOiBJc2xhbmRNZXRhIHwgdW5kZWZpbmVkIHtcbiAgcmV0dXJuIF9pc2xhbmRNZXRhLmdldChjdG9yKTtcbn1cblxuLyoqIENsZWFyIGFsbCBhY3RpdmUgdmlzaWJpbGl0eSBzdHJhdGVneSB0aW1lb3V0cyAoZm9yIHRlc3QgY2xlYW51cCkuICovXG5leHBvcnQgZnVuY3Rpb24gX2NsZWFyQWxsVmlzaWJpbGl0eVRpbWVvdXRzKCk6IHZvaWQge1xuICBmb3IgKGNvbnN0IGlkIG9mIF92aXNpYmlsaXR5VGltZW91dHMpIHtcbiAgICBjbGVhclRpbWVvdXQoaWQpO1xuICB9XG4gIF92aXNpYmlsaXR5VGltZW91dHMuY2xlYXIoKTtcbn1cblxuLyoqXG4gKiBHZXQgdGhlIHZhbHVlIG9mIHRoZSBkYXRhLXNzci1wcm9wcyBhdHRyaWJ1dGUgZnJvbSBhIGhvc3QgZWxlbWVudC5cbiAqIFVzZWQgdG8gcmVjb25zdHJ1Y3QgU1NSIHByb3BzIG9uIGNsaWVudCB1cGdyYWRlLlxuICpcbiAqIEBwYXJhbSBlbCAtIFRoZSBjdXN0b20gZWxlbWVudCBob3N0IGVsZW1lbnRcbiAqIEByZXR1cm5zIFBhcnNlZCBwcm9wcyBvYmplY3QsIG9yIG51bGwgaWYgbm8gZGF0YS1zc3ItcHJvcHMgYXR0cmlidXRlXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHRzXG4gKiBjb25uZWN0ZWRDYWxsYmFjaygpIHtcbiAqICAgc3VwZXIuY29ubmVjdGVkQ2FsbGJhY2soKTtcbiAqICAgY29uc3QgcHJvcHMgPSBnZXRTc3JQcm9wcyh0aGlzKTtcbiAqICAgaWYgKHByb3BzKSB7XG4gKiAgICAgdGhpcy5jb3VudCA9IHByb3BzLmNvdW50ID8/IDA7XG4gKiAgIH1cbiAqIH1cbiAqIGBgYFxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0U3NyUHJvcHMoZWw6IEhUTUxFbGVtZW50KTogUmVjb3JkPHN0cmluZywgdW5rbm93bj4gfCBudWxsIHtcbiAgY29uc3QgcmF3ID0gZWwuZ2V0QXR0cmlidXRlKCdkYXRhLXNzci1wcm9wcycpO1xuICBpZiAoIXJhdykgcmV0dXJuIG51bGw7XG4gIHRyeSB7XG4gICAgcmV0dXJuIEpTT04ucGFyc2UocmF3KSBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbiAgfSBjYXRjaCB7XG4gICAgbG9nLndhcm4oYEZhaWxlZCB0byBwYXJzZSBkYXRhLXNzci1wcm9wcyBvbiA8JHtlbC50YWdOYW1lLnRvTG93ZXJDYXNlKCl9PmApO1xuICAgIHJldHVybiBudWxsO1xuICB9XG59XG5cbi8qKlxuICogQXBwbHkgU1NSIHByb3BzIGZyb20gZGF0YS1zc3ItcHJvcHMgdG8gYSBjb21wb25lbnQgaW5zdGFuY2UuXG4gKiBUaGlzIHJlc3RvcmVzIHNlcnZlci1yZW5kZXJlZCBwcm9wZXJ0eSB2YWx1ZXMgdG8gdGhlXG4gKiBjbGllbnQtc2lkZSBjb21wb25lbnQgb24gdXBncmFkZSwgZW5zdXJpbmcgY29uc2lzdGVuY3kgYmV0d2VlblxuICogU1NSIGFuZCBjbGllbnQgc3RhdGUuXG4gKlxuICogdjAuNi4yOiBGcmFtZXdvcmstYWdub3N0aWMuIE5vIExpdC1zcGVjaWZpYyBkZXRlY3Rpb24uXG4gKiBQcm9wcyBhcmUgc2V0IGRpcmVjdGx5IG9uIHRoZSBpbnN0YW5jZS4gRFNEIGh5ZHJhdGlvbiBhbmQgVk5vZGUgZXZlbnRcbiAqIG1hcmtlcnMgYXJlIGhhbmRsZWQgYXQgdGhlIGNvbXBvbmVudCBsZXZlbCB2aWEgRHNkRWxlbWVudC5cbiAqXG4gKiB2MC4xNC4zOiBQcm90b3R5cGUgcG9sbHV0aW9uIGZpeCAtIGZpbHRlcnMgZGFuZ2Vyb3VzIGtleXNcbiAqIChfX3Byb3RvX18sIGNvbnN0cnVjdG9yLCBwcm90b3R5cGUpIGZyb20gcGFyc2VkIFNTUiBwcm9wcy5cbiAqXG4gKiBAcGFyYW0gZWwgLSBUaGUgdXBncmFkZWQgY3VzdG9tIGVsZW1lbnRcbiAqL1xuXG4vKiogS2V5cyB0aGF0IGNvdWxkIGNhdXNlIHByb3RvdHlwZSBwb2xsdXRpb24gaWYgYXNzaWduZWQgdG8gYW4gb2JqZWN0IGluc3RhbmNlLlxuICogdjAuMTQuNzogRXh0ZW5kZWQgdG8gY292ZXIgYWxsIE9iamVjdC5wcm90b3R5cGUgbWV0aG9kcyB0aGF0IGNvdWxkIGJlXG4gKiBleHBsb2l0ZWQgdmlhIGFyYml0cmFyeSBwcm9wZXJ0eSBhc3NpZ25tZW50IChDLTAzIGZpeCkuXG4gKi9cbmltcG9ydCB7IERBTkdFUk9VU19LRVlTIH0gZnJvbSAnLi9zZWN1cml0eS5qcyc7XG5cbmV4cG9ydCBmdW5jdGlvbiBiaW5kU3NyUHJvcHMoZWw6IEhUTUxFbGVtZW50KTogdm9pZCB7XG4gIGNvbnN0IHByb3BzID0gZ2V0U3NyUHJvcHMoZWwpO1xuICBpZiAoIXByb3BzKSByZXR1cm47XG5cbiAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMocHJvcHMpKSB7XG4gICAgLy8gdjAuMTQuMzogUHJldmVudCBwcm90b3R5cGUgcG9sbHV0aW9uIC0gc2tpcCBkYW5nZXJvdXMga2V5c1xuICAgIGlmIChEQU5HRVJPVVNfS0VZUy5oYXMoa2V5KSkge1xuICAgICAgbG9nLndhcm4oXG4gICAgICAgIGBTa2lwcGluZyBkYW5nZXJvdXMga2V5IFwiJHtrZXl9XCIgaW4gZGF0YS1zc3ItcHJvcHMgb24gPCR7ZWwudGFnTmFtZS50b0xvd2VyQ2FzZSgpfT5gLFxuICAgICAgKTtcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cbiAgICB0cnkge1xuICAgICAgKGVsIGFzIHVua25vd24gYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4pW2tleV0gPSB2YWx1ZTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICAvLyBTb21lIHByb3BlcnRpZXMgbWF5IGJlIHJlYWQtb25seSAtIHNhZmUgdG8gc2tpcCwgYnV0IGxvZyBmb3IgZGVidWdnYWJpbGl0eVxuICAgICAgbG9nLmRlYnVnKFxuICAgICAgICBgQ2Fubm90IHNldCByZWFkLW9ubHkgcHJvcGVydHkgXCIke2tleX1cIiBvbiA8JHtlbC50YWdOYW1lLnRvTG93ZXJDYXNlKCl9PjogJHtcbiAgICAgICAgICBmb3JtYXRFcnJvcihlKVxuICAgICAgICB9YCxcbiAgICAgICk7XG4gICAgfVxuICB9XG59XG5cbi8qKlxuICogQ3JlYXRlIGFuIEludGVyc2VjdGlvbk9ic2VydmVyLWJhc2VkIHVwZ3JhZGUgc3RyYXRlZ3kuXG4gKiB2MC42JzogVXNlcyBxdWVyeVNlbGVjdG9yQWxsIHRvIG9ic2VydmUgQUxMIGluc3RhbmNlcyBvZiB0aGUgdGFnLFxuICogbm90IGp1c3QgdGhlIGZpcnN0IG9uZS4gUGVyIFdIQVRXRyBJbnRlcnNlY3Rpb25PYnNlcnZlciBzcGVjLlxuICovXG5mdW5jdGlvbiBjcmVhdGVWaXNpYmxlU3RyYXRlZ3koXG4gIHRhZ05hbWU6IHN0cmluZyxcbiAgcmVnaXN0ZXJGbjogKCkgPT4gdm9pZCxcbik6IHZvaWQge1xuICBsZXQgcmVnaXN0ZXJlZCA9IGZhbHNlO1xuICBjb25zdCBvYnNlcnZlciA9IG5ldyBJbnRlcnNlY3Rpb25PYnNlcnZlcihcbiAgICAoZW50cmllcykgPT4ge1xuICAgICAgZm9yIChjb25zdCBlbnRyeSBvZiBlbnRyaWVzKSB7XG4gICAgICAgIGlmIChlbnRyeS5pc0ludGVyc2VjdGluZykge1xuICAgICAgICAgIG9ic2VydmVyLmRpc2Nvbm5lY3QoKTtcbiAgICAgICAgICBpZiAoIXJlZ2lzdGVyZWQpIHtcbiAgICAgICAgICAgIHJlZ2lzdGVyZWQgPSB0cnVlO1xuICAgICAgICAgICAgY2xlYXJUaW1lb3V0KHRpbWVvdXRJZCk7XG4gICAgICAgICAgICBfdmlzaWJpbGl0eVRpbWVvdXRzLmRlbGV0ZSh0aW1lb3V0SWQpO1xuICAgICAgICAgICAgcmVnaXN0ZXJGbigpO1xuICAgICAgICAgIH1cbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9LFxuICAgIHsgcm9vdE1hcmdpbjogJzIwMHB4JyB9LCAvLyBTdGFydCBsb2FkaW5nIDIwMHB4IGJlZm9yZSB2aXNpYmxlXG4gICk7XG5cbiAgLy8gV2UgbmVlZCB0byB3YWl0IGZvciBlbGVtZW50cyB0byBiZSBpbiB0aGUgRE9NIGZpcnN0XG4gIGNvbnN0IG9ic2VydmVBbGwgPSAoKSA9PiB7XG4gICAgY29uc3QgZWxlbWVudHMgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKHRhZ05hbWUpO1xuICAgIGlmIChlbGVtZW50cy5sZW5ndGggPiAwKSB7XG4gICAgICBlbGVtZW50cy5mb3JFYWNoKChlbCkgPT4gb2JzZXJ2ZXIub2JzZXJ2ZShlbCkpO1xuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuICAgIHJldHVybiBmYWxzZTtcbiAgfTtcblxuICBjb25zdCBtbyA9IG5ldyBNdXRhdGlvbk9ic2VydmVyKChfbXV0YXRpb25zLCBtdXRPYnMpID0+IHtcbiAgICBpZiAob2JzZXJ2ZUFsbCgpKSB7XG4gICAgICBtdXRPYnMuZGlzY29ubmVjdCgpO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyB2MC4xNC41OiBJZiBlbGVtZW50cyB3ZXJlIHJlbW92ZWQgZnJvbSBET00sIGRpc2Nvbm5lY3QgYWxsIG9ic2VydmVyc1xuICAgICAgY29uc3QgZWxlbWVudHMgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yQWxsKHRhZ05hbWUpO1xuICAgICAgaWYgKGVsZW1lbnRzLmxlbmd0aCA9PT0gMCAmJiAhcmVnaXN0ZXJlZCkge1xuICAgICAgICBtdXRPYnMuZGlzY29ubmVjdCgpO1xuICAgICAgICBvYnNlcnZlci5kaXNjb25uZWN0KCk7XG4gICAgICAgIGNsZWFyVGltZW91dCh0aW1lb3V0SWQpO1xuICAgICAgICBfdmlzaWJpbGl0eVRpbWVvdXRzLmRlbGV0ZSh0aW1lb3V0SWQpO1xuICAgICAgfVxuICAgIH1cbiAgfSk7XG5cbiAgLy8gdjAuMTQuMzogVGltZW91dCBndWFyZCAtIGlmIHRoZSB0YXJnZXQgZWxlbWVudCBuZXZlciBhcHBlYXJzXG4gIC8vIChlLmcuLCByb3V0ZSBjaGFuZ2VkIGFmdGVyIGlzbGFuZCB3YXMgcmVnaXN0ZXJlZCksIGRpc2Nvbm5lY3RcbiAgLy8gYm90aCBvYnNlcnZlcnMgYWZ0ZXIgMzAgc2Vjb25kcyB0byBwcmV2ZW50IG1lbW9yeS9wZXJmIGxlYWtzLlxuICBjb25zdCBWSVNJQklMSVRZX1RJTUVPVVQgPSAzMF8wMDA7IC8vIDMwc1xuICBjb25zdCB0aW1lb3V0SWQgPSBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICBfdmlzaWJpbGl0eVRpbWVvdXRzLmRlbGV0ZSh0aW1lb3V0SWQpO1xuICAgIGlmICghcmVnaXN0ZXJlZCkge1xuICAgICAgbW8uZGlzY29ubmVjdCgpO1xuICAgICAgb2JzZXJ2ZXIuZGlzY29ubmVjdCgpO1xuICAgICAgbG9nLmRlYnVnKGBWaXNpYmlsaXR5IHN0cmF0ZWd5IGZvciA8JHt0YWdOYW1lfT4gdGltZWQgb3V0IGFmdGVyICR7VklTSUJJTElUWV9USU1FT1VUfW1zYCk7XG4gICAgfVxuICB9LCBWSVNJQklMSVRZX1RJTUVPVVQpO1xuICBfdmlzaWJpbGl0eVRpbWVvdXRzLmFkZCh0aW1lb3V0SWQpO1xuXG4gIC8vIFN0YXJ0IG9ic2VydmluZyBhZnRlciBET00gY29udGVudCBsb2FkZWRcbiAgaWYgKGRvY3VtZW50LnJlYWR5U3RhdGUgPT09ICdsb2FkaW5nJykge1xuICAgIGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ0RPTUNvbnRlbnRMb2FkZWQnLCAoKSA9PiB7XG4gICAgICBpZiAoIW9ic2VydmVBbGwoKSkge1xuICAgICAgICBtby5vYnNlcnZlKGRvY3VtZW50LmJvZHksIHsgY2hpbGRMaXN0OiB0cnVlLCBzdWJ0cmVlOiB0cnVlIH0pO1xuICAgICAgfVxuICAgIH0pO1xuICB9IGVsc2Uge1xuICAgIGlmICghb2JzZXJ2ZUFsbCgpKSB7XG4gICAgICBtby5vYnNlcnZlKGRvY3VtZW50LmJvZHksIHsgY2hpbGRMaXN0OiB0cnVlLCBzdWJ0cmVlOiB0cnVlIH0pO1xuICAgIH1cbiAgfVxufVxuXG4vKipcbiAqIENyZWF0ZSBhbiBpZGxlIChyZXF1ZXN0SWRsZUNhbGxiYWNrLWJhc2VkKSBoeWRyYXRpb24gc3RyYXRlZ3kuXG4gKiB2MC42JzogSW1wcm92ZWQgZmFsbGJhY2sgY2hhaW46XG4gKiAgIDEuIHJlcXVlc3RJZGxlQ2FsbGJhY2sgKG9wdGltYWwsIHByb2dyZXNzaXZlKVxuICogICAyLiByZXF1ZXN0QW5pbWF0aW9uRnJhbWUgKG5leHQgZnJhbWUsIGdvb2QgZm9yIGludGVyYWN0aW9uKVxuICogICAzLiBzZXRUaW1lb3V0KGZuLCA1MCkgKGZpbmFsIGZhbGxiYWNrLCBzaG9ydGVyIHRoYW4gb2xkIDIwMG1zKVxuICovXG5mdW5jdGlvbiBjcmVhdGVJZGxlU3RyYXRlZ3kocmVnaXN0ZXJGbjogKCkgPT4gdm9pZCk6IHZvaWQge1xuICBjb25zdCBnID0gZ2xvYmFsVGhpcyBhcyB7XG4gICAgcmVxdWVzdElkbGVDYWxsYmFjaz86IChmbjogKCkgPT4gdm9pZCkgPT4gdm9pZDtcbiAgICByZXF1ZXN0QW5pbWF0aW9uRnJhbWU/OiAoZm46ICgpID0+IHZvaWQpID0+IG51bWJlcjtcbiAgfTtcblxuICBpZiAodHlwZW9mIGcucmVxdWVzdElkbGVDYWxsYmFjayA9PT0gJ2Z1bmN0aW9uJykge1xuICAgIGcucmVxdWVzdElkbGVDYWxsYmFjayhyZWdpc3RlckZuKTtcbiAgfSBlbHNlIGlmICh0eXBlb2YgZy5yZXF1ZXN0QW5pbWF0aW9uRnJhbWUgPT09ICdmdW5jdGlvbicpIHtcbiAgICBnLnJlcXVlc3RBbmltYXRpb25GcmFtZSgoKSA9PiByZWdpc3RlckZuKCkpO1xuICB9IGVsc2Uge1xuICAgIHNldFRpbWVvdXQocmVnaXN0ZXJGbiwgNTApO1xuICB9XG59XG5cbi8qKlxuICogV3JhcCBhIGNvbXBvbmVudCBjbGFzcyBhcyBhIG9wZW5FbGVtZW50IElzbGFuZC5cbiAqXG4gKiBIYW5kbGVzOlxuICogICAtIEF1dG9tYXRpYyBjdXN0b21FbGVtZW50cy5kZWZpbmUoKSByZWdpc3RyYXRpb25cbiAqICAgLSBTdHJhdGVneS1iYXNlZCB1cGdyYWRlIHRpbWluZ1xuICogICAtIGRhdGEtc3NyLXByb3BzIGJpbmRpbmcgKG9wZW46YmluZClcbiAqICAgLSBfX2lzbGFuZCAvIF9fdGFnTmFtZSBleHBvcnQgbWFya2Vyc1xuICogICAtIF9fbGF5ZXIgbWV0YWRhdGEgKGRzZC1zdGF0aWMsIGRzZC1pbnRlcmFjdGl2ZSwgb3IgcHVyZS1pc2xhbmQpXG4gKiAgIC0gSWRlbXBvdGVudCByZWdpc3RyYXRpb24gKHNhZmUgZm9yIFNTUiB3aXRoIG11bHRpcGxlIHJvdXRlcylcbiAqXG4gKiB2MC42LjI6IEFkZGVkIGBkc2RgIG9wdGlvbi4gV2hlbiBmYWxzZSwgdGhlIGlzbGFuZCBpcyBhIFB1cmUgSXNsYW5kXG4gKiAoTGF5ZXIgMykgLSBubyBEU0QgdGVtcGxhdGUgaXMgZW1pdHRlZCwgZnJhbWV3b3JrIGZ1bGx5IG93bnMgc2hhZG93IHJvb3QuXG4gKlxuICogQHBhcmFtIHRhZ05hbWUgLSBDdXN0b20gZWxlbWVudCB0YWcgbmFtZSAobXVzdCBjb250YWluIGh5cGhlbilcbiAqIEBwYXJhbSBjb21wb25lbnRDbGFzcyAtIEN1c3RvbSBFbGVtZW50IGNvbnN0cnVjdG9yIChmcmFtZXdvcmstYWdub3N0aWMpXG4gKiBAcGFyYW0gb3B0aW9ucyAtIElzbGFuZCBvcHRpb25zXG4gKiBAcmV0dXJucyBUaGUgY29tcG9uZW50IGNsYXNzIChmb3IgY2hhaW5pbmcgLyByZS1leHBvcnQpXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHRzXG4gKiAvLyBCYXNpYyB1c2FnZSAoRFNEIGVuYWJsZWQgYnkgZGVmYXVsdClcbiAqIGV4cG9ydCBkZWZhdWx0IGRlZmluZUlzbGFuZCgnbXktY291bnRlcicsIE15Q291bnRlcik7XG4gKlxuICogLy8gUHVyZSBJc2xhbmQgLSBubyBEU0QsIGZ1bGwgZnJhbWV3b3JrIHJlYWN0aXZpdHlcbiAqIGV4cG9ydCBkZWZhdWx0IGRlZmluZUlzbGFuZCgnbXktY291bnRlcicsIE15Q291bnRlciwgeyBkc2Q6IGZhbHNlIH0pO1xuICpcbiAqIC8vIFdpdGggdmlzaWJsZSBzdHJhdGVneSAoSW50ZXJzZWN0aW9uT2JzZXJ2ZXIpXG4gKiBleHBvcnQgZGVmYXVsdCBkZWZpbmVJc2xhbmQoJ215LWNvdW50ZXInLCBNeUNvdW50ZXIsIHsgc3RyYXRlZ3k6ICd2aXNpYmxlJyB9KTtcbiAqXG4gKiAvLyBXaXRoIGxvYWQgc3RyYXRlZ3kgKGltbWVkaWF0ZSB1cGdyYWRlKVxuICogZXhwb3J0IGRlZmF1bHQgZGVmaW5lSXNsYW5kKCdteS1jb3VudGVyJywgTXlDb3VudGVyLCB7IHN0cmF0ZWd5OiAnbG9hZCcgfSk7XG4gKiBgYGBcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGRlZmluZUlzbGFuZDxUIGV4dGVuZHMgQ3VzdG9tRWxlbWVudENvbnN0cnVjdG9yPihcbiAgdGFnTmFtZTogc3RyaW5nLFxuICBjb21wb25lbnRDbGFzczogVCxcbiAgb3B0aW9uczogSXNsYW5kT3B0aW9ucyA9IHt9LFxuKTogVCB7XG4gIGNvbnN0IHN0cmF0ZWd5ID0gb3B0aW9ucy5zdHJhdGVneSB8fCAnaWRsZSc7XG4gIGlmICghVkFMSURfU1RSQVRFR0lFUy5oYXMoc3RyYXRlZ3kpKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgYCR7RVJST1JfUFJFRklYfSBJbnZhbGlkIGlzbGFuZCBoeWRyYXRpb24gc3RyYXRlZ3kgXCIke1N0cmluZyhzdHJhdGVneSl9XCIuIGAgK1xuICAgICAgICAnVXNlIG9uZSBvZjogbG9hZCwgaWRsZSwgdmlzaWJsZSwgb25seS4nLFxuICAgICk7XG4gIH1cbiAgY29uc3QgdXNlRHNkID0gc3RyYXRlZ3kgPT09ICdvbmx5JyA/IGZhbHNlIDogb3B0aW9ucy5kc2QgIT09IGZhbHNlOyAvLyBkZWZhdWx0IHRydWVcbiAgY29uc3QgdXNlU3NyID0gc3RyYXRlZ3kgPT09ICdvbmx5JyA/IGZhbHNlIDogb3B0aW9ucy5zc3IgIT09IGZhbHNlOyAvLyBkZWZhdWx0IHRydWVcblxuICAvLyBWYWxpZGF0ZSB0YWcgbmFtZSBwZXIgV0hBVFdHIEN1c3RvbSBFbGVtZW50IG5hbWUgcnVsZXNcbiAgLy8gaHR0cHM6Ly9odG1sLnNwZWMud2hhdHdnLm9yZy9tdWx0aXBhZ2UvY3VzdG9tLWVsZW1lbnRzLmh0bWwjdmFsaWQtY3VzdG9tLWVsZW1lbnQtbmFtZVxuICBpZiAoIXRhZ05hbWUgfHwgIXRhZ05hbWUuaW5jbHVkZXMoJy0nKSkge1xuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgIGAke0VSUk9SX1BSRUZJWH0gZGVmaW5lSXNsYW5kKCkgcmVxdWlyZXMgYSBoeXBoZW5hdGVkIHRhZyBuYW1lLCBnb3QgXCIke3RhZ05hbWV9XCIuIGAgK1xuICAgICAgICAnQ3VzdG9tIEVsZW1lbnQgbmFtZXMgbXVzdCBjb250YWluIGEgaHlwaGVuIHBlciB0aGUgSFRNTCBzcGVjLicsXG4gICAgKTtcbiAgfVxuICAvLyBXSEFUV0c6IG11c3Qgc3RhcnQgd2l0aCBsb3dlcmNhc2UgbGV0dGVyLCBvbmx5IGxvd2VyY2FzZS9kaWdpdHMvaHlwaGVucyxcbiAgLy8gbXVzdCBub3Qgc3RhcnQgd2l0aCBhIHJlc2VydmVkIHByZWZpeCwgbm8gdXBwZXJjYXNlXG4gIGlmICghL15bYS16XVthLXowLTldKigtW2EtejAtOV0rKSokLy50ZXN0KHRhZ05hbWUpKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgYCR7RVJST1JfUFJFRklYfSBkZWZpbmVJc2xhbmQoKSB0YWcgbmFtZSBcIiR7dGFnTmFtZX1cIiBpcyBub3QgYSB2YWxpZCBjdXN0b20gZWxlbWVudCBuYW1lLiBgICtcbiAgICAgICAgJ011c3Qgc3RhcnQgd2l0aCBhIGxvd2VyY2FzZSBBU0NJSSBsZXR0ZXIsIGNvbnRhaW4gb25seSBsb3dlcmNhc2UgQVNDSUkgJyArXG4gICAgICAgICdsZXR0ZXJzLCBkaWdpdHMsIGFuZCBoeXBoZW5zLCBhbmQgbm90IHVzZSByZXNlcnZlZCBuYW1lcy4nLFxuICAgICk7XG4gIH1cbiAgLy8gUmVzZXJ2ZWQgbmFtZXMgcGVyIFdIQVRXRyAocGFydGlhbCBsaXN0KVxuICBjb25zdCByZXNlcnZlZFByZWZpeGVzID0gW1xuICAgICdhbm5vdGF0aW9uLScsXG4gICAgJ2NvbG9yLXByb2ZpbGUnLFxuICAgICdmb250LWZhY2UnLFxuICAgICdmb250LWZhY2UtJyxcbiAgICAnbWlzc2luZy1nbHlwaCcsXG4gIF07XG4gIGZvciAoY29uc3QgcHJlZml4IG9mIHJlc2VydmVkUHJlZml4ZXMpIHtcbiAgICBpZiAodGFnTmFtZS5zdGFydHNXaXRoKHByZWZpeCkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgYCR7RVJST1JfUFJFRklYfSBkZWZpbmVJc2xhbmQoKSB0YWcgbmFtZSBcIiR7dGFnTmFtZX1cIiB1c2VzIGEgcmVzZXJ2ZWQgcHJlZml4IFwiJHtwcmVmaXh9XCIuYCxcbiAgICAgICk7XG4gICAgfVxuICB9XG5cbiAgX2lzbGFuZE1ldGEuc2V0KGNvbXBvbmVudENsYXNzLCB7XG4gICAgaXNJc2xhbmQ6IHRydWUsXG4gICAgdGFnTmFtZSxcbiAgICBsYXllcjogdXNlRHNkID8gJ2RzZC1pbnRlcmFjdGl2ZScgOiAncHVyZS1pc2xhbmQnLFxuICAgIHNzcjogdXNlU3NyLFxuICAgIGRzZDogdXNlRHNkLFxuICB9KTtcblxuICAvLyB2MC42JzogTWl4aW4gcGF0dGVybiBmb3IgY29ubmVjdGVkQ2FsbGJhY2sgLSByZXBsYWNlcyBtb25rZXktcGF0Y2guXG4gIC8vIEluc3RlYWQgb2YgbW9kaWZ5aW5nIHRoZSBwcm90b3R5cGUgZGlyZWN0bHksIHdlIGNyZWF0ZSBhIHdyYXBwZXJcbiAgLy8gdGhhdCBjYWxscyB0aGUgb3JpZ2luYWwgY2FsbGJhY2sgKyBhdXRvLWJpbmRzIFNTUiBwcm9wcy5cbiAgLy8gVGhpcyBpcyBzYWZlciB0aGFuIG1vbmtleS1wYXRjaGluZyBiZWNhdXNlIGl0IGRvZXNuJ3QgaW50ZXJmZXJlXG4gIC8vIHdpdGggTGl0J3Mgb3duIGNvbm5lY3RlZENhbGxiYWNrIGNoYWluLlxuICAvL1xuICAvLyB2MC4xNC4zOiBBZGRlZCBfX3NzclByb3BzQm91bmQgaWRlbXBvdGVuY3kgZ3VhcmQgdG8gcHJldmVudFxuICAvLyBkb3VibGUgYmluZFNzclByb3BzKCkgY2FsbHMgd2hlbiBhIHN1YmNsYXNzIGlzbGFuZCBpbmhlcml0cyBmcm9tIGFcbiAgLy8gcGFyZW50IGlzbGFuZCAoYm90aCByZWdpc3RlcmVkIHZpYSBkZWZpbmVJc2xhbmQoKSkuIFdpdGhvdXQgdGhpcyBndWFyZCxcbiAgLy8gdGhlIHBhcmVudCdzIHdyYXBwZWQgY29ubmVjdGVkQ2FsbGJhY2sgYW5kIHRoZSBzdWJjbGFzcydzIGJvdGhcbiAgLy8gY2FsbCBiaW5kU3NyUHJvcHMgb24gdGhlIHNhbWUgZWxlbWVudC5cbiAgY29uc3Qgb3JpZ0Nvbm5lY3RlZCA9IGNvbXBvbmVudENsYXNzLnByb3RvdHlwZS5jb25uZWN0ZWRDYWxsYmFjaztcbiAgaWYgKCFjb21wb25lbnRDbGFzcy5wcm90b3R5cGUuX19vcGVuSXNsYW5kV3JhcHBlZCkge1xuICAgIGNvbXBvbmVudENsYXNzLnByb3RvdHlwZS5fX29wZW5Jc2xhbmRXcmFwcGVkID0gdHJ1ZTtcbiAgICBjb21wb25lbnRDbGFzcy5wcm90b3R5cGUuY29ubmVjdGVkQ2FsbGJhY2sgPSBmdW5jdGlvbiAodGhpczogSFRNTEVsZW1lbnQpIHtcbiAgICAgIC8vIENhbGwgb3JpZ2luYWwgY29ubmVjdGVkQ2FsbGJhY2sgZmlyc3QgKHN1cGVyLmNvbm5lY3RlZENhbGxiYWNrKVxuICAgICAgaWYgKHR5cGVvZiBvcmlnQ29ubmVjdGVkID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgIG9yaWdDb25uZWN0ZWQuY2FsbCh0aGlzKTtcbiAgICAgIH1cbiAgICAgIC8vIEF1dG8tYmluZCBTU1IgcHJvcHMgb24gdXBncmFkZSAoaWRlbXBvdGVudCAtIG9ubHkgb25jZSBwZXIgZWxlbWVudClcbiAgICAgIGlmIChcbiAgICAgICAgdGhpcy5oYXNBdHRyaWJ1dGUoJ2RhdGEtc3NyLXByb3BzJykgJiZcbiAgICAgICAgIXNzclByb3BzQm91bmRTZXQuaGFzKHRoaXMpXG4gICAgICApIHtcbiAgICAgICAgc3NyUHJvcHNCb3VuZFNldC5hZGQodGhpcyk7XG4gICAgICAgIFByb21pc2UucmVzb2x2ZSgpLnRoZW4oKCkgPT4gYmluZFNzclByb3BzKHRoaXMpKTtcbiAgICAgIH1cbiAgICB9IGFzIHVua25vd24gYXMgdHlwZW9mIGNvbXBvbmVudENsYXNzLnByb3RvdHlwZS5jb25uZWN0ZWRDYWxsYmFjaztcbiAgfVxuXG4gIC8vIERlZmluZSBhIHJlZ2lzdHJhdGlvbiBmdW5jdGlvbiB0aGF0J3MgaWRlbXBvdGVudFxuICBjb25zdCByZWdpc3RlciA9ICgpID0+IHtcbiAgICBjb25zdCByZWdpc3RyeSA9IGdsb2JhbFRoaXMuY3VzdG9tRWxlbWVudHM7XG4gICAgaWYgKCFyZWdpc3RyeSkgcmV0dXJuO1xuICAgIGlmICghcmVnaXN0cnkuZ2V0KHRhZ05hbWUpKSB7XG4gICAgICB0cnkge1xuICAgICAgICByZWdpc3RyeS5kZWZpbmUodGFnTmFtZSwgY29tcG9uZW50Q2xhc3MpO1xuICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAvLyBBbHJlYWR5IGRlZmluZWQgLSBzYWZlIHRvIGlnbm9yZSBpbiBTU1IgY29udGV4dHNcbiAgICAgICAgbG9nLmRlYnVnKFxuICAgICAgICAgIGBjdXN0b21FbGVtZW50cy5kZWZpbmUoXCIke3RhZ05hbWV9XCIpIHNraXBwZWQ6ICR7Zm9ybWF0RXJyb3IoZSl9YCxcbiAgICAgICAgKTtcbiAgICAgIH1cbiAgICB9XG4gIH07XG5cbiAgLy8gU1NSIGd1YXJkOiBicm93c2VyLXNwZWNpZmljIHN0cmF0ZWd5IHNjaGVkdWxpbmcgaXMgYSBuby1vcCBkdXJpbmcgU1NSLlxuICAvLyBJbnRlcnNlY3Rpb25PYnNlcnZlciwgTXV0YXRpb25PYnNlcnZlciBldGMuIGFyZSBicm93c2VyLW9ubHkgQVBJcy5cbiAgLy8gRHVyaW5nIFNTUiB3ZSBqdXN0IGRlZmluZSB0aGUgY3VzdG9tIGVsZW1lbnQgYW5kIGxldCB0aGUgZ2VuZXJhdGVkXG4gIC8vIGNsaWVudCBlbnRyeSBoYW5kbGUgc3RyYXRlZ3kgZGlzcGF0Y2ggaW4gdGhlIGJyb3dzZXIuXG4gIGNvbnN0IGlzQnJvd3NlciA9IHR5cGVvZiBJbnRlcnNlY3Rpb25PYnNlcnZlciAhPT0gJ3VuZGVmaW5lZCc7XG5cbiAgaWYgKGlzQnJvd3Nlcikge1xuICAgIHN3aXRjaCAoc3RyYXRlZ3kpIHtcbiAgICAgIGNhc2UgJ2xvYWQnOiAvLyBwb255dGFpbDogZmFsbHRocm91Z2gsIGJvdGggJ2xvYWQnIGFuZCAnb25seScgY2FsbCByZWdpc3RlcigpXG4gICAgICBjYXNlICdvbmx5JzpcbiAgICAgICAgcmVnaXN0ZXIoKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICdpZGxlJzpcbiAgICAgICAgY3JlYXRlSWRsZVN0cmF0ZWd5KHJlZ2lzdGVyKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICd2aXNpYmxlJzpcbiAgICAgICAgY3JlYXRlVmlzaWJsZVN0cmF0ZWd5KHRhZ05hbWUsIHJlZ2lzdGVyKTtcbiAgICAgICAgYnJlYWs7XG4gICAgfVxuICB9IGVsc2Uge1xuICAgIC8vIFNTUiBwYXRoOiBkZWZpbmUgdGhlIGVsZW1lbnQgaWRlbXBvdGVudGx5LCBzdHJhdGVneSBydW5zIG9uIGNsaWVudC5cbiAgICByZWdpc3RlcigpO1xuICB9XG5cbiAgcmV0dXJuIGNvbXBvbmVudENsYXNzO1xufVxuXG4vKipcbiAqIEV4cG9ydHMgdGhlIGBpc2xhbmRgIGZ1bmN0aW9uIGFzIGRlZmF1bHQgZm9yIGNvbnZlbmllbmNlIGltcG9ydHMuXG4gKiBUcmVlLXNoYWthYmxlOiBidW5kbGVycyBjYW4gZWxpbWluYXRlIHVudXNlZCBuYW1lZCBleHBvcnRzIGZyb20gdGhlIHNhbWUgbW9kdWxlLlxuICovXG5leHBvcnQgZGVmYXVsdCBkZWZpbmVJc2xhbmQ7XG4iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsU0FBUyxZQUFZLFFBQVEsb0JBQW9CO0FBQ2pELFNBQVMsV0FBVyxRQUFRLGNBQWM7QUFDMUM7Ozs7Ozs7Ozs7Ozs7OztDQWVDLEdBRUQsU0FBUyxZQUFZLFFBQVEsY0FBYztBQUszQyxrRkFBa0YsR0FDbEYsTUFBTSxtQkFBbUIsSUFBSTtBQUM3QixNQUFNLE1BQU0sYUFBYTtBQUV6Qjs7O0NBR0MsR0FDRCxPQUFPLFNBQVMsb0JBQ2QsR0FBVyxFQUNYLElBQThCO0VBRTlCLElBQUksT0FBTyxXQUFXLGNBQWMsS0FBSyxhQUFhO0VBQ3RELElBQUksQ0FBQyxXQUFXLGNBQWMsQ0FBQyxHQUFHLENBQUMsTUFBTTtJQUN2QyxXQUFXLGNBQWMsQ0FBQyxNQUFNLENBQUMsS0FBSztFQUN4QztBQUNGO0FBRUEsTUFBTSxtQkFBbUIsSUFBSSxJQUF1QjtFQUFDO0VBQVE7RUFBUTtFQUFXO0NBQU87QUFFdkYsZ0VBQWdFO0FBQ2hFLHVFQUF1RTtBQUN2RSwwQkFBMEI7QUFDMUIsTUFBTSxzQkFBc0IsSUFBSTtBQUVoQyxNQUFNLGNBQWMsSUFBSTtBQUV4QixPQUFPLFNBQVMsY0FBYyxJQUE4QjtFQUMxRCxPQUFPLFlBQVksR0FBRyxDQUFDO0FBQ3pCO0FBRUEsc0VBQXNFLEdBQ3RFLE9BQU8sU0FBUztFQUNkLEtBQUssTUFBTSxNQUFNLG9CQUFxQjtJQUNwQyxhQUFhO0VBQ2Y7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
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { IsrCacheResult } from '@openelement/protocol/isr';
|
|
2
|
+
import type { IsrManifestEntry } from '@openelement/protocol/framework';
|
|
3
|
+
import type { MemoryIsrCache } from "./isr.js";
|
|
4
|
+
export type IsrRuntimeState = IsrCacheResult['state'] | 'not-found';
|
|
5
|
+
export interface IsrRuntimeRenderResult {
|
|
6
|
+
html: string;
|
|
7
|
+
headers?: Record<string, string>;
|
|
8
|
+
}
|
|
9
|
+
export interface IsrRuntimeRenderContext {
|
|
10
|
+
entry: IsrManifestEntry;
|
|
11
|
+
request?: Request;
|
|
12
|
+
}
|
|
13
|
+
export interface IsrRuntimeOptions {
|
|
14
|
+
manifest: IsrManifestEntry[];
|
|
15
|
+
cache: MemoryIsrCache;
|
|
16
|
+
render: (path: string, context: IsrRuntimeRenderContext) => Promise<IsrRuntimeRenderResult> | IsrRuntimeRenderResult;
|
|
17
|
+
now?: () => number;
|
|
18
|
+
regenerate?: 'blocking' | 'background';
|
|
19
|
+
onRegenerateError?: (error: unknown, entry: IsrManifestEntry) => void;
|
|
20
|
+
schedule?: (task: Promise<void>) => void;
|
|
21
|
+
}
|
|
22
|
+
export interface IsrRuntimeResult {
|
|
23
|
+
state: IsrRuntimeState;
|
|
24
|
+
entry?: IsrManifestEntry;
|
|
25
|
+
response: Response;
|
|
26
|
+
}
|
|
27
|
+
export declare function findIsrManifestEntry(manifest: IsrManifestEntry[], path: string, params?: Record<string, string>): IsrManifestEntry | undefined;
|
|
28
|
+
export declare function renderIsrResponse(path: string, params: Record<string, string>, options: IsrRuntimeOptions, request?: Request): Promise<IsrRuntimeResult>;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
export function findIsrManifestEntry(manifest, path, params = {}) {
|
|
2
|
+
const paramEntries = Object.entries(params).sort(([a], [b])=>a.localeCompare(b));
|
|
3
|
+
return manifest.find((entry)=>{
|
|
4
|
+
if (entry.path !== path) return false;
|
|
5
|
+
const entryParams = Object.entries(entry.params).sort(([a], [b])=>a.localeCompare(b));
|
|
6
|
+
if (entryParams.length !== paramEntries.length) return false;
|
|
7
|
+
return entryParams.every(([key, value], index)=>{
|
|
8
|
+
const [otherKey, otherValue] = paramEntries[index];
|
|
9
|
+
return key === otherKey && value === otherValue;
|
|
10
|
+
});
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
export async function renderIsrResponse(path, params, options, request) {
|
|
14
|
+
const entry = findIsrManifestEntry(options.manifest, path, params);
|
|
15
|
+
if (!entry) {
|
|
16
|
+
return {
|
|
17
|
+
state: 'not-found',
|
|
18
|
+
response: new Response('Not found', {
|
|
19
|
+
status: 404
|
|
20
|
+
})
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
const now = options.now?.() ?? Date.now();
|
|
24
|
+
const cached = await options.cache.get(entry.cacheKey, now);
|
|
25
|
+
if (cached.state === 'hit' && cached.entry) {
|
|
26
|
+
return {
|
|
27
|
+
state: 'hit',
|
|
28
|
+
entry,
|
|
29
|
+
response: responseFromCacheEntry(cached.entry, 'hit')
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
if (cached.state === 'stale' && cached.entry) {
|
|
33
|
+
const regenerate = regenerateEntry(entry, options, request, now);
|
|
34
|
+
if (options.regenerate === 'background') {
|
|
35
|
+
options.schedule?.(regenerate);
|
|
36
|
+
if (!options.schedule) regenerate.catch((error)=>options.onRegenerateError?.(error, entry));
|
|
37
|
+
return {
|
|
38
|
+
state: 'stale',
|
|
39
|
+
entry,
|
|
40
|
+
response: responseFromCacheEntry(cached.entry, 'stale')
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
await regenerate;
|
|
44
|
+
const refreshed = await options.cache.get(entry.cacheKey, options.now?.() ?? Date.now());
|
|
45
|
+
return {
|
|
46
|
+
state: 'stale',
|
|
47
|
+
entry,
|
|
48
|
+
response: refreshed.entry ? responseFromCacheEntry(refreshed.entry, 'stale') : responseFromCacheEntry(cached.entry, 'stale')
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
if (cached.state === 'error') {
|
|
52
|
+
return {
|
|
53
|
+
state: 'error',
|
|
54
|
+
entry,
|
|
55
|
+
response: new Response('ISR cache error', {
|
|
56
|
+
status: 500
|
|
57
|
+
})
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
const rendered = await renderAndStore(entry, options, request, now);
|
|
61
|
+
return {
|
|
62
|
+
state: 'miss',
|
|
63
|
+
entry,
|
|
64
|
+
response: responseFromCacheEntry(rendered, 'miss')
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
async function regenerateEntry(entry, options, request, now) {
|
|
68
|
+
try {
|
|
69
|
+
await renderAndStore(entry, options, request, now);
|
|
70
|
+
} catch (error) {
|
|
71
|
+
options.onRegenerateError?.(error, entry);
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async function renderAndStore(entry, options, request, now) {
|
|
76
|
+
const result = await options.render(entry.path, {
|
|
77
|
+
entry,
|
|
78
|
+
request
|
|
79
|
+
});
|
|
80
|
+
const cacheEntry = {
|
|
81
|
+
html: result.html,
|
|
82
|
+
headers: result.headers,
|
|
83
|
+
createdAt: now,
|
|
84
|
+
revalidate: entry.revalidate
|
|
85
|
+
};
|
|
86
|
+
await options.cache.set(entry.cacheKey, cacheEntry);
|
|
87
|
+
return cacheEntry;
|
|
88
|
+
}
|
|
89
|
+
function responseFromCacheEntry(entry, state) {
|
|
90
|
+
const headers = new Headers(entry.headers);
|
|
91
|
+
if (!headers.has('content-type')) {
|
|
92
|
+
headers.set('content-type', 'text/html; charset=utf-8');
|
|
93
|
+
}
|
|
94
|
+
headers.set('x-openelement-isr', state);
|
|
95
|
+
return new Response(entry.html, {
|
|
96
|
+
headers
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImZpbGU6Ly8vaG9tZS9ydW5uZXIvd29yay9vcGVuZWxlbWVudC9vcGVuZWxlbWVudC9wYWNrYWdlcy9jb3JlL3NyYy9pc3ItcnVudGltZS50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgdHlwZSB7IElzckNhY2hlRW50cnksIElzckNhY2hlUmVzdWx0IH0gZnJvbSAnQG9wZW5lbGVtZW50L3Byb3RvY29sL2lzcic7XG5pbXBvcnQgdHlwZSB7IElzck1hbmlmZXN0RW50cnkgfSBmcm9tICdAb3BlbmVsZW1lbnQvcHJvdG9jb2wvZnJhbWV3b3JrJztcbmltcG9ydCB0eXBlIHsgTWVtb3J5SXNyQ2FjaGUgfSBmcm9tICcuL2lzci5qcyc7XG5cbmV4cG9ydCB0eXBlIElzclJ1bnRpbWVTdGF0ZSA9IElzckNhY2hlUmVzdWx0WydzdGF0ZSddIHwgJ25vdC1mb3VuZCc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgSXNyUnVudGltZVJlbmRlclJlc3VsdCB7XG4gIGh0bWw6IHN0cmluZztcbiAgaGVhZGVycz86IFJlY29yZDxzdHJpbmcsIHN0cmluZz47XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgSXNyUnVudGltZVJlbmRlckNvbnRleHQge1xuICBlbnRyeTogSXNyTWFuaWZlc3RFbnRyeTtcbiAgcmVxdWVzdD86IFJlcXVlc3Q7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgSXNyUnVudGltZU9wdGlvbnMge1xuICBtYW5pZmVzdDogSXNyTWFuaWZlc3RFbnRyeVtdO1xuICBjYWNoZTogTWVtb3J5SXNyQ2FjaGU7XG4gIHJlbmRlcjogKFxuICAgIHBhdGg6IHN0cmluZyxcbiAgICBjb250ZXh0OiBJc3JSdW50aW1lUmVuZGVyQ29udGV4dCxcbiAgKSA9PiBQcm9taXNlPElzclJ1bnRpbWVSZW5kZXJSZXN1bHQ+IHwgSXNyUnVudGltZVJlbmRlclJlc3VsdDtcbiAgbm93PzogKCkgPT4gbnVtYmVyO1xuICByZWdlbmVyYXRlPzogJ2Jsb2NraW5nJyB8ICdiYWNrZ3JvdW5kJztcbiAgb25SZWdlbmVyYXRlRXJyb3I/OiAoZXJyb3I6IHVua25vd24sIGVudHJ5OiBJc3JNYW5pZmVzdEVudHJ5KSA9PiB2b2lkO1xuICBzY2hlZHVsZT86ICh0YXNrOiBQcm9taXNlPHZvaWQ+KSA9PiB2b2lkO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIElzclJ1bnRpbWVSZXN1bHQge1xuICBzdGF0ZTogSXNyUnVudGltZVN0YXRlO1xuICBlbnRyeT86IElzck1hbmlmZXN0RW50cnk7XG4gIHJlc3BvbnNlOiBSZXNwb25zZTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGZpbmRJc3JNYW5pZmVzdEVudHJ5KFxuICBtYW5pZmVzdDogSXNyTWFuaWZlc3RFbnRyeVtdLFxuICBwYXRoOiBzdHJpbmcsXG4gIHBhcmFtczogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHt9LFxuKTogSXNyTWFuaWZlc3RFbnRyeSB8IHVuZGVmaW5lZCB7XG4gIGNvbnN0IHBhcmFtRW50cmllcyA9IE9iamVjdC5lbnRyaWVzKHBhcmFtcykuc29ydCgoW2FdLCBbYl0pID0+IGEubG9jYWxlQ29tcGFyZShiKSk7XG4gIHJldHVybiBtYW5pZmVzdC5maW5kKChlbnRyeSkgPT4ge1xuICAgIGlmIChlbnRyeS5wYXRoICE9PSBwYXRoKSByZXR1cm4gZmFsc2U7XG4gICAgY29uc3QgZW50cnlQYXJhbXMgPSBPYmplY3QuZW50cmllcyhlbnRyeS5wYXJhbXMpLnNvcnQoKFthXSwgW2JdKSA9PiBhLmxvY2FsZUNvbXBhcmUoYikpO1xuICAgIGlmIChlbnRyeVBhcmFtcy5sZW5ndGggIT09IHBhcmFtRW50cmllcy5sZW5ndGgpIHJldHVybiBmYWxzZTtcbiAgICByZXR1cm4gZW50cnlQYXJhbXMuZXZlcnkoKFtrZXksIHZhbHVlXSwgaW5kZXgpID0+IHtcbiAgICAgIGNvbnN0IFtvdGhlcktleSwgb3RoZXJWYWx1ZV0gPSBwYXJhbUVudHJpZXNbaW5kZXhdO1xuICAgICAgcmV0dXJuIGtleSA9PT0gb3RoZXJLZXkgJiYgdmFsdWUgPT09IG90aGVyVmFsdWU7XG4gICAgfSk7XG4gIH0pO1xufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcmVuZGVySXNyUmVzcG9uc2UoXG4gIHBhdGg6IHN0cmluZyxcbiAgcGFyYW1zOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+LFxuICBvcHRpb25zOiBJc3JSdW50aW1lT3B0aW9ucyxcbiAgcmVxdWVzdD86IFJlcXVlc3QsXG4pOiBQcm9taXNlPElzclJ1bnRpbWVSZXN1bHQ+IHtcbiAgY29uc3QgZW50cnkgPSBmaW5kSXNyTWFuaWZlc3RFbnRyeShvcHRpb25zLm1hbmlmZXN0LCBwYXRoLCBwYXJhbXMpO1xuICBpZiAoIWVudHJ5KSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIHN0YXRlOiAnbm90LWZvdW5kJyxcbiAgICAgIHJlc3BvbnNlOiBuZXcgUmVzcG9uc2UoJ05vdCBmb3VuZCcsIHsgc3RhdHVzOiA0MDQgfSksXG4gICAgfTtcbiAgfVxuXG4gIGNvbnN0IG5vdyA9IG9wdGlvbnMubm93Py4oKSA/PyBEYXRlLm5vdygpO1xuICBjb25zdCBjYWNoZWQgPSBhd2FpdCBvcHRpb25zLmNhY2hlLmdldChlbnRyeS5jYWNoZUtleSwgbm93KTtcbiAgaWYgKGNhY2hlZC5zdGF0ZSA9PT0gJ2hpdCcgJiYgY2FjaGVkLmVudHJ5KSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIHN0YXRlOiAnaGl0JyxcbiAgICAgIGVudHJ5LFxuICAgICAgcmVzcG9uc2U6IHJlc3BvbnNlRnJvbUNhY2hlRW50cnkoY2FjaGVkLmVudHJ5LCAnaGl0JyksXG4gICAgfTtcbiAgfVxuXG4gIGlmIChjYWNoZWQuc3RhdGUgPT09ICdzdGFsZScgJiYgY2FjaGVkLmVudHJ5KSB7XG4gICAgY29uc3QgcmVnZW5lcmF0ZSA9IHJlZ2VuZXJhdGVFbnRyeShlbnRyeSwgb3B0aW9ucywgcmVxdWVzdCwgbm93KTtcbiAgICBpZiAob3B0aW9ucy5yZWdlbmVyYXRlID09PSAnYmFja2dyb3VuZCcpIHtcbiAgICAgIG9wdGlvbnMuc2NoZWR1bGU/LihyZWdlbmVyYXRlKTtcbiAgICAgIGlmICghb3B0aW9ucy5zY2hlZHVsZSkgcmVnZW5lcmF0ZS5jYXRjaCgoZXJyb3IpID0+IG9wdGlvbnMub25SZWdlbmVyYXRlRXJyb3I/LihlcnJvciwgZW50cnkpKTtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIHN0YXRlOiAnc3RhbGUnLFxuICAgICAgICBlbnRyeSxcbiAgICAgICAgcmVzcG9uc2U6IHJlc3BvbnNlRnJvbUNhY2hlRW50cnkoY2FjaGVkLmVudHJ5LCAnc3RhbGUnKSxcbiAgICAgIH07XG4gICAgfVxuICAgIGF3YWl0IHJlZ2VuZXJhdGU7XG4gICAgY29uc3QgcmVmcmVzaGVkID0gYXdhaXQgb3B0aW9ucy5jYWNoZS5nZXQoZW50cnkuY2FjaGVLZXksIG9wdGlvbnMubm93Py4oKSA/PyBEYXRlLm5vdygpKTtcbiAgICByZXR1cm4ge1xuICAgICAgc3RhdGU6ICdzdGFsZScsXG4gICAgICBlbnRyeSxcbiAgICAgIHJlc3BvbnNlOiByZWZyZXNoZWQuZW50cnlcbiAgICAgICAgPyByZXNwb25zZUZyb21DYWNoZUVudHJ5KHJlZnJlc2hlZC5lbnRyeSwgJ3N0YWxlJylcbiAgICAgICAgOiByZXNwb25zZUZyb21DYWNoZUVudHJ5KGNhY2hlZC5lbnRyeSwgJ3N0YWxlJyksXG4gICAgfTtcbiAgfVxuXG4gIGlmIChjYWNoZWQuc3RhdGUgPT09ICdlcnJvcicpIHtcbiAgICByZXR1cm4ge1xuICAgICAgc3RhdGU6ICdlcnJvcicsXG4gICAgICBlbnRyeSxcbiAgICAgIHJlc3BvbnNlOiBuZXcgUmVzcG9uc2UoJ0lTUiBjYWNoZSBlcnJvcicsIHsgc3RhdHVzOiA1MDAgfSksXG4gICAgfTtcbiAgfVxuXG4gIGNvbnN0IHJlbmRlcmVkID0gYXdhaXQgcmVuZGVyQW5kU3RvcmUoZW50cnksIG9wdGlvbnMsIHJlcXVlc3QsIG5vdyk7XG4gIHJldHVybiB7XG4gICAgc3RhdGU6ICdtaXNzJyxcbiAgICBlbnRyeSxcbiAgICByZXNwb25zZTogcmVzcG9uc2VGcm9tQ2FjaGVFbnRyeShyZW5kZXJlZCwgJ21pc3MnKSxcbiAgfTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gcmVnZW5lcmF0ZUVudHJ5KFxuICBlbnRyeTogSXNyTWFuaWZlc3RFbnRyeSxcbiAgb3B0aW9uczogSXNyUnVudGltZU9wdGlvbnMsXG4gIHJlcXVlc3Q6IFJlcXVlc3QgfCB1bmRlZmluZWQsXG4gIG5vdzogbnVtYmVyLFxuKTogUHJvbWlzZTx2b2lkPiB7XG4gIHRyeSB7XG4gICAgYXdhaXQgcmVuZGVyQW5kU3RvcmUoZW50cnksIG9wdGlvbnMsIHJlcXVlc3QsIG5vdyk7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgb3B0aW9ucy5vblJlZ2VuZXJhdGVFcnJvcj8uKGVycm9yLCBlbnRyeSk7XG4gICAgdGhyb3cgZXJyb3I7XG4gIH1cbn1cblxuYXN5bmMgZnVuY3Rpb24gcmVuZGVyQW5kU3RvcmUoXG4gIGVudHJ5OiBJc3JNYW5pZmVzdEVudHJ5LFxuICBvcHRpb25zOiBJc3JSdW50aW1lT3B0aW9ucyxcbiAgcmVxdWVzdDogUmVxdWVzdCB8IHVuZGVmaW5lZCxcbiAgbm93OiBudW1iZXIsXG4pOiBQcm9taXNlPElzckNhY2hlRW50cnk+IHtcbiAgY29uc3QgcmVzdWx0ID0gYXdhaXQgb3B0aW9ucy5yZW5kZXIoZW50cnkucGF0aCwgeyBlbnRyeSwgcmVxdWVzdCB9KTtcbiAgY29uc3QgY2FjaGVFbnRyeTogSXNyQ2FjaGVFbnRyeSA9IHtcbiAgICBodG1sOiByZXN1bHQuaHRtbCxcbiAgICBoZWFkZXJzOiByZXN1bHQuaGVhZGVycyxcbiAgICBjcmVhdGVkQXQ6IG5vdyxcbiAgICByZXZhbGlkYXRlOiBlbnRyeS5yZXZhbGlkYXRlLFxuICB9O1xuICBhd2FpdCBvcHRpb25zLmNhY2hlLnNldChlbnRyeS5jYWNoZUtleSwgY2FjaGVFbnRyeSk7XG4gIHJldHVybiBjYWNoZUVudHJ5O1xufVxuXG5mdW5jdGlvbiByZXNwb25zZUZyb21DYWNoZUVudHJ5KGVudHJ5OiBJc3JDYWNoZUVudHJ5LCBzdGF0ZTogSXNyQ2FjaGVSZXN1bHRbJ3N0YXRlJ10pOiBSZXNwb25zZSB7XG4gIGNvbnN0IGhlYWRlcnMgPSBuZXcgSGVhZGVycyhlbnRyeS5oZWFkZXJzKTtcbiAgaWYgKCFoZWFkZXJzLmhhcygnY29udGVudC10eXBlJykpIHtcbiAgICBoZWFkZXJzLnNldCgnY29udGVudC10eXBlJywgJ3RleHQvaHRtbDsgY2hhcnNldD11dGYtOCcpO1xuICB9XG4gIGhlYWRlcnMuc2V0KCd4LW9wZW5lbGVtZW50LWlzcicsIHN0YXRlKTtcbiAgcmV0dXJuIG5ldyBSZXNwb25zZShlbnRyeS5odG1sLCB7IGhlYWRlcnMgfSk7XG59XG4iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBbUNBLE9BQU8sU0FBUyxxQkFDZCxRQUE0QixFQUM1QixJQUFZLEVBQ1osU0FBaUMsQ0FBQyxDQUFDO0VBRW5DLE1BQU0sZUFBZSxPQUFPLE9BQU8sQ0FBQyxRQUFRLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsRUFBRSxHQUFLLEVBQUUsYUFBYSxDQUFDO0VBQy9FLE9BQU8sU0FBUyxJQUFJLENBQUMsQ0FBQztJQUNwQixJQUFJLE1BQU0sSUFBSSxLQUFLLE1BQU0sT0FBTztJQUNoQyxNQUFNLGNBQWMsT0FBTyxPQUFPLENBQUMsTUFBTSxNQUFNLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxFQUFFLEdBQUssRUFBRSxhQUFhLENBQUM7SUFDcEYsSUFBSSxZQUFZLE1BQU0sS0FBSyxhQUFhLE1BQU0sRUFBRSxPQUFPO0lBQ3ZELE9BQU8sWUFBWSxLQUFLLENBQUMsQ0FBQyxDQUFDLEtBQUssTUFBTSxFQUFFO01BQ3RDLE1BQU0sQ0FBQyxVQUFVLFdBQVcsR0FBRyxZQUFZLENBQUMsTUFBTTtNQUNsRCxPQUFPLFFBQVEsWUFBWSxVQUFVO0lBQ3ZDO0VBQ0Y7QUFDRjtBQUVBLE9BQU8sZUFBZSxrQkFDcEIsSUFBWSxFQUNaLE1BQThCLEVBQzlCLE9BQTBCLEVBQzFCLE9BQWlCO0VB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
|
package/src/isr.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @openelement/core - route-level ISR cache primitives.
|
|
3
|
+
*
|
|
4
|
+
* v0.22: Platform adapters (CF Workers KV, Deno KV).
|
|
5
|
+
*
|
|
6
|
+
* Architecture:
|
|
7
|
+
* 1. Build: SSG produces static HTML + isr-manifest.json
|
|
8
|
+
* 2. Runtime: Edge handler checks cache before serving static
|
|
9
|
+
* 4. Stale: serve cached HTML + async background regeneration
|
|
10
|
+
*
|
|
11
|
+
* MemoryIsrCache is the reference in-memory implementation. Production adapters
|
|
12
|
+
* (Cloudflare Workers KV, Deno KV) are v0.22 scope.
|
|
13
|
+
*/ import type { CacheAdapter, CacheEntry, IsrCacheEntry, IsrCacheResult, IsrCacheState, IsrRouteConfig } from '@openelement/protocol/isr';
|
|
14
|
+
import type { IsrManifestEntry } from '@openelement/protocol/framework';
|
|
15
|
+
export type { CacheAdapter, CacheEntry, IsrCacheEntry, IsrCacheResult, IsrCacheState, IsrManifestEntry, IsrRouteConfig };
|
|
16
|
+
export declare function isIsrRouteConfig(value: unknown): value is IsrRouteConfig;
|
|
17
|
+
export declare function createIsrCacheKey(routePath: string, params?: Record<string, string>): string;
|
|
18
|
+
export declare class MemoryIsrCache {
|
|
19
|
+
get(key: string, now?: number): IsrCacheResult;
|
|
20
|
+
set(key: string, entry: IsrCacheEntry): void;
|
|
21
|
+
delete(key: string): void;
|
|
22
|
+
}
|
package/src/isr.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @openelement/core - route-level ISR cache primitives.
|
|
3
|
+
*
|
|
4
|
+
* v0.22: Platform adapters (CF Workers KV, Deno KV).
|
|
5
|
+
*
|
|
6
|
+
* Architecture:
|
|
7
|
+
* 1. Build: SSG produces static HTML + isr-manifest.json
|
|
8
|
+
* 2. Runtime: Edge handler checks cache before serving static
|
|
9
|
+
* 4. Stale: serve cached HTML + async background regeneration
|
|
10
|
+
*
|
|
11
|
+
* MemoryIsrCache is the reference in-memory implementation. Production adapters
|
|
12
|
+
* (Cloudflare Workers KV, Deno KV) are v0.22 scope.
|
|
13
|
+
*/ export function isIsrRouteConfig(value) {
|
|
14
|
+
return typeof value === 'object' && value !== null && typeof value.revalidate === 'number' && Number.isFinite(value.revalidate) && value.revalidate > 0;
|
|
15
|
+
}
|
|
16
|
+
export function createIsrCacheKey(routePath, params = {}) {
|
|
17
|
+
const sortedParams = Object.entries(params).sort(([a], [b])=>a.localeCompare(b));
|
|
18
|
+
const suffix = sortedParams.length === 0 ? '' : '?' + sortedParams.map(([key, value])=>`${encodeURIComponent(key)}=${encodeURIComponent(value)}`).join('&');
|
|
19
|
+
return `openelement:isr:${routePath}${suffix}`;
|
|
20
|
+
}
|
|
21
|
+
export class MemoryIsrCache {
|
|
22
|
+
#entries = new Map();
|
|
23
|
+
get(key, now = Date.now()) {
|
|
24
|
+
const entry = this.#entries.get(key);
|
|
25
|
+
if (!entry) return {
|
|
26
|
+
state: 'miss'
|
|
27
|
+
};
|
|
28
|
+
const ageSeconds = Math.max(0, Math.floor((now - entry.createdAt) / 1000));
|
|
29
|
+
return {
|
|
30
|
+
state: ageSeconds >= entry.revalidate ? 'stale' : 'hit',
|
|
31
|
+
entry
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
set(key, entry) {
|
|
35
|
+
this.#entries.set(key, entry);
|
|
36
|
+
}
|
|
37
|
+
delete(key) {
|
|
38
|
+
this.#entries.delete(key);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImZpbGU6Ly8vaG9tZS9ydW5uZXIvd29yay9vcGVuZWxlbWVudC9vcGVuZWxlbWVudC9wYWNrYWdlcy9jb3JlL3NyYy9pc3IudHMiXSwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBAb3BlbmVsZW1lbnQvY29yZSAtIHJvdXRlLWxldmVsIElTUiBjYWNoZSBwcmltaXRpdmVzLlxuICpcbiAqIHYwLjIyOiBQbGF0Zm9ybSBhZGFwdGVycyAoQ0YgV29ya2VycyBLViwgRGVubyBLVikuXG4gKlxuICogQXJjaGl0ZWN0dXJlOlxuICogICAxLiBCdWlsZDogU1NHIHByb2R1Y2VzIHN0YXRpYyBIVE1MICsgaXNyLW1hbmlmZXN0Lmpzb25cbiAqICAgMi4gUnVudGltZTogRWRnZSBoYW5kbGVyIGNoZWNrcyBjYWNoZSBiZWZvcmUgc2VydmluZyBzdGF0aWNcbiAqICAgNC4gU3RhbGU6IHNlcnZlIGNhY2hlZCBIVE1MICsgYXN5bmMgYmFja2dyb3VuZCByZWdlbmVyYXRpb25cbiAqXG4gKiBNZW1vcnlJc3JDYWNoZSBpcyB0aGUgcmVmZXJlbmNlIGluLW1lbW9yeSBpbXBsZW1lbnRhdGlvbi4gUHJvZHVjdGlvbiBhZGFwdGVyc1xuICogKENsb3VkZmxhcmUgV29ya2VycyBLViwgRGVubyBLVikgYXJlIHYwLjIyIHNjb3BlLlxuICovXG5cbmltcG9ydCB0eXBlIHtcbiAgQ2FjaGVBZGFwdGVyLFxuICBDYWNoZUVudHJ5LFxuICBJc3JDYWNoZUVudHJ5LFxuICBJc3JDYWNoZVJlc3VsdCxcbiAgSXNyQ2FjaGVTdGF0ZSxcbiAgSXNyUm91dGVDb25maWcsXG59IGZyb20gJ0BvcGVuZWxlbWVudC9wcm90b2NvbC9pc3InO1xuaW1wb3J0IHR5cGUgeyBJc3JNYW5pZmVzdEVudHJ5IH0gZnJvbSAnQG9wZW5lbGVtZW50L3Byb3RvY29sL2ZyYW1ld29yayc7XG5leHBvcnQgdHlwZSB7XG4gIENhY2hlQWRhcHRlcixcbiAgQ2FjaGVFbnRyeSxcbiAgSXNyQ2FjaGVFbnRyeSxcbiAgSXNyQ2FjaGVSZXN1bHQsXG4gIElzckNhY2hlU3RhdGUsXG4gIElzck1hbmlmZXN0RW50cnksXG4gIElzclJvdXRlQ29uZmlnLFxufTtcblxuZXhwb3J0IGZ1bmN0aW9uIGlzSXNyUm91dGVDb25maWcodmFsdWU6IHVua25vd24pOiB2YWx1ZSBpcyBJc3JSb3V0ZUNvbmZpZyB7XG4gIHJldHVybiB0eXBlb2YgdmFsdWUgPT09ICdvYmplY3QnICYmIHZhbHVlICE9PSBudWxsICYmXG4gICAgdHlwZW9mICh2YWx1ZSBhcyBJc3JSb3V0ZUNvbmZpZykucmV2YWxpZGF0ZSA9PT0gJ251bWJlcicgJiZcbiAgICBOdW1iZXIuaXNGaW5pdGUoKHZhbHVlIGFzIElzclJvdXRlQ29uZmlnKS5yZXZhbGlkYXRlKSAmJlxuICAgICh2YWx1ZSBhcyBJc3JSb3V0ZUNvbmZpZykucmV2YWxpZGF0ZSA+IDA7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVJc3JDYWNoZUtleShcbiAgcm91dGVQYXRoOiBzdHJpbmcsXG4gIHBhcmFtczogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHt9LFxuKTogc3RyaW5nIHtcbiAgY29uc3Qgc29ydGVkUGFyYW1zID0gT2JqZWN0LmVudHJpZXMocGFyYW1zKS5zb3J0KChbYV0sIFtiXSkgPT4gYS5sb2NhbGVDb21wYXJlKGIpKTtcbiAgY29uc3Qgc3VmZml4ID0gc29ydGVkUGFyYW1zLmxlbmd0aCA9PT0gMCA/ICcnIDogJz8nICtcbiAgICBzb3J0ZWRQYXJhbXMubWFwKChba2V5LCB2YWx1ZV0pID0+IGAke2VuY29kZVVSSUNvbXBvbmVudChrZXkpfT0ke2VuY29kZVVSSUNvbXBvbmVudCh2YWx1ZSl9YClcbiAgICAgIC5qb2luKCcmJyk7XG4gIHJldHVybiBgb3BlbmVsZW1lbnQ6aXNyOiR7cm91dGVQYXRofSR7c3VmZml4fWA7XG59XG5cbmV4cG9ydCBjbGFzcyBNZW1vcnlJc3JDYWNoZSB7XG4gIHJlYWRvbmx5ICNlbnRyaWVzID0gbmV3IE1hcDxzdHJpbmcsIElzckNhY2hlRW50cnk+KCk7XG5cbiAgZ2V0KGtleTogc3RyaW5nLCBub3c6IG51bWJlciA9IERhdGUubm93KCkpOiBJc3JDYWNoZVJlc3VsdCB7XG4gICAgY29uc3QgZW50cnkgPSB0aGlzLiNlbnRyaWVzLmdldChrZXkpO1xuICAgIGlmICghZW50cnkpIHJldHVybiB7IHN0YXRlOiAnbWlzcycgfTtcbiAgICBjb25zdCBhZ2VTZWNvbmRzID0gTWF0aC5tYXgoMCwgTWF0aC5mbG9vcigobm93IC0gZW50cnkuY3JlYXRlZEF0KSAvIDEwMDApKTtcbiAgICByZXR1cm4ge1xuICAgICAgc3RhdGU6IGFnZVNlY29uZHMgPj0gZW50cnkucmV2YWxpZGF0ZSA/ICdzdGFsZScgOiAnaGl0JyxcbiAgICAgIGVudHJ5LFxuICAgIH07XG4gIH1cblxuICBzZXQoa2V5OiBzdHJpbmcsIGVudHJ5OiBJc3JDYWNoZUVudHJ5KTogdm9pZCB7XG4gICAgdGhpcy4jZW50cmllcy5zZXQoa2V5LCBlbnRyeSk7XG4gIH1cblxuICBkZWxldGUoa2V5OiBzdHJpbmcpOiB2b2lkIHtcbiAgICB0aGlzLiNlbnRyaWVzLmRlbGV0ZShrZXkpO1xuICB9XG59XG4iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7OztDQVlDLEdBcUJELE9BQU8sU0FBUyxpQkFBaUIsS0FBYztFQUM3QyxPQUFPLE9BQU8sVUFBVSxZQUFZLFVBQVUsUUFDNUMsT0FBTyxBQUFDLE1BQXlCLFVBQVUsS0FBSyxZQUNoRCxPQUFPLFFBQVEsQ0FBQyxBQUFDLE1BQXlCLFVBQVUsS0FDcEQsQUFBQyxNQUF5QixVQUFVLEdBQUc7QUFDM0M7QUFFQSxPQUFPLFNBQVMsa0JBQ2QsU0FBaUIsRUFDakIsU0FBaUMsQ0FBQyxDQUFDO0VBRW5DLE1BQU0sZUFBZSxPQUFPLE9BQU8sQ0FBQyxRQUFRLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsRUFBRSxHQUFLLEVBQUUsYUFBYSxDQUFDO0VBQy9FLE1BQU0sU0FBUyxhQUFhLE1BQU0sS0FBSyxJQUFJLEtBQUssTUFDOUMsYUFBYSxHQUFHLENBQUMsQ0FBQyxDQUFDLEtBQUssTUFBTSxHQUFLLEdBQUcsbUJBQW1CLEtBQUssQ0FBQyxFQUFFLG1CQUFtQixRQUFRLEVBQ3pGLElBQUksQ0FBQztFQUNWLE9BQU8sQ0FBQyxnQkFBZ0IsRUFBRSxZQUFZLFFBQVE7QUFDaEQ7QUFFQSxPQUFPLE1BQU07RUFDRixDQUFBLE9BQVEsR0FBRyxJQUFJLE1BQTZCO0VBRXJELElBQUksR0FBVyxFQUFFLE1BQWMsS0FBSyxHQUFHLEVBQUUsRUFBa0I7SUFDekQsTUFBTSxRQUFRLElBQUksQ0FBQyxDQUFBLE9BQVEsQ0FBQyxHQUFHLENBQUM7SUFDaEMsSUFBSSxDQUFDLE9BQU8sT0FBTztNQUFFLE9BQU87SUFBTztJQUNuQyxNQUFNLGFBQWEsS0FBSyxHQUFHLENBQUMsR0FBRyxLQUFLLEtBQUssQ0FBQyxDQUFDLE1BQU0sTUFBTSxTQUFTLElBQUk7SUFDcEUsT0FBTztNQUNMLE9BQU8sY0FBYyxNQUFNLFVBQVUsR0FBRyxVQUFVO01BQ2xEO0lBQ0Y7RUFDRjtFQUVBLElBQUksR0FBVyxFQUFFLEtBQW9CLEVBQVE7SUFDM0MsSUFBSSxDQUFDLENBQUEsT0FBUSxDQUFDLEdBQUcsQ0FBQyxLQUFLO0VBQ3pCO0VBRUEsT0FBTyxHQUFXLEVBQVE7SUFDeEIsSUFBSSxDQUFDLENBQUEsT0FBUSxDQUFDLE1BQU0sQ0FBQztFQUN2QjtBQUNGIn0=
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Signal } from '@openelement/protocol/signal';
|
|
2
|
+
import type { BindingDescriptor, BindingLifecycle } from "./binding-descriptor.js";
|
|
3
|
+
/**
|
|
4
|
+
* Collect BindingDescriptor objects from a JSX props object.
|
|
5
|
+
*
|
|
6
|
+
* @param el - Target element the descriptors will apply to.
|
|
7
|
+
* @param props - VNode props.
|
|
8
|
+
* @param signalRegistry - Optional registry used to name signals for hydration markers.
|
|
9
|
+
* @returns Array of binding descriptors.
|
|
10
|
+
*/ export declare function collectPropBindings(el: Element, props: Record<string, unknown>, signalRegistry?: Map<string, Signal<unknown>>): BindingDescriptor[];
|
|
11
|
+
/**
|
|
12
|
+
* Apply a props object to a real DOM element.
|
|
13
|
+
*/ export declare function applyProps(el: Element, props: Record<string, unknown>, lifecycle?: BindingLifecycle, signalRegistry?: Map<string, Signal<unknown>>): void;
|
|
14
|
+
/**
|
|
15
|
+
* Render a VNode tree to a real DOM node.
|
|
16
|
+
*
|
|
17
|
+
* @param node - VNode, string, number, or null/undefined
|
|
18
|
+
* @param lifecycle - Optional BindingLifecycle for automatic cleanup
|
|
19
|
+
* @param disposers - Optional Set to collect effect dispose fns (backward compat)
|
|
20
|
+
* @param signalRegistry - Optional registry used to resolve signal names for markers
|
|
21
|
+
* @returns DOM Node (Element, Text, or DocumentFragment)
|
|
22
|
+
*/ export declare function renderToDom(node: unknown, lifecycle?: BindingLifecycle, disposers?: Set<() => void>, signalRegistry?: Map<string, Signal<unknown>>): Node;
|