@bytesbrains/weblocks 0.2.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 (109) hide show
  1. package/AGENT.md +214 -0
  2. package/CATALOG.md +387 -0
  3. package/CHANGELOG.md +63 -0
  4. package/LICENSE +21 -0
  5. package/README.md +294 -0
  6. package/catalog.json +1850 -0
  7. package/lib/blocks/about.d.ts +2 -0
  8. package/lib/blocks/about.js +50 -0
  9. package/lib/blocks/accordion.d.ts +2 -0
  10. package/lib/blocks/accordion.js +51 -0
  11. package/lib/blocks/announcementBar.d.ts +2 -0
  12. package/lib/blocks/announcementBar.js +56 -0
  13. package/lib/blocks/appShell.d.ts +2 -0
  14. package/lib/blocks/appShell.js +54 -0
  15. package/lib/blocks/auth.d.ts +2 -0
  16. package/lib/blocks/auth.js +82 -0
  17. package/lib/blocks/blogList.d.ts +2 -0
  18. package/lib/blocks/blogList.js +81 -0
  19. package/lib/blocks/blogPost.d.ts +2 -0
  20. package/lib/blocks/blogPost.js +60 -0
  21. package/lib/blocks/carousel.d.ts +2 -0
  22. package/lib/blocks/carousel.js +56 -0
  23. package/lib/blocks/contactDetails.d.ts +2 -0
  24. package/lib/blocks/contactDetails.js +50 -0
  25. package/lib/blocks/contactForm.d.ts +2 -0
  26. package/lib/blocks/contactForm.js +100 -0
  27. package/lib/blocks/cta.d.ts +2 -0
  28. package/lib/blocks/cta.js +40 -0
  29. package/lib/blocks/divider.d.ts +2 -0
  30. package/lib/blocks/divider.js +28 -0
  31. package/lib/blocks/faq.d.ts +2 -0
  32. package/lib/blocks/faq.js +52 -0
  33. package/lib/blocks/features.d.ts +2 -0
  34. package/lib/blocks/features.js +58 -0
  35. package/lib/blocks/feed.d.ts +2 -0
  36. package/lib/blocks/feed.js +81 -0
  37. package/lib/blocks/footer.d.ts +2 -0
  38. package/lib/blocks/footer.js +40 -0
  39. package/lib/blocks/gallery.d.ts +2 -0
  40. package/lib/blocks/gallery.js +64 -0
  41. package/lib/blocks/hero.d.ts +2 -0
  42. package/lib/blocks/hero.js +48 -0
  43. package/lib/blocks/heroApp.d.ts +2 -0
  44. package/lib/blocks/heroApp.js +65 -0
  45. package/lib/blocks/logos.d.ts +2 -0
  46. package/lib/blocks/logos.js +59 -0
  47. package/lib/blocks/map.d.ts +2 -0
  48. package/lib/blocks/map.js +43 -0
  49. package/lib/blocks/nav.d.ts +2 -0
  50. package/lib/blocks/nav.js +39 -0
  51. package/lib/blocks/newsletter.d.ts +2 -0
  52. package/lib/blocks/newsletter.js +56 -0
  53. package/lib/blocks/pricing.d.ts +2 -0
  54. package/lib/blocks/pricing.js +89 -0
  55. package/lib/blocks/prose.d.ts +5 -0
  56. package/lib/blocks/prose.js +46 -0
  57. package/lib/blocks/richText.d.ts +2 -0
  58. package/lib/blocks/richText.js +46 -0
  59. package/lib/blocks/services.d.ts +2 -0
  60. package/lib/blocks/services.js +65 -0
  61. package/lib/blocks/sidebar.d.ts +2 -0
  62. package/lib/blocks/sidebar.js +58 -0
  63. package/lib/blocks/socialLinks.d.ts +2 -0
  64. package/lib/blocks/socialLinks.js +53 -0
  65. package/lib/blocks/spacer.d.ts +2 -0
  66. package/lib/blocks/spacer.js +25 -0
  67. package/lib/blocks/split.d.ts +2 -0
  68. package/lib/blocks/split.js +67 -0
  69. package/lib/blocks/stats.d.ts +2 -0
  70. package/lib/blocks/stats.js +64 -0
  71. package/lib/blocks/steps.d.ts +2 -0
  72. package/lib/blocks/steps.js +63 -0
  73. package/lib/blocks/tabs.d.ts +2 -0
  74. package/lib/blocks/tabs.js +57 -0
  75. package/lib/blocks/team.d.ts +2 -0
  76. package/lib/blocks/team.js +94 -0
  77. package/lib/blocks/testimonials.d.ts +2 -0
  78. package/lib/blocks/testimonials.js +54 -0
  79. package/lib/blocks/timeline.d.ts +2 -0
  80. package/lib/blocks/timeline.js +53 -0
  81. package/lib/blocks/video.d.ts +2 -0
  82. package/lib/blocks/video.js +61 -0
  83. package/lib/catalog.d.ts +26 -0
  84. package/lib/catalog.js +62 -0
  85. package/lib/generate.d.ts +35 -0
  86. package/lib/generate.js +160 -0
  87. package/lib/index.d.ts +19 -0
  88. package/lib/index.js +11 -0
  89. package/lib/ops.d.ts +85 -0
  90. package/lib/ops.js +248 -0
  91. package/lib/presets.d.ts +16 -0
  92. package/lib/presets.js +50 -0
  93. package/lib/pwa.d.ts +30 -0
  94. package/lib/pwa.js +79 -0
  95. package/lib/registry.d.ts +53 -0
  96. package/lib/registry.js +81 -0
  97. package/lib/render.d.ts +8 -0
  98. package/lib/render.js +105 -0
  99. package/lib/runtime.d.ts +68 -0
  100. package/lib/runtime.js +46 -0
  101. package/lib/schema.d.ts +67 -0
  102. package/lib/schema.js +150 -0
  103. package/lib/tokens.d.ts +37 -0
  104. package/lib/tokens.js +136 -0
  105. package/lib/types.d.ts +108 -0
  106. package/lib/types.js +10 -0
  107. package/lib/validate.d.ts +10 -0
  108. package/lib/validate.js +48 -0
  109. package/package.json +65 -0
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Design tokens → CSS custom properties (the shared "baseplate").
3
+ *
4
+ * Every brick styles itself from these variables, so a single token edit
5
+ * ("make the whole site warmer") restyles the entire page — coherence by
6
+ * construction. [normalizeTokens] is total: any missing field falls back to a
7
+ * default, so the renderer never sees a hole.
8
+ *
9
+ * Two derived colours — `--on-primary` / `--on-accent` — carry a legible text
10
+ * colour for anything sitting on a primary/accent fill, computed from the
11
+ * palette so contrast holds on ANY colours (no hardcoded `#fff`). And when
12
+ * `mode: 'auto'`, a `@media (prefers-color-scheme: dark)` block swaps the palette
13
+ * to the viewer's OS preference.
14
+ */
15
+ import type { DesignTokens, SectionOverrides } from './types.js';
16
+ export declare const DEFAULT_TOKENS: DesignTokens;
17
+ /**
18
+ * A legible text colour (near-black or white) for text on a given fill, by
19
+ * WCAG relative luminance. Total: any unparseable colour falls back to white.
20
+ */
21
+ export declare function readableOn(color: string): string;
22
+ /**
23
+ * Fill missing tokens with defaults AND coerce any out-of-range enum back to its
24
+ * default. Never throws. This is what keeps the renderer total: an AI-emitted
25
+ * `mode: "neon"` or `scale: "huge"` can't reach `tokensToCss` (which would index
26
+ * to `undefined`/`NaN`) — it lands on a valid value here first.
27
+ */
28
+ export declare function normalizeTokens(input: Partial<DesignTokens> | undefined): DesignTokens;
29
+ /** The `:root` variable block, plus a dark-palette override when `mode: 'auto'`. */
30
+ export declare function tokensToCss(tokens: DesignTokens): string;
31
+ /**
32
+ * Inline CSS-variable declarations for a block's opt-in per-section overrides —
33
+ * a palette subset (each re-deriving its on-fill colour) plus optional radius /
34
+ * spacing. Returns '' when there's nothing to scope. The caller escapes this for
35
+ * the `style` attribute.
36
+ */
37
+ export declare function sectionOverrideCss(o: SectionOverrides | undefined): string;
package/lib/tokens.js ADDED
@@ -0,0 +1,136 @@
1
+ export const DEFAULT_TOKENS = {
2
+ mode: 'light',
3
+ palette: {
4
+ bg: '#f7f4ee',
5
+ surface: '#ffffff',
6
+ text: '#2a2622',
7
+ muted: '#6b6459',
8
+ primary: '#3a5a40',
9
+ accent: '#c2703d',
10
+ },
11
+ typography: {
12
+ fontStack: "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif",
13
+ scale: 'default',
14
+ },
15
+ radius: 'soft',
16
+ spacing: 'default',
17
+ motion: 'subtle',
18
+ };
19
+ /** Fallback dark palette for `mode: 'auto'` when no `darkPalette` is supplied. */
20
+ const DARK_FALLBACK = {
21
+ bg: '#0d1117', surface: '#161b22', text: '#e6edf3', muted: '#8b949e', primary: '#4c8dff', accent: '#f778ba',
22
+ };
23
+ const SCALE_BASE_PX = { compact: 15, default: 16, expressive: 18 };
24
+ const RADIUS_PX = { sharp: '0px', soft: '12px', round: '24px' };
25
+ const SPACE_MULT = { tight: 0.8, default: 1, airy: 1.4 };
26
+ /** Coerce to one of `allowed`, else the default — so a bad enum never leaks into CSS. */
27
+ function oneOf(value, allowed, def) {
28
+ return allowed.includes(value) ? value : def;
29
+ }
30
+ /**
31
+ * A legible text colour (near-black or white) for text on a given fill, by
32
+ * WCAG relative luminance. Total: any unparseable colour falls back to white.
33
+ */
34
+ export function readableOn(color) {
35
+ const h = String(color ?? '').trim().replace(/^#/, '');
36
+ const full = h.length === 3 ? h.split('').map((c) => c + c).join('') : h;
37
+ if (!/^[0-9a-fA-F]{6}$/.test(full))
38
+ return '#ffffff';
39
+ const chan = (i) => parseInt(full.slice(i, i + 2), 16) / 255;
40
+ const lin = (c) => (c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4);
41
+ const L = 0.2126 * lin(chan(0)) + 0.7152 * lin(chan(2)) + 0.0722 * lin(chan(4));
42
+ return L > 0.45 ? '#111111' : '#ffffff';
43
+ }
44
+ /** The six palette roles + the two derived on-fill text colours. */
45
+ function paletteVars(p) {
46
+ return [
47
+ `--bg:${p.bg}`,
48
+ `--surface:${p.surface}`,
49
+ `--text:${p.text}`,
50
+ `--muted:${p.muted}`,
51
+ `--primary:${p.primary}`,
52
+ `--accent:${p.accent}`,
53
+ `--on-primary:${readableOn(p.primary)}`,
54
+ `--on-accent:${readableOn(p.accent)}`,
55
+ ];
56
+ }
57
+ /**
58
+ * Fill missing tokens with defaults AND coerce any out-of-range enum back to its
59
+ * default. Never throws. This is what keeps the renderer total: an AI-emitted
60
+ * `mode: "neon"` or `scale: "huge"` can't reach `tokensToCss` (which would index
61
+ * to `undefined`/`NaN`) — it lands on a valid value here first.
62
+ */
63
+ export function normalizeTokens(input) {
64
+ const t = input ?? {};
65
+ const d = DEFAULT_TOKENS;
66
+ const core = {
67
+ mode: oneOf(t.mode, ['light', 'dark', 'auto'], d.mode),
68
+ palette: { ...d.palette, ...(t.palette ?? {}) },
69
+ typography: {
70
+ fontStack: t.typography?.fontStack ?? d.typography.fontStack,
71
+ scale: oneOf(t.typography?.scale, ['compact', 'default', 'expressive'], d.typography.scale),
72
+ },
73
+ radius: oneOf(t.radius, ['sharp', 'soft', 'round'], d.radius),
74
+ spacing: oneOf(t.spacing, ['tight', 'default', 'airy'], d.spacing),
75
+ motion: oneOf(t.motion, ['none', 'subtle', 'lively'], d.motion),
76
+ };
77
+ // Keep a dark palette only when supplied (merged onto the fallback so a partial
78
+ // one is still complete). Consulted only for `mode: 'auto'`.
79
+ if (t.darkPalette)
80
+ core.darkPalette = { ...DARK_FALLBACK, ...t.darkPalette };
81
+ return core;
82
+ }
83
+ /** The `:root` variable block, plus a dark-palette override when `mode: 'auto'`. */
84
+ export function tokensToCss(tokens) {
85
+ const t = normalizeTokens(tokens);
86
+ const base = SCALE_BASE_PX[t.typography.scale];
87
+ const space = SPACE_MULT[t.spacing];
88
+ const sizeVars = [
89
+ `--font:${t.typography.fontStack}`,
90
+ `--fs-base:${base}px`,
91
+ `--fs-lg:${(base * 1.35).toFixed(1)}px`,
92
+ `--fs-xl:${(base * 2.4).toFixed(1)}px`,
93
+ `--radius:${RADIUS_PX[t.radius]}`,
94
+ `--space:${space}rem`,
95
+ `--space-lg:${(space * 3).toFixed(2)}rem`,
96
+ `--motion:${t.motion === 'none' ? '0ms' : t.motion === 'lively' ? '320ms' : '180ms'}`,
97
+ ];
98
+ const root = `:root{${[...paletteVars(t.palette), ...sizeVars].join(';')}}`;
99
+ if (t.mode !== 'auto')
100
+ return root;
101
+ // Only the palette (colours + on-fill text) flips with the OS theme; sizes,
102
+ // radius, spacing, and font stay put.
103
+ const dark = t.darkPalette ?? DARK_FALLBACK;
104
+ return `${root}\n@media(prefers-color-scheme:dark){:root{${paletteVars(dark).join(';')}}}`;
105
+ }
106
+ /**
107
+ * Inline CSS-variable declarations for a block's opt-in per-section overrides —
108
+ * a palette subset (each re-deriving its on-fill colour) plus optional radius /
109
+ * spacing. Returns '' when there's nothing to scope. The caller escapes this for
110
+ * the `style` attribute.
111
+ */
112
+ export function sectionOverrideCss(o) {
113
+ if (!o || typeof o !== 'object')
114
+ return '';
115
+ const decls = [];
116
+ const roles = [
117
+ ['bg', '--bg'], ['surface', '--surface'], ['text', '--text'],
118
+ ['muted', '--muted'], ['primary', '--primary'], ['accent', '--accent'],
119
+ ];
120
+ for (const [key, cssVar] of roles) {
121
+ const v = o[key];
122
+ if (typeof v === 'string' && v)
123
+ decls.push(`${cssVar}:${v}`);
124
+ }
125
+ if (typeof o.primary === 'string' && o.primary)
126
+ decls.push(`--on-primary:${readableOn(o.primary)}`);
127
+ if (typeof o.accent === 'string' && o.accent)
128
+ decls.push(`--on-accent:${readableOn(o.accent)}`);
129
+ if (o.radius && RADIUS_PX[o.radius] !== undefined)
130
+ decls.push(`--radius:${RADIUS_PX[o.radius]}`);
131
+ if (o.spacing && SPACE_MULT[o.spacing] !== undefined) {
132
+ const m = SPACE_MULT[o.spacing];
133
+ decls.push(`--space:${m}rem`, `--space-lg:${(m * 3).toFixed(2)}rem`);
134
+ }
135
+ return decls.join(';');
136
+ }
package/lib/types.d.ts ADDED
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Core types for the block engine (see docs/APPS_BLOCKS_ARCHITECTURE.md).
3
+ *
4
+ * A `SiteManifest` is the single source of truth the AI *composes and edits* —
5
+ * design tokens (the shared "baseplate") plus an ordered list of typed blocks
6
+ * (the "bricks"). It is never raw HTML: the renderer turns a manifest into a
7
+ * self-contained static document, and every reachable manifest is valid by
8
+ * construction (schema-validated before it is applied).
9
+ */
10
+ export type Mode = 'light' | 'dark' | 'auto';
11
+ export type Scale = 'compact' | 'default' | 'expressive';
12
+ export type Radius = 'sharp' | 'soft' | 'round';
13
+ export type Spacing = 'tight' | 'default' | 'airy';
14
+ export type Motion = 'none' | 'subtle' | 'lively';
15
+ /** The shared design system every block inherits (coherence = one edit changes the whole site). */
16
+ export interface Palette {
17
+ bg: string;
18
+ surface: string;
19
+ text: string;
20
+ muted: string;
21
+ primary: string;
22
+ accent: string;
23
+ }
24
+ export interface DesignTokens {
25
+ mode: Mode;
26
+ palette: Palette;
27
+ /**
28
+ * Optional dark-mode palette. Only consulted when `mode: 'auto'`: the base
29
+ * `palette` renders by default and this one takes over under
30
+ * `@media (prefers-color-scheme: dark)`. Absent + `auto` → a built-in default
31
+ * dark palette. Ignored for `mode: 'light' | 'dark'` (the palette is used as-is).
32
+ */
33
+ darkPalette?: Palette;
34
+ typography: {
35
+ fontStack: string;
36
+ scale: Scale;
37
+ };
38
+ radius: Radius;
39
+ spacing: Spacing;
40
+ motion: Motion;
41
+ }
42
+ /**
43
+ * Per-section design overrides (opt-in). A block may tint itself away from the
44
+ * shared baseplate for a subset of palette roles; the renderer scopes these as
45
+ * CSS custom properties on a wrapper so only that section is affected and the
46
+ * rest of the site stays coherent. Additive + optional — absent = inherit.
47
+ */
48
+ export type SectionOverrides = Partial<Palette> & {
49
+ radius?: Radius;
50
+ spacing?: Spacing;
51
+ };
52
+ /** One placed brick: a type from the closed catalog + its typed, validated config. */
53
+ export interface Block {
54
+ id: string;
55
+ type: string;
56
+ visible: boolean;
57
+ config: Record<string, unknown>;
58
+ /** Opt-in per-section palette overrides (§ theming). Absent = inherit tokens. */
59
+ overrides?: SectionOverrides;
60
+ }
61
+ export interface SiteMeta {
62
+ title: string;
63
+ description: string;
64
+ lang: string;
65
+ }
66
+ /**
67
+ * Optional PWA descriptor. When present the engine can emit a Web App Manifest
68
+ * + service worker alongside the HTML, and the document gains installability
69
+ * meta. Additive: a manifest without a `pwa` block renders exactly as before.
70
+ */
71
+ export interface PwaConfig {
72
+ name?: string;
73
+ shortName?: string;
74
+ description?: string;
75
+ themeColor?: string;
76
+ backgroundColor?: string;
77
+ display?: 'standalone' | 'fullscreen' | 'minimal-ui' | 'browser';
78
+ startUrl?: string;
79
+ scope?: string;
80
+ icons?: Array<{
81
+ src: string;
82
+ sizes: string;
83
+ type?: string;
84
+ purpose?: string;
85
+ }>;
86
+ /** Precache the static shell + offer an offline fallback. */
87
+ offline?: boolean;
88
+ }
89
+ /** Optional SEO/social descriptor emitted into <head>. Additive + optional. */
90
+ export interface SeoConfig {
91
+ canonical?: string;
92
+ ogTitle?: string;
93
+ ogDescription?: string;
94
+ ogImage?: string;
95
+ twitterCard?: 'summary' | 'summary_large_image';
96
+ noindex?: boolean;
97
+ }
98
+ export interface SiteManifest {
99
+ meta: SiteMeta;
100
+ design: DesignTokens;
101
+ blocks: Block[];
102
+ /** Bumped on each accepted edit → undo / history / draft-vs-publish. */
103
+ version: number;
104
+ /** Optional: emit a Web App Manifest + service worker (installable PWA). */
105
+ pwa?: PwaConfig;
106
+ /** Optional: SEO/social meta for <head>. */
107
+ seo?: SeoConfig;
108
+ }
package/lib/types.js ADDED
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Core types for the block engine (see docs/APPS_BLOCKS_ARCHITECTURE.md).
3
+ *
4
+ * A `SiteManifest` is the single source of truth the AI *composes and edits* —
5
+ * design tokens (the shared "baseplate") plus an ordered list of typed blocks
6
+ * (the "bricks"). It is never raw HTML: the renderer turns a manifest into a
7
+ * self-contained static document, and every reachable manifest is valid by
8
+ * construction (schema-validated before it is applied).
9
+ */
10
+ export {};
@@ -0,0 +1,10 @@
1
+ import type { SiteManifest } from './types.js';
2
+ export interface Validation {
3
+ ok: boolean;
4
+ errors: string[];
5
+ warnings: string[];
6
+ }
7
+ /** Validate one block's config against its brick schema. */
8
+ export declare function validateBlock(type: string, config: unknown): Validation;
9
+ /** Validate a whole manifest: shape + every block. */
10
+ export declare function validateManifest(manifest: SiteManifest): Validation;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Strict validation — the gate an *edit operation* passes before it is applied.
3
+ * `ok` keys off HARD errors only (wrong type / invalid enum / unknown type /
4
+ * duplicate id): those mean the op is malformed and must be rejected. Missing
5
+ * content (a headline not yet written) is a `warning` — the block renders with a
6
+ * placeholder, so it never blocks the edit.
7
+ */
8
+ import { parse } from './schema.js';
9
+ import { getSpec } from './registry.js';
10
+ /** Validate one block's config against its brick schema. */
11
+ export function validateBlock(type, config) {
12
+ const spec = getSpec(type);
13
+ if (!spec)
14
+ return { ok: false, errors: [`unknown block type: ${type}`], warnings: [] };
15
+ const r = parse(spec.schema, config ?? {});
16
+ return {
17
+ ok: r.ok,
18
+ errors: r.errors.map((e) => `${type}.${e}`),
19
+ warnings: r.warnings.map((w) => `${type}.${w}`),
20
+ };
21
+ }
22
+ /** Validate a whole manifest: shape + every block. */
23
+ export function validateManifest(manifest) {
24
+ const errors = [];
25
+ const warnings = [];
26
+ if (!manifest || typeof manifest !== 'object')
27
+ return { ok: false, errors: ['manifest: required'], warnings: [] };
28
+ if (!Array.isArray(manifest.blocks)) {
29
+ errors.push('blocks: must be an array');
30
+ }
31
+ else {
32
+ const seen = new Set();
33
+ manifest.blocks.forEach((b, i) => {
34
+ if (!b || typeof b.id !== 'string' || !b.id)
35
+ errors.push(`blocks[${i}].id: required`);
36
+ else if (seen.has(b.id))
37
+ errors.push(`blocks[${i}].id: duplicate "${b.id}"`);
38
+ else
39
+ seen.add(b.id);
40
+ const v = validateBlock(b?.type, b?.config);
41
+ for (const e of v.errors)
42
+ errors.push(`blocks[${i}].${e}`);
43
+ for (const w of v.warnings)
44
+ warnings.push(`blocks[${i}].${w}`);
45
+ });
46
+ }
47
+ return { ok: errors.length === 0, errors, warnings };
48
+ }
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@bytesbrains/weblocks",
3
+ "version": "0.2.0",
4
+ "description": "Block engine for AI-composable web apps — the AI composes a SiteManifest from a fixed block catalog; the engine validates and renders it to static HTML. Snap-together \"Lego\" web building blocks, shareable across repos.",
5
+ "keywords": [
6
+ "ai",
7
+ "llm",
8
+ "web-builder",
9
+ "site-generator",
10
+ "blocks",
11
+ "components",
12
+ "lego",
13
+ "manifest",
14
+ "static-site",
15
+ "structured-output",
16
+ "function-calling"
17
+ ],
18
+ "license": "MIT",
19
+ "author": "bytesbrains",
20
+ "type": "module",
21
+ "engines": { "node": ">=20" },
22
+ "sideEffects": false,
23
+ "main": "./lib/index.js",
24
+ "module": "./lib/index.js",
25
+ "types": "./lib/index.d.ts",
26
+ "exports": {
27
+ ".": {
28
+ "types": "./lib/index.d.ts",
29
+ "import": "./lib/index.js"
30
+ },
31
+ "./catalog.json": "./catalog.json"
32
+ },
33
+ "files": [
34
+ "lib",
35
+ "!lib/**/*.test.js",
36
+ "!lib/**/*.test.d.ts",
37
+ "!lib/example.*",
38
+ "catalog.json",
39
+ "CATALOG.md",
40
+ "AGENT.md",
41
+ "README.md",
42
+ "CHANGELOG.md",
43
+ "LICENSE"
44
+ ],
45
+ "repository": {
46
+ "type": "git",
47
+ "url": "git+https://github.com/bytesbrains/weblocks.git"
48
+ },
49
+ "homepage": "https://github.com/bytesbrains/weblocks#readme",
50
+ "bugs": { "url": "https://github.com/bytesbrains/weblocks/issues" },
51
+ "publishConfig": { "access": "public" },
52
+ "scripts": {
53
+ "build": "tsc -p tsconfig.json",
54
+ "emit:catalog": "node scripts/emit-catalog.mjs",
55
+ "test": "tsc -p tsconfig.json && node --test lib/*.test.js",
56
+ "example": "tsc -p tsconfig.json && node lib/example.js",
57
+ "ai": "tsc -p tsconfig.json && node scripts/ai-run.mjs",
58
+ "prepare": "npm run build",
59
+ "prepublishOnly": "npm run build && npm run emit:catalog && npm test"
60
+ },
61
+ "devDependencies": {
62
+ "@types/node": "^22.0.0",
63
+ "typescript": "^5.6.0"
64
+ }
65
+ }