@hamak/ui-remote-resource-impl 0.4.19

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 (41) hide show
  1. package/dist/es2015/index.js +33 -0
  2. package/dist/es2015/middleware/entity-middleware.js +209 -0
  3. package/dist/es2015/middleware/entity-sync-middleware.js +36 -0
  4. package/dist/es2015/middleware/resource-middleware.js +111 -0
  5. package/dist/es2015/middleware/sync-middleware.js +85 -0
  6. package/dist/es2015/plugin/resource-plugin-factory.js +151 -0
  7. package/dist/es2015/providers/mock-resource-provider.js +215 -0
  8. package/dist/es2015/providers/rest-resource-provider.js +140 -0
  9. package/dist/es2015/registry/entity-registry.js +50 -0
  10. package/dist/es2015/registry/resource-registry.js +68 -0
  11. package/dist/index.d.ts +18 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +16 -0
  14. package/dist/middleware/entity-middleware.d.ts +13 -0
  15. package/dist/middleware/entity-middleware.d.ts.map +1 -0
  16. package/dist/middleware/entity-middleware.js +221 -0
  17. package/dist/middleware/entity-sync-middleware.d.ts +7 -0
  18. package/dist/middleware/entity-sync-middleware.d.ts.map +1 -0
  19. package/dist/middleware/entity-sync-middleware.js +31 -0
  20. package/dist/middleware/resource-middleware.d.ts +13 -0
  21. package/dist/middleware/resource-middleware.d.ts.map +1 -0
  22. package/dist/middleware/resource-middleware.js +97 -0
  23. package/dist/middleware/sync-middleware.d.ts +12 -0
  24. package/dist/middleware/sync-middleware.d.ts.map +1 -0
  25. package/dist/middleware/sync-middleware.js +80 -0
  26. package/dist/plugin/resource-plugin-factory.d.ts +31 -0
  27. package/dist/plugin/resource-plugin-factory.d.ts.map +1 -0
  28. package/dist/plugin/resource-plugin-factory.js +131 -0
  29. package/dist/providers/mock-resource-provider.d.ts +147 -0
  30. package/dist/providers/mock-resource-provider.d.ts.map +1 -0
  31. package/dist/providers/mock-resource-provider.js +196 -0
  32. package/dist/providers/rest-resource-provider.d.ts +51 -0
  33. package/dist/providers/rest-resource-provider.d.ts.map +1 -0
  34. package/dist/providers/rest-resource-provider.js +117 -0
  35. package/dist/registry/entity-registry.d.ts +54 -0
  36. package/dist/registry/entity-registry.d.ts.map +1 -0
  37. package/dist/registry/entity-registry.js +46 -0
  38. package/dist/registry/resource-registry.d.ts +80 -0
  39. package/dist/registry/resource-registry.d.ts.map +1 -0
  40. package/dist/registry/resource-registry.js +64 -0
  41. package/package.json +57 -0
