@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
@@ -0,0 +1,157 @@
1
+ import { walkProjectForTemplateExport } from "./template_export.js";
2
+ import YAML from "yaml";
3
+ import { pathToFileURL } from "node:url";
4
+ import { PROJECT_TEMPLATE_SCHEMA_V1, kindReferenceToSelectorReference, parseProjectTemplateV1 } from "@milaboratories/pl-model-common";
5
+ //#region src/model/template_serializer.ts
6
+ /**
7
+ * The `location` to write for a block installed from the filesystem, or `undefined`
8
+ * for one that came from a registry and therefore needs no locator.
9
+ *
10
+ * Both filesystem spec shapes are emitted, and they anchor at different directories
11
+ * — a dev block at its facade package, an npm-consumed one at its block-pack folder.
12
+ * The document does not distinguish them: one URI is written either way, and telling
13
+ * the two layouts apart is done by looking at what is actually there, by the side
14
+ * that has the filesystem anyway. Encoding the layout in the file instead would
15
+ * freeze today's two shapes into the format.
16
+ *
17
+ * A dev spec carries an OS path and is converted here, which also percent-encodes a
18
+ * path containing spaces. An npm-consumed spec already carries a `file:` URL and is
19
+ * passed through: it is the locator the block itself emitted, and reconstructing one
20
+ * from it could only lose information.
21
+ */
22
+ function locationOf(spec) {
23
+ switch (spec.type) {
24
+ case "dev-v2": return pathToFileURL(spec.folder).href;
25
+ case "from-pack-v2": return spec.packUrl;
26
+ case "dev-v1":
27
+ case "from-registry-v1":
28
+ case "from-registry-v2": return;
29
+ }
30
+ }
31
+ /**
32
+ * Turn a project into a template document.
33
+ *
34
+ * Assembly is deliberately dull — the entry is the block's id, its widened kind
35
+ * reference, and the params the walk already collected. The interesting decisions
36
+ * were made upstream; what is left here is the two things only this layer can
37
+ * check, both of which produce problems rather than a broken file:
38
+ *
39
+ * - **A block with no kind cannot be written.** An entry's `kind` is required — it
40
+ * is the params contract the entry is typed against — while a block's kind is
41
+ * optional, so a block that predates kinds, or that uses the deprecated
42
+ * kind-less model overload, has no legal entry. Reported per block. This is not
43
+ * an edge case today: it is what most existing projects will hit until their
44
+ * blocks are republished.
45
+ * - **References must point at an entry declared earlier.** Verbatim id reuse
46
+ * means a reference to a deleted block survives into the file naming nothing:
47
+ * deleting a block only removes it from the structure and does not rewrite
48
+ * downstream args, so a live project holds such references routinely.
49
+ *
50
+ * `block` is never emitted. That override exists to pin an implementation against
51
+ * a kind's version range, and export always writes the exact version the block
52
+ * implements, so there is nothing left for it to pin.
53
+ *
54
+ * `location` IS emitted, for every block that was installed from the filesystem. Such
55
+ * a block is not in any registry, so the kind reference alone names nothing the
56
+ * importer could find, and a file that omitted the one usable answer would describe a
57
+ * project that cannot be recreated. It costs portability, and nothing says so: such a
58
+ * file is the debugging path, read by the developer who wrote it on the machine that
59
+ * wrote it.
60
+ *
61
+ * Problems from `walk` are carried through, so a caller can hand a walk straight
62
+ * in and get one combined list.
63
+ */
64
+ function assembleProjectTemplateV1(walk, kindProvider, specProvider) {
65
+ const problems = [...walk.problems];
66
+ const blocks = [];
67
+ for (const entry of walk.entries) {
68
+ const kind = kindProvider(entry.blockId);
69
+ if (kind === void 0) {
70
+ problems.push({
71
+ blockId: entry.blockId,
72
+ error: "Block declares no kind, so it cannot be written to a template: an entry's kind carries the params contract the entry is typed against"
73
+ });
74
+ continue;
75
+ }
76
+ let selector;
77
+ try {
78
+ selector = kindReferenceToSelectorReference(kind);
79
+ } catch (e) {
80
+ problems.push({
81
+ blockId: entry.blockId,
82
+ error: `Block's stored kind reference is malformed: ${e instanceof Error ? e.message : String(e)}`
83
+ });
84
+ continue;
85
+ }
86
+ const spec = specProvider(entry.blockId);
87
+ const location = spec === void 0 ? void 0 : locationOf(spec);
88
+ blocks.push({
89
+ id: entry.blockId,
90
+ kind: selector,
91
+ params: entry.params,
92
+ ...location !== void 0 ? { location } : {}
93
+ });
94
+ }
95
+ return {
96
+ document: {
97
+ schema: PROJECT_TEMPLATE_SCHEMA_V1,
98
+ blocks
99
+ },
100
+ problems
101
+ };
102
+ }
103
+ /**
104
+ * Render a template document to YAML text.
105
+ *
106
+ * Two non-default emitter settings, both about the file being read by someone
107
+ * else's code:
108
+ *
109
+ * - **No line folding.** A wrapped scalar still parses, but it makes a diff between
110
+ * two exported templates unreadable, which is most of the reason to prefer YAML
111
+ * over JSON here.
112
+ * - **Quote as if the reader were YAML 1.1**, while still parsing as 1.2. YAML 1.2
113
+ * dropped `yes`/`no`/`on`/`off`/`y`/`n` as booleans and dropped sexagesimal
114
+ * integers, so a 1.2 emitter leaves a params value of `"yes"` or `"1:30"` bare —
115
+ * which a 1.1 reader (PyYAML's default, and Go's yaml.v2) turns into `true` and
116
+ * `90`. A template is a contract for a second implementation, so the safe
117
+ * combination is to quote against the stricter ruleset and read with the looser
118
+ * one: a quoted scalar means the same thing under both. This adds no `%YAML`
119
+ * directive — it only changes which scalars get quotes.
120
+ */
121
+ function stringifyProjectTemplateV1(document) {
122
+ return YAML.stringify(document, {
123
+ lineWidth: 0,
124
+ version: "1.1"
125
+ });
126
+ }
127
+ /**
128
+ * Export a project as `template-v1` YAML, or report every reason it cannot be.
129
+ *
130
+ * All-or-nothing on purpose. A partial template silently drops blocks and the
131
+ * surviving entries may reference the dropped ones, so what looks like a
132
+ * successful export would produce a project missing pieces the user never chose
133
+ * to leave out. Reporting everything at once instead of failing on the first
134
+ * problem is the other half of that: fixing an export should take one pass.
135
+ *
136
+ * @param structure The project structure, which supplies both membership and order
137
+ * @param paramsProvider A block's derived template params, in live form
138
+ * @param kindProvider A block's exact kind reference, read from its stored config
139
+ * @param specProvider A block's origin spec, read from the same stored container
140
+ */
141
+ function exportProjectAsTemplateV1(structure, paramsProvider, kindProvider, specProvider) {
142
+ const { document, problems } = assembleProjectTemplateV1(walkProjectForTemplateExport(structure, paramsProvider), kindProvider, specProvider);
143
+ if (problems.length > 0) return {
144
+ ok: false,
145
+ problems
146
+ };
147
+ parseProjectTemplateV1(document);
148
+ return {
149
+ ok: true,
150
+ yaml: stringifyProjectTemplateV1(document),
151
+ document
152
+ };
153
+ }
154
+ //#endregion
155
+ export { assembleProjectTemplateV1, exportProjectAsTemplateV1, locationOf, stringifyProjectTemplateV1 };
156
+
157
+ //# sourceMappingURL=template_serializer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template_serializer.js","names":[],"sources":["../../src/model/template_serializer.ts"],"sourcesContent":["import YAML from \"yaml\";\nimport { pathToFileURL } from \"node:url\";\nimport type {\n BlockKindReference,\n BlockKindSelectorReference,\n BlockPackLocationReference,\n ProjectTemplateV1,\n ProjectTemplateV1Entry,\n} from \"@milaboratories/pl-model-common\";\nimport {\n PROJECT_TEMPLATE_SCHEMA_V1,\n kindReferenceToSelectorReference,\n parseProjectTemplateV1,\n} from \"@milaboratories/pl-model-common\";\nimport type { BlockPackSpec } from \"@milaboratories/pl-model-middle-layer\";\nimport type { ProjectStructure } from \"./project_model\";\nimport type {\n TemplateExportProblem,\n TemplateExportWalk,\n TemplateParamsResult,\n} from \"./template_export\";\nimport { walkProjectForTemplateExport } from \"./template_export\";\n\n/** A block's exact kind reference, or `undefined` for a block that declares no kind. */\nexport type BlockKindProvider = (blockId: string) => BlockKindReference | undefined;\n\n/**\n * A block's origin spec — where the installed block came from — or `undefined` when\n * it is not known for that block.\n *\n * The project stores this next to the kind reference, so both are read from the same\n * place and neither costs an extra round-trip.\n */\nexport type BlockPackSpecProvider = (blockId: string) => BlockPackSpec | undefined;\n\n/** What the caller gets back for a whole project. */\nexport type ProjectTemplateExportOutcome =\n | {\n readonly ok: true;\n readonly yaml: string;\n /** The document the YAML was rendered from, already validated. */\n readonly document: ProjectTemplateV1;\n }\n | {\n readonly ok: false;\n /** Every block that stands in the way, not just the first. */\n readonly problems: readonly TemplateExportProblem[];\n };\n\n/**\n * The `location` to write for a block installed from the filesystem, or `undefined`\n * for one that came from a registry and therefore needs no locator.\n *\n * Both filesystem spec shapes are emitted, and they anchor at different directories\n * — a dev block at its facade package, an npm-consumed one at its block-pack folder.\n * The document does not distinguish them: one URI is written either way, and telling\n * the two layouts apart is done by looking at what is actually there, by the side\n * that has the filesystem anyway. Encoding the layout in the file instead would\n * freeze today's two shapes into the format.\n *\n * A dev spec carries an OS path and is converted here, which also percent-encodes a\n * path containing spaces. An npm-consumed spec already carries a `file:` URL and is\n * passed through: it is the locator the block itself emitted, and reconstructing one\n * from it could only lose information.\n */\nexport function locationOf(spec: BlockPackSpec): BlockPackLocationReference | undefined {\n switch (spec.type) {\n case \"dev-v2\":\n return pathToFileURL(spec.folder).href as BlockPackLocationReference;\n case \"from-pack-v2\":\n return spec.packUrl as BlockPackLocationReference;\n // A registry block is found by name, which is what makes the entry portable —\n // writing where this machine happened to cache it would take that away. `dev-v1`\n // predates kinds entirely, so such a block has no kind and never reaches here.\n case \"dev-v1\":\n case \"from-registry-v1\":\n case \"from-registry-v2\":\n return undefined;\n }\n}\n\n/**\n * Turn a project into a template document.\n *\n * Assembly is deliberately dull — the entry is the block's id, its widened kind\n * reference, and the params the walk already collected. The interesting decisions\n * were made upstream; what is left here is the two things only this layer can\n * check, both of which produce problems rather than a broken file:\n *\n * - **A block with no kind cannot be written.** An entry's `kind` is required — it\n * is the params contract the entry is typed against — while a block's kind is\n * optional, so a block that predates kinds, or that uses the deprecated\n * kind-less model overload, has no legal entry. Reported per block. This is not\n * an edge case today: it is what most existing projects will hit until their\n * blocks are republished.\n * - **References must point at an entry declared earlier.** Verbatim id reuse\n * means a reference to a deleted block survives into the file naming nothing:\n * deleting a block only removes it from the structure and does not rewrite\n * downstream args, so a live project holds such references routinely.\n *\n * `block` is never emitted. That override exists to pin an implementation against\n * a kind's version range, and export always writes the exact version the block\n * implements, so there is nothing left for it to pin.\n *\n * `location` IS emitted, for every block that was installed from the filesystem. Such\n * a block is not in any registry, so the kind reference alone names nothing the\n * importer could find, and a file that omitted the one usable answer would describe a\n * project that cannot be recreated. It costs portability, and nothing says so: such a\n * file is the debugging path, read by the developer who wrote it on the machine that\n * wrote it.\n *\n * Problems from `walk` are carried through, so a caller can hand a walk straight\n * in and get one combined list.\n */\nexport function assembleProjectTemplateV1(\n walk: TemplateExportWalk,\n kindProvider: BlockKindProvider,\n specProvider: BlockPackSpecProvider,\n): { document: ProjectTemplateV1; problems: readonly TemplateExportProblem[] } {\n const problems: TemplateExportProblem[] = [...walk.problems];\n const blocks: ProjectTemplateV1Entry[] = [];\n\n for (const entry of walk.entries) {\n const kind = kindProvider(entry.blockId);\n\n if (kind === undefined) {\n problems.push({\n blockId: entry.blockId,\n error:\n \"Block declares no kind, so it cannot be written to a template: an entry's kind \" +\n \"carries the params contract the entry is typed against\",\n });\n continue;\n }\n\n let selector: BlockKindSelectorReference;\n try {\n // Widening validates, and therefore throws — which is why it happens here\n // and not where the reference is read: every read site sits inside a\n // recomputed project overview, where one malformed stored reference must not\n // be able to break unrelated blocks.\n selector = kindReferenceToSelectorReference(kind);\n } catch (e) {\n problems.push({\n blockId: entry.blockId,\n error: `Block's stored kind reference is malformed: ${e instanceof Error ? e.message : String(e)}`,\n });\n continue;\n }\n\n const spec = specProvider(entry.blockId);\n const location = spec === undefined ? undefined : locationOf(spec);\n\n blocks.push({\n id: entry.blockId,\n kind: selector,\n params: entry.params,\n ...(location !== undefined ? { location } : {}),\n });\n }\n\n // References are not examined. A project's structure is topological by construction, so an\n // entry cannot legally reference one below it — and checking would mean reading the params,\n // which only the block that wrote them can do.\n const document: ProjectTemplateV1 = { schema: PROJECT_TEMPLATE_SCHEMA_V1, blocks };\n\n return { document, problems };\n}\n\n/**\n * Render a template document to YAML text.\n *\n * Two non-default emitter settings, both about the file being read by someone\n * else's code:\n *\n * - **No line folding.** A wrapped scalar still parses, but it makes a diff between\n * two exported templates unreadable, which is most of the reason to prefer YAML\n * over JSON here.\n * - **Quote as if the reader were YAML 1.1**, while still parsing as 1.2. YAML 1.2\n * dropped `yes`/`no`/`on`/`off`/`y`/`n` as booleans and dropped sexagesimal\n * integers, so a 1.2 emitter leaves a params value of `\"yes\"` or `\"1:30\"` bare —\n * which a 1.1 reader (PyYAML's default, and Go's yaml.v2) turns into `true` and\n * `90`. A template is a contract for a second implementation, so the safe\n * combination is to quote against the stricter ruleset and read with the looser\n * one: a quoted scalar means the same thing under both. This adds no `%YAML`\n * directive — it only changes which scalars get quotes.\n */\nexport function stringifyProjectTemplateV1(document: ProjectTemplateV1): string {\n return YAML.stringify(document, { lineWidth: 0, version: \"1.1\" });\n}\n\n/**\n * Export a project as `template-v1` YAML, or report every reason it cannot be.\n *\n * All-or-nothing on purpose. A partial template silently drops blocks and the\n * surviving entries may reference the dropped ones, so what looks like a\n * successful export would produce a project missing pieces the user never chose\n * to leave out. Reporting everything at once instead of failing on the first\n * problem is the other half of that: fixing an export should take one pass.\n *\n * @param structure The project structure, which supplies both membership and order\n * @param paramsProvider A block's derived template params, in live form\n * @param kindProvider A block's exact kind reference, read from its stored config\n * @param specProvider A block's origin spec, read from the same stored container\n */\nexport function exportProjectAsTemplateV1(\n structure: ProjectStructure,\n paramsProvider: (blockId: string) => TemplateParamsResult | undefined,\n kindProvider: BlockKindProvider,\n specProvider: BlockPackSpecProvider,\n): ProjectTemplateExportOutcome {\n const walk = walkProjectForTemplateExport(structure, paramsProvider);\n const { document, problems } = assembleProjectTemplateV1(walk, kindProvider, specProvider);\n\n if (problems.length > 0) return { ok: false, problems };\n\n // Export must emit exactly what import parses, so that is asserted on every\n // export rather than only in tests — running the import-side parser over the\n // document we are about to write is the cheapest possible proof of it. Nothing\n // user-facing is expected to fail here: the kind grammar was checked by the\n // widening above, params were checked to be a mapping by the walk, and the\n // reference rules by the assembler. A throw means a bug in the assembler, with\n // one known exception: a project structure holding two blocks with the same id,\n // which is reachable through the mutator and produces duplicate entry ids.\n parseProjectTemplateV1(document);\n\n return {\n ok: true,\n yaml: stringifyProjectTemplateV1(document),\n document,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAiEA,SAAgB,WAAW,MAA6D;CACtF,QAAQ,KAAK,MAAb;EACE,KAAK,UACH,OAAO,cAAc,KAAK,MAAM,CAAC,CAAC;EACpC,KAAK,gBACH,OAAO,KAAK;EAId,KAAK;EACL,KAAK;EACL,KAAK,oBACH;CACJ;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,SAAgB,0BACd,MACA,cACA,cAC6E;CAC7E,MAAM,WAAoC,CAAC,GAAG,KAAK,QAAQ;CAC3D,MAAM,SAAmC,CAAC;CAE1C,KAAK,MAAM,SAAS,KAAK,SAAS;EAChC,MAAM,OAAO,aAAa,MAAM,OAAO;EAEvC,IAAI,SAAS,KAAA,GAAW;GACtB,SAAS,KAAK;IACZ,SAAS,MAAM;IACf,OACE;GAEJ,CAAC;GACD;EACF;EAEA,IAAI;EACJ,IAAI;GAKF,WAAW,iCAAiC,IAAI;EAClD,SAAS,GAAG;GACV,SAAS,KAAK;IACZ,SAAS,MAAM;IACf,OAAO,+CAA+C,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;GACjG,CAAC;GACD;EACF;EAEA,MAAM,OAAO,aAAa,MAAM,OAAO;EACvC,MAAM,WAAW,SAAS,KAAA,IAAY,KAAA,IAAY,WAAW,IAAI;EAEjE,OAAO,KAAK;GACV,IAAI,MAAM;GACV,MAAM;GACN,QAAQ,MAAM;GACd,GAAI,aAAa,KAAA,IAAY,EAAE,SAAS,IAAI,CAAC;EAC/C,CAAC;CACH;CAOA,OAAO;EAAE,UAAA;GAF6B,QAAQ;GAA4B;EAE1D;EAAG;CAAS;AAC9B;;;;;;;;;;;;;;;;;;;AAoBA,SAAgB,2BAA2B,UAAqC;CAC9E,OAAO,KAAK,UAAU,UAAU;EAAE,WAAW;EAAG,SAAS;CAAM,CAAC;AAClE;;;;;;;;;;;;;;;AAgBA,SAAgB,0BACd,WACA,gBACA,cACA,cAC8B;CAE9B,MAAM,EAAE,UAAU,aAAa,0BADlB,6BAA6B,WAAW,cACO,GAAG,cAAc,YAAY;CAEzF,IAAI,SAAS,SAAS,GAAG,OAAO;EAAE,IAAI;EAAO;CAAS;CAUtD,uBAAuB,QAAQ;CAE/B,OAAO;EACL,IAAI;EACJ,MAAM,2BAA2B,QAAQ;EACzC;CACF;AACF"}
@@ -13,9 +13,9 @@ let _platforma_sdk_block_tools = require("@platforma-sdk/block-tools");
13
13
  let node_fs = require("node:fs");
