@objectstack/objectql 4.0.3 → 4.0.5

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/index.d.ts CHANGED
@@ -1,61 +1,12 @@
1
- import { z } from 'zod';
2
- import * as _objectstack_spec_data from '@objectstack/spec/data';
3
- import { ObjectOwnership, ServiceObject, QueryAST, HookContext, EngineQueryOptions, DataEngineInsertOptions, EngineUpdateOptions, EngineDeleteOptions, EngineCountOptions, EngineAggregateOptions } from '@objectstack/spec/data';
1
+ import { ServiceObject, ObjectOwnership, HookContext, QueryAST, EngineQueryOptions, DataEngineInsertOptions, EngineUpdateOptions, EngineDeleteOptions, EngineCountOptions, EngineAggregateOptions, Hook } from '@objectstack/spec/data';
4
2
  import { ObjectStackManifest, InstalledPackage, ExecutionContext } from '@objectstack/spec/kernel';
5
3
  import { ObjectStackProtocol, MetadataCacheRequest, MetadataCacheResponse, BatchUpdateRequest, BatchUpdateResponse, UpdateManyDataRequest, DeleteManyDataRequest } from '@objectstack/spec/api';
6
4
  import { IDataEngine, DriverInterface, Logger, Plugin, PluginContext, ObjectKernel } from '@objectstack/core';
7
5
  import { IFeedService, IRealtimeService } from '@objectstack/spec/contracts';
8
6
 
9
- /**
10
- * XState-inspired State Machine Protocol
11
- * Used to define strict business logic constraints and lifecycle management.
12
- * Prevent AI "hallucinations" by enforcing valid valid transitions.
13
- */
14
- /**
15
- * References a named action (side effect)
16
- * Can be a script, a webhook, or a field update.
17
- */
18
- declare const ActionRefSchema: z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
19
- type: z.ZodString;
20
- params: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
21
- }, z.core.$strip>]>;
22
- /**
23
- * State Transition Definition
24
- * "When EVENT happens, if GUARD is true, go to TARGET and run ACTIONS"
25
- */
26
- declare const TransitionSchema: z.ZodObject<{
27
- target: z.ZodOptional<z.ZodString>;
28
- cond: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
29
- type: z.ZodString;
30
- params: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
31
- }, z.core.$strip>]>>;
32
- actions: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
33
- type: z.ZodString;
34
- params: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
35
- }, z.core.$strip>]>>>;
36
- description: z.ZodOptional<z.ZodString>;
37
- }, z.core.$strip>;
38
- type ActionRef = z.infer<typeof ActionRefSchema>;
39
- type Transition = z.infer<typeof TransitionSchema>;
40
- type StateNodeConfig = {
41
- type?: 'atomic' | 'compound' | 'parallel' | 'final' | 'history';
42
- entry?: ActionRef[];
43
- exit?: ActionRef[];
44
- on?: Record<string, string | Transition | Transition[]>;
45
- always?: Transition[];
46
- initial?: string;
47
- states?: Record<string, StateNodeConfig>;
48
- meta?: {
49
- label?: string;
50
- description?: string;
51
- color?: string;
52
- aiInstructions?: string;
53
- };
54
- };
55
-
56
7
  /**
57
8
  * Reserved namespaces that do not get FQN prefix applied.
58
- * Objects in these namespaces keep their short names (e.g., "user" not "base__user").
9
+ * Objects in these namespaces keep their short names (e.g., "user" short name IS the canonical key).
59
10
  */
60
11
  declare const RESERVED_NAMESPACES: Set<string>;
61
12
  /**
@@ -75,22 +26,25 @@ interface ObjectContributor {
75
26
  definition: ServiceObject;
76
27
  }
77
28
  /**
78
- * Compute Fully Qualified Name (FQN) for an object.
29
+ * Compute canonical registry key for an object.
79
30
  *
80
- * @param namespace - The package namespace (e.g., "crm", "todo")
81
- * @param shortName - The object's short name (e.g., "task", "account")
82
- * @returns FQN string (e.g., "crm__task") or just shortName for reserved namespaces
31
+ * Under the current naming convention, object names are canonical identifiers
32
+ * and are used as-is (no namespace__ prefix). The namespace parameter is
33
+ * retained for backward compatibility but no longer affects the returned key.
34
+ *
35
+ * @param namespace - The package namespace (unused, kept for API compatibility)
36
+ * @param shortName - The object's name (already the canonical identifier)
37
+ * @returns The object name unchanged
83
38
  *
84
39
  * @example
85
- * computeFQN('crm', 'account') // => 'crm__account'
86
- * computeFQN('base', 'user') // => 'user' (reserved, no prefix)
87
- * computeFQN(undefined, 'task') // => 'task' (legacy, no namespace)
40
+ * computeFQN('crm', 'account') // => 'account'
41
+ * computeFQN(undefined, 'task') // => 'task'
88
42
  */
89
- declare function computeFQN(namespace: string | undefined, shortName: string): string;
43
+ declare function computeFQN(_namespace: string | undefined, shortName: string): string;
90
44
  /**
91
45
  * Parse FQN back to namespace and short name.
92
46
  *
93
- * @param fqn - Fully qualified name (e.g., "crm__account" or "user")
47
+ * @param fqn - Object name (e.g., "account" or legacy "crm__account" for backward compat)
94
48
  * @returns { namespace, shortName } - namespace is undefined for unprefixed names
95
49
  */
