@gradial/aci 0.1.20-preview.2 → 0.1.20

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 (249) hide show
  1. package/README.md +48 -110
  2. package/bin/{aci → aci-darwin-arm64} +0 -0
  3. package/bin/aci.js +99 -6
  4. package/dist/assets/index.d.ts +3 -3
  5. package/dist/assets/index.js +3 -3
  6. package/dist/astro/index.d.ts +14 -3
  7. package/dist/astro/index.js +24 -5
  8. package/dist/content/assets.d.ts +17 -0
  9. package/dist/content/assets.js +65 -0
  10. package/dist/content/index.d.ts +2 -0
  11. package/dist/content/index.js +2 -0
  12. package/dist/content/provider.d.ts +19 -0
  13. package/dist/content/provider.js +7 -0
  14. package/dist/content/resolve-slots.d.ts +9 -0
  15. package/dist/content/resolve-slots.js +24 -0
  16. package/dist/content/types.d.ts +1 -1
  17. package/dist/define-component.d.ts +1 -2
  18. package/dist/define-component.js +73 -11
  19. package/dist/define-layout.d.ts +2 -0
  20. package/dist/define-layout.js +3 -0
  21. package/dist/dev/content-watch.d.ts +37 -0
  22. package/dist/dev/content-watch.js +154 -0
  23. package/dist/dev/index.d.ts +24 -43
  24. package/dist/dev/index.js +4 -169
  25. package/dist/index.d.ts +14 -5
  26. package/dist/index.js +8 -8
  27. package/dist/next/asset-route.d.ts +2 -0
  28. package/dist/next/asset-route.js +12 -4
  29. package/dist/next/config.d.ts +2 -2
  30. package/dist/next/config.js +6 -6
  31. package/dist/next/content-watch.js +6 -1
  32. package/dist/next/index.d.ts +2 -2
  33. package/dist/next/index.js +2 -2
  34. package/dist/next/middleware.d.ts +7 -3
  35. package/dist/next/middleware.js +12 -7
  36. package/dist/next/page.d.ts +16 -15
  37. package/dist/next/page.js +30 -10
  38. package/dist/next/render.d.ts +5 -0
  39. package/dist/next/render.js +17 -0
  40. package/dist/next/root-layout.d.ts +4 -0
  41. package/dist/next/root-layout.js +5 -0
  42. package/dist/next/server.d.ts +14 -13
  43. package/dist/next/server.js +13 -3
  44. package/dist/providers/file.d.ts +2 -0
  45. package/dist/providers/file.js +61 -1
  46. package/dist/providers/s3.d.ts +1 -0
  47. package/dist/providers/s3.js +14 -1
  48. package/dist/react/Media.d.ts +24 -0
  49. package/dist/react/Media.js +30 -0
  50. package/dist/react/Picture.d.ts +12 -0
  51. package/dist/react/Picture.js +11 -0
  52. package/dist/react/VideoPlayer.d.ts +13 -0
  53. package/dist/react/{GradialVideoPlayer.js → VideoPlayer.js} +6 -5
  54. package/dist/react/index.d.ts +333 -3
  55. package/dist/react/index.js +89 -3
  56. package/dist/registry.d.ts +15 -0
  57. package/dist/registry.js +10 -0
  58. package/dist/testing/index.d.ts +3 -0
  59. package/dist/testing/index.js +15 -2
  60. package/dist/types/block-ref.d.ts +26 -0
  61. package/dist/types/block-ref.js +28 -0
  62. package/dist/types/component.d.ts +36 -41
  63. package/dist/types/config.d.ts +1 -2
  64. package/dist/types/image.d.ts +33 -20
  65. package/dist/types/image.js +42 -50
  66. package/dist/types/index.d.ts +1 -0
  67. package/dist/types/index.js +1 -0
  68. package/dist/types/link.d.ts +38 -0
  69. package/dist/types/link.js +58 -0
  70. package/dist/types/media.d.ts +16 -35
  71. package/dist/types/media.js +15 -84
  72. package/dist/types/page.d.ts +1 -2
  73. package/dist/types/render-mode.d.ts +0 -1
  74. package/dist/types/renderer.d.ts +1 -1
  75. package/dist/types/rich-text.d.ts +40 -0
  76. package/dist/types/rich-text.js +39 -0
  77. package/dist/types/video.d.ts +7 -13
  78. package/dist/types/video.js +3 -3
  79. package/package.json +32 -74
  80. package/src/cli/compile-registry.mjs +236 -59
  81. package/src/cli/validate-content.mjs +38 -75
  82. package/src/types/image.ts +79 -60
  83. package/templates/astro/template/.aci.yaml +27 -0
  84. package/templates/astro/template/.storybook/main.ts +79 -0
  85. package/templates/astro/template/.storybook/manager.ts +6 -0
  86. package/templates/astro/template/.storybook/preview.ts +33 -0
  87. package/templates/astro/template/astro.config.mjs +37 -0
  88. package/templates/astro/template/package.json.tmpl +41 -0
  89. package/templates/astro/template/src/cms/contracts/layouts/index.ts +18 -0
  90. package/templates/astro/template/src/cms/registry.ts +28 -0
  91. package/templates/astro/template/src/components/chrome/Footer.astro +107 -0
  92. package/templates/astro/template/src/components/chrome/Header.astro +94 -0
  93. package/templates/astro/template/src/components/elements/AtomicBlock.astro +15 -0
  94. package/templates/astro/template/src/components/elements/AtomicBlocks.astro +16 -0
  95. package/templates/astro/template/src/components/elements/Button.astro +51 -0
  96. package/templates/astro/template/src/components/elements/ButtonBlock.astro +25 -0
  97. package/templates/astro/template/src/components/elements/Container.astro +11 -0
  98. package/templates/astro/template/src/components/elements/Icon.astro +31 -0
  99. package/templates/astro/template/src/components/elements/Logo.astro +29 -0
  100. package/templates/astro/template/src/components/elements/RemoteImage.astro +41 -0
  101. package/templates/astro/template/src/components/sections/ContainerSection.astro +32 -0
  102. package/templates/astro/template/src/components/sections/FeatureCard.astro +23 -0
  103. package/templates/astro/template/src/components/sections/FeatureGrid.astro +35 -0
  104. package/templates/astro/template/src/components/sections/HomeHero.astro +51 -0
  105. package/templates/astro/template/src/components/sections/StatsBar.astro +33 -0
  106. package/templates/astro/template/src/design-system/styles.css +399 -0
  107. package/templates/astro/template/src/design-system/tokens/colors.css +60 -0
  108. package/templates/astro/template/src/design-system/tokens/components.css +69 -0
  109. package/templates/astro/template/src/design-system/tokens/index.css +6 -0
  110. package/templates/astro/template/src/design-system/tokens/radii.css +5 -0
  111. package/templates/astro/template/src/design-system/tokens/shadows.css +5 -0
  112. package/templates/astro/template/src/design-system/tokens/spacing.css +13 -0
  113. package/templates/astro/template/src/design-system/tokens/typography.css +24 -0
  114. package/templates/astro/template/src/env.d.ts +1 -0
  115. package/templates/astro/template/src/fixtures/atomicBlocks.ts +33 -0
  116. package/templates/astro/template/src/fixtures/composition.ts +31 -0
  117. package/templates/astro/template/src/fixtures/index.ts +17 -0
  118. package/templates/astro/template/src/fixtures/sections.ts +39 -0
  119. package/templates/astro/template/src/layouts/SiteLayout.astro +30 -0
  120. package/templates/astro/template/src/pages/[...slug].astro +61 -0
  121. package/templates/astro/template/src/render/RenderPage.astro +66 -0
  122. package/templates/astro/template/src/stories/CompositionStoryFrame.astro +15 -0
  123. package/templates/astro/template/src/stories/HomeHero.stories.ts +24 -0
  124. package/templates/astro/template/src/stories/SectionStoryFrame.astro +16 -0
  125. package/templates/astro/template/src/stories/StoryDocFrame.astro +31 -0
  126. package/templates/astro/template/tsconfig.json +11 -0
  127. package/templates/common/.agents/AGENTS.md +37 -0
  128. package/templates/common/.agents/skills/aci/SKILL.md +162 -0
  129. package/templates/common/.agents/skills/authoring/SKILL.md +590 -0
  130. package/templates/common/.agents/skills/authoring/references/block-registry.md +151 -0
  131. package/templates/common/.agents/skills/authoring/references/contracts.md +153 -0
  132. package/templates/common/.agents/skills/authoring/references/design-system.md +171 -0
  133. package/templates/common/.agents/skills/authoring/references/image-assets.md +73 -0
  134. package/templates/common/.agents/skills/authoring/references/repo-structure.md +157 -0
  135. package/templates/common/.agents/skills/migration/SKILL.md +160 -0
  136. package/templates/common/.agents/skills/migration/references/analyzing-source.md +215 -0
  137. package/templates/common/.agents/skills/migration/references/component-migration.md +200 -0
  138. package/templates/common/.agents/skills/migration/references/selector-map.md +31 -0
  139. package/templates/common/.content/.gradial-dam/blobs/sha256/7f/bf/7fbfc7fe230ff12688c496f660119e41d9725c4504d82e310c15665c73c09292 +0 -0
  140. package/templates/common/.content/.gradial-dam/blobs/sha256/a0/f5/a0f506689de9b15a7055f70103a3f02cd3ba105771ae2de6754df2ede1210d3a +0 -0
  141. package/templates/common/.content/.gradial-dam/blobs/sha256/dd/b7/ddb7e9642be08317bcd4b2bd26b58ba8d0d34dd7239e03e684a4f3634fa3845b +0 -0
  142. package/templates/common/.content/.gradial-dam/blobs/sha256/fb/32/fb3260e8286ea8231ea1c77b9c0be92d6edd9026565731eb14cfdcfd6b1b75b3 +0 -0
  143. package/templates/common/.content/config/site.json +192 -0
  144. package/templates/common/.content/dam/folders/seed/ast_site_about_hero/asset.json +9 -0
  145. package/templates/common/.content/dam/folders/seed/ast_site_about_hero/editorial.json +14 -0
  146. package/templates/common/.content/dam/folders/seed/ast_site_about_hero/governance.json +23 -0
  147. package/templates/common/.content/dam/folders/seed/ast_site_about_hero/versions/v0001.json +37 -0
  148. package/templates/common/.content/dam/folders/seed/ast_site_home_hero/asset.json +9 -0
  149. package/templates/common/.content/dam/folders/seed/ast_site_home_hero/editorial.json +14 -0
  150. package/templates/common/.content/dam/folders/seed/ast_site_home_hero/governance.json +23 -0
  151. package/templates/common/.content/dam/folders/seed/ast_site_home_hero/versions/v0001.json +37 -0
  152. package/templates/common/.content/fragments/navbar/_index.json +25 -0
  153. package/templates/common/.content/fragments/site-footer/_index.json +62 -0
  154. package/templates/common/.content/pages/about/_index.json +51 -0
  155. package/templates/common/.content/pages/home/_index.json +112 -0
  156. package/templates/common/.content/schemas/site.schema.json +123 -0
  157. package/templates/common/README.md.tmpl +45 -0
  158. package/templates/common/contracts/components/button.contract.ts +19 -0
  159. package/templates/common/contracts/components/container.contract.ts +19 -0
  160. package/templates/common/contracts/components/featureCard.contract.ts +15 -0
  161. package/templates/common/contracts/components/featureGrid.contract.ts +16 -0
  162. package/templates/common/contracts/components/homeHero.contract.ts +36 -0
  163. package/templates/common/contracts/components/index.ts +21 -0
  164. package/templates/common/contracts/components/siteFooter.contract.ts +45 -0
  165. package/templates/common/contracts/components/siteNavigation.contract.ts +30 -0
  166. package/templates/common/contracts/components/statsBar.contract.ts +19 -0
  167. package/templates/common/design-system/styles.css +346 -0
  168. package/templates/common/design-system/tokens/colors.css +110 -0
  169. package/templates/common/design-system/tokens/components.css +86 -0
  170. package/templates/common/design-system/tokens/index.css +6 -0
  171. package/templates/common/design-system/tokens/radii.css +5 -0
  172. package/templates/common/design-system/tokens/shadows.css +9 -0
  173. package/templates/common/design-system/tokens/spacing.css +26 -0
  174. package/templates/common/design-system/tokens/typography.css +32 -0
  175. package/templates/common/eslint-rules/no-raw-tailwind-values.js +127 -0
  176. package/templates/common/public/assets/Dwire-model.png +0 -0
  177. package/templates/common/public/assets/bare-metal-logo.png +0 -0
  178. package/templates/common/public/assets/meridian-logo.svg +8 -0
  179. package/templates/common/public/assets/seattle.avif +0 -0
  180. package/templates/nextjs/template/.aci.yaml +25 -0
  181. package/templates/nextjs/template/.storybook/main.ts +13 -0
  182. package/templates/nextjs/template/.storybook/preview.ts +34 -0
  183. package/templates/nextjs/template/eslint.config.mjs +36 -0
  184. package/templates/nextjs/template/next-env.d.ts +6 -0
  185. package/templates/nextjs/template/next.config.ts +8 -0
  186. package/templates/nextjs/template/package.json.tmpl +46 -0
  187. package/templates/nextjs/template/postcss.config.mjs +7 -0
  188. package/templates/nextjs/template/src/app/[[...slug]]/page.tsx +8 -0
  189. package/templates/nextjs/template/src/app/gradial/assets/[releaseId]/[...path]/route.ts +31 -0
  190. package/templates/nextjs/template/src/app/layout.tsx +29 -0
  191. package/templates/nextjs/template/src/cms/contracts/components/button.contract.ts +19 -0
  192. package/templates/nextjs/template/src/cms/contracts/components/container.contract.ts +20 -0
  193. package/templates/nextjs/template/src/cms/contracts/components/homeHero.contract.ts +36 -0
  194. package/templates/nextjs/template/src/cms/contracts/components/index.ts +15 -0
  195. package/templates/nextjs/template/src/cms/contracts/components/siteFooter.contract.ts +45 -0
  196. package/templates/nextjs/template/src/cms/contracts/components/siteNavigation.contract.ts +30 -0
  197. package/templates/nextjs/template/src/cms/contracts/layouts/index.ts +16 -0
  198. package/templates/nextjs/template/src/cms/registry.ts +28 -0
  199. package/templates/nextjs/template/src/components/chrome/Footer.tsx +86 -0
  200. package/templates/nextjs/template/src/components/chrome/Header.tsx +74 -0
  201. package/templates/nextjs/template/src/components/elements/AtomicBlock.tsx +13 -0
  202. package/templates/nextjs/template/src/components/elements/Button.tsx +40 -0
  203. package/templates/nextjs/template/src/components/elements/ButtonBlock.tsx +18 -0
  204. package/templates/nextjs/template/src/components/elements/Container.tsx +14 -0
  205. package/templates/nextjs/template/src/components/elements/Icon.tsx +29 -0
  206. package/templates/nextjs/template/src/components/elements/Logo.tsx +38 -0
  207. package/templates/nextjs/template/src/components/sections/ContainerSection.tsx +28 -0
  208. package/templates/nextjs/template/src/components/sections/FeatureCard.tsx +25 -0
  209. package/templates/nextjs/template/src/components/sections/FeatureGrid.tsx +36 -0
  210. package/templates/nextjs/template/src/components/sections/HomeHero.tsx +62 -0
  211. package/templates/nextjs/template/src/components/sections/StatsBar.tsx +30 -0
  212. package/templates/nextjs/template/src/middleware.ts +4 -0
  213. package/templates/nextjs/template/src/stories/Button.stories.tsx +38 -0
  214. package/templates/nextjs/template/src/stories/Header.stories.tsx +30 -0
  215. package/templates/nextjs/template/src/stories/HomeHero.stories.tsx +33 -0
  216. package/templates/nextjs/template/tsconfig.json +23 -0
  217. package/templates/nextjs/template/vercel.json +6 -0
  218. package/dist/content/cache.test.d.ts +0 -1
  219. package/dist/content/cache.test.js +0 -86
  220. package/dist/content/contract.d.ts +0 -83
  221. package/dist/content/contract.js +0 -104
  222. package/dist/content/tailwind-validator.d.ts +0 -6
  223. package/dist/content/tailwind-validator.js +0 -31
  224. package/dist/content/validation.d.ts +0 -108
  225. package/dist/content/validation.js +0 -184
  226. package/dist/next/preview-banner.d.ts +0 -4
  227. package/dist/next/preview-banner.js +0 -51
  228. package/dist/next/preview.d.ts +0 -7
  229. package/dist/next/preview.js +0 -37
  230. package/dist/react/GradialMedia.d.ts +0 -24
  231. package/dist/react/GradialMedia.js +0 -31
  232. package/dist/react/GradialPicture.d.ts +0 -14
  233. package/dist/react/GradialPicture.js +0 -30
  234. package/dist/react/GradialVideoPlayer.d.ts +0 -13
  235. package/dist/sveltekit/index.d.ts +0 -23
  236. package/dist/sveltekit/index.js +0 -62
  237. package/dist/sveltekit/preview.d.ts +0 -5
  238. package/dist/sveltekit/preview.js +0 -33
  239. package/src/cli/verify-renderer.mjs +0 -73
  240. package/src/types/component.ts +0 -59
  241. package/src/types/config.ts +0 -38
  242. package/src/types/data.ts +0 -47
  243. package/src/types/index.ts +0 -10
  244. package/src/types/layout.ts +0 -29
  245. package/src/types/media.ts +0 -125
  246. package/src/types/page.ts +0 -48
  247. package/src/types/render-mode.ts +0 -18
  248. package/src/types/renderer.ts +0 -83
  249. package/src/types/video.ts +0 -66
