@milaboratories/pl-middle-layer 1.66.18 → 1.67.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 (130) hide show
  1. package/dist/block_registry/index.cjs +3 -0
  2. package/dist/block_registry/index.d.ts +2 -1
  3. package/dist/block_registry/index.js +2 -1
  4. package/dist/block_registry/location_provider.cjs +117 -0
  5. package/dist/block_registry/location_provider.cjs.map +1 -0
  6. package/dist/block_registry/location_provider.js +114 -0
  7. package/dist/block_registry/location_provider.js.map +1 -0
  8. package/dist/block_registry/registry.cjs +13 -0
  9. package/dist/block_registry/registry.cjs.map +1 -1
  10. package/dist/block_registry/registry.d.ts +14 -1
  11. package/dist/block_registry/registry.d.ts.map +1 -1
  12. package/dist/block_registry/registry.js +13 -0
  13. package/dist/block_registry/registry.js.map +1 -1
  14. package/dist/block_registry/template_provider.cjs +104 -0
  15. package/dist/block_registry/template_provider.cjs.map +1 -0
  16. package/dist/block_registry/template_provider.d.ts +62 -0
  17. package/dist/block_registry/template_provider.d.ts.map +1 -0
  18. package/dist/block_registry/template_provider.js +103 -0
  19. package/dist/block_registry/template_provider.js.map +1 -0
  20. package/dist/block_registry/watcher.cjs +3 -3
  21. package/dist/block_registry/watcher.js +2 -2
  22. package/dist/dev_env/util.js +2 -2
  23. package/dist/dev_env/util.js.map +1 -1
  24. package/dist/index.cjs +10 -0
  25. package/dist/index.d.ts +5 -1
  26. package/dist/index.js +5 -1
  27. package/dist/js_render/computable_context.cjs +1 -1
  28. package/dist/js_render/computable_context.js +1 -1
  29. package/dist/middle_layer/driver_kit.d.ts +1 -1
  30. package/dist/middle_layer/middle_layer.cjs +144 -2
  31. package/dist/middle_layer/middle_layer.cjs.map +1 -1
  32. package/dist/middle_layer/middle_layer.d.ts +67 -1
  33. package/dist/middle_layer/middle_layer.d.ts.map +1 -1
  34. package/dist/middle_layer/middle_layer.js +146 -4
  35. package/dist/middle_layer/middle_layer.js.map +1 -1
  36. package/dist/middle_layer/project.cjs +1 -0
  37. package/dist/middle_layer/project.cjs.map +1 -1
  38. package/dist/middle_layer/project.d.ts +2 -2
  39. package/dist/middle_layer/project.js +4 -4
  40. package/dist/middle_layer/project.js.map +1 -1
  41. package/dist/middle_layer/sharing_list.d.ts +1 -1
  42. package/dist/model/args.cjs +9 -28
  43. package/dist/model/args.cjs.map +1 -1
  44. package/dist/model/args.js +9 -28
  45. package/dist/model/args.js.map +1 -1
  46. package/dist/model/index.cjs +7 -0
  47. package/dist/model/index.d.ts +4 -1
  48. package/dist/model/index.js +4 -1
  49. package/dist/model/project_helper.cjs +111 -0
  50. package/dist/model/project_helper.cjs.map +1 -1
  51. package/dist/model/project_helper.d.ts +76 -1
  52. package/dist/model/project_helper.d.ts.map +1 -1
  53. package/dist/model/project_helper.js +111 -0
  54. package/dist/model/project_helper.js.map +1 -1
  55. package/dist/model/project_model.d.ts +1 -1
  56. package/dist/model/sharing_model.d.ts +1 -1
  57. package/dist/model/template_apply.cjs +67 -0
  58. package/dist/model/template_apply.cjs.map +1 -0
  59. package/dist/model/template_apply.d.ts +50 -0
  60. package/dist/model/template_apply.d.ts.map +1 -0
  61. package/dist/model/template_apply.js +64 -0
  62. package/dist/model/template_apply.js.map +1 -0
  63. package/dist/model/template_export.cjs +82 -0
  64. package/dist/model/template_export.cjs.map +1 -0
  65. package/dist/model/template_export.d.ts +9 -0
  66. package/dist/model/template_export.d.ts.map +1 -0
  67. package/dist/model/template_export.js +82 -0
  68. package/dist/model/template_export.js.map +1 -0
  69. package/dist/model/template_parser.cjs +74 -0
  70. package/dist/model/template_parser.cjs.map +1 -0
  71. package/dist/model/template_parser.d.ts +43 -0
  72. package/dist/model/template_parser.d.ts.map +1 -0
  73. package/dist/model/template_parser.js +72 -0
  74. package/dist/model/template_parser.js.map +1 -0
  75. package/dist/model/template_resolve.cjs +134 -0
  76. package/dist/model/template_resolve.cjs.map +1 -0
  77. package/dist/model/template_resolve.d.ts +193 -0
  78. package/dist/model/template_resolve.d.ts.map +1 -0
  79. package/dist/model/template_resolve.js +133 -0
  80. package/dist/model/template_resolve.js.map +1 -0
  81. package/dist/model/template_serializer.cjs +162 -0
  82. package/dist/model/template_serializer.cjs.map +1 -0
  83. package/dist/model/template_serializer.d.ts +17 -0
  84. package/dist/model/template_serializer.d.ts.map +1 -0
  85. package/dist/model/template_serializer.js +157 -0
  86. package/dist/model/template_serializer.js.map +1 -0
  87. package/dist/mutator/block-pack/block_pack.cjs +1 -1
  88. package/dist/mutator/block-pack/block_pack.js +1 -1
  89. package/dist/mutator/migration.cjs +1 -1
  90. package/dist/mutator/migration.js +1 -1
  91. package/dist/mutator/project.cjs +35 -5
  92. package/dist/mutator/project.cjs.map +1 -1
  93. package/dist/mutator/project.d.ts.map +1 -1
  94. package/dist/mutator/project.js +35 -5
  95. package/dist/mutator/project.js.map +1 -1
  96. package/dist/mutator/template_construct.cjs +77 -0
  97. package/dist/mutator/template_construct.cjs.map +1 -0
  98. package/dist/mutator/template_construct.js +77 -0
  99. package/dist/mutator/template_construct.js.map +1 -0
  100. package/dist/network_check/template.js +3 -3
  101. package/dist/network_check/template.js.map +1 -1
  102. package/package.json +17 -17
  103. package/src/block_registry/index.ts +1 -0
  104. package/src/block_registry/location_provider.test.ts +236 -0
  105. package/src/block_registry/location_provider.ts +124 -0
  106. package/src/block_registry/registry.ts +22 -0
  107. package/src/block_registry/template_provider.test.ts +275 -0
  108. package/src/block_registry/template_provider.ts +168 -0
  109. package/src/middle_layer/middle_layer.ts +192 -1
  110. package/src/middle_layer/project.ts +1 -1
  111. package/src/model/args.test.ts +26 -1
  112. package/src/model/args.ts +16 -31
  113. package/src/model/index.ts +21 -0
  114. package/src/model/project_helper.ts +238 -0
  115. package/src/model/project_helper_params_init.test.ts +249 -0
  116. package/src/model/template_apply.test.ts +65 -0
  117. package/src/model/template_apply.ts +128 -0
  118. package/src/model/template_export.test.ts +255 -0
  119. package/src/model/template_export.ts +136 -0
  120. package/src/model/template_parser.test.ts +264 -0
  121. package/src/model/template_parser.ts +108 -0
  122. package/src/model/template_resolve.test.ts +397 -0
  123. package/src/model/template_resolve.ts +346 -0
  124. package/src/model/template_serializer.test.ts +398 -0
  125. package/src/model/template_serializer.ts +232 -0
  126. package/src/model/template_serializer_fixtures.test.ts +274 -0
  127. package/src/mutator/project-v3.test.ts +11 -11
  128. package/src/mutator/project.ts +92 -5
  129. package/src/mutator/template_construct.test.ts +456 -0
  130. package/src/mutator/template_construct.ts +150 -0
@@ -1,6 +1,7 @@
1
1
  const require_registry = require("./registry.cjs");
2
2
  const require_registry_v2_provider = require("./registry-v2-provider.cjs");
3
3
  const require_well_known_registries = require("./well_known_registries.cjs");
4
+ const require_template_provider = require("./template_provider.cjs");
4
5
  exports.BlockPackRegistry = require_registry.BlockPackRegistry;
5
6
  exports.CentralBlockRegistry = require_well_known_registries.CentralBlockRegistry;
6
7
  exports.V1CentralDevSnapshotRegistry = require_well_known_registries.V1CentralDevSnapshotRegistry;
@@ -9,3 +10,5 @@ exports.V2RegistryProvider = require_registry_v2_provider.V2RegistryProvider;
9
10
  exports.getDevV1PacketMtime = require_registry.getDevV1PacketMtime;
10
11
  exports.getDevV2PacketMtime = require_registry.getDevV2PacketMtime;
11
12
  exports.getFromPackV2Mtime = require_registry.getFromPackV2Mtime;
13
+ exports.kindCapableRegistryIds = require_template_provider.kindCapableRegistryIds;
14
+ exports.templateBlockPackProvider = require_template_provider.templateBlockPackProvider;
@@ -1,4 +1,5 @@
1
1
  import { V2RegistryProvider } from "./registry-v2-provider.js";
2
2
  import { BlockPackRegistry, getDevV1PacketMtime, getDevV2PacketMtime, getFromPackV2Mtime } from "./registry.js";
3
3
  import { CentralBlockRegistry, V1CentralDevSnapshotRegistry, V1CentralRegistry } from "./well_known_registries.js";
4
- export { BlockPackRegistry, CentralBlockRegistry, V1CentralDevSnapshotRegistry, V1CentralRegistry, V2RegistryProvider, getDevV1PacketMtime, getDevV2PacketMtime, getFromPackV2Mtime };
4
+ import { KindAwareRegistry, kindCapableRegistryIds, templateBlockPackProvider } from "./template_provider.js";
5
+ export { BlockPackRegistry, CentralBlockRegistry, KindAwareRegistry, V1CentralDevSnapshotRegistry, V1CentralRegistry, V2RegistryProvider, getDevV1PacketMtime, getDevV2PacketMtime, getFromPackV2Mtime, kindCapableRegistryIds, templateBlockPackProvider };
@@ -1,4 +1,5 @@
1
1
  import { BlockPackRegistry, getDevV1PacketMtime, getDevV2PacketMtime, getFromPackV2Mtime } from "./registry.js";
2
2
  import { V2RegistryProvider } from "./registry-v2-provider.js";
3
3
  import { CentralBlockRegistry, V1CentralDevSnapshotRegistry, V1CentralRegistry } from "./well_known_registries.js";
