@botlr/cli 0.0.0-stage → 0.1.1

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/dist/index.js ADDED
@@ -0,0 +1,601 @@
1
+ // src/config.ts
2
+ import { access, readFile } from "fs/promises";
3
+ import path from "path";
4
+ var CONFIG_FILE = "botlr.config.json";
5
+ var DEFAULT_FRAMEWORK = "react-next";
6
+ var DEFAULT_SECTIONS_DIR = "components/sections";
7
+ var ConfigError = class extends Error {
8
+ constructor(message) {
9
+ super(message);
10
+ this.name = "ConfigError";
11
+ }
12
+ };
13
+ async function exists(file) {
14
+ try {
15
+ await access(file);
16
+ return true;
17
+ } catch {
18
+ return false;
19
+ }
20
+ }
21
+ async function findConfigFile(startDir) {
22
+ let dir = path.resolve(startDir);
23
+ for (; ; ) {
24
+ const candidate = path.join(dir, CONFIG_FILE);
25
+ if (await exists(candidate)) return candidate;
26
+ const parent = path.dirname(dir);
27
+ if (parent === dir) return null;
28
+ dir = parent;
29
+ }
30
+ }
31
+ async function readConfigFile(file) {
32
+ const raw = await readFile(file, "utf8");
33
+ try {
34
+ const parsed = JSON.parse(raw);
35
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
36
+ throw new ConfigError(`${file} must hold a JSON object`);
37
+ }
38
+ return parsed;
39
+ } catch (error) {
40
+ if (error instanceof ConfigError) throw error;
41
+ throw new ConfigError(`${file} is not valid JSON: ${error.message}`);
42
+ }
43
+ }
44
+ async function loadConfig(cwd, overrides = {}, env = process.env) {
45
+ const configPath = await findConfigFile(cwd);
46
+ const file = configPath ? await readConfigFile(configPath) : {};
47
+ const root = configPath ? path.dirname(configPath) : path.resolve(cwd);
48
+ const baseUrl = overrides.baseUrl ?? env.BOTLR_BASE_URL ?? file.baseUrl;
49
+ const appId = overrides.appId ?? env.BOTLR_APP_ID ?? file.appId;
50
+ if (!baseUrl) {
51
+ throw new ConfigError(`No baseUrl: set it in ${CONFIG_FILE} or BOTLR_BASE_URL`);
52
+ }
53
+ if (!appId) {
54
+ throw new ConfigError(`No appId: set it in ${CONFIG_FILE} or BOTLR_APP_ID`);
55
+ }
56
+ const sectionsDir = overrides.dir ? path.resolve(cwd, overrides.dir) : path.resolve(root, env.BOTLR_SECTIONS_DIR ?? file.sectionsDir ?? DEFAULT_SECTIONS_DIR);
57
+ return {
58
+ root,
59
+ configPath,
60
+ baseUrl: baseUrl.replace(/\/+$/, ""),
61
+ appId,
62
+ framework: overrides.framework ?? env.BOTLR_FRAMEWORK ?? file.framework ?? DEFAULT_FRAMEWORK,
63
+ sectionsDir,
64
+ apiKey: env.BOTLR_API_KEY || void 0,
65
+ personalToken: env.BOTLR_PAT || void 0
66
+ };
67
+ }
68
+
69
+ // src/lock.ts
70
+ import { mkdir, readFile as readFile2, writeFile } from "fs/promises";
71
+ import path2 from "path";
72
+ var LOCK_DIR = ".botlr";
73
+ var LOCK_FILE = "sections.lock.json";
74
+ function lockPath(root) {
75
+ return path2.join(root, LOCK_DIR, LOCK_FILE);
76
+ }
77
+ async function readLock(root) {
78
+ try {
79
+ const raw = await readFile2(lockPath(root), "utf8");
80
+ const parsed = JSON.parse(raw);
81
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
82
+ return parsed;
83
+ } catch (error) {
84
+ if (error.code === "ENOENT") return {};
85
+ throw error;
86
+ }
87
+ }
88
+ async function writeLock(root, lock) {
89
+ const file = lockPath(root);
90
+ await mkdir(path2.dirname(file), { recursive: true });
91
+ const sorted = Object.fromEntries(Object.entries(lock).sort(([a], [b]) => a.localeCompare(b)));
92
+ await writeFile(file, `${JSON.stringify(sorted, null, 2)}
93
+ `, "utf8");
94
+ }
95
+ async function updateLock(root, name, entry) {
96
+ const lock = await readLock(root);
97
+ lock[name] = entry;
98
+ await writeLock(root, lock);
99
+ return lock;
100
+ }
101
+
102
+ // src/manifest.ts
103
+ import { mkdir as mkdir2, readFile as readFile3, readdir, writeFile as writeFile2 } from "fs/promises";
104
+ import path3 from "path";
105
+ import {
106
+ isValidSectionPath,
107
+ stripManifestState
108
+ } from "@botlr/sdk";
109
+ var MANIFEST_SUFFIX = ".botlr.json";
110
+ var ManifestError = class extends Error {
111
+ constructor(message) {
112
+ super(message);
113
+ this.name = "ManifestError";
114
+ }
115
+ };
116
+ function manifestPath(sectionsDir, name) {
117
+ return path3.join(sectionsDir, `${name}${MANIFEST_SUFFIX}`);
118
+ }
119
+ async function listManifests(sectionsDir) {
120
+ let entries;
121
+ try {
122
+ entries = await readdir(sectionsDir);
123
+ } catch (error) {
124
+ if (error.code === "ENOENT") return [];
125
+ throw error;
126
+ }
127
+ return entries.filter((entry) => entry.endsWith(MANIFEST_SUFFIX)).map((entry) => entry.slice(0, -MANIFEST_SUFFIX.length)).sort();
128
+ }
129
+ async function readManifest(sectionsDir, name) {
130
+ const file = manifestPath(sectionsDir, name);
131
+ let raw;
132
+ try {
133
+ raw = await readFile3(file, "utf8");
134
+ } catch (error) {
135
+ if (error.code === "ENOENT") {
136
+ throw new ManifestError(`No manifest for '${name}': expected ${file}`);
137
+ }
138
+ throw error;
139
+ }
140
+ let parsed;
141
+ try {
142
+ parsed = JSON.parse(raw);
143
+ } catch (error) {
144
+ throw new ManifestError(`${file} is not valid JSON: ${error.message}`);
145
+ }
146
+ return validateManifest(parsed, file);
147
+ }
148
+ function validateManifest(value, where = "manifest") {
149
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
150
+ throw new ManifestError(`${where} must be a JSON object`);
151
+ }
152
+ const m = value;
153
+ if (typeof m.section !== "string" || !m.section) throw new ManifestError(`${where}: 'section' is required`);
154
+ if (typeof m.entry !== "string" || !m.entry) throw new ManifestError(`${where}: 'entry' is required`);
155
+ if (!Array.isArray(m.files) || m.files.length === 0 || !m.files.every((f) => typeof f === "string")) {
156
+ throw new ManifestError(`${where}: 'files' must list at least one path`);
157
+ }
158
+ const files = m.files;
159
+ for (const file of files) {
160
+ if (!isValidSectionPath(file)) throw new ManifestError(`${where}: invalid file path '${file}'`);
161
+ }
162
+ if (!files.includes(m.entry)) throw new ManifestError(`${where}: 'entry' (${m.entry}) must be listed in 'files'`);
163
+ if (m.framework !== void 0 && typeof m.framework !== "string") {
164
+ throw new ManifestError(`${where}: 'framework' must be a string`);
165
+ }
166
+ return m;
167
+ }
168
+ async function writeManifest(sectionsDir, name, manifest) {
169
+ const file = manifestPath(sectionsDir, name);
170
+ await mkdir2(sectionsDir, { recursive: true });
171
+ await writeFile2(file, `${JSON.stringify(stripManifestState(manifest), null, 2)}
172
+ `, "utf8");
173
+ return file;
174
+ }
175
+ async function readSectionFiles(sectionsDir, manifest) {
176
+ return Promise.all(
177
+ manifest.files.map(async (relative) => {
178
+ const file = safeJoin(sectionsDir, relative);
179
+ try {
180
+ return { path: relative, content: await readFile3(file, "utf8") };
181
+ } catch (error) {
182
+ if (error.code === "ENOENT") {
183
+ throw new ManifestError(`Missing file '${relative}' listed in ${manifest.section}${MANIFEST_SUFFIX}`);
184
+ }
185
+ throw error;
186
+ }
187
+ })
188
+ );
189
+ }
190
+ async function writeSectionFiles(sectionsDir, files) {
191
+ const written = [];
192
+ for (const file of files) {
193
+ const target = safeJoin(sectionsDir, file.path);
194
+ await mkdir2(path3.dirname(target), { recursive: true });
195
+ await writeFile2(target, file.content, "utf8");
196
+ written.push(target);
197
+ }
198
+ return written;
199
+ }
200
+ function safeJoin(dir, relative) {
201
+ if (!isValidSectionPath(relative)) throw new ManifestError(`Invalid file path '${relative}'`);
202
+ const target = path3.resolve(dir, relative);
203
+ const base = path3.resolve(dir);
204
+ if (target !== base && !target.startsWith(base + path3.sep)) {
205
+ throw new ManifestError(`File path '${relative}' escapes ${dir}`);
206
+ }
207
+ return target;
208
+ }
209
+
210
+ // src/status.ts
211
+ function computeStatus({ localHash: localHash2, lock, remote }) {
212
+ if (!lock) return "untracked";
213
+ const localChanged = localHash2 !== lock.contentHash;
214
+ const remoteChanged = remote != null && remote.contentHash !== lock.contentHash;
215
+ if (localChanged && remoteChanged) return "diverged";
216
+ if (localChanged) return "modified";
217
+ if (remoteChanged) return "behind";
218
+ return "clean";
219
+ }
220
+ function remoteForLock(summary, lock) {
221
+ if (!summary) return void 0;
222
+ if (!lock) return summary.effective === "shared" ? summary.shared : summary.instance;
223
+ return lock.scope === "shared" ? summary.shared : summary.instance;
224
+ }
225
+
226
+ // src/init.ts
227
+ import { readFile as readFile4, readdir as readdir2 } from "fs/promises";
228
+ import path4 from "path";
229
+ var REGISTRY_FILE = "publisher-section-registry.ts";
230
+ function parseRegistry(source) {
231
+ const importedFrom = /* @__PURE__ */ new Map();
232
+ const importRe = /import\s*(?:type\s*)?\{([^}]*)\}\s*from\s*["']([^"']+)["']/g;
233
+ for (const match of source.matchAll(importRe)) {
234
+ const names = match[1];
235
+ const spec = match[2];
236
+ for (const raw of names.split(",")) {
237
+ const name = raw.trim().split(/\s+as\s+/).pop()?.trim();
238
+ if (name) importedFrom.set(name, spec);
239
+ }
240
+ }
241
+ const bodyMatch = source.match(/=\s*\{([\s\S]*?)\n\}\s*;?/);
242
+ if (!bodyMatch) return [];
243
+ const entries = [];
244
+ const entryRe = /^\s*(?:"([^"]+)"|'([^']+)'|([A-Za-z0-9_$-]+))\s*:\s*([A-Za-z0-9_$]+)\s*,?\s*(?:\/\/.*)?$/gm;
245
+ for (const match of bodyMatch[1].matchAll(entryRe)) {
246
+ const slug = match[1] ?? match[2] ?? match[3];
247
+ const exportName = match[4];
248
+ const importPath = importedFrom.get(exportName);
249
+ if (!importPath || !importPath.startsWith(".")) continue;
250
+ entries.push({ slug, exportName, importPath });
251
+ }
252
+ return entries;
253
+ }
254
+ function parseDefinition(source) {
255
+ const match = source.match(/export\s+const\s+\w+Definition\s*=\s*\{([\s\S]*?)\n\}/);
256
+ if (!match) return null;
257
+ const body = match[1];
258
+ const field = (key) => {
259
+ const m = body.match(new RegExp(`(?:^|\\n)\\s*${key}\\s*:\\s*(?:"([^"]*)"|'([^']*)'|\`([^\`]*)\`)`));
260
+ return m ? m[1] ?? m[2] ?? m[3] : void 0;
261
+ };
262
+ return {
263
+ name: field("name"),
264
+ heading: field("heading"),
265
+ category: field("category"),
266
+ description: field("description"),
267
+ icon: field("icon")
268
+ };
269
+ }
270
+ function parseImports(source) {
271
+ const internal = /* @__PURE__ */ new Set();
272
+ const npm = /* @__PURE__ */ new Set();
273
+ const re = /(?:import|export)\s+(?:[^"';]*?\s+from\s+)?["']([^"']+)["']|import\s*\(\s*["']([^"']+)["']\s*\)/g;
274
+ for (const match of source.matchAll(re)) {
275
+ const spec = match[1] ?? match[2];
276
+ if (!spec) continue;
277
+ if (spec.startsWith(".") || spec.startsWith("@/") || spec.startsWith("~/")) internal.add(spec);
278
+ else npm.add(spec.startsWith("@") ? spec.split("/").slice(0, 2).join("/") : spec.split("/")[0]);
279
+ }
280
+ return { internal: [...internal].sort(), npm: [...npm].sort() };
281
+ }
282
+ var SOURCE_EXTENSIONS = [".tsx", ".ts", ".jsx", ".js"];
283
+ async function resolveImport(sectionsDir, spec) {
284
+ const base = path4.resolve(sectionsDir, spec);
285
+ for (const ext of SOURCE_EXTENSIONS) {
286
+ try {
287
+ await readFile4(base + ext);
288
+ return base + ext;
289
+ } catch {
290
+ }
291
+ }
292
+ return null;
293
+ }
294
+ async function deriveManifests(sectionsDir, options) {
295
+ const registrySource = await readFile4(path4.join(sectionsDir, REGISTRY_FILE), "utf8");
296
+ const entries = parseRegistry(registrySource);
297
+ const out = [];
298
+ for (const entry of entries) {
299
+ const entryFile = await resolveImport(sectionsDir, entry.importPath);
300
+ if (!entryFile) continue;
301
+ const source = await readFile4(entryFile, "utf8");
302
+ const meta = parseDefinition(source);
303
+ const imports = parseImports(source);
304
+ const relative = path4.relative(sectionsDir, entryFile).split(path4.sep).join("/");
305
+ const npm = {};
306
+ for (const pkg of imports.npm) {
307
+ if (pkg === "react" || pkg === "next") continue;
308
+ npm[pkg] = options.npmVersions?.[pkg] ?? "*";
309
+ }
310
+ const manifest = {
311
+ section: entry.slug,
312
+ framework: options.framework,
313
+ entry: relative,
314
+ exportName: entry.exportName,
315
+ files: [relative],
316
+ ...meta?.heading ? { title: meta.heading } : {},
317
+ ...meta?.description ? { description: meta.description } : {},
318
+ ...meta?.category ? { category: meta.category } : {},
319
+ ...meta?.icon ? { icon: meta.icon } : {},
320
+ dependencies: {
321
+ ...Object.keys(npm).length ? { npm } : {},
322
+ ...imports.internal.length ? { internal: imports.internal } : {}
323
+ }
324
+ };
325
+ out.push({ name: entry.slug, manifest, entryFile });
326
+ }
327
+ return out;
328
+ }
329
+ async function readNpmVersions(dir) {
330
+ let current = path4.resolve(dir);
331
+ for (; ; ) {
332
+ try {
333
+ const raw = await readFile4(path4.join(current, "package.json"), "utf8");
334
+ const pkg = JSON.parse(raw);
335
+ return { ...pkg.devDependencies, ...pkg.dependencies };
336
+ } catch {
337
+ const parent = path4.dirname(current);
338
+ if (parent === current) return {};
339
+ current = parent;
340
+ }
341
+ }
342
+ }
343
+ async function hasRegistry(sectionsDir) {
344
+ try {
345
+ return (await readdir2(sectionsDir)).includes(REGISTRY_FILE);
346
+ } catch {
347
+ return false;
348
+ }
349
+ }
350
+
351
+ // src/client.ts
352
+ import { createBotlrClient } from "@botlr/sdk";
353
+ function clientFor(config, options = {}) {
354
+ if (!config.apiKey && !options.needPersonalToken) {
355
+ process.stderr.write("botlr: BOTLR_API_KEY is not set, requests go out without a site token\n");
356
+ }
357
+ if (options.needPersonalToken && !config.personalToken) {
358
+ throw new ConfigError("Pushing needs BOTLR_PAT: a personal access token with the write:sections scope");
359
+ }
360
+ return createBotlrClient({
361
+ baseUrl: config.baseUrl,
362
+ appId: config.appId,
363
+ apiKey: config.apiKey,
364
+ personalToken: config.personalToken
365
+ });
366
+ }
367
+
368
+ // src/commands/pull.ts
369
+ import { contentHash } from "@botlr/sdk";
370
+ async function localHash(sectionsDir, name) {
371
+ try {
372
+ const manifest = await readManifest(sectionsDir, name);
373
+ return await contentHash(await readSectionFiles(sectionsDir, manifest));
374
+ } catch {
375
+ return null;
376
+ }
377
+ }
378
+ async function pullSections(config, options) {
379
+ const client = clientFor(config);
380
+ const scope = options.shared ? "shared" : void 0;
381
+ let names = options.names;
382
+ if (options.all) {
383
+ const listing = await client.sections.list({ framework: config.framework });
384
+ names = listing.sections.filter((s) => options.shared ? s.shared : s.instance || s.shared).map((s) => s.name);
385
+ }
386
+ if (names.length === 0) return [];
387
+ const lock = await readLock(config.root);
388
+ const results = [];
389
+ for (const name of names) {
390
+ const entry = lock[name];
391
+ if (entry && !options.force) {
392
+ const current = await localHash(config.sectionsDir, name);
393
+ if (current !== null && current !== entry.contentHash) {
394
+ results.push({ name, scope: entry.scope, version: entry.version, files: [], skipped: "modified locally, use --force to overwrite" });
395
+ continue;
396
+ }
397
+ }
398
+ const response = await client.sections.pull(name, {
399
+ framework: config.framework,
400
+ version: options.version,
401
+ scope
402
+ });
403
+ const { source } = response;
404
+ const files = await writeSectionFiles(config.sectionsDir, source.files);
405
+ await writeManifest(config.sectionsDir, name, source.manifest);
406
+ const newEntry = {
407
+ scope: source.scope,
408
+ framework: source.framework,
409
+ version: source.version,
410
+ contentHash: source.contentHash,
411
+ pulledAt: (/* @__PURE__ */ new Date()).toISOString()
412
+ };
413
+ lock[name] = newEntry;
414
+ results.push({ name, scope: source.scope, version: source.version, files });
415
+ }
416
+ await writeLock(config.root, lock);
417
+ return results;
418
+ }
419
+
420
+ // src/commands/push.ts
421
+ import {
422
+ BotlrConflictError,
423
+ contentHash as contentHash2
424
+ } from "@botlr/sdk";
425
+ var PushConflict = class extends Error {
426
+ constructor(message, latest) {
427
+ super(message);
428
+ this.latest = latest;
429
+ this.name = "PushConflict";
430
+ }
431
+ latest;
432
+ };
433
+ async function pushSection(config, options) {
434
+ const client = clientFor(config, { needPersonalToken: true });
435
+ const { name } = options;
436
+ const manifest = await readManifest(config.sectionsDir, name);
437
+ if (manifest.section !== name) {
438
+ throw new ManifestError(`${name}.botlr.json says section '${manifest.section}', expected '${name}'`);
439
+ }
440
+ if (manifest.framework && manifest.framework !== config.framework) {
441
+ throw new ManifestError(
442
+ `${name}.botlr.json is for framework '${manifest.framework}', the config says '${config.framework}'`
443
+ );
444
+ }
445
+ manifest.framework = config.framework;
446
+ const files = await readSectionFiles(config.sectionsDir, manifest);
447
+ const localHash2 = await contentHash2(files);
448
+ const targetScope = options.shared ? "shared" : "instance";
449
+ let baseVersion;
450
+ if (!options.force) {
451
+ const lock = (await readLock(config.root))[name];
452
+ if (lock && lock.scope === targetScope) {
453
+ baseVersion = lock.version;
454
+ } else {
455
+ const listing = await client.sections.list({ framework: config.framework });
456
+ const summary = listing.sections.find((s) => s.name === name);
457
+ const remote = summary ? targetScope === "shared" ? summary.shared : summary.instance : null;
458
+ baseVersion = remote?.version ?? 0;
459
+ if (remote && lock === void 0 && remote.contentHash === localHash2) {
460
+ }
461
+ }
462
+ }
463
+ let response;
464
+ try {
465
+ response = await client.sections.push(name, {
466
+ framework: config.framework,
467
+ manifest,
468
+ files,
469
+ shared: options.shared ?? false,
470
+ baseVersion,
471
+ message: options.message,
472
+ create: options.create ?? false
473
+ });
474
+ } catch (error) {
475
+ if (error instanceof BotlrConflictError) {
476
+ throw new PushConflict(error.message, error.latest);
477
+ }
478
+ throw error;
479
+ }
480
+ await updateLock(config.root, name, {
481
+ scope: response.scope,
482
+ framework: config.framework,
483
+ version: response.version,
484
+ contentHash: response.contentHash,
485
+ pulledAt: (/* @__PURE__ */ new Date()).toISOString()
486
+ });
487
+ return {
488
+ name,
489
+ created: response.created,
490
+ scope: response.scope,
491
+ version: response.version,
492
+ contentHash: response.contentHash
493
+ };
494
+ }
495
+
496
+ // src/commands/list.ts
497
+ async function listSections(config) {
498
+ const client = clientFor(config);
499
+ const listing = await client.sections.list({ framework: config.framework });
500
+ return listing.sections;
501
+ }
502
+
503
+ // src/commands/status.ts
504
+ import { contentHash as contentHash3 } from "@botlr/sdk";
505
+ async function sectionStatuses(config, options = {}) {
506
+ const names = options.names?.length ? options.names : await listManifests(config.sectionsDir);
507
+ const lock = await readLock(config.root);
508
+ let summaries = /* @__PURE__ */ new Map();
509
+ if (!options.offline) {
510
+ const listing = await clientFor(config).sections.list({ framework: config.framework });
511
+ summaries = new Map(listing.sections.map((s) => [s.name, s]));
512
+ }
513
+ const rows = [];
514
+ for (const name of names) {
515
+ const entry = lock[name];
516
+ const summary = summaries.get(name);
517
+ const remote = remoteForLock(summary, entry);
518
+ try {
519
+ const manifest = await readManifest(config.sectionsDir, name);
520
+ const localHash2 = await contentHash3(await readSectionFiles(config.sectionsDir, manifest));
521
+ rows.push({
522
+ name,
523
+ status: computeStatus({ localHash: localHash2, lock: entry, remote }),
524
+ local: entry?.version ?? null,
525
+ remote: remote?.version ?? null,
526
+ scope: entry?.scope ?? summary?.effective ?? "-"
527
+ });
528
+ } catch (error) {
529
+ rows.push({
530
+ name,
531
+ status: "error",
532
+ local: entry?.version ?? null,
533
+ remote: remote?.version ?? null,
534
+ scope: entry?.scope ?? "-",
535
+ detail: error.message
536
+ });
537
+ }
538
+ }
539
+ return rows;
540
+ }
541
+
542
+ // src/commands/init.ts
543
+ async function initSections(config, options = {}) {
544
+ if (!await hasRegistry(config.sectionsDir)) {
545
+ throw new Error(`No ${REGISTRY_FILE} in ${config.sectionsDir}; sections init reads the registry map to find components`);
546
+ }
547
+ const existing = new Set(await listManifests(config.sectionsDir));
548
+ const npmVersions = await readNpmVersions(config.sectionsDir);
549
+ const derived = await deriveManifests(config.sectionsDir, { framework: config.framework, npmVersions });
550
+ const results = [];
551
+ for (const { name, manifest, entryFile } of derived) {
552
+ const skip = existing.has(name) && !options.force;
553
+ let file = `${name}.botlr.json`;
554
+ if (!skip && !options.dryRun) {
555
+ file = await writeManifest(config.sectionsDir, name, manifest);
556
+ }
557
+ results.push({ name, file, entry: entryFile, written: !skip && !options.dryRun });
558
+ }
559
+ return results;
560
+ }
561
+ export {
562
+ CONFIG_FILE,
563
+ ConfigError,
564
+ DEFAULT_FRAMEWORK,
565
+ DEFAULT_SECTIONS_DIR,
566
+ LOCK_DIR,
567
+ LOCK_FILE,
568
+ MANIFEST_SUFFIX,
569
+ ManifestError,
570
+ REGISTRY_FILE,
571
+ clientFor,
572
+ computeStatus,
573
+ deriveManifests,
574
+ findConfigFile,
575
+ hasRegistry,
576
+ initSections,
577
+ listManifests,
578
+ listSections,
579
+ loadConfig,
580
+ lockPath,
581
+ manifestPath,
582
+ parseDefinition,
583
+ parseImports,
584
+ parseRegistry,
585
+ pullSections,
586
+ pushSection,
587
+ readConfigFile,
588
+ readLock,
589
+ readManifest,
590
+ readNpmVersions,
591
+ readSectionFiles,
592
+ remoteForLock,
593
+ safeJoin,
594
+ sectionStatuses,
595
+ updateLock,
596
+ validateManifest,
597
+ writeLock,
598
+ writeManifest,
599
+ writeSectionFiles
600
+ };
601
+ //# sourceMappingURL=index.js.map