This commit is contained in:
@@ -325,8 +325,9 @@ mirror the path, and they are claimed **globally** — across `cubeDirs`,
|
||||
- a duplicate id (the message names every claimant and how each got into the run);
|
||||
- a manifest that throws on import, exports a non-object, or has no `name`;
|
||||
- a `secrets` entry naming a key that is not in the schema;
|
||||
- a package in `cubePackages` that is not installed, cannot be read, or declares
|
||||
no `nopy.cubes`;
|
||||
- a package in `cubePackages` that is not installed, cannot be read, has neither a
|
||||
`cubes/` directory nor a `nopy.cubes` override, or whose `nopy.cubes` is not a
|
||||
non-empty array of strings;
|
||||
- a `nopy.cubes` entry that does not exist or points outside its package root.
|
||||
|
||||
Any of them aborts the run (`nopy.main.ts` returns before the workflow). None is
|
||||
@@ -380,7 +381,7 @@ const { packages, errors } = resolveCubePackages(config.cubePackages);
|
||||
interface CubePackage {
|
||||
name: string; // the name it was requested under
|
||||
root: string; // absolute path to the package root
|
||||
dirs: string[]; // absolute paths, from the package's `nopy.cubes` field
|
||||
dirs: string[]; // absolute paths: `<root>/cubes`, or the package's `nopy.cubes`
|
||||
}
|
||||
```
|
||||
|
||||
@@ -892,8 +893,8 @@ Nothing feeds the result into the built command — see
|
||||
const { selectedCubes } = await CubeSelection(cubes); // string[] of ids
|
||||
```
|
||||
|
||||
Multi-select with fuzzy filtering on the rendered label. A cancelled prompt
|
||||
returns an empty array rather than throwing.
|
||||
Multi-select with fuzzy filtering on the rendered label. A prompt dismissed with
|
||||
Escape returns an empty array rather than throwing.
|
||||
|
||||
### `HostSelection(hosts)`
|
||||
|
||||
@@ -1182,7 +1183,7 @@ Real behaviour that a reader would otherwise take on trust. Tracked in
|
||||
`Cube.getDefaults()`, against `{}` — and prompt input is type-coerced, which is
|
||||
not the same thing.
|
||||
- **Nothing checks bundle/CLI compatibility.** A cube package declares no
|
||||
supported nopy range and the loader reads whatever `nopy.cubes` points at.
|
||||
supported nopy range and the loader scans whatever directories it finds.
|
||||
- **`self-update` reports an empty channel as unreachable.** `latest === null`
|
||||
means either the request failed *or* the registry answered normally and the
|
||||
dist-tag simply has no version — the second is exactly what a Gitea package
|
||||
|
||||
@@ -22,13 +22,13 @@ The rest of this document is for writing one.
|
||||
|
||||
## What a bundle is
|
||||
|
||||
An ordinary npm package that ships cube directories and points at them from its
|
||||
own `package.json`. There is no build step, no plugin API and no entry point —
|
||||
nopy reads the directories off disk and imports each `manifest.mjs` directly.
|
||||
An ordinary npm package that ships its cubes in a `cubes/` directory. There is no
|
||||
build step, no plugin API and no entry point — nopy reads the directory off disk
|
||||
and imports each `manifest.mjs` directly.
|
||||
|
||||
```
|
||||
@acme/cubes-web
|
||||
├── package.json nopy.cubes → ["./cubes"]
|
||||
├── package.json no nopy block needed
|
||||
├── README.md
|
||||
└── cubes/
|
||||
├── nginx/
|
||||
@@ -50,7 +50,6 @@ special-cased.
|
||||
"name": "@acme/cubes-web",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"nopy": { "cubes": ["./cubes"] },
|
||||
"files": ["cubes", "!cubes/**/*.log", "README.md", "LICENSE"],
|
||||
"publishConfig": { "access": "public" },
|
||||
"dependencies": {
|
||||
@@ -60,10 +59,24 @@ special-cased.
|
||||
}
|
||||
```
|
||||
|
||||
**`nopy.cubes`** is the only field nopy requires. It is an array of directories,
|
||||
relative to the package root, each scanned recursively for cubes. Several
|
||||
entries are fine; a single `["./cubes"]` is the norm. Every entry must exist and
|
||||
must stay inside the package — a path escaping the root is refused, not resolved.
|
||||
**Nothing declares the cubes.** `cubes/` at the package root is the convention,
|
||||
scanned recursively, and a bundle that follows it needs no nopy-specific field at
|
||||
all. Naming the package in `cubePackages` is already the statement that cubes are
|
||||
expected from it.
|
||||
|
||||
**`nopy.cubes`** overrides that, for the bundle whose cubes are somewhere else — a
|
||||
package compiled from TypeScript sources into `dist/cubes`, say, or one shipping
|
||||
two separate trees:
|
||||
|
||||
```json
|
||||
"nopy": { "cubes": ["./dist/cubes", "./contrib"] }
|
||||
```
|
||||
|
||||
It is an array of directories relative to the package root. Every entry must
|
||||
exist and must stay inside the package — a path escaping the root is refused, not
|
||||
resolved. Present-but-malformed (an empty array, a bare string, non-strings) is an
|
||||
error rather than a fall back to the default: saying something that does not parse
|
||||
is not the same as saying nothing.
|
||||
|
||||
**`type: "module"`** matters: manifests are ESM. Without it a `manifest.mjs` still
|
||||
loads (the extension carries the day), but anything it imports relatively will
|
||||
@@ -222,7 +235,7 @@ Nothing bundle-specific: `npm publish` (or `pnpm publish`) with a version bump.
|
||||
Some things worth deciding once:
|
||||
|
||||
- **Version the bundle independently of nopy.** There is no compatibility check
|
||||
between the two — the loader reads whatever `nopy.cubes` points at. Document
|
||||
between the two — the loader scans whatever directories it finds. Document
|
||||
the nopy version you test against in your README.
|
||||
- **Renaming or removing an id is breaking.** It invalidates recorded sessions
|
||||
and breaks any manifest listing it as a dependency, including manifests in
|
||||
@@ -254,8 +267,9 @@ For how this repository releases its own packages, see
|
||||
| Symptom | Cause |
|
||||
| --- | --- |
|
||||
| `Cube package 'X' is not installed (looked up from …)` | Not installed, or installed somewhere other than the config that named it. The path in the message is where the lookup started. |
|
||||
| `Cube package 'X' declares no cubes` | Missing or malformed `nopy.cubes` in the package's `package.json`. It must be a non-empty array of strings. |
|
||||
| `'./cubes' does not exist in …` | The directory was not packed. Check `files` and `npm pack --dry-run`. |
|
||||
| `Cube package 'X' has no cubes/ directory in …` | No `cubes/` at the package root and no `nopy.cubes` pointing elsewhere. Usually the directory was not packed — check `files` and `npm pack --dry-run`. |
|
||||
| `"nopy": { "cubes": … } must be a non-empty array of strings` | The override is present but malformed. Fix it, or omit it entirely to use `./cubes`. |
|
||||
| `'…' does not exist in …` | A `nopy.cubes` entry pointing at a directory the tarball does not contain. |
|
||||
| `'…' points outside the package` | A `nopy.cubes` entry escaping the package root. Not allowed. |
|
||||
| `Duplicate cube id 'X' from N sources:` | Two or more cubes claiming one id; the message lists each source. Rename one — there is no precedence rule to lean on. |
|
||||
| `ERR_MODULE_NOT_FOUND` for `zod` or `@bitsquare/nopy-cube` | The bundle did not declare them as dependencies. The resolve-hook fallback covers loose local cubes, not published packages. |
|
||||
|
||||
@@ -141,7 +141,6 @@ A cube bundle is an npm package with a `nopy` field:
|
||||
"name": "@acme/cubes-net",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"nopy": { "cubes": ["./cubes"] },
|
||||
"files": ["cubes", "README.md", "LICENSE"],
|
||||
"keywords": ["nopy", "nopy-cubes", "pyinfra"],
|
||||
"dependencies": {
|
||||
@@ -154,10 +153,16 @@ A cube bundle is an npm package with a `nopy` field:
|
||||
|
||||
Rules:
|
||||
|
||||
- `nopy.cubes` — directories relative to the package root, scanned exactly like
|
||||
`cubeDirs` entries. Required; a package listed in `cubePackages` without a
|
||||
`nopy` field is an error, not a silent skip. Listing it means the user expects
|
||||
cubes from it.
|
||||
- **Cube location is a convention, `<root>/cubes`.** *(Amended after Phase 5;
|
||||
originally `nopy.cubes` was a required field.)* The field bought nothing a
|
||||
convention does not: it is not a discovery marker — naming the package in
|
||||
`cubePackages` already is one — and it says nothing about whether the
|
||||
directories were actually packed, which is the failure authors really hit.
|
||||
`nopy.cubes` remains as an **override**, directories relative to the package
|
||||
root, for the bundle whose cubes are elsewhere (`dist/cubes` after a build).
|
||||
Absent means the default; present-and-malformed is an error rather than a fall
|
||||
back. Finding no cube directory at all is still an error, not a silent skip:
|
||||
listing a package means the user expects cubes from it.
|
||||
- Both dependencies are **regular dependencies, not peers**, and both are
|
||||
load-bearing: a manifest imports `Manifest` from `@bitsquare/nopy-cube` and `z`
|
||||
from `zod`. `@bitsquare/nopy-cube` peer-depends on zod, so the bundle's copy is
|
||||
@@ -255,7 +260,8 @@ Errors (each aborts the run, consistent with the existing `errors` contract):
|
||||
|
||||
- package not found on any candidate path
|
||||
- `package.json` unparseable
|
||||
- no `nopy.cubes`, or it is not a non-empty array of strings
|
||||
- neither a `cubes/` directory nor a `nopy.cubes` override
|
||||
- `nopy.cubes` present but not a non-empty array of strings
|
||||
- a `nopy.cubes` entry escapes the package root, or does not exist
|
||||
|
||||
### Wiring
|
||||
@@ -625,7 +631,8 @@ runner alike.
|
||||
- resolves through a symlinked package directory (mimicking pnpm)
|
||||
- resolves from the declaring config's directory, not `cwd`
|
||||
- missing package → error naming the spec
|
||||
- package without `nopy.cubes` → error
|
||||
- package without `nopy.cubes` → falls back to `cubes/`
|
||||
- package with neither → error; malformed `nopy.cubes` → error, no fall back
|
||||
- `nopy.cubes` entry that does not exist, and one that escapes the root → errors
|
||||
- last-wins dedupe when parent and child config both name a package
|
||||
|
||||
|
||||
Reference in New Issue
Block a user