14
14
  node_fs = require_runtime.__toESM(node_fs, 1);
15
15
  let _milaboratories_ts_helpers = require("@milaboratories/ts-helpers");
16
+ let node_url = require("node:url");
16
17
  let _milaboratories_pl_client = require("@milaboratories/pl-client");
17
18
  let lru_cache = require("lru-cache");
18
- let node_url = require("node:url");
19
19
  let canonicalize = require("canonicalize");
20
20
  canonicalize = require_runtime.__toESM(canonicalize, 1);
21
21
  let zod = require("zod");
@@ -11,9 +11,9 @@ import { request } from "undici";
11
11
  import { RegistryV1, loadPackDescription, loadPackDescriptionFromManifest } from "@platforma-sdk/block-tools";
12
12
  import fs from "node:fs";
13
13
  import { assertNever } from "@milaboratories/ts-helpers";
14
+ import { fileURLToPath } from "node:url";
14
15
  import { field } from "@milaboratories/pl-client";
15
16
  import { LRUCache } from "lru-cache";
16
- import { fileURLToPath } from "node:url";
17
17
  import canonicalize from "canonicalize";
18
18
  import { z } from "zod";
19
19
  //#region src/mutator/block-pack/block_pack.ts
@@ -2,8 +2,8 @@ const require_project_model = require("../model/project_model.cjs");
2
2
  const require_project_model_util = require("../model/project_model_util.cjs");
