@objectstack/objectql 7.0.0 → 7.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  import { ServiceObject, ObjectOwnership, HookContext, QueryAST, EngineQueryOptions, DataEngineInsertOptions, EngineUpdateOptions, EngineDeleteOptions, EngineCountOptions, EngineAggregateOptions, DateGranularityValue, Hook } from '@objectstack/spec/data';
2
- import { ObjectStackManifest, InstalledPackage, ExecutionContext } from '@objectstack/spec/kernel';
2
+ import { ObjectStackManifest, InstalledPackage, MetadataValidationResult, ExecutionContext } from '@objectstack/spec/kernel';
3
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';
4
+ import { MetadataRepository, MetaRef, MetadataItem, PutOptions, PutResult, DeleteOptions, DeleteResult, MetadataWriteIntent, ListFilter, MetadataItemHeader, HistoryOptions, MetadataEvent, WatchFilter } from '@objectstack/metadata-core';
5
5
  import { ObjectStackProtocol, MetadataCacheRequest, MetadataCacheResponse, BatchUpdateRequest, BatchUpdateResponse, UpdateManyDataRequest, DeleteManyDataRequest } from '@objectstack/spec/api';
6
6
  import { IDataEngine, DriverInterface, Logger, Plugin, PluginContext, ObjectKernel } from '@objectstack/core';
7
7
  import { IFeedService, IRealtimeService } from '@objectstack/spec/contracts';
@@ -270,6 +270,12 @@ declare class SchemaRegistry {
270
270
  reset(): void;
271
271
  }
272
272
 
273
+ /**
274
+ * Re-export the canonical validation-result type so callers in this
275
+ * package don't need to dual-import from `@objectstack/spec/kernel`.
276
+ */
277
+ type MetadataDiagnostics = MetadataValidationResult;
278
+
273
279
  declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
274
280
  private engine;
275
281
  private getServicesRegistry?;
@@ -488,19 +494,55 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
488
494
  description?: string | undefined;
489
495
  }[];
490
496
  }>;
497
+ /**
498
+ * Sweep all (or filtered) metadata types and report entries that
499
+ * fail spec validation. Powers the Studio governance view
500
+ * (`GET /api/v1/meta/diagnostics`) and `os doctor`-style CLI
501
+ * checks.
502
+ *
503
+ * `severity` defaults to `'error'` — only entries with at least
504
+ * one Zod error issue are returned. `'warning'` includes
505
+ * everything we surface (warnings are reserved for a future lint
506
+ * layer on top of spec validation).
507
+ *
508
+ * `type` may be either a singular (`'view'`) or plural (`'views'`)
509
+ * identifier; the underlying `getMetaItems` already normalises.
510
+ *
511
+ * Implementation note: leverages the `_diagnostics` already
512
+ * decorated onto items by `getMetaItems()` to avoid running
513
+ * `safeParse()` twice. For types whose schema is unregistered we
514
+ * skip silently (they cannot be validated and should not appear
515
+ * as "valid" either — they are simply opaque to this report).
516
+ */
517
+ getMetaDiagnostics(request?: {
518
+ type?: string;
519
+ severity?: 'error' | 'warning';
520
+ organizationId?: string;
521
+ packageId?: string;
522
+ }): Promise<{
523
+ entries: Array<{
524
+ type: string;
525
+ name: string;
526
+ diagnostics: MetadataDiagnostics;
527
+ }>;
528
+ total: number;
529
+ scannedTypes: number;
530
+ scannedItems: number;
531
+ }>;
491
532
  getMetaItems(request: {
492
533
  type: string;
493
534
  packageId?: string;
494
535
  organizationId?: string;
495
536
  }): Promise<{
496
537
  type: string;
497
- items: unknown[];
538
+ items: any[];
498
539
  }>;
499
540
  getMetaItem(request: {
500
541
  type: string;
501
542
  name: string;
502
543
  packageId?: string;
503
544
  organizationId?: string;
545
+ state?: 'active' | 'draft';
504
546
  }): Promise<{
505
547
  type: string;
506
548
  name: string;
@@ -532,6 +574,15 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
532
574
  overlay: unknown | null;
533
575
  overlayScope: 'org' | 'env' | null;
534
576
  effective: unknown | null;
577
+ /**
578
+ * Load-time validation result for the effective payload — same
579
+ * shape attached to getMetaItems/getMetaItem by
580
+ * decorateMetadataItem. Undefined for types without a registered
581
+ * Zod schema (function/service/router). Lets the Studio edit
582
+ * page surface invalid-metadata banners + inline field errors
583
+ * without a second round-trip.
584
+ */
585
+ _diagnostics?: MetadataDiagnostics;
535
586
  }>;