4
- export { BlockPackRegistry, CentralBlockRegistry, V1CentralDevSnapshotRegistry, V1CentralRegistry, V2RegistryProvider, getDevV1PacketMtime, getDevV2PacketMtime, getFromPackV2Mtime };
4
+ import { kindCapableRegistryIds, templateBlockPackProvider } from "./template_provider.js";
5
+ export { BlockPackRegistry, CentralBlockRegistry, V1CentralDevSnapshotRegistry, V1CentralRegistry, V2RegistryProvider, getDevV1PacketMtime, getDevV2PacketMtime, getFromPackV2Mtime, kindCapableRegistryIds, templateBlockPackProvider };
@@ -0,0 +1,117 @@
1
+ const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
2
+ let _platforma_sdk_block_tools = require("@platforma-sdk/block-tools");
3
+ let node_path = require("node:path");
4
+ node_path = require_runtime.__toESM(node_path, 1);
5
+ let node_fs_promises = require("node:fs/promises");
6
+ node_fs_promises = require_runtime.__toESM(node_fs_promises, 1);
7
+ let node_url = require("node:url");
8
+ let _milaboratories_pl_model_common = require("@milaboratories/pl-model-common");
9
+ //#region src/block_registry/location_provider.ts
10
+ /**
11
+ * Read the block a template entry's `location` points at.
12
+ *
13
+ * The filesystem half of {@link BlockPackProvider}, and the reason a project built from
14
+ * locally developed blocks can be exported and applied without publishing anything. No
15
+ * registry is involved and nothing is searched: the entry named a place.
16
+ *
17
+ * Only `file:` is served. The document's grammar admits any scheme so that a template
18
+ * stays readable by a consumer that can fetch more than this one can, and an unknown
19
+ * scheme is reported rather than treated as a missing block — "I cannot read this" and
20
+ * "there is nothing there" send the reader to different places.
21
+ */
22
+ async function resolveBlockPackLocation(location) {
23
+ if ((0, _milaboratories_pl_model_common.parseBlockPackLocation)(location).scheme !== "file") return {
24
+ ok: false,
25
+ reason: "unsupported-scheme"
26
+ };
27
+ const dir = (0, node_url.fileURLToPath)(location);
28
+ const found = await readPackAt(dir);
29
+ if (found === void 0) return await exists(dir) ? {
30
+ ok: false,
31
+ reason: "not-a-block"
32
+ } : {
33
+ ok: false,
34
+ reason: "not-found"
35
+ };
36
+ return {
37
+ ok: true,
38
+ spec: found.spec,
39
+ title: found.description.meta.title
40
+ };
41
+ }
42
+ /**
43
+ * Identify what kind of block pack is at `dir`, by looking rather than by being told.
44
+ *
45
+ * Two layouts can sit behind one `location`, and they anchor differently — a packed
46
+ * block at the folder holding its `manifest.json`, a source block at the folder holding
47
+ * the `package.json` that names its components. The document deliberately records only
48
+ * the URI, so the layout is established here, where the filesystem is: encoding it in
49
+ * the file instead would freeze today's two shapes into the format, and a template
50
+ * written last month would name a layout that has since moved.
51
+ *
52
+ * A packed layout is checked first because it is the more specific one: a facade
53
+ * package's own folder holds `package.json` and, once packed, `block-pack/` beside it
54
+ * — never `manifest.json` directly.
55
+ *
56
+ * When neither matches, the two conventional subfolders are tried. Those are the same
57
+ * names the dev-block scanner probes, so a hand-written entry may name the block's own
58
+ * folder rather than the package inside it. Export never emits these: it writes the
59
+ * anchor the loader accepts, so there is nothing to guess on a round trip.
60
+ */
61
+ async function readPackAt(dir) {
62
+ const direct = await readPackExactlyAt(dir);
63
+ if (direct !== void 0) return direct;
64
+ for (const subfolder of ["block", "meta"]) {
65
+ const nested = await readPackExactlyAt(node_path.default.join(dir, subfolder));
66
+ if (nested !== void 0) return nested;
67
+ }
68
+ }
69
+ async function readPackExactlyAt(dir) {
70
+ if (await exists(node_path.default.join(dir, "manifest.json"))) {
71
+ const description = await (0, _platforma_sdk_block_tools.loadPackDescriptionFromManifest)(dir);
72
+ return {
73
+ spec: {
74
+ type: "from-pack-v2",
75
+ packUrl: (0, node_url.pathToFileURL)(dir).href
76
+ },
77
+ description
78
+ };
79
+ }
80
+ if (await hasBlockDescription(node_path.default.join(dir, "package.json"))) {
81
+ const description = await (0, _platforma_sdk_block_tools.loadPackDescription)(dir);
82
+ return {
83
+ spec: {
84
+ type: "dev-v2",
85
+ folder: dir
86
+ },
87
+ description
88
+ };
89
+ }
90
+ }
91
+ /**
92
+ * Whether this `package.json` describes a block at all.
93
+ *
94
+ * Checked before handing the folder to the loader so that an ordinary npm package —
95
+ * anything with a `package.json` and no block description — comes back as "not a
96
+ * block" rather than as a thrown parse failure from inside the loader.
97
+ */
98
+ async function hasBlockDescription(packageJsonPath) {
99
+ try {
100
+ const parsed = JSON.parse(await node_fs_promises.default.readFile(packageJsonPath, "utf-8"));
101
+ return typeof parsed === "object" && parsed !== null && "block" in parsed;
102
+ } catch {
103
+ return false;
104
+ }
105
+ }
106
+ async function exists(target) {
107
+ try {
108
+ await node_fs_promises.default.stat(target);
109
+ return true;
110
+ } catch {
111
+ return false;
112
+ }
113
+ }
114
+ //#endregion
115
+ exports.resolveBlockPackLocation = resolveBlockPackLocation;
116
+
117
+ //# sourceMappingURL=location_provider.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"location_provider.cjs","names":["path","fsp"],"sources":["../../src/block_registry/location_provider.ts"],"sourcesContent":["import fsp from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\nimport type { BlockPackLocationReference } from \"@milaboratories/pl-model-common\";\nimport { parseBlockPackLocation } from \"@milaboratories/pl-model-common\";\nimport type { BlockPackSpec } from \"@milaboratories/pl-model-middle-layer\";\nimport type { BlockPackDescriptionAbsolute } from \"@platforma-sdk/block-tools\";\nimport { loadPackDescription, loadPackDescriptionFromManifest } from \"@platforma-sdk/block-tools\";\nimport type { LocationResolution } from \"../model/template_resolve\";\n\n/**\n * Read the block a template entry's `location` points at.\n *\n * The filesystem half of {@link BlockPackProvider}, and the reason a project built from\n * locally developed blocks can be exported and applied without publishing anything. No\n * registry is involved and nothing is searched: the entry named a place.\n *\n * Only `file:` is served. The document's grammar admits any scheme so that a template\n * stays readable by a consumer that can fetch more than this one can, and an unknown\n * scheme is reported rather than treated as a missing block — \"I cannot read this\" and\n * \"there is nothing there\" send the reader to different places.\n */\nexport async function resolveBlockPackLocation(\n location: BlockPackLocationReference,\n): Promise<LocationResolution> {\n if (parseBlockPackLocation(location).scheme !== \"file\") {\n return { ok: false, reason: \"unsupported-scheme\" };\n }\n\n // The value is a URL, never a path: `fileURLToPath` is what turns `%20` back into the\n // space that was in the folder name. Reading `pathname` directly would look right and\n // fail on any path with a space in it.\n const dir = fileURLToPath(location);\n\n const found = await readPackAt(dir);\n if (found === undefined) {\n return (await exists(dir))\n ? { ok: false, reason: \"not-a-block\" }\n : { ok: false, reason: \"not-found\" };\n }\n\n return { ok: true, spec: found.spec, title: found.description.meta.title };\n}\n\n/** One readable block pack: which spec addresses it, and what it declares. */\ntype FoundPack = {\n readonly spec: BlockPackSpec;\n readonly description: BlockPackDescriptionAbsolute;\n};\n\n/**\n * Identify what kind of block pack is at `dir`, by looking rather than by being told.\n *\n * Two layouts can sit behind one `location`, and they anchor differently — a packed\n * block at the folder holding its `manifest.json`, a source block at the folder holding\n * the `package.json` that names its components. The document deliberately records only\n * the URI, so the layout is established here, where the filesystem is: encoding it in\n * the file instead would freeze today's two shapes into the format, and a template\n * written last month would name a layout that has since moved.\n *\n * A packed layout is checked first because it is the more specific one: a facade\n * package's own folder holds `package.json` and, once packed, `block-pack/` beside it\n * — never `manifest.json` directly.\n *\n * When neither matches, the two conventional subfolders are tried. Those are the same\n * names the dev-block scanner probes, so a hand-written entry may name the block's own\n * folder rather than the package inside it. Export never emits these: it writes the\n * anchor the loader accepts, so there is nothing to guess on a round trip.\n */\nasync function readPackAt(dir: string): Promise<FoundPack | undefined> {\n const direct = await readPackExactlyAt(dir);\n if (direct !== undefined) return direct;\n\n for (const subfolder of [\"block\", \"meta\"]) {\n const nested = await readPackExactlyAt(path.join(dir, subfolder));\n if (nested !== undefined) return nested;\n }\n\n return undefined;\n}\n\nasync function readPackExactlyAt(dir: string): Promise<FoundPack | undefined> {\n if (await exists(path.join(dir, \"manifest.json\"))) {\n const description = await loadPackDescriptionFromManifest(dir);\n return {\n // The pack directory is what the preparer expects to be handed, and it is where\n // the manifest was just read from — not something reconstructed from a parent.\n spec: { type: \"from-pack-v2\", packUrl: pathToFileURL(dir).href },\n description,\n };\n }\n\n if (await hasBlockDescription(path.join(dir, \"package.json\"))) {\n const description = await loadPackDescription(dir);\n return { spec: { type: \"dev-v2\", folder: dir }, description };\n }\n\n return undefined;\n}\n\n/**\n * Whether this `package.json` describes a block at all.\n *\n * Checked before handing the folder to the loader so that an ordinary npm package —\n * anything with a `package.json` and no block description — comes back as \"not a\n * block\" rather than as a thrown parse failure from inside the loader.\n */\nasync function hasBlockDescription(packageJsonPath: string): Promise<boolean> {\n try {\n const parsed: unknown = JSON.parse(await fsp.readFile(packageJsonPath, \"utf-8\"));\n return typeof parsed === \"object\" && parsed !== null && \"block\" in parsed;\n } catch {\n return false;\n }\n}\n\nasync function exists(target: string): Promise<boolean> {\n try {\n await fsp.stat(target);\n return true;\n } catch {\n return false;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAsBA,eAAsB,yBACpB,UAC6B;CAC7B,KAAA,GAAA,gCAAA,uBAAA,CAA2B,QAAQ,CAAC,CAAC,WAAW,QAC9C,OAAO;EAAE,IAAI;EAAO,QAAQ;CAAqB;CAMnD,MAAM,OAAA,GAAA,SAAA,cAAA,CAAoB,QAAQ;CAElC,MAAM,QAAQ,MAAM,WAAW,GAAG;CAClC,IAAI,UAAU,KAAA,GACZ,OAAQ,MAAM,OAAO,GAAG,IACpB;EAAE,IAAI;EAAO,QAAQ;CAAc,IACnC;EAAE,IAAI;EAAO,QAAQ;CAAY;CAGvC,OAAO;EAAE,IAAI;EAAM,MAAM,MAAM;EAAM,OAAO,MAAM,YAAY,KAAK;CAAM;AAC3E;;;;;;;;;;;;;;;;;;;;AA2BA,eAAe,WAAW,KAA6C;CACrE,MAAM,SAAS,MAAM,kBAAkB,GAAG;CAC1C,IAAI,WAAW,KAAA,GAAW,OAAO;CAEjC,KAAK,MAAM,aAAa,CAAC,SAAS,MAAM,GAAG;EACzC,MAAM,SAAS,MAAM,kBAAkBA,UAAAA,QAAK,KAAK,KAAK,SAAS,CAAC;EAChE,IAAI,WAAW,KAAA,GAAW,OAAO;CACnC;AAGF;AAEA,eAAe,kBAAkB,KAA6C;CAC5E,IAAI,MAAM,OAAOA,UAAAA,QAAK,KAAK,KAAK,eAAe,CAAC,GAAG;EACjD,MAAM,cAAc,OAAA,GAAA,2BAAA,gCAAA,CAAsC,GAAG;EAC7D,OAAO;GAGL,MAAM;IAAE,MAAM;IAAgB,UAAA,GAAA,SAAA,cAAA,CAAuB,GAAG,CAAC,CAAC;GAAK;GAC/D;EACF;CACF;CAEA,IAAI,MAAM,oBAAoBA,UAAAA,QAAK,KAAK,KAAK,cAAc,CAAC,GAAG;EAC7D,MAAM,cAAc,OAAA,GAAA,2BAAA,oBAAA,CAA0B,GAAG;EACjD,OAAO;GAAE,MAAM;IAAE,MAAM;IAAU,QAAQ;GAAI;GAAG;EAAY;CAC9D;AAGF;;;;;;;;AASA,eAAe,oBAAoB,iBAA2C;CAC5E,IAAI;EACF,MAAM,SAAkB,KAAK,MAAM,MAAMC,iBAAAA,QAAI,SAAS,iBAAiB,OAAO,CAAC;EAC/E,OAAO,OAAO,WAAW,YAAY,WAAW,QAAQ,WAAW;CACrE,QAAQ;EACN,OAAO;CACT;AACF;AAEA,eAAe,OAAO,QAAkC;CACtD,IAAI;EACF,MAAMA,iBAAAA,QAAI,KAAK,MAAM;EACrB,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF"}
@@ -0,0 +1,114 @@
1
+ import { loadPackDescription, loadPackDescriptionFromManifest } from "@platforma-sdk/block-tools";
2
+ import path from "node:path";
3
+ import fsp from "node:fs/promises";
4
+ import { fileURLToPath, pathToFileURL } from "node:url";
5
+ import { parseBlockPackLocation } from "@milaboratories/pl-model-common";
6
+ //#region src/block_registry/location_provider.ts
7
+ /**
8
+ * Read the block a template entry's `location` points at.
9
+ *
10
+ * The filesystem half of {@link BlockPackProvider}, and the reason a project built from
11
+ * locally developed blocks can be exported and applied without publishing anything. No
12
+ * registry is involved and nothing is searched: the entry named a place.
13
+ *
14
+ * Only `file:` is served. The document's grammar admits any scheme so that a template
15
+ * stays readable by a consumer that can fetch more than this one can, and an unknown
16
+ * scheme is reported rather than treated as a missing block — "I cannot read this" and
17
+ * "there is nothing there" send the reader to different places.
18
+ */
19
+ async function resolveBlockPackLocation(location) {
20
+ if (parseBlockPackLocation(location).scheme !== "file") return {
21
+ ok: false,
22
+ reason: "unsupported-scheme"
23
+ };
24
+ const dir = fileURLToPath(location);
25
+ const found = await readPackAt(dir);
26
+ if (found === void 0) return await exists(dir) ? {
27
+ ok: false,
28
+ reason: "not-a-block"
29
+ } : {
30
+ ok: false,
31
+ reason: "not-found"
32
+ };
33
+ return {
34
+ ok: true,
35
+ spec: found.spec,
36
+ title: found.description.meta.title
37
+ };
38
+ }
39
+ /**
40
+ * Identify what kind of block pack is at `dir`, by looking rather than by being told.
41
+ *
42
+ * Two layouts can sit behind one `location`, and they anchor differently — a packed
43
+ * block at the folder holding its `manifest.json`, a source block at the folder holding
44
+ * the `package.json` that names its components. The document deliberately records only
45
+ * the URI, so the layout is established here, where the filesystem is: encoding it in
46
+ * the file instead would freeze today's two shapes into the format, and a template
47
+ * written last month would name a layout that has since moved.
48
+ *
49
+ * A packed layout is checked first because it is the more specific one: a facade
50
+ * package's own folder holds `package.json` and, once packed, `block-pack/` beside it
51
+ * — never `manifest.json` directly.
52
+ *
53
+ * When neither matches, the two conventional subfolders are tried. Those are the same
54
+ * names the dev-block scanner probes, so a hand-written entry may name the block's own
55
+ * folder rather than the package inside it. Export never emits these: it writes the
56
+ * anchor the loader accepts, so there is nothing to guess on a round trip.
57
+ */
58
+ async function readPackAt(dir) {
59
+ const direct = await readPackExactlyAt(dir);
60
+ if (direct !== void 0) return direct;
61
+ for (const subfolder of ["block", "meta"]) {
62
+ const nested = await readPackExactlyAt(path.join(dir, subfolder));
63
+ if (nested !== void 0) return nested;
64
+ }
65
+ }
66
+ async function readPackExactlyAt(dir) {
67
+ if (await exists(path.join(dir, "manifest.json"))) {
68
+ const description = await loadPackDescriptionFromManifest(dir);
69
+ return {
70
+ spec: {
71
+ type: "from-pack-v2",
72
+ packUrl: pathToFileURL(dir).href
73
+ },
74
+ description
75
+ };
76
+ }
77
+ if (await hasBlockDescription(path.join(dir, "package.json"))) {
78
+ const description = await loadPackDescription(dir);
79
+ return {
80
+ spec: {
81
+ type: "dev-v2",
82
+ folder: dir
83
+ },
84
+ description
85
+ };
86
+ }
87
+ }
88
+ /**
89
+ * Whether this `package.json` describes a block at all.
90
+ *
91
+ * Checked before handing the folder to the loader so that an ordinary npm package —
92
+ * anything with a `package.json` and no block description — comes back as "not a
93
+ * block" rather than as a thrown parse failure from inside the loader.
94
+ */
95
+ async function hasBlockDescription(packageJsonPath) {
96
+ try {
97
+ const parsed = JSON.parse(await fsp.readFile(packageJsonPath, "utf-8"));
98
+ return typeof parsed === "object" && parsed !== null && "block" in parsed;
99
+ } catch {
100
+ return false;
101
+ }
102
+ }
103
+ async function exists(target) {
104
+ try {
105
+ await fsp.stat(target);
106
+ return true;
107
+ } catch {
108
+ return false;
109
+ }
110
+ }
111
+ //#endregion
112
+ export { resolveBlockPackLocation };
113
+
114
+ //# sourceMappingURL=location_provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"location_provider.js","names":[],"sources":["../../src/block_registry/location_provider.ts"],"sourcesContent":["import fsp from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\nimport type { BlockPackLocationReference } from \"@milaboratories/pl-model-common\";\nimport { parseBlockPackLocation } from \"@milaboratories/pl-model-common\";\nimport type { BlockPackSpec } from \"@milaboratories/pl-model-middle-layer\";\nimport type { BlockPackDescriptionAbsolute } from \"@platforma-sdk/block-tools\";\nimport { loadPackDescription, loadPackDescriptionFromManifest } from \"@platforma-sdk/block-tools\";\nimport type { LocationResolution } from \"../model/template_resolve\";\n\n/**\n * Read the block a template entry's `location` points at.\n *\n * The filesystem half of {@link BlockPackProvider}, and the reason a project built from\n * locally developed blocks can be exported and applied without publishing anything. No\n * registry is involved and nothing is searched: the entry named a place.\n *\n * Only `file:` is served. The document's grammar admits any scheme so that a template\n * stays readable by a consumer that can fetch more than this one can, and an unknown\n * scheme is reported rather than treated as a missing block — \"I cannot read this\" and\n * \"there is nothing there\" send the reader to different places.\n */\nexport async function resolveBlockPackLocation(\n location: BlockPackLocationReference,\n): Promise<LocationResolution> {\n if (parseBlockPackLocation(location).scheme !== \"file\") {\n return { ok: false, reason: \"unsupported-scheme\" };\n }\n\n // The value is a URL, never a path: `fileURLToPath` is what turns `%20` back into the\n // space that was in the folder name. Reading `pathname` directly would look right and\n // fail on any path with a space in it.\n const dir = fileURLToPath(location);\n\n const found = await readPackAt(dir);\n if (found === undefined) {\n return (await exists(dir))\n ? { ok: false, reason: \"not-a-block\" }\n : { ok: false, reason: \"not-found\" };\n }\n\n return { ok: true, spec: found.spec, title: found.description.meta.title };\n}\n\n/** One readable block pack: which spec addresses it, and what it declares. */\ntype FoundPack = {\n readonly spec: BlockPackSpec;\n readonly description: BlockPackDescriptionAbsolute;\n};\n\n/**\n * Identify what kind of block pack is at `dir`, by looking rather than by being told.\n *\n * Two layouts can sit behind one `location`, and they anchor differently — a packed\n * block at the folder holding its `manifest.json`, a source block at the folder holding\n * the `package.json` that names its components. The document deliberately records only\n * the URI, so the layout is established here, where the filesystem is: encoding it in\n * the file instead would freeze today's two shapes into the format, and a template\n * written last month would name a layout that has since moved.\n *\n * A packed layout is checked first because it is the more specific one: a facade\n * package's own folder holds `package.json` and, once packed, `block-pack/` beside it\n * — never `manifest.json` directly.\n *\n * When neither matches, the two conventional subfolders are tried. Those are the same\n * names the dev-block scanner probes, so a hand-written entry may name the block's own\n * folder rather than the package inside it. Export never emits these: it writes the\n * anchor the loader accepts, so there is nothing to guess on a round trip.\n */\nasync function readPackAt(dir: string): Promise<FoundPack | undefined> {\n const direct = await readPackExactlyAt(dir);\n if (direct !== undefined) return direct;\n\n for (const subfolder of [\"block\", \"meta\"]) {\n const nested = await readPackExactlyAt(path.join(dir, subfolder));\n if (nested !== undefined) return nested;\n }\n\n return undefined;\n}\n\nasync function readPackExactlyAt(dir: string): Promise<FoundPack | undefined> {\n if (await exists(path.join(dir, \"manifest.json\"))) {\n const description = await loadPackDescriptionFromManifest(dir);\n return {\n // The pack directory is what the preparer expects to be handed, and it is where\n // the manifest was just read from — not something reconstructed from a parent.\n spec: { type: \"from-pack-v2\", packUrl: pathToFileURL(dir).href },\n description,\n };\n }\n\n if (await hasBlockDescription(path.join(dir, \"package.json\"))) {\n const description = await loadPackDescription(dir);\n return { spec: { type: \"dev-v2\", folder: dir }, description };\n }\n\n return undefined;\n}\n\n/**\n * Whether this `package.json` describes a block at all.\n *\n * Checked before handing the folder to the loader so that an ordinary npm package —\n * anything with a `package.json` and no block description — comes back as \"not a\n * block\" rather than as a thrown parse failure from inside the loader.\n */\nasync function hasBlockDescription(packageJsonPath: string): Promise<boolean> {\n try {\n const parsed: unknown = JSON.parse(await fsp.readFile(packageJsonPath, \"utf-8\"));\n return typeof parsed === \"object\" && parsed !== null && \"block\" in parsed;\n } catch {\n return false;\n }\n}\n\nasync function exists(target: string): Promise<boolean> {\n try {\n await fsp.stat(target);\n return true;\n } catch {\n return false;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAsBA,eAAsB,yBACpB,UAC6B;CAC7B,IAAI,uBAAuB,QAAQ,CAAC,CAAC,WAAW,QAC9C,OAAO;EAAE,IAAI;EAAO,QAAQ;CAAqB;CAMnD,MAAM,MAAM,cAAc,QAAQ;CAElC,MAAM,QAAQ,MAAM,WAAW,GAAG;CAClC,IAAI,UAAU,KAAA,GACZ,OAAQ,MAAM,OAAO,GAAG,IACpB;EAAE,IAAI;EAAO,QAAQ;CAAc,IACnC;EAAE,IAAI;EAAO,QAAQ;CAAY;CAGvC,OAAO;EAAE,IAAI;EAAM,MAAM,MAAM;EAAM,OAAO,MAAM,YAAY,KAAK;CAAM;AAC3E;;;;;;;;;;;;;;;;;;;;AA2BA,eAAe,WAAW,KAA6C;CACrE,MAAM,SAAS,MAAM,kBAAkB,GAAG;CAC1C,IAAI,WAAW,KAAA,GAAW,OAAO;CAEjC,KAAK,MAAM,aAAa,CAAC,SAAS,MAAM,GAAG;EACzC,MAAM,SAAS,MAAM,kBAAkB,KAAK,KAAK,KAAK,SAAS,CAAC;EAChE,IAAI,WAAW,KAAA,GAAW,OAAO;CACnC;AAGF;AAEA,eAAe,kBAAkB,KAA6C;CAC5E,IAAI,MAAM,OAAO,KAAK,KAAK,KAAK,eAAe,CAAC,GAAG;EACjD,MAAM,cAAc,MAAM,gCAAgC,GAAG;EAC7D,OAAO;GAGL,MAAM;IAAE,MAAM;IAAgB,SAAS,cAAc,GAAG,CAAC,CAAC;GAAK;GAC/D;EACF;CACF;CAEA,IAAI,MAAM,oBAAoB,KAAK,KAAK,KAAK,cAAc,CAAC,GAAG;EAC7D,MAAM,cAAc,MAAM,oBAAoB,GAAG;EACjD,OAAO;GAAE,MAAM;IAAE,MAAM;IAAU,QAAQ;GAAI;GAAG;EAAY;CAC9D;AAGF;;;;;;;;AASA,eAAe,oBAAoB,iBAA2C;CAC5E,IAAI;EACF,MAAM,SAAkB,KAAK,MAAM,MAAM,IAAI,SAAS,iBAAiB,OAAO,CAAC;EAC/E,OAAO,OAAO,WAAW,YAAY,WAAW,QAAQ,WAAW;CACrE,QAAQ;EACN,OAAO;CACT;AACF;AAEA,eAAe,OAAO,QAAkC;CACtD,IAAI;EACF,MAAM,IAAI,KAAK,MAAM;EACrB,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF"}
@@ -216,6 +216,19 @@ var BlockPackRegistry = class {
216
216
  if (regSpec.type !== "remote-v2") throw new Error(`Only "remote-v2" registries support specific package version overview retrieval.`);
217
217
  return await this.v2Provider.getRegistry(regSpec.url).getSpecificOverview(blockId, channel);
218
218
  }
