@objectql/core 1.4.0 → 1.6.0
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/CHANGELOG.md +23 -0
- package/README.md +287 -0
- package/dist/app.d.ts +2 -6
- package/dist/app.js +53 -124
- package/dist/app.js.map +1 -1
- package/dist/index.d.ts +3 -4
- package/dist/index.js +3 -4
- package/dist/index.js.map +1 -1
- package/dist/util.d.ts +1 -0
- package/dist/util.js +9 -0
- package/dist/util.js.map +1 -0
- package/dist/validator.d.ts +69 -0
- package/dist/validator.js +464 -0
- package/dist/validator.js.map +1 -0
- package/package.json +2 -5
- package/src/app.ts +64 -104
- package/src/index.ts +5 -4
- package/src/util.ts +5 -0
- package/src/validator.ts +553 -0
- package/tsconfig.json +2 -3
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/driver.d.ts +0 -2
- package/dist/driver.js +0 -55
- package/dist/driver.js.map +0 -1
- package/dist/loader.d.ts +0 -12
- package/dist/loader.js +0 -276
- package/dist/loader.js.map +0 -1
- package/dist/plugin.d.ts +0 -2
- package/dist/plugin.js +0 -56
- package/dist/plugin.js.map +0 -1
- package/dist/remote.d.ts +0 -8
- package/dist/remote.js +0 -43
- package/dist/remote.js.map +0 -1
- package/src/driver.ts +0 -54
- package/src/loader.ts +0 -259
- package/src/plugin.ts +0 -53
- package/src/remote.ts +0 -50
- package/test/dynamic.test.ts +0 -34
- package/test/fixtures/project.action.js +0 -8
- package/test/fixtures/project.object.yml +0 -41
- package/test/loader.test.ts +0 -15
- package/test/metadata.test.ts +0 -49
- package/test/remote.test.ts +0 -119
package/src/app.ts
CHANGED
|
@@ -14,21 +14,16 @@ import {
|
|
|
14
14
|
ActionContext,
|
|
15
15
|
LoaderPlugin
|
|
16
16
|
} from '@objectql/types';
|
|
17
|
-
import * as fs from 'fs';
|
|
18
|
-
import * as path from 'path';
|
|
19
|
-
import * as yaml from 'js-yaml';
|
|
20
|
-
import { ObjectLoader } from './loader';
|
|
21
17
|
import { ObjectRepository } from './repository';
|
|
22
|
-
import {
|
|
23
|
-
|
|
24
|
-
import { loadRemoteFromUrl } from './remote';
|
|
18
|
+
// import { createDriverFromConnection } from './driver'; // REMOVE THIS
|
|
19
|
+
|
|
20
|
+
// import { loadRemoteFromUrl } from './remote';
|
|
25
21
|
import { executeActionHelper, registerActionHelper, ActionEntry } from './action';
|
|
26
22
|
import { registerHookHelper, triggerHookHelper, HookEntry } from './hook';
|
|
27
23
|
import { registerObjectHelper, getConfigsHelper } from './object';
|
|
28
24
|
|
|
29
25
|
export class ObjectQL implements IObjectQL {
|
|
30
26
|
public metadata: MetadataRegistry;
|
|
31
|
-
private loader: ObjectLoader;
|
|
32
27
|
private datasources: Record<string, Driver> = {};
|
|
33
28
|
private remotes: string[] = [];
|
|
34
29
|
private hooks: Record<string, HookEntry[]> = {};
|
|
@@ -41,30 +36,24 @@ export class ObjectQL implements IObjectQL {
|
|
|
41
36
|
constructor(config: ObjectQLConfig) {
|
|
42
37
|
this.config = config;
|
|
43
38
|
this.metadata = config.registry || new MetadataRegistry();
|
|
44
|
-
this.loader = new ObjectLoader(this.metadata);
|
|
45
39
|
this.datasources = config.datasources || {};
|
|
46
|
-
this.remotes = config.remotes || [];
|
|
40
|
+
// this.remotes = config.remotes || [];
|
|
47
41
|
|
|
48
42
|
if (config.connection) {
|
|
49
|
-
|
|
43
|
+
throw new Error("Connection strings are not supported in core directly. Use @objectql/platform-node's createDriverFromConnection or pass a driver instance to 'datasources'.");
|
|
50
44
|
}
|
|
51
45
|
|
|
52
46
|
// Initialize Plugin List (but don't setup yet)
|
|
53
47
|
if (config.plugins) {
|
|
54
48
|
for (const plugin of config.plugins) {
|
|
55
49
|
if (typeof plugin === 'string') {
|
|
56
|
-
|
|
50
|
+
throw new Error("String plugins are not supported in core. Use @objectql/platform-node or pass plugin instance.");
|
|
57
51
|
} else {
|
|
58
52
|
this.use(plugin);
|
|
59
53
|
}
|
|
60
54
|
}
|
|
61
55
|
}
|
|
62
56
|
}
|
|
63
|
-
|
|
64
|
-
addPackage(name: string) {
|
|
65
|
-
this.loader.loadPackage(name);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
57
|
use(plugin: ObjectQLPlugin) {
|
|
69
58
|
this.pluginsList.push(plugin);
|
|
70
59
|
}
|
|
@@ -101,58 +90,6 @@ export class ObjectQL implements IObjectQL {
|
|
|
101
90
|
return await executeActionHelper(this.metadata, this.actions, objectName, actionName, ctx);
|
|
102
91
|
}
|
|
103
92
|
|
|
104
|
-
loadFromDirectory(dir: string, packageName?: string) {
|
|
105
|
-
this.loader.load(dir, packageName);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
addLoader(plugin: LoaderPlugin) {
|
|
109
|
-
this.loader.use(plugin);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
async updateMetadata(type: string, id: string, content: any): Promise<void> {
|
|
113
|
-
// Use registry to find the entry so we can get the file path
|
|
114
|
-
const entry = this.metadata.getEntry(type, id);
|
|
115
|
-
if (!entry) {
|
|
116
|
-
throw new Error(`Metadata ${type}:${id} not found`);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if (!entry.path) {
|
|
120
|
-
throw new Error('Cannot update: Metadata source file not found (in-memory only?)');
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Safety Check: Prevent writing to node_modules
|
|
124
|
-
if (entry.path.includes('node_modules')) {
|
|
125
|
-
throw new Error(`Cannot update metadata ${type}:${id}: File is inside node_modules (read-only package).`);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Check file extension
|
|
129
|
-
const ext = path.extname(entry.path).toLowerCase();
|
|
130
|
-
let newContent = '';
|
|
131
|
-
|
|
132
|
-
if (ext === '.yml' || ext === '.yaml') {
|
|
133
|
-
newContent = yaml.dump(content);
|
|
134
|
-
} else if (ext === '.json') {
|
|
135
|
-
newContent = JSON.stringify(content, null, 2);
|
|
136
|
-
} else {
|
|
137
|
-
throw new Error(`Cannot update: Unsupported file format ${ext} (only .yml, .yaml, .json supported)`);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// Write file
|
|
141
|
-
try {
|
|
142
|
-
await fs.promises.chmod(entry.path, 0o666).catch(() => {}); // Try to ensure writable
|
|
143
|
-
await fs.promises.writeFile(entry.path, newContent, 'utf8');
|
|
144
|
-
} catch (e: any) {
|
|
145
|
-
throw new Error(`Failed to write file ${entry.path}: ${e.message}`);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Update registry in-memory
|
|
149
|
-
entry.content = content;
|
|
150
|
-
|
|
151
|
-
// If it's an object update, we might need some re-processing?
|
|
152
|
-
// For now, assume a restart or reload is needed for deep schema changes,
|
|
153
|
-
// but simple property updates are reflected immediately in registry.
|
|
154
|
-
}
|
|
155
|
-
|
|
156
93
|
createContext(options: ObjectQLContextOptions): ObjectQLContext {
|
|
157
94
|
const ctx: ObjectQLContext = {
|
|
158
95
|
userId: options.userId,
|
|
@@ -249,26 +186,9 @@ export class ObjectQL implements IObjectQL {
|
|
|
249
186
|
await plugin.setup(app);
|
|
250
187
|
}
|
|
251
188
|
|
|
252
|
-
//
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
this.addPackage(name);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
if (this.config.presets) {
|
|
259
|
-
for (const name of this.config.presets) {
|
|
260
|
-
this.addPackage(name);
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// 2. Load Local Sources (Application Layer)
|
|
265
|
-
if (this.config.source) {
|
|
266
|
-
const sources = Array.isArray(this.config.source) ? this.config.source : [this.config.source];
|
|
267
|
-
for (const src of sources) {
|
|
268
|
-
this.loader.load(src);
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
189
|
+
// Packages, Presets, Source, Objects loading logic removed from Core.
|
|
190
|
+
// Use @objectql/platform-node's ObjectLoader or platform-specific loaders.
|
|
191
|
+
|
|
272
192
|
// 3. Load In-Memory Objects (Dynamic Layer)
|
|
273
193
|
if (this.config.objects) {
|
|
274
194
|
for (const [key, obj] of Object.entries(this.config.objects)) {
|
|
@@ -276,23 +196,9 @@ export class ObjectQL implements IObjectQL {
|
|
|
276
196
|
}
|
|
277
197
|
}
|
|
278
198
|
|
|
279
|
-
// 4. Load Remotes
|
|
280
|
-
if (this.remotes.length > 0) {
|
|
281
|
-
console.log(`Loading ${this.remotes.length} remotes...`);
|
|
282
|
-
const results = await Promise.all(this.remotes.map(url => loadRemoteFromUrl(url)));
|
|
283
|
-
for (const res of results) {
|
|
284
|
-
if (res) {
|
|
285
|
-
this.datasources[res.driverName] = res.driver;
|
|
286
|
-
for (const obj of res.objects) {
|
|
287
|
-
this.registerObject(obj);
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
199
|
const objects = this.metadata.list<ObjectConfig>('object');
|
|
294
200
|
|
|
295
|
-
// 5. Init
|
|
201
|
+
// 5. Init Datasources
|
|
296
202
|
// Let's pass all objects to all configured drivers.
|
|
297
203
|
for (const [name, driver] of Object.entries(this.datasources)) {
|
|
298
204
|
if (driver.init) {
|
|
@@ -300,5 +206,59 @@ export class ObjectQL implements IObjectQL {
|
|
|
300
206
|
await driver.init(objects);
|
|
301
207
|
}
|
|
302
208
|
}
|
|
209
|
+
|
|
210
|
+
// 6. Process Initial Data
|
|
211
|
+
await this.processInitialData();
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
private async processInitialData() {
|
|
215
|
+
const dataEntries = this.metadata.list<any>('data');
|
|
216
|
+
if (dataEntries.length === 0) return;
|
|
217
|
+
|
|
218
|
+
console.log(`Processing ${dataEntries.length} initial data files...`);
|
|
219
|
+
|
|
220
|
+
// We need a system context to write data
|
|
221
|
+
const ctx = this.createContext({ isSystem: true });
|
|
222
|
+
|
|
223
|
+
for (const entry of dataEntries) {
|
|
224
|
+
// Expected format:
|
|
225
|
+
// 1. { object: 'User', records: [...] }
|
|
226
|
+
// 2. [ record1, record2 ] (with name property added by loader inferred from filename)
|
|
227
|
+
|
|
228
|
+
let objectName = entry.object;
|
|
229
|
+
let records = entry.records;
|
|
230
|
+
|
|
231
|
+
if (Array.isArray(entry)) {
|
|
232
|
+
records = entry;
|
|
233
|
+
if (!objectName && (entry as any).name) {
|
|
234
|
+
objectName = (entry as any).name;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (!objectName || !records || !Array.isArray(records)) {
|
|
239
|
+
console.warn(`Skipping invalid data entry:`, entry);
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const repo = ctx.object(objectName);
|
|
244
|
+
|
|
245
|
+
for (const record of records) {
|
|
246
|
+
try {
|
|
247
|
+
// Check existence if a unique key is provided?
|
|
248
|
+
// For now, let's assume if it has an ID, we check it.
|
|
249
|
+
// Or we could try to find existing record by some key matching logic.
|
|
250
|
+
// Simple approach: create. If it fails (constraint), ignore.
|
|
251
|
+
|
|
252
|
+
// Actually, a better approach for initial data is "upsert" or "create if not exists".
|
|
253
|
+
// But without unique keys defined in data, we can't reliably dedup.
|
|
254
|
+
// Let's try to 'create' and catch errors.
|
|
255
|
+
await repo.create(record);
|
|
256
|
+
console.log(`Initialized record for ${objectName}`);
|
|
257
|
+
} catch (e: any) {
|
|
258
|
+
// Ignore duplicate key errors silently-ish
|
|
259
|
+
console.warn(`Failed to insert initial data for ${objectName}: ${e.message}`);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
303
263
|
}
|
|
304
264
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
export * from './loader';
|
|
2
1
|
export * from './repository';
|
|
3
2
|
export * from './app';
|
|
4
|
-
|
|
5
|
-
export * from './driver';
|
|
6
|
-
export * from './remote';
|
|
3
|
+
|
|
7
4
|
export * from './action';
|
|
8
5
|
export * from './hook';
|
|
9
6
|
export * from './object';
|
|
7
|
+
export * from './validator';
|
|
8
|
+
export * from './util';
|
|
9
|
+
|
|
10
|
+
export * from './util';
|