@sandboxsonny/ace-kit 0.0.0-stage → 0.1.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 (44) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/LICENSE +21 -0
  3. package/README.md +79 -2
  4. package/astro/AceImage.astro +29 -0
  5. package/astro/AceMenu.astro +24 -0
  6. package/astro/AceMenuList.astro +25 -0
  7. package/astro/AceNotice.astro +19 -0
  8. package/astro/AcePreviewRefresh.astro +15 -0
  9. package/astro/AceRichText.astro +12 -0
  10. package/astro/AceTextArea.astro +14 -0
  11. package/dist/astro-integration.d.ts +20 -0
  12. package/dist/astro-integration.js +32 -0
  13. package/dist/astro-middleware.js +10 -0
  14. package/dist/astro.d.ts +139 -0
  15. package/dist/astro.js +24 -0
  16. package/dist/chunk-KEV3YAWN.js +66 -0
  17. package/dist/chunk-KLRCWAE4.js +147 -0
  18. package/dist/chunk-M5WR5TCR.js +16 -0
  19. package/dist/chunk-URLKW2YS.js +107 -0
  20. package/dist/chunk-XPZY5R5A.js +16 -0
  21. package/dist/chunk-YLO6IAXF.js +45 -0
  22. package/dist/chunk-YN7LJWGI.js +36 -0
  23. package/dist/cli.js +211 -0
  24. package/dist/index.d.ts +310 -0
  25. package/dist/index.js +6 -0
  26. package/dist/preview-script.js +6 -0
  27. package/dist/react-router-server.d.ts +225 -0
  28. package/dist/react-router-server.js +89 -0
  29. package/dist/react-router.d.ts +155 -0
  30. package/dist/react-router.js +55 -0
  31. package/dist/react.d.ts +88 -0
  32. package/dist/react.js +5 -0
  33. package/dist/vite.d.ts +17 -0
  34. package/dist/vite.js +27 -0
  35. package/docs/api-reference.md +105 -0
  36. package/docs/astro.md +93 -0
  37. package/docs/getting-started.md +62 -0
  38. package/docs/live-preview.md +15 -0
  39. package/docs/react-router.md +116 -0
  40. package/docs/react.md +81 -0
  41. package/docs/releasing.md +53 -0
  42. package/docs/rich-text.md +54 -0
  43. package/docs/typed-content.md +50 -0
  44. package/package.json +158 -4
