@xemahq/repo-build-tooling 0.10.0 → 0.10.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xemahq/repo-build-tooling",
3
- "version": "0.10.0",
3
+ "version": "0.10.1",
4
4
  "description": "Dev-time build tooling shared by every Xema repository. Ships as plain ESM with zero dependencies so the published artifact is the reviewed source.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Neuralchowder Inc. <developer@xema.dev> (https://xema.dev)",
@@ -42,6 +42,6 @@
42
42
  "test": "node src/scrub-swagger-plugin-paths.mjs --self-test && node --test src/check-workspace-range-matches-local.test.mjs && node --test src/check-no-vendored-deps.test.mjs && node --test src/readiness.test.mjs && node --test src/composed-image.test.mjs && node --test src/ghcr-manifest.test.mjs && node --test src/declaration-refusal.test.mjs && node --test src/smoke-refusal.test.mjs && node --test src/load-image.test.mjs && node --test src/composed-image-coordinate.test.mjs && node --test src/composed-image-dockerfile.test.mjs && node --test src/registry-fetch.test.mjs && node --test src/service-bootstrap.test.mjs && node --test src/service-bootstrap-protocols.test.mjs"
43
43
  },
44
44
  "xemaRelease": {
45
- "sourceDigest": "sha256:525d85f0cc251fa763bc18261159ae689b59c3465d9da0d3c9f6037445d68713"
45
+ "sourceDigest": "sha256:35b4aafd2345b2d892fff707b22790985f6ae1e20f75d0f7467f3401f8b583d2"
46
46
  }
47
47
  }
@@ -167,12 +167,15 @@ const FLAT_LEAK_REGEX = new RegExp(
167
167
  // a relative path from the service source file back up to the workspace root
168
168
  // and down into the contracts package. There is no `node_modules/` segment
169
169
  // so shapes 1 and 2 above never fire.
170
- // Capture group 1: package directory name (= npm name without @xemahq/ scope).
170
+ // Capture group 1: relative path from the scrubbed file to the package directory.
171
171
  // Capture group 2: first path segment after dist/ = the subpath export name.
172
+ // The public package name is NOT derived from the directory name or a fixed
173
+ // scope: it is read from the target package's own package.json `name`, so
174
+ // unscoped and differently-named workspace packages resolve correctly.
172
175
  // Scoped to paths that pass through `packages/`, `biomes/`, or `kernel/` to
173
176
  // avoid false-positive matches on intra-service relative requires.
174
177
  const RELATIVE_WORKSPACE_REGEX = new RegExp(
175
- `require\\("(?:\\.\\./)+(?:packages|biomes|kernel)/(?:[a-z0-9][a-z0-9.-]*/)*([a-z0-9][a-z0-9.-]*)/dist/([a-z0-9][a-z0-9-]*)(?:/[^"]*)?"\\)`,
178
+ `require\\("((?:\\.\\./)+(?:packages|biomes|kernel)/(?:[a-z0-9][a-z0-9.-]*/)*[a-z0-9][a-z0-9.-]*)/dist/([a-z0-9][a-z0-9-]*)(?:/[^"]*)?"\\)`,
176
179
  'g',
177
180
  );
178
181
 
@@ -416,6 +419,28 @@ function publicSpecifierFor(resolveFrom, pkg, subpath, original) {
416
419
  );
417
420
  }
418
421
 