package/README.md CHANGED
@@ -1,149 +1,87 @@
1
1
  # Agentic Content Infrastructure SDK
2
2
 
3
- Agentic Content Infrastructure (ACI) sites implement the frontend contract by
4
- installing `@gradial/aci`, Zod 4, and TypeScript tooling. The
5
- SDK exposes component and layout helpers, renderer types, compiler tooling,
6
- content provider helpers, and framework-specific subpaths from one package.
3
+ `@gradial/aci` `0.1.20` is a pre-1.0 public-surface reset for the ACI
4
+ client SDK. Breaking changes from the old `0.1.x` accidental surface are
5
+ intentional: the root import is framework-neutral, framework runtime behavior is
6
+ behind explicit subpaths, and the first required customer path is React/Next.
7
7
 
8
- ## Getting Started
9
-
10
- The fastest way to create a new ACI site is via the scaffolding script:
8
+ ## Install
11
9
 
12
10
  ```bash
13
- npx tsx scripts/create-site.ts --framework astro --name my-site --dir ./my-site
14
- cd my-site
15
- npm run dev
11
+ npm install @gradial/aci@0.1.20 zod@^4
16
12
  ```
17
13
 
18
- Available frameworks: `astro`, `nextjs`, `sveltekit`.
19
-
20
- Generated starters include:
21
- - `.aci/docs/` — Understanding ACI, running, and testing guides
22
- - `.aci/guides/` — Component development, design system, content authoring
23
- - `.aci/migration/` — Migrating existing sites to ACI
24
- - Design system tokens, ESLint rules, and demo content
25
-
26
- See the [templates directory](templates/) for the canonical starter source.
27
-
28
- ## Installation
29
-
30
- ```bash
31
- npm install @gradial/aci zod@^4
32
- npm install -D typescript tsx
33
- ```
14
+ ## Root Import
34
15
 
