@constela/runtime 0.7.0 → 0.9.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/dist/index.d.ts +12 -0
- package/dist/index.js +386 -2
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -50,6 +50,13 @@ declare function createStateStore(definitions: Record<string, StateDefinition>):
|
|
|
50
50
|
interface EvaluationContext {
|
|
51
51
|
state: StateStore;
|
|
52
52
|
locals: Record<string, unknown>;
|
|
53
|
+
route?: {
|
|
54
|
+
params: Record<string, string>;
|
|
55
|
+
query: Record<string, string>;
|
|
56
|
+
path: string;
|
|
57
|
+
};
|
|
58
|
+
imports?: Record<string, unknown>;
|
|
59
|
+
refs?: Record<string, Element>;
|
|
53
60
|
}
|
|
54
61
|
declare function evaluate(expr: CompiledExpression, ctx: EvaluationContext): unknown;
|
|
55
62
|
|
|
@@ -60,6 +67,9 @@ declare function evaluate(expr: CompiledExpression, ctx: EvaluationContext): unk
|
|
|
60
67
|
* - set: Update state with value
|
|
61
68
|
* - update: Increment/decrement numbers, push/pop/remove for arrays
|
|
62
69
|
* - fetch: Make HTTP requests with onSuccess/onError handlers
|
|
70
|
+
* - storage: localStorage/sessionStorage operations
|
|
71
|
+
* - clipboard: Clipboard API operations
|
|
72
|
+
* - navigate: Page navigation
|
|
63
73
|
*/
|
|
64
74
|
|
|
65
75
|
interface ActionContext {
|
|
@@ -67,6 +77,8 @@ interface ActionContext {
|
|
|
67
77
|
actions: Record<string, CompiledAction>;
|
|
68
78
|
locals: Record<string, unknown>;
|
|
69
79
|
eventPayload?: unknown;
|
|
80
|
+
refs?: Record<string, Element>;
|
|
81
|
+
subscriptions?: (() => void)[];
|
|
70
82
|
}
|
|
71
83
|
declare function executeAction(action: CompiledAction, ctx: ActionContext): Promise<void>;
|
|
72
84
|
|
package/dist/index.js
CHANGED
|
@@ -172,12 +172,61 @@ function evaluate(expr, ctx) {
|
|
|
172
172
|
}
|
|
173
173
|
return value;
|
|
174
174
|
}
|
|
175
|
+
case "route": {
|
|
176
|
+
const source = expr.source ?? "param";
|
|
177
|
+
const routeCtx = ctx.route;
|
|
178
|
+
if (!routeCtx) return "";
|
|
179
|
+
switch (source) {
|
|
180
|
+
case "param":
|
|
181
|
+
return routeCtx.params[expr.name] ?? "";
|
|
182
|
+
case "query":
|
|
183
|
+
return routeCtx.query[expr.name] ?? "";
|
|
184
|
+
case "path":
|
|
185
|
+
return routeCtx.path;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
case "import": {
|
|
189
|
+
const importData = ctx.imports?.[expr.name];
|
|
190
|
+
if (importData === void 0) return void 0;
|
|
191
|
+
if (expr.path) {
|
|
192
|
+
return getNestedValue(importData, expr.path);
|
|
193
|
+
}
|
|
194
|
+
return importData;
|
|
195
|
+
}
|
|
196
|
+
case "ref":
|
|
197
|
+
return ctx.refs?.[expr.name] ?? null;
|
|
175
198
|
default: {
|
|
176
199
|
const _exhaustiveCheck = expr;
|
|
177
200
|
throw new Error(`Unknown expression type: ${JSON.stringify(_exhaustiveCheck)}`);
|
|
178
201
|
}
|
|
179
202
|
}
|
|
180
203
|
}
|
|
204
|
+
function getNestedValue(obj, path) {
|
|
205
|
+
const forbiddenKeys = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
206
|
+
const parts = path.split(".");
|
|
207
|
+
let value = obj;
|
|
208
|
+
for (const part of parts) {
|
|
209
|
+
if (forbiddenKeys.has(part)) {
|
|
210
|
+
return void 0;
|
|
211
|
+
}
|
|
212
|
+
if (value == null) {
|
|
213
|
+
return void 0;
|
|
214
|
+
}
|
|
215
|
+
if (Array.isArray(value)) {
|
|
216
|
+
const index = Number(part);
|
|
217
|
+
if (Number.isInteger(index) && index >= 0) {
|
|
218
|
+
value = value[index];
|
|
219
|
+
} else {
|
|
220
|
+
value = value[part];
|
|
221
|
+
}
|
|
222
|
+
} else if (typeof value === "object") {
|
|
223
|
+
value = value[part];
|
|
224
|
+
} else {
|
|
225
|
+
return void 0;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return value;
|
|
229
|
+
}
|
|
181
230
|
function evaluateBinary(op, left, right, ctx) {
|
|
182
231
|
if (op === "&&") {
|
|
183
232
|
const leftVal2 = evaluate(left, ctx);
|
|
@@ -243,7 +292,112 @@ function evaluateBinary(op, left, right, ctx) {
|
|
|
243
292
|
// src/action/executor.ts
|
|
244
293
|
async function executeAction(action, ctx) {
|
|
245
294
|
for (const step of action.steps) {
|
|
246
|
-
|
|
295
|
+
if (step.do === "set" || step.do === "update") {
|
|
296
|
+
executeStepSync(step, ctx);
|
|
297
|
+
} else {
|
|
298
|
+
await executeStep(step, ctx);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
function executeStepSync(step, ctx) {
|
|
303
|
+
switch (step.do) {
|
|
304
|
+
case "set":
|
|
305
|
+
executeSetStepSync(step.target, step.value, ctx);
|
|
306
|
+
break;
|
|
307
|
+
case "update":
|
|
308
|
+
executeUpdateStepSync(step, ctx);
|
|
309
|
+
break;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
function executeSetStepSync(target, value, ctx) {
|
|
313
|
+
const evalCtx = { state: ctx.state, locals: ctx.locals };
|
|
314
|
+
const newValue = evaluate(value, evalCtx);
|
|
315
|
+
ctx.state.set(target, newValue);
|
|
316
|
+
}
|
|
317
|
+
function executeUpdateStepSync(step, ctx) {
|
|
318
|
+
const { target, operation, value } = step;
|
|
319
|
+
const evalCtx = { state: ctx.state, locals: ctx.locals };
|
|
320
|
+
const currentValue = ctx.state.get(target);
|
|
321
|
+
switch (operation) {
|
|
322
|
+
case "increment": {
|
|
323
|
+
const evalResult = value ? evaluate(value, evalCtx) : 1;
|
|
324
|
+
const amount = typeof evalResult === "number" ? evalResult : 1;
|
|
325
|
+
const current = typeof currentValue === "number" ? currentValue : 0;
|
|
326
|
+
ctx.state.set(target, current + amount);
|
|
327
|
+
break;
|
|
328
|
+
}
|
|
329
|
+
case "decrement": {
|
|
330
|
+
const evalResult = value ? evaluate(value, evalCtx) : 1;
|
|
331
|
+
const amount = typeof evalResult === "number" ? evalResult : 1;
|
|
332
|
+
const current = typeof currentValue === "number" ? currentValue : 0;
|
|
333
|
+
ctx.state.set(target, current - amount);
|
|
334
|
+
break;
|
|
335
|
+
}
|
|
336
|
+
case "push": {
|
|
337
|
+
const item = value ? evaluate(value, evalCtx) : void 0;
|
|
338
|
+
const arr = Array.isArray(currentValue) ? currentValue : [];
|
|
339
|
+
ctx.state.set(target, [...arr, item]);
|
|
340
|
+
break;
|
|
341
|
+
}
|
|
342
|
+
case "pop": {
|
|
343
|
+
const arr = Array.isArray(currentValue) ? currentValue : [];
|
|
344
|
+
ctx.state.set(target, arr.slice(0, -1));
|
|
345
|
+
break;
|
|
346
|
+
}
|
|
347
|
+
case "remove": {
|
|
348
|
+
const removeValue = value ? evaluate(value, evalCtx) : void 0;
|
|
349
|
+
const arr = Array.isArray(currentValue) ? currentValue : [];
|
|
350
|
+
if (typeof removeValue === "number") {
|
|
351
|
+
ctx.state.set(target, arr.filter((_, i) => i !== removeValue));
|
|
352
|
+
} else {
|
|
353
|
+
ctx.state.set(target, arr.filter((x) => x !== removeValue));
|
|
354
|
+
}
|
|
355
|
+
break;
|
|
356
|
+
}
|
|
357
|
+
case "toggle": {
|
|
358
|
+
const current = typeof currentValue === "boolean" ? currentValue : false;
|
|
359
|
+
ctx.state.set(target, !current);
|
|
360
|
+
break;
|
|
361
|
+
}
|
|
362
|
+
case "merge": {
|
|
363
|
+
const evalResult = value ? evaluate(value, evalCtx) : {};
|
|
364
|
+
const mergeValue = typeof evalResult === "object" && evalResult !== null ? evalResult : {};
|
|
365
|
+
const current = typeof currentValue === "object" && currentValue !== null ? currentValue : {};
|
|
366
|
+
ctx.state.set(target, { ...current, ...mergeValue });
|
|
367
|
+
break;
|
|
368
|
+
}
|
|
369
|
+
case "replaceAt": {
|
|
370
|
+
const idx = step.index ? evaluate(step.index, evalCtx) : 0;
|
|
371
|
+
const newValue = value ? evaluate(value, evalCtx) : void 0;
|
|
372
|
+
const arr = Array.isArray(currentValue) ? [...currentValue] : [];
|
|
373
|
+
if (typeof idx === "number" && idx >= 0 && idx < arr.length) {
|
|
374
|
+
arr[idx] = newValue;
|
|
375
|
+
}
|
|
376
|
+
ctx.state.set(target, arr);
|
|
377
|
+
break;
|
|
378
|
+
}
|
|
379
|
+
case "insertAt": {
|
|
380
|
+
const idx = step.index ? evaluate(step.index, evalCtx) : 0;
|
|
381
|
+
const newValue = value ? evaluate(value, evalCtx) : void 0;
|
|
382
|
+
const arr = Array.isArray(currentValue) ? [...currentValue] : [];
|
|
383
|
+
if (typeof idx === "number" && idx >= 0) {
|
|
384
|
+
arr.splice(idx, 0, newValue);
|
|
385
|
+
}
|
|
386
|
+
ctx.state.set(target, arr);
|
|
387
|
+
break;
|
|
388
|
+
}
|
|
389
|
+
case "splice": {
|
|
390
|
+
const idx = step.index ? evaluate(step.index, evalCtx) : 0;
|
|
391
|
+
const delCount = step.deleteCount ? evaluate(step.deleteCount, evalCtx) : 0;
|
|
392
|
+
const items = value ? evaluate(value, evalCtx) : [];
|
|
393
|
+
const arr = Array.isArray(currentValue) ? [...currentValue] : [];
|
|
394
|
+
if (typeof idx === "number" && typeof delCount === "number") {
|
|
395
|
+
const insertItems = Array.isArray(items) ? items : [];
|
|
396
|
+
arr.splice(idx, delCount, ...insertItems);
|
|
397
|
+
}
|
|
398
|
+
ctx.state.set(target, arr);
|
|
399
|
+
break;
|
|
400
|
+
}
|
|
247
401
|
}
|
|
248
402
|
}
|
|
249
403
|
async function executeStep(step, ctx) {
|
|
@@ -257,6 +411,27 @@ async function executeStep(step, ctx) {
|
|
|
257
411
|
case "fetch":
|
|
258
412
|
await executeFetchStep(step, ctx);
|
|
259
413
|
break;
|
|
414
|
+
case "storage":
|
|
415
|
+
await executeStorageStep(step, ctx);
|
|
416
|
+
break;
|
|
417
|
+
case "clipboard":
|
|
418
|
+
await executeClipboardStep(step, ctx);
|
|
419
|
+
break;
|
|
420
|
+
case "navigate":
|
|
421
|
+
await executeNavigateStep(step, ctx);
|
|
422
|
+
break;
|
|
423
|
+
case "import":
|
|
424
|
+
await executeImportStep(step, ctx);
|
|
425
|
+
break;
|
|
426
|
+
case "call":
|
|
427
|
+
await executeCallStep(step, ctx);
|
|
428
|
+
break;
|
|
429
|
+
case "subscribe":
|
|
430
|
+
await executeSubscribeStep(step, ctx);
|
|
431
|
+
break;
|
|
432
|
+
case "dispose":
|
|
433
|
+
await executeDisposeStep(step, ctx);
|
|
434
|
+
break;
|
|
260
435
|
}
|
|
261
436
|
}
|
|
262
437
|
async function executeSetStep(target, value, ctx) {
|
|
@@ -373,13 +548,66 @@ async function executeFetchStep(step, ctx) {
|
|
|
373
548
|
}
|
|
374
549
|
}
|
|
375
550
|
} else {
|
|
551
|
+
ctx.locals["error"] = {
|
|
552
|
+
message: `HTTP error: ${response.status} ${response.statusText}`,
|
|
553
|
+
name: "HTTPError"
|
|
554
|
+
};
|
|
376
555
|
if (step.onError) {
|
|
377
556
|
for (const errorStep of step.onError) {
|
|
378
557
|
await executeStep(errorStep, ctx);
|
|
379
558
|
}
|
|
380
559
|
}
|
|
381
560
|
}
|
|
382
|
-
} catch (
|
|
561
|
+
} catch (err) {
|
|
562
|
+
ctx.locals["error"] = {
|
|
563
|
+
message: err instanceof Error ? err.message : String(err),
|
|
564
|
+
name: err instanceof Error ? err.name : "Error"
|
|
565
|
+
};
|
|
566
|
+
if (step.onError) {
|
|
567
|
+
for (const errorStep of step.onError) {
|
|
568
|
+
await executeStep(errorStep, ctx);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
async function executeStorageStep(step, ctx) {
|
|
574
|
+
const evalCtx = { state: ctx.state, locals: ctx.locals };
|
|
575
|
+
const key = evaluate(step.key, evalCtx);
|
|
576
|
+
const storage = step.storage === "local" ? localStorage : sessionStorage;
|
|
577
|
+
try {
|
|
578
|
+
switch (step.operation) {
|
|
579
|
+
case "get": {
|
|
580
|
+
const value = storage.getItem(key);
|
|
581
|
+
if (step.result) {
|
|
582
|
+
try {
|
|
583
|
+
ctx.locals[step.result] = value !== null ? JSON.parse(value) : null;
|
|
584
|
+
} catch {
|
|
585
|
+
ctx.locals[step.result] = value;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
break;
|
|
589
|
+
}
|
|
590
|
+
case "set": {
|
|
591
|
+
const setValue = step.value ? evaluate(step.value, evalCtx) : void 0;
|
|
592
|
+
const valueToStore = JSON.stringify(setValue);
|
|
593
|
+
storage.setItem(key, valueToStore);
|
|
594
|
+
break;
|
|
595
|
+
}
|
|
596
|
+
case "remove": {
|
|
597
|
+
storage.removeItem(key);
|
|
598
|
+
break;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
if (step.onSuccess) {
|
|
602
|
+
for (const successStep of step.onSuccess) {
|
|
603
|
+
await executeStep(successStep, ctx);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
} catch (err) {
|
|
607
|
+
ctx.locals["error"] = {
|
|
608
|
+
message: err instanceof Error ? err.message : String(err),
|
|
609
|
+
name: err instanceof Error ? err.name : "Error"
|
|
610
|
+
};
|
|
383
611
|
if (step.onError) {
|
|
384
612
|
for (const errorStep of step.onError) {
|
|
385
613
|
await executeStep(errorStep, ctx);
|
|
@@ -387,6 +615,145 @@ async function executeFetchStep(step, ctx) {
|
|
|
387
615
|
}
|
|
388
616
|
}
|
|
389
617
|
}
|
|
618
|
+
async function executeClipboardStep(step, ctx) {
|
|
619
|
+
const evalCtx = { state: ctx.state, locals: ctx.locals };
|
|
620
|
+
try {
|
|
621
|
+
switch (step.operation) {
|
|
622
|
+
case "write": {
|
|
623
|
+
const value = step.value ? evaluate(step.value, evalCtx) : "";
|
|
624
|
+
const text = typeof value === "string" ? value : String(value);
|
|
625
|
+
await navigator.clipboard.writeText(text);
|
|
626
|
+
break;
|
|
627
|
+
}
|
|
628
|
+
case "read": {
|
|
629
|
+
const readText = await navigator.clipboard.readText();
|
|
630
|
+
if (step.result) {
|
|
631
|
+
ctx.locals[step.result] = readText;
|
|
632
|
+
}
|
|
633
|
+
break;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
if (step.onSuccess) {
|
|
637
|
+
for (const successStep of step.onSuccess) {
|
|
638
|
+
await executeStep(successStep, ctx);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
} catch (err) {
|
|
642
|
+
ctx.locals["error"] = {
|
|
643
|
+
message: err instanceof Error ? err.message : String(err),
|
|
644
|
+
name: err instanceof Error ? err.name : "Error"
|
|
645
|
+
};
|
|
646
|
+
if (step.onError) {
|
|
647
|
+
for (const errorStep of step.onError) {
|
|
648
|
+
await executeStep(errorStep, ctx);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
async function executeNavigateStep(step, ctx) {
|
|
654
|
+
const evalCtx = { state: ctx.state, locals: ctx.locals };
|
|
655
|
+
const url = evaluate(step.url, evalCtx);
|
|
656
|
+
const target = step.target ?? "_self";
|
|
657
|
+
if (target === "_blank") {
|
|
658
|
+
window.open(url, "_blank");
|
|
659
|
+
} else if (step.replace) {
|
|
660
|
+
window.location.replace(url);
|
|
661
|
+
} else {
|
|
662
|
+
window.location.assign(url);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
async function executeImportStep(step, ctx) {
|
|
666
|
+
try {
|
|
667
|
+
const module = await import(
|
|
668
|
+
/* @vite-ignore */
|
|
669
|
+
step.module
|
|
670
|
+
);
|
|
671
|
+
ctx.locals[step.result] = module;
|
|
672
|
+
if (step.onSuccess) {
|
|
673
|
+
for (const successStep of step.onSuccess) {
|
|
674
|
+
await executeStep(successStep, ctx);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
} catch (err) {
|
|
678
|
+
ctx.locals["error"] = {
|
|
679
|
+
message: err instanceof Error ? err.message : String(err),
|
|
680
|
+
name: err instanceof Error ? err.name : "Error"
|
|
681
|
+
};
|
|
682
|
+
if (step.onError) {
|
|
683
|
+
for (const errorStep of step.onError) {
|
|
684
|
+
await executeStep(errorStep, ctx);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
async function executeCallStep(step, ctx) {
|
|
690
|
+
const evalCtx = { state: ctx.state, locals: ctx.locals, ...ctx.refs && { refs: ctx.refs } };
|
|
691
|
+
try {
|
|
692
|
+
const target = evaluate(step.target, evalCtx);
|
|
693
|
+
const args = step.args?.map((arg) => evaluate(arg, evalCtx)) ?? [];
|
|
694
|
+
if (typeof target === "function") {
|
|
695
|
+
const result = await target(...args);
|
|
696
|
+
if (step.result) {
|
|
697
|
+
ctx.locals[step.result] = result;
|
|
698
|
+
}
|
|
699
|
+
} else {
|
|
700
|
+
throw new Error(`Target is not callable: received ${typeof target}`);
|
|
701
|
+
}
|
|
702
|
+
if (step.onSuccess) {
|
|
703
|
+
for (const successStep of step.onSuccess) {
|
|
704
|
+
await executeStep(successStep, ctx);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
} catch (err) {
|
|
708
|
+
ctx.locals["error"] = {
|
|
709
|
+
message: err instanceof Error ? err.message : String(err),
|
|
710
|
+
name: err instanceof Error ? err.name : "Error"
|
|
711
|
+
};
|
|
712
|
+
if (step.onError) {
|
|
713
|
+
for (const errorStep of step.onError) {
|
|
714
|
+
await executeStep(errorStep, ctx);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
async function executeSubscribeStep(step, ctx) {
|
|
720
|
+
const evalCtx = { state: ctx.state, locals: ctx.locals, ...ctx.refs && { refs: ctx.refs } };
|
|
721
|
+
const target = evaluate(step.target, evalCtx);
|
|
722
|
+
if (target && typeof target === "object" && step.event in target) {
|
|
723
|
+
const eventMethod = target[step.event];
|
|
724
|
+
if (typeof eventMethod === "function") {
|
|
725
|
+
const disposable = eventMethod.call(target, async (eventData) => {
|
|
726
|
+
const action = ctx.actions[step.action];
|
|
727
|
+
if (action) {
|
|
728
|
+
const subscriptionCtx = {
|
|
729
|
+
...ctx,
|
|
730
|
+
locals: { ...ctx.locals, event: eventData }
|
|
731
|
+
};
|
|
732
|
+
await executeAction(action, subscriptionCtx);
|
|
733
|
+
}
|
|
734
|
+
});
|
|
735
|
+
if (ctx.subscriptions) {
|
|
736
|
+
if (disposable && typeof disposable === "object" && "dispose" in disposable && typeof disposable.dispose === "function") {
|
|
737
|
+
ctx.subscriptions.push(() => disposable.dispose());
|
|
738
|
+
} else if (typeof disposable === "function") {
|
|
739
|
+
ctx.subscriptions.push(disposable);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
async function executeDisposeStep(step, ctx) {
|
|
746
|
+
const evalCtx = { state: ctx.state, locals: ctx.locals, ...ctx.refs && { refs: ctx.refs } };
|
|
747
|
+
const target = evaluate(step.target, evalCtx);
|
|
748
|
+
if (target && typeof target === "object") {
|
|
749
|
+
const obj = target;
|
|
750
|
+
if (typeof obj["dispose"] === "function") {
|
|
751
|
+
obj["dispose"]();
|
|
752
|
+
} else if (typeof obj["destroy"] === "function") {
|
|
753
|
+
obj["destroy"]();
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
}
|
|
390
757
|
|
|
391
758
|
// src/renderer/markdown.ts
|
|
392
759
|
import { marked } from "marked";
|
|
@@ -716,6 +1083,17 @@ function createApp(program, mount) {
|
|
|
716
1083
|
locals: {},
|
|
717
1084
|
cleanups
|
|
718
1085
|
};
|
|
1086
|
+
const actionCtx = {
|
|
1087
|
+
state,
|
|
1088
|
+
actions,
|
|
1089
|
+
locals: {}
|
|
1090
|
+
};
|
|
1091
|
+
if (program.lifecycle?.onMount) {
|
|
1092
|
+
const onMountAction = actions[program.lifecycle.onMount];
|
|
1093
|
+
if (onMountAction) {
|
|
1094
|
+
void executeAction(onMountAction, actionCtx);
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
719
1097
|
const rootNode = render(program.view, ctx);
|
|
720
1098
|
mount.appendChild(rootNode);
|
|
721
1099
|
let destroyed = false;
|
|
@@ -723,6 +1101,12 @@ function createApp(program, mount) {
|
|
|
723
1101
|
destroy() {
|
|
724
1102
|
if (destroyed) return;
|
|
725
1103
|
destroyed = true;
|
|
1104
|
+
if (program.lifecycle?.onUnmount) {
|
|
1105
|
+
const onUnmountAction = actions[program.lifecycle.onUnmount];
|
|
1106
|
+
if (onUnmountAction) {
|
|
1107
|
+
void executeAction(onUnmountAction, actionCtx);
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
726
1110
|
for (const cleanup of cleanups) {
|
|
727
1111
|
cleanup();
|
|
728
1112
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@constela/runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Runtime DOM renderer for Constela UI framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
"dompurify": "^3.3.1",
|
|
19
19
|
"marked": "^17.0.1",
|
|
20
20
|
"shiki": "^3.20.0",
|
|
21
|
-
"@constela/compiler": "0.
|
|
22
|
-
"@constela/core": "0.
|
|
21
|
+
"@constela/compiler": "0.6.0",
|
|
22
|
+
"@constela/core": "0.6.0"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@types/dompurify": "^3.2.0",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"tsup": "^8.0.0",
|
|
30
30
|
"typescript": "^5.3.0",
|
|
31
31
|
"vitest": "^2.0.0",
|
|
32
|
-
"@constela/server": "0.
|
|
32
|
+
"@constela/server": "2.0.0"
|
|
33
33
|
},
|
|
34
34
|
"engines": {
|
|
35
35
|
"node": ">=20.0.0"
|