@objectstack/objectql 7.9.0 → 8.0.1

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.mts CHANGED
@@ -5,6 +5,7 @@ import { MetadataRepository, MetaRef, MetadataItem, PutOptions, PutResult, Delet
5
5
  import { ObjectStackProtocol, MetadataCacheRequest, MetadataCacheResponse, BatchUpdateRequest, BatchUpdateResponse, UpdateManyDataRequest, DeleteManyDataRequest, InstallPackageRequest, InstallPackageResponse } from '@objectstack/spec/api';
6
6
  import { IDataEngine, DriverInterface, Logger, Plugin, PluginContext, ObjectKernel } from '@objectstack/core';
7
7
  import { IFeedService, IRealtimeService, ICryptoProvider } from '@objectstack/spec/contracts';
8
+ import { Expression } from '@objectstack/spec';
8
9
 
9
10
  /**
10
11
  * Reserved namespaces that do not get FQN prefix applied.
@@ -525,7 +526,18 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
525
526
  } | undefined;
526
527
  shortcut?: string | undefined;
527
528
  bulkEnabled?: boolean | undefined;
528
- aiExposed?: boolean | undefined;
529
+ ai?: {
530
+ exposed: boolean;
531
+ description?: string | undefined;
532
+ category?: "data" | "flow" | "analytics" | "action" | "integration" | "vector_search" | "utility" | undefined;
533
+ paramHints?: Record<string, {
534
+ description?: string | undefined;
535
+ enum?: (string | number)[] | undefined;
536
+ examples?: unknown[] | undefined;
537
+ }> | undefined;
538
+ outputSchema?: Record<string, unknown> | undefined;
539
+ requiresConfirmation?: boolean | undefined;
540
+ } | undefined;
529
541
  recordIdParam?: string | undefined;
530
542
  recordIdField?: string | undefined;
531
543
  bodyShape?: "flat" | {
@@ -627,6 +639,17 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
627
639
  } | undefined;
628
640
  } | undefined;
629
641
  }[] | undefined;
642
+ subforms?: {
643
+ childObject: string;
644
+ relationshipField?: string | undefined;
645
+ columns?: any[] | undefined;
646
+ amountField?: string | undefined;
647
+ totalField?: string | undefined;
648
+ title?: string | undefined;
649
+ addLabel?: string | undefined;
650
+ minRows?: number | undefined;
651
+ maxRows?: number | undefined;
652
+ }[] | undefined;
630
653
  defaultSort?: {
631
654
  field: string;
632
655
  order: "asc" | "desc";
@@ -665,7 +688,7 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
665
688
  supportsVersioning: boolean;
666
689
  executionPinned: boolean;
667
690
  loadOrder: number;
668
- domain: "system" | "data" | "ui" | "automation" | "ai" | "security";
691
+ domain: "system" | "data" | "ai" | "ui" | "automation" | "security";
669
692
  description?: string | undefined;
670
693
  }[];
671
694
  }>;
@@ -1779,15 +1802,16 @@ interface ObjectQLHostContext {
1779
1802
  logger: Logger;
1780
1803
  [key: string]: any;
1781
1804
  }
1782
- /**
1783
- * ObjectQL Engine
1784
- *
1785
- * Implements the IDataEngine interface for data persistence.
1786
- * Acts as the reference implementation for:
1787
- * - CoreServiceName.data (CRUD)
1788
- * - CoreServiceName.metadata (Schema Registry)
1789
- */
1790
1805
  declare class ObjectQL implements IDataEngine {
1806
+ /**
1807
+ * Ambient transaction store (ADR-0034). While a `transaction()` callback
1808
+ * runs, the active transaction handle lives here so that EVERY data
1809
+ * operation — including internal reads done during a write (reference
1810
+ * checks, hooks, expand) — automatically binds to the same connection
1811
+ * instead of asking the pool for another one and deadlocking on the
1812
+ * single-connection SQLite pool.
1813
+ */
1814
+ private readonly txStore;
1791
1815
  private drivers;
1792
1816
  private defaultDriver;
1793
1817
  private logger;
@@ -1965,6 +1989,26 @@ declare class ObjectQL implements IDataEngine {
1965
1989
  * declarative `defaultValue: cel\`today()\``.
1966
1990
  */
1967
1991
  private applyFieldDefaults;
1992
+ /**
1993
+ * Generate values for empty `autonumber` fields on insert — ONLY for drivers
1994
+ * that do not generate them natively (memory, mongodb). For SQL-backed objects
1995
+ * the driver owns a persistent, atomic `_objectstack_sequences` table and
1996
+ * advertises `supports.autonumber === true`; the engine then defers entirely
1997
+ * and never pre-fills (so the persistent sequence is the single source of
1998
+ * truth — see #1603). Required-validation exempts `autonumber` either way, so
1999
+ * a `required` record number is never rejected for "missing" — the runtime
2000
+ * owns the value, not the client.
2001
+ *
2002
+ * In the fallback path the next value is `max(existing) + 1`, seeded once per
2003
+ * `object.field` from the store then incremented in memory (monotonic within
2004
+ * the process, resilient to deletions). `autonumberFormat` is honored, e.g.
2005
+ * `CASE-{0000}` → `CASE-0042`. NOTE: this in-memory seeding is single-instance.
2006
+ */
2007
+ private applyAutonumbers;
2008
+ /** Seed the autonumber counter from the current max numeric value in store. */
2009
+ private seedAutonumber;
2010
+ /** Apply an autonumber format like `CASE-{0000}`; default to the bare number. */
2011
+ private formatAutonumber;
1968
2012
  /**
1969
2013
  * Register contribution (Manifest)
1970
2014
  *
@@ -2131,6 +2175,27 @@ declare class ObjectQL implements IDataEngine {
2131
2175
  destroy(): Promise<void>;
2132
2176
  /** Maximum depth for recursive expand to prevent infinite loops */
2133
2177
  private static readonly MAX_EXPAND_DEPTH;
2178
+ private static readonly MAX_CASCADE_DEPTH;
2179
+ /** In-memory next-value cache per `object.field` for autonumber generation,
2180
+ * lazily seeded from the current max in the store. */
2181
+ private readonly autonumberCounters;
2182
+ /** Lazily-built index: child object name → roll-up summary descriptors on
2183
+ * parent objects that aggregate it. Invalidated when packages register. */
2184
+ private summaryIndex;
2185
+ /** Invalidate the cached roll-up summary index (call when metadata changes). */
2186
+ private invalidateSummaryIndex;
2187
+ /** Scan all registered objects for `summary` fields and index them by the
2188
+ * child object they aggregate, resolving the child→parent FK field. */
2189
+ private buildSummaryIndex;
2190
+ private getSummaryDescriptors;
2191
+ /**
2192
+ * Recompute roll-up `summary` fields on parent records after a child write.
2193
+ * For each affected parent (the FK value on the changed/old child record), it
2194
+ * aggregates the child collection and writes the result onto the parent's
2195
+ * summary field. Runs in the caller's execution context so it joins the same
2196
+ * transaction (e.g. the cross-object batch) when one is open.
2197
+ */
2198
+ private recomputeSummaries;
2134
2199
  /**
2135
2200
  * Post-process expand: resolve lookup/master_detail fields by batch-loading related records.
2136
2201
  *
@@ -2148,6 +2213,19 @@ declare class ObjectQL implements IDataEngine {
2148
2213
  findOne(objectName: string, query?: EngineQueryOptions): Promise<any>;
2149
2214
  insert(object: string, data: any | any[], options?: DataEngineInsertOptions): Promise<any>;
2150
2215
  update(object: string, data: any, options?: EngineUpdateOptions): Promise<any>;
2216
+ /**
2217
+ * Apply referential delete behavior for relations pointing AT this record,
2218
+ * before it is removed. For every registered object with a `master_detail`
2219
+ * or `lookup` field referencing `object`, honor the field's `deleteBehavior`:
2220
+ * - `cascade` → delete the dependent rows (recursively, so grandchildren
2221
+ * are handled by each child's own delete),
2222
+ * - `set_null` → clear the foreign key,
2223
+ * - `restrict` → refuse the delete when dependents exist.
2224
+ * `master_detail` defaults to `cascade` (the parent owns the child
2225
+ * lifecycle); `lookup` defaults to `set_null`. Only runs for single-id
2226
+ * deletes — multi/predicate deletes skip cascade (logged).
2227
+ */
2228
+ private cascadeDeleteRelations;
2151
2229
  delete(object: string, options?: EngineDeleteOptions): Promise<any>;
2152
2230
  count(object: string, query?: EngineCountOptions): Promise<number>;
2153
2231
  aggregate(object: string, query: EngineAggregateOptions): Promise<any[]>;
@@ -2643,7 +2721,15 @@ interface EvaluateRulesOptions {
2643
2721
  */
2644
2722
  declare function needsPriorRecord(objectSchema: {
2645
2723
  validations?: unknown[];
2724
+ fields?: Record<string, ConditionalFieldDef>;
2646
2725
  } | undefined | null): boolean;
2726
+ /** Field-level conditional rules (B2): a field is required / read-only when its
2727
+ * CEL predicate is TRUE over the record. */
2728
+ interface ConditionalFieldDef {
2729
+ requiredWhen?: string | Expression;
2730
+ conditionalRequired?: string | Expression;
2731
+ readonlyWhen?: string | Expression;
2732
+ }
2647
2733
  /**
2648
2734
  * Evaluate an object's declared validation rules against an incoming write.
2649
2735
  *
@@ -2653,6 +2739,7 @@ declare function needsPriorRecord(objectSchema: {
2653
2739
  */
2654
2740
  declare function evaluateValidationRules(objectSchema: {
2655
2741
  validations?: unknown[];
2742
+ fields?: Record<string, ConditionalFieldDef>;
2656
2743
  } | undefined | null, data: Record<string, unknown> | undefined | null, mode: Mode, opts?: EvaluateRulesOptions): void;
2657
2744
  /**
2658
2745
  * Introspection helper (ADR-0020 D3.3): given an object's schema, a state
package/dist/index.d.ts CHANGED
@@ -5,6 +5,7 @@ import { MetadataRepository, MetaRef, MetadataItem, PutOptions, PutResult, Delet
5
5
  import { ObjectStackProtocol, MetadataCacheRequest, MetadataCacheResponse, BatchUpdateRequest, BatchUpdateResponse, UpdateManyDataRequest, DeleteManyDataRequest, InstallPackageRequest, InstallPackageResponse } from '@objectstack/spec/api';
6
6
  import { IDataEngine, DriverInterface, Logger, Plugin, PluginContext, ObjectKernel } from '@objectstack/core';
7
7
  import { IFeedService, IRealtimeService, ICryptoProvider } from '@objectstack/spec/contracts';
8
+ import { Expression } from '@objectstack/spec';
8
9
 
9
10
  /**
10
11
  * Reserved namespaces that do not get FQN prefix applied.
@@ -525,7 +526,18 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
525
526
  } | undefined;
526
527
  shortcut?: string | undefined;
527
528
  bulkEnabled?: boolean | undefined;
528
- aiExposed?: boolean | undefined;
529
+ ai?: {
530
+ exposed: boolean;
531
+ description?: string | undefined;
532
+ category?: "data" | "flow" | "analytics" | "action" | "integration" | "vector_search" | "utility" | undefined;
533
+ paramHints?: Record<string, {
534
+ description?: string | undefined;
535
+ enum?: (string | number)[] | undefined;
536
+ examples?: unknown[] | undefined;
537
+ }> | undefined;
538
+ outputSchema?: Record<string, unknown> | undefined;
539
+ requiresConfirmation?: boolean | undefined;
540
+ } | undefined;
529
541
  recordIdParam?: string | undefined;
530
542
  recordIdField?: string | undefined;
531
543
  bodyShape?: "flat" | {
@@ -627,6 +639,17 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
627
639
  } | undefined;
628
640
  } | undefined;
629
641
  }[] | undefined;
642
+ subforms?: {
643
+ childObject: string;
644
+ relationshipField?: string | undefined;
645
+ columns?: any[] | undefined;
646
+ amountField?: string | undefined;
647
+ totalField?: string | undefined;
648
+ title?: string | undefined;
649
+ addLabel?: string | undefined;
650
+ minRows?: number | undefined;
651
+ maxRows?: number | undefined;
652
+ }[] | undefined;
630
653
  defaultSort?: {
631
654
  field: string;
632
655
  order: "asc" | "desc";
@@ -665,7 +688,7 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
665
688
  supportsVersioning: boolean;
666
689
  executionPinned: boolean;
667
690
  loadOrder: number;
668
- domain: "system" | "data" | "ui" | "automation" | "ai" | "security";
691
+ domain: "system" | "data" | "ai" | "ui" | "automation" | "security";
669
692
  description?: string | undefined;
670
693
  }[];
671
694
  }>;
@@ -1779,15 +1802,16 @@ interface ObjectQLHostContext {
1779
1802
  logger: Logger;
1780
1803
  [key: string]: any;
1781
1804
  }
1782
- /**
1783
- * ObjectQL Engine
1784
- *
1785
- * Implements the IDataEngine interface for data persistence.
1786
- * Acts as the reference implementation for:
1787
- * - CoreServiceName.data (CRUD)
1788
- * - CoreServiceName.metadata (Schema Registry)
1789
- */
1790
1805
  declare class ObjectQL implements IDataEngine {
1806
+ /**
1807
+ * Ambient transaction store (ADR-0034). While a `transaction()` callback
1808
+ * runs, the active transaction handle lives here so that EVERY data
1809
+ * operation — including internal reads done during a write (reference
1810
+ * checks, hooks, expand) — automatically binds to the same connection
1811
+ * instead of asking the pool for another one and deadlocking on the
1812
+ * single-connection SQLite pool.
1813
+ */
1814
+ private readonly txStore;
1791
1815
  private drivers;
1792
1816
  private defaultDriver;
1793
1817
  private logger;
@@ -1965,6 +1989,26 @@ declare class ObjectQL implements IDataEngine {
1965
1989
  * declarative `defaultValue: cel\`today()\``.
1966
1990
  */
1967
1991
  private applyFieldDefaults;
1992
+ /**
1993
+ * Generate values for empty `autonumber` fields on insert — ONLY for drivers
1994
+ * that do not generate them natively (memory, mongodb). For SQL-backed objects
1995
+ * the driver owns a persistent, atomic `_objectstack_sequences` table and
1996
+ * advertises `supports.autonumber === true`; the engine then defers entirely
1997
+ * and never pre-fills (so the persistent sequence is the single source of
1998
+ * truth — see #1603). Required-validation exempts `autonumber` either way, so
1999
+ * a `required` record number is never rejected for "missing" — the runtime
2000
+ * owns the value, not the client.
2001
+ *
2002
+ * In the fallback path the next value is `max(existing) + 1`, seeded once per
2003
+ * `object.field` from the store then incremented in memory (monotonic within
2004
+ * the process, resilient to deletions). `autonumberFormat` is honored, e.g.
2005
+ * `CASE-{0000}` → `CASE-0042`. NOTE: this in-memory seeding is single-instance.
2006
+ */
2007
+ private applyAutonumbers;
2008
+ /** Seed the autonumber counter from the current max numeric value in store. */
2009
+ private seedAutonumber;
2010
+ /** Apply an autonumber format like `CASE-{0000}`; default to the bare number. */
2011
+ private formatAutonumber;
1968
2012
  /**
1969
2013
  * Register contribution (Manifest)
1970
2014
  *
@@ -2131,6 +2175,27 @@ declare class ObjectQL implements IDataEngine {
2131
2175
  destroy(): Promise<void>;
2132
2176
  /** Maximum depth for recursive expand to prevent infinite loops */
2133
2177
  private static readonly MAX_EXPAND_DEPTH;
2178
+ private static readonly MAX_CASCADE_DEPTH;
2179
+ /** In-memory next-value cache per `object.field` for autonumber generation,
2180
+ * lazily seeded from the current max in the store. */
2181
+ private readonly autonumberCounters;
2182
+ /** Lazily-built index: child object name → roll-up summary descriptors on
2183
+ * parent objects that aggregate it. Invalidated when packages register. */
2184
+ private summaryIndex;
2185
+ /** Invalidate the cached roll-up summary index (call when metadata changes). */
2186
+ private invalidateSummaryIndex;
2187
+ /** Scan all registered objects for `summary` fields and index them by the
2188
+ * child object they aggregate, resolving the child→parent FK field. */
2189
+ private buildSummaryIndex;
2190
+ private getSummaryDescriptors;
2191
+ /**
2192
+ * Recompute roll-up `summary` fields on parent records after a child write.
2193
+ * For each affected parent (the FK value on the changed/old child record), it
2194
+ * aggregates the child collection and writes the result onto the parent's
2195
+ * summary field. Runs in the caller's execution context so it joins the same
2196
+ * transaction (e.g. the cross-object batch) when one is open.
2197
+ */
2198
+ private recomputeSummaries;
2134
2199
  /**
2135
2200
  * Post-process expand: resolve lookup/master_detail fields by batch-loading related records.
2136
2201
  *
@@ -2148,6 +2213,19 @@ declare class ObjectQL implements IDataEngine {
2148
2213
  findOne(objectName: string, query?: EngineQueryOptions): Promise<any>;
2149
2214
  insert(object: string, data: any | any[], options?: DataEngineInsertOptions): Promise<any>;
2150
2215
  update(object: string, data: any, options?: EngineUpdateOptions): Promise<any>;
2216
+ /**
2217
+ * Apply referential delete behavior for relations pointing AT this record,
2218
+ * before it is removed. For every registered object with a `master_detail`
2219
+ * or `lookup` field referencing `object`, honor the field's `deleteBehavior`:
2220
+ * - `cascade` → delete the dependent rows (recursively, so grandchildren
2221
+ * are handled by each child's own delete),
2222
+ * - `set_null` → clear the foreign key,
2223
+ * - `restrict` → refuse the delete when dependents exist.
2224
+ * `master_detail` defaults to `cascade` (the parent owns the child
2225
+ * lifecycle); `lookup` defaults to `set_null`. Only runs for single-id
2226
+ * deletes — multi/predicate deletes skip cascade (logged).
2227
+ */
2228
+ private cascadeDeleteRelations;
2151
2229
  delete(object: string, options?: EngineDeleteOptions): Promise<any>;
2152
2230
  count(object: string, query?: EngineCountOptions): Promise<number>;
2153
2231
  aggregate(object: string, query: EngineAggregateOptions): Promise<any[]>;
@@ -2643,7 +2721,15 @@ interface EvaluateRulesOptions {
2643
2721
  */
2644
2722
  declare function needsPriorRecord(objectSchema: {
2645
2723
  validations?: unknown[];
2724
+ fields?: Record<string, ConditionalFieldDef>;
2646
2725
  } | undefined | null): boolean;
2726
+ /** Field-level conditional rules (B2): a field is required / read-only when its
2727
+ * CEL predicate is TRUE over the record. */
2728
+ interface ConditionalFieldDef {
2729
+ requiredWhen?: string | Expression;
2730
+ conditionalRequired?: string | Expression;
2731
+ readonlyWhen?: string | Expression;
2732
+ }
2647
2733
  /**
2648
2734
  * Evaluate an object's declared validation rules against an incoming write.
2649
2735
  *
@@ -2653,6 +2739,7 @@ declare function needsPriorRecord(objectSchema: {
2653
2739
  */
2654
2740
  declare function evaluateValidationRules(objectSchema: {
2655
2741
  validations?: unknown[];
2742
+ fields?: Record<string, ConditionalFieldDef>;
2656
2743
  } | undefined | null, data: Record<string, unknown> | undefined | null, mode: Mode, opts?: EvaluateRulesOptions): void;
2657
2744
  /**
2658
2745
  * Introspection helper (ADR-0020 D3.3): given an object's schema, a state