@object-ui/data-objectstack 17.5.0 → 17.6.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.
Files changed (52) hide show
  1. package/CHANGELOG.md +219 -0
  2. package/dist/index.cjs +262 -357
  3. package/dist/index.d.cts +385 -448
  4. package/dist/index.d.ts +385 -448
  5. package/dist/index.js +258 -350
  6. package/package.json +5 -6
  7. package/dist/tsconfig.tsbuildinfo +0 -1
  8. package/src/aggregate-capability.test.ts +0 -177
  9. package/src/apiAccessDenied.test.ts +0 -233
  10. package/src/appAccessProbe.test.ts +0 -147
  11. package/src/bulkDelete.test.ts +0 -75
  12. package/src/bulkUpdate.test.ts +0 -93
  13. package/src/cache/MetadataCache.test.ts +0 -477
  14. package/src/cache/MetadataCache.ts +0 -289
  15. package/src/cloud-surface-retired-4152.pin.test.ts +0 -171
  16. package/src/connection.test.ts +0 -188
  17. package/src/contracts.ts +0 -115
  18. package/src/deleteView.homes.test.ts +0 -389
  19. package/src/deleteViewContract.types.test.ts +0 -246
  20. package/src/errors.test.ts +0 -426
  21. package/src/errors.ts +0 -293
  22. package/src/exportDownload.test.ts +0 -141
  23. package/src/filter-entry-translation.test.ts +0 -360
  24. package/src/filter-operator-ast-parity.test.ts +0 -135
  25. package/src/getObjectSchema.test.ts +0 -145
  26. package/src/httpFailureLogging.test.ts +0 -109
  27. package/src/importJob.test.ts +0 -71
  28. package/src/index.ts +0 -4442
  29. package/src/integration.ts +0 -200
  30. package/src/listViewOverrides.test.ts +0 -241
  31. package/src/listViews.test.ts +0 -175
  32. package/src/metadata-client.get-envelope.test.ts +0 -171
  33. package/src/metadata-client.saveAdvisories.test.ts +0 -226
  34. package/src/metadata-client.test.ts +0 -303
  35. package/src/metadata-client.ts +0 -1046
  36. package/src/occ.test.ts +0 -101
  37. package/src/onMutation.test.ts +0 -589
  38. package/src/onSaveAdvisory.test.ts +0 -311
  39. package/src/onWriteWarning.test.ts +0 -273
  40. package/src/orderby-serialization.test.ts +0 -115
  41. package/src/queryDataset.test.ts +0 -543
  42. package/src/searchAll.test.ts +0 -120
  43. package/src/security.ts +0 -243
  44. package/src/spec-symbol-batch6.test.ts +0 -326
  45. package/src/studio.ts +0 -152
  46. package/src/updateView.draft.test.ts +0 -242
  47. package/src/userState.test.ts +0 -315
  48. package/src/userState.ts +0 -238
  49. package/src/v3-compat.test.ts +0 -229
  50. package/src/validation-error.test.ts +0 -94
  51. package/src/viewCacheInvalidation.pin.test.ts +0 -260
  52. package/src/viewInvalidationSeam.pin.test.ts +0 -162
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { ObjectStackClient } from '@objectstack/client';
2
2
  import { DroppedFieldsEvent } from '@objectstack/spec/data';
3
3
  export { DroppedFieldsEvent } from '@objectstack/spec/data';
4
- import { RuntimeAuthoringIssue } from '@objectstack/spec/api';
4
+ import { GetMetaItemLayeredResponse, RuntimeAuthoringIssue } from '@objectstack/spec/api';
5
5
  export { RuntimeAuthoringIssue } from '@objectstack/spec/api';
6
6
  import { DatasetSelection, AnalyticsResult } from '@objectstack/spec/contracts';
7
7
  import { DataSource, QueryParams, QueryResult, GlobalSearchResult, DataSourceMutationEvent, BatchTransactionOperation, ImportRequestOptions, ImportRecordsResult, CreateImportJobResult, ImportJobProgressInfo, ImportJobResultsInfo, ListImportJobsOptions, ImportJobSummaryInfo, ImportJobUndoResult, ExportDownloadRequest, DeleteViewResult, FileUploadResult } from '@object-ui/types';
