@protobase/schema 0.22.8 → 0.22.10

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.
@@ -27,6 +27,8 @@ export declare const addressWords: {
27
27
  readonly edit: "edit";
28
28
  /** A record page's open collapsed group, by title, once per group. */
29
29
  readonly open: "open";
30
+ /** A record page's shown tab, by title. */
31
+ readonly tab: "tab";
30
32
  };
31
33
  /** The path of `address` below `basePath` (such as `/admin`), every name and key encoded the one way the app reads them. */
32
34
  export declare const appPath: (address: AppAddress, basePath?: string) => string;
@@ -26,7 +26,9 @@ const addressWords = {
26
26
  /** A record's form, `edit=true`, on a view whose record page opens as a read view or locked. */
27
27
  edit: "edit",
28
28
  /** A record page's open collapsed group, by title, once per group. */
29
- open: "open"
29
+ open: "open",
30
+ /** A record page's shown tab, by title. */
31
+ tab: "tab"
30
32
  };
31
33
  const listWords = [
32
34
  "list",
@@ -81,6 +83,7 @@ const recordQuery = (address) => {
81
83
  const query = new URLSearchParams();
82
84
  if (address.edit) query.set(addressWords.edit, "true");
83
85
  for (const title of address.open ?? []) query.append(addressWords.open, title);
86
+ if (address.tab) query.set(addressWords.tab, address.tab);
84
87
  const text = query.toString();
85
88
  return text ? `?${text}` : "";
86
89
  };
@@ -130,12 +133,14 @@ const readAppPath = (pathAndQuery, pages) => {
130
133
  }
131
134
  if (second !== void 0) {
132
135
  const open = [...new Set(url.searchParams.getAll(addressWords.open).filter((title) => title !== ""))];
136
+ const tab = url.searchParams.get(addressWords.tab) || void 0;
133
137
  return {
134
138
  kind: "record",
135
139
  resource: first,
136
140
  key: second,
137
141
  ...url.searchParams.get(addressWords.edit) === "true" && { edit: true },
138
- ...open.length > 0 && { open }
142
+ ...open.length > 0 && { open },
143
+ ...tab && { tab }
139
144
  };
140
145
  }
141
146
  const params = url.searchParams;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { FileOptions, FileProcessor, FileProcessorInput, ImageFormat, ImageVariant, ImageVariantOptions, imageFormats } from "./files/file-options.js";
2
- 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, ListModel, ListQuery, Literal, MapSectionModel, MapView, NamedListModel, NavModel, NavRecentModel, NowValue, OrderByItem, PeriodChartModel, RecordMode, RecordWhen, RelatedColumnModel, RelatedModel, ResourceModel, SaveFeedback, SaveSearchModel, SearchMatch, SearchResultModel, SearchesOfModel, SectionModel, SectionPresentation, ShowWhen, SignInPromptModel, SignInViewModel, SortSpec, Span, StatusTone, UserMenuItemModel, UserMenuModel, ViewModel } from "./model.js";
2
+ 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, ListModel, ListQuery, Literal, MapSectionModel, MapView, NamedListModel, NavModel, NavRecentModel, NowValue, OrderByItem, PeriodChartModel, RecordMode, RecordWhen, RelatedColumnModel, RelatedModel, ResourceModel, SaveFeedback, SaveSearchModel, SearchMatch, SearchResultModel, SearchesOfModel, SectionModel, SectionPresentation, ShowWhen, SignInPromptModel, SignInViewModel, SortSpec, Span, StatusTone, TabItemModel, TabModel, UserMenuItemModel, UserMenuModel, ViewModel } from "./model.js";
3
3
  import { AccessAction, AccessContext, AccessFn, AccessResult, AccessUser, FieldAccess, FieldRule } from "./access/types.js";
4
4
  import { AnyField, Constraint, Field, FieldMeta, FieldTypeDef } from "./field.js";
5
5
  import { RelationField } from "./relation-field.js";
@@ -42,4 +42,4 @@ import { AccessSource, ResolveOptions, ResolvedAccess, RowFilterOperation, acces
42
42
  import { restrictModel } from "./access/restrict.js";
43
43
  import "./access/index.js";
44
44
  import { AssistantAction, AssistantActionRequest, AssistantCardPart, AssistantDiff, AssistantEvent, AssistantMessage, AssistantMessageRequest, AssistantPart, AssistantState, AssistantStep, AssistantStepState, AssistantTablePart, AssistantTextPart, AssistantTone, AssistantWidgetPart } from "./assistant/protocol.js";
45
- 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 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 ListModel, type ListQuery, type Literal, type MapSectionModel, type MapView, type NamedListModel, type NavModel, type NavRecentModel, type NowValue, type OrderByCheck, type OrderByItem, type OrderByParse, type PeriodChartInput, type PeriodChartModel, type RecordKey, type RecordMode, type RecordOf, type RecordWhen, type Ref, type Refs, type RelatedColumnModel, type RelatedModel, type RelationField, type ResolveOptions, type ResolvedAccess, type ResourceBuilder, type ResourceModel, type RoleEntry, type RoleLimit, type Roles, type RolesOptions, 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 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 };
45
+ 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 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 ListModel, type ListQuery, type Literal, type MapSectionModel, type MapView, type NamedListModel, type NavModel, type NavRecentModel, type NowValue, type OrderByCheck, type OrderByItem, type OrderByParse, type PeriodChartInput, type PeriodChartModel, type RecordKey, type RecordMode, type RecordOf, type RecordWhen, type Ref, type Refs, type RelatedColumnModel, type RelatedModel, type RelationField, type ResolveOptions, type ResolvedAccess, type ResourceBuilder, type ResourceModel, type RoleEntry, type RoleLimit, type Roles, type RolesOptions, 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 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 };
package/dist/internal.js CHANGED
@@ -3,6 +3,7 @@ import { promptError, promptFields, renderPrompt } from "./ai-prompt.js";
3
3
  import { checkedAccept, checkedProvider, parseSize } from "./files/file-options.js";
