@object-ui/data-objectstack 17.1.0 → 17.3.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 +228 -0
- package/README.md +6 -1
- package/dist/index.cjs +56 -14
- package/dist/index.d.cts +66 -28
- package/dist/index.d.ts +66 -28
- package/dist/index.js +55 -13
- package/package.json +5 -5
- package/src/cache/MetadataCache.ts +12 -3
- package/src/errors.test.ts +12 -12
- package/src/errors.ts +23 -5
- package/src/index.ts +110 -21
- package/src/metadata-client.ts +17 -3
- package/src/onWriteWarning.test.ts +101 -0
- package/src/security.ts +18 -5
- package/src/spec-symbol-batch6.test.ts +306 -0
- package/src/validation-error.test.ts +9 -9
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,233 @@
|
|
|
1
1
|
# @object-ui/data-objectstack
|
|
2
2
|
|
|
3
|
+
## 17.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- d22ae31: Track `@objectstack/spec` 17.0.0-rc.2 (objectui#3235, #3208, #3287, #3264).
|
|
8
|
+
|
|
9
|
+
The pin moves from `^17.0.0-rc.1` to `^17.0.0-rc.2` across the workspace, and
|
|
10
|
+
the sibling `@objectstack/*` packages (`client` / `core` / `formula` / `lint`)
|
|
11
|
+
move with it — they pin `@objectstack/spec` **exactly**, so leaving them behind
|
|
12
|
+
kept a second copy of the spec in the tree and would have had `@objectstack/lint`
|
|
13
|
+
validating against rc.1 schemas that still accept keys rc.2 retires.
|
|
14
|
+
|
|
15
|
+
Breaking semantics, in FROM → TO form:
|
|
16
|
+
|
|
17
|
+
- **`app.homePageId` is retired — an app's landing page is now its first
|
|
18
|
+
navigation item.** An app that pinned a landing page with `homePageId` will
|
|
19
|
+
open on the first reachable navigation entry (by `order`) instead; the root
|
|
20
|
+
landing still follows `isDefault`. To restore a specific landing page, reorder
|
|
21
|
+
`navigation` so the intended entry comes first. Stored metadata is migrated by
|
|
22
|
+
`os migrate meta --from 16`. The key is a hard error now, not a stripped one:
|
|
23
|
+
the spec ships a tombstone that names the migration.
|
|
24
|
+
Upstream retired it because of its SHAPE, not its usage — it was an ID
|
|
25
|
+
cross-reference with no referential integrity, so a `homePageId` that pointed
|
|
26
|
+
at nothing silently fell back to the first navigation item anyway
|
|
27
|
+
(objectstack#4667, premise corrected in #4709). If the capability returns, it
|
|
28
|
+
returns as a flag on the navigation item itself, which cannot dangle.
|
|
29
|
+
- **`@object-ui/types`' `HttpMethod` now resolves to the spec's
|
|
30
|
+
`HttpMethodType`.** Shape is verbatim identical — the same 5-value UI subset —
|
|
31
|
+
and `@object-ui/types` still exports it as `HttpMethod`, so no consumer
|
|
32
|
+
changes. The spec renamed its `./ui` export because `HttpMethod` named two
|
|
33
|
+
different types depending on the import path (`./shared` / `./api` carry a
|
|
34
|
+
7-value enum including `HEAD` / `OPTIONS`); objectui deliberately keeps the
|
|
35
|
+
5-value one (objectstack#4691).
|
|
36
|
+
- **`AppContextSelector.includeAll` / `placement` are gone.** Neither ever did
|
|
37
|
+
anything in this renderer: context selectors are mandatory-scope, so no "All"
|
|
38
|
+
row was ever rendered, and `placement: 'topbar'` put nothing in the topbar.
|
|
39
|
+
Both carried schema defaults, which is why the liveness lint structurally
|
|
40
|
+
could not flag them — removal was the only channel that reaches an author
|
|
41
|
+
(framework#4509).
|
|
42
|
+
- **`NavigationArea.visible` / `order` / `requiredPermissions` are gone.** An
|
|
43
|
+
area is a layout grouping, not an access boundary. Gating moved down to the
|
|
44
|
+
navigation ITEM, where `visible` and `requiredPermissions` are unchanged and
|
|
45
|
+
still enforced. `AppSchemaRenderer`'s area switcher no longer hides an area, so
|
|
46
|
+
an area whose items are all gated away renders as visible-but-empty rather
|
|
47
|
+
than disappearing.
|
|
48
|
+
- **`@object-ui/core` no longer exports `NotificationProtocol`**
|
|
49
|
+
(`resolveNotificationConfig`, `specNotificationToToast`, `mapSeverityToVariant`,
|
|
50
|
+
`mapPosition`, `ToastNotification`). It bridged `@objectstack/spec/ui`'s
|
|
51
|
+
`Notification` / `NotificationConfig`, which objectstack#4610 deleted with no
|
|
52
|
+
successor. Use `resolveNotificationConfig` from `@object-ui/react`
|
|
53
|
+
(`NotificationContext`), which owns the live `NotificationSystemConfig` and is
|
|
54
|
+
what every notification surface already read. Note that the spec's _other_
|
|
55
|
+
`Notification` — `@objectstack/spec/api` — is the REST inbox row, a different
|
|
56
|
+
contract, and is deliberately NOT aliased in as a replacement.
|
|
57
|
+
- **The `email_template` client-side validator now uses
|
|
58
|
+
`EmailTemplateDefinitionSchema`.** It was pointing at the removed
|
|
59
|
+
`EmailTemplateSchema`, so authored templates were being checked against the
|
|
60
|
+
wrong contract: the live one is keyed `name` + `locale` (not `id`) and splits
|
|
61
|
+
the body into `bodyHtml` / `bodyText` (not `body` + `bodyType`)
|
|
62
|
+
(objectstack#4616 / #4807).
|
|
63
|
+
|
|
64
|
+
Fixes that are not breaking, but were only found because rc.2 stopped being
|
|
65
|
+
lenient — each had been passing vacuously:
|
|
66
|
+
|
|
67
|
+
- **`view` drafts are actually validated now.** The client validator named the
|
|
68
|
+
aggregated container schema while this admin authors first-class `ViewItem`s,
|
|
69
|
+
and the container used to strip `viewKind` / `config` in silence — so no view
|
|
70
|
+
draft ever had one of its own keys checked. It now validates each shape
|
|
71
|
+
against its own schema (objectui#3312).
|
|
72
|
+
- **The console's worked examples were wrong**, and being stripped rather than
|
|
73
|
+
refused: `view.list.object` (the container root already declares it),
|
|
74
|
+
`job.concurrency` / `job.timeoutMs` (no such keys; the spelling is `timeout`,
|
|
75
|
+
already in ms), `email_template.from` / `.to` (a template is not a send —
|
|
76
|
+
the sender override is `fromOverride`, an object), and
|
|
77
|
+
`datasource.capabilities` / `.healthCheck` (objectstack#4583 removed the
|
|
78
|
+
former; the latter was never a datasource key). These are the drafts an
|
|
79
|
+
author — or a model generating metadata — copies.
|
|
80
|
+
- Action key inventory re-derived: `ActionSchema` gained the package-lock
|
|
81
|
+
envelope (`_lock*` / `_package*` / `_provenance`), so a packaged action no
|
|
82
|
+
longer reports them as unknown keys.
|
|
83
|
+
- The schema-diff panel labels the new `default_mismatch` finding.
|
|
84
|
+
- Test fixtures pinning the retired `managedBy: 'system'` bucket now use
|
|
85
|
+
`engine-owned`. Protocol 17 split that value (objectstack#3355), so it
|
|
86
|
+
resolved to the default-writable fallback and a batch of "stays locked"
|
|
87
|
+
assertions had quietly stopped asserting anything.
|
|
88
|
+
|
|
89
|
+
### Patch Changes
|
|
90
|
+
|
|
91
|
+
- Updated dependencies [18cd432]
|
|
92
|
+
- Updated dependencies [d915c47]
|
|
93
|
+
- Updated dependencies [5781fb1]
|
|
94
|
+
- Updated dependencies [9e9e9a9]
|
|
95
|
+
- Updated dependencies [23018cc]
|
|
96
|
+
- Updated dependencies [d915c47]
|
|
97
|
+
- Updated dependencies [f44d872]
|
|
98
|
+
- Updated dependencies [509104a]
|
|
99
|
+
- Updated dependencies [a4cff5b]
|
|
100
|
+
- Updated dependencies [f833d3a]
|
|
101
|
+
- Updated dependencies [2a9513d]
|
|
102
|
+
- Updated dependencies [d22ae31]
|
|
103
|
+
- @object-ui/core@17.3.0
|
|
104
|
+
- @object-ui/types@17.3.0
|
|
105
|
+
|
|
106
|
+
## 17.2.0
|
|
107
|
+
|
|
108
|
+
### Minor Changes
|
|
109
|
+
|
|
110
|
+
- c5ccbd5: Stop declaring 12 `@object-ui/data-objectstack` / `@object-ui/plugin-chatbot` /
|
|
111
|
+
`@object-ui/plugin-list` symbols under names `@objectstack/spec` owns
|
|
112
|
+
(objectui#3160, objectstack#4115 batch 6). All three packages leave the ledger.
|
|
113
|
+
|
|
114
|
+
**Breaking for importers of `@object-ui/data-objectstack`** — four exported
|
|
115
|
+
names changed, because the spec exports the same name for a _different_ thing:
|
|
116
|
+
|
|
117
|
+
| was | now | what the spec's same-named export actually is |
|
|
118
|
+
| :-------------------- | :-------------------------- | :--------------------------------------------------------------------------------------- |
|
|
119
|
+
| `CacheStats` | `MetadataCacheStats` | the platform `ICacheService` counters (`keyCount`, `memoryUsage`) |
|
|
120
|
+
| `MetadataSaveOptions` | `MetadataClientSaveOptions` | options for writing a metadata item to a **file** (`format`, `path`, `indent`, `atomic`) |
|
|
121
|
+
| `SecurityPolicy` | `SecurityManagerPolicy` | the package supply-chain policy (`autoScan`, licences, code signing, sandbox) |
|
|
122
|
+
| `ValidationError` | `DataApiValidationError` | a plain `{ field, message, code? }` entry in a validation report |
|
|
123
|
+
|
|
124
|
+
Each pair is disjoint or nearly so — `MetadataSaveOptions` and `SecurityPolicy`
|
|
125
|
+
share not one key with the spec type whose name they wore — so none of them was
|
|
126
|
+
a dialect to reconcile; they were four unrelated concepts squatting on spec
|
|
127
|
+
names. `DataApiValidationError` follows the `<what was validated>Validation<Error|Result>`
|
|
128
|
+
convention registered on objectstack#4115 (`@object-ui/core` took
|
|
129
|
+
`SchemaNodeValidationError` in batch 4). Its **runtime** `name` deliberately
|
|
130
|
+
stays `'ValidationError'`: `normaliseClientError` and `@object-ui/react`'s
|
|
131
|
+
error-message helper both sniff `err.name`, so that string is a wire contract,
|
|
132
|
+
not a symbol.
|
|
133
|
+
|
|
134
|
+
**Breaking for importers of `@object-ui/plugin-chatbot`** — `PendingActionRow`
|
|
135
|
+
and `PendingActionStatus` are now re-exported from `@objectstack/spec/contracts`
|
|
136
|
+
instead of hand-transcribed, which narrows them. The copies had drifted three
|
|
137
|
+
ways, and each drift had **disabled a compile-time check** rather than merely
|
|
138
|
+
differed from one:
|
|
139
|
+
|
|
140
|
+
- `status: PendingActionStatus | string` — a union with `string` absorbs the
|
|
141
|
+
literals, so that annotation carried no information at all;
|
|
142
|
+
- `[key: string]: unknown` — the objectstack#4075 mechanism: an index signature
|
|
143
|
+
makes every structural comparison against the spec answer "identical", however
|
|
144
|
+
far the copy has drifted;
|
|
145
|
+
- `created_at` / `updated_at`, which the service contract does not carry and no
|
|
146
|
+
consumer in this repo reads.
|
|
147
|
+
|
|
148
|
+
**Breaking for importers of `@object-ui/plugin-list`** — `ViewTab` is derived from the spec's `ViewTabSchema`
|
|
149
|
+
— from its **input** side, because `pinned` / `isDefault` / `visible` carry
|
|
150
|
+
`.default()`s and this component is handed authored metadata, not parsed output.
|
|
151
|
+
That removes a renderer-side tolerance the copy carried: `visible` accepted
|
|
152
|
+
`string | boolean` and the tab bar compared it against the literal `'false'`, a
|
|
153
|
+
spelling no producer emits. `label` also stops being required (the spec makes it
|
|
154
|
+
optional; `name` is the identifier) and `filter` stops being `any`.
|
|
155
|
+
|
|
156
|
+
`ListView` and `UserFilters` keep their names as declared dialects: both are the
|
|
157
|
+
React **renderers** of the spec types whose names they share, and each takes that
|
|
158
|
+
spec type as a prop (`ListViewProps.schema`, `UserFiltersProps.config`) rather
|
|
159
|
+
than restating its shape. `Tool` and `MessageContent` in `plugin-chatbot` are
|
|
160
|
+
vendored Vercel AI Elements / Shadcn primitives — upstream's component API, not
|
|
161
|
+
objectui's authored surface — so the guard now skips that directory the same way
|
|
162
|
+
it already skips `components/src/ui/`, with a test that fails if any file there
|
|
163
|
+
stops carrying its vendor banner.
|
|
164
|
+
|
|
165
|
+
Scored `minor`, not `major`, per this repo's fixed-group rule — objectui's major
|
|
166
|
+
tracks `@objectstack`, so breaking changes of our own ship as minor with the
|
|
167
|
+
semantics spelled out above (see AGENTS.md §版本号策略). A `major` here would carry
|
|
168
|
+
all 39 packages of the fixed group to `18.0.0` and off objectstack's 17.x line.
|
|
169
|
+
|
|
170
|
+
- d3584c6: Bring the whole `@objectstack` family to `17.0.0-rc.1`, so the dependency graph resolves a
|
|
171
|
+
single copy of `@objectstack/spec`.
|
|
172
|
+
|
|
173
|
+
#3178 bumped **only** `@objectstack/spec` to `17.0.0-rc.1`. The rest of the family —
|
|
174
|
+
`client`, `core`, `formula`, `lint` (and `sdui-parser`, reached through `lint`) — stayed on
|
|
175
|
+
`17.0.0-rc.0`, and each of them depends on spec at an **exact** version rather than a
|
|
176
|
+
caret:
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
@objectstack/client@17.0.0-rc.0 -> spec "17.0.0-rc.0"
|
|
180
|
+
@objectstack/core@17.0.0-rc.0 -> spec "17.0.0-rc.0"
|
|
181
|
+
@objectstack/formula@17.0.0-rc.0 -> spec "17.0.0-rc.0"
|
|
182
|
+
@objectstack/lint@17.0.0-rc.0 -> spec "17.0.0-rc.0"
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
So `main` carried **two** spec copies: objectui's own code read `17.0.0-rc.1` while every
|
|
186
|
+
`@objectstack/*` package read `17.0.0-rc.0` from its own nested `node_modules`. That breaks
|
|
187
|
+
the single-contract invariant this repo's guards are built on, and it breaks them
|
|
188
|
+
_silently_ — the affected checks depend on identity, not on version strings:
|
|
189
|
+
|
|
190
|
+
- `spec-subschema-parity.test.ts` distinguishes a genuine re-export from a fork by
|
|
191
|
+
**reference identity** of the zod schema object. Two spec copies make every schema a
|
|
192
|
+
distinct object, so a real re-export starts reading as a fork (or a fork slips through,
|
|
193
|
+
depending on which copy each side resolved).
|
|
194
|
+
- `scripts/check-spec-symbol-derivation.mjs` and `spec-symbol-parity.test.ts` use
|
|
195
|
+
`createRequire` to resolve spec's `.d.ts` and run it through the TS checker. With two
|
|
196
|
+
copies installed, _which_ declaration file the checker sees is a function of resolution
|
|
197
|
+
order rather than of intent.
|
|
198
|
+
|
|
199
|
+
The declared ranges were already `^17.0.0-rc.0`, which technically admits rc.1 — the pin
|
|
200
|
+
lived in the lockfile. Raising the remaining ranges to `^17.0.0-rc.1` makes the floor
|
|
201
|
+
explicit and forbids a future install from silently sliding back onto a family member that
|
|
202
|
+
drags rc.0 along with it. The rc.1 family members pin spec at `17.0.0-rc.1` exactly, so the
|
|
203
|
+
graph now converges on one copy by construction, not by luck.
|
|
204
|
+
|
|
205
|
+
No product behaviour changes here. `check:spec-symbols` reconciliation was already
|
|
206
|
+
completed by #3178 and stays green under the unified graph; this changeset is `minor`
|
|
207
|
+
per the repo's fixed-group version policy.
|
|
208
|
+
|
|
209
|
+
### Patch Changes
|
|
210
|
+
|
|
211
|
+
- Updated dependencies [4ae0ac4]
|
|
212
|
+
- Updated dependencies [696e3c1]
|
|
213
|
+
- Updated dependencies [bca45cc]
|
|
214
|
+
- Updated dependencies [4bf612c]
|
|
215
|
+
- Updated dependencies [335041c]
|
|
216
|
+
- Updated dependencies [b414983]
|
|
217
|
+
- Updated dependencies [256f8cc]
|
|
218
|
+
- Updated dependencies [d9668a7]
|
|
219
|
+
- Updated dependencies [cb82705]
|
|
220
|
+
- Updated dependencies [f572849]
|
|
221
|
+
- Updated dependencies [d3584c6]
|
|
222
|
+
- Updated dependencies [a8ad6c0]
|
|
223
|
+
- Updated dependencies [444457c]
|
|
224
|
+
- Updated dependencies [850033c]
|
|
225
|
+
- Updated dependencies [022e4c3]
|
|
226
|
+
- Updated dependencies [009e25d]
|
|
227
|
+
- Updated dependencies [726b89c]
|
|
228
|
+
- @object-ui/types@17.2.0
|
|
229
|
+
- @object-ui/core@17.2.0
|
|
230
|
+
|
|
3
231
|
## 17.1.0
|
|
4
232
|
|
|
5
233
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -179,7 +179,12 @@ import {
|
|
|
179
179
|
BulkOperationError, // Bulk operation failures with partial results
|
|
180
180
|
ConnectionError, // Network/connection errors (503/504)
|
|
181
181
|
AuthenticationError, // Authentication failures (401/403)
|
|
182
|
-
|
|
182
|
+
DataApiValidationError, // Data validation errors (400). Its runtime `name` is
|
|
183
|
+
// still 'ValidationError' — that string is the wire
|
|
184
|
+
// discriminator shared with @objectstack/client. The
|
|
185
|
+
// SYMBOL is prefixed because @objectstack/spec/kernel
|
|
186
|
+
// owns `ValidationError` for a { field, message, code? }
|
|
187
|
+
// record (objectui#3160).
|
|
183
188
|
} from '@object-ui/data-objectstack';
|
|
184
189
|
```
|
|
185
190
|
|
package/dist/index.cjs
CHANGED
|
@@ -29,6 +29,7 @@ __export(index_exports, {
|
|
|
29
29
|
CloudOperations: () => CloudOperations,
|
|
30
30
|
ConcurrentUpdateError: () => ConcurrentUpdateError,
|
|
31
31
|
ConnectionError: () => ConnectionError,
|
|
32
|
+
DataApiValidationError: () => DataApiValidationError,
|
|
32
33
|
FILTER_OPERATOR_ALIASES: () => FILTER_OPERATOR_ALIASES,
|
|
33
34
|
IntegrationManager: () => IntegrationManager,
|
|
34
35
|
MalformedFilterError: () => MalformedFilterError,
|
|
@@ -37,7 +38,6 @@ __export(index_exports, {
|
|
|
37
38
|
ObjectStackAdapter: () => ObjectStackAdapter,
|
|
38
39
|
ObjectStackError: () => ObjectStackError,
|
|
39
40
|
SecurityManager: () => SecurityManager,
|
|
40
|
-
ValidationError: () => ValidationError,
|
|
41
41
|
calculateAutoLayout: () => calculateAutoLayout,
|
|
42
42
|
classifyAnalyticsFailure: () => classifyAnalyticsFailure,
|
|
43
43
|
clearSharedDiscoveryCache: () => clearSharedDiscoveryCache,
|
|
@@ -76,6 +76,7 @@ function errorCodeIsAnyOf(err, codes) {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
// ../core/src/utils/filter-converter.ts
|
|
79
|
+
var import_ui = require("@objectstack/spec/ui");
|
|
79
80
|
var FilterOperatorError = class extends Error {
|
|
80
81
|
constructor(message) {
|
|
81
82
|
super(message);
|
|
@@ -933,10 +934,10 @@ var AuthenticationError = class extends ObjectStackError {
|
|
|
933
934
|
this.name = "AuthenticationError";
|
|
934
935
|
}
|
|
935
936
|
};
|
|
936
|
-
var
|
|
937
|
+
var DataApiValidationError = class extends ObjectStackError {
|
|
937
938
|
/**
|
|
938
|
-
* Create a new
|
|
939
|
-
*
|
|
939
|
+
* Create a new DataApiValidationError
|
|
940
|
+
*
|
|
940
941
|
* @param message - Human-readable error message
|
|
941
942
|
* @param field - The field that failed validation (optional)
|
|
942
943
|
* @param validationErrors - Array of validation error details
|
|
@@ -987,7 +988,7 @@ function createErrorFromResponse(response, context) {
|
|
|
987
988
|
}
|
|
988
989
|
return new ObjectStackError(message, "NOT_FOUND", 404, details);
|
|
989
990
|
case 400:
|
|
990
|
-
return new
|
|
991
|
+
return new DataApiValidationError(message, void 0, response?.data?.errors, details);
|
|
991
992
|
case 503:
|
|
992
993
|
return new ConnectionError(message, response?.config?.url, details, 503);
|
|
993
994
|
case 504:
|
|
@@ -1504,7 +1505,7 @@ var FILTER_OPERATOR_ALIASES = {
|
|
|
1504
1505
|
before: "<",
|
|
1505
1506
|
after: ">"
|
|
1506
1507
|
};
|
|
1507
|
-
function
|
|
1508
|
+
function normalizeFilterOperator2(op) {
|
|
1508
1509
|
if (typeof op !== "string") return null;
|
|
1509
1510
|
const lower = op.toLowerCase();
|
|
1510
1511
|
return FILTER_OPERATOR_ALIASES[lower] ?? FILTER_OPERATOR_ALIASES[op] ?? op;
|
|
@@ -1533,7 +1534,7 @@ function objectFilterEntryToAST(entry) {
|
|
|
1533
1534
|
if (!entry || typeof entry !== "object") return null;
|
|
1534
1535
|
const field = entry.field;
|
|
1535
1536
|
const rawOp = entry.operator ?? entry.op ?? "=";
|
|
1536
|
-
const op =
|
|
1537
|
+
const op = normalizeFilterOperator2(rawOp);
|
|
1537
1538
|
if (!field || !op) return null;
|
|
1538
1539
|
return [String(field), op, entry.value];
|
|
1539
1540
|
}
|
|
@@ -1700,7 +1701,7 @@ function normaliseClientError(error) {
|
|
|
1700
1701
|
const message = typeof rec.message === "string" && rec.message.trim() ? rec.message : typeof rec.code === "string" ? rec.code : "";
|
|
1701
1702
|
return { field, message };
|
|
1702
1703
|
}).filter((x) => x !== null);
|
|
1703
|
-
return new
|
|
1704
|
+
return new DataApiValidationError(
|
|
1704
1705
|
typeof e.message === "string" ? e.message : "Validation failed",
|
|
1705
1706
|
validationErrors[0]?.field,
|
|
1706
1707
|
validationErrors,
|
|
@@ -1751,6 +1752,31 @@ function stableStringify(value) {
|
|
|
1751
1752
|
const keys = Object.keys(obj).sort();
|
|
1752
1753
|
return `{${keys.map((k) => `${JSON.stringify(k)}:${stableStringify(obj[k])}`).join(",")}}`;
|
|
1753
1754
|
}
|
|
1755
|
+
function sameWireValue(a, b) {
|
|
1756
|
+
if (a === b) return true;
|
|
1757
|
+
const aEmpty = a === null || a === void 0;
|
|
1758
|
+
const bEmpty = b === null || b === void 0;
|
|
1759
|
+
if (aEmpty || bEmpty) return aEmpty && bEmpty;
|
|
1760
|
+
return stableStringify(a) === stableStringify(b);
|
|
1761
|
+
}
|
|
1762
|
+
function withoutNoOpDrops(droppedFields, sent, stored) {
|
|
1763
|
+
if (!sent || !stored || typeof sent !== "object" || typeof stored !== "object") {
|
|
1764
|
+
return droppedFields;
|
|
1765
|
+
}
|
|
1766
|
+
const out = [];
|
|
1767
|
+
for (const e of droppedFields) {
|
|
1768
|
+
const kept = e.fields.filter((f) => {
|
|
1769
|
+
if (!Object.prototype.hasOwnProperty.call(sent, f)) return true;
|
|
1770
|
+
if (!Object.prototype.hasOwnProperty.call(stored, f)) return true;
|
|
1771
|
+
return !sameWireValue(
|
|
1772
|
+
sent[f],
|
|
1773
|
+
stored[f]
|
|
1774
|
+
);
|
|
1775
|
+
});
|
|
1776
|
+
if (kept.length > 0) out.push({ ...e, fields: kept });
|
|
1777
|
+
}
|
|
1778
|
+
return out;
|
|
1779
|
+
}
|
|
1754
1780
|
var ObjectStackAdapter = class {
|
|
1755
1781
|
constructor(config) {
|
|
1756
1782
|
__publicField(this, "client");
|
|
@@ -2127,12 +2153,14 @@ var ObjectStackAdapter = class {
|
|
|
2127
2153
|
* whose response type predates `droppedFields`: the field is read structurally
|
|
2128
2154
|
* and validated, so an older client (or a backend that never drops) is a no-op.
|
|
2129
2155
|
*/
|
|
2130
|
-
notifyDroppedFields(operation, resource, result, id) {
|
|
2156
|
+
notifyDroppedFields(operation, resource, result, id, sent) {
|
|
2131
2157
|
const dropped = result?.droppedFields;
|
|
2132
2158
|
if (!Array.isArray(dropped) || dropped.length === 0) return;
|
|
2133
|
-
const
|
|
2159
|
+
const valid = dropped.filter(
|
|
2134
2160
|
(e) => !!e && typeof e === "object" && Array.isArray(e.fields) && e.fields.length > 0
|
|
2135
2161
|
);
|
|
2162
|
+
const stored = result?.record;
|
|
2163
|
+
const droppedFields = withoutNoOpDrops(valid, sent, stored);
|
|
2136
2164
|
if (droppedFields.length === 0) return;
|
|
2137
2165
|
this.emitWriteWarning({ operation, resource, ...id !== void 0 ? { id } : {}, droppedFields });
|
|
2138
2166
|
}
|
|
@@ -2151,17 +2179,25 @@ var ObjectStackAdapter = class {
|
|
|
2151
2179
|
notifyBatchDroppedFields(operations, payload) {
|
|
2152
2180
|
const dropped = payload?.droppedFields;
|
|
2153
2181
|
if (!Array.isArray(dropped) || dropped.length === 0) return;
|
|
2182
|
+
const results = payload?.results;
|
|
2154
2183
|
for (const entry of dropped) {
|
|
2155
2184
|
if (!entry || typeof entry !== "object") continue;
|
|
2156
2185
|
const e = entry;
|
|
2157
2186
|
if (!Array.isArray(e.fields) || e.fields.length === 0) continue;
|
|
2158
2187
|
const op = typeof e.index === "number" ? operations[e.index] : void 0;
|
|
2188
|
+
const stored = typeof e.index === "number" && Array.isArray(results) ? results[e.index] : void 0;
|
|
2189
|
+
const [live] = withoutNoOpDrops(
|
|
2190
|
+
[{ object: e.object, fields: e.fields, reason: e.reason }],
|
|
2191
|
+
op?.data,
|
|
2192
|
+
stored
|
|
2193
|
+
);
|
|
2194
|
+
if (!live) continue;
|
|
2159
2195
|
const operation = (op?.action ?? "create") === "create" ? "create" : "update";
|
|
2160
2196
|
this.emitWriteWarning({
|
|
2161
2197
|
operation,
|
|
2162
2198
|
resource: e.object ?? op?.object ?? "",
|
|
2163
2199
|
...op?.id !== void 0 && op?.id !== null ? { id: op.id } : {},
|
|
2164
|
-
droppedFields: [
|
|
2200
|
+
droppedFields: [live]
|
|
2165
2201
|
});
|
|
2166
2202
|
}
|
|
2167
2203
|
}
|
|
@@ -2181,7 +2217,13 @@ var ObjectStackAdapter = class {
|
|
|
2181
2217
|
try {
|
|
2182
2218
|
const result = await this.client.data.create(resource, data);
|
|
2183
2219
|
this.emitMutation({ type: "create", resource, record: { ...result.record } });
|
|
2184
|
-
this.notifyDroppedFields(
|
|
2220
|
+
this.notifyDroppedFields(
|
|
2221
|
+
"create",
|
|
2222
|
+
resource,
|
|
2223
|
+
result,
|
|
2224
|
+
result.record?.id,
|
|
2225
|
+
data
|
|
2226
|
+
);
|
|
2185
2227
|
return result.record;
|
|
2186
2228
|
} catch (err) {
|
|
2187
2229
|
throw normaliseClientError(err);
|
|
@@ -2208,7 +2250,7 @@ var ObjectStackAdapter = class {
|
|
|
2208
2250
|
opts?.ifMatch ? { ifMatch: opts.ifMatch } : void 0
|
|
2209
2251
|
);
|
|
2210
2252
|
this.emitMutation({ type: "update", resource, id, record: { ...result.record } });
|
|
2211
|
-
this.notifyDroppedFields("update", resource, result, id);
|
|
2253
|
+
this.notifyDroppedFields("update", resource, result, id, data);
|
|
2212
2254
|
return result.record;
|
|
2213
2255
|
} catch (err) {
|
|
2214
2256
|
throw normaliseClientError(err);
|
|
@@ -3736,6 +3778,7 @@ function createObjectStackAdapter(config) {
|
|
|
3736
3778
|
CloudOperations,
|
|
3737
3779
|
ConcurrentUpdateError,
|
|
3738
3780
|
ConnectionError,
|
|
3781
|
+
DataApiValidationError,
|
|
3739
3782
|
FILTER_OPERATOR_ALIASES,
|
|
3740
3783
|
IntegrationManager,
|
|
3741
3784
|
MalformedFilterError,
|
|
@@ -3744,7 +3787,6 @@ function createObjectStackAdapter(config) {
|
|
|
3744
3787
|
ObjectStackAdapter,
|
|
3745
3788
|
ObjectStackError,
|
|
3746
3789
|
SecurityManager,
|
|
3747
|
-
ValidationError,
|
|
3748
3790
|
calculateAutoLayout,
|
|
3749
3791
|
classifyAnalyticsFailure,
|
|
3750
3792
|
clearSharedDiscoveryCache,
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { ObjectStackClient } from '@objectstack/client';
|
|
2
|
+
import { DroppedFieldsEvent } from '@objectstack/spec/data';
|
|
3
|
+
export { DroppedFieldsEvent } from '@objectstack/spec/data';
|
|
2
4
|
import { DataSource, QueryParams, QueryResult, GlobalSearchResult, DataSourceMutationEvent, BatchTransactionOperation, ImportRequestOptions, ImportRecordsResult, CreateImportJobResult, ImportJobProgressInfo, ImportJobResultsInfo, ListImportJobsOptions, ImportJobSummaryInfo, ImportJobUndoResult, ExportDownloadRequest, FileUploadResult } from '@object-ui/types';
|
|
3
5
|
export { FileUploadResult } from '@object-ui/types';
|
|
4
6
|
|
|
@@ -95,16 +97,28 @@ declare class AuthenticationError extends ObjectStackError {
|
|
|
95
97
|
constructor(message?: string, details?: Record<string, unknown>, statusCode?: number);
|
|
96
98
|
}
|
|
97
99
|
/**
|
|
98
|
-
* Error thrown when data
|
|
100
|
+
* Error thrown when the ObjectStack data API rejects a write as invalid.
|
|
101
|
+
*
|
|
102
|
+
* NOT the spec's `ValidationError` (`@objectstack/spec/kernel`), whose name this
|
|
103
|
+
* class wore until objectui#3160 (objectstack#4115 ledger batch 6). That one is
|
|
104
|
+
* a plain DATA SHAPE — `{ field, message, code? }`, one entry in a plugin
|
|
105
|
+
* manifest's validation report — and `@object-ui/types` re-exports it under that
|
|
106
|
+
* name. This is a runtime `Error` subclass carrying an HTTP status plus a list
|
|
107
|
+
* of such entries, so the two are not even the same KIND of thing.
|
|
108
|
+
*
|
|
109
|
+
* The name follows the convention registered on objectstack#4115 for this
|
|
110
|
+
* family — `<what was validated>Validation<Error|Result>`. `@object-ui/core`
|
|
111
|
+
* took `SchemaNodeValidationError` for its SDUI-tree walk; this one belongs to
|
|
112
|
+
* the data API.
|
|
99
113
|
*/
|
|
100
|
-
declare class
|
|
114
|
+
declare class DataApiValidationError extends ObjectStackError {
|
|
101
115
|
field?: string | undefined;
|
|
102
116
|
validationErrors?: Array<{
|
|
103
117
|
field: string;
|
|
104
118
|
message: string;
|
|
105
119
|
}> | undefined;
|
|
106
120
|
/**
|
|
107
|
-
* Create a new
|
|
121
|
+
* Create a new DataApiValidationError
|
|
108
122
|
*
|
|
109
123
|
* @param message - Human-readable error message
|
|
110
124
|
* @param field - The field that failed validation (optional)
|
|
@@ -148,9 +162,18 @@ declare function isErrorType<T extends ObjectStackError>(error: unknown, errorCl
|
|
|
148
162
|
* LICENSE file in the root directory of this source tree.
|
|
149
163
|
*/
|
|
150
164
|
/**
|
|
151
|
-
*
|
|
165
|
+
* Statistics reported by {@link MetadataCache.getStats} for monitoring.
|
|
166
|
+
*
|
|
167
|
+
* NOT the spec's `CacheStats` (`@objectstack/spec/contracts`), whose name this
|
|
168
|
+
* interface wore until objectui#3160 (objectstack#4115 ledger batch 6). That one
|
|
169
|
+
* describes the platform's `ICacheService` — a server-side KV cache measured by
|
|
170
|
+
* `keyCount` and `memoryUsage`. This one describes the browser-side LRU in front
|
|
171
|
+
* of `/api/v1/meta/*`: it is bounded (`size`/`maxSize`), it evicts, it coalesces
|
|
172
|
+
* concurrent fetches onto one in-flight promise, and it reports a `hitRate`.
|
|
173
|
+
* Neither type has a key the other has, so this is a name collision, not a
|
|
174
|
+
* dialect — deriving would have replaced every field.
|
|
152
175
|
*/
|
|
153
|
-
interface
|
|
176
|
+
interface MetadataCacheStats {
|
|
154
177
|
size: number;
|
|
155
178
|
maxSize: number;
|
|
156
179
|
hits: number;
|
|
@@ -563,7 +586,21 @@ interface MetadataDraftHeader {
|
|
|
563
586
|
updatedAt: string | null;
|
|
564
587
|
updatedBy: string | null;
|
|
565
588
|
}
|
|
566
|
-
|
|
589
|
+
/**
|
|
590
|
+
* Options for {@link MetadataClient.save} — a WRITE OVER HTTP to
|
|
591
|
+
* `/api/v1/meta/:type/:name`.
|
|
592
|
+
*
|
|
593
|
+
* NOT the spec's `MetadataSaveOptions` (`@objectstack/spec/system` and
|
|
594
|
+
* `/kernel`), whose name this interface wore until objectui#3160
|
|
595
|
+
* (objectstack#4115 ledger batch 6). Both spec copies describe writing a
|
|
596
|
+
* metadata item to a FILE — `format: json|yaml|ts`, `path`, `indent`,
|
|
597
|
+
* `prettify`, `sortKeys`, `backup`, `atomic`, `loader`. Not one of those keys
|
|
598
|
+
* exists here, and not one of these exists there: this is the REST client's
|
|
599
|
+
* request envelope — optimistic concurrency (`ifMatch` → `If-Match`), actor
|
|
600
|
+
* attribution, the destructive-change override, the ADR-0033 draft/publish
|
|
601
|
+
* mode, and the owning package. Same words, different layer.
|
|
602
|
+
*/
|
|
603
|
+
interface MetadataClientSaveOptions {
|
|
567
604
|
/**
|
|
568
605
|
* Optimistic concurrency token (the `checksum` returned by the last
|
|
569
606
|
* read). When present, sent as the `If-Match` header so concurrent
|
|
@@ -608,7 +645,7 @@ interface MetadataGetOptions {
|
|
|
608
645
|
*/
|
|
609
646
|
packageId?: string;
|
|
610
647
|
}
|
|
611
|
-
interface MetadataDeleteOptions extends
|
|
648
|
+
interface MetadataDeleteOptions extends MetadataClientSaveOptions {
|
|
612
649
|
/**
|
|
613
650
|
* Target state. `'draft'` discards the pending draft (keeps the
|
|
614
651
|
* published overlay intact). Omit to reset the active overlay back
|
|
@@ -847,7 +884,7 @@ declare class MetadataClient {
|
|
|
847
884
|
* envelopes; we send bare for consistency. Pass `mode: 'draft'` to
|
|
848
885
|
* stage the change without publishing (Studio's "Save" button).
|
|
849
886
|
*/
|
|
850
|
-
save<T = unknown>(type: string, name: string, item: unknown, options?:
|
|
887
|
+
save<T = unknown>(type: string, name: string, item: unknown, options?: MetadataClientSaveOptions): Promise<T>;
|
|
851
888
|
/**
|
|
852
889
|
* Publish a single pending draft BY REFERENCE — promotes the draft row for
|
|
853
890
|
* `(type, name)` into the active overlay and drops the draft. Works for ANY
|
|
@@ -957,7 +994,20 @@ declare class MetadataClient {
|
|
|
957
994
|
* Security module integration for @objectstack/spec v3.0.0
|
|
958
995
|
* Provides advanced security policies: CSP config, audit logging, data masking.
|
|
959
996
|
*/
|
|
960
|
-
|
|
997
|
+
/**
|
|
998
|
+
* Configuration for {@link SecurityManager} — this adapter's browser-side
|
|
999
|
+
* security posture: the CSP header it generates, the in-memory audit log it
|
|
1000
|
+
* keeps, and the field-masking rules it applies before a record is rendered.
|
|
1001
|
+
*
|
|
1002
|
+
* NOT the spec's `SecurityPolicy` (`@objectstack/spec/kernel`), whose name this
|
|
1003
|
+
* interface wore until objectui#3160 (objectstack#4115 ledger batch 6). That one
|
|
1004
|
+
* is the PACKAGE SUPPLY-CHAIN policy — `{ id, name, autoScan, thresholds,
|
|
1005
|
+
* allowedLicenses, prohibitedLicenses, codeSigning, sandbox }` — the rules a
|
|
1006
|
+
* marketplace applies when scanning a plugin before install. It shares no key
|
|
1007
|
+
* with this one, so there was nothing to derive; the collision is the word
|
|
1008
|
+
* "security", not the concept.
|
|
1009
|
+
*/
|
|
1010
|
+
interface SecurityManagerPolicy {
|
|
961
1011
|
/** Content Security Policy configuration */
|
|
962
1012
|
csp?: CSPConfig;
|
|
963
1013
|
/** Audit logging configuration */
|
|
@@ -1031,7 +1081,7 @@ interface AuditLogEntry {
|
|
|
1031
1081
|
declare class SecurityManager {
|
|
1032
1082
|
private policy;
|
|
1033
1083
|
private auditLog;
|
|
1034
|
-
constructor(policy?:
|
|
1084
|
+
constructor(policy?: SecurityManagerPolicy);
|
|
1035
1085
|
/**
|
|
1036
1086
|
* Generate a CSP header string from the configuration.
|
|
1037
1087
|
*/
|
|
@@ -1055,11 +1105,11 @@ declare class SecurityManager {
|
|
|
1055
1105
|
/**
|
|
1056
1106
|
* Update the security policy.
|
|
1057
1107
|
*/
|
|
1058
|
-
updatePolicy(policy: Partial<
|
|
1108
|
+
updatePolicy(policy: Partial<SecurityManagerPolicy>): void;
|
|
1059
1109
|
/**
|
|
1060
1110
|
* Get current security policy.
|
|
1061
1111
|
*/
|
|
1062
|
-
getPolicy():
|
|
1112
|
+
getPolicy(): SecurityManagerPolicy;
|
|
1063
1113
|
}
|
|
1064
1114
|
|
|
1065
1115
|
/**
|
|
@@ -1379,7 +1429,7 @@ declare function isConcurrentUpdateError(error: unknown): error is ConcurrentUpd
|
|
|
1379
1429
|
*
|
|
1380
1430
|
* Two shapes are recognised:
|
|
1381
1431
|
* - `409` + `CONCURRENT_UPDATE` → {@link ConcurrentUpdateError};
|
|
1382
|
-
* - `400` + `VALIDATION_FAILED` → {@link
|
|
1432
|
+
* - `400` + `VALIDATION_FAILED` → {@link DataApiValidationError}, carrying the
|
|
1383
1433
|
* server's per-field entries so a form can mark the offending inputs
|
|
1384
1434
|
* instead of showing one undirected toast.
|
|
1385
1435
|
*/
|
|
@@ -1414,19 +1464,7 @@ type ConnectionStateListener = (event: ConnectionStateEvent) => void;
|
|
|
1414
1464
|
* Event listener type for batch operation progress
|
|
1415
1465
|
*/
|
|
1416
1466
|
type BatchProgressListener = (event: BatchProgressEvent) => void;
|
|
1417
|
-
|
|
1418
|
-
* One server-reported write-strip: caller-supplied fields the backend LEGALLY
|
|
1419
|
-
* removed from a write before persisting (a non-system caller cannot seed a
|
|
1420
|
-
* `readonly` field, a `readonlyWhen` predicate locked it, etc.). Mirrors the
|
|
1421
|
-
* framework `DroppedFieldsEvent` (spec `DroppedFieldsEventSchema`) structurally
|
|
1422
|
-
* so we don't pin a client type version — `reason` is kept as a widened string
|
|
1423
|
-
* for forward-compatibility with reasons added server-side.
|
|
1424
|
-
*/
|
|
1425
|
-
interface DroppedFieldsEvent {
|
|
1426
|
-
object: string;
|
|
1427
|
-
fields: string[];
|
|
1428
|
-
reason: string;
|
|
1429
|
-
}
|
|
1467
|
+
|
|
1430
1468
|
/**
|
|
1431
1469
|
* Emitted after a create/update whose response carried `droppedFields`
|
|
1432
1470
|
* (framework #3431/#3455). The write SUCCEEDED — this is a warning that some
|
|
@@ -2085,7 +2123,7 @@ declare class ObjectStackAdapter<T = unknown> implements DataSource<T> {
|
|
|
2085
2123
|
/**
|
|
2086
2124
|
* Get cache statistics for monitoring performance.
|
|
2087
2125
|
*/
|
|
2088
|
-
getCacheStats():
|
|
2126
|
+
getCacheStats(): MetadataCacheStats;
|
|
2089
2127
|
/**
|
|
2090
2128
|
* Invalidate metadata cache entries.
|
|
2091
2129
|
*
|
|
@@ -2173,4 +2211,4 @@ declare function createObjectStackAdapter<T = unknown>(config: {
|
|
|
2173
2211
|
reconnectDelay?: number;
|
|
2174
2212
|
}): DataSource<T>;
|
|
2175
2213
|
|
|
2176
|
-
export { AnalyticsNotInstalledError, AnalyticsQueryRejectedError, type AuditEventType, type AuditLogConfig, type AuditLogEntry, AuthenticationError, type BatchProgressEvent, type BatchProgressListener, BulkOperationError, type CSPConfig, type
|
|
2214
|
+
export { AnalyticsNotInstalledError, AnalyticsQueryRejectedError, type AuditEventType, type AuditLogConfig, type AuditLogEntry, AuthenticationError, type BatchProgressEvent, type BatchProgressListener, BulkOperationError, type CSPConfig, type CloudDeploymentConfig, type CloudHostingConfig, type CloudMarketplaceEntry, CloudOperations, ConcurrentUpdateError, ConnectionError, type ConnectionState, type ConnectionStateEvent, type ConnectionStateListener, type ContractValidationError, type ContractValidationResult, DataApiValidationError, type DataMaskingConfig, type DataMaskingRule, type EmailIntegrationConfig, FILTER_OPERATOR_ALIASES, type IntegrationConfig, IntegrationManager, type IntegrationProvider, type IntegrationTrigger, MalformedFilterError, type MetadataAuditEntry, type MetadataAuditResponse, type MetadataCacheStats, MetadataClient, type MetadataClientConfig, type MetadataClientSaveOptions, type MetadataDeleteOptions, type MetadataDiagnostics, type MetadataDiagnosticsEntry, type MetadataDiagnosticsOptions, type MetadataDiagnosticsSummary, type MetadataDraftHeader, type MetadataError, type MetadataGetOptions, type MetadataHistoryOptions, type MetadataLayered, type MetadataListOptions, MetadataNotFoundError, type MetadataReference, type MetadataValidationIssue, ObjectStackAdapter, ObjectStackError, type ObjectStackUserStateAdapterOptions, type PluginAPIContract, type PluginContract, type PluginExport, SecurityManager, type SecurityManagerPolicy, type SlackIntegrationConfig, type StudioCanvasConfig, type StudioColorPalette, type StudioPropertyEditor, type StudioShadowPreset, type StudioThemeBuilderConfig, type StudioTypographyPreset, type UserDataAdapter, type WebhookIntegrationConfig, type WriteWarningEvent, type WriteWarningListener, calculateAutoLayout, classifyAnalyticsFailure, clearSharedDiscoveryCache, createDefaultCanvasConfig, createErrorFromResponse, createObjectStackAdapter, createObjectStackUserStateAdapter, generateContractManifest, getSharedDiscovery, is404Error, isAnalyticsNotInstalledError, isConcurrentUpdateError, isErrorType, isMalformedFilterError, isObjectStackError, normaliseClientError, readTransactionalBatchCapability, serializeOrderBy, snapToGrid, validatePluginContract };
|