streetui 1.0.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 +127 -0
- package/dist/bin.cjs +894 -0
- package/dist/bin.cjs.map +1 -0
- package/dist/bin.d.cts +1 -0
- package/dist/bin.d.ts +1 -0
- package/dist/bin.js +892 -0
- package/dist/bin.js.map +1 -0
- package/dist/compile-B0q07Hzq.d.cts +656 -0
- package/dist/compile-B0q07Hzq.d.ts +656 -0
- package/dist/create-bin.cjs +896 -0
- package/dist/create-bin.cjs.map +1 -0
- package/dist/create-bin.d.cts +1 -0
- package/dist/create-bin.d.ts +1 -0
- package/dist/create-bin.js +894 -0
- package/dist/create-bin.js.map +1 -0
- package/dist/hydration-diagnostics-BE6xVWD1.d.cts +89 -0
- package/dist/hydration-diagnostics-Bck5dMbz.d.ts +89 -0
- package/dist/index.cjs +4284 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1759 -0
- package/dist/index.d.ts +1759 -0
- package/dist/index.js +4114 -0
- package/dist/index.js.map +1 -0
- package/dist/server-84Rz4g8W.d.cts +165 -0
- package/dist/server-D9GPmB49.d.ts +165 -0
- package/dist/server.cjs +972 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +2 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +940 -0
- package/dist/server.js.map +1 -0
- package/dist/testing.cjs +1754 -0
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.d.cts +113 -0
- package/dist/testing.d.ts +113 -0
- package/dist/testing.js +1719 -0
- package/dist/testing.js.map +1 -0
- package/package.json +113 -0
- package/templates/basic/README.md +39 -0
- package/templates/basic/_gitignore +15 -0
- package/templates/basic/_package.json +21 -0
- package/templates/basic/public/styles.css +40 -0
- package/templates/basic/src/app.ts +62 -0
- package/templates/basic/src/main.ts +39 -0
- package/templates/basic/src/server.ts +40 -0
- package/templates/basic/streetui.config.ts +6 -0
- package/templates/basic/tsconfig.json +16 -0
- package/templates/ssr/README.md +46 -0
- package/templates/ssr/_gitignore +15 -0
- package/templates/ssr/_package.json +21 -0
- package/templates/ssr/public/favicon.svg +4 -0
- package/templates/ssr/public/styles.css +61 -0
- package/templates/ssr/src/app.ts +135 -0
- package/templates/ssr/src/main.ts +53 -0
- package/templates/ssr/src/server.ts +47 -0
- package/templates/ssr/streetui.config.ts +14 -0
- package/templates/ssr/tsconfig.json +16 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,4284 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
|
+
AppBuilder: () => AppBuilder,
|
|
24
|
+
Application: () => Application,
|
|
25
|
+
ApplicationGraph: () => ApplicationGraph,
|
|
26
|
+
BaseNode: () => BaseNode,
|
|
27
|
+
BrowserDOMAdapter: () => BrowserDOMAdapter,
|
|
28
|
+
CleanupRegistry: () => CleanupRegistry,
|
|
29
|
+
ContainerBuilderImpl: () => ContainerBuilderImpl,
|
|
30
|
+
DEFAULT_PERF_THRESHOLDS: () => DEFAULT_PERF_THRESHOLDS,
|
|
31
|
+
DerivedSignal: () => DerivedSignal,
|
|
32
|
+
DiagnosticCollector: () => DiagnosticCollector,
|
|
33
|
+
DiagnosticError: () => DiagnosticError,
|
|
34
|
+
DomEventRegistry: () => DomEventRegistry,
|
|
35
|
+
Environment: () => Environment,
|
|
36
|
+
EventBus: () => EventBus,
|
|
37
|
+
FOCUSABLE_SELECTOR: () => FOCUSABLE_SELECTOR,
|
|
38
|
+
FormBuilderImpl: () => FormBuilderImpl,
|
|
39
|
+
GraphNode: () => GraphNode,
|
|
40
|
+
Lifecycle: () => Lifecycle,
|
|
41
|
+
ListBuilderImpl: () => ListBuilderImpl,
|
|
42
|
+
NodeInstance: () => NodeInstance,
|
|
43
|
+
PageBuilderImpl: () => PageBuilderImpl,
|
|
44
|
+
ROUTER_OUTLET_ID: () => ROUTER_OUTLET_ID,
|
|
45
|
+
Runtime: () => Runtime,
|
|
46
|
+
RuntimeNodeInstance: () => RuntimeNodeInstance,
|
|
47
|
+
STATE_MARKER_ATTR: () => STATE_MARKER_ATTR,
|
|
48
|
+
Scheduler: () => Scheduler,
|
|
49
|
+
SectionBuilderImpl: () => SectionBuilderImpl,
|
|
50
|
+
ServerComment: () => ServerComment,
|
|
51
|
+
ServerDOMAdapter: () => ServerDOMAdapter,
|
|
52
|
+
ServerElement: () => ServerElement,
|
|
53
|
+
ServerFragment: () => ServerFragment,
|
|
54
|
+
ServerStyle: () => ServerStyle,
|
|
55
|
+
ServerText: () => ServerText,
|
|
56
|
+
Signal: () => Signal,
|
|
57
|
+
Store: () => Store,
|
|
58
|
+
StreetApp: () => StreetApp,
|
|
59
|
+
StreetFrameworkError: () => StreetFrameworkError,
|
|
60
|
+
StreetRenderHandle: () => StreetRenderHandle,
|
|
61
|
+
StreetRendererImpl: () => StreetRendererImpl,
|
|
62
|
+
VERSION: () => VERSION,
|
|
63
|
+
a11yIds: () => a11yIds,
|
|
64
|
+
applyNodeProps: () => applyNodeProps,
|
|
65
|
+
applyProp: () => applyProp,
|
|
66
|
+
batch: () => batch,
|
|
67
|
+
bindDomEvent: () => bindDomEvent,
|
|
68
|
+
browserDOMAdapter: () => browserDOMAdapter,
|
|
69
|
+
buttonUpdate: () => buttonUpdate,
|
|
70
|
+
compile: () => compile,
|
|
71
|
+
compileGraph: () => compileGraph,
|
|
72
|
+
consoleDiagnosticSink: () => consoleDiagnosticSink,
|
|
73
|
+
consoleHydrationDiagnosticSink: () => consoleHydrationDiagnosticSink,
|
|
74
|
+
createApplication: () => createApplication,
|
|
75
|
+
createBrowserHistory: () => createBrowserHistory,
|
|
76
|
+
createContext: () => createContext,
|
|
77
|
+
createDevTools: () => createDevTools,
|
|
78
|
+
createForm: () => createForm,
|
|
79
|
+
createHydrationDiagnosticCollector: () => createHydrationDiagnosticCollector,
|
|
80
|
+
createI18n: () => createI18n,
|
|
81
|
+
createMemoryHistory: () => createMemoryHistory,
|
|
82
|
+
createNodeId: () => createNodeId,
|
|
83
|
+
createRenderContext: () => createRenderContext,
|
|
84
|
+
createRenderer: () => createRenderer,
|
|
85
|
+
createRouter: () => createRouter,
|
|
86
|
+
createRuntime: () => createRuntime,
|
|
87
|
+
createStore: () => createStore,
|
|
88
|
+
createStreetEvent: () => createStreetEvent,
|
|
89
|
+
defineConfig: () => defineConfig,
|
|
90
|
+
derived: () => derived,
|
|
91
|
+
diagnosePerformance: () => diagnosePerformance,
|
|
92
|
+
effect: () => effect,
|
|
93
|
+
email: () => email,
|
|
94
|
+
environment: () => environment,
|
|
95
|
+
escapeHtmlAttr: () => escapeHtmlAttr,
|
|
96
|
+
escapeHtmlText: () => escapeHtmlText,
|
|
97
|
+
flushSync: () => flushSync,
|
|
98
|
+
focusById: () => focusById,
|
|
99
|
+
focusFirst: () => focusFirst,
|
|
100
|
+
formatDiagnostic: () => formatDiagnostic,
|
|
101
|
+
formatDiagnosticContext: () => formatDiagnosticContext,
|
|
102
|
+
formatHydrationDiagnostic: () => formatHydrationDiagnostic,
|
|
103
|
+
frameworkError: () => frameworkError,
|
|
104
|
+
generateApplicationId: () => generateApplicationId,
|
|
105
|
+
generateNodeId: () => generateNodeId,
|
|
106
|
+
globalEventBus: () => globalEventBus,
|
|
107
|
+
headingUpdate: () => headingUpdate,
|
|
108
|
+
hydrateGraph: () => hydrateGraph,
|
|
109
|
+
inputUpdate: () => inputUpdate,
|
|
110
|
+
inspectApplication: () => inspectApplication,
|
|
111
|
+
inspectContext: () => inspectContext,
|
|
112
|
+
inspectForm: () => inspectForm,
|
|
113
|
+
inspectGraph: () => inspectGraph,
|
|
114
|
+
inspectI18n: () => inspectI18n,
|
|
115
|
+
inspectResource: () => inspectResource,
|
|
116
|
+
inspectRouter: () => inspectRouter,
|
|
117
|
+
inspectSignal: () => inspectSignal,
|
|
118
|
+
interpolate: () => interpolate,
|
|
119
|
+
isBatching: () => isBatching,
|
|
120
|
+
matchPattern: () => matchPattern,
|
|
121
|
+
matchRoutes: () => matchRoutes,
|
|
122
|
+
maxLength: () => maxLength,
|
|
123
|
+
minLength: () => minLength,
|
|
124
|
+
mountGraph: () => mountGraph,
|
|
125
|
+
mountNode: () => mountNode,
|
|
126
|
+
mountRouter: () => mountRouter,
|
|
127
|
+
nextId: () => nextId,
|
|
128
|
+
nodeIdPrefix: () => nodeIdPrefix,
|
|
129
|
+
nodeTypeStats: () => nodeTypeStats,
|
|
130
|
+
normalizePath: () => normalizePath,
|
|
131
|
+
observerCount: () => observerCount,
|
|
132
|
+
patchNode: () => patchNode,
|
|
133
|
+
patchProp: () => patchProp,
|
|
134
|
+
pattern: () => pattern,
|
|
135
|
+
printDiagnostics: () => printDiagnostics,
|
|
136
|
+
printGraph: () => printGraph,
|
|
137
|
+
reactiveListItemKey: () => reactiveListItemKey,
|
|
138
|
+
reactiveListItemSignature: () => reactiveListItemSignature,
|
|
139
|
+
readState: () => readState,
|
|
140
|
+
reconcileChildren: () => reconcileChildren,
|
|
141
|
+
renderToString: () => renderToString,
|
|
142
|
+
reportDiagnostic: () => reportDiagnostic,
|
|
143
|
+
required: () => required,
|
|
144
|
+
resetIdCounter: () => resetIdCounter,
|
|
145
|
+
resolveTag: () => resolveTag,
|
|
146
|
+
resource: () => resource,
|
|
147
|
+
routerOutlet: () => routerOutlet,
|
|
148
|
+
runValidators: () => runValidators,
|
|
149
|
+
scheduleImmediate: () => scheduleImmediate,
|
|
150
|
+
scheduleUpdate: () => scheduleUpdate,
|
|
151
|
+
scheduler: () => scheduler,
|
|
152
|
+
serializeChildren: () => serializeChildren,
|
|
153
|
+
serializeServerNode: () => serializeServerNode,
|
|
154
|
+
serializeState: () => serializeState,
|
|
155
|
+
serverDOMAdapter: () => serverDOMAdapter,
|
|
156
|
+
signal: () => signal,
|
|
157
|
+
signalKind: () => signalKind,
|
|
158
|
+
splitTarget: () => splitTarget,
|
|
159
|
+
streetui: () => streetui,
|
|
160
|
+
textUpdate: () => textUpdate,
|
|
161
|
+
toIdToken: () => toIdToken,
|
|
162
|
+
transformGraph: () => transformGraph,
|
|
163
|
+
validateGraph: () => validateGraph,
|
|
164
|
+
wireEvents: () => wireEvents,
|
|
165
|
+
wireReactiveList: () => wireReactiveList,
|
|
166
|
+
wireSignalBindings: () => wireSignalBindings
|
|
167
|
+
});
|
|
168
|
+
module.exports = __toCommonJS(src_exports);
|
|
169
|
+
|
|
170
|
+
// src/version.ts
|
|
171
|
+
var VERSION = "1.0.0";
|
|
172
|
+
|
|
173
|
+
// ../state/src/signal.ts
|
|
174
|
+
var _activeConsumer = null;
|
|
175
|
+
function withConsumer(consumer, fn) {
|
|
176
|
+
const prev = _activeConsumer;
|
|
177
|
+
_activeConsumer = consumer;
|
|
178
|
+
try {
|
|
179
|
+
return fn();
|
|
180
|
+
} finally {
|
|
181
|
+
_activeConsumer = prev;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
var _batchDepth = 0;
|
|
185
|
+
var _pendingFlushes = /* @__PURE__ */ new Map();
|
|
186
|
+
function _enqueueBatchFlush(sig, value) {
|
|
187
|
+
_pendingFlushes.set(sig, { signal: sig, value });
|
|
188
|
+
}
|
|
189
|
+
function _drainBatch() {
|
|
190
|
+
const flushes = [..._pendingFlushes.values()];
|
|
191
|
+
_pendingFlushes.clear();
|
|
192
|
+
for (const { signal: sig, value } of flushes) {
|
|
193
|
+
sig._flushBatch(value);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
var Signal = class {
|
|
197
|
+
_value;
|
|
198
|
+
_subscribers = /* @__PURE__ */ new Set();
|
|
199
|
+
_consumers = /* @__PURE__ */ new Set();
|
|
200
|
+
constructor(initial) {
|
|
201
|
+
this._value = initial;
|
|
202
|
+
}
|
|
203
|
+
get() {
|
|
204
|
+
if (_activeConsumer !== null) {
|
|
205
|
+
this._consumers.add(_activeConsumer);
|
|
206
|
+
_activeConsumer._addSource(this);
|
|
207
|
+
}
|
|
208
|
+
return this._value;
|
|
209
|
+
}
|
|
210
|
+
peek() {
|
|
211
|
+
return this._value;
|
|
212
|
+
}
|
|
213
|
+
set(value) {
|
|
214
|
+
if (Object.is(this._value, value)) return;
|
|
215
|
+
this._value = value;
|
|
216
|
+
if (_batchDepth > 0) {
|
|
217
|
+
_enqueueBatchFlush(this, value);
|
|
218
|
+
} else {
|
|
219
|
+
this._flush(value);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
update(fn) {
|
|
223
|
+
this.set(fn(this._value));
|
|
224
|
+
}
|
|
225
|
+
subscribe(fn) {
|
|
226
|
+
this._subscribers.add(fn);
|
|
227
|
+
return () => {
|
|
228
|
+
this._subscribers.delete(fn);
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
_removeConsumer(consumer) {
|
|
232
|
+
this._consumers.delete(consumer);
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Called by the batch machinery after the batch has completed.
|
|
236
|
+
* Notifies subscribers with the final coalesced value.
|
|
237
|
+
*/
|
|
238
|
+
_flushBatch(value) {
|
|
239
|
+
this._flush(value);
|
|
240
|
+
}
|
|
241
|
+
_flush(value) {
|
|
242
|
+
for (const sub of [...this._subscribers]) sub(value);
|
|
243
|
+
for (const consumer of [...this._consumers]) consumer._invalidate();
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* @internal DevTools inspection only. The number of live observers
|
|
247
|
+
* (direct subscribers plus derived/effect consumers). Read-only; never
|
|
248
|
+
* mutates reactive state.
|
|
249
|
+
*/
|
|
250
|
+
_observerCount() {
|
|
251
|
+
return this._subscribers.size + this._consumers.size;
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
var DerivedSignal = class {
|
|
255
|
+
_value = void 0;
|
|
256
|
+
_dirty = true;
|
|
257
|
+
_disposed = false;
|
|
258
|
+
_fn;
|
|
259
|
+
_subscribers = /* @__PURE__ */ new Set();
|
|
260
|
+
/** All upstream sources this derived currently reads from. */
|
|
261
|
+
_sources = /* @__PURE__ */ new Set();
|
|
262
|
+
/** Downstream consumers that depend on this derived. */
|
|
263
|
+
_consumers = /* @__PURE__ */ new Set();
|
|
264
|
+
constructor(fn) {
|
|
265
|
+
this._fn = fn;
|
|
266
|
+
}
|
|
267
|
+
get() {
|
|
268
|
+
if (_activeConsumer !== null) {
|
|
269
|
+
this._consumers.add(_activeConsumer);
|
|
270
|
+
_activeConsumer._addSource(this);
|
|
271
|
+
}
|
|
272
|
+
if (this._dirty) this._recompute();
|
|
273
|
+
return this._value;
|
|
274
|
+
}
|
|
275
|
+
peek() {
|
|
276
|
+
if (this._dirty) this._recompute();
|
|
277
|
+
return this._value;
|
|
278
|
+
}
|
|
279
|
+
subscribe(fn) {
|
|
280
|
+
if (this._dirty) this._recompute();
|
|
281
|
+
this._subscribers.add(fn);
|
|
282
|
+
return () => {
|
|
283
|
+
this._subscribers.delete(fn);
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
_addSource(src) {
|
|
287
|
+
this._sources.add(src);
|
|
288
|
+
}
|
|
289
|
+
_removeConsumer(consumer) {
|
|
290
|
+
this._consumers.delete(consumer);
|
|
291
|
+
}
|
|
292
|
+
_invalidate() {
|
|
293
|
+
if (this._disposed) return;
|
|
294
|
+
this._dirty = true;
|
|
295
|
+
const newVal = this.peek();
|
|
296
|
+
for (const sub of [...this._subscribers]) sub(newVal);
|
|
297
|
+
for (const consumer of [...this._consumers]) consumer._invalidate();
|
|
298
|
+
}
|
|
299
|
+
_recompute() {
|
|
300
|
+
for (const src of this._sources) src._removeConsumer(this);
|
|
301
|
+
this._sources.clear();
|
|
302
|
+
this._value = withConsumer(this, this._fn);
|
|
303
|
+
this._dirty = false;
|
|
304
|
+
}
|
|
305
|
+
dispose() {
|
|
306
|
+
this._disposed = true;
|
|
307
|
+
for (const src of this._sources) src._removeConsumer(this);
|
|
308
|
+
this._sources.clear();
|
|
309
|
+
this._subscribers.clear();
|
|
310
|
+
this._consumers.clear();
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* @internal DevTools inspection only. Live observers (subscribers plus
|
|
314
|
+
* downstream consumers). Read-only.
|
|
315
|
+
*/
|
|
316
|
+
_observerCount() {
|
|
317
|
+
return this._subscribers.size + this._consumers.size;
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
var Effect = class {
|
|
321
|
+
_fn;
|
|
322
|
+
_cleanup = void 0;
|
|
323
|
+
_disposed = false;
|
|
324
|
+
_sources = /* @__PURE__ */ new Set();
|
|
325
|
+
constructor(fn) {
|
|
326
|
+
this._fn = fn;
|
|
327
|
+
this._run();
|
|
328
|
+
}
|
|
329
|
+
_addSource(src) {
|
|
330
|
+
this._sources.add(src);
|
|
331
|
+
}
|
|
332
|
+
_invalidate() {
|
|
333
|
+
if (this._disposed) return;
|
|
334
|
+
this._run();
|
|
335
|
+
}
|
|
336
|
+
_run() {
|
|
337
|
+
for (const src of this._sources) src._removeConsumer(this);
|
|
338
|
+
this._sources.clear();
|
|
339
|
+
if (typeof this._cleanup === "function") this._cleanup();
|
|
340
|
+
const result = withConsumer(this, this._fn);
|
|
341
|
+
this._cleanup = typeof result === "function" ? result : void 0;
|
|
342
|
+
}
|
|
343
|
+
dispose() {
|
|
344
|
+
this._disposed = true;
|
|
345
|
+
for (const src of this._sources) src._removeConsumer(this);
|
|
346
|
+
this._sources.clear();
|
|
347
|
+
if (typeof this._cleanup === "function") this._cleanup();
|
|
348
|
+
this._cleanup = void 0;
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
function signal(initial) {
|
|
352
|
+
return new Signal(initial);
|
|
353
|
+
}
|
|
354
|
+
function derived(fn) {
|
|
355
|
+
return new DerivedSignal(fn);
|
|
356
|
+
}
|
|
357
|
+
function effect(fn) {
|
|
358
|
+
const e = new Effect(fn);
|
|
359
|
+
return () => e.dispose();
|
|
360
|
+
}
|
|
361
|
+
function batch(fn) {
|
|
362
|
+
_batchDepth++;
|
|
363
|
+
try {
|
|
364
|
+
fn();
|
|
365
|
+
} finally {
|
|
366
|
+
_batchDepth--;
|
|
367
|
+
if (_batchDepth === 0) {
|
|
368
|
+
_drainBatch();
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
function isBatching() {
|
|
373
|
+
return _batchDepth > 0;
|
|
374
|
+
}
|
|
375
|
+
function signalKind(source) {
|
|
376
|
+
return source instanceof DerivedSignal ? "derived" : "writable";
|
|
377
|
+
}
|
|
378
|
+
function observerCount(source) {
|
|
379
|
+
const maybe = source;
|
|
380
|
+
return typeof maybe._observerCount === "function" ? maybe._observerCount() : void 0;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// ../state/src/store.ts
|
|
384
|
+
var Store = class {
|
|
385
|
+
_signals;
|
|
386
|
+
constructor(initial) {
|
|
387
|
+
const signals = {};
|
|
388
|
+
for (const key in initial) {
|
|
389
|
+
if (Object.prototype.hasOwnProperty.call(initial, key)) {
|
|
390
|
+
signals[key] = signal(initial[key]);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
this._signals = signals;
|
|
394
|
+
}
|
|
395
|
+
get(key) {
|
|
396
|
+
const s = this._signals[key];
|
|
397
|
+
if (s === void 0) {
|
|
398
|
+
throw new Error(`Store: unknown key "${String(key)}"`);
|
|
399
|
+
}
|
|
400
|
+
return s.get();
|
|
401
|
+
}
|
|
402
|
+
set(key, value) {
|
|
403
|
+
const s = this._signals[key];
|
|
404
|
+
if (s === void 0) {
|
|
405
|
+
throw new Error(`Store: unknown key "${String(key)}"`);
|
|
406
|
+
}
|
|
407
|
+
s.set(value);
|
|
408
|
+
}
|
|
409
|
+
signal(key) {
|
|
410
|
+
const s = this._signals[key];
|
|
411
|
+
if (s === void 0) {
|
|
412
|
+
throw new Error(`Store: unknown key "${String(key)}"`);
|
|
413
|
+
}
|
|
414
|
+
return s;
|
|
415
|
+
}
|
|
416
|
+
subscribe(key, fn) {
|
|
417
|
+
return this.signal(key).subscribe(fn);
|
|
418
|
+
}
|
|
419
|
+
getSnapshot() {
|
|
420
|
+
const snap = {};
|
|
421
|
+
for (const key in this._signals) {
|
|
422
|
+
if (Object.prototype.hasOwnProperty.call(this._signals, key)) {
|
|
423
|
+
snap[key] = this._signals[key]?.peek();
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
return snap;
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
function createStore(initial) {
|
|
430
|
+
return new Store(initial);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// ../state/src/resource.ts
|
|
434
|
+
function isAbortError(err) {
|
|
435
|
+
return err instanceof Error && err.name === "AbortError" || typeof DOMException !== "undefined" && err instanceof DOMException && err.name === "AbortError";
|
|
436
|
+
}
|
|
437
|
+
function resource(loader, options = {}) {
|
|
438
|
+
const hasInitial = options.initialData !== void 0 || options.initialError !== void 0 || options.initialStatus !== void 0;
|
|
439
|
+
const seededStatus = options.initialStatus ?? (options.initialData !== void 0 ? "success" : options.initialError !== void 0 ? "error" : "idle");
|
|
440
|
+
const status = signal(seededStatus);
|
|
441
|
+
const data = signal(options.initialData);
|
|
442
|
+
const error = signal(options.initialError);
|
|
443
|
+
const loading = derived(() => status.get() === "loading");
|
|
444
|
+
const isRefetching = derived(() => status.get() === "loading" && data.get() !== void 0);
|
|
445
|
+
let disposed = false;
|
|
446
|
+
let runId = 0;
|
|
447
|
+
let controller = null;
|
|
448
|
+
const load = async () => {
|
|
449
|
+
if (disposed) return;
|
|
450
|
+
controller?.abort();
|
|
451
|
+
const myRun = ++runId;
|
|
452
|
+
const myController = new AbortController();
|
|
453
|
+
controller = myController;
|
|
454
|
+
batch(() => {
|
|
455
|
+
error.set(void 0);
|
|
456
|
+
status.set("loading");
|
|
457
|
+
});
|
|
458
|
+
try {
|
|
459
|
+
const result = await loader({ signal: myController.signal });
|
|
460
|
+
if (disposed || myRun !== runId) return;
|
|
461
|
+
batch(() => {
|
|
462
|
+
data.set(result);
|
|
463
|
+
error.set(void 0);
|
|
464
|
+
status.set("success");
|
|
465
|
+
});
|
|
466
|
+
} catch (err) {
|
|
467
|
+
if (disposed || myRun !== runId) return;
|
|
468
|
+
if (isAbortError(err)) return;
|
|
469
|
+
batch(() => {
|
|
470
|
+
error.set(err);
|
|
471
|
+
status.set("error");
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
};
|
|
475
|
+
const refetch = () => load();
|
|
476
|
+
const watchUnsubs = [];
|
|
477
|
+
if (options.watch !== void 0) {
|
|
478
|
+
for (const dep of options.watch) {
|
|
479
|
+
watchUnsubs.push(
|
|
480
|
+
dep.subscribe(() => {
|
|
481
|
+
if (!disposed) void load();
|
|
482
|
+
})
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
const dispose = () => {
|
|
487
|
+
if (disposed) return;
|
|
488
|
+
disposed = true;
|
|
489
|
+
controller?.abort();
|
|
490
|
+
controller = null;
|
|
491
|
+
for (const unsub of watchUnsubs) unsub();
|
|
492
|
+
watchUnsubs.length = 0;
|
|
493
|
+
};
|
|
494
|
+
if (options.onCleanup !== void 0) {
|
|
495
|
+
options.onCleanup(dispose);
|
|
496
|
+
}
|
|
497
|
+
if (options.immediate === true || options.immediate !== false && !hasInitial) {
|
|
498
|
+
void load();
|
|
499
|
+
}
|
|
500
|
+
return {
|
|
501
|
+
status,
|
|
502
|
+
data,
|
|
503
|
+
error,
|
|
504
|
+
loading,
|
|
505
|
+
isRefetching,
|
|
506
|
+
refetch,
|
|
507
|
+
dispose
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// ../core/src/a11y-ids.ts
|
|
512
|
+
var UNSAFE = /[^A-Za-z0-9_-]+/g;
|
|
513
|
+
function toIdToken(base) {
|
|
514
|
+
const token = base.trim().replace(UNSAFE, "-").replace(/^-+|-+$/g, "");
|
|
515
|
+
return token.length > 0 ? token : "field";
|
|
516
|
+
}
|
|
517
|
+
function a11yIds(base) {
|
|
518
|
+
const token = toIdToken(base);
|
|
519
|
+
return {
|
|
520
|
+
base: token,
|
|
521
|
+
input: `${token}-input`,
|
|
522
|
+
label: `${token}-label`,
|
|
523
|
+
description: `${token}-description`,
|
|
524
|
+
error: `${token}-error`,
|
|
525
|
+
title: `${token}-title`,
|
|
526
|
+
id: (suffix) => `${token}-${toIdToken(suffix)}`
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// ../core/src/identity.ts
|
|
531
|
+
var _counter = 0;
|
|
532
|
+
function nextId() {
|
|
533
|
+
return ++_counter;
|
|
534
|
+
}
|
|
535
|
+
function resetIdCounter() {
|
|
536
|
+
_counter = 0;
|
|
537
|
+
}
|
|
538
|
+
function createNodeId(value) {
|
|
539
|
+
return value;
|
|
540
|
+
}
|
|
541
|
+
function generateNodeId(prefix = "node") {
|
|
542
|
+
return createNodeId(`${prefix}:${nextId()}`);
|
|
543
|
+
}
|
|
544
|
+
function nodeIdPrefix(id) {
|
|
545
|
+
const colon = id.indexOf(":");
|
|
546
|
+
return colon === -1 ? id : id.slice(0, colon);
|
|
547
|
+
}
|
|
548
|
+
function generateApplicationId(name) {
|
|
549
|
+
return `app:${name}:${nextId()}`;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// ../core/src/lifecycle.ts
|
|
553
|
+
var Lifecycle = class {
|
|
554
|
+
_phase = "created";
|
|
555
|
+
_hooks = /* @__PURE__ */ new Map();
|
|
556
|
+
get phase() {
|
|
557
|
+
return this._phase;
|
|
558
|
+
}
|
|
559
|
+
get isMounted() {
|
|
560
|
+
return this._phase === "mounted" || this._phase === "active" || this._phase === "updating";
|
|
561
|
+
}
|
|
562
|
+
get isDestroyed() {
|
|
563
|
+
return this._phase === "destroyed";
|
|
564
|
+
}
|
|
565
|
+
on(phase, hook) {
|
|
566
|
+
const hooks = this._hooks.get(phase) ?? [];
|
|
567
|
+
hooks.push(hook);
|
|
568
|
+
this._hooks.set(phase, hooks);
|
|
569
|
+
return () => {
|
|
570
|
+
const current = this._hooks.get(phase);
|
|
571
|
+
if (current !== void 0) {
|
|
572
|
+
const idx = current.indexOf(hook);
|
|
573
|
+
if (idx !== -1) current.splice(idx, 1);
|
|
574
|
+
}
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
async transition(to) {
|
|
578
|
+
this._phase = to;
|
|
579
|
+
const hooks = this._hooks.get(to) ?? [];
|
|
580
|
+
for (const hook of hooks) {
|
|
581
|
+
await hook();
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
onMount(hook) {
|
|
585
|
+
return this.on("mounted", hook);
|
|
586
|
+
}
|
|
587
|
+
onUnmount(hook) {
|
|
588
|
+
return this.on("unmounting", hook);
|
|
589
|
+
}
|
|
590
|
+
onDestroy(hook) {
|
|
591
|
+
return this.on("destroyed", hook);
|
|
592
|
+
}
|
|
593
|
+
};
|
|
594
|
+
var CleanupRegistry = class {
|
|
595
|
+
_fns = [];
|
|
596
|
+
add(fn) {
|
|
597
|
+
this._fns.push(fn);
|
|
598
|
+
}
|
|
599
|
+
run() {
|
|
600
|
+
for (const fn of this._fns) {
|
|
601
|
+
try {
|
|
602
|
+
fn();
|
|
603
|
+
} catch {
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
this._fns.length = 0;
|
|
607
|
+
}
|
|
608
|
+
};
|
|
609
|
+
|
|
610
|
+
// ../core/src/environment.ts
|
|
611
|
+
function detectEnvironment() {
|
|
612
|
+
try {
|
|
613
|
+
if (typeof process !== "undefined" && process !== null && typeof process === "object" && (process.env?.["NODE_ENV"] === "test" || process.env?.["VITEST"] === "true")) {
|
|
614
|
+
return "test";
|
|
615
|
+
}
|
|
616
|
+
} catch {
|
|
617
|
+
}
|
|
618
|
+
if (typeof window !== "undefined" && typeof document !== "undefined") {
|
|
619
|
+
return "browser";
|
|
620
|
+
}
|
|
621
|
+
if (typeof self !== "undefined" && typeof self["importScripts"] === "function") {
|
|
622
|
+
return "worker";
|
|
623
|
+
}
|
|
624
|
+
try {
|
|
625
|
+
if (typeof process !== "undefined" && typeof process === "object") {
|
|
626
|
+
return "server";
|
|
627
|
+
}
|
|
628
|
+
} catch {
|
|
629
|
+
}
|
|
630
|
+
return "unknown";
|
|
631
|
+
}
|
|
632
|
+
function detectCapabilities() {
|
|
633
|
+
return {
|
|
634
|
+
hasDom: typeof document !== "undefined",
|
|
635
|
+
hasWindow: typeof window !== "undefined",
|
|
636
|
+
hasDocument: typeof document !== "undefined",
|
|
637
|
+
isSecureContext: typeof window !== "undefined" ? window["isSecureContext"] === true : false
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
var Environment = class {
|
|
641
|
+
kind;
|
|
642
|
+
capabilities;
|
|
643
|
+
constructor(kind) {
|
|
644
|
+
this.kind = kind ?? detectEnvironment();
|
|
645
|
+
this.capabilities = detectCapabilities();
|
|
646
|
+
}
|
|
647
|
+
get isBrowser() {
|
|
648
|
+
return this.kind === "browser";
|
|
649
|
+
}
|
|
650
|
+
get isServer() {
|
|
651
|
+
return this.kind === "server";
|
|
652
|
+
}
|
|
653
|
+
get isTest() {
|
|
654
|
+
return this.kind === "test";
|
|
655
|
+
}
|
|
656
|
+
get isWorker() {
|
|
657
|
+
return this.kind === "worker";
|
|
658
|
+
}
|
|
659
|
+
};
|
|
660
|
+
var environment = new Environment();
|
|
661
|
+
|
|
662
|
+
// ../core/src/diagnostics.ts
|
|
663
|
+
var DiagnosticError = class extends Error {
|
|
664
|
+
diagnostics;
|
|
665
|
+
constructor(diagnostics) {
|
|
666
|
+
const summary = diagnostics.filter((d) => d.severity === "error").map((d) => `[${d.code}] ${d.message}`).join("\n");
|
|
667
|
+
super(`StreetUI diagnostics:
|
|
668
|
+
${summary}`);
|
|
669
|
+
this.name = "DiagnosticError";
|
|
670
|
+
this.diagnostics = diagnostics;
|
|
671
|
+
}
|
|
672
|
+
};
|
|
673
|
+
var DiagnosticCollector = class {
|
|
674
|
+
_diagnostics = [];
|
|
675
|
+
get diagnostics() {
|
|
676
|
+
return this._diagnostics;
|
|
677
|
+
}
|
|
678
|
+
get hasErrors() {
|
|
679
|
+
return this._diagnostics.some((d) => d.severity === "error");
|
|
680
|
+
}
|
|
681
|
+
get hasWarnings() {
|
|
682
|
+
return this._diagnostics.some((d) => d.severity === "warning");
|
|
683
|
+
}
|
|
684
|
+
error(code, message, location, cause) {
|
|
685
|
+
this._diagnostics.push({ severity: "error", code, message, location: location ?? void 0, cause: cause ?? void 0 });
|
|
686
|
+
}
|
|
687
|
+
warn(code, message, location) {
|
|
688
|
+
this._diagnostics.push({ severity: "warning", code, message, location: location ?? void 0, cause: void 0 });
|
|
689
|
+
}
|
|
690
|
+
info(code, message, location) {
|
|
691
|
+
this._diagnostics.push({ severity: "info", code, message, location: location ?? void 0, cause: void 0 });
|
|
692
|
+
}
|
|
693
|
+
merge(other) {
|
|
694
|
+
for (const d of other.diagnostics) {
|
|
695
|
+
this._diagnostics.push(d);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
throwIfErrors() {
|
|
699
|
+
if (this.hasErrors) {
|
|
700
|
+
throw new DiagnosticError(this._diagnostics);
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
clear() {
|
|
704
|
+
this._diagnostics.length = 0;
|
|
705
|
+
}
|
|
706
|
+
};
|
|
707
|
+
function formatDiagnostic(d) {
|
|
708
|
+
const loc = d.location !== void 0 ? ` (${[d.location.file, d.location.line, d.location.column].filter(Boolean).join(":")})` : "";
|
|
709
|
+
return `[${d.severity.toUpperCase()}] ${d.code}: ${d.message}${loc}`;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
// ../core/src/application.ts
|
|
713
|
+
var Application = class {
|
|
714
|
+
id;
|
|
715
|
+
name;
|
|
716
|
+
version;
|
|
717
|
+
lifecycle;
|
|
718
|
+
cleanup;
|
|
719
|
+
diagnostics;
|
|
720
|
+
environment;
|
|
721
|
+
constructor(options) {
|
|
722
|
+
this.name = options.name;
|
|
723
|
+
this.version = options.version ?? "0.0.1";
|
|
724
|
+
this.id = generateApplicationId(options.name);
|
|
725
|
+
this.lifecycle = new Lifecycle();
|
|
726
|
+
this.cleanup = new CleanupRegistry();
|
|
727
|
+
this.diagnostics = new DiagnosticCollector();
|
|
728
|
+
this.environment = options.environment ?? environment;
|
|
729
|
+
}
|
|
730
|
+
async mount() {
|
|
731
|
+
if (this.lifecycle.phase !== "created") {
|
|
732
|
+
throw new Error(`Application "${this.name}" is already mounted (phase: ${this.lifecycle.phase})`);
|
|
733
|
+
}
|
|
734
|
+
await this.lifecycle.transition("mounted");
|
|
735
|
+
await this.lifecycle.transition("active");
|
|
736
|
+
}
|
|
737
|
+
async unmount() {
|
|
738
|
+
if (!this.lifecycle.isMounted) {
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
await this.lifecycle.transition("unmounting");
|
|
742
|
+
this.cleanup.run();
|
|
743
|
+
await this.lifecycle.transition("destroyed");
|
|
744
|
+
}
|
|
745
|
+
onMount(fn) {
|
|
746
|
+
this.lifecycle.onMount(fn);
|
|
747
|
+
}
|
|
748
|
+
onUnmount(fn) {
|
|
749
|
+
this.lifecycle.onUnmount(fn);
|
|
750
|
+
}
|
|
751
|
+
};
|
|
752
|
+
function createApplication(options) {
|
|
753
|
+
return new Application(options);
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
// ../core/src/node.ts
|
|
757
|
+
var BaseNode = class {
|
|
758
|
+
id;
|
|
759
|
+
type;
|
|
760
|
+
metadata;
|
|
761
|
+
constructor(type, id) {
|
|
762
|
+
this.type = type;
|
|
763
|
+
this.id = id ?? generateNodeId(type);
|
|
764
|
+
this.metadata = { createdAt: Date.now() };
|
|
765
|
+
}
|
|
766
|
+
};
|
|
767
|
+
|
|
768
|
+
// ../core/src/observability.ts
|
|
769
|
+
function formatDiagnosticContext(context) {
|
|
770
|
+
if (context === void 0) return "";
|
|
771
|
+
const parts = [];
|
|
772
|
+
if (context.package !== void 0) parts.push(`package=${context.package}`);
|
|
773
|
+
if (context.operation !== void 0) parts.push(`operation=${context.operation}`);
|
|
774
|
+
if (context.nodeId !== void 0) parts.push(`node=${context.nodeId}`);
|
|
775
|
+
if (context.route !== void 0) parts.push(`route=${context.route}`);
|
|
776
|
+
if (context.resource !== void 0) parts.push(`resource=${context.resource}`);
|
|
777
|
+
return parts.length > 0 ? ` [${parts.join(", ")}]` : "";
|
|
778
|
+
}
|
|
779
|
+
var StreetFrameworkError = class extends Error {
|
|
780
|
+
context;
|
|
781
|
+
constructor(message, context) {
|
|
782
|
+
super(`${message}${formatDiagnosticContext(context)}`);
|
|
783
|
+
this.name = "StreetFrameworkError";
|
|
784
|
+
this.context = context ?? void 0;
|
|
785
|
+
}
|
|
786
|
+
};
|
|
787
|
+
function frameworkError(message, context) {
|
|
788
|
+
return new StreetFrameworkError(message, context);
|
|
789
|
+
}
|
|
790
|
+
function reportDiagnostic(sink, level, message, context) {
|
|
791
|
+
if (sink === void 0) return;
|
|
792
|
+
const fn = sink[level];
|
|
793
|
+
if (typeof fn !== "function") return;
|
|
794
|
+
try {
|
|
795
|
+
fn.call(sink, message, context);
|
|
796
|
+
} catch {
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
function consoleDiagnosticSink(logger = console) {
|
|
800
|
+
return {
|
|
801
|
+
debug: (m, c) => logger.debug?.(`${m}${formatDiagnosticContext(c)}`),
|
|
802
|
+
info: (m, c) => logger.info?.(`${m}${formatDiagnosticContext(c)}`),
|
|
803
|
+
warn: (m, c) => logger.warn?.(`${m}${formatDiagnosticContext(c)}`),
|
|
804
|
+
error: (m, c) => logger.error?.(`${m}${formatDiagnosticContext(c)}`)
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
// ../graph/src/graph-node.ts
|
|
809
|
+
var GraphNode = class _GraphNode {
|
|
810
|
+
id;
|
|
811
|
+
type;
|
|
812
|
+
key;
|
|
813
|
+
props;
|
|
814
|
+
events;
|
|
815
|
+
stateRefs;
|
|
816
|
+
children;
|
|
817
|
+
parent;
|
|
818
|
+
constructor(type, options = {}) {
|
|
819
|
+
this.type = type;
|
|
820
|
+
this.id = options.id ?? generateNodeId(type);
|
|
821
|
+
this.key = options.key;
|
|
822
|
+
this.props = options.props ?? {};
|
|
823
|
+
this.events = options.events ?? [];
|
|
824
|
+
this.stateRefs = options.stateRefs ?? [];
|
|
825
|
+
this.children = [];
|
|
826
|
+
this.parent = null;
|
|
827
|
+
}
|
|
828
|
+
// ── Child management ────────────────────────────────────────────────────────
|
|
829
|
+
appendChild(child) {
|
|
830
|
+
if (child.parent !== null) {
|
|
831
|
+
child.parent.removeChild(child);
|
|
832
|
+
}
|
|
833
|
+
child.parent = this;
|
|
834
|
+
this.children.push(child);
|
|
835
|
+
}
|
|
836
|
+
insertBefore(child, reference) {
|
|
837
|
+
const idx = this.children.indexOf(reference);
|
|
838
|
+
if (idx === -1) {
|
|
839
|
+
this.appendChild(child);
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
842
|
+
if (child.parent !== null) {
|
|
843
|
+
child.parent.removeChild(child);
|
|
844
|
+
}
|
|
845
|
+
child.parent = this;
|
|
846
|
+
this.children.splice(idx, 0, child);
|
|
847
|
+
}
|
|
848
|
+
removeChild(child) {
|
|
849
|
+
const idx = this.children.indexOf(child);
|
|
850
|
+
if (idx === -1) return;
|
|
851
|
+
this.children.splice(idx, 1);
|
|
852
|
+
child.parent = null;
|
|
853
|
+
}
|
|
854
|
+
replaceChild(newChild, oldChild) {
|
|
855
|
+
const idx = this.children.indexOf(oldChild);
|
|
856
|
+
if (idx === -1) {
|
|
857
|
+
throw new Error(`GraphNode.replaceChild: oldChild is not a child of this node`);
|
|
858
|
+
}
|
|
859
|
+
if (newChild.parent !== null) {
|
|
860
|
+
newChild.parent.removeChild(newChild);
|
|
861
|
+
}
|
|
862
|
+
oldChild.parent = null;
|
|
863
|
+
newChild.parent = this;
|
|
864
|
+
this.children.splice(idx, 1, newChild);
|
|
865
|
+
}
|
|
866
|
+
// ── Prop helpers ────────────────────────────────────────────────────────────
|
|
867
|
+
setProp(key, value) {
|
|
868
|
+
this.props = { ...this.props, [key]: value };
|
|
869
|
+
}
|
|
870
|
+
getProp(key) {
|
|
871
|
+
return this.props[key];
|
|
872
|
+
}
|
|
873
|
+
// ── Event helpers ───────────────────────────────────────────────────────────
|
|
874
|
+
addEvent(descriptor) {
|
|
875
|
+
this.events.push(descriptor);
|
|
876
|
+
}
|
|
877
|
+
removeEvent(type) {
|
|
878
|
+
this.events = this.events.filter((e) => e.type !== type);
|
|
879
|
+
}
|
|
880
|
+
// ── Queries ─────────────────────────────────────────────────────────────────
|
|
881
|
+
get isLeaf() {
|
|
882
|
+
return this.children.length === 0;
|
|
883
|
+
}
|
|
884
|
+
get depth() {
|
|
885
|
+
let d = 0;
|
|
886
|
+
let node = this.parent;
|
|
887
|
+
while (node !== null) {
|
|
888
|
+
d++;
|
|
889
|
+
node = node.parent;
|
|
890
|
+
}
|
|
891
|
+
return d;
|
|
892
|
+
}
|
|
893
|
+
get root() {
|
|
894
|
+
let node = this;
|
|
895
|
+
while (node.parent !== null) {
|
|
896
|
+
node = node.parent;
|
|
897
|
+
}
|
|
898
|
+
return node;
|
|
899
|
+
}
|
|
900
|
+
/** Shallow clone — does not clone children. */
|
|
901
|
+
shallowClone() {
|
|
902
|
+
const opts = {
|
|
903
|
+
props: { ...this.props },
|
|
904
|
+
events: [...this.events],
|
|
905
|
+
stateRefs: [...this.stateRefs]
|
|
906
|
+
};
|
|
907
|
+
if (this.key !== void 0) opts.key = this.key;
|
|
908
|
+
return new _GraphNode(this.type, opts);
|
|
909
|
+
}
|
|
910
|
+
};
|
|
911
|
+
|
|
912
|
+
// ../graph/src/graph.ts
|
|
913
|
+
var ApplicationGraph = class {
|
|
914
|
+
root;
|
|
915
|
+
name;
|
|
916
|
+
version;
|
|
917
|
+
_nodeIndex = /* @__PURE__ */ new Map();
|
|
918
|
+
/** Handler registry — maps handlerKey → actual function */
|
|
919
|
+
handlers = /* @__PURE__ */ new Map();
|
|
920
|
+
constructor(options) {
|
|
921
|
+
this.name = options.name;
|
|
922
|
+
this.version = options.version ?? "0.0.1";
|
|
923
|
+
this.root = new GraphNode("application", { props: { name: options.name } });
|
|
924
|
+
this._nodeIndex.set(this.root.id, this.root);
|
|
925
|
+
}
|
|
926
|
+
// ── Node creation & attachment ────────────────────────────────────────────
|
|
927
|
+
createNode(type, options = {}) {
|
|
928
|
+
const nodeOpts = {};
|
|
929
|
+
if (options.key !== void 0) nodeOpts.key = options.key;
|
|
930
|
+
if (options.props !== void 0) nodeOpts.props = options.props;
|
|
931
|
+
const node = new GraphNode(type, nodeOpts);
|
|
932
|
+
this._nodeIndex.set(node.id, node);
|
|
933
|
+
if (options.parent !== void 0) {
|
|
934
|
+
options.parent.appendChild(node);
|
|
935
|
+
}
|
|
936
|
+
return node;
|
|
937
|
+
}
|
|
938
|
+
attachNode(node, parent) {
|
|
939
|
+
this._nodeIndex.set(node.id, node);
|
|
940
|
+
parent.appendChild(node);
|
|
941
|
+
}
|
|
942
|
+
detachNode(node) {
|
|
943
|
+
if (node.parent !== null) {
|
|
944
|
+
node.parent.removeChild(node);
|
|
945
|
+
}
|
|
946
|
+
this._removeFromIndex(node);
|
|
947
|
+
}
|
|
948
|
+
_removeFromIndex(node) {
|
|
949
|
+
this._nodeIndex.delete(node.id);
|
|
950
|
+
this._unregisterNodeHandlers(node);
|
|
951
|
+
for (const child of node.children) {
|
|
952
|
+
this._removeFromIndex(child);
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
/**
|
|
956
|
+
* Remove every handler-registry entry owned by a single node. A node owns:
|
|
957
|
+
* - one entry per event descriptor (its `handlerKey`),
|
|
958
|
+
* - one `__signal__<signalId>` entry per state ref (signalIds are namespaced
|
|
959
|
+
* by node id, so they are never shared between nodes), and
|
|
960
|
+
* - a `__listbuild__<id>` entry if it is a reactive-list.
|
|
961
|
+
* Called for every node in a detached subtree so removing list items (or
|
|
962
|
+
* discarding freshly-built-but-unadopted item subtrees) leaves no stale
|
|
963
|
+
* registrations behind.
|
|
964
|
+
*/
|
|
965
|
+
_unregisterNodeHandlers(node) {
|
|
966
|
+
for (const event of node.events) {
|
|
967
|
+
this.handlers.delete(event.handlerKey);
|
|
968
|
+
}
|
|
969
|
+
for (const ref of node.stateRefs) {
|
|
970
|
+
this.handlers.delete(`__signal__${ref.signalId}`);
|
|
971
|
+
}
|
|
972
|
+
this.handlers.delete(`__listbuild__${node.id}`);
|
|
973
|
+
}
|
|
974
|
+
// ── Handler registry ──────────────────────────────────────────────────────
|
|
975
|
+
registerHandler(key, fn) {
|
|
976
|
+
this.handlers.set(key, fn);
|
|
977
|
+
}
|
|
978
|
+
getHandler(key) {
|
|
979
|
+
return this.handlers.get(key);
|
|
980
|
+
}
|
|
981
|
+
/** True if a handler is currently registered under `key`. Inspection helper. */
|
|
982
|
+
hasHandler(key) {
|
|
983
|
+
return this.handlers.has(key);
|
|
984
|
+
}
|
|
985
|
+
/** Number of currently-registered handlers. Inspection helper. */
|
|
986
|
+
get handlerCount() {
|
|
987
|
+
return this.handlers.size;
|
|
988
|
+
}
|
|
989
|
+
// ── Lookup ────────────────────────────────────────────────────────────────
|
|
990
|
+
findById(id) {
|
|
991
|
+
return this._nodeIndex.get(id);
|
|
992
|
+
}
|
|
993
|
+
findAll(predicate) {
|
|
994
|
+
const results = [];
|
|
995
|
+
this._walk(this.root, (node) => {
|
|
996
|
+
if (predicate(node)) results.push(node);
|
|
997
|
+
});
|
|
998
|
+
return results;
|
|
999
|
+
}
|
|
1000
|
+
findByType(type) {
|
|
1001
|
+
return this.findAll((n) => n.type === type);
|
|
1002
|
+
}
|
|
1003
|
+
// ── Traversal ─────────────────────────────────────────────────────────────
|
|
1004
|
+
walk(visitor) {
|
|
1005
|
+
this._walk(this.root, visitor, 0);
|
|
1006
|
+
}
|
|
1007
|
+
_walk(node, visitor, depth = 0) {
|
|
1008
|
+
visitor(node, depth);
|
|
1009
|
+
for (const child of node.children) {
|
|
1010
|
+
this._walk(child, visitor, depth + 1);
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
get nodeCount() {
|
|
1014
|
+
return this._nodeIndex.size;
|
|
1015
|
+
}
|
|
1016
|
+
// ── Validation ────────────────────────────────────────────────────────────
|
|
1017
|
+
validate() {
|
|
1018
|
+
const dc = new DiagnosticCollector();
|
|
1019
|
+
this.walk((node) => {
|
|
1020
|
+
for (const event of node.events) {
|
|
1021
|
+
if (!this.handlers.has(event.handlerKey)) {
|
|
1022
|
+
dc.warn(
|
|
1023
|
+
"GRAPH_MISSING_HANDLER",
|
|
1024
|
+
`Node "${node.id}" references handler "${event.handlerKey}" which is not registered`,
|
|
1025
|
+
{ nodeId: node.id }
|
|
1026
|
+
);
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
if (node.type === "page" && node.parent?.type !== "application") {
|
|
1030
|
+
dc.error(
|
|
1031
|
+
"GRAPH_PAGE_DEPTH",
|
|
1032
|
+
`Page node "${node.id}" must be a direct child of the application root`,
|
|
1033
|
+
{ nodeId: node.id }
|
|
1034
|
+
);
|
|
1035
|
+
}
|
|
1036
|
+
});
|
|
1037
|
+
return dc;
|
|
1038
|
+
}
|
|
1039
|
+
// ── Serialization ─────────────────────────────────────────────────────────
|
|
1040
|
+
serialize() {
|
|
1041
|
+
return {
|
|
1042
|
+
name: this.name,
|
|
1043
|
+
version: this.version,
|
|
1044
|
+
root: this._serializeNode(this.root)
|
|
1045
|
+
};
|
|
1046
|
+
}
|
|
1047
|
+
_serializeNode(node) {
|
|
1048
|
+
const result = {
|
|
1049
|
+
id: node.id,
|
|
1050
|
+
type: node.type,
|
|
1051
|
+
key: node.key,
|
|
1052
|
+
props: node.props,
|
|
1053
|
+
events: node.events,
|
|
1054
|
+
stateRefs: node.stateRefs,
|
|
1055
|
+
children: node.children.map((c) => this._serializeNode(c))
|
|
1056
|
+
};
|
|
1057
|
+
return result;
|
|
1058
|
+
}
|
|
1059
|
+
};
|
|
1060
|
+
|
|
1061
|
+
// ../dsl/src/builders.ts
|
|
1062
|
+
function isSignal(v) {
|
|
1063
|
+
return v !== null && typeof v === "object" && typeof v["get"] === "function" && typeof v["subscribe"] === "function";
|
|
1064
|
+
}
|
|
1065
|
+
function bindValue(graph, node, propKey, value) {
|
|
1066
|
+
if (isSignal(value)) {
|
|
1067
|
+
const signalId = `${node.id}:${propKey}`;
|
|
1068
|
+
node.stateRefs.push({ signalId, propKey });
|
|
1069
|
+
graph.registerHandler(`__signal__${signalId}`, value);
|
|
1070
|
+
return value.peek();
|
|
1071
|
+
}
|
|
1072
|
+
return value;
|
|
1073
|
+
}
|
|
1074
|
+
function applyA11yProps(props, options) {
|
|
1075
|
+
if (options.role !== void 0) props["role"] = options.role;
|
|
1076
|
+
if (options.tabIndex !== void 0) props["tabindex"] = String(options.tabIndex);
|
|
1077
|
+
if (options.ariaLabel !== void 0) props["aria-label"] = options.ariaLabel;
|
|
1078
|
+
if (options.ariaLabelledBy !== void 0) props["aria-labelledby"] = options.ariaLabelledBy;
|
|
1079
|
+
if (options.ariaDescribedBy !== void 0) props["aria-describedby"] = options.ariaDescribedBy;
|
|
1080
|
+
if (options.ariaExpanded !== void 0) props["aria-expanded"] = String(options.ariaExpanded);
|
|
1081
|
+
if (options.ariaControls !== void 0) props["aria-controls"] = options.ariaControls;
|
|
1082
|
+
if (options.ariaHidden !== void 0) props["aria-hidden"] = String(options.ariaHidden);
|
|
1083
|
+
if (options.ariaLive !== void 0) props["aria-live"] = options.ariaLive;
|
|
1084
|
+
if (options.ariaCurrent !== void 0) props["aria-current"] = String(options.ariaCurrent);
|
|
1085
|
+
if (options.ariaInvalid !== void 0) props["aria-invalid"] = String(options.ariaInvalid);
|
|
1086
|
+
if (options.ariaRequired !== void 0) props["aria-required"] = String(options.ariaRequired);
|
|
1087
|
+
}
|
|
1088
|
+
function containerProps(options) {
|
|
1089
|
+
const props = {};
|
|
1090
|
+
if (options.class !== void 0) props["class"] = options.class;
|
|
1091
|
+
if (options.id !== void 0) props["id"] = options.id;
|
|
1092
|
+
if (options.key !== void 0) props["key"] = options.key;
|
|
1093
|
+
applyA11yProps(props, options);
|
|
1094
|
+
return props;
|
|
1095
|
+
}
|
|
1096
|
+
function itemIdentity(item, index) {
|
|
1097
|
+
if (item !== null && typeof item === "object") {
|
|
1098
|
+
const obj = item;
|
|
1099
|
+
if ("id" in obj) return `id:${String(obj["id"])}`;
|
|
1100
|
+
if ("key" in obj) return `key:${String(obj["key"])}`;
|
|
1101
|
+
return `idx:${index}`;
|
|
1102
|
+
}
|
|
1103
|
+
return `val:${String(item)}`;
|
|
1104
|
+
}
|
|
1105
|
+
function itemValueSignature(item) {
|
|
1106
|
+
try {
|
|
1107
|
+
return JSON.stringify(item) ?? String(item);
|
|
1108
|
+
} catch {
|
|
1109
|
+
return String(item);
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
function reactiveListItemSignature(item) {
|
|
1113
|
+
return itemValueSignature(item);
|
|
1114
|
+
}
|
|
1115
|
+
function reactiveListItemKey(item, index) {
|
|
1116
|
+
return itemIdentity(item, index);
|
|
1117
|
+
}
|
|
1118
|
+
var ContentBuilderBase = class {
|
|
1119
|
+
constructor(_node, _graph) {
|
|
1120
|
+
this._node = _node;
|
|
1121
|
+
this._graph = _graph;
|
|
1122
|
+
}
|
|
1123
|
+
heading(text, options = {}) {
|
|
1124
|
+
const props = { level: options.level ?? 1 };
|
|
1125
|
+
if (options.class !== void 0) props["class"] = options.class;
|
|
1126
|
+
if (options.id !== void 0) props["id"] = options.id;
|
|
1127
|
+
applyA11yProps(props, options);
|
|
1128
|
+
const node = this._graph.createNode("heading", { parent: this._node, props });
|
|
1129
|
+
const resolved = bindValue(this._graph, node, "text", text);
|
|
1130
|
+
node.setProp("text", resolved);
|
|
1131
|
+
}
|
|
1132
|
+
text(content, options = {}) {
|
|
1133
|
+
const props = {};
|
|
1134
|
+
if (options.class !== void 0) props["class"] = options.class;
|
|
1135
|
+
if (options.id !== void 0) props["id"] = options.id;
|
|
1136
|
+
applyA11yProps(props, options);
|
|
1137
|
+
const node = this._graph.createNode("text", { parent: this._node, props });
|
|
1138
|
+
const resolved = bindValue(this._graph, node, "text", content);
|
|
1139
|
+
node.setProp("text", resolved);
|
|
1140
|
+
}
|
|
1141
|
+
button(label, options = {}) {
|
|
1142
|
+
const props = {};
|
|
1143
|
+
if (options.class !== void 0) props["class"] = options.class;
|
|
1144
|
+
if (options.id !== void 0) props["id"] = options.id;
|
|
1145
|
+
applyA11yProps(props, options);
|
|
1146
|
+
const node = this._graph.createNode("button", { parent: this._node, props });
|
|
1147
|
+
const resolved = bindValue(this._graph, node, "label", label);
|
|
1148
|
+
node.setProp("label", resolved);
|
|
1149
|
+
if (options.disabled !== void 0) {
|
|
1150
|
+
const resolvedDisabled = bindValue(this._graph, node, "disabled", options.disabled);
|
|
1151
|
+
node.setProp("disabled", resolvedDisabled);
|
|
1152
|
+
}
|
|
1153
|
+
if (options.onClick !== void 0) {
|
|
1154
|
+
const handlerKey = `click:${node.id}`;
|
|
1155
|
+
this._graph.registerHandler(handlerKey, options.onClick);
|
|
1156
|
+
node.addEvent({ type: "click", handlerKey });
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
input(options = {}) {
|
|
1160
|
+
const props = {};
|
|
1161
|
+
props["inputType"] = options.type ?? "text";
|
|
1162
|
+
if (options.placeholder !== void 0) props["placeholder"] = options.placeholder;
|
|
1163
|
+
if (options.class !== void 0) props["class"] = options.class;
|
|
1164
|
+
if (options.id !== void 0) props["id"] = options.id;
|
|
1165
|
+
applyA11yProps(props, options);
|
|
1166
|
+
const nodeOpts = {
|
|
1167
|
+
props,
|
|
1168
|
+
parent: this._node
|
|
1169
|
+
};
|
|
1170
|
+
if (options.id !== void 0) nodeOpts.key = options.id;
|
|
1171
|
+
const node = this._graph.createNode("input", nodeOpts);
|
|
1172
|
+
const bindSignal = options.bind;
|
|
1173
|
+
const valueBindable = bindSignal !== void 0 ? bindSignal : options.value;
|
|
1174
|
+
const inputHandler = bindSignal !== void 0 ? (v) => bindSignal.set(v) : options.onInput;
|
|
1175
|
+
if (valueBindable !== void 0) {
|
|
1176
|
+
const resolved = bindValue(this._graph, node, "value", valueBindable);
|
|
1177
|
+
node.setProp("value", resolved);
|
|
1178
|
+
}
|
|
1179
|
+
if (options.disabled !== void 0) {
|
|
1180
|
+
const resolved = bindValue(this._graph, node, "disabled", options.disabled);
|
|
1181
|
+
node.setProp("disabled", resolved);
|
|
1182
|
+
}
|
|
1183
|
+
if (inputHandler !== void 0) {
|
|
1184
|
+
const handlerKey = `input:${node.id}`;
|
|
1185
|
+
this._graph.registerHandler(handlerKey, inputHandler);
|
|
1186
|
+
node.addEvent({ type: "input", handlerKey });
|
|
1187
|
+
}
|
|
1188
|
+
if (options.onChange !== void 0) {
|
|
1189
|
+
const handlerKey = `change:${node.id}`;
|
|
1190
|
+
this._graph.registerHandler(handlerKey, options.onChange);
|
|
1191
|
+
node.addEvent({ type: "change", handlerKey });
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
image(options) {
|
|
1195
|
+
const props = {
|
|
1196
|
+
src: options.src,
|
|
1197
|
+
alt: options.alt
|
|
1198
|
+
};
|
|
1199
|
+
if (options.width !== void 0) props["width"] = options.width;
|
|
1200
|
+
if (options.height !== void 0) props["height"] = options.height;
|
|
1201
|
+
if (options.class !== void 0) props["class"] = options.class;
|
|
1202
|
+
if (options.id !== void 0) props["id"] = options.id;
|
|
1203
|
+
applyA11yProps(props, options);
|
|
1204
|
+
const nodeOpts = {
|
|
1205
|
+
props,
|
|
1206
|
+
parent: this._node
|
|
1207
|
+
};
|
|
1208
|
+
if (options.id !== void 0) nodeOpts.key = options.id;
|
|
1209
|
+
this._graph.createNode("image", nodeOpts);
|
|
1210
|
+
}
|
|
1211
|
+
link(label, options) {
|
|
1212
|
+
const props = {
|
|
1213
|
+
href: options.href,
|
|
1214
|
+
external: options.external ?? false
|
|
1215
|
+
};
|
|
1216
|
+
if (options.class !== void 0) props["class"] = options.class;
|
|
1217
|
+
if (options.id !== void 0) props["id"] = options.id;
|
|
1218
|
+
applyA11yProps(props, options);
|
|
1219
|
+
const node = this._graph.createNode("link", { parent: this._node, props });
|
|
1220
|
+
const resolved = bindValue(this._graph, node, "label", label);
|
|
1221
|
+
node.setProp("label", resolved);
|
|
1222
|
+
if (options.onClick !== void 0) {
|
|
1223
|
+
const handlerKey = `click:${node.id}`;
|
|
1224
|
+
this._graph.registerHandler(handlerKey, options.onClick);
|
|
1225
|
+
node.addEvent({ type: "click", handlerKey });
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
};
|
|
1229
|
+
var ContainerBuilderBase = class extends ContentBuilderBase {
|
|
1230
|
+
section(key, builder, options = {}) {
|
|
1231
|
+
const node = this._graph.createNode("section", {
|
|
1232
|
+
key,
|
|
1233
|
+
parent: this._node,
|
|
1234
|
+
props: containerProps(options)
|
|
1235
|
+
});
|
|
1236
|
+
builder(new SectionBuilderImpl(node, this._graph));
|
|
1237
|
+
}
|
|
1238
|
+
container(key, builder, options = {}) {
|
|
1239
|
+
const node = this._graph.createNode("container", {
|
|
1240
|
+
key,
|
|
1241
|
+
parent: this._node,
|
|
1242
|
+
props: containerProps(options)
|
|
1243
|
+
});
|
|
1244
|
+
builder(new ContainerBuilderImpl(node, this._graph));
|
|
1245
|
+
}
|
|
1246
|
+
list(key, builder, options = {}) {
|
|
1247
|
+
const node = this._graph.createNode("list", {
|
|
1248
|
+
key,
|
|
1249
|
+
parent: this._node,
|
|
1250
|
+
props: containerProps(options)
|
|
1251
|
+
});
|
|
1252
|
+
builder(new ListBuilderImpl(node, this._graph));
|
|
1253
|
+
}
|
|
1254
|
+
listOf(key, items, renderItem, options = {}) {
|
|
1255
|
+
const graph = this._graph;
|
|
1256
|
+
const node = graph.createNode("reactive-list", {
|
|
1257
|
+
key,
|
|
1258
|
+
parent: this._node,
|
|
1259
|
+
props: containerProps(options)
|
|
1260
|
+
});
|
|
1261
|
+
const signalId = `${node.id}:items`;
|
|
1262
|
+
node.stateRefs.push({ signalId, propKey: "items" });
|
|
1263
|
+
graph.registerHandler(`__signal__${signalId}`, items);
|
|
1264
|
+
const buildItem = (item, index) => {
|
|
1265
|
+
const itemKey = reactiveListItemKey(item, index);
|
|
1266
|
+
const itemNode = graph.createNode("list-item", {
|
|
1267
|
+
key: itemKey,
|
|
1268
|
+
props: { key: itemKey, _sig: reactiveListItemSignature(item) }
|
|
1269
|
+
});
|
|
1270
|
+
renderItem(item, index, new ContainerBuilderImpl(itemNode, graph));
|
|
1271
|
+
return itemNode;
|
|
1272
|
+
};
|
|
1273
|
+
const buildAll = (raw) => {
|
|
1274
|
+
const arr = Array.isArray(raw) ? raw : [];
|
|
1275
|
+
return arr.map((item, i) => buildItem(item, i));
|
|
1276
|
+
};
|
|
1277
|
+
graph.registerHandler(`__listbuild__${node.id}`, buildAll);
|
|
1278
|
+
const current = isSignal(items) ? items.peek() : items;
|
|
1279
|
+
const initial = Array.isArray(current) ? current : [];
|
|
1280
|
+
initial.forEach((item, i) => {
|
|
1281
|
+
node.appendChild(buildItem(item, i));
|
|
1282
|
+
});
|
|
1283
|
+
}
|
|
1284
|
+
form(key, builder, options = {}) {
|
|
1285
|
+
const props = containerProps(options);
|
|
1286
|
+
const node = this._graph.createNode("form", {
|
|
1287
|
+
key,
|
|
1288
|
+
parent: this._node,
|
|
1289
|
+
props
|
|
1290
|
+
});
|
|
1291
|
+
if (options.onSubmit !== void 0) {
|
|
1292
|
+
const handlerKey = `submit:${node.id}`;
|
|
1293
|
+
this._graph.registerHandler(handlerKey, options.onSubmit);
|
|
1294
|
+
node.addEvent({ type: "submit", handlerKey });
|
|
1295
|
+
}
|
|
1296
|
+
builder(new FormBuilderImpl(node, this._graph));
|
|
1297
|
+
}
|
|
1298
|
+
when(condition, builder, elseBuilder) {
|
|
1299
|
+
const graph = this._graph;
|
|
1300
|
+
const node = graph.createNode("conditional", {
|
|
1301
|
+
parent: this._node,
|
|
1302
|
+
props: containerProps({})
|
|
1303
|
+
});
|
|
1304
|
+
const buildBranch = (build, tag) => {
|
|
1305
|
+
const branchKey = `when-${tag}:${node.id}`;
|
|
1306
|
+
const branch = graph.createNode("container", {
|
|
1307
|
+
key: branchKey,
|
|
1308
|
+
props: { key: branchKey }
|
|
1309
|
+
});
|
|
1310
|
+
build(new ContainerBuilderImpl(branch, graph));
|
|
1311
|
+
return branch;
|
|
1312
|
+
};
|
|
1313
|
+
const buildAll = (raw) => {
|
|
1314
|
+
if (raw) return [buildBranch(builder, "then")];
|
|
1315
|
+
return elseBuilder !== void 0 ? [buildBranch(elseBuilder, "else")] : [];
|
|
1316
|
+
};
|
|
1317
|
+
if (isSignal(condition)) {
|
|
1318
|
+
const signalId = `${node.id}:items`;
|
|
1319
|
+
node.stateRefs.push({ signalId, propKey: "items" });
|
|
1320
|
+
graph.registerHandler(`__signal__${signalId}`, condition);
|
|
1321
|
+
graph.registerHandler(`__listbuild__${node.id}`, buildAll);
|
|
1322
|
+
const current = condition.peek();
|
|
1323
|
+
for (const child of buildAll(current)) node.appendChild(child);
|
|
1324
|
+
} else {
|
|
1325
|
+
for (const child of buildAll(condition)) node.appendChild(child);
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
errorBoundary(id, builder, options) {
|
|
1329
|
+
const sources = options.source === void 0 ? [] : Array.isArray(options.source) ? [...options.source] : [options.source];
|
|
1330
|
+
const localError = signal(void 0);
|
|
1331
|
+
const retryNonce = signal(0);
|
|
1332
|
+
const readError = () => {
|
|
1333
|
+
const local = localError.peek();
|
|
1334
|
+
if (local !== void 0 && local !== null) return local;
|
|
1335
|
+
for (const s of sources) {
|
|
1336
|
+
const e = s.peek();
|
|
1337
|
+
if (e !== void 0 && e !== null) return e;
|
|
1338
|
+
}
|
|
1339
|
+
return void 0;
|
|
1340
|
+
};
|
|
1341
|
+
const hasError = derived(() => {
|
|
1342
|
+
retryNonce.get();
|
|
1343
|
+
localError.get();
|
|
1344
|
+
for (const s of sources) s.get();
|
|
1345
|
+
return readError() !== void 0;
|
|
1346
|
+
});
|
|
1347
|
+
const retry = () => {
|
|
1348
|
+
localError.set(void 0);
|
|
1349
|
+
options.onRetry?.();
|
|
1350
|
+
retryNonce.update((n) => n + 1);
|
|
1351
|
+
};
|
|
1352
|
+
this.container(id, (c) => {
|
|
1353
|
+
c.when(
|
|
1354
|
+
hasError,
|
|
1355
|
+
// Error state → fallback.
|
|
1356
|
+
(fb) => options.fallback(fb, readError(), retry),
|
|
1357
|
+
// Healthy state → body, guarded against synchronous build throws.
|
|
1358
|
+
(body) => {
|
|
1359
|
+
try {
|
|
1360
|
+
builder(body);
|
|
1361
|
+
} catch (err) {
|
|
1362
|
+
queueMicrotask(() => localError.set(err));
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
);
|
|
1366
|
+
}, { id });
|
|
1367
|
+
}
|
|
1368
|
+
};
|
|
1369
|
+
var SectionBuilderImpl = class extends ContainerBuilderBase {
|
|
1370
|
+
};
|
|
1371
|
+
var ContainerBuilderImpl = class extends ContainerBuilderBase {
|
|
1372
|
+
};
|
|
1373
|
+
var FormBuilderImpl = class extends ContainerBuilderBase {
|
|
1374
|
+
};
|
|
1375
|
+
var ListBuilderImpl = class extends ContentBuilderBase {
|
|
1376
|
+
item(key, builder, options = {}) {
|
|
1377
|
+
const node = this._graph.createNode("list-item", {
|
|
1378
|
+
key,
|
|
1379
|
+
parent: this._node,
|
|
1380
|
+
props: containerProps(options)
|
|
1381
|
+
});
|
|
1382
|
+
builder(new ContainerBuilderImpl(node, this._graph));
|
|
1383
|
+
}
|
|
1384
|
+
};
|
|
1385
|
+
var PageBuilderImpl = class extends ContainerBuilderBase {
|
|
1386
|
+
};
|
|
1387
|
+
var AppBuilder = class {
|
|
1388
|
+
constructor(_graph) {
|
|
1389
|
+
this._graph = _graph;
|
|
1390
|
+
}
|
|
1391
|
+
page(key, builder) {
|
|
1392
|
+
const node = this._graph.createNode("page", {
|
|
1393
|
+
key,
|
|
1394
|
+
parent: this._graph.root,
|
|
1395
|
+
props: { key }
|
|
1396
|
+
});
|
|
1397
|
+
builder(new PageBuilderImpl(node, this._graph));
|
|
1398
|
+
}
|
|
1399
|
+
};
|
|
1400
|
+
|
|
1401
|
+
// ../dsl/src/dsl.ts
|
|
1402
|
+
var StreetApp = class {
|
|
1403
|
+
_graph;
|
|
1404
|
+
_builder;
|
|
1405
|
+
constructor(options) {
|
|
1406
|
+
const graphOpts = { name: options.name };
|
|
1407
|
+
if (options.version !== void 0) graphOpts.version = options.version;
|
|
1408
|
+
this._graph = new ApplicationGraph(graphOpts);
|
|
1409
|
+
this._builder = new AppBuilder(this._graph);
|
|
1410
|
+
}
|
|
1411
|
+
page(key, builder) {
|
|
1412
|
+
this._builder.page(key, builder);
|
|
1413
|
+
return this;
|
|
1414
|
+
}
|
|
1415
|
+
/** Compile to ApplicationGraph — validates and returns the graph. */
|
|
1416
|
+
build() {
|
|
1417
|
+
const dc = this._graph.validate();
|
|
1418
|
+
dc.throwIfErrors();
|
|
1419
|
+
return this._graph;
|
|
1420
|
+
}
|
|
1421
|
+
/** Access graph before building (useful for inspection). */
|
|
1422
|
+
get graph() {
|
|
1423
|
+
return this._graph;
|
|
1424
|
+
}
|
|
1425
|
+
};
|
|
1426
|
+
var streetui = {
|
|
1427
|
+
app(options) {
|
|
1428
|
+
return new StreetApp(options);
|
|
1429
|
+
}
|
|
1430
|
+
};
|
|
1431
|
+
|
|
1432
|
+
// ../compiler/src/validation/validator.ts
|
|
1433
|
+
function validateGraph(graph) {
|
|
1434
|
+
const dc = new DiagnosticCollector();
|
|
1435
|
+
dc.merge(graph.validate());
|
|
1436
|
+
const pages = graph.findByType("page");
|
|
1437
|
+
if (pages.length === 0) {
|
|
1438
|
+
dc.warn(
|
|
1439
|
+
"COMPILER_NO_PAGES",
|
|
1440
|
+
"Application has no pages defined. At least one page is recommended."
|
|
1441
|
+
);
|
|
1442
|
+
}
|
|
1443
|
+
graph.walk((node) => {
|
|
1444
|
+
validateNode(node, dc);
|
|
1445
|
+
});
|
|
1446
|
+
return dc;
|
|
1447
|
+
}
|
|
1448
|
+
function validateNode(node, dc) {
|
|
1449
|
+
switch (node.type) {
|
|
1450
|
+
case "heading": {
|
|
1451
|
+
const text = node.getProp("text");
|
|
1452
|
+
if (text === void 0 || text === "") {
|
|
1453
|
+
dc.warn("COMPILER_EMPTY_HEADING", `Heading node "${node.id}" has no text content`, {
|
|
1454
|
+
nodeId: node.id
|
|
1455
|
+
});
|
|
1456
|
+
}
|
|
1457
|
+
break;
|
|
1458
|
+
}
|
|
1459
|
+
case "image": {
|
|
1460
|
+
const src = node.getProp("src");
|
|
1461
|
+
const alt = node.getProp("alt");
|
|
1462
|
+
if (!src) {
|
|
1463
|
+
dc.error("COMPILER_IMAGE_NO_SRC", `Image node "${node.id}" is missing src`, {
|
|
1464
|
+
nodeId: node.id
|
|
1465
|
+
});
|
|
1466
|
+
}
|
|
1467
|
+
if (!alt) {
|
|
1468
|
+
dc.warn("COMPILER_IMAGE_NO_ALT", `Image node "${node.id}" is missing alt text`, {
|
|
1469
|
+
nodeId: node.id
|
|
1470
|
+
});
|
|
1471
|
+
}
|
|
1472
|
+
break;
|
|
1473
|
+
}
|
|
1474
|
+
case "link": {
|
|
1475
|
+
const href = node.getProp("href");
|
|
1476
|
+
if (!href) {
|
|
1477
|
+
dc.error("COMPILER_LINK_NO_HREF", `Link node "${node.id}" is missing href`, {
|
|
1478
|
+
nodeId: node.id
|
|
1479
|
+
});
|
|
1480
|
+
}
|
|
1481
|
+
break;
|
|
1482
|
+
}
|
|
1483
|
+
default:
|
|
1484
|
+
break;
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
// ../compiler/src/transform/transform.ts
|
|
1489
|
+
function transformGraph(graph) {
|
|
1490
|
+
graph.walk((node, depth) => {
|
|
1491
|
+
applyDefaults(node);
|
|
1492
|
+
ensureRenderKey(node, depth);
|
|
1493
|
+
});
|
|
1494
|
+
}
|
|
1495
|
+
function applyDefaults(node) {
|
|
1496
|
+
switch (node.type) {
|
|
1497
|
+
case "heading": {
|
|
1498
|
+
if (node.getProp("level") === void 0) {
|
|
1499
|
+
node.setProp("level", 1);
|
|
1500
|
+
}
|
|
1501
|
+
break;
|
|
1502
|
+
}
|
|
1503
|
+
case "input": {
|
|
1504
|
+
if (node.getProp("inputType") === void 0) {
|
|
1505
|
+
node.setProp("inputType", "text");
|
|
1506
|
+
}
|
|
1507
|
+
break;
|
|
1508
|
+
}
|
|
1509
|
+
case "link": {
|
|
1510
|
+
if (node.getProp("external") === void 0) {
|
|
1511
|
+
node.setProp("external", false);
|
|
1512
|
+
}
|
|
1513
|
+
break;
|
|
1514
|
+
}
|
|
1515
|
+
default:
|
|
1516
|
+
break;
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
function ensureRenderKey(node, depth) {
|
|
1520
|
+
if (node.getProp("_renderKey") === void 0) {
|
|
1521
|
+
const key = node.key ?? `${node.type}:${node.id}:${depth}`;
|
|
1522
|
+
node.setProp("_renderKey", key);
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
// ../compiler/src/compile.ts
|
|
1527
|
+
function compile(app, options = {}) {
|
|
1528
|
+
const strict = options.strict ?? true;
|
|
1529
|
+
const strictWarnings = options.strictWarnings ?? false;
|
|
1530
|
+
const dc = new DiagnosticCollector();
|
|
1531
|
+
const graph = app.graph;
|
|
1532
|
+
const validationDc = validateGraph(graph);
|
|
1533
|
+
dc.merge(validationDc);
|
|
1534
|
+
if (strict && dc.hasErrors) {
|
|
1535
|
+
dc.throwIfErrors();
|
|
1536
|
+
}
|
|
1537
|
+
if (strictWarnings && dc.hasWarnings) {
|
|
1538
|
+
throw new Error(
|
|
1539
|
+
`[StreetUI Compiler] Compilation failed: warnings treated as errors.
|
|
1540
|
+
` + dc.diagnostics.filter((d) => d.severity === "warning").map((d) => ` [${d.code}] ${d.message}`).join("\n")
|
|
1541
|
+
);
|
|
1542
|
+
}
|
|
1543
|
+
transformGraph(graph);
|
|
1544
|
+
return {
|
|
1545
|
+
graph,
|
|
1546
|
+
diagnostics: dc,
|
|
1547
|
+
name: graph.name,
|
|
1548
|
+
version: graph.version,
|
|
1549
|
+
compiledAt: Date.now()
|
|
1550
|
+
};
|
|
1551
|
+
}
|
|
1552
|
+
function compileGraph(graph, options = {}) {
|
|
1553
|
+
const strict = options.strict ?? true;
|
|
1554
|
+
const dc = new DiagnosticCollector();
|
|
1555
|
+
const validationDc = validateGraph(graph);
|
|
1556
|
+
dc.merge(validationDc);
|
|
1557
|
+
if (strict && dc.hasErrors) {
|
|
1558
|
+
dc.throwIfErrors();
|
|
1559
|
+
}
|
|
1560
|
+
transformGraph(graph);
|
|
1561
|
+
return {
|
|
1562
|
+
graph,
|
|
1563
|
+
diagnostics: dc,
|
|
1564
|
+
name: graph.name,
|
|
1565
|
+
version: graph.version,
|
|
1566
|
+
compiledAt: Date.now()
|
|
1567
|
+
};
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
// ../runtime/src/node-instance.ts
|
|
1571
|
+
var RuntimeNodeInstance = class {
|
|
1572
|
+
graphNode;
|
|
1573
|
+
domNode;
|
|
1574
|
+
children = [];
|
|
1575
|
+
cleanup = new CleanupRegistry();
|
|
1576
|
+
_mounted = false;
|
|
1577
|
+
constructor(options) {
|
|
1578
|
+
this.graphNode = options.graphNode;
|
|
1579
|
+
this.domNode = options.domNode;
|
|
1580
|
+
}
|
|
1581
|
+
get isMounted() {
|
|
1582
|
+
return this._mounted;
|
|
1583
|
+
}
|
|
1584
|
+
mount() {
|
|
1585
|
+
this._mounted = true;
|
|
1586
|
+
}
|
|
1587
|
+
unmount() {
|
|
1588
|
+
if (!this._mounted) return;
|
|
1589
|
+
this._mounted = false;
|
|
1590
|
+
for (const child of this.children) {
|
|
1591
|
+
child.unmount();
|
|
1592
|
+
}
|
|
1593
|
+
this.cleanup.run();
|
|
1594
|
+
}
|
|
1595
|
+
addChild(instance) {
|
|
1596
|
+
this.children.push(instance);
|
|
1597
|
+
}
|
|
1598
|
+
/** Subscribe to a signal and register the unsubscribe for cleanup. */
|
|
1599
|
+
trackSignal(signal2, handler) {
|
|
1600
|
+
const unsub = signal2.subscribe(handler);
|
|
1601
|
+
this.cleanup.add(unsub);
|
|
1602
|
+
}
|
|
1603
|
+
/** Register an arbitrary cleanup function (e.g. DOM event removal). */
|
|
1604
|
+
trackCleanup(fn) {
|
|
1605
|
+
this.cleanup.add(fn);
|
|
1606
|
+
}
|
|
1607
|
+
};
|
|
1608
|
+
|
|
1609
|
+
// ../scheduler/src/scheduler.ts
|
|
1610
|
+
var PRIORITY_ORDER = {
|
|
1611
|
+
immediate: 0,
|
|
1612
|
+
normal: 1,
|
|
1613
|
+
idle: 2
|
|
1614
|
+
};
|
|
1615
|
+
var Scheduler = class {
|
|
1616
|
+
_queue = /* @__PURE__ */ new Map();
|
|
1617
|
+
_flushScheduled = false;
|
|
1618
|
+
_flushing = false;
|
|
1619
|
+
_diagnostics = void 0;
|
|
1620
|
+
/**
|
|
1621
|
+
* Install an optional diagnostic sink for swallowed job errors. Pass
|
|
1622
|
+
* `undefined` to restore the default `console.error` reporting. Additive and
|
|
1623
|
+
* opt-in — the scheduler never sends anything anywhere on its own.
|
|
1624
|
+
*/
|
|
1625
|
+
setDiagnostics(sink) {
|
|
1626
|
+
this._diagnostics = sink;
|
|
1627
|
+
}
|
|
1628
|
+
/** Total jobs currently queued. */
|
|
1629
|
+
get size() {
|
|
1630
|
+
return this._queue.size;
|
|
1631
|
+
}
|
|
1632
|
+
/** True if a flush has been scheduled but not yet executed. */
|
|
1633
|
+
get isPending() {
|
|
1634
|
+
return this._flushScheduled;
|
|
1635
|
+
}
|
|
1636
|
+
/**
|
|
1637
|
+
* Enqueue a job. If a job with the same key exists, the new one replaces it
|
|
1638
|
+
* (allowing callers to coalesce repeated updates for the same node).
|
|
1639
|
+
*/
|
|
1640
|
+
schedule(job) {
|
|
1641
|
+
this._queue.set(job.key, job);
|
|
1642
|
+
if (!this._flushScheduled && !this._flushing) {
|
|
1643
|
+
this._flushScheduled = true;
|
|
1644
|
+
this._scheduleMicrotask();
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
/** Schedule multiple jobs atomically. */
|
|
1648
|
+
scheduleAll(jobs) {
|
|
1649
|
+
for (const job of jobs) {
|
|
1650
|
+
this._queue.set(job.key, job);
|
|
1651
|
+
}
|
|
1652
|
+
if (!this._flushScheduled && !this._flushing && this._queue.size > 0) {
|
|
1653
|
+
this._flushScheduled = true;
|
|
1654
|
+
this._scheduleMicrotask();
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
/**
|
|
1658
|
+
* Cancel a queued job by key. No-op if not queued.
|
|
1659
|
+
*/
|
|
1660
|
+
cancel(key) {
|
|
1661
|
+
this._queue.delete(key);
|
|
1662
|
+
}
|
|
1663
|
+
/**
|
|
1664
|
+
* Synchronously flush all queued jobs (sorted by priority).
|
|
1665
|
+
* Useful in tests and for immediate rendering.
|
|
1666
|
+
*/
|
|
1667
|
+
flush() {
|
|
1668
|
+
if (this._flushing) return;
|
|
1669
|
+
this._flushScheduled = false;
|
|
1670
|
+
this._flushing = true;
|
|
1671
|
+
const jobs = Array.from(this._queue.values()).sort(
|
|
1672
|
+
(a, b) => PRIORITY_ORDER[a.priority] - PRIORITY_ORDER[b.priority]
|
|
1673
|
+
);
|
|
1674
|
+
this._queue.clear();
|
|
1675
|
+
try {
|
|
1676
|
+
for (const job of jobs) {
|
|
1677
|
+
try {
|
|
1678
|
+
job.fn();
|
|
1679
|
+
} catch (err) {
|
|
1680
|
+
if (this._diagnostics?.error) {
|
|
1681
|
+
this._diagnostics.error(`Scheduler job "${job.key}" threw`, err);
|
|
1682
|
+
} else {
|
|
1683
|
+
console.error(`[Scheduler] Job "${job.key}" threw:`, err);
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
} finally {
|
|
1688
|
+
this._flushing = false;
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
/** Clear all pending jobs without executing them. */
|
|
1692
|
+
clear() {
|
|
1693
|
+
this._queue.clear();
|
|
1694
|
+
this._flushScheduled = false;
|
|
1695
|
+
}
|
|
1696
|
+
_scheduleMicrotask() {
|
|
1697
|
+
Promise.resolve().then(() => {
|
|
1698
|
+
if (this._flushScheduled) {
|
|
1699
|
+
this.flush();
|
|
1700
|
+
}
|
|
1701
|
+
});
|
|
1702
|
+
}
|
|
1703
|
+
};
|
|
1704
|
+
var scheduler = new Scheduler();
|
|
1705
|
+
function scheduleUpdate(key, fn) {
|
|
1706
|
+
scheduler.schedule({ key, priority: "normal", fn });
|
|
1707
|
+
}
|
|
1708
|
+
function scheduleImmediate(key, fn) {
|
|
1709
|
+
scheduler.schedule({ key, priority: "immediate", fn });
|
|
1710
|
+
}
|
|
1711
|
+
function flushSync() {
|
|
1712
|
+
scheduler.flush();
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1715
|
+
// ../runtime/src/runtime.ts
|
|
1716
|
+
var Runtime = class {
|
|
1717
|
+
_renderer;
|
|
1718
|
+
_scheduler;
|
|
1719
|
+
_cleanup = new CleanupRegistry();
|
|
1720
|
+
_renderHandle = null;
|
|
1721
|
+
_mounted = false;
|
|
1722
|
+
constructor(options) {
|
|
1723
|
+
this._renderer = options.renderer;
|
|
1724
|
+
this._scheduler = options.scheduler ?? new Scheduler();
|
|
1725
|
+
}
|
|
1726
|
+
get isMounted() {
|
|
1727
|
+
return this._mounted;
|
|
1728
|
+
}
|
|
1729
|
+
/**
|
|
1730
|
+
* Mount the compiled application into the given DOM container.
|
|
1731
|
+
*/
|
|
1732
|
+
mount(compiled, container) {
|
|
1733
|
+
if (this._mounted) {
|
|
1734
|
+
throw new Error("[Runtime] Already mounted. Call unmount() first.");
|
|
1735
|
+
}
|
|
1736
|
+
this._renderHandle = this._renderer.mount(compiled, container);
|
|
1737
|
+
this._mounted = true;
|
|
1738
|
+
this._bindSignals(compiled.graph);
|
|
1739
|
+
const self2 = this;
|
|
1740
|
+
return {
|
|
1741
|
+
renderHandle: this._renderHandle,
|
|
1742
|
+
runtime: this,
|
|
1743
|
+
unmount() {
|
|
1744
|
+
self2.unmount();
|
|
1745
|
+
},
|
|
1746
|
+
flush() {
|
|
1747
|
+
self2._scheduler.flush();
|
|
1748
|
+
}
|
|
1749
|
+
};
|
|
1750
|
+
}
|
|
1751
|
+
unmount() {
|
|
1752
|
+
if (!this._mounted) return;
|
|
1753
|
+
this._mounted = false;
|
|
1754
|
+
this._renderHandle?.unmount();
|
|
1755
|
+
this._renderHandle = null;
|
|
1756
|
+
this._cleanup.run();
|
|
1757
|
+
}
|
|
1758
|
+
/**
|
|
1759
|
+
* Hydrate a container that already holds server-rendered HTML for this
|
|
1760
|
+
* application. Delegates to the renderer's `hydrate` (adopting the existing
|
|
1761
|
+
* DOM instead of recreating it) and falls back to `mount` for renderers that
|
|
1762
|
+
* cannot hydrate. Signal binding is identical to `mount`, so the live client
|
|
1763
|
+
* lifecycle is established the same way.
|
|
1764
|
+
*/
|
|
1765
|
+
hydrate(compiled, container) {
|
|
1766
|
+
if (this._mounted) {
|
|
1767
|
+
throw new Error("[Runtime] Already mounted. Call unmount() first.");
|
|
1768
|
+
}
|
|
1769
|
+
this._renderHandle = this._renderer.hydrate !== void 0 ? this._renderer.hydrate(compiled, container) : this._renderer.mount(compiled, container);
|
|
1770
|
+
this._mounted = true;
|
|
1771
|
+
this._bindSignals(compiled.graph);
|
|
1772
|
+
const self2 = this;
|
|
1773
|
+
return {
|
|
1774
|
+
renderHandle: this._renderHandle,
|
|
1775
|
+
runtime: this,
|
|
1776
|
+
unmount() {
|
|
1777
|
+
self2.unmount();
|
|
1778
|
+
},
|
|
1779
|
+
flush() {
|
|
1780
|
+
self2._scheduler.flush();
|
|
1781
|
+
}
|
|
1782
|
+
};
|
|
1783
|
+
}
|
|
1784
|
+
/**
|
|
1785
|
+
* Walk the graph and subscribe to all signal-bound nodes.
|
|
1786
|
+
* When a signal changes, schedule a renderer update for that node.
|
|
1787
|
+
*/
|
|
1788
|
+
_bindSignals(graph) {
|
|
1789
|
+
graph.walk((node) => {
|
|
1790
|
+
for (const stateRef of node.stateRefs) {
|
|
1791
|
+
const signalKey = `__signal__${stateRef.signalId}`;
|
|
1792
|
+
const maybeSignal = graph.getHandler(signalKey);
|
|
1793
|
+
if (maybeSignal === void 0 || typeof maybeSignal.subscribe !== "function") continue;
|
|
1794
|
+
const unsub = maybeSignal.subscribe(() => {
|
|
1795
|
+
this._scheduler.schedule({
|
|
1796
|
+
key: `update:${node.id}:${stateRef.propKey}`,
|
|
1797
|
+
priority: "normal",
|
|
1798
|
+
fn: () => {
|
|
1799
|
+
this._renderHandle?.flush();
|
|
1800
|
+
}
|
|
1801
|
+
});
|
|
1802
|
+
});
|
|
1803
|
+
this._cleanup.add(unsub);
|
|
1804
|
+
}
|
|
1805
|
+
});
|
|
1806
|
+
}
|
|
1807
|
+
};
|
|
1808
|
+
function createRuntime(options) {
|
|
1809
|
+
return new Runtime(options);
|
|
1810
|
+
}
|
|
1811
|
+
|
|
1812
|
+
// ../events/src/event-types.ts
|
|
1813
|
+
function createStreetEvent(type, target, data, originalEvent) {
|
|
1814
|
+
let _stopped = false;
|
|
1815
|
+
const ev = {
|
|
1816
|
+
type,
|
|
1817
|
+
target,
|
|
1818
|
+
data,
|
|
1819
|
+
originalEvent,
|
|
1820
|
+
timestamp: Date.now(),
|
|
1821
|
+
defaultPrevented: false,
|
|
1822
|
+
stopPropagation() {
|
|
1823
|
+
_stopped = true;
|
|
1824
|
+
},
|
|
1825
|
+
preventDefault() {
|
|
1826
|
+
ev.defaultPrevented = true;
|
|
1827
|
+
}
|
|
1828
|
+
};
|
|
1829
|
+
return ev;
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
// ../events/src/event-bus.ts
|
|
1833
|
+
var EventBus = class {
|
|
1834
|
+
_handlers = /* @__PURE__ */ new Map();
|
|
1835
|
+
on(type, handler) {
|
|
1836
|
+
let set = this._handlers.get(type);
|
|
1837
|
+
if (set === void 0) {
|
|
1838
|
+
set = /* @__PURE__ */ new Set();
|
|
1839
|
+
this._handlers.set(type, set);
|
|
1840
|
+
}
|
|
1841
|
+
set.add(handler);
|
|
1842
|
+
return () => this.off(type, handler);
|
|
1843
|
+
}
|
|
1844
|
+
off(type, handler) {
|
|
1845
|
+
this._handlers.get(type)?.delete(handler);
|
|
1846
|
+
}
|
|
1847
|
+
once(type, handler) {
|
|
1848
|
+
const wrapped = (event) => {
|
|
1849
|
+
handler(event);
|
|
1850
|
+
this.off(type, wrapped);
|
|
1851
|
+
};
|
|
1852
|
+
return this.on(type, wrapped);
|
|
1853
|
+
}
|
|
1854
|
+
emit(event) {
|
|
1855
|
+
const handlers = this._handlers.get(event.type);
|
|
1856
|
+
if (handlers === void 0) return;
|
|
1857
|
+
for (const h of handlers) {
|
|
1858
|
+
h(event);
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
clear(type) {
|
|
1862
|
+
if (type !== void 0) {
|
|
1863
|
+
this._handlers.delete(type);
|
|
1864
|
+
} else {
|
|
1865
|
+
this._handlers.clear();
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
listenerCount(type) {
|
|
1869
|
+
return this._handlers.get(type)?.size ?? 0;
|
|
1870
|
+
}
|
|
1871
|
+
};
|
|
1872
|
+
var globalEventBus = new EventBus();
|
|
1873
|
+
|
|
1874
|
+
// ../events/src/dom-bridge.ts
|
|
1875
|
+
function bindDomEvent(element, domEventType, handler, options) {
|
|
1876
|
+
const listener = (e) => {
|
|
1877
|
+
const streetEvent = createStreetEvent(domEventType, element, void 0, e);
|
|
1878
|
+
handler(streetEvent);
|
|
1879
|
+
};
|
|
1880
|
+
element.addEventListener(domEventType, listener, options);
|
|
1881
|
+
return {
|
|
1882
|
+
remove() {
|
|
1883
|
+
element.removeEventListener(domEventType, listener, options);
|
|
1884
|
+
}
|
|
1885
|
+
};
|
|
1886
|
+
}
|
|
1887
|
+
var DomEventRegistry = class {
|
|
1888
|
+
_bindings = [];
|
|
1889
|
+
bind(element, type, handler, options) {
|
|
1890
|
+
this._bindings.push(bindDomEvent(element, type, handler, options));
|
|
1891
|
+
}
|
|
1892
|
+
removeAll() {
|
|
1893
|
+
for (const b of this._bindings) {
|
|
1894
|
+
b.remove();
|
|
1895
|
+
}
|
|
1896
|
+
this._bindings.length = 0;
|
|
1897
|
+
}
|
|
1898
|
+
get count() {
|
|
1899
|
+
return this._bindings.length;
|
|
1900
|
+
}
|
|
1901
|
+
};
|
|
1902
|
+
|
|
1903
|
+
// ../dom/src/browser-adapter.ts
|
|
1904
|
+
var BrowserDOMAdapter = class {
|
|
1905
|
+
createElement(tag, ns) {
|
|
1906
|
+
if (ns !== void 0) {
|
|
1907
|
+
return document.createElementNS(ns, tag);
|
|
1908
|
+
}
|
|
1909
|
+
return document.createElement(tag);
|
|
1910
|
+
}
|
|
1911
|
+
createTextNode(data) {
|
|
1912
|
+
return document.createTextNode(data);
|
|
1913
|
+
}
|
|
1914
|
+
createComment(data) {
|
|
1915
|
+
return document.createComment(data);
|
|
1916
|
+
}
|
|
1917
|
+
createFragment() {
|
|
1918
|
+
return document.createDocumentFragment();
|
|
1919
|
+
}
|
|
1920
|
+
appendChild(parent, child) {
|
|
1921
|
+
parent.appendChild(child);
|
|
1922
|
+
}
|
|
1923
|
+
insertBefore(parent, child, reference) {
|
|
1924
|
+
parent.insertBefore(child, reference);
|
|
1925
|
+
}
|
|
1926
|
+
removeChild(parent, child) {
|
|
1927
|
+
parent.removeChild(child);
|
|
1928
|
+
}
|
|
1929
|
+
replaceChild(parent, newChild, oldChild) {
|
|
1930
|
+
parent.replaceChild(newChild, oldChild);
|
|
1931
|
+
}
|
|
1932
|
+
setAttribute(element, name, value) {
|
|
1933
|
+
element.setAttribute(name, value);
|
|
1934
|
+
}
|
|
1935
|
+
removeAttribute(element, name) {
|
|
1936
|
+
element.removeAttribute(name);
|
|
1937
|
+
}
|
|
1938
|
+
getAttribute(element, name) {
|
|
1939
|
+
return element.getAttribute(name);
|
|
1940
|
+
}
|
|
1941
|
+
setProperty(element, name, value) {
|
|
1942
|
+
element[name] = value;
|
|
1943
|
+
}
|
|
1944
|
+
setTextContent(node, text) {
|
|
1945
|
+
node.textContent = text;
|
|
1946
|
+
}
|
|
1947
|
+
getTextContent(node) {
|
|
1948
|
+
return node.textContent;
|
|
1949
|
+
}
|
|
1950
|
+
addEventListener(target, type, handler, options) {
|
|
1951
|
+
target.addEventListener(type, handler, options);
|
|
1952
|
+
}
|
|
1953
|
+
removeEventListener(target, type, handler, options) {
|
|
1954
|
+
target.removeEventListener(type, handler, options);
|
|
1955
|
+
}
|
|
1956
|
+
querySelector(root, selector) {
|
|
1957
|
+
return root.querySelector(selector);
|
|
1958
|
+
}
|
|
1959
|
+
querySelectorAll(root, selector) {
|
|
1960
|
+
return root.querySelectorAll(selector);
|
|
1961
|
+
}
|
|
1962
|
+
getElementById(id) {
|
|
1963
|
+
return document.getElementById(id);
|
|
1964
|
+
}
|
|
1965
|
+
focus(element) {
|
|
1966
|
+
element.focus?.();
|
|
1967
|
+
}
|
|
1968
|
+
isElement(node) {
|
|
1969
|
+
return node.nodeType === Node.ELEMENT_NODE;
|
|
1970
|
+
}
|
|
1971
|
+
isTextNode(node) {
|
|
1972
|
+
return node.nodeType === Node.TEXT_NODE;
|
|
1973
|
+
}
|
|
1974
|
+
tagName(element) {
|
|
1975
|
+
return element.tagName.toLowerCase();
|
|
1976
|
+
}
|
|
1977
|
+
parentNode(node) {
|
|
1978
|
+
return node.parentNode;
|
|
1979
|
+
}
|
|
1980
|
+
nextSibling(node) {
|
|
1981
|
+
return node.nextSibling;
|
|
1982
|
+
}
|
|
1983
|
+
firstChild(node) {
|
|
1984
|
+
return node.firstChild;
|
|
1985
|
+
}
|
|
1986
|
+
childNodes(node) {
|
|
1987
|
+
return Array.from(node.childNodes);
|
|
1988
|
+
}
|
|
1989
|
+
};
|
|
1990
|
+
var browserDOMAdapter = new BrowserDOMAdapter();
|
|
1991
|
+
|
|
1992
|
+
// ../dom/src/server-node.ts
|
|
1993
|
+
var ServerStyle = class {
|
|
1994
|
+
declarations = /* @__PURE__ */ new Map();
|
|
1995
|
+
setProperty(name, value) {
|
|
1996
|
+
this.declarations.set(name, value);
|
|
1997
|
+
}
|
|
1998
|
+
get isEmpty() {
|
|
1999
|
+
return this.declarations.size === 0;
|
|
2000
|
+
}
|
|
2001
|
+
toCss() {
|
|
2002
|
+
return [...this.declarations.entries()].map(([k, v]) => `${k}: ${v}`).join("; ");
|
|
2003
|
+
}
|
|
2004
|
+
};
|
|
2005
|
+
var ServerText = class {
|
|
2006
|
+
kind = "text";
|
|
2007
|
+
parent = null;
|
|
2008
|
+
data;
|
|
2009
|
+
constructor(data) {
|
|
2010
|
+
this.data = data;
|
|
2011
|
+
}
|
|
2012
|
+
};
|
|
2013
|
+
var ServerComment = class {
|
|
2014
|
+
kind = "comment";
|
|
2015
|
+
parent = null;
|
|
2016
|
+
data;
|
|
2017
|
+
constructor(data) {
|
|
2018
|
+
this.data = data;
|
|
2019
|
+
}
|
|
2020
|
+
};
|
|
2021
|
+
var ServerFragment = class {
|
|
2022
|
+
kind = "fragment";
|
|
2023
|
+
parent = null;
|
|
2024
|
+
children = [];
|
|
2025
|
+
};
|
|
2026
|
+
var ServerElement = class {
|
|
2027
|
+
kind = "element";
|
|
2028
|
+
parent = null;
|
|
2029
|
+
tagName;
|
|
2030
|
+
attributes = /* @__PURE__ */ new Map();
|
|
2031
|
+
/** JS properties set via `setProperty` (e.g. input `value`, `checked`). */
|
|
2032
|
+
properties = /* @__PURE__ */ new Map();
|
|
2033
|
+
children = [];
|
|
2034
|
+
style = new ServerStyle();
|
|
2035
|
+
constructor(tagName) {
|
|
2036
|
+
this.tagName = tagName.toLowerCase();
|
|
2037
|
+
}
|
|
2038
|
+
};
|
|
2039
|
+
var VOID_ELEMENTS = /* @__PURE__ */ new Set([
|
|
2040
|
+
"area",
|
|
2041
|
+
"base",
|
|
2042
|
+
"br",
|
|
2043
|
+
"col",
|
|
2044
|
+
"embed",
|
|
2045
|
+
"hr",
|
|
2046
|
+
"img",
|
|
2047
|
+
"input",
|
|
2048
|
+
"link",
|
|
2049
|
+
"meta",
|
|
2050
|
+
"param",
|
|
2051
|
+
"source",
|
|
2052
|
+
"track",
|
|
2053
|
+
"wbr"
|
|
2054
|
+
]);
|
|
2055
|
+
var SERIALIZED_PROPERTIES = {
|
|
2056
|
+
value: "attr",
|
|
2057
|
+
checked: "boolean",
|
|
2058
|
+
selected: "boolean"
|
|
2059
|
+
};
|
|
2060
|
+
function escapeHtmlText(value) {
|
|
2061
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
2062
|
+
}
|
|
2063
|
+
function escapeHtmlAttr(value) {
|
|
2064
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
2065
|
+
}
|
|
2066
|
+
function serializeAttributes(el) {
|
|
2067
|
+
const parts = [];
|
|
2068
|
+
for (const [name, value] of el.attributes) {
|
|
2069
|
+
if (value === "") {
|
|
2070
|
+
parts.push(` ${name}`);
|
|
2071
|
+
} else {
|
|
2072
|
+
parts.push(` ${name}="${escapeHtmlAttr(value)}"`);
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
for (const [name, kind] of Object.entries(SERIALIZED_PROPERTIES)) {
|
|
2076
|
+
if (!el.properties.has(name)) continue;
|
|
2077
|
+
if (el.attributes.has(name)) continue;
|
|
2078
|
+
const raw = el.properties.get(name);
|
|
2079
|
+
if (kind === "boolean") {
|
|
2080
|
+
if (raw === true) parts.push(` ${name}`);
|
|
2081
|
+
} else {
|
|
2082
|
+
if (raw !== void 0 && raw !== null) {
|
|
2083
|
+
parts.push(` ${name}="${escapeHtmlAttr(String(raw))}"`);
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
if (!el.style.isEmpty && !el.attributes.has("style")) {
|
|
2088
|
+
parts.push(` style="${escapeHtmlAttr(el.style.toCss())}"`);
|
|
2089
|
+
}
|
|
2090
|
+
return parts.join("");
|
|
2091
|
+
}
|
|
2092
|
+
function serializeServerNode(node) {
|
|
2093
|
+
switch (node.kind) {
|
|
2094
|
+
case "text":
|
|
2095
|
+
return escapeHtmlText(node.data);
|
|
2096
|
+
case "comment":
|
|
2097
|
+
return `<!--${node.data}-->`;
|
|
2098
|
+
case "fragment":
|
|
2099
|
+
return serializeChildren(node);
|
|
2100
|
+
case "element": {
|
|
2101
|
+
const el = node;
|
|
2102
|
+
const tag = el.tagName;
|
|
2103
|
+
const attrs = serializeAttributes(el);
|
|
2104
|
+
if (VOID_ELEMENTS.has(tag)) {
|
|
2105
|
+
return `<${tag}${attrs}>`;
|
|
2106
|
+
}
|
|
2107
|
+
return `<${tag}${attrs}>${serializeChildren(el)}</${tag}>`;
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2110
|
+
}
|
|
2111
|
+
function serializeChildren(node) {
|
|
2112
|
+
let out = "";
|
|
2113
|
+
for (const child of node.children) {
|
|
2114
|
+
out += serializeServerNode(child);
|
|
2115
|
+
}
|
|
2116
|
+
return out;
|
|
2117
|
+
}
|
|
2118
|
+
|
|
2119
|
+
// ../dom/src/server-adapter.ts
|
|
2120
|
+
function asServer(node) {
|
|
2121
|
+
return node;
|
|
2122
|
+
}
|
|
2123
|
+
function asParent(node) {
|
|
2124
|
+
return node;
|
|
2125
|
+
}
|
|
2126
|
+
var ServerDOMAdapter = class {
|
|
2127
|
+
createElement(tag, _ns) {
|
|
2128
|
+
return new ServerElement(tag);
|
|
2129
|
+
}
|
|
2130
|
+
createTextNode(data) {
|
|
2131
|
+
return new ServerText(data);
|
|
2132
|
+
}
|
|
2133
|
+
createComment(data) {
|
|
2134
|
+
return new ServerComment(data);
|
|
2135
|
+
}
|
|
2136
|
+
createFragment() {
|
|
2137
|
+
return new ServerFragment();
|
|
2138
|
+
}
|
|
2139
|
+
appendChild(parent, child) {
|
|
2140
|
+
const p = asParent(parent);
|
|
2141
|
+
const c = asServer(child);
|
|
2142
|
+
this._detach(c);
|
|
2143
|
+
c.parent = p;
|
|
2144
|
+
p.children.push(c);
|
|
2145
|
+
}
|
|
2146
|
+
insertBefore(parent, child, reference) {
|
|
2147
|
+
const p = asParent(parent);
|
|
2148
|
+
const c = asServer(child);
|
|
2149
|
+
this._detach(c);
|
|
2150
|
+
c.parent = p;
|
|
2151
|
+
if (reference === null) {
|
|
2152
|
+
p.children.push(c);
|
|
2153
|
+
return;
|
|
2154
|
+
}
|
|
2155
|
+
const ref = asServer(reference);
|
|
2156
|
+
const idx = p.children.indexOf(ref);
|
|
2157
|
+
if (idx === -1) p.children.push(c);
|
|
2158
|
+
else p.children.splice(idx, 0, c);
|
|
2159
|
+
}
|
|
2160
|
+
removeChild(parent, child) {
|
|
2161
|
+
const p = asParent(parent);
|
|
2162
|
+
const c = asServer(child);
|
|
2163
|
+
const idx = p.children.indexOf(c);
|
|
2164
|
+
if (idx !== -1) {
|
|
2165
|
+
p.children.splice(idx, 1);
|
|
2166
|
+
c.parent = null;
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2169
|
+
replaceChild(parent, newChild, oldChild) {
|
|
2170
|
+
const p = asParent(parent);
|
|
2171
|
+
const nc = asServer(newChild);
|
|
2172
|
+
const oc = asServer(oldChild);
|
|
2173
|
+
const idx = p.children.indexOf(oc);
|
|
2174
|
+
if (idx === -1) return;
|
|
2175
|
+
this._detach(nc);
|
|
2176
|
+
nc.parent = p;
|
|
2177
|
+
p.children.splice(idx, 1, nc);
|
|
2178
|
+
oc.parent = null;
|
|
2179
|
+
}
|
|
2180
|
+
_detach(node) {
|
|
2181
|
+
if (node.parent !== null) {
|
|
2182
|
+
const siblings = node.parent.children;
|
|
2183
|
+
const idx = siblings.indexOf(node);
|
|
2184
|
+
if (idx !== -1) siblings.splice(idx, 1);
|
|
2185
|
+
node.parent = null;
|
|
2186
|
+
}
|
|
2187
|
+
}
|
|
2188
|
+
setAttribute(element, name, value) {
|
|
2189
|
+
element.attributes.set(name, value);
|
|
2190
|
+
}
|
|
2191
|
+
removeAttribute(element, name) {
|
|
2192
|
+
element.attributes.delete(name);
|
|
2193
|
+
}
|
|
2194
|
+
getAttribute(element, name) {
|
|
2195
|
+
return element.attributes.get(name) ?? null;
|
|
2196
|
+
}
|
|
2197
|
+
setProperty(element, name, value) {
|
|
2198
|
+
element.properties.set(name, value);
|
|
2199
|
+
}
|
|
2200
|
+
setTextContent(node, text) {
|
|
2201
|
+
const n = asServer(node);
|
|
2202
|
+
if (n.kind === "element" || n.kind === "fragment") {
|
|
2203
|
+
const el = n;
|
|
2204
|
+
el.children.length = 0;
|
|
2205
|
+
const t = new ServerText(text);
|
|
2206
|
+
t.parent = el;
|
|
2207
|
+
el.children.push(t);
|
|
2208
|
+
} else if (n.kind === "text") {
|
|
2209
|
+
n.data = text;
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
getTextContent(node) {
|
|
2213
|
+
const n = asServer(node);
|
|
2214
|
+
if (n.kind === "text") return n.data;
|
|
2215
|
+
if (n.kind === "element" || n.kind === "fragment") {
|
|
2216
|
+
let out = "";
|
|
2217
|
+
for (const c of n.children) {
|
|
2218
|
+
out += this.getTextContent(c) ?? "";
|
|
2219
|
+
}
|
|
2220
|
+
return out;
|
|
2221
|
+
}
|
|
2222
|
+
return null;
|
|
2223
|
+
}
|
|
2224
|
+
// Server nodes never dispatch events — listeners are a no-op on the server.
|
|
2225
|
+
addEventListener() {
|
|
2226
|
+
}
|
|
2227
|
+
removeEventListener() {
|
|
2228
|
+
}
|
|
2229
|
+
querySelector() {
|
|
2230
|
+
return null;
|
|
2231
|
+
}
|
|
2232
|
+
querySelectorAll() {
|
|
2233
|
+
return [];
|
|
2234
|
+
}
|
|
2235
|
+
getElementById() {
|
|
2236
|
+
return null;
|
|
2237
|
+
}
|
|
2238
|
+
focus() {
|
|
2239
|
+
}
|
|
2240
|
+
isElement(node) {
|
|
2241
|
+
return asServer(node).kind === "element";
|
|
2242
|
+
}
|
|
2243
|
+
isTextNode(node) {
|
|
2244
|
+
return asServer(node).kind === "text";
|
|
2245
|
+
}
|
|
2246
|
+
tagName(element) {
|
|
2247
|
+
return element.tagName;
|
|
2248
|
+
}
|
|
2249
|
+
parentNode(node) {
|
|
2250
|
+
return asServer(node).parent ?? null;
|
|
2251
|
+
}
|
|
2252
|
+
nextSibling(node) {
|
|
2253
|
+
const n = asServer(node);
|
|
2254
|
+
const parent = n.parent;
|
|
2255
|
+
if (parent === null) return null;
|
|
2256
|
+
const idx = parent.children.indexOf(n);
|
|
2257
|
+
if (idx === -1 || idx + 1 >= parent.children.length) return null;
|
|
2258
|
+
return parent.children[idx + 1];
|
|
2259
|
+
}
|
|
2260
|
+
firstChild(node) {
|
|
2261
|
+
const n = asServer(node);
|
|
2262
|
+
if (n.kind === "element" || n.kind === "fragment") {
|
|
2263
|
+
const el = n;
|
|
2264
|
+
return el.children[0] ?? null;
|
|
2265
|
+
}
|
|
2266
|
+
return null;
|
|
2267
|
+
}
|
|
2268
|
+
childNodes(node) {
|
|
2269
|
+
const n = asServer(node);
|
|
2270
|
+
if (n.kind === "element" || n.kind === "fragment") {
|
|
2271
|
+
return n.children;
|
|
2272
|
+
}
|
|
2273
|
+
return [];
|
|
2274
|
+
}
|
|
2275
|
+
// ── Server-only ────────────────────────────────────────────────────────────
|
|
2276
|
+
/** Serialize a node's children ("inner HTML") to an HTML string. */
|
|
2277
|
+
serializeInner(node) {
|
|
2278
|
+
const n = asServer(node);
|
|
2279
|
+
if (n.kind === "element" || n.kind === "fragment") {
|
|
2280
|
+
return serializeChildren(n);
|
|
2281
|
+
}
|
|
2282
|
+
return "";
|
|
2283
|
+
}
|
|
2284
|
+
/** Serialize a node (including itself) to an HTML string. */
|
|
2285
|
+
serializeOuter(node) {
|
|
2286
|
+
return serializeServerNode(asServer(node));
|
|
2287
|
+
}
|
|
2288
|
+
};
|
|
2289
|
+
var serverDOMAdapter = new ServerDOMAdapter();
|
|
2290
|
+
|
|
2291
|
+
// ../dom/src/focus.ts
|
|
2292
|
+
var FOCUSABLE_SELECTOR = 'a[href],button:not([disabled]),input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex="-1"])';
|
|
2293
|
+
function focusById(dom, root, id) {
|
|
2294
|
+
const el = dom.querySelector(root, `[id="${id}"]`);
|
|
2295
|
+
if (el === null) return false;
|
|
2296
|
+
dom.focus(el);
|
|
2297
|
+
return true;
|
|
2298
|
+
}
|
|
2299
|
+
function focusFirst(dom, container, selector = FOCUSABLE_SELECTOR) {
|
|
2300
|
+
const el = dom.querySelector(container, selector);
|
|
2301
|
+
if (el === null) return false;
|
|
2302
|
+
dom.focus(el);
|
|
2303
|
+
return true;
|
|
2304
|
+
}
|
|
2305
|
+
|
|
2306
|
+
// ../renderer/src/render-context.ts
|
|
2307
|
+
function createRenderContext(dom, graph, container, hydrationDiagnostics) {
|
|
2308
|
+
return {
|
|
2309
|
+
dom,
|
|
2310
|
+
graph,
|
|
2311
|
+
instances: /* @__PURE__ */ new Map(),
|
|
2312
|
+
container,
|
|
2313
|
+
...hydrationDiagnostics !== void 0 ? { hydrationDiagnostics } : {}
|
|
2314
|
+
};
|
|
2315
|
+
}
|
|
2316
|
+
|
|
2317
|
+
// ../renderer/src/node-instance.ts
|
|
2318
|
+
var NodeInstance = class {
|
|
2319
|
+
graphNode;
|
|
2320
|
+
/** The primary DOM node for this instance (element or text node). */
|
|
2321
|
+
domNode;
|
|
2322
|
+
children = [];
|
|
2323
|
+
cleanup = new CleanupRegistry();
|
|
2324
|
+
constructor(graphNode, domNode) {
|
|
2325
|
+
this.graphNode = graphNode;
|
|
2326
|
+
this.domNode = domNode;
|
|
2327
|
+
}
|
|
2328
|
+
addChild(child) {
|
|
2329
|
+
this.children.push(child);
|
|
2330
|
+
}
|
|
2331
|
+
/** Subscribe to a signal; auto-cleanup on unmount. */
|
|
2332
|
+
trackSignal(sig, handler) {
|
|
2333
|
+
const unsub = sig.subscribe(handler);
|
|
2334
|
+
this.cleanup.add(unsub);
|
|
2335
|
+
}
|
|
2336
|
+
/** Register a raw cleanup fn (DOM event removal, etc.). */
|
|
2337
|
+
trackCleanup(fn) {
|
|
2338
|
+
this.cleanup.add(fn);
|
|
2339
|
+
}
|
|
2340
|
+
dispose() {
|
|
2341
|
+
for (const child of this.children) {
|
|
2342
|
+
child.dispose();
|
|
2343
|
+
}
|
|
2344
|
+
this.cleanup.run();
|
|
2345
|
+
}
|
|
2346
|
+
};
|
|
2347
|
+
|
|
2348
|
+
// ../renderer/src/attributes.ts
|
|
2349
|
+
var DOM_PROPERTIES = /* @__PURE__ */ new Set([
|
|
2350
|
+
"value",
|
|
2351
|
+
"checked",
|
|
2352
|
+
"selected",
|
|
2353
|
+
"indeterminate",
|
|
2354
|
+
"innerHTML",
|
|
2355
|
+
"textContent",
|
|
2356
|
+
"innerText",
|
|
2357
|
+
"scrollTop",
|
|
2358
|
+
"scrollLeft"
|
|
2359
|
+
]);
|
|
2360
|
+
var BOOLEAN_ATTRS = /* @__PURE__ */ new Set([
|
|
2361
|
+
"disabled",
|
|
2362
|
+
"readonly",
|
|
2363
|
+
"required",
|
|
2364
|
+
"checked",
|
|
2365
|
+
"selected",
|
|
2366
|
+
"multiple",
|
|
2367
|
+
"autofocus",
|
|
2368
|
+
"autoplay",
|
|
2369
|
+
"controls",
|
|
2370
|
+
"default",
|
|
2371
|
+
"defer",
|
|
2372
|
+
"formnovalidate",
|
|
2373
|
+
"hidden",
|
|
2374
|
+
"ismap",
|
|
2375
|
+
"loop",
|
|
2376
|
+
"novalidate",
|
|
2377
|
+
"open",
|
|
2378
|
+
"reversed",
|
|
2379
|
+
"scoped",
|
|
2380
|
+
"seamless"
|
|
2381
|
+
]);
|
|
2382
|
+
function applyProp(dom, element, name, value) {
|
|
2383
|
+
if (name.startsWith("_")) return;
|
|
2384
|
+
if (name.startsWith("on")) return;
|
|
2385
|
+
if (DOM_PROPERTIES.has(name)) {
|
|
2386
|
+
dom.setProperty(element, name, value);
|
|
2387
|
+
return;
|
|
2388
|
+
}
|
|
2389
|
+
if (BOOLEAN_ATTRS.has(name)) {
|
|
2390
|
+
if (value === true || value === "" || value === name) {
|
|
2391
|
+
dom.setAttribute(element, name, "");
|
|
2392
|
+
} else {
|
|
2393
|
+
dom.removeAttribute(element, name);
|
|
2394
|
+
}
|
|
2395
|
+
return;
|
|
2396
|
+
}
|
|
2397
|
+
if (name === "class" || name === "className") {
|
|
2398
|
+
dom.setAttribute(element, "class", String(value ?? ""));
|
|
2399
|
+
return;
|
|
2400
|
+
}
|
|
2401
|
+
if (name === "style" && typeof value === "object" && value !== null) {
|
|
2402
|
+
const el = element;
|
|
2403
|
+
const styles = value;
|
|
2404
|
+
for (const [k, v] of Object.entries(styles)) {
|
|
2405
|
+
el.style.setProperty(k, v);
|
|
2406
|
+
}
|
|
2407
|
+
return;
|
|
2408
|
+
}
|
|
2409
|
+
if (value === null || value === void 0 || value === false) {
|
|
2410
|
+
dom.removeAttribute(element, name);
|
|
2411
|
+
return;
|
|
2412
|
+
}
|
|
2413
|
+
dom.setAttribute(element, name, String(value));
|
|
2414
|
+
}
|
|
2415
|
+
function patchProp(dom, element, name, oldValue, newValue) {
|
|
2416
|
+
if (Object.is(oldValue, newValue)) return;
|
|
2417
|
+
applyProp(dom, element, name, newValue);
|
|
2418
|
+
}
|
|
2419
|
+
|
|
2420
|
+
// ../renderer/src/events.ts
|
|
2421
|
+
function wireEvents(dom, graph, node, element, instance) {
|
|
2422
|
+
for (const eventDesc of node.events) {
|
|
2423
|
+
const handler = graph.getHandler(eventDesc.handlerKey);
|
|
2424
|
+
if (handler === void 0) continue;
|
|
2425
|
+
const domListener = (domEvent) => {
|
|
2426
|
+
if (eventDesc.type === "input" || eventDesc.type === "change") {
|
|
2427
|
+
const input = domEvent.target;
|
|
2428
|
+
handler(input.value);
|
|
2429
|
+
} else if (eventDesc.type === "submit") {
|
|
2430
|
+
domEvent.preventDefault();
|
|
2431
|
+
handler(domEvent);
|
|
2432
|
+
} else {
|
|
2433
|
+
handler();
|
|
2434
|
+
}
|
|
2435
|
+
};
|
|
2436
|
+
dom.addEventListener(element, eventDesc.type, domListener);
|
|
2437
|
+
instance.trackCleanup(() => {
|
|
2438
|
+
dom.removeEventListener(element, eventDesc.type, domListener);
|
|
2439
|
+
});
|
|
2440
|
+
}
|
|
2441
|
+
}
|
|
2442
|
+
|
|
2443
|
+
// ../renderer/src/tag-map.ts
|
|
2444
|
+
var TAG_MAP = {
|
|
2445
|
+
application: "div",
|
|
2446
|
+
page: "div",
|
|
2447
|
+
section: "section",
|
|
2448
|
+
container: "div",
|
|
2449
|
+
heading: "h1",
|
|
2450
|
+
text: "span",
|
|
2451
|
+
button: "button",
|
|
2452
|
+
input: "input",
|
|
2453
|
+
form: "form",
|
|
2454
|
+
list: "ul",
|
|
2455
|
+
"list-item": "li",
|
|
2456
|
+
image: "img",
|
|
2457
|
+
link: "a",
|
|
2458
|
+
component: "div",
|
|
2459
|
+
slot: "div",
|
|
2460
|
+
fragment: "div",
|
|
2461
|
+
"reactive-list": "ul"
|
|
2462
|
+
};
|
|
2463
|
+
function resolveTag(type) {
|
|
2464
|
+
return TAG_MAP[type] ?? "div";
|
|
2465
|
+
}
|
|
2466
|
+
|
|
2467
|
+
// ../renderer/src/patch.ts
|
|
2468
|
+
function patchNode(ctx, graphNode, propKey, newValue) {
|
|
2469
|
+
const instance = ctx.instances.get(graphNode.id);
|
|
2470
|
+
if (instance === void 0) return;
|
|
2471
|
+
const domNode = instance.domNode;
|
|
2472
|
+
if (!ctx.dom.isElement(domNode)) return;
|
|
2473
|
+
const oldValue = graphNode.getProp(propKey);
|
|
2474
|
+
switch (propKey) {
|
|
2475
|
+
case "text":
|
|
2476
|
+
if (!Object.is(oldValue, newValue)) {
|
|
2477
|
+
ctx.dom.setTextContent(domNode, String(newValue ?? ""));
|
|
2478
|
+
graphNode.setProp("text", String(newValue ?? ""));
|
|
2479
|
+
}
|
|
2480
|
+
break;
|
|
2481
|
+
case "label":
|
|
2482
|
+
if (!Object.is(oldValue, newValue)) {
|
|
2483
|
+
ctx.dom.setTextContent(domNode, String(newValue ?? ""));
|
|
2484
|
+
graphNode.setProp("label", String(newValue ?? ""));
|
|
2485
|
+
}
|
|
2486
|
+
break;
|
|
2487
|
+
case "disabled":
|
|
2488
|
+
if (newValue === true) {
|
|
2489
|
+
ctx.dom.setAttribute(domNode, "disabled", "");
|
|
2490
|
+
} else {
|
|
2491
|
+
ctx.dom.removeAttribute(domNode, "disabled");
|
|
2492
|
+
}
|
|
2493
|
+
graphNode.setProp("disabled", Boolean(newValue));
|
|
2494
|
+
break;
|
|
2495
|
+
case "value":
|
|
2496
|
+
if (!Object.is(oldValue, newValue)) {
|
|
2497
|
+
ctx.dom.setProperty(domNode, "value", String(newValue ?? ""));
|
|
2498
|
+
graphNode.setProp("value", String(newValue ?? ""));
|
|
2499
|
+
}
|
|
2500
|
+
break;
|
|
2501
|
+
default:
|
|
2502
|
+
patchProp(ctx.dom, domNode, propKey, oldValue, newValue);
|
|
2503
|
+
graphNode.setProp(propKey, newValue);
|
|
2504
|
+
break;
|
|
2505
|
+
}
|
|
2506
|
+
}
|
|
2507
|
+
|
|
2508
|
+
// ../renderer/src/reconciliation.ts
|
|
2509
|
+
function reconcileChildren(ctx, parentDom, oldInstances, newNodes, mountFn) {
|
|
2510
|
+
const oldByKey = /* @__PURE__ */ new Map();
|
|
2511
|
+
for (const inst of oldInstances) {
|
|
2512
|
+
const key = inst.graphNode.key ?? inst.graphNode.id;
|
|
2513
|
+
oldByKey.set(key, inst);
|
|
2514
|
+
}
|
|
2515
|
+
const newInstances = [];
|
|
2516
|
+
const usedKeys = /* @__PURE__ */ new Set();
|
|
2517
|
+
for (const newNode of newNodes) {
|
|
2518
|
+
const key = newNode.key ?? newNode.id;
|
|
2519
|
+
const existing = oldByKey.get(key);
|
|
2520
|
+
if (existing !== void 0) {
|
|
2521
|
+
usedKeys.add(key);
|
|
2522
|
+
const oldSig = existing.graphNode.getProp("_sig");
|
|
2523
|
+
const newSig = newNode.getProp("_sig");
|
|
2524
|
+
patchExistingInstance(ctx, existing, newNode);
|
|
2525
|
+
if (!Object.is(oldSig, newSig)) {
|
|
2526
|
+
reconcileItemChildren(ctx, existing, newNode, mountFn);
|
|
2527
|
+
}
|
|
2528
|
+
newInstances.push(existing);
|
|
2529
|
+
} else {
|
|
2530
|
+
const inst = mountFn(newNode, parentDom);
|
|
2531
|
+
newInstances.push(inst);
|
|
2532
|
+
}
|
|
2533
|
+
}
|
|
2534
|
+
const removed = [];
|
|
2535
|
+
for (const inst of oldInstances) {
|
|
2536
|
+
const key = inst.graphNode.key ?? inst.graphNode.id;
|
|
2537
|
+
if (!usedKeys.has(key)) {
|
|
2538
|
+
removed.push(inst);
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2541
|
+
for (const inst of removed) {
|
|
2542
|
+
const parent = ctx.dom.parentNode(inst.domNode);
|
|
2543
|
+
if (parent !== null) {
|
|
2544
|
+
ctx.dom.removeChild(parent, inst.domNode);
|
|
2545
|
+
}
|
|
2546
|
+
inst.dispose();
|
|
2547
|
+
}
|
|
2548
|
+
reorderDom(ctx, parentDom, newInstances);
|
|
2549
|
+
return { instances: newInstances, removed };
|
|
2550
|
+
}
|
|
2551
|
+
function reconcileItemChildren(ctx, itemInstance, newItemNode, mountFn) {
|
|
2552
|
+
const el = itemInstance.domNode;
|
|
2553
|
+
if (!ctx.dom.isElement(el)) return;
|
|
2554
|
+
const oldChildren = [...itemInstance.children];
|
|
2555
|
+
const newChildNodes = [...newItemNode.children];
|
|
2556
|
+
const nextChildren = [];
|
|
2557
|
+
const kept = /* @__PURE__ */ new Set();
|
|
2558
|
+
for (let i = 0; i < newChildNodes.length; i++) {
|
|
2559
|
+
const newChild = newChildNodes[i];
|
|
2560
|
+
const oldChild = oldChildren[i];
|
|
2561
|
+
if (oldChild !== void 0 && oldChild.graphNode.type === newChild.type) {
|
|
2562
|
+
patchExistingInstance(ctx, oldChild, newChild);
|
|
2563
|
+
reconcileItemChildren(ctx, oldChild, newChild, mountFn);
|
|
2564
|
+
nextChildren.push(oldChild);
|
|
2565
|
+
kept.add(oldChild);
|
|
2566
|
+
} else {
|
|
2567
|
+
itemInstance.graphNode.appendChild(newChild);
|
|
2568
|
+
const inst = mountFn(newChild, el);
|
|
2569
|
+
nextChildren.push(inst);
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2572
|
+
for (const old of oldChildren) {
|
|
2573
|
+
if (kept.has(old)) continue;
|
|
2574
|
+
const parent = ctx.dom.parentNode(old.domNode);
|
|
2575
|
+
if (parent !== null) ctx.dom.removeChild(parent, old.domNode);
|
|
2576
|
+
old.dispose();
|
|
2577
|
+
forgetInstanceTree(ctx, old);
|
|
2578
|
+
ctx.graph.detachNode(old.graphNode);
|
|
2579
|
+
}
|
|
2580
|
+
reorderDom(ctx, el, nextChildren);
|
|
2581
|
+
itemInstance.children.length = 0;
|
|
2582
|
+
for (const c of nextChildren) itemInstance.children.push(c);
|
|
2583
|
+
for (const c of [...itemInstance.graphNode.children]) {
|
|
2584
|
+
itemInstance.graphNode.removeChild(c);
|
|
2585
|
+
}
|
|
2586
|
+
for (const c of nextChildren) itemInstance.graphNode.appendChild(c.graphNode);
|
|
2587
|
+
}
|
|
2588
|
+
function reorderDom(ctx, parentDom, instances) {
|
|
2589
|
+
let referenceNode = null;
|
|
2590
|
+
for (let i = instances.length - 1; i >= 0; i--) {
|
|
2591
|
+
const inst = instances[i];
|
|
2592
|
+
if (inst === void 0) continue;
|
|
2593
|
+
const domNode = inst.domNode;
|
|
2594
|
+
const currentNext = ctx.dom.nextSibling(domNode);
|
|
2595
|
+
if (currentNext !== referenceNode) {
|
|
2596
|
+
ctx.dom.insertBefore(parentDom, domNode, referenceNode);
|
|
2597
|
+
}
|
|
2598
|
+
referenceNode = domNode;
|
|
2599
|
+
}
|
|
2600
|
+
}
|
|
2601
|
+
function forgetInstanceTree(ctx, instance) {
|
|
2602
|
+
ctx.instances.delete(instance.graphNode.id);
|
|
2603
|
+
for (const child of instance.children) forgetInstanceTree(ctx, child);
|
|
2604
|
+
}
|
|
2605
|
+
function patchExistingInstance(ctx, instance, newNode) {
|
|
2606
|
+
const oldNode = instance.graphNode;
|
|
2607
|
+
for (const [key, newVal] of Object.entries(newNode.props)) {
|
|
2608
|
+
const oldVal = oldNode.getProp(key);
|
|
2609
|
+
if (!Object.is(oldVal, newVal)) {
|
|
2610
|
+
patchNode(ctx, instance.graphNode, key, newVal);
|
|
2611
|
+
}
|
|
2612
|
+
}
|
|
2613
|
+
}
|
|
2614
|
+
|
|
2615
|
+
// ../renderer/src/mount.ts
|
|
2616
|
+
var SKIP_PROP_KEYS = /* @__PURE__ */ new Set([
|
|
2617
|
+
"text",
|
|
2618
|
+
"label",
|
|
2619
|
+
"level",
|
|
2620
|
+
"inputType",
|
|
2621
|
+
"src",
|
|
2622
|
+
"alt",
|
|
2623
|
+
"href",
|
|
2624
|
+
"external",
|
|
2625
|
+
"value",
|
|
2626
|
+
"placeholder",
|
|
2627
|
+
"disabled",
|
|
2628
|
+
"_renderKey",
|
|
2629
|
+
"key",
|
|
2630
|
+
"name"
|
|
2631
|
+
]);
|
|
2632
|
+
function mountGraph(ctx) {
|
|
2633
|
+
return mountNode(ctx, ctx.graph.root, ctx.container);
|
|
2634
|
+
}
|
|
2635
|
+
function mountNode(ctx, graphNode, parentDom) {
|
|
2636
|
+
const { dom, graph } = ctx;
|
|
2637
|
+
if (graphNode.type === "application") {
|
|
2638
|
+
const instance2 = new NodeInstance(graphNode, parentDom);
|
|
2639
|
+
ctx.instances.set(graphNode.id, instance2);
|
|
2640
|
+
for (const child of graphNode.children) {
|
|
2641
|
+
const childInstance = mountNode(ctx, child, parentDom);
|
|
2642
|
+
instance2.addChild(childInstance);
|
|
2643
|
+
}
|
|
2644
|
+
return instance2;
|
|
2645
|
+
}
|
|
2646
|
+
if (graphNode.type === "text") {
|
|
2647
|
+
const text = String(graphNode.getProp("text") ?? "");
|
|
2648
|
+
const el2 = dom.createElement("span");
|
|
2649
|
+
const textNode = dom.createTextNode(text);
|
|
2650
|
+
dom.appendChild(el2, textNode);
|
|
2651
|
+
applyNodeProps(ctx, graphNode, el2);
|
|
2652
|
+
wireEvents(dom, graph, graphNode, el2, new NodeInstance(graphNode, el2));
|
|
2653
|
+
const instance2 = new NodeInstance(graphNode, el2);
|
|
2654
|
+
ctx.instances.set(graphNode.id, instance2);
|
|
2655
|
+
wireSignalBindings(ctx, graphNode, instance2, textUpdate(dom, el2, textNode));
|
|
2656
|
+
dom.appendChild(parentDom, el2);
|
|
2657
|
+
return instance2;
|
|
2658
|
+
}
|
|
2659
|
+
if (graphNode.type === "heading") {
|
|
2660
|
+
const level = graphNode.getProp("level") ?? 1;
|
|
2661
|
+
const tag2 = `h${level}`;
|
|
2662
|
+
const el2 = dom.createElement(tag2);
|
|
2663
|
+
const text = String(graphNode.getProp("text") ?? "");
|
|
2664
|
+
dom.setTextContent(el2, text);
|
|
2665
|
+
applyNodeProps(ctx, graphNode, el2);
|
|
2666
|
+
const instance2 = new NodeInstance(graphNode, el2);
|
|
2667
|
+
ctx.instances.set(graphNode.id, instance2);
|
|
2668
|
+
wireEvents(dom, graph, graphNode, el2, instance2);
|
|
2669
|
+
wireSignalBindings(ctx, graphNode, instance2, headingUpdate(dom, el2));
|
|
2670
|
+
dom.appendChild(parentDom, el2);
|
|
2671
|
+
return instance2;
|
|
2672
|
+
}
|
|
2673
|
+
if (graphNode.type === "input") {
|
|
2674
|
+
const el2 = dom.createElement("input");
|
|
2675
|
+
const inputType = String(graphNode.getProp("inputType") ?? "text");
|
|
2676
|
+
dom.setAttribute(el2, "type", inputType);
|
|
2677
|
+
const placeholder = graphNode.getProp("placeholder");
|
|
2678
|
+
if (placeholder !== void 0) dom.setAttribute(el2, "placeholder", String(placeholder));
|
|
2679
|
+
const value = graphNode.getProp("value");
|
|
2680
|
+
if (value !== void 0) dom.setProperty(el2, "value", String(value));
|
|
2681
|
+
applyNodeProps(ctx, graphNode, el2);
|
|
2682
|
+
const instance2 = new NodeInstance(graphNode, el2);
|
|
2683
|
+
ctx.instances.set(graphNode.id, instance2);
|
|
2684
|
+
wireEvents(dom, graph, graphNode, el2, instance2);
|
|
2685
|
+
wireSignalBindings(ctx, graphNode, instance2, inputUpdate(dom, el2));
|
|
2686
|
+
dom.appendChild(parentDom, el2);
|
|
2687
|
+
return instance2;
|
|
2688
|
+
}
|
|
2689
|
+
if (graphNode.type === "image") {
|
|
2690
|
+
const el2 = dom.createElement("img");
|
|
2691
|
+
const src = graphNode.getProp("src");
|
|
2692
|
+
const alt = graphNode.getProp("alt");
|
|
2693
|
+
if (src !== void 0) dom.setAttribute(el2, "src", String(src));
|
|
2694
|
+
if (alt !== void 0) dom.setAttribute(el2, "alt", String(alt));
|
|
2695
|
+
const width = graphNode.getProp("width");
|
|
2696
|
+
const height = graphNode.getProp("height");
|
|
2697
|
+
if (width !== void 0) dom.setAttribute(el2, "width", String(width));
|
|
2698
|
+
if (height !== void 0) dom.setAttribute(el2, "height", String(height));
|
|
2699
|
+
applyNodeProps(ctx, graphNode, el2);
|
|
2700
|
+
const instance2 = new NodeInstance(graphNode, el2);
|
|
2701
|
+
ctx.instances.set(graphNode.id, instance2);
|
|
2702
|
+
dom.appendChild(parentDom, el2);
|
|
2703
|
+
return instance2;
|
|
2704
|
+
}
|
|
2705
|
+
if (graphNode.type === "link") {
|
|
2706
|
+
const el2 = dom.createElement("a");
|
|
2707
|
+
const href = graphNode.getProp("href");
|
|
2708
|
+
const label = graphNode.getProp("label");
|
|
2709
|
+
const external = graphNode.getProp("external");
|
|
2710
|
+
if (href !== void 0) dom.setAttribute(el2, "href", String(href));
|
|
2711
|
+
if (label !== void 0) dom.setTextContent(el2, String(label));
|
|
2712
|
+
if (external === true) {
|
|
2713
|
+
dom.setAttribute(el2, "target", "_blank");
|
|
2714
|
+
dom.setAttribute(el2, "rel", "noopener noreferrer");
|
|
2715
|
+
}
|
|
2716
|
+
applyNodeProps(ctx, graphNode, el2);
|
|
2717
|
+
const instance2 = new NodeInstance(graphNode, el2);
|
|
2718
|
+
ctx.instances.set(graphNode.id, instance2);
|
|
2719
|
+
wireEvents(dom, graph, graphNode, el2, instance2);
|
|
2720
|
+
dom.appendChild(parentDom, el2);
|
|
2721
|
+
return instance2;
|
|
2722
|
+
}
|
|
2723
|
+
if (graphNode.type === "button") {
|
|
2724
|
+
const el2 = dom.createElement("button");
|
|
2725
|
+
const label = graphNode.getProp("label");
|
|
2726
|
+
if (label !== void 0) dom.setTextContent(el2, String(label));
|
|
2727
|
+
const disabled = graphNode.getProp("disabled");
|
|
2728
|
+
if (disabled === true) dom.setAttribute(el2, "disabled", "");
|
|
2729
|
+
applyNodeProps(ctx, graphNode, el2);
|
|
2730
|
+
const instance2 = new NodeInstance(graphNode, el2);
|
|
2731
|
+
ctx.instances.set(graphNode.id, instance2);
|
|
2732
|
+
wireEvents(dom, graph, graphNode, el2, instance2);
|
|
2733
|
+
wireSignalBindings(ctx, graphNode, instance2, buttonUpdate(dom, el2));
|
|
2734
|
+
dom.appendChild(parentDom, el2);
|
|
2735
|
+
return instance2;
|
|
2736
|
+
}
|
|
2737
|
+
if (graphNode.type === "reactive-list" || graphNode.type === "conditional") {
|
|
2738
|
+
const tag2 = resolveTag(graphNode.type);
|
|
2739
|
+
const el2 = dom.createElement(tag2);
|
|
2740
|
+
applyNodeProps(ctx, graphNode, el2);
|
|
2741
|
+
const instance2 = new NodeInstance(graphNode, el2);
|
|
2742
|
+
ctx.instances.set(graphNode.id, instance2);
|
|
2743
|
+
for (const child of graphNode.children) {
|
|
2744
|
+
const childInstance = mountNode(ctx, child, el2);
|
|
2745
|
+
instance2.addChild(childInstance);
|
|
2746
|
+
}
|
|
2747
|
+
dom.appendChild(parentDom, el2);
|
|
2748
|
+
wireReactiveList(ctx, graphNode, instance2, el2);
|
|
2749
|
+
return instance2;
|
|
2750
|
+
}
|
|
2751
|
+
const tag = resolveTag(graphNode.type);
|
|
2752
|
+
const el = dom.createElement(tag);
|
|
2753
|
+
applyNodeProps(ctx, graphNode, el);
|
|
2754
|
+
if (graphNode.type === "list-item") {
|
|
2755
|
+
const itemKey = graphNode.getProp("key");
|
|
2756
|
+
if (itemKey !== void 0) {
|
|
2757
|
+
dom.setAttribute(el, "data-streetui-key", String(itemKey));
|
|
2758
|
+
}
|
|
2759
|
+
}
|
|
2760
|
+
if (graphNode.type === "form") {
|
|
2761
|
+
wireEvents(dom, graph, graphNode, el, new NodeInstance(graphNode, el));
|
|
2762
|
+
}
|
|
2763
|
+
const instance = new NodeInstance(graphNode, el);
|
|
2764
|
+
ctx.instances.set(graphNode.id, instance);
|
|
2765
|
+
for (const child of graphNode.children) {
|
|
2766
|
+
const childInstance = mountNode(ctx, child, el);
|
|
2767
|
+
instance.addChild(childInstance);
|
|
2768
|
+
}
|
|
2769
|
+
dom.appendChild(parentDom, el);
|
|
2770
|
+
return instance;
|
|
2771
|
+
}
|
|
2772
|
+
function textUpdate(dom, el, textNode) {
|
|
2773
|
+
return (propKey, value) => {
|
|
2774
|
+
if (propKey === "text") {
|
|
2775
|
+
dom.setTextContent(textNode, String(value ?? ""));
|
|
2776
|
+
} else {
|
|
2777
|
+
applyProp(dom, el, propKey, value);
|
|
2778
|
+
}
|
|
2779
|
+
};
|
|
2780
|
+
}
|
|
2781
|
+
function headingUpdate(dom, el) {
|
|
2782
|
+
return (propKey, value) => {
|
|
2783
|
+
if (propKey === "text") {
|
|
2784
|
+
dom.setTextContent(el, String(value ?? ""));
|
|
2785
|
+
} else {
|
|
2786
|
+
applyProp(dom, el, propKey, value);
|
|
2787
|
+
}
|
|
2788
|
+
};
|
|
2789
|
+
}
|
|
2790
|
+
function inputUpdate(dom, el) {
|
|
2791
|
+
return (propKey, value) => {
|
|
2792
|
+
if (propKey === "value") {
|
|
2793
|
+
dom.setProperty(el, "value", String(value ?? ""));
|
|
2794
|
+
} else {
|
|
2795
|
+
applyProp(dom, el, propKey, value);
|
|
2796
|
+
}
|
|
2797
|
+
};
|
|
2798
|
+
}
|
|
2799
|
+
function buttonUpdate(dom, el) {
|
|
2800
|
+
return (propKey, value) => {
|
|
2801
|
+
if (propKey === "label") {
|
|
2802
|
+
dom.setTextContent(el, String(value ?? ""));
|
|
2803
|
+
} else if (propKey === "disabled") {
|
|
2804
|
+
if (value === true) {
|
|
2805
|
+
dom.setAttribute(el, "disabled", "");
|
|
2806
|
+
} else {
|
|
2807
|
+
dom.removeAttribute(el, "disabled");
|
|
2808
|
+
}
|
|
2809
|
+
} else {
|
|
2810
|
+
applyProp(dom, el, propKey, value);
|
|
2811
|
+
}
|
|
2812
|
+
};
|
|
2813
|
+
}
|
|
2814
|
+
function applyNodeProps(ctx, graphNode, el) {
|
|
2815
|
+
for (const [key, value] of Object.entries(graphNode.props)) {
|
|
2816
|
+
if (SKIP_PROP_KEYS.has(key)) continue;
|
|
2817
|
+
applyProp(ctx.dom, el, key, value);
|
|
2818
|
+
}
|
|
2819
|
+
}
|
|
2820
|
+
function wireSignalBindings(ctx, graphNode, instance, onUpdate) {
|
|
2821
|
+
for (const stateRef of graphNode.stateRefs) {
|
|
2822
|
+
const signalKey = `__signal__${stateRef.signalId}`;
|
|
2823
|
+
const maybeSig = ctx.graph.getHandler(signalKey);
|
|
2824
|
+
if (maybeSig === void 0 || typeof maybeSig.subscribe !== "function") continue;
|
|
2825
|
+
const unsub = maybeSig.subscribe((value) => {
|
|
2826
|
+
onUpdate(stateRef.propKey, value);
|
|
2827
|
+
});
|
|
2828
|
+
instance.trackCleanup(unsub);
|
|
2829
|
+
}
|
|
2830
|
+
}
|
|
2831
|
+
function wireReactiveList(ctx, graphNode, instance, el) {
|
|
2832
|
+
const build = ctx.graph.getHandler(`__listbuild__${graphNode.id}`);
|
|
2833
|
+
if (build === void 0) return;
|
|
2834
|
+
for (const stateRef of graphNode.stateRefs) {
|
|
2835
|
+
if (stateRef.propKey !== "items") continue;
|
|
2836
|
+
const sig = ctx.graph.getHandler(`__signal__${stateRef.signalId}`);
|
|
2837
|
+
if (sig === void 0 || typeof sig.subscribe !== "function") continue;
|
|
2838
|
+
const unsub = sig.subscribe((value) => {
|
|
2839
|
+
reconcileReactiveList(ctx, graphNode, instance, el, build(value));
|
|
2840
|
+
});
|
|
2841
|
+
instance.trackCleanup(unsub);
|
|
2842
|
+
}
|
|
2843
|
+
}
|
|
2844
|
+
function reconcileReactiveList(ctx, listNode, listInstance, listEl, newNodes) {
|
|
2845
|
+
const oldInstances = [...listInstance.children];
|
|
2846
|
+
const result = reconcileChildren(
|
|
2847
|
+
ctx,
|
|
2848
|
+
listEl,
|
|
2849
|
+
oldInstances,
|
|
2850
|
+
newNodes,
|
|
2851
|
+
(node, parent) => mountNode(ctx, node, parent)
|
|
2852
|
+
);
|
|
2853
|
+
listInstance.children.length = 0;
|
|
2854
|
+
for (const inst of result.instances) listInstance.children.push(inst);
|
|
2855
|
+
for (const removed of result.removed) {
|
|
2856
|
+
forgetInstance(ctx, removed);
|
|
2857
|
+
ctx.graph.detachNode(removed.graphNode);
|
|
2858
|
+
}
|
|
2859
|
+
const adopted = new Set(result.instances.map((i) => i.graphNode));
|
|
2860
|
+
for (const built of newNodes) {
|
|
2861
|
+
if (!adopted.has(built)) ctx.graph.detachNode(built);
|
|
2862
|
+
}
|
|
2863
|
+
for (const child of [...listNode.children]) listNode.removeChild(child);
|
|
2864
|
+
for (const inst of result.instances) listNode.appendChild(inst.graphNode);
|
|
2865
|
+
}
|
|
2866
|
+
function forgetInstance(ctx, instance) {
|
|
2867
|
+
ctx.instances.delete(instance.graphNode.id);
|
|
2868
|
+
for (const child of instance.children) forgetInstance(ctx, child);
|
|
2869
|
+
}
|
|
2870
|
+
|
|
2871
|
+
// ../renderer/src/hydration-diagnostics.ts
|
|
2872
|
+
function formatHydrationDiagnostic(d) {
|
|
2873
|
+
const at = ` at ${d.path}`;
|
|
2874
|
+
switch (d.type) {
|
|
2875
|
+
case "tag-mismatch":
|
|
2876
|
+
return `Hydration mismatch${at} \u2014 Expected: ${d.expected} / Found: ${d.found} / Action: ${d.action}`;
|
|
2877
|
+
case "missing-element":
|
|
2878
|
+
return `Hydration mismatch${at} \u2014 Expected: ${d.expected} / Found: (nothing) / Action: ${d.action}`;
|
|
2879
|
+
case "surplus-element":
|
|
2880
|
+
return `Hydration mismatch${at} \u2014 Expected: (nothing) / Found: ${d.found} / Action: ${d.action}`;
|
|
2881
|
+
}
|
|
2882
|
+
}
|
|
2883
|
+
function createHydrationDiagnosticCollector() {
|
|
2884
|
+
const diagnostics = [];
|
|
2885
|
+
return {
|
|
2886
|
+
diagnostics,
|
|
2887
|
+
sink: {
|
|
2888
|
+
report(d) {
|
|
2889
|
+
diagnostics.push(d);
|
|
2890
|
+
}
|
|
2891
|
+
}
|
|
2892
|
+
};
|
|
2893
|
+
}
|
|
2894
|
+
function consoleHydrationDiagnosticSink(logger = console) {
|
|
2895
|
+
return {
|
|
2896
|
+
report(d) {
|
|
2897
|
+
logger.warn(d.message);
|
|
2898
|
+
}
|
|
2899
|
+
};
|
|
2900
|
+
}
|
|
2901
|
+
|
|
2902
|
+
// ../renderer/src/hydrate.ts
|
|
2903
|
+
function hydrateGraph(ctx) {
|
|
2904
|
+
const root = ctx.graph.root;
|
|
2905
|
+
const instance = new NodeInstance(root, ctx.container);
|
|
2906
|
+
ctx.instances.set(root.id, instance);
|
|
2907
|
+
hydrateChildren(ctx, root, instance, ctx.container, "app");
|
|
2908
|
+
return instance;
|
|
2909
|
+
}
|
|
2910
|
+
function hydrateNode(ctx, graphNode, domNode, path) {
|
|
2911
|
+
const { dom, graph } = ctx;
|
|
2912
|
+
switch (graphNode.type) {
|
|
2913
|
+
case "text": {
|
|
2914
|
+
let textNode = dom.firstChild(domNode);
|
|
2915
|
+
if (textNode === null || !dom.isTextNode(textNode)) {
|
|
2916
|
+
const created = dom.createTextNode(String(graphNode.getProp("text") ?? ""));
|
|
2917
|
+
dom.appendChild(domNode, created);
|
|
2918
|
+
textNode = created;
|
|
2919
|
+
}
|
|
2920
|
+
const instance = new NodeInstance(graphNode, domNode);
|
|
2921
|
+
ctx.instances.set(graphNode.id, instance);
|
|
2922
|
+
wireEvents(dom, graph, graphNode, domNode, instance);
|
|
2923
|
+
wireSignalBindings(ctx, graphNode, instance, textUpdate(dom, domNode, textNode));
|
|
2924
|
+
return instance;
|
|
2925
|
+
}
|
|
2926
|
+
case "heading": {
|
|
2927
|
+
const instance = new NodeInstance(graphNode, domNode);
|
|
2928
|
+
ctx.instances.set(graphNode.id, instance);
|
|
2929
|
+
wireEvents(dom, graph, graphNode, domNode, instance);
|
|
2930
|
+
wireSignalBindings(ctx, graphNode, instance, headingUpdate(dom, domNode));
|
|
2931
|
+
return instance;
|
|
2932
|
+
}
|
|
2933
|
+
case "input": {
|
|
2934
|
+
const instance = new NodeInstance(graphNode, domNode);
|
|
2935
|
+
ctx.instances.set(graphNode.id, instance);
|
|
2936
|
+
const value = graphNode.getProp("value");
|
|
2937
|
+
if (value !== void 0) dom.setProperty(domNode, "value", String(value));
|
|
2938
|
+
wireEvents(dom, graph, graphNode, domNode, instance);
|
|
2939
|
+
wireSignalBindings(ctx, graphNode, instance, inputUpdate(dom, domNode));
|
|
2940
|
+
return instance;
|
|
2941
|
+
}
|
|
2942
|
+
case "button": {
|
|
2943
|
+
const instance = new NodeInstance(graphNode, domNode);
|
|
2944
|
+
ctx.instances.set(graphNode.id, instance);
|
|
2945
|
+
wireEvents(dom, graph, graphNode, domNode, instance);
|
|
2946
|
+
wireSignalBindings(ctx, graphNode, instance, buttonUpdate(dom, domNode));
|
|
2947
|
+
return instance;
|
|
2948
|
+
}
|
|
2949
|
+
case "image":
|
|
2950
|
+
case "link": {
|
|
2951
|
+
const instance = new NodeInstance(graphNode, domNode);
|
|
2952
|
+
ctx.instances.set(graphNode.id, instance);
|
|
2953
|
+
if (graphNode.type === "link") wireEvents(dom, graph, graphNode, domNode, instance);
|
|
2954
|
+
return instance;
|
|
2955
|
+
}
|
|
2956
|
+
case "reactive-list":
|
|
2957
|
+
case "conditional": {
|
|
2958
|
+
const instance = new NodeInstance(graphNode, domNode);
|
|
2959
|
+
ctx.instances.set(graphNode.id, instance);
|
|
2960
|
+
hydrateChildren(ctx, graphNode, instance, domNode, path);
|
|
2961
|
+
wireReactiveList(ctx, graphNode, instance, domNode);
|
|
2962
|
+
return instance;
|
|
2963
|
+
}
|
|
2964
|
+
default: {
|
|
2965
|
+
const instance = new NodeInstance(graphNode, domNode);
|
|
2966
|
+
ctx.instances.set(graphNode.id, instance);
|
|
2967
|
+
if (graphNode.type === "form") {
|
|
2968
|
+
wireEvents(dom, graph, graphNode, domNode, instance);
|
|
2969
|
+
}
|
|
2970
|
+
if (graphNode.getProp("_hydrationBoundary") === true) {
|
|
2971
|
+
return instance;
|
|
2972
|
+
}
|
|
2973
|
+
hydrateChildren(ctx, graphNode, instance, domNode, path);
|
|
2974
|
+
return instance;
|
|
2975
|
+
}
|
|
2976
|
+
}
|
|
2977
|
+
}
|
|
2978
|
+
function hydrateChildren(ctx, parentGraphNode, parentInstance, parentDom, parentPath) {
|
|
2979
|
+
const expected = parentGraphNode.children;
|
|
2980
|
+
const actual = elementChildren(ctx, parentDom);
|
|
2981
|
+
let cursor = 0;
|
|
2982
|
+
for (let i = 0; i < expected.length; i++) {
|
|
2983
|
+
const childNode = expected[i];
|
|
2984
|
+
const want = expectedTag(ctx, childNode);
|
|
2985
|
+
const childPath = `${parentPath} / ${childNode.type}[${i}]`;
|
|
2986
|
+
const actualEl = actual[cursor];
|
|
2987
|
+
if (actualEl !== void 0 && ctx.dom.isElement(actualEl) && ctx.dom.tagName(actualEl) === want) {
|
|
2988
|
+
const inst = hydrateNode(ctx, childNode, actualEl, childPath);
|
|
2989
|
+
parentInstance.addChild(inst);
|
|
2990
|
+
cursor++;
|
|
2991
|
+
} else {
|
|
2992
|
+
const ref = actualEl ?? null;
|
|
2993
|
+
const inst = mountFreshAt(ctx, childNode, parentDom, ref);
|
|
2994
|
+
parentInstance.addChild(inst);
|
|
2995
|
+
if (actualEl !== void 0) {
|
|
2996
|
+
const found = ctx.dom.isElement(actualEl) ? ctx.dom.tagName(actualEl) : null;
|
|
2997
|
+
reportHydrationDiagnostic(ctx, {
|
|
2998
|
+
type: "tag-mismatch",
|
|
2999
|
+
expected: want,
|
|
3000
|
+
found,
|
|
3001
|
+
path: childPath,
|
|
3002
|
+
nodeId: childNode.id,
|
|
3003
|
+
nodeType: childNode.type,
|
|
3004
|
+
action: "mounted fresh subtree in place"
|
|
3005
|
+
});
|
|
3006
|
+
ctx.dom.removeChild(parentDom, actualEl);
|
|
3007
|
+
cursor++;
|
|
3008
|
+
} else {
|
|
3009
|
+
reportHydrationDiagnostic(ctx, {
|
|
3010
|
+
type: "missing-element",
|
|
3011
|
+
expected: want,
|
|
3012
|
+
found: null,
|
|
3013
|
+
path: childPath,
|
|
3014
|
+
nodeId: childNode.id,
|
|
3015
|
+
nodeType: childNode.type,
|
|
3016
|
+
action: "mounted fresh subtree"
|
|
3017
|
+
});
|
|
3018
|
+
}
|
|
3019
|
+
}
|
|
3020
|
+
}
|
|
3021
|
+
for (let i = cursor; i < actual.length; i++) {
|
|
3022
|
+
const surplus = actual[i];
|
|
3023
|
+
reportHydrationDiagnostic(ctx, {
|
|
3024
|
+
type: "surplus-element",
|
|
3025
|
+
expected: null,
|
|
3026
|
+
found: ctx.dom.isElement(surplus) ? ctx.dom.tagName(surplus) : null,
|
|
3027
|
+
path: `${parentPath} / [surplus ${i}]`,
|
|
3028
|
+
nodeId: null,
|
|
3029
|
+
nodeType: null,
|
|
3030
|
+
action: "removed surplus server element"
|
|
3031
|
+
});
|
|
3032
|
+
ctx.dom.removeChild(parentDom, surplus);
|
|
3033
|
+
}
|
|
3034
|
+
}
|
|
3035
|
+
function reportHydrationDiagnostic(ctx, d) {
|
|
3036
|
+
const sink = ctx.hydrationDiagnostics;
|
|
3037
|
+
if (sink === void 0) return;
|
|
3038
|
+
sink.report({ ...d, message: formatHydrationDiagnostic(d) });
|
|
3039
|
+
}
|
|
3040
|
+
function mountFreshAt(ctx, node, parentDom, ref) {
|
|
3041
|
+
const inst = mountNode(ctx, node, parentDom);
|
|
3042
|
+
if (ref !== null) {
|
|
3043
|
+
ctx.dom.insertBefore(parentDom, inst.domNode, ref);
|
|
3044
|
+
}
|
|
3045
|
+
return inst;
|
|
3046
|
+
}
|
|
3047
|
+
function elementChildren(ctx, parent) {
|
|
3048
|
+
const out = [];
|
|
3049
|
+
for (const node of ctx.dom.childNodes(parent)) {
|
|
3050
|
+
if (ctx.dom.isElement(node)) out.push(node);
|
|
3051
|
+
}
|
|
3052
|
+
return out;
|
|
3053
|
+
}
|
|
3054
|
+
function expectedTag(ctx, graphNode) {
|
|
3055
|
+
switch (graphNode.type) {
|
|
3056
|
+
case "text":
|
|
3057
|
+
return "span";
|
|
3058
|
+
case "heading": {
|
|
3059
|
+
const level = graphNode.getProp("level") ?? 1;
|
|
3060
|
+
return `h${level}`;
|
|
3061
|
+
}
|
|
3062
|
+
case "input":
|
|
3063
|
+
return "input";
|
|
3064
|
+
case "image":
|
|
3065
|
+
return "img";
|
|
3066
|
+
case "link":
|
|
3067
|
+
return "a";
|
|
3068
|
+
case "button":
|
|
3069
|
+
return "button";
|
|
3070
|
+
default:
|
|
3071
|
+
return resolveTag(graphNode.type);
|
|
3072
|
+
}
|
|
3073
|
+
}
|
|
3074
|
+
|
|
3075
|
+
// ../renderer/src/render-handle.ts
|
|
3076
|
+
var StreetRenderHandle = class {
|
|
3077
|
+
_disposed = false;
|
|
3078
|
+
_ctx;
|
|
3079
|
+
_rootInstance;
|
|
3080
|
+
constructor(ctx, rootInstance) {
|
|
3081
|
+
this._ctx = ctx;
|
|
3082
|
+
this._rootInstance = rootInstance;
|
|
3083
|
+
}
|
|
3084
|
+
flush() {
|
|
3085
|
+
if (this._disposed) return;
|
|
3086
|
+
}
|
|
3087
|
+
unmount() {
|
|
3088
|
+
if (this._disposed) return;
|
|
3089
|
+
this._disposed = true;
|
|
3090
|
+
this._rootInstance.dispose();
|
|
3091
|
+
const dom = this._ctx.dom;
|
|
3092
|
+
const container = this._ctx.container;
|
|
3093
|
+
for (const child of dom.childNodes(container)) {
|
|
3094
|
+
dom.removeChild(container, child);
|
|
3095
|
+
}
|
|
3096
|
+
this._ctx.instances.clear();
|
|
3097
|
+
}
|
|
3098
|
+
};
|
|
3099
|
+
|
|
3100
|
+
// ../renderer/src/renderer.ts
|
|
3101
|
+
var StreetRendererImpl = class {
|
|
3102
|
+
_dom;
|
|
3103
|
+
_hydrationDiagnostics;
|
|
3104
|
+
constructor(options = {}) {
|
|
3105
|
+
this._dom = options.domAdapter ?? new BrowserDOMAdapter();
|
|
3106
|
+
if (options.hydrationDiagnostics !== void 0) {
|
|
3107
|
+
this._hydrationDiagnostics = options.hydrationDiagnostics;
|
|
3108
|
+
}
|
|
3109
|
+
}
|
|
3110
|
+
mount(compiled, container) {
|
|
3111
|
+
const ctx = createRenderContext(this._dom, compiled.graph, container);
|
|
3112
|
+
const rootInstance = mountGraph(ctx);
|
|
3113
|
+
this._wireSignals(ctx, rootInstance);
|
|
3114
|
+
return new StreetRenderHandle(ctx, rootInstance);
|
|
3115
|
+
}
|
|
3116
|
+
/**
|
|
3117
|
+
* Hydrate a container that already holds server-rendered HTML for this
|
|
3118
|
+
* application. Instead of recreating the DOM, it walks the semantic graph
|
|
3119
|
+
* against the existing nodes, adopting matching elements and attaching
|
|
3120
|
+
* behavior (events + signal subscriptions). Mismatched subtrees are locally
|
|
3121
|
+
* replaced. Returns the same handle type as `mount`.
|
|
3122
|
+
*/
|
|
3123
|
+
hydrate(compiled, container) {
|
|
3124
|
+
const ctx = createRenderContext(
|
|
3125
|
+
this._dom,
|
|
3126
|
+
compiled.graph,
|
|
3127
|
+
container,
|
|
3128
|
+
this._hydrationDiagnostics
|
|
3129
|
+
);
|
|
3130
|
+
const rootInstance = hydrateGraph(ctx);
|
|
3131
|
+
this._wireSignals(ctx, rootInstance);
|
|
3132
|
+
return new StreetRenderHandle(ctx, rootInstance);
|
|
3133
|
+
}
|
|
3134
|
+
_wireSignals(ctx, rootInstance) {
|
|
3135
|
+
}
|
|
3136
|
+
};
|
|
3137
|
+
function createRenderer(options) {
|
|
3138
|
+
return new StreetRendererImpl(options);
|
|
3139
|
+
}
|
|
3140
|
+
|
|
3141
|
+
// ../renderer/src/dehydrate.ts
|
|
3142
|
+
var STATE_MARKER_ATTR = "data-streetui-state";
|
|
3143
|
+
function escapeForScript(json) {
|
|
3144
|
+
let out = "";
|
|
3145
|
+
for (const ch of json) {
|
|
3146
|
+
const code = ch.charCodeAt(0);
|
|
3147
|
+
if (ch === "<") out += "\\u003c";
|
|
3148
|
+
else if (ch === ">") out += "\\u003e";
|
|
3149
|
+
else if (ch === "&") out += "\\u0026";
|
|
3150
|
+
else if (code === 8232) out += "\\u2028";
|
|
3151
|
+
else if (code === 8233) out += "\\u2029";
|
|
3152
|
+
else out += ch;
|
|
3153
|
+
}
|
|
3154
|
+
return out;
|
|
3155
|
+
}
|
|
3156
|
+
function serializeState(state) {
|
|
3157
|
+
if (Object.keys(state).length === 0) return "";
|
|
3158
|
+
const json = escapeForScript(JSON.stringify(state));
|
|
3159
|
+
return `<script type="application/json" ${STATE_MARKER_ATTR}>${json}</script>`;
|
|
3160
|
+
}
|
|
3161
|
+
function readState(dom, root) {
|
|
3162
|
+
const el = dom.querySelector(root, `script[${STATE_MARKER_ATTR}]`);
|
|
3163
|
+
if (el === null) return {};
|
|
3164
|
+
const text = dom.getTextContent(el);
|
|
3165
|
+
if (text === null || text.length === 0) return {};
|
|
3166
|
+
try {
|
|
3167
|
+
const parsed = JSON.parse(text);
|
|
3168
|
+
if (parsed !== null && typeof parsed === "object") {
|
|
3169
|
+
return parsed;
|
|
3170
|
+
}
|
|
3171
|
+
return {};
|
|
3172
|
+
} catch {
|
|
3173
|
+
return {};
|
|
3174
|
+
}
|
|
3175
|
+
}
|
|
3176
|
+
|
|
3177
|
+
// ../renderer/src/ssr.ts
|
|
3178
|
+
function renderToString(compiled, options = {}) {
|
|
3179
|
+
const dom = options.domAdapter ?? new ServerDOMAdapter();
|
|
3180
|
+
const container = dom.createElement("div");
|
|
3181
|
+
const ctx = createRenderContext(dom, compiled.graph, container);
|
|
3182
|
+
const rootInstance = mountGraph(ctx);
|
|
3183
|
+
const html = dom.serializeInner(container);
|
|
3184
|
+
rootInstance.dispose();
|
|
3185
|
+
ctx.instances.clear();
|
|
3186
|
+
return html;
|
|
3187
|
+
}
|
|
3188
|
+
|
|
3189
|
+
// ../router/src/matching.ts
|
|
3190
|
+
function segments(path) {
|
|
3191
|
+
return path.split("/").filter((s) => s.length > 0);
|
|
3192
|
+
}
|
|
3193
|
+
function normalizePath(path) {
|
|
3194
|
+
let p = path.trim();
|
|
3195
|
+
if (p === "") return "/";
|
|
3196
|
+
if (!p.startsWith("/")) p = `/${p}`;
|
|
3197
|
+
if (p.length > 1 && p.endsWith("/")) p = p.slice(0, -1);
|
|
3198
|
+
return p;
|
|
3199
|
+
}
|
|
3200
|
+
function matchPattern(pattern2, pathname) {
|
|
3201
|
+
if (pattern2 === "*") {
|
|
3202
|
+
return { "*": normalizePath(pathname).slice(1) };
|
|
3203
|
+
}
|
|
3204
|
+
const patSegs = segments(pattern2);
|
|
3205
|
+
const pathSegs = segments(normalizePath(pathname));
|
|
3206
|
+
const params = {};
|
|
3207
|
+
for (let i = 0; i < patSegs.length; i++) {
|
|
3208
|
+
const patSeg = patSegs[i];
|
|
3209
|
+
if (patSeg === "*") {
|
|
3210
|
+
params["*"] = pathSegs.slice(i).map((s) => decodeURIComponent(s)).join("/");
|
|
3211
|
+
return params;
|
|
3212
|
+
}
|
|
3213
|
+
const pathSeg = pathSegs[i];
|
|
3214
|
+
if (pathSeg === void 0) return null;
|
|
3215
|
+
if (patSeg.startsWith(":")) {
|
|
3216
|
+
const name = patSeg.slice(1);
|
|
3217
|
+
if (name === "") return null;
|
|
3218
|
+
params[name] = decodeURIComponent(pathSeg);
|
|
3219
|
+
continue;
|
|
3220
|
+
}
|
|
3221
|
+
if (patSeg !== pathSeg) return null;
|
|
3222
|
+
}
|
|
3223
|
+
if (pathSegs.length !== patSegs.length) return null;
|
|
3224
|
+
return params;
|
|
3225
|
+
}
|
|
3226
|
+
function matchRoutes(routes, pathname) {
|
|
3227
|
+
for (const route of routes) {
|
|
3228
|
+
const params = matchPattern(route.path, pathname);
|
|
3229
|
+
if (params !== null) return { route, params };
|
|
3230
|
+
}
|
|
3231
|
+
return null;
|
|
3232
|
+
}
|
|
3233
|
+
function splitTarget(to) {
|
|
3234
|
+
const hashIndex = to.indexOf("#");
|
|
3235
|
+
const withoutHash = hashIndex >= 0 ? to.slice(0, hashIndex) : to;
|
|
3236
|
+
const qIndex = withoutHash.indexOf("?");
|
|
3237
|
+
if (qIndex < 0) return { pathname: normalizePath(withoutHash), search: "" };
|
|
3238
|
+
return {
|
|
3239
|
+
pathname: normalizePath(withoutHash.slice(0, qIndex)),
|
|
3240
|
+
search: withoutHash.slice(qIndex + 1)
|
|
3241
|
+
};
|
|
3242
|
+
}
|
|
3243
|
+
|
|
3244
|
+
// ../router/src/history.ts
|
|
3245
|
+
function buildLocation(pathname, search) {
|
|
3246
|
+
return { pathname, search };
|
|
3247
|
+
}
|
|
3248
|
+
function toUrl(pathname, search) {
|
|
3249
|
+
return search.length > 0 ? `${pathname}?${search}` : pathname;
|
|
3250
|
+
}
|
|
3251
|
+
function createBrowserHistory() {
|
|
3252
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
3253
|
+
const notify = () => {
|
|
3254
|
+
for (const cb of listeners) cb();
|
|
3255
|
+
};
|
|
3256
|
+
const onPopState = () => notify();
|
|
3257
|
+
window.addEventListener("popstate", onPopState);
|
|
3258
|
+
const current = () => {
|
|
3259
|
+
const loc = window.location;
|
|
3260
|
+
return buildLocation(loc.pathname, loc.search.replace(/^\?/, ""));
|
|
3261
|
+
};
|
|
3262
|
+
return {
|
|
3263
|
+
location: current,
|
|
3264
|
+
push(pathname, search) {
|
|
3265
|
+
window.history.pushState({}, "", toUrl(pathname, search));
|
|
3266
|
+
notify();
|
|
3267
|
+
},
|
|
3268
|
+
replace(pathname, search) {
|
|
3269
|
+
window.history.replaceState({}, "", toUrl(pathname, search));
|
|
3270
|
+
notify();
|
|
3271
|
+
},
|
|
3272
|
+
back() {
|
|
3273
|
+
window.history.back();
|
|
3274
|
+
},
|
|
3275
|
+
forward() {
|
|
3276
|
+
window.history.forward();
|
|
3277
|
+
},
|
|
3278
|
+
listen(cb) {
|
|
3279
|
+
listeners.add(cb);
|
|
3280
|
+
return () => listeners.delete(cb);
|
|
3281
|
+
},
|
|
3282
|
+
dispose() {
|
|
3283
|
+
window.removeEventListener("popstate", onPopState);
|
|
3284
|
+
listeners.clear();
|
|
3285
|
+
}
|
|
3286
|
+
};
|
|
3287
|
+
}
|
|
3288
|
+
function createMemoryHistory(initial = "/") {
|
|
3289
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
3290
|
+
const notify = () => {
|
|
3291
|
+
for (const cb of listeners) cb();
|
|
3292
|
+
};
|
|
3293
|
+
const parse = (entry) => {
|
|
3294
|
+
const qIndex = entry.indexOf("?");
|
|
3295
|
+
if (qIndex < 0) return buildLocation(entry, "");
|
|
3296
|
+
return buildLocation(entry.slice(0, qIndex), entry.slice(qIndex + 1));
|
|
3297
|
+
};
|
|
3298
|
+
const stack = [initial];
|
|
3299
|
+
let index = 0;
|
|
3300
|
+
return {
|
|
3301
|
+
location() {
|
|
3302
|
+
return parse(stack[index]);
|
|
3303
|
+
},
|
|
3304
|
+
push(pathname, search) {
|
|
3305
|
+
stack.splice(index + 1);
|
|
3306
|
+
stack.push(toUrl(pathname, search));
|
|
3307
|
+
index = stack.length - 1;
|
|
3308
|
+
notify();
|
|
3309
|
+
},
|
|
3310
|
+
replace(pathname, search) {
|
|
3311
|
+
stack[index] = toUrl(pathname, search);
|
|
3312
|
+
notify();
|
|
3313
|
+
},
|
|
3314
|
+
back() {
|
|
3315
|
+
if (index > 0) {
|
|
3316
|
+
index--;
|
|
3317
|
+
notify();
|
|
3318
|
+
}
|
|
3319
|
+
},
|
|
3320
|
+
forward() {
|
|
3321
|
+
if (index < stack.length - 1) {
|
|
3322
|
+
index++;
|
|
3323
|
+
notify();
|
|
3324
|
+
}
|
|
3325
|
+
},
|
|
3326
|
+
listen(cb) {
|
|
3327
|
+
listeners.add(cb);
|
|
3328
|
+
return () => listeners.delete(cb);
|
|
3329
|
+
},
|
|
3330
|
+
dispose() {
|
|
3331
|
+
listeners.clear();
|
|
3332
|
+
}
|
|
3333
|
+
};
|
|
3334
|
+
}
|
|
3335
|
+
|
|
3336
|
+
// ../router/src/router.ts
|
|
3337
|
+
var DEFAULT_NOT_FOUND = {
|
|
3338
|
+
path: "*",
|
|
3339
|
+
builder: (page) => {
|
|
3340
|
+
page.section("not-found", (s) => {
|
|
3341
|
+
s.heading("404 \u2014 Page not found", { level: 1, id: "not-found-title" });
|
|
3342
|
+
s.text("The page you were looking for does not exist.", { id: "not-found-text" });
|
|
3343
|
+
s.link("Go home", { href: "/", id: "not-found-home" });
|
|
3344
|
+
}, { id: "not-found" });
|
|
3345
|
+
}
|
|
3346
|
+
};
|
|
3347
|
+
function createRouter(options) {
|
|
3348
|
+
const routes = options.routes;
|
|
3349
|
+
const history = options.history ?? createBrowserHistory();
|
|
3350
|
+
const fallback = options.notFound ?? DEFAULT_NOT_FOUND;
|
|
3351
|
+
const resolve = () => {
|
|
3352
|
+
const loc = history.location();
|
|
3353
|
+
const pathname = normalizePath(loc.pathname);
|
|
3354
|
+
const query = new URLSearchParams(loc.search);
|
|
3355
|
+
const matched = matchRoutes(routes, pathname);
|
|
3356
|
+
if (matched !== null) {
|
|
3357
|
+
return {
|
|
3358
|
+
path: pathname,
|
|
3359
|
+
pattern: matched.route.path,
|
|
3360
|
+
params: matched.params,
|
|
3361
|
+
query,
|
|
3362
|
+
route: matched.route,
|
|
3363
|
+
// A catch-all `*` match is the 404 route whether user-supplied or built-in.
|
|
3364
|
+
isFallback: matched.route.path === "*"
|
|
3365
|
+
};
|
|
3366
|
+
}
|
|
3367
|
+
const fallbackParams = matchRoutes([fallback], pathname)?.params ?? {};
|
|
3368
|
+
return {
|
|
3369
|
+
path: pathname,
|
|
3370
|
+
pattern: fallback.path,
|
|
3371
|
+
params: fallbackParams,
|
|
3372
|
+
query,
|
|
3373
|
+
route: fallback,
|
|
3374
|
+
isFallback: true
|
|
3375
|
+
};
|
|
3376
|
+
};
|
|
3377
|
+
const current = signal(resolve());
|
|
3378
|
+
const stopListening = history.listen(() => {
|
|
3379
|
+
current.set(resolve());
|
|
3380
|
+
});
|
|
3381
|
+
const navigate = (to, opts = {}) => {
|
|
3382
|
+
const { pathname, search } = splitTarget(to);
|
|
3383
|
+
if (opts.replace === true) history.replace(pathname, search);
|
|
3384
|
+
else history.push(pathname, search);
|
|
3385
|
+
};
|
|
3386
|
+
const isActive = (path, opts = {}) => {
|
|
3387
|
+
const target = normalizePath(path);
|
|
3388
|
+
const exact = opts.exact === true;
|
|
3389
|
+
return derived(() => {
|
|
3390
|
+
const activePath = current.get().path;
|
|
3391
|
+
if (activePath === target) return true;
|
|
3392
|
+
if (exact || target === "/") return false;
|
|
3393
|
+
return activePath.startsWith(`${target}/`);
|
|
3394
|
+
});
|
|
3395
|
+
};
|
|
3396
|
+
return {
|
|
3397
|
+
currentRoute: current,
|
|
3398
|
+
navigate,
|
|
3399
|
+
back: () => history.back(),
|
|
3400
|
+
forward: () => history.forward(),
|
|
3401
|
+
isActive,
|
|
3402
|
+
destroy: () => {
|
|
3403
|
+
stopListening();
|
|
3404
|
+
history.dispose();
|
|
3405
|
+
}
|
|
3406
|
+
};
|
|
3407
|
+
}
|
|
3408
|
+
|
|
3409
|
+
// ../router/src/mount-router.ts
|
|
3410
|
+
var ROUTER_OUTLET_ID = "streetui-router-outlet";
|
|
3411
|
+
function routerOutlet(scope, id = ROUTER_OUTLET_ID) {
|
|
3412
|
+
scope.container("router-outlet", () => {
|
|
3413
|
+
}, { id });
|
|
3414
|
+
}
|
|
3415
|
+
function isExternalHref(href) {
|
|
3416
|
+
return /^[a-zA-Z][a-zA-Z\d+.-]*:/.test(href) || // scheme: http:, https:, mailto:, tel:
|
|
3417
|
+
href.startsWith("//");
|
|
3418
|
+
}
|
|
3419
|
+
function mountRouter(router, options) {
|
|
3420
|
+
const { container } = options;
|
|
3421
|
+
const renderer = options.renderer ?? createRenderer();
|
|
3422
|
+
const outletId = options.outletId ?? ROUTER_OUTLET_ID;
|
|
3423
|
+
const interceptLinks = options.interceptLinks ?? true;
|
|
3424
|
+
const hydrateMode = options.hydrate ?? false;
|
|
3425
|
+
let shellMounted = null;
|
|
3426
|
+
let outlet;
|
|
3427
|
+
if (options.shell !== void 0) {
|
|
3428
|
+
const shellApp = streetui.app({ name: "router-shell" });
|
|
3429
|
+
const shellBuilder = options.shell;
|
|
3430
|
+
shellApp.page("shell", (page) => shellBuilder(page, router));
|
|
3431
|
+
const shellRuntime = createRuntime({ renderer });
|
|
3432
|
+
const shellCompiled = compile(shellApp);
|
|
3433
|
+
if (hydrateMode) {
|
|
3434
|
+
for (const node of shellCompiled.graph.findAll((n) => n.getProp("id") === outletId)) {
|
|
3435
|
+
node.setProp("_hydrationBoundary", true);
|
|
3436
|
+
}
|
|
3437
|
+
}
|
|
3438
|
+
shellMounted = hydrateMode ? shellRuntime.hydrate(shellCompiled, container) : shellRuntime.mount(shellCompiled, container);
|
|
3439
|
+
const found = container.querySelector(`[id="${outletId}"]`);
|
|
3440
|
+
if (found === null) {
|
|
3441
|
+
throw new Error(
|
|
3442
|
+
`[Router] The shell must contain a route outlet. Call routerOutlet(scope) (or add a container with id="${outletId}") inside your shell builder.`
|
|
3443
|
+
);
|
|
3444
|
+
}
|
|
3445
|
+
outlet = found;
|
|
3446
|
+
} else {
|
|
3447
|
+
outlet = container;
|
|
3448
|
+
}
|
|
3449
|
+
let active = null;
|
|
3450
|
+
let firstRender = hydrateMode;
|
|
3451
|
+
const disposeActive = () => {
|
|
3452
|
+
if (active === null) return;
|
|
3453
|
+
active.registry.run();
|
|
3454
|
+
active.mounted.unmount();
|
|
3455
|
+
active = null;
|
|
3456
|
+
};
|
|
3457
|
+
const renderRoute = (match) => {
|
|
3458
|
+
disposeActive();
|
|
3459
|
+
const registry = new CleanupRegistry();
|
|
3460
|
+
const ctx = {
|
|
3461
|
+
path: match.path,
|
|
3462
|
+
pattern: match.pattern,
|
|
3463
|
+
params: match.params,
|
|
3464
|
+
query: match.query,
|
|
3465
|
+
onCleanup: (fn) => registry.add(fn)
|
|
3466
|
+
};
|
|
3467
|
+
const routeApp = streetui.app({ name: `route:${match.pattern}` });
|
|
3468
|
+
routeApp.page("route", (page) => match.route.builder(page, ctx));
|
|
3469
|
+
const runtime = createRuntime({ renderer });
|
|
3470
|
+
const routeCompiled = compile(routeApp);
|
|
3471
|
+
const mounted = firstRender ? runtime.hydrate(routeCompiled, outlet) : runtime.mount(routeCompiled, outlet);
|
|
3472
|
+
firstRender = false;
|
|
3473
|
+
active = { registry, mounted };
|
|
3474
|
+
};
|
|
3475
|
+
renderRoute(router.currentRoute.peek());
|
|
3476
|
+
const stopRouteSub = router.currentRoute.subscribe((match) => renderRoute(match));
|
|
3477
|
+
const onClick = (event) => {
|
|
3478
|
+
if (event.defaultPrevented) return;
|
|
3479
|
+
const mouse = event;
|
|
3480
|
+
if (typeof mouse.button === "number" && mouse.button !== 0) return;
|
|
3481
|
+
if (mouse.metaKey || mouse.ctrlKey || mouse.shiftKey || mouse.altKey) return;
|
|
3482
|
+
const target = event.target;
|
|
3483
|
+
const anchor = target?.closest?.("a") ?? null;
|
|
3484
|
+
if (anchor === null) return;
|
|
3485
|
+
const targetAttr = anchor.getAttribute("target");
|
|
3486
|
+
if (targetAttr !== null && targetAttr !== "_self") return;
|
|
3487
|
+
const href = anchor.getAttribute("href");
|
|
3488
|
+
if (href === null || href === "" || href.startsWith("#")) return;
|
|
3489
|
+
if (isExternalHref(href)) return;
|
|
3490
|
+
event.preventDefault();
|
|
3491
|
+
router.navigate(href);
|
|
3492
|
+
};
|
|
3493
|
+
if (interceptLinks) {
|
|
3494
|
+
container.addEventListener("click", onClick);
|
|
3495
|
+
}
|
|
3496
|
+
return {
|
|
3497
|
+
outlet,
|
|
3498
|
+
unmount() {
|
|
3499
|
+
if (interceptLinks) container.removeEventListener("click", onClick);
|
|
3500
|
+
stopRouteSub();
|
|
3501
|
+
disposeActive();
|
|
3502
|
+
shellMounted?.unmount();
|
|
3503
|
+
router.destroy();
|
|
3504
|
+
}
|
|
3505
|
+
};
|
|
3506
|
+
}
|
|
3507
|
+
|
|
3508
|
+
// ../forms/src/validators.ts
|
|
3509
|
+
function required(message = "This field is required") {
|
|
3510
|
+
return (value) => value.trim().length === 0 ? message : void 0;
|
|
3511
|
+
}
|
|
3512
|
+
function minLength(length, message) {
|
|
3513
|
+
return (value) => value.length < length ? message ?? `Must be at least ${length} characters` : void 0;
|
|
3514
|
+
}
|
|
3515
|
+
function maxLength(length, message) {
|
|
3516
|
+
return (value) => value.length > length ? message ?? `Must be at most ${length} characters` : void 0;
|
|
3517
|
+
}
|
|
3518
|
+
var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
3519
|
+
function email(message = "Enter a valid email address") {
|
|
3520
|
+
return (value) => value.length === 0 || EMAIL_RE.test(value) ? void 0 : message;
|
|
3521
|
+
}
|
|
3522
|
+
function pattern(regex, message = "Invalid format") {
|
|
3523
|
+
return (value) => value.length === 0 || regex.test(value) ? void 0 : message;
|
|
3524
|
+
}
|
|
3525
|
+
function runValidators(value, validators) {
|
|
3526
|
+
if (validators === void 0) return void 0;
|
|
3527
|
+
const list = Array.isArray(validators) ? validators : [validators];
|
|
3528
|
+
for (const validate of list) {
|
|
3529
|
+
const error = validate(value);
|
|
3530
|
+
if (error !== void 0) return error;
|
|
3531
|
+
}
|
|
3532
|
+
return void 0;
|
|
3533
|
+
}
|
|
3534
|
+
|
|
3535
|
+
// ../forms/src/form.ts
|
|
3536
|
+
function createForm(config) {
|
|
3537
|
+
const names = Object.keys(config.initialValues);
|
|
3538
|
+
const validators = config.validators ?? {};
|
|
3539
|
+
let programmatic = false;
|
|
3540
|
+
const fields = /* @__PURE__ */ new Map();
|
|
3541
|
+
for (const name of names) {
|
|
3542
|
+
const initial = config.initialValues[name];
|
|
3543
|
+
const value = signal(initial);
|
|
3544
|
+
const touched = signal(false);
|
|
3545
|
+
const error = derived(
|
|
3546
|
+
() => runValidators(value.get(), validators[name])
|
|
3547
|
+
);
|
|
3548
|
+
const valid2 = derived(() => error.get() === void 0);
|
|
3549
|
+
const dirty2 = derived(() => value.get() !== initial);
|
|
3550
|
+
const unsub = value.subscribe(() => {
|
|
3551
|
+
if (!programmatic) touched.set(true);
|
|
3552
|
+
});
|
|
3553
|
+
const api = {
|
|
3554
|
+
name,
|
|
3555
|
+
value,
|
|
3556
|
+
error,
|
|
3557
|
+
touched,
|
|
3558
|
+
dirty: dirty2,
|
|
3559
|
+
valid: valid2,
|
|
3560
|
+
setValue(next) {
|
|
3561
|
+
value.set(next);
|
|
3562
|
+
},
|
|
3563
|
+
markTouched(next = true) {
|
|
3564
|
+
touched.set(next);
|
|
3565
|
+
},
|
|
3566
|
+
reset() {
|
|
3567
|
+
programmatic = true;
|
|
3568
|
+
try {
|
|
3569
|
+
batch(() => {
|
|
3570
|
+
value.set(initial);
|
|
3571
|
+
touched.set(false);
|
|
3572
|
+
});
|
|
3573
|
+
} finally {
|
|
3574
|
+
programmatic = false;
|
|
3575
|
+
}
|
|
3576
|
+
}
|
|
3577
|
+
};
|
|
3578
|
+
fields.set(name, { api, value, touched, error, valid: valid2, dirty: dirty2, initial, unsub });
|
|
3579
|
+
}
|
|
3580
|
+
const field = (name) => {
|
|
3581
|
+
const f = fields.get(name);
|
|
3582
|
+
if (f === void 0) throw new Error(`Unknown form field: ${name}`);
|
|
3583
|
+
return f;
|
|
3584
|
+
};
|
|
3585
|
+
const values = derived(() => {
|
|
3586
|
+
const out = {};
|
|
3587
|
+
for (const name of names) out[name] = field(name).value.get();
|
|
3588
|
+
return out;
|
|
3589
|
+
});
|
|
3590
|
+
const errors = derived(() => {
|
|
3591
|
+
const out = {};
|
|
3592
|
+
for (const name of names) {
|
|
3593
|
+
const e = field(name).error.get();
|
|
3594
|
+
if (e !== void 0) out[name] = e;
|
|
3595
|
+
}
|
|
3596
|
+
return out;
|
|
3597
|
+
});
|
|
3598
|
+
const touchedMap = derived(() => {
|
|
3599
|
+
const out = {};
|
|
3600
|
+
for (const name of names) out[name] = field(name).touched.get();
|
|
3601
|
+
return out;
|
|
3602
|
+
});
|
|
3603
|
+
const dirty = derived(() => names.some((n) => field(n).dirty.get()));
|
|
3604
|
+
const valid = derived(() => names.every((n) => field(n).valid.get()));
|
|
3605
|
+
const status = signal("idle");
|
|
3606
|
+
const submitting = derived(() => status.get() === "submitting");
|
|
3607
|
+
const submitted = derived(() => status.get() === "success");
|
|
3608
|
+
const submitError = signal(void 0);
|
|
3609
|
+
function setValues(partial) {
|
|
3610
|
+
programmatic = true;
|
|
3611
|
+
try {
|
|
3612
|
+
batch(() => {
|
|
3613
|
+
for (const name of names) {
|
|
3614
|
+
const next = partial[name];
|
|
3615
|
+
if (next !== void 0) field(name).value.set(next);
|
|
3616
|
+
}
|
|
3617
|
+
});
|
|
3618
|
+
} finally {
|
|
3619
|
+
programmatic = false;
|
|
3620
|
+
}
|
|
3621
|
+
}
|
|
3622
|
+
function reset() {
|
|
3623
|
+
programmatic = true;
|
|
3624
|
+
try {
|
|
3625
|
+
batch(() => {
|
|
3626
|
+
for (const name of names) {
|
|
3627
|
+
const f = field(name);
|
|
3628
|
+
f.value.set(f.initial);
|
|
3629
|
+
f.touched.set(false);
|
|
3630
|
+
}
|
|
3631
|
+
status.set("idle");
|
|
3632
|
+
submitError.set(void 0);
|
|
3633
|
+
});
|
|
3634
|
+
} finally {
|
|
3635
|
+
programmatic = false;
|
|
3636
|
+
}
|
|
3637
|
+
}
|
|
3638
|
+
async function submit() {
|
|
3639
|
+
batch(() => {
|
|
3640
|
+
for (const name of names) field(name).touched.set(true);
|
|
3641
|
+
});
|
|
3642
|
+
if (!valid.peek()) {
|
|
3643
|
+
return;
|
|
3644
|
+
}
|
|
3645
|
+
submitError.set(void 0);
|
|
3646
|
+
status.set("submitting");
|
|
3647
|
+
try {
|
|
3648
|
+
await config.onSubmit?.(values.peek());
|
|
3649
|
+
status.set("success");
|
|
3650
|
+
} catch (err) {
|
|
3651
|
+
submitError.set(err);
|
|
3652
|
+
status.set("error");
|
|
3653
|
+
}
|
|
3654
|
+
}
|
|
3655
|
+
function dispose() {
|
|
3656
|
+
for (const f of fields.values()) {
|
|
3657
|
+
f.unsub();
|
|
3658
|
+
f.error.dispose();
|
|
3659
|
+
f.valid.dispose();
|
|
3660
|
+
f.dirty.dispose();
|
|
3661
|
+
}
|
|
3662
|
+
values.dispose();
|
|
3663
|
+
errors.dispose();
|
|
3664
|
+
touchedMap.dispose();
|
|
3665
|
+
dirty.dispose();
|
|
3666
|
+
valid.dispose();
|
|
3667
|
+
submitting.dispose();
|
|
3668
|
+
submitted.dispose();
|
|
3669
|
+
}
|
|
3670
|
+
return {
|
|
3671
|
+
values,
|
|
3672
|
+
errors,
|
|
3673
|
+
touched: touchedMap,
|
|
3674
|
+
dirty,
|
|
3675
|
+
valid,
|
|
3676
|
+
submitting,
|
|
3677
|
+
submitted,
|
|
3678
|
+
status,
|
|
3679
|
+
submitError,
|
|
3680
|
+
field: (name) => field(name).api,
|
|
3681
|
+
setValues,
|
|
3682
|
+
submit,
|
|
3683
|
+
reset,
|
|
3684
|
+
dispose
|
|
3685
|
+
};
|
|
3686
|
+
}
|
|
3687
|
+
|
|
3688
|
+
// ../context/src/context.ts
|
|
3689
|
+
function createContext(defaultValue, description) {
|
|
3690
|
+
const id = Symbol(description ?? "streetui.context");
|
|
3691
|
+
const stack = [];
|
|
3692
|
+
return {
|
|
3693
|
+
id,
|
|
3694
|
+
defaultValue,
|
|
3695
|
+
provide(value, run) {
|
|
3696
|
+
stack.push(value);
|
|
3697
|
+
try {
|
|
3698
|
+
return run();
|
|
3699
|
+
} finally {
|
|
3700
|
+
stack.pop();
|
|
3701
|
+
}
|
|
3702
|
+
},
|
|
3703
|
+
consume() {
|
|
3704
|
+
return stack.length > 0 ? stack[stack.length - 1] : defaultValue;
|
|
3705
|
+
},
|
|
3706
|
+
hasProvider() {
|
|
3707
|
+
return stack.length > 0;
|
|
3708
|
+
}
|
|
3709
|
+
};
|
|
3710
|
+
}
|
|
3711
|
+
|
|
3712
|
+
// ../i18n/src/i18n.ts
|
|
3713
|
+
var INTERPOLATION = /\{(\w+)\}/g;
|
|
3714
|
+
function interpolate(template, params) {
|
|
3715
|
+
if (params === void 0) return template;
|
|
3716
|
+
return template.replace(INTERPOLATION, (whole, name) => {
|
|
3717
|
+
const value = params[name];
|
|
3718
|
+
return value === void 0 ? whole : String(value);
|
|
3719
|
+
});
|
|
3720
|
+
}
|
|
3721
|
+
function createI18n(config) {
|
|
3722
|
+
const messages = config.messages;
|
|
3723
|
+
const fallback = config.fallbackLocale;
|
|
3724
|
+
const localeSignal = signal(config.locale);
|
|
3725
|
+
const locales = Object.keys(messages);
|
|
3726
|
+
function lookup(loc, key) {
|
|
3727
|
+
const active = messages[loc];
|
|
3728
|
+
const hit = active === void 0 ? void 0 : active[key];
|
|
3729
|
+
if (hit !== void 0) return hit;
|
|
3730
|
+
if (fallback !== void 0 && fallback !== loc) {
|
|
3731
|
+
const fb = messages[fallback];
|
|
3732
|
+
if (fb !== void 0) return fb[key];
|
|
3733
|
+
}
|
|
3734
|
+
return void 0;
|
|
3735
|
+
}
|
|
3736
|
+
function resolve(loc, key, params) {
|
|
3737
|
+
const template = lookup(loc, key);
|
|
3738
|
+
return template === void 0 ? key : interpolate(template, params);
|
|
3739
|
+
}
|
|
3740
|
+
function pluralKey(loc, key, count) {
|
|
3741
|
+
const category = new Intl.PluralRules(loc).select(count);
|
|
3742
|
+
if (lookup(loc, `${key}.${category}`) !== void 0) return `${key}.${category}`;
|
|
3743
|
+
return `${key}.other`;
|
|
3744
|
+
}
|
|
3745
|
+
return {
|
|
3746
|
+
locale: localeSignal,
|
|
3747
|
+
locales,
|
|
3748
|
+
setLocale(loc) {
|
|
3749
|
+
localeSignal.set(loc);
|
|
3750
|
+
},
|
|
3751
|
+
t(key, params) {
|
|
3752
|
+
return derived(() => resolve(localeSignal.get(), key, params));
|
|
3753
|
+
},
|
|
3754
|
+
translate(key, params) {
|
|
3755
|
+
return resolve(localeSignal.peek(), key, params);
|
|
3756
|
+
},
|
|
3757
|
+
plural(key, count, params) {
|
|
3758
|
+
const merged = { count, ...params ?? {} };
|
|
3759
|
+
return derived(() => {
|
|
3760
|
+
const loc = localeSignal.get();
|
|
3761
|
+
return resolve(loc, pluralKey(loc, key, count), merged);
|
|
3762
|
+
});
|
|
3763
|
+
},
|
|
3764
|
+
has(key) {
|
|
3765
|
+
return lookup(localeSignal.peek(), key) !== void 0;
|
|
3766
|
+
}
|
|
3767
|
+
};
|
|
3768
|
+
}
|
|
3769
|
+
|
|
3770
|
+
// ../devtools/src/inspector.ts
|
|
3771
|
+
function inspectGraph(graph) {
|
|
3772
|
+
return inspectNode(graph.root, 0);
|
|
3773
|
+
}
|
|
3774
|
+
function inspectNode(node, depth) {
|
|
3775
|
+
return {
|
|
3776
|
+
id: node.id,
|
|
3777
|
+
type: node.type,
|
|
3778
|
+
key: node.key,
|
|
3779
|
+
props: { ...node.props },
|
|
3780
|
+
eventTypes: node.events.map((e) => e.type),
|
|
3781
|
+
stateBindings: node.stateRefs.map((r) => `${r.propKey}\u2192${r.signalId}`),
|
|
3782
|
+
depth,
|
|
3783
|
+
children: node.children.map((c) => inspectNode(c, depth + 1))
|
|
3784
|
+
};
|
|
3785
|
+
}
|
|
3786
|
+
function printGraph(graph) {
|
|
3787
|
+
const lines = [];
|
|
3788
|
+
graph.walk((node, depth) => {
|
|
3789
|
+
const indent = " ".repeat(depth);
|
|
3790
|
+
const props = Object.entries(node.props).filter(([k]) => !k.startsWith("_")).map(([k, v]) => `${k}=${JSON.stringify(v)}`).join(", ");
|
|
3791
|
+
const events = node.events.length > 0 ? ` [events: ${node.events.map((e) => e.type).join(", ")}]` : "";
|
|
3792
|
+
const stateRefs = node.stateRefs.length > 0 ? ` [signals: ${node.stateRefs.map((r) => r.propKey).join(", ")}]` : "";
|
|
3793
|
+
lines.push(`${indent}<${node.type}${props ? ` ${props}` : ""}${events}${stateRefs}>`);
|
|
3794
|
+
});
|
|
3795
|
+
return lines.join("\n");
|
|
3796
|
+
}
|
|
3797
|
+
function printDiagnostics(compiled) {
|
|
3798
|
+
if (compiled.diagnostics.diagnostics.length === 0) {
|
|
3799
|
+
return "(no diagnostics)";
|
|
3800
|
+
}
|
|
3801
|
+
return compiled.diagnostics.diagnostics.map(formatDiagnostic).join("\n");
|
|
3802
|
+
}
|
|
3803
|
+
function nodeTypeStats(graph) {
|
|
3804
|
+
const counts = {};
|
|
3805
|
+
graph.walk((node) => {
|
|
3806
|
+
counts[node.type] = (counts[node.type] ?? 0) + 1;
|
|
3807
|
+
});
|
|
3808
|
+
return counts;
|
|
3809
|
+
}
|
|
3810
|
+
|
|
3811
|
+
// ../devtools/src/application.ts
|
|
3812
|
+
function collectPerf(node, distinctSignals, acc) {
|
|
3813
|
+
acc.totalNodes += 1;
|
|
3814
|
+
if (node.depth > acc.maxDepth) acc.maxDepth = node.depth;
|
|
3815
|
+
acc.eventHandlers += node.eventTypes.length;
|
|
3816
|
+
acc.stateBindings += node.stateBindings.length;
|
|
3817
|
+
if (node.children.length > acc.largestChildCount) acc.largestChildCount = node.children.length;
|
|
3818
|
+
for (const child of node.children) collectPerf(child, distinctSignals, acc);
|
|
3819
|
+
}
|
|
3820
|
+
function collectSignals(node, into) {
|
|
3821
|
+
for (const binding of node.stateBindings) {
|
|
3822
|
+
const arrow = binding.indexOf("\u2192");
|
|
3823
|
+
const signalId = arrow >= 0 ? binding.slice(arrow + 1) : binding;
|
|
3824
|
+
if (signalId.length > 0) into.add(signalId);
|
|
3825
|
+
}
|
|
3826
|
+
for (const child of node.children) collectSignals(child, into);
|
|
3827
|
+
}
|
|
3828
|
+
function inspectApplication(compiled) {
|
|
3829
|
+
const graph = inspectGraph(compiled.graph);
|
|
3830
|
+
const signals = /* @__PURE__ */ new Set();
|
|
3831
|
+
collectSignals(graph, signals);
|
|
3832
|
+
const pages = graph.children.filter((child) => child.type === "page").map((child) => ({ id: child.id, key: child.key }));
|
|
3833
|
+
const diags = compiled.diagnostics.diagnostics;
|
|
3834
|
+
const errors = diags.filter((d) => d.severity === "error").length;
|
|
3835
|
+
const perfAcc = { totalNodes: 0, maxDepth: 0, eventHandlers: 0, stateBindings: 0, largestChildCount: 0 };
|
|
3836
|
+
collectPerf(graph, signals.size, perfAcc);
|
|
3837
|
+
return {
|
|
3838
|
+
identity: {
|
|
3839
|
+
name: compiled.name,
|
|
3840
|
+
version: compiled.version,
|
|
3841
|
+
compiledAt: compiled.compiledAt
|
|
3842
|
+
},
|
|
3843
|
+
graph,
|
|
3844
|
+
nodeStats: nodeTypeStats(compiled.graph),
|
|
3845
|
+
signals: [...signals].sort(),
|
|
3846
|
+
pages,
|
|
3847
|
+
diagnostics: {
|
|
3848
|
+
errors,
|
|
3849
|
+
warnings: diags.length - errors,
|
|
3850
|
+
messages: diags.map(formatDiagnostic)
|
|
3851
|
+
},
|
|
3852
|
+
perf: {
|
|
3853
|
+
totalNodes: perfAcc.totalNodes,
|
|
3854
|
+
maxDepth: perfAcc.maxDepth,
|
|
3855
|
+
eventHandlers: perfAcc.eventHandlers,
|
|
3856
|
+
stateBindings: perfAcc.stateBindings,
|
|
3857
|
+
distinctSignals: signals.size,
|
|
3858
|
+
largestChildCount: perfAcc.largestChildCount
|
|
3859
|
+
}
|
|
3860
|
+
};
|
|
3861
|
+
}
|
|
3862
|
+
|
|
3863
|
+
// ../devtools/src/diagnostics.ts
|
|
3864
|
+
var DEFAULT_PERF_THRESHOLDS = {
|
|
3865
|
+
maxNodes: 5e3,
|
|
3866
|
+
maxDepth: 32,
|
|
3867
|
+
maxChildCount: 1e3,
|
|
3868
|
+
maxSignals: 1e3
|
|
3869
|
+
};
|
|
3870
|
+
function diagnosePerformance(compiled, thresholds = {}) {
|
|
3871
|
+
const t = { ...DEFAULT_PERF_THRESHOLDS, ...thresholds };
|
|
3872
|
+
const { perf } = inspectApplication(compiled);
|
|
3873
|
+
const out = [];
|
|
3874
|
+
if (perf.totalNodes > t.maxNodes) {
|
|
3875
|
+
out.push({
|
|
3876
|
+
code: "large-graph",
|
|
3877
|
+
message: `Graph has ${perf.totalNodes} nodes (> ${t.maxNodes}); consider splitting the view or paginating.`,
|
|
3878
|
+
observed: perf.totalNodes,
|
|
3879
|
+
threshold: t.maxNodes
|
|
3880
|
+
});
|
|
3881
|
+
}
|
|
3882
|
+
if (perf.maxDepth > t.maxDepth) {
|
|
3883
|
+
out.push({
|
|
3884
|
+
code: "deep-tree",
|
|
3885
|
+
message: `Graph nests ${perf.maxDepth} levels deep (> ${t.maxDepth}); deep trees slow mount and reconciliation.`,
|
|
3886
|
+
observed: perf.maxDepth,
|
|
3887
|
+
threshold: t.maxDepth
|
|
3888
|
+
});
|
|
3889
|
+
}
|
|
3890
|
+
if (perf.largestChildCount > t.maxChildCount) {
|
|
3891
|
+
out.push({
|
|
3892
|
+
code: "large-list",
|
|
3893
|
+
message: `A single node has ${perf.largestChildCount} children (> ${t.maxChildCount}); large un-windowed lists dominate DOM cost.`,
|
|
3894
|
+
observed: perf.largestChildCount,
|
|
3895
|
+
threshold: t.maxChildCount
|
|
3896
|
+
});
|
|
3897
|
+
}
|
|
3898
|
+
if (perf.distinctSignals > t.maxSignals) {
|
|
3899
|
+
out.push({
|
|
3900
|
+
code: "high-signal-fanout",
|
|
3901
|
+
message: `${perf.distinctSignals} distinct signals are bound (> ${t.maxSignals}); heavy reactive fan-out increases update overhead.`,
|
|
3902
|
+
observed: perf.distinctSignals,
|
|
3903
|
+
threshold: t.maxSignals
|
|
3904
|
+
});
|
|
3905
|
+
}
|
|
3906
|
+
return out;
|
|
3907
|
+
}
|
|
3908
|
+
|
|
3909
|
+
// ../devtools/src/inspect-reactive.ts
|
|
3910
|
+
function inspectSignal(source, options = {}) {
|
|
3911
|
+
const raw = source.peek();
|
|
3912
|
+
let value = raw;
|
|
3913
|
+
if (options.redact === true) value = "[redacted]";
|
|
3914
|
+
else if (typeof options.redact === "function") value = options.redact(raw);
|
|
3915
|
+
return {
|
|
3916
|
+
kind: signalKind(source),
|
|
3917
|
+
value,
|
|
3918
|
+
observerCount: observerCount(source)
|
|
3919
|
+
};
|
|
3920
|
+
}
|
|
3921
|
+
function inspectResource(resource2, options = {}) {
|
|
3922
|
+
const error = resource2.error.peek();
|
|
3923
|
+
const hasError = error !== void 0 && error !== null;
|
|
3924
|
+
const base = {
|
|
3925
|
+
status: resource2.status.peek(),
|
|
3926
|
+
loading: resource2.loading.peek(),
|
|
3927
|
+
isRefetching: resource2.isRefetching.peek(),
|
|
3928
|
+
hasData: resource2.data.peek() !== void 0,
|
|
3929
|
+
hasError,
|
|
3930
|
+
errorName: hasError ? errorConstructorName(error) : void 0
|
|
3931
|
+
};
|
|
3932
|
+
if (options.includeData !== true) return base;
|
|
3933
|
+
return {
|
|
3934
|
+
...base,
|
|
3935
|
+
data: resource2.data.peek(),
|
|
3936
|
+
...hasError ? { errorMessage: errorMessageOf(error) } : {}
|
|
3937
|
+
};
|
|
3938
|
+
}
|
|
3939
|
+
function errorConstructorName(error) {
|
|
3940
|
+
if (error instanceof Error) return error.name;
|
|
3941
|
+
return typeof error;
|
|
3942
|
+
}
|
|
3943
|
+
function errorMessageOf(error) {
|
|
3944
|
+
if (error instanceof Error) return error.message;
|
|
3945
|
+
return String(error);
|
|
3946
|
+
}
|
|
3947
|
+
function inspectRouter(router) {
|
|
3948
|
+
const match = router.currentRoute.peek();
|
|
3949
|
+
const query = {};
|
|
3950
|
+
for (const [k, v] of match.query.entries()) query[k] = v;
|
|
3951
|
+
return {
|
|
3952
|
+
path: match.path,
|
|
3953
|
+
pattern: match.pattern,
|
|
3954
|
+
params: { ...match.params },
|
|
3955
|
+
query,
|
|
3956
|
+
isFallback: match.isFallback === true
|
|
3957
|
+
};
|
|
3958
|
+
}
|
|
3959
|
+
function inspectForm(form, options = {}) {
|
|
3960
|
+
const values = form.values.peek();
|
|
3961
|
+
const errorsRaw = form.errors.peek();
|
|
3962
|
+
const touchedRaw = form.touched.peek();
|
|
3963
|
+
const errors = {};
|
|
3964
|
+
for (const [k, v] of Object.entries(errorsRaw)) if (v !== void 0) errors[k] = v;
|
|
3965
|
+
const touched = {};
|
|
3966
|
+
for (const [k, v] of Object.entries(touchedRaw)) touched[k] = v === true;
|
|
3967
|
+
const base = {
|
|
3968
|
+
fields: Object.keys(values),
|
|
3969
|
+
errors,
|
|
3970
|
+
touched,
|
|
3971
|
+
dirty: form.dirty.peek(),
|
|
3972
|
+
valid: form.valid.peek(),
|
|
3973
|
+
status: form.status.peek()
|
|
3974
|
+
};
|
|
3975
|
+
if (options.includeValues !== true) return base;
|
|
3976
|
+
return { ...base, values: { ...values } };
|
|
3977
|
+
}
|
|
3978
|
+
function inspectContext(context) {
|
|
3979
|
+
return {
|
|
3980
|
+
description: context.id.description ?? "streetui.context",
|
|
3981
|
+
hasProvider: context.hasProvider()
|
|
3982
|
+
};
|
|
3983
|
+
}
|
|
3984
|
+
function inspectI18n(i18n, options = {}) {
|
|
3985
|
+
const base = {
|
|
3986
|
+
locale: i18n.locale.peek(),
|
|
3987
|
+
locales: [...i18n.locales]
|
|
3988
|
+
};
|
|
3989
|
+
if (options.checkKeys === void 0) return base;
|
|
3990
|
+
const missingKeys = options.checkKeys.filter((k) => !i18n.has(k));
|
|
3991
|
+
return { ...base, missingKeys };
|
|
3992
|
+
}
|
|
3993
|
+
|
|
3994
|
+
// ../devtools/src/panels.ts
|
|
3995
|
+
function createDevTools(compiled, sources = {}, options = {}) {
|
|
3996
|
+
let current = capture(compiled, sources, options);
|
|
3997
|
+
return {
|
|
3998
|
+
get snapshot() {
|
|
3999
|
+
return current;
|
|
4000
|
+
},
|
|
4001
|
+
refresh() {
|
|
4002
|
+
current = capture(compiled, sources, options);
|
|
4003
|
+
return current;
|
|
4004
|
+
},
|
|
4005
|
+
selectNode(id) {
|
|
4006
|
+
return findNode(current.graph, id);
|
|
4007
|
+
},
|
|
4008
|
+
format() {
|
|
4009
|
+
return formatSnapshot(current);
|
|
4010
|
+
}
|
|
4011
|
+
};
|
|
4012
|
+
}
|
|
4013
|
+
function capture(compiled, sources, options) {
|
|
4014
|
+
const app = inspectApplication(compiled);
|
|
4015
|
+
const application = {
|
|
4016
|
+
identity: app.identity,
|
|
4017
|
+
nodeCount: app.perf.totalNodes,
|
|
4018
|
+
maxDepth: app.perf.maxDepth,
|
|
4019
|
+
pages: app.pages,
|
|
4020
|
+
signalCount: app.signals.length,
|
|
4021
|
+
eventHandlers: app.perf.eventHandlers,
|
|
4022
|
+
stateBindings: app.perf.stateBindings,
|
|
4023
|
+
errors: app.diagnostics.errors,
|
|
4024
|
+
warnings: app.diagnostics.warnings
|
|
4025
|
+
};
|
|
4026
|
+
const live = {};
|
|
4027
|
+
if (sources.signals !== void 0) {
|
|
4028
|
+
for (const [label, sig] of Object.entries(sources.signals)) {
|
|
4029
|
+
live[label] = inspectSignal(
|
|
4030
|
+
sig,
|
|
4031
|
+
options.redactSignals !== void 0 ? { redact: options.redactSignals } : {}
|
|
4032
|
+
);
|
|
4033
|
+
}
|
|
4034
|
+
}
|
|
4035
|
+
const signals = { boundSignalIds: app.signals, live };
|
|
4036
|
+
const performance = {
|
|
4037
|
+
snapshot: app.perf,
|
|
4038
|
+
diagnostics: diagnosePerformance(compiled, options.perfThresholds)
|
|
4039
|
+
};
|
|
4040
|
+
const snapshot = {
|
|
4041
|
+
application,
|
|
4042
|
+
graph: app.graph,
|
|
4043
|
+
signals,
|
|
4044
|
+
performance,
|
|
4045
|
+
...sources.router !== void 0 ? { router: inspectRouter(sources.router) } : {},
|
|
4046
|
+
...sources.resources !== void 0 ? { resources: mapInspect(sources.resources, (r) => inspectResource(r)) } : {},
|
|
4047
|
+
...sources.forms !== void 0 ? { forms: mapInspect(sources.forms, (f) => inspectForm(f)) } : {},
|
|
4048
|
+
...sources.contexts !== void 0 ? { contexts: mapInspect(sources.contexts, (c) => inspectContext(c)) } : {},
|
|
4049
|
+
...sources.i18n !== void 0 ? {
|
|
4050
|
+
i18n: inspectI18n(
|
|
4051
|
+
sources.i18n,
|
|
4052
|
+
options.i18nCheckKeys !== void 0 ? { checkKeys: options.i18nCheckKeys } : {}
|
|
4053
|
+
)
|
|
4054
|
+
} : {}
|
|
4055
|
+
};
|
|
4056
|
+
return snapshot;
|
|
4057
|
+
}
|
|
4058
|
+
function mapInspect(source, inspect) {
|
|
4059
|
+
const out = {};
|
|
4060
|
+
for (const [label, value] of Object.entries(source)) out[label] = inspect(value);
|
|
4061
|
+
return out;
|
|
4062
|
+
}
|
|
4063
|
+
function findNode(node, id) {
|
|
4064
|
+
if (node.id === id) return node;
|
|
4065
|
+
for (const child of node.children) {
|
|
4066
|
+
const found = findNode(child, id);
|
|
4067
|
+
if (found !== void 0) return found;
|
|
4068
|
+
}
|
|
4069
|
+
return void 0;
|
|
4070
|
+
}
|
|
4071
|
+
function formatSnapshot(s) {
|
|
4072
|
+
const lines = [];
|
|
4073
|
+
const app = s.application;
|
|
4074
|
+
lines.push(`StreetUI DevTools \u2014 ${app.identity.name} v${app.identity.version}`);
|
|
4075
|
+
lines.push(
|
|
4076
|
+
`Application: ${app.nodeCount} nodes, depth ${app.maxDepth}, ${app.pages.length} page(s)`
|
|
4077
|
+
);
|
|
4078
|
+
lines.push(
|
|
4079
|
+
` signals ${app.signalCount} \xB7 handlers ${app.eventHandlers} \xB7 bindings ${app.stateBindings} \xB7 errors ${app.errors} \xB7 warnings ${app.warnings}`
|
|
4080
|
+
);
|
|
4081
|
+
lines.push(`Signals: ${s.signals.boundSignalIds.length} bound in graph`);
|
|
4082
|
+
for (const [label, sig] of Object.entries(s.signals.live)) {
|
|
4083
|
+
lines.push(` ${label} [${sig.kind}] = ${format(sig.value)} \xB7 observers ${sig.observerCount ?? "?"}`);
|
|
4084
|
+
}
|
|
4085
|
+
if (s.router !== void 0) {
|
|
4086
|
+
lines.push(`Router: ${s.router.path} (${s.router.pattern})${s.router.isFallback ? " [fallback]" : ""}`);
|
|
4087
|
+
}
|
|
4088
|
+
if (s.resources !== void 0) {
|
|
4089
|
+
lines.push("Resources:");
|
|
4090
|
+
for (const [label, r] of Object.entries(s.resources)) {
|
|
4091
|
+
lines.push(` ${label}: ${r.status}${r.loading ? " (loading)" : ""}${r.hasError ? ` !${r.errorName}` : ""}`);
|
|
4092
|
+
}
|
|
4093
|
+
}
|
|
4094
|
+
if (s.forms !== void 0) {
|
|
4095
|
+
lines.push("Forms:");
|
|
4096
|
+
for (const [label, f] of Object.entries(s.forms)) {
|
|
4097
|
+
lines.push(` ${label}: ${f.valid ? "valid" : "invalid"} \xB7 ${f.status} \xB7 fields ${f.fields.length}`);
|
|
4098
|
+
}
|
|
4099
|
+
}
|
|
4100
|
+
if (s.contexts !== void 0) {
|
|
4101
|
+
lines.push("Contexts:");
|
|
4102
|
+
for (const [label, c] of Object.entries(s.contexts)) {
|
|
4103
|
+
lines.push(` ${label} (${c.description}): ${c.hasProvider ? "provided" : "no provider"}`);
|
|
4104
|
+
}
|
|
4105
|
+
}
|
|
4106
|
+
if (s.i18n !== void 0) {
|
|
4107
|
+
const missing = s.i18n.missingKeys;
|
|
4108
|
+
lines.push(
|
|
4109
|
+
`i18n: ${s.i18n.locale} of [${s.i18n.locales.join(", ")}]${missing !== void 0 ? ` \xB7 missing ${missing.length}` : ""}`
|
|
4110
|
+
);
|
|
4111
|
+
}
|
|
4112
|
+
lines.push(
|
|
4113
|
+
`Performance: ${s.performance.diagnostics.length} diagnostic(s), ${s.performance.snapshot.totalNodes} nodes`
|
|
4114
|
+
);
|
|
4115
|
+
for (const d of s.performance.diagnostics) {
|
|
4116
|
+
lines.push(` ${d.code}: ${d.message}`);
|
|
4117
|
+
}
|
|
4118
|
+
return lines.join("\n");
|
|
4119
|
+
}
|
|
4120
|
+
function format(value) {
|
|
4121
|
+
if (typeof value === "string") return JSON.stringify(value);
|
|
4122
|
+
if (value === null || value === void 0) return String(value);
|
|
4123
|
+
if (typeof value === "object") {
|
|
4124
|
+
try {
|
|
4125
|
+
return JSON.stringify(value);
|
|
4126
|
+
} catch {
|
|
4127
|
+
return "[object]";
|
|
4128
|
+
}
|
|
4129
|
+
}
|
|
4130
|
+
return String(value);
|
|
4131
|
+
}
|
|
4132
|
+
|
|
4133
|
+
// src/config.ts
|
|
4134
|
+
function defineConfig(config) {
|
|
4135
|
+
return config;
|
|
4136
|
+
}
|
|
4137
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
4138
|
+
0 && (module.exports = {
|
|
4139
|
+
AppBuilder,
|
|
4140
|
+
Application,
|
|
4141
|
+
ApplicationGraph,
|
|
4142
|
+
BaseNode,
|
|
4143
|
+
BrowserDOMAdapter,
|
|
4144
|
+
CleanupRegistry,
|
|
4145
|
+
ContainerBuilderImpl,
|
|
4146
|
+
DEFAULT_PERF_THRESHOLDS,
|
|
4147
|
+
DerivedSignal,
|
|
4148
|
+
DiagnosticCollector,
|
|
4149
|
+
DiagnosticError,
|
|
4150
|
+
DomEventRegistry,
|
|
4151
|
+
Environment,
|
|
4152
|
+
EventBus,
|
|
4153
|
+
FOCUSABLE_SELECTOR,
|
|
4154
|
+
FormBuilderImpl,
|
|
4155
|
+
GraphNode,
|
|
4156
|
+
Lifecycle,
|
|
4157
|
+
ListBuilderImpl,
|
|
4158
|
+
NodeInstance,
|
|
4159
|
+
PageBuilderImpl,
|
|
4160
|
+
ROUTER_OUTLET_ID,
|
|
4161
|
+
Runtime,
|
|
4162
|
+
RuntimeNodeInstance,
|
|
4163
|
+
STATE_MARKER_ATTR,
|
|
4164
|
+
Scheduler,
|
|
4165
|
+
SectionBuilderImpl,
|
|
4166
|
+
ServerComment,
|
|
4167
|
+
ServerDOMAdapter,
|
|
4168
|
+
ServerElement,
|
|
4169
|
+
ServerFragment,
|
|
4170
|
+
ServerStyle,
|
|
4171
|
+
ServerText,
|
|
4172
|
+
Signal,
|
|
4173
|
+
Store,
|
|
4174
|
+
StreetApp,
|
|
4175
|
+
StreetFrameworkError,
|
|
4176
|
+
StreetRenderHandle,
|
|
4177
|
+
StreetRendererImpl,
|
|
4178
|
+
VERSION,
|
|
4179
|
+
a11yIds,
|
|
4180
|
+
applyNodeProps,
|
|
4181
|
+
applyProp,
|
|
4182
|
+
batch,
|
|
4183
|
+
bindDomEvent,
|
|
4184
|
+
browserDOMAdapter,
|
|
4185
|
+
buttonUpdate,
|
|
4186
|
+
compile,
|
|
4187
|
+
compileGraph,
|
|
4188
|
+
consoleDiagnosticSink,
|
|
4189
|
+
consoleHydrationDiagnosticSink,
|
|
4190
|
+
createApplication,
|
|
4191
|
+
createBrowserHistory,
|
|
4192
|
+
createContext,
|
|
4193
|
+
createDevTools,
|
|
4194
|
+
createForm,
|
|
4195
|
+
createHydrationDiagnosticCollector,
|
|
4196
|
+
createI18n,
|
|
4197
|
+
createMemoryHistory,
|
|
4198
|
+
createNodeId,
|
|
4199
|
+
createRenderContext,
|
|
4200
|
+
createRenderer,
|
|
4201
|
+
createRouter,
|
|
4202
|
+
createRuntime,
|
|
4203
|
+
createStore,
|
|
4204
|
+
createStreetEvent,
|
|
4205
|
+
defineConfig,
|
|
4206
|
+
derived,
|
|
4207
|
+
diagnosePerformance,
|
|
4208
|
+
effect,
|
|
4209
|
+
email,
|
|
4210
|
+
environment,
|
|
4211
|
+
escapeHtmlAttr,
|
|
4212
|
+
escapeHtmlText,
|
|
4213
|
+
flushSync,
|
|
4214
|
+
focusById,
|
|
4215
|
+
focusFirst,
|
|
4216
|
+
formatDiagnostic,
|
|
4217
|
+
formatDiagnosticContext,
|
|
4218
|
+
formatHydrationDiagnostic,
|
|
4219
|
+
frameworkError,
|
|
4220
|
+
generateApplicationId,
|
|
4221
|
+
generateNodeId,
|
|
4222
|
+
globalEventBus,
|
|
4223
|
+
headingUpdate,
|
|
4224
|
+
hydrateGraph,
|
|
4225
|
+
inputUpdate,
|
|
4226
|
+
inspectApplication,
|
|
4227
|
+
inspectContext,
|
|
4228
|
+
inspectForm,
|
|
4229
|
+
inspectGraph,
|
|
4230
|
+
inspectI18n,
|
|
4231
|
+
inspectResource,
|
|
4232
|
+
inspectRouter,
|
|
4233
|
+
inspectSignal,
|
|
4234
|
+
interpolate,
|
|
4235
|
+
isBatching,
|
|
4236
|
+
matchPattern,
|
|
4237
|
+
matchRoutes,
|
|
4238
|
+
maxLength,
|
|
4239
|
+
minLength,
|
|
4240
|
+
mountGraph,
|
|
4241
|
+
mountNode,
|
|
4242
|
+
mountRouter,
|
|
4243
|
+
nextId,
|
|
4244
|
+
nodeIdPrefix,
|
|
4245
|
+
nodeTypeStats,
|
|
4246
|
+
normalizePath,
|
|
4247
|
+
observerCount,
|
|
4248
|
+
patchNode,
|
|
4249
|
+
patchProp,
|
|
4250
|
+
pattern,
|
|
4251
|
+
printDiagnostics,
|
|
4252
|
+
printGraph,
|
|
4253
|
+
reactiveListItemKey,
|
|
4254
|
+
reactiveListItemSignature,
|
|
4255
|
+
readState,
|
|
4256
|
+
reconcileChildren,
|
|
4257
|
+
renderToString,
|
|
4258
|
+
reportDiagnostic,
|
|
4259
|
+
required,
|
|
4260
|
+
resetIdCounter,
|
|
4261
|
+
resolveTag,
|
|
4262
|
+
resource,
|
|
4263
|
+
routerOutlet,
|
|
4264
|
+
runValidators,
|
|
4265
|
+
scheduleImmediate,
|
|
4266
|
+
scheduleUpdate,
|
|
4267
|
+
scheduler,
|
|
4268
|
+
serializeChildren,
|
|
4269
|
+
serializeServerNode,
|
|
4270
|
+
serializeState,
|
|
4271
|
+
serverDOMAdapter,
|
|
4272
|
+
signal,
|
|
4273
|
+
signalKind,
|
|
4274
|
+
splitTarget,
|
|
4275
|
+
streetui,
|
|
4276
|
+
textUpdate,
|
|
4277
|
+
toIdToken,
|
|
4278
|
+
transformGraph,
|
|
4279
|
+
validateGraph,
|
|
4280
|
+
wireEvents,
|
|
4281
|
+
wireReactiveList,
|
|
4282
|
+
wireSignalBindings
|
|
4283
|
+
});
|
|
4284
|
+
//# sourceMappingURL=index.cjs.map
|