@zpress/core 0.2.0 → 0.3.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 Joggr
3
+ Copyright (c) 2026 Joggr, Inc.
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md ADDED
@@ -0,0 +1,63 @@
1
+ <div align="center">
2
+ <img src="https://raw.githubusercontent.com/joggrdocs/zpress/main/assets/banner.svg" alt="zpress" width="90%" />
3
+ <p><strong>Config loading, sync engine, and asset utilities for zpress.</strong></p>
4
+
5
+ <a href="https://github.com/joggrdocs/zpress/actions/workflows/ci.yml"><img src="https://github.com/joggrdocs/zpress/actions/workflows/ci.yml/badge.svg?branch=main" alt="CI" /></a>
6
+ <a href="https://www.npmjs.com/package/@zpress/core"><img src="https://img.shields.io/npm/v/@zpress/core" alt="npm version" /></a>
7
+ <a href="https://github.com/joggrdocs/zpress/blob/main/LICENSE"><img src="https://img.shields.io/github/license/joggrdocs/zpress" alt="License" /></a>
8
+
9
+ </div>
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ npm install @zpress/core
15
+ ```
16
+
17
+ ## API
18
+
19
+ ### Config
20
+
21
+ | Export | Description |
22
+ | -------------- | ----------------------------------- |
23
+ | `defineConfig` | Type-safe config factory |
24
+ | `loadConfig` | Load and validate `zpress.config.*` |
25
+
26
+ ### Sync Engine
27
+
28
+ | Export | Description |
29
+ | ---------------- | ------------------------------------- |
30
+ | `sync` | Run the full sync pipeline |
31
+ | `resolveEntries` | Resolve glob patterns to page entries |
32
+ | `loadManifest` | Load a previously written manifest |
33
+
34
+ ### Assets
35
+
36
+ | Export | Description |
37
+ | ------------------- | ------------------------ |
38
+ | `generateAssets` | Generate all asset files |
39
+ | `generateBannerSvg` | Generate banner SVG |
40
+ | `generateIconSvg` | Generate icon SVG |
41
+ | `generateLogoSvg` | Generate logo SVG |
42
+
43
+ ### Utilities
44
+
45
+ | Export | Description |
46
+ | -------------- | --------------------------- |
47
+ | `createPaths` | Build resolved path helpers |
48
+ | `hasGlobChars` | Check if a string has globs |
49
+
50
+ ## Usage
51
+
52
+ ```ts
53
+ import { defineConfig, loadConfig, sync } from '@zpress/core'
54
+
55
+ const config = defineConfig({
56
+ title: 'my-project',
57
+ sections: [{ text: 'Guide', from: 'docs/*.md' }],
58
+ })
59
+ ```
60
+
61
+ ## License
62
+
63
+ [MIT](https://github.com/joggrdocs/zpress/blob/main/LICENSE) - Joggr, Inc.
@@ -0,0 +1,967 @@
1
+ /**
2
+ * Input configuration for asset generation, extracted from ZpressConfig.
3
+ */
4
+ export declare interface AssetConfig {
5
+ readonly title: string;
6
+ readonly tagline: string | undefined;
7
+ }
8
+
9
+ /**
10
+ * Error produced during asset generation or file writing.
11
+ */
12
+ export declare interface AssetError {
13
+ readonly _tag: 'AssetError';
14
+ readonly type: 'empty_title' | 'write_failed';
15
+ readonly message: string;
16
+ }
17
+
18
+ /**
19
+ * Convenience alias for asset operation results.
20
+ */
21
+ export declare type AssetResult<T> = Result<T, AssetError>;
22
+
23
+ /**
24
+ * Controls how an entry appears as a card on its parent section's
25
+ * auto-generated landing page.
26
+ *
27
+ * When present, the landing page uses workspace-style cards
28
+ * (icon + scope + name + description + tags + optional badge).
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * card: {
33
+ * icon: 'devicon:hono',
34
+ * iconColor: 'api',
35
+ * scope: 'apps/',
36
+ * description: 'Hono REST API with RPC-typed routes',
37
+ * tags: ['Hono', 'REST', 'Serverless'],
38
+ * badge: { src: '/logos/vercel.svg', alt: 'Vercel' },
39
+ * }
40
+ * ```
41
+ */
42
+ export declare interface CardConfig {
43
+ /**
44
+ * Iconify identifier for the card icon (e.g. 'devicon:hono').
45
+ */
46
+ icon?: string;
47
+ /**
48
+ * CSS class suffix for the icon color (maps to `.workspace-icon--{color}`).
49
+ */
50
+ iconColor?: string;
51
+ /**
52
+ * Scope label shown above the name (e.g. `"apps/"`).
53
+ */
54
+ scope?: string;
55
+ /**
56
+ * Short description shown on the card. Overrides auto-extracted description.
57
+ */
58
+ description?: string;
59
+ /**
60
+ * Technology tags shown at the bottom of the card.
61
+ */
62
+ tags?: string[];
63
+ /**
64
+ * Deploy badge image shown in the card header.
65
+ */
66
+ badge?: {
67
+ src: string;
68
+ alt: string;
69
+ };
70
+ }
71
+
72
+ /**
73
+ * Error produced during config validation in `defineConfig`.
74
+ */
75
+ export declare interface ConfigError {
76
+ readonly _tag: 'ConfigError';
77
+ readonly type: 'empty_sections' | 'missing_field' | 'duplicate_prefix' | 'invalid_icon' | 'invalid_entry';
78
+ readonly message: string;
79
+ }
80
+
81
+ /**
82
+ * Create a `ConfigError` value.
83
+ *
84
+ * @param type - Error classification
85
+ * @param message - Human-readable description
86
+ * @returns A frozen `ConfigError` object
87
+ */
88
+ export declare function configError(type: ConfigError['type'], message: string): ConfigError;
89
+
90
+ /**
91
+ * Convenience alias for config validation results.
92
+ */
93
+ export declare type ConfigResult<T> = readonly [ConfigError, null] | readonly [null, T];
94
+
95
+ /**
96
+ * Create all derived project paths from a resolved directory.
97
+ */
98
+ export declare function createPaths(dir: string): Paths;
99
+
100
+ /**
101
+ * Type-safe config helper with validation.
102
+ *
103
+ * Validates the config structure and exits with a clear error message
104
+ * if any issues are found. This is the primary entry point for user-
105
+ * provided config — validation happens at the boundary.
106
+ *
107
+ * `defineConfig` is called in user config files (e.g. `zpress.config.ts`)
108
+ * where the return value is consumed by the c12 config loader, which
109
+ * expects a plain `ZpressConfig` object — not a `Result` tuple.
110
+ * Because this is the outermost user-facing boundary (not a library
111
+ * function), `process.exit(1)` is acceptable here: the user must fix
112
+ * their config before any downstream code can run.
113
+ *
114
+ * @param config - Raw zpress config object
115
+ * @returns The validated config (unchanged)
116
+ */
117
+ export declare function defineConfig(config: ZpressConfig): ZpressConfig;
118
+
119
+ /**
120
+ * A single node in the information architecture.
121
+ *
122
+ * What you provide determines what it is:
123
+ *
124
+ * **Page — explicit file**
125
+ * ```ts
126
+ * { text: 'Architecture', link: '/architecture', from: 'docs/architecture.md' }
127
+ * ```
128
+ *
129
+ * **Page — inline/generated content**
130
+ * ```ts
131
+ * { text: 'Overview', link: '/api/overview', content: '# API Overview\n...' }
132
+ * ```
133
+ *
134
+ * **Section — explicit children**
135
+ * ```ts
136
+ * { text: 'Guides', items: [ ... ] }
137
+ * ```
138
+ *
139
+ * **Section — auto-discovered from glob**
140
+ * ```ts
141
+ * { text: 'Guides', prefix: '/guides', from: 'docs/guides/*.md' }
142
+ * ```
143
+ *
144
+ * **Section — mix of explicit + auto-discovered**
145
+ * ```ts
146
+ * {
147
+ * text: 'Guides',
148
+ * prefix: '/guides',
149
+ * from: 'docs/guides/*.md',
150
+ * items: [
151
+ * { text: 'Getting Started', link: '/guides/start', from: 'docs/intro.md' },
152
+ * ],
153
+ * }
154
+ * ```
155
+ */
156
+ export declare interface Entry {
157
+ /**
158
+ * Display text in sidebar and nav.
159
+ */
160
+ readonly text: string;
161
+ /**
162
+ * Output URL path.
163
+ * - Pages: exact URL (e.g. `"/guides/add-api-route"`)
164
+ * - Sections: optional — makes the section header clickable
165
+ */
166
+ readonly link?: UrlPath;
167
+ /**
168
+ * Content source — file path or glob, relative to repo root.
169
+ *
170
+ * - **No wildcards** → single file (e.g. `"docs/architecture.md"`)
171
+ * - **With wildcards** → auto-discover children (e.g. `"docs/guides/*.md"`)
172
+ */
173
+ readonly from?: FilePath | GlobPattern;
174
+ /**
175
+ * URL prefix for auto-discovered children.
176
+ * Used with glob `from` — each discovered file gets `prefix + "/" + slug`.
177
+ *
178
+ * @example
179
+ * `prefix: "/guides"` + file `add-api-route.md` → `/guides/add-api-route`
180
+ */
181
+ readonly prefix?: UrlPath;
182
+ /**
183
+ * Inline markdown or async content generator.
184
+ * For virtual pages that have no source `.md` file.
185
+ * Mutually exclusive with `from`.
186
+ */
187
+ readonly content?: string | (() => string | Promise<string>);
188
+ /**
189
+ * Child entries — pages and/or sub-sections.
190
+ */
191
+ readonly items?: readonly Entry[];
192
+ /**
193
+ * Make this section collapsible in the sidebar.
194
+ * Sections at depth > 1 are collapsible by default.
195
+ * Set to `false` to keep a deep section always-open.
196
+ */
197
+ readonly collapsible?: boolean;
198
+ /**
199
+ * Exclude globs, scoped to this entry's `from` glob.
200
+ */
201
+ readonly exclude?: readonly GlobPattern[];
202
+ /**
203
+ * Hide from sidebar. Page is still built and routable.
204
+ * Useful for pages that should exist but not clutter navigation.
205
+ */
206
+ readonly hidden?: boolean;
207
+ /**
208
+ * Frontmatter injected at build time.
209
+ * - On a page: applied to that page.
210
+ * - On a section: applied to all pages within.
211
+ */
212
+ readonly frontmatter?: Frontmatter;
213
+ /**
214
+ * How to derive `text` for auto-discovered children.
215
+ * - `"filename"` — kebab-to-title from filename (default)
216
+ * - `"heading"` — first `# heading` in the file
217
+ * - `"frontmatter"` — `title` field from YAML frontmatter, falls back to heading
218
+ */
219
+ readonly textFrom?: 'filename' | 'heading' | 'frontmatter';
220
+ /**
221
+ * Transform function applied to auto-derived text (from `textFrom`).
222
+ * Called after text derivation for glob-discovered and recursive children.
223
+ * Does NOT apply to entries with explicit `text` (those are already user-controlled).
224
+ *
225
+ * @param text - The derived text (from heading or filename)
226
+ * @param slug - The filename slug (without extension)
227
+ * @returns Transformed text for sidebar display
228
+ */
229
+ readonly textTransform?: (text: string, slug: string) => string;
230
+ /**
231
+ * Sort order for auto-discovered children.
232
+ * - `"alpha"` — alphabetical by derived text (default)
233
+ * - `"filename"` — alphabetical by filename
234
+ * - Custom comparator function
235
+ */
236
+ readonly sort?: 'alpha' | 'filename' | ((a: ResolvedPage, b: ResolvedPage) => number);
237
+ /**
238
+ * Enable recursive directory-based nesting for glob patterns.
239
+ * When true, directory structure under the glob base drives sidebar nesting:
240
+ * - The `indexFile` (default `"overview"`) in a directory becomes the section header page
241
+ * - Other `.md` files become children
242
+ * - Sub-directories become nested collapsible sections
243
+ *
244
+ * Requires `from` with a recursive glob (e.g. `"docs/**\/*.md"`) and `prefix`.
245
+ * @default false
246
+ */
247
+ readonly recursive?: boolean;
248
+ /**
249
+ * Filename (without extension) used as the section header page in each directory.
250
+ * Only meaningful when `recursive` is true.
251
+ * @default "overview"
252
+ */
253
+ readonly indexFile?: string;
254
+ /**
255
+ * Card display metadata for the parent section's auto-generated landing page.
256
+ *
257
+ * When present on child entries, the parent's landing page uses
258
+ * workspace-style cards instead of the default simple cards.
259
+ */
260
+ readonly card?: CardConfig;
261
+ /**
262
+ * Isolate this section into its own Rspress sidebar namespace.
263
+ *
264
+ * When `true`, the section's children appear under a dedicated sidebar
265
+ * keyed by `link` (e.g. `"/apps/"`) instead of the root `"/"` sidebar.
266
+ * This mirrors how the OpenAPI reference already works.
267
+ *
268
+ * Requires `link` to be set.
269
+ * @default false
270
+ */
271
+ readonly isolated?: boolean;
272
+ }
273
+
274
+ /**
275
+ * Explicit feature card for the home page.
276
+ *
277
+ * When `features` is provided on the config, these replace the
278
+ * auto-generated feature cards that are normally derived from
279
+ * top-level sections.
280
+ *
281
+ * @example
282
+ * ```ts
283
+ * {
284
+ * text: 'Getting Started',
285
+ * description: 'Everything you need to set up and start building.',
286
+ * link: '/getting-started',
287
+ * icon: 'pixelarticons:speed-fast',
288
+ * }
289
+ * ```
290
+ */
291
+ export declare interface Feature {
292
+ /**
293
+ * Display title for the feature card.
294
+ */
295
+ readonly text: string;
296
+ /**
297
+ * Short description shown below the title.
298
+ */
299
+ readonly description: string;
300
+ /**
301
+ * Link target when the card is clicked.
302
+ */
303
+ readonly link?: string;
304
+ /**
305
+ * Iconify icon identifier (e.g. `"pixelarticons:speed-fast"`).
306
+ */
307
+ readonly icon?: string;
308
+ }
309
+
310
+ /**
311
+ * Relative file path from repo root (e.g. `"docs/guides/add-api-route.md"`)
312
+ */
313
+ declare type FilePath = string;
314
+
315
+ /**
316
+ * Rspress frontmatter fields injectable at build time.
317
+ */
318
+ export declare interface Frontmatter {
319
+ readonly title?: string;
320
+ readonly titleTemplate?: string | boolean;
321
+ readonly description?: string;
322
+ readonly layout?: 'doc' | 'page' | 'home' | (string & {});
323
+ readonly sidebar?: boolean;
324
+ readonly aside?: boolean | 'left';
325
+ readonly outline?: false | number | [number, number] | 'deep';
326
+ readonly navbar?: boolean;
327
+ readonly editLink?: boolean;
328
+ readonly lastUpdated?: boolean;
329
+ readonly footer?: boolean;
330
+ readonly pageClass?: string;
331
+ readonly head?: readonly [string, Record<string, string>][];
332
+ /**
333
+ * Arbitrary extra fields merged into frontmatter.
334
+ */
335
+ readonly [key: string]: unknown;
336
+ }
337
+
338
+ /**
339
+ * Generate banner, logo, and icon SVGs, writing them to the public directory.
340
+ *
341
+ * For each asset:
342
+ * 1. If the file is missing or has the zpress-generated marker → write it
343
+ * 2. If the file exists without the marker → skip (user-customized)
344
+ *
345
+ * @param params - Config and target directory
346
+ * @returns Result containing the list of filenames written, or an error
347
+ */
348
+ export declare function generateAssets(params: GenerateAssetsParams): Promise<AssetResult<readonly string[]>>;
349
+
350
+ declare interface GenerateAssetsParams {
351
+ readonly config: AssetConfig;
352
+ readonly publicDir: string;
353
+ }
354
+
355
+ /**
356
+ * Generate a banner SVG from the project config.
357
+ *
358
+ * @param config - Title and optional tagline
359
+ * @returns Result containing the generated asset or an error
360
+ */
361
+ export declare function generateBannerSvg(config: AssetConfig): AssetResult<GeneratedAsset>;
362
+
363
+ /**
364
+ * A generated SVG asset ready to be written to disk.
365
+ */
366
+ export declare interface GeneratedAsset {
367
+ readonly filename: string;
368
+ readonly content: string;
369
+ }
370
+
371
+ /**
372
+ * Generate an icon (favicon) SVG from the project config.
373
+ *
374
+ * @param config - Title (first character is used for the icon glyph)
375
+ * @returns Result containing the generated asset or an error
376
+ */
377
+ export declare function generateIconSvg(config: AssetConfig): AssetResult<GeneratedAsset>;
378
+
379
+ /**
380
+ * Generate a logo SVG from the project config.
381
+ *
382
+ * @param config - Title (tagline is ignored for logos)
383
+ * @returns Result containing the generated asset or an error
384
+ */
385
+ export declare function generateLogoSvg(config: AssetConfig): AssetResult<GeneratedAsset>;
386
+
387
+ /**
388
+ * Glob pattern (e.g. `"docs/guides/*.md"`)
389
+ */
390
+ declare type GlobPattern = string;
391
+
392
+ /**
393
+ * Returns true if the string contains glob metacharacters.
394
+ */
395
+ export declare function hasGlobChars(s: string): boolean;
396
+
397
+ /**
398
+ * Load zpress config at runtime via c12.
399
+ *
400
+ * Returns a `ConfigResult` tuple instead of calling `process.exit` — the
401
+ * CLI boundary is responsible for surfacing the error and exiting.
402
+ */
403
+ export declare function loadConfig(dir: string): Promise<ConfigResult<ZpressConfig>>;
404
+
405
+ /**
406
+ * Load the previous sync manifest from disk.
407
+ *
408
+ * @param outDir - Absolute path to the output directory
409
+ * @returns Parsed manifest, or `null` if no manifest exists
410
+ */
411
+ export declare function loadManifest(outDir: string): Promise<Manifest | null>;
412
+
413
+ /**
414
+ * Tracks output files for incremental sync and stale file cleanup.
415
+ */
416
+ export declare interface Manifest {
417
+ /**
418
+ * Map of output relative path to entry metadata.
419
+ *
420
+ * @remarks Mutable — entries are accumulated during the sync pass as pages
421
+ * are copied. The manifest is built incrementally and then persisted to disk.
422
+ * Will be refactored to an immutable accumulator pattern alongside `SyncContext.manifest`.
423
+ */
424
+ files: Record<string, ManifestEntry>;
425
+ /**
426
+ * Timestamp of last sync.
427
+ */
428
+ readonly timestamp: number;
429
+ }
430
+
431
+ /**
432
+ * Metadata for a single output file tracked in the manifest.
433
+ */
434
+ export declare interface ManifestEntry {
435
+ /**
436
+ * Repo-relative path to source file (undefined for virtual pages).
437
+ */
438
+ readonly source?: string;
439
+ /**
440
+ * Source file mtime in ms (for quick-check).
441
+ */
442
+ readonly sourceMtime?: number;
443
+ /**
444
+ * SHA-256 hex of the written output.
445
+ */
446
+ readonly contentHash: string;
447
+ /**
448
+ * Output path relative to .content/.
449
+ */
450
+ readonly outputPath: string;
451
+ }
452
+
453
+ export declare interface NavItem {
454
+ readonly text: string;
455
+ readonly link?: UrlPath;
456
+ readonly items?: readonly NavItem[];
457
+ readonly activeMatch?: string;
458
+ }
459
+
460
+ /**
461
+ * Configuration for OpenAPI spec integration.
462
+ */
463
+ export declare interface OpenAPIConfig {
464
+ /**
465
+ * Path to openapi.json relative to repo root.
466
+ */
467
+ spec: FilePath;
468
+ /**
469
+ * URL prefix for API operation pages (e.g., '/api').
470
+ */
471
+ prefix: UrlPath;
472
+ /**
473
+ * Sidebar group title.
474
+ * @default 'API Reference'
475
+ */
476
+ title?: string;
477
+ }
478
+
479
+ /**
480
+ * Data for a single page to be written to the output directory.
481
+ */
482
+ export declare interface PageData {
483
+ /**
484
+ * Absolute path to source .md (undefined for virtual pages).
485
+ */
486
+ readonly source?: string;
487
+ /**
488
+ * Inline content for virtual pages.
489
+ */
490
+ readonly content?: string | (() => string | Promise<string>);
491
+ /**
492
+ * Relative path inside .content/ (e.g. "guides/add-api-route.md").
493
+ */
494
+ readonly outputPath: string;
495
+ /**
496
+ * Merged frontmatter to inject.
497
+ */
498
+ readonly frontmatter: Frontmatter;
499
+ }
500
+
501
+ /**
502
+ * All user-project paths derived from a single root directory.
503
+ */
504
+ export declare interface Paths {
505
+ readonly repoRoot: string;
506
+ readonly outputRoot: string;
507
+ readonly contentDir: string;
508
+ readonly publicDir: string;
509
+ readonly distDir: string;
510
+ readonly cacheDir: string;
511
+ }
512
+
513
+ /**
514
+ * Internal resolved node — produced by the resolver, consumed by copy + sidebar/nav generators.
515
+ */
516
+ export declare interface ResolvedEntry {
517
+ readonly text: string;
518
+ readonly link?: string;
519
+ readonly collapsible?: boolean;
520
+ readonly hidden?: boolean;
521
+ /**
522
+ * @remarks Mutable — `injectLandingPages` may reassign items when
523
+ * promoting an overview child to section page. Will be refactored
524
+ * to immutable tree rebuild.
525
+ */
526
+ items?: readonly ResolvedEntry[];
527
+ /**
528
+ * Present on leaf pages (and section headers that are also pages).
529
+ *
530
+ * @remarks Mutable — `injectLandingPages` assigns virtual pages to sections
531
+ * that have a link but no page. Will be refactored to immutable tree rebuild
532
+ * when landing pages move to Vue components.
533
+ */
534
+ page?: PageData;
535
+ readonly card?: CardConfig;
536
+ /**
537
+ * When true, this section gets its own sidebar namespace keyed by `link`.
538
+ */
539
+ readonly isolated?: boolean;
540
+ }
541
+
542
+ /**
543
+ * A fully resolved page after the sync engine processes the config.
544
+ */
545
+ export declare interface ResolvedPage {
546
+ /**
547
+ * Display text.
548
+ */
549
+ readonly text: string;
550
+ /**
551
+ * Output URL path.
552
+ */
553
+ readonly link: UrlPath;
554
+ /**
555
+ * Source file path (undefined for virtual pages).
556
+ */
557
+ readonly source?: FilePath;
558
+ /**
559
+ * Merged frontmatter.
560
+ */
561
+ readonly frontmatter: Frontmatter;
562
+ }
563
+
564
+ /**
565
+ * A fully resolved section.
566
+ */
567
+ export declare interface ResolvedSection {
568
+ readonly text: string;
569
+ readonly link?: UrlPath;
570
+ readonly collapsible?: boolean;
571
+ readonly items: readonly (ResolvedPage | ResolvedSection)[];
572
+ }
573
+
574
+ /**
575
+ * Walk the Entry tree and produce a ResolvedEntry tree.
576
+ *
577
+ * Resolves globs, derives text, merges frontmatter, deduplicates.
578
+ * Returns a `SyncOutcome` tuple — the caller is responsible for
579
+ * surfacing errors and exiting.
580
+ *
581
+ * @param entries - Config entry tree to resolve
582
+ * @param ctx - Sync context (provides repo root, config, quiet flag)
583
+ * @param inheritedFrontmatter - Frontmatter inherited from parent entries
584
+ * @param depth - Current nesting depth (0 = top-level)
585
+ * @returns Result tuple containing resolved entry tree or the first sync error
586
+ */
587
+ export declare function resolveEntries(entries: readonly Entry[], ctx: SyncContext, inheritedFrontmatter?: Frontmatter, depth?: number): Promise<readonly [SyncError, null] | readonly [null, ResolvedEntry[]]>;
588
+
589
+ /**
590
+ * zpress — unified information architecture config.
591
+ *
592
+ * The IA tree IS the config. Each node defines what it is, where its
593
+ * content comes from, and where it sits in the sidebar — all in one place.
594
+ * Source `.md` files are never edited.
595
+ *
596
+ * @example
597
+ * ```ts
598
+ * import { defineConfig } from '@zpress/core'
599
+ *
600
+ * export default defineConfig({
601
+ * title: 'My Docs',
602
+ * sections: [
603
+ * {
604
+ * text: 'Introduction',
605
+ * items: [
606
+ * { text: 'Architecture', link: '/architecture', from: 'docs/architecture.md' },
607
+ * { text: 'Structure', link: '/structure', from: 'docs/structure.md' },
608
+ * ],
609
+ * },
610
+ * {
611
+ * text: 'Guides',
612
+ * prefix: '/guides',
613
+ * from: 'docs/guides/*.md',
614
+ * },
615
+ * {
616
+ * text: 'API Reference',
617
+ * items: [
618
+ * { text: 'Overview', link: '/api/overview', content: '# API\n...' },
619
+ * { text: 'Routes', link: '/api/routes', from: 'apps/api/docs/routes.md' },
620
+ * ],
621
+ * },
622
+ * ],
623
+ * })
624
+ * ```
625
+ */
626
+ /**
627
+ * Result type for error handling without exceptions.
628
+ *
629
+ * Success: `[null, value]`
630
+ * Failure: `[error, null]`
631
+ *
632
+ * @example
633
+ * ```ts
634
+ * const [error, value] = loadConfig(path)
635
+ * if (error) return [error, null]
636
+ * ```
637
+ */
638
+ export declare type Result<T, E = Error> = readonly [E, null] | readonly [null, T];
639
+
640
+ /**
641
+ * Rspress sidebar item shape.
642
+ *
643
+ * Constructed immutably by sidebar generators, then serialized to JSON.
644
+ */
645
+ export declare interface SidebarItem {
646
+ readonly text: string;
647
+ readonly link?: string;
648
+ /**
649
+ * Rspress `collapsed` — set by sidebar generator from `collapsible`.
650
+ */
651
+ readonly collapsed?: boolean;
652
+ readonly items?: readonly SidebarItem[];
653
+ }
654
+
655
+ /**
656
+ * Mapping from repo-relative source path to content-relative output path.
657
+ */
658
+ declare type SourceMap = ReadonlyMap<string, string>;
659
+
660
+ export declare function sync(config: ZpressConfig, options: SyncOptions): Promise<SyncResult>;
661
+
662
+ /**
663
+ * Context threaded through all sync operations.
664
+ */
665
+ export declare interface SyncContext {
666
+ /**
667
+ * Absolute path to repo root.
668
+ */
669
+ readonly repoRoot: string;
670
+ /**
671
+ * Absolute path to .content/ output directory.
672
+ */
673
+ readonly outDir: string;
674
+ /**
675
+ * Resolved config.
676
+ */
677
+ readonly config: ZpressConfig;
678
+ /**
679
+ * Previous manifest (for incremental sync).
680
+ */
681
+ readonly previousManifest: Manifest | null;
682
+ /**
683
+ * Current manifest being built.
684
+ *
685
+ * @remarks Mutable during the sync pass — entries are added as pages are copied.
686
+ * Will be refactored to an immutable accumulator pattern in a future pass.
687
+ */
688
+ manifest: Manifest;
689
+ /**
690
+ * When true, suppress all log output during sync.
691
+ */
692
+ readonly quiet: boolean;
693
+ /**
694
+ * Mapping from repo-relative source paths to content-relative output paths.
695
+ * Used by the copy step to rewrite relative markdown links.
696
+ */
697
+ readonly sourceMap?: SourceMap;
698
+ }
699
+
700
+ /**
701
+ * Domain-specific error types for the sync engine.
702
+ *
703
+ * All sync operations return `Result<T, SyncError>` instead of throwing.
704
+ * Config validation returns `Result<T, ConfigError>`.
705
+ */
706
+ /**
707
+ * Error produced by the sync engine during entry resolution, page copy, or sidebar generation.
708
+ */
709
+ export declare interface SyncError {
710
+ readonly _tag: 'SyncError';
711
+ readonly type: 'missing_from' | 'missing_link' | 'file_not_found' | 'missing_content' | 'internal';
712
+ readonly message: string;
713
+ }
714
+
715
+ /**
716
+ * Create a `SyncError` value.
717
+ *
718
+ * @param type - Error classification
719
+ * @param message - Human-readable description
720
+ * @returns A frozen `SyncError` object
721
+ */
722
+ export declare function syncError(type: SyncError['type'], message: string): SyncError;
723
+
724
+ export declare interface SyncOptions {
725
+ /**
726
+ * Resolved project paths.
727
+ */
728
+ readonly paths: Paths;
729
+ /**
730
+ * When true, suppress all log output during sync.
731
+ */
732
+ readonly quiet?: boolean;
733
+ }
734
+
735
+ /**
736
+ * Convenience alias for sync operation results.
737
+ *
738
+ * Named `SyncOutcome` to avoid collision with the `SyncResult` interface
739
+ * in `sync/index.ts` (which represents the aggregate return value of a full sync pass).
740
+ */
741
+ export declare type SyncOutcome<T> = readonly [SyncError, null] | readonly [null, T];
742
+
743
+ export declare interface SyncResult {
744
+ readonly pagesWritten: number;
745
+ readonly pagesSkipped: number;
746
+ readonly pagesRemoved: number;
747
+ readonly elapsed: number;
748
+ }
749
+
750
+ /**
751
+ * URL path (e.g. `"/guides/add-api-route"`)
752
+ */
753
+ declare type UrlPath = string;
754
+
755
+ /**
756
+ * A named group of workspace items for custom workspace categories.
757
+ *
758
+ * Lets users define arbitrary groups beyond the built-in `apps` and `packages`
759
+ * (e.g. "Services", "Tools", "Integrations") that receive the same
760
+ * card/landing-page treatment.
761
+ *
762
+ * @example
763
+ * ```ts
764
+ * {
765
+ * name: 'Integrations',
766
+ * description: 'Third-party service connectors',
767
+ * icon: 'pixelarticons:integration',
768
+ * items: [
769
+ * { text: 'Stripe', description: 'Payment processing', docsPrefix: '/integrations/stripe' },
770
+ * ],
771
+ * }
772
+ * ```
773
+ */
774
+ export declare interface WorkspaceGroup {
775
+ readonly name: string;
776
+ readonly description: string;
777
+ readonly icon: string;
778
+ readonly items: readonly WorkspaceItem[];
779
+ /**
780
+ * URL prefix override for the group's landing page.
781
+ * Defaults to `/${slugify(name)}` when omitted.
782
+ */
783
+ readonly link?: string;
784
+ }
785
+
786
+ /**
787
+ * A workspace item representing an app or package in the monorepo.
788
+ *
789
+ * Used as the single source of truth for workspace metadata — home page cards,
790
+ * landing page cards, and introduction bullets all derive from these arrays.
791
+ *
792
+ * @example
793
+ * ```ts
794
+ * {
795
+ * text: 'API',
796
+ * icon: 'devicon:hono',
797
+ * iconColor: 'api',
798
+ * description: 'Hono REST API serving all client applications with RPC-typed routes',
799
+ * tags: ['hono', 'react', 'vercel'],
800
+ * badge: { src: '/logos/vercel.svg', alt: 'Vercel' },
801
+ * docsPrefix: '/apps/api',
802
+ * }
803
+ * ```
804
+ */
805
+ export declare interface WorkspaceItem {
806
+ /**
807
+ * Display name (e.g. "API", "Console", "AI").
808
+ */
809
+ readonly text: string;
810
+ /**
811
+ * Main icon — Iconify identifier (e.g. "devicon:hono").
812
+ * Falls back to a default app or package icon when omitted.
813
+ */
814
+ readonly icon?: string;
815
+ /**
816
+ * CSS class suffix for icon color (maps to `.workspace-icon--{color}`).
817
+ */
818
+ readonly iconColor?: string;
819
+ /**
820
+ * Short description for cards and bullet lists.
821
+ */
822
+ readonly description: string;
823
+ /**
824
+ * Technology tags — kebab-case keys resolved by the UI TechTag component.
825
+ * Each tag maps to an Iconify icon and display label.
826
+ */
827
+ readonly tags?: readonly string[];
828
+ /**
829
+ * Deploy badge image for the card header.
830
+ */
831
+ readonly badge?: {
832
+ readonly src: string;
833
+ readonly alt: string;
834
+ };
835
+ /**
836
+ * Docs path prefix (e.g. "/apps/api"). Matches section entries and derives card links.
837
+ * Also used as the URL prefix for glob-discovered children.
838
+ */
839
+ readonly docsPrefix: string;
840
+ /**
841
+ * Content source — file path or glob, **relative to the workspace item's
842
+ * base path** (derived from `docsPrefix`).
843
+ *
844
+ * - `docsPrefix: "/apps/api"` + `from: "docs/*.md"` → resolves to `apps/api/docs/*.md`
845
+ * - **No wildcards** → single file (e.g. `"docs/overview.md"`)
846
+ * - **With wildcards** → auto-discover children (e.g. `"docs/*.md"`)
847
+ *
848
+ * @default "docs/*.md"
849
+ */
850
+ readonly from?: string;
851
+ /**
852
+ * Explicit child entries for this workspace item.
853
+ * Can be combined with `from` — explicit children override glob-discovered pages.
854
+ */
855
+ readonly items?: readonly Entry[];
856
+ /**
857
+ * Sort order for auto-discovered children.
858
+ * - `"alpha"` — alphabetical by derived text (default)
859
+ * - `"filename"` — alphabetical by filename
860
+ * - Custom comparator function
861
+ */
862
+ readonly sort?: Entry['sort'];
863
+ /**
864
+ * How to derive `text` for auto-discovered children.
865
+ * - `"filename"` — kebab-to-title from filename (default)
866
+ * - `"heading"` — first `# heading` in the file
867
+ * - `"frontmatter"` — `title` field from YAML frontmatter, falls back to heading
868
+ */
869
+ readonly textFrom?: Entry['textFrom'];
870
+ /**
871
+ * Transform function applied to auto-derived text (from `textFrom`).
872
+ */
873
+ readonly textTransform?: Entry['textTransform'];
874
+ /**
875
+ * Enable recursive directory-based nesting for glob patterns.
876
+ * Requires `from` with a recursive glob (e.g. `"apps/api/docs/**\/*.md"`).
877
+ * @default false
878
+ */
879
+ readonly recursive?: boolean;
880
+ /**
881
+ * Filename (without extension) used as the section header page in each directory.
882
+ * Only meaningful when `recursive` is true.
883
+ * @default "overview"
884
+ */
885
+ readonly indexFile?: string;
886
+ /**
887
+ * Exclude globs, scoped to this item's `from` glob.
888
+ */
889
+ readonly exclude?: readonly string[];
890
+ /**
891
+ * Make this item's section collapsible in the sidebar.
892
+ */
893
+ readonly collapsible?: boolean;
894
+ /**
895
+ * Frontmatter injected at build time for all pages under this workspace item.
896
+ */
897
+ readonly frontmatter?: Frontmatter;
898
+ }
899
+
900
+ export declare interface ZpressConfig {
901
+ /**
902
+ * Site title.
903
+ */
904
+ readonly title?: string;
905
+ /**
906
+ * Site meta description. Used as the hero headline on the home page.
907
+ */
908
+ readonly description?: string;
909
+ /**
910
+ * Path to a custom favicon file served from `.zpress/public/`.
911
+ * When omitted, defaults to the auto-generated `/icon.svg`.
912
+ */
913
+ readonly icon?: string;
914
+ /**
915
+ * Hero tagline displayed below the headline on the home page.
916
+ * When omitted, the tagline is not rendered.
917
+ */
918
+ readonly tagline?: string;
919
+ /**
920
+ * Workspace apps — deployable services that make up the platform.
921
+ * Single source of truth for app metadata used on the home page,
922
+ * landing pages, and introduction page.
923
+ */
924
+ readonly apps?: readonly WorkspaceItem[];
925
+ /**
926
+ * Workspace packages — shared libraries consumed by apps.
927
+ * Single source of truth for package metadata used on the home page,
928
+ * landing pages, and introduction page.
929
+ */
930
+ readonly packages?: readonly WorkspaceItem[];
931
+ /**
932
+ * Custom workspace groups — arbitrary named groups of workspace items.
933
+ * Each group receives the same card/landing-page treatment as apps and packages.
934
+ * Rendered after apps and packages, in array order.
935
+ */
936
+ readonly workspaces?: readonly WorkspaceGroup[];
937
+ /**
938
+ * Explicit feature cards for the home page.
939
+ *
940
+ * When provided, these replace the auto-generated feature cards
941
+ * that are normally derived from top-level sections.
942
+ * When omitted, features are auto-generated from sections with icons.
943
+ */
944
+ readonly features?: readonly Feature[];
945
+ /**
946
+ * The information architecture.
947
+ * Defines content sources, sidebar structure, and routing in a single tree.
948
+ */
949
+ readonly sections: readonly Entry[];
950
+ /**
951
+ * Top navigation bar.
952
+ * - `"auto"` — one nav item per top-level section
953
+ * - Array — explicit nav items
954
+ * @default "auto"
955
+ */
956
+ readonly nav?: 'auto' | readonly NavItem[];
957
+ /**
958
+ * Globs to exclude globally across all sources.
959
+ */
960
+ readonly exclude?: readonly GlobPattern[];
961
+ /**
962
+ * OpenAPI spec integration for interactive API docs.
963
+ */
964
+ readonly openapi?: OpenAPIConfig;
965
+ }
966
+
967
+ export { }
package/dist/index.mjs CHANGED
@@ -90,11 +90,6 @@ function validateConfig(config) {
90
90
  featErr,
91
91
  null
92
92
  ];
