Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions etc/blocks/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
block-pack/
dist-dbg/
dist-rdp/
84 changes: 84 additions & 0 deletions etc/blocks/enter-numbers-v3/block/build-facade.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Scratch facade-bundle build for the slim-facade spike.
// Runs both bundlers side-by-side. Output is for inspection only.
//
// pnpm build:facade
//
// Produces:
// dist-dbg/index.d.ts — dts-bundle-generator output
// dist-rdp/index.d.ts — rolldown-plugin-dts output
//
// Both bundlers configured to fully inline @platforma-sdk/model and the
// sibling model package so the facade is self-contained.

import { readFileSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { generateDtsBundle } from "dts-bundle-generator";
import { rolldown } from "rolldown";
import { dts } from "rolldown-plugin-dts";

const __dirname = dirname(fileURLToPath(import.meta.url));
const pkg = JSON.parse(readFileSync(resolve(__dirname, "package.json"), "utf-8"));

const entryTs = resolve(__dirname, "src/index.ts");
const tsconfigPath = resolve(__dirname, "tsconfig.json");

const modelPkgName = Object.keys(pkg.dependencies ?? {}).find((n) => n.endsWith(".model"));
if (!modelPkgName) throw new Error("Could not find sibling .model package in dependencies.");

const inlineList = ["@platforma-sdk/model", modelPkgName];

console.log(`Facade source: ${entryTs}`);
console.log(`Inlining: ${inlineList.join(", ")}`);

// ---------------------------------------------------------------------------
// dts-bundle-generator
// ---------------------------------------------------------------------------
{
const outDir = resolve(__dirname, "dist-dbg");
rmSync(outDir, { recursive: true, force: true });
mkdirSync(outDir, { recursive: true });

const t0 = Date.now();
const [content] = generateDtsBundle(
[
{
filePath: entryTs,
libraries: { inlinedLibraries: inlineList },
output: { sortNodes: false, noBanner: false },
},
],
{ preferredConfigPath: tsconfigPath, followSymlinks: true },
);
writeFileSync(resolve(outDir, "index.d.ts"), content);
console.log(`dts-bundle-generator → dist-dbg/index.d.ts (${Date.now() - t0} ms, ${content.length} bytes)`);
}

// ---------------------------------------------------------------------------
// rolldown-plugin-dts
// ---------------------------------------------------------------------------
{
const outDir = resolve(__dirname, "dist-rdp");
rmSync(outDir, { recursive: true, force: true });
mkdirSync(outDir, { recursive: true });

const t0 = Date.now();
const bundle = await rolldown({
input: entryTs,
// Force every package to be resolved and pulled into the bundle so the
// facade is self-contained. Default Rolldown behavior would keep
// non-relative imports external.
external: () => false,
plugins: [
dts({
tsconfig: tsconfigPath,
emitDtsOnly: true,
sourcemap: false,
}),
],
});
await bundle.write({ dir: outDir, format: "es" });
await bundle.close();
const out = readFileSync(resolve(outDir, "index.d.ts"), "utf-8");
console.log(`rolldown-plugin-dts → dist-rdp/index.d.ts (${Date.now() - t0} ms, ${out.length} bytes)`);
}
10 changes: 8 additions & 2 deletions etc/blocks/enter-numbers-v3/block/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"version": "1.0.0",
"private": true,
"scripts": {
"build": "rm -rf ./block-pack && block-tools pack"
"build": "rm -rf ./block-pack && block-tools pack",
"build:facade": "node ./build-facade.mjs"
},
"files": [
"index.d.ts",
Expand All @@ -16,7 +17,12 @@
"@milaboratories/milaboratories.test-enter-numbers-v3.ui": "workspace:*"
},
"devDependencies": {
"@platforma-sdk/block-tools": "workspace:*"
"@milaboratories/ts-configs": "workspace:*",
"@platforma-sdk/block-tools": "workspace:*",
"dts-bundle-generator": "^9.5.1",
"rolldown": "^1.0.1",
"rolldown-plugin-dts": "^0.25.1",
"typescript": "catalog:"
},
"block": {
"components": {
Expand Down
59 changes: 59 additions & 0 deletions etc/blocks/enter-numbers-v3/block/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import type { InferDataType, InferHrefType, InferOutputsType } from "@platforma-sdk/model";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To ensure cross-platform compatibility, especially on Windows, it is recommended to use fileURLToPath from the node:url module when deriving file system paths from import.meta.url. This avoids issues with URI-encoded characters and the leading slash present in .pathname on Windows.

import { fileURLToPath } from "node:url";
import type { InferDataType, InferHrefType, InferOutputsType } from "@platforma-sdk/model";

import type {
BlockData,
EnterNumbersRef,
} from "@milaboratories/milaboratories.test-enter-numbers-v3.model";
import { platforma } from "@milaboratories/milaboratories.test-enter-numbers-v3.model";

/**
* Spec marker for the Enter Numbers v3 block. Phantom type — encodes the
* outputs, data, and href shapes so consumers (tests, MCP, other blocks)
* can reach them via the `OutputsOf` / `DataOf` / `HrefOf` generators.
*
* Spike probe — top-level alias JSDoc with rich content.
*/
export interface EnterNumbersV3Spec {
/** Nominal tag pinning this Spec to the enter-numbers-v3 block. */
readonly __block: "enter-numbers-v3";
/** Outputs reachable through the block's workflow run. */
outputs: InferOutputsType<typeof platforma>;
/** Persistent block state (data) — see {@link BlockData}. */
data: InferDataType<typeof platforma>;
/** Hrefs the block UI advertises. */
href: InferHrefType<typeof platforma>;
}

/** Universal Spec twin — every facade exports `BlockSpec` as an alias of its block-named Spec. */
export type BlockSpec = EnterNumbersV3Spec;

/** Extract the outputs shape from a block Spec. */
export type OutputsOf<S> = S extends { outputs: infer O } ? O : never;

/** Extract the data (block state) shape from a block Spec. */
export type DataOf<S> = S extends { data: infer D } ? D : never;

/** Extract the href shape from a block Spec. */
export type HrefOf<S> = S extends { href: infer H } ? H : never;

/**
* Outputs of this block. Convenience alias equal to `OutputsOf<BlockSpec>`.
* The shape is derived via `InferOutputsType<typeof platforma>` — JSDoc
* placed on the individual `.output(...)` builders in the model package must
* survive into the bundled facade `.d.ts`.
*/
export type BlockOutputs = InferOutputsType<typeof platforma>;

export type { BlockData, EnterNumbersRef };

const __facadeDir__ = new URL(".", import.meta.url).pathname;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using .pathname on a URL object returns a URI-style path (e.g., /C:/path on Windows), which is not a valid native path for Node.js file system APIs. Use fileURLToPath to obtain a platform-native path. Additionally, rename the constant to FACADE_DIR to adhere to the repository's naming convention (SCREAM_CASE or UpperCamelCase).

Suggested change
const __facadeDir__ = new URL(".", import.meta.url).pathname;
const FACADE_DIR = fileURLToPath(new URL(".", import.meta.url));
References
  1. Use SCREAM_CASE or UpperCamelCase for constants.


/**
* Dev-v2 block spec — folder-based, resolved relative to the facade directory.
*
* Phase C will replace the runtime side with a registry-aware dispatcher;
* Phase B keeps the existing dev-v2 shape unchanged.
*/
export const blockSpec = {
type: "dev-v2" as const,
folder: __facadeDir__,
};
12 changes: 12 additions & 0 deletions etc/blocks/enter-numbers-v3/block/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"extends": "@milaboratories/ts-configs/node",
"compilerOptions": {
"noEmit": false,
"declaration": true,
"emitDeclarationOnly": true,
"outDir": "dist-types",
"customConditions": []
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "dist-types", "dist-dbg", "dist-rdp", "**/*.test.ts"]
}
3 changes: 1 addition & 2 deletions etc/blocks/enter-numbers-v3/model/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@
"fmt": "ts-builder format"
},
"dependencies": {
"@platforma-sdk/model": "workspace:*",
"zod": "catalog:"
"@platforma-sdk/model": "workspace:*"
},
"devDependencies": {
"@milaboratories/build-configs": "workspace:*",
Expand Down
37 changes: 28 additions & 9 deletions etc/blocks/enter-numbers-v3/model/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { InferHrefType, InferOutputsType } from "@platforma-sdk/model";
import { BlockModelV3, DataModelBuilder } from "@platforma-sdk/model";
import { z } from "zod";

// Data version 1: just numbers
type BlockDataV1 = {
Expand All @@ -13,14 +12,20 @@ type BlockDataV2 = {
labels: string[];
};

// Data version 3 (current): added description
export const $BlockData = z.object({
numbers: z.array(z.coerce.number()),
labels: z.array(z.string()),
description: z.string(),
});

export type BlockData = z.infer<typeof $BlockData>;
/**
* Persistent block state for enter-numbers-v3. Version 3 of the schema.
*
* Spike probe — JSDoc on the alias and on each property must reach the
* bundled facade `.d.ts` through `InferDataType<typeof platforma>`.
*/
export type BlockData = {
/** Raw numbers entered by the user, in input order. */
numbers: number[];
/** Optional labels paired with each number. Empty until user provides one. */
labels: string[];
/** Human-friendly description of the dataset. */
description: string;
};

// Define data model with migrations from v1 to current
const dataModel = new DataModelBuilder()
Expand Down Expand Up @@ -91,3 +96,17 @@ export const platforma = BlockModelV3.create(dataModel)

export type BlockOutputs = InferOutputsType<typeof platforma>;
export type Href = InferHrefType<typeof platforma>;

/**
* Reference shape exposed by this block — nominal record pinning the entered
* numbers to the block identity.
*
* Spike probe — JSDoc on a hand-written helper type re-exported through the
* facade. Both alias-level JSDoc and per-property JSDoc must survive.
*/
export type EnterNumbersRef = {
/** Nominal tag pinning this ref to the enter-numbers-v3 block. */
readonly kind: "enter-numbers-v3";
/** Sorted numbers from the most recent run. */
numbers: readonly number[];
};
84 changes: 84 additions & 0 deletions etc/blocks/sum-numbers-v3/block/build-facade.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Scratch facade-bundle build for the slim-facade spike.
// Runs both bundlers side-by-side. Output is for inspection only.
//
// pnpm build:facade
//
// Produces:
// dist-dbg/index.d.ts — dts-bundle-generator output
// dist-rdp/index.d.ts — rolldown-plugin-dts output
//
// Both bundlers configured to fully inline @platforma-sdk/model and the
// sibling model package so the facade is self-contained.

import { readFileSync, mkdirSync, writeFileSync, rmSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { generateDtsBundle } from "dts-bundle-generator";
import { rolldown } from "rolldown";
import { dts } from "rolldown-plugin-dts";

const __dirname = dirname(fileURLToPath(import.meta.url));
const pkg = JSON.parse(readFileSync(resolve(__dirname, "package.json"), "utf-8"));

const entryTs = resolve(__dirname, "src/index.ts");
const tsconfigPath = resolve(__dirname, "tsconfig.json");

const modelPkgName = Object.keys(pkg.dependencies ?? {}).find((n) => n.endsWith(".model"));
if (!modelPkgName) throw new Error("Could not find sibling .model package in dependencies.");

const inlineList = ["@platforma-sdk/model", modelPkgName];

console.log(`Facade source: ${entryTs}`);
console.log(`Inlining: ${inlineList.join(", ")}`);

// ---------------------------------------------------------------------------
// dts-bundle-generator
// ---------------------------------------------------------------------------
{
const outDir = resolve(__dirname, "dist-dbg");
rmSync(outDir, { recursive: true, force: true });
mkdirSync(outDir, { recursive: true });

const t0 = Date.now();
const [content] = generateDtsBundle(
[
{
filePath: entryTs,
libraries: { inlinedLibraries: inlineList },
output: { sortNodes: false, noBanner: false },
},
],
{ preferredConfigPath: tsconfigPath, followSymlinks: true },
);
writeFileSync(resolve(outDir, "index.d.ts"), content);
console.log(`dts-bundle-generator → dist-dbg/index.d.ts (${Date.now() - t0} ms, ${content.length} bytes)`);
}

// ---------------------------------------------------------------------------
// rolldown-plugin-dts
// ---------------------------------------------------------------------------
{
const outDir = resolve(__dirname, "dist-rdp");
rmSync(outDir, { recursive: true, force: true });
mkdirSync(outDir, { recursive: true });

const t0 = Date.now();
const bundle = await rolldown({
input: entryTs,
// Force every package to be resolved and pulled into the bundle so the
// facade is self-contained. Default Rolldown behavior would keep
// non-relative imports external.
external: () => false,
plugins: [
dts({
tsconfig: tsconfigPath,
emitDtsOnly: true,
sourcemap: false,
}),
],
});
await bundle.write({ dir: outDir, format: "es" });
await bundle.close();
const out = readFileSync(resolve(outDir, "index.d.ts"), "utf-8");
console.log(`rolldown-plugin-dts → dist-rdp/index.d.ts (${Date.now() - t0} ms, ${out.length} bytes)`);
}
10 changes: 8 additions & 2 deletions etc/blocks/sum-numbers-v3/block/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"version": "1.0.0",
"private": true,
"scripts": {
"build": "rm -rf ./block-pack && block-tools pack"
"build": "rm -rf ./block-pack && block-tools pack",
"build:facade": "node ./build-facade.mjs"
},
"files": [
"index.d.ts",
Expand All @@ -16,7 +17,12 @@
"@milaboratories/milaboratories.test-sum-numbers-v3.ui": "workspace:*"
},
"devDependencies": {
"@platforma-sdk/block-tools": "workspace:*"
"@milaboratories/ts-configs": "workspace:*",
"@platforma-sdk/block-tools": "workspace:*",
"dts-bundle-generator": "^9.5.1",
"rolldown": "^1.0.1",
"rolldown-plugin-dts": "^0.25.1",
"typescript": "catalog:"
},
"block": {
"components": {
Expand Down
Loading
Loading