@hamak/ui-store 0.6.0 → 0.7.2

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 (90) hide show
  1. package/dist/api/api/index.d.ts +3 -3
  2. package/dist/api/api/index.js +3 -3
  3. package/dist/api/api/middleware-registry.d.ts +1 -1
  4. package/dist/api/api/reducer-registry.d.ts +1 -1
  5. package/dist/api/api/store-manager.d.ts +3 -3
  6. package/dist/api/autosave/autosave-action-factory.d.ts +2 -2
  7. package/dist/api/autosave/autosave-action-factory.js +1 -1
  8. package/dist/api/autosave/autosave-action-types.d.ts +1 -1
  9. package/dist/api/autosave/index.d.ts +3 -3
  10. package/dist/api/autosave/index.js +3 -3
  11. package/dist/api/fs/index.d.ts +2 -2
  12. package/dist/api/fs/index.js +2 -2
  13. package/dist/api/index.d.ts +5 -5
  14. package/dist/api/index.js +5 -5
  15. package/dist/api/tokens/index.d.ts +1 -1
  16. package/dist/api/tokens/index.js +1 -1
  17. package/dist/api/tokens/service-tokens.d.ts +8 -0
  18. package/dist/api/tokens/service-tokens.d.ts.map +1 -1
  19. package/dist/api/tokens/service-tokens.js +11 -0
  20. package/dist/api/types/extension-types.d.ts +1 -1
  21. package/dist/api/types/index.d.ts +4 -4
  22. package/dist/api/types/index.js +4 -4
  23. package/dist/api/types/store-types.d.ts +20 -12
  24. package/dist/api/types/store-types.d.ts.map +1 -1
  25. package/dist/impl/autosave/autosave-config-resolver.d.ts +1 -1
  26. package/dist/impl/autosave/autosave-config-resolver.js +1 -1
  27. package/dist/impl/autosave/autosave-middleware.d.ts +2 -2
  28. package/dist/impl/autosave/autosave-middleware.js +2 -2
  29. package/dist/impl/autosave/autosave-registry.d.ts +1 -1
  30. package/dist/impl/autosave/autosave-sync-middleware.d.ts +1 -1
  31. package/dist/impl/autosave/autosave-sync-middleware.js +1 -1
  32. package/dist/impl/autosave/index.d.ts +4 -4
  33. package/dist/impl/autosave/index.js +4 -4
  34. package/dist/impl/core/index.d.ts +3 -3
  35. package/dist/impl/core/index.js +3 -3
  36. package/dist/impl/core/middleware-registry.d.ts +1 -1
  37. package/dist/impl/core/reducer-registry.d.ts +1 -1
  38. package/dist/impl/core/store-manager.d.ts +16 -4
  39. package/dist/impl/core/store-manager.d.ts.map +1 -1
  40. package/dist/impl/core/store-manager.js +19 -2
  41. package/dist/impl/extensions/store-extensions.d.ts +1 -1
  42. package/dist/impl/fs/commands/fs-commands.d.ts +3 -3
  43. package/dist/impl/fs/commands/fs-commands.d.ts.map +1 -1
  44. package/dist/impl/fs/commands/fs-commands.js +35 -9
  45. package/dist/impl/fs/commands/structure-commands.d.ts +25 -0
  46. package/dist/impl/fs/commands/structure-commands.d.ts.map +1 -1
  47. package/dist/impl/fs/core/fs-adapter.d.ts +3 -3
  48. package/dist/impl/fs/core/fs-adapter.js +2 -2
  49. package/dist/impl/fs/core/fs-facade.d.ts +2 -2
  50. package/dist/impl/fs/index.d.ts +6 -6
  51. package/dist/impl/fs/index.js +6 -6
  52. package/dist/impl/fs/utils/data-updater.d.ts +1 -1
  53. package/dist/impl/index.d.ts +8 -7
  54. package/dist/impl/index.d.ts.map +1 -1
  55. package/dist/impl/index.js +8 -7
  56. package/dist/impl/middleware/index.d.ts +2 -2
  57. package/dist/impl/middleware/index.js +2 -2
  58. package/dist/impl/persistence/index.d.ts +9 -0
  59. package/dist/impl/persistence/index.d.ts.map +1 -0
  60. package/dist/impl/persistence/index.js +8 -0
  61. package/dist/impl/persistence/persistence-filter.d.ts +11 -0
  62. package/dist/impl/persistence/persistence-filter.d.ts.map +1 -0
  63. package/dist/impl/persistence/persistence-filter.js +24 -0
  64. package/dist/impl/persistence/persistence-resolver.d.ts +9 -0
  65. package/dist/impl/persistence/persistence-resolver.d.ts.map +1 -0
  66. package/dist/impl/persistence/persistence-resolver.js +22 -0
  67. package/dist/impl/persistence/persistence-wiring.d.ts +29 -0
  68. package/dist/impl/persistence/persistence-wiring.d.ts.map +1 -0
  69. package/dist/impl/persistence/persistence-wiring.js +69 -0
  70. package/dist/impl/persistence/web-storage-persistence-provider.d.ts +46 -0
  71. package/dist/impl/persistence/web-storage-persistence-provider.d.ts.map +1 -0
  72. package/dist/impl/persistence/web-storage-persistence-provider.js +155 -0
  73. package/dist/impl/plugin/index.d.ts +1 -1
  74. package/dist/impl/plugin/index.js +1 -1
  75. package/dist/impl/plugin/store-plugin-factory.d.ts +13 -1
  76. package/dist/impl/plugin/store-plugin-factory.d.ts.map +1 -1
  77. package/dist/impl/plugin/store-plugin-factory.js +60 -5
  78. package/dist/index.d.ts +1 -1
  79. package/dist/index.js +1 -1
  80. package/dist/spi/autosave/i-autosave-registry.d.ts +1 -1
  81. package/dist/spi/autosave/index.d.ts +2 -2
  82. package/dist/spi/autosave/index.js +2 -2
  83. package/dist/spi/index.d.ts +4 -4
  84. package/dist/spi/index.js +4 -4
  85. package/dist/spi/middleware/index.d.ts +1 -1
  86. package/dist/spi/middleware/index.js +1 -1
  87. package/dist/spi/middleware/middleware-provider.d.ts +1 -1
  88. package/dist/spi/persistence/index.d.ts +1 -1
  89. package/dist/spi/persistence/index.js +1 -1
  90. package/package.json +8 -4
@@ -1,8 +1,8 @@
1
- import { fileSystemNodeInitialState } from '../../../api';
1
+ import { fileSystemNodeInitialState } from '../../../api/index.js';
2
2
  import { pathSteps } from '@hamak/shared-utils';
3
- import { DataUpdater } from '../utils/data-updater';
3
+ import { DataUpdater } from '../utils/data-updater.js';
4
4
  import { produce, current, isDraft, original } from 'immer';
5
- import { deepEqual } from '../utils/deep-equal';
5
+ import { deepEqual } from '../utils/deep-equal.js';
6
6
  // Re-export path utilities from shared-utils for internal use
7
7
  export { pathSteps, parentPathSteps } from '@hamak/shared-utils';
8
8
  /**
@@ -258,6 +258,20 @@ export class FileSystemCommandHandler {
258
258
  return isDraft(o) ? original(o) : o;
259
259
  }
260
260
  }
261
+ /**
262
+ * Build an empty-object prototype stack mirroring `itinerary` depth-for-depth,
263
+ * with `{}` at every level. Passed to `DataUpdater` so that missing object
264
+ * intermediates along the path are materialized instead of silently no-op'ing.
265
+ *
266
+ * Object-only: it cannot synthesize array intermediates — callers needing those
267
+ * must supply an explicit `prototypes` stack (see `ensurePath` JSDoc).
268
+ */
269
+ function buildEmptyObjectProtos(itinerary) {
270
+ if (itinerary === undefined) {
271
+ return undefined;
272
+ }
273
+ return { value: {}, parent: buildEmptyObjectProtos(itinerary.parent) };
274
+ }
261
275
  /**
262
276
  * File content command handler - handles structure commands on file content
263
277
  */
@@ -308,25 +322,37 @@ export class FileContentCommandHandler {
308
322
  this.updater.executeDelete(content, itinerary);
309
323
  }
310
324
  executeSet(file, command) {
311
- const { itinerary, value } = command;
325
+ const { itinerary, value, prototypes } = command;
312
326
  if (itinerary === undefined) {
313
327
  file.content = value;
314
328
  }
315
329
  const { content } = file;
316
- this.updater.executeSet(content, itinerary, value);
330
+ // executeSet navigates the itinerary's parent, so the prototype stack must
331
+ // mirror `itinerary.parent`.
332
+ const effectivePrototypes = prototypes
333
+ ?? (command.ensurePath ? buildEmptyObjectProtos(itinerary?.parent) : undefined);
334
+ this.updater.executeSet(content, itinerary, value, effectivePrototypes);
317
335
  }
318
336
  executeAdd(file, command) {
319
- const { itinerary, value } = command;
337
+ const { itinerary, value, prototypes } = command;
320
338
  const { content } = file;
321
- this.updater.executeAdd(content, itinerary, value);
339
+ // executeAdd navigates the full itinerary (the target container), so the
340
+ // prototype stack mirrors `itinerary`.
341
+ const effectivePrototypes = prototypes
342
+ ?? (command.ensurePath ? buildEmptyObjectProtos(itinerary) : undefined);
343
+ this.updater.executeAdd(content, itinerary, value, effectivePrototypes);
322
344
  }
