@objectstack/objectql 7.2.1 → 7.4.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
@@ -4,7 +4,7 @@ import * as _objectstack_metadata_core from '@objectstack/metadata-core';
4
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
- import { IFeedService, IRealtimeService } from '@objectstack/spec/contracts';
7
+ import { IFeedService, IRealtimeService, ICryptoProvider } from '@objectstack/spec/contracts';
8
8
 
9
9
  /**
10
10
  * Reserved namespaces that do not get FQN prefix applied.
@@ -138,6 +138,29 @@ declare class SchemaRegistry {
138
138
  private namespaceRegistry;
139
139
  /** Type → Name/ID → MetadataItem */
140
140
  private metadata;
141
+ /**
142
+ * App name → navigation contributions (ADR-0029 D7).
143
+ *
144
+ * Lets packages inject nav items into apps they do not own (the UI analog
145
+ * of object extenders). Merged into the owning app's `navigation` tree on
146
+ * read in {@link getApp} / {@link getAllApps} by group id + priority.
147
+ */
148
+ private appNavContributions;
149
+ /**
150
+ * Package ids that must be installed in a DISABLED state. Seeded once at
151
+ * boot (from persisted state) BEFORE any package registration so that every
152
+ * registration path — boot artifact, marketplace rehydrate, local import —
153
+ * honors persisted disable state uniformly without a fragile post-boot
154
+ * re-application hook. See {@link setInitialDisabledPackageIds} and
155
+ * {@link installPackage}.
156
+ */
157
+ private initialDisabledPackageIds;
158
+ /**
159
+ * Seed the set of package ids that should be installed disabled. Call this
160
+ * before package registration begins; later `installPackage` calls for these
161
+ * ids land in the `disabled` state. Replaces any previously seeded set.
162
+ */
163
+ setInitialDisabledPackageIds(ids: Iterable<string>): void;
141
164
  /**
142
165
  * Register a namespace for a package.
143
166
  * Multiple packages can share the same namespace (e.g. 'sys').
@@ -200,6 +223,25 @@ declare class SchemaRegistry {
200
223
  * Get the owner contributor for an object.
201
224
  */
202
225
  getObjectOwner(fqn: string): ObjectContributor | undefined;
226
+ /**
227
+ * ADR-0029 K0 — assert every registered object resolves to exactly one
228
+ * owner.
229
+ *
230
+ * A second `own` from a different package is already rejected eagerly in
231
+ * {@link registerObject} (it throws). This is the install-time backstop
232
+ * called once all packages are registered (kernel bootstrap complete),
233
+ * and it additionally catches the case `registerObject` cannot: an object
234
+ * that has only `extend` contributions and **no owner** — which would
235
+ * otherwise resolve to nothing. Surfacing it here turns a silent
236
+ * "extend a non-existent object" into a clear bootstrap error.
237
+ *
238
+ * This is the invariant the kernel-decomposition (ADR-0029) relies on:
239
+ * the `sys` namespace is shared across many first-party plugins, but each
240
+ * object name has exactly one owner.
241
+ *
242
+ * @throws Error listing every object whose owner count is not exactly 1.
243
+ */
244
+ assertSingleOwnerPerObject(): void;
203
245
  /**
204
246
  * Unregister all objects contributed by a package.
205
247
  *
@@ -226,6 +268,11 @@ declare class SchemaRegistry {
226
268
  * Universal List Method
227
269
  */
228
270
  listItems<T>(type: string, packageId?: string): T[];
271
+ /**
272
+ * Whether a package has been explicitly disabled. Unknown packages and
273
+ * items with no owning package are treated as enabled.
274
+ */
275
+ isPackageDisabled(packageId?: string): boolean;
229
276
  /**
230
277
  * Get all registered metadata types (Kinds)
231
278
  */
