@tailor-platform/sdk 1.52.0 → 1.53.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/application-Cmz1Y7X_.mjs +4 -0
  3. package/dist/{application-CECifcE9.mjs → application-DF74unzA.mjs} +2 -2
  4. package/dist/{application-CECifcE9.mjs.map → application-DF74unzA.mjs.map} +1 -1
  5. package/dist/cli/index.d.mts +1 -3
  6. package/dist/cli/index.mjs +5 -5
  7. package/dist/cli/lib.d.mts +1 -2
  8. package/dist/cli/lib.mjs +3 -3
  9. package/dist/client-CGO7gniI.mjs +1061 -0
  10. package/dist/{client-CKlZhiq4.mjs.map → client-CGO7gniI.mjs.map} +1 -1
  11. package/dist/{client-btP2hNF1.mjs → client-yfFdZU9s.mjs} +1 -1
  12. package/dist/configure/index.d.mts +1 -1
  13. package/dist/{crashreport-DJ5SS5Q-.mjs → crashreport-DGdAgX8Y.mjs} +2 -2
  14. package/dist/{crashreport-DJ5SS5Q-.mjs.map → crashreport-DGdAgX8Y.mjs.map} +1 -1
  15. package/dist/{crashreport-CGE6P9Kf.mjs → crashreport-DnwIxpzF.mjs} +1 -1
  16. package/dist/{file-umdycPAF.mjs → file-B58Dm-2P.mjs} +18 -3
  17. package/dist/file-B58Dm-2P.mjs.map +1 -0
  18. package/dist/{file-DaCYOLPz.d.mts → file-VTJbbOL3.d.mts} +49 -5
  19. package/dist/{index-wuuZLoFt.d.mts → index-ECerapTN.d.mts} +2 -2
  20. package/dist/{mock-BabuSqbj.mjs → mock-B6PI49C_.mjs} +25 -2
  21. package/dist/{mock-BabuSqbj.mjs.map → mock-B6PI49C_.mjs.map} +1 -1
  22. package/dist/runtime/file.d.mts +2 -2
  23. package/dist/runtime/file.mjs +2 -2
  24. package/dist/runtime/globals.d.mts +2 -2
  25. package/dist/runtime/index.d.mts +2 -2
  26. package/dist/runtime/index.mjs +1 -1
  27. package/dist/{runtime-BN484A6U.mjs → runtime-D-3AE1x-.mjs} +4 -4
  28. package/dist/{runtime-BN484A6U.mjs.map → runtime-D-3AE1x-.mjs.map} +1 -1
  29. package/dist/vitest/environment.mjs +1 -1
  30. package/dist/vitest/index.mjs +1 -1
  31. package/dist/vitest/setup.mjs +1 -1
  32. package/docs/runtime.md +1 -1
  33. package/docs/testing.md +21 -1
  34. package/package.json +9 -9
  35. package/dist/application-B_yEREFy.mjs +0 -4
  36. package/dist/client-CKlZhiq4.mjs +0 -1061
  37. package/dist/file-umdycPAF.mjs.map +0 -1
@@ -1,4 +1,4 @@
1
1
 
2
- import { a as fetchMachineUserToken, c as fetchUserInfo, d as initOperatorClient, f as parseMethodName, h as userAgent, i as fetchAll, l as formatRequestParams, m as resolveStaticWebsiteUrls, n as closeConnectionPool, o as fetchPaged, p as platformBaseUrl, r as createTransport, s as fetchPlatformMachineUserToken, t as MAX_PAGE_SIZE, u as initOAuth2Client } from "./client-CKlZhiq4.mjs";
2
+ import { a as fetchMachineUserToken, c as fetchUserInfo, d as initOperatorClient, f as parseMethodName, h as userAgent, i as fetchAll, l as formatRequestParams, m as resolveStaticWebsiteUrls, n as closeConnectionPool, o as fetchPaged, p as platformBaseUrl, r as createTransport, s as fetchPlatformMachineUserToken, t as MAX_PAGE_SIZE, u as initOAuth2Client } from "./client-CGO7gniI.mjs";
3
3
 
4
4
  export { userAgent };
@@ -3,4 +3,4 @@ import { B as BeforeLoginHookArgs, C as ResolverReadyContext, Ct as SCIMAuthoriz
3
3
  import { A as PermissionCondition, D as TailorDBInstance, E as TailorDBField, M as TailorTypePermission, N as unsafeAllowAllGqlPermission, O as TailorDBType, P as unsafeAllowAllTypePermission, S as IdPGqlOperationsInput, T as TailorAnyDBType, _ as IdPExternalConfig, a as ExecutorServiceInput, b as IdPEmailConfig, c as ResolverServiceInput, d as StaticWebsiteConfig, g as IdPConfig, i as ExecutorServiceConfig, j as TailorTypeGqlPermission, k as db, l as WorkflowServiceConfig, m as SecretsConfig, n as RetryPolicy, o as ResolverExternalConfig, s as ResolverServiceConfig, t as ConcurrencyPolicy, u as WorkflowServiceInput, w as TailorAnyDBField, x as IdPGqlOperations } from "../workflow.generated-BIY41La-.mjs";
4
4
  import { $ as recordDeletedTrigger, A as AuthAccessTokenRefreshedArgs, B as RecordUpdatedArgs, C as IncomingWebhookTriggerOptions, Ct as MachineUserNameRegistry, D as scheduleTrigger, E as ScheduleTrigger, F as IdpUserDeletedArgs, G as authAccessTokenRefreshedTrigger, H as ResolverExecutedTrigger, I as IdpUserTrigger, J as idpUserCreatedTrigger, K as authAccessTokenRevokedTrigger, L as IdpUserUpdatedArgs, M as AuthAccessTokenTrigger, N as IdpUserArgs, O as AuthAccessTokenArgs, P as IdpUserCreatedArgs, Q as recordCreatedTrigger, R as RecordCreatedArgs, S as IncomingWebhookTrigger, St as MachineUserName, T as ScheduleArgs, U as TailorDBTrigger, V as ResolverExecutedArgs, W as authAccessTokenIssuedTrigger, X as idpUserTrigger, Y as idpUserDeletedTrigger, Z as idpUserUpdatedTrigger, _ as Trigger, _t as AuthInvoker, a as defineGenerators, at as Operation, b as IncomingWebhookResponse, bt as IdpNameRegistry, c as defineIdp, ct as Workflow, d as unsafeAllowAllIdPPermission, dt as WORKFLOW_TEST_ENV_KEY, et as recordTrigger, f as defineStaticWebSite, ft as WorkflowJob, g as createExecutor, gt as createResolver, h as defineWaitPoints, ht as QueryType, i as defineConfig, it as GqlOperation, j as AuthAccessTokenRevokedArgs, k as AuthAccessTokenIssuedArgs, l as IdPPermission, lt as WorkflowConfig, m as defineWaitPoint, mt as createWorkflowJob, n as output, nt as resolverExecutedTrigger, o as definePlugins, ot as WebhookOperation, p as WaitPointInstance, pt as WorkflowJobContext, q as authAccessTokenTrigger, r as t, rt as FunctionOperation, s as defineSecretManager, st as WorkflowOperation, t as __Infer, tt as recordUpdatedTrigger, u as IdPPermissionCondition, ut as createWorkflow, v as IncomingWebhookArgs, vt as defineAuth, w as incomingWebhookTrigger, x as IncomingWebhookResponseConfig, xt as TailorField, y as IncomingWebhookRequest, yt as IdpName, z as RecordDeletedArgs } from "../index-UySZfxON.mjs";
5
5
  import { t as Env } from "../env-B-g-qgE4.mjs";
6
- export { AttributeList, AttributeMap, AuthAccessTokenArgs, AuthAccessTokenIssuedArgs, AuthAccessTokenRefreshedArgs, AuthAccessTokenRevokedArgs, AuthAccessTokenTrigger, AuthConfig, AuthConnectionConfig, AuthConnectionOAuth2Config, AuthConnectionTokenResult, AuthExternalConfig, AuthInvoker, AuthOwnConfig, AuthServiceInput, BeforeLoginHookArgs, BuiltinIdP, ConcurrencyPolicy, DefinedAuth, Env, ExecutorReadyContext, ExecutorServiceConfig, ExecutorServiceInput, FunctionOperation, GeneratorResult, GqlOperation, IDToken, IdPConfig, IdPEmailConfig, IdPExternalConfig, IdPGqlOperations, IdPGqlOperationsInput as IdPGqlOperationsConfig, IdPPermission, IdPPermissionCondition, IdProvider as IdProviderConfig, IdpName, IdpNameRegistry, IdpUserArgs, IdpUserCreatedArgs, IdpUserDeletedArgs, IdpUserTrigger, IdpUserUpdatedArgs, IncomingWebhookArgs, IncomingWebhookRequest, IncomingWebhookResponse, IncomingWebhookResponseConfig, IncomingWebhookTrigger, IncomingWebhookTriggerOptions, MachineUserName, MachineUserNameRegistry, NamespacePluginOutput, OAuth2ClientInput as OAuth2Client, OAuth2ClientGrantType, OIDC, Operation, PermissionCondition, Plugin, PluginAttachment, PluginConfigs, PluginExecutorContext, PluginExecutorContextBase, PluginGeneratedExecutor, PluginGeneratedExecutorWithFile, PluginGeneratedResolver, PluginGeneratedType, PluginNamespaceProcessContext, PluginOutput, PluginProcessContext, QueryType, RecordCreatedArgs, RecordDeletedArgs, RecordUpdatedArgs, Resolver, ResolverExecutedArgs, ResolverExecutedTrigger, ResolverExternalConfig, ResolverNamespaceData, ResolverReadyContext, ResolverServiceConfig, ResolverServiceInput, RetryPolicy, SAML, SCIMAttribute, SCIMAttributeMapping, SCIMAttributeType, SCIMAuthorization, SCIMConfig, SCIMResource, ScheduleArgs, ScheduleTrigger, SecretsConfig, StaticWebsiteConfig, TailorAnyDBField, TailorAnyDBType, TailorDBField, TailorDBInstance, TailorDBNamespaceData, TailorDBReadyContext, TailorDBTrigger, TailorDBType, TailorDBTypeForPlugin, TailorField, TailorInvoker, TailorTypeGqlPermission, TailorTypePermission, TailorUser, TenantProvider as TenantProviderConfig, Trigger, TypePluginOutput, UserAttributeKey, UserAttributeListKey, UserAttributeMap, UsernameFieldKey, ValueOperand, WORKFLOW_TEST_ENV_KEY, WaitPointInstance, WebhookOperation, Workflow, WorkflowConfig, WorkflowJob, WorkflowJobContext, WorkflowOperation, WorkflowServiceConfig, WorkflowServiceInput, authAccessTokenIssuedTrigger, authAccessTokenRefreshedTrigger, authAccessTokenRevokedTrigger, authAccessTokenTrigger, createExecutor, createResolver, createWorkflow, createWorkflowJob, db, defineAuth, defineConfig, defineGenerators, defineIdp, definePlugins, defineSecretManager, defineStaticWebSite, defineWaitPoint, defineWaitPoints, idpUserCreatedTrigger, idpUserDeletedTrigger, idpUserTrigger, idpUserUpdatedTrigger, incomingWebhookTrigger, infer, output, recordCreatedTrigger, recordDeletedTrigger, recordTrigger, recordUpdatedTrigger, resolverExecutedTrigger, scheduleTrigger, t, unauthenticatedTailorUser, unsafeAllowAllGqlPermission, unsafeAllowAllIdPPermission, unsafeAllowAllTypePermission };
6
+ export { type AttributeList, type AttributeMap, AuthAccessTokenArgs, AuthAccessTokenIssuedArgs, AuthAccessTokenRefreshedArgs, AuthAccessTokenRevokedArgs, AuthAccessTokenTrigger, type AuthConfig, type AuthConnectionConfig, type AuthConnectionOAuth2Config, type AuthConnectionTokenResult, type AuthExternalConfig, AuthInvoker, type AuthOwnConfig, type AuthServiceInput, type BeforeLoginHookArgs, type BuiltinIdP, type ConcurrencyPolicy, type DefinedAuth, type Env, type ExecutorReadyContext, type ExecutorServiceConfig, type ExecutorServiceInput, FunctionOperation, type GeneratorResult, GqlOperation, type IDToken, type IdPConfig, type IdPEmailConfig, type IdPExternalConfig, type IdPGqlOperations, type IdPGqlOperationsInput as IdPGqlOperationsConfig, type IdPPermission, type IdPPermissionCondition, type IdProvider as IdProviderConfig, type IdpName, type IdpNameRegistry, IdpUserArgs, IdpUserCreatedArgs, IdpUserDeletedArgs, IdpUserTrigger, IdpUserUpdatedArgs, IncomingWebhookArgs, IncomingWebhookRequest, IncomingWebhookResponse, IncomingWebhookResponseConfig, IncomingWebhookTrigger, IncomingWebhookTriggerOptions, type MachineUserName, type MachineUserNameRegistry, type NamespacePluginOutput, type OAuth2ClientInput as OAuth2Client, type OAuth2ClientGrantType, type OIDC, Operation, type PermissionCondition, type Plugin, type PluginAttachment, type PluginConfigs, type PluginExecutorContext, type PluginExecutorContextBase, type PluginGeneratedExecutor, type PluginGeneratedExecutorWithFile, type PluginGeneratedResolver, type PluginGeneratedType, type PluginNamespaceProcessContext, type PluginOutput, type PluginProcessContext, type QueryType, RecordCreatedArgs, RecordDeletedArgs, RecordUpdatedArgs, type Resolver, ResolverExecutedArgs, ResolverExecutedTrigger, type ResolverExternalConfig, type ResolverNamespaceData, type ResolverReadyContext, type ResolverServiceConfig, type ResolverServiceInput, type RetryPolicy, type SAML, type SCIMAttribute, type SCIMAttributeMapping, type SCIMAttributeType, type SCIMAuthorization, type SCIMConfig, type SCIMResource, ScheduleArgs, ScheduleTrigger, type SecretsConfig, type StaticWebsiteConfig, type TailorAnyDBField, type TailorAnyDBType, type TailorDBField, type TailorDBInstance, type TailorDBNamespaceData, type TailorDBReadyContext, TailorDBTrigger, type TailorDBType, type TailorDBTypeForPlugin, type TailorField, type TailorInvoker, type TailorTypeGqlPermission, type TailorTypePermission, type TailorUser, type TenantProvider as TenantProviderConfig, Trigger, type TypePluginOutput, type UserAttributeKey, type UserAttributeListKey, type UserAttributeMap, type UsernameFieldKey, type ValueOperand, WORKFLOW_TEST_ENV_KEY, WaitPointInstance, WebhookOperation, Workflow, WorkflowConfig, WorkflowJob, WorkflowJobContext, WorkflowOperation, type WorkflowServiceConfig, type WorkflowServiceInput, authAccessTokenIssuedTrigger, authAccessTokenRefreshedTrigger, authAccessTokenRevokedTrigger, authAccessTokenTrigger, createExecutor, createResolver, createWorkflow, createWorkflowJob, db, defineAuth, defineConfig, defineGenerators, defineIdp, definePlugins, defineSecretManager, defineStaticWebSite, defineWaitPoint, defineWaitPoints, idpUserCreatedTrigger, idpUserDeletedTrigger, idpUserTrigger, idpUserUpdatedTrigger, incomingWebhookTrigger, infer, output, recordCreatedTrigger, recordDeletedTrigger, recordTrigger, recordUpdatedTrigger, resolverExecutedTrigger, scheduleTrigger, t, unauthenticatedTailorUser, unsafeAllowAllGqlPermission, unsafeAllowAllIdPPermission, unsafeAllowAllTypePermission };
@@ -386,7 +386,7 @@ async function reportCrash(error, errorType) {
386
386
  ].join("\n"));