4
4
  import { matchedSearchField, matchesSearch, searchTerms, termMatches } from "./search-match.js";
5
5
  import { checkEventFilter } from "./events/event-models.js";
6
+ import { layoutItems } from "./layout-items.js";
6
7
  import { viewOfList } from "./named-list.js";
7
8
  import { defaultRecentLimit, navRecentErrors, statusTones } from "./nav-recent.js";
8
9
  import { actionInputErrors, shownInputs } from "./action-inputs.js";
@@ -19,5 +20,4 @@ import { redactSecrets } from "./redact-secrets.js";
19
20
  import { addressErrors } from "./address-errors.js";
20
21
  import { applyAssistantEvent, emptyAssistantState } from "./assistant/apply-event.js";
21
22
  import { readEventStream } from "./assistant/event-stream.js";
22
- import { layoutItems } from "./layout-items.js";
23
23
  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, viewOfList, webhookResources };
@@ -1,5 +1,5 @@
1
1
  import { LayoutItemModel } from "./model.js";
2
2
  //#region src/layout-items.d.ts
3
- /** The layout's items with every group's items in its place, for code that looks for sections and related tables wherever they sit. */
3
+ /** The layout's items with every tab's and group's items in its place, for code that looks for items wherever they sit. */
4
4
  export declare const layoutItems: (layout: readonly LayoutItemModel[]) => LayoutItemModel[];
5
5
  //#endregion
@@ -1,5 +1,5 @@
1
1
  //#region src/layout-items.ts
2
- /** The layout's items with every group's items in its place, for code that looks for sections and related tables wherever they sit. */
3
- const layoutItems = (layout) => layout.flatMap((item) => item.kind === "group" ? item.items : [item]);
2
+ /** The layout's items with every tab's and group's items in its place, for code that looks for items wherever they sit. */
3
+ const layoutItems = (layout) => layout.flatMap((item) => item.kind === "group" || item.kind === "tab" ? layoutItems(item.items) : [item]);
4
4
  //#endregion
5
5
  export { layoutItems };
package/dist/layout.d.ts CHANGED
@@ -59,6 +59,13 @@ export declare const layout: {
59
59
  collapsed?: boolean;
60
60
  when?: RecordWhen;
61
61
  }) => LayoutItemModel;
62
+ /**
63
+ * A tab of the record page holding main-column items: sections, groups, related tables, custom components, maps,
64
+ * galleries, history and comments. A view's tabs stand together and show as one strip, the first tab open unless the
65
+ * address names another; items before and after them show on every tab. Throws on a sidebar item or another tab. See
66
+ * [Tabs](website/src/content/docs/reference/ui-config.mdx#tabs).
67
+ */
68
+ tab: (title: string, items: LayoutItemModel[]) => LayoutItemModel;
62
69
  /** A table of the records of another resource that point at this one, directly or through a join table. */
63
70
  related: (title: string, o: Omit<RelatedModel, "kind" | "title">) => LayoutItemModel;
64
71
  /**
package/dist/layout.js CHANGED
@@ -75,6 +75,21 @@ const layout = {
75
75
  ...o.when !== void 0 && { when: o.when }
76
76
  };
77
77
  },
78
+ /**
79
+ * A tab of the record page holding main-column items: sections, groups, related tables, custom components, maps,
80
+ * galleries, history and comments. A view's tabs stand together and show as one strip, the first tab open unless the
81
+ * address names another; items before and after them show on every tab. Throws on a sidebar item or another tab. See
82
+ * [Tabs](website/src/content/docs/reference/ui-config.mdx#tabs).
83
+ */
84
+ tab: (title, items) => {
85
+ const wrong = items.find((item) => item.kind === "sidebar" || item.kind === "tab" || (item.kind === "custom" || item.kind === "map") && item.sidebar);
86
+ if (wrong) throw new Error(`l.tab "${title}" holds main-column items, not ${wrong.kind === "sidebar" || wrong.kind === "tab" ? wrong.kind : `a sidebar ${wrong.kind}`}`);
87
+ return {
88
+ kind: "tab",
89
+ title,
90
+ items
91
+ };
92
+ },
78
93
  /** A table of the records of another resource that point at this one, directly or through a join table. */