323
345
  executeInsert(file, command) {
324
- const { itinerary, value } = command;
346
+ const { itinerary, value, prototypes } = command;
325
347
  if (itinerary === undefined) {
326
348
  return;
327
349
  }
328
350
  const { content } = file;
329
- this.updater.executeInsert(content, itinerary, value);
351
+ // executeInsert navigates the itinerary's parent, so the prototype stack
352
+ // mirrors `itinerary.parent`.
353
+ const effectivePrototypes = prototypes
354
+ ?? (command.ensurePath ? buildEmptyObjectProtos(itinerary.parent) : undefined);
355
+ this.updater.executeInsert(content, itinerary, value, effectivePrototypes);
330
356
  }
331
357
  /**
332
358
  * Executes a batch of commands, rebasing each on previous mutations.
@@ -17,7 +17,32 @@ export interface UnitaryStructureNodeCommandBase extends StructureNodeCommandBas
17
17
  */
18
18
  export interface StructureNodeUpsertCommandBase extends UnitaryStructureNodeCommandBase {
19
19
  name: "add-at" | "set-at" | 'insert-at';
20
+ /**
21
+ * Explicit prototype stack used to materialize missing intermediates along
22
+ * the itinerary's parent. Each {@link StackElement} level mirrors one
23
+ * itinerary step (deepest first) and its `value` is the node inserted when
24
+ * that step is absent. Use this when the missing intermediates include
25
+ * arrays, or when they need non-empty seed values.
26
+ *
27
+ * Takes precedence over {@link ensurePath} when both are supplied.
28
+ */
20
29
  prototypes?: StackElement<any>;
30
+ /**
31
+ * Convenience flag for the common "create missing object intermediates"
32
+ * case: when true (and `prototypes` is absent), the handler synthesizes an
33
+ * empty-object (`{}`) prototype stack of matching depth, so deep writes into
34
+ * nested-object documents materialize their parent path instead of silently
35
+ * no-op'ing when the parent is absent.
36
+ *
37
+ * Object-only: each synthesized intermediate is `{}`. For **absent-array**
38
+ * intermediates this is insufficient — supply `prototypes` explicitly with
39
+ * an array (`[]`) at the relevant level.
40
+ *
41
+ * When false/absent, today's silent-no-op semantics are preserved (no
42
+ * intermediates are created and a deep write into an absent parent does
43
+ * nothing).
44
+ */
45
+ ensurePath?: boolean;
21
46
  }
