@objectstack/objectql 4.2.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +215 -1
- package/dist/index.d.ts +215 -1
- package/dist/index.js +776 -19
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +773 -17
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -5
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { ServiceObject, ObjectOwnership, HookContext, QueryAST, EngineQueryOptions, DataEngineInsertOptions, EngineUpdateOptions, EngineDeleteOptions, EngineCountOptions, EngineAggregateOptions, DateGranularityValue, Hook } from '@objectstack/spec/data';
|
|
2
2
|
import { ObjectStackManifest, InstalledPackage, ExecutionContext } from '@objectstack/spec/kernel';
|
|
3
|
+
import * as _objectstack_metadata_core from '@objectstack/metadata-core';
|
|
4
|
+
import { MetadataRepository, MetaRef, MetadataItem, PutOptions, PutResult, DeleteOptions, DeleteResult, ListFilter, MetadataItemHeader, HistoryOptions, MetadataEvent, WatchFilter } from '@objectstack/metadata-core';
|
|
3
5
|
import { ObjectStackProtocol, MetadataCacheRequest, MetadataCacheResponse, BatchUpdateRequest, BatchUpdateResponse, UpdateManyDataRequest, DeleteManyDataRequest } from '@objectstack/spec/api';
|
|
4
6
|
import { IDataEngine, DriverInterface, Logger, Plugin, PluginContext, ObjectKernel } from '@objectstack/core';
|
|
5
7
|
import { IFeedService, IRealtimeService } from '@objectstack/spec/contracts';
|
|
@@ -244,6 +246,21 @@ declare class SchemaRegistry {
|
|
|
244
246
|
id: string;
|
|
245
247
|
globs: string[];
|
|
246
248
|
}[];
|
|
249
|
+
/**
|
|
250
|
+
* Invalidate the merged-schema cache for a single FQN (or short name).
|
|
251
|
+
*
|
|
252
|
+
* Call this from event-driven consumers (ADR-0008 M0 PR-7) when an
|
|
253
|
+
* upstream metadata change makes the cached merged definition stale.
|
|
254
|
+
* The contributor list is preserved — only the cached merge result is
|
|
255
|
+
* dropped, so the next `resolveObject(fqn)` recomputes from scratch.
|
|
256
|
+
*
|
|
257
|
+
* Accepts either an FQN (`acme__contact`) or a bare short name
|
|
258
|
+
* (`contact`); for the latter, all entries whose suffix matches the
|
|
259
|
+
* name are invalidated.
|
|
260
|
+
*/
|
|
261
|
+
invalidate(fqnOrName: string): void;
|
|
262
|
+
/** Drop every entry from the merged-schema cache. */
|
|
263
|
+
invalidateAll(): void;
|
|
247
264
|
/**
|
|
248
265
|
* Clear all registry state. Use only for testing.
|
|
249
266
|
*/
|
|
@@ -263,7 +280,20 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
|
|
|
263
280
|
* metadata even if several projects share the same physical database.
|
|
264
281
|
*/
|
|
265
282
|
private projectId?;
|
|
283
|
+
/**
|
|
284
|
+
* Lazily-instantiated SysMetadataRepository per organization. Keyed by
|
|
285
|
+
* `${organizationId ?? '__env__'}`. Repositories are stateful — they
|
|
286
|
+
* carry the per-org `seqCounter` and watch subscribers — so we cache
|
|
287
|
+
* them rather than constructing one per call.
|
|
288
|
+
*/
|
|
289
|
+
private overlayRepos;
|
|
266
290
|
constructor(engine: IDataEngine, getServicesRegistry?: () => Map<string, any>, getFeedService?: () => IFeedService | undefined, projectId?: string);
|
|
291
|
+
/**
|
|
292
|
+
* Lazily obtain a SysMetadataRepository for the given organization.
|
|
293
|
+
* Env-wide overlays (organizationId == null) share a singleton under
|
|
294
|
+
* the `__env__` key.
|
|
295
|
+
*/
|
|
296
|
+
private getOverlayRepo;
|
|
267
297
|
/**
|
|
268
298
|
* One-time guard for ensuring the overlay-uniqueness UNIQUE INDEX exists
|
|
269
299
|
* on `sys_metadata`. ADR-0005: scopes overlays by
|
|
@@ -561,14 +591,53 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
|
|
|
561
591
|
private static readonly OVERLAY_ALLOWED_TYPES;
|
|
562
592
|
/** Normalize plural→singular before consulting the allow-list. */
|
|
563
593
|
private static isOverlayAllowed;
|
|
594
|
+
/**
|
|
595
|
+
* Mirror an object-type overlay write into the in-memory engine
|
|
596
|
+
* registry so subsequent CRUD finds the new schema. Idempotent and
|
|
597
|
+
* safe to call after a successful persistence call. For the legacy
|
|
598
|
+
* write path this is invoked BEFORE persistence (historical behavior
|
|
599
|
+
* preserved); for the PR-10d.3 repository path it is invoked only
|
|
600
|
+
* AFTER `put()` resolves successfully, so a failed write — DB error,
|
|
601
|
+
* optimistic-lock conflict, validation failure — never leaks a
|
|
602
|
+
* stale schema into the registry.
|
|
603
|
+
*/
|
|
604
|
+
private applyObjectRegistryMutation;
|
|
564
605
|
saveMetaItem(request: {
|
|
565
606
|
type: string;
|
|
566
607
|
name: string;
|
|
567
608
|
item?: any;
|
|
568
609
|
organizationId?: string;
|
|
610
|
+
parentVersion?: string | null;
|
|
611
|
+
actor?: string;
|
|
569
612
|
}): Promise<{
|
|
613
|
+
success: boolean;
|
|
614
|
+
version: string;
|
|
615
|
+
seq: number;
|
|
616
|
+
message: string;
|
|
617
|
+
} | {
|
|
570
618
|
success: boolean;
|
|
571
619
|
message: string;
|
|
620
|
+
version?: undefined;
|
|
621
|
+
seq?: undefined;
|
|
622
|
+
}>;
|
|
623
|
+
/**
|
|
624
|
+
* Yield the durable change-log for a single metadata item — every
|
|
625
|
+
* put/delete recorded in `sys_metadata_history` for `(org, type, name)`,
|
|
626
|
+
* in event_seq order. Powers the Studio "History" tab and any
|
|
627
|
+
* client-side audit timeline.
|
|
628
|
+
*
|
|
629
|
+
* Returns `[]` for non-overlay-allowed types (the legacy raw-engine
|
|
630
|
+
* path doesn't record history) instead of throwing — callers can treat
|
|
631
|
+
* "no history" uniformly.
|
|
632
|
+
*/
|
|
633
|
+
historyMetaItem(request: {
|
|
634
|
+
type: string;
|
|
635
|
+
name: string;
|
|
636
|
+
organizationId?: string;
|
|
637
|
+
sinceSeq?: number;
|
|
638
|
+
limit?: number;
|
|
639
|
+
}): Promise<{
|
|
640
|
+
events: _objectstack_metadata_core.MetadataEvent[];
|
|
572
641
|
}>;
|
|
573
642
|
/**
|
|
574
643
|
* Remove a customization overlay row for the given metadata item, so the
|
|
@@ -580,10 +649,13 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
|
|
|
580
649
|
type: string;
|
|
581
650
|
name: string;
|
|
582
651
|
organizationId?: string;
|
|
652
|
+
parentVersion?: string | null;
|
|
653
|
+
actor?: string;
|
|
583
654
|
}): Promise<{
|
|
584
655
|
success: boolean;
|
|
585
656
|
message?: string;
|
|
586
657
|
reset?: boolean;
|
|
658
|
+
seq?: number;
|
|
587
659
|
}>;
|
|
588
660
|
/**
|
|
589
661
|
* Hydrate SchemaRegistry from the database on startup.
|
|
@@ -614,6 +686,132 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
|
|
|
614
686
|
feedUnsubscribe(request: any): Promise<any>;
|
|
615
687
|
}
|
|
616
688
|
|
|
689
|
+
/**
|
|
690
|
+
* Sub-set of the ObjectQL engine shape we depend on. Kept narrow so
|
|
691
|
+
* tests can stub it with a plain mock. Mirrors the real engine's
|
|
692
|
+
* `options.context` pattern so transactions can thread through.
|
|
693
|
+
*/
|
|
694
|
+
interface SysMetadataEngine {
|
|
695
|
+
find(table: string, options: {
|
|
696
|
+
where: Record<string, unknown>;
|
|
697
|
+
limit?: number;
|
|
698
|
+
orderBy?: any;
|
|
699
|
+
context?: any;
|
|
700
|
+
}): Promise<any[]>;
|
|
701
|
+
findOne(table: string, options: {
|
|
702
|
+
where: Record<string, unknown>;
|
|
703
|
+
context?: any;
|
|
704
|
+
}): Promise<any | null>;
|
|
705
|
+
insert(table: string, data: Record<string, unknown>, options?: {
|
|
706
|
+
context?: any;
|
|
707
|
+
}): Promise<{
|
|
708
|
+
id: string;
|
|
709
|
+
}>;
|
|
710
|
+
update(table: string, data: Record<string, unknown>, options: {
|
|
711
|
+
where: Record<string, unknown>;
|
|
712
|
+
context?: any;
|
|
713
|
+
}): Promise<{
|
|
714
|
+
id: string;
|
|
715
|
+
}>;
|
|
716
|
+
delete(table: string, options: {
|
|
717
|
+
where: Record<string, unknown>;
|
|
718
|
+
context?: any;
|
|
719
|
+
}): Promise<{
|
|
720
|
+
deleted: number;
|
|
721
|
+
}>;
|
|
722
|
+
/**
|
|
723
|
+
* Optional. Falls through to direct callback invocation if the
|
|
724
|
+
* underlying driver lacks ACID support (matches the real
|
|
725
|
+
* `ObjectQL.transaction` semantics). Repository code must not rely on
|
|
726
|
+
* rollback for correctness against in-memory drivers.
|
|
727
|
+
*/
|
|
728
|
+
transaction?<T>(callback: (trxCtx: any) => Promise<T>, baseContext?: any): Promise<T>;
|
|
729
|
+
}
|
|
730
|
+
interface SysMetadataRepositoryOptions {
|
|
731
|
+
engine: SysMetadataEngine;
|
|
732
|
+
/**
|
|
733
|
+
* Tenancy scope. `null` writes to env-wide overlay rows; a string
|
|
734
|
+
* scopes to one organization (the supported shared-DB tenant model
|
|
735
|
+
* — see ADR-0005 amendment).
|
|
736
|
+
*/
|
|
737
|
+
organizationId?: string | null;
|
|
738
|
+
/** Org label embedded in returned MetaRefs. Defaults to organizationId or `"system"`. */
|
|
739
|
+
orgLabel?: string;
|
|
740
|
+
}
|
|
741
|
+
declare class SysMetadataRepository implements MetadataRepository {
|
|
742
|
+
private readonly engine;
|
|
743
|
+
private readonly organizationId;
|
|
744
|
+
private readonly orgLabel;
|
|
745
|
+
/**
|
|
746
|
+
* Local seq counter for in-memory watch() event broadcasts. Mirrors
|
|
747
|
+
* the durable `event_seq` we write into `sys_metadata_history` on
|
|
748
|
+
* each successful put/delete — assigned AFTER the transaction commits
|
|
749
|
+
* so we never broadcast events that got rolled back.
|
|
750
|
+
*/
|
|
751
|
+
private seqCounter;
|
|
752
|
+
private readonly watchers;
|
|
753
|
+
private closed;
|
|
754
|
+
/** Table name for the durable event log. */
|
|
755
|
+
private readonly historyTable;
|
|
756
|
+
constructor(opts: SysMetadataRepositoryOptions);
|
|
757
|
+
/**
|
|
758
|
+
* Run `cb` inside `engine.transaction(...)` if the engine supports it,
|
|
759
|
+
* otherwise fall through to a direct call. Matches the real
|
|
760
|
+
* `ObjectQL.transaction` semantics — in-memory drivers (and our test
|
|
761
|
+
* fakes) get no rollback, which is acceptable because production
|
|
762
|
+
* always runs on a SQL driver with real ACID.
|
|
763
|
+
*/
|
|
764
|
+
private withTxn;
|
|
765
|
+
/**
|
|
766
|
+
* Read the current overlay row. Returns null if no row exists —
|
|
767
|
+
* callers (e.g. LayeredRepository) fall through to lower layers.
|
|
768
|
+
*/
|
|
769
|
+
get(ref: MetaRef): Promise<MetadataItem | null>;
|
|
770
|
+
put(ref: MetaRef, spec: unknown, opts: PutOptions): Promise<PutResult>;
|
|
771
|
+
delete(ref: MetaRef, opts: DeleteOptions): Promise<DeleteResult>;
|
|
772
|
+
list(filter: ListFilter): AsyncIterable<MetadataItemHeader>;
|
|
773
|
+
/**
|
|
774
|
+
* Yield every history event for `(org, type?, name?)` from the
|
|
775
|
+
* durable log, ordered by per-(type,name) `version` ascending. When
|
|
776
|
+
* `filter.type`/`filter.name` are unset the consumer gets the full
|
|
777
|
+
* org-scoped event stream — still ordered by version within each
|
|
778
|
+
* (type,name) bucket, then by `recorded_at` across buckets (we sort
|
|
779
|
+
* client-side because the test engine doesn't honor `orderBy`).
|
|
780
|
+
*/
|
|
781
|
+
history(ref: MetaRef, opts?: HistoryOptions): AsyncIterable<MetadataEvent>;
|
|
782
|
+
/**
|
|
783
|
+
* Live event stream. Fires for every successful put/delete on THIS
|
|
784
|
+
* instance — cross-replica fan-out is M1. Manual AsyncIterator (not
|
|
785
|
+
* an async generator) so we can deterministically tear down via
|
|
786
|
+
* `iter.return()`, matching the pattern used by InMemoryRepository.
|
|
787
|
+
*/
|
|
788
|
+
watch(filter: WatchFilter, since?: number): AsyncIterable<MetadataEvent>;
|
|
789
|
+
/** Shut down all watch iterators. */
|
|
790
|
+
close(): void;
|
|
791
|
+
private assertOpen;
|
|
792
|
+
private assertAllowed;
|
|
793
|
+
private whereFor;
|
|
794
|
+
private fullRef;
|
|
795
|
+
private rowToItem;
|
|
796
|
+
private broadcast;
|
|
797
|
+
private matchesFilter;
|
|
798
|
+
/**
|
|
799
|
+
* Per-org monotonic event sequence. Reads `MAX(event_seq) + 1` from
|
|
800
|
+
* `sys_metadata_history` scoped by `organization_id`. MUST be called
|
|
801
|
+
* inside a transaction (the only caller is the put/delete txn body) —
|
|
802
|
+
* concurrent writers in the same org race otherwise.
|
|
803
|
+
*/
|
|
804
|
+
private nextEventSeq;
|
|
805
|
+
/**
|
|
806
|
+
* Per-(org,type,name) lineage counter. Reads from history (not from
|
|
807
|
+
* `sys_metadata.version`) so delete + recreate continues incrementing
|
|
808
|
+
* instead of restarting at 1.
|
|
809
|
+
*/
|
|
810
|
+
private nextItemVersion;
|
|
811
|
+
/** Lightweight UUID-ish id for history rows; sufficient for an audit log. */
|
|
812
|
+
private uuid;
|
|
813
|
+
}
|
|
814
|
+
|
|
617
815
|
type HookHandler = (context: HookContext) => Promise<void> | void;
|
|
618
816
|
/**
|
|
619
817
|
* Per-object hook entry with priority support
|
|
@@ -1515,9 +1713,25 @@ declare class ObjectQLPlugin implements Plugin {
|
|
|
1515
1713
|
private hostContext?;
|
|
1516
1714
|
private projectId?;
|
|
1517
1715
|
private skipSchemaSync;
|
|
1716
|
+
/** Unsubscribe handles for metadata-event subscriptions (ADR-0008 PR-7). */
|
|
1717
|
+
private metadataUnsubscribes;
|
|
1518
1718
|
constructor(qlOrOptions?: ObjectQL | ObjectQLPluginOptions, hostContext?: Record<string, any>);
|
|
1519
1719
|
init: (ctx: PluginContext) => Promise<void>;
|
|
1520
1720
|
start: (ctx: PluginContext) => Promise<void>;
|
|
1721
|
+
stop: (ctx: PluginContext) => Promise<void>;
|
|
1722
|
+
/**
|
|
1723
|
+
* Subscribe to `object` metadata events from the metadata service and
|
|
1724
|
+
* invalidate the SchemaRegistry merge cache on each event (ADR-0008
|
|
1725
|
+
* PR-7). For create/update we also re-load the affected object from
|
|
1726
|
+
* the metadata service so subsequent reads see the new definition;
|
|
1727
|
+
* for delete we unregister it from every contributing package.
|
|
1728
|
+
*
|
|
1729
|
+
* Events are filtered to the canonical `object` type — view/dashboard
|
|
1730
|
+
* /flow edits go through their own consumers (Studio SSE, REST cache).
|
|
1731
|
+
*
|
|
1732
|
+
* Stored unsubscribe handle is invoked from {@link stop}.
|
|
1733
|
+
*/
|
|
1734
|
+
private subscribeToMetadataEvents;
|
|
1521
1735
|
/**
|
|
1522
1736
|
* Register built-in audit hooks for auto-stamping created_by/updated_by
|
|
1523
1737
|
* and fetching previousData for update/delete operations. These are
|
|
@@ -1698,4 +1912,4 @@ declare function convertIntrospectedSchemaToObjects(introspectedSchema: Introspe
|
|
|
1698
1912
|
skipSystemColumns?: boolean;
|
|
1699
1913
|
}): ServiceObject[];
|
|
1700
1914
|
|
|
1701
|
-
export { type BindHooksOptions, type BindHooksResult, DEFAULT_EXTENDER_PRIORITY, DEFAULT_OWNER_PRIORITY, type EngineMiddleware, type FieldValidationError, 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, ValidationError, type WrapDeclarativeOptions, applyInMemoryAggregation, applySystemFields, bindHooksToEngine, bucketDateValue, computeFQN, convertIntrospectedSchemaToObjects, createObjectQLKernel, noopHookMetricsRecorder, parseFQN, toTitleCase, validateRecord, wrapDeclarativeHook };
|
|
1915
|
+
export { type BindHooksOptions, type BindHooksResult, DEFAULT_EXTENDER_PRIORITY, DEFAULT_OWNER_PRIORITY, type EngineMiddleware, type FieldValidationError, 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 SysMetadataEngine, SysMetadataRepository, type SysMetadataRepositoryOptions, ValidationError, type WrapDeclarativeOptions, applyInMemoryAggregation, applySystemFields, bindHooksToEngine, bucketDateValue, computeFQN, convertIntrospectedSchemaToObjects, createObjectQLKernel, noopHookMetricsRecorder, parseFQN, toTitleCase, validateRecord, wrapDeclarativeHook };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { ServiceObject, ObjectOwnership, HookContext, QueryAST, EngineQueryOptions, DataEngineInsertOptions, EngineUpdateOptions, EngineDeleteOptions, EngineCountOptions, EngineAggregateOptions, DateGranularityValue, Hook } from '@objectstack/spec/data';
|
|
2
2
|
import { ObjectStackManifest, InstalledPackage, ExecutionContext } from '@objectstack/spec/kernel';
|
|
3
|
+
import * as _objectstack_metadata_core from '@objectstack/metadata-core';
|
|
4
|
+
import { MetadataRepository, MetaRef, MetadataItem, PutOptions, PutResult, DeleteOptions, DeleteResult, ListFilter, MetadataItemHeader, HistoryOptions, MetadataEvent, WatchFilter } from '@objectstack/metadata-core';
|
|
3
5
|
import { ObjectStackProtocol, MetadataCacheRequest, MetadataCacheResponse, BatchUpdateRequest, BatchUpdateResponse, UpdateManyDataRequest, DeleteManyDataRequest } from '@objectstack/spec/api';
|
|
4
6
|
import { IDataEngine, DriverInterface, Logger, Plugin, PluginContext, ObjectKernel } from '@objectstack/core';
|
|
5
7
|
import { IFeedService, IRealtimeService } from '@objectstack/spec/contracts';
|
|
@@ -244,6 +246,21 @@ declare class SchemaRegistry {
|
|
|
244
246
|
id: string;
|
|
245
247
|
globs: string[];
|
|
246
248
|
}[];
|
|
249
|
+
/**
|
|
250
|
+
* Invalidate the merged-schema cache for a single FQN (or short name).
|
|
251
|
+
*
|
|
252
|
+
* Call this from event-driven consumers (ADR-0008 M0 PR-7) when an
|
|
253
|
+
* upstream metadata change makes the cached merged definition stale.
|
|
254
|
+
* The contributor list is preserved — only the cached merge result is
|
|
255
|
+
* dropped, so the next `resolveObject(fqn)` recomputes from scratch.
|
|
256
|
+
*
|
|
257
|
+
* Accepts either an FQN (`acme__contact`) or a bare short name
|
|
258
|
+
* (`contact`); for the latter, all entries whose suffix matches the
|
|
259
|
+
* name are invalidated.
|
|
260
|
+
*/
|
|
261
|
+
invalidate(fqnOrName: string): void;
|
|
262
|
+
/** Drop every entry from the merged-schema cache. */
|
|
263
|
+
invalidateAll(): void;
|
|
247
264
|
/**
|
|
248
265
|
* Clear all registry state. Use only for testing.
|
|
249
266
|
*/
|
|
@@ -263,7 +280,20 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
|
|
|
263
280
|
* metadata even if several projects share the same physical database.
|
|
264
281
|
*/
|
|
265
282
|
private projectId?;
|
|
283
|
+
/**
|
|
284
|
+
* Lazily-instantiated SysMetadataRepository per organization. Keyed by
|
|
285
|
+
* `${organizationId ?? '__env__'}`. Repositories are stateful — they
|
|
286
|
+
* carry the per-org `seqCounter` and watch subscribers — so we cache
|
|
287
|
+
* them rather than constructing one per call.
|
|
288
|
+
*/
|
|
289
|
+
private overlayRepos;
|
|
266
290
|
constructor(engine: IDataEngine, getServicesRegistry?: () => Map<string, any>, getFeedService?: () => IFeedService | undefined, projectId?: string);
|
|
291
|
+
/**
|
|
292
|
+
* Lazily obtain a SysMetadataRepository for the given organization.
|
|
293
|
+
* Env-wide overlays (organizationId == null) share a singleton under
|
|
294
|
+
* the `__env__` key.
|
|
295
|
+
*/
|
|
296
|
+
private getOverlayRepo;
|
|
267
297
|
/**
|
|
268
298
|
* One-time guard for ensuring the overlay-uniqueness UNIQUE INDEX exists
|
|
269
299
|
* on `sys_metadata`. ADR-0005: scopes overlays by
|
|
@@ -561,14 +591,53 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
|
|
|
561
591
|
private static readonly OVERLAY_ALLOWED_TYPES;
|
|
562
592
|
/** Normalize plural→singular before consulting the allow-list. */
|
|
563
593
|
private static isOverlayAllowed;
|
|
594
|
+
/**
|
|
595
|
+
* Mirror an object-type overlay write into the in-memory engine
|
|
596
|
+
* registry so subsequent CRUD finds the new schema. Idempotent and
|
|
597
|
+
* safe to call after a successful persistence call. For the legacy
|
|
598
|
+
* write path this is invoked BEFORE persistence (historical behavior
|
|
599
|
+
* preserved); for the PR-10d.3 repository path it is invoked only
|
|
600
|
+
* AFTER `put()` resolves successfully, so a failed write — DB error,
|
|
601
|
+
* optimistic-lock conflict, validation failure — never leaks a
|
|
602
|
+
* stale schema into the registry.
|
|
603
|
+
*/
|
|
604
|
+
private applyObjectRegistryMutation;
|
|
564
605
|
saveMetaItem(request: {
|
|
565
606
|
type: string;
|
|
566
607
|
name: string;
|
|
567
608
|
item?: any;
|
|
568
609
|
organizationId?: string;
|
|
610
|
+
parentVersion?: string | null;
|
|
611
|
+
actor?: string;
|
|
569
612
|
}): Promise<{
|
|
613
|
+
success: boolean;
|
|
614
|
+
version: string;
|
|
615
|
+
seq: number;
|
|
616
|
+
message: string;
|
|
617
|
+
} | {
|
|
570
618
|
success: boolean;
|
|
571
619
|
message: string;
|
|
620
|
+
version?: undefined;
|
|
621
|
+
seq?: undefined;
|
|
622
|
+
}>;
|
|
623
|
+
/**
|
|
624
|
+
* Yield the durable change-log for a single metadata item — every
|
|
625
|
+
* put/delete recorded in `sys_metadata_history` for `(org, type, name)`,
|
|
626
|
+
* in event_seq order. Powers the Studio "History" tab and any
|
|
627
|
+
* client-side audit timeline.
|
|
628
|
+
*
|
|
629
|
+
* Returns `[]` for non-overlay-allowed types (the legacy raw-engine
|
|
630
|
+
* path doesn't record history) instead of throwing — callers can treat
|
|
631
|
+
* "no history" uniformly.
|
|
632
|
+
*/
|
|
633
|
+
historyMetaItem(request: {
|
|
634
|
+
type: string;
|
|
635
|
+
name: string;
|
|
636
|
+
organizationId?: string;
|
|
637
|
+
sinceSeq?: number;
|
|
638
|
+
limit?: number;
|
|
639
|
+
}): Promise<{
|
|
640
|
+
events: _objectstack_metadata_core.MetadataEvent[];
|
|
572
641
|
}>;
|
|
573
642
|
/**
|
|
574
643
|
* Remove a customization overlay row for the given metadata item, so the
|
|
@@ -580,10 +649,13 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
|
|
|
580
649
|
type: string;
|
|
581
650
|
name: string;
|
|
582
651
|
organizationId?: string;
|
|
652
|
+
parentVersion?: string | null;
|
|
653
|
+
actor?: string;
|
|
583
654
|
}): Promise<{
|
|
584
655
|
success: boolean;
|
|
585
656
|
message?: string;
|
|
586
657
|
reset?: boolean;
|
|
658
|
+
seq?: number;
|
|
587
659
|
}>;
|
|
588
660
|
/**
|
|
589
661
|
* Hydrate SchemaRegistry from the database on startup.
|
|
@@ -614,6 +686,132 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
|
|
|
614
686
|
feedUnsubscribe(request: any): Promise<any>;
|
|
615
687
|
}
|
|
616
688
|
|
|
689
|
+
/**
|
|
690
|
+
* Sub-set of the ObjectQL engine shape we depend on. Kept narrow so
|
|
691
|
+
* tests can stub it with a plain mock. Mirrors the real engine's
|
|
692
|
+
* `options.context` pattern so transactions can thread through.
|
|
693
|
+
*/
|
|
694
|
+
interface SysMetadataEngine {
|
|
695
|
+
find(table: string, options: {
|
|
696
|
+
where: Record<string, unknown>;
|
|
697
|
+
limit?: number;
|
|
698
|
+
orderBy?: any;
|
|
699
|
+
context?: any;
|
|
700
|
+
}): Promise<any[]>;
|
|
701
|
+
findOne(table: string, options: {
|
|
702
|
+
where: Record<string, unknown>;
|
|
703
|
+
context?: any;
|
|
704
|
+
}): Promise<any | null>;
|
|
705
|
+
insert(table: string, data: Record<string, unknown>, options?: {
|
|
706
|
+
context?: any;
|
|
707
|
+
}): Promise<{
|
|
708
|
+
id: string;
|
|
709
|
+
}>;
|
|
710
|
+
update(table: string, data: Record<string, unknown>, options: {
|
|
711
|
+
where: Record<string, unknown>;
|
|
712
|
+
context?: any;
|
|
713
|
+
}): Promise<{
|
|
714
|
+
id: string;
|
|
715
|
+
}>;
|
|
716
|
+
delete(table: string, options: {
|
|
717
|
+
where: Record<string, unknown>;
|
|
718
|
+
context?: any;
|
|
719
|
+
}): Promise<{
|
|
720
|
+
deleted: number;
|
|
721
|
+
}>;
|
|
722
|
+
/**
|
|
723
|
+
* Optional. Falls through to direct callback invocation if the
|
|
724
|
+
* underlying driver lacks ACID support (matches the real
|
|
725
|
+
* `ObjectQL.transaction` semantics). Repository code must not rely on
|
|
726
|
+
* rollback for correctness against in-memory drivers.
|
|
727
|
+
*/
|
|
728
|
+
transaction?<T>(callback: (trxCtx: any) => Promise<T>, baseContext?: any): Promise<T>;
|
|
729
|
+
}
|
|
730
|
+
interface SysMetadataRepositoryOptions {
|
|
731
|
+
engine: SysMetadataEngine;
|
|
732
|
+
/**
|
|
733
|
+
* Tenancy scope. `null` writes to env-wide overlay rows; a string
|
|
734
|
+
* scopes to one organization (the supported shared-DB tenant model
|
|
735
|
+
* — see ADR-0005 amendment).
|
|
736
|
+
*/
|
|
737
|
+
organizationId?: string | null;
|
|
738
|
+
/** Org label embedded in returned MetaRefs. Defaults to organizationId or `"system"`. */
|
|
739
|
+
orgLabel?: string;
|
|
740
|
+
}
|
|
741
|
+
declare class SysMetadataRepository implements MetadataRepository {
|
|
742
|
+
private readonly engine;
|
|
743
|
+
private readonly organizationId;
|
|
744
|
+
private readonly orgLabel;
|
|
745
|
+
/**
|
|
746
|
+
* Local seq counter for in-memory watch() event broadcasts. Mirrors
|
|
747
|
+
* the durable `event_seq` we write into `sys_metadata_history` on
|
|
748
|
+
* each successful put/delete — assigned AFTER the transaction commits
|
|
749
|
+
* so we never broadcast events that got rolled back.
|
|
750
|
+
*/
|
|
751
|
+
private seqCounter;
|
|
752
|
+
private readonly watchers;
|
|
753
|
+
private closed;
|
|
754
|
+
/** Table name for the durable event log. */
|
|
755
|
+
private readonly historyTable;
|
|
756
|
+
constructor(opts: SysMetadataRepositoryOptions);
|
|
757
|
+
/**
|
|
758
|
+
* Run `cb` inside `engine.transaction(...)` if the engine supports it,
|
|
759
|
+
* otherwise fall through to a direct call. Matches the real
|
|
760
|
+
* `ObjectQL.transaction` semantics — in-memory drivers (and our test
|
|
761
|
+
* fakes) get no rollback, which is acceptable because production
|
|
762
|
+
* always runs on a SQL driver with real ACID.
|
|
763
|
+
*/
|
|
764
|
+
private withTxn;
|
|
765
|
+
/**
|
|
766
|
+
* Read the current overlay row. Returns null if no row exists —
|
|
767
|
+
* callers (e.g. LayeredRepository) fall through to lower layers.
|
|
768
|
+
*/
|
|
769
|
+
get(ref: MetaRef): Promise<MetadataItem | null>;
|
|
770
|
+
put(ref: MetaRef, spec: unknown, opts: PutOptions): Promise<PutResult>;
|
|
771
|
+
delete(ref: MetaRef, opts: DeleteOptions): Promise<DeleteResult>;
|
|
772
|
+
list(filter: ListFilter): AsyncIterable<MetadataItemHeader>;
|
|
773
|
+
/**
|
|
774
|
+
* Yield every history event for `(org, type?, name?)` from the
|
|
775
|
+
* durable log, ordered by per-(type,name) `version` ascending. When
|
|
776
|
+
* `filter.type`/`filter.name` are unset the consumer gets the full
|
|
777
|
+
* org-scoped event stream — still ordered by version within each
|
|
778
|
+
* (type,name) bucket, then by `recorded_at` across buckets (we sort
|
|
779
|
+
* client-side because the test engine doesn't honor `orderBy`).
|
|
780
|
+
*/
|
|
781
|
+
history(ref: MetaRef, opts?: HistoryOptions): AsyncIterable<MetadataEvent>;
|
|
782
|
+
/**
|
|
783
|
+
* Live event stream. Fires for every successful put/delete on THIS
|
|
784
|
+
* instance — cross-replica fan-out is M1. Manual AsyncIterator (not
|
|
785
|
+
* an async generator) so we can deterministically tear down via
|
|
786
|
+
* `iter.return()`, matching the pattern used by InMemoryRepository.
|
|
787
|
+
*/
|
|
788
|
+
watch(filter: WatchFilter, since?: number): AsyncIterable<MetadataEvent>;
|
|
789
|
+
/** Shut down all watch iterators. */
|
|
790
|
+
close(): void;
|
|
791
|
+
private assertOpen;
|
|
792
|
+
private assertAllowed;
|
|
793
|
+
private whereFor;
|
|
794
|
+
private fullRef;
|
|
795
|
+
private rowToItem;
|
|
796
|
+
private broadcast;
|
|
797
|
+
private matchesFilter;
|
|
798
|
+
/**
|
|
799
|
+
* Per-org monotonic event sequence. Reads `MAX(event_seq) + 1` from
|
|
800
|
+
* `sys_metadata_history` scoped by `organization_id`. MUST be called
|
|
801
|
+
* inside a transaction (the only caller is the put/delete txn body) —
|
|
802
|
+
* concurrent writers in the same org race otherwise.
|
|
803
|
+
*/
|
|
804
|
+
private nextEventSeq;
|
|
805
|
+
/**
|
|
806
|
+
* Per-(org,type,name) lineage counter. Reads from history (not from
|
|
807
|
+
* `sys_metadata.version`) so delete + recreate continues incrementing
|
|
808
|
+
* instead of restarting at 1.
|
|
809
|
+
*/
|
|
810
|
+
private nextItemVersion;
|
|
811
|
+
/** Lightweight UUID-ish id for history rows; sufficient for an audit log. */
|
|
812
|
+
private uuid;
|
|
813
|
+
}
|
|
814
|
+
|
|
617
815
|
type HookHandler = (context: HookContext) => Promise<void> | void;
|
|
618
816
|
/**
|
|
619
817
|
* Per-object hook entry with priority support
|
|
@@ -1515,9 +1713,25 @@ declare class ObjectQLPlugin implements Plugin {
|
|
|
1515
1713
|
private hostContext?;
|
|
1516
1714
|
private projectId?;
|
|
1517
1715
|
private skipSchemaSync;
|
|
1716
|
+
/** Unsubscribe handles for metadata-event subscriptions (ADR-0008 PR-7). */
|
|
1717
|
+
private metadataUnsubscribes;
|
|
1518
1718
|
constructor(qlOrOptions?: ObjectQL | ObjectQLPluginOptions, hostContext?: Record<string, any>);
|
|
1519
1719
|
init: (ctx: PluginContext) => Promise<void>;
|
|
1520
1720
|
start: (ctx: PluginContext) => Promise<void>;
|
|
1721
|
+
stop: (ctx: PluginContext) => Promise<void>;
|
|
1722
|
+
/**
|
|
1723
|
+
* Subscribe to `object` metadata events from the metadata service and
|
|
1724
|
+
* invalidate the SchemaRegistry merge cache on each event (ADR-0008
|
|
1725
|
+
* PR-7). For create/update we also re-load the affected object from
|
|
1726
|
+
* the metadata service so subsequent reads see the new definition;
|
|
1727
|
+
* for delete we unregister it from every contributing package.
|
|
1728
|
+
*
|
|
1729
|
+
* Events are filtered to the canonical `object` type — view/dashboard
|
|
1730
|
+
* /flow edits go through their own consumers (Studio SSE, REST cache).
|
|
1731
|
+
*
|
|
1732
|
+
* Stored unsubscribe handle is invoked from {@link stop}.
|
|
1733
|
+
*/
|
|
1734
|
+
private subscribeToMetadataEvents;
|
|
1521
1735
|
/**
|
|
1522
1736
|
* Register built-in audit hooks for auto-stamping created_by/updated_by
|
|
1523
1737
|
* and fetching previousData for update/delete operations. These are
|
|
@@ -1698,4 +1912,4 @@ declare function convertIntrospectedSchemaToObjects(introspectedSchema: Introspe
|
|
|
1698
1912
|
skipSystemColumns?: boolean;
|
|
1699
1913
|
}): ServiceObject[];
|
|
1700
1914
|
|
|
1701
|
-
export { type BindHooksOptions, type BindHooksResult, DEFAULT_EXTENDER_PRIORITY, DEFAULT_OWNER_PRIORITY, type EngineMiddleware, type FieldValidationError, 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, ValidationError, type WrapDeclarativeOptions, applyInMemoryAggregation, applySystemFields, bindHooksToEngine, bucketDateValue, computeFQN, convertIntrospectedSchemaToObjects, createObjectQLKernel, noopHookMetricsRecorder, parseFQN, toTitleCase, validateRecord, wrapDeclarativeHook };
|
|
1915
|
+
export { type BindHooksOptions, type BindHooksResult, DEFAULT_EXTENDER_PRIORITY, DEFAULT_OWNER_PRIORITY, type EngineMiddleware, type FieldValidationError, 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 SysMetadataEngine, SysMetadataRepository, type SysMetadataRepositoryOptions, ValidationError, type WrapDeclarativeOptions, applyInMemoryAggregation, applySystemFields, bindHooksToEngine, bucketDateValue, computeFQN, convertIntrospectedSchemaToObjects, createObjectQLKernel, noopHookMetricsRecorder, parseFQN, toTitleCase, validateRecord, wrapDeclarativeHook };
|