@vidact/runtime 0.2.0-beta.6 → 0.2.0-beta.8

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.
Files changed (51) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/actions.js +1 -1
  3. package/dist/compiled/core.d.ts +17 -5
  4. package/dist/compiled/core.d.ts.map +1 -1
  5. package/dist/compiled/core.js +545 -155
  6. package/dist/compiled/core.js.map +1 -1
  7. package/dist/direct-dom.d.ts.map +1 -1
  8. package/dist/direct-dom.js +102 -11
  9. package/dist/direct-dom.js.map +1 -1
  10. package/dist/dom/events.d.ts +4 -1
  11. package/dist/dom/events.d.ts.map +1 -1
  12. package/dist/dom/events.js +66 -1
  13. package/dist/dom/events.js.map +1 -1
  14. package/dist/dom/namespace.js +1 -1
  15. package/dist/hydration-bridge.d.ts +4 -4
  16. package/dist/hydration-bridge.d.ts.map +1 -1
  17. package/dist/hydration-bridge.js.map +1 -1
  18. package/dist/hydration.d.ts +2 -2
  19. package/dist/hydration.d.ts.map +1 -1
  20. package/dist/hydration.js.map +1 -1
  21. package/dist/index.d.ts +3 -3
  22. package/dist/index.js +3 -3
  23. package/dist/keyed-list.d.ts +6 -2
  24. package/dist/keyed-list.d.ts.map +1 -1
  25. package/dist/keyed-list.js +122 -11
  26. package/dist/keyed-list.js.map +1 -1
  27. package/dist/metadata.js +1 -1
  28. package/dist/renderable.d.ts +2 -1
  29. package/dist/renderable.d.ts.map +1 -1
  30. package/dist/renderable.js +12 -1
  31. package/dist/renderable.js.map +1 -1
  32. package/dist/source-mask.d.ts +2 -1
  33. package/dist/source-mask.d.ts.map +1 -1
  34. package/dist/source-mask.js +16 -1
  35. package/dist/source-mask.js.map +1 -1
  36. package/dist/spread.js +2 -2
  37. package/dist/state-slot.d.ts +12 -1
  38. package/dist/state-slot.d.ts.map +1 -1
  39. package/dist/state-slot.js +54 -24
  40. package/dist/state-slot.js.map +1 -1
  41. package/package.json +3 -3
  42. package/src/compiled/core.ts +785 -179
  43. package/src/direct-dom.ts +136 -10
  44. package/src/dom/events.ts +86 -0
  45. package/src/hydration-bridge.ts +10 -4
  46. package/src/hydration.ts +5 -3
  47. package/src/index.ts +2 -0
  48. package/src/keyed-list.ts +168 -22
  49. package/src/renderable.ts +30 -0
  50. package/src/source-mask.ts +20 -0
  51. package/src/state-slot.ts +83 -27
package/src/renderable.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { binding, isCompiledBinding } from './compiled/core.ts'
2
2
  import type { CompiledBinding, CompiledRenderValue, CompiledScope } from './compiled/types.ts'
3
+ import { compiledComponentSpread } from './component-spread.ts'
3
4
  import { Fragment, h, type DirectComponent } from './direct-dom.ts'
4
5
  import {
5
6
  installRenderableClone,
@@ -48,6 +49,35 @@ export function createRenderable(
48
49
  return capability
49
50
  }
50
51
 
52
+ export function createComponentRenderable(
53
+ type: DirectComponent,
54
+ input: RenderablePropsInput,
55
+ identity: unknown = type,
56
+ ): CompiledRenderable {
57
+ return createRenderable(
58
+ input,
59
+ (currentInput) =>
60
+ h(
61
+ type,
62
+ { ...renderableComponentProps(currentInput), ref: renderableRef(currentInput) },
63
+ renderableChildren(currentInput),
64
+ ),
65
+ identity,
66
+ true,
67
+ )
68
+ }
69
+
70
+ function renderableComponentProps(input: RenderablePropsInput): Record<string, unknown> {
71
+ const ordinary = projectInput<Record<string, unknown>>(input, (props) =>
72
+ Object.fromEntries(
73
+ Reflect.ownKeys(props)
74
+ .filter((name) => typeof name !== 'string' || !SPECIAL_PROPS.has(name))
75
+ .map((name) => [name, Reflect.get(props, name)]),
76
+ ),
77
+ )
78
+ return isCompiledBinding(ordinary) ? compiledComponentSpread(ordinary, []) : ordinary
79
+ }
80
+
51
81
  export function isRenderable(value: unknown): value is CompiledRenderable {
52
82
  return isRenderableProtocol(value)
53
83
  }
@@ -50,6 +50,17 @@ export function intersectsSources(left: SourceMask, right: SourceMask): boolean
50
50
  return false
51
51
  }
52
52
 
