knitto 0.0.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 (87) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +869 -0
  3. package/dist/src/checks/run.d.ts +3 -0
  4. package/dist/src/checks/run.js +173 -0
  5. package/dist/src/checks/run.js.map +1 -0
  6. package/dist/src/cli.d.ts +2 -0
  7. package/dist/src/cli.js +406 -0
  8. package/dist/src/cli.js.map +1 -0
  9. package/dist/src/config.d.ts +11 -0
  10. package/dist/src/config.js +585 -0
  11. package/dist/src/config.js.map +1 -0
  12. package/dist/src/context/build.d.ts +2 -0
  13. package/dist/src/context/build.js +147 -0
  14. package/dist/src/context/build.js.map +1 -0
  15. package/dist/src/context/render.d.ts +7 -0
  16. package/dist/src/context/render.js +74 -0
  17. package/dist/src/context/render.js.map +1 -0
  18. package/dist/src/engine/apply.d.ts +2 -0
  19. package/dist/src/engine/apply.js +96 -0
  20. package/dist/src/engine/apply.js.map +1 -0
  21. package/dist/src/engine/diff.d.ts +2 -0
  22. package/dist/src/engine/diff.js +100 -0
  23. package/dist/src/engine/diff.js.map +1 -0
  24. package/dist/src/engine/plan.d.ts +2 -0
  25. package/dist/src/engine/plan.js +259 -0
  26. package/dist/src/engine/plan.js.map +1 -0
  27. package/dist/src/errors.d.ts +14 -0
  28. package/dist/src/errors.js +31 -0
  29. package/dist/src/errors.js.map +1 -0
  30. package/dist/src/filesystem/paths.d.ts +2 -0
  31. package/dist/src/filesystem/paths.js +47 -0
  32. package/dist/src/filesystem/paths.js.map +1 -0
  33. package/dist/src/hooks/run.d.ts +10 -0
  34. package/dist/src/hooks/run.js +105 -0
  35. package/dist/src/hooks/run.js.map +1 -0
  36. package/dist/src/index.d.ts +7 -0
  37. package/dist/src/index.js +7 -0
  38. package/dist/src/index.js.map +1 -0
  39. package/dist/src/lock.d.ts +2 -0
  40. package/dist/src/lock.js +16 -0
  41. package/dist/src/lock.js.map +1 -0
  42. package/dist/src/onboarding.d.ts +26 -0
  43. package/dist/src/onboarding.js +212 -0
  44. package/dist/src/onboarding.js.map +1 -0
  45. package/dist/src/parsers/index.d.ts +18 -0
  46. package/dist/src/parsers/index.js +194 -0
  47. package/dist/src/parsers/index.js.map +1 -0
  48. package/dist/src/parsers/json.d.ts +17 -0
  49. package/dist/src/parsers/json.js +214 -0
  50. package/dist/src/parsers/json.js.map +1 -0
  51. package/dist/src/parsers/merge.d.ts +1 -0
  52. package/dist/src/parsers/merge.js +17 -0
  53. package/dist/src/parsers/merge.js.map +1 -0
  54. package/dist/src/project/discover.d.ts +2 -0
  55. package/dist/src/project/discover.js +144 -0
  56. package/dist/src/project/discover.js.map +1 -0
  57. package/dist/src/rules/json-pointer.d.ts +3 -0
  58. package/dist/src/rules/json-pointer.js +114 -0
  59. package/dist/src/rules/json-pointer.js.map +1 -0
  60. package/dist/src/snapshots/canonical.d.ts +11 -0
  61. package/dist/src/snapshots/canonical.js +150 -0
  62. package/dist/src/snapshots/canonical.js.map +1 -0
  63. package/dist/src/sources/process.d.ts +3 -0
  64. package/dist/src/sources/process.js +28 -0
  65. package/dist/src/sources/process.js.map +1 -0
  66. package/dist/src/sources/resolve.d.ts +3 -0
  67. package/dist/src/sources/resolve.js +144 -0
  68. package/dist/src/sources/resolve.js.map +1 -0
  69. package/dist/src/template/init.d.ts +1 -0
  70. package/dist/src/template/init.js +62 -0
  71. package/dist/src/template/init.js.map +1 -0
  72. package/dist/src/template/manifest.d.ts +2 -0
  73. package/dist/src/template/manifest.js +80 -0
  74. package/dist/src/template/manifest.js.map +1 -0
  75. package/dist/src/template/release.d.ts +3 -0
  76. package/dist/src/template/release.js +8 -0
  77. package/dist/src/template/release.js.map +1 -0
  78. package/dist/src/template/validate.d.ts +2 -0
  79. package/dist/src/template/validate.js +62 -0
  80. package/dist/src/template/validate.js.map +1 -0
  81. package/dist/src/types.d.ts +242 -0
  82. package/dist/src/types.js +4 -0
  83. package/dist/src/types.js.map +1 -0
  84. package/dist/src/version.d.ts +2 -0
  85. package/dist/src/version.js +3 -0
  86. package/dist/src/version.js.map +1 -0
  87. package/package.json +51 -0
