@orbytes/astrolab 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.
Files changed (94) hide show
  1. package/LICENSE +37 -0
  2. package/README.md +410 -0
  3. package/bin/lab-cull.mjs +401 -0
  4. package/bin/pin-gallery.mjs +121 -0
  5. package/defaults.mjs +120 -0
  6. package/dist/core/astro-integration.js +130 -0
  7. package/dist/core/index.js +6 -0
  8. package/dist/core/lib-paths.js +29 -0
  9. package/dist/core/options.js +173 -0
  10. package/dist/core/utils/get-exports.js +52 -0
  11. package/dist/core/utils/invariant.js +12 -0
  12. package/dist/core/utils/kebab-case.js +15 -0
  13. package/dist/core/utils/path-builder.js +22 -0
  14. package/dist/core/utils/path.js +53 -0
  15. package/dist/core/virtual-module/get-story-modules.js +60 -0
  16. package/dist/core/virtual-module/story-modules.js +8 -0
  17. package/dist/core/virtual-module/virtual-module-ids.js +22 -0
  18. package/dist/core/virtual-module/virtual-routes.js +83 -0
  19. package/dist/core/virtual-module/vite-plugin.js +98 -0
  20. package/docs/PIN-CONTRACT.md +263 -0
  21. package/docs/PIN.md +429 -0
  22. package/index.d.ts +279 -0
  23. package/index.mjs +347 -0
  24. package/package.json +93 -0
  25. package/src/Empty.astro +4 -0
  26. package/src/Home.astro +298 -0
  27. package/src/LabHead.astro +1102 -0
  28. package/src/core/LICENSE-astrobook +166 -0
  29. package/src/core/astro-integration.ts +166 -0
  30. package/src/core/client.ts +89 -0
  31. package/src/core/index.ts +7 -0
  32. package/src/core/lib/components/empty.astro +1 -0
  33. package/src/core/lib/components/head.astro +1 -0
  34. package/src/core/lib/components/home.astro +8 -0
  35. package/src/core/lib/components/with-decorators.astro +22 -0
  36. package/src/core/lib/pages/app.astro +19 -0
  37. package/src/core/lib/pages/preview.astro +17 -0
  38. package/src/core/lib/pages/story.astro +16 -0
  39. package/src/core/lib-paths.ts +72 -0
  40. package/src/core/options.ts +262 -0
  41. package/src/core/utils/get-exports.ts +59 -0
  42. package/src/core/utils/invariant.ts +13 -0
  43. package/src/core/utils/kebab-case.ts +30 -0
  44. package/src/core/utils/path-builder.ts +45 -0
  45. package/src/core/utils/path.ts +80 -0
  46. package/src/core/virtual-module/get-story-modules.ts +110 -0
  47. package/src/core/virtual-module/story-modules.ts +9 -0
  48. package/src/core/virtual-module/virtual-module-ids.ts +17 -0
  49. package/src/core/virtual-module/virtual-routes.ts +130 -0
  50. package/src/core/virtual-module/vite-plugin.ts +125 -0
  51. package/src/pin/board.mjs +1521 -0
  52. package/src/pin/index.mjs +666 -0
  53. package/src/pin/shot.mjs +427 -0
  54. package/src/pin/source-stamp.mjs +159 -0
  55. package/src/pin/tickets.mjs +697 -0
  56. package/src/pin/toolbar.js +3181 -0
  57. package/src/shell/Browse.astro +371 -0
  58. package/src/shell/CardGrid.astro +297 -0
  59. package/src/shell/Viewport.astro +1330 -0
  60. package/src/shell/index.json.ts +12 -0
  61. package/src/shell/lab-index.ts +344 -0
  62. package/src/shell/lab-params.ts +245 -0
  63. package/src/shell/live-files.mjs +164 -0
  64. package/src/shell/marks.mjs +136 -0
  65. package/src/types/index.ts +6 -0
  66. package/src/types/types.ts +239 -0
  67. package/src/types/virtual.d.ts +29 -0
  68. package/src/ui/components/app.astro +13 -0
  69. package/src/ui/components/build-path.ts +13 -0
  70. package/src/ui/components/build-tree.ts +108 -0
  71. package/src/ui/components/collapse-duration.ts +28 -0
  72. package/src/ui/components/compress-terms.ts +10 -0
  73. package/src/ui/components/dashboard-layout.astro +39 -0
  74. package/src/ui/components/home.astro +65 -0
  75. package/src/ui/components/layout.astro +110 -0
  76. package/src/ui/components/preview-layout.astro +109 -0
  77. package/src/ui/components/sidebar-button-fullscreen.astro +38 -0
  78. package/src/ui/components/sidebar-button-search.astro +23 -0
  79. package/src/ui/components/sidebar-button-theme.astro +9 -0
  80. package/src/ui/components/sidebar-button.astro +24 -0
  81. package/src/ui/components/sidebar-resize-handle.astro +74 -0
  82. package/src/ui/components/sidebar-search-panel.astro +41 -0
  83. package/src/ui/components/sidebar-search-script.ts +103 -0
  84. package/src/ui/components/sidebar-title.astro +17 -0
  85. package/src/ui/components/sidebar-tree-node.astro +143 -0
  86. package/src/ui/components/sidebar-tree.astro +84 -0
  87. package/src/ui/components/sidebar.astro +29 -0
  88. package/src/ui/components/theme-message.ts +26 -0
  89. package/src/ui/components/theme-script.astro +71 -0
  90. package/src/ui/components/theme-toggle.astro +63 -0
  91. package/src/ui/components/theme.ts +32 -0
  92. package/src/ui/index.ts +4 -0
  93. package/src/ui/lab.css +549 -0
  94. package/virtual.d.ts +42 -0
