container-superposition 0.1.1 → 0.1.4
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 +569 -8
- package/dist/scripts/init.js +436 -254
- package/dist/scripts/init.js.map +1 -1
- package/dist/tool/commands/doctor.d.ts +15 -0
- package/dist/tool/commands/doctor.d.ts.map +1 -0
- package/dist/tool/commands/doctor.js +862 -0
- package/dist/tool/commands/doctor.js.map +1 -0
- package/dist/tool/commands/explain.d.ts +13 -0
- package/dist/tool/commands/explain.d.ts.map +1 -0
- package/dist/tool/commands/explain.js +299 -0
- package/dist/tool/commands/explain.js.map +1 -0
- package/dist/tool/commands/list.d.ts +16 -0
- package/dist/tool/commands/list.d.ts.map +1 -0
- package/dist/tool/commands/list.js +121 -0
- package/dist/tool/commands/list.js.map +1 -0
- package/dist/tool/commands/plan.d.ts +67 -0
- package/dist/tool/commands/plan.d.ts.map +1 -0
- package/dist/tool/commands/plan.js +851 -0
- package/dist/tool/commands/plan.js.map +1 -0
- package/dist/tool/questionnaire/composer.d.ts +16 -2
- package/dist/tool/questionnaire/composer.d.ts.map +1 -1
- package/dist/tool/questionnaire/composer.js +411 -200
- package/dist/tool/questionnaire/composer.js.map +1 -1
- package/dist/tool/readme/markdown-parser.d.ts.map +1 -1
- package/dist/tool/readme/markdown-parser.js.map +1 -1
- package/dist/tool/readme/readme-generator.d.ts.map +1 -1
- package/dist/tool/readme/readme-generator.js +11 -6
- package/dist/tool/readme/readme-generator.js.map +1 -1
- package/dist/tool/schema/deployment-targets.d.ts +77 -0
- package/dist/tool/schema/deployment-targets.d.ts.map +1 -0
- package/dist/tool/schema/deployment-targets.js +91 -0
- package/dist/tool/schema/deployment-targets.js.map +1 -0
- package/dist/tool/schema/manifest-migrations.d.ts +51 -0
- package/dist/tool/schema/manifest-migrations.d.ts.map +1 -0
- package/dist/tool/schema/manifest-migrations.js +159 -0
- package/dist/tool/schema/manifest-migrations.js.map +1 -0
- package/dist/tool/schema/overlay-loader.d.ts +1 -1
- package/dist/tool/schema/overlay-loader.d.ts.map +1 -1
- package/dist/tool/schema/overlay-loader.js +42 -14
- package/dist/tool/schema/overlay-loader.js.map +1 -1
- package/dist/tool/schema/types.d.ts +62 -2
- package/dist/tool/schema/types.d.ts.map +1 -1
- package/dist/tool/utils/gitignore.d.ts +15 -0
- package/dist/tool/utils/gitignore.d.ts.map +1 -0
- package/dist/tool/utils/gitignore.js +41 -0
- package/dist/tool/utils/gitignore.js.map +1 -0
- package/dist/tool/utils/merge.d.ts +134 -0
- package/dist/tool/utils/merge.d.ts.map +1 -0
- package/dist/tool/utils/merge.js +277 -0
- package/dist/tool/utils/merge.js.map +1 -0
- package/dist/tool/utils/port-utils.d.ts +29 -0
- package/dist/tool/utils/port-utils.d.ts.map +1 -0
- package/dist/tool/utils/port-utils.js +128 -0
- package/dist/tool/utils/port-utils.js.map +1 -0
- package/dist/tool/utils/services-export.d.ts +14 -0
- package/dist/tool/utils/services-export.d.ts.map +1 -0
- package/dist/tool/utils/services-export.js +478 -0
- package/dist/tool/utils/services-export.js.map +1 -0
- package/dist/tool/utils/summary.d.ts +69 -0
- package/dist/tool/utils/summary.d.ts.map +1 -0
- package/dist/tool/utils/summary.js +260 -0
- package/dist/tool/utils/summary.js.map +1 -0
- package/dist/tool/utils/version.d.ts +9 -0
- package/dist/tool/utils/version.d.ts.map +1 -0
- package/dist/tool/utils/version.js +32 -0
- package/dist/tool/utils/version.js.map +1 -0
- package/docs/architecture.md +25 -21
- package/docs/deployment-targets.md +150 -0
- package/docs/discovery-commands.md +442 -0
- package/docs/merge-strategy.md +700 -0
- package/docs/minimal-and-editor.md +265 -0
- package/docs/overlay-imports.md +209 -0
- package/docs/overlay-manifest-refactoring.md +2 -2
- package/docs/overlay-metadata-archive.md +1 -1
- package/docs/overlays.md +139 -28
- package/docs/presets-architecture.md +3 -3
- package/docs/presets.md +1 -1
- package/docs/publishing.md +36 -35
- package/docs/team-workflow.md +540 -0
- package/overlays/.presets/data-engineering.yml +392 -0
- package/overlays/.presets/event-sourced-service.yml +262 -0
- package/overlays/.presets/frontend.yml +287 -0
- package/overlays/.presets/k8s-operator-dev.yml +462 -0
- package/overlays/{presets → .presets}/microservice.yml +32 -6
- package/overlays/.presets/web-api.yml +129 -0
- package/overlays/.registry/README.md +1 -1
- package/overlays/.registry/deployment-targets.yml +54 -0
- package/overlays/.shared/README.md +43 -0
- package/overlays/.shared/compose/common-healthchecks.yml +38 -0
- package/overlays/.shared/otel/instrumentation.env +20 -0
- package/overlays/.shared/otel/otel-base-config.yaml +30 -0
- package/overlays/.shared/vscode/recommended-extensions.json +14 -0
- package/overlays/README.md +1 -1
- package/overlays/cloudflared/README.md +190 -0
- package/overlays/cloudflared/devcontainer.patch.json +3 -0
- package/overlays/cloudflared/overlay.yml +15 -0
- package/overlays/cloudflared/setup.sh +49 -0
- package/overlays/cloudflared/verify.sh +21 -0
- package/overlays/codex/overlay.yml +1 -0
- package/overlays/direnv/README.md +6 -4
- package/overlays/direnv/setup.sh +0 -12
- package/overlays/duckdb/README.md +274 -0
- package/overlays/duckdb/devcontainer.patch.json +10 -0
- package/overlays/duckdb/overlay.yml +17 -0
- package/overlays/duckdb/setup.sh +45 -0
- package/overlays/duckdb/verify.sh +32 -0
- package/overlays/git-helpers/overlay.yml +1 -0
- package/overlays/grafana/README.md +5 -5
- package/overlays/grafana/dashboard-provider.yml +1 -1
- package/overlays/grafana/docker-compose.yml +2 -2
- package/overlays/grafana/overlay.yml +6 -1
- package/overlays/grpc-tools/README.md +242 -0
- package/overlays/grpc-tools/devcontainer.patch.json +14 -0
- package/overlays/grpc-tools/overlay.yml +14 -0
- package/overlays/grpc-tools/setup.sh +57 -0
- package/overlays/grpc-tools/verify.sh +47 -0
- package/overlays/jaeger/overlay.yml +16 -3
- package/overlays/jupyter/.env.example +6 -0
- package/overlays/jupyter/README.md +210 -0
- package/overlays/jupyter/devcontainer.patch.json +14 -0
- package/overlays/jupyter/docker-compose.yml +23 -0
- package/overlays/jupyter/overlay.yml +18 -0
- package/overlays/jupyter/verify.sh +35 -0
- package/overlays/keycloak/.env.example +5 -0
- package/overlays/keycloak/README.md +238 -0
- package/overlays/keycloak/devcontainer.patch.json +17 -0
- package/overlays/keycloak/docker-compose.yml +32 -0
- package/overlays/keycloak/overlay.yml +23 -0
- package/overlays/keycloak/verify.sh +54 -0
- package/overlays/kind/README.md +221 -0
- package/overlays/kind/devcontainer.patch.json +10 -0
- package/overlays/kind/overlay.yml +18 -0
- package/overlays/kind/setup.sh +43 -0
- package/overlays/kind/verify.sh +40 -0
- package/overlays/localstack/.env.example +6 -0
- package/overlays/localstack/README.md +188 -0
- package/overlays/localstack/devcontainer.patch.json +21 -0
- package/overlays/localstack/docker-compose.yml +25 -0
- package/overlays/localstack/overlay.yml +18 -0
- package/overlays/localstack/verify.sh +47 -0
- package/overlays/loki/overlay.yml +6 -1
- package/overlays/mailpit/.env.example +4 -0
- package/overlays/mailpit/README.md +191 -0
- package/overlays/mailpit/devcontainer.patch.json +20 -0
- package/overlays/mailpit/docker-compose.yml +17 -0
- package/overlays/mailpit/overlay.yml +26 -0
- package/overlays/mailpit/verify.sh +52 -0
- package/overlays/modern-cli-tools/overlay.yml +1 -0
- package/overlays/mongodb/overlay.yml +12 -2
- package/overlays/mysql/overlay.yml +12 -2
- package/overlays/nats/overlay.yml +12 -2
- package/overlays/ngrok/overlay.yml +2 -1
- package/overlays/openapi-tools/README.md +243 -0
- package/overlays/openapi-tools/devcontainer.patch.json +10 -0
- package/overlays/openapi-tools/overlay.yml +16 -0
- package/overlays/openapi-tools/setup.sh +45 -0
- package/overlays/openapi-tools/verify.sh +51 -0
- package/overlays/otel-collector/overlay.yml.example +26 -0
- package/overlays/postgres/overlay.yml +6 -1
- package/overlays/prometheus/overlay.yml +6 -1
- package/overlays/python/README.md +51 -35
- package/overlays/python/devcontainer.patch.json +7 -4
- package/overlays/python/setup.sh +50 -23
- package/overlays/python/verify.sh +29 -1
- package/overlays/rabbitmq/overlay.yml +12 -2
- package/overlays/redis/overlay.yml +6 -1
- package/overlays/tilt/README.md +259 -0
- package/overlays/tilt/devcontainer.patch.json +17 -0
- package/overlays/tilt/overlay.yml +19 -0
- package/overlays/tilt/setup.sh +25 -0
- package/overlays/tilt/verify.sh +24 -0
- package/package.json +8 -6
- package/tool/README.md +12 -16
- package/tool/schema/overlay-manifest.schema.json +64 -4
- package/tool/schema/superposition-manifest.schema.json +104 -0
- package/overlays/presets/web-api.yml +0 -109
- /package/overlays/{presets → .presets}/docs-site.yml +0 -0
- /package/overlays/{presets → .presets}/fullstack.yml +0 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merge utilities for container-superposition
|
|
3
|
+
*
|
|
4
|
+
* This module implements the formal merge strategy specification for combining
|
|
5
|
+
* devcontainer configurations, docker-compose files, and environment variables.
|
|
6
|
+
*
|
|
7
|
+
* See docs/merge-strategy.md for the complete specification.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Deep merge two objects with special handling for arrays and specific fields.
|
|
11
|
+
*
|
|
12
|
+
* Merge strategy:
|
|
13
|
+
* - Objects: Recursively merged
|
|
14
|
+
* - Arrays: Concatenated and deduplicated (union strategy)
|
|
15
|
+
* - Primitives: Source overwrites target (last writer wins)
|
|
16
|
+
* - Special cases: remoteEnv (PATH handling), features, etc.
|
|
17
|
+
*
|
|
18
|
+
* @param target - Base object to merge into
|
|
19
|
+
* @param source - Object to merge from (takes precedence)
|
|
20
|
+
* @returns Merged object
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* const base = { features: { "node:1": { version: "lts" } } };
|
|
24
|
+
* const overlay = { features: { "node:1": { nodeGypDependencies: true } } };
|
|
25
|
+
* const result = deepMerge(base, overlay);
|
|
26
|
+
* // Result: { features: { "node:1": { version: "lts", nodeGypDependencies: true } } }
|
|
27
|
+
*/
|
|
28
|
+
export declare function deepMerge(target: any, source: any): any;
|
|
29
|
+
/**
|
|
30
|
+
* Merge remoteEnv objects with intelligent PATH handling.
|
|
31
|
+
*
|
|
32
|
+
* For PATH variables:
|
|
33
|
+
* 1. Split on colons (preserving ${...} references)
|
|
34
|
+
* 2. Remove ${containerEnv:PATH} placeholders
|
|
35
|
+
* 3. Concatenate and deduplicate
|
|
36
|
+
* 4. Append ${containerEnv:PATH} at end
|
|
37
|
+
*
|
|
38
|
+
* For other variables:
|
|
39
|
+
* - Source overwrites target (last writer wins)
|
|
40
|
+
*
|
|
41
|
+
* @param target - Base environment variables
|
|
42
|
+
* @param source - Overlay environment variables
|
|
43
|
+
* @returns Merged environment variables
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* mergeRemoteEnv(
|
|
47
|
+
* { PATH: "/usr/local/bin:${containerEnv:PATH}", NODE_ENV: "development" },
|
|
48
|
+
* { PATH: "${containerEnv:HOME}/.local/bin:${containerEnv:PATH}", NODE_ENV: "production" }
|
|
49
|
+
* )
|
|
50
|
+
* // Returns:
|
|
51
|
+
* // {
|
|
52
|
+
* // PATH: "/usr/local/bin:${containerEnv:HOME}/.local/bin:${containerEnv:PATH}",
|
|
53
|
+
* // NODE_ENV: "production"
|
|
54
|
+
* // }
|
|
55
|
+
*/
|
|
56
|
+
export declare function mergeRemoteEnv(target: Record<string, string>, source: Record<string, string>): Record<string, string>;
|
|
57
|
+
/**
|
|
58
|
+
* Merge space-separated package lists with deduplication.
|
|
59
|
+
*
|
|
60
|
+
* Used for apt, apk, and other package manager lists.
|
|
61
|
+
*
|
|
62
|
+
* @param existing - Current package list (space-separated)
|
|
63
|
+
* @param additional - Additional packages to merge (space-separated)
|
|
64
|
+
* @returns Merged package list (space-separated, deduplicated)
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* mergePackages("curl wget", "wget jq")
|
|
68
|
+
* // Returns: "curl wget jq"
|
|
69
|
+
*/
|
|
70
|
+
export declare function mergePackages(existing: string, additional: string): string;
|
|
71
|
+
/**
|
|
72
|
+
* Filter depends_on to only include services that exist in the final composition.
|
|
73
|
+
*
|
|
74
|
+
* Supports both Docker Compose syntaxes:
|
|
75
|
+
* - Array form: depends_on: [serviceA, serviceB]
|
|
76
|
+
* - Object form: depends_on: { serviceA: { condition: ... } }
|
|
77
|
+
*
|
|
78
|
+
* @param dependsOn - Original depends_on configuration
|
|
79
|
+
* @param existingServices - Set of service names that exist
|
|
80
|
+
* @returns Filtered depends_on (undefined if empty after filtering)
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* filterDependsOn(
|
|
84
|
+
* ["postgres", "redis", "rabbitmq"],
|
|
85
|
+
* new Set(["postgres", "redis"])
|
|
86
|
+
* )
|
|
87
|
+
* // Returns: ["postgres", "redis"]
|
|
88
|
+
*/
|
|
89
|
+
export declare function filterDependsOn(dependsOn: unknown, existingServices: Set<string>): unknown;
|
|
90
|
+
/**
|
|
91
|
+
* Apply port offset to port string or number.
|
|
92
|
+
*
|
|
93
|
+
* @param port - Port value (string or number)
|
|
94
|
+
* @param offset - Offset to apply
|
|
95
|
+
* @returns Port with offset applied
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* applyPortOffset("5432:5432", 100)
|
|
99
|
+
* // Returns: "5532:5432" (only host port offset)
|
|
100
|
+
*
|
|
101
|
+
* applyPortOffset(3000, 100)
|
|
102
|
+
* // Returns: 3100
|
|
103
|
+
*/
|
|
104
|
+
export declare function applyPortOffset(port: string | number, offset: number): string | number;
|
|
105
|
+
/**
|
|
106
|
+
* Apply port offset to environment variable content.
|
|
107
|
+
*
|
|
108
|
+
* Finds variables matching *PORT*=number pattern and applies offset.
|
|
109
|
+
*
|
|
110
|
+
* @param envContent - Environment file content
|
|
111
|
+
* @param offset - Port offset to apply
|
|
112
|
+
* @returns Modified environment content
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* applyPortOffsetToEnv("POSTGRES_PORT=5432\nAPP_NAME=myapp", 100)
|
|
116
|
+
* // Returns: "POSTGRES_PORT=5532\nAPP_NAME=myapp"
|
|
117
|
+
*/
|
|
118
|
+
export declare function applyPortOffsetToEnv(envContent: string, offset: number): string;
|
|
119
|
+
/**
|
|
120
|
+
* Merge strategy metadata for documentation and testing.
|
|
121
|
+
*/
|
|
122
|
+
export declare const MERGE_STRATEGY: {
|
|
123
|
+
readonly version: "1.0.0";
|
|
124
|
+
readonly description: "Formal merge strategy specification for container-superposition";
|
|
125
|
+
readonly rules: {
|
|
126
|
+
readonly objects: "deep-merge";
|
|
127
|
+
readonly arrays: "union-deduplicate";
|
|
128
|
+
readonly primitives: "last-writer-wins";
|
|
129
|
+
readonly remoteEnv: "intelligent-PATH-merging";
|
|
130
|
+
readonly packages: "space-separated-union";
|
|
131
|
+
readonly dependsOn: "filter-to-existing-services";
|
|
132
|
+
};
|
|
133
|
+
};
|
|
134
|
+
//# sourceMappingURL=merge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../../../tool/utils/merge.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,GAAG,GAAG,CA6BvD;AAiDD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,cAAc,CAC1B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC9B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAyBxB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAS1E;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,OAAO,EAAE,gBAAgB,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,OAAO,CAgB1F;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAuBtF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAe/E;AAED;;GAEG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;CAWjB,CAAC"}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merge utilities for container-superposition
|
|
3
|
+
*
|
|
4
|
+
* This module implements the formal merge strategy specification for combining
|
|
5
|
+
* devcontainer configurations, docker-compose files, and environment variables.
|
|
6
|
+
*
|
|
7
|
+
* See docs/merge-strategy.md for the complete specification.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Deep merge two objects with special handling for arrays and specific fields.
|
|
11
|
+
*
|
|
12
|
+
* Merge strategy:
|
|
13
|
+
* - Objects: Recursively merged
|
|
14
|
+
* - Arrays: Concatenated and deduplicated (union strategy)
|
|
15
|
+
* - Primitives: Source overwrites target (last writer wins)
|
|
16
|
+
* - Special cases: remoteEnv (PATH handling), features, etc.
|
|
17
|
+
*
|
|
18
|
+
* @param target - Base object to merge into
|
|
19
|
+
* @param source - Object to merge from (takes precedence)
|
|
20
|
+
* @returns Merged object
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* const base = { features: { "node:1": { version: "lts" } } };
|
|
24
|
+
* const overlay = { features: { "node:1": { nodeGypDependencies: true } } };
|
|
25
|
+
* const result = deepMerge(base, overlay);
|
|
26
|
+
* // Result: { features: { "node:1": { version: "lts", nodeGypDependencies: true } } }
|
|
27
|
+
*/
|
|
28
|
+
export function deepMerge(target, source) {
|
|
29
|
+
const output = { ...target };
|
|
30
|
+
for (const key in source) {
|
|
31
|
+
if (source[key] instanceof Object && key in target) {
|
|
32
|
+
if (Array.isArray(source[key])) {
|
|
33
|
+
// For arrays, concatenate and deduplicate
|
|
34
|
+
// Empty source arrays do not clear target arrays
|
|
35
|
+
if (source[key].length === 0) {
|
|
36
|
+
output[key] = target[key];
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
output[key] = Array.isArray(target[key])
|
|
40
|
+
? [...new Set([...target[key], ...source[key]])]
|
|
41
|
+
: source[key];
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
else if (key === 'remoteEnv') {
|
|
45
|
+
// Special handling for remoteEnv to merge PATH variables intelligently
|
|
46
|
+
output[key] = mergeRemoteEnv(target[key], source[key]);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
// Recursively merge objects
|
|
50
|
+
output[key] = deepMerge(target[key], source[key]);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
// Source value overwrites target (last writer wins)
|
|
55
|
+
output[key] = source[key];
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return output;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Split PATH string on colons, preserving ${...} variable references.
|
|
62
|
+
*
|
|
63
|
+
* Handles environment variable references like ${containerEnv:HOME} without
|
|
64
|
+
* splitting them on the colon inside the braces.
|
|
65
|
+
*
|
|
66
|
+
* @param pathString - PATH value with colon-separated components
|
|
67
|
+
* @returns Array of path components
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* splitPath("${containerEnv:HOME}/bin:${containerEnv:PATH}")
|
|
71
|
+
* // Returns: ["${containerEnv:HOME}/bin", "${containerEnv:PATH}"]
|
|
72
|
+
*/
|
|
73
|
+
function splitPath(pathString) {
|
|
74
|
+
const paths = [];
|
|
75
|
+
let current = '';
|
|
76
|
+
let braceDepth = 0;
|
|
77
|
+
for (let i = 0; i < pathString.length; i++) {
|
|
78
|
+
const char = pathString[i];
|
|
79
|
+
const nextChar = pathString[i + 1];
|
|
80
|
+
if (char === '$' && nextChar === '{') {
|
|
81
|
+
current += char;
|
|
82
|
+
braceDepth++;
|
|
83
|
+
}
|
|
84
|
+
else if (char === '}' && braceDepth > 0) {
|
|
85
|
+
current += char;
|
|
86
|
+
braceDepth--;
|
|
87
|
+
}
|
|
88
|
+
else if (char === ':' && braceDepth === 0) {
|
|
89
|
+
// Split here - we're not inside ${...}
|
|
90
|
+
if (current) {
|
|
91
|
+
paths.push(current);
|
|
92
|
+
}
|
|
93
|
+
current = '';
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
current += char;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// Add the last component
|
|
100
|
+
if (current) {
|
|
101
|
+
paths.push(current);
|
|
102
|
+
}
|
|
103
|
+
return paths;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Merge remoteEnv objects with intelligent PATH handling.
|
|
107
|
+
*
|
|
108
|
+
* For PATH variables:
|
|
109
|
+
* 1. Split on colons (preserving ${...} references)
|
|
110
|
+
* 2. Remove ${containerEnv:PATH} placeholders
|
|
111
|
+
* 3. Concatenate and deduplicate
|
|
112
|
+
* 4. Append ${containerEnv:PATH} at end
|
|
113
|
+
*
|
|
114
|
+
* For other variables:
|
|
115
|
+
* - Source overwrites target (last writer wins)
|
|
116
|
+
*
|
|
117
|
+
* @param target - Base environment variables
|
|
118
|
+
* @param source - Overlay environment variables
|
|
119
|
+
* @returns Merged environment variables
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* mergeRemoteEnv(
|
|
123
|
+
* { PATH: "/usr/local/bin:${containerEnv:PATH}", NODE_ENV: "development" },
|
|
124
|
+
* { PATH: "${containerEnv:HOME}/.local/bin:${containerEnv:PATH}", NODE_ENV: "production" }
|
|
125
|
+
* )
|
|
126
|
+
* // Returns:
|
|
127
|
+
* // {
|
|
128
|
+
* // PATH: "/usr/local/bin:${containerEnv:HOME}/.local/bin:${containerEnv:PATH}",
|
|
129
|
+
* // NODE_ENV: "production"
|
|
130
|
+
* // }
|
|
131
|
+
*/
|
|
132
|
+
export function mergeRemoteEnv(target, source) {
|
|
133
|
+
const output = { ...target };
|
|
134
|
+
for (const key in source) {
|
|
135
|
+
if (key === 'PATH' && target[key]) {
|
|
136
|
+
// Collect PATH components from both target and source using smart split
|
|
137
|
+
const targetPaths = splitPath(target[key]).filter((p) => p && p !== '${containerEnv:PATH}');
|
|
138
|
+
const sourcePaths = splitPath(source[key]).filter((p) => p && p !== '${containerEnv:PATH}');
|
|
139
|
+
// Combine and deduplicate paths, preserving order
|
|
140
|
+
const allPaths = [...new Set([...targetPaths, ...sourcePaths])];
|
|
141
|
+
// Rebuild PATH with original ${containerEnv:PATH} at the end
|
|
142
|
+
output[key] = [...allPaths, '${containerEnv:PATH}'].join(':');
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
// For non-PATH variables, source overwrites target
|
|
146
|
+
output[key] = source[key];
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return output;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Merge space-separated package lists with deduplication.
|
|
153
|
+
*
|
|
154
|
+
* Used for apt, apk, and other package manager lists.
|
|
155
|
+
*
|
|
156
|
+
* @param existing - Current package list (space-separated)
|
|
157
|
+
* @param additional - Additional packages to merge (space-separated)
|
|
158
|
+
* @returns Merged package list (space-separated, deduplicated)
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* mergePackages("curl wget", "wget jq")
|
|
162
|
+
* // Returns: "curl wget jq"
|
|
163
|
+
*/
|
|
164
|
+
export function mergePackages(existing, additional) {
|
|
165
|
+
// Filter out empty tokens from split to avoid leading/trailing spaces
|
|
166
|
+
const existingPackages = existing.split(' ').filter((p) => p);
|
|
167
|
+
const newPackages = additional.split(' ').filter((p) => p);
|
|
168
|
+
// Merge and deduplicate
|
|
169
|
+
const merged = [...new Set([...existingPackages, ...newPackages])];
|
|
170
|
+
return merged.join(' ');
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Filter depends_on to only include services that exist in the final composition.
|
|
174
|
+
*
|
|
175
|
+
* Supports both Docker Compose syntaxes:
|
|
176
|
+
* - Array form: depends_on: [serviceA, serviceB]
|
|
177
|
+
* - Object form: depends_on: { serviceA: { condition: ... } }
|
|
178
|
+
*
|
|
179
|
+
* @param dependsOn - Original depends_on configuration
|
|
180
|
+
* @param existingServices - Set of service names that exist
|
|
181
|
+
* @returns Filtered depends_on (undefined if empty after filtering)
|
|
182
|
+
*
|
|
183
|
+
* @example
|
|
184
|
+
* filterDependsOn(
|
|
185
|
+
* ["postgres", "redis", "rabbitmq"],
|
|
186
|
+
* new Set(["postgres", "redis"])
|
|
187
|
+
* )
|
|
188
|
+
* // Returns: ["postgres", "redis"]
|
|
189
|
+
*/
|
|
190
|
+
export function filterDependsOn(dependsOn, existingServices) {
|
|
191
|
+
if (Array.isArray(dependsOn)) {
|
|
192
|
+
const filtered = dependsOn.filter((dep) => typeof dep === 'string' && existingServices.has(dep));
|
|
193
|
+
return filtered.length > 0 ? filtered : undefined;
|
|
194
|
+
}
|
|
195
|
+
if (dependsOn && typeof dependsOn === 'object') {
|
|
196
|
+
const filtered = Object.fromEntries(Object.entries(dependsOn).filter(([dep]) => existingServices.has(dep)));
|
|
197
|
+
return Object.keys(filtered).length > 0 ? filtered : undefined;
|
|
198
|
+
}
|
|
199
|
+
return dependsOn;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Apply port offset to port string or number.
|
|
203
|
+
*
|
|
204
|
+
* @param port - Port value (string or number)
|
|
205
|
+
* @param offset - Offset to apply
|
|
206
|
+
* @returns Port with offset applied
|
|
207
|
+
*
|
|
208
|
+
* @example
|
|
209
|
+
* applyPortOffset("5432:5432", 100)
|
|
210
|
+
* // Returns: "5532:5432" (only host port offset)
|
|
211
|
+
*
|
|
212
|
+
* applyPortOffset(3000, 100)
|
|
213
|
+
* // Returns: 3100
|
|
214
|
+
*/
|
|
215
|
+
export function applyPortOffset(port, offset) {
|
|
216
|
+
if (typeof port === 'number') {
|
|
217
|
+
return port + offset;
|
|
218
|
+
}
|
|
219
|
+
// Handle string port mappings like "5432:5432" or "127.0.0.1:5432:5432"
|
|
220
|
+
const parts = port.split(':');
|
|
221
|
+
if (parts.length >= 2) {
|
|
222
|
+
// Format: "host:container" or "ip:host:container"
|
|
223
|
+
// Offset the host port (last or second-to-last number)
|
|
224
|
+
const isThreePart = parts.length === 3;
|
|
225
|
+
const hostPortIndex = isThreePart ? 1 : 0;
|
|
226
|
+
const hostPort = parseInt(parts[hostPortIndex], 10);
|
|
227
|
+
if (!isNaN(hostPort)) {
|
|
228
|
+
parts[hostPortIndex] = String(hostPort + offset);
|
|
229
|
+
return parts.join(':');
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
// If we can't parse it, return as-is
|
|
233
|
+
return port;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Apply port offset to environment variable content.
|
|
237
|
+
*
|
|
238
|
+
* Finds variables matching *PORT*=number pattern and applies offset.
|
|
239
|
+
*
|
|
240
|
+
* @param envContent - Environment file content
|
|
241
|
+
* @param offset - Port offset to apply
|
|
242
|
+
* @returns Modified environment content
|
|
243
|
+
*
|
|
244
|
+
* @example
|
|
245
|
+
* applyPortOffsetToEnv("POSTGRES_PORT=5432\nAPP_NAME=myapp", 100)
|
|
246
|
+
* // Returns: "POSTGRES_PORT=5532\nAPP_NAME=myapp"
|
|
247
|
+
*/
|
|
248
|
+
export function applyPortOffsetToEnv(envContent, offset) {
|
|
249
|
+
const lines = envContent.split('\n');
|
|
250
|
+
const portVarPattern = /^([A-Z_]*PORT[A-Z_]*)=(\d+)$/;
|
|
251
|
+
const modifiedLines = lines.map((line) => {
|
|
252
|
+
const match = line.match(portVarPattern);
|
|
253
|
+
if (match) {
|
|
254
|
+
const [, varName, portValue] = match;
|
|
255
|
+
const newPort = parseInt(portValue, 10) + offset;
|
|
256
|
+
return `${varName}=${newPort}`;
|
|
257
|
+
}
|
|
258
|
+
return line;
|
|
259
|
+
});
|
|
260
|
+
return modifiedLines.join('\n');
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Merge strategy metadata for documentation and testing.
|
|
264
|
+
*/
|
|
265
|
+
export const MERGE_STRATEGY = {
|
|
266
|
+
version: '1.0.0',
|
|
267
|
+
description: 'Formal merge strategy specification for container-superposition',
|
|
268
|
+
rules: {
|
|
269
|
+
objects: 'deep-merge',
|
|
270
|
+
arrays: 'union-deduplicate',
|
|
271
|
+
primitives: 'last-writer-wins',
|
|
272
|
+
remoteEnv: 'intelligent-PATH-merging',
|
|
273
|
+
packages: 'space-separated-union',
|
|
274
|
+
dependsOn: 'filter-to-existing-services',
|
|
275
|
+
},
|
|
276
|
+
};
|
|
277
|
+
//# sourceMappingURL=merge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge.js","sourceRoot":"","sources":["../../../tool/utils/merge.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Port utilities for normalizing and documenting ports
|
|
3
|
+
*/
|
|
4
|
+
import type { PortMetadata, NormalizedPort, PortsDocumentation, OverlayMetadata } from '../schema/types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Normalize a port entry (number or PortMetadata) to NormalizedPort format
|
|
7
|
+
*/
|
|
8
|
+
export declare function normalizePort(port: number | PortMetadata, offset: number, overlayId?: string): NormalizedPort;
|
|
9
|
+
/**
|
|
10
|
+
* Generate connection string from template
|
|
11
|
+
*/
|
|
12
|
+
export declare function generateConnectionString(template: string, port: NormalizedPort, envVars?: Record<string, string>): string;
|
|
13
|
+
/**
|
|
14
|
+
* Get default connection string template for common services
|
|
15
|
+
*/
|
|
16
|
+
export declare function getDefaultConnectionStringTemplate(service: string, protocol?: string): string | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* Generate URL for HTTP/HTTPS services
|
|
19
|
+
*/
|
|
20
|
+
export declare function generateUrl(port: NormalizedPort): string | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* Generate ports documentation from overlays
|
|
23
|
+
*/
|
|
24
|
+
export declare function generatePortsDocumentation(overlays: OverlayMetadata[], portOffset: number, envVars?: Record<string, string>): PortsDocumentation;
|
|
25
|
+
/**
|
|
26
|
+
* Extract all unique ports from overlays (for offset calculation)
|
|
27
|
+
*/
|
|
28
|
+
export declare function extractPorts(overlays: OverlayMetadata[]): number[];
|
|
29
|
+
//# sourceMappingURL=port-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"port-utils.d.ts","sourceRoot":"","sources":["../../../tool/utils/port-utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACR,YAAY,EACZ,cAAc,EACd,kBAAkB,EAClB,eAAe,EAClB,MAAM,oBAAoB,CAAC;AAE5B;;GAEG;AACH,wBAAgB,aAAa,CACzB,IAAI,EAAE,MAAM,GAAG,YAAY,EAC3B,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,MAAM,GACnB,cAAc,CAgBhB;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CACpC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,cAAc,EACpB,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GACrC,MAAM,CAoBR;AAED;;GAEG;AACH,wBAAgB,kCAAkC,CAC9C,OAAO,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,MAAM,GAClB,MAAM,GAAG,SAAS,CAiBpB;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,GAAG,SAAS,CAMpE;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CACtC,QAAQ,EAAE,eAAe,EAAE,EAC3B,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GACrC,kBAAkB,CAgDpB;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,eAAe,EAAE,GAAG,MAAM,EAAE,CAelE"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Port utilities for normalizing and documenting ports
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Normalize a port entry (number or PortMetadata) to NormalizedPort format
|
|
6
|
+
*/
|
|
7
|
+
export function normalizePort(port, offset, overlayId) {
|
|
8
|
+
if (typeof port === 'number') {
|
|
9
|
+
// Legacy format - just a number
|
|
10
|
+
return {
|
|
11
|
+
port,
|
|
12
|
+
actualPort: port + offset,
|
|
13
|
+
service: overlayId,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
// Rich format - already an object
|
|
17
|
+
return {
|
|
18
|
+
...port,
|
|
19
|
+
actualPort: port.port + offset,
|
|
20
|
+
service: port.service || overlayId,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Generate connection string from template
|
|
25
|
+
*/
|
|
26
|
+
export function generateConnectionString(template, port, envVars = {}) {
|
|
27
|
+
let result = template;
|
|
28
|
+
// Replace port placeholder
|
|
29
|
+
result = result.replace(/\{port\}/g, String(port.actualPort));
|
|
30
|
+
// Replace other placeholders from environment variables or port metadata
|
|
31
|
+
const replacements = {
|
|
32
|
+
host: 'localhost',
|
|
33
|
+
service: port.service || 'unknown',
|
|
34
|
+
path: port.path || '',
|
|
35
|
+
...envVars,
|
|
36
|
+
};
|
|
37
|
+
for (const [key, value] of Object.entries(replacements)) {
|
|
38
|
+
const regex = new RegExp(`\\{${key}\\}`, 'g');
|
|
39
|
+
result = result.replace(regex, value);
|
|
40
|
+
}
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Get default connection string template for common services
|
|
45
|
+
*/
|
|
46
|
+
export function getDefaultConnectionStringTemplate(service, protocol) {
|
|
47
|
+
const templates = {
|
|
48
|
+
postgres: 'postgresql://{user}:{password}@{host}:{port}/{database}',
|
|
49
|
+
postgresql: 'postgresql://{user}:{password}@{host}:{port}/{database}',
|
|
50
|
+
mysql: 'mysql://{user}:{password}@{host}:{port}/{database}',
|
|
51
|
+
mongodb: 'mongodb://{user}:{password}@{host}:{port}/{database}',
|
|
52
|
+
redis: 'redis://{host}:{port}',
|
|
53
|
+
rabbitmq: 'amqp://{user}:{password}@{host}:{port}',
|
|
54
|
+
nats: 'nats://{host}:{port}',
|
|
55
|
+
};
|
|
56
|
+
// For HTTP/HTTPS services, use URL format
|
|
57
|
+
if (protocol === 'http' || protocol === 'https') {
|
|
58
|
+
return `${protocol}://{host}:{port}{path}`;
|
|
59
|
+
}
|
|
60
|
+
return templates[service.toLowerCase()];
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Generate URL for HTTP/HTTPS services
|
|
64
|
+
*/
|
|
65
|
+
export function generateUrl(port) {
|
|
66
|
+
if (port.protocol === 'http' || port.protocol === 'https') {
|
|
67
|
+
const path = port.path || '';
|
|
68
|
+
return `${port.protocol}://localhost:${port.actualPort}${path}`;
|
|
69
|
+
}
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Generate ports documentation from overlays
|
|
74
|
+
*/
|
|
75
|
+
export function generatePortsDocumentation(overlays, portOffset, envVars = {}) {
|
|
76
|
+
const allPorts = [];
|
|
77
|
+
const connectionStrings = {};
|
|
78
|
+
for (const overlay of overlays) {
|
|
79
|
+
if (!overlay.ports || overlay.ports.length === 0) {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
for (const portEntry of overlay.ports) {
|
|
83
|
+
const normalizedPort = normalizePort(portEntry, portOffset, overlay.id);
|
|
84
|
+
allPorts.push(normalizedPort);
|
|
85
|
+
// Generate connection string if template is provided or can be inferred
|
|
86
|
+
const template = typeof portEntry === 'object' && portEntry.connectionStringTemplate
|
|
87
|
+
? portEntry.connectionStringTemplate
|
|
88
|
+
: getDefaultConnectionStringTemplate(normalizedPort.service || '', normalizedPort.protocol);
|
|
89
|
+
if (template) {
|
|
90
|
+
const connStr = generateConnectionString(template, normalizedPort, envVars);
|
|
91
|
+
// Use unique key for multi-port services by appending port number if service already exists
|
|
92
|
+
let key = normalizedPort.service || `port-${normalizedPort.port}`;
|
|
93
|
+
// If key already exists, make it unique by adding port number
|
|
94
|
+
if (connectionStrings[key]) {
|
|
95
|
+
key = `${key}-${normalizedPort.port}`;
|
|
96
|
+
}
|
|
97
|
+
connectionStrings[key] = connStr;
|
|
98
|
+
}
|
|
99
|
+
// Generate URL for HTTP/HTTPS services
|
|
100
|
+
const url = generateUrl(normalizedPort);
|
|
101
|
+
if (url && normalizedPort.service) {
|
|
102
|
+
connectionStrings[`${normalizedPort.service}-url`] = url;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
portOffset,
|
|
108
|
+
ports: allPorts,
|
|
109
|
+
connectionStrings,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Extract all unique ports from overlays (for offset calculation)
|
|
114
|
+
*/
|
|
115
|
+
export function extractPorts(overlays) {
|
|
116
|
+
const ports = new Set();
|
|
117
|
+
for (const overlay of overlays) {
|
|
118
|
+
if (!overlay.ports) {
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
for (const portEntry of overlay.ports) {
|
|
122
|
+
const port = typeof portEntry === 'number' ? portEntry : portEntry.port;
|
|
123
|
+
ports.add(port);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return Array.from(ports).sort((a, b) => a - b);
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=port-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"port-utils.js","sourceRoot":"","sources":["../../../tool/utils/port-utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Export utilities for generating service reference documentation
|
|
3
|
+
*/
|
|
4
|
+
import type { OverlayMetadata } from '../schema/types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Generate the services.md reference document
|
|
7
|
+
*/
|
|
8
|
+
export declare function generateServicesMarkdown(overlays: OverlayMetadata[], portOffset: number, envVars: Record<string, string>, generatedAt?: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* Generate env.local.example - a commented-out local override template
|
|
11
|
+
* derived from each overlay's .env.example
|
|
12
|
+
*/
|
|
13
|
+
export declare function generateEnvLocalExample(overlays: OverlayMetadata[], overlaysDir: string, portOffset: number): string;
|
|
14
|
+
//# sourceMappingURL=services-export.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"services-export.d.ts","sourceRoot":"","sources":["../../../tool/utils/services-export.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAkB,MAAM,oBAAoB,CAAC;AAwM1E;;GAEG;AACH,wBAAgB,wBAAwB,CACpC,QAAQ,EAAE,eAAe,EAAE,EAC3B,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,WAAW,GAAE,MAAiC,GAC/C,MAAM,CAoQR;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACnC,QAAQ,EAAE,eAAe,EAAE,EAC3B,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,GACnB,MAAM,CA0DR"}
|