@ykdz/template 0.0.31 → 0.0.33

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 (70) hide show
  1. package/node_modules/@ykdz/template-builtin-presets/dist/src/foundation.d.ts +10 -1
  2. package/node_modules/@ykdz/template-builtin-presets/dist/src/foundation.js +320 -161
  3. package/node_modules/@ykdz/template-builtin-presets/dist/src/rust-bin/definition.js +66 -9
  4. package/node_modules/@ykdz/template-builtin-presets/dist/src/shared/development-container.d.ts +2 -0
  5. package/node_modules/@ykdz/template-builtin-presets/dist/src/shared/development-container.js +35 -0
  6. package/node_modules/@ykdz/template-builtin-presets/dist/src/shared/typescript.d.ts +13 -0
  7. package/node_modules/@ykdz/template-builtin-presets/dist/src/shared/typescript.js +70 -0
  8. package/node_modules/@ykdz/template-builtin-presets/dist/src/shared/vue.d.ts +2 -1
  9. package/node_modules/@ykdz/template-builtin-presets/dist/src/shared/vue.js +22 -7
  10. package/node_modules/@ykdz/template-builtin-presets/dist/src/ts-cli/definition.js +4 -3
  11. package/node_modules/@ykdz/template-builtin-presets/dist/src/ts-lib/definition.js +4 -3
  12. package/node_modules/@ykdz/template-builtin-presets/dist/src/vike-app/definition.js +82 -11
  13. package/node_modules/@ykdz/template-builtin-presets/dist/src/vue-app/definition.js +9 -2
  14. package/node_modules/@ykdz/template-builtin-presets/dist/src/vue-hono-app/definition.js +27 -10
  15. package/node_modules/@ykdz/template-builtin-presets/package.json +1 -1
  16. package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check-deployment-diagnostics.yml +81 -0
  17. package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check-deployment.yml +55 -0
  18. package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check-diagnostics.yml +64 -0
  19. package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check.yml +24 -5
  20. package/node_modules/@ykdz/template-builtin-presets/templates/foundation/devcontainer.json +3 -3
  21. package/node_modules/@ykdz/template-builtin-presets/templates/foundation/gitignore +1 -0
  22. package/node_modules/@ykdz/template-builtin-presets/templates/foundation/turbo.json +1 -0
  23. package/node_modules/@ykdz/template-builtin-presets/templates/foundation/typescript-config/base.json +15 -0
  24. package/node_modules/@ykdz/template-builtin-presets/templates/foundation/typescript-config/turbo.json +4 -0
  25. package/node_modules/@ykdz/template-builtin-presets/templates/rust-bin/devcontainer/rust.Dockerfile +3 -3
  26. package/node_modules/@ykdz/template-builtin-presets/templates/shared/devcontainer/browser-test.Dockerfile +1 -1
  27. package/node_modules/@ykdz/template-builtin-presets/templates/shared/oxc/tsconfig.config.json +2 -12
  28. package/node_modules/@ykdz/template-builtin-presets/templates/shared/vue/tsconfig.app.json +6 -13
  29. package/node_modules/@ykdz/template-builtin-presets/templates/shared/vue/tsconfig.node.json +2 -12
  30. package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/tsconfig.json +2 -6
  31. package/node_modules/@ykdz/template-builtin-presets/templates/ts-lib/tsconfig.json +3 -3
  32. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db/tsconfig.json +2 -11
  33. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db-migrations/tsconfig.json +2 -11
  34. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/devcontainer/docker-client.Dockerfile +3 -0
  35. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/playwright.config.ts +1 -0
  36. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/scripts/check-standalone-deployment.ts +67 -27
  37. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/tsconfig.app.json +6 -12
  38. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/tsconfig.node.json +2 -11
  39. package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/tsconfig.test.json +0 -1
  40. package/node_modules/@ykdz/template-builtin-presets/templates/vue-app/playwright.config.ts +2 -1
  41. package/node_modules/@ykdz/template-builtin-presets/templates/vue-hono-app/api/tsconfig.json +2 -12
  42. package/node_modules/@ykdz/template-builtin-presets/templates/vue-hono-app/web/playwright.config.ts +2 -0
  43. package/node_modules/@ykdz/template-core/dist/ci-diagnostic-artifact.d.ts +19 -0
  44. package/node_modules/@ykdz/template-core/dist/ci-diagnostic-artifact.js +68 -0
  45. package/node_modules/@ykdz/template-core/dist/development-container-tool-layer.d.ts +56 -0
  46. package/node_modules/@ykdz/template-core/dist/development-container-tool-layer.js +361 -0
  47. package/node_modules/@ykdz/template-core/dist/package-contribution.d.ts +15 -1
  48. package/node_modules/@ykdz/template-core/dist/package-contribution.js +7 -0
  49. package/node_modules/@ykdz/template-core/dist/pnpm-workspace.yaml +1 -0
  50. package/node_modules/@ykdz/template-core/dist/project-github.d.ts +51 -15
  51. package/node_modules/@ykdz/template-core/dist/project-github.js +83 -96
  52. package/node_modules/@ykdz/template-core/dist/project-projection.d.ts +9 -0
  53. package/node_modules/@ykdz/template-core/dist/project-projection.js +84 -5
  54. package/node_modules/@ykdz/template-core/dist/renderer.d.ts +1 -0
  55. package/node_modules/@ykdz/template-core/dist/renderer.js +47 -7
  56. package/node_modules/@ykdz/template-core/dist/template-boundary-check.d.ts +15 -1
  57. package/node_modules/@ykdz/template-core/dist/template-boundary-check.js +183 -1
  58. package/package.json +1 -1
  59. package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check-playwright.yml +0 -20
  60. package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check-rust.yml +0 -23
  61. package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check.dynamic.template +0 -19
  62. package/node_modules/@ykdz/template-builtin-presets/templates/foundation/rust/.vscode/extensions.json +0 -3
  63. package/node_modules/@ykdz/template-builtin-presets/templates/foundation/rust/.vscode/settings.json +0 -5
  64. package/node_modules/@ykdz/template-builtin-presets/templates/foundation/rust/devcontainer/devcontainer.json +0 -21
  65. package/node_modules/@ykdz/template-builtin-presets/templates/foundation/rust/devcontainer/rust.Dockerfile +0 -18
  66. package/node_modules/@ykdz/template-builtin-presets/templates/foundation/rust/rust-toolchain.toml +0 -3
  67. package/node_modules/@ykdz/template-builtin-presets/templates/rust-bin/devcontainer/devcontainer.json +0 -21
  68. package/node_modules/@ykdz/template-core/dist/devcontainer.d.ts +0 -83
  69. package/node_modules/@ykdz/template-core/dist/devcontainer.js +0 -217
  70. /package/node_modules/@ykdz/template-builtin-presets/templates/{shared → vike-app}/devcontainer/shellcheck.Dockerfile +0 -0
