@sprigr/apps-app-sdk 0.27.0 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -493,9 +493,10 @@ declare function readFileRef(env: FileRefEnv, ref: FileRef | null | undefined, o
493
493
  * Apps that receive webhooks on their OWN per-install URL (e.g.
494
494
  * procore POSTs to `<procore-XXX.staging-apps.sprigr.com>/api/webhook/procore`)
495
495
  * don't go through the marketplace runtime dispatcher. Those apps
496
- * derive the callback URL from the inbound request's `Host` header
497
- * instead, which is naturally env-correct. This helper is only for
498
- * the marketplace-dispatcher pattern.
496
+ * build the callback URL from `publicBaseUrl(env)` below. Never from
497
+ * the inbound request's `Host` or `X-Forwarded-Host`: a visitor
498
+ * controls those, and a callback URL built from one hands an outside
499
+ * host to the provider (sprigr-apps#3453).
499
500
  */
500
501
  /**
501
502
  * Minimal env shape this helper reads. Apps' ShopifyEnv /
@@ -531,6 +532,35 @@ declare function resolvePlatformWebhookBase(env: PlatformHostEnv, override?: str
531
532
  * form should pre-translate before calling.
532
533
  */
533
534
  declare function buildMarketplaceWebhookUrl(env: PlatformHostEnv, installId: string, topicPath: string, override?: string | null): string;
535
+ /** Minimal env shape `publicBaseUrl` reads; pass the full app env. */
536
+ interface PublicBaseUrlEnv {
537
+ SITE_PUBLIC_BASE_URL?: string;
538
+ }
539
+ /**
540
+ * This install's own public https origin, as the platform bound it:
541
+ * the install site's custom domain when one is configured, else its
542
+ * slug-free `<app>-<tail>.apps.sprigr.com` host (`.staging-apps` on
543
+ * staging). Never has a trailing slash or a path.
544
+ *
545
+ * const base = publicBaseUrl(env);
546
+ * if (!base) return refuse('no_public_base_url');
547
+ * const callbackUrl = `${base}/api/public/myapp/webhook/${subKey}`;
548
+ *
549
+ * Use it for every URL handed to an outside system: webhook and push
550
+ * callbacks, links in outbound mail, a provider's "return to" URL.
551
+ * Do NOT derive one from a request's `Host` / `X-Forwarded-Host` (a
552
+ * visitor sets those), and do NOT store a request-derived origin for
553
+ * later use (sprigr-apps#3453). The binding is not tied to the
554
+ * company slug, so it survives a company rename (sprigr-team#11787).
555
+ * It does change when a custom domain is added or removed (the
556
+ * platform rebinds the install), so an app that registered callbacks
557
+ * should re-check them on its reconcile, not only at connect time.
558
+ *
559
+ * Returns `null` when the binding is absent or is not an https URL
560
+ * (a unit test, a local `sprigr app dev`). Callers should refuse and
561
+ * say why rather than fall back to a request-derived host.
562
+ */
563
+ declare function publicBaseUrl(env: PublicBaseUrlEnv): string | null;
534
564
  /**
535
565
  * Is this URL (or bare hostname) served by a STAGING host?
536
566
  *
@@ -1018,7 +1048,12 @@ interface SprigrDataPartialUpdateBody {
1018
1048
  withAcl?: true;
1019
1049
  }
1020
1050
  interface PartialUpdateDataOptions extends SprigrDataPartialUpdateOpts {
1021
- /** Ceiling on the HTTP fallback. No default: a data write is awaited for its result. */
1051
+ /**
1052
+ * Ceiling on the call, by either transport: the injected member bounds its
1053
+ * own fetch (sprigr/sprigr-team#11950; older wrapper builds ignore it), the
1054
+ * HTTP fallback its request. No default: a data write is awaited for its
1055
+ * result.
1056
+ */
1022
1057
  timeoutMs?: number;
1023
1058
  }
1024
1059
  /**
@@ -3266,8 +3301,24 @@ interface SprigrDataIndexOpts {
3266
3301
  /** LOGICAL index name (a `data_indexes` key). See {@link SprigrDataSearchOpts.index}. */
3267
3302
  index?: string;
3268
3303
  }
