@saws/core 2.0.0-beta.2 → 2.0.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/Host.d.ts +62 -0
  3. package/dist/Host.js +202 -0
  4. package/dist/ServiceDefinition.d.ts +46 -0
  5. package/dist/ServiceDefinition.js +119 -0
  6. package/dist/find-service-definition.d.ts +1 -8
  7. package/dist/find-service-definition.js +0 -6
  8. package/dist/get-saws-config.d.ts +7 -3
  9. package/dist/get-saws-config.js +6 -56
  10. package/dist/host-readiness.d.ts +12 -0
  11. package/dist/host-readiness.js +230 -0
  12. package/dist/index.d.ts +4 -5
  13. package/dist/index.js +4 -4
  14. package/dist/secrets-manager.d.ts +63 -0
  15. package/dist/secrets-manager.js +323 -0
  16. package/dist/tsconfig.tsbuildinfo +1 -0
  17. package/dist/utils/constants.d.ts +4 -0
  18. package/dist/utils/constants.js +5 -0
  19. package/dist/utils/create-directories.d.ts +2 -0
  20. package/dist/utils/create-directories.js +20 -0
  21. package/dist/utils/create-file-if-not-exists.d.ts +1 -0
  22. package/dist/utils/create-file-if-not-exists.js +12 -0
  23. package/dist/utils/create-file-if-note-exists.d.ts +1 -0
  24. package/dist/utils/create-file-if-note-exists.js +11 -0
  25. package/dist/utils/dependency-management.d.ts +16 -0
  26. package/dist/utils/dependency-management.js +111 -0
  27. package/dist/utils/file-exists.d.ts +1 -0
  28. package/dist/utils/file-exists.js +12 -0
  29. package/dist/utils/generate-token.d.ts +1 -0
  30. package/dist/utils/generate-token.js +13 -0
  31. package/dist/utils/get-project-name.d.ts +1 -0
  32. package/dist/utils/get-project-name.js +5 -0
  33. package/dist/utils/get-service-path.js +1 -0
  34. package/dist/utils/list-files.d.ts +1 -0
  35. package/dist/utils/list-files.js +17 -0
  36. package/dist/utils/on-exit.d.ts +1 -0
  37. package/dist/utils/on-exit.js +14 -0
  38. package/dist/utils/parameterized-env-var-name.d.ts +1 -0
  39. package/dist/utils/parameterized-env-var-name.js +1 -0
  40. package/dist/utils/recursively-read-dir.d.ts +1 -0
  41. package/dist/utils/recursively-read-dir.js +15 -0
  42. package/dist/utils/retry-until.d.ts +1 -0
  43. package/dist/utils/retry-until.js +9 -0
  44. package/dist/utils/run-local.d.ts +8 -0
  45. package/dist/utils/run-local.js +86 -0
  46. package/dist/utils/shell-quote.d.ts +1 -0
  47. package/dist/utils/shell-quote.js +3 -0
  48. package/dist/utils/stage-outputs.d.ts +3 -0
  49. package/dist/utils/stage-outputs.js +24 -0
  50. package/dist/utils/uppercase.d.ts +1 -0
  51. package/dist/utils/uppercase.js +4 -0
  52. package/package.json +32 -24
  53. package/dist/context.d.ts +0 -41
  54. package/dist/context.d.ts.map +0 -1
  55. package/dist/context.js +0 -38
  56. package/dist/context.test.d.ts +0 -2
  57. package/dist/context.test.d.ts.map +0 -1
  58. package/dist/context.test.js +0 -13
  59. package/dist/find-service-definition.d.ts.map +0 -1
  60. package/dist/find-service-definition.test.d.ts +0 -2
  61. package/dist/find-service-definition.test.d.ts.map +0 -1
  62. package/dist/find-service-definition.test.js +0 -44
  63. package/dist/get-saws-config.d.ts.map +0 -1
  64. package/dist/index.d.ts.map +0 -1
  65. package/dist/service-commands.d.ts +0 -10
  66. package/dist/service-commands.d.ts.map +0 -1
  67. package/dist/service-commands.js +0 -25
  68. package/dist/service-commands.test.d.ts +0 -2
  69. package/dist/service-commands.test.d.ts.map +0 -1
  70. package/dist/service-commands.test.js +0 -26
  71. package/dist/service-definition.d.ts +0 -36
  72. package/dist/service-definition.d.ts.map +0 -1
  73. package/dist/service-definition.js +0 -104
  74. package/dist/service-types.d.ts +0 -9
  75. package/dist/service-types.d.ts.map +0 -1
  76. /package/dist/{service-types.js → utils/get-service-path.d.ts} +0 -0