@@ -0,0 +1,259 @@
1
+ import { createHash } from "node:crypto";
2
+ import path from "node:path";
3
+ import { readFile } from "node:fs/promises";
4
+ import { glob } from "glob";
5
+ import { KnittoError } from "../errors.js";
6
+ import { assertNoEscapingSymlink, resolveInside, } from "../filesystem/paths.js";
7
+ import { buildContext } from "../context/build.js";
8
+ import { renderTemplate, } from "../context/render.js";
9
+ import { prepareContent } from "../parsers/index.js";
10
+ import { discoverProjectUnits } from "../project/discover.js";
11
+ import { runChecks } from "../checks/run.js";
12
+ import { applyContextHooks, assertHooksTrusted, runCheckHooks, runParserHook, } from "../hooks/run.js";
13
+ function digest(contents) {
14
+ return `sha256:${createHash("sha256").update(contents).digest("hex")}`;
15
+ }
16
+ async function readOptional(file) {
17
+ try {
18
+ return await readFile(file, "utf8");
19
+ }
20
+ catch (error) {
21
+ if (typeof error === "object" &&
22
+ error !== null &&
23
+ "code" in error &&
24
+ error.code === "ENOENT") {
25
+ return null;
26
+ }
27
+ throw error;
28
+ }
29
+ }
30
+ async function renderRule(rule, snapshot, context, partials) {
31
+ const templatePath = resolveInside(snapshot.directory, rule.template);
32
+ const template = await readFile(templatePath, "utf8");
33
+ return renderTemplate(template, context, rule.template, partials);
34
+ }
35
+ async function loadPartials(snapshot) {
36
+ const partials = [];
37
+ for (const [name, file] of Object.entries(snapshot.manifest.partials ?? {})) {
38
+ const contents = await readFile(resolveInside(snapshot.directory, file), "utf8");
39
+ partials.push({ name, contents });
40
+ }
41
+ return partials;
42
+ }
43
+ function parserFor(rule) {
44
+ if (rule.type === "file")
45
+ return "text";
46
+ if (rule.type === "json")
47
+ return "json-merge";
48
+ return rule.parser;
49
+ }
50
+ function rulePointers(rule) {
51
+ return rule.type === "json" || rule.type === "content"
52
+ ? rule.pointers
53
+ : undefined;
54
+ }
55
+ function ruleSchema(rule) {
56
+ return rule.type === "json" || rule.type === "content"
57
+ ? rule.schema
58
+ : undefined;
59
+ }
60
+ function structuredRule(rule) {
61
+ if (rule.type === "json")
62
+ return true;
63
+ return rule.type === "content" && rule.parser !== "text";
64
+ }
65
+ function ruleEnabled(rule, context, partials) {
66
+ if (!rule.when)
67
+ return true;
68
+ const rendered = renderTemplate(rule.when, context, `${rule.id}.when`, partials)
69
+ .trim()
70
+ .toLowerCase();
71
+ return rendered !== "" && rendered !== "false" && rendered !== "0";
72
+ }
73
+ function appliesToUnit(rule, isRoot) {
74
+ const scope = rule.scope ?? "root";
75
+ return scope === "all" || (scope === "root" ? isRoot : !isRoot);
76
+ }
77
+ function relativeProjectPath(root, destination) {
78
+ return path.relative(root, destination).split(path.sep).join("/");
79
+ }
80
+ function validateExceptions(config, snapshot) {
81
+ const rules = new Map(snapshot.manifest.rules.map((rule) => [rule.id, rule]));
82
+ for (const ruleId of config.exclude?.rules ?? []) {
83
+ if (!rules.has(ruleId)) {
84
+ throw new KnittoError(`Excluded rule does not exist: ${ruleId}`, "CONFIG");
85
+ }
86
+ }
87
+ for (const [ruleId, pointers] of Object.entries(config.exclude?.pointers ?? {})) {
88
+ const rule = rules.get(ruleId);
89
+ if (!rule || !structuredRule(rule)) {
90
+ throw new KnittoError(`Pointer exclusions require a JSON rule: ${ruleId}`, "CONFIG");
91
+ }
92
+ for (const pointer of pointers) {
93
+ const pointers = rulePointers(rule);
94
+ if (pointers && !pointers.includes(pointer)) {
95
+ throw new KnittoError(`Excluded pointer is not managed by ${ruleId}: ${pointer}`, "CONFIG");
96
+ }
97
+ }
98
+ }
99
+ for (const [ruleId, overrides] of Object.entries(config.overrides ?? {})) {
100
+ const rule = rules.get(ruleId);
101
+ if (!rule || !structuredRule(rule)) {
102
+ throw new KnittoError(`Overrides require a JSON rule: ${ruleId}`, "CONFIG");
103
+ }
104
+ for (const pointer of Object.keys(overrides)) {
105
+ const pointers = rulePointers(rule);
106
+ if (pointers && !pointers.includes(pointer)) {
107
+ throw new KnittoError(`Override pointer is not managed by ${ruleId}: ${pointer}`, "CONFIG");
108
+ }
109
+ }
110
+ }
111
+ }
112
+ export async function createPlan(projectRoot, config, snapshot) {
113
+ const root = path.resolve(projectRoot);
114
+ const partials = await loadPartials(snapshot);
115
+ const units = await discoverProjectUnits(root, config);
116
+ const excludedRules = new Set();
117
+ const excludedPointers = {};
118
+ const operations = [];
119
+ const checks = [];
120
+ const operatedPaths = new Map();
121
+ for (const unit of units) {
122
+ assertHooksTrusted(snapshot, unit);
123
+ validateExceptions(unit.config, snapshot);
124
+ const context = await applyContextHooks(snapshot, unit, await buildContext(unit, snapshot.manifest));
125
+ for (const ruleId of unit.config.exclude?.rules ?? []) {
126
+ excludedRules.add(ruleId);
127
+ }
128
+ for (const [ruleId, pointers] of Object.entries(unit.config.exclude?.pointers ?? {})) {
129
+ excludedPointers[ruleId] = [
130
+ ...new Set([...(excludedPointers[ruleId] ?? []), ...pointers]),
131
+ ];
132
+ }
133
+ for (const rule of snapshot.manifest.rules) {
134
+ if (!appliesToUnit(rule, unit.isRoot))
135
+ continue;
136
+ if (unit.config.exclude?.rules?.includes(rule.id))
137
+ continue;
138
+ if (!ruleEnabled(rule, context, partials))
139
+ continue;
140
+ const renderedDestination = renderTemplate(rule.destination, context, `${rule.id}.destination`, partials);
141
+ const destinationRoot = rule.target === "root" ? root : unit.path;
142
+ if (rule.type === "delete" && rule.glob) {
143
+ const matches = await glob(renderedDestination, {
144
+ cwd: destinationRoot,
145
+ dot: true,
146
+ nodir: true,
147
+ });
148
+ for (const match of matches.sort()) {
149
+ const matchedDestination = resolveInside(destinationRoot, match);
150
+ await assertNoEscapingSymlink(root, matchedDestination);
151
+ const matchedCurrent = await readOptional(matchedDestination);
152
+ if (matchedCurrent !== null) {
153
+ const operationPath = relativeProjectPath(root, matchedDestination);
154
+ operations.push({
155
+ type: "delete",
156
+ ruleId: rule.id,
157
+ path: operationPath,
158
+ before: matchedCurrent,
159
+ beforeDigest: digest(matchedCurrent),
160
+ });
161
+ }
162
+ }
163
+ continue;
164
+ }
165
+ const destination = resolveInside(destinationRoot, renderedDestination);
166
+ await assertNoEscapingSymlink(root, destination);
167
+ const current = await readOptional(destination);
168
+ const operationPath = relativeProjectPath(root, destination);
169
+ const existingRule = operatedPaths.get(operationPath);
170
+ if (existingRule) {
171
+ throw new KnittoError(`Rules ${existingRule} and ${rule.id} both manage ${operationPath}`, "TEMPLATE");
172
+ }
173
+ if (rule.type === "delete") {
174
+ if (current !== null) {
175
+ operatedPaths.set(operationPath, rule.id);
176
+ operations.push({
177
+ type: "delete",
178
+ ruleId: rule.id,
179
+ path: operationPath,
180
+ before: current,
181
+ beforeDigest: digest(current),
182
+ });
183
+ }
184
+ continue;
185
+ }
186
+ const rendered = await renderRule(rule, snapshot, context, partials);
187
+ const pointers = rulePointers(rule);
188
+ const schemaPath = ruleSchema(rule);
189
+ let schema;
190
+ if (schemaPath) {
191
+ const schemaTemplate = await readFile(resolveInside(snapshot.directory, schemaPath), "utf8");
192
+ const renderedSchema = renderTemplate(schemaTemplate, context, schemaPath, partials);
193
+ try {
194
+ schema = JSON.parse(renderedSchema);
195
+ }
196
+ catch (error) {
197
+ throw new KnittoError(`Rendered JSON Schema is invalid: ${schemaPath}`, "TEMPLATE", { cause: error });
198
+ }
199
+ }
200
+ const excluded = unit.config.exclude?.pointers?.[rule.id];
201
+ const overrides = unit.config.overrides?.[rule.id];
202
+ const prepared = rule.type === "content" && rule.parser === "hook"
203
+ ? await runParserHook(snapshot, rule.hook ?? "", {
204
+ rule,
205
+ rendered,
206
+ current,
207
+ schema,
208
+ pointers,
209
+ excludedPointers: excluded ?? [],
210
+ overrides: overrides ?? {},
211
+ context,
212
+ })
213
+ : prepareContent({
214
+ parser: parserFor(rule),
215
+ rendered,
216
+ current,
217
+ ...(schema !== undefined ? { schema } : {}),
218
+ ...((rule.type === "json" || rule.type === "content") &&
219
+ rule.exact
220
+ ? { exactPointers: rule.exact }
221
+ : {}),
222
+ ...(pointers ? { pointers } : {}),
223
+ ...(excluded ? { excludedPointers: excluded } : {}),
224
+ ...(overrides ? { overrides } : {}),
225
+ ...((rule.type === "json" || rule.type === "content") &&
226
+ rule.indent !== undefined
227
+ ? { indent: rule.indent }
228
+ : {}),
229
+ });
230
+ const desired = prepared.contents;
231
+ if (current !== desired) {
232
+ operatedPaths.set(operationPath, rule.id);
233
+ operations.push({
234
+ type: "write",
235
+ ruleId: rule.id,
236
+ path: operationPath,
237
+ before: current,
238
+ after: desired,
239
+ beforeDigest: current === null ? null : digest(current),
240
+ ...(prepared.jsonPatch ? { jsonPatch: prepared.jsonPatch } : {}),
241
+ ...((rule.type === "file" || rule.type === "content") &&
242
+ rule.mode !== undefined
243
+ ? { mode: rule.mode }
244
+ : {}),
245
+ });
246
+ }
247
+ }
248
+ checks.push(...(await runChecks(snapshot.manifest.checks ?? [], unit, context, partials)), ...(await runCheckHooks(snapshot, unit, context)));
249
+ }
250
+ return {
251
+ projectRoot: root,
252
+ templateDigest: snapshot.digest,
253
+ operations,
254
+ checks,
255
+ excludedRules: [...excludedRules],
256
+ excludedPointers,
257
+ };
258
+ }
259
+ //# sourceMappingURL=plan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.js","sourceRoot":"","sources":["../../../src/engine/plan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ export declare class KnittoError extends Error {
2
+ readonly code: "DRIFT" | "CONFIG" | "SOURCE" | "TEMPLATE" | "APPLY" | "USAGE";
3
+ constructor(message: string, code: "DRIFT" | "CONFIG" | "SOURCE" | "TEMPLATE" | "APPLY" | "USAGE", options?: ErrorOptions);
4
+ }
5
+ export declare const EXIT_CODES: {
6
+ readonly success: 0;
7
+ readonly drift: 1;
8
+ readonly config: 2;
9
+ readonly source: 3;
10
+ readonly template: 4;
11
+ readonly apply: 5;
12
+ readonly usage: 64;
13
+ };
14
+ export declare function exitCodeFor(error: unknown): number;
@@ -0,0 +1,31 @@
1
+ export class KnittoError extends Error {
2
+ code;
3
+ constructor(message, code, options) {
4
+ super(message, options);
5
+ this.code = code;
6
+ this.name = "KnittoError";
7
+ }
8
+ }
9
+ export const EXIT_CODES = {
10
+ success: 0,
11
+ drift: 1,
12
+ config: 2,
13
+ source: 3,
14
+ template: 4,
15
+ apply: 5,
16
+ usage: 64,
17
+ };
18
+ export function exitCodeFor(error) {
19
+ if (!(error instanceof KnittoError)) {
20
+ return EXIT_CODES.apply;
21
+ }
22
+ return {
23
+ DRIFT: EXIT_CODES.drift,
24
+ CONFIG: EXIT_CODES.config,
25
+ SOURCE: EXIT_CODES.source,
26
+ TEMPLATE: EXIT_CODES.template,
27
+ APPLY: EXIT_CODES.apply,
28
+ USAGE: EXIT_CODES.usage,
29
+ }[error.code];
30
+ }
31
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,WAAY,SAAQ,KAAK;IAGzB;IAFX,YACE,OAAe,EACN,IAME,EACX,OAAsB;QAEtB,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QATf,SAAI,GAAJ,IAAI,CAMF;QAIX,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;IAC5B,CAAC;CACF;AAED,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,OAAO,EAAE,CAAC;IACV,KAAK,EAAE,CAAC;IACR,MAAM,EAAE,CAAC;IACT,MAAM,EAAE,CAAC;IACT,QAAQ,EAAE,CAAC;IACX,KAAK,EAAE,CAAC;IACR,KAAK,EAAE,EAAE;CACD,CAAC;AAEX,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,IAAI,CAAC,CAAC,KAAK,YAAY,WAAW,CAAC,EAAE,CAAC;QACpC,OAAO,UAAU,CAAC,KAAK,CAAC;IAC1B,CAAC;IAED,OAAO;QACL,KAAK,EAAE,UAAU,CAAC,KAAK;QACvB,MAAM,EAAE,UAAU,CAAC,MAAM;QACzB,MAAM,EAAE,UAAU,CAAC,MAAM;QACzB,QAAQ,EAAE,UAAU,CAAC,QAAQ;QAC7B,KAAK,EAAE,UAAU,CAAC,KAAK;QACvB,KAAK,EAAE,UAAU,CAAC,KAAK;KACxB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function resolveInside(root: string, relativePath: string): string;
2
+ export declare function assertNoEscapingSymlink(root: string, target: string): Promise<void>;
@@ -0,0 +1,47 @@
1
+ import path from "node:path";
2
+ import { lstat, realpath } from "node:fs/promises";
3
+ import { KnittoError } from "../errors.js";
4
+ export function resolveInside(root, relativePath) {
5
+ if (path.isAbsolute(relativePath)) {
6
+ throw new KnittoError(`Absolute paths are not allowed: ${relativePath}`, "TEMPLATE");
7
+ }
8
+ const resolvedRoot = path.resolve(root);
9
+ const resolved = path.resolve(resolvedRoot, relativePath);
10
+ const relative = path.relative(resolvedRoot, resolved);
11
+ if (relative.startsWith("..") || path.isAbsolute(relative)) {
12
+ throw new KnittoError(`Path escapes the selected root: ${relativePath}`, "TEMPLATE");
13
+ }
14
+ return resolved;
15
+ }
16
+ export async function assertNoEscapingSymlink(root, target) {
17
+ const resolvedRoot = await realpath(root);
18
+ let current = path.resolve(target);
19
+ while (current.startsWith(path.resolve(root))) {
20
+ try {
21
+ const stat = await lstat(current);
22
+ if (stat.isSymbolicLink()) {
23
+ const destination = await realpath(current);
24
+ const relative = path.relative(resolvedRoot, destination);
25
+ if (relative.startsWith("..") || path.isAbsolute(relative)) {
26
+ throw new KnittoError(`Symlink escapes the selected root: ${target}`, "TEMPLATE");
27
+ }
28
+ }
29
+ }
30
+ catch (error) {
31
+ if (typeof error === "object" &&
32
+ error !== null &&
33
+ "code" in error &&
34
+ error.code === "ENOENT") {
35
+ // Missing destination components are safe until created.
36
+ }
37
+ else {
38
+ throw error;
39
+ }
40
+ }
41
+ if (current === path.resolve(root)) {
42
+ break;
43
+ }
44
+ current = path.dirname(current);
45
+ }
46
+ }
47
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../../src/filesystem/paths.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,YAAoB;IAC9D,IAAI,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,WAAW,CACnB,mCAAmC,YAAY,EAAE,EACjD,UAAU,CACX,CAAC;IACJ,CAAC;IAED,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;IAC1D,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;IAEvD,IAAI,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3D,MAAM,IAAI,WAAW,CACnB,mCAAmC,YAAY,EAAE,EACjD,UAAU,CACX,CAAC;IACJ,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,IAAY,EACZ,MAAc;IAEd,MAAM,YAAY,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC1C,IAAI,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAEnC,OAAO,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;QAC9C,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,CAAC;YAClC,IAAI,IAAI,CAAC,cAAc,EAAE,EAAE,CAAC;gBAC1B,MAAM,WAAW,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,CAAC;gBAC5C,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;gBAC1D,IAAI,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;oBAC3D,MAAM,IAAI,WAAW,CACnB,sCAAsC,MAAM,EAAE,EAC9C,UAAU,CACX,CAAC;gBACJ,CAAC;YACH,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IACE,OAAO,KAAK,KAAK,QAAQ;gBACzB,KAAK,KAAK,IAAI;gBACd,MAAM,IAAI,KAAK;gBACf,KAAK,CAAC,IAAI,KAAK,QAAQ,EACvB,CAAC;gBACD,yDAAyD;YAC3D,CAAC;iBAAM,CAAC;gBACN,MAAM,KAAK,CAAC;YACd,CAAC;QACH,CAAC;QAED,IAAI,OAAO,KAAK,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YACnC,MAAM;QACR,CAAC;QACD,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAClC,CAAC;AACH,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { CheckResult, JsonPatchOperation, ProjectUnit, RenderContext, Snapshot } from "../types.js";
2
+ interface ParserHookResult {
3
+ contents: string;
4
+ jsonPatch?: JsonPatchOperation[];
5
+ }
6
+ export declare function assertHooksTrusted(snapshot: Snapshot, unit: ProjectUnit): void;
7
+ export declare function applyContextHooks(snapshot: Snapshot, unit: ProjectUnit, context: RenderContext): Promise<RenderContext>;
8
+ export declare function runParserHook(snapshot: Snapshot, hookId: string, input: unknown): Promise<ParserHookResult>;
9
+ export declare function runCheckHooks(snapshot: Snapshot, unit: ProjectUnit, context: RenderContext): Promise<CheckResult[]>;
10
+ export {};
@@ -0,0 +1,105 @@
1
+ import { spawn } from "node:child_process";
2
+ import { KnittoError } from "../errors.js";
3
+ import { resolveInside } from "../filesystem/paths.js";
4
+ async function executeHook(snapshot, hook, input) {
5
+ const command = resolveInside(snapshot.directory, hook.command);
6
+ return new Promise((resolve, reject) => {
7
+ const child = spawn(command, hook.args ?? [], {
8
+ cwd: snapshot.directory,
9
+ env: {
10
+ PATH: process.env.PATH,
11
+ HOME: process.env.HOME,
12
+ TMPDIR: process.env.TMPDIR,
13
+ },
14
+ stdio: ["pipe", "pipe", "pipe"],
15
+ });
16
+ const stdout = [];
17
+ const stderr = [];
18
+ child.stdout.on("data", (chunk) => stdout.push(chunk));
19
+ child.stderr.on("data", (chunk) => stderr.push(chunk));
20
+ child.on("error", (error) => reject(new KnittoError(`Unable to execute hook ${hook.id}`, "TEMPLATE", {
21
+ cause: error,
22
+ })));
23
+ child.on("close", (code) => {
24
+ if (code !== 0) {
25
+ reject(new KnittoError(`Hook ${hook.id} failed with exit code ${code}: ${Buffer.concat(stderr).toString("utf8").trim()}`, "TEMPLATE"));
26
+ return;
27
+ }
28
+ try {
29
+ resolve(JSON.parse(Buffer.concat(stdout).toString("utf8")));
30
+ }
31
+ catch (error) {
32
+ reject(new KnittoError(`Hook ${hook.id} did not return valid JSON`, "TEMPLATE", { cause: error }));
33
+ }
34
+ });
35
+ child.stdin.end(JSON.stringify(input));
36
+ });
37
+ }
38
+ export function assertHooksTrusted(snapshot, unit) {
39
+ if ((snapshot.manifest.hooks?.length ?? 0) > 0 &&
40
+ !unit.config.trust?.hooks?.includes(snapshot.digest)) {
41
+ throw new KnittoError(`Template ${snapshot.digest} contains executable hooks and is not trusted`, "CONFIG");
42
+ }
43
+ }
44
+ export async function applyContextHooks(snapshot, unit, context) {
45
+ let derived = context.derived;
46
+ for (const hook of snapshot.manifest.hooks ?? []) {
47
+ if (hook.kind !== "context")
48
+ continue;
49
+ const result = await executeHook(snapshot, hook, { unit, context });
50
+ if (typeof result !== "object" || result === null || Array.isArray(result)) {
51
+ throw new KnittoError(`Context hook ${hook.id} must return an object`, "TEMPLATE");
52
+ }
53
+ derived = { ...derived, ...result };
54
+ }
55
+ return { ...context, derived };
56
+ }
57
+ export async function runParserHook(snapshot, hookId, input) {
58
+ const hook = snapshot.manifest.hooks?.find((candidate) => candidate.id === hookId && candidate.kind === "parser");
59
+ if (!hook) {
60
+ throw new KnittoError(`Parser hook does not exist: ${hookId}`, "TEMPLATE");
61
+ }
62
+ const result = await executeHook(snapshot, hook, input);
63
+ if (typeof result !== "object" ||
64
+ result === null ||
65
+ !("contents" in result) ||
66
+ typeof result.contents !== "string") {
67
+ throw new KnittoError(`Parser hook ${hookId} must return a contents string`, "TEMPLATE");
68
+ }
69
+ return {
70
+ contents: result.contents,
71
+ ...("jsonPatch" in result && Array.isArray(result.jsonPatch)
72
+ ? { jsonPatch: result.jsonPatch }
73
+ : {}),
74
+ };
75
+ }
76
+ export async function runCheckHooks(snapshot, unit, context) {
77
+ const results = [];
78
+ for (const hook of snapshot.manifest.hooks ?? []) {
79
+ if (hook.kind !== "check")
80
+ continue;
81
+ const result = await executeHook(snapshot, hook, { unit, context });
82
+ if (!Array.isArray(result)) {
83
+ throw new KnittoError(`Check hook ${hook.id} must return an array`, "TEMPLATE");
84
+ }
85
+ for (const entry of result) {
86
+ if (typeof entry !== "object" ||
87
+ entry === null ||
88
+ typeof entry.title !== "string" ||
89
+ !Array.isArray(entry.body)) {
90
+ throw new KnittoError(`Check hook ${hook.id} returned an invalid result`, "TEMPLATE");
91
+ }
92
+ results.push({
93
+ id: hook.id,
94
+ project: unit.relativePath,
95
+ title: entry.title,
96
+ body: entry.body.map(String),
97
+ ...("solution" in entry && typeof entry.solution === "string"
98
+ ? { solution: entry.solution }
99
+ : {}),
100
+ });
101
+ }
102
+ }
103
+ return results;
104
+ }
105
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../../../src/hooks/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAevD,KAAK,UAAU,WAAW,CACxB,QAAkB,EAClB,IAAkB,EAClB,KAAc;IAEd,MAAM,OAAO,GAAG,aAAa,CAAC,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IAChE,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,IAAI,EAAE,EAAE;YAC5C,GAAG,EAAE,QAAQ,CAAC,SAAS;YACvB,GAAG,EAAE;gBACH,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,IAAI;gBACtB,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,IAAI;gBACtB,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,MAAM;aAC3B;YACD,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;SAChC,CAAC,CAAC;QACH,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QAC/D,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QAC/D,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAC1B,MAAM,CACJ,IAAI,WAAW,CAAC,0BAA0B,IAAI,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE;YAC/D,KAAK,EAAE,KAAK;SACb,CAAC,CACH,CACF,CAAC;QACF,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;YACzB,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;gBACf,MAAM,CACJ,IAAI,WAAW,CACb,QAAQ,IAAI,CAAC,EAAE,0BAA0B,IAAI,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,EACjG,UAAU,CACX,CACF,CAAC;gBACF,OAAO;YACT,CAAC;YACD,IAAI,CAAC;gBACH,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAY,CAAC,CAAC;YACzE,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,CACJ,IAAI,WAAW,CACb,QAAQ,IAAI,CAAC,EAAE,4BAA4B,EAC3C,UAAU,EACV,EAAE,KAAK,EAAE,KAAK,EAAE,CACjB,CACF,CAAC;YACJ,CAAC;QACH,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,QAAkB,EAAE,IAAiB;IACtE,IACE,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC;QAC1C,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,EACpD,CAAC;QACD,MAAM,IAAI,WAAW,CACnB,YAAY,QAAQ,CAAC,MAAM,+CAA+C,EAC1E,QAAQ,CACT,CAAC;IACJ,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,QAAkB,EAClB,IAAiB,EACjB,OAAsB;IAEtB,IAAI,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAC9B,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,QAAQ,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC;QACjD,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;YAAE,SAAS;QACtC,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;QACpE,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3E,MAAM,IAAI,WAAW,CACnB,gBAAgB,IAAI,CAAC,EAAE,wBAAwB,EAC/C,UAAU,CACX,CAAC;QACJ,CAAC;QACD,OAAO,GAAG,EAAE,GAAG,OAAO,EAAE,GAAI,MAAkC,EAAE,CAAC;IACnE,CAAC;IACD,OAAO,EAAE,GAAG,OAAO,EAAE,OAAO,EAAE,CAAC;AACjC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,QAAkB,EAClB,MAAc,EACd,KAAc;IAEd,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CACxC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE,KAAK,MAAM,IAAI,SAAS,CAAC,IAAI,KAAK,QAAQ,CACtE,CAAC;IACF,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,WAAW,CAAC,+BAA+B,MAAM,EAAE,EAAE,UAAU,CAAC,CAAC;IAC7E,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,QAAQ,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;IACxD,IACE,OAAO,MAAM,KAAK,QAAQ;QAC1B,MAAM,KAAK,IAAI;QACf,CAAC,CAAC,UAAU,IAAI,MAAM,CAAC;QACvB,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ,EACnC,CAAC;QACD,MAAM,IAAI,WAAW,CACnB,eAAe,MAAM,gCAAgC,EACrD,UAAU,CACX,CAAC;IACJ,CAAC;IACD,OAAO;QACL,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,GAAG,CAAC,WAAW,IAAI,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC;YAC1D,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,SAAiC,EAAE;YACzD,CAAC,CAAC,EAAE,CAAC;KACR,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,QAAkB,EAClB,IAAiB,EACjB,OAAsB;IAEtB,MAAM,OAAO,GAAkB,EAAE,CAAC;IAClC,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,QAAQ,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC;QACjD,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO;YAAE,SAAS;QACpC,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;QACpE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3B,MAAM,IAAI,WAAW,CACnB,cAAc,IAAI,CAAC,EAAE,uBAAuB,EAC5C,UAAU,CACX,CAAC;QACJ,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,IACE,OAAO,KAAK,KAAK,QAAQ;gBACzB,KAAK,KAAK,IAAI;gBACd,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ;gBAC/B,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,EAC1B,CAAC;gBACD,MAAM,IAAI,WAAW,CACnB,cAAc,IAAI,CAAC,EAAE,6BAA6B,EAClD,UAAU,CACX,CAAC;YACJ,CAAC;YACD,OAAO,CAAC,IAAI,CAAC;gBACX,EAAE,EAAE,IAAI,CAAC,EAAE;gBACX,OAAO,EAAE,IAAI,CAAC,YAAY;gBAC1B,KAAK,EAAE,KAAK,CAAC,KAAK;gBAClB,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC;gBAC5B,GAAG,CAAC,UAAU,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,QAAQ,KAAK,QAAQ;oBAC3D,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE;oBAC9B,CAAC,CAAC,EAAE,CAAC;aACR,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,7 @@
1
+ export { applyPlan } from "./engine/apply.js";
2
+ export { createPlan } from "./engine/plan.js";
3
+ export { formatPlan } from "./engine/diff.js";
4
+ export { resolveCurrentSnapshot, resolveLockedSnapshot, } from "./sources/resolve.js";
5
+ export { createLock } from "./lock.js";
6
+ export { prepareJson } from "./parsers/json.js";
7
+ export type { ProjectConfig, ProjectLock, ReconciliationPlan, Snapshot, SourceConfig, TemplateManifest, TemplateRule, } from "./types.js";
@@ -0,0 +1,7 @@
1
+ export { applyPlan } from "./engine/apply.js";
2
+ export { createPlan } from "./engine/plan.js";
3
+ export { formatPlan } from "./engine/diff.js";
4
+ export { resolveCurrentSnapshot, resolveLockedSnapshot, } from "./sources/resolve.js";
5
+ export { createLock } from "./lock.js";
6
+ export { prepareJson } from "./parsers/json.js";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EACL,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { ProjectLock, Snapshot, SourceConfig } from "./types.js";
2
+ export declare function createLock(source: SourceConfig, snapshot: Snapshot): ProjectLock;
@@ -0,0 +1,16 @@
1
+ import { KNITTO_PACKAGE, KNITTO_VERSION } from "./version.js";
2
+ export function createLock(source, snapshot) {
3
+ return {
4
+ schemaVersion: 1,
5
+ digest: snapshot.digest,
6
+ source,
7
+ engine: {
8
+ package: KNITTO_PACKAGE,
9
+ version: KNITTO_VERSION,
10
+ },
11
+ provenance: snapshot.provenance,
12
+ templateSchemaVersion: snapshot.manifest.schemaVersion,
13
+ resolvedAt: new Date().toISOString(),
14
+ };
15
+ }
16
+ //# sourceMappingURL=lock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lock.js","sourceRoot":"","sources":["../../src/lock.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9D,MAAM,UAAU,UAAU,CACxB,MAAoB,EACpB,QAAkB;IAElB,OAAO;QACL,aAAa,EAAE,CAAC;QAChB,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,MAAM;QACN,MAAM,EAAE;YACN,OAAO,EAAE,cAAc;YACvB,OAAO,EAAE,cAAc;SACxB;QACD,UAAU,EAAE,QAAQ,CAAC,UAAU;QAC/B,qBAAqB,EAAE,QAAQ,CAAC,QAAQ,CAAC,aAAa;QACtD,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACrC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,26 @@
1
+ import type { ProjectConfig, TemplateManifest, TemplatePrompt } from "./types.js";
2
+ export declare function parsePromptAssignments(values: string[]): Record<string, string>;
3
+ export declare function describeTemplateInputs(root: string, config: ProjectConfig, manifest: TemplateManifest): {
4
+ inputs: Array<{
5
+ path: string;
6
+ type: TemplatePrompt["type"];
7
+ message: string;
8
+ description?: string;
9
+ required: boolean;
10
+ configured: boolean;
11
+ willPrompt: boolean;
12
+ value?: unknown;
13
+ default?: string;
14
+ choices?: Array<{
15
+ value: string;
16
+ label?: string;
17
+ }>;
18
+ }>;
19
+ willPrompt: boolean;
20
+ missingRequired: string[];
21
+ };
22
+ export declare function assertCiTemplateInputsConfigured(root: string, config: ProjectConfig, manifest: TemplateManifest, supplied: Record<string, string>, ci: boolean): void;
23
+ export declare function resolveTemplateInputs(root: string, originalConfig: ProjectConfig, manifest: TemplateManifest, supplied: Record<string, string>): Promise<{
24
+ config: ProjectConfig;
25
+ changed: boolean;
26
+ }>;