22
47
  /**
23
48
  * Add a value at the specified itinerary
@@ -1 +1 @@
1
- {"version":3,"file":"structure-commands.d.ts","sourceRoot":"","sources":["../../../../src/impl/fs/commands/structure-commands.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAE9D;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,WAAW,GAAG,WAAW,GAAG,cAAc,CAAA;CACvE;AAED;;GAEG;AACH,MAAM,WAAW,+BAAgC,SAAQ,wBAAwB;IAC/E,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,WAAW,GAAG,WAAW,CAAA;IACrD,SAAS,EAAE,SAAS,CAAA;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,8BAA+B,SAAQ,+BAA+B;IACrF,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,WAAW,CAAA;IACvC,UAAU,CAAC,EAAG,YAAY,CAAC,GAAG,CAAC,CAAA;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,uBAAwB,SAAQ,8BAA8B;IAC7E,IAAI,EAAE,QAAQ,CAAA;IACd,KAAK,EAAE,GAAG,CAAA;CACX;AAED;;GAEG;AACH,MAAM,WAAW,0BAA2B,SAAQ,8BAA8B;IAChF,IAAI,EAAE,WAAW,CAAA;IACjB,KAAK,EAAE,GAAG,CAAA;CACX;AAED;;GAEG;AACH,MAAM,WAAW,uBAAwB,SAAQ,8BAA8B;IAC7E,IAAI,EAAE,QAAQ,CAAA;IACd,KAAK,EAAE,GAAG,CAAA;CACX;AAED;;GAEG;AACH,MAAM,WAAW,0BAA2B,SAAQ,+BAA+B;IACjF,IAAI,EAAE,WAAW,CAAA;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,+BAAgC,SAAQ,wBAAwB;IAC/E,IAAI,EAAE,cAAc,CAAA;IACpB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,QAAQ,EAAE,2BAA2B,EAAE,CAAA;CACxC;AAED;;GAEG;AACH,MAAM,MAAM,2BAA2B,GACnC,uBAAuB,GACvB,0BAA0B,GAC1B,uBAAuB,GACvB,0BAA0B,CAAA;AAE9B;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,2BAA2B,GAAG,+BAA+B,CAAA;AAEhG;;GAEG;AACH,qBAAa,0BAA0B;IACrC,OAAO,CAAC,SAAS,EAAG,SAAS,EAAE,KAAK,EAAG,GAAG,EAAE,UAAU,CAAC,EAAE,YAAY,CAAC,GAAG,CAAC,GAAI,uBAAuB;IAIrG,UAAU,CAAC,SAAS,EAAG,SAAS,EAAE,KAAK,EAAG,GAAG,EAAE,UAAU,CAAC,EAAE,YAAY,CAAC,GAAG,CAAC,GAAI,0BAA0B;IAI3G,OAAO,CAAC,SAAS,EAAG,SAAS,EAAE,KAAK,EAAG,GAAG,EAAE,UAAU,CAAC,EAAE,YAAY,CAAC,GAAG,CAAC,GAAI,uBAAuB;IAIrG,UAAU,CAAC,SAAS,EAAG,SAAS,GAAI,0BAA0B;IAI9D,WAAW,CAAC,QAAQ,EAAG,2BAA2B,EAAE,EAAE,UAAU,UAAQ,GAAI,+BAA+B;CAG5G;AAED,eAAO,MAAM,OAAO,4BAAmC,CAAA"}
1
+ {"version":3,"file":"structure-commands.d.ts","sourceRoot":"","sources":["../../../../src/impl/fs/commands/structure-commands.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAE9D;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,WAAW,GAAG,WAAW,GAAG,cAAc,CAAA;CACvE;AAED;;GAEG;AACH,MAAM,WAAW,+BAAgC,SAAQ,wBAAwB;IAC/E,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,WAAW,GAAG,WAAW,CAAA;IACrD,SAAS,EAAE,SAAS,CAAA;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,8BAA+B,SAAQ,+BAA+B;IACrF,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,WAAW,CAAA;IACvC;;;;;;;;OAQG;IACH,UAAU,CAAC,EAAG,YAAY,CAAC,GAAG,CAAC,CAAA;IAC/B;;;;;;;;;;;;;;OAcG;IACH,UAAU,CAAC,EAAG,OAAO,CAAA;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,uBAAwB,SAAQ,8BAA8B;IAC7E,IAAI,EAAE,QAAQ,CAAA;IACd,KAAK,EAAE,GAAG,CAAA;CACX;AAED;;GAEG;AACH,MAAM,WAAW,0BAA2B,SAAQ,8BAA8B;IAChF,IAAI,EAAE,WAAW,CAAA;IACjB,KAAK,EAAE,GAAG,CAAA;CACX;AAED;;GAEG;AACH,MAAM,WAAW,uBAAwB,SAAQ,8BAA8B;IAC7E,IAAI,EAAE,QAAQ,CAAA;IACd,KAAK,EAAE,GAAG,CAAA;CACX;AAED;;GAEG;AACH,MAAM,WAAW,0BAA2B,SAAQ,+BAA+B;IACjF,IAAI,EAAE,WAAW,CAAA;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,+BAAgC,SAAQ,wBAAwB;IAC/E,IAAI,EAAE,cAAc,CAAA;IACpB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,QAAQ,EAAE,2BAA2B,EAAE,CAAA;CACxC;AAED;;GAEG;AACH,MAAM,MAAM,2BAA2B,GACnC,uBAAuB,GACvB,0BAA0B,GAC1B,uBAAuB,GACvB,0BAA0B,CAAA;AAE9B;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,2BAA2B,GAAG,+BAA+B,CAAA;AAEhG;;GAEG;AACH,qBAAa,0BAA0B;IACrC,OAAO,CAAC,SAAS,EAAG,SAAS,EAAE,KAAK,EAAG,GAAG,EAAE,UAAU,CAAC,EAAE,YAAY,CAAC,GAAG,CAAC,GAAI,uBAAuB;IAIrG,UAAU,CAAC,SAAS,EAAG,SAAS,EAAE,KAAK,EAAG,GAAG,EAAE,UAAU,CAAC,EAAE,YAAY,CAAC,GAAG,CAAC,GAAI,0BAA0B;IAI3G,OAAO,CAAC,SAAS,EAAG,SAAS,EAAE,KAAK,EAAG,GAAG,EAAE,UAAU,CAAC,EAAE,YAAY,CAAC,GAAG,CAAC,GAAI,uBAAuB;IAIrG,UAAU,CAAC,SAAS,EAAG,SAAS,GAAI,0BAA0B;IAI9D,WAAW,CAAC,QAAQ,EAAG,2BAA2B,EAAE,EAAE,UAAU,UAAQ,GAAI,+BAA+B;CAG5G;AAED,eAAO,MAAM,OAAO,4BAAmC,CAAA"}
@@ -1,7 +1,7 @@
1
1
  import { Action } from '@reduxjs/toolkit';
2
- import { FileSystemCommandHandler } from '../commands/fs-commands';
3
- import { FileSystemNode, FileSystemState, FileContentSchema, FileSystemNodeActionParams, FileSystemNodeAction, Selector } from '../../../api';
4
- import { StructureNodeCommand } from '../commands/structure-commands';
2
+ import { FileSystemCommandHandler } from '../commands/fs-commands.js';
3
+ import { FileSystemNode, FileSystemState, FileContentSchema, FileSystemNodeActionParams, FileSystemNodeAction, Selector } from '../../../api/index.js';
4
+ import { StructureNodeCommand } from '../commands/structure-commands.js';
5
5
  /**
6
6
  * Factory function to create a FileSystemAdapter
7
7
  */
@@ -1,6 +1,6 @@
1
1
  import { createSelector } from '@reduxjs/toolkit';
