@object-ui/data-objectstack 17.7.0 → 18.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +368 -0
- package/README.md +172 -2
- package/dist/index.cjs +875 -362
- package/dist/index.d.cts +742 -307
- package/dist/index.d.ts +742 -307
- package/dist/index.js +870 -360
- package/package.json +5 -5
package/dist/index.d.ts
CHANGED
|
@@ -2,13 +2,113 @@ import { ObjectStackClient } from '@objectstack/client';
|
|
|
2
2
|
import { DroppedFieldsEvent, EngineAggregateOptions } from '@objectstack/spec/data';
|
|
3
3
|
export { DroppedFieldsEvent } from '@objectstack/spec/data';
|
|
4
4
|
import { ListViewGroupHeaderRow } from '@objectstack/spec/ui';
|
|
5
|
-
import { GetMetaItemLayeredResponse, RuntimeAuthoringIssue, PublishPackageDraftsResponse } from '@objectstack/spec/api';
|
|
5
|
+
import { GetMetaItemResponse, GetMetaItemLayeredResponse, ListDraftsResponse, RuntimeAuthoringIssue, GetMetaItemsResponse, PublishPackageDraftsResponse, ValidateDataIssue } from '@objectstack/spec/api';
|
|
6
6
|
export { RuntimeAuthoringIssue } from '@objectstack/spec/api';
|
|
7
7
|
import { DatasetSelection, AnalyticsResult } from '@objectstack/spec/contracts';
|
|
8
|
-
import { DataSource, QueryParams, QueryResult, GlobalSearchResult, DataSourceMutationEvent, BatchTransactionOperation, ImportRequestOptions, ImportRecordsResult, CreateImportJobResult, ImportJobProgressInfo, ImportJobResultsInfo, ListImportJobsOptions, ImportJobSummaryInfo, ImportJobUndoResult, ExportDownloadRequest, DeleteViewResult, FileUploadResult } from '@object-ui/types';
|
|
9
|
-
export { DeleteViewResult, FileUploadResult, ViewHomeDeleteOutcome } from '@object-ui/types';
|
|
8
|
+
import { DataSource, ConnectionState, QueryParams, QueryResult, GlobalSearchResult, DataSourceMutationEvent, BatchTransactionOperation, ImportRequestOptions, ImportRecordsResult, CreateImportJobResult, ImportJobProgressInfo, ImportJobResultsInfo, ListImportJobsOptions, ImportJobSummaryInfo, ImportJobUndoResult, ExportDownloadRequest, DeleteViewResult, FileUploadResult } from '@object-ui/types';
|
|
9
|
+
export { ConnectionState, DeleteViewResult, FileUploadResult, ViewHomeDeleteOutcome } from '@object-ui/types';
|
|
10
10
|
import { DatasetDrillRange } from '@object-ui/core';
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Take the body out of a served draft envelope (`{ type, name, item }`) and
|
|
14
|
+
* remove the framework's own READ DECORATIONS from it — or answer `null` when
|
|
15
|
+
* there is nothing pending.
|
|
16
|
+
*
|
|
17
|
+
* ## Why this function exists at all (objectui#8181)
|
|
18
|
+
*
|
|
19
|
+
* It was implemented three separate times — once in `ResourceEditPage`, once in
|
|
20
|
+
* `StudioDesignSurface`, once in `PackageOwdOverviewPanel` — plus a fourth
|
|
21
|
+
* hand-rolled copy in `ObjectHooksPanel` under a different name. objectui#7603
|
|
22
|
+
* taught exactly ONE of those copies to strip. That is the defect this file
|
|
23
|
+
* closes, and it is a different defect from "N consumers forgot": four
|
|
24
|
+
* byte-identical copies of a rule mean the next copy is free to omit it again,
|
|
25
|
+
* so the cure is one function rather than four strips.
|
|
26
|
+
*
|
|
27
|
+
* ## Why it lives HERE and not in `app-shell`
|
|
28
|
+
*
|
|
29
|
+
* Next to the {@link MetadataClient.getDraft} that produces the envelope this
|
|
30
|
+
* decodes. `getDraft` deliberately hands back the wire envelope rather than the
|
|
31
|
+
* body (objectui#4271), so the unwrap is part of that method's contract, not a
|
|
32
|
+
* detail of any one view. `app-shell` already depends on this package, and the
|
|
33
|
+
* one non-app-shell consumer (`updateView`, in this package) is a caller too.
|
|
34
|
+
*
|
|
35
|
+
* ## The verdict runs BEFORE the strip, never after
|
|
36
|
+
*
|
|
37
|
+
* What counts as a pending draft is `getDraft`'s answer. A draft whose only
|
|
38
|
+
* keys are decorations is still a served draft, so the emptiness verdict is
|
|
39
|
+
* taken on the body as it arrived; the strip may return an EMPTY object, but it
|
|
40
|
+
* may never turn a served draft into "no draft". Pinned in
|
|
41
|
+
* `draft-envelope.test.ts`.
|
|
42
|
+
*
|
|
43
|
+
* ## Which keys, and which keys deliberately survive
|
|
44
|
+
*
|
|
45
|
+
* The list is the SPEC'S (`METADATA_READ_DECORATIONS`), reached through its own
|
|
46
|
+
* exported helper — the same one `MetadataService.saveFields` uses on the write
|
|
47
|
+
* side. Never a second hand-maintained `['_diagnostics', '_draft']` in this
|
|
48
|
+
* repo: a local copy goes stale the next time the framework adds a decoration,
|
|
49
|
+
* and a decoration this code does not know to remove is precisely the defect.
|
|
50
|
+
*
|
|
51
|
+
* The ADR-0010 protection envelope (`_lock`, `_provenance`, `_packageId`,
|
|
52
|
+
* `_packageVersion`, ...) shares the underscore spelling and is deliberately
|
|
53
|
+
* NOT on that list: those keys are declared by the closed schemas so provenance
|
|
54
|
+
* survives a re-parse. This strip leaves them alone, and doing otherwise would
|
|
55
|
+
* be the same "drop whatever looks internal" pass AGENTS.md #0.1 bans.
|
|
56
|
+
*
|
|
57
|
+
* ## Not a lenient fallback
|
|
58
|
+
*
|
|
59
|
+
* It removes exactly the two keys the framework ADDS AT READ TIME and never
|
|
60
|
+
* stores. A genuinely unrecognized key still fails loudly at whichever gate
|
|
61
|
+
* sees it next.
|
|
62
|
+
*
|
|
63
|
+
* @param resp the value `MetadataClient.getDraft()` resolved to.
|
|
64
|
+
* @returns the decoration-free draft body, or `null` for "nothing pending".
|
|
65
|
+
*/
|
|
66
|
+
declare function extractDraftBody(resp: unknown): Record<string, unknown> | null;
|
|
67
|
+
/**
|
|
68
|
+
* One entry of a pending draft's per-key provenance (objectui#12725): which
|
|
69
|
+
* authoring surface last changed one key path of the draft body, under which
|
|
70
|
+
* actor, and when. DERIVED from `@objectstack/spec`'s
|
|
71
|
+
* `GetMetaItemResponseSchema.draftChanges` (objectstack#24298), never
|
|
72
|
+
* restated: `path` is the key path inside `item` (an array value is one leaf,
|
|
73
|
+
* `[]` the whole body), `by` is `copilot | studio | api`, `at` an ISO instant.
|
|
74
|
+
*/
|
|
75
|
+
type DraftChange = NonNullable<GetMetaItemResponse['draftChanges']>[number];
|
|
76
|
+
/**
|
|
77
|
+
* The `draftChanges` of a served draft envelope — the value
|
|
78
|
+
* {@link MetadataClient.getDraft} resolved to — or `null` when the read
|
|
79
|
+
* recorded none.
|
|
80
|
+
*
|
|
81
|
+
* The strict draft read (`?state=draft`) serves the key beside `item`, and
|
|
82
|
+
* leaves it ABSENT, never `[]`, when nothing was recorded (a draft row saved
|
|
83
|
+
* before the framework's writer, objectstack#24395). So `null` here means
|
|
84
|
+
* "not recorded", and it is never folded into an empty list a caller would read
|
|
85
|
+
* as "nothing changed".
|
|
86
|
+
*
|
|
87
|
+
* @param resp the value `MetadataClient.getDraft()` resolved to.
|
|
88
|
+
*/
|
|
89
|
+
declare function readDraftChanges(resp: unknown): DraftChange[] | null;
|
|
90
|
+
/**
|
|
91
|
+
* The entry that answers for the key at `path`: of the entries whose path is
|
|
92
|
+
* `path` or one of its ancestors, the LONGEST — the framework's own rule, since
|
|
93
|
+
* a draft save replaces every entry at or under each path it changes, so the
|
|
94
|
+
* deepest recorded ancestor is the last write that changed this key. `[]` (the
|
|
95
|
+
* whole body) is everyone's ancestor. `null` when no entry is an ancestor, and
|
|
96
|
+
* when `changes` is `null` ("not recorded").
|
|
97
|
+
*
|
|
98
|
+
* Paths compare segment by segment, never as a joined string: a segment may
|
|
99
|
+
* itself contain a dot (a permission set's `fields` map is keyed
|
|
100
|
+
* `object.field`).
|
|
101
|
+
*/
|
|
102
|
+
declare function governingDraftChange(changes: readonly DraftChange[] | null | undefined, path: readonly string[]): DraftChange | null;
|
|
103
|
+
/**
|
|
104
|
+
* The entries strictly UNDER `path`: changes to keys inside the value at
|
|
105
|
+
* `path` (`["fields", "x", "label"]` under `["fields", "x"]`). The governing
|
|
106
|
+
* entry ({@link governingDraftChange}) answers for the value as a whole; these
|
|
107
|
+
* answer for its parts, which a later write may have changed through another
|
|
108
|
+
* surface.
|
|
109
|
+
*/
|
|
110
|
+
declare function draftChangesUnder(changes: readonly DraftChange[] | null | undefined, path: readonly string[]): DraftChange[];
|
|
111
|
+
|
|
12
112
|
/**
|
|
13
113
|
* ObjectUI
|
|
14
114
|
* Copyright (c) 2024-present ObjectStack Inc.
|
|
@@ -57,6 +157,15 @@ interface MetadataSaveAdvisoryEvent {
|
|
|
57
157
|
type: string;
|
|
58
158
|
/** Item name saved. */
|
|
59
159
|
name: string;
|
|
160
|
+
/**
|
|
161
|
+
* The bundle member the write named (objectui#12413), when it named one:
|
|
162
|
+
* set by {@link MetadataClient.publishDraft} from its `options.member`, so
|
|
163
|
+
* an advisory raised on one language's publish of an `email_template` is
|
|
164
|
+
* attributed to that member and not to the whole template. Absent for every
|
|
165
|
+
* write that names no member. Typed from {@link MetadataDraftHeader}'s
|
|
166
|
+
* `member`, the value the caller read off the draft row.
|
|
167
|
+
*/
|
|
168
|
+
member?: MetadataDraftHeader['member'];
|
|
60
169
|
/**
|
|
61
170
|
* The save mode the call used. `'draft'` writes are **never gated** — the
|
|
62
171
|
* framework returns at `runtime-authoring-gate.ts`'s D1 early-return
|
|
@@ -178,18 +287,50 @@ interface MetadataListOptions {
|
|
|
178
287
|
/** Filter by source package id (matches the `package` query param). */
|
|
179
288
|
packageId?: string;
|
|
180
289
|
}
|
|
290
|
+
/**
|
|
291
|
+
* A list read as {@link MetadataClient.listResponse} returns it: the items, and
|
|
292
|
+
* what the server serves BESIDE them, typed by the spec's
|
|
293
|
+
* `GetMetaItemsResponse` rather than restated (its `type` key is not kept).
|
|
294
|
+
*
|
|
295
|
+
* `registrations` is the automation engine's verdict on each listed flow,
|
|
296
|
+
* keyed by the item's `name` (`FlowRegistrationSchema`, objectstack#23316):
|
|
297
|
+
* `registered`, `refused` with the engine's `reason` verbatim, or
|
|
298
|
+
* `unregistered`. It is absent when the server sent none — a type other than
|
|
299
|
+
* `flow`, a server with no automation service, or one that predates the key —
|
|
300
|
+
* and absence means no engine judged the flow, ⛔ never `registered`. A name
|
|
301
|
+
* the map has no entry for is unjudged in the same way.
|
|
302
|
+
*/
|
|
303
|
+
type MetadataListAnswer<T = unknown> = {
|
|
304
|
+
items: T[];
|
|
305
|
+
} & Pick<GetMetaItemsResponse, 'registrations'>;
|
|
181
306
|
/**
|
|
182
307
|
* A pending DRAFT metadata item (ADR-0033), as returned by {@link MetadataClient.listDrafts}.
|
|
183
308
|
* Light header — no body — carrying the owning package so the console can group
|
|
184
309
|
* pending changes by app package.
|
|
310
|
+
*
|
|
311
|
+
* DERIVED from the spec's row, `ListDraftsResponseSchema.drafts[]`, rather than
|
|
312
|
+
* re-spelled (objectui#12413): the key names and their types come from the
|
|
313
|
+
* spec symbol, so a key the spec renames or retypes fails the type-check here
|
|
314
|
+
* instead of drifting past it.
|
|
315
|
+
*
|
|
316
|
+
* - `member` — the bundle member this draft is (objectstack#23073): present on
|
|
317
|
+
* every draft of a type whose identity is a pair (today only
|
|
318
|
+
* `email_template`, `(name, locale)`), absent for a type identified by its
|
|
319
|
+
* name alone. It is the value the by-name doors take back as `?member=`
|
|
320
|
+
* ({@link MetadataClient.publishDraft}'s `options.member`).
|
|
321
|
+
*
|
|
322
|
+
* ⛔ The pick is deliberate: the five keys this interface declared before keep
|
|
323
|
+
* their exact types, and `member` is the one key added. The spec row's `label`
|
|
324
|
+
* is not picked here: it is declared REQUIRED there, and this client talks to
|
|
325
|
+
* runtimes of several vintages, so claiming it would be this client asserting
|
|
326
|
+
* a server version it cannot check.
|
|
185
327
|
*/
|
|
186
|
-
interface MetadataDraftHeader {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
packageId: string | null;
|
|
190
|
-
updatedAt: string | null;
|
|
191
|
-
updatedBy: string | null;
|
|
328
|
+
interface MetadataDraftHeader extends Pick<ListDraftsRow, 'type' | 'name' | 'packageId' | 'updatedAt' | 'updatedBy'> {
|
|
329
|
+
/** The bundle member, typed by the spec row's own `member` (see above). */
|
|
330
|
+
member?: ListDraftsRow['member'];
|
|
192
331
|
}
|
|
332
|
+
/** One element of `ListDraftsResponseSchema.drafts`, the spec row `MetadataDraftHeader` derives from. */
|
|
333
|
+
type ListDraftsRow = ListDraftsResponse['drafts'][number];
|
|
193
334
|
/**
|
|
194
335
|
* What {@link MetadataClient.publishPackageDrafts} resolves with — the "publish
|
|
195
336
|
* whole app" body, DERIVED from `PublishPackageDraftsResponseSchema` rather
|
|
@@ -212,6 +353,26 @@ interface MetadataDraftHeader {
|
|
|
212
353
|
* or retypes arrives here with no edit.
|
|
213
354
|
*/
|
|
214
355
|
type MetadataPublishPackageDraftsResult = Partial<PublishPackageDraftsResponse> & Record<string, unknown>;
|
|
356
|
+
/**
|
|
357
|
+
* What {@link MetadataClient.publishPackageDrafts} may ask of the batch door
|
|
358
|
+
* beyond the package id (objectui#12929): today the protocol's opt-in
|
|
359
|
+
* `backfill` (objectstack#24253), and nothing else.
|
|
360
|
+
*
|
|
361
|
+
* Picked off `@objectstack/client`'s own `packages.publishDrafts` signature
|
|
362
|
+
* rather than re-spelled, so the element shape (`{ object, field }`) is the
|
|
363
|
+
* client's declaration. The request has no spec schema of its own; the
|
|
364
|
+
* client's signature is where the protocol declares it. `actor` is not
|
|
365
|
+
* picked: the door writes as the signed-in caller, and no console surface
|
|
366
|
+
* names another.
|
|
367
|
+
*
|
|
368
|
+
* `backfill` names fields this publish ADDS whose literal `defaultValue` the
|
|
369
|
+
* server writes into the object's existing rows, as the caller, after the
|
|
370
|
+
* batch goes live; the receipt comes back as `backfillApplied`. A field that
|
|
371
|
+
* is not new, or declares no literal default, refuses the WHOLE request with a
|
|
372
|
+
* 400 before anything is published, so a caller offers only fields it has
|
|
373
|
+
* judged by the same rule.
|
|
374
|
+
*/
|
|
375
|
+
type MetadataPublishPackageDraftsOptions = Pick<NonNullable<Parameters<ObjectStackClient['packages']['publishDrafts']>[1]>, 'backfill'>;
|
|
215
376
|
/**
|
|
216
377
|
* Options for {@link MetadataClient.save} — a WRITE OVER HTTP to
|
|
217
378
|
* `/api/v1/meta/:type/:name`.
|
|
@@ -222,17 +383,41 @@ type MetadataPublishPackageDraftsResult = Partial<PublishPackageDraftsResponse>
|
|
|
222
383
|
* metadata item to a FILE — `format: json|yaml|ts`, `path`, `indent`,
|
|
223
384
|
* `prettify`, `sortKeys`, `backup`, `atomic`, `loader`. Not one of those keys
|
|
224
385
|
* exists here, and not one of these exists there: this is the REST client's
|
|
225
|
-
* request envelope — optimistic concurrency (`ifMatch` → `If-Match
|
|
226
|
-
*
|
|
227
|
-
* owning package. Same words, different
|
|
386
|
+
* request envelope — optimistic concurrency (`ifMatch` → `If-Match`,
|
|
387
|
+
* `ifNoneMatch` → `If-None-Match`), the destructive-change override, the
|
|
388
|
+
* ADR-0033 draft/publish mode, and the owning package. Same words, different
|
|
389
|
+
* layer.
|
|
228
390
|
*/
|
|
229
391
|
interface MetadataClientSaveOptions {
|
|
230
392
|
/**
|
|
231
|
-
* Optimistic concurrency
|
|
232
|
-
*
|
|
233
|
-
*
|
|
393
|
+
* Optimistic concurrency (ADR-0008): the `version` token of the stored row
|
|
394
|
+
* this write was built on, sent as the `If-Match` header, so a write over a
|
|
395
|
+
* row that has moved since is refused `409 METADATA_CONFLICT` instead of
|
|
396
|
+
* overwriting it.
|
|
397
|
+
*
|
|
398
|
+
* The `/meta` doors serve that token in three places, each naming the row a
|
|
399
|
+
* save to the SAME address (type, name, `?package=`, lifecycle) compares
|
|
400
|
+
* against: the item read's `version` (`?state=draft` for the draft row,
|
|
401
|
+
* declared on `GetMetaItemResponseSchema`), a save receipt's `version`, and
|
|
402
|
+
* a version conflict's `currentVersion` (`MetadataConflictErrorSchema`). A
|
|
403
|
+
* served `null` means no such row: pin with {@link ifNoneMatch} instead.
|
|
404
|
+
*
|
|
405
|
+
* ⛔ Never together with {@link ifNoneMatch}: {@link MetadataClient.save}
|
|
406
|
+
* throws before sending a request that carries both.
|
|
234
407
|
*/
|
|
235
408
|
ifMatch?: string;
|
|
409
|
+
/**
|
|
410
|
+
* The first-write pin, sent as `If-None-Match: *`: write only where no row of
|
|
411
|
+
* this write's lifecycle exists at its address (the draft row, for
|
|
412
|
+
* `mode: 'draft'`). A create sends it, and so does the first draft save of an
|
|
413
|
+
* item with no pending draft; the door refuses it `409 METADATA_CONFLICT`,
|
|
414
|
+
* carrying the existing row's `currentVersion`, when such a row is there.
|
|
415
|
+
*
|
|
416
|
+
* `'*'` alone: the door reads no other value. ⛔ Never together with
|
|
417
|
+
* {@link ifMatch} — the door refuses that pair `400`, and
|
|
418
|
+
* {@link MetadataClient.save} throws before sending it.
|
|
419
|
+
*/
|
|
420
|
+
ifNoneMatch?: '*';
|
|
236
421
|
/**
|
|
237
422
|
* Bypass destructive-change protection (Phase 3a). The server returns
|
|
238
423
|
* `409 destructive_change` with `issues[]` if a write would drop or
|
|
@@ -253,6 +438,19 @@ interface MetadataClientSaveOptions {
|
|
|
253
438
|
* Studio package workspace. Omit for an env-local overlay.
|
|
254
439
|
*/
|
|
255
440
|
packageId?: string;
|
|
441
|
+
/**
|
|
442
|
+
* The authoring surface this save is made through (objectui#12725), sent as
|
|
443
|
+
* `?surface=`. A draft save stamps it as `by` on every key path it changes,
|
|
444
|
+
* the entries the strict draft read serves as `draftChanges`; absent, the
|
|
445
|
+
* framework stamps `api`. Studio's own saves send `'studio'`, so a person's
|
|
446
|
+
* edit there is told apart from the AI copilot's (objectstack#24395).
|
|
447
|
+
*
|
|
448
|
+
* DERIVED from the spec's authoring-surface set (the one `draftChanges[].by`
|
|
449
|
+
* reports, `MetadataAuthoringSurface`), minus `'copilot'`: the HTTP doors
|
|
450
|
+
* refuse `?surface=copilot` `400` (only an in-process tool may state it), so
|
|
451
|
+
* this client cannot send it. Omit for any other caller.
|
|
452
|
+
*/
|
|
453
|
+
surface?: Exclude<DraftChange['by'], 'copilot'>;
|
|
256
454
|
}
|
|
257
455
|
interface MetadataGetOptions {
|
|
258
456
|
/**
|
|
@@ -269,7 +467,7 @@ interface MetadataGetOptions {
|
|
|
269
467
|
*/
|
|
270
468
|
packageId?: string;
|
|
271
469
|
}
|
|
272
|
-
interface MetadataDeleteOptions extends MetadataClientSaveOptions {
|
|
470
|
+
interface MetadataDeleteOptions extends Omit<MetadataClientSaveOptions, 'surface'> {
|
|
273
471
|
/**
|
|
274
472
|
* Target state. `'draft'` discards the pending draft (keeps the
|
|
275
473
|
* published overlay intact). Omit to reset the active overlay back
|
|
@@ -278,12 +476,13 @@ interface MetadataDeleteOptions extends MetadataClientSaveOptions {
|
|
|
278
476
|
state?: 'active' | 'draft';
|
|
279
477
|
}
|
|
280
478
|
/**
|
|
281
|
-
* Which layer an overlay was saved at — `'
|
|
282
|
-
*
|
|
479
|
+
* Which layer an overlay was saved at — `'env'` for an environment-level one,
|
|
480
|
+
* `null` exactly when `overlay` is null. Every metadata read is environment →
|
|
481
|
+
* code since objectstack#15206 S5, which retired the `'org'` tenant scope.
|
|
283
482
|
*
|
|
284
483
|
* DERIVED from `@objectstack/spec`, not restated: the vocabulary lives once, in
|
|
285
484
|
* `GetMetaItemLayeredResponseSchema`'s `overlayScope`
|
|
286
|
-
* (`z.enum(['
|
|
485
|
+
* (`z.enum(['env']).nullable()`), and this alias indexes the published
|
|
287
486
|
* response type so a scope the spec adds arrives here with no edit. Restating
|
|
288
487
|
* the union locally is the fork `scripts/check-spec-symbol-derivation.mjs`
|
|
289
488
|
* exists to reject, and the mirror-image mistake — a consumer re-spelling a
|
|
@@ -549,10 +748,51 @@ declare class MetadataClient {
|
|
|
549
748
|
* renderer tree into ADR-0037 preview mode off one URL flag.
|
|
550
749
|
*/
|
|
551
750
|
withPreviewDrafts(previewDrafts: boolean): MetadataClient;
|
|
751
|
+
/**
|
|
752
|
+
* Share one in-flight GET among the callers asking the same method for the
|
|
753
|
+
* same URL with the same headers through the same transport (objectui#11797).
|
|
754
|
+
*
|
|
755
|
+
* Opening a Studio package mounts several readers that ask the same
|
|
756
|
+
* question at once, and each one sent its own request. The first call for a
|
|
757
|
+
* key sends `read()`; a call that arrives while it is pending waits for that
|
|
758
|
+
* request instead of sending another.
|
|
759
|
+
*
|
|
760
|
+
* ⛔ Not a response cache, deliberately — the rules `findOne` keeps on the
|
|
761
|
+
* data adapter (objectui#11699):
|
|
762
|
+
* - the entry goes the moment the read settles, so a call made after an
|
|
763
|
+
* answer always reaches the network (there is no reuse window: a client
|
|
764
|
+
* cannot see every write that changes metadata, an agent's server-side
|
|
765
|
+
* edits among them, and a window would answer the read after such a write
|
|
766
|
+
* with the state from before it);
|
|
767
|
+
* - a rejection reaches every caller already waiting and is not remembered;
|
|
768
|
+
* - a write sent through the same transport drops every pending read when
|
|
769
|
+
* it lands (see {@link sendWrite}), so a read asked after a save or a
|
|
770
|
+
* publish is never answered by a request sent before it;
|
|
771
|
+
* - every caller, the first one included, gets an answer no other caller
|
|
772
|
+
* holds (see {@link handOut}).
|
|
773
|
+
*/
|
|
774
|
+
private shareRead;
|
|
775
|
+
/**
|
|
776
|
+
* Send a write, then drop every read still pending on this transport
|
|
777
|
+
* (objectui#11797): a read on the wire when the write lands was asked before
|
|
778
|
+
* it, so a caller asking after the write must not join it. Dropped however
|
|
779
|
+
* the write ends — a refused or interrupted write may still have changed
|
|
780
|
+
* something. The callers already holding a pending read keep its answer.
|
|
781
|
+
*/
|
|
782
|
+
private sendWrite;
|
|
552
783
|
/** List all registered metadata types (returns the registry rows). */
|
|
553
784
|
listTypes(): Promise<unknown[]>;
|
|
554
785
|
/** List items of a metadata type (e.g. `object`, `field`, `view`). */
|
|
555
786
|
list<T = unknown>(type: string, options?: MetadataListOptions): Promise<T[]>;
|
|
787
|
+
/**
|
|
788
|
+
* The same list read as {@link list}, with what the server serves beside
|
|
789
|
+
* the items (objectui#12349): today that is the automation engine's
|
|
790
|
+
* `registrations` on a `flow` list — see {@link MetadataListAnswer}.
|
|
791
|
+
*
|
|
792
|
+
* One request for both methods: `list` is this read with the items picked
|
|
793
|
+
* out, so a caller asking both at once shares one in-flight GET.
|
|
794
|
+
*/
|
|
795
|
+
listResponse<T = unknown>(type: string, options?: MetadataListOptions): Promise<MetadataListAnswer<T>>;
|
|
556
796
|
/**
|
|
557
797
|
* List pending DRAFT items (ADR-0033) — what an AI authored but nobody
|
|
558
798
|
* published yet. `list()` only sees published/active metadata, so a
|
|
@@ -626,10 +866,39 @@ declare class MetadataClient {
|
|
|
626
866
|
* (app-shell) and `unwrapViewDraft` (this package) read a draft that arrived
|
|
627
867
|
* through {@link get}, which already unwrapped the envelope. They split on
|
|
628
868
|
* which method feeds them, not on what they mean — all three strip.
|
|
869
|
+
*
|
|
870
|
+
* objectui#11799 — whether a draft exists is answered by the pending-drafts
|
|
871
|
+
* ledger first (see {@link draftLedgerMayHold}), and `?state=draft` is sent
|
|
872
|
+
* only when the ledger lists the name. The item read answers 404 when there
|
|
873
|
+
* is no draft, so opening every item in Studio used to log one error per
|
|
874
|
+
* item for an answer the ledger gives with a 200.
|
|
629
875
|
*/
|
|
630
876
|
getDraft<T = unknown>(type: string, name: string, options?: {
|
|
631
877
|
packageId?: string;
|
|
632
878
|
}): Promise<T | null>;
|
|
879
|
+
/**
|
|
880
|
+
* Whether the pending-drafts ledger (`GET /meta/_drafts`) may hold a draft
|
|
881
|
+
* named `name` (objectui#11799).
|
|
882
|
+
*
|
|
883
|
+
* Read through {@link listDrafts}, the shared read objectui#11797 made: a
|
|
884
|
+
* question asked while a ledger read is pending waits for it, and a write
|
|
885
|
+
* through this transport drops it when the write lands ({@link sendWrite}),
|
|
886
|
+
* so a draft saved a moment ago is never reported absent by a read sent
|
|
887
|
+
* before the save.
|
|
888
|
+
*
|
|
889
|
+
* The WHOLE ledger, matched by name. The `?state=draft` read finds a row
|
|
890
|
+
* under either spelling of the type, and, when it names a package, that
|
|
891
|
+
* package's row or the package-less one. The ledger's `type` and `packageId`
|
|
892
|
+
* filters are exact, so a filtered ledger could miss a draft the item read
|
|
893
|
+
* would serve, and the editor would open the published body over a pending
|
|
894
|
+
* draft. Unfiltered, every miss here is a real absence; a same-name row of
|
|
895
|
+
* another type or package only costs the item read that was always sent.
|
|
896
|
+
*
|
|
897
|
+
* A ledger that cannot be read (403 without an authoring capability, 501 on
|
|
898
|
+
* a kernel without drafts, a network fault) is UNKNOWN, not empty: the item
|
|
899
|
+
* read is sent, as before.
|
|
900
|
+
*/
|
|
901
|
+
private draftLedgerMayHold;
|
|
633
902
|
/**
|
|
634
903
|
* Report the runtime authoring gate's advisory findings for a write the
|
|
635
904
|
* server has ALREADY committed (#4133 for the save door, #5026 for the
|
|
@@ -721,8 +990,22 @@ declare class MetadataClient {
|
|
|
721
990
|
* {@link listDrafts} reads `data?.data?.drafts` because `GET /meta/_drafts`
|
|
722
991
|
* IS a dispatcher route, and `publishHealthFromResponse` unwraps because the
|
|
723
992
|
* batch door really is enveloped. The rule is per-route, not per-file.
|
|
993
|
+
*
|
|
994
|
+
* ## The bundle member (objectui#12402)
|
|
995
|
+
*
|
|
996
|
+
* Pass `member` to publish one member of a type whose identity is a pair
|
|
997
|
+
* (today only `email_template`, `(name, locale)`), sent as `?member=` — the
|
|
998
|
+
* selector the by-name doors read since objectstack `038d4c958`, and the
|
|
999
|
+
* value a `GET /meta/_drafts` row carries as `member`. A publish that names
|
|
1000
|
+
* none promotes the CANONICAL member's draft, so two languages of one
|
|
1001
|
+
* template drafted outside a package cannot both be published by name
|
|
1002
|
+
* without it. The parameter is OMITTED, never sent empty, when the caller
|
|
1003
|
+
* has no member: the door refuses a blank selector, and refuses any
|
|
1004
|
+
* selector on a type with no members.
|
|
724
1005
|
*/
|
|
725
|
-
publishDraft(type: string, name: string
|
|
1006
|
+
publishDraft(type: string, name: string, options?: {
|
|
1007
|
+
member?: string;
|
|
1008
|
+
}): Promise<{
|
|
726
1009
|
success?: boolean;
|
|
727
1010
|
seedApplied?: {
|
|
728
1011
|
success: boolean;
|
|
@@ -777,8 +1060,18 @@ declare class MetadataClient {
|
|
|
777
1060
|
* says to read `outcome`, not the boolean), and the two callers have their
|
|
778
1061
|
* own, different rules for the refusal and rolled-back cases. Deciding that
|
|
779
1062
|
* here would change behaviour this card is not about.
|
|
1063
|
+
*
|
|
1064
|
+
* ## The opt-in backfill (objectui#12929)
|
|
1065
|
+
*
|
|
1066
|
+
* `options.backfill` is sent as the body's `backfill` key, as given, and only
|
|
1067
|
+
* when the caller passes one ({@link MetadataPublishPackageDraftsOptions}).
|
|
1068
|
+
* Without it the body is `{}`, byte for byte what every caller sent before,
|
|
1069
|
+
* so a caller that names nothing writes nothing into existing rows. The
|
|
1070
|
+
* receipt is `backfillApplied` on the returned body, unexamined like the rest
|
|
1071
|
+
* of a 2xx: one element per named field with the rows written, and `error`
|
|
1072
|
+
* on a field that stopped short (the publish itself stands).
|
|
780
1073
|
*/
|
|
781
|
-
publishPackageDrafts(packageId: string): Promise<MetadataPublishPackageDraftsResult>;
|
|
1074
|
+
publishPackageDrafts(packageId: string, options?: MetadataPublishPackageDraftsOptions): Promise<MetadataPublishPackageDraftsResult>;
|
|
782
1075
|
/**
|
|
783
1076
|
* Get the 3-state layered view of a metadata item: `code` (the packaged
|
|
784
1077
|
* artifact baseline), `overlay` (the tenant customisation row alone) and
|
|
@@ -834,6 +1127,12 @@ declare class MetadataClient {
|
|
|
834
1127
|
* Idempotent: returns the result even when no overlay row existed.
|
|
835
1128
|
* Pass `state: 'draft'` to discard the pending draft only (keeps the
|
|
836
1129
|
* published overlay intact) — useful for a Studio "Discard draft" button.
|
|
1130
|
+
*
|
|
1131
|
+
* Pass `packageId` to name the package whose row the delete removes, sent
|
|
1132
|
+
* as `?package=`. A delete that names none addresses the one package that
|
|
1133
|
+
* holds the item; when two or more packages hold it the door refuses it
|
|
1134
|
+
* `409 RESOURCE_CONFLICT`, deletes nothing, and names `?package=` as the
|
|
1135
|
+
* remedy. That refusal is thrown like any other {@link MetadataError}.
|
|
837
1136
|
*/
|
|
838
1137
|
reset<T = unknown>(type: string, name: string, options?: MetadataDeleteOptions): Promise<T>;
|
|
839
1138
|
/**
|
|
@@ -1111,6 +1410,35 @@ interface MetadataCacheStats {
|
|
|
1111
1410
|
* As soon as the backend supports `sys_user_preference`, persistence
|
|
1112
1411
|
* "lights up" with no code change.
|
|
1113
1412
|
*
|
|
1413
|
+
* ## One read per load (objectui#12187)
|
|
1414
|
+
*
|
|
1415
|
+
* The console builds one adapter per slot (favorites, recent items, flow
|
|
1416
|
+
* palette recents) over the same data source and user, and their providers
|
|
1417
|
+
* call `load()` in the same effects pass. Those loads share ONE request: the
|
|
1418
|
+
* first `load()` opens a read for its (data source, resource, user), every
|
|
1419
|
+
* `load()` of that scope issued before the next microtask adds its key to it,
|
|
1420
|
+
* and the read is then sent once and each adapter takes its own row from the
|
|
1421
|
+
* answer. A read that collected one key sends the per-key query this adapter
|
|
1422
|
+
* always sent; several keys go as one `key in (...)` query.
|
|
1423
|
+
*
|
|
1424
|
+
* Nothing is kept past that: the read closes the moment it is sent, so a later
|
|
1425
|
+
* `load()` (a new adapter after a language or account switch) asks the server
|
|
1426
|
+
* again, and a different user or data source never joins another's read.
|
|
1427
|
+
* `save()` does not use it: a write still finds and writes its own row.
|
|
1428
|
+
*
|
|
1429
|
+
* ## A family of keys in the same read (objectui#12418)
|
|
1430
|
+
*
|
|
1431
|
+
* Some state is one row per subject the console cannot name at load time: the
|
|
1432
|
+
* list state of every object a user has opened is a row per object,
|
|
1433
|
+
* `ui.list.<object>`, and which objects those are is only known from the rows
|
|
1434
|
+
* themselves. {@link createObjectStackUserPreferenceFamily} loads such a family
|
|
1435
|
+
* by its key PREFIX, and joins the same shared read: the read then asks for its
|
|
1436
|
+
* keys OR any key starting with the prefix, still one request. A prefix bounds
|
|
1437
|
+
* nothing the way the unique index bounds a key, so the read asks for at most
|
|
1438
|
+
* {@link PREFERENCE_FAMILY_ROW_LIMIT} family rows on top of its keys; an
|
|
1439
|
+
* answer that fills that limit may have left a key's row out, and such a key
|
|
1440
|
+
* asks for its own row instead (the same fallback an untagged row takes).
|
|
1441
|
+
*
|
|
1114
1442
|
* @module
|
|
1115
1443
|
*/
|
|
1116
1444
|
|
|
@@ -1133,10 +1461,31 @@ interface ObjectStackUserStateAdapterOptions {
|
|
|
1133
1461
|
*/
|
|
1134
1462
|
onError?: (where: 'load' | 'save', error: unknown) => void;
|
|
1135
1463
|
}
|
|
1464
|
+
/**
|
|
1465
|
+
* Generic persistence adapter for a user-scoped list.
|
|
1466
|
+
*
|
|
1467
|
+
* Implementations must be safe to call concurrently and should *never*
|
|
1468
|
+
* throw — failures should be swallowed so the hosting provider can degrade
|
|
1469
|
+
* to localStorage-only mode without crashing the UI.
|
|
1470
|
+
*
|
|
1471
|
+
* This is the one declaration of the name: `@object-ui/app-shell`'s
|
|
1472
|
+
* `UserStateAdaptersProvider` injects adapters of this type and re-exports it
|
|
1473
|
+
* (objectui#6349, batch 9).
|
|
1474
|
+
*/
|
|
1136
1475
|
interface UserDataAdapter<T> {
|
|
1476
|
+
/** Load the persisted list for the current user. Resolve to [] when absent. */
|
|
1137
1477
|
load(): Promise<T[]>;
|
|
1478
|
+
/** Persist the full list (debounced upstream). Errors are silently ignored. */
|
|
1138
1479
|
save(items: T[]): Promise<void>;
|
|
1139
1480
|
}
|
|
1481
|
+
/**
|
|
1482
|
+
* How many rows of one key family a shared read asks for (objectui#12418).
|
|
1483
|
+
* The unique index bounds a KEY to one row; nothing bounds a prefix, so the
|
|
1484
|
+
* read's `$top` is its key count plus this many rows per family. An answer
|
|
1485
|
+
* that reaches its `$top` is read as possibly cut short: see
|
|
1486
|
+
* {@link SharedAnswer.saturated}.
|
|
1487
|
+
*/
|
|
1488
|
+
declare const PREFERENCE_FAMILY_ROW_LIMIT = 200;
|
|
1140
1489
|
/**
|
|
1141
1490
|
* Build a `UserDataAdapter<T>` backed by ObjectStack.
|
|
1142
1491
|
*
|
|
@@ -1144,62 +1493,117 @@ interface UserDataAdapter<T> {
|
|
|
1144
1493
|
* one per slot you want to persist.
|
|
1145
1494
|
*/
|
|
1146
1495
|
declare function createObjectStackUserStateAdapter<T = unknown>(options: ObjectStackUserStateAdapterOptions): UserDataAdapter<T>;
|
|
1147
|
-
|
|
1496
|
+
interface ObjectStackUserPreferenceFamilyOptions {
|
|
1497
|
+
/** Connected data source (usually the one provided by `<AdapterProvider>`). */
|
|
1498
|
+
dataSource: DataSource<any>;
|
|
1499
|
+
/** Authenticated user id. */
|
|
1500
|
+
userId: string;
|
|
1501
|
+
/**
|
|
1502
|
+
* The family's key prefix, dotted and ending in a dot, e.g. `ui.list.`: the
|
|
1503
|
+
* family is every key that starts with it, one row per subject
|
|
1504
|
+
* (`ui.list.account`, `ui.list.contact`, …).
|
|
1505
|
+
*/
|
|
1506
|
+
prefix: string;
|
|
1507
|
+
/** Override the storage object name. Defaults to `"sys_user_preference"`. */
|
|
1508
|
+
resource?: string;
|
|
1509
|
+
/** Optional diagnostics hook; defaults to noop. */
|
|
1510
|
+
onError?: (where: 'load' | 'save', error: unknown) => void;
|
|
1511
|
+
}
|
|
1148
1512
|
/**
|
|
1149
|
-
*
|
|
1150
|
-
*
|
|
1151
|
-
* there is nothing pending.
|
|
1152
|
-
*
|
|
1153
|
-
* ## Why this function exists at all (objectui#8181)
|
|
1154
|
-
*
|
|
1155
|
-
* It was implemented three separate times — once in `ResourceEditPage`, once in
|
|
1156
|
-
* `StudioDesignSurface`, once in `PackageOwdOverviewPanel` — plus a fourth
|
|
1157
|
-
* hand-rolled copy in `ObjectHooksPanel` under a different name. objectui#7603
|
|
1158
|
-
* taught exactly ONE of those copies to strip. That is the defect this file
|
|
1159
|
-
* closes, and it is a different defect from "N consumers forgot": four
|
|
1160
|
-
* byte-identical copies of a rule mean the next copy is free to omit it again,
|
|
1161
|
-
* so the cure is one function rather than four strips.
|
|
1162
|
-
*
|
|
1163
|
-
* ## Why it lives HERE and not in `app-shell`
|
|
1164
|
-
*
|
|
1165
|
-
* Next to the {@link MetadataClient.getDraft} that produces the envelope this
|
|
1166
|
-
* decodes. `getDraft` deliberately hands back the wire envelope rather than the
|
|
1167
|
-
* body (objectui#4271), so the unwrap is part of that method's contract, not a
|
|
1168
|
-
* detail of any one view. `app-shell` already depends on this package, and the
|
|
1169
|
-
* one non-app-shell consumer (`updateView`, in this package) is a caller too.
|
|
1170
|
-
*
|
|
1171
|
-
* ## The verdict runs BEFORE the strip, never after
|
|
1172
|
-
*
|
|
1173
|
-
* What counts as a pending draft is `getDraft`'s answer. A draft whose only
|
|
1174
|
-
* keys are decorations is still a served draft, so the emptiness verdict is
|
|
1175
|
-
* taken on the body as it arrived; the strip may return an EMPTY object, but it
|
|
1176
|
-
* may never turn a served draft into "no draft". Pinned in
|
|
1177
|
-
* `draft-envelope.test.ts`.
|
|
1178
|
-
*
|
|
1179
|
-
* ## Which keys, and which keys deliberately survive
|
|
1513
|
+
* A user's rows of one key family: every `sys_user_preference` row whose key
|
|
1514
|
+
* starts with the family's prefix, each holding one JSON object.
|
|
1180
1515
|
*
|
|
1181
|
-
*
|
|
1182
|
-
*
|
|
1183
|
-
*
|
|
1184
|
-
* repo: a local copy goes stale the next time the framework adds a decoration,
|
|
1185
|
-
* and a decoration this code does not know to remove is precisely the defect.
|
|
1186
|
-
*
|
|
1187
|
-
* The ADR-0010 protection envelope (`_lock`, `_provenance`, `_packageId`,
|
|
1188
|
-
* `_packageVersion`, ...) shares the underscore spelling and is deliberately
|
|
1189
|
-
* NOT on that list: those keys are declared by the closed schemas so provenance
|
|
1190
|
-
* survives a re-parse. This strip leaves them alone, and doing otherwise would
|
|
1191
|
-
* be the same "drop whatever looks internal" pass AGENTS.md #0.1 bans.
|
|
1192
|
-
*
|
|
1193
|
-
* ## Not a lenient fallback
|
|
1194
|
-
*
|
|
1195
|
-
* It removes exactly the two keys the framework ADDS AT READ TIME and never
|
|
1196
|
-
* stores. A genuinely unrecognized key still fails loudly at whichever gate
|
|
1197
|
-
* sees it next.
|
|
1198
|
-
*
|
|
1199
|
-
* @param resp the value `MetadataClient.getDraft()` resolved to.
|
|
1200
|
-
* @returns the decoration-free draft body, or `null` for "nothing pending".
|
|
1516
|
+
* Same contract as {@link UserDataAdapter}: never throws; a failed read loads
|
|
1517
|
+
* as no rows and a failed write is reported to `onError` and swallowed, so
|
|
1518
|
+
* the hosting provider keeps its localStorage state.
|
|
1201
1519
|
*/
|
|
1202
|
-
|
|
1520
|
+
interface UserPreferenceFamily<T> {
|
|
1521
|
+
/** The key prefix this family covers. */
|
|
1522
|
+
readonly prefix: string;
|
|
1523
|
+
/**
|
|
1524
|
+
* Load every row of the family, joining the shared read of the load
|
|
1525
|
+
* (one request with the slots' keys). Resolves to `{ [key]: value }`, with
|
|
1526
|
+
* only the rows whose value is a JSON object; `{}` when there are none.
|
|
1527
|
+
*/
|
|
1528
|
+
load(): Promise<Record<string, T>>;
|
|
1529
|
+
/**
|
|
1530
|
+
* Upsert one key's row. A key outside the family is refused (reported to
|
|
1531
|
+
* `onError`, nothing written). Writes to one key run in order.
|
|
1532
|
+
*/
|
|
1533
|
+
save(key: string, value: T): Promise<void>;
|
|
1534
|
+
}
|
|
1535
|
+
/**
|
|
1536
|
+
* Build a {@link UserPreferenceFamily} backed by ObjectStack: one row per key,
|
|
1537
|
+
* loaded together by prefix in the shared read (see the module header), each
|
|
1538
|
+
* written with the same upsert a single-key adapter uses.
|
|
1539
|
+
*/
|
|
1540
|
+
declare function createObjectStackUserPreferenceFamily<T extends object = Record<string, unknown>>(options: ObjectStackUserPreferenceFamilyOptions): UserPreferenceFamily<T>;
|
|
1541
|
+
|
|
1542
|
+
/**
|
|
1543
|
+
* The served form of a picklist-bound field, turned back into the authored
|
|
1544
|
+
* form before a writer that seeded its body from a served read PUTs it
|
|
1545
|
+
* (objectui#10202, objectui#11692).
|
|
1546
|
+
*
|
|
1547
|
+
* ## Why the writer has to do this
|
|
1548
|
+
*
|
|
1549
|
+
* A select field may name a shared list instead of carrying its own options:
|
|
1550
|
+
* `picklist: 'industry'` (`FieldSchema.picklist`, `@objectstack/spec`
|
|
1551
|
+
* `data/picklist.zod.ts`). The runtime serves such a field with BOTH keys:
|
|
1552
|
+
* `picklist`, still naming the list, and `options`, RESOLVED from that list and
|
|
1553
|
+
* from every `picklistExtensions` entry other packages add to it
|
|
1554
|
+
* (`PicklistServedFieldSchema`). Every object read carries that served form —
|
|
1555
|
+
* the object list `GET /meta/object`, `GET /meta/object/NAME`, and the
|
|
1556
|
+
* `effective` layer of `GET /meta/object/NAME/layers` alike, because the
|
|
1557
|
+
* runtime resolves the binding in the one fold all three share.
|
|
1558
|
+
*
|
|
1559
|
+
* The authoring door refuses the two keys together: `FieldSchema` adds an issue
|
|
1560
|
+
* at `fields.FIELD.options` whose prescription is "Keep `picklist` and delete
|
|
1561
|
+
* `options`". So a writer that sends its seed back unchanged gets
|
|
1562
|
+
* `422 INVALID_METADATA` for the whole object, whatever the author edited —
|
|
1563
|
+
* an OWD batch edit, a relabel, a field reorder, anything.
|
|
1564
|
+
* The resolved list belongs to the picklist, not to the field: the producer of
|
|
1565
|
+
* the PUT drops it (objectstack's producer obligation, pinned server-side in
|
|
1566
|
+
* `protocol-picklist-served-roundtrip.test.ts`).
|
|
1567
|
+
*
|
|
1568
|
+
* ## What this changes, and what it never touches
|
|
1569
|
+
*
|
|
1570
|
+
* EVERY field that names a picklist leaves without `options` — not only the one
|
|
1571
|
+
* the author edited, because the whole document is re-sent and any bound field
|
|
1572
|
+
* in it is refused. A field that names no picklist is not touched at all: its
|
|
1573
|
+
* inline `options` stay byte-identical. No other key of any field, and no key of
|
|
1574
|
+
* the document, is touched.
|
|
1575
|
+
*
|
|
1576
|
+
* "Names a picklist" is the spec's own test, `picklist !== undefined` (the
|
|
1577
|
+
* `FieldSchema` refinement), so this drops `options` on exactly the fields the
|
|
1578
|
+
* door would refuse for carrying both.
|
|
1579
|
+
*
|
|
1580
|
+
* Both `fields` shapes a document can carry are handled: the spec's record keyed
|
|
1581
|
+
* by field name, and the array `readFields` / `writeFields` round-trip for a
|
|
1582
|
+
* document that arrived that way.
|
|
1583
|
+
*
|
|
1584
|
+
* ⛔ Not at the transport. `MetadataClient.save` and the other metadata-write
|
|
1585
|
+
* doors cannot tell a served copy from an author who wrote both keys, and the
|
|
1586
|
+
* second must stay a loud refusal with its prescription. The writer can tell:
|
|
1587
|
+
* it seeded its body from the served read. So this is exported for WRITERS —
|
|
1588
|
+
* the opposite of `assertObjectMetadataWritable`, which is exported for doors —
|
|
1589
|
+
* and the rule each writer applies is one sentence: a body seeded from a served
|
|
1590
|
+
* object read goes through this function on its way to the PUT; a body built
|
|
1591
|
+
* from the author's own input does not.
|
|
1592
|
+
*
|
|
1593
|
+
* ## Why it lives in this package (objectui#11692)
|
|
1594
|
+
*
|
|
1595
|
+
* The writers that seed from a served read are not all in one package: the
|
|
1596
|
+
* Studio and metadata-admin surfaces live in `@object-ui/app-shell`, and the
|
|
1597
|
+
* Setup fields and objects pages in `@object-ui/plugin-designer`, which does
|
|
1598
|
+
* not depend on `app-shell`. Both depend on this package, which also owns the
|
|
1599
|
+
* client that serves the read and carries the write — the same reason
|
|
1600
|
+
* `extractDraftBody` lives here. One function, not one copy per package: a
|
|
1601
|
+
* second copy is free to drift from the spec's served shape on its own.
|
|
1602
|
+
*
|
|
1603
|
+
* Pure: the input is never mutated, and a document with nothing to drop comes
|
|
1604
|
+
* back BY REFERENCE, so a caller can tell "unchanged" by identity.
|
|
1605
|
+
*/
|
|
1606
|
+
declare function dropServedPicklistOptions<T extends Record<string, unknown>>(body: T): T;
|
|
1203
1607
|
|
|
1204
1608
|
/**
|
|
1205
1609
|
* One issue → a single field-anchored line: `• fields.amount.type — Required`.
|
|
@@ -1272,36 +1676,31 @@ declare function formatMetadataError(e: unknown): string;
|
|
|
1272
1676
|
* live Zod pass, on the stated ground that a schema issue on a draft the server
|
|
1273
1677
|
* ACCEPTS would dead-bolt Save with no on-screen editor able to clear it.
|
|
1274
1678
|
*
|
|
1275
|
-
*
|
|
1276
|
-
* dead-bolt argument
|
|
1277
|
-
*
|
|
1278
|
-
* - The relationship refusal
|
|
1279
|
-
*
|
|
1280
|
-
*
|
|
1281
|
-
*
|
|
1282
|
-
*
|
|
1283
|
-
*
|
|
1284
|
-
*
|
|
1285
|
-
*
|
|
1286
|
-
*
|
|
1287
|
-
*
|
|
1288
|
-
*
|
|
1289
|
-
*
|
|
1290
|
-
*
|
|
1291
|
-
*
|
|
1292
|
-
*
|
|
1293
|
-
*
|
|
1294
|
-
*
|
|
1295
|
-
* already stores such a field (both pinned by their
|
|
1296
|
-
* `*.choiceWithoutOptions-11253` suites). plugin-designer's drawer has no
|
|
1297
|
-
* options editor, so it stops offering the choice types; a stored `select`
|
|
1679
|
+
* Neither invariant forecloses anything the server would have taken, and that
|
|
1680
|
+
* is the whole of the dead-bolt argument for both:
|
|
1681
|
+
*
|
|
1682
|
+
* - The relationship refusal: the same body is refused one layer down with a
|
|
1683
|
+
* 422 on `fields.NAME.reference`, so the guard only moves an identical
|
|
1684
|
+
* refusal earlier, where it can name the field while it is still on screen.
|
|
1685
|
+
* - The choice refusal: the same body is refused one layer down at
|
|
1686
|
+
* `fields.NAME.options`, an empty list included, since `@objectstack/spec`
|
|
1687
|
+
* 17.7.0 shipped the door (objectstack#21390, the ruling's own door). Until
|
|
1688
|
+
* that release the installed spec still accepted a choice with no option
|
|
1689
|
+
* source and this refusal was deliberately AHEAD of the server, by the
|
|
1690
|
+
* ruling's execution parameter (2): "if Studio saves a `select` before its
|
|
1691
|
+
* options exist, the objectui side changes its order first (a card in that
|
|
1692
|
+
* lane) and the door closes after". The door has closed, the order changed
|
|
1693
|
+
* first, and the refusal collapsed into the relationship form at
|
|
1694
|
+
* objectui#11717's bump. The editors still clear it on screen: the Studio
|
|
1695
|
+
* data page and the metadata-admin object editor carry an options editor in
|
|
1696
|
+
* that field's inspector (both pinned by their
|
|
1697
|
+
* `*.choiceWithoutOptions-11253` suites); plugin-designer's drawer has no
|
|
1698
|
+
* options editor, so it stops offering the choice types, a stored `select`
|
|
1298
1699
|
* keeps its type there and can be retyped or removed, and a stored `radio`
|
|
1299
1700
|
* is among the types that page carries through unedited and sends to
|
|
1300
1701
|
* metadata-admin, as it already does for a target-less `master_detail`.
|
|
1301
|
-
* `object-metadata-write-guard.derivation.test.ts` re-measures "
|
|
1302
|
-
*
|
|
1303
|
-
* the pin moves, that reading turns red, and this paragraph collapses into
|
|
1304
|
-
* the relationship one above.
|
|
1702
|
+
* `object-metadata-write-guard.derivation.test.ts` re-measures "refused one
|
|
1703
|
+
* layer down" for both invariants on every run.
|
|
1305
1704
|
*
|
|
1306
1705
|
* ⛔ **NOT strip-and-report-saved.** Dropping the offending field and reporting
|
|
1307
1706
|
* success would trade a visible refusal for an invisible deletion —
|
|
@@ -1323,7 +1722,7 @@ declare function formatMetadataError(e: unknown): string;
|
|
|
1323
1722
|
* - the choice set from the same members run through `checkFieldCompleteness`
|
|
1324
1723
|
* (ADR-0078's author-time rule, which the ruling's door adopts), keeping
|
|
1325
1724
|
* those it reports as `field/choice-without-options` at `error` severity.
|
|
1326
|
-
*
|
|
1725
|
+
* The door itself shipped in 17.7.0, and the pin holds the two answers equal.
|
|
1327
1726
|
*
|
|
1328
1727
|
* A spec release that changes either set turns that pin red instead of leaving
|
|
1329
1728
|
* a guard that quietly stopped covering the contract.
|
|
@@ -1359,12 +1758,25 @@ declare const OBJECT_METADATA_TYPE = "object";
|
|
|
1359
1758
|
* `fields` member — this door serves `view`, `app`, `flow`, `permission`,
|
|
1360
1759
|
* `hook` and `dashboard` writes too, and has no opinion about them.
|
|
1361
1760
|
*
|
|
1362
|
-
*
|
|
1363
|
-
*
|
|
1364
|
-
*
|
|
1761
|
+
* ## The message is the same at every door, and names no code (objectui#11785)
|
|
1762
|
+
*
|
|
1763
|
+
* The thrown text reaches authors: Studio keeps it under a refusal's Details,
|
|
1764
|
+
* and the other surfaces that save through a door show it as their banner. So
|
|
1765
|
+
* it names the field, its type and the remedy, and ⛔ no class, package or
|
|
1766
|
+
* tracker id. The rationale for each rule lives in this module's docblock, not
|
|
1767
|
+
* in the runtime string. The door's label is not printed either, which makes the
|
|
1768
|
+
* text a function of the body alone: a caller that kept the body it sent can
|
|
1769
|
+
* re-run this guard on it and get the identical message, which is how Studio
|
|
1770
|
+
* tells this refusal apart from any other failure without parsing the prose.
|
|
1771
|
+
*
|
|
1772
|
+
* @param type the metadata type segment of the write (`'object'`, `'view'`, …)
|
|
1773
|
+
* @param item the body about to be serialised onto the wire
|
|
1774
|
+
* @param _writer the door's label. Every door still names itself at the call,
|
|
1775
|
+
* and the stack trace says where the write stopped; the message
|
|
1776
|
+
* does not print it (see above)
|
|
1365
1777
|
* @throws Error naming the offending field, before any request is issued
|
|
1366
1778
|
*/
|
|
1367
|
-
declare function assertObjectMetadataWritable(type: unknown, item: unknown,
|
|
1779
|
+
declare function assertObjectMetadataWritable(type: unknown, item: unknown, _writer: string): void;
|
|
1368
1780
|
|
|
1369
1781
|
/**
|
|
1370
1782
|
* Map human-readable filter operator names produced by SDUI view configs
|
|
@@ -1682,6 +2094,20 @@ declare function isApiAccessDeniedError(error: unknown): boolean;
|
|
|
1682
2094
|
* question was never put.
|
|
1683
2095
|
*/
|
|
1684
2096
|
type AppAccessVerdict = 'granted' | 'denied' | 'not_found' | 'unreachable' | 'unknown';
|
|
2097
|
+
/**
|
|
2098
|
+
* What {@link ObjectStackAdapter.probeMetaItemAccess} obtained about one item
|
|
2099
|
+
* of any type the by-name meta route gates per caller (objectui#12112). The
|
|
2100
|
+
* verdicts mean what they mean for an app ({@link AppAccessVerdict}).
|
|
2101
|
+
*/
|
|
2102
|
+
interface MetaItemAccess {
|
|
2103
|
+
readonly verdict: AppAccessVerdict;
|
|
2104
|
+
/**
|
|
2105
|
+
* The refusal's own `message`, as the server wrote it — for a dashboard,
|
|
2106
|
+
* `You do not have permission to open the 'NAME' dashboard.` Only on
|
|
2107
|
+
* `denied`, and absent when the server wrote none.
|
|
2108
|
+
*/
|
|
2109
|
+
readonly message?: string;
|
|
2110
|
+
}
|
|
1685
2111
|
/**
|
|
1686
2112
|
* The ADR-0112 standard catalog code the by-name meta app route answers with
|
|
1687
2113
|
* when an app exists and the session lacks its `requiredPermissions`
|
|
@@ -1728,61 +2154,6 @@ declare const META_ITEM_ABSENT_CODE = "RESOURCE_NOT_FOUND";
|
|
|
1728
2154
|
* so the honest reading is that no answer was obtained.
|
|
1729
2155
|
*/
|
|
1730
2156
|
declare function isMetaItemAbsentError(error: unknown): boolean;
|
|
1731
|
-
/**
|
|
1732
|
-
* [ADR-0066] The system capability a view-configuration write requires.
|
|
1733
|
-
*
|
|
1734
|
-
* objectstack#7494's ruling (maintainer, 2026-08-12) settled what this store
|
|
1735
|
-
* IS: the `sort` / `hiddenFields` / `columnState` / `rowHeight` persisted by
|
|
1736
|
-
* {@link ObjectStackAdapter.updateViewConfig} are ORG-WIDE view configuration
|
|
1737
|
-
* — shared by every user of the view — not a per-user preference. A per-user
|
|
1738
|
-
* scope is parked (objectstack#7611, v18) and deliberately NOT built here, so
|
|
1739
|
-
* there is no second, narrower store for an ungated write to fall back to:
|
|
1740
|
-
* the only thing this write can do is change the view for everyone.
|
|
1741
|
-
*
|
|
1742
|
-
* That makes a toolbar toggle a metadata-authoring act, so it gates on the
|
|
1743
|
-
* capability this repo ALREADY uses for metadata authoring rather than a newly
|
|
1744
|
-
* minted name. `manage_metadata` is what `HomePage`'s `AUTHORING_CAPABILITY`
|
|
1745
|
-
* gates the builder CTAs on, what the server itself refuses metadata writes
|
|
1746
|
-
* without (`PackageFormDialog` reads that 403), and what
|
|
1747
|
-
* `capability.label.manage_metadata` is already translated as in all ten
|
|
1748
|
-
* locale packs. Decisively: the write gated here goes through
|
|
1749
|
-
* `client.meta.saveItem` — the very same ADR-0005 metadata door — so this is
|
|
1750
|
-
* the same authority the server is already applying, not an analogous one.
|
|
1751
|
-
*/
|
|
1752
|
-
declare const VIEW_CONFIG_CAPABILITY = "manage_metadata";
|
|
1753
|
-
/**
|
|
1754
|
-
* Thrown by {@link ObjectStackAdapter.updateViewConfig} when the session's
|
|
1755
|
-
* REPORTED capability set does not contain {@link VIEW_CONFIG_CAPABILITY}.
|
|
1756
|
-
*
|
|
1757
|
-
* Raised BEFORE `connect()` and before `saveItem`, so a refused call issues no
|
|
1758
|
-
* request at all — the org-wide row is not touched, not even optimistically.
|
|
1759
|
-
*
|
|
1760
|
-
* Deliberately an ERROR and not a silent `return`. The production caller is a
|
|
1761
|
-
* debounced toolbar toggle whose UI has ALREADY moved by the time this runs, so
|
|
1762
|
-
* a quiet no-op would leave the operator looking at a density they did not get,
|
|
1763
|
-
* with nothing to explain it and a reload to discover it. The message names the
|
|
1764
|
-
* SCOPE first and the capability second, in that order, because the scope is
|
|
1765
|
-
* the part the operator cannot otherwise see — and it is written to be shown
|
|
1766
|
-
* as-is, the same contract {@link AnalyticsNotInstalledError} states.
|
|
1767
|
-
*/
|
|
1768
|
-
declare class ViewConfigPermissionDeniedError extends Error {
|
|
1769
|
-
readonly code = "VIEW_CONFIG_PERMISSION_DENIED";
|
|
1770
|
-
/** The capability that was required and not held. */
|
|
1771
|
-
readonly capability = "manage_metadata";
|
|
1772
|
-
/** The object whose view configuration the refused write targeted. */
|
|
1773
|
-
readonly objectName: string;
|
|
1774
|
-
/** The view id the refused write targeted. */
|
|
1775
|
-
readonly viewId: string;
|
|
1776
|
-
constructor(objectName: string, viewId: string);
|
|
1777
|
-
}
|
|
1778
|
-
/**
|
|
1779
|
-
* True when `error` is a {@link ViewConfigPermissionDeniedError}.
|
|
1780
|
-
*
|
|
1781
|
-
* Duck-checks `code`/`name` rather than using `instanceof`, matching
|
|
1782
|
-
* {@link isConcurrentUpdateError}: a host that bundles this package twice
|
|
1783
|
-
* (or re-throws across a worker boundary) still gets the right verdict.
|
|
1784
|
-
*/
|
|
1785
|
-
declare function isViewConfigPermissionDeniedError(error: unknown): error is ViewConfigPermissionDeniedError;
|
|
1786
2157
|
/**
|
|
1787
2158
|
* Thrown when the deployment has no analytics capability installed
|
|
1788
2159
|
* (framework#3891 / objectstack-ai/objectstack#4019).
|
|
@@ -2121,11 +2492,9 @@ declare class ConcurrentUpdateError extends Error {
|
|
|
2121
2492
|
* reads `httpStatus` for neither. This paragraph exists because the doc used
|
|
2122
2493
|
* to name only the wire shape, which left the `name` limb reading as drift
|
|
2123
2494
|
* (objectui#6375). It is not drift: it is the deliberate cross-realm
|
|
2124
|
-
* duck-check that
|
|
2125
|
-
*
|
|
2126
|
-
*
|
|
2127
|
-
* copies of the class, `instanceof` fails, and the `name` string is the only
|
|
2128
|
-
* discriminator left. That host is out of tree by construction, so an in-repo
|
|
2495
|
+
* duck-check — a host that bundles this package twice (or re-throws across a
|
|
2496
|
+
* worker boundary) ends up holding two copies of the class, `instanceof`
|
|
2497
|
+
* fails, and the `name` string is the only discriminator left. That host is out of tree by construction, so an in-repo
|
|
2129
2498
|
* consumer census cannot see the case the limb was written for and is not
|
|
2130
2499
|
* evidence against it; `@object-ui/plugin-form` and `@object-ui/plugin-detail`
|
|
2131
2500
|
* each carry their own copy of the same two-limb check for adapters they must
|
|
@@ -2199,10 +2568,7 @@ declare function formatHttpFailureMessage(message: string, meta?: Record<string,
|
|
|
2199
2568
|
* own `ObjectStackClient` gets the same identified failures.
|
|
2200
2569
|
*/
|
|
2201
2570
|
declare function createQuietHttpLogger(): any;
|
|
2202
|
-
|
|
2203
|
-
* Connection state for monitoring
|
|
2204
|
-
*/
|
|
2205
|
-
type ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'reconnecting' | 'error';
|
|
2571
|
+
|
|
2206
2572
|
/**
|
|
2207
2573
|
* Connection state change event
|
|
2208
2574
|
*/
|
|
@@ -2281,20 +2647,31 @@ interface UnrecognizedDropReasonEvent {
|
|
|
2281
2647
|
type DroppedFieldsNotice = DroppedFieldsEvent | UnrecognizedDropReasonEvent;
|
|
2282
2648
|
/**
|
|
2283
2649
|
* Emitted after a create/update whose response carried `droppedFields`
|
|
2284
|
-
* (objectstack-ai/objectstack#3431 / objectstack-ai/objectstack#3455)
|
|
2285
|
-
*
|
|
2286
|
-
*
|
|
2650
|
+
* (objectstack-ai/objectstack#3431 / objectstack-ai/objectstack#3455) or
|
|
2651
|
+
* `warnings` (objectui#12107). The write SUCCEEDED — this is a warning that
|
|
2652
|
+
* some supplied fields never landed, or that an advisory validation rule hit,
|
|
2653
|
+
* so the UI can tell the user rather than let it pass silently. Subscribe via
|
|
2654
|
+
* {@link ObjectStackAdapter.onWriteWarning}.
|
|
2287
2655
|
*
|
|
2288
2656
|
* `droppedFields` is the two-arm {@link DroppedFieldsNotice} and not
|
|
2289
2657
|
* `DroppedFieldsEvent[]`: the wire is parsed here, and an entry whose `reason`
|
|
2290
2658
|
* this bundle's spec pin cannot name arrives on the explicit skew arm rather
|
|
2291
|
-
* than being asserted into the union (objectui#4934).
|
|
2659
|
+
* than being asserted into the union (objectui#4934). It stays REQUIRED and is
|
|
2660
|
+
* an empty array when the event exists only for `warnings`.
|
|
2661
|
+
*
|
|
2662
|
+
* `warnings` is the write answer's own key, typed by the spec's
|
|
2663
|
+
* `ValidateDataIssueSchema` (the element of `CreateDataResponseSchema` /
|
|
2664
|
+
* `UpdateDataResponseSchema`'s `warnings`) and parsed through it here. It is
|
|
2665
|
+
* present only when at least one entry parsed: an answer without the key —
|
|
2666
|
+
* including one from a runtime that predates it — leaves the event exactly as
|
|
2667
|
+
* it was.
|
|
2292
2668
|
*/
|
|
2293
2669
|
interface WriteWarningEvent {
|
|
2294
2670
|
operation: 'create' | 'update';
|
|
2295
2671
|
resource: string;
|
|
2296
2672
|
id?: string | number;
|
|
2297
2673
|
droppedFields: DroppedFieldsNotice[];
|
|
2674
|
+
warnings?: ValidateDataIssue[];
|
|
2298
2675
|
}
|
|
2299
2676
|
/** Event listener type for write-warning (dropped-fields) events. */
|
|
2300
2677
|
type WriteWarningListener = (event: WriteWarningEvent) => void;
|
|
@@ -2562,13 +2939,18 @@ declare function viewItemObjectName(item: any): string | undefined;
|
|
|
2562
2939
|
* The keys a personalization overlay row legitimately OWNS (objectui#5233).
|
|
2563
2940
|
*
|
|
2564
2941
|
* One per `persistViewPatch` call site in app-shell's `ObjectView` — the ONLY
|
|
2565
|
-
* production writer of these rows
|
|
2942
|
+
* production writer of these rows, read off the tree when it was written:
|
|
2566
2943
|
* `rowHeight` (the density toggle, spec-canonical since #2890), `sort`,
|
|
2567
|
-
* `hiddenFields
|
|
2568
|
-
*
|
|
2569
|
-
*
|
|
2570
|
-
*
|
|
2571
|
-
*
|
|
2944
|
+
* `hiddenFields` and `columnState`. Plus `inlineEdit`, which no console call
|
|
2945
|
+
* site wrote after objectui#5144 (triage's ruling E: the inline-edit toggle
|
|
2946
|
+
* is session-only). That writer is retired (objectui#12504): no toolbar
|
|
2947
|
+
* control writes an overlay, and every key here is kept so an overlay an
|
|
2948
|
+
* earlier console wrote is still read; the rulings keep that data rather than
|
|
2949
|
+
* migrating it. Nothing else in
|
|
2950
|
+
* such a row is an opinion the user expressed; anything else it carries is a
|
|
2951
|
+
* COPY of the source view as it stood at write time, because
|
|
2952
|
+
* `persistViewPatch` USED TO send `{ ...baseViewDef, ...patch }` and this
|
|
2953
|
+
* adapter persists what it is given.
|
|
2572
2954
|
*
|
|
2573
2955
|
* That copy was the defect the maintainer ruled on (objectstack#7494, comment
|
|
2574
2956
|
* 5261754173): an overlay written by a mere column drag froze the view's
|
|
@@ -2578,22 +2960,19 @@ declare function viewItemObjectName(item: any): string | undefined;
|
|
|
2578
2960
|
* view's filter and every user who once resized a column kept the old one,
|
|
2579
2961
|
* with nothing anywhere reporting it.
|
|
2580
2962
|
*
|
|
2581
|
-
* Both halves of that ruling
|
|
2582
|
-
* unchanged by either — it
|
|
2963
|
+
* Both halves of that ruling landed, and this adapter's behaviour was
|
|
2964
|
+
* unchanged by either — it persisted what it was given:
|
|
2583
2965
|
*
|
|
2584
2966
|
* - **read** (PR #5272, {@link narrowPersonalizationOverlay}): the consumer
|
|
2585
2967
|
* that MERGES an overlay over a source view contributes only these keys, so
|
|
2586
2968
|
* every already-stored fat row that carries the marker stops shadowing its
|
|
2587
2969
|
* source. A fat row written before the marker is no longer narrowed
|
|
2588
2970
|
* (objectui#10210, ruling B — see {@link isPersonalizationOverlayRow}).
|
|
2589
|
-
* - **write** (objectui#5233, `
|
|
2590
|
-
*
|
|
2591
|
-
*
|
|
2592
|
-
*
|
|
2593
|
-
*
|
|
2594
|
-
* a whole-document PUT the next toggle also strips an old fat row. A *saved
|
|
2595
|
-
* view's* own row is deliberately still written whole — for it the body IS
|
|
2596
|
-
* the view, not a copy of one.
|
|
2971
|
+
* - **write** (objectui#5233, unblocked by `columnState`'s admission to the
|
|
2972
|
+
* view-metadata surface as a runtime-only overlay key — objectstack
|
|
2973
|
+
* `d5552ca13`, released in `@objectstack/spec` 17.1.0): a *system view's*
|
|
2974
|
+
* overlay was written as the patch alone from then on, and the whole write
|
|
2975
|
+
* is retired since objectui#12504.
|
|
2597
2976
|
*
|
|
2598
2977
|
* A fat row is therefore a legacy shape, not a shape this product still
|
|
2599
2978
|
* produces; the list below is still what a reader is allowed to trust from one.
|
|
@@ -2601,9 +2980,10 @@ declare function viewItemObjectName(item: any): string | undefined;
|
|
|
2601
2980
|
* ⛔ Do not grow this list to make some other key "stick" through an overlay.
|
|
2602
2981
|
* A key that belongs to the view belongs in the view; the overlay is a patch,
|
|
2603
2982
|
* and a patch that carries the whole document is what this list exists to
|
|
2604
|
-
* stop.
|
|
2605
|
-
*
|
|
2606
|
-
*
|
|
2983
|
+
* stop. No writer of these rows is left (objectui#12504), so there is no new
|
|
2984
|
+
* key for an entry to answer; each entry is kept for reading the rows already
|
|
2985
|
+
* at rest — `inlineEdit` for rows written before objectui#5144, the other four
|
|
2986
|
+
* for rows written before objectui#12474 / objectui#12418.
|
|
2607
2987
|
*/
|
|
2608
2988
|
declare const VIEW_OVERLAY_OWNED_KEYS: readonly ["rowHeight", "sort", "hiddenFields", "columnState", "inlineEdit"];
|
|
2609
2989
|
/**
|
|
@@ -2689,6 +3069,7 @@ declare class ObjectStackAdapter<T = unknown> implements DataSource<T> {
|
|
|
2689
3069
|
private analyticsCapabilityWarned;
|
|
2690
3070
|
private fetchImpl;
|
|
2691
3071
|
private inflightFinds;
|
|
3072
|
+
private inflightFindOnes;
|
|
2692
3073
|
private missingResources;
|
|
2693
3074
|
private batchUnsupported;
|
|
2694
3075
|
private atomicBatchCapability;
|
|
@@ -2696,7 +3077,6 @@ declare class ObjectStackAdapter<T = unknown> implements DataSource<T> {
|
|
|
2696
3077
|
private writeWarningListeners;
|
|
2697
3078
|
private saveAdvisoryListeners;
|
|
2698
3079
|
private metadataReadWarningListeners;
|
|
2699
|
-
private systemCapabilities;
|
|
2700
3080
|
constructor(config: {
|
|
2701
3081
|
baseUrl: string;
|
|
2702
3082
|
token?: string;
|
|
@@ -2708,19 +3088,22 @@ declare class ObjectStackAdapter<T = unknown> implements DataSource<T> {
|
|
|
2708
3088
|
autoReconnect?: boolean;
|
|
2709
3089
|
maxReconnectAttempts?: number;
|
|
2710
3090
|
reconnectDelay?: number;
|
|
2711
|
-
/**
|
|
2712
|
-
* [ADR-0066] The session's system capabilities, when the host already has
|
|
2713
|
-
* them at construction time. Most hosts do NOT — `/me/permissions`
|
|
2714
|
-
* resolves after the adapter exists — and push them in later via
|
|
2715
|
-
* {@link ObjectStackAdapter.setSystemCapabilities}. Omit for "unreported".
|
|
2716
|
-
*/
|
|
2717
|
-
systemCapabilities?: string[];
|
|
2718
3091
|
});
|
|
2719
3092
|
/**
|
|
2720
3093
|
* Ensure the client is connected to the server.
|
|
2721
3094
|
* Call this before making requests or it will auto-connect on first request.
|
|
3095
|
+
*
|
|
3096
|
+
* With `autoReconnect`, a failed discovery read is retried up to
|
|
3097
|
+
* `maxReconnectAttempts` times with exponential backoff. The promise resolves
|
|
3098
|
+
* once discovery answers and rejects with the last `ConnectionError` once the
|
|
3099
|
+
* retries are spent (objectui#12318).
|
|
2722
3100
|
*/
|
|
2723
3101
|
connect(): Promise<void>;
|
|
3102
|
+
/**
|
|
3103
|
+
* One discovery attempt, and the retry that follows it when it fails with
|
|
3104
|
+
* budget left. Runs inside the promise {@link connect} hands every caller.
|
|
3105
|
+
*/
|
|
3106
|
+
private attemptConnect;
|
|
2724
3107
|
/**
|
|
2725
3108
|
* Attempt to reconnect to the server with exponential backoff
|
|
2726
3109
|
*/
|
|
@@ -2745,6 +3128,34 @@ declare class ObjectStackAdapter<T = unknown> implements DataSource<T> {
|
|
|
2745
3128
|
* Set connection state and notify listeners
|
|
2746
3129
|
*/
|
|
2747
3130
|
private setConnectionState;
|
|
3131
|
+
/**
|
|
3132
|
+
* Run one request through the adapter's fetch and let its outcome move the
|
|
3133
|
+
* connection state (objectui#12295). Before the first successful
|
|
3134
|
+
* {@link connect} this records nothing: `connect()` owns the state until
|
|
3135
|
+
* then, and its own discovery read passes through here too.
|
|
3136
|
+
*
|
|
3137
|
+
* - **Answered** — the fetch resolved with a Response, whatever its status.
|
|
3138
|
+
* A 4xx or 5xx is an answer: the server is reachable, so it counts the
|
|
3139
|
+
* same as a 200 and ends any outage.
|
|
3140
|
+
* - **Not answered** — the fetch rejected: no HTTP answer at all (refused,
|
|
3141
|
+
* reset, DNS, an offline browser). A rejection the caller asked for by
|
|
3142
|
+
* aborting is not evidence about the server and records nothing.
|
|
3143
|
+
*
|
|
3144
|
+
* The rejection or the Response reaches the caller unchanged.
|
|
3145
|
+
*/
|
|
3146
|
+
private observeServerAnswer;
|
|
3147
|
+
/** An answered request after the first connect: the server is reachable. */
|
|
3148
|
+
private noteServerAnswered;
|
|
3149
|
+
/**
|
|
3150
|
+
* An unanswered request after the first connect. It spends the adapter's own
|
|
3151
|
+
* retry window — the `autoReconnect` / `maxReconnectAttempts` budget that
|
|
3152
|
+
* {@link connect} grants a failed discovery — counted in the requests the
|
|
3153
|
+
* host already makes rather than in timed probes: while the budget lasts the
|
|
3154
|
+
* state is `reconnecting`, and the first unanswered request past it moves the
|
|
3155
|
+
* state to `error`. No timer and no request of the adapter's own is involved;
|
|
3156
|
+
* the next answered request, whenever it comes, restores `connected`.
|
|
3157
|
+
*/
|
|
3158
|
+
private noteServerUnanswered;
|
|
2748
3159
|
/**
|
|
2749
3160
|
* Emit batch progress event to listeners
|
|
2750
3161
|
*/
|
|
@@ -2754,6 +3165,16 @@ declare class ObjectStackAdapter<T = unknown> implements DataSource<T> {
|
|
|
2754
3165
|
* Converts OData-style params to ObjectStack query options.
|
|
2755
3166
|
*/
|
|
2756
3167
|
find(resource: string, params?: QueryParams): Promise<QueryResult<T>>;
|
|
3168
|
+
/**
|
|
3169
|
+
* Share one in-flight read among concurrent callers asking the same
|
|
3170
|
+
* question: the first call for `key` issues `read()`, every call that
|
|
3171
|
+
* arrives while it is still pending gets that same promise, and the entry is
|
|
3172
|
+
* dropped the moment it settles — so nothing is cached past the round trip,
|
|
3173
|
+
* a rejection reaches every caller that shared it and is not remembered, and
|
|
3174
|
+
* the next call after settle issues a fresh read. Used by `find` and
|
|
3175
|
+
* `findOne` (objectui#11699).
|
|
3176
|
+
*/
|
|
3177
|
+
private shareInFlight;
|
|
2757
3178
|
/**
|
|
2758
3179
|
* Full-text search across every searchable object in a single round-trip.
|
|
2759
3180
|
*
|
|
@@ -2776,8 +3197,13 @@ declare class ObjectStackAdapter<T = unknown> implements DataSource<T> {
|
|
|
2776
3197
|
}): Promise<GlobalSearchResult>;
|
|
2777
3198
|
/**
|
|
2778
3199
|
* Find a single record by ID.
|
|
3200
|
+
*
|
|
3201
|
+
* Concurrent calls for the same resource, id and params share one request,
|
|
3202
|
+
* exactly as `find` does (objectui#11699); see {@link shareInFlight}.
|
|
2779
3203
|
*/
|
|
2780
3204
|
findOne(resource: string, id: string | number, params?: QueryParams): Promise<T | null>;
|
|
3205
|
+
/** The one round trip behind {@link findOne}, unshared. */
|
|
3206
|
+
private readOne;
|
|
2781
3207
|
/**
|
|
2782
3208
|
* Create a new record.
|
|
2783
3209
|
*/
|
|
@@ -2809,6 +3235,10 @@ declare class ObjectStackAdapter<T = unknown> implements DataSource<T> {
|
|
|
2809
3235
|
* reason routed to the skew arm, a non-string field element refused, a missing
|
|
2810
3236
|
* `object` healed from `resource` — never asserted into the union, and the
|
|
2811
3237
|
* entry itself never dropped for them (objectui#4934, `f75810e7c`).
|
|
3238
|
+
*
|
|
3239
|
+
* The same answer's `warnings` (objectui#12107) rides this one event: it is
|
|
3240
|
+
* emitted when either list is non-empty, so a write whose only news is an
|
|
3241
|
+
* advisory rule hit still reaches the subscriber, with `droppedFields: []`.
|
|
2812
3242
|
*/
|
|
2813
3243
|
private notifyDroppedFields;
|
|
2814
3244
|
/**
|
|
@@ -2892,8 +3322,8 @@ declare class ObjectStackAdapter<T = unknown> implements DataSource<T> {
|
|
|
2892
3322
|
*
|
|
2893
3323
|
* `ObjectStackClient.meta.saveItem` is the second client class that writes
|
|
2894
3324
|
* through `PUT /api/v1/meta/:type/:name`, and every one of its callers reaches
|
|
2895
|
-
* it through an adapter this class constructed — the
|
|
2896
|
-
* (
|
|
3325
|
+
* it through an adapter this class constructed — the three inside this file
|
|
3326
|
+
* (the two view paths, `updateDashboard`) via
|
|
2897
3327
|
* `this.client`, and every caller outside it via {@link getClient}, which
|
|
2898
3328
|
* hands back this same instance: `MetadataService` (app-shell, five saves),
|
|
2899
3329
|
* `useNavigationSync`, and plugin-designer's Create/EditAppPage. Wrapping the
|
|
@@ -2922,16 +3352,60 @@ declare class ObjectStackAdapter<T = unknown> implements DataSource<T> {
|
|
|
2922
3352
|
*
|
|
2923
3353
|
* Drafts are NEVER gated: the framework returns at its D1 early-return
|
|
2924
3354
|
* (`if (args.state !== 'active') return null`) before running a rule, so a
|
|
2925
|
-
* draft save produces no findings to withhold.
|
|
2926
|
-
*
|
|
2927
|
-
*
|
|
2928
|
-
*
|
|
2929
|
-
* the
|
|
2930
|
-
*
|
|
2931
|
-
*
|
|
2932
|
-
*
|
|
3355
|
+
* draft save produces no findings to withhold. `mode` on the emitted event is
|
|
3356
|
+
* derived from the response's own `state`, not from the caller's options:
|
|
3357
|
+
* `'draft'` when the server says the row landed as a draft, `'publish'`
|
|
3358
|
+
* otherwise. The server's answer is the one that says which door the write
|
|
3359
|
+
* took, so the event cannot claim a door the request only asked for.
|
|
3360
|
+
*
|
|
3361
|
+
* ## Every argument is forwarded (objectui#12300)
|
|
3362
|
+
*
|
|
3363
|
+
* The SDK's `saveItem(type, name, item, options?)` carries the write's scope
|
|
3364
|
+
* in its fourth argument, `SaveMetaItemOptions`: `packageId` (`?package=`),
|
|
3365
|
+
* `force`, `mode` and `ifMatch`. This wrapper used to name three parameters
|
|
3366
|
+
* and call the original with three, so that bag was dropped before the SDK
|
|
3367
|
+
* saw it. Every caller reaching the SDK through {@link getClient} got a bare
|
|
3368
|
+
* `PUT` whatever it passed, while the declared signature still accepted the
|
|
3369
|
+
* bag, so nothing at the call site could tell. The wrapper now takes and
|
|
3370
|
+
* passes the original's own parameter list, so it forwards whatever the SDK
|
|
3371
|
+
* declares, today's bag and any later member, without naming one.
|
|
2933
3372
|
*/
|
|
2934
3373
|
private installSaveAdvisoryInterceptor;
|
|
3374
|
+
/**
|
|
3375
|
+
* Route this adapter's client's published view-list read through the one
|
|
3376
|
+
* {@link VIEW_LIST_KEY} (objectui#13044).
|
|
3377
|
+
*
|
|
3378
|
+
* The app-shell `MetadataProvider` reads `client.meta.getItems('view')`
|
|
3379
|
+
* through {@link getClient}; {@link listViews} and {@link listViewOverrides}
|
|
3380
|
+
* read the same `GET /api/v1/meta/view`. Each used to put its own request on
|
|
3381
|
+
* the wire, so one list-page open asked the server three times for one URL.
|
|
3382
|
+
*
|
|
3383
|
+
* A caller of the client keeps its contract, "ask the server": it is never
|
|
3384
|
+
* answered from a settled entry. It joins a request already in flight for
|
|
3385
|
+
* the same URL instead of sending a second one, and its answer is stored
|
|
3386
|
+
* under {@link VIEW_LIST_KEY} (see {@link MetadataCache.share}), which is
|
|
3387
|
+
* what {@link readViewList} answers this adapter's own readers from
|
|
3388
|
+
* (objectui#13057). Only the bare call is routed: any argument after the
|
|
3389
|
+
* type changes the URL, so such a call goes straight to the SDK.
|
|
3390
|
+
*
|
|
3391
|
+
* Every caller gets its own copy of the answer. Before this, every reader
|
|
3392
|
+
* got its own parsed response, so a reader that changed what it was handed
|
|
3393
|
+
* changed nothing anyone else read; a shared answer must not change that.
|
|
3394
|
+
*
|
|
3395
|
+
* Same wrap shape and the same bound as `installSaveAdvisoryInterceptor`:
|
|
3396
|
+
* `meta` is an own property of a client this adapter built and never shares.
|
|
3397
|
+
*/
|
|
3398
|
+
private installViewListShare;
|
|
3399
|
+
/**
|
|
3400
|
+
* The published view list for this adapter's own readers
|
|
3401
|
+
* ({@link listViews}, {@link listViewOverrides}): the answer stored under
|
|
3402
|
+
* {@link VIEW_LIST_KEY} while it holds one (objectui#13057), else the
|
|
3403
|
+
* client's read, which joins the request in flight for that key or starts
|
|
3404
|
+
* one, and stores its answer (objectui#13044). Always a copy the caller may
|
|
3405
|
+
* keep. Every path that should see a fresh list drops the key first; the
|
|
3406
|
+
* list is in {@link VIEW_LIST_KEY}'s doc.
|
|
3407
|
+
*/
|
|
3408
|
+
private readViewList;
|
|
2935
3409
|
create(resource: string, data: Partial<T>): Promise<T>;
|
|
2936
3410
|
/**
|
|
2937
3411
|
* Update an existing record.
|
|
@@ -3139,15 +3613,6 @@ declare class ObjectStackAdapter<T = unknown> implements DataSource<T> {
|
|
|
3139
3613
|
* @returns Promise resolving to the object schema
|
|
3140
3614
|
*/
|
|
3141
3615
|
getObjectSchema(objectName: string): Promise<unknown>;
|
|
3142
|
-
/**
|
|
3143
|
-
* ADR-0056 P2 (epic #2398) — stamp structured-widget hints onto platform
|
|
3144
|
-
* fields whose framework type is a storage primitive (e.g. `textarea`) but
|
|
3145
|
-
* whose authoring UX should be a structured editor. Only the render `widget`
|
|
3146
|
-
* is added; the field's `type` (the storage contract) is untouched. Applied
|
|
3147
|
-
* idempotently to the cached schema so form + detail both honor it. Widget
|
|
3148
|
-
* components are registered as `field:<widget>` in `@object-ui/fields`.
|
|
3149
|
-
*/
|
|
3150
|
-
private applyFieldWidgetOverrides;
|
|
3151
3616
|
/**
|
|
3152
3617
|
* Fetch a single object's schema while always revalidating the browser cache.
|
|
3153
3618
|
*
|
|
@@ -3177,6 +3642,27 @@ declare class ObjectStackAdapter<T = unknown> implements DataSource<T> {
|
|
|
3177
3642
|
name: string;
|
|
3178
3643
|
label?: string;
|
|
3179
3644
|
}>>;
|
|
3645
|
+
/**
|
|
3646
|
+
* List the items one metadata registry type declares —
|
|
3647
|
+
* `GET /api/v1/meta/:type`, through the client's `meta.getItems` — as
|
|
3648
|
+
* `{ name, label }` headers for the `position-ref` / `permission-ref` field
|
|
3649
|
+
* widgets (objectui#7611).
|
|
3650
|
+
*
|
|
3651
|
+
* Unlike {@link getObjects}, a failure REJECTS (the `DataSource` contract):
|
|
3652
|
+
* the transport's own error propagates, and an answer without the spec's
|
|
3653
|
+
* `items` list is refused rather than read as an empty registry, so a
|
|
3654
|
+
* failed load can never reach the widget as "this registry declares
|
|
3655
|
+
* nothing". Uncached, like `getObjects`: an item authored in Studio is
|
|
3656
|
+
* listed on the next form that opens.
|
|
3657
|
+
*
|
|
3658
|
+
* An item without a string `name` is not listed (the widget stores the
|
|
3659
|
+
* name, so there is nothing to store); a `label` that is not a string is
|
|
3660
|
+
* left off rather than coerced.
|
|
3661
|
+
*/
|
|
3662
|
+
listMetadataItems(type: string): Promise<Array<{
|
|
3663
|
+
name: string;
|
|
3664
|
+
label?: string;
|
|
3665
|
+
}>>;
|
|
3180
3666
|
/**
|
|
3181
3667
|
* Get access to the underlying ObjectStack client for advanced operations.
|
|
3182
3668
|
*/
|
|
@@ -3236,9 +3722,10 @@ declare class ObjectStackAdapter<T = unknown> implements DataSource<T> {
|
|
|
3236
3722
|
* Batch-fetch all persisted view overrides for an object.
|
|
3237
3723
|
*
|
|
3238
3724
|
* Per-view runtime overrides (density, column widths, sort, hidden
|
|
3239
|
-
* columns, inlineEdit …) live in the SAME metadata namespace the
|
|
3240
|
-
*
|
|
3241
|
-
* {@link
|
|
3725
|
+
* columns, inlineEdit …) live in the SAME metadata namespace the view
|
|
3726
|
+
* writes use: `type='view'`, `name=<viewId>` (see {@link createView} /
|
|
3727
|
+
* {@link updateView}; the toolbar's `updateViewConfig` wrote overlays there
|
|
3728
|
+
* until objectui#12504 retired it). Loading them per-view fires N HTTP GETs
|
|
3242
3729
|
* that 404 for every view the user never customized — console noise on
|
|
3243
3730
|
* every page load. This batch method performs a single
|
|
3244
3731
|
* `GET /api/v1/meta/view` (returns `{type, items}`) and narrows the
|
|
@@ -3262,9 +3749,9 @@ declare class ObjectStackAdapter<T = unknown> implements DataSource<T> {
|
|
|
3262
3749
|
*
|
|
3263
3750
|
* Result is cached identically to {@link getView}, and EVERY view write —
|
|
3264
3751
|
* on this adapter and above it — invalidates it through the one seam,
|
|
3265
|
-
* {@link invalidateViewKeys}. For a long time only
|
|
3266
|
-
* did (objectui#4363), which left the other three
|
|
3267
|
-
* for the cache's 5-minute TTL. That gap does not self-heal: the consumer
|
|
3752
|
+
* {@link invalidateViewKeys}. For a long time only `updateViewConfig` (since
|
|
3753
|
+
* retired, objectui#12504) did (objectui#4363), which left the other three
|
|
3754
|
+
* adapter paths stale for the cache's 5-minute TTL. That gap does not self-heal: the consumer
|
|
3268
3755
|
* (`loadViewOverrides`, app-shell `ObjectView`) treats a RESOLVED map as
|
|
3269
3756
|
* authoritative and deliberately does not re-probe per view — objectui#3774,
|
|
3270
3757
|
* and correct, since re-probing reinstates the 404 flurry the batch read
|
|
@@ -3288,82 +3775,6 @@ declare class ObjectStackAdapter<T = unknown> implements DataSource<T> {
|
|
|
3288
3775
|
* @returns Promise resolving to the view definition or null
|
|
3289
3776
|
*/
|
|
3290
3777
|
getView(objectName: string, viewId: string): Promise<unknown | null>;
|
|
3291
|
-
/**
|
|
3292
|
-
* Persist a toolbar-driven view config patch — density, column widths,
|
|
3293
|
-
* sort, hidden columns, inline edit. Symmetric counterpart to
|
|
3294
|
-
* {@link getView}: writes the row to the server metadata store via
|
|
3295
|
-
* `client.meta.saveItem`, then invalidates the matching cache entry so the
|
|
3296
|
-
* next {@link getView} reflects the new payload. Returns the persisted item
|
|
3297
|
-
* when the server echoes it, otherwise undefined.
|
|
3298
|
-
*
|
|
3299
|
-
* Called from exactly ONE production site — `ObjectView`'s
|
|
3300
|
-
* `persistViewPatch`, for the toolbar toggle — but that ONE call site
|
|
3301
|
-
* fires for BOTH kinds of active tab: a code-defined **system** view (no
|
|
3302
|
-
* row of its own yet) and a genuinely user-created **saved** view (already
|
|
3303
|
-
* has a row — the toggle is editing ITS OWN definition, not laying an
|
|
3304
|
-
* overlay on top of it). Which one a given call means is NOT re-derived
|
|
3305
|
-
* here from the write's shape (objectui#4227's own lesson: shape inference
|
|
3306
|
-
* on this namespace is exactly what let a system view masquerade as
|
|
3307
|
-
* saved) — the caller already knows, via the same `isSavedViewId`
|
|
3308
|
-
* classification that gates the switcher's readonly flag and its five
|
|
3309
|
-
* mutating handlers, and passes it as {@link opts.isSavedView}.
|
|
3310
|
-
*
|
|
3311
|
-
* - `isSavedView` false/omitted (system-view target, the common case and
|
|
3312
|
-
* the default for backward compatibility): stamps
|
|
3313
|
-
* {@link VIEW_OVERLAY_MARKER} so {@link listViews} excludes the row —
|
|
3314
|
-
* the original objectui#4227 fix.
|
|
3315
|
-
* - `isSavedView` true: the marker is withheld. Stamping it here would
|
|
3316
|
-
* flag the saved view's OWN row as a personalization overlay, and
|
|
3317
|
-
* `listViews()` would exclude it on the very next read — the user's own
|
|
3318
|
-
* view would vanish from the switcher the moment they toggled its
|
|
3319
|
-
* density (objectui#4227 follow-up, PM review on PR #4713, measured:
|
|
3320
|
-
* `persistViewPatch` has no gate on which kind of tab is active, and
|
|
3321
|
-
* this method writes to the exact same `(type='view', name=viewId)` key
|
|
3322
|
-
* {@link createView}/the ADR-0034 `viewEnvelope` seam already used for
|
|
3323
|
-
* that view, so the write is an upsert onto the saved view's row, not a
|
|
3324
|
-
* new one).
|
|
3325
|
-
*
|
|
3326
|
-
* Per objectstack#7494's ruling, the overlay this writes (system-view
|
|
3327
|
-
* case) is ORG-WIDE shared view settings, not a per-user preference — a
|
|
3328
|
-
* true per-user scope is a parked v18 direction on the platform side.
|
|
3329
|
-
*
|
|
3330
|
-
* @param objectName - Object name (e.g. 'lead')
|
|
3331
|
-
* @param viewId - View identifier (e.g. 'all_leads')
|
|
3332
|
-
* @param config - Full view definition to persist
|
|
3333
|
-
* @param opts.isSavedView - Whether `viewId` already names a saved view
|
|
3334
|
-
* (vs. a system view being personalized for the first time). Omit /
|
|
3335
|
-
* `false` for the default overlay-marking behavior.
|
|
3336
|
-
*/
|
|
3337
|
-
/**
|
|
3338
|
-
* [ADR-0066] Tell this adapter which system capabilities the session holds.
|
|
3339
|
-
*
|
|
3340
|
-
* Pass `undefined` for "no answer was reported" (a backend predating
|
|
3341
|
-
* ADR-0066, or no permission provider mounted). Pass `[]` for a real,
|
|
3342
|
-
* reported, empty grant — {@link maySetViewConfig} treats those two
|
|
3343
|
-
* differently and callers must not collapse them, which is the same
|
|
3344
|
-
* distinction `MePermissionsProvider` preserves natively (objectui#4656).
|
|
3345
|
-
*/
|
|
3346
|
-
setSystemCapabilities(capabilities: string[] | undefined): void;
|
|
3347
|
-
/**
|
|
3348
|
-
* May this session write ORG-WIDE view configuration?
|
|
3349
|
-
*
|
|
3350
|
-
* **Unknown fails OPEN**, deliberately, and this is the one judgement in the
|
|
3351
|
-
* gate worth reading twice. It is the doctrine `PermissionContextValue`
|
|
3352
|
-
* .`hasCapabilities` states for every ADR-0066 gate in this repo
|
|
3353
|
-
* (objectui#4656, framework#3923): the server enforces `manage_metadata` on
|
|
3354
|
-
* the metadata door regardless of what the client believes, so a client-side
|
|
3355
|
-
* denial on MISSING DATA cannot protect anything the server was not already
|
|
3356
|
-
* protecting — it can only break a permitted user. Failing closed here would
|
|
3357
|
-
* have refused the write on every deployment predating ADR-0066 and every
|
|
3358
|
-
* host with no permission provider, which is "break the write for everyone"
|
|
3359
|
-
* wearing the costume of a security fix.
|
|
3360
|
-
*
|
|
3361
|
-
* A REPORTED empty array gates strictly: "holds nothing" is a real answer.
|
|
3362
|
-
*/
|
|
3363
|
-
private maySetViewConfig;
|
|
3364
|
-
updateViewConfig(objectName: string, viewId: string, config: Record<string, any>, opts?: {
|
|
3365
|
-
isSavedView?: boolean;
|
|
3366
|
-
}): Promise<Record<string, any> | void>;
|
|
3367
3778
|
/**
|
|
3368
3779
|
* List user-created views for a given object via the metadata overlay
|
|
3369
3780
|
* API (ADR-0005). Replaces the legacy `find('sys_view', {...})` path
|
|
@@ -3574,6 +3985,10 @@ declare class ObjectStackAdapter<T = unknown> implements DataSource<T> {
|
|
|
3574
3985
|
* the PUBLISHED half after the draft was discarded carries the partial
|
|
3575
3986
|
* state on the error's `outcome` — "draft gone, overlay left" is exactly
|
|
3576
3987
|
* what the old `{ deleted: boolean }` could not express.
|
|
3988
|
+
* Its message is the adapter's "NOT fully removed" sentence followed, on
|
|
3989
|
+
* the next line, by the server's own reason (objectui#12416), so
|
|
3990
|
+
* `formatMetadataError(err)` shows the remedy a refusal names; the
|
|
3991
|
+
* original error stays on `cause`.
|
|
3577
3992
|
*
|
|
3578
3993
|
* Invalidates through {@link invalidateViewKeys}: the deleted row leaves the
|
|
3579
3994
|
* batch override map too, and a ghost entry there is what the object page
|
|
@@ -3662,6 +4077,28 @@ declare class ObjectStackAdapter<T = unknown> implements DataSource<T> {
|
|
|
3662
4077
|
* @param appName - the app name as it appears in the URL segment
|
|
3663
4078
|
*/
|
|
3664
4079
|
probeAppAccess(appName: string): Promise<AppAccessVerdict>;
|
|
4080
|
+
/**
|
|
4081
|
+
* {@link probeAppAccess} for any metadata type whose by-name read refuses a
|
|
4082
|
+
* caller (objectui#12112): why `name` is not in this session's list of
|
|
4083
|
+
* `type`, plus the server's own sentence when it refused.
|
|
4084
|
+
*
|
|
4085
|
+
* Since objectstack#22731 a dashboard and a list view carry the app's gate:
|
|
4086
|
+
* `GET /api/v1/meta/dashboard` and `/meta/view` list only what the caller may
|
|
4087
|
+
* open, so a withheld item and a missing one are the same absence there, and
|
|
4088
|
+
* the by-name read of one the caller does not hold answers `403`
|
|
4089
|
+
* `PERMISSION_DENIED`, "the app arm's whole refusal (same emitter, same
|
|
4090
|
+
* envelope)". The verdicts are therefore read exactly as for an app.
|
|
4091
|
+
*
|
|
4092
|
+
* `message` is the refusal's `message`, as the server wrote it: the one
|
|
4093
|
+
* sentence that names the item and says it was refused. Only on `denied`.
|
|
4094
|
+
*
|
|
4095
|
+
* Nothing here throws, and nothing is cached: a verdict about the CALLER
|
|
4096
|
+
* must not outlive a grant.
|
|
4097
|
+
*
|
|
4098
|
+
* @param type - the singular metadata type (`app`, `dashboard`, `view`)
|
|
4099
|
+
* @param name - the item's name, as the by-name route addresses it
|
|
4100
|
+
*/
|
|
4101
|
+
probeMetaItemAccess(type: string, name: string): Promise<MetaItemAccess>;
|
|
3665
4102
|
/**
|
|
3666
4103
|
* Get a page definition from ObjectStack.
|
|
3667
4104
|
* Uses the metadata API to fetch page layouts.
|
|
@@ -3897,8 +4334,8 @@ declare class ObjectStackAdapter<T = unknown> implements DataSource<T> {
|
|
|
3897
4334
|
* `DataSource<T>` interface: the object this hands back is an
|
|
3898
4335
|
* `ObjectStackAdapter`, and the adapter-only members the package README
|
|
3899
4336
|
* documents — `getClient`, the metadata-cache controls, the connection-state
|
|
3900
|
-
* and batch-progress subscriptions
|
|
3901
|
-
*
|
|
4337
|
+
* and batch-progress subscriptions — live on the class, not on `DataSource`.
|
|
4338
|
+
* Declaring the interface here narrowed all of them
|
|
3902
4339
|
* away from the factory while leaving them on the value (objectui#7323), so
|
|
3903
4340
|
* `createObjectStackAdapter(...)` and `new ObjectStackAdapter(...)` — the two
|
|
3904
4341
|
* documented ways to obtain the same object — handed back different type
|
|
@@ -3930,8 +4367,6 @@ declare function createObjectStackAdapter<T = unknown>(config: {
|
|
|
3930
4367
|
autoReconnect?: boolean;
|
|
3931
4368
|
maxReconnectAttempts?: number;
|
|
3932
4369
|
reconnectDelay?: number;
|
|
3933
|
-
/** [ADR-0066] See {@link ObjectStackAdapter.setSystemCapabilities}. */
|
|
3934
|
-
systemCapabilities?: string[];
|
|
3935
4370
|
}): ObjectStackAdapter<T>;
|
|
3936
4371
|
|
|
3937
|
-
export { API_ACCESS_DENIED_CODES, APP_PERMISSION_DENIED_CODE, AnalyticsDatasetNotFoundError, AnalyticsForbiddenError, AnalyticsKeysOnSpecShapeError, AnalyticsNotInstalledError, AnalyticsQueryRejectedError, AnalyticsResultShapeError, AnalyticsUnauthenticatedError, type AppAccessVerdict, AuthenticationError, type BatchProgressEvent, type BatchProgressListener, BulkOperationError, CHOICE_TYPES_REQUIRING_OPTIONS, ConcurrentUpdateError, ConnectionError, type
|
|
4372
|
+
export { API_ACCESS_DENIED_CODES, APP_PERMISSION_DENIED_CODE, AnalyticsDatasetNotFoundError, AnalyticsForbiddenError, AnalyticsKeysOnSpecShapeError, AnalyticsNotInstalledError, AnalyticsQueryRejectedError, AnalyticsResultShapeError, AnalyticsUnauthenticatedError, type AppAccessVerdict, AuthenticationError, type BatchProgressEvent, type BatchProgressListener, BulkOperationError, CHOICE_TYPES_REQUIRING_OPTIONS, ConcurrentUpdateError, ConnectionError, type ConnectionStateEvent, type ConnectionStateListener, DataApiValidationError, type DraftChange, type DroppedFieldsNotice, FILTER_OPERATOR_ALIASES, type ImportMappingsFailureKind, META_ITEM_ABSENT_CODE, MalformedFilterError, type MetaItemAccess, 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 MetadataListAnswer, type MetadataListOptions, type MetadataLockState, MetadataNotFoundError, type MetadataOverlayScope, type MetadataReadFailureKind, type MetadataReadWarningEvent, type MetadataReadWarningListener, type MetadataReference, type MetadataSaveAdvisoryEvent, type MetadataSaveAdvisoryListener, type MetadataValidationIssue, OBJECT_METADATA_TYPE, ObjectStackAdapter, ObjectStackError, type ObjectStackUserPreferenceFamilyOptions, type ObjectStackUserStateAdapterOptions, PREFERENCE_FAMILY_ROW_LIMIT, RELATIONSHIP_TYPES_REQUIRING_REFERENCE, UNRECOGNIZED_DROP_REASON, UnlowerableAnalyticsFilterError, UnloweredAggregateWhereError, type UnrecognizedDropReasonEvent, type UserDataAdapter, type UserPreferenceFamily, VIEW_OVERLAY_OWNED_KEYS, type WriteWarningEvent, type WriteWarningListener, assertObjectMetadataWritable, classifyAnalyticsFailure, classifyImportMappingsFailure, classifyViewsFailure, clearSharedDiscoveryCache, createErrorFromResponse, createObjectStackAdapter, createObjectStackUserPreferenceFamily, createObjectStackUserStateAdapter, createQuietHttpLogger, draftChangesUnder, dropServedPicklistOptions, extractDraftBody, formatHttpFailureMessage, formatMetadataError, formatMetadataIssue, getSharedDiscovery, governingDraftChange, is404Error, isAnalyticsNotInstalledError, isApiAccessDeniedError, isAppPermissionDeniedError, isConcurrentUpdateError, isErrorType, isMalformedFilterError, isMetaItemAbsentError, isObjectStackError, narrowPersonalizationOverlay, normaliseClientError, readDraftChanges, readSaveAdvisories, readTransactionalBatchCapability, serializeOrderBy, viewItemObjectName };
|