@protobase/server 0.12.18 → 0.12.19

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.
@@ -4,6 +4,7 @@ import { PluginOption } from "./plugins/plugin-settings.js";
4
4
  import { SourceOption } from "./sources/source-settings.js";
5
5
  import { CorsOrigins } from "./api-cors.js";
6
6
  import { AssistantOptions } from "./assistant/assistant-settings.js";
7
+ import { MapOptions } from "./maps/map-settings.js";
7
8
  import { RuntimeOptions } from "./runtime/runtime-settings.js";
8
9
  import { ResolveOptions, RoleLimit } from "@protobase/schema";
9
10
  //#region src/admin-options.d.ts
@@ -53,6 +54,11 @@ export type AdminOptions = {
53
54
  cors?: CorsOrigins;
54
55
  /** The runtime updates endpoint behind the top bar's update button (see `RuntimeOptions`); `false` turns it off. */
55
56
  runtime?: RuntimeOptions | false;
57
+ /**
58
+ * The map style the record page's maps (`l.map`) are drawn with (see `MapOptions`). Defaults to the one the platform
59
+ * passes in `PROTOBASE_MAP_STYLE_URL`; without either, or with `false`, the app draws no maps.
60
+ */
61
+ maps?: MapOptions | false;
56
62
  /**
57
63
  * The plugins the admin app loads (see `PluginOption`), before the ones the platform lists in `PROTOBASE_PLUGINS`;
58
64
  * `false` turns both off.
@@ -1,5 +1,6 @@
1
1
  import { relatedErrors } from "./view-related.js";
2
2
  import { cardPhotosErrors } from "./view-card-photos.js";
3
+ import { mapErrors } from "./view-map.js";
3
4
  import { addressErrors, addressPlaceholders, appHrefError, displayFields, fieldRefsIn, hrefPlaceholders } from "@protobase/schema";
4
5
  //#region src/check-views.ts
5
6
  const linkFields = (run) => run?.kind !== "link" ? [] : "to" in run ? addressPlaceholders(run.to) : hrefPlaceholders(run.href);
@@ -17,6 +18,7 @@ const sensitiveErrors = (view, model) => {
17
18
  ["a filter", view.filters.flatMap((widget) => widget.kind === "flags" ? widget.fields : [widget.field])],
18
19
  ["the chart", view.chart ? [view.chart.field] : []],
19
20
  ["the sidebar", view.layout.flatMap((item) => item.kind === "sidebar" ? item.fields : [])],
21
+ ["a map", view.layout.flatMap((item) => item.kind === "map" ? [item.latitude, item.longitude] : [])],
20
22
  ["the recent records", view.nav?.recent ? [view.nav.recent.status] : []],
21
23
  ["an action", view.actions.flatMap((action) => action.run?.kind === "update" ? Object.keys(action.run.values) : linkFields(action.run))]
22
24
  ].flatMap(([place, names]) => [...new Set(names.filter(sensitive))].map((name) => `sensitive field "${name}" cannot be in ${place}`));
@@ -59,7 +61,8 @@ const widgetErrors = (view, model, fieldTypes) => Object.entries(view.fields).fl
59
61
  * Fails at startup when a view uses a sensitive field where it would show empty, a display or custom section names a
60
62
  * field the resource does not have, labels or colours values its enum does not have, shows a field with a field type of
61
63
  * `fieldTypes` (each with the storage types it shows) that does not show it,
62
- * has a related section or a cards display's photos that do not fit the resources, or a link to an address the app does not have.
64
+ * has a related section or a cards display's photos that do not fit the resources, a map that cannot show the
65
+ * record's position, or a link to an address the app does not have.
63
66
  */
64
67
  const checkViews = (registry, views, pages = [], fieldTypes = {}) => {
65
68
  const models = Object.fromEntries(registry.entries.map((entry) => [entry.name, entry.model]));
@@ -69,6 +72,7 @@ const checkViews = (registry, views, pages = [], fieldTypes = {}) => {
69
72
  if (!model) return [];
70
73
  const related = view.layout.flatMap((item) => item.kind === "related" ? relatedErrors(item, view.resource, models).map((error) => `related "${item.title}": ${error}`) : []);
71
74
  const photos = (view.list?.displays ?? []).flatMap((display) => display.kind === "cards" && display.photos ? cardPhotosErrors(display.photos, view.resource, models).map((error) => `display "${display.name}" photos: ${error}`) : []);
75
+ const maps = view.layout.flatMap((item) => item.kind === "map" ? mapErrors(item, model).map((error) => `map "${item.title}": ${error}`) : []);
72
76
  return [
73
77
  ...sensitiveErrors(view, model),
74
78
  ...unknownFieldErrors(view, model),
@@ -77,6 +81,7 @@ const checkViews = (registry, views, pages = [], fieldTypes = {}) => {
77
81
  ...widgetErrors(view, model, fieldTypes),
78
82
  ...related,
79
83
  ...photos,
84
+ ...maps,
80
85
  ...linkErrors(view, model, models, pageNames)
81
86
  ].map((problem) => `View "${view.resource}": ${problem}`);
82
87
  });
@@ -22,6 +22,7 @@ import { errorReportingSettings } from "./error-reporting/error-reporting-settin
22
22
  import { createFilesRuntime } from "./files/runtime.js";
23
23
  import { fileRoutes } from "./files/routes.js";
24
24
  import { functionRoutes } from "./functions/routes.js";
25
+ import { mapStyleUrl } from "./maps/map-settings.js";
25
26
  import { pluginOptions } from "./plugins/plugin-settings.js";
26
27
  import { runtimeUrl } from "./runtime/runtime-settings.js";
27
28
  import { checkSourceNames, sourceOptions } from "./sources/source-settings.js";
@@ -66,6 +67,7 @@ const createAdmin = ({ resources: appResources, views: appViews = [], pages = []
66
67
  const assistant = assistantSettings(options.assistant, globalThis.process?.env ?? {});
67
68
  const runtime = runtimeUrl(options.runtime, globalThis.process?.env ?? {});
68
69
  const errorReporting = errorReportingSettings(options.errorReporting, globalThis.process?.env ?? {});
70
+ const mapStyle = mapStyleUrl(options.maps, globalThis.process?.env ?? {});
69
71
  const plugins = pluginOptions(options.plugins, globalThis.process?.env ?? {}, bundlePlugins);
70
72
  const sources = sourceOptions(options.sources, globalThis.process?.env ?? {});
71
73
  checkSourceNames(sources, {
@@ -100,6 +102,7 @@ const createAdmin = ({ resources: appResources, views: appViews = [], pages = []
100
102
  ...assistant && { assistant: assistant.kind === "external" ? assistant.url : `${systemPath}/assistant` },
101
103
  ...runtime && { runtime },
102
104
  ...errorReporting && { errorReporting },
105
+ ...mapStyle && { mapStyle },
103
106
  plugins,
104
107
  sources,
105
108
  ...files && { files },
package/dist/index.d.ts CHANGED
@@ -31,6 +31,7 @@ import { Conversation, ConversationsOptions, conversationKey, createConversation
31
31
  import { ApprovalAnswer, ApprovalRequest, requestApproval } from "./assistant/approval.js";
32
32
  import { AssistantTool, ToolContext, defineTool } from "./assistant/tools.js";
33
33
  import { AssistantOptions, AssistantSettings, assistantRoles, assistantSettings, assistantVariables, seesAssistant } from "./assistant/assistant-settings.js";
34
+ import { MapOptions, mapStyleUrl, mapStyleVariable } from "./maps/map-settings.js";
34
35
  import { RuntimeOptions, runtimeUrl, runtimeVariable, seesRuntime } from "./runtime/runtime-settings.js";
35
36
  import { AdminOptions } from "./admin-options.js";
36
37
  import { CreateAdminInput, createAdmin } from "./create-admin.js";
@@ -77,4 +78,4 @@ import { FatalJobError } from "./jobs/run-job.js";
77
78
  import { DrainResult, JobsRuntime, StartJobsOptions, startJobs } from "./jobs/runtime.js";
78
79
  import { jobsDrainApp } from "./jobs/drain-route.js";
79
80
  import { checkJobsSchema, jobsSchema, jobsSchemaMigration } from "./jobs/schema.js";
80
- export { type Admin, type AdminAuth, type AdminOptions, type ApiFunction, type ApprovalAnswer, type ApprovalRequest, type AssistantBackend, type AssistantOptions, type AssistantSettings, type AssistantTool, type AuditEvent, type AuditQueue, type Authenticator, type BetterAuthAuthenticatorOptions, type CapturedRow, type ChatMessage, type ChatTextPart, type CleanupEntry, type CleanupReport, type CleanupSchedule, type CoalescedChange, type Completion, type CompletionOptions, type Conversation, type ConversationRecord, type ConversationStore, type ConversationsOptions, type CorsOptions, type CorsOrigins, type CreateAdminInput, type CreateAuthOptions, type DrainResult, type EnqueueOptions, type ErrorReporter, type ErrorReportingOptions, type ErrorReportingSettings, type EventData, type EventsOptions, FatalJobError, type FileCleanupInput, type FileLimits, type FileObject, type FileProvider, type FileStore, type FilesOptions, type FunctionApp, type FunctionCaller, type FunctionContext, type FunctionEnv, type FunctionHandler, type FunctionOptions, type FunctionRecords, type FunctionSource, HttpProblem, type IssueTokenOptions, type JobContext, type JobDefinition, type JobOptions, type JobsRuntime, type JwtOptions, type LinkedAccount, type ListParams, type LocalFilesOptions, type MailMessage, type Mailer, type MemberInput, MissingCredentials, ModelError, type ModelSettings, type NewOrganization, type NewUser, type OperatorProvider, type OrganizationRole, type OrganizationsOptions, type PipelineHook, type PlatformSignIn, type PluginOption, type PluginRef, type ProjectConfig, type ProtectedFunctionOptions, type PublicFunctionContext, type PublicFunctionEnv, type PublicFunctionHandler, type PublicFunctionOptions, type QueryOptions, type QueryResult, type ReasoningDetail, type ReasoningEffort, type RecordKeyInput, type RecordPage, type ResourceChange, type ResourceSource, type RoleDefinition, type RolesInput, type RunContext, type RuntimeOptions, type S3Bucket, type S3FilesOptions, type S3ListPage, type S3Object, type ScanGuardMode, type ScheduledDelete, type Session, type SessionOrganization, type SignInPolicy, type SignInRule, type SignatureOptions, type SourceOption, type SourceRef, type StaffAccessRule, type StaffSignIn, type StandardSchema, type StandardWebhookOptions, type StartJobsOptions, type StoredMember, type StoredObject, type StoredOrganization, type StoredUser, type SubscriptionDefinition, type SubscriptionEvent, type SubscriptionOptions, type TenantValue, type ToolCall, type ToolContext, type ToolRecord, type ToolRecords, type ToolSpec, type TrustedApp, type TurnOptions, type User, type UserCreatedHook, type ViewSource, type WebhooksOptions, type WidgetOptions, type WriteEvent, type WriteOperation, addAppRoleToOwners, addMember, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaMigration, badRequest, betterAuthAuthenticator, changesMigration, checkAuthSchema, checkChanges, checkJobsSchema, checkSourceNames, checkWebhooksSchema, coalesce, configExports, consoleAuditQueue, conversationKey, createAdmin, createAuth, createConversations, createFileCleanup, createOrganization, createUser, defaultSignInPolicy, defineConfig, defineFunction, defineJob, defineTool, deleteUser, errorReportingSettings, errorReportingVariable, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, functionApp, hasUsers, imageSize, isJobDefinition, issueToken, jobDefinitions, jobsDrainApp, jobsSchema, jobsSchemaMigration, jwtAuthenticator, limitedFileStore, listOrganizations, listUsers, localFileStore, localFiles, maxTokenTtlSeconds, maxWidgetPropsBytes, mergeConfig, migrationCommand, newWebhookSecret, normalizeRoles, notFound, parseColumns, parseRoles, pluginOptions, pluginsFor, pluginsVariable, processor, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readWriteQueryTool, recordOf, removeMember, requestApproval, requireEnv, resourceChange, roleChoices, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, s3FileStore, s3Files, seesAssistant, seesRuntime, setMemberRoles, setUserBanned, setUserRole, signInPolicyMethods, sourceOptions, sourcesFor, sourcesVariable, startErrorReporting, startJobs, storageCleanupSchedule, streamChatCompletion, subscription, unauthorized, verifySignature, verifyStandardWebhook, webauthnPath, webhooks, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps };
81
+ export { type Admin, type AdminAuth, type AdminOptions, type ApiFunction, type ApprovalAnswer, type ApprovalRequest, type AssistantBackend, type AssistantOptions, type AssistantSettings, type AssistantTool, type AuditEvent, type AuditQueue, type Authenticator, type BetterAuthAuthenticatorOptions, type CapturedRow, type ChatMessage, type ChatTextPart, type CleanupEntry, type CleanupReport, type CleanupSchedule, type CoalescedChange, type Completion, type CompletionOptions, type Conversation, type ConversationRecord, type ConversationStore, type ConversationsOptions, type CorsOptions, type CorsOrigins, type CreateAdminInput, type CreateAuthOptions, type DrainResult, type EnqueueOptions, type ErrorReporter, type ErrorReportingOptions, type ErrorReportingSettings, type EventData, type EventsOptions, FatalJobError, type FileCleanupInput, type FileLimits, type FileObject, type FileProvider, type FileStore, type FilesOptions, type FunctionApp, type FunctionCaller, type FunctionContext, type FunctionEnv, type FunctionHandler, type FunctionOptions, type FunctionRecords, type FunctionSource, HttpProblem, type IssueTokenOptions, type JobContext, type JobDefinition, type JobOptions, type JobsRuntime, type JwtOptions, type LinkedAccount, type ListParams, type LocalFilesOptions, type MailMessage, type Mailer, type MapOptions, type MemberInput, MissingCredentials, ModelError, type ModelSettings, type NewOrganization, type NewUser, type OperatorProvider, type OrganizationRole, type OrganizationsOptions, type PipelineHook, type PlatformSignIn, type PluginOption, type PluginRef, type ProjectConfig, type ProtectedFunctionOptions, type PublicFunctionContext, type PublicFunctionEnv, type PublicFunctionHandler, type PublicFunctionOptions, type QueryOptions, type QueryResult, type ReasoningDetail, type ReasoningEffort, type RecordKeyInput, type RecordPage, type ResourceChange, type ResourceSource, type RoleDefinition, type RolesInput, type RunContext, type RuntimeOptions, type S3Bucket, type S3FilesOptions, type S3ListPage, type S3Object, type ScanGuardMode, type ScheduledDelete, type Session, type SessionOrganization, type SignInPolicy, type SignInRule, type SignatureOptions, type SourceOption, type SourceRef, type StaffAccessRule, type StaffSignIn, type StandardSchema, type StandardWebhookOptions, type StartJobsOptions, type StoredMember, type StoredObject, type StoredOrganization, type StoredUser, type SubscriptionDefinition, type SubscriptionEvent, type SubscriptionOptions, type TenantValue, type ToolCall, type ToolContext, type ToolRecord, type ToolRecords, type ToolSpec, type TrustedApp, type TurnOptions, type User, type UserCreatedHook, type ViewSource, type WebhooksOptions, type WidgetOptions, type WriteEvent, type WriteOperation, addAppRoleToOwners, addMember, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaMigration, badRequest, betterAuthAuthenticator, changesMigration, checkAuthSchema, checkChanges, checkJobsSchema, checkSourceNames, checkWebhooksSchema, coalesce, configExports, consoleAuditQueue, conversationKey, createAdmin, createAuth, createConversations, createFileCleanup, createOrganization, createUser, defaultSignInPolicy, defineConfig, defineFunction, defineJob, defineTool, deleteUser, errorReportingSettings, errorReportingVariable, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, functionApp, hasUsers, imageSize, isJobDefinition, issueToken, jobDefinitions, jobsDrainApp, jobsSchema, jobsSchemaMigration, jwtAuthenticator, limitedFileStore, listOrganizations, listUsers, localFileStore, localFiles, mapStyleUrl, mapStyleVariable, maxTokenTtlSeconds, maxWidgetPropsBytes, mergeConfig, migrationCommand, newWebhookSecret, normalizeRoles, notFound, parseColumns, parseRoles, pluginOptions, pluginsFor, pluginsVariable, processor, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readWriteQueryTool, recordOf, removeMember, requestApproval, requireEnv, resourceChange, roleChoices, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, s3FileStore, s3Files, seesAssistant, seesRuntime, setMemberRoles, setUserBanned, setUserRole, signInPolicyMethods, sourceOptions, sourcesFor, sourcesVariable, startErrorReporting, startJobs, storageCleanupSchedule, streamChatCompletion, subscription, unauthorized, verifySignature, verifyStandardWebhook, webauthnPath, webhooks, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps };
package/dist/index.js CHANGED
@@ -34,6 +34,7 @@ import { storageCleanupSchedule } from "./files/cleanup-schedule.js";
34
34
  import { limitedFileStore } from "./files/limits.js";
35
35
  import { localFileStore, localFiles } from "./files/local-files.js";
36
36
  import { s3FileStore, s3Files } from "./files/s3-files.js";
37
+ import { mapStyleUrl, mapStyleVariable } from "./maps/map-settings.js";
37
38
  import { pluginOptions, pluginsFor, pluginsVariable } from "./plugins/plugin-settings.js";
38
39
  import { runtimeUrl, runtimeVariable, seesRuntime } from "./runtime/runtime-settings.js";
39
40
  import { checkSourceNames, sourceOptions, sourcesFor, sourcesVariable } from "./sources/source-settings.js";
@@ -58,4 +59,4 @@ import { createFileCleanup } from "./files/file-cleanup.js";
58
59
  import { aspectRatio, fileSize, fileType, imageSize, processor } from "./files/processors.js";
59
60
  import { startJobs } from "./jobs/runtime.js";
60
61
  import { jobsDrainApp } from "./jobs/drain-route.js";
61
- export { FatalJobError, HttpProblem, MissingCredentials, ModelError, addAppRoleToOwners, addMember, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaMigration, badRequest, betterAuthAuthenticator, changesMigration, checkAuthSchema, checkChanges, checkJobsSchema, checkSourceNames, checkWebhooksSchema, coalesce, configExports, consoleAuditQueue, conversationKey, createAdmin, createAuth, createConversations, createFileCleanup, createOrganization, createUser, defaultSignInPolicy, defineConfig, defineFunction, defineJob, defineTool, deleteUser, errorReportingSettings, errorReportingVariable, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, functionApp, hasUsers, imageSize, isJobDefinition, issueToken, jobDefinitions, jobsDrainApp, jobsSchema, jobsSchemaMigration, jwtAuthenticator, limitedFileStore, listOrganizations, listUsers, localFileStore, localFiles, maxTokenTtlSeconds, maxWidgetPropsBytes, mergeConfig, migrationCommand, newWebhookSecret, normalizeRoles, notFound, parseColumns, parseRoles, pluginOptions, pluginsFor, pluginsVariable, processor, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readWriteQueryTool, recordOf, removeMember, requestApproval, requireEnv, resourceChange, roleChoices, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, s3FileStore, s3Files, seesAssistant, seesRuntime, setMemberRoles, setUserBanned, setUserRole, signInPolicyMethods, sourceOptions, sourcesFor, sourcesVariable, startErrorReporting, startJobs, storageCleanupSchedule, streamChatCompletion, subscription, unauthorized, verifySignature, verifyStandardWebhook, webauthnPath, webhooks, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps };
62
+ export { FatalJobError, HttpProblem, MissingCredentials, ModelError, addAppRoleToOwners, addMember, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaMigration, badRequest, betterAuthAuthenticator, changesMigration, checkAuthSchema, checkChanges, checkJobsSchema, checkSourceNames, checkWebhooksSchema, coalesce, configExports, consoleAuditQueue, conversationKey, createAdmin, createAuth, createConversations, createFileCleanup, createOrganization, createUser, defaultSignInPolicy, defineConfig, defineFunction, defineJob, defineTool, deleteUser, errorReportingSettings, errorReportingVariable, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, functionApp, hasUsers, imageSize, isJobDefinition, issueToken, jobDefinitions, jobsDrainApp, jobsSchema, jobsSchemaMigration, jwtAuthenticator, limitedFileStore, listOrganizations, listUsers, localFileStore, localFiles, mapStyleUrl, mapStyleVariable, maxTokenTtlSeconds, maxWidgetPropsBytes, mergeConfig, migrationCommand, newWebhookSecret, normalizeRoles, notFound, parseColumns, parseRoles, pluginOptions, pluginsFor, pluginsVariable, processor, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readWriteQueryTool, recordOf, removeMember, requestApproval, requireEnv, resourceChange, roleChoices, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, s3FileStore, s3Files, seesAssistant, seesRuntime, setMemberRoles, setUserBanned, setUserRole, signInPolicyMethods, sourceOptions, sourcesFor, sourcesVariable, startErrorReporting, startJobs, storageCleanupSchedule, streamChatCompletion, subscription, unauthorized, verifySignature, verifyStandardWebhook, webauthnPath, webhooks, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps };
@@ -0,0 +1,12 @@
1
+ //#region src/maps/map-settings.d.ts
2
+ /**
3
+ * `style`: the URL of the MapLibre style the app draws its maps with (`l.map`), such as one of self-hosted PMTiles
4
+ * (`pmtiles://` sources). Falls back to `PROTOBASE_MAP_STYLE_URL`.
5
+ */
6
+ export type MapOptions = {
7
+ style?: string;
8
+ };
9
+ export declare const mapStyleVariable = "PROTOBASE_MAP_STYLE_URL";
10
+ /** The map style, when one is set and the option is not `false`. A value that is no http(s) URL stops the server at startup. */
11
+ export declare const mapStyleUrl: (option: MapOptions | false | undefined, env: Record<string, string | undefined>) => string | undefined;
12
+ //#endregion
@@ -0,0 +1,12 @@
1
+ import { checkedUrl } from "../checked-url.js";
2
+ //#region src/maps/map-settings.ts
3
+ const mapStyleVariable = "PROTOBASE_MAP_STYLE_URL";
4
+ /** The map style, when one is set and the option is not `false`. A value that is no http(s) URL stops the server at startup. */
5
+ const mapStyleUrl = (option, env) => {
6
+ if (option === false) return void 0;
7
+ const url = (option?.style ?? env["PROTOBASE_MAP_STYLE_URL"])?.trim();
8
+ if (!url) return void 0;
9
+ return checkedUrl(url, option?.style === void 0 ? mapStyleVariable : "options.maps.style");
10
+ };
11
+ //#endregion
12
+ export { mapStyleUrl, mapStyleVariable };
package/dist/meta.js CHANGED
@@ -62,6 +62,7 @@ const createMeta = (deps) => {
62
62
  ...deps.assistant && seesAssistant(session.user.roles) && { assistant: { url: deps.assistant } },
63
63
  ...deps.runtime && seesRuntime(session.user.roles) && { runtime: { url: deps.runtime } },
64
64
  ...deps.errorReporting && { errorReporting: deps.errorReporting },
65
+ ...deps.mapStyle && { maps: { style: deps.mapStyle } },
65
66
  ...plugins.length > 0 && { plugins },
66
67
  ...sources.length > 0 && { sources }
67
68
  };
@@ -96,7 +96,15 @@ const layoutShape = (resource, models) => shapes.oneOf({
96
96
  title: shapes.data,
97
97
  sidebar: shapes.data,
98
98
  props: shapes.refs
99
- }, { requires: ["props"] })
99
+ }, { requires: ["props"] }),
100
+ map: shapes.object({
101
+ kind: shapes.data,
102
+ title: shapes.data,
103
+ latitude: shapes.field,
104
+ longitude: shapes.field,
105
+ zoom: shapes.data,
106
+ sidebar: shapes.data
107
+ }, { requires: ["latitude", "longitude"] })
100
108
  });
101
109
  const linkShape = (model, models) => shapes.map((run) => {
102
110
  return ("to" in run ? addressErrors(run.to, {
@@ -150,7 +158,7 @@ const viewShape = (resource, model, models) => shapes.object({
150
158
  });
151
159
  /**
152
160
  * The view with every reference to a field the caller cannot read removed: list columns, sort, search and displays,
153
- * field hints, filter widgets, record sections and custom components' props, the chart, title and search result, the
161
+ * field hints, filter widgets, record sections, maps and custom components' props, the chart, title and search result, the
154
162
  * actions that set or read one, and the sidebar's `recent` group. Nothing in it may name a hidden field. Related sections
155
163
  * and links are checked against `models`, the caller's view of every resource.
156
164
  */
@@ -0,0 +1,21 @@
1
+ //#region src/view-map.ts
2
+ const coordinateTypes = /* @__PURE__ */ new Set([
3
+ "integer",
4
+ "bigint",
5
+ "decimal"
6
+ ]);
7
+ const maxZoom = 22;
8
+ const coordinateErrors = (model, name) => {
9
+ const field = Object.hasOwn(model.fields, name) ? model.fields[name] : void 0;
10
+ if (!field) return [`unknown field "${name}" on ${model.name}`];
11
+ if (!coordinateTypes.has(field.type)) return [`"${name}" on ${model.name} is ${field.type}, not a number of degrees`];
12
+ return [];
13
+ };
14
+ /** What makes a map section unable to show the record's position: a field that is no number, or a zoom MapLibre has not. */
15
+ const mapErrors = (item, model) => [
16
+ ...coordinateErrors(model, item.latitude),
17
+ ...coordinateErrors(model, item.longitude),
18
+ ...item.zoom === void 0 || Number.isFinite(item.zoom) && item.zoom >= 0 && item.zoom <= maxZoom ? [] : [`zoom ${item.zoom} is not between 0 and ${maxZoom}`]
19
+ ];
20
+ //#endregion
21
+ export { mapErrors };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@protobase/server",
3
- "version": "0.12.18",
3
+ "version": "0.12.19",
4
4
  "description": "The Protobase REST API as a Hono app, with Better Auth sign-in.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -30,9 +30,9 @@
30
30
  },
31
31
  "dependencies": {
32
32
  "@better-auth/passkey": "^1.7.7",
33
- "@protobase/layout": "0.12.18",
34
- "@protobase/query": "0.12.18",
35
- "@protobase/schema": "0.12.18",
33
+ "@protobase/layout": "0.12.19",
34
+ "@protobase/query": "0.12.19",
35
+ "@protobase/schema": "0.12.19",
36
36
  "@scalar/hono-api-reference": "^0.12.10",
37
37
  "@sentry/core": "^11.7.0",
38
38
  "@simplewebauthn/server": "^13.3.1",