@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.
- package/dist/es2015/index.js +33 -0
- package/dist/es2015/middleware/entity-middleware.js +209 -0
- package/dist/es2015/middleware/entity-sync-middleware.js +36 -0
- package/dist/es2015/middleware/resource-middleware.js +111 -0
- package/dist/es2015/middleware/sync-middleware.js +85 -0
- package/dist/es2015/plugin/resource-plugin-factory.js +151 -0
- package/dist/es2015/providers/mock-resource-provider.js +215 -0
- package/dist/es2015/providers/rest-resource-provider.js +140 -0
- package/dist/es2015/registry/entity-registry.js +50 -0
- package/dist/es2015/registry/resource-registry.js +68 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/middleware/entity-middleware.d.ts +13 -0
- package/dist/middleware/entity-middleware.d.ts.map +1 -0
- package/dist/middleware/entity-middleware.js +221 -0
- package/dist/middleware/entity-sync-middleware.d.ts +7 -0
- package/dist/middleware/entity-sync-middleware.d.ts.map +1 -0
- package/dist/middleware/entity-sync-middleware.js +31 -0
- package/dist/middleware/resource-middleware.d.ts +13 -0
- package/dist/middleware/resource-middleware.d.ts.map +1 -0
- package/dist/middleware/resource-middleware.js +97 -0
- package/dist/middleware/sync-middleware.d.ts +12 -0
- package/dist/middleware/sync-middleware.d.ts.map +1 -0
- package/dist/middleware/sync-middleware.js +80 -0
- package/dist/plugin/resource-plugin-factory.d.ts +31 -0
- package/dist/plugin/resource-plugin-factory.d.ts.map +1 -0
- package/dist/plugin/resource-plugin-factory.js +131 -0
- package/dist/providers/mock-resource-provider.d.ts +147 -0
- package/dist/providers/mock-resource-provider.d.ts.map +1 -0
- package/dist/providers/mock-resource-provider.js +196 -0
- package/dist/providers/rest-resource-provider.d.ts +51 -0
- package/dist/providers/rest-resource-provider.d.ts.map +1 -0
- package/dist/providers/rest-resource-provider.js +117 -0
- package/dist/registry/entity-registry.d.ts +54 -0
- package/dist/registry/entity-registry.d.ts.map +1 -0
- package/dist/registry/entity-registry.js +46 -0
- package/dist/registry/resource-registry.d.ts +80 -0
- package/dist/registry/resource-registry.d.ts.map +1 -0
- package/dist/registry/resource-registry.js +64 -0
- package/package.json +57 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createEntitySyncMiddleware = exports.createEntityMiddleware = exports.createSyncMiddleware = exports.createResourceMiddleware = exports.EntityRegistry = exports.ResourceRegistry = exports.MockResourceProvider = exports.RestResourceProvider = exports.STORE_EXTENSIONS_TOKEN = exports.MIDDLEWARE_REGISTRY_TOKEN = exports.STORE_MANAGER_TOKEN = exports.ENTITY_REGISTRY_TOKEN = exports.RESOURCE_REGISTRY_TOKEN = exports.createResourcePlugin = void 0;
|
|
4
|
+
// Plugin factory (main export)
|
|
5
|
+
var resource_plugin_factory_1 = require("./plugin/resource-plugin-factory");
|
|
6
|
+
Object.defineProperty(exports, "createResourcePlugin", { enumerable: true, get: function () { return resource_plugin_factory_1.createResourcePlugin; } });
|
|
7
|
+
var resource_plugin_factory_2 = require("./plugin/resource-plugin-factory");
|
|
8
|
+
Object.defineProperty(exports, "RESOURCE_REGISTRY_TOKEN", { enumerable: true, get: function () { return resource_plugin_factory_2.RESOURCE_REGISTRY_TOKEN; } });
|
|
9
|
+
Object.defineProperty(exports, "ENTITY_REGISTRY_TOKEN", { enumerable: true, get: function () { return resource_plugin_factory_2.ENTITY_REGISTRY_TOKEN; } });
|
|
10
|
+
// Re-export store tokens from ui-store-api for convenience
|
|
11
|
+
var ui_store_api_1 = require("@hamak/ui-store-api");
|
|
12
|
+
Object.defineProperty(exports, "STORE_MANAGER_TOKEN", { enumerable: true, get: function () { return ui_store_api_1.STORE_MANAGER_TOKEN; } });
|
|
13
|
+
Object.defineProperty(exports, "MIDDLEWARE_REGISTRY_TOKEN", { enumerable: true, get: function () { return ui_store_api_1.MIDDLEWARE_REGISTRY_TOKEN; } });
|
|
14
|
+
Object.defineProperty(exports, "STORE_EXTENSIONS_TOKEN", { enumerable: true, get: function () { return ui_store_api_1.STORE_EXTENSIONS_TOKEN; } });
|
|
15
|
+
// Built-in providers (exported for convenience and testing)
|
|
16
|
+
var rest_resource_provider_1 = require("./providers/rest-resource-provider");
|
|
17
|
+
Object.defineProperty(exports, "RestResourceProvider", { enumerable: true, get: function () { return rest_resource_provider_1.RestResourceProvider; } });
|
|
18
|
+
var mock_resource_provider_1 = require("./providers/mock-resource-provider");
|
|
19
|
+
Object.defineProperty(exports, "MockResourceProvider", { enumerable: true, get: function () { return mock_resource_provider_1.MockResourceProvider; } });
|
|
20
|
+
// Registry implementations (internal, but exported for testing)
|
|
21
|
+
var resource_registry_1 = require("./registry/resource-registry");
|
|
22
|
+
Object.defineProperty(exports, "ResourceRegistry", { enumerable: true, get: function () { return resource_registry_1.ResourceRegistry; } });
|
|
23
|
+
var entity_registry_1 = require("./registry/entity-registry");
|
|
24
|
+
Object.defineProperty(exports, "EntityRegistry", { enumerable: true, get: function () { return entity_registry_1.EntityRegistry; } });
|
|
25
|
+
// Middleware factories (internal, but exported for advanced use cases)
|
|
26
|
+
var resource_middleware_1 = require("./middleware/resource-middleware");
|
|
27
|
+
Object.defineProperty(exports, "createResourceMiddleware", { enumerable: true, get: function () { return resource_middleware_1.createResourceMiddleware; } });
|
|
28
|
+
var sync_middleware_1 = require("./middleware/sync-middleware");
|
|
29
|
+
Object.defineProperty(exports, "createSyncMiddleware", { enumerable: true, get: function () { return sync_middleware_1.createSyncMiddleware; } });
|
|
30
|
+
var entity_middleware_1 = require("./middleware/entity-middleware");
|
|
31
|
+
Object.defineProperty(exports, "createEntityMiddleware", { enumerable: true, get: function () { return entity_middleware_1.createEntityMiddleware; } });
|
|
32
|
+
var entity_sync_middleware_1 = require("./middleware/entity-sync-middleware");
|
|
33
|
+
Object.defineProperty(exports, "createEntitySyncMiddleware", { enumerable: true, get: function () { return entity_sync_middleware_1.createEntitySyncMiddleware; } });
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createEntityMiddleware = void 0;
|
|
4
|
+
const ui_remote_resource_api_1 = require("@hamak/ui-remote-resource-api");
|
|
5
|
+
/**
|
|
6
|
+
* Entity middleware
|
|
7
|
+
* Translates entity actions to resource actions
|
|
8
|
+
*/
|
|
9
|
+
function createEntityMiddleware(config) {
|
|
10
|
+
const { entityRegistry, fsSliceName, onError } = config;
|
|
11
|
+
const resourceActionFactory = new ui_remote_resource_api_1.ResourceActionFactory();
|
|
12
|
+
return (store) => (next) => (action) => {
|
|
13
|
+
// Only handle entity actions
|
|
14
|
+
if (!isEntityAction(action)) {
|
|
15
|
+
return next(action);
|
|
16
|
+
}
|
|
17
|
+
// Pass through for tracking
|
|
18
|
+
next(action);
|
|
19
|
+
const entityAction = action;
|
|
20
|
+
try {
|
|
21
|
+
// Translate entity action to resource action
|
|
22
|
+
const resourceAction = translateEntityAction(entityAction, entityRegistry, resourceActionFactory, store.getState(), fsSliceName);
|
|
23
|
+
// Dispatch resource action
|
|
24
|
+
store.dispatch(resourceAction);
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
onError === null || onError === void 0 ? void 0 : onError(error, entityAction);
|
|
28
|
+
console.error('Entity middleware error:', error);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
exports.createEntityMiddleware = createEntityMiddleware;
|
|
33
|
+
function isEntityAction(action) {
|
|
34
|
+
return [
|
|
35
|
+
ui_remote_resource_api_1.EntityActionTypes.FETCH_ENTITY,
|
|
36
|
+
ui_remote_resource_api_1.EntityActionTypes.UPDATE_ENTITY,
|
|
37
|
+
ui_remote_resource_api_1.EntityActionTypes.DELETE_ENTITY,
|
|
38
|
+
ui_remote_resource_api_1.EntityActionTypes.CREATE_ENTITY
|
|
39
|
+
].includes(action.type);
|
|
40
|
+
}
|
|
41
|
+
function translateEntityAction(entityAction, entityRegistry, resourceActionFactory, state, fsSliceName) {
|
|
42
|
+
const { entityId } = entityAction.payload;
|
|
43
|
+
// Get entity definition
|
|
44
|
+
const entityDef = entityRegistry.getEntity(entityId);
|
|
45
|
+
if (!entityDef) {
|
|
46
|
+
throw new Error(`Entity not found: ${entityId}`);
|
|
47
|
+
}
|
|
48
|
+
switch (entityAction.type) {
|
|
49
|
+
case ui_remote_resource_api_1.EntityActionTypes.FETCH_ENTITY:
|
|
50
|
+
return handleFetchEntity(entityAction, entityDef, resourceActionFactory);
|
|
51
|
+
case ui_remote_resource_api_1.EntityActionTypes.UPDATE_ENTITY:
|
|
52
|
+
return handleUpdateEntity(entityAction, entityDef, resourceActionFactory, state, fsSliceName);
|
|
53
|
+
case ui_remote_resource_api_1.EntityActionTypes.DELETE_ENTITY:
|
|
54
|
+
return handleDeleteEntity(entityAction, entityDef, resourceActionFactory, state, fsSliceName);
|
|
55
|
+
case ui_remote_resource_api_1.EntityActionTypes.CREATE_ENTITY:
|
|
56
|
+
return handleCreateEntity(entityAction, entityDef, resourceActionFactory);
|
|
57
|
+
default:
|
|
58
|
+
throw new Error(`Unknown entity action type: ${entityAction.type}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Handle FETCH_ENTITY: keys provided, generate path and params
|
|
63
|
+
*/
|
|
64
|
+
function handleFetchEntity(action, entityDef, resourceActionFactory) {
|
|
65
|
+
const { keys, params, path } = action.payload;
|
|
66
|
+
// Validate keys
|
|
67
|
+
validateKeys(keys, entityDef.keySchema);
|
|
68
|
+
// Generate path from keys (if not explicitly provided)
|
|
69
|
+
const targetPath = path || generatePath(keys, entityDef);
|
|
70
|
+
// Map keys to resource params
|
|
71
|
+
const resourceParams = entityDef.keyMapper(keys, 'fetch');
|
|
72
|
+
// Merge with additional params
|
|
73
|
+
const mergedParams = Object.assign(Object.assign(Object.assign({}, resourceParams.params), params), {
|
|
74
|
+
// Store entity context for entity sync middleware
|
|
75
|
+
_entityContext: {
|
|
76
|
+
entityId: entityDef.id,
|
|
77
|
+
keys,
|
|
78
|
+
keyFields: entityDef.keySchema.fields
|
|
79
|
+
} });
|
|
80
|
+
return resourceActionFactory.callRequest(entityDef.resourceEndpointId, 'fetch', mergedParams, targetPath, action.id);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Handle UPDATE_ENTITY: path provided, read keys from file attributes
|
|
84
|
+
*/
|
|
85
|
+
function handleUpdateEntity(action, entityDef, resourceActionFactory, state, fsSliceName) {
|
|
86
|
+
const { path, data, params } = action.payload;
|
|
87
|
+
// Read file node
|
|
88
|
+
const fileNode = selectFileNode(state, path, fsSliceName);
|
|
89
|
+
if (!fileNode) {
|
|
90
|
+
throw new Error(`Entity not found at path: ${JSON.stringify(path)}`);
|
|
91
|
+
}
|
|
92
|
+
// Get entity keys from extension state
|
|
93
|
+
const keys = ui_remote_resource_api_1.resourceAttributesUtil.getEntityKeys(fileNode);
|
|
94
|
+
if (!keys) {
|
|
95
|
+
throw new Error(`Entity keys not found in file attributes at: ${JSON.stringify(path)}`);
|
|
96
|
+
}
|
|
97
|
+
// Validate keys
|
|
98
|
+
validateKeys(keys, entityDef.keySchema);
|
|
99
|
+
// Map keys to resource params
|
|
100
|
+
const resourceParams = entityDef.keyMapper(keys, 'update');
|
|
101
|
+
// Merge with body data and additional params
|
|
102
|
+
const mergedParams = Object.assign(Object.assign(Object.assign({}, resourceParams.params), params), { body: data, _entityContext: {
|
|
103
|
+
entityId: entityDef.id,
|
|
104
|
+
keys,
|
|
105
|
+
keyFields: entityDef.keySchema.fields
|
|
106
|
+
} });
|
|
107
|
+
return resourceActionFactory.callRequest(entityDef.resourceEndpointId, 'update', mergedParams, path, action.id);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Handle DELETE_ENTITY: path provided, read keys from file attributes
|
|
111
|
+
*/
|
|
112
|
+
function handleDeleteEntity(action, entityDef, resourceActionFactory, state, fsSliceName) {
|
|
113
|
+
const { path, params } = action.payload;
|
|
114
|
+
// Read file node
|
|
115
|
+
const fileNode = selectFileNode(state, path, fsSliceName);
|
|
116
|
+
if (!fileNode) {
|
|
117
|
+
throw new Error(`Entity not found at path: ${JSON.stringify(path)}`);
|
|
118
|
+
}
|
|
119
|
+
// Get entity keys from extension state
|
|
120
|
+
const keys = ui_remote_resource_api_1.resourceAttributesUtil.getEntityKeys(fileNode);
|
|
121
|
+
if (!keys) {
|
|
122
|
+
throw new Error(`Entity keys not found in file attributes at: ${JSON.stringify(path)}`);
|
|
123
|
+
}
|
|
124
|
+
// Validate keys
|
|
125
|
+
validateKeys(keys, entityDef.keySchema);
|
|
126
|
+
// Map keys to resource params
|
|
127
|
+
const resourceParams = entityDef.keyMapper(keys, 'delete');
|
|
128
|
+
// Merge with additional params
|
|
129
|
+
const mergedParams = Object.assign(Object.assign(Object.assign({}, resourceParams.params), params), { _entityContext: {
|
|
130
|
+
entityId: entityDef.id,
|
|
131
|
+
keys,
|
|
132
|
+
keyFields: entityDef.keySchema.fields
|
|
133
|
+
} });
|
|
134
|
+
return resourceActionFactory.callRequest(entityDef.resourceEndpointId, 'delete', mergedParams, path, action.id);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Handle CREATE_ENTITY: keys optional (may be generated by server)
|
|
138
|
+
*/
|
|
139
|
+
function handleCreateEntity(action, entityDef, resourceActionFactory) {
|
|
140
|
+
const { keys, data, path, params } = action.payload;
|
|
141
|
+
// Keys are optional for create (server may generate ID)
|
|
142
|
+
let targetPath = path;
|
|
143
|
+
let resourceParams;
|
|
144
|
+
if (keys) {
|
|
145
|
+
validateKeys(keys, entityDef.keySchema, true); // Partial validation
|
|
146
|
+
targetPath = path || generatePath(keys, entityDef);
|
|
147
|
+
resourceParams = entityDef.keyMapper(keys, 'create');
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
resourceParams = { params: {} };
|
|
151
|
+
}
|
|
152
|
+
// Merge with body data and additional params
|
|
153
|
+
const mergedParams = Object.assign(Object.assign(Object.assign({}, resourceParams.params), params), { body: data, _entityContext: keys
|
|
154
|
+
? {
|
|
155
|
+
entityId: entityDef.id,
|
|
156
|
+
keys,
|
|
157
|
+
keyFields: entityDef.keySchema.fields
|
|
158
|
+
}
|
|
159
|
+
: undefined });
|
|
160
|
+
return resourceActionFactory.callRequest(entityDef.resourceEndpointId, 'create', mergedParams, targetPath, action.id);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Validate entity keys against schema
|
|
164
|
+
*/
|
|
165
|
+
function validateKeys(keys, schema, partial = false) {
|
|
166
|
+
if (!partial) {
|
|
167
|
+
for (const field of schema.fields) {
|
|
168
|
+
if (!(field in keys) || keys[field] === undefined || keys[field] === null) {
|
|
169
|
+
throw new Error(`Missing required key field: ${field}`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (schema.validate) {
|
|
174
|
+
const result = schema.validate(keys);
|
|
175
|
+
if (result !== true) {
|
|
176
|
+
throw new Error(typeof result === 'string' ? result : 'Invalid entity keys');
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Generate file path from entity keys
|
|
182
|
+
*/
|
|
183
|
+
function generatePath(keys, entityDef) {
|
|
184
|
+
if (entityDef.pathGenerator) {
|
|
185
|
+
const result = entityDef.pathGenerator(keys, entityDef.id);
|
|
186
|
+
return Array.isArray(result) ? result : [result];
|
|
187
|
+
}
|
|
188
|
+
// Default path generator: ['entities', entityId, ...keyValues]
|
|
189
|
+
const keyValues = entityDef.keySchema.fields.map((field) => String(keys[field]));
|
|
190
|
+
return ['entities', entityDef.id, ...keyValues];
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Select file node from state
|
|
194
|
+
*/
|
|
195
|
+
function selectFileNode(state, path, fsSliceName) {
|
|
196
|
+
var _a;
|
|
197
|
+
const fsState = state[fsSliceName];
|
|
198
|
+
if (!(fsState === null || fsState === void 0 ? void 0 : fsState.root))
|
|
199
|
+
return undefined;
|
|
200
|
+
const pathArray = Array.isArray(path) ? path : [path];
|
|
201
|
+
let currentNode = fsState.root;
|
|
202
|
+
for (const segment of pathArray) {
|
|
203
|
+
if (!currentNode || currentNode.type !== 'directory') {
|
|
204
|
+
return undefined;
|
|
205
|
+
}
|
|
206
|
+
currentNode = (_a = currentNode.children) === null || _a === void 0 ? void 0 : _a[segment];
|
|
207
|
+
}
|
|
208
|
+
return currentNode;
|
|
209
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createEntitySyncMiddleware = void 0;
|
|
4
|
+
const ui_remote_resource_api_1 = require("@hamak/ui-remote-resource-api");
|
|
5
|
+
/**
|
|
6
|
+
* Entity sync middleware
|
|
7
|
+
* Stores entity keys in extension state when resource calls succeed
|
|
8
|
+
*/
|
|
9
|
+
function createEntitySyncMiddleware() {
|
|
10
|
+
return (store) => (next) => (action) => {
|
|
11
|
+
const result = next(action);
|
|
12
|
+
// Listen for resource success actions that originated from entity actions
|
|
13
|
+
if (action.type === ui_remote_resource_api_1.ResourceActionTypes.RESOURCE_CALL_SUCCESS) {
|
|
14
|
+
handleEntityResourceSuccess(store, action);
|
|
15
|
+
}
|
|
16
|
+
return result;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
exports.createEntitySyncMiddleware = createEntitySyncMiddleware;
|
|
20
|
+
function handleEntityResourceSuccess(store, action) {
|
|
21
|
+
var _a;
|
|
22
|
+
const { request } = action;
|
|
23
|
+
// Check if this resource action originated from an entity action
|
|
24
|
+
const entityContext = (_a = request.payload.params) === null || _a === void 0 ? void 0 : _a._entityContext;
|
|
25
|
+
if (!entityContext) {
|
|
26
|
+
return; // Not an entity-originated action
|
|
27
|
+
}
|
|
28
|
+
const { entityId, keys, keyFields } = entityContext;
|
|
29
|
+
const { path } = action.payload;
|
|
30
|
+
// Create entity attributes
|
|
31
|
+
const entityAttrs = ui_remote_resource_api_1.resourceAttributesUtil.createEntityAttributes(entityId, keys, keyFields);
|
|
32
|
+
// TODO: Entity attributes are no longer stored in file extension state
|
|
33
|
+
// The FileSystemAdapter doesn't support updating extension states directly
|
|
34
|
+
// Entity information is now tracked through the file content itself
|
|
35
|
+
// Future: Consider adding entity metadata support to FileSystemAdapter
|
|
36
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.createResourceMiddleware = void 0;
|
|
13
|
+
const ui_remote_resource_api_1 = require("@hamak/ui-remote-resource-api");
|
|
14
|
+
/**
|
|
15
|
+
* Resource middleware
|
|
16
|
+
* Intercepts RESOURCE_CALL_REQUEST actions and executes API calls via providers
|
|
17
|
+
*/
|
|
18
|
+
function createResourceMiddleware(config) {
|
|
19
|
+
const { registry, onError, onSuccess } = config;
|
|
20
|
+
const actionFactory = new ui_remote_resource_api_1.ResourceActionFactory();
|
|
21
|
+
return (store) => (next) => (action) => __awaiter(this, void 0, void 0, function* () {
|
|
22
|
+
// Only handle RESOURCE_CALL_REQUEST actions
|
|
23
|
+
if (action.type !== ui_remote_resource_api_1.ResourceActionTypes.RESOURCE_CALL_REQUEST) {
|
|
24
|
+
return next(action);
|
|
25
|
+
}
|
|
26
|
+
// Continue action flow (for sync middleware to set loading state)
|
|
27
|
+
next(action);
|
|
28
|
+
const requestAction = action;
|
|
29
|
+
const { endpointId, operation, params, path } = requestAction.payload;
|
|
30
|
+
try {
|
|
31
|
+
// Lookup endpoint
|
|
32
|
+
const endpointDef = registry.getEndpoint(endpointId);
|
|
33
|
+
if (!endpointDef) {
|
|
34
|
+
throw new Error(`Endpoint not found: ${endpointId}`);
|
|
35
|
+
}
|
|
36
|
+
// Get provider
|
|
37
|
+
const provider = registry.getProvider(endpointDef.providerType);
|
|
38
|
+
if (!provider) {
|
|
39
|
+
throw new Error(`Provider not found: ${endpointDef.providerType}`);
|
|
40
|
+
}
|
|
41
|
+
// Map action to call params (mapper receives entire action)
|
|
42
|
+
const callParams = endpointDef.payloadMapper
|
|
43
|
+
? endpointDef.payloadMapper(requestAction)
|
|
44
|
+
: { params }; // Default: just use params
|
|
45
|
+
// Execute provider call
|
|
46
|
+
const result = yield provider.call(endpointDef.endpoint, callParams, operation);
|
|
47
|
+
// Transform response (transformer receives entire action)
|
|
48
|
+
const transformedData = endpointDef.responseTransformer
|
|
49
|
+
? endpointDef.responseTransformer(result.data, result.metadata, requestAction)
|
|
50
|
+
: result.data;
|
|
51
|
+
// Determine storage path
|
|
52
|
+
const storagePath = determinePath(path, endpointDef, params, operation);
|
|
53
|
+
// Dispatch success action
|
|
54
|
+
const successAction = actionFactory.callSuccess(endpointId, storagePath, transformedData, result.metadata, requestAction);
|
|
55
|
+
store.dispatch(successAction);
|
|
56
|
+
onSuccess === null || onSuccess === void 0 ? void 0 : onSuccess(result, requestAction);
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
// Determine storage path for error state
|
|
60
|
+
const endpointDef = registry.getEndpoint(endpointId);
|
|
61
|
+
const storagePath = determinePath(path, endpointDef, params, operation);
|
|
62
|
+
// Dispatch failure action
|
|
63
|
+
const failureAction = actionFactory.callFailure(endpointId, storagePath, normalizeError(error), requestAction);
|
|
64
|
+
store.dispatch(failureAction);
|
|
65
|
+
onError === null || onError === void 0 ? void 0 : onError(error, requestAction);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
exports.createResourceMiddleware = createResourceMiddleware;
|
|
70
|
+
/**
|
|
71
|
+
* Determine the file path where resource should be stored
|
|
72
|
+
*/
|
|
73
|
+
function determinePath(explicitPath, endpointDef, params, operation) {
|
|
74
|
+
// 1. Use explicit path from action if provided
|
|
75
|
+
if (explicitPath) {
|
|
76
|
+
return explicitPath;
|
|
77
|
+
}
|
|
78
|
+
// 2. Use endpoint's default folder + generate filename
|
|
79
|
+
if (endpointDef === null || endpointDef === void 0 ? void 0 : endpointDef.defaultFolder) {
|
|
80
|
+
const folder = Array.isArray(endpointDef.defaultFolder)
|
|
81
|
+
? endpointDef.defaultFolder
|
|
82
|
+
: [endpointDef.defaultFolder];
|
|
83
|
+
// Generate filename from params or operation
|
|
84
|
+
const filename = generateFilename(params, operation);
|
|
85
|
+
return [...folder, filename];
|
|
86
|
+
}
|
|
87
|
+
// 3. Fallback to root with generated filename
|
|
88
|
+
return [generateFilename(params, operation)];
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Generate filename from params or operation
|
|
92
|
+
*/
|
|
93
|
+
function generateFilename(params, operation) {
|
|
94
|
+
// If params has an id field, use it
|
|
95
|
+
if (params === null || params === void 0 ? void 0 : params.id) {
|
|
96
|
+
return String(params.id);
|
|
97
|
+
}
|
|
98
|
+
// Otherwise generate based on operation + timestamp
|
|
99
|
+
return `${operation}-${Date.now()}`;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Normalize error to consistent format
|
|
103
|
+
*/
|
|
104
|
+
function normalizeError(error) {
|
|
105
|
+
var _a, _b, _c;
|
|
106
|
+
return {
|
|
107
|
+
message: error.message || 'Unknown error',
|
|
108
|
+
code: error.code || ((_b = (_a = error.response) === null || _a === void 0 ? void 0 : _a.status) === null || _b === void 0 ? void 0 : _b.toString()),
|
|
109
|
+
details: ((_c = error.response) === null || _c === void 0 ? void 0 : _c.data) || error
|
|
110
|
+
};
|
|
111
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createSyncMiddleware = void 0;
|
|
4
|
+
const ui_remote_resource_api_1 = require("@hamak/ui-remote-resource-api");
|
|
5
|
+
const navigation_utils_1 = require("@hamak/navigation-utils");
|
|
6
|
+
/**
|
|
7
|
+
* Sync middleware
|
|
8
|
+
* Updates file system state when resource actions complete
|
|
9
|
+
*/
|
|
10
|
+
function createSyncMiddleware(config) {
|
|
11
|
+
const { fsActions } = config;
|
|
12
|
+
return (store) => (next) => (action) => {
|
|
13
|
+
// Pass through all actions first
|
|
14
|
+
const result = next(action);
|
|
15
|
+
// Handle request action (set loading state)
|
|
16
|
+
if (action.type === ui_remote_resource_api_1.ResourceActionTypes.RESOURCE_CALL_REQUEST) {
|
|
17
|
+
handleRequestAction(store, action, fsActions);
|
|
18
|
+
}
|
|
19
|
+
// Handle success action (update content)
|
|
20
|
+
if (action.type === ui_remote_resource_api_1.ResourceActionTypes.RESOURCE_CALL_SUCCESS) {
|
|
21
|
+
handleSuccessAction(store, action, fsActions);
|
|
22
|
+
}
|
|
23
|
+
// Handle failure action (set error state)
|
|
24
|
+
if (action.type === ui_remote_resource_api_1.ResourceActionTypes.RESOURCE_CALL_FAILURE) {
|
|
25
|
+
handleFailureAction(store, action, fsActions);
|
|
26
|
+
}
|
|
27
|
+
return result;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
exports.createSyncMiddleware = createSyncMiddleware;
|
|
31
|
+
function handleRequestAction(store, action, fsActions) {
|
|
32
|
+
const { path } = action.payload;
|
|
33
|
+
if (!path) {
|
|
34
|
+
// Loading state will be set when success/failure with resolved path
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
// Normalize path using Pathway utility
|
|
38
|
+
const pathway = navigation_utils_1.Pathway.of(path);
|
|
39
|
+
const parentPathway = pathway.getParent();
|
|
40
|
+
const parentSegments = parentPathway.getSegments();
|
|
41
|
+
// Ensure parent directory exists
|
|
42
|
+
if (parentSegments.length > 0) {
|
|
43
|
+
store.dispatch(fsActions.mkdir(parentSegments, true));
|
|
44
|
+
}
|
|
45
|
+
// Set loading state by creating file node with contentIsPresent=false
|
|
46
|
+
store.dispatch(fsActions.setFile(path, null, 'xs:any', { override: false, contentIsPresent: false }));
|
|
47
|
+
}
|
|
48
|
+
function handleSuccessAction(store, action, fsActions) {
|
|
49
|
+
var _a;
|
|
50
|
+
const { path, data } = action.payload;
|
|
51
|
+
const { request } = action;
|
|
52
|
+
// Normalize path using Pathway utility
|
|
53
|
+
const pathway = navigation_utils_1.Pathway.of(path);
|
|
54
|
+
const parentPathway = pathway.getParent();
|
|
55
|
+
const parentSegments = parentPathway.getSegments();
|
|
56
|
+
// Ensure parent directory exists
|
|
57
|
+
if (parentSegments.length > 0) {
|
|
58
|
+
store.dispatch(fsActions.mkdir(parentSegments, true));
|
|
59
|
+
}
|
|
60
|
+
// Set file content from remote (automatically sets contentLoaded=true and clears loading)
|
|
61
|
+
// Schema should come from endpoint definition if available
|
|
62
|
+
const schema = ((_a = request.meta) === null || _a === void 0 ? void 0 : _a.schema) || 'xs:any';
|
|
63
|
+
// First ensure file exists with proper schema
|
|
64
|
+
store.dispatch(fsActions.setFile(path, data, schema, { override: true, contentIsPresent: true }));
|
|
65
|
+
// Then set content from remote to mark it as loaded from remote source
|
|
66
|
+
store.dispatch(fsActions.setFileContent(path, data, true));
|
|
67
|
+
}
|
|
68
|
+
function handleFailureAction(store, action, fsActions) {
|
|
69
|
+
const { path, error } = action.payload;
|
|
70
|
+
if (!path) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
// Normalize path using Pathway utility
|
|
74
|
+
const pathway = navigation_utils_1.Pathway.of(path);
|
|
75
|
+
const parentPathway = pathway.getParent();
|
|
76
|
+
const parentSegments = parentPathway.getSegments();
|
|
77
|
+
// Ensure parent directory exists
|
|
78
|
+
if (parentSegments.length > 0) {
|
|
79
|
+
store.dispatch(fsActions.mkdir(parentSegments, true));
|
|
80
|
+
}
|
|
81
|
+
// For now, just ensure the file exists and set content to null/error placeholder
|
|
82
|
+
// TODO: FileSystemAdapter doesn't currently support setting error state directly
|
|
83
|
+
// We might need to extend it or handle errors differently
|
|
84
|
+
store.dispatch(fsActions.setFile(path, { error }, 'xs:any', { override: true, contentIsPresent: true }));
|
|
85
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.createResourcePlugin = exports.ENTITY_REGISTRY_TOKEN = exports.RESOURCE_REGISTRY_TOKEN = void 0;
|
|
13
|
+
const ui_store_api_1 = require("@hamak/ui-store-api");
|
|
14
|
+
const ui_store_impl_1 = require("@hamak/ui-store-impl");
|
|
15
|
+
const resource_registry_1 = require("../registry/resource-registry");
|
|
16
|
+
const entity_registry_1 = require("../registry/entity-registry");
|
|
17
|
+
const rest_resource_provider_1 = require("../providers/rest-resource-provider");
|
|
18
|
+
const mock_resource_provider_1 = require("../providers/mock-resource-provider");
|
|
19
|
+
const resource_middleware_1 = require("../middleware/resource-middleware");
|
|
20
|
+
const sync_middleware_1 = require("../middleware/sync-middleware");
|
|
21
|
+
const entity_middleware_1 = require("../middleware/entity-middleware");
|
|
22
|
+
const entity_sync_middleware_1 = require("../middleware/entity-sync-middleware");
|
|
23
|
+
// Dependency injection tokens (for local use)
|
|
24
|
+
exports.RESOURCE_REGISTRY_TOKEN = 'RESOURCE_REGISTRY';
|
|
25
|
+
exports.ENTITY_REGISTRY_TOKEN = 'ENTITY_REGISTRY';
|
|
26
|
+
/**
|
|
27
|
+
* Create remote resource plugin
|
|
28
|
+
*
|
|
29
|
+
* @param config - Plugin configuration
|
|
30
|
+
* @returns Plugin module for microkernel registration
|
|
31
|
+
*/
|
|
32
|
+
function createResourcePlugin(config = {}) {
|
|
33
|
+
const resourceRegistry = new resource_registry_1.ResourceRegistry();
|
|
34
|
+
const entityRegistry = new entity_registry_1.EntityRegistry();
|
|
35
|
+
return {
|
|
36
|
+
initialize(ctx) {
|
|
37
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
38
|
+
// Register default providers
|
|
39
|
+
const restProvider = new rest_resource_provider_1.RestResourceProvider();
|
|
40
|
+
const mockProvider = new mock_resource_provider_1.MockResourceProvider();
|
|
41
|
+
resourceRegistry.registerProvider(restProvider);
|
|
42
|
+
resourceRegistry.registerProvider(mockProvider);
|
|
43
|
+
// Register custom providers
|
|
44
|
+
if (config.providers) {
|
|
45
|
+
config.providers.forEach((provider) => {
|
|
46
|
+
resourceRegistry.registerProvider(provider);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
// Register endpoints
|
|
50
|
+
if (config.endpoints) {
|
|
51
|
+
config.endpoints.forEach((endpoint) => {
|
|
52
|
+
resourceRegistry.registerEndpoint(endpoint);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
// Register entities
|
|
56
|
+
if (config.entities) {
|
|
57
|
+
config.entities.forEach((entity) => {
|
|
58
|
+
entityRegistry.registerEntity(entity);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
// Provide registries via DI
|
|
62
|
+
ctx.provide({
|
|
63
|
+
provide: exports.RESOURCE_REGISTRY_TOKEN,
|
|
64
|
+
useValue: resourceRegistry
|
|
65
|
+
});
|
|
66
|
+
ctx.provide({
|
|
67
|
+
provide: exports.ENTITY_REGISTRY_TOKEN,
|
|
68
|
+
useValue: entityRegistry
|
|
69
|
+
});
|
|
70
|
+
// Register middleware during initialization
|
|
71
|
+
try {
|
|
72
|
+
// Access resolve method if available on context
|
|
73
|
+
const resolve = ctx.resolve;
|
|
74
|
+
if (resolve) {
|
|
75
|
+
// Resolve FileSystemAdapter from DI
|
|
76
|
+
const fsAdapter = resolve(ui_store_impl_1.FILESYSTEM_ADAPTER_TOKEN);
|
|
77
|
+
if (!fsAdapter) {
|
|
78
|
+
console.warn('[ui-remote-resource] FileSystemAdapter not available in DI');
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const fsActions = fsAdapter.getActions();
|
|
82
|
+
const fsSliceName = fsAdapter.sliceName;
|
|
83
|
+
// Create middleware with FileSystemAdapter actions
|
|
84
|
+
const entityMiddleware = (0, entity_middleware_1.createEntityMiddleware)({
|
|
85
|
+
entityRegistry,
|
|
86
|
+
fsSliceName,
|
|
87
|
+
onError: config.onError
|
|
88
|
+
});
|
|
89
|
+
const resourceMiddleware = (0, resource_middleware_1.createResourceMiddleware)({
|
|
90
|
+
registry: resourceRegistry,
|
|
91
|
+
onError: config.onError,
|
|
92
|
+
onSuccess: config.onSuccess
|
|
93
|
+
});
|
|
94
|
+
const entitySyncMiddleware = (0, entity_sync_middleware_1.createEntitySyncMiddleware)();
|
|
95
|
+
const resourceSyncMiddleware = (0, sync_middleware_1.createSyncMiddleware)({
|
|
96
|
+
fsActions
|
|
97
|
+
});
|
|
98
|
+
const middlewareRegistry = resolve(ui_store_api_1.MIDDLEWARE_REGISTRY_TOKEN);
|
|
99
|
+
middlewareRegistry.register({
|
|
100
|
+
id: 'entity-middleware',
|
|
101
|
+
middleware: entityMiddleware,
|
|
102
|
+
priority: 110,
|
|
103
|
+
plugin: 'ui-remote-resource'
|
|
104
|
+
});
|
|
105
|
+
middlewareRegistry.register({
|
|
106
|
+
id: 'resource-middleware',
|
|
107
|
+
middleware: resourceMiddleware,
|
|
108
|
+
priority: 100,
|
|
109
|
+
plugin: 'ui-remote-resource'
|
|
110
|
+
});
|
|
111
|
+
middlewareRegistry.register({
|
|
112
|
+
id: 'entity-sync-middleware',
|
|
113
|
+
middleware: entitySyncMiddleware,
|
|
114
|
+
priority: 60,
|
|
115
|
+
plugin: 'ui-remote-resource'
|
|
116
|
+
});
|
|
117
|
+
middlewareRegistry.register({
|
|
118
|
+
id: 'resource-sync-middleware',
|
|
119
|
+
middleware: resourceSyncMiddleware,
|
|
120
|
+
priority: 50,
|
|
121
|
+
plugin: 'ui-remote-resource'
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
console.warn('Resolve method not available in initialization context');
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
console.warn('Failed to register middleware during initialization:', error);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
},
|
|
133
|
+
activate(ctx) {
|
|
134
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
135
|
+
var _a;
|
|
136
|
+
// Emit ready event
|
|
137
|
+
(_a = ctx.hooks) === null || _a === void 0 ? void 0 : _a.emit('ui-remote-resource:ready', {
|
|
138
|
+
resourceRegistry,
|
|
139
|
+
entityRegistry
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
},
|
|
143
|
+
deactivate() {
|
|
144
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
145
|
+
// Cleanup if needed
|
|
146
|
+
// For now, no cleanup required
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
exports.createResourcePlugin = createResourcePlugin;
|