@orkestrel/scaffold 0.0.7 → 0.0.9

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.
@@ -846,6 +846,16 @@ function isRealDirectory(path) {
846
846
  return status.success && status.value;
847
847
  }
848
848
  /**
849
+ * Whether a path is an existing physical file rather than a directory or link.
850
+ *
851
+ * @param path - The filesystem path to inspect without following links.
852
+ * @returns `true` only for a successful `lstat` reporting a single-link file.
853
+ */
854
+ function isRealFile(path) {
855
+ const status = (0, _orkestrel_contract.attempt)(() => (0, node_fs.lstatSync)(path));
856
+ return status.success && status.value.isFile() && !status.value.isSymbolicLink() && status.value.nlink === 1;
857
+ }
858
+ /**
849
859
  * Compute a bounded-memory SHA-256 digest for one file.
850
860
  *
851
861
  * @param path - The file to read.
@@ -1734,16 +1744,16 @@ function selectOrkestrelEntries(value) {
1734
1744
  * and the name satisfies `isWorkspaceName`; every other name is a coded
1735
1745
  * `TARGET` failure. `src` is derived from `src/<environment>/` and `app` from
1736
1746
  * `app/<environment>/`; a target with no environment on either axis is also a coded
1737
- * `TARGET` failure. `dependencies` /
1747
+ * `TARGET` failure. The `bin`, `integration`, and `service` facts probe physical
1748
+ * directories at `src/bin`, `tests/integration`, and `tests/service`, respectively,
1749
+ * never the workspace name. `dependencies` /
1738
1750
  * `peers` are the `@orkestrel/`-prefixed entries of `manifest.dependencies` /
1739
1751
  * `manifest.peerDependencies` (a peer flagged `peerDependenciesMeta[name]
1740
1752
  * .optional === true` carries `optional: true`). `extras` is EVERY entry of
1741
1753
  * `manifest.devDependencies` (not only `@orkestrel/`-prefixed ones — an
1742
- * external extra like `zod` must round-trip too), EXCLUDING the generated
1743
- * devDependency baseline (`devDependenciesFor([])`'s keys, which already
1744
- * cover `@orkestrel/guide` and `@orkestrel/scaffold`) every scaffolded
1745
- * package already carries, never a package-specific extra. A devDependency
1746
- * ALSO present in
1754
+ * external extra like `zod` must round-trip too), EXCLUDING the complete
1755
+ * generated dependency set `devDependenciesFor` emits for the reconstructed
1756
+ * blueprint's actual environments and structural axes. A devDependency ALSO present in
1747
1757
  * `manifest.peerDependencies` or `manifest.dependencies` (e.g. a peer
1748
1758
  * dev-installed for local testing) is likewise excluded from `extras` — it
1749
1759
  * already appears as a `peer`/`dependency` above, and double-counting it as
@@ -1754,6 +1764,8 @@ function selectOrkestrelEntries(value) {
1754
1764
  * @throws `ScaffoldError('TARGET', …)` when `target`'s manifest is unreadable
1755
1765
  * (via `readManifest`), is not valid JSON, its name is unsafe for its
1756
1766
  * publication mode, or `target` carries no source or application environment.
1767
+ * @throws `ScaffoldError('TARGET', …)` when `tests/service` exists without the
1768
+ * physical companion files `tests/setupService.ts` and `scripts/service.sh`.
1757
1769
  *
1758
1770
  * @example
1759
1771
  * ```ts
@@ -1763,11 +1775,6 @@ function selectOrkestrelEntries(value) {
1763
1775
  * ```
1764
1776
  */