219
+ /**
220
+ * Resolve a block-kind reference (`{name}@{selector}`) against a `remote-v2`
221
+ * registry to a concrete `from-registry-v2` block spec. Thin facade cloned
222
+ * from {@link getOverview}: resolve `registryId` → assert `remote-v2` →
223
+ * delegate to the reader's pure-core-backed `resolveKind`. All version-math
224
+ * lives in `@platforma-sdk/block-tools` (no backend involvement).
225
+ */
226
+ async resolveKind(registryId, ref, { allowUnstable }) {
227
+ const regSpec = this.registries.find((reg) => reg.id === registryId)?.spec;
228
+ if (!regSpec) throw new Error(`Registry with id "${registryId}" not found`);
229
+ if (regSpec.type !== "remote-v2") throw new Error(`Only "remote-v2" registries support block-kind resolution.`);
230
+ return await this.v2Provider.getRegistry(regSpec.url).resolveKind(ref, { allowUnstable });
231
+ }
219
232
  };
220
233
  //#endregion
221
234
  exports.BlockPackRegistry = BlockPackRegistry;
@@ -1 +1 @@
1
- {"version":3,"file":"registry.cjs","names":["fs","LegacyDevBlockPackFiles","path","RegistryV1","AnyChannel","StableChannel","YAML","deriveRequiredCapabilities"],"sources":["../../src/block_registry/registry.ts"],"sourcesContent":["import type { Dispatcher } from \"undici\";\nimport { request } from \"undici\";\nimport type { BlockPackDescriptionAbsolute } from \"@platforma-sdk/block-tools\";\nimport { embedBlockPackMetaAbsoluteBytes, RegistryV1 } from \"@platforma-sdk/block-tools\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport YAML from \"yaml\";\nimport { assertNever } from \"@milaboratories/ts-helpers\";\nimport { LegacyDevBlockPackFiles } from \"../dev_env\";\nimport { tryLoadPackDescription } from \"@platforma-sdk/block-tools\";\nimport { deriveRequiredCapabilities } from \"../mutator/block-pack/required_capabilities\";\nimport type { V2RegistryProvider } from \"./registry-v2-provider\";\nimport type {\n BlockPackId,\n BlockPackListing,\n BlockPackOverview,\n RegistryEntry,\n RegistryStatus,\n SingleBlockPackOverview,\n} from \"@milaboratories/pl-model-middle-layer\";\nimport { AnyChannel, StableChannel } from \"@milaboratories/pl-model-middle-layer\";\n\nasync function getFileContent(path: string) {\n try {\n return await fs.promises.readFile(path, \"utf8\");\n } catch (error: unknown) {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") {\n return undefined;\n }\n throw error;\n }\n}\n\nasync function getFileStat(path: string) {\n try {\n return await fs.promises.stat(path, { bigint: true });\n } catch (error: unknown) {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") {\n return undefined;\n }\n throw error;\n }\n}\n\nexport async function getDevV1PacketMtime(devPath: string): Promise<string> {\n let mtime = 0n;\n for (const f of LegacyDevBlockPackFiles) {\n const fullPath = path.join(devPath, ...f);\n const stat = await getFileStat(fullPath);\n if (stat === undefined) continue;\n if (mtime < stat.mtimeNs) mtime = stat.mtimeNs;\n }\n return mtime.toString();\n}\n\nexport async function getDevV2PacketMtime(\n description: BlockPackDescriptionAbsolute,\n): Promise<string> {\n const wfStats = await fs.promises.stat(description.components.workflow.main.file, {\n bigint: true,\n });\n const modelStats = await fs.promises.stat(description.components.model.file, { bigint: true });\n return (wfStats.mtimeNs > modelStats.mtimeNs ? wfStats.mtimeNs : modelStats.mtimeNs).toString();\n}\n\n/** Mtime for a `from-pack-v2` locator: prefer the manifest `timestamp`, else\n * stat the `ui.tgz` archive. Must be called identically by `block_pack.ts`\n * prepare (which bakes the result into the spec) and the update watcher (which\n * recomputes it to diff against the stored value) — divergence makes the block\n * flap perpetually \"update available\". */\nexport async function getFromPackV2Mtime(packDir: string, uiTgzPath: string): Promise<string> {\n try {\n const manifestPath = path.join(packDir, \"manifest.json\");\n const raw = JSON.parse(await fs.promises.readFile(manifestPath, { encoding: \"utf-8\" })) as {\n timestamp?: unknown;\n };\n if (typeof raw.timestamp === \"number\") return String(raw.timestamp);\n } catch {\n // fall through to stat\n }\n const stat = await fs.promises.stat(uiTgzPath, { bigint: true });\n return stat.mtimeNs.toString();\n}\n\nexport class BlockPackRegistry {\n constructor(\n private readonly v2Provider: V2RegistryProvider,\n private readonly registries: RegistryEntry[],\n private readonly http?: Dispatcher,\n ) {}\n\n private async getPackagesForRoot(\n regEntry: RegistryEntry,\n options?: { signal?: AbortSignal },\n ): Promise<BlockPackOverview[]> {\n const result: BlockPackOverview[] = [];\n const regSpec = regEntry.spec;\n switch (regSpec.type) {\n case \"remote-v1\": {\n const httpOptions = this.http !== undefined ? { dispatcher: this.http } : {};\n\n const overviewResponse = await request(`${regSpec.url}/${RegistryV1.GlobalOverviewPath}`, {\n ...httpOptions,\n signal: options?.signal,\n });\n const overview = (await overviewResponse.body.json()) as RegistryV1.GlobalOverview;\n for (const overviewEntry of overview) {\n const { organization, package: pkg, latestMeta, latestVersion } = overviewEntry;\n const id = {\n organization,\n name: pkg,\n version: latestVersion,\n };\n const latestOverview: SingleBlockPackOverview = {\n id,\n meta: {\n title: (latestMeta[\"title\"] as string | undefined) ?? \"No title\",\n description: (latestMeta[\"description\"] as string | undefined) ?? \"No Description\",\n organization: {\n name: organization,\n url: \"https://unknown.com\",\n },\n },\n spec: {\n type: \"from-registry-v1\",\n id,\n registryUrl: regSpec.url,\n },\n };\n result.push({\n registryId: regEntry.id,\n id,\n latestByChannel: {\n [AnyChannel]: latestOverview,\n [StableChannel]: latestOverview,\n },\n allVersions: overviewEntry.allVersions.map((v) => ({ version: v, channels: [] })),\n });\n }\n return result;\n }\n\n case \"remote-v2\":\n return (\n await this.v2Provider.getRegistry(regSpec.url).listBlockPacks({ signal: options?.signal })\n ).map((e) => ({\n ...e,\n registryId: regEntry.id,\n }));\n // e.latestByChannel[StableChannel]\n // ? {\n // ...e,\n // registryId: regEntry.id,\n // }\n // : {\n // ...e,\n // latestByChannel: {\n // ...e.latestByChannel,\n // [StableChannel]: ((s: SingleBlockPackOverview) => {\n // if (s.spec.type === 'from-registry-v2') {\n // return { ...s, spec: { ...s.spec, channel: StableChannel } };\n // }\n\n // return s;\n // })(e.latestByChannel[AnyChannel]),\n // },\n // registryId: regEntry.id,\n // },\n\n case \"local-dev\":\n for (const entry of await fs.promises.readdir(regSpec.path, { withFileTypes: true })) {\n if (!entry.isDirectory()) continue;\n const devPath = path.join(regSpec.path, entry.name);\n\n const legacyYamlContent = await getFileContent(\n path.join(devPath, RegistryV1.PlPackageYamlConfigFile),\n );\n if (legacyYamlContent !== undefined) {\n const config = RegistryV1.PlPackageConfigData.parse(YAML.parse(legacyYamlContent));\n\n const mtime = await getDevV1PacketMtime(devPath);\n\n const id = {\n organization: config.organization,\n name: config.package,\n version: \"DEV\",\n };\n\n const latestOverview: SingleBlockPackOverview = {\n id,\n meta: {\n title: (config.meta[\"title\"] as string) ?? \"No title\",\n description: (config.meta[\"description\"] as string) ?? \"No Description\",\n organization: {\n name: config.organization,\n url: \"https://unknown.com\",\n },\n },\n spec: {\n type: \"dev-v2\",\n folder: devPath,\n mtime,\n },\n };\n\n result.push({\n registryId: regEntry.id,\n id,\n latestByChannel: {\n [AnyChannel]: latestOverview,\n [StableChannel]: latestOverview,\n },\n allVersions: [],\n });\n } else {\n let actualDevPath = devPath;\n let v2Description = await tryLoadPackDescription(actualDevPath);\n\n if (v2Description === undefined)\n // iterating over expected subfolder names where block developer may put root block-pack package\n for (const bpSubfolder of [\"block\", \"meta\"]) {\n actualDevPath = path.join(devPath, bpSubfolder);\n v2Description = await tryLoadPackDescription(actualDevPath);\n if (v2Description !== undefined) break;\n }\n\n if (v2Description !== undefined) {\n const mtime = await getDevV2PacketMtime(v2Description);\n\n const meta = await embedBlockPackMetaAbsoluteBytes(v2Description.meta);\n\n // `tryLoadPackDescription` reads `package.json#block.meta` —\n // the dev SOURCE meta the developer wrote.\n // `requiredCapabilities` is a PACK-TIME artifact written by\n // `block-tools pack` into `block-pack/manifest.json`.\n // So for any dev block, the source meta never carries the field.\n //\n // To surface the gate at listing time without requiring the\n // developer to re-pack on every workflow change, derive from\n // the compiled workflow bytes directly.\n if (meta.requiredCapabilities === undefined) {\n const workflowBytes = await fs.promises.readFile(\n v2Description.components.workflow.main.file,\n );\n const derived = deriveRequiredCapabilities(workflowBytes);\n if (derived !== undefined) meta.requiredCapabilities = derived;\n }\n\n const latestOverview: SingleBlockPackOverview = {\n id: v2Description.id,\n meta,\n featureFlags: v2Description.featureFlags,\n spec: {\n type: \"dev-v2\",\n folder: actualDevPath,\n mtime,\n },\n };\n\n result.push({\n registryId: regEntry.id,\n id: v2Description.id,\n latestByChannel: {\n [AnyChannel]: latestOverview,\n [StableChannel]: latestOverview,\n },\n allVersions: [],\n });\n }\n }\n\n continue;\n }\n return result;\n default:\n return assertNever(regSpec);\n }\n }\n\n public async listBlockPacks(options?: { signal?: AbortSignal }): Promise<BlockPackListing> {\n const blockPacks: BlockPackOverview[] = [];\n const registries: RegistryStatus[] = [];\n for (const regSpecs of this.registries) {\n registries.push({ ...regSpecs, status: \"online\" });\n blockPacks.push(...(await this.getPackagesForRoot(regSpecs, options)));\n }\n return { registries, blockPacks };\n }\n\n public async getOverview(\n registryId: string,\n blockId: BlockPackId,\n channel: string,\n ): Promise<SingleBlockPackOverview> {\n const regSpec = this.registries.find((reg) => reg.id === registryId)?.spec;\n if (!regSpec) throw new Error(`Registry with id \"${registryId}\" not found`);\n if (regSpec.type !== \"remote-v2\")\n throw new Error(\n `Only \"remote-v2\" registries support specific package version overview retrieval.`,\n );\n const reg = this.v2Provider.getRegistry(regSpec.url);\n return await reg.getSpecificOverview(blockId, channel);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"registry.cjs","names":["fs","LegacyDevBlockPackFiles","path","RegistryV1","AnyChannel","StableChannel","YAML","deriveRequiredCapabilities"],"sources":["../../src/block_registry/registry.ts"],"sourcesContent":["import type { Dispatcher } from \"undici\";\nimport { request } from \"undici\";\nimport type { BlockPackDescriptionAbsolute } from \"@platforma-sdk/block-tools\";\nimport { embedBlockPackMetaAbsoluteBytes, RegistryV1 } from \"@platforma-sdk/block-tools\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport YAML from \"yaml\";\nimport { assertNever } from \"@milaboratories/ts-helpers\";\nimport { LegacyDevBlockPackFiles } from \"../dev_env\";\nimport { tryLoadPackDescription } from \"@platforma-sdk/block-tools\";\nimport { deriveRequiredCapabilities } from \"../mutator/block-pack/required_capabilities\";\nimport type { V2RegistryProvider } from \"./registry-v2-provider\";\nimport type {\n BlockPackId,\n BlockPackListing,\n BlockPackOverview,\n BlockPackFromRegistryV2,\n RegistryEntry,\n RegistryStatus,\n SingleBlockPackOverview,\n} from \"@milaboratories/pl-model-middle-layer\";\nimport { AnyChannel, StableChannel } from \"@milaboratories/pl-model-middle-layer\";\nimport type { BlockKindReference } from \"@milaboratories/pl-model-common\";\n\nasync function getFileContent(path: string) {\n try {\n return await fs.promises.readFile(path, \"utf8\");\n } catch (error: unknown) {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") {\n return undefined;\n }\n throw error;\n }\n}\n\nasync function getFileStat(path: string) {\n try {\n return await fs.promises.stat(path, { bigint: true });\n } catch (error: unknown) {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") {\n return undefined;\n }\n throw error;\n }\n}\n\nexport async function getDevV1PacketMtime(devPath: string): Promise<string> {\n let mtime = 0n;\n for (const f of LegacyDevBlockPackFiles) {\n const fullPath = path.join(devPath, ...f);\n const stat = await getFileStat(fullPath);\n if (stat === undefined) continue;\n if (mtime < stat.mtimeNs) mtime = stat.mtimeNs;\n }\n return mtime.toString();\n}\n\nexport async function getDevV2PacketMtime(\n description: BlockPackDescriptionAbsolute,\n): Promise<string> {\n const wfStats = await fs.promises.stat(description.components.workflow.main.file, {\n bigint: true,\n });\n const modelStats = await fs.promises.stat(description.components.model.file, { bigint: true });\n return (wfStats.mtimeNs > modelStats.mtimeNs ? wfStats.mtimeNs : modelStats.mtimeNs).toString();\n}\n\n/** Mtime for a `from-pack-v2` locator: prefer the manifest `timestamp`, else\n * stat the `ui.tgz` archive. Must be called identically by `block_pack.ts`\n * prepare (which bakes the result into the spec) and the update watcher (which\n * recomputes it to diff against the stored value) — divergence makes the block\n * flap perpetually \"update available\". */\nexport async function getFromPackV2Mtime(packDir: string, uiTgzPath: string): Promise<string> {\n try {\n const manifestPath = path.join(packDir, \"manifest.json\");\n const raw = JSON.parse(await fs.promises.readFile(manifestPath, { encoding: \"utf-8\" })) as {\n timestamp?: unknown;\n };\n if (typeof raw.timestamp === \"number\") return String(raw.timestamp);\n } catch {\n // fall through to stat\n }\n const stat = await fs.promises.stat(uiTgzPath, { bigint: true });\n return stat.mtimeNs.toString();\n}\n\nexport class BlockPackRegistry {\n constructor(\n private readonly v2Provider: V2RegistryProvider,\n private readonly registries: RegistryEntry[],\n private readonly http?: Dispatcher,\n ) {}\n\n private async getPackagesForRoot(\n regEntry: RegistryEntry,\n options?: { signal?: AbortSignal },\n ): Promise<BlockPackOverview[]> {\n const result: BlockPackOverview[] = [];\n const regSpec = regEntry.spec;\n switch (regSpec.type) {\n case \"remote-v1\": {\n const httpOptions = this.http !== undefined ? { dispatcher: this.http } : {};\n\n const overviewResponse = await request(`${regSpec.url}/${RegistryV1.GlobalOverviewPath}`, {\n ...httpOptions,\n signal: options?.signal,\n });\n const overview = (await overviewResponse.body.json()) as RegistryV1.GlobalOverview;\n for (const overviewEntry of overview) {\n const { organization, package: pkg, latestMeta, latestVersion } = overviewEntry;\n const id = {\n organization,\n name: pkg,\n version: latestVersion,\n };\n const latestOverview: SingleBlockPackOverview = {\n id,\n meta: {\n title: (latestMeta[\"title\"] as string | undefined) ?? \"No title\",\n description: (latestMeta[\"description\"] as string | undefined) ?? \"No Description\",\n organization: {\n name: organization,\n url: \"https://unknown.com\",\n },\n },\n spec: {\n type: \"from-registry-v1\",\n id,\n registryUrl: regSpec.url,\n },\n };\n result.push({\n registryId: regEntry.id,\n id,\n latestByChannel: {\n [AnyChannel]: latestOverview,\n [StableChannel]: latestOverview,\n },\n allVersions: overviewEntry.allVersions.map((v) => ({ version: v, channels: [] })),\n });\n }\n return result;\n }\n\n case \"remote-v2\":\n return (\n await this.v2Provider.getRegistry(regSpec.url).listBlockPacks({ signal: options?.signal })\n ).map((e) => ({\n ...e,\n registryId: regEntry.id,\n }));\n // e.latestByChannel[StableChannel]\n // ? {\n // ...e,\n // registryId: regEntry.id,\n // }\n // : {\n // ...e,\n // latestByChannel: {\n // ...e.latestByChannel,\n // [StableChannel]: ((s: SingleBlockPackOverview) => {\n // if (s.spec.type === 'from-registry-v2') {\n // return { ...s, spec: { ...s.spec, channel: StableChannel } };\n // }\n\n // return s;\n // })(e.latestByChannel[AnyChannel]),\n // },\n // registryId: regEntry.id,\n // },\n\n case \"local-dev\":\n for (const entry of await fs.promises.readdir(regSpec.path, { withFileTypes: true })) {\n if (!entry.isDirectory()) continue;\n const devPath = path.join(regSpec.path, entry.name);\n\n const legacyYamlContent = await getFileContent(\n path.join(devPath, RegistryV1.PlPackageYamlConfigFile),\n );\n if (legacyYamlContent !== undefined) {\n const config = RegistryV1.PlPackageConfigData.parse(YAML.parse(legacyYamlContent));\n\n const mtime = await getDevV1PacketMtime(devPath);\n\n const id = {\n organization: config.organization,\n name: config.package,\n version: \"DEV\",\n };\n\n const latestOverview: SingleBlockPackOverview = {\n id,\n meta: {\n title: (config.meta[\"title\"] as string) ?? \"No title\",\n description: (config.meta[\"description\"] as string) ?? \"No Description\",\n organization: {\n name: config.organization,\n url: \"https://unknown.com\",\n },\n },\n spec: {\n type: \"dev-v2\",\n folder: devPath,\n mtime,\n },\n };\n\n result.push({\n registryId: regEntry.id,\n id,\n latestByChannel: {\n [AnyChannel]: latestOverview,\n [StableChannel]: latestOverview,\n },\n allVersions: [],\n });\n } else {\n let actualDevPath = devPath;\n let v2Description = await tryLoadPackDescription(actualDevPath);\n\n if (v2Description === undefined)\n // iterating over expected subfolder names where block developer may put root block-pack package\n for (const bpSubfolder of [\"block\", \"meta\"]) {\n actualDevPath = path.join(devPath, bpSubfolder);\n v2Description = await tryLoadPackDescription(actualDevPath);\n if (v2Description !== undefined) break;\n }\n\n if (v2Description !== undefined) {\n const mtime = await getDevV2PacketMtime(v2Description);\n\n const meta = await embedBlockPackMetaAbsoluteBytes(v2Description.meta);\n\n // `tryLoadPackDescription` reads `package.json#block.meta` —\n // the dev SOURCE meta the developer wrote.\n // `requiredCapabilities` is a PACK-TIME artifact written by\n // `block-tools pack` into `block-pack/manifest.json`.\n // So for any dev block, the source meta never carries the field.\n //\n // To surface the gate at listing time without requiring the\n // developer to re-pack on every workflow change, derive from\n // the compiled workflow bytes directly.\n if (meta.requiredCapabilities === undefined) {\n const workflowBytes = await fs.promises.readFile(\n v2Description.components.workflow.main.file,\n );\n const derived = deriveRequiredCapabilities(workflowBytes);\n if (derived !== undefined) meta.requiredCapabilities = derived;\n }\n\n const latestOverview: SingleBlockPackOverview = {\n id: v2Description.id,\n meta,\n featureFlags: v2Description.featureFlags,\n spec: {\n type: \"dev-v2\",\n folder: actualDevPath,\n mtime,\n },\n };\n\n result.push({\n registryId: regEntry.id,\n id: v2Description.id,\n latestByChannel: {\n [AnyChannel]: latestOverview,\n [StableChannel]: latestOverview,\n },\n allVersions: [],\n });\n }\n }\n\n continue;\n }\n return result;\n default:\n return assertNever(regSpec);\n }\n }\n\n public async listBlockPacks(options?: { signal?: AbortSignal }): Promise<BlockPackListing> {\n const blockPacks: BlockPackOverview[] = [];\n const registries: RegistryStatus[] = [];\n for (const regSpecs of this.registries) {\n registries.push({ ...regSpecs, status: \"online\" });\n blockPacks.push(...(await this.getPackagesForRoot(regSpecs, options)));\n }\n return { registries, blockPacks };\n }\n\n public async getOverview(\n registryId: string,\n blockId: BlockPackId,\n channel: string,\n ): Promise<SingleBlockPackOverview> {\n const regSpec = this.registries.find((reg) => reg.id === registryId)?.spec;\n if (!regSpec) throw new Error(`Registry with id \"${registryId}\" not found`);\n if (regSpec.type !== \"remote-v2\")\n throw new Error(\n `Only \"remote-v2\" registries support specific package version overview retrieval.`,\n );\n const reg = this.v2Provider.getRegistry(regSpec.url);\n return await reg.getSpecificOverview(blockId, channel);\n }\n\n /**\n * Resolve a block-kind reference (`{name}@{selector}`) against a `remote-v2`\n * registry to a concrete `from-registry-v2` block spec. Thin facade cloned\n * from {@link getOverview}: resolve `registryId` → assert `remote-v2` →\n * delegate to the reader's pure-core-backed `resolveKind`. All version-math\n * lives in `@platforma-sdk/block-tools` (no backend involvement).\n */\n public async resolveKind(\n registryId: string,\n ref: BlockKindReference,\n { allowUnstable }: { allowUnstable: boolean },\n ): Promise<BlockPackFromRegistryV2> {\n const regSpec = this.registries.find((reg) => reg.id === registryId)?.spec;\n if (!regSpec) throw new Error(`Registry with id \"${registryId}\" not found`);\n if (regSpec.type !== \"remote-v2\")\n throw new Error(`Only \"remote-v2\" registries support block-kind resolution.`);\n const reg = this.v2Provider.getRegistry(regSpec.url);\n return await reg.resolveKind(ref, { allowUnstable });\n }\n}\n"],"mappings":"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"}
@@ -1,7 +1,8 @@
1
1
  import { V2RegistryProvider } from "./registry-v2-provider.js";
