alchemy-effect 0.2.0 → 0.4.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/bin/alchemy-effect.js +55354 -8
- package/bin/alchemy-effect.js.map +1 -1
- package/bin/alchemy-effect.ts +266 -10
- package/lib/$.d.ts +5 -0
- package/lib/$.d.ts.map +1 -0
- package/lib/$.js +9 -0
- package/lib/$.js.map +1 -0
- package/lib/app.d.ts +4 -17
- package/lib/app.d.ts.map +1 -1
- package/lib/app.js +0 -20
- package/lib/app.js.map +1 -1
- package/lib/apply.d.ts +15 -75
- package/lib/apply.d.ts.map +1 -1
- package/lib/apply.js +439 -154
- package/lib/apply.js.map +1 -1
- package/lib/assert-never.d.ts +12 -0
- package/lib/assert-never.d.ts.map +1 -0
- package/lib/assert-never.js +11 -0
- package/lib/assert-never.js.map +1 -0
- package/lib/aws/account.d.ts +10 -1
- package/lib/aws/account.d.ts.map +1 -1
- package/lib/aws/account.js +18 -3
- package/lib/aws/account.js.map +1 -1
- package/lib/aws/client.d.ts.map +1 -1
- package/lib/aws/client.js +0 -1
- package/lib/aws/client.js.map +1 -1
- package/lib/aws/config.d.ts +15 -0
- package/lib/aws/config.d.ts.map +1 -0
- package/lib/aws/config.js +1 -0
- package/lib/aws/config.js.map +1 -0
- package/lib/aws/credentials.d.ts +10 -0
- package/lib/aws/credentials.d.ts.map +1 -1
- package/lib/aws/credentials.js +73 -47
- package/lib/aws/credentials.js.map +1 -1
- package/lib/aws/dynamodb/client.d.ts +1 -1
- package/lib/aws/dynamodb/client.d.ts.map +1 -1
- package/lib/aws/dynamodb/index.d.ts +2 -1
- package/lib/aws/dynamodb/index.d.ts.map +1 -1
- package/lib/aws/dynamodb/index.js +1 -2
- package/lib/aws/dynamodb/index.js.map +1 -1
- package/lib/aws/dynamodb/secondary-index.d.ts +5 -4
- package/lib/aws/dynamodb/secondary-index.d.ts.map +1 -1
- package/lib/aws/dynamodb/table.d.ts +23 -20
- package/lib/aws/dynamodb/table.d.ts.map +1 -1
- package/lib/aws/dynamodb/table.get-item.d.ts +8 -6
- package/lib/aws/dynamodb/table.get-item.d.ts.map +1 -1
- package/lib/aws/dynamodb/table.get-item.js +4 -2
- package/lib/aws/dynamodb/table.get-item.js.map +1 -1
- package/lib/aws/dynamodb/table.js.map +1 -1
- package/lib/aws/dynamodb/table.provider.d.ts +3 -4
- package/lib/aws/dynamodb/table.provider.d.ts.map +1 -1
- package/lib/aws/dynamodb/table.provider.js +18 -29
- package/lib/aws/dynamodb/table.provider.js.map +1 -1
- package/lib/aws/ec2/client.d.ts +1 -1
- package/lib/aws/ec2/client.d.ts.map +1 -1
- package/lib/aws/ec2/index.d.ts +11 -0
- package/lib/aws/ec2/index.d.ts.map +1 -1
- package/lib/aws/ec2/index.js +11 -0
- package/lib/aws/ec2/index.js.map +1 -1
- package/lib/aws/ec2/internet-gateway.d.ts +65 -0
- package/lib/aws/ec2/internet-gateway.d.ts.map +1 -0
- package/lib/aws/ec2/internet-gateway.js +4 -0
- package/lib/aws/ec2/internet-gateway.js.map +1 -0
- package/lib/aws/ec2/internet-gateway.provider.d.ts +6 -0
- package/lib/aws/ec2/internet-gateway.provider.d.ts.map +1 -0
- package/lib/aws/ec2/internet-gateway.provider.js +193 -0
- package/lib/aws/ec2/internet-gateway.provider.js.map +1 -0
- package/lib/aws/ec2/route-table-association.d.ts +63 -0
- package/lib/aws/ec2/route-table-association.d.ts.map +1 -0
- package/lib/aws/ec2/route-table-association.js +4 -0
- package/lib/aws/ec2/route-table-association.js.map +1 -0
- package/lib/aws/ec2/route-table-association.provider.d.ts +4 -0
- package/lib/aws/ec2/route-table-association.provider.d.ts.map +1 -0
- package/lib/aws/ec2/route-table-association.provider.js +121 -0
- package/lib/aws/ec2/route-table-association.provider.js.map +1 -0
- package/lib/aws/ec2/route-table.d.ts +159 -0
- package/lib/aws/ec2/route-table.d.ts.map +1 -0
- package/lib/aws/ec2/route-table.js +4 -0
- package/lib/aws/ec2/route-table.js.map +1 -0
- package/lib/aws/ec2/route-table.provider.d.ts +6 -0
- package/lib/aws/ec2/route-table.provider.d.ts.map +1 -0
- package/lib/aws/ec2/route-table.provider.js +213 -0
- package/lib/aws/ec2/route-table.provider.js.map +1 -0
- package/lib/aws/ec2/route.d.ts +155 -0
- package/lib/aws/ec2/route.d.ts.map +1 -0
- package/lib/aws/ec2/route.js +3 -0
- package/lib/aws/ec2/route.js.map +1 -0
- package/lib/aws/ec2/route.provider.d.ts +4 -0
- package/lib/aws/ec2/route.provider.d.ts.map +1 -0
- package/lib/aws/ec2/route.provider.js +166 -0
- package/lib/aws/ec2/route.provider.js.map +1 -0
- package/lib/aws/ec2/subnet.d.ts +175 -0
- package/lib/aws/ec2/subnet.d.ts.map +1 -0
- package/lib/aws/ec2/subnet.js +4 -0
- package/lib/aws/ec2/subnet.js.map +1 -0
- package/lib/aws/ec2/subnet.provider.d.ts +4 -0
- package/lib/aws/ec2/subnet.provider.d.ts.map +1 -0
- package/lib/aws/ec2/subnet.provider.js +250 -0
- package/lib/aws/ec2/subnet.provider.js.map +1 -0
- package/lib/aws/ec2/vpc.d.ts +13 -8
- package/lib/aws/ec2/vpc.d.ts.map +1 -1
- package/lib/aws/ec2/vpc.js +1 -0
- package/lib/aws/ec2/vpc.js.map +1 -1
- package/lib/aws/ec2/vpc.provider.d.ts +1 -2
- package/lib/aws/ec2/vpc.provider.d.ts.map +1 -1
- package/lib/aws/ec2/vpc.provider.js +45 -37
- package/lib/aws/ec2/vpc.provider.js.map +1 -1
- package/lib/aws/index.d.ts +15 -19
- package/lib/aws/index.d.ts.map +1 -1
- package/lib/aws/index.js +8 -10
- package/lib/aws/index.js.map +1 -1
- package/lib/aws/lambda/consume.d.ts +10 -11
- package/lib/aws/lambda/consume.d.ts.map +1 -1
- package/lib/aws/lambda/consume.js +3 -3
- package/lib/aws/lambda/consume.js.map +1 -1
- package/lib/aws/lambda/function.d.ts +7 -7
- package/lib/aws/lambda/function.d.ts.map +1 -1
- package/lib/aws/lambda/function.handler.d.ts +1 -1
- package/lib/aws/lambda/function.handler.d.ts.map +1 -1
- package/lib/aws/lambda/function.handler.js.map +1 -1
- package/lib/aws/lambda/function.invoke.d.ts +6 -4
- package/lib/aws/lambda/function.invoke.d.ts.map +1 -1
- package/lib/aws/lambda/function.invoke.js +3 -1
- package/lib/aws/lambda/function.invoke.js.map +1 -1
- package/lib/aws/lambda/function.js +1 -1
- package/lib/aws/lambda/function.js.map +1 -1
- package/lib/aws/lambda/function.provider.d.ts +3 -2
- package/lib/aws/lambda/function.provider.d.ts.map +1 -1
- package/lib/aws/lambda/function.provider.js +28 -25
- package/lib/aws/lambda/function.provider.js.map +1 -1
- package/lib/aws/lambda/index.d.ts +1 -0
- package/lib/aws/lambda/index.d.ts.map +1 -1
- package/lib/aws/lambda/index.js +1 -0
- package/lib/aws/lambda/index.js.map +1 -1
- package/lib/aws/lambda/serve.d.ts +2 -4
- package/lib/aws/lambda/serve.d.ts.map +1 -1
- package/lib/aws/profile.d.ts +2 -2
- package/lib/aws/profile.d.ts.map +1 -1
- package/lib/aws/profile.js +1 -1
- package/lib/aws/profile.js.map +1 -1
- package/lib/aws/region.d.ts +14 -2
- package/lib/aws/region.d.ts.map +1 -1
- package/lib/aws/region.js +26 -1
- package/lib/aws/region.js.map +1 -1
- package/lib/aws/sqs/client.d.ts +1 -1
- package/lib/aws/sqs/client.d.ts.map +1 -1
- package/lib/aws/sqs/index.d.ts +1 -0
- package/lib/aws/sqs/index.d.ts.map +1 -1
- package/lib/aws/sqs/index.js +1 -0
- package/lib/aws/sqs/index.js.map +1 -1
- package/lib/aws/sqs/queue.consume.d.ts +1 -1
- package/lib/aws/sqs/queue.consume.d.ts.map +1 -1
- package/lib/aws/sqs/queue.consume.js +0 -1
- package/lib/aws/sqs/queue.consume.js.map +1 -1
- package/lib/aws/sqs/queue.d.ts +6 -4
- package/lib/aws/sqs/queue.d.ts.map +1 -1
- package/lib/aws/sqs/queue.event-source.d.ts +8 -6
- package/lib/aws/sqs/queue.event-source.d.ts.map +1 -1
- package/lib/aws/sqs/queue.event-source.js +26 -44
- package/lib/aws/sqs/queue.event-source.js.map +1 -1
- package/lib/aws/sqs/queue.js +1 -1
- package/lib/aws/sqs/queue.js.map +1 -1
- package/lib/aws/sqs/queue.provider.d.ts +2 -2
- package/lib/aws/sqs/queue.provider.d.ts.map +1 -1
- package/lib/aws/sqs/queue.provider.js +2 -1
- package/lib/aws/sqs/queue.provider.js.map +1 -1
- package/lib/aws/sqs/queue.send-message.d.ts +7 -5
- package/lib/aws/sqs/queue.send-message.d.ts.map +1 -1
- package/lib/aws/sqs/queue.send-message.js +4 -2
- package/lib/aws/sqs/queue.send-message.js.map +1 -1
- package/lib/binding.d.ts +12 -12
- package/lib/binding.d.ts.map +1 -1
- package/lib/binding.js.map +1 -1
- package/lib/cli/components/ApprovePlan.d.ts +2 -2
- package/lib/cli/components/ApprovePlan.d.ts.map +1 -1
- package/lib/cli/components/ApprovePlan.js.map +1 -1
- package/lib/cli/components/Plan.d.ts +2 -2
- package/lib/cli/components/Plan.d.ts.map +1 -1
- package/lib/cli/components/Plan.js.map +1 -1
- package/lib/cli/components/PlanProgress.d.ts +8 -4
- package/lib/cli/components/PlanProgress.d.ts.map +1 -1
- package/lib/cli/components/PlanProgress.js +11 -1
- package/lib/cli/components/PlanProgress.js.map +1 -1
- package/lib/cli/index.d.ts +384 -264
- package/lib/cli/index.d.ts.map +1 -1
- package/lib/cli/index.js +57 -65
- package/lib/cli/index.js.map +1 -1
- package/lib/cli/ink-service.d.ts +4 -0
- package/lib/cli/ink-service.d.ts.map +1 -0
- package/lib/cli/ink-service.js +43 -0
- package/lib/cli/ink-service.js.map +1 -0
- package/lib/cli/service.d.ts +21 -0
- package/lib/cli/service.d.ts.map +1 -0
- package/lib/cli/service.js +5 -0
- package/lib/cli/service.js.map +1 -0
- package/lib/cloudflare/account.d.ts +10 -0
- package/lib/cloudflare/account.d.ts.map +1 -0
- package/lib/cloudflare/account.js +24 -0
- package/lib/cloudflare/account.js.map +1 -0
- package/lib/cloudflare/api.d.ts +7 -7
- package/lib/cloudflare/api.d.ts.map +1 -1
- package/lib/cloudflare/api.js +18 -17
- package/lib/cloudflare/api.js.map +1 -1
- package/lib/cloudflare/config.d.ts +9 -0
- package/lib/cloudflare/config.d.ts.map +1 -0
- package/lib/cloudflare/config.js +1 -0
- package/lib/cloudflare/config.js.map +1 -0
- package/lib/cloudflare/index.d.ts +3 -1
- package/lib/cloudflare/index.d.ts.map +1 -1
- package/lib/cloudflare/index.js +3 -0
- package/lib/cloudflare/index.js.map +1 -1
- package/lib/cloudflare/kv/namespace.binding.d.ts +5 -3
- package/lib/cloudflare/kv/namespace.binding.d.ts.map +1 -1
- package/lib/cloudflare/kv/namespace.binding.js +1 -1
- package/lib/cloudflare/kv/namespace.binding.js.map +1 -1
- package/lib/cloudflare/kv/namespace.client.d.ts +1 -1
- package/lib/cloudflare/kv/namespace.d.ts +3 -2
- package/lib/cloudflare/kv/namespace.d.ts.map +1 -1
- package/lib/cloudflare/kv/namespace.js.map +1 -1
- package/lib/cloudflare/kv/namespace.provider.d.ts +3 -2
- package/lib/cloudflare/kv/namespace.provider.d.ts.map +1 -1
- package/lib/cloudflare/kv/namespace.provider.js +9 -7
- package/lib/cloudflare/kv/namespace.provider.js.map +1 -1
- package/lib/cloudflare/live.d.ts +5 -5
- package/lib/cloudflare/live.d.ts.map +1 -1
- package/lib/cloudflare/live.js +5 -8
- package/lib/cloudflare/live.js.map +1 -1
- package/lib/cloudflare/r2/bucket.binding.d.ts +5 -3
- package/lib/cloudflare/r2/bucket.binding.d.ts.map +1 -1
- package/lib/cloudflare/r2/bucket.binding.js +1 -1
- package/lib/cloudflare/r2/bucket.binding.js.map +1 -1
- package/lib/cloudflare/r2/bucket.d.ts +3 -2
- package/lib/cloudflare/r2/bucket.d.ts.map +1 -1
- package/lib/cloudflare/r2/bucket.js.map +1 -1
- package/lib/cloudflare/r2/bucket.provider.d.ts +3 -2
- package/lib/cloudflare/r2/bucket.provider.d.ts.map +1 -1
- package/lib/cloudflare/r2/bucket.provider.js +14 -8
- package/lib/cloudflare/r2/bucket.provider.js.map +1 -1
- package/lib/cloudflare/worker/assets.fetch.d.ts +3 -2
- package/lib/cloudflare/worker/assets.fetch.d.ts.map +1 -1
- package/lib/cloudflare/worker/assets.fetch.js +2 -1
- package/lib/cloudflare/worker/assets.fetch.js.map +1 -1
- package/lib/cloudflare/worker/assets.provider.d.ts +1 -1
- package/lib/cloudflare/worker/assets.provider.d.ts.map +1 -1
- package/lib/cloudflare/worker/index.d.ts +0 -1
- package/lib/cloudflare/worker/index.d.ts.map +1 -1
- package/lib/cloudflare/worker/worker.d.ts +5 -6
- package/lib/cloudflare/worker/worker.d.ts.map +1 -1
- package/lib/cloudflare/worker/worker.handler.d.ts +1 -1
- package/lib/cloudflare/worker/worker.handler.d.ts.map +1 -1
- package/lib/cloudflare/worker/worker.handler.js.map +1 -1
- package/lib/cloudflare/worker/worker.js.map +1 -1
- package/lib/cloudflare/worker/worker.provider.d.ts +3 -2
- package/lib/cloudflare/worker/worker.provider.d.ts.map +1 -1
- package/lib/cloudflare/worker/worker.provider.js +12 -7
- package/lib/cloudflare/worker/worker.provider.js.map +1 -1
- package/lib/cloudflare/worker/worker.serve.d.ts +7 -7
- package/lib/cloudflare/worker/worker.serve.d.ts.map +1 -1
- package/lib/cloudflare/worker/worker.serve.js.map +1 -1
- package/lib/data.d.ts +3 -0
- package/lib/data.d.ts.map +1 -0
- package/lib/data.js +8 -0
- package/lib/data.js.map +1 -0
- package/lib/destroy.d.ts +1 -1
- package/lib/destroy.d.ts.map +1 -1
- package/lib/destroy.js +1 -4
- package/lib/destroy.js.map +1 -1
- package/lib/diff.d.ts +18 -0
- package/lib/diff.d.ts.map +1 -0
- package/lib/diff.js +22 -0
- package/lib/diff.js.map +1 -0
- package/lib/env.d.ts +5 -0
- package/lib/env.d.ts.map +1 -1
- package/lib/env.js +15 -29
- package/lib/env.js.map +1 -1
- package/lib/event.d.ts +1 -1
- package/lib/event.d.ts.map +1 -1
- package/lib/exports.d.ts +9 -0
- package/lib/exports.d.ts.map +1 -0
- package/lib/exports.js +13 -0
- package/lib/exports.js.map +1 -0
- package/lib/index.d.ts +10 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +10 -5
- package/lib/index.js.map +1 -1
- package/lib/input.d.ts +32 -0
- package/lib/input.d.ts.map +1 -0
- package/lib/input.js +1 -0
- package/lib/input.js.map +1 -0
- package/lib/instance-id.d.ts +8 -0
- package/lib/instance-id.d.ts.map +1 -0
- package/lib/instance-id.js +12 -0
- package/lib/instance-id.js.map +1 -0
- package/lib/output.d.ts +145 -0
- package/lib/output.d.ts.map +1 -0
- package/lib/output.js +283 -0
- package/lib/output.js.map +1 -0
- package/lib/physical-name.d.ts +14 -1
- package/lib/physical-name.d.ts.map +1 -1
- package/lib/physical-name.js +41 -2
- package/lib/physical-name.js.map +1 -1
- package/lib/plan.d.ts +84 -58
- package/lib/plan.d.ts.map +1 -1
- package/lib/plan.js +504 -166
- package/lib/plan.js.map +1 -1
- package/lib/policy.d.ts +3 -4
- package/lib/policy.d.ts.map +1 -1
- package/lib/policy.js +0 -1
- package/lib/policy.js.map +1 -1
- package/lib/provider.d.ts +39 -24
- package/lib/provider.d.ts.map +1 -1
- package/lib/provider.js +9 -0
- package/lib/provider.js.map +1 -1
- package/lib/ref.d.ts +14 -0
- package/lib/ref.d.ts.map +1 -0
- package/lib/ref.js +21 -0
- package/lib/ref.js.map +1 -0
- package/lib/resource.d.ts +13 -8
- package/lib/resource.d.ts.map +1 -1
- package/lib/resource.js.map +1 -1
- package/lib/runtime.d.ts +7 -6
- package/lib/runtime.d.ts.map +1 -1
- package/lib/runtime.js.map +1 -1
- package/lib/service.d.ts +9 -6
- package/lib/service.d.ts.map +1 -1
- package/lib/service.js.map +1 -1
- package/lib/stack.d.ts +60 -0
- package/lib/stack.d.ts.map +1 -0
- package/lib/stack.js +11 -0
- package/lib/stack.js.map +1 -0
- package/lib/stage.d.ts +39 -0
- package/lib/stage.d.ts.map +1 -0
- package/lib/stage.js +32 -0
- package/lib/stage.js.map +1 -0
- package/lib/state.d.ts +135 -17
- package/lib/state.d.ts.map +1 -1
- package/lib/state.js +34 -30
- package/lib/state.js.map +1 -1
- package/lib/tags.d.ts +15 -0
- package/lib/tags.d.ts.map +1 -1
- package/lib/tags.js +27 -0
- package/lib/tags.js.map +1 -1
- package/lib/test.d.ts +25 -4
- package/lib/test.d.ts.map +1 -1
- package/lib/test.js +54 -14
- package/lib/test.js.map +1 -1
- package/lib/todo.d.ts +3 -0
- package/lib/todo.d.ts.map +1 -0
- package/lib/todo.js +3 -0
- package/lib/todo.js.map +1 -0
- package/lib/tsconfig.test.tsbuildinfo +1 -1
- package/lib/type.d.ts +3 -0
- package/lib/type.d.ts.map +1 -1
- package/lib/unknown.d.ts +4 -0
- package/lib/unknown.d.ts.map +1 -0
- package/lib/unknown.js +4 -0
- package/lib/unknown.js.map +1 -0
- package/lib/user.d.ts +3 -0
- package/lib/user.d.ts.map +1 -0
- package/lib/user.js +3 -0
- package/lib/user.js.map +1 -0
- package/lib/util.d.ts +6 -0
- package/lib/util.d.ts.map +1 -0
- package/lib/util.js +9 -0
- package/lib/util.js.map +1 -0
- package/package.json +18 -12
- package/src/$.ts +17 -0
- package/src/app.ts +3 -32
- package/src/apply.ts +824 -452
- package/src/assert-never.ts +18 -0
- package/src/aws/account.ts +23 -3
- package/src/aws/client.ts +0 -1
- package/src/aws/config.ts +16 -0
- package/src/aws/credentials.ts +212 -177
- package/src/aws/dynamodb/index.ts +3 -3
- package/src/aws/dynamodb/table.get-item.ts +5 -9
- package/src/aws/dynamodb/table.provider.ts +36 -39
- package/src/aws/dynamodb/table.ts +29 -84
- package/src/aws/ec2/index.ts +12 -0
- package/src/aws/ec2/internet-gateway.provider.ts +316 -0
- package/src/aws/ec2/internet-gateway.ts +79 -0
- package/src/aws/ec2/route-table-association.provider.ts +214 -0
- package/src/aws/ec2/route-table-association.ts +82 -0
- package/src/aws/ec2/route-table.provider.ts +306 -0
- package/src/aws/ec2/route-table.ts +175 -0
- package/src/aws/ec2/route.provider.ts +213 -0
- package/src/aws/ec2/route.ts +192 -0
- package/src/aws/ec2/subnet.provider.ts +358 -0
- package/src/aws/ec2/subnet.ts +213 -0
- package/src/aws/ec2/vpc.provider.ts +58 -50
- package/src/aws/ec2/vpc.ts +21 -8
- package/src/aws/index.ts +49 -40
- package/src/aws/lambda/consume.ts +8 -7
- package/src/aws/lambda/function.handler.ts +1 -1
- package/src/aws/lambda/function.invoke.ts +6 -2
- package/src/aws/lambda/function.provider.ts +41 -32
- package/src/aws/lambda/function.ts +7 -4
- package/src/aws/lambda/index.ts +2 -0
- package/src/aws/profile.ts +1 -4
- package/src/aws/region.ts +42 -3
- package/src/aws/sqs/index.ts +2 -0
- package/src/aws/sqs/queue.consume.ts +1 -1
- package/src/aws/sqs/queue.event-source.ts +29 -55
- package/src/aws/sqs/queue.provider.ts +10 -2
- package/src/aws/sqs/queue.send-message.ts +5 -8
- package/src/aws/sqs/queue.ts +9 -4
- package/src/binding.ts +19 -19
- package/src/cli/components/ApprovePlan.tsx +2 -2
- package/src/cli/components/Plan.tsx +3 -2
- package/src/cli/components/PlanProgress.tsx +32 -14
- package/src/cli/index.ts +2 -6
- package/src/cli/ink-service.tsx +61 -0
- package/src/cli/service.ts +23 -0
- package/src/cloudflare/account.ts +37 -0
- package/src/cloudflare/api.ts +33 -29
- package/src/cloudflare/config.ts +7 -0
- package/src/cloudflare/index.ts +3 -1
- package/src/cloudflare/kv/namespace.binding.ts +3 -1
- package/src/cloudflare/kv/namespace.provider.ts +10 -8
- package/src/cloudflare/kv/namespace.ts +3 -2
- package/src/cloudflare/live.ts +11 -17
- package/src/cloudflare/r2/bucket.binding.ts +3 -1
- package/src/cloudflare/r2/bucket.provider.ts +16 -9
- package/src/cloudflare/r2/bucket.ts +8 -2
- package/src/cloudflare/worker/assets.fetch.ts +3 -1
- package/src/cloudflare/worker/assets.provider.ts +1 -1
- package/src/cloudflare/worker/index.ts +0 -2
- package/src/cloudflare/worker/worker.handler.ts +1 -1
- package/src/cloudflare/worker/worker.provider.ts +21 -14
- package/src/cloudflare/worker/worker.serve.ts +5 -2
- package/src/cloudflare/worker/worker.ts +4 -3
- package/src/data.ts +18 -0
- package/src/destroy.ts +1 -5
- package/src/diff.ts +48 -0
- package/src/env.ts +20 -32
- package/src/event.ts +6 -0
- package/src/exports.ts +21 -0
- package/src/index.ts +10 -5
- package/src/input.ts +81 -0
- package/src/instance-id.ts +16 -0
- package/src/output.ts +542 -0
- package/src/physical-name.ts +57 -2
- package/src/plan.ts +757 -278
- package/src/policy.ts +3 -5
- package/src/provider.ts +70 -31
- package/src/ref.ts +48 -0
- package/src/resource.ts +70 -10
- package/src/runtime.ts +15 -8
- package/src/service.ts +11 -7
- package/src/stack.ts +116 -0
- package/src/stage.ts +85 -0
- package/src/state.ts +269 -76
- package/src/tags.ts +31 -0
- package/src/test.ts +118 -17
- package/src/todo.ts +4 -0
- package/src/type.ts +4 -0
- package/src/unknown.ts +6 -0
- package/src/user.ts +4 -0
- package/src/util.ts +21 -0
- package/lib/approve.d.ts +0 -15
- package/lib/approve.d.ts.map +0 -1
- package/lib/approve.js +0 -7
- package/lib/approve.js.map +0 -1
- package/lib/cli/approve.d.ts +0 -4
- package/lib/cli/approve.d.ts.map +0 -1
- package/lib/cli/approve.js +0 -18
- package/lib/cli/approve.js.map +0 -1
- package/lib/cli/clack.d.ts +0 -14
- package/lib/cli/clack.d.ts.map +0 -1
- package/lib/cli/clack.js +0 -12
- package/lib/cli/clack.js.map +0 -1
- package/lib/cli/main.d.ts +0 -2
- package/lib/cli/main.d.ts.map +0 -1
- package/lib/cli/main.js +0 -1
- package/lib/cli/main.js.map +0 -1
- package/lib/cli/plan.d.ts +0 -13
- package/lib/cli/plan.d.ts.map +0 -1
- package/lib/cli/plan.js +0 -1
- package/lib/cli/plan.js.map +0 -1
- package/lib/cli/progress.d.ts +0 -7
- package/lib/cli/progress.d.ts.map +0 -1
- package/lib/cli/progress.js +0 -30
- package/lib/cli/progress.js.map +0 -1
- package/lib/cli/spinner.d.ts +0 -2
- package/lib/cli/spinner.d.ts.map +0 -1
- package/lib/cli/spinner.js +0 -13
- package/lib/cli/spinner.js.map +0 -1
- package/src/approve.ts +0 -13
- package/src/cli/approve.tsx +0 -30
- package/src/cli/clack.ts +0 -22
- package/src/cli/main.ts +0 -0
- package/src/cli/plan.ts +0 -16
- package/src/cli/progress.tsx +0 -46
- package/src/cli/spinner.ts +0 -14
package/src/plan.ts
CHANGED
|
@@ -2,18 +2,32 @@ import * as Context from "effect/Context";
|
|
|
2
2
|
import * as Data from "effect/Data";
|
|
3
3
|
import * as Effect from "effect/Effect";
|
|
4
4
|
import { omit } from "effect/Struct";
|
|
5
|
+
import { App } from "./app.ts";
|
|
5
6
|
import type {
|
|
6
7
|
AnyBinding,
|
|
7
8
|
BindingDiffProps,
|
|
8
9
|
BindingService,
|
|
9
10
|
} from "./binding.ts";
|
|
10
11
|
import type { Capability } from "./capability.ts";
|
|
11
|
-
import type {
|
|
12
|
+
import type { Diff, NoopDiff, UpdateDiff } from "./diff.ts";
|
|
13
|
+
import * as Output from "./output.ts";
|
|
12
14
|
import type { Instance } from "./policy.ts";
|
|
13
|
-
import
|
|
14
|
-
import type
|
|
15
|
+
import type { Provider } from "./provider.ts";
|
|
16
|
+
import { type ProviderService, getProviderByType } from "./provider.ts";
|
|
17
|
+
import type { AnyResource, Resource, ResourceTags } from "./resource.ts";
|
|
15
18
|
import { isService, type IService, type Service } from "./service.ts";
|
|
16
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
type CreatedResourceState,
|
|
21
|
+
type CreatingResourceState,
|
|
22
|
+
type ReplacedResourceState,
|
|
23
|
+
type ReplacingResourceState,
|
|
24
|
+
type UpdatedResourceState,
|
|
25
|
+
type UpdatingReourceState,
|
|
26
|
+
State,
|
|
27
|
+
StateStoreError,
|
|
28
|
+
type ResourceState,
|
|
29
|
+
} from "./state.ts";
|
|
30
|
+
import { asEffect } from "./util.ts";
|
|
17
31
|
|
|
18
32
|
export type PlanError = never;
|
|
19
33
|
|
|
@@ -76,311 +90,669 @@ export const isCRUD = (node: any): node is CRUD => {
|
|
|
76
90
|
/**
|
|
77
91
|
* A node in the plan that represents a resource CRUD operation.
|
|
78
92
|
*/
|
|
79
|
-
export type CRUD<R extends Resource =
|
|
93
|
+
export type CRUD<R extends Resource = AnyResource> =
|
|
80
94
|
| Create<R>
|
|
81
95
|
| Update<R>
|
|
82
96
|
| Delete<R>
|
|
83
97
|
| Replace<R>
|
|
84
98
|
| NoopUpdate<R>;
|
|
85
99
|
|
|
86
|
-
export type Apply<R extends Resource =
|
|
100
|
+
export type Apply<R extends Resource = AnyResource> =
|
|
87
101
|
| Create<R>
|
|
88
102
|
| Update<R>
|
|
89
103
|
| Replace<R>
|
|
90
104
|
| NoopUpdate<R>;
|
|
91
105
|
|
|
92
|
-
|
|
93
|
-
...node,
|
|
94
|
-
toString(): string {
|
|
95
|
-
return `${this.action.charAt(0).toUpperCase()}${this.action.slice(1)}(${this.resource})`;
|
|
96
|
-
},
|
|
97
|
-
[Symbol.toStringTag]() {
|
|
98
|
-
return this.toString();
|
|
99
|
-
},
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
export type Create<R extends Resource> = {
|
|
103
|
-
action: "create";
|
|
106
|
+
export interface BaseNode<R extends Resource = AnyResource> {
|
|
104
107
|
resource: R;
|
|
105
|
-
|
|
106
|
-
provider: ProviderService;
|
|
107
|
-
attributes: R["attr"];
|
|
108
|
+
provider: ProviderService<R>;
|
|
108
109
|
bindings: BindNode[];
|
|
109
|
-
|
|
110
|
+
downstream: string[];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export interface Create<R extends Resource = AnyResource> extends BaseNode<R> {
|
|
114
|
+
action: "create";
|
|
115
|
+
props: any;
|
|
116
|
+
state: CreatingResourceState | undefined;
|
|
117
|
+
}
|
|
110
118
|
|
|
111
|
-
export
|
|
119
|
+
export interface Update<R extends Resource = AnyResource> extends BaseNode<R> {
|
|
112
120
|
action: "update";
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
+
props: any;
|
|
122
|
+
state:
|
|
123
|
+
| CreatedResourceState
|
|
124
|
+
| UpdatedResourceState
|
|
125
|
+
| UpdatingReourceState
|
|
126
|
+
// the props can change after creating the replacement resource,
|
|
127
|
+
// so Apply needs to handle updates and then continue with cleaning up the replaced graph
|
|
128
|
+
| ReplacedResourceState;
|
|
129
|
+
}
|
|
121
130
|
|
|
122
|
-
export
|
|
131
|
+
export interface Delete<R extends Resource = AnyResource> extends BaseNode<R> {
|
|
123
132
|
action: "delete";
|
|
124
|
-
resource
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
provider: ProviderService;
|
|
128
|
-
bindings: BindNode[];
|
|
129
|
-
attributes: R["attr"];
|
|
130
|
-
downstream: string[];
|
|
131
|
-
};
|
|
133
|
+
// a resource can be deleted no matter what state it's in
|
|
134
|
+
state: ResourceState;
|
|
135
|
+
}
|
|
132
136
|
|
|
133
|
-
export
|
|
137
|
+
export interface NoopUpdate<R extends Resource = AnyResource>
|
|
138
|
+
extends BaseNode<R> {
|
|
134
139
|
action: "noop";
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
bindings: BindNode[];
|
|
138
|
-
};
|
|
140
|
+
state: CreatedResourceState | UpdatedResourceState;
|
|
141
|
+
}
|
|
139
142
|
|
|
140
|
-
export
|
|
143
|
+
export interface Replace<R extends Resource = AnyResource> extends BaseNode<R> {
|
|
141
144
|
action: "replace";
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
145
|
+
props: any;
|
|
146
|
+
deleteFirst: boolean;
|
|
147
|
+
state:
|
|
148
|
+
| CreatingResourceState
|
|
149
|
+
| CreatedResourceState
|
|
150
|
+
| UpdatingReourceState
|
|
151
|
+
| UpdatedResourceState
|
|
152
|
+
| ReplacingResourceState
|
|
153
|
+
| ReplacedResourceState;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export type ResourceGraph<Resources extends Service | Resource> = ToGraph<
|
|
157
|
+
TraverseResources<Resources>
|
|
158
|
+
>;
|
|
159
|
+
|
|
160
|
+
export type TraverseResources<Resources extends Service | Resource> =
|
|
161
|
+
| Resources
|
|
162
|
+
| BoundResources<Resources>
|
|
163
|
+
| TransitiveResources<Resources>;
|
|
164
|
+
|
|
165
|
+
type ToGraph<Resources extends Service | Resource> = {
|
|
166
|
+
[ID in Resources["id"]]: Apply<Extract<Resources, { id: ID }>>;
|
|
150
167
|
};
|
|
151
168
|
|
|
152
|
-
export type
|
|
153
|
-
|
|
169
|
+
export type BoundResources<Resources extends Service | Resource> = NeverUnknown<
|
|
170
|
+
Extract<
|
|
171
|
+
Resources,
|
|
172
|
+
IService
|
|
173
|
+
>["props"]["bindings"]["capabilities"][number]["resource"]
|
|
174
|
+
>;
|
|
175
|
+
|
|
176
|
+
// finds transitive dependencies at most two levels deep
|
|
177
|
+
// TODO(sam): figure out an efficient way to do arbitrary depth
|
|
178
|
+
export type TransitiveResources<
|
|
179
|
+
Resources extends Service | Resource,
|
|
180
|
+
Found extends Service | Resource = never,
|
|
181
|
+
> = Extract<
|
|
182
|
+
| Found
|
|
183
|
+
| {
|
|
184
|
+
[prop in keyof Resources["props"]]: IsAny<
|
|
185
|
+
Resources["props"][prop]
|
|
186
|
+
> extends true
|
|
187
|
+
? Found
|
|
188
|
+
: Resources["props"][prop] extends { kind: "alchemy/Policy" }
|
|
189
|
+
? Found
|
|
190
|
+
: Resources["props"][prop] extends Output.Output<any, infer Src, any>
|
|
191
|
+
? Src extends Found
|
|
192
|
+
? Found
|
|
193
|
+
: TransitiveResources<Src, Src | Found>
|
|
194
|
+
: {
|
|
195
|
+
[p in keyof Resources["props"][prop]]: IsAny<
|
|
196
|
+
Resources["props"][prop][p]
|
|
197
|
+
> extends true
|
|
198
|
+
? Found
|
|
199
|
+
: Resources["props"][prop][p] extends Output.Output<
|
|
200
|
+
any,
|
|
201
|
+
infer Src,
|
|
202
|
+
any
|
|
203
|
+
>
|
|
204
|
+
? Src extends Found
|
|
205
|
+
? Found
|
|
206
|
+
: TransitiveResources<Src, Src | Found>
|
|
207
|
+
: Found;
|
|
208
|
+
}[keyof Resources["props"][prop]];
|
|
209
|
+
}[keyof Resources["props"]],
|
|
210
|
+
Service | Resource
|
|
211
|
+
>;
|
|
212
|
+
|
|
213
|
+
export type Providers<Resources extends Service | Resource> =
|
|
214
|
+
| ResourceProviders<Resources>
|
|
215
|
+
| BindingTags<Resources>;
|
|
216
|
+
|
|
217
|
+
export type ResourceProviders<Res extends Service | Resource> = Res extends any
|
|
218
|
+
? Provider<Extract<Res["base"], Service | Resource>>
|
|
219
|
+
: never;
|
|
220
|
+
|
|
221
|
+
export type BindingTags<Resources extends Service | Resource> = NeverUnknown<
|
|
222
|
+
Extract<Resources, Service>["props"]["bindings"]["tags"][number]
|
|
223
|
+
>;
|
|
224
|
+
|
|
225
|
+
type NeverUnknown<T> = unknown extends T ? never : T;
|
|
226
|
+
|
|
227
|
+
type IsAny<T> = 0 extends 1 & T ? true : false;
|
|
228
|
+
|
|
229
|
+
export type DerivePlan<Resources extends Service | Resource> = {
|
|
154
230
|
resources: {
|
|
155
|
-
[
|
|
231
|
+
[ID in keyof ResourceGraph<Resources>]: ResourceGraph<Resources>[ID];
|
|
156
232
|
};
|
|
157
233
|
deletions: {
|
|
158
|
-
[
|
|
234
|
+
[ID in string]: Delete<AnyResource>;
|
|
159
235
|
};
|
|
160
236
|
};
|
|
161
237
|
|
|
162
|
-
export
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
>({
|
|
166
|
-
phase,
|
|
167
|
-
resources,
|
|
168
|
-
}: {
|
|
169
|
-
phase: Phase;
|
|
170
|
-
resources: Resources;
|
|
171
|
-
}) => {
|
|
172
|
-
type Services = Extract<Resources[number], IService>[];
|
|
173
|
-
type ServiceIDs = Services[number]["id"];
|
|
174
|
-
type ServiceHosts = {
|
|
175
|
-
[ID in ServiceIDs]: Extract<Services[number], Service<Extract<ID, string>>>;
|
|
238
|
+
export type IPlan = {
|
|
239
|
+
resources: {
|
|
240
|
+
[id in string]: Apply<any>;
|
|
176
241
|
};
|
|
177
|
-
|
|
178
|
-
[
|
|
179
|
-
}[ServiceIDs];
|
|
180
|
-
type UpstreamResources = {
|
|
181
|
-
[ID in ServiceIDs]: Extract<
|
|
182
|
-
ServiceHosts[ID]["props"]["bindings"]["capabilities"][number]["resource"],
|
|
183
|
-
Resource
|
|
184
|
-
>;
|
|
185
|
-
}[ServiceIDs];
|
|
186
|
-
type ExplicitResources = Resources[number];
|
|
187
|
-
type ResourceGraph = {
|
|
188
|
-
[ID in ServiceIDs]: Apply<Extract<Instance<ServiceHosts[ID]>, Resource>>;
|
|
189
|
-
} & {
|
|
190
|
-
[ID in UpstreamResources["id"]]: Apply<
|
|
191
|
-
Extract<UpstreamResources, { id: ID }>
|
|
192
|
-
>;
|
|
193
|
-
} & {
|
|
194
|
-
[ID in ExplicitResources["id"]]: Apply<
|
|
195
|
-
Extract<ExplicitResources, { id: ID }>
|
|
196
|
-
>;
|
|
242
|
+
deletions: {
|
|
243
|
+
[id in string]?: Delete<Resource>;
|
|
197
244
|
};
|
|
245
|
+
};
|
|
198
246
|
|
|
199
|
-
|
|
247
|
+
export type Plan<Resources extends Service | Resource> = Effect.Effect<
|
|
248
|
+
DerivePlan<Resources>,
|
|
249
|
+
| CannotReplacePartiallyReplacedResource
|
|
250
|
+
| DeleteResourceHasDownstreamDependencies,
|
|
251
|
+
Providers<Resources> | State
|
|
252
|
+
>;
|
|
253
|
+
|
|
254
|
+
export const plan = <const Resources extends (Service | Resource)[]>(
|
|
255
|
+
..._resources: Resources
|
|
256
|
+
): Plan<Instance<Resources[number]>> =>
|
|
257
|
+
Effect.gen(function* () {
|
|
200
258
|
const state = yield* State;
|
|
201
259
|
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
260
|
+
const findResources = (
|
|
261
|
+
resource: Service | Resource,
|
|
262
|
+
visited: Set<string>,
|
|
263
|
+
): (Service | Resource)[] => {
|
|
264
|
+
if (visited.has(resource.id)) {
|
|
265
|
+
return [];
|
|
266
|
+
}
|
|
267
|
+
visited.add(resource.id);
|
|
268
|
+
const upstream = Object.values(Output.upstreamAny(resource.props)) as (
|
|
269
|
+
| Service
|
|
270
|
+
| Resource
|
|
271
|
+
)[];
|
|
272
|
+
return [
|
|
273
|
+
resource,
|
|
274
|
+
...upstream,
|
|
275
|
+
...upstream.flatMap((r) => findResources(r, visited)),
|
|
276
|
+
];
|
|
277
|
+
};
|
|
278
|
+
const resources = _resources
|
|
279
|
+
.flatMap((r) => findResources(r, new Set()))
|
|
280
|
+
.filter((r, i, arr) => arr.findIndex((r2) => r2.id === r.id) === i);
|
|
281
|
+
|
|
282
|
+
// TODO(sam): rename terminology to Stack
|
|
283
|
+
const app = yield* App;
|
|
284
|
+
|
|
285
|
+
const resourceIds = yield* state.list({
|
|
286
|
+
stack: app.name,
|
|
287
|
+
stage: app.stage,
|
|
288
|
+
});
|
|
289
|
+
const oldResources = yield* Effect.all(
|
|
290
|
+
resourceIds.map((id) =>
|
|
291
|
+
state.get({ stack: app.name, stage: app.stage, resourceId: id }),
|
|
292
|
+
),
|
|
205
293
|
);
|
|
294
|
+
|
|
295
|
+
type ResolveEffect<T> = Effect.Effect<T, ResolveErr, ResolveReq>;
|
|
296
|
+
type ResolveErr = StateStoreError;
|
|
297
|
+
type ResolveReq =
|
|
298
|
+
| Context.TagClass<
|
|
299
|
+
Provider<Resource<string, string, any, any>>,
|
|
300
|
+
string,
|
|
301
|
+
ProviderService<Resource<string, string, any, any>>
|
|
302
|
+
>
|
|
303
|
+
| State;
|
|
304
|
+
|
|
305
|
+
const resolvedResources: Record<
|
|
306
|
+
string,
|
|
307
|
+
ResolveEffect<
|
|
308
|
+
| {
|
|
309
|
+
[attr in string]: any;
|
|
310
|
+
}
|
|
311
|
+
| undefined
|
|
312
|
+
>
|
|
313
|
+
> = {};
|
|
314
|
+
|
|
315
|
+
const resolveResource = (
|
|
316
|
+
resourceExpr: Output.ResourceExpr<any, any, any>,
|
|
317
|
+
) =>
|
|
318
|
+
Effect.gen(function* () {
|
|
319
|
+
return yield* (resolvedResources[resourceExpr.src.id] ??=
|
|
320
|
+
yield* Effect.cached(
|
|
321
|
+
Effect.gen(function* () {
|
|
322
|
+
const resource = resourceExpr.src as Resource & {
|
|
323
|
+
provider: ResourceTags<Resource<string, string, any, any>>;
|
|
324
|
+
};
|
|
325
|
+
const provider = yield* resource.provider.tag;
|
|
326
|
+
const props = yield* resolveInput(resource.props);
|
|
327
|
+
const oldState = yield* state.get({
|
|
328
|
+
stack: app.name,
|
|
329
|
+
stage: app.stage,
|
|
330
|
+
resourceId: resource.id,
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
if (!oldState || oldState.status === "creating") {
|
|
334
|
+
return resourceExpr;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const oldProps =
|
|
338
|
+
oldState.status === "created" ||
|
|
339
|
+
oldState.status === "updated" ||
|
|
340
|
+
oldState.status === "replaced"
|
|
341
|
+
? // if we're in a stable state, then just use the props
|
|
342
|
+
oldState.props
|
|
343
|
+
: // if we failed to update or replace, compare with the last known stable props
|
|
344
|
+
oldState.status === "updating" ||
|
|
345
|
+
oldState.status === "replacing"
|
|
346
|
+
? oldState.old.props
|
|
347
|
+
: // TODO(sam): it kinda doesn't make sense to diff with a "deleting" state
|
|
348
|
+
oldState.props;
|
|
349
|
+
|
|
350
|
+
const diff = yield* provider.diff
|
|
351
|
+
? provider.diff({
|
|
352
|
+
id: resource.id,
|
|
353
|
+
olds: oldProps,
|
|
354
|
+
instanceId: oldState.instanceId,
|
|
355
|
+
news: props,
|
|
356
|
+
output: oldState.attr,
|
|
357
|
+
})
|
|
358
|
+
: Effect.succeed(undefined);
|
|
359
|
+
|
|
360
|
+
const stables: string[] = [
|
|
361
|
+
...(provider.stables ?? []),
|
|
362
|
+
...(diff?.stables ?? []),
|
|
363
|
+
];
|
|
364
|
+
|
|
365
|
+
const withStables = (output: any) =>
|
|
366
|
+
stables.length > 0
|
|
367
|
+
? new Output.ResourceExpr(
|
|
368
|
+
resourceExpr.src,
|
|
369
|
+
Object.fromEntries(
|
|
370
|
+
stables.map((stable) => [stable, output?.[stable]]),
|
|
371
|
+
),
|
|
372
|
+
)
|
|
373
|
+
: // if there are no stable properties, treat every property as changed
|
|
374
|
+
resourceExpr;
|
|
375
|
+
|
|
376
|
+
if (diff == null) {
|
|
377
|
+
if (arePropsChanged(oldProps, props)) {
|
|
378
|
+
// the props have changed but the provider did not provide any hints as to what is stable
|
|
379
|
+
// so we must assume everything has changed
|
|
380
|
+
return withStables(oldState?.attr);
|
|
381
|
+
}
|
|
382
|
+
} else if (diff.action === "update") {
|
|
383
|
+
return withStables(oldState?.attr);
|
|
384
|
+
} else if (diff.action === "replace") {
|
|
385
|
+
return resourceExpr;
|
|
386
|
+
}
|
|
387
|
+
if (
|
|
388
|
+
oldState.status === "created" ||
|
|
389
|
+
oldState.status === "updated" ||
|
|
390
|
+
oldState.status === "replaced"
|
|
391
|
+
) {
|
|
392
|
+
// we can safely return the attributes if we know they have stabilized
|
|
393
|
+
return oldState?.attr;
|
|
394
|
+
} else {
|
|
395
|
+
// we must assume the resource doesn't exist if it hasn't stabilized
|
|
396
|
+
return resourceExpr;
|
|
397
|
+
}
|
|
398
|
+
}),
|
|
399
|
+
));
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
const resolveInput = (input: any): ResolveEffect<any> =>
|
|
403
|
+
Effect.gen(function* () {
|
|
404
|
+
if (!input) {
|
|
405
|
+
return input;
|
|
406
|
+
} else if (Output.isExpr(input)) {
|
|
407
|
+
return yield* resolveOutput(input);
|
|
408
|
+
} else if (Array.isArray(input)) {
|
|
409
|
+
return yield* Effect.all(input.map(resolveInput));
|
|
410
|
+
} else if (typeof input === "object") {
|
|
411
|
+
return Object.fromEntries(
|
|
412
|
+
yield* Effect.all(
|
|
413
|
+
Object.entries(input).map(([key, value]) =>
|
|
414
|
+
resolveInput(value).pipe(Effect.map((value) => [key, value])),
|
|
415
|
+
),
|
|
416
|
+
),
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
return input;
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
const resolveOutput = (expr: Output.Expr<any>): ResolveEffect<any> =>
|
|
423
|
+
Effect.gen(function* () {
|
|
424
|
+
if (Output.isResourceExpr(expr)) {
|
|
425
|
+
return yield* resolveResource(expr);
|
|
426
|
+
} else if (Output.isPropExpr(expr)) {
|
|
427
|
+
const upstream = yield* resolveOutput(expr.expr);
|
|
428
|
+
return upstream?.[expr.identifier];
|
|
429
|
+
} else if (Output.isApplyExpr(expr)) {
|
|
430
|
+
const upstream = yield* resolveOutput(expr.expr);
|
|
431
|
+
return Output.hasOutputs(upstream) ? expr : expr.f(upstream);
|
|
432
|
+
} else if (Output.isEffectExpr(expr)) {
|
|
433
|
+
const upstream = yield* resolveOutput(expr.expr);
|
|
434
|
+
return Output.hasOutputs(upstream) ? expr : yield* expr.f(upstream);
|
|
435
|
+
} else if (Output.isAllExpr(expr)) {
|
|
436
|
+
return yield* Effect.all(expr.outs.map(resolveOutput));
|
|
437
|
+
}
|
|
438
|
+
return yield* Effect.die(new Error("Not implemented yet"));
|
|
439
|
+
});
|
|
440
|
+
|
|
206
441
|
// map of resource ID -> its downstream dependencies (resources that depend on it)
|
|
207
|
-
const
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
)
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
)
|
|
215
|
-
.flatMap((resource) =>
|
|
216
|
-
resource.bindings.flatMap(({ binding }) => [
|
|
217
|
-
[binding.capability.resource.id, binding.capability.resource],
|
|
218
|
-
]),
|
|
219
|
-
)
|
|
220
|
-
.reduce(
|
|
221
|
-
(acc, [id, resourceId]) => ({
|
|
222
|
-
...acc,
|
|
223
|
-
[id]: [...(acc[id] ?? []), resourceId],
|
|
224
|
-
}),
|
|
225
|
-
{} as Record<string, string[]>,
|
|
226
|
-
);
|
|
442
|
+
const oldDownstreamDependencies: {
|
|
443
|
+
[resourceId: string]: string[];
|
|
444
|
+
} = Object.fromEntries(
|
|
445
|
+
oldResources
|
|
446
|
+
.filter((resource) => !!resource)
|
|
447
|
+
.map((resource) => [resource.logicalId, resource.downstream]),
|
|
448
|
+
);
|
|
227
449
|
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
450
|
+
const newUpstreamDependencies: {
|
|
451
|
+
[resourceId: string]: string[];
|
|
452
|
+
} = Object.fromEntries(
|
|
453
|
+
resources.map((resource) => [
|
|
454
|
+
resource.id,
|
|
455
|
+
[
|
|
456
|
+
...Object.values(Output.upstreamAny(resource.props)).map((r) => r.id),
|
|
457
|
+
...(isService(resource)
|
|
458
|
+
? resource.props.bindings.capabilities.map((cap) => cap.resource.id)
|
|
459
|
+
: []),
|
|
460
|
+
],
|
|
461
|
+
]),
|
|
462
|
+
);
|
|
463
|
+
|
|
464
|
+
const newDownstreamDependencies: {
|
|
465
|
+
[resourceId: string]: string[];
|
|
466
|
+
} = Object.fromEntries(
|
|
467
|
+
resources.map((resource) => [
|
|
468
|
+
resource.id,
|
|
469
|
+
Object.entries(newUpstreamDependencies)
|
|
470
|
+
.filter(([_, downstream]) => downstream.includes(resource.id))
|
|
471
|
+
.map(([id]) => id),
|
|
472
|
+
]),
|
|
473
|
+
);
|
|
474
|
+
|
|
475
|
+
const resourceGraph = Object.fromEntries(
|
|
476
|
+
(yield* Effect.all(
|
|
477
|
+
resources
|
|
478
|
+
.flatMap((resource) => [
|
|
479
|
+
...(isService(resource)
|
|
480
|
+
? resource.props.bindings.capabilities.map(
|
|
481
|
+
(cap: Capability) => cap.resource as Resource,
|
|
244
482
|
)
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
)
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
});
|
|
333
|
-
} else {
|
|
334
|
-
return Node<NoopUpdate<Resource>>({
|
|
335
|
-
action: "noop",
|
|
336
|
-
resource,
|
|
337
|
-
bindings,
|
|
338
|
-
// phantom
|
|
339
|
-
attributes: undefined!,
|
|
340
|
-
});
|
|
341
|
-
}
|
|
342
|
-
}),
|
|
483
|
+
: []),
|
|
484
|
+
...Object.values(Output.upstreamAny(resource.props)),
|
|
485
|
+
resource,
|
|
486
|
+
])
|
|
487
|
+
.filter(
|
|
488
|
+
(node, i, arr) => arr.findIndex((n) => n.id === node.id) === i,
|
|
489
|
+
)
|
|
490
|
+
.map(
|
|
491
|
+
Effect.fn(function* (node) {
|
|
492
|
+
const id = node.id;
|
|
493
|
+
const resource = node as Resource & {
|
|
494
|
+
provider: ResourceTags<Resource<string, string, any, any>>;
|
|
495
|
+
};
|
|
496
|
+
const news = yield* resolveInput(resource.props);
|
|
497
|
+
|
|
498
|
+
const oldState = yield* state.get({
|
|
499
|
+
stack: app.name,
|
|
500
|
+
stage: app.stage,
|
|
501
|
+
resourceId: id,
|
|
502
|
+
});
|
|
503
|
+
const provider = yield* resource.provider.tag;
|
|
504
|
+
|
|
505
|
+
const downstream = newDownstreamDependencies[id] ?? [];
|
|
506
|
+
|
|
507
|
+
const bindings = isService(node)
|
|
508
|
+
? yield* diffBindings({
|
|
509
|
+
oldState,
|
|
510
|
+
bindings: (
|
|
511
|
+
node.props.bindings as unknown as {
|
|
512
|
+
bindings: AnyBinding[];
|
|
513
|
+
}
|
|
514
|
+
).bindings,
|
|
515
|
+
target: {
|
|
516
|
+
id: node.id,
|
|
517
|
+
props: node.props,
|
|
518
|
+
// TODO(sam): pick the right ones based on old status
|
|
519
|
+
oldAttr: oldState?.attr,
|
|
520
|
+
oldProps: oldState?.props,
|
|
521
|
+
},
|
|
522
|
+
})
|
|
523
|
+
: []; // TODO(sam): return undefined instead of empty array
|
|
524
|
+
|
|
525
|
+
const Node = <T extends Apply>(
|
|
526
|
+
node: Omit<
|
|
527
|
+
T,
|
|
528
|
+
"provider" | "resource" | "bindings" | "downstream"
|
|
529
|
+
>,
|
|
530
|
+
) =>
|
|
531
|
+
({
|
|
532
|
+
...node,
|
|
533
|
+
provider,
|
|
534
|
+
resource,
|
|
535
|
+
bindings,
|
|
536
|
+
downstream,
|
|
537
|
+
}) as any as T;
|
|
538
|
+
|
|
539
|
+
// handle empty and intermediate (non-final) states:
|
|
540
|
+
if (oldState === undefined) {
|
|
541
|
+
return Node<Create<Resource>>({
|
|
542
|
+
action: "create",
|
|
543
|
+
props: news,
|
|
544
|
+
state: oldState,
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// TODO(sam): is this correct for all possible states a resource can be in?
|
|
549
|
+
const oldProps = oldState.props;
|
|
550
|
+
|
|
551
|
+
const diff = yield* asEffect(
|
|
552
|
+
provider.diff
|
|
553
|
+
? provider.diff({
|
|
554
|
+
id,
|
|
555
|
+
olds: oldProps,
|
|
556
|
+
instanceId: oldState.instanceId,
|
|
557
|
+
output: oldState.attr,
|
|
558
|
+
news,
|
|
559
|
+
})
|
|
560
|
+
: undefined,
|
|
561
|
+
).pipe(
|
|
562
|
+
Effect.map(
|
|
563
|
+
(diff) =>
|
|
564
|
+
diff ??
|
|
565
|
+
({
|
|
566
|
+
action: arePropsChanged(oldProps, news)
|
|
567
|
+
? "update"
|
|
568
|
+
: "noop",
|
|
569
|
+
} as UpdateDiff | NoopDiff),
|
|
343
570
|
),
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
571
|
+
);
|
|
572
|
+
|
|
573
|
+
if (oldState.status === "creating") {
|
|
574
|
+
if (diff.action === "noop") {
|
|
575
|
+
// we're in the creating state and props are un-changed
|
|
576
|
+
// let's just continue where we left off
|
|
577
|
+
return Node<Create<Resource>>({
|
|
578
|
+
action: "create",
|
|
579
|
+
props: news,
|
|
580
|
+
state: oldState,
|
|
581
|
+
});
|
|
582
|
+
} else if (diff.action === "update") {
|
|
583
|
+
// props have changed in a way that is updatable
|
|
584
|
+
// again, just continue with the create
|
|
585
|
+
// TODO(sam): should we maybe try an update instead?
|
|
586
|
+
return Node<Create<Resource>>({
|
|
587
|
+
action: "create",
|
|
588
|
+
props: news,
|
|
589
|
+
state: oldState,
|
|
590
|
+
});
|
|
591
|
+
} else {
|
|
592
|
+
// props have changed in an incompatible way
|
|
593
|
+
// because it's possible that an un-updatable resource has already been created
|
|
594
|
+
// we must use a replace step to create a new one and delete the potential old one
|
|
595
|
+
return Node<Replace<Resource>>({
|
|
596
|
+
action: "replace",
|
|
597
|
+
props: news,
|
|
598
|
+
deleteFirst: diff.deleteFirst ?? false,
|
|
599
|
+
state: oldState,
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
} else if (oldState.status === "updating") {
|
|
603
|
+
// we started to update a resource but did not complete
|
|
604
|
+
if (diff.action === "update" || diff.action === "noop") {
|
|
605
|
+
return Node<Update<Resource>>({
|
|
606
|
+
action: "update",
|
|
607
|
+
props: news,
|
|
608
|
+
state: oldState,
|
|
609
|
+
});
|
|
610
|
+
} else {
|
|
611
|
+
// we started to update a resource but now believe we should replace it
|
|
612
|
+
return Node<Replace<Resource>>({
|
|
613
|
+
action: "replace",
|
|
614
|
+
deleteFirst: diff.deleteFirst ?? false,
|
|
615
|
+
props: news,
|
|
616
|
+
// TODO(sam): can Apply handle replacements when the oldState is UpdatingResourceState?
|
|
617
|
+
// -> or is there we do a provider.read to try and reconcile back to UpdatedResourceState?
|
|
618
|
+
state: oldState,
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
} else if (oldState.status === "replacing") {
|
|
622
|
+
// resource replacement started, but the replacement may or may not have been created
|
|
623
|
+
if (diff.action === "noop") {
|
|
624
|
+
// this is the stable case - noop means just continue with the replacement
|
|
625
|
+
return Node<Replace<Resource>>({
|
|
626
|
+
action: "replace",
|
|
627
|
+
deleteFirst: oldState.deleteFirst,
|
|
628
|
+
props: news,
|
|
629
|
+
state: oldState,
|
|
630
|
+
});
|
|
631
|
+
} else if (diff.action === "update") {
|
|
632
|
+
// potential problem here - the props have changed since we tried to replace,
|
|
633
|
+
// but not enough to trigger another replacement. the resource provider should
|
|
634
|
+
// be designed as idempotent to converge to the right state when creating the new resource
|
|
635
|
+
// the newly generated instanceId is intended to assist with this
|
|
636
|
+
return Node<Replace<Resource>>({
|
|
637
|
+
action: "replace",
|
|
638
|
+
deleteFirst: oldState.deleteFirst,
|
|
639
|
+
props: news,
|
|
640
|
+
state: oldState,
|
|
641
|
+
});
|
|
642
|
+
} else {
|
|
643
|
+
// ah shit, so we tried to replace the resource and then crashed
|
|
644
|
+
// now the props have changed again in such a way that the (maybe, maybe not)
|
|
645
|
+
// created resource should also be replaced
|
|
646
|
+
|
|
647
|
+
// TODO(sam): what should we do?
|
|
648
|
+
// 1. trigger a deletion of the potentially created resource
|
|
649
|
+
// 2. expect the resource provider to handle it idempotently?
|
|
650
|
+
// -> i don't think this case is fair to put on the resource provider
|
|
651
|
+
// because if the resource was created, it's in a state that can't be updated
|
|
652
|
+
return yield* Effect.fail(
|
|
653
|
+
new CannotReplacePartiallyReplacedResource(id),
|
|
654
|
+
);
|
|
655
|
+
}
|
|
656
|
+
} else if (oldState.status === "replaced") {
|
|
657
|
+
// replacement has been created but we're not done cleaning up the old state
|
|
658
|
+
if (diff.action === "noop") {
|
|
659
|
+
// this is the stable case - noop means just continue cleaning up the replacement
|
|
660
|
+
return Node<Replace<Resource>>({
|
|
661
|
+
action: "replace",
|
|
662
|
+
deleteFirst: oldState.deleteFirst,
|
|
663
|
+
props: news,
|
|
664
|
+
state: oldState,
|
|
665
|
+
});
|
|
666
|
+
} else if (diff.action === "update") {
|
|
667
|
+
// the replacement has been created but now also needs to be updated
|
|
668
|
+
// the resource provider should:
|
|
669
|
+
// 1. Update the newly created replacement resource
|
|
670
|
+
// 2. Then proceed as normal to delete the replaced resources (after all downstream references are updated)
|
|
671
|
+
return Node<Update<Resource>>({
|
|
672
|
+
action: "update",
|
|
673
|
+
props: news,
|
|
674
|
+
state: oldState,
|
|
675
|
+
});
|
|
676
|
+
} else {
|
|
677
|
+
// the replacement has been created but now it needs to be replaced
|
|
678
|
+
// this is the worst-case scenario because downstream resources
|
|
679
|
+
// could have been been updated to point to the replaced resources
|
|
680
|
+
return yield* Effect.fail(
|
|
681
|
+
new CannotReplacePartiallyReplacedResource(id),
|
|
682
|
+
);
|
|
683
|
+
}
|
|
684
|
+
} else if (oldState.status === "deleting") {
|
|
685
|
+
if (diff.action === "noop" || diff.action === "update") {
|
|
686
|
+
// we're in a partially deleted state, it is unclear whether it was or was not deleted
|
|
687
|
+
// it should be safe to re-create it with the same instanceId?
|
|
688
|
+
return Node<Create<Resource>>({
|
|
689
|
+
action: "create",
|
|
690
|
+
props: news,
|
|
691
|
+
state: {
|
|
692
|
+
...oldState,
|
|
693
|
+
status: "creating",
|
|
694
|
+
props: news,
|
|
695
|
+
},
|
|
696
|
+
});
|
|
697
|
+
} else {
|
|
698
|
+
return yield* Effect.fail(
|
|
699
|
+
new CannotReplacePartiallyReplacedResource(id),
|
|
700
|
+
);
|
|
701
|
+
}
|
|
702
|
+
} else if (diff.action === "update") {
|
|
703
|
+
return Node<Update<Resource>>({
|
|
704
|
+
action: "update",
|
|
705
|
+
props: news,
|
|
706
|
+
state: oldState,
|
|
707
|
+
});
|
|
708
|
+
} else if (diff.action === "replace") {
|
|
709
|
+
return Node<Replace<Resource>>({
|
|
710
|
+
action: "replace",
|
|
711
|
+
props: news,
|
|
712
|
+
state: oldState,
|
|
713
|
+
deleteFirst: diff?.deleteFirst ?? false,
|
|
714
|
+
});
|
|
715
|
+
} else {
|
|
716
|
+
return Node<NoopUpdate<Resource>>({
|
|
717
|
+
action: "noop",
|
|
718
|
+
state: oldState,
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
}),
|
|
722
|
+
),
|
|
723
|
+
)).map((update) => [update.resource.id, update]),
|
|
724
|
+
) as IPlan["resources"];
|
|
347
725
|
|
|
348
726
|
const deletions = Object.fromEntries(
|
|
349
727
|
(yield* Effect.all(
|
|
350
|
-
(yield* state.list()).map(
|
|
728
|
+
(yield* state.list({ stack: app.name, stage: app.stage })).map(
|
|
351
729
|
Effect.fn(function* (id) {
|
|
352
730
|
if (id in resourceGraph) {
|
|
353
731
|
return;
|
|
354
732
|
}
|
|
355
|
-
const oldState = yield* state.get(
|
|
356
|
-
|
|
733
|
+
const oldState = yield* state.get({
|
|
734
|
+
stack: app.name,
|
|
735
|
+
stage: app.stage,
|
|
736
|
+
resourceId: id,
|
|
737
|
+
});
|
|
357
738
|
if (oldState) {
|
|
358
|
-
const provider
|
|
359
|
-
oldState?.type,
|
|
360
|
-
);
|
|
361
|
-
if (!provider) {
|
|
362
|
-
yield* Effect.die(
|
|
363
|
-
new Error(`Provider not found for ${oldState?.type}`),
|
|
364
|
-
);
|
|
365
|
-
}
|
|
739
|
+
const provider = yield* getProviderByType(oldState.resourceType);
|
|
366
740
|
return [
|
|
367
741
|
id,
|
|
368
742
|
{
|
|
369
743
|
action: "delete",
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
provider,
|
|
373
|
-
attributes: oldState?.output,
|
|
374
|
-
// TODO(sam): Support Detach Bindings
|
|
744
|
+
state: oldState,
|
|
745
|
+
// // TODO(sam): Support Detach Bindings
|
|
375
746
|
bindings: [],
|
|
747
|
+
provider,
|
|
376
748
|
resource: {
|
|
377
749
|
id: id,
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
attr: oldState.output,
|
|
750
|
+
type: oldState.resourceType,
|
|
751
|
+
attr: oldState.attr,
|
|
381
752
|
props: oldState.props,
|
|
382
753
|
} as Resource,
|
|
383
|
-
|
|
754
|
+
// TODO(sam): is it enough to just pass through oldState?
|
|
755
|
+
downstream: oldDownstreamDependencies[id] ?? [],
|
|
384
756
|
} satisfies Delete<Resource>,
|
|
385
757
|
] as const;
|
|
386
758
|
}
|
|
@@ -390,7 +762,7 @@ export const plan = <
|
|
|
390
762
|
);
|
|
391
763
|
|
|
392
764
|
for (const [resourceId, deletion] of Object.entries(deletions)) {
|
|
393
|
-
const dependencies = deletion.downstream.filter(
|
|
765
|
+
const dependencies = deletion.state.downstream.filter(
|
|
394
766
|
(d) => d in resourceGraph,
|
|
395
767
|
);
|
|
396
768
|
if (dependencies.length > 0) {
|
|
@@ -405,26 +777,29 @@ export const plan = <
|
|
|
405
777
|
}
|
|
406
778
|
|
|
407
779
|
return {
|
|
408
|
-
phase,
|
|
409
780
|
resources: resourceGraph,
|
|
410
781
|
deletions,
|
|
411
|
-
} satisfies
|
|
412
|
-
}) as
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
782
|
+
} satisfies IPlan as IPlan;
|
|
783
|
+
}) as any;
|
|
784
|
+
|
|
785
|
+
export class CannotReplacePartiallyReplacedResource extends Data.TaggedError(
|
|
786
|
+
"CannotReplacePartiallyReplacedResource",
|
|
787
|
+
)<{
|
|
788
|
+
message: string;
|
|
789
|
+
logicalId: string;
|
|
790
|
+
}> {
|
|
791
|
+
constructor(logicalId: string) {
|
|
792
|
+
super({
|
|
793
|
+
message:
|
|
794
|
+
`Resource '${logicalId}' did not finish being replaced in a previous deployment ` +
|
|
795
|
+
`and is expected to be replaced again in this deployment. ` +
|
|
796
|
+
`You should revert its properties and try again after a successful deployment.`,
|
|
797
|
+
logicalId,
|
|
798
|
+
});
|
|
799
|
+
}
|
|
800
|
+
}
|
|
426
801
|
|
|
427
|
-
class DeleteResourceHasDownstreamDependencies extends Data.TaggedError(
|
|
802
|
+
export class DeleteResourceHasDownstreamDependencies extends Data.TaggedError(
|
|
428
803
|
"DeleteResourceHasDownstreamDependencies",
|
|
429
804
|
)<{
|
|
430
805
|
message: string;
|
|
@@ -433,12 +808,13 @@ class DeleteResourceHasDownstreamDependencies extends Data.TaggedError(
|
|
|
433
808
|
}> {}
|
|
434
809
|
|
|
435
810
|
const arePropsChanged = <R extends Resource>(
|
|
436
|
-
|
|
811
|
+
oldProps: R["props"] | undefined,
|
|
437
812
|
newProps: R["props"],
|
|
438
813
|
) => {
|
|
439
814
|
return (
|
|
440
|
-
|
|
441
|
-
JSON.stringify(omit((
|
|
815
|
+
Output.hasOutputs(newProps) ||
|
|
816
|
+
JSON.stringify(omit((oldProps ?? {}) as any, "bindings")) !==
|
|
817
|
+
JSON.stringify(omit((newProps ?? {}) as any, "bindings"))
|
|
442
818
|
);
|
|
443
819
|
};
|
|
444
820
|
|
|
@@ -453,9 +829,9 @@ const diffBindings = Effect.fn(function* ({
|
|
|
453
829
|
}) {
|
|
454
830
|
// const actions: BindNode[] = [];
|
|
455
831
|
const oldBindings = oldState?.bindings;
|
|
456
|
-
const oldSids = new Set(
|
|
457
|
-
|
|
458
|
-
);
|
|
832
|
+
// const oldSids = new Set(
|
|
833
|
+
// oldBindings?.map(({ binding }) => binding.capability.sid),
|
|
834
|
+
// );
|
|
459
835
|
|
|
460
836
|
const diffBinding: (
|
|
461
837
|
binding: AnyBinding,
|
|
@@ -559,7 +935,7 @@ const isBindingDiff = Effect.fn(function* ({
|
|
|
559
935
|
id: oldCap.resource.id,
|
|
560
936
|
props: newCap.resource.props,
|
|
561
937
|
oldProps: oldState?.props,
|
|
562
|
-
oldAttr: oldState?.
|
|
938
|
+
oldAttr: oldState?.attr,
|
|
563
939
|
},
|
|
564
940
|
props: newBinding.props,
|
|
565
941
|
attr: oldBinding.attr,
|
|
@@ -581,3 +957,106 @@ const isBindingDiff = Effect.fn(function* ({
|
|
|
581
957
|
});
|
|
582
958
|
// TODO(sam): compare props
|
|
583
959
|
// oldBinding.props !== newBinding.props;
|
|
960
|
+
|
|
961
|
+
/**
|
|
962
|
+
* Print a plan in a human-readable format that shows the graph topology.
|
|
963
|
+
*/
|
|
964
|
+
export const printPlan = (plan: IPlan): string => {
|
|
965
|
+
const lines: string[] = [];
|
|
966
|
+
const allNodes = { ...plan.resources, ...plan.deletions };
|
|
967
|
+
|
|
968
|
+
// Build reverse mapping: upstream -> downstream
|
|
969
|
+
const upstreamMap: Record<string, string[]> = {};
|
|
970
|
+
for (const [id] of Object.entries(allNodes)) {
|
|
971
|
+
upstreamMap[id] = [];
|
|
972
|
+
}
|
|
973
|
+
for (const [id, node] of Object.entries(allNodes)) {
|
|
974
|
+
if (!node) continue;
|
|
975
|
+
for (const downstreamId of node.state?.downstream ?? []) {
|
|
976
|
+
if (upstreamMap[downstreamId]) {
|
|
977
|
+
upstreamMap[downstreamId].push(id);
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
// Action symbols
|
|
983
|
+
const actionSymbol = (action: string) => {
|
|
984
|
+
switch (action) {
|
|
985
|
+
case "create":
|
|
986
|
+
return "+";
|
|
987
|
+
case "update":
|
|
988
|
+
return "~";
|
|
989
|
+
case "delete":
|
|
990
|
+
return "-";
|
|
991
|
+
case "replace":
|
|
992
|
+
return "±";
|
|
993
|
+
case "noop":
|
|
994
|
+
return "=";
|
|
995
|
+
default:
|
|
996
|
+
return "?";
|
|
997
|
+
}
|
|
998
|
+
};
|
|
999
|
+
|
|
1000
|
+
// Print header
|
|
1001
|
+
lines.push(
|
|
1002
|
+
"╔════════════════════════════════════════════════════════════════╗",
|
|
1003
|
+
);
|
|
1004
|
+
lines.push(
|
|
1005
|
+
"║ PLAN ║",
|
|
1006
|
+
);
|
|
1007
|
+
lines.push(
|
|
1008
|
+
"╠════════════════════════════════════════════════════════════════╣",
|
|
1009
|
+
);
|
|
1010
|
+
lines.push(
|
|
1011
|
+
"║ Legend: + create, ~ update, - delete, ± replace, = noop ║",
|
|
1012
|
+
);
|
|
1013
|
+
lines.push(
|
|
1014
|
+
"╚════════════════════════════════════════════════════════════════╝",
|
|
1015
|
+
);
|
|
1016
|
+
lines.push("");
|
|
1017
|
+
|
|
1018
|
+
// Print resources section
|
|
1019
|
+
lines.push(
|
|
1020
|
+
"┌─ Resources ────────────────────────────────────────────────────┐",
|
|
1021
|
+
);
|
|
1022
|
+
const resourceIds = Object.keys(plan.resources).sort();
|
|
1023
|
+
for (const id of resourceIds) {
|
|
1024
|
+
const node = plan.resources[id];
|
|
1025
|
+
const symbol = actionSymbol(node.action);
|
|
1026
|
+
const type = node.resource?.type ?? "unknown";
|
|
1027
|
+
const downstream = node.state?.downstream?.length
|
|
1028
|
+
? ` → [${node.state?.downstream.join(", ")}]`
|
|
1029
|
+
: "";
|
|
1030
|
+
lines.push(`│ [${symbol}] ${id} (${type})${downstream}`);
|
|
1031
|
+
}
|
|
1032
|
+
if (resourceIds.length === 0) {
|
|
1033
|
+
lines.push("│ (none)");
|
|
1034
|
+
}
|
|
1035
|
+
lines.push(
|
|
1036
|
+
"└────────────────────────────────────────────────────────────────┘",
|
|
1037
|
+
);
|
|
1038
|
+
lines.push("");
|
|
1039
|
+
|
|
1040
|
+
// Print deletions section
|
|
1041
|
+
lines.push(
|
|
1042
|
+
"┌─ Deletions ────────────────────────────────────────────────────┐",
|
|
1043
|
+
);
|
|
1044
|
+
const deletionIds = Object.keys(plan.deletions).sort();
|
|
1045
|
+
for (const id of deletionIds) {
|
|
1046
|
+
const node = plan.deletions[id]!;
|
|
1047
|
+
const type = node.resource?.type ?? "unknown";
|
|
1048
|
+
const downstream = node.state.downstream?.length
|
|
1049
|
+
? ` → [${node.state.downstream.join(", ")}]`
|
|
1050
|
+
: "";
|
|
1051
|
+
lines.push(`│ [-] ${id} (${type})${downstream}`);
|
|
1052
|
+
}
|
|
1053
|
+
if (deletionIds.length === 0) {
|
|
1054
|
+
lines.push("│ (none)");
|
|
1055
|
+
}
|
|
1056
|
+
lines.push(
|
|
1057
|
+
"└────────────────────────────────────────────────────────────────┘",
|
|
1058
|
+
);
|
|
1059
|
+
lines.push("");
|
|
1060
|
+
|
|
1061
|
+
return lines.join("\n");
|
|
1062
|
+
};
|