@kici-dev/compiler 0.0.0 → 0.1.1

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 +202 -0
  2. package/README.md +1 -6
  3. package/dist/assets/api-TJJVHrjC.json +118 -0
  4. package/dist/assets/descriptor-BTtjzN9L.json +1382 -0
  5. package/dist/assets/package-BpQF9kR8.json +74 -0
  6. package/dist/assets/package-Ceo2h27X.json +89 -0
  7. package/dist/assets/source_context-D0atuL28.json +20 -0
  8. package/dist/assets/type-BFqO8SCZ.json +202 -0
  9. package/dist/auth/headless-detect.d.ts +14 -0
  10. package/dist/auth/headless-detect.js +25 -0
  11. package/dist/chunk-gOLHoazu.js +4 -0
  12. package/dist/cli.d.ts +3 -0
  13. package/dist/cli.js +193 -0
  14. package/dist/commands/cancel.d.ts +22 -0
  15. package/dist/commands/cancel.js +124 -0
  16. package/dist/commands/compile.d.ts +17 -0
  17. package/dist/commands/compile.js +99 -0
  18. package/dist/commands/docs.d.ts +26 -0
  19. package/dist/commands/docs.js +60 -0
  20. package/dist/commands/drain-worker.d.ts +19 -0
  21. package/dist/commands/drain-worker.js +45 -0
  22. package/dist/commands/endpoints.d.ts +53 -0
  23. package/dist/commands/endpoints.js +185 -0
  24. package/dist/commands/fixture.d.ts +14 -0
  25. package/dist/commands/fixture.js +104 -0
  26. package/dist/commands/hook.d.ts +20 -0
  27. package/dist/commands/hook.js +97 -0
  28. package/dist/commands/index.d.ts +33 -0
  29. package/dist/commands/index.js +20 -0
  30. package/dist/commands/init.d.ts +35 -0
  31. package/dist/commands/init.js +393 -0
  32. package/dist/commands/login.d.ts +23 -0
  33. package/dist/commands/login.js +144 -0
  34. package/dist/commands/logout.d.ts +18 -0
  35. package/dist/commands/logout.js +55 -0
  36. package/dist/commands/org.d.ts +27 -0
  37. package/dist/commands/org.js +126 -0
  38. package/dist/commands/run.d.ts +21 -0
  39. package/dist/commands/run.js +562 -0
  40. package/dist/commands/secrets-list.d.ts +16 -0
  41. package/dist/commands/secrets-list.js +85 -0
  42. package/dist/commands/status.d.ts +30 -0
  43. package/dist/commands/status.js +210 -0
  44. package/dist/commands/test.d.ts +61 -0
  45. package/dist/commands/test.js +220 -0
  46. package/dist/commands/types.d.ts +18 -0
  47. package/dist/commands/types.js +74 -0
  48. package/dist/commands/watch.d.ts +14 -0
  49. package/dist/commands/watch.js +97 -0
  50. package/dist/commands/workflows.d.ts +28 -0
  51. package/dist/commands/workflows.js +154 -0
  52. package/dist/comment-created.json +30 -0
  53. package/dist/create-branch.json +17 -0
  54. package/dist/delete-branch.json +15 -0
  55. package/dist/dispatch.json +15 -0
  56. package/dist/errors/capability-gap.d.ts +50 -0
  57. package/dist/errors/capability-gap.js +54 -0
  58. package/dist/errors/codes.d.ts +25 -0
  59. package/dist/errors/formatter.d.ts +32 -0
  60. package/dist/errors/formatter.js +35 -0
  61. package/dist/errors/index.d.ts +5 -0
  62. package/dist/errors/index.js +4 -0
  63. package/dist/execution/executor.d.ts +61 -0
  64. package/dist/execution/executor.js +230 -0
  65. package/dist/execution/index.d.ts +4 -0
  66. package/dist/execution/index.js +3 -0
  67. package/dist/execution/sdk-alias.d.ts +33 -0
  68. package/dist/execution/sdk-alias.js +102 -0
  69. package/dist/fixtures/compiler.d.ts +47 -0
  70. package/dist/fixtures/compiler.js +177 -0
  71. package/dist/fixtures/defaults/comment-created.json +30 -0
  72. package/dist/fixtures/defaults/create-branch.json +17 -0
  73. package/dist/fixtures/defaults/delete-branch.json +15 -0
  74. package/dist/fixtures/defaults/dispatch.json +15 -0
  75. package/dist/fixtures/defaults/fork.json +19 -0
  76. package/dist/fixtures/defaults/index.d.ts +16 -0
  77. package/dist/fixtures/defaults/index.js +105 -0
  78. package/dist/fixtures/defaults/index.ts +145 -0
  79. package/dist/fixtures/defaults/pr-closed.json +28 -0
  80. package/dist/fixtures/defaults/pr-opened.json +27 -0
  81. package/dist/fixtures/defaults/pr-reopened.json +27 -0
  82. package/dist/fixtures/defaults/pr-synchronize.json +27 -0
  83. package/dist/fixtures/defaults/push.json +16 -0
  84. package/dist/fixtures/defaults/release-published.json +23 -0
  85. package/dist/fixtures/defaults/review-comment-created.json +36 -0
  86. package/dist/fixtures/defaults/review-submitted.json +35 -0
  87. package/dist/fixtures/defaults/star-created.json +15 -0
  88. package/dist/fixtures/defaults/status.json +26 -0
  89. package/dist/fixtures/defaults/tag-push.json +20 -0
  90. package/dist/fixtures/defaults/watch-started.json +15 -0
  91. package/dist/fixtures/defaults/workflow-run-completed.json +23 -0
  92. package/dist/fork.json +19 -0
  93. package/dist/format.d.ts +5 -0
  94. package/dist/format.js +20 -0
  95. package/dist/generators/secrets-dts.d.ts +29 -0
  96. package/dist/generators/secrets-dts.js +67 -0
  97. package/dist/hooks/detector.d.ts +39 -0
  98. package/dist/hooks/detector.js +133 -0
  99. package/dist/hooks/index.d.ts +6 -0
  100. package/dist/hooks/index.js +5 -0
  101. package/dist/hooks/installer.d.ts +24 -0
  102. package/dist/hooks/installer.js +328 -0
  103. package/dist/hooks/templates.d.ts +26 -0
  104. package/dist/hooks/templates.js +63 -0
  105. package/dist/index.d.ts +12 -0
  106. package/dist/index.js +12 -0
  107. package/dist/llm-context/llms-full.txt +9974 -0
  108. package/dist/llm-context/llms.txt +61 -0
  109. package/dist/local-executor/dag-scheduler.d.ts +44 -0
  110. package/dist/local-executor/dag-scheduler.js +183 -0
  111. package/dist/local-executor/index.d.ts +23 -0
  112. package/dist/local-executor/index.js +309 -0
  113. package/dist/local-executor/job-runner.d.ts +40 -0
  114. package/dist/local-executor/job-runner.js +307 -0
  115. package/dist/local-executor/output-streamer.d.ts +31 -0
  116. package/dist/local-executor/output-streamer.js +166 -0
  117. package/dist/local-executor/payload-generator.d.ts +16 -0
  118. package/dist/local-executor/payload-generator.js +138 -0
  119. package/dist/local-executor/picker.d.ts +33 -0
  120. package/dist/local-executor/picker.js +109 -0
  121. package/dist/local-executor/secret-loader.d.ts +18 -0
  122. package/dist/local-executor/secret-loader.js +105 -0
  123. package/dist/local-executor/types.d.ts +80 -0
  124. package/dist/local-executor/types.js +2 -0
  125. package/dist/local-executor/workflow-lock.d.ts +81 -0
  126. package/dist/local-executor/workflow-lock.js +0 -0
  127. package/dist/lockfile/generator.d.ts +39 -0
  128. package/dist/lockfile/generator.js +688 -0
  129. package/dist/lockfile/hash-files.d.ts +14 -0
  130. package/dist/lockfile/hash-files.js +50 -0
  131. package/dist/lockfile/hasher.d.ts +36 -0
  132. package/dist/lockfile/hasher.js +46 -0
  133. package/dist/lockfile/index.d.ts +3 -0
  134. package/dist/lockfile/index.js +4 -0
  135. package/dist/lockfile/purity-analyzer.d.ts +25 -0
  136. package/dist/lockfile/purity-analyzer.js +204 -0
  137. package/dist/package-F7UXSDHW.json +74 -0
  138. package/dist/postinstall.d.ts +9 -0
  139. package/dist/postinstall.js +60 -0
  140. package/dist/pr-closed.json +28 -0
  141. package/dist/pr-opened.json +27 -0
  142. package/dist/pr-reopened.json +27 -0
  143. package/dist/pr-synchronize.json +27 -0
  144. package/dist/push.json +16 -0
  145. package/dist/release-published.json +23 -0
  146. package/dist/remote/client.d.ts +204 -0
  147. package/dist/remote/client.js +203 -0
  148. package/dist/remote/config.d.ts +54 -0
  149. package/dist/remote/config.js +92 -0
  150. package/dist/remote/encryption.d.ts +35 -0
  151. package/dist/remote/encryption.js +87 -0
  152. package/dist/remote/history.d.ts +91 -0
  153. package/dist/remote/history.js +146 -0
  154. package/dist/remote/oauth.d.ts +65 -0
  155. package/dist/remote/oauth.js +302 -0
  156. package/dist/remote/observer.d.ts +81 -0
  157. package/dist/remote/observer.js +174 -0
  158. package/dist/remote/output/json.d.ts +13 -0
  159. package/dist/remote/output/json.js +36 -0
  160. package/dist/remote/output/junit.d.ts +18 -0
  161. package/dist/remote/output/junit.js +50 -0
  162. package/dist/remote/output/streaming.d.ts +57 -0
  163. package/dist/remote/output/streaming.js +124 -0
  164. package/dist/remote/output/summary.d.ts +39 -0
  165. package/dist/remote/output/summary.js +99 -0
  166. package/dist/remote/uploader.d.ts +87 -0
  167. package/dist/remote/uploader.js +197 -0
  168. package/dist/review-comment-created.json +36 -0
  169. package/dist/review-submitted.json +35 -0
  170. package/dist/star-created.json +15 -0
  171. package/dist/status.json +26 -0
  172. package/dist/tag-push.json +20 -0
  173. package/dist/templates/agents-md.d.ts +8 -0
  174. package/dist/templates/agents-md.js +130 -0
  175. package/dist/templates/index.d.ts +43 -0
  176. package/dist/templates/index.js +48 -0
  177. package/dist/templates/package-json.d.ts +18 -0
  178. package/dist/templates/package-json.js +34 -0
  179. package/dist/templates/tsconfig-json.d.ts +9 -0
  180. package/dist/templates/tsconfig-json.js +26 -0
  181. package/dist/templates/workflows/hello-world.d.ts +2 -0
  182. package/dist/templates/workflows/hello-world.js +16 -0
  183. package/dist/templates/workflows/hello-world.ts +21 -0
  184. package/dist/templates/workflows/pr-checks.d.ts +2 -0
  185. package/dist/templates/workflows/pr-checks.js +40 -0
  186. package/dist/templates/workflows/pr-checks.ts +57 -0
  187. package/dist/test-runner/dry-run.d.ts +12 -0
  188. package/dist/test-runner/dry-run.js +66 -0
  189. package/dist/test-runner/event-types.d.ts +86 -0
  190. package/dist/test-runner/event-types.js +330 -0
  191. package/dist/test-runner/git-detector.d.ts +15 -0
  192. package/dist/test-runner/git-detector.js +30 -0
  193. package/dist/test-runner/index.d.ts +13 -0
  194. package/dist/test-runner/index.js +11 -0
  195. package/dist/test-runner/job-executor.d.ts +48 -0
  196. package/dist/test-runner/job-executor.js +245 -0
  197. package/dist/test-runner/output-formatter.d.ts +47 -0
  198. package/dist/test-runner/output-formatter.js +92 -0
  199. package/dist/test-runner/payload-builder.d.ts +20 -0
  200. package/dist/test-runner/payload-builder.js +216 -0
  201. package/dist/test-runner/rule-evaluator.d.ts +14 -0
  202. package/dist/test-runner/rule-evaluator.js +31 -0
  203. package/dist/test-runner/secrets-file.d.ts +24 -0
  204. package/dist/test-runner/secrets-file.js +62 -0
  205. package/dist/test-runner/step-context.d.ts +15 -0
  206. package/dist/test-runner/step-context.js +140 -0
  207. package/dist/test-runner/summary.d.ts +7 -0
  208. package/dist/types.d.ts +525 -0
  209. package/dist/types.js +33 -0
  210. package/dist/validation/index.d.ts +3 -0
  211. package/dist/validation/index.js +3 -0
  212. package/dist/validation/validator.d.ts +26 -0
  213. package/dist/validation/validator.js +164 -0
  214. package/dist/watch-started.json +15 -0
  215. package/dist/workflow-run-completed.json +23 -0
  216. package/dist/workflows/hello-world.ts +21 -0
  217. package/dist/workflows/pr-checks.ts +57 -0
  218. package/hack/postinstall.mjs +105 -0
  219. package/package.json +61 -5
  220. package/sbom.spdx.json +10997 -0
  221. package/index.js +0 -3
