@platforma-sdk/block-tools 2.11.3 → 2.11.5

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 (51) hide show
  1. package/dist/cli.js +13 -13
  2. package/dist/cli.js.map +1 -1
  3. package/dist/cli.mjs +634 -580
  4. package/dist/cli.mjs.map +1 -1
  5. package/dist/config-BmJWc8sR.js +3 -0
  6. package/dist/config-BmJWc8sR.js.map +1 -0
  7. package/dist/{config-DpfTzcgG.mjs → config-CiC347sB.mjs} +453 -398
  8. package/dist/config-CiC347sB.mjs.map +1 -0
  9. package/dist/index.js +1 -1
  10. package/dist/index.mjs +15 -15
  11. package/dist/structure/engine/discovery.d.ts +2 -3
  12. package/dist/structure/rules/block-package-json.d.ts +3 -3
  13. package/dist/structure/rules/shared/pascal-case.d.ts +1 -0
  14. package/dist/v2/index.d.ts +1 -0
  15. package/dist/v2/model/block_components.d.ts +11 -0
  16. package/dist/v2/model/block_meta.d.ts +7 -0
  17. package/dist/v2/registry/schema_public.d.ts +3600 -3600
  18. package/dist/v2/resolve_to_registry.d.ts +37 -0
  19. package/dist/v2/source_package.d.ts +12 -0
  20. package/package.json +8 -8
  21. package/src/cmd/publish.test.ts +35 -0
  22. package/src/cmd/publish.ts +20 -0
  23. package/src/structure/__tests__/discovery-shortname.test.ts +3 -0
  24. package/src/structure/__tests__/facade-end-state.test.ts +195 -0
  25. package/src/structure/__tests__/model-package-json.snapshot.test.ts +1 -1
  26. package/src/structure/engine/__tests__/discovery.test.ts +22 -26
  27. package/src/structure/engine/discovery.ts +12 -30
  28. package/src/structure/rules/block-package-json.ts +146 -64
  29. package/src/structure/rules/block.ts +40 -5
  30. package/src/structure/rules/model-package-json.ts +6 -1
  31. package/src/structure/rules/shared/pascal-case.ts +16 -0
  32. package/src/structure/rules/software-package-json.ts +6 -1
  33. package/src/structure/rules/test-package-json.ts +5 -1
  34. package/src/structure/rules/ui-package-json.ts +5 -1
  35. package/src/structure/rules/workflow-package-json.ts +5 -1
  36. package/src/structure/templates/static/block/src/AGENTS.ts +5 -0
  37. package/src/structure/templates/static/block/src/agents-extra.ts +8 -0
  38. package/src/structure/templates/static/block/src/block-extra.ts +9 -0
  39. package/src/structure/templates/static/block/tsconfig.json +4 -0
  40. package/src/structure/templates/text/block/src/index.tpl.ts +55 -0
  41. package/src/v2/from_pack_v2.test.ts +142 -0
  42. package/src/v2/index.ts +1 -0
  43. package/src/v2/model/block_components.ts +33 -0
  44. package/src/v2/model/block_meta.ts +18 -0
  45. package/src/v2/resolve_to_registry.ts +84 -0
  46. package/src/v2/source_package.ts +34 -1
  47. package/dist/config-DpfTzcgG.mjs.map +0 -1
  48. package/dist/config-xxVy3IQR.js +0 -3
  49. package/dist/config-xxVy3IQR.js.map +0 -1
  50. package/src/structure/templates/static/block/index.d.ts +0 -6
  51. package/src/structure/templates/static/block/index.js +0 -8
@@ -1,24 +1,67 @@
1
- // Block-level (orchestrator) `package.json`: the initial generator and the
2
- // drift-correcting body rules, co-located. Workspace-scope deps reflect the
3
- // discovered modules at run time; the generator pre-computes them and the
4
- // body rule re-asserts them.
1
+ // Facade `package.json`: the initial generator and the drift-correcting body
2
+ // rules, co-located. The facade is the only sibling of a block that publishes,
3
+ // and it ships SLIM: `dependencies` is empty (the consumer's install pulls zero
4
+ // `@platforma-sdk/*` transitive runtime deps), siblings are build-time
5
+ // `workspace:*` devDeps, and the built `dist/` + `block-pack/` carry everything
6
+ // the loader needs.
5
7
 