@@ -0,0 +1,166 @@
1
+ Everything under src/core/, src/ui/ and src/types/ is vendored from Astrobook.
2
+
3
+ Upstream: https://github.com/ocavue/astrobook
4
+ Version: 0.13.3
5
+ Commit: a79f03cdfd32eb08330b33fa7d5830ac506f9272
6
+
7
+ This notice lives under src/core/ for historical reasons — that was the only vendored directory
8
+ when it was written, on 2026-09-21 — and it governs all three. What each one is:
9
+
10
+ src/core/ the upstream package `@astrobook/core` (packages/core/src/ → src/core/) plus the
11
+ hand-written Astro components of the `astrobook` package (packages/astrobook/lib/
12
+ → src/core/lib/).
13
+ src/ui/ the upstream package `@astrobook/ui` (packages/ui/src/ → src/ui/), vendored
14
+ 2026-09-22.
15
+ src/types/ the upstream package `@astrobook/types` (packages/types/lib/ → src/types/),
16
+ vendored 2026-09-22.
17
+
18
+ So this package depends on no astrobook npm package at all, and on nothing outside Astro itself.
19
+ The upstream layout is mirrored in each case so a future `diff -r` against an upstream tag stays
20
+ possible.
21
+
22
+ THE CONVENTION, UNCHANGED: a file here is byte-identical to upstream unless it opens with a
23
+ `// Vendored from astrobook 0.13.3 (…)` header, and that header names what changed in it. So
24
+ `diff -r` against a future upstream checkout shows real upstream changes, and the headers are the
25
+ list of what to re-apply. Four files carry no header and no change: core/utils/get-exports.ts,
26
+ core/utils/invariant.ts, core/utils/path.ts, core/virtual-module/virtual-module-ids.ts. Under
27
+ core/lib/, four of the six .astro files are unchanged (components/head.astro,
28
+ components/empty.astro, components/with-decorators.astro, and — apart from one import — nothing
29
+ else); every file under src/ui/ and src/types/ carries a header, because every one of them
30
+ changed.
31
+
32
+ THE VIRTUAL MODULE IDS ARE UPSTREAM'S AND MUST STAY THAT WAY. The vendored core emits
33
+ `virtual:astrobook/story-modules.mjs` and its siblings (core/virtual-module/virtual-module-ids.ts)
34
+ and the vendored UI imports them by name. Both sides are ours now, so renaming them to
35
+ `virtual:orbytes-lab/*` is possible — it is pure churn with silent-breakage risk, so it is later
36
+ cleanup and deliberately not part of the 2026-09-22 pass.
37
+
38
+ --------------------------------------------------------------------------------
39
+ Every deliberate divergence in src/core/
40
+ --------------------------------------------------------------------------------
41
+
42
+ 1. Relative imports carry the `.ts` extension. Upstream is bundled by tsdown before it ships;
43
+ this package ships source, and node cannot resolve an extensionless specifier. Astro loads
44
+ astro.config.mjs with a plain node import() first, so this graph has to load under node.
45
+ 2. `src/core/lib-paths.ts` is new. It resolves the vendored `.astro` files and client.ts to
46
+ absolute posix paths from `import.meta.url`, replacing the bare `astrobook/pages/story.astro`,
47
+ `astrobook/components/with-decorators.astro`, `astrobook/client`,
48
+ `astrobook/components/{head,home,empty}.astro` and `astrobook/pages/app.astro` specifiers that
49
+ upstream emits into generated routes, injects as a route entrypoint, and uses as option
50
+ defaults. A bare specifier resolves to the wrong checkout's copy in an agent git worktree
51
+ (measured on a real consuming site); an absolute path cannot.
52
+ 3. `src/core/utils/kebab-case.ts` is new: `just-kebab-case@4.2.0`, inlined. Upstream imports it
53
+ but declares it only as a devDependency, because tsdown bundles it into the published artifact.
54
+ Nothing bundles here, so it is inlined from that artifact instead.
55
+ 4. `options.ts` is rewritten in plain TypeScript. Upstream expresses it as a valibot schema;
56
+ valibot is 1.8 MB for one caller. Same option names, same defaults, same `home: false`
57
+ transform, same resolved shape, same thrown-message layout. The upstream version string used
58
+ for the home page's version-badge label is inlined as `0.13.3`.
59
+ 5. `picomatch` is declared by upstream `@astrobook/core` and imported nowhere in it, so it is not
60
+ carried over.
61
+ 6. The `*.spec.ts` / `*.test.ts` files are not vendored — this package has no test runner.
62
+ 7. Type imports point at `src/types/` by relative path rather than at the `@astrobook/types`
63
+ package (2026-09-22).
64
+ 8. `src/core/lib/pages/preview.astro` is new, and `virtual-routes.ts` picks between it and
65
+ `story.astro` per route (2026-09-22). Upstream routes the dashboard and the bare story through
66
+ one page, so a bare story also received the lab's chrome. Splitting them is what lets the
67
+ lab's own styling stop at the preview's edge — see src/ui/lab.css.
68
+
69
+ --------------------------------------------------------------------------------
70
+ Every deliberate divergence in src/ui/ (all of it added 2026-09-22)
71
+ --------------------------------------------------------------------------------
72
+
73
+ 9. NO UNOCSS. Upstream's UI is written in UnoCSS atomic classes (`:uno: flex shrink-0 …`) and
74
+ three shortcuts, compiled by `unocss --rewrite` at publish time into a 24 KB `style.css` of
75
+ hashed `astrobook-7fpo4s` class names. This package ships source and has no build step, so
76
+ every utility string is replaced by a semantic class — `.lab-sidebar__nav`, `.lab-tree-row`,
77
+ `.lab-search__input` — and every rule lives in one hand-written stylesheet, `src/ui/lab.css`.
78
+ Upstream's `src/style.css` (a placeholder for the generated file) and `uno.config.ts` are not
79
+ vendored. Neither are the icon classes: `presetIcons` turned `i-fluent-search-16-regular` and
80
+ friends into background images at build time, so each is now an inline `<svg>`.
81
+ 10. THE CHROME NO LONGER RECEIVES THE CONSUMER'S CSS OR HEAD (decided 2026-09-22: the lab's own
82
+ furniture looks the same in every repo it is installed in, and only the rendered story takes
83
+ the site's styling). Upstream's `layout.astro` imported `virtual:astrobook/user-css.mjs` last, "so it
84
+ can override built-in styles". Here the consumer's stylesheets and head component reach the
85
+ PREVIEW only. Three layouts where upstream had one:
86
+ layout.astro the chrome shell. No consumer CSS, no consumer head. `/lab`.
87
+ dashboard-layout.astro NEW. The chrome plus the story inside it — the one document that
88
+ carries both stylesheets, because it is both things at once.
89
+ preview-layout.astro NEW. The bare story: consumer CSS and head, none of the lab's own.
90
+ `layout.astro` also gained a `<slot name="head" />` so a wrapping layout can add to the head
91
+ without this file importing anything. Full reasoning: src/ui/lab.css.
92
+ 11. `astro-theme-toggle` is vendored, not depended on: `theme-script.astro`, `theme-toggle.astro`
93
+ and `theme.ts` are written for this package. Same `theme-toggle` localStorage key, same
94
+ `.dark` class, `data-theme` and `color-scheme` on <html>, same `astro:after-swap` re-apply, so
95
+ an existing reader's stored preference carries over. Dropped: the ripple
96
+ `startViewTransition` animation. Why the dependency went, at length: theme-script.astro.
97
+ 12. `customElements.define` calls are guarded with `customElements.get`, so a second definition on
98
+ a view-transition swap cannot throw.
99
+ 13. `build-path.ts` imports `createPathBuilder` from the vendored core by relative path rather
100
+ than from `@astrobook/core/client`, and the `import '@astrobook/types'` side-effect lines are
101
+ gone — the ambient declarations they pulled in live in src/types/virtual.d.ts.
102
+
103
+ --------------------------------------------------------------------------------
104
+ Every deliberate divergence in src/types/ (all of it added 2026-09-22)
105
+ --------------------------------------------------------------------------------
106
+
107
+ 14. `lib/types.d.ts` → `types.ts` and `lib/index.d.ts` + `lib/index.js` → one `index.ts`. The
108
+ bodies are interfaces and one `declare global`, so they emit nothing at runtime and every
109
+ importer uses `import type`; a plain `.ts` is what the vendored core can import by relative
110
+ path without `allowImportingTsExtensions`. `virtual.d.ts` keeps its extension and its module
111
+ ids, and its one internal import is spelled `./types.js` as node16 resolution requires.
112
+ 15. The option doc comments for `css` and `head` gained a line saying they now reach the preview
113
+ only. Nothing else in the text changed.
114
+
115
+ --------------------------------------------------------------------------------
116
+
117
+ MIT License
118
+
119
+ Copyright (c) 2024 ocavue
120
+
121
+ Permission is hereby granted, free of charge, to any person obtaining a copy
122
+ of this software and associated documentation files (the "Software"), to deal
123
+ in the Software without restriction, including without limitation the rights
124
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
125
+ copies of the Software, and to permit persons to whom the Software is
126
+ furnished to do so, subject to the following conditions:
127
+
128
+ The above copyright notice and this permission notice shall be included in all
129
+ copies or substantial portions of the Software.
130
+
131
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
132
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
133
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
134
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
135
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
136
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
137
+ SOFTWARE.
138
+
139
+ --------------------------------------------------------------------------------
140
+
141
+ astro-theme-toggle (vendored in part — see divergence 11 above)
142
+
143
+ Upstream: https://github.com/ocavue/astro-theme-toggle
144
+ Version: 0.8.2
145
+
146
+ MIT License
147
+
148
+ Copyright (c) 2025 ocavue
149
+
150
+ Permission is hereby granted, free of charge, to any person obtaining a copy
151
+ of this software and associated documentation files (the "Software"), to deal
152
+ in the Software without restriction, including without limitation the rights
153
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
154
+ copies of the Software, and to permit persons to whom the Software is
155
+ furnished to do so, subject to the following conditions:
156
+
157
+ The above copyright notice and this permission notice shall be included in all
158
+ copies or substantial portions of the Software.
159
+
160
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
161
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
162
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
163
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
164
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
165
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
166
+ SOFTWARE.
@@ -0,0 +1,166 @@
1
+ // Vendored from astrobook 0.13.3 (astro-integration.ts) — see ./LICENSE-astrobook.
2
+ // Divergence: relative imports carry the .ts extension so node can load this graph directly
3
+ // (upstream is bundled before it ships; this package is not).
4
+ // Divergence: the specifiers this file injects as a route entrypoint are absolute paths into ./lib-paths.ts rather
5
+ // than bare `astrobook/...` ids — see that file for why.
6
+ import fs from 'node:fs/promises'
7
+ import path from 'node:path'
8
+ import { fileURLToPath } from 'node:url'
9
+
10
+ import type { IntegrationOptions } from '../types/types.ts'
11
+ import type { AstroIntegration } from 'astro'
12
+ import colors from 'yoctocolors'
13
+
14
+ import { APP_PAGE } from './lib-paths.ts'
15
+ import { resolveOptions } from './options.ts'
16
+ import { createPathBuilder } from './utils/path-builder.ts'
17
+ import {
18
+ createVirtualRouteComponent,
19
+ getVirtualRoutes,
20
+ } from './virtual-module/virtual-routes.ts'
21
+ import { createVirtualFilesPlugin } from './virtual-module/vite-plugin.ts'
22
+
23
+ export type { IntegrationOptions }
24
+
25
+ export function createAstrobookIntegration(
26
+ userOptions?: IntegrationOptions,
27
+ ): AstroIntegration {
28
+ let astrobookBaseForLogging: string | undefined
29
+
30
+ const resolvedOptions = resolveOptions(userOptions)
31
+
32
+ return {
33
+ name: 'astrobook',
34
+ hooks: {
35
+ 'astro:config:setup': async ({
36
+ updateConfig,
37
+ injectRoute,
38
+ createCodegenDir,
39
+ config,
40
+ logger,
41
+ command,
42
+ }) => {
43
+ const rootDir = path.resolve(
44
+ fileURLToPath(config.root),
45
+ resolvedOptions.directory,
46
+ )
47
+
48
+ const astroBase = config.base || ''
49
+
50
+ // The subpaths are relative to the Astro base path.
51
+ const astrobookSubpath = resolvedOptions.subpath
52
+
53
+ const buildPath = createPathBuilder({
54
+ trailingSlash: config.trailingSlash,
55
+ })
56
+
57
+ const dashboardSubpath = buildPath(
58
+ astrobookSubpath,
59
+ resolvedOptions.dashboardSubpath,
60
+ )
61
+ const previewSubpath = buildPath(
62
+ astrobookSubpath,
63
+ resolvedOptions.previewSubpath,
64
+ )
65
+
66
+ if (dashboardSubpath === previewSubpath) {
67
+ throw new Error(
68
+ '[Astrobook] The dashboard and preview subpaths cannot be the same. Please set different values for `dashboardSubpath` and `previewSubpath` options.',
69
+ )
70
+ }
71
+
72
+ const astrobookBase = buildPath(astroBase, astrobookSubpath)
73
+ const dashboardBase = buildPath(astroBase, dashboardSubpath)
74
+ const storyBase = buildPath(astroBase, previewSubpath)
75
+
76
+ // If subpath is set, Astrobook is only part of the current Astro
77
+ // project. In this case, we want to print the URL of the Astrobook
78
+ // entrypoint for easy access.
79
+ if (astrobookSubpath && command === 'dev') {
80
+ astrobookBaseForLogging = astrobookBase
81
+ } else {
82
+ astrobookBaseForLogging = undefined
83
+ }
84
+
85
+ logger.debug(`Creating dedicated folder`)
86
+ const codegenDir: string = fileURLToPath(createCodegenDir())
87
+
88
+ logger.debug(`Created dedicated folder at ${codegenDir}`)
89
+
90
+ logger.debug(`Scanning for stories in ${rootDir}`)
91
+
92
+ const routes = await getVirtualRoutes(
93
+ rootDir,
94
+ codegenDir,
95
+ logger,
96
+ dashboardSubpath,
97
+ previewSubpath,
98
+ buildPath,
99
+ )
100
+
101
+ logger.debug(`Writing files to ${codegenDir}`)
102
+ await Promise.all(
103
+ Array.from(routes.values(), async (route) => {
104
+ const filePath = route.entrypoint
105
+ const fileContent = createVirtualRouteComponent(route)
106
+ await fs.mkdir(path.dirname(filePath), { recursive: true })
107
+ await fs.writeFile(filePath, fileContent, { encoding: 'utf-8' })
108
+ }),
109
+ )
110
+
111
+ updateConfig({
112
+ vite: {
113
+ plugins: [
114
+ createVirtualFilesPlugin(
115
+ rootDir,
116
+ {
117
+ astrobookBase,
118
+ dashboardBase,
119
+ storyBase,
120
+ head: resolvedOptions.head,
121
+ home: resolvedOptions.home,
122
+ homeContent: resolvedOptions.homeContent,
123
+ css: resolvedOptions.css,
124
+ title: resolvedOptions.title,
125
+ trailingSlash: config.trailingSlash,
126
+ },
127
+ config,
128
+ logger,
129
+ ),
130
+ ],
131
+ },
132
+ })
133
+
134
+ logger.debug(`Injecting routes`)
135
+ for (const route of routes.values()) {
136
+ injectRoute({
137
+ pattern: route.pattern,
138
+ entrypoint: route.entrypoint,
139
+ prerender: true,
140
+ })
141
+ }
142
+ injectRoute({
143
+ pattern: astrobookSubpath,
144
+ entrypoint: APP_PAGE,
145
+ prerender: true,
146
+ })
147
+ },
148
+ 'astro:server:start': ({ logger, address }) => {
149
+ if (astrobookBaseForLogging) {
150
+ const url = new URL(
151
+ astrobookBaseForLogging,
152
+ `http://localhost:${address.port}`,
153
+ )
154
+ const message =
155
+ colors.bgGreen(colors.white(colors.bold(' astrobook '))) +
156
+ ' is available at ' +
157
+ colors.cyan(url.href)
158
+ // Get a logger that don't print the label and the current time
159
+ // https://github.com/withastro/astro/blob/ff72ebe/packages/astro/src/core/logger/core.ts#L38
160
+ const plainLogger = logger.fork('SKIP_FORMAT')
161
+ plainLogger.info(message)
162
+ }
163
+ },
164
+ },
165
+ }
166
+ }
@@ -0,0 +1,89 @@
1
+ // Vendored from astrobook 0.13.3 (client.ts) — see LICENSE-astrobook.
2
+ // Divergence: relative imports carry the .ts extension so node can load this graph directly
3
+ // (upstream is bundled before it ships; this package is not).
4
+ import type { StoryNamedExport } from '../types/types.ts'
5
+ import type { AstroComponentFactory } from 'astro/runtime/server/index.js'
6
+
7
+ export { createPathBuilder, type PathBuilder } from './utils/path-builder.ts'
8
+
9
+ // Copy from https://github.com/withastro/astro/blob/astro@5.0.0/packages/astro/src/runtime/server/render/astro/factory.ts#L15
10
+ function isAstroComponentFactory(obj: unknown): obj is AstroComponentFactory {
11
+ return obj == null
12
+ ? false
13
+ : (obj as Record<string, unknown>).isAstroComponentFactory === true
14
+ }
15
+
16
+ /**
17
+ * @internal
18
+ */
19
+ export function parseStoryDefaultExport(
20
+ module: Record<string, unknown>,
21
+ importPath: string,
22
+ ): {
23
+ isAstroComponent: boolean
24
+ } {
25
+ const defaultExport = 'default' in module ? module['default'] : undefined
26
+ if (!defaultExport) {
27
+ throw new TypeError(
28
+ `[astrobook] '${importPath}' should have a default export`,
29
+ )
30
+ }
31
+ if (typeof defaultExport !== 'object') {
32
+ throw new TypeError(
33
+ `[astrobook] '${importPath}' default export should be an object, but got ${typeof defaultExport}`,
34
+ )
35
+ }
36
+
37
+ const component =
38
+ 'component' in defaultExport ? defaultExport.component : undefined
39
+ if (!component) {
40
+ throw new TypeError(
41
+ `[astrobook] '${importPath}' default export should have a component property`,
42
+ )
43
+ }
44
+
45
+ // Whether the component is an Astro component or not (e.g. React, Vue, etc.)
46
+ const isAstroComponent: boolean = isAstroComponentFactory(component)
47
+
48
+ return { isAstroComponent }
49
+ }
50
+
51
+ /**
52
+ * @internal
53
+ */
54
+ export function parseStoryNamedExport(
55
+ module: Record<string, unknown>,
56
+ importPath: string,
57
+ storyName: string,
58
+ ): StoryNamedExport {
59
+ const storyObject: unknown =
60
+ module && typeof module === 'object' ? module[storyName] : undefined
61
+ if (!storyObject) {
62
+ throw new Error(
63
+ `[astrobook] Unable to find named export '${storyName}' from '${importPath}'`,
64
+ )
65
+ }
66
+ if (typeof storyObject !== 'object') {
67
+ throw new TypeError(
68
+ `[astrobook] Named export '${storyName}' from '${importPath}' should be an object, but got ${typeof storyObject}`,
69
+ )
70
+ }
71
+
72
+ const decorators =
73
+ 'decorators' in storyObject ? storyObject.decorators : undefined
74
+
75
+ if (decorators != null && !Array.isArray(decorators)) {
76
+ throw new TypeError(
77
+ `[astrobook] The 'decorators' property of the named export '${storyName}' from '${importPath}' should be an array or undefined, but got ${typeof decorators}`,
78
+ )
79
+ }
80
+
81
+ const args = 'args' in storyObject ? storyObject.args : undefined
82
+ if (args != null && typeof args !== 'object') {
83
+ throw new TypeError(
84
+ `[astrobook] The 'args' property of the named export '${storyName}' from '${importPath}' should be an object or undefined, but got ${typeof args}`,
85
+ )
86
+ }
87
+
88
+ return { decorators, args }
89
+ }
@@ -0,0 +1,7 @@
1
+ // Vendored from astrobook 0.13.3 (index.ts) — see LICENSE-astrobook.
2
+ // Divergence: relative imports carry the .ts extension so node can load this graph directly
3
+ // (upstream is bundled before it ships; this package is not).
4
+ export {
5
+ createAstrobookIntegration,
6
+ type IntegrationOptions,
7
+ } from './astro-integration.ts'
@@ -0,0 +1 @@
1
+ <!-- Empty home component, used when `home: false` is set in the Astrobook integration options -->
@@ -0,0 +1 @@
1
+ <slot />
@@ -0,0 +1,8 @@
1
+ ---
2
+ // Vendored from astrobook 0.13.3 (packages/astrobook/lib/components/home.astro) — see
3
+ // ../../LICENSE-astrobook.
4
+ // Divergence: `@astrobook/ui/components/home.astro` → the vendored UI, by relative path.
5
+ import Home from '../../../ui/components/home.astro'
6
+ ---
7
+
8
+ <Home />
@@ -0,0 +1,22 @@
1
+ ---
2
+ interface Decorator {
3
+ component: any
4
+ props?: any
5
+ }
6
+
7
+ interface Props {
8
+ decorators?: Decorator[] | null | undefined
9
+ }
10
+
11
+ const decorators: Decorator[] = (Astro.props.decorators || []).slice().reverse()
12
+ ---
13
+
14
+ {
15
+ decorators.reduce(
16
+ (children, decorator) => {
17
+ const Decorator = decorator.component
18
+ return <Decorator {...decorator?.props}>{children}</Decorator>
19
+ },
20
+ <slot />,
21
+ )
22
+ }
@@ -0,0 +1,19 @@
1
+ ---
2
+ // Vendored from astrobook 0.13.3 (packages/astrobook/lib/pages/app.astro) — see
3
+ // ../../LICENSE-astrobook.
4
+ // Divergence: `@astrobook/ui/components/app.astro` → the vendored UI, by relative path.
5
+ import AppComponent from '../../../ui/components/app.astro'
6
+ import type { GetStaticPathsResult } from 'astro'
7
+
8
+ export function getStaticPaths(): GetStaticPathsResult {
9
+ return [
10
+ {
11
+ params: {
12
+ path: undefined,
13
+ },
14
+ },
15
+ ]
16
+ }
17
+ ---
18
+
19
+ <AppComponent />
@@ -0,0 +1,17 @@
1
+ ---
2
+ // The bare story page — <subpath>/stories/<id>. New in this package: upstream routed the preview
3
+ // through the same `story.astro` as the dashboard, so a bare story also received the lab's
4
+ // chrome stylesheet, its head and its scripts.
5
+ //
6
+ // ../../virtual-module/virtual-routes.ts picks this page for the preview route and ./story.astro
7
+ // for the dashboard route. The split exists so the lab's own styling stops at the preview's edge
8
+ // (decided 2026-09-22 — ../../../ui/lab.css).
9
+ import LayoutComponent from '../../../ui/components/preview-layout.astro'
10
+ import type { ComponentProps } from 'astro/types'
11
+
12
+ type Props = ComponentProps<typeof LayoutComponent>
13
+ ---
14
+
15
+ <LayoutComponent {...Astro.props}>
16
+ <slot />
17
+ </LayoutComponent>
@@ -0,0 +1,16 @@
1
+ ---
2
+ // Vendored from astrobook 0.13.3 (packages/astrobook/lib/pages/story.astro) — see
3
+ // ../../LICENSE-astrobook.
4
+ // Divergence: `@astrobook/ui/components/layout.astro` → the vendored UI's DASHBOARD layout, by
5
+ // relative path. Upstream used one layout for the dashboard and the bare story alike; this page
6
+ // is now the dashboard only (chrome + the story inside it), and the bare story route renders
7
+ // through ./preview.astro. Why the two split: ../../../ui/lab.css.
8
+ import LayoutComponent from '../../../ui/components/dashboard-layout.astro'
9
+ import type { ComponentProps } from 'astro/types'
10
+
11
+ type Props = ComponentProps<typeof LayoutComponent>
12
+ ---
13
+
14
+ <LayoutComponent {...Astro.props}>
15
+ <slot />
16
+ </LayoutComponent>
@@ -0,0 +1,72 @@
1
+ // Absolute paths to the vendored .astro files in ./lib/ — the one file in src/core/ with no
2
+ // upstream counterpart.
3
+ //
4
+ // Upstream emits BARE specifiers for these ('astrobook/pages/story.astro' and friends), which
5
+ // node resolves by walking up for a node_modules directory. In an agent git worktree that walk
6
+ // leaves the checkout and finds the MAIN clone's copy, so the worktree's own edits are silently
7
+ // ignored — measured 2026-09-06 on a real consuming site. An absolute path computed from
8
+ // import.meta.url cannot go wrong
9
+ // that way: it names this file's own neighbour, in whichever checkout this file was loaded from.
10
+ //
11
+ // Posix slashes via slash(), matching how virtual-routes.ts normalises the entrypoints it
12
+ // generates; Vite resolves an absolute fs path in an import specifier, which is already how
13
+ // upstream emits the story module's own import.
14
+ import { fileURLToPath } from 'node:url'
15
+
16
+ import slash from 'slash'
17
+
18
+ /**
19
+ * The base these paths resolve against — ALWAYS `src/core/`, even when this module is the compiled
20
+ * copy in `dist/core/`.
21
+ *
22
+ * Why it cannot just be `import.meta.url`. For npm, `scripts/build-core.mjs` compiles this tree to
23
+ * `dist/core/` so Node can load it from inside `node_modules` (Node refuses to strip types there).
24
+ * The `.astro` files named below do NOT move with it, and must not: they import sideways into
25
+ * `../../ui/`, so a copy under `dist/` would need a second copy of the whole UI beside it — and
26
+ * two copies of the UI in one build means duplicate components and duplicate CSS in the
27
+ * consumer's output. Measured: moving them produced
28
+ * `Could not resolve '../../../ui/components/app.astro'`.
29
+ *
30
+ * So the compiled copy reaches back across the two directories to the real ones. `endsWith` rather
31
+ * than `includes`, so a consumer whose own project path happens to contain `dist/core` cannot
32
+ * trip it.
33
+ */
34
+ const LIB_BASE = new URL(
35
+ import.meta.url.endsWith('/dist/core/lib-paths.js') ? '../../src/core/' : './',
36
+ import.meta.url,
37
+ )
38
+
39
+ function libFile(relative: string): string {
40
+ return slash(fileURLToPath(new URL(relative, LIB_BASE)))
41
+ }
42
+
43
+ /** The vendored `@astrobook/core` client helpers, imported by every generated route. */
44
+ export const CLIENT_MODULE = libFile('./client.ts')
45
+
46
+ /** `astrobook/pages/story.astro` — the DASHBOARD page (chrome + the story inside it). */
47
+ export const STORY_PAGE = libFile('./lib/pages/story.astro')
48
+
49
+ /**
50
+ * The BARE STORY page. No upstream counterpart: upstream routes both the dashboard and the
51
+ * preview through `story.astro`, so a bare story also received the lab's chrome. Splitting them
52
+ * is what lets the lab's own styling stop at the preview's edge (decided 2026-09-22) — see
53
+ * ../ui/lab.css and ./virtual-module/virtual-routes.ts, which picks between the two.
54
+ */
55
+ export const PREVIEW_PAGE = libFile('./lib/pages/preview.astro')
56
+
57
+ /** `astrobook/pages/app.astro` */
58
+ export const APP_PAGE = libFile('./lib/pages/app.astro')
59
+
60
+ /** `astrobook/components/with-decorators.astro` */
61
+ export const WITH_DECORATORS_COMPONENT = libFile(
62
+ './lib/components/with-decorators.astro',
63
+ )
64
+
65
+ /** `astrobook/components/head.astro` */
66
+ export const HEAD_COMPONENT = libFile('./lib/components/head.astro')
67
+
68
+ /** `astrobook/components/home.astro` */
69
+ export const HOME_COMPONENT = libFile('./lib/components/home.astro')
70
+
71
+ /** `astrobook/components/empty.astro` */
72
+ export const EMPTY_COMPONENT = libFile('./lib/components/empty.astro')