35
- Common imports:
16
+ The root import is for framework-neutral contracts and value types only:
36
17
 
37
18
  ```ts
38
- import { defineComponentContract, defineLayoutContract, slot } from '@gradial/aci';
39
- import type { GradialRenderer } from '@gradial/aci';
40
- import type { KernelPage, KernelSiteConfig } from '@gradial/aci/content';
41
- import { createContentSchemas, stringField } from '@gradial/aci/content';
42
- import { FileContentProvider } from '@gradial/aci/providers/file';
19
+ import {
20
+ ImageSchema,
21
+ HeadingSchema,
22
+ LinkSchema,
23
+ RichTextValueSchema,
24
+ defineComponentContract,
25
+ type Image,
26
+ type Heading,
27
+ type Link,
28
+ type RichTextValue,
29
+ } from '@gradial/aci';
43
30
  ```
44
31
 
45
- Content contract helpers are also available through focused subpaths:
46
- `@gradial/aci/content/contract`, `@gradial/aci/content/validation`, and
47
- `@gradial/aci/content/tailwind-validator`.
48
-
49
- Framework helpers live under `@gradial/aci/astro`,
50
- `@gradial/aci/next/server`, `@gradial/aci/next/middleware`,
51
- `@gradial/aci/next/dev-refresh`, and `@gradial/aci/sveltekit`.
52
-
53
- ## Component Contracts vs Runtime Components
32
+ The root import does not load React, Next, Vite, Webpack, browser-only code, or
33
+ server providers.
54
34
 
