webspresso 0.0.110 → 0.0.112
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/core/client/adapter/datastar.js +190 -51
- package/core/client/api.js +23 -21
- package/core/client/controller.js +407 -40
- package/core/client/debug.js +12 -12
- package/core/client/devtool.js +124 -19
- package/core/client/forms.js +163 -29
- package/core/client/fragments.js +616 -118
- package/core/client/host.js +369 -0
- package/core/client/index.d.ts +18 -0
- package/core/client/index.js +154 -23
- package/core/client/lite.js +101 -4
- package/core/client/server-fragments.js +142 -22
- package/core/client/services.js +588 -0
- package/core/client/utils.js +188 -2
- package/index.d.ts +334 -29
- package/index.js +3 -0
- package/package.json +1 -1
- package/src/bundle/index.js +175 -14
- package/src/dev/banner.js +4 -1
- package/src/dev-reload/index.js +29 -1
- package/src/server.js +28 -0
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Webspresso Client UI Host Lifecycle Helper (`core/client/host.js`)
|
|
5
|
+
*
|
|
6
|
+
* Manages optional, lazy DOM host containers for client services that need
|
|
7
|
+
* to render shared UI outside normal SSR fragment patch regions.
|
|
8
|
+
*
|
|
9
|
+
* Principles:
|
|
10
|
+
* - Zero DOM creation until a service explicitly requests a host (`mountHost`).
|
|
11
|
+
* - Idempotent per host key within a runtime: repeated calls return the same host handle
|
|
12
|
+
* and never create duplicate DOM containers.
|
|
13
|
+
* - Supports attaching to an existing layout element (`target` / `selector` / `[data-w-host]`)
|
|
14
|
+
* or lazily creating a container appended outside fragment areas (default: `document.body`).
|
|
15
|
+
* - Clean unmount and resource disposal contract when the owning service or runtime is destroyed.
|
|
16
|
+
* - Agnostic to UI rendering: does not impose any template language, virtual DOM, or widget type.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const { decorateElement } = require('./utils');
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Resolve the owning Document from a root Document or Element.
|
|
23
|
+
*
|
|
24
|
+
* @param {Document|Element|null} [root]
|
|
25
|
+
* @returns {Document|null}
|
|
26
|
+
*/
|
|
27
|
+
function resolveDocument(root) {
|
|
28
|
+
if (root) {
|
|
29
|
+
if (root.nodeType === 9) return root;
|
|
30
|
+
if (root.ownerDocument) return root.ownerDocument;
|
|
31
|
+
}
|
|
32
|
+
return typeof document !== 'undefined' ? document : null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Resolve the default mount container outside `[data-w-fragment]` areas.
|
|
37
|
+
*
|
|
38
|
+
* @param {Document|Element|null} [root]
|
|
39
|
+
* @param {Document|null} [doc]
|
|
40
|
+
* @returns {Element|null}
|
|
41
|
+
*/
|
|
42
|
+
function resolveDefaultContainer(root, doc) {
|
|
43
|
+
if (root && root.nodeType === 1 && root.tagName !== 'HTML') {
|
|
44
|
+
if (doc && doc.body && (root === doc.body || root === doc.documentElement)) {
|
|
45
|
+
return doc.body;
|
|
46
|
+
}
|
|
47
|
+
const insideFragment =
|
|
48
|
+
typeof root.closest === 'function' ? Boolean(root.closest('[data-w-fragment]')) : false;
|
|
49
|
+
if (!insideFragment) {
|
|
50
|
+
return root;
|
|
51
|
+
}
|
|
52
|
+
if (!doc || !doc.body) {
|
|
53
|
+
return root;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (doc) {
|
|
57
|
+
return doc.body || doc.documentElement || null;
|
|
58
|
+
}
|
|
59
|
+
return root && root.nodeType === 1 ? root : null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Create a runtime-scoped UI Host Manager.
|
|
64
|
+
*
|
|
65
|
+
* @param {object} [options]
|
|
66
|
+
* @param {Document|Element|null} [options.root]
|
|
67
|
+
* @param {object} [options.debug]
|
|
68
|
+
* @returns {object}
|
|
69
|
+
*/
|
|
70
|
+
function createClientHostManager(options = {}) {
|
|
71
|
+
const root = options.root || null;
|
|
72
|
+
const debug = options.debug || null;
|
|
73
|
+
const activeHosts = new Map(); // key -> hostRecord
|
|
74
|
+
let disposed = false;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Mount or retrieve a shared UI host by key.
|
|
78
|
+
*
|
|
79
|
+
* @param {string|object} [keyOrOptions]
|
|
80
|
+
* @param {object} [maybeOptions]
|
|
81
|
+
* @param {string} [defaultKey]
|
|
82
|
+
*/
|
|
83
|
+
function mount(keyOrOptions = {}, maybeOptions = {}, defaultKey = 'default') {
|
|
84
|
+
if (disposed) {
|
|
85
|
+
const err = new Error('Cannot mount UI host on a disposed client runtime.');
|
|
86
|
+
err.code = 'CLIENT_RUNTIME_DISPOSED';
|
|
87
|
+
throw err;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
let opts = {};
|
|
91
|
+
let key = defaultKey;
|
|
92
|
+
|
|
93
|
+
if (typeof keyOrOptions === 'string') {
|
|
94
|
+
key = keyOrOptions.trim() || defaultKey;
|
|
95
|
+
opts = maybeOptions && typeof maybeOptions === 'object' ? maybeOptions : {};
|
|
96
|
+
} else if (keyOrOptions && typeof keyOrOptions === 'object') {
|
|
97
|
+
opts = keyOrOptions;
|
|
98
|
+
if (typeof opts.key === 'string' && opts.key.trim()) {
|
|
99
|
+
key = opts.key.trim();
|
|
100
|
+
} else if (typeof opts.name === 'string' && opts.name.trim()) {
|
|
101
|
+
key = opts.name.trim();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (!key) {
|
|
106
|
+
throw new TypeError('mountHost() requires a non-empty host key.');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const existing = activeHosts.get(key);
|
|
110
|
+
if (existing && existing.mounted) {
|
|
111
|
+
// If the element is still connected (or in a headless/custom tree), reuse it
|
|
112
|
+
if (!existing.el || existing.el.isConnected !== false || !existing.created) {
|
|
113
|
+
return existing.handle;
|
|
114
|
+
}
|
|
115
|
+
// If a created host element was externally removed from DOM, clean up before re-creating
|
|
116
|
+
existing.handle.unmount();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const doc = resolveDocument(opts.root || root);
|
|
120
|
+
let el = null;
|
|
121
|
+
let created = false;
|
|
122
|
+
const prevAttributes = new Map();
|
|
123
|
+
|
|
124
|
+
// 1. Explicit target Element
|
|
125
|
+
if (opts.target && opts.target.nodeType === 1) {
|
|
126
|
+
el = opts.target;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// 2. Explicit CSS selector
|
|
130
|
+
if (!el && typeof opts.selector === 'string' && opts.selector.trim()) {
|
|
131
|
+
const searchRoot = opts.root || root || doc;
|
|
132
|
+
if (searchRoot && typeof searchRoot.querySelector === 'function') {
|
|
133
|
+
el = searchRoot.querySelector(opts.selector.trim());
|
|
134
|
+
}
|
|
135
|
+
if (!el && doc && searchRoot !== doc && typeof doc.querySelector === 'function') {
|
|
136
|
+
el = doc.querySelector(opts.selector.trim());
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// 3. Existing layout placeholder `[data-w-host="<key>"]` not owned by another active host manager
|
|
141
|
+
if (!el && opts.createOnly !== true) {
|
|
142
|
+
const searchRoot = opts.root || root || doc;
|
|
143
|
+
if (searchRoot && typeof searchRoot.querySelectorAll === 'function') {
|
|
144
|
+
const candidates = searchRoot.querySelectorAll(`[data-w-host="${key}"]`);
|
|
145
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
146
|
+
const cand = candidates[i];
|
|
147
|
+
if (!cand.__wHostManager || cand.__wHostManager === api) {
|
|
148
|
+
el = cand;
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// 4. Create a new host container outside fragment regions
|
|
156
|
+
if (!el) {
|
|
157
|
+
if (!doc || typeof doc.createElement !== 'function') {
|
|
158
|
+
const err = new Error(`Cannot create UI host "${key}": no Document is available in the current environment.`);
|
|
159
|
+
err.code = 'CLIENT_HOST_NO_DOCUMENT';
|
|
160
|
+
throw err;
|
|
161
|
+
}
|
|
162
|
+
const tagName = typeof opts.tagName === 'string' && opts.tagName.trim() ? opts.tagName.trim() : 'div';
|
|
163
|
+
el = doc.createElement(tagName);
|
|
164
|
+
created = true;
|
|
165
|
+
|
|
166
|
+
const container =
|
|
167
|
+
(opts.container && opts.container.nodeType === 1 ? opts.container : null) ||
|
|
168
|
+
resolveDefaultContainer(opts.root || root, doc);
|
|
169
|
+
|
|
170
|
+
if (container && typeof container.appendChild === 'function') {
|
|
171
|
+
container.appendChild(el);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function recordAndSetAttr(attrName, attrVal) {
|
|
176
|
+
if (!el || typeof el.setAttribute !== 'function') return;
|
|
177
|
+
if (!created && !prevAttributes.has(attrName)) {
|
|
178
|
+
prevAttributes.set(attrName, el.hasAttribute(attrName) ? el.getAttribute(attrName) : null);
|
|
179
|
+
}
|
|
180
|
+
el.setAttribute(attrName, String(attrVal));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
recordAndSetAttr('data-w-host', key);
|
|
184
|
+
|
|
185
|
+
if (typeof opts.id === 'string' && opts.id.trim()) {
|
|
186
|
+
recordAndSetAttr('id', opts.id.trim());
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (typeof opts.className === 'string' && opts.className.trim()) {
|
|
190
|
+
if (created) {
|
|
191
|
+
el.className = opts.className.trim();
|
|
192
|
+
} else {
|
|
193
|
+
recordAndSetAttr('class', opts.className.trim());
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (opts.attributes && typeof opts.attributes === 'object') {
|
|
198
|
+
for (const [attrName, attrVal] of Object.entries(opts.attributes)) {
|
|
199
|
+
if (attrVal === undefined || attrVal === null || attrVal === false) continue;
|
|
200
|
+
recordAndSetAttr(attrName, attrVal === true ? '' : String(attrVal));
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
try {
|
|
205
|
+
Object.defineProperty(el, '__wHostManager', {
|
|
206
|
+
value: api,
|
|
207
|
+
configurable: true,
|
|
208
|
+
enumerable: false,
|
|
209
|
+
writable: true,
|
|
210
|
+
});
|
|
211
|
+
} catch (_e) {
|
|
212
|
+
el.__wHostManager = api;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const decoratedEl = decorateElement(el);
|
|
216
|
+
const unmountCallbacks = [];
|
|
217
|
+
let isMounted = true;
|
|
218
|
+
|
|
219
|
+
function onUnmount(fn) {
|
|
220
|
+
if (typeof fn !== 'function') return () => {};
|
|
221
|
+
if (!isMounted) {
|
|
222
|
+
try {
|
|
223
|
+
fn();
|
|
224
|
+
} catch (_e) {}
|
|
225
|
+
return () => {};
|
|
226
|
+
}
|
|
227
|
+
unmountCallbacks.push(fn);
|
|
228
|
+
return () => {
|
|
229
|
+
const idx = unmountCallbacks.indexOf(fn);
|
|
230
|
+
if (idx !== -1) unmountCallbacks.splice(idx, 1);
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function clear() {
|
|
235
|
+
if (el) {
|
|
236
|
+
el.innerHTML = '';
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function unmount() {
|
|
241
|
+
if (!isMounted) return;
|
|
242
|
+
isMounted = false;
|
|
243
|
+
activeHosts.delete(key);
|
|
244
|
+
|
|
245
|
+
for (let i = unmountCallbacks.length - 1; i >= 0; i--) {
|
|
246
|
+
try {
|
|
247
|
+
unmountCallbacks[i](decoratedEl);
|
|
248
|
+
} catch (err) {
|
|
249
|
+
if (debug && typeof debug.trackCleanupError === 'function') {
|
|
250
|
+
debug.trackCleanupError(`host_${key}`, key, 'host:unmount', err);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
unmountCallbacks.length = 0;
|
|
255
|
+
|
|
256
|
+
if (el) {
|
|
257
|
+
if (el.__wHostManager === api) {
|
|
258
|
+
delete el.__wHostManager;
|
|
259
|
+
}
|
|
260
|
+
if (created) {
|
|
261
|
+
if (el.parentNode && typeof el.parentNode.removeChild === 'function') {
|
|
262
|
+
el.parentNode.removeChild(el);
|
|
263
|
+
} else if (typeof el.remove === 'function') {
|
|
264
|
+
el.remove();
|
|
265
|
+
}
|
|
266
|
+
} else {
|
|
267
|
+
if (opts.clearOnUnmount !== false) {
|
|
268
|
+
el.innerHTML = '';
|
|
269
|
+
}
|
|
270
|
+
for (const [attrName, prevVal] of prevAttributes.entries()) {
|
|
271
|
+
if (prevVal === null) {
|
|
272
|
+
el.removeAttribute(attrName);
|
|
273
|
+
} else {
|
|
274
|
+
el.setAttribute(attrName, prevVal);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (debug && typeof debug.recordEvent === 'function') {
|
|
281
|
+
debug.recordEvent('host:unmount', { key, created });
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const handle = Object.freeze({
|
|
286
|
+
key,
|
|
287
|
+
el: decoratedEl,
|
|
288
|
+
created,
|
|
289
|
+
get mounted() {
|
|
290
|
+
return isMounted;
|
|
291
|
+
},
|
|
292
|
+
clear,
|
|
293
|
+
onUnmount,
|
|
294
|
+
unmount,
|
|
295
|
+
destroy: unmount,
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
activeHosts.set(key, {
|
|
299
|
+
key,
|
|
300
|
+
el: decoratedEl,
|
|
301
|
+
created,
|
|
302
|
+
get mounted() {
|
|
303
|
+
return isMounted;
|
|
304
|
+
},
|
|
305
|
+
handle,
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
if (debug && typeof debug.recordEvent === 'function') {
|
|
309
|
+
debug.recordEvent('host:mount', { key, created });
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return handle;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function get(key) {
|
|
316
|
+
const record = activeHosts.get(String(key || '').trim());
|
|
317
|
+
return record && record.mounted ? record.handle : null;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function has(key) {
|
|
321
|
+
const record = activeHosts.get(String(key || '').trim());
|
|
322
|
+
return Boolean(record && record.mounted);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function unmount(key) {
|
|
326
|
+
const record = activeHosts.get(String(key || '').trim());
|
|
327
|
+
if (record && record.mounted) {
|
|
328
|
+
record.handle.unmount();
|
|
329
|
+
return true;
|
|
330
|
+
}
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function dispose() {
|
|
335
|
+
if (disposed) return;
|
|
336
|
+
disposed = true;
|
|
337
|
+
const records = Array.from(activeHosts.values());
|
|
338
|
+
activeHosts.clear();
|
|
339
|
+
for (let i = records.length - 1; i >= 0; i--) {
|
|
340
|
+
try {
|
|
341
|
+
records[i].handle.unmount();
|
|
342
|
+
} catch (err) {
|
|
343
|
+
if (debug && typeof debug.trackCleanupError === 'function') {
|
|
344
|
+
debug.trackCleanupError(`host_${records[i].key}`, records[i].key, 'host:dispose', err);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
const api = {
|
|
351
|
+
mount,
|
|
352
|
+
get,
|
|
353
|
+
has,
|
|
354
|
+
unmount,
|
|
355
|
+
dispose,
|
|
356
|
+
get isDisposed() {
|
|
357
|
+
return disposed;
|
|
358
|
+
},
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
return api;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
module.exports = {
|
|
365
|
+
createClientHostManager,
|
|
366
|
+
resolveDocument,
|
|
367
|
+
resolveDefaultContainer,
|
|
368
|
+
};
|
|
369
|
+
|
package/core/client/index.d.ts
CHANGED
|
@@ -6,9 +6,27 @@ export {
|
|
|
6
6
|
ClientRetryOptions,
|
|
7
7
|
ClientApiClient,
|
|
8
8
|
AppApiRouteMap,
|
|
9
|
+
AppClientServices,
|
|
10
|
+
ClientServiceMountHostOptions,
|
|
11
|
+
ClientServiceHostHandle,
|
|
12
|
+
ClientServiceHostHelper,
|
|
13
|
+
ClientServiceContext,
|
|
14
|
+
ClientServiceDefinition,
|
|
15
|
+
InferClientServiceExport,
|
|
16
|
+
InferClientService,
|
|
17
|
+
InferClientServicesMap,
|
|
18
|
+
ResolvedClientServicesMap,
|
|
19
|
+
ClientServiceKey,
|
|
20
|
+
ClientServicesRegistry,
|
|
21
|
+
ClientRuntimeOptions,
|
|
22
|
+
ClientRuntime,
|
|
23
|
+
defineClientService,
|
|
24
|
+
createClientServiceRegistry,
|
|
25
|
+
createClientHostManager,
|
|
9
26
|
ClientTask,
|
|
10
27
|
ClientTaskOptions,
|
|
11
28
|
ClientFormSubmitResult,
|
|
29
|
+
ClientFormHookMeta,
|
|
12
30
|
ClientFormHandle,
|
|
13
31
|
ClientFormFillOptions,
|
|
14
32
|
ControllerFormsRegistry,
|
package/core/client/index.js
CHANGED
|
@@ -57,11 +57,6 @@ const {
|
|
|
57
57
|
deduplicateParentChildFragments,
|
|
58
58
|
} = require('./fragments');
|
|
59
59
|
|
|
60
|
-
const {
|
|
61
|
-
createClientDebugger,
|
|
62
|
-
summarizePayloadSafely,
|
|
63
|
-
} = require('./debug');
|
|
64
|
-
|
|
65
60
|
const {
|
|
66
61
|
MemoryStorage,
|
|
67
62
|
StorageAdapter,
|
|
@@ -71,10 +66,62 @@ const {
|
|
|
71
66
|
} = require('./storage');
|
|
72
67
|
|
|
73
68
|
const {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
} = require('./
|
|
69
|
+
defineClientService,
|
|
70
|
+
createClientServiceRegistry,
|
|
71
|
+
createServiceError,
|
|
72
|
+
} = require('./services');
|
|
73
|
+
|
|
74
|
+
const {
|
|
75
|
+
createClientHostManager,
|
|
76
|
+
} = require('./host');
|
|
77
|
+
|
|
78
|
+
const { isArray, isNumber, NOOP_DEBUGGER, randomId, uid } = require('./utils');
|
|
79
|
+
|
|
80
|
+
const NOOP_DEVTOOL_HANDLE = Object.freeze({
|
|
81
|
+
open() {},
|
|
82
|
+
close() {},
|
|
83
|
+
toggle() {},
|
|
84
|
+
refresh() {},
|
|
85
|
+
destroy() {},
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
let _debugMod = null;
|
|
89
|
+
function getDebugModule() {
|
|
90
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
91
|
+
if (!_debugMod) {
|
|
92
|
+
_debugMod = require('./debug');
|
|
93
|
+
}
|
|
94
|
+
return _debugMod;
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
createClientDebugger: () => NOOP_DEBUGGER,
|
|
98
|
+
summarizePayloadSafely: () => null,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
let _devtoolMod = null;
|
|
103
|
+
function getDevtoolModule() {
|
|
104
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
105
|
+
if (!_devtoolMod) {
|
|
106
|
+
_devtoolMod = require('./devtool');
|
|
107
|
+
}
|
|
108
|
+
return _devtoolMod;
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
mountDevtool: () => NOOP_DEVTOOL_HANDLE,
|
|
112
|
+
highlightElement: () => {},
|
|
113
|
+
setupReduxDevToolsBridge: () => null,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
let _liteMod = null;
|
|
118
|
+
function getLiteModule() {
|
|
119
|
+
if (!_liteMod) {
|
|
120
|
+
const reqFn = typeof module !== 'undefined' && module.require ? module.require.bind(module) : require;
|
|
121
|
+
_liteMod = reqFn('./lite');
|
|
122
|
+
}
|
|
123
|
+
return _liteMod;
|
|
124
|
+
}
|
|
78
125
|
|
|
79
126
|
let _serverFragments = null;
|
|
80
127
|
function getServerFragments() {
|
|
@@ -106,10 +153,12 @@ function createClientRuntime(options = {}) {
|
|
|
106
153
|
options.debugger ||
|
|
107
154
|
(options.debug && typeof options.debug === 'object' && typeof options.debug.getSnapshot === 'function'
|
|
108
155
|
? options.debug
|
|
109
|
-
:
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
156
|
+
: options.debug
|
|
157
|
+
? getDebugModule().createClientDebugger({
|
|
158
|
+
enabled: true,
|
|
159
|
+
logger: options.debugLogger,
|
|
160
|
+
})
|
|
161
|
+
: NOOP_DEBUGGER);
|
|
113
162
|
|
|
114
163
|
const datastar = createDatastarAdapter({
|
|
115
164
|
datastar: options.datastar,
|
|
@@ -124,6 +173,14 @@ function createClientRuntime(options = {}) {
|
|
|
124
173
|
endpoint: options.fragmentEndpoint || DEFAULT_FRAGMENT_ENDPOINT,
|
|
125
174
|
datastar,
|
|
126
175
|
debug,
|
|
176
|
+
staleTime: options.staleTime,
|
|
177
|
+
refetchOnWindowFocus: options.refetchOnWindowFocus,
|
|
178
|
+
refetchOnFocus: options.refetchOnFocus,
|
|
179
|
+
refetchOnReconnect: options.refetchOnReconnect,
|
|
180
|
+
refetchOnMount: options.refetchOnMount,
|
|
181
|
+
refetchOnVisit: options.refetchOnVisit,
|
|
182
|
+
refetchInterval: options.refetchInterval,
|
|
183
|
+
refetchIntervalInBackground: options.refetchIntervalInBackground,
|
|
127
184
|
getCsrfToken: options.getCsrfToken || (() => discoverCsrfToken(rootOption)),
|
|
128
185
|
onBeforeNodeRemove(removedEl) {
|
|
129
186
|
if (controllers) {
|
|
@@ -153,11 +210,26 @@ function createClientRuntime(options = {}) {
|
|
|
153
210
|
|
|
154
211
|
const api = createApiClient(apiConfig);
|
|
155
212
|
|
|
213
|
+
let runtime = null;
|
|
214
|
+
|
|
215
|
+
const services =
|
|
216
|
+
options.services && typeof options.services.get === 'function' && typeof options.services.register === 'function'
|
|
217
|
+
? options.services
|
|
218
|
+
: createClientServiceRegistry({
|
|
219
|
+
root: rootOption,
|
|
220
|
+
api,
|
|
221
|
+
debug,
|
|
222
|
+
getRuntime: () => runtime,
|
|
223
|
+
services: options.services,
|
|
224
|
+
});
|
|
225
|
+
|
|
156
226
|
controllers = createControllerRuntime({
|
|
227
|
+
root: rootOption,
|
|
157
228
|
datastar,
|
|
158
229
|
fragments,
|
|
159
230
|
debug,
|
|
160
231
|
apiConfig,
|
|
232
|
+
services,
|
|
161
233
|
lazyLoader: options.lazyLoader,
|
|
162
234
|
});
|
|
163
235
|
|
|
@@ -216,24 +288,37 @@ function createClientRuntime(options = {}) {
|
|
|
216
288
|
observer = null;
|
|
217
289
|
}
|
|
218
290
|
controllers.destroyAll();
|
|
291
|
+
if (fragments && typeof fragments.dispose === 'function') {
|
|
292
|
+
fragments.dispose();
|
|
293
|
+
}
|
|
294
|
+
services.dispose();
|
|
295
|
+
if (runtime && runtime.devtool && typeof runtime.devtool.destroy === 'function') {
|
|
296
|
+
try {
|
|
297
|
+
runtime.devtool.destroy();
|
|
298
|
+
} catch (_e) {}
|
|
299
|
+
runtime.devtool = null;
|
|
300
|
+
}
|
|
219
301
|
}
|
|
220
302
|
|
|
221
|
-
|
|
303
|
+
runtime = {
|
|
222
304
|
api,
|
|
305
|
+
services,
|
|
223
306
|
fragments,
|
|
224
307
|
controllers,
|
|
225
308
|
datastar,
|
|
226
309
|
adapter: datastar,
|
|
227
310
|
debug,
|
|
228
311
|
register: (name, def, meta) => controllers.register(name, def, meta),
|
|
312
|
+
registerService: (name, def) => services.register(name, def),
|
|
229
313
|
mount,
|
|
230
314
|
unmount,
|
|
231
315
|
destroy,
|
|
316
|
+
dispose: destroy,
|
|
232
317
|
getControllerInstance: (el, name) => controllers.getInstanceForElement(el, name),
|
|
233
318
|
getController: (el, name) => controllers.getInstanceForElement(el, name),
|
|
234
319
|
invalidate: (tags, meta) => fragments.invalidate(tags, meta),
|
|
235
320
|
refresh: (idOrName, input, opts) => fragments.refresh(idOrName, input, opts),
|
|
236
|
-
mountDevtool: (opts) => mountDevtool(runtime, opts),
|
|
321
|
+
mountDevtool: (opts) => getDevtoolModule().mountDevtool(runtime, opts),
|
|
237
322
|
forms: {
|
|
238
323
|
parse: parseFormElement,
|
|
239
324
|
create: (formEl, formOpts = {}) =>
|
|
@@ -241,6 +326,7 @@ function createClientRuntime(options = {}) {
|
|
|
241
326
|
api,
|
|
242
327
|
datastar,
|
|
243
328
|
debug,
|
|
329
|
+
services,
|
|
244
330
|
...formOpts,
|
|
245
331
|
}),
|
|
246
332
|
},
|
|
@@ -257,7 +343,32 @@ function createClientRuntime(options = {}) {
|
|
|
257
343
|
* Boot the client runtime on DOMContentLoaded (or immediately if ready).
|
|
258
344
|
*/
|
|
259
345
|
function bootClient(options = {}) {
|
|
346
|
+
const bootRoot = options.root || options.document || (typeof document !== 'undefined' ? document : null);
|
|
347
|
+
if (
|
|
348
|
+
options.hmr !== false &&
|
|
349
|
+
bootRoot &&
|
|
350
|
+
bootRoot.__wBootedClientRuntime &&
|
|
351
|
+
typeof bootRoot.__wBootedClientRuntime.destroy === 'function'
|
|
352
|
+
) {
|
|
353
|
+
try {
|
|
354
|
+
bootRoot.__wBootedClientRuntime.destroy();
|
|
355
|
+
} catch (_e) {}
|
|
356
|
+
}
|
|
357
|
+
|
|
260
358
|
const runtime = createClientRuntime(options);
|
|
359
|
+
if (bootRoot && (typeof bootRoot === 'object' || typeof bootRoot === 'function')) {
|
|
360
|
+
try {
|
|
361
|
+
Object.defineProperty(bootRoot, '__wBootedClientRuntime', {
|
|
362
|
+
value: runtime,
|
|
363
|
+
configurable: true,
|
|
364
|
+
enumerable: false,
|
|
365
|
+
writable: true,
|
|
366
|
+
});
|
|
367
|
+
} catch (_e) {
|
|
368
|
+
bootRoot.__wBootedClientRuntime = runtime;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
261
372
|
const shouldMountDevtools =
|
|
262
373
|
options.devtools === true ||
|
|
263
374
|
(options.devtools !== false &&
|
|
@@ -271,7 +382,7 @@ function bootClient(options = {}) {
|
|
|
271
382
|
const onReady = () => {
|
|
272
383
|
runtime.mount();
|
|
273
384
|
if (shouldMountDevtools) {
|
|
274
|
-
runtime.devtool = mountDevtool(runtime, options.devtoolsOptions || {});
|
|
385
|
+
runtime.devtool = getDevtoolModule().mountDevtool(runtime, options.devtoolsOptions || {});
|
|
275
386
|
}
|
|
276
387
|
};
|
|
277
388
|
if (document.readyState === 'loading') {
|
|
@@ -290,10 +401,10 @@ module.exports = {
|
|
|
290
401
|
createClientRuntime,
|
|
291
402
|
bootClient,
|
|
292
403
|
get createLiteClientRuntime() {
|
|
293
|
-
return
|
|
404
|
+
return getLiteModule().createLiteClientRuntime;
|
|
294
405
|
},
|
|
295
406
|
get bootLiteClient() {
|
|
296
|
-
return
|
|
407
|
+
return getLiteModule().bootLiteClient;
|
|
297
408
|
},
|
|
298
409
|
createControllerRuntime,
|
|
299
410
|
queryScopedTargets,
|
|
@@ -369,17 +480,31 @@ module.exports = {
|
|
|
369
480
|
createDatastarAdapter,
|
|
370
481
|
createSignalsEngine,
|
|
371
482
|
evaluateStateExpression,
|
|
483
|
+
isArray,
|
|
484
|
+
isNumber,
|
|
485
|
+
randomId,
|
|
486
|
+
uid,
|
|
372
487
|
getByPath,
|
|
373
488
|
setByPath,
|
|
374
489
|
morphDomTrees,
|
|
375
490
|
parseHtmlFragment,
|
|
376
491
|
|
|
377
492
|
// Debug & Telemetry & DevTools
|
|
378
|
-
createClientDebugger
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
493
|
+
get createClientDebugger() {
|
|
494
|
+
return getDebugModule().createClientDebugger;
|
|
495
|
+
},
|
|
496
|
+
get summarizePayloadSafely() {
|
|
497
|
+
return getDebugModule().summarizePayloadSafely;
|
|
498
|
+
},
|
|
499
|
+
get mountDevtool() {
|
|
500
|
+
return getDevtoolModule().mountDevtool;
|
|
501
|
+
},
|
|
502
|
+
get highlightElement() {
|
|
503
|
+
return getDevtoolModule().highlightElement;
|
|
504
|
+
},
|
|
505
|
+
get setupReduxDevToolsBridge() {
|
|
506
|
+
return getDevtoolModule().setupReduxDevToolsBridge;
|
|
507
|
+
},
|
|
383
508
|
|
|
384
509
|
// Client Storage & Persistence
|
|
385
510
|
MemoryStorage,
|
|
@@ -387,5 +512,11 @@ module.exports = {
|
|
|
387
512
|
getNativeStorageBackend,
|
|
388
513
|
createClientStorage,
|
|
389
514
|
defaultStorage,
|
|
515
|
+
|
|
516
|
+
// Client Services & Optional Lazy UI Hosts
|
|
517
|
+
defineClientService,
|
|
518
|
+
createClientServiceRegistry,
|
|
519
|
+
createServiceError,
|
|
520
|
+
createClientHostManager,
|
|
390
521
|
};
|
|
391
522
|
|