@atarashi/registry 1.0.0
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/LICENSE +21 -0
- package/README.md +81 -0
- package/dist/index.d.ts +477 -0
- package/dist/index.js +1191 -0
- package/dist/index.js.map +1 -0
- package/package.json +54 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Gautam Suthar
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# `@atarashi/registry`
|
|
2
|
+
|
|
3
|
+
The only I/O boundary for blueprints. `@atarashi/core` asks this package for a
|
|
4
|
+
`LoadedBlueprint` and never touches the disk or the network itself.
|
|
5
|
+
|
|
6
|
+
```ts
|
|
7
|
+
import { createRegistry } from '@atarashi/registry';
|
|
8
|
+
import { generate } from '@atarashi/core';
|
|
9
|
+
|
|
10
|
+
const registry = createRegistry({ cwd: process.cwd(), offline: false });
|
|
11
|
+
const plan = await generate(spec, { source: registry, atarashiVersion });
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Four layers
|
|
15
|
+
|
|
16
|
+
| Layer | Where | Trust |
|
|
17
|
+
|---|---|---|
|
|
18
|
+
| Local | `./.atarashi/blueprints/<ns>/<name>/blueprint.json` | Untrusted |
|
|
19
|
+
| npm | `atarashi-blueprint-*` packages, by `npm:` spec | Untrusted |
|
|
20
|
+
| Registry | `atarashi.gautamsuthar.in/registry`, verified and cached | Per source |
|
|
21
|
+
| Bundled | `@atarashi/blueprints`, inside the npm package | Trusted |
|
|
22
|
+
|
|
23
|
+
Local wins outright, so an author can override anything while developing. Then
|
|
24
|
+
npm. Then the registry and the bundled collection compete on version: the newer
|
|
25
|
+
one wins, which is how a registry release ships a blueprint fix without a CLI
|
|
26
|
+
release, while a run with no network still resolves everything from the
|
|
27
|
+
bundled copy.
|
|
28
|
+
|
|
29
|
+
`trusted` is set by the *source*, never by the document the source serves. Only
|
|
30
|
+
trusted blueprints may run hooks without explicit consent (doc 09, T2).
|
|
31
|
+
|
|
32
|
+
## Integrity
|
|
33
|
+
|
|
34
|
+
- The index carries a root hash over its canonicalized entries and version
|
|
35
|
+
manifest; it is re-verified on every read, including from the cache.
|
|
36
|
+
- Every tarball is checked against its `sha256` digest before extraction.
|
|
37
|
+
- A mismatch is a hard failure that names the artifact. There is no fallback
|
|
38
|
+
path to an unverified copy: unreachability degrades, tampering does not.
|
|
39
|
+
|
|
40
|
+
## Extraction
|
|
41
|
+
|
|
42
|
+
Archives are parsed entirely in memory and only written once every member has
|
|
43
|
+
been validated: no absolute paths, drive letters, UNC paths, `..` segments,
|
|
44
|
+
null bytes, symlinks or hard links; per-file, total-size and entry-count
|
|
45
|
+
ceilings; case-collision detection; and a re-check that each resolved path is
|
|
46
|
+
inside the destination. This is a security boundary; see `tests/extract.test.ts`.
|
|
47
|
+
|
|
48
|
+
## Cache
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
$XDG_CACHE_HOME/atarashi/ # ~/Library/Caches on macOS, LOCALAPPDATA on Windows
|
|
52
|
+
index/1.4.2.json # verified against its published root hash
|
|
53
|
+
blueprints/db-postgres-1.2.0-8f2c…/
|
|
54
|
+
meta.json # ETags, fetch timestamps
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Content-addressed, so it is safe to share between projects and CI runs
|
|
58
|
+
(`~/.cache/atarashi` makes a good CI cache key). Override with
|
|
59
|
+
`ATARASHI_CACHE_DIR`.
|
|
60
|
+
|
|
61
|
+
Freshness is a 24 h TTL plus an ETag: inside the TTL nothing is requested, and
|
|
62
|
+
after it a `304` costs one conditional GET. A pinned version skips both: a
|
|
63
|
+
published registry version is immutable, so a cache hit is always correct.
|
|
64
|
+
|
|
65
|
+
## Offline
|
|
66
|
+
|
|
67
|
+
`offline: true` or `ATARASHI_OFFLINE=1` skips the network entirely. Bundled
|
|
68
|
+
blueprints and anything already cached still resolve; a registry-only blueprint
|
|
69
|
+
reports what to run to populate the cache.
|
|
70
|
+
|
|
71
|
+
## Publishing
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
pnpm --filter @atarashi/registry build:index # pack, hash, emit v1/index.json
|
|
75
|
+
pnpm --filter @atarashi/registry bump:versions # propose version-manifest bumps
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`build-index.mts` is deterministic (sorted members, fixed mtime, no uid/gid),
|
|
79
|
+
so rebuilding the same sources produces byte-identical tarballs and the same
|
|
80
|
+
digests. `.github/workflows/registry-publish.yml` publishes it and refuses to
|
|
81
|
+
overwrite a version that already exists.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
import * as _atarashi_schema from '@atarashi/schema';
|
|
2
|
+
import { RegistryIndex, Diagnostic, JsonValue, PresetManifest, BlueprintManifest, RegistryEntry, RegistrySource, RegistryPin } from '@atarashi/schema';
|
|
3
|
+
import { LoadedBlueprint, BlueprintSummary, BlueprintSource } from '@atarashi/core';
|
|
4
|
+
|
|
5
|
+
/** The default public registry. Overridable per-run and per-source. */
|
|
6
|
+
declare const DEFAULT_REGISTRY_URL = "https://atarashi.gautamsuthar.in/registry";
|
|
7
|
+
/** The index is re-checked at most once a day; pinned versions are immutable. */
|
|
8
|
+
declare const DEFAULT_TTL_MS: number;
|
|
9
|
+
interface CacheLayout {
|
|
10
|
+
root: string;
|
|
11
|
+
/** Verified index documents, one file per registry version. */
|
|
12
|
+
index: string;
|
|
13
|
+
/** Extracted blueprints, each directory named by its content hash. */
|
|
14
|
+
blueprints: string;
|
|
15
|
+
/** ETags and fetch timestamps. */
|
|
16
|
+
meta: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* XDG on Linux, the platform convention elsewhere. `ATARASHI_CACHE_DIR` wins so
|
|
20
|
+
* CI can point the whole cache at a single restorable directory.
|
|
21
|
+
*/
|
|
22
|
+
declare function resolveCacheRoot(env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform): string;
|
|
23
|
+
declare function cacheLayout(root: string): CacheLayout;
|
|
24
|
+
/** `db/postgres` → `db-postgres`, so ids are usable as path and file names. */
|
|
25
|
+
declare const slugifyId: (id: string) => string;
|
|
26
|
+
|
|
27
|
+
/** What we remember between runs: ETags, fetch times and what is extracted. */
|
|
28
|
+
interface CacheMeta {
|
|
29
|
+
metaVersion: 1;
|
|
30
|
+
index: Record<string, {
|
|
31
|
+
etag?: string;
|
|
32
|
+
fetchedAt: string;
|
|
33
|
+
version: string;
|
|
34
|
+
}>;
|
|
35
|
+
blueprints: Record<string, {
|
|
36
|
+
id: string;
|
|
37
|
+
version: string;
|
|
38
|
+
integrity: string;
|
|
39
|
+
dir: string;
|
|
40
|
+
}>;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The on-disk cache. Content-addressed: a blueprint directory is named by its
|
|
44
|
+
* tarball's hash, so entries for different versions never collide and the whole
|
|
45
|
+
* directory is safe to share between projects and CI runs.
|
|
46
|
+
*/
|
|
47
|
+
declare class RegistryCache {
|
|
48
|
+
readonly layout: CacheLayout;
|
|
49
|
+
private meta;
|
|
50
|
+
constructor(root: string);
|
|
51
|
+
readMeta(): Promise<CacheMeta>;
|
|
52
|
+
writeMeta(update: (meta: CacheMeta) => void): Promise<void>;
|
|
53
|
+
indexPath(version: string): string;
|
|
54
|
+
readIndex(version: string, source: string): Promise<RegistryIndex | undefined>;
|
|
55
|
+
writeIndex(index: RegistryIndex): Promise<void>;
|
|
56
|
+
/** Every registry version this machine has already verified. */
|
|
57
|
+
cachedIndexVersions(): Promise<string[]>;
|
|
58
|
+
/**
|
|
59
|
+
* Content-addressed, but still legible: `db-postgres-1.2.0-8f2c…`. The hash
|
|
60
|
+
* suffix is what makes it content-addressed; the rest is for humans reading
|
|
61
|
+
* `atarashi registry verify` output.
|
|
62
|
+
*/
|
|
63
|
+
blueprintDir(id: string, version: string, integrity: string): string;
|
|
64
|
+
hasBlueprint(id: string, version: string, integrity: string): boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Verifies, extracts into a temp directory, then renames into place — so a
|
|
67
|
+
* cache directory only ever exists in its complete, verified form.
|
|
68
|
+
*/
|
|
69
|
+
storeBlueprint(entry: {
|
|
70
|
+
id: string;
|
|
71
|
+
version: string;
|
|
72
|
+
integrity: string;
|
|
73
|
+
}, tarball: Buffer): Promise<string>;
|
|
74
|
+
clear(): Promise<void>;
|
|
75
|
+
/**
|
|
76
|
+
* Re-checks that everything the meta file claims is cached is still there.
|
|
77
|
+
* Extracted trees cannot be re-hashed against the tarball digest, so this
|
|
78
|
+
* reports presence and shape, and prunes what has gone missing.
|
|
79
|
+
*/
|
|
80
|
+
verify(): Promise<{
|
|
81
|
+
checked: number;
|
|
82
|
+
removed: string[];
|
|
83
|
+
}>;
|
|
84
|
+
size(): Promise<number>;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Every failure that crosses the registry boundary. Carries a
|
|
89
|
+
* `DIAGNOSTIC_CODES` value so the CLI can map it to an exit code (6 for every
|
|
90
|
+
* registry failure, per doc 04) and the web builder to a UI affordance.
|
|
91
|
+
*/
|
|
92
|
+
declare class RegistryError extends Error {
|
|
93
|
+
readonly code: string;
|
|
94
|
+
readonly suggestions: string[];
|
|
95
|
+
readonly detail: string | undefined;
|
|
96
|
+
constructor(code: string, message: string, options?: {
|
|
97
|
+
suggestions?: string[];
|
|
98
|
+
detail?: string;
|
|
99
|
+
cause?: unknown;
|
|
100
|
+
});
|
|
101
|
+
toDiagnostic(): Diagnostic;
|
|
102
|
+
}
|
|
103
|
+
declare const unavailable: (message: string, suggestions?: string[]) => RegistryError;
|
|
104
|
+
declare const integrityFailure: (message: string, detail?: string) => RegistryError;
|
|
105
|
+
|
|
106
|
+
interface ExtractLimits {
|
|
107
|
+
/** Refuses archives with an absurd number of members. */
|
|
108
|
+
maxEntries: number;
|
|
109
|
+
/** Per-file ceiling. */
|
|
110
|
+
maxFileSize: number;
|
|
111
|
+
/** Total uncompressed bytes — the zip-bomb guard. */
|
|
112
|
+
maxTotalSize: number;
|
|
113
|
+
}
|
|
114
|
+
declare const DEFAULT_LIMITS: ExtractLimits;
|
|
115
|
+
interface ExtractedFile {
|
|
116
|
+
path: string;
|
|
117
|
+
mode: number;
|
|
118
|
+
contents: Buffer;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* The same path rules the writer applies to generated files, applied to archive
|
|
122
|
+
* members: no absolute paths, no drive letters, no `..`, no null bytes. This is
|
|
123
|
+
* a security boundary (doc 09, T1) — every rejection here is a hard failure,
|
|
124
|
+
* never a skipped entry.
|
|
125
|
+
*/
|
|
126
|
+
declare function safeEntryPath(raw: string, strip?: number): string | undefined;
|
|
127
|
+
/**
|
|
128
|
+
* Parses a tarball entirely in memory. Nothing reaches the filesystem until
|
|
129
|
+
* every member has been validated, so a malicious archive cannot leave a
|
|
130
|
+
* half-extracted mess behind.
|
|
131
|
+
*/
|
|
132
|
+
declare function readTarball(tarball: Buffer, options?: {
|
|
133
|
+
strip?: number;
|
|
134
|
+
limits?: Partial<ExtractLimits>;
|
|
135
|
+
}): Promise<ExtractedFile[]>;
|
|
136
|
+
/**
|
|
137
|
+
* Writes a validated archive into `dest`. Every resolved path is re-checked
|
|
138
|
+
* against the destination root, so a bug in the path rules above still cannot
|
|
139
|
+
* write outside it.
|
|
140
|
+
*/
|
|
141
|
+
declare function extractTarball(tarball: Buffer, dest: string, options?: {
|
|
142
|
+
strip?: number;
|
|
143
|
+
limits?: Partial<ExtractLimits>;
|
|
144
|
+
}): Promise<string[]>;
|
|
145
|
+
|
|
146
|
+
type FetchLike = (url: string, init?: RequestInit) => Promise<Response>;
|
|
147
|
+
interface HttpResponse {
|
|
148
|
+
status: number;
|
|
149
|
+
body: Buffer;
|
|
150
|
+
etag: string | undefined;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* TLS only, with no plaintext fallback (doc 09, T4). `file:` is allowed so a
|
|
154
|
+
* local mirror — and the test suite — can stand in for the CDN.
|
|
155
|
+
*/
|
|
156
|
+
declare function assertFetchable(url: string): URL;
|
|
157
|
+
interface HttpOptions {
|
|
158
|
+
etag?: string | undefined;
|
|
159
|
+
timeoutMs?: number;
|
|
160
|
+
maxBytes?: number;
|
|
161
|
+
fetchImpl?: FetchLike;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* A conditional GET. `304` comes back with an empty body and the caller keeps
|
|
165
|
+
* what it has cached; anything else is a registry failure with the URL named.
|
|
166
|
+
*/
|
|
167
|
+
declare function httpGet(url: string, options?: HttpOptions): Promise<HttpResponse>;
|
|
168
|
+
/** Joins a registry base URL with a path, tolerating a trailing slash either way. */
|
|
169
|
+
declare const joinUrl: (base: string, path: string) => string;
|
|
170
|
+
|
|
171
|
+
interface IndexStoreOptions {
|
|
172
|
+
cache: RegistryCache;
|
|
173
|
+
url?: string;
|
|
174
|
+
/** Exact registry version to resolve against. Immutable once published. */
|
|
175
|
+
pin?: string | null;
|
|
176
|
+
offline?: boolean;
|
|
177
|
+
ttlMs?: number;
|
|
178
|
+
fetchImpl?: FetchLike;
|
|
179
|
+
now?: () => Date;
|
|
180
|
+
/** Third-party sources never get to run hooks, whatever their index says. */
|
|
181
|
+
trusted?: boolean;
|
|
182
|
+
}
|
|
183
|
+
/** Where a resolved index came from — reported by `atarashi doctor`. */
|
|
184
|
+
interface IndexOrigin {
|
|
185
|
+
version: string;
|
|
186
|
+
url: string;
|
|
187
|
+
from: 'network' | 'cache' | 'bundled';
|
|
188
|
+
fetchedAt?: string;
|
|
189
|
+
integrity?: string;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Fetches, verifies and caches the registry index.
|
|
193
|
+
*
|
|
194
|
+
* Freshness is a 24 h TTL plus an ETag: inside the TTL nothing is requested at
|
|
195
|
+
* all, and after it a `304` costs one conditional GET. A pinned version skips
|
|
196
|
+
* both — it is immutable, so a cache hit is always correct.
|
|
197
|
+
*/
|
|
198
|
+
declare class IndexStore {
|
|
199
|
+
private readonly options;
|
|
200
|
+
private loaded;
|
|
201
|
+
readonly diagnostics: Diagnostic[];
|
|
202
|
+
constructor(options: IndexStoreOptions);
|
|
203
|
+
get url(): string;
|
|
204
|
+
get origin(): IndexOrigin | undefined;
|
|
205
|
+
/** Resolves once per process; subsequent calls reuse the verified document. */
|
|
206
|
+
load(): Promise<{
|
|
207
|
+
index: RegistryIndex;
|
|
208
|
+
origin: IndexOrigin;
|
|
209
|
+
}>;
|
|
210
|
+
/** Discards the TTL and re-checks the network — `atarashi registry update`. */
|
|
211
|
+
refresh(): Promise<{
|
|
212
|
+
index: RegistryIndex;
|
|
213
|
+
origin: IndexOrigin;
|
|
214
|
+
}>;
|
|
215
|
+
private loadPinned;
|
|
216
|
+
private loadLatest;
|
|
217
|
+
private parse;
|
|
218
|
+
private persist;
|
|
219
|
+
private touch;
|
|
220
|
+
private newestCached;
|
|
221
|
+
private isFresh;
|
|
222
|
+
private originOf;
|
|
223
|
+
private now;
|
|
224
|
+
private nowIso;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
type HashAlgorithm = 'sha256' | 'sha384' | 'sha512';
|
|
228
|
+
/** Subresource-integrity style digest: `sha256-<base64>`. */
|
|
229
|
+
declare function digest(data: Buffer | string, algorithm?: HashAlgorithm): string;
|
|
230
|
+
/** The hex form, used for content-addressed directory names. */
|
|
231
|
+
declare function hexDigest(data: Buffer | string, algorithm?: HashAlgorithm): string;
|
|
232
|
+
declare function parseIntegrity(value: string): {
|
|
233
|
+
algorithm: HashAlgorithm;
|
|
234
|
+
base64: string;
|
|
235
|
+
};
|
|
236
|
+
/**
|
|
237
|
+
* A mismatch is a hard failure that names the artifact — never a silent
|
|
238
|
+
* fallback to an unverified copy (doc 09, T4).
|
|
239
|
+
*/
|
|
240
|
+
declare function verifyIntegrity(data: Buffer, expected: string, what: string): void;
|
|
241
|
+
/**
|
|
242
|
+
* Deterministic JSON: object keys sorted, no incidental whitespace. Two
|
|
243
|
+
* machines must produce the same bytes for the same document, or the root hash
|
|
244
|
+
* is meaningless.
|
|
245
|
+
*/
|
|
246
|
+
declare function canonicalize(value: JsonValue | unknown): string;
|
|
247
|
+
/** The index's root hash covers its entries and its version manifest. */
|
|
248
|
+
declare function indexRootHash(index: Pick<RegistryIndex, 'entries' | 'versionManifest' | 'version' | 'indexVersion'>): string;
|
|
249
|
+
declare function verifyIndexIntegrity(index: RegistryIndex, source: string): void;
|
|
250
|
+
|
|
251
|
+
declare const MANIFEST_FILE = "blueprint.json";
|
|
252
|
+
declare const PRESET_FILE = "preset.json";
|
|
253
|
+
interface DirectoryBlueprintOptions {
|
|
254
|
+
trusted: boolean;
|
|
255
|
+
/** Human-readable provenance for diagnostics. Defaults to the directory. */
|
|
256
|
+
origin?: string;
|
|
257
|
+
}
|
|
258
|
+
/** Loads one blueprint from a directory containing a `blueprint.json`. */
|
|
259
|
+
declare function loadBlueprintDir(dir: string, options: DirectoryBlueprintOptions): Promise<LoadedBlueprint>;
|
|
260
|
+
declare function loadPresetFile(path: string): Promise<PresetManifest>;
|
|
261
|
+
declare const summarize: (manifest: BlueprintManifest) => BlueprintSummary;
|
|
262
|
+
/**
|
|
263
|
+
* A tree of blueprints laid out as `<root>/<namespace>/<name>/blueprint.json`,
|
|
264
|
+
* with presets in `<root>/../presets/*.json`. Backs both the bundled collection
|
|
265
|
+
* and a project's own `./.atarashi/blueprints`.
|
|
266
|
+
*/
|
|
267
|
+
declare class DirectorySource {
|
|
268
|
+
readonly root: string;
|
|
269
|
+
private readonly options;
|
|
270
|
+
private manifests;
|
|
271
|
+
constructor(root: string, options: DirectoryBlueprintOptions & {
|
|
272
|
+
presetsDir?: string;
|
|
273
|
+
});
|
|
274
|
+
/** Scans lazily and once — a cold `atarashi list` should not pay twice. */
|
|
275
|
+
private scan;
|
|
276
|
+
ids(): Promise<string[]>;
|
|
277
|
+
has(id: string): Promise<boolean>;
|
|
278
|
+
load(id: string): Promise<LoadedBlueprint | undefined>;
|
|
279
|
+
summaries(): Promise<BlueprintSummary[]>;
|
|
280
|
+
loadPreset(id: string): Promise<PresetManifest | undefined>;
|
|
281
|
+
presets(): Promise<PresetManifest[]>;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* The first-party collection shipped inside the npm package. Its presence is
|
|
286
|
+
* why `atarashi new` works on a plane: no index fetch, no tarball, no network.
|
|
287
|
+
*/
|
|
288
|
+
declare function findBundledRoot(explicit?: string): string | undefined;
|
|
289
|
+
interface BundledSource {
|
|
290
|
+
source: DirectorySource;
|
|
291
|
+
root: string;
|
|
292
|
+
versionManifestPath: string;
|
|
293
|
+
}
|
|
294
|
+
declare function createBundledSource(explicit?: string): BundledSource | undefined;
|
|
295
|
+
/**
|
|
296
|
+
* The bundled fallback for the registry's version manifest, so dependency
|
|
297
|
+
* ranges resolve offline exactly as they do online.
|
|
298
|
+
*/
|
|
299
|
+
declare function readBundledVersionManifest(path: string): Promise<Record<string, string> | undefined>;
|
|
300
|
+
|
|
301
|
+
/** Where a project keeps blueprints it is developing or vendoring. */
|
|
302
|
+
declare const LOCAL_BLUEPRINTS_DIR: string;
|
|
303
|
+
/**
|
|
304
|
+
* Project-local blueprints win over every other source, so an author can
|
|
305
|
+
* override a published blueprint by dropping a directory in the project. They
|
|
306
|
+
* are untrusted all the same: being on disk is not review.
|
|
307
|
+
*/
|
|
308
|
+
declare function createLocalSource(cwd: string, dir?: string): DirectorySource;
|
|
309
|
+
|
|
310
|
+
declare const NPM_PREFIX = "npm:";
|
|
311
|
+
/** `npm:atarashi-blueprint-redis` → `atarashi-blueprint-redis`. */
|
|
312
|
+
declare const parseNpmSpec: (spec: string) => string | undefined;
|
|
313
|
+
/**
|
|
314
|
+
* A community blueprint published to npm. The package's `atarashi` field points
|
|
315
|
+
* at the directory holding its `blueprint.json`; everything else about it is
|
|
316
|
+
* treated as hostile — untrusted, so hooks require explicit consent.
|
|
317
|
+
*/
|
|
318
|
+
declare class NpmSource {
|
|
319
|
+
private readonly cwd;
|
|
320
|
+
private readonly packages;
|
|
321
|
+
private readonly cache;
|
|
322
|
+
private resolved;
|
|
323
|
+
constructor(cwd: string, packages?: string[]);
|
|
324
|
+
/** Adds a package for this run, e.g. from `--add npm:atarashi-blueprint-redis`. */
|
|
325
|
+
add(spec: string): this;
|
|
326
|
+
private loadPackage;
|
|
327
|
+
private all;
|
|
328
|
+
load(id: string): Promise<LoadedBlueprint | undefined>;
|
|
329
|
+
summaries(): Promise<BlueprintSummary[]>;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
declare const entrySummary: (entry: RegistryEntry) => BlueprintSummary;
|
|
333
|
+
/**
|
|
334
|
+
* Blueprints from the remote index. Resolution is index-only — capabilities,
|
|
335
|
+
* versions and conflicts all come from entry summaries — so nothing is
|
|
336
|
+
* downloaded until a blueprint is actually part of the graph.
|
|
337
|
+
*/
|
|
338
|
+
declare class RemoteSource {
|
|
339
|
+
private readonly store;
|
|
340
|
+
private readonly cache;
|
|
341
|
+
private readonly options;
|
|
342
|
+
constructor(store: IndexStore, cache: RegistryCache, options?: {
|
|
343
|
+
offline?: boolean;
|
|
344
|
+
fetchImpl?: FetchLike;
|
|
345
|
+
/** Set from the *source* config, never from the index it serves. */
|
|
346
|
+
trusted?: boolean;
|
|
347
|
+
});
|
|
348
|
+
/** `undefined` when the index cannot be reached at all — bundled still works. */
|
|
349
|
+
private index;
|
|
350
|
+
entry(id: string): Promise<RegistryEntry | undefined>;
|
|
351
|
+
summaries(): Promise<BlueprintSummary[]>;
|
|
352
|
+
versionManifest(): Promise<Record<string, string> | undefined>;
|
|
353
|
+
load(id: string): Promise<LoadedBlueprint | undefined>;
|
|
354
|
+
loadEntry(entry: RegistryEntry): Promise<LoadedBlueprint>;
|
|
355
|
+
preset(id: string): Promise<{
|
|
356
|
+
kind: "blueprint" | "preset";
|
|
357
|
+
name: string;
|
|
358
|
+
version: string;
|
|
359
|
+
description: string;
|
|
360
|
+
id: string;
|
|
361
|
+
category: string;
|
|
362
|
+
tags: string[];
|
|
363
|
+
experimental: boolean;
|
|
364
|
+
deprecated: boolean;
|
|
365
|
+
provides: string[];
|
|
366
|
+
requires: string[];
|
|
367
|
+
conflicts: string[];
|
|
368
|
+
engines: {
|
|
369
|
+
atarashi?: string | undefined;
|
|
370
|
+
};
|
|
371
|
+
tarball: string;
|
|
372
|
+
integrity: string;
|
|
373
|
+
bundled: boolean;
|
|
374
|
+
} | undefined>;
|
|
375
|
+
loadPreset(id: string): Promise<{
|
|
376
|
+
blueprints: string[];
|
|
377
|
+
kind: "preset";
|
|
378
|
+
name: string;
|
|
379
|
+
version: string;
|
|
380
|
+
description: string;
|
|
381
|
+
manifestVersion: number;
|
|
382
|
+
id: string;
|
|
383
|
+
tags: string[];
|
|
384
|
+
experimental: boolean;
|
|
385
|
+
recommended: boolean;
|
|
386
|
+
answers: Record<string, _atarashi_schema.JsonValue>;
|
|
387
|
+
deprecated?: {
|
|
388
|
+
since: string;
|
|
389
|
+
use?: string | undefined;
|
|
390
|
+
reason?: string | undefined;
|
|
391
|
+
} | null | undefined;
|
|
392
|
+
} | undefined>;
|
|
393
|
+
/** Downloads and verifies once; every later run is a cache hit. */
|
|
394
|
+
private materialize;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
interface RegistryOptions {
|
|
398
|
+
/** The project directory — where local and npm blueprints are looked up. */
|
|
399
|
+
cwd?: string;
|
|
400
|
+
cacheDir?: string;
|
|
401
|
+
url?: string;
|
|
402
|
+
/** Exact registry version, from `atarashi.json` or `atarashi registry pin`. */
|
|
403
|
+
pin?: string | null;
|
|
404
|
+
offline?: boolean;
|
|
405
|
+
ttlMs?: number;
|
|
406
|
+
/** Extra third-party sources. Untrusted unless the user marked them trusted. */
|
|
407
|
+
sources?: RegistrySource[];
|
|
408
|
+
/** Community packages pulled in with `--add npm:atarashi-blueprint-x`. */
|
|
409
|
+
npmPackages?: string[];
|
|
410
|
+
bundledDir?: string;
|
|
411
|
+
localDir?: string;
|
|
412
|
+
fetchImpl?: FetchLike;
|
|
413
|
+
now?: () => Date;
|
|
414
|
+
env?: NodeJS.ProcessEnv;
|
|
415
|
+
}
|
|
416
|
+
/** Where a blueprint id was resolved from, for `atarashi info` and diagnostics. */
|
|
417
|
+
type SourceKind = 'local' | 'npm' | 'registry' | 'bundled';
|
|
418
|
+
/**
|
|
419
|
+
* The one I/O boundary for blueprints. Core asks this for a `LoadedBlueprint`
|
|
420
|
+
* and never touches disk or the network itself.
|
|
421
|
+
*
|
|
422
|
+
* Four layers, in precedence order:
|
|
423
|
+
*
|
|
424
|
+
* 1. `./.atarashi/blueprints` — a project's own, so an author can override
|
|
425
|
+
* anything while developing.
|
|
426
|
+
* 2. `npm:` packages the user explicitly asked for.
|
|
427
|
+
* 3. the remote registry and the bundled collection, whichever has the newer
|
|
428
|
+
* version — this is what lets a registry release ship a blueprint fix
|
|
429
|
+
* without a CLI release, while a plane-mode run still works.
|
|
430
|
+
*/
|
|
431
|
+
declare class Registry implements BlueprintSource {
|
|
432
|
+
private readonly local;
|
|
433
|
+
private readonly npm;
|
|
434
|
+
private readonly bundled;
|
|
435
|
+
private readonly remotes;
|
|
436
|
+
private readonly primary;
|
|
437
|
+
readonly cache: RegistryCache;
|
|
438
|
+
readonly offline: boolean;
|
|
439
|
+
constructor(options?: RegistryOptions);
|
|
440
|
+
/** Warnings collected while resolving — degraded-network notices, mostly. */
|
|
441
|
+
get diagnostics(): Diagnostic[];
|
|
442
|
+
load(id: string): Promise<LoadedBlueprint | undefined>;
|
|
443
|
+
providersOf(capability: string): Promise<BlueprintSummary[]>;
|
|
444
|
+
loadPreset(id: string): Promise<PresetManifest | undefined>;
|
|
445
|
+
/** Every preset visible from every layer, deduplicated by precedence — `atarashi list presets`. */
|
|
446
|
+
presets(): Promise<PresetManifest[]>;
|
|
447
|
+
/**
|
|
448
|
+
* One place defines the range every blueprint uses. The registry's manifest
|
|
449
|
+
* wins over the bundled one; anything the registry does not pin keeps the
|
|
450
|
+
* bundled value, so an offline run resolves the same names.
|
|
451
|
+
*/
|
|
452
|
+
versionManifest(): Promise<Record<string, string>>;
|
|
453
|
+
/** Every blueprint visible from every layer, deduplicated by precedence. */
|
|
454
|
+
summaries(): Promise<BlueprintSummary[]>;
|
|
455
|
+
list(): Promise<BlueprintSummary[]>;
|
|
456
|
+
/** Which layer an id resolves from, without loading its files. */
|
|
457
|
+
originOf(id: string): Promise<SourceKind | undefined>;
|
|
458
|
+
/** What gets written into `atarashi.json` so this run can be reproduced. */
|
|
459
|
+
pin(): Promise<RegistryPin | undefined>;
|
|
460
|
+
/** `atarashi registry update` — ignore the TTL and re-check every source. */
|
|
461
|
+
update(): Promise<{
|
|
462
|
+
name: string;
|
|
463
|
+
version: string;
|
|
464
|
+
from: string;
|
|
465
|
+
}[]>;
|
|
466
|
+
/** `atarashi registry list` — every version this machine has verified. */
|
|
467
|
+
cachedVersions(): Promise<string[]>;
|
|
468
|
+
verifyCache(): Promise<{
|
|
469
|
+
checked: number;
|
|
470
|
+
removed: string[];
|
|
471
|
+
}>;
|
|
472
|
+
clearCache(): Promise<void>;
|
|
473
|
+
addNpmPackage(spec: string): this;
|
|
474
|
+
}
|
|
475
|
+
declare const createRegistry: (options?: RegistryOptions) => Registry;
|
|
476
|
+
|
|
477
|
+
export { type CacheLayout, type CacheMeta, DEFAULT_LIMITS, DEFAULT_REGISTRY_URL, DEFAULT_TTL_MS, DirectorySource, type ExtractLimits, type ExtractedFile, type FetchLike, type HttpResponse, type IndexOrigin, IndexStore, type IndexStoreOptions, LOCAL_BLUEPRINTS_DIR, MANIFEST_FILE, NPM_PREFIX, NpmSource, PRESET_FILE, Registry, RegistryCache, RegistryError, type RegistryOptions, RemoteSource, type SourceKind, assertFetchable, cacheLayout, canonicalize, createBundledSource, createLocalSource, createRegistry, digest, entrySummary, extractTarball, findBundledRoot, hexDigest, httpGet, indexRootHash, integrityFailure, joinUrl, loadBlueprintDir, loadPresetFile, parseIntegrity, parseNpmSpec, readBundledVersionManifest, readTarball, resolveCacheRoot, safeEntryPath, slugifyId, summarize, unavailable, verifyIndexIntegrity, verifyIntegrity };
|