@nisli/core 0.55.0 → 0.57.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/README.md +58 -1
- package/dist/bundler/component.js +409 -0
- package/dist/bundler/component.js.map +7 -0
- package/dist/bundler/context.js +27 -0
- package/dist/bundler/context.js.map +7 -0
- package/dist/bundler/diagnostics.js +55 -0
- package/dist/bundler/diagnostics.js.map +7 -0
- package/dist/bundler/disposable.js +9 -0
- package/dist/bundler/disposable.js.map +7 -0
- package/dist/bundler/element-context.js +47 -0
- package/dist/bundler/element-context.js.map +7 -0
- package/dist/bundler/emitter.js +70 -0
- package/dist/bundler/emitter.js.map +7 -0
- package/dist/bundler/hmr/registry.js +72 -0
- package/dist/bundler/hmr/registry.js.map +7 -0
- package/dist/bundler/hmr/transform.js +30 -0
- package/dist/bundler/hmr/transform.js.map +7 -0
- package/dist/bundler/index.js +103 -0
- package/dist/bundler/index.js.map +7 -0
- package/dist/bundler/injector.js +75 -0
- package/dist/bundler/injector.js.map +7 -0
- package/dist/bundler/lifecycle.js +56 -0
- package/dist/bundler/lifecycle.js.map +7 -0
- package/dist/bundler/projection.js +92 -0
- package/dist/bundler/projection.js.map +7 -0
- package/dist/bundler/query.js +302 -0
- package/dist/bundler/query.js.map +7 -0
- package/dist/bundler/ref.js +16 -0
- package/dist/bundler/ref.js.map +7 -0
- package/dist/bundler/resource.js +99 -0
- package/dist/bundler/resource.js.map +7 -0
- package/dist/bundler/settle.js +43 -0
- package/dist/bundler/settle.js.map +7 -0
- package/dist/bundler/signal.js +403 -0
- package/dist/bundler/signal.js.map +7 -0
- package/dist/bundler/template-audit.js +162 -0
- package/dist/bundler/template-audit.js.map +7 -0
- package/dist/bundler/template.js +847 -0
- package/dist/bundler/template.js.map +7 -0
- package/dist/bundler/view-transition.js +20 -0
- package/dist/bundler/view-transition.js.map +7 -0
- package/dist/bundler/vite-hmr/plugin.js +32 -0
- package/dist/bundler/vite-hmr/plugin.js.map +7 -0
- package/dist/bundler/vite-hmr/runtime.js +8 -0
- package/dist/bundler/vite-hmr/runtime.js.map +7 -0
- package/dist/index.js +2607 -44
- package/dist/index.js.map +7 -1
- package/dist/index.production.js +2 -0
- package/dist/index.production.js.map +6 -0
- package/dist/{diagnostics.d.ts → types/diagnostics.d.ts} +8 -15
- package/dist/{index.d.ts → types/index.d.ts} +1 -0
- package/dist/{template-audit.d.ts → types/template-audit.d.ts} +4 -5
- package/dist/{template.d.ts → types/template.d.ts} +3 -15
- package/package.json +12 -9
- package/dist/component.js +0 -633
- package/dist/component.js.map +0 -1
- package/dist/context.js +0 -55
- package/dist/context.js.map +0 -1
- package/dist/diagnostics.js +0 -118
- package/dist/diagnostics.js.map +0 -1
- package/dist/disposable.js +0 -8
- package/dist/disposable.js.map +0 -1
- package/dist/element-context.js +0 -108
- package/dist/element-context.js.map +0 -1
- package/dist/emitter.js +0 -91
- package/dist/emitter.js.map +0 -1
- package/dist/hmr/registry.js +0 -136
- package/dist/hmr/registry.js.map +0 -1
- package/dist/hmr/transform.js +0 -52
- package/dist/hmr/transform.js.map +0 -1
- package/dist/injector.js +0 -118
- package/dist/injector.js.map +0 -1
- package/dist/lifecycle.js +0 -117
- package/dist/lifecycle.js.map +0 -1
- package/dist/projection.js +0 -179
- package/dist/projection.js.map +0 -1
- package/dist/query.js +0 -429
- package/dist/query.js.map +0 -1
- package/dist/ref.js +0 -43
- package/dist/ref.js.map +0 -1
- package/dist/resource.js +0 -129
- package/dist/resource.js.map +0 -1
- package/dist/settle.js +0 -106
- package/dist/settle.js.map +0 -1
- package/dist/signal.js +0 -645
- package/dist/signal.js.map +0 -1
- package/dist/template-audit.js +0 -246
- package/dist/template-audit.js.map +0 -1
- package/dist/template.js +0 -1218
- package/dist/template.js.map +0 -1
- package/dist/view-transition.js +0 -98
- package/dist/view-transition.js.map +0 -1
- package/dist/vite-hmr/plugin.js +0 -62
- package/dist/vite-hmr/plugin.js.map +0 -1
- package/dist/vite-hmr/runtime.js +0 -17
- package/dist/vite-hmr/runtime.js.map +0 -1
- /package/dist/{component.d.ts → types/component.d.ts} +0 -0
- /package/dist/{context.d.ts → types/context.d.ts} +0 -0
- /package/dist/{disposable.d.ts → types/disposable.d.ts} +0 -0
- /package/dist/{element-context.d.ts → types/element-context.d.ts} +0 -0
- /package/dist/{emitter.d.ts → types/emitter.d.ts} +0 -0
- /package/dist/{hmr → types/hmr}/registry.d.ts +0 -0
- /package/dist/{hmr → types/hmr}/transform.d.ts +0 -0
- /package/dist/{injector.d.ts → types/injector.d.ts} +0 -0
- /package/dist/{lifecycle.d.ts → types/lifecycle.d.ts} +0 -0
- /package/dist/{projection.d.ts → types/projection.d.ts} +0 -0
- /package/dist/{query.d.ts → types/query.d.ts} +0 -0
- /package/dist/{ref.d.ts → types/ref.d.ts} +0 -0
- /package/dist/{resource.d.ts → types/resource.d.ts} +0 -0
- /package/dist/{settle.d.ts → types/settle.d.ts} +0 -0
- /package/dist/{signal.d.ts → types/signal.d.ts} +0 -0
- /package/dist/{view-transition.d.ts → types/view-transition.d.ts} +0 -0
- /package/dist/{vite-hmr → types/vite-hmr}/plugin.d.ts +0 -0
- /package/dist/{vite-hmr → types/vite-hmr}/runtime.d.ts +0 -0
package/dist/index.js
CHANGED
|
@@ -1,44 +1,2607 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
//
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
1
|
+
// src/diagnostics.ts
|
|
2
|
+
var override = null;
|
|
3
|
+
function isDev() {
|
|
4
|
+
return override ?? true;
|
|
5
|
+
}
|
|
6
|
+
function setDevMode(on) {
|
|
7
|
+
if (true) override = on;
|
|
8
|
+
}
|
|
9
|
+
function formatDiag(code, message) {
|
|
10
|
+
return `[nisli ${code}] ${message}`;
|
|
11
|
+
}
|
|
12
|
+
function diag(code, message, detail) {
|
|
13
|
+
if (detail === void 0) {
|
|
14
|
+
console.error(formatDiag(code, message));
|
|
15
|
+
} else {
|
|
16
|
+
console.error(formatDiag(code, message), detail);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// src/disposable.ts
|
|
21
|
+
var DISPOSE = Symbol.dispose;
|
|
22
|
+
function disposable(value, dispose) {
|
|
23
|
+
if (DISPOSE) value[DISPOSE] = dispose;
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// src/signal.ts
|
|
28
|
+
var contextHook = null;
|
|
29
|
+
function setContextHook(hook) {
|
|
30
|
+
contextHook = hook;
|
|
31
|
+
}
|
|
32
|
+
var SIGNAL_BRAND = /* @__PURE__ */ Symbol.for("backlog.signal");
|
|
33
|
+
var activeObserver = null;
|
|
34
|
+
var runningEffect = null;
|
|
35
|
+
var globalEpoch = 0;
|
|
36
|
+
var nextEffectOrder = 0;
|
|
37
|
+
function diag2(code, message, detail) {
|
|
38
|
+
if (!isDev()) return;
|
|
39
|
+
diag(code, message, detail);
|
|
40
|
+
}
|
|
41
|
+
var pendingEffects = /* @__PURE__ */ new Set();
|
|
42
|
+
var flushScheduled = false;
|
|
43
|
+
function scheduleFlush() {
|
|
44
|
+
if (!flushScheduled) {
|
|
45
|
+
flushScheduled = true;
|
|
46
|
+
queueMicrotask(flushPendingEffects);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function flushPendingEffects() {
|
|
50
|
+
flushScheduled = false;
|
|
51
|
+
const effects = [...pendingEffects].sort((a, b) => a.order - b.order);
|
|
52
|
+
pendingEffects.clear();
|
|
53
|
+
for (const effect3 of effects) {
|
|
54
|
+
if (!effect3.disposed) {
|
|
55
|
+
runEffect(effect3);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
var NO_COMPUTED_ERROR = /* @__PURE__ */ Symbol("no-computed-error");
|
|
60
|
+
var SignalImpl = class {
|
|
61
|
+
[SIGNAL_BRAND] = true;
|
|
62
|
+
/** @internal */
|
|
63
|
+
_node;
|
|
64
|
+
constructor(initialValue) {
|
|
65
|
+
this._node = {
|
|
66
|
+
value: initialValue,
|
|
67
|
+
lastChanged: globalEpoch,
|
|
68
|
+
observers: /* @__PURE__ */ new Set()
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
get value() {
|
|
72
|
+
if (activeObserver) {
|
|
73
|
+
activeObserver.sources.add(this._node);
|
|
74
|
+
this._node.observers.add(activeObserver);
|
|
75
|
+
}
|
|
76
|
+
return this._node.value;
|
|
77
|
+
}
|
|
78
|
+
set value(newValue) {
|
|
79
|
+
if (Object.is(this._node.value, newValue)) return;
|
|
80
|
+
if (isDev() && runningEffect !== null && runningEffect.sources.has(this._node)) {
|
|
81
|
+
diag2(
|
|
82
|
+
"N302",
|
|
83
|
+
"Effect wrote to a signal it also reads \u2014 the write re-schedules the effect. If this does not converge to an equal value it is an infinite loop. Derive the value with computed() instead.",
|
|
84
|
+
runningEffect.createdAt ? { effect: runningEffect.createdAt } : void 0
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
this._node.value = newValue;
|
|
88
|
+
this._node.lastChanged = ++globalEpoch;
|
|
89
|
+
notifyObservers(this._node.observers, 2 /* Dirty */);
|
|
90
|
+
}
|
|
91
|
+
peek() {
|
|
92
|
+
return this._node.value;
|
|
93
|
+
}
|
|
94
|
+
subscribe(fn) {
|
|
95
|
+
return effect(() => {
|
|
96
|
+
const value = this.value;
|
|
97
|
+
untrack(() => fn(value));
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
function notifyObservers(observers, flavor) {
|
|
102
|
+
for (const observer of observers) {
|
|
103
|
+
observer.notify(flavor);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
var ComputedImpl = class {
|
|
107
|
+
[SIGNAL_BRAND] = true;
|
|
108
|
+
/** @internal */
|
|
109
|
+
_node;
|
|
110
|
+
compute;
|
|
111
|
+
state = 2 /* Dirty */;
|
|
112
|
+
// Start dirty so first read computes
|
|
113
|
+
sources = /* @__PURE__ */ new Set();
|
|
114
|
+
computing = false;
|
|
115
|
+
error = NO_COMPUTED_ERROR;
|
|
116
|
+
/**
|
|
117
|
+
* globalEpoch snapshot at the end of the last update()/validation pass.
|
|
118
|
+
* A MaybeDirty computed recomputes only if some source's lastChanged
|
|
119
|
+
* advanced past this — otherwise it revalidates to Clean for free.
|
|
120
|
+
*/
|
|
121
|
+
lastValidated = 0;
|
|
122
|
+
constructor(fn) {
|
|
123
|
+
this.compute = fn;
|
|
124
|
+
this._node = {
|
|
125
|
+
value: void 0,
|
|
126
|
+
lastChanged: 0,
|
|
127
|
+
observers: /* @__PURE__ */ new Set(),
|
|
128
|
+
pull: () => {
|
|
129
|
+
this.peek();
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
get value() {
|
|
134
|
+
if (this.computing) {
|
|
135
|
+
throw new Error("Circular dependency detected in computed()");
|
|
136
|
+
}
|
|
137
|
+
if (activeObserver) {
|
|
138
|
+
activeObserver.sources.add(this._node);
|
|
139
|
+
this._node.observers.add(activeObserver);
|
|
140
|
+
}
|
|
141
|
+
if (this.state !== 0 /* Clean */) {
|
|
142
|
+
this.update();
|
|
143
|
+
}
|
|
144
|
+
if (this.error !== NO_COMPUTED_ERROR) {
|
|
145
|
+
throw this.error;
|
|
146
|
+
}
|
|
147
|
+
return this._node.value;
|
|
148
|
+
}
|
|
149
|
+
peek() {
|
|
150
|
+
if (this.computing) {
|
|
151
|
+
throw new Error("Circular dependency detected in computed()");
|
|
152
|
+
}
|
|
153
|
+
if (this.state !== 0 /* Clean */) {
|
|
154
|
+
this.update();
|
|
155
|
+
}
|
|
156
|
+
if (this.error !== NO_COMPUTED_ERROR) {
|
|
157
|
+
throw this.error;
|
|
158
|
+
}
|
|
159
|
+
return this._node.value;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* MaybeDirty validation: pull each source (computeds recompute lazily) and
|
|
163
|
+
* report whether any source's change-epoch advanced past our last
|
|
164
|
+
* validation. A pulled source may throw — its error-state transition
|
|
165
|
+
* already bumped its change-epoch, so the epoch comparison decides.
|
|
166
|
+
*/
|
|
167
|
+
sourcesChanged() {
|
|
168
|
+
for (const source of this.sources) {
|
|
169
|
+
if ("pull" in source) {
|
|
170
|
+
try {
|
|
171
|
+
source.pull();
|
|
172
|
+
} catch (_) {
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (source.lastChanged > this.lastValidated) return true;
|
|
176
|
+
}
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
update() {
|
|
180
|
+
if (this.state === 1 /* MaybeDirty */ && !this.sourcesChanged()) {
|
|
181
|
+
this.state = 0 /* Clean */;
|
|
182
|
+
this.lastValidated = globalEpoch;
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
for (const source of this.sources) {
|
|
186
|
+
source.observers.delete(this);
|
|
187
|
+
}
|
|
188
|
+
this.sources.clear();
|
|
189
|
+
const prevObserver = activeObserver;
|
|
190
|
+
activeObserver = this;
|
|
191
|
+
this.computing = true;
|
|
192
|
+
const wasError = this.error !== NO_COMPUTED_ERROR;
|
|
193
|
+
try {
|
|
194
|
+
const newValue = this.compute();
|
|
195
|
+
this.error = NO_COMPUTED_ERROR;
|
|
196
|
+
if (!Object.is(this._node.value, newValue)) {
|
|
197
|
+
this._node.value = newValue;
|
|
198
|
+
this._node.lastChanged = ++globalEpoch;
|
|
199
|
+
} else if (wasError) {
|
|
200
|
+
this._node.lastChanged = ++globalEpoch;
|
|
201
|
+
}
|
|
202
|
+
} catch (error) {
|
|
203
|
+
this.error = error;
|
|
204
|
+
if (!wasError) {
|
|
205
|
+
this._node.lastChanged = ++globalEpoch;
|
|
206
|
+
}
|
|
207
|
+
throw error;
|
|
208
|
+
} finally {
|
|
209
|
+
this.computing = false;
|
|
210
|
+
activeObserver = prevObserver;
|
|
211
|
+
this.state = 0 /* Clean */;
|
|
212
|
+
this.lastValidated = globalEpoch;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
/** @internal — called by the ReactiveNode interface when a source changes */
|
|
216
|
+
notify(flavor) {
|
|
217
|
+
if (flavor > this.state) {
|
|
218
|
+
const wasClean = this.state === 0 /* Clean */;
|
|
219
|
+
this.state = flavor;
|
|
220
|
+
if (wasClean) {
|
|
221
|
+
notifyObservers(this._node.observers, 1 /* MaybeDirty */);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
subscribe(fn) {
|
|
226
|
+
return effect(() => {
|
|
227
|
+
const value = this.value;
|
|
228
|
+
untrack(() => fn(value));
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
// ReactiveNode interface — used by dependency tracking
|
|
232
|
+
get sources_() {
|
|
233
|
+
return this.sources;
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
Object.defineProperty(ComputedImpl.prototype, "sources", {
|
|
237
|
+
enumerable: false
|
|
238
|
+
});
|
|
239
|
+
var MAX_EFFECT_RERUNS = 100;
|
|
240
|
+
function createEffectNode(fn) {
|
|
241
|
+
return {
|
|
242
|
+
state: 2 /* Dirty */,
|
|
243
|
+
fn,
|
|
244
|
+
cleanup: null,
|
|
245
|
+
disposed: false,
|
|
246
|
+
sources: /* @__PURE__ */ new Set(),
|
|
247
|
+
disposers: [],
|
|
248
|
+
order: nextEffectOrder++,
|
|
249
|
+
lastRunEpoch: 0,
|
|
250
|
+
selfReschedules: 0,
|
|
251
|
+
notify(flavor) {
|
|
252
|
+
if (this.disposed) return;
|
|
253
|
+
if (flavor > this.state) this.state = flavor;
|
|
254
|
+
pendingEffects.add(this);
|
|
255
|
+
scheduleFlush();
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
function effectSourcesChanged(node) {
|
|
260
|
+
for (const source of node.sources) {
|
|
261
|
+
if ("pull" in source) {
|
|
262
|
+
try {
|
|
263
|
+
source.pull();
|
|
264
|
+
} catch (_) {
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
if (source.lastChanged > node.lastRunEpoch) return true;
|
|
268
|
+
}
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
function isThenable(value) {
|
|
272
|
+
return value !== null && typeof value === "object" && typeof value.then === "function";
|
|
273
|
+
}
|
|
274
|
+
function runEffect(node) {
|
|
275
|
+
if (node.disposed) return;
|
|
276
|
+
if (node.state === 1 /* MaybeDirty */ && !effectSourcesChanged(node)) {
|
|
277
|
+
node.state = 0 /* Clean */;
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
if (node.cleanup) {
|
|
281
|
+
try {
|
|
282
|
+
node.cleanup();
|
|
283
|
+
} catch (_) {
|
|
284
|
+
}
|
|
285
|
+
node.cleanup = null;
|
|
286
|
+
}
|
|
287
|
+
for (const source of node.sources) {
|
|
288
|
+
source.observers.delete(node);
|
|
289
|
+
}
|
|
290
|
+
node.sources.clear();
|
|
291
|
+
node.state = 0 /* Clean */;
|
|
292
|
+
const prevObserver = activeObserver;
|
|
293
|
+
const prevRunning = true ? runningEffect : null;
|
|
294
|
+
activeObserver = node;
|
|
295
|
+
if (true) runningEffect = node;
|
|
296
|
+
try {
|
|
297
|
+
const result = node.fn();
|
|
298
|
+
if (typeof result === "function") {
|
|
299
|
+
node.cleanup = result;
|
|
300
|
+
} else if (isThenable(result)) {
|
|
301
|
+
if (isDev() && !node.asyncWarned) {
|
|
302
|
+
node.asyncWarned = true;
|
|
303
|
+
diag2(
|
|
304
|
+
"N310",
|
|
305
|
+
"effect() callback returned a Promise \u2014 effects must be synchronous. Async state belongs in resource(). The returned Promise is not a cleanup function; its rejection is contained by the effect error path.",
|
|
306
|
+
node.createdAt ? { effect: node.createdAt } : void 0
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
result.then(void 0, (err) => {
|
|
310
|
+
console.error("Effect error:", err);
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
} catch (err) {
|
|
314
|
+
console.error("Effect error:", err);
|
|
315
|
+
} finally {
|
|
316
|
+
activeObserver = prevObserver;
|
|
317
|
+
if (true) runningEffect = prevRunning;
|
|
318
|
+
node.lastRunEpoch = globalEpoch;
|
|
319
|
+
}
|
|
320
|
+
if (pendingEffects.has(node)) {
|
|
321
|
+
if (++node.selfReschedules > MAX_EFFECT_RERUNS) {
|
|
322
|
+
if (isDev()) diag2(
|
|
323
|
+
"N301",
|
|
324
|
+
`Effect exceeded maximum re-run limit (${MAX_EFFECT_RERUNS}). This usually means the effect writes to a signal it reads. The effect has been disposed to prevent a UI freeze.`,
|
|
325
|
+
node.createdAt ? { effect: node.createdAt } : void 0
|
|
326
|
+
);
|
|
327
|
+
node.disposed = true;
|
|
328
|
+
for (const source of node.sources) {
|
|
329
|
+
source.observers.delete(node);
|
|
330
|
+
}
|
|
331
|
+
node.sources.clear();
|
|
332
|
+
pendingEffects.delete(node);
|
|
333
|
+
}
|
|
334
|
+
} else {
|
|
335
|
+
node.selfReschedules = 0;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
function signal(initialValue) {
|
|
339
|
+
return new SignalImpl(initialValue);
|
|
340
|
+
}
|
|
341
|
+
function computed(fn) {
|
|
342
|
+
return new ComputedImpl(fn);
|
|
343
|
+
}
|
|
344
|
+
function effect(fn, ..._rejectAsync) {
|
|
345
|
+
const node = createEffectNode(fn);
|
|
346
|
+
if (isDev()) {
|
|
347
|
+
node.createdAt = new Error("effect() created here").stack;
|
|
348
|
+
}
|
|
349
|
+
runEffect(node);
|
|
350
|
+
const dispose = () => {
|
|
351
|
+
node.disposed = true;
|
|
352
|
+
if (node.cleanup) {
|
|
353
|
+
try {
|
|
354
|
+
node.cleanup();
|
|
355
|
+
} catch (_) {
|
|
356
|
+
}
|
|
357
|
+
node.cleanup = null;
|
|
358
|
+
}
|
|
359
|
+
for (const source of node.sources) {
|
|
360
|
+
source.observers.delete(node);
|
|
361
|
+
}
|
|
362
|
+
node.sources.clear();
|
|
363
|
+
pendingEffects.delete(node);
|
|
364
|
+
};
|
|
365
|
+
if (contextHook) {
|
|
366
|
+
const addDisposer = contextHook();
|
|
367
|
+
if (addDisposer) {
|
|
368
|
+
addDisposer(dispose);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
return disposable(dispose, dispose);
|
|
372
|
+
}
|
|
373
|
+
function isSignal(value) {
|
|
374
|
+
return value !== null && typeof value === "object" && SIGNAL_BRAND in value;
|
|
375
|
+
}
|
|
376
|
+
function untrack(fn) {
|
|
377
|
+
const prev = activeObserver;
|
|
378
|
+
activeObserver = null;
|
|
379
|
+
try {
|
|
380
|
+
return fn();
|
|
381
|
+
} finally {
|
|
382
|
+
activeObserver = prev;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
function flush() {
|
|
386
|
+
let guard = 0;
|
|
387
|
+
while (pendingEffects.size > 0) {
|
|
388
|
+
flushPendingEffects();
|
|
389
|
+
if (++guard > 1e5) {
|
|
390
|
+
if (isDev()) diag2(
|
|
391
|
+
"N303",
|
|
392
|
+
"flush() cascade cap reached (100000 passes) \u2014 effects keep scheduling more effects (a cross-effect write cycle?). Breaking out to avoid a freeze; pending work remains unflushed."
|
|
393
|
+
);
|
|
394
|
+
break;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
async function tick() {
|
|
399
|
+
flush();
|
|
400
|
+
for (let i = 0; i < 50; i++) {
|
|
401
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
402
|
+
if (pendingEffects.size === 0) return;
|
|
403
|
+
flush();
|
|
404
|
+
}
|
|
405
|
+
if (isDev()) diag2(
|
|
406
|
+
"N303",
|
|
407
|
+
"tick() iteration cap (50) reached with effects still pending \u2014 something keeps scheduling reactive work every task (a self-perpetuating scheduler?). Returning with work still queued."
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// src/context.ts
|
|
412
|
+
var currentComponent = null;
|
|
413
|
+
function runWithContext(component2, fn) {
|
|
414
|
+
const prev = currentComponent;
|
|
415
|
+
currentComponent = component2;
|
|
416
|
+
try {
|
|
417
|
+
fn();
|
|
418
|
+
} finally {
|
|
419
|
+
currentComponent = prev;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
function getCurrentComponent() {
|
|
423
|
+
if (!currentComponent) {
|
|
424
|
+
throw new Error(
|
|
425
|
+
"getCurrentComponent() called outside setup(). inject(), effect(), and other context-dependent functions can only be called during component initialization."
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
return currentComponent;
|
|
429
|
+
}
|
|
430
|
+
function hasContext() {
|
|
431
|
+
return currentComponent !== null;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// src/emitter.ts
|
|
435
|
+
var Emitter = class {
|
|
436
|
+
listeners = /* @__PURE__ */ new Map();
|
|
437
|
+
/**
|
|
438
|
+
* Emit a typed event. All subscribers for this event name are called
|
|
439
|
+
* synchronously. If a subscriber throws, other subscribers still fire.
|
|
440
|
+
*/
|
|
441
|
+
emit(event, payload) {
|
|
442
|
+
const set = this.listeners.get(event);
|
|
443
|
+
if (!set) return;
|
|
444
|
+
for (const fn of [...set]) {
|
|
445
|
+
try {
|
|
446
|
+
fn(payload);
|
|
447
|
+
} catch (err) {
|
|
448
|
+
console.error(`Emitter: subscriber for '${String(event)}' threw:`, err);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Subscribe to a typed event. Returns an unsubscribe function.
|
|
454
|
+
*
|
|
455
|
+
* If called during component setup, the subscription is automatically
|
|
456
|
+
* disposed on disconnectedCallback.
|
|
457
|
+
*/
|
|
458
|
+
on(event, fn) {
|
|
459
|
+
let set = this.listeners.get(event);
|
|
460
|
+
if (!set) {
|
|
461
|
+
set = /* @__PURE__ */ new Set();
|
|
462
|
+
this.listeners.set(event, set);
|
|
463
|
+
}
|
|
464
|
+
set.add(fn);
|
|
465
|
+
const unsubscribe = () => {
|
|
466
|
+
set.delete(fn);
|
|
467
|
+
if (set.size === 0) {
|
|
468
|
+
this.listeners.delete(event);
|
|
469
|
+
}
|
|
470
|
+
};
|
|
471
|
+
if (hasContext()) {
|
|
472
|
+
getCurrentComponent().addDisposer(unsubscribe);
|
|
473
|
+
}
|
|
474
|
+
return disposable(unsubscribe, unsubscribe);
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* Bridge an event into the signal system.
|
|
478
|
+
* Returns a signal that updates whenever the event fires.
|
|
479
|
+
*
|
|
480
|
+
* @param event - Event name to subscribe to
|
|
481
|
+
* @param selector - Transform the payload into the signal value
|
|
482
|
+
* @param initial - Initial signal value before any event fires
|
|
483
|
+
*/
|
|
484
|
+
toSignal(event, selector, initial) {
|
|
485
|
+
const s = signal(initial);
|
|
486
|
+
this.on(event, (payload) => {
|
|
487
|
+
s.value = selector(payload);
|
|
488
|
+
});
|
|
489
|
+
return s;
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Remove all subscribers. Useful for testing cleanup.
|
|
493
|
+
*/
|
|
494
|
+
clear() {
|
|
495
|
+
this.listeners.clear();
|
|
496
|
+
}
|
|
497
|
+
};
|
|
498
|
+
|
|
499
|
+
// src/injector.ts
|
|
500
|
+
var singletonCache = /* @__PURE__ */ new Map();
|
|
501
|
+
var factoryOverrides = /* @__PURE__ */ new Map();
|
|
502
|
+
var instantiating = /* @__PURE__ */ new Set();
|
|
503
|
+
function inject(token) {
|
|
504
|
+
if (singletonCache.has(token)) {
|
|
505
|
+
return singletonCache.get(token);
|
|
506
|
+
}
|
|
507
|
+
if (instantiating.has(token)) {
|
|
508
|
+
const name = typeof token === "function" ? token.name : token.name;
|
|
509
|
+
throw new Error(
|
|
510
|
+
`Circular dependency detected when instantiating '${name}'. Check your service constructors for circular inject() calls.`
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
instantiating.add(token);
|
|
514
|
+
try {
|
|
515
|
+
let instance;
|
|
516
|
+
const factory = factoryOverrides.get(token);
|
|
517
|
+
if (factory) {
|
|
518
|
+
instance = factory();
|
|
519
|
+
} else if (typeof token === "function") {
|
|
520
|
+
instance = new token();
|
|
521
|
+
} else {
|
|
522
|
+
const injToken = token;
|
|
523
|
+
if (injToken._factory) {
|
|
524
|
+
instance = injToken._factory();
|
|
525
|
+
} else {
|
|
526
|
+
throw new Error(
|
|
527
|
+
`No provider found for token '${injToken.name}'. Use provide() to register a factory before calling inject().`
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
singletonCache.set(token, instance);
|
|
532
|
+
return instance;
|
|
533
|
+
} finally {
|
|
534
|
+
instantiating.delete(token);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
function provide(token, factory) {
|
|
538
|
+
if (isDev() && singletonCache.has(token)) {
|
|
539
|
+
const name = typeof token === "function" ? token.name : token.name;
|
|
540
|
+
throw new Error(formatDiag(
|
|
541
|
+
"N501",
|
|
542
|
+
`provide(${name}) after '${name}' was already instantiated \u2014 existing consumers keep the old instance while new inject() calls would get the new one (mixed-instance split). Call resetInjector() first (tests), or provide() before the first inject().`
|
|
543
|
+
));
|
|
544
|
+
}
|
|
545
|
+
factoryOverrides.set(token, factory);
|
|
546
|
+
singletonCache.delete(token);
|
|
547
|
+
}
|
|
548
|
+
function createToken(name, defaultFactory) {
|
|
549
|
+
return {
|
|
550
|
+
__brand: "InjectionToken",
|
|
551
|
+
name,
|
|
552
|
+
_factory: defaultFactory
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
var resetHooks = /* @__PURE__ */ new Set();
|
|
556
|
+
function registerResetHook(hook) {
|
|
557
|
+
resetHooks.add(hook);
|
|
558
|
+
}
|
|
559
|
+
function resetInjector() {
|
|
560
|
+
singletonCache.clear();
|
|
561
|
+
factoryOverrides.clear();
|
|
562
|
+
instantiating.clear();
|
|
563
|
+
for (const hook of resetHooks) hook();
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// src/element-context.ts
|
|
567
|
+
function createContext(name, options = {}) {
|
|
568
|
+
const { providerTag } = options;
|
|
569
|
+
const KEY = /* @__PURE__ */ Symbol(`nisli.context:${name}`);
|
|
570
|
+
const store = (el2) => el2;
|
|
571
|
+
const provide2 = (host, value) => {
|
|
572
|
+
store(host)[KEY] = value;
|
|
573
|
+
};
|
|
574
|
+
const peek2 = (host) => KEY in host ? store(host)[KEY] : void 0;
|
|
575
|
+
const find = (start) => {
|
|
576
|
+
let el2 = start;
|
|
577
|
+
while (el2) {
|
|
578
|
+
if (KEY in el2) return store(el2)[KEY];
|
|
579
|
+
el2 = el2.parentElement;
|
|
580
|
+
}
|
|
581
|
+
return void 0;
|
|
582
|
+
};
|
|
583
|
+
const startHost = (host, caller) => {
|
|
584
|
+
if (host) return host;
|
|
585
|
+
if (!hasContext()) {
|
|
586
|
+
throw new Error(
|
|
587
|
+
`${caller} for context "${name}" was called without a host and outside component setup. Call it during setup (so the provider is resolved before any portal move), or pass an explicit host.`
|
|
588
|
+
);
|
|
589
|
+
}
|
|
590
|
+
return getCurrentComponent().element;
|
|
591
|
+
};
|
|
592
|
+
const inject2 = ((host) => {
|
|
593
|
+
const start = startHost(host, "inject()");
|
|
594
|
+
const found = find(start);
|
|
595
|
+
if (found === void 0) {
|
|
596
|
+
const injectingTag = start.tagName.toLowerCase();
|
|
597
|
+
throw new Error(
|
|
598
|
+
providerTag ? `Context "${name}": <${injectingTag}> must be used inside <${providerTag}>.` : `Context "${name}" not found: <${injectingTag}> must be rendered inside a provider that calls ${name}Context.provide(host, \u2026).`
|
|
599
|
+
);
|
|
600
|
+
}
|
|
601
|
+
return found;
|
|
602
|
+
});
|
|
603
|
+
inject2.optional = (host) => {
|
|
604
|
+
if (!host && !hasContext()) return void 0;
|
|
605
|
+
return find(startHost(host, "inject.optional()"));
|
|
606
|
+
};
|
|
607
|
+
return { name, provide: provide2, inject: inject2, peek: peek2 };
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
// src/lifecycle.ts
|
|
611
|
+
var mountCallbacks = /* @__PURE__ */ new WeakMap();
|
|
612
|
+
function onMount(callback) {
|
|
613
|
+
if (!hasContext()) {
|
|
614
|
+
throw new Error(
|
|
615
|
+
"onMount() called outside setup(). It can only be called during component initialization."
|
|
616
|
+
);
|
|
617
|
+
}
|
|
618
|
+
const comp = getCurrentComponent();
|
|
619
|
+
let callbacks = mountCallbacks.get(comp);
|
|
620
|
+
if (!callbacks) {
|
|
621
|
+
callbacks = [];
|
|
622
|
+
mountCallbacks.set(comp, callbacks);
|
|
623
|
+
}
|
|
624
|
+
callbacks.push(callback);
|
|
625
|
+
}
|
|
626
|
+
function onCleanup(callback) {
|
|
627
|
+
if (!hasContext()) {
|
|
628
|
+
throw new Error(
|
|
629
|
+
"onCleanup() called outside setup(). It can only be called during component initialization."
|
|
630
|
+
);
|
|
631
|
+
}
|
|
632
|
+
getCurrentComponent().addDisposer(callback);
|
|
633
|
+
}
|
|
634
|
+
function useHostEvent(target, eventName, handler) {
|
|
635
|
+
if (!hasContext()) {
|
|
636
|
+
throw new Error(
|
|
637
|
+
"useHostEvent() called outside setup(). It can only be called during component initialization."
|
|
638
|
+
);
|
|
639
|
+
}
|
|
640
|
+
target.addEventListener(eventName, handler);
|
|
641
|
+
getCurrentComponent().addDisposer(() => {
|
|
642
|
+
target.removeEventListener(eventName, handler);
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
function runMountCallbacks(comp) {
|
|
646
|
+
const callbacks = mountCallbacks.get(comp);
|
|
647
|
+
if (!callbacks) return;
|
|
648
|
+
try {
|
|
649
|
+
for (const cb of callbacks) {
|
|
650
|
+
const cleanup = cb();
|
|
651
|
+
if (typeof cleanup === "function") {
|
|
652
|
+
comp.addDisposer(cleanup);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
} finally {
|
|
656
|
+
mountCallbacks.delete(comp);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
// src/component.ts
|
|
661
|
+
var propsReadKeys = true ? /* @__PURE__ */ new WeakMap() : void 0;
|
|
662
|
+
var completedSetups = true ? /* @__PURE__ */ new WeakSet() : void 0;
|
|
663
|
+
var echoedProps = true ? /* @__PURE__ */ new WeakMap() : void 0;
|
|
664
|
+
setContextHook(() => {
|
|
665
|
+
if (hasContext()) {
|
|
666
|
+
const comp = getCurrentComponent();
|
|
667
|
+
return (disposer) => comp.addDisposer(disposer);
|
|
668
|
+
}
|
|
669
|
+
return null;
|
|
670
|
+
});
|
|
671
|
+
var camelToKebab = (s) => s.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
|
|
672
|
+
var declType = (decl) => typeof decl === "string" ? decl : decl.type;
|
|
673
|
+
var declAttrName = (key, decl) => typeof decl === "object" && decl.attr ? decl.attr : camelToKebab(key);
|
|
674
|
+
function resolveAttrValue(decl, raw2) {
|
|
675
|
+
const t = declType(decl);
|
|
676
|
+
if (t === "boolean") {
|
|
677
|
+
const def = typeof decl === "object" && "default" in decl && decl.default !== void 0 ? decl.default : false;
|
|
678
|
+
if (raw2 === null) return def;
|
|
679
|
+
return raw2 !== "false";
|
|
680
|
+
}
|
|
681
|
+
if (t === "number") {
|
|
682
|
+
const def = typeof decl === "object" && "default" in decl ? decl.default : void 0;
|
|
683
|
+
if (raw2 === null) return def;
|
|
684
|
+
const n = Number(raw2);
|
|
685
|
+
return Number.isNaN(n) ? def : n;
|
|
686
|
+
}
|
|
687
|
+
if (raw2 === null) {
|
|
688
|
+
return typeof decl === "object" && "default" in decl ? decl.default : void 0;
|
|
689
|
+
}
|
|
690
|
+
return raw2;
|
|
691
|
+
}
|
|
692
|
+
var ComponentHostImpl = class {
|
|
693
|
+
element;
|
|
694
|
+
disposers = [];
|
|
695
|
+
setupComplete = false;
|
|
696
|
+
constructor(element) {
|
|
697
|
+
this.element = element;
|
|
698
|
+
}
|
|
699
|
+
addDisposer(fn) {
|
|
700
|
+
this.disposers.push(fn);
|
|
701
|
+
}
|
|
702
|
+
dispose() {
|
|
703
|
+
for (const fn of this.disposers) {
|
|
704
|
+
try {
|
|
705
|
+
fn();
|
|
706
|
+
} catch (_) {
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
this.disposers.length = 0;
|
|
710
|
+
}
|
|
711
|
+
};
|
|
712
|
+
function createPropsProxy() {
|
|
713
|
+
const signals = /* @__PURE__ */ new Map();
|
|
714
|
+
const getOrCreate = (key, initialValue) => {
|
|
715
|
+
let s = signals.get(key);
|
|
716
|
+
if (!s) {
|
|
717
|
+
s = signal(initialValue);
|
|
718
|
+
signals.set(key, s);
|
|
719
|
+
}
|
|
720
|
+
return s;
|
|
721
|
+
};
|
|
722
|
+
const props = new Proxy({}, {
|
|
723
|
+
get(_target, key) {
|
|
724
|
+
if (typeof key !== "string") return void 0;
|
|
725
|
+
if (true) propsReadKeys.get(props).add(key);
|
|
726
|
+
return getOrCreate(key);
|
|
727
|
+
}
|
|
728
|
+
});
|
|
729
|
+
if (true) propsReadKeys.set(props, /* @__PURE__ */ new Set());
|
|
730
|
+
const setProperty = (key, value) => {
|
|
731
|
+
const s = getOrCreate(key, value);
|
|
732
|
+
s.value = value;
|
|
733
|
+
};
|
|
734
|
+
return { props, setProperty };
|
|
735
|
+
}
|
|
736
|
+
var firstDefineSites = true ? /* @__PURE__ */ new Map() : void 0;
|
|
737
|
+
function captureDefineSite() {
|
|
738
|
+
const stack = new Error().stack;
|
|
739
|
+
if (!stack) return "(site unavailable)";
|
|
740
|
+
const lines = stack.split("\n");
|
|
741
|
+
for (let i = 1; i < lines.length; i++) {
|
|
742
|
+
const line = lines[i].trim();
|
|
743
|
+
if (!line) continue;
|
|
744
|
+
if (line.includes("captureDefineSite") || /[/\\]component\.(?:ts|js)\b/.test(line)) continue;
|
|
745
|
+
return line;
|
|
746
|
+
}
|
|
747
|
+
return "(site unavailable)";
|
|
748
|
+
}
|
|
749
|
+
function component(tagName, setup, ...[options]) {
|
|
750
|
+
const attrEntries = /* @__PURE__ */ new Map();
|
|
751
|
+
const attrEntriesByKey = /* @__PURE__ */ new Map();
|
|
752
|
+
if (options?.attrs) {
|
|
753
|
+
for (const key of Object.keys(options.attrs)) {
|
|
754
|
+
const decl = options.attrs[key];
|
|
755
|
+
const attrName = declAttrName(key, decl);
|
|
756
|
+
const entry = { key, attrName, decl };
|
|
757
|
+
attrEntries.set(attrName, entry);
|
|
758
|
+
attrEntriesByKey.set(key, entry);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
const observedList = Array.from(attrEntries.keys());
|
|
762
|
+
class FrameworkComponent extends HTMLElement {
|
|
763
|
+
_host = null;
|
|
764
|
+
_propsProxy = null;
|
|
765
|
+
_mounted = false;
|
|
766
|
+
_templateResult = null;
|
|
767
|
+
_errorTemplateResult = null;
|
|
768
|
+
_teardownScheduled = false;
|
|
769
|
+
// Props explicitly set via _setProp (factory / direct assignment) are
|
|
770
|
+
// "pinned": an explicit prop wins, so attribute writes no longer overwrite
|
|
771
|
+
// it. (Precedence ruling: prop-pins.)
|
|
772
|
+
_pinned = /* @__PURE__ */ new Set();
|
|
773
|
+
// Guards our own removeAttribute() during 'forward' relocation from
|
|
774
|
+
// re-entering attributeChangedCallback.
|
|
775
|
+
_relocating = false;
|
|
776
|
+
/** The browser reads this once at define time. Empty → no callbacks. */
|
|
777
|
+
static get observedAttributes() {
|
|
778
|
+
return observedList;
|
|
779
|
+
}
|
|
780
|
+
constructor() {
|
|
781
|
+
super();
|
|
782
|
+
this._propsProxy = createPropsProxy();
|
|
783
|
+
}
|
|
784
|
+
/** Write one declared attribute onto its prop signal, honoring pinning. */
|
|
785
|
+
_applyAttr(entry, raw2) {
|
|
786
|
+
if (declType(entry.decl) === "forward") {
|
|
787
|
+
if (raw2 !== null) {
|
|
788
|
+
if (!this._pinned.has(entry.key)) {
|
|
789
|
+
this._propsProxy.setProperty(entry.key, raw2);
|
|
790
|
+
}
|
|
791
|
+
this._relocating = true;
|
|
792
|
+
this.removeAttribute(entry.attrName);
|
|
793
|
+
this._relocating = false;
|
|
794
|
+
}
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
if (this._pinned.has(entry.key)) return;
|
|
798
|
+
this._propsProxy.setProperty(entry.key, resolveAttrValue(entry.decl, raw2));
|
|
799
|
+
}
|
|
800
|
+
/** Seed initial prop values from current attributes (incl. defaults). */
|
|
801
|
+
_seedAttrs() {
|
|
802
|
+
for (const entry of attrEntries.values()) {
|
|
803
|
+
this._applyAttr(entry, this.getAttribute(entry.attrName));
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
attributeChangedCallback(name, _old, next) {
|
|
807
|
+
if (this._relocating) return;
|
|
808
|
+
const entry = attrEntries.get(name);
|
|
809
|
+
if (entry) this._applyAttr(entry, next);
|
|
810
|
+
}
|
|
811
|
+
connectedCallback() {
|
|
812
|
+
if (this._mounted) return;
|
|
813
|
+
this._mounted = true;
|
|
814
|
+
untrack(() => {
|
|
815
|
+
const host = new ComponentHostImpl(this);
|
|
816
|
+
this._host = host;
|
|
817
|
+
if (attrEntries.size) this._seedAttrs();
|
|
818
|
+
if (this.childNodes.length) {
|
|
819
|
+
this.__capturedChildren = Array.from(this.childNodes);
|
|
820
|
+
}
|
|
821
|
+
let phase = "setup";
|
|
822
|
+
try {
|
|
823
|
+
runWithContext(host, () => {
|
|
824
|
+
this._templateResult = setup(
|
|
825
|
+
this._propsProxy.props,
|
|
826
|
+
this
|
|
827
|
+
);
|
|
828
|
+
});
|
|
829
|
+
if (this._templateResult) {
|
|
830
|
+
mountTemplate(this, this._templateResult, host);
|
|
831
|
+
}
|
|
832
|
+
const sweepHost = this;
|
|
833
|
+
if (sweepHost.__nisliProjectionSweep) {
|
|
834
|
+
const sweep = sweepHost.__nisliProjectionSweep;
|
|
835
|
+
sweepHost.__nisliProjectionSweep = void 0;
|
|
836
|
+
sweep();
|
|
837
|
+
}
|
|
838
|
+
phase = "mount";
|
|
839
|
+
runMountCallbacks(host);
|
|
840
|
+
if (true) completedSetups.add(this);
|
|
841
|
+
this.removeAttribute("data-nisli-error");
|
|
842
|
+
} catch (err) {
|
|
843
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
844
|
+
const code = phase === "setup" ? "N401" : "N402";
|
|
845
|
+
diag(code, `<${tagName}> ${phase} failed: ${error.message}`, {
|
|
846
|
+
tag: tagName,
|
|
847
|
+
phase,
|
|
848
|
+
error
|
|
849
|
+
});
|
|
850
|
+
this.__nisliProjectionSweep = void 0;
|
|
851
|
+
if (this._templateResult?.dispose) {
|
|
852
|
+
this._templateResult.dispose();
|
|
853
|
+
}
|
|
854
|
+
this._templateResult = null;
|
|
855
|
+
host.dispose();
|
|
856
|
+
this._host = null;
|
|
857
|
+
this.replaceChildren();
|
|
858
|
+
this.setAttribute("data-nisli-error", code);
|
|
859
|
+
if (options?.onError) {
|
|
860
|
+
try {
|
|
861
|
+
const fallback = options.onError(error, this);
|
|
862
|
+
if (typeof fallback === "string") {
|
|
863
|
+
this.innerHTML = fallback;
|
|
864
|
+
} else {
|
|
865
|
+
this._errorTemplateResult = fallback;
|
|
866
|
+
mountTemplate(this, fallback);
|
|
867
|
+
}
|
|
868
|
+
} catch (_) {
|
|
869
|
+
if (this._errorTemplateResult?.dispose) {
|
|
870
|
+
this._errorTemplateResult.dispose();
|
|
871
|
+
}
|
|
872
|
+
this._errorTemplateResult = null;
|
|
873
|
+
this.innerHTML = `<div style="color:red;padding:4px">Error in <${tagName}></div>`;
|
|
874
|
+
}
|
|
875
|
+
} else {
|
|
876
|
+
this.innerHTML = `<div style="color:red;padding:4px">Error in <${tagName}></div>`;
|
|
877
|
+
}
|
|
878
|
+
this.dispatchEvent(
|
|
879
|
+
new CustomEvent("nisli-error", {
|
|
880
|
+
bubbles: true,
|
|
881
|
+
composed: true,
|
|
882
|
+
detail: { code, tag: tagName, phase, message: error.message }
|
|
883
|
+
})
|
|
884
|
+
);
|
|
885
|
+
}
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
disconnectedCallback() {
|
|
889
|
+
if (this._teardownScheduled) return;
|
|
890
|
+
this._teardownScheduled = true;
|
|
891
|
+
queueMicrotask(() => {
|
|
892
|
+
this._teardownScheduled = false;
|
|
893
|
+
if (this.isConnected) return;
|
|
894
|
+
this._teardownNow();
|
|
895
|
+
});
|
|
896
|
+
}
|
|
897
|
+
/**
|
|
898
|
+
* Atomic DOM moves preserve this component's position-independent state,
|
|
899
|
+
* so the platform-level lifecycle is a no-op. ADR 0023's deferred
|
|
900
|
+
* disconnect teardown remains the fallback for append-based moves.
|
|
901
|
+
*/
|
|
902
|
+
connectedMoveCallback() {
|
|
903
|
+
}
|
|
904
|
+
/**
|
|
905
|
+
* Synchronously dispose the current component scope and restore any
|
|
906
|
+
* projected light-DOM children as direct host children. Normal disconnect
|
|
907
|
+
* calls this after the move-detection microtask; HMR calls it directly so
|
|
908
|
+
* reconnect cannot be defeated by the `_mounted` guard (issues 0001/0002).
|
|
909
|
+
*/
|
|
910
|
+
_teardownNow() {
|
|
911
|
+
this._mounted = false;
|
|
912
|
+
if (true) completedSetups.delete(this);
|
|
913
|
+
if (this._templateResult?.dispose) {
|
|
914
|
+
this._templateResult.dispose();
|
|
915
|
+
}
|
|
916
|
+
this._templateResult = null;
|
|
917
|
+
if (this._errorTemplateResult?.dispose) {
|
|
918
|
+
this._errorTemplateResult.dispose();
|
|
919
|
+
}
|
|
920
|
+
this._errorTemplateResult = null;
|
|
921
|
+
if (this._host) {
|
|
922
|
+
this._host.dispose();
|
|
923
|
+
this._host = null;
|
|
924
|
+
}
|
|
925
|
+
const captured = this.__capturedChildren ?? [];
|
|
926
|
+
this.replaceChildren(...captured);
|
|
927
|
+
}
|
|
928
|
+
/** @internal — transport-agnostic HMR in-place remount hook. */
|
|
929
|
+
_remount() {
|
|
930
|
+
this._teardownScheduled = false;
|
|
931
|
+
this._teardownNow();
|
|
932
|
+
this.connectedCallback();
|
|
933
|
+
}
|
|
934
|
+
/**
|
|
935
|
+
* Set a prop value. Called by the factory and by direct property assignment.
|
|
936
|
+
* The Proxy intercepts and creates/updates backing signals.
|
|
937
|
+
*
|
|
938
|
+
* Precedence = DEFINED-write-pins (ADR 0025 item 3): a defined
|
|
939
|
+
* value pins the key so a declared attribute no longer overwrites it. An
|
|
940
|
+
* explicit `undefined` (e.g. a factory spreading an unset optional prop —
|
|
941
|
+
* `{...opts}` fires _setProp(key, undefined) per the Object.entries walk)
|
|
942
|
+
* must NOT pin; it falls back to the declared attribute/default, matching
|
|
943
|
+
* attr()'s coalesce. For a declared attribute, that fallback re-resolves
|
|
944
|
+
* from the current attribute, keeping declared-default booleans
|
|
945
|
+
* non-undefined.
|
|
946
|
+
*/
|
|
947
|
+
_setProp(key, value) {
|
|
948
|
+
if (completedSetups.has(this) && value !== void 0 && isDev() && !attrEntriesByKey.has(key) && !propsReadKeys.get(this._propsProxy.props).has(key) && !echoedProps.get(this)?.has(key)) {
|
|
949
|
+
const echoed = echoedProps.get(this) ?? /* @__PURE__ */ new Set();
|
|
950
|
+
echoed.add(key);
|
|
951
|
+
echoedProps.set(this, echoed);
|
|
952
|
+
diag(
|
|
953
|
+
"N202",
|
|
954
|
+
`<${tagName}> prop '${key}' was set but setup() never read it \u2014 misspelled or unsupported prop?`,
|
|
955
|
+
{ tag: tagName, key }
|
|
956
|
+
);
|
|
957
|
+
}
|
|
958
|
+
if (value === void 0) {
|
|
959
|
+
this._pinned.delete(key);
|
|
960
|
+
const entry = attrEntriesByKey.get(key);
|
|
961
|
+
if (entry) {
|
|
962
|
+
const raw2 = this.getAttribute(entry.attrName);
|
|
963
|
+
if (raw2 === null && declType(entry.decl) === "forward") {
|
|
964
|
+
this._propsProxy?.setProperty(key, resolveAttrValue(entry.decl, null));
|
|
965
|
+
} else {
|
|
966
|
+
this._applyAttr(entry, raw2);
|
|
967
|
+
}
|
|
968
|
+
return;
|
|
969
|
+
}
|
|
970
|
+
} else {
|
|
971
|
+
this._pinned.add(key);
|
|
972
|
+
}
|
|
973
|
+
this._propsProxy?.setProperty(key, value);
|
|
974
|
+
}
|
|
975
|
+
/**
|
|
976
|
+
* Read a prop's backing signal (framework-internal). Used by children()
|
|
977
|
+
* to fold the factory `children` prop into the projection slot (ADR 0025
|
|
978
|
+
* item 1) without a new public read API.
|
|
979
|
+
*/
|
|
980
|
+
_propSignal(key) {
|
|
981
|
+
return this._propsProxy.props[key];
|
|
982
|
+
}
|
|
983
|
+
/**
|
|
984
|
+
* Whether a prop is currently pinned by a defined factory/property write
|
|
985
|
+
* (framework-internal, like _propSignal). The attribute-as-truth open-state
|
|
986
|
+
* pattern needs this as its controlled-mode discriminator: a declared
|
|
987
|
+
* 'boolean' prop is never undefined (absent → false), so pin state is the
|
|
988
|
+
* ONLY way a component can tell "parent drives this" from "the attribute
|
|
989
|
+
* drives this" — e.g. setOpen() must not write the open attribute while a
|
|
990
|
+
* controlled parent has it pinned, or the DOM attribute diverges from the
|
|
991
|
+
* pinned state until the next prop write.
|
|
992
|
+
*/
|
|
993
|
+
_isPinned(key) {
|
|
994
|
+
return this._pinned.has(key);
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
if (!customElements.get(tagName)) {
|
|
998
|
+
customElements.define(tagName, FrameworkComponent);
|
|
999
|
+
if (isDev()) firstDefineSites.set(tagName, captureDefineSite());
|
|
1000
|
+
} else if (isDev() && !setup.__nisliHmr) {
|
|
1001
|
+
const first = firstDefineSites.get(tagName) ?? "(unknown site \u2014 defined outside component(), or before dev mode was enabled)";
|
|
1002
|
+
throw new Error(formatDiag(
|
|
1003
|
+
"N201",
|
|
1004
|
+
`duplicate define for <${tagName}>. First defined ${first}; the second definition site is this error's own stack. The first definition stays live (first-wins) \u2014 rename one of the components.`
|
|
1005
|
+
));
|
|
1006
|
+
}
|
|
1007
|
+
const factory = (props, hostAttrs) => {
|
|
1008
|
+
return {
|
|
1009
|
+
__type: "factory",
|
|
1010
|
+
tagName,
|
|
1011
|
+
props,
|
|
1012
|
+
hostAttrs
|
|
1013
|
+
};
|
|
1014
|
+
};
|
|
1015
|
+
return factory;
|
|
1016
|
+
}
|
|
1017
|
+
function ensureSignal(value) {
|
|
1018
|
+
if (isSignal(value)) return value;
|
|
1019
|
+
return signal(value);
|
|
1020
|
+
}
|
|
1021
|
+
function mountTemplate(host, result, hostImpl) {
|
|
1022
|
+
if (result && typeof result === "object" && "mount" in result && typeof result.mount === "function") {
|
|
1023
|
+
result.mount(host);
|
|
1024
|
+
} else if (result && typeof result === "object" && "__type" in result) {
|
|
1025
|
+
const factoryResult = result;
|
|
1026
|
+
const el2 = document.createElement(factoryResult.tagName);
|
|
1027
|
+
for (const [key, raw2] of Object.entries(factoryResult.props)) {
|
|
1028
|
+
const sig = ensureSignal(raw2);
|
|
1029
|
+
el2._setProp(key, sig.value);
|
|
1030
|
+
const unsub = sig.subscribe((newVal) => {
|
|
1031
|
+
el2._setProp(key, newVal);
|
|
1032
|
+
});
|
|
1033
|
+
if (hostImpl) {
|
|
1034
|
+
hostImpl.addDisposer(unsub);
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
if (factoryResult.hostAttrs?.class != null) {
|
|
1038
|
+
const classSig = ensureSignal(factoryResult.hostAttrs.class);
|
|
1039
|
+
applyHostClass(el2, "", classSig.value);
|
|
1040
|
+
let prevClass = classSig.value;
|
|
1041
|
+
const unsub = classSig.subscribe((newClass) => {
|
|
1042
|
+
applyHostClass(el2, prevClass, newClass);
|
|
1043
|
+
prevClass = newClass;
|
|
1044
|
+
});
|
|
1045
|
+
if (hostImpl) {
|
|
1046
|
+
hostImpl.addDisposer(unsub);
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
host.appendChild(el2);
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
function applyHostClass(el2, prev, next) {
|
|
1053
|
+
const prevClasses = prev ? prev.split(/\s+/).filter(Boolean) : [];
|
|
1054
|
+
const nextClasses = next ? next.split(/\s+/).filter(Boolean) : [];
|
|
1055
|
+
for (const cls of prevClasses) {
|
|
1056
|
+
if (!nextClasses.includes(cls)) el2.classList.remove(cls);
|
|
1057
|
+
}
|
|
1058
|
+
for (const cls of nextClasses) {
|
|
1059
|
+
if (!prevClasses.includes(cls)) el2.classList.add(cls);
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
// src/ref.ts
|
|
1064
|
+
var REF_BRAND = /* @__PURE__ */ Symbol.for("backlog.ref");
|
|
1065
|
+
function ref() {
|
|
1066
|
+
return {
|
|
1067
|
+
[REF_BRAND]: true,
|
|
1068
|
+
current: null
|
|
1069
|
+
};
|
|
1070
|
+
}
|
|
1071
|
+
function isRef(value) {
|
|
1072
|
+
return value !== null && typeof value === "object" && REF_BRAND in value;
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
// src/template-audit.ts
|
|
1076
|
+
var enabledOverride = null;
|
|
1077
|
+
function isTemplateAuditEnabled() {
|
|
1078
|
+
return enabledOverride ?? isDev();
|
|
1079
|
+
}
|
|
1080
|
+
function diag3(code, message, detail) {
|
|
1081
|
+
if (!isTemplateAuditEnabled()) return;
|
|
1082
|
+
diag(code, message, detail === void 0 ? void 0 : { detail });
|
|
1083
|
+
}
|
|
1084
|
+
var callsites = true ? /* @__PURE__ */ new WeakMap() : void 0;
|
|
1085
|
+
function captureCallsite(strings) {
|
|
1086
|
+
if (!isTemplateAuditEnabled() || callsites.has(strings)) return;
|
|
1087
|
+
const stack = new Error().stack ?? "";
|
|
1088
|
+
const frame = stack.split("\n").slice(1).find((line) => line.includes("at ") && !/template(-audit)?\.(ts|js|mjs|cjs)/.test(line));
|
|
1089
|
+
callsites.set(strings, frame ? frame.trim() : "(unknown callsite)");
|
|
1090
|
+
}
|
|
1091
|
+
var audited = true ? /* @__PURE__ */ new WeakSet() : void 0;
|
|
1092
|
+
var KNOWN_MODIFIERS = true ? /* @__PURE__ */ new Set(["stop", "prevent", "once", "enter", "escape", "space", "tab"]) : void 0;
|
|
1093
|
+
var COMPONENT_EVENT_PREFIXES = ["ui-", "nisli-"];
|
|
1094
|
+
var NATIVE_EVENTS_WITHOUT_IDL = true ? /* @__PURE__ */ new Set(["focusin", "focusout"]) : void 0;
|
|
1095
|
+
var GLOBAL_ATTRS = true ? /* @__PURE__ */ new Set([
|
|
1096
|
+
"id",
|
|
1097
|
+
"class",
|
|
1098
|
+
"style",
|
|
1099
|
+
"slot",
|
|
1100
|
+
"title",
|
|
1101
|
+
"role",
|
|
1102
|
+
"tabindex",
|
|
1103
|
+
"name",
|
|
1104
|
+
"part",
|
|
1105
|
+
"hidden",
|
|
1106
|
+
"dir",
|
|
1107
|
+
"lang",
|
|
1108
|
+
"is"
|
|
1109
|
+
]) : void 0;
|
|
1110
|
+
function parseDirectives(content) {
|
|
1111
|
+
const directives = { off: false, allowUndefined: /* @__PURE__ */ new Set() };
|
|
1112
|
+
for (const node of content.childNodes) {
|
|
1113
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
1114
|
+
if ((node.textContent ?? "").trim() === "") continue;
|
|
1115
|
+
break;
|
|
1116
|
+
}
|
|
1117
|
+
if (node.nodeType === Node.COMMENT_NODE) {
|
|
1118
|
+
const body = (node.textContent ?? "").trim();
|
|
1119
|
+
if (!body.startsWith("nisli-audit")) break;
|
|
1120
|
+
const rest = body.slice("nisli-audit".length).trim();
|
|
1121
|
+
if (rest === "off") {
|
|
1122
|
+
directives.off = true;
|
|
1123
|
+
} else if (rest.startsWith("allow-undefined")) {
|
|
1124
|
+
const tags = rest.slice("allow-undefined".length).replace(/^[:\s]+/, "").split(/[\s,]+/).filter(Boolean);
|
|
1125
|
+
for (const tag of tags) directives.allowUndefined.add(tag.toLowerCase());
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
break;
|
|
1129
|
+
}
|
|
1130
|
+
return directives;
|
|
1131
|
+
}
|
|
1132
|
+
function checkDashTag(tag, site) {
|
|
1133
|
+
let registry;
|
|
1134
|
+
try {
|
|
1135
|
+
registry = customElements;
|
|
1136
|
+
} catch {
|
|
1137
|
+
return;
|
|
1138
|
+
}
|
|
1139
|
+
if (!registry || registry.get(tag)) return;
|
|
1140
|
+
let defined = false;
|
|
1141
|
+
try {
|
|
1142
|
+
registry.whenDefined(tag).then(
|
|
1143
|
+
() => {
|
|
1144
|
+
defined = true;
|
|
1145
|
+
},
|
|
1146
|
+
() => {
|
|
1147
|
+
}
|
|
1148
|
+
);
|
|
1149
|
+
} catch {
|
|
1150
|
+
diag3("N101", `<${tag}> is not a valid custom element name \u2014 at ${site}`);
|
|
1151
|
+
return;
|
|
1152
|
+
}
|
|
1153
|
+
queueMicrotask(() => {
|
|
1154
|
+
if (defined) return;
|
|
1155
|
+
setTimeout(() => {
|
|
1156
|
+
try {
|
|
1157
|
+
if (!isTemplateAuditEnabled() || defined || registry.get(tag)) return;
|
|
1158
|
+
diag3(
|
|
1159
|
+
"N101",
|
|
1160
|
+
`<${tag}> is used in a template but never defined (customElements.get returned undefined after grace) \u2014 at ${site}. If this tag is a lazily-loaded island, suppress with a leading <!-- nisli-audit allow-undefined: ${tag} --> comment.`
|
|
1161
|
+
);
|
|
1162
|
+
} catch {
|
|
1163
|
+
}
|
|
1164
|
+
}, 0);
|
|
1165
|
+
});
|
|
1166
|
+
}
|
|
1167
|
+
function checkEvent(el2, attrName, site) {
|
|
1168
|
+
const parts = attrName.slice(1).split(".");
|
|
1169
|
+
const eventName = parts[0] ?? "";
|
|
1170
|
+
const modifiers = parts.slice(1);
|
|
1171
|
+
const isNative = `on${eventName}` in el2 || NATIVE_EVENTS_WITHOUT_IDL.has(eventName);
|
|
1172
|
+
const isComponentEvent = COMPONENT_EVENT_PREFIXES.some((p) => eventName.startsWith(p));
|
|
1173
|
+
if (eventName && !isNative && !isComponentEvent) {
|
|
1174
|
+
diag3(
|
|
1175
|
+
"N102",
|
|
1176
|
+
`@${eventName} on <${el2.tagName.toLowerCase()}> is not a known native event (and not an allowlisted ${COMPONENT_EVENT_PREFIXES.join("/")} component event) \u2014 at ${site}`
|
|
1177
|
+
);
|
|
1178
|
+
}
|
|
1179
|
+
for (const mod of modifiers) {
|
|
1180
|
+
if (!KNOWN_MODIFIERS.has(mod)) {
|
|
1181
|
+
diag3(
|
|
1182
|
+
"N103",
|
|
1183
|
+
`unknown event modifier ".${mod}" in "${attrName}" \u2014 known: ${[...KNOWN_MODIFIERS].join(", ")} \u2014 at ${site}`
|
|
1184
|
+
);
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
function checkComponentAttr(tag, name, observed, site) {
|
|
1189
|
+
if (name.startsWith("data-") || name.startsWith("aria-")) return;
|
|
1190
|
+
if (GLOBAL_ATTRS.has(name) || observed.includes(name)) return;
|
|
1191
|
+
diag3(
|
|
1192
|
+
"N104",
|
|
1193
|
+
`<${tag}> declares attributes [${observed.join(", ")}] but the template sets unknown "${name}" \u2014 at ${site}. Undeclared names route through _setProp and are silently dead as attributes.`
|
|
1194
|
+
);
|
|
1195
|
+
}
|
|
1196
|
+
function auditTemplate(strings, template) {
|
|
1197
|
+
if (!isTemplateAuditEnabled() || audited.has(strings)) return;
|
|
1198
|
+
audited.add(strings);
|
|
1199
|
+
const site = callsites.get(strings) ?? "(unknown callsite)";
|
|
1200
|
+
const content = template.content;
|
|
1201
|
+
const directives = parseDirectives(content);
|
|
1202
|
+
if (directives.off) return;
|
|
1203
|
+
for (const el2 of content.querySelectorAll("*")) {
|
|
1204
|
+
const tag = el2.tagName.toLowerCase();
|
|
1205
|
+
const isDashTag = tag.includes("-");
|
|
1206
|
+
if (isDashTag && !directives.allowUndefined.has(tag)) {
|
|
1207
|
+
checkDashTag(tag, site);
|
|
1208
|
+
}
|
|
1209
|
+
let observed;
|
|
1210
|
+
if (isDashTag) {
|
|
1211
|
+
const ctor = customElements.get(tag);
|
|
1212
|
+
const list = ctor?.observedAttributes;
|
|
1213
|
+
if (Array.isArray(list) && list.length > 0) observed = list;
|
|
1214
|
+
}
|
|
1215
|
+
for (const attr of [...el2.attributes]) {
|
|
1216
|
+
const name = attr.name;
|
|
1217
|
+
if (name.startsWith("@")) {
|
|
1218
|
+
checkEvent(el2, name, site);
|
|
1219
|
+
continue;
|
|
1220
|
+
}
|
|
1221
|
+
if (name.startsWith("class:") || name === "ref" || name === "html:inner") continue;
|
|
1222
|
+
if (observed) checkComponentAttr(tag, name, observed, site);
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
// src/template.ts
|
|
1228
|
+
var canMoveWithin = (parent) => typeof parent.moveBefore === "function";
|
|
1229
|
+
function untracked(fn) {
|
|
1230
|
+
let result;
|
|
1231
|
+
untrack(() => {
|
|
1232
|
+
result = fn();
|
|
1233
|
+
});
|
|
1234
|
+
return result;
|
|
1235
|
+
}
|
|
1236
|
+
function raw(value) {
|
|
1237
|
+
return { __raw: true, value };
|
|
1238
|
+
}
|
|
1239
|
+
function isRawHtml(value) {
|
|
1240
|
+
return typeof value === "object" && value !== null && "__raw" in value && value.__raw === true && typeof value.value === "string";
|
|
1241
|
+
}
|
|
1242
|
+
function sanitized(value) {
|
|
1243
|
+
return { __sanitize: true, value };
|
|
1244
|
+
}
|
|
1245
|
+
function isSanitizedHtml(value) {
|
|
1246
|
+
return typeof value === "object" && value !== null && "__sanitize" in value && value.__sanitize === true && typeof value.value === "string";
|
|
1247
|
+
}
|
|
1248
|
+
var sanitizerFallback = null;
|
|
1249
|
+
function setSanitizerFallback(fn) {
|
|
1250
|
+
sanitizerFallback = fn;
|
|
1251
|
+
}
|
|
1252
|
+
var nativeSetHtml = (el2, html2) => {
|
|
1253
|
+
el2.setHTML(html2);
|
|
1254
|
+
};
|
|
1255
|
+
function sanitizerFor(el2) {
|
|
1256
|
+
const platform = typeof Element === "function" ? Element.prototype.setHTML : void 0;
|
|
1257
|
+
return typeof platform === "function" && el2.setHTML === platform ? nativeSetHtml : sanitizerFallback;
|
|
1258
|
+
}
|
|
1259
|
+
function isTrustedHtml(value) {
|
|
1260
|
+
const tt = globalThis.trustedTypes;
|
|
1261
|
+
return tt !== void 0 && tt.isHTML(value);
|
|
1262
|
+
}
|
|
1263
|
+
function nisliError(code, message) {
|
|
1264
|
+
return new Error(`[nisli] ${code}: ${message}`);
|
|
1265
|
+
}
|
|
1266
|
+
var templateCache = /* @__PURE__ */ new WeakMap();
|
|
1267
|
+
var parseCount = 0;
|
|
1268
|
+
function parseTemplate(strings, valueCount) {
|
|
1269
|
+
let template = templateCache.get(strings);
|
|
1270
|
+
if (template) return template;
|
|
1271
|
+
let htmlStr = "";
|
|
1272
|
+
let inTag = false;
|
|
1273
|
+
let quoteChar = null;
|
|
1274
|
+
for (let i = 0; i < strings.length; i++) {
|
|
1275
|
+
const s = strings[i] ?? "";
|
|
1276
|
+
for (let c = 0; c < s.length; c++) {
|
|
1277
|
+
const ch = s.charAt(c);
|
|
1278
|
+
if (quoteChar) {
|
|
1279
|
+
if (ch === quoteChar) quoteChar = null;
|
|
1280
|
+
} else if (inTag) {
|
|
1281
|
+
if (ch === ">") inTag = false;
|
|
1282
|
+
else if (ch === '"' || ch === "'") quoteChar = ch;
|
|
1283
|
+
} else {
|
|
1284
|
+
if (ch === "<") inTag = true;
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
htmlStr += s;
|
|
1288
|
+
if (i < valueCount) {
|
|
1289
|
+
const needsQuotes = inTag && !quoteChar && /=\s*$/.test(s);
|
|
1290
|
+
htmlStr += needsQuotes ? `"${createMarker(i)}"` : createMarker(i);
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
parseCount++;
|
|
1294
|
+
template = document.createElement("template");
|
|
1295
|
+
template.innerHTML = htmlStr;
|
|
1296
|
+
templateCache.set(strings, template);
|
|
1297
|
+
if (true) auditTemplate(strings, template);
|
|
1298
|
+
return template;
|
|
1299
|
+
}
|
|
1300
|
+
var MARKER_PREFIX = "<!--bk-";
|
|
1301
|
+
var MARKER_SUFFIX = "-->";
|
|
1302
|
+
function createMarker(index) {
|
|
1303
|
+
return `${MARKER_PREFIX}${index}${MARKER_SUFFIX}`;
|
|
1304
|
+
}
|
|
1305
|
+
function html(strings, ...values) {
|
|
1306
|
+
const bindings = [];
|
|
1307
|
+
const disposers = [];
|
|
1308
|
+
if (true) captureCallsite(strings);
|
|
1309
|
+
let mounted = false;
|
|
1310
|
+
return {
|
|
1311
|
+
__templateResult: true,
|
|
1312
|
+
mount(host) {
|
|
1313
|
+
if (mounted && values.length > 0) {
|
|
1314
|
+
throw nisliError(
|
|
1315
|
+
"N105",
|
|
1316
|
+
`mount() on an already-mounted TemplateResult with ${values.length} value binding(s) \u2014 dispose() it first or create a fresh template per mount. Template starts: ${JSON.stringify((strings[0] ?? "").slice(0, 60))}`
|
|
1317
|
+
);
|
|
1318
|
+
}
|
|
1319
|
+
mounted = true;
|
|
1320
|
+
const template = parseTemplate(strings, values.length);
|
|
1321
|
+
const fragment = template.content.cloneNode(true);
|
|
1322
|
+
processNode(fragment, values, bindings, disposers);
|
|
1323
|
+
host.appendChild(fragment);
|
|
1324
|
+
},
|
|
1325
|
+
dispose() {
|
|
1326
|
+
for (const d of disposers) {
|
|
1327
|
+
try {
|
|
1328
|
+
d();
|
|
1329
|
+
} catch (_) {
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
disposers.length = 0;
|
|
1333
|
+
for (const b of bindings) {
|
|
1334
|
+
if ("dispose" in b && b.dispose) {
|
|
1335
|
+
try {
|
|
1336
|
+
b.dispose();
|
|
1337
|
+
} catch (_) {
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
bindings.length = 0;
|
|
1342
|
+
mounted = false;
|
|
1343
|
+
}
|
|
1344
|
+
};
|
|
1345
|
+
}
|
|
1346
|
+
function processNode(node, values, bindings, disposers) {
|
|
1347
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
1348
|
+
const el2 = node;
|
|
1349
|
+
processAttributes(el2, values, bindings, disposers);
|
|
1350
|
+
}
|
|
1351
|
+
if (node.nodeType === Node.COMMENT_NODE) {
|
|
1352
|
+
const comment = node;
|
|
1353
|
+
const text = comment.textContent || "";
|
|
1354
|
+
if (text.startsWith("bk-") && !isNaN(Number(text.slice(3)))) {
|
|
1355
|
+
const index = Number(text.slice(3));
|
|
1356
|
+
const value = values[index];
|
|
1357
|
+
replaceMarkerWithBinding(comment, value, bindings, disposers);
|
|
1358
|
+
return;
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
const children2 = [...node.childNodes];
|
|
1362
|
+
for (const child of children2) {
|
|
1363
|
+
processNode(child, values, bindings, disposers);
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
function processAttributes(el2, values, bindings, disposers) {
|
|
1367
|
+
const attrsToRemove = [];
|
|
1368
|
+
for (const attr of [...el2.attributes]) {
|
|
1369
|
+
const name = attr.name;
|
|
1370
|
+
const attrValue = attr.value;
|
|
1371
|
+
if (name.startsWith("@")) {
|
|
1372
|
+
const eventParts = name.slice(1).split(".");
|
|
1373
|
+
const eventName = eventParts[0] ?? "";
|
|
1374
|
+
const modifiers = eventParts.slice(1);
|
|
1375
|
+
const markerMatch = attrValue.match(/<!--bk-(\d+)-->/);
|
|
1376
|
+
if (markerMatch) {
|
|
1377
|
+
const index = Number(markerMatch[1]);
|
|
1378
|
+
const handler = values[index];
|
|
1379
|
+
if (typeof handler === "function") {
|
|
1380
|
+
bindEvent(el2, eventName, handler, modifiers, bindings);
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
attrsToRemove.push(name);
|
|
1384
|
+
continue;
|
|
1385
|
+
}
|
|
1386
|
+
if (name.startsWith("class:")) {
|
|
1387
|
+
const className = name.slice(6);
|
|
1388
|
+
const markerMatch = attrValue.match(/<!--bk-(\d+)-->/);
|
|
1389
|
+
if (markerMatch) {
|
|
1390
|
+
const index = Number(markerMatch[1]);
|
|
1391
|
+
const value = values[index];
|
|
1392
|
+
bindClass(el2, className, value, bindings, disposers);
|
|
1393
|
+
}
|
|
1394
|
+
attrsToRemove.push(name);
|
|
1395
|
+
continue;
|
|
1396
|
+
}
|
|
1397
|
+
if (name === "html:inner") {
|
|
1398
|
+
const markerMatch = attrValue.match(/<!--bk-(\d+)-->/);
|
|
1399
|
+
if (markerMatch) {
|
|
1400
|
+
const index = Number(markerMatch[1]);
|
|
1401
|
+
const value = values[index];
|
|
1402
|
+
bindInnerHtml(el2, value, bindings, disposers);
|
|
1403
|
+
}
|
|
1404
|
+
attrsToRemove.push(name);
|
|
1405
|
+
continue;
|
|
1406
|
+
}
|
|
1407
|
+
if (name === "ref" && attrValue.includes(MARKER_PREFIX)) {
|
|
1408
|
+
const markerMatch = attrValue.match(/<!--bk-(\d+)-->/);
|
|
1409
|
+
if (markerMatch) {
|
|
1410
|
+
const index = Number(markerMatch[1]);
|
|
1411
|
+
const value = values[index];
|
|
1412
|
+
if (isRef(value)) {
|
|
1413
|
+
value.current = el2;
|
|
1414
|
+
disposers.push(() => {
|
|
1415
|
+
value.current = null;
|
|
1416
|
+
});
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
attrsToRemove.push(name);
|
|
1420
|
+
continue;
|
|
1421
|
+
}
|
|
1422
|
+
if (attrValue.includes(MARKER_PREFIX)) {
|
|
1423
|
+
const markers = [...attrValue.matchAll(/<!--bk-(\d+)-->/g)];
|
|
1424
|
+
if (markers.length) {
|
|
1425
|
+
const first = markers[0];
|
|
1426
|
+
if (markers.length === 1 && first && attrValue === first[0]) {
|
|
1427
|
+
bindAttribute(el2, name, values[Number(first[1])], bindings, disposers);
|
|
1428
|
+
} else {
|
|
1429
|
+
const resolve = () => attrValue.replace(/<!--bk-(\d+)-->/g, (_, i) => {
|
|
1430
|
+
const v = values[Number(i)];
|
|
1431
|
+
const raw2 = isSignal(v) ? v.value : v;
|
|
1432
|
+
return raw2 == null || raw2 === false ? "" : String(raw2);
|
|
1433
|
+
});
|
|
1434
|
+
const hasSignals = markers.some((m) => isSignal(values[Number(m[1])]));
|
|
1435
|
+
bindAttribute(el2, name, hasSignals ? computed(resolve) : resolve(), bindings, disposers);
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
continue;
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
for (const name of attrsToRemove) {
|
|
1442
|
+
el2.removeAttribute(name);
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
function mountFactoryResult(factory) {
|
|
1446
|
+
const el2 = document.createElement(factory.tagName);
|
|
1447
|
+
const localDisposers = [];
|
|
1448
|
+
for (const [key, raw2] of Object.entries(factory.props)) {
|
|
1449
|
+
if (isSignal(raw2)) {
|
|
1450
|
+
const sig = raw2;
|
|
1451
|
+
const unsub = sig.subscribe((newVal) => {
|
|
1452
|
+
el2._setProp?.(key, newVal);
|
|
1453
|
+
});
|
|
1454
|
+
localDisposers.push(unsub);
|
|
1455
|
+
} else {
|
|
1456
|
+
el2._setProp?.(key, raw2);
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1459
|
+
if (factory.hostAttrs?.class != null) {
|
|
1460
|
+
const classVal = factory.hostAttrs.class;
|
|
1461
|
+
if (isSignal(classVal)) {
|
|
1462
|
+
const sig = classVal;
|
|
1463
|
+
let prevClasses = [];
|
|
1464
|
+
const applyClasses = (raw2) => {
|
|
1465
|
+
const next = raw2 ? raw2.split(/\s+/).filter(Boolean) : [];
|
|
1466
|
+
for (const cls of prevClasses) {
|
|
1467
|
+
if (!next.includes(cls)) el2.classList.remove(cls);
|
|
1468
|
+
}
|
|
1469
|
+
for (const cls of next) {
|
|
1470
|
+
if (!prevClasses.includes(cls)) el2.classList.add(cls);
|
|
1471
|
+
}
|
|
1472
|
+
prevClasses = next;
|
|
1473
|
+
};
|
|
1474
|
+
const unsub = sig.subscribe(applyClasses);
|
|
1475
|
+
localDisposers.push(unsub);
|
|
1476
|
+
} else if (typeof classVal === "string" && classVal) {
|
|
1477
|
+
for (const cls of classVal.split(/\s+/).filter(Boolean)) {
|
|
1478
|
+
el2.classList.add(cls);
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
return {
|
|
1483
|
+
element: el2,
|
|
1484
|
+
dispose() {
|
|
1485
|
+
for (const dispose of localDisposers.splice(0)) {
|
|
1486
|
+
try {
|
|
1487
|
+
dispose();
|
|
1488
|
+
} catch (_) {
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
};
|
|
1493
|
+
}
|
|
1494
|
+
function replaceMarkerWithBinding(comment, value, bindings, disposers) {
|
|
1495
|
+
const parent = comment.parentNode;
|
|
1496
|
+
if (!parent) return;
|
|
1497
|
+
if (isSignal(value)) {
|
|
1498
|
+
const startMarker = document.createComment("slot-start");
|
|
1499
|
+
const endMarker = document.createComment("slot-end");
|
|
1500
|
+
parent.replaceChild(endMarker, comment);
|
|
1501
|
+
parent.insertBefore(startMarker, endMarker);
|
|
1502
|
+
let currentResults = [];
|
|
1503
|
+
let currentNodes = [];
|
|
1504
|
+
let currentFactoryDisposers = [];
|
|
1505
|
+
let currentText = null;
|
|
1506
|
+
const NOT_RENDERED = /* @__PURE__ */ Symbol("not-rendered");
|
|
1507
|
+
let lastValue = NOT_RENDERED;
|
|
1508
|
+
const dispose = effect(() => {
|
|
1509
|
+
const newValue = value.value;
|
|
1510
|
+
const liveParent = endMarker.parentNode;
|
|
1511
|
+
if (!liveParent) return;
|
|
1512
|
+
if (newValue === lastValue) return;
|
|
1513
|
+
lastValue = newValue;
|
|
1514
|
+
const isTemplate = Boolean(
|
|
1515
|
+
newValue && typeof newValue === "object" && "__templateResult" in newValue
|
|
1516
|
+
);
|
|
1517
|
+
const isFactory = Boolean(
|
|
1518
|
+
newValue && typeof newValue === "object" && "__type" in newValue && newValue.__type === "factory"
|
|
1519
|
+
);
|
|
1520
|
+
const isPrimitive = newValue != null && newValue !== false && !Array.isArray(newValue) && !isTemplate && !isFactory;
|
|
1521
|
+
if (isPrimitive && currentText) {
|
|
1522
|
+
currentText.data = String(newValue);
|
|
1523
|
+
return;
|
|
1524
|
+
}
|
|
1525
|
+
for (const r of currentResults) {
|
|
1526
|
+
try {
|
|
1527
|
+
r.dispose();
|
|
1528
|
+
} catch (_) {
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
for (const dispose2 of currentFactoryDisposers) {
|
|
1532
|
+
try {
|
|
1533
|
+
dispose2();
|
|
1534
|
+
} catch (_) {
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
for (const node of currentNodes) {
|
|
1538
|
+
node.parentNode?.removeChild(node);
|
|
1539
|
+
}
|
|
1540
|
+
currentNodes = [];
|
|
1541
|
+
currentResults = [];
|
|
1542
|
+
currentFactoryDisposers = [];
|
|
1543
|
+
currentText = null;
|
|
1544
|
+
if (newValue == null) {
|
|
1545
|
+
} else if (Array.isArray(newValue)) {
|
|
1546
|
+
for (const item of newValue) {
|
|
1547
|
+
if (item && typeof item === "object" && "__templateResult" in item) {
|
|
1548
|
+
const tpl = item;
|
|
1549
|
+
const wrapper = document.createDocumentFragment();
|
|
1550
|
+
tpl.mount(wrapper);
|
|
1551
|
+
const nodes = [...wrapper.childNodes];
|
|
1552
|
+
currentNodes.push(...nodes);
|
|
1553
|
+
currentResults.push(tpl);
|
|
1554
|
+
liveParent.insertBefore(wrapper, endMarker);
|
|
1555
|
+
} else if (item && typeof item === "object" && "__type" in item && item.__type === "factory") {
|
|
1556
|
+
const mounted = mountFactoryResult(item);
|
|
1557
|
+
currentNodes.push(mounted.element);
|
|
1558
|
+
currentFactoryDisposers.push(mounted.dispose);
|
|
1559
|
+
liveParent.insertBefore(mounted.element, endMarker);
|
|
1560
|
+
} else if (item != null && item !== false) {
|
|
1561
|
+
const textNode = document.createTextNode(String(item));
|
|
1562
|
+
currentNodes.push(textNode);
|
|
1563
|
+
liveParent.insertBefore(textNode, endMarker);
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
} else if (isTemplate) {
|
|
1567
|
+
const tpl = newValue;
|
|
1568
|
+
const wrapper = document.createDocumentFragment();
|
|
1569
|
+
tpl.mount(wrapper);
|
|
1570
|
+
currentNodes = [...wrapper.childNodes];
|
|
1571
|
+
currentResults.push(tpl);
|
|
1572
|
+
liveParent.insertBefore(wrapper, endMarker);
|
|
1573
|
+
} else if (isFactory) {
|
|
1574
|
+
const mounted = mountFactoryResult(newValue);
|
|
1575
|
+
currentNodes.push(mounted.element);
|
|
1576
|
+
currentFactoryDisposers.push(mounted.dispose);
|
|
1577
|
+
liveParent.insertBefore(mounted.element, endMarker);
|
|
1578
|
+
} else if (newValue !== false) {
|
|
1579
|
+
currentText = document.createTextNode(String(newValue));
|
|
1580
|
+
currentNodes.push(currentText);
|
|
1581
|
+
liveParent.insertBefore(currentText, endMarker);
|
|
1582
|
+
}
|
|
1583
|
+
});
|
|
1584
|
+
disposers.push(dispose);
|
|
1585
|
+
disposers.push(() => {
|
|
1586
|
+
for (const r of currentResults.splice(0)) {
|
|
1587
|
+
try {
|
|
1588
|
+
r.dispose();
|
|
1589
|
+
} catch (_) {
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
for (const factoryDispose of currentFactoryDisposers.splice(0)) {
|
|
1593
|
+
try {
|
|
1594
|
+
factoryDispose();
|
|
1595
|
+
} catch (_) {
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
currentNodes = [];
|
|
1599
|
+
currentText = null;
|
|
1600
|
+
});
|
|
1601
|
+
} else if (value && typeof value === "object" && "__templateResult" in value) {
|
|
1602
|
+
const result = value;
|
|
1603
|
+
const startMarker = document.createComment("tpl-start");
|
|
1604
|
+
const endMarker = document.createComment("tpl-end");
|
|
1605
|
+
parent.replaceChild(endMarker, comment);
|
|
1606
|
+
parent.insertBefore(startMarker, endMarker);
|
|
1607
|
+
const wrapper = document.createDocumentFragment();
|
|
1608
|
+
result.mount(wrapper);
|
|
1609
|
+
parent.insertBefore(wrapper, endMarker);
|
|
1610
|
+
disposers.push(() => result.dispose());
|
|
1611
|
+
} else if (value && typeof value === "object" && "__type" in value && value.__type === "factory") {
|
|
1612
|
+
const mounted = mountFactoryResult(value);
|
|
1613
|
+
parent.replaceChild(mounted.element, comment);
|
|
1614
|
+
disposers.push(mounted.dispose);
|
|
1615
|
+
} else if (Array.isArray(value)) {
|
|
1616
|
+
const startMarker = document.createComment("list-start");
|
|
1617
|
+
const endMarker = document.createComment("list-end");
|
|
1618
|
+
parent.replaceChild(endMarker, comment);
|
|
1619
|
+
parent.insertBefore(startMarker, endMarker);
|
|
1620
|
+
for (const item of value) {
|
|
1621
|
+
if (item && typeof item === "object" && "__templateResult" in item) {
|
|
1622
|
+
const wrapper = document.createDocumentFragment();
|
|
1623
|
+
item.mount(wrapper);
|
|
1624
|
+
parent.insertBefore(wrapper, endMarker);
|
|
1625
|
+
disposers.push(() => item.dispose());
|
|
1626
|
+
} else if (item && typeof item === "object" && "__type" in item && item.__type === "factory") {
|
|
1627
|
+
const mounted = mountFactoryResult(item);
|
|
1628
|
+
parent.insertBefore(mounted.element, endMarker);
|
|
1629
|
+
disposers.push(mounted.dispose);
|
|
1630
|
+
} else {
|
|
1631
|
+
const textNode = document.createTextNode(String(item));
|
|
1632
|
+
parent.insertBefore(textNode, endMarker);
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
} else if (value == null || value === false) {
|
|
1636
|
+
parent.removeChild(comment);
|
|
1637
|
+
} else {
|
|
1638
|
+
const textNode = document.createTextNode(String(value));
|
|
1639
|
+
parent.replaceChild(textNode, comment);
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
function bindAttribute(el2, name, value, bindings, disposers) {
|
|
1643
|
+
if (name === "class") {
|
|
1644
|
+
bindClassAttribute(el2, value, bindings, disposers);
|
|
1645
|
+
return;
|
|
1646
|
+
}
|
|
1647
|
+
const isHtmlAttr = name === "id" || name === "style" || name === "slot" || name === "title" || name === "role" || name === "tabindex" || name === "name" || name.startsWith("data-") || name.startsWith("aria-");
|
|
1648
|
+
const hasPropSetter = !isHtmlAttr && typeof el2._setProp === "function";
|
|
1649
|
+
if (hasPropSetter) {
|
|
1650
|
+
if (isSignal(value)) {
|
|
1651
|
+
const binding = { type: "attribute", element: el2, name };
|
|
1652
|
+
bindings.push(binding);
|
|
1653
|
+
const dispose = effect(() => {
|
|
1654
|
+
el2._setProp(name, value.value);
|
|
1655
|
+
});
|
|
1656
|
+
binding.dispose = dispose;
|
|
1657
|
+
disposers.push(dispose);
|
|
1658
|
+
} else {
|
|
1659
|
+
el2._setProp(name, value);
|
|
1660
|
+
}
|
|
1661
|
+
return;
|
|
1662
|
+
}
|
|
1663
|
+
bindPlainAttribute(el2, name, value, bindings, disposers);
|
|
1664
|
+
}
|
|
1665
|
+
function bindPlainAttribute(el2, name, value, bindings, disposers) {
|
|
1666
|
+
if (isSignal(value)) {
|
|
1667
|
+
const binding = { type: "attribute", element: el2, name };
|
|
1668
|
+
bindings.push(binding);
|
|
1669
|
+
const dispose = effect(() => {
|
|
1670
|
+
const v = value.value;
|
|
1671
|
+
if (v == null || v === false) {
|
|
1672
|
+
el2.removeAttribute(name);
|
|
1673
|
+
} else if (v === true) {
|
|
1674
|
+
el2.setAttribute(name, "");
|
|
1675
|
+
} else {
|
|
1676
|
+
el2.setAttribute(name, String(v));
|
|
1677
|
+
}
|
|
1678
|
+
});
|
|
1679
|
+
binding.dispose = dispose;
|
|
1680
|
+
disposers.push(dispose);
|
|
1681
|
+
} else {
|
|
1682
|
+
if (value == null || value === false) {
|
|
1683
|
+
el2.removeAttribute(name);
|
|
1684
|
+
} else if (value === true) {
|
|
1685
|
+
el2.setAttribute(name, "");
|
|
1686
|
+
} else {
|
|
1687
|
+
el2.setAttribute(name, String(value));
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
function bindClassAttribute(el2, value, bindings, disposers) {
|
|
1692
|
+
let initialized = false;
|
|
1693
|
+
let prevClasses = [];
|
|
1694
|
+
const applyClasses = (raw2) => {
|
|
1695
|
+
if (!initialized) {
|
|
1696
|
+
el2.setAttribute("class", "");
|
|
1697
|
+
initialized = true;
|
|
1698
|
+
}
|
|
1699
|
+
const str = raw2 == null || raw2 === false ? "" : String(raw2);
|
|
1700
|
+
const next = str.split(/\s+/).filter(Boolean);
|
|
1701
|
+
for (const cls of prevClasses) {
|
|
1702
|
+
if (!next.includes(cls)) {
|
|
1703
|
+
el2.classList.remove(cls);
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
for (const cls of next) {
|
|
1707
|
+
if (!prevClasses.includes(cls)) {
|
|
1708
|
+
el2.classList.add(cls);
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
prevClasses = next;
|
|
1712
|
+
};
|
|
1713
|
+
if (isSignal(value)) {
|
|
1714
|
+
const binding = { type: "attribute", element: el2, name: "class" };
|
|
1715
|
+
bindings.push(binding);
|
|
1716
|
+
const dispose = effect(() => {
|
|
1717
|
+
applyClasses(value.value);
|
|
1718
|
+
});
|
|
1719
|
+
binding.dispose = dispose;
|
|
1720
|
+
disposers.push(dispose);
|
|
1721
|
+
} else {
|
|
1722
|
+
applyClasses(value);
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
function bindClass(el2, className, value, bindings, disposers) {
|
|
1726
|
+
if (isSignal(value)) {
|
|
1727
|
+
const binding = { type: "class", element: el2, className };
|
|
1728
|
+
bindings.push(binding);
|
|
1729
|
+
const dispose = effect(() => {
|
|
1730
|
+
const v = value.value;
|
|
1731
|
+
el2.classList.toggle(className, !!v);
|
|
1732
|
+
});
|
|
1733
|
+
binding.dispose = dispose;
|
|
1734
|
+
disposers.push(dispose);
|
|
1735
|
+
} else {
|
|
1736
|
+
el2.classList.toggle(className, !!value);
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
function bindInnerHtml(el2, value, bindings, disposers) {
|
|
1740
|
+
const resolveHtml = (v) => {
|
|
1741
|
+
if (v == null) return "";
|
|
1742
|
+
if (isRawHtml(v)) {
|
|
1743
|
+
if (isSanitizedHtml(v)) {
|
|
1744
|
+
throw nisliError(
|
|
1745
|
+
"N106",
|
|
1746
|
+
`html:inner rejects a value branded both raw() and sanitized() \u2014 the trust decision is ambiguous. Pick one brand.`
|
|
1747
|
+
);
|
|
1748
|
+
}
|
|
1749
|
+
return v.value;
|
|
1750
|
+
}
|
|
1751
|
+
if (isSanitizedHtml(v)) {
|
|
1752
|
+
if (sanitizerFor(el2) === null) {
|
|
1753
|
+
throw nisliError(
|
|
1754
|
+
"N107",
|
|
1755
|
+
`html:inner cannot render sanitized() markup: this engine has no native Element.setHTML() and no fallback is registered. Wire one at startup \u2014 setSanitizerFallback((el, html) => { el.innerHTML = DOMPurify.sanitize(html) }) \u2014 nisli ships no sanitizer and will not fall back to innerHTML.`
|
|
1756
|
+
);
|
|
1757
|
+
}
|
|
1758
|
+
return v;
|
|
1759
|
+
}
|
|
1760
|
+
if (isTrustedHtml(v)) return v;
|
|
1761
|
+
throw nisliError(
|
|
1762
|
+
"N106",
|
|
1763
|
+
`html:inner rejects unbranded values (got ${typeof v}). Wrap trusted markup in raw(html), untrusted markup in sanitized(html) \u2014 and never wrap user-generated input in raw().`
|
|
1764
|
+
);
|
|
1765
|
+
};
|
|
1766
|
+
const writeHtml = (v) => {
|
|
1767
|
+
const resolved = resolveHtml(v);
|
|
1768
|
+
if (isSanitizedHtml(resolved)) {
|
|
1769
|
+
sanitizerFor(el2)?.(el2, resolved.value);
|
|
1770
|
+
return;
|
|
1771
|
+
}
|
|
1772
|
+
el2.innerHTML = resolved;
|
|
1773
|
+
};
|
|
1774
|
+
if (isSignal(value)) {
|
|
1775
|
+
untracked(() => resolveHtml(value.value));
|
|
1776
|
+
const binding = { type: "innerHtml", element: el2 };
|
|
1777
|
+
bindings.push(binding);
|
|
1778
|
+
const dispose = effect(() => {
|
|
1779
|
+
writeHtml(value.value);
|
|
1780
|
+
});
|
|
1781
|
+
binding.dispose = dispose;
|
|
1782
|
+
disposers.push(dispose);
|
|
1783
|
+
} else {
|
|
1784
|
+
writeHtml(value);
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
function bindEvent(el2, eventName, handler, modifiers, bindings) {
|
|
1788
|
+
let safeHandler;
|
|
1789
|
+
let composed = handler;
|
|
1790
|
+
if (modifiers.includes("stop")) {
|
|
1791
|
+
const inner = composed;
|
|
1792
|
+
composed = (e) => {
|
|
1793
|
+
e.stopPropagation();
|
|
1794
|
+
inner(e);
|
|
1795
|
+
};
|
|
1796
|
+
}
|
|
1797
|
+
if (modifiers.includes("prevent")) {
|
|
1798
|
+
const inner = composed;
|
|
1799
|
+
composed = (e) => {
|
|
1800
|
+
e.preventDefault();
|
|
1801
|
+
inner(e);
|
|
1802
|
+
};
|
|
1803
|
+
}
|
|
1804
|
+
if (modifiers.includes("once")) {
|
|
1805
|
+
const inner = composed;
|
|
1806
|
+
composed = (e) => {
|
|
1807
|
+
el2.removeEventListener(eventName, safeHandler);
|
|
1808
|
+
inner(e);
|
|
1809
|
+
};
|
|
1810
|
+
}
|
|
1811
|
+
for (const mod of modifiers) {
|
|
1812
|
+
if (["enter", "escape", "space", "tab"].includes(mod)) {
|
|
1813
|
+
const keyMap = {
|
|
1814
|
+
enter: "Enter",
|
|
1815
|
+
escape: "Escape",
|
|
1816
|
+
space: " ",
|
|
1817
|
+
tab: "Tab"
|
|
1818
|
+
};
|
|
1819
|
+
const inner = composed;
|
|
1820
|
+
composed = (e) => {
|
|
1821
|
+
if (e.key === keyMap[mod]) {
|
|
1822
|
+
inner(e);
|
|
1823
|
+
}
|
|
1824
|
+
};
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
safeHandler = (e) => {
|
|
1828
|
+
try {
|
|
1829
|
+
untrack(() => composed(e));
|
|
1830
|
+
} catch (err) {
|
|
1831
|
+
console.error(`Event handler error for '${eventName}':`, err);
|
|
1832
|
+
}
|
|
1833
|
+
};
|
|
1834
|
+
el2.addEventListener(eventName, safeHandler);
|
|
1835
|
+
bindings.push({
|
|
1836
|
+
type: "event",
|
|
1837
|
+
element: el2,
|
|
1838
|
+
eventName,
|
|
1839
|
+
handler: safeHandler,
|
|
1840
|
+
modifiers,
|
|
1841
|
+
dispose: () => el2.removeEventListener(eventName, safeHandler)
|
|
1842
|
+
});
|
|
1843
|
+
}
|
|
1844
|
+
function asTemplateResult(result) {
|
|
1845
|
+
if (result && typeof result === "object" && "__templateResult" in result) {
|
|
1846
|
+
return result;
|
|
1847
|
+
}
|
|
1848
|
+
if (result && typeof result === "object" && result.__type === "factory") {
|
|
1849
|
+
let mounted = null;
|
|
1850
|
+
return {
|
|
1851
|
+
__templateResult: true,
|
|
1852
|
+
mount(host) {
|
|
1853
|
+
mounted = mountFactoryResult(result);
|
|
1854
|
+
host.appendChild(mounted.element);
|
|
1855
|
+
},
|
|
1856
|
+
dispose() {
|
|
1857
|
+
mounted?.dispose();
|
|
1858
|
+
mounted?.element.remove();
|
|
1859
|
+
mounted = null;
|
|
1860
|
+
}
|
|
1861
|
+
};
|
|
1862
|
+
}
|
|
1863
|
+
throw new TypeError(
|
|
1864
|
+
`each(): template callback must return a TemplateResult or a component factory result, got ${result === null ? "null" : typeof result}`
|
|
1865
|
+
);
|
|
1866
|
+
}
|
|
1867
|
+
function each(items, keyFn, templateFn) {
|
|
1868
|
+
let startMarker;
|
|
1869
|
+
let endMarker;
|
|
1870
|
+
let entries = [];
|
|
1871
|
+
let effectDispose = null;
|
|
1872
|
+
return {
|
|
1873
|
+
__templateResult: true,
|
|
1874
|
+
mount(host) {
|
|
1875
|
+
startMarker = document.createComment("each-start");
|
|
1876
|
+
endMarker = document.createComment("each-end");
|
|
1877
|
+
host.appendChild(startMarker);
|
|
1878
|
+
host.appendChild(endMarker);
|
|
1879
|
+
effectDispose = effect(() => {
|
|
1880
|
+
const newItems = items.value;
|
|
1881
|
+
reconcile(newItems);
|
|
1882
|
+
});
|
|
1883
|
+
},
|
|
1884
|
+
dispose() {
|
|
1885
|
+
if (effectDispose) {
|
|
1886
|
+
effectDispose();
|
|
1887
|
+
effectDispose = null;
|
|
1888
|
+
}
|
|
1889
|
+
for (const entry of entries) {
|
|
1890
|
+
entry.templateResult.dispose();
|
|
1891
|
+
}
|
|
1892
|
+
entries = [];
|
|
1893
|
+
}
|
|
1894
|
+
};
|
|
1895
|
+
function reconcile(newItems) {
|
|
1896
|
+
const parent = endMarker.parentNode;
|
|
1897
|
+
if (!parent) return;
|
|
1898
|
+
const keyedItems = [];
|
|
1899
|
+
const keyIndices = /* @__PURE__ */ new Map();
|
|
1900
|
+
for (let i = 0; i < newItems.length; i++) {
|
|
1901
|
+
const item = newItems[i];
|
|
1902
|
+
if (item === void 0) continue;
|
|
1903
|
+
const key = keyFn(item, i);
|
|
1904
|
+
const previousIndex = keyIndices.get(key);
|
|
1905
|
+
if (previousIndex !== void 0) {
|
|
1906
|
+
const formattedKey = typeof key === "string" ? JSON.stringify(key) : String(key);
|
|
1907
|
+
console.error(
|
|
1908
|
+
`each() requires unique keys. Duplicate key ${formattedKey} found at indices ${previousIndex} and ${i}; reconciliation skipped.`
|
|
1909
|
+
);
|
|
1910
|
+
return;
|
|
1911
|
+
}
|
|
1912
|
+
keyIndices.set(key, i);
|
|
1913
|
+
keyedItems.push({ item, index: i, key });
|
|
1914
|
+
}
|
|
1915
|
+
const oldMap = /* @__PURE__ */ new Map();
|
|
1916
|
+
for (const entry of entries) {
|
|
1917
|
+
oldMap.set(entry.key, entry);
|
|
1918
|
+
}
|
|
1919
|
+
const newEntries = [];
|
|
1920
|
+
const newKeys = /* @__PURE__ */ new Set();
|
|
1921
|
+
for (const { item, index, key } of keyedItems) {
|
|
1922
|
+
newKeys.add(key);
|
|
1923
|
+
const existing = oldMap.get(key);
|
|
1924
|
+
if (existing) {
|
|
1925
|
+
existing.itemSignal.value = item;
|
|
1926
|
+
existing.indexSignal.value = index;
|
|
1927
|
+
newEntries.push(existing);
|
|
1928
|
+
} else {
|
|
1929
|
+
const itemSignal = signal(item);
|
|
1930
|
+
const indexSignal = signal(index);
|
|
1931
|
+
const wrapper = document.createElement("each-item");
|
|
1932
|
+
wrapper.style.display = "contents";
|
|
1933
|
+
const templateResult = asTemplateResult(templateFn(itemSignal, indexSignal));
|
|
1934
|
+
templateResult.mount(wrapper);
|
|
1935
|
+
newEntries.push({ key, itemSignal, indexSignal, templateResult, wrapper });
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
for (const entry of entries) {
|
|
1939
|
+
if (!newKeys.has(entry.key)) {
|
|
1940
|
+
entry.templateResult.dispose();
|
|
1941
|
+
entry.wrapper.parentNode?.removeChild(entry.wrapper);
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
let cursor = startMarker;
|
|
1945
|
+
for (const entry of newEntries) {
|
|
1946
|
+
const nextSibling = cursor.nextSibling;
|
|
1947
|
+
if (nextSibling !== entry.wrapper) {
|
|
1948
|
+
if (entry.wrapper.parentNode === parent && canMoveWithin(parent)) {
|
|
1949
|
+
parent.moveBefore(entry.wrapper, nextSibling);
|
|
1950
|
+
} else {
|
|
1951
|
+
parent.insertBefore(entry.wrapper, nextSibling);
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
cursor = entry.wrapper;
|
|
1955
|
+
}
|
|
1956
|
+
entries = newEntries;
|
|
1957
|
+
}
|
|
1958
|
+
}
|
|
1959
|
+
function when(condition, template, elseTemplate) {
|
|
1960
|
+
const resolveBranch = (branch) => {
|
|
1961
|
+
if (branch === void 0) return null;
|
|
1962
|
+
if (typeof branch === "function") return untracked(branch);
|
|
1963
|
+
return branch;
|
|
1964
|
+
};
|
|
1965
|
+
if (isSignal(condition)) {
|
|
1966
|
+
let lastGate;
|
|
1967
|
+
let lastResult = null;
|
|
1968
|
+
return computed(() => {
|
|
1969
|
+
const gate = !!condition.value;
|
|
1970
|
+
if (gate === lastGate) return lastResult;
|
|
1971
|
+
const next = gate ? resolveBranch(template) : resolveBranch(elseTemplate);
|
|
1972
|
+
lastGate = gate;
|
|
1973
|
+
lastResult = next;
|
|
1974
|
+
return next;
|
|
1975
|
+
});
|
|
1976
|
+
}
|
|
1977
|
+
return condition ? resolveBranch(template) : resolveBranch(elseTemplate);
|
|
1978
|
+
}
|
|
1979
|
+
function el(tag, props = {}, children2) {
|
|
1980
|
+
let node = null;
|
|
1981
|
+
const bindings = [];
|
|
1982
|
+
const disposers = [];
|
|
1983
|
+
return {
|
|
1984
|
+
__templateResult: true,
|
|
1985
|
+
mount(host) {
|
|
1986
|
+
const element = document.createElement(tag);
|
|
1987
|
+
node = element;
|
|
1988
|
+
for (const [key, val] of Object.entries(props)) {
|
|
1989
|
+
if (val === void 0) continue;
|
|
1990
|
+
if (key === "ref") {
|
|
1991
|
+
if (isRef(val)) {
|
|
1992
|
+
val.current = element;
|
|
1993
|
+
disposers.push(() => {
|
|
1994
|
+
val.current = null;
|
|
1995
|
+
});
|
|
1996
|
+
}
|
|
1997
|
+
continue;
|
|
1998
|
+
}
|
|
1999
|
+
if (key === "on") {
|
|
2000
|
+
for (const [event, handler] of Object.entries(val)) {
|
|
2001
|
+
bindEvent(element, event, handler, [], bindings);
|
|
2002
|
+
}
|
|
2003
|
+
continue;
|
|
2004
|
+
}
|
|
2005
|
+
if (key === "class") {
|
|
2006
|
+
bindClassAttribute(element, val, bindings, disposers);
|
|
2007
|
+
continue;
|
|
2008
|
+
}
|
|
2009
|
+
bindPlainAttribute(element, key, val, bindings, disposers);
|
|
2010
|
+
}
|
|
2011
|
+
const flat = [];
|
|
2012
|
+
flattenChildren(children2, flat);
|
|
2013
|
+
for (const child of flat) {
|
|
2014
|
+
const anchor = document.createComment("");
|
|
2015
|
+
element.appendChild(anchor);
|
|
2016
|
+
replaceMarkerWithBinding(anchor, child, bindings, disposers);
|
|
2017
|
+
}
|
|
2018
|
+
host.appendChild(element);
|
|
2019
|
+
},
|
|
2020
|
+
dispose() {
|
|
2021
|
+
for (const d of disposers) {
|
|
2022
|
+
try {
|
|
2023
|
+
d();
|
|
2024
|
+
} catch (_) {
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
disposers.length = 0;
|
|
2028
|
+
for (const b of bindings) {
|
|
2029
|
+
if ("dispose" in b && b.dispose) {
|
|
2030
|
+
try {
|
|
2031
|
+
b.dispose();
|
|
2032
|
+
} catch (_) {
|
|
2033
|
+
}
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
2036
|
+
bindings.length = 0;
|
|
2037
|
+
node?.parentNode?.removeChild(node);
|
|
2038
|
+
node = null;
|
|
2039
|
+
}
|
|
2040
|
+
};
|
|
2041
|
+
}
|
|
2042
|
+
function flattenChildren(children2, out) {
|
|
2043
|
+
if (children2 === void 0) return;
|
|
2044
|
+
if (Array.isArray(children2)) {
|
|
2045
|
+
for (const c of children2) flattenChildren(c, out);
|
|
2046
|
+
} else {
|
|
2047
|
+
out.push(children2);
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2050
|
+
|
|
2051
|
+
// src/projection.ts
|
|
2052
|
+
function isMeaningful(nodes) {
|
|
2053
|
+
return nodes.some(
|
|
2054
|
+
(n) => n.nodeType === 1 || n.nodeType === 3 && (n.textContent ?? "").trim() !== ""
|
|
2055
|
+
);
|
|
2056
|
+
}
|
|
2057
|
+
function nodesTemplate(nodes) {
|
|
2058
|
+
return {
|
|
2059
|
+
__templateResult: true,
|
|
2060
|
+
mount(w) {
|
|
2061
|
+
for (const node of nodes) w.appendChild(node);
|
|
2062
|
+
},
|
|
2063
|
+
dispose() {
|
|
2064
|
+
}
|
|
2065
|
+
};
|
|
2066
|
+
}
|
|
2067
|
+
function textTemplate(text) {
|
|
2068
|
+
return {
|
|
2069
|
+
__templateResult: true,
|
|
2070
|
+
mount(w) {
|
|
2071
|
+
w.appendChild(document.createTextNode(text));
|
|
2072
|
+
},
|
|
2073
|
+
dispose() {
|
|
2074
|
+
}
|
|
2075
|
+
};
|
|
2076
|
+
}
|
|
2077
|
+
function asSlot(value) {
|
|
2078
|
+
if (value == null || value === "") return null;
|
|
2079
|
+
if (typeof value === "object" && ("__templateResult" in value || "__type" in value)) {
|
|
2080
|
+
return value;
|
|
2081
|
+
}
|
|
2082
|
+
if (Array.isArray(value)) return value;
|
|
2083
|
+
return textTemplate(String(value));
|
|
2084
|
+
}
|
|
2085
|
+
function children(fallback) {
|
|
2086
|
+
const host = getCurrentComponent().element;
|
|
2087
|
+
const captured = host.__capturedChildren ?? [];
|
|
2088
|
+
host.__capturedChildren = void 0;
|
|
2089
|
+
for (const node of captured) {
|
|
2090
|
+
if (node.parentNode === host) host.removeChild(node);
|
|
2091
|
+
}
|
|
2092
|
+
const childrenProp = host._propSignal ? host._propSignal("children") : void 0;
|
|
2093
|
+
const projectedNodes = signal(captured);
|
|
2094
|
+
let disposed = false;
|
|
2095
|
+
onCleanup(() => {
|
|
2096
|
+
disposed = true;
|
|
2097
|
+
const nodes = projectedNodes.value;
|
|
2098
|
+
host.__capturedChildren = nodes.length ? nodes : void 0;
|
|
2099
|
+
});
|
|
2100
|
+
host.__nisliProjectionSweep = () => {
|
|
2101
|
+
const renderedRoots = new Set(Array.from(host.childNodes));
|
|
2102
|
+
queueMicrotask(() => {
|
|
2103
|
+
if (disposed) return;
|
|
2104
|
+
const late = Array.from(host.childNodes).filter((n) => !renderedRoots.has(n));
|
|
2105
|
+
if (late.length && isMeaningful(late)) {
|
|
2106
|
+
for (const node of late) host.removeChild(node);
|
|
2107
|
+
projectedNodes.value = [...projectedNodes.value, ...late];
|
|
2108
|
+
flush();
|
|
2109
|
+
}
|
|
2110
|
+
});
|
|
2111
|
+
};
|
|
2112
|
+
let fallbackSlot = null;
|
|
2113
|
+
if (fallback != null) {
|
|
2114
|
+
if (typeof fallback === "string") {
|
|
2115
|
+
fallbackSlot = textTemplate(fallback);
|
|
2116
|
+
} else {
|
|
2117
|
+
const holder = document.createDocumentFragment();
|
|
2118
|
+
fallback.mount(holder);
|
|
2119
|
+
const fallbackNodes = Array.from(holder.childNodes);
|
|
2120
|
+
onCleanup(() => {
|
|
2121
|
+
try {
|
|
2122
|
+
fallback.dispose();
|
|
2123
|
+
} catch (_) {
|
|
2124
|
+
}
|
|
2125
|
+
});
|
|
2126
|
+
fallbackSlot = nodesTemplate(fallbackNodes);
|
|
2127
|
+
}
|
|
2128
|
+
}
|
|
2129
|
+
return computed(() => {
|
|
2130
|
+
const kids = childrenProp?.value;
|
|
2131
|
+
if (kids != null && kids !== "") return asSlot(kids);
|
|
2132
|
+
const nodes = projectedNodes.value;
|
|
2133
|
+
if (isMeaningful(nodes)) return nodesTemplate(nodes);
|
|
2134
|
+
return fallbackSlot;
|
|
2135
|
+
});
|
|
2136
|
+
}
|
|
2137
|
+
|
|
2138
|
+
// src/settle.ts
|
|
2139
|
+
var pending = /* @__PURE__ */ new Set();
|
|
2140
|
+
function __enrollPending() {
|
|
2141
|
+
let resolve;
|
|
2142
|
+
const entry = new Promise((res) => {
|
|
2143
|
+
resolve = res;
|
|
2144
|
+
});
|
|
2145
|
+
pending.add(entry);
|
|
2146
|
+
let done = false;
|
|
2147
|
+
return () => {
|
|
2148
|
+
if (done) return;
|
|
2149
|
+
done = true;
|
|
2150
|
+
pending.delete(entry);
|
|
2151
|
+
resolve();
|
|
2152
|
+
};
|
|
2153
|
+
}
|
|
2154
|
+
function __resetPending() {
|
|
2155
|
+
pending.clear();
|
|
2156
|
+
}
|
|
2157
|
+
registerResetHook(__resetPending);
|
|
2158
|
+
async function settle() {
|
|
2159
|
+
for (let i = 0; i < 50; i++) {
|
|
2160
|
+
await tick();
|
|
2161
|
+
if (pending.size === 0) return;
|
|
2162
|
+
await Promise.allSettled([...pending]);
|
|
2163
|
+
}
|
|
2164
|
+
if (isDev()) diag(
|
|
2165
|
+
"N603",
|
|
2166
|
+
"settle() did not reach quiescence within 50 iterations \u2014 something keeps scheduling new async work (a self-invalidating query or a polling loop?). settle() is a test/verify primitive; under live background refetch it is unbounded by design. Returning anyway."
|
|
2167
|
+
);
|
|
2168
|
+
}
|
|
2169
|
+
|
|
2170
|
+
// src/query.ts
|
|
2171
|
+
function keyError(detail) {
|
|
2172
|
+
return new Error(formatDiag(
|
|
2173
|
+
"N602",
|
|
2174
|
+
`invalid query key: ${detail}. Keys must be flat readonly (string | number | boolean | null)[] arrays \u2014 spread object params into tuple elements; null is the optional sentinel.`
|
|
2175
|
+
));
|
|
2176
|
+
}
|
|
2177
|
+
function validateKey(raw2) {
|
|
2178
|
+
if (!Array.isArray(raw2)) {
|
|
2179
|
+
throw keyError(`expected an array, got ${typeof raw2}`);
|
|
2180
|
+
}
|
|
2181
|
+
for (let i = 0; i < raw2.length; i++) {
|
|
2182
|
+
const el2 = raw2[i];
|
|
2183
|
+
if (el2 === null) continue;
|
|
2184
|
+
const t = typeof el2;
|
|
2185
|
+
if (t === "string" || t === "boolean") continue;
|
|
2186
|
+
if (t === "number") {
|
|
2187
|
+
if (Number.isFinite(el2)) continue;
|
|
2188
|
+
throw keyError(`non-finite number at index ${i} (would collide with null)`);
|
|
2189
|
+
}
|
|
2190
|
+
if (el2 === void 0) {
|
|
2191
|
+
throw keyError(`undefined at index ${i}`);
|
|
2192
|
+
}
|
|
2193
|
+
throw keyError(`${t} at index ${i}`);
|
|
2194
|
+
}
|
|
2195
|
+
return raw2.slice();
|
|
2196
|
+
}
|
|
2197
|
+
function serializeKey(key) {
|
|
2198
|
+
return JSON.stringify(key);
|
|
2199
|
+
}
|
|
2200
|
+
function matchesPrefix(key, prefix) {
|
|
2201
|
+
if (prefix.length > key.length) return false;
|
|
2202
|
+
for (let i = 0; i < prefix.length; i++) {
|
|
2203
|
+
if (key[i] !== prefix[i]) return false;
|
|
2204
|
+
}
|
|
2205
|
+
return true;
|
|
2206
|
+
}
|
|
2207
|
+
var nextMountIndex = 0;
|
|
2208
|
+
function peek(s) {
|
|
2209
|
+
let v;
|
|
2210
|
+
untrack(() => {
|
|
2211
|
+
v = s.value;
|
|
2212
|
+
});
|
|
2213
|
+
return v;
|
|
2214
|
+
}
|
|
2215
|
+
function toError(cause) {
|
|
2216
|
+
if (cause instanceof Error) return cause;
|
|
2217
|
+
try {
|
|
2218
|
+
return new Error(String(cause));
|
|
2219
|
+
} catch {
|
|
2220
|
+
return new Error("Unknown error");
|
|
2221
|
+
}
|
|
2222
|
+
}
|
|
2223
|
+
var QueryClient = class {
|
|
2224
|
+
/** @internal */
|
|
2225
|
+
_records = /* @__PURE__ */ new Map();
|
|
2226
|
+
/** @internal clear() bumps this; observer effects track it and re-resolve. */
|
|
2227
|
+
_version = signal(0);
|
|
2228
|
+
/** @internal live observer count (N601 mixed-client diagnostic). */
|
|
2229
|
+
_observers = 0;
|
|
2230
|
+
/**
|
|
2231
|
+
* Mark records matching the key prefix stale and rerun those with ≥1
|
|
2232
|
+
* enabled observer (using the owning observer's fetcher/options).
|
|
2233
|
+
* Disabled observers revalidate on re-enable; a record with only
|
|
2234
|
+
* disabled/no observers stays stale until one arrives. Returns the
|
|
2235
|
+
* number of records invalidated.
|
|
2236
|
+
*
|
|
2237
|
+
* invalidate(['tasks']) matches ['tasks'], ['tasks', '1'], ['tasks', '2', 'x'].
|
|
2238
|
+
*/
|
|
2239
|
+
invalidate(keyPrefix) {
|
|
2240
|
+
const prefix = validateKey(keyPrefix);
|
|
2241
|
+
let count = 0;
|
|
2242
|
+
for (const rec of this._records.values()) {
|
|
2243
|
+
if (!matchesPrefix(rec.key, prefix)) continue;
|
|
2244
|
+
count++;
|
|
2245
|
+
rec.stale = true;
|
|
2246
|
+
const owner = ownerOf(
|
|
2247
|
+
rec,
|
|
2248
|
+
/* requireEnabled */
|
|
2249
|
+
true
|
|
2250
|
+
);
|
|
2251
|
+
if (owner) startRun(rec, owner.fetcher, owner.retry);
|
|
2252
|
+
}
|
|
2253
|
+
return count;
|
|
2254
|
+
}
|
|
2255
|
+
/**
|
|
2256
|
+
* Drop every record. In-flight runs are aborted (a settle() termination).
|
|
2257
|
+
* Live observers re-resolve fresh records on the version bump — enabled
|
|
2258
|
+
* ones revalidate immediately.
|
|
2259
|
+
*/
|
|
2260
|
+
clear() {
|
|
2261
|
+
for (const rec of this._records.values()) {
|
|
2262
|
+
rec.generation++;
|
|
2263
|
+
rec.controller?.abort();
|
|
2264
|
+
rec.terminate?.();
|
|
2265
|
+
rec.controller = null;
|
|
2266
|
+
rec.terminate = null;
|
|
2267
|
+
rec.runPromise = null;
|
|
2268
|
+
rec.running = false;
|
|
2269
|
+
rec.observers.clear();
|
|
2270
|
+
}
|
|
2271
|
+
this._records.clear();
|
|
2272
|
+
this._version.value++;
|
|
2273
|
+
}
|
|
2274
|
+
/**
|
|
2275
|
+
* Imperatively warm a record. Joins an in-flight run; a fresh record
|
|
2276
|
+
* (per the given staleTime) is a no-op. Otherwise starts a run with THE
|
|
2277
|
+
* GIVEN fetcher — an explicit imperative is its own owner. The run
|
|
2278
|
+
* commits even with zero observers. Resolves at run termination.
|
|
2279
|
+
*/
|
|
2280
|
+
prefetch(key, fetcher, staleTime = 0) {
|
|
2281
|
+
const rec = resolveRecord(this, key);
|
|
2282
|
+
if (rec.running) return rec.runPromise ?? Promise.resolve();
|
|
2283
|
+
if (!rec.stale && peek(rec.status) === "success" && staleTime > 0 && Date.now() - peek(rec.fetchedAt) < staleTime) {
|
|
2284
|
+
return Promise.resolve();
|
|
2285
|
+
}
|
|
2286
|
+
return startRun(rec, fetcher, 0);
|
|
2287
|
+
}
|
|
2288
|
+
};
|
|
2289
|
+
function resolveRecord(client, rawKey) {
|
|
2290
|
+
const key = validateKey(rawKey);
|
|
2291
|
+
const serialized = serializeKey(key);
|
|
2292
|
+
let rec = client._records.get(serialized);
|
|
2293
|
+
if (!rec) {
|
|
2294
|
+
rec = {
|
|
2295
|
+
key,
|
|
2296
|
+
serialized,
|
|
2297
|
+
data: signal(void 0),
|
|
2298
|
+
error: signal(null),
|
|
2299
|
+
status: signal("idle"),
|
|
2300
|
+
fetchedAt: signal(0),
|
|
2301
|
+
stale: false,
|
|
2302
|
+
running: false,
|
|
2303
|
+
generation: 0,
|
|
2304
|
+
controller: null,
|
|
2305
|
+
runPromise: null,
|
|
2306
|
+
terminate: null,
|
|
2307
|
+
observers: /* @__PURE__ */ new Set()
|
|
2308
|
+
};
|
|
2309
|
+
client._records.set(serialized, rec);
|
|
2310
|
+
}
|
|
2311
|
+
return rec;
|
|
2312
|
+
}
|
|
2313
|
+
function ownerOf(rec, requireEnabled = false) {
|
|
2314
|
+
let best;
|
|
2315
|
+
let bestDisabled;
|
|
2316
|
+
for (const obs of rec.observers) {
|
|
2317
|
+
if (obs.enabled) {
|
|
2318
|
+
if (!best || obs.mountIndex > best.mountIndex) best = obs;
|
|
2319
|
+
} else if (!requireEnabled) {
|
|
2320
|
+
if (!bestDisabled || obs.mountIndex > bestDisabled.mountIndex) bestDisabled = obs;
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
return best ?? bestDisabled;
|
|
2324
|
+
}
|
|
2325
|
+
function needsRun(rec, observer) {
|
|
2326
|
+
if (rec.running) return false;
|
|
2327
|
+
if (rec.stale) return true;
|
|
2328
|
+
const status = peek(rec.status);
|
|
2329
|
+
if (status === "idle" || status === "error") return true;
|
|
2330
|
+
if (observer.staleTime <= 0) return true;
|
|
2331
|
+
return Date.now() - peek(rec.fetchedAt) >= observer.staleTime;
|
|
2332
|
+
}
|
|
2333
|
+
function startRun(rec, fetcher, retry) {
|
|
2334
|
+
rec.controller?.abort();
|
|
2335
|
+
rec.terminate?.();
|
|
2336
|
+
const generation = ++rec.generation;
|
|
2337
|
+
const controller = new AbortController();
|
|
2338
|
+
const terminate = __enrollPending();
|
|
2339
|
+
rec.controller = controller;
|
|
2340
|
+
rec.terminate = terminate;
|
|
2341
|
+
rec.running = true;
|
|
2342
|
+
rec.stale = false;
|
|
2343
|
+
rec.status.value = "loading";
|
|
2344
|
+
rec.error.value = null;
|
|
2345
|
+
const run = (async () => {
|
|
2346
|
+
try {
|
|
2347
|
+
let lastError = null;
|
|
2348
|
+
for (let attempt = 0; attempt <= retry; attempt++) {
|
|
2349
|
+
if (generation !== rec.generation) return;
|
|
2350
|
+
try {
|
|
2351
|
+
const result = await Promise.resolve().then(() => fetcher(controller.signal));
|
|
2352
|
+
if (generation !== rec.generation) return;
|
|
2353
|
+
rec.data.value = result;
|
|
2354
|
+
rec.error.value = null;
|
|
2355
|
+
rec.fetchedAt.value = Date.now();
|
|
2356
|
+
rec.status.value = "success";
|
|
2357
|
+
return;
|
|
2358
|
+
} catch (cause) {
|
|
2359
|
+
lastError = toError(cause);
|
|
2360
|
+
}
|
|
2361
|
+
}
|
|
2362
|
+
if (generation !== rec.generation) return;
|
|
2363
|
+
rec.error.value = lastError;
|
|
2364
|
+
rec.status.value = "error";
|
|
2365
|
+
} finally {
|
|
2366
|
+
if (generation === rec.generation) {
|
|
2367
|
+
rec.running = false;
|
|
2368
|
+
rec.controller = null;
|
|
2369
|
+
rec.runPromise = null;
|
|
2370
|
+
rec.terminate = null;
|
|
2371
|
+
}
|
|
2372
|
+
terminate();
|
|
2373
|
+
}
|
|
2374
|
+
})();
|
|
2375
|
+
rec.runPromise = run;
|
|
2376
|
+
return run;
|
|
2377
|
+
}
|
|
2378
|
+
function query(keyFn, fetcher, options = {}) {
|
|
2379
|
+
const { staleTime = 0, retry = 0, enabled, initialData } = options;
|
|
2380
|
+
const client = inject(QueryClient);
|
|
2381
|
+
untrack(() => {
|
|
2382
|
+
validateKey(keyFn());
|
|
2383
|
+
});
|
|
2384
|
+
const observer = {
|
|
2385
|
+
mountIndex: nextMountIndex++,
|
|
2386
|
+
fetcher,
|
|
2387
|
+
staleTime,
|
|
2388
|
+
retry,
|
|
2389
|
+
enabled: true,
|
|
2390
|
+
record: null
|
|
2391
|
+
};
|
|
2392
|
+
client._observers++;
|
|
2393
|
+
const recSig = signal(null);
|
|
2394
|
+
const enabledSig = signal(true);
|
|
2395
|
+
let disposed = false;
|
|
2396
|
+
let prevEnabled = null;
|
|
2397
|
+
let prevVersion = -1;
|
|
2398
|
+
const stopEffect = effect(() => {
|
|
2399
|
+
if (disposed) return;
|
|
2400
|
+
const version = client._version.value;
|
|
2401
|
+
const key = keyFn();
|
|
2402
|
+
const on = enabled ? !!enabled() : true;
|
|
2403
|
+
const rec = resolveRecord(client, key);
|
|
2404
|
+
observer.enabled = on;
|
|
2405
|
+
enabledSig.value = on;
|
|
2406
|
+
const moved = rec !== observer.record;
|
|
2407
|
+
if (moved) {
|
|
2408
|
+
observer.record?.observers.delete(observer);
|
|
2409
|
+
rec.observers.add(observer);
|
|
2410
|
+
observer.record = rec;
|
|
2411
|
+
if (initialData !== void 0 && peek(rec.status) === "idle" && peek(rec.data) === void 0) {
|
|
2412
|
+
rec.data.value = initialData;
|
|
2413
|
+
}
|
|
2414
|
+
recSig.value = rec;
|
|
2415
|
+
}
|
|
2416
|
+
const reEnabled = prevEnabled === false && on;
|
|
2417
|
+
const versionChanged = version !== prevVersion;
|
|
2418
|
+
prevEnabled = on;
|
|
2419
|
+
prevVersion = version;
|
|
2420
|
+
if (on && (moved || reEnabled || versionChanged) && needsRun(rec, observer)) {
|
|
2421
|
+
const owner = ownerOf(rec) ?? observer;
|
|
2422
|
+
startRun(rec, owner.fetcher, owner.retry);
|
|
2423
|
+
}
|
|
2424
|
+
});
|
|
2425
|
+
const dispose = () => {
|
|
2426
|
+
if (disposed) return;
|
|
2427
|
+
disposed = true;
|
|
2428
|
+
observer.record?.observers.delete(observer);
|
|
2429
|
+
observer.record = null;
|
|
2430
|
+
client._observers--;
|
|
2431
|
+
stopEffect();
|
|
2432
|
+
};
|
|
2433
|
+
if (hasContext()) {
|
|
2434
|
+
getCurrentComponent().addDisposer(dispose);
|
|
2435
|
+
}
|
|
2436
|
+
const refetch = () => {
|
|
2437
|
+
if (disposed) return;
|
|
2438
|
+
const rec = observer.record;
|
|
2439
|
+
if (!rec || !observer.enabled) return;
|
|
2440
|
+
if (rec.running) return;
|
|
2441
|
+
const owner = ownerOf(rec) ?? observer;
|
|
2442
|
+
startRun(rec, owner.fetcher, owner.retry);
|
|
2443
|
+
};
|
|
2444
|
+
const result = {
|
|
2445
|
+
data: computed(() => {
|
|
2446
|
+
const rec = recSig.value;
|
|
2447
|
+
return rec ? rec.data.value : initialData;
|
|
2448
|
+
}),
|
|
2449
|
+
loading: computed(() => enabledSig.value && recSig.value?.status.value === "loading"),
|
|
2450
|
+
error: computed(() => recSig.value?.error.value ?? null),
|
|
2451
|
+
status: computed(() => recSig.value?.status.value ?? "idle"),
|
|
2452
|
+
refetch,
|
|
2453
|
+
dispose
|
|
2454
|
+
};
|
|
2455
|
+
return disposable(result, dispose);
|
|
2456
|
+
}
|
|
2457
|
+
|
|
2458
|
+
// src/resource.ts
|
|
2459
|
+
function resource(source, loader) {
|
|
2460
|
+
const data = signal(void 0);
|
|
2461
|
+
const loading = signal(false);
|
|
2462
|
+
const error = signal(null);
|
|
2463
|
+
const refreshVersion = signal(0);
|
|
2464
|
+
let generation = 0;
|
|
2465
|
+
let disposed = false;
|
|
2466
|
+
let stopEffect = () => {
|
|
2467
|
+
};
|
|
2468
|
+
const dispose = () => {
|
|
2469
|
+
if (disposed) return;
|
|
2470
|
+
disposed = true;
|
|
2471
|
+
generation++;
|
|
2472
|
+
stopEffect();
|
|
2473
|
+
loading.value = false;
|
|
2474
|
+
};
|
|
2475
|
+
if (hasContext()) {
|
|
2476
|
+
getCurrentComponent().addDisposer(dispose);
|
|
2477
|
+
}
|
|
2478
|
+
stopEffect = effect(() => {
|
|
2479
|
+
refreshVersion.value;
|
|
2480
|
+
const currentGeneration = ++generation;
|
|
2481
|
+
let sourceValue;
|
|
2482
|
+
try {
|
|
2483
|
+
sourceValue = source();
|
|
2484
|
+
} catch (cause) {
|
|
2485
|
+
loading.value = false;
|
|
2486
|
+
error.value = toError2(cause);
|
|
2487
|
+
return;
|
|
2488
|
+
}
|
|
2489
|
+
if (sourceValue === void 0) {
|
|
2490
|
+
data.value = void 0;
|
|
2491
|
+
loading.value = false;
|
|
2492
|
+
error.value = null;
|
|
2493
|
+
return;
|
|
2494
|
+
}
|
|
2495
|
+
const controller = new AbortController();
|
|
2496
|
+
loading.value = true;
|
|
2497
|
+
error.value = null;
|
|
2498
|
+
const enabledSource = sourceValue;
|
|
2499
|
+
const isStale = () => disposed || controller.signal.aborted || currentGeneration !== generation;
|
|
2500
|
+
const settled = __enrollPending();
|
|
2501
|
+
void Promise.resolve().then(async () => {
|
|
2502
|
+
if (isStale()) {
|
|
2503
|
+
settled();
|
|
2504
|
+
return;
|
|
2505
|
+
}
|
|
2506
|
+
try {
|
|
2507
|
+
const value = await loader(enabledSource, controller.signal);
|
|
2508
|
+
if (isStale()) {
|
|
2509
|
+
settled();
|
|
2510
|
+
return;
|
|
2511
|
+
}
|
|
2512
|
+
data.value = value;
|
|
2513
|
+
loading.value = false;
|
|
2514
|
+
settled();
|
|
2515
|
+
} catch (cause) {
|
|
2516
|
+
if (isStale()) {
|
|
2517
|
+
settled();
|
|
2518
|
+
return;
|
|
2519
|
+
}
|
|
2520
|
+
error.value = toError2(cause);
|
|
2521
|
+
loading.value = false;
|
|
2522
|
+
settled();
|
|
2523
|
+
}
|
|
2524
|
+
});
|
|
2525
|
+
return () => {
|
|
2526
|
+
if (currentGeneration === generation) generation++;
|
|
2527
|
+
controller.abort();
|
|
2528
|
+
settled();
|
|
2529
|
+
};
|
|
2530
|
+
});
|
|
2531
|
+
const result = {
|
|
2532
|
+
data,
|
|
2533
|
+
loading,
|
|
2534
|
+
error,
|
|
2535
|
+
refresh() {
|
|
2536
|
+
if (!disposed) refreshVersion.value++;
|
|
2537
|
+
},
|
|
2538
|
+
dispose
|
|
2539
|
+
};
|
|
2540
|
+
return disposable(result, dispose);
|
|
2541
|
+
}
|
|
2542
|
+
function toError2(cause) {
|
|
2543
|
+
if (cause instanceof Error) return cause;
|
|
2544
|
+
try {
|
|
2545
|
+
return new Error(String(cause));
|
|
2546
|
+
} catch {
|
|
2547
|
+
return new Error("Unknown error");
|
|
2548
|
+
}
|
|
2549
|
+
}
|
|
2550
|
+
|
|
2551
|
+
// src/view-transition.ts
|
|
2552
|
+
function supportsTransitionTypes() {
|
|
2553
|
+
return typeof ViewTransition !== "undefined" && "types" in ViewTransition.prototype;
|
|
2554
|
+
}
|
|
2555
|
+
function viewTransition(update, options = {}) {
|
|
2556
|
+
const commit = () => {
|
|
2557
|
+
update();
|
|
2558
|
+
flush();
|
|
2559
|
+
};
|
|
2560
|
+
if (typeof document === "undefined" || typeof document.startViewTransition !== "function") {
|
|
2561
|
+
commit();
|
|
2562
|
+
return null;
|
|
2563
|
+
}
|
|
2564
|
+
const types = options.types;
|
|
2565
|
+
return types !== void 0 && types.length > 0 && supportsTransitionTypes() ? document.startViewTransition({ update: commit, types }) : document.startViewTransition(commit);
|
|
2566
|
+
}
|
|
2567
|
+
export {
|
|
2568
|
+
Emitter,
|
|
2569
|
+
QueryClient,
|
|
2570
|
+
SIGNAL_BRAND,
|
|
2571
|
+
children,
|
|
2572
|
+
component,
|
|
2573
|
+
computed,
|
|
2574
|
+
createContext,
|
|
2575
|
+
createToken,
|
|
2576
|
+
each,
|
|
2577
|
+
effect,
|
|
2578
|
+
el,
|
|
2579
|
+
flush,
|
|
2580
|
+
flush as flushEffects,
|
|
2581
|
+
getCurrentComponent,
|
|
2582
|
+
hasContext,
|
|
2583
|
+
html,
|
|
2584
|
+
inject,
|
|
2585
|
+
isRef,
|
|
2586
|
+
isSignal,
|
|
2587
|
+
onCleanup,
|
|
2588
|
+
onMount,
|
|
2589
|
+
provide,
|
|
2590
|
+
query,
|
|
2591
|
+
raw,
|
|
2592
|
+
ref,
|
|
2593
|
+
resetInjector,
|
|
2594
|
+
resource,
|
|
2595
|
+
runWithContext,
|
|
2596
|
+
sanitized,
|
|
2597
|
+
setDevMode,
|
|
2598
|
+
setSanitizerFallback,
|
|
2599
|
+
settle,
|
|
2600
|
+
signal,
|
|
2601
|
+
tick,
|
|
2602
|
+
untrack,
|
|
2603
|
+
useHostEvent,
|
|
2604
|
+
viewTransition,
|
|
2605
|
+
when
|
|
2606
|
+
};
|
|
2607
|
+
//# sourceMappingURL=index.js.map
|