96
50
  declare function parseFQN(fqn: string): {
@@ -105,7 +59,7 @@ declare function parseFQN(fqn: string): {
105
59
  *
106
60
  * Objects use a namespace-based FQN system:
107
61
  * - `namespace`: Short identifier from package manifest (e.g., "crm", "todo")
108
- * - `FQN`: `{namespace}__{short_name}` (e.g., "crm__account")
62
+ * - `name`: canonical object name (e.g., "account", "sys_user")
109
63
  * - Reserved namespaces (`base`, `system`) don't get prefixed
110
64
  *
111
65
  * Ownership modes:
@@ -120,37 +74,75 @@ declare function parseFQN(fqn: string): {
120
74
  * - A package may contain 0, 1, or many apps.
121
75
  */
122
76
  type RegistryLogLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent';
77
+ /**
78
+ * Construction options for {@link SchemaRegistry}.
79
+ */
80
+ interface SchemaRegistryOptions {
81
+ /**
82
+ * Whether the host kernel runs in multi-tenant mode. When `true` (default),
83
+ * the registry auto-injects `organization_id` (lookup → sys_organization)
84
+ * into every registered user object that doesn't already declare it and
85
+ * isn't `managedBy` an external subsystem or explicitly opted-out via
86
+ * `systemFields: false`.
87
+ *
88
+ * Sourced from the `OS_MULTI_TENANT` env var when not explicitly set —
89
+ * matches how the SecurityPlugin and CLI startup banner pick the mode.
90
+ * Pass an explicit boolean to override (useful in tests).
91
+ */
92
+ multiTenant?: boolean;
93
+ }
94
+ /**
95
+ * Augment a registered object with implicit system fields.
96
+ *
97
+ * Returns a *new* schema object when fields are added; returns the input
98
+ * unchanged when nothing applies (the cheap path for system tables).
99
+ *
100
+ * Author-declared fields always win — we splice the system fields at the
101
+ * front of the field map, so any same-named author field overwrites them
102
+ * via the natural `{ ...sys, ...authored }` merge.
103
+ *
104
+ * Currently injects:
105
+ * - `organization_id` — multi-tenant deployments. Required-false (the
106
+ * SecurityPlugin populates it on insert; nullable rows are still
107
+ * filtered out by the `tenant_isolation` RLS USING clause).
108
+ */
109
+ declare function applySystemFields(schema: ServiceObject, opts: {
110
+ multiTenant: boolean;
111
+ }): ServiceObject;
123
112
  declare class SchemaRegistry {
124
113
  /** Controls verbosity of registry console messages. Default: 'info'. */
125
- private static _logLevel;
126
- static get logLevel(): RegistryLogLevel;
127
- static set logLevel(level: RegistryLogLevel);
128
- private static log;
114
+ private _logLevel;
115
+ /** Whether to auto-inject multi-tenant system fields. */
116
+ private readonly multiTenant;
117
+ constructor(options?: SchemaRegistryOptions);
118
+ get logLevel(): RegistryLogLevel;
119
+ set logLevel(level: RegistryLogLevel);
120
+ private log;
129
121
  /** FQN → Contributor[] (all packages that own/extend this object) */
130
- private static objectContributors;
122
+ private objectContributors;
131
123
  /** FQN → Merged ServiceObject (cached, invalidated on changes) */
132
- private static mergedObjectCache;
124
+ private mergedObjectCache;
133
125
  /** Namespace → Set<PackageId> (multiple packages can share a namespace) */
134
- private static namespaceRegistry;
126
+ private namespaceRegistry;
135
127
  /** Type → Name/ID → MetadataItem */
136
- private static metadata;
128
+ private metadata;
137
129
  /**
138
130
  * Register a namespace for a package.
139
131
  * Multiple packages can share the same namespace (e.g. 'sys').
140
132
  */
141
- static registerNamespace(namespace: string, packageId: string): void;
133
+ registerNamespace(namespace: string, packageId: string): void;
142
134
  /**
143
135
  * Unregister a namespace when a package is uninstalled.
144
136
  */
145
- static unregisterNamespace(namespace: string, packageId: string): void;
137
+ unregisterNamespace(namespace: string, packageId: string): void;
146
138
  /**
147
139
  * Get the packages that use a namespace.
148
140
  */
149
- static getNamespaceOwner(namespace: string): string | undefined;
141
+ getNamespaceOwner(namespace: string): string | undefined;
150
142
  /**
151
143
  * Get all packages that share a namespace.
152
144
  */
153
- static getNamespaceOwners(namespace: string): string[];
145
+ getNamespaceOwners(namespace: string): string[];
154
146
  /**
155
147
  * Register an object with ownership semantics.
156
148
  *
@@ -162,1084 +154,116 @@ declare class SchemaRegistry {
162
154
  *
163
155
  * @throws Error if trying to 'own' an object that already has an owner
164
156
  */
165
- static registerObject(schema: ServiceObject, packageId: string, namespace?: string, ownership?: ObjectOwnership, priority?: number): string;
157
+ registerObject(schema: ServiceObject, packageId: string, namespace?: string, ownership?: ObjectOwnership, priority?: number): string;
166
158
  /**
167
159
  * Resolve an object by FQN, merging all contributions.
168
160
  * Returns the merged object or undefined if not found.
169
161
  */
170
- static resolveObject(fqn: string): ServiceObject | undefined;
162
+ resolveObject(fqn: string): ServiceObject | undefined;
171
163
  /**
172
- * Get object by name (FQN, short name, or physical table name).
164
+ * Get object by name (short name canonical, FQN supported for disambiguation).
165
+ *
166
+ * Short names are canonical for user code, AI generation, and most lookups.
167
+ * FQN is accepted as an explicit fallback for cross-package disambiguation
168
+ * when two packages contribute objects with the same short name.
173
169
  *
174
170
  * Resolution order:
175
- * 1. Exact FQN match (e.g., 'crm__account')
176
- * 2. Short name fallback (e.g., 'account' → 'crm__account')
177
- * 3. Physical table name match (e.g., 'sys_user' 'sys__user')
178
- * ObjectSchema.create() auto-derives tableName as {namespace}_{name},
179
- * which uses a single underscore — different from the FQN double underscore.
171
+ * 1. Exact name match — the name IS the canonical key.
172
+ * If multiple packages contribute the same short name, a warning is logged
173
+ * and the first match wins disambiguate by passing the FQN explicitly.
174
+ * 2. Legacy FQN match (e.g., 'crm__account') backward compat.
180
175
  */
181
- static getObject(name: string): ServiceObject | undefined;
176
+ getObject(name: string): ServiceObject | undefined;
182
177
  /**
183
178
  * Get all registered objects (merged).
184
179
  *
185
180
  * @param packageId - Optional filter: only objects contributed by this package
186
181
  */
187
- static getAllObjects(packageId?: string): ServiceObject[];
182
+ getAllObjects(packageId?: string): ServiceObject[];
188
183
  /**
189
184
  * Get all contributors for an object.
190
185
  */
191
- static getObjectContributors(fqn: string): ObjectContributor[];
186
+ getObjectContributors(fqn: string): ObjectContributor[];
192
187
  /**
193
188
  * Get the owner contributor for an object.
194
189
  */
195
- static getObjectOwner(fqn: string): ObjectContributor | undefined;
190
+ getObjectOwner(fqn: string): ObjectContributor | undefined;
196
191
  /**
197
192
  * Unregister all objects contributed by a package.
198
193
  *
199
194
  * @throws Error if trying to uninstall an owner that has extenders
200
195
  */
201
- static unregisterObjectsByPackage(packageId: string, force?: boolean): void;
196
+ unregisterObjectsByPackage(packageId: string, force?: boolean): void;
202
197
  /**
203
198
  * Universal Register Method for non-object metadata.
204
199
  */
205
- static registerItem<T>(type: string, item: T, keyField?: keyof T, packageId?: string): void;
200
+ registerItem<T>(type: string, item: T, keyField?: keyof T, packageId?: string): void;
206
201
  /**
207
202
  * Validate Metadata against Spec Zod Schemas
208
203
  */
209
- static validate(type: string, item: any): true | {
210
- name: string;
211
- active: boolean;
212
- isSystem: boolean;
213
- abstract: boolean;
214
- datasource: string;
215
- fields: Record<string, {
216
- type: "number" | "boolean" | "tags" | "date" | "lookup" | "file" | "url" | "json" | "text" | "textarea" | "email" | "phone" | "password" | "markdown" | "html" | "richtext" | "currency" | "percent" | "datetime" | "time" | "toggle" | "select" | "multiselect" | "radio" | "checkboxes" | "master_detail" | "tree" | "image" | "avatar" | "video" | "audio" | "formula" | "summary" | "autonumber" | "location" | "address" | "code" | "color" | "rating" | "slider" | "signature" | "qrcode" | "progress" | "vector";
217
- required: boolean;
218
- searchable: boolean;
219
- multiple: boolean;
220
- unique: boolean;
221
- deleteBehavior: "set_null" | "cascade" | "restrict";
222
- auditTrail: boolean;
223
- hidden: boolean;
224
- readonly: boolean;
225
- sortable: boolean;
226
- index: boolean;
227
- externalId: boolean;
228
- name?: string | undefined;
229
- label?: string | undefined;
230
- description?: string | undefined;
231
- format?: string | undefined;
232
- columnName?: string | undefined;
233
- defaultValue?: unknown;
234
- maxLength?: number | undefined;
235
- minLength?: number | undefined;
236
- precision?: number | undefined;
237
- scale?: number | undefined;
238
- min?: number | undefined;
239
- max?: number | undefined;
240
- options?: {
241
- label: string;
242
- value: string;
243
- color?: string | undefined;
244
- default?: boolean | undefined;
245
- }[] | undefined;
246
- reference?: string | undefined;
247
- referenceFilters?: string[] | undefined;
248
- writeRequiresMasterRead?: boolean | undefined;
249
- expression?: string | undefined;
250
- summaryOperations?: {
251
- object: string;
252
- field: string;
253
- function: "min" | "max" | "count" | "sum" | "avg";
254
- } | undefined;
255
- language?: string | undefined;
256
- theme?: string | undefined;
257
- lineNumbers?: boolean | undefined;
258
- maxRating?: number | undefined;
259
- allowHalf?: boolean | undefined;
260
- displayMap?: boolean | undefined;
261
- allowGeocoding?: boolean | undefined;
262
- addressFormat?: "us" | "uk" | "international" | undefined;
263
- colorFormat?: "hex" | "rgb" | "rgba" | "hsl" | undefined;
264
- allowAlpha?: boolean | undefined;
265
- presetColors?: string[] | undefined;
266
- step?: number | undefined;
267
- showValue?: boolean | undefined;
268
- marks?: Record<string, string> | undefined;
269
- barcodeFormat?: "qr" | "ean13" | "ean8" | "code128" | "code39" | "upca" | "upce" | undefined;
270
- qrErrorCorrection?: "L" | "M" | "Q" | "H" | undefined;
271
- displayValue?: boolean | undefined;
272
- allowScanning?: boolean | undefined;
273
- currencyConfig?: {
274
- precision: number;
275
- currencyMode: "dynamic" | "fixed";
276
- defaultCurrency: string;
277
- } | undefined;
278
- vectorConfig?: {
279
- dimensions: number;
280
- distanceMetric: "cosine" | "euclidean" | "dotProduct" | "manhattan";
281
- normalized: boolean;
282
- indexed: boolean;
283
- indexType?: "hnsw" | "ivfflat" | "flat" | undefined;
284
- } | undefined;
285
- fileAttachmentConfig?: {
286
- virusScan: boolean;
287
- virusScanOnUpload: boolean;
288
- quarantineOnThreat: boolean;
289
- allowMultiple: boolean;
290
- allowReplace: boolean;
291
- allowDelete: boolean;
292
- requireUpload: boolean;
293
- extractMetadata: boolean;
294
- extractText: boolean;
295
- versioningEnabled: boolean;
296
- publicRead: boolean;
297
- presignedUrlExpiry: number;
298
- minSize?: number | undefined;
299
- maxSize?: number | undefined;
300
- allowedTypes?: string[] | undefined;
301
- blockedTypes?: string[] | undefined;
302
- allowedMimeTypes?: string[] | undefined;
303
- blockedMimeTypes?: string[] | undefined;
304
- virusScanProvider?: "custom" | "clamav" | "virustotal" | "metadefender" | undefined;
305
- storageProvider?: string | undefined;
306
- storageBucket?: string | undefined;
307
- storagePrefix?: string | undefined;
308
- imageValidation?: {
309
- generateThumbnails: boolean;
310
- preserveMetadata: boolean;
311
- autoRotate: boolean;
312
- minWidth?: number | undefined;
313
- maxWidth?: number | undefined;
314
- minHeight?: number | undefined;
315
- maxHeight?: number | undefined;
316
- aspectRatio?: string | undefined;
317
- thumbnailSizes?: {
318
- name: string;
319
- width: number;
320
- height: number;
321
- crop: boolean;
322
- }[] | undefined;
323
- } | undefined;
324
- maxVersions?: number | undefined;
325
- } | undefined;
326
- encryptionConfig?: {
327
- enabled: boolean;
328
- algorithm: "aes-256-gcm" | "aes-256-cbc" | "chacha20-poly1305";
329
- keyManagement: {
330
- provider: "local" | "aws-kms" | "azure-key-vault" | "gcp-kms" | "hashicorp-vault";
331
- keyId?: string | undefined;
332
- rotationPolicy?: {
333
- enabled: boolean;
334
- frequencyDays: number;
335
- retainOldVersions: number;
336
- autoRotate: boolean;
337
- } | undefined;
338
- };
339
- scope: "field" | "table" | "record" | "database";
340
- deterministicEncryption: boolean;
341
- searchableEncryption: boolean;
342
- } | undefined;
343
- maskingRule?: {
344
- field: string;
345
- strategy: "redact" | "partial" | "hash" | "tokenize" | "randomize" | "nullify" | "substitute";
346
- preserveFormat: boolean;
347
- preserveLength: boolean;
348
- pattern?: string | undefined;
349
- roles?: string[] | undefined;
350
- exemptRoles?: string[] | undefined;
351
- } | undefined;
352
- dependencies?: string[] | undefined;
353
- cached?: {
354
- enabled: boolean;
355
- ttl: number;
356
- invalidateOn: string[];
357
- } | undefined;
358
- dataQuality?: {
359
- uniqueness: boolean;
360
- completeness: number;
361
- accuracy?: {
362
- source: string;
363
- threshold: number;
364
- } | undefined;
365
- } | undefined;
366
- group?: string | undefined;
367
- conditionalRequired?: string | undefined;
368
- inlineHelpText?: string | undefined;
369
- trackFeedHistory?: boolean | undefined;
370
- caseSensitive?: boolean | undefined;
371
- autonumberFormat?: string | undefined;
372
- }>;
373
- label?: string | undefined;
374
- pluralLabel?: string | undefined;
375
- description?: string | undefined;
376
- icon?: string | undefined;
377
- namespace?: string | undefined;
378
- tags?: string[] | undefined;
379
- tableName?: string | undefined;
380
- indexes?: {
381
- fields: string[];
382
- type: "hash" | "btree" | "gin" | "gist" | "fulltext";
383
- unique: boolean;
384
- name?: string | undefined;
385
- partial?: string | undefined;
386
- }[] | undefined;
387
- tenancy?: {
388
- enabled: boolean;
389
- strategy: "shared" | "isolated" | "hybrid";
390
- tenantField: string;
391
- crossTenantAccess: boolean;
392
- } | undefined;
393
- softDelete?: {
394
- enabled: boolean;
395
- field: string;
396
- cascadeDelete: boolean;
397
- } | undefined;
398
- versioning?: {
399
- enabled: boolean;
400
- strategy: "snapshot" | "delta" | "event-sourcing";
401
- versionField: string;
402
- retentionDays?: number | undefined;
403
- } | undefined;
404
- partitioning?: {
405
- enabled: boolean;
406
- strategy: "hash" | "range" | "list";
407
- key: string;
408
- interval?: string | undefined;
409
- } | undefined;
410
- cdc?: {
411
- enabled: boolean;
412
- events: ("update" | "delete" | "insert")[];
413
- destination: string;
414
- } | undefined;
415
- validations?: _objectstack_spec_data.BaseValidationRuleShape[] | undefined;
416
- stateMachines?: Record<string, {
417
- id: string;
418
- initial: string;
419
- states: Record<string, StateNodeConfig>;
420
- description?: string | undefined;
421
- contextSchema?: Record<string, unknown> | undefined;
422
- on?: Record<string, string | {
423
- target?: string | undefined;
424
- cond?: string | {
425
- type: string;
426
- params?: Record<string, unknown> | undefined;
427
- } | undefined;
428
- actions?: (string | {
429
- type: string;
430
- params?: Record<string, unknown> | undefined;
431
- })[] | undefined;
432
- description?: string | undefined;
433
- } | {
434
- target?: string | undefined;
435
- cond?: string | {
436
- type: string;
437
- params?: Record<string, unknown> | undefined;
438
- } | undefined;
439
- actions?: (string | {
440
- type: string;
441
- params?: Record<string, unknown> | undefined;
442
- })[] | undefined;
443
- description?: string | undefined;
444
- }[]> | undefined;
445
- }> | undefined;
446
- displayNameField?: string | undefined;
447
- recordName?: {
448
- type: "text" | "autonumber";
449
- displayFormat?: string | undefined;
450
- startNumber?: number | undefined;
451
- } | undefined;
452
- titleFormat?: string | undefined;
453
- compactLayout?: string[] | undefined;
454
- search?: {
455
- fields: string[];
456
- displayFields?: string[] | undefined;
457
- filters?: string[] | undefined;
458
- } | undefined;
459
- enable?: {
460
- trackHistory: boolean;
461
- searchable: boolean;
462
- apiEnabled: boolean;
463
- files: boolean;
464
- feeds: boolean;
465
- activities: boolean;
466
- trash: boolean;
467
- mru: boolean;
468
- clone: boolean;
469
- apiMethods?: ("search" | "list" | "update" | "delete" | "upsert" | "history" | "get" | "create" | "bulk" | "aggregate" | "restore" | "purge" | "import" | "export")[] | undefined;
470
- } | undefined;
471
- recordTypes?: string[] | undefined;
472
- sharingModel?: "full" | "private" | "read" | "read_write" | undefined;
473
- keyPrefix?: string | undefined;
474
- actions?: {
475
- name: string;
476
- label: string;
477
- type: "url" | "flow" | "api" | "script" | "modal";
478
- refreshAfter: boolean;
479
- objectName?: string | undefined;
480
- icon?: string | undefined;
481
- locations?: ("list_toolbar" | "list_item" | "record_header" | "record_more" | "record_related" | "global_nav")[] | undefined;
482
- component?: "action:button" | "action:icon" | "action:menu" | "action:group" | undefined;
483
- target?: string | undefined;
484
- execute?: string | undefined;
485
- params?: {
486
- name: string;
487
- label: string;
488
- type: "number" | "boolean" | "date" | "lookup" | "file" | "url" | "json" | "text" | "textarea" | "email" | "phone" | "password" | "markdown" | "html" | "richtext" | "currency" | "percent" | "datetime" | "time" | "toggle" | "select" | "multiselect" | "radio" | "checkboxes" | "master_detail" | "tree" | "image" | "avatar" | "video" | "audio" | "formula" | "summary" | "autonumber" | "location" | "address" | "code" | "color" | "rating" | "slider" | "signature" | "qrcode" | "progress" | "tags" | "vector";
489
- required: boolean;
490
- options?: {
491
- label: string;
492
- value: string;
493
- }[] | undefined;
494
- }[] | undefined;
495
- variant?: "link" | "primary" | "secondary" | "danger" | "ghost" | undefined;
496
- confirmText?: string | undefined;
497
- successMessage?: string | undefined;
498
- visible?: string | undefined;
499
- disabled?: string | boolean | undefined;
500
- shortcut?: string | undefined;
501
- bulkEnabled?: boolean | undefined;
502
- timeout?: number | undefined;
503
- aria?: {
504
- ariaLabel?: string | undefined;
505
- ariaDescribedBy?: string | undefined;
506
- role?: string | undefined;
507
- } | undefined;
508
- }[] | undefined;
509
- } | {
510
- name: string;
511
- label: string;
512
- active: boolean;
513
- isDefault: boolean;
514
- version?: string | undefined;
515
- description?: string | undefined;
516
- icon?: string | undefined;
517
- branding?: {
518
- primaryColor?: string | undefined;
519
- logo?: string | undefined;
520
- favicon?: string | undefined;
521
- } | undefined;
522
- navigation?: any[] | undefined;
523
- areas?: {
524
- id: string;
525
- label: string;
526
- navigation: any[];
527
- icon?: string | undefined;
528
- order?: number | undefined;
529
- description?: string | undefined;
530
- visible?: string | undefined;
531
- requiredPermissions?: string[] | undefined;
532
- }[] | undefined;
533
- homePageId?: string | undefined;
534
- requiredPermissions?: string[] | undefined;
535
- objects?: unknown[] | undefined;
536
- apis?: unknown[] | undefined;
537
- sharing?: {
538
- enabled: boolean;
539
- allowAnonymous: boolean;
540
- publicLink?: string | undefined;
541
- password?: string | undefined;
542
- allowedDomains?: string[] | undefined;
543
- expiresAt?: string | undefined;
544
- } | undefined;
545
- embed?: {
546
- enabled: boolean;
547
- width: string;
548
- height: string;
549
- showHeader: boolean;
550
- showNavigation: boolean;
551
- responsive: boolean;
552
- allowedOrigins?: string[] | undefined;
553
- } | undefined;
554
- mobileNavigation?: {
555
- mode: "drawer" | "bottom_nav" | "hamburger";
556
- bottomNavItems?: string[] | undefined;
557
- } | undefined;
558
- aria?: {
559
- ariaLabel?: string | undefined;
560
- ariaDescribedBy?: string | undefined;
561
- role?: string | undefined;
562
- } | undefined;
563
- } | {
564
- manifest: {
565
- id: string;
566
- version: string;
567
- type: "theme" | "app" | "agent" | "driver" | "server" | "ui" | "module" | "objectql" | "plugin" | "gateway" | "adapter";
568
- name: string;
569
- namespace?: string | undefined;
570
- description?: string | undefined;
571
- permissions?: string[] | undefined;
572
- objects?: string[] | undefined;
573
- datasources?: string[] | undefined;
574
- dependencies?: Record<string, string> | undefined;
575
- configuration?: {
576
- properties: Record<string, {
577
- type: "string" | "number" | "boolean" | "object" | "array";
578
- default?: unknown;
579
- description?: string | undefined;
580
- required?: boolean | undefined;
581
- secret?: boolean | undefined;
582
- enum?: string[] | undefined;
583
- }>;
584
- title?: string | undefined;
585
- } | undefined;
586
- contributes?: {
587
- kinds?: {
588
- id: string;
589
- globs: string[];
590
- description?: string | undefined;
591
- }[] | undefined;
592
- events?: string[] | undefined;
593
- menus?: Record<string, {
594
- id: string;
595
- label: string;
596
- command?: string | undefined;
597
- }[]> | undefined;
598
- themes?: {
599
- id: string;
600
- label: string;
601
- path: string;
602
- }[] | undefined;
603
- translations?: {
604
- locale: string;
605
- path: string;
606
- }[] | undefined;
607
- actions?: {
608
- name: string;
609
- label?: string | undefined;
610
- description?: string | undefined;
611
- input?: unknown;
612
- output?: unknown;
613
- }[] | undefined;
614
- drivers?: {
615
- id: string;
616
- label: string;
617
- description?: string | undefined;
618
- }[] | undefined;
619
- fieldTypes?: {
620
- name: string;
621
- label: string;
622
- description?: string | undefined;
623
- }[] | undefined;
624
- functions?: {
625
- name: string;
626
- description?: string | undefined;
627
- args?: string[] | undefined;
628
- returnType?: string | undefined;
629
- }[] | undefined;
630
- routes?: {
631
- prefix: string;
632
- service: string;
633
- methods?: string[] | undefined;
634
- }[] | undefined;
635
- commands?: {
636
- name: string;
637
- description?: string | undefined;
638
- module?: string | undefined;
639
- }[] | undefined;
640
- } | undefined;
641
- data?: {
642
- object: string;
643
- externalId: string;
644
- mode: "update" | "upsert" | "insert" | "replace" | "ignore";
645
- env: ("prod" | "dev" | "test")[];
646
- records: Record<string, unknown>[];
647
- }[] | undefined;
648
- capabilities?: {
649
- implements?: {
650
- protocol: {
651
- id: string;
652
- label: string;
653
- version: {
654
- major: number;
655
- minor: number;
656
- patch: number;
657
- };
658
- specification?: string | undefined;
659
- description?: string | undefined;
660
- };
661
- conformance: "partial" | "full" | "deprecated" | "experimental";
662
- certified: boolean;
663
- implementedFeatures?: string[] | undefined;
664
- features?: {
665
- name: string;
666
- enabled: boolean;
667
- description?: string | undefined;
668
- sinceVersion?: string | undefined;
669
- deprecatedSince?: string | undefined;
670
- }[] | undefined;
671
- metadata?: Record<string, unknown> | undefined;
672
- certificationDate?: string | undefined;
673
- }[] | undefined;
674
- provides?: {
675
- id: string;
676
- name: string;
677
- version: {
678
- major: number;
679
- minor: number;
680
- patch: number;
681
- };
682
- methods: {
683
- name: string;
684
- async: boolean;
685
- description?: string | undefined;
686
- parameters?: {
687
- name: string;
688
- type: string;
689
- required: boolean;
690
- description?: string | undefined;
691
- }[] | undefined;
692
- returnType?: string | undefined;
693
- }[];
694
- stability: "experimental" | "alpha" | "stable" | "beta";
695
- description?: string | undefined;
696
- events?: {
697
- name: string;
698
- description?: string | undefined;
699
- payload?: string | undefined;
700
- }[] | undefined;
701
- }[] | undefined;
702
- requires?: {
703
- pluginId: string;
704
- version: string;
705
- optional: boolean;
706
- reason?: string | undefined;
707
- requiredCapabilities?: string[] | undefined;
708
- }[] | undefined;
709
- extensionPoints?: {
710
- id: string;
711
- name: string;
712
- type: "provider" | "action" | "hook" | "widget" | "transformer" | "validator" | "decorator";
713
- cardinality: "multiple" | "single";
714
- description?: string | undefined;
715
- contract?: {
716
- input?: string | undefined;
717
- output?: string | undefined;
718
- signature?: string | undefined;
719
- } | undefined;
720
- }[] | undefined;
721
- extensions?: {
722
- targetPluginId: string;
723
- extensionPointId: string;
724
- implementation: string;
725
- priority: number;
726
- }[] | undefined;
727
- } | undefined;
728
- extensions?: Record<string, unknown> | undefined;
729
- loading?: {
730
- strategy: "lazy" | "parallel" | "eager" | "deferred" | "on-demand";
731
- preload?: {
732
- enabled: boolean;
733
- priority: number;
734
- resources?: ("dependencies" | "code" | "metadata" | "assets" | "services")[] | undefined;
735
- conditions?: {
736
- routes?: string[] | undefined;
737
- roles?: string[] | undefined;
738
- deviceType?: ("desktop" | "mobile" | "tablet")[] | undefined;
739
- minNetworkSpeed?: "slow-2g" | "2g" | "3g" | "4g" | undefined;
740
- } | undefined;
741
- } | undefined;
742
- codeSplitting?: {
743
- enabled: boolean;
744
- strategy: "custom" | "size" | "route" | "feature";
745
- chunkNaming: "hashed" | "sequential" | "named";
746
- maxChunkSize?: number | undefined;
747
- sharedDependencies?: {
748
- enabled: boolean;
749
- minChunks: number;
750
- } | undefined;
751
- } | undefined;
752
- dynamicImport?: {
753
- enabled: boolean;
754
- mode: "lazy" | "eager" | "async" | "sync";
755
- prefetch: boolean;
756
- preload: boolean;
757
- timeout: number;
758
- webpackChunkName?: string | undefined;
759
- retry?: {
760
- enabled: boolean;
761
- maxAttempts: number;
762
- backoffMs: number;
763
- } | undefined;
764
- } | undefined;
765
- initialization?: {
766
- mode: "parallel" | "sequential" | "async" | "sync";
767
- timeout: number;
768
- priority: number;
769
- critical: boolean;
770
- retry?: {
771
- enabled: boolean;
772
- maxAttempts: number;
773
- backoffMs: number;
774
- } | undefined;
775
- healthCheckInterval?: number | undefined;
776
- } | undefined;
777
- dependencyResolution?: {
778
- strategy: "strict" | "pinned" | "latest" | "compatible";
779
- conflictResolution: "latest" | "manual" | "fail" | "oldest";
780
- circularDependencies: "warn" | "error" | "allow";
781
- peerDependencies?: {
782
- resolve: boolean;
783
- onMissing: "warn" | "error" | "ignore";
784
- onMismatch: "warn" | "error" | "ignore";
785
- } | undefined;
786
- optionalDependencies?: {
787
- load: boolean;
788
- onFailure: "warn" | "ignore";
789
- } | undefined;
790
- } | undefined;
791
- hotReload?: {
792
- enabled: boolean;
793
- environment: "development" | "production" | "staging";
794
- strategy: "partial" | "full" | "state-preserve";
795
- debounceMs: number;
796
- preserveState: boolean;
797
- watchPatterns?: string[] | undefined;
798
- ignorePatterns?: string[] | undefined;
799
- stateSerialization?: {
800
- enabled: boolean;
801
- handler?: string | undefined;
802
- } | undefined;
803
- hooks?: {
804
- beforeReload?: string | undefined;
805
- afterReload?: string | undefined;
806
- onError?: string | undefined;
807
- } | undefined;
808
- productionSafety?: {
809
- healthValidation: boolean;
810
- rollbackOnFailure: boolean;
811
- healthTimeout: number;
812
- drainConnections: boolean;
813
- drainTimeout: number;
814
- maxConcurrentReloads: number;
815
- minReloadInterval: number;
816
- } | undefined;
817
- } | undefined;
818
- caching?: {
819
- enabled: boolean;
820
- storage: "hybrid" | "indexeddb" | "memory" | "disk";
821
- keyStrategy: "hash" | "version" | "timestamp";
822
- ttl?: number | undefined;
823
- maxSize?: number | undefined;
824
- invalidateOn?: ("error" | "manual" | "version-change" | "dependency-change")[] | undefined;
825
- compression?: {
826
- enabled: boolean;
827
- algorithm: "gzip" | "brotli" | "deflate";
828
- } | undefined;
829
- } | undefined;
830
- sandboxing?: {
831
- enabled: boolean;
832
- scope: "automation-only" | "untrusted-only" | "all-plugins";
833
- isolationLevel: "none" | "process" | "vm" | "iframe" | "web-worker";
834
- allowedCapabilities?: string[] | undefined;
835
- resourceQuotas?: {
836
- maxMemoryMB?: number | undefined;
837
- maxCpuTimeMs?: number | undefined;
838
- maxFileDescriptors?: number | undefined;
839
- maxNetworkKBps?: number | undefined;
840
- } | undefined;
841
- permissions?: {
842
- allowedAPIs?: string[] | undefined;
843
- allowedPaths?: string[] | undefined;
844
- allowedEndpoints?: string[] | undefined;
845
- allowedEnvVars?: string[] | undefined;
846
- } | undefined;
847
- ipc?: {
848
- enabled: boolean;
849
- transport: "memory" | "message-port" | "unix-socket" | "tcp";
850
- maxMessageSize: number;
851
- timeout: number;
852
- allowedServices?: string[] | undefined;
853
- } | undefined;
854
- } | undefined;
855
- monitoring?: {
856
- enabled: boolean;
857
- samplingRate: number;
858
- reportingInterval: number;
859
- onBudgetViolation: "warn" | "error" | "ignore";
860
- metrics?: ("load-time" | "init-time" | "memory-usage" | "cpu-usage" | "api-calls" | "error-rate" | "cache-hit-rate")[] | undefined;
861
- budgets?: {
862
- maxLoadTimeMs?: number | undefined;
863
- maxInitTimeMs?: number | undefined;
864
- maxMemoryMB?: number | undefined;
865
- } | undefined;
866
- } | undefined;
867
- } | undefined;
868
- engine?: {
869
- objectstack: string;
870
- } | undefined;
871
- };
872
- status: "disabled" | "error" | "installed" | "installing" | "upgrading" | "uninstalling";
873
- enabled: boolean;
874
- installedAt?: string | undefined;
875
- updatedAt?: string | undefined;
876
- installedVersion?: string | undefined;
877
- previousVersion?: string | undefined;
878
- statusChangedAt?: string | undefined;
879
- errorMessage?: string | undefined;
880
- settings?: Record<string, unknown> | undefined;
881
- upgradeHistory?: {
882
- fromVersion: string;
883
- toVersion: string;
884
- upgradedAt: string;
885
- status: "success" | "failed" | "rolled_back";
886
- migrationLog?: string[] | undefined;
887
- }[] | undefined;
888
- registeredNamespaces?: string[] | undefined;
889
- } | {
890
- id: string;
891
- version: string;
892
- type: "theme" | "app" | "agent" | "driver" | "server" | "ui" | "module" | "objectql" | "plugin" | "gateway" | "adapter";
893
- name: string;
894
- namespace?: string | undefined;
895
- description?: string | undefined;
896
- permissions?: string[] | undefined;
897
- objects?: string[] | undefined;
898
- datasources?: string[] | undefined;
899
- dependencies?: Record<string, string> | undefined;
900
- configuration?: {
901
- properties: Record<string, {
902
- type: "string" | "number" | "boolean" | "object" | "array";
903
- default?: unknown;
904
- description?: string | undefined;
905
- required?: boolean | undefined;
906
- secret?: boolean | undefined;
907
- enum?: string[] | undefined;
908
- }>;
909
- title?: string | undefined;
910
- } | undefined;
911
- contributes?: {
912
- kinds?: {
913
- id: string;
914
- globs: string[];
915
- description?: string | undefined;
916
- }[] | undefined;
917
- events?: string[] | undefined;
918
- menus?: Record<string, {
919
- id: string;
920
- label: string;
921
- command?: string | undefined;
922
- }[]> | undefined;
923
- themes?: {
924
- id: string;
925
- label: string;
926
- path: string;
927
- }[] | undefined;
928
- translations?: {
929
- locale: string;
930
- path: string;
931
- }[] | undefined;
932
- actions?: {
933
- name: string;
934
- label?: string | undefined;
935
- description?: string | undefined;
936
- input?: unknown;
937
- output?: unknown;
938
- }[] | undefined;
939
- drivers?: {
940
- id: string;
941
- label: string;
942
- description?: string | undefined;
943
- }[] | undefined;
944
- fieldTypes?: {
945
- name: string;
946
- label: string;
947
- description?: string | undefined;
948
- }[] | undefined;
949
- functions?: {
950
- name: string;
951
- description?: string | undefined;
952
- args?: string[] | undefined;
953
- returnType?: string | undefined;
954
- }[] | undefined;
955
- routes?: {
956
- prefix: string;
957
- service: string;
958
- methods?: string[] | undefined;
959
- }[] | undefined;
960
- commands?: {
961
- name: string;
962
- description?: string | undefined;
963
- module?: string | undefined;
964
- }[] | undefined;
965
- } | undefined;
966
- data?: {
967
- object: string;
968
- externalId: string;
969
- mode: "update" | "upsert" | "insert" | "replace" | "ignore";
970
- env: ("prod" | "dev" | "test")[];
971
- records: Record<string, unknown>[];
972
- }[] | undefined;
973
- capabilities?: {
974
- implements?: {
975
- protocol: {
976
- id: string;
977
- label: string;
978
- version: {
979
- major: number;
980
- minor: number;
981
- patch: number;
982
- };
983
- specification?: string | undefined;
984
- description?: string | undefined;
985
- };
986
- conformance: "partial" | "full" | "deprecated" | "experimental";
987
- certified: boolean;
988
- implementedFeatures?: string[] | undefined;
989
- features?: {
990
- name: string;
991
- enabled: boolean;
992
- description?: string | undefined;
993
- sinceVersion?: string | undefined;
994
- deprecatedSince?: string | undefined;
995
- }[] | undefined;
996
- metadata?: Record<string, unknown> | undefined;
997
- certificationDate?: string | undefined;
998
- }[] | undefined;
999
- provides?: {
1000
- id: string;
1001
- name: string;
1002
- version: {
1003
- major: number;
1004
- minor: number;
1005
- patch: number;
1006
- };
1007
- methods: {
1008
- name: string;
1009
- async: boolean;
1010
- description?: string | undefined;
1011
- parameters?: {
1012
- name: string;
1013
- type: string;
1014
- required: boolean;
1015
- description?: string | undefined;
1016
- }[] | undefined;
1017
- returnType?: string | undefined;
1018
- }[];
1019
- stability: "experimental" | "alpha" | "stable" | "beta";
1020
- description?: string | undefined;
1021
- events?: {
1022
- name: string;
1023
- description?: string | undefined;
1024
- payload?: string | undefined;
1025
- }[] | undefined;
1026
- }[] | undefined;
1027
- requires?: {
1028
- pluginId: string;
1029
- version: string;
1030
- optional: boolean;
1031
- reason?: string | undefined;
1032
- requiredCapabilities?: string[] | undefined;
1033
- }[] | undefined;
1034
- extensionPoints?: {
1035
- id: string;
1036
- name: string;
1037
- type: "provider" | "action" | "hook" | "widget" | "transformer" | "validator" | "decorator";
1038
- cardinality: "multiple" | "single";
1039
- description?: string | undefined;
1040
- contract?: {
1041
- input?: string | undefined;
1042
- output?: string | undefined;
1043
- signature?: string | undefined;
1044
- } | undefined;
1045
- }[] | undefined;
1046
- extensions?: {
1047
- targetPluginId: string;
1048
- extensionPointId: string;
1049
- implementation: string;
1050
- priority: number;
1051
- }[] | undefined;
1052
- } | undefined;
1053
- extensions?: Record<string, unknown> | undefined;
1054
- loading?: {
1055
- strategy: "lazy" | "parallel" | "eager" | "deferred" | "on-demand";
1056
- preload?: {
1057
- enabled: boolean;
1058
- priority: number;
1059
- resources?: ("dependencies" | "code" | "metadata" | "assets" | "services")[] | undefined;
1060
- conditions?: {
1061
- routes?: string[] | undefined;
1062
- roles?: string[] | undefined;
1063
- deviceType?: ("desktop" | "mobile" | "tablet")[] | undefined;
1064
- minNetworkSpeed?: "slow-2g" | "2g" | "3g" | "4g" | undefined;
1065
- } | undefined;
1066
- } | undefined;
1067
- codeSplitting?: {
1068
- enabled: boolean;
1069
- strategy: "custom" | "size" | "route" | "feature";
1070
- chunkNaming: "hashed" | "sequential" | "named";
1071
- maxChunkSize?: number | undefined;
1072
- sharedDependencies?: {
1073
- enabled: boolean;
1074
- minChunks: number;
1075
- } | undefined;
1076
- } | undefined;
1077
- dynamicImport?: {
1078
- enabled: boolean;
1079
- mode: "lazy" | "eager" | "async" | "sync";
1080
- prefetch: boolean;
1081
- preload: boolean;
1082
- timeout: number;
1083
- webpackChunkName?: string | undefined;
1084
- retry?: {
1085
- enabled: boolean;
1086
- maxAttempts: number;
1087
- backoffMs: number;
1088
- } | undefined;
1089
- } | undefined;
1090
- initialization?: {
1091
- mode: "parallel" | "sequential" | "async" | "sync";
1092
- timeout: number;
1093
- priority: number;
1094
- critical: boolean;
1095
- retry?: {
1096
- enabled: boolean;
1097
- maxAttempts: number;
1098
- backoffMs: number;
1099
- } | undefined;
1100
- healthCheckInterval?: number | undefined;
1101
- } | undefined;
1102
- dependencyResolution?: {
1103
- strategy: "strict" | "pinned" | "latest" | "compatible";
1104
- conflictResolution: "latest" | "manual" | "fail" | "oldest";
1105
- circularDependencies: "warn" | "error" | "allow";
1106
- peerDependencies?: {
1107
- resolve: boolean;
1108
- onMissing: "warn" | "error" | "ignore";
1109
- onMismatch: "warn" | "error" | "ignore";
1110
- } | undefined;
1111
- optionalDependencies?: {
1112
- load: boolean;
1113
- onFailure: "warn" | "ignore";
1114
- } | undefined;
1115
- } | undefined;
1116
- hotReload?: {
1117
- enabled: boolean;
1118
- environment: "development" | "production" | "staging";
1119
- strategy: "partial" | "full" | "state-preserve";
1120
- debounceMs: number;
1121
- preserveState: boolean;
1122
- watchPatterns?: string[] | undefined;
1123
- ignorePatterns?: string[] | undefined;
1124
- stateSerialization?: {
1125
- enabled: boolean;
1126
- handler?: string | undefined;
1127
- } | undefined;
1128
- hooks?: {
1129
- beforeReload?: string | undefined;
1130
- afterReload?: string | undefined;
1131
- onError?: string | undefined;
1132
- } | undefined;
1133
- productionSafety?: {
1134
- healthValidation: boolean;
1135
- rollbackOnFailure: boolean;
1136
- healthTimeout: number;
1137
- drainConnections: boolean;
1138
- drainTimeout: number;
1139
- maxConcurrentReloads: number;
1140
- minReloadInterval: number;
1141
- } | undefined;
1142
- } | undefined;
1143
- caching?: {
1144
- enabled: boolean;
1145
- storage: "hybrid" | "indexeddb" | "memory" | "disk";
1146
- keyStrategy: "hash" | "version" | "timestamp";
1147
- ttl?: number | undefined;
1148
- maxSize?: number | undefined;
1149
- invalidateOn?: ("error" | "manual" | "version-change" | "dependency-change")[] | undefined;
1150
- compression?: {
1151
- enabled: boolean;
1152
- algorithm: "gzip" | "brotli" | "deflate";
1153
- } | undefined;
1154
- } | undefined;
1155
- sandboxing?: {
1156
- enabled: boolean;
1157
- scope: "automation-only" | "untrusted-only" | "all-plugins";
1158
- isolationLevel: "none" | "process" | "vm" | "iframe" | "web-worker";
1159
- allowedCapabilities?: string[] | undefined;
1160
- resourceQuotas?: {
1161
- maxMemoryMB?: number | undefined;
1162
- maxCpuTimeMs?: number | undefined;
1163
- maxFileDescriptors?: number | undefined;
1164
- maxNetworkKBps?: number | undefined;
1165
- } | undefined;
1166
- permissions?: {
1167
- allowedAPIs?: string[] | undefined;
1168
- allowedPaths?: string[] | undefined;
1169
- allowedEndpoints?: string[] | undefined;
1170
- allowedEnvVars?: string[] | undefined;
1171
- } | undefined;
1172
- ipc?: {
1173
- enabled: boolean;
1174
- transport: "memory" | "message-port" | "unix-socket" | "tcp";
1175
- maxMessageSize: number;
1176
- timeout: number;
1177
- allowedServices?: string[] | undefined;
1178
- } | undefined;
1179
- } | undefined;
1180
- monitoring?: {
1181
- enabled: boolean;
1182
- samplingRate: number;
1183
- reportingInterval: number;
1184
- onBudgetViolation: "warn" | "error" | "ignore";
1185
- metrics?: ("load-time" | "init-time" | "memory-usage" | "cpu-usage" | "api-calls" | "error-rate" | "cache-hit-rate")[] | undefined;
1186
- budgets?: {
1187
- maxLoadTimeMs?: number | undefined;
1188
- maxInitTimeMs?: number | undefined;
1189
- maxMemoryMB?: number | undefined;
1190
- } | undefined;
1191
- } | undefined;
1192
- } | undefined;
1193
- engine?: {
1194
- objectstack: string;
1195
- } | undefined;
1196
- };
204
+ validate(type: string, item: any): unknown;
1197
205
  /**
1198
206
  * Universal Unregister Method
1199
207
  */
1200
- static unregisterItem(type: string, name: string): void;
208
+ unregisterItem(type: string, name: string): void;
1201
209
  /**
1202
210
  * Universal Get Method
1203
211
  */
1204
- static getItem<T>(type: string, name: string): T | undefined;
212
+ getItem<T>(type: string, name: string): T | undefined;
1205
213
  /**
1206
214
  * Universal List Method
1207
215
  */
1208
- static listItems<T>(type: string, packageId?: string): T[];
216
+ listItems<T>(type: string, packageId?: string): T[];
1209
217
  /**
1210
218
  * Get all registered metadata types (Kinds)
1211
219
  */
1212
- static getRegisteredTypes(): string[];
1213
- static installPackage(manifest: ObjectStackManifest, settings?: Record<string, any>): InstalledPackage;
1214
- static uninstallPackage(id: string): boolean;
1215
- static getPackage(id: string): InstalledPackage | undefined;
1216
- static getAllPackages(): InstalledPackage[];
1217
- static enablePackage(id: string): InstalledPackage | undefined;
1218
- static disablePackage(id: string): InstalledPackage | undefined;
1219
- static registerApp(app: any, packageId?: string): void;
1220
- static getApp(name: string): any;
1221
- static getAllApps(): any[];
1222
- static registerPlugin(manifest: ObjectStackManifest): void;
1223
- static getAllPlugins(): ObjectStackManifest[];
1224
- static registerKind(kind: {
220
+ getRegisteredTypes(): string[];
221
+ installPackage(manifest: ObjectStackManifest, settings?: Record<string, any>): InstalledPackage;
222
+ uninstallPackage(id: string): boolean;
223
+ getPackage(id: string): InstalledPackage | undefined;
224
+ getAllPackages(): InstalledPackage[];
225
+ enablePackage(id: string): InstalledPackage | undefined;
226
+ disablePackage(id: string): InstalledPackage | undefined;
227
+ registerApp(app: any, packageId?: string): void;
228
+ getApp(name: string): any;
229
+ getAllApps(): any[];
230
+ registerPlugin(manifest: ObjectStackManifest): void;
231
+ getAllPlugins(): ObjectStackManifest[];
232
+ registerKind(kind: {
1225
233
  id: string;
1226
234
  globs: string[];
1227
235
  }): void;
1228
- static getAllKinds(): {
236
+ getAllKinds(): {
1229
237
  id: string;
1230
238
  globs: string[];
1231
239
  }[];
1232
240
  /**
1233
241
  * Clear all registry state. Use only for testing.
1234
242
  */
1235
- static reset(): void;
243
+ reset(): void;
1236
244
  }
1237
245
 
1238
246
  declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
1239
247
  private engine;
1240
248
  private getServicesRegistry?;
1241
249
  private getFeedService?;
1242
- constructor(engine: IDataEngine, getServicesRegistry?: () => Map<string, any>, getFeedService?: () => IFeedService | undefined);
250
+ /**
251
+ * Project scope applied to sys_metadata reads/writes. When undefined
252
+ * (single-kernel deployments), rows land in / come from the
253
+ * platform-global bucket (`project_id IS NULL`). When set, every
254
+ * saveMetaItem insert/update and loadMetaFromDb query is filtered by
255
+ * `project_id = projectId`, so per-project kernels see only their own
256
+ * metadata even if several projects share the same physical database.
257
+ */
258
+ private projectId?;
259
+ constructor(engine: IDataEngine, getServicesRegistry?: () => Map<string, any>, getFeedService?: () => IFeedService | undefined, projectId?: string);
260
+ /**
261
+ * Exposes the project scope the protocol is bound to. Consumers like
262
+ * the HTTP dispatcher use this to decide whether to trust the process-
263
+ * wide SchemaRegistry or whether they must route a read through the
264
+ * protocol's project_id-filtered lookup.
265
+ */
266
+ getProjectId(): string | undefined;
1243
267
  private requireFeedService;
1244
268
  getDiscovery(): Promise<{
1245
269
  version: string;
@@ -1264,7 +288,7 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
1264
288
  };
1265
289
  services: Record<string, {
1266
290
  enabled: boolean;
1267
- status: "degraded" | "stub" | "available" | "registered" | "unavailable";
291
+ status: "available" | "registered" | "unavailable" | "degraded" | "stub";
1268
292
  handlerReady?: boolean | undefined;
1269
293
  route?: string | undefined;
1270
294
  provider?: string | undefined;
@@ -1333,7 +357,7 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
1333
357
  label: string | undefined;
1334
358
  required: boolean;
1335
359
  readonly: boolean;
1336
- type: "number" | "boolean" | "tags" | "date" | "lookup" | "file" | "url" | "json" | "text" | "textarea" | "email" | "phone" | "password" | "markdown" | "html" | "richtext" | "currency" | "percent" | "datetime" | "time" | "toggle" | "select" | "multiselect" | "radio" | "checkboxes" | "master_detail" | "tree" | "image" | "avatar" | "video" | "audio" | "formula" | "summary" | "autonumber" | "location" | "address" | "code" | "color" | "rating" | "slider" | "signature" | "qrcode" | "progress" | "vector";
360
+ type: "number" | "boolean" | "tags" | "date" | "file" | "code" | "datetime" | "signature" | "progress" | "url" | "text" | "textarea" | "email" | "phone" | "password" | "markdown" | "html" | "richtext" | "currency" | "percent" | "time" | "toggle" | "select" | "multiselect" | "radio" | "checkboxes" | "lookup" | "master_detail" | "tree" | "image" | "avatar" | "video" | "audio" | "formula" | "summary" | "autonumber" | "location" | "address" | "json" | "color" | "rating" | "slider" | "qrcode" | "vector";
1337
361
  colSpan: number;
1338
362
  }[];
1339
363
  }[];
@@ -1343,6 +367,7 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
1343
367
  findData(request: {
1344
368
  object: string;
1345
369
  query?: any;
370
+ context?: any;
1346
371
  }): Promise<{
1347
372
  object: string;
1348
373
  records: any[];
@@ -1354,6 +379,7 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
1354
379
  id: string;
1355
380
  expand?: string | string[];
1356
381
  select?: string | string[];
382
+ context?: any;
1357
383
  }): Promise<{
1358
384
  object: string;
1359
385
  id: string;
@@ -1362,6 +388,7 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
1362
388
  createData(request: {
1363
389
  object: string;
1364
390
  data: any;
391
+ context?: any;
1365
392
  }): Promise<{
1366
393
  object: string;
1367
394
  id: any;
@@ -1371,6 +398,7 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
1371
398
  object: string;
1372
399
  id: string;
1373
400
  data: any;
401
+ context?: any;
1374
402
  }): Promise<{
1375
403
  object: string;
1376
404
  id: string;
@@ -1379,6 +407,7 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
1379
407
  deleteData(request: {
1380
408
  object: string;
1381
409
  id: string;
410
+ context?: any;
1382
411
  }): Promise<{
1383
412
  object: string;
1384
413
  id: string;
@@ -1450,6 +479,14 @@ interface HookEntry {
1450
479
  object?: string | string[];
1451
480
  priority: number;
1452
481
  packageId?: string;
482
+ /**
483
+ * Original metadata-form `Hook` definition this entry was bound from
484
+ * (when registered via `bindHooksToEngine`). Pure code-paths that call
485
+ * `engine.registerHook` directly leave this undefined.
486
+ */
487
+ meta?: any;
488
+ /** Hook `name` from metadata; used for diagnostics & deduplication. */
489
+ hookName?: string;
1453
490
  }
1454
491
  /**
1455
492
  * Operation Context for Middleware Chain
@@ -1487,11 +524,15 @@ declare class ObjectQL implements IDataEngine {
1487
524
  private drivers;
1488
525
  private defaultDriver;
1489
526
  private logger;
527
+ private datasourceMapping;
528
+ private manifests;
1490
529
  private hooks;
1491
530
  private middlewares;
1492
531
  private actions;
532
+ private functions;
1493
533
  private hostContext;
1494
534
  private realtimeService?;
535
+ private _registry;
1495
536
  constructor(hostContext?: Record<string, any>);
1496
537
  /**
1497
538
  * Service Status Report
@@ -1504,9 +545,12 @@ declare class ObjectQL implements IDataEngine {
1504
545
  features: string[];
1505
546
  };
1506
547
  /**
1507
- * Expose the SchemaRegistry for plugins to register metadata
548
+ * Expose the SchemaRegistry for plugins to register metadata.
549
+ *
550
+ * Returns the per-engine instance, NOT the class. Each ObjectQL engine
551
+ * owns its registry so multi-project kernels remain isolated.
1508
552
  */
1509
- get registry(): typeof SchemaRegistry;
553
+ get registry(): SchemaRegistry;
1510
554
  /**
1511
555
  * Load and Register a Plugin
1512
556
  */
@@ -1521,7 +565,67 @@ declare class ObjectQL implements IDataEngine {
1521
565
  object?: string | string[];
1522
566
  priority?: number;
1523
567
  packageId?: string;
568
+ /** Original metadata Hook definition (set by `bindHooksToEngine`). */
569
+ meta?: any;
570
+ /** Stable name from metadata (set by `bindHooksToEngine`). */
571
+ hookName?: string;
572
+ }): void;
573
+ /**
574
+ * Remove all hooks registered under a given `packageId`. Used by
575
+ * `bindHooksToEngine` to make re-binding (hot reload, app reinstall)
576
+ * idempotent, and by app uninstall flows.
577
+ */
578
+ unregisterHooksByPackage(packageId: string): number;
579
+ /**
580
+ * Register a named function handler that can later be referenced by
581
+ * string from a `Hook.handler` field. This is the JSON-safe form of
582
+ * handler binding — declarative metadata persisted to disk or shipped
583
+ * over the wire only carries the name.
584
+ */
585
+ registerFunction(name: string, handler: HookHandler, packageId?: string): void;
586
+ /** Look up a registered function by name. */
587
+ resolveFunction(name: string): HookHandler | undefined;
588
+ /** Remove all functions registered under a given `packageId`. */
589
+ unregisterFunctionsByPackage(packageId: string): number;
590
+ /**
591
+ * Bind a list of declarative `Hook` metadata definitions to this engine.
592
+ *
593
+ * Convenience proxy to the canonical `bindHooksToEngine` so callers do
594
+ * not need a separate import. Use `import { bindHooksToEngine } from
595
+ * '@objectstack/objectql'` directly when you want the result object.
596
+ */
597
+ bindHooks(hooks: any[] | undefined, opts?: {
598
+ packageId?: string;
599
+ functions?: Record<string, HookHandler>;
600
+ bodyRunner?: any;
601
+ strict?: boolean;
602
+ warnLegacyHandler?: boolean;
603
+ metrics?: any;
1524
604
  }): void;
605
+ /**
606
+ * Install a default body-runner used when `bindHooks` is called without
607
+ * an explicit one. The runtime layer sets this once on each per-project
608
+ * engine so every binding path (template seed, metadata sync, AppPlugin)
609
+ * can execute hook `body.source` consistently.
610
+ */
611
+ setDefaultBodyRunner(runner: any): void;
612
+ /**
613
+ * Toggle strict hook-binding mode for this engine. When enabled, every
614
+ * subsequent `bindHooks` call rejects on the first unresolved hook
615
+ * instead of silently warning. Production runtimes should enable this.
616
+ */
617
+ setStrictHookBinding(strict: boolean): void;
618
+ /** Toggle deprecation warnings for hooks still using legacy `handler` ref. */
619
+ setWarnLegacyHandler(warn: boolean): void;
620
+ /**
621
+ * Install a metrics recorder used by every subsequent `bindHooks` call.
622
+ * The recorder's methods are invoked per-execution to count outcomes
623
+ * (success / error / timeout / capability_rejected), skips, and retries.
624
+ * Defaults to no-op so the engine pays zero cost when nobody is observing.
625
+ */
626
+ setHookMetricsRecorder(recorder: any): void;
627
+ /** Read the engine's installed metrics recorder, if any. */
628
+ getHookMetricsRecorder(): any;
1525
629
  triggerHooks(event: string, context: HookContext): Promise<void>;
1526
630
  /**
1527
631
  * Register a named action on an object.
@@ -1558,6 +662,26 @@ declare class ObjectQL implements IDataEngine {
1558
662
  * Build a HookContext.session from ExecutionContext
1559
663
  */
1560
664
  private buildSession;
665
+ /**
666
+ * Build a HookContext.api: a ScopedContext that hooks can use to
667
+ * read/write other objects within the same execution context.
668
+ * Falls back to a system-elevated empty context when no execCtx
669
+ * is supplied (e.g. system-triggered hooks).
670
+ */
671
+ private buildHookApi;
672
+ /**
673
+ * Apply field defaults to an incoming insert payload. Defaults that are
674
+ * Expression envelopes (e.g. `{ dialect: 'cel', source: 'today()' }`,
675
+ * `{ dialect: 'cel', source: 'os.user.id' }`) are evaluated via
676
+ * `ExpressionEngine` against the calling user/org/now snapshot. Static
677
+ * defaults are applied verbatim. Records that already supplied a value for a
678
+ * field are left untouched.
679
+ *
680
+ * Implements ROADMAP §M9.9b — `defaultValue` accepts Expression so authors
681
+ * can replace "write a hook to default to today/current-user" with a
682
+ * declarative `defaultValue: cel\`today()\``.
683
+ */
684
+ private applyFieldDefaults;
1561
685
  /**
1562
686
  * Register contribution (Manifest)
1563
687
  *
@@ -1569,6 +693,12 @@ declare class ObjectQL implements IDataEngine {
1569
693
  * the manifest contains UI navigation definitions (AppSchema).
1570
694
  */
1571
695
  registerApp(manifest: any): void;
696
+ /**
697
+ * Deep-clone an app definition, resolving objectName references in navigation
698
+ * items via the registry. Object names are canonical identifiers — no FQN
699
+ * expansion is applied.
700
+ */
701
+ private resolveNavObjectNames;
1572
702
  /**
1573
703
  * Register a nested plugin's metadata (objects, actions, views, etc.)
1574
704
  *
@@ -1597,20 +727,47 @@ declare class ObjectQL implements IDataEngine {
1597
727
  */
1598
728
  getSchema(objectName: string): ServiceObject | undefined;
1599
729
  /**
1600
- * Resolve an object name to its Fully Qualified Name (FQN).
1601
- *
1602
- * Short names like 'task' are resolved to FQN like 'todo__task'
1603
- * via SchemaRegistry lookup. If no match is found, the name is
1604
- * returned as-is (for ad-hoc / unregistered objects).
730
+ * Resolve any object identifier to the physical storage name used by drivers.
1605
731
  *
1606
- * This ensures that all driver operations use a consistent key
1607
- * regardless of whether the caller uses the short name or FQN.
732
+ * Accepts the canonical short name (e.g., 'account') or, for explicit
733
+ * cross-package disambiguation, the canonical object name (e.g., 'account'). The result is
734
+ * the physical table name derived via `StorageNameMapping.resolveTableName`.
1608
735
  */
1609
736
  private resolveObjectName;
1610
737
  /**
1611
738
  * Helper to get the target driver
739
+ *
740
+ * Resolution priority (first match wins):
741
+ * 1. Object's explicit `datasource` field (if not 'default')
742
+ * 2. DatasourceMapping rules (namespace/package/pattern matching)
743
+ * 3. Package's `defaultDatasource` from manifest
744
+ * 4. Global default driver
1612
745
  */
1613
746
  private getDriver;
747
+ /**
748
+ * Resolve datasource from mapping rules
749
+ *
750
+ * Rules are evaluated in order (or by priority if specified).
751
+ * First matching rule wins.
752
+ */
753
+ private resolveDatasourceFromMapping;
754
+ /**
755
+ * Simple glob pattern matching
756
+ * Supports * (any chars) and ? (single char)
757
+ */
758
+ private matchPattern;
759
+ /**
760
+ * Set datasource mapping rules
761
+ * Called by ObjectQLPlugin during bootstrap
762
+ */
763
+ setDatasourceMapping(rules: Array<{
764
+ namespace?: string;
765
+ package?: string;
766
+ objectPattern?: string;
767
+ default?: boolean;
768
+ datasource: string;
769
+ priority?: number;
770
+ }>): void;
1614
771
  /**
1615
772
  * Initialize the engine and all registered drivers
1616
773
  */
@@ -1678,6 +835,13 @@ declare class ObjectQL implements IDataEngine {
1678
835
  * @returns The resolved DriverInterface, or undefined if no driver is available.
1679
836
  */
1680
837
  getDriverForObject(objectName: string): DriverInterface | undefined;
838
+ /**
839
+ * Sync all registered object schemas to their respective drivers.
840
+ * Call this after dynamically registering new objects at runtime
841
+ * (e.g. after template seeding) to ensure tables/collections exist
842
+ * before inserting seed data.
843
+ */
844
+ syncSchemas(): Promise<void>;
1681
845
  /**
1682
846
  * Get a registered driver by datasource name.
1683
847
  * Alias matching @objectql/core datasource() API.
@@ -1800,6 +964,198 @@ declare class ScopedContext {
1800
964
  get transactionHandle(): unknown;
1801
965
  }
1802
966
 
967
+ /**
968
+ * Hook Execution Metrics
969
+ *
970
+ * Lightweight, transport-agnostic recorder interface for per-hook execution
971
+ * counters and latencies. The default implementation is a no-op so the
972
+ * engine pays zero cost when nobody is observing.
973
+ *
974
+ * Wire a real recorder by calling `engine.setHookMetricsRecorder(recorder)`.
975
+ * The runtime / kernel can adapt this to Otel, Prometheus, StatsD, or
976
+ * whatever telemetry pipeline ships with the deployment.
977
+ *
978
+ * Recorded events:
979
+ * - `recordExecution(label, outcome, durationMs)`
980
+ * outcome ∈ 'success' | 'error' | 'timeout' | 'capability_rejected'
981
+ * - `recordSkip(label, reason)`
982
+ * reason ∈ 'condition' | 'fire_and_forget'
983
+ * - `recordRetry(label, attempt)`
984
+ */
985
+ type HookMetricOutcome = 'success' | 'error' | 'timeout' | 'capability_rejected';
986
+ type HookSkipReason = 'condition' | 'fire_and_forget';
987
+ interface HookMetricLabel {
988
+ /** Hook name (stable id from metadata). */
989
+ hook: string;
990
+ /** Object name the hook is bound to. May be undefined for global hooks. */
991
+ object?: string;
992
+ /** Lifecycle event (`beforeInsert`, `afterUpdate`, etc.). */
993
+ event?: string;
994
+ /** True when the handler comes from a metadata `body` (sandboxed JS). */
995
+ body?: boolean;
996
+ }
997
+ interface HookMetricsRecorder {
998
+ recordExecution(label: HookMetricLabel, outcome: HookMetricOutcome, durationMs: number): void;
999
+ recordSkip(label: HookMetricLabel, reason: HookSkipReason): void;
1000
+ recordRetry(label: HookMetricLabel, attempt: number): void;
1001
+ }
1002
+ declare const noopHookMetricsRecorder: HookMetricsRecorder;
1003
+ /**
1004
+ * In-memory recorder useful for tests, dev-mode dashboards, and as a
1005
+ * starting point for adapter implementations. Aggregates counts + a
1006
+ * rolling sum of latency per (hook, outcome).
1007
+ */
1008
+ declare class InMemoryHookMetricsRecorder implements HookMetricsRecorder {
1009
+ private executions;
1010
+ private skips;
1011
+ private retries;
1012
+ recordExecution(label: HookMetricLabel, outcome: HookMetricOutcome, durationMs: number): void;
1013
+ recordSkip(label: HookMetricLabel, reason: HookSkipReason): void;
1014
+ recordRetry(label: HookMetricLabel, _attempt: number): void;
1015
+ snapshot(): {
1016
+ executions: Array<{
1017
+ hook: string;
1018
+ outcome: HookMetricOutcome;
1019
+ count: number;
1020
+ totalMs: number;
1021
+ }>;
1022
+ skips: Array<{
1023
+ hook: string;
1024
+ reason: HookSkipReason;
1025
+ count: number;
1026
+ }>;
1027
+ retries: Array<{
1028
+ hook: string;
1029
+ count: number;
1030
+ }>;
1031
+ };
1032
+ reset(): void;
1033
+ }
1034
+
1035
+ /**
1036
+ * Hook Binder
1037
+ *
1038
+ * Single, canonical entry point that turns declarative `Hook` metadata into
1039
+ * runtime registrations on the `ObjectQL` engine. Every metadata source —
1040
+ * `defineStack({ hooks })` (consumed by `AppPlugin`), the per-project
1041
+ * template seeder (`MultiProjectPlugin`), and the metadata service
1042
+ * (`ObjectQLPlugin.loadMetadataFromService`) — funnels through here so
1043
+ * that:
1044
+ *
1045
+ * - Inline function handlers and string-named handlers share one resolver.
1046
+ * - Declarative fields (`condition`, `async`, `retryPolicy`, `timeout`,
1047
+ * `onError`) are honoured uniformly via `wrapDeclarativeHook`.
1048
+ * - Hooks can be unregistered as a unit via `packageId`, enabling clean
1049
+ * hot-reload and app uninstall.
1050
+ *
1051
+ * The ObjectQL engine itself stays simple — it knows how to store and
1052
+ * trigger handlers, but knows nothing about declarative semantics. All
1053
+ * metadata-aware behaviour lives in this binder + the wrapper module.
1054
+ */
1055
+
1056
+ interface BindHooksOptions {
1057
+ /** Owning package / app id — used for `unregisterHooksByPackage`. */
1058
+ packageId?: string;
1059
+ /**
1060
+ * Optional name → function map for resolving string `handler` references.
1061
+ * Typically supplied by `defineStack({ functions })` and merged with any
1062
+ * functions previously registered on the engine.
1063
+ */
1064
+ functions?: Record<string, HookHandler>;
1065
+ /**
1066
+ * Optional factory that converts a metadata-only `Hook.body` (L1 expression
1067
+ * or L2 sandboxed JS source) into an executable `HookHandler`. The runtime
1068
+ * package wires this up using `QuickJSScriptRunner`; objectql itself stays
1069
+ * sandbox-free so it can run in lightweight environments.
1070
+ *
1071
+ * If `hook.body` is set and this factory is missing, the hook is skipped
1072
+ * with a clear error.
1073
+ */
1074
+ bodyRunner?: (hook: Hook) => HookHandler | undefined;
1075
+ /**
1076
+ * When true, treat unresolved hooks (body present but no runner, or handler
1077
+ * string with no implementation) as fatal errors instead of warnings. Used
1078
+ * by production runtimes to fail fast on misconfiguration. Defaults false.
1079
+ */
1080
+ strict?: boolean;
1081
+ /**
1082
+ * When true, emit a deprecation warning for every hook that still relies
1083
+ * on a `handler` ref string instead of the metadata-only `body`. Used by
1084
+ * the CLI (compile time) and runtime (boot time) to nudge users away from
1085
+ * the legacy `.mjs` runtime bundle path. Defaults false.
1086
+ */
1087
+ warnLegacyHandler?: boolean;
1088
+ /** Per-hook execution metrics sink. Defaults to no-op. */
1089
+ metrics?: HookMetricsRecorder;
1090
+ /** Logger; defaults to a silent no-op. */
1091
+ logger?: {
1092
+ debug: (msg: string, meta?: any) => void;
1093
+ info: (msg: string, meta?: any) => void;
1094
+ warn: (msg: string, meta?: any) => void;
1095
+ error: (msg: string, meta?: any) => void;
1096
+ };
1097
+ }
1098
+ /** Counter for stats. */
1099
+ interface BindHooksResult {
1100
+ registered: number;
1101
+ skipped: number;
1102
+ errors: Array<{
1103
+ hook: string;
1104
+ reason: string;
1105
+ }>;
1106
+ }
1107
+ /**
1108
+ * Bind a list of declarative `Hook` definitions to a running ObjectQL engine.
1109
+ *
1110
+ * Idempotent on `(packageId, hook.name, event, object)`: re-binding the
1111
+ * same set after a hot reload first calls `unregisterHooksByPackage`
1112
+ * (when `packageId` is provided).
1113
+ */
1114
+ declare function bindHooksToEngine(engine: ObjectQL, hooks: Hook[] | undefined, opts?: BindHooksOptions): BindHooksResult;
1115
+
1116
+ /**
1117
+ * Declarative Hook Wrappers
1118
+ *
1119
+ * Turns a raw `HookHandler` into one that honours the declarative metadata
1120
+ * fields defined on `HookSchema` (`condition`, `async`, `retryPolicy`,
1121
+ * `timeout`, `onError`). This lives outside the engine's `triggerHooks`
1122
+ * loop so the engine stays minimal and the semantics are unit-testable in
1123
+ * isolation.
1124
+ *
1125
+ * The resulting wrapped handler keeps the original `(ctx) => Promise<void>`
1126
+ * signature, so `engine.registerHook` does not need to know anything about
1127
+ * the metadata-driven behaviours.
1128
+ */
1129
+
1130
+ interface WrapDeclarativeOptions {
1131
+ /** Logger for declarative-layer diagnostics (timeouts, retries, swallowed errors). */
1132
+ logger?: {
1133
+ debug: (msg: string, meta?: any) => void;
1134
+ info: (msg: string, meta?: any) => void;
1135
+ warn: (msg: string, meta?: any) => void;
1136
+ error: (msg: string, meta?: any) => void;
1137
+ };
1138
+ /** Optional per-execution metrics sink. Defaults to no-op. */
1139
+ metrics?: HookMetricsRecorder;
1140
+ }
1141
+ /**
1142
+ * Wrap a hook handler so it honours the declarative fields defined on
1143
+ * `HookSchema`. The wrapping order, from outermost to innermost, is:
1144
+ *
1145
+ * 1. condition → skip when formula evaluates falsy
1146
+ * 2. async → fire-and-forget (after* events only)
1147
+ * 3. retry → repeat on throw with backoff
1148
+ * 4. timeout → abort if handler runs too long
1149
+ * 5. onError → swallow when set to 'log'
1150
+ *
1151
+ * The condition formula is evaluated against the most useful record-shaped
1152
+ * payload available on the context (write payloads first, then `previous`,
1153
+ * then a flat merge of input). Read events typically have no record yet,
1154
+ * so a condition on a `beforeFind` will simply skip when no data is
1155
+ * present.
1156
+ */
1157
+ declare function wrapDeclarativeHook(meta: Hook, handler: HookHandler, opts?: WrapDeclarativeOptions): HookHandler;
1158
+
1803
1159
  /**
1804
1160
  * MetadataFacade
1805
1161
  *
@@ -1809,8 +1165,14 @@ declare class ScopedContext {
1809
1165
  *
1810
1166
  * Implements the async IMetadataService interface.
1811
1167
  * Internally delegates to SchemaRegistry (in-memory) with Promise wrappers.
1168
+ *
1169
+ * Each facade is bound to a specific SchemaRegistry instance — passed in the
1170
+ * constructor — so that multi-kernel servers can give every kernel its own
1171
+ * metadata surface without leaking state across tenants.
1812
1172
  */
1813
1173
  declare class MetadataFacade {
1174
+ private registry;
1175
+ constructor(registry: SchemaRegistry);
1814
1176
  /**
1815
1177
  * Register a metadata item
1816
1178
  */
@@ -1853,25 +1215,52 @@ declare class MetadataFacade {
1853
1215
  listObjects(): Promise<any[]>;
1854
1216
  }
1855
1217
 
1218
+ /**
1219
+ * Options for ObjectQLPlugin.
1220
+ *
1221
+ * `projectId` scopes all metadata writes + reads to a specific project.
1222
+ * When set, `protocol.saveMetaItem` stamps `project_id = <projectId>` on
1223
+ * new sys_metadata rows, and `protocol.loadMetaFromDb` filters by the same
1224
+ * column. Leave undefined in single-kernel / self-hosted mode — rows land
1225
+ * in the platform-global scope (project_id IS NULL).
1226
+ */
1227
+ interface ObjectQLPluginOptions {
1228
+ /** Optional pre-built engine. When absent, one is lazily created in init. */
1229
+ ql?: ObjectQL;
1230
+ /** Passed to `new ObjectQL(...)` when `ql` is not supplied. */
1231
+ hostContext?: Record<string, any>;
1232
+ /** Scope sys_metadata reads/writes to this project. */
1233
+ projectId?: string;
1234
+ }
1856
1235
  declare class ObjectQLPlugin implements Plugin {
1857
1236
  name: string;
1858
1237
  type: string;
1859
1238
  version: string;
1860
1239
  private ql;
1861
1240
  private hostContext?;
1862
- constructor(ql?: ObjectQL, hostContext?: Record<string, any>);
1241
+ private projectId?;
1242
+ constructor(qlOrOptions?: ObjectQL | ObjectQLPluginOptions, hostContext?: Record<string, any>);
1863
1243
  init: (ctx: PluginContext) => Promise<void>;
1864
1244
  start: (ctx: PluginContext) => Promise<void>;
1865
1245
  /**
1866
1246
  * Register built-in audit hooks for auto-stamping created_by/updated_by
1867
- * and fetching previousData for update/delete operations.
1247
+ * and fetching previousData for update/delete operations. These are
1248
+ * declared as canonical `Hook` metadata and bound through the same
1249
+ * `bindHooksToEngine` path used by `defineStack({ hooks })`, so the
1250
+ * engine's built-ins flow through the same rails as user code
1251
+ * (dogfooding the protocol).
1868
1252
  */
1869
1253
  private registerAuditHooks;
1870
1254
  /**
1871
- * Register tenant isolation middleware that auto-injects tenant_id filter
1872
- * for multi-tenant operations.
1255
+ * Tenant isolation moved to `@objectstack/plugin-security`'s
1256
+ * `member_default` permission set RLS
1257
+ * (`organization_id = current_user.organization_id`, with
1258
+ * field-existence guards). The legacy `registerTenantMiddleware`
1259
+ * method was removed because it (a) collided with SecurityPlugin's
1260
+ * RLS pipeline and (b) blindly filtered tables that don't have a
1261
+ * `tenant_id` column (e.g. `sys_organization`), returning 0 rows
1262
+ * instead of all rows.
1873
1263
  */
1874
- private registerTenantMiddleware;
1875
1264
  /**
1876
1265
  * Synchronize all registered object schemas to the database.
1877
1266
  *
@@ -2033,4 +1422,4 @@ declare function convertIntrospectedSchemaToObjects(introspectedSchema: Introspe
2033
1422
  skipSystemColumns?: boolean;
2034
1423
  }): ServiceObject[];
2035
1424
 
2036
- export { DEFAULT_EXTENDER_PRIORITY, DEFAULT_OWNER_PRIORITY, type EngineMiddleware, type HookEntry, type HookHandler, type IntrospectedColumn, type IntrospectedForeignKey, type IntrospectedSchema, type IntrospectedTable, MetadataFacade, type ObjectContributor, ObjectQL, type ObjectQLHostContext, type ObjectQLKernelOptions, ObjectQLPlugin, ObjectRepository, ObjectStackProtocolImplementation, type OperationContext, RESERVED_NAMESPACES, SchemaRegistry, ScopedContext, computeFQN, convertIntrospectedSchemaToObjects, createObjectQLKernel, parseFQN, toTitleCase };
1425
+ export { type BindHooksOptions, type BindHooksResult, DEFAULT_EXTENDER_PRIORITY, DEFAULT_OWNER_PRIORITY, type EngineMiddleware, type HookEntry, type HookHandler, type HookMetricLabel, type HookMetricOutcome, type HookMetricsRecorder, type HookSkipReason, InMemoryHookMetricsRecorder, type IntrospectedColumn, type IntrospectedForeignKey, type IntrospectedSchema, type IntrospectedTable, MetadataFacade, type ObjectContributor, ObjectQL, type ObjectQLHostContext, type ObjectQLKernelOptions, ObjectQLPlugin, ObjectRepository, ObjectStackProtocolImplementation, type OperationContext, RESERVED_NAMESPACES, SchemaRegistry, type SchemaRegistryOptions, ScopedContext, type WrapDeclarativeOptions, applySystemFields, bindHooksToEngine, computeFQN, convertIntrospectedSchemaToObjects, createObjectQLKernel, noopHookMetricsRecorder, parseFQN, toTitleCase, wrapDeclarativeHook };