@hamak/ui-store 0.8.1 → 0.9.1

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.
@@ -1,5 +1,5 @@
1
- export type { FileSystemNode, DirectoryNode, FileNode, FileSystemNodeBase, FileSystemNodeState, FileMemoState, FileSystemState, FileContentSchema } from '@hamak/shared-utils';
2
- export { fileSystemNodeInitialState } from '@hamak/shared-utils';
1
+ export type { FileSystemNode, DirectoryNode, FileNode, FileSystemNodeBase, FileSystemNodeState, FileMemoState, FileSystemState, FileContentSchema, FileSystemNodePermissions, ResolvedFileSystemNodePermissions } from '@hamak/shared-utils';
2
+ export { fileSystemNodeInitialState, resolveNodePermissions, resolvePermissions, isWritable, isReadOnly } from '@hamak/shared-utils';
3
3
  export * from './contracts/filesystem-manager.contract.js';
4
4
  export * from './contracts/filesystem-facade.contract.js';
5
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/api/fs/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,cAAc,EACd,aAAa,EACb,QAAQ,EACR,kBAAkB,EAClB,mBAAmB,EACnB,aAAa,EACb,eAAe,EACf,iBAAiB,EAClB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AAGjE,cAAc,yCAAyC,CAAC;AACxD,cAAc,wCAAwC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/api/fs/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,cAAc,EACd,aAAa,EACb,QAAQ,EACR,kBAAkB,EAClB,mBAAmB,EACnB,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,yBAAyB,EACzB,iCAAiC,EAClC,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,0BAA0B,EAC1B,sBAAsB,EACtB,kBAAkB,EAClB,UAAU,EACV,UAAU,EACX,MAAM,qBAAqB,CAAC;AAG7B,cAAc,yCAAyC,CAAC;AACxD,cAAc,wCAAwC,CAAC"}
@@ -1,4 +1,4 @@
1
- export { fileSystemNodeInitialState } from '@hamak/shared-utils';
1
+ export { fileSystemNodeInitialState, resolveNodePermissions, resolvePermissions, isWritable, isReadOnly } from '@hamak/shared-utils';
2
2
  // Filesystem contracts
3
3
  export * from './contracts/filesystem-manager.contract.js';
4
4
  export * from './contracts/filesystem-facade.contract.js';