6
8
  import {
7
9
  ensureField,
8
10
  ensureScript,
9
11
  ensureDevDep,
10
- ensureWorkspaceScopeDeps,
12
+ ensureWorkspaceScopeDevDeps,
13
+ requireField,
14
+ removeScript,
11
15
  enforceAlphabeticalOrder,
12
16
  enforceFieldOrder,
13
17
  type RunContext,
14
18
  } from "../engine/api";
15
- import { findModules, scopeDepMap } from "../engine/ctx";
19
+ import { findModules, scopeDepMaps } from "../engine/ctx";
16
20
  import { canonicalPackageJsonOrder } from "./shared/key-order";
17
21
 
22
+ /** Per-org publish coordinates: the S3 upload bucket and the public registry
23
+ * serve URL the facade's `prepublishOnly` script passes to `block-tools
24
+ * publish`. One block-registry bucket serves both orgs; only the public read
25
+ * hostname differs by org. Safe for the never-published `etc/blocks` test
26
+ * blocks — the script never runs for them. Extend the map per new org. */
27
+ const ORG_PUBLISH_TARGETS: Record<string, { s3: string; serveUrl: string }> = {
28
+ "@platforma-open": {
29
+ s3: "s3://milab-euce1-prod-pkgs-s3-block-registry/pub/releases/?region=eu-central-1",
30
+ serveUrl: "https://blocks.pl-open.science",
31
+ },
32
+ "@milaboratories": {
33
+ s3: "s3://milab-euce1-prod-pkgs-s3-block-registry/pub/releases/?region=eu-central-1",
34
+ serveUrl: "https://block.registry.platforma.bio/releases",
35
+ },
36
+ };
37
+
38
+ /** Fallback for an org not in the table — keeps the generated script valid
39
+ * (and inert until published) rather than throwing during a refresh. */
40
+ const FALLBACK_PUBLISH_TARGET = {
41
+ s3: "s3://milab-euce1-prod-pkgs-s3-block-registry/pub/releases/?region=eu-central-1",
42
+ serveUrl: "https://blocks.pl-open.science",
43
+ };
44
+
45
+ /** Where the in-SDK blocks (`etc/blocks/*`) publish: the aux/dev registry, not
46
+ * production `pub/releases/`. These are the monorepo's test/example blocks, so
47
+ * one target regardless of org — their `@milaboratories` scope must NOT route
48
+ * them to the real catalog alongside standalone blocks. */
49
+ const SDK_INTERNAL_PUBLISH_TARGET = {
50
+ s3: "s3://milab-euce1-prod-pkgs-s3-block-registry/aux/dev/?region=eu-central-1",
51
+ serveUrl: "https://aux-blocks.pl-open.science",
52
+ };
53
+
54
+ function prepublishScript(npmOrg: string, isSdkInternal: boolean): string {
55
+ const t = isSdkInternal
56
+ ? SDK_INTERNAL_PUBLISH_TARGET
57
+ : (ORG_PUBLISH_TARGETS[npmOrg] ?? FALLBACK_PUBLISH_TARGET);
58
+ return `block-tools publish -r ${t.s3} --registry-serve-url ${t.serveUrl}`;
59
+ }
60
+
18
61
  /** The facade's `block.components` map: the dist artifact each module build
19
- * emits, keyed by role. Fully determined by the discovered modules — the
20
- * body rule re-asserts this exact value, so it is engine-managed (NOT an
21
- * author seed). Mirrors the artifact paths produced by each scope's build:
62
+ * emits, keyed by role. Fully determined by the discovered modules — the body
63
+ * rule re-asserts this exact value, so it is engine-managed (NOT an author
64
+ * seed). Mirrors the artifact paths produced by each scope's build:
22
65
  * workflow → pl-tengo, model → build-model, ui → ts-builder. */
