@fictjs/runtime 0.0.13 → 0.0.14

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 (52) hide show
  1. package/dist/advanced.cjs +79 -0
  2. package/dist/advanced.cjs.map +1 -0
  3. package/dist/advanced.d.cts +50 -0
  4. package/dist/advanced.d.ts +50 -0
  5. package/dist/advanced.js +79 -0
  6. package/dist/advanced.js.map +1 -0
  7. package/dist/chunk-624QY53A.cjs +45 -0
  8. package/dist/chunk-624QY53A.cjs.map +1 -0
  9. package/dist/chunk-F3AIYQB7.js +45 -0
  10. package/dist/chunk-F3AIYQB7.js.map +1 -0
  11. package/dist/chunk-GJTYOFMO.cjs +109 -0
  12. package/dist/chunk-GJTYOFMO.cjs.map +1 -0
  13. package/dist/chunk-IUZXKAAY.js +109 -0
  14. package/dist/chunk-IUZXKAAY.js.map +1 -0
  15. package/dist/chunk-PMF6MWEV.cjs +3301 -0
  16. package/dist/chunk-PMF6MWEV.cjs.map +1 -0
  17. package/dist/chunk-RY4WDS6R.js +3301 -0
  18. package/dist/chunk-RY4WDS6R.js.map +1 -0
  19. package/dist/context-B7UYnfzM.d.ts +153 -0
  20. package/dist/context-UXySaqI_.d.cts +153 -0
  21. package/dist/effect-Auji1rz9.d.cts +350 -0
  22. package/dist/effect-Auji1rz9.d.ts +350 -0
  23. package/dist/index.cjs +98 -3558
  24. package/dist/index.cjs.map +1 -1
  25. package/dist/index.d.cts +5 -1358
  26. package/dist/index.d.ts +5 -1358
  27. package/dist/index.dev.js +240 -1698
  28. package/dist/index.dev.js.map +1 -1
  29. package/dist/index.js +63 -3435
  30. package/dist/index.js.map +1 -1
  31. package/dist/internal.cjs +901 -0
  32. package/dist/internal.cjs.map +1 -0
  33. package/dist/internal.d.cts +158 -0
  34. package/dist/internal.d.ts +158 -0
  35. package/dist/internal.js +901 -0
  36. package/dist/internal.js.map +1 -0
  37. package/dist/{jsx-dev-runtime.d.ts → props-CrOMYbLv.d.cts} +107 -18
  38. package/dist/{jsx-dev-runtime.d.cts → props-ES0Ag_Wd.d.ts} +107 -18
  39. package/dist/scope-DKYzWfTn.d.cts +55 -0
  40. package/dist/scope-S6eAzBJZ.d.ts +55 -0
  41. package/package.json +11 -1
  42. package/src/advanced.ts +101 -0
  43. package/src/constants.ts +3 -26
  44. package/src/context.ts +300 -0
  45. package/src/delegated-events.ts +24 -0
  46. package/src/index.ts +41 -112
  47. package/src/internal.ts +130 -0
  48. package/src/props.ts +48 -46
  49. package/src/store.ts +47 -7
  50. package/src/versioned-signal.ts +3 -3
  51. package/dist/jsx-runtime.d.cts +0 -671
  52. package/dist/jsx-runtime.d.ts +0 -671
package/src/props.ts CHANGED
@@ -122,38 +122,46 @@ export function mergeProps<T extends Record<string, unknown>>(
122
122
  return unwrapProps(value as T)
123
123
  }
124
124
 