79
94
  related: (title, o) => ({
80
95
  kind: "related",
package/dist/model.d.ts CHANGED
@@ -222,6 +222,8 @@ export type AppAddress = {
222
222
  edit?: boolean;
223
223
  /** The titles of the record page's collapsed groups that are open. */
224
224
  open?: readonly string[];
225
+ /** The title of the record page's tab shown; the first tab when omitted. */
226
+ tab?: string;
225
227
  } | {
226
228
  /** The form for a new record. */
227
229
  kind: 'create';
@@ -754,7 +756,7 @@ export type LayoutItemModel = SectionModel | {
754
756
  fields: string[];
755
757
  /** Whether it shows on the form, the read view or both, or a condition on the saved record that it shows while it holds. */
756
758
  when?: RecordWhen;
757
- } | RelatedModel | GroupModel | CustomSectionModel | MapSectionModel | GalleryModel | HistorySectionModel | CommentsSectionModel;
759
+ } | RelatedModel | GroupModel | CustomSectionModel | MapSectionModel | GalleryModel | HistorySectionModel | CommentsSectionModel | TabModel;
758
760
  /** A titled group of sections and related tables on the record page, closed until opened with `collapsed`; see [Groups](website/src/content/docs/reference/ui-config.mdx#groups). */
759
761
  export type GroupModel = {
760
762
  /** A group of sections and related tables. */
@@ -770,6 +772,17 @@ export type GroupModel = {
770
772
  };
771
773
  /** An item a group may hold. */
772
774
  export type GroupItemModel = SectionModel | RelatedModel;
775
+ /** A tab of the record page, holding main-column items; a view's tabs stand together as one strip. See [Tabs](website/src/content/docs/reference/ui-config.mdx#tabs). */
776
+ export type TabModel = {
777
+ /** A tab of the record page. */
778
+ kind: 'tab';
779
+ /** Its label, unique among the view's tabs: the address names the open tab by it. */
780
+ title: string;
781
+ /** The main-column items it shows, in order. */
782
+ items: TabItemModel[];
783
+ };
784
+ /** An item a tab may hold: any of the main column. */
785
+ export type TabItemModel = SectionModel | RelatedModel | GroupModel | CustomSectionModel | MapSectionModel | GalleryModel | HistorySectionModel | CommentsSectionModel;
773
786
  /**
774
787
  * A record's photos as tiles in the main column under `title`, one large and the next few smaller, opening a lightbox
775
788
  * that steps through them all: first the record's own file field `image`, then the `image` of the records of
package/dist/view.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { createRefs, ref } from "./refs.js";
2
2
  import { formNamePattern } from "./form-address.js";
3
+ import { layoutItems } from "./layout-items.js";
3
4
  import { isMenuHref } from "./menu-href.js";
4
5
  import { viewOfList } from "./named-list.js";
5
6
  import { navRecentErrors } from "./nav-recent.js";
@@ -301,12 +302,23 @@ var ViewBuilder = class ViewBuilder {
301
302
  const clash = resource ? list?.relatedColumns?.find((column) => Object.hasOwn(resource.fields, column.name)) : void 0;
302
303
  if (clash) throw new Error(`View "${this.state.resource}": related column "${clash.name}" has the name of a field of the resource`);
303
304
  const groupTitles = /* @__PURE__ */ new Set();
304
- for (const item of this.state.layout) {
305
+ const tabs = this.state.layout.flatMap((item, index) => item.kind === "tab" ? [{
306
+ item,
307
+ index
308
+ }] : []);
309
+ const apart = tabs.find((tab, at) => at > 0 && tab.index !== tabs[at - 1].index + 1);
310
+ if (apart) throw new Error(`View "${this.state.resource}": tabs stand together, and "${apart.item.title}" is apart from the others`);
311
+ const tabTitles = /* @__PURE__ */ new Set();
312
+ for (const { item } of tabs) {
313
+ if (tabTitles.has(item.title)) throw new Error(`View "${this.state.resource}": two tabs are titled "${item.title}"`);
314
+ tabTitles.add(item.title);
315
+ }
316
+ for (const item of this.state.layout.flatMap((item) => item.kind === "tab" ? item.items : [item])) {
305
317
  if (item.kind !== "group") continue;
306
318
  if (groupTitles.has(item.title)) throw new Error(`View "${this.state.resource}": two groups are titled "${item.title}"`);
307
319
  groupTitles.add(item.title);
308
320
  }
309
- for (const item of resource ? this.state.layout : []) {
321
+ for (const item of resource ? layoutItems(this.state.layout) : []) {
310
322
  if (item.kind !== "gallery" || item.image === void 0) continue;
311
323
  if (!Object.hasOwn(resource.fields, item.image)) throw new Error(`View "${this.state.resource}": gallery "${item.title}" names field "${item.image}", which the resource does not have`);
312
324
  if (resource.fields[item.image].type !== "file") throw new Error(`View "${this.state.resource}": gallery "${item.title}" shows "${item.image}" as its image, which is not a file field`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@protobase/schema",
3
- "version": "0.22.8",
3
+ "version": "0.22.10",
4
4
  "description": "Resource, field, view and access definitions for Protobase configs.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {