@object-ui/core 17.6.0 → 17.7.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 +6610 -0
- package/README.md +101 -14
- package/dist/actions/ActionEngine.d.ts +4 -2
- package/dist/actions/ActionEngine.js +13 -8
- package/dist/actions/ActionRunner.d.ts +288 -27
- package/dist/actions/ActionRunner.js +443 -63
- package/dist/actions/TransactionManager.d.ts +7 -2
- package/dist/actions/actionKeys.d.ts +9 -7
- package/dist/actions/actionKeys.js +57 -14
- package/dist/actions/actionResponse.js +2 -2
- package/dist/actions/index.d.ts +1 -0
- package/dist/actions/index.js +1 -0
- package/dist/actions/recordDelete.d.ts +118 -0
- package/dist/actions/recordDelete.js +112 -0
- package/dist/adapters/ValueDataSource.d.ts +6 -1
- package/dist/adapters/ValueDataSource.js +1090 -98
- package/dist/adapters/batchTransaction.d.ts +1 -1
- package/dist/adapters/batchTransaction.js +1 -1
- package/dist/builder/schema-builder.d.ts +38 -81
- package/dist/builder/schema-builder.js +33 -152
- package/dist/data-scope/ViewDataProvider.d.ts +1 -1
- package/dist/data-scope/ViewDataProvider.js +21 -1
- package/dist/data-scope/element-data-source.d.ts +261 -7
- package/dist/data-scope/element-data-source.js +342 -52
- package/dist/data-scope/index.d.ts +9 -4
- package/dist/data-scope/index.js +9 -4
- package/dist/evaluator/ExpressionEvaluator.d.ts +86 -0
- package/dist/evaluator/ExpressionEvaluator.js +184 -12
- package/dist/evaluator/FormulaFunctions.d.ts +0 -10
- package/dist/evaluator/FormulaFunctions.js +166 -27
- package/dist/evaluator/declaredPredicate.d.ts +93 -9
- package/dist/evaluator/declaredPredicate.js +117 -16
- package/dist/evaluator/fieldRules.d.ts +88 -4
- package/dist/evaluator/fieldRules.js +203 -31
- package/dist/evaluator/index.d.ts +1 -0
- package/dist/evaluator/index.js +1 -0
- package/dist/evaluator/listConditional.d.ts +103 -34
- package/dist/evaluator/listConditional.js +127 -50
- package/dist/evaluator/optionRules.d.ts +77 -6
- package/dist/evaluator/optionRules.js +87 -10
- package/dist/evaluator/predicateInput.d.ts +15 -4
- package/dist/evaluator/predicateInput.js +35 -2
- package/dist/evaluator/rowPredicateCanon.d.ts +7 -18
- package/dist/evaluator/rowPredicateCanon.js +65 -115
- package/dist/evaluator/subjectPermissions.d.ts +76 -0
- package/dist/evaluator/subjectPermissions.js +44 -0
- package/dist/evaluator/unevaluablePredicate.d.ts +57 -0
- package/dist/evaluator/unevaluablePredicate.js +70 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +80 -2
- package/dist/protocols/index.d.ts +0 -1
- package/dist/protocols/index.js +9 -1
- package/dist/registry/Registry.d.ts +358 -31
- package/dist/registry/Registry.js +263 -11
- package/dist/registry/WidgetRegistry.js +7 -3
- package/dist/registry/html-tier-intrinsics.d.ts +90 -0
- package/dist/registry/html-tier-intrinsics.js +110 -0
- package/dist/registry/public-blocks.d.ts +8 -0
- package/dist/registry/public-blocks.js +57 -0
- package/dist/theme/ThemeEngine.js +1 -1
- package/dist/utils/chart-category-key.d.ts +82 -0
- package/dist/utils/chart-category-key.js +84 -0
- package/dist/utils/chart-measure-key.d.ts +64 -0
- package/dist/utils/chart-measure-key.js +65 -0
- package/dist/utils/chart-presentation.d.ts +95 -112
- package/dist/utils/chart-presentation.js +169 -104
- package/dist/utils/chart-series.d.ts +131 -5
- package/dist/utils/chart-series.js +98 -16
- package/dist/utils/column-sortability.d.ts +157 -0
- package/dist/utils/column-sortability.js +217 -0
- package/dist/utils/dashboard-filters.d.ts +38 -6
- package/dist/utils/dashboard-filters.js +107 -61
- package/dist/utils/dataset-format.d.ts +211 -30
- package/dist/utils/dataset-format.js +389 -46
- package/dist/utils/date-display.d.ts +354 -0
- package/dist/utils/date-display.js +463 -0
- package/dist/utils/date-macros.js +7 -0
- package/dist/utils/drill-down.d.ts +78 -0
- package/dist/utils/drill-down.js +93 -0
- package/dist/utils/emptiness.d.ts +96 -0
- package/dist/utils/emptiness.js +101 -0
- package/dist/utils/expand-fields.d.ts +76 -6
- package/dist/utils/expand-fields.js +76 -6
- package/dist/utils/extract-records.d.ts +65 -5
- package/dist/utils/extract-records.js +65 -8
- package/dist/utils/filter-converter.d.ts +152 -4
- package/dist/utils/filter-converter.js +1092 -22
- package/dist/utils/filter-tokens.d.ts +49 -5
- package/dist/utils/filter-tokens.js +105 -33
- package/dist/utils/grouping-fields.d.ts +61 -0
- package/dist/utils/grouping-fields.js +84 -0
- package/dist/utils/managedBy.d.ts +3 -3
- package/dist/utils/managedBy.js +5 -5
- package/dist/utils/native-date-value.d.ts +48 -0
- package/dist/utils/native-date-value.js +167 -0
- package/dist/utils/non-grid-row-ceiling.d.ts +143 -0
- package/dist/utils/non-grid-row-ceiling.js +141 -0
- package/dist/utils/normalize-list-view.d.ts +121 -1
- package/dist/utils/normalize-list-view.js +186 -6
- package/dist/utils/number-display.d.ts +63 -14
- package/dist/utils/number-display.js +34 -12
- package/dist/utils/object-aggregate-query.d.ts +113 -0
- package/dist/utils/object-aggregate-query.js +50 -0
- package/dist/utils/parent-scope.d.ts +123 -0
- package/dist/utils/parent-scope.js +140 -0
- package/dist/utils/predicate-fields.d.ts +45 -12
- package/dist/utils/predicate-fields.js +137 -12
- package/dist/utils/record-color.d.ts +75 -0
- package/dist/utils/record-color.js +65 -0
- package/dist/utils/record-source.d.ts +298 -0
- package/dist/utils/record-source.js +331 -0
- package/dist/utils/record-title.d.ts +148 -8
- package/dist/utils/record-title.js +220 -26
- package/dist/utils/reference-keys.d.ts +21 -22
- package/dist/utils/reference-keys.js +532 -29
- package/dist/utils/sort-query.d.ts +115 -15
- package/dist/utils/sort-query.js +240 -13
- package/dist/utils/text-comparand.d.ts +92 -0
- package/dist/utils/text-comparand.js +99 -0
- package/dist/utils/without-denied-fields.d.ts +49 -0
- package/dist/utils/without-denied-fields.js +52 -0
- package/dist/validation/index.d.ts +8 -4
- package/dist/validation/index.js +8 -4
- package/dist/validation/schema-validator.d.ts +1 -1
- package/dist/validation/schema-validator.js +200 -82
- package/dist/validation/validators/object-validation-engine.d.ts +1 -1
- package/dist/validation/validators/object-validation-engine.js +14 -3
- package/package.json +8 -9
- package/dist/data-scope/DataScopeManager.d.ts +0 -127
- package/dist/data-scope/DataScopeManager.js +0 -229
- package/dist/protocols/ResponsiveProtocol.d.ts +0 -73
- package/dist/protocols/ResponsiveProtocol.js +0 -158
- package/dist/validation/validation-engine.d.ts +0 -88
- package/dist/validation/validation-engine.js +0 -434
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ Core logic, types, and validation for Object UI. Zero React dependencies.
|
|
|
7
7
|
- 🎯 **Type Definitions** - Re-exported runtime types; the component schema
|
|
8
8
|
vocabulary itself is `@object-ui/types`
|
|
9
9
|
- 🔍 **Component Registry** - Framework-agnostic component registration system
|
|
10
|
-
-
|
|
10
|
+
- 🧮 **Expressions** - `${...}` expression evaluation against a context
|
|
11
11
|
- ✅ **Validation** - Zod-based schema validation
|
|
12
12
|
- 🚀 **Zero React** - Can run in Node.js or any JavaScript environment
|
|
13
13
|
|
|
@@ -36,8 +36,9 @@ import type {
|
|
|
36
36
|
|
|
37
37
|
const mySchema: PageNodeSchema = {
|
|
38
38
|
type: 'page',
|
|
39
|
+
pageType: 'app',
|
|
39
40
|
title: 'My Page',
|
|
40
|
-
|
|
41
|
+
children: []
|
|
41
42
|
}
|
|
42
43
|
```
|
|
43
44
|
|
|
@@ -45,28 +46,28 @@ const mySchema: PageNodeSchema = {
|
|
|
45
46
|
|
|
46
47
|
```typescript
|
|
47
48
|
import { ComponentRegistry } from '@object-ui/core'
|
|
49
|
+
import type { ComponentRenderer } from '@object-ui/core'
|
|
48
50
|
|
|
49
|
-
|
|
50
|
-
const
|
|
51
|
+
// Your component, in whatever renderer shape the host framework uses.
|
|
52
|
+
declare const buttonRenderer: ComponentRenderer
|
|
53
|
+
|
|
54
|
+
ComponentRegistry.register('button', buttonRenderer)
|
|
55
|
+
const renderer = ComponentRegistry.get('button')
|
|
51
56
|
```
|
|
52
57
|
|
|
53
58
|
`ComponentRegistry` is a process-level singleton exported by `@object-ui/core`;
|
|
54
59
|
`SchemaRenderer` resolves every `type` against it, so a component registered
|
|
55
|
-
here is renderable from schema anywhere in the app.
|
|
60
|
+
here is renderable from schema anywhere in the app. `register()`'s second
|
|
61
|
+
argument is the component itself; registration metadata is its optional third
|
|
62
|
+
argument, and `getMeta()` — not `get()` — reads that metadata back.
|
|
56
63
|
|
|
57
|
-
###
|
|
64
|
+
### Expressions
|
|
58
65
|
|
|
59
|
-
`
|
|
60
|
-
`evaluateExpression` evaluates a `${...}` expression against a context. They
|
|
61
|
-
are separate exports: a scope holds data, it does not evaluate.
|
|
66
|
+
`evaluateExpression` evaluates a `${...}` expression against a context.
|
|
62
67
|
|
|
63
68
|
```typescript
|
|
64
|
-
import {
|
|
65
|
-
|
|
66
|
-
const manager = new DataScopeManager()
|
|
67
|
-
manager.registerScope('user', { data: { name: 'John', role: 'admin' } })
|
|
69
|
+
import { evaluateExpression } from '@object-ui/core'
|
|
68
70
|
|
|
69
|
-
const userName = manager.getScope('user')?.data.name // 'John'
|
|
70
71
|
const isAdmin = evaluateExpression('${user.role === "admin"}', {
|
|
71
72
|
user: { name: 'John', role: 'admin' },
|
|
72
73
|
}) // true
|
|
@@ -82,6 +83,14 @@ object scope, which are injected:
|
|
|
82
83
|
|
|
83
84
|
```typescript
|
|
84
85
|
import { createServerActionHandler } from '@object-ui/core'
|
|
86
|
+
import type { ActionRunner, ServerActionFetch } from '@object-ui/core'
|
|
87
|
+
|
|
88
|
+
// Injected by the host. Each stand-in is typed from the shipped surface, so
|
|
89
|
+
// this example is checked against the factory's own config rather than a copy.
|
|
90
|
+
declare const myAuthenticatedFetch: ServerActionFetch
|
|
91
|
+
declare const currentObject: string | undefined
|
|
92
|
+
declare const refetchData: () => void
|
|
93
|
+
declare const runner: ActionRunner
|
|
85
94
|
|
|
86
95
|
const script = createServerActionHandler({
|
|
87
96
|
fetch: myAuthenticatedFetch, // your auth wrapper (Bearer/cookies/...)
|
|
@@ -116,6 +125,7 @@ export const userListView = defineSystemView({
|
|
|
116
125
|
columns: [{ name: 'email' }],
|
|
117
126
|
})
|
|
118
127
|
|
|
128
|
+
// @ts-expect-error readonly by defineSystemView — refused at compile time, not just at run time
|
|
119
129
|
userListView.columns.push({ name: 'name' }) // ❌ TypeError (strict mode)
|
|
120
130
|
isSystemView(userListView) // ✅ true
|
|
121
131
|
|
|
@@ -136,6 +146,83 @@ isSystemView(draft) // false — clone is no longer Sys
|
|
|
136
146
|
`Date`, `RegExp`, `Map`, `Set`, and class instances passed via `props` are
|
|
137
147
|
intentionally **not** frozen so infrastructure objects keep working.
|
|
138
148
|
|
|
149
|
+
### Field-level read gate (`withoutDeniedFields`)
|
|
150
|
+
|
|
151
|
+
Field-level security gates what the renderer DRAWS (objectui#7215 /
|
|
152
|
+
objectui#7230): a value the loaded permission policy denies is not shown, even
|
|
153
|
+
when a backend serves it. `withoutDeniedFields` is that rule for one record —
|
|
154
|
+
call it on a row before building a display value from the whole row (a record
|
|
155
|
+
title, a lookup label, a search-hit label), so a denied field reads exactly as
|
|
156
|
+
an absent one.
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
import { withoutDeniedFields } from '@object-ui/core'
|
|
160
|
+
|
|
161
|
+
// `policy` is structural: `usePermissions()` from `@object-ui/permissions`
|
|
162
|
+
// satisfies it, as does any `{ isLoaded, checkField }` object.
|
|
163
|
+
const policy = {
|
|
164
|
+
isLoaded: true,
|
|
165
|
+
checkField: (object: string, field: string) => !(object === 'account' && field === 'salary'),
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
withoutDeniedFields({ id: 'a1', name: 'Acme', salary: 100 }, policy, 'account')
|
|
169
|
+
// { id: 'a1', name: 'Acme' }
|
|
170
|
+
|
|
171
|
+
// A declared id field is kept without asking the policy, like `id` and `_id`:
|
|
172
|
+
withoutDeniedFields({ account_code: 'A-1', salary: 100 }, policy, 'account', ['account_code'])
|
|
173
|
+
// { account_code: 'A-1' }
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
- `id`, `_id` and every key in the optional fourth argument are never judged.
|
|
177
|
+
- Before the policy loads, with no policy, with no object name, or for a value
|
|
178
|
+
that is not an object, the record comes back as it is.
|
|
179
|
+
- When nothing is withheld the SAME object comes back, so callers can tell the
|
|
180
|
+
two cases apart by identity.
|
|
181
|
+
|
|
182
|
+
### Undo snapshot for an update (`captureUpdateUndoData`)
|
|
183
|
+
|
|
184
|
+
An `undoable` update restores exactly the fields it wrote, from their values
|
|
185
|
+
before the write. `captureUpdateUndoData` is the one rule for reading those
|
|
186
|
+
values off a record. `ActionRunner` uses it, and so should any surface that
|
|
187
|
+
builds its own `update` Undo operation.
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
import { captureUpdateUndoData, type FieldContainerLike } from '@object-ui/core'
|
|
191
|
+
|
|
192
|
+
// The written object's field definitions, as its schema serves them
|
|
193
|
+
// (`objectSchema.fields`). Below, `status` is a text field, `account` a
|
|
194
|
+
// lookup and `config` a json field.
|
|
195
|
+
declare const fields: FieldContainerLike
|
|
196
|
+
|
|
197
|
+
captureUpdateUndoData(['status'], { id: 't1', status: 'open' }, fields) // { status: 'open' }
|
|
198
|
+
captureUpdateUndoData(['status'], { id: 't1', status: null }, fields) // { status: null }
|
|
199
|
+
captureUpdateUndoData(['status'], { id: 't1' }, fields) // undefined
|
|
200
|
+
|
|
201
|
+
// A relation read with `$expand` is captured as its stored id.
|
|
202
|
+
captureUpdateUndoData(['account'], { id: 't1', account: { id: 'a1', name: 'Acme' } }, fields)
|
|
203
|
+
// { account: 'a1' }
|
|
204
|
+
|
|
205
|
+
// Any other field is captured as the record carries it, whatever its shape.
|
|
206
|
+
captureUpdateUndoData(['config'], { id: 't1', config: { id: 'c1', mode: 'strict' } }, fields)
|
|
207
|
+
// { config: { id: 'c1', mode: 'strict' } }
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
- A field counts as carried when it is an own key whose value is not
|
|
211
|
+
`undefined`. A carried `null` is a real empty value and is captured.
|
|
212
|
+
- When any written field is not carried, the answer is `undefined` and the
|
|
213
|
+
caller offers no Undo at all. A record projected by `$select`, or one the
|
|
214
|
+
server stripped of fields the reader may not read, can lack a written field
|
|
215
|
+
while the server holds a real value for it; a partial or `null` snapshot
|
|
216
|
+
would overwrite that value on Undo.
|
|
217
|
+
- A field the object declares as a relation (`lookup`, `master_detail`,
|
|
218
|
+
`user`, `tree`) is captured as the id it stores, and a `multiple` one as the
|
|
219
|
+
array of ids. `$expand` puts the related record where the id was, and
|
|
220
|
+
writing that record back into the reference is what Undo must never do.
|
|
221
|
+
Which fields are relations is read from `fields`, never from the value's
|
|
222
|
+
shape: a `json` field may hold an object with an `id`.
|
|
223
|
+
- `fields` is required. Pass `undefined` only when the caller has no field
|
|
224
|
+
definitions for the object; nothing is then treated as a relation.
|
|
225
|
+
|
|
139
226
|
## Philosophy
|
|
140
227
|
|
|
141
228
|
This package is designed to be **framework-agnostic**. It contains:
|
|
@@ -118,8 +118,10 @@ export declare class ActionEngine {
|
|
|
118
118
|
* DECLARED, which is asked through core's one definition
|
|
119
119
|
* `hasDeclaredPredicate` (objectui#3850's ruling, adopted here by
|
|
120
120
|
* objectui#3957). No gate — absent, `''`, blank predicate text in either
|
|
121
|
-
* spelling, an envelope
|
|
122
|
-
*
|
|
121
|
+
* spelling, or an envelope whose `source` is `''` — means visible. A
|
|
122
|
+
* value that is present but has no evaluable `source` (`0`, `{}`, an
|
|
123
|
+
* array, an `ast`-only envelope) is a declared gate that faults
|
|
124
|
+
* (objectui#11358), so it hides like any other. Evaluator errors hide the
|
|
123
125
|
* action (fail-closed) rather than throwing — this matches the contract
|
|
124
126
|
* used by every individual action renderer (`action-button`,
|
|
125
127
|
* `action-menu`, `action-bar`, …) so the same action behaves identically
|
|
@@ -171,8 +171,10 @@ export class ActionEngine {
|
|
|
171
171
|
* DECLARED, which is asked through core's one definition
|
|
172
172
|
* `hasDeclaredPredicate` (objectui#3850's ruling, adopted here by
|
|
173
173
|
* objectui#3957). No gate — absent, `''`, blank predicate text in either
|
|
174
|
-
* spelling, an envelope
|
|
175
|
-
*
|
|
174
|
+
* spelling, or an envelope whose `source` is `''` — means visible. A
|
|
175
|
+
* value that is present but has no evaluable `source` (`0`, `{}`, an
|
|
176
|
+
* array, an `ast`-only envelope) is a declared gate that faults
|
|
177
|
+
* (objectui#11358), so it hides like any other. Evaluator errors hide the
|
|
176
178
|
* action (fail-closed) rather than throwing — this matches the contract
|
|
177
179
|
* used by every individual action renderer (`action-button`,
|
|
178
180
|
* `action-menu`, `action-bar`, …) so the same action behaves identically
|
|
@@ -226,10 +228,12 @@ export class ActionEngine {
|
|
|
226
228
|
// hid it here because the normalizer wraps a blank string into `'${ }'`
|
|
227
229
|
// whose verdict is falsy. That is the shape objectui#3314's invariant
|
|
228
230
|
// forbids, and it is fixed by deleting a range, not by adding one
|
|
229
|
-
// (objectui#3957). A
|
|
230
|
-
//
|
|
231
|
-
//
|
|
232
|
-
//
|
|
231
|
+
// (objectui#3957). A predicate that says nothing (blank) must not be
|
|
232
|
+
// the reason an action is hidden from everyone. A value that is not a
|
|
233
|
+
// predicate at all is no longer "nothing" (objectui#11358): it is a
|
|
234
|
+
// declared gate the client cannot evaluate, the normalizer keeps it
|
|
235
|
+
// as such, and the `throwOnError` evaluation below hides it and
|
|
236
|
+
// reports it, as every action renderer's `visible` leg does.
|
|
233
237
|
//
|
|
234
238
|
// Booleans need no branch of their own: they are DECLARED (`visible:
|
|
235
239
|
// false` is a verdict, objectui#3812) and `evaluateCondition`
|
|
@@ -241,7 +245,8 @@ export class ActionEngine {
|
|
|
241
245
|
// predicate to the legacy JS engine; `toPredicateInput` keeps the
|
|
242
246
|
// envelope so `evaluateCondition` can route it to `@objectstack/formula`
|
|
243
247
|
// (the engine the server enforces with), exactly as the renderers do.
|
|
244
|
-
// Declared ⇒ the normalizer left something
|
|
248
|
+
// Declared ⇒ the normalizer left something (possibly the declared-but-
|
|
249
|
+
// unevaluable `cel` envelope, which throws below), so there is no
|
|
245
250
|
// `undefined` case left to handle here.
|
|
246
251
|
const expr = toPredicateInput(raw);
|
|
247
252
|
try {
|
|
@@ -261,7 +266,7 @@ export class ActionEngine {
|
|
|
261
266
|
// BARE field reference (`done` instead of `record.done`), which is
|
|
262
267
|
// an undeclared variable in the `{ record, recordId, objectName,
|
|
263
268
|
// user }` eval scope. Silently hiding it makes that bug invisible
|
|
264
|
-
// (the #2183 hunt). Warn once per predicate so it is diagnosable
|
|
269
|
+
// (the objectstack-ai/objectstack#2183 hunt). Warn once per predicate so it is diagnosable
|
|
265
270
|
// without spamming re-renders.
|
|
266
271
|
warnHiddenPredicate(ra.action.name, raw, err);
|
|
267
272
|
return false;
|