3
3
  const require_project_model_v1 = require("../model/project_model_v1.cjs");
4
4
  let _milaboratories_ts_helpers = require("@milaboratories/ts-helpers");
5
- let _milaboratories_pl_client = require("@milaboratories/pl-client");
6
5
  let _milaboratories_pl_model_common = require("@milaboratories/pl-model-common");
6
+ let _milaboratories_pl_client = require("@milaboratories/pl-client");
7
7
  //#region src/mutator/migration.ts
8
8
  /**
9
9
  * Migrates the project to the latest schema version.
@@ -2,8 +2,8 @@ import { ProjectStructureKey, SchemaVersionKey, projectFieldName } from "../mode
2
2
  import { allBlocks } from "../model/project_model_util.js";
3
3
  import { BlockFrontendStateKeyPrefixV1 } from "../model/project_model_v1.js";
4
4
  import { cachedDeserialize } from "@milaboratories/ts-helpers";
5
- import { field, isNullSignedResourceId } from "@milaboratories/pl-client";
6
5
  import { UiError } from "@milaboratories/pl-model-common";
6
+ import { field, isNullSignedResourceId } from "@milaboratories/pl-client";
7
7
  //#region src/mutator/migration.ts
8
8
  /**
9
9
  * Migrates the project to the latest schema version.
@@ -5,6 +5,7 @@ const require_template_loading = require("./template/template_loading.cjs");
5
5
  const require_block_pack = require("./block-pack/block_pack.cjs");
6
6
  const require_project_model_util = require("../model/project_model_util.cjs");
7
7
  const require_context_export = require("./context_export.cjs");
8
+ const require_template_serializer = require("../model/template_serializer.cjs");
8
9
  const require_index = require("../debug/index.cjs");
9
10
  let _platforma_sdk_model = require("@platforma-sdk/model");
10
11
  let _milaboratories_pl_model_middle_layer = require("@milaboratories/pl-model-middle-layer");
@@ -48,12 +49,14 @@ var BlockInfo = class {
48
49
  fields;
49
50
  config;
50
51
  source;
52
+ kind;
51
53
  logger;
52
- constructor(id, fields, config, source, logger = new _milaboratories_ts_helpers.ConsoleLoggerAdapter()) {
54
+ constructor(id, fields, config, source, kind, logger = new _milaboratories_ts_helpers.ConsoleLoggerAdapter()) {
53
55
  this.id = id;
54
56
  this.fields = fields;
55
57
  this.config = config;
56
58
  this.source = source;
59
+ this.kind = kind;
57
60
  this.logger = logger;
58
61
  }
59
62
  check() {
@@ -243,6 +246,32 @@ var ProjectMutator = class ProjectMutator {
243
246
  enrichmentTargets: this.projectHelper.getEnrichmentTargets(() => bInfo.config, () => args)
244
247
  };
245
248
  }
249
+ /**
250
+ * Render this project as a `template-v1` YAML document, or report every reason it
251
+ * cannot be.
252
+ *
253
+ * Read-only: it derives params in the VM and touches no field, so a `withProject`
254
+ * wrapper sees `wasModified === false` and skips the commit.
255
+ *
256
+ * This is where the three providers the serializer needs come from, and all are
257
+ * only reachable here. A block's storage lives behind `BlockInfo`, which is
258
+ * populated by the loader's batched round-trips, and both its kind reference and
259
+ * its origin spec are read off the block-pack container during that same load —
260
+ * `config` cannot carry the kind, since `extractConfig` normalizes the render
261
+ * envelope one level below it, and the spec is not part of the config at all.
262
+ */
263
+ exportAsTemplateV1() {
264
+ return require_template_serializer.exportProjectAsTemplateV1(this.struct, (blockId) => this.deriveTemplateParams(blockId), (blockId) => this.blockInfos.get(blockId)?.kind, (blockId) => this.blockInfos.get(blockId)?.source);
265
+ }
266
+ deriveTemplateParams(blockId) {
267
+ const info = this.blockInfos.get(blockId);
268
+ if (info === void 0) return void 0;
269
+ const storageJson = info.blockStorageJson;
270
+ if (storageJson === void 0) return void 0;
271
+ const derived = this.projectHelper.deriveTemplateParamsFromStorage(info.config, storageJson);
272
+ if (derived.error !== void 0) return { error: derived.error.message };
273
+ return { value: derived.value };
274
+ }
246
275
  getPendingProductionGraph() {
247
276
  if (this.pendingProductionGraph === void 0) this.pendingProductionGraph = require_project_model_util.productionGraph(this.struct, (blockId) => this.getProductionGraphBlockInfo(blockId, false));
248
277
  return this.pendingProductionGraph;
@@ -601,7 +630,7 @@ var ProjectMutator = class ProjectMutator {
601
630
  if (this.blocksInLimbo.delete(blockId)) this.renderingStateChanged = true;
602
631
  }
603
632
  initializeNewBlock(blockId, spec) {
604
- const info = new BlockInfo(blockId, {}, (0, _platforma_sdk_model.extractConfig)(spec.blockPack.config), spec.blockPack.source, this.projectHelper.logger);
633
+ const info = new BlockInfo(blockId, {}, (0, _platforma_sdk_model.extractConfig)(spec.blockPack.config), spec.blockPack.source, spec.blockPack.config.kind, this.projectHelper.logger);
605
634
  this.blockInfos.set(blockId, info);
606
635
  const bp = require_block_pack.createBlockPack(this.tx, spec.blockPack);
607
636
  this.setBlockField(blockId, "blockPack", _milaboratories_pl_client.Pl.wrapInHolder(this.tx, bp), "NotReady");
@@ -611,7 +640,7 @@ var ProjectMutator = class ProjectMutator {
611
640
  let prerunArgs;
612
641
  let storageToWrite;
613
642
  if (spec.storageMode === "fromModel") {
614
- storageToWrite = this.projectHelper.getInitialStorageInVM(blockConfig);
643
+ storageToWrite = spec.initialStorage ?? this.projectHelper.getInitialStorageInVM(blockConfig);
615
644
  prerunArgs = this.projectHelper.derivePrerunArgsFromStorage(blockConfig, storageToWrite);
616
645
  const deriveArgsResult = this.projectHelper.deriveArgsFromStorage(blockConfig, storageToWrite);
617
646
  args = deriveArgsResult.error ? void 0 : deriveArgsResult.value;
@@ -647,7 +676,7 @@ var ProjectMutator = class ProjectMutator {
647
676
  ]));
648
677
  }
649
678
  initializeBlockDuplicate(blockId, originalBlockInfo) {
650
- const info = new BlockInfo(blockId, {}, originalBlockInfo.config, originalBlockInfo.source, this.projectHelper.logger);
679
+ const info = new BlockInfo(blockId, {}, originalBlockInfo.config, originalBlockInfo.source, originalBlockInfo.kind, this.projectHelper.logger);
651
680
  this.blockInfos.set(blockId, info);
652
681
  const fieldNamesToDuplicate = this.getFieldNamesToDuplicate(blockId);
653
682
  for (const [fieldName, fieldState] of Object.entries(originalBlockInfo.fields)) if (fieldNamesToDuplicate.has(fieldName) && fieldState && fieldState.ref) this.setBlockFieldObj(blockId, fieldName, {
@@ -990,6 +1019,7 @@ var ProjectMutator = class ProjectMutator {
990
1019
  for (const [info, response] of blockPackRequests) {
991
1020
  const bpInfo = (0, _milaboratories_ts_helpers.cachedDeserialize)((0, _milaboratories_ts_helpers.notEmpty)((await response).data));
992
1021
  info.blockConfig = (0, _platforma_sdk_model.extractConfig)(bpInfo.config);
1022
+ info.blockKind = bpInfo.config.kind;
993
1023
  info.blockPack = bpInfo.source;
994
1024
  }
995
1025
  const ctxExportTplEnvelope = await require_context_export.getPreparedExportTemplateEnvelope();
@@ -1004,7 +1034,7 @@ var ProjectMutator = class ProjectMutator {
1004
1034
  const renderingState = { stagingRefreshTimestamp };
1005
1035
  const blocksInLimboSet = new Set(blocksInLimbo);
1006
1036
  const blockInfos = /* @__PURE__ */ new Map();
1007
- blockInfoStates.forEach(({ id, fields, blockConfig, blockPack }) => blockInfos.set(id, new BlockInfo(id, fields, (0, _milaboratories_ts_helpers.notEmpty)(blockConfig), (0, _milaboratories_ts_helpers.notEmpty)(blockPack), projectHelper.logger)));
1037
+ blockInfoStates.forEach(({ id, fields, blockConfig, blockKind, blockPack }) => blockInfos.set(id, new BlockInfo(id, fields, (0, _milaboratories_ts_helpers.notEmpty)(blockConfig), (0, _milaboratories_ts_helpers.notEmpty)(blockPack), blockKind, projectHelper.logger)));
1008
1038
  const blockInStruct = /* @__PURE__ */ new Set();
1009
1039
  for (const b of require_project_model_util.allBlocks(structure)) {
1010
1040
  if (!blockInfos.has(b.id)) throw new Error(`Inconsistent project structure: no inputs for ${b.id}`);