@@ -239,6 +286,41 @@ declare class SchemaRegistry {
239
286
  registerApp(app: any, packageId?: string): void;
240
287
  getApp(name: string): any;
241
288
  getAllApps(): any[];
289
+ /**
290
+ * Register a navigation contribution — a package injecting nav items into
291
+ * an app it does not own (the UI-layer analog of object `extend`).
292
+ *
293
+ * Contributions are merged into the target app's `navigation` tree lazily
294
+ * on read ({@link getApp} / {@link getAllApps}) by group id + priority, so
295
+ * registration order does not matter and the owning app can be registered
296
+ * before or after its contributors.
297
+ */
298
+ registerAppNavContribution(contribution: {
299
+ app: string;
300
+ group?: string;
301
+ priority?: number;
302
+ items?: any[];
303
+ }, packageId?: string): void;
304
+ /** Contributions registered for an app (empty array when none). */
305
+ getAppNavContributions(appName: string): Array<{
306
+ packageId?: string;
307
+ group?: string;
308
+ priority: number;
309
+ items: any[];
310
+ }>;
311
+ /**
312
+ * Return a copy of `app` with all registered navigation contributions
313
+ * merged into its `navigation` tree. The stored app is never mutated, so
314
+ * repeated reads stay idempotent.
315
+ *
316
+ * Public so the protocol serving path (`getMetaItems` / `getMetaItem` for
317
+ * `app`) can merge contributions the same way `getApp` / `getAllApps` do —
318
+ * the REST app endpoints read through the protocol, not these helpers, so
319
+ * the merge must be reachable from there too (ADR-0029 D7).
320
+ */
321
+ applyNavContributions(app: any): any;
322
+ /** Depth-first search for a `type: 'group'` nav item by id. */
323
+ private findNavGroup;
242
324
  registerPlugin(manifest: ObjectStackManifest): void;
243
325
  getAllPlugins(): ObjectStackManifest[];
244
326
  registerKind(kind: {
@@ -366,13 +448,100 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
366
448
  getMetaTypes(): Promise<{
367
449
  types: string[];
368
450
  entries: {
451
+ actions?: {
452
+ name: string;
453
+ label: string;
454
+ type: "url" | "flow" | "script" | "api" | "form" | "modal";
455
+ refreshAfter: boolean;
456
+ objectName?: string | undefined;
457
+ icon?: string | undefined;
458
+ locations?: ("list_toolbar" | "list_item" | "record_header" | "record_more" | "record_related" | "record_section" | "global_nav")[] | undefined;
459
+ component?: "action:button" | "action:icon" | "action:menu" | "action:group" | undefined;
460
+ target?: string | undefined;
461
+ body?: {
462
+ language: "expression";
463
+ source: string;
464
+ } | {
465
+ language: "js";
466
+ source: string;
467
+ capabilities: ("log" | "api.read" | "api.write" | "crypto.uuid" | "crypto.hash")[];
468
+ timeoutMs?: number | undefined;
469
+ memoryMb?: number | undefined;
470
+ } | undefined;
471
+ execute?: string | undefined;
472
+ params?: {
473
+ required: boolean;
474
+ name?: string | undefined;
475
+ field?: string | undefined;
476
+ objectOverride?: string | undefined;
477
+ label?: string | undefined;
478
+ type?: "number" | "boolean" | "date" | "record" | "file" | "code" | "json" | "url" | "summary" | "datetime" | "color" | "time" | "text" | "textarea" | "email" | "phone" | "password" | "secret" | "markdown" | "html" | "richtext" | "currency" | "percent" | "toggle" | "select" | "multiselect" | "radio" | "checkboxes" | "lookup" | "master_detail" | "tree" | "image" | "avatar" | "video" | "audio" | "formula" | "autonumber" | "composite" | "repeater" | "location" | "address" | "rating" | "slider" | "signature" | "qrcode" | "progress" | "tags" | "vector" | undefined;
479
+ options?: {
480
+ label: string;
481
+ value: string;
482
+ }[] | undefined;
483
+ placeholder?: string | undefined;
484
+ helpText?: string | undefined;
485
+ defaultValue?: unknown;
486
+ defaultFromRow?: boolean | undefined;
487
+ }[] | undefined;
488
+ variant?: "link" | "primary" | "secondary" | "danger" | "ghost" | undefined;
489
+ confirmText?: string | undefined;
490
+ successMessage?: string | undefined;
491
+ resultDialog?: {
492
+ title?: string | undefined;
493
+ description?: string | undefined;
494
+ acknowledge?: string | undefined;
495
+ format?: "json" | "text" | "secret" | "qrcode" | "code-list" | undefined;
496
+ fields?: {
497
+ path: string;
498
+ label?: string | undefined;
499
+ format?: "json" | "text" | "secret" | "qrcode" | "code-list" | undefined;
500
+ }[] | undefined;
501
+ } | undefined;
502
+ visible?: {
503
+ dialect: "cron" | "cel" | "js" | "template";
504
+ source?: string | undefined;
505
+ ast?: unknown;
506
+ meta?: {
507
+ rationale?: string | undefined;
508
+ generatedBy?: string | undefined;
509
+ } | undefined;
510
+ } | undefined;
511
+ disabled?: boolean | {
512
+ dialect: "cron" | "cel" | "js" | "template";
513
+ source?: string | undefined;
514
+ ast?: unknown;
515
+ meta?: {
516
+ rationale?: string | undefined;
517
+ generatedBy?: string | undefined;
518
+ } | undefined;
519
+ } | undefined;
520
+ shortcut?: string | undefined;
521
+ bulkEnabled?: boolean | undefined;
522
+ aiExposed?: boolean | undefined;
523
+ recordIdParam?: string | undefined;
524
+ recordIdField?: string | undefined;
525
+ bodyShape?: "flat" | {
526
+ wrap: string;
527
+ } | undefined;
528
+ method?: "POST" | "PUT" | "DELETE" | "PATCH" | undefined;
529
+ bodyExtra?: Record<string, unknown> | undefined;
530
+ mode?: "custom" | "create" | "delete" | "edit" | undefined;
531
+ timeout?: number | undefined;
532
+ aria?: {
533
+ ariaLabel?: string | undefined;
534
+ ariaDescribedBy?: string | undefined;
535
+ role?: string | undefined;
536
+ } | undefined;
537
+ }[] | undefined;
369
538
  type: string;
370
539
  schemaId: string;
371
540
  allowOrgOverride: boolean;
372
541
  overrideSource: "env" | "registry";
373
542
  schema: Record<string, unknown>;
374
543
  form: {
375
- type: "split" | "modal" | "drawer" | "simple" | "tabbed" | "wizard";
544
+ type: "split" | "drawer" | "modal" | "simple" | "tabbed" | "wizard";
376
545
  data?: {
377
546
  provider: "object";
378
547
  object: string;
@@ -403,7 +572,7 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
403
572
  sections?: {
404
573
  collapsible: boolean;
405
574
  collapsed: boolean;
406
- columns: 1 | 2 | 3 | 4;
575
+ columns: 1 | 2 | 4 | 3;
407
576
  fields: any[];
408
577
  name?: string | undefined;
409
578
  label?: string | undefined;
@@ -429,7 +598,7 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
429
598
  groups?: {
430
599
  collapsible: boolean;
431
600
  collapsed: boolean;
432
- columns: 1 | 2 | 3 | 4;
601
+ columns: 1 | 2 | 4 | 3;
433
602
  fields: any[];
434
603
  name?: string | undefined;
435
604
  label?: string | undefined;
@@ -685,7 +854,7 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
685
854
  label: string | undefined;
686
855
  required: boolean;
687
856
  readonly: boolean;
688
- 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";
857
+ type: "number" | "boolean" | "tags" | "date" | "record" | "file" | "code" | "datetime" | "signature" | "progress" | "url" | "currency" | "percent" | "password" | "secret" | "email" | "time" | "text" | "textarea" | "phone" | "markdown" | "html" | "richtext" | "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";
689
858
  colSpan: number;
690
859
  }[];
691
860
  }[];
@@ -841,6 +1010,7 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
841
1010
  type: string;
842
1011
  name: string;
843
1012
  cacheRequest?: MetadataCacheRequest;
1013
+ locale?: string;
844
1014
  }): Promise<MetadataCacheResponse>;
845
1015
  batchData(request: {
846
1016
  object: string;
@@ -977,6 +1147,7 @@ declare class ObjectStackProtocolImplementation implements ObjectStackProtocol {
977
1147
  actor?: string;
978
1148
  force?: boolean;
979
1149
  mode?: 'draft' | 'publish';
1150
+ packageId?: string | null;
980
1151
  }): Promise<{
981
1152
  success: boolean;
982
1153
  version: string;
@@ -1432,12 +1603,14 @@ declare class ObjectQL implements IDataEngine {
1432
1603
  private logger;
1433
1604
  private datasourceMapping;
1434
1605
  private manifests;
1606
+ private datasourceDefs;
1435
1607
  private hooks;
1436
1608
  private middlewares;
1437
1609
  private actions;
1438
1610
  private functions;
1439
1611
  private hostContext;
1440
1612
  private realtimeService?;
1613
+ private cryptoProvider?;
1441
1614
  private _registry;
1442
1615
  constructor(hostContext?: Record<string, any>);
1443
1616
  /**
@@ -1635,6 +1808,31 @@ declare class ObjectQL implements IDataEngine {
1635
1808
  * Register a new storage driver
1636
1809
  */
1637
1810
  registerDriver(driver: DriverInterface, isDefault?: boolean): void;
1811
+ /**
1812
+ * Register a Datasource *definition* (ADR-0015).
1813
+ *
1814
+ * Distinct from {@link registerDriver}, which registers a live connection.
1815
+ * This captures the declarative `schemaMode` + `external.allowWrites` so the
1816
+ * write gate ({@link assertWriteAllowed}) can enforce external-datasource
1817
+ * ownership. Safe to call repeatedly; last write wins.
1818
+ */
1819
+ registerDatasourceDef(def: {
1820
+ name: string;
1821
+ schemaMode?: string;
1822
+ external?: {
1823
+ allowWrites?: boolean;
1824
+ };
1825
+ }): void;
1826
+ /**
1827
+ * Write gate — Gate 3 of ADR-0015 §5.3.
1828
+ *
1829
+ * Blocks insert/update/delete against a federated datasource
1830
+ * (`schemaMode !== 'managed'`) unless BOTH the datasource opts in
1831
+ * (`external.allowWrites`) AND the object opts in (`external.writable`).
1832
+ * Managed datasources (the common case, including the absence of any
1833
+ * definition) are unaffected.
1834
+ */
1835
+ private assertWriteAllowed;
1638
1836
  /**
1639
1837
  * Set the realtime service for publishing data change events.
1640
1838
  * Should be called after kernel resolves the realtime service.
@@ -1642,6 +1840,54 @@ declare class ObjectQL implements IDataEngine {
1642
1840
  * @param service - An IRealtimeService instance for event publishing
1643
1841
  */
1644
1842
  setRealtimeService(service: IRealtimeService): void;
1843
+ /**
1844
+ * Register the crypto provider that backs `secret`-typed fields.
1845
+ *
1846
+ * When set, the engine encrypts secret fields on write (storing ciphertext in
1847
+ * `sys_secret` and only an opaque ref on the business row) and masks them on
1848
+ * read. When NOT set, writing to an object that declares a secret field is
1849
+ * **fail-closed** — the write throws rather than persist cleartext.
1850
+ *
1851
+ * Mirrors the Settings subsystem's ICryptoProvider wiring; the host (e.g.
1852
+ * `serve`) injects `InMemoryCryptoProvider` in dev and a KMS/Vault-backed
1853
+ * provider in production.
1854
+ */
1855
+ setCryptoProvider(provider: ICryptoProvider): void;
1856
+ /**
1857
+ * Encrypt any `secret`-typed fields on `row` in place before it reaches the
1858
+ * driver. Each plaintext is wrapped by the ICryptoProvider, persisted as a
1859
+ * `sys_secret` row, and replaced on `row` by an opaque ref. Cleartext never
1860
+ * reaches the business table.
1861
+ *
1862
+ * Rules:
1863
+ * - No secret fields on the object ⇒ no-op (fast path, no crypto cost).
1864
+ * - `null`/`undefined` value ⇒ left as-is (clears the secret).
1865
+ * - Value already a ref (re-save of an unchanged ref) ⇒ left as-is.
1866
+ * - Value equal to the read mask ⇒ dropped, so a form round-trip that
1867
+ * echoes the mask does not overwrite the stored secret.
1868
+ * - **Fail-closed:** any other value with no CryptoProvider registered, or
1869
+ * no reachable `sys_secret` store, THROWS — never persists cleartext.
1870
+ */
1871
+ private encryptSecretFields;
1872
+ /**
1873
+ * Mask `secret`-typed fields on read so plaintext never leaves the engine
1874
+ * through the normal query path. A set secret becomes {@link SECRET_MASK};
1875
+ * an unset one stays `null`. Privileged callers that genuinely need the
1876
+ * plaintext use {@link resolveSecret} against the stored ref.
1877
+ */
1878
+ private maskSecretFields;
1879
+ /**
1880
+ * Dereference a stored secret ref back to its plaintext. Intended for
1881
+ * privileged, server-side consumers (e.g. a datasource connection-pool
1882
+ * binder) — NOT exposed through the generic read path, which only ever
1883
+ * returns the mask.
1884
+ *
1885
+ * Fail-closed: throws when no CryptoProvider is registered or the
1886
+ * `sys_secret` row is missing. Returns `null` when `ref` is not a secret ref.
1887
+ */
1888
+ resolveSecret(ref: unknown, opts?: {
1889
+ tenantId?: string;
1890
+ }): Promise<string | null>;
1645
1891
  /**
1646
1892
  * Helper to get object definition
1647
1893
  */
@@ -1777,6 +2023,17 @@ declare class ObjectQL implements IDataEngine {
1777
2023
  * this method directly looks up a driver by its registered name.
1778
2024
  */
1779
2025
  getDriverByName(name: string): DriverInterface | undefined;
2026
+ /**
2027
+ * Introspect a datasource's live remote schema (ADR-0015).
2028
+ *
2029
+ * Resolves the driver registered under `datasource` and delegates to its
2030
+ * `introspectSchema()` capability. Used by the external-datasource service
2031
+ * (and CLI/REST) to list remote tables and validate federated objects.
2032
+ *
2033
+ * @throws if the datasource has no registered driver, or the driver does
2034
+ * not support introspection.
2035
+ */
2036
+ introspectDatasource(datasource: string): Promise<unknown>;
1780
2037
  /**
1781
2038
  * Get the driver responsible for the given object.
1782
2039
  *
@@ -2122,7 +2379,7 @@ declare function wrapDeclarativeHook(meta: Hook, handler: HookHandler, opts?: Wr
2122
2379
 
2123
2380
  interface FieldValidationError {
2124
2381
  field: string;
2125
- code: 'required' | 'min_length' | 'max_length' | 'min_value' | 'max_value' | 'invalid_email' | 'invalid_url' | 'invalid_phone' | 'invalid_number' | 'invalid_boolean' | 'invalid_date' | 'invalid_option';
2382
+ code: 'required' | 'min_length' | 'max_length' | 'min_value' | 'max_value' | 'invalid_email' | 'invalid_url' | 'invalid_phone' | 'invalid_number' | 'invalid_boolean' | 'invalid_date' | 'invalid_option' | 'invalid_transition' | 'rule_violation';
2126
2383
  message: string;
2127
2384
  /** Allowed values for select/multiselect, when applicable. */
2128
2385
  options?: string[];
@@ -2132,7 +2389,7 @@ declare class ValidationError extends Error {
2132
2389
  readonly fields: FieldValidationError[];
2133
2390
  constructor(fields: FieldValidationError[]);
2134
2391
  }
2135
- type Mode = 'insert' | 'update';
2392
+ type Mode$1 = 'insert' | 'update';
2136
2393
  interface FieldDef {
2137
2394
  name?: string;
2138
2395
  type: string;
@@ -2158,7 +2415,45 @@ interface FieldDef {
2158
2415
  */
2159
2416
  declare function validateRecord(objectSchema: {
2160
2417
  fields?: Record<string, FieldDef>;
2161
- } | undefined | null, data: Record<string, unknown> | undefined | null, mode: Mode): void;
2418
+ } | undefined | null, data: Record<string, unknown> | undefined | null, mode: Mode$1): void;
2419
+
2420
+ type Mode = 'insert' | 'update';
2421
+ interface EvaluateRulesOptions {
2422
+ /** Prior persisted record (update only). Absent on insert. */
2423
+ previous?: Record<string, unknown> | null;
2424
+ /** Optional logger for non-blocking diagnostics (broken rules, warnings). */
2425
+ logger?: {
2426
+ warn?: (msg: string, meta?: any) => void;
2427
+ };
2428
+ }
2429
+ /**
2430
+ * Returns true when the object declares at least one validation rule whose
2431
+ * correct evaluation needs the prior record (so the engine knows whether the
2432
+ * extra fetch on the update path is worth it).
2433
+ */
2434
+ declare function needsPriorRecord(objectSchema: {
2435
+ validations?: unknown[];
2436
+ } | undefined | null): boolean;
2437
+ /**
2438
+ * Evaluate an object's declared validation rules against an incoming write.
2439
+ *
2440
+ * Throws `ValidationError` (the same envelope `validateRecord` uses, so REST
2441
+ * surfaces a single `400 VALIDATION_FAILED`) when one or more `error`-severity
2442
+ * rules are violated. Returns void otherwise.
2443
+ */
2444
+ declare function evaluateValidationRules(objectSchema: {
2445
+ validations?: unknown[];
2446
+ } | undefined | null, data: Record<string, unknown> | undefined | null, mode: Mode, opts?: EvaluateRulesOptions): void;
2447
+ /**
2448
+ * Introspection helper (ADR-0020 D3.3): given an object's schema, a state
2449
+ * field, and a current state, return the legal next states declared by the
2450
+ * matching `state_machine` rule. Returns `null` when no such rule exists (so
2451
+ * callers can distinguish "no FSM governs this field" from "a dead-end state
2452
+ * with zero outgoing transitions", which returns `[]`).
2453
+ */
2454
+ declare function legalNextStates(objectSchema: {
2455
+ validations?: unknown[];
2456
+ } | undefined | null, field: string, currentState: string): string[] | null;
2162
2457
 
2163
2458
  /**
2164
2459
  * MetadataFacade
@@ -2389,6 +2684,45 @@ interface ObjectQLKernelOptions {
2389
2684
  */
2390
2685
  declare function createObjectQLKernel(options?: ObjectQLKernelOptions): Promise<ObjectKernel>;
2391
2686
 
2687
+ /**
2688
+ * Secret-field channel — helpers for the `secret` FieldType.
2689
+ *
2690
+ * A `secret` field (DB password, API key, token) is **reversible**: the engine
2691
+ * encrypts it on write via the registered `ICryptoProvider`, persists the
2692
+ * ciphertext as a `sys_secret` row, and stores only an opaque *ref* on the
2693
+ * business row. On read the ref is masked, never the plaintext. This mirrors
2694
+ * the Settings subsystem (`sys_setting.value_enc → sys_secret.id`), generalized
2695
+ * to object fields.
2696
+ *
2697
+ * Contrast with `password` — a one-way hash owned by the auth subsystem, never
2698
+ * decrypted. The two never share a code path.
2699
+ */
2700
+
2701
+ /**
2702
+ * Prefix marking a persisted field value as a `sys_secret` handle ref rather
2703
+ * than cleartext. Chosen to be unambiguous and human-greppable in a DB dump,
2704
+ * while making it obvious that the column holds no plaintext.
2705
+ */
2706
+ declare const SECRET_REF_PREFIX = "secret:";
2707
+ /**
2708
+ * Value returned in place of a secret field on a normal read. Indicates
2709
+ * "a secret is set" without leaking the handle id or the plaintext. A field
2710
+ * with no stored secret resolves to `null` instead.
2711
+ */
2712
+ declare const SECRET_MASK = "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
2713
+ /** Wrap a `sys_secret` handle id as the opaque ref persisted on the row. */
2714
+ declare function makeSecretRef(handleId: string): string;
2715
+ /** True when `value` is a secret ref previously produced by {@link makeSecretRef}. */
2716
+ declare function isSecretRef(value: unknown): value is string;
2717
+ /** Extract the `sys_secret` handle id from a ref, or `null` when not a ref. */
2718
+ declare function parseSecretRef(value: unknown): string | null;
2719
+ /**
2720
+ * Collect the names of `secret`-typed fields declared on an object schema.
2721
+ * Returns an empty array when the schema has no fields or no secret fields —
2722
+ * callers can fast-path on `length === 0` to skip all crypto work.
2723
+ */
2724
+ declare function collectSecretFields(schema: ServiceObject | undefined | null): string[];
2725
+
2392
2726
  /**
2393
2727
  * Column metadata from database introspection.
2394
2728
  */
@@ -2476,4 +2810,4 @@ declare function convertIntrospectedSchemaToObjects(introspectedSchema: Introspe
2476
2810
  skipSystemColumns?: boolean;
2477
2811
  }): ServiceObject[];
2478
2812
 
2479
- 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 };
2813
+ export { type BindHooksOptions, type BindHooksResult, DEFAULT_EXTENDER_PRIORITY, DEFAULT_OWNER_PRIORITY, type EngineMiddleware, type EvaluateRulesOptions, 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, SECRET_MASK, SECRET_REF_PREFIX, SchemaRegistry, type SchemaRegistryOptions, ScopedContext, type SysMetadataEngine, SysMetadataRepository, type SysMetadataRepositoryOptions, ValidationError, type WrapDeclarativeOptions, applyInMemoryAggregation, applySystemFields, bindHooksToEngine, bucketDateValue, collectSecretFields, computeFQN, convertIntrospectedSchemaToObjects, createObjectQLKernel, evaluateValidationRules, isSecretRef, legalNextStates, makeSecretRef, needsPriorRecord, noopHookMetricsRecorder, parseFQN, parseSecretRef, toTitleCase, validateRecord, wrapDeclarativeHook };