2
- import { BlockPackId, BlockPackListing, RegistryEntry, SingleBlockPackOverview } from "@milaboratories/pl-model-middle-layer";
2
+ import { BlockPackFromRegistryV2, BlockPackId, BlockPackListing, RegistryEntry, SingleBlockPackOverview } from "@milaboratories/pl-model-middle-layer";
3
3
  import { Dispatcher } from "undici";
4
4
  import { BlockPackDescriptionAbsolute } from "@platforma-sdk/block-tools";
5
+ import { BlockKindReference } from "@milaboratories/pl-model-common";
5
6
 
6
7
  //#region src/block_registry/registry.d.ts
7
8
  declare function getDevV1PacketMtime(devPath: string): Promise<string>;
@@ -22,6 +23,18 @@ declare class BlockPackRegistry {
22
23
  signal?: AbortSignal;
23
24
  }): Promise<BlockPackListing>;
24
25
  getOverview(registryId: string, blockId: BlockPackId, channel: string): Promise<SingleBlockPackOverview>;
26
+ /**
27
+ * Resolve a block-kind reference (`{name}@{selector}`) against a `remote-v2`
28
+ * registry to a concrete `from-registry-v2` block spec. Thin facade cloned
29
+ * from {@link getOverview}: resolve `registryId` → assert `remote-v2` →
30
+ * delegate to the reader's pure-core-backed `resolveKind`. All version-math
31
+ * lives in `@platforma-sdk/block-tools` (no backend involvement).
32
+ */
33
+ resolveKind(registryId: string, ref: BlockKindReference, {
34
+ allowUnstable
35
+ }: {
36
+ allowUnstable: boolean;
37
+ }): Promise<BlockPackFromRegistryV2>;
25
38
  }