2
- import { FileSystemCommandHandler, pathSteps } from '../commands/fs-commands';
3
- import { fileSystemNodeInitialState } from '../../../api';
2
+ import { FileSystemCommandHandler, pathSteps } from '../commands/fs-commands.js';
3
+ import { fileSystemNodeInitialState } from '../../../api/index.js';
4
4
  /**
5
5
  * Factory function to create a FileSystemAdapter
6
6
  */
@@ -1,5 +1,5 @@
1
- import { FileNode, FileSystemNode, FileSystemState, Selector } from '../../../api';
2
- import { FileSystemAdapter, FileSystemNodeActions } from './fs-adapter';
1
+ import { FileNode, FileSystemNode, FileSystemState, Selector } from '../../../api/index.js';
2
+ import { FileSystemAdapter, FileSystemNodeActions } from './fs-adapter.js';
3
3
  import { Path, Pathway, PathwayResolver, RelativePathwayResolver } from '@hamak/shared-utils';
4
4
  /**
5
5
  * Selector that returns a FileSystemNode generally the root of the Filesystem Overlay
@@ -1,8 +1,8 @@
1
- export * from './core/fs-adapter';
2
- export * from './core/fs-facade';
3
- export * from './commands/fs-commands';
4
- export * from './commands/structure-commands';
1
+ export * from './core/fs-adapter.js';
2
+ export * from './core/fs-facade.js';
3
+ export * from './commands/fs-commands.js';
4
+ export * from './commands/structure-commands.js';
5
5
  export { pathSteps, parentPathSteps } from '@hamak/shared-utils';
6
- export * from './utils/data-updater';
7
- export * from './utils/deep-equal';
6
+ export * from './utils/data-updater.js';
7
+ export * from './utils/deep-equal.js';
8
8
  //# sourceMappingURL=index.d.ts.map
@@ -1,11 +1,11 @@
1
1
  // Core filesystem functionality
2
- export * from './core/fs-adapter';
3
- export * from './core/fs-facade';
2
+ export * from './core/fs-adapter.js';
3
+ export * from './core/fs-facade.js';
4
4
  // Commands
5
- export * from './commands/fs-commands';
6
- export * from './commands/structure-commands';
5
+ export * from './commands/fs-commands.js';
6
+ export * from './commands/structure-commands.js';
7
7
  // Re-export path utilities from shared-utils for backward compatibility
8
8
  export { pathSteps, parentPathSteps } from '@hamak/shared-utils';
9
9
  // Utilities
10
- export * from './utils/data-updater';
11
- export * from './utils/deep-equal';
10
+ export * from './utils/data-updater.js';
11
+ export * from './utils/deep-equal.js';
@@ -1,5 +1,5 @@
1
1
  import { Itinerary, StackElement } from '@hamak/shared-utils';
2
- import { UnitaryStructureNodeCommand, BatchUpdateStructureNodeCommand } from '../commands/structure-commands';
2
+ import { UnitaryStructureNodeCommand, BatchUpdateStructureNodeCommand } from '../commands/structure-commands.js';
3
3
  /**
4
4
  * DataUpdater class handles operations on data structures using itineraries
5
5
  */
@@ -2,11 +2,12 @@
2
2
  * UI Store Implementation
3
3
  * Concrete implementations of Redux store management
4
4
  */
5
- export * from '../api';
6
- export * from '../spi';
7
- export * from './core';
8
- export * from './middleware';
9
- export * from './plugin';
10
- export * from './fs';
11
- export * from './autosave';
5
+ export * from '../api/index.js';
6
+ export * from '../spi/index.js';
7
+ export * from './core/index.js';
8
+ export * from './middleware/index.js';
9
+ export * from './plugin/index.js';
10
+ export * from './fs/index.js';
11
+ export * from './autosave/index.js';
12
+ export * from './persistence/index.js';
12
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/impl/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,cAAc,QAAQ,CAAC;AACvB,cAAc,QAAQ,CAAC;AAEvB,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,MAAM,CAAC;AACrB,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/impl/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,cAAc,QAAQ,CAAC;AACvB,cAAc,QAAQ,CAAC;AAEvB,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,MAAM,CAAC;AACrB,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC"}
@@ -3,10 +3,11 @@
3
3
  * Concrete implementations of Redux store management
4
4
  */
5
5
  // Re-export API and SPI for convenience
6
- export * from '../api';
7
- export * from '../spi';
8
- export * from './core';
9
- export * from './middleware';
10
- export * from './plugin';
11
- export * from './fs';
12
- export * from './autosave';
6
+ export * from '../api/index.js';
7
+ export * from '../spi/index.js';
8
+ export * from './core/index.js';
9
+ export * from './middleware/index.js';
10
+ export * from './plugin/index.js';
11
+ export * from './fs/index.js';
12
+ export * from './autosave/index.js';
13
+ export * from './persistence/index.js';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Middleware Export
3
3
  */
