@01.software/sdk 0.1.4 → 0.1.5

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.
@@ -139,9 +139,7 @@ interface Config {
139
139
  globals: {};
140
140
  globalsSelect: {};
141
141
  locale: null;
142
- user: User & {
143
- collection: 'users';
144
- };
142
+ user: User;
145
143
  jobs: {
146
144
  tasks: unknown;
147
145
  workflows: unknown;
@@ -232,6 +230,7 @@ interface User {
232
230
  expiresAt: string;
233
231
  }[] | null;
234
232
  password?: string | null;
233
+ collection: 'users';
235
234
  }
236
235
  /**
237
236
  * This interface was referenced by `Config`'s JSON-Schema
@@ -591,6 +590,16 @@ interface TenantOgImage {
591
590
  height?: number | null;
592
591
  focalX?: number | null;
593
592
  focalY?: number | null;
593
+ sizes?: {
594
+ '1200'?: {
595
+ url?: string | null;
596
+ width?: number | null;
597
+ height?: number | null;
598
+ mimeType?: string | null;
599
+ filesize?: number | null;
600
+ filename?: string | null;
601
+ };
602
+ };
594
603
  }
595
604
  /**
596
605
  * This interface was referenced by `Config`'s JSON-Schema
@@ -621,6 +630,64 @@ interface TenantLogo {
621
630
  height?: number | null;
622
631
  focalX?: number | null;
623
632
  focalY?: number | null;
633
+ sizes?: {
634
+ '16'?: {
635
+ url?: string | null;
636
+ width?: number | null;
637
+ height?: number | null;
638
+ mimeType?: string | null;
639
+ filesize?: number | null;
640
+ filename?: string | null;
641
+ };
642
+ '32'?: {
643
+ url?: string | null;
644
+ width?: number | null;
645
+ height?: number | null;
646
+ mimeType?: string | null;
647
+ filesize?: number | null;
648
+ filename?: string | null;
649
+ };
650
+ '64'?: {
651
+ url?: string | null;
652
+ width?: number | null;
653
+ height?: number | null;
654
+ mimeType?: string | null;
655
+ filesize?: number | null;
656
+ filename?: string | null;
657
+ };
658
+ '128'?: {
659
+ url?: string | null;
660
+ width?: number | null;
661
+ height?: number | null;
662
+ mimeType?: string | null;
663
+ filesize?: number | null;
664
+ filename?: string | null;
665
+ };
666
+ '180'?: {
667
+ url?: string | null;
668
+ width?: number | null;
669
+ height?: number | null;
670
+ mimeType?: string | null;
671
+ filesize?: number | null;
672
+ filename?: string | null;
673
+ };
674
+ '192'?: {
675
+ url?: string | null;
676
+ width?: number | null;
677
+ height?: number | null;
678
+ mimeType?: string | null;
679
+ filesize?: number | null;
680
+ filename?: string | null;
681
+ };
682
+ '512'?: {
683
+ url?: string | null;
684
+ width?: number | null;
685
+ height?: number | null;
686
+ mimeType?: string | null;
687
+ filesize?: number | null;
688
+ filename?: string | null;
689
+ };
690
+ };
624
691
  }
625
692
  /**
626
693
  * This interface was referenced by `Config`'s JSON-Schema
@@ -898,6 +965,7 @@ interface ProductCategory {
898
965
  description?: string | null;
899
966
  image?: (number | null) | ProductImage;
900
967
  parent?: (number | null) | ProductCategory;
968
+ color?: string | null;
901
969
  updatedAt: string;
902
970
  createdAt: string;
903
971
  }
@@ -917,6 +985,8 @@ interface ProductTag {
917
985
  slug?: string | null;
918
986
  description?: string | null;
919
987
  image?: (number | null) | ProductImage;
988
+ parent?: (number | null) | ProductTag;
989
+ color?: string | null;
920
990
  updatedAt: string;
921
991
  createdAt: string;
922
992
  }
@@ -973,6 +1043,7 @@ interface ProductVariant {
973
1043
  * Override price (uses product price if empty)
974
1044
  */
975
1045
  price?: number | null;
1046
+ isSoldOut?: boolean | null;
976
1047
  images?: (number | ProductImage)[] | null;
977
1048
  productOptions?: {
978
1049
  docs?: (number | ProductOption)[];
@@ -1268,6 +1339,7 @@ interface PostCategory {
1268
1339
  description?: string | null;
1269
1340
  image?: (number | null) | PostImage;
1270
1341
  parent?: (number | null) | PostCategory;
1342
+ color?: string | null;
1271
1343
  updatedAt: string;
1272
1344
  createdAt: string;
1273
1345
  }
@@ -1287,6 +1359,8 @@ interface PostTag {
1287
1359
  slug?: string | null;
1288
1360
  description?: string | null;
1289
1361
  image?: (number | null) | PostImage;
1362
+ parent?: (number | null) | PostTag;
1363
+ color?: string | null;
1290
1364
  updatedAt: string;
1291
1365
  createdAt: string;
1292
1366
  }
@@ -1369,6 +1443,8 @@ interface DocumentCategory {
1369
1443
  slug?: string | null;
1370
1444
  description?: string | null;
1371
1445
  image?: (number | null) | DocumentImage;
1446
+ parent?: (number | null) | DocumentCategory;
1447
+ color?: string | null;
1372
1448
  updatedAt: string;
1373
1449
  createdAt: string;
1374
1450
  }
@@ -2126,6 +2202,64 @@ interface TenantLogosSelect<T extends boolean = true> {
2126
2202
  height?: T;
2127
2203
  focalX?: T;
2128
2204
  focalY?: T;
2205
+ sizes?: T | {
2206
+ '16'?: T | {
2207
+ url?: T;
2208
+ width?: T;
2209
+ height?: T;
2210
+ mimeType?: T;
2211
+ filesize?: T;
2212
+ filename?: T;
2213
+ };
2214
+ '32'?: T | {
2215
+ url?: T;
2216
+ width?: T;
2217
+ height?: T;
2218
+ mimeType?: T;
2219
+ filesize?: T;
2220
+ filename?: T;
2221
+ };
2222
+ '64'?: T | {
2223
+ url?: T;
2224
+ width?: T;
2225
+ height?: T;
2226
+ mimeType?: T;
2227
+ filesize?: T;
2228
+ filename?: T;
2229
+ };
2230
+ '128'?: T | {
2231
+ url?: T;
2232
+ width?: T;
2233
+ height?: T;
2234
+ mimeType?: T;
2235
+ filesize?: T;
2236
+ filename?: T;
2237
+ };
2238
+ '180'?: T | {
2239
+ url?: T;
2240
+ width?: T;
2241
+ height?: T;
2242
+ mimeType?: T;
2243
+ filesize?: T;
2244
+ filename?: T;
2245
+ };
2246
+ '192'?: T | {
2247
+ url?: T;
2248
+ width?: T;
2249
+ height?: T;
2250
+ mimeType?: T;
2251
+ filesize?: T;
2252
+ filename?: T;
2253
+ };
2254
+ '512'?: T | {
2255
+ url?: T;
2256
+ width?: T;
2257
+ height?: T;
2258
+ mimeType?: T;
2259
+ filesize?: T;
2260
+ filename?: T;
2261
+ };
2262
+ };
2129
2263
  }
2130
2264
  /**
2131
2265
  * This interface was referenced by `Config`'s JSON-Schema
@@ -2155,6 +2289,16 @@ interface TenantOgImagesSelect<T extends boolean = true> {
2155
2289
  height?: T;
2156
2290
  focalX?: T;
2157
2291
  focalY?: T;
2292
+ sizes?: T | {
2293
+ '1200'?: T | {
2294
+ url?: T;
2295
+ width?: T;
2296
+ height?: T;
2297
+ mimeType?: T;
2298
+ filesize?: T;
2299
+ filename?: T;
2300
+ };
2301
+ };
2158
2302
  }
2159
2303
  /**
2160
2304
  * This interface was referenced by `Config`'s JSON-Schema
@@ -2277,6 +2421,7 @@ interface ProductVariantsSelect<T extends boolean = true> {
2277
2421
  title?: T;
2278
2422
  sku?: T;
2279
2423
  price?: T;
2424
+ isSoldOut?: T;
2280
2425
  images?: T;
2281
2426
  productOptions?: T;
2282
2427
  updatedAt?: T;
@@ -2313,6 +2458,7 @@ interface ProductCategoriesSelect<T extends boolean = true> {
2313
2458
  description?: T;
2314
2459
  image?: T;
2315
2460
  parent?: T;
2461
+ color?: T;
2316
2462
  updatedAt?: T;
2317
2463
  createdAt?: T;
2318
2464
  }
@@ -2328,6 +2474,8 @@ interface ProductTagsSelect<T extends boolean = true> {
2328
2474
  slug?: T;
2329
2475
  description?: T;
2330
2476
  image?: T;
2477
+ parent?: T;
2478
+ color?: T;
2331
2479
  updatedAt?: T;
2332
2480
  createdAt?: T;
2333
2481
  }
@@ -2614,6 +2762,7 @@ interface PostCategoriesSelect<T extends boolean = true> {
2614
2762
  description?: T;
2615
2763
  image?: T;
2616
2764
  parent?: T;
2765
+ color?: T;
2617
2766
  updatedAt?: T;
2618
2767
  createdAt?: T;
2619
2768
  }
@@ -2629,6 +2778,8 @@ interface PostTagsSelect<T extends boolean = true> {
2629
2778
  slug?: T;
2630
2779
  description?: T;
2631
2780
  image?: T;
2781
+ parent?: T;
2782
+ color?: T;
2632
2783
  updatedAt?: T;
2633
2784
  createdAt?: T;
2634
2785
  }
@@ -2734,6 +2885,8 @@ interface DocumentCategoriesSelect<T extends boolean = true> {
2734
2885
  slug?: T;
2735
2886
  description?: T;
2736
2887
  image?: T;
2888
+ parent?: T;
2889
+ color?: T;
2737
2890
  updatedAt?: T;
2738
2891
  createdAt?: T;
2739
2892
  }
@@ -139,9 +139,7 @@ interface Config {
139
139
  globals: {};
140
140
  globalsSelect: {};
141
141
  locale: null;
142
- user: User & {
143
- collection: 'users';
144
- };
142
+ user: User;
145
143
  jobs: {
146
144
  tasks: unknown;
147
145
  workflows: unknown;
@@ -232,6 +230,7 @@ interface User {
232
230
  expiresAt: string;
233
231
  }[] | null;
234
232
  password?: string | null;
233
+ collection: 'users';
235
234
  }
236
235
  /**
237
236
  * This interface was referenced by `Config`'s JSON-Schema
@@ -591,6 +590,16 @@ interface TenantOgImage {
591
590
  height?: number | null;
592
591
  focalX?: number | null;
593
592
  focalY?: number | null;
593
+ sizes?: {
594
+ '1200'?: {
595
+ url?: string | null;
596
+ width?: number | null;
597
+ height?: number | null;
598
+ mimeType?: string | null;
599
+ filesize?: number | null;
600
+ filename?: string | null;
601
+ };
602
+ };
594
603
  }
595
604
  /**
596
605
  * This interface was referenced by `Config`'s JSON-Schema
@@ -621,6 +630,64 @@ interface TenantLogo {
621
630
  height?: number | null;
622
631
  focalX?: number | null;
623
632
  focalY?: number | null;
633
+ sizes?: {
634
+ '16'?: {
635
+ url?: string | null;
636
+ width?: number | null;
637
+ height?: number | null;
638
+ mimeType?: string | null;
639
+ filesize?: number | null;
640
+ filename?: string | null;
641
+ };
642
+ '32'?: {
643
+ url?: string | null;
644
+ width?: number | null;
645
+ height?: number | null;
646
+ mimeType?: string | null;
647
+ filesize?: number | null;
648
+ filename?: string | null;
649
+ };
650
+ '64'?: {
651
+ url?: string | null;
652
+ width?: number | null;
653
+ height?: number | null;
654
+ mimeType?: string | null;
655
+ filesize?: number | null;
656
+ filename?: string | null;
657
+ };
658
+ '128'?: {
659
+ url?: string | null;
660
+ width?: number | null;
661
+ height?: number | null;
662
+ mimeType?: string | null;
663
+ filesize?: number | null;
664
+ filename?: string | null;
665
+ };
666
+ '180'?: {
667
+ url?: string | null;
668
+ width?: number | null;
669
+ height?: number | null;
670
+ mimeType?: string | null;
671
+ filesize?: number | null;
672
+ filename?: string | null;
673
+ };
674
+ '192'?: {
675
+ url?: string | null;
676
+ width?: number | null;
677
+ height?: number | null;
678
+ mimeType?: string | null;
679
+ filesize?: number | null;
680
+ filename?: string | null;
681
+ };
682
+ '512'?: {
683
+ url?: string | null;
684
+ width?: number | null;
685
+ height?: number | null;
686
+ mimeType?: string | null;
687
+ filesize?: number | null;
688
+ filename?: string | null;
689
+ };
690
+ };
624
691
  }
625
692
  /**
626
693
  * This interface was referenced by `Config`'s JSON-Schema
@@ -898,6 +965,7 @@ interface ProductCategory {
898
965
  description?: string | null;
899
966
  image?: (number | null) | ProductImage;
900
967
  parent?: (number | null) | ProductCategory;
968
+ color?: string | null;
901
969
  updatedAt: string;
902
970
  createdAt: string;
903
971
  }
@@ -917,6 +985,8 @@ interface ProductTag {
917
985
  slug?: string | null;
918
986
  description?: string | null;
919
987
  image?: (number | null) | ProductImage;
988
+ parent?: (number | null) | ProductTag;
989
+ color?: string | null;
920
990
  updatedAt: string;
921
991
  createdAt: string;
922
992
  }
@@ -973,6 +1043,7 @@ interface ProductVariant {
973
1043
  * Override price (uses product price if empty)
974
1044
  */
975
1045
  price?: number | null;
1046
+ isSoldOut?: boolean | null;
976
1047
  images?: (number | ProductImage)[] | null;
977
1048
  productOptions?: {
978
1049
  docs?: (number | ProductOption)[];
@@ -1268,6 +1339,7 @@ interface PostCategory {
1268
1339
  description?: string | null;
1269
1340
  image?: (number | null) | PostImage;
1270
1341
  parent?: (number | null) | PostCategory;
1342
+ color?: string | null;
1271
1343
  updatedAt: string;
1272
1344
  createdAt: string;
1273
1345
  }
@@ -1287,6 +1359,8 @@ interface PostTag {
1287
1359
  slug?: string | null;
1288
1360
  description?: string | null;
1289
1361
  image?: (number | null) | PostImage;
1362
+ parent?: (number | null) | PostTag;
1363
+ color?: string | null;
1290
1364
  updatedAt: string;
1291
1365
  createdAt: string;
1292
1366
  }
@@ -1369,6 +1443,8 @@ interface DocumentCategory {
1369
1443
  slug?: string | null;
1370
1444
  description?: string | null;
1371
1445
  image?: (number | null) | DocumentImage;
1446
+ parent?: (number | null) | DocumentCategory;
1447
+ color?: string | null;
1372
1448
  updatedAt: string;
1373
1449
  createdAt: string;
1374
1450
  }
@@ -2126,6 +2202,64 @@ interface TenantLogosSelect<T extends boolean = true> {
2126
2202
  height?: T;
2127
2203
  focalX?: T;
2128
2204
  focalY?: T;
2205
+ sizes?: T | {
2206
+ '16'?: T | {
2207
+ url?: T;
2208
+ width?: T;
2209
+ height?: T;
2210
+ mimeType?: T;
2211
+ filesize?: T;
2212
+ filename?: T;
2213
+ };
2214
+ '32'?: T | {
2215
+ url?: T;
2216
+ width?: T;
2217
+ height?: T;
2218
+ mimeType?: T;
2219
+ filesize?: T;
2220
+ filename?: T;
2221
+ };
2222
+ '64'?: T | {
2223
+ url?: T;
2224
+ width?: T;
2225
+ height?: T;
2226
+ mimeType?: T;
2227
+ filesize?: T;
2228
+ filename?: T;
2229
+ };
2230
+ '128'?: T | {
2231
+ url?: T;
2232
+ width?: T;
2233
+ height?: T;
2234
+ mimeType?: T;
2235
+ filesize?: T;
2236
+ filename?: T;
2237
+ };
2238
+ '180'?: T | {
2239
+ url?: T;
2240
+ width?: T;
2241
+ height?: T;
2242
+ mimeType?: T;
2243
+ filesize?: T;
2244
+ filename?: T;
2245
+ };
2246
+ '192'?: T | {
2247
+ url?: T;
2248
+ width?: T;
2249
+ height?: T;
2250
+ mimeType?: T;
2251
+ filesize?: T;
2252
+ filename?: T;
2253
+ };
2254
+ '512'?: T | {
2255
+ url?: T;
2256
+ width?: T;
2257
+ height?: T;
2258
+ mimeType?: T;
2259
+ filesize?: T;
2260
+ filename?: T;
2261
+ };
2262
+ };
2129
2263
  }
2130
2264
  /**
2131
2265
  * This interface was referenced by `Config`'s JSON-Schema
@@ -2155,6 +2289,16 @@ interface TenantOgImagesSelect<T extends boolean = true> {
2155
2289
  height?: T;
2156
2290
  focalX?: T;
2157
2291
  focalY?: T;
2292
+ sizes?: T | {
2293
+ '1200'?: T | {
2294
+ url?: T;
2295
+ width?: T;
2296
+ height?: T;
2297
+ mimeType?: T;
2298
+ filesize?: T;
2299
+ filename?: T;
2300
+ };
2301
+ };
2158
2302
  }
2159
2303
  /**
2160
2304
  * This interface was referenced by `Config`'s JSON-Schema
@@ -2277,6 +2421,7 @@ interface ProductVariantsSelect<T extends boolean = true> {
2277
2421
  title?: T;
2278
2422
  sku?: T;
2279
2423
  price?: T;
2424
+ isSoldOut?: T;
2280
2425
  images?: T;
2281
2426
  productOptions?: T;
2282
2427
  updatedAt?: T;
@@ -2313,6 +2458,7 @@ interface ProductCategoriesSelect<T extends boolean = true> {
2313
2458
  description?: T;
2314
2459
  image?: T;
2315
2460
  parent?: T;
2461
+ color?: T;
2316
2462
  updatedAt?: T;
2317
2463
  createdAt?: T;
2318
2464
  }
@@ -2328,6 +2474,8 @@ interface ProductTagsSelect<T extends boolean = true> {
2328
2474
  slug?: T;
2329
2475
  description?: T;
2330
2476
  image?: T;
2477
+ parent?: T;
2478
+ color?: T;
2331
2479
  updatedAt?: T;
2332
2480
  createdAt?: T;
2333
2481
  }
@@ -2614,6 +2762,7 @@ interface PostCategoriesSelect<T extends boolean = true> {
2614
2762
  description?: T;
2615
2763
  image?: T;
2616
2764
  parent?: T;
2765
+ color?: T;
2617
2766
  updatedAt?: T;
2618
2767
  createdAt?: T;
2619
2768
  }
@@ -2629,6 +2778,8 @@ interface PostTagsSelect<T extends boolean = true> {
2629
2778
  slug?: T;
2630
2779
  description?: T;
2631
2780
  image?: T;
2781
+ parent?: T;
2782
+ color?: T;
2632
2783
  updatedAt?: T;
2633
2784
  createdAt?: T;
2634
2785
  }
@@ -2734,6 +2885,8 @@ interface DocumentCategoriesSelect<T extends boolean = true> {
2734
2885
  slug?: T;
2735
2886
  description?: T;
2736
2887
  image?: T;
2888
+ parent?: T;
2889
+ color?: T;
2737
2890
  updatedAt?: T;
2738
2891
  createdAt?: T;
2739
2892
  }
@@ -1,4 +1,4 @@
1
- import { C as Config } from './payload-types-BjyFdz5j.js';
1
+ import { C as Config } from './payload-types-BAZCcssT.js';
2
2
 
3
3
  /**
4
4
  * Collection type derived from Payload Config.
@@ -1,4 +1,4 @@
1
- import { C as Config } from './payload-types-BjyFdz5j.cjs';
1
+ import { C as Config } from './payload-types-BAZCcssT.cjs';
2
2
 
3
3
  /**
4
4
  * Collection type derived from Payload Config.
package/dist/webhook.cjs CHANGED
@@ -64,10 +64,10 @@ function verifySignature(payload, secret, signature) {
64
64
  );
65
65
  const sig = yield crypto.subtle.sign("HMAC", key, encoder.encode(payload));
66
66
  const expected = Array.from(new Uint8Array(sig)).map((b) => b.toString(16).padStart(2, "0")).join("");
67
- if (expected.length !== signature.length) return false;
68
- let result = 0;
69
- for (let i = 0; i < expected.length; i++) {
70
- result |= expected.charCodeAt(i) ^ signature.charCodeAt(i);
67
+ let result = expected.length !== signature.length ? 1 : 0;
68
+ const len = Math.max(expected.length, signature.length);
69
+ for (let i = 0; i < len; i++) {
70
+ result |= (expected.charCodeAt(i) || 0) ^ (signature.charCodeAt(i) || 0);
71
71
  }
72
72
  return result === 0;
73
73
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/webhook.ts","../src/core/webhook/index.tsx"],"sourcesContent":["export {\n handleWebhook,\n createTypedWebhookHandler,\n isValidWebhookEvent,\n} from './core/webhook'\nexport type {\n WebhookEvent,\n WebhookHandler,\n WebhookOperation,\n WebhookOptions,\n} from './core/webhook'\n","import type { Collection } from '../client/types'\nimport type { CollectionType } from '../collection/types'\n\nexport type WebhookOperation = 'create' | 'update'\n\nexport interface WebhookEvent<T extends Collection = Collection> {\n collection: T\n operation: WebhookOperation\n data: CollectionType<T>\n}\n\nexport type WebhookHandler<T extends Collection = Collection> = (\n event: WebhookEvent<T>,\n) => Promise<void> | void\n\nexport interface WebhookOptions {\n secret?: string\n}\n\nexport function isValidWebhookEvent(data: unknown): data is WebhookEvent {\n if (typeof data !== 'object' || data === null) return false\n const obj = data as Record<string, unknown>\n return (\n typeof obj.collection === 'string' &&\n (obj.operation === 'create' || obj.operation === 'update') &&\n typeof obj.data === 'object' &&\n obj.data !== null\n )\n}\n\nasync function verifySignature(\n payload: string,\n secret: string,\n signature: string,\n): Promise<boolean> {\n const encoder = new TextEncoder()\n const key = await crypto.subtle.importKey(\n 'raw',\n encoder.encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign'],\n )\n const sig = await crypto.subtle.sign('HMAC', key, encoder.encode(payload))\n const expected = Array.from(new Uint8Array(sig))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('')\n // Constant-time comparison to prevent timing attacks\n if (expected.length !== signature.length) return false\n let result = 0\n for (let i = 0; i < expected.length; i++) {\n result |= expected.charCodeAt(i) ^ signature.charCodeAt(i)\n }\n return result === 0\n}\n\nexport async function handleWebhook<T extends Collection = Collection>(\n request: Request,\n handler: WebhookHandler<T>,\n options?: WebhookOptions,\n): Promise<Response> {\n try {\n const rawBody = await request.text()\n\n if (options?.secret) {\n const signature = request.headers.get('x-webhook-signature') || ''\n const valid = await verifySignature(rawBody, options.secret, signature)\n if (!valid) {\n return new Response(\n JSON.stringify({ error: 'Invalid webhook signature' }),\n { status: 401, headers: { 'Content-Type': 'application/json' } },\n )\n }\n }\n\n const body = JSON.parse(rawBody)\n\n if (!isValidWebhookEvent(body)) {\n return new Response(\n JSON.stringify({ error: 'Invalid webhook event format' }),\n { status: 400, headers: { 'Content-Type': 'application/json' } },\n )\n }\n\n await handler(body as WebhookEvent<T>)\n\n return new Response(\n JSON.stringify({ success: true, message: 'Webhook processed' }),\n { status: 200, headers: { 'Content-Type': 'application/json' } },\n )\n } catch (error) {\n console.error('Webhook processing error:', error)\n\n return new Response(\n JSON.stringify({\n error: 'Internal server error',\n message: error instanceof Error ? error.message : 'Unknown error',\n }),\n { status: 500, headers: { 'Content-Type': 'application/json' } },\n )\n }\n}\n\nexport function createTypedWebhookHandler<T extends Collection>(\n collection: T,\n handler: (event: WebhookEvent<T>) => Promise<void> | void,\n): WebhookHandler<T> {\n return async (event: WebhookEvent<T>) => {\n if (event.collection !== collection) {\n throw new Error(`Expected collection \"${collection}\", got \"${event.collection}\"`)\n }\n return handler(event)\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACmBO,SAAS,oBAAoB,MAAqC;AACvE,MAAI,OAAO,SAAS,YAAY,SAAS,KAAM,QAAO;AACtD,QAAM,MAAM;AACZ,SACE,OAAO,IAAI,eAAe,aACzB,IAAI,cAAc,YAAY,IAAI,cAAc,aACjD,OAAO,IAAI,SAAS,YACpB,IAAI,SAAS;AAEjB;AAEA,SAAe,gBACb,SACA,QACA,WACkB;AAAA;AAClB,UAAM,UAAU,IAAI,YAAY;AAChC,UAAM,MAAM,MAAM,OAAO,OAAO;AAAA,MAC9B;AAAA,MACA,QAAQ,OAAO,MAAM;AAAA,MACrB,EAAE,MAAM,QAAQ,MAAM,UAAU;AAAA,MAChC;AAAA,MACA,CAAC,MAAM;AAAA,IACT;AACA,UAAM,MAAM,MAAM,OAAO,OAAO,KAAK,QAAQ,KAAK,QAAQ,OAAO,OAAO,CAAC;AACzE,UAAM,WAAW,MAAM,KAAK,IAAI,WAAW,GAAG,CAAC,EAC5C,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAC1C,KAAK,EAAE;AAEV,QAAI,SAAS,WAAW,UAAU,OAAQ,QAAO;AACjD,QAAI,SAAS;AACb,aAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,gBAAU,SAAS,WAAW,CAAC,IAAI,UAAU,WAAW,CAAC;AAAA,IAC3D;AACA,WAAO,WAAW;AAAA,EACpB;AAAA;AAEA,SAAsB,cACpB,SACA,SACA,SACmB;AAAA;AACnB,QAAI;AACF,YAAM,UAAU,MAAM,QAAQ,KAAK;AAEnC,UAAI,mCAAS,QAAQ;AACnB,cAAM,YAAY,QAAQ,QAAQ,IAAI,qBAAqB,KAAK;AAChE,cAAM,QAAQ,MAAM,gBAAgB,SAAS,QAAQ,QAAQ,SAAS;AACtE,YAAI,CAAC,OAAO;AACV,iBAAO,IAAI;AAAA,YACT,KAAK,UAAU,EAAE,OAAO,4BAA4B,CAAC;AAAA,YACrD,EAAE,QAAQ,KAAK,SAAS,EAAE,gBAAgB,mBAAmB,EAAE;AAAA,UACjE;AAAA,QACF;AAAA,MACF;AAEA,YAAM,OAAO,KAAK,MAAM,OAAO;AAE/B,UAAI,CAAC,oBAAoB,IAAI,GAAG;AAC9B,eAAO,IAAI;AAAA,UACT,KAAK,UAAU,EAAE,OAAO,+BAA+B,CAAC;AAAA,UACxD,EAAE,QAAQ,KAAK,SAAS,EAAE,gBAAgB,mBAAmB,EAAE;AAAA,QACjE;AAAA,MACF;AAEA,YAAM,QAAQ,IAAuB;AAErC,aAAO,IAAI;AAAA,QACT,KAAK,UAAU,EAAE,SAAS,MAAM,SAAS,oBAAoB,CAAC;AAAA,QAC9D,EAAE,QAAQ,KAAK,SAAS,EAAE,gBAAgB,mBAAmB,EAAE;AAAA,MACjE;AAAA,IACF,SAAS,OAAO;AACd,cAAQ,MAAM,6BAA6B,KAAK;AAEhD,aAAO,IAAI;AAAA,QACT,KAAK,UAAU;AAAA,UACb,OAAO;AAAA,UACP,SAAS,iBAAiB,QAAQ,MAAM,UAAU;AAAA,QACpD,CAAC;AAAA,QACD,EAAE,QAAQ,KAAK,SAAS,EAAE,gBAAgB,mBAAmB,EAAE;AAAA,MACjE;AAAA,IACF;AAAA,EACF;AAAA;AAEO,SAAS,0BACd,YACA,SACmB;AACnB,SAAO,CAAO,UAA2B;AACvC,QAAI,MAAM,eAAe,YAAY;AACnC,YAAM,IAAI,MAAM,wBAAwB,UAAU,WAAW,MAAM,UAAU,GAAG;AAAA,IAClF;AACA,WAAO,QAAQ,KAAK;AAAA,EACtB;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/webhook.ts","../src/core/webhook/index.tsx"],"sourcesContent":["export {\n handleWebhook,\n createTypedWebhookHandler,\n isValidWebhookEvent,\n} from './core/webhook'\nexport type {\n WebhookEvent,\n WebhookHandler,\n WebhookOperation,\n WebhookOptions,\n} from './core/webhook'\n","import type { Collection } from '../client/types'\nimport type { CollectionType } from '../collection/types'\n\nexport type WebhookOperation = 'create' | 'update'\n\nexport interface WebhookEvent<T extends Collection = Collection> {\n collection: T\n operation: WebhookOperation\n data: CollectionType<T>\n}\n\nexport type WebhookHandler<T extends Collection = Collection> = (\n event: WebhookEvent<T>,\n) => Promise<void> | void\n\nexport interface WebhookOptions {\n secret?: string\n}\n\nexport function isValidWebhookEvent(data: unknown): data is WebhookEvent {\n if (typeof data !== 'object' || data === null) return false\n const obj = data as Record<string, unknown>\n return (\n typeof obj.collection === 'string' &&\n (obj.operation === 'create' || obj.operation === 'update') &&\n typeof obj.data === 'object' &&\n obj.data !== null\n )\n}\n\nasync function verifySignature(\n payload: string,\n secret: string,\n signature: string,\n): Promise<boolean> {\n const encoder = new TextEncoder()\n const key = await crypto.subtle.importKey(\n 'raw',\n encoder.encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign'],\n )\n const sig = await crypto.subtle.sign('HMAC', key, encoder.encode(payload))\n const expected = Array.from(new Uint8Array(sig))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('')\n // Constant-time comparison to prevent timing attacks\n let result = expected.length !== signature.length ? 1 : 0\n const len = Math.max(expected.length, signature.length)\n for (let i = 0; i < len; i++) {\n result |= (expected.charCodeAt(i) || 0) ^ (signature.charCodeAt(i) || 0)\n }\n return result === 0\n}\n\nexport async function handleWebhook<T extends Collection = Collection>(\n request: Request,\n handler: WebhookHandler<T>,\n options?: WebhookOptions,\n): Promise<Response> {\n try {\n const rawBody = await request.text()\n\n if (options?.secret) {\n const signature = request.headers.get('x-webhook-signature') || ''\n const valid = await verifySignature(rawBody, options.secret, signature)\n if (!valid) {\n return new Response(\n JSON.stringify({ error: 'Invalid webhook signature' }),\n { status: 401, headers: { 'Content-Type': 'application/json' } },\n )\n }\n }\n\n const body = JSON.parse(rawBody)\n\n if (!isValidWebhookEvent(body)) {\n return new Response(\n JSON.stringify({ error: 'Invalid webhook event format' }),\n { status: 400, headers: { 'Content-Type': 'application/json' } },\n )\n }\n\n await handler(body as WebhookEvent<T>)\n\n return new Response(\n JSON.stringify({ success: true, message: 'Webhook processed' }),\n { status: 200, headers: { 'Content-Type': 'application/json' } },\n )\n } catch (error) {\n console.error('Webhook processing error:', error)\n\n return new Response(\n JSON.stringify({\n error: 'Internal server error',\n message: error instanceof Error ? error.message : 'Unknown error',\n }),\n { status: 500, headers: { 'Content-Type': 'application/json' } },\n )\n }\n}\n\nexport function createTypedWebhookHandler<T extends Collection>(\n collection: T,\n handler: (event: WebhookEvent<T>) => Promise<void> | void,\n): WebhookHandler<T> {\n return async (event: WebhookEvent<T>) => {\n if (event.collection !== collection) {\n throw new Error(`Expected collection \"${collection}\", got \"${event.collection}\"`)\n }\n return handler(event)\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACmBO,SAAS,oBAAoB,MAAqC;AACvE,MAAI,OAAO,SAAS,YAAY,SAAS,KAAM,QAAO;AACtD,QAAM,MAAM;AACZ,SACE,OAAO,IAAI,eAAe,aACzB,IAAI,cAAc,YAAY,IAAI,cAAc,aACjD,OAAO,IAAI,SAAS,YACpB,IAAI,SAAS;AAEjB;AAEA,SAAe,gBACb,SACA,QACA,WACkB;AAAA;AAClB,UAAM,UAAU,IAAI,YAAY;AAChC,UAAM,MAAM,MAAM,OAAO,OAAO;AAAA,MAC9B;AAAA,MACA,QAAQ,OAAO,MAAM;AAAA,MACrB,EAAE,MAAM,QAAQ,MAAM,UAAU;AAAA,MAChC;AAAA,MACA,CAAC,MAAM;AAAA,IACT;AACA,UAAM,MAAM,MAAM,OAAO,OAAO,KAAK,QAAQ,KAAK,QAAQ,OAAO,OAAO,CAAC;AACzE,UAAM,WAAW,MAAM,KAAK,IAAI,WAAW,GAAG,CAAC,EAC5C,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAC1C,KAAK,EAAE;AAEV,QAAI,SAAS,SAAS,WAAW,UAAU,SAAS,IAAI;AACxD,UAAM,MAAM,KAAK,IAAI,SAAS,QAAQ,UAAU,MAAM;AACtD,aAAS,IAAI,GAAG,IAAI,KAAK,KAAK;AAC5B,iBAAW,SAAS,WAAW,CAAC,KAAK,MAAM,UAAU,WAAW,CAAC,KAAK;AAAA,IACxE;AACA,WAAO,WAAW;AAAA,EACpB;AAAA;AAEA,SAAsB,cACpB,SACA,SACA,SACmB;AAAA;AACnB,QAAI;AACF,YAAM,UAAU,MAAM,QAAQ,KAAK;AAEnC,UAAI,mCAAS,QAAQ;AACnB,cAAM,YAAY,QAAQ,QAAQ,IAAI,qBAAqB,KAAK;AAChE,cAAM,QAAQ,MAAM,gBAAgB,SAAS,QAAQ,QAAQ,SAAS;AACtE,YAAI,CAAC,OAAO;AACV,iBAAO,IAAI;AAAA,YACT,KAAK,UAAU,EAAE,OAAO,4BAA4B,CAAC;AAAA,YACrD,EAAE,QAAQ,KAAK,SAAS,EAAE,gBAAgB,mBAAmB,EAAE;AAAA,UACjE;AAAA,QACF;AAAA,MACF;AAEA,YAAM,OAAO,KAAK,MAAM,OAAO;AAE/B,UAAI,CAAC,oBAAoB,IAAI,GAAG;AAC9B,eAAO,IAAI;AAAA,UACT,KAAK,UAAU,EAAE,OAAO,+BAA+B,CAAC;AAAA,UACxD,EAAE,QAAQ,KAAK,SAAS,EAAE,gBAAgB,mBAAmB,EAAE;AAAA,QACjE;AAAA,MACF;AAEA,YAAM,QAAQ,IAAuB;AAErC,aAAO,IAAI;AAAA,QACT,KAAK,UAAU,EAAE,SAAS,MAAM,SAAS,oBAAoB,CAAC;AAAA,QAC9D,EAAE,QAAQ,KAAK,SAAS,EAAE,gBAAgB,mBAAmB,EAAE;AAAA,MACjE;AAAA,IACF,SAAS,OAAO;AACd,cAAQ,MAAM,6BAA6B,KAAK;AAEhD,aAAO,IAAI;AAAA,QACT,KAAK,UAAU;AAAA,UACb,OAAO;AAAA,UACP,SAAS,iBAAiB,QAAQ,MAAM,UAAU;AAAA,QACpD,CAAC;AAAA,QACD,EAAE,QAAQ,KAAK,SAAS,EAAE,gBAAgB,mBAAmB,EAAE;AAAA,MACjE;AAAA,IACF;AAAA,EACF;AAAA;AAEO,SAAS,0BACd,YACA,SACmB;AACnB,SAAO,CAAO,UAA2B;AACvC,QAAI,MAAM,eAAe,YAAY;AACnC,YAAM,IAAI,MAAM,wBAAwB,UAAU,WAAW,MAAM,UAAU,GAAG;AAAA,IAClF;AACA,WAAO,QAAQ,KAAK;AAAA,EACtB;AACF;","names":[]}
@@ -1,2 +1,2 @@
1
- export { c as WebhookEvent, d as WebhookHandler, W as WebhookOperation, e as WebhookOptions, f as createTypedWebhookHandler, h as handleWebhook, i as isValidWebhookEvent } from './webhook-BbT5xP-e.cjs';
2
- import './payload-types-BjyFdz5j.cjs';
1
+ export { c as WebhookEvent, d as WebhookHandler, W as WebhookOperation, e as WebhookOptions, f as createTypedWebhookHandler, h as handleWebhook, i as isValidWebhookEvent } from './webhook-DdNkaI-f.cjs';
2
+ import './payload-types-BAZCcssT.cjs';
package/dist/webhook.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { c as WebhookEvent, d as WebhookHandler, W as WebhookOperation, e as WebhookOptions, f as createTypedWebhookHandler, h as handleWebhook, i as isValidWebhookEvent } from './webhook-Cgywcim7.js';
2
- import './payload-types-BjyFdz5j.js';
1
+ export { c as WebhookEvent, d as WebhookHandler, W as WebhookOperation, e as WebhookOptions, f as createTypedWebhookHandler, h as handleWebhook, i as isValidWebhookEvent } from './webhook-CYJ0OPds.js';
2
+ import './payload-types-BAZCcssT.js';
package/dist/webhook.js CHANGED
@@ -37,10 +37,10 @@ function verifySignature(payload, secret, signature) {
37
37
  );
38
38
  const sig = yield crypto.subtle.sign("HMAC", key, encoder.encode(payload));
39
39
  const expected = Array.from(new Uint8Array(sig)).map((b) => b.toString(16).padStart(2, "0")).join("");
40
- if (expected.length !== signature.length) return false;
41
- let result = 0;
42
- for (let i = 0; i < expected.length; i++) {
43
- result |= expected.charCodeAt(i) ^ signature.charCodeAt(i);
40
+ let result = expected.length !== signature.length ? 1 : 0;
41
+ const len = Math.max(expected.length, signature.length);
42
+ for (let i = 0; i < len; i++) {
43
+ result |= (expected.charCodeAt(i) || 0) ^ (signature.charCodeAt(i) || 0);
44
44
  }
45
45
  return result === 0;
46
46
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/webhook/index.tsx"],"sourcesContent":["import type { Collection } from '../client/types'\nimport type { CollectionType } from '../collection/types'\n\nexport type WebhookOperation = 'create' | 'update'\n\nexport interface WebhookEvent<T extends Collection = Collection> {\n collection: T\n operation: WebhookOperation\n data: CollectionType<T>\n}\n\nexport type WebhookHandler<T extends Collection = Collection> = (\n event: WebhookEvent<T>,\n) => Promise<void> | void\n\nexport interface WebhookOptions {\n secret?: string\n}\n\nexport function isValidWebhookEvent(data: unknown): data is WebhookEvent {\n if (typeof data !== 'object' || data === null) return false\n const obj = data as Record<string, unknown>\n return (\n typeof obj.collection === 'string' &&\n (obj.operation === 'create' || obj.operation === 'update') &&\n typeof obj.data === 'object' &&\n obj.data !== null\n )\n}\n\nasync function verifySignature(\n payload: string,\n secret: string,\n signature: string,\n): Promise<boolean> {\n const encoder = new TextEncoder()\n const key = await crypto.subtle.importKey(\n 'raw',\n encoder.encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign'],\n )\n const sig = await crypto.subtle.sign('HMAC', key, encoder.encode(payload))\n const expected = Array.from(new Uint8Array(sig))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('')\n // Constant-time comparison to prevent timing attacks\n if (expected.length !== signature.length) return false\n let result = 0\n for (let i = 0; i < expected.length; i++) {\n result |= expected.charCodeAt(i) ^ signature.charCodeAt(i)\n }\n return result === 0\n}\n\nexport async function handleWebhook<T extends Collection = Collection>(\n request: Request,\n handler: WebhookHandler<T>,\n options?: WebhookOptions,\n): Promise<Response> {\n try {\n const rawBody = await request.text()\n\n if (options?.secret) {\n const signature = request.headers.get('x-webhook-signature') || ''\n const valid = await verifySignature(rawBody, options.secret, signature)\n if (!valid) {\n return new Response(\n JSON.stringify({ error: 'Invalid webhook signature' }),\n { status: 401, headers: { 'Content-Type': 'application/json' } },\n )\n }\n }\n\n const body = JSON.parse(rawBody)\n\n if (!isValidWebhookEvent(body)) {\n return new Response(\n JSON.stringify({ error: 'Invalid webhook event format' }),\n { status: 400, headers: { 'Content-Type': 'application/json' } },\n )\n }\n\n await handler(body as WebhookEvent<T>)\n\n return new Response(\n JSON.stringify({ success: true, message: 'Webhook processed' }),\n { status: 200, headers: { 'Content-Type': 'application/json' } },\n )\n } catch (error) {\n console.error('Webhook processing error:', error)\n\n return new Response(\n JSON.stringify({\n error: 'Internal server error',\n message: error instanceof Error ? error.message : 'Unknown error',\n }),\n { status: 500, headers: { 'Content-Type': 'application/json' } },\n )\n }\n}\n\nexport function createTypedWebhookHandler<T extends Collection>(\n collection: T,\n handler: (event: WebhookEvent<T>) => Promise<void> | void,\n): WebhookHandler<T> {\n return async (event: WebhookEvent<T>) => {\n if (event.collection !== collection) {\n throw new Error(`Expected collection \"${collection}\", got \"${event.collection}\"`)\n }\n return handler(event)\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAmBO,SAAS,oBAAoB,MAAqC;AACvE,MAAI,OAAO,SAAS,YAAY,SAAS,KAAM,QAAO;AACtD,QAAM,MAAM;AACZ,SACE,OAAO,IAAI,eAAe,aACzB,IAAI,cAAc,YAAY,IAAI,cAAc,aACjD,OAAO,IAAI,SAAS,YACpB,IAAI,SAAS;AAEjB;AAEA,SAAe,gBACb,SACA,QACA,WACkB;AAAA;AAClB,UAAM,UAAU,IAAI,YAAY;AAChC,UAAM,MAAM,MAAM,OAAO,OAAO;AAAA,MAC9B;AAAA,MACA,QAAQ,OAAO,MAAM;AAAA,MACrB,EAAE,MAAM,QAAQ,MAAM,UAAU;AAAA,MAChC;AAAA,MACA,CAAC,MAAM;AAAA,IACT;AACA,UAAM,MAAM,MAAM,OAAO,OAAO,KAAK,QAAQ,KAAK,QAAQ,OAAO,OAAO,CAAC;AACzE,UAAM,WAAW,MAAM,KAAK,IAAI,WAAW,GAAG,CAAC,EAC5C,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAC1C,KAAK,EAAE;AAEV,QAAI,SAAS,WAAW,UAAU,OAAQ,QAAO;AACjD,QAAI,SAAS;AACb,aAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,gBAAU,SAAS,WAAW,CAAC,IAAI,UAAU,WAAW,CAAC;AAAA,IAC3D;AACA,WAAO,WAAW;AAAA,EACpB;AAAA;AAEA,SAAsB,cACpB,SACA,SACA,SACmB;AAAA;AACnB,QAAI;AACF,YAAM,UAAU,MAAM,QAAQ,KAAK;AAEnC,UAAI,mCAAS,QAAQ;AACnB,cAAM,YAAY,QAAQ,QAAQ,IAAI,qBAAqB,KAAK;AAChE,cAAM,QAAQ,MAAM,gBAAgB,SAAS,QAAQ,QAAQ,SAAS;AACtE,YAAI,CAAC,OAAO;AACV,iBAAO,IAAI;AAAA,YACT,KAAK,UAAU,EAAE,OAAO,4BAA4B,CAAC;AAAA,YACrD,EAAE,QAAQ,KAAK,SAAS,EAAE,gBAAgB,mBAAmB,EAAE;AAAA,UACjE;AAAA,QACF;AAAA,MACF;AAEA,YAAM,OAAO,KAAK,MAAM,OAAO;AAE/B,UAAI,CAAC,oBAAoB,IAAI,GAAG;AAC9B,eAAO,IAAI;AAAA,UACT,KAAK,UAAU,EAAE,OAAO,+BAA+B,CAAC;AAAA,UACxD,EAAE,QAAQ,KAAK,SAAS,EAAE,gBAAgB,mBAAmB,EAAE;AAAA,QACjE;AAAA,MACF;AAEA,YAAM,QAAQ,IAAuB;AAErC,aAAO,IAAI;AAAA,QACT,KAAK,UAAU,EAAE,SAAS,MAAM,SAAS,oBAAoB,CAAC;AAAA,QAC9D,EAAE,QAAQ,KAAK,SAAS,EAAE,gBAAgB,mBAAmB,EAAE;AAAA,MACjE;AAAA,IACF,SAAS,OAAO;AACd,cAAQ,MAAM,6BAA6B,KAAK;AAEhD,aAAO,IAAI;AAAA,QACT,KAAK,UAAU;AAAA,UACb,OAAO;AAAA,UACP,SAAS,iBAAiB,QAAQ,MAAM,UAAU;AAAA,QACpD,CAAC;AAAA,QACD,EAAE,QAAQ,KAAK,SAAS,EAAE,gBAAgB,mBAAmB,EAAE;AAAA,MACjE;AAAA,IACF;AAAA,EACF;AAAA;AAEO,SAAS,0BACd,YACA,SACmB;AACnB,SAAO,CAAO,UAA2B;AACvC,QAAI,MAAM,eAAe,YAAY;AACnC,YAAM,IAAI,MAAM,wBAAwB,UAAU,WAAW,MAAM,UAAU,GAAG;AAAA,IAClF;AACA,WAAO,QAAQ,KAAK;AAAA,EACtB;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/core/webhook/index.tsx"],"sourcesContent":["import type { Collection } from '../client/types'\nimport type { CollectionType } from '../collection/types'\n\nexport type WebhookOperation = 'create' | 'update'\n\nexport interface WebhookEvent<T extends Collection = Collection> {\n collection: T\n operation: WebhookOperation\n data: CollectionType<T>\n}\n\nexport type WebhookHandler<T extends Collection = Collection> = (\n event: WebhookEvent<T>,\n) => Promise<void> | void\n\nexport interface WebhookOptions {\n secret?: string\n}\n\nexport function isValidWebhookEvent(data: unknown): data is WebhookEvent {\n if (typeof data !== 'object' || data === null) return false\n const obj = data as Record<string, unknown>\n return (\n typeof obj.collection === 'string' &&\n (obj.operation === 'create' || obj.operation === 'update') &&\n typeof obj.data === 'object' &&\n obj.data !== null\n )\n}\n\nasync function verifySignature(\n payload: string,\n secret: string,\n signature: string,\n): Promise<boolean> {\n const encoder = new TextEncoder()\n const key = await crypto.subtle.importKey(\n 'raw',\n encoder.encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign'],\n )\n const sig = await crypto.subtle.sign('HMAC', key, encoder.encode(payload))\n const expected = Array.from(new Uint8Array(sig))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('')\n // Constant-time comparison to prevent timing attacks\n let result = expected.length !== signature.length ? 1 : 0\n const len = Math.max(expected.length, signature.length)\n for (let i = 0; i < len; i++) {\n result |= (expected.charCodeAt(i) || 0) ^ (signature.charCodeAt(i) || 0)\n }\n return result === 0\n}\n\nexport async function handleWebhook<T extends Collection = Collection>(\n request: Request,\n handler: WebhookHandler<T>,\n options?: WebhookOptions,\n): Promise<Response> {\n try {\n const rawBody = await request.text()\n\n if (options?.secret) {\n const signature = request.headers.get('x-webhook-signature') || ''\n const valid = await verifySignature(rawBody, options.secret, signature)\n if (!valid) {\n return new Response(\n JSON.stringify({ error: 'Invalid webhook signature' }),\n { status: 401, headers: { 'Content-Type': 'application/json' } },\n )\n }\n }\n\n const body = JSON.parse(rawBody)\n\n if (!isValidWebhookEvent(body)) {\n return new Response(\n JSON.stringify({ error: 'Invalid webhook event format' }),\n { status: 400, headers: { 'Content-Type': 'application/json' } },\n )\n }\n\n await handler(body as WebhookEvent<T>)\n\n return new Response(\n JSON.stringify({ success: true, message: 'Webhook processed' }),\n { status: 200, headers: { 'Content-Type': 'application/json' } },\n )\n } catch (error) {\n console.error('Webhook processing error:', error)\n\n return new Response(\n JSON.stringify({\n error: 'Internal server error',\n message: error instanceof Error ? error.message : 'Unknown error',\n }),\n { status: 500, headers: { 'Content-Type': 'application/json' } },\n )\n }\n}\n\nexport function createTypedWebhookHandler<T extends Collection>(\n collection: T,\n handler: (event: WebhookEvent<T>) => Promise<void> | void,\n): WebhookHandler<T> {\n return async (event: WebhookEvent<T>) => {\n if (event.collection !== collection) {\n throw new Error(`Expected collection \"${collection}\", got \"${event.collection}\"`)\n }\n return handler(event)\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAmBO,SAAS,oBAAoB,MAAqC;AACvE,MAAI,OAAO,SAAS,YAAY,SAAS,KAAM,QAAO;AACtD,QAAM,MAAM;AACZ,SACE,OAAO,IAAI,eAAe,aACzB,IAAI,cAAc,YAAY,IAAI,cAAc,aACjD,OAAO,IAAI,SAAS,YACpB,IAAI,SAAS;AAEjB;AAEA,SAAe,gBACb,SACA,QACA,WACkB;AAAA;AAClB,UAAM,UAAU,IAAI,YAAY;AAChC,UAAM,MAAM,MAAM,OAAO,OAAO;AAAA,MAC9B;AAAA,MACA,QAAQ,OAAO,MAAM;AAAA,MACrB,EAAE,MAAM,QAAQ,MAAM,UAAU;AAAA,MAChC;AAAA,MACA,CAAC,MAAM;AAAA,IACT;AACA,UAAM,MAAM,MAAM,OAAO,OAAO,KAAK,QAAQ,KAAK,QAAQ,OAAO,OAAO,CAAC;AACzE,UAAM,WAAW,MAAM,KAAK,IAAI,WAAW,GAAG,CAAC,EAC5C,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAC1C,KAAK,EAAE;AAEV,QAAI,SAAS,SAAS,WAAW,UAAU,SAAS,IAAI;AACxD,UAAM,MAAM,KAAK,IAAI,SAAS,QAAQ,UAAU,MAAM;AACtD,aAAS,IAAI,GAAG,IAAI,KAAK,KAAK;AAC5B,iBAAW,SAAS,WAAW,CAAC,KAAK,MAAM,UAAU,WAAW,CAAC,KAAK;AAAA,IACxE;AACA,WAAO,WAAW;AAAA,EACpB;AAAA;AAEA,SAAsB,cACpB,SACA,SACA,SACmB;AAAA;AACnB,QAAI;AACF,YAAM,UAAU,MAAM,QAAQ,KAAK;AAEnC,UAAI,mCAAS,QAAQ;AACnB,cAAM,YAAY,QAAQ,QAAQ,IAAI,qBAAqB,KAAK;AAChE,cAAM,QAAQ,MAAM,gBAAgB,SAAS,QAAQ,QAAQ,SAAS;AACtE,YAAI,CAAC,OAAO;AACV,iBAAO,IAAI;AAAA,YACT,KAAK,UAAU,EAAE,OAAO,4BAA4B,CAAC;AAAA,YACrD,EAAE,QAAQ,KAAK,SAAS,EAAE,gBAAgB,mBAAmB,EAAE;AAAA,UACjE;AAAA,QACF;AAAA,MACF;AAEA,YAAM,OAAO,KAAK,MAAM,OAAO;AAE/B,UAAI,CAAC,oBAAoB,IAAI,GAAG;AAC9B,eAAO,IAAI;AAAA,UACT,KAAK,UAAU,EAAE,OAAO,+BAA+B,CAAC;AAAA,UACxD,EAAE,QAAQ,KAAK,SAAS,EAAE,gBAAgB,mBAAmB,EAAE;AAAA,QACjE;AAAA,MACF;AAEA,YAAM,QAAQ,IAAuB;AAErC,aAAO,IAAI;AAAA,QACT,KAAK,UAAU,EAAE,SAAS,MAAM,SAAS,oBAAoB,CAAC;AAAA,QAC9D,EAAE,QAAQ,KAAK,SAAS,EAAE,gBAAgB,mBAAmB,EAAE;AAAA,MACjE;AAAA,IACF,SAAS,OAAO;AACd,cAAQ,MAAM,6BAA6B,KAAK;AAEhD,aAAO,IAAI;AAAA,QACT,KAAK,UAAU;AAAA,UACb,OAAO;AAAA,UACP,SAAS,iBAAiB,QAAQ,MAAM,UAAU;AAAA,QACpD,CAAC;AAAA,QACD,EAAE,QAAQ,KAAK,SAAS,EAAE,gBAAgB,mBAAmB,EAAE;AAAA,MACjE;AAAA,IACF;AAAA,EACF;AAAA;AAEO,SAAS,0BACd,YACA,SACmB;AACnB,SAAO,CAAO,UAA2B;AACvC,QAAI,MAAM,eAAe,YAAY;AACnC,YAAM,IAAI,MAAM,wBAAwB,UAAU,WAAW,MAAM,UAAU,GAAG;AAAA,IAClF;AACA,WAAO,QAAQ,KAAK;AAAA,EACtB;AACF;","names":[]}