@protobase/schema 0.33.4 → 0.34.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.
@@ -31,7 +31,7 @@ const checked = (model, expr, operation) => {
31
31
  ...model,
32
32
  fields: Object.fromEntries(Object.entries(model.fields).map(([name, field]) => [name, {
33
33
  ...field,
34
- filterable: true
34
+ filterable: !field.encrypted
35
35
  }]))
36
36
  };
37
37
  const result = checkFilter(trusted, expr);
@@ -0,0 +1,21 @@
1
+ //#region src/connect-address.d.ts
2
+ /**
3
+ * The path of the connect page, where someone signs in and allows an outside app, such as an MCP client, to act as
4
+ * them. The OAuth provider sends people there with the authorization request it signed as the page's query.
5
+ */
6
+ export declare const connectRoute = "/-/connect";
7
+ /** The names of the connect page's query parameters the page reads itself; the rest belong to the signed request. */
8
+ export declare const connectParams: {
9
+ /** The signature the OAuth provider puts on the request, without which the query is no request. */
10
+ readonly signature: "sig";
11
+ };
12
+ /** Where the connect page is: the signed authorization request, as the provider wrote it, when the query holds one. */
13
+ export type ConnectAddress = {
14
+ /** The whole query, without its `?`, which the page hands back to the provider as it is. */
15
+ oauthQuery?: string;
16
+ };
17
+ /** The path of the connect page for a signed authorization request, `oauthQuery` being its query without the `?`. */
18
+ export declare const connectPath: (oauthQuery?: string) => string;
19
+ /** The connect page's signed request, read from its query (`location.search`, with or without the `?`). */
20
+ export declare const readConnectAddress: (search: string) => ConnectAddress;
21
+ //#endregion
@@ -0,0 +1,19 @@
1
+ //#region src/connect-address.ts
2
+ /**
3
+ * The path of the connect page, where someone signs in and allows an outside app, such as an MCP client, to act as
4
+ * them. The OAuth provider sends people there with the authorization request it signed as the page's query.
5
+ */
6
+ const connectRoute = "/-/connect";
7
+ /** The names of the connect page's query parameters the page reads itself; the rest belong to the signed request. */
8
+ const connectParams = {
9
+ /** The signature the OAuth provider puts on the request, without which the query is no request. */
10
+ signature: "sig" };
11
+ /** The path of the connect page for a signed authorization request, `oauthQuery` being its query without the `?`. */
12
+ const connectPath = (oauthQuery) => oauthQuery ? `${connectRoute}?${oauthQuery}` : connectRoute;
13
+ /** The connect page's signed request, read from its query (`location.search`, with or without the `?`). */
14
+ const readConnectAddress = (search) => {
15
+ const query = search.startsWith("?") ? search.slice(1) : search;
16
+ return new URLSearchParams(query).has(connectParams.signature) ? { oauthQuery: query } : {};
17
+ };
18
+ //#endregion
19
+ export { connectParams, connectPath, connectRoute, readConnectAddress };
@@ -0,0 +1,14 @@
1
+ //#region src/encrypted-value.d.ts
2
+ /** The type of {@link encrypted}: what app code gets in place of a set encrypted field's stored value. */
3
+ export type Encrypted = {
4
+ /** Reading the value as text throws: only whether the field is set can be read. */
5
+ readonly [Symbol.toPrimitive]: () => never;
6
+ };
7
+ /**
8
+ * What record rules, validators and write hooks get in place of an `.encrypted()` field's stored value when it is set;
9
+ * an empty one is `null`. It is the same for every value and carries nothing of it: test it with `== null` or
10
+ * `=== encrypted`, while comparing it loosely or reading it as text throws. See
11
+ * [Encrypted fields](website/src/content/docs/reference/data-config.mdx#encrypted-fields).
12
+ */
13
+ export declare const encrypted: Encrypted;
14
+ //#endregion
@@ -0,0 +1,12 @@
1
+ //#region src/encrypted-value.ts
2
+ /**
3
+ * What record rules, validators and write hooks get in place of an `.encrypted()` field's stored value when it is set;
4
+ * an empty one is `null`. It is the same for every value and carries nothing of it: test it with `== null` or
5
+ * `=== encrypted`, while comparing it loosely or reading it as text throws. See
6
+ * [Encrypted fields](website/src/content/docs/reference/data-config.mdx#encrypted-fields).
7
+ */
8
+ const encrypted = Object.freeze(Object.create(null, { [Symbol.toPrimitive]: { value: () => {
9
+ throw new TypeError("An encrypted field's stored value cannot be read, only whether it is set");
10
+ } } }));
11
+ //#endregion
12
+ export { encrypted };
package/dist/field.d.ts CHANGED
@@ -2,6 +2,7 @@ import { Rollup } from "./computed/rollup.js";
2
2
  import { FileOptions } from "./files/file-options.js";
