@object-ui/core 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 +650 -0
- package/README.md +127 -0
- package/dist/actions/ActionRunner.d.ts +192 -49
- package/dist/actions/ActionRunner.js +389 -102
- package/dist/actions/UndoManager.d.ts +8 -13
- package/dist/actions/actionKeys.d.ts +1 -1
- package/dist/actions/actionKeys.js +10 -0
- package/dist/actions/index.d.ts +1 -0
- package/dist/actions/index.js +1 -0
- package/dist/actions/navigationTarget.d.ts +41 -0
- package/dist/actions/navigationTarget.js +20 -0
- package/dist/actions/recordDelete.d.ts +59 -23
- package/dist/actions/recordDelete.js +40 -42
- package/dist/adapters/ApiDataSource.d.ts +8 -4
- package/dist/adapters/ApiDataSource.js +19 -7
- package/dist/adapters/resolveDataSource.d.ts +9 -6
- package/dist/adapters/resolveDataSource.js +9 -12
- package/dist/data-scope/ViewDataProvider.d.ts +7 -24
- package/dist/data-scope/ViewDataProvider.js +6 -53
- package/dist/evaluator/deadlineSettled.d.ts +40 -0
- package/dist/evaluator/deadlineSettled.js +45 -0
- package/dist/evaluator/fieldRules.js +6 -1
- package/dist/evaluator/index.d.ts +1 -0
- package/dist/evaluator/index.js +1 -0
- package/dist/evaluator/optionRules.d.ts +1 -1
- package/dist/evaluator/optionRules.js +1 -1
- package/dist/evaluator/subjectPermissions.d.ts +30 -1
- package/dist/evaluator/subjectPermissions.js +42 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -0
- package/dist/registry/Registry.d.ts +34 -13
- package/dist/registry/Registry.js +5 -22
- package/dist/utils/affordanceGrants.d.ts +371 -0
- package/dist/utils/affordanceGrants.js +252 -0
- package/dist/utils/chart-series.d.ts +34 -0
- package/dist/utils/chart-series.js +25 -2
- package/dist/utils/date-display.d.ts +42 -50
- package/dist/utils/date-display.js +192 -11
- package/dist/utils/expand-fields.d.ts +6 -4
- package/dist/utils/expand-fields.js +6 -4
- package/dist/utils/filter-converter.d.ts +6 -2
- package/dist/utils/filter-converter.js +6 -2
- package/dist/utils/normalize-list-view.d.ts +16 -12
- package/dist/utils/normalize-list-view.js +66 -67
- package/dist/utils/predicate-fields.js +6 -2
- package/dist/utils/record-source.d.ts +1 -1
- package/dist/utils/record-source.js +1 -1
- package/dist/utils/reference-keys.js +1 -1
- package/dist/validation/schema-validator.js +29 -5
- package/dist/validation/server-owned-value.d.ts +5 -1
- package/dist/validation/server-owned-value.js +5 -1
- package/package.json +4 -4
|
@@ -12,19 +12,14 @@
|
|
|
12
12
|
* pattern for use from ActionRunner, React hooks, and standalone contexts.
|
|
13
13
|
* Maintains bounded history stacks with subscriber notifications.
|
|
14
14
|
*/
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
/** Data needed to undo: for create=recordId, for update=previousData, for delete=fullRecord */
|
|
24
|
-
undoData: Record<string, unknown>;
|
|
25
|
-
/** Data needed to redo: for create=newData, for update=newData, for delete=recordId */
|
|
26
|
-
redoData: Record<string, unknown>;
|
|
27
|
-
}
|
|
15
|
+
import type { UndoableOperation } from '@object-ui/types';
|
|
16
|
+
/**
|
|
17
|
+
* A single undoable CRUD operation — declared once, in `@object-ui/types`
|
|
18
|
+
* (objectui#6349, batch 4), beside the `ActionResult` whose `undo` member it
|
|
19
|
+
* types, and re-exported here so an import from `@object-ui/core` keeps
|
|
20
|
+
* resolving to the same type.
|
|
21
|
+
*/
|
|
22
|
+
export type { UndoableOperation } from '@object-ui/types';
|
|
28
23
|
export interface UndoManagerOptions {
|
|
29
24
|
/** Maximum number of operations to retain in history. @default 50 */
|
|
30
25
|
maxHistory?: number;
|
|
@@ -128,7 +128,7 @@ export declare const ACTION_DEF_KEYS: readonly ["type", "actionType", "name", "l
|
|
|
128
128
|
* test and wrong in shipped code — `@object-ui/types` made the same call for
|
|
129
129
|
* `ActionComponent`. The pin test does the walk and fails the day this drifts.
|
|
130
130
|
*/
|
|
131
|
-
export declare const SPEC_ACTION_KEYS: readonly ["_lock", "_lockDocsUrl", "_lockReason", "_lockSource", "_packageId", "_packageVersion", "_provenance", "ai", "aria", "body", "bodyExtra", "bodyShape", "bulkEnabled", "component", "confirmText", "description", "disabled", "errorMessage", "execute", "execution", "icon", "label", "locations", "method", "mode", "name", "newTabUrl", "objectName", "onSuccess", "openIn", "opensInNewTab", "operation", "order", "outcomeMessages", "params", "patch", "recordIdField", "recordIdParam", "refreshAfter", "requiredPermissions", "requiresFeature", "resultDialog", "shortcut", "successMessage", "target", "type", "undoable", "variant", "visible"];
|
|
131
|
+
export declare const SPEC_ACTION_KEYS: readonly ["_lock", "_lockDocsUrl", "_lockReason", "_lockSource", "_packageId", "_packageVersion", "_provenance", "ai", "aria", "body", "bodyExtra", "bodyShape", "bulkEnabled", "component", "confirmText", "description", "disabled", "errorMessage", "execute", "execution", "icon", "label", "locations", "method", "mode", "name", "newTabUrl", "objectName", "onSuccess", "openIn", "opensInNewTab", "operation", "order", "outcomeMessages", "params", "patch", "recordIdField", "recordIdParam", "refreshAfter", "requiredPermissions", "requiresFeature", "requiresMembershipReach", "resultDialog", "shortcut", "successMessage", "target", "type", "undoable", "variant", "visible"];
|
|
132
132
|
/**
|
|
133
133
|
* The `navigation` alias's own spelling of a target, read off the action itself
|
|
134
134
|
* when no nested `navigate` object is present (`ActionRunner.executeNavigation`).
|
|
@@ -296,6 +296,16 @@ export const SPEC_ACTION_KEYS = [
|
|
|
296
296
|
'refreshAfter',
|
|
297
297
|
'requiredPermissions',
|
|
298
298
|
'requiresFeature',
|
|
299
|
+
// Added to `ActionSchema` in @objectstack/spec 17.7.0 (objectui#11717): the
|
|
300
|
+
// organization endpoint (a `MEMBERSHIP_REACH` row) whose membership-grade gate
|
|
301
|
+
// the action follows, which the spec's parse LOWERS into `visible` over
|
|
302
|
+
// `current_user.positions` (the same transform that lowers `requiresFeature`
|
|
303
|
+
// above). Listed for the reason `description` is — this array restates the
|
|
304
|
+
// spec's declared keys, so an action carrying it is not reported as having an
|
|
305
|
+
// unknown key. `ActionDef` does NOT declare it and the runner does not read
|
|
306
|
+
// it; whether it should, beside `requiresFeature`, is not this inventory's
|
|
307
|
+
// question.
|
|
308
|
+
'requiresMembershipReach',
|
|
299
309
|
'resultDialog',
|
|
300
310
|
'shortcut',
|
|
301
311
|
'successMessage',
|
package/dist/actions/index.d.ts
CHANGED
package/dist/actions/index.js
CHANGED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* What a click may navigate to, and how.
|
|
10
|
+
*
|
|
11
|
+
* This is the one answer, shared by every caller that turns an authored or
|
|
12
|
+
* served target into a user-click navigation (objectui#12191, ruling A+):
|
|
13
|
+
*
|
|
14
|
+
* - the action runner: a `type: 'url'` action, its `navigation` alias (which is
|
|
15
|
+
* how an `element:button` CTA reaches it) and `onSuccess.navigate`;
|
|
16
|
+
* - app-shell's entitlement CTA, `resolveCtaHref`.
|
|
17
|
+
*
|
|
18
|
+
* ⛔ No caller keeps a scheme list of its own. A second list is how the runner
|
|
19
|
+
* came to refuse the `mailto:` target the CTA already admitted, so a mail button
|
|
20
|
+
* the spec accepts published cleanly and then failed at click.
|
|
21
|
+
*
|
|
22
|
+
* The admitted set is closed:
|
|
23
|
+
*
|
|
24
|
+
* - `'web'`: `http://` or `https://`. It leaves the app. The caller's `openIn`
|
|
25
|
+
* decides the tab.
|
|
26
|
+
* - `'mail'`: `mailto:`. It leaves the app IN PLACE: never a new tab, and
|
|
27
|
+
* `openIn` is not consulted. The mail client opens and the page stays.
|
|
28
|
+
* - `'path'`: an in-app relative path, `/…` or `./…`.
|
|
29
|
+
*
|
|
30
|
+
* Everything else answers `null` and is refused: `javascript:`, `data:`, `tel:`
|
|
31
|
+
* (not admitted: no measured producer) and every other scheme, and a
|
|
32
|
+
* protocol-relative `//host`. Two other spellings of `//host` are refused with
|
|
33
|
+
* it, because the browser's URL parser reads them as `//host`: a backslash in
|
|
34
|
+
* second place (`/` then a backslash, then `host`), and any target that holds an
|
|
35
|
+
* ASCII tab, line feed or carriage return, since the parser deletes those three
|
|
36
|
+
* before it reads anything.
|
|
37
|
+
*
|
|
38
|
+
* Scheme names compare case-insensitively, as the URL parser compares them.
|
|
39
|
+
*/
|
|
40
|
+
export type NavigationTargetKind = 'web' | 'mail' | 'path';
|
|
41
|
+
export declare function classifyNavigationTarget(url: unknown): NavigationTargetKind | null;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
export function classifyNavigationTarget(url) {
|
|
9
|
+
if (typeof url !== 'string' || /[\t\n\r]/.test(url))
|
|
10
|
+
return null;
|
|
11
|
+
if (/^https?:\/\//i.test(url))
|
|
12
|
+
return 'web';
|
|
13
|
+
if (/^mailto:/i.test(url))
|
|
14
|
+
return 'mail';
|
|
15
|
+
if (url.startsWith('./'))
|
|
16
|
+
return 'path';
|
|
17
|
+
if (url.startsWith('/') && url[1] !== '/' && url[1] !== '\\')
|
|
18
|
+
return 'path';
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
@@ -17,8 +17,9 @@
|
|
|
17
17
|
* `object-view` renderer (`plugin-view`) had no delete at all — its grid
|
|
18
18
|
* handlers only refreshed — and the first repair COPIED those steps into a
|
|
19
19
|
* second flow. The copy already diverged on consent: it asked every row the
|
|
20
|
-
* plain "delete this record?" question, while the console
|
|
21
|
-
* permission set the
|
|
20
|
+
* plain "delete this record?" question, while the console asked a package-owned
|
|
21
|
+
* permission set the reset question (that question went with the catalog
|
|
22
|
+
* objects, ADR-0131). One operation with two
|
|
22
23
|
* implementations drifts on exactly the part that matters, so the rule is: the
|
|
23
24
|
* side with the governance keeps it, the other side re-binds to it, and there
|
|
24
25
|
* is one copy. This module is that copy, and both hosts call it.
|
|
@@ -26,20 +27,20 @@
|
|
|
26
27
|
* `@object-ui/core` is the lowest package both hosts already depend on. The
|
|
27
28
|
* host-specific pieces are INJECTED — the translator, the toast sink, the data
|
|
28
29
|
* source and the refresh — so this package gains no i18n or toast dependency.
|
|
29
|
-
* The host keeps its own confirm UI (`app-shell`
|
|
30
|
-
*
|
|
31
|
-
*
|
|
30
|
+
* The host keeps its own confirm UI (`app-shell` hands the copy to the console's
|
|
31
|
+
* confirm handler; `plugin-view` renders it in its own AlertDialog); the COPY —
|
|
32
|
+
* title, body and confirm-button label — comes from
|
|
33
|
+
* {@link recordDelete}.confirmCopy, whose body for one record is
|
|
34
|
+
* {@link recordDelete}.confirmText.
|
|
32
35
|
*
|
|
33
|
-
* ##
|
|
36
|
+
* ## The confirmation names what it deletes (objectui#11695)
|
|
34
37
|
*
|
|
35
|
-
* "
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* is looked up with `findOne`, best-effort — a failed lookup falls back to the
|
|
42
|
-
* generic copy rather than blocking the delete.
|
|
38
|
+
* A generic "Confirm Action" / "Continue" dialog never said WHICH record was
|
|
39
|
+
* about to go, nor that the click is a delete. The title names the record by
|
|
40
|
+
* the ONE ADR-0079 display-name resolver ({@link getRecordDisplayName}, the
|
|
41
|
+
* resolver the record header and lookups use), and the object label — or, for
|
|
42
|
+
* a batch, the count. The confirm button reads as the deletion it is; every
|
|
43
|
+
* host paints it in the destructive button style.
|
|
43
44
|
*
|
|
44
45
|
* ## The result shape is the action runner's, on purpose
|
|
45
46
|
*
|
|
@@ -66,7 +67,6 @@ interface RecordDeleteDeps {
|
|
|
66
67
|
label: string;
|
|
67
68
|
dataSource: {
|
|
68
69
|
delete(resource: string, id: string): Promise<unknown>;
|
|
69
|
-
findOne(resource: string, id: string): Promise<unknown>;
|
|
70
70
|
};
|
|
71
71
|
/** The host's toast sink (sonner's `toast` in both hosts today). */
|
|
72
72
|
toast: {
|
|
@@ -92,25 +92,61 @@ interface RecordDeleteRequest {
|
|
|
92
92
|
};
|
|
93
93
|
recordId?: unknown;
|
|
94
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* The copy of a delete confirmation — everything a host's confirm dialog shows,
|
|
97
|
+
* from {@link recordDelete}.confirmCopy (objectui#11695).
|
|
98
|
+
*/
|
|
99
|
+
export interface RecordDeleteConfirmCopy {
|
|
100
|
+
/** The dialog title: names the record and its object, or the batch size. */
|
|
101
|
+
title: string;
|
|
102
|
+
/** The dialog body: the delete question, or the batch question. */
|
|
103
|
+
message: string;
|
|
104
|
+
/** The confirm button's label: a deletion verb, painted destructive by the host. */
|
|
105
|
+
confirmText: string;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* What a delete confirmation is about: ONE record (its row — at least its
|
|
109
|
+
* `id`, so a nameless row still reads by the resolver's `Record #id` floor) or
|
|
110
|
+
* a batch (how many).
|
|
111
|
+
*/
|
|
112
|
+
export type RecordDeleteConfirmTarget = {
|
|
113
|
+
record: Row | null | undefined;
|
|
114
|
+
} | {
|
|
115
|
+
count: number;
|
|
116
|
+
};
|
|
95
117
|
/**
|
|
96
118
|
* The record-delete core: `confirmText` is the question to ask before deleting
|
|
97
119
|
* one record, `run` performs the delete (one record, or several settled
|
|
98
|
-
* together) and reports it. See the module docblock for objectui#10383
|
|
99
|
-
* ADR-0094.
|
|
120
|
+
* together) and reports it. See the module docblock for objectui#10383.
|
|
100
121
|
*/
|
|
101
122
|
export declare const recordDelete: Readonly<{
|
|
102
123
|
/**
|
|
103
|
-
* The question to ask before deleting ONE record
|
|
104
|
-
*
|
|
105
|
-
|
|
124
|
+
* The question to ask before deleting ONE record — the body of
|
|
125
|
+
* {@link recordDelete}.confirmCopy.
|
|
126
|
+
*/
|
|
127
|
+
confirmText(deps: Pick<RecordDeleteDeps, "t">): string;
|
|
128
|
+
/**
|
|
129
|
+
* The whole delete confirmation — title, body and confirm-button label — for
|
|
130
|
+
* ONE record or a batch (objectui#11695). Every host's delete dialog shows
|
|
131
|
+
* this copy, so the list's row Delete, its bulk Delete and the record page's
|
|
132
|
+
* Delete say the same thing.
|
|
133
|
+
*
|
|
134
|
+
* One record: the title names it — the object label plus the record's
|
|
135
|
+
* display name from {@link getRecordDisplayName}, reading `objectDef`'s
|
|
136
|
+
* declared name field when the host has the definition — and the body is
|
|
137
|
+
* {@link recordDelete}.confirmText. A batch: the title counts it. The confirm
|
|
138
|
+
* button is the delete verb in both, which the host paints destructive.
|
|
106
139
|
*/
|
|
107
|
-
|
|
140
|
+
confirmCopy(deps: Pick<RecordDeleteDeps, "objectName" | "t" | "label"> & {
|
|
141
|
+
/** The object definition, so the name follows its declared `nameField`. */
|
|
142
|
+
objectDef?: unknown;
|
|
143
|
+
}, target: RecordDeleteConfirmTarget): RecordDeleteConfirmCopy;
|
|
108
144
|
/**
|
|
109
145
|
* Delete, refresh and report. Several records (`params.records`, more than
|
|
110
146
|
* one with an `id`) are deleted one call each and settled together, then the
|
|
111
147
|
* list refreshes and ONE summary toast reports the batch. Otherwise one
|
|
112
|
-
* record is deleted; on success the list refreshes and a success toast shows
|
|
113
|
-
*
|
|
148
|
+
* record is deleted; on success the list refreshes and a success toast shows,
|
|
149
|
+
* on failure an error
|
|
114
150
|
* toast carries the error message and the list is left as it is.
|
|
115
151
|
*/
|
|
116
152
|
run(deps: RecordDeleteDeps, request: RecordDeleteRequest): Promise<ActionResult>;
|
|
@@ -5,38 +5,57 @@
|
|
|
5
5
|
* This source code is licensed under the MIT license found in the
|
|
6
6
|
* LICENSE file in the root directory of this source tree.
|
|
7
7
|
*/
|
|
8
|
-
|
|
9
|
-
/** ADR-0094: is this permission-set row owned by an installed package? */
|
|
10
|
-
function isPackageOwned(row) {
|
|
11
|
-
return row?.managed_by === 'package';
|
|
12
|
-
}
|
|
8
|
+
import { getRecordDisplayName } from '../utils/record-title.js';
|
|
13
9
|
/**
|
|
14
10
|
* The record-delete core: `confirmText` is the question to ask before deleting
|
|
15
11
|
* one record, `run` performs the delete (one record, or several settled
|
|
16
|
-
* together) and reports it. See the module docblock for objectui#10383
|
|
17
|
-
* ADR-0094.
|
|
12
|
+
* together) and reports it. See the module docblock for objectui#10383.
|
|
18
13
|
*/
|
|
19
14
|
export const recordDelete = Object.freeze({
|
|
20
15
|
/**
|
|
21
|
-
* The question to ask before deleting ONE record
|
|
22
|
-
*
|
|
23
|
-
* otherwise. Reads only the row it is handed — no lookup.
|
|
16
|
+
* The question to ask before deleting ONE record — the body of
|
|
17
|
+
* {@link recordDelete}.confirmCopy.
|
|
24
18
|
*/
|
|
25
|
-
confirmText(deps
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
19
|
+
confirmText(deps) {
|
|
20
|
+
return deps.t('objectActions.deleteConfirm');
|
|
21
|
+
},
|
|
22
|
+
/**
|
|
23
|
+
* The whole delete confirmation — title, body and confirm-button label — for
|
|
24
|
+
* ONE record or a batch (objectui#11695). Every host's delete dialog shows
|
|
25
|
+
* this copy, so the list's row Delete, its bulk Delete and the record page's
|
|
26
|
+
* Delete say the same thing.
|
|
27
|
+
*
|
|
28
|
+
* One record: the title names it — the object label plus the record's
|
|
29
|
+
* display name from {@link getRecordDisplayName}, reading `objectDef`'s
|
|
30
|
+
* declared name field when the host has the definition — and the body is
|
|
31
|
+
* {@link recordDelete}.confirmText. A batch: the title counts it. The confirm
|
|
32
|
+
* button is the delete verb in both, which the host paints destructive.
|
|
33
|
+
*/
|
|
34
|
+
confirmCopy(deps, target) {
|
|
35
|
+
const { t, label } = deps;
|
|
36
|
+
const confirmText = t('objectActions.deleteConfirmButton');
|
|
37
|
+
if ('count' in target) {
|
|
38
|
+
return {
|
|
39
|
+
title: t('objectActions.bulkDeleteConfirmTitle', { count: target.count, label }),
|
|
40
|
+
message: t('console.objectView.bulkDeleteConfirm', { count: target.count }),
|
|
41
|
+
confirmText,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
title: t('objectActions.deleteConfirmTitle', {
|
|
46
|
+
label,
|
|
47
|
+
name: getRecordDisplayName(deps.objectDef, target.record),
|
|
48
|
+
}),
|
|
49
|
+
message: recordDelete.confirmText(deps),
|
|
50
|
+
confirmText,
|
|
51
|
+
};
|
|
33
52
|
},
|
|
34
53
|
/**
|
|
35
54
|
* Delete, refresh and report. Several records (`params.records`, more than
|
|
36
55
|
* one with an `id`) are deleted one call each and settled together, then the
|
|
37
56
|
* list refreshes and ONE summary toast reports the batch. Otherwise one
|
|
38
|
-
* record is deleted; on success the list refreshes and a success toast shows
|
|
39
|
-
*
|
|
57
|
+
* record is deleted; on success the list refreshes and a success toast shows,
|
|
58
|
+
* on failure an error
|
|
40
59
|
* toast carries the error message and the list is left as it is.
|
|
41
60
|
*/
|
|
42
61
|
async run(deps, request) {
|
|
@@ -73,31 +92,10 @@ export const recordDelete = Object.freeze({
|
|
|
73
92
|
const recordId = params?.recordId ?? params?.record?.id ?? records?.[0]?.id ?? request.recordId;
|
|
74
93
|
if (!recordId)
|
|
75
94
|
return { success: false, error: t('objectActions.noRecordId') };
|
|
76
|
-
// [ADR-0094] Detect the reset BEFORE the delete (the row still exists), so
|
|
77
|
-
// the success toast tells the truth. A caller that passed the row spares
|
|
78
|
-
// the lookup.
|
|
79
|
-
let packagedSetReset = false;
|
|
80
|
-
if (objectName === PERMISSION_SET_OBJECT) {
|
|
81
|
-
try {
|
|
82
|
-
const row = params?.record?.managed_by !== undefined
|
|
83
|
-
? params.record
|
|
84
|
-
: await dataSource.findOne(objectName, recordId);
|
|
85
|
-
packagedSetReset = isPackageOwned(row);
|
|
86
|
-
}
|
|
87
|
-
catch { /* best-effort — fall back to the generic delete copy */ }
|
|
88
|
-
}
|
|
89
95
|
try {
|
|
90
96
|
await dataSource.delete(objectName, recordId);
|
|
91
97
|
onRefresh?.();
|
|
92
|
-
toast.success(
|
|
93
|
-
// No `label` argument, unlike `deleteSuccess` below: this sentence
|
|
94
|
-
// names the permission set by kind rather than by label, so none of
|
|
95
|
-
// the ten packs has a `{{label}}` hole and i18next would drop the
|
|
96
|
-
// argument in silence (objectui#3845).
|
|
97
|
-
? t('objectActions.resetPackageSetSuccess', {
|
|
98
|
-
defaultValue: 'Permission set reset to its shipped baseline',
|
|
99
|
-
})
|
|
100
|
-
: t('objectActions.deleteSuccess', { label }));
|
|
98
|
+
toast.success(t('objectActions.deleteSuccess', { label }));
|
|
101
99
|
return { success: true, reload: true, silent: true };
|
|
102
100
|
}
|
|
103
101
|
catch (err) {
|
|
@@ -5,8 +5,11 @@
|
|
|
5
5
|
* This source code is licensed under the MIT license found in the
|
|
6
6
|
* LICENSE file in the root directory of this source tree.
|
|
7
7
|
*
|
|
8
|
-
* A DataSource adapter
|
|
9
|
-
*
|
|
8
|
+
* A DataSource adapter over raw HTTP, built in code from an
|
|
9
|
+
* `ApiDataSourceConfig` (e.g. the console's approvals inbox). It is not a
|
|
10
|
+
* `ViewData` mode: the view-level `provider: 'api'` is retired in
|
|
11
|
+
* `@objectstack/spec` (objectstack#23082), and `resolveDataSource` builds no
|
|
12
|
+
* `ApiDataSource` (objectui#12325).
|
|
10
13
|
*/
|
|
11
14
|
import type { DataSource, BatchTransactionOperation, QueryParams, QueryResult, HttpRequest, AggregateParams, AggregateResult } from '@object-ui/types';
|
|
12
15
|
export interface ApiDataSourceConfig {
|
|
@@ -22,8 +25,9 @@ export interface ApiDataSourceConfig {
|
|
|
22
25
|
/**
|
|
23
26
|
* ApiDataSource — a DataSource adapter for raw HTTP APIs.
|
|
24
27
|
*
|
|
25
|
-
*
|
|
26
|
-
* configs define the endpoints; all
|
|
28
|
+
* Constructed in code — never resolved from a `ViewData` block (see the file
|
|
29
|
+
* header). The read and write HttpRequest configs define the endpoints; all
|
|
30
|
+
* CRUD methods map onto HTTP verbs.
|
|
27
31
|
*
|
|
28
32
|
* Read operations use the `read` config, write operations use the `write` config.
|
|
29
33
|
* Both fall back to each other when one is not provided.
|
|
@@ -5,8 +5,11 @@
|
|
|
5
5
|
* This source code is licensed under the MIT license found in the
|
|
6
6
|
* LICENSE file in the root directory of this source tree.
|
|
7
7
|
*
|
|
8
|
-
* A DataSource adapter
|
|
9
|
-
*
|
|
8
|
+
* A DataSource adapter over raw HTTP, built in code from an
|
|
9
|
+
* `ApiDataSourceConfig` (e.g. the console's approvals inbox). It is not a
|
|
10
|
+
* `ViewData` mode: the view-level `provider: 'api'` is retired in
|
|
11
|
+
* `@objectstack/spec` (objectstack#23082), and `resolveDataSource` builds no
|
|
12
|
+
* `ApiDataSource` (objectui#12325).
|
|
10
13
|
*/
|
|
11
14
|
import { emulateBatchTransaction } from './batchTransaction.js';
|
|
12
15
|
// ---------------------------------------------------------------------------
|
|
@@ -91,8 +94,9 @@ function mergeHeaders(base, override) {
|
|
|
91
94
|
/**
|
|
92
95
|
* ApiDataSource — a DataSource adapter for raw HTTP APIs.
|
|
93
96
|
*
|
|
94
|
-
*
|
|
95
|
-
* configs define the endpoints; all
|
|
97
|
+
* Constructed in code — never resolved from a `ViewData` block (see the file
|
|
98
|
+
* header). The read and write HttpRequest configs define the endpoints; all
|
|
99
|
+
* CRUD methods map onto HTTP verbs.
|
|
96
100
|
*
|
|
97
101
|
* Read operations use the `read` config, write operations use the `write` config.
|
|
98
102
|
* Both fall back to each other when one is not provided.
|
|
@@ -144,7 +148,7 @@ export class ApiDataSource {
|
|
|
144
148
|
async request(base, options = {}) {
|
|
145
149
|
if (!base) {
|
|
146
150
|
throw new Error('ApiDataSource: No HTTP configuration provided for this operation. ' +
|
|
147
|
-
'Ensure the
|
|
151
|
+
'Ensure the ApiDataSourceConfig has read/write configs.');
|
|
148
152
|
}
|
|
149
153
|
const method = options.method ?? base.method ?? 'GET';
|
|
150
154
|
// Merge query params: base.params + extra queryParams
|
|
@@ -213,8 +217,16 @@ export class ApiDataSource {
|
|
|
213
217
|
});
|
|
214
218
|
return raw ?? null;
|
|
215
219
|
}
|
|
216
|
-
catch {
|
|
217
|
-
|
|
220
|
+
catch (err) {
|
|
221
|
+
// `null` is "not found", and only a 404 says that. Every other failure
|
|
222
|
+
// (a refusal, a 5xx, a transport error) rejects, as `ObjectStackAdapter`
|
|
223
|
+
// does, so a record page can tell "no access" and "could not load" from
|
|
224
|
+
// "not found" (objectui#11902, objectui#12032). The status is read from
|
|
225
|
+
// the message `request` above composes ("ApiDataSource: HTTP 404 …").
|
|
226
|
+
if (err instanceof Error && /^ApiDataSource: HTTP 404\b/.test(err.message)) {
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
throw err;
|
|
218
230
|
}
|
|
219
231
|
}
|
|
220
232
|
async create(_resource, data) {
|
|
@@ -9,10 +9,6 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import type { DataSource, ViewData } from '@object-ui/types';
|
|
11
11
|
export interface ResolveDataSourceOptions {
|
|
12
|
-
/** Custom fetch implementation passed to ApiDataSource */
|
|
13
|
-
fetch?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
14
|
-
/** Default headers for API requests */
|
|
15
|
-
defaultHeaders?: Record<string, string>;
|
|
16
12
|
/** Custom ID field for ValueDataSource */
|
|
17
13
|
idField?: string;
|
|
18
14
|
}
|
|
@@ -20,9 +16,16 @@ export interface ResolveDataSourceOptions {
|
|
|
20
16
|
* Resolve a ViewData configuration into a concrete DataSource instance.
|
|
21
17
|
*
|
|
22
18
|
* - `provider: 'object'` → returns `fallback` (the context DataSource — typically ObjectStackAdapter)
|
|
23
|
-
* - `provider: 'api'` → returns a new `ApiDataSource`
|
|
24
19
|
* - `provider: 'value'` → returns a new `ValueDataSource`
|
|
25
20
|
*
|
|
21
|
+
* There is no `provider: 'api'` arm. The view-level `api` data source is
|
|
22
|
+
* retired in `@objectstack/spec` (objectstack#23082, ADR-0049): a view binds
|
|
23
|
+
* its object or static rows, and a metadata-declared API call is the page
|
|
24
|
+
* element's `dataSource` binding. A view block that still says `api` is an
|
|
25
|
+
* unknown provider here and gets the same answer as one, so no request is
|
|
26
|
+
* ever built from its `read` / `write` (objectui#12325). Code that needs raw
|
|
27
|
+
* HTTP constructs `ApiDataSource` itself, from an `ApiDataSourceConfig`.
|
|
28
|
+
*
|
|
26
29
|
* @param viewData - The ViewData configuration from the schema
|
|
27
30
|
* @param fallback - The default DataSource from context (for `provider: 'object'`)
|
|
28
31
|
* @param options - Additional options for adapter construction
|
|
@@ -31,7 +34,7 @@ export interface ResolveDataSourceOptions {
|
|
|
31
34
|
* @example
|
|
32
35
|
* ```ts
|
|
33
36
|
* const ds = resolveDataSource(
|
|
34
|
-
* { provider: '
|
|
37
|
+
* { provider: 'value', items: [{ id: '1', name: 'Ada' }] },
|
|
35
38
|
* contextDataSource,
|
|
36
39
|
* );
|
|
37
40
|
* const result = await ds.find('users');
|
|
@@ -7,15 +7,21 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Factory function to create the right DataSource from a ViewData config.
|
|
9
9
|
*/
|
|
10
|
-
import { ApiDataSource } from './ApiDataSource.js';
|
|
11
10
|
import { ValueDataSource } from './ValueDataSource.js';
|
|
12
11
|
/**
|
|
13
12
|
* Resolve a ViewData configuration into a concrete DataSource instance.
|
|
14
13
|
*
|
|
15
14
|
* - `provider: 'object'` → returns `fallback` (the context DataSource — typically ObjectStackAdapter)
|
|
16
|
-
* - `provider: 'api'` → returns a new `ApiDataSource`
|
|
17
15
|
* - `provider: 'value'` → returns a new `ValueDataSource`
|
|
18
16
|
*
|
|
17
|
+
* There is no `provider: 'api'` arm. The view-level `api` data source is
|
|
18
|
+
* retired in `@objectstack/spec` (objectstack#23082, ADR-0049): a view binds
|
|
19
|
+
* its object or static rows, and a metadata-declared API call is the page
|
|
20
|
+
* element's `dataSource` binding. A view block that still says `api` is an
|
|
21
|
+
* unknown provider here and gets the same answer as one, so no request is
|
|
22
|
+
* ever built from its `read` / `write` (objectui#12325). Code that needs raw
|
|
23
|
+
* HTTP constructs `ApiDataSource` itself, from an `ApiDataSourceConfig`.
|
|
24
|
+
*
|
|
19
25
|
* @param viewData - The ViewData configuration from the schema
|
|
20
26
|
* @param fallback - The default DataSource from context (for `provider: 'object'`)
|
|
21
27
|
* @param options - Additional options for adapter construction
|
|
@@ -24,7 +30,7 @@ import { ValueDataSource } from './ValueDataSource.js';
|
|
|
24
30
|
* @example
|
|
25
31
|
* ```ts
|
|
26
32
|
* const ds = resolveDataSource(
|
|
27
|
-
* { provider: '
|
|
33
|
+
* { provider: 'value', items: [{ id: '1', name: 'Ada' }] },
|
|
28
34
|
* contextDataSource,
|
|
29
35
|
* );
|
|
30
36
|
* const result = await ds.find('users');
|
|
@@ -38,15 +44,6 @@ export function resolveDataSource(viewData, fallback, options) {
|
|
|
38
44
|
case 'object':
|
|
39
45
|
// Delegate to the context DataSource (ObjectStackAdapter, etc.)
|
|
40
46
|
return fallback ?? null;
|
|
41
|
-
case 'api': {
|
|
42
|
-
const config = {
|
|
43
|
-
read: viewData.read,
|
|
44
|
-
write: viewData.write,
|
|
45
|
-
fetch: options?.fetch,
|
|
46
|
-
defaultHeaders: options?.defaultHeaders,
|
|
47
|
-
};
|
|
48
|
-
return new ApiDataSource(config);
|
|
49
|
-
}
|
|
50
47
|
case 'value':
|
|
51
48
|
return new ValueDataSource({
|
|
52
49
|
items: (viewData.items ?? []),
|
|
@@ -9,11 +9,14 @@
|
|
|
9
9
|
* @object-ui/core - View Data Provider
|
|
10
10
|
*
|
|
11
11
|
* Resolves @objectstack/spec ViewData discriminated union into
|
|
12
|
-
* a unified data access interface. Supports
|
|
12
|
+
* a unified data access interface. Supports two data modes:
|
|
13
13
|
*
|
|
14
14
|
* 1. 'object' — Metadata-driven via ObjectStack API (object name → fields/records)
|
|
15
|
-
* 2. '
|
|
16
|
-
*
|
|
15
|
+
* 2. 'value' — Static data (pre-loaded items array)
|
|
16
|
+
*
|
|
17
|
+
* The view-level `'api'` mode is retired in `@objectstack/spec`
|
|
18
|
+
* (objectstack#23082, ADR-0049) and has no arm here (objectui#12325): a
|
|
19
|
+
* config that still names it resolves as an unknown provider, with an error.
|
|
17
20
|
*
|
|
18
21
|
* @module data-scope
|
|
19
22
|
* @packageDocumentation
|
|
@@ -23,18 +26,6 @@ import { type ElementDataSourceConfig, type ElementSavedView } from './element-d
|
|
|
23
26
|
export type ViewDataConfig = {
|
|
24
27
|
provider: 'object';
|
|
25
28
|
object: string;
|
|
26
|
-
} | {
|
|
27
|
-
provider: 'api';
|
|
28
|
-
read: {
|
|
29
|
-
url: string;
|
|
30
|
-
method?: string;
|
|
31
|
-
headers?: Record<string, string>;
|
|
32
|
-
};
|
|
33
|
-
write?: {
|
|
34
|
-
url: string;
|
|
35
|
-
method?: string;
|
|
36
|
-
headers?: Record<string, string>;
|
|
37
|
-
};
|
|
38
29
|
} | {
|
|
39
30
|
provider: 'value';
|
|
40
31
|
items: any[];
|
|
@@ -72,12 +63,6 @@ export interface DataFetcher {
|
|
|
72
63
|
required?: boolean;
|
|
73
64
|
}>;
|
|
74
65
|
}>;
|
|
75
|
-
/** Fetch from a custom API URL */
|
|
76
|
-
fetchUrl?(url: string, options?: {
|
|
77
|
-
method?: string;
|
|
78
|
-
headers?: Record<string, string>;
|
|
79
|
-
body?: any;
|
|
80
|
-
}): Promise<any>;
|
|
81
66
|
/**
|
|
82
67
|
* Fetch the object's saved views, so an `ElementDataSource.view` name can be
|
|
83
68
|
* resolved to real columns / filter / sort (objectstack#5576).
|
|
@@ -110,7 +95,7 @@ export interface ResolvedData {
|
|
|
110
95
|
}>;
|
|
111
96
|
};
|
|
112
97
|
/** Provider type */
|
|
113
|
-
provider: 'object' | '
|
|
98
|
+
provider: 'object' | 'value';
|
|
114
99
|
/** Loading state */
|
|
115
100
|
loading: boolean;
|
|
116
101
|
/** Error */
|
|
@@ -143,8 +128,6 @@ export declare class ViewDataProvider {
|
|
|
143
128
|
}): Promise<ResolvedData>;
|
|
144
129
|
/** Resolve static value data */
|
|
145
130
|
private resolveValue;
|
|
146
|
-
/** Resolve API-based data */
|
|
147
|
-
private resolveApi;
|
|
148
131
|
/** Resolve object-based data (metadata-driven) */
|
|
149
132
|
private resolveObject;
|
|
150
133
|
/**
|