ctxinit 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (221) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +484 -0
  3. package/bin/ctx.js +3 -0
  4. package/dist/analysis/index.d.ts +2 -0
  5. package/dist/analysis/index.d.ts.map +1 -0
  6. package/dist/analysis/index.js +18 -0
  7. package/dist/analysis/index.js.map +1 -0
  8. package/dist/analysis/static-analysis.d.ts +79 -0
  9. package/dist/analysis/static-analysis.d.ts.map +1 -0
  10. package/dist/analysis/static-analysis.js +279 -0
  11. package/dist/analysis/static-analysis.js.map +1 -0
  12. package/dist/bootstrap/index.d.ts +8 -0
  13. package/dist/bootstrap/index.d.ts.map +1 -0
  14. package/dist/bootstrap/index.js +13 -0
  15. package/dist/bootstrap/index.js.map +1 -0
  16. package/dist/bootstrap/orchestrator.d.ts +48 -0
  17. package/dist/bootstrap/orchestrator.d.ts.map +1 -0
  18. package/dist/bootstrap/orchestrator.js +363 -0
  19. package/dist/bootstrap/orchestrator.js.map +1 -0
  20. package/dist/bootstrap/validator.d.ts +25 -0
  21. package/dist/bootstrap/validator.d.ts.map +1 -0
  22. package/dist/bootstrap/validator.js +412 -0
  23. package/dist/bootstrap/validator.js.map +1 -0
  24. package/dist/build/atomic.d.ts +74 -0
  25. package/dist/build/atomic.d.ts.map +1 -0
  26. package/dist/build/atomic.js +235 -0
  27. package/dist/build/atomic.js.map +1 -0
  28. package/dist/build/index.d.ts +10 -0
  29. package/dist/build/index.d.ts.map +1 -0
  30. package/dist/build/index.js +26 -0
  31. package/dist/build/index.js.map +1 -0
  32. package/dist/build/lock.d.ts +102 -0
  33. package/dist/build/lock.d.ts.map +1 -0
  34. package/dist/build/lock.js +297 -0
  35. package/dist/build/lock.js.map +1 -0
  36. package/dist/build/manifest.d.ts +138 -0
  37. package/dist/build/manifest.d.ts.map +1 -0
  38. package/dist/build/manifest.js +269 -0
  39. package/dist/build/manifest.js.map +1 -0
  40. package/dist/build/orchestrator.d.ts +103 -0
  41. package/dist/build/orchestrator.d.ts.map +1 -0
  42. package/dist/build/orchestrator.js +524 -0
  43. package/dist/build/orchestrator.js.map +1 -0
  44. package/dist/cli/bootstrap.d.ts +77 -0
  45. package/dist/cli/bootstrap.d.ts.map +1 -0
  46. package/dist/cli/bootstrap.js +527 -0
  47. package/dist/cli/bootstrap.js.map +1 -0
  48. package/dist/cli/build.d.ts +32 -0
  49. package/dist/cli/build.d.ts.map +1 -0
  50. package/dist/cli/build.js +156 -0
  51. package/dist/cli/build.js.map +1 -0
  52. package/dist/cli/diff.d.ts +23 -0
  53. package/dist/cli/diff.d.ts.map +1 -0
  54. package/dist/cli/diff.js +226 -0
  55. package/dist/cli/diff.js.map +1 -0
  56. package/dist/cli/hooks.d.ts +29 -0
  57. package/dist/cli/hooks.d.ts.map +1 -0
  58. package/dist/cli/hooks.js +176 -0
  59. package/dist/cli/hooks.js.map +1 -0
  60. package/dist/cli/init.d.ts +53 -0
  61. package/dist/cli/init.d.ts.map +1 -0
  62. package/dist/cli/init.js +254 -0
  63. package/dist/cli/init.js.map +1 -0
  64. package/dist/cli/lint.d.ts +46 -0
  65. package/dist/cli/lint.d.ts.map +1 -0
  66. package/dist/cli/lint.js +210 -0
  67. package/dist/cli/lint.js.map +1 -0
  68. package/dist/cli/migrate.d.ts +28 -0
  69. package/dist/cli/migrate.d.ts.map +1 -0
  70. package/dist/cli/migrate.js +350 -0
  71. package/dist/cli/migrate.js.map +1 -0
  72. package/dist/cli/verify.d.ts +21 -0
  73. package/dist/cli/verify.d.ts.map +1 -0
  74. package/dist/cli/verify.js +209 -0
  75. package/dist/cli/verify.js.map +1 -0
  76. package/dist/cli.d.ts +8 -0
  77. package/dist/cli.d.ts.map +1 -0
  78. package/dist/cli.js +262 -0
  79. package/dist/cli.js.map +1 -0
  80. package/dist/compiler/agents-compiler.d.ts +24 -0
  81. package/dist/compiler/agents-compiler.d.ts.map +1 -0
  82. package/dist/compiler/agents-compiler.js +192 -0
  83. package/dist/compiler/agents-compiler.js.map +1 -0
  84. package/dist/compiler/base-compiler.d.ts +152 -0
  85. package/dist/compiler/base-compiler.d.ts.map +1 -0
  86. package/dist/compiler/base-compiler.js +180 -0
  87. package/dist/compiler/base-compiler.js.map +1 -0
  88. package/dist/compiler/claude-compiler.d.ts +24 -0
  89. package/dist/compiler/claude-compiler.d.ts.map +1 -0
  90. package/dist/compiler/claude-compiler.js +182 -0
  91. package/dist/compiler/claude-compiler.js.map +1 -0
  92. package/dist/compiler/cursor-compiler.d.ts +33 -0
  93. package/dist/compiler/cursor-compiler.d.ts.map +1 -0
  94. package/dist/compiler/cursor-compiler.js +136 -0
  95. package/dist/compiler/cursor-compiler.js.map +1 -0
  96. package/dist/compiler/index.d.ts +7 -0
  97. package/dist/compiler/index.d.ts.map +1 -0
  98. package/dist/compiler/index.js +24 -0
  99. package/dist/compiler/index.js.map +1 -0
  100. package/dist/compiler/rule-selector.d.ts +115 -0
  101. package/dist/compiler/rule-selector.d.ts.map +1 -0
  102. package/dist/compiler/rule-selector.js +273 -0
  103. package/dist/compiler/rule-selector.js.map +1 -0
  104. package/dist/compiler/token-estimator.d.ts +74 -0
  105. package/dist/compiler/token-estimator.d.ts.map +1 -0
  106. package/dist/compiler/token-estimator.js +191 -0
  107. package/dist/compiler/token-estimator.js.map +1 -0
  108. package/dist/config/index.d.ts +2 -0
  109. package/dist/config/index.d.ts.map +1 -0
  110. package/dist/config/index.js +18 -0
  111. package/dist/config/index.js.map +1 -0
  112. package/dist/config/loader.d.ts +48 -0
  113. package/dist/config/loader.d.ts.map +1 -0
  114. package/dist/config/loader.js +175 -0
  115. package/dist/config/loader.js.map +1 -0
  116. package/dist/git/gitignore.d.ts +59 -0
  117. package/dist/git/gitignore.d.ts.map +1 -0
  118. package/dist/git/gitignore.js +268 -0
  119. package/dist/git/gitignore.js.map +1 -0
  120. package/dist/git/hooks.d.ts +34 -0
  121. package/dist/git/hooks.d.ts.map +1 -0
  122. package/dist/git/hooks.js +129 -0
  123. package/dist/git/hooks.js.map +1 -0
  124. package/dist/git/husky.d.ts +52 -0
  125. package/dist/git/husky.d.ts.map +1 -0
  126. package/dist/git/husky.js +219 -0
  127. package/dist/git/husky.js.map +1 -0
  128. package/dist/git/index.d.ts +9 -0
  129. package/dist/git/index.d.ts.map +1 -0
  130. package/dist/git/index.js +15 -0
  131. package/dist/git/index.js.map +1 -0
  132. package/dist/index.d.ts +6 -0
  133. package/dist/index.d.ts.map +1 -0
  134. package/dist/index.js +23 -0
  135. package/dist/index.js.map +1 -0
  136. package/dist/llm/base-provider.d.ts +43 -0
  137. package/dist/llm/base-provider.d.ts.map +1 -0
  138. package/dist/llm/base-provider.js +91 -0
  139. package/dist/llm/base-provider.js.map +1 -0
  140. package/dist/llm/index.d.ts +17 -0
  141. package/dist/llm/index.d.ts.map +1 -0
  142. package/dist/llm/index.js +36 -0
  143. package/dist/llm/index.js.map +1 -0
  144. package/dist/llm/prompts/bootstrap-prompt.d.ts +27 -0
  145. package/dist/llm/prompts/bootstrap-prompt.d.ts.map +1 -0
  146. package/dist/llm/prompts/bootstrap-prompt.js +278 -0
  147. package/dist/llm/prompts/bootstrap-prompt.js.map +1 -0
  148. package/dist/llm/prompts/index.d.ts +5 -0
  149. package/dist/llm/prompts/index.d.ts.map +1 -0
  150. package/dist/llm/prompts/index.js +11 -0
  151. package/dist/llm/prompts/index.js.map +1 -0
  152. package/dist/llm/provider-factory.d.ts +27 -0
  153. package/dist/llm/provider-factory.d.ts.map +1 -0
  154. package/dist/llm/provider-factory.js +213 -0
  155. package/dist/llm/provider-factory.js.map +1 -0
  156. package/dist/llm/providers/claude-api.d.ts +21 -0
  157. package/dist/llm/providers/claude-api.d.ts.map +1 -0
  158. package/dist/llm/providers/claude-api.js +110 -0
  159. package/dist/llm/providers/claude-api.js.map +1 -0
  160. package/dist/llm/providers/claude-code.d.ts +21 -0
  161. package/dist/llm/providers/claude-code.d.ts.map +1 -0
  162. package/dist/llm/providers/claude-code.js +120 -0
  163. package/dist/llm/providers/claude-code.js.map +1 -0
  164. package/dist/llm/providers/codex-cli.d.ts +25 -0
  165. package/dist/llm/providers/codex-cli.d.ts.map +1 -0
  166. package/dist/llm/providers/codex-cli.js +129 -0
  167. package/dist/llm/providers/codex-cli.js.map +1 -0
  168. package/dist/llm/providers/cursor-cli.d.ts +24 -0
  169. package/dist/llm/providers/cursor-cli.d.ts.map +1 -0
  170. package/dist/llm/providers/cursor-cli.js +106 -0
  171. package/dist/llm/providers/cursor-cli.js.map +1 -0
  172. package/dist/llm/providers/gemini-api.d.ts +20 -0
  173. package/dist/llm/providers/gemini-api.d.ts.map +1 -0
  174. package/dist/llm/providers/gemini-api.js +121 -0
  175. package/dist/llm/providers/gemini-api.js.map +1 -0
  176. package/dist/llm/providers/gemini-cli.d.ts +20 -0
  177. package/dist/llm/providers/gemini-cli.d.ts.map +1 -0
  178. package/dist/llm/providers/gemini-cli.js +109 -0
  179. package/dist/llm/providers/gemini-cli.js.map +1 -0
  180. package/dist/llm/providers/interactive.d.ts +42 -0
  181. package/dist/llm/providers/interactive.d.ts.map +1 -0
  182. package/dist/llm/providers/interactive.js +200 -0
  183. package/dist/llm/providers/interactive.js.map +1 -0
  184. package/dist/llm/providers/openai-api.d.ts +21 -0
  185. package/dist/llm/providers/openai-api.d.ts.map +1 -0
  186. package/dist/llm/providers/openai-api.js +107 -0
  187. package/dist/llm/providers/openai-api.js.map +1 -0
  188. package/dist/llm/types.d.ts +128 -0
  189. package/dist/llm/types.d.ts.map +1 -0
  190. package/dist/llm/types.js +8 -0
  191. package/dist/llm/types.js.map +1 -0
  192. package/dist/parser/index.d.ts +3 -0
  193. package/dist/parser/index.d.ts.map +1 -0
  194. package/dist/parser/index.js +19 -0
  195. package/dist/parser/index.js.map +1 -0
  196. package/dist/parser/path-security.d.ts +40 -0
  197. package/dist/parser/path-security.d.ts.map +1 -0
  198. package/dist/parser/path-security.js +183 -0
  199. package/dist/parser/path-security.js.map +1 -0
  200. package/dist/parser/rule-parser.d.ts +50 -0
  201. package/dist/parser/rule-parser.d.ts.map +1 -0
  202. package/dist/parser/rule-parser.js +203 -0
  203. package/dist/parser/rule-parser.js.map +1 -0
  204. package/dist/schemas/config.d.ts +202 -0
  205. package/dist/schemas/config.d.ts.map +1 -0
  206. package/dist/schemas/config.js +96 -0
  207. package/dist/schemas/config.js.map +1 -0
  208. package/dist/schemas/index.d.ts +3 -0
  209. package/dist/schemas/index.d.ts.map +1 -0
  210. package/dist/schemas/index.js +19 -0
  211. package/dist/schemas/index.js.map +1 -0
  212. package/dist/schemas/rule.d.ts +67 -0
  213. package/dist/schemas/rule.d.ts.map +1 -0
  214. package/dist/schemas/rule.js +44 -0
  215. package/dist/schemas/rule.js.map +1 -0
  216. package/package.json +69 -0
  217. package/templates/architecture.md +35 -0
  218. package/templates/bootstrap-prompt.md +242 -0
  219. package/templates/config.yaml +25 -0
  220. package/templates/project.md +44 -0
  221. package/templates/rules/example.md +36 -0
