@specific.dev/spectest 0.90.2 → 0.91.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/browser-coverage.d.ts +5 -5
  2. package/dist/browser-coverage.js +11 -14
  3. package/dist/components/expo.d.ts +2 -0
  4. package/dist/components/index.d.ts +1 -1
  5. package/dist/components/k3s-trust.d.ts +57 -0
  6. package/dist/components/k3s-trust.js +125 -0
  7. package/dist/components/k3s.d.ts +17 -5
  8. package/dist/components/k3s.js +77 -11
  9. package/dist/coverage.d.ts +1 -1
  10. package/dist/coverage.js +3 -3
  11. package/dist/daemon.js +53 -14
  12. package/dist/harness/component-images.d.ts +31 -0
  13. package/dist/harness/component-images.js +69 -0
  14. package/dist/harness/k3s-image-loader.d.ts +5 -0
  15. package/dist/harness/k3s-image-loader.js +150 -0
  16. package/dist/harness/k3s-prepull.d.ts +7 -0
  17. package/dist/harness/k3s-prepull.js +39 -0
  18. package/dist/harness/sticky-consumer.d.ts +3 -3
  19. package/dist/harness/sticky-consumer.js +19 -4
  20. package/dist/harness/sticky-kubernetes.d.ts +15 -0
  21. package/dist/harness/sticky-kubernetes.js +163 -0
  22. package/dist/harness/sticky-paths.d.ts +5 -0
  23. package/dist/harness/sticky-paths.js +18 -0
  24. package/dist/index.d.ts +32 -2
  25. package/dist/index.js +25 -2
  26. package/package.json +1 -1
  27. package/src/browser-coverage.ts +11 -13
  28. package/src/components/index.ts +1 -0
  29. package/src/components/k3s-trust.test.ts +81 -0
  30. package/src/components/k3s-trust.ts +135 -0
  31. package/src/components/k3s.ts +81 -10
  32. package/src/coverage.test.ts +50 -0
  33. package/src/coverage.ts +3 -3
  34. package/src/daemon.ts +56 -15
  35. package/src/harness/component-images.test.ts +44 -0
  36. package/src/harness/component-images.ts +73 -0
  37. package/src/harness/k3s-image-loader.ts +129 -0
  38. package/src/harness/k3s-prepull.test.ts +47 -0
  39. package/src/harness/k3s-prepull.ts +36 -0
  40. package/src/harness/sticky-consumer.test.ts +28 -0
  41. package/src/harness/sticky-consumer.ts +14 -3
  42. package/src/harness/sticky-kubernetes.test.ts +88 -0
  43. package/src/harness/sticky-kubernetes.ts +165 -0
  44. package/src/harness/sticky-paths.ts +18 -0
  45. package/src/index.ts +48 -3