125
+ const hasProp = (prop: string | symbol) => {
126
+ for (const src of validSources) {
127
+ const raw = resolveSource(src)
128
+ if (raw && prop in raw) {
129
+ return true
130
+ }
131
+ }
132
+ return false
133
+ }
134
+
135
+ const readProp = (prop: string | symbol) => {
136
+ // Only return undefined if no source has this Symbol property
137
+ // Search sources in reverse order (last wins)
138
+ for (let i = validSources.length - 1; i >= 0; i--) {
139
+ const src = validSources[i]!
140
+ const raw = resolveSource(src)
141
+ if (!raw || !(prop in raw)) continue
142
+
143
+ const value = (raw as Record<string | symbol, unknown>)[prop]
144
+ // Preserve prop getters - let child component's createPropsProxy unwrap lazily
145
+ // Note: For Symbol properties, we still wrap in getter if source is dynamic
146
+ if (typeof src === 'function' && !isPropGetter(value)) {
147
+ return __fictProp(() => {
148
+ const latest = resolveSource(src)
149
+ if (!latest || !(prop in latest)) return undefined
150
+ return (latest as Record<string | symbol, unknown>)[prop]
151
+ })
152
+ }
153
+ return value
154
+ }
155
+ return undefined
156
+ }
157
+
125
158
  return new Proxy({} as Record<string, unknown>, {
126
159
  get(_, prop) {
127
- // Only return undefined if no source has this Symbol property
128
- // Search sources in reverse order (last wins)
129
- for (let i = validSources.length - 1; i >= 0; i--) {
130
- const src = validSources[i]!
131
- const raw = resolveSource(src)
132
- if (!raw || !(prop in raw)) continue
133
-
134
- const value = (raw as Record<string | symbol, unknown>)[prop]
135
- // Preserve prop getters - let child component's createPropsProxy unwrap lazily
136
- // Note: For Symbol properties, we still wrap in getter if source is dynamic
137
- if (typeof src === 'function' && !isPropGetter(value)) {
138
- return __fictProp(() => {
139
- const latest = resolveSource(src)
140
- if (!latest || !(prop in latest)) return undefined
141
- return (latest as Record<string | symbol, unknown>)[prop]
142
- })
143
- }
144
- return value
145
- }
146
- return undefined
160
+ return readProp(prop)
147
161
  },
148
162
 
149
163
  has(_, prop) {
150
- for (const src of validSources) {
151
- const raw = resolveSource(src)
152
- if (raw && prop in raw) {
153
- return true
154
- }
155
- }
156
- return false
164
+ return hasProp(prop)
157
165
  },
158
166
 
159
167
  ownKeys() {
@@ -170,21 +178,12 @@ export function mergeProps<T extends Record<string, unknown>>(
170
178
  },
171
179
 
172
180
  getOwnPropertyDescriptor(_, prop) {
173
- for (let i = validSources.length - 1; i >= 0; i--) {
174
- const raw = resolveSource(validSources[i]!)
175
- if (raw && prop in raw) {
176
- return {
177
- enumerable: true,
178
- configurable: true,
179
- get: () => {
180
- const value = (raw as Record<string | symbol, unknown>)[prop]
181
- // Preserve prop getters - let child component's createPropsProxy unwrap lazily
182
- return value
183
- },
184
- }
185
- }
181
+ if (!hasProp(prop)) return undefined
182
+ return {
183
+ enumerable: true,
184
+ configurable: true,
185
+ get: () => readProp(prop),
186
186
  }
187
- return undefined
188
187
  },
189
188
  })
190
189
  }
@@ -192,19 +191,22 @@ export function mergeProps<T extends Record<string, unknown>>(
192
191
  export type PropGetter<T> = (() => T) & { __fictProp: true }
193
192
  /**
194
193
  * Memoize a prop getter to cache expensive computations.
195
- * Use when prop expressions involve heavy calculations.
194
+ * Use when prop expressions involve heavy calculations or you need lazy, reactive props.
196
195
  *
197
196
  * @example
198
197
  * ```tsx
199
- * // Without useProp - recomputes on every access
198
+ * // Without prop - recomputes on every access
200
199
  * <Child data={expensiveComputation(list, filter)} />
201
200
  *
202
- * // With useProp - cached until dependencies change, auto-unwrapped by props proxy
203
- * const memoizedData = useProp(() => expensiveComputation(list, filter))
201
+ * // With prop - cached until dependencies change, auto-unwrapped by props proxy
202
+ * const memoizedData = prop(() => expensiveComputation(list, filter))
204
203
  * <Child data={memoizedData} />
205
204
  * ```
206
205
  */
207
- export function useProp<T>(getter: () => T): PropGetter<T> {
206
+ export function prop<T>(getter: () => T): PropGetter<T> {
207
+ if (isPropGetter(getter)) {
208
+ return getter as PropGetter<T>
209
+ }
208
210
  // Wrap in prop so component props proxy auto-unwraps when passed down.
209
211
  return __fictProp(createMemo(getter)) as PropGetter<T>
210
212
  }
package/src/store.ts CHANGED
@@ -152,7 +152,13 @@ function getLastValue(target: any, prop: string | symbol) {
152
152
  */
153
153
  function reconcile(target: any, value: any) {
154
154
  if (target === value) return
155
- if (value === null || typeof value !== 'object') return // Should replace?
155
+ if (value === null || typeof value !== 'object') {
156
+ throw new Error(
157
+ `[Fict] Cannot replace store with primitive value: ${String(
158
+ value,
159
+ )}. setStore should return an object/array to merge.`,
160
+ )
161
+ }
156
162
 
157
163
  const realTarget = unwrap(target)
158
164
  const realValue = unwrap(value)
@@ -166,6 +172,11 @@ function reconcile(target: any, value: any) {
166
172
  target[key] = realValue[key] // Triggers proxy trap
167
173
  }
168
174
  }
175
+
176
+ // Fix array length if needed
177
+ if (Array.isArray(target) && target.length !== realValue.length) {
178
+ target.length = realValue.length
179
+ }
169
180
  }
170
181
 
171
182
  // ============================================================================
@@ -180,6 +191,29 @@ function reconcile(target: any, value: any) {
180
191
  export function createDiffingSignal<T extends object>(initialValue: T) {
181
192
  let currentValue = unwrap(initialValue)
182
193
  const signals = new Map<string | symbol, SignalAccessor<any>>()
194
+ let iterateSignal: SignalAccessor<number> | undefined
195
+
196
+ const getPropSignal = (prop: string | symbol) => {
197
+ let s = signals.get(prop)
198
+ if (!s) {
199
+ s = signal((currentValue as any)[prop])
200
+ signals.set(prop, s)
201
+ }
202
+ return s
203
+ }
204
+
205
+ const trackIterate = () => {
206
+ if (!iterateSignal) {
207
+ iterateSignal = signal(Reflect.ownKeys(currentValue).length)
208
+ }
209
+ iterateSignal()
210
+ }
211
+
212
+ const updateIterate = (value: T) => {
213
+ if (iterateSignal) {
214
+ iterateSignal(Reflect.ownKeys(value).length)
215
+ }
216
+ }
183
217
 
184
218
  // The stable proxy we return
185
219
  const proxy = new Proxy({} as T, {
@@ -188,17 +222,21 @@ export function createDiffingSignal<T extends object>(initialValue: T) {
188
222
  if (prop === TARGET) return currentValue
189
223
 
190
224
  // Subscribe to property
191
- let s = signals.get(prop)
192
- if (!s) {
193
- // Initialize signal with current property value
194
- s = signal((currentValue as any)[prop])
195
- signals.set(prop, s)
196
- }
225
+ const s = getPropSignal(prop)
197
226
  return s()
198
227
  },
199
228
  ownKeys() {
229
+ trackIterate()
200
230
  return Reflect.ownKeys(currentValue)
201
231
  },
232
+ has(target, prop) {
233
+ getPropSignal(prop)()
234
+ return Reflect.has(currentValue, prop)
235
+ },
236
+ getOwnPropertyDescriptor(target, prop) {
237
+ getPropSignal(prop)()
238
+ return Reflect.getOwnPropertyDescriptor(currentValue, prop)
239
+ },
202
240
  })
203
241
 
204
242
  const read = () => proxy
@@ -215,6 +253,7 @@ export function createDiffingSignal<T extends object>(initialValue: T) {
215
253
  const newVal = (next as any)[prop]
216
254
  s(newVal)
217
255
  }
256
+ updateIterate(next)
218
257
  return
219
258
  }
220
259
 
@@ -228,6 +267,7 @@ export function createDiffingSignal<T extends object>(initialValue: T) {
228
267
  s(newVal)
229
268
  }
230
269
  }
270
+ updateIterate(next)
231
271
 
232
272
  // Note: If new properties appeared that weren't tracked, we don't care
233
273
  // because no one is listening.
@@ -1,4 +1,4 @@
1
- import { createSignal } from './signal'
1
+ import { createSignal, untrack } from './signal'
2
2
 
3
3
  export interface VersionedSignalOptions<T> {
4
4
  equals?: (prev: T, next: T) => boolean
@@ -52,7 +52,7 @@ export function createVersionedSignal<T>(
52
52
  force: () => {
53
53
  bumpVersion()
54
54
  },
55
- peekVersion: () => version(),
56
- peekValue: () => value(),
55
+ peekVersion: () => untrack(() => version()),
56
+ peekValue: () => untrack(() => value()),
57
57
  }
58
58
  }