@protobase/schema 0.46.8 → 0.46.9

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.
@@ -0,0 +1,28 @@
1
+ import { AccessUser, FieldRule } from "../access/types.js";
2
+ //#region src/computed/computed-reveal.d.ts
3
+ /** Who asks for a computed reveal: the user, and with organizations the one the request works in. */
4
+ export type RevealCaller = {
5
+ /** The user, with the roles they act with. */
6
+ user: AccessUser;
7
+ /** With organizations, the one the request works in. */
8
+ organization?: {
9
+ /** The organization's id. */
10
+ id: string | number;
11
+ };
12
+ };
13
+ /**
14
+ * A value revealed like a sensitive field, but made when it is asked for instead of read from a column, such as a
15
+ * secret another service keeps (`.reveals()`).
16
+ */
17
+ export type ComputedReveal<R> = {
18
+ /** The value, from the record as the caller may read it, without its sensitive fields; `null` reveals nothing. */
19
+ value: (record: R, caller: RevealCaller) => unknown;
20
+ /** Who may reveal it, among those who may read the record: a role rule, as a field's read rule is. Default: all of them. */
21
+ read?: FieldRule;
22
+ /** Revealing it needs a strong sign-in this recent, as the field option `.strongSignIn()` does; `minutes` is 15 by default. */
23
+ strongSignIn?: {
24
+ /** The most minutes since the sign-in, or its confirmation, proved a passkey or a second step. */
25
+ minutes?: number;
26
+ };
27
+ };
28
+ //#endregion
package/dist/field.d.ts CHANGED
@@ -35,6 +35,11 @@ export type FieldMeta = {
35
35
  sensitive?: boolean;
36
36
  /** Whether it is stored encrypted, and so sensitive too (`.encrypted()`). */
37
37
  encrypted?: boolean;
38
+ /** How recent a strong sign-in its reveal needs, and so sensitive too (`.strongSignIn()`). */
39
+ strongSignIn?: {
40
+ /** The most minutes since the sign-in, or its confirmation, proved a passkey or a second step. */
41
+ minutes: number;
42
+ };
38
43
  /** An enum's values (`f.enum()`). */
39
44
  enumValues?: string[];
40
45
  /** A relation's resource and the columns that hold its key (`f.relation()`, `.columns()`). */
@@ -121,6 +126,15 @@ export declare class Field<V> {
121
126
  * [Encrypted fields](website/src/content/docs/reference/data-config.mdx#encrypted-fields).
122
127
  */
123
128
  encrypted(): Field<V | Encrypted>;
129
+ /**
130
+ * Revealing it needs a strong sign-in: one that proved a passkey or a second step, made or confirmed in the last
131
+ * `minutes` (15 by default). Otherwise the reveal is refused with `403 strong-sign-in-required`, and the record page
132
+ * asks the person to confirm it's them first. It is sensitive too. See
133
+ * [Recent strong sign-in](website/src/content/docs/reference/data-config.mdx#recent-strong-sign-in).
134
+ */
135
+ strongSignIn(o?: {
136
+ minutes?: number;
137
+ }): Field<V>;
124
138
  /** The database supplies a value when none is given (identity, now(), ...). */
125
139
  dbDefault(): Field<V>;
126
140
  /** Other names filters may use for the field. */
package/dist/field.js CHANGED
@@ -69,6 +69,20 @@ var Field = class Field {
69
69
  sensitive: true
70
70
  });
71
71
  }
72
+ /**
73
+ * Revealing it needs a strong sign-in: one that proved a passkey or a second step, made or confirmed in the last
74
+ * `minutes` (15 by default). Otherwise the reveal is refused with `403 strong-sign-in-required`, and the record page
75
+ * asks the person to confirm it's them first. It is sensitive too. See
76
+ * [Recent strong sign-in](website/src/content/docs/reference/data-config.mdx#recent-strong-sign-in).
77
+ */
78
+ strongSignIn(o = {}) {
79
+ const minutes = o.minutes ?? 15;
80
+ if (!Number.isInteger(minutes) || minutes < 1) throw new Error(".strongSignIn(): minutes must be a whole number of at least 1");
81
+ return this.with({
82
+ strongSignIn: { minutes },
83
+ sensitive: true
84
+ });
85
+ }
72
86
  /** The database supplies a value when none is given (identity, now(), ...). */
73
87
  dbDefault() {
74
88
  return this.with({ dbDefault: true });
package/dist/index.d.ts CHANGED
@@ -7,6 +7,7 @@ import { AnyField, Constraint, Field, FieldMeta, FieldTypeDef } from "./field.js
7
7
  import { RelationField } from "./relation-field.js";
8
8
  import { FileField } from "./file-field.js";
9
9
  import { f } from "./fields.js";
10
+ import { ComputedReveal, RevealCaller } from "./computed/computed-reveal.js";
10
11
  import { FieldRefs, FieldsInput, Ref, Refs } from "./refs.js";
11
12
  import { CreatedOptions, DeletedOptions, EventActor, EventContext, EventHelpers, EventMatch, EventSpec, UndeletedOptions, UpdatedOptions } from "./events/event-helpers.js";
12
13
  import { Access, InferRecord, Issue, KeyOf, KeyRefs, RecordKey, RecordOf } from "./resource-types.js";
@@ -45,4 +46,4 @@ import { AccessSource, ResolveOptions, ResolvedAccess, RowFilterOperation, acces
45
46
  import { restrictModel } from "./access/restrict.js";
46
47
  import "./access/index.js";
47
48
  import { AssistantAction, AssistantActionRequest, AssistantCardPart, AssistantDiff, AssistantEvent, AssistantMessage, AssistantMessageRequest, AssistantPart, AssistantState, AssistantStep, AssistantStepState, AssistantTablePart, AssistantTextPart, AssistantTone, AssistantWidgetPart } from "./assistant/protocol.js";
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 TenantOptions, 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 };
49
+ 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 ComputedReveal, 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 RevealCaller, 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 TenantOptions, 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/model.d.ts CHANGED
@@ -37,6 +37,11 @@ export type FieldModel = {
37
37
  sensitive?: true;
38
38
  /** Stored encrypted, and sensitive too (`.encrypted()`). */
39
39
  encrypted?: true;
40
+ /** Its reveal needs a strong sign-in this recent, and it is sensitive too (`.strongSignIn()`). */
41
+ strongSignIn?: {
42
+ /** The most minutes since the sign-in, or its confirmation, proved a passkey or a second step. */
43
+ minutes: number;
44
+ };
40
45
  /**
41
46
  * In `/meta` only: the caller may write the field but not read it, as its own `.access()` write rule allows. It comes
42
47
  * sensitive, with no value to reveal, and the form edits it only where the view's layout names it.
@@ -29,6 +29,7 @@ const fieldModel = (name, field) => {
29
29
  ...meta.dbDefault && !meta.hasDefault && { default: { db: true } },
30
30
  ...meta.sensitive && { sensitive: true },
31
31
  ...meta.encrypted && { encrypted: true },
32
+ ...meta.strongSignIn && { strongSignIn: { minutes: meta.strongSignIn.minutes } },
32
33
  ...meta.enumValues && { enumValues: [...meta.enumValues] },
33
34
  ...meta.relation && { relation: {
34
35
  resource: meta.relation.resource,
@@ -1,5 +1,6 @@
1
1
  import { ParentAccess, PositionAfterModel, ResourceModel, SearchMatch } from "./model.js";
2
2
  import { FieldAccess } from "./access/types.js";
3
+ import { ComputedReveal } from "./computed/computed-reveal.js";
3
4
  import { FieldRefs, FieldsInput, Ref } from "./refs.js";
4
5
  import { EventHelpers, EventMatch, EventSpec } from "./events/event-helpers.js";
5
6
  import { ResourceState } from "./resource-model.js";
@@ -38,6 +39,8 @@ export declare class ResourceBuilder<F extends FieldsInput = {}, K = never> {
38
39
  readonly validators: readonly Validator<F>[];
39
40
  /** @internal */
40
41
  readonly accessRules: Access<F>;
42
+ /** @internal */
43
+ readonly computedReveals: Readonly<Record<string, ComputedReveal<RecordOf<F>>>>;
41
44
  /** The fields, for the compiler only, which {@link InferRecord} reads; never set. */
42
45
  readonly $fields: F;
43
46
  /** The fields of the key, for the compiler only, which {@link RecordKey} reads; never set. */
@@ -49,7 +52,9 @@ export declare class ResourceBuilder<F extends FieldsInput = {}, K = never> {
49
52
  /** @internal */
50
53
  validators?: readonly Validator<F>[],
51
54
  /** @internal */
52
- accessRules?: Access<F>);
55
+ accessRules?: Access<F>,
56
+ /** @internal */
57
+ computedReveals?: Readonly<Record<string, ComputedReveal<RecordOf<F>>>>);
53
58
  /** The table, with or without its schema: `'shop.repairs'` or `'repairs'`. Required. */
54
59
  table(table: string): ResourceBuilder<F, K>;
55
60
  /**
@@ -168,6 +173,19 @@ export declare class ResourceBuilder<F extends FieldsInput = {}, K = never> {
168
173
  * ```
169
174
  */
170
175
  access(rules: Access<F>): ResourceBuilder<F, K>;
176
+ /**
177
+ * Values revealed like sensitive fields, by name, but made when one is asked for instead of read from a column: a
178
+ * secret another service keeps, or a file of settings built from several. `POST /{resource}/{key}:reveal` with
179
+ * `{ "field": "<name>" }` answers one for a caller who may read the record, after the same audit event as a field's
180
+ * reveal. Each call adds names, which no field may have. See
181
+ * [Computed reveals](website/src/content/docs/reference/data-config.mdx#computed-reveals).
182
+ *
183
+ * @example
184
+ * ```ts
185
+ * .reveals({ env: { value: (app) => secrets.envFile(app.id), read: roles.is('owner'), strongSignIn: { minutes: 15 } } })
186
+ * ```
187
+ */
188
+ reveals(reveals: Record<string, ComputedReveal<RecordOf<F>>>): ResourceBuilder<F, K>;
171
189
  /** Role-based rules per field, for fields that declared `.access(...)`. */
172
190
  fieldAccess(): Record<string, FieldAccess>;
173
191
  /** The resource as the server and the app read it. Throws when the table, fields or key are missing, or a field option conflicts with another. */
package/dist/resource.js CHANGED
@@ -12,11 +12,13 @@ var ResourceBuilder = class ResourceBuilder {
12
12
  state;
13
13
  validators;
14
14
  accessRules;
15
+ computedReveals;
15
16
  /** @internal */
16
- constructor(state, validators = [], accessRules = {}) {
17
+ constructor(state, validators = [], accessRules = {}, computedReveals = {}) {
17
18
  this.state = state;
18
19
  this.validators = validators;
19
20
  this.accessRules = accessRules;
21
+ this.computedReveals = computedReveals;
20
22
  }
21
23
  /** The table, with or without its schema: `'shop.repairs'` or `'repairs'`. Required. */
22
24
  table(table) {
@@ -39,7 +41,7 @@ var ResourceBuilder = class ResourceBuilder {
39
41
  return new ResourceBuilder({
40
42
  ...this.state,
41
43
  primaryKey: refs.map((ref) => ref.name)
42
- }, this.validators, this.accessRules);
44
+ }, this.validators, this.accessRules, this.computedReveals);
43
45
  }
44
46
  /**
45
47
  * A nullable timestamp or date that marks a record deleted: deleting sets it, lists leave such rows out and
@@ -176,7 +178,7 @@ var ResourceBuilder = class ResourceBuilder {
176
178
  * ```
177
179
  */
178
180
  validate(validator) {
179
- return new ResourceBuilder(this.state, [...this.validators, validator], this.accessRules);
181
+ return new ResourceBuilder(this.state, [...this.validators, validator], this.accessRules, this.computedReveals);
180
182
  }
181
183
  /**
182
184
  * Rules per operation (`list`, `read`, `create`, `update`, `delete`). A rule returns `true`, `false`, or a filter that
@@ -196,6 +198,29 @@ var ResourceBuilder = class ResourceBuilder {
196
198
  return new ResourceBuilder(this.state, this.validators, {
197
199
  ...this.accessRules,
198
200
  ...rules
201
+ }, this.computedReveals);
202
+ }
203
+ /**
204
+ * Values revealed like sensitive fields, by name, but made when one is asked for instead of read from a column: a
205
+ * secret another service keeps, or a file of settings built from several. `POST /{resource}/{key}:reveal` with
206
+ * `{ "field": "<name>" }` answers one for a caller who may read the record, after the same audit event as a field's
207
+ * reveal. Each call adds names, which no field may have. See
208
+ * [Computed reveals](website/src/content/docs/reference/data-config.mdx#computed-reveals).
209
+ *
210
+ * @example
211
+ * ```ts
212
+ * .reveals({ env: { value: (app) => secrets.envFile(app.id), read: roles.is('owner'), strongSignIn: { minutes: 15 } } })
213
+ * ```
214
+ */
215
+ reveals(reveals) {
216
+ for (const [name, reveal] of Object.entries(reveals)) {
217
+ if (Object.hasOwn(this.state.fields ?? {}, name)) throw new Error(`Resource "${this.state.name}": the reveal "${name}" has the name of a field`);
218
+ const minutes = reveal.strongSignIn?.minutes;
219
+ if (minutes !== void 0 && (!Number.isInteger(minutes) || minutes < 1)) throw new Error(`Resource "${this.state.name}": the reveal "${name}" needs minutes of at least 1`);
220
+ }
221
+ return new ResourceBuilder(this.state, this.validators, this.accessRules, {
222
+ ...this.computedReveals,
223
+ ...reveals
199
224
  });
200
225
  }
201
226
  /** Role-based rules per field, for fields that declared `.access(...)`. */
@@ -225,7 +250,7 @@ var ResourceBuilder = class ResourceBuilder {
225
250
  return new ResourceBuilder({
226
251
  ...this.state,
227
252
  ...patch
228
- }, this.validators, this.accessRules);
253
+ }, this.validators, this.accessRules, this.computedReveals);
229
254
  }
230
255
  };
231
256
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@protobase/schema",
3
- "version": "0.46.8",
3
+ "version": "0.46.9",
4
4
  "description": "Resource, field, view and access definitions for Protobase configs.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {