@fractal_cloud/sdk 1.5.2 → 2.0.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/README.md +210 -383
- package/dist/custom_workloads-DdYOZg-e.cjs +1971 -0
- package/dist/index.cjs +119 -17689
- package/dist/index.d.cts +2 -8513
- package/dist/index.d.mts +2 -8513
- package/dist/index.mjs +2 -16657
- package/dist/model/index.cjs +119 -0
- package/dist/model/index.d.cts +767 -0
- package/dist/model/index.d.mts +767 -0
- package/dist/model/index.mjs +1248 -0
- package/package.json +11 -1
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
The Fractal Cloud TypeScript SDK lets platform engineers and application developers define cloud-agnostic infrastructure blueprints in TypeScript and deploy them to any supported cloud provider.
|
|
13
13
|
|
|
14
|
-
Infrastructure is modelled as reusable architectural building blocks — **Fractals** — that can be validated, governed, and evolved over time. Live
|
|
14
|
+
Infrastructure is modelled as reusable architectural building blocks — **Fractals** — that can be validated, governed, and evolved over time. A **Live System** is produced by selecting, per component, a concrete **Offer** from the catalogue — without ever touching vendor-specific tooling or DSLs.
|
|
15
15
|
|
|
16
16
|
## Why Fractal
|
|
17
17
|
|
|
@@ -28,35 +28,35 @@ Fractal Cloud takes a different approach: define infrastructure as **architectur
|
|
|
28
28
|
|
|
29
29
|
### Fractal (Blueprint)
|
|
30
30
|
|
|
31
|
-
A Fractal is a governed, reusable infrastructure pattern. It
|
|
31
|
+
A Fractal is a governed, reusable infrastructure pattern. It references **abstract Components only** — never offers or vendors — and declares their structure (dependencies and links) and their **guardrails** (locked parameters). Adding a vendor never requires editing an existing Fractal.
|
|
32
32
|
|
|
33
33
|
### Live System
|
|
34
34
|
|
|
35
|
-
A Live System is a running instance of a Fractal. It
|
|
35
|
+
A Live System is a running instance of a Fractal. It is built by **per-component offer selection**: each abstract Component is mapped to one concrete provider-specific **Offer** that carries the vendor parameters the Automation Engine needs. There is no global provider — mixed-vendor live systems are normal. There are no state files; the cloud is the source of truth.
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
### The Catalogue — three levels
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
```
|
|
40
|
+
Level 1 — COMPONENT {domain}.{name} e.g. Storage.ObjectStorage
|
|
41
|
+
Abstract capability contract. Referenced by blueprints. Never provisioned.
|
|
42
|
+
|
|
43
|
+
Level 2 — SERVICE {domain}.{deliveryModel}.{name} delivery-model contract. Never provisioned.
|
|
44
|
+
|
|
45
|
+
Level 3 — OFFER {domain}.{deliveryModel}.{name} + a provider
|
|
46
|
+
Concrete, vendor-specific. The only level that maps to real infra.
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
A blueprint references a Component (`Storage.ObjectStorage`); a Live System selects any Offer that `satisfies` it (`AwsS3`, `AzureBlob`, `GcsBucket`, `MinIO`, …). Selection is **compile-checked**: an offer that does not satisfy a slot's Component is a type error (and is also rejected at runtime).
|
|
44
50
|
|
|
45
|
-
|
|
51
|
+
`provider` is a **vendor** axis (`AWS | Azure | GCP | OCI | Hetzner | Aruba | RedHat | VMware`). `IaaS | PaaS | CaaS | SaaS | FaaS` is the separate `deliveryModel` axis. Vendor-neutral self-hosted offers (e.g. `Kafka`, `Prometheus`, `MinIO` on any cluster) omit `provider`.
|
|
46
52
|
|
|
47
|
-
|
|
48
|
-
|--------|----------------|-------------------|-----------|
|
|
49
|
-
| **NetworkAndCompute** | VirtualNetwork, Subnet, SecurityGroup, VirtualMachine, ContainerPlatform | 41 offers | AWS, Azure, GCP, OCI, Hetzner, VMware, OpenShift, Aruba |
|
|
50
|
-
| **CustomWorkloads** | Workload | Satisfied by PaaS/CaaS offers (ECS, Container Apps, Cloud Run, OpenShift, Kubernetes, etc.) | AWS, Azure, GCP, OCI, OpenShift, CaaS |
|
|
51
|
-
| **Storage** | FilesAndBlobs, RelationalDbms, RelationalDatabase, DocumentDbms, DocumentDatabase, ColumnOrientedDbms, ColumnOrientedEntity, KeyValueDbms, KeyValueEntity, GraphDbms, GraphDatabase, Search, SearchEntity, Unmanaged | 27 offers | AWS, Azure, GCP, Aruba, CaaS, SaaS |
|
|
52
|
-
| **Messaging** | Broker, Entity (PaaS + CaaS), Unmanaged | 12 offers | Azure, GCP, CaaS |
|
|
53
|
-
| **BigData** | DistributedDataProcessing, ComputeCluster, DataProcessingJob, MlExperiment, Datalake, Unmanaged | 21 offers | AWS, Azure, GCP, Aruba, CaaS |
|
|
54
|
-
| **APIManagement** | PaaS ApiGateway, CaaS ApiGateway, Unmanaged | 6 offers | AWS, Azure, GCP, CaaS |
|
|
55
|
-
| **Observability** | Monitoring, Tracing, Logging, Unmanaged | 4 offers | CaaS |
|
|
56
|
-
| **Security** | ServiceMesh, Unmanaged | 2 offers | CaaS |
|
|
57
|
-
| **Custom** | Any (via `Custom.blueprint()`) | Any (via `Custom.offer()`) | Any |
|
|
53
|
+
### Guardrails vs operations
|
|
58
54
|
|
|
59
|
-
|
|
55
|
+
| Surface | Who sets it | When | Effect |
|
|
56
|
+
|---|---|---|---|
|
|
57
|
+
| **Guardrail** — `.withXxx()` on a Component, at design time | The architect, inside `createFractal` | Authoring | Sets a neutral parameter and **locks** it. Devs cannot override; a later write throws. |
|
|
58
|
+
| **Operation** — a verb on the Fractal `operations` interface | The consuming dev, via `.specialize()` | Specialization | Application-level intent (`withCollation`, `withDatabases`, `withRoutes`). Writes open params or adds child components. Never a pass-through infra knob. |
|
|
59
|
+
| **Vendor config** — an Offer's config object | The dev, at offer selection | Live System | Vendor-only knobs (`bucketRegion`, `amiId`, `resourceGroup`, …). |
|
|
60
60
|
|
|
61
61
|
## Installation
|
|
62
62
|
|
|
@@ -70,464 +70,290 @@ Requires Node.js 18+ and TypeScript 5+.
|
|
|
70
70
|
|
|
71
71
|
## Quick start
|
|
72
72
|
|
|
73
|
-
The following
|
|
73
|
+
The following defines a cloud-agnostic blueprint (a VPC, a subnet, a security group, two VMs, and a container platform), then specializes it and selects AWS offers to build and deploy a Live System.
|
|
74
74
|
|
|
75
|
-
### 1.
|
|
75
|
+
### 1. Author the Fractal (`fractal.ts`)
|
|
76
76
|
|
|
77
77
|
```typescript
|
|
78
78
|
import {
|
|
79
|
-
|
|
80
|
-
VirtualNetwork, Subnet, SecurityGroup, VirtualMachine,
|
|
79
|
+
createFractal,
|
|
80
|
+
VirtualNetwork, Subnet, SecurityGroup, VirtualMachine, ContainerPlatform,
|
|
81
81
|
} from '@fractal_cloud/sdk';
|
|
82
82
|
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
83
|
+
const boundedContextId = {
|
|
84
|
+
ownerType: 'Personal',
|
|
85
|
+
ownerId: process.env['OWNER_ID']!,
|
|
86
|
+
name: 'my-team',
|
|
87
|
+
};
|
|
88
88
|
|
|
89
|
-
const
|
|
90
|
-
id: '
|
|
89
|
+
export const fractal = createFractal({
|
|
90
|
+
id: 'standard-network',
|
|
91
91
|
version: {major: 1, minor: 0, patch: 0},
|
|
92
|
-
|
|
92
|
+
boundedContextId,
|
|
93
|
+
blueprint: bp => {
|
|
94
|
+
const network = bp.add(
|
|
95
|
+
VirtualNetwork({id: 'main-network'})
|
|
96
|
+
.withCidrBlock('10.0.0.0/16')
|
|
97
|
+
.withRegion('us-east-1'),
|
|
98
|
+
);
|
|
99
|
+
const subnet = bp.add(
|
|
100
|
+
Subnet({id: 'app-subnet'}).withCidrBlock('10.0.1.0/24').dependsOn(network),
|
|
101
|
+
);
|
|
102
|
+
const sg = bp.add(
|
|
103
|
+
SecurityGroup({id: 'app-sg'})
|
|
104
|
+
.withIngressRules([{protocol: 'tcp', fromPort: 443, sourceCidr: '0.0.0.0/0'}])
|
|
105
|
+
.dependsOn(network),
|
|
106
|
+
);
|
|
107
|
+
const web = bp.add(VirtualMachine({id: 'web-server'}).withOsType('linux').dependsOn(subnet));
|
|
108
|
+
const api = bp.add(VirtualMachine({id: 'api-server'}).withOsType('linux').dependsOn(subnet));
|
|
109
|
+
const cluster = bp.add(
|
|
110
|
+
ContainerPlatform({id: 'app-cluster'}).withKubernetesVersion('1.29').dependsOn(subnet),
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
// Runtime link: web can reach api on 8080 (blueprint owns all links).
|
|
114
|
+
bp.link(web, api, {fromPort: 8080, protocol: 'tcp'});
|
|
115
|
+
|
|
116
|
+
return {network, subnet, sg, web, api, cluster};
|
|
117
|
+
},
|
|
93
118
|
});
|
|
94
|
-
|
|
95
|
-
const webServer = VirtualMachine.create({
|
|
96
|
-
id: 'web-server',
|
|
97
|
-
version: {major: 1, minor: 0, patch: 0},
|
|
98
|
-
displayName: 'Web Server',
|
|
99
|
-
}).withLinks([{target: apiServer, fromPort: 8080}]);
|
|
100
|
-
|
|
101
|
-
const network = VirtualNetwork.create({
|
|
102
|
-
id: 'main-network',
|
|
103
|
-
version: {major: 1, minor: 0, patch: 0},
|
|
104
|
-
displayName: 'Main VPC',
|
|
105
|
-
cidrBlock: '10.0.0.0/16',
|
|
106
|
-
})
|
|
107
|
-
.withSubnets([
|
|
108
|
-
Subnet.create({
|
|
109
|
-
id: 'public-subnet',
|
|
110
|
-
version: {major: 1, minor: 0, patch: 0},
|
|
111
|
-
displayName: 'Public Subnet',
|
|
112
|
-
cidrBlock: '10.0.1.0/24',
|
|
113
|
-
}).withVirtualMachines([webServer, apiServer]),
|
|
114
|
-
])
|
|
115
|
-
.withSecurityGroups([
|
|
116
|
-
SecurityGroup.create({
|
|
117
|
-
id: 'web-sg',
|
|
118
|
-
version: {major: 1, minor: 0, patch: 0},
|
|
119
|
-
displayName: 'Web Security Group',
|
|
120
|
-
description: 'Allow SSH and HTTP',
|
|
121
|
-
ingressRules: [
|
|
122
|
-
{protocol: 'tcp', fromPort: 22, toPort: 22, sourceCidr: '0.0.0.0/0'},
|
|
123
|
-
{protocol: 'tcp', fromPort: 80, toPort: 80, sourceCidr: '0.0.0.0/0'},
|
|
124
|
-
],
|
|
125
|
-
}),
|
|
126
|
-
]);
|
|
127
|
-
|
|
128
|
-
export const fractal = Fractal.getBuilder()
|
|
129
|
-
.withId(
|
|
130
|
-
Fractal.Id.getBuilder()
|
|
131
|
-
.withBoundedContextId(bcId)
|
|
132
|
-
.withName(KebabCaseString.getBuilder().withValue('my-iaas-fractal').build())
|
|
133
|
-
.withVersion(Version.getBuilder().withMajor(1).withMinor(0).withPatch(0).build())
|
|
134
|
-
.build(),
|
|
135
|
-
)
|
|
136
|
-
.withComponents([...network.components])
|
|
137
|
-
.build();
|
|
138
119
|
```
|
|
139
120
|
|
|
140
|
-
### 2.
|
|
121
|
+
### 2. Select offers and build the Live System (`aws_live_system.ts`)
|
|
141
122
|
|
|
142
123
|
```typescript
|
|
143
124
|
import {
|
|
144
|
-
AwsVpc, AwsSubnet, AwsSecurityGroup, Ec2Instance,
|
|
145
|
-
Environment, KebabCaseString, LiveSystem, OwnerId, OwnerType,
|
|
125
|
+
AwsVpc, AwsSubnet, AwsSecurityGroup, Ec2Instance, Eks,
|
|
146
126
|
} from '@fractal_cloud/sdk';
|
|
147
|
-
import {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
export
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
.withEnvironment(Environment.getBuilder()
|
|
168
|
-
.withId(Environment.Id.getBuilder()
|
|
169
|
-
.withOwnerType(OwnerType.Personal)
|
|
170
|
-
.withOwnerId(OwnerId.getBuilder().withValue(process.env['OWNER_ID']!).build())
|
|
171
|
-
.withName(KebabCaseString.getBuilder().withValue('dev').build()).build()).build())
|
|
172
|
-
.withComponent(vpc).withComponent(subnet).withComponent(sg).withComponent(web).withComponent(api)
|
|
173
|
-
.build();
|
|
174
|
-
}
|
|
127
|
+
import {fractal} from './fractal';
|
|
128
|
+
|
|
129
|
+
const environment = {
|
|
130
|
+
ownerType: 'Personal',
|
|
131
|
+
ownerId: process.env['OWNER_ID']!,
|
|
132
|
+
name: 'dev',
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
export const liveSystem = fractal.specialize().toLiveSystem({
|
|
136
|
+
name: 'acme-net-aws',
|
|
137
|
+
environment,
|
|
138
|
+
select: {
|
|
139
|
+
'main-network': AwsVpc({}),
|
|
140
|
+
'app-subnet': AwsSubnet({}),
|
|
141
|
+
'app-sg': AwsSecurityGroup({}),
|
|
142
|
+
'web-server': Ec2Instance({amiId: 'ami-0c55b159cbfafe1f0', instanceType: 't3.micro'}),
|
|
143
|
+
'api-server': Ec2Instance({amiId: 'ami-0c55b159cbfafe1f0', instanceType: 't3.small'}),
|
|
144
|
+
'app-cluster': Eks({}),
|
|
145
|
+
},
|
|
146
|
+
});
|
|
175
147
|
```
|
|
176
148
|
|
|
149
|
+
Every component id must map to an offer whose `satisfies` matches its Component. Selecting, say, `AwsVpc({})` for `'app-subnet'` is a compile-time error.
|
|
150
|
+
|
|
177
151
|
### 3. Deploy (`index.ts`)
|
|
178
152
|
|
|
179
153
|
```typescript
|
|
180
|
-
import {
|
|
181
|
-
import {
|
|
182
|
-
import {getLiveSystem} from './aws_live_system';
|
|
154
|
+
import {deploy} from '@fractal_cloud/sdk';
|
|
155
|
+
import {liveSystem} from './aws_live_system';
|
|
183
156
|
|
|
184
|
-
const credentials =
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
157
|
+
const credentials = {
|
|
158
|
+
clientId: process.env['SERVICE_ACCOUNT_ID']!,
|
|
159
|
+
clientSecret: process.env['SERVICE_ACCOUNT_SECRET']!,
|
|
160
|
+
};
|
|
188
161
|
|
|
189
|
-
await
|
|
190
|
-
await getLiveSystem().deploy(credentials);
|
|
162
|
+
await deploy(liveSystem, credentials, {mode: 'wait'});
|
|
191
163
|
```
|
|
192
164
|
|
|
193
|
-
|
|
165
|
+
`deploy` submits the Live System (create or update) to Fractal Cloud; the Automation Engine reconciles cloud resources to match it.
|
|
194
166
|
|
|
195
167
|
## Deployment modes
|
|
196
168
|
|
|
197
|
-
`
|
|
169
|
+
`deploy(liveSystem, credentials, options)` supports two modes.
|
|
198
170
|
|
|
199
171
|
### Fire and forget (default)
|
|
200
172
|
|
|
201
|
-
Submits the live system
|
|
173
|
+
Submits the live system and returns immediately. Provisioning happens asynchronously. This is the default when no options are passed.
|
|
202
174
|
|
|
203
175
|
```typescript
|
|
204
|
-
|
|
205
|
-
await
|
|
206
|
-
await liveSystem.deploy(credentials, {mode: 'fire-and-forget'});
|
|
176
|
+
await deploy(liveSystem, credentials);
|
|
177
|
+
await deploy(liveSystem, credentials, {mode: 'fire-and-forget'}); // equivalent
|
|
207
178
|
```
|
|
208
179
|
|
|
209
180
|
Best for: **applications, CLIs, scripts** where infrastructure deployment is a background concern.
|
|
210
181
|
|
|
211
182
|
### Wait for Active
|
|
212
183
|
|
|
213
|
-
Submits
|
|
184
|
+
Submits, then polls until the live system reaches `Active`. Throws on terminal failure (`FailedMutation`, `Error`) or timeout.
|
|
214
185
|
|
|
215
186
|
```typescript
|
|
216
|
-
await
|
|
187
|
+
await deploy(liveSystem, credentials, {
|
|
217
188
|
mode: 'wait',
|
|
218
189
|
pollIntervalMs: 10_000, // check every 10 s (default: 5 s)
|
|
219
190
|
timeoutMs: 900_000, // give up after 15 min (default: 10 min)
|
|
191
|
+
quiet: false, // set true to suppress wait-mode log lines
|
|
220
192
|
});
|
|
221
193
|
// reaches here only when the live system is fully Active
|
|
222
194
|
```
|
|
223
195
|
|
|
224
|
-
Best for: **CI/CD pipelines**
|
|
196
|
+
Best for: **CI/CD pipelines** that must not advance until infrastructure is provisioned.
|
|
197
|
+
|
|
198
|
+
### Destroy
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
import {destroy} from '@fractal_cloud/sdk';
|
|
202
|
+
await destroy(liveSystem, credentials);
|
|
203
|
+
```
|
|
225
204
|
|
|
226
205
|
---
|
|
227
206
|
|
|
228
|
-
##
|
|
207
|
+
## Catalogue
|
|
229
208
|
|
|
230
|
-
The
|
|
209
|
+
The blueprint references the **Component** in the left column; a Live System selects any **Offer** beneath it. Offers with no provider are vendor-neutral self-hosted (run on any cluster).
|
|
231
210
|
|
|
232
|
-
###
|
|
211
|
+
### NetworkAndCompute
|
|
233
212
|
|
|
234
|
-
|
|
|
235
|
-
|
|
236
|
-
| `VirtualNetwork` | `AwsVpc` | `AzureVnet` | `GcpVpc` | `OciVcn` | `HetznerNetwork` | `VspherePortGroup` | — |
|
|
237
|
-
| `Subnet` | `AwsSubnet` | `AzureSubnet` | `GcpSubnet` | `OciSubnet` | `HetznerSubnet` | `VsphereVlan` | — |
|
|
238
|
-
| `SecurityGroup` | `AwsSecurityGroup` | `AzureNsg` | `GcpFirewall` | `OciSecurityList` | `HetznerFirewall` | — | `OpenshiftSecurityGroup` |
|
|
239
|
-
| `VirtualMachine` | `Ec2Instance` | `AzureVm` | `GcpVm` | `OciInstance` | `HetznerServer` | `VsphereVm` | `OpenshiftVm` |
|
|
213
|
+
| Component | AWS | Azure | GCP | OCI | Hetzner | VMware | RedHat (OpenShift) |
|
|
214
|
+
|---|---|---|---|---|---|---|---|
|
|
215
|
+
| `VirtualNetwork` | `AwsVpc` | `AzureVnet` | `GcpVpc` | `OciVcn` | `HetznerNetwork` | `VspherePortGroup` | — |
|
|
216
|
+
| `Subnet` | `AwsSubnet` | `AzureSubnet` | `GcpSubnet` | `OciSubnet` | `HetznerSubnet` | `VsphereVlan` | — |
|
|
217
|
+
| `SecurityGroup` | `AwsSecurityGroup` | `AzureNsg` | `GcpFirewall` | `OciSecurityList` | `HetznerFirewall` | — | `OpenshiftSecurityGroup` |
|
|
218
|
+
| `VirtualMachine` | `Ec2Instance` | `AzureVm` | `GcpVm` | `OciInstance` | `HetznerServer` | `VsphereVm` | `OpenshiftVm` |
|
|
219
|
+
| `ContainerPlatform` | `Eks` | `Aks` | `Gke` | — | — | — | — |
|
|
220
|
+
| `LoadBalancer` | `AwsLb` | `AzureLb` | `GcpGlb` | — | — | — | `OpenshiftService` |
|
|
240
221
|
|
|
241
|
-
###
|
|
222
|
+
### CustomWorkloads
|
|
242
223
|
|
|
243
|
-
|
|
|
244
|
-
|
|
245
|
-
| `
|
|
246
|
-
| `
|
|
224
|
+
| Component | AWS | Azure | GCP | RedHat | Self-hosted |
|
|
225
|
+
|---|---|---|---|---|---|
|
|
226
|
+
| `Workload` | `EcsService` | `AzureContainerApp` | `CloudRun` | `OpenshiftWorkload` | `K8sWorkload` |
|
|
227
|
+
| `Function` | `AwsLambda` | `AzureFunction` | `GcpFunction` | — | — |
|
|
247
228
|
|
|
248
229
|
### Storage
|
|
249
230
|
|
|
250
|
-
|
|
|
251
|
-
|
|
252
|
-
| `
|
|
253
|
-
| `RelationalDbms` | — | `
|
|
254
|
-
| `RelationalDatabase` | — | `
|
|
255
|
-
| `DocumentDbms` | — | `AzureCosmosDbAccount` | `GcpFirestore` | — | — |
|
|
256
|
-
| `ColumnOrientedDbms` | — | `AzureCosmosDbCassandra` | `GcpBigTable` | — | — |
|
|
257
|
-
| `Search` | — | — | — | — | `Elastic` |
|
|
231
|
+
| Component | AWS | Azure | GCP | Aruba | RedHat | Self-hosted |
|
|
232
|
+
|---|---|---|---|---|---|---|
|
|
233
|
+
| `ObjectStorage` | `AwsS3` | `AzureBlob` | `GcsBucket` | — | `OpenshiftPersistentVolume` | `MinIO` |
|
|
234
|
+
| `RelationalDbms` | — | `AzurePostgresDbms` | `GcpPostgresDbms` | `ArubaMySqlDbms` | — | — |
|
|
235
|
+
| `RelationalDatabase` | — | `AzurePostgresDatabase` | `GcpPostgresDatabase` | — | — | — |
|
|
258
236
|
|
|
259
|
-
|
|
237
|
+
> `RelationalDatabase` components added under a DBMS via an operation are emitted by the **DBMS's own offer** in its vendor family — selecting `AzurePostgresDbms` makes its databases `AzurePostgresDatabase`. They are not independently offer-selected.
|
|
260
238
|
|
|
261
|
-
|
|
262
|
-
|---------------------|-----|-------|-----|-------|------|
|
|
263
|
-
| `DistributedDataProcessing` | `AwsDatabricks` | `AzureDatabricks` | `GcpDatabricks` | — | `CaaSSparkOperator` |
|
|
264
|
-
| `ComputeCluster` | `AwsDatabricksCluster` | `AzureDatabricksCluster` | `GcpDatabricksCluster` | — | `CaaSSparkCluster` |
|
|
265
|
-
| `DataProcessingJob` | `AwsDatabricksJob` | `AzureDatabricksJob` | `GcpDatabricksJob` | — | `CaaSSparkJob` |
|
|
266
|
-
| `MlExperiment` | `AwsDatabricksMlflow` | `AzureDatabricksMlflow` | `GcpDatabricksMlflow` | — | `CaaSMlflow` |
|
|
267
|
-
| `Datalake` | `AwsS3Datalake` | `AzureDatalake` | `GcpDatalake` | — | `CaaSMinioTenant` |
|
|
239
|
+
### Messaging
|
|
268
240
|
|
|
269
|
-
|
|
241
|
+
| Component | Azure | GCP | Self-hosted |
|
|
242
|
+
|---|---|---|---|
|
|
243
|
+
| `Broker` | `AzureServiceBus` | `GcpPubSub` | `Kafka` |
|
|
244
|
+
| `MessagingEntity` | `AzureServiceBusTopic` | `GcpPubSubTopic` | `KafkaTopic` |
|
|
270
245
|
|
|
271
|
-
|
|
272
|
-
|-------|-------------|---------------------|
|
|
273
|
-
| `OpenshiftService` | `NetworkAndCompute.CaaS.OpenshiftService` | Kubernetes Service + Route (standalone) |
|
|
274
|
-
| `OpenshiftPersistentVolume` | `Storage.CaaS.OpenshiftPersistentVolume` | Persistent Volume Claim (standalone) |
|
|
246
|
+
### BigData
|
|
275
247
|
|
|
276
|
-
|
|
248
|
+
| Component | AWS | Azure | GCP | Self-hosted |
|
|
249
|
+
|---|---|---|---|---|
|
|
250
|
+
| `DistributedDataProcessing` | `AwsDatabricks` | `AzureDatabricks` | `GcpDatabricks` | — |
|
|
251
|
+
| `ComputeCluster` | `AwsDatabricksCluster` | `AzureDatabricksCluster` | `GcpDatabricksCluster` | `CaaSSparkCluster` |
|
|
252
|
+
| `DataProcessingJob` | `AwsDatabricksJob` | `AzureDatabricksJob` | `GcpDatabricksJob` | `CaaSSparkJob` |
|
|
253
|
+
| `MlExperiment` | `AwsDatabricksMlflow` | `AzureDatabricksMlflow` | `GcpDatabricksMlflow` | `CaaSMlflow` |
|
|
254
|
+
| `Datalake` | `AwsS3Datalake` | `AzureDatalake` | `GcpDatalake` | — |
|
|
277
255
|
|
|
278
|
-
|
|
256
|
+
### APIManagement
|
|
279
257
|
|
|
280
|
-
|
|
|
281
|
-
|
|
282
|
-
| `
|
|
283
|
-
| `ArubaElasticIp` | `NetworkAndCompute.IaaS.ArubaElasticIp` | Reserved public IP for Aruba cloud servers |
|
|
284
|
-
| `ArubaVpcPeering` | `NetworkAndCompute.IaaS.ArubaVpcPeering` | VPC-to-VPC peering connection |
|
|
285
|
-
| `ArubaVpnTunnel` | `NetworkAndCompute.IaaS.ArubaVpnTunnel` | Site-to-site VPN tunnel |
|
|
286
|
-
| `ArubaContainerRegistry` | `NetworkAndCompute.PaaS.ArubaContainerRegistry` | Managed container image registry |
|
|
287
|
-
| `ArubaBlockStorage` | `Storage.IaaS.ArubaBlockStorage` | Block storage volume attachable to a cloud server |
|
|
258
|
+
| Component | AWS | Azure | GCP | Self-hosted |
|
|
259
|
+
|---|---|---|---|---|
|
|
260
|
+
| `ApiGateway` | `AwsCloudFront` | `AzureApiManagement` | `GcpApiGateway` | `Ambassador` · `Traefik` |
|
|
288
261
|
|
|
289
|
-
|
|
262
|
+
### Observability (self-hosted, CaaS)
|
|
290
263
|
|
|
291
|
-
|
|
264
|
+
| Component | Offer |
|
|
265
|
+
|---|---|
|
|
266
|
+
| `Monitoring` | `Prometheus` |
|
|
267
|
+
| `Tracing` | `Jaeger` |
|
|
268
|
+
| `Logging` | `ObservabilityElastic` |
|
|
292
269
|
|
|
293
|
-
###
|
|
270
|
+
### Security
|
|
294
271
|
|
|
295
|
-
|
|
272
|
+
| Component | AWS | Self-hosted |
|
|
273
|
+
|---|---|---|
|
|
274
|
+
| `ServiceMesh` | — | `Ocelot` |
|
|
275
|
+
| `IdentityProvider` | `Cognito` | `Keycloak` |
|
|
296
276
|
|
|
297
|
-
|
|
298
|
-
import {
|
|
299
|
-
Custom, Fractal, LiveSystem, ServiceDeliveryModel,
|
|
300
|
-
} from '@fractal_cloud/sdk';
|
|
277
|
+
## Extending the catalogue
|
|
301
278
|
|
|
302
|
-
|
|
279
|
+
Both Components and Offers are plain values — you can add your own without forking.
|
|
303
280
|
|
|
304
|
-
|
|
305
|
-
domain: 'Analytics', // any string — not limited to built-in domains
|
|
306
|
-
serviceDeliveryModel: ServiceDeliveryModel.PaaS,
|
|
307
|
-
name: 'TimeSeriesStore', // must be PascalCase
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
const InfluxDb = Custom.offer({
|
|
311
|
-
domain: 'Analytics',
|
|
312
|
-
serviceDeliveryModel: ServiceDeliveryModel.PaaS,
|
|
313
|
-
name: 'InfluxDb',
|
|
314
|
-
provider: 'CustomProvider', // any string — not limited to built-in providers
|
|
315
|
-
});
|
|
316
|
-
```
|
|
281
|
+
### Add a new Offer (including a new vendor)
|
|
317
282
|
|
|
318
|
-
|
|
283
|
+
`defineOffer` returns an offer constructor. Declare which Component it `satisfies`, its 3-part `offerType`, its `deliveryModel`, an optional `provider` (one of the `Provider` vendors; omit for vendor-neutral self-hosted), and a config type for vendor knobs. **Every existing Fractal can select it automatically** — no blueprint changes.
|
|
319
284
|
|
|
320
285
|
```typescript
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
286
|
+
import {defineOffer} from '@fractal_cloud/sdk';
|
|
287
|
+
|
|
288
|
+
// A self-hosted object store satisfying the built-in Storage.ObjectStorage
|
|
289
|
+
// Component. Vendor-neutral, so `provider` is omitted.
|
|
290
|
+
const Ceph = defineOffer<'Storage.ObjectStorage', {storageClass?: string}>({
|
|
291
|
+
satisfies: 'Storage.ObjectStorage',
|
|
292
|
+
offerType: 'Storage.CaaS.Ceph',
|
|
293
|
+
deliveryModel: 'CaaS',
|
|
328
294
|
});
|
|
329
|
-
```
|
|
330
|
-
|
|
331
|
-
### Satisfying with a custom offer (vendor-specific)
|
|
332
|
-
|
|
333
|
-
The `satisfy()` method works exactly like built-in offers: it locks the blueprint's id, version, displayName, description, dependencies, links, and parameters, then exposes only vendor-specific parameter setters.
|
|
334
|
-
|
|
335
|
-
```typescript
|
|
336
|
-
// ── 3. Live system — how the architecture is provisioned ──────────────────
|
|
337
|
-
|
|
338
|
-
const influx = InfluxDb.satisfy(metricsDb)
|
|
339
|
-
.withParameter('bucket', 'metrics')
|
|
340
|
-
.withParameter('orgId', 'my-org')
|
|
341
|
-
.build();
|
|
342
|
-
```
|
|
343
295
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
const influx = InfluxDb.create({
|
|
350
|
-
id: 'metrics-db',
|
|
351
|
-
version: { major: 1, minor: 0, patch: 0 },
|
|
352
|
-
displayName: 'Metrics Database',
|
|
353
|
-
parameters: { bucket: 'metrics', orgId: 'my-org' },
|
|
296
|
+
// Select it for any ObjectStorage slot, just like a built-in offer:
|
|
297
|
+
fractal.specialize().toLiveSystem({
|
|
298
|
+
name: 'acme-prod',
|
|
299
|
+
environment,
|
|
300
|
+
select: {uploads: Ceph({storageClass: 'rbd'})},
|
|
354
301
|
});
|
|
355
302
|
```
|
|
356
303
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
For advanced scenarios (dependencies, links, conditional params), use the builder API:
|
|
360
|
-
|
|
361
|
-
```typescript
|
|
362
|
-
const metricsDb = TimeSeriesStore.getBuilder()
|
|
363
|
-
.withId('metrics-db')
|
|
364
|
-
.withVersion(1, 0, 0)
|
|
365
|
-
.withDisplayName('Metrics Database')
|
|
366
|
-
.withParameter('retentionDays', '90')
|
|
367
|
-
.withDependencies([{ id: someOtherComponent.id }])
|
|
368
|
-
.withLinks([{ id: anotherComponent.id, parameters: linkParams }])
|
|
369
|
-
.build();
|
|
370
|
-
```
|
|
371
|
-
|
|
372
|
-
### Deploying custom components
|
|
373
|
-
|
|
374
|
-
Custom components integrate seamlessly with the standard deploy flow:
|
|
304
|
+
By default an offer emits one live component merging the blueprint's neutral params with its vendor config. Pass an `instantiate(ctx, config)` to emit a custom set of live components (e.g. a parent plus one child per `ctx.children` entry).
|
|
375
305
|
|
|
376
|
-
|
|
377
|
-
// ── 4. Deploy as usual ────────────────────────────────────────────────────
|
|
378
|
-
|
|
379
|
-
const fractal = Fractal.getBuilder()
|
|
380
|
-
.withId(fractalId)
|
|
381
|
-
.withComponents([metricsDb])
|
|
382
|
-
.build();
|
|
383
|
-
|
|
384
|
-
const liveSystem = LiveSystem.getBuilder()
|
|
385
|
-
.withId(liveSystemId)
|
|
386
|
-
.withFractalId(fractal.id)
|
|
387
|
-
.withGenericProvider('CustomProvider')
|
|
388
|
-
.withEnvironment(environment)
|
|
389
|
-
.withComponent(influx)
|
|
390
|
-
.build();
|
|
391
|
-
|
|
392
|
-
await fractal.deploy(credentials);
|
|
393
|
-
await liveSystem.deploy(credentials, { mode: 'wait' });
|
|
394
|
-
```
|
|
395
|
-
|
|
396
|
-
### Mixing custom and built-in components
|
|
306
|
+
### Add a new abstract Component
|
|
397
307
|
|
|
398
|
-
|
|
308
|
+
Author a Component factory on the core authoring primitives (`newNode`, `guardrail`, `addDependency`). Each `.withXxx()` setter records a neutral parameter and locks it as a guardrail.
|
|
399
309
|
|
|
400
310
|
```typescript
|
|
401
|
-
import {
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
311
|
+
import {ComponentNode, NodeState, newNode, guardrail} from '@fractal_cloud/sdk';
|
|
312
|
+
|
|
313
|
+
type TimeSeriesNode<Id extends string = string> = ComponentNode<Id, 'Analytics.TimeSeries'> & {
|
|
314
|
+
withRetentionDays: (v: number) => TimeSeriesNode<Id>;
|
|
315
|
+
};
|
|
316
|
+
const node = <Id extends string>(s: NodeState): TimeSeriesNode<Id> => ({
|
|
317
|
+
state: s,
|
|
318
|
+
withRetentionDays: v => node<Id>(guardrail(s, 'retentionDays', v)),
|
|
407
319
|
});
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
.withSubnets([
|
|
411
|
-
Subnet.create({ /* ... */ }),
|
|
412
|
-
]);
|
|
413
|
-
|
|
414
|
-
const cache = MyCache.create({
|
|
415
|
-
id: 'app-cache',
|
|
416
|
-
version: { major: 1, minor: 0, patch: 0 },
|
|
417
|
-
displayName: 'Application Cache',
|
|
418
|
-
parameters: { maxMemoryMb: '4096', evictionPolicy: 'lru' },
|
|
419
|
-
dependencies: [{ id: network.vpc.id }], // depend on built-in components
|
|
420
|
-
});
|
|
421
|
-
|
|
422
|
-
const fractal = Fractal.getBuilder()
|
|
423
|
-
.withId(fractalId)
|
|
424
|
-
.withComponents([...network.components, cache])
|
|
425
|
-
.build();
|
|
320
|
+
export const TimeSeries = <const Id extends string>(cfg: {id: Id}): TimeSeriesNode<Id> =>
|
|
321
|
+
node<Id>(newNode(cfg.id, 'Analytics.TimeSeries'));
|
|
426
322
|
```
|
|
427
323
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
| Function | Returns | Description |
|
|
431
|
-
|----------|---------|-------------|
|
|
432
|
-
| `Custom.blueprint(config)` | `CustomBlueprintFactory` | Define a reusable blueprint component type |
|
|
433
|
-
| `Custom.offer(config)` | `CustomOfferFactory` | Define a reusable live system offer type |
|
|
434
|
-
|
|
435
|
-
**`CustomBlueprintFactory`**
|
|
436
|
-
|
|
437
|
-
| Method | Returns | Description |
|
|
438
|
-
|--------|---------|-------------|
|
|
439
|
-
| `.create(config)` | `BlueprintComponent` | Create a component with id, version, displayName, parameters, dependencies, links |
|
|
440
|
-
| `.getBuilder()` | `CustomBlueprintBuilder` | Fluent builder with `withParameter(key, value)`, `withDependencies()`, `withLinks()` |
|
|
441
|
-
| `.typeString` | `string` | The full type string (e.g. `"Analytics.PaaS.TimeSeriesStore"`) |
|
|
442
|
-
|
|
443
|
-
**`CustomOfferFactory`**
|
|
444
|
-
|
|
445
|
-
| Method | Returns | Description |
|
|
446
|
-
|--------|---------|-------------|
|
|
447
|
-
| `.create(config)` | `LiveSystemComponent` | Create a live system component directly |
|
|
448
|
-
| `.getBuilder()` | `CustomOfferBuilder` | Fluent builder with full access to all fields |
|
|
449
|
-
| `.satisfy(blueprint)` | `CustomSatisfiedBuilder` | Lock structural fields from blueprint; only `withParameter()`/`withParameters()` exposed |
|
|
450
|
-
| `.typeString` | `string` | The full type string (e.g. `"Analytics.PaaS.InfluxDb"`) |
|
|
451
|
-
|
|
452
|
-
**`CustomSatisfiedBuilder`** (returned by `satisfy()`)
|
|
453
|
-
|
|
454
|
-
| Method | Returns | Description |
|
|
455
|
-
|--------|---------|-------------|
|
|
456
|
-
| `.withParameter(key, value)` | `CustomSatisfiedBuilder` | Add a single vendor-specific parameter |
|
|
457
|
-
| `.withParameters(record)` | `CustomSatisfiedBuilder` | Add multiple vendor-specific parameters at once |
|
|
458
|
-
| `.build()` | `LiveSystemComponent` | Build the final component |
|
|
459
|
-
|
|
460
|
-
### Validation
|
|
461
|
-
|
|
462
|
-
- **Component name** must be PascalCase (e.g. `TimeSeriesStore`, `InfluxDb`). Validated eagerly when the factory is created — a `SyntaxError` is thrown immediately for invalid names.
|
|
463
|
-
- **Component id** must be kebab-case (e.g. `metrics-db`). Each segment must start with a letter.
|
|
464
|
-
- **Domain and provider** accept any string. Built-in values (`InfrastructureDomain.Storage`, `'AWS'`, etc.) provide autocomplete but are not enforced.
|
|
465
|
-
- Custom domains and providers must be registered with the Fractal Cloud platform for deployment to succeed.
|
|
466
|
-
|
|
467
|
-
---
|
|
324
|
+
Then write offers that `satisfies: 'Analytics.TimeSeries'`. Custom domains, components, and vendors must be registered with the Fractal Cloud platform for deployment to succeed.
|
|
468
325
|
|
|
469
326
|
## Samples
|
|
470
327
|
|
|
471
|
-
The [sample repository](https://github.com/Fractal-Cloud/fractal-ts-sdk-samples) contains ready-to-run examples
|
|
472
|
-
|
|
473
|
-
| Sample | Providers | Description |
|
|
474
|
-
|--------|-----------|-------------|
|
|
475
|
-
| `basic_iaas` | AWS · Azure · GCP · OCI · Hetzner | VPC + Subnet + Security Group + two VMs |
|
|
476
|
-
| `basic_container_platform` | AWS · Azure · GCP | VPC + Subnet + SG + container platform + two workloads |
|
|
477
|
-
| `basic_cicd` | AWS | CI/CD pipeline deployment with wait mode |
|
|
478
|
-
| `basic_storage` | AWS · Azure · GCP | PostgreSQL DBMS + Database + object storage |
|
|
479
|
-
| `basic_messaging` | Azure · GCP | Message broker + two topics |
|
|
480
|
-
| `basic_big_data` | AWS · Azure · GCP | Databricks workspace + cluster + job + MLflow |
|
|
481
|
-
| `basic_api_management` | AWS · Azure · GCP | API Gateway |
|
|
482
|
-
| `basic_observability` | CaaS | Monitoring + tracing + logging (Prometheus, Jaeger, Elastic) |
|
|
483
|
-
| `basic_security` | CaaS | Service mesh (Ocelot) |
|
|
328
|
+
The [sample repository](https://github.com/Fractal-Cloud/fractal-ts-sdk-samples) contains ready-to-run examples consumed via the `@fractal_cloud/sdk/model` subpath. `basic_storage` is the canonical reference.
|
|
484
329
|
|
|
485
330
|
## Architecture
|
|
486
331
|
|
|
487
|
-
|
|
488
|
-
src/
|
|
489
|
-
custom/ # Custom aria component factories (Custom.blueprint, Custom.offer)
|
|
490
|
-
fractal/ # Cloud-agnostic blueprint helpers
|
|
491
|
-
component/
|
|
492
|
-
network_and_compute/ # VirtualNetwork, Subnet, SecurityGroup, VirtualMachine, ContainerPlatform
|
|
493
|
-
custom_workloads/ # Workload
|
|
494
|
-
storage/ # FilesAndBlobs, RelationalDbms, DocumentDbms, Search, etc.
|
|
495
|
-
messaging/ # Broker, Entity
|
|
496
|
-
big_data/ # DistributedDataProcessing, ComputeCluster, DataProcessingJob, etc.
|
|
497
|
-
api_management/ # ApiGateway
|
|
498
|
-
observability/ # Monitoring, Tracing, Logging
|
|
499
|
-
security/ # ServiceMesh
|
|
500
|
-
live_system/ # Provider-specific helpers
|
|
501
|
-
component/
|
|
502
|
-
network_and_compute/ # AWS, Azure, GCP, OCI, Hetzner, VMware, Aruba (IaaS) + OpenShift (CaaS) + AWS/Azure/GCP/Aruba (PaaS)
|
|
503
|
-
custom_workloads/ # OpenShift, generic Kubernetes (CaaS)
|
|
504
|
-
storage/ # AWS S3, Azure Storage/CosmosDB/PostgreSQL, GCP Storage/Firestore/BigTable, Aruba MySQL/MsSQL/Object/Block, OpenShift PV
|
|
505
|
-
messaging/ # Azure ServiceBus/EventHub, GCP PubSub, CaaS Kafka
|
|
506
|
-
big_data/ # AWS/Azure/GCP Databricks + Datalake, Aruba S3 Datalake, CaaS Spark (Operator/Cluster/Job/MLflow)
|
|
507
|
-
api_management/ # AWS CloudFront, Azure API Management, GCP API Gateway, CaaS Ambassador/Traefik
|
|
508
|
-
observability/ # CaaS Prometheus, Jaeger, Elastic
|
|
509
|
-
security/ # CaaS Ocelot
|
|
510
|
-
```
|
|
511
|
-
|
|
512
|
-
## Debug logging
|
|
513
|
-
|
|
514
|
-
Set `FRACTAL_DEBUG=true` to log every outbound HTTP request and every inbound response to stdout. This covers all API calls made by `fractal.deploy()`, `liveSystem.deploy()`, `fractal.destroy()`, and `liveSystem.destroy()`.
|
|
332
|
+
The package root and the `./model` subpath export the same surface.
|
|
515
333
|
|
|
516
334
|
```
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
335
|
+
src/model/
|
|
336
|
+
core.ts # Engine: createFractal, defineOffer, ComponentNode, typed Selection,
|
|
337
|
+
# guardrails/locking, links, child components, fluent .specialize()
|
|
338
|
+
service.ts # deploy / destroy a LiveSystem (HTTP + poll + wait-mode log contract)
|
|
339
|
+
index.ts # Public barrel
|
|
340
|
+
components/ # Abstract Component factories (Level 1, vendor-agnostic)
|
|
341
|
+
network_and_compute.ts # VirtualNetwork, Subnet, SecurityGroup, VirtualMachine,
|
|
342
|
+
# ContainerPlatform, LoadBalancer
|
|
343
|
+
custom_workloads.ts # Workload, Function
|
|
344
|
+
storage.ts # ObjectStorage, RelationalDbms, RelationalDatabase
|
|
345
|
+
messaging.ts # Broker, MessagingEntity
|
|
346
|
+
big_data.ts # DistributedDataProcessing, ComputeCluster, DataProcessingJob,
|
|
347
|
+
# MlExperiment, Datalake
|
|
348
|
+
api_management.ts # ApiGateway
|
|
349
|
+
observability.ts # Monitoring, Tracing, Logging
|
|
350
|
+
security.ts # ServiceMesh, IdentityProvider
|
|
351
|
+
offers/ # Concrete Offers (Level 3) declaring what Component they satisfy
|
|
352
|
+
<domain>.ts # one file per domain, mirroring components/
|
|
353
|
+
*.test.ts # vitest specs — the executable regression suite
|
|
521
354
|
```
|
|
522
355
|
|
|
523
|
-
|
|
524
|
-
- Request and response bodies are truncated to 2 000 characters.
|
|
525
|
-
- Both success and error responses are logged.
|
|
526
|
-
- Works in all deploy modes (`fire-and-forget` and `wait`).
|
|
527
|
-
|
|
528
|
-
```bash
|
|
529
|
-
FRACTAL_DEBUG=true node build/src/index.js
|
|
530
|
-
```
|
|
356
|
+
See [`docs/fractal-model.md`](docs/fractal-model.md) for the locked model specification.
|
|
531
357
|
|
|
532
358
|
## Contributing and feedback
|
|
533
359
|
|
|
@@ -552,3 +378,4 @@ Made with ❤️ by the Fractal Cloud team.
|
|
|
552
378
|
[codecov-url]: https://codecov.io/gh/Fractal-Cloud/fractal-ts-sdk
|
|
553
379
|
[snyk-image]: https://snyk.io/test/github/Fractal-Cloud/fractal-ts-sdk/badge.svg
|
|
554
380
|
[snyk-url]: https://snyk.io/test/github/Fractal-Cloud/fractal-ts-sdk
|
|
381
|
+
</content>
|