@gradial/aci 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 (64) hide show
  1. package/README.md +63 -0
  2. package/dist/astro/index.d.ts +17 -0
  3. package/dist/astro/index.js +21 -0
  4. package/dist/compiler/index.d.ts +2 -0
  5. package/dist/compiler/index.js +2 -0
  6. package/dist/compiler/validate-subset.d.ts +2 -0
  7. package/dist/compiler/validate-subset.js +73 -0
  8. package/dist/compiler/zod-to-jsonschema.d.ts +2 -0
  9. package/dist/compiler/zod-to-jsonschema.js +13 -0
  10. package/dist/content/contract.d.ts +83 -0
  11. package/dist/content/contract.js +104 -0
  12. package/dist/content/index.d.ts +6 -0
  13. package/dist/content/index.js +6 -0
  14. package/dist/content/provider.d.ts +15 -0
  15. package/dist/content/provider.js +36 -0
  16. package/dist/content/routes.d.ts +16 -0
  17. package/dist/content/routes.js +69 -0
  18. package/dist/content/tailwind-validator.d.ts +6 -0
  19. package/dist/content/tailwind-validator.js +31 -0
  20. package/dist/content/types.d.ts +105 -0
  21. package/dist/content/types.js +1 -0
  22. package/dist/content/validation.d.ts +108 -0
  23. package/dist/content/validation.js +184 -0
  24. package/dist/define-component.d.ts +2 -0
  25. package/dist/define-component.js +13 -0
  26. package/dist/define-layout.d.ts +3 -0
  27. package/dist/define-layout.js +6 -0
  28. package/dist/dev/browser.d.ts +7 -0
  29. package/dist/dev/browser.js +2 -0
  30. package/dist/dev/index.d.ts +30 -0
  31. package/dist/dev/index.js +70 -0
  32. package/dist/index.d.ts +3 -0
  33. package/dist/index.js +3 -0
  34. package/dist/next/dev-refresh.d.ts +5 -0
  35. package/dist/next/dev-refresh.js +44 -0
  36. package/dist/next/middleware.d.ts +8 -0
  37. package/dist/next/middleware.js +132 -0
  38. package/dist/next/preview.d.ts +7 -0
  39. package/dist/next/preview.js +37 -0
  40. package/dist/next/server.d.ts +58 -0
  41. package/dist/next/server.js +269 -0
  42. package/dist/providers/file.d.ts +18 -0
  43. package/dist/providers/file.js +91 -0
  44. package/dist/sveltekit/index.d.ts +2 -0
  45. package/dist/sveltekit/index.js +18 -0
  46. package/dist/testing/index.d.ts +20 -0
  47. package/dist/testing/index.js +86 -0
  48. package/dist/types/component.d.ts +26 -0
  49. package/dist/types/component.js +1 -0
  50. package/dist/types/config.d.ts +28 -0
  51. package/dist/types/config.js +1 -0
  52. package/dist/types/index.d.ts +6 -0
  53. package/dist/types/index.js +6 -0
  54. package/dist/types/layout.d.ts +12 -0
  55. package/dist/types/layout.js +1 -0
  56. package/dist/types/page.d.ts +45 -0
  57. package/dist/types/page.js +1 -0
  58. package/dist/types/render-mode.d.ts +3 -0
  59. package/dist/types/render-mode.js +1 -0
  60. package/dist/types/renderer.d.ts +71 -0
  61. package/dist/types/renderer.js +1 -0
  62. package/package.json +112 -0
  63. package/src/cli/compile-registry.mjs +199 -0
  64. package/src/cli/verify-renderer.mjs +73 -0
