@platforma-sdk/block-tools 2.11.2 → 2.11.4
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.
- package/bin/run.js +9 -5
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +13 -13
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +845 -981
- package/dist/cli.mjs.map +1 -1
- package/dist/cmd/build-meta.d.ts +2 -9
- package/dist/cmd/build-model.d.ts +2 -10
- package/dist/cmd/list-overview-snapshots.d.ts +2 -9
- package/dist/cmd/mark-stable.d.ts +2 -13
- package/dist/cmd/pack.d.ts +2 -9
- package/dist/cmd/publish.d.ts +2 -12
- package/dist/cmd/refresh-registry.d.ts +2 -9
- package/dist/cmd/restore-overview-from-snapshot.d.ts +2 -10
- package/dist/cmd/structure/check.d.ts +2 -10
- package/dist/cmd/structure/init.d.ts +2 -18
- package/dist/cmd/structure/refresh.d.ts +10 -11
- package/dist/cmd/update-deps.d.ts +2 -6
- package/dist/cmd/upload-package-v1.d.ts +2 -14
- package/dist/config-BmJWc8sR.js +3 -0
- package/dist/config-BmJWc8sR.js.map +1 -0
- package/dist/{config-DpfTzcgG.mjs → config-CiC347sB.mjs} +453 -398
- package/dist/config-CiC347sB.mjs.map +1 -0
- package/dist/index.js +1 -1
- package/dist/index.mjs +15 -15
- package/dist/registry_v1/config_schema.d.ts +7 -7
- package/dist/registry_v1/flags.d.ts +2 -4
- package/dist/structure/cli/run-structure.d.ts +1 -1
- package/dist/structure/engine/discovery.d.ts +2 -3
- package/dist/structure/rules/block-package-json.d.ts +3 -3
- package/dist/structure/rules/shared/pascal-case.d.ts +1 -0
- package/dist/v2/index.d.ts +1 -0
- package/dist/v2/model/block_components.d.ts +11 -0
- package/dist/v2/model/block_meta.d.ts +7 -0
- package/dist/v2/resolve_to_registry.d.ts +37 -0
- package/dist/v2/source_package.d.ts +12 -0
- package/package.json +9 -24
- package/src/cli.test.ts +56 -0
- package/src/cli.ts +50 -0
- package/src/cmd/build-meta.ts +11 -22
- package/src/cmd/build-model.ts +16 -33
- package/src/cmd/list-overview-snapshots.ts +24 -28
- package/src/cmd/mark-stable.ts +35 -55
- package/src/cmd/pack.ts +11 -22
- package/src/cmd/publish.test.ts +35 -0
- package/src/cmd/publish.ts +55 -49
- package/src/cmd/refresh-registry.ts +22 -26
- package/src/cmd/restore-overview-from-snapshot.ts +24 -34
- package/src/cmd/structure/check.ts +19 -28
- package/src/cmd/structure/init.ts +40 -47
- package/src/cmd/structure/refresh.ts +45 -36
- package/src/cmd/update-deps.ts +22 -12
- package/src/cmd/upload-package-v1.ts +42 -60
- package/src/registry_v1/flags.ts +7 -7
- package/src/structure/__tests__/discovery-shortname.test.ts +3 -0
- package/src/structure/__tests__/facade-end-state.test.ts +195 -0
- package/src/structure/__tests__/model-package-json.snapshot.test.ts +1 -1
- package/src/structure/cli/run-structure.ts +2 -2
- package/src/structure/engine/__tests__/discovery.test.ts +22 -26
- package/src/structure/engine/discovery.ts +12 -30
- package/src/structure/rules/block-package-json.ts +146 -64
- package/src/structure/rules/block.ts +40 -5
- package/src/structure/rules/model-package-json.ts +6 -1
- package/src/structure/rules/shared/pascal-case.ts +16 -0
- package/src/structure/rules/software-package-json.ts +6 -1
- package/src/structure/rules/test-package-json.ts +5 -1
- package/src/structure/rules/ui-package-json.ts +5 -1
- package/src/structure/rules/workflow-package-json.ts +5 -1
- package/src/structure/templates/static/block/src/AGENTS.ts +5 -0
- package/src/structure/templates/static/block/src/agents-extra.ts +8 -0
- package/src/structure/templates/static/block/src/block-extra.ts +9 -0
- package/src/structure/templates/static/block/tsconfig.json +4 -0
- package/src/structure/templates/text/block/src/index.tpl.ts +55 -0
- package/src/v2/from_pack_v2.test.ts +142 -0
- package/src/v2/index.ts +1 -0
- package/src/v2/model/block_components.ts +33 -0
- package/src/v2/model/block_meta.ts +18 -0
- package/src/v2/model/content_conversion.ts +0 -1
- package/src/v2/resolve_to_registry.ts +84 -0
- package/src/v2/source_package.ts +34 -1
- package/bin/dev.cmd +0 -3
- package/bin/dev.js +0 -6
- package/dist/cmd/index.d.ts +0 -28
- package/dist/config-DpfTzcgG.mjs.map +0 -1
- package/dist/config-xxVy3IQR.js +0 -3
- package/dist/config-xxVy3IQR.js.map +0 -1
- package/src/cmd/index.ts +0 -31
- package/src/structure/templates/static/block/index.d.ts +0 -6
- package/src/structure/templates/static/block/index.js +0 -8
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
// Workspace-package classification — covers
|
|
2
|
-
//
|
|
1
|
+
// Workspace-package classification — covers the detection rules plus the
|
|
2
|
+
// block-scope override and the unclassified-error path.
|
|
3
3
|
|
|
4
4
|
import { describe, test, expect } from "vitest";
|
|
5
5
|
import { classifyOne, discoverModules, DiscoveryError, type WorkspacePackage } from "../discovery";
|
|
@@ -14,7 +14,7 @@ describe("discovery — single-package classification", () => {
|
|
|
14
14
|
name: "x.software",
|
|
15
15
|
"block-software": {},
|
|
16
16
|
});
|
|
17
|
-
expect(classifyOne(wp
|
|
17
|
+
expect(classifyOne(wp)).toBe("software");
|
|
18
18
|
});
|
|
19
19
|
|
|
20
20
|
test("rule 1 alt: software via package-builder + runenv dep", () => {
|
|
@@ -25,67 +25,62 @@ describe("discovery — single-package classification", () => {
|
|
|
25
25
|
"@platforma-open/runenv-python-3": "catalog:",
|
|
26
26
|
},
|
|
27
27
|
});
|
|
28
|
-
expect(classifyOne(wp
|
|
28
|
+
expect(classifyOne(wp)).toBe("software");
|
|
29
29
|
});
|
|
30
30
|
|
|
31
|
-
test("
|
|
31
|
+
test("workflow via workflow-tengo dep", () => {
|
|
32
32
|
const wp = pkg("workflow", {
|
|
33
33
|
name: "x.workflow",
|
|
34
34
|
dependencies: { "@platforma-sdk/workflow-tengo": "catalog:" },
|
|
35
35
|
});
|
|
36
|
-
expect(classifyOne(wp
|
|
36
|
+
expect(classifyOne(wp)).toBe("workflow");
|
|
37
37
|
});
|
|
38
38
|
|
|
39
|
-
test("
|
|
39
|
+
test("ui via ui-vue dep and no main", () => {
|
|
40
40
|
const wp = pkg("ui", {
|
|
41
41
|
name: "x.ui",
|
|
42
42
|
dependencies: { "@platforma-sdk/ui-vue": "catalog:" },
|
|
43
43
|
});
|
|
44
|
-
expect(classifyOne(wp
|
|
44
|
+
expect(classifyOne(wp)).toBe("ui");
|
|
45
45
|
});
|
|
46
46
|
|
|
47
|
-
test("
|
|
48
|
-
const siblings = new Set(["x.model", "x.ui", "x.workflow"]);
|
|
47
|
+
test("block via the top-level `block` field", () => {
|
|
49
48
|
const wp = pkg("block", {
|
|
50
49
|
name: "x.block",
|
|
51
|
-
|
|
52
|
-
"@platforma-sdk/block-tools": "catalog:",
|
|
53
|
-
"x.model": "workspace:*",
|
|
54
|
-
"x.ui": "workspace:*",
|
|
55
|
-
},
|
|
50
|
+
block: { components: {}, meta: {} },
|
|
56
51
|
});
|
|
57
|
-
expect(classifyOne(wp
|
|
52
|
+
expect(classifyOne(wp)).toBe("block");
|
|
58
53
|
});
|
|
59
54
|
|
|
60
|
-
test("
|
|
55
|
+
test("a block-tools-ish package WITHOUT a `block` field is not classified `block`", () => {
|
|
61
56
|
const wp = pkg("block", {
|
|
62
57
|
name: "x.block",
|
|
63
58
|
files: ["index.d.ts", "index.js"],
|
|
64
59
|
dependencies: { "@platforma-sdk/block-tools": "catalog:" },
|
|
65
60
|
});
|
|
66
|
-
expect(classifyOne(wp
|
|
61
|
+
expect(classifyOne(wp)).toBeUndefined();
|
|
67
62
|
});
|
|
68
63
|
|
|
69
|
-
test("
|
|
64
|
+
test("model via model dep + main", () => {
|
|
70
65
|
const wp = pkg("model", {
|
|
71
66
|
name: "x.model",
|
|
72
67
|
main: "dist/index.js",
|
|
73
68
|
dependencies: { "@platforma-sdk/model": "catalog:" },
|
|
74
69
|
});
|
|
75
|
-
expect(classifyOne(wp
|
|
70
|
+
expect(classifyOne(wp)).toBe("model");
|
|
76
71
|
});
|
|
77
72
|
|
|
78
|
-
test("
|
|
73
|
+
test("test via @platforma-sdk/test dep", () => {
|
|
79
74
|
const wp = pkg("test", {
|
|
80
75
|
name: "x.test",
|
|
81
76
|
devDependencies: { "@platforma-sdk/test": "catalog:" },
|
|
82
77
|
});
|
|
83
|
-
expect(classifyOne(wp
|
|
78
|
+
expect(classifyOne(wp)).toBe("test");
|
|
84
79
|
});
|
|
85
80
|
|
|
86
|
-
test("
|
|
81
|
+
test("root as catch-all", () => {
|
|
87
82
|
const wp = pkg("", { name: "x" });
|
|
88
|
-
expect(classifyOne(wp
|
|
83
|
+
expect(classifyOne(wp)).toBe("root");
|
|
89
84
|
});
|
|
90
85
|
|
|
91
86
|
test("override: block-scope wins over detection", () => {
|
|
@@ -94,7 +89,7 @@ describe("discovery — single-package classification", () => {
|
|
|
94
89
|
"block-scope": "software",
|
|
95
90
|
dependencies: { "@platforma-sdk/workflow-tengo": "catalog:" },
|
|
96
91
|
});
|
|
97
|
-
expect(classifyOne(wp
|
|
92
|
+
expect(classifyOne(wp)).toBe("software");
|
|
98
93
|
});
|
|
99
94
|
|
|
100
95
|
test("override: bad value throws DiscoveryError", () => {
|
|
@@ -102,7 +97,7 @@ describe("discovery — single-package classification", () => {
|
|
|
102
97
|
name: "x",
|
|
103
98
|
"block-scope": "nope",
|
|
104
99
|
});
|
|
105
|
-
expect(() => classifyOne(wp
|
|
100
|
+
expect(() => classifyOne(wp)).toThrow(DiscoveryError);
|
|
106
101
|
});
|
|
107
102
|
|
|
108
103
|
test("unclassified non-root throws DiscoveryError", () => {
|
|
@@ -134,6 +129,7 @@ describe("discovery — full workspace", () => {
|
|
|
134
129
|
}),
|
|
135
130
|
pkg("block", {
|
|
136
131
|
name: "x.block",
|
|
132
|
+
block: { components: {}, meta: {} },
|
|
137
133
|
dependencies: {
|
|
138
134
|
"@platforma-sdk/block-tools": "catalog:",
|
|
139
135
|
"x.model": "workspace:*",
|
|
@@ -25,6 +25,8 @@ export type PackageJsonLike = {
|
|
|
25
25
|
devDependencies?: Record<string, string>;
|
|
26
26
|
peerDependencies?: Record<string, string>;
|
|
27
27
|
optionalDependencies?: Record<string, string>;
|
|
28
|
+
// Top-level `block` field — the unambiguous facade marker.
|
|
29
|
+
block?: unknown;
|
|
28
30
|
// Override field — value MUST be one of the seven scopes.
|
|
29
31
|
"block-scope"?: string;
|
|
30
32
|
"block-software"?: unknown;
|
|
@@ -61,11 +63,9 @@ function isRoot(p: string): boolean {
|
|
|
61
63
|
* Classify one package.
|
|
62
64
|
*
|
|
63
65
|
* @param wp The workspace package.
|
|
64
|
-
* @param siblingNames Set of all workspace package `name`s (used by
|
|
65
|
-
* rule 4 "block": depends on ≥2 sibling .model/.ui/.workflow).
|
|
66
66
|
* @returns Scope or `undefined` if unclassified.
|
|
67
67
|
*/
|
|
68
|
-
export function classifyOne(wp: WorkspacePackage
|
|
68
|
+
export function classifyOne(wp: WorkspacePackage): Scope | undefined {
|
|
69
69
|
const override = wp.pkg["block-scope"];
|
|
70
70
|
if (typeof override === "string") {
|
|
71
71
|
if (!(ALL_SCOPES as string[]).includes(override)) {
|
|
@@ -88,6 +88,12 @@ export function classifyOne(wp: WorkspacePackage, siblingNames: Set<string>): Sc
|
|
|
88
88
|
// sub-package dirs), so this only ever fires in standalone mode.
|
|
89
89
|
if (isRoot(wp.path)) return "root";
|
|
90
90
|
|
|
91
|
+
// block (facade) — a top-level `block` field is the unambiguous facade
|
|
92
|
+
// marker; no non-facade workspace package carries one. MUST precede the model
|
|
93
|
+
// rule: a migrated facade carries `@platforma-sdk/model` as a devDep plus
|
|
94
|
+
// `main`, which would otherwise match model.
|
|
95
|
+
if (wp.pkg.block !== undefined) return "block";
|
|
96
|
+
|
|
91
97
|
const deps = depKeys(wp.pkg);
|
|
92
98
|
|
|
93
99
|
// 1. software — top-level `block-software` field OR
|
|
@@ -107,30 +113,10 @@ export function classifyOne(wp: WorkspacePackage, siblingNames: Set<string>): Sc
|
|
|
107
113
|
return "ui";
|
|
108
114
|
}
|
|
109
115
|
|
|
110
|
-
// 4.
|
|
111
|
-
if (deps.has("@platforma-sdk/block-tools")) {
|
|
112
|
-
const matches = [...deps].filter(
|
|
113
|
-
(d) =>
|
|
114
|
-
siblingNames.has(d) &&
|
|
115
|
-
(d.endsWith(".model") || d.endsWith(".ui") || d.endsWith(".workflow")),
|
|
116
|
-
);
|
|
117
|
-
if (matches.length >= 2) return "block";
|
|
118
|
-
// Fallback: `files == ["index.d.ts", "index.js"]` + block-tools.
|
|
119
|
-
const files = wp.pkg.files;
|
|
120
|
-
if (
|
|
121
|
-
Array.isArray(files) &&
|
|
122
|
-
files.length === 2 &&
|
|
123
|
-
files.includes("index.d.ts") &&
|
|
124
|
-
files.includes("index.js")
|
|
125
|
-
) {
|
|
126
|
-
return "block";
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// 5. model — @platforma-sdk/model in deps AND main is set.
|
|
116
|
+
// 4. model — @platforma-sdk/model in deps AND main is set.
|
|
131
117
|
if (deps.has("@platforma-sdk/model") && wp.pkg.main) return "model";
|
|
132
118
|
|
|
133
|
-
//
|
|
119
|
+
// 5. test — @platforma-sdk/test in deps.
|
|
134
120
|
if (deps.has("@platforma-sdk/test")) return "test";
|
|
135
121
|
|
|
136
122
|
// (root — the workspace root, path "", is classified above, before the
|
|
@@ -144,13 +130,9 @@ export function classifyOne(wp: WorkspacePackage, siblingNames: Set<string>): Sc
|
|
|
144
130
|
* package.json.
|
|
145
131
|
*/
|
|
146
132
|
export function discoverModules(packages: WorkspacePackage[]): Module[] {
|
|
147
|
-
const siblingNames = new Set<string>();
|
|
148
|
-
for (const p of packages) {
|
|
149
|
-
if (typeof p.pkg.name === "string") siblingNames.add(p.pkg.name);
|
|
150
|
-
}
|
|
151
133
|
const out: Module[] = [];
|
|
152
134
|
for (const p of packages) {
|
|
153
|
-
const scope = classifyOne(p
|
|
135
|
+
const scope = classifyOne(p);
|
|
154
136
|
if (!scope) {
|
|
155
137
|
throw new DiscoveryError(
|
|
156
138
|
`Unclassified workspace package at '${p.path}'. ` +
|
|
@@ -1,24 +1,67 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
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
|
-
|
|
12
|
+
ensureWorkspaceScopeDevDeps,
|
|
13
|
+
requireField,
|
|
14
|
+
removeScript,
|
|
11
15
|
enforceAlphabeticalOrder,
|
|
12
16
|
enforceFieldOrder,
|
|
13
17
|
type RunContext,
|
|
14
18
|
} from "../engine/api";
|
|
15
|
-
import { findModules,
|
|
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
|
-
*
|
|
21
|
-
*
|
|
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
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
const
|
|
71
|
-
...
|
|
72
|
-
...
|
|
73
|
-
...
|
|
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
|
|
76
|
-
//
|
|
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
|
-
//
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
ensureField("files", ["
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
146
|
-
//
|
|
147
|
-
//
|
|
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
|
|
2
|
-
//
|
|
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 {
|
|
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
|
-
|
|
10
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|