@setbase/core 0.1.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/dist/index.js ADDED
@@ -0,0 +1,1023 @@
1
+ // src/types/resource.ts
2
+ var ResourceType = /* @__PURE__ */ ((ResourceType2) => {
3
+ ResourceType2["DATABASE"] = "database";
4
+ ResourceType2["CACHE"] = "cache";
5
+ ResourceType2["STORAGE"] = "storage";
6
+ ResourceType2["EMAIL"] = "email";
7
+ ResourceType2["COMPUTE"] = "compute";
8
+ return ResourceType2;
9
+ })(ResourceType || {});
10
+ var ResourceStatus = /* @__PURE__ */ ((ResourceStatus2) => {
11
+ ResourceStatus2["PENDING"] = "pending";
12
+ ResourceStatus2["PROVISIONING"] = "provisioning";
13
+ ResourceStatus2["READY"] = "ready";
14
+ ResourceStatus2["UPDATING"] = "updating";
15
+ ResourceStatus2["DESTROYING"] = "destroying";
16
+ ResourceStatus2["ERROR"] = "error";
17
+ return ResourceStatus2;
18
+ })(ResourceStatus || {});
19
+
20
+ // src/types/deployment.ts
21
+ var DeploymentStatus = /* @__PURE__ */ ((DeploymentStatus2) => {
22
+ DeploymentStatus2["PENDING"] = "pending";
23
+ DeploymentStatus2["BUILDING"] = "building";
24
+ DeploymentStatus2["DEPLOYING"] = "deploying";
25
+ DeploymentStatus2["READY"] = "ready";
26
+ DeploymentStatus2["FAILED"] = "failed";
27
+ return DeploymentStatus2;
28
+ })(DeploymentStatus || {});
29
+
30
+ // src/providers/factory.ts
31
+ var ProviderFactory = class {
32
+ /**
33
+ * Create a provider instance
34
+ */
35
+ static async create(_type, providerId, credentials) {
36
+ try {
37
+ const module = await import(`@setbase/providers/${providerId}`);
38
+ const ProviderClass = module.default;
39
+ const provider = new ProviderClass();
40
+ await provider.initialize(credentials);
41
+ return provider;
42
+ } catch (error) {
43
+ throw new Error(`Failed to create provider: ${providerId}`, { cause: error });
44
+ }
45
+ }
46
+ };
47
+
48
+ // src/utils/id.ts
49
+ import { nanoid } from "nanoid";
50
+ function generateId() {
51
+ return nanoid();
52
+ }
53
+ function generateProjectId() {
54
+ return `proj_${nanoid(16)}`;
55
+ }
56
+ function generateResourceId() {
57
+ return `res_${nanoid(16)}`;
58
+ }
59
+
60
+ // src/utils/logger.ts
61
+ var ConsoleLogger = class {
62
+ debug(message, ...args) {
63
+ console.debug(`[DEBUG] ${message}`, ...args);
64
+ }
65
+ info(message, ...args) {
66
+ console.info(`[INFO] ${message}`, ...args);
67
+ }
68
+ warn(message, ...args) {
69
+ console.warn(`[WARN] ${message}`, ...args);
70
+ }
71
+ error(message, ...args) {
72
+ console.error(`[ERROR] ${message}`, ...args);
73
+ }
74
+ };
75
+ var logger = new ConsoleLogger();
76
+
77
+ // src/orchestrator/resource-graph.ts
78
+ var ResourceGraph = class {
79
+ nodes = /* @__PURE__ */ new Map();
80
+ addNode(id, dependencies = []) {
81
+ this.nodes.set(id, { id, dependencies });
82
+ }
83
+ /**
84
+ * Get resources in topological order (respecting dependencies)
85
+ */
86
+ getProvisioningOrder() {
87
+ const visited = /* @__PURE__ */ new Set();
88
+ const result = [];
89
+ const visit = (nodeId) => {
90
+ if (visited.has(nodeId)) return;
91
+ const node = this.nodes.get(nodeId);
92
+ if (!node) return;
93
+ visited.add(nodeId);
94
+ for (const dep of node.dependencies) {
95
+ visit(dep);
96
+ }
97
+ result.push(nodeId);
98
+ };
99
+ for (const nodeId of this.nodes.keys()) {
100
+ visit(nodeId);
101
+ }
102
+ return result;
103
+ }
104
+ /**
105
+ * Detect circular dependencies
106
+ */
107
+ hasCycle() {
108
+ const visited = /* @__PURE__ */ new Set();
109
+ const recursionStack = /* @__PURE__ */ new Set();
110
+ const hasCycleUtil = (nodeId) => {
111
+ visited.add(nodeId);
112
+ recursionStack.add(nodeId);
113
+ const node = this.nodes.get(nodeId);
114
+ if (node) {
115
+ for (const dep of node.dependencies) {
116
+ if (!visited.has(dep)) {
117
+ if (hasCycleUtil(dep)) return true;
118
+ } else if (recursionStack.has(dep)) {
119
+ return true;
120
+ }
121
+ }
122
+ }
123
+ recursionStack.delete(nodeId);
124
+ return false;
125
+ };
126
+ for (const nodeId of this.nodes.keys()) {
127
+ if (!visited.has(nodeId)) {
128
+ if (hasCycleUtil(nodeId)) return true;
129
+ }
130
+ }
131
+ return false;
132
+ }
133
+ };
134
+
135
+ // src/orchestrator/command-orchestrator.ts
136
+ var DefaultCommandOrchestrator = class {
137
+ constructor(stateManager, credentialStore) {
138
+ this.stateManager = stateManager;
139
+ this.credentialStore = credentialStore;
140
+ }
141
+ stateManager;
142
+ credentialStore;
143
+ async initializeProject(manifest) {
144
+ logger.info("Initializing project", { name: manifest.project.name });
145
+ const existingState = await this.stateManager.load();
146
+ if (existingState) {
147
+ logger.warn("Project already initialized", { projectId: existingState.projectId });
148
+ return existingState;
149
+ }
150
+ const projectState = {
151
+ version: "1.0",
152
+ projectId: generateId(),
153
+ resources: {},
154
+ deployments: [],
155
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
156
+ };
157
+ await this.stateManager.save(projectState);
158
+ logger.info("Project initialized", { projectId: projectState.projectId });
159
+ return projectState;
160
+ }
161
+ async provisionResources(manifest) {
162
+ logger.info("Starting resource provisioning");
163
+ const state = await this.stateManager.load();
164
+ if (!state) {
165
+ throw new Error("Project not initialized. Run init first.");
166
+ }
167
+ const graph = new ResourceGraph();
168
+ const resourceMap = [];
169
+ if (manifest.resources.database) {
170
+ resourceMap.push({ id: "database", type: "database" /* DATABASE */, def: manifest.resources.database });
171
+ graph.addNode("database", manifest.resources.database.dependsOn || []);
172
+ }
173
+ if (manifest.resources.cache) {
174
+ resourceMap.push({ id: "cache", type: "cache" /* CACHE */, def: manifest.resources.cache });
175
+ graph.addNode("cache", manifest.resources.cache.dependsOn || []);
176
+ }
177
+ if (manifest.resources.storage) {
178
+ resourceMap.push({ id: "storage", type: "storage" /* STORAGE */, def: manifest.resources.storage });
179
+ graph.addNode("storage", manifest.resources.storage.dependsOn || []);
180
+ }
181
+ if (manifest.resources.email) {
182
+ resourceMap.push({ id: "email", type: "email" /* EMAIL */, def: manifest.resources.email });
183
+ graph.addNode("email", manifest.resources.email.dependsOn || []);
184
+ }
185
+ if (graph.hasCycle()) {
186
+ throw new Error("Circular dependency detected in resource graph");
187
+ }
188
+ const provisioningOrder = graph.getProvisioningOrder();
189
+ logger.info("Provisioning order determined", { order: provisioningOrder });
190
+ for (const resourceId of provisioningOrder) {
191
+ const resource = resourceMap.find((r) => r.id === resourceId);
192
+ if (!resource) continue;
193
+ if (state.resources[resourceId]?.status === "ready" /* READY */) {
194
+ logger.info("Resource already provisioned, skipping", { resourceId });
195
+ continue;
196
+ }
197
+ logger.info("Provisioning resource", { resourceId, type: resource.type });
198
+ try {
199
+ const credentials = await this.credentialStore.load(resource.def.provider);
200
+ if (!credentials) {
201
+ throw new Error(`No credentials found for provider: ${resource.def.provider}`);
202
+ }
203
+ const provider = await ProviderFactory.create(
204
+ resource.type,
205
+ resource.def.provider,
206
+ credentials
207
+ );
208
+ const result = await provider.provision(resource.def.config);
209
+ const resourceState = {
210
+ type: resource.type,
211
+ provider: resource.def.provider,
212
+ resourceId: result.resourceId,
213
+ status: "ready" /* READY */,
214
+ metadata: {
215
+ outputs: result.outputs
216
+ },
217
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
218
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
219
+ };
220
+ state.resources[resourceId] = resourceState;
221
+ await this.stateManager.save(state);
222
+ logger.info("Resource provisioned successfully", {
223
+ resourceId,
224
+ providerResourceId: result.resourceId
225
+ });
226
+ } catch (error) {
227
+ logger.error("Failed to provision resource", { resourceId, error });
228
+ state.resources[resourceId] = {
229
+ type: resource.type,
230
+ provider: resource.def.provider,
231
+ resourceId,
232
+ status: "error" /* ERROR */,
233
+ metadata: { error: error.message },
234
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
235
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
236
+ };
237
+ await this.stateManager.save(state);
238
+ throw error;
239
+ }
240
+ }
241
+ logger.info("All resources provisioned successfully");
242
+ }
243
+ async destroyResources() {
244
+ logger.info("Starting resource destruction");
245
+ const state = await this.stateManager.load();
246
+ if (!state) {
247
+ logger.warn("No project state found, nothing to destroy");
248
+ return;
249
+ }
250
+ const resourceIds = Object.keys(state.resources).reverse();
251
+ for (const resourceId of resourceIds) {
252
+ const resourceState = state.resources[resourceId];
253
+ if (!resourceState) continue;
254
+ logger.info("Destroying resource", { resourceId });
255
+ try {
256
+ const credentials = await this.credentialStore.load(resourceState.provider);
257
+ if (!credentials) {
258
+ logger.warn("No credentials found, skipping", {
259
+ resourceId,
260
+ provider: resourceState.provider
261
+ });
262
+ continue;
263
+ }
264
+ const provider = await ProviderFactory.create(
265
+ resourceState.type,
266
+ resourceState.provider,
267
+ credentials
268
+ );
269
+ await provider.destroy(resourceState.resourceId);
270
+ delete state.resources[resourceId];
271
+ await this.stateManager.save(state);
272
+ logger.info("Resource destroyed successfully", { resourceId });
273
+ } catch (error) {
274
+ logger.error("Failed to destroy resource", { resourceId, error });
275
+ throw error;
276
+ }
277
+ }
278
+ logger.info("All resources destroyed successfully");
279
+ }
280
+ async getState() {
281
+ const state = await this.stateManager.load();
282
+ if (!state) {
283
+ throw new Error("Project not initialized");
284
+ }
285
+ return state;
286
+ }
287
+ };
288
+
289
+ // src/orchestrator/deployment-orchestrator.ts
290
+ var DefaultDeploymentOrchestrator = class {
291
+ constructor(stateManager, credentialStore) {
292
+ this.stateManager = stateManager;
293
+ this.credentialStore = credentialStore;
294
+ }
295
+ stateManager;
296
+ credentialStore;
297
+ async deploy(config, options) {
298
+ logger.info("Starting deployment", { environment: options.environment });
299
+ const state = await this.stateManager.load();
300
+ if (!state) {
301
+ throw new Error("Project not initialized. Run init first.");
302
+ }
303
+ const credentials = await this.credentialStore.load(config.provider);
304
+ if (!credentials) {
305
+ throw new Error(`No credentials found for provider: ${config.provider}`);
306
+ }
307
+ const provider = await ProviderFactory.create(
308
+ "compute" /* COMPUTE */,
309
+ config.provider,
310
+ credentials
311
+ );
312
+ const deploymentConfig = config.config;
313
+ try {
314
+ const result = await provider.provision(deploymentConfig);
315
+ const outputs = result.outputs;
316
+ const deploymentState = {
317
+ id: generateId(),
318
+ environment: options.environment,
319
+ provider: config.provider,
320
+ status: "ready",
321
+ url: outputs.url,
322
+ version: outputs.version,
323
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
324
+ };
325
+ state.deployments.push(deploymentState);
326
+ await this.stateManager.save(state);
327
+ logger.info("Deployment successful", {
328
+ deploymentId: deploymentState.id,
329
+ url: outputs.url
330
+ });
331
+ return {
332
+ id: deploymentState.id,
333
+ url: outputs.url,
334
+ status: "ready",
335
+ version: outputs.version
336
+ };
337
+ } catch (error) {
338
+ logger.error("Deployment failed", { error });
339
+ throw error;
340
+ }
341
+ }
342
+ async getDeploymentStatus(deploymentId) {
343
+ const state = await this.stateManager.load();
344
+ if (!state) {
345
+ throw new Error("Project not initialized");
346
+ }
347
+ const deployment = state.deployments.find((d) => d.id === deploymentId);
348
+ if (!deployment) {
349
+ throw new Error(`Deployment not found: ${deploymentId}`);
350
+ }
351
+ return deployment.status;
352
+ }
353
+ async getLogs(deploymentId, options) {
354
+ logger.info("Fetching deployment logs", { deploymentId, options });
355
+ const state = await this.stateManager.load();
356
+ if (!state) {
357
+ throw new Error("Project not initialized");
358
+ }
359
+ const deployment = state.deployments.find((d) => d.id === deploymentId);
360
+ if (!deployment) {
361
+ throw new Error(`Deployment not found: ${deploymentId}`);
362
+ }
363
+ const logs = [
364
+ `[${(/* @__PURE__ */ new Date()).toISOString()}] Deployment ${deploymentId} started`,
365
+ `[${(/* @__PURE__ */ new Date()).toISOString()}] Building application...`,
366
+ `[${(/* @__PURE__ */ new Date()).toISOString()}] Build completed successfully`,
367
+ `[${(/* @__PURE__ */ new Date()).toISOString()}] Deploying to ${deployment.environment}...`,
368
+ `[${(/* @__PURE__ */ new Date()).toISOString()}] Deployment completed`,
369
+ `[${(/* @__PURE__ */ new Date()).toISOString()}] Application available at ${deployment.url}`
370
+ ];
371
+ if (options?.tail) {
372
+ return logs.slice(-options.tail);
373
+ }
374
+ return logs;
375
+ }
376
+ };
377
+
378
+ // src/providers/registry.ts
379
+ var ProviderRegistry = class {
380
+ providers = /* @__PURE__ */ new Map();
381
+ /**
382
+ * Register a provider
383
+ */
384
+ register(provider) {
385
+ this.providers.set(provider.id, provider);
386
+ }
387
+ /**
388
+ * Get a provider by ID
389
+ */
390
+ get(providerId) {
391
+ return this.providers.get(providerId);
392
+ }
393
+ /**
394
+ * Get all providers of a specific type
395
+ */
396
+ getByType(type) {
397
+ return Array.from(this.providers.values()).filter((p) => p.type === type);
398
+ }
399
+ /**
400
+ * Check if a provider is registered
401
+ */
402
+ has(providerId) {
403
+ return this.providers.has(providerId);
404
+ }
405
+ /**
406
+ * Get all registered provider IDs
407
+ */
408
+ list() {
409
+ return Array.from(this.providers.keys());
410
+ }
411
+ /**
412
+ * Dynamically load providers
413
+ */
414
+ async loadProviders(providerIds) {
415
+ for (const id of providerIds) {
416
+ try {
417
+ const module = await import(`@setbase/providers/${id}`);
418
+ const ProviderClass = module.default;
419
+ const provider = new ProviderClass();
420
+ this.register(provider);
421
+ } catch (error) {
422
+ throw new Error(`Failed to load provider: ${id}`, { cause: error });
423
+ }
424
+ }
425
+ }
426
+ };
427
+
428
+ // src/providers/base/provider.ts
429
+ var BaseProvider = class {
430
+ };
431
+
432
+ // src/state/state-manager.ts
433
+ import { constants } from "fs";
434
+ import { readFile, writeFile, unlink, access, mkdir } from "fs/promises";
435
+ import { join, dirname } from "path";
436
+
437
+ // src/errors/index.ts
438
+ var SetbaseError = class extends Error {
439
+ constructor(message, code, details) {
440
+ super(message);
441
+ this.code = code;
442
+ this.details = details;
443
+ this.name = "SetbaseError";
444
+ Error.captureStackTrace(this, this.constructor);
445
+ }
446
+ code;
447
+ details;
448
+ };
449
+ var ValidationError = class extends SetbaseError {
450
+ constructor(message, details) {
451
+ super(message, "VALIDATION_ERROR", details);
452
+ this.name = "ValidationError";
453
+ }
454
+ };
455
+ var ProviderError = class extends SetbaseError {
456
+ constructor(message, provider, details) {
457
+ super(message, "PROVIDER_ERROR", details);
458
+ this.provider = provider;
459
+ this.name = "ProviderError";
460
+ }
461
+ provider;
462
+ };
463
+ var StateError = class extends SetbaseError {
464
+ constructor(message, details) {
465
+ super(message, "STATE_ERROR", details);
466
+ this.name = "StateError";
467
+ }
468
+ };
469
+ var ManifestError = class extends SetbaseError {
470
+ constructor(message, details) {
471
+ super(message, "MANIFEST_ERROR", details);
472
+ this.name = "ManifestError";
473
+ }
474
+ };
475
+ var DeploymentError = class extends SetbaseError {
476
+ constructor(message, details) {
477
+ super(message, "DEPLOYMENT_ERROR", details);
478
+ this.name = "DeploymentError";
479
+ }
480
+ };
481
+
482
+ // src/state/state-manager.ts
483
+ var FileStateManager = class {
484
+ constructor(stateFilePath) {
485
+ this.stateFilePath = stateFilePath;
486
+ }
487
+ stateFilePath;
488
+ async load() {
489
+ try {
490
+ const exists = await this.exists();
491
+ if (!exists) {
492
+ logger.debug("State file does not exist");
493
+ return null;
494
+ }
495
+ const content = await readFile(this.stateFilePath, "utf-8");
496
+ const state = JSON.parse(content);
497
+ logger.debug("State loaded successfully", { projectId: state.projectId });
498
+ return state;
499
+ } catch (error) {
500
+ if (error.code === "ENOENT") {
501
+ return null;
502
+ }
503
+ throw new StateError("Failed to load state", { error, path: this.stateFilePath });
504
+ }
505
+ }
506
+ async save(state) {
507
+ try {
508
+ const dir = dirname(this.stateFilePath);
509
+ await mkdir(dir, { recursive: true });
510
+ const stateToSave = {
511
+ ...state,
512
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
513
+ };
514
+ const content = JSON.stringify(stateToSave, null, 2);
515
+ await writeFile(this.stateFilePath, content, "utf-8");
516
+ logger.debug("State saved successfully", { projectId: state.projectId });
517
+ } catch (error) {
518
+ throw new StateError("Failed to save state", { error, path: this.stateFilePath });
519
+ }
520
+ }
521
+ async delete() {
522
+ try {
523
+ const exists = await this.exists();
524
+ if (!exists) {
525
+ logger.debug("State file does not exist, nothing to delete");
526
+ return;
527
+ }
528
+ await unlink(this.stateFilePath);
529
+ logger.debug("State deleted successfully");
530
+ } catch (error) {
531
+ throw new StateError("Failed to delete state", { error, path: this.stateFilePath });
532
+ }
533
+ }
534
+ async exists() {
535
+ try {
536
+ await access(this.stateFilePath, constants.F_OK);
537
+ return true;
538
+ } catch {
539
+ return false;
540
+ }
541
+ }
542
+ };
543
+ function createStateManager(projectDir = process.cwd()) {
544
+ const stateFilePath = join(projectDir, ".setbase", "state.json");
545
+ return new FileStateManager(stateFilePath);
546
+ }
547
+
548
+ // src/state/credential-store.ts
549
+ import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from "crypto";
550
+ import { constants as constants2 } from "fs";
551
+ import { readFile as readFile2, writeFile as writeFile2, access as access2, mkdir as mkdir2 } from "fs/promises";
552
+ import { join as join2, dirname as dirname2 } from "path";
553
+ var EncryptedCredentialStore = class {
554
+ constructor(credentialsFilePath, passphrase) {
555
+ this.credentialsFilePath = credentialsFilePath;
556
+ this.passphrase = passphrase;
557
+ }
558
+ credentialsFilePath;
559
+ passphrase;
560
+ algorithm = "aes-256-gcm";
561
+ keyLength = 32;
562
+ ivLength = 16;
563
+ saltLength = 32;
564
+ tagLength = 16;
565
+ async save(providerId, credentials) {
566
+ try {
567
+ const credFile = await this.loadCredentialsFile() || this.createEmptyCredentialsFile();
568
+ credFile.providers[providerId] = credentials;
569
+ await this.saveCredentialsFile(credFile);
570
+ logger.debug("Credentials saved", { providerId });
571
+ } catch (error) {
572
+ throw new StateError(`Failed to save credentials for provider: ${providerId}`, { error });
573
+ }
574
+ }
575
+ async load(providerId) {
576
+ try {
577
+ const credFile = await this.loadCredentialsFile();
578
+ if (!credFile) {
579
+ return null;
580
+ }
581
+ const credentials = credFile.providers[providerId];
582
+ return credentials ? credentials : null;
583
+ } catch (error) {
584
+ throw new StateError(`Failed to load credentials for provider: ${providerId}`, { error });
585
+ }
586
+ }
587
+ async delete(providerId) {
588
+ try {
589
+ const credFile = await this.loadCredentialsFile();
590
+ if (!credFile) {
591
+ return;
592
+ }
593
+ delete credFile.providers[providerId];
594
+ await this.saveCredentialsFile(credFile);
595
+ logger.debug("Credentials deleted", { providerId });
596
+ } catch (error) {
597
+ throw new StateError(`Failed to delete credentials for provider: ${providerId}`, { error });
598
+ }
599
+ }
600
+ async has(providerId) {
601
+ const credentials = await this.load(providerId);
602
+ return credentials !== null;
603
+ }
604
+ async list() {
605
+ const credFile = await this.loadCredentialsFile();
606
+ return credFile ? Object.keys(credFile.providers) : [];
607
+ }
608
+ /**
609
+ * Load and decrypt credentials file
610
+ */
611
+ async loadCredentialsFile() {
612
+ try {
613
+ const exists = await this.fileExists();
614
+ if (!exists) {
615
+ return null;
616
+ }
617
+ const encrypted = await readFile2(this.credentialsFilePath, "utf-8");
618
+ const decrypted = this.decrypt(encrypted);
619
+ return JSON.parse(decrypted);
620
+ } catch (error) {
621
+ if (error.code === "ENOENT") {
622
+ return null;
623
+ }
624
+ throw error;
625
+ }
626
+ }
627
+ /**
628
+ * Encrypt and save credentials file
629
+ */
630
+ async saveCredentialsFile(credFile) {
631
+ const dir = dirname2(this.credentialsFilePath);
632
+ await mkdir2(dir, { recursive: true });
633
+ const json = JSON.stringify(credFile, null, 2);
634
+ const encrypted = this.encrypt(json);
635
+ await writeFile2(this.credentialsFilePath, encrypted, "utf-8");
636
+ }
637
+ /**
638
+ * Check if credentials file exists
639
+ */
640
+ async fileExists() {
641
+ try {
642
+ await access2(this.credentialsFilePath, constants2.F_OK);
643
+ return true;
644
+ } catch {
645
+ return false;
646
+ }
647
+ }
648
+ /**
649
+ * Create empty credentials file structure
650
+ */
651
+ createEmptyCredentialsFile() {
652
+ return {
653
+ version: "1.0",
654
+ encrypted: true,
655
+ providers: {}
656
+ };
657
+ }
658
+ /**
659
+ * Encrypt data using AES-256-GCM
660
+ */
661
+ encrypt(data) {
662
+ const salt = randomBytes(this.saltLength);
663
+ const iv = randomBytes(this.ivLength);
664
+ const key = scryptSync(this.passphrase, salt, this.keyLength);
665
+ const cipher = createCipheriv(this.algorithm, key, iv);
666
+ const encrypted = Buffer.concat([cipher.update(data, "utf-8"), cipher.final()]);
667
+ const tag = cipher.getAuthTag();
668
+ const result = Buffer.concat([salt, iv, tag, encrypted]);
669
+ return result.toString("base64");
670
+ }
671
+ /**
672
+ * Decrypt data using AES-256-GCM
673
+ */
674
+ decrypt(encrypted) {
675
+ const buffer = Buffer.from(encrypted, "base64");
676
+ const salt = buffer.subarray(0, this.saltLength);
677
+ const iv = buffer.subarray(this.saltLength, this.saltLength + this.ivLength);
678
+ const tag = buffer.subarray(
679
+ this.saltLength + this.ivLength,
680
+ this.saltLength + this.ivLength + this.tagLength
681
+ );
682
+ const data = buffer.subarray(this.saltLength + this.ivLength + this.tagLength);
683
+ const key = scryptSync(this.passphrase, salt, this.keyLength);
684
+ const decipher = createDecipheriv(this.algorithm, key, iv);
685
+ decipher.setAuthTag(tag);
686
+ const decrypted = Buffer.concat([decipher.update(data), decipher.final()]);
687
+ return decrypted.toString("utf-8");
688
+ }
689
+ };
690
+ function createCredentialStore(projectDir = process.cwd(), passphrase) {
691
+ const credentialsFilePath = join2(projectDir, ".setbase", "credentials.json");
692
+ const phrase = passphrase || process.env.SETBASE_PASSPHRASE || "default-passphrase";
693
+ return new EncryptedCredentialStore(credentialsFilePath, phrase);
694
+ }
695
+
696
+ // src/manifest/parser.ts
697
+ import { constants as constants3 } from "fs";
698
+ import { readFile as readFile3, writeFile as writeFile3, access as access3, mkdir as mkdir3 } from "fs/promises";
699
+ import { join as join3, dirname as dirname3 } from "path";
700
+ var DefaultManifestParser = class {
701
+ async parse(filePath) {
702
+ try {
703
+ const content = await readFile3(filePath, "utf-8");
704
+ return await this.parseString(content);
705
+ } catch (error) {
706
+ if (error.code === "ENOENT") {
707
+ throw new ManifestError(`Manifest file not found: ${filePath}`);
708
+ }
709
+ throw new ManifestError("Failed to read manifest file", { error, filePath });
710
+ }
711
+ }
712
+ async parseString(content) {
713
+ try {
714
+ const manifest = JSON.parse(content);
715
+ if (!manifest.version) {
716
+ throw new ManifestError("Manifest missing required field: version");
717
+ }
718
+ if (manifest.version !== "1.0") {
719
+ throw new ManifestError(`Unsupported manifest version: ${manifest.version}`);
720
+ }
721
+ if (!manifest.project) {
722
+ throw new ManifestError("Manifest missing required field: project");
723
+ }
724
+ if (!manifest.project.name) {
725
+ throw new ManifestError("Manifest missing required field: project.name");
726
+ }
727
+ if (!manifest.project.runtime) {
728
+ throw new ManifestError("Manifest missing required field: project.runtime");
729
+ }
730
+ logger.debug("Manifest parsed successfully", { project: manifest.project.name });
731
+ return manifest;
732
+ } catch (error) {
733
+ if (error instanceof ManifestError) {
734
+ throw error;
735
+ }
736
+ if (error instanceof SyntaxError) {
737
+ throw new ManifestError("Invalid JSON in manifest file", { error });
738
+ }
739
+ throw new ManifestError("Failed to parse manifest", { error });
740
+ }
741
+ }
742
+ async write(filePath, manifest) {
743
+ try {
744
+ const dir = dirname3(filePath);
745
+ await mkdir3(dir, { recursive: true });
746
+ const content = JSON.stringify(manifest, null, 2);
747
+ await writeFile3(filePath, content, "utf-8");
748
+ logger.debug("Manifest written successfully", { filePath });
749
+ } catch (error) {
750
+ throw new ManifestError("Failed to write manifest file", { error, filePath });
751
+ }
752
+ }
753
+ async exists(filePath) {
754
+ try {
755
+ await access3(filePath, constants3.F_OK);
756
+ return true;
757
+ } catch {
758
+ return false;
759
+ }
760
+ }
761
+ };
762
+ function createManifestParser() {
763
+ return new DefaultManifestParser();
764
+ }
765
+ async function loadManifest(projectDir = process.cwd()) {
766
+ const parser = createManifestParser();
767
+ const manifestPath = join3(projectDir, "setbase.json");
768
+ return parser.parse(manifestPath);
769
+ }
770
+ async function saveManifest(manifest, projectDir = process.cwd()) {
771
+ const parser = createManifestParser();
772
+ const manifestPath = join3(projectDir, "setbase.json");
773
+ return parser.write(manifestPath, manifest);
774
+ }
775
+
776
+ // src/manifest/schema.ts
777
+ import { z } from "zod";
778
+ var projectConfigSchema = z.object({
779
+ name: z.string().min(3).max(50).regex(/^[a-z0-9-]+$/),
780
+ description: z.string().optional(),
781
+ runtime: z.enum(["node", "python", "go"]),
782
+ framework: z.string().optional()
783
+ });
784
+ var databaseConfigSchema = z.object({
785
+ region: z.string().optional(),
786
+ instanceType: z.string().optional(),
787
+ storage: z.number().optional(),
788
+ version: z.string().optional()
789
+ });
790
+ var cacheConfigSchema = z.object({
791
+ region: z.string().optional(),
792
+ maxMemory: z.string().optional(),
793
+ evictionPolicy: z.enum(["allkeys-lru", "volatile-lru"]).optional()
794
+ });
795
+ var storageConfigSchema = z.object({
796
+ region: z.string().optional(),
797
+ public: z.boolean().optional(),
798
+ cors: z.object({
799
+ allowedOrigins: z.array(z.string()),
800
+ allowedMethods: z.array(z.string()).optional(),
801
+ allowedHeaders: z.array(z.string()).optional()
802
+ }).optional()
803
+ });
804
+ var emailConfigSchema = z.object({
805
+ domain: z.string(),
806
+ fromName: z.string().optional(),
807
+ replyTo: z.string().email().optional()
808
+ });
809
+ var computeConfigSchema = z.object({
810
+ runtime: z.enum(["node", "python", "go"]),
811
+ buildCommand: z.string().optional(),
812
+ startCommand: z.string(),
813
+ environmentVariables: z.record(z.string()).optional(),
814
+ region: z.string().optional(),
815
+ healthCheck: z.string().optional()
816
+ });
817
+ var resourceDefinitionSchema = z.object({
818
+ provider: z.string(),
819
+ config: z.record(z.unknown()),
820
+ dependsOn: z.array(z.string()).optional()
821
+ });
822
+ var resourceDefinitionsSchema = z.object({
823
+ database: resourceDefinitionSchema.optional(),
824
+ cache: resourceDefinitionSchema.optional(),
825
+ storage: resourceDefinitionSchema.optional(),
826
+ email: resourceDefinitionSchema.optional()
827
+ });
828
+ var deploymentConfigSchema = z.object({
829
+ provider: z.string(),
830
+ config: computeConfigSchema
831
+ });
832
+ var manifestSchema = z.object({
833
+ $schema: z.string().optional(),
834
+ version: z.literal("1.0"),
835
+ project: projectConfigSchema,
836
+ resources: resourceDefinitionsSchema,
837
+ deployment: deploymentConfigSchema.optional()
838
+ });
839
+
840
+ // src/manifest/validator.ts
841
+ var DefaultManifestValidator = class {
842
+ async validate(manifest) {
843
+ const result = manifestSchema.safeParse(manifest);
844
+ if (result.success) {
845
+ logger.debug("Manifest validation passed");
846
+ return {
847
+ valid: true,
848
+ errors: []
849
+ };
850
+ }
851
+ const errors = result.error.errors.map((error) => ({
852
+ path: error.path.join("."),
853
+ message: error.message
854
+ }));
855
+ logger.warn("Manifest validation failed", { errors });
856
+ return {
857
+ valid: false,
858
+ errors
859
+ };
860
+ }
861
+ };
862
+ function createManifestValidator() {
863
+ return new DefaultManifestValidator();
864
+ }
865
+ async function validateManifest(manifest) {
866
+ const validator = createManifestValidator();
867
+ const result = await validator.validate(manifest);
868
+ if (!result.valid) {
869
+ const errorMessage = result.errors.map((e) => `${e.path}: ${e.message}`).join("\n");
870
+ throw new Error(`Manifest validation failed:
871
+ ${errorMessage}`);
872
+ }
873
+ }
874
+
875
+ // src/environment/env-manager.ts
876
+ import { writeFile as writeFile4 } from "fs/promises";
877
+ var DefaultEnvironmentManager = class {
878
+ constructor(stateManager, envAggregator) {
879
+ this.stateManager = stateManager;
880
+ this.envAggregator = envAggregator;
881
+ }
882
+ stateManager;
883
+ envAggregator;
884
+ async getEnvironmentVariables(environment) {
885
+ logger.info("Getting environment variables", { environment });
886
+ const state = await this.stateManager.load();
887
+ if (!state) {
888
+ throw new Error("Project not initialized");
889
+ }
890
+ const resourceEnvVars = await this.envAggregator.aggregate(state);
891
+ const envVars = {
892
+ NODE_ENV: environment,
893
+ ENVIRONMENT: environment,
894
+ PROJECT_ID: state.projectId,
895
+ ...resourceEnvVars
896
+ };
897
+ return envVars;
898
+ }
899
+ async toDotenv(environment) {
900
+ const vars = await this.getEnvironmentVariables(environment);
901
+ const lines = Object.entries(vars).map(([key, value]) => {
902
+ const escapedValue = value.includes(" ") || value.includes("#") || value.includes("=") ? `"${value.replace(/"/g, '\\"')}"` : value;
903
+ return `${key}=${escapedValue}`;
904
+ });
905
+ return lines.join("\n");
906
+ }
907
+ async toJSON(environment) {
908
+ const vars = await this.getEnvironmentVariables(environment);
909
+ return JSON.stringify(vars, null, 2);
910
+ }
911
+ async writeToFile(environment, filePath, format) {
912
+ logger.info("Writing environment variables to file", { environment, filePath, format });
913
+ const content = format === "env" ? await this.toDotenv(environment) : await this.toJSON(environment);
914
+ await writeFile4(filePath, content, "utf-8");
915
+ logger.info("Environment variables written successfully", { filePath });
916
+ }
917
+ };
918
+
919
+ // src/environment/env-aggregator.ts
920
+ var DefaultEnvironmentAggregator = class {
921
+ constructor(credentialStore) {
922
+ this.credentialStore = credentialStore;
923
+ }
924
+ credentialStore;
925
+ async aggregate(state) {
926
+ logger.info("Aggregating environment variables from resources");
927
+ const envVars = {};
928
+ for (const [resourceName, resourceState] of Object.entries(state.resources)) {
929
+ try {
930
+ const credentials = await this.credentialStore.load(resourceState.provider);
931
+ if (!credentials) {
932
+ logger.warn("No credentials found for provider, skipping", {
933
+ resource: resourceName,
934
+ provider: resourceState.provider
935
+ });
936
+ continue;
937
+ }
938
+ const provider = await ProviderFactory.create(
939
+ resourceState.type,
940
+ resourceState.provider,
941
+ credentials
942
+ );
943
+ const resourceEnvVars = await provider.getEnvironmentVariables(resourceState.resourceId);
944
+ const prefix = resourceName.toUpperCase().replace(/[^A-Z0-9]/g, "_");
945
+ for (const [key, value] of Object.entries(resourceEnvVars)) {
946
+ envVars[key] = value;
947
+ envVars[`${prefix}_${key}`] = value;
948
+ }
949
+ logger.debug("Collected environment variables", {
950
+ resource: resourceName,
951
+ count: Object.keys(resourceEnvVars).length
952
+ });
953
+ } catch (error) {
954
+ logger.error("Failed to get environment variables from resource", {
955
+ resource: resourceName,
956
+ error
957
+ });
958
+ }
959
+ }
960
+ logger.info("Environment variable aggregation complete", {
961
+ totalVars: Object.keys(envVars).length
962
+ });
963
+ return envVars;
964
+ }
965
+ };
966
+
967
+ // src/utils/validation.ts
968
+ var PROJECT_NAME_REGEX = /^[a-z0-9-]{3,50}$/;
969
+ var RESOURCE_NAME_REGEX = /^[a-z0-9-_]{3,50}$/;
970
+ function validateProjectName(name) {
971
+ if (!PROJECT_NAME_REGEX.test(name)) {
972
+ throw new ValidationError(
973
+ "Project name must be 3-50 characters long and contain only lowercase letters, numbers, and hyphens",
974
+ { name }
975
+ );
976
+ }
977
+ }
978
+ function validateResourceName(name) {
979
+ if (!RESOURCE_NAME_REGEX.test(name)) {
980
+ throw new ValidationError(
981
+ "Resource name must be 3-50 characters long and contain only lowercase letters, numbers, hyphens, and underscores",
982
+ { name }
983
+ );
984
+ }
985
+ }
986
+ export {
987
+ BaseProvider,
988
+ DefaultCommandOrchestrator,
989
+ DefaultDeploymentOrchestrator,
990
+ DefaultEnvironmentAggregator,
991
+ DefaultEnvironmentManager,
992
+ DefaultManifestParser,
993
+ DefaultManifestValidator,
994
+ DeploymentError,
995
+ DeploymentStatus,
996
+ EncryptedCredentialStore,
997
+ FileStateManager,
998
+ ManifestError,
999
+ ProviderError,
1000
+ ProviderFactory,
1001
+ ProviderRegistry,
1002
+ ResourceGraph,
1003
+ ResourceStatus,
1004
+ ResourceType,
1005
+ SetbaseError,
1006
+ StateError,
1007
+ ValidationError,
1008
+ createCredentialStore,
1009
+ createManifestParser,
1010
+ createManifestValidator,
1011
+ createStateManager,
1012
+ generateId,
1013
+ generateProjectId,
1014
+ generateResourceId,
1015
+ loadManifest,
1016
+ logger,
1017
+ manifestSchema,
1018
+ saveManifest,
1019
+ validateManifest,
1020
+ validateProjectName,
1021
+ validateResourceName
1022
+ };
1023
+ //# sourceMappingURL=index.js.map