@sprigr/apps-app-sdk 0.12.0 → 0.14.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
@@ -669,6 +669,12 @@ declare function withSprigrLogFallback<E extends WfpBridgeEnv & object>(env: E,
669
669
  * fails with `shard_field_invalid`: a patch cannot be routed to its shard
670
670
  * otherwise. The per-call cap is the same 1000 objects as `import`.
671
671
  *
672
+ * `withAcl: true` is a narrower mode: it re-stamps `acl_principals` on rows
673
+ * already in the app's `-acl-files` index and nothing else. Every patch must
674
+ * be exactly `{ objectID, acl_principals }`, and `createIfNotExists` and
675
+ * `index` cannot be combined with it. Content is written only by an ACL
676
+ * `import`, whole.
677
+ *
672
678
  * This module carries two things:
673
679
  *
674
680
  * 1. `buildPartialUpdateBody`, the wire body both transports send, with
@@ -687,10 +693,13 @@ declare function withSprigrLogFallback<E extends WfpBridgeEnv & object>(env: E,
687
693
  * swallow.
688
694
  *
689
695
  * Wire contract (workers/provisioning/src/wfp-data.ts in sprigr-team):
690
- * POST { index?, objects, createIfNotExists? }
696
+ * POST { index?, objects, createIfNotExists?, withAcl? }
691
697
  * 200 { ok: true, updated, skippedMissing, index, physical_indexes? }
692
698
  * 400 { error, detail, index? } with error one of too_many_objects,
693
- * invalid_object, unknown_data_index, shard_field_invalid, ...
699
+ * invalid_object, unknown_data_index, shard_field_invalid,
700
+ * acl_partial_update_fields, acl_partial_update_create_unsupported,
701
+ * acl_index_combination_unsupported, missing_acl_principals,
702
+ * invalid_acl_principal, ...
694
703
  * 401 / 404 bad token / inactive install
695
704
  */
696
705
 
@@ -708,6 +717,8 @@ interface SprigrDataPartialUpdateBody {
708
717
  index?: string;
709
718
  objects: SprigrDataPatch[];
710
719
  createIfNotExists: boolean;
720
+ /** Present (and true) only for an ACL re-stamp. */
721
+ withAcl?: true;
711
722
  }
712
723
  interface PartialUpdateDataOptions extends SprigrDataPartialUpdateOpts {
713
724
  /** Ceiling on the HTTP fallback. No default: a data write is awaited for its result. */
@@ -2244,6 +2255,17 @@ interface SprigrDataPartialUpdateOpts extends SprigrDataIndexOpts {
2244
2255
  * missing target is counted in `skippedMissing` instead of being written.
2245
2256
  */
2246
2257
  createIfNotExists?: boolean;
2258
+ /**
2259
+ * Re-stamp `acl_principals` on rows already in the app's `-acl-files`
2260
+ * index without rewriting the rest of each row. Every object must be
2261
+ * exactly `{ objectID, acl_principals }` with a non-empty, valid principal
2262
+ * list; `createIfNotExists` and `index` are refused alongside it. Use it
2263
+ * when the source changes who may see an item without reporting the item
2264
+ * as changed (OneDrive's delta omits sharing-only changes). Needs a
2265
+ * platform with sprigr/sprigr-team#9843; older wrappers drop the flag,
2266
+ * which `partialUpdateData` detects and throws on.
2267
+ */
2268
+ withAcl?: boolean;
2247
2269
  }
2248
2270
  /** Reply of `env.SPRIGR.data.partialUpdate` (`POST /internal/wfp/data/partial-update`). */
2249
2271
  interface SprigrDataPartialUpdateResult {
@@ -2379,6 +2401,12 @@ interface SprigrDataApi {
2379
2401
  * URL the upstream deletes after a short retention window) so the URL your
2380
2402
  * app hands back keeps resolving.
2381
2403
  *
2404
+ * Per-user storage (sprigr-team#9621): keys under the manifest's
2405
+ * `actor_scoped_storage_prefixes` are stored and resolved per CALLER by the
2406
+ * platform, from the signed actor the wrapper forwards on every call. Keep
2407
+ * using app-relative keys; never add your own owner tag. A call with no actor
2408
+ * may only `list` / `delete` there (maintenance), never read.
2409
+ *
2382
2410
  * This is the in-isolate bridge surface, injected on `env.SPRIGR` for
2383
2411
  * `/__sprigr/*` tool / webhook / event handlers. The standalone
2384
2412
  * `putAppFileStream` / `appFileUrl` helpers in this package call the same
@@ -2418,6 +2446,75 @@ interface SprigrFilesApi {
2418
2446
  expires_at: number;
2419
2447
  key: string;
2420
2448
  }>;
2449
+ /**
2450
+ * Buffered put of base64 bytes (cap 10 MB); prefer `putStream` for anything
2451
+ * large. Optional: absent on wrapper builds before sprigr-team#9621, so
2452
+ * feature-detect. Throws with `err.code` / `err.status` on a refusal.
2453
+ */
2454
+ put?(key: string, base64: string, opts?: {
2455
+ contentType?: string;
2456
+ filename?: string;
2457
+ }): Promise<{
2458
+ ok: boolean;
2459
+ key: string;
2460
+ bytes: number;
2461
+ contentType: string;
2462
+ }>;
2463
+ /** Read a stored object back as base64. Throws `err.code === 'not_found'`
2464
+ * (404) when absent. Optional: see `put`. */
2465
+ get?(key: string): Promise<{
2466
+ ok: boolean;
2467
+ key: string;
2468
+ base64: string;
2469
+ contentType: string;
2470
+ filename?: string;
2471
+ bytes: number;
2472
+ }>;
2473
+ /**
2474
+ * List stored keys under an app-relative prefix, 1000 per page; pass the
2475
+ * returned `cursor` for the next page. On a call with no actor (a schedule,
2476
+ * webhook or event handler), a prefix under `actor_scoped_storage_prefixes`
2477
+ * lists EVERY user's copies, each entry carrying an opaque `owner_ref`
2478
+ * (`owner_scope: 'all_owners'`); with `ownerless: true` it lists the copies
2479
+ * written there before the prefix was declared (`owner_scope: 'ownerless'`).
2480
+ * Optional: see `put`.
2481
+ */
2482
+ list?(prefix?: string, opts?: {
2483
+ cursor?: string;
2484
+ ownerless?: boolean;
2485
+ }): Promise<SprigrFilesListResult>;
2486
+ /**
2487
+ * Delete a stored key. On a call with no actor, a per-user key needs the
2488
+ * `owner_ref` (or `ownerless: true`) a maintenance `list` returned; on a call
2489
+ * made for a user both are ignored and that user's own copy is deleted.
2490
+ * Optional: see `put`.
2491
+ */
2492
+ delete?(key: string, opts?: {
2493
+ owner_ref?: string;
2494
+ ownerless?: boolean;
2495
+ }): Promise<{
2496
+ ok: boolean;
2497
+ key: string;
2498
+ }>;
2499
+ }
2500
+ /** One entry of `env.SPRIGR.files.list`. `owner_ref` / `ownerless` appear only
2501
+ * on a maintenance listing of a per-user prefix (sprigr-team#9621). */
2502
+ interface SprigrFilesListItem {
2503
+ key: string;
2504
+ bytes: number;
2505
+ uploaded?: string;
2506
+ filename?: string;
2507
+ contentType?: string;
2508
+ owner_ref?: string;
2509
+ ownerless?: boolean;
2510
+ }
2511
+ /** Result of `env.SPRIGR.files.list`. */
2512
+ interface SprigrFilesListResult {
2513
+ ok: boolean;
2514
+ files: SprigrFilesListItem[];
2515
+ truncated: boolean;
2516
+ cursor?: string;
2517
+ owner_scope?: 'all_owners' | 'ownerless';
2421
2518
  }
2422
2519
  /**
2423
2520
  * Platform document-engine bridge types (`env.SPRIGR.files.edit` / `.create`
@@ -2539,4 +2636,4 @@ interface ExecutionContextLike {
2539
2636
  }
2540
2637
  type HandlerFn<TArgs, TResult> = (args: TArgs, env: Record<string, unknown>, ctx?: ExecutionContextLike) => Promise<TResult>;
2541
2638
 
2542
- export { APPROVAL_GRANTED_KEY, type Actor, type AppApprovalEnvelope, type AppFileListItem, type AppFileUrlArgs, type AppFileUrlResult, type AppFilesEnv, type AppToolEffectsDeclaration, type AppUndoEnvelope, type ApplyPoliciesOptions, type ApprovalGateOptions, type ApprovalSpec, type ArchiveOfferOptions, type ArchiveOfferRefusal, type CaptureJournal, type ConfirmCondition, type ConfirmRule, type ConfirmationPolicy, type D1Like, type D1PreparedStatementLike, DATA_PARTIAL_UPDATE_PATH, DEFAULT_EMIT_TIMEOUT_MS, DEFAULT_LOG_TIMEOUT_MS, DEFAULT_MAX_FILE_BYTES, DEFAULT_MONEY_FIELDS, DEFAULT_WRITE_PREFIXES, type DerivePolicyOptions, type DerivedPolicy, type DispatcherApprovalGate, type DispatcherApprovalOptions, type EmitMarketplaceEventOptions, type EmitResult, type EmitTransport, type EventArgs, type ExecutionContextLike, type FetchFileResult, type FetchWithRetryOptions, type GetAppFileResult, type HandlerFn, type InboxOwner, type InstallBridge, InvalidUndoEnvelopeError, type JournalRowLike, type LogToPlatformOptions, type ManifestDispatchBlock, type ManifestLike, type ManifestToolLike, type MarketplaceEventEmitOpts, type MarketplaceEventSourceIntegration, NotConnectedError, type OAuthBouncerSignals, type OAuthState, type OfferUndoArgs, type PartialUpdateDataOptions, type PlatformHostEnv, type PolicyCheckInput, type PolicySource, type PutAppFileArgs, type PutAppFileResult, type PutAppFileStreamArgs, type RequireApprovalOptions, type ResolveViewerOptions, type RestoreResult, type RestoreSpec, SPRIGR_DATA_MAX_OBJECTS_PER_CALL, SPRIGR_LOG_LEVELS, SPRIGR_LOG_LIMITS, type ScheduleArgs, type SprigrDataAggregationCell, type SprigrDataAggregationResult, type SprigrDataAggregationSpec, type SprigrDataApi, 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 SprigrLogEntry, type SprigrLogFn, type SprigrLogLevel, type SprigrLogResult, SprigrLogValidationError, 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, type WebhookArgs, type WfpBridgeEnv, type WfpEmitReply, actorKey, actorTool, appFileUrl, applyConfirmationPolicies, approvalHash, archiveOfferRefusal, base64ToBytes, buildConfirmationPolicy, buildMarketplaceWebhookUrl, buildPartialUpdateBody, bytesToBase64, canEmit, canLog, canPartialUpdate, checkConfirmationPolicy, commonDestructiveVerbs, constantTimeEqual, createMarketplaceEmitter, createToolWrappers, decodeState, deleteAppFile, deriveConfirmationPolicy, describeMissingBridge, dispatcherApproval, emitMarketplaceEvent, encodeState, fetchFileAsBase64, fetchFileBytes, fetchWithRetry, fullWarning, getAppFile, hmacSha256Hex, installTokenPost, isNotConnectedError, isStagingHost, listAppFiles, logToPlatform, manifestIsFresh, offerUndo, overlaySprigr, ownerFromActorKey, parseActor, partialUpdateData, putAppFile, putAppFileStream, randomHex, recreatedWarning, refuseWithoutForce, requireApproval, resolveInstallBridge, resolveOAuthBouncerBase, resolveOAuthBouncerCallbackUrl, resolvePlatformWebhookBase, resolveViewerContext, resolveViewerUserId, runUndoApply, safeCapture, seq, serializeManifest, set, tool, undoEnvelope, validateLogEntries, validateLogEntry, verifyViewerToken, withSprigrEmitFallback, withSprigrLogFallback };
2639
+ export { APPROVAL_GRANTED_KEY, type Actor, type AppApprovalEnvelope, type AppFileListItem, type AppFileUrlArgs, type AppFileUrlResult, type AppFilesEnv, type AppToolEffectsDeclaration, type AppUndoEnvelope, type ApplyPoliciesOptions, type ApprovalGateOptions, type ApprovalSpec, type ArchiveOfferOptions, type ArchiveOfferRefusal, type CaptureJournal, type ConfirmCondition, type ConfirmRule, type ConfirmationPolicy, type D1Like, type D1PreparedStatementLike, DATA_PARTIAL_UPDATE_PATH, DEFAULT_EMIT_TIMEOUT_MS, DEFAULT_LOG_TIMEOUT_MS, DEFAULT_MAX_FILE_BYTES, DEFAULT_MONEY_FIELDS, DEFAULT_WRITE_PREFIXES, type DerivePolicyOptions, type DerivedPolicy, type DispatcherApprovalGate, type DispatcherApprovalOptions, type EmitMarketplaceEventOptions, type EmitResult, type EmitTransport, type EventArgs, type ExecutionContextLike, type FetchFileResult, type FetchWithRetryOptions, type GetAppFileResult, type HandlerFn, type InboxOwner, type InstallBridge, InvalidUndoEnvelopeError, type JournalRowLike, type LogToPlatformOptions, type ManifestDispatchBlock, type ManifestLike, type ManifestToolLike, type MarketplaceEventEmitOpts, type MarketplaceEventSourceIntegration, NotConnectedError, type OAuthBouncerSignals, type OAuthState, type OfferUndoArgs, type PartialUpdateDataOptions, type PlatformHostEnv, type PolicyCheckInput, type PolicySource, type PutAppFileArgs, type PutAppFileResult, type PutAppFileStreamArgs, type RequireApprovalOptions, type ResolveViewerOptions, type RestoreResult, type RestoreSpec, SPRIGR_DATA_MAX_OBJECTS_PER_CALL, SPRIGR_LOG_LEVELS, SPRIGR_LOG_LIMITS, type ScheduleArgs, type SprigrDataAggregationCell, type SprigrDataAggregationResult, type SprigrDataAggregationSpec, type SprigrDataApi, 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 SprigrLogEntry, type SprigrLogFn, type SprigrLogLevel, type SprigrLogResult, SprigrLogValidationError, 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, type WebhookArgs, type WfpBridgeEnv, type WfpEmitReply, actorKey, actorTool, appFileUrl, applyConfirmationPolicies, approvalHash, archiveOfferRefusal, base64ToBytes, buildConfirmationPolicy, buildMarketplaceWebhookUrl, buildPartialUpdateBody, bytesToBase64, canEmit, canLog, canPartialUpdate, checkConfirmationPolicy, commonDestructiveVerbs, constantTimeEqual, createMarketplaceEmitter, createToolWrappers, decodeState, deleteAppFile, deriveConfirmationPolicy, describeMissingBridge, dispatcherApproval, emitMarketplaceEvent, encodeState, fetchFileAsBase64, fetchFileBytes, fetchWithRetry, fullWarning, getAppFile, hmacSha256Hex, installTokenPost, isNotConnectedError, isStagingHost, listAppFiles, logToPlatform, manifestIsFresh, offerUndo, overlaySprigr, ownerFromActorKey, parseActor, partialUpdateData, putAppFile, putAppFileStream, randomHex, recreatedWarning, refuseWithoutForce, requireApproval, resolveInstallBridge, resolveOAuthBouncerBase, resolveOAuthBouncerCallbackUrl, resolvePlatformWebhookBase, resolveViewerContext, resolveViewerUserId, runUndoApply, safeCapture, seq, serializeManifest, set, tool, undoEnvelope, validateLogEntries, validateLogEntry, verifyViewerToken, withSprigrEmitFallback, withSprigrLogFallback };
package/dist/index.js CHANGED
@@ -512,6 +512,7 @@ function withSprigrLogFallback(env, opts) {
512
512
  // src/platform-data.ts
513
513
  var DATA_PARTIAL_UPDATE_PATH = "/internal/wfp/data/partial-update";
514
514
  var SPRIGR_DATA_MAX_OBJECTS_PER_CALL = 1e3;
515
+ var ACL_INDEX_SUFFIX = "-acl-files";
515
516
  var SprigrDataValidationError = class extends Error {
516
517
  error;
517
518
  index;
@@ -556,12 +557,51 @@ function buildPartialUpdateBody(objects, opts) {
556
557
  error: "unknown_data_index"
557
558
  });
558
559
  }
560
+ const withAcl = opts?.withAcl === true;
561
+ if (withAcl) checkAclRestamp(objects, opts, at);
559
562
  return {
560
563
  ...opts?.index !== void 0 ? { index: opts.index } : {},
561
564
  objects,
562
- createIfNotExists: opts?.createIfNotExists === true
565
+ createIfNotExists: opts?.createIfNotExists === true,
566
+ ...withAcl ? { withAcl: true } : {}
563
567
  };
564
568
  }
569
+ function checkAclRestamp(objects, opts, at) {
570
+ if (opts?.createIfNotExists === true) {
571
+ throw new SprigrDataValidationError(
572
+ `${at}: withAcl only re-stamps existing rows; createIfNotExists would create a row with principals and no content`,
573
+ { error: "acl_partial_update_create_unsupported" }
574
+ );
575
+ }
576
+ if (opts?.index !== void 0) {
577
+ throw new SprigrDataValidationError(`${at}: withAcl targets the reserved -acl-files index and cannot be combined with index`, {
578
+ error: "acl_index_combination_unsupported"
579
+ });
580
+ }
581
+ for (let i = 0; i < objects.length; i++) {
582
+ const o = objects[i];
583
+ const extra = Object.keys(o).find((k) => k !== "objectID" && k !== "acl_principals");
584
+ if (extra !== void 0) {
585
+ throw new SprigrDataValidationError(
586
+ `${at}: objects[${i}] carries "${extra}"; a withAcl partial update sets only objectID and acl_principals (write content with data.import withAcl)`,
587
+ { error: "acl_partial_update_fields", index: i }
588
+ );
589
+ }
590
+ const principals = o.acl_principals;
591
+ if (!Array.isArray(principals) || principals.length === 0) {
592
+ throw new SprigrDataValidationError(`${at}: objects[${i}] needs a non-empty acl_principals: string[]`, {
593
+ error: "missing_acl_principals",
594
+ index: i
595
+ });
596
+ }
597
+ }
598
+ }
599
+ function assertAclIndex(result) {
600
+ if (typeof result?.index === "string" && result.index.endsWith(ACL_INDEX_SUFFIX)) return result;
601
+ throw new Error(
602
+ `data.partialUpdate withAcl landed on ${String(result?.index)}, not the ${ACL_INDEX_SUFFIX} index: this app was built by a platform wrapper that drops withAcl. Republish the app to pick up the current wrapper.`
603
+ );
604
+ }
565
605
  function bindingPartialUpdate(env) {
566
606
  const data = env.SPRIGR?.data;
567
607
  return typeof data?.partialUpdate === "function" ? data.partialUpdate.bind(data) : null;
@@ -579,10 +619,12 @@ async function partialUpdateData(env, objects, opts) {
579
619
  const body = buildPartialUpdateBody(objects, dataOpts);
580
620
  const injected = bindingPartialUpdate(env);
581
621
  if (injected) {
582
- return injected(body.objects, {
622
+ const result = await injected(body.objects, {
583
623
  ...body.index !== void 0 ? { index: body.index } : {},
584
- createIfNotExists: body.createIfNotExists
624
+ createIfNotExists: body.createIfNotExists,
625
+ ...body.withAcl ? { withAcl: true } : {}
585
626
  });
627
+ return body.withAcl ? assertAclIndex(result) : result;
586
628
  }
587
629
  const bridge = resolveInstallBridge(env);
588
630
  if (!bridge) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sprigr/apps-app-sdk",
3
- "version": "0.12.0",
3
+ "version": "0.14.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",