@protobase/server 0.19.3 → 0.19.4

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.
@@ -1,10 +1,10 @@
1
1
  import { AuditQueue, PipelineHook, ScanGuardMode } from "./types.js";
2
2
  import { ErrorReportingOptions } from "./error-reporting/error-reporting-settings.js";
3
+ import { MapOptions } from "./maps/map-settings.js";
3
4
  import { PluginOption } from "./plugins/plugin-settings.js";
4
5
  import { SourceOption } from "./sources/source-settings.js";
5
6
  import { CorsOrigins } from "./api-cors.js";
6
7
  import { AssistantOptions } from "./assistant/assistant-settings.js";
7
- import { MapOptions } from "./maps/map-settings.js";
8
8
  import { RuntimeOptions } from "./runtime/runtime-settings.js";
9
9
  import { ResolveOptions, RoleLimit } from "@protobase/schema";
10
10
  //#region src/admin-options.d.ts
@@ -54,8 +54,9 @@ export type AdminOptions = {
54
54
  /** The runtime updates endpoint behind the top bar's update button (see `RuntimeOptions`); `false` turns it off. */
55
55
  runtime?: RuntimeOptions | false;
56
56
  /**
57
- * The map style the record page's maps (`l.map`) are drawn with (see `MapOptions`). Defaults to the one the platform
58
- * passes in `PROTOBASE_MAP_STYLE_URL`; without either, or with `false`, the app draws no maps.
57
+ * The map style the app's maps (`l.map`, `d.map`) are drawn with, and the layers they may switch to (see
58
+ * `MapOptions`). The style defaults to the one the platform passes in `PROTOBASE_MAP_STYLE_URL`; without either, or
59
+ * with `false`, the app draws no maps.
59
60
  */
60
61
  maps?: MapOptions | false;
61
62
  /**
@@ -22,7 +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
+ import { mapSettings } from "./maps/map-settings.js";
26
26
  import { pluginOptions, pluginsBeforeSignIn } from "./plugins/plugin-settings.js";
27
27
  import { runtimeUrl } from "./runtime/runtime-settings.js";
28
28
  import { checkSourceNames, sourceOptions } from "./sources/source-settings.js";
@@ -67,7 +67,7 @@ const createAdmin = ({ resources: appResources, views: appViews = [], pages = []
67
67
  const assistant = assistantSettings(options.assistant, globalThis.process?.env ?? {});
68
68
  const runtime = runtimeUrl(options.runtime, globalThis.process?.env ?? {});
69
69
  const errorReporting = errorReportingSettings(options.errorReporting, globalThis.process?.env ?? {});
70
- const mapStyle = mapStyleUrl(options.maps, globalThis.process?.env ?? {});
70
+ const maps = mapSettings(options.maps, globalThis.process?.env ?? {});
71
71
  const plugins = pluginOptions(options.plugins, globalThis.process?.env ?? {}, bundlePlugins);
72
72
  const sources = sourceOptions(options.sources, globalThis.process?.env ?? {});
73
73
  checkSourceNames(sources, {
@@ -102,7 +102,7 @@ const createAdmin = ({ resources: appResources, views: appViews = [], pages = []
102
102
  ...assistant && { assistant: assistant.kind === "external" ? assistant.url : `${systemPath}/assistant` },
103
103
  ...runtime && { runtime },
104
104
  ...errorReporting && { errorReporting },
105
- ...mapStyle && { mapStyle },
105
+ ...maps && { maps },
106
106
  plugins,
107
107
  sources,
108
108
  ...files && { files },
package/dist/index.d.ts CHANGED
@@ -20,6 +20,8 @@ import { FilesOptions } from "./files/options.js";
20
20
  import { ErrorReportingOptions, ErrorReportingSettings, errorReportingSettings, errorReportingVariable } from "./error-reporting/error-reporting-settings.js";
21
21
  import { CleanupEntry, CleanupSchedule, ScheduledDelete, storageCleanupSchedule } from "./files/cleanup-schedule.js";
22
22
  import { CleanupReport } from "./files/cleanup-runner.js";
23
+ import { MapLayer } from "./maps/map-layers.js";
24
+ import { MapOptions, MapSettings, mapSettings, mapStyleUrl, mapStyleVariable } from "./maps/map-settings.js";
23
25
  import { PluginOption, PluginRef, beforeSignInPluginsVariable, pluginOptions, pluginsBeforeSignIn, pluginsFor, pluginsVariable } from "./plugins/plugin-settings.js";
24
26
  import { ResourceSource, configExports } from "./resource-source.js";
25
27
  import { SourceOption, SourceRef, checkSourceNames, sourceOptions, sourcesFor, sourcesVariable } from "./sources/source-settings.js";
@@ -33,7 +35,6 @@ import { Conversation, ConversationsOptions, conversationKey, createConversation
33
35
  import { ApprovalAnswer, ApprovalRequest, requestApproval } from "./assistant/approval.js";
34
36
  import { AssistantTool, ToolContext, defineTool } from "./assistant/tools.js";
35
37
  import { AssistantOptions, AssistantSettings, assistantRoles, assistantSettings, assistantVariables, seesAssistant } from "./assistant/assistant-settings.js";
36
- import { MapOptions, mapStyleUrl, mapStyleVariable } from "./maps/map-settings.js";
37
38
  import { RuntimeOptions, runtimeUrl, runtimeVariable, seesRuntime } from "./runtime/runtime-settings.js";
38
39
  import { AdminOptions } from "./admin-options.js";
39
40
  import { CreateAdminInput, createAdmin } from "./create-admin.js";
@@ -83,4 +84,4 @@ import { FatalJobError } from "./jobs/run-job.js";
83
84
  import { DrainResult, JobsRuntime, StartJobsOptions, startJobs } from "./jobs/runtime.js";
84
85
  import { jobsDrainApp } from "./jobs/drain-route.js";
85
86
  import { checkJobsSchema, jobsSchema, jobsSchemaMigration } from "./jobs/schema.js";
86
- 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 EmailComponent, type EmailOptions, type EmailTemplate, type EnqueueOptions, type ErrorReporter, type ErrorReportingOptions, type ErrorReportingSettings, type EventData, type EventsOptions, FatalJobError, type FileCleanupInput, type FileLimits, type FileObject, type FileProvider, type FileRef, type FileStore, type FileVariantObject, 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 RenderedEmail, 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 Sharp, type SignInPolicy, type SignInRule, type SignatureOptions, type SourceOption, type SourceRef, type StaffAccessRule, type StaffSignIn, type StandardSchema, type StandardWebhookOptions, type StartJobsOptions, type StoreFileInput, 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, beforeSignInPluginsVariable, betterAuthAuthenticator, changesMigration, checkAuthSchema, checkChanges, checkJobsSchema, checkSourceNames, checkViewQueries, checkWebhooksSchema, coalesce, configExports, consoleAuditQueue, conversationKey, createAdmin, createAuth, createConversations, createFileCleanup, createOrganization, createUser, defaultSignInPolicy, defineConfig, defineEmail, defineFunction, defineJob, defineTool, deleteUser, errorReportingSettings, errorReportingVariable, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, formatUri, 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, pluginsBeforeSignIn, pluginsFor, pluginsVariable, processor, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readWriteQueryTool, recordOf, removeMember, renderVariant, requestApproval, requireEnv, resourceChange, roleChoices, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, s3FileStore, s3Files, seesAssistant, seesRuntime, setMemberRoles, setUserBanned, setUserRole, signInPolicyMethods, sourceOptions, sourcesFor, sourcesVariable, startErrorReporting, startJobs, storageCleanupSchedule, storeFile, streamChatCompletion, subscription, thumbHash, unauthorized, variantPath, verifySignature, verifyStandardWebhook, webauthnPath, webhooks, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps };
87
+ 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 EmailComponent, type EmailOptions, type EmailTemplate, type EnqueueOptions, type ErrorReporter, type ErrorReportingOptions, type ErrorReportingSettings, type EventData, type EventsOptions, FatalJobError, type FileCleanupInput, type FileLimits, type FileObject, type FileProvider, type FileRef, type FileStore, type FileVariantObject, 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 MapLayer, type MapOptions, type MapSettings, 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 RenderedEmail, 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 Sharp, type SignInPolicy, type SignInRule, type SignatureOptions, type SourceOption, type SourceRef, type StaffAccessRule, type StaffSignIn, type StandardSchema, type StandardWebhookOptions, type StartJobsOptions, type StoreFileInput, 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, beforeSignInPluginsVariable, betterAuthAuthenticator, changesMigration, checkAuthSchema, checkChanges, checkJobsSchema, checkSourceNames, checkViewQueries, checkWebhooksSchema, coalesce, configExports, consoleAuditQueue, conversationKey, createAdmin, createAuth, createConversations, createFileCleanup, createOrganization, createUser, defaultSignInPolicy, defineConfig, defineEmail, defineFunction, defineJob, defineTool, deleteUser, errorReportingSettings, errorReportingVariable, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, formatUri, functionApp, hasUsers, imageSize, isJobDefinition, issueToken, jobDefinitions, jobsDrainApp, jobsSchema, jobsSchemaMigration, jwtAuthenticator, limitedFileStore, listOrganizations, listUsers, localFileStore, localFiles, mapSettings, mapStyleUrl, mapStyleVariable, maxTokenTtlSeconds, maxWidgetPropsBytes, mergeConfig, migrationCommand, newWebhookSecret, normalizeRoles, notFound, parseColumns, parseRoles, pluginOptions, pluginsBeforeSignIn, pluginsFor, pluginsVariable, processor, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readWriteQueryTool, recordOf, removeMember, renderVariant, requestApproval, requireEnv, resourceChange, roleChoices, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, s3FileStore, s3Files, seesAssistant, seesRuntime, setMemberRoles, setUserBanned, setUserRole, signInPolicyMethods, sourceOptions, sourcesFor, sourcesVariable, startErrorReporting, startJobs, storageCleanupSchedule, storeFile, streamChatCompletion, subscription, thumbHash, unauthorized, variantPath, verifySignature, verifyStandardWebhook, webauthnPath, webhooks, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps };
package/dist/index.js CHANGED
@@ -35,7 +35,7 @@ import { storageCleanupSchedule } from "./files/cleanup-schedule.js";
35
35
  import { limitedFileStore } from "./files/limits.js";
36
36
  import { localFileStore, localFiles } from "./files/local-files.js";
37
37
  import { s3FileStore, s3Files } from "./files/s3-files.js";
38
- import { mapStyleUrl, mapStyleVariable } from "./maps/map-settings.js";
38
+ import { mapSettings, mapStyleUrl, mapStyleVariable } from "./maps/map-settings.js";
39
39
  import { beforeSignInPluginsVariable, pluginOptions, pluginsBeforeSignIn, pluginsFor, pluginsVariable } from "./plugins/plugin-settings.js";
40
40
  import { runtimeUrl, runtimeVariable, seesRuntime } from "./runtime/runtime-settings.js";
41
41
  import { checkSourceNames, sourceOptions, sourcesFor, sourcesVariable } from "./sources/source-settings.js";
@@ -64,4 +64,4 @@ import { aspectRatio, fileSize, fileType, imageSize, processor } from "./files/p
64
64
  import { defineEmail } from "./mail/define-email.js";
65
65
  import { startJobs } from "./jobs/runtime.js";
66
66
  import { jobsDrainApp } from "./jobs/drain-route.js";
67
- export { FatalJobError, HttpProblem, MissingCredentials, ModelError, addAppRoleToOwners, addMember, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaMigration, badRequest, beforeSignInPluginsVariable, betterAuthAuthenticator, changesMigration, checkAuthSchema, checkChanges, checkJobsSchema, checkSourceNames, checkViewQueries, checkWebhooksSchema, coalesce, configExports, consoleAuditQueue, conversationKey, createAdmin, createAuth, createConversations, createFileCleanup, createOrganization, createUser, defaultSignInPolicy, defineConfig, defineEmail, defineFunction, defineJob, defineTool, deleteUser, errorReportingSettings, errorReportingVariable, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, formatUri, 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, pluginsBeforeSignIn, pluginsFor, pluginsVariable, processor, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readWriteQueryTool, recordOf, removeMember, renderVariant, requestApproval, requireEnv, resourceChange, roleChoices, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, s3FileStore, s3Files, seesAssistant, seesRuntime, setMemberRoles, setUserBanned, setUserRole, signInPolicyMethods, sourceOptions, sourcesFor, sourcesVariable, startErrorReporting, startJobs, storageCleanupSchedule, storeFile, streamChatCompletion, subscription, thumbHash, unauthorized, variantPath, verifySignature, verifyStandardWebhook, webauthnPath, webhooks, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps };
67
+ export { FatalJobError, HttpProblem, MissingCredentials, ModelError, addAppRoleToOwners, addMember, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaMigration, badRequest, beforeSignInPluginsVariable, betterAuthAuthenticator, changesMigration, checkAuthSchema, checkChanges, checkJobsSchema, checkSourceNames, checkViewQueries, checkWebhooksSchema, coalesce, configExports, consoleAuditQueue, conversationKey, createAdmin, createAuth, createConversations, createFileCleanup, createOrganization, createUser, defaultSignInPolicy, defineConfig, defineEmail, defineFunction, defineJob, defineTool, deleteUser, errorReportingSettings, errorReportingVariable, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, formatUri, functionApp, hasUsers, imageSize, isJobDefinition, issueToken, jobDefinitions, jobsDrainApp, jobsSchema, jobsSchemaMigration, jwtAuthenticator, limitedFileStore, listOrganizations, listUsers, localFileStore, localFiles, mapSettings, mapStyleUrl, mapStyleVariable, maxTokenTtlSeconds, maxWidgetPropsBytes, mergeConfig, migrationCommand, newWebhookSecret, normalizeRoles, notFound, parseColumns, parseRoles, pluginOptions, pluginsBeforeSignIn, pluginsFor, pluginsVariable, processor, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readWriteQueryTool, recordOf, removeMember, renderVariant, requestApproval, requireEnv, resourceChange, roleChoices, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, s3FileStore, s3Files, seesAssistant, seesRuntime, setMemberRoles, setUserBanned, setUserRole, signInPolicyMethods, sourceOptions, sourcesFor, sourcesVariable, startErrorReporting, startJobs, storageCleanupSchedule, storeFile, streamChatCompletion, subscription, thumbHash, unauthorized, variantPath, verifySignature, verifyStandardWebhook, webauthnPath, webhooks, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps };
@@ -0,0 +1,20 @@
1
+ //#region src/maps/map-layers.d.ts
2
+ /**
3
+ * A map of raster tiles that the maps' switch offers in place of the style's, such as aerial photos.
4
+ * `id`: names the layer in the browser's memory of the choice, in lowercase letters, digits and `_`, like `aerial_nl`.
5
+ * `label`: what the switch says.
6
+ * `tiles`: the URL of its 256-pixel tiles in Web Mercator, with `{z}`, `{x}` and `{y}` where the zoom, column and row
7
+ * go, as an XYZ service or a WMTS in its `GoogleMapsCompatible` (EPSG:3857) tile matrix set takes them.
8
+ * `attribution`: the credit the tiles' licence asks for, shown in the map's corner while the layer is; it may hold links.
9
+ * `bounds`: `[west, south, east, north]` in degrees, where the tiles are: elsewhere the style's map shows through.
10
+ * `maxzoom`: the deepest zoom the tiles go to; the map enlarges them past it. Default 22.
11
+ */
12
+ export type MapLayer = {
13
+ id: string;
14
+ label: string;
15
+ tiles: string;
16
+ attribution: string;
17
+ bounds?: [number, number, number, number];
18
+ maxzoom?: number;
19
+ };
20
+ //#endregion
@@ -0,0 +1,46 @@
1
+ //#region src/maps/map-layers.ts
2
+ const idPattern = /^[a-z][a-z0-9_]*$/;
3
+ const checkedBounds = (bounds, source) => {
4
+ if (!(Array.isArray(bounds) && bounds.length === 4 && bounds.every((value) => typeof value === "number" && Number.isFinite(value)))) throw new Error(`${source}.bounds must be [west, south, east, north] in degrees`);
5
+ const [west, south, east, north] = bounds;
6
+ if (!(west >= -180 && east <= 180 && south >= -90 && north <= 90 && west < east && south < north)) throw new Error(`${source}.bounds must be [west, south, east, north] in degrees, west of east and south of north`);
7
+ return [
8
+ west,
9
+ south,
10
+ east,
11
+ north
12
+ ];
13
+ };
14
+ const checkedLayer = (layer, index) => {
15
+ const source = `options.maps.layers[${index}]`;
16
+ if (typeof layer.id !== "string" || !idPattern.test(layer.id)) throw new Error(`${source}.id must be lowercase letters, digits and _, like aerial_nl`);
17
+ if (typeof layer.label !== "string" || !layer.label.trim()) throw new Error(`${source}.label must say what the switch shows`);
18
+ if (typeof layer.attribution !== "string" || !layer.attribution.trim()) throw new Error(`${source}.attribution must credit the tiles as their licence asks`);
19
+ const tiles = typeof layer.tiles === "string" ? layer.tiles.trim() : "";
20
+ if (!URL.canParse(tiles) || !["https:", "http:"].includes(new URL(tiles).protocol)) throw new Error(`${source}.tiles must be an https:// or http:// URL`);
21
+ const missing = [
22
+ "{z}",
23
+ "{x}",
24
+ "{y}"
25
+ ].filter((placeholder) => !tiles.includes(placeholder));
26
+ if (missing.length > 0) throw new Error(`${source}.tiles must hold ${missing.join(", ")} where the tile's zoom, column and row go`);
27
+ const maxzoom = layer.maxzoom;
28
+ if (maxzoom !== void 0 && !(Number.isInteger(maxzoom) && maxzoom >= 0 && maxzoom <= 24)) throw new Error(`${source}.maxzoom must be a whole number from 0 to 24`);
29
+ return {
30
+ id: layer.id,
31
+ label: layer.label.trim(),
32
+ tiles,
33
+ attribution: layer.attribution.trim(),
34
+ ...layer.bounds !== void 0 && { bounds: checkedBounds(layer.bounds, source) },
35
+ ...maxzoom !== void 0 && { maxzoom }
36
+ };
37
+ };
38
+ /** The layers as `/meta` names them; one that is not as `MapLayer` says, or a repeated id, stops the server at startup. */
39
+ const checkedMapLayers = (layers) => {
40
+ const checked = layers.map(checkedLayer);
41
+ const repeated = checked.find((layer, index) => checked.findIndex((other) => other.id === layer.id) !== index);
42
+ if (repeated) throw new Error(`options.maps.layers: the id ${repeated.id} is used twice`);
43
+ return checked;
44
+ };
45
+ //#endregion
46
+ export { checkedMapLayers };
@@ -1,12 +1,27 @@
1
+ import { MapLayer } from "./map-layers.js";
1
2
  //#region src/maps/map-settings.d.ts
2
3
  /**
3
4
  * `style`: the URL of the MapLibre style the app draws its maps with (`l.map`), such as one of self-hosted PMTiles
4
5
  * (`pmtiles://` sources). Falls back to `PROTOBASE_MAP_STYLE_URL`.
6
+ * `layers`: other maps of raster tiles, such as aerial photos, that a switch on every map offers in place of the
7
+ * style's (see `MapLayer`). They need a style: without one there is no map to switch.
5
8
  */
6
9
  export type MapOptions = {
7
10
  style?: string;
11
+ layers?: MapLayer[];
12
+ };
13
+ /** What `/meta` names: the style and the layers a map may switch to. */
14
+ export type MapSettings = {
15
+ style: string;
16
+ layers?: MapLayer[];
8
17
  };
9
18
  export declare const mapStyleVariable = "PROTOBASE_MAP_STYLE_URL";
10
19
  /** 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
20
  export declare const mapStyleUrl: (option: MapOptions | false | undefined, env: Record<string, string | undefined>) => string | undefined;
12
- //#endregion
21
+ /**
22
+ * The style and its layers, when there is a style. The layers are checked whether or not there is one, so a mistake in
23
+ * them stops the server even where the style is not set.
24
+ */
25
+ export declare const mapSettings: (option: MapOptions | false | undefined, env: Record<string, string | undefined>) => MapSettings | undefined;
26
+ //#endregion
27
+ export type { MapLayer };
@@ -1,4 +1,5 @@
1
1
  import { checkedUrl } from "../checked-url.js";
2
+ import { checkedMapLayers } from "./map-layers.js";
2
3
  //#region src/maps/map-settings.ts
3
4
  const mapStyleVariable = "PROTOBASE_MAP_STYLE_URL";
4
5
  /** 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. */
@@ -8,5 +9,18 @@ const mapStyleUrl = (option, env) => {
8
9
  if (!url) return void 0;
9
10
  return checkedUrl(url, option?.style === void 0 ? mapStyleVariable : "options.maps.style");
10
11
  };
12
+ /**
13
+ * The style and its layers, when there is a style. The layers are checked whether or not there is one, so a mistake in
14
+ * them stops the server even where the style is not set.
15
+ */
16
+ const mapSettings = (option, env) => {
17
+ const layers = option ? checkedMapLayers(option.layers ?? []) : [];
18
+ const style = mapStyleUrl(option, env);
19
+ if (!style) return void 0;
20
+ return {
21
+ style,
22
+ ...layers.length > 0 && { layers }
23
+ };
24
+ };
11
25
  //#endregion
12
- export { mapStyleUrl, mapStyleVariable };
26
+ export { mapSettings, mapStyleUrl, mapStyleVariable };
package/dist/meta.js CHANGED
@@ -78,7 +78,7 @@ const createMeta = (deps) => {
78
78
  ...deps.assistant && seesAssistant(session.user.roles) && { assistant: { url: deps.assistant } },
79
79
  ...deps.runtime && seesRuntime(session.user.roles) && { runtime: { url: deps.runtime } },
80
80
  ...deps.errorReporting && { errorReporting: deps.errorReporting },
81
- ...deps.mapStyle && { maps: { style: deps.mapStyle } },
81
+ ...deps.maps && { maps: deps.maps },
82
82
  ...deps.files && deps.files.trustedHosts.length > 0 && { files: { trustedHosts: deps.files.trustedHosts } },
83
83
  ...plugins.length > 0 && { plugins },
84
84
  ...sources.length > 0 && { sources }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@protobase/server",
3
- "version": "0.19.3",
3
+ "version": "0.19.4",
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.19.3",
34
- "@protobase/query": "0.19.3",
35
- "@protobase/schema": "0.19.3",
33
+ "@protobase/layout": "0.19.4",
34
+ "@protobase/query": "0.19.4",
35
+ "@protobase/schema": "0.19.4",
36
36
  "@react-email/render": "^2.1.0",
37
37
  "@scalar/hono-api-reference": "^0.12.10",
38
38
  "@sentry/core": "^11.7.0",