26
39
  //#endregion
27
40
  export { BlockPackRegistry, getDevV1PacketMtime, getDevV2PacketMtime, getFromPackV2Mtime };
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","names":[],"sources":["../../src/block_registry/registry.ts"],"mappings":";;;;;;iBA4CsB,mBAAA,CAAoB,OAAA,WAAkB,OAAO;AAAA,iBAW7C,mBAAA,CACpB,WAAA,EAAa,4BAAA,GACZ,OAAO;AAbV;;;;AAAmE;AAAnE,iBA0BsB,kBAAA,CAAmB,OAAA,UAAiB,SAAA,WAAoB,OAAO;AAAA,cAcxE,iBAAA;EAAA,iBAEQ,UAAA;EAAA,iBACA,UAAA;EAAA,iBACA,IAAA;cAFA,UAAA,EAAY,kBAAA,EACZ,UAAA,EAAY,aAAA,IACZ,IAAA,GAAO,UAAA;EAAA,QAGZ,kBAAA;EA4LD,cAAA,CAAe,OAAA;IAAY,MAAA,GAAS,WAAA;EAAA,IAAgB,OAAA,CAAQ,gBAAA;EAU5D,WAAA,CACX,UAAA,UACA,OAAA,EAAS,WAAA,EACT,OAAA,WACC,OAAA,CAAQ,uBAAA;AAAA"}
1
+ {"version":3,"file":"registry.d.ts","names":[],"sources":["../../src/block_registry/registry.ts"],"mappings":";;;;;;;iBA8CsB,mBAAA,CAAoB,OAAA,WAAkB,OAAO;AAAA,iBAW7C,mBAAA,CACpB,WAAA,EAAa,4BAAA,GACZ,OAAO;AAbV;;;;AAAmE;AAAnE,iBA0BsB,kBAAA,CAAmB,OAAA,UAAiB,SAAA,WAAoB,OAAO;AAAA,cAcxE,iBAAA;EAAA,iBAEQ,UAAA;EAAA,iBACA,UAAA;EAAA,iBACA,IAAA;cAFA,UAAA,EAAY,kBAAA,EACZ,UAAA,EAAY,aAAA,IACZ,IAAA,GAAO,UAAA;EAAA,QAGZ,kBAAA;EA4LD,cAAA,CAAe,OAAA;IAAY,MAAA,GAAS,WAAA;EAAA,IAAgB,OAAA,CAAQ,gBAAA;EAU5D,WAAA,CACX,UAAA,UACA,OAAA,EAAS,WAAA,EACT,OAAA,WACC,OAAA,CAAQ,uBAAA;;;;;;;AA/NwE;EAiPtE,WAAA,CACX,UAAA,UACA,GAAA,EAAK,kBAAA;IACH;EAAA;IAAmB,aAAA;EAAA,IACpB,OAAA,CAAQ,uBAAA;AAAA"}
@@ -212,6 +212,19 @@ var BlockPackRegistry = class {
212
212
  if (regSpec.type !== "remote-v2") throw new Error(`Only "remote-v2" registries support specific package version overview retrieval.`);
213
213
  return await this.v2Provider.getRegistry(regSpec.url).getSpecificOverview(blockId, channel);
214
214
  }