536
587
  getUiView(request: {
537
588
  object: string;
@@ -565,7 +616,7 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
565
616
  label: string | undefined;
566
617
  required: boolean;
567
618
  readonly: boolean;
568
- 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" | "composite" | "repeater" | "location" | "address" | "json" | "color" | "rating" | "slider" | "qrcode" | "vector";
619
+ type: "number" | "boolean" | "tags" | "date" | "record" | "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" | "composite" | "repeater" | "location" | "address" | "json" | "color" | "rating" | "slider" | "qrcode" | "vector";
569
620
  colSpan: number;
570
621
  }[];
571
622
  }[];
@@ -762,8 +813,44 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
762
813
  private static envWritableTypes;
763
814
  /** Test hook — clear the memoised env-writable cache. */
764
815
  static resetEnvWritableCache(): void;
816
+ /**
817
+ * Types that opt into runtime creation of brand-new items (ADR-0005
818
+ * extension — two-tier model). A type may have
819
+ * `allowOrgOverride: false` (cannot overlay artifact-shipped items)
820
+ * yet still set `allowRuntimeCreate: true` (users can author new
821
+ * items in `sys_metadata`). The two flags are orthogonal; see
822
+ * {@link isArtifactBacked} for how the protocol decides which gate
823
+ * applies to a given save/delete.
824
+ */
825
+ /**
826
+ * Set of type names that have a static entry in
827
+ * `DEFAULT_METADATA_TYPE_REGISTRY`. Anything outside this set is
828
+ * runtime-registered (plugin-provided types like `theme`, `api`,
829
+ * `connector`) — the listing endpoint at `getMetaTypes()` synthesises
830
+ * those with `allowRuntimeCreate: true`, so this gate must agree.
831
+ */
832
+ private static readonly STATIC_REGISTRY_TYPES;
833
+ private static readonly RUNTIME_CREATE_ALLOWED_TYPES;
765
834
  /** Normalize plural→singular before consulting the allow-list. */
766
835
  private static isOverlayAllowed;
