tutuca 0.9.52 → 0.9.54
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 +1 -1
- package/dist/tutuca-cli.js +36 -22
- package/dist/tutuca-dev.js +84 -25
- package/dist/tutuca-dev.min.js +2 -2
- package/dist/tutuca-extra.js +7 -13
- package/dist/tutuca-extra.min.js +2 -2
- package/dist/tutuca.js +7 -13
- package/dist/tutuca.min.js +2 -2
- package/package.json +1 -1
- package/skill/tutuca/SKILL.md +5 -3
- package/skill/tutuca/core.md +9 -4
- package/skill/tutuca/testing.md +266 -0
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Zero-dependency batteries included SPA framework.
|
|
4
4
|
|
|
5
5
|
- **Single file, no build, no dependencies, no setup** — a script tag is all you need
|
|
6
|
-
- **Batteries included** — state management, side effects, automatic memoization, drag and drop and more
|
|
6
|
+
- **Batteries included** — state management, side effects, automatic memoization, drag and drop, testing, CLI tooling, LLM skills and more
|
|
7
7
|
- **Fits in your head** (and the context window)
|
|
8
8
|
- **View source friendly** — step through the whole stack
|
|
9
9
|
- **As much HTML as possible, as little JS as needed**
|
package/dist/tutuca-cli.js
CHANGED
|
@@ -3615,16 +3615,25 @@ function getFieldMethods(field) {
|
|
|
3615
3615
|
name: `reset${uname}`,
|
|
3616
3616
|
sig: `reset${uname}()`,
|
|
3617
3617
|
desc: "Reset to default value"
|
|
3618
|
-
}
|
|
3618
|
+
}
|
|
3619
|
+
];
|
|
3620
|
+
const truthy = [
|
|
3619
3621
|
{
|
|
3620
|
-
name: `is${uname}
|
|
3621
|
-
sig: `is${uname}
|
|
3622
|
-
desc: "Check if
|
|
3622
|
+
name: `is${uname}Truthy`,
|
|
3623
|
+
sig: `is${uname}Truthy()`,
|
|
3624
|
+
desc: "Check if value is truthy"
|
|
3623
3625
|
},
|
|
3624
3626
|
{
|
|
3625
|
-
name: `is${uname}
|
|
3626
|
-
sig: `is${uname}
|
|
3627
|
-
desc: "Check if
|
|
3627
|
+
name: `is${uname}Falsy`,
|
|
3628
|
+
sig: `is${uname}Falsy()`,
|
|
3629
|
+
desc: "Check if value is falsy"
|
|
3630
|
+
}
|
|
3631
|
+
];
|
|
3632
|
+
const nullable = [
|
|
3633
|
+
{
|
|
3634
|
+
name: `is${uname}Null`,
|
|
3635
|
+
sig: `is${uname}Null()`,
|
|
3636
|
+
desc: "Check if value is null/undefined"
|
|
3628
3637
|
}
|
|
3629
3638
|
];
|
|
3630
3639
|
switch (type3) {
|
|
@@ -3634,21 +3643,28 @@ function getFieldMethods(field) {
|
|
|
3634
3643
|
name: `toggle${uname}`,
|
|
3635
3644
|
sig: `toggle${uname}()`,
|
|
3636
3645
|
desc: "Toggle boolean value"
|
|
3637
|
-
});
|
|
3646
|
+
}, ...truthy);
|
|
3647
|
+
break;
|
|
3648
|
+
case "int":
|
|
3649
|
+
case "float":
|
|
3650
|
+
methods.push(...truthy);
|
|
3651
|
+
break;
|
|
3652
|
+
case "any":
|
|
3653
|
+
methods.push(...nullable, ...truthy);
|
|
3638
3654
|
break;
|
|
3639
3655
|
case "text":
|
|
3640
3656
|
methods.push({
|
|
3641
3657
|
name: `is${uname}Empty`,
|
|
3642
3658
|
sig: `is${uname}Empty()`,
|
|
3643
3659
|
desc: "Check if string is empty"
|
|
3644
|
-
}, { name: `${name}Len`, sig: `${name}Len()`, desc: "Get string length" });
|
|
3660
|
+
}, { name: `${name}Len`, sig: `${name}Len()`, desc: "Get string length" }, ...truthy);
|
|
3645
3661
|
break;
|
|
3646
3662
|
case "list":
|
|
3647
3663
|
methods.push({
|
|
3648
3664
|
name: `is${uname}Empty`,
|
|
3649
3665
|
sig: `is${uname}Empty()`,
|
|
3650
3666
|
desc: "Check if list is empty"
|
|
3651
|
-
}, { name: `${name}Len`, sig: `${name}Len()`, desc: "Get list size" }, {
|
|
3667
|
+
}, { name: `${name}Len`, sig: `${name}Len()`, desc: "Get list size" }, ...truthy, {
|
|
3652
3668
|
name: `setIn${uname}At`,
|
|
3653
3669
|
sig: `setIn${uname}At(i, v)`,
|
|
3654
3670
|
desc: "Set item at index"
|
|
@@ -3689,7 +3705,7 @@ function getFieldMethods(field) {
|
|
|
3689
3705
|
name: `${name}Len`,
|
|
3690
3706
|
sig: `${name}Len()`,
|
|
3691
3707
|
desc: `Get ${label} size`
|
|
3692
|
-
}, {
|
|
3708
|
+
}, ...truthy, {
|
|
3693
3709
|
name: `setIn${uname}At`,
|
|
3694
3710
|
sig: `setIn${uname}At(key, v)`,
|
|
3695
3711
|
desc: "Set value at key"
|
|
@@ -3717,7 +3733,7 @@ function getFieldMethods(field) {
|
|
|
3717
3733
|
name: `is${uname}Empty`,
|
|
3718
3734
|
sig: `is${uname}Empty()`,
|
|
3719
3735
|
desc: "Check if set is empty"
|
|
3720
|
-
}, { name: `${name}Len`, sig: `${name}Len()`, desc: "Get set size" }, {
|
|
3736
|
+
}, { name: `${name}Len`, sig: `${name}Len()`, desc: "Get set size" }, ...truthy, {
|
|
3721
3737
|
name: `addIn${uname}`,
|
|
3722
3738
|
sig: `addIn${uname}(v)`,
|
|
3723
3739
|
desc: "Add value to set"
|
|
@@ -3739,6 +3755,9 @@ function getFieldMethods(field) {
|
|
|
3739
3755
|
desc: "Toggle value in set"
|
|
3740
3756
|
});
|
|
3741
3757
|
break;
|
|
3758
|
+
default:
|
|
3759
|
+
methods.push(...nullable, ...truthy);
|
|
3760
|
+
break;
|
|
3742
3761
|
}
|
|
3743
3762
|
return methods;
|
|
3744
3763
|
}
|
|
@@ -5406,15 +5425,13 @@ class Components {
|
|
|
5406
5425
|
return v?.[this.getComponentSymbol]?.() ?? null;
|
|
5407
5426
|
}
|
|
5408
5427
|
getOnEnterFor(v) {
|
|
5409
|
-
|
|
5410
|
-
return comp ? comp.on.stackEnter : defaultOnStackEnter;
|
|
5428
|
+
return this.getCompFor(v)?.on.stackEnter ?? defaultOnStackEnter;
|
|
5411
5429
|
}
|
|
5412
5430
|
getHandlerFor(v, name, key) {
|
|
5413
5431
|
return this.getCompFor(v)?.[key][name] ?? null;
|
|
5414
5432
|
}
|
|
5415
5433
|
getRequestFor(v, name) {
|
|
5416
|
-
|
|
5417
|
-
return comp ? comp.scope.lookupRequest(name) : null;
|
|
5434
|
+
return this.getCompFor(v)?.scope.lookupRequest(name) ?? null;
|
|
5418
5435
|
}
|
|
5419
5436
|
compileStyles() {
|
|
5420
5437
|
const styles2 = [];
|
|
@@ -13630,9 +13647,6 @@ class Renderer {
|
|
|
13630
13647
|
this.comps = comps;
|
|
13631
13648
|
this.cache = new WeakMapDomCache;
|
|
13632
13649
|
}
|
|
13633
|
-
getSeqInfo(seq) {
|
|
13634
|
-
return isIndexed(seq) ? imIndexedIter : isKeyed(seq) ? imKeyedIter : seq?.[SEQ_INFO] ?? unkIter;
|
|
13635
|
-
}
|
|
13636
13650
|
renderTag(tag, attrs, childs) {
|
|
13637
13651
|
return h(tag, attrs, childs);
|
|
13638
13652
|
}
|
|
@@ -13690,7 +13704,7 @@ class Renderer {
|
|
|
13690
13704
|
const { seq, filter, loopWith } = iterInfo.eval(stack);
|
|
13691
13705
|
const r = [];
|
|
13692
13706
|
const iterData = loopWith.call(stack.it, seq);
|
|
13693
|
-
|
|
13707
|
+
getSeqInfo(seq)(seq, (key, value, attrName) => {
|
|
13694
13708
|
if (filter.call(stack.it, key, value, iterData)) {
|
|
13695
13709
|
const dom = this.renderIt(stack.enter(value, { key }, true), nodeId, key, viewName);
|
|
13696
13710
|
this.pushEachEntry(r, nodeId, attrName, key, dom);
|
|
@@ -13703,7 +13717,7 @@ class Renderer {
|
|
|
13703
13717
|
const r = [];
|
|
13704
13718
|
const it = stack.it;
|
|
13705
13719
|
const iterData = loopWith.call(it, seq);
|
|
13706
|
-
|
|
13720
|
+
getSeqInfo(seq)(seq, (key, value, attrName) => {
|
|
13707
13721
|
if (filter.call(it, key, value, iterData)) {
|
|
13708
13722
|
const cachePath = enricher ? [it, value] : [value];
|
|
13709
13723
|
const binds = { key, value };
|
|
@@ -13740,7 +13754,7 @@ class Renderer {
|
|
|
13740
13754
|
return new VComment(`§${JSON.stringify(info)}§`);
|
|
13741
13755
|
}
|
|
13742
13756
|
}
|
|
13743
|
-
var DATASET_ATTRS, imIndexedIter = (seq, visit) => {
|
|
13757
|
+
var DATASET_ATTRS, getSeqInfo = (seq) => isIndexed(seq) ? imIndexedIter : isKeyed(seq) ? imKeyedIter : seq?.[SEQ_INFO] ?? unkIter, imIndexedIter = (seq, visit) => {
|
|
13744
13758
|
let i = 0;
|
|
13745
13759
|
for (const v of seq)
|
|
13746
13760
|
visit(i++, v, "si");
|
package/dist/tutuca-dev.js
CHANGED
|
@@ -8510,15 +8510,13 @@ class Components {
|
|
|
8510
8510
|
return v?.[this.getComponentSymbol]?.() ?? null;
|
|
8511
8511
|
}
|
|
8512
8512
|
getOnEnterFor(v) {
|
|
8513
|
-
|
|
8514
|
-
return comp ? comp.on.stackEnter : defaultOnStackEnter;
|
|
8513
|
+
return this.getCompFor(v)?.on.stackEnter ?? defaultOnStackEnter;
|
|
8515
8514
|
}
|
|
8516
8515
|
getHandlerFor(v, name, key) {
|
|
8517
8516
|
return this.getCompFor(v)?.[key][name] ?? null;
|
|
8518
8517
|
}
|
|
8519
8518
|
getRequestFor(v, name) {
|
|
8520
|
-
|
|
8521
|
-
return comp ? comp.scope.lookupRequest(name) : null;
|
|
8519
|
+
return this.getCompFor(v)?.scope.lookupRequest(name) ?? null;
|
|
8522
8520
|
}
|
|
8523
8521
|
compileStyles() {
|
|
8524
8522
|
const styles2 = [];
|
|
@@ -8603,8 +8601,7 @@ class DynamicAlias extends Dynamic {
|
|
|
8603
8601
|
this.dynName = dynName;
|
|
8604
8602
|
}
|
|
8605
8603
|
_resolveSymbol(stack) {
|
|
8606
|
-
|
|
8607
|
-
return t?.dynamic[this.dynName]?.symbol ?? null;
|
|
8604
|
+
return stack.lookupType(this.compName)?.dynamic[this.dynName]?.symbol ?? null;
|
|
8608
8605
|
}
|
|
8609
8606
|
getSymbol(stack) {
|
|
8610
8607
|
this.symbol ??= this._resolveSymbol(stack);
|
|
@@ -8652,9 +8649,8 @@ class Component {
|
|
|
8652
8649
|
} else if (isString(dinfo?.default) && isString(dinfo?.for)) {
|
|
8653
8650
|
const val = vp.parseDynamic(dinfo.default, this.views.main.ctx);
|
|
8654
8651
|
const [compName, dynName] = dinfo.for.split(".");
|
|
8655
|
-
if (isString(compName) && isString(dynName))
|
|
8652
|
+
if (isString(compName) && isString(dynName))
|
|
8656
8653
|
this.dynamic[key] = new DynamicAlias(key, val, compName, dynName);
|
|
8657
|
-
}
|
|
8658
8654
|
}
|
|
8659
8655
|
}
|
|
8660
8656
|
for (const name in this.views)
|
|
@@ -14601,9 +14597,6 @@ class Renderer {
|
|
|
14601
14597
|
this.comps = comps;
|
|
14602
14598
|
this.cache = new WeakMapDomCache;
|
|
14603
14599
|
}
|
|
14604
|
-
getSeqInfo(seq) {
|
|
14605
|
-
return isIndexed(seq) ? imIndexedIter : isKeyed(seq) ? imKeyedIter : seq?.[SEQ_INFO] ?? unkIter;
|
|
14606
|
-
}
|
|
14607
14600
|
renderTag(tag, attrs, childs) {
|
|
14608
14601
|
return h(tag, attrs, childs);
|
|
14609
14602
|
}
|
|
@@ -14661,7 +14654,7 @@ class Renderer {
|
|
|
14661
14654
|
const { seq, filter, loopWith } = iterInfo.eval(stack);
|
|
14662
14655
|
const r = [];
|
|
14663
14656
|
const iterData = loopWith.call(stack.it, seq);
|
|
14664
|
-
|
|
14657
|
+
getSeqInfo(seq)(seq, (key, value, attrName) => {
|
|
14665
14658
|
if (filter.call(stack.it, key, value, iterData)) {
|
|
14666
14659
|
const dom = this.renderIt(stack.enter(value, { key }, true), nodeId, key, viewName);
|
|
14667
14660
|
this.pushEachEntry(r, nodeId, attrName, key, dom);
|
|
@@ -14674,7 +14667,7 @@ class Renderer {
|
|
|
14674
14667
|
const r = [];
|
|
14675
14668
|
const it = stack.it;
|
|
14676
14669
|
const iterData = loopWith.call(it, seq);
|
|
14677
|
-
|
|
14670
|
+
getSeqInfo(seq)(seq, (key, value, attrName) => {
|
|
14678
14671
|
if (filter.call(it, key, value, iterData)) {
|
|
14679
14672
|
const cachePath = enricher ? [it, value] : [value];
|
|
14680
14673
|
const binds = { key, value };
|
|
@@ -14711,6 +14704,7 @@ class Renderer {
|
|
|
14711
14704
|
return new VComment(`§${JSON.stringify(info)}§`);
|
|
14712
14705
|
}
|
|
14713
14706
|
}
|
|
14707
|
+
var getSeqInfo = (seq) => isIndexed(seq) ? imIndexedIter : isKeyed(seq) ? imKeyedIter : seq?.[SEQ_INFO] ?? unkIter;
|
|
14714
14708
|
var imIndexedIter = (seq, visit) => {
|
|
14715
14709
|
let i = 0;
|
|
14716
14710
|
for (const v of seq)
|
|
@@ -14871,16 +14865,25 @@ function getFieldMethods(field) {
|
|
|
14871
14865
|
name: `reset${uname}`,
|
|
14872
14866
|
sig: `reset${uname}()`,
|
|
14873
14867
|
desc: "Reset to default value"
|
|
14874
|
-
}
|
|
14868
|
+
}
|
|
14869
|
+
];
|
|
14870
|
+
const truthy = [
|
|
14875
14871
|
{
|
|
14876
|
-
name: `is${uname}
|
|
14877
|
-
sig: `is${uname}
|
|
14878
|
-
desc: "Check if
|
|
14872
|
+
name: `is${uname}Truthy`,
|
|
14873
|
+
sig: `is${uname}Truthy()`,
|
|
14874
|
+
desc: "Check if value is truthy"
|
|
14879
14875
|
},
|
|
14880
14876
|
{
|
|
14881
|
-
name: `is${uname}
|
|
14882
|
-
sig: `is${uname}
|
|
14883
|
-
desc: "Check if
|
|
14877
|
+
name: `is${uname}Falsy`,
|
|
14878
|
+
sig: `is${uname}Falsy()`,
|
|
14879
|
+
desc: "Check if value is falsy"
|
|
14880
|
+
}
|
|
14881
|
+
];
|
|
14882
|
+
const nullable = [
|
|
14883
|
+
{
|
|
14884
|
+
name: `is${uname}Null`,
|
|
14885
|
+
sig: `is${uname}Null()`,
|
|
14886
|
+
desc: "Check if value is null/undefined"
|
|
14884
14887
|
}
|
|
14885
14888
|
];
|
|
14886
14889
|
switch (type3) {
|
|
@@ -14890,21 +14893,28 @@ function getFieldMethods(field) {
|
|
|
14890
14893
|
name: `toggle${uname}`,
|
|
14891
14894
|
sig: `toggle${uname}()`,
|
|
14892
14895
|
desc: "Toggle boolean value"
|
|
14893
|
-
});
|
|
14896
|
+
}, ...truthy);
|
|
14897
|
+
break;
|
|
14898
|
+
case "int":
|
|
14899
|
+
case "float":
|
|
14900
|
+
methods.push(...truthy);
|
|
14901
|
+
break;
|
|
14902
|
+
case "any":
|
|
14903
|
+
methods.push(...nullable, ...truthy);
|
|
14894
14904
|
break;
|
|
14895
14905
|
case "text":
|
|
14896
14906
|
methods.push({
|
|
14897
14907
|
name: `is${uname}Empty`,
|
|
14898
14908
|
sig: `is${uname}Empty()`,
|
|
14899
14909
|
desc: "Check if string is empty"
|
|
14900
|
-
}, { name: `${name}Len`, sig: `${name}Len()`, desc: "Get string length" });
|
|
14910
|
+
}, { name: `${name}Len`, sig: `${name}Len()`, desc: "Get string length" }, ...truthy);
|
|
14901
14911
|
break;
|
|
14902
14912
|
case "list":
|
|
14903
14913
|
methods.push({
|
|
14904
14914
|
name: `is${uname}Empty`,
|
|
14905
14915
|
sig: `is${uname}Empty()`,
|
|
14906
14916
|
desc: "Check if list is empty"
|
|
14907
|
-
}, { name: `${name}Len`, sig: `${name}Len()`, desc: "Get list size" }, {
|
|
14917
|
+
}, { name: `${name}Len`, sig: `${name}Len()`, desc: "Get list size" }, ...truthy, {
|
|
14908
14918
|
name: `setIn${uname}At`,
|
|
14909
14919
|
sig: `setIn${uname}At(i, v)`,
|
|
14910
14920
|
desc: "Set item at index"
|
|
@@ -14945,7 +14955,7 @@ function getFieldMethods(field) {
|
|
|
14945
14955
|
name: `${name}Len`,
|
|
14946
14956
|
sig: `${name}Len()`,
|
|
14947
14957
|
desc: `Get ${label} size`
|
|
14948
|
-
}, {
|
|
14958
|
+
}, ...truthy, {
|
|
14949
14959
|
name: `setIn${uname}At`,
|
|
14950
14960
|
sig: `setIn${uname}At(key, v)`,
|
|
14951
14961
|
desc: "Set value at key"
|
|
@@ -14973,7 +14983,7 @@ function getFieldMethods(field) {
|
|
|
14973
14983
|
name: `is${uname}Empty`,
|
|
14974
14984
|
sig: `is${uname}Empty()`,
|
|
14975
14985
|
desc: "Check if set is empty"
|
|
14976
|
-
}, { name: `${name}Len`, sig: `${name}Len()`, desc: "Get set size" }, {
|
|
14986
|
+
}, { name: `${name}Len`, sig: `${name}Len()`, desc: "Get set size" }, ...truthy, {
|
|
14977
14987
|
name: `addIn${uname}`,
|
|
14978
14988
|
sig: `addIn${uname}(v)`,
|
|
14979
14989
|
desc: "Add value to set"
|
|
@@ -14995,6 +15005,9 @@ function getFieldMethods(field) {
|
|
|
14995
15005
|
desc: "Toggle value in set"
|
|
14996
15006
|
});
|
|
14997
15007
|
break;
|
|
15008
|
+
default:
|
|
15009
|
+
methods.push(...nullable, ...truthy);
|
|
15010
|
+
break;
|
|
14998
15011
|
}
|
|
14999
15012
|
return methods;
|
|
15000
15013
|
}
|
|
@@ -15036,6 +15049,51 @@ function docComponents(normalized, { name = null } = {}) {
|
|
|
15036
15049
|
const picked = name === null ? comps : comps.filter((c) => c.name === name);
|
|
15037
15050
|
return new ComponentDocs({ items: getComponentsDocs(picked) });
|
|
15038
15051
|
}
|
|
15052
|
+
// src/util/testing.js
|
|
15053
|
+
var filterAlwaysTrue2 = () => true;
|
|
15054
|
+
var nullLoopWith2 = (seq) => ({ seq });
|
|
15055
|
+
var plainArrayIter = (seq, visit) => {
|
|
15056
|
+
for (let i = 0;i < seq.length; i++)
|
|
15057
|
+
visit(i, seq[i]);
|
|
15058
|
+
};
|
|
15059
|
+
var plainMapIter = (seq, visit) => {
|
|
15060
|
+
for (const [k, v] of seq.entries())
|
|
15061
|
+
visit(k, v);
|
|
15062
|
+
};
|
|
15063
|
+
function pickIter(seq) {
|
|
15064
|
+
if (Array.isArray(seq))
|
|
15065
|
+
return plainArrayIter;
|
|
15066
|
+
if (seq instanceof Map)
|
|
15067
|
+
return plainMapIter;
|
|
15068
|
+
return getSeqInfo(seq);
|
|
15069
|
+
}
|
|
15070
|
+
function resolveAlter(Comp, name) {
|
|
15071
|
+
if (name == null)
|
|
15072
|
+
return null;
|
|
15073
|
+
const fn = Comp.alter?.[name];
|
|
15074
|
+
if (typeof fn !== "function") {
|
|
15075
|
+
throw new Error(`alter handler '${name}' not found on component '${Comp.name}'`);
|
|
15076
|
+
}
|
|
15077
|
+
return fn;
|
|
15078
|
+
}
|
|
15079
|
+
function collectIterBindings(Comp, compInstance, seq, opts = {}) {
|
|
15080
|
+
const whenFn = resolveAlter(Comp, opts.when) ?? filterAlwaysTrue2;
|
|
15081
|
+
const loopWithFn = resolveAlter(Comp, opts.loopWith) ?? nullLoopWith2;
|
|
15082
|
+
const enrichFn = resolveAlter(Comp, opts.enrichWith);
|
|
15083
|
+
const it = compInstance;
|
|
15084
|
+
const iterData = loopWithFn.call(it, seq);
|
|
15085
|
+
const out = [];
|
|
15086
|
+
const iter = pickIter(seq);
|
|
15087
|
+
iter(seq, (key, value) => {
|
|
15088
|
+
if (!whenFn.call(it, key, value, iterData))
|
|
15089
|
+
return;
|
|
15090
|
+
const binds = { key, value };
|
|
15091
|
+
if (enrichFn)
|
|
15092
|
+
enrichFn.call(it, binds, key, value, iterData);
|
|
15093
|
+
out.push(binds);
|
|
15094
|
+
});
|
|
15095
|
+
return out;
|
|
15096
|
+
}
|
|
15039
15097
|
|
|
15040
15098
|
// dev.js
|
|
15041
15099
|
async function test3(opts = {}) {
|
|
@@ -15156,6 +15214,7 @@ export {
|
|
|
15156
15214
|
component,
|
|
15157
15215
|
compileClassesToStyleText,
|
|
15158
15216
|
compileClassesToStyle,
|
|
15217
|
+
collectIterBindings,
|
|
15159
15218
|
checkComponent,
|
|
15160
15219
|
check,
|
|
15161
15220
|
UNSUPPORTED_EXPR_SYNTAX,
|