feat: introduced omptype validation package and migrated workspace dependencies
- Introduce `@oh-my-pi/omptype` as a new ArkType-compatible schema validation package featuring a lazy JIT runtime, JSON Schema emission, and compatibility adapters. - Replace `arktype` across workspace packages and test utilities with `@oh-my-pi/omptype`. - Add benchmark suites, tests, and documentation for the new validation engine and adapters. - Update workspace build, test runner, and release configurations to include the new package.
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
import { describe, expect, it } from "bun:test";
|
||||
import { packages, rewriteManifest } from "./ci-release-publish";
|
||||
|
||||
describe("published manifest topology", () => {
|
||||
it("repoints omptype runtime entries to dist/js with a bun source condition", async () => {
|
||||
const pkg = packages.find(entry => entry.dir === "packages/omptype");
|
||||
if (!pkg) throw new Error("omptype missing from publish set");
|
||||
expect(pkg.publishJs).toBe(true);
|
||||
|
||||
const manifest = await rewriteManifest(pkg, false);
|
||||
expect(manifest.main).toBe("./dist/js/index.js");
|
||||
expect(manifest.types).toBe("./dist/types/index.d.ts");
|
||||
expect(manifest.files).toContain("dist/js");
|
||||
expect(manifest.files).toContain("dist/types");
|
||||
// `src` must stay packed — the `bun` condition resolves into it.
|
||||
expect(manifest.files).toContain("src");
|
||||
expect(manifest.exports).toEqual({
|
||||
".": {
|
||||
types: "./dist/types/index.d.ts",
|
||||
bun: "./src/index.ts",
|
||||
default: "./dist/js/index.js",
|
||||
},
|
||||
"./*": {
|
||||
types: "./dist/types/*.d.ts",
|
||||
bun: "./src/*.ts",
|
||||
default: "./dist/js/*.js",
|
||||
},
|
||||
"./*.js": {
|
||||
types: "./dist/types/*.d.ts",
|
||||
bun: "./src/*.ts",
|
||||
default: "./dist/js/*.js",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps source-runtime packages on src with only types repointed", async () => {
|
||||
const pkg = packages.find(entry => entry.dir === "packages/utils");
|
||||
if (!pkg) throw new Error("utils missing from publish set");
|
||||
|
||||
const manifest = await rewriteManifest(pkg, false);
|
||||
expect(manifest.main).toBe("./src/index.ts");
|
||||
expect(manifest.exports).toEqual({
|
||||
".": {
|
||||
types: "./dist/types/index.d.ts",
|
||||
import: "./src/index.ts",
|
||||
},
|
||||
"./*": {
|
||||
types: "./dist/types/*.d.ts",
|
||||
import: "./src/*.ts",
|
||||
},
|
||||
"./*.js": "./src/*.ts",
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -14,9 +14,14 @@
|
||||
* that points at `./src/*.ts(x)` is repointed to `./dist/types/*.d.ts`,
|
||||
* `dist/types` (plus `dist/client` for `stats`) is added to `files`,
|
||||
* and packages with a `publishBin` override get their `bin` swapped to
|
||||
* the prepack bundle (coding-agent: `src/cli.ts` → `dist/cli.js`). The
|
||||
* on-repo manifest keeps pointing at source so local dev and source
|
||||
* installs (`bun link`, `install.sh --source`) work without a build.
|
||||
* the prepack bundle (coding-agent: `src/cli.ts` → `dist/cli.js`).
|
||||
* Packages flagged `publishJs` (omptype) additionally emit transpiled
|
||||
* per-module JS into `dist/js/` and get their runtime entries (`main`,
|
||||
* `exports[*]` import paths) repointed there, with a `bun` condition
|
||||
* keeping TS-source resolution for Bun consumers — so the published
|
||||
* package runs on plain Node. The on-repo manifest keeps pointing at
|
||||
* source so local dev and source installs (`bun link`,
|
||||
* `install.sh --source`) work without a build.
|
||||
* 3. Pack with `bun pm pack` (resolves the `catalog:`/`workspace:`
|
||||
* protocols npm cannot, and runs each package's `prepack` lifecycle),
|
||||
* then publish the resolved tarball with `npm publish` — see
|
||||
@@ -35,7 +40,7 @@ import {
|
||||
generateNpmPackages,
|
||||
LEAF_TARGETS,
|
||||
} from "../packages/natives/scripts/gen-npm-packages.ts";
|
||||
import { fixDtsExtensions } from "./fix-dts-extensions.ts";
|
||||
import { fixEmitExtensions } from "./fix-emit-extensions.ts";
|
||||
|
||||
export interface PublishPackage {
|
||||
dir: string;
|
||||
@@ -46,6 +51,12 @@ export interface PublishPackage {
|
||||
extraFiles?: readonly string[];
|
||||
/** Extra tsgo invocations beyond `tsconfig.publish.json`. */
|
||||
extraTypeConfigs?: readonly string[];
|
||||
/**
|
||||
* Also emit transpiled JS to `dist/js` (via `tsconfig.publish.js.json`)
|
||||
* and repoint the published runtime entries there so the package runs on
|
||||
* plain Node. Requires the package to be dependency-free of Bun APIs.
|
||||
*/
|
||||
publishJs?: boolean;
|
||||
/**
|
||||
* `bin` map for the published manifest. The on-repo manifest points `bin`
|
||||
* at TS source so source installs (`bun link`, `install.sh --source`) work
|
||||
@@ -87,6 +98,7 @@ const nativeLeafTag = nativeLeafTagFromArgs(process.argv.slice(2));
|
||||
export const packages: PublishPackage[] = [
|
||||
{ dir: "packages/utils", kind: "typescript" },
|
||||
{ dir: "packages/wire", kind: "typescript" },
|
||||
{ dir: "packages/omptype", kind: "typescript", publishJs: true },
|
||||
{ dir: "packages/catalog", kind: "typescript" },
|
||||
{ dir: "packages/ai", kind: "typescript" },
|
||||
{ dir: "packages/natives", kind: "native" },
|
||||
@@ -105,18 +117,30 @@ export const packages: PublishPackage[] = [
|
||||
{ dir: "packages/coding-agent", kind: "typescript", publishBin: { omp: "dist/cli.js" } },
|
||||
];
|
||||
|
||||
function rewriteSrcPath(value: string): string {
|
||||
function rewriteSrcToTypes(value: string): string {
|
||||
if (!value.startsWith("./src/")) return value;
|
||||
const rel = value.slice("./src/".length).replace(/\.tsx?$/, "");
|
||||
return `./dist/types/${rel}.d.ts`;
|
||||
}
|
||||
|
||||
function rewriteExports(exports: JsonValue): JsonValue {
|
||||
function rewriteSrcToJs(value: string): string {
|
||||
if (!value.startsWith("./src/")) return value;
|
||||
const rel = value.slice("./src/".length).replace(/\.tsx?$/, "");
|
||||
return `./dist/js/${rel}.js`;
|
||||
}
|
||||
|
||||
function rewriteExports(exports: JsonValue, publishJs: boolean): JsonValue {
|
||||
if (exports === null || typeof exports !== "object" || Array.isArray(exports)) return exports;
|
||||
const src = exports as JsonObject;
|
||||
const out: JsonObject = {};
|
||||
for (const key in src) {
|
||||
const val = src[key];
|
||||
if (publishJs && typeof val === "string" && val.startsWith("./src/")) {
|
||||
// String-form subpath (e.g. `"./*.js": "./src/*.ts"`): declarations
|
||||
// for TS, TS source for Bun, transpiled JS for everything else.
|
||||
out[key] = { types: rewriteSrcToTypes(val), bun: val, default: rewriteSrcToJs(val) };
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
val !== null &&
|
||||
typeof val === "object" &&
|
||||
@@ -124,9 +148,16 @@ function rewriteExports(exports: JsonValue): JsonValue {
|
||||
typeof (val as JsonObject).types === "string" &&
|
||||
((val as JsonObject).types as string).startsWith("./src/")
|
||||
) {
|
||||
const next: JsonObject = { ...(val as JsonObject) };
|
||||
next.types = rewriteSrcPath(next.types as string);
|
||||
out[key] = next;
|
||||
const srcTypes = (val as JsonObject).types as string;
|
||||
if (publishJs) {
|
||||
// Condition order matters: `types` is TS-only, `bun` must win
|
||||
// over `default` for Bun consumers.
|
||||
out[key] = { types: rewriteSrcToTypes(srcTypes), bun: srcTypes, default: rewriteSrcToJs(srcTypes) };
|
||||
} else {
|
||||
const next: JsonObject = { ...(val as JsonObject) };
|
||||
next.types = rewriteSrcToTypes(srcTypes);
|
||||
out[key] = next;
|
||||
}
|
||||
} else {
|
||||
out[key] = val;
|
||||
}
|
||||
@@ -134,17 +165,22 @@ function rewriteExports(exports: JsonValue): JsonValue {
|
||||
return out;
|
||||
}
|
||||
|
||||
async function rewriteManifest(pkg: PublishPackage, write: boolean): Promise<PackageManifest> {
|
||||
/** Compute (and optionally write) the published manifest for a package. */
|
||||
export async function rewriteManifest(pkg: PublishPackage, write: boolean): Promise<PackageManifest> {
|
||||
const manifestPath = path.join(repoRoot, pkg.dir, "package.json");
|
||||
const manifest = (await Bun.file(manifestPath).json()) as PackageManifest;
|
||||
if (pkg.publishBin) manifest.bin = { ...pkg.publishBin };
|
||||
if (typeof manifest.types === "string" && manifest.types.startsWith("./src/")) {
|
||||
manifest.types = rewriteSrcPath(manifest.types);
|
||||
manifest.types = rewriteSrcToTypes(manifest.types);
|
||||
}
|
||||
if (manifest.exports !== undefined) manifest.exports = rewriteExports(manifest.exports);
|
||||
if (pkg.publishJs && typeof manifest.main === "string") {
|
||||
manifest.main = rewriteSrcToJs(manifest.main);
|
||||
}
|
||||
if (manifest.exports !== undefined) manifest.exports = rewriteExports(manifest.exports, pkg.publishJs === true);
|
||||
const files = Array.isArray(manifest.files) ? [...manifest.files] : [];
|
||||
const hasDist = files.includes("dist");
|
||||
if (!hasDist && !files.includes("dist/types")) files.push("dist/types");
|
||||
if (pkg.publishJs && !hasDist && !files.includes("dist/js")) files.push("dist/js");
|
||||
for (const extra of pkg.extraFiles ?? []) {
|
||||
if (!hasDist && !files.includes(extra)) files.push(extra);
|
||||
}
|
||||
@@ -162,10 +198,16 @@ async function preparePackage(pkg: PublishPackage): Promise<PackageManifest> {
|
||||
for (const cfg of pkg.extraTypeConfigs ?? []) {
|
||||
await $`bun x tsgo -p ${cfg}`.cwd(pkgDir);
|
||||
}
|
||||
// The declaration emit runs under `moduleResolution: "Bundler"`, so relative
|
||||
// specifiers land extensionless — unresolvable for a `nodenext` consumer.
|
||||
// Rewrite them to explicit `.js` so the published types resolve everywhere.
|
||||
await fixDtsExtensions(path.join(pkgDir, "dist/types"));
|
||||
if (pkg.publishJs) {
|
||||
await $`bun x tsgo -p tsconfig.publish.js.json`.cwd(pkgDir);
|
||||
}
|
||||
// Both emits run under `moduleResolution: "Bundler"`, so relative
|
||||
// specifiers land extensionless — unresolvable for a `nodenext` consumer
|
||||
// (types) and for Node ESM at runtime (js). Rewrite them to explicit `.js`.
|
||||
await fixEmitExtensions(path.join(pkgDir, "dist/types"), ".d.ts");
|
||||
if (pkg.publishJs) {
|
||||
await fixEmitExtensions(path.join(pkgDir, "dist/js"), ".js");
|
||||
}
|
||||
return rewriteManifest(pkg, !isDryRun);
|
||||
}
|
||||
|
||||
|
||||
@@ -88,6 +88,7 @@ const codingAgentBucketPlans: Record<CodingAgentBucket, { label: string; paralle
|
||||
const fastWorkspacePackages = [
|
||||
"packages/hashline",
|
||||
"packages/wire",
|
||||
"packages/omptype",
|
||||
"packages/utils",
|
||||
"packages/catalog",
|
||||
"packages/ai",
|
||||
|
||||
Executable → Regular
+32
-24
@@ -1,24 +1,26 @@
|
||||
#!/usr/bin/env bun
|
||||
/**
|
||||
* Rewrite extensionless relative specifiers in emitted `.d.ts` to explicit
|
||||
* `.js` extensions so the published declarations resolve under
|
||||
* Rewrite extensionless relative specifiers in emitted `.d.ts`/`.js` files to
|
||||
* explicit `.js` extensions so published output resolves under Node ESM and
|
||||
* `moduleResolution: "node16" | "nodenext"`.
|
||||
*
|
||||
* The workspace type-checks under `moduleResolution: "Bundler"`
|
||||
* (`tsconfig.base.json`), which permits extensionless relative imports. `tsgo`
|
||||
* therefore emits `export * from "./sdk"` / `import … from "./modes/components"`
|
||||
* into `dist/types`. A downstream consumer on `nodenext` (the modern default)
|
||||
* then can't resolve the barrel — every relative re-export is a `TS2834`, which
|
||||
* cascades into `TS2305` "no exported member" on the whole package root.
|
||||
* into `dist/types` (declarations) and `dist/js` (transpiled runtime). A
|
||||
* downstream consumer on `nodenext` then can't resolve the barrel — every
|
||||
* relative re-export is a `TS2834`, which cascades into `TS2305` "no exported
|
||||
* member" on the whole package root — and Node refuses the extensionless
|
||||
* import outright at runtime.
|
||||
*
|
||||
* This post-emit pass appends the correct extension to each **relative**
|
||||
* specifier, resolving it against the emitted tree:
|
||||
* `./sdk` → `./sdk.js` (sibling `sdk.d.ts`)
|
||||
* `./sdk` → `./sdk.js` (sibling `sdk.d.ts` / `sdk.js`)
|
||||
* `./modes/components` → `./modes/components/index.js` (directory barrel)
|
||||
* Bare (`@scope/pkg`, `zod/v4`) and already-suffixed (`.js`, `.json`)
|
||||
* specifiers are left untouched.
|
||||
*
|
||||
* Applied by `ci-release-publish.ts` after `tsgo -p tsconfig.publish.json`.
|
||||
* Applied by `ci-release-publish.ts` after each `tsgo -p tsconfig.publish*.json`.
|
||||
*/
|
||||
import * as fs from "node:fs/promises";
|
||||
import * as path from "node:path";
|
||||
@@ -28,19 +30,24 @@ import * as path from "node:path";
|
||||
// Captures the quote-enclosed module string; we filter to relative ones below.
|
||||
const SPECIFIER_RE = /(\b(?:from|import|module)\b\s*(?:\(\s*)?)("|')(\.[^"']*)(\2)/g;
|
||||
|
||||
/** Emitted-file kind: declaration or transpiled-runtime output. */
|
||||
export type EmitExt = ".d.ts" | ".js";
|
||||
|
||||
/** Resolve an extensionless relative specifier to its `.js` runtime form,
|
||||
* given the directory of the importing `.d.ts`. Returns null to leave as-is. */
|
||||
export async function resolveDtsSpecifier(fromDir: string, spec: string): Promise<string | null> {
|
||||
* given the directory of the importing emitted file and the extension the
|
||||
* emitted tree uses (`.d.ts` for declarations, `.js` for runtime output).
|
||||
* Returns null to leave as-is. */
|
||||
export async function resolveEmitSpecifier(fromDir: string, spec: string, ext: EmitExt): Promise<string | null> {
|
||||
// Already has a JS/JSON extension, or a declaration extension we map to .js.
|
||||
if (/\.(js|json|mjs|cjs)$/.test(spec)) return null;
|
||||
if (/\.d\.ts$/.test(spec)) return `${spec.slice(0, -".d.ts".length)}.js`;
|
||||
|
||||
const abs = path.join(fromDir, spec);
|
||||
// Sibling declaration file: `./sdk` → `./sdk.js`.
|
||||
if (await exists(`${abs}.d.ts`)) return `${spec}.js`;
|
||||
// Sibling emitted file: `./sdk` → `./sdk.js`.
|
||||
if (await exists(`${abs}${ext}`)) return `${spec}.js`;
|
||||
// Directory barrel: `./modes/components` → `./modes/components/index.js`.
|
||||
if (await exists(path.join(abs, "index.d.ts"))) return `${spec.replace(/\/$/, "")}/index.js`;
|
||||
// Unresolved (e.g. an asset or a specifier with no emitted `.d.ts`): leave it.
|
||||
if (await exists(path.join(abs, `index${ext}`))) return `${spec.replace(/\/$/, "")}/index.js`;
|
||||
// Unresolved (e.g. an asset or a specifier with no emitted file): leave it.
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -53,8 +60,8 @@ async function exists(p: string): Promise<boolean> {
|
||||
}
|
||||
}
|
||||
|
||||
/** Rewrite one `.d.ts` file in place. Returns the number of specifiers changed. */
|
||||
export async function fixDtsFile(filePath: string): Promise<number> {
|
||||
/** Rewrite one emitted file in place. Returns the number of specifiers changed. */
|
||||
export async function fixEmitFile(filePath: string, ext: EmitExt): Promise<number> {
|
||||
const source = await Bun.file(filePath).text();
|
||||
const fromDir = path.dirname(filePath);
|
||||
let changed = 0;
|
||||
@@ -63,7 +70,7 @@ export async function fixDtsFile(filePath: string): Promise<number> {
|
||||
const edits: Array<{ match: string; replacement: string }> = [];
|
||||
for (const m of source.matchAll(SPECIFIER_RE)) {
|
||||
const [full, prefix, quote, spec] = m;
|
||||
const resolved = await resolveDtsSpecifier(fromDir, spec);
|
||||
const resolved = await resolveEmitSpecifier(fromDir, spec, ext);
|
||||
if (resolved && resolved !== spec) {
|
||||
edits.push({ match: full, replacement: `${prefix}${quote}${resolved}${quote}` });
|
||||
}
|
||||
@@ -79,15 +86,15 @@ export async function fixDtsFile(filePath: string): Promise<number> {
|
||||
return changed;
|
||||
}
|
||||
|
||||
/** Walk `dir` recursively and fix every `.d.ts`. Returns totals. */
|
||||
export async function fixDtsExtensions(dir: string): Promise<{ files: number; specifiers: number }> {
|
||||
/** Walk `dir` recursively and fix every emitted file with extension `ext`. Returns totals. */
|
||||
export async function fixEmitExtensions(dir: string, ext: EmitExt): Promise<{ files: number; specifiers: number }> {
|
||||
let files = 0;
|
||||
let specifiers = 0;
|
||||
const entries = await fs.readdir(dir, { withFileTypes: true, recursive: true });
|
||||
for (const entry of entries) {
|
||||
if (!entry.isFile() || !entry.name.endsWith(".d.ts")) continue;
|
||||
if (!entry.isFile() || !entry.name.endsWith(ext)) continue;
|
||||
const filePath = path.join(entry.parentPath, entry.name);
|
||||
const n = await fixDtsFile(filePath);
|
||||
const n = await fixEmitFile(filePath, ext);
|
||||
if (n > 0) {
|
||||
files++;
|
||||
specifiers += n;
|
||||
@@ -98,10 +105,11 @@ export async function fixDtsExtensions(dir: string): Promise<{ files: number; sp
|
||||
|
||||
if (import.meta.main) {
|
||||
const target = process.argv[2];
|
||||
if (!target) {
|
||||
console.error("usage: fix-dts-extensions.ts <dist/types dir>");
|
||||
const ext = (process.argv[3] ?? ".d.ts") as EmitExt;
|
||||
if (!target || (ext !== ".d.ts" && ext !== ".js")) {
|
||||
console.error("usage: fix-emit-extensions.ts <emitted dir> [.d.ts|.js]");
|
||||
process.exit(1);
|
||||
}
|
||||
const { files, specifiers } = await fixDtsExtensions(target);
|
||||
console.log(`fix-dts-extensions: rewrote ${specifiers} specifiers across ${files} files in ${target}`);
|
||||
const { files, specifiers } = await fixEmitExtensions(target, ext);
|
||||
console.log(`fix-emit-extensions: rewrote ${specifiers} specifiers across ${files} files in ${target}`);
|
||||
}
|
||||
@@ -95,7 +95,7 @@ cp "$natives_pkg_backup" "$ROOT_DIR/packages/natives/package.json"
|
||||
# 3. Pack the remaining workspace packages (natives core and coding-agent
|
||||
# handled separately). `collab-web` is private but still packed here so its
|
||||
# prepack build and tarball file list stay release-safe.
|
||||
for pkg in utils wire hashline catalog ai mnemopi snapcompact agent tui stats collab-web; do
|
||||
for pkg in utils wire omptype hashline catalog ai mnemopi snapcompact agent tui stats collab-web; do
|
||||
(
|
||||
cd "$ROOT_DIR/packages/$pkg"
|
||||
bun pm pack --destination "$TARBALL_DIR" --quiet >/dev/null
|
||||
@@ -120,6 +120,7 @@ cp "$agent_pkg_backup" "$ROOT_DIR/packages/coding-agent/package.json"
|
||||
|
||||
utils_tgz="$(find_tarball "$TARBALL_DIR"/oh-my-pi-pi-utils-*.tgz)"
|
||||
wire_tgz="$(find_tarball "$TARBALL_DIR"/oh-my-pi-pi-wire-*.tgz)"
|
||||
omptype_tgz="$(find_tarball "$TARBALL_DIR"/oh-my-pi-omptype-*.tgz)"
|
||||
natives_tgz="$(find_tarball "$TARBALL_DIR"/oh-my-pi-pi-natives-[0-9]*.tgz)"
|
||||
natives_leaf_tgz="$(find_tarball "$TARBALL_DIR"/oh-my-pi-pi-natives-"$host_tag"-*.tgz)"
|
||||
hashline_tgz="$(find_tarball "$TARBALL_DIR"/oh-my-pi-hashline-*.tgz)"
|
||||
@@ -146,6 +147,7 @@ mkdir -p "$TARBALL_APP_DIR"
|
||||
pkg.overrides = {
|
||||
'@oh-my-pi/pi-utils': '$utils_tgz',
|
||||
'@oh-my-pi/pi-wire': '$wire_tgz',
|
||||
'@oh-my-pi/omptype': '$omptype_tgz',
|
||||
'@oh-my-pi/pi-natives': '$natives_tgz',
|
||||
'@oh-my-pi/pi-natives-$host_tag': '$natives_leaf_tgz',
|
||||
'@oh-my-pi/hashline': '$hashline_tgz',
|
||||
@@ -162,7 +164,7 @@ mkdir -p "$TARBALL_APP_DIR"
|
||||
require('fs').writeFileSync('package.json', JSON.stringify(pkg, null, 2));
|
||||
"
|
||||
|
||||
bun add "$utils_tgz" "$wire_tgz" "$natives_tgz" "$hashline_tgz" "$catalog_tgz" "$ai_tgz" "$mnemopi_tgz" "$snapcompact_tgz" "$agent_tgz" "$tui_tgz" "$stats_tgz" "$coding_agent_tgz" "$collab_web_tgz"
|
||||
bun add "$utils_tgz" "$wire_tgz" "$omptype_tgz" "$natives_tgz" "$hashline_tgz" "$catalog_tgz" "$ai_tgz" "$mnemopi_tgz" "$snapcompact_tgz" "$agent_tgz" "$tui_tgz" "$stats_tgz" "$coding_agent_tgz" "$collab_web_tgz"
|
||||
# The platform leaf must arrive through the core's optionalDependencies +
|
||||
# override, not as a direct dependency — assert it landed before smoking so a
|
||||
# resolution regression is distinguishable from a runtime loader bug.
|
||||
@@ -176,6 +178,19 @@ mkdir -p "$TARBALL_APP_DIR"
|
||||
echo "Unexpected @oh-my-pi/pi-wire COLLAB_PROTO: $wire_proto"
|
||||
exit 1
|
||||
}
|
||||
omptype_probe="$(bun -e '
|
||||
import { type } from "@oh-my-pi/omptype";
|
||||
import { Type } from "@oh-my-pi/omptype/typebox";
|
||||
import { z } from "@oh-my-pi/omptype/zod";
|
||||
const root = type({ name: "string", enabled: "boolean = false" }).assert({ name: "omp" });
|
||||
const typebox = Type.Object({ name: Type.String() }).assert({ name: "tb" });
|
||||
const zod = z.object({ name: z.string() }).parse({ name: "z" });
|
||||
process.stdout.write(`${root.name}:${root.enabled}:${typebox.name}:${zod.name}`);
|
||||
')"
|
||||
[ "$omptype_probe" = "omp:false:tb:z" ] || {
|
||||
echo "Unexpected @oh-my-pi/omptype probe result: $omptype_probe"
|
||||
exit 1
|
||||
}
|
||||
[ -f "node_modules/@oh-my-pi/collab-web/dist/index.html" ] || {
|
||||
echo "Collab web tarball did not install built dist/index.html"
|
||||
exit 1
|
||||
|
||||
Reference in New Issue
Block a user