1765
1777
  function deriveBlueprint(target) {
1766
- const baselineExtras = /* @__PURE__ */ new Set([
1767
- ...Object.keys((0, _src_core.devDependenciesFor)([])),
1768
- "@orkestrel/guide",
1769
- "@orkestrel/scaffold"
1770
- ]);
1771
1778
  const parsed = (0, _orkestrel_contract.parseJSON)(readManifest(target));
1772
1779
  if (!(0, _orkestrel_contract.isRecord)(parsed)) throw new _src_core.ScaffoldError("TARGET", `Manifest at ${target} is not a valid JSON object`, { target });
1773
1780
  const src = _src_core.ENVIRONMENTS.filter((environment) => isRealDirectory((0, node_path.join)(target, "src", environment)));
@@ -1794,7 +1801,18 @@ function deriveBlueprint(target) {
1794
1801
  const version = typeof rawVersion === "string" ? rawVersion : _src_core.DEFAULT_VERSION;
1795
1802
  const rawNode = (0, _src_core.ownDataValue)((0, _src_core.ownDataValue)(parsed, "engines"), "node");
1796
1803
  const engines = typeof rawNode === "string" ? rawNode : _src_core.DEFAULT_ENGINES;
1797
- const engine = isRealDirectory((0, node_path.join)(target, "src", "bin"));
1804
+ const bin = isRealDirectory((0, node_path.join)(target, "src", "bin"));
1805
+ const integration = isRealDirectory((0, node_path.join)(target, "tests", "integration"));
1806
+ const service = isRealDirectory((0, node_path.join)(target, "tests", "service"));
1807
+ if (service) {
1808
+ const missing = [];
1809
+ if (!isRealFile((0, node_path.join)(target, "tests", "setupService.ts"))) missing.push("tests/setupService.ts");
1810
+ if (!isRealFile((0, node_path.join)(target, _src_core.SERVICE_SCRIPT_PATH))) missing.push(_src_core.SERVICE_SCRIPT_PATH);
1811
+ if (missing.length > 0) throw new _src_core.ScaffoldError("TARGET", `Service tests under ${target} are missing ${missing.join(" and ")}`, {
1812
+ target,
1813
+ missing
1814
+ });
1815
+ }
1798
1816
  const rawDependencies = (0, _src_core.ownDataValue)(parsed, "dependencies");
1799
1817
  const rawPeerDependencies = (0, _src_core.ownDataValue)(parsed, "peerDependencies");
1800
1818
  const dependencies = selectOrkestrelEntries(rawDependencies).map(([depName, range]) => ({
@@ -1815,9 +1833,19 @@ function deriveBlueprint(target) {
1815
1833
  };
1816
1834
  });
1817
1835
  const peerAndDependencyNames = /* @__PURE__ */ new Set([...selectOrkestrelEntries(rawPeerDependencies).map(([depName]) => depName), ...selectOrkestrelEntries(rawDependencies).map(([depName]) => depName)]);
1836
+ const generatedDevDependencies = new Set(Object.keys((0, _src_core.devDependenciesFor)((0, _src_core.blueprint)(name, {
1837
+ src,
1838
+ app,
1839
+ dependencies,
1840
+ peers,
1841
+ extras: [],
1842
+ bin,
1843
+ integration,
1844
+ service
1845
+ }))));
1818
1846
  const rawDevDependencies = (0, _src_core.ownDataValue)(parsed, "devDependencies");
1819
1847
  const devDependencies = (0, _orkestrel_contract.isRecord)(rawDevDependencies) ? rawDevDependencies : {};
1820
- const extras = Object.entries(devDependencies).filter((entry) => typeof entry[1] === "string").filter(([depName]) => !baselineExtras.has(depName) && !peerAndDependencyNames.has(depName)).map(([depName, range]) => ({
1848
+ const extras = Object.entries(devDependencies).filter((entry) => typeof entry[1] === "string").filter(([depName]) => !generatedDevDependencies.has(depName) && !peerAndDependencyNames.has(depName)).map(([depName, range]) => ({
1821
1849
  name: depName,
1822
1850
  range
1823
1851
  }));
@@ -1832,7 +1860,9 @@ function deriveBlueprint(target) {
1832
1860
  version,
1833
1861
  engines,
1834
1862
  overrides: [],
1835
- engine
1863
+ bin,
1864
+ integration,
1865
+ service
1836
1866
  });
1837
1867
  }
1838
1868
  /**
@@ -4089,6 +4119,7 @@ exports.isMaterializerEventHooks = isMaterializerEventHooks;
4089
4119
  exports.isMissingPathError = isMissingPathError;
4090
4120
  exports.isPortablePath = isPortablePath;
4091
4121
  exports.isRealDirectory = isRealDirectory;
4122
+ exports.isRealFile = isRealFile;
4092
4123
  exports.isReservedTargetPath = isReservedTargetPath;
4093
4124
  exports.isSensitiveHostPath = isSensitiveHostPath;
4094
4125
  exports.isSyncEventHooks = isSyncEventHooks;