@protobase/schema 0.22.0 → 0.22.1
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/dist/internal.d.ts +2 -1
- package/dist/internal.js +2 -1
- package/dist/model.d.ts +2 -2
- package/dist/update-set.d.ts +12 -0
- package/dist/update-set.js +19 -0
- package/dist/view-actions.d.ts +9 -4
- package/dist/view-actions.js +26 -16
- package/package.json +1 -1
package/dist/internal.d.ts
CHANGED
|
@@ -12,10 +12,11 @@ import { SearchTerm, matchedSearchField, matchesSearch, searchTerms, termMatches
|
|
|
12
12
|
import { defaultRecentLimit, navRecentErrors, statusTones } from "./nav-recent.js";
|
|
13
13
|
import { PluginHost, PluginRequirement, inRange, pluginProblem } from "./plugin-version.js";
|
|
14
14
|
import { redactSecrets } from "./redact-secrets.js";
|
|
15
|
+
import { updateSetErrors, updateValues } from "./update-set.js";
|
|
15
16
|
import { addressPlaceholders, appHrefError, fillAddress, hrefField, hrefPlaceholders, leavesApp, linkActionError } from "./address-template.js";
|
|
16
17
|
import { promptError, promptFields, renderPrompt } from "./ai-prompt.js";
|
|
17
18
|
import { AddressCheck, addressErrors } from "./address-errors.js";
|
|
18
19
|
import { applyAssistantEvent, emptyAssistantState } from "./assistant/apply-event.js";
|
|
19
20
|
import { readEventStream } from "./assistant/event-stream.js";
|
|
20
21
|
import { layoutItems } from "./layout-items.js";
|
|
21
|
-
export { type AddressCheck, type PluginHost, type PluginRequirement, type PropShapes, type RefShape, type SearchTerm, actionInputErrors, addressErrors, addressPlaceholders, appHrefError, applyAssistantEvent, boardErrors, bottomBarFor, calendarErrors, cardPhotoLimit, chartFields, checkEventFilter, checkedAccept, checkedProvider, decodeKey, defaultRecentLimit, displayFields, emptyAssistantState, encodeKey, fieldRefsIn, fillAddress, hrefField, hrefPlaceholders, inRange, isFieldRef, isGroupChart, layoutItems, leavesApp, linkActionError, markFieldRefs, matchedSearchField, matchesSearch, navRecentErrors, parseSize, pluginProblem, promptError, promptFields, pruneByShape, readEventStream, redactSecrets, renderPrompt, searchTerms, shapes, shownInputs, statusTones, tableDisplay, termMatches, unmarkFieldRefs, webhookResources };
|
|
22
|
+
export { type AddressCheck, type PluginHost, type PluginRequirement, type PropShapes, type RefShape, type SearchTerm, actionInputErrors, addressErrors, addressPlaceholders, appHrefError, applyAssistantEvent, boardErrors, bottomBarFor, calendarErrors, cardPhotoLimit, chartFields, checkEventFilter, checkedAccept, checkedProvider, decodeKey, defaultRecentLimit, displayFields, emptyAssistantState, encodeKey, fieldRefsIn, fillAddress, hrefField, hrefPlaceholders, inRange, isFieldRef, isGroupChart, layoutItems, leavesApp, linkActionError, markFieldRefs, matchedSearchField, matchesSearch, navRecentErrors, parseSize, pluginProblem, promptError, promptFields, pruneByShape, readEventStream, redactSecrets, renderPrompt, searchTerms, shapes, shownInputs, statusTones, tableDisplay, termMatches, unmarkFieldRefs, updateSetErrors, updateValues, webhookResources };
|
package/dist/internal.js
CHANGED
|
@@ -5,6 +5,7 @@ import { matchedSearchField, matchesSearch, searchTerms, termMatches } from "./s
|
|
|
5
5
|
import { checkEventFilter } from "./events/event-models.js";
|
|
6
6
|
import { defaultRecentLimit, navRecentErrors, statusTones } from "./nav-recent.js";
|
|
7
7
|
import { actionInputErrors, shownInputs } from "./action-inputs.js";
|
|
8
|
+
import { updateSetErrors, updateValues } from "./update-set.js";
|
|
8
9
|
import { chartFields, isGroupChart } from "./view-chart.js";
|
|
9
10
|
import { fieldRefsIn, isFieldRef, markFieldRefs, unmarkFieldRefs } from "./field-ref.js";
|
|
10
11
|
import { boardErrors, calendarErrors, cardPhotoLimit, displayFields, tableDisplay } from "./view-displays.js";
|
|
@@ -18,4 +19,4 @@ import { addressErrors } from "./address-errors.js";
|
|
|
18
19
|
import { applyAssistantEvent, emptyAssistantState } from "./assistant/apply-event.js";
|
|
19
20
|
import { readEventStream } from "./assistant/event-stream.js";
|
|
20
21
|
import { layoutItems } from "./layout-items.js";
|
|
21
|
-
export { actionInputErrors, addressErrors, addressPlaceholders, appHrefError, applyAssistantEvent, boardErrors, bottomBarFor, calendarErrors, cardPhotoLimit, chartFields, checkEventFilter, checkedAccept, checkedProvider, decodeKey, defaultRecentLimit, displayFields, emptyAssistantState, encodeKey, fieldRefsIn, fillAddress, hrefField, hrefPlaceholders, inRange, isFieldRef, isGroupChart, layoutItems, leavesApp, linkActionError, markFieldRefs, matchedSearchField, matchesSearch, navRecentErrors, parseSize, pluginProblem, promptError, promptFields, pruneByShape, readEventStream, redactSecrets, renderPrompt, searchTerms, shapes, shownInputs, statusTones, tableDisplay, termMatches, unmarkFieldRefs, webhookResources };
|
|
22
|
+
export { actionInputErrors, addressErrors, addressPlaceholders, appHrefError, applyAssistantEvent, boardErrors, bottomBarFor, calendarErrors, cardPhotoLimit, chartFields, checkEventFilter, checkedAccept, checkedProvider, decodeKey, defaultRecentLimit, displayFields, emptyAssistantState, encodeKey, fieldRefsIn, fillAddress, hrefField, hrefPlaceholders, inRange, isFieldRef, isGroupChart, layoutItems, leavesApp, linkActionError, markFieldRefs, matchedSearchField, matchesSearch, navRecentErrors, parseSize, pluginProblem, promptError, promptFields, pruneByShape, readEventStream, redactSecrets, renderPrompt, searchTerms, shapes, shownInputs, statusTones, tableDisplay, termMatches, unmarkFieldRefs, updateSetErrors, updateValues, webhookResources };
|
package/dist/model.d.ts
CHANGED
|
@@ -822,7 +822,7 @@ export type SaveFeedback = 'toast' | 'button';
|
|
|
822
822
|
export type ActionRun = {
|
|
823
823
|
/** Updates the record. */
|
|
824
824
|
kind: 'update';
|
|
825
|
-
/** The fields it sets, by name. */
|
|
825
|
+
/** The fields it sets, by name; a value that is exactly `{input}` is that input's answer. */
|
|
826
826
|
values: Record<string, unknown>;
|
|
827
827
|
} | {
|
|
828
828
|
/** Deletes the record, soft deleting when the resource does. */
|
|
@@ -865,7 +865,7 @@ export type ActionModel = {
|
|
|
865
865
|
bulk: boolean;
|
|
866
866
|
/** What it does; without it the app runs the handler registered under `name`. */
|
|
867
867
|
run?: ActionRun;
|
|
868
|
-
/** What the button asks for in a dialog before the
|
|
868
|
+
/** What the button asks for in a dialog before the action runs: a handler gets the answers as `values`, an update writes them through `set`. */
|
|
869
869
|
inputs?: ActionInputModel[];
|
|
870
870
|
};
|
|
871
871
|
/**
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ActionInputModel } from "./model.js";
|
|
2
|
+
//#region src/update-set.d.ts
|
|
3
|
+
/** Why an update's `set` cannot be used: a value that is exactly `{name}` must name one of the inputs the action asks for. */
|
|
4
|
+
export declare const updateSetErrors: (set: Record<string, unknown>, inputs?: ActionInputModel[]) => string[];
|
|
5
|
+
/**
|
|
6
|
+
* The fields an update writes: a literal as it is, a value that is exactly `{input}` as that input's answer. A field whose
|
|
7
|
+
* input was hidden has no answer in `values` and is left out, so it keeps what it holds.
|
|
8
|
+
*/
|
|
9
|
+
export declare const updateValues: (set: Record<string, unknown>, values: Record<string, unknown>) => {
|
|
10
|
+
[k: string]: unknown;
|
|
11
|
+
};
|
|
12
|
+
//#endregion
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { hrefField } from "./address-template.js";
|
|
2
|
+
//#region src/update-set.ts
|
|
3
|
+
/** Why an update's `set` cannot be used: a value that is exactly `{name}` must name one of the inputs the action asks for. */
|
|
4
|
+
const updateSetErrors = (set, inputs = []) => Object.entries(set).flatMap(([field, value]) => {
|
|
5
|
+
const input = typeof value === "string" ? hrefField(value) : void 0;
|
|
6
|
+
if (input === void 0 || inputs.some((entry) => entry.name === input)) return [];
|
|
7
|
+
return [`set "${field}" names input "${input}", which it does not ask for`];
|
|
8
|
+
});
|
|
9
|
+
/**
|
|
10
|
+
* The fields an update writes: a literal as it is, a value that is exactly `{input}` as that input's answer. A field whose
|
|
11
|
+
* input was hidden has no answer in `values` and is left out, so it keeps what it holds.
|
|
12
|
+
*/
|
|
13
|
+
const updateValues = (set, values) => Object.fromEntries(Object.entries(set).flatMap(([field, value]) => {
|
|
14
|
+
const input = typeof value === "string" ? hrefField(value) : void 0;
|
|
15
|
+
if (input === void 0) return [[field, value]];
|
|
16
|
+
return Object.hasOwn(values, input) ? [[field, values[input]]] : [];
|
|
17
|
+
}));
|
|
18
|
+
//#endregion
|
|
19
|
+
export { updateSetErrors, updateValues };
|
package/dist/view-actions.d.ts
CHANGED
|
@@ -7,21 +7,26 @@ type ActionOptions = {
|
|
|
7
7
|
icon?: string;
|
|
8
8
|
confirm?: string;
|
|
9
9
|
};
|
|
10
|
+
/** @inline */
|
|
11
|
+
type InputsOption = {
|
|
12
|
+
inputs?: (i: typeof inputs) => ActionInputModel[];
|
|
13
|
+
};
|
|
10
14
|
export declare const actions: {
|
|
11
15
|
/**
|
|
12
16
|
* An action the app implements: it runs the handler registered under `name`. With `inputs`, such as
|
|
13
17
|
* `(i) => [i.text('reason', { label: 'Reason' })]`, the button first asks for them in a dialog, and the handler gets the
|
|
14
18
|
* answers as `values`.
|
|
15
19
|
*/
|
|
16
|
-
action: (name: string, o: ActionOptions & {
|
|
20
|
+
action: (name: string, o: ActionOptions & InputsOption & {
|
|
17
21
|
bulk?: boolean;
|
|
18
|
-
inputs?: (i: typeof inputs) => ActionInputModel[];
|
|
19
22
|
}) => ActionModel;
|
|
20
23
|
/**
|
|
21
24
|
* Sets fields of the record, as one update with its ETag, for example `{ status: 'paid' }`. It is not offered on a
|
|
22
|
-
* record that holds these values already.
|
|
25
|
+
* record that holds these values already. With `inputs`, the button first asks for them in a dialog, and a `set` value
|
|
26
|
+
* that is exactly `'{name}'` is written as that input's answer, with no handler code:
|
|
27
|
+
* `a.update('answer', { label: 'Answer', inputs: (i) => [i.text('answer', { label: 'Answer', multiline: true })], set: { answer: '{answer}' } })`.
|
|
23
28
|
*/
|
|
24
|
-
update: (name: string, o: ActionOptions & {
|
|
29
|
+
update: (name: string, o: ActionOptions & InputsOption & {
|
|
25
30
|
set: Record<string, unknown>;
|
|
26
31
|
}) => ActionModel;
|
|
27
32
|
/** Deletes the record (soft delete when the resource has one). */
|
package/dist/view-actions.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { linkActionError } from "./address-template.js";
|
|
2
2
|
import { promptError } from "./ai-prompt.js";
|
|
3
3
|
import { actionInputErrors, inputs } from "./action-inputs.js";
|
|
4
|
+
import { updateSetErrors } from "./update-set.js";
|
|
4
5
|
//#region src/view-actions.ts
|
|
5
6
|
const named = (name, o) => ({
|
|
6
7
|
name,
|
|
@@ -9,33 +10,42 @@ const named = (name, o) => ({
|
|
|
9
10
|
...o.icon !== void 0 && { icon: o.icon },
|
|
10
11
|
...o.confirm !== void 0 && { confirm: o.confirm }
|
|
11
12
|
});
|
|
13
|
+
const builtInputs = (name, option) => {
|
|
14
|
+
if (!option) return {};
|
|
15
|
+
const built = option(inputs);
|
|
16
|
+
const errors = actionInputErrors(built);
|
|
17
|
+
if (errors.length > 0) throw new Error(`Action "${name}": ${errors.join("; ")}`);
|
|
18
|
+
return { inputs: built };
|
|
19
|
+
};
|
|
12
20
|
const actions = {
|
|
13
21
|
/**
|
|
14
22
|
* An action the app implements: it runs the handler registered under `name`. With `inputs`, such as
|
|
15
23
|
* `(i) => [i.text('reason', { label: 'Reason' })]`, the button first asks for them in a dialog, and the handler gets the
|
|
16
24
|
* answers as `values`.
|
|
17
25
|
*/
|
|
18
|
-
action: (name, o) => {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
26
|
+
action: (name, o) => ({
|
|
27
|
+
...named(name, o),
|
|
28
|
+
...builtInputs(name, o.inputs)
|
|
29
|
+
}),
|
|
30
|
+
/**
|
|
31
|
+
* Sets fields of the record, as one update with its ETag, for example `{ status: 'paid' }`. It is not offered on a
|
|
32
|
+
* record that holds these values already. With `inputs`, the button first asks for them in a dialog, and a `set` value
|
|
33
|
+
* that is exactly `'{name}'` is written as that input's answer, with no handler code:
|
|
34
|
+
* `a.update('answer', { label: 'Answer', inputs: (i) => [i.text('answer', { label: 'Answer', multiline: true })], set: { answer: '{answer}' } })`.
|
|
35
|
+
*/
|
|
36
|
+
update: (name, o) => {
|
|
37
|
+
const built = builtInputs(name, o.inputs);
|
|
38
|
+
const errors = updateSetErrors(o.set, built.inputs);
|
|
22
39
|
if (errors.length > 0) throw new Error(`Action "${name}": ${errors.join("; ")}`);
|
|
23
40
|
return {
|
|
24
41
|
...named(name, o),
|
|
25
|
-
|
|
42
|
+
...built,
|
|
43
|
+
run: {
|
|
44
|
+
kind: "update",
|
|
45
|
+
values: o.set
|
|
46
|
+
}
|
|
26
47
|
};
|
|
27
48
|
},
|
|
28
|
-
/**
|
|
29
|
-
* Sets fields of the record, as one update with its ETag, for example `{ status: 'paid' }`. It is not offered on a
|
|
30
|
-
* record that holds these values already.
|
|
31
|
-
*/
|
|
32
|
-
update: (name, o) => ({
|
|
33
|
-
...named(name, o),
|
|
34
|
-
run: {
|
|
35
|
-
kind: "update",
|
|
36
|
-
values: o.set
|
|
37
|
-
}
|
|
38
|
-
}),
|
|
39
49
|
/** Deletes the record (soft delete when the resource has one). */
|
|
40
50
|
remove: (name, o) => ({
|
|
41
51
|
...named(name, o),
|