@@ -0,0 +1,116 @@
1
+ # React Router 7
2
+
3
+ Ace Kit has a React Router adapter, so a site only writes its design. It needs `react-router` 7 in framework mode with server rendering (`ssr: true`, the default), `@react-router/node`, `react-dom` and `vite`.
4
+
5
+ ```
6
+ app/
7
+ routes.ts route("*", "routes/page.tsx") one route draws every page
8
+ entry.server.tsx one line
9
+ root.tsx createRootLoader + your layout and header
10
+ routes/page.tsx createPageLoader + your page
11
+ vite.config.ts plugins: [ace(), reactRouter()]
12
+ ```
13
+
14
+ ## Set up
15
+
16
+ **1. The Vite plugin** reads the settings from `.env` in development and makes sure the site uses one copy of React:
17
+
18
+ ```ts
19
+ // vite.config.ts
20
+ import ace from "@sandboxsonny/ace-kit/vite";
21
+ import { reactRouter } from "@react-router/dev/vite";
22
+ import { defineConfig } from "vite";
23
+
24
+ export default defineConfig({ plugins: [ace(), reactRouter()] });
25
+ ```
26
+
27
+ The settings are environment variables, read when the server starts, so one build runs anywhere. Put them in `.env` for development and in the host's environment for production:
28
+
29
+ | Variable | Default | Meaning |
30
+ | -------------------------- | ----------------------- | --------------------------------------------------------- |
31
+ | `ACE_API_URL` | `http://localhost:8080` | where the CMS Ace API lives |
32
+ | `ACE_SITE_DOMAIN` | `localhost` | the domain this site has in the CMS (the port is ignored) |
33
+ | `ACE_PREVIEW_ADMIN_ORIGIN` | `http://localhost:5173` | where the CMS admin app runs |
34
+
35
+ **2. The server entry**, which renders pages and lets the CMS's admin app (and only it) frame the site for [live preview](./live-preview.md):
36
+
37
+ ```tsx
38
+ // app/entry.server.tsx
39
+ export { handleRequest as default, streamTimeout } from "@sandboxsonny/ace-kit/react-router/server";
40
+ ```
41
+
42
+ It waits until a page is completely ready before answering (every page is fully loaded by its loader, and crawlers get whole pages). If you stream deferred data, write your own entry and call `addFramingPolicy(responseHeaders, origin)` in it.
43
+
44
+ **3. One route for every page:**
45
+
46
+ ```ts
47
+ // app/routes.ts
48
+ import { type RouteConfig, route } from "@react-router/dev/routes";
49
+
50
+ export default [route("*", "routes/page.tsx")] satisfies RouteConfig;
51
+ ```
52
+
53
+ ```tsx
54
+ // app/routes/page.tsx
55
+ import { AceNotice, createMeta } from "@sandboxsonny/ace-kit/react-router";
56
+ import { createPageLoader } from "@sandboxsonny/ace-kit/react-router/server";
57
+ import type { Route } from "./+types/page";
58
+
59
+ export const loader = createPageLoader();
60
+ export { headers } from "@sandboxsonny/ace-kit/react-router/server";
61
+ export const meta = createMeta({ siteName: "My Site" });
62
+
63
+ export default function Page({ loaderData: page }: Route.ComponentProps) {
64
+ return page.kind === "entry" ? <h1>{page.entry.title}</h1> : <AceNotice page={page} />;
65
+ }
66
+ ```
67
+
68
+ The loader reads the address (`/{content type}/{entry}`, and `/` is the `home` entry of `pages`), loads the entry and answers with the right status and headers: a missing page is a real 404, an unreachable CMS a 503, an expired preview a 403, `/pages/home` redirects to `/`, and a preview is never cached or indexed. `page.kind` is `entry`, `not-found`, `unavailable` or `preview-expired`. `createMeta` titles the page from the entry (followed by the site's name), adds its description, and asks search engines to ignore a preview. With [typed content](./typed-content.md), `createPageLoader<PageValues>()` gives `page.entry.values` its real shape.
69
+
70
+ **4. The root route** loads the header menu and keeps previews fresh:
71
+
72
+ ```tsx
73
+ // app/root.tsx
74
+ import { useAceMenu, useAcePreview } from "@sandboxsonny/ace-kit/react-router";
75
+ import { createRootLoader } from "@sandboxsonny/ace-kit/react-router/server";
76
+
77
+ export const loader = createRootLoader({
78
+ menu: "main-menu", // the CMS menu with this handle
79
+ fallback: [{ label: "Home", to: "/" }], // if the site has none yet, or the CMS is down
80
+ });
81
+
82
+ export default function App() {
83
+ useAcePreview(); // in the CMS's preview frame, redraws the page when the editor saves a draft
84
+ const links = useAceMenu(); // [{ label, to, children? }]
85
+ return (/* your header, <Outlet />, your footer */);
86
+ }
87
+ ```
88
+
89
+ `useAcePreview` and `useAceMenu` read what `createRootLoader` loaded, so call them in the root route (or anywhere below it). If your root loader loads more, spread ours into yours: `return { ...(await aceLoader(args)), other }`.
90
+
91
+ ## Components
92
+
93
+ From `@sandboxsonny/ace-kit/react-router` (these are safe in the browser):
94
+
95
+ - **AceRichText** draws a rich text field. Links to pages of this site are router `Link`s, so navigating doesn't reload the page. It renders bare elements; style them from a wrapper.
96
+ - **AceTextArea** draws a Textarea field (plain text): `multiline` (the default) makes paragraphs and line breaks, `multiline={false}` is one line of text.
97
+ - **AceImage** is an `<img>` with `src`, `srcset`, `sizes`, size and alt text. Set `sizes` to how wide it is shown, and `lazy={false}` for the image at the top of the page.
98
+ - **AceNotice** is the heading and message for a page that isn't an entry. Give it a `className`.
99
+
100
+ ## Reading content in your own loaders
101
+
102
+ From `@sandboxsonny/ace-kit/react-router/server`, with no client to create:
103
+
104
+ ```ts
105
+ import { getEntries, getEntry, getMenuLinks } from "@sandboxsonny/ace-kit/react-router/server";
106
+
107
+ export async function loader() {
108
+ return { posts: await getEntries("posts", { limit: 10 }) };
109
+ }
110
+ ```
111
+
112
+ `getAceClient()` is the client behind them. Import these only in loaders, `headers` and `entry.server.tsx`: React Router leaves them out of the browser bundle, which is what keeps the settings and the CMS address on the server.
113
+
114
+ ## Without the adapter
115
+
116
+ Everything the adapter does is built from the framework-free [core](./getting-started.md): `loadPage`, `loadMenuLinks`, `previewTokenFrom`, `addFramingPolicy`, the [React components](./react.md) and `usePreviewRefresh`. The adapter is about 150 lines; read it if you need to do something it doesn't.
package/docs/react.md ADDED
@@ -0,0 +1,81 @@
1
+ # React
2
+
3
+ Needs `react` 18 or newer. The components work with any React framework (React Router, Next.js, Remix, plain Vite). **On React Router 7, use the [React Router adapter](./react-router.md) instead of wiring this up yourself.** Fetch with the [client](./getting-started.md#the-client) on the server (a loader, a server component, `getServerSideProps`), then draw.
4
+
5
+ ```tsx
6
+ import { AceImage, AceRichText } from "@sandboxsonny/ace-kit/react";
7
+ ```
8
+
9
+ ## AceImage
10
+
11
+ ```tsx
12
+ <AceImage image={entry.values.hero} sizes="(min-width: 64rem) 64rem, 100vw" lazy={false} className="hero" />
13
+ ```
14
+
15
+ An `<img>` with `src`, `srcset`, `sizes`, `width`, `height` and `alt`. Any other `<img>` prop (`className`, `style`, `onLoad`...) is passed through. `lazy` defaults to `true`; turn it off for the image at the top of the page.
16
+
17
+ ## AceRichText
18
+
19
+ ```tsx
20
+ <div className="prose">
21
+ <AceRichText document={entry.values.body} />
22
+ </div>
23
+ ```
24
+
25
+ Draws a rich text field as React elements: no `dangerouslySetInnerHTML`, text is escaped by React, only the kinds of content the CMS allows are drawn, and link addresses are checked again. It renders bare elements, so style them from the wrapper.
26
+
27
+ Give it your router's link so navigation stays in the browser:
28
+
29
+ ```tsx
30
+ import { Link } from "react-router";
31
+
32
+ <AceRichText document={body} link={({ href, children }) => <Link to={href}>{children}</Link>} />;
33
+ ```
34
+
35
+ External links, `mailto:` and `tel:` links should stay plain `<a>`: have your `link` component check the address and fall back, if you mix them.
36
+
37
+ ## AceTextArea
38
+
39
+ ```tsx
40
+ <AceTextArea value={entry.values.summary} /> {/* paragraphs; a new line is a <br> */}
41
+ <p><AceTextArea value={entry.values.tagline} multiline={false} /></p> {/* one line, no element around it */}
42
+ ```
43
+
44
+ Draws a Textarea field (plain text, not rich text). Text is escaped by React.
45
+
46
+ ## Live preview
47
+
48
+ ```tsx
49
+ import { usePreviewRefresh } from "@sandboxsonny/ace-kit/react";
50
+ import { useRevalidator } from "react-router";
51
+
52
+ function PreviewRefresh({ adminOrigin }: { adminOrigin: string | null }) {
53
+ const { revalidate } = useRevalidator();
54
+ const refresh = useCallback(() => revalidate(), [revalidate]);
55
+ usePreviewRefresh(adminOrigin, refresh);
56
+ return null;
57
+ }
58
+ ```
59
+
60
+ Pass `adminOrigin` only for a page shown with a preview token (`previewTokenFrom(url)` tells you). See [Live preview](./live-preview.md) for the rest: call `addFramingPolicy(headers, origin)` on every response (React Router: `entry.server.tsx`), and pass `page.headers` on so a preview is never cached.
61
+
62
+ ## A route in React Router 7
63
+
64
+ ```tsx
65
+ // app/routes.ts: one route draws every page
66
+ export default [route("*", "routes/page.tsx")] satisfies RouteConfig;
67
+
68
+ // app/routes/page.tsx
69
+ export async function loader({ request, params }: Route.LoaderArgs) {
70
+ const page = await loadPage(ace, { slug: params["*"], url: new URL(request.url) });
71
+ if (page.kind === "redirect") return redirect(page.location, { status: page.status, headers: page.headers });
72
+ return data(page, { status: page.status, headers: page.headers });
73
+ }
74
+ export const headers: Route.HeadersFunction = ({ loaderHeaders }) => loaderHeaders;
75
+
76
+ export default function Page({ loaderData: page }: Route.ComponentProps) {
77
+ return page.kind === "entry" ? <h1>{page.entry.title}</h1> : <p>Not here</p>;
78
+ }
79
+ ```
80
+
81
+ Returning `data(page, { status })` (rather than throwing) keeps a 404 or 503 inside your layout, with the header and footer.
@@ -0,0 +1,53 @@
1
+ # Releasing
2
+
3
+ Versions and the changelog are managed with [Changesets](https://github.com/changesets/changesets), and releases are published to [npm](https://www.npmjs.com) by GitHub Actions. The package follows [semantic versioning](https://semver.org): patch for fixes, minor for backwards compatible additions, major for breaking changes. While the version is below 1.0.0, a minor release may contain breaking changes (say so in the changeset).
4
+
5
+ ## Day to day
6
+
7
+ Every change that someone using the kit would notice gets a changeset:
8
+
9
+ ```sh
10
+ npm run changeset
11
+ ```
12
+
13
+ It asks for the size and a summary, and writes a small file in `.changeset/`. Commit it with the change. Nothing else is needed.
14
+
15
+ ## Releasing
16
+
17
+ When changes are merged to `main`, the **Release** workflow opens (or updates) a pull request called "Version Packages". It bumps `package.json` and writes the summaries into `CHANGELOG.md`. Read it, then merge it: the workflow then publishes the new version to npm and tags it.
18
+
19
+ Each published version carries npm **provenance**: a public statement, signed by GitHub, that this exact package was built from this repository by this workflow. People can check it on the package's npm page.
20
+
21
+ ## One-time setup
22
+
23
+ 1. **Own the scope.** The package is `@sandboxsonny/ace-kit`; a scope on npm belongs to a user or an organisation of that name. [Create an npm account](https://www.npmjs.com/signup) called `sandboxsonny` (or an organisation with that name), turn on two-factor authentication, and check <https://www.npmjs.com/package/@sandboxsonny/ace-kit> doesn't already exist. If the name isn't available, pick another scope and follow "If the scope changes" below.
24
+ 2. **A token for the workflow.** On npm: Access Tokens → Generate New Token → _Granular access token_, with **Read and write** permission on the `@sandboxsonny` scope, and no expiry longer than you want to manage. Add it to this repository as the secret **`NPM_TOKEN`** (Settings → Secrets and variables → Actions).
25
+ 3. **A public repository.** Provenance needs one. Also turn on branch protection for `main` and require the CI checks, so a release can only come from reviewed, passing code.
26
+
27
+ ### The very first release
28
+
29
+ The first version can also be published by hand, from a clean checkout of `main`:
30
+
31
+ ```sh
32
+ npm login
33
+ npm run version # turns the pending changeset into 0.1.0 and the changelog (if not already done)
34
+ npm run release # runs the whole check, then publishes
35
+ ```
36
+
37
+ (Provenance is only added when publishing from GitHub Actions (the workflow turns it on with `NPM_CONFIG_PROVENANCE`), so a hand-published first version has none; later ones do. It is deliberately not in `package.json`: npm refuses to publish from a laptop when it is.)
38
+
39
+ ## What the checks protect
40
+
41
+ `npm run release` and CI run `npm run check`, which ends with `scripts/check-package.mjs`. It fails if a file the package points at is missing, if the command line tool has no `#!` line, if sources or tests would be published, or if `package.json` is not set up for publishing (scoped name, public access).
42
+
43
+ ## Trying it before it is published
44
+
45
+ `npm run pack:local` builds the package and packs it into `sandboxsonny-ace-kit-<version>.tgz`, which is exactly what would be published. Install that file in a site (`npm install /path/to/sandboxsonny-ace-kit-0.1.0.tgz`). Prefer this to linking the folder (`file:../ace-kit`): a link hides problems a real install has (missing files, a wrong `exports` map), and Astro cannot find the preview script through an absolute link from outside the project.
46
+
47
+ ## If the scope changes
48
+
49
+ The package name appears in `package.json`, in `src/helpers/package-name.ts` (generated types augment it; a test checks the two agree), in the docs, and in the integration code (`src/astro/integration.ts`). Search for `@sandboxsonny` and change it everywhere, then add a major changeset: consumers must change their imports.
50
+
51
+ ## Publishing to GitHub Packages instead
52
+
53
+ Possible, with more friction for everyone who installs: GitHub's npm registry asks for a token even for public packages. Set `publishConfig.registry` to `https://npm.pkg.github.com`, `registry-url` in the workflow to the same, `NODE_AUTH_TOKEN` to `${{ secrets.GITHUB_TOKEN }}`, give the job `packages: write`, and tell users to add `@sandboxsonny:registry=https://npm.pkg.github.com` and a `read:packages` token to their `.npmrc`. (Provenance isn't available there.)
@@ -0,0 +1,54 @@
1
+ # Rich text
2
+
3
+ A rich text field's value is a **document**: a small, safe JSON tree (no HTML in it).
4
+
5
+ ```json
6
+ {
7
+ "type": "doc",
8
+ "content": [
9
+ { "type": "heading", "attrs": { "level": 2 }, "content": [{ "type": "text", "text": "Hello" }] },
10
+ {
11
+ "type": "paragraph",
12
+ "content": [
13
+ { "type": "text", "text": "Read " },
14
+ { "type": "text", "text": "about us", "marks": [{ "type": "link", "attrs": { "href": "/pages/about" } }] }
15
+ ]
16
+ },
17
+ { "type": "image", "attrs": { "assetId": 12, "image": { "src": "...", "srcset": "...", "alt_text": "..." } } }
18
+ ]
19
+ }
20
+ ```
21
+
22
+ | Node | Notes |
23
+ | ------------------------------------- | ------------------------------------------------------------ |
24
+ | `paragraph`, `blockquote`, `listItem` | |
25
+ | `heading` | `attrs.level` 2, 3 or 4 |
26
+ | `bulletList`, `orderedList` | `orderedList` has `attrs.start` when it doesn't begin at 1 |
27
+ | `text` | `marks`: `bold`, `italic`, `link` |
28
+ | `hardBreak` | |
29
+ | `image` | `attrs.image` is the usual image object, resolved by the API |
30
+
31
+ Links arrive with their address already worked out: a link to an entry is `/{content type}/{entry}`. An image that was deleted is left out, and a link to a deleted entry keeps its words but loses the link.
32
+
33
+ ## Drawing it
34
+
35
+ - **Astro:** `<AceRichText document={...} />` ([Astro](./astro.md)).
36
+ - **React:** `<AceRichText document={...} />` ([React](./react.md)).
37
+ - **Anything else:** `renderRichText(document)` returns an HTML string. Every piece of text and every attribute is escaped, so it is safe to put on a page as it is.
38
+
39
+ All three draw only what is in the table, check link addresses again (only paths, `https://`, `http://`, `mailto:` and `tel:` are links), and leave out anything they don't know, so a newer CMS can't break an older site.
40
+
41
+ ## Plain text fields
42
+
43
+ A _Textarea_ field (not rich text) holds a plain string. `AceTextArea` draws it, and `multiline` says how:
44
+
45
+ ```astro
46
+ <AceTextArea value={entry.values.summary} />{" "}
47
+ <!-- <p>…</p><p>…</p>: blank lines start paragraphs, a new line is a <br> -->
48
+ <p>
49
+ <AceTextArea value={entry.values.tagline} multiline={false} />
50
+ </p>
51
+ <!-- one line of text, with no element around it -->
52
+ ```
53
+
54
+ It is the same component in Astro (`AceTextArea.astro`), React and React Router (`AceTextArea`). Text is escaped. For anything else, `renderTextArea(text, { multiline })` returns the same thing as an HTML string.
@@ -0,0 +1,50 @@
1
+ # Typed content
2
+
3
+ The kit can write TypeScript types for your site's content types, so `getEntry` and `getEntries` return the right shape and a change in the CMS shows up as a type error in your site.
4
+
5
+ ```sh
6
+ npx ace-kit generate --api-url http://localhost:8080 --domain localhost
7
+ ```
8
+
9
+ or with the settings in the environment:
10
+
11
+ ```sh
12
+ ACE_API_URL=http://localhost:8080 ACE_SITE_DOMAIN=localhost npx ace-kit generate
13
+ ```
14
+
15
+ It reads `GET /public/content-types` (no login: it describes field names and kinds, never entries) and writes `src/ace-kit.generated.d.ts` (change it with `--out`). Make sure the file is inside your `tsconfig` `include`. Commit it, and run the command again whenever content types change (a good moment is a CI step, or a `generate` script next to `dev`).
16
+
17
+ ```ts
18
+ // ace-kit.generated.d.ts (generated)
19
+ declare module "@sandboxsonny/ace-kit" {
20
+ interface AceContentTypes {
21
+ pages: {
22
+ headline: string; // required fields are always there
23
+ body?: AceRichTextDocument; // others may be missing
24
+ hero?: AceImageData;
25
+ };
26
+ }
27
+ }
28
+ ```
29
+
30
+ Now the content type's handle is checked and its values are typed:
31
+
32
+ ```ts
33
+ const about = await cms.getEntry("pages", "about"); // AceEntry<{ headline: string; body?: ...; hero?: ... }> | null
34
+ about?.values.headline; // string
35
+ cms.getEntry("pagez", "about"); // type error: no such content type
36
+ ```
37
+
38
+ | Field | Type |
39
+ | -------------------------------- | --------------------------------------------------- |
40
+ | Text, Textarea | `string` |
41
+ | Number | `number` |
42
+ | Checkbox | `boolean` |
43
+ | Select | a union of its options, e.g. `"formal" \| "casual"` |
44
+ | Image | `AceImageData` |
45
+ | Rich text | `AceRichTextDocument` |
46
+ | a kind this version doesn't know | `unknown` |
47
+
48
+ A field is optional unless it is marked required in the CMS. Before you generate, handles are plain `string` and values are `Record<string, unknown>`, so nothing breaks.
49
+
50
+ For a content type you use in a component, name its values once: `type PageValues = AceContentTypes["pages"]`.
package/package.json CHANGED
@@ -1,6 +1,160 @@
1
1
  {
2
2
  "name": "@sandboxsonny/ace-kit",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Everything a website needs to use CMS Ace: a typed client, addressing, images, menus, rich text, live preview, and Astro and React adapters.",
5
+ "keywords": [
6
+ "cms",
7
+ "cms-ace",
8
+ "headless-cms",
9
+ "astro",
10
+ "react"
11
+ ],
12
+ "license": "MIT",
13
+ "author": "Adams Development <sonny@adamsdevelopment.co.uk>",
14
+ "homepage": "https://github.com/sandboxSonny/ace-kit#readme",
15
+ "bugs": {
16
+ "url": "https://github.com/sandboxSonny/ace-kit/issues"
17
+ },
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/sandboxSonny/ace-kit.git"
21
+ },
22
+ "type": "module",
23
+ "sideEffects": [
24
+ "./dist/preview-script.js"
25
+ ],
26
+ "engines": {
27
+ "node": ">=22.12.0"
28
+ },
29
+ "files": [
30
+ "dist",
31
+ "astro",
32
+ "docs",
33
+ "CHANGELOG.md",
34
+ "LICENSE"
35
+ ],
36
+ "exports": {
37
+ ".": {
38
+ "types": "./dist/index.d.ts",
39
+ "import": "./dist/index.js"
40
+ },
41
+ "./react": {
42
+ "types": "./dist/react.d.ts",
43
+ "import": "./dist/react.js"
44
+ },
45
+ "./react-router": {
46
+ "types": "./dist/react-router.d.ts",
47
+ "import": "./dist/react-router.js"
48
+ },
49
+ "./react-router/server": {
50
+ "types": "./dist/react-router-server.d.ts",
51
+ "import": "./dist/react-router-server.js"
52
+ },
53
+ "./astro": {
54
+ "types": "./dist/astro.d.ts",
55
+ "import": "./dist/astro.js"
56
+ },
57
+ "./astro/integration": {
58
+ "types": "./dist/astro-integration.d.ts",
59
+ "import": "./dist/astro-integration.js"
60
+ },
61
+ "./astro/middleware": {
62
+ "import": "./dist/astro-middleware.js"
63
+ },
64
+ "./astro/*.astro": "./astro/*.astro",
65
+ "./preview-script": {
66
+ "import": "./dist/preview-script.js"
67
+ },
68
+ "./package.json": "./package.json",
69
+ "./vite": {
70
+ "types": "./dist/vite.d.ts",
71
+ "import": "./dist/vite.js"
72
+ }
73
+ },
74
+ "bin": {
75
+ "ace-kit": "./dist/cli.js"
76
+ },
77
+ "publishConfig": {
78
+ "access": "public"
79
+ },
80
+ "scripts": {
81
+ "build": "tsup",
82
+ "dev": "tsup --watch",
83
+ "test": "vitest run",
84
+ "test:watch": "vitest",
85
+ "lint": "eslint .",
86
+ "lint:fix": "eslint . --fix",
87
+ "format": "prettier --write .",
88
+ "format:check": "prettier --check .",
89
+ "typecheck": "tsc --noEmit",
90
+ "check": "npm run format:check && npm run lint && npm run typecheck && npm test && npm run build && npm run check:package",
91
+ "check:package": "node scripts/check-package.mjs",
92
+ "pack:local": "npm run build && npm pack",
93
+ "storybook": "storybook dev -p 6006",
94
+ "build:storybook": "storybook build",
95
+ "changeset": "changeset",
96
+ "version": "changeset version && prettier --write CHANGELOG.md package.json",
97
+ "release": "npm run check && changeset publish"
98
+ },
99
+ "peerDependencies": {
100
+ "astro": ">=5",
101
+ "react": ">=18",
102
+ "react-router": ">=7",
103
+ "react-dom": ">=18",
104
+ "@react-router/node": ">=7",
105
+ "vite": ">=5"
106
+ },
107
+ "peerDependenciesMeta": {
108
+ "astro": {
109
+ "optional": true
110
+ },
111
+ "react": {
112
+ "optional": true
113
+ },
114
+ "react-router": {
115
+ "optional": true
116
+ },
117
+ "react-dom": {
118
+ "optional": true
119
+ },
120
+ "@react-router/node": {
121
+ "optional": true
122
+ },
123
+ "vite": {
124
+ "optional": true
125
+ }
126
+ },
127
+ "devDependencies": {
128
+ "@changesets/cli": "^3.0.3",
129
+ "@eslint/js": "^10.0.1",
130
+ "@react-router/node": "^7.18.4",
131
+ "@storybook/addon-docs": "^10.6.1",
132
+ "@storybook/react-vite": "^10.6.1",
133
+ "@testing-library/dom": "^10.4.2",
134
+ "@testing-library/react": "^16.3.3",
135
+ "@types/node": "^22.20.5",
136
+ "@types/react": "^19.3.0",
137
+ "@types/react-dom": "^19.3.0",
138
+ "astro": "^7.3.8",
139
+ "eslint": "^10.12.0",
140
+ "eslint-config-prettier": "^10.1.8",
141
+ "eslint-plugin-astro": "^1.7.0",
142
+ "eslint-plugin-react-hooks": "^7.1.1",
143
+ "globals": "^17.13.0",
144
+ "jsdom": "^29.1.1",
145
+ "prettier": "^3.9.10",
146
+ "prettier-plugin-astro": "^1.1.1",
147
+ "react": "^19.3.0",
148
+ "react-dom": "^19.3.0",
149
+ "react-router": "^7.18.4",
150
+ "storybook": "^10.6.1",
151
+ "tsup": "^8.5.1",
152
+ "typescript": "^6.0.3",
153
+ "typescript-eslint": "^8.71.1",
154
+ "vite": "^8.3.4",
155
+ "vitest": "^5.0.4"
156
+ },
157
+ "overrides": {
158
+ "esbuild": "^0.28.2"
159
+ }
160
+ }