@@ -0,0 +1,235 @@
1
+ "use strict";
2
+ /**
3
+ * Atomic File Operations
4
+ *
5
+ * Provides safe file writing with:
6
+ * - Temp file → atomic rename pattern
7
+ * - Multi-file transactions (all-or-nothing)
8
+ * - Cleanup on error
9
+ */
10
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ var desc = Object.getOwnPropertyDescriptor(m, k);
13
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
14
+ desc = { enumerable: true, get: function() { return m[k]; } };
15
+ }
16
+ Object.defineProperty(o, k2, desc);
17
+ }) : (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ o[k2] = m[k];
20
+ }));
21
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
22
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
23
+ }) : function(o, v) {
24
+ o["default"] = v;
25
+ });
26
+ var __importStar = (this && this.__importStar) || (function () {
27
+ var ownKeys = function(o) {
28
+ ownKeys = Object.getOwnPropertyNames || function (o) {
29
+ var ar = [];
30
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
31
+ return ar;
32
+ };
33
+ return ownKeys(o);
34
+ };
35
+ return function (mod) {
36
+ if (mod && mod.__esModule) return mod;
37
+ var result = {};
38
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
39
+ __setModuleDefault(result, mod);
40
+ return result;
41
+ };
42
+ })();
43
+ Object.defineProperty(exports, "__esModule", { value: true });
44
+ exports.getTempPath = getTempPath;
45
+ exports.atomicWrite = atomicWrite;
46
+ exports.atomicWriteSync = atomicWriteSync;
47
+ exports.transaction = transaction;
48
+ exports.cleanupStaleTempFiles = cleanupStaleTempFiles;
49
+ const fs = __importStar(require("fs"));
50
+ const path = __importStar(require("path"));
51
+ /**
52
+ * Generate temp file path for atomic write
53
+ *
54
+ * @param targetPath - Target file path
55
+ * @returns Temp file path with pid suffix
56
+ */
57
+ function getTempPath(targetPath) {
58
+ const dir = path.dirname(targetPath);
59
+ const base = path.basename(targetPath);
60
+ return path.join(dir, `${base}.tmp.${process.pid}`);
61
+ }
62
+ /**
63
+ * Write file atomically using temp file + rename
64
+ *
65
+ * @param targetPath - Target file path
66
+ * @param content - Content to write
67
+ * @throws Error if write or rename fails
68
+ */
69
+ async function atomicWrite(targetPath, content) {
70
+ const tempPath = getTempPath(targetPath);
71
+ const dir = path.dirname(targetPath);
72
+ // Ensure directory exists
73
+ await fs.promises.mkdir(dir, { recursive: true });
74
+ try {
75
+ // Write to temp file
76
+ await fs.promises.writeFile(tempPath, content, 'utf-8');
77
+ // Atomic rename
78
+ await fs.promises.rename(tempPath, targetPath);
79
+ }
80
+ catch (error) {
81
+ // Cleanup temp file on error
82
+ try {
83
+ await fs.promises.unlink(tempPath);
84
+ }
85
+ catch {
86
+ // Ignore cleanup errors
87
+ }
88
+ throw error;
89
+ }
90
+ }
91
+ /**
92
+ * Write file atomically (synchronous version)
93
+ *
94
+ * @param targetPath - Target file path
95
+ * @param content - Content to write
96
+ * @throws Error if write or rename fails
97
+ */
98
+ function atomicWriteSync(targetPath, content) {
99
+ const tempPath = getTempPath(targetPath);
100
+ const dir = path.dirname(targetPath);
101
+ // Ensure directory exists
102
+ fs.mkdirSync(dir, { recursive: true });
103
+ try {
104
+ // Write to temp file
105
+ fs.writeFileSync(tempPath, content, 'utf-8');
106
+ // Atomic rename
107
+ fs.renameSync(tempPath, targetPath);
108
+ }
109
+ catch (error) {
110
+ // Cleanup temp file on error
111
+ try {
112
+ fs.unlinkSync(tempPath);
113
+ }
114
+ catch {
115
+ // Ignore cleanup errors
116
+ }
117
+ throw error;
118
+ }
119
+ }
120
+ /**
121
+ * Execute a multi-file transaction (all-or-nothing)
122
+ *
123
+ * If any file fails to write, all temp files are cleaned up
124
+ * and no target files are modified.
125
+ *
126
+ * @param writes - Array of pending writes
127
+ * @returns Transaction result
128
+ */
129
+ async function transaction(writes) {
130
+ const result = {
131
+ success: false,
132
+ writtenFiles: [],
133
+ errors: [],
134
+ };
135
+ // Track temp files for cleanup
136
+ const tempFiles = [];
137
+ try {
138
+ // Phase 1: Write all temp files
139
+ for (const write of writes) {
140
+ const tempPath = getTempPath(write.targetPath);
141
+ const dir = path.dirname(write.targetPath);
142
+ try {
143
+ // Ensure directory exists
144
+ await fs.promises.mkdir(dir, { recursive: true });
145
+ // Write to temp file
146
+ await fs.promises.writeFile(tempPath, write.content, 'utf-8');
147
+ write.tempPath = tempPath;
148
+ tempFiles.push(tempPath);
149
+ }
150
+ catch (error) {
151
+ result.errors.push({
152
+ path: write.targetPath,
153
+ error: error,
154
+ });
155
+ // Cleanup all temp files on any error
156
+ await cleanupTempFiles(tempFiles);
157
+ return result;
158
+ }
159
+ }
160
+ // Phase 2: Atomic rename all files
161
+ for (const write of writes) {
162
+ if (!write.tempPath)
163
+ continue;
164
+ try {
165
+ await fs.promises.rename(write.tempPath, write.targetPath);
166
+ result.writtenFiles.push(write.targetPath);
167
+ }
168
+ catch (error) {
169
+ result.errors.push({
170
+ path: write.targetPath,
171
+ error: error,
172
+ });
173
+ // Cleanup remaining temp files
174
+ const remaining = tempFiles.filter(tf => !result.writtenFiles.some(wf => getTempPath(wf) === tf));
175
+ await cleanupTempFiles(remaining);
176
+ return result;
177
+ }
178
+ }
179
+ result.success = true;
180
+ return result;
181
+ }
182
+ catch (error) {
183
+ // Unexpected error - cleanup all temp files
184
+ await cleanupTempFiles(tempFiles);
185
+ result.errors.push({
186
+ path: 'transaction',
187
+ error: error,
188
+ });
189
+ return result;
190
+ }
191
+ }
192
+ /**
193
+ * Cleanup temp files
194
+ *
195
+ * @param tempFiles - Array of temp file paths to cleanup
196
+ */
197
+ async function cleanupTempFiles(tempFiles) {
198
+ for (const tempPath of tempFiles) {
199
+ try {
200
+ await fs.promises.unlink(tempPath);
201
+ }
202
+ catch {
203
+ // Ignore cleanup errors
204
+ }
205
+ }
206
+ }
207
+ /**
208
+ * Cleanup stale temp files from a previous crashed build
209
+ *
210
+ * @param directory - Directory to scan for temp files
211
+ * @param pattern - Pattern to match temp files (default: *.tmp.*)
212
+ */
213
+ async function cleanupStaleTempFiles(directory, pattern = /\.tmp\.\d+$/) {
214
+ const cleaned = [];
215
+ try {
216
+ const entries = await fs.promises.readdir(directory, { withFileTypes: true });
217
+ for (const entry of entries) {
218
+ if (entry.isFile() && pattern.test(entry.name)) {
219
+ const filePath = path.join(directory, entry.name);
220
+ try {
221
+ await fs.promises.unlink(filePath);
222
+ cleaned.push(filePath);
223
+ }
224
+ catch {
225
+ // Ignore cleanup errors
226
+ }
227
+ }
228
+ }
229
+ }
230
+ catch {
231
+ // Directory doesn't exist or not accessible
232
+ }
233
+ return cleaned;
234
+ }
235
+ //# sourceMappingURL=atomic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atomic.js","sourceRoot":"","sources":["../../src/build/atomic.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Build System
3
+ *
4
+ * Exports for the build system modules.
5
+ */
6
+ export * from './manifest';
7
+ export * from './atomic';
8
+ export * from './lock';
9
+ export * from './orchestrator';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/build/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,gBAAgB,CAAC"}
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ /**
3
+ * Build System
4
+ *
5
+ * Exports for the build system modules.
6
+ */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
19
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
20
+ };
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ __exportStar(require("./manifest"), exports);
23
+ __exportStar(require("./atomic"), exports);
24
+ __exportStar(require("./lock"), exports);
25
+ __exportStar(require("./orchestrator"), exports);
26
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/build/index.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;;;;;;;;;;;;;;AAEH,6CAA2B;AAC3B,2CAAyB;AACzB,yCAAuB;AACvB,iDAA+B"}
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Build Locking
3
+ *
4
+ * Prevents concurrent builds with:
5
+ * - Lock file with pid, timestamp, hostname
6
+ * - Stale lock detection (>5 minutes old)
7
+ * - Automatic cleanup on build completion
8
+ */
9
+ /**
10
+ * Lock file content structure
11
+ */
12
+ export interface LockInfo {
13
+ /** Process ID that holds the lock */
14
+ pid: number;
15
+ /** Timestamp when lock was acquired (Unix ms) */
16
+ timestamp: number;
17
+ /** Hostname of the machine holding the lock */
18
+ hostname: string;
19
+ /** Target being built */
20
+ target: string;
21
+ }
22
+ /**
23
+ * Lock acquisition result
24
+ */
25
+ export interface LockResult {
26
+ /** Whether lock was acquired */
27
+ acquired: boolean;
28
+ /** Path to lock file */
29
+ lockPath: string;
30
+ /** If not acquired, info about existing lock */
31
+ existingLock?: LockInfo;
32
+ /** If stale lock was removed */
33
+ staleRemoved?: boolean;
34
+ }
35
+ /**
36
+ * Get lock file path
37
+ *
38
+ * @param projectRoot - Project root directory
39
+ * @returns Lock file path
40
+ */
41
+ export declare function getLockPath(projectRoot: string): string;
42
+ /**
43
+ * Create lock info for current process
44
+ *
45
+ * @param target - Build target
46
+ * @returns Lock info
47
+ */
48
+ export declare function createLockInfo(target: string): LockInfo;
49
+ /**
50
+ * Read existing lock file
51
+ *
52
+ * @param lockPath - Path to lock file
53
+ * @returns Lock info or null if not found/invalid
54
+ */
55
+ export declare function readLock(lockPath: string): Promise<LockInfo | null>;
56
+ /**
57
+ * Check if a lock is stale (older than threshold)
58
+ *
59
+ * @param lock - Lock info to check
60
+ * @param thresholdMs - Stale threshold in milliseconds
61
+ * @returns true if lock is stale
62
+ */
63
+ export declare function isLockStale(lock: LockInfo, thresholdMs?: number): boolean;
64
+ /**
65
+ * Check if process with given PID is still running
66
+ *
67
+ * @param pid - Process ID to check
68
+ * @returns true if process is running
69
+ */
70
+ export declare function isProcessRunning(pid: number): boolean;
71
+ /**
72
+ * Acquire build lock
73
+ *
74
+ * @param projectRoot - Project root directory
75
+ * @param target - Build target
76
+ * @param staleThresholdMs - Stale lock threshold in milliseconds
77
+ * @returns Lock acquisition result
78
+ */
79
+ export declare function acquireLock(projectRoot: string, target: string, staleThresholdMs?: number): Promise<LockResult>;
80
+ /**
81
+ * Release build lock
82
+ *
83
+ * @param projectRoot - Project root directory
84
+ * @returns true if lock was released
85
+ */
86
+ export declare function releaseLock(projectRoot: string): Promise<boolean>;
87
+ /**
88
+ * Format lock info for display
89
+ *
90
+ * @param lock - Lock info to format
91
+ * @returns Human-readable lock description
92
+ */
93
+ export declare function formatLockInfo(lock: LockInfo): string;
94
+ /**
95
+ * Create a lock guard for RAII-style lock management
96
+ *
97
+ * @param projectRoot - Project root directory
98
+ * @param target - Build target
99
+ * @returns Lock guard with release method
100
+ */
101
+ export declare function withLock<T>(projectRoot: string, target: string, fn: () => Promise<T>): Promise<T>;
102
+ //# sourceMappingURL=lock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lock.d.ts","sourceRoot":"","sources":["../../src/build/lock.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,qCAAqC;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,iDAAiD;IACjD,SAAS,EAAE,MAAM,CAAC;IAClB,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,CAAC;IACjB,yBAAyB;IACzB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,gCAAgC;IAChC,QAAQ,EAAE,OAAO,CAAC;IAClB,wBAAwB;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,gDAAgD;IAChD,YAAY,CAAC,EAAE,QAAQ,CAAC;IACxB,gCAAgC;IAChC,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAYD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAOvD;AAED;;;;;GAKG;AACH,wBAAsB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAWzE;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,EAAE,WAAW,GAAE,MAA2B,GAAG,OAAO,CAG7F;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAUrD;AAED;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAC/B,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,EACd,gBAAgB,GAAE,MAA2B,GAC5C,OAAO,CAAC,UAAU,CAAC,CAiDrB;AAwCD;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CA0BvE;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAIrD;AAmBD;;;;;;GAMG;AACH,wBAAsB,QAAQ,CAAC,CAAC,EAC9B,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,CAAC,CAAC,CAgBZ"}
@@ -0,0 +1,297 @@
1
+ "use strict";
2
+ /**
3
+ * Build Locking
4
+ *
5
+ * Prevents concurrent builds with:
6
+ * - Lock file with pid, timestamp, hostname
7
+ * - Stale lock detection (>5 minutes old)
8
+ * - Automatic cleanup on build completion
9
+ */
10
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ var desc = Object.getOwnPropertyDescriptor(m, k);
13
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
14
+ desc = { enumerable: true, get: function() { return m[k]; } };
15
+ }
16
+ Object.defineProperty(o, k2, desc);
17
+ }) : (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ o[k2] = m[k];
20
+ }));
21
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
22
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
23
+ }) : function(o, v) {
24
+ o["default"] = v;
25
+ });
26
+ var __importStar = (this && this.__importStar) || (function () {
27
+ var ownKeys = function(o) {
28
+ ownKeys = Object.getOwnPropertyNames || function (o) {
29
+ var ar = [];
30
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
31
+ return ar;
32
+ };
33
+ return ownKeys(o);
34
+ };
35
+ return function (mod) {
36
+ if (mod && mod.__esModule) return mod;
37
+ var result = {};
38
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
39
+ __setModuleDefault(result, mod);
40
+ return result;
41
+ };
42
+ })();
43
+ Object.defineProperty(exports, "__esModule", { value: true });
44
+ exports.getLockPath = getLockPath;
45
+ exports.createLockInfo = createLockInfo;
46
+ exports.readLock = readLock;
47
+ exports.isLockStale = isLockStale;
48
+ exports.isProcessRunning = isProcessRunning;
49
+ exports.acquireLock = acquireLock;
50
+ exports.releaseLock = releaseLock;
51
+ exports.formatLockInfo = formatLockInfo;
52
+ exports.withLock = withLock;
53
+ const fs = __importStar(require("fs"));
54
+ const path = __importStar(require("path"));
55
+ const os = __importStar(require("os"));
56
+ /**
57
+ * Default stale lock threshold (5 minutes in ms)
58
+ */
59
+ const STALE_THRESHOLD_MS = 5 * 60 * 1000;
60
+ /**
61
+ * Lock file name
62
+ */
63
+ const LOCK_FILENAME = '.build.lock';
64
+ /**
65
+ * Get lock file path
66
+ *
67
+ * @param projectRoot - Project root directory
68
+ * @returns Lock file path
69
+ */
70
+ function getLockPath(projectRoot) {
71
+ return path.join(projectRoot, '.context', LOCK_FILENAME);
72
+ }
73
+ /**
74
+ * Create lock info for current process
75
+ *
76
+ * @param target - Build target
77
+ * @returns Lock info
78
+ */
79
+ function createLockInfo(target) {
80
+ return {
81
+ pid: process.pid,
82
+ timestamp: Date.now(),
83
+ hostname: os.hostname(),
84
+ target,
85
+ };
86
+ }
87
+ /**
88
+ * Read existing lock file
89
+ *
90
+ * @param lockPath - Path to lock file
91
+ * @returns Lock info or null if not found/invalid
92
+ */
93
+ async function readLock(lockPath) {
94
+ try {
95
+ const content = await fs.promises.readFile(lockPath, 'utf-8');
96
+ return JSON.parse(content);
97
+ }
98
+ catch (error) {
99
+ if (error.code === 'ENOENT') {
100
+ return null;
101
+ }
102
+ // Invalid JSON or other error - treat as no lock
103
+ return null;
104
+ }
105
+ }
106
+ /**
107
+ * Check if a lock is stale (older than threshold)
108
+ *
109
+ * @param lock - Lock info to check
110
+ * @param thresholdMs - Stale threshold in milliseconds
111
+ * @returns true if lock is stale
112
+ */
113
+ function isLockStale(lock, thresholdMs = STALE_THRESHOLD_MS) {
114
+ const age = Date.now() - lock.timestamp;
115
+ return age > thresholdMs;
116
+ }
117
+ /**
118
+ * Check if process with given PID is still running
119
+ *
120
+ * @param pid - Process ID to check
121
+ * @returns true if process is running
122
+ */
123
+ function isProcessRunning(pid) {
124
+ try {
125
+ // kill with signal 0 checks if process exists without killing it
126
+ process.kill(pid, 0);
127
+ return true;
128
+ }
129
+ catch (error) {
130
+ // EPERM means process exists but we don't have permission
131
+ // ESRCH means process doesn't exist
132
+ return error.code === 'EPERM';
133
+ }
134
+ }
135
+ /**
136
+ * Acquire build lock
137
+ *
138
+ * @param projectRoot - Project root directory
139
+ * @param target - Build target
140
+ * @param staleThresholdMs - Stale lock threshold in milliseconds
141
+ * @returns Lock acquisition result
142
+ */
143
+ async function acquireLock(projectRoot, target, staleThresholdMs = STALE_THRESHOLD_MS) {
144
+ const lockPath = getLockPath(projectRoot);
145
+ const lockDir = path.dirname(lockPath);
146
+ // Ensure directory exists
147
+ await fs.promises.mkdir(lockDir, { recursive: true });
148
+ // Check for existing lock
149
+ const existingLock = await readLock(lockPath);
150
+ if (existingLock) {
151
+ // Check if lock is stale
152
+ if (isLockStale(existingLock, staleThresholdMs)) {
153
+ // Remove stale lock
154
+ try {
155
+ await fs.promises.unlink(lockPath);
156
+ }
157
+ catch {
158
+ // Ignore removal errors
159
+ }
160
+ // Try to acquire
161
+ return await tryAcquireLock(lockPath, target, true);
162
+ }
163
+ // Check if holding process is still running (same hostname only)
164
+ if (existingLock.hostname === os.hostname()) {
165
+ if (!isProcessRunning(existingLock.pid)) {
166
+ // Process is dead, remove lock
167
+ try {
168
+ await fs.promises.unlink(lockPath);
169
+ }
170
+ catch {
171
+ // Ignore removal errors
172
+ }
173
+ // Try to acquire
174
+ return await tryAcquireLock(lockPath, target, true);
175
+ }
176
+ }
177
+ // Lock is active - cannot acquire
178
+ return {
179
+ acquired: false,
180
+ lockPath,
181
+ existingLock,
182
+ };
183
+ }
184
+ // No existing lock - try to acquire
185
+ return await tryAcquireLock(lockPath, target, false);
186
+ }
187
+ /**
188
+ * Try to acquire the lock (internal helper)
189
+ */
190
+ async function tryAcquireLock(lockPath, target, staleRemoved) {
191
+ const lockInfo = createLockInfo(target);
192
+ try {
193
+ // Write lock file with exclusive flag
194
+ const fd = await fs.promises.open(lockPath, fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_EXCL);
195
+ try {
196
+ await fd.writeFile(JSON.stringify(lockInfo, null, 2), 'utf-8');
197
+ }
198
+ finally {
199
+ await fd.close();
200
+ }
201
+ return {
202
+ acquired: true,
203
+ lockPath,
204
+ staleRemoved,
205
+ };
206
+ }
207
+ catch (error) {
208
+ if (error.code === 'EEXIST') {
209
+ // Race condition - another process acquired the lock
210
+ const existingLock = await readLock(lockPath);
211
+ return {
212
+ acquired: false,
213
+ lockPath,
214
+ existingLock: existingLock || undefined,
215
+ };
216
+ }
217
+ throw error;
218
+ }
219
+ }
220
+ /**
221
+ * Release build lock
222
+ *
223
+ * @param projectRoot - Project root directory
224
+ * @returns true if lock was released
225
+ */
226
+ async function releaseLock(projectRoot) {
227
+ const lockPath = getLockPath(projectRoot);
228
+ try {
229
+ // Verify we own the lock before releasing
230
+ const existingLock = await readLock(lockPath);
231
+ if (!existingLock) {
232
+ // Lock doesn't exist - nothing to release
233
+ return false;
234
+ }
235
+ // Only release if we own the lock
236
+ if (existingLock.pid !== process.pid || existingLock.hostname !== os.hostname()) {
237
+ // We don't own this lock
238
+ return false;
239
+ }
240
+ await fs.promises.unlink(lockPath);
241
+ return true;
242
+ }
243
+ catch (error) {
244
+ if (error.code === 'ENOENT') {
245
+ return false;
246
+ }
247
+ throw error;
248
+ }
249
+ }
250
+ /**
251
+ * Format lock info for display
252
+ *
253
+ * @param lock - Lock info to format
254
+ * @returns Human-readable lock description
255
+ */
256
+ function formatLockInfo(lock) {
257
+ const age = Date.now() - lock.timestamp;
258
+ const ageStr = formatDuration(age);
259
+ return `Build in progress by PID ${lock.pid} on ${lock.hostname} (${ageStr} ago) for target: ${lock.target}`;
260
+ }
261
+ /**
262
+ * Format duration in human-readable form
263
+ */
264
+ function formatDuration(ms) {
265
+ const seconds = Math.floor(ms / 1000);
266
+ const minutes = Math.floor(seconds / 60);
267
+ const hours = Math.floor(minutes / 60);
268
+ if (hours > 0) {
269
+ return `${hours}h ${minutes % 60}m`;
270
+ }
271
+ if (minutes > 0) {
272
+ return `${minutes}m ${seconds % 60}s`;
273
+ }
274
+ return `${seconds}s`;
275
+ }
276
+ /**
277
+ * Create a lock guard for RAII-style lock management
278
+ *
279
+ * @param projectRoot - Project root directory
280
+ * @param target - Build target
281
+ * @returns Lock guard with release method
282
+ */
283
+ async function withLock(projectRoot, target, fn) {
284
+ const result = await acquireLock(projectRoot, target);
285
+ if (!result.acquired) {
286
+ throw new Error(result.existingLock
287
+ ? `Build already in progress: ${formatLockInfo(result.existingLock)}`
288
+ : 'Failed to acquire build lock');
289
+ }
290
+ try {
291
+ return await fn();
292
+ }
293
+ finally {
294
+ await releaseLock(projectRoot);
295
+ }
296
+ }
297
+ //# sourceMappingURL=lock.js.map