@protobase/server 0.67.7 → 0.67.9
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/config-exports.d.ts +2 -1
- package/dist/config-exports.js +2 -1
- package/dist/create-admin.d.ts +4 -1
- package/dist/create-admin.js +6 -2
- package/dist/index.d.ts +4 -1
- package/dist/index.js +3 -1
- package/dist/internal.d.ts +2 -1
- package/dist/internal.js +2 -1
- package/dist/jobs/database.js +1 -1
- package/dist/package.js +1 -1
- package/dist/workflows/http-step.js +103 -0
- package/dist/workflows/input-limits.js +22 -0
- package/dist/workflows/mail-step.js +58 -0
- package/dist/workflows/options.d.ts +27 -0
- package/dist/workflows/options.js +19 -0
- package/dist/workflows/piece-step.js +11 -0
- package/dist/workflows/records-step.js +114 -0
- package/dist/workflows/run-files.js +70 -0
- package/dist/workflows/save-hook.js +20 -0
- package/dist/workflows/schema-sql.js +147 -0
- package/dist/workflows/schema.d.ts +11 -0
- package/dist/workflows/schema.js +41 -0
- package/dist/workflows/segment-job.js +51 -0
- package/dist/workflows/start-hook.js +26 -0
- package/dist/workflows/start-workflow.d.ts +26 -0
- package/dist/workflows/start-workflow.js +28 -0
- package/dist/workflows/step-types.js +34 -0
- package/dist/workflows/store.js +42 -0
- package/dist/workflows/workflow-engine.js +37 -0
- package/dist/workflows/workflows-module.d.ts +124 -0
- package/dist/workflows/workflows-module.js +11 -0
- package/package.json +5 -4
package/dist/config-exports.d.ts
CHANGED
|
@@ -6,4 +6,5 @@ import { maxTextLength } from "./user-tables/definition-resource.js";
|
|
|
6
6
|
import { userTableResources } from "./user-tables/user-table-resources.js";
|
|
7
7
|
import { builtInResources } from "./built-in-resources.js";
|
|
8
8
|
import { webhooksModule } from "./webhooks/table/webhooks-module.js";
|
|
9
|
-
|
|
9
|
+
import { workflowsModule } from "./workflows/workflows-module.js";
|
|
10
|
+
export { type CheckedDefinition, builtInResources, configExports, definitionsSchema, maxTextLength, ownedTableNames, userTableResources, userTablesSchema, webhooksModule, workflowsModule };
|
package/dist/config-exports.js
CHANGED
|
@@ -5,4 +5,5 @@ import { configExports } from "./resource-source.js";
|
|
|
5
5
|
import { ownedTableNames } from "./user-tables/owned-definitions.js";
|
|
6
6
|
import { userTableResources } from "./user-tables/user-table-resources.js";
|
|
7
7
|
import { webhooksModule } from "./webhooks/table/webhooks-module.js";
|
|
8
|
-
|
|
8
|
+
import { workflowsModule } from "./workflows/workflows-module.js";
|
|
9
|
+
export { builtInResources, configExports, definitionsSchema, maxTextLength, ownedTableNames, userTableResources, userTablesSchema, webhooksModule, workflowsModule };
|
package/dist/create-admin.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { Admin, Authenticator, PageSource, ViewSource } from "./types.js";
|
|
|
8
8
|
import { CommentUsers } from "./comments/comment-users.js";
|
|
9
9
|
import { HistoryOptions } from "./history/options.js";
|
|
10
10
|
import { WebhooksOptions } from "./webhooks/table/options.js";
|
|
11
|
+
import { WorkflowsOptions } from "./workflows/options.js";
|
|
11
12
|
import { NotificationsOptions } from "./notifications/options.js";
|
|
12
13
|
import { CallsOptions } from "./calls/calls-options.js";
|
|
13
14
|
import { StatusOptions } from "./status/options.js";
|
|
@@ -60,6 +61,8 @@ export type CreateAdminInput = {
|
|
|
60
61
|
subscriptions?: SubscriptionDefinition[];
|
|
61
62
|
/** Webhook subscriptions made in the admin app, from `webhooks: {}` (see `WebhooksOptions`). */
|
|
62
63
|
webhooks?: WebhooksOptions;
|
|
64
|
+
/** Workflows made in the admin app and run on the app's jobs, from `workflows: {}` (see `WorkflowsOptions`). */
|
|
65
|
+
workflows?: WorkflowsOptions;
|
|
63
66
|
/** How `/api/sb/realtime/v1` and `/api/channels` are served: by one process or several, and whether SQL sends to channels (see `RealtimeOptions`). */
|
|
64
67
|
realtime?: RealtimeOptions;
|
|
65
68
|
/** Each user's devices and the endpoints that register them, from `notifications: {}` (see `NotificationsOptions`). */
|
|
@@ -107,5 +110,5 @@ export type CreateAdminInput = {
|
|
|
107
110
|
upgradeWebSocket?: UpgradeWebSocket;
|
|
108
111
|
};
|
|
109
112
|
/** The admin REST API as a Hono app. It uses only Web APIs, so it runs on Node, Workers and anywhere else Hono does. */
|
|
110
|
-
export declare const createAdmin: ({ resources: appResources, views: appViews, userTables, 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;
|
|
113
|
+
export declare const createAdmin: ({ resources: appResources, views: appViews, userTables, pages, userMenu, bottomBars, db, authenticate, auth, functions, secret, files: filesOptions, events, history: historyOptions, jobs, subscriptions, webhooks: webhooksOptions, workflows: workflowsOptions, realtime, notifications, status, calls, mobile, app: identity, icons, bundlePlugins, fieldTypes, options, formsDifficulty, commentUsers: givenCommentUsers, verifyToken, upgradeWebSocket }: CreateAdminInput) => Admin;
|
|
111
114
|
//#endregion
|
package/dist/create-admin.js
CHANGED
|
@@ -57,6 +57,7 @@ import { toApiFunction } from "./functions/define-function.js";
|
|
|
57
57
|
import { createWebhooks } from "./webhooks/webhooks.js";
|
|
58
58
|
import { webhookTable } from "./webhooks/table/webhook-table.js";
|
|
59
59
|
import { subscriptionUrlCheck, subscriptionWriteCheck } from "./webhooks/table/validate-hook.js";
|
|
60
|
+
import { workflowEngine } from "./workflows/workflow-engine.js";
|
|
60
61
|
import { callsService } from "./calls/calls-service.js";
|
|
61
62
|
import { ringRoutes } from "./calls/ring-routes.js";
|
|
62
63
|
import { upgradeAuthenticator } from "./functions/upgrade-caller.js";
|
|
@@ -124,7 +125,7 @@ const declaredRoles = (auth, roles) => {
|
|
|
124
125
|
return names.length > 0 ? [...new Set(names)] : void 0;
|
|
125
126
|
};
|
|
126
127
|
/** The admin REST API as a Hono app. It uses only Web APIs, so it runs on Node, Workers and anywhere else Hono does. */
|
|
127
|
-
const createAdmin = ({ resources: appResources, views: appViews = [], userTables, 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 }) => {
|
|
128
|
+
const createAdmin = ({ resources: appResources, views: appViews = [], userTables, pages = [], userMenu, bottomBars = [], db, authenticate, auth, functions = {}, secret, files: filesOptions, events, history: historyOptions, jobs = [], subscriptions = [], webhooks: webhooksOptions, workflows: workflowsOptions, realtime, notifications, status, calls, mobile, app: identity, icons, bundlePlugins = [], fieldTypes = {}, options = {}, formsDifficulty, commentUsers: givenCommentUsers, verifyToken, upgradeWebSocket }) => {
|
|
128
129
|
const basePath = options.basePath ?? "/api/v1";
|
|
129
130
|
const appModels = appResources.map((resource) => resource.toModel());
|
|
130
131
|
const withComments = appModels.some((model) => model.comments);
|
|
@@ -223,6 +224,7 @@ const createAdmin = ({ resources: appResources, views: appViews = [], userTables
|
|
|
223
224
|
storage: servesKeys
|
|
224
225
|
});
|
|
225
226
|
const table = webhooksOptions && webhookTable(webhooksOptions, auth);
|
|
227
|
+
const engine = workflowsOptions && workflowEngine(workflowsOptions, () => deps, auth);
|
|
226
228
|
const withAi = withAiFields || hasAiActions(viewModels);
|
|
227
229
|
const model = withAi ? aiModel(options.assistant, globalThis.process?.env ?? {}) : void 0;
|
|
228
230
|
const fill = withAiFields ? fillJob(() => deps) : void 0;
|
|
@@ -242,9 +244,10 @@ const createAdmin = ({ resources: appResources, views: appViews = [], userTables
|
|
|
242
244
|
...options.writeHooks ?? [],
|
|
243
245
|
...served.hooks,
|
|
244
246
|
...table ? [subscriptionWriteCheck(registry.entries.map((entry) => entry.model))] : [],
|
|
247
|
+
...engine?.hooks ?? [],
|
|
245
248
|
...fill ? [fillHook(() => deps, fill)] : []
|
|
246
249
|
],
|
|
247
|
-
writeChecks: table?.checkUrl ? [subscriptionUrlCheck(table.checkUrl)] : [],
|
|
250
|
+
writeChecks: [...table?.checkUrl ? [subscriptionUrlCheck(table.checkUrl)] : [], ...engine?.writeChecks ?? []],
|
|
248
251
|
audit: options.audit ?? consoleAuditQueue(),
|
|
249
252
|
report: options.onUnhandledError ?? ((error) => console.error(error)),
|
|
250
253
|
accessOptions: {
|
|
@@ -572,6 +575,7 @@ const createAdmin = ({ resources: appResources, views: appViews = [], userTables
|
|
|
572
575
|
metaPath: `${deps.systemPath}/meta`,
|
|
573
576
|
jobs: [
|
|
574
577
|
...webhooks.jobs,
|
|
578
|
+
...engine ? [engine.job] : [],
|
|
575
579
|
...withHistory && days !== void 0 ? [pruneHistoryJob(changes, days)] : [],
|
|
576
580
|
...mentions ? [mentions] : [],
|
|
577
581
|
...fill ? [fill] : [],
|
package/dist/index.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ import { Admin, AuditEvent, AuditQueue, Authenticator, Membership, PipelineHook,
|
|
|
16
16
|
import { CommentUsers } from "./comments/comment-users.js";
|
|
17
17
|
import { HistoryOptions } from "./history/options.js";
|
|
18
18
|
import { WebhooksOptions } from "./webhooks/table/options.js";
|
|
19
|
+
import { WorkflowsOptions } from "./workflows/options.js";
|
|
19
20
|
import { ApnsEnvironment, Device, DeviceKind, NewDevice } from "./notifications/device.js";
|
|
20
21
|
import { DeviceStore } from "./notifications/device-store.js";
|
|
21
22
|
import { HistoryStore, maxHistoryPage, maxUnreadCount } from "./notifications/history-store.js";
|
|
@@ -101,6 +102,8 @@ import { eventMatcher } from "./changes/match-events.js";
|
|
|
101
102
|
import { parseColumns, readOutbox } from "./changes/outbox.js";
|
|
102
103
|
import { newWebhookSecret } from "./webhooks/standard-webhooks.js";
|
|
103
104
|
import { checkWebhooksSchema, webhooksMigration } from "./webhooks/table/schema.js";
|
|
105
|
+
import { checkWorkflowsSchema, workflowsMigration } from "./workflows/schema.js";
|
|
106
|
+
import { StartWorkflowOptions, startWorkflow } from "./workflows/start-workflow.js";
|
|
104
107
|
import { checkUserTablesSchema, userTablesMigration } from "./user-tables/schema.js";
|
|
105
108
|
import { ownedDefinitionsMigration } from "./user-tables/owned-definitions.js";
|
|
106
109
|
import { UserTables, userTableResources } from "./user-tables/user-table-resources.js";
|
|
@@ -189,4 +192,4 @@ import { DrainResult, JobsRuntime, StartJobsOptions, startJobs } from "./jobs/ru
|
|
|
189
192
|
import { jobsDrainApp } from "./jobs/drain-route.js";
|
|
190
193
|
import { checkJobsSchema, jobsSchema, jobsSchemaMigration } from "./jobs/schema.js";
|
|
191
194
|
import { AdminAuth, CreateAuthOptions, UserCreatedHook, normalizeRoles } from "./better-auth/create-auth.js";
|
|
192
|
-
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 MailAttachment, type MailMessage, MailServerTimeoutError, 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 PdfComponent, type PdfOptions, type PdfTemplate, PersonalMailError, type PersonalMailErrorCode, type PersonalMailProviderId, 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 UserTables, 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, callsMigration, capSocketMessages, changesMigration, channelSendsMigration, checkAi, checkAuthSchema, checkCalls, checkChanges, checkChannelSends, checkComments, checkComputed, checkJobsSchema, checkNotificationsSchema, checkQueryRunner, checkRealtimeRelay, checkResourcesOutsideAuthStore, checkSourceNames, checkStatusSchema, checkUserTablesSchema, checkViewQueries, checkWebhooksSchema, coalesce, commentTools, commentsMigration, computedMigration, configExports, consoleAuditQueue, conversationKey, countRecordsTool, createAdmin, createConversations, createEncryptionTools, createFileCleanup, createOrganization, createRecordTool, createUser, defaultAppName, defaultHistoryPage, defaultSignInPolicy, defineEmail, defineFunction, defineJob, definePdf, 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, ownedDefinitionsMigration, parseColumns, parseRoles, passwordLength, pluginOptions, pluginsBeforeSignIn, pluginsFor, pluginsVariable, pngIcons, poolWaitMs, postgresDeviceStore, postgresHistoryStore, processor, pushMessage, queryRunnerMigration, readDbConnections, readDefinitionsVersion, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readRecordTools, readVapidSettings, readWriteQueryTool, readableResources, realtimeLimits, realtimeRelayMigration, recordOf, refusalOf, refused, relayed, reloadingAdmin, 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, userTableResources, userTablesMigration, vapidPrivateKeyVariable, vapidPublicKeyVariable, vapidSubjectVariable, variantPath, verifySignature, verifyStandardWebhook, waitUntil, wantsLongQueries, webManifestPath, webPushSender, webauthnPath, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps, writeRecordTools };
|
|
195
|
+
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 MailAttachment, type MailMessage, MailServerTimeoutError, 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 PdfComponent, type PdfOptions, type PdfTemplate, PersonalMailError, type PersonalMailErrorCode, type PersonalMailProviderId, 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 StartWorkflowOptions, 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 UserTables, type VapidSettings, type ViewSource, type WebPushFields, type WebPushOptions, type WebhooksOptions, type WidgetOptions, type WorkflowsOptions, 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, callsMigration, capSocketMessages, changesMigration, channelSendsMigration, checkAi, checkAuthSchema, checkCalls, checkChanges, checkChannelSends, checkComments, checkComputed, checkJobsSchema, checkNotificationsSchema, checkQueryRunner, checkRealtimeRelay, checkResourcesOutsideAuthStore, checkSourceNames, checkStatusSchema, checkUserTablesSchema, checkViewQueries, checkWebhooksSchema, checkWorkflowsSchema, coalesce, commentTools, commentsMigration, computedMigration, configExports, consoleAuditQueue, conversationKey, countRecordsTool, createAdmin, createConversations, createEncryptionTools, createFileCleanup, createOrganization, createRecordTool, createUser, defaultAppName, defaultHistoryPage, defaultSignInPolicy, defineEmail, defineFunction, defineJob, definePdf, 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, ownedDefinitionsMigration, parseColumns, parseRoles, passwordLength, pluginOptions, pluginsBeforeSignIn, pluginsFor, pluginsVariable, pngIcons, poolWaitMs, postgresDeviceStore, postgresHistoryStore, processor, pushMessage, queryRunnerMigration, readDbConnections, readDefinitionsVersion, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readRecordTools, readVapidSettings, readWriteQueryTool, readableResources, realtimeLimits, realtimeRelayMigration, recordOf, refusalOf, refused, relayed, reloadingAdmin, 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, startWorkflow, statusMigration, statusNames, storageCleanupSchedule, storageProvider, storeFile, storeUsage, streamChatCompletion, subscription, supabasePath, thumbHash, unauthorized, updateRecordTool, urgencies, userTableResources, userTablesMigration, vapidPrivateKeyVariable, vapidPublicKeyVariable, vapidSubjectVariable, variantPath, verifySignature, verifyStandardWebhook, waitUntil, wantsLongQueries, webManifestPath, webPushSender, webauthnPath, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps, workflowsMigration, writeRecordTools };
|
package/dist/index.js
CHANGED
|
@@ -113,6 +113,8 @@ import { authStoreTables } from "./better-auth/auth-tables.js";
|
|
|
113
113
|
import { checkResourcesOutsideAuthStore } from "./better-auth/store/resources-outside.js";
|
|
114
114
|
import { checkComputed, computedMigration } from "./computed/computed-schema.js";
|
|
115
115
|
import { checkWebhooksSchema, webhooksMigration } from "./webhooks/table/schema.js";
|
|
116
|
+
import { checkWorkflowsSchema, workflowsMigration } from "./workflows/schema.js";
|
|
117
|
+
import { startWorkflow } from "./workflows/start-workflow.js";
|
|
116
118
|
import { checkUserTablesSchema, userTablesMigration } from "./user-tables/schema.js";
|
|
117
119
|
import { ownedDefinitionsMigration } from "./user-tables/owned-definitions.js";
|
|
118
120
|
import { userTableResources } from "./user-tables/user-table-resources.js";
|
|
@@ -140,4 +142,4 @@ import { defineEmail } from "./mail/define-email.js";
|
|
|
140
142
|
import { definePdf } from "./pdf/define-pdf.js";
|
|
141
143
|
import { startJobs } from "./jobs/runtime.js";
|
|
142
144
|
import { jobsDrainApp } from "./jobs/drain-route.js";
|
|
143
|
-
export { ConnectionsBusy, DeviceStoreError, FatalJobError, HistoryStoreError, HttpProblem, MailServerTimeoutError, MissingCredentials, ModelError, NotificationSendError, PersonalMailError, PushMessageError, addAppRoleToOwners, addCommentTool, addMember, apiKeyCaller, apiKeySettings, apiKeyVariables, appNameOver, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaDrift, authSchemaMigration, authStoreTables, backgroundSettled, badRequest, batchWriteTool, beforeSignInPluginsVariable, betterAuthTokens, callsMigration, capSocketMessages, changesMigration, channelSendsMigration, checkAi, checkAuthSchema, checkCalls, checkChanges, checkChannelSends, checkComments, checkComputed, checkJobsSchema, checkNotificationsSchema, checkQueryRunner, checkRealtimeRelay, checkResourcesOutsideAuthStore, checkSourceNames, checkStatusSchema, checkUserTablesSchema, checkViewQueries, checkWebhooksSchema, coalesce, commentTools, commentsMigration, computedMigration, configExports, consoleAuditQueue, conversationKey, countRecordsTool, createAdmin, createConversations, createEncryptionTools, createFileCleanup, createOrganization, createRecordTool, createUser, defaultAppName, defaultHistoryPage, defaultSignInPolicy, defineEmail, defineFunction, defineJob, definePdf, 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, ownedDefinitionsMigration, parseColumns, parseRoles, passwordLength, pluginOptions, pluginsBeforeSignIn, pluginsFor, pluginsVariable, pngIcons, poolWaitMs, postgresDeviceStore, postgresHistoryStore, processor, pushMessage, queryRunnerMigration, readDbConnections, readDefinitionsVersion, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readRecordTools, readVapidSettings, readWriteQueryTool, readableResources, realtimeLimits, realtimeRelayMigration, recordOf, refusalOf, refused, relayed, reloadingAdmin, 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, userTableResources, userTablesMigration, vapidPrivateKeyVariable, vapidPublicKeyVariable, vapidSubjectVariable, variantPath, verifySignature, verifyStandardWebhook, waitUntil, wantsLongQueries, webManifestPath, webPushSender, webauthnPath, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps, writeRecordTools };
|
|
145
|
+
export { ConnectionsBusy, DeviceStoreError, FatalJobError, HistoryStoreError, HttpProblem, MailServerTimeoutError, MissingCredentials, ModelError, NotificationSendError, PersonalMailError, PushMessageError, addAppRoleToOwners, addCommentTool, addMember, apiKeyCaller, apiKeySettings, apiKeyVariables, appNameOver, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaDrift, authSchemaMigration, authStoreTables, backgroundSettled, badRequest, batchWriteTool, beforeSignInPluginsVariable, betterAuthTokens, callsMigration, capSocketMessages, changesMigration, channelSendsMigration, checkAi, checkAuthSchema, checkCalls, checkChanges, checkChannelSends, checkComments, checkComputed, checkJobsSchema, checkNotificationsSchema, checkQueryRunner, checkRealtimeRelay, checkResourcesOutsideAuthStore, checkSourceNames, checkStatusSchema, checkUserTablesSchema, checkViewQueries, checkWebhooksSchema, checkWorkflowsSchema, coalesce, commentTools, commentsMigration, computedMigration, configExports, consoleAuditQueue, conversationKey, countRecordsTool, createAdmin, createConversations, createEncryptionTools, createFileCleanup, createOrganization, createRecordTool, createUser, defaultAppName, defaultHistoryPage, defaultSignInPolicy, defineEmail, defineFunction, defineJob, definePdf, 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, ownedDefinitionsMigration, parseColumns, parseRoles, passwordLength, pluginOptions, pluginsBeforeSignIn, pluginsFor, pluginsVariable, pngIcons, poolWaitMs, postgresDeviceStore, postgresHistoryStore, processor, pushMessage, queryRunnerMigration, readDbConnections, readDefinitionsVersion, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readRecordTools, readVapidSettings, readWriteQueryTool, readableResources, realtimeLimits, realtimeRelayMigration, recordOf, refusalOf, refused, relayed, reloadingAdmin, 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, startWorkflow, statusMigration, statusNames, storageCleanupSchedule, storageProvider, storeFile, storeUsage, streamChatCompletion, subscription, supabasePath, thumbHash, unauthorized, updateRecordTool, urgencies, userTableResources, userTablesMigration, vapidPrivateKeyVariable, vapidPublicKeyVariable, vapidSubjectVariable, variantPath, verifySignature, verifyStandardWebhook, waitUntil, wantsLongQueries, webManifestPath, webPushSender, webauthnPath, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps, workflowsMigration, writeRecordTools };
|
package/dist/internal.d.ts
CHANGED
|
@@ -12,4 +12,5 @@ import { fileProviderOf } from "./files/provider-settings.js";
|
|
|
12
12
|
import { declaredJob } from "./jobs/declared-job.js";
|
|
13
13
|
import { AuthPlugins, buildAuth, createAuth, loadAuthPlugins } from "./better-auth/create-auth.js";
|
|
14
14
|
import { webhooksModule } from "./webhooks/table/webhooks-module.js";
|
|
15
|
-
|
|
15
|
+
import { workflowsModule } from "./workflows/workflows-module.js";
|
|
16
|
+
export { type AppAuthInput, type AuthPlugins, appAuthOptions, appPool, betterAuthAuthenticator, buildAppAuth, buildAuth, buildAuthenticator, checkCallsOptions, checkNotificationsOptions, checkStatusOptions, createAuth, declaredJob, fileProviderOf, isSubscription, loadAuthPlugins, localFiles, longQueryPool, s3Files, webhooksModule, workflowsModule };
|
package/dist/internal.js
CHANGED
|
@@ -9,7 +9,8 @@ import { betterAuthAuthenticator } from "./better-auth/authenticator.js";
|
|
|
9
9
|
import { fileProviderOf } from "./files/provider-settings.js";
|
|
10
10
|
import { declaredJob } from "./jobs/declared-job.js";
|
|
11
11
|
import { webhooksModule } from "./webhooks/table/webhooks-module.js";
|
|
12
|
+
import { workflowsModule } from "./workflows/workflows-module.js";
|
|
12
13
|
import { appAuthOptions, buildAppAuth } from "./better-auth/auth-settings.js";
|
|
13
14
|
import { buildAuthenticator } from "./better-auth/authenticate-settings.js";
|
|
14
15
|
import { checkCallsOptions } from "./calls/calls-options.js";
|
|
15
|
-
export { appAuthOptions, appPool, betterAuthAuthenticator, buildAppAuth, buildAuth, buildAuthenticator, checkCallsOptions, checkNotificationsOptions, checkStatusOptions, createAuth, declaredJob, fileProviderOf, isSubscription, loadAuthPlugins, localFiles, longQueryPool, s3Files, webhooksModule };
|
|
16
|
+
export { appAuthOptions, appPool, betterAuthAuthenticator, buildAppAuth, buildAuth, buildAuthenticator, checkCallsOptions, checkNotificationsOptions, checkStatusOptions, createAuth, declaredJob, fileProviderOf, isSubscription, loadAuthPlugins, localFiles, longQueryPool, s3Files, webhooksModule, workflowsModule };
|
package/dist/jobs/database.js
CHANGED
|
@@ -19,7 +19,7 @@ const transactionStatements = (text) => {
|
|
|
19
19
|
};
|
|
20
20
|
/**
|
|
21
21
|
* pg-boss's database over the app's own Kysely pool or transaction: one connection pool for the app and its jobs, the
|
|
22
|
-
* same driver on Node
|
|
22
|
+
* same driver on Node and Bun. Every statement runs on its own, or inside one transaction for pg-boss's
|
|
23
23
|
* transaction texts, which works through a pooler in transaction mode: pg-boss takes only transaction-scoped locks.
|
|
24
24
|
*/
|
|
25
25
|
const jobsDatabase = (db) => ({ executeSql: async (text, values) => {
|
package/dist/package.js
CHANGED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { AddressRefusedError, pinnedRequest } from "../webhooks/table/pinned-request.js";
|
|
2
|
+
import { readLimited } from "../webhooks/table/read-limited.js";
|
|
3
|
+
import { isFileValue, readRunFile, storeRunFile } from "./run-files.js";
|
|
4
|
+
import { StepError } from "@protobase/workflows";
|
|
5
|
+
//#region src/workflows/http-step.ts
|
|
6
|
+
/** The most of an answer's body the `http` step reads; a larger one fails the step. */
|
|
7
|
+
const httpBodyLimit = 10485760;
|
|
8
|
+
/** How much of a failing answer's body its error keeps, after secrets are blanked out of it. */
|
|
9
|
+
const causeBodyLength = 200;
|
|
10
|
+
const methods = /* @__PURE__ */ new Set([
|
|
11
|
+
"GET",
|
|
12
|
+
"POST",
|
|
13
|
+
"PUT",
|
|
14
|
+
"PATCH",
|
|
15
|
+
"DELETE"
|
|
16
|
+
]);
|
|
17
|
+
const readings = /* @__PURE__ */ new Set([
|
|
18
|
+
"json",
|
|
19
|
+
"text",
|
|
20
|
+
"file"
|
|
21
|
+
]);
|
|
22
|
+
const invalid = (reason) => new StepError("Http.InvalidArguments", reason);
|
|
23
|
+
const isTextRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && Object.values(value).every((item) => typeof item === "string");
|
|
24
|
+
const checked = (args) => {
|
|
25
|
+
if (typeof args.url !== "string" || !URL.canParse(args.url)) throw invalid("url must be an http or https URL");
|
|
26
|
+
const method = args.method === void 0 ? "GET" : String(args.method).toUpperCase();
|
|
27
|
+
if (!methods.has(method)) throw invalid(`method must be one of ${[...methods].join(", ")}`);
|
|
28
|
+
if (args.headers !== void 0 && !isTextRecord(args.headers)) throw invalid("headers must be an object of text values");
|
|
29
|
+
if (args.query !== void 0 && !isTextRecord(args.query)) throw invalid("query must be an object of text values");
|
|
30
|
+
const response = args.response ?? "json";
|
|
31
|
+
if (typeof response !== "string" || !readings.has(response)) throw invalid("response must be json, text or file");
|
|
32
|
+
const url = new URL(args.url);
|
|
33
|
+
for (const [name, value] of Object.entries(args.query ?? {})) url.searchParams.append(name, value);
|
|
34
|
+
return {
|
|
35
|
+
url,
|
|
36
|
+
method,
|
|
37
|
+
headers: new Headers(args.headers),
|
|
38
|
+
response
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
const answerBody = async (bytes, response, url, type, keep) => {
|
|
42
|
+
if (response === "file") return keep(bytes, decodeURIComponent(url.pathname.slice(url.pathname.lastIndexOf("/") + 1)), type?.split(";")[0]?.trim() || "application/octet-stream");
|
|
43
|
+
const text = new TextDecoder().decode(bytes);
|
|
44
|
+
if (response === "text") return text;
|
|
45
|
+
if (text.trim() === "") return null;
|
|
46
|
+
try {
|
|
47
|
+
return JSON.parse(text);
|
|
48
|
+
} catch (error) {
|
|
49
|
+
if (!(error instanceof SyntaxError)) throw error;
|
|
50
|
+
throw new StepError("Http.InvalidBody", `${url.host} answered with a body that is not JSON; read it with "response": "text"`);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* The `app:http` step: one request to `url`, built at run time, through the pinned request, which resolves its host
|
|
55
|
+
* once and connects only to a public address it checked. It follows no redirect, reads at most `httpBodyLimit` of
|
|
56
|
+
* the answer, and fails with `Http.Status` on a status outside 2xx, `Http.Network` when no answer came.
|
|
57
|
+
*/
|
|
58
|
+
const httpStep = ({ allowPrivateAddresses = false, resolve, files = () => void 0 } = {}) => async (args, { runId, signal, log, redact }) => {
|
|
59
|
+
const given = args ?? {};
|
|
60
|
+
const { url, method, headers, response: reading } = checked(given);
|
|
61
|
+
let body;
|
|
62
|
+
if (given.body !== void 0 && method !== "GET") {
|
|
63
|
+
if (isFileValue(given.body)) {
|
|
64
|
+
const file = await readRunFile(files(), runId, given.body, "body");
|
|
65
|
+
body = file.bytes;
|
|
66
|
+
if (!headers.has("content-type")) headers.set("content-type", file.ref.type ?? "application/octet-stream");
|
|
67
|
+
} else if (typeof given.body === "string") body = given.body;
|
|
68
|
+
else {
|
|
69
|
+
body = JSON.stringify(given.body);
|
|
70
|
+
if (!headers.has("content-type")) headers.set("content-type", "application/json");
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const started = Date.now();
|
|
74
|
+
const answer = await pinnedRequest(url.href, {
|
|
75
|
+
method,
|
|
76
|
+
headers,
|
|
77
|
+
...body !== void 0 && { body },
|
|
78
|
+
signal
|
|
79
|
+
}, {
|
|
80
|
+
allowPrivateAddresses,
|
|
81
|
+
...resolve && { resolve }
|
|
82
|
+
}).catch((error) => {
|
|
83
|
+
if (error instanceof AddressRefusedError) throw new StepError("Http.AddressRefused", error.message);
|
|
84
|
+
if (signal.aborted) throw error;
|
|
85
|
+
throw new StepError("Http.Network", `No answer from ${url.host}: ${error instanceof Error ? error.message : String(error)}`);
|
|
86
|
+
});
|
|
87
|
+
const bytes = await readLimited(answer, 10485761);
|
|
88
|
+
log(`${method} ${given.url}`.replace(/[?#].*$/, "") + `: ${answer.status} in ${Date.now() - started} ms`);
|
|
89
|
+
if (bytes.length > 10485760) throw new StepError("Http.ResponseTooLarge", `The answer's body is larger than ${httpBodyLimit / 1024 / 1024} MiB, the most the http step reads`);
|
|
90
|
+
if (answer.status < 200 || answer.status > 299) {
|
|
91
|
+
const excerpt = redact(new TextDecoder().decode(bytes)).slice(0, causeBodyLength);
|
|
92
|
+
throw new StepError("Http.Status", `${url.host} answered ${answer.status}${excerpt ? `: ${excerpt}` : ""}`);
|
|
93
|
+
}
|
|
94
|
+
const kept = Object.fromEntries([...answer.headers].filter(([name]) => name !== "set-cookie"));
|
|
95
|
+
const keep = (data, name, type) => storeRunFile(files(), runId, data, name, type);
|
|
96
|
+
return {
|
|
97
|
+
status: answer.status,
|
|
98
|
+
headers: kept,
|
|
99
|
+
body: await answerBody(bytes, reading, url, answer.headers.get("content-type"), keep)
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
//#endregion
|
|
103
|
+
export { httpBodyLimit, httpStep };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { badRequest } from "../problem.js";
|
|
2
|
+
import { limits, nestingDepth } from "@protobase/workflows";
|
|
3
|
+
//#region src/workflows/input-limits.ts
|
|
4
|
+
/**
|
|
5
|
+
* Refuses a run's input over `limits.inputBytes` as JSON or nested deeper than `limits.inputDepth`, before anything
|
|
6
|
+
* evaluates it: a 400 problem, so the run is not created.
|
|
7
|
+
*/
|
|
8
|
+
const checkRunInput = (input) => {
|
|
9
|
+
if (nestingDepth(input, limits.inputDepth) > limits.inputDepth) throw badRequest("input-too-deep", `A run's input nests at most ${limits.inputDepth} levels of arrays and objects`);
|
|
10
|
+
const bytes = new TextEncoder().encode(JSON.stringify(input ?? null)).length;
|
|
11
|
+
if (bytes > limits.inputBytes) throw badRequest("input-too-large", `A run's input is at most ${limits.inputBytes / 1024 / 1024} MiB as JSON; this one is ${bytes} bytes`);
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* The same limits on a run's input as the API sent it, before the body is validated, whose walk of a deeply nested
|
|
15
|
+
* value would overflow the call stack.
|
|
16
|
+
*/
|
|
17
|
+
const runInputCheck = async ({ resource, operation, body }) => {
|
|
18
|
+
if (resource !== "workflow_runs" || operation !== "create" || typeof body !== "object" || body === null) return;
|
|
19
|
+
checkRunInput(body.input);
|
|
20
|
+
};
|
|
21
|
+
//#endregion
|
|
22
|
+
export { checkRunInput, runInputCheck };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { readRunFile, runFileLimit } from "./run-files.js";
|
|
2
|
+
import { StepError } from "@protobase/workflows";
|
|
3
|
+
const invalid = (reason) => new StepError("Mail.InvalidArguments", reason);
|
|
4
|
+
const line = (value, name) => {
|
|
5
|
+
if (typeof value !== "string" || value.trim() === "") throw invalid(`${name} must be text`);
|
|
6
|
+
if (/[\r\n]/.test(value)) throw invalid(`${name} must be one line`);
|
|
7
|
+
return value;
|
|
8
|
+
};
|
|
9
|
+
const recipients = (value) => {
|
|
10
|
+
const list = Array.isArray(value) ? value : [value];
|
|
11
|
+
if (list.length === 0 || list.length > 50) throw invalid(`to must be one address or a list of 1 to 50`);
|
|
12
|
+
return list.map((address, index) => {
|
|
13
|
+
const name = Array.isArray(value) ? `to[${index}]` : "to";
|
|
14
|
+
if (/[,;]/.test(line(address, name))) throw invalid(`${name} must be one address; give several as a list`);
|
|
15
|
+
return address;
|
|
16
|
+
}).join(", ");
|
|
17
|
+
};
|
|
18
|
+
const attachmentsOf = async (value, files, runId) => {
|
|
19
|
+
if (value === void 0 || value === null) return [];
|
|
20
|
+
if (!Array.isArray(value)) throw invalid("attachments must be a list of file values");
|
|
21
|
+
const attachments = [];
|
|
22
|
+
let total = 0;
|
|
23
|
+
for (const [index, item] of value.entries()) {
|
|
24
|
+
const { ref, bytes } = await readRunFile(files, runId, item, `attachments[${index}]`);
|
|
25
|
+
total += bytes.length;
|
|
26
|
+
if (total > 10485760) throw invalid(`attachments are larger than ${runFileLimit / 1024 / 1024} MiB together`);
|
|
27
|
+
attachments.push({
|
|
28
|
+
filename: ref.name,
|
|
29
|
+
content: bytes,
|
|
30
|
+
...ref.type && { contentType: ref.type }
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
return attachments;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* The `app:mail.send` step: one message from the app's own sender, with the app's mailer; a workflow cannot set the
|
|
37
|
+
* sender. Its Message-ID comes from the step's idempotency key, the same for each try, so a receiver can tell a
|
|
38
|
+
* repeated try from a new mail.
|
|
39
|
+
*/
|
|
40
|
+
const mailStep = (mail, files) => async (args, { runId, idempotencyKey }) => {
|
|
41
|
+
const given = args ?? {};
|
|
42
|
+
const mailer = mail();
|
|
43
|
+
if (!mailer) throw new StepError("States.TaskFailed", "The app sends no mail: set its mailer, or PROTOBASE_SMTP_URL and PROTOBASE_MAIL_FROM");
|
|
44
|
+
if (typeof given.text !== "string") throw invalid("text must be text");
|
|
45
|
+
if (given.html !== void 0 && typeof given.html !== "string") throw invalid("html must be text");
|
|
46
|
+
const messageId = `<${idempotencyKey.replace(/[^A-Za-z0-9.-]/g, ".")}@workflows.invalid>`;
|
|
47
|
+
await mailer.send({
|
|
48
|
+
to: recipients(given.to),
|
|
49
|
+
subject: line(given.subject, "subject"),
|
|
50
|
+
text: given.text,
|
|
51
|
+
...given.html !== void 0 && { html: given.html },
|
|
52
|
+
headers: { "Message-ID": messageId },
|
|
53
|
+
attachments: await attachmentsOf(given.attachments, files(), runId)
|
|
54
|
+
});
|
|
55
|
+
return { message_id: messageId };
|
|
56
|
+
};
|
|
57
|
+
//#endregion
|
|
58
|
+
export { mailStep };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { TenantValue } from "../types.js";
|
|
2
|
+
//#region src/workflows/options.d.ts
|
|
3
|
+
/** Workflows made in the admin app (`workflows: {}`), kept with their runs in a schema of the app's database. */
|
|
4
|
+
export type WorkflowsOptions = {
|
|
5
|
+
/** The schema of the workflows, their versions, runs and steps; default `workflows`. */
|
|
6
|
+
schema?: string;
|
|
7
|
+
/**
|
|
8
|
+
* The environment variables workflows may read, each as `$states.context.Secrets.<NAME>`; no other is reachable.
|
|
9
|
+
* Their values are blanked out of all a run records. Startup stops when one is unset or shorter than 8 characters.
|
|
10
|
+
*/
|
|
11
|
+
secrets?: string[];
|
|
12
|
+
/**
|
|
13
|
+
* The roles a workflow's last editor holds now in its tenant, which its records steps act with. Default: read from
|
|
14
|
+
* the project's Better Auth (`auth`); a project that signs people in otherwise passes its own, which the CLI reads
|
|
15
|
+
* from the default export of `workflows/saved-by-roles.ts`.
|
|
16
|
+
*/
|
|
17
|
+
savedByRoles?: (user: {
|
|
18
|
+
id: string;
|
|
19
|
+
tenant?: TenantValue;
|
|
20
|
+
}) => Promise<string[]>;
|
|
21
|
+
/**
|
|
22
|
+
* Lets the `app:http` step reach private, loopback and link-local addresses, for development and tests. Default
|
|
23
|
+
* false: a URL whose host resolves to one fails the step, so a workflow cannot reach the app's own network.
|
|
24
|
+
*/
|
|
25
|
+
allowPrivateAddresses?: boolean;
|
|
26
|
+
};
|
|
27
|
+
//#endregion
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
//#region src/workflows/options.ts
|
|
2
|
+
const schemaPattern = /^[a-z_][a-z0-9_]*$/;
|
|
3
|
+
const secretName = /^[A-Z_][A-Z0-9_]*$/;
|
|
4
|
+
/** The schema of the options, checked. */
|
|
5
|
+
const workflowsSchema = (options = {}) => {
|
|
6
|
+
const schema = options.schema ?? "workflows";
|
|
7
|
+
if (!schemaPattern.test(schema)) throw new Error(`Invalid workflows schema "${schema}": use lowercase letters, digits and "_"`);
|
|
8
|
+
if (schema.length > 63) throw new Error(`Invalid workflows schema "${schema}": at most 63 characters`);
|
|
9
|
+
return schema;
|
|
10
|
+
};
|
|
11
|
+
/** The secrets workflows may read, by name, from `env`; startup stops on a name that is invalid or unset. */
|
|
12
|
+
const workflowSecrets = (options, env) => Object.fromEntries((options.secrets ?? []).map((name) => {
|
|
13
|
+
if (!secretName.test(name)) throw new Error(`workflows.secrets: "${name}" is not an environment variable name; use capitals, digits and "_"`);
|
|
14
|
+
const value = env[name];
|
|
15
|
+
if (value === void 0 || value === "") throw new Error(`workflows.secrets names ${name}, which is not set`);
|
|
16
|
+
return [name, value];
|
|
17
|
+
}));
|
|
18
|
+
//#endregion
|
|
19
|
+
export { workflowSecrets, workflowsSchema };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { StepError } from "@protobase/workflows";
|
|
2
|
+
//#region src/workflows/piece-step.ts
|
|
3
|
+
/**
|
|
4
|
+
* The `app:piece` step, which a definition may name but which runs no piece yet: it fails with `Piece.Error` until the
|
|
5
|
+
* app's plugins give it a handler that sends through the plugin context's guarded fetch.
|
|
6
|
+
*/
|
|
7
|
+
const pieceStep = async () => {
|
|
8
|
+
throw new StepError("Piece.Error", "No piece runs in this app yet");
|
|
9
|
+
};
|
|
10
|
+
//#endregion
|
|
11
|
+
export { pieceStep };
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { HttpProblem } from "../problem.js";
|
|
2
|
+
import { tenantScope } from "../tenant.js";
|
|
3
|
+
import { issueTicket } from "../files/ticket.js";
|
|
4
|
+
import { assistantRecords, toolEntry } from "../assistant/records.js";
|
|
5
|
+
import { runFileRef } from "./run-files.js";
|
|
6
|
+
import { sql } from "kysely";
|
|
7
|
+
import { StepError } from "@protobase/workflows";
|
|
8
|
+
//#region src/workflows/records-step.ts
|
|
9
|
+
const ticketSeconds = 60;
|
|
10
|
+
/**
|
|
11
|
+
* The session a run's records steps act in: the user who saved the version the run pinned, with the roles they hold
|
|
12
|
+
* now in the run's tenant, so a workflow never does more than its last editor may do today.
|
|
13
|
+
*/
|
|
14
|
+
const saverSession = async ({ deps, schema, roles }, runId) => {
|
|
15
|
+
const { rows } = await sql`select v.saved_by, r.tenant_id from ${sql.id(schema, "runs")} r
|
|
16
|
+
join ${sql.id(schema, "versions")} v on v.workflow_id = r.workflow_id and v.version = r.version where r.id = ${runId}`.execute(deps().db);
|
|
17
|
+
const row = rows[0];
|
|
18
|
+
if (!row?.saved_by) throw new StepError("Records.Refused", "The run's version was not saved by a user, so its steps act on no records");
|
|
19
|
+
const tenant = row.tenant_id ?? void 0;
|
|
20
|
+
const held = await roles({
|
|
21
|
+
id: row.saved_by,
|
|
22
|
+
...tenant !== void 0 && { tenant }
|
|
23
|
+
});
|
|
24
|
+
if (held.length === 0) throw new StepError("Records.Refused", "The workflow's last editor holds no role here any more, so its steps act on no records");
|
|
25
|
+
return {
|
|
26
|
+
user: {
|
|
27
|
+
id: row.saved_by,
|
|
28
|
+
roles: held
|
|
29
|
+
},
|
|
30
|
+
...tenant !== void 0 && {
|
|
31
|
+
tenant,
|
|
32
|
+
organization: { id: tenant }
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
const withTickets = async (deps, session, resource, data, runId) => {
|
|
37
|
+
const entry = toolEntry(deps, resource);
|
|
38
|
+
const result = { ...data };
|
|
39
|
+
for (const [name, value] of Object.entries(data)) {
|
|
40
|
+
const field = entry.model.fields[name];
|
|
41
|
+
if (field?.type === "decimal" && typeof value === "number") result[name] = String(value);
|
|
42
|
+
if (!field?.file || value === null || typeof value !== "object") continue;
|
|
43
|
+
if (!deps.files) throw new StepError("States.TaskFailed", "The app keeps no files");
|
|
44
|
+
const ref = runFileRef(runId, value, `data.${name}`);
|
|
45
|
+
const scope = {
|
|
46
|
+
tenant: String(tenantScope(entry, session)?.tenantValue ?? ""),
|
|
47
|
+
resource: entry.name,
|
|
48
|
+
field: name,
|
|
49
|
+
user: String(session.user.id)
|
|
50
|
+
};
|
|
51
|
+
result[name] = await issueTicket(deps.files.signer, {
|
|
52
|
+
provider: ref.provider,
|
|
53
|
+
path: ref.path,
|
|
54
|
+
name: ref.name,
|
|
55
|
+
...ref.size !== void 0 && { size: ref.size }
|
|
56
|
+
}, {}, scope, Math.floor(Date.now() / 1e3) + ticketSeconds);
|
|
57
|
+
}
|
|
58
|
+
return result;
|
|
59
|
+
};
|
|
60
|
+
const text = (value, name) => {
|
|
61
|
+
if (typeof value !== "string" || value === "") throw new StepError("Records.InvalidArguments", `${name} must be text`);
|
|
62
|
+
return value;
|
|
63
|
+
};
|
|
64
|
+
const keyOf = (value) => {
|
|
65
|
+
if (typeof value === "string" || typeof value === "number") return value;
|
|
66
|
+
if (Array.isArray(value) && value.every((part) => typeof part === "string" || typeof part === "number")) return value;
|
|
67
|
+
throw new StepError("Records.InvalidArguments", "key must be text, a number or a list of them");
|
|
68
|
+
};
|
|
69
|
+
const rowOf = (value, name) => {
|
|
70
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new StepError("Records.InvalidArguments", `${name} must be an object of field values`);
|
|
71
|
+
return value;
|
|
72
|
+
};
|
|
73
|
+
const refusedAs = (error) => {
|
|
74
|
+
if (error instanceof HttpProblem) throw new StepError("Records.Refused", `${error.status} ${error.title}: ${error.detail}`);
|
|
75
|
+
throw error;
|
|
76
|
+
};
|
|
77
|
+
const run = async (input, operation, args, { runId }) => {
|
|
78
|
+
const resource = text(args.resource, "resource");
|
|
79
|
+
const deps = input.deps();
|
|
80
|
+
const session = await saverSession(input, runId);
|
|
81
|
+
const records = assistantRecords(deps, session);
|
|
82
|
+
if (operation === "get") return (await records.get(resource, keyOf(args.key))).record;
|
|
83
|
+
if (operation === "update") return (await records.update(resource, keyOf(args.key), await withTickets(deps, session, resource, rowOf(args.data, "data"), runId))).record;
|
|
84
|
+
if (operation === "list") return { records: (await records.list(resource, {
|
|
85
|
+
...args.filter !== void 0 && { filter: text(args.filter, "filter") },
|
|
86
|
+
...args.order_by !== void 0 && { order_by: text(args.order_by, "order_by") },
|
|
87
|
+
...args.limit !== void 0 && { page_size: Number(args.limit) }
|
|
88
|
+
})).items.map(({ etag: _, permissions: __, ...record }) => record) };
|
|
89
|
+
if (Array.isArray(args.data)) {
|
|
90
|
+
const operations = [];
|
|
91
|
+
for (const [index, item] of args.data.entries()) operations.push({
|
|
92
|
+
op: "create",
|
|
93
|
+
resource,
|
|
94
|
+
data: await withTickets(deps, session, resource, rowOf(item, `data[${index}]`), runId)
|
|
95
|
+
});
|
|
96
|
+
return (await records.batch(operations)).flatMap((result) => result.op === "create" ? [result.record] : []);
|
|
97
|
+
}
|
|
98
|
+
return (await records.create(resource, await withTickets(deps, session, resource, rowOf(args.data, "data"), runId))).record;
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* The `app:records.*` steps: `create`, `update`, `get` and `list` through the app's API as the workflow's last editor,
|
|
102
|
+
* so its access rules, tenant scope, validation and write hooks apply as they do to that user's own requests.
|
|
103
|
+
*/
|
|
104
|
+
const recordsSteps = (input) => {
|
|
105
|
+
const step = (operation) => (args, context) => run(input, operation, rowOf(args ?? {}, "Arguments"), context).catch(refusedAs);
|
|
106
|
+
return {
|
|
107
|
+
"app:records.create": step("create"),
|
|
108
|
+
"app:records.update": step("update"),
|
|
109
|
+
"app:records.get": step("get"),
|
|
110
|
+
"app:records.list": step("list")
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
//#endregion
|
|
114
|
+
export { recordsSteps };
|