@object-ui/data-objectstack 17.3.0 → 17.5.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/CHANGELOG.md +669 -0
- package/README.md +3 -11
- package/dist/index.cjs +1258 -767
- package/dist/index.d.cts +1367 -906
- package/dist/index.d.ts +1367 -906
- package/dist/index.js +1210 -726
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +5 -5
- package/src/apiAccessDenied.test.ts +233 -0
- package/src/appAccessProbe.test.ts +147 -0
- package/src/cloud-surface-retired-4152.pin.test.ts +171 -0
- package/src/deleteView.homes.test.ts +389 -0
- package/src/deleteViewContract.types.test.ts +246 -0
- package/src/filter-operator-ast-parity.test.ts +75 -11
- package/src/httpFailureLogging.test.ts +109 -0
- package/src/index.ts +879 -95
- package/src/listViewOverrides.test.ts +241 -0
- package/src/metadata-client.get-envelope.test.ts +171 -0
- package/src/metadata-client.saveAdvisories.test.ts +226 -0
- package/src/metadata-client.ts +208 -11
- package/src/onMutation.test.ts +29 -2
- package/src/onSaveAdvisory.test.ts +311 -0
- package/src/queryDataset.test.ts +411 -0
- package/src/spec-symbol-batch6.test.ts +21 -1
- package/src/updateView.draft.test.ts +242 -0
- package/src/v3-compat.test.ts +10 -21
- package/src/viewCacheInvalidation.pin.test.ts +260 -0
- package/src/viewInvalidationSeam.pin.test.ts +162 -0
- package/src/cloud.ts +0 -109
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
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 {
|
|
5
|
-
export {
|
|
4
|
+
import { RuntimeAuthoringIssue } from '@objectstack/spec/api';
|
|
5
|
+
export { RuntimeAuthoringIssue } from '@objectstack/spec/api';
|
|
6
|
+
import { DatasetSelection, AnalyticsResult } from '@objectstack/spec/contracts';
|
|
7
|
+
import { DataSource, QueryParams, QueryResult, GlobalSearchResult, DataSourceMutationEvent, BatchTransactionOperation, ImportRequestOptions, ImportRecordsResult, CreateImportJobResult, ImportJobProgressInfo, ImportJobResultsInfo, ListImportJobsOptions, ImportJobSummaryInfo, ImportJobUndoResult, ExportDownloadRequest, DeleteViewResult, FileUploadResult } from '@object-ui/types';
|
|
8
|
+
export { DeleteViewResult, FileUploadResult, ViewHomeDeleteOutcome } from '@object-ui/types';
|
|
9
|
+
import { DatasetDrillRange } from '@object-ui/core';
|
|
6
10
|
|
|
7
11
|
/**
|
|
8
12
|
* ObjectUI
|
|
@@ -12,975 +16,971 @@ export { FileUploadResult } from '@object-ui/types';
|
|
|
12
16
|
* LICENSE file in the root directory of this source tree.
|
|
13
17
|
*/
|
|
14
18
|
/**
|
|
15
|
-
*
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
details: Record<string, unknown> | undefined;
|
|
39
|
-
stack: string | undefined;
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Error thrown when requested metadata/schema is not found
|
|
19
|
+
* MetadataClient
|
|
20
|
+
*
|
|
21
|
+
* Thin, framework-agnostic HTTP client for the ObjectStack metadata
|
|
22
|
+
* API (`/api/v1/meta/*`). Used by the platform Setup app surfaces in
|
|
23
|
+
* `@object-ui/plugin-designer` (Object Manager, Field Designer, etc.)
|
|
24
|
+
* so they can read and write protocol metadata without depending on
|
|
25
|
+
* `ObjectStackAdapter` (which is a generic data-source binding and
|
|
26
|
+
* carries a lot of view/dashboard-specific behaviour we don't need
|
|
27
|
+
* here).
|
|
28
|
+
*
|
|
29
|
+
* Endpoints (see `packages/rest/src/rest-server.ts`):
|
|
30
|
+
* GET /api/v1/meta — list all metadata types
|
|
31
|
+
* GET /api/v1/meta/:type — list items of a type
|
|
32
|
+
* GET /api/v1/meta/:type/:name — get one item (returns the
|
|
33
|
+
* unwrapped item content)
|
|
34
|
+
* PUT /api/v1/meta/:type/:name — save (overlay) one item
|
|
35
|
+
* honours `If-Match` for OCC
|
|
36
|
+
* DELETE /api/v1/meta/:type/:name — reset overlay to artifact
|
|
37
|
+
* GET /api/v1/meta/:type/:name/history — durable change log
|
|
38
|
+
*
|
|
39
|
+
* The client deliberately keeps the response shape opaque (typed as
|
|
40
|
+
* `unknown`/generic `T`) so callers explicitly narrow per-metadata-
|
|
41
|
+
* type, mirroring the framework's "single Zod source per type" rule.
|
|
44
42
|
*/
|
|
45
|
-
|
|
46
|
-
constructor(objectName: string, details?: Record<string, unknown>);
|
|
47
|
-
}
|
|
43
|
+
|
|
48
44
|
/**
|
|
49
|
-
*
|
|
45
|
+
* Emitted after a {@link MetadataClient.save} whose response carried a
|
|
46
|
+
* non-empty `advisories` array. The save SUCCEEDED — the row persisted and the
|
|
47
|
+
* server returned 200 — so this is advisory, never a failure.
|
|
48
|
+
*
|
|
49
|
+
* Deliberately the same shape of seam as `ObjectStackAdapter.onWriteWarning`
|
|
50
|
+
* (#3431/#3455): a successful write whose response carries something the author
|
|
51
|
+
* needs to be told, surfaced to the shell as an event so the data layer never
|
|
52
|
+
* imports a toaster. The difference is only which door produced it — that one
|
|
53
|
+
* is record CRUD, this one is the metadata save door.
|
|
50
54
|
*/
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
error: unknown;
|
|
57
|
-
}>;
|
|
58
|
-
/**
|
|
59
|
-
* Create a new BulkOperationError
|
|
60
|
-
*
|
|
61
|
-
* @param operation - The bulk operation that failed (create, update, delete)
|
|
62
|
-
* @param successCount - Number of successful operations
|
|
63
|
-
* @param failureCount - Number of failed operations
|
|
64
|
-
* @param errors - Array of individual errors
|
|
65
|
-
* @param details - Additional error details
|
|
66
|
-
*/
|
|
67
|
-
constructor(operation: 'create' | 'update' | 'delete', successCount: number, failureCount: number, errors: Array<{
|
|
68
|
-
index: number;
|
|
69
|
-
error: unknown;
|
|
70
|
-
}>, details?: Record<string, unknown>);
|
|
55
|
+
interface MetadataSaveAdvisoryEvent {
|
|
56
|
+
/** Metadata type saved (e.g. `'flow'`). */
|
|
57
|
+
type: string;
|
|
58
|
+
/** Item name saved. */
|
|
59
|
+
name: string;
|
|
71
60
|
/**
|
|
72
|
-
*
|
|
61
|
+
* The save mode the call used. `'draft'` writes are **never gated** — the
|
|
62
|
+
* framework returns at `runtime-authoring-gate.ts`'s D1 early-return
|
|
63
|
+
* (`if (args.state !== 'active') return null`), so a draft save produces no
|
|
64
|
+
* findings at all and this event never fires for one. Carried anyway so a
|
|
65
|
+
* consumer reading the event can tell which door it came through.
|
|
73
66
|
*/
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
successful: number;
|
|
78
|
-
failed: number;
|
|
79
|
-
failureRate: number;
|
|
80
|
-
errors: {
|
|
81
|
-
index: number;
|
|
82
|
-
error: unknown;
|
|
83
|
-
}[];
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Error thrown when connection to ObjectStack server fails
|
|
88
|
-
*/
|
|
89
|
-
declare class ConnectionError extends ObjectStackError {
|
|
90
|
-
url?: string | undefined;
|
|
91
|
-
constructor(message: string, url?: string | undefined, details?: Record<string, unknown>, statusCode?: number);
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* Error thrown when authentication fails
|
|
95
|
-
*/
|
|
96
|
-
declare class AuthenticationError extends ObjectStackError {
|
|
97
|
-
constructor(message?: string, details?: Record<string, unknown>, statusCode?: number);
|
|
67
|
+
mode: 'draft' | 'publish';
|
|
68
|
+
/** The findings. Never empty — the event is not emitted otherwise. */
|
|
69
|
+
advisories: RuntimeAuthoringIssue[];
|
|
98
70
|
}
|
|
71
|
+
/** Event listener type for save-advisory events. */
|
|
72
|
+
type MetadataSaveAdvisoryListener = (event: MetadataSaveAdvisoryEvent) => void;
|
|
99
73
|
/**
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
* NOT the spec's `ValidationError` (`@objectstack/spec/kernel`), whose name this
|
|
103
|
-
* class wore until objectui#3160 (objectstack#4115 ledger batch 6). That one is
|
|
104
|
-
* a plain DATA SHAPE — `{ field, message, code? }`, one entry in a plugin
|
|
105
|
-
* manifest's validation report — and `@object-ui/types` re-exports it under that
|
|
106
|
-
* name. This is a runtime `Error` subclass carrying an HTTP status plus a list
|
|
107
|
-
* of such entries, so the two are not even the same KIND of thing.
|
|
74
|
+
* Read the `advisories` array off a save response, defensively.
|
|
108
75
|
*
|
|
109
|
-
* The
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
* the
|
|
76
|
+
* The server omits the key entirely on a clean save, so `undefined` is the
|
|
77
|
+
* common case and means "nothing to say". Anything that is not an array of
|
|
78
|
+
* objects carrying the six required keys is dropped rather than rendered: a
|
|
79
|
+
* half-shaped finding would print blanks at the author, and this channel must
|
|
80
|
+
* never turn a successful save into noise.
|
|
113
81
|
*/
|
|
114
|
-
declare
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
message: string;
|
|
119
|
-
}> | undefined;
|
|
82
|
+
declare function readSaveAdvisories(body: unknown): RuntimeAuthoringIssue[];
|
|
83
|
+
interface MetadataClientConfig {
|
|
84
|
+
/** Base URL of the ObjectStack server (no trailing slash needed). */
|
|
85
|
+
baseUrl: string;
|
|
120
86
|
/**
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
* @param validationErrors - Array of validation error details
|
|
126
|
-
* @param details - Additional error details
|
|
87
|
+
* Optional environment ID to scope reads/writes to a tenant. When
|
|
88
|
+
* provided, requests use the scoped path
|
|
89
|
+
* `/api/v1/environments/:environmentId/meta/...` and overlays are
|
|
90
|
+
* persisted in the environment's own metadata store.
|
|
127
91
|
*/
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
92
|
+
environmentId?: string;
|
|
93
|
+
/** Optional custom fetch implementation (defaults to `globalThis.fetch`). */
|
|
94
|
+
fetch?: typeof fetch;
|
|
95
|
+
/** Additional headers (e.g. auth) included on every request. */
|
|
96
|
+
headers?: Record<string, string>;
|
|
132
97
|
/**
|
|
133
|
-
*
|
|
98
|
+
* ADR-0037 Live Canvas — render the world **as-if-published**. When true,
|
|
99
|
+
* `list()` and `get()` append `?preview=draft`, which the framework
|
|
100
|
+
* dispatcher maps to `getMetaItems/getMetaItem({ previewDrafts: true })`:
|
|
101
|
+
* pending ADR-0033 drafts overlay the active registry (draft wins by name,
|
|
102
|
+
* draft-only items surface). Reads only — writes are unaffected, and
|
|
103
|
+
* nothing the preview shows is live until Publish.
|
|
134
104
|
*/
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
105
|
+
previewDrafts?: boolean;
|
|
106
|
+
/**
|
|
107
|
+
* Called after a {@link MetadataClient.save} whose 2xx response carried a
|
|
108
|
+
* non-empty `advisories` array (objectstack#7435). The save already
|
|
109
|
+
* succeeded; this is how the shell learns there is something to tell the
|
|
110
|
+
* author instead of the findings being discarded client-side.
|
|
111
|
+
*
|
|
112
|
+
* Set on the CONFIG rather than exposed as a `subscribe()` method on purpose:
|
|
113
|
+
* console metadata clients are minted per-component by `useMetadataClient`,
|
|
114
|
+
* so there is no long-lived instance to subscribe to — but every one of them
|
|
115
|
+
* is built by the single `createConsoleMetadataClient` factory, which is
|
|
116
|
+
* where this gets wired exactly once for every save call site in the app.
|
|
117
|
+
*
|
|
118
|
+
* A throw from this callback is swallowed — an advisory channel must never
|
|
119
|
+
* be able to fail a save that the server already committed.
|
|
120
|
+
*/
|
|
121
|
+
onSaveAdvisory?: MetadataSaveAdvisoryListener;
|
|
139
122
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
* @param response - Response object or error from fetch/axios
|
|
144
|
-
* @param context - Additional context for debugging
|
|
145
|
-
* @returns Appropriate error instance
|
|
146
|
-
*/
|
|
147
|
-
declare function createErrorFromResponse(response: Record<string, unknown>, context?: string): ObjectStackError;
|
|
148
|
-
/**
|
|
149
|
-
* Type guard to check if an error is an ObjectStackError
|
|
150
|
-
*/
|
|
151
|
-
declare function isObjectStackError(error: unknown): error is ObjectStackError;
|
|
152
|
-
/**
|
|
153
|
-
* Type guard to check if an error is a specific ObjectStack error type
|
|
154
|
-
*/
|
|
155
|
-
declare function isErrorType<T extends ObjectStackError>(error: unknown, errorClass: new (...args: any[]) => T): error is T;
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* ObjectUI
|
|
159
|
-
* Copyright (c) 2024-present ObjectStack Inc.
|
|
160
|
-
*
|
|
161
|
-
* This source code is licensed under the MIT license found in the
|
|
162
|
-
* LICENSE file in the root directory of this source tree.
|
|
163
|
-
*/
|
|
164
|
-
/**
|
|
165
|
-
* Statistics reported by {@link MetadataCache.getStats} for monitoring.
|
|
166
|
-
*
|
|
167
|
-
* NOT the spec's `CacheStats` (`@objectstack/spec/contracts`), whose name this
|
|
168
|
-
* interface wore until objectui#3160 (objectstack#4115 ledger batch 6). That one
|
|
169
|
-
* describes the platform's `ICacheService` — a server-side KV cache measured by
|
|
170
|
-
* `keyCount` and `memoryUsage`. This one describes the browser-side LRU in front
|
|
171
|
-
* of `/api/v1/meta/*`: it is bounded (`size`/`maxSize`), it evicts, it coalesces
|
|
172
|
-
* concurrent fetches onto one in-flight promise, and it reports a `hitRate`.
|
|
173
|
-
* Neither type has a key the other has, so this is a name collision, not a
|
|
174
|
-
* dialect — deriving would have replaced every field.
|
|
175
|
-
*/
|
|
176
|
-
interface MetadataCacheStats {
|
|
177
|
-
size: number;
|
|
178
|
-
maxSize: number;
|
|
179
|
-
hits: number;
|
|
180
|
-
misses: number;
|
|
181
|
-
evictions: number;
|
|
182
|
-
/** Number of concurrent fetches that were coalesced onto an in-flight request. */
|
|
183
|
-
coalesced: number;
|
|
184
|
-
hitRate: number;
|
|
123
|
+
interface MetadataListOptions {
|
|
124
|
+
/** Filter by source package id (matches the `package` query param). */
|
|
125
|
+
packageId?: string;
|
|
185
126
|
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* ObjectUI
|
|
189
|
-
* Copyright (c) 2024-present ObjectStack Inc.
|
|
190
|
-
*
|
|
191
|
-
* This source code is licensed under the MIT license found in the
|
|
192
|
-
* LICENSE file in the root directory of this source tree.
|
|
193
|
-
*/
|
|
194
127
|
/**
|
|
195
|
-
*
|
|
196
|
-
*
|
|
128
|
+
* A pending DRAFT metadata item (ADR-0033), as returned by {@link MetadataClient.listDrafts}.
|
|
129
|
+
* Light header — no body — carrying the owning package so the console can group
|
|
130
|
+
* pending changes by app package.
|
|
197
131
|
*/
|
|
198
|
-
interface
|
|
199
|
-
|
|
200
|
-
environment: 'development' | 'staging' | 'production';
|
|
201
|
-
/** Cloud region */
|
|
202
|
-
region?: string;
|
|
203
|
-
/** Auto-scaling configuration */
|
|
204
|
-
scaling?: {
|
|
205
|
-
minInstances: number;
|
|
206
|
-
maxInstances: number;
|
|
207
|
-
targetCPU?: number;
|
|
208
|
-
};
|
|
209
|
-
/** Environment variables */
|
|
210
|
-
envVars?: Record<string, string>;
|
|
211
|
-
}
|
|
212
|
-
interface CloudHostingConfig {
|
|
213
|
-
/** Custom domain */
|
|
214
|
-
customDomain?: string;
|
|
215
|
-
/** SSL configuration */
|
|
216
|
-
ssl?: {
|
|
217
|
-
enabled: boolean;
|
|
218
|
-
autoRenew: boolean;
|
|
219
|
-
};
|
|
220
|
-
/** CDN configuration */
|
|
221
|
-
cdn?: {
|
|
222
|
-
enabled: boolean;
|
|
223
|
-
regions?: string[];
|
|
224
|
-
};
|
|
225
|
-
}
|
|
226
|
-
interface CloudMarketplaceEntry {
|
|
227
|
-
/** Plugin or app ID */
|
|
228
|
-
id: string;
|
|
229
|
-
/** Display name */
|
|
132
|
+
interface MetadataDraftHeader {
|
|
133
|
+
type: string;
|
|
230
134
|
name: string;
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
version: string;
|
|
235
|
-
/** Author */
|
|
236
|
-
author: string;
|
|
237
|
-
/** Category */
|
|
238
|
-
category: string;
|
|
239
|
-
/** Tags */
|
|
240
|
-
tags: string[];
|
|
241
|
-
/** Rating (1-5) */
|
|
242
|
-
rating?: number;
|
|
243
|
-
/** Install count */
|
|
244
|
-
installCount?: number;
|
|
245
|
-
/** Price (0 = free) */
|
|
246
|
-
price?: number;
|
|
135
|
+
packageId: string | null;
|
|
136
|
+
updatedAt: string | null;
|
|
137
|
+
updatedBy: string | null;
|
|
247
138
|
}
|
|
248
139
|
/**
|
|
249
|
-
*
|
|
250
|
-
*
|
|
140
|
+
* Options for {@link MetadataClient.save} — a WRITE OVER HTTP to
|
|
141
|
+
* `/api/v1/meta/:type/:name`.
|
|
142
|
+
*
|
|
143
|
+
* NOT the spec's `MetadataSaveOptions` (`@objectstack/spec/system` and
|
|
144
|
+
* `/kernel`), whose name this interface wore until objectui#3160
|
|
145
|
+
* (objectstack#4115 ledger batch 6). Both spec copies describe writing a
|
|
146
|
+
* metadata item to a FILE — `format: json|yaml|ts`, `path`, `indent`,
|
|
147
|
+
* `prettify`, `sortKeys`, `backup`, `atomic`, `loader`. Not one of those keys
|
|
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.
|
|
251
152
|
*/
|
|
252
|
-
|
|
253
|
-
private getClient;
|
|
254
|
-
constructor(getClient: () => any);
|
|
153
|
+
interface MetadataClientSaveOptions {
|
|
255
154
|
/**
|
|
256
|
-
*
|
|
155
|
+
* Optimistic concurrency token (the `checksum` returned by the last
|
|
156
|
+
* read). When present, sent as the `If-Match` header so concurrent
|
|
157
|
+
* edits get a 409 instead of overwriting each other.
|
|
257
158
|
*/
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
}>;
|
|
159
|
+
ifMatch?: string;
|
|
160
|
+
/** Optional actor id, sent as `X-Actor` for history attribution. */
|
|
161
|
+
actor?: string;
|
|
262
162
|
/**
|
|
263
|
-
*
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
163
|
+
* Bypass destructive-change protection (Phase 3a). The server returns
|
|
164
|
+
* `409 destructive_change` with `issues[]` if a write would drop or
|
|
165
|
+
* narrow data; setting `force: true` adds `?force=true` to the request
|
|
166
|
+
* so the operator can confirm and proceed.
|
|
167
|
+
*/
|
|
168
|
+
force?: boolean;
|
|
269
169
|
/**
|
|
270
|
-
*
|
|
170
|
+
* Save mode — `'draft'` writes a pending draft row (invisible to the
|
|
171
|
+
* runtime until `publish()` is called); `'publish'` writes directly
|
|
172
|
+
* to the active overlay. Omit (or set `'publish'`) for the legacy
|
|
173
|
+
* "save = live" behaviour.
|
|
271
174
|
*/
|
|
272
|
-
|
|
175
|
+
mode?: 'draft' | 'publish';
|
|
273
176
|
/**
|
|
274
|
-
*
|
|
177
|
+
* Software-package id to bind the saved row to (sent as the `package`
|
|
178
|
+
* query param → `sys_metadata.package_id`). Set when authoring inside a
|
|
179
|
+
* Studio package workspace. Omit for an env-local overlay.
|
|
275
180
|
*/
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
181
|
+
packageId?: string;
|
|
182
|
+
}
|
|
183
|
+
interface MetadataGetOptions {
|
|
184
|
+
/**
|
|
185
|
+
* Read a specific overlay state. `'draft'` returns the pending draft
|
|
186
|
+
* body (no fallback to the published overlay or the registry); omit
|
|
187
|
+
* to read the active (published) value.
|
|
188
|
+
*/
|
|
189
|
+
state?: 'active' | 'draft';
|
|
190
|
+
/**
|
|
191
|
+
* Software-package id to scope resolution (sent as the `package` query
|
|
192
|
+
* param → server prefer-local, ADR-0048). Set when reading one item that
|
|
193
|
+
* may collide by name across installed packages (e.g. the Studio editor
|
|
194
|
+
* passes the edited item's owning package). Omit for context-free reads.
|
|
195
|
+
*/
|
|
196
|
+
packageId?: string;
|
|
197
|
+
}
|
|
198
|
+
interface MetadataDeleteOptions extends MetadataClientSaveOptions {
|
|
199
|
+
/**
|
|
200
|
+
* Target state. `'draft'` discards the pending draft (keeps the
|
|
201
|
+
* published overlay intact). Omit to reset the active overlay back
|
|
202
|
+
* to the artifact default (the legacy behaviour).
|
|
203
|
+
*/
|
|
204
|
+
state?: 'active' | 'draft';
|
|
205
|
+
}
|
|
206
|
+
/** Layered view of a metadata item — Phase 3a `?layers=true`. */
|
|
207
|
+
interface MetadataLayered<T = unknown> {
|
|
208
|
+
/** Code-level (artifact) item; null if the item only exists as an overlay. */
|
|
209
|
+
code: T | null;
|
|
210
|
+
/** Org/environment overlay (just the saved delta or full overlay row). */
|
|
211
|
+
overlay: T | null;
|
|
212
|
+
/** Overlay scope (`organization` | `environment` | `package` | null). */
|
|
213
|
+
overlayScope: string | null;
|
|
214
|
+
/** Merged effective view — what the runtime actually sees. */
|
|
215
|
+
effective: T | null;
|
|
216
|
+
/**
|
|
217
|
+
* Load-time validation result for `effective` (server-computed via
|
|
218
|
+
* the same Zod registry used at save time). Undefined for types
|
|
219
|
+
* without a registered Zod schema. Surfaced by the Studio as a
|
|
220
|
+
* banner + inline field errors so operators can spot bad metadata
|
|
221
|
+
* without having to hit Save.
|
|
222
|
+
*/
|
|
223
|
+
_diagnostics?: MetadataDiagnostics;
|
|
224
|
+
/** 4-state lock: `none` / `no-overlay` / `no-delete` / `full`. */
|
|
225
|
+
lock?: 'none' | 'no-overlay' | 'no-delete' | 'full';
|
|
226
|
+
/** Human-readable reason for the lock (tooltip text). */
|
|
227
|
+
lockReason?: string;
|
|
228
|
+
/** Which layer set the lock: artifact / package / overlay / env-forced. */
|
|
229
|
+
lockSource?: 'artifact' | 'package' | 'overlay' | 'env-forced';
|
|
230
|
+
/** Optional docs URL for the lock reason (rendered as "View docs →"). */
|
|
231
|
+
lockDocsUrl?: string;
|
|
232
|
+
/** Origin of the item: `package` (loader) | `org` (tenant) | `env-forced`. */
|
|
233
|
+
provenance?: 'package' | 'org' | 'env-forced';
|
|
234
|
+
/** Owning package id (denormalised from the loader tag). */
|
|
235
|
+
packageId?: string;
|
|
236
|
+
/** Owning package version. */
|
|
237
|
+
packageVersion?: string;
|
|
238
|
+
/** True when the editor should allow Save (PUT). */
|
|
239
|
+
editable?: boolean;
|
|
240
|
+
/** True when the editor should allow Delete. */
|
|
241
|
+
deletable?: boolean;
|
|
242
|
+
/** True when "Reset to package default" applies (has overlay + artifact). */
|
|
243
|
+
resettable?: boolean;
|
|
279
244
|
}
|
|
280
|
-
|
|
281
245
|
/**
|
|
282
|
-
*
|
|
283
|
-
*
|
|
284
|
-
*
|
|
285
|
-
* This source code is licensed under the MIT license found in the
|
|
286
|
-
* LICENSE file in the root directory of this source tree.
|
|
246
|
+
* One row in the metadata protection-audit trail
|
|
247
|
+
* (ADR-0010 §3.6 / Phase 4.1). Mirrors `sys_metadata_audit` columns
|
|
248
|
+
* the API exposes.
|
|
287
249
|
*/
|
|
250
|
+
interface MetadataAuditEntry {
|
|
251
|
+
/** Stable audit-row id (uuid). */
|
|
252
|
+
id: unknown;
|
|
253
|
+
/** ISO timestamp when the attempt happened. */
|
|
254
|
+
occurredAt: string;
|
|
255
|
+
/** Who attempted the operation (`x-actor` header or `'system'`). */
|
|
256
|
+
actor: string;
|
|
257
|
+
/** Code path that recorded the row (e.g. `protocol.saveMetaItem`). */
|
|
258
|
+
source: string | null;
|
|
259
|
+
/** Which lifecycle op was attempted. */
|
|
260
|
+
operation: 'save' | 'publish' | 'rollback' | 'delete' | 'reset';
|
|
261
|
+
/** Decision: allowed / denied / forced (admin override). */
|
|
262
|
+
outcome: 'allowed' | 'denied' | 'forced';
|
|
263
|
+
/** Machine-readable reason code (`item_locked`, `ok`, …). */
|
|
264
|
+
code: string;
|
|
265
|
+
/** Effective lock at the moment of the attempt. */
|
|
266
|
+
lockState: 'none' | 'no-overlay' | 'no-delete' | 'full' | null;
|
|
267
|
+
/** True when admin forced the write through despite the lock. */
|
|
268
|
+
lockOverridden: boolean;
|
|
269
|
+
/** Request-id for trace correlation (if propagated). */
|
|
270
|
+
requestId: string | null;
|
|
271
|
+
/** Free-text note (often the lock reason). */
|
|
272
|
+
note: string | null;
|
|
273
|
+
}
|
|
274
|
+
/** Response shape for `MetadataClient.audit()`. */
|
|
275
|
+
interface MetadataAuditResponse {
|
|
276
|
+
events: MetadataAuditEntry[];
|
|
277
|
+
}
|
|
288
278
|
/**
|
|
289
|
-
*
|
|
290
|
-
*
|
|
279
|
+
* Load-time validation envelope attached to metadata items by the
|
|
280
|
+
* framework. Mirrors `MetadataValidationResult` in the kernel spec.
|
|
291
281
|
*/
|
|
292
|
-
interface
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
/** API surface contract */
|
|
304
|
-
api?: PluginAPIContract;
|
|
282
|
+
interface MetadataDiagnostics {
|
|
283
|
+
valid: boolean;
|
|
284
|
+
errors?: Array<{
|
|
285
|
+
path: string;
|
|
286
|
+
message: string;
|
|
287
|
+
code?: string;
|
|
288
|
+
}>;
|
|
289
|
+
warnings?: Array<{
|
|
290
|
+
path: string;
|
|
291
|
+
message: string;
|
|
292
|
+
}>;
|
|
305
293
|
}
|
|
306
|
-
|
|
307
|
-
|
|
294
|
+
/** Options for the cross-type `/meta/diagnostics` sweep call. */
|
|
295
|
+
interface MetadataDiagnosticsOptions {
|
|
296
|
+
/** Restrict the sweep to a single metadata type (e.g. `'view'`). */
|
|
297
|
+
type?: string;
|
|
298
|
+
/**
|
|
299
|
+
* `'error'` (default) returns only items that fail validation.
|
|
300
|
+
* `'warning'` also includes items whose only diagnostics are warnings.
|
|
301
|
+
*/
|
|
302
|
+
severity?: 'error' | 'warning';
|
|
303
|
+
/** Restrict to items owned by this package id. */
|
|
304
|
+
packageId?: string;
|
|
305
|
+
}
|
|
306
|
+
/** One row in the `/meta/diagnostics` response. */
|
|
307
|
+
interface MetadataDiagnosticsEntry {
|
|
308
|
+
type: string;
|
|
308
309
|
name: string;
|
|
309
|
-
|
|
310
|
-
type: 'component' | 'hook' | 'utility' | 'provider';
|
|
311
|
-
/** Description */
|
|
312
|
-
description?: string;
|
|
310
|
+
diagnostics: MetadataDiagnostics;
|
|
313
311
|
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
/**
|
|
318
|
-
|
|
319
|
-
/**
|
|
320
|
-
|
|
312
|
+
/** Top-level envelope returned by `/meta/diagnostics`. */
|
|
313
|
+
interface MetadataDiagnosticsSummary {
|
|
314
|
+
entries: MetadataDiagnosticsEntry[];
|
|
315
|
+
/** Number of `entries` returned (post-filter). */
|
|
316
|
+
total: number;
|
|
317
|
+
/** How many metadata types the sweep visited. */
|
|
318
|
+
scannedTypes: number;
|
|
319
|
+
/** How many individual items were validated. */
|
|
320
|
+
scannedItems: number;
|
|
321
|
+
/**
|
|
322
|
+
* Per-type aggregate stats — count of items and the list of
|
|
323
|
+
* packages contributing to each type. Computed in the same sweep
|
|
324
|
+
* so a single call serves both the diagnostics governance page and
|
|
325
|
+
* the directory tile counts / package filter.
|
|
326
|
+
*
|
|
327
|
+
* Optional for backward compatibility with older framework
|
|
328
|
+
* versions that do not yet emit it; clients should fall back to
|
|
329
|
+
* an empty record.
|
|
330
|
+
*/
|
|
331
|
+
stats?: Record<string, {
|
|
332
|
+
count: number;
|
|
333
|
+
locked?: number;
|
|
334
|
+
packages: string[];
|
|
335
|
+
}>;
|
|
321
336
|
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
337
|
+
/** Reference back-pointer — Phase 3a `/references`. */
|
|
338
|
+
interface MetadataReference {
|
|
339
|
+
/** Referencing item's metadata type. */
|
|
340
|
+
fromType: string;
|
|
341
|
+
/** Referencing item's name. */
|
|
342
|
+
fromName: string;
|
|
343
|
+
/** JSON path within the referencing item that holds the reference. */
|
|
344
|
+
path: string;
|
|
345
|
+
/** The actual value seen at `path` (the referenced name). */
|
|
346
|
+
value: string;
|
|
326
347
|
}
|
|
327
|
-
interface
|
|
328
|
-
|
|
348
|
+
interface MetadataHistoryOptions {
|
|
349
|
+
/** Only return events after this sequence number. */
|
|
350
|
+
sinceSeq?: number;
|
|
351
|
+
/** Limit the number of events returned. */
|
|
352
|
+
limit?: number;
|
|
353
|
+
}
|
|
354
|
+
/** A single field-anchored spec-validation issue (server `error.details.issues`). */
|
|
355
|
+
interface MetadataValidationIssue {
|
|
356
|
+
/** Dot-path to the offending field, e.g. `fields.amount.type` (`''` = root). */
|
|
357
|
+
path: string;
|
|
329
358
|
message: string;
|
|
330
|
-
code
|
|
359
|
+
code?: string;
|
|
360
|
+
}
|
|
361
|
+
interface MetadataError extends Error {
|
|
362
|
+
status: number;
|
|
363
|
+
code?: string;
|
|
364
|
+
body?: unknown;
|
|
365
|
+
/** Structured spec-validation issues, when the failure was a validation error. */
|
|
366
|
+
issues?: MetadataValidationIssue[];
|
|
331
367
|
}
|
|
332
368
|
/**
|
|
333
|
-
*
|
|
334
|
-
*/
|
|
335
|
-
declare function validatePluginContract(contract: PluginContract): ContractValidationResult;
|
|
336
|
-
/**
|
|
337
|
-
* Generate a plugin contract manifest for marketplace publishing.
|
|
338
|
-
*/
|
|
339
|
-
declare function generateContractManifest(contract: PluginContract): Record<string, unknown>;
|
|
340
|
-
|
|
341
|
-
/**
|
|
342
|
-
* ObjectStack-backed `UserDataAdapter` factory.
|
|
343
|
-
*
|
|
344
|
-
* Persists arbitrary per-user UI state (favorites, recently-accessed items, …)
|
|
345
|
-
* as a single JSON blob per `(user_id, key)` row in the **unified per-user
|
|
346
|
-
* KV store** — the `sys_user_preference` object shipped by every
|
|
347
|
-
* `@objectstack/plugin-auth`-enabled environment.
|
|
348
|
-
*
|
|
349
|
-
* Using the existing `sys_user_preference` table (rather than a parallel
|
|
350
|
-
* `user_app_state` table) keeps things consistent with the platform's
|
|
351
|
-
* "one KV store per scope" pattern:
|
|
352
|
-
*
|
|
353
|
-
* - `sys_setting` ← tenant / env scope
|
|
354
|
-
* - `sys_user_preference` ← per-user scope ← we live here
|
|
369
|
+
* MetadataClient — read/write protocol metadata via the framework REST API.
|
|
355
370
|
*
|
|
356
|
-
*
|
|
357
|
-
*
|
|
358
|
-
*
|
|
359
|
-
*
|
|
360
|
-
*
|
|
361
|
-
*
|
|
362
|
-
* The canonical `sys_user_preference` schema (from
|
|
363
|
-
* `@objectstack/platform-objects`):
|
|
364
|
-
*
|
|
365
|
-
* ```yaml
|
|
366
|
-
* object: sys_user_preference
|
|
367
|
-
* fields:
|
|
368
|
-
* user_id: lookup(sys_user) # indexed
|
|
369
|
-
* key: string # indexed (e.g. "ui.favorites")
|
|
370
|
-
* value: json # the serialised list
|
|
371
|
-
* updated_at: datetime # auto-managed
|
|
372
|
-
* unique: [user_id, key]
|
|
371
|
+
* @example
|
|
372
|
+
* ```ts
|
|
373
|
+
* const client = new MetadataClient({ baseUrl: 'http://localhost:3000' });
|
|
374
|
+
* const objects = await client.list<{ name: string; label?: string }>('object');
|
|
375
|
+
* const account = await client.get<{ fields: Record<string, unknown> }>('object', 'account');
|
|
376
|
+
* await client.save('object', 'account', { ...account, label: 'Customer' });
|
|
373
377
|
* ```
|
|
374
|
-
*
|
|
375
|
-
* ## Failure modes
|
|
376
|
-
*
|
|
377
|
-
* The adapter is designed to **degrade silently**. Any error — missing
|
|
378
|
-
* schema, 4xx/5xx, network — is caught:
|
|
379
|
-
*
|
|
380
|
-
* - `load()` returns `[]`, so the hosting provider keeps its localStorage state.
|
|
381
|
-
* - `save()` resolves without throwing, so UI mutations never surface a toast.
|
|
382
|
-
*
|
|
383
|
-
* As soon as the backend supports `sys_user_preference`, persistence
|
|
384
|
-
* "lights up" with no code change.
|
|
385
|
-
*
|
|
386
|
-
* @module
|
|
387
378
|
*/
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
/**
|
|
393
|
-
|
|
379
|
+
declare class MetadataClient {
|
|
380
|
+
private readonly base;
|
|
381
|
+
private readonly fetchImpl;
|
|
382
|
+
private readonly headers;
|
|
383
|
+
/** ADR-0037: when true, reads render the draft-overlaid world. */
|
|
384
|
+
readonly previewDrafts: boolean;
|
|
385
|
+
/** #4133 — sink for post-save advisory findings; see the config field. */
|
|
386
|
+
private readonly onSaveAdvisory;
|
|
387
|
+
constructor(config: MetadataClientConfig);
|
|
388
|
+
/** Update the client's environment scope at runtime. */
|
|
389
|
+
withEnvironment(environmentId: string | undefined): MetadataClient;
|
|
394
390
|
/**
|
|
395
|
-
*
|
|
396
|
-
*
|
|
397
|
-
*
|
|
391
|
+
* Derive a client whose reads render the draft-overlaid world (or not).
|
|
392
|
+
* Same base/fetch/headers; used by the app-shell to switch the whole
|
|
393
|
+
* renderer tree into ADR-0037 preview mode off one URL flag.
|
|
398
394
|
*/
|
|
399
|
-
|
|
400
|
-
/**
|
|
401
|
-
|
|
395
|
+
withPreviewDrafts(previewDrafts: boolean): MetadataClient;
|
|
396
|
+
/** List all registered metadata types (returns the registry rows). */
|
|
397
|
+
listTypes(): Promise<unknown[]>;
|
|
398
|
+
/** List items of a metadata type (e.g. `object`, `field`, `view`). */
|
|
399
|
+
list<T = unknown>(type: string, options?: MetadataListOptions): Promise<T[]>;
|
|
402
400
|
/**
|
|
403
|
-
*
|
|
404
|
-
*
|
|
401
|
+
* List pending DRAFT items (ADR-0033) — what an AI authored but nobody
|
|
402
|
+
* published yet. `list()` only sees published/active metadata, so a
|
|
403
|
+
* just-built app package looks empty there; this surfaces the drafts so the
|
|
404
|
+
* console can show a "pending changes" view and draft-aware package contents.
|
|
405
|
+
* Optionally narrow by `packageId` and/or `type`. Returns light headers
|
|
406
|
+
* (no body) carrying `packageId` for grouping.
|
|
405
407
|
*/
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
save(items: T[]): Promise<void>;
|
|
411
|
-
}
|
|
412
|
-
/**
|
|
413
|
-
* Build a `UserDataAdapter<T>` backed by ObjectStack.
|
|
414
|
-
*
|
|
415
|
-
* Each adapter instance is bound to a single `(user, key)` pair; create
|
|
416
|
-
* one per slot you want to persist.
|
|
417
|
-
*/
|
|
418
|
-
declare function createObjectStackUserStateAdapter<T = unknown>(options: ObjectStackUserStateAdapterOptions): UserDataAdapter<T>;
|
|
419
|
-
|
|
420
|
-
/**
|
|
421
|
-
* ObjectUI
|
|
422
|
-
* Copyright (c) 2024-present ObjectStack Inc.
|
|
423
|
-
*
|
|
424
|
-
* This source code is licensed under the MIT license found in the
|
|
425
|
-
* LICENSE file in the root directory of this source tree.
|
|
426
|
-
*/
|
|
427
|
-
/**
|
|
428
|
-
* Integration module for @objectstack/spec v3.0.0
|
|
429
|
-
* Provides third-party service connectors for Slack, email, webhooks.
|
|
430
|
-
*/
|
|
431
|
-
type IntegrationProvider = 'slack' | 'email' | 'webhook' | 'teams' | 'discord';
|
|
432
|
-
interface IntegrationConfig {
|
|
433
|
-
/** Integration provider type */
|
|
434
|
-
provider: IntegrationProvider;
|
|
435
|
-
/** Whether this integration is enabled */
|
|
436
|
-
enabled: boolean;
|
|
437
|
-
/** Provider-specific configuration */
|
|
438
|
-
config: Record<string, unknown>;
|
|
439
|
-
/** Event triggers */
|
|
440
|
-
triggers?: IntegrationTrigger[];
|
|
441
|
-
}
|
|
442
|
-
interface IntegrationTrigger {
|
|
443
|
-
/** Event name (e.g. 'record.created', 'record.updated') */
|
|
444
|
-
event: string;
|
|
445
|
-
/** Filter condition */
|
|
446
|
-
filter?: string;
|
|
447
|
-
/** Template for the message/payload */
|
|
448
|
-
template?: string;
|
|
449
|
-
}
|
|
450
|
-
interface SlackIntegrationConfig extends IntegrationConfig {
|
|
451
|
-
provider: 'slack';
|
|
452
|
-
config: {
|
|
453
|
-
webhookUrl: string;
|
|
454
|
-
channel?: string;
|
|
455
|
-
username?: string;
|
|
456
|
-
iconEmoji?: string;
|
|
457
|
-
};
|
|
458
|
-
}
|
|
459
|
-
interface EmailIntegrationConfig extends IntegrationConfig {
|
|
460
|
-
provider: 'email';
|
|
461
|
-
config: {
|
|
462
|
-
smtpHost: string;
|
|
463
|
-
smtpPort: number;
|
|
464
|
-
secure: boolean;
|
|
465
|
-
from: string;
|
|
466
|
-
to: string[];
|
|
467
|
-
subject?: string;
|
|
468
|
-
};
|
|
469
|
-
}
|
|
470
|
-
interface WebhookIntegrationConfig extends IntegrationConfig {
|
|
471
|
-
provider: 'webhook';
|
|
472
|
-
config: {
|
|
473
|
-
url: string;
|
|
474
|
-
method: 'GET' | 'POST' | 'PUT' | 'PATCH';
|
|
475
|
-
headers?: Record<string, string>;
|
|
476
|
-
retryCount?: number;
|
|
477
|
-
retryDelay?: number;
|
|
478
|
-
};
|
|
479
|
-
}
|
|
480
|
-
/**
|
|
481
|
-
* Integration service manager.
|
|
482
|
-
* Manages third-party service connections and event dispatching.
|
|
483
|
-
*/
|
|
484
|
-
declare class IntegrationManager {
|
|
485
|
-
private integrations;
|
|
408
|
+
listDrafts(options?: {
|
|
409
|
+
packageId?: string;
|
|
410
|
+
type?: string;
|
|
411
|
+
}): Promise<MetadataDraftHeader[]>;
|
|
486
412
|
/**
|
|
487
|
-
*
|
|
413
|
+
* Fetch a single metadata item and return the response body EXACTLY as the
|
|
414
|
+
* server sent it — envelope and all.
|
|
415
|
+
*
|
|
416
|
+
* The one transport both {@link get} (which unwraps) and {@link getDraft}
|
|
417
|
+
* (which does not) sit on, so the two can differ in what they hand back
|
|
418
|
+
* without differing in how they ask. Private on purpose: the envelope is a
|
|
419
|
+
* wire detail, and the two published contracts above are the supported ways
|
|
420
|
+
* to read an item.
|
|
488
421
|
*/
|
|
489
|
-
|
|
422
|
+
private readItemResponse;
|
|
490
423
|
/**
|
|
491
|
-
*
|
|
424
|
+
* Get a single metadata item. Returns the unwrapped item CONTENT — the
|
|
425
|
+
* metadata document itself, so `obj.fields` / `obj.label` are reachable
|
|
426
|
+
* directly.
|
|
427
|
+
*
|
|
428
|
+
* The framework answers `GET /meta/:type/:name` with the spec-declared
|
|
429
|
+
* envelope `{ type, name, item, …protection fields }`
|
|
430
|
+
* (`GetMetaItemResponseSchema`; objectstack#5563 collapsed this read to that
|
|
431
|
+
* ONE shape), so the envelope is unwrapped here — at the client boundary,
|
|
432
|
+
* once — rather than by each caller.
|
|
433
|
+
*
|
|
434
|
+
* **This method used to return the envelope while promising the body**
|
|
435
|
+
* (objectui#4271). Nothing detected the disagreement, because the test
|
|
436
|
+
* doubles across the repo were written against this docblock: every consumer
|
|
437
|
+
* reading `obj.fields` got `undefined` in production and a field list in
|
|
438
|
+
* unit tests. The visible cost was the entire field half of the permission
|
|
439
|
+
* matrix reporting "No fields registered for this object." for every object,
|
|
440
|
+
* plus dead RLS CEL autocomplete, an inert report drill-down and a designer
|
|
441
|
+
* that saved the envelope back over the object body.
|
|
442
|
+
*
|
|
443
|
+
* A response that is NOT the envelope (an older server answering the bare
|
|
444
|
+
* document, or a body of its own shape) passes through untouched, and `null`
|
|
445
|
+
* still comes back on 404 to keep the call site ergonomic.
|
|
492
446
|
*/
|
|
493
|
-
|
|
447
|
+
get<T = unknown>(type: string, name: string, options?: MetadataGetOptions): Promise<T | null>;
|
|
494
448
|
/**
|
|
495
|
-
*
|
|
449
|
+
* Read the pending draft body for an item (`?state=draft`). Returns
|
|
450
|
+
* `null` when there is no draft pending. Draft reads do NOT fall
|
|
451
|
+
* back to the published overlay or the artifact registry — a `null`
|
|
452
|
+
* unambiguously means "nothing to publish".
|
|
453
|
+
*
|
|
454
|
+
* Note: this method hands back the `{ type, name, item }` envelope the
|
|
455
|
+
* framework sends, NOT the body — callers read `.item`. That asymmetry with
|
|
456
|
+
* {@link get} is deliberate and long-standing (the draft envelope's identity
|
|
457
|
+
* and protection carriers are part of what a draft reader inspects), so it
|
|
458
|
+
* is preserved by reading the transport directly instead of going through
|
|
459
|
+
* `get()`'s unwrap. `unwrapDraftBody` (app-shell) and `unwrapViewDraft`
|
|
460
|
+
* (this package) are the shared helpers for taking the body out.
|
|
496
461
|
*/
|
|
497
|
-
|
|
462
|
+
getDraft<T = unknown>(type: string, name: string, options?: {
|
|
463
|
+
packageId?: string;
|
|
464
|
+
}): Promise<T | null>;
|
|
498
465
|
/**
|
|
499
|
-
*
|
|
466
|
+
* Save (PUT) a metadata item. The framework accepts both the bare
|
|
467
|
+
* item payload and the `{ item: ... }` / `{ metadata: ... }`
|
|
468
|
+
* envelopes; we send bare for consistency. Pass `mode: 'draft'` to
|
|
469
|
+
* stage the change without publishing (Studio's "Save" button).
|
|
500
470
|
*/
|
|
501
|
-
|
|
471
|
+
save<T = unknown>(type: string, name: string, item: unknown, options?: MetadataClientSaveOptions): Promise<T>;
|
|
502
472
|
/**
|
|
503
|
-
*
|
|
504
|
-
*
|
|
473
|
+
* Publish a single pending draft BY REFERENCE — promotes the draft row for
|
|
474
|
+
* `(type, name)` into the active overlay and drops the draft. Works for ANY
|
|
475
|
+
* draft, including ones with no `packageId` binding (which the package-scoped
|
|
476
|
+
* `/packages/:id/publish-drafts` flow cannot reach). Use this to publish the
|
|
477
|
+
* exact set returned by {@link listDrafts} without needing a package.
|
|
478
|
+
*
|
|
479
|
+
* Returns the server result. For `seed` drafts the protocol also materializes
|
|
480
|
+
* the rows and reports under `seedApplied` — a data problem never fails the
|
|
481
|
+
* publish, so callers should check `seedApplied?.success` and warn the user
|
|
482
|
+
* rather than assume the data went live.
|
|
505
483
|
*/
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
484
|
+
publishDraft(type: string, name: string): Promise<{
|
|
485
|
+
success?: boolean;
|
|
486
|
+
seedApplied?: {
|
|
487
|
+
success: boolean;
|
|
488
|
+
inserted?: number;
|
|
489
|
+
updated?: number;
|
|
490
|
+
error?: string;
|
|
491
|
+
errors?: unknown[];
|
|
492
|
+
};
|
|
493
|
+
} & Record<string, unknown>>;
|
|
511
494
|
/**
|
|
512
|
-
*
|
|
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).
|
|
513
498
|
*/
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
/**
|
|
518
|
-
* ObjectUI
|
|
519
|
-
* Copyright (c) 2024-present ObjectStack Inc.
|
|
520
|
-
*
|
|
521
|
-
* This source code is licensed under the MIT license found in the
|
|
522
|
-
* LICENSE file in the root directory of this source tree.
|
|
523
|
-
*/
|
|
524
|
-
/**
|
|
525
|
-
* MetadataClient
|
|
526
|
-
*
|
|
527
|
-
* Thin, framework-agnostic HTTP client for the ObjectStack metadata
|
|
528
|
-
* API (`/api/v1/meta/*`). Used by the platform Setup app surfaces in
|
|
529
|
-
* `@object-ui/plugin-designer` (Object Manager, Field Designer, etc.)
|
|
530
|
-
* so they can read and write protocol metadata without depending on
|
|
531
|
-
* `ObjectStackAdapter` (which is a generic data-source binding and
|
|
532
|
-
* carries a lot of view/dashboard-specific behaviour we don't need
|
|
533
|
-
* here).
|
|
534
|
-
*
|
|
535
|
-
* Endpoints (see `packages/rest/src/rest-server.ts`):
|
|
536
|
-
* GET /api/v1/meta — list all metadata types
|
|
537
|
-
* GET /api/v1/meta/:type — list items of a type
|
|
538
|
-
* GET /api/v1/meta/:type/:name — get one item (returns the
|
|
539
|
-
* unwrapped item content)
|
|
540
|
-
* PUT /api/v1/meta/:type/:name — save (overlay) one item
|
|
541
|
-
* honours `If-Match` for OCC
|
|
542
|
-
* DELETE /api/v1/meta/:type/:name — reset overlay to artifact
|
|
543
|
-
* GET /api/v1/meta/:type/:name/history — durable change log
|
|
544
|
-
*
|
|
545
|
-
* The client deliberately keeps the response shape opaque (typed as
|
|
546
|
-
* `unknown`/generic `T`) so callers explicitly narrow per-metadata-
|
|
547
|
-
* type, mirroring the framework's "single Zod source per type" rule.
|
|
548
|
-
*/
|
|
549
|
-
interface MetadataClientConfig {
|
|
550
|
-
/** Base URL of the ObjectStack server (no trailing slash needed). */
|
|
551
|
-
baseUrl: string;
|
|
499
|
+
layered<T = unknown>(type: string, name: string, options?: {
|
|
500
|
+
packageId?: string;
|
|
501
|
+
}): Promise<MetadataLayered<T>>;
|
|
552
502
|
/**
|
|
553
|
-
*
|
|
554
|
-
*
|
|
555
|
-
*
|
|
556
|
-
*
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
fetch?: typeof fetch;
|
|
561
|
-
/** Additional headers (e.g. auth) included on every request. */
|
|
562
|
-
headers?: Record<string, string>;
|
|
563
|
-
/**
|
|
564
|
-
* ADR-0037 Live Canvas — render the world **as-if-published**. When true,
|
|
565
|
-
* `list()` and `get()` append `?preview=draft`, which the framework
|
|
566
|
-
* dispatcher maps to `getMetaItems/getMetaItem({ previewDrafts: true })`:
|
|
567
|
-
* pending ADR-0033 drafts overlay the active registry (draft wins by name,
|
|
568
|
-
* draft-only items surface). Reads only — writes are unaffected, and
|
|
569
|
-
* nothing the preview shows is live until Publish.
|
|
503
|
+
* Cross-type sweep of load-time validation results — calls
|
|
504
|
+
* `GET /meta/diagnostics`. Returns every entry the framework
|
|
505
|
+
* considers invalid (or, when `severity: 'warning'`, also entries
|
|
506
|
+
* with only warnings).
|
|
507
|
+
*
|
|
508
|
+
* Used by the Studio's governance overview page and by the
|
|
509
|
+
* directory page to show "N invalid" badges per metadata type.
|
|
570
510
|
*/
|
|
571
|
-
|
|
572
|
-
}
|
|
573
|
-
interface MetadataListOptions {
|
|
574
|
-
/** Filter by source package id (matches the `package` query param). */
|
|
575
|
-
packageId?: string;
|
|
576
|
-
}
|
|
577
|
-
/**
|
|
578
|
-
* A pending DRAFT metadata item (ADR-0033), as returned by {@link MetadataClient.listDrafts}.
|
|
579
|
-
* Light header — no body — carrying the owning package so the console can group
|
|
580
|
-
* pending changes by app package.
|
|
581
|
-
*/
|
|
582
|
-
interface MetadataDraftHeader {
|
|
583
|
-
type: string;
|
|
584
|
-
name: string;
|
|
585
|
-
packageId: string | null;
|
|
586
|
-
updatedAt: string | null;
|
|
587
|
-
updatedBy: string | null;
|
|
588
|
-
}
|
|
589
|
-
/**
|
|
590
|
-
* Options for {@link MetadataClient.save} — a WRITE OVER HTTP to
|
|
591
|
-
* `/api/v1/meta/:type/:name`.
|
|
592
|
-
*
|
|
593
|
-
* NOT the spec's `MetadataSaveOptions` (`@objectstack/spec/system` and
|
|
594
|
-
* `/kernel`), whose name this interface wore until objectui#3160
|
|
595
|
-
* (objectstack#4115 ledger batch 6). Both spec copies describe writing a
|
|
596
|
-
* metadata item to a FILE — `format: json|yaml|ts`, `path`, `indent`,
|
|
597
|
-
* `prettify`, `sortKeys`, `backup`, `atomic`, `loader`. Not one of those keys
|
|
598
|
-
* exists here, and not one of these exists there: this is the REST client's
|
|
599
|
-
* request envelope — optimistic concurrency (`ifMatch` → `If-Match`), actor
|
|
600
|
-
* attribution, the destructive-change override, the ADR-0033 draft/publish
|
|
601
|
-
* mode, and the owning package. Same words, different layer.
|
|
602
|
-
*/
|
|
603
|
-
interface MetadataClientSaveOptions {
|
|
511
|
+
diagnostics(options?: MetadataDiagnosticsOptions): Promise<MetadataDiagnosticsSummary>;
|
|
604
512
|
/**
|
|
605
|
-
*
|
|
606
|
-
*
|
|
607
|
-
*
|
|
513
|
+
* Find every metadata item that references this one (Phase 3a). Useful
|
|
514
|
+
* for pre-delete impact analysis: "Are any views pointing at this
|
|
515
|
+
* object before I drop it?".
|
|
608
516
|
*/
|
|
609
|
-
|
|
610
|
-
/** Optional actor id, sent as `X-Actor` for history attribution. */
|
|
611
|
-
actor?: string;
|
|
517
|
+
references(type: string, name: string): Promise<MetadataReference[]>;
|
|
612
518
|
/**
|
|
613
|
-
*
|
|
614
|
-
*
|
|
615
|
-
*
|
|
616
|
-
*
|
|
519
|
+
* Reset a metadata customization overlay back to the artifact default.
|
|
520
|
+
* Idempotent: returns the result even when no overlay row existed.
|
|
521
|
+
* Pass `state: 'draft'` to discard the pending draft only (keeps the
|
|
522
|
+
* published overlay intact) — useful for a Studio "Discard draft" button.
|
|
617
523
|
*/
|
|
618
|
-
|
|
524
|
+
reset<T = unknown>(type: string, name: string, options?: MetadataDeleteOptions): Promise<T>;
|
|
619
525
|
/**
|
|
620
|
-
*
|
|
621
|
-
*
|
|
622
|
-
*
|
|
623
|
-
*
|
|
526
|
+
* Promote the pending draft of an item to the active overlay
|
|
527
|
+
* (POST `/meta/:type/:name/publish`). Returns
|
|
528
|
+
* `{ success, version, seq, message }`. Throws a `404 no_draft` if
|
|
529
|
+
* nothing is pending and `409 metadata_conflict` if the published
|
|
530
|
+
* overlay moved while the draft was sitting.
|
|
624
531
|
*/
|
|
625
|
-
|
|
532
|
+
publish<T = unknown>(type: string, name: string, options?: {
|
|
533
|
+
actor?: string;
|
|
534
|
+
message?: string;
|
|
535
|
+
}): Promise<T>;
|
|
626
536
|
/**
|
|
627
|
-
*
|
|
628
|
-
*
|
|
629
|
-
*
|
|
537
|
+
* Restore an item to a previous version (POST `/meta/:type/:name/rollback`).
|
|
538
|
+
* The server reads the history row at `toVersion`, writes its body back
|
|
539
|
+
* as the active overlay with `operation_type='revert'`. Returns
|
|
540
|
+
* `{ success, version, seq, restoredFromVersion, message }`. Throws
|
|
541
|
+
* `404 version_not_found` for unknown versions and `409 version_not_restorable`
|
|
542
|
+
* when the target version is a delete tombstone.
|
|
630
543
|
*/
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
544
|
+
rollback<T = unknown>(type: string, name: string, toVersion: number, options?: {
|
|
545
|
+
actor?: string;
|
|
546
|
+
message?: string;
|
|
547
|
+
}): Promise<T>;
|
|
634
548
|
/**
|
|
635
|
-
*
|
|
636
|
-
*
|
|
637
|
-
*
|
|
549
|
+
* Compute a structured top-level key diff between two history versions
|
|
550
|
+
* (GET `/meta/:type/:name/diff?from=&to=`). Returns
|
|
551
|
+
* `{ added, removed, changed }` where each entry carries a `path` and
|
|
552
|
+
* the relevant value(s). Useful for "what changed in this draft?" and
|
|
553
|
+
* pre-rollback previews.
|
|
638
554
|
*/
|
|
639
|
-
|
|
555
|
+
diff<T = unknown>(type: string, name: string, fromVersion?: number, toVersion?: number): Promise<T>;
|
|
556
|
+
/** Fetch the durable history (change log) for one metadata item. */
|
|
557
|
+
history<T = unknown>(type: string, name: string, options?: MetadataHistoryOptions): Promise<T>;
|
|
640
558
|
/**
|
|
641
|
-
*
|
|
642
|
-
*
|
|
643
|
-
*
|
|
644
|
-
*
|
|
559
|
+
* Fetch the protection-audit trail for one metadata item
|
|
560
|
+
* (ADR-0010 §3.6 / Phase 4.1). Shows every save/publish/rollback/
|
|
561
|
+
* delete/reset attempt — allowed, denied, or forced — so the
|
|
562
|
+
* Studio "审计日志 / Audit log" tab can render who tried what
|
|
563
|
+
* and whether a lock blocked it.
|
|
645
564
|
*/
|
|
646
|
-
|
|
565
|
+
audit(type: string, name: string, options?: {
|
|
566
|
+
limit?: number;
|
|
567
|
+
}): Promise<MetadataAuditResponse>;
|
|
647
568
|
}
|
|
648
|
-
|
|
569
|
+
|
|
570
|
+
/**
|
|
571
|
+
* ObjectUI
|
|
572
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
573
|
+
*
|
|
574
|
+
* This source code is licensed under the MIT license found in the
|
|
575
|
+
* LICENSE file in the root directory of this source tree.
|
|
576
|
+
*/
|
|
577
|
+
/**
|
|
578
|
+
* Base error class for all ObjectStack adapter errors
|
|
579
|
+
*/
|
|
580
|
+
declare class ObjectStackError extends Error {
|
|
581
|
+
code: string;
|
|
582
|
+
statusCode?: number | undefined;
|
|
583
|
+
details?: Record<string, unknown> | undefined;
|
|
649
584
|
/**
|
|
650
|
-
*
|
|
651
|
-
*
|
|
652
|
-
*
|
|
585
|
+
* Create a new ObjectStackError
|
|
586
|
+
*
|
|
587
|
+
* @param message - Human-readable error message
|
|
588
|
+
* @param code - Unique error code for programmatic handling
|
|
589
|
+
* @param statusCode - Optional HTTP status code
|
|
590
|
+
* @param details - Optional additional error details for debugging
|
|
653
591
|
*/
|
|
654
|
-
|
|
655
|
-
}
|
|
656
|
-
/** Layered view of a metadata item — Phase 3a `?layers=true`. */
|
|
657
|
-
interface MetadataLayered<T = unknown> {
|
|
658
|
-
/** Code-level (artifact) item; null if the item only exists as an overlay. */
|
|
659
|
-
code: T | null;
|
|
660
|
-
/** Org/environment overlay (just the saved delta or full overlay row). */
|
|
661
|
-
overlay: T | null;
|
|
662
|
-
/** Overlay scope (`organization` | `environment` | `package` | null). */
|
|
663
|
-
overlayScope: string | null;
|
|
664
|
-
/** Merged effective view — what the runtime actually sees. */
|
|
665
|
-
effective: T | null;
|
|
592
|
+
constructor(message: string, code: string, statusCode?: number | undefined, details?: Record<string, unknown> | undefined);
|
|
666
593
|
/**
|
|
667
|
-
*
|
|
668
|
-
* the same Zod registry used at save time). Undefined for types
|
|
669
|
-
* without a registered Zod schema. Surfaced by the Studio as a
|
|
670
|
-
* banner + inline field errors so operators can spot bad metadata
|
|
671
|
-
* without having to hit Save.
|
|
594
|
+
* Convert error to JSON for logging/debugging
|
|
672
595
|
*/
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
lockDocsUrl?: string;
|
|
682
|
-
/** Origin of the item: `package` (loader) | `org` (tenant) | `env-forced`. */
|
|
683
|
-
provenance?: 'package' | 'org' | 'env-forced';
|
|
684
|
-
/** Owning package id (denormalised from the loader tag). */
|
|
685
|
-
packageId?: string;
|
|
686
|
-
/** Owning package version. */
|
|
687
|
-
packageVersion?: string;
|
|
688
|
-
/** True when the editor should allow Save (PUT). */
|
|
689
|
-
editable?: boolean;
|
|
690
|
-
/** True when the editor should allow Delete. */
|
|
691
|
-
deletable?: boolean;
|
|
692
|
-
/** True when "Reset to package default" applies (has overlay + artifact). */
|
|
693
|
-
resettable?: boolean;
|
|
596
|
+
toJSON(): {
|
|
597
|
+
name: string;
|
|
598
|
+
message: string;
|
|
599
|
+
code: string;
|
|
600
|
+
statusCode: number | undefined;
|
|
601
|
+
details: Record<string, unknown> | undefined;
|
|
602
|
+
stack: string | undefined;
|
|
603
|
+
};
|
|
694
604
|
}
|
|
695
605
|
/**
|
|
696
|
-
*
|
|
697
|
-
* (ADR-0010 §3.6 / Phase 4.1). Mirrors `sys_metadata_audit` columns
|
|
698
|
-
* the API exposes.
|
|
606
|
+
* Error thrown when requested metadata/schema is not found
|
|
699
607
|
*/
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
id: unknown;
|
|
703
|
-
/** ISO timestamp when the attempt happened. */
|
|
704
|
-
occurredAt: string;
|
|
705
|
-
/** Who attempted the operation (`x-actor` header or `'system'`). */
|
|
706
|
-
actor: string;
|
|
707
|
-
/** Code path that recorded the row (e.g. `protocol.saveMetaItem`). */
|
|
708
|
-
source: string | null;
|
|
709
|
-
/** Which lifecycle op was attempted. */
|
|
710
|
-
operation: 'save' | 'publish' | 'rollback' | 'delete' | 'reset';
|
|
711
|
-
/** Decision: allowed / denied / forced (admin override). */
|
|
712
|
-
outcome: 'allowed' | 'denied' | 'forced';
|
|
713
|
-
/** Machine-readable reason code (`item_locked`, `ok`, …). */
|
|
714
|
-
code: string;
|
|
715
|
-
/** Effective lock at the moment of the attempt. */
|
|
716
|
-
lockState: 'none' | 'no-overlay' | 'no-delete' | 'full' | null;
|
|
717
|
-
/** True when admin forced the write through despite the lock. */
|
|
718
|
-
lockOverridden: boolean;
|
|
719
|
-
/** Request-id for trace correlation (if propagated). */
|
|
720
|
-
requestId: string | null;
|
|
721
|
-
/** Free-text note (often the lock reason). */
|
|
722
|
-
note: string | null;
|
|
723
|
-
}
|
|
724
|
-
/** Response shape for `MetadataClient.audit()`. */
|
|
725
|
-
interface MetadataAuditResponse {
|
|
726
|
-
events: MetadataAuditEntry[];
|
|
608
|
+
declare class MetadataNotFoundError extends ObjectStackError {
|
|
609
|
+
constructor(objectName: string, details?: Record<string, unknown>);
|
|
727
610
|
}
|
|
728
611
|
/**
|
|
729
|
-
*
|
|
730
|
-
* framework. Mirrors `MetadataValidationResult` in the kernel spec.
|
|
612
|
+
* Error thrown when a bulk operation fails
|
|
731
613
|
*/
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
614
|
+
declare class BulkOperationError extends ObjectStackError {
|
|
615
|
+
successCount: number;
|
|
616
|
+
failureCount: number;
|
|
617
|
+
errors: Array<{
|
|
618
|
+
index: number;
|
|
619
|
+
error: unknown;
|
|
738
620
|
}>;
|
|
739
|
-
warnings?: Array<{
|
|
740
|
-
path: string;
|
|
741
|
-
message: string;
|
|
742
|
-
}>;
|
|
743
|
-
}
|
|
744
|
-
/** Options for the cross-type `/meta/diagnostics` sweep call. */
|
|
745
|
-
interface MetadataDiagnosticsOptions {
|
|
746
|
-
/** Restrict the sweep to a single metadata type (e.g. `'view'`). */
|
|
747
|
-
type?: string;
|
|
748
621
|
/**
|
|
749
|
-
*
|
|
750
|
-
*
|
|
622
|
+
* Create a new BulkOperationError
|
|
623
|
+
*
|
|
624
|
+
* @param operation - The bulk operation that failed (create, update, delete)
|
|
625
|
+
* @param successCount - Number of successful operations
|
|
626
|
+
* @param failureCount - Number of failed operations
|
|
627
|
+
* @param errors - Array of individual errors
|
|
628
|
+
* @param details - Additional error details
|
|
751
629
|
*/
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
630
|
+
constructor(operation: 'create' | 'update' | 'delete', successCount: number, failureCount: number, errors: Array<{
|
|
631
|
+
index: number;
|
|
632
|
+
error: unknown;
|
|
633
|
+
}>, details?: Record<string, unknown>);
|
|
634
|
+
/**
|
|
635
|
+
* Get a summary of the bulk operation failure
|
|
636
|
+
*/
|
|
637
|
+
getSummary(): {
|
|
638
|
+
operation: string;
|
|
639
|
+
total: number;
|
|
640
|
+
successful: number;
|
|
641
|
+
failed: number;
|
|
642
|
+
failureRate: number;
|
|
643
|
+
errors: {
|
|
644
|
+
index: number;
|
|
645
|
+
error: unknown;
|
|
646
|
+
}[];
|
|
647
|
+
};
|
|
755
648
|
}
|
|
756
|
-
/**
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
649
|
+
/**
|
|
650
|
+
* Error thrown when connection to ObjectStack server fails
|
|
651
|
+
*/
|
|
652
|
+
declare class ConnectionError extends ObjectStackError {
|
|
653
|
+
url?: string | undefined;
|
|
654
|
+
constructor(message: string, url?: string | undefined, details?: Record<string, unknown>, statusCode?: number);
|
|
761
655
|
}
|
|
762
|
-
/**
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
656
|
+
/**
|
|
657
|
+
* Error thrown when authentication fails
|
|
658
|
+
*/
|
|
659
|
+
declare class AuthenticationError extends ObjectStackError {
|
|
660
|
+
constructor(message?: string, details?: Record<string, unknown>, statusCode?: number);
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* Error thrown when the ObjectStack data API rejects a write as invalid.
|
|
664
|
+
*
|
|
665
|
+
* NOT the spec's `ValidationError` (`@objectstack/spec/kernel`), whose name this
|
|
666
|
+
* class wore until objectui#3160 (objectstack#4115 ledger batch 6). That one is
|
|
667
|
+
* a plain DATA SHAPE — `{ field, message, code? }`, one entry in a plugin
|
|
668
|
+
* manifest's validation report — and `@object-ui/types` re-exports it under that
|
|
669
|
+
* name. This is a runtime `Error` subclass carrying an HTTP status plus a list
|
|
670
|
+
* of such entries, so the two are not even the same KIND of thing.
|
|
671
|
+
*
|
|
672
|
+
* The name follows the convention registered on objectstack#4115 for this
|
|
673
|
+
* family — `<what was validated>Validation<Error|Result>`. `@object-ui/core`
|
|
674
|
+
* took `SchemaNodeValidationError` for its SDUI-tree walk; this one belongs to
|
|
675
|
+
* the data API.
|
|
676
|
+
*/
|
|
677
|
+
declare class DataApiValidationError extends ObjectStackError {
|
|
678
|
+
field?: string | undefined;
|
|
679
|
+
validationErrors?: Array<{
|
|
680
|
+
field: string;
|
|
681
|
+
message: string;
|
|
682
|
+
}> | undefined;
|
|
771
683
|
/**
|
|
772
|
-
*
|
|
773
|
-
* packages contributing to each type. Computed in the same sweep
|
|
774
|
-
* so a single call serves both the diagnostics governance page and
|
|
775
|
-
* the directory tile counts / package filter.
|
|
684
|
+
* Create a new DataApiValidationError
|
|
776
685
|
*
|
|
777
|
-
*
|
|
778
|
-
*
|
|
779
|
-
*
|
|
686
|
+
* @param message - Human-readable error message
|
|
687
|
+
* @param field - The field that failed validation (optional)
|
|
688
|
+
* @param validationErrors - Array of validation error details
|
|
689
|
+
* @param details - Additional error details
|
|
780
690
|
*/
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
691
|
+
constructor(message: string, field?: string | undefined, validationErrors?: Array<{
|
|
692
|
+
field: string;
|
|
693
|
+
message: string;
|
|
694
|
+
}> | undefined, details?: Record<string, unknown>);
|
|
695
|
+
/**
|
|
696
|
+
* Get all validation errors as a formatted list
|
|
697
|
+
*/
|
|
698
|
+
getValidationErrors(): {
|
|
699
|
+
field: string;
|
|
700
|
+
message: string;
|
|
701
|
+
}[];
|
|
786
702
|
}
|
|
787
|
-
/**
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
703
|
+
/**
|
|
704
|
+
* Helper function to create an error from an HTTP response
|
|
705
|
+
*
|
|
706
|
+
* @param response - Response object or error from fetch/axios
|
|
707
|
+
* @param context - Additional context for debugging
|
|
708
|
+
* @returns Appropriate error instance
|
|
709
|
+
*/
|
|
710
|
+
declare function createErrorFromResponse(response: Record<string, unknown>, context?: string): ObjectStackError;
|
|
711
|
+
/**
|
|
712
|
+
* Type guard to check if an error is an ObjectStackError
|
|
713
|
+
*/
|
|
714
|
+
declare function isObjectStackError(error: unknown): error is ObjectStackError;
|
|
715
|
+
/**
|
|
716
|
+
* Type guard to check if an error is a specific ObjectStack error type
|
|
717
|
+
*/
|
|
718
|
+
declare function isErrorType<T extends ObjectStackError>(error: unknown, errorClass: new (...args: any[]) => T): error is T;
|
|
719
|
+
|
|
720
|
+
/**
|
|
721
|
+
* ObjectUI
|
|
722
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
723
|
+
*
|
|
724
|
+
* This source code is licensed under the MIT license found in the
|
|
725
|
+
* LICENSE file in the root directory of this source tree.
|
|
726
|
+
*/
|
|
727
|
+
/**
|
|
728
|
+
* Statistics reported by {@link MetadataCache.getStats} for monitoring.
|
|
729
|
+
*
|
|
730
|
+
* NOT the spec's `CacheStats` (`@objectstack/spec/contracts`), whose name this
|
|
731
|
+
* interface wore until objectui#3160 (objectstack#4115 ledger batch 6). That one
|
|
732
|
+
* describes the platform's `ICacheService` — a server-side KV cache measured by
|
|
733
|
+
* `keyCount` and `memoryUsage`. This one describes the browser-side LRU in front
|
|
734
|
+
* of `/api/v1/meta/*`: it is bounded (`size`/`maxSize`), it evicts, it coalesces
|
|
735
|
+
* concurrent fetches onto one in-flight promise, and it reports a `hitRate`.
|
|
736
|
+
* Neither type has a key the other has, so this is a name collision, not a
|
|
737
|
+
* dialect — deriving would have replaced every field.
|
|
738
|
+
*/
|
|
739
|
+
interface MetadataCacheStats {
|
|
740
|
+
size: number;
|
|
741
|
+
maxSize: number;
|
|
742
|
+
hits: number;
|
|
743
|
+
misses: number;
|
|
744
|
+
evictions: number;
|
|
745
|
+
/** Number of concurrent fetches that were coalesced onto an in-flight request. */
|
|
746
|
+
coalesced: number;
|
|
747
|
+
hitRate: number;
|
|
797
748
|
}
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
749
|
+
|
|
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;
|
|
803
774
|
}
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
775
|
+
interface PluginExport {
|
|
776
|
+
/** Export name */
|
|
777
|
+
name: string;
|
|
778
|
+
/** Export type */
|
|
779
|
+
type: 'component' | 'hook' | 'utility' | 'provider';
|
|
780
|
+
/** Description */
|
|
781
|
+
description?: string;
|
|
810
782
|
}
|
|
811
|
-
interface
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
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;
|
|
817
800
|
}
|
|
818
801
|
/**
|
|
819
|
-
*
|
|
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
|
+
/**
|
|
811
|
+
* ObjectStack-backed `UserDataAdapter` factory.
|
|
820
812
|
*
|
|
821
|
-
*
|
|
822
|
-
*
|
|
823
|
-
*
|
|
824
|
-
*
|
|
825
|
-
*
|
|
826
|
-
*
|
|
813
|
+
* Persists arbitrary per-user UI state (favorites, recently-accessed items, …)
|
|
814
|
+
* as a single JSON blob per `(user_id, key)` row in the **unified per-user
|
|
815
|
+
* KV store** — the `sys_user_preference` object shipped by every
|
|
816
|
+
* `@objectstack/plugin-auth`-enabled environment.
|
|
817
|
+
*
|
|
818
|
+
* Using the existing `sys_user_preference` table (rather than a parallel
|
|
819
|
+
* `user_app_state` table) keeps things consistent with the platform's
|
|
820
|
+
* "one KV store per scope" pattern:
|
|
821
|
+
*
|
|
822
|
+
* - `sys_setting` ← tenant / env scope
|
|
823
|
+
* - `sys_user_preference` ← per-user scope ← we live here
|
|
824
|
+
*
|
|
825
|
+
* Callers are encouraged to namespace their keys (e.g. `ui.favorites`,
|
|
826
|
+
* `ui.recent`, `ui.grid.account.state`) so explicit settings (`theme`,
|
|
827
|
+
* `locale`) and machine-written UI traces stay easy to tell apart.
|
|
828
|
+
*
|
|
829
|
+
* ## Schema this adapter expects
|
|
830
|
+
*
|
|
831
|
+
* The canonical `sys_user_preference` schema (from
|
|
832
|
+
* `@objectstack/platform-objects`):
|
|
833
|
+
*
|
|
834
|
+
* ```yaml
|
|
835
|
+
* object: sys_user_preference
|
|
836
|
+
* fields:
|
|
837
|
+
* user_id: lookup(sys_user) # indexed
|
|
838
|
+
* key: string # indexed (e.g. "ui.favorites")
|
|
839
|
+
* value: json # the serialised list
|
|
840
|
+
* updated_at: datetime # auto-managed
|
|
841
|
+
* unique: [user_id, key]
|
|
827
842
|
* ```
|
|
843
|
+
*
|
|
844
|
+
* ## Failure modes
|
|
845
|
+
*
|
|
846
|
+
* The adapter is designed to **degrade silently**. Any error — missing
|
|
847
|
+
* schema, 4xx/5xx, network — is caught:
|
|
848
|
+
*
|
|
849
|
+
* - `load()` returns `[]`, so the hosting provider keeps its localStorage state.
|
|
850
|
+
* - `save()` resolves without throwing, so UI mutations never surface a toast.
|
|
851
|
+
*
|
|
852
|
+
* As soon as the backend supports `sys_user_preference`, persistence
|
|
853
|
+
* "lights up" with no code change.
|
|
854
|
+
*
|
|
855
|
+
* @module
|
|
828
856
|
*/
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
/**
|
|
834
|
-
|
|
835
|
-
constructor(config: MetadataClientConfig);
|
|
836
|
-
/** Update the client's environment scope at runtime. */
|
|
837
|
-
withEnvironment(environmentId: string | undefined): MetadataClient;
|
|
838
|
-
/**
|
|
839
|
-
* Derive a client whose reads render the draft-overlaid world (or not).
|
|
840
|
-
* Same base/fetch/headers; used by the app-shell to switch the whole
|
|
841
|
-
* renderer tree into ADR-0037 preview mode off one URL flag.
|
|
842
|
-
*/
|
|
843
|
-
withPreviewDrafts(previewDrafts: boolean): MetadataClient;
|
|
844
|
-
/** List all registered metadata types (returns the registry rows). */
|
|
845
|
-
listTypes(): Promise<unknown[]>;
|
|
846
|
-
/** List items of a metadata type (e.g. `object`, `field`, `view`). */
|
|
847
|
-
list<T = unknown>(type: string, options?: MetadataListOptions): Promise<T[]>;
|
|
848
|
-
/**
|
|
849
|
-
* List pending DRAFT items (ADR-0033) — what an AI authored but nobody
|
|
850
|
-
* published yet. `list()` only sees published/active metadata, so a
|
|
851
|
-
* just-built app package looks empty there; this surfaces the drafts so the
|
|
852
|
-
* console can show a "pending changes" view and draft-aware package contents.
|
|
853
|
-
* Optionally narrow by `packageId` and/or `type`. Returns light headers
|
|
854
|
-
* (no body) carrying `packageId` for grouping.
|
|
855
|
-
*/
|
|
856
|
-
listDrafts(options?: {
|
|
857
|
-
packageId?: string;
|
|
858
|
-
type?: string;
|
|
859
|
-
}): Promise<MetadataDraftHeader[]>;
|
|
860
|
-
/**
|
|
861
|
-
* Get a single metadata item. Returns the unwrapped item content
|
|
862
|
-
* (matching the framework REST handler which calls `res.json(item)`).
|
|
863
|
-
* Returns `null` on 404 to keep the call-site ergonomic.
|
|
864
|
-
*/
|
|
865
|
-
get<T = unknown>(type: string, name: string, options?: MetadataGetOptions): Promise<T | null>;
|
|
866
|
-
/**
|
|
867
|
-
* Read the pending draft body for an item (`?state=draft`). Returns
|
|
868
|
-
* `null` when there is no draft pending. Draft reads do NOT fall
|
|
869
|
-
* back to the published overlay or the artifact registry — a `null`
|
|
870
|
-
* unambiguously means "nothing to publish".
|
|
871
|
-
*
|
|
872
|
-
* Note: the framework wraps draft responses in an envelope
|
|
873
|
-
* `{ type, name, item }` (matching `getMetaItem`); callers should
|
|
874
|
-
* read `.item` to get the body. The legacy `get()` returns the
|
|
875
|
-
* unwrapped body, so this method preserves that asymmetry by
|
|
876
|
-
* returning whatever the server sent.
|
|
877
|
-
*/
|
|
878
|
-
getDraft<T = unknown>(type: string, name: string, options?: {
|
|
879
|
-
packageId?: string;
|
|
880
|
-
}): Promise<T | null>;
|
|
881
|
-
/**
|
|
882
|
-
* Save (PUT) a metadata item. The framework accepts both the bare
|
|
883
|
-
* item payload and the `{ item: ... }` / `{ metadata: ... }`
|
|
884
|
-
* envelopes; we send bare for consistency. Pass `mode: 'draft'` to
|
|
885
|
-
* stage the change without publishing (Studio's "Save" button).
|
|
886
|
-
*/
|
|
887
|
-
save<T = unknown>(type: string, name: string, item: unknown, options?: MetadataClientSaveOptions): Promise<T>;
|
|
888
|
-
/**
|
|
889
|
-
* Publish a single pending draft BY REFERENCE — promotes the draft row for
|
|
890
|
-
* `(type, name)` into the active overlay and drops the draft. Works for ANY
|
|
891
|
-
* draft, including ones with no `packageId` binding (which the package-scoped
|
|
892
|
-
* `/packages/:id/publish-drafts` flow cannot reach). Use this to publish the
|
|
893
|
-
* exact set returned by {@link listDrafts} without needing a package.
|
|
894
|
-
*
|
|
895
|
-
* Returns the server result. For `seed` drafts the protocol also materializes
|
|
896
|
-
* the rows and reports under `seedApplied` — a data problem never fails the
|
|
897
|
-
* publish, so callers should check `seedApplied?.success` and warn the user
|
|
898
|
-
* rather than assume the data went live.
|
|
899
|
-
*/
|
|
900
|
-
publishDraft(type: string, name: string): Promise<{
|
|
901
|
-
success?: boolean;
|
|
902
|
-
seedApplied?: {
|
|
903
|
-
success: boolean;
|
|
904
|
-
inserted?: number;
|
|
905
|
-
updated?: number;
|
|
906
|
-
error?: string;
|
|
907
|
-
errors?: unknown[];
|
|
908
|
-
};
|
|
909
|
-
} & Record<string, unknown>>;
|
|
857
|
+
|
|
858
|
+
interface ObjectStackUserStateAdapterOptions {
|
|
859
|
+
/** Connected data source (usually the one provided by `<AdapterProvider>`). */
|
|
860
|
+
dataSource: DataSource<any>;
|
|
861
|
+
/** Authenticated user id. */
|
|
862
|
+
userId: string;
|
|
910
863
|
/**
|
|
911
|
-
*
|
|
912
|
-
*
|
|
913
|
-
*
|
|
864
|
+
* Storage key. Should be a dotted, namespaced string so UI traces
|
|
865
|
+
* don't collide with user-facing preferences. Examples:
|
|
866
|
+
* `ui.favorites`, `ui.recent`, `ui.grid.account.state`.
|
|
914
867
|
*/
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
868
|
+
key: string;
|
|
869
|
+
/** Override the storage object name. Defaults to `"sys_user_preference"`. */
|
|
870
|
+
resource?: string;
|
|
918
871
|
/**
|
|
919
|
-
*
|
|
920
|
-
*
|
|
921
|
-
* considers invalid (or, when `severity: 'warning'`, also entries
|
|
922
|
-
* with only warnings).
|
|
923
|
-
*
|
|
924
|
-
* Used by the Studio's governance overview page and by the
|
|
925
|
-
* directory page to show "N invalid" badges per metadata type.
|
|
872
|
+
* Optional console logger for development diagnostics. Defaults to noop so
|
|
873
|
+
* production builds stay quiet.
|
|
926
874
|
*/
|
|
927
|
-
|
|
875
|
+
onError?: (where: 'load' | 'save', error: unknown) => void;
|
|
876
|
+
}
|
|
877
|
+
interface UserDataAdapter<T> {
|
|
878
|
+
load(): Promise<T[]>;
|
|
879
|
+
save(items: T[]): Promise<void>;
|
|
880
|
+
}
|
|
881
|
+
/**
|
|
882
|
+
* Build a `UserDataAdapter<T>` backed by ObjectStack.
|
|
883
|
+
*
|
|
884
|
+
* Each adapter instance is bound to a single `(user, key)` pair; create
|
|
885
|
+
* one per slot you want to persist.
|
|
886
|
+
*/
|
|
887
|
+
declare function createObjectStackUserStateAdapter<T = unknown>(options: ObjectStackUserStateAdapterOptions): UserDataAdapter<T>;
|
|
888
|
+
|
|
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;
|
|
928
955
|
/**
|
|
929
|
-
*
|
|
930
|
-
* for pre-delete impact analysis: "Are any views pointing at this
|
|
931
|
-
* object before I drop it?".
|
|
956
|
+
* Register a new integration.
|
|
932
957
|
*/
|
|
933
|
-
|
|
958
|
+
register(id: string, config: IntegrationConfig): void;
|
|
934
959
|
/**
|
|
935
|
-
*
|
|
936
|
-
* Idempotent: returns the result even when no overlay row existed.
|
|
937
|
-
* Pass `state: 'draft'` to discard the pending draft only (keeps the
|
|
938
|
-
* published overlay intact) — useful for a Studio "Discard draft" button.
|
|
960
|
+
* Remove an integration.
|
|
939
961
|
*/
|
|
940
|
-
|
|
962
|
+
unregister(id: string): void;
|
|
941
963
|
/**
|
|
942
|
-
*
|
|
943
|
-
* (POST `/meta/:type/:name/publish`). Returns
|
|
944
|
-
* `{ success, version, seq, message }`. Throws a `404 no_draft` if
|
|
945
|
-
* nothing is pending and `409 metadata_conflict` if the published
|
|
946
|
-
* overlay moved while the draft was sitting.
|
|
964
|
+
* Get all registered integrations.
|
|
947
965
|
*/
|
|
948
|
-
|
|
949
|
-
actor?: string;
|
|
950
|
-
message?: string;
|
|
951
|
-
}): Promise<T>;
|
|
966
|
+
getAll(): Map<string, IntegrationConfig>;
|
|
952
967
|
/**
|
|
953
|
-
*
|
|
954
|
-
* The server reads the history row at `toVersion`, writes its body back
|
|
955
|
-
* as the active overlay with `operation_type='revert'`. Returns
|
|
956
|
-
* `{ success, version, seq, restoredFromVersion, message }`. Throws
|
|
957
|
-
* `404 version_not_found` for unknown versions and `409 version_not_restorable`
|
|
958
|
-
* when the target version is a delete tombstone.
|
|
968
|
+
* Get integrations that match a specific event.
|
|
959
969
|
*/
|
|
960
|
-
|
|
961
|
-
actor?: string;
|
|
962
|
-
message?: string;
|
|
963
|
-
}): Promise<T>;
|
|
970
|
+
getForEvent(event: string): IntegrationConfig[];
|
|
964
971
|
/**
|
|
965
|
-
*
|
|
966
|
-
*
|
|
967
|
-
* `{ added, removed, changed }` where each entry carries a `path` and
|
|
968
|
-
* the relevant value(s). Useful for "what changed in this draft?" and
|
|
969
|
-
* pre-rollback previews.
|
|
972
|
+
* Dispatch an event to all matching integrations.
|
|
973
|
+
* Returns results for each integration.
|
|
970
974
|
*/
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
975
|
+
dispatch(event: string, payload: Record<string, unknown>): Promise<Array<{
|
|
976
|
+
id: string;
|
|
977
|
+
success: boolean;
|
|
978
|
+
error?: string;
|
|
979
|
+
}>>;
|
|
974
980
|
/**
|
|
975
|
-
*
|
|
976
|
-
* (ADR-0010 §3.6 / Phase 4.1). Shows every save/publish/rollback/
|
|
977
|
-
* delete/reset attempt — allowed, denied, or forced — so the
|
|
978
|
-
* Studio "审计日志 / Audit log" tab can render who tried what
|
|
979
|
-
* and whether a lock blocked it.
|
|
981
|
+
* Send payload to a specific integration.
|
|
980
982
|
*/
|
|
981
|
-
|
|
982
|
-
limit?: number;
|
|
983
|
-
}): Promise<MetadataAuditResponse>;
|
|
983
|
+
private send;
|
|
984
984
|
}
|
|
985
985
|
|
|
986
986
|
/**
|
|
@@ -1320,6 +1320,83 @@ declare function readTransactionalBatchCapability(discovery: unknown): boolean |
|
|
|
1320
1320
|
* tripping on the property-name mismatch.
|
|
1321
1321
|
*/
|
|
1322
1322
|
declare function is404Error(error: unknown): boolean;
|
|
1323
|
+
/**
|
|
1324
|
+
* The two denials the server derives from an object's `enable` block —
|
|
1325
|
+
* `apiAccessDenialFromEnable` (objectstack `packages/rest/src/rest-server.ts`).
|
|
1326
|
+
*
|
|
1327
|
+
* - `OBJECT_API_DISABLED` (404) — `enable.apiEnabled: false`; the object is
|
|
1328
|
+
* not exposed over the data API at all.
|
|
1329
|
+
* - `OBJECT_API_METHOD_NOT_ALLOWED` (405) — the operation is absent from the
|
|
1330
|
+
* `enable.apiMethods` whitelist.
|
|
1331
|
+
*
|
|
1332
|
+
* Both are **pure functions of the object's metadata**: no user, no permission,
|
|
1333
|
+
* no context, no request body. So neither is transient and neither is
|
|
1334
|
+
* per-user — when one happens it is a permanent property of that object, and
|
|
1335
|
+
* every retry of every persona gets the identical answer.
|
|
1336
|
+
*
|
|
1337
|
+
* That is exactly why they must not be degraded into "no data". A 404 from a
|
|
1338
|
+
* missing collection means *this backend doesn't have that table*, which the
|
|
1339
|
+
* optional-collection probes below legitimately read as "feature unavailable";
|
|
1340
|
+
* a 404 from `OBJECT_API_DISABLED` means *this page can never work*. Answering
|
|
1341
|
+
* the second with an empty result set renders "you have no records" over a
|
|
1342
|
+
* surface that is not allowed to have any (objectui#4408 — it also hid the
|
|
1343
|
+
* upstream defect objectstack#7544 for its entire life, because a merely
|
|
1344
|
+
* unpopulated page invites nobody to click through).
|
|
1345
|
+
*/
|
|
1346
|
+
declare const API_ACCESS_DENIED_CODES: readonly ["OBJECT_API_DISABLED", "OBJECT_API_METHOD_NOT_ALLOWED"];
|
|
1347
|
+
/**
|
|
1348
|
+
* True when `error` is an `enable`-block API denial (see
|
|
1349
|
+
* {@link API_ACCESS_DENIED_CODES}).
|
|
1350
|
+
*
|
|
1351
|
+
* Discriminates on the ADR-0112 `code`, never on the status: 404 alone cannot
|
|
1352
|
+
* separate a disabled object from a missing collection, and 405 alone cannot
|
|
1353
|
+
* separate a withheld method from any other method rejection. The code survives
|
|
1354
|
+
* the transport — `@objectstack/client`'s fetch wrapper stamps `error.code`
|
|
1355
|
+
* from the response envelope, and both spellings are declared members of the
|
|
1356
|
+
* spec's `StandardErrorCode` — so no heuristic on status is needed or wanted.
|
|
1357
|
+
*/
|
|
1358
|
+
declare function isApiAccessDeniedError(error: unknown): boolean;
|
|
1359
|
+
/**
|
|
1360
|
+
* What the by-name meta app route said about THIS session's access to an app
|
|
1361
|
+
* (objectui#4252 / objectstack#8013).
|
|
1362
|
+
*
|
|
1363
|
+
* - `granted` — the route served the app document.
|
|
1364
|
+
* - `denied` — the app EXISTS and the session lacks its `requiredPermissions`.
|
|
1365
|
+
* The only verdict a caller may render as an authorization refusal.
|
|
1366
|
+
* - `unknown` — anything else: an absent app, an unpublished one, an app
|
|
1367
|
+
* withheld by an absent optional service, an unreachable server, an adapter
|
|
1368
|
+
* that cannot ask. All of these are cases where the server declined to say
|
|
1369
|
+
* that a permission of the caller's is missing, so no caller may claim it.
|
|
1370
|
+
*
|
|
1371
|
+
* Three values rather than a boolean because the third is not a shade of the
|
|
1372
|
+
* other two: "the app is missing" and "I could not find out" both have to leave
|
|
1373
|
+
* the caller's existing copy alone, and collapsing them into `false` invites a
|
|
1374
|
+
* consumer to read a failed probe as a positive absence.
|
|
1375
|
+
*/
|
|
1376
|
+
type AppAccessVerdict = 'granted' | 'denied' | 'unknown';
|
|
1377
|
+
/**
|
|
1378
|
+
* The ADR-0112 standard catalog code the by-name meta app route answers with
|
|
1379
|
+
* when an app exists and the session lacks its `requiredPermissions`
|
|
1380
|
+
* (objectstack#8013, `sendError(res, 403, 'PERMISSION_DENIED', …)` in
|
|
1381
|
+
* `packages/rest/src/rest-server.ts`).
|
|
1382
|
+
*
|
|
1383
|
+
* Deliberately NOT a member of {@link API_ACCESS_DENIED_CODES}: those two are
|
|
1384
|
+
* pure functions of an object's `enable` metadata — permanent, identical for
|
|
1385
|
+
* every persona — whereas this one is a statement about the CALLER, and the same
|
|
1386
|
+
* request by a different session succeeds. Same word "denied", different
|
|
1387
|
+
* question, so a consumer that wants one must never match the other.
|
|
1388
|
+
*/
|
|
1389
|
+
declare const APP_PERMISSION_DENIED_CODE = "PERMISSION_DENIED";
|
|
1390
|
+
/**
|
|
1391
|
+
* True when `error` is the by-name app route's permission denial.
|
|
1392
|
+
*
|
|
1393
|
+
* Discriminates on the ADR-0112 `code`, never on the status (objectui#4408): the
|
|
1394
|
+
* route answers 403 for this and 404 for absence today, but a status is a
|
|
1395
|
+
* transport fact many conditions share, while the code is the contract. The
|
|
1396
|
+
* console's whole reason to call this is to tell two REFUSALS apart, and both
|
|
1397
|
+
* are errors.
|
|
1398
|
+
*/
|
|
1399
|
+
declare function isAppPermissionDeniedError(error: unknown): boolean;
|
|
1323
1400
|
/**
|
|
1324
1401
|
* Thrown when the deployment has no analytics capability installed
|
|
1325
1402
|
* (framework#3891 / #4019).
|
|
@@ -1434,6 +1511,50 @@ declare function isConcurrentUpdateError(error: unknown): error is ConcurrentUpd
|
|
|
1434
1511
|
* instead of showing one undirected toast.
|
|
1435
1512
|
*/
|
|
1436
1513
|
declare function normaliseClientError(error: unknown): unknown;
|
|
1514
|
+
/**
|
|
1515
|
+
* Fold an @objectstack/client HTTP-failure `meta` bag into the log MESSAGE.
|
|
1516
|
+
*
|
|
1517
|
+
* The client already hands us everything worth knowing —
|
|
1518
|
+
* `logger.error("HTTP request failed", undefined, { method, url, status, error })`
|
|
1519
|
+
* — but it hands it as the THIRD argument. Everything that flattens a console
|
|
1520
|
+
* record to text (a headless/CDP console capture, a log shipper, a copied
|
|
1521
|
+
* DevTools line) keeps only the message and renders the rest as `[object
|
|
1522
|
+
* Object]` / `Object`, so a wall of failures carried no method, no URL and no
|
|
1523
|
+
* status: the reporter of objectui#4042 had to diff the network panel by hand
|
|
1524
|
+
* to find out that 30 red lines were all one benign pre-login burst.
|
|
1525
|
+
*
|
|
1526
|
+
* So the identifying fields go into the string itself, and the structured bag
|
|
1527
|
+
* is STILL passed alongside for DevTools to expand — text for the flatteners,
|
|
1528
|
+
* object for the inspectors, neither at the other's expense.
|
|
1529
|
+
*
|
|
1530
|
+
* Exported for tests. Returns `null` when `meta` carries none of the three
|
|
1531
|
+
* fields, so callers keep the original message rather than printing a husk.
|
|
1532
|
+
*/
|
|
1533
|
+
declare function formatHttpFailureMessage(message: string, meta?: Record<string, any>): string | null;
|
|
1534
|
+
/**
|
|
1535
|
+
* Build a Logger compatible with @objectstack/client that (a) spells every
|
|
1536
|
+
* request failure out in the message — see {@link formatHttpFailureMessage} —
|
|
1537
|
+
* and (b) demotes expected 404 noise to console.debug. The client logs every
|
|
1538
|
+
* non-2xx response with
|
|
1539
|
+
* `logger.error("HTTP request failed", undefined, { method, url, status, error })`,
|
|
1540
|
+
* but 404s on optional collections (sys_presence, sys_activity, …) are part of
|
|
1541
|
+
* normal degraded operation when those plugins aren't installed on the
|
|
1542
|
+
* server — they should not surface as errors in the browser DevTools.
|
|
1543
|
+
*
|
|
1544
|
+
* NOTE the asymmetry, and keep it: 404-on-an-optional-collection is demoted
|
|
1545
|
+
* because it is an EXPECTED outcome of a request we still mean to make. No
|
|
1546
|
+
* other status is demoted — a 401 that survives the console's session gate
|
|
1547
|
+
* (objectui#4042: a mid-session expiry, say) is a real event and must stay a
|
|
1548
|
+
* visible, fully-identified error. The cure for doomed requests is not issuing
|
|
1549
|
+
* them, never hiding them once issued.
|
|
1550
|
+
*
|
|
1551
|
+
* Returned object is loosely typed because the spec's Logger interface lives
|
|
1552
|
+
* in a transitive package; using `any` keeps us decoupled.
|
|
1553
|
+
*
|
|
1554
|
+
* Exported so the console's log contract is testable, and so an app wiring its
|
|
1555
|
+
* own `ObjectStackClient` gets the same identified failures.
|
|
1556
|
+
*/
|
|
1557
|
+
declare function createQuietHttpLogger(): any;
|
|
1437
1558
|
/**
|
|
1438
1559
|
* Connection state for monitoring
|
|
1439
1560
|
*/
|
|
@@ -1480,6 +1601,36 @@ interface WriteWarningEvent {
|
|
|
1480
1601
|
/** Event listener type for write-warning (dropped-fields) events. */
|
|
1481
1602
|
type WriteWarningListener = (event: WriteWarningEvent) => void;
|
|
1482
1603
|
|
|
1604
|
+
/**
|
|
1605
|
+
* Resolve which object a `type='view'` metadata item belongs to.
|
|
1606
|
+
*
|
|
1607
|
+
* The metadata index is name-only, not field-typed: `GET /api/v1/meta/view`
|
|
1608
|
+
* accepts `?package=` and `?preview=draft` and nothing else (measured on
|
|
1609
|
+
* framework `packages/rest/src/rest-server.ts` — the `GET /meta/:type`
|
|
1610
|
+
* handler — and on `client.meta.getItems(type, { packageId })`). So every
|
|
1611
|
+
* reader of the view namespace enumerates `type='view'` once and narrows to
|
|
1612
|
+
* one object HERE, client-side.
|
|
1613
|
+
*
|
|
1614
|
+
* ONE spelling, one place, deliberately: {@link ObjectStackAdapter.listViews}
|
|
1615
|
+
* and {@link ObjectStackAdapter.listViewOverrides} read the same rows out of
|
|
1616
|
+
* the same namespace, and two private copies of "which object is this?" is a
|
|
1617
|
+
* drift waiting to happen — the switcher showing a view whose override the
|
|
1618
|
+
* grid cannot find, or the reverse.
|
|
1619
|
+
*
|
|
1620
|
+
* `object` is the identity field the write path stamps (and that the
|
|
1621
|
+
* framework's overlay heals onto identity-less personalization rows —
|
|
1622
|
+
* objectstack#2555); `data.object` is the config's data-provider target and
|
|
1623
|
+
* `objectName` the legacy artifact spelling.
|
|
1624
|
+
*
|
|
1625
|
+
* **Exported** since objectui#4373, for the same one-spelling reason: a writer
|
|
1626
|
+
* outside this module that holds a view BODY but not its object name (app-shell's
|
|
1627
|
+
* `MetadataService`) needs the object to name the keys
|
|
1628
|
+
* {@link ObjectStackAdapter.invalidateViewKeys} drops, and a fourth private copy
|
|
1629
|
+
* of "which object is this?" is exactly the drift this accessor exists to
|
|
1630
|
+
* prevent. Identity only — it names no cache key.
|
|
1631
|
+
*/
|
|
1632
|
+
declare function viewItemObjectName(item: any): string | undefined;
|
|
1633
|
+
|
|
1483
1634
|
/**
|
|
1484
1635
|
* ObjectStack Data Source Adapter
|
|
1485
1636
|
*
|
|
@@ -1532,6 +1683,7 @@ declare class ObjectStackAdapter<T = unknown> implements DataSource<T> {
|
|
|
1532
1683
|
private atomicBatchCapability;
|
|
1533
1684
|
private mutationListeners;
|
|
1534
1685
|
private writeWarningListeners;
|
|
1686
|
+
private saveAdvisoryListeners;
|
|
1535
1687
|
constructor(config: {
|
|
1536
1688
|
baseUrl: string;
|
|
1537
1689
|
token?: string;
|
|
@@ -1652,6 +1804,82 @@ declare class ObjectStackAdapter<T = unknown> implements DataSource<T> {
|
|
|
1652
1804
|
* this to toast the user; the write itself already succeeded.
|
|
1653
1805
|
*/
|
|
1654
1806
|
onWriteWarning(callback: WriteWarningListener): () => void;
|
|
1807
|
+
/**
|
|
1808
|
+
* Subscribe to metadata save-advisory events — the runtime authoring gate's
|
|
1809
|
+
* advisory findings on a save that SUCCEEDED (#4237; backend
|
|
1810
|
+
* objectstack#7435). Returns an unsubscribe function.
|
|
1811
|
+
*
|
|
1812
|
+
* Deliberately the same seam as {@link onWriteWarning} (#3431/#3455), which
|
|
1813
|
+
* is what {@link MetadataSaveAdvisoryEvent}'s own declaration already said it
|
|
1814
|
+
* was modelled on. It is a SIBLING of that channel rather than a second
|
|
1815
|
+
* payload pushed down it: `WriteWarningEvent` is a closed shape whose
|
|
1816
|
+
* `droppedFields` is required and means "fields the write legally stripped",
|
|
1817
|
+
* so carrying advisories on it would either force every existing
|
|
1818
|
+
* `onWriteWarning` consumer to grow a branch or make the event lie about what
|
|
1819
|
+
* happened. The seam's SHAPE is what is reused here — a long-lived instance
|
|
1820
|
+
* with a `subscribe → unsubscribe` registration that `AdapterProvider` wires
|
|
1821
|
+
* once — not its event type.
|
|
1822
|
+
*
|
|
1823
|
+
* Why here and not on the config, which is how the other client class does it
|
|
1824
|
+
* (#4133/#4236): `MetadataClient` is minted per component by
|
|
1825
|
+
* `useMetadataClient`, so it has no instance to subscribe to and its sink
|
|
1826
|
+
* rides the factory. `ObjectStackAdapter` is the opposite — one long-lived
|
|
1827
|
+
* instance per app, already carrying this exact subscription pattern.
|
|
1828
|
+
*/
|
|
1829
|
+
onSaveAdvisory(callback: MetadataSaveAdvisoryListener): () => void;
|
|
1830
|
+
/**
|
|
1831
|
+
* Notify all save-advisory subscribers. Isolated exactly like
|
|
1832
|
+
* {@link emitWriteWarning}: a throwing listener must neither break the save
|
|
1833
|
+
* nor starve the others.
|
|
1834
|
+
*/
|
|
1835
|
+
private emitSaveAdvisory;
|
|
1836
|
+
/**
|
|
1837
|
+
* Install the ONE emitter for the metadata save door (#4237).
|
|
1838
|
+
*
|
|
1839
|
+
* ## Why this seam, and what it covers
|
|
1840
|
+
*
|
|
1841
|
+
* `ObjectStackClient.meta.saveItem` is the second client class that writes
|
|
1842
|
+
* through `PUT /api/v1/meta/:type/:name`, and every one of its callers reaches
|
|
1843
|
+
* it through an adapter this class constructed — the four inside this file
|
|
1844
|
+
* (`updateViewConfig`, the two view paths, `updateDashboard`) via
|
|
1845
|
+
* `this.client`, and every caller outside it via {@link getClient}, which
|
|
1846
|
+
* hands back this same instance: `MetadataService` (app-shell, five saves),
|
|
1847
|
+
* `useNavigationSync`, and plugin-designer's Create/EditAppPage. Wrapping the
|
|
1848
|
+
* method once here therefore covers all of them WITHOUT a per-site edit, which
|
|
1849
|
+
* is the whole point — a toast copied into a dozen call sites is the shape
|
|
1850
|
+
* #4133 rejected for the other client class and it is no better here.
|
|
1851
|
+
*
|
|
1852
|
+
* `meta` is an own, writable property assigned per instance in the SDK's
|
|
1853
|
+
* constructor (`this.meta = { … }`), and the client this adapter builds is
|
|
1854
|
+
* never shared, so the wrap is bounded to an object this adapter owns for its
|
|
1855
|
+
* whole lifetime. It is not a prototype or global patch.
|
|
1856
|
+
*
|
|
1857
|
+
* ## Response shape — measured, not assumed
|
|
1858
|
+
*
|
|
1859
|
+
* The two client classes' envelopes coincide at the top level, which is what
|
|
1860
|
+
* makes `readSaveAdvisories` reusable unchanged across both. `SaveMetaItem-
|
|
1861
|
+
* ResponseSchema` puts `advisories` at the body's top level next to
|
|
1862
|
+
* `success` / `version` / `seq` / `state`, and the SDK's `unwrapResponse`
|
|
1863
|
+
* strips its `{ success, data }` envelope only when the body actually HAS a
|
|
1864
|
+
* `data` key — this body does not, so it is returned verbatim. So the same
|
|
1865
|
+
* reader that `MetadataClient.save` uses reads this response correctly, and
|
|
1866
|
+
* the pins in `onSaveAdvisory.test.ts` drive a real SDK client through a fake
|
|
1867
|
+
* `fetch` rather than stubbing `meta`, so that continues to be measured.
|
|
1868
|
+
*
|
|
1869
|
+
* ## Draft-door honesty (D1)
|
|
1870
|
+
*
|
|
1871
|
+
* Drafts are NEVER gated: the framework returns at its D1 early-return
|
|
1872
|
+
* (`if (args.state !== 'active') return null`) before running a rule, so a
|
|
1873
|
+
* draft save produces no findings to withhold. This client class has no draft
|
|
1874
|
+
* door at all to worry about — the SDK's `saveItem(type, name, item)` takes no
|
|
1875
|
+
* mode and always writes the active door, which is exactly why the gate DOES
|
|
1876
|
+
* run for its callers. `mode` on the emitted event is therefore derived from
|
|
1877
|
+
* the response's own `state` rather than from a request-side flag that does
|
|
1878
|
+
* not exist here: `'draft'` when the server says the row landed as a draft,
|
|
1879
|
+
* `'publish'` otherwise. That keeps the event truthful about which door it
|
|
1880
|
+
* came through instead of hard-coding one.
|
|
1881
|
+
*/
|
|
1882
|
+
private installSaveAdvisoryInterceptor;
|
|
1655
1883
|
create(resource: string, data: Partial<T>): Promise<T>;
|
|
1656
1884
|
/**
|
|
1657
1885
|
* Update an existing record.
|
|
@@ -1887,23 +2115,90 @@ declare class ObjectStackAdapter<T = unknown> implements DataSource<T> {
|
|
|
1887
2115
|
* @returns Promise resolving to discovery data, or null if not connected
|
|
1888
2116
|
*/
|
|
1889
2117
|
getDiscovery(): Promise<unknown | null>;
|
|
2118
|
+
/**
|
|
2119
|
+
* **The one place that knows which cache keys a view-row write invalidates.**
|
|
2120
|
+
*
|
|
2121
|
+
* Two reads are cached under view-shaped keys, and both go stale when any
|
|
2122
|
+
* writer touches a view row for `objectName`:
|
|
2123
|
+
*
|
|
2124
|
+
* | reader | key |
|
|
2125
|
+
* |----------------------------|----------------------------|
|
|
2126
|
+
* | {@link getView} | `view:{object}:{viewName}` |
|
|
2127
|
+
* | {@link listViewOverrides} | `view-overrides:{object}` |
|
|
2128
|
+
*
|
|
2129
|
+
* Every writer routes here rather than restating that pair. The repo has now
|
|
2130
|
+
* paid twice for restatement: objectui#3778 removed five copies of a key
|
|
2131
|
+
* (`views:{object}`) that no reader had ever populated, and objectui#4363
|
|
2132
|
+
* fixed four copies that named only half the live set — and objectui#4373 is
|
|
2133
|
+
* the measured proof that the failure recurs by default, because the console's
|
|
2134
|
+
* real create/publish flow writes these same rows through the ADR-0034
|
|
2135
|
+
* metadata seam and never learned the key list at all. A pin suite can only
|
|
2136
|
+
* guard the writers that exist; a mandatory seam makes the next writer
|
|
2137
|
+
* structurally unable to forget.
|
|
2138
|
+
*
|
|
2139
|
+
* The rule is uniform per WRITE, not per branch: a write to a view row drops
|
|
2140
|
+
* both keys, in that order. Draft-addressed writes over-invalidate on purpose
|
|
2141
|
+
* — both readers enumerate PUBLISHED rows, so a draft write stales neither —
|
|
2142
|
+
* because the costs are not symmetric. An unnecessary invalidation costs one
|
|
2143
|
+
* refetch; a missed one costs up to the cache's 5-minute TTL of stale
|
|
2144
|
+
* overrides, and "which half am I in?" is not a question a future writer
|
|
2145
|
+
* should have to re-answer to stay correct.
|
|
2146
|
+
*
|
|
2147
|
+
* Public because the writers are not all in this class: app-shell's ADR-0034
|
|
2148
|
+
* seam (`runtime-metadata-persistence.ts`) and `MetadataService` write the
|
|
2149
|
+
* same rows through `client.meta` / the `/meta` draft-publish API, and they
|
|
2150
|
+
* reach this adapter as the `dataSource` they were already handed.
|
|
2151
|
+
*
|
|
2152
|
+
* @param objectName - Object the view belongs to (the `{object}` in both keys)
|
|
2153
|
+
* @param viewName - The view's canonical `name` — the identity
|
|
2154
|
+
* {@link getView} reads back by and {@link listViewOverrides} keys its map
|
|
2155
|
+
* with. Callers that hold a qualified `<object>.<key>` name pass it as-is:
|
|
2156
|
+
* it is the row key, not a path to be split.
|
|
2157
|
+
*/
|
|
2158
|
+
invalidateViewKeys(objectName: string, viewName: string): void;
|
|
1890
2159
|
/**
|
|
1891
2160
|
* Batch-fetch all persisted view overrides for an object.
|
|
1892
2161
|
*
|
|
1893
|
-
* Per-view runtime overrides (density, column widths, sort,
|
|
1894
|
-
*
|
|
1895
|
-
*
|
|
1896
|
-
*
|
|
1897
|
-
*
|
|
1898
|
-
*
|
|
1899
|
-
* returns
|
|
1900
|
-
*
|
|
2162
|
+
* Per-view runtime overrides (density, column widths, sort, hidden
|
|
2163
|
+
* columns, inlineEdit …) live in the SAME metadata namespace the
|
|
2164
|
+
* write path uses: `type='view'`, `name=<viewId>` (see
|
|
2165
|
+
* {@link updateViewConfig}). Loading them per-view fires N HTTP GETs
|
|
2166
|
+
* that 404 for every view the user never customized — console noise on
|
|
2167
|
+
* every page load. This batch method performs a single
|
|
2168
|
+
* `GET /api/v1/meta/view` (returns `{type, items}`) and narrows the
|
|
2169
|
+
* result to `objectName` client-side, exactly as {@link listViews}
|
|
2170
|
+
* does over the same rows (shared accessor: {@link viewItemObjectName}).
|
|
2171
|
+
*
|
|
2172
|
+
* objectui#3774 — this used to enumerate `GET /api/v1/meta/<objectName>`,
|
|
2173
|
+
* putting the OBJECT name in the metadata TYPE slot. That key space is
|
|
2174
|
+
* disjoint from the one the write path lands in, so the batch map came
|
|
2175
|
+
* back empty for every object, forever, and every saved personalization
|
|
2176
|
+
* read back as "setting didn't save".
|
|
1901
2177
|
*
|
|
1902
|
-
*
|
|
1903
|
-
*
|
|
2178
|
+
* FAILURES REJECT — they are not answered as `{}`. An empty map is an
|
|
2179
|
+
* authoritative "this object has no overrides" (callers may trust it and
|
|
2180
|
+
* skip the per-view reads); a transport/permission failure is "I could
|
|
2181
|
+
* not tell", and reporting the two as the same value is what made
|
|
2182
|
+
* ObjectView's per-view {@link getView} fallback unreachable code. When
|
|
2183
|
+
* we cannot tell, we do not pretend we can. Rejections are not cached
|
|
2184
|
+
* (the cache stores on success only), so a transient failure does not
|
|
2185
|
+
* pin an empty answer for the TTL.
|
|
2186
|
+
*
|
|
2187
|
+
* Result is cached identically to {@link getView}, and EVERY view write —
|
|
2188
|
+
* on this adapter and above it — invalidates it through the one seam,
|
|
2189
|
+
* {@link invalidateViewKeys}. For a long time only {@link updateViewConfig}
|
|
2190
|
+
* did (objectui#4363), which left the other three adapter paths stale
|
|
2191
|
+
* for the cache's 5-minute TTL. That gap does not self-heal: the consumer
|
|
2192
|
+
* (`loadViewOverrides`, app-shell `ObjectView`) treats a RESOLVED map as
|
|
2193
|
+
* authoritative and deliberately does not re-probe per view — objectui#3774,
|
|
2194
|
+
* and correct, since re-probing reinstates the 404 flurry the batch read
|
|
2195
|
+
* exists to remove. So a stale map here is served in full, and the per-view
|
|
2196
|
+
* {@link getView} fallback that would have masked it never runs.
|
|
1904
2197
|
*
|
|
1905
2198
|
* @param objectName - Object name (e.g. 'lead')
|
|
1906
2199
|
* @returns Map keyed by view name with the persisted override config
|
|
2200
|
+
* @throws whatever the metadata transport throws — callers that have a
|
|
2201
|
+
* per-view fallback should catch and use it.
|
|
1907
2202
|
*/
|
|
1908
2203
|
listViewOverrides(objectName: string): Promise<Record<string, any>>;
|
|
1909
2204
|
/**
|
|
@@ -1942,8 +2237,10 @@ declare class ObjectStackAdapter<T = unknown> implements DataSource<T> {
|
|
|
1942
2237
|
* match the view spec shape.
|
|
1943
2238
|
*
|
|
1944
2239
|
* Returns view spec objects with their canonical `name` as identifier.
|
|
1945
|
-
*
|
|
1946
|
-
*
|
|
2240
|
+
* Narrows to one object client-side via {@link viewItemObjectName} —
|
|
2241
|
+
* the metadata index is name-only, not field-typed, so the route has no
|
|
2242
|
+
* `?object=` to push the filter down into. {@link listViewOverrides}
|
|
2243
|
+
* reads the same rows through the same accessor.
|
|
1947
2244
|
*/
|
|
1948
2245
|
listViews(objectName: string, options?: {
|
|
1949
2246
|
previewDrafts?: boolean;
|
|
@@ -1971,23 +2268,127 @@ declare class ObjectStackAdapter<T = unknown> implements DataSource<T> {
|
|
|
1971
2268
|
*
|
|
1972
2269
|
* Generates a snake_case name if `spec.name` is not provided by appending
|
|
1973
2270
|
* a short timestamp suffix to the source-name hint.
|
|
2271
|
+
*
|
|
2272
|
+
* Invalidates through {@link invalidateViewKeys}, like every other writer —
|
|
2273
|
+
* see {@link listViewOverrides} for why the batch map is the one that cannot
|
|
2274
|
+
* heal itself (objectui#4363).
|
|
1974
2275
|
*/
|
|
1975
2276
|
createView(objectName: string, spec: Record<string, any>): Promise<Record<string, any> | void>;
|
|
1976
2277
|
/**
|
|
1977
2278
|
* Apply a partial update to an existing overlay view. Reads the current
|
|
1978
|
-
*
|
|
1979
|
-
*
|
|
1980
|
-
*
|
|
2279
|
+
* document, merges, and writes it back. ADR-0005 overlay rows store the
|
|
2280
|
+
* *full* view document, so partial updates require a read-merge-write cycle.
|
|
2281
|
+
*
|
|
2282
|
+
* **Both halves address the same row (#4139).** A view has two possible
|
|
2283
|
+
* homes and the read must resolve the one the write will target:
|
|
2284
|
+
*
|
|
2285
|
+
* - a pending per-item **draft** (`?state=draft` / `?mode=draft`) — where
|
|
2286
|
+
* ADR-0034 stages every runtime-created view, so a view made from the `+`
|
|
2287
|
+
* tab lives ONLY here until an explicit Publish;
|
|
2288
|
+
* - the **published** overlay (`client.meta.getItem` / `saveItem`).
|
|
2289
|
+
*
|
|
2290
|
+
* The draft is probed FIRST, and a hit is merged and written straight back
|
|
2291
|
+
* as a draft. Two things that ordering buys, both load-bearing:
|
|
2292
|
+
*
|
|
2293
|
+
* 1. A draft-only view is no longer invisible to the read. It used to 404,
|
|
2294
|
+
* and a `catch {}` labelled "treat missing as create-equivalent"
|
|
2295
|
+
* substituted `current = {}` — so a rename merged onto nothing and went
|
|
2296
|
+
* out as a `{label, name, object}` partial the server rejects (422),
|
|
2297
|
+
* while the draft row the UI reads back through `?preview=draft` kept the
|
|
2298
|
+
* old label. The edit was lost with no error surfaced to the user.
|
|
2299
|
+
* 2. A draft is never bypassed. Writing the published row while a draft is
|
|
2300
|
+
* pending would put the edit somewhere the draft shadows — and Publish
|
|
2301
|
+
* would then overwrite it with the pre-edit body, losing the change a
|
|
2302
|
+
* second time, later, where nothing connects it to this call.
|
|
2303
|
+
*
|
|
2304
|
+
* A draft edit stays a draft: `mode: 'draft'` keeps ADR-0037's guarantee
|
|
2305
|
+
* that nothing the preview shows goes live until Publish. Renaming a
|
|
2306
|
+
* *published* view (no draft pending) is unchanged — it writes the
|
|
2307
|
+
* published overlay, as before.
|
|
2308
|
+
*
|
|
2309
|
+
* **Both halves invalidate through {@link invalidateViewKeys}** (objectui#4363,
|
|
2310
|
+
* #4373) — same call, same key set, so the draft half's deliberate
|
|
2311
|
+
* over-invalidation is the seam's uniform rule rather than a per-branch
|
|
2312
|
+
* decision this method has to keep re-making.
|
|
2313
|
+
*
|
|
2314
|
+
* @throws when the view resolves in neither home, or when either read fails
|
|
2315
|
+
* for any other reason (network, permission). Both used to be swallowed
|
|
2316
|
+
* and converted into the bad partial write above; a caller that wants a
|
|
2317
|
+
* view created should call {@link createView}, which is the operation that
|
|
2318
|
+
* actually means "create".
|
|
1981
2319
|
*/
|
|
1982
2320
|
updateView(objectName: string, viewName: string, partial: Record<string, any>): Promise<Record<string, any> | void>;
|
|
1983
2321
|
/**
|
|
1984
|
-
* Delete an overlay view
|
|
1985
|
-
*
|
|
1986
|
-
*
|
|
2322
|
+
* Delete an overlay view — from **every home it has**.
|
|
2323
|
+
*
|
|
2324
|
+
* A view has two possible homes, the same two {@link updateView} addresses:
|
|
2325
|
+
* the pending per-item **draft** (`DELETE /meta/view/:name?state=draft`) and
|
|
2326
|
+
* the **published** overlay (`DELETE /meta/view/:name`). This method used to
|
|
2327
|
+
* issue only the second, and the three cases came out like this (#4479):
|
|
2328
|
+
*
|
|
2329
|
+
* | case | before |
|
|
2330
|
+
* |--------------------|-----------------------------------------------------|
|
|
2331
|
+
* | draft-only view | BUG — the published delete answered `reset:false` / |
|
|
2332
|
+
* | | "nothing to delete", the draft survived, and the tab |
|
|
2333
|
+
* | | was still there after reload |
|
|
2334
|
+
* | published-only | correct — `reset:true`, tab gone |
|
|
2335
|
+
* | published + draft | ACCIDENTALLY correct — the published row went, so |
|
|
2336
|
+
* | | the tab went; the orphan draft stayed behind |
|
|
2337
|
+
*
|
|
2338
|
+
* **Why this is not the mechanical mirror of #4139.** `updateView` probes
|
|
2339
|
+
* the draft first and writes back to whichever home the read resolved, and
|
|
2340
|
+
* that is right for an update in all three cases. Copying it here would be
|
|
2341
|
+
* wrong in the third: `persistRuntimeMetadata` (app-shell) stages EVERY
|
|
2342
|
+
* runtime edit as a draft, so "publish a view, then edit it" routinely
|
|
2343
|
+
* produces a pair — and a draft-first-only delete on a pair discards the
|
|
2344
|
+
* draft and leaves the published row serving the view. That is not Delete
|
|
2345
|
+
* view, it is **Discard draft**, a deliberately different operation that
|
|
2346
|
+
* already exists (`discardRuntimeDraft`, documented as "the published
|
|
2347
|
+
* overlay is untouched"). The clean statement of the asymmetry: for an
|
|
2348
|
+
* update, one home is the right home; for a delete, "remove this view" is
|
|
2349
|
+
* satisfied only when NO home is left serving it.
|
|
2350
|
+
*
|
|
2351
|
+
* So both homes are deleted, **draft first**. The order is load-bearing on
|
|
2352
|
+
* the failure path: a fault between the two calls leaves the PUBLISHED
|
|
2353
|
+
* overlay intact, so the view is still served and the operation is cleanly
|
|
2354
|
+
* retryable. The reverse order would strand a draft-only view — which is
|
|
2355
|
+
* precisely the bug shape above.
|
|
2356
|
+
*
|
|
2357
|
+
* **Two blind calls, no probe.** The framework's `deleteMetaItem` answers a
|
|
2358
|
+
* missing home with a **200** carrying `reset:false` (`"No pending draft
|
|
2359
|
+
* for view/x."` / `"No view 'x' found — nothing to delete."`), never a 404,
|
|
2360
|
+
* so there is nothing for a probe to protect against. `updateView` needs its
|
|
2361
|
+
* probe for a different reason — its read must resolve the row the merge
|
|
2362
|
+
* writes back to — and that reason has no counterpart for a delete.
|
|
2363
|
+
*
|
|
2364
|
+
* **One transport, one error contract.** Both halves go through
|
|
2365
|
+
* {@link MetadataClient} (`reset`), the transport that can express the
|
|
2366
|
+
* `?state=` qualifier and the one `updateView`'s draft half already uses.
|
|
2367
|
+
* The published half used to go through `client.meta.deleteItem`; measured,
|
|
2368
|
+
* that issues the byte-identical request (`DELETE
|
|
2369
|
+
* {baseUrl}/api/v1/meta/view/:name`, no environment scoping is configured on
|
|
2370
|
+
* this adapter), so routing it here costs no addressing change and buys a
|
|
2371
|
+
* single `MetadataError` shape across both calls instead of two.
|
|
2372
|
+
*
|
|
2373
|
+
* @returns `deleted` is true only when no home is left serving the view AND
|
|
2374
|
+
* at least one actually held a row. A view that existed in neither home
|
|
2375
|
+
* still answers `false`, unchanged. The per-home outcomes are additive:
|
|
2376
|
+
* a partial result is observable rather than rounded up to `true`.
|
|
2377
|
+
* @throws when either delete fails, matching {@link updateView}'s
|
|
2378
|
+
* convention of surfacing the fault rather than degrading. A failure of
|
|
2379
|
+
* the PUBLISHED half after the draft was discarded carries the partial
|
|
2380
|
+
* state on the error's `outcome` — "draft gone, overlay left" is exactly
|
|
2381
|
+
* what the old `{ deleted: boolean }` could not express.
|
|
2382
|
+
*
|
|
2383
|
+
* Invalidates through {@link invalidateViewKeys}: the deleted row leaves the
|
|
2384
|
+
* batch override map too, and a ghost entry there is what the object page
|
|
2385
|
+
* would keep applying (objectui#4363). Fired in a `finally`, so it happens
|
|
2386
|
+
* once per call on EVERY outcome including the throw — after a half-failure
|
|
2387
|
+
* the draft row really is gone, and #4363's asymmetry decides it: an
|
|
2388
|
+
* unnecessary invalidation costs one refetch, a missed one costs the cache's
|
|
2389
|
+
* full 5-minute TTL of stale overrides.
|
|
1987
2390
|
*/
|
|
1988
|
-
deleteView(objectName: string, viewName: string): Promise<
|
|
1989
|
-
deleted: boolean;
|
|
1990
|
-
}>;
|
|
2391
|
+
deleteView(objectName: string, viewName: string): Promise<DeleteViewResult>;
|
|
1991
2392
|
/**
|
|
1992
2393
|
* Get an application definition by name or ID.
|
|
1993
2394
|
* Attempts to fetch from the server metadata API.
|
|
@@ -1998,6 +2399,43 @@ declare class ObjectStackAdapter<T = unknown> implements DataSource<T> {
|
|
|
1998
2399
|
* @returns Promise resolving to the app definition or null
|
|
1999
2400
|
*/
|
|
2000
2401
|
getApp(appId: string): Promise<unknown | null>;
|
|
2402
|
+
/**
|
|
2403
|
+
* Ask the by-name meta app route WHY an app is not in this session's app list
|
|
2404
|
+
* (objectui#4252).
|
|
2405
|
+
*
|
|
2406
|
+
* `GET /api/v1/meta/apps` is filtered per session server-side
|
|
2407
|
+
* (`filterAppForUser`), so an app withheld by `requiredPermissions` and an app
|
|
2408
|
+
* that does not exist are byte-identical there: both are simply absent. A
|
|
2409
|
+
* console reading only that list has one fact and two conditions, and it
|
|
2410
|
+
* renders its copy for the wrong one — "it may still be publishing" over a
|
|
2411
|
+
* permanent authorization decision, which cost a downstream acceptance round
|
|
2412
|
+
* two test batches spent chasing a platform defect that was a missing
|
|
2413
|
+
* permission-set binding.
|
|
2414
|
+
*
|
|
2415
|
+
* The maintainer ruling (2026-08-12) put the answer on the BY-NAME route
|
|
2416
|
+
* rather than in the list, so the enumeration surface is not widened past what
|
|
2417
|
+
* a by-name probe already implies (objectstack#8013 / PR #8135): an app that
|
|
2418
|
+
* exists and whose `requiredPermissions` the session lacks answers `403` with
|
|
2419
|
+
* `PERMISSION_DENIED` in the declared envelope, and absence — a nonexistent
|
|
2420
|
+
* name, an unpublished app, an app gated by an absent optional service —
|
|
2421
|
+
* keeps answering `404 RESOURCE_NOT_FOUND`.
|
|
2422
|
+
*
|
|
2423
|
+
* ## Why this is a separate method and not a flavour of {@link getApp}
|
|
2424
|
+
*
|
|
2425
|
+
* - `getApp` degrades EVERY failure to `null`, which is exactly the
|
|
2426
|
+
* conflation this exists to undo; changing it would silently re-point its
|
|
2427
|
+
* own callers' fallback-to-static-config path.
|
|
2428
|
+
* - `getApp` memoises in `metadataCache`. A verdict about the CALLER must not
|
|
2429
|
+
* be cached beside a document about the APP — one grant, and a cached
|
|
2430
|
+
* denial outlives the session it described.
|
|
2431
|
+
*
|
|
2432
|
+
* Nothing here throws: a probe that cannot reach an answer returns `unknown`
|
|
2433
|
+
* and the caller keeps whatever it was already showing. Only the measured
|
|
2434
|
+
* `code` produces `denied` — never a status, never a message (objectui#4408).
|
|
2435
|
+
*
|
|
2436
|
+
* @param appName - the app name as it appears in the URL segment
|
|
2437
|
+
*/
|
|
2438
|
+
probeAppAccess(appName: string): Promise<AppAccessVerdict>;
|
|
2001
2439
|
/**
|
|
2002
2440
|
* Get a page definition from ObjectStack.
|
|
2003
2441
|
* Uses the metadata API to fetch page layouts.
|
|
@@ -2057,44 +2495,67 @@ declare class ObjectStackAdapter<T = unknown> implements DataSource<T> {
|
|
|
2057
2495
|
* silently returning wrong numbers.
|
|
2058
2496
|
*
|
|
2059
2497
|
* @param dataset - An inline dataset definition (draft) OR a saved dataset name.
|
|
2060
|
-
* @param selection -
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
offset?: number;
|
|
2074
|
-
timezone?: string;
|
|
2075
|
-
/** Marginal-aggregate groupings (e.g. `[rows, [colDim], []]`) — server
|
|
2076
|
-
* computes each subtotal with the measure's TRUE aggregate (never client
|
|
2077
|
-
* re-derived). `[]` is the grand total. */
|
|
2078
|
-
totals?: {
|
|
2079
|
-
groupings: string[][];
|
|
2080
|
-
};
|
|
2081
|
-
}): Promise<{
|
|
2498
|
+
* @param selection - The spec's {@link DatasetSelection} — dimension/measure
|
|
2499
|
+
* names to project plus runtime directives. This parameter IS the spec type
|
|
2500
|
+
* by reference, never a local restatement of it (objectui#3613): a hand
|
|
2501
|
+
* copy of a contract is a second dialect of it, and the copy this replaced
|
|
2502
|
+
* had already drifted three ways from `@objectstack/spec` — it required
|
|
2503
|
+
* `compareTo.dimension` (optional since objectstack#5011, and resolved by
|
|
2504
|
+
* the EXECUTOR, so requiring it pushed callers into exactly the
|
|
2505
|
+
* consumer-side dimension guess AGENTS.md #0.1 forbids), it widened
|
|
2506
|
+
* `timeDimensions` to `unknown[]` and `runtimeFilter` to
|
|
2507
|
+
* `Record<string, unknown>`, and it had never grown `dateGranularity` at
|
|
2508
|
+
* all. Pinned in `queryDataset.test.ts`.
|
|
2509
|
+
*/
|
|
2510
|
+
queryDataset(dataset: Record<string, unknown> | string, selection: DatasetSelection): Promise<{
|
|
2082
2511
|
rows: Array<Record<string, unknown>>;
|
|
2083
|
-
/**
|
|
2084
|
-
*
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2512
|
+
/**
|
|
2513
|
+
* Column metadata — the spec's `AnalyticsResult.fields[]` element BY
|
|
2514
|
+
* REFERENCE, never a local restatement of it (objectui#3752). Read
|
|
2515
|
+
* `@objectstack/spec` for what a column carries; this comment deliberately
|
|
2516
|
+
* does not re-list the keys, because the enumeration it replaced was the
|
|
2517
|
+
* bug: it named five (`name`/`type`/`label`/`format`/`currency`) and stopped
|
|
2518
|
+
* at the contract of the day it was written, so it never grew
|
|
2519
|
+
* `percentScale` — the server's answer to whether a percentage column is a
|
|
2520
|
+
* 0–1 fraction or already percentage points. The spec says a renderer that
|
|
2521
|
+
* receives it "must scale by it instead of guessing from the value"
|
|
2522
|
+
* (objectui#3136), so a declaration that hides the key steers a typed
|
|
2523
|
+
* consumer into exactly the guess-by-magnitude the issue banned. Pinned in
|
|
2524
|
+
* `queryDataset.test.ts`.
|
|
2525
|
+
*
|
|
2526
|
+
* Only this element is spec-owned: the envelope around it (`object` /
|
|
2527
|
+
* `dimensionFields` / `drillRawRows`) is ADR-0021 D2 drill metadata the REST
|
|
2528
|
+
* route adds on top of `AnalyticsResult`, and this method never returns the
|
|
2529
|
+
* result's `sql`, so the whole envelope is NOT an `AnalyticsResult`.
|
|
2530
|
+
*/
|
|
2531
|
+
fields: Array<AnalyticsResult['fields'][number]>;
|
|
2092
2532
|
/** ADR-0021 D2 drill-through: the dataset's base object (records to drill into). */
|
|
2093
2533
|
object?: string;
|
|
2094
2534
|
/** Drillable dimension NAME → underlying object FIELD name. */
|
|
2095
2535
|
dimensionFields?: Record<string, string>;
|
|
2096
2536
|
/** Raw grouped values per row (aligned to `rows` by index) for drill filters. */
|
|
2097
2537
|
drillRawRows?: Array<Record<string, unknown>>;
|
|
2538
|
+
/**
|
|
2539
|
+
* Half-open date-range drill scope per row (framework#1752), aligned to
|
|
2540
|
+
* `rows` by index: dimension NAME → the field and `[gte, lt)` bounds of that
|
|
2541
|
+
* row's time bucket. The RANGE companion to `drillRawRows`, which handles
|
|
2542
|
+
* equality dims only — a `dateGranularity` dimension groups a SPAN of
|
|
2543
|
+
* records into one bucket, so the server excludes date dims from
|
|
2544
|
+
* `dimensionFields`/`drillRawRows` and sends this sidecar instead.
|
|
2545
|
+
*
|
|
2546
|
+
* The entry type is `@object-ui/core`'s `DatasetDrillRange` BY REFERENCE,
|
|
2547
|
+
* not a local restatement of it (objectui#3613/#3752 discipline): the same
|
|
2548
|
+
* declaration is what `buildDatasetDrillFilter` — the single consumer that
|
|
2549
|
+
* turns these bounds into an ObjectQL `{ $gte, $lt }` — accepts, and what
|
|
2550
|
+
* `DatasetWidget` / `DatasetReportRenderer` type their state with. Nothing
|
|
2551
|
+
* in `@objectstack/spec` owns this shape yet (the server's own
|
|
2552
|
+
* `AnalyticsResultWithDrill` is local to `service-analytics`), so the shared
|
|
2553
|
+
* in-repo interface is the one contract available; restating it here would
|
|
2554
|
+
* make a third dialect of it. Like `drillRawRows`, only the ARRAY is
|
|
2555
|
+
* validated below — the bounds are unvalidated payload, which is exactly why
|
|
2556
|
+
* `DatasetDrillRange` declares them `unknown`.
|
|
2557
|
+
*/
|
|
2558
|
+
drillRanges?: Array<Record<string, DatasetDrillRange>>;
|
|
2098
2559
|
/** Server-computed marginal aggregates, one entry per requested grouping. */
|
|
2099
2560
|
totals?: Array<{
|
|
2100
2561
|
dimensions: string[];
|
|
@@ -2211,4 +2672,4 @@ declare function createObjectStackAdapter<T = unknown>(config: {
|
|
|
2211
2672
|
reconnectDelay?: number;
|
|
2212
2673
|
}): DataSource<T>;
|
|
2213
2674
|
|
|
2214
|
-
export { AnalyticsNotInstalledError, AnalyticsQueryRejectedError, type AuditEventType, type AuditLogConfig, type AuditLogEntry, AuthenticationError, type BatchProgressEvent, type BatchProgressListener, BulkOperationError, type CSPConfig,
|
|
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 };
|