@hamak/ui-remote-resource 0.5.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.
- package/dist/api/actions/entity-action-factory.d.ts +28 -0
- package/dist/api/actions/entity-action-factory.d.ts.map +1 -0
- package/dist/api/actions/entity-action-factory.js +62 -0
- package/dist/api/actions/entity-action-types.d.ts +104 -0
- package/dist/api/actions/entity-action-types.d.ts.map +1 -0
- package/dist/api/actions/entity-action-types.js +16 -0
- package/dist/api/actions/resource-action-factory.d.ts +40 -0
- package/dist/api/actions/resource-action-factory.d.ts.map +1 -0
- package/dist/api/actions/resource-action-factory.js +93 -0
- package/dist/api/actions/resource-action-types.d.ts +105 -0
- package/dist/api/actions/resource-action-types.d.ts.map +1 -0
- package/dist/api/actions/resource-action-types.js +13 -0
- package/dist/api/constants.d.ts +6 -0
- package/dist/api/constants.d.ts.map +1 -0
- package/dist/api/constants.js +5 -0
- package/dist/api/contracts/i-entity-registry.d.ts +19 -0
- package/dist/api/contracts/i-entity-registry.d.ts.map +1 -0
- package/dist/api/contracts/i-entity-registry.js +1 -0
- package/dist/api/contracts/i-resource-registry.d.ts +19 -0
- package/dist/api/contracts/i-resource-registry.d.ts.map +1 -0
- package/dist/api/contracts/i-resource-registry.js +1 -0
- package/dist/api/index.d.ts +12 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +15 -0
- package/dist/api/types/action-types.d.ts +34 -0
- package/dist/api/types/action-types.d.ts.map +1 -0
- package/dist/api/types/action-types.js +1 -0
- package/dist/api/types/resource-attributes.d.ts +43 -0
- package/dist/api/types/resource-attributes.d.ts.map +1 -0
- package/dist/api/types/resource-attributes.js +1 -0
- package/dist/api/types/resource-types.d.ts +20 -0
- package/dist/api/types/resource-types.d.ts.map +1 -0
- package/dist/api/types/resource-types.js +1 -0
- package/dist/api/utils/resource-attributes-util.d.ts +183 -0
- package/dist/api/utils/resource-attributes-util.d.ts.map +1 -0
- package/dist/api/utils/resource-attributes-util.js +280 -0
- package/dist/impl/autosave/index.d.ts +5 -0
- package/dist/impl/autosave/index.d.ts.map +1 -0
- package/dist/impl/autosave/index.js +4 -0
- package/dist/impl/autosave/resource-autosave-provider.d.ts +49 -0
- package/dist/impl/autosave/resource-autosave-provider.d.ts.map +1 -0
- package/dist/impl/autosave/resource-autosave-provider.js +112 -0
- package/dist/impl/index.d.ts +20 -0
- package/dist/impl/index.d.ts.map +1 -0
- package/dist/impl/index.js +20 -0
- package/dist/impl/middleware/entity-middleware.d.ts +13 -0
- package/dist/impl/middleware/entity-middleware.d.ts.map +1 -0
- package/dist/impl/middleware/entity-middleware.js +221 -0
- package/dist/impl/middleware/entity-sync-middleware.d.ts +7 -0
- package/dist/impl/middleware/entity-sync-middleware.d.ts.map +1 -0
- package/dist/impl/middleware/entity-sync-middleware.js +32 -0
- package/dist/impl/middleware/resource-middleware.d.ts +13 -0
- package/dist/impl/middleware/resource-middleware.d.ts.map +1 -0
- package/dist/impl/middleware/resource-middleware.js +97 -0
- package/dist/impl/middleware/sync-middleware.d.ts +12 -0
- package/dist/impl/middleware/sync-middleware.d.ts.map +1 -0
- package/dist/impl/middleware/sync-middleware.js +80 -0
- package/dist/impl/plugin/resource-plugin-factory.d.ts +31 -0
- package/dist/impl/plugin/resource-plugin-factory.d.ts.map +1 -0
- package/dist/impl/plugin/resource-plugin-factory.js +131 -0
- package/dist/impl/providers/mock-resource-provider.d.ts +147 -0
- package/dist/impl/providers/mock-resource-provider.d.ts.map +1 -0
- package/dist/impl/providers/mock-resource-provider.js +242 -0
- package/dist/impl/providers/rest-resource-provider.d.ts +51 -0
- package/dist/impl/providers/rest-resource-provider.d.ts.map +1 -0
- package/dist/impl/providers/rest-resource-provider.js +128 -0
- package/dist/impl/registry/entity-registry.d.ts +54 -0
- package/dist/impl/registry/entity-registry.d.ts.map +1 -0
- package/dist/impl/registry/entity-registry.js +51 -0
- package/dist/impl/registry/resource-registry.d.ts +80 -0
- package/dist/impl/registry/resource-registry.d.ts.map +1 -0
- package/dist/impl/registry/resource-registry.js +74 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/spi/config/endpoint-definition.d.ts +46 -0
- package/dist/spi/config/endpoint-definition.d.ts.map +1 -0
- package/dist/spi/config/endpoint-definition.js +1 -0
- package/dist/spi/config/entity-definition.d.ts +50 -0
- package/dist/spi/config/entity-definition.d.ts.map +1 -0
- package/dist/spi/config/entity-definition.js +1 -0
- package/dist/spi/config/plugin-config.d.ts +21 -0
- package/dist/spi/config/plugin-config.d.ts.map +1 -0
- package/dist/spi/config/plugin-config.js +1 -0
- package/dist/spi/index.d.ts +6 -0
- package/dist/spi/index.d.ts.map +1 -0
- package/dist/spi/index.js +7 -0
- package/dist/spi/providers/i-resource-provider.d.ts +42 -0
- package/dist/spi/providers/i-resource-provider.d.ts.map +1 -0
- package/dist/spi/providers/i-resource-provider.js +1 -0
- package/package.json +71 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resource Autosave Provider
|
|
3
|
+
*
|
|
4
|
+
* Implements autosave for files managed by the remote resource plugin.
|
|
5
|
+
* Uses UPDATE_ENTITY or RESOURCE_CALL_REQUEST actions to save files.
|
|
6
|
+
*/
|
|
7
|
+
import { REMOTE_RESOURCE_EXTENSION_KEY, } from '../../api';
|
|
8
|
+
import { EntityActionFactory } from '../../api';
|
|
9
|
+
import { ResourceActionFactory } from '../../api';
|
|
10
|
+
/**
|
|
11
|
+
* Autosave provider for remote resources
|
|
12
|
+
*
|
|
13
|
+
* This provider handles autosave for files that are managed via the
|
|
14
|
+
* remote resource plugin. It checks for the presence of resource/entity
|
|
15
|
+
* extension state and uses appropriate actions to save changes.
|
|
16
|
+
*/
|
|
17
|
+
export class ResourceAutosaveProvider {
|
|
18
|
+
constructor(config = {}) {
|
|
19
|
+
Object.defineProperty(this, "id", {
|
|
20
|
+
enumerable: true,
|
|
21
|
+
configurable: true,
|
|
22
|
+
writable: true,
|
|
23
|
+
value: 'remote-resource'
|
|
24
|
+
});
|
|
25
|
+
Object.defineProperty(this, "priority", {
|
|
26
|
+
enumerable: true,
|
|
27
|
+
configurable: true,
|
|
28
|
+
writable: true,
|
|
29
|
+
value: 20
|
|
30
|
+
}); // Higher priority than remote-fs
|
|
31
|
+
Object.defineProperty(this, "entityActionFactory", {
|
|
32
|
+
enumerable: true,
|
|
33
|
+
configurable: true,
|
|
34
|
+
writable: true,
|
|
35
|
+
value: void 0
|
|
36
|
+
});
|
|
37
|
+
Object.defineProperty(this, "resourceActionFactory", {
|
|
38
|
+
enumerable: true,
|
|
39
|
+
configurable: true,
|
|
40
|
+
writable: true,
|
|
41
|
+
value: void 0
|
|
42
|
+
});
|
|
43
|
+
this.entityActionFactory = config.entityActionFactory ?? new EntityActionFactory();
|
|
44
|
+
this.resourceActionFactory = config.resourceActionFactory ?? new ResourceActionFactory();
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Check if this provider supports a given path.
|
|
48
|
+
* Returns true if the node has remote resource extension state.
|
|
49
|
+
*/
|
|
50
|
+
supports(_path, node) {
|
|
51
|
+
const extensionState = node.state?.extensionStates?.[REMOTE_RESOURCE_EXTENSION_KEY];
|
|
52
|
+
return extensionState !== undefined;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Save file content to remote server.
|
|
56
|
+
* Dispatches UPDATE_ENTITY (if entity) or RESOURCE_CALL_REQUEST (if resource).
|
|
57
|
+
*/
|
|
58
|
+
async save(path, content, node, dispatch) {
|
|
59
|
+
try {
|
|
60
|
+
const resourceAttrs = node.state?.extensionStates?.[REMOTE_RESOURCE_EXTENSION_KEY];
|
|
61
|
+
if (!resourceAttrs) {
|
|
62
|
+
return {
|
|
63
|
+
success: false,
|
|
64
|
+
error: {
|
|
65
|
+
code: 'NO_RESOURCE_STATE',
|
|
66
|
+
message: 'Node has no remote resource extension state',
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
// Check if this is an entity
|
|
71
|
+
if (resourceAttrs.entity?.entityId) {
|
|
72
|
+
// Use entity update action
|
|
73
|
+
dispatch(this.entityActionFactory.update(resourceAttrs.entity.entityId, path, content));
|
|
74
|
+
}
|
|
75
|
+
else if (resourceAttrs.endpointId) {
|
|
76
|
+
// Use resource call request with update operation
|
|
77
|
+
dispatch(this.resourceActionFactory.callRequest(resourceAttrs.endpointId, 'update', { body: content }, path));
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
return {
|
|
81
|
+
success: false,
|
|
82
|
+
error: {
|
|
83
|
+
code: 'NO_ENDPOINT',
|
|
84
|
+
message: 'Node has no entity or endpoint information',
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
// Note: The actual HTTP request is async and handled by resource middleware.
|
|
89
|
+
// Success/failure will be tracked by the middleware dispatching
|
|
90
|
+
// RESOURCE_CALL_SUCCESS or RESOURCE_CALL_FAILURE actions.
|
|
91
|
+
return {
|
|
92
|
+
success: true,
|
|
93
|
+
timestamp: Date.now(),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
return {
|
|
98
|
+
success: false,
|
|
99
|
+
error: {
|
|
100
|
+
code: error.code ?? 'SAVE_ERROR',
|
|
101
|
+
message: error.message ?? 'Failed to dispatch save action',
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Cancel is a no-op for resources since we don't track in-flight requests here.
|
|
108
|
+
*/
|
|
109
|
+
cancel(_path) {
|
|
110
|
+
// No-op: resource middleware handles request lifecycle
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export * from '../api';
|
|
2
|
+
export * from '../spi';
|
|
3
|
+
export { createResourcePlugin } from './plugin/resource-plugin-factory';
|
|
4
|
+
export type { PluginModule } from './plugin/resource-plugin-factory';
|
|
5
|
+
export { RESOURCE_REGISTRY_TOKEN, ENTITY_REGISTRY_TOKEN } from './plugin/resource-plugin-factory';
|
|
6
|
+
export { STORE_MANAGER_TOKEN, MIDDLEWARE_REGISTRY_TOKEN, STORE_EXTENSIONS_TOKEN } from '@hamak/ui-store-api';
|
|
7
|
+
export { RestResourceProvider } from './providers/rest-resource-provider';
|
|
8
|
+
export type { RestProviderConfig } from './providers/rest-resource-provider';
|
|
9
|
+
export { MockResourceProvider } from './providers/mock-resource-provider';
|
|
10
|
+
export type { MockProviderConfig, EndpointMockConfig, MockConfigMap, MockCallHistoryEntry } from './providers/mock-resource-provider';
|
|
11
|
+
export { ResourceRegistry } from './registry/resource-registry';
|
|
12
|
+
export { EntityRegistry } from './registry/entity-registry';
|
|
13
|
+
export { createResourceMiddleware } from './middleware/resource-middleware';
|
|
14
|
+
export { createSyncMiddleware } from './middleware/sync-middleware';
|
|
15
|
+
export { createEntityMiddleware } from './middleware/entity-middleware';
|
|
16
|
+
export { createEntitySyncMiddleware } from './middleware/entity-sync-middleware';
|
|
17
|
+
export type { FileSystemNodeActions } from '@hamak/ui-store-impl';
|
|
18
|
+
export { ResourceAutosaveProvider } from './autosave/resource-autosave-provider';
|
|
19
|
+
export type { ResourceAutosaveProviderConfig } from './autosave/resource-autosave-provider';
|
|
20
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/impl/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,QAAQ,CAAC;AAGvB,OAAO,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AACxE,YAAY,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AACrE,OAAO,EACL,uBAAuB,EACvB,qBAAqB,EACtB,MAAM,kCAAkC,CAAC;AAG1C,OAAO,EACL,mBAAmB,EACnB,yBAAyB,EACzB,sBAAsB,EACvB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAC1E,YAAY,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAC1E,YAAY,EACV,kBAAkB,EAClB,kBAAkB,EAClB,aAAa,EACb,oBAAoB,EACrB,MAAM,oCAAoC,CAAC;AAG5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAG5D,OAAO,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAC5E,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EAAE,0BAA0B,EAAE,MAAM,qCAAqC,CAAC;AAGjF,YAAY,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAGlE,OAAO,EAAE,wBAAwB,EAAE,MAAM,uCAAuC,CAAC;AACjF,YAAY,EAAE,8BAA8B,EAAE,MAAM,uCAAuC,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export * from '../api';
|
|
2
|
+
export * from '../spi';
|
|
3
|
+
// Plugin factory (main export)
|
|
4
|
+
export { createResourcePlugin } from './plugin/resource-plugin-factory';
|
|
5
|
+
export { RESOURCE_REGISTRY_TOKEN, ENTITY_REGISTRY_TOKEN } from './plugin/resource-plugin-factory';
|
|
6
|
+
// Re-export store tokens from ui-store-api for convenience
|
|
7
|
+
export { STORE_MANAGER_TOKEN, MIDDLEWARE_REGISTRY_TOKEN, STORE_EXTENSIONS_TOKEN } from '@hamak/ui-store-api';
|
|
8
|
+
// Built-in providers (exported for convenience and testing)
|
|
9
|
+
export { RestResourceProvider } from './providers/rest-resource-provider';
|
|
10
|
+
export { MockResourceProvider } from './providers/mock-resource-provider';
|
|
11
|
+
// Registry implementations (internal, but exported for testing)
|
|
12
|
+
export { ResourceRegistry } from './registry/resource-registry';
|
|
13
|
+
export { EntityRegistry } from './registry/entity-registry';
|
|
14
|
+
// Middleware factories (internal, but exported for advanced use cases)
|
|
15
|
+
export { createResourceMiddleware } from './middleware/resource-middleware';
|
|
16
|
+
export { createSyncMiddleware } from './middleware/sync-middleware';
|
|
17
|
+
export { createEntityMiddleware } from './middleware/entity-middleware';
|
|
18
|
+
export { createEntitySyncMiddleware } from './middleware/entity-sync-middleware';
|
|
19
|
+
// Autosave provider
|
|
20
|
+
export { ResourceAutosaveProvider } from './autosave/resource-autosave-provider';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Middleware } from 'redux';
|
|
2
|
+
import { type EntityAction } from '../../api';
|
|
3
|
+
export interface EntityMiddlewareConfig {
|
|
4
|
+
entityRegistry: any;
|
|
5
|
+
fsSliceName: string;
|
|
6
|
+
onError?: (error: any, action: EntityAction) => void;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Entity middleware
|
|
10
|
+
* Translates entity actions to resource actions
|
|
11
|
+
*/
|
|
12
|
+
export declare function createEntityMiddleware<S = any>(config: EntityMiddlewareConfig): Middleware<{}, S>;
|
|
13
|
+
//# sourceMappingURL=entity-middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entity-middleware.d.ts","sourceRoot":"","sources":["../../../src/impl/middleware/entity-middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AACxC,OAAO,EAGL,KAAK,YAAY,EAMlB,MAAM,WAAW,CAAC;AAGnB,MAAM,WAAW,sBAAsB;IACrC,cAAc,EAAE,GAAG,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,YAAY,KAAK,IAAI,CAAC;CACtD;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,GAAG,GAAG,EAC5C,MAAM,EAAE,sBAAsB,GAC7B,UAAU,CAAC,EAAE,EAAE,CAAC,CAAC,CAgCnB"}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { EntityActionTypes, ResourceActionFactory, resourceAttributesUtil } from '../../api';
|
|
2
|
+
/**
|
|
3
|
+
* Entity middleware
|
|
4
|
+
* Translates entity actions to resource actions
|
|
5
|
+
*/
|
|
6
|
+
export function createEntityMiddleware(config) {
|
|
7
|
+
const { entityRegistry, fsSliceName, onError } = config;
|
|
8
|
+
const resourceActionFactory = new ResourceActionFactory();
|
|
9
|
+
return (store) => (next) => (action) => {
|
|
10
|
+
// Only handle entity actions
|
|
11
|
+
if (!isEntityAction(action)) {
|
|
12
|
+
return next(action);
|
|
13
|
+
}
|
|
14
|
+
// Pass through for tracking
|
|
15
|
+
next(action);
|
|
16
|
+
const entityAction = action;
|
|
17
|
+
try {
|
|
18
|
+
// Translate entity action to resource action
|
|
19
|
+
const resourceAction = translateEntityAction(entityAction, entityRegistry, resourceActionFactory, store.getState(), fsSliceName);
|
|
20
|
+
// Dispatch resource action
|
|
21
|
+
store.dispatch(resourceAction);
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
onError?.(error, entityAction);
|
|
25
|
+
console.error('Entity middleware error:', error);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function isEntityAction(action) {
|
|
30
|
+
return [
|
|
31
|
+
EntityActionTypes.FETCH_ENTITY,
|
|
32
|
+
EntityActionTypes.UPDATE_ENTITY,
|
|
33
|
+
EntityActionTypes.DELETE_ENTITY,
|
|
34
|
+
EntityActionTypes.CREATE_ENTITY
|
|
35
|
+
].includes(action.type);
|
|
36
|
+
}
|
|
37
|
+
function translateEntityAction(entityAction, entityRegistry, resourceActionFactory, state, fsSliceName) {
|
|
38
|
+
const { entityId } = entityAction.payload;
|
|
39
|
+
// Get entity definition
|
|
40
|
+
const entityDef = entityRegistry.getEntity(entityId);
|
|
41
|
+
if (!entityDef) {
|
|
42
|
+
throw new Error(`Entity not found: ${entityId}`);
|
|
43
|
+
}
|
|
44
|
+
switch (entityAction.type) {
|
|
45
|
+
case EntityActionTypes.FETCH_ENTITY:
|
|
46
|
+
return handleFetchEntity(entityAction, entityDef, resourceActionFactory);
|
|
47
|
+
case EntityActionTypes.UPDATE_ENTITY:
|
|
48
|
+
return handleUpdateEntity(entityAction, entityDef, resourceActionFactory, state, fsSliceName);
|
|
49
|
+
case EntityActionTypes.DELETE_ENTITY:
|
|
50
|
+
return handleDeleteEntity(entityAction, entityDef, resourceActionFactory, state, fsSliceName);
|
|
51
|
+
case EntityActionTypes.CREATE_ENTITY:
|
|
52
|
+
return handleCreateEntity(entityAction, entityDef, resourceActionFactory);
|
|
53
|
+
default:
|
|
54
|
+
throw new Error(`Unknown entity action type: ${entityAction.type}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Handle FETCH_ENTITY: keys provided, generate path and params
|
|
59
|
+
*/
|
|
60
|
+
function handleFetchEntity(action, entityDef, resourceActionFactory) {
|
|
61
|
+
const { keys, params, path } = action.payload;
|
|
62
|
+
// Validate keys
|
|
63
|
+
validateKeys(keys, entityDef.keySchema);
|
|
64
|
+
// Generate path from keys (if not explicitly provided)
|
|
65
|
+
const targetPath = path || generatePath(keys, entityDef);
|
|
66
|
+
// Map keys to resource params
|
|
67
|
+
const resourceParams = entityDef.keyMapper(keys, 'fetch');
|
|
68
|
+
// Merge with additional params
|
|
69
|
+
const mergedParams = {
|
|
70
|
+
...resourceParams.params,
|
|
71
|
+
...params,
|
|
72
|
+
// Store entity context for entity sync middleware
|
|
73
|
+
_entityContext: {
|
|
74
|
+
entityId: entityDef.id,
|
|
75
|
+
keys,
|
|
76
|
+
keyFields: entityDef.keySchema.fields
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
return resourceActionFactory.callRequest(entityDef.resourceEndpointId, 'fetch', mergedParams, targetPath, action.id);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Handle UPDATE_ENTITY: path provided, read keys from file attributes
|
|
83
|
+
*/
|
|
84
|
+
function handleUpdateEntity(action, entityDef, resourceActionFactory, state, fsSliceName) {
|
|
85
|
+
const { path, data, params } = action.payload;
|
|
86
|
+
// Read file node
|
|
87
|
+
const fileNode = selectFileNode(state, path, fsSliceName);
|
|
88
|
+
if (!fileNode) {
|
|
89
|
+
throw new Error(`Entity not found at path: ${JSON.stringify(path)}`);
|
|
90
|
+
}
|
|
91
|
+
// Get entity keys from extension state
|
|
92
|
+
const keys = resourceAttributesUtil.getEntityKeys(fileNode);
|
|
93
|
+
if (!keys) {
|
|
94
|
+
throw new Error(`Entity keys not found in file attributes at: ${JSON.stringify(path)}`);
|
|
95
|
+
}
|
|
96
|
+
// Validate keys
|
|
97
|
+
validateKeys(keys, entityDef.keySchema);
|
|
98
|
+
// Map keys to resource params
|
|
99
|
+
const resourceParams = entityDef.keyMapper(keys, 'update');
|
|
100
|
+
// Merge with body data and additional params
|
|
101
|
+
const mergedParams = {
|
|
102
|
+
...resourceParams.params,
|
|
103
|
+
...params,
|
|
104
|
+
body: data,
|
|
105
|
+
_entityContext: {
|
|
106
|
+
entityId: entityDef.id,
|
|
107
|
+
keys,
|
|
108
|
+
keyFields: entityDef.keySchema.fields
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
return resourceActionFactory.callRequest(entityDef.resourceEndpointId, 'update', mergedParams, path, action.id);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Handle DELETE_ENTITY: path provided, read keys from file attributes
|
|
115
|
+
*/
|
|
116
|
+
function handleDeleteEntity(action, entityDef, resourceActionFactory, state, fsSliceName) {
|
|
117
|
+
const { path, params } = action.payload;
|
|
118
|
+
// Read file node
|
|
119
|
+
const fileNode = selectFileNode(state, path, fsSliceName);
|
|
120
|
+
if (!fileNode) {
|
|
121
|
+
throw new Error(`Entity not found at path: ${JSON.stringify(path)}`);
|
|
122
|
+
}
|
|
123
|
+
// Get entity keys from extension state
|
|
124
|
+
const keys = resourceAttributesUtil.getEntityKeys(fileNode);
|
|
125
|
+
if (!keys) {
|
|
126
|
+
throw new Error(`Entity keys not found in file attributes at: ${JSON.stringify(path)}`);
|
|
127
|
+
}
|
|
128
|
+
// Validate keys
|
|
129
|
+
validateKeys(keys, entityDef.keySchema);
|
|
130
|
+
// Map keys to resource params
|
|
131
|
+
const resourceParams = entityDef.keyMapper(keys, 'delete');
|
|
132
|
+
// Merge with additional params
|
|
133
|
+
const mergedParams = {
|
|
134
|
+
...resourceParams.params,
|
|
135
|
+
...params,
|
|
136
|
+
_entityContext: {
|
|
137
|
+
entityId: entityDef.id,
|
|
138
|
+
keys,
|
|
139
|
+
keyFields: entityDef.keySchema.fields
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
return resourceActionFactory.callRequest(entityDef.resourceEndpointId, 'delete', mergedParams, path, action.id);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Handle CREATE_ENTITY: keys optional (may be generated by server)
|
|
146
|
+
*/
|
|
147
|
+
function handleCreateEntity(action, entityDef, resourceActionFactory) {
|
|
148
|
+
const { keys, data, path, params } = action.payload;
|
|
149
|
+
// Keys are optional for create (server may generate ID)
|
|
150
|
+
let targetPath = path;
|
|
151
|
+
let resourceParams;
|
|
152
|
+
if (keys) {
|
|
153
|
+
validateKeys(keys, entityDef.keySchema, true); // Partial validation
|
|
154
|
+
targetPath = path || generatePath(keys, entityDef);
|
|
155
|
+
resourceParams = entityDef.keyMapper(keys, 'create');
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
resourceParams = { params: {} };
|
|
159
|
+
}
|
|
160
|
+
// Merge with body data and additional params
|
|
161
|
+
const mergedParams = {
|
|
162
|
+
...resourceParams.params,
|
|
163
|
+
...params,
|
|
164
|
+
body: data,
|
|
165
|
+
_entityContext: keys
|
|
166
|
+
? {
|
|
167
|
+
entityId: entityDef.id,
|
|
168
|
+
keys,
|
|
169
|
+
keyFields: entityDef.keySchema.fields
|
|
170
|
+
}
|
|
171
|
+
: undefined
|
|
172
|
+
};
|
|
173
|
+
return resourceActionFactory.callRequest(entityDef.resourceEndpointId, 'create', mergedParams, targetPath, action.id);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Validate entity keys against schema
|
|
177
|
+
*/
|
|
178
|
+
function validateKeys(keys, schema, partial = false) {
|
|
179
|
+
if (!partial) {
|
|
180
|
+
for (const field of schema.fields) {
|
|
181
|
+
if (!(field in keys) || keys[field] === undefined || keys[field] === null) {
|
|
182
|
+
throw new Error(`Missing required key field: ${field}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (schema.validate) {
|
|
187
|
+
const result = schema.validate(keys);
|
|
188
|
+
if (result !== true) {
|
|
189
|
+
throw new Error(typeof result === 'string' ? result : 'Invalid entity keys');
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Generate file path from entity keys
|
|
195
|
+
*/
|
|
196
|
+
function generatePath(keys, entityDef) {
|
|
197
|
+
if (entityDef.pathGenerator) {
|
|
198
|
+
const result = entityDef.pathGenerator(keys, entityDef.id);
|
|
199
|
+
return Array.isArray(result) ? result : [result];
|
|
200
|
+
}
|
|
201
|
+
// Default path generator: ['entities', entityId, ...keyValues]
|
|
202
|
+
const keyValues = entityDef.keySchema.fields.map((field) => String(keys[field]));
|
|
203
|
+
return ['entities', entityDef.id, ...keyValues];
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Select file node from state
|
|
207
|
+
*/
|
|
208
|
+
function selectFileNode(state, path, fsSliceName) {
|
|
209
|
+
const fsState = state[fsSliceName];
|
|
210
|
+
if (!fsState?.root)
|
|
211
|
+
return undefined;
|
|
212
|
+
const pathArray = Array.isArray(path) ? path : [path];
|
|
213
|
+
let currentNode = fsState.root;
|
|
214
|
+
for (const segment of pathArray) {
|
|
215
|
+
if (!currentNode || currentNode.type !== 'directory') {
|
|
216
|
+
return undefined;
|
|
217
|
+
}
|
|
218
|
+
currentNode = currentNode.children?.[segment];
|
|
219
|
+
}
|
|
220
|
+
return currentNode;
|
|
221
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Middleware } from 'redux';
|
|
2
|
+
/**
|
|
3
|
+
* Entity sync middleware
|
|
4
|
+
* Stores entity keys in extension state when resource calls succeed
|
|
5
|
+
*/
|
|
6
|
+
export declare function createEntitySyncMiddleware<S = any>(): Middleware<{}, S>;
|
|
7
|
+
//# sourceMappingURL=entity-sync-middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entity-sync-middleware.d.ts","sourceRoot":"","sources":["../../../src/impl/middleware/entity-sync-middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAOxC;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,CAAC,GAAG,GAAG,KAAK,UAAU,CAAC,EAAE,EAAE,CAAC,CAAC,CAWvE"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { ResourceActionTypes, resourceAttributesUtil } from '../../api';
|
|
2
|
+
/**
|
|
3
|
+
* Entity sync middleware
|
|
4
|
+
* Stores entity keys in extension state when resource calls succeed
|
|
5
|
+
*/
|
|
6
|
+
export function createEntitySyncMiddleware() {
|
|
7
|
+
return (_store) => (next) => (action) => {
|
|
8
|
+
const result = next(action);
|
|
9
|
+
// Listen for resource success actions that originated from entity actions
|
|
10
|
+
if (action.type === ResourceActionTypes.RESOURCE_CALL_SUCCESS) {
|
|
11
|
+
handleEntityResourceSuccess(action);
|
|
12
|
+
}
|
|
13
|
+
return result;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function handleEntityResourceSuccess(action) {
|
|
17
|
+
const { request } = action;
|
|
18
|
+
// Check if this resource action originated from an entity action
|
|
19
|
+
const entityContext = request.payload.params?._entityContext;
|
|
20
|
+
if (!entityContext) {
|
|
21
|
+
return; // Not an entity-originated action
|
|
22
|
+
}
|
|
23
|
+
const { entityId, keys, keyFields } = entityContext;
|
|
24
|
+
const { path: _path } = action.payload;
|
|
25
|
+
// Create entity attributes
|
|
26
|
+
// Entity attributes - reserved for future use when metadata support is added
|
|
27
|
+
void resourceAttributesUtil.createEntityAttributes(entityId, keys, keyFields);
|
|
28
|
+
// TODO: Entity attributes are no longer stored in file extension state
|
|
29
|
+
// The FileSystemAdapter doesn't support updating extension states directly
|
|
30
|
+
// Entity information is now tracked through the file content itself
|
|
31
|
+
// Future: Consider adding entity metadata support to FileSystemAdapter
|
|
32
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Middleware } from 'redux';
|
|
2
|
+
import { type ResourceAction } from '../../api';
|
|
3
|
+
export interface ResourceMiddlewareConfig {
|
|
4
|
+
registry: any;
|
|
5
|
+
onError?: (error: any, action: ResourceAction) => void;
|
|
6
|
+
onSuccess?: (result: any, action: ResourceAction) => void;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Resource middleware
|
|
10
|
+
* Intercepts RESOURCE_CALL_REQUEST actions and executes API calls via providers
|
|
11
|
+
*/
|
|
12
|
+
export declare function createResourceMiddleware<S = any>(config: ResourceMiddlewareConfig): Middleware<{}, S>;
|
|
13
|
+
//# sourceMappingURL=resource-middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource-middleware.d.ts","sourceRoot":"","sources":["../../../src/impl/middleware/resource-middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AACxC,OAAO,EAIL,KAAK,cAAc,EACpB,MAAM,WAAW,CAAC;AAGnB,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,GAAG,CAAC;IACd,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,cAAc,KAAK,IAAI,CAAC;IACvD,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,cAAc,KAAK,IAAI,CAAC;CAC3D;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,GAAG,GAAG,EAC9C,MAAM,EAAE,wBAAwB,GAC/B,UAAU,CAAC,EAAE,EAAE,CAAC,CAAC,CA6EnB"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { ResourceActionTypes, ResourceActionFactory } from '../../api';
|
|
2
|
+
/**
|
|
3
|
+
* Resource middleware
|
|
4
|
+
* Intercepts RESOURCE_CALL_REQUEST actions and executes API calls via providers
|
|
5
|
+
*/
|
|
6
|
+
export function createResourceMiddleware(config) {
|
|
7
|
+
const { registry, onError, onSuccess } = config;
|
|
8
|
+
const actionFactory = new ResourceActionFactory();
|
|
9
|
+
return (store) => (next) => async (action) => {
|
|
10
|
+
// Only handle RESOURCE_CALL_REQUEST actions
|
|
11
|
+
if (action.type !== ResourceActionTypes.RESOURCE_CALL_REQUEST) {
|
|
12
|
+
return next(action);
|
|
13
|
+
}
|
|
14
|
+
// Continue action flow (for sync middleware to set loading state)
|
|
15
|
+
next(action);
|
|
16
|
+
const requestAction = action;
|
|
17
|
+
const { endpointId, operation, params, path } = requestAction.payload;
|
|
18
|
+
try {
|
|
19
|
+
// Lookup endpoint
|
|
20
|
+
const endpointDef = registry.getEndpoint(endpointId);
|
|
21
|
+
if (!endpointDef) {
|
|
22
|
+
throw new Error(`Endpoint not found: ${endpointId}`);
|
|
23
|
+
}
|
|
24
|
+
// Get provider
|
|
25
|
+
const provider = registry.getProvider(endpointDef.providerType);
|
|
26
|
+
if (!provider) {
|
|
27
|
+
throw new Error(`Provider not found: ${endpointDef.providerType}`);
|
|
28
|
+
}
|
|
29
|
+
// Map action to call params (mapper receives entire action)
|
|
30
|
+
const callParams = endpointDef.payloadMapper
|
|
31
|
+
? endpointDef.payloadMapper(requestAction)
|
|
32
|
+
: { params }; // Default: just use params
|
|
33
|
+
// Execute provider call
|
|
34
|
+
const result = await provider.call(endpointDef.endpoint, callParams, operation);
|
|
35
|
+
// Transform response (transformer receives entire action)
|
|
36
|
+
const transformedData = endpointDef.responseTransformer
|
|
37
|
+
? endpointDef.responseTransformer(result.data, result.metadata, requestAction)
|
|
38
|
+
: result.data;
|
|
39
|
+
// Determine storage path
|
|
40
|
+
const storagePath = determinePath(path, endpointDef, params, operation);
|
|
41
|
+
// Dispatch success action
|
|
42
|
+
const successAction = actionFactory.callSuccess(endpointId, storagePath, transformedData, result.metadata, requestAction);
|
|
43
|
+
store.dispatch(successAction);
|
|
44
|
+
onSuccess?.(result, requestAction);
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
// Determine storage path for error state
|
|
48
|
+
const endpointDef = registry.getEndpoint(endpointId);
|
|
49
|
+
const storagePath = determinePath(path, endpointDef, params, operation);
|
|
50
|
+
// Dispatch failure action
|
|
51
|
+
const failureAction = actionFactory.callFailure(endpointId, storagePath, normalizeError(error), requestAction);
|
|
52
|
+
store.dispatch(failureAction);
|
|
53
|
+
onError?.(error, requestAction);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Determine the file path where resource should be stored
|
|
59
|
+
*/
|
|
60
|
+
function determinePath(explicitPath, endpointDef, params, operation) {
|
|
61
|
+
// 1. Use explicit path from action if provided
|
|
62
|
+
if (explicitPath) {
|
|
63
|
+
return explicitPath;
|
|
64
|
+
}
|
|
65
|
+
// 2. Use endpoint's default folder + generate filename
|
|
66
|
+
if (endpointDef?.defaultFolder) {
|
|
67
|
+
const folder = Array.isArray(endpointDef.defaultFolder)
|
|
68
|
+
? endpointDef.defaultFolder
|
|
69
|
+
: [endpointDef.defaultFolder];
|
|
70
|
+
// Generate filename from params or operation
|
|
71
|
+
const filename = generateFilename(params, operation);
|
|
72
|
+
return [...folder, filename];
|
|
73
|
+
}
|
|
74
|
+
// 3. Fallback to root with generated filename
|
|
75
|
+
return [generateFilename(params, operation)];
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Generate filename from params or operation
|
|
79
|
+
*/
|
|
80
|
+
function generateFilename(params, operation) {
|
|
81
|
+
// If params has an id field, use it
|
|
82
|
+
if (params?.id) {
|
|
83
|
+
return String(params.id);
|
|
84
|
+
}
|
|
85
|
+
// Otherwise generate based on operation + timestamp
|
|
86
|
+
return `${operation}-${Date.now()}`;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Normalize error to consistent format
|
|
90
|
+
*/
|
|
91
|
+
function normalizeError(error) {
|
|
92
|
+
return {
|
|
93
|
+
message: error.message || 'Unknown error',
|
|
94
|
+
code: error.code || error.response?.status?.toString(),
|
|
95
|
+
details: error.response?.data || error
|
|
96
|
+
};
|
|
97
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Middleware } from 'redux';
|
|
2
|
+
import type { FileSystemNodeActions } from '@hamak/ui-store-impl';
|
|
3
|
+
export interface SyncMiddlewareConfig {
|
|
4
|
+
/** FileSystem actions from FileSystemAdapter */
|
|
5
|
+
fsActions: FileSystemNodeActions;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Sync middleware
|
|
9
|
+
* Updates file system state when resource actions complete
|
|
10
|
+
*/
|
|
11
|
+
export declare function createSyncMiddleware<S = any>(config: SyncMiddlewareConfig): Middleware<{}, S>;
|
|
12
|
+
//# sourceMappingURL=sync-middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-middleware.d.ts","sourceRoot":"","sources":["../../../src/impl/middleware/sync-middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAOxC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAGlE,MAAM,WAAW,oBAAoB;IACnC,gDAAgD;IAChD,SAAS,EAAE,qBAAqB,CAAC;CAClC;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,GAAG,GAAG,EAC1C,MAAM,EAAE,oBAAoB,GAC3B,UAAU,CAAC,EAAE,EAAE,CAAC,CAAC,CAwBnB"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { ResourceActionTypes } from '../../api';
|
|
2
|
+
import { Pathway } from '@hamak/navigation-utils';
|
|
3
|
+
/**
|
|
4
|
+
* Sync middleware
|
|
5
|
+
* Updates file system state when resource actions complete
|
|
6
|
+
*/
|
|
7
|
+
export function createSyncMiddleware(config) {
|
|
8
|
+
const { fsActions } = config;
|
|
9
|
+
return (store) => (next) => (action) => {
|
|
10
|
+
// Pass through all actions first
|
|
11
|
+
const result = next(action);
|
|
12
|
+
// Handle request action (set loading state)
|
|
13
|
+
if (action.type === ResourceActionTypes.RESOURCE_CALL_REQUEST) {
|
|
14
|
+
handleRequestAction(store, action, fsActions);
|
|
15
|
+
}
|
|
16
|
+
// Handle success action (update content)
|
|
17
|
+
if (action.type === ResourceActionTypes.RESOURCE_CALL_SUCCESS) {
|
|
18
|
+
handleSuccessAction(store, action, fsActions);
|
|
19
|
+
}
|
|
20
|
+
// Handle failure action (set error state)
|
|
21
|
+
if (action.type === ResourceActionTypes.RESOURCE_CALL_FAILURE) {
|
|
22
|
+
handleFailureAction(store, action, fsActions);
|
|
23
|
+
}
|
|
24
|
+
return result;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function handleRequestAction(store, action, fsActions) {
|
|
28
|
+
const { path } = action.payload;
|
|
29
|
+
if (!path) {
|
|
30
|
+
// Loading state will be set when success/failure with resolved path
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
// Normalize path using Pathway utility
|
|
34
|
+
const pathway = Pathway.of(path);
|
|
35
|
+
const parentPathway = pathway.getParent();
|
|
36
|
+
const parentSegments = parentPathway.getSegments();
|
|
37
|
+
// Ensure parent directory exists
|
|
38
|
+
if (parentSegments.length > 0) {
|
|
39
|
+
store.dispatch(fsActions.mkdir(parentSegments, true));
|
|
40
|
+
}
|
|
41
|
+
// Set loading state by creating file node with contentIsPresent=false
|
|
42
|
+
store.dispatch(fsActions.setFile(path, null, 'xs:any', { override: false, contentIsPresent: false }));
|
|
43
|
+
}
|
|
44
|
+
function handleSuccessAction(store, action, fsActions) {
|
|
45
|
+
const { path, data } = action.payload;
|
|
46
|
+
const { request } = action;
|
|
47
|
+
// Normalize path using Pathway utility
|
|
48
|
+
const pathway = Pathway.of(path);
|
|
49
|
+
const parentPathway = pathway.getParent();
|
|
50
|
+
const parentSegments = parentPathway.getSegments();
|
|
51
|
+
// Ensure parent directory exists
|
|
52
|
+
if (parentSegments.length > 0) {
|
|
53
|
+
store.dispatch(fsActions.mkdir(parentSegments, true));
|
|
54
|
+
}
|
|
55
|
+
// Set file content from remote (automatically sets contentLoaded=true and clears loading)
|
|
56
|
+
// Schema should come from endpoint definition if available
|
|
57
|
+
const schema = request.meta?.schema || 'xs:any';
|
|
58
|
+
// First ensure file exists with proper schema
|
|
59
|
+
store.dispatch(fsActions.setFile(path, data, schema, { override: true, contentIsPresent: true }));
|
|
60
|
+
// Then set content from remote to mark it as loaded from remote source
|
|
61
|
+
store.dispatch(fsActions.setFileContent(path, data, true));
|
|
62
|
+
}
|
|
63
|
+
function handleFailureAction(store, action, fsActions) {
|
|
64
|
+
const { path, error } = action.payload;
|
|
65
|
+
if (!path) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
// Normalize path using Pathway utility
|
|
69
|
+
const pathway = Pathway.of(path);
|
|
70
|
+
const parentPathway = pathway.getParent();
|
|
71
|
+
const parentSegments = parentPathway.getSegments();
|
|
72
|
+
// Ensure parent directory exists
|
|
73
|
+
if (parentSegments.length > 0) {
|
|
74
|
+
store.dispatch(fsActions.mkdir(parentSegments, true));
|
|
75
|
+
}
|
|
76
|
+
// For now, just ensure the file exists and set content to null/error placeholder
|
|
77
|
+
// TODO: FileSystemAdapter doesn't currently support setting error state directly
|
|
78
|
+
// We might need to extend it or handle errors differently
|
|
79
|
+
store.dispatch(fsActions.setFile(path, { error }, 'xs:any', { override: true, contentIsPresent: true }));
|
|
80
|
+
}
|