@beignet/core 0.0.41 → 0.0.43

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.
@@ -58,8 +58,9 @@ export interface IdempotencyHooksOptions<Ctx> {
58
58
  /**
59
59
  * Build the idempotency scope after context exists.
60
60
  *
61
- * Defaults to a scope derived from `meta.scope`: `"global"` stays global,
62
- * `"actor"` scopes by `ctx.actor?.id`, `"tenant"` scopes by `ctx.tenant?.id`,
61
+ * Defaults to a scope derived from `meta.scope`: omitted metadata scopes by
62
+ * `ctx.actor?.id` and also `ctx.tenant?.id` when present, `"actor"` scopes by
63
+ * the actor only, `"global"` stays global, `"tenant"` scopes by the tenant,
63
64
  * and `"actor-tenant"` scopes by both.
64
65
  */
65
66
  scope?: (args: {
@@ -97,7 +98,7 @@ function defaultIdempotencyScope(
97
98
  ctx: CtxWithIdempotency,
98
99
  meta: IdempotencyMeta,
99
100
  ): IdempotencyScope {
100
- const mode = meta.scope ?? "global";
101
+ const mode = meta.scope ?? (ctx.tenant?.id ? "actor-tenant" : "actor");
101
102
 
102
103
  switch (mode) {
103
104
  case "global":
@@ -1037,63 +1037,69 @@ export function createUploadRouter<Ctx>(
1037
1037
 
1038
1038
  const completed: CompletedUploadFile[] = [];
1039
1039
  const storedKeys: string[] = [];
1040
- for (const [index, file] of webFiles.entries()) {
1041
- const intent = intents[index];
1042
- if (!intent) continue;
1043
- const verified = await verifyBlobUploadFile(definition, intent, file);
1044
- const verifiedIntent = {
1045
- ...intent,
1046
- ...(verified.checksum ? { checksum: verified.checksum } : {}),
1047
- };
1048
- const uploadId = id();
1049
- await assertAuthorized(definition, {
1050
- ctx,
1051
- metadata,
1052
- file: verifiedIntent,
1053
- uploadId,
1054
- });
1055
- const key = await definition.key({
1056
- ctx,
1057
- metadata,
1058
- file: verifiedIntent,
1059
- uploadId,
1060
- });
1061
- const storageMetadata =
1062
- (await definition.storageMetadata?.({
1040
+ try {
1041
+ for (const [index, file] of webFiles.entries()) {
1042
+ const intent = intents[index];
1043
+ if (!intent) continue;
1044
+ const uploadId = id();
1045
+ await assertAuthorized(definition, {
1046
+ ctx,
1047
+ metadata,
1048
+ file: intent,
1049
+ uploadId,
1050
+ });
1051
+ const verified = await verifyBlobUploadFile(definition, intent, file);
1052
+ const verifiedIntent = {
1053
+ ...intent,
1054
+ ...(verified.checksum ? { checksum: verified.checksum } : {}),
1055
+ };
1056
+ const key = await definition.key({
1063
1057
  ctx,
1064
1058
  metadata,
1065
1059
  file: verifiedIntent,
1066
1060
  uploadId,
1067
- })) ?? {};
1068
- const object = await options.storage.put(key, file, {
1069
- contentType: verifiedIntent.contentType,
1070
- ...(definition.file.cacheControl !== undefined
1071
- ? { cacheControl: definition.file.cacheControl }
1072
- : {}),
1073
- metadata: storageMetadata,
1074
- visibility: definition.file.visibility ?? "private",
1075
- });
1076
- storedKeys.push(key);
1077
- const completedFile = {
1078
- ...verifiedIntent,
1079
- uploadId,
1080
- key,
1081
- object,
1082
- };
1083
- try {
1061
+ });
1062
+ const storageMetadata =
1063
+ (await definition.storageMetadata?.({
1064
+ ctx,
1065
+ metadata,
1066
+ file: verifiedIntent,
1067
+ uploadId,
1068
+ })) ?? {};
1069
+ const object = await options.storage.put(key, file, {
1070
+ contentType: verifiedIntent.contentType,
1071
+ ...(definition.file.cacheControl !== undefined
1072
+ ? { cacheControl: definition.file.cacheControl }
1073
+ : {}),
1074
+ metadata: storageMetadata,
1075
+ visibility: definition.file.visibility ?? "private",
1076
+ });
1077
+ storedKeys.push(key);
1078
+ const completedFile = {
1079
+ ...verifiedIntent,
1080
+ uploadId,
1081
+ key,
1082
+ object,
1083
+ };
1084
1084
  await assertVerifiedFile(definition, {
1085
1085
  ctx,
1086
1086
  metadata,
1087
1087
  file: completedFile,
1088
1088
  storage: options.storage,
1089
1089
  });
1090
- } catch (error) {
1090
+ completed.push(completedFile);
1091
+ }
1092
+ } catch (error) {
1093
+ if (storedKeys.length > 0) {
1091
1094
  await cleanupRejectedServerUpload(uploadName, storedKeys);
1092
- throw error;
1093
1095
  }
1094
- completed.push(completedFile);
1096
+ throw error;
1095
1097
  }
1096
1098
 
1099
+ // Once app-owned completion begins, the app may persist durable references
1100
+ // to these objects. The framework can no longer delete them safely if a
1101
+ // later completion step fails; transaction or compensation belongs to the
1102
+ // app from this point forward.
1097
1103
  const result = await definition.onComplete?.({
1098
1104
  ctx,
1099
1105
  metadata,