@platforma-sdk/block-tools 2.16.17 → 2.17.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 (35) hide show
  1. package/dist/cli.js +13 -13
  2. package/dist/cli.js.map +1 -1
  3. package/dist/cli.mjs +849 -875
  4. package/dist/cli.mjs.map +1 -1
  5. package/dist/{config-Dx45aFJ_.mjs → config-C07cYptA.mjs} +606 -591
  6. package/dist/config-C07cYptA.mjs.map +1 -0
  7. package/dist/config-on5N57rI.js +3 -0
  8. package/dist/config-on5N57rI.js.map +1 -0
  9. package/dist/index.js +1 -1
  10. package/dist/index.js.map +1 -1
  11. package/dist/index.mjs +4 -5
  12. package/dist/index.mjs.map +1 -1
  13. package/dist/structure/engine/api.d.ts +1 -1
  14. package/dist/structure/engine/content-rules.d.ts +12 -0
  15. package/dist/structure/rules/root-pnpm-workspace.d.ts +1 -1
  16. package/dist/structure/rules/shared/key-order.d.ts +1 -1
  17. package/dist/structure/rules/shared/toolchain.d.ts +18 -0
  18. package/package.json +25 -25
  19. package/src/structure/__tests__/root-catalog-bump.test.ts +1 -1
  20. package/src/structure/__tests__/root-ci-workflows.test.ts +4 -3
  21. package/src/structure/__tests__/toolchain.test.ts +156 -0
  22. package/src/structure/engine/__tests__/content-rules/ensure-catalog-at-least.test.ts +28 -0
  23. package/src/structure/engine/api.ts +4 -0
  24. package/src/structure/engine/content-rules.ts +60 -0
  25. package/src/structure/rules/root-ci.ts +3 -0
  26. package/src/structure/rules/root-package-json.ts +17 -2
  27. package/src/structure/rules/root-pnpm-workspace.ts +23 -5
  28. package/src/structure/rules/shared/key-order.ts +2 -1
  29. package/src/structure/rules/shared/toolchain.ts +35 -0
  30. package/src/structure/templates/text/workflows/build.tpl.yaml +1 -1
  31. package/src/structure/templates/text/workflows/mark-stable.tpl.yaml +1 -1
  32. package/dist/config-CtXMWNgm.js +0 -3
  33. package/dist/config-CtXMWNgm.js.map +0 -1
  34. package/dist/config-Dx45aFJ_.mjs.map +0 -1
  35. package/src/structure/templates/static/root/.gitignore +0 -13
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./config-CtXMWNgm.js`);var t=e.Yt({GlobalOverviewPath:()=>e.r,MetaFile:()=>e.i,PlPackageConfigData:()=>e.c,PlPackageJsonConfigFile:()=>e.l,PlPackageYamlConfigFile:()=>e.u,PlRegCommonConfigData:()=>e.d,PlRegFullPackageConfigData:()=>e.f,PlRegPackageConfig:()=>e.t,PlRegPackageConfigDataShard:()=>e.p,packageContentPrefix:()=>e.a,packageOverviewPath:()=>e.o,payloadFilePath:()=>e.s});exports.BlockDescriptionPackageJsonField=e.mt,exports.BlockPackFolderName=e.ht,exports.BlockRegistryV2=e.w,exports.ChannelNameRegexp=e.T,exports.ChannelsFolder=e.E,exports.FSStorage=e.h,exports.GlobalOverviewFileName=e.D,exports.GlobalOverviewGzFileName=e.O,exports.GlobalOverviewGzPath=e.k,exports.GlobalOverviewPath=e.A,exports.KindImplementer=e.tt,exports.KindManifest=e.nt,exports.KindManifestFile=e.xt,exports.KindManifestFileInfo=e.rt,exports.KindManifestFileName=e.it,exports.KindManifestIdentity=e.at,exports.KindOverview=e.ot,exports.KindOverviewFileName=e.st,exports.KindOverviewPathPattern=e.ct,exports.KindResolutionError=e.b,exports.KindVersionMismatchError=e.X,exports.KindVersionOverview=e.lt,exports.KindsPrefix=e.ut,exports.MainPrefix=e.j,exports.ManifestFileName=e.M,exports.ManifestSuffix=e.N,exports.PackageManifestPattern=e.P,exports.PackageOverview=e.F,exports.PackageOverviewFileName=e.I,exports.PackageOverviewVersionEntry=e.L,exports.PublishedCoordsFileName=e.G,Object.defineProperty(exports,`RegistryV1`,{enumerable:!0,get:function(){return t}}),exports.RegistryV2Reader=e.v,exports.S3Storage=e.g,exports.absoluteToBase64=e.Lt,exports.absoluteToBytes=e.Rt,exports.absoluteToString=e.zt,exports.addRelativePathPrefix=e.wt,exports.blockComponentsManifestToAbsoluteUrl=e.Dt,exports.buildBlockPackDist=e.Ct,exports.buildKindDist=e.St,exports.buildPublishedCoords=e.K,exports.checkKindVersionMatch=e.Z,exports.consolidateBlockComponents=e.Ot,exports.consolidateBlockPackDescription=e.Tt,exports.consolidateBlockPackMeta=e.jt,exports.cpAbsoluteToRelative=e.Bt,exports.embedBlockPackMetaAbsoluteBase64=e.Mt,exports.embedBlockPackMetaAbsoluteBytes=e.Nt,exports.embedBlockPackMetaBytes=e.Pt,exports.folderReaderByUrl=e.m,exports.inferUpdateSuggestions=e.y,exports.kindContentPrefix=e.dt,exports.kindOverviewPath=e.ft,exports.loadPackDescription=e.gt,exports.loadPackDescriptionFromManifest=e._t,exports.loadPackDescriptionRaw=e.vt,exports.mapLocalToAbsolute=e.Vt,exports.npmNameToKindPath=e.pt,exports.packFolderToRelativeTgz=e.Ht,exports.packageChannelPrefix=e.R,exports.packageChannelPrefixInsideV2=e.z,exports.packageContentPrefix=e.B,exports.packageContentPrefixInsideV2=e.V,exports.packageOverviewPath=e.H,exports.packageOverviewPathInsideV2=e.U,exports.parseGlobalOverviewReg=e.W,exports.parsePackageName=e.yt,exports.parseSelector=e.x,exports.publishBlock=e.Y,exports.readFacadeKindDependency=e.Q,exports.readModelCompiledKindRef=e.$,exports.relativeToContentString=e.Ut,exports.relativeToExplicitBinary64=e.Wt,exports.relativeToExplicitBytes=e.Gt,exports.relativeToExplicitString=e.Kt,exports.resolveBlockComponents=e.kt,exports.resolveBlockPackDescription=e.Et,exports.resolveBlockPackMeta=e.Ft,exports.resolveFacadeKind=e.et,exports.resolveKind=e.S,exports.resolveManifestBlockComponents=e.At,exports.resolveManifestBlockPackMeta=e.It,exports.resolveModuleFile=e.qt,exports.resolveModuleFolder=e.Jt,exports.resolveToRegistry=e.q,exports.selectorToRange=e.C,exports.storageByUrl=e._,exports.tryLoadPackDescription=e.bt,exports.writePublishedCoords=e.J;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./config-on5N57rI.js");var t=e.Xt({GlobalOverviewPath:()=>e.r,MetaFile:()=>e.i,PlPackageConfigData:()=>e.c,PlPackageJsonConfigFile:()=>e.l,PlPackageYamlConfigFile:()=>e.u,PlRegCommonConfigData:()=>e.d,PlRegFullPackageConfigData:()=>e.f,PlRegPackageConfig:()=>e.t,PlRegPackageConfigDataShard:()=>e.p,packageContentPrefix:()=>e.a,packageOverviewPath:()=>e.o,payloadFilePath:()=>e.s});exports.BlockDescriptionPackageJsonField=e.ht,exports.BlockPackFolderName=e.gt,exports.BlockRegistryV2=e.w,exports.ChannelNameRegexp=e.T,exports.ChannelsFolder=e.E,exports.FSStorage=e.h,exports.GlobalOverviewFileName=e.D,exports.GlobalOverviewGzFileName=e.O,exports.GlobalOverviewGzPath=e.k,exports.GlobalOverviewPath=e.A,exports.KindImplementer=e.nt,exports.KindManifest=e.rt,exports.KindManifestFile=e.St,exports.KindManifestFileInfo=e.it,exports.KindManifestFileName=e.at,exports.KindManifestIdentity=e.ot,exports.KindOverview=e.st,exports.KindOverviewFileName=e.ct,exports.KindOverviewPathPattern=e.lt,exports.KindResolutionError=e.b,exports.KindVersionMismatchError=e.Z,exports.KindVersionOverview=e.ut,exports.KindsPrefix=e.dt,exports.MainPrefix=e.j,exports.ManifestFileName=e.M,exports.ManifestSuffix=e.N,exports.PackageManifestPattern=e.P,exports.PackageOverview=e.F,exports.PackageOverviewFileName=e.I,exports.PackageOverviewVersionEntry=e.L,exports.PublishedCoordsFileName=e.K,Object.defineProperty(exports,"RegistryV1",{enumerable:!0,get:function(){return t}}),exports.RegistryV2Reader=e.v,exports.S3Storage=e.g,exports.absoluteToBase64=e.Rt,exports.absoluteToBytes=e.zt,exports.absoluteToString=e.Bt,exports.addRelativePathPrefix=e.Tt,exports.blockComponentsManifestToAbsoluteUrl=e.Ot,exports.buildBlockPackDist=e.wt,exports.buildKindDist=e.Ct,exports.buildPublishedCoords=e.q,exports.checkKindVersionMatch=e.Q,exports.consolidateBlockComponents=e.kt,exports.consolidateBlockPackDescription=e.Et,exports.consolidateBlockPackMeta=e.Mt,exports.cpAbsoluteToRelative=e.Vt,exports.embedBlockPackMetaAbsoluteBase64=e.Nt,exports.embedBlockPackMetaAbsoluteBytes=e.Pt,exports.embedBlockPackMetaBytes=e.Ft,exports.folderReaderByUrl=e.m,exports.inferUpdateSuggestions=e.y,exports.kindContentPrefix=e.ft,exports.kindOverviewPath=e.pt,exports.loadPackDescription=e._t,exports.loadPackDescriptionFromManifest=e.vt,exports.loadPackDescriptionRaw=e.yt,exports.mapLocalToAbsolute=e.Ht,exports.npmNameToKindPath=e.mt,exports.packFolderToRelativeTgz=e.Ut,exports.packageChannelPrefix=e.R,exports.packageChannelPrefixInsideV2=e.z,exports.packageContentPrefix=e.B,exports.packageContentPrefixInsideV2=e.V,exports.packageOverviewPath=e.H,exports.packageOverviewPathInsideV2=e.U,exports.parseGlobalOverviewReg=e.W,exports.parsePackageName=e.bt,exports.parseSelector=e.x,exports.publishBlock=e.X,exports.readFacadeKindDependency=e.$,exports.readModelCompiledKindRef=e.et,exports.relativeToContentString=e.Wt,exports.relativeToExplicitBinary64=e.Gt,exports.relativeToExplicitBytes=e.Kt,exports.relativeToExplicitString=e.qt,exports.resolveBlockComponents=e.At,exports.resolveBlockPackDescription=e.Dt,exports.resolveBlockPackMeta=e.It,exports.resolveFacadeKind=e.tt,exports.resolveKind=e.S,exports.resolveManifestBlockComponents=e.jt,exports.resolveManifestBlockPackMeta=e.Lt,exports.resolveModuleFile=e.Jt,exports.resolveModuleFolder=e.Yt,exports.resolveToRegistry=e.J,exports.selectorToRange=e.C,exports.storageByUrl=e._,exports.tryLoadPackDescription=e.xt,exports.writePublishedCoords=e.Y;
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/registry_v1/index.ts"],"sourcesContent":["export * from \"./v1_repo_schema\";\nexport * from \"./config_schema\";\nexport { PlRegPackageConfig } from \"./config\";\n"],"mappings":""}
1
+ {"version":3,"file":"index.js","names":["require_config","registry_v1_exports"],"sources":["../src/registry_v1/index.ts"],"sourcesContent":["export * from \"./v1_repo_schema\";\nexport * from \"./config_schema\";\nexport { PlRegPackageConfig } from \"./config\";\n"],"mappings":""}
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import { $ as e, A as t, At as n, B as r, Bt as i, C as a, Ct as o, D as s, Dt as c, E as l, Et as u, F as d, Ft as f, G as p, Gt as m, H as h, Ht as g, I as _, It as v, J as y, Jt as b, K as x, Kt as S, L as C, Lt as w, M as T, Mt as E, N as D, Nt as O, O as k, Ot as A, P as j, Pt as M, Q as N, R as P, Rt as F, S as I, St as L, T as R, Tt as z, U as B, Ut as V, V as H, Vt as U, W, Wt as G, X as K, Y as q, Yt as J, Z as Y, _ as X, _t as Z, a as Q, at as $, b as ee, bt as te, c as ne, ct as re, d as ie, dt as ae, et as oe, f as se, ft as ce, g as le, gt as ue, h as de, ht as fe, i as pe, it as me, j as he, jt as ge, k as _e, kt as ve, l as ye, lt as be, m as xe, mt as Se, nt as Ce, o as we, ot as Te, p as Ee, pt as De, q as Oe, qt as ke, r as Ae, rt as je, s as Me, st as Ne, t as Pe, tt as Fe, u as Ie, ut as Le, v as Re, vt as ze, w as Be, wt as Ve, x as He, xt as Ue, y as We, yt as Ge, z as Ke, zt as qe } from "./config-Dx45aFJ_.mjs";
2
- //#region src/registry_v1/index.ts
3
- var Je = /* @__PURE__ */ J({
1
+ import { $ as e, A as t, At as n, B as r, Bt as i, C as a, Ct as o, D as s, Dt as c, E as l, Et as u, F as d, Ft as f, Gt as p, H as m, Ht as h, I as g, It as _, J as v, Jt as y, K as b, Kt as x, L as S, Lt as C, M as w, Mt as T, N as E, Nt as D, O, Ot as k, P as A, Pt as j, Q as M, R as N, Rt as P, S as F, St as I, T as L, Tt as R, U as z, Ut as B, V, Vt as H, W as U, Wt as W, X as G, Xt as K, Y as q, Yt as J, Z as Y, _ as X, _t as Z, a as Q, at as $, b as ee, bt as te, c as ne, ct as re, d as ie, dt as ae, et as oe, f as se, ft as ce, g as le, gt as ue, h as de, ht as fe, i as pe, it as me, j as he, jt as ge, k as _e, kt as ve, l as ye, lt as be, m as xe, mt as Se, nt as Ce, o as we, ot as Te, p as Ee, pt as De, q as Oe, qt as ke, r as Ae, rt as je, s as Me, st as Ne, t as Pe, tt as Fe, u as Ie, ut as Le, v as Re, vt as ze, w as Be, wt as Ve, x as He, xt as Ue, y as We, yt as Ge, z as Ke, zt as qe } from "./config-C07cYptA.mjs";
2
+ // src/registry_v1/index.ts
3
+ var Je = /* @__PURE__ */ K({
4
4
  GlobalOverviewPath: () => Ae,
5
5
  MetaFile: () => pe,
6
6
  PlPackageConfigData: () => ne,
@@ -14,7 +14,6 @@ var Je = /* @__PURE__ */ J({
14
14
  packageOverviewPath: () => we,
15
15
  payloadFilePath: () => Me
16
16
  });
17
- //#endregion
18
- export { Se as BlockDescriptionPackageJsonField, fe as BlockPackFolderName, Be as BlockRegistryV2, R as ChannelNameRegexp, l as ChannelsFolder, de as FSStorage, s as GlobalOverviewFileName, k as GlobalOverviewGzFileName, _e as GlobalOverviewGzPath, t as GlobalOverviewPath, Fe as KindImplementer, Ce as KindManifest, Ue as KindManifestFile, je as KindManifestFileInfo, me as KindManifestFileName, $ as KindManifestIdentity, Te as KindOverview, Ne as KindOverviewFileName, re as KindOverviewPathPattern, ee as KindResolutionError, K as KindVersionMismatchError, be as KindVersionOverview, Le as KindsPrefix, he as MainPrefix, T as ManifestFileName, D as ManifestSuffix, j as PackageManifestPattern, d as PackageOverview, _ as PackageOverviewFileName, C as PackageOverviewVersionEntry, p as PublishedCoordsFileName, Je as RegistryV1, Re as RegistryV2Reader, le as S3Storage, w as absoluteToBase64, F as absoluteToBytes, qe as absoluteToString, Ve as addRelativePathPrefix, c as blockComponentsManifestToAbsoluteUrl, o as buildBlockPackDist, L as buildKindDist, x as buildPublishedCoords, Y as checkKindVersionMatch, A as consolidateBlockComponents, z as consolidateBlockPackDescription, ge as consolidateBlockPackMeta, i as cpAbsoluteToRelative, E as embedBlockPackMetaAbsoluteBase64, O as embedBlockPackMetaAbsoluteBytes, M as embedBlockPackMetaBytes, xe as folderReaderByUrl, We as inferUpdateSuggestions, ae as kindContentPrefix, ce as kindOverviewPath, ue as loadPackDescription, Z as loadPackDescriptionFromManifest, ze as loadPackDescriptionRaw, U as mapLocalToAbsolute, De as npmNameToKindPath, g as packFolderToRelativeTgz, P as packageChannelPrefix, Ke as packageChannelPrefixInsideV2, r as packageContentPrefix, H as packageContentPrefixInsideV2, h as packageOverviewPath, B as packageOverviewPathInsideV2, W as parseGlobalOverviewReg, Ge as parsePackageName, He as parseSelector, q as publishBlock, N as readFacadeKindDependency, e as readModelCompiledKindRef, V as relativeToContentString, G as relativeToExplicitBinary64, m as relativeToExplicitBytes, S as relativeToExplicitString, ve as resolveBlockComponents, u as resolveBlockPackDescription, f as resolveBlockPackMeta, oe as resolveFacadeKind, I as resolveKind, n as resolveManifestBlockComponents, v as resolveManifestBlockPackMeta, ke as resolveModuleFile, b as resolveModuleFolder, Oe as resolveToRegistry, a as selectorToRange, X as storageByUrl, te as tryLoadPackDescription, y as writePublishedCoords };
17
+ export { fe as BlockDescriptionPackageJsonField, ue as BlockPackFolderName, Be as BlockRegistryV2, L as ChannelNameRegexp, l as ChannelsFolder, de as FSStorage, s as GlobalOverviewFileName, O as GlobalOverviewGzFileName, _e as GlobalOverviewGzPath, t as GlobalOverviewPath, Ce as KindImplementer, je as KindManifest, I as KindManifestFile, me as KindManifestFileInfo, $ as KindManifestFileName, Te as KindManifestIdentity, Ne as KindOverview, re as KindOverviewFileName, be as KindOverviewPathPattern, ee as KindResolutionError, Y as KindVersionMismatchError, Le as KindVersionOverview, ae as KindsPrefix, he as MainPrefix, w as ManifestFileName, E as ManifestSuffix, A as PackageManifestPattern, d as PackageOverview, g as PackageOverviewFileName, S as PackageOverviewVersionEntry, b as PublishedCoordsFileName, Je as RegistryV1, Re as RegistryV2Reader, le as S3Storage, P as absoluteToBase64, qe as absoluteToBytes, i as absoluteToString, R as addRelativePathPrefix, k as blockComponentsManifestToAbsoluteUrl, Ve as buildBlockPackDist, o as buildKindDist, Oe as buildPublishedCoords, M as checkKindVersionMatch, ve as consolidateBlockComponents, u as consolidateBlockPackDescription, T as consolidateBlockPackMeta, H as cpAbsoluteToRelative, D as embedBlockPackMetaAbsoluteBase64, j as embedBlockPackMetaAbsoluteBytes, f as embedBlockPackMetaBytes, xe as folderReaderByUrl, We as inferUpdateSuggestions, ce as kindContentPrefix, De as kindOverviewPath, Z as loadPackDescription, ze as loadPackDescriptionFromManifest, Ge as loadPackDescriptionRaw, h as mapLocalToAbsolute, Se as npmNameToKindPath, B as packFolderToRelativeTgz, N as packageChannelPrefix, Ke as packageChannelPrefixInsideV2, r as packageContentPrefix, V as packageContentPrefixInsideV2, m as packageOverviewPath, z as packageOverviewPathInsideV2, U as parseGlobalOverviewReg, te as parsePackageName, He as parseSelector, G as publishBlock, e as readFacadeKindDependency, oe as readModelCompiledKindRef, W as relativeToContentString, p as relativeToExplicitBinary64, x as relativeToExplicitBytes, ke as relativeToExplicitString, n as resolveBlockComponents, c as resolveBlockPackDescription, _ as resolveBlockPackMeta, Fe as resolveFacadeKind, F as resolveKind, ge as resolveManifestBlockComponents, C as resolveManifestBlockPackMeta, y as resolveModuleFile, J as resolveModuleFolder, v as resolveToRegistry, a as selectorToRange, X as storageByUrl, Ue as tryLoadPackDescription, q as writePublishedCoords };
19
18
 
20
19
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/registry_v1/index.ts"],"sourcesContent":["export * from \"./v1_repo_schema\";\nexport * from \"./config_schema\";\nexport { PlRegPackageConfig } from \"./config\";\n"],"mappings":""}
1
+ {"version":3,"file":"index.mjs","names":["readFacadeKindDependency","GlobalOverviewPath","resolveBlockComponents","packageContentPrefix","absoluteToString","selectorToRange","buildKindDist","GlobalOverviewFileName","resolveBlockPackDescription","ChannelsFolder","consolidateBlockPackDescription","PackageOverview","embedBlockPackMetaBytes","relativeToExplicitBinary64","packageOverviewPath","mapLocalToAbsolute","PackageOverviewFileName","resolveBlockPackMeta","resolveToRegistry","resolveModuleFile","PublishedCoordsFileName","relativeToExplicitBytes","PackageOverviewVersionEntry","resolveManifestBlockPackMeta","ManifestFileName","consolidateBlockPackMeta","ManifestSuffix","embedBlockPackMetaAbsoluteBase64","blockComponentsManifestToAbsoluteUrl","PackageManifestPattern","embedBlockPackMetaAbsoluteBytes","checkKindVersionMatch","packageChannelPrefix","absoluteToBase64","resolveKind","KindManifestFile","ChannelNameRegexp","addRelativePathPrefix","packageOverviewPathInsideV2","packFolderToRelativeTgz","cpAbsoluteToRelative","parseGlobalOverviewReg","relativeToContentString","publishBlock","__exportAll","writePublishedCoords","resolveModuleFolder","KindVersionMismatchError","storageByUrl","loadPackDescription","packageContentPrefix$1","KindManifestFileName","KindResolutionError","parsePackageName","PlPackageConfigData","KindOverviewFileName","PlRegCommonConfigData","KindsPrefix","readModelCompiledKindRef","PlRegFullPackageConfigData","kindContentPrefix","S3Storage","BlockPackFolderName","FSStorage","BlockDescriptionPackageJsonField","MetaFile","KindManifestFileInfo","MainPrefix","resolveManifestBlockComponents","GlobalOverviewGzPath","consolidateBlockComponents","PlPackageJsonConfigFile","KindOverviewPathPattern","folderReaderByUrl","npmNameToKindPath","KindImplementer","packageOverviewPath$1","KindManifestIdentity","PlRegPackageConfigDataShard","kindOverviewPath","buildPublishedCoords","relativeToExplicitString","GlobalOverviewPath$1","KindManifest","payloadFilePath","KindOverview","PlRegPackageConfig","resolveFacadeKind","PlPackageYamlConfigFile","KindVersionOverview","RegistryV2Reader","loadPackDescriptionFromManifest","BlockRegistryV2","buildBlockPackDist","parseSelector","tryLoadPackDescription","inferUpdateSuggestions","loadPackDescriptionRaw","packageChannelPrefixInsideV2","absoluteToBytes","registry_v1_exports","GlobalOverviewGzFileName","packageContentPrefixInsideV2"],"sources":["../src/registry_v1/index.ts"],"sourcesContent":["export * from \"./v1_repo_schema\";\nexport * from \"./config_schema\";\nexport { PlRegPackageConfig } from \"./config\";\n"],"mappings":""}
@@ -51,5 +51,5 @@ export type RunContext = {
51
51
  };
52
52
  export type { ContentForm, ManagedBody, RunMode, Structure, TriggerFn };
53
53
  export { defineStructure, scope, when, whenFilesExist, onUpdateDeps, onInitOrUpdate, fixed, managed, scaffold, seed, remove, rename, file, binaryFile, text, tpl, generate, blockVars, } from './builders';
54
- export { ensureField, removeField, requireField, ensureFieldEntries, ensureScript, removeScript, ensureDep, ensureDevDep, ensurePeerDep, ensureOptionalDep, removeDep, ensureDeps, ensureDevDeps, ensurePeerDeps, ensureOptionalDeps, ensureWorkspaceScopeDeps, ensureWorkspaceScopeDevDeps, ensureWorkspaceScopePeerDeps, pruneKeysMatching, pruneKeysMatchingAt, enforceFieldOrder, enforceFieldOrderAt, enforceAlphabeticalOrder, transformAt, ensureWorkspaceModulePaths, ensureCatalogVersion, pinCatalogTo, pinCatalogToDependencyOf, ensureCatalogLatest, ensureCatalogAbsent, ensureGitignoreEntries, removeGitignoreEntries, withManagedBody, withManagedYaml, withManagedLines, } from './content-rules';
54
+ export { ensureField, removeField, requireField, forbidField, ensureFieldEntries, ensureScript, removeScript, ensureDep, ensureDevDep, ensurePeerDep, ensureOptionalDep, removeDep, ensureDeps, ensureDevDeps, ensurePeerDeps, ensureOptionalDeps, ensureWorkspaceScopeDeps, ensureWorkspaceScopeDevDeps, ensureWorkspaceScopePeerDeps, pruneKeysMatching, pruneKeysMatchingAt, enforceFieldOrder, enforceFieldOrderAt, enforceAlphabeticalOrder, transformAt, ensureWorkspaceModulePaths, ensureCatalogVersion, pinCatalogTo, pinCatalogToDependencyOf, ensureCatalogLatest, ensureCatalogAbsent, ensureCatalogAtLeast, ensureWorkspaceMapEntries, ensureWorkspaceListItems, ensureGitignoreEntries, removeGitignoreEntries, withManagedBody, withManagedYaml, withManagedLines, } from './content-rules';
55
55
  export type { DepVersion } from './content-rules';
@@ -52,6 +52,8 @@ export declare function ensureField(jsonPath: string, value: unknown): void;
52
52
  export declare function removeField(jsonPath: string, predicate?: (currentValue: unknown) => boolean): void;
53
53
  /** Assert a field exists; throw if absent. No mutation. */
54
54
  export declare function requireField(jsonPath: string, message?: string): void;
55
+ /** Throw `message` when the field at `jsonPath` exists. No mutation. */
56
+ export declare function forbidField(jsonPath: string, message: string): void;
55
57
  /** Merge `entries` into the object at `jsonPath`. Auto-creates the
56
58
  * object if missing. Existing other entries preserved. */
57
59
  export declare function ensureFieldEntries(jsonPath: string, entries: Record<string, unknown>): void;
@@ -157,8 +159,18 @@ export declare function pinCatalogToDependencyOf(catalogEntry: string, _opts: {
157
159
  of: string;
158
160
  ofVersion?: string;
159
161
  }): void;
162
+ /** Raise `catalog.<name>` to `range` when its lowest version is lower; add it when absent.
163
+ * An entry that is not a semver range stays. */
164
+ export declare function ensureCatalogAtLeast(name: string, range: string): void;
160
165
  /** Remove `catalog.<name>` if present; no-op when absent. */
161
166
  export declare function ensureCatalogAbsent(name: string): void;
167
+ /** Set each `entries` key under the top-level map `key`; other keys stay.
168
+ * With `keepExisting`, a key the map already has keeps its value. */
169
+ export declare function ensureWorkspaceMapEntries(key: string, entries: Record<string, unknown>, { keepExisting }?: {
170
+ keepExisting?: boolean | undefined;
171
+ }): void;
172
+ /** Append each missing `items` entry to the top-level list `key`. */
173
+ export declare function ensureWorkspaceListItems(key: string, items: readonly string[]): void;
162
174
  /** Append each `entry` if not already present (comment-aware equality). */
163
175
  export declare function ensureGitignoreEntries(entries: string[]): void;
164
176
  /** Drop any line whose normalised form matches any of `patterns`. */
@@ -5,7 +5,7 @@ import { DerivedCatalogPin } from '../engine/registry-client';
5
5
  * them. */
6
6
  export declare const SDK_CATALOG_PACKAGES: readonly string[];
7
7
  /** Infra / tooling — a `~`-floored curated set. Not exact-pinned on refresh
8
- * and not bumped by update-deps. */
8
+ * (refresh only raises turbo to `TURBO_VERSION`) and not bumped by update-deps. */
9
9
  export declare const INFRA_CATALOG_FLOOR: Record<string, string>;
10
10
  /** Catalog entries whose exact version is DERIVED from the version another
11
11
  * package declares for them (see `pinCatalogToDependencyOf`). Resolved on
@@ -1 +1 @@
1
- export declare const canonicalPackageJsonOrder: readonly ["name", "version", "private", "description", "keywords", "files", "type", "main", "module", "types", "exports", "scripts", "dependencies", "devDependencies", "peerDependencies", "optionalDependencies", "packageManager", "pnpm", "block", "block-software"];
1
+ export declare const canonicalPackageJsonOrder: readonly ["name", "version", "private", "description", "keywords", "files", "type", "main", "module", "types", "exports", "scripts", "dependencies", "devDependencies", "peerDependencies", "optionalDependencies", "devEngines", "engines", "packageManager", "block", "block-software"];
@@ -0,0 +1,18 @@
1
+ /** Node version for the block CI workflows. */
2
+ export declare const NODE_VERSION = "24.22.1";
3
+ /** Root `devEngines.runtime` of a block: pnpm runs the block scripts on this Node. */
4
+ export declare const NODE_RUNTIME: {
5
+ name: string;
6
+ version: string;
7
+ onFail: string;
8
+ };
9
+ /** Root `packageManager` of a block. The shared CI workflow installs this pnpm. */
10
+ export declare const PACKAGE_MANAGER = "pnpm@12.16.1+sha512.091d1dee7927575661b25dab3c2ae31cafc1c54d6b71ed6a6600cf0c1cb10434a447eae14015f1b23e2e6bedeed79df94a932e2164d2832280dce3fb32f656d1";
11
+ /** turbo before 2.11 cannot parse a pnpm 12 lockfile. */
12
+ export declare const TURBO_VERSION = "~2.11.10";
13
+ /** Our scopes skip `minimumReleaseAge`, so `upgrade-sdk` gets a fresh SDK release. */
14
+ export declare const MINIMUM_RELEASE_AGE_EXCLUDE: readonly string[];
15
+ /** pnpm 11+ fails an install on an unlisted install script. This one downloads the native addon. */
16
+ export declare const REQUIRED_BUILDS: Record<string, boolean>;
17
+ /** Install scripts in the SDK tree that only clean up, print, or build optional native code. */
18
+ export declare const SKIPPED_BUILDS: Record<string, boolean>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platforma-sdk/block-tools",
3
- "version": "2.16.17",
3
+ "version": "2.17.1",
4
4
  "description": "Utility to manipulate Platforma Blocks and Block Registry",
5
5
  "license": "UNLICENSED",
6
6
  "bin": {
@@ -23,42 +23,42 @@
23
23
  }
24
24
  },
25
25
  "dependencies": {
26
- "@aws-sdk/client-ecr-public": "3.859.0",
27
- "@aws-sdk/client-s3": "3.859.0",
26
+ "@aws-sdk/client-ecr-public": "3.1157.0",
27
+ "@aws-sdk/client-s3": "3.1157.0",
28
28
  "@inquirer/prompts": "^7.0.1",
29
+ "@milaboratories/pl-http": "1.2.5",
30
+ "@milaboratories/pl-model-backend": "1.4.40",
31
+ "@milaboratories/pl-model-common": "1.54.2",
32
+ "@milaboratories/pl-model-middle-layer": "1.34.6",
33
+ "@milaboratories/resolve-helper": "1.1.3",
34
+ "@milaboratories/ts-helpers": "1.9.0",
35
+ "@platforma-sdk/blocks-deps-updater": "2.2.1",
36
+ "@platforma-sdk/package-builder-lib": "1.4.2",
29
37
  "canonicalize": "~2.1.0",
30
38
  "commander": "^15.0.0",
31
- "lru-cache": "^11.2.2",
39
+ "lru-cache": "^11.5.3",
32
40
  "mime-types": "^2.1.35",
33
- "tar": "^7.4.3",
34
- "undici": "~7.16.0",
35
- "yaml": "^2.8.0",
36
- "zod": "~3.25.76",
37
- "@milaboratories/pl-http": "1.2.4",
38
- "@milaboratories/pl-model-middle-layer": "1.34.4",
39
- "@milaboratories/pl-model-backend": "1.4.38",
40
- "@milaboratories/resolve-helper": "1.1.3",
41
- "@platforma-sdk/blocks-deps-updater": "2.2.0",
42
- "@milaboratories/pl-model-common": "1.54.0",
43
- "@milaboratories/ts-helpers": "1.8.6",
44
- "@platforma-sdk/package-builder-lib": "1.4.1"
41
+ "tar": "^7.5.22",
42
+ "undici": "~7.30.1",
43
+ "yaml": "^2.9.1",
44
+ "zod": "~3.25.76"
45
45
  },
46
46
  "devDependencies": {
47
+ "@milaboratories/build-configs": "2.0.2",
48
+ "@milaboratories/ts-builder": "1.7.3",
49
+ "@milaboratories/ts-configs": "1.4.0",
47
50
  "@rollup/plugin-node-resolve": "^16.0.1",
48
51
  "@types/mime-types": "^2.1.4",
49
- "@types/node": "~24.5.2",
52
+ "@types/node": "~24.19.2",
50
53
  "@types/semver": "^7.7.0",
51
- "@vitest/coverage-istanbul": "^4.1.3",
52
- "oxfmt": "0.35.0",
54
+ "@vitest/coverage-istanbul": "^4.1.12",
55
+ "oxfmt": "0.75.0",
53
56
  "rollup-plugin-node-externals": "^8.0.0",
54
57
  "semver": "^7.7.2",
55
58
  "typescript": "7.0.2",
56
- "vite": "^8.0.6",
57
- "vite-plugin-dts": "^5.0.3",
58
- "vitest": "^4.1.3",
59
- "@milaboratories/build-configs": "2.0.2",
60
- "@milaboratories/ts-configs": "1.4.0",
61
- "@milaboratories/ts-builder": "1.7.2"
59
+ "vite": "^8.3.5",
60
+ "vite-plugin-dts": "^5.1.2",
61
+ "vitest": "^4.1.12"
62
62
  },
63
63
  "repository": {
64
64
  "type": "git",
@@ -86,7 +86,7 @@ describe("rootCatalogBumpRules mode split", () => {
86
86
  // standard infra-floor keys ABSENT from the on-disk catalog are
87
87
  // SEEDED add-if-absent (no manual seed step needed).
88
88
  expect(cat["shx"]).toBe("~0.4.0");
89
- expect(cat["turbo"]).toBe("~2.8.11");
89
+ expect(cat["turbo"]).toBe("~2.11.10");
90
90
  expect(cat["vitest"]).toBe("~4.0.18");
91
91
  // Non-matching dep left alone.
92
92
  expect(cat["lodash"]).toBe("1.0.0");
@@ -12,6 +12,7 @@ import type { BlockVars, RunContext } from "../engine/api";
12
12
  import { STRUCTURE } from "../structure-definition";
13
13
  import { run as engineRun } from "../engine/runner";
14
14
  import { defaultTemplateProvider, simulateInit } from "../engine/testing";
15
+ import { NODE_VERSION } from "../rules/shared/toolchain";
15
16
 
16
17
  const TEMPLATES = defaultTemplateProvider();
17
18
 
@@ -37,9 +38,9 @@ describe("root CI workflows (fixed, generated)", () => {
37
38
  // Shared constants.
38
39
  expect(build).toContain("team-id: 'ciplopen'");
39
40
  expect(build).toContain("test: true");
40
- // Both workflows track the toolchain's node major (mise pins 22).
41
- expect(build).toContain("node-version: '22.x'");
42
- expect(fs.read(MARK_STABLE)).toContain("node-version: '22.x'");
41
+ // Both workflows run the SDK's Node.
42
+ expect(build).toContain(`node-version: '${NODE_VERSION}'`);
43
+ expect(fs.read(MARK_STABLE)).toContain(`node-version: '${NODE_VERSION}'`);
43
44
  // Opt-in gate: the published `block` package must be bumped on PRs.
44
45
  expect(build).toContain("require-package-path-bump: true");
45
46
  // No bare `build` script exists (root-package-json removes it), so both
@@ -0,0 +1,156 @@
1
+ // A block must run on the SDK's Node and pnpm: `upgrade-sdk` aligns it.
2
+
3
+ import { readFileSync } from "node:fs";
4
+ import { satisfies } from "semver";
5
+ import { describe, expect, test } from "vitest";
6
+ import { parse as parseYaml, parseDocument } from "yaml";
7
+ import type { BlockVars } from "../engine/api";
8
+ import type { MemoryFileSystem } from "../engine/fs/memory";
9
+ import { discoverRunContext } from "../engine/discovery-fs";
10
+ import { run as engineRun } from "../engine/runner";
11
+ import { defaultTemplateProvider, simulateInit } from "../engine/testing";
12
+ import {
13
+ MINIMUM_RELEASE_AGE_EXCLUDE,
14
+ NODE_RUNTIME,
15
+ NODE_VERSION,
16
+ PACKAGE_MANAGER,
17
+ REQUIRED_BUILDS,
18
+ SKIPPED_BUILDS,
19
+ TURBO_VERSION,
20
+ } from "../rules/shared/toolchain";
21
+ import { STRUCTURE } from "../structure-definition";
22
+
23
+ const VARS: BlockVars = {
24
+ facadeName: "@platforma-open/test-org.demo",
25
+ baseName: "test-org.demo",
26
+ npmOrg: "@platforma-open",
27
+ orgScope: "test-org",
28
+ shortName: "demo",
29
+ };
30
+
31
+ const readMonorepoFile = (relativePath: string) =>
32
+ readFileSync(new URL(`../../../../../${relativePath}`, import.meta.url), "utf-8");
33
+
34
+ function refresh(fs: MemoryFileSystem) {
35
+ return engineRun(STRUCTURE, fs, discoverRunContext({ fs, isSdkInternal: false }), {
36
+ templates: defaultTemplateProvider(),
37
+ rediscover: () => discoverRunContext({ fs, isSdkInternal: false, dryRun: true }),
38
+ });
39
+ }
40
+
41
+ function editJson(fs: MemoryFileSystem, edit: (json: Record<string, unknown>) => void) {
42
+ const json = JSON.parse(fs.read("package.json"));
43
+ edit(json);
44
+ fs.write("package.json", JSON.stringify(json, null, 2) + "\n");
45
+ }
46
+
47
+ function editWorkspace(
48
+ fs: MemoryFileSystem,
49
+ edit: (doc: ReturnType<typeof parseDocument>) => void,
50
+ ) {
51
+ const doc = parseDocument(fs.read("pnpm-workspace.yaml"));
52
+ edit(doc);
53
+ fs.write("pnpm-workspace.yaml", doc.toString());
54
+ }
55
+
56
+ const readWorkspace = (fs: MemoryFileSystem) => parseYaml(fs.read("pnpm-workspace.yaml"));
57
+
58
+ describe("block toolchain", () => {
59
+ test("matches the SDK monorepo", () => {
60
+ const root = JSON.parse(readMonorepoFile("package.json"));
61
+ expect(PACKAGE_MANAGER).toBe(root.packageManager);
62
+ const setup = parseYaml(readMonorepoFile(".github/actions/setup/action.yml"));
63
+ expect(NODE_VERSION).toBe(setup.inputs["node-version"].default);
64
+ const workspace = parseYaml(readMonorepoFile("pnpm-workspace.yaml"));
65
+ expect(MINIMUM_RELEASE_AGE_EXCLUDE).toEqual(workspace.minimumReleaseAgeExclude);
66
+ expect(satisfies(workspace.catalog.turbo, TURBO_VERSION)).toBe(true);
67
+ expect(workspace.allowBuilds).toMatchObject({ ...REQUIRED_BUILDS, ...SKIPPED_BUILDS });
68
+ });
69
+
70
+ test("refresh moves an old block to the SDK pnpm and Node", () => {
71
+ const { fs } = simulateInit({ vars: VARS });
72
+ editJson(fs, (json) => {
73
+ json.packageManager = "pnpm@9.12.0";
74
+ delete json.devEngines;
75
+ json.pnpm = { overrides: {} };
76
+ });
77
+ editWorkspace(fs, (doc) => {
78
+ doc.delete("minimumReleaseAgeExclude");
79
+ doc.delete("allowBuilds");
80
+ doc.setIn(["catalog", "turbo"], "~2.8.11");
81
+ });
82
+
83
+ refresh(fs);
84
+
85
+ const pkg = JSON.parse(fs.read("package.json"));
86
+ expect(pkg.packageManager).toBe(PACKAGE_MANAGER);
87
+ expect(pkg.devEngines).toEqual({ runtime: NODE_RUNTIME });
88
+ expect(pkg.pnpm).toBeUndefined();
89
+ const workspace = readWorkspace(fs);
90
+ expect(workspace.minimumReleaseAgeExclude).toEqual(MINIMUM_RELEASE_AGE_EXCLUDE);
91
+ expect(workspace.allowBuilds).toEqual({ ...SKIPPED_BUILDS, ...REQUIRED_BUILDS });
92
+ expect(workspace.catalog.turbo).toBe(TURBO_VERSION);
93
+ });
94
+
95
+ test("refresh replaces only the runtime among the author's engines", () => {
96
+ const { fs } = simulateInit({ vars: VARS });
97
+ const packageManager = { name: "pnpm", version: "12.16.1" };
98
+ editJson(fs, (json) => {
99
+ json.engines = { node: ">=20" };
100
+ json.devEngines = { packageManager, runtime: { name: "node", version: "22" } };
101
+ });
102
+
103
+ refresh(fs);
104
+
105
+ const pkg = JSON.parse(fs.read("package.json"));
106
+ expect(pkg.devEngines).toEqual({ packageManager, runtime: NODE_RUNTIME });
107
+ expect(pkg.engines).toEqual({ node: ">=20" });
108
+ const toolchainKeys = ["devEngines", "engines", "packageManager"];
109
+ expect(Object.keys(pkg).filter((key) => toolchainKeys.includes(key))).toEqual(toolchainKeys);
110
+ });
111
+
112
+ test("refresh keeps the author's workspace settings", () => {
113
+ const { fs } = simulateInit({ vars: VARS });
114
+ editWorkspace(fs, (doc) => {
115
+ doc.set("overrides", { zod: "~3.25.76" });
116
+ doc.setIn(["allowBuilds", "sharp"], true);
117
+ doc.setIn(["allowBuilds", "esbuild"], true);
118
+ doc.setIn(["allowBuilds", "@milaboratories/pframes-rs-node"], false);
119
+ doc.setIn(["catalog", "turbo"], "~2.12.3");
120
+ });
121
+
122
+ refresh(fs);
123
+
124
+ const workspace = readWorkspace(fs);
125
+ expect(workspace.overrides).toEqual({ zod: "~3.25.76" });
126
+ expect(workspace.allowBuilds).toEqual({
127
+ ...SKIPPED_BUILDS,
128
+ ...REQUIRED_BUILDS,
129
+ sharp: true,
130
+ esbuild: true,
131
+ });
132
+ expect(workspace.catalog.turbo).toBe("~2.12.3");
133
+ });
134
+
135
+ test("refresh fills empty workspace settings", () => {
136
+ const { fs } = simulateInit({ vars: VARS });
137
+ editWorkspace(fs, (doc) => {
138
+ doc.set("minimumReleaseAgeExclude", null);
139
+ doc.set("allowBuilds", null);
140
+ });
141
+
142
+ refresh(fs);
143
+
144
+ const workspace = readWorkspace(fs);
145
+ expect(workspace.minimumReleaseAgeExclude).toEqual(MINIMUM_RELEASE_AGE_EXCLUDE);
146
+ expect(workspace.allowBuilds).toEqual({ ...SKIPPED_BUILDS, ...REQUIRED_BUILDS });
147
+ });
148
+
149
+ test("refresh refuses a package.json pnpm field with settings", () => {
150
+ const { fs } = simulateInit({ vars: VARS });
151
+ editJson(fs, (json) => {
152
+ json.pnpm = { overrides: { zod: "~3.25.76" } };
153
+ });
154
+ expect(() => refresh(fs)).toThrow(/pnpm-workspace\.yaml/);
155
+ });
156
+ });
@@ -0,0 +1,28 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import { ensureCatalogAtLeast, withManagedYaml } from "../../content-rules";
3
+ import { parseYaml } from "../../parsers/yaml";
4
+
5
+ function turboAfter(catalog: string): unknown {
6
+ const doc = parseYaml(`catalog:\n${catalog}`);
7
+ withManagedYaml(doc, () => ensureCatalogAtLeast("turbo", "~2.11.10"));
8
+ return doc.getIn(["catalog", "turbo"]);
9
+ }
10
+
11
+ describe("ensureCatalogAtLeast", () => {
12
+ test("raises a lower range", () => {
13
+ expect(turboAfter(" turbo: ~2.8.11\n")).toBe("~2.11.10");
14
+ });
15
+
16
+ test("keeps an equal or higher range", () => {
17
+ expect(turboAfter(" turbo: ^2.11.10\n")).toBe("^2.11.10");
18
+ expect(turboAfter(" turbo: 2.12.0\n")).toBe("2.12.0");
19
+ });
20
+
21
+ test("adds an absent entry", () => {
22
+ expect(turboAfter(" shx: ~0.4.0\n")).toBe("~2.11.10");
23
+ });
24
+
25
+ test("keeps an entry that is not a semver range", () => {
26
+ expect(turboAfter(" turbo: latest\n")).toBe("latest");
27
+ });
28
+ });
@@ -92,6 +92,7 @@ export {
92
92
  ensureField,
93
93
  removeField,
94
94
  requireField,
95
+ forbidField,
95
96
  ensureFieldEntries,
96
97
  ensureScript,
97
98
  removeScript,
@@ -120,6 +121,9 @@ export {
120
121
  pinCatalogToDependencyOf,
121
122
  ensureCatalogLatest,
122
123
  ensureCatalogAbsent,
124
+ ensureCatalogAtLeast,
125
+ ensureWorkspaceMapEntries,
126
+ ensureWorkspaceListItems,
123
127
  // Lines managed builders
124
128
  ensureGitignoreEntries,
125
129
  removeGitignoreEntries,
@@ -11,6 +11,8 @@
11
11
  // ensure*/remove*/require*/prune/enforce*/transform; YAML catalog
12
12
  // management + workspace module paths; gitignore line management.
13
13
 
14
+ import { gte, minVersion, validRange } from "semver";
15
+ import { isMap, isSeq } from "yaml";
14
16
  import { registerInnerWhenHandler, tryGetActiveRunContext } from "./builders";
15
17
  import type { RunContext, Scope } from "./api";
16
18
  import type { ManagedBody, TriggerContext, TriggerFn } from "./ir";
@@ -286,6 +288,13 @@ export function requireField(jsonPath: string, message?: string): void {
286
288
  }
287
289
  }
288
290
 
291
+ /** Throw `message` when the field at `jsonPath` exists. No mutation. */
292
+ export function forbidField(jsonPath: string, message: string): void {
293
+ if (hasAtPath(requireJson("forbidField").obj, jsonPath)) {
294
+ throw new Error(message);
295
+ }
296
+ }
297
+
289
298
  /** Merge `entries` into the object at `jsonPath`. Auto-creates the
290
299
  * object if missing. Existing other entries preserved. */
291
300
  export function ensureFieldEntries(jsonPath: string, entries: Record<string, unknown>): void {
@@ -608,6 +617,20 @@ export function pinCatalogToDependencyOf(
608
617
  state.doc.setIn(["catalog", catalogEntry], version);
609
618
  }
610
619
 
620
+ /** Raise `catalog.<name>` to `range` when its lowest version is lower; add it when absent.
621
+ * An entry that is not a semver range stays. */
622
+ export function ensureCatalogAtLeast(name: string, range: string): void {
623
+ const state = requireYaml("ensureCatalogAtLeast");
624
+ const current = readCatalogString(state, name);
625
+ const floor = minVersion(range);
626
+ const currentMin =
627
+ current !== undefined && validRange(current) !== null ? minVersion(current) : null;
628
+ if (current !== undefined && (floor === null || currentMin === null || gte(currentMin, floor))) {
629
+ return;
630
+ }
631
+ state.doc.setIn(["catalog", name], range);
632
+ }
633
+
611
634
  /** Remove `catalog.<name>` if present; no-op when absent. */
612
635
  export function ensureCatalogAbsent(name: string): void {
613
636
  const state = requireYaml("ensureCatalogAbsent");
@@ -615,6 +638,43 @@ export function ensureCatalogAbsent(name: string): void {
615
638
  state.doc.deleteIn(["catalog", name]);
616
639
  }
617
640
 
641
+ /** Set each `entries` key under the top-level map `key`; other keys stay.
642
+ * With `keepExisting`, a key the map already has keeps its value. */
643
+ export function ensureWorkspaceMapEntries(
644
+ key: string,
645
+ entries: Record<string, unknown>,
646
+ { keepExisting = false } = {},
647
+ ): void {
648
+ const state = requireYaml("ensureWorkspaceMapEntries");
649
+ if (!isMap(state.doc.get(key))) {
650
+ state.doc.delete(key);
651
+ }
652
+ const changed = Object.entries(entries).filter(([name, value]) =>
653
+ keepExisting ? !state.doc.hasIn([key, name]) : state.doc.getIn([key, name]) !== value,
654
+ );
655
+ for (const [name, value] of changed) {
656
+ state.doc.setIn([key, name], value);
657
+ }
658
+ }
659
+
660
+ /** Append each missing `items` entry to the top-level list `key`. */
661
+ export function ensureWorkspaceListItems(key: string, items: readonly string[]): void {
662
+ const state = requireYaml("ensureWorkspaceListItems");
663
+ const current = state.doc.toJSON()?.[key];
664
+ const present = new Set(Array.isArray(current) ? current : []);
665
+ const missing = items.filter((item) => !present.has(item));
666
+ if (missing.length === 0) {
667
+ return;
668
+ }
669
+ if (!isSeq(state.doc.get(key))) {
670
+ state.doc.set(key, missing);
671
+ return;
672
+ }
673
+ for (const item of missing) {
674
+ state.doc.addIn([key], item);
675
+ }
676
+ }
677
+
618
678
  // ============================================================
619
679
  // Lines builders — `.gitignore`
620
680
  // ============================================================
@@ -15,6 +15,7 @@
15
15
  // `when(!isSdkInternal)` guard, matching the rest of the root scope.
16
16
 
17
17
  import { scope, when, fixed, tpl } from "../engine/api";
18
+ import { NODE_VERSION } from "./shared/toolchain";
18
19
 
19
20
  /** Readable default CI label from a short name (`mixcr-clonotyping-2` →
20
21
  * `Mixcr Clonotyping 2`). Dash/underscore words, title-cased. Human-curated
@@ -40,6 +41,7 @@ export function rootCiRules(): void {
40
41
  return {
41
42
  appName: `Block: ${humanizeShortName(shortName)}`,
42
43
  appNameSlug: `block-${shortName}`,
44
+ nodeVersion: NODE_VERSION,
43
45
  };
44
46
  }),
45
47
  );
@@ -47,6 +49,7 @@ export function rootCiRules(): void {
47
49
  ".github/workflows/mark-stable.yaml",
48
50
  tpl("workflows/mark-stable.tpl.yaml", (ctx) => ({
49
51
  appName: `Block: ${humanizeShortName(ctx.blockVars.shortName)} - Mark Stable`,
52
+ nodeVersion: NODE_VERSION,
50
53
  })),
51
54
  );
52
55
  },
@@ -13,9 +13,11 @@ import {
13
13
  pruneKeysMatching,
14
14
  enforceAlphabeticalOrder,
15
15
  enforceFieldOrder,
16
+ forbidField,
16
17
  } from "../engine/api";
17
18
  import { canonicalPackageJsonOrder } from "./shared/key-order";
18
19
  import { INITIAL_MODULE_VERSION } from "./shared/initial-version";
20
+ import { NODE_RUNTIME, PACKAGE_MANAGER } from "./shared/toolchain";
19
21
 
20
22
  // `variant=all` builds every variant the software declares — docker-vs-binary is not a per-script
21
23
  // choice. `no-software` builds placeholder software for validators.
@@ -71,12 +73,25 @@ export function rootPackageJsonInitial(): Record<string, unknown> {
71
73
  shx: "catalog:",
72
74
  turbo: "catalog:",
73
75
  },
74
- packageManager: "pnpm@9.12.0",
75
76
  };
76
77
  }
77
78
 
78
79
  export function rootPackageJsonRules(): void {
79
- ensureField("packageManager", "pnpm@9.12.0");
80
+ ensureField("packageManager", PACKAGE_MANAGER);
81
+ ensureField("devEngines.runtime", NODE_RUNTIME);
82
+ removeField(
83
+ "pnpm",
84
+ (settings) =>
85
+ settings === null ||
86
+ (typeof settings === "object" &&
87
+ Object.values(settings).every(
88
+ (value) => typeof value === "object" && value !== null && Object.keys(value).length === 0,
89
+ )),
90
+ );
91
+ forbidField(
92
+ "pnpm",
93
+ 'pnpm 11+ ignores the "pnpm" field in package.json. Move its settings to pnpm-workspace.yaml: overrides as is, onlyBuiltDependencies as allowBuilds true, neverBuiltDependencies as allowBuilds false.',
94
+ );
80
95
 
81
96
  // Root is never published — it carries no `name`; delete any stray one.
82
97
  removeField("name");