3304
+ /**
3305
+ * Opt-in bound for one data write (`import`, `partialUpdate`, `delete`),
3306
+ * the same knob `data.search` takes (sprigr/sprigr-team#11950).
3307
+ */
3308
+ interface SprigrDataWriteTimeoutOpts {
3309
+ /**
3310
+ * Abort the call after this many ms and throw a named error
3311
+ * (`data_import_timeout`, `data_partial_update_timeout`,
3312
+ * `data_delete_timeout`) whose message names the index. Omit it and the
3313
+ * call is unbounded as before (`import` keeps the platform's 100 s
3314
+ * backstop). The abort is client-side only: the platform call may still
3315
+ * complete, so retry only idempotent writes. Ignored by wrapper builds
3316
+ * older than sprigr/sprigr-team#11950.
3317
+ */
3318
+ timeoutMs?: number;
3319
+ }
3269
3320
  /** Options for `env.SPRIGR.data.import`. */
3270
- interface SprigrDataImportOpts extends SprigrDataIndexOpts {
3321
+ interface SprigrDataImportOpts extends SprigrDataIndexOpts, SprigrDataWriteTimeoutOpts {
3271
3322
  /**
3272
3323
  * Write to the app's reserved ACL file index
3273
3324
  * (`<companyId>-app-<slug>-acl-files`) instead of its data index. Every
@@ -3282,7 +3333,7 @@ interface SprigrDataImportOpts extends SprigrDataIndexOpts {
3282
3333
  withAcl?: boolean;
3283
3334
  }
3284
3335
  /** Options for `env.SPRIGR.data.partialUpdate`. */
3285
- interface SprigrDataPartialUpdateOpts extends SprigrDataIndexOpts {
3336
+ interface SprigrDataPartialUpdateOpts extends SprigrDataIndexOpts, SprigrDataWriteTimeoutOpts {
3286
3337
  /**
3287
3338
  * Create an object that does not exist yet from the patch alone. Default
3288
3339
  * `false`: a patch never creates half an object unless asked, and a
@@ -3422,7 +3473,7 @@ interface SprigrDataApi {
3422
3473
  * uncertainty), per the mirror-maintenance guidance in
3423
3474
  * docs/platform-reference.md.
3424
3475
  */
3425
- delete?(objectIDs: string[], opts?: SprigrDataIndexOpts & {
3476
+ delete?(objectIDs: string[], opts?: SprigrDataIndexOpts & SprigrDataWriteTimeoutOpts & {
3426
3477
  withAcl?: boolean;
3427
3478
  }): Promise<{
3428
3479
  ok: boolean;
@@ -3471,10 +3522,12 @@ interface SprigrFilesApi {
3471
3522
  * Stream bytes into the app's store under `key` (app-relative; the platform
3472
3523
  * adds/strips the `_apps/{installId}/` prefix). A `ReadableStream` is piped
3473
3524
  * straight through to R2 without buffering whole in the isolate, so use it
3474
- * for multi-MB blobs (videos, full-res photos, PDFs). Size cap: 50 MB.
3525
+ * for multi-MB blobs (videos, full-res photos, PDFs). Size cap: 200 MB
3526
+ * (`MAX_STREAM_PUT_BYTES` in sprigr-team `wfp-files.ts`).
3475
3527
  * `opts.length` declares the byte length so the platform can enforce the cap
3476
3528
  * before opening the write; forward it for streamed bodies, which carry no
3477
- * Content-Length (`bytes` comes back `null` when it is omitted).
3529
+ * Content-Length. The platform checks the cap ONLY against a declared
3530
+ * length, so always pass it (`bytes` comes back `null` when it is omitted).
3478
3531
  */
3479
3532
  putStream(key: string, body: ReadableStream | ArrayBuffer | Uint8Array | string, opts?: {
3480
3533
  contentType?: string;
@@ -3689,4 +3742,4 @@ interface ExecutionContextLike {
3689
3742
  }
3690
3743
  type HandlerFn<TArgs, TResult> = (args: TArgs, env: Record<string, unknown>, ctx?: ExecutionContextLike) => Promise<TResult>;
3691
3744
 
3692
- export { ACTOR_RETIRED_HOOK_TOOL, ACTOR_RETIRE_REASONS, APPROVAL_GRANTED_KEY, APPROVAL_PREVIEW_MAX, APP_FILES_ROOT, APP_OWNER_SEGMENT, type Actor, type ActorRetireReason, type ActorRetired, type ActorRetiredHookBody, type ActorRetiredResult, type AppApprovalEnvelope, type AppFileListItem, type AppFileUrlArgs, type AppFileUrlResult, type AppFilesEnv, type AppToolEffectsDeclaration, type AppUndoEnvelope, type ApplyPoliciesOptions, type ApprovalCoverage, type ApprovalGateOptions, type ApprovalPreviewFact, type ApprovalPreviewSlot, type ApprovalPreviewSpec, type ApprovalSpec, type ArchiveOfferOptions, type ArchiveOfferRefusal, type CallerKeyResolution, type CaptureJournal, type CompanyTimezoneEnv, type ConfirmCondition, type ConfirmRule, type ConfirmationPolicy, type ConnectCompleteRequest, type ConnectCompleteResult, type D1Like, type D1PreparedStatementLike, DATA_DELETE_MAX_IDS, DATA_PARTIAL_UPDATE_PATH, DEFAULT_EMIT_TIMEOUT_MS, DEFAULT_FILE_READ_TIMEOUT_MS, DEFAULT_LOG_TIMEOUT_MS, DEFAULT_MAX_FILE_BYTES, DEFAULT_MONEY_FIELDS, DEFAULT_WRITE_PREFIXES, type DataClient, type DataIdsOpts, type DataListIdsResult, type DataTransport, type DerivePolicyOptions, type DerivedPolicy, type DispatcherApprovalGate, type DispatcherApprovalOptions, type EmailPreviewInput, type EmitMarketplaceEventOptions, type EmitResult, type EmitTransport, type EncodeStateWithinEnvelopeOptions, type EncodedStateWithinEnvelope, type EventArgs, type ExecutionContextLike, FILE_REF_SOURCES_HELP, type FetchFileResult, type FetchWithRetryOptions, type FileRef, type FileRefContent, type FileRefEnv, FileRefError, type FileRefErrorCode, type FileRefStream, type FileRefVia, type GetAppFileResult, type HandlerFn, INBOX_SEND_CONTRACT_V1, type IdPrefixSpec, type IdSnapshot, type InboxAddress, type InboxAppendRequest, type InboxAppendResult, type InboxChannelDeclaration, type InboxFoldersRequest, type InboxFoldersResult, type InboxOwner, type InboxSendConnection, type InboxSendHandlerImpl, InboxSendRefusal, type InboxSendSupports, type InboxSendV1Args, type InboxSendV1Result, type InstallBridge, InvalidUndoEnvelopeError, type JournalRowLike, type LogToPlatformOptions, type ManifestDispatchBlock, type ManifestLike, type ManifestToolLike, type MarketplaceEventEmitOpts, type MarketplaceEventSourceIntegration, NotConnectedError, OAUTH_STATE_ENVELOPE_MAX_CHARS, type OAuthBouncerSignals, type OAuthState, type OfferUndoArgs, type PartialUpdateDataOptions, type PlatformHostEnv, type PolicyCheckInput, type PolicySource, type PurgeResult, type PutAppFileArgs, type PutAppFileResult, type PutAppFileStreamArgs, type ReadFileRefOptions, type RequireApprovalOptions, type ResolveFileRefOptions, type ResolveViewerOptions, type ResolvedFileRef, type RestoreResult, type RestoreSpec, SPRIGR_DATA_MAX_OBJECTS_PER_CALL, SPRIGR_FILE_HOSTS, SPRIGR_LOG_LEVELS, SPRIGR_LOG_LIMITS, type ScheduleArgs, type SmsPreviewInput, type SprigrDataAggregationCell, type SprigrDataAggregationResult, type SprigrDataAggregationSpec, type SprigrDataApi, type SprigrDataImportOpts, type SprigrDataIndexOpts, type SprigrDataPartialUpdateBody, type SprigrDataPartialUpdateOpts, type SprigrDataPartialUpdateResult, type SprigrDataPatch, type SprigrDataSearchOpts, type SprigrDataSearchResult, SprigrDataValidationError, type SprigrFilesApi, type SprigrFilesApiWithEdit, type SprigrFilesCreateInput, type SprigrFilesCreateResult, type SprigrFilesEditInput, type SprigrFilesEditResult, type SprigrFilesJobResult, type SprigrFilesListItem, type SprigrFilesListResult, type SprigrInboxApi, type SprigrLogEntry, type SprigrLogFn, type SprigrLogLevel, type SprigrLogResult, SprigrLogValidationError, type SyncedAttachment, type SyncedMessage, type ToolArgs, type ToolHandler, type ToolResult, type ToolWrapperOptions, UNIT_SEP, type UndoApplyOptions, type UndoApplyResult, type UndoCaptureSpec, type UndoFidelity, VIEWER_ANONYMOUS, VIEWER_CONTEXT_HEADER, VIEWER_PLATFORM_USER_ID_HEADER, VIEWER_ROLE_HEADER, VIEWER_USER_ID_HEADER, type VerbRule, type ViewerContext, type ViewerContextFailure, type ViewerContextVerification, type ViewerEnv, WORKSPACE_FILE_HOW_TO, type WebhookArgs, type WfpBridgeEnv, type WfpEmitReply, actorKey, actorRetiredHook, actorTool, agentFileKeyFromUrl, appFileUrl, appKeyFromCallerKey, applyConfirmationPolicies, approvalHash, approvalPreview, approvalRecipients, archiveOfferRefusal, attendedFindings, base64ToBytes, buildConfirmationPolicy, buildMarketplaceWebhookUrl, buildPartialUpdateBody, bytesToBase64, callerKeyRefusal, canEmit, canLog, canPartialUpdate, checkConfirmationPolicy, commonDestructiveVerbs, connectComplete, connectDisconnect, constantTimeEqual, createMarketplaceEmitter, createToolWrappers, decodeState, defineInboxSendHandler, deleteAppFile, deleteSnapshotIds, deriveConfirmationPolicy, describeMissingBridge, dispatcherApproval, emailBodyHtml, emailPreview, emitMarketplaceEvent, encodeState, encodeStateWithinEnvelope, fetchFileAsBase64, fetchFileBytes, fetchWithRetry, fieldsPreview, fullWarning, getAppFile, getCompanyTimezone, hasHtmlMarkup, hmacSha256Hex, installTokenPost, isNotConnectedError, isStagingHost, listAppFiles, logToPlatform, makeDataClient, manifestIsFresh, oauthStateEnvelopeOverhead, offerUndo, openFileRef, overlaySprigr, ownerFromActorKey, parseActor, parseActorRetiredArgs, partialUpdateData, purgeIndexByPrefix, putAppFile, putAppFileStream, randomHex, readFileRef, readableStoredKey, recreatedWarning, refuseWithoutForce, requireApproval, resolveFileRef, resolveInstallBridge, resolveOAuthBouncerBase, resolveOAuthBouncerCallbackUrl, resolvePlatformWebhookBase, resolveViewerContext, resolveViewerUserId, runUndoApply, safeCapture, seq, serializeManifest, set, smsPreview, snapshotIndexIds, tool, undoEnvelope, validateLogEntries, validateLogEntry, verifyViewerToken, withSprigrEmitFallback, withSprigrInboxFallback, withSprigrLogFallback };
3745
+ export { ACTOR_RETIRED_HOOK_TOOL, ACTOR_RETIRE_REASONS, APPROVAL_GRANTED_KEY, APPROVAL_PREVIEW_MAX, APP_FILES_ROOT, APP_OWNER_SEGMENT, type Actor, type ActorRetireReason, type ActorRetired, type ActorRetiredHookBody, type ActorRetiredResult, type AppApprovalEnvelope, type AppFileListItem, type AppFileUrlArgs, type AppFileUrlResult, type AppFilesEnv, type AppToolEffectsDeclaration, type AppUndoEnvelope, type ApplyPoliciesOptions, type ApprovalCoverage, type ApprovalGateOptions, type ApprovalPreviewFact, type ApprovalPreviewSlot, type ApprovalPreviewSpec, type ApprovalSpec, type ArchiveOfferOptions, type ArchiveOfferRefusal, type CallerKeyResolution, type CaptureJournal, type CompanyTimezoneEnv, type ConfirmCondition, type ConfirmRule, type ConfirmationPolicy, type ConnectCompleteRequest, type ConnectCompleteResult, type D1Like, type D1PreparedStatementLike, DATA_DELETE_MAX_IDS, DATA_PARTIAL_UPDATE_PATH, DEFAULT_EMIT_TIMEOUT_MS, DEFAULT_FILE_READ_TIMEOUT_MS, DEFAULT_LOG_TIMEOUT_MS, DEFAULT_MAX_FILE_BYTES, DEFAULT_MONEY_FIELDS, DEFAULT_WRITE_PREFIXES, type DataClient, type DataIdsOpts, type DataListIdsResult, type DataTransport, type DerivePolicyOptions, type DerivedPolicy, type DispatcherApprovalGate, type DispatcherApprovalOptions, type EmailPreviewInput, type EmitMarketplaceEventOptions, type EmitResult, type EmitTransport, type EncodeStateWithinEnvelopeOptions, type EncodedStateWithinEnvelope, type EventArgs, type ExecutionContextLike, FILE_REF_SOURCES_HELP, type FetchFileResult, type FetchWithRetryOptions, type FileRef, type FileRefContent, type FileRefEnv, FileRefError, type FileRefErrorCode, type FileRefStream, type FileRefVia, type GetAppFileResult, type HandlerFn, INBOX_SEND_CONTRACT_V1, type IdPrefixSpec, type IdSnapshot, type InboxAddress, type InboxAppendRequest, type InboxAppendResult, type InboxChannelDeclaration, type InboxFoldersRequest, type InboxFoldersResult, type InboxOwner, type InboxSendConnection, type InboxSendHandlerImpl, InboxSendRefusal, type InboxSendSupports, type InboxSendV1Args, type InboxSendV1Result, type InstallBridge, InvalidUndoEnvelopeError, type JournalRowLike, type LogToPlatformOptions, type ManifestDispatchBlock, type ManifestLike, type ManifestToolLike, type MarketplaceEventEmitOpts, type MarketplaceEventSourceIntegration, NotConnectedError, OAUTH_STATE_ENVELOPE_MAX_CHARS, type OAuthBouncerSignals, type OAuthState, type OfferUndoArgs, type PartialUpdateDataOptions, type PlatformHostEnv, type PolicyCheckInput, type PolicySource, type PublicBaseUrlEnv, type PurgeResult, type PutAppFileArgs, type PutAppFileResult, type PutAppFileStreamArgs, type ReadFileRefOptions, type RequireApprovalOptions, type ResolveFileRefOptions, type ResolveViewerOptions, type ResolvedFileRef, type RestoreResult, type RestoreSpec, SPRIGR_DATA_MAX_OBJECTS_PER_CALL, SPRIGR_FILE_HOSTS, SPRIGR_LOG_LEVELS, SPRIGR_LOG_LIMITS, type ScheduleArgs, type SmsPreviewInput, type SprigrDataAggregationCell, type SprigrDataAggregationResult, type SprigrDataAggregationSpec, type SprigrDataApi, type SprigrDataImportOpts, type SprigrDataIndexOpts, type SprigrDataPartialUpdateBody, type SprigrDataPartialUpdateOpts, type SprigrDataPartialUpdateResult, type SprigrDataPatch, type SprigrDataSearchOpts, type SprigrDataSearchResult, SprigrDataValidationError, type SprigrDataWriteTimeoutOpts, type SprigrFilesApi, type SprigrFilesApiWithEdit, type SprigrFilesCreateInput, type SprigrFilesCreateResult, type SprigrFilesEditInput, type SprigrFilesEditResult, type SprigrFilesJobResult, type SprigrFilesListItem, type SprigrFilesListResult, type SprigrInboxApi, type SprigrLogEntry, type SprigrLogFn, type SprigrLogLevel, type SprigrLogResult, SprigrLogValidationError, type SyncedAttachment, type SyncedMessage, type ToolArgs, type ToolHandler, type ToolResult, type ToolWrapperOptions, UNIT_SEP, type UndoApplyOptions, type UndoApplyResult, type UndoCaptureSpec, type UndoFidelity, VIEWER_ANONYMOUS, VIEWER_CONTEXT_HEADER, VIEWER_PLATFORM_USER_ID_HEADER, VIEWER_ROLE_HEADER, VIEWER_USER_ID_HEADER, type VerbRule, type ViewerContext, type ViewerContextFailure, type ViewerContextVerification, type ViewerEnv, WORKSPACE_FILE_HOW_TO, type WebhookArgs, type WfpBridgeEnv, type WfpEmitReply, actorKey, actorRetiredHook, actorTool, agentFileKeyFromUrl, appFileUrl, appKeyFromCallerKey, applyConfirmationPolicies, approvalHash, approvalPreview, approvalRecipients, archiveOfferRefusal, attendedFindings, base64ToBytes, buildConfirmationPolicy, buildMarketplaceWebhookUrl, buildPartialUpdateBody, bytesToBase64, callerKeyRefusal, canEmit, canLog, canPartialUpdate, checkConfirmationPolicy, commonDestructiveVerbs, connectComplete, connectDisconnect, constantTimeEqual, createMarketplaceEmitter, createToolWrappers, decodeState, defineInboxSendHandler, deleteAppFile, deleteSnapshotIds, deriveConfirmationPolicy, describeMissingBridge, dispatcherApproval, emailBodyHtml, emailPreview, emitMarketplaceEvent, encodeState, encodeStateWithinEnvelope, fetchFileAsBase64, fetchFileBytes, fetchWithRetry, fieldsPreview, fullWarning, getAppFile, getCompanyTimezone, hasHtmlMarkup, hmacSha256Hex, installTokenPost, isNotConnectedError, isStagingHost, listAppFiles, logToPlatform, makeDataClient, manifestIsFresh, oauthStateEnvelopeOverhead, offerUndo, openFileRef, overlaySprigr, ownerFromActorKey, parseActor, parseActorRetiredArgs, partialUpdateData, publicBaseUrl, purgeIndexByPrefix, putAppFile, putAppFileStream, randomHex, readFileRef, readableStoredKey, recreatedWarning, refuseWithoutForce, requireApproval, resolveFileRef, resolveInstallBridge, resolveOAuthBouncerBase, resolveOAuthBouncerCallbackUrl, resolvePlatformWebhookBase, resolveViewerContext, resolveViewerUserId, runUndoApply, safeCapture, seq, serializeManifest, set, smsPreview, snapshotIndexIds, tool, undoEnvelope, validateLogEntries, validateLogEntry, verifyViewerToken, withSprigrEmitFallback, withSprigrInboxFallback, withSprigrLogFallback };
package/dist/index.js CHANGED
@@ -603,6 +603,16 @@ function buildMarketplaceWebhookUrl(env, installId, topicPath, override) {
603
603
  const base = resolvePlatformWebhookBase(env, override);
604
604
  return `${base}/webhook/marketplace/${installId}/${topicPath}`;
605
605
  }
606
+ function publicBaseUrl(env) {
607
+ const raw = typeof env.SITE_PUBLIC_BASE_URL === "string" ? env.SITE_PUBLIC_BASE_URL.trim() : "";
608
+ if (!raw) return null;
609
+ try {
610
+ const url = new URL(raw);
611
+ return url.protocol === "https:" ? url.origin : null;
612
+ } catch {
613
+ return null;
614
+ }
615
+ }
606
616
  var OAUTH_BOUNCER_PROD = "https://oauth-bouncer.sprigr.com";
607
617
  var OAUTH_BOUNCER_STAGING = "https://staging-oauth-bouncer.sprigr.com";
608
618
  function isStagingHost(urlOrHostname) {
@@ -689,6 +699,9 @@ async function installTokenPost(bridge, path, body, opts) {
689
699
  err.status = resp.status;
690
700
  err.error = code;
691
701
  if (obj && typeof obj.detail === "string") err.detail = obj.detail;
702
+ const retryAfter = resp.headers.get("retry-after");
703
+ const retryAfterSeconds = retryAfter === null ? NaN : Number(retryAfter.trim());
704
+ if (Number.isFinite(retryAfterSeconds) && retryAfterSeconds >= 0) err.retryAfterSeconds = retryAfterSeconds;
692
705
  throw err;
693
706
  }
694
707
  return parsed ?? {};
@@ -1067,7 +1080,8 @@ async function partialUpdateData(env, objects, opts) {
1067
1080
  const result = await injected(body.objects, {
1068
1081
  ...body.index !== void 0 ? { index: body.index } : {},
1069
1082
  createIfNotExists: body.createIfNotExists,
1070
- ...body.withAcl ? { withAcl: true } : {}
1083
+ ...body.withAcl ? { withAcl: true } : {},
1084
+ ...timeoutMs !== void 0 ? { timeoutMs } : {}
1071
1085
  });
1072
1086
  return body.withAcl ? assertAclIndex(result) : result;
1073
1087
  }
@@ -2124,6 +2138,10 @@ async function connectDisconnect(env, req) {
2124
2138
 
2125
2139
  // src/data-purge.ts
2126
2140
  var BRIDGE_TIMEOUT_MS = 2e4;
2141
+ var IMPORT_RETRY_MAX_WAIT_MS = 3e3;
2142
+ var IMPORT_RETRY_WINDOW_MS = 12e3;
2143
+ var IMPORT_TOTAL_BUDGET_MS = 25e3;
2144
+ var IMPORT_RETRY_MIN_MS = 1500;
2127
2145
  var DATA_DELETE_MAX_IDS = 1e3;
2128
2146
  function injectedData(env) {
2129
2147
  const data = env.SPRIGR?.data;
@@ -2162,7 +2180,26 @@ function makeDataClient(env) {
2162
2180
  return {
2163
2181
  via: "http",
2164
2182
  async import(objects, opts) {
2165
- const r = await post("/internal/wfp/data/import", { objects, ...opts ?? {} }, "data.import");
2183
+ const body = { objects, ...opts ?? {} };
2184
+ const startedAt = Date.now();
2185
+ const callerMs = typeof opts?.timeoutMs === "number" && opts.timeoutMs > 0 ? opts.timeoutMs : void 0;
2186
+ const sendImport = (timeoutMs) => installTokenPost(bridge, "/internal/wfp/data/import", body, { label: "data.import", timeoutMs });
2187
+ let r;
2188
+ try {
2189
+ r = await sendImport(Math.min(BRIDGE_TIMEOUT_MS, callerMs ?? BRIDGE_TIMEOUT_MS));
2190
+ } catch (err) {
2191
+ const e = err;
2192
+ if (e?.status !== 503) throw err;
2193
+ const waitMs = Math.min(
2194
+ typeof e.retryAfterSeconds === "number" ? e.retryAfterSeconds * 1e3 : 1e3,
2195
+ IMPORT_RETRY_MAX_WAIT_MS
2196
+ );
2197
+ const spentMs = Date.now() - startedAt;
2198
+ const retryMs = Math.min(BRIDGE_TIMEOUT_MS, (callerMs ?? IMPORT_TOTAL_BUDGET_MS) - spentMs - waitMs);
2199
+ if (spentMs + waitMs > IMPORT_RETRY_WINDOW_MS || retryMs < IMPORT_RETRY_MIN_MS) throw err;
2200
+ if (waitMs > 0) await new Promise((resolve) => setTimeout(resolve, waitMs));
2201
+ r = await sendImport(retryMs);
2202
+ }
2166
2203
  return { ok: r.ok !== false, indexed: typeof r.indexed === "number" ? r.indexed : objects.length, index: String(r.index ?? opts?.index ?? "") };
2167
2204
  },
2168
2205
  async search(opts) {
@@ -2380,6 +2417,7 @@ export {
2380
2417
  parseActor,
2381
2418
  parseActorRetiredArgs,
2382
2419
  partialUpdateData,
2420
+ publicBaseUrl,
2383
2421
  purgeIndexByPrefix,
2384
2422
  putAppFile,
2385
2423
  putAppFileStream,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sprigr/apps-app-sdk",
3
- "version": "0.27.0",
3
+ "version": "0.28.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",