@beignet/core 0.0.37 → 0.0.38

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 (111) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +108 -19
  3. package/dist/client/client.d.ts.map +1 -1
  4. package/dist/client/client.js +6 -28
  5. package/dist/client/client.js.map +1 -1
  6. package/dist/error-reporting/index.d.ts +42 -1
  7. package/dist/error-reporting/index.d.ts.map +1 -1
  8. package/dist/error-reporting/index.js +60 -0
  9. package/dist/error-reporting/index.js.map +1 -1
  10. package/dist/error-reporting/internal.d.ts +11 -0
  11. package/dist/error-reporting/internal.d.ts.map +1 -0
  12. package/dist/error-reporting/internal.js +36 -0
  13. package/dist/error-reporting/internal.js.map +1 -0
  14. package/dist/events/index.d.ts +9 -4
  15. package/dist/events/index.d.ts.map +1 -1
  16. package/dist/events/index.js +5 -3
  17. package/dist/events/index.js.map +1 -1
  18. package/dist/idempotency/index.d.ts +25 -2
  19. package/dist/idempotency/index.d.ts.map +1 -1
  20. package/dist/idempotency/index.js +35 -5
  21. package/dist/idempotency/index.js.map +1 -1
  22. package/dist/jobs/index.d.ts +26 -4
  23. package/dist/jobs/index.d.ts.map +1 -1
  24. package/dist/jobs/index.js +51 -6
  25. package/dist/jobs/index.js.map +1 -1
  26. package/dist/locks/index.d.ts +19 -1
  27. package/dist/locks/index.d.ts.map +1 -1
  28. package/dist/locks/index.js +27 -12
  29. package/dist/locks/index.js.map +1 -1
  30. package/dist/outbox/index.d.ts +23 -4
  31. package/dist/outbox/index.d.ts.map +1 -1
  32. package/dist/outbox/index.js +71 -23
  33. package/dist/outbox/index.js.map +1 -1
  34. package/dist/ports/events.d.ts +5 -5
  35. package/dist/ports/events.d.ts.map +1 -1
  36. package/dist/ports/index.d.ts +4 -4
  37. package/dist/ports/unbound.js +1 -1
  38. package/dist/ports/unbound.js.map +1 -1
  39. package/dist/ports/unit-of-work.d.ts +4 -1
  40. package/dist/ports/unit-of-work.d.ts.map +1 -1
  41. package/dist/ports/unit-of-work.js +4 -3
  42. package/dist/ports/unit-of-work.js.map +1 -1
  43. package/dist/query-codec.d.ts +3 -0
  44. package/dist/query-codec.d.ts.map +1 -0
  45. package/dist/query-codec.js +110 -0
  46. package/dist/query-codec.js.map +1 -0
  47. package/dist/server/hooks/cors.d.ts +3 -2
  48. package/dist/server/hooks/cors.d.ts.map +1 -1
  49. package/dist/server/hooks/cors.js +7 -3
  50. package/dist/server/hooks/cors.js.map +1 -1
  51. package/dist/server/hooks/error-reporting.d.ts +8 -0
  52. package/dist/server/hooks/error-reporting.d.ts.map +1 -1
  53. package/dist/server/hooks/error-reporting.js +55 -25
  54. package/dist/server/hooks/error-reporting.js.map +1 -1
  55. package/dist/server/hooks/idempotency.d.ts +1 -1
  56. package/dist/server/hooks/idempotency.d.ts.map +1 -1
  57. package/dist/server/hooks/idempotency.js +30 -9
  58. package/dist/server/hooks/idempotency.js.map +1 -1
  59. package/dist/server/request-executor.d.ts.map +1 -1
  60. package/dist/server/request-executor.js +4 -10
  61. package/dist/server/request-executor.js.map +1 -1
  62. package/dist/server/request-preparation.d.ts.map +1 -1
  63. package/dist/server/request-preparation.js +3 -1
  64. package/dist/server/request-preparation.js.map +1 -1
  65. package/dist/server/response-finalization.d.ts +3 -1
  66. package/dist/server/response-finalization.d.ts.map +1 -1
  67. package/dist/server/response-finalization.js +24 -2
  68. package/dist/server/response-finalization.js.map +1 -1
  69. package/dist/server/server.js +1 -1
  70. package/dist/server/server.js.map +1 -1
  71. package/dist/testing/index.d.ts +5 -5
  72. package/dist/testing/index.d.ts.map +1 -1
  73. package/dist/testing/index.js +9 -4
  74. package/dist/testing/index.js.map +1 -1
  75. package/dist/tracing/index.d.ts +27 -0
  76. package/dist/tracing/index.d.ts.map +1 -1
  77. package/dist/tracing/index.js +96 -0
  78. package/dist/tracing/index.js.map +1 -1
  79. package/dist/uploads/index.d.ts.map +1 -1
  80. package/dist/uploads/index.js +39 -6
  81. package/dist/uploads/index.js.map +1 -1
  82. package/dist/webhooks/index.d.ts +2 -1
  83. package/dist/webhooks/index.d.ts.map +1 -1
  84. package/dist/webhooks/index.js +25 -1
  85. package/dist/webhooks/index.js.map +1 -1
  86. package/package.json +1 -1
  87. package/skills/app-architecture/SKILL.md +19 -1
  88. package/src/client/client.ts +6 -31
  89. package/src/error-reporting/index.ts +111 -0
  90. package/src/error-reporting/internal.ts +47 -0
  91. package/src/events/index.ts +20 -4
  92. package/src/idempotency/index.ts +81 -7
  93. package/src/jobs/index.ts +85 -6
  94. package/src/locks/index.ts +65 -16
  95. package/src/outbox/index.ts +119 -27
  96. package/src/ports/events.ts +8 -1
  97. package/src/ports/index.ts +4 -4
  98. package/src/ports/unbound.ts +1 -1
  99. package/src/ports/unit-of-work.ts +10 -3
  100. package/src/query-codec.ts +130 -0
  101. package/src/server/hooks/cors.ts +10 -3
  102. package/src/server/hooks/error-reporting.ts +79 -23
  103. package/src/server/hooks/idempotency.ts +36 -10
  104. package/src/server/request-executor.ts +4 -12
  105. package/src/server/request-preparation.ts +4 -2
  106. package/src/server/response-finalization.ts +36 -5
  107. package/src/server/server.ts +1 -1
  108. package/src/testing/index.ts +25 -14
  109. package/src/tracing/index.ts +120 -0
  110. package/src/uploads/index.ts +44 -6
  111. package/src/webhooks/index.ts +34 -2
@@ -1036,6 +1036,7 @@ export function createUploadRouter<Ctx>(
1036
1036
  assertFiles(definition, intents, { requireChecksum: false });
1037
1037
 
1038
1038
  const completed: CompletedUploadFile[] = [];
1039
+ const storedKeys: string[] = [];
1039
1040
  for (const [index, file] of webFiles.entries()) {
1040
1041
  const intent = intents[index];
1041
1042
  if (!intent) continue;
@@ -1072,18 +1073,24 @@ export function createUploadRouter<Ctx>(
1072
1073
  metadata: storageMetadata,
1073
1074
  visibility: definition.file.visibility ?? "private",
1074
1075
  });
1076
+ storedKeys.push(key);
1075
1077
  const completedFile = {
1076
1078
  ...verifiedIntent,
1077
1079
  uploadId,
1078
1080
  key,
1079
1081
  object,
1080
1082
  };
1081
- await assertVerifiedFile(definition, {
1082
- ctx,
1083
- metadata,
1084
- file: completedFile,
1085
- storage: options.storage,
1086
- });
1083
+ try {
1084
+ await assertVerifiedFile(definition, {
1085
+ ctx,
1086
+ metadata,
1087
+ file: completedFile,
1088
+ storage: options.storage,
1089
+ });
1090
+ } catch (error) {
1091
+ await cleanupRejectedServerUpload(uploadName, storedKeys);
1092
+ throw error;
1093
+ }
1087
1094
  completed.push(completedFile);
1088
1095
  }
1089
1096
 
@@ -1114,6 +1121,37 @@ export function createUploadRouter<Ctx>(
1114
1121
  }
1115
1122
  }
1116
1123
 
1124
+ async function cleanupRejectedServerUpload(
1125
+ uploadName: string,
1126
+ keys: readonly string[],
1127
+ ): Promise<void> {
1128
+ const failures: Array<{ key: string; error: string }> = [];
1129
+
1130
+ await Promise.all(
1131
+ keys.map(async (key) => {
1132
+ try {
1133
+ await options.storage.delete(key);
1134
+ } catch (error) {
1135
+ failures.push({
1136
+ key,
1137
+ error: error instanceof Error ? error.message : String(error),
1138
+ });
1139
+ }
1140
+ }),
1141
+ );
1142
+
1143
+ if (failures.length === 0) return;
1144
+ instrumentation.custom({
1145
+ name: "upload.server.cleanup.failed",
1146
+ label: "Rejected upload cleanup failed",
1147
+ summary: uploadName,
1148
+ details: {
1149
+ uploadName,
1150
+ failures,
1151
+ },
1152
+ });
1153
+ }
1154
+
1117
1155
  function recordFailure(
1118
1156
  name: string,
1119
1157
  uploadName: string,
@@ -133,7 +133,8 @@ export type HmacWebhookTimestampFormat =
133
133
  export type HmacWebhookTimestampOptions =
134
134
  | {
135
135
  /**
136
- * Header that carries the provider event timestamp.
136
+ * Header that carries the provider event timestamp. Header timestamps
137
+ * are authenticated as `<timestamp>.<rawBody>`.
137
138
  */
138
139
  header: string;
139
140
  payloadPath?: never;
@@ -461,7 +462,12 @@ export function createHmacWebhookVerifier(
461
462
  });
462
463
  }
463
464
 
464
- const expected = await hmacHex(algorithm, options.secret, input.rawBody);
465
+ const signedBody = hmacSignedBody({
466
+ input,
467
+ provider: options.provider,
468
+ timestamp,
469
+ });
470
+ const expected = await hmacHex(algorithm, options.secret, signedBody);
465
471
  const actual = normalizeSignature(signature, options.signaturePrefix);
466
472
  if (!(await timingSafeStringEqual(actual, expected))) {
467
473
  throw new WebhookVerificationError({
@@ -573,6 +579,32 @@ function rawBodyText(rawBody: WebhookRawBody): string {
573
579
  return new TextDecoder().decode(rawBodyBytes(rawBody));
574
580
  }
575
581
 
582
+ function hmacSignedBody(args: {
583
+ input: VerifyWebhookInput;
584
+ provider: string | undefined;
585
+ timestamp: NormalizedHmacTimestampOptions | undefined;
586
+ }): WebhookRawBody {
587
+ if (!args.timestamp || args.timestamp.source === "payload") {
588
+ return args.input.rawBody;
589
+ }
590
+
591
+ const value = args.input.headers?.[args.timestamp.key];
592
+ if (value === undefined || value === null || value === "") {
593
+ throw new WebhookVerificationError({
594
+ message: `Missing ${args.timestamp.key} header.`,
595
+ provider: args.provider,
596
+ code: "missing_timestamp",
597
+ });
598
+ }
599
+
600
+ const prefix = new TextEncoder().encode(`${value}.`);
601
+ const body = rawBodyBytes(args.input.rawBody);
602
+ const signed = new Uint8Array(prefix.byteLength + body.byteLength);
603
+ signed.set(prefix);
604
+ signed.set(body, prefix.byteLength);
605
+ return signed;
606
+ }
607
+
576
608
  function parseJsonBody(rawBody: WebhookRawBody): unknown {
577
609
  try {
578
610
  return JSON.parse(rawBodyText(rawBody));