@objectql/core 1.0.0 → 1.2.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.
@@ -1,104 +0,0 @@
1
- /**
2
- * Represents the supported field data types in the ObjectQL schema.
3
- * These types determine how data is stored, validated, and rendered.
4
- *
5
- * - `text`: Simple string.
6
- * - `textarea`: Long string.
7
- * - `select`: Choice from a list.
8
- * - `lookup`: Relationship to another object.
9
- */
10
- export type FieldType = 'text' | 'textarea' | 'html' | 'select' | 'multiselect' | 'date' | 'datetime' | 'number' | 'currency' | 'boolean' | 'lookup' | 'master_detail' | 'password' | 'object' | 'grid';
11
- /**
12
- * Defines a single option for select/multiselect fields.
13
- */
14
- export interface FieldOption {
15
- /** The display label for the option. */
16
- label: string;
17
- /** The actual value stored in the database. */
18
- value: string | number;
19
- }
20
- /**
21
- * Configuration for a single field on an object.
22
- * This defines the schema, validation rules, and UI hints for the attribute.
23
- */
24
- export interface FieldConfig {
25
- /**
26
- * The unique API name of the field.
27
- * If defined within an object map, this is often automatically populated from the key.
28
- */
29
- name?: string;
30
- /** The human-readable label used in UIs. */
31
- label?: string;
32
- /** The data type of the field. */
33
- type: FieldType;
34
- /** Whether the field is mandatory. Defaults to false. */
35
- required?: boolean;
36
- /** The default value if not provided during creation. */
37
- defaultValue?: any;
38
- /**
39
- * Options available for `select` or `multiselect` types.
40
- * Can be an array of strings or {@link FieldOption} objects.
41
- */
42
- options?: FieldOption[] | string[];
43
- /** Number of decimal places for `currency` types (e.g., 2). */
44
- scale?: number;
45
- /** Total number of digits for `number` types. */
46
- precision?: number;
47
- /**
48
- * The API name of the target object.
49
- * Required when type is `lookup` or `master_detail`.
50
- */
51
- reference_to?: string;
52
- /** Implementation hint: Whether this field should be indexed for search. */
53
- searchable?: boolean;
54
- /** Implementation hint: Whether this field is sortable in lists. */
55
- sortable?: boolean;
56
- /** Implementation hint: Whether to create a database index for this column. */
57
- index?: boolean;
58
- /** Description for documentation purposes. */
59
- description?: string;
60
- }
61
- /**
62
- * Configuration for a custom action (RPC).
63
- */
64
- export interface ActionConfig {
65
- label?: string;
66
- description?: string;
67
- /** Output/Result type definition. */
68
- result?: {
69
- type: FieldType;
70
- };
71
- /** Input parameters schema. */
72
- params?: Record<string, FieldConfig>;
73
- /** Implementation of the action. */
74
- handler?: (ctx: any, params: any) => Promise<any>;
75
- }
76
- import { HookFunction } from './types';
77
- export interface ObjectListeners {
78
- beforeCreate?: HookFunction;
79
- afterCreate?: HookFunction;
80
- beforeUpdate?: HookFunction;
81
- afterUpdate?: HookFunction;
82
- beforeDelete?: HookFunction;
83
- afterDelete?: HookFunction;
84
- beforeFind?: HookFunction;
85
- afterFind?: HookFunction;
86
- }
87
- /**
88
- * Configuration for a business object (Entity).
89
- * Analogous to a Database Table or MongoDB Collection.
90
- */
91
- export interface ObjectConfig {
92
- name: string;
93
- datasource?: string;
94
- label?: string;
95
- icon?: string;
96
- description?: string;
97
- fields: Record<string, FieldConfig>;
98
- /** Custom Actions (RPC) defined on this object. */
99
- actions?: Record<string, ActionConfig>;
100
- /** Lifecycle hooks. */
101
- listeners?: ObjectListeners;
102
- /** Initial data to populate when system starts. */
103
- data?: any[];
104
- }
package/dist/metadata.js DELETED
@@ -1,3 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- //# sourceMappingURL=metadata.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"metadata.js","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":""}
package/dist/query.d.ts DELETED
@@ -1,10 +0,0 @@
1
- export type FilterCriterion = [string, string, any];
2
- export type FilterExpression = FilterCriterion | 'and' | 'or' | FilterExpression[];
3
- export interface UnifiedQuery {
4
- fields?: string[];
5
- filters?: FilterExpression[];
6
- sort?: [string, 'asc' | 'desc'][];
7
- skip?: number;
8
- limit?: number;
9
- expand?: Record<string, UnifiedQuery>;
10
- }
package/dist/query.js DELETED
@@ -1,3 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- //# sourceMappingURL=query.js.map
package/dist/query.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"query.js","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":""}
package/dist/types.d.ts DELETED
@@ -1,77 +0,0 @@
1
- import { ObjectRepository } from "./repository";
2
- import { ObjectConfig } from "./metadata";
3
- import { Driver } from "./driver";
4
- import { UnifiedQuery, FilterCriterion } from "./query";
5
- export { ObjectConfig } from "./metadata";
6
- export interface ObjectQLConfig {
7
- datasources: Record<string, Driver>;
8
- objects?: Record<string, ObjectConfig>;
9
- packages?: string[];
10
- }
11
- export interface IObjectQL {
12
- getObject(name: string): ObjectConfig | undefined;
13
- getConfigs(): Record<string, ObjectConfig>;
14
- datasource(name: string): Driver;
15
- init(): Promise<void>;
16
- }
17
- export interface HookContext<T = any> {
18
- ctx: ObjectQLContext;
19
- entity: string;
20
- op: 'find' | 'create' | 'update' | 'delete' | 'count' | 'aggregate';
21
- doc?: T;
22
- query?: UnifiedQuery;
23
- getPreviousDoc: () => Promise<T | undefined>;
24
- utils: {
25
- /**
26
- * Safely injects a new filter criterion into the existing AST.
27
- * It wraps existing filters in a new group to preserve operator precedence.
28
- * * Logic: (Existing_Filters) AND (New_Filter)
29
- */
30
- restrict: (criterion: FilterCriterion) => void;
31
- };
32
- }
33
- export type HookFunction = (context: HookContext) => Promise<void>;
34
- export interface ObjectQLContext {
35
- userId?: string;
36
- spaceId?: string;
37
- roles: string[];
38
- /**
39
- * Sudo Mode / System Bypass.
40
- * - true: Bypasses all permission checks (CRUD, Field Level Security, Record Level Security).
41
- * - false/undefined: Enforces all permission checks based on 'roles'.
42
- */
43
- isSystem?: boolean;
44
- /**
45
- * Trigger Control.
46
- * - true: Skips all lifecycle hooks (beforeCreate, afterUpdate, etc.).
47
- * - Useful for bulk data imports or raw data correction to prevent side effects.
48
- * - Requires 'isSystem: true' (Security Safeguard).
49
- */
50
- ignoreTriggers?: boolean;
51
- /**
52
- * Returns a repository proxy bound to this context.
53
- * All operations performed via this proxy inherit userId, spaceId, and transaction.
54
- */
55
- object(entityName: string): ObjectRepository;
56
- /**
57
- * Execute a function within a transaction.
58
- * The callback receives a new context 'trxCtx' which inherits userId and spaceId from this context.
59
- */
60
- transaction(callback: (trxCtx: ObjectQLContext) => Promise<any>): Promise<any>;
61
- /**
62
- * Returns a new context with system privileges (isSystem: true).
63
- * It shares the same transaction scope as the current context.
64
- */
65
- sudo(): ObjectQLContext;
66
- /**
67
- * Internal: Driver-specific transaction handle.
68
- */
69
- transactionHandle?: any;
70
- }
71
- export interface ObjectQLContextOptions {
72
- userId?: string;
73
- spaceId?: string;
74
- roles?: string[];
75
- isSystem?: boolean;
76
- ignoreTriggers?: boolean;
77
- }
package/dist/types.js DELETED
@@ -1,3 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- //# sourceMappingURL=types.js.map
package/dist/types.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/src/driver.ts DELETED
@@ -1,24 +0,0 @@
1
- export interface Driver {
2
- find(objectName: string, query: any, options?: any): Promise<any[]>;
3
- findOne(objectName: string, id: string | number, query?: any, options?: any): Promise<any>;
4
- create(objectName: string, data: any, options?: any): Promise<any>;
5
- update(objectName: string, id: string | number, data: any, options?: any): Promise<any>;
6
- delete(objectName: string, id: string | number, options?: any): Promise<any>;
7
- count(objectName: string, filters: any, options?: any): Promise<number>;
8
-
9
- // Advanced
10
- aggregate?(objectName: string, query: any, options?: any): Promise<any>;
11
- distinct?(objectName: string, field: string, filters?: any, options?: any): Promise<any[]>;
12
-
13
- // Bulk / Atomic
14
- createMany?(objectName: string, data: any[], options?: any): Promise<any>;
15
- updateMany?(objectName: string, filters: any, data: any, options?: any): Promise<any>;
16
- deleteMany?(objectName: string, filters: any, options?: any): Promise<any>;
17
- findOneAndUpdate?(objectName: string, filters: any, update: any, options?: any): Promise<any>;
18
-
19
- // Transaction
20
- beginTransaction?(): Promise<any>;
21
- commitTransaction?(trx: any): Promise<void>;
22
- rollbackTransaction?(trx: any): Promise<void>;
23
- }
24
-
package/src/metadata.ts DELETED
@@ -1,143 +0,0 @@
1
- /**
2
- * Represents the supported field data types in the ObjectQL schema.
3
- * These types determine how data is stored, validated, and rendered.
4
- *
5
- * - `text`: Simple string.
6
- * - `textarea`: Long string.
7
- * - `select`: Choice from a list.
8
- * - `lookup`: Relationship to another object.
9
- */
10
- export type FieldType =
11
- | 'text'
12
- | 'textarea'
13
- | 'html'
14
- | 'select'
15
- | 'multiselect'
16
- | 'date'
17
- | 'datetime'
18
- | 'number'
19
- | 'currency'
20
- | 'boolean'
21
- | 'lookup'
22
- | 'master_detail'
23
- | 'password'
24
- | 'object'
25
- | 'grid';
26
-
27
- /**
28
- * Defines a single option for select/multiselect fields.
29
- */
30
- export interface FieldOption {
31
- /** The display label for the option. */
32
- label: string;
33
- /** The actual value stored in the database. */
34
- value: string | number;
35
- }
36
-
37
- /**
38
- * Configuration for a single field on an object.
39
- * This defines the schema, validation rules, and UI hints for the attribute.
40
- */
41
- export interface FieldConfig {
42
- /**
43
- * The unique API name of the field.
44
- * If defined within an object map, this is often automatically populated from the key.
45
- */
46
- name?: string;
47
-
48
- /** The human-readable label used in UIs. */
49
- label?: string;
50
-
51
- /** The data type of the field. */
52
- type: FieldType;
53
-
54
- /** Whether the field is mandatory. Defaults to false. */
55
- required?: boolean;
56
-
57
- /** The default value if not provided during creation. */
58
- defaultValue?: any;
59
-
60
- // String options
61
- /**
62
- * Options available for `select` or `multiselect` types.
63
- * Can be an array of strings or {@link FieldOption} objects.
64
- */
65
- options?: FieldOption[] | string[];
66
-
67
- // Number options
68
- /** Number of decimal places for `currency` types (e.g., 2). */
69
- scale?: number;
70
- /** Total number of digits for `number` types. */
71
- precision?: number;
72
-
73
- // Relationship properties
74
- /**
75
- * The API name of the target object.
76
- * Required when type is `lookup` or `master_detail`.
77
- */
78
- reference_to?: string;
79
-
80
- // UI properties (kept for compatibility, though ObjectQL is a query engine)
81
- /** Implementation hint: Whether this field should be indexed for search. */
82
- searchable?: boolean;
83
- /** Implementation hint: Whether this field is sortable in lists. */
84
- sortable?: boolean;
85
- /** Implementation hint: Whether to create a database index for this column. */
86
- index?: boolean;
87
-
88
- // Other properties
89
- /** Description for documentation purposes. */
90
- description?: string;
91
- }
92
-
93
- /**
94
- * Configuration for a custom action (RPC).
95
- */
96
- export interface ActionConfig {
97
- label?: string;
98
- description?: string;
99
- /** Output/Result type definition. */
100
- result?: {
101
- type: FieldType;
102
- };
103
- /** Input parameters schema. */
104
- params?: Record<string, FieldConfig>;
105
- /** Implementation of the action. */
106
- handler?: (ctx: any, params: any) => Promise<any>;
107
- }
108
-
109
- import { HookFunction } from './types';
110
-
111
- export interface ObjectListeners {
112
- beforeCreate?: HookFunction;
113
- afterCreate?: HookFunction;
114
- beforeUpdate?: HookFunction;
115
- afterUpdate?: HookFunction;
116
- beforeDelete?: HookFunction;
117
- afterDelete?: HookFunction;
118
- beforeFind?: HookFunction;
119
- afterFind?: HookFunction;
120
- }
121
-
122
- /**
123
- * Configuration for a business object (Entity).
124
- * Analogous to a Database Table or MongoDB Collection.
125
- */
126
- export interface ObjectConfig {
127
- name: string;
128
- datasource?: string; // The name of the datasource to use
129
- label?: string;
130
- icon?: string;
131
- description?: string;
132
-
133
- fields: Record<string, FieldConfig>;
134
-
135
- /** Custom Actions (RPC) defined on this object. */
136
- actions?: Record<string, ActionConfig>;
137
-
138
- /** Lifecycle hooks. */
139
- listeners?: ObjectListeners;
140
-
141
- /** Initial data to populate when system starts. */
142
- data?: any[];
143
- }
package/src/query.ts DELETED
@@ -1,11 +0,0 @@
1
- export type FilterCriterion = [string, string, any];
2
- export type FilterExpression = FilterCriterion | 'and' | 'or' | FilterExpression[];
3
-
4
- export interface UnifiedQuery {
5
- fields?: string[];
6
- filters?: FilterExpression[];
7
- sort?: [string, 'asc' | 'desc'][];
8
- skip?: number;
9
- limit?: number;
10
- expand?: Record<string, UnifiedQuery>;
11
- }
package/src/types.ts DELETED
@@ -1,109 +0,0 @@
1
- import { ObjectRepository } from "./repository";
2
- import { ObjectConfig } from "./metadata";
3
- import { Driver } from "./driver";
4
- import { UnifiedQuery, FilterCriterion } from "./query";
5
-
6
- export { ObjectConfig } from "./metadata";
7
-
8
- export interface ObjectQLConfig {
9
- datasources: Record<string, Driver>;
10
- objects?: Record<string, ObjectConfig>;
11
- packages?: string[];
12
- }
13
-
14
- export interface IObjectQL {
15
- getObject(name: string): ObjectConfig | undefined;
16
- getConfigs(): Record<string, ObjectConfig>;
17
- datasource(name: string): Driver;
18
- init(): Promise<void>;
19
- }
20
-
21
- export interface HookContext<T = any> {
22
- // === 1. The Session Context ===
23
- // Automatically propagates userId, spaceId, and Transaction.
24
- ctx: ObjectQLContext;
25
-
26
- // === 2. Operational Info ===
27
- entity: string;
28
- op: 'find' | 'create' | 'update' | 'delete' | 'count' | 'aggregate';
29
-
30
- // === 3. Data Payload (Mutable) ===
31
- // - In beforeCreate/Update: The data to be written.
32
- // - In afterCreate/Update: The result record returned from DB.
33
- doc?: T;
34
-
35
- // === 4. Query Context (Mutable, for 'find' only) ===
36
- // Complies strictly with the UnifiedQuery JSON-DSL (AST).
37
- // Developers can modify 'fields', 'sort', or wrap 'filters'.
38
- query?: UnifiedQuery;
39
-
40
- // === 5. Helpers ===
41
- getPreviousDoc: () => Promise<T | undefined>;
42
-
43
- // AST Manipulation Utilities
44
- utils: {
45
- /**
46
- * Safely injects a new filter criterion into the existing AST.
47
- * It wraps existing filters in a new group to preserve operator precedence.
48
- * * Logic: (Existing_Filters) AND (New_Filter)
49
- */
50
- restrict: (criterion: FilterCriterion) => void;
51
- };
52
- }
53
-
54
- export type HookFunction = (context: HookContext) => Promise<void>;
55
-
56
- export interface ObjectQLContext {
57
- // === Identity & Isolation ===
58
- userId?: string; // Current User ID
59
- spaceId?: string; // Multi-tenancy Isolation (Organization ID)
60
- roles: string[]; // RBAC Roles
61
-
62
- // === Execution Flags ===
63
- /**
64
- * Sudo Mode / System Bypass.
65
- * - true: Bypasses all permission checks (CRUD, Field Level Security, Record Level Security).
66
- * - false/undefined: Enforces all permission checks based on 'roles'.
67
- */
68
- isSystem?: boolean;
69
-
70
- /**
71
- * Trigger Control.
72
- * - true: Skips all lifecycle hooks (beforeCreate, afterUpdate, etc.).
73
- * - Useful for bulk data imports or raw data correction to prevent side effects.
74
- * - Requires 'isSystem: true' (Security Safeguard).
75
- */
76
- ignoreTriggers?: boolean;
77
-
78
- // === Data Entry Point ===
79
- /**
80
- * Returns a repository proxy bound to this context.
81
- * All operations performed via this proxy inherit userId, spaceId, and transaction.
82
- */
83
- object(entityName: string): ObjectRepository;
84
-
85
- /**
86
- * Execute a function within a transaction.
87
- * The callback receives a new context 'trxCtx' which inherits userId and spaceId from this context.
88
- */
89
- transaction(callback: (trxCtx: ObjectQLContext) => Promise<any>): Promise<any>;
90
-
91
- /**
92
- * Returns a new context with system privileges (isSystem: true).
93
- * It shares the same transaction scope as the current context.
94
- */
95
- sudo(): ObjectQLContext;
96
-
97
- /**
98
- * Internal: Driver-specific transaction handle.
99
- */
100
- transactionHandle?: any;
101
- }
102
-
103
- export interface ObjectQLContextOptions {
104
- userId?: string;
105
- spaceId?: string;
106
- roles?: string[];
107
- isSystem?: boolean;
108
- ignoreTriggers?: boolean;
109
- }
@@ -1,6 +0,0 @@
1
- // Fixture for testing action loader
2
- export const listenTo = 'project';
3
-
4
- export async function closeProject(ctx: any, params: any) {
5
- return { success: true };
6
- }
@@ -1,41 +0,0 @@
1
- name: project
2
- label: Project
3
- icon: standard:case
4
- enable_search: true
5
- fields:
6
- name:
7
- label: Project Name
8
- type: text
9
- required: true
10
- searchable: true
11
- index: true
12
-
13
- status:
14
- label: Status
15
- type: select
16
- options:
17
- - label: Planned
18
- value: planned
19
- - label: In Progress
20
- value: in_progress
21
- - label: Completed
22
- value: completed
23
- defaultValue: planned
24
-
25
- start_date:
26
- label: Start Date
27
- type: date
28
-
29
- owner:
30
- label: Project Manager
31
- type: lookup
32
- reference_to: users
33
-
34
- budget:
35
- label: Total Budget
36
- type: currency
37
- scale: 2
38
-
39
- description:
40
- label: Description
41
- type: textarea
@@ -1,22 +0,0 @@
1
- import { loadObjectConfigs } from '../src/loader';
2
- import * as path from 'path';
3
-
4
- describe('Loader', () => {
5
- it('should load object configs from directory', () => {
6
- const fixturesDir = path.join(__dirname, 'fixtures');
7
- const configs = loadObjectConfigs(fixturesDir);
8
- expect(configs).toBeDefined();
9
- expect(configs['project']).toBeDefined();
10
- expect(configs['project'].name).toBe('project');
11
- expect(configs['project'].fields).toBeDefined();
12
- expect(configs['project'].fields.name).toBeDefined();
13
- });
14
-
15
- it('should load actions from .action.ts files', () => {
16
- const fixturesDir = path.join(__dirname, 'fixtures');
17
- const configs = loadObjectConfigs(fixturesDir);
18
- expect(configs['project'].actions).toBeDefined();
19
- expect(configs['project'].actions!.closeProject).toBeDefined();
20
- expect(typeof configs['project'].actions!.closeProject.handler).toBe('function');
21
- });
22
- });
@@ -1,49 +0,0 @@
1
- import { ObjectQL } from '../src/index';
2
- import { ObjectConfig } from '../src/metadata';
3
- import * as fs from 'fs';
4
- import * as path from 'path';
5
- import * as yaml from 'js-yaml';
6
-
7
- describe('Metadata Loading', () => {
8
-
9
- it('should load definitions from .object.yml file', () => {
10
- // 1. Read YAML file
11
- const yamlPath = path.join(__dirname, 'fixtures', 'project.object.yml');
12
- const fileContents = fs.readFileSync(yamlPath, 'utf8');
13
-
14
- // 2. Parse YAML
15
- const objectDef = yaml.load(fileContents) as ObjectConfig;
16
-
17
- // 3. Verify Structure
18
- expect(objectDef.name).toBe('project');
19
- expect(objectDef.fields.name.type).toBe('text');
20
- expect(objectDef.fields.status.options).toHaveLength(3);
21
- expect(objectDef.fields.budget.type).toBe('currency');
22
- expect(objectDef.fields.owner.reference_to).toBe('users');
23
-
24
- // 4. Register with ObjectQL
25
- const app = new ObjectQL({
26
- datasources: {}
27
- });
28
-
29
- app.registerObject(objectDef);
30
-
31
- // 5. Verify Registration
32
- const retrieved = app.getObject('project');
33
- expect(retrieved).toBeDefined();
34
- expect(retrieved?.label).toBe('Project');
35
- });
36
-
37
- it('should validate required properties (manual validation simulation)', () => {
38
- const yamlPath = path.join(__dirname, 'fixtures', 'project.object.yml');
39
- const fileContents = fs.readFileSync(yamlPath, 'utf8');
40
- const objectDef = yaml.load(fileContents) as ObjectConfig;
41
-
42
- function validateObject(obj: ObjectConfig) {
43
- if (!obj.name) throw new Error('Object name is required');
44
- if (!obj.fields) throw new Error('Object fields are required');
45
- }
46
-
47
- expect(() => validateObject(objectDef)).not.toThrow();
48
- });
49
- });