@@ -146,9 +146,9 @@ interface MetadataDraftHeader {
146
146
  * metadata item to a FILE — `format: json|yaml|ts`, `path`, `indent`,
147
147
  * `prettify`, `sortKeys`, `backup`, `atomic`, `loader`. Not one of those keys
148
148
  * exists here, and not one of these exists there: this is the REST client's
149
- * request envelope — optimistic concurrency (`ifMatch` → `If-Match`), actor
150
- * attribution, the destructive-change override, the ADR-0033 draft/publish
151
- * mode, and the owning package. Same words, different layer.
149
+ * request envelope — optimistic concurrency (`ifMatch` → `If-Match`), the
150
+ * destructive-change override, the ADR-0033 draft/publish mode, and the
151
+ * owning package. Same words, different layer.
152
152
  */
153
153
  interface MetadataClientSaveOptions {
154
154
  /**
@@ -157,8 +157,6 @@ interface MetadataClientSaveOptions {
157
157
  * edits get a 409 instead of overwriting each other.
158
158
  */
159
159
  ifMatch?: string;
160
- /** Optional actor id, sent as `X-Actor` for history attribution. */
161
- actor?: string;
162
160
  /**
163
161
  * Bypass destructive-change protection (Phase 3a). The server returns
164
162
  * `409 destructive_change` with `issues[]` if a write would drop or
@@ -203,14 +201,74 @@ interface MetadataDeleteOptions extends MetadataClientSaveOptions {
203
201
  */
204
202
  state?: 'active' | 'draft';
205
203
  }
206
- /** Layered view of a metadata item — Phase 3a `?layers=true`. */
204
+ /**
205
+ * Which layer an overlay was saved at — `'org'` for a tenant overlay, `'env'`
206
+ * for an environment-level one, `null` exactly when `overlay` is null.
207
+ *
208
+ * DERIVED from `@objectstack/spec`, not restated: the vocabulary lives once, in
209
+ * `GetMetaItemLayeredResponseSchema`'s `overlayScope`
210
+ * (`z.enum(['org', 'env']).nullable()`), and this alias indexes the published
211
+ * response type so a scope the spec adds arrives here with no edit. Restating
212
+ * the union locally is the fork `scripts/check-spec-symbol-derivation.mjs`
213
+ * exists to reject, and the mirror-image mistake — a consumer re-spelling a
214
+ * producer's enum — is what this field carried until objectui#4982.
215
+ *
216
+ * What it carried: `string | null`, under a comment naming the vocabulary as
217
+ * `organization | environment | package`. Not one of those three spellings is a
218
+ * value the producer emits (`metadata-protocol`'s two assignment sites write
219
+ * `'org'` / `'env'`); the schema rejects all three by name, and `package` is
220
+ * not a scope this field has ever had. Because the declared type was `string`
221
+ * the compiler had no opinion, so the wrong comment was the only description of
222
+ * the vocabulary — a planted premise rather than stale prose, and the reason
223
+ * the Studio's layer badge shipped the raw value straight to screen.
224
+ */
225
+ type MetadataOverlayScope = GetMetaItemLayeredResponse['overlayScope'];
226
+ /**
227
+ * ADR-0010 §3.6 — the four-state metadata protection lock
228
+ * (`none` / `no-overlay` / `no-delete` / `full`), read from
229
+ * `GetMetaItemLayeredResponseSchema`'s own `z.enum` rather than spelled out
230
+ * again here (objectui#5024).
231
+ *
232
+ * It used to be written out twice in this file, 42 lines apart: once as
233
+ * {@link MetadataLayered.lock} (optional) and once as
234
+ * {@link MetadataAuditEntry.lockState} (nullable), identical in every other
235
+ * respect and compared by no gate. A fifth state added to one would have left
236
+ * the other compiling — the same "declared N times, diffed by nothing" failure
237
+ * objectui#4972 and objectui#4984 record for other vocabularies.
238
+ *
239
+ * Deriving beats a local alias the two merely share. The producer of these
240
+ * values is the framework, and `packages/spec` already declares the vocabulary,
241
+ * so the copies were restating a schema that existed rather than filling a gap.
242
+ * The card that reported this recorded the opposite — "`@objectstack/spec` 也没
243
+ * 有对应的 `z.enum` 可派生" — and so did the audit panel's neighbouring comment;
244
+ * both predate the enum, which ships in `@objectstack/spec` 17.1.0. This is the
245
+ * same treatment {@link MetadataOverlayScope} above already gets, and it closes
246
+ * the cross-repo half of the drift, not just the in-repo half.
247
+ *
248
+ * ⚠️ This types what this repo may WRITE. It does NOT constrain what a server
249
+ * may SEND: {@link MetadataClient.layered} casts the wire value through
250
+ * unchecked, so every reader must still handle a value outside these four. The
251
+ * lock banner in `ResourceEditPage` is the worked example.
252
+ */
253
+ type MetadataLockState = GetMetaItemLayeredResponse['lock'];
254
+ /**
255
+ * Layered view of a metadata item — the body of
256
+ * `GET /meta/:type/:name/layers` (`GetMetaItemLayeredResponseSchema`).
257
+ *
258
+ * A subset by design: the response also carries `type` / `name`, which the
259
+ * caller already knows because it passed them in, so
260
+ * {@link MetadataClient.layered} does not hand them back.
261
+ */
207
262
  interface MetadataLayered<T = unknown> {
208
263
  /** Code-level (artifact) item; null if the item only exists as an overlay. */
209
264
  code: T | null;
210
265
  /** Org/environment overlay (just the saved delta or full overlay row). */
211
266
  overlay: T | null;
212
- /** Overlay scope (`organization` | `environment` | `package` | null). */
213
- overlayScope: string | null;
267
+ /**
268
+ * Which layer {@link MetadataLayered.overlay} came from, or null when there
269
+ * is no overlay. Spec-derived — see {@link MetadataOverlayScope}.
270
+ */
271
+ overlayScope: MetadataOverlayScope;
214
272
  /** Merged effective view — what the runtime actually sees. */
215
273
  effective: T | null;
216
274
  /**
@@ -222,7 +280,7 @@ interface MetadataLayered<T = unknown> {
222
280
  */
223
281
  _diagnostics?: MetadataDiagnostics;
224
282
  /** 4-state lock: `none` / `no-overlay` / `no-delete` / `full`. */
225
- lock?: 'none' | 'no-overlay' | 'no-delete' | 'full';
283
+ lock?: MetadataLockState;
226
284
  /** Human-readable reason for the lock (tooltip text). */
227
285
  lockReason?: string;
228
286
  /** Which layer set the lock: artifact / package / overlay / env-forced. */
@@ -252,7 +310,10 @@ interface MetadataAuditEntry {
252
310
  id: unknown;
253
311
  /** ISO timestamp when the attempt happened. */
254
312
  occurredAt: string;
255
- /** Who attempted the operation (`x-actor` header or `'system'`). */
313
+ /**
314
+ * Who attempted the operation — the identity the request was authorized
315
+ * as, or `'system'` for internal machine writes (objectstack#7941).
316
+ */
256
317
  actor: string;
257
318
  /** Code path that recorded the row (e.g. `protocol.saveMetaItem`). */
258
319
  source: string | null;
@@ -263,7 +324,7 @@ interface MetadataAuditEntry {
263
324
  /** Machine-readable reason code (`item_locked`, `ok`, …). */
264
325
  code: string;
265
326
  /** Effective lock at the moment of the attempt. */
266
- lockState: 'none' | 'no-overlay' | 'no-delete' | 'full' | null;
327
+ lockState: MetadataLockState | null;
267
328
  /** True when admin forced the write through despite the lock. */
268
329
  lockOverridden: boolean;
269
330
  /** Request-id for trace correlation (if propagated). */
@@ -492,9 +553,35 @@ declare class MetadataClient {
492
553
  };
493
554
  } & Record<string, unknown>>;
494
555
  /**
495
- * Get the 3-state layered view of a metadata item (Phase 3a). Returns
496
- * `code` (the artifact / fallback default), `overlay` (the saved
497
- * customisation, if any), and `effective` (what the runtime sees).
556
+ * Get the 3-state layered view of a metadata item: `code` (the packaged
557
+ * artifact baseline), `overlay` (the tenant customisation row alone) and
558
+ * `effective` (the merged value the runtime sees).
559
+ *
560
+ * Reads **`GET /meta/:type/:name/layers`** — the path the framework declares
561
+ * for this projection, with a response schema of its own
562
+ * (`GetMetaItemLayeredResponseSchema`, objectstack#5882 ruling B). It used to
563
+ * be reached by hanging a `layers` flag on the ordinary item read, which made
564
+ * one route answer two unrelated representations while `packages/spec`
565
+ * declared only one of them. That spelling still answers this same body
566
+ * inside its deprecation window (the response carries RFC 9745
567
+ * `Deprecation: true` and an RFC 8288 `Link: rel="successor-version"` back to
568
+ * this path) and is scheduled for removal upstream, so nothing here may
569
+ * depend on it — the repo-wide ratchet is
570
+ * `scripts/__tests__/layered-read-declared-path-4016.test.ts`.
571
+ *
572
+ * The request is built here rather than delegated to `@objectstack/client`
573
+ * because the SDK expresses no layered read in EITHER spelling: the
574
+ * framework's REST route ledger records this route as `server-only`,
575
+ * "consumed by objectui over plain HTTP", and whether the SDK should express
576
+ * it is an open upstream product call.
577
+ *
578
+ * One behaviour delta rides along with the path, and it is the server's
579
+ * choice rather than ours: the retired flag FELL THROUGH to the plain item
580
+ * read on a backend whose protocol implementation had no layered support,
581
+ * answering the `{ type, name, item }` envelope. A dedicated path refuses to
582
+ * answer a different resource under this one's declared shape, so it returns
583
+ * 501 `NOT_IMPLEMENTED` instead — which surfaces here as a thrown error
584
+ * rather than a view with `code` and `overlay` silently blank.
498
585
  */
499
586
  layered<T = unknown>(type: string, name: string, options?: {
500
587
  packageId?: string;
@@ -530,7 +617,6 @@ declare class MetadataClient {
530
617
  * overlay moved while the draft was sitting.
531
618
  */
532
619
  publish<T = unknown>(type: string, name: string, options?: {
533
- actor?: string;
534
620
  message?: string;
535
621
  }): Promise<T>;
536
622
  /**
@@ -542,7 +628,6 @@ declare class MetadataClient {
542
628
  * when the target version is a delete tombstone.
543
629
  */
544
630
  rollback<T = unknown>(type: string, name: string, toVersion: number, options?: {
545
- actor?: string;
546
631
  message?: string;
547
632
  }): Promise<T>;
548
633
  /**
@@ -747,66 +832,6 @@ interface MetadataCacheStats {
747
832
  hitRate: number;
748
833
  }
749
834
 
750
- /**
751
- * ObjectUI
752
- * Copyright (c) 2024-present ObjectStack Inc.
753
- *
754
- * This source code is licensed under the MIT license found in the
755
- * LICENSE file in the root directory of this source tree.
756
- */
757
- /**
758
- * Contracts module integration for @objectstack/spec v3.0.0
759
- * Provides plugin contract validation and marketplace publishing utilities.
760
- */
761
- interface PluginContract {
762
- /** Plugin name */
763
- name: string;
764
- /** Plugin version */
765
- version: string;
766
- /** Required peer dependencies */
767
- peerDependencies?: Record<string, string>;
768
- /** Exported component types */
769
- exports: PluginExport[];
770
- /** Required permissions */
771
- permissions?: string[];
772
- /** API surface contract */
773
- api?: PluginAPIContract;
774
- }
775
- interface PluginExport {
776
- /** Export name */
777
- name: string;
778
- /** Export type */
779
- type: 'component' | 'hook' | 'utility' | 'provider';
780
- /** Description */
781
- description?: string;
782
- }
783
- interface PluginAPIContract {
784
- /** Consumed data sources */
785
- dataSources?: string[];
786
- /** Required object schemas */
787
- requiredSchemas?: string[];
788
- /** Event subscriptions */
789
- events?: string[];
790
- }
791
- interface ContractValidationResult {
792
- valid: boolean;
793
- errors: ContractValidationError[];
794
- warnings: string[];
795
- }
796
- interface ContractValidationError {
797
- field: string;
798
- message: string;
799
- code: string;
800
- }
801
- /**
802
- * Validate a plugin contract against the ObjectStack spec.
803
- */
804
- declare function validatePluginContract(contract: PluginContract): ContractValidationResult;
805
- /**
806
- * Generate a plugin contract manifest for marketplace publishing.
807
- */
808
- declare function generateContractManifest(contract: PluginContract): Record<string, unknown>;
809
-
810
835
  /**
811
836
  * ObjectStack-backed `UserDataAdapter` factory.
812
837
  *
@@ -886,344 +911,6 @@ interface UserDataAdapter<T> {
886
911
  */
887
912
  declare function createObjectStackUserStateAdapter<T = unknown>(options: ObjectStackUserStateAdapterOptions): UserDataAdapter<T>;
888
913
 
889
- /**
890
- * ObjectUI
891
- * Copyright (c) 2024-present ObjectStack Inc.
892
- *
893
- * This source code is licensed under the MIT license found in the
894
- * LICENSE file in the root directory of this source tree.
895
- */
896
- /**
897
- * Integration module for @objectstack/spec v3.0.0
898
- * Provides third-party service connectors for Slack, email, webhooks.
899
- */
900
- type IntegrationProvider = 'slack' | 'email' | 'webhook' | 'teams' | 'discord';
901
- interface IntegrationConfig {
902
- /** Integration provider type */
903
- provider: IntegrationProvider;
904
- /** Whether this integration is enabled */
905
- enabled: boolean;
906
- /** Provider-specific configuration */
907
- config: Record<string, unknown>;
908
- /** Event triggers */
909
- triggers?: IntegrationTrigger[];
910
- }
911
- interface IntegrationTrigger {
912
- /** Event name (e.g. 'record.created', 'record.updated') */
913
- event: string;
914
- /** Filter condition */
915
- filter?: string;
916
- /** Template for the message/payload */
917
- template?: string;
918
- }
919
- interface SlackIntegrationConfig extends IntegrationConfig {
920
- provider: 'slack';
921
- config: {
922
- webhookUrl: string;
923
- channel?: string;
924
- username?: string;
925
- iconEmoji?: string;
926
- };
927
- }
928
- interface EmailIntegrationConfig extends IntegrationConfig {
929
- provider: 'email';
930
- config: {
931
- smtpHost: string;
932
- smtpPort: number;
933
- secure: boolean;
934
- from: string;
935
- to: string[];
936
- subject?: string;
937
- };
938
- }
939
- interface WebhookIntegrationConfig extends IntegrationConfig {
940
- provider: 'webhook';
941
- config: {
942
- url: string;
943
- method: 'GET' | 'POST' | 'PUT' | 'PATCH';
944
- headers?: Record<string, string>;
945
- retryCount?: number;
946
- retryDelay?: number;
947
- };
948
- }
949
- /**
950
- * Integration service manager.
951
- * Manages third-party service connections and event dispatching.
952
- */
953
- declare class IntegrationManager {
954
- private integrations;
955
- /**
956
- * Register a new integration.
957
- */
958
- register(id: string, config: IntegrationConfig): void;
959
- /**
960
- * Remove an integration.
961
- */
962
- unregister(id: string): void;
963
- /**
964
- * Get all registered integrations.
965
- */
966
- getAll(): Map<string, IntegrationConfig>;
967
- /**
968
- * Get integrations that match a specific event.
969
- */
970
- getForEvent(event: string): IntegrationConfig[];
971
- /**
972
- * Dispatch an event to all matching integrations.
973
- * Returns results for each integration.
974
- */
975
- dispatch(event: string, payload: Record<string, unknown>): Promise<Array<{
976
- id: string;
977
- success: boolean;
978
- error?: string;
979
- }>>;
980
- /**
981
- * Send payload to a specific integration.
982
- */
983
- private send;
984
- }
985
-
986
- /**
987
- * ObjectUI
988
- * Copyright (c) 2024-present ObjectStack Inc.
989
- *
990
- * This source code is licensed under the MIT license found in the
991
- * LICENSE file in the root directory of this source tree.
992
- */
993
- /**
994
- * Security module integration for @objectstack/spec v3.0.0
995
- * Provides advanced security policies: CSP config, audit logging, data masking.
996
- */
997
- /**
998
- * Configuration for {@link SecurityManager} — this adapter's browser-side
999
- * security posture: the CSP header it generates, the in-memory audit log it
1000
- * keeps, and the field-masking rules it applies before a record is rendered.
1001
- *
1002
- * NOT the spec's `SecurityPolicy` (`@objectstack/spec/kernel`), whose name this
1003
- * interface wore until objectui#3160 (objectstack#4115 ledger batch 6). That one
1004
- * is the PACKAGE SUPPLY-CHAIN policy — `{ id, name, autoScan, thresholds,
1005
- * allowedLicenses, prohibitedLicenses, codeSigning, sandbox }` — the rules a
1006
- * marketplace applies when scanning a plugin before install. It shares no key
1007
- * with this one, so there was nothing to derive; the collision is the word
1008
- * "security", not the concept.
1009
- */
1010
- interface SecurityManagerPolicy {
1011
- /** Content Security Policy configuration */
1012
- csp?: CSPConfig;
1013
- /** Audit logging configuration */
1014
- auditLog?: AuditLogConfig;
1015
- /** Data masking rules */
1016
- dataMasking?: DataMaskingConfig;
1017
- }
1018
- interface CSPConfig {
1019
- /** Script sources */
1020
- scriptSrc: string[];
1021
- /** Style sources */
1022
- styleSrc: string[];
1023
- /** Image sources */
1024
- imgSrc: string[];
1025
- /** Connect sources (APIs, WebSockets) */
1026
- connectSrc: string[];
1027
- /** Font sources */
1028
- fontSrc: string[];
1029
- /** Frame sources */
1030
- frameSrc?: string[];
1031
- /** Report URI for violations */
1032
- reportUri?: string;
1033
- }
1034
- interface AuditLogConfig {
1035
- /** Whether audit logging is enabled */
1036
- enabled: boolean;
1037
- /** Events to audit */
1038
- events: AuditEventType[];
1039
- /** Log retention days */
1040
- retentionDays?: number;
1041
- /** External log destination */
1042
- destination?: 'console' | 'server' | 'both';
1043
- }
1044
- type AuditEventType = 'auth.login' | 'auth.logout' | 'auth.failed' | 'data.create' | 'data.read' | 'data.update' | 'data.delete' | 'admin.config' | 'admin.user' | 'admin.role';
1045
- interface DataMaskingConfig {
1046
- /** Fields to mask */
1047
- rules: DataMaskingRule[];
1048
- /** Default masking character */
1049
- maskChar?: string;
1050
- }
1051
- interface DataMaskingRule {
1052
- /** Field name or pattern */
1053
- field: string;
1054
- /** Masking strategy */
1055
- strategy: 'full' | 'partial' | 'hash' | 'redact';
1056
- /** Number of visible characters (for partial masking) */
1057
- visibleChars?: number;
1058
- /** Roles that can see unmasked data */
1059
- exemptRoles?: string[];
1060
- }
1061
- interface AuditLogEntry {
1062
- /** Event timestamp */
1063
- timestamp: string;
1064
- /** Event type */
1065
- event: AuditEventType;
1066
- /** User who triggered the event */
1067
- userId: string;
1068
- /** Target resource */
1069
- resource?: string;
1070
- /** Record ID */
1071
- recordId?: string;
1072
- /** Additional details */
1073
- details?: Record<string, unknown>;
1074
- /** IP address */
1075
- ipAddress?: string;
1076
- }
1077
- /**
1078
- * Security policy manager.
1079
- * Handles CSP generation, audit logging, and data masking.
1080
- */
1081
- declare class SecurityManager {
1082
- private policy;
1083
- private auditLog;
1084
- constructor(policy?: SecurityManagerPolicy);
1085
- /**
1086
- * Generate a CSP header string from the configuration.
1087
- */
1088
- generateCSPHeader(): string;
1089
- /**
1090
- * Record an audit log entry.
1091
- */
1092
- recordAudit(entry: Omit<AuditLogEntry, 'timestamp'>): void;
1093
- /**
1094
- * Get audit log entries.
1095
- */
1096
- getAuditLog(filter?: {
1097
- event?: AuditEventType;
1098
- userId?: string;
1099
- since?: string;
1100
- }): AuditLogEntry[];
1101
- /**
1102
- * Apply data masking to a record.
1103
- */
1104
- maskRecord(record: Record<string, unknown>, userRoles?: string[]): Record<string, unknown>;
1105
- /**
1106
- * Update the security policy.
1107
- */
1108
- updatePolicy(policy: Partial<SecurityManagerPolicy>): void;
1109
- /**
1110
- * Get current security policy.
1111
- */
1112
- getPolicy(): SecurityManagerPolicy;
1113
- }
1114
-
1115
- /**
1116
- * ObjectUI
1117
- * Copyright (c) 2024-present ObjectStack Inc.
1118
- *
1119
- * This source code is licensed under the MIT license found in the
1120
- * LICENSE file in the root directory of this source tree.
1121
- */
1122
- /**
1123
- * Studio module integration for @objectstack/spec v3.0.0
1124
- * Provides visual designer schema improvements: canvas, property editors, theme builder.
1125
- */
1126
- interface StudioCanvasConfig {
1127
- /** Canvas width */
1128
- width: number;
1129
- /** Canvas height */
1130
- height: number;
1131
- /** Background type */
1132
- background: 'grid' | 'dots' | 'lines' | 'none';
1133
- /** Grid size in pixels */
1134
- gridSize: number;
1135
- /** Snap to grid */
1136
- snapToGrid: boolean;
1137
- /** Zoom range */
1138
- zoom: {
1139
- min: number;
1140
- max: number;
1141
- step: number;
1142
- current: number;
1143
- };
1144
- /** Pan offset */
1145
- panOffset: {
1146
- x: number;
1147
- y: number;
1148
- };
1149
- /** Show minimap */
1150
- showMinimap: boolean;
1151
- /** Minimap position */
1152
- minimapPosition: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
1153
- }
1154
- interface StudioPropertyEditor {
1155
- /** Property name */
1156
- name: string;
1157
- /** Display label */
1158
- label: string;
1159
- /** Editor type */
1160
- type: 'text' | 'number' | 'boolean' | 'select' | 'color' | 'code' | 'expression';
1161
- /** Default value */
1162
- defaultValue?: unknown;
1163
- /** Options for select type */
1164
- options?: Array<{
1165
- label: string;
1166
- value: string;
1167
- }>;
1168
- /** Group/category */
1169
- group?: string;
1170
- /** Description */
1171
- description?: string;
1172
- /** Whether the property supports live preview */
1173
- livePreview?: boolean;
1174
- }
1175
- interface StudioThemeBuilderConfig {
1176
- /** Available color palettes */
1177
- palettes: StudioColorPalette[];
1178
- /** Typography presets */
1179
- typography: StudioTypographyPreset[];
1180
- /** Spacing scale */
1181
- spacing: number[];
1182
- /** Border radius options */
1183
- borderRadius: number[];
1184
- /** Shadow presets */
1185
- shadows: StudioShadowPreset[];
1186
- }
1187
- interface StudioColorPalette {
1188
- name: string;
1189
- colors: Record<string, string>;
1190
- }
1191
- interface StudioTypographyPreset {
1192
- name: string;
1193
- fontFamily: string;
1194
- fontSize: Record<string, string>;
1195
- fontWeight: Record<string, number>;
1196
- lineHeight: Record<string, string>;
1197
- }
1198
- interface StudioShadowPreset {
1199
- name: string;
1200
- value: string;
1201
- }
1202
- /**
1203
- * Default canvas configuration for designers.
1204
- */
1205
- declare function createDefaultCanvasConfig(overrides?: Partial<StudioCanvasConfig>): StudioCanvasConfig;
1206
- /**
1207
- * Snap a position to the grid.
1208
- */
1209
- declare function snapToGrid(x: number, y: number, gridSize: number): {
1210
- x: number;
1211
- y: number;
1212
- };
1213
- /**
1214
- * Calculate auto-layout positions for a set of items.
1215
- * Uses a simple grid-based layout algorithm.
1216
- */
1217
- declare function calculateAutoLayout(items: Array<{
1218
- id: string;
1219
- width: number;
1220
- height: number;
1221
- }>, canvasWidth: number, padding?: number, gap?: number): Array<{
1222
- id: string;
1223
- x: number;
1224
- y: number;
1225
- }>;
1226
-
1227
914
  /**
1228
915
  * Map human-readable filter operator names produced by SDUI view configs
1229
916
  * (e.g. `lead.view.ts`) to the canonical operator symbols expected by the
@@ -1416,10 +1103,97 @@ declare class AnalyticsNotInstalledError extends Error {
1416
1103
  readonly code = "ANALYTICS_NOT_INSTALLED";
1417
1104
  /** The surface that was unavailable, for the message a host renders. */
1418
1105
  readonly surface: string;
1419
- constructor(surface: string, detail?: string);
1106
+ /**
1107
+ * The server's own ADR-0112 code — the field this branch was chosen BY, when
1108
+ * a producer named one (`NOT_IMPLEMENTED` from the mounted route with no
1109
+ * service behind it, `ROUTE_NOT_FOUND` from the dispatcher when the route is
1110
+ * not mounted at all). Absent for the no-code residual, where a bare
1111
+ * transport 404/501 is all there was to read.
1112
+ *
1113
+ * Additive (objectui#5663): carried so a reader can audit that the headline
1114
+ * and the quoted `detail` below came off the same answer.
1115
+ */
1116
+ readonly serverCode?: string;
1117
+ constructor(surface: string, detail?: string, serverCode?: string);
1420
1118
  }
1421
1119
  /** True when `error` is an {@link AnalyticsNotInstalledError} (or its wire twin). */
1422
1120
  declare function isAnalyticsNotInstalledError(error: unknown): boolean;
1121
+ /**
1122
+ * Thrown when the dataset route ANSWERED and said the dataset this query named
1123
+ * does not exist in this environment — `404` + ADR-0112 `NOT_FOUND`, the
1124
+ * `body.datasetName` lookup miss in `@objectstack/rest`'s
1125
+ * `registerAnalyticsEndpoints` (`Dataset "<name>" not found.`).
1126
+ *
1127
+ * A SIBLING of {@link AnalyticsNotInstalledError}, never a shade of it
1128
+ * (objectui#5663). Both conditions answer **404** — routes-not-mounted through
1129
+ * the runtime dispatcher's `ROUTE_NOT_FOUND`, an unknown dataset through this
1130
+ * route's own `NOT_FOUND` — so a `res.status === 404` test cannot separate
1131
+ * them, and the mapping that could not tell them apart reported EVERY unknown
1132
+ * dataset as a missing server capability.
1133
+ *
1134
+ * Measured live on a prod tenant: four HotCRM Executive Overview widgets told
1135
+ * the operator to install `@objectstack/service-analytics` and mount
1136
+ * `AnalyticsServicePlugin`, while the analytics service was installed and
1137
+ * answering the whole time. The real condition was an installed
1138
+ * `app.objectstack.hotcrm` pinned at 1.3.0 whose datasets ship in 2.2.2. The
1139
+ * remedy the banner named — install a server plugin — is the opposite corner of
1140
+ * the system from the remedy that works: upgrade the installed app. A wrong
1141
+ * diagnosis is not a smaller version of no diagnosis; it spends the operator's
1142
+ * time in the wrong subsystem.
1143
+ *
1144
+ * The `code` is the ONLY field that separates the two, which is why this branch
1145
+ * reads it and nothing else. See `readAnalyticsErrorEnvelope` below — the
1146
+ * module-private reader that pulls the code out of either declared envelope.
1147
+ */
1148
+ declare class AnalyticsDatasetNotFoundError extends Error {
1149
+ readonly code = "ANALYTICS_DATASET_NOT_FOUND";
1150
+ /**
1151
+ * The dataset this query asked for, taken from the REQUEST rather than parsed
1152
+ * back out of the server's prose: the request is what we know for certain,
1153
+ * and re-reading the name out of a message would make the headline depend on
1154
+ * that message's WORDING — the coupling framework#5367 spent a whole issue
1155
+ * removing from the producing route.
1156
+ */
1157
+ readonly datasetName?: string;
1158
+ /** The server's own ADR-0112 code — the field this branch was chosen BY. */
1159
+ readonly serverCode?: string;
1160
+ /** The server's own message, quoted verbatim in the parenthetical. */
1161
+ readonly serverMessage?: string;
1162
+ constructor(opts: {
1163
+ datasetName?: string;
1164
+ serverCode?: string;
1165
+ serverMessage?: string;
1166
+ });
1167
+ }
1168
+ /**
1169
+ * Thrown when the dataset query was refused before it ran because the session
1170
+ * is not authenticated — `401` + ADR-0112 `UNAUTHENTICATED`, the REST seam's
1171
+ * `ANONYMOUS_DENY_BODY` (`@objectstack/core`'s `security/anonymous-deny.ts`,
1172
+ * written verbatim by `enforceAuth`).
1173
+ *
1174
+ * The THIRD branch, and the one the reported card under-states (objectui#5663):
1175
+ * it recorded a live `POST /api/v1/analytics/dataset/query -> 401
1176
+ * UNAUTHENTICATED` and read it as evidence for the dataset-unknown branch. It
1177
+ * is neither of the other two. An expired session reported as "the deployment
1178
+ * is missing a capability" is the SAME defect wearing a different mask — an
1179
+ * operator sent to install a server plugin because their token lapsed — so the
1180
+ * separation is the point, not a nicety.
1181
+ *
1182
+ * Deliberately not left to the generic `Dataset query failed: 401 …` either:
1183
+ * that string names a transport status where a person needs an action, and
1184
+ * "sign in again" is an action.
1185
+ */
1186
+ declare class AnalyticsUnauthenticatedError extends Error {
1187
+ readonly code = "ANALYTICS_UNAUTHENTICATED";
1188
+ /** The server's own ADR-0112 code — the field this branch was chosen BY. */
1189
+ readonly serverCode?: string;
1190
+ /** The server's own message, quoted verbatim in the parenthetical. */
1191
+ readonly serverMessage?: string;
1192
+ constructor(opts?: {
1193
+ serverCode?: string;
1194
+ serverMessage?: string;
1195
+ });
1196
+ }
1423
1197
  /**
1424
1198
  * Thrown when the server REJECTED the analytics query body (HTTP 400 —
1425
1199
  * `VALIDATION_FAILED` since framework#4010 validates `/analytics/query` at the
@@ -1442,25 +1216,73 @@ declare class AnalyticsQueryRejectedError extends Error {
1442
1216
  *
1443
1217
  * `@objectstack/client`'s fetch wrapper throws on a non-2xx, decorating the
1444
1218
  * error with the semantic `code` string and the numeric `httpStatus` (the
1445
- * ADR-0112 / framework#3842 shape this repo already reads elsewhere). Two
1446
- * outcomes must NOT be conflated:
1447
- *
1448
- * - **`not-installed`** — the deployment has no analytics service. Since
1449
- * framework#3891 retired the degraded in-kernel shim, that is a 404 (the
1450
- * routes aren't mounted at all, framework#4019) or a 501 `NOT_IMPLEMENTED`
1451
- * (REST's dataset route with no service behind it). Degrading to a
1452
- * client-side aggregate over a scoped `find()` is CORRECT here.
1453
- * - **`rejected`** — the server refused OUR body (400 `VALIDATION_FAILED`;
1454
- * framework#4010 validates `/analytics/query` at the entry). Degrading
1455
- * would answer our own contract violation with plausible numbers from a
1456
- * different code path and bury it — the misdirection framework#3878
1457
- * documented. It must be surfaced.
1458
- *
1459
- * Anything else (5xx, network) is `unknown`: degrade, but silently — it is a
1460
- * transient failure, not a deployment that is missing a capability.
1219
+ * ADR-0112 / framework#3842 shape this repo already reads elsewhere).
1220
+ *
1221
+ * ## The `code` is the contract; the status is a transport fact (objectui#5721)
1222
+ *
1223
+ * What stood here tested `status === 404 || status === 501` BEFORE the code
1224
+ * operands that followed them, so the status short-circuited every one of
1225
+ * them: any 404 on this face was `not-installed` whatever code it carried, and
1226
+ * `NOT_IMPLEMENTED` / `ROUTE_NOT_FOUND` were unreachable for the very
1227
+ * conditions they name. Three unrelated conditions answer 404 on this url —
1228
+ *
1229
+ * route absent 404 `ROUTE_NOT_FOUND` (runtime dispatcher, framework#4019)
1230
+ * cube unknown 404 `CUBE_NOT_FOUND` (service-analytics' inference gate)
1231
+ * object unknown 404 `OBJECT_NOT_FOUND` (the `/data` fallback's own answer)
1232
+ *
1233
+ * — so a misspelled cube read as "this deployment has no analytics", told the
1234
+ * operator to install a server plugin, and re-answered the chart from a
1235
+ * different code path. Same defect as objectui#5663, arrived at from the other
1236
+ * direction: the mapping read a field it did not quote. Branch on the `code`;
1237
+ * consult the status only as a residual, when the answer declared no code at
1238
+ * all. Comparisons go through `errorCodeIs`/`errorCodeIsAnyOf` — the pre- and
1239
+ * post-ADR-0112 spellings both have to match (`@object-ui/types`).
1240
+ *
1241
+ * ### Both envelope families reach this function already decorated (MEASURED)
1242
+ *
1243
+ * `/analytics/query` exits through `@objectstack/runtime`'s
1244
+ * `dispatcher-plugin.errorResponseBase`, i.e. the **wrapped** `{ success:
1245
+ * false, error: { code, message } }` shape — not the flat one the dataset
1246
+ * route writes — and its 401 is `enforceAuth`'s **flat** `ANONYMOUS_DENY_BODY`.
1247
+ * The client's fetch wrapper flattens BOTH before throwing:
1248
+ * `errorBody?.code ?? errorBody?.error?.code` → `error.code`, plus
1249
+ * `error.httpStatus = res.status`. So no envelope reading belongs here; unlike
1250
+ * `queryDataset` (which owns its `fetch` and must read the body itself), this
1251
+ * function is handed the already-flattened error and reads one field.
1252
+ *
1253
+ * ## Each branch names ONE of the three outcomes the caller can take
1254
+ *
1255
+ * - **`not-installed`** — *degrade LOUDLY*. The deployment has no analytics
1256
+ * service: 501 `NOT_IMPLEMENTED` (route mounted, nothing behind it) or 404
1257
+ * `ROUTE_NOT_FOUND` (framework#4019 stops mounting the routes at all).
1258
+ * A client-side aggregate over a scoped `find()` answers the chart
1259
+ * correctly, and the operator is told once that the semantic layer is off.
1260
+ * - **`rejected`** — *THROW*. The server refused OUR body (400
1261
+ * `VALIDATION_FAILED`; framework#4010 validates `/analytics/query` at the
1262
+ * entry). Degrading would answer our own contract violation with plausible
1263
+ * numbers from a different code path and bury it — the misdirection
1264
+ * framework#3878 documented.
1265
+ * - **`unauthenticated`** — *THROW*. 401 `UNAUTHENTICATED`: the request was
1266
+ * refused before it ran, so it is evidence about the SESSION and none at
1267
+ * all about the capability. Degrading is not merely misleading here, it is
1268
+ * futile: the fallback's `find()` carries the same lapsed token and is
1269
+ * refused the same way, so the chart cannot be answered either. "Sign in
1270
+ * again" is an action; "install @objectstack/service-analytics" is not.
1271
+ * - **`cube-not-found`** — *THROW*. 404 `CUBE_NOT_FOUND`
1272
+ * (`analytics-service.assertInferableCube`, framework#3867): analytics IS
1273
+ * installed and answering — the NAME does not exist. That gate throws only
1274
+ * when the name is neither a registered cube nor a registered object, and
1275
+ * the fallback asks `/data/<the same name>`, which objectql's
1276
+ * `assertObjectRegistered` (framework#3770) answers 404 `OBJECT_NOT_FOUND`.
1277
+ * So degrading cannot produce numbers; it can only swap a message that
1278
+ * names the fix ("Define a Cube in your stack, or check the object name")
1279
+ * for a distant one, behind a warning that instructs the wrong repair.
1280
+ * - **`unknown`** — *degrade SILENTLY*. Anything else (5xx, network, a code
1281
+ * this consumer does not know): a transient failure, not a deployment
1282
+ * missing a capability and not a request that named something absent.
1461
1283
  */
1462
1284
  declare function classifyAnalyticsFailure(error: unknown): {
1463
- kind: 'not-installed' | 'rejected' | 'unknown';
1285
+ kind: 'not-installed' | 'rejected' | 'unauthenticated' | 'cube-not-found' | 'unknown';
1464
1286
  code?: string;
1465
1287
  message?: string;
1466
1288
  };
@@ -1630,6 +1452,85 @@ type WriteWarningListener = (event: WriteWarningEvent) => void;
1630
1452
  * prevent. Identity only — it names no cache key.
1631
1453
  */
1632
1454
  declare function viewItemObjectName(item: any): string | undefined;
1455
+ /**
1456
+ * The keys a personalization overlay row legitimately OWNS (objectui#5233).
1457
+ *
1458
+ * One per `persistViewPatch` call site in app-shell's `ObjectView` — the ONLY
1459
+ * production writer of these rows — read off the tree rather than recalled:
1460
+ * `rowHeight` (the density toggle, spec-canonical since #2890), `sort`,
1461
+ * `hiddenFields`, `columnState` and `inlineEdit`. Nothing else in such a row
1462
+ * is an opinion the user expressed; anything else it carries is a COPY of the
1463
+ * source view as it stood at write time, because `persistViewPatch` USED TO
1464
+ * send `{ ...baseViewDef, ...patch }` and this adapter persists what it is
1465
+ * given.
1466
+ *
1467
+ * That copy was the defect the maintainer ruled on (objectstack#7494, comment
1468
+ * 5261754173): an overlay written by a mere column drag froze the view's
1469
+ * effective `filter` — and its `columns`, `label`, `type`, `isDefault` … — as
1470
+ * of that moment, and because the display merge is `{ ...source, ...override }`
1471
+ * the frozen copy SHADOWED the source view forever. An admin then edited the
1472
+ * view's filter and every user who once resized a column kept the old one,
1473
+ * with nothing anywhere reporting it.
1474
+ *
1475
+ * Both halves of that ruling have now landed, and this adapter's behaviour is
1476
+ * unchanged by either — it still persists what it is given:
1477
+ *
1478
+ * - **read** (PR #5272, {@link narrowPersonalizationOverlay}): the consumer
1479
+ * that MERGES an overlay over a source view contributes only these keys, so
1480
+ * every already-stored fat row stops shadowing its source.
1481
+ * - **write** (objectui#5233, `buildPersistedViewBody` in app-shell's
1482
+ * `ObjectView`, unblocked by `columnState`'s admission to the view-metadata
1483
+ * surface as a runtime-only overlay key — objectstack#9933, released in
1484
+ * `@objectstack/spec` 17.1.0): a *system view's* overlay is now written as
1485
+ * the patch alone, so no new row freezes anything, and because the write is
1486
+ * a whole-document PUT the next toggle also strips an old fat row. A *saved
1487
+ * view's* own row is deliberately still written whole — for it the body IS
1488
+ * the view, not a copy of one.
1489
+ *
1490
+ * A fat row is therefore a legacy shape, not a shape this product still
1491
+ * produces; the list below is still what a reader is allowed to trust from one.
1492
+ *
1493
+ * ⛔ Do not grow this list to make some other key "stick" through an overlay.
1494
+ * A key that belongs to the view belongs in the view; the overlay is a patch,
1495
+ * and a patch that carries the whole document is what this list exists to
1496
+ * stop. Adding a sixth entry is only correct alongside a sixth
1497
+ * `persistViewPatch` call site — {@link narrowPersonalizationOverlay} is what
1498
+ * a reader checks that against.
1499
+ */
1500
+ declare const VIEW_OVERLAY_OWNED_KEYS: readonly ["rowHeight", "sort", "hiddenFields", "columnState", "inlineEdit"];
1501
+ /**
1502
+ * Reduce a personalization overlay row to the keys it owns — the read-side
1503
+ * half of objectui#5233, and the half that reaches rows ALREADY STORED.
1504
+ *
1505
+ * Rows written before this shipped carry the whole source view (see
1506
+ * {@link VIEW_OVERLAY_OWNED_KEYS}). Of the three dispositions the issue names
1507
+ * for them — strip on next write, migrate, tolerate on read — this is the
1508
+ * third, chosen deliberately and stated here rather than left implicit,
1509
+ * because it is the only one that is already true for every existing row the
1510
+ * moment it ships: strip-on-next-write heals a row only when its user happens
1511
+ * to touch that view again (and leaves the frozen filter live until then),
1512
+ * and a migration needs a runner this product does not have for `sys_metadata`
1513
+ * rows an operator may not even know exist. What the issue forbids is SILENT
1514
+ * tolerance; this is the explicit, pinned kind (`viewOverlayPatchOnly.test.ts`
1515
+ * in this package, `ObjectView.overlayPatchOnly.test.tsx` in app-shell).
1516
+ *
1517
+ * Applied by the consumer that MERGES an override over a source view
1518
+ * (app-shell's `sanitizeViewOverride`, the one seam both of
1519
+ * `loadViewOverrides`' read branches pass through), NOT by
1520
+ * {@link ObjectStackAdapter.listViewOverrides} / {@link ObjectStackAdapter.getView}:
1521
+ * those two answer with the stored DOCUMENT, their equality is itself pinned
1522
+ * ("same key space, same document" — `listViewOverrides.test.ts`), and
1523
+ * `InterfaceListPage` hydrates a hollow view out of that document. Narrowing
1524
+ * belongs where a row is read AS A PATCH, not where it is read as a row.
1525
+ *
1526
+ * Non-overlay rows — a genuine saved view's own body, which the same batch
1527
+ * read enumerates — are returned by REFERENCE, untouched: for those the row
1528
+ * IS the view, and every key on it is an opinion its author expressed.
1529
+ * Classification is {@link isPersonalizationOverlayRow}, the same predicate
1530
+ * {@link ObjectStackAdapter.listViews} excludes rows by, so a row cannot be a
1531
+ * saved view for one reader and an overlay for the other.
1532
+ */
1533
+ declare function narrowPersonalizationOverlay<T>(row: T): T;
1633
1534
 
1634
1535
  /**
1635
1536
  * ObjectStack Data Source Adapter
@@ -2213,23 +2114,54 @@ declare class ObjectStackAdapter<T = unknown> implements DataSource<T> {
2213
2114
  */
2214
2115
  getView(objectName: string, viewId: string): Promise<unknown | null>;
2215
2116
  /**
2216
- * Persist a view definition for an object.
2217
- *
2218
- * Symmetric counterpart to {@link getView}: writes the view to the
2219
- * server metadata store via `client.meta.saveItem`, then invalidates
2220
- * the matching cache entry so the next {@link getView} reflects the
2221
- * new payload. Returns the persisted item when the server echoes it,
2222
- * otherwise undefined.
2223
- *
2224
- * Used by ObjectView for "live" toolbar persistence (density,
2225
- * column widths, sort, etc.) and by the View Config Panel for
2226
- * explicit saves.
2117
+ * Persist a toolbar-driven view config patch — density, column widths,
2118
+ * sort, hidden columns, inline edit. Symmetric counterpart to
2119
+ * {@link getView}: writes the row to the server metadata store via
2120
+ * `client.meta.saveItem`, then invalidates the matching cache entry so the
2121
+ * next {@link getView} reflects the new payload. Returns the persisted item
2122
+ * when the server echoes it, otherwise undefined.
2123
+ *
2124
+ * Called from exactly ONE production site — `ObjectView`'s
2125
+ * `persistViewPatch`, for the toolbar toggle — but that ONE call site
2126
+ * fires for BOTH kinds of active tab: a code-defined **system** view (no
2127
+ * row of its own yet) and a genuinely user-created **saved** view (already
2128
+ * has a row — the toggle is editing ITS OWN definition, not laying an
2129
+ * overlay on top of it). Which one a given call means is NOT re-derived
2130
+ * here from the write's shape (objectui#4227's own lesson: shape inference
2131
+ * on this namespace is exactly what let a system view masquerade as
2132
+ * saved) — the caller already knows, via the same `isSavedViewId`
2133
+ * classification that gates the switcher's readonly flag and its five
2134
+ * mutating handlers, and passes it as {@link opts.isSavedView}.
2135
+ *
2136
+ * - `isSavedView` false/omitted (system-view target, the common case and
2137
+ * the default for backward compatibility): stamps
2138
+ * {@link VIEW_OVERLAY_MARKER} so {@link listViews} excludes the row —
2139
+ * the original objectui#4227 fix.
2140
+ * - `isSavedView` true: the marker is withheld. Stamping it here would
2141
+ * flag the saved view's OWN row as a personalization overlay, and
2142
+ * `listViews()` would exclude it on the very next read — the user's own
2143
+ * view would vanish from the switcher the moment they toggled its
2144
+ * density (objectui#4227 follow-up, PM review on PR #4713, measured:
2145
+ * `persistViewPatch` has no gate on which kind of tab is active, and
2146
+ * this method writes to the exact same `(type='view', name=viewId)` key
2147
+ * {@link createView}/the ADR-0034 `viewEnvelope` seam already used for
2148
+ * that view, so the write is an upsert onto the saved view's row, not a
2149
+ * new one).
2150
+ *
2151
+ * Per objectstack#7494's ruling, the overlay this writes (system-view
2152
+ * case) is ORG-WIDE shared view settings, not a per-user preference — a
2153
+ * true per-user scope is a parked v18 direction on the platform side.
2227
2154
  *
2228
2155
  * @param objectName - Object name (e.g. 'lead')
2229
2156
  * @param viewId - View identifier (e.g. 'all_leads')
2230
2157
  * @param config - Full view definition to persist
2158
+ * @param opts.isSavedView - Whether `viewId` already names a saved view
2159
+ * (vs. a system view being personalized for the first time). Omit /
2160
+ * `false` for the default overlay-marking behavior.
2231
2161
  */
2232
- updateViewConfig(objectName: string, viewId: string, config: Record<string, any>): Promise<Record<string, any> | void>;
2162
+ updateViewConfig(objectName: string, viewId: string, config: Record<string, any>, opts?: {
2163
+ isSavedView?: boolean;
2164
+ }): Promise<Record<string, any> | void>;
2233
2165
  /**
2234
2166
  * List user-created views for a given object via the metadata overlay
2235
2167
  * API (ADR-0005). Replaces the legacy `find('sys_view', {...})` path
@@ -2403,8 +2335,13 @@ declare class ObjectStackAdapter<T = unknown> implements DataSource<T> {
2403
2335
  * Ask the by-name meta app route WHY an app is not in this session's app list
2404
2336
  * (objectui#4252).
2405
2337
  *
2406
- * `GET /api/v1/meta/apps` is filtered per session server-side
2407
- * (`filterAppForUser`), so an app withheld by `requiredPermissions` and an app
2338
+ * The app LIST is the generic metadata list route `GET /api/v1/meta/:type`,
2339
+ * requested with the singular type segment `app` — the same address this
2340
+ * method appends a name to below, and the one `MetadataProvider` reads its
2341
+ * items from. The server filters that list per session in `filterAppForUser`
2342
+ * (`packages/rest/src/rest-server.ts`, applied inside the `:type` list handler
2343
+ * once the type segment resolves to `app`), so an app withheld by
2344
+ * `requiredPermissions` and an app
2408
2345
  * that does not exist are byte-identical there: both are simply absent. A
2409
2346
  * console reading only that list has one fact and two conditions, and it
2410
2347
  * renders its copy for the wrong one — "it may still be publishing" over a
@@ -2672,4 +2609,4 @@ declare function createObjectStackAdapter<T = unknown>(config: {
2672
2609
  reconnectDelay?: number;
2673
2610
  }): DataSource<T>;
2674
2611
 
2675
- export { API_ACCESS_DENIED_CODES, APP_PERMISSION_DENIED_CODE, AnalyticsNotInstalledError, AnalyticsQueryRejectedError, type AppAccessVerdict, type AuditEventType, type AuditLogConfig, type AuditLogEntry, AuthenticationError, type BatchProgressEvent, type BatchProgressListener, BulkOperationError, type CSPConfig, ConcurrentUpdateError, ConnectionError, type ConnectionState, type ConnectionStateEvent, type ConnectionStateListener, type ContractValidationError, type ContractValidationResult, DataApiValidationError, type DataMaskingConfig, type DataMaskingRule, type EmailIntegrationConfig, FILTER_OPERATOR_ALIASES, type IntegrationConfig, IntegrationManager, type IntegrationProvider, type IntegrationTrigger, MalformedFilterError, type MetadataAuditEntry, type MetadataAuditResponse, type MetadataCacheStats, MetadataClient, type MetadataClientConfig, type MetadataClientSaveOptions, type MetadataDeleteOptions, type MetadataDiagnostics, type MetadataDiagnosticsEntry, type MetadataDiagnosticsOptions, type MetadataDiagnosticsSummary, type MetadataDraftHeader, type MetadataError, type MetadataGetOptions, type MetadataHistoryOptions, type MetadataLayered, type MetadataListOptions, MetadataNotFoundError, type MetadataReference, type MetadataSaveAdvisoryEvent, type MetadataSaveAdvisoryListener, type MetadataValidationIssue, ObjectStackAdapter, ObjectStackError, type ObjectStackUserStateAdapterOptions, type PluginAPIContract, type PluginContract, type PluginExport, SecurityManager, type SecurityManagerPolicy, type SlackIntegrationConfig, type StudioCanvasConfig, type StudioColorPalette, type StudioPropertyEditor, type StudioShadowPreset, type StudioThemeBuilderConfig, type StudioTypographyPreset, type UserDataAdapter, type WebhookIntegrationConfig, type WriteWarningEvent, type WriteWarningListener, calculateAutoLayout, classifyAnalyticsFailure, clearSharedDiscoveryCache, createDefaultCanvasConfig, createErrorFromResponse, createObjectStackAdapter, createObjectStackUserStateAdapter, createQuietHttpLogger, formatHttpFailureMessage, generateContractManifest, getSharedDiscovery, is404Error, isAnalyticsNotInstalledError, isApiAccessDeniedError, isAppPermissionDeniedError, isConcurrentUpdateError, isErrorType, isMalformedFilterError, isObjectStackError, normaliseClientError, readSaveAdvisories, readTransactionalBatchCapability, serializeOrderBy, snapToGrid, validatePluginContract, viewItemObjectName };
2612
+ export { API_ACCESS_DENIED_CODES, APP_PERMISSION_DENIED_CODE, AnalyticsDatasetNotFoundError, AnalyticsNotInstalledError, AnalyticsQueryRejectedError, AnalyticsUnauthenticatedError, type AppAccessVerdict, AuthenticationError, type BatchProgressEvent, type BatchProgressListener, BulkOperationError, ConcurrentUpdateError, ConnectionError, type ConnectionState, type ConnectionStateEvent, type ConnectionStateListener, DataApiValidationError, FILTER_OPERATOR_ALIASES, MalformedFilterError, type MetadataAuditEntry, type MetadataAuditResponse, type MetadataCacheStats, MetadataClient, type MetadataClientConfig, type MetadataClientSaveOptions, type MetadataDeleteOptions, type MetadataDiagnostics, type MetadataDiagnosticsEntry, type MetadataDiagnosticsOptions, type MetadataDiagnosticsSummary, type MetadataDraftHeader, type MetadataError, type MetadataGetOptions, type MetadataHistoryOptions, type MetadataLayered, type MetadataListOptions, type MetadataLockState, MetadataNotFoundError, type MetadataOverlayScope, type MetadataReference, type MetadataSaveAdvisoryEvent, type MetadataSaveAdvisoryListener, type MetadataValidationIssue, ObjectStackAdapter, ObjectStackError, type ObjectStackUserStateAdapterOptions, type UserDataAdapter, VIEW_OVERLAY_OWNED_KEYS, type WriteWarningEvent, type WriteWarningListener, classifyAnalyticsFailure, clearSharedDiscoveryCache, createErrorFromResponse, createObjectStackAdapter, createObjectStackUserStateAdapter, createQuietHttpLogger, formatHttpFailureMessage, getSharedDiscovery, is404Error, isAnalyticsNotInstalledError, isApiAccessDeniedError, isAppPermissionDeniedError, isConcurrentUpdateError, isErrorType, isMalformedFilterError, isObjectStackError, narrowPersonalizationOverlay, normaliseClientError, readSaveAdvisories, readTransactionalBatchCapability, serializeOrderBy, viewItemObjectName };