3
3
  import { FieldType } from "./model.js";
4
4
  import { FieldAccess } from "./access/types.js";
5
+ import { Encrypted } from "./encrypted-value.js";
5
6
  import { z } from "zod";
6
7
  //#region src/field.d.ts
7
8
  /** A check on the value that only some field types take: `.min()`, `.max()` and `.regex()`. */
@@ -32,6 +33,8 @@ export type FieldMeta = {
32
33
  access?: FieldAccess;
33
34
  /** Whether it is hidden like a password (`.sensitive()`). */
34
35
  sensitive?: boolean;
36
+ /** Whether it is stored encrypted, and so sensitive too (`.encrypted()`). */
37
+ encrypted?: boolean;
35
38
  /** An enum's values (`f.enum()`). */
36
39
  enumValues?: string[];
37
40
  /** A relation's resource and the columns that hold its key (`f.relation()`, `.columns()`). */
@@ -111,6 +114,13 @@ export declare class Field<V> {
111
114
  * value of one record on its own (`POST /{resource}/{key}:reveal`), and every reveal is published as an audit event.
112
115
  */
113
116
  sensitive(): Field<V>;
117
+ /**
118
+ * Stored encrypted with the app's own key (`PROTOBASE_ENCRYPTION_KEY`): the database only ever holds ciphertext. It is
119
+ * sensitive too, so only its reveal decrypts it, and record rules, validators and hooks get a set stored value as
120
+ * {@link encrypted}, never the value. Only `f.text()` takes it. See
121
+ * [Encrypted fields](website/src/content/docs/reference/data-config.mdx#encrypted-fields).
122
+ */
123
+ encrypted(): Field<V | Encrypted>;
114
124
  /** The database supplies a value when none is given (identity, now(), ...). */
115
125
  dbDefault(): Field<V>;
116
126
  /** Other names filters may use for the field. */
package/dist/field.js CHANGED
@@ -56,6 +56,19 @@ var Field = class Field {
56
56
  sensitive() {
57
57
  return this.with({ sensitive: true });
58
58
  }
59
+ /**
60
+ * Stored encrypted with the app's own key (`PROTOBASE_ENCRYPTION_KEY`): the database only ever holds ciphertext. It is
61
+ * sensitive too, so only its reveal decrypts it, and record rules, validators and hooks get a set stored value as
62
+ * {@link encrypted}, never the value. Only `f.text()` takes it. See
63
+ * [Encrypted fields](website/src/content/docs/reference/data-config.mdx#encrypted-fields).
64
+ */
65
+ encrypted() {
66
+ if (this.meta.type !== "text") throw new Error(".encrypted() takes only f.text() fields");
67
+ return this.with({
68
+ encrypted: true,
69
+ sensitive: true
70
+ });
71
+ }
59
72
  /** The database supplies a value when none is given (identity, now(), ...). */
60
73
  dbDefault() {
61
74
  return this.with({ dbDefault: true });
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@ import { Rollup, RollupCount, RollupFn, RollupSource, RollupValue } from "./comp
2
2
  import { FileOptions, FileProcessor, FileProcessorInput, ImageFormat, ImageVariant, ImageVariantOptions, imageFormats } from "./files/file-options.js";
3
3
  import { ActionInputKind, ActionInputModel, ActionModel, ActionRun, AppAddress, BadgeShape, BottomBarItemModel, BottomBarModel, CalendarView, CardFavoriteModel, CardPhotosModel, ChartGranularity, ChartModel, ChartRange, CheckedFilter, CommentsSectionModel, CompareOp, CreateModel, CustomSectionModel, DatePreset, DurationLiteral, DurationUnit, EventModel, EventOperation, ExportModel, FieldFormat, FieldModel, FieldPath, FieldType, FieldViewModel, FieldWidgetModel, FileFieldModel, FilterError, FilterErrorCode, FilterExpr, FilterLayout, FilterOf, FilterValue, FilterWidgetModel, FormModel, GalleryModel, GroupChartModel, GroupItemModel, GroupModel, HelpWhen, HistorySectionModel, JsonValue, KeyValue, LayoutItemModel, ListDisplayModel, ListDragModel, ListModel, ListQuery, Literal, MapSectionModel, MapView, NamedListModel, NavModel, NavRecentModel, NowValue, OrderByItem, ParentAccess, ParentModel, PeriodChartModel, PositionAfterModel, RecordChartModel, RecordChartRange, RecordMode, RecordWhen, RelatedColumnModel, RelatedModel, RelatedPresentation, ResourceModel, SaveFeedback, SaveSearchModel, SearchMatch, SearchResultModel, SearchesOfModel, SectionModel, SectionPresentation, ShowWhen, SignInPromptModel, SignInViewModel, SortSpec, Span, StatusTone, TabItemModel, TabModel, TimelineView, UserMenuItemModel, UserMenuModel, ViewModel } from "./model.js";
4
4
  import { AccessAction, AccessContext, AccessFn, AccessResult, AccessUser, FieldAccess, FieldRule } from "./access/types.js";
5
+ import { Encrypted, encrypted } from "./encrypted-value.js";
5
6
  import { AnyField, Constraint, Field, FieldMeta, FieldTypeDef } from "./field.js";
6
7
  import { RelationField } from "./relation-field.js";
7
8
  import { FileField } from "./file-field.js";
@@ -26,6 +27,7 @@ import { layout } from "./layout.js";
26
27
  import { KeyType, fitsKeyType, keyTypes } from "./keys.js";
27
28
  import { addressWords, appPath, readAppPath } from "./app-address.js";
28
29
  import { FormAddress, formPath, formRoute, readFormAddress } from "./form-address.js";
30
+ import { ConnectAddress, connectParams, connectPath, connectRoute, readConnectAddress } from "./connect-address.js";
29
31
  import { SignInAddress, SignInStep, readSignInAddress, signInParams, signInPath, signInRoute, signInSteps } from "./sign-in-address.js";
30
32
  import { FilterLimits, FilterParse, parseFilter } from "./filter/parse.js";
31
33
  import { CheckResult, checkFilter } from "./filter/check.js";
@@ -43,4 +45,4 @@ import { AccessSource, ResolveOptions, ResolvedAccess, RowFilterOperation, acces
43
45
  import { restrictModel } from "./access/restrict.js";
44
46
  import "./access/index.js";
45
47
  import { AssistantAction, AssistantActionRequest, AssistantCardPart, AssistantDiff, AssistantEvent, AssistantMessage, AssistantMessageRequest, AssistantPart, AssistantState, AssistantStep, AssistantStepState, AssistantTablePart, AssistantTextPart, AssistantTone, AssistantWidgetPart } from "./assistant/protocol.js";
46
- export { type Access, type AccessAction, type AccessContext, type AccessFn, type AccessResult, type AccessSource, type AccessUser, type ActionInputKind, type ActionInputModel, type ActionModel, type ActionRun, type AnyField, type AppAddress, type AssistantAction, type AssistantActionRequest, type AssistantCardPart, type AssistantDiff, type AssistantEvent, type AssistantMessage, type AssistantMessageRequest, type AssistantPart, type AssistantState, type AssistantStep, type AssistantStepState, type AssistantTablePart, type AssistantTextPart, type AssistantTone, type AssistantWidgetPart, type BadgeShape, type BottomBarItemModel, type BottomBarModel, type BottomBarNames, type BottomBarSource, type CalendarView, type Capability, type CapabilityAction, type CapabilityLimit, type CapabilityScope, type CardFavoriteModel, type CardPhotosModel, type ChartGranularity, type ChartInput, type ChartModel, type ChartRange, type CheckResult, type CheckedFilter, type CommentsSectionModel, type CompareOp, type ComponentRef, type Constraint, type CreateModel, type CreatedOptions, type CustomSectionModel, type DatePreset, type DeletedOptions, type DurationLiteral, type DurationUnit, type EvaluateOptions, type EventActor, type EventContext, type EventHelpers, type EventMatch, type EventModel, type EventOperation, type EventSpec, type ExactCapability, type ExportModel, type Field, type FieldAccess, type FieldFormat, type FieldMeta, type FieldModel, type FieldPath, type FieldRefValue, type FieldRefs, type FieldRule, type FieldType, type FieldTypeDef, type FieldView, type FieldViewModel, type FieldViews, type FieldWidgetModel, type FieldsInput, type FileField, type FileFieldModel, type FileOptions, type FileProcessor, type FileProcessorInput, type FilterError, type FilterErrorCode, type FilterExpr, type FilterLayout, type FilterLimits, type FilterOf, type FilterParse, type FilterValue, type FilterWidgetModel, type FormAddress, type FormInput, type FormModel, type GalleryModel, type Grant, type GroupChartInput, type GroupChartModel, type GroupItemModel, type GroupModel, type HeldCapability, type HelpWhen, type HistorySectionModel, type ImageFormat, type ImageVariant, type ImageVariantOptions, type InferRecord, type Issue, type JsonValue, type KeyOf, type KeyRefs, type KeyType, type KeyValue, type LayoutItemModel, type ListColumn, type ListDisplayModel, type ListDragModel, type ListModel, type ListQuery, type Literal, type MapSectionModel, type MapView, type NamedListModel, type NavModel, type NavRecentModel, type NowValue, type OrderByCheck, type OrderByItem, type OrderByParse, type ParentAccess, type ParentModel, type PeriodChartInput, type PeriodChartModel, type PositionAfterModel, type RecordChartModel, type RecordChartRange, type RecordKey, type RecordMode, type RecordOf, type RecordWhen, type Ref, type Refs, type RelatedColumnModel, type RelatedModel, type RelatedPresentation, type RelationField, type ResolveOptions, type ResolvedAccess, type ResourceBuilder, type ResourceModel, type RoleEntry, type RoleLimit, type Roles, type RolesOptions, type Rollup, type RollupCount, type RollupFn, type RollupSource, type RollupValue, type RowFilterOperation, type Rule, type SaveFeedback, type SaveSearchModel, type SearchMatch, type SearchRef, type SearchRefs, type SearchResultModel, type SearchesOfModel, type SectionModel, type SectionPresentation, type ShowWhen, type SignInAddress, type SignInPromptModel, type SignInStep, type SignInViewModel, type SortSpec, type Span, type StatusTone, type TabItemModel, type TabModel, type TimelineView, type UndeletedOptions, type UpdatedOptions, type UserMenuItemModel, type UserMenuModel, type UserMenuSource, type Validator, type ViewBuilder, type ViewModel, type ViewProps, accessWarnings, addressWords, anonymousRole, appPath, bottomBar, bottomBarErrors, checkFilter, checkOrderBy, checkRecord, createWhere, defineRoles, enumLabel, evaluateFilter, f, fieldRef, fieldsUsedBy, fitsKeyType, formPath, formRoute, imageFormats, isRecordCondition, keyTypes, layout as l, maxBottomBarItems, parseFilter, parseOrderBy, pickView, printFilter, printOrderBy, readAppPath, readFormAddress, readSignInAddress, resolveAccess, resource, restrictModel, rule, signInParams, signInPath, signInRoute, signInSteps, userMenu, view, where };
48
+ export { type Access, type AccessAction, type AccessContext, type AccessFn, type AccessResult, type AccessSource, type AccessUser, type ActionInputKind, type ActionInputModel, type ActionModel, type ActionRun, type AnyField, type AppAddress, type AssistantAction, type AssistantActionRequest, type AssistantCardPart, type AssistantDiff, type AssistantEvent, type AssistantMessage, type AssistantMessageRequest, type AssistantPart, type AssistantState, type AssistantStep, type AssistantStepState, type AssistantTablePart, type AssistantTextPart, type AssistantTone, type AssistantWidgetPart, type BadgeShape, type BottomBarItemModel, type BottomBarModel, type BottomBarNames, type BottomBarSource, type CalendarView, type Capability, type CapabilityAction, type CapabilityLimit, type CapabilityScope, type CardFavoriteModel, type CardPhotosModel, type ChartGranularity, type ChartInput, type ChartModel, type ChartRange, type CheckResult, type CheckedFilter, type CommentsSectionModel, type CompareOp, type ComponentRef, type ConnectAddress, type Constraint, type CreateModel, type CreatedOptions, type CustomSectionModel, type DatePreset, type DeletedOptions, type DurationLiteral, type DurationUnit, type Encrypted, type EvaluateOptions, type EventActor, type EventContext, type EventHelpers, type EventMatch, type EventModel, type EventOperation, type EventSpec, type ExactCapability, type ExportModel, type Field, type FieldAccess, type FieldFormat, type FieldMeta, type FieldModel, type FieldPath, type FieldRefValue, type FieldRefs, type FieldRule, type FieldType, type FieldTypeDef, type FieldView, type FieldViewModel, type FieldViews, type FieldWidgetModel, type FieldsInput, type FileField, type FileFieldModel, type FileOptions, type FileProcessor, type FileProcessorInput, type FilterError, type FilterErrorCode, type FilterExpr, type FilterLayout, type FilterLimits, type FilterOf, type FilterParse, type FilterValue, type FilterWidgetModel, type FormAddress, type FormInput, type FormModel, type GalleryModel, type Grant, type GroupChartInput, type GroupChartModel, type GroupItemModel, type GroupModel, type HeldCapability, type HelpWhen, type HistorySectionModel, type ImageFormat, type ImageVariant, type ImageVariantOptions, type InferRecord, type Issue, type JsonValue, type KeyOf, type KeyRefs, type KeyType, type KeyValue, type LayoutItemModel, type ListColumn, type ListDisplayModel, type ListDragModel, type ListModel, type ListQuery, type Literal, type MapSectionModel, type MapView, type NamedListModel, type NavModel, type NavRecentModel, type NowValue, type OrderByCheck, type OrderByItem, type OrderByParse, type ParentAccess, type ParentModel, type PeriodChartInput, type PeriodChartModel, type PositionAfterModel, type RecordChartModel, type RecordChartRange, type RecordKey, type RecordMode, type RecordOf, type RecordWhen, type Ref, type Refs, type RelatedColumnModel, type RelatedModel, type RelatedPresentation, type RelationField, type ResolveOptions, type ResolvedAccess, type ResourceBuilder, type ResourceModel, type RoleEntry, type RoleLimit, type Roles, type RolesOptions, type Rollup, type RollupCount, type RollupFn, type RollupSource, type RollupValue, type RowFilterOperation, type Rule, type SaveFeedback, type SaveSearchModel, type SearchMatch, type SearchRef, type SearchRefs, type SearchResultModel, type SearchesOfModel, type SectionModel, type SectionPresentation, type ShowWhen, type SignInAddress, type SignInPromptModel, type SignInStep, type SignInViewModel, type SortSpec, type Span, type StatusTone, type TabItemModel, type TabModel, type TimelineView, type UndeletedOptions, type UpdatedOptions, type UserMenuItemModel, type UserMenuModel, type UserMenuSource, type Validator, type ViewBuilder, type ViewModel, type ViewProps, accessWarnings, addressWords, anonymousRole, appPath, bottomBar, bottomBarErrors, checkFilter, checkOrderBy, checkRecord, connectParams, connectPath, connectRoute, createWhere, defineRoles, encrypted, enumLabel, evaluateFilter, f, fieldRef, fieldsUsedBy, fitsKeyType, formPath, formRoute, imageFormats, isRecordCondition, keyTypes, layout as l, maxBottomBarItems, parseFilter, parseOrderBy, pickView, printFilter, printOrderBy, readAppPath, readConnectAddress, readFormAddress, readSignInAddress, resolveAccess, resource, restrictModel, rule, signInParams, signInPath, signInRoute, signInSteps, userMenu, view, where };
package/dist/index.js CHANGED
@@ -20,9 +20,11 @@ import { bottomBarErrors } from "./bottom-bar-errors.js";
20
20
  import { layout } from "./layout.js";
21
21
  import { fitsKeyType, keyTypes } from "./keys.js";
22
22
  import { addressWords, appPath, readAppPath } from "./app-address.js";
23
+ import { connectParams, connectPath, connectRoute, readConnectAddress } from "./connect-address.js";
23
24
  import { readSignInAddress, signInParams, signInPath, signInRoute, signInSteps } from "./sign-in-address.js";
25
+ import { encrypted } from "./encrypted-value.js";
24
26
  import { rule } from "./access/rule.js";
25
27
  import { defineRoles } from "./access/roles.js";
26
28
  import { pickView } from "./access/pick-view.js";
27
29
  import { restrictModel } from "./access/restrict.js";
28
- export { accessWarnings, addressWords, anonymousRole, appPath, bottomBar, bottomBarErrors, checkFilter, checkOrderBy, checkRecord, createWhere, defineRoles, enumLabel, evaluateFilter, f, fieldRef, fieldsUsedBy, fitsKeyType, formPath, formRoute, imageFormats, isRecordCondition, keyTypes, layout as l, maxBottomBarItems, parseFilter, parseOrderBy, pickView, printFilter, printOrderBy, readAppPath, readFormAddress, readSignInAddress, resolveAccess, resource, restrictModel, rule, signInParams, signInPath, signInRoute, signInSteps, userMenu, view, where };
30
+ export { accessWarnings, addressWords, anonymousRole, appPath, bottomBar, bottomBarErrors, checkFilter, checkOrderBy, checkRecord, connectParams, connectPath, connectRoute, createWhere, defineRoles, encrypted, enumLabel, evaluateFilter, f, fieldRef, fieldsUsedBy, fitsKeyType, formPath, formRoute, imageFormats, isRecordCondition, keyTypes, layout as l, maxBottomBarItems, parseFilter, parseOrderBy, pickView, printFilter, printOrderBy, readAppPath, readConnectAddress, readFormAddress, readSignInAddress, resolveAccess, resource, restrictModel, rule, signInParams, signInPath, signInRoute, signInSteps, userMenu, view, where };
@@ -1,4 +1,5 @@
1
1
  import { checkedAccept, checkedProvider, parseSize } from "./files/file-options.js";
2
+ import { parseTable } from "./resource-model.js";
2
3
  import { actionInputErrors, shownInputs } from "./action-inputs.js";
3
4
  import { actionConditionError } from "./view-actions.js";
4
5
  import { chartFields, isGroupChart } from "./view-chart.js";
@@ -25,4 +26,4 @@ import { layoutItems } from "./layout-items.js";
25
26
  import { viewOfList } from "./named-list.js";
26
27
  import { Locale, isLocale, locales, pickLocale } from "./locale.js";
27
28
  import { apiOriginMeta } from "./api-origin-meta.js";
28
- export { type AddressCheck, type FormulaNode, type Locale, type PluginHost, type PluginRequirement, type PropShapes, type RefShape, type SearchTerm, actionConditionError, actionInputErrors, addressErrors, addressPlaceholders, apiOriginMeta, appHrefError, applyAssistantEvent, boardErrors, bottomBarFor, cardPhotoLimit, chartFields, checkEventFilter, checkedAccept, checkedProvider, datesErrors, decodeKey, defaultRecentLimit, displayFields, emptyAssistantState, encodeKey, fieldRefsIn, fillAddress, hrefField, hrefPlaceholders, inRange, isFieldRef, isGroupChart, isLocale, layoutItems, leavesApp, linkActionError, locales, markFieldRefs, matchedSearchField, matchesSearch, navRecentErrors, parseCapability, parseFormula, parseSize, pickLocale, pluginProblem, promptError, promptFields, pruneByShape, readEventStream, redactSecrets, renderPrompt, searchTerms, shapes, shownInputs, statusTones, tableDisplay, termMatches, unmarkFieldRefs, updateSetErrors, updateValues, viewOfList, webhookResources };
29
+ export { type AddressCheck, type FormulaNode, type Locale, type PluginHost, type PluginRequirement, type PropShapes, type RefShape, type SearchTerm, actionConditionError, actionInputErrors, addressErrors, addressPlaceholders, apiOriginMeta, appHrefError, applyAssistantEvent, boardErrors, bottomBarFor, cardPhotoLimit, chartFields, checkEventFilter, checkedAccept, checkedProvider, datesErrors, decodeKey, defaultRecentLimit, displayFields, emptyAssistantState, encodeKey, fieldRefsIn, fillAddress, hrefField, hrefPlaceholders, inRange, isFieldRef, isGroupChart, isLocale, layoutItems, leavesApp, linkActionError, locales, markFieldRefs, matchedSearchField, matchesSearch, navRecentErrors, parseCapability, parseFormula, parseSize, parseTable, pickLocale, pluginProblem, promptError, promptFields, pruneByShape, readEventStream, redactSecrets, renderPrompt, searchTerms, shapes, shownInputs, statusTones, tableDisplay, termMatches, unmarkFieldRefs, updateSetErrors, updateValues, viewOfList, webhookResources };
package/dist/internal.js CHANGED
@@ -5,6 +5,7 @@ import { checkedAccept, checkedProvider, parseSize } from "./files/file-options.
5
5
  import { matchedSearchField, matchesSearch, searchTerms, termMatches } from "./search-match.js";
6
6
  import { parseCapability } from "./access/capability.js";
7
7
  import { checkEventFilter } from "./events/event-models.js";
8
+ import { parseTable } from "./resource-model.js";
8
9
  import { layoutItems } from "./layout-items.js";
9
10
  import { viewOfList } from "./named-list.js";
10
11
  import { defaultRecentLimit, navRecentErrors, statusTones } from "./nav-recent.js";
@@ -25,4 +26,4 @@ import { applyAssistantEvent, emptyAssistantState } from "./assistant/apply-even
25
26
  import { readEventStream } from "./assistant/event-stream.js";
26
27
  import { isLocale, locales, pickLocale } from "./locale.js";
27
28
  import { apiOriginMeta } from "./api-origin-meta.js";
28
- export { actionConditionError, actionInputErrors, addressErrors, addressPlaceholders, apiOriginMeta, appHrefError, applyAssistantEvent, boardErrors, bottomBarFor, cardPhotoLimit, chartFields, checkEventFilter, checkedAccept, checkedProvider, datesErrors, decodeKey, defaultRecentLimit, displayFields, emptyAssistantState, encodeKey, fieldRefsIn, fillAddress, hrefField, hrefPlaceholders, inRange, isFieldRef, isGroupChart, isLocale, layoutItems, leavesApp, linkActionError, locales, markFieldRefs, matchedSearchField, matchesSearch, navRecentErrors, parseCapability, parseFormula, parseSize, pickLocale, pluginProblem, promptError, promptFields, pruneByShape, readEventStream, redactSecrets, renderPrompt, searchTerms, shapes, shownInputs, statusTones, tableDisplay, termMatches, unmarkFieldRefs, updateSetErrors, updateValues, viewOfList, webhookResources };
29
+ export { actionConditionError, actionInputErrors, addressErrors, addressPlaceholders, apiOriginMeta, appHrefError, applyAssistantEvent, boardErrors, bottomBarFor, cardPhotoLimit, chartFields, checkEventFilter, checkedAccept, checkedProvider, datesErrors, decodeKey, defaultRecentLimit, displayFields, emptyAssistantState, encodeKey, fieldRefsIn, fillAddress, hrefField, hrefPlaceholders, inRange, isFieldRef, isGroupChart, isLocale, layoutItems, leavesApp, linkActionError, locales, markFieldRefs, matchedSearchField, matchesSearch, navRecentErrors, parseCapability, parseFormula, parseSize, parseTable, pickLocale, pluginProblem, promptError, promptFields, pruneByShape, readEventStream, redactSecrets, renderPrompt, searchTerms, shapes, shownInputs, statusTones, tableDisplay, termMatches, unmarkFieldRefs, updateSetErrors, updateValues, viewOfList, webhookResources };
package/dist/model.d.ts CHANGED
@@ -35,6 +35,8 @@ export type FieldModel = {
35
35
  };
36
36
  /** Never sent with the record; revealed one value at a time (`.sensitive()`). */
37
37
  sensitive?: true;
38
+ /** Stored encrypted, and sensitive too (`.encrypted()`). */
39
+ encrypted?: true;
38
40
  /** A value the form prefills, or `db` when the database supplies it (identity, now(), ...). */
39
41
  default?: {
40
42
  /** The value a create uses when none is given (`.default()`). */
@@ -23,4 +23,11 @@ export type ResourceState = {
23
23
  comments?: boolean;
24
24
  live?: boolean;
25
25
  };
26
+ export declare const parseTable: (table: string) => {
27
+ name: string;
28
+ schema?: undefined;
29
+ } | {
30
+ schema: string;
31
+ name: string;
32
+ };
26
33
  //#endregion
@@ -28,6 +28,7 @@ const fieldModel = (name, field) => {
28
28
  ...meta.hasDefault && { default: { value: meta.defaultValue } },
29
29
  ...meta.dbDefault && !meta.hasDefault && { default: { db: true } },
30
30
  ...meta.sensitive && { sensitive: true },
31
+ ...meta.encrypted && { encrypted: true },
31
32
  ...meta.enumValues && { enumValues: [...meta.enumValues] },
32
33
  ...meta.relation && { relation: {
33
34
  resource: meta.relation.resource,
@@ -86,6 +87,7 @@ const sensitiveProblems = (model) => Object.values(model.fields).filter((field)
86
87
  model.softDelete
87
88
  ].includes(field.name) ? ["a key, tenant, owner or soft delete field"] : []
88
89
  ].map((problem) => `sensitive field "${field.name}" cannot be ${problem}`));
90
+ const encryptedProblems = (model) => Object.values(model.fields).filter((field) => field.encrypted).flatMap((field) => [...field.readOnly ? ["readOnly()"] : [], ...field.default && "db" in field.default ? ["given a database default"] : []].map((problem) => `encrypted field "${field.name}" cannot be ${problem}`));
89
91
  const fileProblems = (model) => Object.values(model.fields).filter((field) => field.file).flatMap((field) => [
90
92
  ...field.filterable ? ["cannot be filterable"] : [],
91
93
  ...field.sortable ? ["cannot be sortable"] : [],
@@ -158,6 +160,7 @@ const buildResourceModel = (state) => {
158
160
  const events = state.events && buildEventModels(model, state.events);
159
161
  const problems = [
160
162
  ...sensitiveProblems(model),
163
+ ...encryptedProblems(model),
161
164
  ...fileProblems(model),
162
165
  ...positionProblems(model),
163
166
  ...parentProblems(model),
@@ -173,4 +176,4 @@ const buildResourceModel = (state) => {
173
176
  };
174
177
  const ignoredColumns = (fields = {}) => Object.entries(fields).filter(([, field]) => field === null).map(([key]) => snakeCase(key));
175
178
  //#endregion
176
- export { buildResourceModel, ignoredColumns };
179
+ export { buildResourceModel, ignoredColumns, parseTable };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@protobase/schema",
3
- "version": "0.33.4",
3
+ "version": "0.34.0",
4
4
  "description": "Resource, field, view and access definitions for Protobase configs.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {