alchemy 0.22.4 → 0.23.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.
Files changed (160) hide show
  1. package/lib/aws/bucket.d.ts.map +1 -1
  2. package/lib/aws/bucket.js +13 -12
  3. package/lib/aws/bucket.js.map +1 -1
  4. package/lib/aws/control/client.d.ts.map +1 -1
  5. package/lib/aws/control/client.js +5 -3
  6. package/lib/aws/control/client.js.map +1 -1
  7. package/lib/aws/control/resource.d.ts.map +1 -1
  8. package/lib/aws/control/resource.js +13 -4
  9. package/lib/aws/control/resource.js.map +1 -1
  10. package/lib/aws/control/types.d.ts +85129 -0
  11. package/lib/aws/function.d.ts.map +1 -1
  12. package/lib/aws/function.js +51 -42
  13. package/lib/aws/function.js.map +1 -1
  14. package/lib/aws/index.d.ts +1 -0
  15. package/lib/aws/index.d.ts.map +1 -1
  16. package/lib/aws/index.js +1 -0
  17. package/lib/aws/index.js.map +1 -1
  18. package/lib/aws/policy-attachment.d.ts.map +1 -1
  19. package/lib/aws/policy-attachment.js +5 -4
  20. package/lib/aws/policy-attachment.js.map +1 -1
  21. package/lib/aws/policy.d.ts.map +1 -1
  22. package/lib/aws/policy.js +21 -20
  23. package/lib/aws/policy.js.map +1 -1
  24. package/lib/aws/queue.d.ts.map +1 -1
  25. package/lib/aws/queue.js +19 -15
  26. package/lib/aws/queue.js.map +1 -1
  27. package/lib/aws/retry.d.ts +5 -0
  28. package/lib/aws/retry.d.ts.map +1 -0
  29. package/lib/aws/retry.js +27 -0
  30. package/lib/aws/retry.js.map +1 -0
  31. package/lib/aws/role.d.ts.map +1 -1
  32. package/lib/aws/role.js +31 -30
  33. package/lib/aws/role.js.map +1 -1
  34. package/lib/aws/ses.d.ts.map +1 -1
  35. package/lib/aws/ses.js +27 -26
  36. package/lib/aws/ses.js.map +1 -1
  37. package/lib/aws/ssm-parameter.d.ts +150 -0
  38. package/lib/aws/ssm-parameter.d.ts.map +1 -0
  39. package/lib/aws/ssm-parameter.js +184 -0
  40. package/lib/aws/ssm-parameter.js.map +1 -0
  41. package/lib/aws/table.d.ts.map +1 -1
  42. package/lib/aws/table.js +9 -27
  43. package/lib/aws/table.js.map +1 -1
  44. package/lib/cloudflare/account-id.d.ts +1 -0
  45. package/lib/cloudflare/account-id.d.ts.map +1 -1
  46. package/lib/cloudflare/account-id.js.map +1 -1
  47. package/lib/cloudflare/api.d.ts.map +1 -1
  48. package/lib/cloudflare/api.js +22 -2
  49. package/lib/cloudflare/api.js.map +1 -1
  50. package/lib/cloudflare/index.d.ts +2 -1
  51. package/lib/cloudflare/index.d.ts.map +1 -1
  52. package/lib/cloudflare/index.js +2 -1
  53. package/lib/cloudflare/index.js.map +1 -1
  54. package/lib/cloudflare/queue.d.ts.map +1 -1
  55. package/lib/cloudflare/queue.js +1 -0
  56. package/lib/cloudflare/queue.js.map +1 -1
  57. package/lib/cloudflare/route.d.ts +7 -0
  58. package/lib/cloudflare/route.d.ts.map +1 -1
  59. package/lib/cloudflare/route.js +44 -2
  60. package/lib/cloudflare/route.js.map +1 -1
  61. package/lib/cloudflare/worker-metadata.d.ts.map +1 -1
  62. package/lib/cloudflare/worker-metadata.js +1 -2
  63. package/lib/cloudflare/worker-metadata.js.map +1 -1
  64. package/lib/cloudflare/worker.d.ts +2 -1
  65. package/lib/cloudflare/worker.d.ts.map +1 -1
  66. package/lib/cloudflare/worker.js +17 -8
  67. package/lib/cloudflare/worker.js.map +1 -1
  68. package/lib/cloudflare/workflow.d.ts +7 -0
  69. package/lib/cloudflare/workflow.d.ts.map +1 -1
  70. package/lib/cloudflare/workflow.js +2 -0
  71. package/lib/cloudflare/workflow.js.map +1 -1
  72. package/lib/cloudflare/wrangler.json.d.ts +1 -0
  73. package/lib/cloudflare/wrangler.json.d.ts.map +1 -1
  74. package/lib/cloudflare/wrangler.json.js +1 -0
  75. package/lib/cloudflare/wrangler.json.js.map +1 -1
  76. package/lib/destroy.js +1 -1
  77. package/lib/destroy.js.map +1 -1
  78. package/lib/dns/godaddy.d.ts.map +1 -1
  79. package/lib/dns/godaddy.js +2 -1
  80. package/lib/dns/godaddy.js.map +1 -1
  81. package/lib/fs/file-system-state-store.d.ts.map +1 -1
  82. package/lib/fs/file-system-state-store.js +14 -2
  83. package/lib/fs/file-system-state-store.js.map +1 -1
  84. package/lib/github/secret.d.ts.map +1 -1
  85. package/lib/github/secret.js +1 -0
  86. package/lib/github/secret.js.map +1 -1
  87. package/lib/neon/api.d.ts.map +1 -1
  88. package/lib/neon/api.js +2 -1
  89. package/lib/neon/api.js.map +1 -1
  90. package/lib/runtime/shims.d.ts +6 -0
  91. package/lib/runtime/shims.d.ts.map +1 -0
  92. package/lib/runtime/shims.js +27 -0
  93. package/lib/runtime/shims.js.map +1 -0
  94. package/lib/sentry/api.d.ts.map +1 -1
  95. package/lib/sentry/api.js +2 -1
  96. package/lib/sentry/api.js.map +1 -1
  97. package/lib/test/bun.d.ts.map +1 -1
  98. package/lib/test/bun.js +1 -7
  99. package/lib/test/bun.js.map +1 -1
  100. package/lib/test/prune.d.ts +2 -1
  101. package/lib/test/prune.d.ts.map +1 -1
  102. package/lib/test/prune.js +29 -5
  103. package/lib/test/prune.js.map +1 -1
  104. package/lib/test/vitest.d.ts +92 -0
  105. package/lib/test/vitest.d.ts.map +1 -0
  106. package/lib/test/vitest.js +94 -0
  107. package/lib/test/vitest.js.map +1 -0
  108. package/lib/upstash/api.d.ts.map +1 -1
  109. package/lib/upstash/api.js +2 -1
  110. package/lib/upstash/api.js.map +1 -1
  111. package/lib/util/retry.js +2 -2
  112. package/lib/util/retry.js.map +1 -1
  113. package/lib/util/safe-fetch.d.ts +2 -0
  114. package/lib/util/safe-fetch.d.ts.map +1 -0
  115. package/lib/util/safe-fetch.js +48 -0
  116. package/lib/util/safe-fetch.js.map +1 -0
  117. package/lib/vercel/api.d.ts.map +1 -1
  118. package/lib/vercel/api.js +2 -1
  119. package/lib/vercel/api.js.map +1 -1
  120. package/lib/web/vitepress/process-front-matter-files.d.ts.map +1 -1
  121. package/lib/web/vitepress/process-front-matter-files.js +11 -7
  122. package/lib/web/vitepress/process-front-matter-files.js.map +1 -1
  123. package/package.json +10 -6
  124. package/src/alchemy.ts +2 -2
  125. package/src/aws/bucket.ts +55 -36
  126. package/src/aws/control/client.ts +13 -13
  127. package/src/aws/control/resource.ts +19 -3
  128. package/src/aws/function.ts +233 -178
  129. package/src/aws/index.ts +1 -0
  130. package/src/aws/policy-attachment.ts +15 -10
  131. package/src/aws/policy.ts +75 -54
  132. package/src/aws/queue.ts +53 -36
  133. package/src/aws/retry.ts +38 -0
  134. package/src/aws/role.ts +121 -90
  135. package/src/aws/ses.ts +101 -73
  136. package/src/aws/ssm-parameter.ts +315 -0
  137. package/src/aws/table.ts +61 -90
  138. package/src/cloudflare/account-id.ts +2 -0
  139. package/src/cloudflare/api.ts +26 -3
  140. package/src/cloudflare/index.ts +2 -1
  141. package/src/cloudflare/queue.ts +2 -0
  142. package/src/cloudflare/route.ts +86 -2
  143. package/src/cloudflare/worker-metadata.ts +1 -2
  144. package/src/cloudflare/worker.ts +21 -9
  145. package/src/cloudflare/workflow.ts +8 -0
  146. package/src/cloudflare/wrangler.json.ts +8 -1
  147. package/src/destroy.ts +1 -1
  148. package/src/dns/godaddy.ts +3 -1
  149. package/src/fs/file-system-state-store.ts +16 -2
  150. package/src/github/secret.ts +2 -0
  151. package/src/neon/api.ts +2 -1
  152. package/src/sentry/api.ts +2 -1
  153. package/src/test/bun.ts +1 -7
  154. package/src/test/prune.ts +31 -4
  155. package/src/test/vitest.ts +209 -0
  156. package/src/upstash/api.ts +2 -1
  157. package/src/util/retry.ts +3 -3
  158. package/src/util/safe-fetch.ts +61 -0
  159. package/src/vercel/api.ts +2 -1
  160. package/src/web/vitepress/process-front-matter-files.ts +15 -7