@@ -1 +1 @@
1
- {"version":3,"file":"autosave-middleware.d.ts","sourceRoot":"","sources":["../../../src/impl/autosave/autosave-middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,UAAU,EAA4B,MAAM,OAAO,CAAC;AAElE,OAAO,KAAK,EAAE,yBAAyB,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3E,OAAO,EAIL,KAAK,cAAc,EACpB,MAAM,WAAW,CAAC;AAgFnB;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,iCAAiC;IACjC,QAAQ,EAAE,yBAAyB,CAAC;IAEpC,mEAAmE;IACnE,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,qCAAqC;IACrC,aAAa,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;IAExC,gCAAgC;IAChC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAEvC,kCAAkC;IAClC,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,cAAc,KAAK,IAAI,CAAC;IAEjE,+BAA+B;IAC/B,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CAClF;AAcD;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,wBAAwB,GAC/B,UAAU,CA6RZ"}
1
+ {"version":3,"file":"autosave-middleware.d.ts","sourceRoot":"","sources":["../../../src/impl/autosave/autosave-middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,UAAU,EAA4B,MAAM,OAAO,CAAC;AAElE,OAAO,KAAK,EAAE,yBAAyB,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3E,OAAO,EAIL,KAAK,cAAc,EACpB,MAAM,WAAW,CAAC;AAgFnB;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,iCAAiC;IACjC,QAAQ,EAAE,yBAAyB,CAAC;IAEpC,mEAAmE;IACnE,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,qCAAqC;IACrC,aAAa,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;IAExC,gCAAgC;IAChC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAEvC,kCAAkC;IAClC,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,cAAc,KAAK,IAAI,CAAC;IAEjE,+BAA+B;IAC/B,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CAClF;AAcD;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,wBAAwB,GAC/B,UAAU,CA+RZ"}
@@ -7,6 +7,7 @@
7
7
  * 3. Delegates save operations to registered providers
8
8
  * 4. Handles retries on failure
9
9
  */
10
+ import { resolvePermissions } from '@hamak/shared-utils';
10
11
  import { AutosaveActionTypes, autosaveActions, DEFAULT_AUTOSAVE_CONFIG, } from '../../api/index.js';
11
12
  import { resolveAutosaveConfig, getNodeAtPath, pathToKey, } from './autosave-config-resolver.js';
12
13
  /**
@@ -247,7 +248,9 @@ export function createAutosaveMiddleware(config) {
247
248
  if (root) {
248
249
  // Resolve config
249
250
  const autosaveConfig = resolveAutosaveConfig(root, path, mergedDefaultConfig);
250
- if (autosaveConfig.enabled) {
251
+ // Effective enable also requires the node to be writable — a
252
+ // read-only node forces autosave off regardless of config.
253
+ if (autosaveConfig.enabled && resolvePermissions(root, path).write) {
251
254
  // Get node
252
255
  const node = getNodeAtPath(root, path);
253
256
  if (node) {
@@ -1,11 +1,12 @@
1
1
  import { FileNode, FileSystemNode, FileSystemState, FileContentSchema } from '../../../api/index.js';
2
+ import { type FileSystemNodePermissions } from '@hamak/shared-utils';
2
3
  import { StructureNodeCommand } from './structure-commands.js';
3
4
  import { DataUpdater } from '../utils/data-updater.js';
4
5
  /**
5
6
  * Base interface for filesystem commands
6
7
  */
7
8
  export interface FsCommandBase {
8
- name: 'mkdir' | 'set-file' | 'remove' | 'update-file-content' | 'set-file-content' | 'set-extension-state';
9
+ name: 'mkdir' | 'set-file' | 'remove' | 'update-file-content' | 'set-file-content' | 'set-extension-state' | 'set-content-loading' | 'set-permissions';
9
10
  path: string | string[];
10
11
  }
11
12
  /**
@@ -57,10 +58,27 @@ export interface SetExtensionStateCommand extends FsCommandBase {
57
58
  key: string;
58
59
  state: Record<string, unknown>;
59
60
  }
61
+ /**
62
+ * Set the loading flag on a file node without touching its content.
63
+ * Used for stale-while-revalidate refreshes (keep previous content visible
64
+ * while a new fetch is in flight). Setting loading=true clears any prior error.
65
+ */
66
+ export interface SetContentLoadingCommand extends FsCommandBase {
67
+ name: 'set-content-loading';
68
+ loading: boolean;
69
+ }
70
+ /**
71
+ * Set (merge) client-side permissions on a node. Typically issued by the
72
+ * provider that loaded the node, reflecting the source's real ACL.
73
+ */
74
+ export interface SetPermissionsCommand extends FsCommandBase {
75
+ name: 'set-permissions';
76
+ permissions: FileSystemNodePermissions;
77
+ }
60
78
  /**
61
79
  * Union type for all filesystem commands
62
80
  */
63
- export type FileSystemCommand = MkdirCommand | RemoveCommand | SetFileCommand | UpdateFileContentCommand | SetFileContentCommand | SetExtensionStateCommand;
81
+ export type FileSystemCommand = MkdirCommand | RemoveCommand | SetFileCommand | UpdateFileContentCommand | SetFileContentCommand | SetExtensionStateCommand | SetContentLoadingCommand | SetPermissionsCommand;
64
82
  export { pathSteps, parentPathSteps } from '@hamak/shared-utils';
65
83
  /**
66
84
  * Get filesystem node at path
@@ -79,6 +97,8 @@ export declare class FileSystemCommandHandler {
79
97
  private contentMayChange;
80
98
  private executeSetContentCommand;
81
99
  private executeSetExtensionState;
100
+ private executeSetContentLoading;
101
+ private executeSetPermissions;
82
102
  protected current<T = any>(o: T): T;
83
103
  protected original<T = any>(o: T): T | undefined;
84
104
  }
@@ -1 +1 @@
1
- {"version":3,"file":"fs-commands.d.ts","sourceRoot":"","sources":["../../../../src/impl/fs/commands/fs-commands.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"fs-commands.d.ts","sourceRoot":"","sources":["../../../../src/impl/fs/commands/fs-commands.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,QAAQ,EACR,cAAc,EACd,eAAe,EACf,iBAAiB,EAGlB,MAAM,cAAc,CAAC;AACtB,OAAO,EAKL,KAAK,yBAAyB,EAC/B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAIpD;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAG,OAAO,GAAG,UAAU,GAAG,QAAQ,GAAG,qBAAqB,GAAG,kBAAkB,GAAG,qBAAqB,GAAG,qBAAqB,GAAG,iBAAiB,CAAA;IACvJ,IAAI,EAAG,MAAM,GAAG,MAAM,EAAE,CAAA;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,YAAa,SAAQ,aAAa;IACjD,IAAI,EAAG,OAAO,CAAA;IACd,OAAO,CAAC,EAAG,OAAO,CAAA;IAClB,eAAe,CAAC,EAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC3C;AAED;;GAEG;AACH,MAAM,WAAW,aAAc,SAAQ,aAAa;IAClD,IAAI,EAAG,QAAQ,CAAA;IACf,SAAS,CAAC,EAAG,OAAO,CAAA;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,cAAe,SAAQ,aAAa;IACnD,IAAI,EAAG,UAAU,CAAA;IACjB,OAAO,EAAG,GAAG,CAAA;IACb,MAAM,EAAG,iBAAiB,CAAA;IAC1B,QAAQ,CAAC,EAAG,OAAO,CAAA;IACnB,gBAAgB,CAAC,EAAG,OAAO,CAAA;IAC3B,eAAe,CAAC,EAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC3C;AAED;;GAEG;AACH,MAAM,WAAW,wBAAyB,SAAQ,aAAa;IAC7D,IAAI,EAAG,qBAAqB,CAAA;IAC5B,cAAc,EAAG,oBAAoB,CAAA;CACtC;AAED;;GAEG;AACH,MAAM,WAAW,qBAAsB,SAAQ,aAAa;IAC1D,IAAI,EAAG,kBAAkB,CAAA;IACzB,OAAO,EAAG,GAAG,CAAC;IACd,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,wBAAyB,SAAQ,aAAa;IAC7D,IAAI,EAAG,qBAAqB,CAAA;IAC5B,GAAG,EAAG,MAAM,CAAA;IACZ,KAAK,EAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAChC;AAED;;;;GAIG;AACH,MAAM,WAAW,wBAAyB,SAAQ,aAAa;IAC7D,IAAI,EAAG,qBAAqB,CAAA;IAC5B,OAAO,EAAG,OAAO,CAAA;CAClB;AAED;;;GAGG;AACH,MAAM,WAAW,qBAAsB,SAAQ,aAAa;IAC1D,IAAI,EAAG,iBAAiB,CAAA;IACxB,WAAW,EAAG,yBAAyB,CAAA;CACxC;AAED;;GAEG;AACH,MAAM,MAAM,iBAAiB,GACzB,YAAY,GACZ,aAAa,GACb,cAAc,GACd,wBAAwB,GACxB,qBAAqB,GACrB,wBAAwB,GACxB,wBAAwB,GACxB,qBAAqB,CAAA;AAGzB,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEjE;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,cAAc,EAAE,IAAI,EAAG,MAAM,GAAG,MAAM,EAAE,GAAI,cAAc,GAAG,SAAS,CAe/G;AA4BD;;GAEG;AACH,qBAAa,wBAAwB;IACnC,QAAQ,CAAC,qBAAqB,4BAAkC;IAEhE,OAAO,CAAC,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,iBAAiB,GAAG,eAAe;IAa5E,OAAO,CAAC,YAAY;IAoCpB,OAAO,CAAC,aAAa;IA+BrB,OAAO,CAAC,cAAc;IA2BtB,OAAO,CAAC,qBAAqB;IA6B7B,OAAO,CAAC,gBAAgB;IAgBxB,OAAO,CAAC,wBAAwB;IA0ChC,OAAO,CAAC,wBAAwB;IA0BhC,OAAO,CAAC,wBAAwB;IAwBhC,OAAO,CAAC,qBAAqB;IAwB7B,SAAS,CAAC,OAAO,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,EAAG,CAAC,GAAE,CAAC;IAQnC,SAAS,CAAC,QAAQ,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,EAAG,CAAC,GAAE,CAAC,GAAG,SAAS;CAOjD;AAiBD;;GAEG;AACH,qBAAa,yBAAyB;IACpC,QAAQ,CAAC,OAAO,cAAoB;IAEpC,OAAO,CAAC,IAAI,EAAG,QAAQ,EAAE,OAAO,EAAE,oBAAoB;IAoBtD,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,UAAU;IAclB,OAAO,CAAC,UAAU;IAWlB,OAAO,CAAC,aAAa;IAcrB;;OAEG;IACI,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,GAAG,IAAI;CAKxD"}
@@ -1,5 +1,5 @@
1
1
  import { fileSystemNodeInitialState } from '../../../api/index.js';
2
- import { pathSteps } from '@hamak/shared-utils';
2
+ import { pathSteps, resolvePermissions } from '@hamak/shared-utils';
3
3
  import { DataUpdater } from '../utils/data-updater.js';
4
4
  import { produce, current, isDraft, original } from 'immer';
5
5
  import { deepEqual } from '../utils/deep-equal.js';
@@ -69,6 +69,8 @@ export class FileSystemCommandHandler {
69
69
  case 'set-file-content': return this.executeSetContentCommand(state, command);
70
70
  case 'remove': return this.executeRemove(state, command);
71
71
  case 'set-extension-state': return this.executeSetExtensionState(state, command);
72
+ case 'set-content-loading': return this.executeSetContentLoading(state, command);
73
+ case 'set-permissions': return this.executeSetPermissions(state, command);
72
74
  }
73
75
  }
74
76
  executeMkdir(state, command) {
@@ -124,6 +126,11 @@ export class FileSystemCommandHandler {
124
126
  && !(recursive === true)) {
125
127
  return;
126
128
  }
129
+ // Permission gate: node (with inheritance) must allow removal.
130
+ if (child !== undefined && !resolvePermissions(root, steps).remove) {
131
+ console.warn(`Remove denied (read-only) at location`, path);
132
+ return;
133
+ }
127
134
  delete parentDir.children[last];
128
135
  }
129
136
  });
@@ -160,6 +167,11 @@ export class FileSystemCommandHandler {
160
167
  const fileNode = getFileSystemNode(root, path);
161
168
  if (fileNode !== undefined) {
162
169
  if (fileNode.type === 'file') {
170
+ // Permission gate: local edits require write (loads use fromRemote).
171
+ if (!resolvePermissions(root, path).write) {
172
+ console.warn(`Edit denied (read-only) at location`, path);
173
+ return;
174
+ }
163
175
  this.contentCommandHandler.execute(fileNode, contentCommand);
164
176
  this.contentMayChange(fileNode);
165
177
  }
@@ -193,6 +205,12 @@ export class FileSystemCommandHandler {
193
205
  const fileNode = getFileSystemNode(root, path);
194
206
  if (fileNode !== undefined) {
195
207
  if (fileNode.type === 'file') {
208
+ // Permission gate: a local (non-remote) set requires write.
209
+ // Remote loads (fromRemote) are always allowed.
210
+ if (!fromRemote && !resolvePermissions(root, path).write) {
211
+ console.warn(`Edit denied (read-only) at location`, path);
212
+ return;
213
+ }
196
214
  fileNode.content = content;
197
215
  if (fileNode.state === undefined) {
198
216
  fileNode.state = fileSystemNodeInitialState(true);
@@ -245,6 +263,46 @@ export class FileSystemCommandHandler {
245
263
  }
246
264
  });
247
265
  }
266
+ executeSetContentLoading(state, command) {
267
+ const { path, loading } = command;
268
+ return produce(state, draft => {
269
+ const { root } = draft;
270
+ const node = getFileSystemNode(root, path);
271
+ if (node === undefined) {
272
+ console.warn(`No node found at location`, path);
273
+ return;
274
+ }
275
+ if (node.state === undefined) {
276
+ node.state = fileSystemNodeInitialState();
277
+ }
278
+ // Flip the loading flag without touching content. Starting a load clears
279
+ // any previous error so a retry doesn't surface a stale failure.
280
+ node.state.contentLoading = loading;
281
+ if (loading) {
282
+ node.state.contentLoadError = undefined;
283
+ }
284
+ });
285
+ }
286
+ executeSetPermissions(state, command) {
287
+ const { path, permissions } = command;
288
+ return produce(state, draft => {
289
+ const { root } = draft;
290
+ const node = getFileSystemNode(root, path);
291
+ if (node === undefined) {
292
+ console.warn(`No node found at location`, path);
293
+ return;
294
+ }
295
+ if (node.state === undefined) {
296
+ node.state = fileSystemNodeInitialState();
297
+ }
298
+ // Merge onto any existing permissions so a provider can set fields
299
+ // incrementally (e.g. { write: false } then { reason }).
300
+ node.state.permissions = {
301
+ ...node.state.permissions,
302
+ ...permissions
303
+ };
304
+ });
305
+ }
248
306
  current(o) {
249
307
  if (o === null || o === undefined) {
250
308
  return o;
@@ -2,6 +2,7 @@ import { Action } from '@reduxjs/toolkit';
2
2
  import { FileSystemCommandHandler } from '../commands/fs-commands.js';
3
3
  import { FileSystemNode, FileSystemState, FileContentSchema, FileSystemNodeActionParams, FileSystemNodeAction, Selector } from '../../../api/index.js';
4
4
  import { StructureNodeCommand } from '../commands/structure-commands.js';
5
+ import type { FileSystemNodePermissions } from '@hamak/shared-utils';
5
6
  /**
6
7
  * Factory function to create a FileSystemAdapter
7
8
  */
@@ -35,6 +36,8 @@ export declare class FileSystemNodeActions {
35
36
  private readonly _updateFileContentType;
36
37
  private readonly _setFileContentType;
37
38
  private readonly _setExtensionStateType;
39
+ private readonly _setContentLoadingType;
40
+ private readonly _setPermissionsType;
38
41
  private actionTypeSet;
39
42
  constructor(sliceName: string);
40
43
  get mkdirType(): string;
@@ -43,6 +46,8 @@ export declare class FileSystemNodeActions {
43
46
  get updateFileContentType(): string;
44
47
  get setFileContentType(): string;
45
48
  get setExtensionStateType(): string;
49
+ get setContentLoadingType(): string;
50
+ get setPermissionsType(): string;
46
51
  isFileSystemNodeAction(action: Action): action is FileSystemNodeAction;
47
52
  mkdir(path: string | string[], parents?: boolean, extensionStates?: Record<string, unknown>): FileSystemNodeAction;
48
53
  setFile(path: string | string[], content: any, schema: FileContentSchema, params?: FileSystemNodeActionParams): FileSystemNodeAction;
@@ -50,5 +55,17 @@ export declare class FileSystemNodeActions {
50
55
  setFileContent(path: string | string[], content: any, fromRemote?: boolean): FileSystemNodeAction;
51
56
  removeNode(path: string | string[], recursive?: boolean): FileSystemNodeAction;
52
57
  setExtensionState(path: string | string[], key: string, state: Record<string, unknown>): FileSystemNodeAction;
58
+ /**
59
+ * Set a file node's loading flag without altering its content.
60
+ * Enables stale-while-revalidate: keep the previous content visible while a
61
+ * re-fetch is in flight. Passing `true` also clears any prior load error.
62
+ */
63
+ setContentLoading(path: string | string[], loading: boolean): FileSystemNodeAction;
64
+ /**
65
+ * Set (merge) client-side permissions on a node. Absent permissions mean a
66
+ * node is fully permissive; providers use this to reflect a source's ACL
67
+ * (e.g. a read-only listing: `{ write: false, reason: 'Query result' }`).
68
+ */
69
+ setPermissions(path: string | string[], permissions: FileSystemNodePermissions): FileSystemNodeAction;
53
70
  }
54
71
  //# sourceMappingURL=fs-adapter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fs-adapter.d.ts","sourceRoot":"","sources":["../../../../src/impl/fs/core/fs-adapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"fs-adapter.d.ts","sourceRoot":"","sources":["../../../../src/impl/fs/core/fs-adapter.ts"],"names":[],"mappings":"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"}
@@ -122,6 +122,18 @@ export class FileSystemNodeActions {
122
122
  writable: true,
123
123
  value: void 0
124
124
  });
125
+ Object.defineProperty(this, "_setContentLoadingType", {
126
+ enumerable: true,
127
+ configurable: true,
128
+ writable: true,
129
+ value: void 0
130
+ });
131
+ Object.defineProperty(this, "_setPermissionsType", {
132
+ enumerable: true,
133
+ configurable: true,
134
+ writable: true,
135
+ value: void 0
136
+ });
125
137
  Object.defineProperty(this, "actionTypeSet", {
126
138
  enumerable: true,
127
139
  configurable: true,
@@ -134,13 +146,17 @@ export class FileSystemNodeActions {
134
146
  this._updateFileContentType = `${this.sliceName}/updateFileContent`;
135
147
  this._setFileContentType = `${this.sliceName}/setFileContent`;
136
148
  this._setExtensionStateType = `${this.sliceName}/setExtensionState`;
149
+ this._setContentLoadingType = `${this.sliceName}/setContentLoading`;
150
+ this._setPermissionsType = `${this.sliceName}/setPermissions`;
137
151
  this.actionTypeSet = new Set([
138
152
  this._mkdirType,
139
153
  this._setFileType,
140
154
  this._removeType,
141
155
  this._updateFileContentType,
142
156
  this._setFileContentType,
143
- this._setExtensionStateType
157
+ this._setExtensionStateType,
158
+ this._setContentLoadingType,
159
+ this._setPermissionsType
144
160
  ]);
145
161
  }
146
162
  get mkdirType() {
@@ -161,6 +177,12 @@ export class FileSystemNodeActions {
161
177
  get setExtensionStateType() {
162
178
  return this._setExtensionStateType;
163
179
  }
180
+ get setContentLoadingType() {
181
+ return this._setContentLoadingType;
182
+ }
183
+ get setPermissionsType() {
184
+ return this._setPermissionsType;
185
+ }
164
186
  isFileSystemNodeAction(action) {
165
187
  return this.actionTypeSet.has(action.type);
166
188
  }
@@ -201,4 +223,26 @@ export class FileSystemNodeActions {
201
223
  command: { name: 'set-extension-state', path, key, state }
202
224
  };
203
225
  }
226
+ /**
227
+ * Set a file node's loading flag without altering its content.
228
+ * Enables stale-while-revalidate: keep the previous content visible while a
229
+ * re-fetch is in flight. Passing `true` also clears any prior load error.
230
+ */
231
+ setContentLoading(path, loading) {
232
+ return {
233
+ type: this.setContentLoadingType,
234
+ command: { name: 'set-content-loading', path, loading }
235
+ };
236
+ }
237
+ /**
238
+ * Set (merge) client-side permissions on a node. Absent permissions mean a
239
+ * node is fully permissive; providers use this to reflect a source's ACL
240
+ * (e.g. a read-only listing: `{ write: false, reason: 'Query result' }`).
241
+ */
242
+ setPermissions(path, permissions) {
243
+ return {
244
+ type: this.setPermissionsType,
245
+ command: { name: 'set-permissions', path, permissions }
246
+ };
247
+ }
204
248
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hamak/ui-store",
3
- "version": "0.8.1",
3
+ "version": "0.9.1",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Redux store management for microkernel-based applications",