@@ -0,0 +1,688 @@
1
+ import "../chunk-gOLHoazu.js";
2
+ import { SCHEMA_VERSION as SCHEMA_VERSION$1 } from "../types.js";
3
+ import { computeContentHash } from "./hasher.js";
4
+ import { resolveHashFiles } from "./hash-files.js";
5
+ import { analyzePurity } from "./purity-analyzer.js";
6
+ import { readFileSync } from "node:fs";
7
+ import path from "node:path";
8
+ import { execSync } from "node:child_process";
9
+ import { sha256 } from "@kici-dev/shared";
10
+ import { getDynamicJobGroup, isDynamicFunction, isDynamicGroupRef, isDynamicJobFn, isStaticArray, isStaticObject } from "@kici-dev/sdk";
11
+ import { validateResourceRequest } from "@kici-dev/engine";
12
+ //#region src/lockfile/generator.ts
13
+ /**
14
+ * Detect git repository root by running `git rev-parse --show-toplevel`.
15
+ * Falls back to cwd if not in a git repo.
16
+ *
17
+ * @returns Absolute path to git root, or cwd if not in git repo
18
+ */
19
+ function detectGitRoot() {
20
+ try {
21
+ return execSync("git rev-parse --show-toplevel", {
22
+ encoding: "utf-8",
23
+ stdio: [
24
+ "pipe",
25
+ "pipe",
26
+ "pipe"
27
+ ]
28
+ }).trim();
29
+ } catch {
30
+ return process.cwd();
31
+ }
32
+ }
33
+ /**
34
+ * Compute SHA-256 hash of the .kici/package-lock.json lockfile.
35
+ * Used as part of the dependency cache key.
36
+ *
37
+ * @param gitRoot - Absolute path to git repository root
38
+ * @returns Hex SHA-256 hash string, or null if no lockfile found in .kici/
39
+ */
40
+ function computeLockfileHash(gitRoot) {
41
+ for (const candidate of ["package-lock.json"]) try {
42
+ return sha256(readFileSync(path.join(gitRoot, ".kici", candidate), "utf-8"));
43
+ } catch {
44
+ continue;
45
+ }
46
+ return null;
47
+ }
48
+ /**
49
+ * Format export reference with hash syntax.
50
+ * Examples:
51
+ * - #build (named export)
52
+ * - #default (single default export)
53
+ * - #default[0] (first item in default array)
54
+ *
55
+ * @param source - Workflow source info
56
+ * @returns Export reference string with hash prefix
57
+ */
58
+ function formatExportRef(source) {
59
+ if (source.arrayIndex !== void 0) return `#${source.exportName}[${source.arrayIndex}]`;
60
+ return `#${source.exportName}`;
61
+ }
62
+ /**
63
+ * Generate a lock file from validated workflows with source tracking.
64
+ *
65
+ * @param workflowsWithSource - Validated workflows with source info
66
+ * @returns Lock file ready for JSON serialization
67
+ */
68
+ function generateLockFile(workflowsWithSource) {
69
+ const gitRoot = detectGitRoot();
70
+ const lockfileHash = computeLockfileHash(gitRoot);
71
+ const firstSource = workflowsWithSource[0]?.source;
72
+ const topLevelSource = firstSource ? {
73
+ file: path.relative(gitRoot, firstSource.file).replaceAll("\\", "/"),
74
+ export: formatExportRef(firstSource)
75
+ } : {
76
+ file: ".kici/workflows",
77
+ export: "#default"
78
+ };
79
+ const workflows = workflowsWithSource.map(({ workflow, source, bundleSource }) => {
80
+ return transformWorkflow(workflow, path.relative(gitRoot, source.file).replaceAll("\\", "/"), formatExportRef(source), bundleSource, gitRoot);
81
+ });
82
+ return {
83
+ schemaVersion: SCHEMA_VERSION$1,
84
+ source: topLevelSource,
85
+ contentHash: sha256(JSON.stringify({
86
+ schemaVersion: SCHEMA_VERSION$1,
87
+ source: topLevelSource,
88
+ workflows
89
+ })),
90
+ ...lockfileHash && { lockfileHash },
91
+ workflows
92
+ };
93
+ }
94
+ /**
95
+ * Transform SDK Workflow to lock file format.
96
+ *
97
+ * @param workflow - The workflow definition
98
+ * @param sourceFile - Relative path to source file (from git root)
99
+ * @param exportRef - Export reference (e.g., #build, #default[0])
100
+ * @param bundleSource - Compiled JS bundle for content hashing
101
+ * @param gitRoot - Git repository root for resolving hashFiles
102
+ */
103
+ function transformWorkflow(workflow, sourceFile, exportRef, bundleSource, gitRoot) {
104
+ let assetDigest;
105
+ let resolvedHashFiles;
106
+ if (workflow.hashFiles && workflow.hashFiles.length > 0) {
107
+ const resolved = resolveHashFiles(gitRoot, workflow.hashFiles);
108
+ if (resolved) {
109
+ assetDigest = resolved.assetDigest;
110
+ resolvedHashFiles = resolved.resolvedPaths;
111
+ }
112
+ }
113
+ const contentHash = bundleSource !== void 0 ? computeContentHash(bundleSource, 5, assetDigest) : "";
114
+ const compileSchemaVersion = bundleSource !== void 0 ? 5 : 0;
115
+ return {
116
+ name: workflow.name,
117
+ source: {
118
+ file: sourceFile,
119
+ export: exportRef
120
+ },
121
+ contentHash,
122
+ compileSchemaVersion,
123
+ triggers: transformTriggers(workflow.on),
124
+ jobs: transformJobs(workflow.jobs, sourceFile, gitRoot),
125
+ rules: workflow.rules ? transformRules(workflow.rules, sourceFile) : void 0,
126
+ description: workflow.description,
127
+ ...workflow.hashFiles?.length && { hashFiles: workflow.hashFiles },
128
+ ...resolvedHashFiles?.length && { resolvedHashFiles },
129
+ ...workflow.registries?.length && { registries: workflow.registries.map((r) => ({
130
+ url: r.url,
131
+ ...r.scope !== void 0 && { scope: r.scope },
132
+ tokenSecret: r.tokenSecret,
133
+ ...r.alwaysAuth !== void 0 && { alwaysAuth: r.alwaysAuth }
134
+ })) },
135
+ ...workflow.installEnv?.length && { installEnv: [...workflow.installEnv] },
136
+ ...workflow.onCancel !== void 0 && { hasOnCancel: true },
137
+ ...workflow.cleanup !== void 0 && { hasCleanup: true },
138
+ ...workflow.onSuccess !== void 0 && { hasOnSuccess: true },
139
+ ...workflow.onFailure !== void 0 && { hasOnFailure: true },
140
+ ...workflow.concurrency && { concurrency: {
141
+ hasGroup: !!workflow.concurrency.group,
142
+ ...workflow.concurrency.cancelInProgress !== void 0 && { cancelInProgress: workflow.concurrency.cancelInProgress },
143
+ ...workflow.concurrency.max !== void 0 && { max: workflow.concurrency.max }
144
+ } }
145
+ };
146
+ }
147
+ /**
148
+ * Spread fragment that adds a `repos` field only when the SDK trigger
149
+ * actually carries cross-repo patterns. Identical pattern is required by
150
+ * 14 of the 22 trigger transforms below.
151
+ */
152
+ function reposField(trigger) {
153
+ return trigger.repos && trigger.repos.length > 0 ? { repos: transformBranchPatterns(trigger.repos) } : {};
154
+ }
155
+ function toLockPr(t) {
156
+ return {
157
+ _type: "pr",
158
+ events: t.events,
159
+ targetBranches: transformBranchPatterns(t.targetBranches),
160
+ sourceBranches: transformBranchPatterns(t.sourceBranches),
161
+ paths: t.paths,
162
+ ...reposField(t)
163
+ };
164
+ }
165
+ function toLockPushAndTag(t) {
166
+ const results = [{
167
+ _type: "push",
168
+ branches: transformBranchPatterns(t.branches),
169
+ paths: t.paths,
170
+ ...reposField(t)
171
+ }];
172
+ if (t.tags.length > 0) results.push({
173
+ _type: "tag",
174
+ patterns: transformBranchPatterns(t.tags)
175
+ });
176
+ return results;
177
+ }
178
+ function toLockTag(t) {
179
+ return {
180
+ _type: "tag",
181
+ patterns: transformBranchPatterns(t.patterns),
182
+ ...reposField(t)
183
+ };
184
+ }
185
+ function toLockComment(t) {
186
+ return {
187
+ _type: "comment",
188
+ actions: t.actions,
189
+ source: t.source,
190
+ bodyMatch: t.bodyMatch,
191
+ ...reposField(t)
192
+ };
193
+ }
194
+ function toLockReview(t) {
195
+ return {
196
+ _type: "review",
197
+ actions: t.actions,
198
+ states: t.states,
199
+ ...reposField(t)
200
+ };
201
+ }
202
+ function toLockReviewComment(t) {
203
+ return {
204
+ _type: "review_comment",
205
+ actions: t.actions,
206
+ ...reposField(t)
207
+ };
208
+ }
209
+ function toLockRelease(t) {
210
+ return {
211
+ _type: "release",
212
+ actions: t.actions,
213
+ ...reposField(t)
214
+ };
215
+ }
216
+ function toLockDispatch(t) {
217
+ return {
218
+ _type: "dispatch",
219
+ types: t.types,
220
+ ...reposField(t)
221
+ };
222
+ }
223
+ function toLockCreate(t) {
224
+ return {
225
+ _type: "create",
226
+ refTypes: t.refTypes,
227
+ patterns: transformBranchPatterns(t.patterns),
228
+ ...reposField(t)
229
+ };
230
+ }
231
+ function toLockDelete(t) {
232
+ return {
233
+ _type: "delete",
234
+ refTypes: t.refTypes,
235
+ patterns: transformBranchPatterns(t.patterns),
236
+ ...reposField(t)
237
+ };
238
+ }
239
+ function toLockStatus(t) {
240
+ return {
241
+ _type: "status",
242
+ contexts: t.contexts,
243
+ states: t.states,
244
+ ...reposField(t)
245
+ };
246
+ }
247
+ function toLockWorkflowRun(t) {
248
+ return {
249
+ _type: "workflow_run",
250
+ actions: t.actions,
251
+ workflows: t.workflows,
252
+ conclusions: t.conclusions,
253
+ ...reposField(t)
254
+ };
255
+ }
256
+ function toLockFork(t) {
257
+ return {
258
+ _type: "fork",
259
+ ...reposField(t)
260
+ };
261
+ }
262
+ function toLockStar(t) {
263
+ return {
264
+ _type: "star",
265
+ actions: t.actions,
266
+ ...reposField(t)
267
+ };
268
+ }
269
+ function toLockWatch(t) {
270
+ return {
271
+ _type: "watch",
272
+ actions: t.actions,
273
+ ...reposField(t)
274
+ };
275
+ }
276
+ function toLockWebhook(t) {
277
+ return {
278
+ _type: "webhook",
279
+ events: t.events,
280
+ actions: t.actions,
281
+ ...reposField(t)
282
+ };
283
+ }
284
+ function toLockKiciEvent(t) {
285
+ return {
286
+ _type: "kici_event",
287
+ eventName: t.name,
288
+ ...t.match !== void 0 && { match: t.match },
289
+ ...t.not !== void 0 && { not: t.not },
290
+ ...t.source !== void 0 && { source: t.source }
291
+ };
292
+ }
293
+ function toLockWorkflowComplete(t) {
294
+ return {
295
+ _type: "workflow_complete",
296
+ ...t.name !== void 0 && { name: t.name },
297
+ ...t.status !== void 0 && { status: t.status },
298
+ ...t.source !== void 0 && { source: t.source }
299
+ };
300
+ }
301
+ function toLockJobComplete(t) {
302
+ return {
303
+ _type: "job_complete",
304
+ ...t.workflow !== void 0 && { workflow: t.workflow },
305
+ ...t.job !== void 0 && { job: t.job },
306
+ ...t.status !== void 0 && { status: t.status },
307
+ ...t.source !== void 0 && { source: t.source }
308
+ };
309
+ }
310
+ function toLockGenericWebhookAuth(auth) {
311
+ return {
312
+ method: auth.method,
313
+ secret: auth.secret,
314
+ ...auth.method === "hmac-sha256" && { signatureHeader: auth.signatureHeader },
315
+ ...auth.method === "api-key" && auth.header && { header: auth.header }
316
+ };
317
+ }
318
+ function toLockGenericWebhook(t) {
319
+ return {
320
+ _type: "generic_webhook",
321
+ source: t.source,
322
+ ...t.events !== void 0 && { events: t.events },
323
+ ...t.match !== void 0 && { match: t.match },
324
+ ...t.not !== void 0 && { not: t.not },
325
+ ...t.auth !== void 0 && { auth: toLockGenericWebhookAuth(t.auth) },
326
+ ...t.path !== void 0 && { path: t.path }
327
+ };
328
+ }
329
+ function toLockSchedule(t) {
330
+ return {
331
+ _type: "schedule",
332
+ cronExpression: t.cron,
333
+ timezone: t.timezone,
334
+ ...t.description && { description: t.description }
335
+ };
336
+ }
337
+ function toLockLifecycle(t) {
338
+ return {
339
+ _type: "lifecycle",
340
+ events: [...t.events],
341
+ ...t.sources && { sources: [...t.sources] },
342
+ ...t.description && { description: t.description }
343
+ };
344
+ }
345
+ function transformOneTrigger(trigger) {
346
+ switch (trigger._tag) {
347
+ case "PrTrigger": return [toLockPr(trigger)];
348
+ case "PushTrigger": return toLockPushAndTag(trigger);
349
+ case "TagTrigger": return [toLockTag(trigger)];
350
+ case "CommentTrigger": return [toLockComment(trigger)];
351
+ case "ReviewTrigger": return [toLockReview(trigger)];
352
+ case "ReviewCommentTrigger": return [toLockReviewComment(trigger)];
353
+ case "ReleaseTrigger": return [toLockRelease(trigger)];
354
+ case "DispatchTrigger": return [toLockDispatch(trigger)];
355
+ case "CreateTrigger": return [toLockCreate(trigger)];
356
+ case "DeleteTrigger": return [toLockDelete(trigger)];
357
+ case "StatusTrigger": return [toLockStatus(trigger)];
358
+ case "WorkflowRunTrigger": return [toLockWorkflowRun(trigger)];
359
+ case "ForkTrigger": return [toLockFork(trigger)];
360
+ case "StarTrigger": return [toLockStar(trigger)];
361
+ case "WatchTrigger": return [toLockWatch(trigger)];
362
+ case "WebhookTrigger": return [toLockWebhook(trigger)];
363
+ case "KiciEventTrigger": return [toLockKiciEvent(trigger)];
364
+ case "WorkflowCompleteTrigger": return [toLockWorkflowComplete(trigger)];
365
+ case "JobCompleteTrigger": return [toLockJobComplete(trigger)];
366
+ case "GenericWebhookTrigger": return [toLockGenericWebhook(trigger)];
367
+ case "ScheduleTrigger": return [toLockSchedule(trigger)];
368
+ case "LifecycleTrigger": return [toLockLifecycle(trigger)];
369
+ }
370
+ }
371
+ /**
372
+ * Transform trigger configs to lock file format.
373
+ * Uses flatMap because push triggers with tags generate two lock triggers.
374
+ * Exported for reuse in the test runner's in-memory lock format conversion.
375
+ */
376
+ function transformTriggers(triggers) {
377
+ if (!triggers) return [];
378
+ return triggers.flatMap(transformOneTrigger);
379
+ }
380
+ /**
381
+ * Transform branch patterns to lock file format.
382
+ */
383
+ function transformBranchPatterns(patterns) {
384
+ return patterns.map((p) => ({
385
+ type: p.type,
386
+ pattern: p.pattern,
387
+ flags: p.flags
388
+ }));
389
+ }
390
+ /** UUID v4 pattern: 8-4-4-4-12 hex chars */
391
+ const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
392
+ /**
393
+ * Transform jobs array (may contain static jobs and dynamic generators).
394
+ * Assigns counter-based IDs to UUID-named jobs (created by id-less job() factory).
395
+ * Counter only increments for unnamed entries; named jobs keep their names.
396
+ *
397
+ * Builds a UUID-to-renamed-name mapping in a pre-pass so that `needs` references
398
+ * to other UUID-named jobs resolve to their lock file names (job-N), not the UUIDs.
399
+ */
400
+ function transformJobs(jobs, configPath, gitRoot) {
401
+ let preCounter = 0;
402
+ const uuidToName = /* @__PURE__ */ new Map();
403
+ for (const jobOrFactory of jobs) {
404
+ if (isDynamicJobFn(jobOrFactory)) continue;
405
+ const j = jobOrFactory;
406
+ if (UUID_PATTERN.test(j.name)) uuidToName.set(j.name, `job-${++preCounter}`);
407
+ }
408
+ let jobCounter = 0;
409
+ return jobs.map((jobOrFactory, index) => {
410
+ if (isDynamicJobFn(jobOrFactory)) {
411
+ const groupName = getDynamicJobGroup(jobOrFactory);
412
+ return {
413
+ _type: "dynamic",
414
+ source: {
415
+ file: configPath,
416
+ index
417
+ },
418
+ ...groupName && { group: groupName }
419
+ };
420
+ }
421
+ const j = jobOrFactory;
422
+ const name = UUID_PATTERN.test(j.name) ? `job-${++jobCounter}` : j.name;
423
+ return transformJob({
424
+ ...j,
425
+ name
426
+ }, configPath, index, gitRoot, uuidToName);
427
+ });
428
+ }
429
+ /**
430
+ * Normalize SDK runsOn into lock file representation.
431
+ * - String passthrough: `'linux'` -> `{ runsOn: 'linux' }`
432
+ * - Array passthrough: `['linux', 'docker']` -> `{ runsOn: ['linux', 'docker'] }`
433
+ * - Object form: `{ labels: ['linux'], exclude: ['gpu'] }` -> `{ runsOn: ['linux'], excludeLabels: ['gpu'] }`
434
+ */
435
+ function normalizeRunsOnForLock(runsOn) {
436
+ if (typeof runsOn === "string") return { runsOn };
437
+ if (Array.isArray(runsOn)) return { runsOn };
438
+ const labels = Array.isArray(runsOn.labels) ? runsOn.labels : [runsOn.labels];
439
+ const exclude = runsOn.exclude ? Array.isArray(runsOn.exclude) ? runsOn.exclude : [runsOn.exclude] : void 0;
440
+ return {
441
+ runsOn: labels.length === 1 ? labels[0] : labels,
442
+ ...exclude && exclude.length > 0 ? { excludeLabels: exclude } : {}
443
+ };
444
+ }
445
+ /**
446
+ * Validate runsOn labels:
447
+ * 1. Reject kici:* reserved prefix in user-supplied labels.
448
+ * 2. Reject overlap between required and excluded labels.
449
+ */
450
+ function validateRunsOn(runsOn, jobName) {
451
+ const labels = typeof runsOn === "string" ? [runsOn] : Array.isArray(runsOn) ? runsOn : Array.isArray(runsOn.labels) ? runsOn.labels : [runsOn.labels];
452
+ const reservedLabels = labels.filter((l) => l.toLowerCase().startsWith("kici:"));
453
+ if (reservedLabels.length > 0) throw new Error(`Job "${jobName}": labels with 'kici:' prefix are reserved and cannot be used in runsOn: ${reservedLabels.join(", ")}`);
454
+ if (typeof runsOn === "string" || Array.isArray(runsOn)) return;
455
+ const exclude = runsOn.exclude ? Array.isArray(runsOn.exclude) ? runsOn.exclude : [runsOn.exclude] : [];
456
+ const overlap = labels.filter((l) => exclude.includes(l));
457
+ if (overlap.length > 0) throw new Error(`Job "${jobName}": labels and exclude overlap on [${overlap.join(", ")}]. A label cannot be both required and excluded.`);
458
+ }
459
+ /**
460
+ * Transform a static job to lock file format.
461
+ */
462
+ function transformJob(job, configPath, index, gitRoot, uuidToName) {
463
+ validateRunsOn(job.runsOn, job.name);
464
+ const environmentFields = {};
465
+ if (job.environment !== void 0) {
466
+ if (typeof job.environment === "function") {
467
+ const fnSource = job.environment.toString();
468
+ const purity = analyzePurity(fnSource);
469
+ environmentFields.dynamicEnvironment = true;
470
+ if (purity.pure) environmentFields.environment = {
471
+ _type: "inline",
472
+ expression: fnSource
473
+ };
474
+ else console.warn(`[kici] Job "${job.name}": environment function is not pure (${purity.reason}). An init job will be required, adding ~5-10s delay.`);
475
+ } else if (typeof job.environment === "string") environmentFields.environment = job.environment;
476
+ }
477
+ const envFields = {};
478
+ if (job.env !== void 0) {
479
+ if (typeof job.env === "function") {
480
+ const fnSource = job.env.toString();
481
+ const purity = analyzePurity(fnSource);
482
+ envFields.dynamicEnv = true;
483
+ if (purity.pure) envFields.env = {
484
+ _type: "inline",
485
+ expression: fnSource
486
+ };
487
+ else console.warn(`[kici] Job "${job.name}": env function is not pure (${purity.reason}). An init job will be required, adding ~5-10s delay.`);
488
+ } else if (typeof job.env === "object") envFields.env = { ...job.env };
489
+ }
490
+ if (job.resources !== void 0) try {
491
+ validateResourceRequest(job.resources);
492
+ } catch (err) {
493
+ const reason = err instanceof Error ? err.message : String(err);
494
+ throw new Error(`Job "${job.name}": invalid resources -- ${reason}`);
495
+ }
496
+ const concurrencyFields = {};
497
+ if (job.concurrencyGroup !== void 0) {
498
+ if (typeof job.concurrencyGroup === "function") {
499
+ const fnSource = job.concurrencyGroup.toString();
500
+ const purity = analyzePurity(fnSource);
501
+ concurrencyFields.dynamicConcurrencyGroup = true;
502
+ if (purity.pure) concurrencyFields.concurrencyGroup = {
503
+ _type: "inline",
504
+ expression: fnSource
505
+ };
506
+ else console.warn(`[kici] Job "${job.name}": concurrencyGroup function is not pure (${purity.reason}). An init job will be required, adding ~5-10s delay.`);
507
+ } else if (typeof job.concurrencyGroup === "string") concurrencyFields.concurrencyGroup = job.concurrencyGroup;
508
+ }
509
+ return {
510
+ _type: "static",
511
+ name: job.name,
512
+ ...normalizeRunsOnForLock(job.runsOn),
513
+ ...resolveNeedsForLock(job.needs, uuidToName),
514
+ steps: transformSteps(job.steps, gitRoot),
515
+ matrix: job.matrix ? transformMatrix(job.matrix, job.name, configPath) : void 0,
516
+ include: job.include?.map((inc) => ({ ...inc })),
517
+ exclude: job.exclude?.map((exc) => ({ ...exc })),
518
+ rules: job.rules ? transformRules(job.rules, configPath, index) : void 0,
519
+ description: job.description,
520
+ ...job.checkout !== void 0 && { checkout: job.checkout },
521
+ ...job.container !== void 0 && { container: job.container },
522
+ ...environmentFields,
523
+ ...envFields,
524
+ ...concurrencyFields,
525
+ ...job.onCancel !== void 0 && { hasOnCancel: true },
526
+ ...job.cleanup !== void 0 && { hasCleanup: true },
527
+ ...job.onSuccess !== void 0 && { hasOnSuccess: true },
528
+ ...job.onFailure !== void 0 && { hasOnFailure: true },
529
+ ...job.beforeStep !== void 0 && { hasBeforeStep: true },
530
+ ...job.afterStep !== void 0 && { hasAfterStep: true },
531
+ ...job.gracePeriod !== void 0 && { gracePeriod: job.gracePeriod },
532
+ ...job.resources !== void 0 && { resources: job.resources }
533
+ };
534
+ }
535
+ /**
536
+ * Resolve needs to lock file format.
537
+ * Handles strings, Job objects, DynamicGroupRef, and object forms with ifFailed policy.
538
+ * Uses the UUID-to-renamed-name mapping so that references to id-less jobs
539
+ * resolve to their lock file names (job-N) instead of the original UUIDs.
540
+ */
541
+ function resolveNeedsForLock(needs, uuidToName) {
542
+ if (!needs) return { needs: [] };
543
+ const resolvedNeeds = [];
544
+ const groups = [];
545
+ for (const need of needs) if (typeof need === "string") resolvedNeeds.push(uuidToName?.get(need) ?? need);
546
+ else if (isDynamicGroupRef(need)) {
547
+ resolvedNeeds.push({
548
+ group: need.group,
549
+ ifFailed: need.ifFailed ?? "skip"
550
+ });
551
+ groups.push(need.group);
552
+ } else if ("group" in need && typeof need.group === "string") {
553
+ const g = need;
554
+ resolvedNeeds.push({
555
+ group: g.group,
556
+ ifFailed: g.ifFailed ?? "skip"
557
+ });
558
+ groups.push(g.group);
559
+ } else if ("ifFailed" in need && "name" in need) {
560
+ const n = need;
561
+ const name = uuidToName?.get(n.name) ?? n.name;
562
+ resolvedNeeds.push({
563
+ name,
564
+ ifFailed: n.ifFailed
565
+ });
566
+ } else {
567
+ const name = need.name;
568
+ resolvedNeeds.push(uuidToName?.get(name) ?? name);
569
+ }
570
+ return {
571
+ needs: resolvedNeeds,
572
+ ...groups.length > 0 && { dependsOnGroups: groups }
573
+ };
574
+ }
575
+ /**
576
+ * Transform steps to lock file format.
577
+ * Assigns counter-based IDs to unnamed steps (bare functions and id-less steps).
578
+ * Counter only increments for unnamed entries; named steps keep their names.
579
+ */
580
+ function transformSteps(steps, gitRoot) {
581
+ let stepCounter = 0;
582
+ return steps.map((stepOrFn) => {
583
+ if (typeof stepOrFn === "function") {
584
+ stepCounter++;
585
+ return {
586
+ name: `step-${stepCounter}`,
587
+ hasOutputs: false
588
+ };
589
+ }
590
+ const step = stepOrFn;
591
+ return {
592
+ name: step.name || `step-${++stepCounter}`,
593
+ hasOutputs: !!step.outputs && Object.keys(step.outputs).length > 0,
594
+ ...step.continueOnError !== void 0 && { continueOnError: step.continueOnError },
595
+ ...step.timeout !== void 0 && { timeout: step.timeout },
596
+ ...step._sourceLocation && { sourceLocation: {
597
+ file: makeRelativePath(step._sourceLocation.file, gitRoot),
598
+ line: step._sourceLocation.line,
599
+ column: step._sourceLocation.column
600
+ } },
601
+ ...step.rules && step.rules.length > 0 && {
602
+ hasRules: true,
603
+ rules: transformRules(step.rules, makeRelativePath(step._sourceLocation?.file ?? "", gitRoot))
604
+ },
605
+ ...step.onCancel !== void 0 && { hasOnCancel: true },
606
+ ...step.cleanup !== void 0 && { hasCleanup: true }
607
+ };
608
+ });
609
+ }
610
+ /**
611
+ * Strip the `?t=...` query suffix added by cache-busting `import()` calls.
612
+ * The cache-buster is a `Date.now()` query parameter used to defeat Node's
613
+ * module cache. It must not leak into the lock file — it poisons sourceLocation
614
+ * determinism and the top-level contentHash. Handles both `.compiled.mjs?t=...`
615
+ * (rolldown compile path) and `.ts?t=...` (direct TS import path).
616
+ */
617
+ function stripCompiledSuffix(filePath) {
618
+ return filePath.replace(/(?:\.compiled\.mjs)?\?t=\d+$/, "");
619
+ }
620
+ /**
621
+ * Convert an absolute file path to a git-root-relative path.
622
+ * If already relative, returns as-is. Normalizes backslashes to forward slashes.
623
+ * Also strips ephemeral `.compiled.mjs?t=...` suffixes from source locations.
624
+ */
625
+ function makeRelativePath(filePath, gitRoot) {
626
+ const cleaned = stripCompiledSuffix(filePath);
627
+ if (!path.isAbsolute(cleaned)) return cleaned.replaceAll("\\", "/");
628
+ return path.relative(gitRoot, cleaned).replaceAll("\\", "/");
629
+ }
630
+ /**
631
+ * Transform matrix configuration to lock file format.
632
+ */
633
+ function transformMatrix(matrix, jobName, configPath) {
634
+ if (isDynamicFunction(matrix)) return {
635
+ _type: "dynamic",
636
+ source: {
637
+ file: configPath,
638
+ jobName
639
+ }
640
+ };
641
+ if (isStaticArray(matrix)) return {
642
+ _type: "static",
643
+ values: [...matrix]
644
+ };
645
+ if (isStaticObject(matrix)) {
646
+ const values = {};
647
+ for (const [key, arr] of Object.entries(matrix)) values[key] = [...arr];
648
+ return {
649
+ _type: "static",
650
+ values
651
+ };
652
+ }
653
+ return {
654
+ _type: "dynamic",
655
+ source: {
656
+ file: configPath,
657
+ jobName
658
+ }
659
+ };
660
+ }
661
+ /**
662
+ * Transform rules to lock file format.
663
+ * Rules contain functions and can't be serialized - store references.
664
+ */
665
+ function transformRules(rules, configPath, parentIndex) {
666
+ return rules.map((rule, index) => ({
667
+ _type: "dynamic",
668
+ label: rule.label,
669
+ source: {
670
+ file: configPath,
671
+ index: parentIndex !== void 0 ? parentIndex * 100 + index : index
672
+ }
673
+ }));
674
+ }
675
+ /**
676
+ * Serialize lock file to JSON string.
677
+ *
678
+ * @param lockFile - Lock file object
679
+ * @param pretty - Whether to pretty-print (default: true)
680
+ * @returns JSON string
681
+ */
682
+ function serializeLockFile(lockFile, pretty = true) {
683
+ return JSON.stringify(lockFile, null, pretty ? 2 : void 0);
684
+ }
685
+ //#endregion
686
+ export { computeLockfileHash, detectGitRoot, generateLockFile, serializeLockFile, transformTriggers };
687
+
688
+ //# sourceMappingURL=generator.js.map
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Resolve hashFiles patterns (paths and globs) relative to git root and build a deterministic
3
+ * digest string: sorted resolved paths, each followed by "\n" and file content.
4
+ * Used as input to the content hash so workflow cache invalidates when these files change.
5
+ *
6
+ * @param gitRoot - Absolute path to git repository root
7
+ * @param patterns - Paths or glob patterns relative to repo root (e.g. ["config.json", "scripts/*.sh"])
8
+ * @returns Object with digest string and list of resolved paths (relative to git root), or null if no patterns or resolution fails
9
+ */
10
+ export declare function resolveHashFiles(gitRoot: string, patterns: string[]): {
11
+ assetDigest: string;
12
+ resolvedPaths: string[];
13
+ } | null;
14
+ //# sourceMappingURL=hash-files.d.ts.map