@repobit/dex-store-elements 1.7.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -905
- package/README.md +9 -7
- package/dist/src/actions/action.adapter.d.ts +36 -0
- package/dist/src/actions/action.adapter.js +183 -0
- package/dist/src/actions/action.adapter.js.map +1 -0
- package/dist/src/actions/action.attributes.d.ts +15 -0
- package/dist/src/actions/action.attributes.js +137 -0
- package/dist/src/actions/action.attributes.js.map +1 -0
- package/dist/src/actions/action.registry.d.ts +4 -0
- package/dist/src/actions/action.registry.js +119 -0
- package/dist/src/actions/action.registry.js.map +1 -0
- package/dist/src/actions/index.d.ts +3 -2
- package/dist/src/actions/index.js +3 -80
- package/dist/src/actions/index.js.map +1 -1
- package/dist/src/compute/compute.expand.d.ts +70 -0
- package/dist/src/compute/compute.expand.js +319 -0
- package/dist/src/compute/compute.expand.js.map +1 -0
- package/dist/src/compute/compute.scan.d.ts +54 -0
- package/dist/src/compute/compute.scan.js +160 -0
- package/dist/src/compute/compute.scan.js.map +1 -0
- package/dist/src/compute/option-resolver.d.ts +31 -0
- package/dist/src/compute/option-resolver.js +147 -0
- package/dist/src/compute/option-resolver.js.map +1 -0
- package/dist/src/contexts/context.compute.d.ts +12 -0
- package/dist/src/contexts/context.compute.js +4 -0
- package/dist/src/contexts/context.compute.js.map +1 -0
- package/dist/src/contexts/context.scope.d.ts +139 -0
- package/dist/src/contexts/context.scope.js +48 -0
- package/dist/src/contexts/context.scope.js.map +1 -0
- package/dist/src/controllers/controller.action-resolve.d.ts +110 -0
- package/dist/src/controllers/controller.action-resolve.js +203 -0
- package/dist/src/controllers/controller.action-resolve.js.map +1 -0
- package/dist/src/controllers/controller.action-topology.d.ts +59 -0
- package/dist/src/controllers/controller.action-topology.js +130 -0
- package/dist/src/controllers/controller.action-topology.js.map +1 -0
- package/dist/src/controllers/controller.compute.d.ts +72 -0
- package/dist/src/controllers/controller.compute.js +110 -0
- package/dist/src/controllers/controller.compute.js.map +1 -0
- package/dist/src/controllers/controller.resolve.d.ts +78 -0
- package/dist/src/controllers/controller.resolve.js +205 -0
- package/dist/src/controllers/controller.resolve.js.map +1 -0
- package/dist/src/core/action-topology.d.ts +62 -0
- package/dist/src/core/action-topology.js +67 -0
- package/dist/src/core/action-topology.js.map +1 -0
- package/dist/src/core/actions.d.ts +64 -0
- package/dist/src/core/actions.js +47 -0
- package/dist/src/core/actions.js.map +1 -0
- package/dist/src/core/node.element.d.ts +179 -0
- package/dist/src/core/node.element.js +573 -0
- package/dist/src/core/node.element.js.map +1 -0
- package/dist/src/core/node.scope.d.ts +374 -0
- package/dist/src/core/node.scope.js +558 -0
- package/dist/src/core/node.scope.js.map +1 -0
- package/dist/src/core/option-update.d.ts +11 -0
- package/dist/src/core/option-update.js +59 -0
- package/dist/src/core/option-update.js.map +1 -0
- package/dist/src/dsl/compilers/array/compiler.js +13 -14
- package/dist/src/dsl/compilers/array/compiler.js.map +1 -1
- package/dist/src/dsl/compilers/boolean/compiler.d.ts +1 -1
- package/dist/src/dsl/compilers/boolean/compiler.js +14 -27
- package/dist/src/dsl/compilers/boolean/compiler.js.map +1 -1
- package/dist/src/dsl/compilers/enum/compiler.js +7 -10
- package/dist/src/dsl/compilers/enum/compiler.js.map +1 -1
- package/dist/src/dsl/compilers/index.d.ts +1 -1
- package/dist/src/dsl/compilers/index.js +5 -5
- package/dist/src/dsl/compilers/index.js.map +1 -1
- package/dist/src/dsl/utilty.d.ts +0 -4
- package/dist/src/dsl/utilty.js +3 -30
- package/dist/src/dsl/utilty.js.map +1 -1
- package/dist/src/eta/batch.d.ts +123 -0
- package/dist/src/eta/batch.js +162 -0
- package/dist/src/eta/batch.js.map +1 -0
- package/dist/src/eta/batcher.d.ts +184 -0
- package/dist/src/eta/batcher.js +335 -0
- package/dist/src/eta/batcher.js.map +1 -0
- package/dist/src/eta/cache.d.ts +36 -0
- package/dist/src/eta/cache.js +56 -0
- package/dist/src/eta/cache.js.map +1 -0
- package/dist/src/eta/controller.d.ts +608 -0
- package/dist/src/eta/controller.js +1036 -0
- package/dist/src/eta/controller.js.map +1 -0
- package/dist/src/eta/factory.d.ts +74 -0
- package/dist/src/eta/factory.js +121 -0
- package/dist/src/eta/factory.js.map +1 -0
- package/dist/src/eta/idle.d.ts +39 -0
- package/dist/src/eta/idle.js +52 -0
- package/dist/src/eta/idle.js.map +1 -0
- package/dist/src/eta/index.d.ts +52 -0
- package/dist/src/eta/index.js +13 -0
- package/dist/src/eta/index.js.map +1 -0
- package/dist/src/eta/observer.d.ts +263 -0
- package/dist/src/eta/observer.js +263 -0
- package/dist/src/eta/observer.js.map +1 -0
- package/dist/src/eta/operations.d.ts +41 -0
- package/dist/src/eta/operations.js +59 -0
- package/dist/src/eta/operations.js.map +1 -0
- package/dist/src/eta/source-registry.d.ts +198 -0
- package/dist/src/eta/source-registry.js +267 -0
- package/dist/src/eta/source-registry.js.map +1 -0
- package/dist/src/eta/template-index.d.ts +285 -0
- package/dist/src/eta/template-index.js +642 -0
- package/dist/src/eta/template-index.js.map +1 -0
- package/dist/src/eta/types.d.ts +529 -0
- package/dist/src/eta/types.js +2 -0
- package/dist/src/eta/types.js.map +1 -0
- package/dist/src/index.d.ts +21 -9
- package/dist/src/index.js +22 -11
- package/dist/src/index.js.map +1 -1
- package/dist/src/nodes/node.context.d.ts +5 -3
- package/dist/src/nodes/node.context.js +48 -3
- package/dist/src/nodes/node.context.js.map +1 -1
- package/dist/src/nodes/node.option.d.ts +11 -31
- package/dist/src/nodes/node.option.js +158 -222
- package/dist/src/nodes/node.option.js.map +1 -1
- package/dist/src/nodes/node.product.d.ts +7 -11
- package/dist/src/nodes/node.product.js +85 -64
- package/dist/src/nodes/node.product.js.map +1 -1
- package/dist/src/renders/attributes/buyLink.d.ts +2 -2
- package/dist/src/renders/attributes/buyLink.js +14 -11
- package/dist/src/renders/attributes/buyLink.js.map +1 -1
- package/dist/src/renders/attributes/devices.d.ts +2 -2
- package/dist/src/renders/attributes/devices.js +9 -19
- package/dist/src/renders/attributes/devices.js.map +1 -1
- package/dist/src/renders/attributes/discount.d.ts +2 -2
- package/dist/src/renders/attributes/discount.js +56 -44
- package/dist/src/renders/attributes/discount.js.map +1 -1
- package/dist/src/renders/attributes/hide.d.ts +2 -2
- package/dist/src/renders/attributes/hide.js +49 -14
- package/dist/src/renders/attributes/hide.js.map +1 -1
- package/dist/src/renders/attributes/index.d.ts +3 -3
- package/dist/src/renders/attributes/index.js +2 -4
- package/dist/src/renders/attributes/index.js.map +1 -1
- package/dist/src/renders/attributes/price.d.ts +2 -2
- package/dist/src/renders/attributes/price.js +30 -44
- package/dist/src/renders/attributes/price.js.map +1 -1
- package/dist/src/renders/attributes/subscription.d.ts +2 -2
- package/dist/src/renders/attributes/subscription.js +14 -20
- package/dist/src/renders/attributes/subscription.js.map +1 -1
- package/dist/src/renders/attributes/utilty.d.ts +2 -0
- package/dist/src/renders/attributes/utilty.js +17 -4
- package/dist/src/renders/attributes/utilty.js.map +1 -1
- package/dist/src/renders/context.d.ts +122 -10
- package/dist/src/renders/context.js +140 -80
- package/dist/src/renders/context.js.map +1 -1
- package/dist/src/renders/format.js +2 -2
- package/dist/src/renders/format.js.map +1 -1
- package/dist/src/renders/index.d.ts +6 -2
- package/dist/src/renders/index.js +67 -29
- package/dist/src/renders/index.js.map +1 -1
- package/dist/src/renders/observe.d.ts +1 -4
- package/dist/src/renders/observe.js +10 -24
- package/dist/src/renders/observe.js.map +1 -1
- package/dist/src/renders/render.wrapper.d.ts +37 -0
- package/dist/src/renders/render.wrapper.js +147 -0
- package/dist/src/renders/render.wrapper.js.map +1 -0
- package/dist/src/templating/eta.js +1 -4
- package/dist/src/templating/eta.js.map +1 -1
- package/package.json +4 -5
- package/dist/src/actions/action.button.d.ts +0 -2
- package/dist/src/actions/action.button.js +0 -8
- package/dist/src/actions/action.button.js.map +0 -1
- package/dist/src/actions/action.input.d.ts +0 -2
- package/dist/src/actions/action.input.js +0 -43
- package/dist/src/actions/action.input.js.map +0 -1
- package/dist/src/actions/action.select.d.ts +0 -2
- package/dist/src/actions/action.select.js +0 -14
- package/dist/src/actions/action.select.js.map +0 -1
- package/dist/src/actions/utilty.d.ts +0 -16
- package/dist/src/actions/utilty.js +0 -149
- package/dist/src/actions/utilty.js.map +0 -1
- package/dist/src/contexts/context.datalayer.d.ts +0 -9
- package/dist/src/contexts/context.datalayer.js +0 -3
- package/dist/src/contexts/context.datalayer.js.map +0 -1
- package/dist/src/contexts/context.derived.d.ts +0 -12
- package/dist/src/contexts/context.derived.js +0 -3
- package/dist/src/contexts/context.derived.js.map +0 -1
- package/dist/src/contexts/context.option.d.ts +0 -5
- package/dist/src/contexts/context.option.js +0 -3
- package/dist/src/contexts/context.option.js.map +0 -1
- package/dist/src/contexts/context.product.d.ts +0 -5
- package/dist/src/contexts/context.product.js +0 -3
- package/dist/src/contexts/context.product.js.map +0 -1
- package/dist/src/contexts/context.state.d.ts +0 -28
- package/dist/src/contexts/context.state.js +0 -3
- package/dist/src/contexts/context.state.js.map +0 -1
- package/dist/src/contexts/context.store.d.ts +0 -5
- package/dist/src/contexts/context.store.js +0 -3
- package/dist/src/contexts/context.store.js.map +0 -1
- package/dist/src/controllers/collect-controller.d.ts +0 -39
- package/dist/src/controllers/collect-controller.js +0 -161
- package/dist/src/controllers/collect-controller.js.map +0 -1
- package/dist/src/controllers/compute-controller.d.ts +0 -55
- package/dist/src/controllers/compute-controller.js +0 -350
- package/dist/src/controllers/compute-controller.js.map +0 -1
- package/dist/src/controllers/eta-renderer/eta-dom-renderer.d.ts +0 -25
- package/dist/src/controllers/eta-renderer/eta-dom-renderer.js +0 -192
- package/dist/src/controllers/eta-renderer/eta-dom-renderer.js.map +0 -1
- package/dist/src/controllers/eta-renderer/eta-mutation-observer.d.ts +0 -28
- package/dist/src/controllers/eta-renderer/eta-mutation-observer.js +0 -206
- package/dist/src/controllers/eta-renderer/eta-mutation-observer.js.map +0 -1
- package/dist/src/controllers/eta-renderer/eta-renderer.d.ts +0 -21
- package/dist/src/controllers/eta-renderer/eta-renderer.js +0 -77
- package/dist/src/controllers/eta-renderer/eta-renderer.js.map +0 -1
- package/dist/src/controllers/eta-renderer/eta-template-cache.d.ts +0 -24
- package/dist/src/controllers/eta-renderer/eta-template-cache.js +0 -61
- package/dist/src/controllers/eta-renderer/eta-template-cache.js.map +0 -1
- package/dist/src/controllers/eta-renderer/eta-types.d.ts +0 -11
- package/dist/src/controllers/eta-renderer/eta-types.js +0 -1
- package/dist/src/controllers/eta-renderer/eta-types.js.map +0 -1
- package/dist/src/controllers/eta-renderer/index.d.ts +0 -2
- package/dist/src/controllers/eta-renderer/index.js +0 -2
- package/dist/src/controllers/eta-renderer/index.js.map +0 -1
- package/dist/src/controllers/event-pipeline-controller.d.ts +0 -28
- package/dist/src/controllers/event-pipeline-controller.js +0 -72
- package/dist/src/controllers/event-pipeline-controller.js.map +0 -1
- package/dist/src/events/events.d.ts +0 -75
- package/dist/src/events/events.js +0 -78
- package/dist/src/events/events.js.map +0 -1
- package/dist/src/nodes/node.root.d.ts +0 -16
- package/dist/src/nodes/node.root.js +0 -49
- package/dist/src/nodes/node.root.js.map +0 -1
- package/dist/src/nodes/node.state.d.ts +0 -88
- package/dist/src/nodes/node.state.js +0 -330
- package/dist/src/nodes/node.state.js.map +0 -1
- package/dist/src/renders/utility.d.ts +0 -32
- package/dist/src/renders/utility.js +0 -191
- package/dist/src/renders/utility.js.map +0 -1
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// src/core/option-update.ts
|
|
2
|
+
/**
|
|
3
|
+
* Pure devices/subscription update semantics shared by the runtime OptionNode
|
|
4
|
+
* and the dry-run compute layer.
|
|
5
|
+
*
|
|
6
|
+
* Given a current numeric value and an UpdateByDelta command, this returns the
|
|
7
|
+
* next value. `next` / `prev` walk the product's declared dimension values, so
|
|
8
|
+
* sequential stepping respects what the product actually offers.
|
|
9
|
+
*/
|
|
10
|
+
export function applyOptionUpdate(field, currentValue, update, product) {
|
|
11
|
+
if (!update || update.type !== field) {
|
|
12
|
+
return currentValue;
|
|
13
|
+
}
|
|
14
|
+
if (update.useAsValue) {
|
|
15
|
+
if (typeof update.delta !== 'number') {
|
|
16
|
+
return currentValue;
|
|
17
|
+
}
|
|
18
|
+
return clampNumericUpdate(update.delta, update);
|
|
19
|
+
}
|
|
20
|
+
if (currentValue === undefined) {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
if (update.delta === 'next' ||
|
|
24
|
+
update.delta === 'prev') {
|
|
25
|
+
return applySequentialUpdate(field, currentValue, update, product);
|
|
26
|
+
}
|
|
27
|
+
return clampNumericUpdate(currentValue + update.delta, update);
|
|
28
|
+
}
|
|
29
|
+
function applySequentialUpdate(field, currentValue, update, product) {
|
|
30
|
+
const values = field === 'devices'
|
|
31
|
+
? product?.getDevices().values
|
|
32
|
+
: product?.getSubscriptions().values;
|
|
33
|
+
if (!values || values.length === 0) {
|
|
34
|
+
return currentValue;
|
|
35
|
+
}
|
|
36
|
+
const currentIndex = values.findIndex((value) => value === currentValue);
|
|
37
|
+
if (currentIndex < 0) {
|
|
38
|
+
return currentValue;
|
|
39
|
+
}
|
|
40
|
+
const nextIndex = update.delta === 'next'
|
|
41
|
+
? currentIndex + 1
|
|
42
|
+
: currentIndex - 1;
|
|
43
|
+
const nextValue = values[nextIndex];
|
|
44
|
+
if (nextValue === undefined) {
|
|
45
|
+
return currentValue;
|
|
46
|
+
}
|
|
47
|
+
return clampNumericUpdate(nextValue, update);
|
|
48
|
+
}
|
|
49
|
+
function clampNumericUpdate(value, update) {
|
|
50
|
+
let nextValue = value;
|
|
51
|
+
if (update.min !== undefined) {
|
|
52
|
+
nextValue = Math.max(update.min, nextValue);
|
|
53
|
+
}
|
|
54
|
+
if (update.max !== undefined) {
|
|
55
|
+
nextValue = Math.min(update.max, nextValue);
|
|
56
|
+
}
|
|
57
|
+
return nextValue;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=option-update.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"option-update.js","sourceRoot":"","sources":["../../../src/core/option-update.ts"],"names":[],"mappings":"AAAA,4BAA4B;AAK5B;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAC/B,KAA4B,EAC5B,YAAgC,EAChC,MAAiC,EACjC,OAA4B;IAE5B,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;QACrC,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QACtB,IAAI,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YACrC,OAAO,YAAY,CAAC;QACtB,CAAC;QAED,OAAO,kBAAkB,CACvB,MAAM,CAAC,KAAK,EACZ,MAAM,CACP,CAAC;IACJ,CAAC;IAED,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;QAC/B,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IACE,MAAM,CAAC,KAAK,KAAK,MAAM;QACvB,MAAM,CAAC,KAAK,KAAK,MAAM,EACvB,CAAC;QACD,OAAO,qBAAqB,CAC1B,KAAK,EACL,YAAY,EACZ,MAAM,EACN,OAAO,CACR,CAAC;IACJ,CAAC;IAED,OAAO,kBAAkB,CACvB,YAAY,GAAG,MAAM,CAAC,KAAK,EAC3B,MAAM,CACP,CAAC;AACJ,CAAC;AAED,SAAS,qBAAqB,CAC5B,KAA4B,EAC5B,YAAoB,EACpB,MAAqB,EACrB,OAA4B;IAE5B,MAAM,MAAM,GACV,KAAK,KAAK,SAAS;QACjB,CAAC,CAAC,OAAO,EAAE,UAAU,EAAE,CAAC,MAAM;QAC9B,CAAC,CAAC,OAAO,EAAE,gBAAgB,EAAE,CAAC,MAAM,CAAC;IAEzC,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnC,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,MAAM,YAAY,GAChB,MAAM,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,YAAY,CAAC,CAAC;IAEtD,IAAI,YAAY,GAAG,CAAC,EAAE,CAAC;QACrB,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,MAAM,SAAS,GACb,MAAM,CAAC,KAAK,KAAK,MAAM;QACrB,CAAC,CAAC,YAAY,GAAG,CAAC;QAClB,CAAC,CAAC,YAAY,GAAG,CAAC,CAAC;IAEvB,MAAM,SAAS,GACb,MAAM,CAAC,SAAS,CAAC,CAAC;IAEpB,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5B,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,OAAO,kBAAkB,CACvB,SAAS,EACT,MAAM,CACP,CAAC;AACJ,CAAC;AAED,SAAS,kBAAkB,CACzB,KAAa,EACb,MAAqB;IAErB,IAAI,SAAS,GAAG,KAAK,CAAC;IAEtB,IAAI,MAAM,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;QAC7B,SAAS,GAAG,IAAI,CAAC,GAAG,CAClB,MAAM,CAAC,GAAG,EACV,SAAS,CACV,CAAC;IACJ,CAAC;IAED,IAAI,MAAM,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;QAC7B,SAAS,GAAG,IAAI,CAAC,GAAG,CAClB,MAAM,CAAC,GAAG,EACV,SAAS,CACV,CAAC;IACJ,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC","sourcesContent":["// src/core/option-update.ts\n\nimport type { UpdateByDelta } from '@/core/actions.js';\nimport type { Product } from '@repobit/dex-store';\n\n/**\n * Pure devices/subscription update semantics shared by the runtime OptionNode\n * and the dry-run compute layer.\n *\n * Given a current numeric value and an UpdateByDelta command, this returns the\n * next value. `next` / `prev` walk the product's declared dimension values, so\n * sequential stepping respects what the product actually offers.\n */\nexport function applyOptionUpdate(\n field: UpdateByDelta['type'],\n currentValue: number | undefined,\n update: UpdateByDelta | undefined,\n product: Product | undefined\n): number | undefined {\n if (!update || update.type !== field) {\n return currentValue;\n }\n\n if (update.useAsValue) {\n if (typeof update.delta !== 'number') {\n return currentValue;\n }\n\n return clampNumericUpdate(\n update.delta,\n update\n );\n }\n\n if (currentValue === undefined) {\n return undefined;\n }\n\n if (\n update.delta === 'next' ||\n update.delta === 'prev'\n ) {\n return applySequentialUpdate(\n field,\n currentValue,\n update,\n product\n );\n }\n\n return clampNumericUpdate(\n currentValue + update.delta,\n update\n );\n}\n\nfunction applySequentialUpdate(\n field: UpdateByDelta['type'],\n currentValue: number,\n update: UpdateByDelta,\n product: Product | undefined\n): number {\n const values =\n field === 'devices'\n ? product?.getDevices().values\n : product?.getSubscriptions().values;\n\n if (!values || values.length === 0) {\n return currentValue;\n }\n\n const currentIndex =\n values.findIndex((value) => value === currentValue);\n\n if (currentIndex < 0) {\n return currentValue;\n }\n\n const nextIndex =\n update.delta === 'next'\n ? currentIndex + 1\n : currentIndex - 1;\n\n const nextValue =\n values[nextIndex];\n\n if (nextValue === undefined) {\n return currentValue;\n }\n\n return clampNumericUpdate(\n nextValue,\n update\n );\n}\n\nfunction clampNumericUpdate(\n value: number,\n update: UpdateByDelta\n): number {\n let nextValue = value;\n\n if (update.min !== undefined) {\n nextValue = Math.max(\n update.min,\n nextValue\n );\n }\n\n if (update.max !== undefined) {\n nextValue = Math.min(\n update.max,\n nextValue\n );\n }\n\n return nextValue;\n}\n"]}
|
|
@@ -1,22 +1,21 @@
|
|
|
1
|
-
import { split } from
|
|
2
|
-
// "2,3,4,5" -> [2,3,4,5]
|
|
3
|
-
// "1..4,8,10..7" -> [1,2,3,4,8,10,9,8,7]
|
|
4
|
-
// "" -> undefined
|
|
1
|
+
import { split } from '../../../dsl/utilty.js';
|
|
5
2
|
export const compiler = (raw) => {
|
|
6
|
-
const
|
|
7
|
-
if (
|
|
3
|
+
const source = raw.trim();
|
|
4
|
+
if (source === '') {
|
|
8
5
|
return undefined;
|
|
6
|
+
}
|
|
9
7
|
const out = [];
|
|
10
8
|
const intRe = /^-?\d+$/;
|
|
11
9
|
const rangeRe = /^(-?\d+)\s*\.\.\s*(-?\d+)$/;
|
|
12
|
-
for (const part of split(
|
|
13
|
-
const
|
|
14
|
-
if (
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
const step =
|
|
18
|
-
for (let
|
|
19
|
-
out.push(
|
|
10
|
+
for (const part of split(source, ',')) {
|
|
11
|
+
const match = part.match(rangeRe);
|
|
12
|
+
if (match) {
|
|
13
|
+
const start = parseInt(match[1], 10);
|
|
14
|
+
const end = parseInt(match[2], 10);
|
|
15
|
+
const step = start <= end ? 1 : -1;
|
|
16
|
+
for (let value = start; value !== end + step; value += step) {
|
|
17
|
+
out.push(value);
|
|
18
|
+
}
|
|
20
19
|
continue;
|
|
21
20
|
}
|
|
22
21
|
if (intRe.test(part)) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compiler.js","sourceRoot":"","sources":["../../../../../src/dsl/compilers/array/compiler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"compiler.js","sourceRoot":"","sources":["../../../../../src/dsl/compilers/array/compiler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAExC,MAAM,CAAC,MAAM,QAAQ,GAAG,CACtB,GAAW,EACW,EAAE;IACxB,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IAE1B,IAAI,MAAM,KAAK,EAAE,EAAE,CAAC;QAClB,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,MAAM,KAAK,GAAG,SAAS,CAAC;IACxB,MAAM,OAAO,GAAG,4BAA4B,CAAC;IAE7C,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC;QACtC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAElC,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACrC,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACnC,MAAM,IAAI,GAAG,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAEnC,KAAK,IAAI,KAAK,GAAG,KAAK,EAAE,KAAK,KAAK,GAAG,GAAG,IAAI,EAAE,KAAK,IAAI,IAAI,EAAE,CAAC;gBAC5D,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAClB,CAAC;YAED,SAAS;QACX,CAAC;QAED,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACrB,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;YAC7B,SAAS;QACX,CAAC;QAED,MAAM,IAAI,KAAK,CAAC,0BAA0B,IAAI,GAAG,CAAC,CAAC;IACrD,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC,CAAC","sourcesContent":["import { split } from '@/dsl/utilty.js';\n\nexport const compiler = (\n raw: string\n): number[] | undefined => {\n const source = raw.trim();\n\n if (source === '') {\n return undefined;\n }\n\n const out: number[] = [];\n const intRe = /^-?\\d+$/;\n const rangeRe = /^(-?\\d+)\\s*\\.\\.\\s*(-?\\d+)$/;\n\n for (const part of split(source, ',')) {\n const match = part.match(rangeRe);\n\n if (match) {\n const start = parseInt(match[1], 10);\n const end = parseInt(match[2], 10);\n const step = start <= end ? 1 : -1;\n\n for (let value = start; value !== end + step; value += step) {\n out.push(value);\n }\n\n continue;\n }\n\n if (intRe.test(part)) {\n out.push(parseInt(part, 10));\n continue;\n }\n\n throw new Error(`Invalid number token: \"${part}\"`);\n }\n\n return out;\n};\n"]}
|
|
@@ -1,53 +1,40 @@
|
|
|
1
|
-
import eta from
|
|
2
|
-
// Very lightweight preflight checks so we can rely on Eta/JS
|
|
3
|
-
// for actual expression evaluation while still catching a few
|
|
4
|
-
// DSL-specific pitfalls we used to guard against.
|
|
1
|
+
import eta from '../../../templating/eta.js';
|
|
5
2
|
function preflight(expr) {
|
|
6
|
-
// 1) Disallow chained comparisons like: a < b < c
|
|
7
|
-
// Match two comparison operators with no logical op (&&, ||) between
|
|
8
3
|
const chainedCmp = /(<=|>=|<|>)\s*[^&|]+(<=|>=|<|>)/;
|
|
9
4
|
if (chainedCmp.test(expr)) {
|
|
10
|
-
throw new Error(
|
|
5
|
+
throw new Error('Chained comparisons are not allowed');
|
|
11
6
|
}
|
|
12
|
-
// 2) Basic parentheses balance check to surface a clearer error
|
|
13
7
|
let depth = 0;
|
|
14
|
-
for (const
|
|
15
|
-
if (
|
|
8
|
+
for (const char of expr) {
|
|
9
|
+
if (char === '(') {
|
|
16
10
|
depth++;
|
|
17
|
-
|
|
11
|
+
}
|
|
12
|
+
else if (char === ')') {
|
|
18
13
|
depth--;
|
|
19
14
|
if (depth < 0) {
|
|
20
|
-
// extra closing paren
|
|
21
15
|
throw new Error("Unexpected ')'");
|
|
22
16
|
}
|
|
23
17
|
}
|
|
24
18
|
}
|
|
25
19
|
if (depth > 0) {
|
|
26
|
-
throw new Error("Expected ')'
|
|
20
|
+
throw new Error("Expected ')'");
|
|
27
21
|
}
|
|
28
22
|
}
|
|
29
23
|
export const compiler = ({ expr, ctx }) => {
|
|
30
24
|
preflight(expr);
|
|
31
|
-
// Normalize equality to strict semantics to match legacy DSL behavior
|
|
32
|
-
// - '==' -> '==='
|
|
33
|
-
// - '!=' -> '!=='
|
|
34
|
-
// Note: This is a simple text replacement and does not account for quoted strings,
|
|
35
|
-
// but our DSL usage does not place operators inside string literals.
|
|
36
25
|
const normalized = expr
|
|
37
26
|
.replace(/!=(?!=)/g, '!==')
|
|
38
27
|
.replace(/(?<![=!])==(?![=])/g, '===');
|
|
39
|
-
// Build a tiny Eta template that coerces the expression to boolean.
|
|
40
|
-
const tpl = `{{= !!(${normalized}) }}`;
|
|
41
28
|
try {
|
|
42
|
-
const fn = eta.compile(
|
|
29
|
+
const fn = eta.compile(`{{= !!(${normalized}) }}`);
|
|
43
30
|
const out = eta.render(fn, ctx);
|
|
44
|
-
|
|
45
|
-
return String(out).trim() === "true";
|
|
31
|
+
return String(out).trim() === 'true';
|
|
46
32
|
}
|
|
47
|
-
catch (
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
33
|
+
catch (error) {
|
|
34
|
+
const message = error instanceof Error
|
|
35
|
+
? error.message
|
|
36
|
+
: String(error);
|
|
37
|
+
throw new Error(`Unexpected expression error: ${message}`);
|
|
51
38
|
}
|
|
52
39
|
};
|
|
53
40
|
//# sourceMappingURL=compiler.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compiler.js","sourceRoot":"","sources":["../../../../../src/dsl/compilers/boolean/compiler.ts"],"names":[],"mappings":"AACA,OAAO,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"compiler.js","sourceRoot":"","sources":["../../../../../src/dsl/compilers/boolean/compiler.ts"],"names":[],"mappings":"AACA,OAAO,GAAG,MAAM,qBAAqB,CAAC;AAOtC,SAAS,SAAS,CAChB,IAAY;IAEZ,MAAM,UAAU,GAAG,iCAAiC,CAAC;IAErD,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;IACzD,CAAC;IAED,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,KAAK,MAAM,IAAI,IAAI,IAAI,EAAE,CAAC;QACxB,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACjB,KAAK,EAAE,CAAC;QACV,CAAC;aAAM,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACxB,KAAK,EAAE,CAAC;YAER,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;gBACd,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC;YACpC,CAAC;QACH,CAAC;IACH,CAAC;IAED,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,cAAc,CAAC,CAAC;IAClC,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,EACvB,IAAI,EACJ,GAAG,EACI,EAAW,EAAE;IACpB,SAAS,CAAC,IAAI,CAAC,CAAC;IAEhB,MAAM,UAAU,GAAG,IAAI;SACpB,OAAO,CAAC,UAAU,EAAE,KAAK,CAAC;SAC1B,OAAO,CAAC,qBAAqB,EAAE,KAAK,CAAC,CAAC;IAEzC,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,GAAG,CAAC,OAAO,CAAC,UAAU,UAAU,MAAM,CAAC,CAAC;QACnD,MAAM,GAAG,GAAG,GAAG,CAAC,MAAM,CACpB,EAAE,EACF,GAAG,CACJ,CAAC;QAEF,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,MAAM,CAAC;IACvC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,GACX,KAAK,YAAY,KAAK;YACpB,CAAC,CAAC,KAAK,CAAC,OAAO;YACf,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAEpB,MAAM,IAAI,KAAK,CAAC,gCAAgC,OAAO,EAAE,CAAC,CAAC;IAC7D,CAAC;AACH,CAAC,CAAC","sourcesContent":["import type { Ctx } from '@/dsl/utilty.js';\nimport eta from '@/templating/eta.js';\n\ntype Params = {\n expr: string;\n ctx : Ctx;\n};\n\nfunction preflight(\n expr: string\n): void {\n const chainedCmp = /(<=|>=|<|>)\\s*[^&|]+(<=|>=|<|>)/;\n\n if (chainedCmp.test(expr)) {\n throw new Error('Chained comparisons are not allowed');\n }\n\n let depth = 0;\n\n for (const char of expr) {\n if (char === '(') {\n depth++;\n } else if (char === ')') {\n depth--;\n\n if (depth < 0) {\n throw new Error(\"Unexpected ')'\");\n }\n }\n }\n\n if (depth > 0) {\n throw new Error(\"Expected ')'\");\n }\n}\n\nexport const compiler = ({\n expr,\n ctx\n}: Params): boolean => {\n preflight(expr);\n\n const normalized = expr\n .replace(/!=(?!=)/g, '!==')\n .replace(/(?<![=!])==(?![=])/g, '===');\n\n try {\n const fn = eta.compile(`{{= !!(${normalized}) }}`);\n const out = eta.render(\n fn,\n ctx\n );\n\n return String(out).trim() === 'true';\n } catch (error) {\n const message =\n error instanceof Error\n ? error.message\n : String(error);\n\n throw new Error(`Unexpected expression error: ${message}`);\n }\n};\n"]}
|
|
@@ -1,16 +1,13 @@
|
|
|
1
|
-
import { split } from
|
|
1
|
+
import { split } from '../../../dsl/utilty.js';
|
|
2
2
|
export const compiler = ({ expr, allowed, isAvailable }) => {
|
|
3
|
-
const tokens = split(expr,
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
if (
|
|
7
|
-
|
|
3
|
+
const tokens = split(expr, '||');
|
|
4
|
+
for (const rawToken of tokens) {
|
|
5
|
+
const token = rawToken;
|
|
6
|
+
if (allowed.includes(token) &&
|
|
7
|
+
isAvailable(token)) {
|
|
8
|
+
return token;
|
|
8
9
|
}
|
|
9
10
|
}
|
|
10
|
-
for (const t of tokens) {
|
|
11
|
-
if (allowed.includes(t) && isAvailable(t))
|
|
12
|
-
return t;
|
|
13
|
-
}
|
|
14
11
|
return undefined;
|
|
15
12
|
};
|
|
16
13
|
//# sourceMappingURL=compiler.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compiler.js","sourceRoot":"","sources":["../../../../../src/dsl/compilers/enum/compiler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"compiler.js","sourceRoot":"","sources":["../../../../../src/dsl/compilers/enum/compiler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAQxC,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAyB,EAC/C,IAAI,EACJ,OAAO,EACP,WAAW,EACK,EAAuB,EAAE;IACzC,MAAM,MAAM,GAAG,KAAK,CAClB,IAAI,EACJ,IAAI,CACL,CAAC;IAEF,KAAK,MAAM,QAAQ,IAAI,MAAM,EAAE,CAAC;QAC9B,MAAM,KAAK,GAAG,QAAmB,CAAC;QAElC,IACE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC;YACvB,WAAW,CAAC,KAAK,CAAC,EAClB,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC,CAAC","sourcesContent":["import { split } from '@/dsl/utilty.js';\n\ntype Params<Allowed extends string> = {\n expr : string;\n allowed : readonly Allowed[];\n isAvailable: (variant: Allowed) => boolean;\n};\n\nexport const compiler = <Allowed extends string>({\n expr,\n allowed,\n isAvailable\n}: Params<Allowed>): Allowed | undefined => {\n const tokens = split(\n expr,\n '||'\n );\n\n for (const rawToken of tokens) {\n const token = rawToken as Allowed;\n\n if (\n allowed.includes(token) &&\n isAvailable(token)\n ) {\n return token;\n }\n }\n\n return undefined;\n};\n"]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export declare const Compiler: {
|
|
2
|
+
readonly array: (raw: string) => number[] | undefined;
|
|
2
3
|
readonly boolean: ({ expr, ctx }: {
|
|
3
4
|
expr: string;
|
|
4
5
|
ctx: import("../utilty.js").Ctx;
|
|
@@ -8,5 +9,4 @@ export declare const Compiler: {
|
|
|
8
9
|
allowed: readonly Allowed[];
|
|
9
10
|
isAvailable: (variant: Allowed) => boolean;
|
|
10
11
|
}) => Allowed | undefined;
|
|
11
|
-
readonly array: (raw: string) => number[] | undefined;
|
|
12
12
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { compiler as array } from
|
|
2
|
-
import { compiler as boolean } from
|
|
3
|
-
import { compiler as enumeration } from
|
|
1
|
+
import { compiler as array } from './array/compiler.js';
|
|
2
|
+
import { compiler as boolean } from './boolean/compiler.js';
|
|
3
|
+
import { compiler as enumeration } from './enum/compiler.js';
|
|
4
4
|
export const Compiler = {
|
|
5
|
+
array,
|
|
5
6
|
boolean,
|
|
6
|
-
enum: enumeration
|
|
7
|
-
array
|
|
7
|
+
enum: enumeration
|
|
8
8
|
};
|
|
9
9
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/dsl/compilers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,KAAK,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/dsl/compilers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,KAAK,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,QAAQ,IAAI,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,QAAQ,IAAI,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAE7D,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,KAAK;IACL,OAAO;IACP,IAAI,EAAE,WAAW;CACT,CAAC","sourcesContent":["import { compiler as array } from './array/compiler.js';\nimport { compiler as boolean } from './boolean/compiler.js';\nimport { compiler as enumeration } from './enum/compiler.js';\n\nexport const Compiler = {\n array,\n boolean,\n enum: enumeration\n} as const;\n"]}
|
package/dist/src/dsl/utilty.d.ts
CHANGED
|
@@ -1,6 +1,2 @@
|
|
|
1
1
|
export type Ctx = Record<string, unknown>;
|
|
2
|
-
export declare const isRecord: (v: unknown) => v is Record<string, unknown>;
|
|
3
|
-
export declare const getPath: <T = unknown>(ctx: unknown, path: string) => T | undefined;
|
|
4
|
-
export declare const isTruthy: (v: unknown) => boolean;
|
|
5
|
-
export declare const coerceLiteral: (s: string) => string | number | boolean | null | undefined;
|
|
6
2
|
export declare const split: (raw: string, sep: string) => string[];
|
package/dist/src/dsl/utilty.js
CHANGED
|
@@ -1,32 +1,5 @@
|
|
|
1
|
-
export const
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
for (const key of path.split(".")) {
|
|
5
|
-
if (!isRecord(acc) || !(key in acc))
|
|
6
|
-
return undefined;
|
|
7
|
-
acc = acc[key];
|
|
8
|
-
}
|
|
9
|
-
return acc;
|
|
10
|
-
};
|
|
11
|
-
export const isTruthy = (v) => v !== "" && v !== false && v !== null && v !== undefined;
|
|
12
|
-
// Parse quoted literal or pass-through
|
|
13
|
-
export const coerceLiteral = (s) => {
|
|
14
|
-
if (/^-?\d+(\.\d+)?$/.test(s))
|
|
15
|
-
return Number(s);
|
|
16
|
-
if (s === "true")
|
|
17
|
-
return true;
|
|
18
|
-
if (s === "false")
|
|
19
|
-
return false;
|
|
20
|
-
if (s === "null")
|
|
21
|
-
return null;
|
|
22
|
-
if (s === "undefined")
|
|
23
|
-
return undefined;
|
|
24
|
-
const q = s[0], end = s[s.length - 1];
|
|
25
|
-
if ((q === "'" && end === "'") || (q === '"' && end === '"'))
|
|
26
|
-
return s.slice(1, -1);
|
|
27
|
-
return s;
|
|
28
|
-
};
|
|
29
|
-
// Split by token, trim, drop empties
|
|
30
|
-
export const split = (raw, sep) => raw.split(sep).map(s => s.trim())
|
|
1
|
+
export const split = (raw, sep) => raw
|
|
2
|
+
.split(sep)
|
|
3
|
+
.map((s) => s.trim())
|
|
31
4
|
.filter(Boolean);
|
|
32
5
|
//# sourceMappingURL=utilty.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utilty.js","sourceRoot":"","sources":["../../../src/dsl/utilty.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"utilty.js","sourceRoot":"","sources":["../../../src/dsl/utilty.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,KAAK,GAAG,CACnB,GAAW,EACX,GAAW,EACD,EAAE,CACZ,GAAG;KACA,KAAK,CAAC,GAAG,CAAC;KACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;KACpB,MAAM,CAAC,OAAO,CAAC,CAAC","sourcesContent":["export type Ctx = Record<string, unknown>;\n\nexport const split = (\n raw: string,\n sep: string\n): string[] =>\n raw\n .split(sep)\n .map((s) => s.trim())\n .filter(Boolean);\n"]}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Accumulator for raw `MutationRecord`s produced by the
|
|
3
|
+
* `EtaMutationObserver` between scheduled flushes.
|
|
4
|
+
*
|
|
5
|
+
* Records fall into four buckets:
|
|
6
|
+
*
|
|
7
|
+
* - `added` — nodes introduced into the DOM via `childList` records.
|
|
8
|
+
* - `removed` — nodes detached via `childList` records.
|
|
9
|
+
* - `text` — `Text` nodes whose data changed via `characterData`
|
|
10
|
+
* records.
|
|
11
|
+
* - `attributes` — `(Element, attributeName)` pairs that flipped via
|
|
12
|
+
* `attributes` records.
|
|
13
|
+
*
|
|
14
|
+
* The buckets are `Set` / `Map` collections rather than arrays so
|
|
15
|
+
* that repeated records for the same target naturally collapse
|
|
16
|
+
* before flush (e.g. five `attributes` flips on the same `title`
|
|
17
|
+
* only yield a single `(element, 'title')` entry by the time the
|
|
18
|
+
* batch is applied).
|
|
19
|
+
*
|
|
20
|
+
* **Compaction rules.** A flush through `EtaMutationBatcher` applies
|
|
21
|
+
* the following reductions to the bucket contents before forwarding
|
|
22
|
+
* the compacted set to the `EtaTemplateIndex`:
|
|
23
|
+
*
|
|
24
|
+
* 1. `add` then `remove` of the same node within one batch is a
|
|
25
|
+
* no-op (the node ends up detached, the index never saw it).
|
|
26
|
+
* 2. `remove` then `add` of the same node is forwarded as both a
|
|
27
|
+
* `remove` and an `add` (the move is real).
|
|
28
|
+
* 3. An ancestor `add` subsumes descendant `add`s — the index walks
|
|
29
|
+
* the subtree on `add`, so descendant adds are redundant.
|
|
30
|
+
* 4. An ancestor `remove` subsumes descendant `remove`s for the
|
|
31
|
+
* same reason.
|
|
32
|
+
* 5. A `text` change inside an added subtree is dropped; the index
|
|
33
|
+
* reads the final text when it walks the new subtree.
|
|
34
|
+
* 6. A `text` / `attribute` change inside a removed subtree is
|
|
35
|
+
* dropped; the node is leaving ownership.
|
|
36
|
+
* 7. Repeated text changes on the same `Text` node collapse to one
|
|
37
|
+
* entry (the `Set` storage already handles this).
|
|
38
|
+
* 8. Repeated attribute changes on the same `(Element, name)` pair
|
|
39
|
+
* collapse to one entry (the inner `Set<string>` storage already
|
|
40
|
+
* handles this).
|
|
41
|
+
*
|
|
42
|
+
* The batch itself does not enforce rules 1–6 — those live in the
|
|
43
|
+
* batcher's compaction pass. Rules 7–8 are enforced automatically
|
|
44
|
+
* by the bucket storage.
|
|
45
|
+
*
|
|
46
|
+
* **Immutability of contents.** Bucket contents are mutable in
|
|
47
|
+
* place for performance reasons (`Set.add` / `Map.set` are O(1)).
|
|
48
|
+
* Callers MUST NOT cache references to bucket internals across a
|
|
49
|
+
* flush — `clear()` resets the bucket to empty. The controller
|
|
50
|
+
* treats the batch as opaque between flushes.
|
|
51
|
+
*/
|
|
52
|
+
export declare class EtaMutationBatch {
|
|
53
|
+
/**
|
|
54
|
+
* Nodes added since the last flush.
|
|
55
|
+
*
|
|
56
|
+
* Stored as a `Set<Node>` so multiple adds of the same node
|
|
57
|
+
* collapse automatically (rule 7-equivalent for adds).
|
|
58
|
+
*/
|
|
59
|
+
readonly added: Set<Node>;
|
|
60
|
+
/**
|
|
61
|
+
* Nodes removed since the last flush.
|
|
62
|
+
*
|
|
63
|
+
* Stored as a `Set<Node>` for the same dedup reasons as `added`.
|
|
64
|
+
*/
|
|
65
|
+
readonly removed: Set<Node>;
|
|
66
|
+
/**
|
|
67
|
+
* `Text` nodes whose character data changed since the last flush.
|
|
68
|
+
*
|
|
69
|
+
* `Set<Text>` collapses repeated changes on the same text node
|
|
70
|
+
* (rule 7).
|
|
71
|
+
*/
|
|
72
|
+
readonly text: Set<Text>;
|
|
73
|
+
/**
|
|
74
|
+
* `Element` → set of changed attribute names since the last flush.
|
|
75
|
+
*
|
|
76
|
+
* The inner `Set<string>` collapses repeated changes to the same
|
|
77
|
+
* attribute name (rule 8).
|
|
78
|
+
*/
|
|
79
|
+
readonly attributes: Map<Element, Set<string>>;
|
|
80
|
+
/**
|
|
81
|
+
* Reset every bucket to empty.
|
|
82
|
+
*
|
|
83
|
+
* Called by `EtaMutationBatcher.flush()` after the compacted
|
|
84
|
+
* mutations have been applied to the index. The bucket instances
|
|
85
|
+
* are preserved (`Set` / `Map` references stay valid) so callers
|
|
86
|
+
* can safely hold long-lived references to them.
|
|
87
|
+
*/
|
|
88
|
+
clear(): void;
|
|
89
|
+
/**
|
|
90
|
+
* `true` when every bucket is empty — i.e. there is nothing to
|
|
91
|
+
* forward to the index on the next flush.
|
|
92
|
+
*
|
|
93
|
+
* Used by `EtaMutationBatcher.flush()` as a fast-path for "no
|
|
94
|
+
* work to do" so the batcher can return `false` without touching
|
|
95
|
+
* the index.
|
|
96
|
+
*/
|
|
97
|
+
get empty(): boolean;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Fold a single `MutationRecord` into the appropriate bucket of
|
|
101
|
+
* `batch`.
|
|
102
|
+
*
|
|
103
|
+
* The mapping mirrors `EtaMutationObserver.handleRecord` but
|
|
104
|
+
* accumulates records rather than forwarding them straight to the
|
|
105
|
+
* index. The batcher's compaction pass later walks the accumulated
|
|
106
|
+
* buckets and decides what to actually apply.
|
|
107
|
+
*
|
|
108
|
+
* Mapping:
|
|
109
|
+
*
|
|
110
|
+
* - `childList` → `addedNodes` / `removedNodes` are added to the
|
|
111
|
+
* matching bucket.
|
|
112
|
+
* - `characterData` → the `Text` target is added to `text` (when
|
|
113
|
+
* the target really is a `Text` — the DOM guard is defensive in
|
|
114
|
+
* case some exotic browser delivers a different node type).
|
|
115
|
+
* - `attributes` → `(element, attributeName)` is added to
|
|
116
|
+
* `attributes` (when the target is an `Element` and the
|
|
117
|
+
* attribute name is present — both are required for the entry
|
|
118
|
+
* to be useful to the index).
|
|
119
|
+
*
|
|
120
|
+
* Unknown record types are silently dropped. The DOM only emits
|
|
121
|
+
* the three types above, so this branch is defensive only.
|
|
122
|
+
*/
|
|
123
|
+
export declare function enqueueRecord(batch: EtaMutationBatch, record: MutationRecord): void;
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
// src/eta/batch.ts
|
|
2
|
+
/**
|
|
3
|
+
* Accumulator for raw `MutationRecord`s produced by the
|
|
4
|
+
* `EtaMutationObserver` between scheduled flushes.
|
|
5
|
+
*
|
|
6
|
+
* Records fall into four buckets:
|
|
7
|
+
*
|
|
8
|
+
* - `added` — nodes introduced into the DOM via `childList` records.
|
|
9
|
+
* - `removed` — nodes detached via `childList` records.
|
|
10
|
+
* - `text` — `Text` nodes whose data changed via `characterData`
|
|
11
|
+
* records.
|
|
12
|
+
* - `attributes` — `(Element, attributeName)` pairs that flipped via
|
|
13
|
+
* `attributes` records.
|
|
14
|
+
*
|
|
15
|
+
* The buckets are `Set` / `Map` collections rather than arrays so
|
|
16
|
+
* that repeated records for the same target naturally collapse
|
|
17
|
+
* before flush (e.g. five `attributes` flips on the same `title`
|
|
18
|
+
* only yield a single `(element, 'title')` entry by the time the
|
|
19
|
+
* batch is applied).
|
|
20
|
+
*
|
|
21
|
+
* **Compaction rules.** A flush through `EtaMutationBatcher` applies
|
|
22
|
+
* the following reductions to the bucket contents before forwarding
|
|
23
|
+
* the compacted set to the `EtaTemplateIndex`:
|
|
24
|
+
*
|
|
25
|
+
* 1. `add` then `remove` of the same node within one batch is a
|
|
26
|
+
* no-op (the node ends up detached, the index never saw it).
|
|
27
|
+
* 2. `remove` then `add` of the same node is forwarded as both a
|
|
28
|
+
* `remove` and an `add` (the move is real).
|
|
29
|
+
* 3. An ancestor `add` subsumes descendant `add`s — the index walks
|
|
30
|
+
* the subtree on `add`, so descendant adds are redundant.
|
|
31
|
+
* 4. An ancestor `remove` subsumes descendant `remove`s for the
|
|
32
|
+
* same reason.
|
|
33
|
+
* 5. A `text` change inside an added subtree is dropped; the index
|
|
34
|
+
* reads the final text when it walks the new subtree.
|
|
35
|
+
* 6. A `text` / `attribute` change inside a removed subtree is
|
|
36
|
+
* dropped; the node is leaving ownership.
|
|
37
|
+
* 7. Repeated text changes on the same `Text` node collapse to one
|
|
38
|
+
* entry (the `Set` storage already handles this).
|
|
39
|
+
* 8. Repeated attribute changes on the same `(Element, name)` pair
|
|
40
|
+
* collapse to one entry (the inner `Set<string>` storage already
|
|
41
|
+
* handles this).
|
|
42
|
+
*
|
|
43
|
+
* The batch itself does not enforce rules 1–6 — those live in the
|
|
44
|
+
* batcher's compaction pass. Rules 7–8 are enforced automatically
|
|
45
|
+
* by the bucket storage.
|
|
46
|
+
*
|
|
47
|
+
* **Immutability of contents.** Bucket contents are mutable in
|
|
48
|
+
* place for performance reasons (`Set.add` / `Map.set` are O(1)).
|
|
49
|
+
* Callers MUST NOT cache references to bucket internals across a
|
|
50
|
+
* flush — `clear()` resets the bucket to empty. The controller
|
|
51
|
+
* treats the batch as opaque between flushes.
|
|
52
|
+
*/
|
|
53
|
+
export class EtaMutationBatch {
|
|
54
|
+
constructor() {
|
|
55
|
+
/**
|
|
56
|
+
* Nodes added since the last flush.
|
|
57
|
+
*
|
|
58
|
+
* Stored as a `Set<Node>` so multiple adds of the same node
|
|
59
|
+
* collapse automatically (rule 7-equivalent for adds).
|
|
60
|
+
*/
|
|
61
|
+
this.added = new Set();
|
|
62
|
+
/**
|
|
63
|
+
* Nodes removed since the last flush.
|
|
64
|
+
*
|
|
65
|
+
* Stored as a `Set<Node>` for the same dedup reasons as `added`.
|
|
66
|
+
*/
|
|
67
|
+
this.removed = new Set();
|
|
68
|
+
/**
|
|
69
|
+
* `Text` nodes whose character data changed since the last flush.
|
|
70
|
+
*
|
|
71
|
+
* `Set<Text>` collapses repeated changes on the same text node
|
|
72
|
+
* (rule 7).
|
|
73
|
+
*/
|
|
74
|
+
this.text = new Set();
|
|
75
|
+
/**
|
|
76
|
+
* `Element` → set of changed attribute names since the last flush.
|
|
77
|
+
*
|
|
78
|
+
* The inner `Set<string>` collapses repeated changes to the same
|
|
79
|
+
* attribute name (rule 8).
|
|
80
|
+
*/
|
|
81
|
+
this.attributes = new Map();
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Reset every bucket to empty.
|
|
85
|
+
*
|
|
86
|
+
* Called by `EtaMutationBatcher.flush()` after the compacted
|
|
87
|
+
* mutations have been applied to the index. The bucket instances
|
|
88
|
+
* are preserved (`Set` / `Map` references stay valid) so callers
|
|
89
|
+
* can safely hold long-lived references to them.
|
|
90
|
+
*/
|
|
91
|
+
clear() {
|
|
92
|
+
this.added.clear();
|
|
93
|
+
this.removed.clear();
|
|
94
|
+
this.text.clear();
|
|
95
|
+
this.attributes.clear();
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* `true` when every bucket is empty — i.e. there is nothing to
|
|
99
|
+
* forward to the index on the next flush.
|
|
100
|
+
*
|
|
101
|
+
* Used by `EtaMutationBatcher.flush()` as a fast-path for "no
|
|
102
|
+
* work to do" so the batcher can return `false` without touching
|
|
103
|
+
* the index.
|
|
104
|
+
*/
|
|
105
|
+
get empty() {
|
|
106
|
+
return (this.added.size === 0 &&
|
|
107
|
+
this.removed.size === 0 &&
|
|
108
|
+
this.text.size === 0 &&
|
|
109
|
+
this.attributes.size === 0);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Fold a single `MutationRecord` into the appropriate bucket of
|
|
114
|
+
* `batch`.
|
|
115
|
+
*
|
|
116
|
+
* The mapping mirrors `EtaMutationObserver.handleRecord` but
|
|
117
|
+
* accumulates records rather than forwarding them straight to the
|
|
118
|
+
* index. The batcher's compaction pass later walks the accumulated
|
|
119
|
+
* buckets and decides what to actually apply.
|
|
120
|
+
*
|
|
121
|
+
* Mapping:
|
|
122
|
+
*
|
|
123
|
+
* - `childList` → `addedNodes` / `removedNodes` are added to the
|
|
124
|
+
* matching bucket.
|
|
125
|
+
* - `characterData` → the `Text` target is added to `text` (when
|
|
126
|
+
* the target really is a `Text` — the DOM guard is defensive in
|
|
127
|
+
* case some exotic browser delivers a different node type).
|
|
128
|
+
* - `attributes` → `(element, attributeName)` is added to
|
|
129
|
+
* `attributes` (when the target is an `Element` and the
|
|
130
|
+
* attribute name is present — both are required for the entry
|
|
131
|
+
* to be useful to the index).
|
|
132
|
+
*
|
|
133
|
+
* Unknown record types are silently dropped. The DOM only emits
|
|
134
|
+
* the three types above, so this branch is defensive only.
|
|
135
|
+
*/
|
|
136
|
+
export function enqueueRecord(batch, record) {
|
|
137
|
+
if (record.type === 'childList') {
|
|
138
|
+
for (const node of record.addedNodes) {
|
|
139
|
+
batch.added.add(node);
|
|
140
|
+
}
|
|
141
|
+
for (const node of record.removedNodes) {
|
|
142
|
+
batch.removed.add(node);
|
|
143
|
+
}
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
if (record.type === 'characterData' &&
|
|
147
|
+
record.target instanceof Text) {
|
|
148
|
+
batch.text.add(record.target);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (record.type === 'attributes' &&
|
|
152
|
+
record.target instanceof Element &&
|
|
153
|
+
record.attributeName) {
|
|
154
|
+
let attrs = batch.attributes.get(record.target);
|
|
155
|
+
if (!attrs) {
|
|
156
|
+
attrs = new Set();
|
|
157
|
+
batch.attributes.set(record.target, attrs);
|
|
158
|
+
}
|
|
159
|
+
attrs.add(record.attributeName);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=batch.js.map
|