@@ -19,6 +19,7 @@ import type { Context } from "../context.ts";
19
19
  import type { Bundle } from "../esbuild/bundle.ts";
20
20
  import { Resource } from "../resource.ts";
21
21
  import { ignore } from "../util/ignore.ts";
22
+ import { retry } from "./retry.ts";
22
23
 
23
24
  /**
24
25
  * Properties for creating or updating a Lambda function
@@ -317,10 +318,12 @@ export const Function = Resource(
317
318
  // Delete function URL if it exists
318
319
  if (this.output?.url) {
319
320
  try {
320
- await client.send(
321
- new DeleteFunctionUrlConfigCommand({
322
- FunctionName: props.functionName,
323
- }),
321
+ await retry(() =>
322
+ client.send(
323
+ new DeleteFunctionUrlConfigCommand({
324
+ FunctionName: props.functionName,
325
+ }),
326
+ ),
324
327
  );
325
328
  } catch (error: any) {
326
329
  if (error.name !== "ResourceNotFoundException") {
@@ -330,10 +333,12 @@ export const Function = Resource(
330
333
  }
331
334
 
332
335
  await ignore(ResourceNotFoundException.name, () =>
333
- client.send(
334
- new DeleteFunctionCommand({
335
- FunctionName: props.functionName,
336
- }),
336
+ retry(() =>
337
+ client.send(
338
+ new DeleteFunctionCommand({
339
+ FunctionName: props.functionName,
340
+ }),
341
+ ),
337
342
  ),
338
343
  );
339
344
 
@@ -342,10 +347,12 @@ export const Function = Resource(
342
347
  let functionUrl: string | undefined;
343
348
  try {
344
349
  // Check if function exists
345
- await client.send(
346
- new GetFunctionCommand({
347
- FunctionName: props.functionName,
348
- }),
350
+ await retry(() =>
351
+ client.send(
352
+ new GetFunctionCommand({
353
+ FunctionName: props.functionName,
354
+ }),
355
+ ),
349
356
  );
350
357
 
351
358
  if (this.phase === "update") {
@@ -353,31 +360,35 @@ export const Function = Resource(
353
360
  await waitForFunctionStabilization(client, props.functionName);
354
361
 
355
362
  // Update function code
356
- await client.send(
357
- new UpdateFunctionCodeCommand({
358
- FunctionName: props.functionName,
359
- ZipFile: code,
360
- }),
363
+ await retry(() =>
364
+ client.send(
365
+ new UpdateFunctionCodeCommand({
366
+ FunctionName: props.functionName,
367
+ ZipFile: code,
368
+ }),
369
+ ),
361
370
  );
362
371
 
363
372
  // Wait for code update to stabilize
364
373
  await waitForFunctionStabilization(client, props.functionName);
365
374
 
366
375
  // Update function configuration
367
- await client.send(
368
- new UpdateFunctionConfigurationCommand({
369
- FunctionName: props.functionName,
370
- Handler: props.handler,
371
- Runtime: props.runtime,
372
- Role: props.roleArn,
373
- Description: props.description,
374
- Timeout: props.timeout,
375
- MemorySize: props.memorySize,
376
- Environment: props.environment
377
- ? { Variables: props.environment }
378
- : undefined,
379
- Layers: props.layers,
380
- }),
376
+ await retry(() =>
377
+ client.send(
378
+ new UpdateFunctionConfigurationCommand({
379
+ FunctionName: props.functionName,
380
+ Handler: props.handler,
381
+ Runtime: props.runtime,
382
+ Role: props.roleArn,
383
+ Description: props.description,
384
+ Timeout: props.timeout,
385
+ MemorySize: props.memorySize,
386
+ Environment: props.environment
387
+ ? { Variables: props.environment }
388
+ : undefined,
389
+ Layers: props.layers,
390
+ }),
391
+ ),
381
392
  );
382
393
 
383
394
  // Wait for configuration update to stabilize
@@ -387,44 +398,50 @@ export const Function = Resource(
387
398
  if (props.url) {
388
399
  try {
389
400
  // Check if URL config exists already
390
- const urlConfig = await client.send(
391
- new GetFunctionUrlConfigCommand({
392
- FunctionName: props.functionName,
393
- }),
401
+ const urlConfig = await retry(() =>
402
+ client.send(
403
+ new GetFunctionUrlConfigCommand({
404
+ FunctionName: props.functionName,
405
+ }),
406
+ ),
394
407
  );
395
408
 
396
409
  // Update URL configuration if it exists
397
410
  if (urlConfig) {
398
- const updateResult = await client.send(
399
- new UpdateFunctionUrlConfigCommand({
400
- FunctionName: props.functionName,
401
- AuthType: props.url.authType || "NONE",
402
- InvokeMode: props.url.invokeMode || "BUFFERED",
403
- Cors: props.url.cors
404
- ? {
405
- AllowCredentials: props.url.cors.allowCredentials,
406
- AllowHeaders: props.url.cors.allowHeaders,
407
- AllowMethods: props.url.cors.allowMethods,
408
- AllowOrigins: props.url.cors.allowOrigins,
409
- ExposeHeaders: props.url.cors.exposeHeaders,
410
- MaxAge: props.url.cors.maxAge,
411
- }
412
- : undefined,
413
- }),
411
+ const updateResult = await retry(() =>
412
+ client.send(
413
+ new UpdateFunctionUrlConfigCommand({
414
+ FunctionName: props.functionName,
415
+ AuthType: props.url!.authType || "NONE",
416
+ InvokeMode: props.url!.invokeMode || "BUFFERED",
417
+ Cors: props.url!.cors
418
+ ? {
419
+ AllowCredentials: props.url!.cors.allowCredentials,
420
+ AllowHeaders: props.url!.cors.allowHeaders,
421
+ AllowMethods: props.url!.cors.allowMethods,
422
+ AllowOrigins: props.url!.cors.allowOrigins,
423
+ ExposeHeaders: props.url!.cors.exposeHeaders,
424
+ MaxAge: props.url!.cors.maxAge,
425
+ }
426
+ : undefined,
427
+ }),
428
+ ),
414
429
  );
415
430
  functionUrl = updateResult.FunctionUrl;
416
431
 
417
432
  // Add public access permission for function URL
418
- if (props.url.authType === "NONE") {
433
+ if (props.url!.authType === "NONE") {
419
434
  try {
420
- await client.send(
421
- new AddPermissionCommand({
422
- FunctionName: props.functionName,
423
- StatementId: "FunctionURLAllowPublicAccess",
424
- Action: "lambda:InvokeFunctionUrl",
425
- Principal: "*",
426
- FunctionUrlAuthType: "NONE",
427
- }),
435
+ await retry(() =>
436
+ client.send(
437
+ new AddPermissionCommand({
438
+ FunctionName: props.functionName,
439
+ StatementId: "FunctionURLAllowPublicAccess",
440
+ Action: "lambda:InvokeFunctionUrl",
441
+ Principal: "*",
442
+ FunctionUrlAuthType: "NONE",
443
+ }),
444
+ ),
428
445
  );
429
446
  } catch (permError: any) {
430
447
  if (!permError.message?.includes("already exists")) {
@@ -434,36 +451,40 @@ export const Function = Resource(
434
451
  }
435
452
  } else {
436
453
  // Create URL configuration if it doesn't exist
437
- const createResult = await client.send(
438
- new CreateFunctionUrlConfigCommand({
439
- FunctionName: props.functionName,
440
- AuthType: props.url.authType || "NONE",
441
- InvokeMode: props.url.invokeMode || "BUFFERED",
442
- Cors: props.url.cors
443
- ? {
444
- AllowCredentials: props.url.cors.allowCredentials,
445
- AllowHeaders: props.url.cors.allowHeaders,
446
- AllowMethods: props.url.cors.allowMethods,
447
- AllowOrigins: props.url.cors.allowOrigins,
448
- ExposeHeaders: props.url.cors.exposeHeaders,
449
- MaxAge: props.url.cors.maxAge,
450
- }
451
- : undefined,
452
- }),
454
+ const createResult = await retry(() =>
455
+ client.send(
456
+ new CreateFunctionUrlConfigCommand({
457
+ FunctionName: props.functionName,
458
+ AuthType: props.url!.authType || "NONE",
459
+ InvokeMode: props.url!.invokeMode || "BUFFERED",
460
+ Cors: props.url!.cors
461
+ ? {
462
+ AllowCredentials: props.url!.cors.allowCredentials,
463
+ AllowHeaders: props.url!.cors.allowHeaders,
464
+ AllowMethods: props.url!.cors.allowMethods,
465
+ AllowOrigins: props.url!.cors.allowOrigins,
466
+ ExposeHeaders: props.url!.cors.exposeHeaders,
467
+ MaxAge: props.url!.cors.maxAge,
468
+ }
469
+ : undefined,
470
+ }),
471
+ ),
453
472
  );
454
473
  functionUrl = createResult.FunctionUrl;
455
474
 
456
475
  // Add public access permission for function URL
457
- if (props.url.authType === "NONE") {
476
+ if (props.url!.authType === "NONE") {
458
477
  try {
459
- await client.send(
460
- new AddPermissionCommand({
461
- FunctionName: props.functionName,
462
- StatementId: "FunctionURLAllowPublicAccess",
463
- Action: "lambda:InvokeFunctionUrl",
464
- Principal: "*",
465
- FunctionUrlAuthType: "NONE",
466
- }),
478
+ await retry(() =>
479
+ client.send(
480
+ new AddPermissionCommand({
481
+ FunctionName: props.functionName,
482
+ StatementId: "FunctionURLAllowPublicAccess",
483
+ Action: "lambda:InvokeFunctionUrl",
484
+ Principal: "*",
485
+ FunctionUrlAuthType: "NONE",
486
+ }),
487
+ ),
467
488
  );
468
489
  } catch (permError: any) {
469
490
  if (!permError.message?.includes("already exists")) {
@@ -475,36 +496,40 @@ export const Function = Resource(
475
496
  } catch (error: any) {
476
497
  if (error.name === "ResourceNotFoundException") {
477
498
  // Create URL configuration if it doesn't exist
478
- const createResult = await client.send(
479
- new CreateFunctionUrlConfigCommand({
480
- FunctionName: props.functionName,
481
- AuthType: props.url.authType || "NONE",
482
- InvokeMode: props.url.invokeMode || "BUFFERED",
483
- Cors: props.url.cors
484
- ? {
485
- AllowCredentials: props.url.cors.allowCredentials,
486
- AllowHeaders: props.url.cors.allowHeaders,
487
- AllowMethods: props.url.cors.allowMethods,
488
- AllowOrigins: props.url.cors.allowOrigins,
489
- ExposeHeaders: props.url.cors.exposeHeaders,
490
- MaxAge: props.url.cors.maxAge,
491
- }
492
- : undefined,
493
- }),
499
+ const createResult = await retry(() =>
500
+ client.send(
501
+ new CreateFunctionUrlConfigCommand({
502
+ FunctionName: props.functionName,
503
+ AuthType: props.url!.authType || "NONE",
504
+ InvokeMode: props.url!.invokeMode || "BUFFERED",
505
+ Cors: props.url!.cors
506
+ ? {
507
+ AllowCredentials: props.url!.cors.allowCredentials,
508
+ AllowHeaders: props.url!.cors.allowHeaders,
509
+ AllowMethods: props.url!.cors.allowMethods,
510
+ AllowOrigins: props.url!.cors.allowOrigins,
511
+ ExposeHeaders: props.url!.cors.exposeHeaders,
512
+ MaxAge: props.url!.cors.maxAge,
513
+ }
514
+ : undefined,
515
+ }),
516
+ ),
494
517
  );
495
518
  functionUrl = createResult.FunctionUrl;
496
519
 
497
520
  // Add public access permission for function URL
498
- if (props.url.authType === "NONE") {
521
+ if (props.url!.authType === "NONE") {
499
522
  try {
500
- await client.send(
501
- new AddPermissionCommand({
502
- FunctionName: props.functionName,
503
- StatementId: "FunctionURLAllowPublicAccess",
504
- Action: "lambda:InvokeFunctionUrl",
505
- Principal: "*",
506
- FunctionUrlAuthType: "NONE",
507
- }),
523
+ await retry(() =>
524
+ client.send(
525
+ new AddPermissionCommand({
526
+ FunctionName: props.functionName,
527
+ StatementId: "FunctionURLAllowPublicAccess",
528
+ Action: "lambda:InvokeFunctionUrl",
529
+ Principal: "*",
530
+ FunctionUrlAuthType: "NONE",
531
+ }),
532
+ ),
508
533
  );
509
534
  } catch (permError: any) {
510
535
  if (!permError.message?.includes("already exists")) {
@@ -519,10 +544,12 @@ export const Function = Resource(
519
544
  } else if (this.output?.url) {
520
545
  // Remove URL config if it was previously set but not in current props
521
546
  try {
522
- await client.send(
523
- new DeleteFunctionUrlConfigCommand({
524
- FunctionName: props.functionName,
525
- }),
547
+ await retry(() =>
548
+ client.send(
549
+ new DeleteFunctionUrlConfigCommand({
550
+ FunctionName: props.functionName,
551
+ }),
552
+ ),
526
553
  );
527
554
  functionUrl = undefined;
528
555
  } catch (error: any) {
@@ -540,25 +567,27 @@ export const Function = Resource(
540
567
 
541
568
  while (true) {
542
569
  try {
543
- await client.send(
544
- new CreateFunctionCommand({
545
- FunctionName: props.functionName,
546
- Code: { ZipFile: code },
547
- Handler: props.handler || "index.handler",
548
- Runtime: props.runtime || Runtime.nodejs20x,
549
- Role: props.roleArn,
550
- Description: props.description,
551
- Timeout: props.timeout || 3,
552
- MemorySize: props.memorySize || 128,
553
- Environment: props.environment
554
- ? { Variables: props.environment }
555
- : undefined,
556
- Architectures: props.architecture
557
- ? [props.architecture]
558
- : [Architecture.x86_64],
559
- Tags: props.tags,
560
- Layers: props.layers,
561
- }),
570
+ await retry(() =>
571
+ client.send(
572
+ new CreateFunctionCommand({
573
+ FunctionName: props.functionName,
574
+ Code: { ZipFile: code },
575
+ Handler: props.handler || "index.handler",
576
+ Runtime: props.runtime || Runtime.nodejs20x,
577
+ Role: props.roleArn,
578
+ Description: props.description,
579
+ Timeout: props.timeout || 3,
580
+ MemorySize: props.memorySize || 128,
581
+ Environment: props.environment
582
+ ? { Variables: props.environment }
583
+ : undefined,
584
+ Architectures: props.architecture
585
+ ? [props.architecture]
586
+ : [Architecture.x86_64],
587
+ Tags: props.tags,
588
+ Layers: props.layers,
589
+ }),
590
+ ),
562
591
  );
563
592
  break; // Success - exit retry loop
564
593
  } catch (createError: any) {
@@ -583,10 +612,12 @@ export const Function = Resource(
583
612
  // Wait for function to be active
584
613
  let isCreating = true;
585
614
  while (isCreating) {
586
- const config = await client.send(
587
- new GetFunctionConfigurationCommand({
588
- FunctionName: props.functionName,
589
- }),
615
+ const config = await retry(() =>
616
+ client.send(
617
+ new GetFunctionConfigurationCommand({
618
+ FunctionName: props.functionName,
619
+ }),
620
+ ),
590
621
  );
591
622
  isCreating = config.State === "Pending";
592
623
  if (isCreating) {
@@ -594,39 +625,50 @@ export const Function = Resource(
594
625
  }
595
626
  }
596
627
 
628
+ // Additional stabilization check - ensure role can be used
629
+ // by waiting a bit more after function becomes Active
630
+ await new Promise((resolve) => setTimeout(resolve, 2000));
631
+
632
+ // Verify function is fully stable and role is usable
633
+ await waitForFunctionStabilization(client, props.functionName);
634
+
597
635
  // Create URL configuration if needed
598
636
  if (props.url) {
599
637
  try {
600
- const createResult = await client.send(
601
- new CreateFunctionUrlConfigCommand({
602
- FunctionName: props.functionName,
603
- AuthType: props.url.authType || "NONE",
604
- InvokeMode: props.url.invokeMode || "BUFFERED",
605
- Cors: props.url.cors
606
- ? {
607
- AllowCredentials: props.url.cors.allowCredentials,
608
- AllowHeaders: props.url.cors.allowHeaders,
609
- AllowMethods: props.url.cors.allowMethods,
610
- AllowOrigins: props.url.cors.allowOrigins,
611
- ExposeHeaders: props.url.cors.exposeHeaders,
612
- MaxAge: props.url.cors.maxAge,
613
- }
614
- : undefined,
615
- }),
638
+ const createResult = await retry(() =>
639
+ client.send(
640
+ new CreateFunctionUrlConfigCommand({
641
+ FunctionName: props.functionName,
642
+ AuthType: props.url!.authType || "NONE",
643
+ InvokeMode: props.url!.invokeMode || "BUFFERED",
644
+ Cors: props.url!.cors
645
+ ? {
646
+ AllowCredentials: props.url!.cors.allowCredentials,
647
+ AllowHeaders: props.url!.cors.allowHeaders,
648
+ AllowMethods: props.url!.cors.allowMethods,
649
+ AllowOrigins: props.url!.cors.allowOrigins,
650
+ ExposeHeaders: props.url!.cors.exposeHeaders,
651
+ MaxAge: props.url!.cors.maxAge,
652
+ }
653
+ : undefined,
654
+ }),
655
+ ),
616
656
  );
617
657
  functionUrl = createResult.FunctionUrl;
618
658
 
619
659
  // Add public access permission for function URL
620
- if (props.url.authType === "NONE") {
660
+ if (props.url!.authType === "NONE") {
621
661
  try {
622
- await client.send(
623
- new AddPermissionCommand({
624
- FunctionName: props.functionName,
625
- StatementId: "FunctionURLAllowPublicAccess",
626
- Action: "lambda:InvokeFunctionUrl",
627
- Principal: "*",
628
- FunctionUrlAuthType: "NONE",
629
- }),
662
+ await retry(() =>
663
+ client.send(
664
+ new AddPermissionCommand({
665
+ FunctionName: props.functionName,
666
+ StatementId: "FunctionURLAllowPublicAccess",
667
+ Action: "lambda:InvokeFunctionUrl",
668
+ Principal: "*",
669
+ FunctionUrlAuthType: "NONE",
670
+ }),
671
+ ),
630
672
  );
631
673
  } catch (permError: any) {
632
674
  if (!permError.message?.includes("already exists")) {
@@ -645,25 +687,31 @@ export const Function = Resource(
645
687
 
646
688
  // Get complete function details
647
689
  const [_func, config] = await Promise.all([
648
- client.send(
649
- new GetFunctionCommand({
650
- FunctionName: props.functionName,
651
- }),
690
+ retry(() =>
691
+ client.send(
692
+ new GetFunctionCommand({
693
+ FunctionName: props.functionName,
694
+ }),
695
+ ),
652
696
  ),
653
- client.send(
654
- new GetFunctionConfigurationCommand({
655
- FunctionName: props.functionName,
656
- }),
697
+ retry(() =>
698
+ client.send(
699
+ new GetFunctionConfigurationCommand({
700
+ FunctionName: props.functionName,
701
+ }),
702
+ ),
657
703
  ),
658
704
  ]);
659
705
 
660
706
  // Try to get function URL if it wasn't already retrieved and URL is configured
661
707
  if (!functionUrl && (props.url || this.output?.url)) {
662
708
  try {
663
- const urlConfig = await client.send(
664
- new GetFunctionUrlConfigCommand({
665
- FunctionName: props.functionName,
666
- }),
709
+ const urlConfig = await retry(() =>
710
+ client.send(
711
+ new GetFunctionUrlConfigCommand({
712
+ FunctionName: props.functionName,
713
+ }),
714
+ ),
667
715
  );
668
716
  functionUrl = urlConfig.FunctionUrl;
669
717
  } catch (error: any) {
@@ -706,10 +754,12 @@ async function waitForFunctionStabilization(
706
754
  functionName: string,
707
755
  ) {
708
756
  while (true) {
709
- const config = await client.send(
710
- new GetFunctionConfigurationCommand({
711
- FunctionName: functionName,
712
- }),
757
+ const config = await retry(() =>
758
+ client.send(
759
+ new GetFunctionConfigurationCommand({
760
+ FunctionName: functionName,
761
+ }),
762
+ ),
713
763
  );
714
764
 
715
765
  // Check if function is in a stable state
@@ -745,9 +795,14 @@ async function zipCode(props: FunctionProps): Promise<Buffer> {
745
795
  parseFile(props.handler) +
746
796
  (props.bundle.format === "cjs" ? ".cjs" : ".mjs");
747
797
 
798
+ if (!fileContent || fileContent.length === 0) {
799
+ throw new Error("Bundle content is empty");
800
+ }
801
+
748
802
  // Create a zip buffer in memory
749
803
  const zip = new (await import("jszip")).default();
750
804
  zip.file(fileName, fileContent);
805
+
751
806
  return zip.generateAsync({
752
807
  type: "nodebuffer",
753
808
  compression: "DEFLATE",
package/src/aws/index.ts CHANGED
@@ -6,4 +6,5 @@ export * from "./policy.ts";
6
6
  export * from "./queue.ts";
7
7
  export * from "./role.ts";
8
8
  export * from "./ses.ts";
9
+ export * from "./ssm-parameter.ts";
9
10
  export * from "./table.ts";
@@ -7,6 +7,7 @@ import {
7
7
  import type { Context } from "../context.ts";
8
8
  import { Resource } from "../resource.ts";
9
9
  import { ignore } from "../util/ignore.ts";
10
+ import { retry } from "./retry.ts";
10
11
 
11
12
  /**
12
13
  * Properties for creating or updating a policy attachment
@@ -72,20 +73,24 @@ export const PolicyAttachment = Resource(
72
73
 
73
74
  if (this.phase === "delete") {
74
75
  await ignore(NoSuchEntityException.name, () =>
75
- client.send(
76
- new DetachRolePolicyCommand({
77
- PolicyArn: props.policyArn,
78
- RoleName: props.roleName,
79
- }),
76
+ retry(() =>
77
+ client.send(
78
+ new DetachRolePolicyCommand({
79
+ PolicyArn: props.policyArn,
80
+ RoleName: props.roleName,
81
+ }),
82
+ ),
80
83
  ),
81
84
  );
82
85
  return this.destroy();
83
86
  }
84
- await client.send(
85
- new AttachRolePolicyCommand({
86
- PolicyArn: props.policyArn,
87
- RoleName: props.roleName,
88
- }),
87
+ await retry(() =>
88
+ client.send(
89
+ new AttachRolePolicyCommand({
90
+ PolicyArn: props.policyArn,
91
+ RoleName: props.roleName,
92
+ }),
93
+ ),
89
94
  );
90
95
 
91
96
  return this(props);