23
66
  export function blockComponents(ctx: RunContext): Record<string, string> {
24
67
  const components: Record<string, string> = {};
@@ -29,6 +72,44 @@ export function blockComponents(ctx: RunContext): Record<string, string> {
29
72
  return components;
30
73
  }
31
74
 
75
+ /** The slim facade's canonical scripts (build / check / prepublishOnly /
76
+ * do-pack). Shared by the initial generator and the body rule so they cannot
77
+ * drift apart. `prepublishOnly` routes by block kind: in-SDK blocks publish to
78
+ * the aux/dev registry, standalone blocks to their per-org production target. */
79
+ function blockScripts(npmOrg: string, isSdkInternal: boolean): Record<string, string> {
80
+ return {
81
+ build: "ts-builder build --target block-facade && block-tools pack",
82
+ check: "ts-builder type-check --target block-facade",
83
+ prepublishOnly: prepublishScript(npmOrg, isSdkInternal),
84
+ "do-pack": "pnpm pack && shx mv *.tgz package.tgz",
85
+ };
86
+ }
87
+
88
+ /** The facade's build/publish devDependencies (the workspace `model`/`ui`/
89
+ * `workflow` siblings are added separately, from discovery). `sdk:` resolves
90
+ * to `workspace:*` for in-monorepo blocks and `catalog:` for standalone — the
91
+ * body rule re-asserts the same set. */
92
+ const FACADE_DEV_DEPS = {
93
+ "@milaboratories/ts-builder": "sdk:",
94
+ "@milaboratories/ts-configs": "sdk:",
95
+ "@platforma-sdk/block-tools": "sdk:",
96
+ "@platforma-sdk/model": "sdk:",
97
+ shx: "catalog:",
98
+ typescript: "catalog:",
99
+ } as const;
100
+
101
+ /** The facade's `exports` map: a single `.` entry. `sources` keeps monorepo IDE
102
+ * go-to-definition pointing at `src/` (external consumers don't enable it);
103
+ * `types`/`default` carry the bundled `.d.ts` / `.js` from `dist/`. No
104
+ * `./AGENTS` subpath — that surface ships as a registry-side component. */
105
+ const FACADE_EXPORTS = {
106
+ ".": {
107
+ sources: "./src/index.ts",
108
+ types: "./dist/index.d.ts",
109
+ default: "./dist/index.js",
110
+ },
111
+ };
112
+
32
113
  /** Fail loudly if any required `block.*` field is empty — a missing
33
114
  * title / description / logo / organization or empty components map would
34
115
  * otherwise produce an init block that `block-tools pack` rejects only
@@ -63,20 +144,18 @@ function assertBlockFieldsFilled(block: {
63
144
 
64
145
  export function blockPackageJsonInitial(ctx: RunContext): Record<string, unknown> {
65
146
  const v = ctx.blockVars;
66
- // The facade depends on its model / ui / workflow modules — not on its
67
- // software module (the workflow owns that dep) and not on its test module
68
- // (the test package depends on the facade, so a reciprocal dep would be a
69
- // pnpm/turbo cycle).
70
- const deps: Record<string, string> = {
71
- ...scopeDepMap(ctx, "model"),
72
- ...scopeDepMap(ctx, "ui"),
73
- ...scopeDepMap(ctx, "workflow"),
147
+ // The facade's siblings are build-time devDeps, never runtime deps — the
148
+ // facade ships its content bundled inside dist/ + block-pack/, so the
149
+ // consumer needs no runtime resolution into siblings. Software/test are
150
+ // excluded (the workflow owns software; a test→facade dep would be a cycle).
151
+ const devDeps: Record<string, string> = {
152
+ ...FACADE_DEV_DEPS,
153
+ ...scopeDepMaps(ctx, "model"),
154
+ ...scopeDepMaps(ctx, "ui"),
155
+ ...scopeDepMaps(ctx, "workflow"),
74
156
  };
75
- // `block.components` is engine-managed (re-asserted by the body rule);
76
- // `block.meta` is an author SEED — written once at init, never touched on
77
- // refresh, so the author owns title / description / organization / logo.
78
- // `block-tools pack` requires both. The author edits the placeholder meta
79
- // before publishing.
157
+ // `block-tools pack` requires both `block.components` and `block.meta`. The
158
+ // author edits the placeholder meta before publishing.
80
159
  const block = {
81
160
  components: blockComponents(ctx),
82
161
  meta: {
@@ -94,59 +173,62 @@ export function blockPackageJsonInitial(ctx: RunContext): Record<string, unknown
94
173
  return {
95
174
  name: `${v.facadeName}.block`,
96
175
  version: "1.0.0",
97
- // type:"module" intentionally omitted.
98
- files: ["index.d.ts", "index.js"],
99
- scripts: {
100
- build: "shx rm -rf ./block-pack && block-tools pack",
101
- "mark-stable":
102
- "block-tools mark-stable -r 's3://milab-euce1-prod-pkgs-s3-block-registry/pub/releases/?region=eu-central-1'",
103
- prepublishOnly:
104
- "block-tools pack && block-tools publish -r 's3://milab-euce1-prod-pkgs-s3-block-registry/pub/releases/?region=eu-central-1'",
105
- "do-pack": "shx rm -f *.tgz && block-tools pack && pnpm pack && shx mv *.tgz package.tgz",
106
- },
107
- dependencies: deps,
108
- devDependencies: { "@platforma-sdk/block-tools": "sdk:", shx: "catalog:" },
176
+ // Facade is ESM: rolldown-plugin-dts requires it, and the BlockPointer's
177
+ // `import.meta.url` path math needs it.
178
+ type: "module",
179
+ // Drives the npm tarball: only the built outputs ship; sources, tsconfig,
180
+ // and node_modules stay out.
181
+ files: ["dist", "block-pack"],
182
+ main: "./dist/index.js",
183
+ module: "./dist/index.js",
184
+ types: "./dist/index.d.ts",
185
+ exports: FACADE_EXPORTS,
186
+ scripts: blockScripts(v.npmOrg, ctx.isSdkInternal),
187
+ // Slim invariant: zero runtime deps reach the consumer.
188
+ dependencies: {},
189
+ devDependencies: devDeps,
109
190
  block,
110
191
  };
111
192
  }
112
193
 
113
194
  export function blockPackageJsonRules(ctx: RunContext): void {
114
- // type:"module" intentionally omitted: the facade's index.js is the
115
- // CommonJS dev-block descriptor (module.exports / __dirname); ESM would
116
- // break it. The facade has no TS sources to compile.
117
- ensureField("files", ["index.d.ts", "index.js"]);
118
-
119
- ensureScript("build", "shx rm -rf ./block-pack && block-tools pack");
120
- ensureScript(
121
- "mark-stable",
122
- "block-tools mark-stable -r 's3://milab-euce1-prod-pkgs-s3-block-registry/pub/releases/?region=eu-central-1'",
123
- );
124
- ensureScript(
125
- "prepublishOnly",
126
- "block-tools pack && block-tools publish -r 's3://milab-euce1-prod-pkgs-s3-block-registry/pub/releases/?region=eu-central-1'",
127
- );
128
- ensureScript(
129
- "do-pack",
130
- "shx rm -f *.tgz && block-tools pack && pnpm pack && shx mv *.tgz package.tgz",
131
- );
132
-
133
- // One workspace dep per discovered module in the facade's scopes. The
134
- // facade depends on model / ui / workflow — not software (the workflow
135
- // owns it) and not test (a reciprocal dep would be a pnpm/turbo cycle).
136
- ensureWorkspaceScopeDeps("model");
137
- ensureWorkspaceScopeDeps("ui");
138
- ensureWorkspaceScopeDeps("workflow");
195
+ const v = ctx.blockVars;
196
+
197
+ ensureField("type", "module");
198
+ ensureField("files", ["dist", "block-pack"]);
199
+ ensureField("main", "./dist/index.js");
200
+ ensureField("module", "./dist/index.js");
201
+ ensureField("types", "./dist/index.d.ts");
202
+ ensureField("exports", FACADE_EXPORTS);
139
203
 
204
+ const scripts = blockScripts(v.npmOrg, ctx.isSdkInternal);
205
+ for (const [name, command] of Object.entries(scripts)) ensureScript(name, command);
206
+ removeScript("mark-stable");
207
+
208
+ // Slim invariant: the facade carries ZERO runtime dependencies. The siblings
209
+ // and the SDK build/publish deps are all devDeps, so `dependencies` ends
210
+ // empty. Software/test are excluded (the workflow owns software; a
211
+ // test→facade dep would be a cycle).
212
+ ensureWorkspaceScopeDevDeps("model");
213
+ ensureWorkspaceScopeDevDeps("ui");
214
+ ensureWorkspaceScopeDevDeps("workflow");
215
+ ensureDevDep("@milaboratories/ts-builder", "sdk:");
216
+ ensureDevDep("@milaboratories/ts-configs", "sdk:");
140
217
  ensureDevDep("@platforma-sdk/block-tools", "sdk:");
141
- // shx powers the cross-platform build / do-pack scripts above.
218
+ ensureDevDep("@platforma-sdk/model", "sdk:");
142
219
  ensureDevDep("shx", "catalog:");
220
+ ensureDevDep("typescript", "catalog:");
143
221
 
144
- // `block.components` is fully determined by the discovered modules — keep
145
- // it in sync on refresh. `block.meta` is deliberately NOT touched here: it
146
- // is an author-owned seed (set once in the init package.json), so refresh
147
- // must never overwrite the author's title / description / logo.
222
+ // `block.components` is fully determined by the discovered modules — keep it
223
+ // in sync on refresh. `block.meta` is deliberately NOT touched: it is an
224
+ // author-owned seed (set once in the init package.json), so refresh must
225
+ // never overwrite the author's title / description / logo.
148
226
  ensureField("block.components", blockComponents(ctx));
149
227
 
228
+ // The author owns `name` and `version`; `version` MUST be present (the
229
+ // facade publishes).
230
+ requireField("version", "facade package.json must carry a 'version' (the facade publishes)");
231
+
150
232
  enforceAlphabeticalOrder("dependencies");
151
233
  enforceAlphabeticalOrder("devDependencies");
152
234
  enforceAlphabeticalOrder("peerDependencies");
@@ -1,13 +1,48 @@
1
- // Block-scope rules — the orchestrator package at `block/`.
2
- // Workspace-scope deps in the body resolve from ctx.modules.
1
+ // Block-scope rules — the facade package at `block/`. The facade is the
2
+ // only sibling of a block that publishes: a slim 4-file `src/` surface
3
+ // (engine-owned `index.ts` + `AGENTS.ts`, author-owned `block-extra.ts` +
4
+ // `agents-extra.ts`), a facade `tsconfig.json`, and a slim publishable
5
+ // `package.json`. Workspace-scope deps in the body resolve from ctx.modules.
3
6
 
4
- import { scope, fixed, managed, seed, file, binaryFile, generate } from "../engine/api";
7
+ import {
8
+ scope,
9
+ fixed,
10
+ scaffold,
11
+ managed,
12
+ seed,
13
+ remove,
14
+ file,
15
+ tpl,
16
+ binaryFile,
17
+ generate,
18
+ } from "../engine/api";
5
19
  import { blockPackageJsonInitial, blockPackageJsonRules } from "./block-package-json";
20
+ import { pascalCase } from "./shared/pascal-case";
6
21
 
7
22
  export function blockRules(): void {
8
23
  scope("block", () => {
9
- fixed("index.js", file("block/index.js"));
10
- fixed("index.d.ts", file("block/index.d.ts"));
24
+ // `remove` is idempotent (absent file → no-op), so this is safe on a block
25
+ // that has no such entry files.
26
+ remove("index.js");
27
+ remove("index.d.ts");
28
+
29
+ // Four-file source surface. `index.ts` is engine-owned and per-block
30
+ // (model import path + `<PascalName>` aliases) → text-templated. `AGENTS.ts`
31
+ // is engine-owned and identical for every block → static. The two `*-extra`
32
+ // files are author scaffolds (`scaffold`: create-if-missing on every refresh,
33
+ // never overwritten).
34
+ fixed(
35
+ "src/index.ts",
36
+ tpl("block/src/index.tpl.ts", (ctx) => ({
37
+ facadeName: ctx.blockVars.facadeName,
38
+ pascalName: pascalCase(ctx.blockVars.shortName),
39
+ })),
40
+ );
41
+ fixed("src/AGENTS.ts", file("block/src/AGENTS.ts"));
42
+ scaffold("src/block-extra.ts", file("block/src/block-extra.ts"));
43
+ scaffold("src/agents-extra.ts", file("block/src/agents-extra.ts"));
44
+
45
+ fixed("tsconfig.json", file("block/tsconfig.json"));
11
46
 
12
47
  // Placeholder logos referenced by `block.meta` (logo + organization.logo).
13
48
  // Binary seeds: written once at init, author-owned thereafter — refresh
@@ -33,7 +33,7 @@ export function modelPackageJsonInitial(ctx: RunContext): Record<string, unknown
33
33
  const v = ctx.blockVars;
34
34
  return {
35
35
  name: `${v.facadeName}.model`,
36
- version: "1.0.0",
36
+ private: true,
37
37
  type: "module",
38
38
  // The block-model build emits both index.cjs and index.js. `main` is
39
39
  // the CommonJS entry (require fallback); `module` the ESM entry.
@@ -70,6 +70,11 @@ export function modelPackageJsonInitial(ctx: RunContext): Record<string, unknown
70
70
  }
71
71
 
72
72
  export function modelPackageJsonRules(): void {
73
+ // Controlled sibling — workspace-only, never published. `private: true` makes
74
+ // npm refuse to publish it; the `version` is kept (changesets-owned) so the
75
+ // packed template's lib carries a version.
76
+ ensureField("private", true);
77
+
73
78
  ensureField("type", "module");
74
79
  // build emits both; main = CJS entry, module = ESM entry (prod convention).
75
80
  ensureField("main", "dist/index.cjs");
@@ -0,0 +1,16 @@
1
+ // `pascalCase(shortName)` → the block-named alias prefix the facade entry
2
+ // template emits (`<PascalName>BlockContract`, …). Splits on any run of
3
+ // non-alphanumeric characters and capitalises each segment's first char:
4
+ // test-sum-numbers-v3 → TestSumNumbersV3
5
+ // mixcr-clonotyping-2 → MixcrClonotyping2
6
+ // samples-and-data → SamplesAndData
7
+ export function pascalCase(s: string): string {
8
+ const joined = s
9
+ .split(/[^A-Za-z0-9]+/)
10
+ .filter((seg) => seg.length > 0)
11
+ .map((seg) => seg.charAt(0).toUpperCase() + seg.slice(1))
12
+ .join("");
13
+ // A leading digit (e.g. `3d-viewer` → `3dViewer`) is not a valid identifier
14
+ // start — the prefix becomes `const 3dViewerBlockPointer = …`. Prepend `_`.
15
+ return /^[0-9]/.test(joined) ? `_${joined}` : joined;
16
+ }
@@ -46,7 +46,7 @@ export function softwarePackageJsonInitial(ctx: RunContext): Record<string, unkn
46
46
  const v = ctx.blockVars;
47
47
  return {
48
48
  name: `${v.facadeName}.software`,
49
- version: "1.0.0",
49
+ private: true,
50
50
  type: "module",
51
51
  description: "Block Software",
52
52
  files: ["./dist/**/*"],
@@ -65,6 +65,11 @@ export function softwarePackageJsonInitial(ctx: RunContext): Record<string, unkn
65
65
  }