@@ -0,0 +1,105 @@
1
+ export type ButtonVariant = 'primary' | 'secondary' | 'tertiary' | 'outline' | 'ghost';
2
+ export interface KernelImage {
3
+ src: string;
4
+ alt?: string;
5
+ }
6
+ export interface KernelLink {
7
+ id: string;
8
+ label: string;
9
+ href: string;
10
+ variant?: ButtonVariant;
11
+ icon?: string;
12
+ }
13
+ export interface NavItem extends KernelLink {
14
+ }
15
+ export interface UtilityItem extends KernelLink {
16
+ }
17
+ export interface FooterLink extends KernelLink {
18
+ }
19
+ export interface FooterGroup {
20
+ id: string;
21
+ title: string;
22
+ links: FooterLink[];
23
+ }
24
+ export interface FooterColumn {
25
+ id: string;
26
+ title?: string;
27
+ groups?: FooterGroup[];
28
+ links?: FooterLink[];
29
+ }
30
+ export interface SiteFooter {
31
+ columns?: FooterColumn[];
32
+ social?: FooterLink[];
33
+ legalLinks?: FooterLink[];
34
+ copyright?: string;
35
+ }
36
+ export type KernelFooterGroup = FooterGroup;
37
+ export type KernelFooterColumn = FooterColumn;
38
+ export interface KernelPageSeo {
39
+ title?: string;
40
+ description?: string;
41
+ canonical?: string;
42
+ }
43
+ export interface KernelPageBlock {
44
+ id: string;
45
+ component: string;
46
+ props: Record<string, unknown>;
47
+ }
48
+ export interface KernelAtomicBlock {
49
+ id: string;
50
+ component: string;
51
+ props: Record<string, unknown>;
52
+ }
53
+ export interface KernelPage {
54
+ id: string;
55
+ $type: 'page';
56
+ status: string;
57
+ layout: string;
58
+ renderMode: string;
59
+ metadata: KernelPageSeo;
60
+ regions: Record<string, KernelPageBlock[]>;
61
+ }
62
+ export interface KernelSiteConfig {
63
+ id: string;
64
+ $type: 'site';
65
+ status: string;
66
+ title: string;
67
+ domain: string;
68
+ defaultLocale: string;
69
+ locales?: string[];
70
+ overlayResolution?: string[];
71
+ brandHref?: string;
72
+ brandLogo?: KernelImage;
73
+ navigation?: NavItem[];
74
+ utilities?: UtilityItem[];
75
+ promo?: Record<string, unknown>;
76
+ footer?: SiteFooter;
77
+ seo?: {
78
+ title?: string;
79
+ description?: string;
80
+ siteName?: string;
81
+ };
82
+ }
83
+ export interface KernelRouteMetadata {
84
+ title: string;
85
+ description: string;
86
+ canonical?: string;
87
+ siteName: string;
88
+ }
89
+ export interface RenderInput<TPage extends KernelPage = KernelPage, TSiteConfig extends KernelSiteConfig = KernelSiteConfig> {
90
+ route: string;
91
+ domain: string;
92
+ locale: string;
93
+ siteConfig: TSiteConfig;
94
+ page: TPage | null;
95
+ }
96
+ export interface RenderOutput {
97
+ route: string;
98
+ html: string;
99
+ assets: {
100
+ css: string[];
101
+ js: string[];
102
+ images: string[];
103
+ };
104
+ diagnostics: string[];
105
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,108 @@
1
+ import { z } from 'zod';
2
+ import { type AtomicBlockContract, type BlockContract, type ContractField } from './contract.js';
3
+ import type { KernelPage, KernelSiteConfig } from './types.js';
4
+ export interface ContentValidationContracts {
5
+ blockContracts?: readonly BlockContract[];
6
+ atomicBlockContracts?: readonly AtomicBlockContract[];
7
+ }
8
+ export interface ContentSchemas {
9
+ siteConfigSchema: z.ZodTypeAny;
10
+ atomicBlockSchema: z.ZodTypeAny;
11
+ pageBlockSchema: z.ZodTypeAny;
12
+ pageSchema: z.ZodTypeAny;
13
+ schemaFromContractField(field: ContractField): z.ZodTypeAny;
14
+ schemaFromContractFields(fields: Record<string, ContractField>): z.ZodTypeAny;
15
+ parseKernelSiteConfig(value: unknown, source?: string): KernelSiteConfig;
16
+ parseKernelPage(value: unknown, source?: string): KernelPage;
17
+ }
18
+ export declare const linkSchema: z.ZodObject<{
19
+ id: z.ZodString;
20
+ label: z.ZodString;
21
+ href: z.ZodString;
22
+ }, z.core.$loose>;
23
+ export declare const imageSchema: z.ZodObject<{
24
+ src: z.ZodString;
25
+ alt: z.ZodString;
26
+ }, z.core.$loose>;
27
+ export declare const optionalImageSchema: z.ZodObject<{
28
+ src: z.ZodString;
29
+ alt: z.ZodOptional<z.ZodString>;
30
+ }, z.core.$loose>;
31
+ export declare const siteConfigSchema: z.ZodObject<{
32
+ id: z.ZodString;
33
+ $type: z.ZodLiteral<"site">;
34
+ status: z.ZodString;
35
+ title: z.ZodString;
36
+ domain: z.ZodString;
37
+ defaultLocale: z.ZodString;
38
+ brandHref: z.ZodOptional<z.ZodString>;
39
+ brandLogo: z.ZodOptional<z.ZodObject<{
40
+ src: z.ZodString;
41
+ alt: z.ZodOptional<z.ZodString>;
42
+ }, z.core.$loose>>;
43
+ navigation: z.ZodOptional<z.ZodArray<z.ZodObject<{
44
+ id: z.ZodString;
45
+ label: z.ZodString;
46
+ href: z.ZodString;
47
+ }, z.core.$loose>>>;
48
+ utilities: z.ZodOptional<z.ZodArray<z.ZodObject<{
49
+ variant: z.ZodOptional<z.ZodEnum<{
50
+ primary: "primary";
51
+ secondary: "secondary";
52
+ tertiary: "tertiary";
53
+ outline: "outline";
54
+ ghost: "ghost";
55
+ }>>;
56
+ id: z.ZodString;
57
+ label: z.ZodString;
58
+ href: z.ZodString;
59
+ }, z.core.$loose>>>;
60
+ promo: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
61
+ footer: z.ZodOptional<z.ZodObject<{
62
+ columns: z.ZodOptional<z.ZodArray<z.ZodObject<{
63
+ id: z.ZodString;
64
+ title: z.ZodOptional<z.ZodString>;
65
+ groups: z.ZodOptional<z.ZodArray<z.ZodObject<{
66
+ id: z.ZodString;
67
+ title: z.ZodString;
68
+ links: z.ZodArray<z.ZodObject<{
69
+ id: z.ZodString;
70
+ label: z.ZodString;
71
+ href: z.ZodString;
72
+ }, z.core.$loose>>;
73
+ }, z.core.$loose>>>;
74
+ links: z.ZodOptional<z.ZodArray<z.ZodObject<{
75
+ id: z.ZodString;
76
+ label: z.ZodString;
77
+ href: z.ZodString;
78
+ }, z.core.$loose>>>;
79
+ }, z.core.$loose>>>;
80
+ social: z.ZodOptional<z.ZodArray<z.ZodObject<{
81
+ icon: z.ZodOptional<z.ZodString>;
82
+ id: z.ZodString;
83
+ label: z.ZodString;
84
+ href: z.ZodString;
85
+ }, z.core.$loose>>>;
86
+ legalLinks: z.ZodOptional<z.ZodArray<z.ZodObject<{
87
+ id: z.ZodString;
88
+ label: z.ZodString;
89
+ href: z.ZodString;
90
+ }, z.core.$loose>>>;
91
+ copyright: z.ZodOptional<z.ZodString>;
92
+ }, z.core.$loose>>;
93
+ seo: z.ZodOptional<z.ZodObject<{
94
+ title: z.ZodOptional<z.ZodString>;
95
+ description: z.ZodOptional<z.ZodString>;
96
+ siteName: z.ZodOptional<z.ZodString>;
97
+ }, z.core.$loose>>;
98
+ }, z.core.$loose>;
99
+ export declare function createContentSchemas(contracts?: ContentValidationContracts): ContentSchemas;
100
+ export declare function formatZodError(source: string, error: z.ZodError): string;
101
+ export declare function parseWithSchema(schema: z.ZodTypeAny, value: unknown, source: string): unknown;
102
+ export declare const atomicBlockSchema: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
103
+ export declare const pageBlockSchema: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
104
+ export declare const pageSchema: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
105
+ export declare const schemaFromContractField: (field: ContractField) => z.ZodTypeAny;
106
+ export declare const schemaFromContractFields: (fields: Record<string, ContractField>) => z.ZodTypeAny;
107
+ export declare const parseKernelSiteConfig: (value: unknown, source?: string) => KernelSiteConfig;
108
+ export declare const parseKernelPage: (value: unknown, source?: string) => KernelPage;
@@ -0,0 +1,184 @@
1
+ import { z } from 'zod';
2
+ import { defaultAtomicBlockContracts, defaultBlockContracts, } from './contract.js';
3
+ import { validateContentClassName, validateComponentClassName } from './tailwind-validator.js';
4
+ const linkFields = {
5
+ id: z.string().min(1),
6
+ label: z.string().min(1),
7
+ href: z.string().min(1),
8
+ };
9
+ export const linkSchema = z.looseObject(linkFields);
10
+ export const imageSchema = z.looseObject({
11
+ src: z.string().min(1),
12
+ alt: z.string().min(1),
13
+ });
14
+ export const optionalImageSchema = z.looseObject({
15
+ src: z.string().min(1),
16
+ alt: z.string().optional(),
17
+ });
18
+ export const siteConfigSchema = z.looseObject({
19
+ id: z.string().min(1),
20
+ $type: z.literal('site'),
21
+ status: z.string().min(1),
22
+ title: z.string().min(1),
23
+ domain: z.string().min(1),
24
+ defaultLocale: z.string().min(1),
25
+ brandHref: z.string().optional(),
26
+ brandLogo: optionalImageSchema.optional(),
27
+ navigation: z.array(linkSchema).optional(),
28
+ utilities: z.array(z.looseObject({
29
+ ...linkFields,
30
+ variant: z.enum(['primary', 'secondary', 'tertiary', 'outline', 'ghost']).optional(),
31
+ })).optional(),
32
+ promo: z.record(z.string(), z.unknown()).optional(),
33
+ footer: z.looseObject({
34
+ columns: z.array(z.looseObject({
35
+ id: z.string().min(1),
36
+ title: z.string().optional(),
37
+ groups: z.array(z.looseObject({
38
+ id: z.string().min(1),
39
+ title: z.string(),
40
+ links: z.array(linkSchema),
41
+ })).optional(),
42
+ links: z.array(linkSchema).optional(),
43
+ })).optional(),
44
+ social: z.array(z.looseObject({
45
+ ...linkFields,
46
+ icon: z.string().optional(),
47
+ })).optional(),
48
+ legalLinks: z.array(linkSchema).optional(),
49
+ copyright: z.string().optional(),
50
+ }).optional(),
51
+ seo: z.looseObject({
52
+ title: z.string().optional(),
53
+ description: z.string().optional(),
54
+ siteName: z.string().optional(),
55
+ }).optional(),
56
+ });
57
+ function withOptional(schema, field) {
58
+ return field.optional ? schema.optional() : schema;
59
+ }
60
+ export function createContentSchemas(contracts = {}) {
61
+ const blockContracts = contracts.blockContracts ?? defaultBlockContracts;
62
+ const atomicBlockContracts = contracts.atomicBlockContracts ?? defaultAtomicBlockContracts;
63
+ let atomicBlockSchema;
64
+ function schemaFromContractField(field) {
65
+ switch (field.kind) {
66
+ case 'atomicBlocks':
67
+ return withOptional(z.array(atomicBlockSchema), field);
68
+ case 'array':
69
+ return withOptional(z.array(schemaFromContractField(field.items)), field);
70
+ case 'boolean':
71
+ return withOptional(z.boolean(), field);
72
+ case 'className': {
73
+ const schema = z.string().check((ctx) => {
74
+ const result = validateContentClassName(ctx.value);
75
+ if (!result.valid) {
76
+ ctx.issues.push({
77
+ code: 'custom',
78
+ message: `Invalid Tailwind classes: ${result.invalidClasses.join(', ')}. Use standard utilities only; arbitrary bracket values are not allowed in content.`,
79
+ input: ctx.value,
80
+ });
81
+ }
82
+ });
83
+ return withOptional(schema, field);
84
+ }
85
+ case 'componentClassName': {
86
+ const schema = z.string().check((ctx) => {
87
+ const result = validateComponentClassName(ctx.value);
88
+ if (!result.valid) {
89
+ ctx.issues.push({
90
+ code: 'custom',
91
+ message: `Invalid Tailwind classes: ${result.invalidClasses.join(', ')}. Raw dimension/color bracket values (for example [12px] or [#fff]) are not allowed.`,
92
+ input: ctx.value,
93
+ });
94
+ }
95
+ });
96
+ return withOptional(schema, field);
97
+ }
98
+ case 'enum':
99
+ return withOptional(z.enum(field.values), field);
100
+ case 'image':
101
+ return withOptional(imageSchema, field);
102
+ case 'object':
103
+ return withOptional(schemaFromContractFields(field.fields), field);
104
+ case 'richText':
105
+ return withOptional(z.string(), field);
106
+ case 'string': {
107
+ const schema = typeof field.minLength === 'number'
108
+ ? z.string().min(field.minLength)
109
+ : z.string();
110
+ return withOptional(schema, field);
111
+ }
112
+ }
113
+ }
114
+ function schemaFromContractFields(fields) {
115
+ return z.looseObject(Object.fromEntries(Object.entries(fields).map(([key, field]) => [key, schemaFromContractField(field)])));
116
+ }
117
+ atomicBlockSchema = discriminatedContractUnion('atomic block', atomicBlockContracts, schemaFromContractFields);
118
+ const pageBlockSchema = discriminatedContractUnion('page block', blockContracts, schemaFromContractFields);
119
+ const pageSchema = z.looseObject({
120
+ id: z.string().min(1),
121
+ $type: z.literal('page'),
122
+ status: z.string().min(1),
123
+ layout: z.string().min(1),
124
+ renderMode: z.string().min(1),
125
+ metadata: z.looseObject({
126
+ title: z.string().optional(),
127
+ description: z.string().optional(),
128
+ canonical: z.string().optional(),
129
+ }),
130
+ regions: z.record(z.string(), z.array(pageBlockSchema)),
131
+ });
132
+ return {
133
+ siteConfigSchema,
134
+ atomicBlockSchema,
135
+ pageBlockSchema,
136
+ pageSchema,
137
+ schemaFromContractField,
138
+ schemaFromContractFields,
139
+ parseKernelSiteConfig(value, source = 'site.json') {
140
+ return parseWithSchema(siteConfigSchema, value, source);
141
+ },
142
+ parseKernelPage(value, source = '_index.json') {
143
+ return parseWithSchema(pageSchema, value, source);
144
+ },
145
+ };
146
+ }
147
+ function discriminatedContractUnion(label, contracts, schemaFromContractFields) {
148
+ if (contracts.length === 0) {
149
+ throw new Error(`At least one ${label} contract is required`);
150
+ }
151
+ const schemas = contracts.map((block) => z.looseObject({
152
+ id: z.string().min(1),
153
+ component: z.literal(block.component),
154
+ props: schemaFromContractFields(block.props),
155
+ }));
156
+ if (schemas.length === 1) {
157
+ return schemas[0];
158
+ }
159
+ return z.discriminatedUnion('component', schemas);
160
+ }
161
+ export function formatZodError(source, error) {
162
+ const issues = error.issues
163
+ .map((issue) => {
164
+ const path = issue.path.length ? issue.path.join('.') : '<root>';
165
+ return `- ${path}: ${issue.message}`;
166
+ })
167
+ .join('\n');
168
+ return `Invalid bare-metal content in ${source}:\n${issues}`;
169
+ }
170
+ export function parseWithSchema(schema, value, source) {
171
+ const parsed = schema.safeParse(value);
172
+ if (!parsed.success) {
173
+ throw new Error(formatZodError(source, parsed.error));
174
+ }
175
+ return parsed.data;
176
+ }
177
+ const defaultContentSchemas = createContentSchemas();
178
+ export const atomicBlockSchema = defaultContentSchemas.atomicBlockSchema;
179
+ export const pageBlockSchema = defaultContentSchemas.pageBlockSchema;
180
+ export const pageSchema = defaultContentSchemas.pageSchema;
181
+ export const schemaFromContractField = defaultContentSchemas.schemaFromContractField;
182
+ export const schemaFromContractFields = defaultContentSchemas.schemaFromContractFields;
183
+ export const parseKernelSiteConfig = defaultContentSchemas.parseKernelSiteConfig;
184
+ export const parseKernelPage = defaultContentSchemas.parseKernelPage;
@@ -0,0 +1,2 @@
1
+ import type { ComponentContract, ComponentDefinition } from './types/component.js';
2
+ export declare function defineComponent<TSchema = unknown>(definition: ComponentDefinition<TSchema>): ComponentContract<TSchema>;
@@ -0,0 +1,13 @@
1
+ export function defineComponent(definition) {
2
+ const renderModes = definition.renderModes ?? definition.render;
3
+ if (!renderModes) {
4
+ throw new Error(`component ${definition.name} must define renderModes`);
5
+ }
6
+ return {
7
+ ...definition,
8
+ renderModes,
9
+ render: renderModes,
10
+ varyDimensions: definition.varyDimensions ?? definition.vary,
11
+ vary: definition.vary ?? definition.varyDimensions,
12
+ };
13
+ }
@@ -0,0 +1,3 @@
1
+ import type { LayoutContract, LayoutDefinition, LayoutSlot } from './types/layout.js';
2
+ export declare function defineLayout(definition: LayoutDefinition): LayoutContract;
3
+ export declare function slot(name: string, required?: boolean): LayoutSlot;
@@ -0,0 +1,6 @@
1
+ export function defineLayout(definition) {
2
+ return definition;
3
+ }
4
+ export function slot(name, required = false) {
5
+ return { name, required };
6
+ }
@@ -0,0 +1,7 @@
1
+ export declare const DEFAULT_DEV_REFRESH_PORT = 24680;
2
+ export declare const DEFAULT_DEV_REFRESH_PATH = "/baremetal-dev";
3
+ export interface DevRefreshOptions {
4
+ wsPort?: number | string;
5
+ path?: string;
6
+ reconnectMs?: number;
7
+ }
@@ -0,0 +1,2 @@
1
+ export const DEFAULT_DEV_REFRESH_PORT = 24680;
2
+ export const DEFAULT_DEV_REFRESH_PATH = '/baremetal-dev';
@@ -0,0 +1,30 @@
1
+ import { type DevRefreshOptions } from './browser.js';
2
+ export { DEFAULT_DEV_REFRESH_PATH, DEFAULT_DEV_REFRESH_PORT, type DevRefreshOptions } from './browser.js';
3
+ declare global {
4
+ interface Window {
5
+ __bareMetalDevRefresh?: boolean;
6
+ }
7
+ }
8
+ export interface BareMetalContentWatchOptions extends DevRefreshOptions {
9
+ contentRoot?: string;
10
+ enabled?: boolean;
11
+ }
12
+ interface ViteDevServerLike {
13
+ watcher: {
14
+ add(path: string): void;
15
+ on(event: string, callback: (eventName: string, filePath: string) => void): void;
16
+ };
17
+ ws: {
18
+ send(payload: unknown): void;
19
+ };
20
+ }
21
+ export interface VitePluginLike {
22
+ name: string;
23
+ apply?: 'serve' | 'build';
24
+ configureServer?(server: ViteDevServerLike): void;
25
+ transformIndexHtml?(html: string): string;
26
+ }
27
+ export declare function devRefreshPort(options?: DevRefreshOptions): number;
28
+ export declare function devRefreshScript(options?: DevRefreshOptions): string;
29
+ export declare function devRefreshScriptTag(options?: DevRefreshOptions): string;
30
+ export declare function baremetalContentWatchPlugin(options?: BareMetalContentWatchOptions): VitePluginLike;
@@ -0,0 +1,70 @@
1
+ import path from 'node:path';
2
+ import { contentRoot } from '../content/routes.js';
3
+ import { DEFAULT_DEV_REFRESH_PATH, DEFAULT_DEV_REFRESH_PORT, } from './browser.js';
4
+ export { DEFAULT_DEV_REFRESH_PATH, DEFAULT_DEV_REFRESH_PORT } from './browser.js';
5
+ function numberOption(value, fallback) {
6
+ if (value === undefined || value === '') {
7
+ return fallback;
8
+ }
9
+ const parsed = Number(value);
10
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
11
+ }
12
+ export function devRefreshPort(options = {}) {
13
+ const value = options.wsPort ?? process.env.BARE_METAL_DEV_WS_PORT;
14
+ if (value === 0 || value === '0') {
15
+ return 0;
16
+ }
17
+ return numberOption(value, DEFAULT_DEV_REFRESH_PORT);
18
+ }
19
+ export function devRefreshScript(options = {}) {
20
+ const wsPort = devRefreshPort(options);
21
+ if (wsPort <= 0) {
22
+ return '';
23
+ }
24
+ const wsPath = options.path || DEFAULT_DEV_REFRESH_PATH;
25
+ const reconnectMs = numberOption(options.reconnectMs, 1000);
26
+ return `(() => {
27
+ if (typeof window === 'undefined' || window.__bareMetalDevRefresh) return;
28
+ window.__bareMetalDevRefresh = true;
29
+ const port = ${JSON.stringify(wsPort)};
30
+ const path = ${JSON.stringify(wsPath)};
31
+ const reconnectMs = ${JSON.stringify(reconnectMs)};
32
+ const connect = () => {
33
+ const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
34
+ const socket = new WebSocket(protocol + '//' + window.location.hostname + ':' + port + path);
35
+ socket.addEventListener('message', (event) => {
36
+ try {
37
+ const message = JSON.parse(event.data);
38
+ if (message && message.type === 'content-updated') window.location.reload();
39
+ } catch (_) {
40
+ }
41
+ });
42
+ socket.addEventListener('close', () => window.setTimeout(connect, reconnectMs));
43
+ };
44
+ connect();
45
+ })();`;
46
+ }
47
+ export function devRefreshScriptTag(options = {}) {
48
+ return `<script>${devRefreshScript(options)}</script>`;
49
+ }
50
+ export function baremetalContentWatchPlugin(options = {}) {
51
+ const enabled = options.enabled ?? process.env.BARE_METAL_DISABLE_CONTENT_WATCH !== '1';
52
+ return {
53
+ name: 'baremetal-content-watch',
54
+ apply: 'serve',
55
+ configureServer(server) {
56
+ if (!enabled) {
57
+ return;
58
+ }
59
+ const root = path.resolve(options.contentRoot || contentRoot());
60
+ server.watcher.add(root);
61
+ server.watcher.on('all', (_eventName, filePath) => {
62
+ const changed = path.resolve(filePath);
63
+ const relative = path.relative(root, changed);
64
+ if (relative && !relative.startsWith('..') && !path.isAbsolute(relative)) {
65
+ server.ws.send({ type: 'full-reload', path: '*' });
66
+ }
67
+ });
68
+ },
69
+ };
70
+ }
@@ -0,0 +1,3 @@
1
+ export * from './define-component.js';
2
+ export * from './define-layout.js';
3
+ export * from './types/index.js';
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export * from './define-component.js';
2
+ export * from './define-layout.js';
3
+ export * from './types/index.js';
@@ -0,0 +1,5 @@
1
+ import { type DevRefreshOptions } from '../dev/browser.js';
2
+ export interface DevRefreshProps extends DevRefreshOptions {
3
+ enabled?: boolean;
4
+ }
5
+ export declare function DevRefresh({ enabled, wsPort, path, reconnectMs, }: DevRefreshProps): null;
@@ -0,0 +1,44 @@
1
+ 'use client';
2
+ import { useEffect } from 'react';
3
+ import { DEFAULT_DEV_REFRESH_PATH, DEFAULT_DEV_REFRESH_PORT, } from '../dev/browser.js';
4
+ export function DevRefresh({ enabled = process.env.NODE_ENV !== 'production', wsPort = DEFAULT_DEV_REFRESH_PORT, path = DEFAULT_DEV_REFRESH_PATH, reconnectMs = 1000, }) {
5
+ useEffect(() => {
6
+ if (!enabled || Number(wsPort) <= 0 || typeof window === 'undefined' || window.__bareMetalDevRefresh) {
7
+ return undefined;
8
+ }
9
+ window.__bareMetalDevRefresh = true;
10
+ let reconnectTimer;
11
+ let socket;
12
+ let active = true;
13
+ const connect = () => {
14
+ const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
15
+ socket = new WebSocket(`${protocol}//${window.location.hostname}:${Number(wsPort)}${path}`);
16
+ socket.addEventListener('message', (event) => {
17
+ try {
18
+ const message = JSON.parse(event.data);
19
+ if (message?.type === 'content-updated') {
20
+ window.location.reload();
21
+ }
22
+ }
23
+ catch {
24
+ // Ignore non-Bare Metal dev server frames.
25
+ }
26
+ });
27
+ socket.addEventListener('close', () => {
28
+ if (active) {
29
+ reconnectTimer = setTimeout(connect, Number(reconnectMs));
30
+ }
31
+ });
32
+ };
33
+ connect();
34
+ return () => {
35
+ active = false;
36
+ window.__bareMetalDevRefresh = false;
37
+ if (reconnectTimer) {
38
+ clearTimeout(reconnectTimer);
39
+ }
40
+ socket?.close();
41
+ };
42
+ }, [enabled, wsPort, path, reconnectMs]);
43
+ return null;
44
+ }
@@ -0,0 +1,8 @@
1
+ import { type NextRequest } from 'next/server';
2
+ export interface GradialMiddlewareConfig {
3
+ siteId: string;
4
+ edgeConfig?: string;
5
+ previewSignKey?: string;
6
+ deploymentId?: string;
7
+ }
8
+ export declare function createGradialMiddleware(config: GradialMiddlewareConfig): (request: NextRequest) => Promise<Response>;