387
387
  }
388
388
  if (config.remoteEnabled) {
389
- const { userAgent } = await import("./client-btP2hNF1.mjs");
389
+ const { userAgent } = await import("./client-yfFdZU9s.mjs");
390
390
  await sendCrashReport(report, await userAgent());
391
391
  }
392
392
  } catch {}
@@ -412,4 +412,4 @@ function initCrashReporting() {
412
412
 
413
413
  //#endregion
414
414
  export { JSON_FOOTER_MARKER as a, CRASH_LOG_EXTENSION as i, reportCrash as n, parseCrashReportConfig as o, sendCrashReport as r, initCrashReporting as t };
415
- //# sourceMappingURL=crashreport-DJ5SS5Q-.mjs.map
415
+ //# sourceMappingURL=crashreport-DGdAgX8Y.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"crashreport-DJ5SS5Q-.mjs","names":[],"sources":["../src/cli/crashreport/config.ts","../src/cli/crashreport/writer.ts","../src/cli/crashreport/sender.ts","../src/cli/crashreport/sanitize.ts","../src/cli/crashreport/report.ts","../src/cli/crashreport/index.ts"],"sourcesContent":["import * as path from \"pathe\";\nimport { isCI } from \"std-env\";\nimport { xdgConfig } from \"xdg-basedir\";\n\nexport interface CrashReportConfig {\n readonly localEnabled: boolean;\n readonly remoteEnabled: boolean;\n readonly localDir: string;\n}\n\n/**\n * Parse crash report configuration from environment variables.\n * Local crash log writing is enabled by default (opt-out via TAILOR_CRASH_REPORTS_LOCAL=off).\n * Remote sending is disabled by default (opt-in via TAILOR_CRASH_REPORTS_REMOTE=on).\n * Both are auto-disabled in CI environments.\n * @returns Crash report configuration\n */\nexport function parseCrashReportConfig(): CrashReportConfig {\n if (isCI) {\n return {\n localEnabled: false,\n remoteEnabled: false,\n localDir: \"\",\n };\n }\n\n const localEnabled = (process.env.TAILOR_CRASH_REPORTS_LOCAL ?? \"on\").toLowerCase() !== \"off\";\n const remoteEnabled = (process.env.TAILOR_CRASH_REPORTS_REMOTE ?? \"off\").toLowerCase() === \"on\";\n const localDir = xdgConfig ? path.join(xdgConfig, \"tailor-platform\", \"crash-reports\") : \"\";\n\n return {\n localEnabled: localEnabled && localDir !== \"\",\n remoteEnabled,\n localDir,\n };\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"pathe\";\nimport { ensureSecretDir, writeSecretFile } from \"@/cli/shared/secret-file\";\nimport type { CrashReport } from \"./report\";\n\nconst MAX_CRASH_FILES = 10;\n\n/** Marker line that separates human-readable content from the JSON footer. */\nexport const JSON_FOOTER_MARKER = \"--- JSON ---\";\n\n/** File extension for crash log files. */\nexport const CRASH_LOG_EXTENSION = \".crash.log\";\n\n/**\n * Format a CrashReport as human-readable text for local crash log files.\n * @param report - Crash report to format\n * @returns Formatted text content\n */\nexport function formatCrashReport(report: CrashReport): string {\n const lines = [\n `Crash Report: ${report.id}`,\n `Timestamp: ${report.timestamp}`,\n `Error Type: ${report.errorType}`,\n \"\",\n \"--- Environment ---\",\n `SDK Version: ${report.sdkVersion}`,\n `Node Version: ${report.nodeVersion}`,\n `OS: ${report.osPlatform} ${report.osRelease}`,\n `Arch: ${report.arch}`,\n \"\",\n \"--- Command ---\",\n `Command: ${report.command}`,\n `Arguments: ${JSON.stringify(report.argv)}`,\n \"\",\n \"--- Error ---\",\n `Name: ${report.errorName}`,\n `Message: ${report.errorMessage}`,\n \"\",\n \"--- Stack Trace ---\",\n report.stackTrace || \"(no stack trace available)\",\n \"\",\n JSON_FOOTER_MARKER,\n JSON.stringify(report),\n \"\",\n ];\n return lines.join(\"\\n\");\n}\n\n/**\n * Generate a filename for a crash log file.\n * Format: {timestamp}-{shortId}.crash.log\n * @param report - Crash report to generate filename for\n * @returns Filename string\n */\nfunction generateFilename(report: CrashReport): string {\n const safeTimestamp = report.timestamp.replace(/[:.]/g, \"-\");\n const shortId = report.id.slice(0, 8);\n return `${safeTimestamp}-${shortId}${CRASH_LOG_EXTENSION}`;\n}\n\n/**\n * Remove old crash log files, keeping only the most recent ones.\n * @param dir - Crash log directory\n */\nfunction cleanupOldFiles(dir: string): void {\n try {\n const files = fs\n .readdirSync(dir)\n .filter((f) => f.endsWith(CRASH_LOG_EXTENSION))\n .sort()\n .reverse();\n\n for (const file of files.slice(MAX_CRASH_FILES)) {\n fs.unlinkSync(path.join(dir, file));\n }\n } catch {\n // Best-effort cleanup, ignore errors\n }\n}\n\n/**\n * Write a crash report to a local file.\n * Creates the directory if it doesn't exist. Keeps only the last 10 crash files.\n * Never throws - returns the file path on success or undefined on failure.\n * @param report - Crash report to write\n * @param dir - Directory to write the crash log file to\n * @returns File path on success, undefined on failure\n */\nexport function writeCrashReport(report: CrashReport, dir: string): string | undefined {\n try {\n ensureSecretDir(dir);\n\n const filename = generateFilename(report);\n const filePath = path.join(dir, filename);\n const content = formatCrashReport(report);\n\n writeSecretFile(filePath, content);\n cleanupOldFiles(dir);\n\n return filePath;\n } catch {\n return undefined;\n }\n}\n","import type { CrashReport } from \"./report\";\n\nconst SEND_TIMEOUT_MS = 5000;\nconst PRODUCTION_ENDPOINT = \"https://sdk-error-tracking-926vh9t4cl.erp.dev/query\";\n\nconst SUBMIT_MUTATION = `\nmutation SubmitCrashReport(\n $id: String!\n $timestamp: String!\n $sdkVersion: String!\n $nodeVersion: String!\n $osPlatform: String!\n $osRelease: String!\n $arch: String!\n $command: String!\n $argv: [String]\n $errorName: String!\n $errorMessage: String!\n $stackTrace: String\n $errorType: String!\n $userId: String\n $userEmail: String\n) {\n submitCrashReport(\n id: $id\n timestamp: $timestamp\n sdkVersion: $sdkVersion\n nodeVersion: $nodeVersion\n osPlatform: $osPlatform\n osRelease: $osRelease\n arch: $arch\n command: $command\n argv: $argv\n errorName: $errorName\n errorMessage: $errorMessage\n stackTrace: $stackTrace\n errorType: $errorType\n userId: $userId\n userEmail: $userEmail\n ) {\n success\n }\n}`;\n\n/**\n * Send a crash report to the remote endpoint via GraphQL mutation.\n * Best-effort: never throws, returns boolean success.\n * @param report - Crash report to send\n * @param ua - User-Agent header value\n * @returns true if the request succeeded, false otherwise\n */\nexport async function sendCrashReport(report: CrashReport, ua: string): Promise<boolean> {\n try {\n const endpoint = process.env.TAILOR_CRASH_REPORT_ENDPOINT || PRODUCTION_ENDPOINT;\n const response = await fetch(endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"User-Agent\": ua,\n },\n body: JSON.stringify({\n query: SUBMIT_MUTATION,\n variables: report,\n }),\n signal: AbortSignal.timeout(SEND_TIMEOUT_MS),\n });\n\n if (!response.ok) return false;\n\n const data = (await response.json()) as {\n errors?: unknown[];\n data?: { submitCrashReport: { success: boolean } };\n };\n if (data.errors?.length) return false;\n return data.data?.submitCrashReport.success === true;\n } catch {\n return false;\n }\n}\n","import * as os from \"node:os\";\n\nconst HOME_DIR = os.homedir();\n\n// Patterns for sanitization (global variants for use with .replace())\nconst UUID_PATTERN = /\\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\\b/gi;\nconst LONG_HEX_PATTERN = /\\b[0-9a-fA-F]{32,}\\b/g;\nconst EMAIL_PATTERN = /\\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}\\b/g;\nconst ABSOLUTE_PATH_PATTERN = /(?:\\/(?:[\\w.@\\- ]+\\/)+[\\w.@\\- ]+)/g;\nconst WINDOWS_PATH_PATTERN = /(?:[A-Za-z]:\\\\(?:[\\w.@\\- ]+\\\\)+[\\w.@\\- ]+)/g;\nconst URL_QUERY_PATTERN = /[?&][^?\\s]*/g;\n\n// Non-global variants for single-match .test() calls (avoids lastIndex state issues)\nconst EMAIL_TEST_PATTERN = /\\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}\\b/;\nconst WINDOWS_DRIVE_TEST_PATTERN = /^[A-Za-z]:\\\\/;\n\n// SDK package path marker for relative paths\nconst SDK_PACKAGE_MARKER = \"packages/sdk/\";\n\nfunction lastSegment(filePath: string, separator: string): string {\n return filePath.split(separator).pop() ?? filePath;\n}\n\n/**\n * Sanitize a stack trace by replacing absolute paths with relative SDK paths.\n * External paths are replaced with `<external>/filename.ext`.\n * Home directories are replaced with `~/<redacted>/`.\n * @param stack - Raw stack trace string\n * @returns Sanitized stack trace\n */\nexport function sanitizeStackTrace(stack: string): string {\n // V8 stack traces start with \"ErrorType: message\\n at ...\".\n // The error message may span multiple lines before the first \" at \" frame.\n // Apply message sanitization to all message lines so secrets embedded in\n // multiline error messages are redacted consistently with errorMessage.\n const firstFrameIndex = stack.search(/\\n\\s+at /);\n let result: string;\n if (firstFrameIndex !== -1) {\n result = sanitizeMessage(stack.slice(0, firstFrameIndex)) + stack.slice(firstFrameIndex);\n } else {\n result = sanitizeMessage(stack);\n }\n\n result = result.replace(ABSOLUTE_PATH_PATTERN, (match) => {\n const sdkIndex = match.indexOf(SDK_PACKAGE_MARKER);\n if (sdkIndex !== -1) {\n return match.slice(sdkIndex);\n }\n\n if (match.startsWith(HOME_DIR)) {\n return `~/<redacted>/${lastSegment(match, \"/\")}`;\n }\n\n return `<external>/${lastSegment(match, \"/\")}`;\n });\n result = result.replace(WINDOWS_PATH_PATTERN, (match) => {\n const normalized = match.replace(/\\\\/g, \"/\");\n const sdkIndex = normalized.indexOf(SDK_PACKAGE_MARKER);\n if (sdkIndex !== -1) {\n return normalized.slice(sdkIndex);\n }\n return `<external>/${lastSegment(match, \"\\\\\")}`;\n });\n return result;\n}\n\n/**\n * Sanitize an error message by redacting sensitive information.\n * Redacts: UUIDs, long hex tokens, email addresses, absolute paths, URL query strings.\n * @param message - Raw error message\n * @returns Sanitized error message\n */\nexport function sanitizeMessage(message: string): string {\n let result = message;\n // Strip serialized request/response bodies that may contain secrets\n result = result.replace(/\\nRequest:\\s*[\\s\\S]*$/, \"\\nRequest: <redacted>\");\n result = result.replace(UUID_PATTERN, \"<uuid>\");\n result = result.replace(LONG_HEX_PATTERN, \"<redacted>\");\n result = result.replace(EMAIL_PATTERN, \"<email>\");\n result = result.replace(URL_QUERY_PATTERN, \"?<redacted>\");\n result = result.replace(ABSOLUTE_PATH_PATTERN, (match) => `<path>/${lastSegment(match, \"/\")}`);\n result = result.replace(WINDOWS_PATH_PATTERN, (match) => `<path>/${lastSegment(match, \"\\\\\")}`);\n\n return result;\n}\n\n/**\n * Sanitize process.argv by keeping command/subcommand names and redacting\n * values of sensitive flags.\n * @param argv - Raw process.argv array\n * @returns Sanitized argv array\n */\nexport function sanitizeArgv(argv: string[]): string[] {\n const result: string[] = [];\n let redactNext = false;\n\n for (const arg of argv) {\n if (redactNext) {\n // If the next token is itself a flag, treat it as a new flag rather\n // than consuming it as the previous flag's value. This avoids leaking\n // the *next* flag's value (e.g., `--verbose --workspace-id secret`\n // would otherwise expose `secret`).\n if (!arg.startsWith(\"-\")) {\n result.push(\"<redacted>\");\n redactNext = false;\n continue;\n }\n redactNext = false;\n }\n\n if (arg.startsWith(\"-\")) {\n // --flag=value: keep flag name, redact value\n const eqIndex = arg.indexOf(\"=\");\n if (eqIndex !== -1) {\n result.push(`${arg.slice(0, eqIndex)}=<redacted>`);\n continue;\n }\n\n // --flag / -f: keep flag name, redact next arg as its value\n result.push(arg);\n redactNext = true;\n continue;\n }\n\n // Redact absolute paths\n if (arg.startsWith(\"/\") && arg.includes(\"/\", 1)) {\n result.push(\"<path>\");\n continue;\n }\n\n // Redact Windows-style absolute paths\n if (WINDOWS_DRIVE_TEST_PATTERN.test(arg)) {\n result.push(\"<path>\");\n continue;\n }\n\n // Redact email addresses\n if (EMAIL_TEST_PATTERN.test(arg)) {\n result.push(\"<email>\");\n continue;\n }\n\n result.push(arg);\n }\n\n return result;\n}\n","import * as crypto from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport { parseYAML } from \"confbox\";\nimport * as path from \"pathe\";\nimport { xdgConfig } from \"xdg-basedir\";\nimport { sanitizeArgv, sanitizeMessage, sanitizeStackTrace } from \"./sanitize\";\n\nexport type ErrorType = \"uncaughtException\" | \"unhandledRejection\" | \"handledError\";\n\nexport interface CrashReport {\n id: string;\n timestamp: string;\n sdkVersion: string;\n nodeVersion: string;\n osPlatform: string;\n osRelease: string;\n arch: string;\n command: string;\n argv: string[];\n errorName: string;\n errorMessage: string;\n stackTrace: string;\n errorType: ErrorType;\n userId: string | null;\n userEmail: string | null;\n}\n\ninterface BuildCrashReportOptions {\n error: unknown;\n sdkVersion: string;\n errorType: ErrorType;\n}\n\n// Maximum subcommand depth to keep (e.g., \"tailordb migrate generate\" = 3 tokens).\n// Positional arguments beyond this are potentially sensitive user input.\n// Accepted trade-off: plain-text positional args that don't match known patterns\n// (UUIDs, hex tokens, emails, paths) pass through to `command` and `argv`.\n// Full redaction would require embedding the CLI command tree here, which is fragile.\nconst MAX_COMMAND_TOKENS = 3;\n\n/**\n * Parse the command name from process.argv.\n * Extracts up to MAX_COMMAND_TOKENS non-flag arguments after the script name.\n * @returns Parsed command string\n */\nfunction parseCommand(): string {\n const args = process.argv.slice(2);\n const commandParts: string[] = [];\n for (const arg of args) {\n if (arg.startsWith(\"-\") || commandParts.length >= MAX_COMMAND_TOKENS) break;\n commandParts.push(arg);\n }\n return commandParts.join(\" \") || \"<unknown>\";\n}\n\n/**\n * Build a CrashReport data structure from an error and context.\n * All sensitive data is sanitized before inclusion.\n * @param options - Error, SDK version, and crash type\n * @returns Sanitized crash report\n */\nexport function buildCrashReport(options: BuildCrashReportOptions): CrashReport {\n const { error, sdkVersion, errorType } = options;\n\n const isError = error instanceof Error;\n const rawMessage = isError ? error.message : String(error);\n const rawStack = isError && error.stack ? error.stack : \"\";\n const errorName = isError ? error.name : \"UnknownError\";\n\n const currentUser = readCurrentUser();\n\n return {\n id: crypto.randomUUID(),\n timestamp: new Date().toISOString(),\n sdkVersion,\n nodeVersion: process.version,\n osPlatform: process.platform,\n osRelease: os.release(),\n arch: process.arch,\n command: sanitizeMessage(parseCommand()),\n argv: sanitizeArgv(process.argv),\n errorName,\n errorMessage: sanitizeMessage(rawMessage),\n stackTrace: sanitizeStackTrace(rawStack),\n errorType,\n userId: currentUser,\n userEmail: currentUser,\n };\n}\n\n/**\n * Read current_user from Tailor Platform config without side effects.\n * Unlike readPlatformConfig(), this never triggers migration or logs warnings.\n * @returns The current user email, or null if unavailable\n */\nfunction readCurrentUser(): string | null {\n try {\n if (!xdgConfig) return null;\n const configPath = path.join(xdgConfig, \"tailor-platform\", \"config.yaml\");\n if (!fs.existsSync(configPath)) return null;\n const raw = parseYAML(fs.readFileSync(configPath, \"utf-8\")) as { current_user?: string | null };\n return raw?.current_user ?? null;\n } catch {\n return null;\n }\n}\n","import { logger } from \"@/cli/shared/logger\";\nimport { readPackageJson } from \"@/cli/shared/package-json\";\nimport { parseCrashReportConfig } from \"./config\";\nimport { buildCrashReport, type ErrorType } from \"./report\";\nimport { sendCrashReport } from \"./sender\";\nimport { writeCrashReport } from \"./writer\";\n\n/**\n * Report an unexpected crash. Writes a local crash log file and optionally\n * sends the report to a remote endpoint. Displays a user-facing message\n * with the crash log path and a command to submit the report.\n *\n * Never throws - all errors are silently caught.\n * @param error - The error that caused the crash\n * @param errorType - How the error was caught\n */\nexport async function reportCrash(error: unknown, errorType: ErrorType): Promise<void> {\n try {\n const config = parseCrashReportConfig();\n if (!config.localEnabled && !config.remoteEnabled) return;\n\n const packageJson = await readPackageJson();\n const sdkVersion = packageJson.version ?? \"unknown\";\n\n const report = buildCrashReport({ error, sdkVersion, errorType });\n\n if (config.localEnabled) {\n const filePath = writeCrashReport(report, config.localDir);\n if (filePath) {\n logger.log(\n [\n \"\",\n \"An unexpected error occurred. A crash report has been saved to:\",\n ` ${filePath}`,\n \"\",\n \"To submit this report:\",\n ` tailor-sdk crashreport send --file \"${filePath}\"`,\n ].join(\"\\n\"),\n );\n }\n }\n\n if (config.remoteEnabled) {\n // Lazy import: client.ts pulls in heavy dependencies (OAuth2, Connect, Protobuf)\n // that should not be loaded on the startup critical path via initCrashReporting().\n const { userAgent } = await import(\"@/cli/shared/client\");\n const ua = await userAgent();\n await sendCrashReport(report, ua);\n }\n } catch {\n // Never throw from crash reporting\n }\n}\n\n/**\n * Register global uncaughtException and unhandledRejection handlers.\n * These catch errors outside the normal cleanup flow (e.g., during\n * argument parsing). Should be called once at CLI startup before runMain.\n */\nexport function initCrashReporting(): void {\n const config = parseCrashReportConfig();\n if (!config.localEnabled && !config.remoteEnabled) return;\n\n const handleFatal = (error: unknown, errorType: ErrorType) => {\n const message = error instanceof Error ? error.message : String(error);\n logger.error(message);\n void reportCrash(error, errorType).finally(() => {\n process.exit(1);\n });\n };\n\n process.on(\"uncaughtException\", (error) => handleFatal(error, \"uncaughtException\"));\n process.on(\"unhandledRejection\", (reason) => handleFatal(reason, \"unhandledRejection\"));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"crashreport-DGdAgX8Y.mjs","names":[],"sources":["../src/cli/crashreport/config.ts","../src/cli/crashreport/writer.ts","../src/cli/crashreport/sender.ts","../src/cli/crashreport/sanitize.ts","../src/cli/crashreport/report.ts","../src/cli/crashreport/index.ts"],"sourcesContent":["import * as path from \"pathe\";\nimport { isCI } from \"std-env\";\nimport { xdgConfig } from \"xdg-basedir\";\n\nexport interface CrashReportConfig {\n readonly localEnabled: boolean;\n readonly remoteEnabled: boolean;\n readonly localDir: string;\n}\n\n/**\n * Parse crash report configuration from environment variables.\n * Local crash log writing is enabled by default (opt-out via TAILOR_CRASH_REPORTS_LOCAL=off).\n * Remote sending is disabled by default (opt-in via TAILOR_CRASH_REPORTS_REMOTE=on).\n * Both are auto-disabled in CI environments.\n * @returns Crash report configuration\n */\nexport function parseCrashReportConfig(): CrashReportConfig {\n if (isCI) {\n return {\n localEnabled: false,\n remoteEnabled: false,\n localDir: \"\",\n };\n }\n\n const localEnabled = (process.env.TAILOR_CRASH_REPORTS_LOCAL ?? \"on\").toLowerCase() !== \"off\";\n const remoteEnabled = (process.env.TAILOR_CRASH_REPORTS_REMOTE ?? \"off\").toLowerCase() === \"on\";\n const localDir = xdgConfig ? path.join(xdgConfig, \"tailor-platform\", \"crash-reports\") : \"\";\n\n return {\n localEnabled: localEnabled && localDir !== \"\",\n remoteEnabled,\n localDir,\n };\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"pathe\";\nimport { ensureSecretDir, writeSecretFile } from \"@/cli/shared/secret-file\";\nimport type { CrashReport } from \"./report\";\n\nconst MAX_CRASH_FILES = 10;\n\n/** Marker line that separates human-readable content from the JSON footer. */\nexport const JSON_FOOTER_MARKER = \"--- JSON ---\";\n\n/** File extension for crash log files. */\nexport const CRASH_LOG_EXTENSION = \".crash.log\";\n\n/**\n * Format a CrashReport as human-readable text for local crash log files.\n * @param report - Crash report to format\n * @returns Formatted text content\n */\nexport function formatCrashReport(report: CrashReport): string {\n const lines = [\n `Crash Report: ${report.id}`,\n `Timestamp: ${report.timestamp}`,\n `Error Type: ${report.errorType}`,\n \"\",\n \"--- Environment ---\",\n `SDK Version: ${report.sdkVersion}`,\n `Node Version: ${report.nodeVersion}`,\n `OS: ${report.osPlatform} ${report.osRelease}`,\n `Arch: ${report.arch}`,\n \"\",\n \"--- Command ---\",\n `Command: ${report.command}`,\n `Arguments: ${JSON.stringify(report.argv)}`,\n \"\",\n \"--- Error ---\",\n `Name: ${report.errorName}`,\n `Message: ${report.errorMessage}`,\n \"\",\n \"--- Stack Trace ---\",\n report.stackTrace || \"(no stack trace available)\",\n \"\",\n JSON_FOOTER_MARKER,\n JSON.stringify(report),\n \"\",\n ];\n return lines.join(\"\\n\");\n}\n\n/**\n * Generate a filename for a crash log file.\n * Format: {timestamp}-{shortId}.crash.log\n * @param report - Crash report to generate filename for\n * @returns Filename string\n */\nfunction generateFilename(report: CrashReport): string {\n const safeTimestamp = report.timestamp.replace(/[:.]/g, \"-\");\n const shortId = report.id.slice(0, 8);\n return `${safeTimestamp}-${shortId}${CRASH_LOG_EXTENSION}`;\n}\n\n/**\n * Remove old crash log files, keeping only the most recent ones.\n * @param dir - Crash log directory\n */\nfunction cleanupOldFiles(dir: string): void {\n try {\n const files = fs\n .readdirSync(dir)\n .filter((f) => f.endsWith(CRASH_LOG_EXTENSION))\n .sort()\n .reverse();\n\n for (const file of files.slice(MAX_CRASH_FILES)) {\n fs.unlinkSync(path.join(dir, file));\n }\n } catch {\n // Best-effort cleanup, ignore errors\n }\n}\n\n/**\n * Write a crash report to a local file.\n * Creates the directory if it doesn't exist. Keeps only the last 10 crash files.\n * Never throws - returns the file path on success or undefined on failure.\n * @param report - Crash report to write\n * @param dir - Directory to write the crash log file to\n * @returns File path on success, undefined on failure\n */\nexport function writeCrashReport(report: CrashReport, dir: string): string | undefined {\n try {\n ensureSecretDir(dir);\n\n const filename = generateFilename(report);\n const filePath = path.join(dir, filename);\n const content = formatCrashReport(report);\n\n writeSecretFile(filePath, content);\n cleanupOldFiles(dir);\n\n return filePath;\n } catch {\n return undefined;\n }\n}\n","import type { CrashReport } from \"./report\";\n\nconst SEND_TIMEOUT_MS = 5000;\nconst PRODUCTION_ENDPOINT = \"https://sdk-error-tracking-926vh9t4cl.erp.dev/query\";\n\nconst SUBMIT_MUTATION = `\nmutation SubmitCrashReport(\n $id: String!\n $timestamp: String!\n $sdkVersion: String!\n $nodeVersion: String!\n $osPlatform: String!\n $osRelease: String!\n $arch: String!\n $command: String!\n $argv: [String]\n $errorName: String!\n $errorMessage: String!\n $stackTrace: String\n $errorType: String!\n $userId: String\n $userEmail: String\n) {\n submitCrashReport(\n id: $id\n timestamp: $timestamp\n sdkVersion: $sdkVersion\n nodeVersion: $nodeVersion\n osPlatform: $osPlatform\n osRelease: $osRelease\n arch: $arch\n command: $command\n argv: $argv\n errorName: $errorName\n errorMessage: $errorMessage\n stackTrace: $stackTrace\n errorType: $errorType\n userId: $userId\n userEmail: $userEmail\n ) {\n success\n }\n}`;\n\n/**\n * Send a crash report to the remote endpoint via GraphQL mutation.\n * Best-effort: never throws, returns boolean success.\n * @param report - Crash report to send\n * @param ua - User-Agent header value\n * @returns true if the request succeeded, false otherwise\n */\nexport async function sendCrashReport(report: CrashReport, ua: string): Promise<boolean> {\n try {\n const endpoint = process.env.TAILOR_CRASH_REPORT_ENDPOINT || PRODUCTION_ENDPOINT;\n const response = await fetch(endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"User-Agent\": ua,\n },\n body: JSON.stringify({\n query: SUBMIT_MUTATION,\n variables: report,\n }),\n signal: AbortSignal.timeout(SEND_TIMEOUT_MS),\n });\n\n if (!response.ok) return false;\n\n const data = (await response.json()) as {\n errors?: unknown[];\n data?: { submitCrashReport: { success: boolean } };\n };\n if (data.errors?.length) return false;\n return data.data?.submitCrashReport.success === true;\n } catch {\n return false;\n }\n}\n","import * as os from \"node:os\";\n\nconst HOME_DIR = os.homedir();\n\n// Patterns for sanitization (global variants for use with .replace())\nconst UUID_PATTERN = /\\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\\b/gi;\nconst LONG_HEX_PATTERN = /\\b[0-9a-fA-F]{32,}\\b/g;\nconst EMAIL_PATTERN = /\\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}\\b/g;\nconst ABSOLUTE_PATH_PATTERN = /(?:\\/(?:[\\w.@\\- ]+\\/)+[\\w.@\\- ]+)/g;\nconst WINDOWS_PATH_PATTERN = /(?:[A-Za-z]:\\\\(?:[\\w.@\\- ]+\\\\)+[\\w.@\\- ]+)/g;\nconst URL_QUERY_PATTERN = /[?&][^?\\s]*/g;\n\n// Non-global variants for single-match .test() calls (avoids lastIndex state issues)\nconst EMAIL_TEST_PATTERN = /\\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}\\b/;\nconst WINDOWS_DRIVE_TEST_PATTERN = /^[A-Za-z]:\\\\/;\n\n// SDK package path marker for relative paths\nconst SDK_PACKAGE_MARKER = \"packages/sdk/\";\n\nfunction lastSegment(filePath: string, separator: string): string {\n return filePath.split(separator).pop() ?? filePath;\n}\n\n/**\n * Sanitize a stack trace by replacing absolute paths with relative SDK paths.\n * External paths are replaced with `<external>/filename.ext`.\n * Home directories are replaced with `~/<redacted>/`.\n * @param stack - Raw stack trace string\n * @returns Sanitized stack trace\n */\nexport function sanitizeStackTrace(stack: string): string {\n // V8 stack traces start with \"ErrorType: message\\n at ...\".\n // The error message may span multiple lines before the first \" at \" frame.\n // Apply message sanitization to all message lines so secrets embedded in\n // multiline error messages are redacted consistently with errorMessage.\n const firstFrameIndex = stack.search(/\\n\\s+at /);\n let result: string;\n if (firstFrameIndex !== -1) {\n result = sanitizeMessage(stack.slice(0, firstFrameIndex)) + stack.slice(firstFrameIndex);\n } else {\n result = sanitizeMessage(stack);\n }\n\n result = result.replace(ABSOLUTE_PATH_PATTERN, (match) => {\n const sdkIndex = match.indexOf(SDK_PACKAGE_MARKER);\n if (sdkIndex !== -1) {\n return match.slice(sdkIndex);\n }\n\n if (match.startsWith(HOME_DIR)) {\n return `~/<redacted>/${lastSegment(match, \"/\")}`;\n }\n\n return `<external>/${lastSegment(match, \"/\")}`;\n });\n result = result.replace(WINDOWS_PATH_PATTERN, (match) => {\n const normalized = match.replace(/\\\\/g, \"/\");\n const sdkIndex = normalized.indexOf(SDK_PACKAGE_MARKER);\n if (sdkIndex !== -1) {\n return normalized.slice(sdkIndex);\n }\n return `<external>/${lastSegment(match, \"\\\\\")}`;\n });\n return result;\n}\n\n/**\n * Sanitize an error message by redacting sensitive information.\n * Redacts: UUIDs, long hex tokens, email addresses, absolute paths, URL query strings.\n * @param message - Raw error message\n * @returns Sanitized error message\n */\nexport function sanitizeMessage(message: string): string {\n let result = message;\n // Strip serialized request/response bodies that may contain secrets\n result = result.replace(/\\nRequest:\\s*[\\s\\S]*$/, \"\\nRequest: <redacted>\");\n result = result.replace(UUID_PATTERN, \"<uuid>\");\n result = result.replace(LONG_HEX_PATTERN, \"<redacted>\");\n result = result.replace(EMAIL_PATTERN, \"<email>\");\n result = result.replace(URL_QUERY_PATTERN, \"?<redacted>\");\n result = result.replace(ABSOLUTE_PATH_PATTERN, (match) => `<path>/${lastSegment(match, \"/\")}`);\n result = result.replace(WINDOWS_PATH_PATTERN, (match) => `<path>/${lastSegment(match, \"\\\\\")}`);\n\n return result;\n}\n\n/**\n * Sanitize process.argv by keeping command/subcommand names and redacting\n * values of sensitive flags.\n * @param argv - Raw process.argv array\n * @returns Sanitized argv array\n */\nexport function sanitizeArgv(argv: string[]): string[] {\n const result: string[] = [];\n let redactNext = false;\n\n for (const arg of argv) {\n if (redactNext) {\n // If the next token is itself a flag, treat it as a new flag rather\n // than consuming it as the previous flag's value. This avoids leaking\n // the *next* flag's value (e.g., `--verbose --workspace-id secret`\n // would otherwise expose `secret`).\n if (!arg.startsWith(\"-\")) {\n result.push(\"<redacted>\");\n redactNext = false;\n continue;\n }\n redactNext = false;\n }\n\n if (arg.startsWith(\"-\")) {\n // --flag=value: keep flag name, redact value\n const eqIndex = arg.indexOf(\"=\");\n if (eqIndex !== -1) {\n result.push(`${arg.slice(0, eqIndex)}=<redacted>`);\n continue;\n }\n\n // --flag / -f: keep flag name, redact next arg as its value\n result.push(arg);\n redactNext = true;\n continue;\n }\n\n // Redact absolute paths\n if (arg.startsWith(\"/\") && arg.includes(\"/\", 1)) {\n result.push(\"<path>\");\n continue;\n }\n\n // Redact Windows-style absolute paths\n if (WINDOWS_DRIVE_TEST_PATTERN.test(arg)) {\n result.push(\"<path>\");\n continue;\n }\n\n // Redact email addresses\n if (EMAIL_TEST_PATTERN.test(arg)) {\n result.push(\"<email>\");\n continue;\n }\n\n result.push(arg);\n }\n\n return result;\n}\n","import * as crypto from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport { parseYAML } from \"confbox\";\nimport * as path from \"pathe\";\nimport { xdgConfig } from \"xdg-basedir\";\nimport { sanitizeArgv, sanitizeMessage, sanitizeStackTrace } from \"./sanitize\";\n\nexport type ErrorType = \"uncaughtException\" | \"unhandledRejection\" | \"handledError\";\n\nexport interface CrashReport {\n id: string;\n timestamp: string;\n sdkVersion: string;\n nodeVersion: string;\n osPlatform: string;\n osRelease: string;\n arch: string;\n command: string;\n argv: string[];\n errorName: string;\n errorMessage: string;\n stackTrace: string;\n errorType: ErrorType;\n userId: string | null;\n userEmail: string | null;\n}\n\ninterface BuildCrashReportOptions {\n error: unknown;\n sdkVersion: string;\n errorType: ErrorType;\n}\n\n// Maximum subcommand depth to keep (e.g., \"tailordb migrate generate\" = 3 tokens).\n// Positional arguments beyond this are potentially sensitive user input.\n// Accepted trade-off: plain-text positional args that don't match known patterns\n// (UUIDs, hex tokens, emails, paths) pass through to `command` and `argv`.\n// Full redaction would require embedding the CLI command tree here, which is fragile.\nconst MAX_COMMAND_TOKENS = 3;\n\n/**\n * Parse the command name from process.argv.\n * Extracts up to MAX_COMMAND_TOKENS non-flag arguments after the script name.\n * @returns Parsed command string\n */\nfunction parseCommand(): string {\n const args = process.argv.slice(2);\n const commandParts: string[] = [];\n for (const arg of args) {\n if (arg.startsWith(\"-\") || commandParts.length >= MAX_COMMAND_TOKENS) break;\n commandParts.push(arg);\n }\n return commandParts.join(\" \") || \"<unknown>\";\n}\n\n/**\n * Build a CrashReport data structure from an error and context.\n * All sensitive data is sanitized before inclusion.\n * @param options - Error, SDK version, and crash type\n * @returns Sanitized crash report\n */\nexport function buildCrashReport(options: BuildCrashReportOptions): CrashReport {\n const { error, sdkVersion, errorType } = options;\n\n const isError = error instanceof Error;\n const rawMessage = isError ? error.message : String(error);\n const rawStack = isError && error.stack ? error.stack : \"\";\n const errorName = isError ? error.name : \"UnknownError\";\n\n const currentUser = readCurrentUser();\n\n return {\n id: crypto.randomUUID(),\n timestamp: new Date().toISOString(),\n sdkVersion,\n nodeVersion: process.version,\n osPlatform: process.platform,\n osRelease: os.release(),\n arch: process.arch,\n command: sanitizeMessage(parseCommand()),\n argv: sanitizeArgv(process.argv),\n errorName,\n errorMessage: sanitizeMessage(rawMessage),\n stackTrace: sanitizeStackTrace(rawStack),\n errorType,\n userId: currentUser,\n userEmail: currentUser,\n };\n}\n\n/**\n * Read current_user from Tailor Platform config without side effects.\n * Unlike readPlatformConfig(), this never triggers migration or logs warnings.\n * @returns The current user email, or null if unavailable\n */\nfunction readCurrentUser(): string | null {\n try {\n if (!xdgConfig) return null;\n const configPath = path.join(xdgConfig, \"tailor-platform\", \"config.yaml\");\n if (!fs.existsSync(configPath)) return null;\n const raw = parseYAML(fs.readFileSync(configPath, \"utf-8\")) as { current_user?: string | null };\n return raw?.current_user ?? null;\n } catch {\n return null;\n }\n}\n","import { logger } from \"@/cli/shared/logger\";\nimport { readPackageJson } from \"@/cli/shared/package-json\";\nimport { parseCrashReportConfig } from \"./config\";\nimport { buildCrashReport, type ErrorType } from \"./report\";\nimport { sendCrashReport } from \"./sender\";\nimport { writeCrashReport } from \"./writer\";\n\n/**\n * Report an unexpected crash. Writes a local crash log file and optionally\n * sends the report to a remote endpoint. Displays a user-facing message\n * with the crash log path and a command to submit the report.\n *\n * Never throws - all errors are silently caught.\n * @param error - The error that caused the crash\n * @param errorType - How the error was caught\n */\nexport async function reportCrash(error: unknown, errorType: ErrorType): Promise<void> {\n try {\n const config = parseCrashReportConfig();\n if (!config.localEnabled && !config.remoteEnabled) return;\n\n const packageJson = await readPackageJson();\n const sdkVersion = packageJson.version ?? \"unknown\";\n\n const report = buildCrashReport({ error, sdkVersion, errorType });\n\n if (config.localEnabled) {\n const filePath = writeCrashReport(report, config.localDir);\n if (filePath) {\n logger.log(\n [\n \"\",\n \"An unexpected error occurred. A crash report has been saved to:\",\n ` ${filePath}`,\n \"\",\n \"To submit this report:\",\n ` tailor-sdk crashreport send --file \"${filePath}\"`,\n ].join(\"\\n\"),\n );\n }\n }\n\n if (config.remoteEnabled) {\n // Lazy import: client.ts pulls in heavy dependencies (OAuth2, Connect, Protobuf)\n // that should not be loaded on the startup critical path via initCrashReporting().\n const { userAgent } = await import(\"@/cli/shared/client\");\n const ua = await userAgent();\n await sendCrashReport(report, ua);\n }\n } catch {\n // Never throw from crash reporting\n }\n}\n\n/**\n * Register global uncaughtException and unhandledRejection handlers.\n * These catch errors outside the normal cleanup flow (e.g., during\n * argument parsing). Should be called once at CLI startup before runMain.\n */\nexport function initCrashReporting(): void {\n const config = parseCrashReportConfig();\n if (!config.localEnabled && !config.remoteEnabled) return;\n\n const handleFatal = (error: unknown, errorType: ErrorType) => {\n const message = error instanceof Error ? error.message : String(error);\n logger.error(message);\n void reportCrash(error, errorType).finally(() => {\n process.exit(1);\n });\n };\n\n process.on(\"uncaughtException\", (error) => handleFatal(error, \"uncaughtException\"));\n process.on(\"unhandledRejection\", (reason) => handleFatal(reason, \"unhandledRejection\"));\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
1
 
2
- import { n as reportCrash, t as initCrashReporting } from "./crashreport-DJ5SS5Q-.mjs";
2
+ import { n as reportCrash, t as initCrashReporting } from "./crashreport-DGdAgX8Y.mjs";
3
3
 
4
4
  export { reportCrash };
@@ -7,9 +7,11 @@ var file_exports = /* @__PURE__ */ __exportAll({
7
7
  deleteFile: () => deleteFile,
8
8
  download: () => download,
9
9
  downloadAsBase64: () => downloadAsBase64,
10
+ downloadStream: () => downloadStream,
10
11
  getMetadata: () => getMetadata,
11
12
  openDownloadStream: () => openDownloadStream,
12
- upload: () => upload
13
+ upload: () => upload,
14
+ uploadStream: () => uploadStream
13
15
  });
14
16
  const api = () => globalThis.tailordb.file;
15
17
  /**
@@ -44,11 +46,24 @@ const deleteFile = (...args) => api().delete(...args);
44
46
  const getMetadata = (...args) => api().getMetadata(...args);
45
47
  /**
46
48
  * See {@link TailorDBFileAPI.openDownloadStream}.
49
+ * @deprecated Use {@link downloadStream} instead.
47
50
  * @param args - Forwarded to {@link TailorDBFileAPI.openDownloadStream}
48
51
  * @returns Async iterator yielding file chunks; call `close()` to release resources
49
52
  */
50
53
  const openDownloadStream = (...args) => api().openDownloadStream(...args);
54
+ /**
55
+ * See {@link TailorDBFileAPI.downloadStream}.
56
+ * @param args - Forwarded to {@link TailorDBFileAPI.downloadStream}
57
+ * @returns ReadableStream body and metadata for the file
58
+ */
59
+ const downloadStream = (...args) => api().downloadStream(...args);
60
+ /**
61
+ * See {@link TailorDBFileAPI.uploadStream}.
62
+ * @param args - Forwarded to {@link TailorDBFileAPI.uploadStream}
63
+ * @returns Upload response containing the file metadata
64
+ */
65
+ const uploadStream = (...args) => api().uploadStream(...args);
51
66
 
52
67
  //#endregion
53
- export { getMetadata as a, file_exports as i, download as n, openDownloadStream as o, downloadAsBase64 as r, upload as s, deleteFile as t };
54
- //# sourceMappingURL=file-umdycPAF.mjs.map
68
+ export { file_exports as a, upload as c, downloadStream as i, uploadStream as l, download as n, getMetadata as o, downloadAsBase64 as r, openDownloadStream as s, deleteFile as t };
69
+ //# sourceMappingURL=file-B58Dm-2P.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-B58Dm-2P.mjs","names":[],"sources":["../src/runtime/file.ts"],"sourcesContent":["/**\n * TailorDB file (BLOB) utilities.\n *\n * Thin typed wrapper around the platform-provided `tailordb.file` runtime API.\n * At runtime this delegates to `globalThis.tailordb.file`. Use `fileMock` from\n * `@tailor-platform/sdk/vitest` to mock these calls in unit tests.\n * @example\n * import { file } from \"@tailor-platform/sdk/runtime\";\n *\n * const { metadata } = await file.upload(\n * \"my-namespace\",\n * \"Document\",\n * \"attachment\",\n * recordId,\n * bytes,\n * );\n */\n\n/** Upload response metadata. */\nexport interface UploadMetadata {\n fileSize: number;\n sha256sum: string;\n}\n\n/** Download response metadata. */\nexport interface DownloadMetadata {\n contentType: string;\n fileSize: number;\n sha256sum: string;\n lastUploadedAt: string;\n}\n\n/** File metadata (for {@link getMetadata}). */\nexport interface FileMetadata {\n contentType: string;\n fileSize: number;\n sha256sum: string;\n urlPath: string;\n lastUploadedAt?: string;\n}\n\n/** Stream metadata (first chunk emitted by {@link openDownloadStream}). */\nexport interface StreamMetadata {\n contentType: string;\n fileSize: number;\n sha256sum: string;\n}\n\n/** Upload options. */\nexport interface FileUploadOptions {\n contentType?: string;\n}\n\n/** Upload stream options. */\nexport interface FileUploadStreamOptions {\n contentType?: string;\n fileSize?: number;\n}\n\n/** Upload response. */\nexport interface FileUploadResponse {\n metadata: UploadMetadata;\n}\n\n/** Download response. */\nexport interface FileDownloadResponse {\n data: Uint8Array;\n metadata: DownloadMetadata;\n}\n\n/** Download-as-Base64 response. */\nexport interface FileDownloadAsBase64Response {\n data: string;\n metadata: DownloadMetadata;\n}\n\n/** Download stream response. */\nexport interface FileDownloadStreamResponse {\n body: ReadableStream<Uint8Array>;\n metadata: DownloadMetadata;\n}\n\n/** Stream chunk types emitted by {@link FileStreamIterator}. */\nexport type StreamValue =\n | { type: \"metadata\"; metadata: StreamMetadata }\n | { type: \"chunk\"; data: Uint8Array; position: number }\n | { type: \"complete\" };\n\n/** Stream iterator returned by {@link openDownloadStream}. */\nexport interface FileStreamIterator extends AsyncIterableIterator<StreamValue> {\n next(): Promise<IteratorResult<StreamValue>>;\n close(): Promise<void>;\n}\n\n/** Error code emitted by {@link TailorDBFileError}. */\nexport type TailorDBFileErrorCode =\n | \"INVALID_PARAMS\"\n | \"INVALID_DATA_TYPE\"\n | \"OPERATION_FAILED\"\n | \"DELETE_FAILED\"\n | \"STREAM_OPEN_FAILED\"\n | \"STREAM_READ_ERROR\"\n | \"STREAM_ERROR\"\n | \"FILE_TOO_LARGE\";\n\n/**\n * Type-only shape of the `TailorDBFileError` runtime class. The class itself\n * is provided by the platform runtime (and by `injectMocks` in tests); this\n * interface mirrors it so callers can `import type { TailorDBFileError }` from\n * the wrapper module without depending on any ambient declaration.\n */\nexport interface TailorDBFileError extends Error {\n name: \"TailorDBFileError\";\n code?: TailorDBFileErrorCode;\n cause?: unknown;\n}\n\n/**\n * Platform API surface for `tailordb.file`. Describes the shape the platform\n * runtime injects on `globalThis.tailordb.file`.\n *\n * Each method below is also re-exported as a top-level named export from this\n * module (e.g. `upload`, `download`, `deleteFile`) so callers can either\n * `import * as file from \"@tailor-platform/sdk/runtime/file\"` or pick\n * individual methods.\n */\nexport interface TailorDBFileAPI {\n /**\n * Upload a file to TailorDB.\n * @param namespace - TailorDB namespace\n * @param typeName - TailorDB type name\n * @param fieldName - File field name on the type\n * @param recordId - Record ID owning the field\n * @param data - File contents\n * @param options - Upload options (e.g. `contentType`)\n * @returns Upload response containing the file metadata\n */\n upload(\n namespace: string,\n typeName: string,\n fieldName: string,\n recordId: string,\n data: string | ArrayBuffer | Uint8Array | number[],\n options?: FileUploadOptions,\n ): Promise<FileUploadResponse>;\n\n /**\n * Download a file from TailorDB.\n *\n * Throws `TailorDBFileError` with code `FILE_TOO_LARGE` when the file\n * exceeds 10MB — use {@link downloadStream} for large files.\n * @param namespace - TailorDB namespace\n * @param typeName - TailorDB type name\n * @param fieldName - File field name on the type\n * @param recordId - Record ID owning the field\n * @returns Bytes and metadata for the file\n */\n download(\n namespace: string,\n typeName: string,\n fieldName: string,\n recordId: string,\n ): Promise<FileDownloadResponse>;\n\n /**\n * Download a file from TailorDB as a Base64-encoded string.\n *\n * Throws `TailorDBFileError` with code `FILE_TOO_LARGE` when the file\n * exceeds 10MB — use {@link downloadStream} for large files.\n * @param namespace - TailorDB namespace\n * @param typeName - TailorDB type name\n * @param fieldName - File field name on the type\n * @param recordId - Record ID owning the field\n * @returns Base64-encoded contents and metadata for the file\n */\n downloadAsBase64(\n namespace: string,\n typeName: string,\n fieldName: string,\n recordId: string,\n ): Promise<FileDownloadAsBase64Response>;\n\n /**\n * Delete a file from TailorDB. Exported as `deleteFile` (and aliased as\n * `delete`) so it can be used both with named and namespace imports.\n * @param namespace - TailorDB namespace\n * @param typeName - TailorDB type name\n * @param fieldName - File field name on the type\n * @param recordId - Record ID owning the field\n * @returns Resolves once the file has been deleted\n */\n delete(namespace: string, typeName: string, fieldName: string, recordId: string): Promise<void>;\n\n /**\n * Get file metadata from TailorDB.\n * @param namespace - TailorDB namespace\n * @param typeName - TailorDB type name\n * @param fieldName - File field name on the type\n * @param recordId - Record ID owning the field\n * @returns Metadata for the stored file\n */\n getMetadata(\n namespace: string,\n typeName: string,\n fieldName: string,\n recordId: string,\n ): Promise<FileMetadata>;\n\n /**\n * Open a download stream for large files.\n * @deprecated Use {@link downloadStream} instead.\n * @param namespace - TailorDB namespace\n * @param typeName - TailorDB type name\n * @param fieldName - File field name on the type\n * @param recordId - Record ID owning the field\n * @returns Async iterator yielding file chunks; call `close()` to release resources\n */\n openDownloadStream(\n namespace: string,\n typeName: string,\n fieldName: string,\n recordId: string,\n ): Promise<FileStreamIterator>;\n\n /**\n * Download a file as a ReadableStream.\n * @param namespace - TailorDB namespace\n * @param typeName - TailorDB type name\n * @param fieldName - File field name on the type\n * @param recordId - Record ID owning the field\n * @returns ReadableStream body and metadata for the file\n */\n downloadStream(\n namespace: string,\n typeName: string,\n fieldName: string,\n recordId: string,\n ): Promise<FileDownloadStreamResponse>;\n\n /**\n * Upload a file using a ReadableStream.\n * @param namespace - TailorDB namespace\n * @param typeName - TailorDB type name\n * @param fieldName - File field name on the type\n * @param recordId - Record ID owning the field\n * @param readableStream - ReadableStream providing the file data\n * @param options - Upload stream options (e.g. `contentType`, `fileSize`)\n * @returns Upload response containing the file metadata\n */\n uploadStream(\n namespace: string,\n typeName: string,\n fieldName: string,\n recordId: string,\n readableStream: ReadableStream<Uint8Array | ArrayBuffer>,\n options?: FileUploadStreamOptions,\n ): Promise<FileUploadResponse>;\n}\n\nconst api = (): TailorDBFileAPI =>\n (globalThis as { tailordb: { file: TailorDBFileAPI } }).tailordb.file;\n\n/**\n * See {@link TailorDBFileAPI.upload}.\n * @param args - Forwarded to {@link TailorDBFileAPI.upload}\n * @returns Upload response containing the file metadata\n */\nexport const upload: TailorDBFileAPI[\"upload\"] = (...args) => api().upload(...args);\n\n/**\n * See {@link TailorDBFileAPI.download}.\n * @param args - Forwarded to {@link TailorDBFileAPI.download}\n * @returns Bytes and metadata for the file\n */\nexport const download: TailorDBFileAPI[\"download\"] = (...args) => api().download(...args);\n\n/**\n * See {@link TailorDBFileAPI.downloadAsBase64}.\n * @param args - Forwarded to {@link TailorDBFileAPI.downloadAsBase64}\n * @returns Base64-encoded contents and metadata for the file\n */\nexport const downloadAsBase64: TailorDBFileAPI[\"downloadAsBase64\"] = (...args) =>\n api().downloadAsBase64(...args);\n\n/**\n * See {@link TailorDBFileAPI.delete}.\n * @param args - Forwarded to {@link TailorDBFileAPI.delete}\n * @returns Resolves once the file has been deleted\n */\nexport const deleteFile: TailorDBFileAPI[\"delete\"] = (...args) => api().delete(...args);\n\n/**\n * See {@link TailorDBFileAPI.getMetadata}.\n * @param args - Forwarded to {@link TailorDBFileAPI.getMetadata}\n * @returns Metadata for the stored file\n */\nexport const getMetadata: TailorDBFileAPI[\"getMetadata\"] = (...args) => api().getMetadata(...args);\n\n/**\n * See {@link TailorDBFileAPI.openDownloadStream}.\n * @deprecated Use {@link downloadStream} instead.\n * @param args - Forwarded to {@link TailorDBFileAPI.openDownloadStream}\n * @returns Async iterator yielding file chunks; call `close()` to release resources\n */\nexport const openDownloadStream: TailorDBFileAPI[\"openDownloadStream\"] = (...args) =>\n api().openDownloadStream(...args);\n\n/**\n * See {@link TailorDBFileAPI.downloadStream}.\n * @param args - Forwarded to {@link TailorDBFileAPI.downloadStream}\n * @returns ReadableStream body and metadata for the file\n */\nexport const downloadStream: TailorDBFileAPI[\"downloadStream\"] = (...args) =>\n api().downloadStream(...args);\n\n/**\n * See {@link TailorDBFileAPI.uploadStream}.\n * @param args - Forwarded to {@link TailorDBFileAPI.uploadStream}\n * @returns Upload response containing the file metadata\n */\nexport const uploadStream: TailorDBFileAPI[\"uploadStream\"] = (...args) =>\n api().uploadStream(...args);\n\nexport { deleteFile as delete };\n"],"mappings":";;;;;;;;;;;;;;;AAmQA,MAAM,YACH,WAAuD,SAAS;;;;;;AAOnE,MAAa,UAAqC,GAAG,SAAS,IAAI,EAAE,OAAO,GAAG,IAAI;;;;;;AAOlF,MAAa,YAAyC,GAAG,SAAS,IAAI,EAAE,SAAS,GAAG,IAAI;;;;;;AAOxF,MAAa,oBAAyD,GAAG,SACvE,IAAI,EAAE,iBAAiB,GAAG,IAAI;;;;;;AAOhC,MAAa,cAAyC,GAAG,SAAS,IAAI,EAAE,OAAO,GAAG,IAAI;;;;;;AAOtF,MAAa,eAA+C,GAAG,SAAS,IAAI,EAAE,YAAY,GAAG,IAAI;;;;;;;AAQjG,MAAa,sBAA6D,GAAG,SAC3E,IAAI,EAAE,mBAAmB,GAAG,IAAI;;;;;;AAOlC,MAAa,kBAAqD,GAAG,SACnE,IAAI,EAAE,eAAe,GAAG,IAAI;;;;;;AAO9B,MAAa,gBAAiD,GAAG,SAC/D,IAAI,EAAE,aAAa,GAAG,IAAI"}
@@ -1,5 +1,5 @@
1
1
  declare namespace file_d_exports {
2
- export { DownloadMetadata, FileDownloadAsBase64Response, FileDownloadResponse, FileMetadata, FileStreamIterator, FileUploadOptions, FileUploadResponse, StreamMetadata, StreamValue, TailorDBFileAPI, TailorDBFileError, TailorDBFileErrorCode, UploadMetadata, deleteFile as delete, deleteFile, download, downloadAsBase64, getMetadata, openDownloadStream, upload };
2
+ export { DownloadMetadata, FileDownloadAsBase64Response, FileDownloadResponse, FileDownloadStreamResponse, FileMetadata, FileStreamIterator, FileUploadOptions, FileUploadResponse, FileUploadStreamOptions, StreamMetadata, StreamValue, TailorDBFileAPI, TailorDBFileError, TailorDBFileErrorCode, UploadMetadata, deleteFile as delete, deleteFile, download, downloadAsBase64, downloadStream, getMetadata, openDownloadStream, upload, uploadStream };
3
3
  }
4
4
  /**
5
5
  * TailorDB file (BLOB) utilities.
@@ -48,6 +48,11 @@ interface StreamMetadata {
48
48
  interface FileUploadOptions {
49
49
  contentType?: string;
50
50
  }
51
+ /** Upload stream options. */
52
+ interface FileUploadStreamOptions {
53
+ contentType?: string;
54
+ fileSize?: number;
55
+ }
51
56
  /** Upload response. */
52
57
  interface FileUploadResponse {
53
58
  metadata: UploadMetadata;
@@ -62,6 +67,11 @@ interface FileDownloadAsBase64Response {
62
67
  data: string;
63
68
  metadata: DownloadMetadata;
64
69
  }
70
+ /** Download stream response. */
71
+ interface FileDownloadStreamResponse {
72
+ body: ReadableStream<Uint8Array>;
73
+ metadata: DownloadMetadata;
74
+ }
65
75
  /** Stream chunk types emitted by {@link FileStreamIterator}. */
66
76
  type StreamValue = {
67
77
  type: "metadata";
@@ -116,7 +126,7 @@ interface TailorDBFileAPI {
116
126
  * Download a file from TailorDB.
117
127
  *
118
128
  * Throws `TailorDBFileError` with code `FILE_TOO_LARGE` when the file
119
- * exceeds 10MB — use {@link openDownloadStream} for large files.
129
+ * exceeds 10MB — use {@link downloadStream} for large files.
120
130
  * @param namespace - TailorDB namespace
121
131
  * @param typeName - TailorDB type name
122
132
  * @param fieldName - File field name on the type
@@ -128,7 +138,7 @@ interface TailorDBFileAPI {
128
138
  * Download a file from TailorDB as a Base64-encoded string.
129
139
  *
130
140
  * Throws `TailorDBFileError` with code `FILE_TOO_LARGE` when the file
131
- * exceeds 10MB — use {@link openDownloadStream} for large files.
141
+ * exceeds 10MB — use {@link downloadStream} for large files.
132
142
  * @param namespace - TailorDB namespace
133
143
  * @param typeName - TailorDB type name
134
144
  * @param fieldName - File field name on the type
@@ -157,6 +167,7 @@ interface TailorDBFileAPI {
157
167
  getMetadata(namespace: string, typeName: string, fieldName: string, recordId: string): Promise<FileMetadata>;
158
168
  /**
159
169
  * Open a download stream for large files.
170
+ * @deprecated Use {@link downloadStream} instead.
160
171
  * @param namespace - TailorDB namespace
161
172
  * @param typeName - TailorDB type name
162
173
  * @param fieldName - File field name on the type
@@ -164,6 +175,26 @@ interface TailorDBFileAPI {
164
175
  * @returns Async iterator yielding file chunks; call `close()` to release resources
165
176
  */
166
177
  openDownloadStream(namespace: string, typeName: string, fieldName: string, recordId: string): Promise<FileStreamIterator>;
178
+ /**
179
+ * Download a file as a ReadableStream.
180
+ * @param namespace - TailorDB namespace
181
+ * @param typeName - TailorDB type name
182
+ * @param fieldName - File field name on the type
183
+ * @param recordId - Record ID owning the field
184
+ * @returns ReadableStream body and metadata for the file
185
+ */
186
+ downloadStream(namespace: string, typeName: string, fieldName: string, recordId: string): Promise<FileDownloadStreamResponse>;
187
+ /**
188
+ * Upload a file using a ReadableStream.
189
+ * @param namespace - TailorDB namespace
190
+ * @param typeName - TailorDB type name
191
+ * @param fieldName - File field name on the type
192
+ * @param recordId - Record ID owning the field
193
+ * @param readableStream - ReadableStream providing the file data
194
+ * @param options - Upload stream options (e.g. `contentType`, `fileSize`)
195
+ * @returns Upload response containing the file metadata
196
+ */
197
+ uploadStream(namespace: string, typeName: string, fieldName: string, recordId: string, readableStream: ReadableStream<Uint8Array | ArrayBuffer>, options?: FileUploadStreamOptions): Promise<FileUploadResponse>;
167
198
  }
168
199
  /**
169
200
  * See {@link TailorDBFileAPI.upload}.
@@ -197,10 +228,23 @@ declare const deleteFile: TailorDBFileAPI["delete"];
197
228
  declare const getMetadata: TailorDBFileAPI["getMetadata"];
198
229
  /**
199
230
  * See {@link TailorDBFileAPI.openDownloadStream}.
231
+ * @deprecated Use {@link downloadStream} instead.
200
232
  * @param args - Forwarded to {@link TailorDBFileAPI.openDownloadStream}
201
233
  * @returns Async iterator yielding file chunks; call `close()` to release resources
202
234
  */
203
235
  declare const openDownloadStream: TailorDBFileAPI["openDownloadStream"];
236
+ /**
237
+ * See {@link TailorDBFileAPI.downloadStream}.
238
+ * @param args - Forwarded to {@link TailorDBFileAPI.downloadStream}
239
+ * @returns ReadableStream body and metadata for the file
240
+ */
241
+ declare const downloadStream: TailorDBFileAPI["downloadStream"];
242
+ /**
243
+ * See {@link TailorDBFileAPI.uploadStream}.
244
+ * @param args - Forwarded to {@link TailorDBFileAPI.uploadStream}
245
+ * @returns Upload response containing the file metadata
246
+ */
247
+ declare const uploadStream: TailorDBFileAPI["uploadStream"];
204
248
  //#endregion
205
- export { file_d_exports as _, FileStreamIterator as a, upload as b, StreamMetadata as c, TailorDBFileError as d, TailorDBFileErrorCode as f, downloadAsBase64 as g, download as h, FileMetadata as i, StreamValue as l, deleteFile as m, FileDownloadAsBase64Response as n, FileUploadOptions as o, UploadMetadata as p, FileDownloadResponse as r, FileUploadResponse as s, DownloadMetadata as t, TailorDBFileAPI as u, getMetadata as v, openDownloadStream as y };
206
- //# sourceMappingURL=file-DaCYOLPz.d.mts.map
249
+ export { upload as C, openDownloadStream as S, download as _, FileMetadata as a, file_d_exports as b, FileUploadResponse as c, StreamValue as d, TailorDBFileAPI as f, deleteFile as g, UploadMetadata as h, FileDownloadStreamResponse as i, FileUploadStreamOptions as l, TailorDBFileErrorCode as m, FileDownloadAsBase64Response as n, FileStreamIterator as o, TailorDBFileError as p, FileDownloadResponse as r, FileUploadOptions as s, DownloadMetadata as t, StreamMetadata as u, downloadAsBase64 as v, uploadStream as w, getMetadata as x, downloadStream as y };
250
+ //# sourceMappingURL=file-VTJbbOL3.d.mts.map
@@ -1,6 +1,6 @@
1
1
  import { t as TailorAuthconnectionAPI } from "./authconnection-TsdLYaLs.mjs";
2
2
  import { r as TailorContextAPI } from "./context-CUBwSBq4.mjs";
3
- import { u as TailorDBFileAPI } from "./file-DaCYOLPz.mjs";
3
+ import { f as TailorDBFileAPI } from "./file-VTJbbOL3.mjs";
4
4
  import { i as TailorIconvAPI } from "./iconv-Chu6Hit2.mjs";
5
5
  import { l as TailorIdpAPI } from "./idp-CbxR6A_0.mjs";
6
6
  import { t as TailorSecretmanagerAPI } from "./secretmanager-BhpDmxwT.mjs";
@@ -43,4 +43,4 @@ interface TailordbRuntime {
43
43
  }
44
44
  //#endregion
45
45
  export { TailordbQueryResult as a, TailordbCommandType as i, TailordbClientConstructor as n, TailordbRuntime as o, TailordbClientInstance as r, TailorRuntime as t };
46
- //# sourceMappingURL=index-wuuZLoFt.d.mts.map
46
+ //# sourceMappingURL=index-ECerapTN.d.mts.map
@@ -646,7 +646,12 @@ const FILE_DEFAULTS = {
646
646
  fileSize: 0,
647
647
  sha256sum: "",
648
648
  urlPath: ""
649
- }
649
+ },
650
+ downloadStream: null,
651
+ uploadStream: { metadata: {
652
+ fileSize: 0,
653
+ sha256sum: ""
654
+ } }
650
655
  };
651
656
  function resolveFileCall(method, namespace, typeName, fieldName, recordId) {
652
657
  const state = getState();
@@ -682,6 +687,24 @@ const mockTailordbFile = {
682
687
  },
683
688
  async openDownloadStream(namespace, typeName, fieldName, recordId) {
684
689
  return toFileStream(resolveFileCall("openDownloadStream", namespace, typeName, fieldName, recordId));
690
+ },
691
+ async downloadStream(namespace, typeName, fieldName, recordId) {
692
+ const resolved = resolveFileCall("downloadStream", namespace, typeName, fieldName, recordId);
693
+ if (resolved != null) return resolved;
694
+ return {
695
+ body: new ReadableStream({ start(c) {
696
+ c.close();
697
+ } }),
698
+ metadata: {
699
+ contentType: "",
700
+ fileSize: 0,
701
+ sha256sum: "",
702
+ lastUploadedAt: ""
703
+ }
704
+ };
705
+ },
706
+ async uploadStream(namespace, typeName, fieldName, recordId, _readableStream, _options) {
707
+ return resolveFileCall("uploadStream", namespace, typeName, fieldName, recordId);
685
708
  }
686
709
  };
687
710
  function toFileStream(value) {
@@ -818,4 +841,4 @@ function cleanupMocks(global) {
818
841
 
819
842
  //#endregion
820
843
  export { fileMock as a, injectMocks as c, workflowMock as d, cleanupMocks as i, secretmanagerMock as l, STATE_KEY as n, iconvMock as o, authconnectionMock as r, idpMock as s, RUNTIME_FLAG_KEY as t, tailordbMock as u };
821
- //# sourceMappingURL=mock-BabuSqbj.mjs.map
844
+ //# sourceMappingURL=mock-B6PI49C_.mjs.map