55
- Keep compile-time CMS contracts separate from runtime component implementation.
56
- The ACI compiler imports only contract files, so those files must not import
57
- React, framework components, CSS, or browser-only code.
35
+ ## React/Next
58
36
 
59
- Recommended shape:
37
+ React renderers and bindings live under `@gradial/aci/react`:
60
38
 
61
- ```txt
62
- src/cms/contracts/components/hero.contract.ts
63
- src/cms/contracts/components/index.ts
64
- src/cms/contracts/layouts/index.ts
65
- src/components/Hero.tsx
66
- src/cms/renderBlock.tsx
39
+ ```tsx
40
+ import { Image, Link, Heading, RichText, bindReactComponent } from '@gradial/aci/react';
67
41
  ```
68
42
 
69
- Contract files own names, schemas, render modes, image slots, and other metadata:
43
+ Next integration helpers live under explicit Next subpaths:
70
44
 
71
45
  ```ts
72
- import { defineComponentContract, GradialImageSchema } from '@gradial/aci';
73
- import { z } from 'zod';
74
-
75
- export const heroContract = defineComponentContract({
76
- name: 'hero',
77
- schema: z.object({
78
- headline: z.string(),
79
- image: GradialImageSchema,
80
- }),
81
- renderModes: { canStatic: true, canSSR: true, canClientIsland: false },
82
- });
83
-
84
- export type HeroProps = z.infer<typeof heroContract.schema>;
46
+ import { createPage, withAci } from '@gradial/aci/next';
47
+ import { createMiddleware } from '@gradial/aci/next/middleware';
48
+ import { DevRefresh } from '@gradial/aci/next/dev-refresh';
85
49
  ```
86
50
 
87
- Runtime components may import the contract for types, but contract files should
88
- never import runtime components:
51
+ Astro public subpath is `@gradial/aci/astro`.
89
52
 
90
- ```tsx
91
- import type { HeroProps } from '../cms/contracts/components/hero.contract';
53
+ ## Server-Side Content Providers
92
54
 
93
- export function Hero(props: HeroProps) {
94
- return <section>{props.headline}</section>;
95
- }
96
- ```
97
-
98
- Testing helpers live under `@gradial/aci/testing`:
55
+ File and S3 providers are public server-only APIs for custom server composition,
56
+ such as blogs, events, product detail pages, and route-specific loaders:
99
57
 
100
58
  ```ts
