@objectstack/objectql 0.3.1 → 0.3.3
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 +78 -0
- package/dist/index.d.ts +121 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +161 -29
- package/dist/registry.d.ts +1 -8
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +0 -12
- package/package.json +2 -2
- package/src/index.ts +189 -33
- package/src/registry.ts +1 -17
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# @objectstack/objectql
|
|
2
2
|
|
|
3
|
+
## 0.3.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Workflow and configuration improvements
|
|
8
|
+
|
|
9
|
+
- Enhanced GitHub workflows for CI, release, and PR automation
|
|
10
|
+
- Added comprehensive prompt templates for different protocol areas
|
|
11
|
+
- Improved project documentation and automation guides
|
|
12
|
+
- Updated changeset configuration
|
|
13
|
+
- Added cursor rules for better development experience
|
|
14
|
+
|
|
15
|
+
- Updated dependencies
|
|
16
|
+
- @objectstack/spec@0.3.3
|
|
17
|
+
|
|
18
|
+
## 0.3.2
|
|
19
|
+
|
|
20
|
+
### Patch Changes
|
|
21
|
+
|
|
22
|
+
- Patch release for maintenance and stability improvements
|
|
23
|
+
- Updated dependencies
|
|
24
|
+
- @objectstack/spec@0.3.2
|
|
25
|
+
|
|
3
26
|
## 0.3.1
|
|
4
27
|
|
|
5
28
|
### Patch Changes
|
package/README.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# ObjectQL Engine
|
|
2
|
+
|
|
3
|
+
**ObjectQL** is a schema-driven, cross-datasource query engine for the [ObjectStack](https://github.com/steedos/objectstack) ecosystem. It acts as a virtual "Meta-Database" that unifies access to SQL, NoSQL, and API data sources under a single semantic layer.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Protocol Agnostic**: Uses standard `ObjectSchema` and `QueryAST` from `@objectstack/spec`.
|
|
8
|
+
- **Cross-Datasource**: Routes queries to the correct driver (Postgres, MongoDB, Redis, etc.) based on Object definition.
|
|
9
|
+
- **Unified API**: Single `find`, `insert`, `update`, `delete` API regardless of the underlying storage.
|
|
10
|
+
- **Plugin System**: Load objects and logic via standard Manifests.
|
|
11
|
+
- **Middleware**: (Planned) Support for Hooks and Validators.
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { ObjectQL } from '@objectstack/objectql';
|
|
17
|
+
import { MemoryDriver } from '@objectstack/driver-memory'; // Example driver
|
|
18
|
+
|
|
19
|
+
async function main() {
|
|
20
|
+
// 1. Initialize Engine
|
|
21
|
+
const ql = new ObjectQL();
|
|
22
|
+
|
|
23
|
+
// 2. Register Drivers
|
|
24
|
+
const memDriver = new MemoryDriver({ name: 'default' });
|
|
25
|
+
ql.registerDriver(memDriver, true);
|
|
26
|
+
|
|
27
|
+
// 3. Load Schema (via Plugin/Manifest)
|
|
28
|
+
await ql.use({
|
|
29
|
+
name: 'my-app',
|
|
30
|
+
objects: [
|
|
31
|
+
{
|
|
32
|
+
name: 'todo',
|
|
33
|
+
fields: {
|
|
34
|
+
title: { type: 'text' },
|
|
35
|
+
completed: { type: 'boolean' }
|
|
36
|
+
},
|
|
37
|
+
datasource: 'default'
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
await ql.init();
|
|
43
|
+
|
|
44
|
+
// 4. Execute Queries
|
|
45
|
+
// Insert
|
|
46
|
+
await ql.insert('todo', { title: 'Buy Milk', completed: false });
|
|
47
|
+
|
|
48
|
+
// Find (Simple)
|
|
49
|
+
const todos = await ql.find('todo', { completed: false });
|
|
50
|
+
|
|
51
|
+
// Find (Advanced AST)
|
|
52
|
+
const results = await ql.find('todo', {
|
|
53
|
+
where: {
|
|
54
|
+
title: { $contains: 'Milk' }
|
|
55
|
+
},
|
|
56
|
+
limit: 10,
|
|
57
|
+
orderBy: [{ field: 'title', order: 'desc' }]
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
console.log(results);
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Architecture
|
|
65
|
+
|
|
66
|
+
- **SchemaRegistry**: Central store for all metadata (Objects, Apps, Config).
|
|
67
|
+
- **DriverRegistry**: Manages connections to physical data sources.
|
|
68
|
+
- **QueryPlanner**: (Internal) Normalizes simplified queries into `QueryAST`.
|
|
69
|
+
- **Executor**: Routes AST to the correct driver.
|
|
70
|
+
|
|
71
|
+
## Roadmap
|
|
72
|
+
|
|
73
|
+
- [x] Basic CRUD
|
|
74
|
+
- [x] Driver Routing
|
|
75
|
+
- [ ] Cross-Object Joins (Federation)
|
|
76
|
+
- [ ] Validation Layer (Zod)
|
|
77
|
+
- [ ] Access Control (ACL)
|
|
78
|
+
- [ ] Caching Layer
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { HookContext } from '@objectstack/spec/data';
|
|
2
|
+
import { DriverInterface, DriverOptions } from '@objectstack/spec/driver';
|
|
2
3
|
export { SchemaRegistry } from './registry';
|
|
4
|
+
export type HookHandler = (context: HookContext) => Promise<void> | void;
|
|
3
5
|
/**
|
|
4
6
|
* Host Context provided to plugins
|
|
5
7
|
*/
|
|
@@ -14,16 +16,129 @@ export interface PluginContext {
|
|
|
14
16
|
export declare class ObjectQL {
|
|
15
17
|
private drivers;
|
|
16
18
|
private defaultDriver;
|
|
19
|
+
private hooks;
|
|
17
20
|
private hostContext;
|
|
18
21
|
constructor(hostContext?: Record<string, any>);
|
|
19
22
|
/**
|
|
20
23
|
* Load and Register a Plugin
|
|
21
24
|
*/
|
|
22
25
|
use(manifestPart: any, runtimePart?: any): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Register a hook
|
|
28
|
+
* @param event The event name (e.g. 'beforeFind', 'afterInsert')
|
|
29
|
+
* @param handler The handler function
|
|
30
|
+
*/
|
|
31
|
+
registerHook(event: string, handler: HookHandler): void;
|
|
32
|
+
private triggerHooks;
|
|
23
33
|
/**
|
|
24
34
|
* Register a new storage driver
|
|
25
35
|
*/
|
|
26
36
|
registerDriver(driver: DriverInterface, isDefault?: boolean): void;
|
|
37
|
+
/**
|
|
38
|
+
* Helper to get object definition
|
|
39
|
+
*/
|
|
40
|
+
getSchema(objectName: string): {
|
|
41
|
+
fields: Record<string, {
|
|
42
|
+
type: "number" | "boolean" | "code" | "date" | "text" | "textarea" | "email" | "url" | "phone" | "password" | "markdown" | "html" | "richtext" | "currency" | "percent" | "datetime" | "time" | "select" | "lookup" | "master_detail" | "image" | "file" | "avatar" | "formula" | "summary" | "autonumber" | "location" | "geolocation" | "address" | "color" | "rating" | "slider" | "signature" | "qrcode";
|
|
43
|
+
required: boolean;
|
|
44
|
+
searchable: boolean;
|
|
45
|
+
multiple: boolean;
|
|
46
|
+
unique: boolean;
|
|
47
|
+
deleteBehavior: "set_null" | "cascade" | "restrict";
|
|
48
|
+
hidden: boolean;
|
|
49
|
+
readonly: boolean;
|
|
50
|
+
encryption: boolean;
|
|
51
|
+
index: boolean;
|
|
52
|
+
externalId: boolean;
|
|
53
|
+
options?: {
|
|
54
|
+
value: string;
|
|
55
|
+
label: string;
|
|
56
|
+
color?: string | undefined;
|
|
57
|
+
default?: boolean | undefined;
|
|
58
|
+
}[] | undefined;
|
|
59
|
+
min?: number | undefined;
|
|
60
|
+
max?: number | undefined;
|
|
61
|
+
formula?: string | undefined;
|
|
62
|
+
label?: string | undefined;
|
|
63
|
+
precision?: number | undefined;
|
|
64
|
+
name?: string | undefined;
|
|
65
|
+
description?: string | undefined;
|
|
66
|
+
format?: string | undefined;
|
|
67
|
+
defaultValue?: any;
|
|
68
|
+
maxLength?: number | undefined;
|
|
69
|
+
minLength?: number | undefined;
|
|
70
|
+
scale?: number | undefined;
|
|
71
|
+
reference?: string | undefined;
|
|
72
|
+
referenceFilters?: string[] | undefined;
|
|
73
|
+
writeRequiresMasterRead?: boolean | undefined;
|
|
74
|
+
expression?: string | undefined;
|
|
75
|
+
summaryOperations?: {
|
|
76
|
+
object: string;
|
|
77
|
+
function: "count" | "sum" | "avg" | "min" | "max";
|
|
78
|
+
field: string;
|
|
79
|
+
} | undefined;
|
|
80
|
+
language?: string | undefined;
|
|
81
|
+
theme?: string | undefined;
|
|
82
|
+
lineNumbers?: boolean | undefined;
|
|
83
|
+
maxRating?: number | undefined;
|
|
84
|
+
allowHalf?: boolean | undefined;
|
|
85
|
+
displayMap?: boolean | undefined;
|
|
86
|
+
allowGeocoding?: boolean | undefined;
|
|
87
|
+
addressFormat?: "us" | "uk" | "international" | undefined;
|
|
88
|
+
colorFormat?: "hex" | "rgb" | "rgba" | "hsl" | undefined;
|
|
89
|
+
allowAlpha?: boolean | undefined;
|
|
90
|
+
presetColors?: string[] | undefined;
|
|
91
|
+
step?: number | undefined;
|
|
92
|
+
showValue?: boolean | undefined;
|
|
93
|
+
marks?: Record<string, string> | undefined;
|
|
94
|
+
barcodeFormat?: "qr" | "ean13" | "ean8" | "code128" | "code39" | "upca" | "upce" | undefined;
|
|
95
|
+
qrErrorCorrection?: "L" | "M" | "Q" | "H" | undefined;
|
|
96
|
+
displayValue?: boolean | undefined;
|
|
97
|
+
allowScanning?: boolean | undefined;
|
|
98
|
+
currencyConfig?: {
|
|
99
|
+
precision: number;
|
|
100
|
+
currencyMode: "dynamic" | "fixed";
|
|
101
|
+
defaultCurrency: string;
|
|
102
|
+
} | undefined;
|
|
103
|
+
}>;
|
|
104
|
+
name: string;
|
|
105
|
+
active: boolean;
|
|
106
|
+
isSystem: boolean;
|
|
107
|
+
abstract: boolean;
|
|
108
|
+
datasource: string;
|
|
109
|
+
label?: string | undefined;
|
|
110
|
+
description?: string | undefined;
|
|
111
|
+
tags?: string[] | undefined;
|
|
112
|
+
search?: {
|
|
113
|
+
fields: string[];
|
|
114
|
+
displayFields?: string[] | undefined;
|
|
115
|
+
filters?: string[] | undefined;
|
|
116
|
+
} | undefined;
|
|
117
|
+
pluralLabel?: string | undefined;
|
|
118
|
+
icon?: string | undefined;
|
|
119
|
+
tableName?: string | undefined;
|
|
120
|
+
indexes?: {
|
|
121
|
+
fields: string[];
|
|
122
|
+
type?: "hash" | "btree" | "gin" | "gist" | undefined;
|
|
123
|
+
name?: string | undefined;
|
|
124
|
+
unique?: boolean | undefined;
|
|
125
|
+
}[] | undefined;
|
|
126
|
+
validations?: any[] | undefined;
|
|
127
|
+
titleFormat?: string | undefined;
|
|
128
|
+
compactLayout?: string[] | undefined;
|
|
129
|
+
enable?: {
|
|
130
|
+
searchable: boolean;
|
|
131
|
+
trackHistory: boolean;
|
|
132
|
+
apiEnabled: boolean;
|
|
133
|
+
files: boolean;
|
|
134
|
+
feeds: boolean;
|
|
135
|
+
activities: boolean;
|
|
136
|
+
trash: boolean;
|
|
137
|
+
mru: boolean;
|
|
138
|
+
clone: boolean;
|
|
139
|
+
apiMethods?: ("update" | "delete" | "get" | "list" | "create" | "upsert" | "bulk" | "aggregate" | "history" | "search" | "restore" | "purge" | "import" | "export")[] | undefined;
|
|
140
|
+
} | undefined;
|
|
141
|
+
} | undefined;
|
|
27
142
|
/**
|
|
28
143
|
* Helper to get the target driver
|
|
29
144
|
*/
|
|
@@ -33,9 +148,10 @@ export declare class ObjectQL {
|
|
|
33
148
|
*/
|
|
34
149
|
init(): Promise<void>;
|
|
35
150
|
destroy(): Promise<void>;
|
|
36
|
-
find(object: string,
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
151
|
+
find(object: string, query?: any, options?: DriverOptions): Promise<any>;
|
|
152
|
+
findOne(object: string, idOrQuery: string | any, options?: DriverOptions): Promise<Record<string, any> | null>;
|
|
153
|
+
insert(object: string, data: Record<string, any>, options?: DriverOptions): Promise<any>;
|
|
154
|
+
update(object: string, id: string | number, data: Record<string, any>, options?: DriverOptions): Promise<any>;
|
|
155
|
+
delete(object: string, id: string | number, options?: DriverOptions): Promise<any>;
|
|
40
156
|
}
|
|
41
157
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAE/D,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAI1E,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAEzE;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,QAAQ,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAEhB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED;;GAEG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,OAAO,CAAsC;IACrD,OAAO,CAAC,aAAa,CAAuB;IAG5C,OAAO,CAAC,KAAK,CAKX;IAGF,OAAO,CAAC,WAAW,CAA2B;gBAElC,WAAW,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM;IAKjD;;OAEG;IACG,GAAG,CAAC,YAAY,EAAE,GAAG,EAAE,WAAW,CAAC,EAAE,GAAG;IA4D9C;;;;OAIG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW;YAQlC,YAAY;IAQ1B;;OAEG;IACH,cAAc,CAAC,MAAM,EAAE,eAAe,EAAE,SAAS,GAAE,OAAe;IAclE;;OAEG;IACH,SAAS,CAAC,UAAU,EAAE,MAAM;;;;;;;;;;;;;mBAyMg/P,CAAC;;;qBAA2E,CAAC;uBAAyC,CAAC;;eAA2D,CAAC;eAAiC,CAAC;mBAAqC,CAAC;iBAAmC,CAAC;qBAAuC,CAAC;gBAAkC,CAAC;uBAAyC,CAAC;kBAAoC,CAAC;wBAA0C,CAAC;qBAAwB,CAAC;qBAAuC,CAAC;iBAAmC,CAAC;qBAAuC,CAAC;4BAA8C,CAAC;mCAAuD,CAAC;sBAAyC,CAAC;6BAA+C,CAAC;;;;;oBAAiK,CAAC;iBAAmC,CAAC;uBAAyC,CAAC;qBAAwC,CAAC;qBAAuC,CAAC;sBAAyC,CAAC;0BAA6C,CAAC;yBAA4C,CAAC;uBAAgE,CAAC;sBAAgE,CAAC;wBAA2C,CAAC;gBAAoC,CAAC;qBAAuC,CAAC;iBAAoC,CAAC;yBAA2D,CAAC;6BAAyG,CAAC;wBAAyD,CAAC;yBAA4C,CAAC;0BAA6C,CAAC;;;;;;;;;;;;;;;;yBAAwa,CAAC;mBAAuC,CAAC;;;;;;;gBAA0M,CAAC;gBAA6D,CAAC;kBAAoC,CAAC;;;;;;;;;;;;;;;sBAA8a,CAAC;;;IArM7gW;;OAEG;IACH,OAAO,CAAC,SAAS;IAqCjB;;OAEG;IACG,IAAI;IAYJ,OAAO;IAUP,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,GAAE,GAAQ,EAAE,OAAO,CAAC,EAAE,aAAa;IAqC7D,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,GAAG,EAAE,OAAO,CAAC,EAAE,aAAa;IAwBxE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,aAAa;IA+BzE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,aAAa;IAoB9F,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa;CAmB1E"}
|
package/dist/index.js
CHANGED
|
@@ -8,6 +8,13 @@ export class ObjectQL {
|
|
|
8
8
|
constructor(hostContext = {}) {
|
|
9
9
|
this.drivers = new Map();
|
|
10
10
|
this.defaultDriver = null;
|
|
11
|
+
// Hooks Registry
|
|
12
|
+
this.hooks = {
|
|
13
|
+
'beforeFind': [], 'afterFind': [],
|
|
14
|
+
'beforeInsert': [], 'afterInsert': [],
|
|
15
|
+
'beforeUpdate': [], 'afterUpdate': [],
|
|
16
|
+
'beforeDelete': [], 'afterDelete': [],
|
|
17
|
+
};
|
|
11
18
|
// Host provided context additions (e.g. Server router)
|
|
12
19
|
this.hostContext = {};
|
|
13
20
|
this.hostContext = hostContext;
|
|
@@ -68,6 +75,25 @@ export class ObjectQL {
|
|
|
68
75
|
}
|
|
69
76
|
}
|
|
70
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* Register a hook
|
|
80
|
+
* @param event The event name (e.g. 'beforeFind', 'afterInsert')
|
|
81
|
+
* @param handler The handler function
|
|
82
|
+
*/
|
|
83
|
+
registerHook(event, handler) {
|
|
84
|
+
if (!this.hooks[event]) {
|
|
85
|
+
this.hooks[event] = [];
|
|
86
|
+
}
|
|
87
|
+
this.hooks[event].push(handler);
|
|
88
|
+
console.log(`[ObjectQL] Registered hook for ${event}`);
|
|
89
|
+
}
|
|
90
|
+
async triggerHooks(event, context) {
|
|
91
|
+
const handlers = this.hooks[event] || [];
|
|
92
|
+
for (const handler of handlers) {
|
|
93
|
+
// In a real system, we might want to catch errors here or allow them to bubble up
|
|
94
|
+
await handler(context);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
71
97
|
/**
|
|
72
98
|
* Register a new storage driver
|
|
73
99
|
*/
|
|
@@ -82,16 +108,47 @@ export class ObjectQL {
|
|
|
82
108
|
this.defaultDriver = driver.name;
|
|
83
109
|
}
|
|
84
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* Helper to get object definition
|
|
113
|
+
*/
|
|
114
|
+
getSchema(objectName) {
|
|
115
|
+
return SchemaRegistry.getObject(objectName);
|
|
116
|
+
}
|
|
85
117
|
/**
|
|
86
118
|
* Helper to get the target driver
|
|
87
119
|
*/
|
|
88
|
-
getDriver(
|
|
89
|
-
|
|
90
|
-
//
|
|
91
|
-
if (
|
|
92
|
-
|
|
120
|
+
getDriver(objectName) {
|
|
121
|
+
const object = SchemaRegistry.getObject(objectName);
|
|
122
|
+
// 1. If object definition exists, check for explicit datasource
|
|
123
|
+
if (object) {
|
|
124
|
+
const datasourceName = object.datasource || 'default';
|
|
125
|
+
// If configured for 'default', try to find the default driver
|
|
126
|
+
if (datasourceName === 'default') {
|
|
127
|
+
if (this.defaultDriver && this.drivers.has(this.defaultDriver)) {
|
|
128
|
+
return this.drivers.get(this.defaultDriver);
|
|
129
|
+
}
|
|
130
|
+
// Fallback: If 'default' not explicitly set, use the first available driver?
|
|
131
|
+
// Better to be strict.
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
// Specific datasource requested
|
|
135
|
+
if (this.drivers.has(datasourceName)) {
|
|
136
|
+
return this.drivers.get(datasourceName);
|
|
137
|
+
}
|
|
138
|
+
// If not found, fall back to default? Or error?
|
|
139
|
+
// Standard behavior: Error if specific datasource is missing.
|
|
140
|
+
throw new Error(`[ObjectQL] Datasource '${datasourceName}' configured for object '${objectName}' is not registered.`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// 2. Fallback for ad-hoc objects or missing definitions
|
|
144
|
+
// If we have a default driver, use it.
|
|
145
|
+
if (this.defaultDriver) {
|
|
146
|
+
if (!object) {
|
|
147
|
+
console.warn(`[ObjectQL] Object '${objectName}' not found in registry. Using default driver.`);
|
|
148
|
+
}
|
|
149
|
+
return this.drivers.get(this.defaultDriver);
|
|
93
150
|
}
|
|
94
|
-
|
|
151
|
+
throw new Error(`[ObjectQL] No driver available for object '${objectName}'`);
|
|
95
152
|
}
|
|
96
153
|
/**
|
|
97
154
|
* Initialize the engine and all registered drivers
|
|
@@ -116,39 +173,114 @@ export class ObjectQL {
|
|
|
116
173
|
// ============================================
|
|
117
174
|
// Data Access Methods
|
|
118
175
|
// ============================================
|
|
119
|
-
async find(object,
|
|
176
|
+
async find(object, query = {}, options) {
|
|
120
177
|
const driver = this.getDriver(object);
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
178
|
+
// Normalize QueryAST
|
|
179
|
+
let ast;
|
|
180
|
+
if (query.where || query.fields || query.orderBy || query.limit) {
|
|
181
|
+
ast = { object, ...query };
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
ast = { object, where: query };
|
|
185
|
+
}
|
|
186
|
+
if (ast.limit === undefined)
|
|
187
|
+
ast.limit = 100;
|
|
188
|
+
// Trigger Before Hook
|
|
189
|
+
const hookContext = {
|
|
190
|
+
object,
|
|
191
|
+
event: 'beforeFind',
|
|
192
|
+
input: { ast, options }, // Hooks can modify AST here
|
|
193
|
+
ql: this
|
|
132
194
|
};
|
|
133
|
-
|
|
195
|
+
await this.triggerHooks('beforeFind', hookContext);
|
|
196
|
+
try {
|
|
197
|
+
const result = await driver.find(object, hookContext.input.ast, hookContext.input.options);
|
|
198
|
+
// Trigger After Hook
|
|
199
|
+
hookContext.event = 'afterFind';
|
|
200
|
+
hookContext.result = result;
|
|
201
|
+
await this.triggerHooks('afterFind', hookContext);
|
|
202
|
+
return hookContext.result;
|
|
203
|
+
}
|
|
204
|
+
catch (e) {
|
|
205
|
+
// hookContext.error = e;
|
|
206
|
+
throw e;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
async findOne(object, idOrQuery, options) {
|
|
210
|
+
const driver = this.getDriver(object);
|
|
211
|
+
let ast;
|
|
212
|
+
if (typeof idOrQuery === 'string') {
|
|
213
|
+
ast = {
|
|
214
|
+
object,
|
|
215
|
+
where: { _id: idOrQuery }
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
// Assume query object
|
|
220
|
+
// reuse logic from find() or just wrap it
|
|
221
|
+
if (idOrQuery.where || idOrQuery.fields) {
|
|
222
|
+
ast = { object, ...idOrQuery };
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
ast = { object, where: idOrQuery };
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
// Limit 1 for findOne
|
|
229
|
+
ast.limit = 1;
|
|
230
|
+
return driver.findOne(object, ast, options);
|
|
134
231
|
}
|
|
135
232
|
async insert(object, data, options) {
|
|
136
233
|
const driver = this.getDriver(object);
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
234
|
+
// 1. Get Schema
|
|
235
|
+
const schema = SchemaRegistry.getObject(object);
|
|
236
|
+
if (schema) {
|
|
237
|
+
// TODO: Validation Logic
|
|
238
|
+
// validate(schema, data);
|
|
239
|
+
}
|
|
240
|
+
// 2. Trigger Before Hook
|
|
241
|
+
const hookContext = {
|
|
242
|
+
object,
|
|
243
|
+
event: 'beforeInsert',
|
|
244
|
+
input: { data, options },
|
|
245
|
+
ql: this
|
|
246
|
+
};
|
|
247
|
+
await this.triggerHooks('beforeInsert', hookContext);
|
|
248
|
+
// 3. Execute Driver
|
|
249
|
+
const result = await driver.create(object, hookContext.input.data, hookContext.input.options);
|
|
250
|
+
// 4. Trigger After Hook
|
|
251
|
+
hookContext.event = 'afterInsert';
|
|
252
|
+
hookContext.result = result;
|
|
253
|
+
await this.triggerHooks('afterInsert', hookContext);
|
|
254
|
+
return hookContext.result;
|
|
143
255
|
}
|
|
144
256
|
async update(object, id, data, options) {
|
|
145
257
|
const driver = this.getDriver(object);
|
|
146
|
-
|
|
147
|
-
|
|
258
|
+
const hookContext = {
|
|
259
|
+
object,
|
|
260
|
+
event: 'beforeUpdate',
|
|
261
|
+
input: { id, data, options },
|
|
262
|
+
ql: this
|
|
263
|
+
};
|
|
264
|
+
await this.triggerHooks('beforeUpdate', hookContext);
|
|
265
|
+
const result = await driver.update(object, hookContext.input.id, hookContext.input.data, hookContext.input.options);
|
|
266
|
+
hookContext.event = 'afterUpdate';
|
|
267
|
+
hookContext.result = result;
|
|
268
|
+
await this.triggerHooks('afterUpdate', hookContext);
|
|
269
|
+
return hookContext.result;
|
|
148
270
|
}
|
|
149
271
|
async delete(object, id, options) {
|
|
150
272
|
const driver = this.getDriver(object);
|
|
151
|
-
|
|
152
|
-
|
|
273
|
+
const hookContext = {
|
|
274
|
+
object,
|
|
275
|
+
event: 'beforeDelete',
|
|
276
|
+
input: { id, options },
|
|
277
|
+
ql: this
|
|
278
|
+
};
|
|
279
|
+
await this.triggerHooks('beforeDelete', hookContext);
|
|
280
|
+
const result = await driver.delete(object, hookContext.input.id, hookContext.input.options);
|
|
281
|
+
hookContext.event = 'afterDelete';
|
|
282
|
+
hookContext.result = result;
|
|
283
|
+
await this.triggerHooks('afterDelete', hookContext);
|
|
284
|
+
return hookContext.result;
|
|
153
285
|
}
|
|
154
286
|
}
|
package/dist/registry.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { ServiceObject } from '@objectstack/spec/data';
|
|
2
|
-
import {
|
|
3
|
-
import { ObjectStackManifest } from '@objectstack/spec/system';
|
|
2
|
+
import { ObjectStackManifest } from '@objectstack/spec/kernel';
|
|
4
3
|
/**
|
|
5
4
|
* Global Schema Registry
|
|
6
5
|
* Unified storage for all metadata types (Objects, Apps, Flows, Layouts, etc.)
|
|
@@ -32,12 +31,6 @@ export declare class SchemaRegistry {
|
|
|
32
31
|
static registerObject(schema: ServiceObject): void;
|
|
33
32
|
static getObject(name: string): ServiceObject | undefined;
|
|
34
33
|
static getAllObjects(): ServiceObject[];
|
|
35
|
-
/**
|
|
36
|
-
* App Helpers
|
|
37
|
-
*/
|
|
38
|
-
static registerApp(app: App): void;
|
|
39
|
-
static getApp(name: string): App | undefined;
|
|
40
|
-
static getAllApps(): App[];
|
|
41
34
|
/**
|
|
42
35
|
* Plugin Helpers
|
|
43
36
|
*/
|
package/dist/registry.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAE/D;;;GAGG;AACH,qBAAa,cAAc;IAEzB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAuC;IAE9D;;;;;OAKG;IACH,MAAM,CAAC,YAAY,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,GAAE,MAAM,CAAqB;IAcnF;;OAEG;IACH,MAAM,CAAC,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAI5D;;OAEG;IACH,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,EAAE;IAItC;;OAEG;IACH,MAAM,CAAC,kBAAkB,IAAI,MAAM,EAAE;IAQrC;;OAEG;IACH,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,aAAa;IAI3C,MAAM,CAAC,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAIzD,MAAM,CAAC,aAAa,IAAI,aAAa,EAAE;IAIvC;;OAEG;IACH,MAAM,CAAC,cAAc,CAAC,QAAQ,EAAE,mBAAmB;IAInD,MAAM,CAAC,aAAa,IAAI,mBAAmB,EAAE;IAI7C;;OAEG;IACH,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,CAAA;KAAE;IAIzD,MAAM,CAAC,WAAW,IAAI;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,CAAA;KAAE,EAAE;CAGxD"}
|
package/dist/registry.js
CHANGED
|
@@ -54,18 +54,6 @@ export class SchemaRegistry {
|
|
|
54
54
|
static getAllObjects() {
|
|
55
55
|
return this.listItems('object');
|
|
56
56
|
}
|
|
57
|
-
/**
|
|
58
|
-
* App Helpers
|
|
59
|
-
*/
|
|
60
|
-
static registerApp(app) {
|
|
61
|
-
this.registerItem('app', app, 'name');
|
|
62
|
-
}
|
|
63
|
-
static getApp(name) {
|
|
64
|
-
return this.getItem('app', name);
|
|
65
|
-
}
|
|
66
|
-
static getAllApps() {
|
|
67
|
-
return this.listItems('app');
|
|
68
|
-
}
|
|
69
57
|
/**
|
|
70
58
|
* Plugin Helpers
|
|
71
59
|
*/
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@objectstack/objectql",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"description": "Isomorphic ObjectQL Engine for ObjectStack",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "src/index.ts",
|
|
7
7
|
"dependencies": {
|
|
8
|
-
"@objectstack/spec": "0.3.
|
|
8
|
+
"@objectstack/spec": "0.3.3"
|
|
9
9
|
},
|
|
10
10
|
"devDependencies": {
|
|
11
11
|
"typescript": "^5.0.0",
|
package/src/index.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import { QueryAST } from '@objectstack/spec/data';
|
|
2
|
-
import {
|
|
1
|
+
import { QueryAST, HookContext } from '@objectstack/spec/data';
|
|
2
|
+
import { ObjectStackManifest } from '@objectstack/spec/kernel';
|
|
3
|
+
import { DriverInterface, DriverOptions } from '@objectstack/spec/driver';
|
|
3
4
|
import { SchemaRegistry } from './registry';
|
|
4
5
|
|
|
5
6
|
// Export Registry for consumers
|
|
6
7
|
export { SchemaRegistry } from './registry';
|
|
7
8
|
|
|
9
|
+
export type HookHandler = (context: HookContext) => Promise<void> | void;
|
|
10
|
+
|
|
8
11
|
/**
|
|
9
12
|
* Host Context provided to plugins
|
|
10
13
|
*/
|
|
@@ -22,6 +25,14 @@ export class ObjectQL {
|
|
|
22
25
|
private drivers = new Map<string, DriverInterface>();
|
|
23
26
|
private defaultDriver: string | null = null;
|
|
24
27
|
|
|
28
|
+
// Hooks Registry
|
|
29
|
+
private hooks: Record<string, HookHandler[]> = {
|
|
30
|
+
'beforeFind': [], 'afterFind': [],
|
|
31
|
+
'beforeInsert': [], 'afterInsert': [],
|
|
32
|
+
'beforeUpdate': [], 'afterUpdate': [],
|
|
33
|
+
'beforeDelete': [], 'afterDelete': [],
|
|
34
|
+
};
|
|
35
|
+
|
|
25
36
|
// Host provided context additions (e.g. Server router)
|
|
26
37
|
private hostContext: Record<string, any> = {};
|
|
27
38
|
|
|
@@ -93,6 +104,27 @@ export class ObjectQL {
|
|
|
93
104
|
}
|
|
94
105
|
}
|
|
95
106
|
|
|
107
|
+
/**
|
|
108
|
+
* Register a hook
|
|
109
|
+
* @param event The event name (e.g. 'beforeFind', 'afterInsert')
|
|
110
|
+
* @param handler The handler function
|
|
111
|
+
*/
|
|
112
|
+
registerHook(event: string, handler: HookHandler) {
|
|
113
|
+
if (!this.hooks[event]) {
|
|
114
|
+
this.hooks[event] = [];
|
|
115
|
+
}
|
|
116
|
+
this.hooks[event].push(handler);
|
|
117
|
+
console.log(`[ObjectQL] Registered hook for ${event}`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private async triggerHooks(event: string, context: HookContext) {
|
|
121
|
+
const handlers = this.hooks[event] || [];
|
|
122
|
+
for (const handler of handlers) {
|
|
123
|
+
// In a real system, we might want to catch errors here or allow them to bubble up
|
|
124
|
+
await handler(context);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
96
128
|
/**
|
|
97
129
|
* Register a new storage driver
|
|
98
130
|
*/
|
|
@@ -110,16 +142,51 @@ export class ObjectQL {
|
|
|
110
142
|
}
|
|
111
143
|
}
|
|
112
144
|
|
|
145
|
+
/**
|
|
146
|
+
* Helper to get object definition
|
|
147
|
+
*/
|
|
148
|
+
getSchema(objectName: string) {
|
|
149
|
+
return SchemaRegistry.getObject(objectName);
|
|
150
|
+
}
|
|
151
|
+
|
|
113
152
|
/**
|
|
114
153
|
* Helper to get the target driver
|
|
115
154
|
*/
|
|
116
|
-
private getDriver(
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
155
|
+
private getDriver(objectName: string): DriverInterface {
|
|
156
|
+
const object = SchemaRegistry.getObject(objectName);
|
|
157
|
+
|
|
158
|
+
// 1. If object definition exists, check for explicit datasource
|
|
159
|
+
if (object) {
|
|
160
|
+
const datasourceName = object.datasource || 'default';
|
|
161
|
+
|
|
162
|
+
// If configured for 'default', try to find the default driver
|
|
163
|
+
if (datasourceName === 'default') {
|
|
164
|
+
if (this.defaultDriver && this.drivers.has(this.defaultDriver)) {
|
|
165
|
+
return this.drivers.get(this.defaultDriver)!;
|
|
166
|
+
}
|
|
167
|
+
// Fallback: If 'default' not explicitly set, use the first available driver?
|
|
168
|
+
// Better to be strict.
|
|
169
|
+
} else {
|
|
170
|
+
// Specific datasource requested
|
|
171
|
+
if (this.drivers.has(datasourceName)) {
|
|
172
|
+
return this.drivers.get(datasourceName)!;
|
|
173
|
+
}
|
|
174
|
+
// If not found, fall back to default? Or error?
|
|
175
|
+
// Standard behavior: Error if specific datasource is missing.
|
|
176
|
+
throw new Error(`[ObjectQL] Datasource '${datasourceName}' configured for object '${objectName}' is not registered.`);
|
|
177
|
+
}
|
|
121
178
|
}
|
|
122
|
-
|
|
179
|
+
|
|
180
|
+
// 2. Fallback for ad-hoc objects or missing definitions
|
|
181
|
+
// If we have a default driver, use it.
|
|
182
|
+
if (this.defaultDriver) {
|
|
183
|
+
if (!object) {
|
|
184
|
+
console.warn(`[ObjectQL] Object '${objectName}' not found in registry. Using default driver.`);
|
|
185
|
+
}
|
|
186
|
+
return this.drivers.get(this.defaultDriver)!;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
throw new Error(`[ObjectQL] No driver available for object '${objectName}'`);
|
|
123
190
|
}
|
|
124
191
|
|
|
125
192
|
/**
|
|
@@ -147,46 +214,135 @@ export class ObjectQL {
|
|
|
147
214
|
// Data Access Methods
|
|
148
215
|
// ============================================
|
|
149
216
|
|
|
150
|
-
async find(object: string,
|
|
217
|
+
async find(object: string, query: any = {}, options?: DriverOptions) {
|
|
151
218
|
const driver = this.getDriver(object);
|
|
152
|
-
console.log(`[ObjectQL] Finding ${object} on ${driver.name}...`);
|
|
153
219
|
|
|
154
|
-
//
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
220
|
+
// Normalize QueryAST
|
|
221
|
+
let ast: QueryAST;
|
|
222
|
+
if (query.where || query.fields || query.orderBy || query.limit) {
|
|
223
|
+
ast = { object, ...query } as QueryAST;
|
|
224
|
+
} else {
|
|
225
|
+
ast = { object, where: query } as QueryAST;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (ast.limit === undefined) ast.limit = 100;
|
|
229
|
+
|
|
230
|
+
// Trigger Before Hook
|
|
231
|
+
const hookContext: HookContext = {
|
|
232
|
+
object,
|
|
233
|
+
event: 'beforeFind',
|
|
234
|
+
input: { ast, options }, // Hooks can modify AST here
|
|
235
|
+
ql: this
|
|
164
236
|
};
|
|
237
|
+
await this.triggerHooks('beforeFind', hookContext);
|
|
165
238
|
|
|
166
|
-
|
|
239
|
+
try {
|
|
240
|
+
const result = await driver.find(object, hookContext.input.ast, hookContext.input.options);
|
|
241
|
+
|
|
242
|
+
// Trigger After Hook
|
|
243
|
+
hookContext.event = 'afterFind';
|
|
244
|
+
hookContext.result = result;
|
|
245
|
+
await this.triggerHooks('afterFind', hookContext);
|
|
246
|
+
|
|
247
|
+
return hookContext.result;
|
|
248
|
+
} catch (e) {
|
|
249
|
+
// hookContext.error = e;
|
|
250
|
+
throw e;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
async findOne(object: string, idOrQuery: string | any, options?: DriverOptions) {
|
|
255
|
+
const driver = this.getDriver(object);
|
|
256
|
+
|
|
257
|
+
let ast: QueryAST;
|
|
258
|
+
if (typeof idOrQuery === 'string') {
|
|
259
|
+
ast = {
|
|
260
|
+
object,
|
|
261
|
+
where: { _id: idOrQuery }
|
|
262
|
+
};
|
|
263
|
+
} else {
|
|
264
|
+
// Assume query object
|
|
265
|
+
// reuse logic from find() or just wrap it
|
|
266
|
+
if (idOrQuery.where || idOrQuery.fields) {
|
|
267
|
+
ast = { object, ...idOrQuery };
|
|
268
|
+
} else {
|
|
269
|
+
ast = { object, where: idOrQuery };
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
// Limit 1 for findOne
|
|
273
|
+
ast.limit = 1;
|
|
274
|
+
|
|
275
|
+
return driver.findOne(object, ast, options);
|
|
167
276
|
}
|
|
168
277
|
|
|
169
278
|
async insert(object: string, data: Record<string, any>, options?: DriverOptions) {
|
|
170
279
|
const driver = this.getDriver(object);
|
|
171
|
-
console.log(`[ObjectQL] Creating ${object} on ${driver.name}...`);
|
|
172
|
-
// 1. Validate Schema
|
|
173
|
-
// 2. Run "Before Insert" Triggers
|
|
174
280
|
|
|
175
|
-
|
|
281
|
+
// 1. Get Schema
|
|
282
|
+
const schema = SchemaRegistry.getObject(object);
|
|
283
|
+
|
|
284
|
+
if (schema) {
|
|
285
|
+
// TODO: Validation Logic
|
|
286
|
+
// validate(schema, data);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// 2. Trigger Before Hook
|
|
290
|
+
const hookContext: HookContext = {
|
|
291
|
+
object,
|
|
292
|
+
event: 'beforeInsert',
|
|
293
|
+
input: { data, options },
|
|
294
|
+
ql: this
|
|
295
|
+
};
|
|
296
|
+
await this.triggerHooks('beforeInsert', hookContext);
|
|
176
297
|
|
|
177
|
-
// 3.
|
|
178
|
-
|
|
298
|
+
// 3. Execute Driver
|
|
299
|
+
const result = await driver.create(object, hookContext.input.data, hookContext.input.options);
|
|
300
|
+
|
|
301
|
+
// 4. Trigger After Hook
|
|
302
|
+
hookContext.event = 'afterInsert';
|
|
303
|
+
hookContext.result = result;
|
|
304
|
+
await this.triggerHooks('afterInsert', hookContext);
|
|
305
|
+
|
|
306
|
+
return hookContext.result;
|
|
179
307
|
}
|
|
180
308
|
|
|
181
|
-
async update(object: string, id: string, data: Record<string, any>, options?: DriverOptions) {
|
|
309
|
+
async update(object: string, id: string | number, data: Record<string, any>, options?: DriverOptions) {
|
|
182
310
|
const driver = this.getDriver(object);
|
|
183
|
-
|
|
184
|
-
|
|
311
|
+
|
|
312
|
+
const hookContext: HookContext = {
|
|
313
|
+
object,
|
|
314
|
+
event: 'beforeUpdate',
|
|
315
|
+
input: { id, data, options },
|
|
316
|
+
ql: this
|
|
317
|
+
};
|
|
318
|
+
await this.triggerHooks('beforeUpdate', hookContext);
|
|
319
|
+
|
|
320
|
+
const result = await driver.update(object, hookContext.input.id, hookContext.input.data, hookContext.input.options);
|
|
321
|
+
|
|
322
|
+
hookContext.event = 'afterUpdate';
|
|
323
|
+
hookContext.result = result;
|
|
324
|
+
await this.triggerHooks('afterUpdate', hookContext);
|
|
325
|
+
|
|
326
|
+
return hookContext.result;
|
|
185
327
|
}
|
|
186
328
|
|
|
187
|
-
async delete(object: string, id: string, options?: DriverOptions) {
|
|
329
|
+
async delete(object: string, id: string | number, options?: DriverOptions) {
|
|
188
330
|
const driver = this.getDriver(object);
|
|
189
|
-
|
|
190
|
-
|
|
331
|
+
|
|
332
|
+
const hookContext: HookContext = {
|
|
333
|
+
object,
|
|
334
|
+
event: 'beforeDelete',
|
|
335
|
+
input: { id, options },
|
|
336
|
+
ql: this
|
|
337
|
+
};
|
|
338
|
+
await this.triggerHooks('beforeDelete', hookContext);
|
|
339
|
+
|
|
340
|
+
const result = await driver.delete(object, hookContext.input.id, hookContext.input.options);
|
|
341
|
+
|
|
342
|
+
hookContext.event = 'afterDelete';
|
|
343
|
+
hookContext.result = result;
|
|
344
|
+
await this.triggerHooks('afterDelete', hookContext);
|
|
345
|
+
|
|
346
|
+
return hookContext.result;
|
|
191
347
|
}
|
|
192
348
|
}
|
package/src/registry.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { ServiceObject } from '@objectstack/spec/data';
|
|
2
|
-
import {
|
|
3
|
-
import { ObjectStackManifest } from '@objectstack/spec/system';
|
|
2
|
+
import { ObjectStackManifest } from '@objectstack/spec/kernel';
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* Global Schema Registry
|
|
@@ -70,21 +69,6 @@ export class SchemaRegistry {
|
|
|
70
69
|
return this.listItems<ServiceObject>('object');
|
|
71
70
|
}
|
|
72
71
|
|
|
73
|
-
/**
|
|
74
|
-
* App Helpers
|
|
75
|
-
*/
|
|
76
|
-
static registerApp(app: App) {
|
|
77
|
-
this.registerItem('app', app, 'name');
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
static getApp(name: string): App | undefined {
|
|
81
|
-
return this.getItem<App>('app', name);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
static getAllApps(): App[] {
|
|
85
|
-
return this.listItems<App>('app');
|
|
86
|
-
}
|
|
87
|
-
|
|
88
72
|
/**
|
|
89
73
|
* Plugin Helpers
|
|
90
74
|
*/
|