@protobase/server 0.40.10 → 0.41.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -125,4 +125,7 @@ import { isSchedule } from "./jobs/schedule.js";
125
125
  import { DrainResult, JobsRuntime, StartJobsOptions, startJobs } from "./jobs/runtime.js";
126
126
  import { jobsDrainApp } from "./jobs/drain-route.js";
127
127
  import { checkJobsSchema, jobsSchema, jobsSchemaMigration } from "./jobs/schema.js";
128
- export { type Admin, type AdminAuth, type AdminOptions, type ApiFunction, type ApiKeys, type ApiKeysOptions, type AppIcons, type AppIdentity, type ApplyOptions, type ApplyResult, type ApprovalAnswer, type ApprovalRequest, type AssistantBackend, type AssistantOptions, type AssistantSettings, type AssistantTool, type AuditEvent, type AuditQueue, type Authenticator, type BetterAuthAuthenticator, type BetterAuthAuthenticatorOptions, type Caller, type CallerOptions, type CapturedRow, type CascadingKey, type ChatMessage, type ChatTextPart, type CheckAiInput, type CleanupEntry, type CleanupReport, type CleanupSchedule, type CoalescedChange, type CommentItem, type CommentUsers, 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 EncryptionStatus, type EncryptionTools, type EncryptionToolsInput, type EnqueueOptions, type ErrorReporter, type ErrorReportingOptions, type ErrorReportingSettings, type EventData, type EventsOptions, FatalJobError, type FieldApplied, type FieldStatus, 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 FunctionSocket, type FunctionSource, type HistoryOptions, HttpProblem, type IssueTokenOptions, type JobContext, type JobDefinition, type JobOptions, type JobsRuntime, type JwtOptions, type JwtVerifyOptions, type KeyFunctionContext, type LinkedAccount, type ListParams, type LocalFilesOptions, type MailMessage, type Mailer, type MapLayer, type MapOptions, type MapSettings, type McpGateInput, type McpHandlerInput, type McpRoutesInput, type McpServerInput, type McpToolsInput, type McpWrites, type MemberInput, MissingCredentials, type MobileConfig, ModelError, type ModelSettings, type NewOrganization, type NewUser, type OperatorProvider, type OrganizationRole, type OrganizationsOptions, type PipelineHook, type PlatformSignIn, type PluginOption, type PluginRef, type PngIcon, type ProjectConfig, type ProtectedFunctionOptions, type PublicFunctionContext, type PublicFunctionEnv, type PublicFunctionHandler, type PublicFunctionOptions, type QueryOptions, type QueryResult, type ReadableResources, type ReasoningDetail, type ReasoningEffort, type RebindOption, 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 SocketEvents, 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 StrayValues, type SubscriptionDefinition, type SubscriptionEvent, type SubscriptionOptions, type TenantValue, type ToolAnnotations, type ToolCall, type ToolContext, type ToolRecord, type ToolRecords, type ToolSpec, type TrustedApp, type TurnOptions, type UnreadableEntry, type User, type UserCreatedHook, type ViewSource, type WebhooksOptions, type WidgetOptions, type WriteEvent, type WriteOperation, type WriteRecordToolsInput, addAppRoleToOwners, addMember, apiKeyCaller, apiKeySettings, apiKeyVariables, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaMigration, authStoreTables, backgroundSettled, badRequest, beforeSignInPluginsVariable, betterAuthAuthenticator, betterAuthTokens, capSocketMessages, changesMigration, checkAi, checkAuthSchema, checkChanges, checkComments, checkComputed, checkJobsSchema, checkQueryRunner, checkSourceNames, checkViewQueries, checkWebhooksSchema, coalesce, commentsMigration, computedMigration, configExports, consoleAuditQueue, conversationKey, createAdmin, createAuth, createConversations, createEncryptionTools, createFileCleanup, createOrganization, createRecordTool, createUser, defaultAppName, defaultSignInPolicy, defineConfig, defineEmail, defineFunction, defineJob, defineTool, deleteUser, errorReportingSettings, errorReportingVariable, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, formatUri, functionApp, getRecordTool, goTruePath, handleDroppedConnections, hasUsers, honorSocketMessageCaps, imageSize, isBackgroundWork, isCaptured, isJobDefinition, isOAuthWellKnownPath, isSchedule, issueToken, jobDefinitions, jobsDrainApp, jobsSchema, jobsSchemaMigration, jwtAuthenticator, jwtVerifier, limitedFileStore, listOrganizations, listRecordsTool, listResourcesTool, listUsers, localFileStore, localFiles, mapSettings, mapStyleUrl, mapStyleVariable, maxTokenTtlSeconds, maxWidgetPropsBytes, mcpGate, mcpHandler, mcpRoutes, mcpServer, mcpToolContext, mcpTools, mergeConfig, migrationCommand, newWebhookSecret, normalizeRoles, notFound, oauthWellKnownPaths, objectKey, parseColumns, parseRoles, passwordLength, pluginOptions, pluginsBeforeSignIn, pluginsFor, pluginsVariable, pngIcons, processor, queryRunnerMigration, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readRecordTools, readWriteQueryTool, readableResources, realtimeLimits, recordOf, removeMember, renderVariant, requestApproval, requireEnv, resourceChange, roleChoices, runInBackground, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, s3FileStore, s3Files, seesAssistant, seesRuntime, sessionOf, setMemberRoles, setUserBanned, setUserRole, signInPolicyMethods, socketMessageCapOf, sourceOptions, sourcesFor, sourcesVariable, startErrorReporting, startJobs, storageCleanupSchedule, storageProvider, storeFile, storeUsage, streamChatCompletion, subscription, supabasePath, thumbHash, unauthorized, updateRecordTool, variantPath, verifySignature, verifyStandardWebhook, waitUntil, webauthnPath, webhooks, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps, writeRecordTools };
128
+ import { MobileRelease, ReleaseFile, ServedReleaseFile, composeRelease, releaseManifest } from "./mobile-release/release.js";
129
+ import { MobileReleaseAppInput, OfferedRelease, mobileReleaseApp } from "./mobile-release/release-app.js";
130
+ import { mobileSigningKeyVariable, readSigningKey } from "./mobile-release/signing-key.js";
131
+ export { type Admin, type AdminAuth, type AdminOptions, type ApiFunction, type ApiKeys, type ApiKeysOptions, type AppIcons, type AppIdentity, type ApplyOptions, type ApplyResult, type ApprovalAnswer, type ApprovalRequest, type AssistantBackend, type AssistantOptions, type AssistantSettings, type AssistantTool, type AuditEvent, type AuditQueue, type Authenticator, type BetterAuthAuthenticator, type BetterAuthAuthenticatorOptions, type Caller, type CallerOptions, type CapturedRow, type CascadingKey, type ChatMessage, type ChatTextPart, type CheckAiInput, type CleanupEntry, type CleanupReport, type CleanupSchedule, type CoalescedChange, type CommentItem, type CommentUsers, 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 EncryptionStatus, type EncryptionTools, type EncryptionToolsInput, type EnqueueOptions, type ErrorReporter, type ErrorReportingOptions, type ErrorReportingSettings, type EventData, type EventsOptions, FatalJobError, type FieldApplied, type FieldStatus, 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 FunctionSocket, type FunctionSource, type HistoryOptions, HttpProblem, type IssueTokenOptions, type JobContext, type JobDefinition, type JobOptions, type JobsRuntime, type JwtOptions, type JwtVerifyOptions, type KeyFunctionContext, type LinkedAccount, type ListParams, type LocalFilesOptions, type MailMessage, type Mailer, type MapLayer, type MapOptions, type MapSettings, type McpGateInput, type McpHandlerInput, type McpRoutesInput, type McpServerInput, type McpToolsInput, type McpWrites, type MemberInput, MissingCredentials, type MobileConfig, type MobileRelease, type MobileReleaseAppInput, ModelError, type ModelSettings, type NewOrganization, type NewUser, type OfferedRelease, type OperatorProvider, type OrganizationRole, type OrganizationsOptions, type PipelineHook, type PlatformSignIn, type PluginOption, type PluginRef, type PngIcon, type ProjectConfig, type ProtectedFunctionOptions, type PublicFunctionContext, type PublicFunctionEnv, type PublicFunctionHandler, type PublicFunctionOptions, type QueryOptions, type QueryResult, type ReadableResources, type ReasoningDetail, type ReasoningEffort, type RebindOption, type RecordKeyInput, type RecordPage, type ReleaseFile, 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 ServedReleaseFile, type Session, type SessionOrganization, type Sharp, type SignInPolicy, type SignInRule, type SignatureOptions, type SocketEvents, 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 StrayValues, type SubscriptionDefinition, type SubscriptionEvent, type SubscriptionOptions, type TenantValue, type ToolAnnotations, type ToolCall, type ToolContext, type ToolRecord, type ToolRecords, type ToolSpec, type TrustedApp, type TurnOptions, type UnreadableEntry, type User, type UserCreatedHook, type ViewSource, type WebhooksOptions, type WidgetOptions, type WriteEvent, type WriteOperation, type WriteRecordToolsInput, addAppRoleToOwners, addMember, apiKeyCaller, apiKeySettings, apiKeyVariables, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaMigration, authStoreTables, backgroundSettled, badRequest, beforeSignInPluginsVariable, betterAuthAuthenticator, betterAuthTokens, capSocketMessages, changesMigration, checkAi, checkAuthSchema, checkChanges, checkComments, checkComputed, checkJobsSchema, checkQueryRunner, checkSourceNames, checkViewQueries, checkWebhooksSchema, coalesce, commentsMigration, composeRelease, computedMigration, configExports, consoleAuditQueue, conversationKey, createAdmin, createAuth, createConversations, createEncryptionTools, createFileCleanup, createOrganization, createRecordTool, createUser, defaultAppName, defaultSignInPolicy, defineConfig, defineEmail, defineFunction, defineJob, defineTool, deleteUser, errorReportingSettings, errorReportingVariable, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, formatUri, functionApp, getRecordTool, goTruePath, handleDroppedConnections, hasUsers, honorSocketMessageCaps, imageSize, isBackgroundWork, isCaptured, isJobDefinition, isOAuthWellKnownPath, isSchedule, issueToken, jobDefinitions, jobsDrainApp, jobsSchema, jobsSchemaMigration, jwtAuthenticator, jwtVerifier, limitedFileStore, listOrganizations, listRecordsTool, listResourcesTool, listUsers, localFileStore, localFiles, mapSettings, mapStyleUrl, mapStyleVariable, maxTokenTtlSeconds, maxWidgetPropsBytes, mcpGate, mcpHandler, mcpRoutes, mcpServer, mcpToolContext, mcpTools, mergeConfig, migrationCommand, mobileReleaseApp, mobileSigningKeyVariable, newWebhookSecret, normalizeRoles, notFound, oauthWellKnownPaths, objectKey, parseColumns, parseRoles, passwordLength, pluginOptions, pluginsBeforeSignIn, pluginsFor, pluginsVariable, pngIcons, processor, queryRunnerMigration, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readRecordTools, readSigningKey, readWriteQueryTool, readableResources, realtimeLimits, recordOf, releaseManifest, removeMember, renderVariant, requestApproval, requireEnv, resourceChange, roleChoices, runInBackground, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, s3FileStore, s3Files, seesAssistant, seesRuntime, sessionOf, setMemberRoles, setUserBanned, setUserRole, signInPolicyMethods, socketMessageCapOf, sourceOptions, sourcesFor, sourcesVariable, startErrorReporting, startJobs, storageCleanupSchedule, storageProvider, storeFile, storeUsage, streamChatCompletion, subscription, supabasePath, thumbHash, unauthorized, updateRecordTool, variantPath, verifySignature, verifyStandardWebhook, waitUntil, webauthnPath, webhooks, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps, writeRecordTools };
package/dist/index.js CHANGED
@@ -95,4 +95,7 @@ import { aspectRatio, fileSize, fileType, imageSize, processor } from "./files/p
95
95
  import { defineEmail } from "./mail/define-email.js";
