@decohere/forge 0.0.1-dev.97 → 0.0.1-dev.99

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/README.md ADDED
@@ -0,0 +1,73 @@
1
+ # @decohere/forge
2
+
3
+ Build system (SWC). `forge build --configuration debug` rewrites workspace imports to **source**. `forge build --configuration release` leaves bare specifiers for Node and real `node_modules`.
4
+
5
+ [Flux](../flux/) (`flux run` / `flux serve`) uses this same compiler in-process — it does not have a second TypeScript pipeline.
6
+
7
+ `forge build` transpiles without waiting for a whole-workspace typecheck. Run `forge typecheck` independently (including in the background) and require it before shipping. Why executable tests come before static contracts: [testing principles](../../docs/testing/#executable-behavior-before-type-contracts).
8
+
9
+ ## Debug path aliases (not `node_modules` links)
10
+
11
+ Forge debug (`aliasPaths: true`) asks Mesh for a map of `package.json` `name` → source `main` (`Workspace.getPackageAliases`) and feeds it to SWC as `jsc.paths`. Flux’s resolve hook uses that same map so dynamic `import("@scope/pkg")` hits source too.
12
+
13
+ That is a **debug** rule. Release does not rewrite aliases: production must resolve like any other Node app (installed packages, `exports` / `main` pointing at emitted JS).
14
+
15
+ ### Why aliases instead of workspace symlinks
16
+
17
+ npm/pnpm/yarn already put workspace packages in `node_modules` as symlinks. Node then follows `exports` / `main` — the **published** shape. That is correct after `forge` release. It is the wrong graph while you are editing `.ts`:
18
+
19
+ - Specifiers land on built JS (often missing or stale) instead of the file you saved.
20
+ - Flux’s HMR map is keyed by Mesh project paths (`projects/…/src/…`). A `node_modules/@scope/pkg/…` URL is a different path, so the load hook and the watch graph miss it.
21
+ - The installer layout (hoist vs isolated, which packages got linked) becomes a second workspace next to Mesh. Those drift.
22
+
23
+ Aliases always point at source `main`. Adding a package is discovering its `package.json`; debug does not wait on a reinstall. `forge link` is a different job: symlink **emit** into a consumer’s `node_modules`. Do not use that for Flux/debug source.
24
+
25
+ ### Where a symlink fails
26
+
27
+ You edit a library and the API imports it by package name:
28
+
29
+ ```
30
+ package.json
31
+ node_modules/
32
+ @app/
33
+ shop.domain -> ../../projects/shop.domain # installer symlink
34
+ projects/
35
+ shop.domain/
36
+ package.json # name: @app/shop.domain
37
+ # exports.import: ./es6/src/index.js (published shape)
38
+ src/index.ts # you save here
39
+ shop.api/
40
+ package.json # main: main.ts
41
+ main.ts # import { x } from "@app/shop.domain"
42
+ ```
43
+
44
+ ```
45
+ shop.api/main.ts
46
+ import "@app/shop.domain"
47
+ │
48
+ ▼
49
+ node_modules/@app/shop.domain (symlink)
50
+ │
51
+ ▼
52
+ projects/shop.domain/package.json
53
+ exports → ./es6/src/index.js missing, or last release — not src/index.ts
54
+ ```
55
+
56
+ Node never opens `projects/shop.domain/src/index.ts`. Flux is watching that file; the running API does not load it.
57
+
58
+ ### Where the alias succeeds
59
+
60
+ Same tree, debug compiler (Forge SWC / Flux):
61
+
62
+ ```
63
+ shop.api/main.ts
64
+ import "@app/shop.domain"
65
+ │
66
+ ▼
67
+ Mesh map: "@app/shop.domain" → projects/shop.domain/src/index.ts
68
+ │
69
+ ▼
70
+ SWC transpile + Flux HMR graph (same path Mesh watches)
71
+ ```
72
+
73
+ Save `src/index.ts` → Flux reboots `shop.api`. No pre-build of `shop.domain`, no `node_modules` link required for that import.
@@ -82,11 +82,11 @@ class Docker {
82
82
  ...extraTags
83
83
  ];
84
84
  const buildArgs = this.interpolateBuildArgs({
85
- ...dockerConfig.buildArgs ?? {},
85
+ ...dockerConfig.buildArgs,
86
86
  ...envBuildArgs
87
87
  }, version);
88
88
  const secrets = {
89
- ...dockerConfig.secrets ?? {},
89
+ ...dockerConfig.secrets,
90
90
  ...envSecrets
91
91
  };
92
92
  this.#logger.info(`${options.dryRun ? "[dry-run] " : ""}Building ${project.name}`);
@@ -83,9 +83,7 @@ class Version {
83
83
  }
84
84
  await workspace.persistPackageJson((await workspace.getPackageJson()).withVersion(nextVersion.toString()));
85
85
  const responses = await Promise.all([
86
- ...[
87
- this.updatePackageJson(workspace.name, workspace, nextVersion)
88
- ],
86
+ this.updatePackageJson(workspace.name, workspace, nextVersion),
89
87
  ...projects.map((project)=>this.updatePackageJson(project.name, project, nextVersion))
90
88
  ]);
91
89
  const failed = responses.filter((r)=>!r.succeeded);
@@ -81,8 +81,11 @@ const DefaultConfigurations = [
81
81
  ];
82
82
  const WorkspaceDefaults = {
83
83
  ..._mesh.WorkspaceDefaults,
84
+ // Root-only files npm ships with the package. Globs that match nothing are fine.
84
85
  copyFiles: [
85
86
  "package.json",
87
+ "README.md",
88
+ "LICENSE*",
86
89
  "**/*.mjs"
87
90
  ],
88
91
  configurations: DefaultConfigurations,
@@ -13,13 +13,11 @@ function _ts_param(paramIndex, decorator) {
13
13
  };
14
14
  }
15
15
  import { inject, injectable } from "@decohere/crucible/decorators";
16
- // biome-ignore lint/style/useImportType: <DI>
17
16
  import { Workspace } from "@decohere/mesh";
18
17
  import { ActionResponse } from "@decohere/nexus/cli";
19
18
  import { Trace } from "@decohere/nexus/diagnostics";
20
19
  import { scribe } from "@decohere/scribe";
21
20
  import { ForgeError } from "../errors.js";
22
- // biome-ignore lint/style/useImportType: <DI>
23
21
  import { FileWriterService } from "../services/file-writer.service.js";
24
22
  import { TransformerKey } from "../types/transformer.type.js";
25
23
  export class Build {
@@ -8,7 +8,6 @@ function _ts_metadata(k, v) {
8
8
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
9
  }
10
10
  import { injectable } from "@decohere/crucible/decorators";
11
- // biome-ignore lint/style/useImportType: <DI>
12
11
  import { Workspace } from "@decohere/mesh";
13
12
  import { ActionResponse } from "@decohere/nexus/cli";
14
13
  import { io } from "@decohere/nexus/io";
@@ -9,7 +9,6 @@ function _ts_metadata(k, v) {
9
9
  }
10
10
  import path from "node:path";
11
11
  import { injectable } from "@decohere/crucible/decorators";
12
- // biome-ignore lint/style/useImportType: <DI>
13
12
  import { Workspace } from "@decohere/mesh";
14
13
  import { ActionResponse } from "@decohere/nexus/cli";
15
14
  import { Git } from "@decohere/nexus/interop";
@@ -9,7 +9,6 @@ function _ts_metadata(k, v) {
9
9
  }
10
10
  import path from "node:path";
11
11
  import { injectable } from "@decohere/crucible/decorators";
12
- // biome-ignore lint/style/useImportType: <DI>
13
12
  import { Workspace } from "@decohere/mesh";
14
13
  import { ActionResponse } from "@decohere/nexus/cli";
15
14
  import { io } from "@decohere/nexus/io";
@@ -8,13 +8,11 @@ function _ts_metadata(k, v) {
8
8
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
9
  }
10
10
  import { injectable } from "@decohere/crucible/decorators";
11
- // biome-ignore lint/style/useImportType: <DI>
12
11
  import { Workspace } from "@decohere/mesh";
13
12
  import { ActionResponse } from "@decohere/nexus/cli";
14
13
  import { io } from "@decohere/nexus/io";
15
14
  import { scribe } from "@decohere/scribe";
16
15
  import { formatUnknownError } from "../errors.js";
17
- // biome-ignore lint/style/useImportType: <DI>
18
16
  import { DependencyResolver } from "../services/dependency-resolver.service.js";
19
17
  export class ReportDependencies {
20
18
  workspace;
@@ -8,12 +8,10 @@ function _ts_metadata(k, v) {
8
8
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
9
  }
10
10
  import { injectable } from "@decohere/crucible/decorators";
11
- // biome-ignore lint/style/useImportType: <DI>
12
11
  import { Workspace } from "@decohere/mesh";
13
12
  import { ActionResponse } from "@decohere/nexus/cli";
14
13
  import { scribe } from "@decohere/scribe";
15
14
  import { formatUnknownError } from "../errors.js";
16
- // biome-ignore lint/style/useImportType: <DI>
17
15
  import { DependencyResolver } from "../services/dependency-resolver.service.js";
18
16
  export class SyncDependencies {
19
17
  workspace;
@@ -9,7 +9,6 @@ function _ts_metadata(k, v) {
9
9
  }
10
10
  import path from "node:path";
11
11
  import { injectable } from "@decohere/crucible/decorators";
12
- // biome-ignore lint/style/useImportType: <DI>
13
12
  import { Workspace } from "@decohere/mesh";
14
13
  import { ActionResponse } from "@decohere/nexus/cli";
15
14
  import { Docker as DockerInterop } from "@decohere/nexus/interop";
@@ -68,11 +67,11 @@ export class Docker {
68
67
  ...extraTags
69
68
  ];
70
69
  const buildArgs = this.interpolateBuildArgs({
71
- ...dockerConfig.buildArgs ?? {},
70
+ ...dockerConfig.buildArgs,
72
71
  ...envBuildArgs
73
72
  }, version);
74
73
  const secrets = {
75
- ...dockerConfig.secrets ?? {},
74
+ ...dockerConfig.secrets,
76
75
  ...envSecrets
77
76
  };
78
77
  this.#logger.info(`${options.dryRun ? "[dry-run] " : ""}Building ${project.name}`);
@@ -10,7 +10,6 @@ function _ts_metadata(k, v) {
10
10
  import fs from "node:fs/promises";
11
11
  import path from "node:path";
12
12
  import { injectable } from "@decohere/crucible/decorators";
13
- // biome-ignore lint/style/useImportType: <DI>
14
13
  import { Workspace } from "@decohere/mesh";
15
14
  import { ActionResponse } from "@decohere/nexus/cli";
16
15
  import { scribe } from "@decohere/scribe";
@@ -8,7 +8,6 @@ function _ts_metadata(k, v) {
8
8
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
9
  }
10
10
  import { injectable } from "@decohere/crucible/decorators";
11
- // biome-ignore lint/style/useImportType: <DI>
12
11
  import { Workspace } from "@decohere/mesh";
13
12
  import { ActionResponse } from "@decohere/nexus/cli";
14
13
  import { Npm } from "@decohere/nexus/interop";
@@ -8,7 +8,6 @@ function _ts_metadata(k, v) {
8
8
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
9
  }
10
10
  import { injectable } from "@decohere/crucible/decorators";
11
- // biome-ignore lint/style/useImportType: <DI>
12
11
  import { Workspace } from "@decohere/mesh";
13
12
  import { ActionResponse } from "@decohere/nexus/cli";
14
13
  import { Git } from "@decohere/nexus/interop";
@@ -8,14 +8,12 @@ function _ts_metadata(k, v) {
8
8
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
9
  }
10
10
  import { injectable } from "@decohere/crucible/decorators";
11
- // biome-ignore lint/style/useImportType: <DI>
12
11
  import { Workspace } from "@decohere/mesh";
13
12
  import { ActionResponse } from "@decohere/nexus/cli";
14
13
  import { Trace } from "@decohere/nexus/diagnostics";
15
14
  import { scribe } from "@decohere/scribe";
16
15
  import ts from "typescript";
17
16
  import { ForgeError } from "../errors.js";
18
- // biome-ignore lint/style/useImportType: <DI>
19
17
  import { TypeEmitter } from "../services/type-emitter.js";
20
18
  export class TypeCheck {
21
19
  workspace;
@@ -11,7 +11,6 @@ import { execSync } from "node:child_process";
11
11
  import { readdir, readFile } from "node:fs/promises";
12
12
  import { join } from "node:path";
13
13
  import { injectable } from "@decohere/crucible/decorators";
14
- // biome-ignore lint/style/useImportType: <DI>
15
14
  import { Workspace } from "@decohere/mesh";
16
15
  import { ActionResponse } from "@decohere/nexus/cli";
17
16
  import { Git } from "@decohere/nexus/interop";
@@ -63,9 +62,7 @@ export class Version {
63
62
  }
64
63
  await workspace.persistPackageJson((await workspace.getPackageJson()).withVersion(nextVersion.toString()));
65
64
  const responses = await Promise.all([
66
- ...[
67
- this.updatePackageJson(workspace.name, workspace, nextVersion)
68
- ],
65
+ this.updatePackageJson(workspace.name, workspace, nextVersion),
69
66
  ...projects.map((project)=>this.updatePackageJson(project.name, project, nextVersion))
70
67
  ]);
71
68
  const failed = responses.filter((r)=>!r.succeeded);
@@ -8,7 +8,6 @@ function _ts_metadata(k, v) {
8
8
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
9
  }
10
10
  import { injectable } from "@decohere/crucible/decorators";
11
- // biome-ignore lint/style/useImportType: <DI>
12
11
  import { Workspace } from "@decohere/mesh";
13
12
  import { ActionResponse } from "@decohere/nexus/cli";
14
13
  import { scribe } from "@decohere/scribe";
@@ -58,8 +58,11 @@ export const DefaultConfigurations = [
58
58
  ];
59
59
  export const WorkspaceDefaults = {
60
60
  ...BaseDefaults,
61
+ // Root-only files npm ships with the package. Globs that match nothing are fine.
61
62
  copyFiles: [
62
63
  "package.json",
64
+ "README.md",
65
+ "LICENSE*",
63
66
  "**/*.mjs"
64
67
  ],
65
68
  configurations: DefaultConfigurations,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decohere/forge",
3
- "version": "0.0.1-dev.97",
3
+ "version": "0.0.1-dev.99",
4
4
  "description": "TypeScript monorepo build system — zero config, fast compilation, streamlined developer workflow",
5
5
  "keywords": [
6
6
  "typescript",
@@ -29,10 +29,10 @@
29
29
  "test": "echo \"Error: no test specified\" && exit 1"
30
30
  },
31
31
  "dependencies": {
32
- "@decohere/crucible": "0.0.1-dev.97",
33
- "@decohere/mesh": "0.0.1-dev.97",
34
- "@decohere/nexus": "0.0.1-dev.97",
35
- "@decohere/scribe": "0.0.1-dev.97",
32
+ "@decohere/crucible": "0.0.1-dev.99",
33
+ "@decohere/mesh": "0.0.1-dev.99",
34
+ "@decohere/nexus": "0.0.1-dev.99",
35
+ "@decohere/scribe": "0.0.1-dev.99",
36
36
  "@swc/core": "^1.11.11",
37
37
  "async": "^3.2.6",
38
38
  "parse-imports": "^2.2.1",