@ryupold/vode 1.7.4 → 1.8.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.
@@ -4,7 +4,7 @@ import { AnimatedPatch, DeepPartial, PatchableState, RenderPatch } from "./vode.
4
4
  * State context for type-safe access and manipulation of nested state paths
5
5
  * while still be able to access the parent state.
6
6
  */
7
- export interface StateContext<S extends PatchableState, SubState> extends SubStateContext<SubState> {
7
+ export interface StateContext<S extends PatchableState, SubState> extends SubContext<SubState> {
8
8
  /**
9
9
  * parent state
10
10
  * @see PatchableState<S>
@@ -15,7 +15,7 @@ export interface StateContext<S extends PatchableState, SubState> extends SubSta
15
15
  /**
16
16
  * State context for type-safe access and manipulation of nested sub-state values without knowledge of the parent state.
17
17
  */
18
- export interface SubStateContext<SubState> {
18
+ export interface SubContext<SubState> {
19
19
  /**
20
20
  * Reads the current value of the substate if it exists.
21
21
  *
@@ -42,14 +42,14 @@ export interface SubStateContext<SubState> {
42
42
 
43
43
  export type ProxyStateContext<S extends PatchableState, SubState> = StateContext<S, SubState> & {
44
44
  [K in keyof SubState]-?: SubState[K] extends object
45
- ? ProxyStateContext<S, SubState[K]>
46
- : StateContext<S, SubState[K]>
45
+ ? ProxyStateContext<S, SubState[K]>
46
+ : StateContext<S, SubState[K]>
47
47
  };
48
48
 
49
- export type ProxySubContext<SubState> = SubStateContext<SubState> & {
49
+ export type ProxySubContext<SubState> = SubContext<SubState> & {
50
50
  [K in keyof SubState]-?: SubState[K] extends object
51
- ? ProxySubContext<SubState[K]>
52
- : SubStateContext<SubState[K]>
51
+ ? ProxySubContext<SubState[K]>
52
+ : SubContext<SubState[K]>
53
53
  };
54
54
 
55
55
  /**
@@ -178,210 +178,3 @@ class ProxyStateContextImpl<S extends PatchableState, SubState>
178
178
  put(value: SubState | DeepPartial<SubState> | null | undefined): void { throw 'implemented in ctor' }
179
179
  patch(value: SubState | DeepPartial<SubState> | DeepPartial<SubState>[] | null | undefined): void { throw 'implemented in ctor' }
180
180
  }
181
-
182
- /**
183
- * Provides type-safe access to sub-state with fetch & store delegate functions.
184
- * Implementer is responsible for reading/writing the sub-state correctly.
185
- *
186
- * **When to use:**
187
- * - State structure is dynamic or complex
188
- * - You need custom logic for accessing nested state
189
- * - You want to encapsulate access logic outside of parent state
190
- *
191
- * **When to avoid:**
192
- * - Simple, static state structures
193
- * - You want automatic path-based access (use KeyStateContext instead)
194
- * - Learning vode for the first time (start simpler)
195
- */
196
- export class DelegateStateContext<S extends PatchableState, SubState>
197
- implements StateContext<S, SubState> {
198
- constructor(
199
- public readonly state: S,
200
-
201
- public readonly get: () => SubState | undefined,
202
-
203
- public readonly put: (value: SubState | DeepPartial<SubState> | undefined | null) => void,
204
-
205
- public readonly patch: (value: SubState | DeepPartial<SubState> | Array<DeepPartial<SubState>> | undefined | null) => void,
206
- ) {
207
- }
208
- }
209
-
210
-
211
- /**
212
- * @deprecated use proxy state context instead
213
- * Generates dot-notation path strings for all nested properties in an object type.
214
- *
215
- * @example
216
- * type User = { profile: { settings: { theme: string } } };
217
- * type Paths = KeyPath<User>; // "profile" | "profile.settings" | "profile.settings.theme"
218
- */
219
- export type KeyPath<ObjectType extends object> =
220
- { [Key in keyof ObjectType & (string | number)]:
221
- NonNullable<ObjectType[Key]> extends object
222
- ? `${Key}` | `${Key}.${KeyPath<NonNullable<ObjectType[Key]>>}`
223
- : `${Key}`
224
- }[keyof ObjectType & (string | number)];
225
-
226
- /**
227
- * @deprecated use proxy state context instead
228
- * Extracts the value type at a given dot-notation path in an object type.
229
- *
230
- * @example
231
- * type User = { profile: { settings: { theme: string } } };
232
- * type Theme = PathValue<User, "profile.settings.theme">; // string
233
- */
234
- export type PathValue<T, P extends string> =
235
- P extends `${infer Key}.${infer Rest}`
236
- ? Key extends keyof T
237
- ? PathValue<NonNullable<T[Key]>, Rest>
238
- : never
239
- : P extends keyof T
240
- ? T[P]
241
- : never;
242
-
243
- /**
244
- * @deprecated use proxy state context instead
245
- * Maps valid paths in an object type to paths that resolve to a specific substate type.
246
- * Used for type-safe path constraints in StateContext.
247
- * Ensures exact type matching (not just compatibility).
248
- *
249
- * @example
250
- * type User = { profile: { name: string, settings: { theme: string } } };
251
- * type SettingsPaths = KeyToSubState<User, { theme: string }>; // "profile.settings"
252
- * type InvalidPath = KeyToSubState<User, { theme: string }, "profile">; // never (type mismatch)
253
- */
254
- export type KeyToSubState<S extends object, Sub, K = KeyPath<S>> =
255
- K extends KeyPath<S>
256
- ? [NonNullable<PathValue<S, K>>] extends [Sub]
257
- ? [Sub] extends [NonNullable<PathValue<S, K>>]
258
- ? K
259
- : never
260
- : never
261
- : never;
262
-
263
- /**
264
- * @deprecated use {context()} instead
265
- * Provides type-safe access to deeply nested state with path-based operations.
266
- *
267
- * **When to use:**
268
- * - State is deeply nested
269
- * - Multiple components access the same nested path
270
- * - You need type safety for nested updates
271
- *
272
- * **When to avoid:**
273
- * - Shallow state structures for main state
274
- * - State structure changes frequently
275
- * - Learning vode for the first time (start simpler)
276
- *
277
- * @template S - The root state type (must extend PatchableState)
278
- * @template SubState - The type of the nested state being accessed
279
- *
280
- * @example
281
- * ```typescript
282
- * const state = createState({
283
- * user: {
284
- * profile: {
285
- * settings: { theme: 'dark', lang: 'en' }
286
- * }
287
- * }
288
- * });
289
- * app(element, state, (s) => [DIV]);
290
- *
291
- * // Create a context for the nested settings
292
- * const settingsCtx = new KeyStateContext(state, 'user.profile.settings');
293
- *
294
- * // Read current value
295
- * const settings = settingsCtx.get(); // { theme: 'dark', lang: 'en' }
296
- *
297
- * // Update and trigger render
298
- * settingsCtx.patch({ theme: 'light' });
299
- *
300
- * // Update without render (silent mutation)
301
- * settingsCtx.put({ lang: 'de' });
302
- * state.patch({}); // trigger render manually later
303
- * ```
304
- */
305
- export class KeyStateContext<S extends PatchableState, SubState>
306
- implements StateContext<S, SubState> {
307
- private readonly keys: string[];
308
-
309
- constructor(
310
- public readonly state: S,
311
- public readonly path: KeyToSubState<S, SubState>
312
- ) {
313
- this.keys = path.split('.');
314
- }
315
-
316
- get(): SubState | undefined {
317
- const keys = this.keys;
318
- let raw = this.state ? (<any>this.state)[keys[0]] : undefined;
319
- for (let i = 1; i < keys.length && !!raw; i++) {
320
- raw = raw[keys[i]];
321
- }
322
- return raw;
323
- }
324
-
325
- put(value: SubState | DeepPartial<SubState> | undefined | null) {
326
- this.putDeep(value, this.state);
327
- }
328
-
329
- patch(value: SubState | DeepPartial<SubState> | Array<DeepPartial<SubState>> | undefined | null) {
330
- if (Array.isArray(value)) {
331
- const animation: AnimatedPatch<S> = [];
332
- for (const v of value) {
333
- animation.push(this.createPatch(v));
334
- }
335
- this.state.patch(animation);
336
- }
337
- else {
338
- this.state.patch(this.createPatch(value as DeepPartial<SubState>));
339
- }
340
- }
341
-
342
- /**
343
- * Creates a render-patch for the parent state by setting a nested sub-state value while creating necessary structure.
344
- *
345
- * @example
346
- * ```typescript
347
- * const ctx = new KeyStateContext(state, 'user.profile.settings');
348
- * const patch = ctx.createPatch({ theme: 'light' });
349
- * // patch is { user: { profile: { settings: { theme: 'light' } } } }
350
- * ```
351
- *
352
- * @param value
353
- * @returns {{key-path}:{...: value}} render-patch for the parent state
354
- */
355
- createPatch(value: SubState | DeepPartial<SubState> | undefined | null): RenderPatch<S> {
356
- const renderPatch: DeepPartial<S> = {};
357
- this.putDeep(value, renderPatch);
358
- return renderPatch;
359
- }
360
-
361
- private putDeep(value: SubState | DeepPartial<SubState> | undefined | null, target: S | DeepPartial<S>) {
362
- const keys = this.keys;
363
- if (keys.length > 1) {
364
- let i = 0;
365
- let raw = (<any>target)[keys[i]];
366
- if (typeof raw !== "object" || raw === null) {
367
- (<any>target)[keys[i]] = raw = {};
368
- }
369
- for (i = 1; i < keys.length - 1; i++) {
370
- const p = raw;
371
- raw = raw[keys[i]];
372
- if (typeof raw !== "object" || raw === null) {
373
- p[keys[i]] = raw = {};
374
- }
375
- }
376
- raw[keys[i]] = value;
377
- } else {
378
- if (typeof (<any>target)[keys[0]] === "object" && typeof value === "object")
379
- Object.assign((<any>target)[keys[0]], value);
380
- else
381
- (<any>target)[keys[0]] = value;
382
- }
383
- }
384
- }
385
-
386
- /** @deprecated Helper to unwrap undefined/null from optional properties */
387
- type NonNullable<T> = T extends null | undefined ? never : T;
package/tsconfig.json CHANGED
@@ -4,25 +4,29 @@
4
4
  "module": "preserve",
5
5
  "moduleResolution": "bundler",
6
6
  "rootDir": ".",
7
- "composite": true,
8
- "removeComments": true,
7
+ "outDir": "./dist",
9
8
  "declaration": true,
10
9
  "declarationDir": "./dist",
10
+ "declarationMap": true,
11
+ "sourceMap": true,
11
12
  "strict": true,
12
- "allowSyntheticDefaultImports": true,
13
- "strictBindCallApply": true,
14
- "strictFunctionTypes": true,
15
- "strictPropertyInitialization": true,
16
- "strictNullChecks": true,
17
- "allowJs": false,
18
- "skipLibCheck": true,
13
+ "noUncheckedSideEffectImports": true,
19
14
  "noImplicitReturns": true,
20
15
  "noFallthroughCasesInSwitch": true,
21
- "downlevelIteration": true,
22
- "isolatedModules": true
16
+ "forceConsistentCasingInFileNames": true,
17
+ "esModuleInterop": true,
18
+ "allowSyntheticDefaultImports": true,
19
+ "isolatedModules": true,
20
+ "skipLibCheck": true,
21
+ "composite": true,
22
+ "tsBuildInfoFile": "./dist/.tsbuildinfo"
23
23
  },
24
24
  "include": [
25
- "./index.ts",
26
- "./src/*.ts",
25
+ "index.ts",
26
+ "src/**/*"
27
+ ],
28
+ "exclude": [
29
+ "node_modules",
30
+ "dist"
27
31
  ]
28
32
  }