96
96
  import { startJobs } from "./jobs/runtime.js";
97
97
  import { jobsDrainApp } from "./jobs/drain-route.js";
98
- export { FatalJobError, HttpProblem, MissingCredentials, ModelError, addAppRoleToOwners, addMember, apiKeyCaller, apiKeySettings, apiKeyVariables, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaMigration, authStoreTables, backgroundSettled, badRequest, beforeSignInPluginsVariable, betterAuthAuthenticator, betterAuthTokens, capSocketMessages, changesMigration, checkAi, checkAuthSchema, checkChanges, checkComments, checkComputed, checkJobsSchema, checkQueryRunner, checkSourceNames, checkViewQueries, checkWebhooksSchema, coalesce, commentsMigration, computedMigration, configExports, consoleAuditQueue, conversationKey, createAdmin, createAuth, createConversations, createEncryptionTools, createFileCleanup, createOrganization, createRecordTool, createUser, defaultAppName, defaultSignInPolicy, defineConfig, defineEmail, defineFunction, defineJob, defineTool, deleteUser, errorReportingSettings, errorReportingVariable, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, formatUri, functionApp, getRecordTool, goTruePath, handleDroppedConnections, hasUsers, honorSocketMessageCaps, imageSize, isBackgroundWork, isCaptured, isJobDefinition, isOAuthWellKnownPath, isSchedule, issueToken, jobDefinitions, jobsDrainApp, jobsSchema, jobsSchemaMigration, jwtAuthenticator, jwtVerifier, limitedFileStore, listOrganizations, listRecordsTool, listResourcesTool, listUsers, localFileStore, localFiles, mapSettings, mapStyleUrl, mapStyleVariable, maxTokenTtlSeconds, maxWidgetPropsBytes, mcpGate, mcpHandler, mcpRoutes, mcpServer, mcpToolContext, mcpTools, mergeConfig, migrationCommand, newWebhookSecret, normalizeRoles, notFound, oauthWellKnownPaths, objectKey, parseColumns, parseRoles, passwordLength, pluginOptions, pluginsBeforeSignIn, pluginsFor, pluginsVariable, pngIcons, processor, queryRunnerMigration, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readRecordTools, readWriteQueryTool, readableResources, realtimeLimits, recordOf, removeMember, renderVariant, requestApproval, requireEnv, resourceChange, roleChoices, runInBackground, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, s3FileStore, s3Files, seesAssistant, seesRuntime, sessionOf, setMemberRoles, setUserBanned, setUserRole, signInPolicyMethods, socketMessageCapOf, sourceOptions, sourcesFor, sourcesVariable, startErrorReporting, startJobs, storageCleanupSchedule, storageProvider, storeFile, storeUsage, streamChatCompletion, subscription, supabasePath, thumbHash, unauthorized, updateRecordTool, variantPath, verifySignature, verifyStandardWebhook, waitUntil, webauthnPath, webhooks, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps, writeRecordTools };
98
+ import { composeRelease, releaseManifest } from "./mobile-release/release.js";
99
+ import { mobileReleaseApp } from "./mobile-release/release-app.js";
100
+ import { mobileSigningKeyVariable, readSigningKey } from "./mobile-release/signing-key.js";
101
+ export { FatalJobError, HttpProblem, MissingCredentials, ModelError, addAppRoleToOwners, addMember, apiKeyCaller, apiKeySettings, apiKeyVariables, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaMigration, authStoreTables, backgroundSettled, badRequest, beforeSignInPluginsVariable, betterAuthAuthenticator, betterAuthTokens, capSocketMessages, changesMigration, checkAi, checkAuthSchema, checkChanges, checkComments, checkComputed, checkJobsSchema, checkQueryRunner, checkSourceNames, checkViewQueries, checkWebhooksSchema, coalesce, commentsMigration, composeRelease, computedMigration, configExports, consoleAuditQueue, conversationKey, createAdmin, createAuth, createConversations, createEncryptionTools, createFileCleanup, createOrganization, createRecordTool, createUser, defaultAppName, defaultSignInPolicy, defineConfig, defineEmail, defineFunction, defineJob, defineTool, deleteUser, errorReportingSettings, errorReportingVariable, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, formatUri, functionApp, getRecordTool, goTruePath, handleDroppedConnections, hasUsers, honorSocketMessageCaps, imageSize, isBackgroundWork, isCaptured, isJobDefinition, isOAuthWellKnownPath, isSchedule, issueToken, jobDefinitions, jobsDrainApp, jobsSchema, jobsSchemaMigration, jwtAuthenticator, jwtVerifier, limitedFileStore, listOrganizations, listRecordsTool, listResourcesTool, listUsers, localFileStore, localFiles, mapSettings, mapStyleUrl, mapStyleVariable, maxTokenTtlSeconds, maxWidgetPropsBytes, mcpGate, mcpHandler, mcpRoutes, mcpServer, mcpToolContext, mcpTools, mergeConfig, migrationCommand, mobileReleaseApp, mobileSigningKeyVariable, newWebhookSecret, normalizeRoles, notFound, oauthWellKnownPaths, objectKey, parseColumns, parseRoles, passwordLength, pluginOptions, pluginsBeforeSignIn, pluginsFor, pluginsVariable, pngIcons, processor, queryRunnerMigration, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readRecordTools, readSigningKey, readWriteQueryTool, readableResources, realtimeLimits, recordOf, releaseManifest, removeMember, renderVariant, requestApproval, requireEnv, resourceChange, roleChoices, runInBackground, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, s3FileStore, s3Files, seesAssistant, seesRuntime, sessionOf, setMemberRoles, setUserBanned, setUserRole, signInPolicyMethods, socketMessageCapOf, sourceOptions, sourcesFor, sourcesVariable, startErrorReporting, startJobs, storageCleanupSchedule, storageProvider, storeFile, storeUsage, streamChatCompletion, subscription, supabasePath, thumbHash, unauthorized, updateRecordTool, variantPath, verifySignature, verifyStandardWebhook, waitUntil, webauthnPath, webhooks, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps, writeRecordTools };
@@ -0,0 +1,43 @@
1
+ import { MobileRelease } from "./release.js";
2
+ //#region src/mobile-release/release-app.d.ts
3
+ /** The release a server offers: its files, the native fingerprint of the binaries it runs on and the API origin it calls. */
4
+ export type OfferedRelease = MobileRelease & {
5
+ fingerprint: string;
6
+ origin: string;
7
+ };
8
+ export type MobileReleaseAppInput = {
9
+ /** `undefined` when the server serves no release, such as the API alone, whose app files a host serves. */
10
+ release?: OfferedRelease;
11
+ /** The server's Protobase version, which the shell a device runs must work with. */
12
+ serverVersion: string;
13
+ };
14
+ /**
15
+ * The live updater's routes. `GET /api/mobile/release` tells a device the release it should run: the server's own,
16
+ * when it runs on the device's binary, and whether the update is required. `GET /api/mobile/releases/<bundle id>?href=`
17
+ * serves the release's manifest and files, each with its sha256 in `X-Checksum` and, signed, its `X-Signature`, which
18
+ * the updater checks each file against: it never checks a file against the manifest.
19
+ */
20
+ export declare const mobileReleaseApp: ({ release, serverVersion }: MobileReleaseAppInput) => import("hono/hono-base").HonoBase<import("hono/types").BlankEnv, {
21
+ "/api/mobile/release": {
22
+ $get: {
23
+ input: {};
24
+ output: {};
25
+ outputFormat: string;
26
+ status: import("hono/utils/http-status").StatusCode;
27
+ };
28
+ };
29
+ } & {
30
+ "/api/mobile/releases/:bundleId": {
31
+ $get: {
32
+ input: {
33
+ param: {
34
+ bundleId: string;
35
+ };
36
+ };
37
+ output: {};
38
+ outputFormat: string;
39
+ status: import("hono/utils/http-status").StatusCode;
40
+ };
41
+ };
42
+ }, "/", "/api/mobile/releases/:bundleId">;
43
+ //#endregion
@@ -0,0 +1,41 @@
1
+ import { badRequest, notFound, problemResponse } from "../problem.js";
2
+ import { isShellVersion, shellUpdateRequired } from "./shell-update.js";
3
+ import { Hono } from "hono";
4
+ import { mobileReleaseBasePath, mobileReleaseParams, mobileReleasePath, mobileReleasesPath } from "@protobase/schema/internal";
5
+ import { getMimeType } from "hono/utils/mime";
6
+ //#region src/mobile-release/release-app.ts
7
+ /**
8
+ * The live updater's routes. `GET /api/mobile/release` tells a device the release it should run: the server's own,
9
+ * when it runs on the device's binary, and whether the update is required. `GET /api/mobile/releases/<bundle id>?href=`
10
+ * serves the release's manifest and files, each with its sha256 in `X-Checksum` and, signed, its `X-Signature`, which
11
+ * the updater checks each file against: it never checks a file against the manifest.
12
+ */
13
+ const mobileReleaseApp = ({ release, serverVersion }) => new Hono().get(mobileReleasePath, (c) => {
14
+ const problem = (error) => problemResponse(error, c.req.path);
15
+ const fingerprint = c.req.query(mobileReleaseParams.fingerprint);
16
+ const shellVersion = c.req.query(mobileReleaseParams.shellVersion);
17
+ if (!fingerprint) return problem(badRequest("invalid-fingerprint", `${mobileReleaseParams.fingerprint} must name the native fingerprint of the app`));
18
+ if (!shellVersion || !isShellVersion(shellVersion)) return problem(badRequest("invalid-shell-version", `${mobileReleaseParams.shellVersion} must be the x.y.z Protobase version of the shell the app runs`));
19
+ const offered = release?.fingerprint === fingerprint ? release : void 0;
20
+ const answer = {
21
+ bundleId: offered?.bundleId ?? null,
22
+ url: offered ? `${offered.origin}${mobileReleaseBasePath(offered.bundleId)}` : null,
23
+ required: shellUpdateRequired(shellVersion, serverVersion)
24
+ };
25
+ return c.json(answer, 200, {
26
+ "cache-control": "no-store",
27
+ "access-control-allow-origin": "*"
28
+ });
29
+ }).get(`${mobileReleasesPath}/:bundleId`, (c) => {
30
+ const href = c.req.query("href") ?? "";
31
+ const file = release && c.req.param("bundleId") === release.bundleId ? release.files.get(href) : void 0;
32
+ if (!file) return problemResponse(notFound("The release has no such file, or is no longer served"), c.req.path);
33
+ return c.body(file.content, 200, {
34
+ "content-type": getMimeType(href) ?? "application/octet-stream",
35
+ "cache-control": "public, max-age=31536000, immutable",
36
+ "x-checksum": file.checksum,
37
+ ...file.signature && { "x-signature": file.signature }
38
+ });
39
+ });
40
+ //#endregion
41
+ export { mobileReleaseApp };
@@ -0,0 +1,33 @@
1
+ import { KeyObject } from "node:crypto";
2
+ //#region src/mobile-release/release.d.ts
3
+ /** A file of a release: its path inside the app's files, such as `assets/index-abc123.js`, and its bytes. */
4
+ export type ReleaseFile = {
5
+ href: string;
6
+ content: Uint8Array;
7
+ };
8
+ /** A file as the updater gets it: its bytes, their sha256 in hex and, with a signing key, their signature. */
9
+ export type ServedReleaseFile = {
10
+ content: Uint8Array;
11
+ checksum: string;
12
+ signature?: string;
13
+ };
14
+ /**
15
+ * A release of the app's files: its bundle id, a hash of its manifest and of the signing key's public key, and every
16
+ * file by `href`, the manifest included.
17
+ */
18
+ export type MobileRelease = {
19
+ bundleId: string;
20
+ files: Map<string, ServedReleaseFile>;
21
+ };
22
+ /** The updater's manifest of `files`: each one's `href`, sha256 in hex and size, in the order of their hrefs. */
23
+ export declare const releaseManifest: (files: ReleaseFile[]) => {
24
+ href: string;
25
+ checksum: string;
26
+ sizeInBytes: number;
27
+ }[];
28
+ /**
29
+ * The release of `files`, each hashed and, with `signingKey`, signed once here. Its manifest is served as a file of
30
+ * its own, so a file of the app may not have the manifest's name.
31
+ */
32
+ export declare const composeRelease: (files: ReleaseFile[], signingKey?: KeyObject) => MobileRelease;
33
+ //#endregion
@@ -0,0 +1,34 @@
1
+ import { releaseManifestFile } from "@protobase/schema/internal";
2
+ import { createHash, createPublicKey, sign } from "node:crypto";
3
+ //#region src/mobile-release/release.ts
4
+ const sha256 = (content) => createHash("sha256").update(content).digest("hex");
5
+ /** The updater's manifest of `files`: each one's `href`, sha256 in hex and size, in the order of their hrefs. */
6
+ const releaseManifest = (files) => [...files].sort((a, b) => a.href < b.href ? -1 : a.href > b.href ? 1 : 0).map(({ href, content }) => ({
7
+ href,
8
+ checksum: sha256(content),
9
+ sizeInBytes: content.byteLength
10
+ }));
11
+ const signature = (content, key) => sign("sha256", content, key).toString("base64");
12
+ const signer = (signingKey) => signingKey ? createPublicKey(signingKey).export({
13
+ type: "spki",
14
+ format: "der"
15
+ }) : /* @__PURE__ */ new Uint8Array();
16
+ /**
17
+ * The release of `files`, each hashed and, with `signingKey`, signed once here. Its manifest is served as a file of
18
+ * its own, so a file of the app may not have the manifest's name.
19
+ */
20
+ const composeRelease = (files, signingKey) => {
21
+ if (files.some((file) => file.href === releaseManifestFile)) throw new Error(`The app's files have a ${releaseManifestFile}, the name of the updater's manifest: rename it`);
22
+ const manifest = new TextEncoder().encode(JSON.stringify(releaseManifest(files)));
23
+ const served = (content) => ({
24
+ content,
25
+ checksum: sha256(content),
26
+ ...signingKey && { signature: signature(content, signingKey) }
27
+ });
28
+ return {
29
+ bundleId: createHash("sha256").update(manifest).update(signer(signingKey)).digest("hex").slice(0, 16),
30
+ files: new Map([...files.map(({ href, content }) => [href, served(content)]), [releaseManifestFile, served(manifest)]])
31
+ };
32
+ };
33
+ //#endregion
34
+ export { composeRelease, releaseManifest };
@@ -0,0 +1,25 @@
1
+ //#region src/mobile-release/shell-update.ts
2
+ const versionPattern = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/;
3
+ const parseVersion = (value) => {
4
+ const match = versionPattern.exec(value);
5
+ return match ? {
6
+ major: Number(match[1]),
7
+ minor: Number(match[2])
8
+ } : void 0;
9
+ };
10
+ /** Whether `value` is an x.y.z version, as a shell's Protobase version is. */
11
+ const isShellVersion = (value) => parseVersion(value) !== void 0;
12
+ /**
13
+ * Whether a device running a shell of Protobase `shell` must update before it goes on: its shell no longer works with
14
+ * the API of a server of Protobase `server`. It works on the same major, and below 1.0, where a minor may break, on the
15
+ * same minor, as a bundle does on a runtime.
16
+ */
17
+ const shellUpdateRequired = (shell, server) => {
18
+ const own = parseVersion(server);
19
+ if (!own) throw new Error(`The server's own Protobase version ${JSON.stringify(server)} is no x.y.z version`);
20
+ const running = parseVersion(shell);
21
+ if (!running) throw new Error(`The shell's Protobase version ${JSON.stringify(shell)} is no x.y.z version`);
22
+ return running.major !== own.major || own.major === 0 && running.minor !== own.minor;
23
+ };
24
+ //#endregion
25
+ export { isShellVersion, shellUpdateRequired };
@@ -0,0 +1,14 @@
1
+ import { KeyObject } from "node:crypto";
2
+ //#region src/mobile-release/signing-key.d.ts
3
+ /**
4
+ * The environment variable holding the RSA private key, PEM, that signs every file of the mobile release, for an app
5
+ * whose updater has the matching public key. The key is whoever deploys the app's: the signature guards the way from
6
+ * the server to the device (proxies, a CDN, a router), not a server that is broken into.
7
+ */
8
+ export declare const mobileSigningKeyVariable = "PROTOBASE_MOBILE_SIGNING_KEY";
9
+ /**
10
+ * The signing key in `PROTOBASE_MOBILE_SIGNING_KEY`, `undefined` when it is unset. Its newlines may be written `\n`, as
11
+ * in an env file that holds one line per value. A key that is not PEM, not RSA or under 2048 bits stops startup.
12
+ */
13
+ export declare const readSigningKey: (env: Record<string, string | undefined>) => KeyObject | undefined;
14
+ //#endregion
@@ -0,0 +1,32 @@
1
+ import { createPrivateKey } from "node:crypto";
2
+ //#region src/mobile-release/signing-key.ts
3
+ /**
4
+ * The environment variable holding the RSA private key, PEM, that signs every file of the mobile release, for an app
5
+ * whose updater has the matching public key. The key is whoever deploys the app's: the signature guards the way from
6
+ * the server to the device (proxies, a CDN, a router), not a server that is broken into.
7
+ */
8
+ const mobileSigningKeyVariable = "PROTOBASE_MOBILE_SIGNING_KEY";
9
+ const minimumBits = 2048;
10
+ /**
11
+ * The signing key in `PROTOBASE_MOBILE_SIGNING_KEY`, `undefined` when it is unset. Its newlines may be written `\n`, as
12
+ * in an env file that holds one line per value. A key that is not PEM, not RSA or under 2048 bits stops startup.
13
+ */
14
+ const readSigningKey = (env) => {
15
+ const value = env[mobileSigningKeyVariable]?.trim();
16
+ if (!value) return void 0;
17
+ const key = parsePrivateKey(value.replace(/\\n/g, "\n"));
18
+ if (key.asymmetricKeyType !== "rsa") throw new Error(`${mobileSigningKeyVariable} must be an RSA private key, which the app's updater verifies, not ${key.asymmetricKeyType ?? "another kind of key"}`);
19
+ const bits = key.asymmetricKeyDetails?.modulusLength ?? 0;
20
+ if (bits < minimumBits) throw new Error(`${mobileSigningKeyVariable} must be an RSA key of at least ${minimumBits} bits, not ${bits}`);
21
+ return key;
22
+ };
23
+ const parsePrivateKey = (pem) => {
24
+ try {
25
+ return createPrivateKey(pem);
26
+ } catch (error) {
27
+ if (error instanceof Error && String(error.code).startsWith("ERR_OSSL_")) throw new Error(`${mobileSigningKeyVariable} is no PEM private key: give the whole key, from -----BEGIN PRIVATE KEY----- to its END line`);
28
+ throw error;
29
+ }
30
+ };
31
+ //#endregion
32
+ export { mobileSigningKeyVariable, readSigningKey };
package/dist/package.js CHANGED
@@ -1,4 +1,4 @@
1
1
  //#region package.json
2
- var version = "0.40.10";
2
+ var version = "0.41.0";
3
3
  //#endregion
4
4
  export { version };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@protobase/server",
3
- "version": "0.40.10",
3
+ "version": "0.41.0",
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": {
@@ -36,9 +36,9 @@
36
36
  "@better-auth/passkey": "^1.7.12",
37
37
  "@better-auth/sso": "^1.7.12",
38
38
  "@modelcontextprotocol/server": "^2.3.1",
39
- "@protobase/layout": "0.40.10",
40
- "@protobase/query": "0.40.10",
41
- "@protobase/schema": "0.40.10",
39
+ "@protobase/layout": "0.41.0",
40
+ "@protobase/query": "0.41.0",
41
+ "@protobase/schema": "0.41.0",
42
42
  "@react-email/render": "^2.1.0",
43
43
  "@scalar/hono-api-reference": "^0.12.10",
44
44
  "@sentry/core": "^11.7.0",