53
+ export function forEachSource(mask: SourceMask, visit: (index: number) => void): void {
54
+ if (typeof mask === 'number') {
55
+ visitWordSources(mask, 0, visit)
56
+ return
57
+ }
58
+
59
+ for (let word = 0; word < mask.length; word += 1) {
60
+ visitWordSources(mask[word] ?? 0, word * BITS_PER_WORD, visit)
61
+ }
62
+ }
63
+
53
64
  export function isEmptySources(mask: SourceMask): boolean {
54
65
  if (typeof mask === 'number') return mask === 0
55
66
  for (const word of mask) {
@@ -78,6 +89,15 @@ function wordAt(mask: SourceMask, index: number): number {
78
89
  return mask[index] ?? 0
79
90
  }
80
91
 
92
+ function visitWordSources(word: number, offset: number, visit: (index: number) => void): void {
93
+ let remaining = word >>> 0
94
+ while (remaining !== 0) {
95
+ const bit = 31 - Math.clz32(remaining)
96
+ visit(offset + bit)
97
+ remaining = (remaining & ~(1 << bit)) >>> 0
98
+ }
99
+ }
100
+
81
101
  function wordLength(mask: SourceMask): number {
82
102
  return typeof mask === 'number' ? 1 : mask.length
83
103
  }
package/src/state-slot.ts CHANGED
@@ -24,6 +24,59 @@ export interface StateSlot<T> {
24
24
  readonly replace: (value: T) => void
25
25
  }
26
26
 
27
+ export interface ReadOnlyStateInvalidationTarget {
28
+ readonly [1]: (sources: SourceMask) => void
29
+ }
30
+
31
+ export interface ReadOnlyStateCell<T> extends Pick<StateSlot<T>, 'get'> {
32
+ value: T
33
+ revision?: number
34
+ readonly invalidationTarget: ReadOnlyStateInvalidationTarget
35
+ readonly source: SourceMask
36
+ }
37
+
38
+ export function createReadOnlyStateSlot<T>(
39
+ invalidationTarget: ReadOnlyStateInvalidationTarget,
40
+ source: SourceMask,
41
+ initialValue: T,
42
+ ): ReadOnlyStateCell<T> {
43
+ return {
44
+ value: initialValue,
45
+ invalidationTarget,
46
+ source,
47
+ get: readReadOnlyState,
48
+ }
49
+ }
50
+
51
+ function readReadOnlyState<T>(this: ReadOnlyStateCell<T>): T {
52
+ return this.value
53
+ }
54
+
55
+ export function replaceReadOnlyStateSlot<T>(cell: ReadOnlyStateCell<T>, replacement: T): void {
56
+ if (Object.is(cell.value, replacement)) return
57
+ const revision = cell.revision ?? 0
58
+ if (
59
+ stateWriteInterceptor?.({
60
+ slot: cell,
61
+ revision,
62
+ update: replacement,
63
+ commit: (pending) => applyReadOnlyState(cell, pending as T),
64
+ currentRevision: () => cell.revision ?? 0,
65
+ })
66
+ ) {
67
+ cell.revision ??= revision
68
+ return
69
+ }
70
+ applyReadOnlyState(cell, replacement)
71
+ }
72
+
73
+ function applyReadOnlyState<T>(cell: ReadOnlyStateCell<T>, next: T): void {
74
+ if (Object.is(cell.value, next)) return
75
+ cell.value = next
76
+ if (cell.revision !== undefined) cell.revision += 1
77
+ cell.invalidationTarget[1](cell.source)
78
+ }
79
+
27
80
  export function createStateSlot<T>(
28
81
  invalidate: (sources: SourceMask) => void,
29
82
  source: SourceMask,
@@ -32,45 +85,48 @@ export function createStateSlot<T>(
32
85
  ): StateSlot<T> {
33
86
  let value = initialValue
34
87
  let revision = 0
35
- const token = {}
88
+ let token: object | undefined
36
89
 
37
- const commit = (next: T): void => {
90
+ const apply = (next: T): void => {
38
91
  if (Object.is(value, next)) return
39
92
 
40
93
  value = next
41
94
  revision += 1
42
95
  invalidate(source)
43
96
  }
44
- const commitUpdate = (update: StateUpdate<T>): void => {
45
- const next = typeof update === 'function' ? (update as (previous: T) => T)(value) : update
46
- commit(next)
47
- }
48
- const commitReplacement = (replacement: StateUpdate<T>): void => commit(replacement as T)
49
- const write = (update: StateUpdate<T>, commitWrite: (update: StateUpdate<T>) => void): void => {
50
- if (
51
- stateWriteInterceptor?.({
52
- slot: token,
53
- revision,
54
- update,
55
- commit: commitWrite,
56
- currentRevision: () => revision,
57
- })
58
- ) {
59
- return
60
- }
61
- commitWrite(update)
62
- }
63
- const replace = (next: T): void => {
64
- assertWritable?.()
65
- write(next, commitReplacement)
66
- }
67
97
 
68
98
  return {
69
99
  get: () => value,
70
100
  set: (update) => {
71
101
  assertWritable?.()
72
- write(update, commitUpdate)
102
+ if (
103
+ stateWriteInterceptor?.({
104
+ slot: (token ??= {}),
105
+ revision,
106
+ update,
107
+ commit: (pending) =>
108
+ apply(typeof pending === 'function' ? (pending as (previous: T) => T)(value) : pending),
109
+ currentRevision: () => revision,
110
+ })
111
+ ) {
112
+ return
113
+ }
114
+ apply(typeof update === 'function' ? (update as (previous: T) => T)(value) : update)
115
+ },
116
+ replace: (replacement) => {
117
+ assertWritable?.()
118
+ if (
119
+ stateWriteInterceptor?.({
120
+ slot: (token ??= {}),
121
+ revision,
122
+ update: replacement,
123
+ commit: (pending) => apply(pending as T),
124
+ currentRevision: () => revision,
125
+ })
126
+ ) {
127
+ return
128
+ }
129
+ apply(replacement)
73
130
  },
74
- replace,
75
131
  }
76
132
  }