836
+ /** Does this type permit creating brand-new (artifact-free) items? */
837
+ private static isRuntimeCreateAllowed;
838
+ /**
839
+ * Does an artifact (npm-package-loaded) item exist at `(type, name)`?
840
+ *
841
+ * The schema registry's `_packageId` tag is set only when
842
+ * `registerItem(..., packageId)` is called with a truthy packageId
843
+ * — and only artifact loaders do that. DB-rehydrated items
844
+ * (sys_metadata rows registered back into the registry by
845
+ * `getMetaItems` / `loadMetaFromDb`) call `registerItem` without a
846
+ * packageId, so they carry no `_packageId` and are correctly
847
+ * excluded here.
848
+ *
849
+ * Used by the two-tier authorization model to distinguish
850
+ * "overlaying a packaged item" (requires `allowOrgOverride`) from
851
+ * "authoring a DB-only item" (requires only `allowRuntimeCreate`).
852
+ */
853
+ private isArtifactBacked;
767
854
  /**
768
855
  * Mirror an object-type overlay write into the in-memory engine
769
856
  * registry so subsequent CRUD finds the new schema. Idempotent and
@@ -783,16 +870,19 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
783
870
  parentVersion?: string | null;
784
871
  actor?: string;
785
872
  force?: boolean;
873
+ mode?: 'draft' | 'publish';
786
874
  }): Promise<{
787
875
  success: boolean;
788
876
  version: string;
789
877
  seq: number;
878
+ state: string;
790
879
  message: string;
791
880
  } | {
792
881
  success: boolean;
793
882
  message: string;
794
883
  version?: undefined;
795
884
  seq?: undefined;
885
+ state?: undefined;
796
886
  }>;
797
887
  /**
798
888
  * Yield the durable change-log for a single metadata item — every
@@ -813,6 +903,78 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
813
903
  }): Promise<{
814
904
  events: _objectstack_metadata_core.MetadataEvent[];
815
905
  }>;
906
+ /**
907
+ * Promote the pending draft overlay to the live (`active`) row.
908
+ * Records a history event with `op='publish'`. 404 (`[no_draft]`)
909
+ * when there is nothing to publish.
910
+ */
911
+ publishMetaItem(request: {
912
+ type: string;
913
+ name: string;
914
+ organizationId?: string;
915
+ actor?: string;
916
+ message?: string;
917
+ }): Promise<{
918
+ success: boolean;
919
+ version: string;
920
+ seq: number;
921
+ message?: string;
922
+ }>;
923
+ /**
924
+ * Restore the body recorded at history `toVersion` as the new
925
+ * live row. Writes a history event with `op='revert'`. 404
926
+ * (`[version_not_found]`) when the target version doesn't exist;
927
+ * 409 (`[version_not_restorable]`) when the target is a delete
928
+ * tombstone (no body to bring back).
929
+ */
930
+ rollbackMetaItem(request: {
931
+ type: string;
932
+ name: string;
933
+ toVersion: number;
934
+ organizationId?: string;
935
+ actor?: string;
936
+ message?: string;
937
+ }): Promise<{
938
+ success: boolean;
939
+ version: string;
940
+ seq: number;
941
+ restoredFromVersion: number;
942
+ message?: string;
943
+ }>;
944
+ /**
945
+ * Compute a shallow structural diff between two historical
946
+ * versions of a metadata item. Either side may be omitted: when
947
+ * `toVersion` is undefined the current active body is used; when
948
+ * `fromVersion` is undefined the immediately previous history row
949
+ * is used. Returns `{ added, removed, changed }` keyed by JSON
950
+ * pointer-style paths for primitive leaves; nested objects/arrays
951
+ * are reported as a single change record.
952
+ */
953
+ diffMetaItem(request: {
954
+ type: string;
955
+ name: string;
956
+ fromVersion?: number;
957
+ toVersion?: number;
958
+ organizationId?: string;
959
+ }): Promise<{
960
+ type: string;
961
+ name: string;
962
+ fromVersion: number | null;
963
+ toVersion: number | null;
964
+ added: Array<{
965
+ path: string;
966
+ value: unknown;
967
+ }>;
968
+ removed: Array<{
969
+ path: string;
970
+ value: unknown;
971
+ }>;
972
+ changed: Array<{
973
+ path: string;
974
+ from: unknown;
975
+ to: unknown;
976
+ }>;
977
+ }>;
816
978
  /**
817
979
  * Remove a customization overlay row for the given metadata item, so the
818
980
  * next read falls through to the artifact-loaded default. Implements the
@@ -825,6 +987,7 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
825
987
  organizationId?: string;
826
988
  parentVersion?: string | null;
827
989
  actor?: string;
990
+ state?: 'active' | 'draft';
828
991
  }): Promise<{
829
992
  success: boolean;
830
993
  message?: string;
@@ -884,6 +1047,28 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
884
1047
  feedUnsubscribe(request: any): Promise<any>;
885
1048
  }
886
1049
 
1050
+ /**
1051
+ * Overlay-row lifecycle state.
1052
+ *
1053
+ * - `'active'` → the published, live overlay. `getMetaItem` (the
1054
+ * default read path) and runtime loaders observe this row.
1055
+ * - `'draft'` → an unpublished pending change. Lives alongside the
1056
+ * active row (one of each per `(org,type,name)`). Promoted to
1057
+ * `active` via {@link SysMetadataRepository.promoteDraft}.
1058
+ *
1059
+ * Other lifecycle values defined on `sys_metadata.state` (`'archived'`,
1060
+ * `'deprecated'`) are not yet plumbed through the overlay write path;
1061
+ * they remain reserved for future flows (item retirement, freeze).
1062
+ */
1063
+ type OverlayState = 'active' | 'draft';
1064
+ /**
1065
+ * Extended history operation tag. The base `'create' | 'update' |
1066
+ * 'delete'` operations are emitted by the canonical put/delete paths.
1067
+ * `'publish'` is recorded when a draft is promoted, `'revert'` when a
1068
+ * historical version is restored. Both are surfaced as MetadataEvent
1069
+ * `.op` values via `history()`.
1070
+ */
1071
+ type ExtendedOperation = 'create' | 'update' | 'publish' | 'revert' | 'delete';
887
1072
  /**
888
1073
  * Sub-set of the ObjectQL engine shape we depend on. Kept narrow so
889
1074
  * tests can stub it with a plain mock. Mirrors the real engine's
@@ -963,8 +1148,14 @@ declare class SysMetadataRepository implements MetadataRepository {
963
1148
  /**
964
1149
  * Read the current overlay row. Returns null if no row exists —
965
1150
  * callers (e.g. LayeredRepository) fall through to lower layers.
1151
+ *
1152
+ * `opts.state` selects which lifecycle row to read: defaults to the
1153
+ * live published row (`'active'`). Pass `'draft'` to read the pending
1154
+ * unpublished revision (if any).
966
1155
  */