package/src/index.ts CHANGED
@@ -253,6 +253,8 @@ export interface EnvironmentConfig<S extends ServicesMap = ServicesMap> {
253
253
  export interface ServiceConfig {
254
254
  /** Image definition: registry pull or inline build. */
255
255
  image: ServiceImage;
256
+ /** @internal Images built by the harness and loaded into this component. */
257
+ imageBuilds?: readonly ComponentImageBuild[];
256
258
  /**
257
259
  * Shell command to run (sh -c). Defaults to the image's CMD. NB: this
258
260
  * **replaces the image's entrypoint** with `/bin/sh -c` — an
@@ -1158,10 +1160,13 @@ export interface ServiceTls {
1158
1160
  port: number;
1159
1161
  }
1160
1162
 
1161
- export type ServiceImage =
1163
+ /** @internal Normalized wire representation; legacy definitions remain accepted. */
1164
+ export type NormalizedServiceImage =
1162
1165
  | { type: "registry"; reference: string }
1163
1166
  | {
1164
1167
  type: "dockerfile";
1168
+ /** Optional full local image reference. */
1169
+ reference?: string;
1165
1170
  /**
1166
1171
  * Path of a Dockerfile in your repo, relative to the project root
1167
1172
  * (where `spectest/` lives) — `"Dockerfile"`, `"apps/api/Dockerfile"`.
@@ -1204,6 +1209,8 @@ export type ServiceImage =
1204
1209
  }
1205
1210
  | {
1206
1211
  type: "dockerfile";
1212
+ /** Optional full local image reference. */
1213
+ reference?: string;
1207
1214
  /**
1208
1215
  * Dockerfile contents, built as if the file sat outside the build
1209
1216
  * context (it is never written into your repo). The context is the
@@ -1233,6 +1240,38 @@ export type ServiceImage =
1233
1240
  buildArgs?: Readonly<Record<string, string>>;
1234
1241
  };
1235
1242
 
1243
+ /** Dockerfile build options shared by services and Kubernetes images. */
1244
+ export type BuildImage = (
1245
+ | Omit<Extract<NormalizedServiceImage, { type: "dockerfile"; path: string }>, "type">
1246
+ | Omit<Extract<NormalizedServiceImage, { type: "dockerfile"; content: string }>, "type">
1247
+ ) & {
1248
+ /** Full image reference, e.g. "k8s.internal:5000/runtime:spectest".
1249
+ * Optional for a service; required for a Kubernetes build. This names a
1250
+ * local image and does not publish it to a registry. */
1251
+ reference?: string;
1252
+ };
1253
+
1254
+ /** A registry reference or Dockerfile build. Tagged objects from older SDKs
1255
+ * remain accepted for compatibility; new definitions do not need `type`. */
1256
+ export type ServiceImage = string | BuildImage | NormalizedServiceImage;
1257
+
1258
+ /** @internal Keep the wire format compatible with existing control planes. */
1259
+ export function normalizeServiceImage(image: ServiceImage): NormalizedServiceImage {
1260
+ if (typeof image === "string") return { type: "registry", reference: image };
1261
+ if (!image || typeof image !== "object" || Array.isArray(image)) throw new Error("image must be a registry reference or Dockerfile build");
1262
+ if ("type" in image) {
1263
+ if (image.type !== "registry" && image.type !== "dockerfile") throw new Error("unsupported image type");
1264
+ return image;
1265
+ }
1266
+ return { ...image, type: "dockerfile" };
1267
+ }
1268
+
1269
+ /** @internal Serializes build-only work with the owning component. */
1270
+ export interface ComponentImageBuild {
1271
+ reference: string;
1272
+ image: BuildImage;
1273
+ }
1274
+
1236
1275
  /** A Dockerfile `ARG` name: what `docker build --build-arg` accepts as a key. */
1237
1276
  const BUILD_ARG_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
1238
1277
 
@@ -1274,8 +1313,13 @@ const BUILD_TARGET_RE = /^[^\s]+$/;
1274
1313
  * checks go through the project-file resolver, so the same rules as
1275
1314
  * `ctx.readProjectFile` apply.
1276
1315
  */
1277
- export function validateServiceImage(serviceName: string, image: ServiceImage): ServiceImage {
1316
+ export function validateServiceImage(serviceName: string, input: ServiceImage): NormalizedServiceImage {
1317
+ const image = normalizeServiceImage(input);
1318
+ const reference = typeof input === "string" ? input : "reference" in input ? input.reference : undefined;
1319
+ if (reference !== undefined && (typeof reference !== "string" || !reference || reference.startsWith("-") || /[\s\x00]/.test(reference))) throw new Error(`service "${serviceName}" image reference must be a non-empty image name without whitespace`);
1278
1320
  if (image.type !== "dockerfile") return image;
1321
+ if (reference?.includes("@")) throw new Error(`service "${serviceName}" build reference must be an image name/tag, not a digest`);
1322
+ if (image.exclude !== undefined && (!Array.isArray(image.exclude) || image.exclude.some(e => typeof e !== "string"))) throw new Error(`service "${serviceName}" image exclude must be an array of glob patterns`);
1279
1323
  validateBuildArgs(serviceName, image.buildArgs);
1280
1324
  const hasPath = typeof image.path === "string";
1281
1325
  const hasContent = typeof image.content === "string";
@@ -1286,7 +1330,7 @@ export function validateServiceImage(serviceName: string, image: ServiceImage):
1286
1330
  }
1287
1331
  if (!hasPath && !hasContent) {
1288
1332
  throw new Error(
1289
- `service "${serviceName}" image { type: "dockerfile" } needs \`path\` (a Dockerfile in your repo, relative to the project root) or \`content\``,
1333
+ `service "${serviceName}" image build needs \`path\` (a Dockerfile in your repo, relative to the project root) or \`content\``,
1290
1334
  );
1291
1335
  }
1292
1336
  if (hasPath) {
@@ -2491,6 +2535,7 @@ export function defineEnvironment<
2491
2535
  const { fakes, services: rawServices, ...rest } = input;
2492
2536
  const expanded = expandServiceGroups(rawServices);
2493
2537
  for (const [key, svc] of Object.entries(expanded)) {
2538
+ for (const build of svc.imageBuilds ?? []) validateServiceImage(`${key} image ${build.reference}`, build.image);
2494
2539
  expanded[key] = applyCoverageAdapters(key, {
2495
2540
  ...svc,
2496
2541
  image: validateServiceImage(key, svc.image),