101
- import { FixtureContentProvider } from '@gradial/aci/testing';
102
-
103
- const content = new FixtureContentProvider({
104
- pages: {
105
- '/': {
106
- id: 'home',
107
- $type: 'page',
108
- status: 'published',
109
- layout: 'marketing',
110
- renderMode: 'static',
111
- metadata: { title: 'Test Home' },
112
- regions: { main: [] }
113
- }
114
- }
115
- });
116
-
117
- const input = await content.loadRenderInput('/');
59
+ import { FileContentProvider } from '@gradial/aci/providers/file';
60
+ import { S3ContentProvider } from '@gradial/aci/providers/s3';
118
61
  ```
119
62
 
120
- Use this in unit tests when you want the same provider API as
121
- `FileContentProvider` without creating `.content` fixture files.
63
+ These providers are not browser/client-side fetch APIs. Client-side fetch should
64
+ go through an app-owned HTTP endpoint backed server-side by a `ContentProvider`.
122
65
 
123
- Publish from this package directory:
66
+ ## Scaffold
67
+
68
+ The first RC scaffold path is Next:
124
69
 
125
70
  ```bash
126
- npm publish --access public
71
+ npx @gradial/aci@0.1.20 init my-site --template next-starter
127
72
  ```
128
73
 
129
- The package includes a `prepack` script that builds `dist` before packing or
130
- publishing.
74
+ List available templates:
131
75
 
132
- ## Verify the Published Shape Locally
76
+ ```bash
77
+ npx @gradial/aci@0.1.20 init --list
78
+ ```
133
79
 
134
- Before publishing, inspect the exact tarball npm will upload. This catches
135
- package `files` mistakes where local source imports work from the repo but fail
136
- after npm filters the packed files.
80
+ ## Verify Locally
137
81
 
138
82
  From `packages/sdk`:
139
83
 
140
84
  ```bash
141
- tmpdir="$(mktemp -d /tmp/gradial-aci-pack.XXXXXX)"
142
- filename="$(npm pack --pack-destination "$tmpdir" | tail -n 1)"
143
- tar -tzf "$tmpdir/$filename" | sort | rg '^(package/src/cli/|package/src/types/|package/dist/types/)'
144
- echo "tarball=$tmpdir/$filename"
85
+ pnpm run build
86
+ pnpm run generate-starters
145
87
  ```
146
-
147
- If a shipped source CLI imports another source file, make sure both paths appear
148
- in the tarball. For example, `src/cli/compile-registry.mjs` imports
149
- `src/types/image.ts`, so both files must be included by `package.json#files`.
Binary file
package/bin/aci.js CHANGED
@@ -1,8 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import { spawn } from 'node:child_process';
3
3
  import { existsSync } from 'node:fs';
4
+ import { cp, mkdir, readdir, readFile, rename, stat, writeFile } from 'node:fs/promises';
4
5
  import { createRequire } from 'node:module';
5
- import { dirname, join, resolve } from 'node:path';
6
+ import { basename, dirname, join, relative, resolve } from 'node:path';
6
7
  import { fileURLToPath } from 'node:url';
7
8
 
8
9
  const require = createRequire(import.meta.url);
@@ -14,6 +15,7 @@ const platformPackages = {
14
15
  'darwin-x64': '@gradial/aci-cli-darwin-x64',
15
16
  'linux-arm64': '@gradial/aci-cli-linux-arm64',
16
17
  'linux-x64': '@gradial/aci-cli-linux-x64',
18
+ 'win32-arm64': '@gradial/aci-cli-win32-arm64',
17
19
  'win32-x64': '@gradial/aci-cli-win32-x64',
18
20
  };
19
21
 
@@ -21,7 +23,7 @@ const platformKey = `${process.platform}-${process.arch}`;
21
23
  const packageName = platformPackages[platformKey];
22
24
 
23
25
  if (!packageName) {
24
- console.error(`Unsupported platform for aci: ${platformKey}`);
26
+ console.error(`Unsupported platform for gradial-aci: ${platformKey}`);
25
27
  process.exit(1);
26
28
  }
27
29
 
@@ -29,9 +31,17 @@ const binName = process.platform === 'win32' ? 'aci.exe' : 'aci';
29
31
  const shimDir = dirname(fileURLToPath(import.meta.url));
30
32
  const repoRoot = resolve(shimDir, '../../..');
31
33
 
34
+ if (argv[0] === 'init') {
35
+ await runInit(argv.slice(1));
36
+ process.exit(0);
37
+ }
38
+
32
39
  function resolveBundledBinary() {
33
- const binaryPath = join(shimDir, binName);
34
- return existsSync(binaryPath) ? binaryPath : null;
40
+ const binaryPath = join(shimDir, `aci-${platformKey}${process.platform === 'win32' ? '.exe' : ''}`);
41
+ if (existsSync(binaryPath)) return binaryPath;
42
+ // Local development fallback for packages built before platform-suffixed bins.
43
+ const legacyPath = join(shimDir, binName);
44
+ return existsSync(legacyPath) ? legacyPath : null;
35
45
  }
36
46
 