967
- get(ref: MetaRef): Promise<MetadataItem | null>;
1156
+ get(ref: MetaRef, opts?: {
1157
+ state?: OverlayState;
1158
+ }): Promise<MetadataItem | null>;
968
1159
  /**
969
1160
  * Resolve a historical version by content hash (ADR-0009).
970
1161
  *
@@ -974,8 +1165,55 @@ declare class SysMetadataRepository implements MetadataRepository {
974
1165
  * them.
975
1166
  */
976
1167
  getByHash(ref: MetaRef, hash: string): Promise<MetadataItem | null>;
977
- put(ref: MetaRef, spec: unknown, opts: PutOptions): Promise<PutResult>;
978
- delete(ref: MetaRef, opts: DeleteOptions): Promise<DeleteResult>;
1168
+ put(ref: MetaRef, spec: unknown, opts: PutOptions & {
1169
+ state?: OverlayState;
1170
+ opType?: ExtendedOperation;
1171
+ }): Promise<PutResult>;
1172
+ delete(ref: MetaRef, opts: DeleteOptions & {
1173
+ state?: OverlayState;
1174
+ }): Promise<DeleteResult>;
1175
+ /**
1176
+ * Promote the pending draft row for `ref` into the live (`active`)
1177
+ * overlay. Atomic: reads the draft inside the same transaction, runs
1178
+ * the canonical `put` to upsert the active row (which appends a
1179
+ * history event with `operation_type='publish'`), then deletes the
1180
+ * draft row.
1181
+ *
1182
+ * Errors if no draft exists (callers should 404). The active row's
1183
+ * `parentVersion` is computed from the current active hash so this
1184
+ * also surfaces optimistic-lock conflicts when something else has
1185
+ * published in between (e.g. another admin reverted to an older
1186
+ * version since the draft was authored).
1187
+ */
1188
+ promoteDraft(ref: MetaRef, opts: {
1189
+ actor: string;
1190
+ source?: string;
1191
+ message?: string;
1192
+ intent?: MetadataWriteIntent;
1193
+ }): Promise<{
1194
+ version: string;
1195
+ seq: number;
1196
+ item: MetadataItem;
1197
+ }>;
1198
+ /**
1199
+ * Restore the body recorded in history at `targetVersion` (per-org
1200
+ * lineage counter) as the new active row. Writes a history event
1201
+ * with `operation_type='revert'` so the audit trail captures the
1202
+ * intent. Does NOT touch any draft row.
1203
+ *
1204
+ * Throws `[version_not_found]` (404) if the target version row is
1205
+ * missing or is a delete tombstone (no body to restore).
1206
+ */
1207
+ restoreVersion(ref: MetaRef, targetVersion: number, opts: {
1208
+ actor: string;
1209
+ source?: string;
1210
+ message?: string;
1211
+ intent?: MetadataWriteIntent;
1212
+ }): Promise<{
1213
+ version: string;
1214
+ seq: number;
1215
+ item: MetadataItem;
1216
+ }>;
979
1217
  list(filter: ListFilter): AsyncIterable<MetadataItemHeader>;
980
1218
  /**
981
1219
  * Yield every history event for `(org, type?, name?)` from the
@@ -996,6 +1234,19 @@ declare class SysMetadataRepository implements MetadataRepository {
996
1234
  /** Shut down all watch iterators. */
997
1235
  close(): void;
998
1236
  private assertOpen;
1237
+ /**
1238
+ * Defense-in-depth authorization gate.
1239
+ *
1240
+ * `intent` defaults to `'override-artifact'` (the historical strict
1241
+ * behavior). The protocol layer passes `'runtime-only'` after it has
1242
+ * verified — via the schema registry — that no artifact item exists
1243
+ * at `(type, name)`. In that case we accept types with
1244
+ * `allowRuntimeCreate: true`, even when `allowOrgOverride` is false.
1245
+ *
1246
+ * The env-var escape hatch (`OBJECTSTACK_METADATA_WRITABLE`) still
1247
+ * applies to BOTH intents, so operators can opt into artifact
1248
+ * overrides at runtime for emergency fixes.
1249
+ */
999
1250
  private assertAllowed;
1000
1251
  private whereFor;
1001
1252
  private fullRef;