4
- export * from './event-bridge-middleware';
5
- export * from './logger-middleware';
4
+ export * from './event-bridge-middleware.js';
5
+ export * from './logger-middleware.js';
6
6
  //# sourceMappingURL=index.d.ts.map
@@ -1,5 +1,5 @@
1
1
  /**
2
2
  * Middleware Export
3
3
  */
4
- export * from './event-bridge-middleware';
5
- export * from './logger-middleware';
4
+ export * from './event-bridge-middleware.js';
5
+ export * from './logger-middleware.js';
@@ -0,0 +1,9 @@
1
+ /**
2
+ * State persistence implementations and wiring.
3
+ * See amah/app-framework#30.
4
+ */
5
+ export * from './web-storage-persistence-provider.js';
6
+ export * from './persistence-filter.js';
7
+ export * from './persistence-resolver.js';
8
+ export * from './persistence-wiring.js';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/impl/persistence/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,oCAAoC,CAAC;AACnD,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * State persistence implementations and wiring.
3
+ * See amah/app-framework#30.
4
+ */
5
+ export * from './web-storage-persistence-provider.js';
6
+ export * from './persistence-filter.js';
7
+ export * from './persistence-resolver.js';
8
+ export * from './persistence-wiring.js';
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Slice-level whitelist/blacklist filtering for persisted state.
3
+ *
4
+ * Persistence operates at the top-level reducer key ("slice") granularity:
5
+ * a whitelist keeps only the named slices; a blacklist drops named slices.
6
+ * The whitelist is applied first, then the blacklist. See amah/app-framework#30.
7
+ */
8
+ import type { StorePersistenceConfig } from '../../api/index.js';
9
+ export type SliceSelection = Pick<StorePersistenceConfig, 'whitelist' | 'blacklist'>;
10
+ export declare function pickPersistableState(state: Record<string, unknown> | null | undefined, selection: SliceSelection): Record<string, unknown>;
11
+ //# sourceMappingURL=persistence-filter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persistence-filter.d.ts","sourceRoot":"","sources":["../../../src/impl/persistence/persistence-filter.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AAExD,MAAM,MAAM,cAAc,GAAG,IAAI,CAC/B,sBAAsB,EACtB,WAAW,GAAG,WAAW,CAC1B,CAAC;AAEF,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,EACjD,SAAS,EAAE,cAAc,GACxB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAgBzB"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Slice-level whitelist/blacklist filtering for persisted state.
3
+ *
4
+ * Persistence operates at the top-level reducer key ("slice") granularity:
5
+ * a whitelist keeps only the named slices; a blacklist drops named slices.
6
+ * The whitelist is applied first, then the blacklist. See amah/app-framework#30.
7
+ */
8
+ export function pickPersistableState(state, selection) {
9
+ if (!state || typeof state !== 'object')
10
+ return {};
11
+ const { whitelist, blacklist } = selection;
12
+ const result = {};
13
+ for (const key of Object.keys(state)) {
14
+ // A set whitelist constrains to its members (an empty whitelist persists
15
+ // nothing); an unset whitelist imposes no constraint. The blacklist is
16
+ // then applied on top.
17
+ if (whitelist && !whitelist.includes(key))
18
+ continue;
19
+ if (blacklist && blacklist.includes(key))
20
+ continue;
21
+ result[key] = state[key];
22
+ }
23
+ return result;
24
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Resolves a concrete `IPersistenceProvider` from a `StorePersistenceConfig`.
3
+ * Used to build the default provider when a consumer enables persistence but
4
+ * does not mount a custom one. See amah/app-framework#30.
5
+ */
6
+ import type { StorePersistenceConfig } from '../../api/index.js';
7
+ import type { IPersistenceProvider } from '../../spi/index.js';
8
+ export declare function resolvePersistenceProvider(persistence: StorePersistenceConfig): IPersistenceProvider;
9
+ //# sourceMappingURL=persistence-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persistence-resolver.d.ts","sourceRoot":"","sources":["../../../src/impl/persistence/persistence-resolver.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AACxD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAMtD,wBAAgB,0BAA0B,CACxC,WAAW,EAAE,sBAAsB,GAClC,oBAAoB,CAiBtB"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Resolves a concrete `IPersistenceProvider` from a `StorePersistenceConfig`.
3
+ * Used to build the default provider when a consumer enables persistence but
4
+ * does not mount a custom one. See amah/app-framework#30.
5
+ */
6
+ import { createLocalStoragePersistenceProvider, createSessionStoragePersistenceProvider, } from './web-storage-persistence-provider.js';
7
+ export function resolvePersistenceProvider(persistence) {
8
+ const storage = persistence.storage ?? 'localStorage';
9
+ switch (storage) {
10
+ case 'sessionStorage':
11
+ return createSessionStoragePersistenceProvider();
12
+ case 'indexedDB':
13
+ // Not yet implemented — fall back to localStorage so enabling it is not a
14
+ // silent no-op. Tracked as a follow-up in #30.
15
+ console.warn("[persistence] storage 'indexedDB' is not implemented yet; " +
16
+ 'falling back to localStorage.');
17
+ return createLocalStoragePersistenceProvider();
18
+ case 'localStorage':
19
+ default:
20
+ return createLocalStoragePersistenceProvider();
21
+ }
22
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Load/save wiring that connects a Redux store to an `IPersistenceProvider`.
3
+ *
4
+ * - `loadPersistedState` rehydrates the whitelisted slice on boot (async,
5
+ * called before store creation so the result can seed `preloadedState`).
6
+ * - `attachPersistenceSave` subscribes to the store and writes the whitelisted
7
+ * slice back, debounced, on every change. See amah/app-framework#30.
8
+ */
9
+ import type { Store } from 'redux';
10
+ import type { StorePersistenceConfig } from '../../api/index.js';
11
+ import type { IPersistenceProvider } from '../../spi/index.js';
12
+ /** Default storage key when `persistence.key` is not set. */
13
+ export declare const DEFAULT_PERSISTENCE_KEY = "state";
14
+ /** Default debounce window for save-on-change. */
15
+ export declare const DEFAULT_PERSISTENCE_DEBOUNCE_MS = 250;
16
+ export declare function persistenceKey(persistence: StorePersistenceConfig): string;
17
+ /**
18
+ * Load the persisted slice and re-apply the whitelist/blacklist defensively
19
+ * (in case the config tightened since the data was written). Returns
20
+ * `undefined` when nothing is persisted, so callers can spread it into
21
+ * `preloadedState` without overwriting reducer defaults.
22
+ */
23
+ export declare function loadPersistedState(provider: IPersistenceProvider, persistence: StorePersistenceConfig): Promise<Record<string, unknown> | undefined>;
24
+ /**
25
+ * Subscribe to the store and persist the whitelisted slice on change,
26
+ * debounced. Returns an unsubscribe function that also cancels a pending save.
27
+ */
28
+ export declare function attachPersistenceSave(store: Pick<Store, 'getState' | 'subscribe'>, provider: IPersistenceProvider, persistence: StorePersistenceConfig): () => void;
29
+ //# sourceMappingURL=persistence-wiring.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persistence-wiring.d.ts","sourceRoot":"","sources":["../../../src/impl/persistence/persistence-wiring.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AACnC,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AACxD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAGtD,6DAA6D;AAC7D,eAAO,MAAM,uBAAuB,UAAU,CAAC;AAE/C,kDAAkD;AAClD,eAAO,MAAM,+BAA+B,MAAM,CAAC;AAEnD,wBAAgB,cAAc,CAAC,WAAW,EAAE,sBAAsB,GAAG,MAAM,CAE1E;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,oBAAoB,EAC9B,WAAW,EAAE,sBAAsB,GAClC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC,CAQ9C;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,UAAU,GAAG,WAAW,CAAC,EAC5C,QAAQ,EAAE,oBAAoB,EAC9B,WAAW,EAAE,sBAAsB,GAClC,MAAM,IAAI,CAyCZ"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Load/save wiring that connects a Redux store to an `IPersistenceProvider`.
3
+ *
4
+ * - `loadPersistedState` rehydrates the whitelisted slice on boot (async,
5
+ * called before store creation so the result can seed `preloadedState`).
6
+ * - `attachPersistenceSave` subscribes to the store and writes the whitelisted
7
+ * slice back, debounced, on every change. See amah/app-framework#30.
8
+ */
9
+ import { pickPersistableState } from './persistence-filter.js';
10
+ /** Default storage key when `persistence.key` is not set. */
11
+ export const DEFAULT_PERSISTENCE_KEY = 'state';
12
+ /** Default debounce window for save-on-change. */
13
+ export const DEFAULT_PERSISTENCE_DEBOUNCE_MS = 250;
14
+ export function persistenceKey(persistence) {
15
+ return persistence.key ?? DEFAULT_PERSISTENCE_KEY;
16
+ }
17
+ /**
18
+ * Load the persisted slice and re-apply the whitelist/blacklist defensively
19
+ * (in case the config tightened since the data was written). Returns
20
+ * `undefined` when nothing is persisted, so callers can spread it into
21
+ * `preloadedState` without overwriting reducer defaults.
22
+ */
23
+ export async function loadPersistedState(provider, persistence) {
24
+ const loaded = await provider.load(persistenceKey(persistence));
25
+ if (loaded == null || typeof loaded !== 'object')
26
+ return undefined;
27
+ const filtered = pickPersistableState(loaded, persistence);
28
+ return Object.keys(filtered).length > 0 ? filtered : undefined;
29
+ }
30
+ /**
31
+ * Subscribe to the store and persist the whitelisted slice on change,
32
+ * debounced. Returns an unsubscribe function that also cancels a pending save.
33
+ */
34
+ export function attachPersistenceSave(store, provider, persistence) {
35
+ const key = persistenceKey(persistence);
36
+ const debounceMs = persistence.debounceMs ?? DEFAULT_PERSISTENCE_DEBOUNCE_MS;
37
+ let timer = null;
38
+ // Seed the baseline with the current slice so a change that leaves the
39
+ // persisted slice untouched (e.g. a non-whitelisted slice updated) never
40
+ // triggers a redundant write — including right after rehydration.
41
+ let lastSerialized = JSON.stringify(pickPersistableState(store.getState(), persistence));
42
+ const flush = () => {
43
+ timer = null;
44
+ const slice = pickPersistableState(store.getState(), persistence);
45
+ const serialized = JSON.stringify(slice);
46
+ // Skip redundant writes when the persisted slice did not actually change.
47
+ if (serialized === lastSerialized)
48
+ return;
49
+ lastSerialized = serialized;
50
+ void provider.save(key, slice);
51
+ };
52
+ const unsubscribe = store.subscribe(() => {
53
+ if (timer !== null)
54
+ clearTimeout(timer);
55
+ if (debounceMs <= 0) {
56
+ flush();
57
+ }
58
+ else {
59
+ timer = setTimeout(flush, debounceMs);
60
+ }
61
+ });
62
+ return () => {
63
+ if (timer !== null) {
64
+ clearTimeout(timer);
65
+ timer = null;
66
+ }
67
+ unsubscribe();
68
+ };
69
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Web Storage Persistence Provider
3
+ *
4
+ * Concrete `IPersistenceProvider` backed by a Web Storage area
5
+ * (`localStorage` / `sessionStorage`). When the backing storage is
6
+ * unavailable — SSR, private-mode quota errors, disabled storage — it
7
+ * degrades gracefully to an in-memory map so callers never throw. This mirrors
8
+ * the `createLocalStorageActiveProjectStorage` fallback pattern used
9
+ * downstream. See amah/app-framework#30.
10
+ */
11
+ import type { IPersistenceProvider } from '../../spi/index.js';
12
+ /** Minimal structural subset of the Web Storage API we depend on. */
13
+ export interface WebStorageLike {
14
+ getItem(key: string): string | null;
15
+ setItem(key: string, value: string): void;
16
+ removeItem(key: string): void;
17
+ key(index: number): string | null;
18
+ readonly length: number;
19
+ }
20
+ /** Default namespace every persisted key is prefixed with. */
21
+ export declare const DEFAULT_PERSISTENCE_NAMESPACE = "@hamak/ui-store";
22
+ export declare class WebStoragePersistenceProvider implements IPersistenceProvider {
23
+ private readonly storage;
24
+ private readonly namespace;
25
+ private readonly memory;
26
+ private readonly available;
27
+ /**
28
+ * @param storage Backing web storage, or `null` to run in-memory only.
29
+ * @param namespace Key prefix; `clear()` only removes keys under this prefix
30
+ * so it never wipes unrelated app state.
31
+ */
32
+ constructor(storage: WebStorageLike | null, namespace?: string);
33
+ isAvailable(): boolean;
34
+ save(key: string, state: unknown): Promise<void>;
35
+ load(key: string): Promise<any | null>;
36
+ remove(key: string): Promise<void>;
37
+ clear(): Promise<void>;
38
+ private fullKey;
39
+ /** Verify the storage area is usable with a write/remove round-trip. */
40
+ private static probe;
41
+ }
42
+ /** Provider backed by `window.localStorage` (in-memory fallback when absent). */
43
+ export declare function createLocalStoragePersistenceProvider(namespace?: string): WebStoragePersistenceProvider;
44
+ /** Provider backed by `window.sessionStorage` (in-memory fallback when absent). */
45
+ export declare function createSessionStoragePersistenceProvider(namespace?: string): WebStoragePersistenceProvider;
46
+ //# sourceMappingURL=web-storage-persistence-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-storage-persistence-provider.d.ts","sourceRoot":"","sources":["../../../src/impl/persistence/web-storage-persistence-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAEtD,qEAAqE;AACrE,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IACpC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,8DAA8D;AAC9D,eAAO,MAAM,6BAA6B,oBAAoB,CAAC;AAI/D,qBAAa,6BAA8B,YAAW,oBAAoB;IAUtE,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAV5B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA6B;IACpD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAU;IAEpC;;;;OAIG;gBAEgB,OAAO,EAAE,cAAc,GAAG,IAAI,EAC9B,SAAS,GAAE,MAAsC;IAKpE,WAAW,IAAI,OAAO;IAIhB,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAchD,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC;IAsBtC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYlC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAmB5B,OAAO,CAAC,OAAO;IAIf,wEAAwE;IACxE,OAAO,CAAC,MAAM,CAAC,KAAK;CAUrB;AAcD,iFAAiF;AACjF,wBAAgB,qCAAqC,CACnD,SAAS,CAAC,EAAE,MAAM,GACjB,6BAA6B,CAK/B;AAED,mFAAmF;AACnF,wBAAgB,uCAAuC,CACrD,SAAS,CAAC,EAAE,MAAM,GACjB,6BAA6B,CAK/B"}