@@ -0,0 +1,13 @@
1
+ import * as crypto from "crypto";
2
+ export const generateToken = async () => {
3
+ return new Promise((resolve, reject) => {
4
+ crypto.randomBytes(20, (err, buffer) => {
5
+ if (err) {
6
+ reject(err);
7
+ return;
8
+ }
9
+ resolve(buffer.toString("hex"));
10
+ return;
11
+ });
12
+ });
13
+ };
@@ -0,0 +1 @@
1
+ export declare const getProjectName: () => string;
@@ -0,0 +1,5 @@
1
+ import { default as finder } from "find-package-json";
2
+ export const getProjectName = () => {
3
+ const pkg = finder(import.meta.dirname).next().value;
4
+ return pkg.name;
5
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export declare function listFiles(directory: string): Promise<string[]>;
@@ -0,0 +1,17 @@
1
+ import { readdir } from "node:fs/promises";
2
+ import path from "node:path";
3
+ export async function listFiles(directory) {
4
+ const entries = await readdir(directory, { withFileTypes: true });
5
+ const files = [];
6
+ for (const entry of entries) {
7
+ const fullPath = path.join(directory, entry.name);
8
+ if (entry.isDirectory()) {
9
+ files.push(...(await listFiles(fullPath)));
10
+ continue;
11
+ }
12
+ if (entry.isFile()) {
13
+ files.push(fullPath);
14
+ }
15
+ }
16
+ return files;
17
+ }
@@ -0,0 +1 @@
1
+ export declare const onProcessExit: (callback: () => void) => void;
@@ -0,0 +1,14 @@
1
+ let registeredFunctions = [];
2
+ export const onProcessExit = (callback) => {
3
+ registeredFunctions.push(callback);
4
+ };
5
+ process.on("exit", () => {
6
+ registeredFunctions.forEach((f) => f());
7
+ registeredFunctions = [];
8
+ process.exit();
9
+ });
10
+ process.on("SIGINT", () => {
11
+ registeredFunctions.forEach((f) => f());
12
+ registeredFunctions = [];
13
+ process.exit();
14
+ });
@@ -0,0 +1 @@
1
+ export declare const parameterizedEnvVarName: (name: string, variable: string) => string;
@@ -0,0 +1 @@
1
+ export const parameterizedEnvVarName = (name, variable) => `${name.replace(/[^a-zA-Z\d]/g, "_").toUpperCase()}_${variable}`;
@@ -0,0 +1 @@
1
+ export declare const recursivelyReadDir: (dir: string) => Promise<string[]>;
@@ -0,0 +1,15 @@
1
+ import { promises as fs } from "fs";
2
+ import { resolve } from "path";
3
+ export const recursivelyReadDir = async (dir) => {
4
+ const files = [];
5
+ const dirContents = await fs.readdir(dir, { withFileTypes: true });
6
+ for (const item of dirContents) {
7
+ if (item.isDirectory()) {
8
+ const nestedFiles = await recursivelyReadDir(resolve(dir, item.name));
9
+ files.push(...nestedFiles);
10
+ continue;
11
+ }
12
+ files.push(resolve(dir, item.name));
13
+ }
14
+ return files;
15
+ };
@@ -0,0 +1 @@
1
+ export declare const retryUntil: (callback: () => Promise<boolean>, timeout: number) => Promise<void>;
@@ -0,0 +1,9 @@
1
+ export const retryUntil = async (callback, timeout) => {
2
+ while (true) {
3
+ const done = await callback();
4
+ if (done)
5
+ break;
6
+ await new Promise((r) => setTimeout(r, timeout));
7
+ }
8
+ return;
9
+ };
@@ -0,0 +1,8 @@
1
+ import type { RuntimeLogSink } from "../ServiceDefinition.js";
2
+ export declare function runLocal(command: string, options?: {
3
+ dryRun?: boolean;
4
+ input?: string;
5
+ logSink?: RuntimeLogSink;
6
+ serviceName?: string;
7
+ signal?: AbortSignal;
8
+ }): Promise<void>;
@@ -0,0 +1,86 @@
1
+ import { spawn } from "node:child_process";
2
+ export async function runLocal(command, options = {}) {
3
+ if (options.dryRun) {
4
+ if (options.logSink == null) {
5
+ console.log(`[dry-run:local] ${command}`);
6
+ }
7
+ else {
8
+ options.logSink({
9
+ serviceName: options.serviceName ?? "system",
10
+ stream: "stdout",
11
+ chunk: `[dry-run:local] ${command}\n`,
12
+ timestamp: new Date(),
13
+ });
14
+ }
15
+ return;
16
+ }
17
+ await new Promise((resolve, reject) => {
18
+ if (options.signal?.aborted) {
19
+ reject(new Error(`local command aborted: ${command}`));
20
+ return;
21
+ }
22
+ const captureOutput = options.logSink != null;
23
+ const serviceName = options.serviceName ?? "system";
24
+ const child = spawn(command, {
25
+ shell: true,
26
+ detached: true,
27
+ stdio: [
28
+ options.input == null ? "ignore" : "pipe",
29
+ captureOutput ? "pipe" : "inherit",
30
+ captureOutput ? "pipe" : "inherit",
31
+ ],
32
+ });
33
+ let aborted = false;
34
+ const abort = () => {
35
+ aborted = true;
36
+ if (child.pid == null)
37
+ return;
38
+ try {
39
+ process.kill(-child.pid, "SIGTERM");
40
+ }
41
+ catch (error) {
42
+ if (error.code !== "ESRCH") {
43
+ options.logSink?.({
44
+ serviceName: options.serviceName ?? "system",
45
+ stream: "stderr",
46
+ chunk: `Failed to stop local command: ${error.message}\n`,
47
+ timestamp: new Date(),
48
+ });
49
+ }
50
+ }
51
+ };
52
+ options.signal?.addEventListener("abort", abort, { once: true });
53
+ if (options.input != null) {
54
+ child.stdin?.end(options.input);
55
+ }
56
+ child.stdout?.on("data", (chunk) => {
57
+ options.logSink?.({
58
+ serviceName,
59
+ stream: "stdout",
60
+ chunk: chunk.toString("utf8"),
61
+ timestamp: new Date(),
62
+ });
63
+ });
64
+ child.stderr?.on("data", (chunk) => {
65
+ options.logSink?.({
66
+ serviceName,
67
+ stream: "stderr",
68
+ chunk: chunk.toString("utf8"),
69
+ timestamp: new Date(),
70
+ });
71
+ });
72
+ child.on("error", reject);
73
+ child.on("exit", (code) => {
74
+ options.signal?.removeEventListener("abort", abort);
75
+ if (aborted) {
76
+ reject(new Error(`local command aborted: ${command}`));
77
+ return;
78
+ }
79
+ if (code === 0) {
80
+ resolve();
81
+ return;
82
+ }
83
+ reject(new Error(`local command exited with code ${code}: ${command}`));
84
+ });
85
+ });
86
+ }
@@ -0,0 +1 @@
1
+ export declare function shellQuote(value: string): string;
@@ -0,0 +1,3 @@
1
+ export function shellQuote(value) {
2
+ return `'${value.replaceAll("'", "'\\''")}'`;
3
+ }
@@ -0,0 +1,3 @@
1
+ export type Outputs = Record<string, string | number | boolean | null | undefined>;
2
+ export declare const getStageOutputs: (stage: string) => Promise<Record<string, Outputs>>;
3
+ export declare const writeStageOutputs: (newOutputs: Record<string, Outputs>, stage: string) => Promise<Record<string, Outputs>>;
@@ -0,0 +1,24 @@
1
+ import { promises as fs } from "node:fs";
2
+ import path from "node:path";
3
+ import { SAWS_DIR } from "./constants.js";
4
+ export const getStageOutputs = async (stage) => {
5
+ const outputPath = path.resolve(SAWS_DIR, `saws-${stage}-output.json`);
6
+ try {
7
+ await fs.stat(outputPath);
8
+ const outputsText = await fs.readFile(outputPath, { encoding: "utf-8" });
9
+ return JSON.parse(outputsText);
10
+ }
11
+ catch (err) {
12
+ return {};
13
+ }
14
+ };
15
+ export const writeStageOutputs = async (newOutputs, stage) => {
16
+ const currentOutputs = await getStageOutputs(stage);
17
+ // write outputs
18
+ const outputs = {
19
+ ...currentOutputs,
20
+ ...newOutputs,
21
+ };
22
+ await fs.writeFile(path.resolve(SAWS_DIR, `saws-${stage}-output.json`), JSON.stringify(outputs, null, 2));
23
+ return outputs;
24
+ };
@@ -0,0 +1 @@
1
+ export declare const uppercase: (text: string) => string;
@@ -0,0 +1,4 @@
1
+ export const uppercase = (text) => {
2
+ const [first, ...rest] = text;
3
+ return first.toUpperCase() + rest.join("");
4
+ };
package/package.json CHANGED
@@ -1,34 +1,42 @@
1
1
  {
2
2
  "name": "@saws/core",
3
- "version": "2.0.0-beta.2",
4
- "repository": {
5
- "type": "git",
6
- "url": "git+https://github.com/shichongrui/saws.git",
7
- "directory": "packages/core"
8
- },
9
- "type": "module",
10
- "main": "./dist/index.js",
11
- "types": "./dist/index.d.ts",
12
- "exports": {
13
- ".": {
14
- "types": "./dist/index.d.ts",
15
- "import": "./dist/index.js"
16
- }
17
- },
3
+ "version": "2.0.0-beta.3",
4
+ "description": "",
5
+ "keywords": [],
6
+ "license": "MIT",
7
+ "author": "",
18
8
  "files": [
19
- "dist"
9
+ "./dist"
20
10
  ],
21
- "scripts": {
22
- "prebuild": "node -e \"fs.rmSync('dist', { recursive: true, force: true })\"",
23
- "build": "tsc -p tsconfig.json",
24
- "test": "npm run build && node --test dist/*.test.js",
25
- "typecheck": "tsc -p tsconfig.json --noEmit"
11
+ "type": "module",
12
+ "exports": {
13
+ ".": "./dist/index.js",
14
+ "./ServiceDefinition": "./dist/ServiceDefinition.js",
15
+ "./get-saws-config": "./dist/get-saws-config.js",
16
+ "./Host": "./dist/Host.js",
17
+ "./host-readiness": "./dist/host-readiness.js",
18
+ "./secrets-manager": "./dist/secrets-manager.js",
19
+ "./utils/constants": "./dist/utils/constants.js",
20
+ "./utils/create-directories": "./dist/utils/create-directories.js",
21
+ "./utils/file-exists": "./dist/utils/file-exists.js",
22
+ "./utils/generate-token": "./dist/utils/generate-token.js",
23
+ "./utils/get-project-name": "./dist/utils/get-project-name.js",
24
+ "./utils/list-files": "./dist/utils/list-files.js",
25
+ "./utils/on-exit": "./dist/utils/on-exit.js",
26
+ "./utils/parameterized-env-var-name": "./dist/utils/parameterized-env-var-name.js",
27
+ "./utils/recursively-read-dir": "./dist/utils/recursively-read-dir.js",
28
+ "./utils/retry-until": "./dist/utils/retry-until.js",
29
+ "./utils/shell-quote": "./dist/utils/shell-quote.js",
30
+ "./utils/stage-outputs": "./dist/utils/stage-outputs.js",
31
+ "./utils/uppercase": "./dist/utils/uppercase.js",
32
+ "./utils/create-file-if-not-exists": "./dist/utils/create-file-if-not-exists.js",
33
+ "./utils/dependency-management": "./dist/utils/dependency-management.js",
34
+ "./utils/run-local": "./dist/utils/run-local.js"
26
35
  },
27
36
  "dependencies": {
28
- "commander": "^14.0.3",
29
- "typescript": "^5.3.3"
37
+ "find-package-json": "^1.2.0"
30
38
  },
31
39
  "devDependencies": {
32
- "@types/node": "^20.11.19"
40
+ "@types/find-package-json": "^1.2.7"
33
41
  }
34
42
  }
package/dist/context.d.ts DELETED
@@ -1,41 +0,0 @@
1
- import type { ServiceDefinition } from "./service-definition.js";
2
- export type StageName = "local" | string;
3
- export interface RuntimeContextConfig {
4
- stage: StageName;
5
- rootDir?: string;
6
- env?: Record<string, string>;
7
- dryRun?: boolean;
8
- logSink?: RuntimeLogSink;
9
- currentServiceName?: string;
10
- }
11
- export interface RuntimeLogEntry {
12
- serviceName: string;
13
- stream: "stdout" | "stderr";
14
- chunk: string;
15
- timestamp: Date;
16
- }
17
- export type RuntimeLogSink = (entry: RuntimeLogEntry) => void;
18
- export declare class RuntimeContext {
19
- readonly stage: StageName;
20
- readonly rootDir: string;
21
- readonly env: Record<string, string>;
22
- readonly dryRun: boolean;
23
- readonly logSink?: RuntimeLogSink;
24
- readonly currentServiceName?: string;
25
- constructor(config: RuntimeContextConfig);
26
- writeLog(chunk: string, stream?: "stdout" | "stderr"): void;
27
- }
28
- export declare class InitContext extends RuntimeContext {
29
- }
30
- export declare class DevContext extends RuntimeContext {
31
- }
32
- export declare class DeployContext extends RuntimeContext {
33
- }
34
- export declare class ExitContext extends RuntimeContext {
35
- }
36
- export declare function assertValidStageName(stage: string): void;
37
- export interface DependencyEnvironment {
38
- service: ServiceDefinition;
39
- variables: Record<string, string>;
40
- }
41
- //# sourceMappingURL=context.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAEjE,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;AAEzC,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,SAAS,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,MAAM,cAAc,GAAG,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;AAE9D,qBAAa,cAAc;IACzB,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,cAAc,CAAC;IAClC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;gBAEzB,MAAM,EAAE,oBAAoB;IAUxC,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,GAAE,QAAQ,GAAG,QAAmB;CAQ/D;AAED,qBAAa,WAAY,SAAQ,cAAc;CAAG;AAClD,qBAAa,UAAW,SAAQ,cAAc;CAAG;AACjD,qBAAa,aAAc,SAAQ,cAAc;CAAG;AACpD,qBAAa,WAAY,SAAQ,cAAc;CAAG;AAElD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,QAMjD;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,iBAAiB,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC"}
package/dist/context.js DELETED
@@ -1,38 +0,0 @@
1
- export class RuntimeContext {
2
- stage;
3
- rootDir;
4
- env;
5
- dryRun;
6
- logSink;
7
- currentServiceName;
8
- constructor(config) {
9
- assertValidStageName(config.stage);
10
- this.stage = config.stage;
11
- this.rootDir = config.rootDir ?? process.cwd();
12
- this.env = config.env ?? process.env;
13
- this.dryRun = config.dryRun ?? false;
14
- this.logSink = config.logSink;
15
- this.currentServiceName = config.currentServiceName;
16
- }
17
- writeLog(chunk, stream = "stdout") {
18
- this.logSink?.({
19
- serviceName: this.currentServiceName ?? "system",
20
- stream,
21
- chunk,
22
- timestamp: new Date(),
23
- });
24
- }
25
- }
26
- export class InitContext extends RuntimeContext {
27
- }
28
- export class DevContext extends RuntimeContext {
29
- }
30
- export class DeployContext extends RuntimeContext {
31
- }
32
- export class ExitContext extends RuntimeContext {
33
- }
34
- export function assertValidStageName(stage) {
35
- if (!/^[a-z0-9][a-z0-9._-]*$/.test(stage)) {
36
- throw new Error(`Invalid stage "${stage}": stages must start with a lower-case letter or number and contain only lower-case letters, numbers, ".", "_", or "-"`);
37
- }
38
- }
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=context.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"context.test.d.ts","sourceRoot":"","sources":["../src/context.test.ts"],"names":[],"mappings":""}
@@ -1,13 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import test from "node:test";
3
- import { RuntimeContext } from "./context.js";
4
- test("accepts stage names that are safe for Docker resources and paths", () => {
5
- for (const stage of ["local", "production", "review-42", "release_1.2"]) {
6
- assert.equal(new RuntimeContext({ stage }).stage, stage);
7
- }
8
- });
9
- test("rejects unsafe or ambiguous stage names", () => {
10
- for (const stage of ["", "../production", "Production", "review 42", "."]) {
11
- assert.throws(() => new RuntimeContext({ stage }), /Invalid stage/);
12
- }
13
- });
@@ -1 +0,0 @@
1
- {"version":3,"file":"find-service-definition.d.ts","sourceRoot":"","sources":["../src/find-service-definition.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAEjE;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,iBAAiB,EACvB,IAAI,EAAE,MAAM,GACX,iBAAiB,CA+BnB"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=find-service-definition.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"find-service-definition.test.d.ts","sourceRoot":"","sources":["../src/find-service-definition.test.ts"],"names":[],"mappings":""}
@@ -1,44 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import test from "node:test";
3
- import { findServiceDefinition } from "./find-service-definition.js";
4
- import { ServiceDefinition } from "./service-definition.js";
5
- test("finds a nested service by its configured name", () => {
6
- const database = new ServiceDefinition({ name: "db" });
7
- const api = new ServiceDefinition({
8
- name: "api",
9
- dependencies: [database],
10
- });
11
- const root = new ServiceDefinition({
12
- name: "app",
13
- dependencies: [api],
14
- });
15
- assert.equal(findServiceDefinition(root, "db"), database);
16
- });
17
- test("allows one shared service instance to appear in multiple branches", () => {
18
- const database = new ServiceDefinition({ name: "db" });
19
- const root = new ServiceDefinition({
20
- name: "app",
21
- dependencies: [
22
- new ServiceDefinition({ name: "api", dependencies: [database] }),
23
- new ServiceDefinition({ name: "worker", dependencies: [database] }),
24
- ],
25
- });
26
- assert.equal(findServiceDefinition(root, "db"), database);
27
- });
28
- test("reports available services when the requested service is missing", () => {
29
- const root = new ServiceDefinition({
30
- name: "app",
31
- dependencies: [new ServiceDefinition({ name: "db" })],
32
- });
33
- assert.throws(() => findServiceDefinition(root, "missing"), /Available services: app, db/);
34
- });
35
- test("rejects duplicate names assigned to different service instances", () => {
36
- const root = new ServiceDefinition({
37
- name: "app",
38
- dependencies: [
39
- new ServiceDefinition({ name: "worker" }),
40
- new ServiceDefinition({ name: "worker" }),
41
- ],
42
- });
43
- assert.throws(() => findServiceDefinition(root, "worker"), /Service name "worker" is ambiguous/);
44
- });
@@ -1 +0,0 @@
1
- {"version":3,"file":"get-saws-config.d.ts","sourceRoot":"","sources":["../src/get-saws-config.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAEjE,wBAAsB,aAAa,CACjC,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,iBAAiB,CAAC,CAuB5B"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,8BAA8B,CAAC;AAC7C,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,oBAAoB,CAAC"}
@@ -1,10 +0,0 @@
1
- import type { Command } from "commander";
2
- import { ServiceDefinition } from "./service-definition.js";
3
- /**
4
- * Collects commands contributed by the configured service types.
5
- *
6
- * A service class contributes its commands once even when several instances
7
- * exist in the graph or a shared instance appears in multiple branches.
8
- */
9
- export declare function getServiceCommands(root: ServiceDefinition): Command[];
10
- //# sourceMappingURL=service-commands.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"service-commands.d.ts","sourceRoot":"","sources":["../src/service-commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAI5D;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,EAAE,CAoBrE"}
@@ -1,25 +0,0 @@
1
- /**
2
- * Collects commands contributed by the configured service types.
3
- *
4
- * A service class contributes its commands once even when several instances
5
- * exist in the graph or a shared instance appears in multiple branches.
6
- */
7
- export function getServiceCommands(root) {
8
- const visitedServices = new Set();
9
- const visitedConstructors = new Set();
10
- const commands = [];
11
- const visit = (service) => {
12
- if (visitedServices.has(service))
13
- return;
14
- visitedServices.add(service);
15
- const constructor = service.constructor;
16
- if (!visitedConstructors.has(constructor)) {
17
- visitedConstructors.add(constructor);
18
- commands.push(...constructor.getCommands());
19
- }
20
- for (const dependency of service.dependencies)
21
- visit(dependency);
22
- };
23
- visit(root);
24
- return commands;
25
- }
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=service-commands.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"service-commands.test.d.ts","sourceRoot":"","sources":["../src/service-commands.test.ts"],"names":[],"mappings":""}
@@ -1,26 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import test from "node:test";
3
- import { Command } from "commander";
4
- import { ServiceDefinition } from "./service-definition.js";
5
- import { getServiceCommands } from "./service-commands.js";
6
- class CommandService extends ServiceDefinition {
7
- static getCommands() {
8
- return [new Command("custom")];
9
- }
10
- }
11
- test("collects commands once per configured service type", () => {
12
- const shared = new CommandService({ name: "shared" });
13
- const first = new CommandService({
14
- name: "first",
15
- dependencies: [shared],
16
- });
17
- const second = new CommandService({
18
- name: "second",
19
- dependencies: [shared],
20
- });
21
- const root = new ServiceDefinition({
22
- name: "root",
23
- dependencies: [first, second],
24
- });
25
- assert.deepEqual(getServiceCommands(root).map((command) => command.name()), ["custom"]);
26
- });
@@ -1,36 +0,0 @@
1
- import { DeployContext, DevContext, ExitContext, InitContext, RuntimeContext } from "./context.js";
2
- import type { Command } from "commander";
3
- import type { EnvironmentVariables, EnvironmentVariableTarget, ServiceOutputs, ServicePermission } from "./service-types.js";
4
- export interface ServiceDefinitionConfig {
5
- name: string;
6
- dependencies?: ServiceDefinition[];
7
- }
8
- export declare class ServiceDefinition {
9
- static getCommands(): Command[];
10
- readonly name: string;
11
- readonly dependencies: ServiceDefinition[];
12
- private initializedStages;
13
- private devedStages;
14
- private deployedStages;
15
- private outputsByStage;
16
- constructor(config: ServiceDefinitionConfig);
17
- init(context?: InitContext): Promise<void>;
18
- dev(context?: DevContext): Promise<void>;
19
- deploy(contextOrStage: DeployContext | string): Promise<void>;
20
- exit(context?: ExitContext): Promise<void>;
21
- setOutputs(stage: string, outputs: ServiceOutputs): void;
22
- getOutputs(context: RuntimeContext): Promise<ServiceOutputs>;
23
- getEnvironmentVariables(context: RuntimeContext, _target?: EnvironmentVariableTarget): Promise<EnvironmentVariables>;
24
- getDependenciesEnvironmentVariables(context: RuntimeContext, target?: EnvironmentVariableTarget): Promise<EnvironmentVariables>;
25
- getPermissions(context: RuntimeContext): Promise<ServicePermission[]>;
26
- /**
27
- * Bootstrap application-owned files or dependencies required by this
28
- * service. Implementations must be safe to run again in a later process and
29
- * should resolve generated paths from context.rootDir.
30
- */
31
- protected onInit(_context: InitContext): Promise<void>;
32
- protected onDev(_context: DevContext): Promise<void>;
33
- protected onDeploy(_context: DeployContext): Promise<void>;
34
- protected onExit(_context: ExitContext): Promise<void>;
35
- }
36
- //# sourceMappingURL=service-definition.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"service-definition.d.ts","sourceRoot":"","sources":["../src/service-definition.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,UAAU,EACV,WAAW,EACX,WAAW,EACX,cAAc,EACf,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EACV,oBAAoB,EACpB,yBAAyB,EACzB,cAAc,EACd,iBAAiB,EAClB,MAAM,oBAAoB,CAAC;AAE5B,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,iBAAiB,EAAE,CAAC;CACpC;AAED,qBAAa,iBAAiB;IAC5B,MAAM,CAAC,WAAW,IAAI,OAAO,EAAE;IAI/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,YAAY,EAAE,iBAAiB,EAAE,CAAC;IAE3C,OAAO,CAAC,iBAAiB,CAAqB;IAC9C,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,cAAc,CAAqB;IAC3C,OAAO,CAAC,cAAc,CAAqC;gBAE/C,MAAM,EAAE,uBAAuB;IAKrC,IAAI,CAAC,OAAO,cAAsC;IAWlD,GAAG,CAAC,OAAO,aAAqC;IAYhD,MAAM,CAAC,cAAc,EAAE,aAAa,GAAG,MAAM;IAgB7C,IAAI,CAAC,OAAO,cAAsC;IAQxD,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc;IAO3C,UAAU,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;IAI5D,uBAAuB,CAC3B,OAAO,EAAE,cAAc,EACvB,OAAO,GAAE,yBAAkC,GAC1C,OAAO,CAAC,oBAAoB,CAAC;IAI1B,mCAAmC,CACvC,OAAO,EAAE,cAAc,EACvB,MAAM,GAAE,yBAAkC,GACzC,OAAO,CAAC,oBAAoB,CAAC;IAa1B,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAI3E;;;;OAIG;cACa,MAAM,CAAC,QAAQ,EAAE,WAAW;cAC5B,KAAK,CAAC,QAAQ,EAAE,UAAU;cAC1B,QAAQ,CAAC,QAAQ,EAAE,aAAa;cAChC,MAAM,CAAC,QAAQ,EAAE,WAAW;CAC7C"}