@vidact/runtime 0.2.0-beta.0 → 0.2.0-beta.5
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 +52 -0
- package/README.md +1 -1
- package/dist/actions.d.ts +32 -30
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +251 -266
- package/dist/actions.js.map +1 -1
- package/dist/async.d.ts +20 -17
- package/dist/async.d.ts.map +1 -1
- package/dist/async.js +18 -17
- package/dist/async.js.map +1 -1
- package/dist/compiled/async-resource.d.ts +9 -6
- package/dist/compiled/async-resource.d.ts.map +1 -1
- package/dist/compiled/async-resource.js +69 -77
- package/dist/compiled/async-resource.js.map +1 -1
- package/dist/compiled/core.d.ts +136 -147
- package/dist/compiled/core.d.ts.map +1 -1
- package/dist/compiled/core.js +2672 -2985
- package/dist/compiled/core.js.map +1 -1
- package/dist/compiled/types.d.ts +44 -56
- package/dist/compiled/types.d.ts.map +1 -1
- package/dist/compiled/types.js +10 -6
- package/dist/compiled/types.js.map +1 -1
- package/dist/compiled/validation.d.ts +4 -1
- package/dist/compiled/validation.d.ts.map +1 -1
- package/dist/compiled/validation.js +17 -20
- package/dist/compiled/validation.js.map +1 -1
- package/dist/component-spread.d.ts +5 -3
- package/dist/component-spread.d.ts.map +1 -1
- package/dist/component-spread.js +49 -67
- package/dist/component-spread.js.map +1 -1
- package/dist/concurrent.d.ts +20 -18
- package/dist/concurrent.d.ts.map +1 -1
- package/dist/concurrent.js +191 -210
- package/dist/concurrent.js.map +1 -1
- package/dist/direct-dom.d.ts +17 -12
- package/dist/direct-dom.d.ts.map +1 -1
- package/dist/direct-dom.js +187 -221
- package/dist/direct-dom.js.map +1 -1
- package/dist/dom/attributes.d.ts +9 -6
- package/dist/dom/attributes.d.ts.map +1 -1
- package/dist/dom/attributes.js +65 -47
- package/dist/dom/attributes.js.map +1 -1
- package/dist/dom/events.d.ts +5 -2
- package/dist/dom/events.d.ts.map +1 -1
- package/dist/dom/events.js +53 -65
- package/dist/dom/events.js.map +1 -1
- package/dist/dom/forms.d.ts +9 -6
- package/dist/dom/forms.d.ts.map +1 -1
- package/dist/dom/forms.js +152 -198
- package/dist/dom/forms.js.map +1 -1
- package/dist/dom/intrinsic.d.ts +17 -15
- package/dist/dom/intrinsic.d.ts.map +1 -1
- package/dist/dom/intrinsic.js +48 -50
- package/dist/dom/intrinsic.js.map +1 -1
- package/dist/dom/namespace.d.ts +4 -1
- package/dist/dom/namespace.d.ts.map +1 -1
- package/dist/dom/namespace.js +25 -28
- package/dist/dom/namespace.js.map +1 -1
- package/dist/dom/namespaces.d.ts +11 -8
- package/dist/dom/namespaces.d.ts.map +1 -1
- package/dist/dom/namespaces.js +14 -12
- package/dist/dom/namespaces.js.map +1 -1
- package/dist/dom/properties.d.ts +18 -16
- package/dist/dom/properties.d.ts.map +1 -1
- package/dist/dom/properties.js +273 -296
- package/dist/dom/properties.js.map +1 -1
- package/dist/dom/style-properties.d.ts +4 -1
- package/dist/dom/style-properties.d.ts.map +1 -1
- package/dist/dom/style-properties.js +49 -45
- package/dist/dom/style-properties.js.map +1 -1
- package/dist/dom/styles.d.ts +5 -2
- package/dist/dom/styles.d.ts.map +1 -1
- package/dist/dom/styles.js +47 -67
- package/dist/dom/styles.js.map +1 -1
- package/dist/dom-forms.d.ts +4 -1
- package/dist/dom-forms.d.ts.map +1 -1
- package/dist/dom-forms.js +16 -13
- package/dist/dom-forms.js.map +1 -1
- package/dist/dom-styles.d.ts +4 -1
- package/dist/dom-styles.d.ts.map +1 -1
- package/dist/dom-styles.js +10 -7
- package/dist/dom-styles.js.map +1 -1
- package/dist/framework-hydrate.d.ts +20 -17
- package/dist/framework-hydrate.d.ts.map +1 -1
- package/dist/framework-hydrate.js +128 -146
- package/dist/framework-hydrate.js.map +1 -1
- package/dist/framework-protocol.d.ts +27 -25
- package/dist/framework-protocol.d.ts.map +1 -1
- package/dist/framework-protocol.js +173 -211
- package/dist/framework-protocol.js.map +1 -1
- package/dist/framework.d.ts +34 -32
- package/dist/framework.d.ts.map +1 -1
- package/dist/framework.js +74 -93
- package/dist/framework.js.map +1 -1
- package/dist/hydrate.d.ts +11 -8
- package/dist/hydrate.d.ts.map +1 -1
- package/dist/hydrate.js +13 -9
- package/dist/hydrate.js.map +1 -1
- package/dist/hydration-bridge.d.ts +70 -58
- package/dist/hydration-bridge.d.ts.map +1 -1
- package/dist/hydration-bridge.js +77 -66
- package/dist/hydration-bridge.js.map +1 -1
- package/dist/hydration.d.ts +54 -28
- package/dist/hydration.d.ts.map +1 -1
- package/dist/hydration.js +450 -463
- package/dist/hydration.js.map +1 -1
- package/dist/index.d.ts +13 -13
- package/dist/index.js +11 -13
- package/dist/indexed-list.d.ts +8 -5
- package/dist/indexed-list.d.ts.map +1 -1
- package/dist/indexed-list.js +14 -7
- package/dist/indexed-list.js.map +1 -1
- package/dist/jsx-runtime.d.ts +10 -8
- package/dist/jsx-runtime.d.ts.map +1 -1
- package/dist/jsx-runtime.js +28 -23
- package/dist/jsx-runtime.js.map +1 -1
- package/dist/keyed-list.d.ts +13 -14
- package/dist/keyed-list.d.ts.map +1 -1
- package/dist/keyed-list.js +132 -160
- package/dist/keyed-list.js.map +1 -1
- package/dist/metadata.d.ts +6 -3
- package/dist/metadata.d.ts.map +1 -1
- package/dist/metadata.js +131 -164
- package/dist/metadata.js.map +1 -1
- package/dist/nested-prop.d.ts +4 -2
- package/dist/nested-prop.d.ts.map +1 -1
- package/dist/nested-prop.js +17 -16
- package/dist/nested-prop.js.map +1 -1
- package/dist/profiling.d.ts +11 -8
- package/dist/profiling.d.ts.map +1 -1
- package/dist/profiling.js +17 -17
- package/dist/profiling.js.map +1 -1
- package/dist/protocol.d.ts +5 -2
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +8 -5
- package/dist/protocol.js.map +1 -1
- package/dist/raw-html.d.ts +5 -2
- package/dist/raw-html.d.ts.map +1 -1
- package/dist/raw-html.js +86 -146
- package/dist/raw-html.js.map +1 -1
- package/dist/ref.d.ts +8 -3
- package/dist/ref.d.ts.map +1 -1
- package/dist/ref.js +10 -2
- package/dist/ref.js.map +1 -1
- package/dist/renderable-protocol.d.ts +26 -8
- package/dist/renderable-protocol.d.ts.map +1 -1
- package/dist/renderable-protocol.js +30 -9
- package/dist/renderable-protocol.js.map +1 -1
- package/dist/renderable.d.ts +27 -22
- package/dist/renderable.d.ts.map +1 -1
- package/dist/renderable.js +141 -133
- package/dist/renderable.js.map +1 -1
- package/dist/retained-ui.d.ts +9 -6
- package/dist/retained-ui.d.ts.map +1 -1
- package/dist/retained-ui.js +12 -10
- package/dist/retained-ui.js.map +1 -1
- package/dist/root.d.ts +18 -15
- package/dist/root.d.ts.map +1 -1
- package/dist/root.js +73 -85
- package/dist/root.js.map +1 -1
- package/dist/scheduler.d.ts +10 -8
- package/dist/scheduler.d.ts.map +1 -1
- package/dist/scheduler.js +97 -105
- package/dist/scheduler.js.map +1 -1
- package/dist/server-framework.d.ts +45 -42
- package/dist/server-framework.d.ts.map +1 -1
- package/dist/server-framework.js +209 -217
- package/dist/server-framework.js.map +1 -1
- package/dist/server.d.ts +130 -128
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +603 -710
- package/dist/server.js.map +1 -1
- package/dist/shared/promise.d.ts +6 -3
- package/dist/shared/promise.d.ts.map +1 -1
- package/dist/shared/promise.js +21 -19
- package/dist/shared/promise.js.map +1 -1
- package/dist/source-mask.d.ts +9 -6
- package/dist/source-mask.d.ts.map +1 -1
- package/dist/source-mask.js +45 -63
- package/dist/source-mask.js.map +1 -1
- package/dist/spread.d.ts +5 -2
- package/dist/spread.d.ts.map +1 -1
- package/dist/spread.js +79 -99
- package/dist/spread.js.map +1 -1
- package/dist/state-slot.d.ts +18 -15
- package/dist/state-slot.d.ts.map +1 -1
- package/dist/state-slot.js +43 -42
- package/dist/state-slot.js.map +1 -1
- package/dist/testing.d.ts +9 -6
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +16 -12
- package/dist/testing.js.map +1 -1
- package/package.json +8 -7
- package/src/actions.ts +415 -0
- package/src/async.ts +78 -0
- package/src/compiled/async-resource.ts +113 -0
- package/src/compiled/core.ts +4041 -0
- package/src/compiled/types.ts +96 -0
- package/src/compiled/validation.ts +23 -0
- package/src/component-spread.ts +86 -0
- package/src/concurrent.ts +310 -0
- package/src/direct-dom.ts +365 -0
- package/src/dom/attributes.ts +55 -0
- package/src/dom/events.ts +93 -0
- package/src/dom/forms.ts +220 -0
- package/src/dom/intrinsic.ts +94 -0
- package/src/dom/namespace.ts +30 -0
- package/src/dom/namespaces.ts +14 -0
- package/src/dom/properties.ts +355 -0
- package/src/dom/style-properties.ts +46 -0
- package/src/dom/styles.ts +86 -0
- package/src/dom-forms.ts +23 -0
- package/src/dom-styles.ts +11 -0
- package/src/env.d.ts +6 -0
- package/src/framework-hydrate.ts +224 -0
- package/src/framework-protocol.ts +335 -0
- package/src/framework.ts +161 -0
- package/src/hydrate.ts +38 -0
- package/src/hydration-bridge.ts +196 -0
- package/src/hydration.ts +642 -0
- package/src/index.ts +92 -0
- package/src/indexed-list.ts +20 -0
- package/src/jsx-runtime.ts +55 -0
- package/src/keyed-list.ts +233 -0
- package/src/metadata.ts +212 -0
- package/src/nested-prop.ts +27 -0
- package/src/profiling.ts +43 -0
- package/src/protocol.ts +8 -0
- package/src/raw-html.ts +206 -0
- package/src/ref.ts +13 -0
- package/src/renderable-protocol.ts +63 -0
- package/src/renderable.ts +283 -0
- package/src/retained-ui.ts +19 -0
- package/src/root.ts +122 -0
- package/src/scheduler.ts +136 -0
- package/src/server-framework.ts +397 -0
- package/src/server.ts +1160 -0
- package/src/shared/promise.ts +28 -0
- package/src/source-mask.ts +83 -0
- package/src/spread.ts +131 -0
- package/src/state-slot.ts +76 -0
- package/src/testing.ts +27 -0
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
package/dist/state-slot.d.ts
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import { SourceMask } from "./source-mask.js";
|
|
2
|
+
//#region src/state-slot.d.ts
|
|
3
|
+
type StateUpdate<T> = T | ((previous: T) => T);
|
|
4
|
+
interface DeferredStateWrite<T> {
|
|
5
|
+
readonly slot: object;
|
|
6
|
+
readonly revision: number;
|
|
7
|
+
readonly update: StateUpdate<T>;
|
|
8
|
+
readonly commit: (update: StateUpdate<T>) => void;
|
|
9
|
+
readonly currentRevision: () => number;
|
|
9
10
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
type StateWriteInterceptor = <T>(write: DeferredStateWrite<T>) => boolean;
|
|
12
|
+
declare function installStateWriteInterceptor(interceptor: StateWriteInterceptor): void;
|
|
13
|
+
interface StateSlot<T> {
|
|
14
|
+
readonly get: () => T;
|
|
15
|
+
readonly set: (update: StateUpdate<T>) => void;
|
|
16
|
+
readonly replace: (value: T) => void;
|
|
16
17
|
}
|
|
17
|
-
|
|
18
|
+
declare function createStateSlot<T>(invalidate: (sources: SourceMask) => void, source: SourceMask, initialValue: T, assertWritable?: () => void): StateSlot<T>;
|
|
19
|
+
//#endregion
|
|
20
|
+
export { DeferredStateWrite, StateSlot, StateUpdate, StateWriteInterceptor, createStateSlot, installStateWriteInterceptor };
|
|
18
21
|
//# sourceMappingURL=state-slot.d.ts.map
|
package/dist/state-slot.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state-slot.d.ts","
|
|
1
|
+
{"version":3,"file":"state-slot.d.ts","names":[],"sources":["../src/state-slot.ts"],"mappings":";;KAEY,YAAY,KAAK,MAAM,UAAU,MAAM;UAElC,mBAAmB;WACzB;WACA;WACA,QAAQ,YAAY;WACpB,SAAS,QAAQ,YAAY;WAC7B;;KAGC,yBAAyB,GAAG,OAAO,mBAAmB;iBAIlD,6BAA6B,aAAa;UAIzC,UAAU;WAChB,WAAW;WACX,MAAM,QAAQ,YAAY;WAC1B,UAAU,OAAO;;iBAGZ,gBAAgB,GAC9B,aAAa,SAAS,qBACtB,QAAQ,YACR,cAAc,GACd,8BACC,UAAU"}
|
package/dist/state-slot.js
CHANGED
|
@@ -1,46 +1,47 @@
|
|
|
1
|
+
//#region src/state-slot.ts
|
|
1
2
|
let stateWriteInterceptor;
|
|
2
|
-
|
|
3
|
-
|
|
3
|
+
function installStateWriteInterceptor(interceptor) {
|
|
4
|
+
stateWriteInterceptor = interceptor;
|
|
4
5
|
}
|
|
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
|
-
replace,
|
|
44
|
-
};
|
|
6
|
+
function createStateSlot(invalidate, source, initialValue, assertWritable) {
|
|
7
|
+
let value = initialValue;
|
|
8
|
+
let revision = 0;
|
|
9
|
+
const token = {};
|
|
10
|
+
const commit = (next) => {
|
|
11
|
+
if (Object.is(value, next)) return;
|
|
12
|
+
value = next;
|
|
13
|
+
revision += 1;
|
|
14
|
+
invalidate(source);
|
|
15
|
+
};
|
|
16
|
+
const commitUpdate = (update) => {
|
|
17
|
+
const next = typeof update === "function" ? update(value) : update;
|
|
18
|
+
commit(next);
|
|
19
|
+
};
|
|
20
|
+
const commitReplacement = (replacement) => commit(replacement);
|
|
21
|
+
const write = (update, commitWrite) => {
|
|
22
|
+
if (stateWriteInterceptor?.({
|
|
23
|
+
slot: token,
|
|
24
|
+
revision,
|
|
25
|
+
update,
|
|
26
|
+
commit: commitWrite,
|
|
27
|
+
currentRevision: () => revision
|
|
28
|
+
})) return;
|
|
29
|
+
commitWrite(update);
|
|
30
|
+
};
|
|
31
|
+
const replace = (next) => {
|
|
32
|
+
assertWritable?.();
|
|
33
|
+
write(next, commitReplacement);
|
|
34
|
+
};
|
|
35
|
+
return {
|
|
36
|
+
get: () => value,
|
|
37
|
+
set: (update) => {
|
|
38
|
+
assertWritable?.();
|
|
39
|
+
write(update, commitUpdate);
|
|
40
|
+
},
|
|
41
|
+
replace
|
|
42
|
+
};
|
|
45
43
|
}
|
|
44
|
+
//#endregion
|
|
45
|
+
export { createStateSlot, installStateWriteInterceptor };
|
|
46
|
+
|
|
46
47
|
//# sourceMappingURL=state-slot.js.map
|
package/dist/state-slot.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state-slot.js","
|
|
1
|
+
{"version":3,"file":"state-slot.js","names":[],"sources":["../src/state-slot.ts"],"mappings":";AAcA,IAAI;AAEJ,SAAgB,6BAA6B,aAA0C;CACrF,wBAAwB;AAC1B;AAQA,SAAgB,gBACd,YACA,QACA,cACA,gBACc;CACd,IAAI,QAAQ;CACZ,IAAI,WAAW;CACf,MAAM,QAAQ,CAAC;CAEf,MAAM,UAAU,SAAkB;EAChC,IAAI,OAAO,GAAG,OAAO,IAAI,GAAG;EAE5B,QAAQ;EACR,YAAY;EACZ,WAAW,MAAM;CACnB;CACA,MAAM,gBAAgB,WAAiC;EACrD,MAAM,OAAO,OAAO,WAAW,aAAc,OAA8B,KAAK,IAAI;EACpF,OAAO,IAAI;CACb;CACA,MAAM,qBAAqB,gBAAsC,OAAO,WAAgB;CACxF,MAAM,SAAS,QAAwB,gBAAwD;EAC7F,IACE,wBAAwB;GACtB,MAAM;GACN;GACA;GACA,QAAQ;GACR,uBAAuB;EACzB,CAAC,GAED;EAEF,YAAY,MAAM;CACpB;CACA,MAAM,WAAW,SAAkB;EACjC,iBAAiB;EACjB,MAAM,MAAM,iBAAiB;CAC/B;CAEA,OAAO;EACL,WAAW;EACX,MAAM,WAAW;GACf,iBAAiB;GACjB,MAAM,QAAQ,YAAY;EAC5B;EACA;CACF;AACF"}
|
package/dist/testing.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import { readCompiledOwnerMetrics as
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import { readCompiledOwnerMetrics as readCompiledOwnerMetrics$1 } from "./compiled/core.js";
|
|
2
|
+
//#region src/testing.d.ts
|
|
3
|
+
declare function beginActScope(): void;
|
|
4
|
+
declare function endActScope(): void;
|
|
5
|
+
declare function flushScheduledTasks(): number;
|
|
6
|
+
declare function hasScheduledTasks(): boolean;
|
|
7
|
+
declare function readCompiledOwnerMetrics(): ReturnType<typeof readCompiledOwnerMetrics$1>;
|
|
8
|
+
//#endregion
|
|
9
|
+
export { beginActScope, endActScope, flushScheduledTasks, hasScheduledTasks, readCompiledOwnerMetrics };
|
|
7
10
|
//# sourceMappingURL=testing.d.ts.map
|
package/dist/testing.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.d.ts","
|
|
1
|
+
{"version":3,"file":"testing.d.ts","names":[],"sources":["../src/testing.ts"],"mappings":";;iBAQgB;iBAIA;iBAIA;iBAIA;iBAIA,4BAA4B,kBAAkB"}
|
package/dist/testing.js
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { beginActScope as beginActScope$1, endActScope as endActScope$1, flushScheduledTasks as flushScheduledTasks$1, hasScheduledTasks as hasScheduledTasks$1 } from "./scheduler.js";
|
|
2
|
+
import { readCompiledOwnerMetrics as readCompiledOwnerMetrics$1 } from "./compiled/core.js";
|
|
3
|
+
//#region src/testing.ts
|
|
4
|
+
function beginActScope() {
|
|
5
|
+
beginActScope$1();
|
|
5
6
|
}
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
function endActScope() {
|
|
8
|
+
endActScope$1();
|
|
8
9
|
}
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
function flushScheduledTasks() {
|
|
11
|
+
return flushScheduledTasks$1();
|
|
11
12
|
}
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
function hasScheduledTasks() {
|
|
14
|
+
return hasScheduledTasks$1();
|
|
14
15
|
}
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
function readCompiledOwnerMetrics() {
|
|
17
|
+
return readCompiledOwnerMetrics$1();
|
|
17
18
|
}
|
|
19
|
+
//#endregion
|
|
20
|
+
export { beginActScope, endActScope, flushScheduledTasks, hasScheduledTasks, readCompiledOwnerMetrics };
|
|
21
|
+
|
|
18
22
|
//# sourceMappingURL=testing.js.map
|
package/dist/testing.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.js","
|
|
1
|
+
{"version":3,"file":"testing.js","names":["flushSchedulerTasks","schedulerHasTasks","readOwnerMetrics"],"sources":["../src/testing.ts"],"mappings":";;;AAQA,SAAgB,gBAAsB;CACpC,gBAAuB;AACzB;AAEA,SAAgB,cAAoB;CAClC,cAAqB;AACvB;AAEA,SAAgB,sBAA8B;CAC5C,OAAOA,sBAAoB;AAC7B;AAEA,SAAgB,oBAA6B;CAC3C,OAAOC,oBAAkB;AAC3B;AAEA,SAAgB,2BAAgE;CAC9E,OAAOC,2BAAiB;AAC1B"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vidact/runtime",
|
|
3
|
-
"version": "0.2.0-beta.
|
|
3
|
+
"version": "0.2.0-beta.5",
|
|
4
4
|
"description": "Fine-grained direct-DOM runtime for compiled Vidact applications",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"compiler",
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
"CHANGELOG.md",
|
|
22
22
|
"dist",
|
|
23
23
|
"LICENSE",
|
|
24
|
-
"README.md"
|
|
24
|
+
"README.md",
|
|
25
|
+
"src"
|
|
25
26
|
],
|
|
26
27
|
"type": "module",
|
|
27
28
|
"sideEffects": [
|
|
@@ -126,16 +127,16 @@
|
|
|
126
127
|
}
|
|
127
128
|
},
|
|
128
129
|
"publishConfig": {
|
|
129
|
-
"access": "public"
|
|
130
|
-
"provenance": true
|
|
130
|
+
"access": "public"
|
|
131
131
|
},
|
|
132
132
|
"devDependencies": {
|
|
133
133
|
"@types/react": "^19.2.18",
|
|
134
134
|
"@vitest/browser-playwright": "4.1.11",
|
|
135
|
+
"tsdown": "0.22.14",
|
|
135
136
|
"typescript": "7.0.2",
|
|
136
137
|
"vitest": "4.1.11",
|
|
137
|
-
"@vidact/test-support": "0.2.0-beta.
|
|
138
|
-
"@vidact/react-types": "0.2.0-beta.
|
|
138
|
+
"@vidact/test-support": "0.2.0-beta.5",
|
|
139
|
+
"@vidact/react-types": "0.2.0-beta.5"
|
|
139
140
|
},
|
|
140
141
|
"peerDependencies": {
|
|
141
142
|
"@types/react": "^19.2.18"
|
|
@@ -144,7 +145,7 @@
|
|
|
144
145
|
"node": ">=24"
|
|
145
146
|
},
|
|
146
147
|
"scripts": {
|
|
147
|
-
"build": "
|
|
148
|
+
"build": "tsdown",
|
|
148
149
|
"test": "vitest run && vitest run --config vitest.retained-ui.config.ts && vitest run --config vitest.server.config.ts",
|
|
149
150
|
"typecheck": "tsc -p tsconfig.json"
|
|
150
151
|
}
|
package/src/actions.ts
ADDED
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
import {
|
|
2
|
+
captureCompiledTask,
|
|
3
|
+
createContext,
|
|
4
|
+
createCompiledExternalStore,
|
|
5
|
+
createCompiledState,
|
|
6
|
+
registerCompiledCleanup,
|
|
7
|
+
runWithCompiledContext,
|
|
8
|
+
useContext,
|
|
9
|
+
} from './compiled/core.ts'
|
|
10
|
+
import type { CompiledScope } from './compiled/types.ts'
|
|
11
|
+
import {
|
|
12
|
+
registerTransitionAborter,
|
|
13
|
+
registerTransitionFinalizer,
|
|
14
|
+
registerTransitionSettlement,
|
|
15
|
+
runUrgentUpdate,
|
|
16
|
+
startIndependentTransition,
|
|
17
|
+
} from './concurrent.ts'
|
|
18
|
+
import { h, type DirectChild, type DirectProps } from './direct-dom.ts'
|
|
19
|
+
import { resetActionForm } from './dom/forms.ts'
|
|
20
|
+
import { installDomPropHandler } from './dom/properties.ts'
|
|
21
|
+
import type { SourceMask } from './source-mask.ts'
|
|
22
|
+
import type { StateSlot } from './state-slot.ts'
|
|
23
|
+
|
|
24
|
+
const DEV = typeof __VIDACT_DEV__ === 'undefined' || __VIDACT_DEV__
|
|
25
|
+
const FORM_ACTION_EXECUTE = Symbol(DEV ? 'Vidact.FormActionExecute' : undefined)
|
|
26
|
+
|
|
27
|
+
export interface FormStatus {
|
|
28
|
+
readonly pending: boolean
|
|
29
|
+
readonly data: FormData | null
|
|
30
|
+
readonly method: 'get' | 'post'
|
|
31
|
+
readonly action: FunctionFormAction | null
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export type FunctionFormAction = (data: FormData) => unknown | PromiseLike<unknown>
|
|
35
|
+
|
|
36
|
+
type FormStatusStore = {
|
|
37
|
+
current: FormStatus
|
|
38
|
+
readonly listeners: Set<() => void>
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
type ActionFrame = {
|
|
42
|
+
readonly settlements: Set<() => void>
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
type ActionDispatch<Payload> = {
|
|
46
|
+
(payload: Payload): void
|
|
47
|
+
readonly permalink?: string
|
|
48
|
+
readonly [FORM_ACTION_EXECUTE]: (payload: Payload) => Promise<void>
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
type ActionQueueEntry<Payload> = {
|
|
52
|
+
readonly payload: Payload
|
|
53
|
+
readonly reportError: boolean
|
|
54
|
+
readonly resolve: () => void
|
|
55
|
+
readonly reject: (error: unknown) => void
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
type OptimisticLayer<Update> = {
|
|
59
|
+
readonly frame: object
|
|
60
|
+
readonly update: Update
|
|
61
|
+
settling: boolean
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface OptimisticSlot<Value, Update> {
|
|
65
|
+
readonly get: () => Value
|
|
66
|
+
readonly set: (update: Update) => void
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const defaultFormStatus: FormStatus = {
|
|
70
|
+
pending: false,
|
|
71
|
+
data: null,
|
|
72
|
+
method: 'get',
|
|
73
|
+
action: null,
|
|
74
|
+
}
|
|
75
|
+
const functionActions = new WeakMap<Element, FunctionFormAction>()
|
|
76
|
+
const formStatusContext = createContext<FormStatusStore | undefined>(undefined)
|
|
77
|
+
let actionDomInstalled = false
|
|
78
|
+
let activeActionFrame: ActionFrame | undefined
|
|
79
|
+
|
|
80
|
+
export function createCompiledActionState<State, Payload>(
|
|
81
|
+
scope: CompiledScope,
|
|
82
|
+
stateSource: SourceMask,
|
|
83
|
+
pendingSource: SourceMask,
|
|
84
|
+
action: (previousState: State, payload: Payload) => State | PromiseLike<State>,
|
|
85
|
+
initialState: State,
|
|
86
|
+
permalink?: string,
|
|
87
|
+
): {
|
|
88
|
+
readonly get: () => { readonly value: State; readonly pending: boolean }
|
|
89
|
+
readonly set: ActionDispatch<Payload>
|
|
90
|
+
} {
|
|
91
|
+
const state = createCompiledState(scope, stateSource, () => initialState)
|
|
92
|
+
const pending = createCompiledState(scope, pendingSource, false)
|
|
93
|
+
const task = captureCompiledTask(scope)
|
|
94
|
+
const queue: ActionQueueEntry<Payload>[] = []
|
|
95
|
+
let running = false
|
|
96
|
+
|
|
97
|
+
const enqueue = (payload: Payload, reportError: boolean): Promise<void> => {
|
|
98
|
+
runUrgentUpdate(() => pending.replace(true))
|
|
99
|
+
return new Promise<void>((resolve, reject) => {
|
|
100
|
+
queue.push({ payload, reportError, resolve, reject })
|
|
101
|
+
processNext()
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const dispatch = ((payload: Payload): void => {
|
|
106
|
+
void enqueue(payload, true)
|
|
107
|
+
}) as ActionDispatch<Payload>
|
|
108
|
+
Object.defineProperty(dispatch, FORM_ACTION_EXECUTE, {
|
|
109
|
+
value: (payload: Payload) => enqueue(payload, false),
|
|
110
|
+
})
|
|
111
|
+
if (permalink !== undefined) Object.defineProperty(dispatch, 'permalink', { value: permalink })
|
|
112
|
+
|
|
113
|
+
const processNext = (): void => {
|
|
114
|
+
if (running) return
|
|
115
|
+
const entry = queue.shift()
|
|
116
|
+
if (entry === undefined) {
|
|
117
|
+
runUrgentUpdate(() => pending.replace(false))
|
|
118
|
+
return
|
|
119
|
+
}
|
|
120
|
+
running = true
|
|
121
|
+
const frame = createActionFrame()
|
|
122
|
+
let result: State | PromiseLike<State>
|
|
123
|
+
const previousFrame = activeActionFrame
|
|
124
|
+
activeActionFrame = frame
|
|
125
|
+
try {
|
|
126
|
+
result = action(state.get(), entry.payload)
|
|
127
|
+
} catch (error) {
|
|
128
|
+
activeActionFrame = previousFrame
|
|
129
|
+
finishFailure(entry, frame, error)
|
|
130
|
+
return
|
|
131
|
+
}
|
|
132
|
+
activeActionFrame = previousFrame
|
|
133
|
+
void Promise.resolve(result).then(
|
|
134
|
+
(nextState) => {
|
|
135
|
+
if (task.disposed()) {
|
|
136
|
+
entry.resolve()
|
|
137
|
+
running = false
|
|
138
|
+
return
|
|
139
|
+
}
|
|
140
|
+
runActionTransition(
|
|
141
|
+
() => {
|
|
142
|
+
state.replace(nextState)
|
|
143
|
+
settleActionFrame(frame)
|
|
144
|
+
},
|
|
145
|
+
() => {
|
|
146
|
+
entry.resolve()
|
|
147
|
+
running = false
|
|
148
|
+
processNext()
|
|
149
|
+
},
|
|
150
|
+
)
|
|
151
|
+
},
|
|
152
|
+
(error: unknown) => finishFailure(entry, frame, error),
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const finishFailure = (
|
|
157
|
+
entry: ActionQueueEntry<Payload>,
|
|
158
|
+
frame: ActionFrame,
|
|
159
|
+
error: unknown,
|
|
160
|
+
): void => {
|
|
161
|
+
if (task.disposed()) {
|
|
162
|
+
entry.resolve()
|
|
163
|
+
running = false
|
|
164
|
+
return
|
|
165
|
+
}
|
|
166
|
+
runActionTransition(
|
|
167
|
+
() => settleActionFrame(frame),
|
|
168
|
+
() => {
|
|
169
|
+
running = false
|
|
170
|
+
if (entry.reportError) {
|
|
171
|
+
entry.resolve()
|
|
172
|
+
task.report(error)
|
|
173
|
+
} else {
|
|
174
|
+
entry.reject(error)
|
|
175
|
+
}
|
|
176
|
+
processNext()
|
|
177
|
+
},
|
|
178
|
+
)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
get: () => ({ value: state.get(), pending: pending.get() }),
|
|
183
|
+
set: dispatch,
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function createCompiledOptimistic<Value, Update = Value>(
|
|
188
|
+
scope: CompiledScope,
|
|
189
|
+
reads: SourceMask,
|
|
190
|
+
writes: SourceMask,
|
|
191
|
+
evaluate: () => Value,
|
|
192
|
+
reducer: (current: Value, update: Update) => Value = (_current, update) =>
|
|
193
|
+
update as unknown as Value,
|
|
194
|
+
): OptimisticSlot<Value, Update> {
|
|
195
|
+
const slot = createCompiledState(scope, writes, () => evaluate())
|
|
196
|
+
let layers: OptimisticLayer<Update>[] = []
|
|
197
|
+
const compute = (items = layers): Value =>
|
|
198
|
+
items.reduce(
|
|
199
|
+
(value, layer) => (layer.settling ? value : reducer(value, layer.update)),
|
|
200
|
+
evaluate(),
|
|
201
|
+
)
|
|
202
|
+
scope[0](reads, () => slot.replace(compute()))
|
|
203
|
+
|
|
204
|
+
const add = (update: Update): void => {
|
|
205
|
+
const frame = activeActionFrame ?? {}
|
|
206
|
+
const layer: OptimisticLayer<Update> = { frame, update, settling: false }
|
|
207
|
+
layers.push(layer)
|
|
208
|
+
try {
|
|
209
|
+
runUrgentUpdate(() => slot.replace(compute()))
|
|
210
|
+
} catch (error) {
|
|
211
|
+
layers = layers.filter((item) => item !== layer)
|
|
212
|
+
throw error
|
|
213
|
+
}
|
|
214
|
+
const settle = (): void => settleOptimisticFrame(frame)
|
|
215
|
+
if (activeActionFrame !== undefined) {
|
|
216
|
+
activeActionFrame.settlements.add(settle)
|
|
217
|
+
return
|
|
218
|
+
}
|
|
219
|
+
if (registerTransitionSettlement(() => startIndependentTransition(settle))) return
|
|
220
|
+
layers = layers.filter((item) => item !== layer)
|
|
221
|
+
runUrgentUpdate(() => slot.replace(compute()))
|
|
222
|
+
throw new Error(DEV ? 'optimistic updates must run inside an Action or transition' : 'V035')
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const settleOptimisticFrame = (frame: object): void => {
|
|
226
|
+
const settling = layers.filter((layer) => layer.frame === frame)
|
|
227
|
+
if (settling.length === 0) return
|
|
228
|
+
for (const layer of settling) layer.settling = true
|
|
229
|
+
slot.replace(compute())
|
|
230
|
+
if (
|
|
231
|
+
registerTransitionFinalizer(() => {
|
|
232
|
+
layers = layers.filter((layer) => layer.frame !== frame)
|
|
233
|
+
})
|
|
234
|
+
) {
|
|
235
|
+
registerTransitionAborter(() => {
|
|
236
|
+
for (const layer of settling) layer.settling = false
|
|
237
|
+
})
|
|
238
|
+
return
|
|
239
|
+
}
|
|
240
|
+
layers = layers.filter((layer) => layer.frame !== frame)
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return { get: slot.get, set: add }
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export function createCompiledFormStatus(
|
|
247
|
+
scope: CompiledScope,
|
|
248
|
+
sourceMask: SourceMask,
|
|
249
|
+
): StateSlot<FormStatus> {
|
|
250
|
+
const store = useContext(formStatusContext)
|
|
251
|
+
if (store === undefined) return createCompiledState(scope, sourceMask, () => defaultFormStatus)
|
|
252
|
+
return createCompiledExternalStore(
|
|
253
|
+
scope,
|
|
254
|
+
sourceMask,
|
|
255
|
+
(notify) => {
|
|
256
|
+
store.listeners.add(notify)
|
|
257
|
+
return () => store.listeners.delete(notify)
|
|
258
|
+
},
|
|
259
|
+
() => store.current,
|
|
260
|
+
)
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export function ActionForm(props: DirectProps): DirectChild {
|
|
264
|
+
ensureActionDom()
|
|
265
|
+
const task = captureCompiledTask()
|
|
266
|
+
const store: FormStatusStore = { current: defaultFormStatus, listeners: new Set() }
|
|
267
|
+
const children = normalizeChildren(props?.children)
|
|
268
|
+
const form = runWithCompiledContext(formStatusContext, store, () =>
|
|
269
|
+
h('form', props, ...children),
|
|
270
|
+
) as HTMLFormElement
|
|
271
|
+
let activeSubmissions = 0
|
|
272
|
+
let latestSubmission = 0
|
|
273
|
+
let latestSucceeded = false
|
|
274
|
+
const submit = (event: SubmitEvent): void => {
|
|
275
|
+
const submitter = event.submitter
|
|
276
|
+
const hasSubmitterOverride =
|
|
277
|
+
submitter instanceof Element &&
|
|
278
|
+
(functionActions.has(submitter) || submitter.hasAttribute('formaction'))
|
|
279
|
+
const action = hasSubmitterOverride
|
|
280
|
+
? functionActions.get(submitter as Element)
|
|
281
|
+
: functionActions.get(form)
|
|
282
|
+
if (action === undefined) return
|
|
283
|
+
event.preventDefault()
|
|
284
|
+
const submission = latestSubmission + 1
|
|
285
|
+
latestSubmission = submission
|
|
286
|
+
activeSubmissions += 1
|
|
287
|
+
const data = createFormData(form, submitter)
|
|
288
|
+
publishFormStatus(store, {
|
|
289
|
+
pending: true,
|
|
290
|
+
data,
|
|
291
|
+
method: 'post',
|
|
292
|
+
action,
|
|
293
|
+
})
|
|
294
|
+
const frame = createActionFrame()
|
|
295
|
+
let result: unknown
|
|
296
|
+
const previousFrame = activeActionFrame
|
|
297
|
+
activeActionFrame = frame
|
|
298
|
+
try {
|
|
299
|
+
result = executeFormAction(action, data)
|
|
300
|
+
} catch (error) {
|
|
301
|
+
activeActionFrame = previousFrame
|
|
302
|
+
finishFormAction(submission, false, frame, error)
|
|
303
|
+
return
|
|
304
|
+
}
|
|
305
|
+
activeActionFrame = previousFrame
|
|
306
|
+
void Promise.resolve(result).then(
|
|
307
|
+
() => finishFormAction(submission, true, frame),
|
|
308
|
+
(error: unknown) => finishFormAction(submission, false, frame, error),
|
|
309
|
+
)
|
|
310
|
+
}
|
|
311
|
+
const finishFormAction = (
|
|
312
|
+
submission: number,
|
|
313
|
+
succeeded: boolean,
|
|
314
|
+
frame: ActionFrame,
|
|
315
|
+
error?: unknown,
|
|
316
|
+
): void => {
|
|
317
|
+
if (task.disposed()) return
|
|
318
|
+
runActionTransition(
|
|
319
|
+
() => settleActionFrame(frame),
|
|
320
|
+
() => {
|
|
321
|
+
activeSubmissions -= 1
|
|
322
|
+
if (submission === latestSubmission) latestSucceeded = succeeded
|
|
323
|
+
if (activeSubmissions === 0) {
|
|
324
|
+
if (latestSucceeded) resetActionForm(form)
|
|
325
|
+
publishFormStatus(store, defaultFormStatus)
|
|
326
|
+
}
|
|
327
|
+
if (!succeeded) task.report(error)
|
|
328
|
+
},
|
|
329
|
+
)
|
|
330
|
+
}
|
|
331
|
+
form.addEventListener('submit', submit)
|
|
332
|
+
registerCompiledCleanup(() => form.removeEventListener('submit', submit))
|
|
333
|
+
return form
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
export function compiledFormAction<Value>(value: Value): Value {
|
|
337
|
+
ensureActionDom()
|
|
338
|
+
return value
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
export function useActionState(): never {
|
|
342
|
+
throw new Error(DEV ? 'useActionState requires compiler lowering' : 'V036')
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
export function useOptimistic(): never {
|
|
346
|
+
throw new Error(DEV ? 'useOptimistic requires compiler lowering' : 'V037')
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
export function useFormStatus(): never {
|
|
350
|
+
throw new Error(DEV ? 'useFormStatus requires compiler lowering' : 'V038')
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function createActionFrame(): ActionFrame {
|
|
354
|
+
return { settlements: new Set() }
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function settleActionFrame(frame: ActionFrame): void {
|
|
358
|
+
for (const settle of frame.settlements) settle()
|
|
359
|
+
if (!registerTransitionFinalizer(() => frame.settlements.clear())) frame.settlements.clear()
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function runActionTransition(stage: () => void, complete: () => void): void {
|
|
363
|
+
let committed = false
|
|
364
|
+
startIndependentTransition(() => {
|
|
365
|
+
stage()
|
|
366
|
+
registerTransitionFinalizer(() => {
|
|
367
|
+
committed = true
|
|
368
|
+
})
|
|
369
|
+
registerTransitionSettlement(() => {
|
|
370
|
+
if (committed) {
|
|
371
|
+
complete()
|
|
372
|
+
} else {
|
|
373
|
+
queueMicrotask(() => runActionTransition(stage, complete))
|
|
374
|
+
}
|
|
375
|
+
})
|
|
376
|
+
})
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function ensureActionDom(): void {
|
|
380
|
+
if (actionDomInstalled) return
|
|
381
|
+
actionDomInstalled = true
|
|
382
|
+
installDomPropHandler((element, name, value) => {
|
|
383
|
+
const valid =
|
|
384
|
+
(name === 'action' && element instanceof HTMLFormElement) ||
|
|
385
|
+
(name === 'formAction' &&
|
|
386
|
+
(element instanceof HTMLButtonElement || element instanceof HTMLInputElement))
|
|
387
|
+
if (!valid) return false
|
|
388
|
+
if (typeof value !== 'function') {
|
|
389
|
+
functionActions.delete(element)
|
|
390
|
+
return false
|
|
391
|
+
}
|
|
392
|
+
functionActions.set(element, value as FunctionFormAction)
|
|
393
|
+
element.removeAttribute(name === 'action' ? 'action' : 'formaction')
|
|
394
|
+
return true
|
|
395
|
+
})
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function executeFormAction(action: FunctionFormAction, data: FormData): unknown {
|
|
399
|
+
const specialized = (action as Partial<ActionDispatch<FormData>>)[FORM_ACTION_EXECUTE]
|
|
400
|
+
return specialized === undefined ? action(data) : specialized(data)
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function createFormData(form: HTMLFormElement, submitter: HTMLElement | null): FormData {
|
|
404
|
+
return submitter === null ? new FormData(form) : new FormData(form, submitter)
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
function publishFormStatus(store: FormStatusStore, status: FormStatus): void {
|
|
408
|
+
store.current = status
|
|
409
|
+
for (const listener of store.listeners) listener()
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function normalizeChildren(value: unknown): DirectChild[] {
|
|
413
|
+
if (value === undefined) return []
|
|
414
|
+
return (Array.isArray(value) ? value : [value]) as DirectChild[]
|
|
415
|
+
}
|