93
- const [navErr] = validateNav(config.nav);
94
- if (navErr) return [
95
- navErr,
96
- null
97
- ];
98
93
  return [
99
94
  null,
100
95
  config
@@ -227,29 +222,6 @@ function validateFeature(feature) {
227
222
  if (feature.icon && !feature.icon.includes(':')) return configError('invalid_icon', `Feature "${feature.text}": icon must be an Iconify identifier (e.g. "pixelarticons:speed-fast")`);
228
223
  return null;
229
224
  }
230
- function validateNav(nav) {
231
- if ('auto' === nav || void 0 === nav) return [
232
- null,
233
- true
234
- ];
235
- const navError = nav.reduce((acc, item)=>{
236
- if (acc) return acc;
237
- return validateNavItem(item);
238
- }, null);
239
- if (navError) return [
240
- navError,
241
- null
242
- ];
243
- return [
244
- null,
245
- true
246
- ];
247
- }
248
- function validateNavItem(item) {
249
- if (!item.icon) return configError('missing_nav_icon', `NavItem "${item.text}": top-level nav items require an "icon" (Iconify identifier)`);
250
- if (!item.icon.includes(':')) return configError('invalid_icon', `NavItem "${item.text}": icon must be an Iconify identifier (e.g. "pixelarticons:folder")`);
251
- return null;
252
- }
253
225
  async function config_loadConfig(dir) {
254
226
  const { config } = await loadConfig({
255
227
  cwd: dir,
@@ -1457,7 +1429,7 @@ function buildFeatures(sections, repoRoot) {
1457
1429
  return Promise.all(sections.slice(0, 3).map(async (section, index)=>{
1458
1430
  const link = section.link ?? findFirstChildLink(section);
1459
1431
  const details = await extractSectionDescription(section, repoRoot);
1460
- const iconId = match(section.icon).with(P.nonNullable, (id)=>id).otherwise(()=>null);
1432
+ const iconId = null;
1461
1433
  const iconColor = ICON_COLORS[index % ICON_COLORS.length];
1462
1434
  return {
1463
1435
  title: section.text,
@@ -2168,13 +2140,12 @@ function deduplicateByLink(entries) {
2168
2140
  });
2169
2141
  return result;
2170
2142
  }
2171
- function buildSidebarEntry(entry, icon) {
2143
+ function buildSidebarEntry(entry) {
2172
2144
  if (entry.items && entry.items.length > 0) return {
2173
2145
  text: entry.text,
2174
2146
  items: generateSidebar(entry.items),
2175
2147
  ...maybeCollapsed(entry.collapsible),
2176
- ...maybeLink(entry.link),
2177
- ...maybeIcon(icon)
2148
+ ...maybeLink(entry.link)
2178
2149
  };
2179
2150
  if (null === entry.link || void 0 === entry.link) {
2180
2151
  log.error(`[zpress] Leaf entry "${entry.text}" has no link — skipping`);
@@ -2184,33 +2155,28 @@ function buildSidebarEntry(entry, icon) {
2184
2155
  }
2185
2156
  return {
2186
2157
  text: entry.text,
2187
- link: entry.link,
2188
- ...maybeIcon(icon)
2158
+ link: entry.link
2189
2159
  };
2190
2160
  }
2191
- function generateSidebar(entries, icons) {
2161
+ function generateSidebar(entries) {
2192
2162
  const visible = entries.filter((e)=>!e.hidden);
2193
2163
  const pages = visible.filter((e)=>!e.items || 0 === e.items.length);
2194
2164
  const sections = visible.filter((e)=>e.items && e.items.length > 0);
2195
2165
  return [
2196
2166
  ...pages,
2197
2167
  ...sections
2198
- ].map((entry)=>{
2199
- const icon = resolveIcon(icons, entry.text);
2200
- return buildSidebarEntry(entry, icon);
2201
- });
2168
+ ].map(buildSidebarEntry);
2202
2169
  }
2203
- function buildNavEntry(entry, icon) {
2170
+ function buildNavEntry(entry) {
2204
2171
  const link = sidebar_resolveLink(entry);
2205
2172
  const children = resolveChildren(entry);
2206
2173
  return {
2207
2174
  text: entry.text,
2208
2175
  link,
2209
- ...maybeIcon(icon),
2210
2176
  ...maybeChildren(children)
2211
2177
  };
2212
2178
  }
2213
- function generateNav(config, resolved, icons) {
2179
+ function generateNav(config, resolved) {
2214
2180
  if ('auto' !== config.nav && void 0 !== config.nav) return [
2215
2181
  ...config.nav
2216
2182
  ];
@@ -2220,7 +2186,7 @@ function generateNav(config, resolved, icons) {
2220
2186
  return [
2221
2187
  ...nonIsolated,
2222
2188
  ...isolated
2223
- ].map((entry)=>buildNavEntry(entry, icons.get(entry.text))).filter((item)=>void 0 !== item.link);
2189
+ ].map(buildNavEntry).filter((item)=>void 0 !== item.link);
2224
2190
  }
2225
2191
  function sidebar_findFirstLink(entry) {
2226
2192
  if (entry.link) return entry.link;
@@ -2241,15 +2207,6 @@ function maybeLink(link) {
2241
2207
  };
2242
2208
  return {};
2243
2209
  }
2244
- function maybeIcon(icon) {
2245
- if (icon) return {
2246
- icon
2247
- };
2248
- return {};
2249
- }
2250
- function resolveIcon(icons, text) {
2251
- if (icons) return icons.get(text);
2252
- }
2253
2210
  function sidebar_resolveLink(entry) {
2254
2211
  if (entry.link) return entry.link;
2255
2212
  return sidebar_findFirstLink(entry);
@@ -2379,10 +2336,10 @@ function resolveTags(tags) {
2379
2336
  ...tags
2380
2337
  ];
2381
2338
  }
2382
- function buildMultiSidebar(resolved, openapiSidebar, icons) {
2339
+ function buildMultiSidebar(resolved, openapiSidebar) {
2383
2340
  const rootEntries = resolved.filter((e)=>!e.isolated);
2384
2341
  const isolatedEntries = resolved.filter((e)=>e.isolated && e.link);
2385
- const docsSidebar = generateSidebar(rootEntries, icons);
2342
+ const docsSidebar = generateSidebar(rootEntries);
2386
2343
  const childrenByLink = new Map(isolatedEntries.map((entry)=>{
2387
2344
  const link = entry.link;
2388
2345
  const items = resolveEntryItems(entry.items);
@@ -2394,23 +2351,19 @@ function buildMultiSidebar(resolved, openapiSidebar, icons) {
2394
2351
  const isolatedSidebar = Object.fromEntries(isolatedEntries.flatMap((entry)=>{
2395
2352
  const entryLink = entry.link;
2396
2353
  const children = resolveChildrenByLink(childrenByLink, entryLink);
2397
- const icon = icons.get(entry.text);
2398
2354
  const parentLink = resolveParentLink(entryLink);
2399
2355
  const parentEntry = match(parentLink).with(P.nonNullable, (pl)=>isolatedEntries.find((e)=>e.link === pl)).otherwise(()=>{});
2400
2356
  const isChild = null != parentEntry && parentEntry !== entry;
2401
2357
  const landing = {
2402
2358
  text: entry.text,
2403
- link: entryLink,
2404
- ...multi_maybeIcon(icon)
2359
+ link: entryLink
2405
2360
  };
2406
2361
  const sidebarItems = match(isChild).with(true, ()=>{
2407
2362
  const pe = parentEntry;
2408
2363
  const peLink = pe.link;
2409
- const parentIcon = icons.get(pe.text);
2410
2364
  const parentLanding = {
2411
2365
  text: pe.text,
2412
- link: peLink,
2413
- ...multi_maybeIcon(parentIcon)
2366
+ link: peLink
2414
2367
  };
2415
2368
  const siblings = isolatedEntries.filter((sib)=>{
2416
2369
  const sibLink = sib.link;
@@ -2480,12 +2433,6 @@ function resolveParentLink(entryLink) {
2480
2433
  if (segments) return segments;
2481
2434
  return null;
2482
2435
  }
2483
- function multi_maybeIcon(icon) {
2484
- if (icon) return {
2485
- icon
2486
- };
2487
- return {};
2488
- }
2489
2436
  function buildSidebarGroup(text, link, children, collapsed) {
2490
2437
  if (children.length > 0) return {
2491
2438
  text,
@@ -2522,7 +2469,6 @@ function synthesizeWorkspaceSections(config) {
2522
2469
  text: 'Apps',
2523
2470
  link: '/apps',
2524
2471
  isolated: true,
2525
- icon: 'pixelarticons:device-laptop',
2526
2472
  frontmatter: {
2527
2473
  description: 'Deployable applications that make up the platform.'
2528
2474
  },
@@ -2532,7 +2478,6 @@ function synthesizeWorkspaceSections(config) {
2532
2478
  text: 'Packages',
2533
2479
  link: '/packages',
2534
2480
  isolated: true,
2535
- icon: 'pixelarticons:archive',
2536
2481
  frontmatter: {
2537
2482
  description: 'Shared libraries and utilities consumed across apps and services.'
2538
2483
  },
@@ -2545,7 +2490,6 @@ function synthesizeWorkspaceSections(config) {
2545
2490
  text: group.name,
2546
2491
  link,
2547
2492
  isolated: true,
2548
- icon: group.icon,
2549
2493
  frontmatter: {
2550
2494
  description: group.description
2551
2495
  },
@@ -2766,9 +2710,8 @@ async function sync(config, options) {
2766
2710
  skipped: 0
2767
2711
  }));
2768
2712
  const removed = await match(previousManifest).with(P.nonNullable, async (m)=>await cleanStaleFiles(outDir, m, ctx.manifest)).otherwise(()=>Promise.resolve(0));
2769
- const icons = buildIconMap(allSections);
2770
- const sortedSidebar = buildMultiSidebar(resolved, openapiSidebar, icons);
2771
- const nav = generateNav(config, resolved, icons);
2713
+ const sortedSidebar = buildMultiSidebar(resolved, openapiSidebar);
2714
+ const nav = generateNav(config, resolved);
2772
2715
  await promises.writeFile(node_path.resolve(outDir, '.generated/sidebar.json'), JSON.stringify(sortedSidebar, null, 2), 'utf8');
2773
2716
  await promises.writeFile(node_path.resolve(outDir, '.generated/nav.json'), JSON.stringify(nav, null, 2), 'utf8');
2774
2717
  await saveManifest(outDir, ctx.manifest);
@@ -2864,17 +2807,6 @@ async function copyAll(src, dest) {
2864
2807
  else await promises.copyFile(srcPath, destPath);
2865
2808
  }, Promise.resolve());
2866
2809
  }
2867
- function buildIconMap(sections) {
2868
- return new Map(sections.flatMap((section)=>{
2869
- if (section.icon) return [
2870
- [
2871
- section.text,
2872
- section.icon
2873
- ]
2874
- ];
2875
- return [];
2876
- }));
2877
- }
2878
2810
  function resolveQuiet(quiet) {
2879
2811
  if (null != quiet) return quiet;
2880
2812
  return false;
package/package.json CHANGED
@@ -1,6 +1,15 @@
1
1
  {
2
2
  "name": "@zpress/core",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
+ "description": "Config loading, sync engine, and asset utilities for zpress",
5
+ "keywords": [
6
+ "config",
7
+ "core",
8
+ "docs",
9
+ "zpress"
10
+ ],
11
+ "homepage": "https://github.com/joggrdocs/zpress/tree/main/packages/core#readme",
12
+ "bugs": "https://github.com/joggrdocs/zpress/issues",
4
13
  "license": "MIT",
5
14
  "repository": {
6
15
  "type": "git",
@@ -13,12 +22,13 @@
13
22
  "type": "module",
14
23
  "exports": {
15
24
  ".": {
16
- "types": "./src/index.ts",
25
+ "types": "./dist/index.d.ts",
17
26
  "import": "./dist/index.mjs"
18
27
  }
19
28
  },
20
29
  "publishConfig": {
21
- "access": "public"
30
+ "access": "public",
31
+ "provenance": true
22
32
  },
23
33
  "dependencies": {
24
34
  "@clack/prompts": "^1.1.0",