@@ -0,0 +1,361 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { readFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { resolveTemplateSource, } from "./renderer.js";
5
+ function hasContinuation(line, escapeCharacter) {
6
+ const trimmed = line.trimEnd();
7
+ let escapeCount = 0;
8
+ for (let index = trimmed.length - 1; index >= 0 && trimmed[index] === escapeCharacter; index -= 1) {
9
+ escapeCount += 1;
10
+ }
11
+ return escapeCount % 2 === 1;
12
+ }
13
+ function parseLogicalDockerfileInstructions(text, sourceFile) {
14
+ const instructions = [];
15
+ let escapeCharacter = "\\";
16
+ let logicalLine = "";
17
+ for (const physicalLine of text.split(/\r?\n/u)) {
18
+ const trimmed = physicalLine.trim();
19
+ if (logicalLine.length === 0 && trimmed.startsWith("#")) {
20
+ const escapeDirective = trimmed.match(/^#\s*escape\s*=\s*([\\`])\s*$/iu);
21
+ if (escapeDirective?.[1] !== undefined) {
22
+ escapeCharacter = escapeDirective[1];
23
+ }
24
+ continue;
25
+ }
26
+ if (trimmed.length === 0 || trimmed.startsWith("#")) {
27
+ continue;
28
+ }
29
+ const continuation = hasContinuation(physicalLine, escapeCharacter);
30
+ const physicalContent = continuation
31
+ ? physicalLine.trimEnd().slice(0, -1)
32
+ : physicalLine;
33
+ logicalLine += physicalContent.trimStart();
34
+ if (continuation) {
35
+ continue;
36
+ }
37
+ const match = logicalLine.match(/^([A-Za-z]+)(?:[ \t]+(.*))?$/u);
38
+ if (match?.[1] === undefined) {
39
+ throw new Error(`Development Container Dockerfile fragment ${sourceFile} contains an invalid logical instruction: ${logicalLine}`);
40
+ }
41
+ instructions.push({
42
+ name: match[1].toUpperCase(),
43
+ arguments: match[2] ?? "",
44
+ });
45
+ logicalLine = "";
46
+ }
47
+ if (logicalLine.length > 0) {
48
+ throw new Error(`Development Container Dockerfile fragment ${sourceFile} ends with an unterminated line continuation.`);
49
+ }
50
+ return instructions;
51
+ }
52
+ function instructionNames(instructions, instruction) {
53
+ return new Set(instructions
54
+ .filter(({ name }) => name === instruction)
55
+ .map(({ arguments: value }) => value.trim().split(/[\s=]/u)[0])
56
+ .filter((name) => name.length > 0));
57
+ }
58
+ function dockerfileWords(value) {
59
+ const words = [];
60
+ let word = "";
61
+ let quote;
62
+ let escaped = false;
63
+ for (const character of value) {
64
+ if (escaped) {
65
+ word += character;
66
+ escaped = false;
67
+ continue;
68
+ }
69
+ if (character === "\\" && quote !== "'") {
70
+ escaped = true;
71
+ continue;
72
+ }
73
+ if (quote !== undefined) {
74
+ if (character === quote) {
75
+ quote = undefined;
76
+ }
77
+ else {
78
+ word += character;
79
+ }
80
+ continue;
81
+ }
82
+ if (character === '"' || character === "'") {
83
+ quote = character;
84
+ continue;
85
+ }
86
+ if (/\s/u.test(character)) {
87
+ if (word.length > 0) {
88
+ words.push(word);
89
+ word = "";
90
+ }
91
+ continue;
92
+ }
93
+ word += character;
94
+ }
95
+ if (escaped) {
96
+ word += "\\";
97
+ }
98
+ if (word.length > 0) {
99
+ words.push(word);
100
+ }
101
+ return words;
102
+ }
103
+ function environmentNames(instructions) {
104
+ const names = new Set();
105
+ for (const instruction of instructions) {
106
+ if (instruction.name !== "ENV") {
107
+ continue;
108
+ }
109
+ const words = dockerfileWords(instruction.arguments);
110
+ const first = words[0];
111
+ if (first === undefined) {
112
+ continue;
113
+ }
114
+ if (!first.includes("=")) {
115
+ names.add(first);
116
+ continue;
117
+ }
118
+ for (const assignment of words) {
119
+ const separator = assignment.indexOf("=");
120
+ if (separator > 0) {
121
+ names.add(assignment.slice(0, separator));
122
+ }
123
+ }
124
+ }
125
+ return names;
126
+ }
127
+ function validateDockerfileFragment(fragment) {
128
+ if (!fragment.from.endsWith(".Dockerfile")) {
129
+ throw new Error(`Development Container Tool Layer ${fragment.identity} must reference checked .Dockerfile template source; received ${fragment.from}.`);
130
+ }
131
+ const instructions = parseLogicalDockerfileInstructions(fragment.text, fragment.from);
132
+ const allowedInstructions = fragment.kind === "base"
133
+ ? new Set(["ARG", "ENV", "FROM", "RUN", "SHELL"])
134
+ : new Set(["ARG", "ENV", "RUN"]);
135
+ for (const instruction of instructions) {
136
+ if (!allowedInstructions.has(instruction.name)) {
137
+ const role = fragment.kind === "base" ? "base" : "optional";
138
+ const allowed = fragment.kind === "base"
139
+ ? "ARG, ENV, FROM, RUN, and SHELL"
140
+ : "ARG, ENV, and RUN";
141
+ throw new Error(`Development Container Tool Layer ${fragment.identity} ${role} fragment ${fragment.from} must not use Dockerfile instruction ${instruction.name}; allowed instructions are ${allowed}.`);
142
+ }
143
+ }
144
+ if (fragment.kind === "base") {
145
+ const fromCount = instructions.filter((instruction) => instruction.name === "FROM").length;
146
+ if (fromCount !== 1) {
147
+ throw new Error(`Development Container Tool Layer ${fragment.identity} base fragment ${fragment.from} must contain exactly one FROM instruction; found ${fromCount}.`);
148
+ }
149
+ }
150
+ return instructions;
151
+ }
152
+ export function validateDevelopmentContainerDockerfileFragments(options) {
153
+ validateDockerfileFragment({ ...options.baseFragment, kind: "base" });
154
+ for (const fragment of options.fragments ?? []) {
155
+ validateDockerfileFragment({ ...fragment, kind: "optional" });
156
+ }
157
+ }
158
+ function compareText(left, right) {
159
+ return left < right ? -1 : left > right ? 1 : 0;
160
+ }
161
+ function assertDockerfileTemplateSource(layer) {
162
+ if (!layer.dockerfile.from.endsWith(".Dockerfile")) {
163
+ throw new Error(`Development Container Tool Layer ${layer.identity} must reference checked .Dockerfile template source; received ${layer.dockerfile.from}.`);
164
+ }
165
+ }
166
+ function normalizeLayer(layer) {
167
+ assertDockerfileTemplateSource(layer);
168
+ const buildArgumentNames = new Set();
169
+ for (const argument of layer.buildArguments ?? []) {
170
+ if (buildArgumentNames.has(argument.name)) {
171
+ throw new Error(`Development Container Tool Layer ${layer.identity} declares build argument ${argument.name} more than once.`);
172
+ }
173
+ buildArgumentNames.add(argument.name);
174
+ }
175
+ return {
176
+ ...layer,
177
+ requires: [...new Set(layer.requires ?? [])].toSorted(compareText),
178
+ buildArguments: [...(layer.buildArguments ?? [])].toSorted((left, right) => compareText(left.name, right.name) ||
179
+ compareText(left.value, right.value)),
180
+ mounts: [...(layer.mounts ?? [])]
181
+ .map((mount) => ({
182
+ ...mount,
183
+ target: path.posix.normalize(mount.target),
184
+ }))
185
+ .toSorted((left, right) => compareText(left.identity, right.identity) ||
186
+ compareText(left.target, right.target)),
187
+ probes: [...(layer.probes ?? [])]
188
+ .map((probe) => ({
189
+ ...probe,
190
+ args: [...(probe.args ?? [])],
191
+ }))
192
+ .toSorted((left, right) => compareText(left.identity, right.identity) ||
193
+ compareText(left.command, right.command) ||
194
+ compareText(JSON.stringify(left.args), JSON.stringify(right.args))),
195
+ };
196
+ }
197
+ function layerFingerprint(layer, kind) {
198
+ return JSON.stringify({
199
+ kind,
200
+ identity: layer.identity,
201
+ dockerfile: resolveTemplateSource(layer.dockerfile.source, layer.dockerfile.from),
202
+ requires: layer.requires,
203
+ buildArguments: layer.buildArguments,
204
+ mounts: layer.mounts,
205
+ probes: layer.probes,
206
+ });
207
+ }
208
+ function findDependencyCycle(layers) {
209
+ const byIdentity = new Map(layers.map((layer) => [layer.identity, layer]));
210
+ const complete = new Set();
211
+ const active = [];
212
+ const activeSet = new Set();
213
+ const visit = (identity) => {
214
+ if (activeSet.has(identity)) {
215
+ return active.slice(active.indexOf(identity)).toSorted(compareText);
216
+ }
217
+ if (complete.has(identity)) {
218
+ return undefined;
219
+ }
220
+ active.push(identity);
221
+ activeSet.add(identity);
222
+ const layer = byIdentity.get(identity);
223
+ for (const dependency of layer?.requires ?? []) {
224
+ if (!byIdentity.has(dependency)) {
225
+ continue;
226
+ }
227
+ const cycle = visit(dependency);
228
+ if (cycle !== undefined) {
229
+ return cycle;
230
+ }
231
+ }
232
+ active.pop();
233
+ activeSet.delete(identity);
234
+ complete.add(identity);
235
+ return undefined;
236
+ };
237
+ for (const identity of [...byIdentity.keys()].toSorted(compareText)) {
238
+ const cycle = visit(identity);
239
+ if (cycle !== undefined) {
240
+ return cycle;
241
+ }
242
+ }
243
+ return undefined;
244
+ }
245
+ function orderLayers(baseLayer, optionalLayers) {
246
+ const normalizedBase = normalizeLayer(baseLayer);
247
+ if ((normalizedBase.requires?.length ?? 0) > 0) {
248
+ throw new Error(`Development Container Tool Layer ${normalizedBase.identity} is the base foundation and must not require other layers.`);
249
+ }
250
+ const base = {
251
+ ...normalizedBase,
252
+ kind: "base",
253
+ };
254
+ const identities = new Map([[base.identity, layerFingerprint(base, "base")]]);
255
+ const uniqueOptionalLayers = [];
256
+ for (const layer of optionalLayers) {
257
+ const normalized = normalizeLayer(layer);
258
+ const fingerprint = layerFingerprint(normalized, "optional");
259
+ const previous = identities.get(normalized.identity);
260
+ if (previous === fingerprint) {
261
+ continue;
262
+ }
263
+ if (previous !== undefined) {
264
+ throw new Error(`Development Container Tool Layer identity ${normalized.identity} has conflicting descriptors.`);
265
+ }
266
+ identities.set(normalized.identity, fingerprint);
267
+ uniqueOptionalLayers.push(normalized);
268
+ }
269
+ for (const layer of uniqueOptionalLayers) {
270
+ for (const requiredIdentity of layer.requires ?? []) {
271
+ if (!identities.has(requiredIdentity)) {
272
+ throw new Error(`Development Container Tool Layer ${layer.identity} requires missing layer ${requiredIdentity}.`);
273
+ }
274
+ }
275
+ }
276
+ const cycle = findDependencyCycle(uniqueOptionalLayers);
277
+ if (cycle !== undefined) {
278
+ throw new Error(`Development Container Tool Layer dependency cycle involves: ${cycle.join(", ")}.`);
279
+ }
280
+ const remaining = uniqueOptionalLayers
281
+ .map((layer) => ({
282
+ ...layer,
283
+ kind: "optional",
284
+ }))
285
+ .toSorted((left, right) => compareText(left.identity, right.identity));
286
+ const ordered = [base];
287
+ const resolved = new Set([base.identity]);
288
+ while (remaining.length > 0) {
289
+ const readyIndex = remaining.findIndex((layer) => (layer.requires ?? []).every((identity) => resolved.has(identity)));
290
+ if (readyIndex === -1) {
291
+ throw new Error("Development Container Tool Layer dependencies cannot be resolved.");
292
+ }
293
+ const [next] = remaining.splice(readyIndex, 1);
294
+ if (next === undefined) {
295
+ throw new Error("Development Container Tool Layer dependency resolution failed.");
296
+ }
297
+ ordered.push(next);
298
+ resolved.add(next.identity);
299
+ }
300
+ return ordered;
301
+ }
302
+ function completeDevelopmentContainerToolLayerPlan(layers, fragmentTexts) {
303
+ const buildArgumentOwners = new Map();
304
+ const environmentOwners = new Map();
305
+ const mountIdentityOwners = new Map();
306
+ const mountTargetOwners = new Map();
307
+ for (const [index, layer] of layers.entries()) {
308
+ for (const mount of layer.mounts ?? []) {
309
+ const previousIdentityOwner = mountIdentityOwners.get(mount.identity);
310
+ if (previousIdentityOwner !== undefined) {
311
+ throw new Error(`Development Container Tool Layer mount identity ${mount.identity} is declared by both ${previousIdentityOwner} and ${layer.identity}.`);
312
+ }
313
+ const previousTargetOwner = mountTargetOwners.get(mount.target);
314
+ if (previousTargetOwner !== undefined) {
315
+ throw new Error(`Development Container Tool Layer mount target ${mount.target} is declared by both ${previousTargetOwner} and ${layer.identity}.`);
316
+ }
317
+ mountIdentityOwners.set(mount.identity, layer.identity);
318
+ mountTargetOwners.set(mount.target, layer.identity);
319
+ }
320
+ const instructions = validateDockerfileFragment({
321
+ identity: layer.identity,
322
+ kind: layer.kind,
323
+ from: layer.dockerfile.from,
324
+ text: fragmentTexts[index],
325
+ });
326
+ const declaredArguments = instructionNames(instructions, "ARG");
327
+ for (const name of declaredArguments) {
328
+ const previousOwner = buildArgumentOwners.get(name);
329
+ if (previousOwner !== undefined) {
330
+ throw new Error(`Development Container Tool Layer build argument ${name} is declared by both ${previousOwner} and ${layer.identity}.`);
331
+ }
332
+ buildArgumentOwners.set(name, layer.identity);
333
+ }
334
+ for (const name of environmentNames(instructions)) {
335
+ const previousOwner = environmentOwners.get(name);
336
+ if (previousOwner !== undefined) {
337
+ throw new Error(`Development Container Tool Layer environment ${name} is declared by both ${previousOwner} and ${layer.identity}.`);
338
+ }
339
+ environmentOwners.set(name, layer.identity);
340
+ }
341
+ for (const argument of layer.buildArguments ?? []) {
342
+ if (!declaredArguments.has(argument.name)) {
343
+ throw new Error(`Development Container Tool Layer ${layer.identity} supplies build argument ${argument.name}, but ${layer.dockerfile.from} does not declare it with ARG.`);
344
+ }
345
+ }
346
+ }
347
+ return {
348
+ layers,
349
+ buildArguments: layers.flatMap((layer) => layer.buildArguments ?? []),
350
+ mounts: layers.flatMap((layer) => layer.mounts ?? []),
351
+ probes: layers.flatMap((layer) => layer.probes ?? []),
352
+ };
353
+ }
354
+ export function planDevelopmentContainerToolLayersSync(options) {
355
+ const layers = orderLayers(options.baseLayer, options.layers ?? []);
356
+ return completeDevelopmentContainerToolLayerPlan(layers, layers.map((layer) => readFileSync(resolveTemplateSource(layer.dockerfile.source, layer.dockerfile.from), "utf8")));
357
+ }
358
+ export async function planDevelopmentContainerToolLayers(options) {
359
+ const layers = orderLayers(options.baseLayer, options.layers ?? []);
360
+ return completeDevelopmentContainerToolLayerPlan(layers, await Promise.all(layers.map((layer) => readFile(resolveTemplateSource(layer.dockerfile.source, layer.dockerfile.from), "utf8"))));
361
+ }
@@ -1,8 +1,10 @@
1
+ import { type CiDiagnosticArtifactDeclaration } from "./ci-diagnostic-artifact.ts";
2
+ import type { DevelopmentContainerToolLayer } from "./development-container-tool-layer.ts";
1
3
  import type { EditorCustomizationCapability } from "./editor-customization.ts";
2
4
  import type { CheckEnvironmentNeed, DeploymentEnvironmentNeed } from "./module-graph.ts";
3
5
  import type { PackageDefinition } from "./project-blueprint-v2.ts";
4
6
  import type { DependencyMaintenancePolicy } from "./project-github.ts";
5
- import type { RenderOperation } from "./renderer.ts";
7
+ import type { RenderOperation, TemplateSourceHandle } from "./renderer.ts";
6
8
  export type FoundationContribution = {
7
9
  /** Toolchains the Foundation must install and project into coordinated root files. */
8
10
  readonly toolchains: {
@@ -19,6 +21,16 @@ export type FoundationContribution = {
19
21
  readonly workspacePackageGlobs?: readonly string[];
20
22
  /** Dependency Catalog entries required by package-owned manifests. */
21
23
  readonly dependencyCatalog?: Readonly<Record<string, string>>;
24
+ /** Source-backed Development Container capabilities coordinated by Foundation. */
25
+ readonly developmentContainerToolLayers?: readonly DevelopmentContainerToolLayer[] | undefined;
26
+ /** Source-backed coordinated files projected by Foundation. */
27
+ readonly templateFiles?: readonly {
28
+ readonly identity: string;
29
+ readonly source: TemplateSourceHandle;
30
+ readonly from: string;
31
+ readonly to: string;
32
+ readonly replacements?: Readonly<Record<string, string>> | undefined;
33
+ }[] | undefined;
22
34
  };
23
35
  /** A preset-agnostic package-sized part of a Generated Repository Plan. */
24
36
  export type PackageContribution = {
@@ -32,6 +44,8 @@ export type PackageContribution = {
32
44
  /** Typed requirements consumed by the Foundation for coordinated root outputs. */
33
45
  readonly foundation: FoundationContribution;
34
46
  readonly environmentNeeds: readonly CheckEnvironmentNeed[];
47
+ /** Closed native CI evidence owned by this Package Boundary. */
48
+ readonly ciDiagnosticArtifacts?: readonly CiDiagnosticArtifactDeclaration[];
35
49
  /** Requirements prepared only by a focused deployment entrypoint. */
36
50
  readonly deploymentEnvironmentNeeds?: readonly DeploymentEnvironmentNeed[];
37
51
  };
@@ -1,3 +1,4 @@
1
+ import { assertCiDiagnosticArtifactDeclaration, } from "./ci-diagnostic-artifact.js";
1
2
  const operationPath = (operation) => {
2
3
  if ("to" in operation)
3
4
  return operation.to;
@@ -20,5 +21,11 @@ export function assertPackageContribution(contribution, provenance = {}) {
20
21
  : `${provenance.definitionName}: ${provenance.planner ?? "Package Contribution"} `;
21
22
  throw new Error(`${owner}${rule}; ${contribution.definition.path} attempted ${target}`);
22
23
  }
24
+ for (const artifact of contribution.ciDiagnosticArtifacts ?? []) {
25
+ const declaration = assertCiDiagnosticArtifactDeclaration(artifact);
26
+ if (declaration.owner.path !== contribution.definition.path) {
27
+ throw new Error(`CI Diagnostic Artifact owner must match its Package Contribution: ${contribution.definition.path}`);
28
+ }
29
+ }
23
30
  return contribution;
24
31
  }
@@ -21,6 +21,7 @@ overrides:
21
21
  "vue>typescript": "-"
22
22
 
23
23
  catalog:
24
+ "@devcontainers/cli": 0.88.0
24
25
  "@hono/node-server": ^2.0.12
25
26
  "@playwright/test": ^1.61.1
26
27
  "@tailwindcss/vite": ^4.3.3
@@ -1,17 +1,52 @@
1
- import { type CheckEnvironmentNeed, type DeploymentEnvironmentNeed } from "./module-graph.ts";
1
+ import { type CiDiagnosticArtifactDeclaration } from "./ci-diagnostic-artifact.ts";
2
+ import type { DeploymentEnvironmentNeed } from "./module-graph.ts";
2
3
  export type CiCapability = {
3
4
  readonly workflowName: "Check";
4
5
  readonly jobName: "check";
6
+ readonly jobDisplayName: "Root Check";
5
7
  readonly runner: "ubuntu-latest";
8
+ readonly timeoutMinutes: 30;
6
9
  };
7
10
  export type CiEnvironmentPreparation = {
8
11
  readonly nodeFromPackageMetadata: boolean;
9
- readonly rustToolchain: boolean;
12
+ readonly packageManagerFromPackageMetadata: boolean;
13
+ readonly pnpmStoreCache: boolean;
10
14
  };
11
15
  export type PnpmTaskLayer = {
12
- readonly installCommand: "pnpm install";
16
+ readonly installCommand: "pnpm install --frozen-lockfile";
13
17
  readonly checkCommand: "pnpm run check";
14
18
  };
19
+ export type CiCheckMatrixEntry = {
20
+ readonly capability: "root" | "deployment";
21
+ readonly jobDisplayName: "Root Check" | "Deployment Check";
22
+ readonly taskEntrypoint: "pnpm run check" | "pnpm run check:deployment";
23
+ readonly timeoutMinutes: 30 | 45;
24
+ readonly requiresDocker: boolean;
25
+ };
26
+ export type ProjectCheckWorkflowPlan = {
27
+ readonly workflowName: "Check";
28
+ readonly triggers: {
29
+ readonly pullRequest: true;
30
+ readonly pushBranches: readonly ["main"];
31
+ };
32
+ readonly permissions: {
33
+ readonly contents: "read";
34
+ };
35
+ readonly concurrency: {
36
+ readonly group: "${{ github.workflow }}-${{ github.ref }}";
37
+ readonly cancelInProgress: true;
38
+ };
39
+ readonly rootCheck: CiCapability;
40
+ readonly matrix: {
41
+ readonly failFast: false;
42
+ readonly include: readonly CiCheckMatrixEntry[];
43
+ } | undefined;
44
+ readonly environmentPreparation: CiEnvironmentPreparation;
45
+ readonly taskLayer: PnpmTaskLayer;
46
+ /** Actual Package Boundaries allowed to own retained native diagnostics. */
47
+ readonly packagePaths: readonly string[];
48
+ readonly diagnosticArtifacts: readonly CiDiagnosticArtifactDeclaration[];
49
+ };
15
50
  export type DependencyEcosystem = "npm" | "cargo" | "github-actions" | "docker" | "rust-toolchain";
16
51
  export type DependabotDirectory = `/${string}`;
17
52
  export type DependencyMaintenancePolicy = {
@@ -20,26 +55,27 @@ export type DependencyMaintenancePolicy = {
20
55
  readonly extraDirectories?: Partial<Record<DependencyEcosystem, readonly DependabotDirectory[]>>;
21
56
  readonly interval: "weekly";
22
57
  };
23
- type ProjectCheckWorkflowOptions = {
24
- readonly environment: {
25
- readonly needs: readonly CheckEnvironmentNeed[];
26
- };
58
+ export type ProjectCheckWorkflowDiagnosticOptions = {
59
+ /** Package Paths from the real Project Blueprint or Package Contributions. */
60
+ readonly packagePaths: readonly string[];
61
+ readonly diagnosticArtifacts?: readonly unknown[] | undefined;
62
+ };
63
+ type ProjectCheckWorkflowOptions = ProjectCheckWorkflowDiagnosticOptions & {
27
64
  readonly deploymentEnvironmentNeeds?: readonly DeploymentEnvironmentNeed[];
28
65
  readonly hasDeploymentTask?: boolean | undefined;
29
66
  readonly capability?: CiCapability | undefined;
30
67
  readonly environmentPreparation?: Partial<CiEnvironmentPreparation> | undefined;
31
68
  readonly taskLayer?: PnpmTaskLayer | undefined;
32
69
  };
33
- export declare function projectCheckWorkflow(options: ProjectCheckWorkflowOptions): string;
34
- /** Limited substitutions for the Foundation-owned workflow Template Source. */
35
- export declare function projectCheckWorkflowTemplateReplacements(options: {
36
- readonly environment: {
37
- readonly needs: readonly CheckEnvironmentNeed[];
38
- };
70
+ /** Capability facts for the Foundation-owned GitHub Actions Template Source. */
71
+ export declare function projectCheckWorkflowPlan(options: ProjectCheckWorkflowOptions): ProjectCheckWorkflowPlan;
72
+ /** Selects a complete Foundation-owned workflow Template Source. */
73
+ export declare function projectCheckWorkflowTemplateSource(options: ProjectCheckWorkflowDiagnosticOptions & {
39
74
  readonly deploymentEnvironmentNeeds?: readonly DeploymentEnvironmentNeed[];
40
75
  readonly hasDeploymentTask?: boolean | undefined;
41
- readonly environmentPreparation?: Partial<CiEnvironmentPreparation>;
42
- }): Record<string, string>;
76
+ }): ".github/workflows/check.yml" | ".github/workflows/check-diagnostics.yml" | ".github/workflows/check-deployment.yml" | ".github/workflows/check-deployment-diagnostics.yml";
77
+ /** Limited, validated facts for static Foundation diagnostic staging. */
78
+ export declare function projectCheckWorkflowTemplateReplacements(options: ProjectCheckWorkflowDiagnosticOptions): Record<string, string>;
43
79
  export declare function projectDependabotConfig(policy: DependencyMaintenancePolicy): string;
44
80
  /** Limited substitution for the Foundation-owned Dependabot Template Source. */
45
81
  export declare function projectDependabotTemplateReplacements(policy: DependencyMaintenancePolicy): Record<string, string>;