@@ -0,0 +1,221 @@
1
+ import { EntityActionTypes, ResourceActionFactory, resourceAttributesUtil } from '@hamak/ui-remote-resource-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/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,31 @@
1
+ import { ResourceActionTypes, resourceAttributesUtil } from '@hamak/ui-remote-resource-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(store, action);
12
+ }
13
+ return result;
14
+ };
15
+ }
16
+ function handleEntityResourceSuccess(store, 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 } = action.payload;
25
+ // Create entity attributes
26
+ const entityAttrs = resourceAttributesUtil.createEntityAttributes(entityId, keys, keyFields);
27
+ // TODO: Entity attributes are no longer stored in file extension state
28
+ // The FileSystemAdapter doesn't support updating extension states directly
29
+ // Entity information is now tracked through the file content itself
30
+ // Future: Consider adding entity metadata support to FileSystemAdapter
31
+ }
@@ -0,0 +1,13 @@
1
+ import type { Middleware } from 'redux';
2
+ import { type ResourceAction } from '@hamak/ui-remote-resource-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/middleware/resource-middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AACxC,OAAO,EAIL,KAAK,cAAc,EACpB,MAAM,+BAA+B,CAAC;AAGvC,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 '@hamak/ui-remote-resource-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/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 '@hamak/ui-remote-resource-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
+ }
@@ -0,0 +1,31 @@
1
+ import type { ResourcePluginConfig } from '@hamak/ui-remote-resource-spi';
2
+ /**
3
+ * Microkernel plugin module interface
4
+ */
5
+ export interface PluginModule {
6
+ initialize(ctx: InitializationContext): Promise<void>;
7
+ activate(ctx: ActivateContext): Promise<void>;
8
+ deactivate?(): Promise<void>;
9
+ }
10
+ interface InitializationContext {
11
+ provide(provider: any): void;
12
+ [key: string]: any;
13
+ }
14
+ interface ActivateContext {
15
+ resolve(token: any): any;
16
+ hooks: {
17
+ emit(event: string, data?: any): void;
18
+ };
19
+ [key: string]: any;
20
+ }
21
+ export declare const RESOURCE_REGISTRY_TOKEN = "RESOURCE_REGISTRY";
22
+ export declare const ENTITY_REGISTRY_TOKEN = "ENTITY_REGISTRY";
23
+ /**
24
+ * Create remote resource plugin
25
+ *
26
+ * @param config - Plugin configuration
27
+ * @returns Plugin module for microkernel registration
28
+ */
29
+ export declare function createResourcePlugin(config?: ResourcePluginConfig): PluginModule;
30
+ export {};
31
+ //# sourceMappingURL=resource-plugin-factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-plugin-factory.d.ts","sourceRoot":"","sources":["../../src/plugin/resource-plugin-factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAgB1E;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,UAAU,CAAC,GAAG,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,QAAQ,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,UAAU,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED,UAAU,qBAAqB;IAC7B,OAAO,CAAC,QAAQ,EAAE,GAAG,GAAG,IAAI,CAAC;IAC7B,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,UAAU,eAAe;IACvB,OAAO,CAAC,KAAK,EAAE,GAAG,GAAG,GAAG,CAAC;IACzB,KAAK,EAAE;QACL,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC;KACvC,CAAC;IACF,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAGD,eAAO,MAAM,uBAAuB,sBAAsB,CAAC;AAC3D,eAAO,MAAM,qBAAqB,oBAAoB,CAAC;AAEvD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,GAAE,oBAAyB,GAChC,YAAY,CAgId"}
@@ -0,0 +1,131 @@
1
+ import { MIDDLEWARE_REGISTRY_TOKEN } from '@hamak/ui-store-api';
2
+ import { FILESYSTEM_ADAPTER_TOKEN } from '@hamak/ui-store-impl';
3
+ import { ResourceRegistry } from '../registry/resource-registry';
4
+ import { EntityRegistry } from '../registry/entity-registry';
5
+ import { RestResourceProvider } from '../providers/rest-resource-provider';
6
+ import { MockResourceProvider } from '../providers/mock-resource-provider';
7
+ import { createResourceMiddleware } from '../middleware/resource-middleware';
8
+ import { createSyncMiddleware } from '../middleware/sync-middleware';
9
+ import { createEntityMiddleware } from '../middleware/entity-middleware';
10
+ import { createEntitySyncMiddleware } from '../middleware/entity-sync-middleware';
11
+ // Dependency injection tokens (for local use)
12
+ export const RESOURCE_REGISTRY_TOKEN = 'RESOURCE_REGISTRY';
13
+ export const ENTITY_REGISTRY_TOKEN = 'ENTITY_REGISTRY';
14
+ /**
15
+ * Create remote resource plugin
16
+ *
17
+ * @param config - Plugin configuration
18
+ * @returns Plugin module for microkernel registration
19
+ */
20
+ export function createResourcePlugin(config = {}) {
21
+ const resourceRegistry = new ResourceRegistry();
22
+ const entityRegistry = new EntityRegistry();
23
+ return {
24
+ async initialize(ctx) {
25
+ // Register default providers
26
+ const restProvider = new RestResourceProvider();
27
+ const mockProvider = new MockResourceProvider();
28
+ resourceRegistry.registerProvider(restProvider);
29
+ resourceRegistry.registerProvider(mockProvider);
30
+ // Register custom providers
31
+ if (config.providers) {
32
+ config.providers.forEach((provider) => {
33
+ resourceRegistry.registerProvider(provider);
34
+ });
35
+ }
36
+ // Register endpoints
37
+ if (config.endpoints) {
38
+ config.endpoints.forEach((endpoint) => {
39
+ resourceRegistry.registerEndpoint(endpoint);
40
+ });
41
+ }
42
+ // Register entities
43
+ if (config.entities) {
44
+ config.entities.forEach((entity) => {
45
+ entityRegistry.registerEntity(entity);
46
+ });
47
+ }
48
+ // Provide registries via DI
49
+ ctx.provide({
50
+ provide: RESOURCE_REGISTRY_TOKEN,
51
+ useValue: resourceRegistry
52
+ });
53
+ ctx.provide({
54
+ provide: ENTITY_REGISTRY_TOKEN,
55
+ useValue: entityRegistry
56
+ });
57
+ // Register middleware during initialization
58
+ try {
59
+ // Access resolve method if available on context
60
+ const resolve = ctx.resolve;
61
+ if (resolve) {
62
+ // Resolve FileSystemAdapter from DI
63
+ const fsAdapter = resolve(FILESYSTEM_ADAPTER_TOKEN);
64
+ if (!fsAdapter) {
65
+ console.warn('[ui-remote-resource] FileSystemAdapter not available in DI');
66
+ return;
67
+ }
68
+ const fsActions = fsAdapter.getActions();
69
+ const fsSliceName = fsAdapter.sliceName;
70
+ // Create middleware with FileSystemAdapter actions
71
+ const entityMiddleware = createEntityMiddleware({
72
+ entityRegistry,
73
+ fsSliceName,
74
+ onError: config.onError
75
+ });
76
+ const resourceMiddleware = createResourceMiddleware({
77
+ registry: resourceRegistry,
78
+ onError: config.onError,
79
+ onSuccess: config.onSuccess
80
+ });
81
+ const entitySyncMiddleware = createEntitySyncMiddleware();
82
+ const resourceSyncMiddleware = createSyncMiddleware({
83
+ fsActions
84
+ });
85
+ const middlewareRegistry = resolve(MIDDLEWARE_REGISTRY_TOKEN);
86
+ middlewareRegistry.register({
87
+ id: 'entity-middleware',
88
+ middleware: entityMiddleware,
89
+ priority: 110,
90
+ plugin: 'ui-remote-resource'
91
+ });
92
+ middlewareRegistry.register({
93
+ id: 'resource-middleware',
94
+ middleware: resourceMiddleware,
95
+ priority: 100,
96
+ plugin: 'ui-remote-resource'
97
+ });
98
+ middlewareRegistry.register({
99
+ id: 'entity-sync-middleware',
100
+ middleware: entitySyncMiddleware,
101
+ priority: 60,
102
+ plugin: 'ui-remote-resource'
103
+ });
104
+ middlewareRegistry.register({
105
+ id: 'resource-sync-middleware',
106
+ middleware: resourceSyncMiddleware,
107
+ priority: 50,
108
+ plugin: 'ui-remote-resource'
109
+ });
110
+ }
111
+ else {
112
+ console.warn('Resolve method not available in initialization context');
113
+ }
114
+ }
115
+ catch (error) {
116
+ console.warn('Failed to register middleware during initialization:', error);
117
+ }
118
+ },
119
+ async activate(ctx) {
120
+ // Emit ready event
121
+ ctx.hooks?.emit('ui-remote-resource:ready', {
122
+ resourceRegistry,
123
+ entityRegistry
124
+ });
125
+ },
126
+ async deactivate() {
127
+ // Cleanup if needed
128
+ // For now, no cleanup required
129
+ }
130
+ };
131
+ }