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.
Files changed (178) hide show
  1. package/README.md +569 -8
  2. package/dist/scripts/init.js +436 -254
  3. package/dist/scripts/init.js.map +1 -1
  4. package/dist/tool/commands/doctor.d.ts +15 -0
  5. package/dist/tool/commands/doctor.d.ts.map +1 -0
  6. package/dist/tool/commands/doctor.js +862 -0
  7. package/dist/tool/commands/doctor.js.map +1 -0
  8. package/dist/tool/commands/explain.d.ts +13 -0
  9. package/dist/tool/commands/explain.d.ts.map +1 -0
  10. package/dist/tool/commands/explain.js +299 -0
  11. package/dist/tool/commands/explain.js.map +1 -0
  12. package/dist/tool/commands/list.d.ts +16 -0
  13. package/dist/tool/commands/list.d.ts.map +1 -0
  14. package/dist/tool/commands/list.js +121 -0
  15. package/dist/tool/commands/list.js.map +1 -0
  16. package/dist/tool/commands/plan.d.ts +67 -0
  17. package/dist/tool/commands/plan.d.ts.map +1 -0
  18. package/dist/tool/commands/plan.js +851 -0
  19. package/dist/tool/commands/plan.js.map +1 -0
  20. package/dist/tool/questionnaire/composer.d.ts +16 -2
  21. package/dist/tool/questionnaire/composer.d.ts.map +1 -1
  22. package/dist/tool/questionnaire/composer.js +411 -200
  23. package/dist/tool/questionnaire/composer.js.map +1 -1
  24. package/dist/tool/readme/markdown-parser.d.ts.map +1 -1
  25. package/dist/tool/readme/markdown-parser.js.map +1 -1
  26. package/dist/tool/readme/readme-generator.d.ts.map +1 -1
  27. package/dist/tool/readme/readme-generator.js +11 -6
  28. package/dist/tool/readme/readme-generator.js.map +1 -1
  29. package/dist/tool/schema/deployment-targets.d.ts +77 -0
  30. package/dist/tool/schema/deployment-targets.d.ts.map +1 -0
  31. package/dist/tool/schema/deployment-targets.js +91 -0
  32. package/dist/tool/schema/deployment-targets.js.map +1 -0
  33. package/dist/tool/schema/manifest-migrations.d.ts +51 -0
  34. package/dist/tool/schema/manifest-migrations.d.ts.map +1 -0
  35. package/dist/tool/schema/manifest-migrations.js +159 -0
  36. package/dist/tool/schema/manifest-migrations.js.map +1 -0
  37. package/dist/tool/schema/overlay-loader.d.ts +1 -1
  38. package/dist/tool/schema/overlay-loader.d.ts.map +1 -1
  39. package/dist/tool/schema/overlay-loader.js +42 -14
  40. package/dist/tool/schema/overlay-loader.js.map +1 -1
  41. package/dist/tool/schema/types.d.ts +62 -2
  42. package/dist/tool/schema/types.d.ts.map +1 -1
  43. package/dist/tool/utils/gitignore.d.ts +15 -0
  44. package/dist/tool/utils/gitignore.d.ts.map +1 -0
  45. package/dist/tool/utils/gitignore.js +41 -0
  46. package/dist/tool/utils/gitignore.js.map +1 -0
  47. package/dist/tool/utils/merge.d.ts +134 -0
  48. package/dist/tool/utils/merge.d.ts.map +1 -0
  49. package/dist/tool/utils/merge.js +277 -0
  50. package/dist/tool/utils/merge.js.map +1 -0
  51. package/dist/tool/utils/port-utils.d.ts +29 -0
  52. package/dist/tool/utils/port-utils.d.ts.map +1 -0
  53. package/dist/tool/utils/port-utils.js +128 -0
  54. package/dist/tool/utils/port-utils.js.map +1 -0
  55. package/dist/tool/utils/services-export.d.ts +14 -0
  56. package/dist/tool/utils/services-export.d.ts.map +1 -0
  57. package/dist/tool/utils/services-export.js +478 -0
  58. package/dist/tool/utils/services-export.js.map +1 -0
  59. package/dist/tool/utils/summary.d.ts +69 -0
  60. package/dist/tool/utils/summary.d.ts.map +1 -0
  61. package/dist/tool/utils/summary.js +260 -0
  62. package/dist/tool/utils/summary.js.map +1 -0
  63. package/dist/tool/utils/version.d.ts +9 -0
  64. package/dist/tool/utils/version.d.ts.map +1 -0
  65. package/dist/tool/utils/version.js +32 -0
  66. package/dist/tool/utils/version.js.map +1 -0
  67. package/docs/architecture.md +25 -21
  68. package/docs/deployment-targets.md +150 -0
  69. package/docs/discovery-commands.md +442 -0
  70. package/docs/merge-strategy.md +700 -0
  71. package/docs/minimal-and-editor.md +265 -0
  72. package/docs/overlay-imports.md +209 -0
  73. package/docs/overlay-manifest-refactoring.md +2 -2
  74. package/docs/overlay-metadata-archive.md +1 -1
  75. package/docs/overlays.md +139 -28
  76. package/docs/presets-architecture.md +3 -3
  77. package/docs/presets.md +1 -1
  78. package/docs/publishing.md +36 -35
  79. package/docs/team-workflow.md +540 -0
  80. package/overlays/.presets/data-engineering.yml +392 -0
  81. package/overlays/.presets/event-sourced-service.yml +262 -0
  82. package/overlays/.presets/frontend.yml +287 -0
  83. package/overlays/.presets/k8s-operator-dev.yml +462 -0
  84. package/overlays/{presets → .presets}/microservice.yml +32 -6
  85. package/overlays/.presets/web-api.yml +129 -0
  86. package/overlays/.registry/README.md +1 -1
  87. package/overlays/.registry/deployment-targets.yml +54 -0
  88. package/overlays/.shared/README.md +43 -0
  89. package/overlays/.shared/compose/common-healthchecks.yml +38 -0
  90. package/overlays/.shared/otel/instrumentation.env +20 -0
  91. package/overlays/.shared/otel/otel-base-config.yaml +30 -0
  92. package/overlays/.shared/vscode/recommended-extensions.json +14 -0
  93. package/overlays/README.md +1 -1
  94. package/overlays/cloudflared/README.md +190 -0
  95. package/overlays/cloudflared/devcontainer.patch.json +3 -0
  96. package/overlays/cloudflared/overlay.yml +15 -0
  97. package/overlays/cloudflared/setup.sh +49 -0
  98. package/overlays/cloudflared/verify.sh +21 -0
  99. package/overlays/codex/overlay.yml +1 -0
  100. package/overlays/direnv/README.md +6 -4
  101. package/overlays/direnv/setup.sh +0 -12
  102. package/overlays/duckdb/README.md +274 -0
  103. package/overlays/duckdb/devcontainer.patch.json +10 -0
  104. package/overlays/duckdb/overlay.yml +17 -0
  105. package/overlays/duckdb/setup.sh +45 -0
  106. package/overlays/duckdb/verify.sh +32 -0
  107. package/overlays/git-helpers/overlay.yml +1 -0
  108. package/overlays/grafana/README.md +5 -5
  109. package/overlays/grafana/dashboard-provider.yml +1 -1
  110. package/overlays/grafana/docker-compose.yml +2 -2
  111. package/overlays/grafana/overlay.yml +6 -1
  112. package/overlays/grpc-tools/README.md +242 -0
  113. package/overlays/grpc-tools/devcontainer.patch.json +14 -0
  114. package/overlays/grpc-tools/overlay.yml +14 -0
  115. package/overlays/grpc-tools/setup.sh +57 -0
  116. package/overlays/grpc-tools/verify.sh +47 -0
  117. package/overlays/jaeger/overlay.yml +16 -3
  118. package/overlays/jupyter/.env.example +6 -0
  119. package/overlays/jupyter/README.md +210 -0
  120. package/overlays/jupyter/devcontainer.patch.json +14 -0
  121. package/overlays/jupyter/docker-compose.yml +23 -0
  122. package/overlays/jupyter/overlay.yml +18 -0
  123. package/overlays/jupyter/verify.sh +35 -0
  124. package/overlays/keycloak/.env.example +5 -0
  125. package/overlays/keycloak/README.md +238 -0
  126. package/overlays/keycloak/devcontainer.patch.json +17 -0
  127. package/overlays/keycloak/docker-compose.yml +32 -0
  128. package/overlays/keycloak/overlay.yml +23 -0
  129. package/overlays/keycloak/verify.sh +54 -0
  130. package/overlays/kind/README.md +221 -0
  131. package/overlays/kind/devcontainer.patch.json +10 -0
  132. package/overlays/kind/overlay.yml +18 -0
  133. package/overlays/kind/setup.sh +43 -0
  134. package/overlays/kind/verify.sh +40 -0
  135. package/overlays/localstack/.env.example +6 -0
  136. package/overlays/localstack/README.md +188 -0
  137. package/overlays/localstack/devcontainer.patch.json +21 -0
  138. package/overlays/localstack/docker-compose.yml +25 -0
  139. package/overlays/localstack/overlay.yml +18 -0
  140. package/overlays/localstack/verify.sh +47 -0
  141. package/overlays/loki/overlay.yml +6 -1
  142. package/overlays/mailpit/.env.example +4 -0
  143. package/overlays/mailpit/README.md +191 -0
  144. package/overlays/mailpit/devcontainer.patch.json +20 -0
  145. package/overlays/mailpit/docker-compose.yml +17 -0
  146. package/overlays/mailpit/overlay.yml +26 -0
  147. package/overlays/mailpit/verify.sh +52 -0
  148. package/overlays/modern-cli-tools/overlay.yml +1 -0
  149. package/overlays/mongodb/overlay.yml +12 -2
  150. package/overlays/mysql/overlay.yml +12 -2
  151. package/overlays/nats/overlay.yml +12 -2
  152. package/overlays/ngrok/overlay.yml +2 -1
  153. package/overlays/openapi-tools/README.md +243 -0
  154. package/overlays/openapi-tools/devcontainer.patch.json +10 -0
  155. package/overlays/openapi-tools/overlay.yml +16 -0
  156. package/overlays/openapi-tools/setup.sh +45 -0
  157. package/overlays/openapi-tools/verify.sh +51 -0
  158. package/overlays/otel-collector/overlay.yml.example +26 -0
  159. package/overlays/postgres/overlay.yml +6 -1
  160. package/overlays/prometheus/overlay.yml +6 -1
  161. package/overlays/python/README.md +51 -35
  162. package/overlays/python/devcontainer.patch.json +7 -4
  163. package/overlays/python/setup.sh +50 -23
  164. package/overlays/python/verify.sh +29 -1
  165. package/overlays/rabbitmq/overlay.yml +12 -2
  166. package/overlays/redis/overlay.yml +6 -1
  167. package/overlays/tilt/README.md +259 -0
  168. package/overlays/tilt/devcontainer.patch.json +17 -0
  169. package/overlays/tilt/overlay.yml +19 -0
  170. package/overlays/tilt/setup.sh +25 -0
  171. package/overlays/tilt/verify.sh +24 -0
  172. package/package.json +8 -6
  173. package/tool/README.md +12 -16
  174. package/tool/schema/overlay-manifest.schema.json +64 -4
  175. package/tool/schema/superposition-manifest.schema.json +104 -0
  176. package/overlays/presets/web-api.yml +0 -109
  177. /package/overlays/{presets → .presets}/docs-site.yml +0 -0
  178. /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"}