215
+ /**
216
+ * Resolve a block-kind reference (`{name}@{selector}`) against a `remote-v2`
217
+ * registry to a concrete `from-registry-v2` block spec. Thin facade cloned
218
+ * from {@link getOverview}: resolve `registryId` → assert `remote-v2` →
219
+ * delegate to the reader's pure-core-backed `resolveKind`. All version-math
220
+ * lives in `@platforma-sdk/block-tools` (no backend involvement).
221
+ */
222
+ async resolveKind(registryId, ref, { allowUnstable }) {
223
+ const regSpec = this.registries.find((reg) => reg.id === registryId)?.spec;
224
+ if (!regSpec) throw new Error(`Registry with id "${registryId}" not found`);
225
+ if (regSpec.type !== "remote-v2") throw new Error(`Only "remote-v2" registries support block-kind resolution.`);
226
+ return await this.v2Provider.getRegistry(regSpec.url).resolveKind(ref, { allowUnstable });
227
+ }
215
228
  };
216
229
  //#endregion
217
230
  export { BlockPackRegistry, getDevV1PacketMtime, getDevV2PacketMtime, getFromPackV2Mtime };
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","names":[],"sources":["../../src/block_registry/registry.ts"],"sourcesContent":["import type { Dispatcher } from \"undici\";\nimport { request } from \"undici\";\nimport type { BlockPackDescriptionAbsolute } from \"@platforma-sdk/block-tools\";\nimport { embedBlockPackMetaAbsoluteBytes, RegistryV1 } from \"@platforma-sdk/block-tools\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport YAML from \"yaml\";\nimport { assertNever } from \"@milaboratories/ts-helpers\";\nimport { LegacyDevBlockPackFiles } from \"../dev_env\";\nimport { tryLoadPackDescription } from \"@platforma-sdk/block-tools\";\nimport { deriveRequiredCapabilities } from \"../mutator/block-pack/required_capabilities\";\nimport type { V2RegistryProvider } from \"./registry-v2-provider\";\nimport type {\n BlockPackId,\n BlockPackListing,\n BlockPackOverview,\n RegistryEntry,\n RegistryStatus,\n SingleBlockPackOverview,\n} from \"@milaboratories/pl-model-middle-layer\";\nimport { AnyChannel, StableChannel } from \"@milaboratories/pl-model-middle-layer\";\n\nasync function getFileContent(path: string) {\n try {\n return await fs.promises.readFile(path, \"utf8\");\n } catch (error: unknown) {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") {\n return undefined;\n }\n throw error;\n }\n}\n\nasync function getFileStat(path: string) {\n try {\n return await fs.promises.stat(path, { bigint: true });\n } catch (error: unknown) {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") {\n return undefined;\n }\n throw error;\n }\n}\n\nexport async function getDevV1PacketMtime(devPath: string): Promise<string> {\n let mtime = 0n;\n for (const f of LegacyDevBlockPackFiles) {\n const fullPath = path.join(devPath, ...f);\n const stat = await getFileStat(fullPath);\n if (stat === undefined) continue;\n if (mtime < stat.mtimeNs) mtime = stat.mtimeNs;\n }\n return mtime.toString();\n}\n\nexport async function getDevV2PacketMtime(\n description: BlockPackDescriptionAbsolute,\n): Promise<string> {\n const wfStats = await fs.promises.stat(description.components.workflow.main.file, {\n bigint: true,\n });\n const modelStats = await fs.promises.stat(description.components.model.file, { bigint: true });\n return (wfStats.mtimeNs > modelStats.mtimeNs ? wfStats.mtimeNs : modelStats.mtimeNs).toString();\n}\n\n/** Mtime for a `from-pack-v2` locator: prefer the manifest `timestamp`, else\n * stat the `ui.tgz` archive. Must be called identically by `block_pack.ts`\n * prepare (which bakes the result into the spec) and the update watcher (which\n * recomputes it to diff against the stored value) — divergence makes the block\n * flap perpetually \"update available\". */\nexport async function getFromPackV2Mtime(packDir: string, uiTgzPath: string): Promise<string> {\n try {\n const manifestPath = path.join(packDir, \"manifest.json\");\n const raw = JSON.parse(await fs.promises.readFile(manifestPath, { encoding: \"utf-8\" })) as {\n timestamp?: unknown;\n };\n if (typeof raw.timestamp === \"number\") return String(raw.timestamp);\n } catch {\n // fall through to stat\n }\n const stat = await fs.promises.stat(uiTgzPath, { bigint: true });\n return stat.mtimeNs.toString();\n}\n\nexport class BlockPackRegistry {\n constructor(\n private readonly v2Provider: V2RegistryProvider,\n private readonly registries: RegistryEntry[],\n private readonly http?: Dispatcher,\n ) {}\n\n private async getPackagesForRoot(\n regEntry: RegistryEntry,\n options?: { signal?: AbortSignal },\n ): Promise<BlockPackOverview[]> {\n const result: BlockPackOverview[] = [];\n const regSpec = regEntry.spec;\n switch (regSpec.type) {\n case \"remote-v1\": {\n const httpOptions = this.http !== undefined ? { dispatcher: this.http } : {};\n\n const overviewResponse = await request(`${regSpec.url}/${RegistryV1.GlobalOverviewPath}`, {\n ...httpOptions,\n signal: options?.signal,\n });\n const overview = (await overviewResponse.body.json()) as RegistryV1.GlobalOverview;\n for (const overviewEntry of overview) {\n const { organization, package: pkg, latestMeta, latestVersion } = overviewEntry;\n const id = {\n organization,\n name: pkg,\n version: latestVersion,\n };\n const latestOverview: SingleBlockPackOverview = {\n id,\n meta: {\n title: (latestMeta[\"title\"] as string | undefined) ?? \"No title\",\n description: (latestMeta[\"description\"] as string | undefined) ?? \"No Description\",\n organization: {\n name: organization,\n url: \"https://unknown.com\",\n },\n },\n spec: {\n type: \"from-registry-v1\",\n id,\n registryUrl: regSpec.url,\n },\n };\n result.push({\n registryId: regEntry.id,\n id,\n latestByChannel: {\n [AnyChannel]: latestOverview,\n [StableChannel]: latestOverview,\n },\n allVersions: overviewEntry.allVersions.map((v) => ({ version: v, channels: [] })),\n });\n }\n return result;\n }\n\n case \"remote-v2\":\n return (\n await this.v2Provider.getRegistry(regSpec.url).listBlockPacks({ signal: options?.signal })\n ).map((e) => ({\n ...e,\n registryId: regEntry.id,\n }));\n // e.latestByChannel[StableChannel]\n // ? {\n // ...e,\n // registryId: regEntry.id,\n // }\n // : {\n // ...e,\n // latestByChannel: {\n // ...e.latestByChannel,\n // [StableChannel]: ((s: SingleBlockPackOverview) => {\n // if (s.spec.type === 'from-registry-v2') {\n // return { ...s, spec: { ...s.spec, channel: StableChannel } };\n // }\n\n // return s;\n // })(e.latestByChannel[AnyChannel]),\n // },\n // registryId: regEntry.id,\n // },\n\n case \"local-dev\":\n for (const entry of await fs.promises.readdir(regSpec.path, { withFileTypes: true })) {\n if (!entry.isDirectory()) continue;\n const devPath = path.join(regSpec.path, entry.name);\n\n const legacyYamlContent = await getFileContent(\n path.join(devPath, RegistryV1.PlPackageYamlConfigFile),\n );\n if (legacyYamlContent !== undefined) {\n const config = RegistryV1.PlPackageConfigData.parse(YAML.parse(legacyYamlContent));\n\n const mtime = await getDevV1PacketMtime(devPath);\n\n const id = {\n organization: config.organization,\n name: config.package,\n version: \"DEV\",\n };\n\n const latestOverview: SingleBlockPackOverview = {\n id,\n meta: {\n title: (config.meta[\"title\"] as string) ?? \"No title\",\n description: (config.meta[\"description\"] as string) ?? \"No Description\",\n organization: {\n name: config.organization,\n url: \"https://unknown.com\",\n },\n },\n spec: {\n type: \"dev-v2\",\n folder: devPath,\n mtime,\n },\n };\n\n result.push({\n registryId: regEntry.id,\n id,\n latestByChannel: {\n [AnyChannel]: latestOverview,\n [StableChannel]: latestOverview,\n },\n allVersions: [],\n });\n } else {\n let actualDevPath = devPath;\n let v2Description = await tryLoadPackDescription(actualDevPath);\n\n if (v2Description === undefined)\n // iterating over expected subfolder names where block developer may put root block-pack package\n for (const bpSubfolder of [\"block\", \"meta\"]) {\n actualDevPath = path.join(devPath, bpSubfolder);\n v2Description = await tryLoadPackDescription(actualDevPath);\n if (v2Description !== undefined) break;\n }\n\n if (v2Description !== undefined) {\n const mtime = await getDevV2PacketMtime(v2Description);\n\n const meta = await embedBlockPackMetaAbsoluteBytes(v2Description.meta);\n\n // `tryLoadPackDescription` reads `package.json#block.meta` —\n // the dev SOURCE meta the developer wrote.\n // `requiredCapabilities` is a PACK-TIME artifact written by\n // `block-tools pack` into `block-pack/manifest.json`.\n // So for any dev block, the source meta never carries the field.\n //\n // To surface the gate at listing time without requiring the\n // developer to re-pack on every workflow change, derive from\n // the compiled workflow bytes directly.\n if (meta.requiredCapabilities === undefined) {\n const workflowBytes = await fs.promises.readFile(\n v2Description.components.workflow.main.file,\n );\n const derived = deriveRequiredCapabilities(workflowBytes);\n if (derived !== undefined) meta.requiredCapabilities = derived;\n }\n\n const latestOverview: SingleBlockPackOverview = {\n id: v2Description.id,\n meta,\n featureFlags: v2Description.featureFlags,\n spec: {\n type: \"dev-v2\",\n folder: actualDevPath,\n mtime,\n },\n };\n\n result.push({\n registryId: regEntry.id,\n id: v2Description.id,\n latestByChannel: {\n [AnyChannel]: latestOverview,\n [StableChannel]: latestOverview,\n },\n allVersions: [],\n });\n }\n }\n\n continue;\n }\n return result;\n default:\n return assertNever(regSpec);\n }\n }\n\n public async listBlockPacks(options?: { signal?: AbortSignal }): Promise<BlockPackListing> {\n const blockPacks: BlockPackOverview[] = [];\n const registries: RegistryStatus[] = [];\n for (const regSpecs of this.registries) {\n registries.push({ ...regSpecs, status: \"online\" });\n blockPacks.push(...(await this.getPackagesForRoot(regSpecs, options)));\n }\n return { registries, blockPacks };\n }\n\n public async getOverview(\n registryId: string,\n blockId: BlockPackId,\n channel: string,\n ): Promise<SingleBlockPackOverview> {\n const regSpec = this.registries.find((reg) => reg.id === registryId)?.spec;\n if (!regSpec) throw new Error(`Registry with id \"${registryId}\" not found`);\n if (regSpec.type !== \"remote-v2\")\n throw new Error(\n `Only \"remote-v2\" registries support specific package version overview retrieval.`,\n );\n const reg = this.v2Provider.getRegistry(regSpec.url);\n return await reg.getSpecificOverview(blockId, channel);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"registry.js","names":[],"sources":["../../src/block_registry/registry.ts"],"sourcesContent":["import type { Dispatcher } from \"undici\";\nimport { request } from \"undici\";\nimport type { BlockPackDescriptionAbsolute } from \"@platforma-sdk/block-tools\";\nimport { embedBlockPackMetaAbsoluteBytes, RegistryV1 } from \"@platforma-sdk/block-tools\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport YAML from \"yaml\";\nimport { assertNever } from \"@milaboratories/ts-helpers\";\nimport { LegacyDevBlockPackFiles } from \"../dev_env\";\nimport { tryLoadPackDescription } from \"@platforma-sdk/block-tools\";\nimport { deriveRequiredCapabilities } from \"../mutator/block-pack/required_capabilities\";\nimport type { V2RegistryProvider } from \"./registry-v2-provider\";\nimport type {\n BlockPackId,\n BlockPackListing,\n BlockPackOverview,\n BlockPackFromRegistryV2,\n RegistryEntry,\n RegistryStatus,\n SingleBlockPackOverview,\n} from \"@milaboratories/pl-model-middle-layer\";\nimport { AnyChannel, StableChannel } from \"@milaboratories/pl-model-middle-layer\";\nimport type { BlockKindReference } from \"@milaboratories/pl-model-common\";\n\nasync function getFileContent(path: string) {\n try {\n return await fs.promises.readFile(path, \"utf8\");\n } catch (error: unknown) {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") {\n return undefined;\n }\n throw error;\n }\n}\n\nasync function getFileStat(path: string) {\n try {\n return await fs.promises.stat(path, { bigint: true });\n } catch (error: unknown) {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") {\n return undefined;\n }\n throw error;\n }\n}\n\nexport async function getDevV1PacketMtime(devPath: string): Promise<string> {\n let mtime = 0n;\n for (const f of LegacyDevBlockPackFiles) {\n const fullPath = path.join(devPath, ...f);\n const stat = await getFileStat(fullPath);\n if (stat === undefined) continue;\n if (mtime < stat.mtimeNs) mtime = stat.mtimeNs;\n }\n return mtime.toString();\n}\n\nexport async function getDevV2PacketMtime(\n description: BlockPackDescriptionAbsolute,\n): Promise<string> {\n const wfStats = await fs.promises.stat(description.components.workflow.main.file, {\n bigint: true,\n });\n const modelStats = await fs.promises.stat(description.components.model.file, { bigint: true });\n return (wfStats.mtimeNs > modelStats.mtimeNs ? wfStats.mtimeNs : modelStats.mtimeNs).toString();\n}\n\n/** Mtime for a `from-pack-v2` locator: prefer the manifest `timestamp`, else\n * stat the `ui.tgz` archive. Must be called identically by `block_pack.ts`\n * prepare (which bakes the result into the spec) and the update watcher (which\n * recomputes it to diff against the stored value) — divergence makes the block\n * flap perpetually \"update available\". */\nexport async function getFromPackV2Mtime(packDir: string, uiTgzPath: string): Promise<string> {\n try {\n const manifestPath = path.join(packDir, \"manifest.json\");\n const raw = JSON.parse(await fs.promises.readFile(manifestPath, { encoding: \"utf-8\" })) as {\n timestamp?: unknown;\n };\n if (typeof raw.timestamp === \"number\") return String(raw.timestamp);\n } catch {\n // fall through to stat\n }\n const stat = await fs.promises.stat(uiTgzPath, { bigint: true });\n return stat.mtimeNs.toString();\n}\n\nexport class BlockPackRegistry {\n constructor(\n private readonly v2Provider: V2RegistryProvider,\n private readonly registries: RegistryEntry[],\n private readonly http?: Dispatcher,\n ) {}\n\n private async getPackagesForRoot(\n regEntry: RegistryEntry,\n options?: { signal?: AbortSignal },\n ): Promise<BlockPackOverview[]> {\n const result: BlockPackOverview[] = [];\n const regSpec = regEntry.spec;\n switch (regSpec.type) {\n case \"remote-v1\": {\n const httpOptions = this.http !== undefined ? { dispatcher: this.http } : {};\n\n const overviewResponse = await request(`${regSpec.url}/${RegistryV1.GlobalOverviewPath}`, {\n ...httpOptions,\n signal: options?.signal,\n });\n const overview = (await overviewResponse.body.json()) as RegistryV1.GlobalOverview;\n for (const overviewEntry of overview) {\n const { organization, package: pkg, latestMeta, latestVersion } = overviewEntry;\n const id = {\n organization,\n name: pkg,\n version: latestVersion,\n };\n const latestOverview: SingleBlockPackOverview = {\n id,\n meta: {\n title: (latestMeta[\"title\"] as string | undefined) ?? \"No title\",\n description: (latestMeta[\"description\"] as string | undefined) ?? \"No Description\",\n organization: {\n name: organization,\n url: \"https://unknown.com\",\n },\n },\n spec: {\n type: \"from-registry-v1\",\n id,\n registryUrl: regSpec.url,\n },\n };\n result.push({\n registryId: regEntry.id,\n id,\n latestByChannel: {\n [AnyChannel]: latestOverview,\n [StableChannel]: latestOverview,\n },\n allVersions: overviewEntry.allVersions.map((v) => ({ version: v, channels: [] })),\n });\n }\n return result;\n }\n\n case \"remote-v2\":\n return (\n await this.v2Provider.getRegistry(regSpec.url).listBlockPacks({ signal: options?.signal })\n ).map((e) => ({\n ...e,\n registryId: regEntry.id,\n }));\n // e.latestByChannel[StableChannel]\n // ? {\n // ...e,\n // registryId: regEntry.id,\n // }\n // : {\n // ...e,\n // latestByChannel: {\n // ...e.latestByChannel,\n // [StableChannel]: ((s: SingleBlockPackOverview) => {\n // if (s.spec.type === 'from-registry-v2') {\n // return { ...s, spec: { ...s.spec, channel: StableChannel } };\n // }\n\n // return s;\n // })(e.latestByChannel[AnyChannel]),\n // },\n // registryId: regEntry.id,\n // },\n\n case \"local-dev\":\n for (const entry of await fs.promises.readdir(regSpec.path, { withFileTypes: true })) {\n if (!entry.isDirectory()) continue;\n const devPath = path.join(regSpec.path, entry.name);\n\n const legacyYamlContent = await getFileContent(\n path.join(devPath, RegistryV1.PlPackageYamlConfigFile),\n );\n if (legacyYamlContent !== undefined) {\n const config = RegistryV1.PlPackageConfigData.parse(YAML.parse(legacyYamlContent));\n\n const mtime = await getDevV1PacketMtime(devPath);\n\n const id = {\n organization: config.organization,\n name: config.package,\n version: \"DEV\",\n };\n\n const latestOverview: SingleBlockPackOverview = {\n id,\n meta: {\n title: (config.meta[\"title\"] as string) ?? \"No title\",\n description: (config.meta[\"description\"] as string) ?? \"No Description\",\n organization: {\n name: config.organization,\n url: \"https://unknown.com\",\n },\n },\n spec: {\n type: \"dev-v2\",\n folder: devPath,\n mtime,\n },\n };\n\n result.push({\n registryId: regEntry.id,\n id,\n latestByChannel: {\n [AnyChannel]: latestOverview,\n [StableChannel]: latestOverview,\n },\n allVersions: [],\n });\n } else {\n let actualDevPath = devPath;\n let v2Description = await tryLoadPackDescription(actualDevPath);\n\n if (v2Description === undefined)\n // iterating over expected subfolder names where block developer may put root block-pack package\n for (const bpSubfolder of [\"block\", \"meta\"]) {\n actualDevPath = path.join(devPath, bpSubfolder);\n v2Description = await tryLoadPackDescription(actualDevPath);\n if (v2Description !== undefined) break;\n }\n\n if (v2Description !== undefined) {\n const mtime = await getDevV2PacketMtime(v2Description);\n\n const meta = await embedBlockPackMetaAbsoluteBytes(v2Description.meta);\n\n // `tryLoadPackDescription` reads `package.json#block.meta` —\n // the dev SOURCE meta the developer wrote.\n // `requiredCapabilities` is a PACK-TIME artifact written by\n // `block-tools pack` into `block-pack/manifest.json`.\n // So for any dev block, the source meta never carries the field.\n //\n // To surface the gate at listing time without requiring the\n // developer to re-pack on every workflow change, derive from\n // the compiled workflow bytes directly.\n if (meta.requiredCapabilities === undefined) {\n const workflowBytes = await fs.promises.readFile(\n v2Description.components.workflow.main.file,\n );\n const derived = deriveRequiredCapabilities(workflowBytes);\n if (derived !== undefined) meta.requiredCapabilities = derived;\n }\n\n const latestOverview: SingleBlockPackOverview = {\n id: v2Description.id,\n meta,\n featureFlags: v2Description.featureFlags,\n spec: {\n type: \"dev-v2\",\n folder: actualDevPath,\n mtime,\n },\n };\n\n result.push({\n registryId: regEntry.id,\n id: v2Description.id,\n latestByChannel: {\n [AnyChannel]: latestOverview,\n [StableChannel]: latestOverview,\n },\n allVersions: [],\n });\n }\n }\n\n continue;\n }\n return result;\n default:\n return assertNever(regSpec);\n }\n }\n\n public async listBlockPacks(options?: { signal?: AbortSignal }): Promise<BlockPackListing> {\n const blockPacks: BlockPackOverview[] = [];\n const registries: RegistryStatus[] = [];\n for (const regSpecs of this.registries) {\n registries.push({ ...regSpecs, status: \"online\" });\n blockPacks.push(...(await this.getPackagesForRoot(regSpecs, options)));\n }\n return { registries, blockPacks };\n }\n\n public async getOverview(\n registryId: string,\n blockId: BlockPackId,\n channel: string,\n ): Promise<SingleBlockPackOverview> {\n const regSpec = this.registries.find((reg) => reg.id === registryId)?.spec;\n if (!regSpec) throw new Error(`Registry with id \"${registryId}\" not found`);\n if (regSpec.type !== \"remote-v2\")\n throw new Error(\n `Only \"remote-v2\" registries support specific package version overview retrieval.`,\n );\n const reg = this.v2Provider.getRegistry(regSpec.url);\n return await reg.getSpecificOverview(blockId, channel);\n }\n\n /**\n * Resolve a block-kind reference (`{name}@{selector}`) against a `remote-v2`\n * registry to a concrete `from-registry-v2` block spec. Thin facade cloned\n * from {@link getOverview}: resolve `registryId` → assert `remote-v2` →\n * delegate to the reader's pure-core-backed `resolveKind`. All version-math\n * lives in `@platforma-sdk/block-tools` (no backend involvement).\n */\n public async resolveKind(\n registryId: string,\n ref: BlockKindReference,\n { allowUnstable }: { allowUnstable: boolean },\n ): Promise<BlockPackFromRegistryV2> {\n const regSpec = this.registries.find((reg) => reg.id === registryId)?.spec;\n if (!regSpec) throw new Error(`Registry with id \"${registryId}\" not found`);\n if (regSpec.type !== \"remote-v2\")\n throw new Error(`Only \"remote-v2\" registries support block-kind resolution.`);\n const reg = this.v2Provider.getRegistry(regSpec.url);\n return await reg.resolveKind(ref, { allowUnstable });\n }\n}\n"],"mappings":"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"}