422
+ /**
423
+ * The npm name of the workspace package at `packageDir`, read from the
424
+ * package.json AT that directory. No walk-up: an ancestor's manifest (usually
425
+ * the workspace root) names a different package. Never a fixed scope or the
426
+ * directory name.
427
+ */
428
+ function packageNameAt(packageDir, original) {
429
+ const manifest = join(packageDir, 'package.json');
430
+ let raw;
431
+ try {
432
+ raw = readFileSync(manifest, 'utf8');
433
+ } catch (err) {
434
+ if (err.code !== 'ENOENT' && err.code !== 'ENOTDIR') throw err;
435
+ throw new ScrubFailure(`${packageDir} has no package.json, so no specifier can be derived for\n ${original}`);
436
+ }
437
+ const { name } = JSON.parse(raw);
438
+ if (typeof name !== 'string' || name === '') {
439
+ throw new ScrubFailure(`${manifest} has no "name", so no specifier can be derived for\n ${original}`);
440
+ }
441
+ return name;
442
+ }
443
+
419
444
  async function* walk(dir) {
420
445
  let entries;
421
446
  try {
@@ -561,9 +586,10 @@ async function scrubFile(path) {
561
586
  ` rather than reaching through dist/.`,
562
587
  );
563
588
  }
564
- out = out.replace(RELATIVE_WORKSPACE_REGEX, (_match, pkgName, subpath) => {
589
+ out = out.replace(RELATIVE_WORKSPACE_REGEX, (match, relPackageDir, subpath) => {
565
590
  count += 1;
566
- return `require("@xemahq/${pkgName}/${subpath}")`;
591
+ const name = packageNameAt(resolve(dirname(path), relPackageDir), match);
592
+ return `require("${name}/${subpath}")`;
567
593
  });
568
594
  if (count > 0) {
569
595
  await writeFile(path, out, 'utf8');
@@ -796,6 +822,59 @@ async function selfTest() {
796
822
  throw new Error(`self-test: rewritten file still leaks: ${greenAfter}`);
797
823
  }
798
824
 
825
+ // 4b. RELATIVE-WORKSPACE — the rewritten specifier is the target package's
826
+ // own manifest `name`: scoped, unscoped, and name != directory.
827
+ const ws = join(root, 'ws');
828
+ const pkgs = [
829
+ ['packages/contracts/scoped-dir', '@xemahq/scoped-dir'],
830
+ ['packages/contracts/plain-dir', 'plain-contract'],
831
+ ['packages/contracts/other-dir', '@xemahq/renamed'],
832
+ ];
833
+ for (const [dir, name] of pkgs) {
834
+ await mkdir(join(ws, dir, 'dist/lib'), { recursive: true });
835
+ await writeFile(join(ws, dir, 'package.json'), JSON.stringify({ name }), 'utf8');
836
+ }
837
+ await mkdir(join(ws, 'services/svc/dist'), { recursive: true });
838
+ const wsFile = join(ws, 'services/svc/dist/dto.js');
839
+ const relOf = (dir) => `../../../${dir}/dist/lib/x`;
840
+ await writeFile(
841
+ wsFile,
842
+ `class Dto {\n static _OPENAPI_METADATA_FACTORY() {\n return { ${pkgs
843
+ .map(([dir], i) => `k${i}: () => require("${relOf(dir)}")`)
844
+ .join(', ')} };\n }\n}\n`,
845
+ 'utf8',
846
+ );
847
+ await run([join(ws, 'services/svc/dist')]);
848
+ const wsAfter = await readFile(wsFile, 'utf8');
849
+ for (const expected of ['require("@xemahq/scoped-dir/lib")', 'require("plain-contract/lib")', 'require("@xemahq/renamed/lib")']) {
850
+ if (!wsAfter.includes(expected)) {
851
+ throw new Error(`self-test: relative-workspace rewrite missing ${expected}: ${wsAfter}`);
852
+ }
853
+ }
854
+ // A target directory without its own package.json is a hard failure.
855
+ const orphan = join(root, 'orphan');
856
+ await mkdir(join(orphan, 'svc/dist'), { recursive: true });
857
+ await writeFile(
858
+ join(orphan, 'svc/dist/dto.js'),
859
+ 'class D {\n static _OPENAPI_METADATA_FACTORY() {\n return { a: () => require("../../packages/none/dist/lib/x") };\n }\n}\n',
860
+ 'utf8',
861
+ );
862
+ await expectFailure('relative workspace target without package.json', () => run([join(orphan, 'svc/dist')]));
863
+
864
+ // An ANCESTOR's package.json must not stand in for the target's own.
865
+ const walkup = join(root, 'walkup');
866
+ await mkdir(join(walkup, 'packages/none/dist/lib'), { recursive: true });
867
+ await mkdir(join(walkup, 'svc/dist'), { recursive: true });
868
+ await writeFile(join(walkup, 'package.json'), JSON.stringify({ name: 'workspace-root' }), 'utf8');
869
+ await writeFile(
870
+ join(walkup, 'svc/dist/dto.js'),
871
+ 'class D {\n static _OPENAPI_METADATA_FACTORY() {\n return { a: () => require("../../packages/none/dist/lib/x") };\n }\n}\n',
872
+ 'utf8',
873
+ );
874
+ await expectFailure('relative workspace target without package.json but with an ancestor that has one', () =>
875
+ run([join(walkup, 'svc/dist')]),
876
+ );
877
+
799
878
  // 5. End-to-end RED — a DOUBLY-nested literal. One `String.replace` pass
800
879
  // does not re-scan its own replacement, so the rewrite leaves a literal
801
880
  // that is still a leak. This is the case the old script shipped