declastruct 1.1.0 → 1.1.1

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.
@@ -0,0 +1 @@
1
+ export * from './contract/sdk';
package/dist/index.js ADDED
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./contract/sdk"), exports);
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,iDAA+B"}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "declastruct",
3
3
  "author": "ehmpathy",
4
4
  "description": "Add declarative control to any resource constructs. Declare, plan, and apply within an observable pit-of-success.",
5
- "version": "1.1.0",
5
+ "version": "1.1.1",
6
6
  "repository": "ehmpathy/declastruct",
7
7
  "homepage": "https://github.com/ehmpathy/declastruct",
8
8
  "keywords": [
package/readme.md CHANGED
@@ -3,74 +3,250 @@
3
3
  ![test](https://github.com/ehmpathy/declastruct/workflows/test/badge.svg)
4
4
  ![publish](https://github.com/ehmpathy/declastruct/workflows/publish/badge.svg)
5
5
 
6
- Add declarative control to any resource constructs. Declare, plan, and apply within an observable pit-of-success.
6
+ declarative control for any resource constructs, batteries included
7
+
8
+ # intro
9
+
10
+ Add declarative control to any resource construct. Declare, plan, and apply within an observable pit-of-success.
7
11
 
8
12
  Declare the structures you want. Plan to see the changes required. Apply to make it so 🪄
9
13
 
10
- # install
14
+
15
+ ## what is it?
16
+
17
+ `declastruct` is a framework for managing any resource construct declaratively using TypeScript.
18
+
19
+ declare what you want, plan the changes, and apply them safely — all without managing separate state files or learning a new language.
20
+
21
+ works with:
22
+ - **infrastructure** — AWS, GCP, Azure resources
23
+ - **SaaS platforms** — Stripe customers, GitHub repos, Slack channels, etc
24
+ - **databases** — application data models
25
+ - **any API** — anything with a remote state you want to control
26
+
27
+ think Terraform, but:
28
+ - **no state files to manage** — compares directly against live remote state
29
+ - **no new language to learn** — uses TypeScript and your existing domain objects
30
+ - **pit-of-success by default** — enforces idempotency, clear unique keys, and safe operations
31
+ - **not just infrastructure** — works with any resource construct (saas, databases, apis, etc)
32
+
33
+ # usage
34
+
35
+ ## install
11
36
 
12
37
  ```sh
13
38
  npm install declastruct --save-dev
14
39
  ```
15
40
 
16
- # use
41
+ ## use
17
42
 
18
- ### 1. declare ✨
43
+ ### 1. **declare** your desired state
19
44
 
20
- declare the resources you wish to have and how you wish to have them
45
+ define resource constructs with strongly-typed domain objects:
21
46
 
22
47
  ```ts
23
48
  import { getDeclastructAwsProvider, DeclaredAwsS3Bucket } from 'declastruct-aws';
49
+ import { getDeclastructStripeProvider, DeclaredStripeCustomer } from 'declastruct-stripe';
24
50
 
25
- // declare the providers that support your resources
51
+ // define which providers support your resource constructs
26
52
  export const getProviders = async () => [
27
- await getDeclastructAwsProvider({
28
- profile: process.env.AWS_PROFILE,
29
- })
30
- ]
31
-
32
- // declare the resources in the states you want them
53
+ await getDeclastructAwsProvider({
54
+ profile: process.env.AWS_PROFILE,
55
+ }),
56
+ await getDeclastructStripeProvider({
57
+ apiKey: process.env.STRIPE_SECRET_KEY,
58
+ }),
59
+ ];
60
+
61
+ // declare the resource constructs you want and their desired state
33
62
  export const getResources = async () => {
34
63
  const bucket = DeclaredAwsS3Bucket.as({
35
64
  name: 'your-s3-bucket',
65
+ versioning: true,
66
+ encryption: 'AES256',
36
67
  });
37
- // declare other resources you wish to have
38
68
 
39
- return [
40
- bucket,
41
- // ... all the resources you wish for will go here
42
- ],
43
- }
69
+ const customer = DeclaredStripeCustomer.as({
70
+ email: 'user@example.com',
71
+ name: 'John Doe',
72
+ metadata: { source: 'app-web' },
73
+ });
74
+
75
+ return [bucket, customer];
76
+ };
44
77
  ```
45
78
 
46
- ### 2. plan 🔮
79
+ ### 2. **plan** the required changes 🔮
47
80
 
48
- plan how to achieve the wish of resources you've declared
81
+ see exactly what will change before applying:
49
82
 
50
83
  ```sh
51
- npx declastruct plan --wish provision/resources.ts --into provision/.temp/plan.json
84
+ npx declastruct plan \
85
+ --wish provision/resources.ts \
86
+ --into provision/.temp/plan.json
52
87
  ```
53
88
 
54
- ### 3. apply 🪄
89
+ output:
90
+ ```
91
+ planned changes:
92
+ CREATE: DeclaredAwsS3Bucket.your-s3-bucket
93
+ + name: "your-s3-bucket"
94
+ + versioning: true
95
+ + encryption: "AES256"
96
+
97
+ CREATE: DeclaredStripeCustomer.user@example.com
98
+ + email: "user@example.com"
99
+ + name: "John Doe"
100
+ + metadata: { source: "app-web" }
101
+ ```
55
102
 
56
- apply the plan to fulfill the wish
103
+ ### 3. **apply** the plan 🪄
104
+
105
+ execute the plan to make your desired state reality:
57
106
 
58
107
  ```sh
59
108
  npx declastruct apply --plan provision/.temp/plan.json
60
109
  ```
61
110
 
62
- # benefits
111
+ # benefits - why declastruct?
112
+
113
+ ## summary
114
+
115
+ ✅ **stateless** — no state files to manage, compare directly against reality
116
+
117
+ ✅ **type-safe** — full TypeScript support with domain objects
118
+
119
+ ✅ **idempotent** — safe to run plans multiple times
120
+
121
+ ✅ **observable** — see exactly what will change before applying
122
+
123
+ ✅ **composable** — reuse domain objects and operations across application code and resource management
124
+
125
+ ✅ **pit-of-success** — enforced best practices via idempotent dao interfaces
126
+
127
+ ✅ **universal** — works with any resource construct (infrastructure, saas platforms, databases, apis, etc)
128
+
129
+
130
+ ## details
131
+
132
+ ### no state file management
133
+
134
+ traditional declarative tools (like Terraform) require maintaining a separate state file that tracks what resources exist. this creates problems:
135
+ - state files can drift from reality
136
+ - state locking issues in team environments
137
+ - state files must be carefully secured and backed up
138
+
139
+ **declastruct eliminates state files entirely.** it compares your desired resource constructs directly against live remote state using unique keys, so the source of truth is always reality itself.
140
+
141
+ ### use your existing domain language
142
+
143
+ instead of learning HCL, YAML, or another DSL:
144
+ - declare resource constructs as TypeScript using `domain-objects`
145
+ - reuse the same domain objects across your application code and remote resource management
146
+ - leverage TypeScript's type safety, IDE autocomplete, and refactoring tools
147
+ - compose and test resource definitions like any other code
148
+
149
+ ### enforced best practices
150
+
151
+ declastruct providers follow a pit-of-success pattern that guarantees:
152
+
153
+ **idempotency** — all operations can be safely retried
154
+ - `finsert`: find-or-insert (safe create)
155
+ - `upsert`: update-or-insert (safe update)
156
+ - running the same plan multiple times produces the same result
157
+
158
+ **explicit unique keys** — every resource declares how to uniquely identify it
159
+ - prevents accidental duplicates
160
+ - enables accurate comparison against live state
161
+ - makes resource relationships clear, typesafe, and composable
162
+
163
+ **observable change plans** — see exactly what will change before applying
164
+ - diff view shows before & after for each resource
165
+ - change actions: CREATE, UPDATE, KEEP, DESTROY
166
+ - no surprises in production
167
+
168
+ ### provider ecosystem
169
+
170
+ declastruct is designed to support any resource construct through adapters:
171
+
172
+ **available providers:**
173
+ - `declastruct-aws` — AWS infrastructure (S3, Lambda, RDS, EC2, etc.)
174
+ - `declastruct-stripe` — Stripe SaaS resources (customers, subscriptions, products, etc.)
175
+ - `declastruct-github` — Github resources (repos, branches, protection, etc.)
176
+ - etc
177
+
178
+ **build your own provider** for any resource construct (GitHub repos, Slack channels, database records, etc.) by implementing the `DeclastructDao` and `DeclastructProvider` interfaces.
179
+
180
+ ## use cases
181
+
182
+ - **infrastructure as code** — manage AWS, GCP, Azure resources declaratively
183
+ - **SaaS platform management** — manage Stripe customers, GitHub repos, Slack channels, etc declaratively
184
+ - **database state management** — control database resource states declaratively
185
+ - **api state management** — control remote resource state through api's declaratively
186
+ - **multi-platform orchestration** — coordinate resources across different providers in one plan
187
+
188
+
189
+ # concepts
190
+
191
+ ## domain
192
+
193
+ ### DeclastructDao
194
+
195
+ the core abstraction that defines how to interact with a resource type:
196
+
197
+ ```ts
198
+ interface DeclastructDao<TResource, TResourceClass, TContext> {
199
+ get: {
200
+ byUnique: (ref: RefByUnique, context) => Promise<TResource | null>;
201
+ byPrimary?: (ref: RefByPrimary, context) => Promise<TResource | null>;
202
+ byRef: (ref: Ref, context) => Promise<TResource | null>;
203
+ };
204
+ set: {
205
+ finsert: (resource: TResource, context) => Promise<TResource>;
206
+ upsert?: (resource: TResource, context) => Promise<TResource>;
207
+ delete?: (ref: Ref, context) => Promise<void>;
208
+ };
209
+ }
210
+ ```
211
+
212
+ every resource class gets a DeclastructDao that enforces safe, idempotent operations.
213
+
214
+ ### DeclastructProvider
215
+
216
+ bundles related DAOs and provider context:
217
+
218
+ ```ts
219
+ interface DeclastructProvider<TDaos, TContext> {
220
+ name: string; // provider identifier
221
+ daos: TDaos; // map of resource types to DAOs
222
+ context: TContext; // auth, region, etc.
223
+ hooks: {
224
+ beforeAll: () => Promise<void>;
225
+ afterAll: () => Promise<void>;
226
+ };
227
+ }
228
+ ```
229
+
230
+ ### DeclastructPlan
231
+
232
+ captures the changes needed to achieve desired state:
233
+
234
+ ```ts
235
+ interface DeclastructPlan {
236
+ changes: DeclastructChange[]; // ordered list of changes
237
+ createdAt: IsoTimestamp; // when plan was created
238
+ hash: string; // fingerprint for validation
239
+ }
240
+ ```
241
+
242
+ each `DeclastructChange` includes:
243
+ - `action`: CREATE | UPDATE | KEEP | DESTROY
244
+ - `forResource`: which resource this affects
245
+ - `state.desired`: what you want
246
+ - `state.remote`: what exists now
247
+ - `state.difference`: human-readable diff
63
248
 
64
- - no dedicated state required
65
- - looks at the source of truth directly
66
- - leverages unique keys of resources to understand remote state automatically and eliminate the middleman
67
249
 
68
- - no new language syntax required
69
- - no awkward new-language limitations
70
- - reuse your existing domain language to manage your resources
250
+ ## inspiration
71
251
 
72
- - pit of success adapters
73
- - each repo that implements a declastruct adapter to control their remote state follows a pit of success
74
- - idempotency is required on operations, to guarantee they operate safely
75
- - clear declaration of unique and primary keys is required, to guarantee readability, composability, and true comparisons against reality
76
- - not only can you use them via declastruct, you can leverage them directly as well
252
+ inspired by Terraform's declarative approach, but designed to eliminate state management overhead, work with any resource construct, and leverage TypeScript's type system for safer declarative resource management.