66
66
 
67
67
  export function softwarePackageJsonRules(): void {
68
+ // Controlled sibling — workspace-only, never npm-published. (Software still
69
+ // publishes runenv artifacts via `pl-pkg`, which is unaffected by `private`.)
70
+ // The `version` is kept (changesets-owned) so the packed template carries it.
71
+ ensureField("private", true);
72
+
68
73
  ensureField("type", "module");
69
74
  ensureField("files", ["./dist/**/*"]);
70
75
 
@@ -19,7 +19,6 @@ export function testPackageJsonInitial(ctx: RunContext): Record<string, unknown>
19
19
  const v = ctx.blockVars;
20
20
  return {
21
21
  name: `${v.facadeName}.test`,
22
- version: "1.0.0",
23
22
  private: true,
24
23
  type: "module",
25
24
  scripts: {
@@ -41,6 +40,11 @@ export function testPackageJsonInitial(ctx: RunContext): Record<string, unknown>
41
40
  }
42
41
 
43
42
  export function testPackageJsonRules(): void {
43
+ // Controlled sibling — workspace-only, never published. (The initial already
44
+ // seeds `private: true`; the body re-asserts it. The `version` is kept —
45
+ // changesets-owned — so the packed template's lib carries a version.)
46
+ ensureField("private", true);
47
+
44
48
  ensureField("type", "module");
45
49
 
46
50
  ensureScript("fmt", "ts-builder format");
@@ -32,7 +32,7 @@ export function uiPackageJsonInitial(ctx: RunContext): Record<string, unknown> {
32
32
  const v = ctx.blockVars;
33
33
  return {
34
34
  name: `${v.facadeName}.ui`,
35
- version: "1.0.0",
35
+ private: true,
36
36
  type: "module",
37
37
  scripts: {
38
38
  fmt: "ts-builder format",
@@ -60,6 +60,10 @@ export function uiPackageJsonInitial(ctx: RunContext): Record<string, unknown> {
60
60
  }
61
61
 
62
62
  export function uiPackageJsonRules(): void {
63
+ // Controlled sibling — workspace-only, never published, but kept versioned
64
+ // (changesets-owned) so the packed template's lib carries a version.
65
+ ensureField("private", true);
66
+
63
67
  ensureField("type", "module");
64
68
 
65
69
  ensureScript("fmt", "ts-builder format");
@@ -25,7 +25,7 @@ export function workflowPackageJsonInitial(ctx: RunContext): Record<string, unkn
25
25
  const v = ctx.blockVars;
26
26
  return {
27
27
  name: `${v.facadeName}.workflow`,
28
- version: "1.0.0",
28
+ private: true,
29
29
  type: "module",
30
30
  scripts: {
31
31
  // No `fmt`: the workflow is Tengo, not TS — nothing for oxlint/oxfmt
@@ -54,6 +54,10 @@ export function workflowPackageJsonInitial(ctx: RunContext): Record<string, unkn
54
54
  }
55
55
 
56
56
  export function workflowPackageJsonRules(): void {
57
+ // Controlled sibling — workspace-only, never published, but kept versioned
58
+ // (changesets-owned) so the packed template's lib carries a version.
59
+ ensureField("private", true);
60
+
57
61
  ensureField("type", "module");
58
62
 
59
63
  // No `fmt` script: the workflow is Tengo, not TS — build + check run via
@@ -0,0 +1,5 @@
1
+ // This file is managed by `block-tools structure`. Do not edit by hand.
2
+ // Narrow MCP / AI surface.
3
+
4
+ export type { BlockContract, BlockOutputs, BlockData } from "./index";
5
+ export * from "./agents-extra";
@@ -0,0 +1,8 @@
1
+ // Author-owned. `block-tools structure` does not modify this file.
2
+ // MCP / AI extension surface. Add types and functions the agent
3
+ // surface should expose.
4
+ //
5
+ // In the future this file will host JS functions the MCP runtime
6
+ // can execute. The `.d.ts` declarations stay visible to the agent;
7
+ // the JS bodies execute in the MCP code-execution context (the
8
+ // agent sees the types but not the implementation).
@@ -0,0 +1,9 @@
1
+ // Author-owned. `block-tools structure` does not modify this file.
2
+ // Add block-specific helper types or values the consumer surface
3
+ // should expose. Anything you `export` here flows out via the
4
+ // main entry (./index re-exports this file).
5
+ //
6
+ // Do NOT redefine: BlockContract, BlockOutputs, BlockData,
7
+ // BlockPointer, platforma, or the block-named <PascalName>Block*
8
+ // aliases — those names come from ./index and `export *` from this
9
+ // file would shadow them.
@@ -0,0 +1,4 @@
1
+ {
2
+ "extends": "@milaboratories/ts-configs/block/facade",
3
+ "include": ["src/**/*"]
4
+ }
@@ -0,0 +1,55 @@
1
+ // This file is managed by `block-tools structure`. Do not edit by hand.
2
+ // Author content lives in ./block-extra.ts.
3
+
4
+ import { platforma } from "${facadeName}.model";
5
+ import {
6
+ InferOutputsType,
7
+ InferDataType,
8
+ InferHrefType,
9
+ } from "@platforma-sdk/model";
10
+
11
+ export { platforma };
12
+
13
+ export type BlockContract = {
14
+ outputs: InferOutputsType<typeof platforma>;
15
+ data: InferDataType<typeof platforma>;
16
+ href: InferHrefType<typeof platforma>;
17
+ };
18
+
19
+ export type BlockOutputs = BlockContract["outputs"];
20
+ export type BlockData = BlockContract["data"];
21
+
22
+ // import.meta.url is a file: URL (always forward-slash, even on Windows:
23
+ // file:///C:/…). We expose URLs, NOT paths — the facade stays dependency-free
24
+ // and loadable in minimal engines (e.g. QuickJS), and each consumer converts
25
+ // at its own edge with the right tool (fileURLToPath in Node), where Windows
26
+ // drive letters / %-encoding / UNC are handled correctly. The bundled entry
27
+ // sits one dir under the package root (dist/index.js, or src/index.ts in dev),
28
+ // so the root is two URL segments up. The structurer owns this layout —
29
+ // consumers read these URLs, they never reconstruct <root>/block-pack.
30
+ //
31
+ // TypeScript ships `ImportMeta.url` only in the `dom`/`webworker` libs; the
32
+ // facade tsconfig is lib-minimal (no `dom`, no `@types/node`) by design. We
33
+ // type the one ESM-standard member we use with a local cast rather than a
34
+ // `declare global` — a global augmentation would leak into the published
35
+ // `dist/index.d.ts` and clash with `@types/node`'s `ImportMeta` in full-Node
36
+ // consumers (test packages, the Middle Layer).
37
+ const selfUrl = (import.meta as ImportMeta & { url: string }).url;
38
+ const dirUrl = selfUrl.slice(0, selfUrl.lastIndexOf("/"));
39
+ const rootUrl = dirUrl.slice(0, dirUrl.lastIndexOf("/"));
40
+
41
+ export const BlockPointer = {
42
+ type: "from-pack-v2" as const,
43
+ packUrl: rootUrl + "/block-pack",
44
+ rootUrl,
45
+ } as const;
46
+
47
+ // Block-named aliases for readable cross-block imports in tests and
48
+ // consumer code. Same types / same runtime value as the universal
49
+ // names above; the aliases avoid `as`-renames at the import site.
50
+ export type ${pascalName}BlockContract = BlockContract;
51
+ export type ${pascalName}BlockOutputs = BlockOutputs;
52
+ export type ${pascalName}BlockData = BlockData;
53
+ export const ${pascalName}BlockPointer = BlockPointer;
54
+
55
+ export * from "./block-extra";