@protobase/server 0.59.2 → 0.59.3
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/better-auth/store/migrations-table.js +1 -1
- package/dist/create-admin.d.ts +4 -1
- package/dist/create-admin.js +17 -2
- package/dist/deps.d.ts +2 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +4 -1
- package/dist/internal.d.ts +2 -1
- package/dist/internal.js +2 -1
- package/dist/meta.d.ts +2 -0
- package/dist/meta.js +1 -0
- package/dist/notifications/caller.js +2 -2
- package/dist/notifications/deliver.js +14 -2
- package/dist/notifications/push-job.js +5 -3
- package/dist/notifications/schema.js +7 -4
- package/dist/package.js +1 -1
- package/dist/status/do-not-disturb.d.ts +9 -0
- package/dist/status/do-not-disturb.js +15 -0
- package/dist/status/options.d.ts +15 -0
- package/dist/status/options.js +25 -0
- package/dist/status/routes.js +38 -0
- package/dist/status/schema-sql.js +19 -0
- package/dist/status/schema.d.ts +12 -0
- package/dist/status/schema.js +33 -0
- package/dist/status/status-input.js +33 -0
- package/dist/status/status-store.js +39 -0
- package/package.json +4 -4
package/dist/create-admin.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { HistoryOptions } from "./history/options.js";
|
|
|
10
10
|
import { WebhooksOptions } from "./webhooks/table/options.js";
|
|
11
11
|
import { NotificationsOptions } from "./notifications/options.js";
|
|
12
12
|
import { CallsOptions } from "./calls/calls-options.js";
|
|
13
|
+
import { StatusOptions } from "./status/options.js";
|
|
13
14
|
import { SubscriptionDefinition } from "./webhooks/subscription.js";
|
|
14
15
|
import { FilesOptions } from "./files/options.js";
|
|
15
16
|
import { Difficulty } from "./forms/challenge.js";
|
|
@@ -55,6 +56,8 @@ export type CreateAdminInput = {
|
|
|
55
56
|
realtime?: RealtimeOptions;
|
|
56
57
|
/** Each user's devices and the endpoints that register them, from `notifications: {}` (see `NotificationsOptions`). */
|
|
57
58
|
notifications?: NotificationsOptions;
|
|
59
|
+
/** People's own status, from `status: {}` (see `StatusOptions`): it needs `auth` with organizations. */
|
|
60
|
+
status?: StatusOptions;
|
|
58
61
|
/** The media server behind the calls functions admit members to with `calls.join` (see `CallsOptions`). */
|
|
59
62
|
calls?: CallsOptions;
|
|
60
63
|
/**
|
|
@@ -96,5 +99,5 @@ export type CreateAdminInput = {
|
|
|
96
99
|
upgradeWebSocket?: UpgradeWebSocket;
|
|
97
100
|
};
|
|
98
101
|
/** The admin REST API as a Hono app. It uses only Web APIs, so it runs on Node, Workers and anywhere else Hono does. */
|
|
99
|
-
export declare const createAdmin: ({ resources: appResources, views: appViews, pages, userMenu, bottomBars, db, authenticate, auth, functions, secret, files: filesOptions, events, history: historyOptions, jobs, subscriptions, webhooks: webhooksOptions, realtime, notifications, calls, mobile, app: identity, icons, bundlePlugins, fieldTypes, options, formsDifficulty, commentUsers: givenCommentUsers, verifyToken, upgradeWebSocket }: CreateAdminInput) => Admin;
|
|
102
|
+
export declare const createAdmin: ({ resources: appResources, views: appViews, pages, userMenu, bottomBars, db, authenticate, auth, functions, secret, files: filesOptions, events, history: historyOptions, jobs, subscriptions, webhooks: webhooksOptions, realtime, notifications, status, calls, mobile, app: identity, icons, bundlePlugins, fieldTypes, options, formsDifficulty, commentUsers: givenCommentUsers, verifyToken, upgradeWebSocket }: CreateAdminInput) => Admin;
|
|
100
103
|
//#endregion
|
package/dist/create-admin.js
CHANGED
|
@@ -66,6 +66,10 @@ import { historyRoutes } from "./notifications/history-routes.js";
|
|
|
66
66
|
import { deviceRoutes } from "./notifications/routes.js";
|
|
67
67
|
import { notificationSenders, webPushKey } from "./notifications/senders-of.js";
|
|
68
68
|
import { pushJob } from "./notifications/push-job.js";
|
|
69
|
+
import { onDoNotDisturb } from "./status/do-not-disturb.js";
|
|
70
|
+
import { checkStatusAuth, statusNames } from "./status/options.js";
|
|
71
|
+
import { statusRoutes } from "./status/routes.js";
|
|
72
|
+
import { storeSchema } from "./better-auth/store/store-names.js";
|
|
69
73
|
import { liveSessions } from "./better-auth/live-sessions.js";
|
|
70
74
|
import { checkBottomBars, checkShell } from "./check-shell.js";
|
|
71
75
|
import { checkOwnerFields } from "./check-owners.js";
|
|
@@ -106,13 +110,14 @@ import { createScanGuard } from "./scan-guard.js";
|
|
|
106
110
|
import { failedJobs, failedJobsView } from "./jobs/failed-jobs.js";
|
|
107
111
|
import { Hono } from "hono";
|
|
108
112
|
import { accountPage, accountPageName, checkPages, settingsResources, withSettingsFields } from "@protobase/layout";
|
|
113
|
+
import { statusPath } from "@protobase/schema/internal";
|
|
109
114
|
//#region src/create-admin.ts
|
|
110
115
|
const declaredRoles = (auth, roles) => {
|
|
111
116
|
const names = [...auth?.roles ?? [], ...Object.keys(roles?.definitions ?? {})];
|
|
112
117
|
return names.length > 0 ? [...new Set(names)] : void 0;
|
|
113
118
|
};
|
|
114
119
|
/** The admin REST API as a Hono app. It uses only Web APIs, so it runs on Node, Workers and anywhere else Hono does. */
|
|
115
|
-
const createAdmin = ({ resources: appResources, views: appViews = [], pages = [], userMenu, bottomBars = [], db, authenticate, auth, functions = {}, secret, files: filesOptions, events, history: historyOptions, jobs = [], subscriptions = [], webhooks: webhooksOptions, realtime, notifications, calls, mobile, app: identity, icons, bundlePlugins = [], fieldTypes = {}, options = {}, formsDifficulty, commentUsers: givenCommentUsers, verifyToken, upgradeWebSocket }) => {
|
|
120
|
+
const createAdmin = ({ resources: appResources, views: appViews = [], pages = [], userMenu, bottomBars = [], db, authenticate, auth, functions = {}, secret, files: filesOptions, events, history: historyOptions, jobs = [], subscriptions = [], webhooks: webhooksOptions, realtime, notifications, status, calls, mobile, app: identity, icons, bundlePlugins = [], fieldTypes = {}, options = {}, formsDifficulty, commentUsers: givenCommentUsers, verifyToken, upgradeWebSocket }) => {
|
|
116
121
|
const basePath = options.basePath ?? "/api/v1";
|
|
117
122
|
const appModels = appResources.map((resource) => resource.toModel());
|
|
118
123
|
const withComments = appModels.some((model) => model.comments);
|
|
@@ -173,6 +178,14 @@ const createAdmin = ({ resources: appResources, views: appViews = [], pages = []
|
|
|
173
178
|
const errorReporting = errorReportingSettings(options.errorReporting, globalThis.process?.env ?? {});
|
|
174
179
|
const maps = mapSettings(options.maps, globalThis.process?.env ?? {});
|
|
175
180
|
const senders = notifications ? notificationSenders(notifications) : [];
|
|
181
|
+
if (status) checkStatusAuth(auth);
|
|
182
|
+
const statusTables = status && auth && {
|
|
183
|
+
statuses: statusNames(status),
|
|
184
|
+
members: {
|
|
185
|
+
schema: storeSchema(auth),
|
|
186
|
+
table: "member"
|
|
187
|
+
}
|
|
188
|
+
};
|
|
176
189
|
const pushKey = webPushKey(senders);
|
|
177
190
|
const locale = localeSetting(options.locale);
|
|
178
191
|
const plugins = pluginOptions(options.plugins, globalThis.process?.env ?? {}, bundlePlugins);
|
|
@@ -231,6 +244,7 @@ const createAdmin = ({ resources: appResources, views: appViews = [], pages = []
|
|
|
231
244
|
...maps && { maps },
|
|
232
245
|
...notifications && notifications.ui !== false && { bell: true },
|
|
233
246
|
...pushKey && { webPushKey: pushKey },
|
|
247
|
+
...statusTables && { status: true },
|
|
234
248
|
...locale && { locale },
|
|
235
249
|
plugins,
|
|
236
250
|
sources,
|
|
@@ -444,6 +458,7 @@ const createAdmin = ({ resources: appResources, views: appViews = [], pages = []
|
|
|
444
458
|
if (auth) removeHistoryWithUsers(auth, history);
|
|
445
459
|
app.route(historyPath(systemPath), guarded((routes) => void routes.route("/", historyRoutes(history, auth?.origins ?? []))));
|
|
446
460
|
}
|
|
461
|
+
if (statusTables) app.route(statusPath(systemPath), guarded((routes) => void routes.route("/", statusRoutes(db, statusTables))));
|
|
447
462
|
if (forms) app.route(`${systemPath}/forms`, formRoutes(deps, forms));
|
|
448
463
|
const feed = tableFeed({
|
|
449
464
|
db,
|
|
@@ -514,7 +529,7 @@ const createAdmin = ({ resources: appResources, views: appViews = [], pages = []
|
|
|
514
529
|
...withHistory && days !== void 0 ? [pruneHistoryJob(changes, days)] : [],
|
|
515
530
|
...mentions ? [mentions] : [],
|
|
516
531
|
...fill ? [fill] : [],
|
|
517
|
-
...notifications ? [pushJob(notifications, senders, auth && liveSessions(auth))] : []
|
|
532
|
+
...notifications ? [pushJob(notifications, senders, auth && liveSessions(auth), statusTables ? (jobDb, userIds) => onDoNotDisturb(jobDb, statusTables.statuses, userIds) : void 0)] : []
|
|
518
533
|
]
|
|
519
534
|
});
|
|
520
535
|
};
|
package/dist/deps.d.ts
CHANGED
|
@@ -53,6 +53,8 @@ export type Deps = {
|
|
|
53
53
|
maps?: MapSettings;
|
|
54
54
|
/** Whether the shell draws the notification bell: notifications are on and their `ui` is not false. */
|
|
55
55
|
bell?: boolean;
|
|
56
|
+
/** Whether people set their own status (`status: {}`), which `/meta` names to everyone. */
|
|
57
|
+
status?: boolean;
|
|
56
58
|
/** The VAPID public key browsers subscribe for Web Push with, when notifications have a web sender, which `/meta` names to everyone. */
|
|
57
59
|
webPushKey?: string;
|
|
58
60
|
/** The language the admin app's screens are in for everyone, which `/meta` names to everyone. */
|
package/dist/index.d.ts
CHANGED
|
@@ -22,6 +22,7 @@ import { HistoryStore, maxHistoryPage, maxUnreadCount } from "./notifications/hi
|
|
|
22
22
|
import { PushAnswer, PushSender } from "./notifications/sender.js";
|
|
23
23
|
import { DeviceColumn, HistoryColumn, NotificationsOptions } from "./notifications/options.js";
|
|
24
24
|
import { CallBackend, CallsOptions, LiveKitBackend } from "./calls/calls-options.js";
|
|
25
|
+
import { StatusOptions, statusNames } from "./status/options.js";
|
|
25
26
|
import { EventData, RunContext, SubscriptionDefinition, SubscriptionEvent, SubscriptionOptions, subscription } from "./webhooks/subscription.js";
|
|
26
27
|
import { Sharp, renderVariant, thumbHash } from "./files/images.js";
|
|
27
28
|
import { FileProvider, FileStore, StoredObject } from "./files/store.js";
|
|
@@ -118,6 +119,8 @@ import { sendNotification } from "./notifications/send-notification.js";
|
|
|
118
119
|
import { VapidSettings, readVapidSettings, vapidPrivateKeyVariable, vapidPublicKeyVariable, vapidSubjectVariable } from "./notifications/web-push/vapid-settings.js";
|
|
119
120
|
import { WebPushOptions, webPushSender } from "./notifications/web-push/web-push-sender.js";
|
|
120
121
|
import { generateVapidKeys } from "./notifications/web-push/keys.js";
|
|
122
|
+
import { checkStatusSchema, statusMigration } from "./status/schema.js";
|
|
123
|
+
import { onDoNotDisturb } from "./status/do-not-disturb.js";
|
|
121
124
|
import { MemberInput, NewOrganization, addAppRoleToOwners, addMember, createOrganization, listOrganizations, removeMember, setMemberRoles } from "./better-auth/organizations/host.js";
|
|
122
125
|
import { AssistantBackend, assistantProtocolRoutes } from "./assistant/protocol-routes.js";
|
|
123
126
|
import { fileConversationStore } from "./assistant/file-conversation-store.js";
|
|
@@ -176,4 +179,4 @@ import { DrainResult, JobsRuntime, StartJobsOptions, startJobs } from "./jobs/ru
|
|
|
176
179
|
import { jobsDrainApp } from "./jobs/drain-route.js";
|
|
177
180
|
import { checkJobsSchema, jobsSchema, jobsSchemaMigration } from "./jobs/schema.js";
|
|
178
181
|
import { AdminAuth, CreateAuthOptions, UserCreatedHook, normalizeRoles } from "./better-auth/create-auth.js";
|
|
179
|
-
export { type Admin, type AdminAuth, type AdminOptions, type AggregateParams, type ApiFunction, type ApiKeys, type ApiKeysOptions, type ApnsEnvironment, type AppIcons, type AppIdentity, type AppPoolConfig, type ApplyOptions, type ApplyResult, type ApprovalAnswer, type ApprovalRequest, type ArgumentsPayload, type AssistantBackend, type AssistantOptions, type AssistantSettings, type AssistantTool, type AuditEvent, type AuditQueue, type AuthHooks, type AuthSettings, type AuthenticateSettings, type Authenticator, type BaseToolContext, type BatchOperation, type BatchResult, type BetterAuthAuthenticator, type BetterAuthAuthenticatorOptions, type CallBackend, type CallMember, type Caller, type CallerOptions, type CallsAccess, type CallsOptions, type CapturedRow, type CascadingKey, type ChannelBroadcast, type ChannelSender, type ChatMessage, type ChatTextPart, type CheckAiInput, type CleanupEntry, type CleanupReport, type CleanupSchedule, type CoalescedChange, type CommentItem, type CommentToolsInput, type CommentUsers, type Completion, type CompletionOptions, ConnectionsBusy, type Conversation, type ConversationRecord, type ConversationStore, type ConversationsOptions, type CorsOptions, type CorsOrigins, type CreateAdminInput, type CreateAuthOptions, type DecryptField, type Device, type DeviceColumn, type DeviceKind, type DeviceStore, DeviceStoreError, type DrainResult, type EmailComponent, type EmailOptions, type EmailTemplate, type EncryptionStatus, type EncryptionTools, type EncryptionToolsInput, type EnqueueJob, type EnqueueOptions, type ErrorReporter, type ErrorReportingOptions, type ErrorReportingSettings, type EventData, type EventsOptions, FatalJobError, type FieldApplied, type FieldDecrypter, type FieldReader, type FieldStatus, type FileCleanupInput, type FileLimits, type FileObject, type FileProvider, type FileProviderSettings, 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 HistoryColumn, type HistoryOptions, type HistoryPosition, type HistoryStore, HistoryStoreError, type HookContext, HttpProblem, type IssueTokenOptions, type JobContext, type JobDeclaration, type JobDefinition, type JobName, type JobOptions, type JobPayload, type JobRun, type JobsRuntime, type JsonObject, type JsonValue, type JwtOptions, type JwtVerifyOptions, type KeyFunctionContext, type LinkedAccount, type ListParams, type LiveKitBackend, type LocalFilesOptions, type MailMessage, type Mailer, type MapLayer, type MapOptions, type MapSettings, type McpApprovals, type McpGateInput, type McpHandlerInput, type McpRoutesInput, type McpServerInput, type McpToolsInput, type McpWrites, type MemberInput, type Membership, MissingCredentials, type MobileConfig, ModelError, type ModelSettings, type NewDevice, type NewNotification, type NewOrganization, type NewUser, type NotificationRecipients, NotificationSendError, type NotificationsOptions, type OperatorProvider, type OrganizationRole, type OrganizationsOptions, type PgModule, type PipelineHook, type PlatformSignIn, type PluginOption, type PluginRef, type PngIcon, type ProtectedFunctionOptions, type PublicFunctionContext, type PublicFunctionEnv, type PublicFunctionHandler, type PublicFunctionOptions, type PushAnswer, type PushMessage, PushMessageError, type PushSender, type QueryOptions, type QueryResult, type ReadableResources, type RealtimeOptions, type RealtimePrune, type ReasoningDetail, type ReasoningEffort, type RebindOption, type RecordGroups, type RecordKey, 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 SendNotification, 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 StoredNotification, type StoredObject, type StoredOrganization, type StoredUser, type StrayValues, type SubscriptionDefinition, type SubscriptionEvent, type SubscriptionOptions, type TenantValue, type ToolAnnotations, type ToolCall, type ToolComment, type ToolCommentPage, type ToolComments, type ToolContext, type ToolRecord, type ToolRecords, type ToolSpec, type TrustedApp, type TurnOptions, type UnreadableEntry, type Urgency, type User, type UserCreatedHook, type VapidSettings, type ViewSource, type WebPushFields, type WebPushOptions, type WebhooksOptions, type WidgetOptions, type WriteEvent, type WriteOperation, type WriteRecordToolsInput, addAppRoleToOwners, addCommentTool, addMember, apiKeyCaller, apiKeySettings, apiKeyVariables, appNameOver, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaDrift, authSchemaMigration, authStoreTables, backgroundSettled, badRequest, batchWriteTool, beforeSignInPluginsVariable, betterAuthTokens, capSocketMessages, changesMigration, channelSendsMigration, checkAi, checkAuthSchema, checkChanges, checkChannelSends, checkComments, checkComputed, checkJobsSchema, checkNotificationsSchema, checkQueryRunner, checkRealtimeRelay, checkResourcesOutsideAuthStore, checkSourceNames, checkViewQueries, checkWebhooksSchema, coalesce, commentTools, commentsMigration, computedMigration, configExports, consoleAuditQueue, conversationKey, countRecordsTool, createAdmin, createConversations, createEncryptionTools, createFileCleanup, createOrganization, createRecordTool, createUser, defaultAppName, defaultHistoryPage, defaultSignInPolicy, defineEmail, defineFunction, defineJob, defineTool, deleteRecordTool, deleteUser, describeResourceTool, deviceIdParam, deviceStore, devicesPath, enqueueJob, errorReportingSettings, errorReportingVariable, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, formatUri, functionApp, generateVapidKeys, getRecordTool, goTruePath, handleDroppedConnections, hasUsers, historyIdParam, historyIdPattern, historyLimits, historyPath, historyStore, honorSocketMessageCaps, imageSize, isBackgroundWork, isCaptured, isJobDefinition, isOAuthWellKnownPath, isSchedule, issueToken, jobDefinitions, jobsDrainApp, jobsSchema, jobsSchemaMigration, jwtAuthenticator, jwtVerifier, limitedFileStore, listCommentsTool, listOrganizations, listRecordsTool, listResourcesTool, listUsers, localFileStore, longQueryTimeoutMs, mapSettings, mapStyleUrl, mapStyleVariable, maxDevicesPerUser, maxHistoryPage, maxMessageBytes, maxRecipients, maxTokenTtlSeconds, maxTtlSeconds, maxUnreadCount, maxWidgetPropsBytes, mcpApprovals, mcpGate, mcpHandler, mcpIcons, mcpInstructions, mcpRoutes, mcpServer, mcpToolContext, mcpTools, migrationCommand, newWebhookSecret, normalizeRoles, notFound, notificationSenders, notificationsMigration, oauthWellKnownPaths, objectKey, parseColumns, parseRoles, passwordLength, pluginOptions, pluginsBeforeSignIn, pluginsFor, pluginsVariable, pngIcons, poolWaitMs, postgresDeviceStore, postgresHistoryStore, processor, pushMessage, queryRunnerMigration, readDbConnections, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readRecordTools, readVapidSettings, readWriteQueryTool, readableResources, realtimeLimits, realtimeRelayMigration, recordOf, refusalOf, refused, relayed, removeDevicesWithUsers, removeHistoryWithUsers, removeMember, renderVariant, requestApproval, requireEnv, resourceChange, roleChoices, runInBackground, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, s3FileStore, seesAssistant, seesRuntime, sendNotification, sessionOf, setMemberRoles, setUserBanned, setUserRole, shownAppName, signInPolicyMethods, socketMessageCapOf, sourceOptions, sourcesFor, sourcesVariable, sqlSends, startErrorReporting, startJobs, storageCleanupSchedule, storageProvider, storeFile, storeUsage, streamChatCompletion, subscription, supabasePath, thumbHash, unauthorized, updateRecordTool, urgencies, vapidPrivateKeyVariable, vapidPublicKeyVariable, vapidSubjectVariable, variantPath, verifySignature, verifyStandardWebhook, waitUntil, wantsLongQueries, webManifestPath, webPushSender, webauthnPath, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps, writeRecordTools };
|
|
182
|
+
export { type Admin, type AdminAuth, type AdminOptions, type AggregateParams, type ApiFunction, type ApiKeys, type ApiKeysOptions, type ApnsEnvironment, type AppIcons, type AppIdentity, type AppPoolConfig, type ApplyOptions, type ApplyResult, type ApprovalAnswer, type ApprovalRequest, type ArgumentsPayload, type AssistantBackend, type AssistantOptions, type AssistantSettings, type AssistantTool, type AuditEvent, type AuditQueue, type AuthHooks, type AuthSettings, type AuthenticateSettings, type Authenticator, type BaseToolContext, type BatchOperation, type BatchResult, type BetterAuthAuthenticator, type BetterAuthAuthenticatorOptions, type CallBackend, type CallMember, type Caller, type CallerOptions, type CallsAccess, type CallsOptions, type CapturedRow, type CascadingKey, type ChannelBroadcast, type ChannelSender, type ChatMessage, type ChatTextPart, type CheckAiInput, type CleanupEntry, type CleanupReport, type CleanupSchedule, type CoalescedChange, type CommentItem, type CommentToolsInput, type CommentUsers, type Completion, type CompletionOptions, ConnectionsBusy, type Conversation, type ConversationRecord, type ConversationStore, type ConversationsOptions, type CorsOptions, type CorsOrigins, type CreateAdminInput, type CreateAuthOptions, type DecryptField, type Device, type DeviceColumn, type DeviceKind, type DeviceStore, DeviceStoreError, type DrainResult, type EmailComponent, type EmailOptions, type EmailTemplate, type EncryptionStatus, type EncryptionTools, type EncryptionToolsInput, type EnqueueJob, type EnqueueOptions, type ErrorReporter, type ErrorReportingOptions, type ErrorReportingSettings, type EventData, type EventsOptions, FatalJobError, type FieldApplied, type FieldDecrypter, type FieldReader, type FieldStatus, type FileCleanupInput, type FileLimits, type FileObject, type FileProvider, type FileProviderSettings, 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 HistoryColumn, type HistoryOptions, type HistoryPosition, type HistoryStore, HistoryStoreError, type HookContext, HttpProblem, type IssueTokenOptions, type JobContext, type JobDeclaration, type JobDefinition, type JobName, type JobOptions, type JobPayload, type JobRun, type JobsRuntime, type JsonObject, type JsonValue, type JwtOptions, type JwtVerifyOptions, type KeyFunctionContext, type LinkedAccount, type ListParams, type LiveKitBackend, type LocalFilesOptions, type MailMessage, type Mailer, type MapLayer, type MapOptions, type MapSettings, type McpApprovals, type McpGateInput, type McpHandlerInput, type McpRoutesInput, type McpServerInput, type McpToolsInput, type McpWrites, type MemberInput, type Membership, MissingCredentials, type MobileConfig, ModelError, type ModelSettings, type NewDevice, type NewNotification, type NewOrganization, type NewUser, type NotificationRecipients, NotificationSendError, type NotificationsOptions, type OperatorProvider, type OrganizationRole, type OrganizationsOptions, type PgModule, type PipelineHook, type PlatformSignIn, type PluginOption, type PluginRef, type PngIcon, type ProtectedFunctionOptions, type PublicFunctionContext, type PublicFunctionEnv, type PublicFunctionHandler, type PublicFunctionOptions, type PushAnswer, type PushMessage, PushMessageError, type PushSender, type QueryOptions, type QueryResult, type ReadableResources, type RealtimeOptions, type RealtimePrune, type ReasoningDetail, type ReasoningEffort, type RebindOption, type RecordGroups, type RecordKey, 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 SendNotification, 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 StatusOptions, type StoreFileInput, type StoredMember, type StoredNotification, type StoredObject, type StoredOrganization, type StoredUser, type StrayValues, type SubscriptionDefinition, type SubscriptionEvent, type SubscriptionOptions, type TenantValue, type ToolAnnotations, type ToolCall, type ToolComment, type ToolCommentPage, type ToolComments, type ToolContext, type ToolRecord, type ToolRecords, type ToolSpec, type TrustedApp, type TurnOptions, type UnreadableEntry, type Urgency, type User, type UserCreatedHook, type VapidSettings, type ViewSource, type WebPushFields, type WebPushOptions, type WebhooksOptions, type WidgetOptions, type WriteEvent, type WriteOperation, type WriteRecordToolsInput, addAppRoleToOwners, addCommentTool, addMember, apiKeyCaller, apiKeySettings, apiKeyVariables, appNameOver, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaDrift, authSchemaMigration, authStoreTables, backgroundSettled, badRequest, batchWriteTool, beforeSignInPluginsVariable, betterAuthTokens, capSocketMessages, changesMigration, channelSendsMigration, checkAi, checkAuthSchema, checkChanges, checkChannelSends, checkComments, checkComputed, checkJobsSchema, checkNotificationsSchema, checkQueryRunner, checkRealtimeRelay, checkResourcesOutsideAuthStore, checkSourceNames, checkStatusSchema, checkViewQueries, checkWebhooksSchema, coalesce, commentTools, commentsMigration, computedMigration, configExports, consoleAuditQueue, conversationKey, countRecordsTool, createAdmin, createConversations, createEncryptionTools, createFileCleanup, createOrganization, createRecordTool, createUser, defaultAppName, defaultHistoryPage, defaultSignInPolicy, defineEmail, defineFunction, defineJob, defineTool, deleteRecordTool, deleteUser, describeResourceTool, deviceIdParam, deviceStore, devicesPath, enqueueJob, errorReportingSettings, errorReportingVariable, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, formatUri, functionApp, generateVapidKeys, getRecordTool, goTruePath, handleDroppedConnections, hasUsers, historyIdParam, historyIdPattern, historyLimits, historyPath, historyStore, honorSocketMessageCaps, imageSize, isBackgroundWork, isCaptured, isJobDefinition, isOAuthWellKnownPath, isSchedule, issueToken, jobDefinitions, jobsDrainApp, jobsSchema, jobsSchemaMigration, jwtAuthenticator, jwtVerifier, limitedFileStore, listCommentsTool, listOrganizations, listRecordsTool, listResourcesTool, listUsers, localFileStore, longQueryTimeoutMs, mapSettings, mapStyleUrl, mapStyleVariable, maxDevicesPerUser, maxHistoryPage, maxMessageBytes, maxRecipients, maxTokenTtlSeconds, maxTtlSeconds, maxUnreadCount, maxWidgetPropsBytes, mcpApprovals, mcpGate, mcpHandler, mcpIcons, mcpInstructions, mcpRoutes, mcpServer, mcpToolContext, mcpTools, migrationCommand, newWebhookSecret, normalizeRoles, notFound, notificationSenders, notificationsMigration, oauthWellKnownPaths, objectKey, onDoNotDisturb, parseColumns, parseRoles, passwordLength, pluginOptions, pluginsBeforeSignIn, pluginsFor, pluginsVariable, pngIcons, poolWaitMs, postgresDeviceStore, postgresHistoryStore, processor, pushMessage, queryRunnerMigration, readDbConnections, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readRecordTools, readVapidSettings, readWriteQueryTool, readableResources, realtimeLimits, realtimeRelayMigration, recordOf, refusalOf, refused, relayed, removeDevicesWithUsers, removeHistoryWithUsers, removeMember, renderVariant, requestApproval, requireEnv, resourceChange, roleChoices, runInBackground, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, s3FileStore, seesAssistant, seesRuntime, sendNotification, sessionOf, setMemberRoles, setUserBanned, setUserRole, shownAppName, signInPolicyMethods, socketMessageCapOf, sourceOptions, sourcesFor, sourcesVariable, sqlSends, startErrorReporting, startJobs, statusMigration, statusNames, storageCleanupSchedule, storageProvider, storeFile, storeUsage, streamChatCompletion, subscription, supabasePath, thumbHash, unauthorized, updateRecordTool, urgencies, vapidPrivateKeyVariable, vapidPublicKeyVariable, vapidSubjectVariable, variantPath, verifySignature, verifyStandardWebhook, waitUntil, wantsLongQueries, webManifestPath, webPushSender, webauthnPath, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps, writeRecordTools };
|
package/dist/index.js
CHANGED
|
@@ -78,6 +78,8 @@ import { readVapidSettings, vapidPrivateKeyVariable, vapidPublicKeyVariable, vap
|
|
|
78
78
|
import { webPushSender } from "./notifications/web-push/web-push-sender.js";
|
|
79
79
|
import { notificationSenders } from "./notifications/senders-of.js";
|
|
80
80
|
import { NotificationSendError, maxMessageBytes, maxRecipients } from "./notifications/send-input.js";
|
|
81
|
+
import { onDoNotDisturb } from "./status/do-not-disturb.js";
|
|
82
|
+
import { statusNames } from "./status/options.js";
|
|
81
83
|
import { errorReportingSettings, errorReportingVariable } from "./error-reporting/error-reporting-settings.js";
|
|
82
84
|
import { storageCleanupSchedule } from "./files/cleanup-schedule.js";
|
|
83
85
|
import { limitedFileStore, storeUsage } from "./files/limits.js";
|
|
@@ -113,6 +115,7 @@ import { channelSendsMigration, checkChannelSends } from "./channels/sends-schem
|
|
|
113
115
|
import { checkAi } from "./ai/check-ai.js";
|
|
114
116
|
import { checkNotificationsSchema, notificationsMigration } from "./notifications/schema.js";
|
|
115
117
|
import { sendNotification } from "./notifications/send-notification.js";
|
|
118
|
+
import { checkStatusSchema, statusMigration } from "./status/schema.js";
|
|
116
119
|
import { maxWidgetPropsBytes, widget } from "./assistant/widget.js";
|
|
117
120
|
import { checkQueryRunner, queryRunnerMigration } from "./assistant/query-runner/query-runner.js";
|
|
118
121
|
import { startErrorReporting } from "./error-reporting/error-reporter.js";
|
|
@@ -128,4 +131,4 @@ import { enqueueJob } from "./jobs/declared-job.js";
|
|
|
128
131
|
import { defineEmail } from "./mail/define-email.js";
|
|
129
132
|
import { startJobs } from "./jobs/runtime.js";
|
|
130
133
|
import { jobsDrainApp } from "./jobs/drain-route.js";
|
|
131
|
-
export { ConnectionsBusy, DeviceStoreError, FatalJobError, HistoryStoreError, HttpProblem, MissingCredentials, ModelError, NotificationSendError, PushMessageError, addAppRoleToOwners, addCommentTool, addMember, apiKeyCaller, apiKeySettings, apiKeyVariables, appNameOver, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaDrift, authSchemaMigration, authStoreTables, backgroundSettled, badRequest, batchWriteTool, beforeSignInPluginsVariable, betterAuthTokens, capSocketMessages, changesMigration, channelSendsMigration, checkAi, checkAuthSchema, checkChanges, checkChannelSends, checkComments, checkComputed, checkJobsSchema, checkNotificationsSchema, checkQueryRunner, checkRealtimeRelay, checkResourcesOutsideAuthStore, checkSourceNames, checkViewQueries, checkWebhooksSchema, coalesce, commentTools, commentsMigration, computedMigration, configExports, consoleAuditQueue, conversationKey, countRecordsTool, createAdmin, createConversations, createEncryptionTools, createFileCleanup, createOrganization, createRecordTool, createUser, defaultAppName, defaultHistoryPage, defaultSignInPolicy, defineEmail, defineFunction, defineJob, defineTool, deleteRecordTool, deleteUser, describeResourceTool, deviceIdParam, deviceStore, devicesPath, enqueueJob, errorReportingSettings, errorReportingVariable, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, formatUri, functionApp, generateVapidKeys, getRecordTool, goTruePath, handleDroppedConnections, hasUsers, historyIdParam, historyIdPattern, historyLimits, historyPath, historyStore, honorSocketMessageCaps, imageSize, isBackgroundWork, isCaptured, isJobDefinition, isOAuthWellKnownPath, isSchedule, issueToken, jobDefinitions, jobsDrainApp, jobsSchema, jobsSchemaMigration, jwtAuthenticator, jwtVerifier, limitedFileStore, listCommentsTool, listOrganizations, listRecordsTool, listResourcesTool, listUsers, localFileStore, longQueryTimeoutMs, mapSettings, mapStyleUrl, mapStyleVariable, maxDevicesPerUser, maxHistoryPage, maxMessageBytes, maxRecipients, maxTokenTtlSeconds, maxTtlSeconds, maxUnreadCount, maxWidgetPropsBytes, mcpApprovals, mcpGate, mcpHandler, mcpIcons, mcpInstructions, mcpRoutes, mcpServer, mcpToolContext, mcpTools, migrationCommand, newWebhookSecret, normalizeRoles, notFound, notificationSenders, notificationsMigration, oauthWellKnownPaths, objectKey, parseColumns, parseRoles, passwordLength, pluginOptions, pluginsBeforeSignIn, pluginsFor, pluginsVariable, pngIcons, poolWaitMs, postgresDeviceStore, postgresHistoryStore, processor, pushMessage, queryRunnerMigration, readDbConnections, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readRecordTools, readVapidSettings, readWriteQueryTool, readableResources, realtimeLimits, realtimeRelayMigration, recordOf, refusalOf, refused, relayed, removeDevicesWithUsers, removeHistoryWithUsers, removeMember, renderVariant, requestApproval, requireEnv, resourceChange, roleChoices, runInBackground, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, s3FileStore, seesAssistant, seesRuntime, sendNotification, sessionOf, setMemberRoles, setUserBanned, setUserRole, shownAppName, signInPolicyMethods, socketMessageCapOf, sourceOptions, sourcesFor, sourcesVariable, sqlSends, startErrorReporting, startJobs, storageCleanupSchedule, storageProvider, storeFile, storeUsage, streamChatCompletion, subscription, supabasePath, thumbHash, unauthorized, updateRecordTool, urgencies, vapidPrivateKeyVariable, vapidPublicKeyVariable, vapidSubjectVariable, variantPath, verifySignature, verifyStandardWebhook, waitUntil, wantsLongQueries, webManifestPath, webPushSender, webauthnPath, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps, writeRecordTools };
|
|
134
|
+
export { ConnectionsBusy, DeviceStoreError, FatalJobError, HistoryStoreError, HttpProblem, MissingCredentials, ModelError, NotificationSendError, PushMessageError, addAppRoleToOwners, addCommentTool, addMember, apiKeyCaller, apiKeySettings, apiKeyVariables, appNameOver, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaDrift, authSchemaMigration, authStoreTables, backgroundSettled, badRequest, batchWriteTool, beforeSignInPluginsVariable, betterAuthTokens, capSocketMessages, changesMigration, channelSendsMigration, checkAi, checkAuthSchema, checkChanges, checkChannelSends, checkComments, checkComputed, checkJobsSchema, checkNotificationsSchema, checkQueryRunner, checkRealtimeRelay, checkResourcesOutsideAuthStore, checkSourceNames, checkStatusSchema, checkViewQueries, checkWebhooksSchema, coalesce, commentTools, commentsMigration, computedMigration, configExports, consoleAuditQueue, conversationKey, countRecordsTool, createAdmin, createConversations, createEncryptionTools, createFileCleanup, createOrganization, createRecordTool, createUser, defaultAppName, defaultHistoryPage, defaultSignInPolicy, defineEmail, defineFunction, defineJob, defineTool, deleteRecordTool, deleteUser, describeResourceTool, deviceIdParam, deviceStore, devicesPath, enqueueJob, errorReportingSettings, errorReportingVariable, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, formatUri, functionApp, generateVapidKeys, getRecordTool, goTruePath, handleDroppedConnections, hasUsers, historyIdParam, historyIdPattern, historyLimits, historyPath, historyStore, honorSocketMessageCaps, imageSize, isBackgroundWork, isCaptured, isJobDefinition, isOAuthWellKnownPath, isSchedule, issueToken, jobDefinitions, jobsDrainApp, jobsSchema, jobsSchemaMigration, jwtAuthenticator, jwtVerifier, limitedFileStore, listCommentsTool, listOrganizations, listRecordsTool, listResourcesTool, listUsers, localFileStore, longQueryTimeoutMs, mapSettings, mapStyleUrl, mapStyleVariable, maxDevicesPerUser, maxHistoryPage, maxMessageBytes, maxRecipients, maxTokenTtlSeconds, maxTtlSeconds, maxUnreadCount, maxWidgetPropsBytes, mcpApprovals, mcpGate, mcpHandler, mcpIcons, mcpInstructions, mcpRoutes, mcpServer, mcpToolContext, mcpTools, migrationCommand, newWebhookSecret, normalizeRoles, notFound, notificationSenders, notificationsMigration, oauthWellKnownPaths, objectKey, onDoNotDisturb, parseColumns, parseRoles, passwordLength, pluginOptions, pluginsBeforeSignIn, pluginsFor, pluginsVariable, pngIcons, poolWaitMs, postgresDeviceStore, postgresHistoryStore, processor, pushMessage, queryRunnerMigration, readDbConnections, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readRecordTools, readVapidSettings, readWriteQueryTool, readableResources, realtimeLimits, realtimeRelayMigration, recordOf, refusalOf, refused, relayed, removeDevicesWithUsers, removeHistoryWithUsers, removeMember, renderVariant, requestApproval, requireEnv, resourceChange, roleChoices, runInBackground, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, s3FileStore, seesAssistant, seesRuntime, sendNotification, sessionOf, setMemberRoles, setUserBanned, setUserRole, shownAppName, signInPolicyMethods, socketMessageCapOf, sourceOptions, sourcesFor, sourcesVariable, sqlSends, startErrorReporting, startJobs, statusMigration, statusNames, storageCleanupSchedule, storageProvider, storeFile, storeUsage, streamChatCompletion, subscription, supabasePath, thumbHash, unauthorized, updateRecordTool, urgencies, vapidPrivateKeyVariable, vapidPublicKeyVariable, vapidSubjectVariable, variantPath, verifySignature, verifyStandardWebhook, waitUntil, wantsLongQueries, webManifestPath, webPushSender, webauthnPath, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps, writeRecordTools };
|
package/dist/internal.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { checkNotificationsOptions } from "./notifications/options.js";
|
|
2
2
|
import { checkCallsOptions } from "./calls/calls-options.js";
|
|
3
|
+
import { checkStatusOptions } from "./status/options.js";
|
|
3
4
|
import { isSubscription } from "./webhooks/subscription.js";
|
|
4
5
|
import { appPool, longQueryPool } from "./app-pool.js";
|
|
5
6
|
import { AppAuthInput, appAuthOptions, buildAppAuth } from "./better-auth/auth-settings.js";
|
|
@@ -11,4 +12,4 @@ import { fileProviderOf } from "./files/provider-settings.js";
|
|
|
11
12
|
import { declaredJob } from "./jobs/declared-job.js";
|
|
12
13
|
import { AuthPlugins, buildAuth, createAuth, loadAuthPlugins } from "./better-auth/create-auth.js";
|
|
13
14
|
import { webhooksModule } from "./webhooks/table/webhooks-module.js";
|
|
14
|
-
export { type AppAuthInput, type AuthPlugins, appAuthOptions, appPool, betterAuthAuthenticator, buildAppAuth, buildAuth, buildAuthenticator, checkCallsOptions, checkNotificationsOptions, createAuth, declaredJob, fileProviderOf, isSubscription, loadAuthPlugins, localFiles, longQueryPool, s3Files, webhooksModule };
|
|
15
|
+
export { type AppAuthInput, type AuthPlugins, appAuthOptions, appPool, betterAuthAuthenticator, buildAppAuth, buildAuth, buildAuthenticator, checkCallsOptions, checkNotificationsOptions, checkStatusOptions, createAuth, declaredJob, fileProviderOf, isSubscription, loadAuthPlugins, localFiles, longQueryPool, s3Files, webhooksModule };
|
package/dist/internal.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { buildAuth, createAuth, loadAuthPlugins } from "./better-auth/create-auth.js";
|
|
2
2
|
import { isSubscription } from "./webhooks/subscription.js";
|
|
3
3
|
import { checkNotificationsOptions } from "./notifications/options.js";
|
|
4
|
+
import { checkStatusOptions } from "./status/options.js";
|
|
4
5
|
import { localFiles } from "./files/local-files.js";
|
|
5
6
|
import { s3Files } from "./files/s3-files.js";
|
|
6
7
|
import { appPool, longQueryPool } from "./app-pool.js";
|
|
@@ -11,4 +12,4 @@ import { appAuthOptions, buildAppAuth } from "./better-auth/auth-settings.js";
|
|
|
11
12
|
import { buildAuthenticator } from "./better-auth/authenticate-settings.js";
|
|
12
13
|
import { webhooksModule } from "./webhooks/table/webhooks-module.js";
|
|
13
14
|
import { checkCallsOptions } from "./calls/calls-options.js";
|
|
14
|
-
export { appAuthOptions, appPool, betterAuthAuthenticator, buildAppAuth, buildAuth, buildAuthenticator, checkCallsOptions, checkNotificationsOptions, createAuth, declaredJob, fileProviderOf, isSubscription, loadAuthPlugins, localFiles, longQueryPool, s3Files, webhooksModule };
|
|
15
|
+
export { appAuthOptions, appPool, betterAuthAuthenticator, buildAppAuth, buildAuth, buildAuthenticator, checkCallsOptions, checkNotificationsOptions, checkStatusOptions, createAuth, declaredJob, fileProviderOf, isSubscription, loadAuthPlugins, localFiles, longQueryPool, s3Files, webhooksModule };
|
package/dist/meta.d.ts
CHANGED
|
@@ -59,6 +59,8 @@ export type CallerMeta = {
|
|
|
59
59
|
bell?: true;
|
|
60
60
|
webPushKey?: string;
|
|
61
61
|
};
|
|
62
|
+
/** With `status: {}`: people set their own status, and see the statuses of those they share an organization with. */
|
|
63
|
+
status?: true;
|
|
62
64
|
/** The language the admin app's screens are in, when the app sets one for everyone. */
|
|
63
65
|
locale?: Locale;
|
|
64
66
|
/** The hosts besides the app's own whose images the admin app draws with the page's referrer, when there are any. */
|
package/dist/meta.js
CHANGED
|
@@ -97,6 +97,7 @@ const createMeta = (deps, appName, appIcon) => {
|
|
|
97
97
|
...deps.bell && { bell: true },
|
|
98
98
|
...deps.webPushKey && { webPushKey: deps.webPushKey }
|
|
99
99
|
} },
|
|
100
|
+
...deps.status && { status: true },
|
|
100
101
|
...deps.locale && { locale: deps.locale },
|
|
101
102
|
...deps.files && deps.files.trustedHosts.length > 0 && { files: { trustedHosts: deps.files.trustedHosts } },
|
|
102
103
|
...plugins.length > 0 && { plugins },
|
|
@@ -5,8 +5,8 @@ import { forbidden } from "../problem.js";
|
|
|
5
5
|
* Refused: visitors, outside apps acting for the user (an MCP client), and every session whose authenticator does not
|
|
6
6
|
* mark it as the user's own sign-in (`Session.signIn`), staff signed in as the user included.
|
|
7
7
|
*/
|
|
8
|
-
const ownSignInUser = (session) => {
|
|
9
|
-
const refuse = () => forbidden("own-sign-in-only",
|
|
8
|
+
const ownSignInUser = (session, what = "reach their devices and notifications") => {
|
|
9
|
+
const refuse = () => forbidden("own-sign-in-only", `Only someone signed in as themselves can ${what}`);
|
|
10
10
|
if (session.anonymous || session.client || session.signIn?.staff !== false) throw refuse();
|
|
11
11
|
const userId = String(session.user.id);
|
|
12
12
|
if (userId === "" || session.user.id === void 0 || session.user.id === null) throw refuse();
|
|
@@ -16,7 +16,8 @@ const listed = (counts) => [...counts].map(([method, count]) => `${count} in ${m
|
|
|
16
16
|
* One try of a send, resuming from `given`: for a user send, each user's history entry when the send is for the inbox
|
|
17
17
|
* (once, as `record` marks it), then the user's devices as the store lists them now; for a device send, the devices
|
|
18
18
|
* with those ids now. A device the store gives that is not the user's, or not one asked for, gets nothing. With
|
|
19
|
-
* `liveSessions`, a device whose session no longer stands is removed instead of pushed to
|
|
19
|
+
* `liveSessions`, a device whose session no longer stands is removed instead of pushed to; with `quiet`, a device of a
|
|
20
|
+
* user on do not disturb gets nothing, now or on a later try, though the history keeps the entry. Each kind's
|
|
20
21
|
* devices go to its sender in one batch; a kind without a sender is skipped. An answer of `gone` removes the device,
|
|
21
22
|
* `retry` leaves it to the next try once and gives up on it the second time, and any other is done. A sender that
|
|
22
23
|
* throws leaves its batch to the next try. The try then throws, counting what is left, so the job retries it, or
|
|
@@ -70,8 +71,19 @@ const deliver = async ({ to, message }, given, steps) => {
|
|
|
70
71
|
}
|
|
71
72
|
return pending.filter(stands);
|
|
72
73
|
};
|
|
74
|
+
const undisturbed = async (devices) => {
|
|
75
|
+
const { quiet } = steps;
|
|
76
|
+
const pending = devices.filter((device) => !done.has(device.id));
|
|
77
|
+
if (!quiet || pending.length === 0) return pending;
|
|
78
|
+
const held = await quiet([...new Set(pending.map((device) => device.userId))]);
|
|
79
|
+
const kept = pending.filter((device) => !held.has(device.userId));
|
|
80
|
+
if (kept.length === pending.length) return kept;
|
|
81
|
+
for (const device of pending.filter((device) => held.has(device.userId))) markDone(device.id);
|
|
82
|
+
await steps.save(progress);
|
|
83
|
+
return kept;
|
|
84
|
+
};
|
|
73
85
|
const push = async (given) => {
|
|
74
|
-
const devices = await standing(given);
|
|
86
|
+
const devices = await undisturbed(await standing(given));
|
|
75
87
|
for (const device of devices) if (!steps.senders.some((sender) => sender.kinds.includes(device.kind))) markDone(device.id);
|
|
76
88
|
for (const sender of steps.senders) {
|
|
77
89
|
const batch = devices.filter((device) => sender.kinds.includes(device.kind) && !done.has(device.id));
|
|
@@ -26,11 +26,12 @@ const recordOnce = (db, options, job) => async (userId, entry, progress) => {
|
|
|
26
26
|
};
|
|
27
27
|
/**
|
|
28
28
|
* The job that delivers what `sendNotification` sends, through `senders` (guarded, see `notificationSenders`) and the
|
|
29
|
-
* stores `options` configure; with `liveSessions`, as with Protobase's auth, only to devices whose session stands
|
|
29
|
+
* stores `options` configure; with `liveSessions`, as with Protobase's auth, only to devices whose session stands, and
|
|
30
|
+
* with `quiet`, as with `status`, to no device of a user on do not disturb. A
|
|
30
31
|
* try resumes where the last one stopped (see `deliver`); once delivered, the job keeps no message. A few tries only: a
|
|
31
32
|
* device that keeps asking for a retry is given up on the second time.
|
|
32
33
|
*/
|
|
33
|
-
const pushJob = (options, senders, liveSessions) => defineJob({
|
|
34
|
+
const pushJob = (options, senders, liveSessions, quiet) => defineJob({
|
|
34
35
|
name: pushJobName,
|
|
35
36
|
payload: pushPayload,
|
|
36
37
|
maxAttempts: 5,
|
|
@@ -44,7 +45,8 @@ const pushJob = (options, senders, liveSessions) => defineJob({
|
|
|
44
45
|
save: (progress) => saveJobProgress(db, job, progress),
|
|
45
46
|
hold: () => holdJob(db, job),
|
|
46
47
|
complete: () => replaceJobData(db, job, { complete: true }),
|
|
47
|
-
...liveSessions && { liveSessions }
|
|
48
|
+
...liveSessions && { liveSessions },
|
|
49
|
+
...quiet && { quiet: (userIds) => quiet(db, userIds) }
|
|
48
50
|
});
|
|
49
51
|
}
|
|
50
52
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { migrationCommand } from "../changes/capture-schema.js";
|
|
2
2
|
import { historyNames, notificationsNames } from "./options.js";
|
|
3
|
-
import { storeSchema } from "../better-auth/store/store-names.js";
|
|
4
3
|
import { deviceSessionColumnSql, devicesTableSql, qualified } from "./schema-sql.js";
|
|
4
|
+
import { storeSchema } from "../better-auth/store/store-names.js";
|
|
5
5
|
import { historyTableSql } from "./history-sql.js";
|
|
6
6
|
import { sql } from "kysely";
|
|
7
7
|
//#region src/notifications/schema.ts
|
|
@@ -11,9 +11,12 @@ const authUsersTable = (auth) => ({
|
|
|
11
11
|
table: auth.options.user?.modelName ?? "user"
|
|
12
12
|
});
|
|
13
13
|
const regclass = async (db, name) => (await sql`select to_regclass(${name})::oid as oid`.execute(db)).rows[0].oid;
|
|
14
|
-
const userIdType = async (db, users
|
|
14
|
+
const userIdType = async (db, users, of = {
|
|
15
|
+
feature: "notifications",
|
|
16
|
+
rows: "devices"
|
|
17
|
+
}) => {
|
|
15
18
|
const oid = await regclass(db, qualified(users));
|
|
16
|
-
if (oid === null) throw new Error(
|
|
19
|
+
if (oid === null) throw new Error(`${of.feature}: auth's users table ${qualified(users)} is not in the app's database, so ${of.rows} cannot point at it`);
|
|
17
20
|
const { rows } = await sql`select format_type(atttypid, atttypmod) as type from pg_attribute where attrelid = ${oid} and attname = 'id'`.execute(db);
|
|
18
21
|
return rows[0].type;
|
|
19
22
|
};
|
|
@@ -89,4 +92,4 @@ const checkNotificationsSchema = async (db, options = {}, auth) => {
|
|
|
89
92
|
await checkHistoryTable(db, options, users);
|
|
90
93
|
};
|
|
91
94
|
//#endregion
|
|
92
|
-
export { authUsersTable, checkNotificationsSchema, notificationsMigration };
|
|
95
|
+
export { authUsersTable, checkNotificationsSchema, notificationsMigration, regclass, userIdType };
|
package/dist/package.js
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { StatusNames } from "./options.js";
|
|
2
|
+
import { Db } from "@protobase/query";
|
|
3
|
+
//#region src/status/do-not-disturb.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Which of these people are on do not disturb now: their status says so and its clear time, if any, is still to come.
|
|
6
|
+
* The one check of everything that would alert someone, such as a push, so each holds it alike.
|
|
7
|
+
*/
|
|
8
|
+
export declare const onDoNotDisturb: (db: Db, names: StatusNames, userIds: readonly string[]) => Promise<Set<string>>;
|
|
9
|
+
//#endregion
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { qualified } from "../notifications/schema-sql.js";
|
|
2
|
+
import { sql } from "kysely";
|
|
3
|
+
//#region src/status/do-not-disturb.ts
|
|
4
|
+
/**
|
|
5
|
+
* Which of these people are on do not disturb now: their status says so and its clear time, if any, is still to come.
|
|
6
|
+
* The one check of everything that would alert someone, such as a push, so each holds it alike.
|
|
7
|
+
*/
|
|
8
|
+
const onDoNotDisturb = async (db, names, userIds) => {
|
|
9
|
+
if (userIds.length === 0) return /* @__PURE__ */ new Set();
|
|
10
|
+
const { rows } = await sql`select user_id from ${sql.raw(qualified(names))}
|
|
11
|
+
where user_id in (${sql.join([...new Set(userIds)])}) and availability = 'do_not_disturb' and (clears_at is null or clears_at > now())`.execute(db);
|
|
12
|
+
return new Set(rows.map((row) => String(row.user_id)));
|
|
13
|
+
};
|
|
14
|
+
//#endregion
|
|
15
|
+
export { onDoNotDisturb };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//#region src/status/options.d.ts
|
|
2
|
+
/** People's own status, turned on with `status: {}` in `protobase.config.ts`; it needs auth with organizations. */
|
|
3
|
+
export type StatusOptions = {
|
|
4
|
+
/** The schema of the statuses table; default `status`. */
|
|
5
|
+
schema?: string;
|
|
6
|
+
};
|
|
7
|
+
/** The statuses table's names, checked: a bad schema name is an error naming the option. */
|
|
8
|
+
export declare const statusNames: (options?: StatusOptions) => {
|
|
9
|
+
schema: string;
|
|
10
|
+
table: string;
|
|
11
|
+
};
|
|
12
|
+
export type StatusNames = ReturnType<typeof statusNames>;
|
|
13
|
+
/** The options, checked as `status` takes them. */
|
|
14
|
+
export declare const checkStatusOptions: (options: StatusOptions) => StatusOptions;
|
|
15
|
+
//#endregion
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//#region src/status/options.ts
|
|
2
|
+
const namePattern = /^[a-z_][a-z0-9_]*$/;
|
|
3
|
+
const maxNameBytes = 63;
|
|
4
|
+
/** The statuses table's names, checked: a bad schema name is an error naming the option. */
|
|
5
|
+
const statusNames = (options = {}) => {
|
|
6
|
+
const schema = options.schema ?? "status";
|
|
7
|
+
if (typeof schema !== "string" || !namePattern.test(schema) || Buffer.byteLength(schema) > maxNameBytes) throw new Error(`status schema is not a valid name: use lowercase letters, digits and "_", at most ${maxNameBytes} characters`);
|
|
8
|
+
return {
|
|
9
|
+
schema,
|
|
10
|
+
table: "statuses"
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
/** The options, checked as `status` takes them. */
|
|
14
|
+
const checkStatusOptions = (options) => {
|
|
15
|
+
const unknown = Object.keys(options).find((key) => key !== "schema");
|
|
16
|
+
if (unknown !== void 0) throw new Error(`status takes no option "${unknown}": only schema`);
|
|
17
|
+
statusNames(options);
|
|
18
|
+
return options;
|
|
19
|
+
};
|
|
20
|
+
/** Status shows people to the members of their organizations, so an app without organizations cannot turn it on. */
|
|
21
|
+
const checkStatusAuth = (auth) => {
|
|
22
|
+
if (!auth?.organizations) throw new Error("status: {} shows people to the members of their organizations: turn organizations on in the auth config, or remove status");
|
|
23
|
+
};
|
|
24
|
+
//#endregion
|
|
25
|
+
export { checkStatusAuth, checkStatusOptions, statusNames };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ownSignInUser } from "../notifications/caller.js";
|
|
2
|
+
import { jsonBody, jsonOnly, limitedBody } from "../notifications/history-request.js";
|
|
3
|
+
import { statusInput } from "./status-input.js";
|
|
4
|
+
import { clearStatus, ownStatus, setStatus, visibleStatuses } from "./status-store.js";
|
|
5
|
+
import { Hono } from "hono";
|
|
6
|
+
//#region src/status/routes.ts
|
|
7
|
+
const noStore = { "Cache-Control": "no-store" };
|
|
8
|
+
const viewerOf = (session) => session.anonymous || session.client ? void 0 : String(session.user.id);
|
|
9
|
+
/**
|
|
10
|
+
* The status endpoints, mounted at `statusPath` behind the API's authenticator: `GET /` the caller's own status,
|
|
11
|
+
* `PUT /` sets it, `DELETE /` clears it, and `GET /people` the statuses of everyone who shares an organization with
|
|
12
|
+
* the caller. No request names whose status it sets: the person comes from the session alone, and only someone
|
|
13
|
+
* signed in as themselves sets theirs, never staff signed in as them (see `ownSignInUser`).
|
|
14
|
+
*/
|
|
15
|
+
const statusRoutes = (db, tables) => {
|
|
16
|
+
const app = new Hono();
|
|
17
|
+
app.get("/", async (c) => {
|
|
18
|
+
const viewer = viewerOf(c.get("session"));
|
|
19
|
+
const status = viewer === void 0 ? void 0 : await ownStatus(db, tables, viewer);
|
|
20
|
+
return c.json({ status: status ?? null }, 200, noStore);
|
|
21
|
+
});
|
|
22
|
+
app.put("/", jsonOnly, limitedBody, async (c) => {
|
|
23
|
+
const userId = ownSignInUser(c.get("session"), "set their status");
|
|
24
|
+
await setStatus(db, tables, userId, statusInput(await jsonBody(c)));
|
|
25
|
+
return c.json({ status: await ownStatus(db, tables, userId) ?? null }, 200, noStore);
|
|
26
|
+
});
|
|
27
|
+
app.delete("/", async (c) => {
|
|
28
|
+
await clearStatus(db, tables, ownSignInUser(c.get("session"), "set their status"));
|
|
29
|
+
return c.body(null, 204);
|
|
30
|
+
});
|
|
31
|
+
app.get("/people", async (c) => {
|
|
32
|
+
const viewer = viewerOf(c.get("session"));
|
|
33
|
+
return c.json({ statuses: viewer === void 0 ? [] : await visibleStatuses(db, tables, viewer) }, 200, noStore);
|
|
34
|
+
});
|
|
35
|
+
return app;
|
|
36
|
+
};
|
|
37
|
+
//#endregion
|
|
38
|
+
export { statusRoutes };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { quoteIdent } from "../changes/capture-sql.js";
|
|
2
|
+
import { qualified } from "../notifications/schema-sql.js";
|
|
3
|
+
//#region src/status/schema-sql.ts
|
|
4
|
+
/**
|
|
5
|
+
* The statuses table: one row per person who set a status, which goes with the user. A custom text or a clear time
|
|
6
|
+
* not set is NULL, and nothing has a default. Clearing a status deletes its row.
|
|
7
|
+
*/
|
|
8
|
+
const statusesTableSql = ({ schema, table }, user) => [`create schema if not exists ${quoteIdent(schema)}`, `create table ${qualified({
|
|
9
|
+
schema,
|
|
10
|
+
table
|
|
11
|
+
})} (
|
|
12
|
+
user_id ${user.type} primary key references ${user.references} on delete cascade,
|
|
13
|
+
availability text not null check (availability in ('automatic', 'busy', 'do_not_disturb')),
|
|
14
|
+
text text check (char_length(text) between 1 and 100),
|
|
15
|
+
clears_at timestamptz,
|
|
16
|
+
updated_at timestamptz not null
|
|
17
|
+
)`];
|
|
18
|
+
//#endregion
|
|
19
|
+
export { statusesTableSql };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { StatusOptions } from "./options.js";
|
|
2
|
+
import { AdminAuth } from "../better-auth/create-auth.js";
|
|
3
|
+
import { Db } from "@protobase/query";
|
|
4
|
+
//#region src/status/schema.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* The SQL that adds the statuses table `status` keeps people's statuses in, its user column pointing at auth's users
|
|
7
|
+
* table; `undefined` when it exists. An app without organizations is refused (see `checkStatusAuth`).
|
|
8
|
+
*/
|
|
9
|
+
export declare const statusMigration: (db: Db, options: StatusOptions, auth: AdminAuth | undefined) => Promise<string | undefined>;
|
|
10
|
+
/** Refuses to start while the statuses table is missing, or the app has no organizations. */
|
|
11
|
+
export declare const checkStatusSchema: (db: Db, options: StatusOptions, auth: AdminAuth | undefined) => Promise<void>;
|
|
12
|
+
//#endregion
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { migrationCommand } from "../changes/capture-schema.js";
|
|
2
|
+
import { qualified } from "../notifications/schema-sql.js";
|
|
3
|
+
import { checkStatusAuth, statusNames } from "./options.js";
|
|
4
|
+
import { authUsersTable, regclass, userIdType } from "../notifications/schema.js";
|
|
5
|
+
import { statusesTableSql } from "./schema-sql.js";
|
|
6
|
+
//#region src/status/schema.ts
|
|
7
|
+
/**
|
|
8
|
+
* The SQL that adds the statuses table `status` keeps people's statuses in, its user column pointing at auth's users
|
|
9
|
+
* table; `undefined` when it exists. An app without organizations is refused (see `checkStatusAuth`).
|
|
10
|
+
*/
|
|
11
|
+
const statusMigration = async (db, options, auth) => {
|
|
12
|
+
checkStatusAuth(auth);
|
|
13
|
+
const names = statusNames(options);
|
|
14
|
+
if (await regclass(db, qualified(names)) !== null) return;
|
|
15
|
+
const users = authUsersTable(auth);
|
|
16
|
+
const type = await userIdType(db, users, {
|
|
17
|
+
feature: "status",
|
|
18
|
+
rows: "statuses"
|
|
19
|
+
});
|
|
20
|
+
return `${statusesTableSql(names, {
|
|
21
|
+
type,
|
|
22
|
+
references: `${qualified(users)} (id)`
|
|
23
|
+
}).join(";\n\n")};`;
|
|
24
|
+
};
|
|
25
|
+
/** Refuses to start while the statuses table is missing, or the app has no organizations. */
|
|
26
|
+
const checkStatusSchema = async (db, options, auth) => {
|
|
27
|
+
checkStatusAuth(auth);
|
|
28
|
+
const table = qualified(statusNames(options));
|
|
29
|
+
if (await regclass(db, table) !== null) return;
|
|
30
|
+
throw new Error(`The statuses table ${table} of status is missing. Write it as a migration with \`${migrationCommand}\`, apply it with the project's migrations, then start again.`);
|
|
31
|
+
};
|
|
32
|
+
//#endregion
|
|
33
|
+
export { checkStatusSchema, statusMigration };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { badRequest } from "../problem.js";
|
|
2
|
+
import { availabilities, statusTextProblem } from "@protobase/schema/internal";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
//#region src/status/status-input.ts
|
|
5
|
+
const body = z.strictObject({
|
|
6
|
+
availability: z.enum(availabilities),
|
|
7
|
+
text: z.string().nullable(),
|
|
8
|
+
clearsAt: z.iso.datetime({ offset: true }).nullable()
|
|
9
|
+
});
|
|
10
|
+
/**
|
|
11
|
+
* The status a request sets, checked: an availability, a one-line text of at most `maxStatusText` characters, kept
|
|
12
|
+
* trimmed, an empty one as none, and a clear time to come or `null` for never. Anything else, a user named in it
|
|
13
|
+
* too, is a 400 that says what is wrong.
|
|
14
|
+
*/
|
|
15
|
+
const statusInput = (given, now = /* @__PURE__ */ new Date()) => {
|
|
16
|
+
const parsed = body.safeParse(given);
|
|
17
|
+
if (!parsed.success) {
|
|
18
|
+
const issue = parsed.error.issues[0];
|
|
19
|
+
throw badRequest("invalid-status", `Send { availability, text, clearsAt }: ${issue.path.join(".") || "the body"} ${issue.message.toLowerCase()}`);
|
|
20
|
+
}
|
|
21
|
+
const { availability, clearsAt } = parsed.data;
|
|
22
|
+
const text = parsed.data.text?.trim() || null;
|
|
23
|
+
const problem = text === null ? void 0 : statusTextProblem(text);
|
|
24
|
+
if (problem) throw badRequest("invalid-status-text", problem);
|
|
25
|
+
if (clearsAt !== null && Date.parse(clearsAt) <= now.getTime()) throw badRequest("invalid-clear-time", "A status clears at a time still to come, or never");
|
|
26
|
+
return {
|
|
27
|
+
availability,
|
|
28
|
+
text,
|
|
29
|
+
clearsAt: clearsAt === null ? null : new Date(clearsAt).toISOString()
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
//#endregion
|
|
33
|
+
export { statusInput };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { qualified } from "../notifications/schema-sql.js";
|
|
2
|
+
import { sql } from "kysely";
|
|
3
|
+
//#region src/status/status-store.ts
|
|
4
|
+
const statusOf = (row) => ({
|
|
5
|
+
userId: String(row.user_id),
|
|
6
|
+
availability: row.availability,
|
|
7
|
+
text: row.text,
|
|
8
|
+
clearsAt: row.clears_at?.toISOString() ?? null
|
|
9
|
+
});
|
|
10
|
+
const holds = sql`(clears_at is null or clears_at > now())`;
|
|
11
|
+
/** The person's own status, as it holds now; `undefined` when they have none. */
|
|
12
|
+
const ownStatus = async (db, tables, userId) => {
|
|
13
|
+
const statuses = sql.raw(qualified(tables.statuses));
|
|
14
|
+
const { rows } = await sql`select user_id, availability, text, clears_at from ${statuses} where user_id = ${userId} and ${holds}`.execute(db);
|
|
15
|
+
return rows[0] && statusOf(rows[0]);
|
|
16
|
+
};
|
|
17
|
+
/** The statuses that hold now of everyone who shares an organization with `viewerId`, their own included when they have one. */
|
|
18
|
+
const visibleStatuses = async (db, tables, viewerId) => {
|
|
19
|
+
const statuses = sql.raw(qualified(tables.statuses));
|
|
20
|
+
const members = sql.raw(qualified(tables.members));
|
|
21
|
+
const { rows } = await sql`select s.user_id, s.availability, s.text, s.clears_at from ${statuses} s
|
|
22
|
+
where ${holds} and exists (select from ${members} own join ${members} other on other.organization_id = own.organization_id
|
|
23
|
+
where own.user_id = ${viewerId} and other.user_id = s.user_id)
|
|
24
|
+
order by s.user_id`.execute(db);
|
|
25
|
+
return rows.map(statusOf);
|
|
26
|
+
};
|
|
27
|
+
/** Sets the person's status, replacing the one they had; one that says nothing, automatic without a text, clears it. */
|
|
28
|
+
const setStatus = async (db, tables, userId, input) => {
|
|
29
|
+
if (input.availability === "automatic" && input.text === null) return clearStatus(db, tables, userId);
|
|
30
|
+
const statuses = sql.raw(qualified(tables.statuses));
|
|
31
|
+
await sql`insert into ${statuses} (user_id, availability, text, clears_at, updated_at) values (${userId}, ${input.availability}, ${input.text}, ${input.clearsAt}, now())
|
|
32
|
+
on conflict (user_id) do update set availability = excluded.availability, text = excluded.text, clears_at = excluded.clears_at, updated_at = excluded.updated_at`.execute(db);
|
|
33
|
+
};
|
|
34
|
+
/** Clears the person's status, whether or not they had one. */
|
|
35
|
+
const clearStatus = async (db, tables, userId) => {
|
|
36
|
+
await sql`delete from ${sql.raw(qualified(tables.statuses))} where user_id = ${userId}`.execute(db);
|
|
37
|
+
};
|
|
38
|
+
//#endregion
|
|
39
|
+
export { clearStatus, ownStatus, setStatus, visibleStatuses };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@protobase/server",
|
|
3
|
-
"version": "0.59.
|
|
3
|
+
"version": "0.59.3",
|
|
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": {
|
|
@@ -42,9 +42,9 @@
|
|
|
42
42
|
"@better-auth/sso": "^1.7.12",
|
|
43
43
|
"@livekit/protocol": "1.53.1",
|
|
44
44
|
"@modelcontextprotocol/server": "^2.3.1",
|
|
45
|
-
"@protobase/layout": "0.59.
|
|
46
|
-
"@protobase/query": "0.59.
|
|
47
|
-
"@protobase/schema": "0.59.
|
|
45
|
+
"@protobase/layout": "0.59.3",
|
|
46
|
+
"@protobase/query": "0.59.3",
|
|
47
|
+
"@protobase/schema": "0.59.3",
|
|
48
48
|
"@react-email/render": "^2.1.0",
|
|
49
49
|
"@scalar/hono-api-reference": "^0.12.10",
|
|
50
50
|
"@sentry/core": "^11.7.0",
|