37
47
  function resolvePackagedBinary() {
@@ -65,14 +75,24 @@ if (!binaryPath && !fallback) {
65
75
  }
66
76
 
67
77
  const child = binaryPath
68
- ? spawn(binaryPath, argv, { stdio: 'inherit' })
78
+ ? spawn(binaryPath, argv, {
79
+ env: {
80
+ ...process.env,
81
+ ACI_PACKAGE_ROOT: resolve(shimDir, '..'),
82
+ },
83
+ stdio: 'inherit',
84
+ })
69
85
  : spawn(fallback.command, fallback.args, {
70
86
  cwd: fallback.cwd,
87
+ env: {
88
+ ...process.env,
89
+ ACI_PACKAGE_ROOT: resolve(shimDir, '..'),
90
+ },
71
91
  stdio: 'inherit',
72
92
  });
73
93
 
74
94
  child.on('error', (error) => {
75
- console.error(`Failed to run aci: ${error.message}`);
95
+ console.error(`Failed to run gradial-aci: ${error.message}`);
76
96
  process.exit(1);
77
97
  });
78
98
 
@@ -155,3 +175,76 @@ function normalizePathArg(value) {
155
175
  function isAbsolutePath(value) {
156
176
  return value.startsWith('/') || /^[A-Za-z]:[\\/]/.test(value);
157
177
  }
178
+
179
+ async function runInit(args) {
180
+ if (args.includes('--list')) {
181
+ console.log('next-starter');
182
+ return;
183
+ }
184
+
185
+ const target = args.find((arg) => !arg.startsWith('-'));
186
+ const template = optionValue(args, '--template') || 'next-starter';
187
+ if (!target) {
188
+ throw new Error('usage: aci init <target> --template next-starter');
189
+ }
190
+ if (template !== 'next-starter') {
191
+ throw new Error(`unknown template "${template}". Available templates: next-starter`);
192
+ }
193
+
194
+ const targetDir = resolve(callerCwd, target);
195
+ await assertEmptyTarget(targetDir);
196
+ await mkdir(targetDir, { recursive: true });
197
+
198
+ const packageRoot = resolve(shimDir, '..');
199
+ await cp(join(packageRoot, 'templates', 'common'), targetDir, { recursive: true });
200
+ await cp(join(packageRoot, 'templates', 'nextjs', 'template'), targetDir, { recursive: true });
201
+ await renderTemplates(targetDir, {
202
+ siteName: basename(targetDir),
203
+ version: '0.1.0',
204
+ });
205
+ console.log(`Created ${relative(callerCwd, targetDir) || '.'} from next-starter`);
206
+ }
207
+
208
+ function optionValue(args, name) {
209
+ for (let index = 0; index < args.length; index += 1) {
210
+ const arg = args[index];
211
+ if (arg === name) return args[index + 1] || '';
212
+ if (arg.startsWith(`${name}=`)) return arg.slice(name.length + 1);
213
+ }
214
+ return '';
215
+ }
216
+
217
+ async function assertEmptyTarget(targetDir) {
218
+ try {
219
+ const info = await stat(targetDir);
220
+ if (!info.isDirectory()) {
221
+ throw new Error(`target exists and is not a directory: ${targetDir}`);
222
+ }
223
+ const entries = await readdir(targetDir);
224
+ if (entries.length > 0) {
225
+ throw new Error(`target directory is not empty: ${targetDir}`);
226
+ }
227
+ } catch (error) {
228
+ if (error?.code === 'ENOENT') return;
229
+ throw error;
230
+ }
231
+ }
232
+
233
+ async function renderTemplates(root, values) {
234
+ const entries = await readdir(root, { withFileTypes: true });
235
+ for (const entry of entries) {
236
+ const fullPath = join(root, entry.name);
237
+ if (entry.isDirectory()) {
238
+ await renderTemplates(fullPath, values);
239
+ continue;
240
+ }
241
+ if (!entry.name.endsWith('.tmpl')) {
242
+ continue;
243
+ }
244
+ const raw = await readFile(fullPath, 'utf-8');
245
+ const rendered = raw.replace(/\{\{\s*([A-Za-z0-9_]+)\s*\}\}/g, (_match, key) => values[key] ?? '');
246
+ const nextPath = fullPath.slice(0, -'.tmpl'.length);
247
+ await writeFile(fullPath, rendered);
248
+ await rename(fullPath, nextPath);
249
+ }
250
+ }
@@ -1,3 +1,3 @@
1
- export { type GradialImage, type ImageSource, type PictureSource, type ImageSlotContract, type SlotOutput, type ImageHTMLAttributes, GradialImageSchema, renderImageHTML, } from '../types/image.js';
2
- export { type GradialVideo, type VideoSource, GradialVideoSchema, VideoSourceSchema, } from '../types/video.js';
3
- export { type GradialAsset, GradialAssetSchema, isGradialImage, isGradialVideo, isGradialAsset, renderVideoHTML, renderAssetHTML, } from '../types/media.js';
1
+ export { type Image, type ImageSource, type ImageAttrs, ImageSchema, imageAttrs, primaryImageSource, } from '../types/image.js';
2
+ export { type Video, type VideoSource, VideoSchema, VideoSourceSchema, } from '../types/video.js';
3
+ export { type Asset, AssetSchema, isImage, isVideo, isAsset, } from '../types/media.js';
@@ -1,3 +1,3 @@
1
- export { GradialImageSchema, renderImageHTML, } from '../types/image.js';
2
- export { GradialVideoSchema, VideoSourceSchema, } from '../types/video.js';
3
- export { GradialAssetSchema, isGradialImage, isGradialVideo, isGradialAsset, renderVideoHTML, renderAssetHTML, } from '../types/media.js';
1
+ export { ImageSchema, imageAttrs, primaryImageSource, } from '../types/image.js';
2
+ export { VideoSchema, VideoSourceSchema, } from '../types/video.js';
3
+ export { AssetSchema, isImage, isVideo, isAsset, } from '../types/media.js';
@@ -1,5 +1,7 @@
1
1
  import type { RenderInput } from '../content/types.js';
2
- import { type GradialContentWatchOptions } from '../dev/index.js';
2
+ import { type ContentWatchOptions } from '../dev/content-watch.js';
3
+ import type { Registry, AnyComponent } from '../registry.js';
4
+ import type { BlockRef } from '../types/block-ref.js';
3
5
  interface AstroConfigSetupParams {
4
6
  injectScript(stage: 'head-inline' | 'page' | 'page-ssr', content: string): void;
5
7
  updateConfig(config: {
@@ -33,9 +35,18 @@ export declare function getGradialStaticPaths(): Promise<Array<{
33
35
  };
34
36
  }>>;
35
37
  export declare function getPageRuntimeRenderInput(request: Request): RenderInput | null;
38
+ export { createRegistry, registryLookup } from '../registry.js';
39
+ export type { Registry, RegistryEntry, AnyComponent } from '../registry.js';
40
+ export type { BlockRef } from '../types/block-ref.js';
41
+ export interface RenderableBlock {
42
+ Component: AnyComponent | undefined;
43
+ props: Record<string, unknown>;
44
+ }
45
+ export type RenderChildren = (blocks: readonly BlockRef[]) => RenderableBlock[];
46
+ export declare function createRenderChildren(registry: Registry): RenderChildren;
36
47
  export { getPreviewMode, createPreviewBannerHTML, createPreviewCookies, type PreviewContext, type PreviewCookie, } from './preview.js';
37
- export interface GradialAstroOptions extends GradialContentWatchOptions {
48
+ export interface AstroOptions extends ContentWatchOptions {
38
49
  /** Path to compiled content root. Defaults to ACI_CONTENT_ROOT env or '.aci/compiled' */
39
50
  compiledRoot?: string;
40
51
  }
41
- export declare function withGradialAci(options?: GradialAstroOptions): AstroIntegrationLike;
52
+ export declare function withAci(options?: AstroOptions): AstroIntegrationLike;
@@ -1,7 +1,8 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { getPendingRenderInputFromHeaders } from '../runtime/page.js';
4
- import { gradialContentWatchPlugin, gradialDamAssetPlugin, devRefreshPort, devRefreshScript, } from '../dev/index.js';
4
+ import { contentWatchPlugin, damAssetPlugin, devRefreshPort, } from '../dev/content-watch.js';
5
+ import { createDevRefreshBrowserScript } from '../dev/index.js';
5
6
  // ---------------------------------------------------------------------------
6
7
  // Static paths helper — for Astro SSG
7
8
  // ---------------------------------------------------------------------------
@@ -32,23 +33,41 @@ export function getPageRuntimeRenderInput(request) {
32
33
  return getPendingRenderInputFromHeaders(request.headers);
33
34
  }
34
35
  // ---------------------------------------------------------------------------
36
+ // Registry helpers
37
+ // ---------------------------------------------------------------------------
38
+ export { createRegistry, registryLookup } from '../registry.js';
39
+ export function createRenderChildren(registry) {
40
+ function renderChildren(blocks) {
41
+ return blocks.map((block) => {
42
+ const entry = registry.find((r) => r.id === block.component);
43
+ const hasBlockSlots = !!entry?.contract.blockSlots;
44
+ const props = { ...block.props };
45
+ if (hasBlockSlots) {
46
+ props.renderChildren = renderChildren;
47
+ }
48
+ return { Component: entry?.component, props };
49
+ });
50
+ }
51
+ return renderChildren;
52
+ }
53
+ // ---------------------------------------------------------------------------
35
54
  // Preview support
36
55
  // ---------------------------------------------------------------------------
37
56
  export { getPreviewMode, createPreviewBannerHTML, createPreviewCookies, } from './preview.js';
38
- export function withGradialAci(options = {}) {
57
+ export function withAci(options = {}) {
39
58
  return {
40
- name: 'gradial-astro',
59
+ name: 'aci-astro',
41
60
  hooks: {
42
61
  'astro:config:setup'({ injectScript, updateConfig }) {
43
62
  if (process.env.NODE_ENV === 'production') {
44
63
  return;
45
64
  }
46
65
  if (devRefreshPort(options) > 0) {
47
- injectScript('page', devRefreshScript(options));
66
+ injectScript('page', createDevRefreshBrowserScript(options));
48
67
  }
49
68
  updateConfig({
50
69
  vite: {
51
- plugins: [gradialDamAssetPlugin(options), gradialContentWatchPlugin(options)],
70
+ plugins: [damAssetPlugin(options), contentWatchPlugin(options)],
52
71
  },
53
72
  });
54
73
  },
@@ -0,0 +1,17 @@
1
+ export interface ReleaseAssetConfig {
2
+ bucket?: string;
3
+ keyPrefix?: string;
4
+ region?: string;
5
+ accessKeyId?: string;
6
+ secretAccessKey?: string;
7
+ sessionToken?: string;
8
+ endpoint?: string;
9
+ }
10
+ export declare class ReleaseAssetNotFoundError extends Error {
11
+ constructor(releaseId: string, assetPath: string, cause?: unknown);
12
+ }
13
+ export declare function getReleaseAssetResponse(input: {
14
+ releaseId: string;
15
+ assetPath: string;
16
+ config?: ReleaseAssetConfig;
17
+ }): Promise<Response>;
@@ -0,0 +1,65 @@
1
+ import { S3ContentProvider } from '../providers/s3.js';
2
+ export class ReleaseAssetNotFoundError extends Error {
3
+ constructor(releaseId, assetPath, cause) {
4
+ super(`ACI release asset not found: ${releaseId}/${assetPath}`);
5
+ this.name = 'ReleaseAssetNotFoundError';
6
+ this.cause = cause;
7
+ }
8
+ }
9
+ export async function getReleaseAssetResponse(input) {
10
+ const clean = cleanAssetPath(input.assetPath);
11
+ if (!input.releaseId || !clean) {
12
+ throw new ReleaseAssetNotFoundError(input.releaseId, input.assetPath);
13
+ }
14
+ const config = input.config ?? {};
15
+ const provider = new S3ContentProvider({
16
+ bucket: config.bucket,
17
+ keyPrefix: releaseKey(config, input.releaseId),
18
+ region: config.region,
19
+ accessKeyId: config.accessKeyId,
20
+ secretAccessKey: config.secretAccessKey,
21
+ sessionToken: config.sessionToken,
22
+ endpoint: config.endpoint,
23
+ });
24
+ const key = joinKey(provider.config.keyPrefix, 'assets', clean);
25
+ const res = await provider.fetchRaw(key);
26
+ if (!res.ok || !res.body) {
27
+ throw new ReleaseAssetNotFoundError(input.releaseId, clean, await safeResponseText(res));
28
+ }
29
+ const headers = new Headers();
30
+ copyHeader(res.headers, headers, 'content-type');
31
+ copyHeader(res.headers, headers, 'content-length');
32
+ copyHeader(res.headers, headers, 'etag');
33
+ copyHeader(res.headers, headers, 'last-modified');
34
+ headers.set('cache-control', 'public, max-age=31536000, immutable');
35
+ return new Response(res.body, { status: 200, headers });
36
+ }
37
+ function cleanAssetPath(value) {
38
+ const clean = value.replace(/^\/+|\/+$/g, '').replace(/\/+/g, '/');
39
+ if (!clean || clean.split('/').some((segment) => segment === '..' || segment === '.'))
40
+ return '';
41
+ return clean;
42
+ }
43
+ function copyHeader(from, to, name) {
44
+ const value = from.get(name);
45
+ if (value)
46
+ to.set(name, value);
47
+ }
48
+ async function safeResponseText(res) {
49
+ try {
50
+ return await res.text();
51
+ }
52
+ catch {
53
+ return '';
54
+ }
55
+ }
56
+ function releaseKey(config, releaseId) {
57
+ return joinKey(config.keyPrefix || process.env.ACI_S3_KEY_PREFIX || '', 'releases', releaseId);
58
+ }
59
+ function joinKey(...parts) {
60
+ return parts
61
+ .filter(Boolean)
62
+ .map((part) => part.replace(/^\/+|\/+$/g, ''))
63
+ .filter(Boolean)
64
+ .join('/');
65
+ }
@@ -2,3 +2,5 @@ export * from './provider.js';
2
2
  export * from './routes.js';
3
3
  export * from './types.js';
4
4
  export * from './cache.js';
5
+ export * from './resolve-slots.js';
6
+ export * from './assets.js';
@@ -2,3 +2,5 @@ export * from './provider.js';
2
2
  export * from './routes.js';
3
3
  export * from './types.js';
4
4
  export * from './cache.js';
5
+ export * from './resolve-slots.js';
6
+ export * from './assets.js';
@@ -12,17 +12,33 @@ export interface FragmentEntry {
12
12
  digest: string;
13
13
  fragmentRefs?: string[];
14
14
  }
15
+ export interface LayoutEntry {
16
+ name: string;
17
+ slots: LayoutSlot[];
18
+ defaults?: Record<string, FragmentRef[]>;
19
+ }
20
+ export interface LayoutSlot {
21
+ name: string;
22
+ required: boolean;
23
+ }
24
+ export interface FragmentRef {
25
+ kind: string;
26
+ fragmentId: string;
27
+ inline: boolean;
28
+ }
15
29
  export interface CompiledManifest {
16
30
  manifestVersion: string;
17
31
  buildDigest: string;
18
32
  siteConfigRef: string;
19
33
  routes: Record<string, RouteEntry>;
20
34
  fragments: Record<string, FragmentEntry>;
35
+ layouts?: Record<string, LayoutEntry>;
21
36
  }
22
37
  export interface ContentProvider {
23
38
  getSiteConfig<T = unknown>(): Promise<T>;
24
39
  getPage<T = unknown>(route: string): Promise<T>;
25
40
  getFragment<T = unknown>(id: string): Promise<T>;
41
+ getLayout<T = unknown>(name: string): Promise<T>;
26
42
  listRoutes(): Promise<RouteEntry[]>;
27
43
  }
28
44
  export declare class PageNotFoundError extends Error {
@@ -31,6 +47,9 @@ export declare class PageNotFoundError extends Error {
31
47
  export declare class FragmentNotFoundError extends Error {
32
48
  constructor(id: string, cause?: unknown);
33
49
  }
50
+ export declare class LayoutNotFoundError extends Error {
51
+ constructor(name: string, cause?: unknown);
52
+ }
34
53
  export interface RenderInputOptions {
35
54
  domain?: string;
36
55
  locale?: string;
@@ -16,6 +16,13 @@ export class FragmentNotFoundError extends Error {
16
16
  this.cause = cause;
17
17
  }
18
18
  }
19
+ export class LayoutNotFoundError extends Error {
20
+ constructor(name, cause) {
21
+ super(`Layout not found: ${name}`);
22
+ this.name = 'LayoutNotFoundError';
23
+ this.cause = cause;
24
+ }
25
+ }
19
26
  import { getCachedSiteConfig } from './cache.js';
20
27
  export async function loadRenderInput(provider, route = '/', options = {}) {
21
28
  const normalized = normalizeRoute(route);
@@ -0,0 +1,9 @@
1
+ import type { LayoutEntry } from './provider.js';
2
+ import type { KernelPageBlock } from './types.js';
3
+ export interface FragmentData {
4
+ id: string;
5
+ component: string;
6
+ props?: Record<string, unknown>;
7
+ }
8
+ export type FragmentLoader = (id: string) => Promise<FragmentData>;
9
+ export declare function resolveSlots(layout: LayoutEntry, pageRegions: Record<string, KernelPageBlock[]>, getFragment: FragmentLoader): Promise<Record<string, KernelPageBlock[]>>;
@@ -0,0 +1,24 @@
1
+ export async function resolveSlots(layout, pageRegions, getFragment) {
2
+ const resolved = {};
3
+ for (const slot of layout.slots) {
4
+ const pageBlocks = pageRegions[slot.name];
5
+ if (pageBlocks && pageBlocks.length > 0) {
6
+ resolved[slot.name] = pageBlocks;
7
+ continue;
8
+ }
9
+ const defaults = layout.defaults?.[slot.name];
10
+ if (defaults?.length) {
11
+ const blocks = [];
12
+ for (const ref of defaults) {
13
+ const fragment = await getFragment(ref.fragmentId);
14
+ blocks.push({
15
+ id: fragment.id,
16
+ component: fragment.component,
17
+ props: fragment.props || {},
18
+ });
19
+ }
20
+ resolved[slot.name] = blocks;
21
+ }
22
+ }
23
+ return resolved;
24
+ }
@@ -55,7 +55,6 @@ export interface KernelPage {
55
55
  $type: 'page';
56
56
  status: string;
57
57
  layout: string;
58
- renderMode: string;
59
58
  metadata: KernelPageSeo;
60
59
  regions: Record<string, KernelPageBlock[]>;
61
60
  }
@@ -93,6 +92,7 @@ export interface RenderInput<TPage extends KernelPage = KernelPage, TSiteConfig
93
92
  siteConfig: TSiteConfig;
94
93
  page: TPage | null;
95
94
  fragments?: Record<string, unknown>;
95
+ layouts?: Record<string, unknown>;
96
96
  }
97
97
  export interface RenderOutput {
98
98
  route: string;