kitfly 0.1.2 → 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.
- package/CHANGELOG.md +34 -0
- package/README.md +63 -16
- package/VERSION +1 -1
- package/dist/_raw/content/deployment/preflight.md +134 -0
- package/dist/_raw/content/deployment/recipes/aws-s3.md +128 -0
- package/dist/_raw/content/deployment/recipes/cloudflare-pages.md +73 -0
- package/dist/_raw/content/deployment/recipes/cloudflare-r2.md +156 -0
- package/dist/_raw/content/deployment/recipes/fly-io.md +57 -0
- package/dist/_raw/content/deployment/recipes/github-pages.md +112 -0
- package/dist/_raw/content/deployment/recipes/netlify.md +99 -0
- package/dist/_raw/content/deployment/recipes/vercel.md +88 -0
- package/dist/_raw/content/deployment/secrets-and-env-vars.md +75 -0
- package/dist/_raw/content/deployment.md +128 -0
- package/dist/_raw/content/guide/approaches.md +182 -0
- package/dist/_raw/content/guide/features.md +121 -0
- package/dist/_raw/content/guide/getting-started.md +112 -0
- package/dist/_raw/content/guide/kitfly-overview.md +209 -0
- package/dist/_raw/content/reference/configuration.md +259 -0
- package/dist/_raw/content/reference/design-catalog.md +167 -0
- package/dist/_raw/content/reference/environment-variables.md +66 -0
- package/dist/_raw/content/reference/glossary.md +92 -0
- package/dist/_raw/content/reference/key-concepts.md +118 -0
- package/dist/_raw/content/reference/plugins.md +220 -0
- package/dist/_raw/content/reference/structure.md +166 -0
- package/dist/_raw/content/reference.md +19 -0
- package/dist/_raw/content/templates/crucible.md +192 -0
- package/dist/_raw/content/templates/handbook.md +83 -0
- package/dist/_raw/content/templates/minimal.md +138 -0
- package/dist/_raw/content/templates/overview.md +187 -0
- package/dist/_raw/content/templates/pipeline.md +151 -0
- package/dist/_raw/content/templates/productbook.md +187 -0
- package/dist/_raw/content/templates/runbook.md +193 -0
- package/dist/_raw/content/templates/servicebook.md +163 -0
- package/dist/_raw/docs/decisions/ADR-0001-minimalist-site-code.md +118 -0
- package/dist/_raw/docs/decisions/ADR-0002-ai-accessibility.md +153 -0
- package/dist/_raw/docs/decisions/ADR-0003-single-file-bundle.md +93 -0
- package/dist/_raw/docs/decisions/ADR-0004-bun-runtime.md +98 -0
- package/dist/_raw/docs/decisions/ADR-0005-plugin-contract-and-distribution.md +110 -0
- package/dist/_raw/docs/decisions/DDR-0001-viewport-locked-layout.md +111 -0
- package/dist/_raw/docs/decisions/DDR-0002-theme-system.md +131 -0
- package/dist/_raw/docs/decisions/DDR-0003-bounded-logo-slot.md +106 -0
- package/dist/_raw/docs/decisions/DDR-0004-slides-rendering-model.md +113 -0
- package/dist/_raw/docs/decisions/DDR-0005-deterministic-layout-boundary.md +107 -0
- package/dist/_raw/docs/userguide/cli/build.md +85 -0
- package/dist/_raw/docs/userguide/cli/bundle.md +81 -0
- package/dist/_raw/docs/userguide/cli/dev.md +92 -0
- package/dist/_raw/docs/userguide/cli/init.md +116 -0
- package/dist/_raw/docs/userguide/cli/servers.md +69 -0
- package/dist/_raw/docs/userguide/cli/stop.md +76 -0
- package/dist/_raw/docs/userguide/cli/update.md +78 -0
- package/dist/_raw/docs/userguide/cli/version.md +65 -0
- package/dist/_raw/docs/userguide/cli.md +34 -0
- package/dist/_raw/docs/userguide/sharing.md +94 -0
- package/dist/_raw/schemas/plugin-schemas-notes.md +71 -0
- package/dist/_raw/schemas.md +42 -0
- package/dist/assets/brand/kitfly-favicon-32.png +0 -0
- package/dist/assets/brand/kitfly-icon-64.png +0 -0
- package/dist/assets/brand/kitfly-logo-128.png +0 -0
- package/dist/assets/brand/kitfly-logo-512.png +0 -0
- package/dist/assets/brand/kitfly-logo.svg +12132 -0
- package/dist/assets/brand/kitfly-neon-128.png +0 -0
- package/dist/assets/brand/kitfly-neon-192.png +0 -0
- package/dist/assets/brand/kitfly-neon-256.png +0 -0
- package/dist/assets/brand/kitfly-neon.png +0 -0
- package/dist/assets/brand/palette.md +75 -0
- package/dist/content/deployment/index.html +11 -0
- package/dist/content/deployment/preflight.html +418 -0
- package/dist/content/deployment/recipes/aws-s3.html +421 -0
- package/dist/content/deployment/recipes/cloudflare-pages.html +372 -0
- package/dist/content/deployment/recipes/cloudflare-r2.html +443 -0
- package/dist/content/deployment/recipes/fly-io.html +356 -0
- package/dist/content/deployment/recipes/github-pages.html +414 -0
- package/dist/content/deployment/recipes/index.html +11 -0
- package/dist/content/deployment/recipes/netlify.html +394 -0
- package/dist/content/deployment/recipes/vercel.html +382 -0
- package/dist/content/deployment/secrets-and-env-vars.html +380 -0
- package/dist/content/deployment.html +426 -0
- package/dist/content/guide/approaches.html +501 -0
- package/dist/content/guide/features.html +436 -0
- package/dist/content/guide/getting-started.html +403 -0
- package/dist/content/guide/index.html +11 -0
- package/dist/content/guide/kitfly-overview.html +544 -0
- package/dist/content/index.html +11 -0
- package/dist/content/reference/configuration.html +580 -0
- package/dist/content/reference/design-catalog.html +449 -0
- package/dist/content/reference/environment-variables.html +367 -0
- package/dist/content/reference/glossary.html +368 -0
- package/dist/content/reference/index.html +11 -0
- package/dist/content/reference/key-concepts.html +399 -0
- package/dist/content/reference/plugins.html +491 -0
- package/dist/content/reference/structure.html +463 -0
- package/dist/content/reference.html +334 -0
- package/dist/content/templates/crucible.html +546 -0
- package/dist/content/templates/handbook.html +405 -0
- package/dist/content/templates/index.html +11 -0
- package/dist/content/templates/minimal.html +447 -0
- package/dist/content/templates/overview.html +558 -0
- package/dist/content/templates/pipeline.html +494 -0
- package/dist/content/templates/productbook.html +540 -0
- package/dist/content/templates/runbook.html +543 -0
- package/dist/content/templates/servicebook.html +523 -0
- package/dist/content-index.json +540 -0
- package/dist/docs/decisions/ADR-0001-minimalist-site-code.html +491 -0
- package/dist/docs/decisions/ADR-0002-ai-accessibility.html +434 -0
- package/dist/docs/decisions/ADR-0003-single-file-bundle.html +412 -0
- package/dist/docs/decisions/ADR-0004-bun-runtime.html +409 -0
- package/dist/docs/decisions/ADR-0005-plugin-contract-and-distribution.html +402 -0
- package/dist/docs/decisions/DDR-0001-viewport-locked-layout.html +459 -0
- package/dist/docs/decisions/DDR-0002-theme-system.html +452 -0
- package/dist/docs/decisions/DDR-0003-bounded-logo-slot.html +423 -0
- package/dist/docs/decisions/DDR-0004-slides-rendering-model.html +399 -0
- package/dist/docs/decisions/DDR-0005-deterministic-layout-boundary.html +422 -0
- package/dist/docs/decisions/index.html +11 -0
- package/dist/docs/userguide/cli/build.html +408 -0
- package/dist/docs/userguide/cli/bundle.html +419 -0
- package/dist/docs/userguide/cli/dev.html +428 -0
- package/dist/docs/userguide/cli/index.html +11 -0
- package/dist/docs/userguide/cli/init.html +436 -0
- package/dist/docs/userguide/cli/servers.html +393 -0
- package/dist/docs/userguide/cli/stop.html +408 -0
- package/dist/docs/userguide/cli/update.html +406 -0
- package/dist/docs/userguide/cli/version.html +406 -0
- package/dist/docs/userguide/cli.html +386 -0
- package/dist/docs/userguide/index.html +11 -0
- package/dist/docs/userguide/sharing.html +465 -0
- package/dist/index.html +387 -0
- package/dist/llms.txt +18 -0
- package/dist/provenance.json +7 -0
- package/dist/schemas/index.html +11 -0
- package/dist/schemas/plugin-registry.schema.html +327 -0
- package/dist/schemas/plugin-schemas-notes.html +364 -0
- package/dist/schemas/plugin.schema.html +327 -0
- package/dist/schemas/plugins.schema.html +327 -0
- package/dist/schemas/v0/common.schema.html +386 -0
- package/dist/schemas/v0/index.html +11 -0
- package/dist/schemas/v0/plugin-registry.schema.html +547 -0
- package/dist/schemas/v0/plugin.schema.html +497 -0
- package/dist/schemas/v0/plugins.schema.html +406 -0
- package/dist/schemas/v0/site.schema.html +541 -0
- package/dist/schemas/v0/theme.schema.html +615 -0
- package/dist/schemas.html +351 -0
- package/dist/styles.css +1262 -0
- package/package.json +4 -2
- package/plugins-dist/callouts.css +32 -0
- package/plugins-dist/callouts.js +46 -0
- package/plugins-dist/slides-visuals.css +224 -0
- package/plugins-dist/slides-visuals.js +598 -0
- package/registry/plugins.yaml +35 -0
- package/schemas/README.md +10 -0
- package/schemas/plugin-registry.schema.json +5 -0
- package/schemas/plugin-schemas-notes.md +71 -0
- package/schemas/plugin.schema.json +5 -0
- package/schemas/plugins.schema.json +5 -0
- package/schemas/v0/common.schema.json +64 -0
- package/schemas/v0/plugin-registry.schema.json +225 -0
- package/schemas/v0/plugin.schema.json +175 -0
- package/schemas/v0/plugins.schema.json +84 -0
- package/schemas/v0/site.schema.json +56 -9
- package/schemas/v0/theme.schema.json +105 -22
- package/scripts/build.ts +155 -3
- package/scripts/bundle.ts +258 -95
- package/scripts/dev.ts +203 -1
- package/src/__tests__/build.test.ts +158 -1
- package/src/__tests__/bundle.test.ts +31 -0
- package/src/__tests__/cli.test.ts +14 -3
- package/src/__tests__/fixtures/fences/slides-visuals/invalid/bad-list-indent.md +5 -0
- package/src/__tests__/fixtures/fences/slides-visuals/invalid/blank-line.md +5 -0
- package/src/__tests__/fixtures/fences/slides-visuals/invalid/compare-object-items.md +9 -0
- package/src/__tests__/fixtures/fences/slides-visuals/invalid/indented-fence.md +4 -0
- package/src/__tests__/fixtures/fences/slides-visuals/invalid/stat-grid-missing-fields.md +5 -0
- package/src/__tests__/fixtures/fences/slides-visuals/invalid/unknown-type.md +3 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/compare.md +10 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/comparison-table.md +14 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/funnel.md +7 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/kpi.md +5 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/layer-cake.md +6 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/pyramid.md +6 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/quadrant-grid.md +8 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/scorecard.md +13 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/stat-grid.md +8 -0
- package/src/__tests__/init.test.ts +35 -0
- package/src/__tests__/plugin-loader.test.ts +221 -0
- package/src/__tests__/shared.test.ts +428 -0
- package/src/__tests__/slides-visuals-fence-contract.test.ts +28 -0
- package/src/__tests__/slides-visuals-runtime-regressions.bun.test.ts +114 -0
- package/src/__tests__/styles.test.ts +35 -0
- package/src/cli.ts +9 -4
- package/src/plugin-loader.ts +245 -0
- package/src/shared.ts +614 -7
- package/src/site/styles.css +331 -0
- package/src/site/template.html +66 -5
- package/src/templates/deck.ts +186 -0
- package/src/templates/driver.ts +11 -1
- package/src/templates/minimal.ts +1 -0
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Recipe: AWS S3 - Kitfly Docs</title>
|
|
7
|
+
<link rel="icon" type="image/png" sizes="32x32" href="../../../assets/brand/kitfly-favicon-32.png">
|
|
8
|
+
<link rel="icon" type="image/png" sizes="64x64" href="../../../assets/brand/kitfly-neon-256.png">
|
|
9
|
+
<link rel="stylesheet" href="../../../styles.css">
|
|
10
|
+
<style id="kitfly-theme">
|
|
11
|
+
:root { --color-bg: #ffffff;
|
|
12
|
+
--color-bg-sidebar: #f5f7f8;
|
|
13
|
+
--color-text: #374151;
|
|
14
|
+
--color-text-muted: #6b7280;
|
|
15
|
+
--color-border: #e5e7eb;
|
|
16
|
+
--color-link: #007182;
|
|
17
|
+
--color-link-hover: #0a6172;
|
|
18
|
+
--color-accent: #152F46;
|
|
19
|
+
--color-code-bg: #f5f7f8;
|
|
20
|
+
--color-logo: #152F46;
|
|
21
|
+
--sidebar-width: 280px;
|
|
22
|
+
--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
23
|
+
--font-headings: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
24
|
+
--font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace; }
|
|
25
|
+
html { font-size: 16px; }
|
|
26
|
+
@media (prefers-color-scheme: dark) {
|
|
27
|
+
:root:not([data-theme="light"]) { --color-bg: #0d1117;
|
|
28
|
+
--color-bg-sidebar: #152F46;
|
|
29
|
+
--color-text: #e5e7eb;
|
|
30
|
+
--color-text-muted: #9ca3af;
|
|
31
|
+
--color-border: #374151;
|
|
32
|
+
--color-link: #709EA6;
|
|
33
|
+
--color-link-hover: #8fb5bc;
|
|
34
|
+
--color-accent: #f9fafb;
|
|
35
|
+
--color-code-bg: #152F46;
|
|
36
|
+
--color-logo: #f9fafb; }
|
|
37
|
+
}
|
|
38
|
+
[data-theme="dark"] { --color-bg: #0d1117;
|
|
39
|
+
--color-bg-sidebar: #152F46;
|
|
40
|
+
--color-text: #e5e7eb;
|
|
41
|
+
--color-text-muted: #9ca3af;
|
|
42
|
+
--color-border: #374151;
|
|
43
|
+
--color-link: #709EA6;
|
|
44
|
+
--color-link-hover: #8fb5bc;
|
|
45
|
+
--color-accent: #f9fafb;
|
|
46
|
+
--color-code-bg: #152F46;
|
|
47
|
+
--color-logo: #f9fafb; }
|
|
48
|
+
[data-theme="light"] { --color-bg: #ffffff;
|
|
49
|
+
--color-bg-sidebar: #f5f7f8;
|
|
50
|
+
--color-text: #374151;
|
|
51
|
+
--color-text-muted: #6b7280;
|
|
52
|
+
--color-border: #e5e7eb;
|
|
53
|
+
--color-link: #007182;
|
|
54
|
+
--color-link-hover: #0a6172;
|
|
55
|
+
--color-accent: #152F46;
|
|
56
|
+
--color-code-bg: #f5f7f8;
|
|
57
|
+
--color-logo: #152F46;
|
|
58
|
+
--sidebar-width: 280px;
|
|
59
|
+
--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
60
|
+
--font-headings: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
61
|
+
--font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace; }
|
|
62
|
+
</style>
|
|
63
|
+
<!-- Syntax highlighting - Prism.js -->
|
|
64
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/prismjs@1/themes/prism.min.css" id="prism-light">
|
|
65
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/prismjs@1/themes/prism-okaidia.min.css" id="prism-dark" disabled>
|
|
66
|
+
|
|
67
|
+
<script>
|
|
68
|
+
// Apply saved theme immediately to prevent flash
|
|
69
|
+
(function() {
|
|
70
|
+
const saved = localStorage.getItem('theme');
|
|
71
|
+
if (saved) {
|
|
72
|
+
document.documentElement.setAttribute('data-theme', saved);
|
|
73
|
+
}
|
|
74
|
+
// Set Prism theme based on saved or system preference
|
|
75
|
+
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
76
|
+
const isDark = saved === 'dark' || (!saved && prefersDark);
|
|
77
|
+
if (isDark) {
|
|
78
|
+
document.getElementById('prism-light')?.setAttribute('disabled', '');
|
|
79
|
+
document.getElementById('prism-dark')?.removeAttribute('disabled');
|
|
80
|
+
}
|
|
81
|
+
})();
|
|
82
|
+
</script>
|
|
83
|
+
</head>
|
|
84
|
+
<body class="mode-docs">
|
|
85
|
+
<div class="mobile-header">
|
|
86
|
+
<button class="nav-toggle" onclick="toggleNav()" aria-label="Toggle navigation">
|
|
87
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
88
|
+
<path d="M3 12h18M3 6h18M3 18h18"/>
|
|
89
|
+
</svg>
|
|
90
|
+
</button>
|
|
91
|
+
<a href="../../../" class="mobile-logo" title="Home" data-initial="K">
|
|
92
|
+
<img src="../../../assets/brand/kitfly-neon-256.png" alt="Kitfly" class="logo-img logo-icon" onerror="this.onerror=null;this.style.display='none';this.parentElement.classList.add('logo-fallback')"/>
|
|
93
|
+
</a>
|
|
94
|
+
<button class="mobile-theme-toggle" onclick="toggleTheme()" title="Toggle theme" aria-label="Toggle theme">
|
|
95
|
+
<svg class="icon-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
96
|
+
<circle cx="12" cy="12" r="5"/>
|
|
97
|
+
<path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/>
|
|
98
|
+
</svg>
|
|
99
|
+
<svg class="icon-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
100
|
+
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
|
|
101
|
+
</svg>
|
|
102
|
+
</button>
|
|
103
|
+
</div>
|
|
104
|
+
<div class="layout">
|
|
105
|
+
<nav class="sidebar">
|
|
106
|
+
<div class="sidebar-header">
|
|
107
|
+
<div class="logo logo-icon">
|
|
108
|
+
<a href="/" class="logo-icon" data-initial="K">
|
|
109
|
+
<img src="../../../assets/brand/kitfly-neon-256.png" alt="Kitfly" class="logo-img" onerror="this.onerror=null;this.style.display='none';this.parentElement.classList.add('logo-fallback')"/>
|
|
110
|
+
</a>
|
|
111
|
+
<span class="logo-text">
|
|
112
|
+
<a href="/" class="brand">Kitfly</a>
|
|
113
|
+
<a href="../../../" class="product">Kitfly Docs</a>
|
|
114
|
+
</span>
|
|
115
|
+
</div>
|
|
116
|
+
<div class="header-tools">
|
|
117
|
+
<button class="theme-toggle" onclick="toggleTheme()" title="Toggle theme" aria-label="Toggle theme">
|
|
118
|
+
<svg class="icon-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
119
|
+
<circle cx="12" cy="12" r="5"/>
|
|
120
|
+
<path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/>
|
|
121
|
+
</svg>
|
|
122
|
+
<svg class="icon-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
123
|
+
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
|
|
124
|
+
</svg>
|
|
125
|
+
</button>
|
|
126
|
+
<div class="sidebar-meta">
|
|
127
|
+
<span class="meta-version">v0.2.0</span>
|
|
128
|
+
<span class="meta-branch">HEAD</span>
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
<div class="sidebar-nav">
|
|
133
|
+
<ul><li><a href="../../../index.html" class="nav-home">Home</a></li><li><span class="nav-section">Guide</span><ul><li><a href="../../../content/guide/approaches.html">approaches</a></li><li><a href="../../../content/guide/features.html">features</a></li><li><a href="../../../content/guide/getting-started.html">getting-started</a></li><li><a href="../../../content/guide/kitfly-overview.html">kitfly-overview</a></li></ul></li><li><span class="nav-section">Templates</span><ul><li><a href="../../../content/templates/crucible.html">crucible</a></li><li><a href="../../../content/templates/handbook.html">handbook</a></li><li><a href="../../../content/templates/minimal.html">minimal</a></li><li><a href="../../../content/templates/overview.html">overview</a></li><li><a href="../../../content/templates/pipeline.html">pipeline</a></li><li><a href="../../../content/templates/productbook.html">productbook</a></li><li><a href="../../../content/templates/runbook.html">runbook</a></li><li><a href="../../../content/templates/servicebook.html">servicebook</a></li></ul></li><li><a href="../../../content/reference.html" class="nav-section">Reference</a><ul><li><a href="../../../content/reference/configuration.html">configuration</a></li><li><a href="../../../content/reference/design-catalog.html">design-catalog</a></li><li><a href="../../../content/reference/environment-variables.html">environment-variables</a></li><li><a href="../../../content/reference/glossary.html">glossary</a></li><li><a href="../../../content/reference/key-concepts.html">key-concepts</a></li><li><a href="../../../content/reference/plugins.html">plugins</a></li><li><a href="../../../content/reference/structure.html">structure</a></li></ul></li><li><a href="../../../content/deployment.html" class="nav-section">Deployment</a><ul><li><a href="../../../content/deployment/preflight.html">preflight</a></li><li><details open><summary class="nav-group">recipes</summary><ul><li><a href="../../../content/deployment/recipes/aws-s3.html" class="active">aws-s3</a></li><li><a href="../../../content/deployment/recipes/cloudflare-pages.html">cloudflare-pages</a></li><li><a href="../../../content/deployment/recipes/cloudflare-r2.html">cloudflare-r2</a></li><li><a href="../../../content/deployment/recipes/fly-io.html">fly-io</a></li><li><a href="../../../content/deployment/recipes/github-pages.html">github-pages</a></li><li><a href="../../../content/deployment/recipes/netlify.html">netlify</a></li><li><a href="../../../content/deployment/recipes/vercel.html">vercel</a></li></ul></details></li><li><a href="../../../content/deployment/secrets-and-env-vars.html">secrets-and-env-vars</a></li></ul></li><li><span class="nav-section">User Guide</span><ul><li><details><summary class="nav-group"><a href="../../../docs/userguide/cli.html">cli</a></summary><ul><li><a href="../../../docs/userguide/cli/build.html">build</a></li><li><a href="../../../docs/userguide/cli/bundle.html">bundle</a></li><li><a href="../../../docs/userguide/cli/dev.html">dev</a></li><li><a href="../../../docs/userguide/cli/init.html">init</a></li><li><a href="../../../docs/userguide/cli/servers.html">servers</a></li><li><a href="../../../docs/userguide/cli/stop.html">stop</a></li><li><a href="../../../docs/userguide/cli/update.html">update</a></li><li><a href="../../../docs/userguide/cli/version.html">version</a></li></ul></details></li><li><a href="../../../docs/userguide/sharing.html">sharing</a></li></ul></li><li><span class="nav-section">Decisions</span><ul><li><a href="../../../docs/decisions/ADR-0001-minimalist-site-code.html">ADR-0001-minimalist-site-code</a></li><li><a href="../../../docs/decisions/ADR-0002-ai-accessibility.html">ADR-0002-ai-accessibility</a></li><li><a href="../../../docs/decisions/ADR-0003-single-file-bundle.html">ADR-0003-single-file-bundle</a></li><li><a href="../../../docs/decisions/ADR-0004-bun-runtime.html">ADR-0004-bun-runtime</a></li><li><a href="../../../docs/decisions/ADR-0005-plugin-contract-and-distribution.html">ADR-0005-plugin-contract-and-distribution</a></li><li><a href="../../../docs/decisions/DDR-0001-viewport-locked-layout.html">DDR-0001-viewport-locked-layout</a></li><li><a href="../../../docs/decisions/DDR-0002-theme-system.html">DDR-0002-theme-system</a></li><li><a href="../../../docs/decisions/DDR-0003-bounded-logo-slot.html">DDR-0003-bounded-logo-slot</a></li><li><a href="../../../docs/decisions/DDR-0004-slides-rendering-model.html">DDR-0004-slides-rendering-model</a></li><li><a href="../../../docs/decisions/DDR-0005-deterministic-layout-boundary.html">DDR-0005-deterministic-layout-boundary</a></li></ul></li><li><a href="../../../schemas.html" class="nav-section">Schemas</a><ul><li><a href="../../../schemas/plugin-registry.schema.html">plugin-registry.schema</a></li><li><a href="../../../schemas/plugin-schemas-notes.html">plugin-schemas-notes</a></li><li><a href="../../../schemas/plugin.schema.html">plugin.schema</a></li><li><a href="../../../schemas/plugins.schema.html">plugins.schema</a></li><li><details><summary class="nav-group">v0</summary><ul><li><a href="../../../schemas/v0/common.schema.html">common.schema</a></li><li><a href="../../../schemas/v0/plugin-registry.schema.html">plugin-registry.schema</a></li><li><a href="../../../schemas/v0/plugin.schema.html">plugin.schema</a></li><li><a href="../../../schemas/v0/plugins.schema.html">plugins.schema</a></li><li><a href="../../../schemas/v0/site.schema.html">site.schema</a></li><li><a href="../../../schemas/v0/theme.schema.html">theme.schema</a></li></ul></details></li></ul></li></ul>
|
|
134
|
+
</div>
|
|
135
|
+
</nav>
|
|
136
|
+
<main class="content">
|
|
137
|
+
<article class="prose">
|
|
138
|
+
<nav class="breadcrumbs"><a href="../../../content/guide/approaches.html">Content</a><span class="separator">›</span><a href="../../../content/deployment/preflight.html">Deployment</a><span class="separator">›</span><a href="../../../content/deployment/recipes/aws-s3.html">Recipes</a><span class="separator">›</span><span>aws-s3</span></nav>
|
|
139
|
+
<div class="page-meta">Last updated: 2026-02-12</div>
|
|
140
|
+
<h1 id="recipe-aws-s3">Recipe: AWS S3</h1>
|
|
141
|
+
<p>S3 is a solid choice when you're already in AWS.</p>
|
|
142
|
+
<p>If you're not already on AWS, Netlify or GitHub Pages is usually faster to get right.</p>
|
|
143
|
+
<h2 id="prerequisites">Prerequisites</h2>
|
|
144
|
+
<ul>
|
|
145
|
+
<li>An AWS account</li>
|
|
146
|
+
<li>AWS CLI installed (<code>aws</code>)</li>
|
|
147
|
+
<li>A bucket to host your site</li>
|
|
148
|
+
</ul>
|
|
149
|
+
<h2 id="secrets-important">Secrets (important)</h2>
|
|
150
|
+
<p>If you use access keys locally, keep them out of git and prefer least-privilege IAM.</p>
|
|
151
|
+
<p>Minimal IAM policy for deployment (scope to your bucket):</p>
|
|
152
|
+
<pre><code class="language-json">{
|
|
153
|
+
"Version": "2012-10-17",
|
|
154
|
+
"Statement": [
|
|
155
|
+
{
|
|
156
|
+
"Effect": "Allow",
|
|
157
|
+
"Action": ["s3:PutObject", "s3:DeleteObject", "s3:ListBucket"],
|
|
158
|
+
"Resource": [
|
|
159
|
+
"arn:aws:s3:::YOUR_BUCKET_NAME",
|
|
160
|
+
"arn:aws:s3:::YOUR_BUCKET_NAME/*"
|
|
161
|
+
]
|
|
162
|
+
}
|
|
163
|
+
]
|
|
164
|
+
}
|
|
165
|
+
</code></pre>
|
|
166
|
+
<p>If you use CloudFront (see below), add:</p>
|
|
167
|
+
<pre><code class="language-json">{
|
|
168
|
+
"Effect": "Allow",
|
|
169
|
+
"Action": "cloudfront:CreateInvalidation",
|
|
170
|
+
"Resource": "arn:aws:cloudfront::ACCOUNT_ID:distribution/DISTRIBUTION_ID"
|
|
171
|
+
}
|
|
172
|
+
</code></pre>
|
|
173
|
+
<p>See: <a href="../secrets-and-env-vars.html">Secrets and Environment Variables</a></p>
|
|
174
|
+
<h2 id="build">Build</h2>
|
|
175
|
+
<pre><code class="language-bash">make build
|
|
176
|
+
</code></pre>
|
|
177
|
+
<h2 id="deploy-sync-dist">Deploy (sync <code>dist/</code>)</h2>
|
|
178
|
+
<pre><code class="language-bash">aws s3 sync dist/ "s3://$KITFLY_AWS_S3_BUCKET/" --delete
|
|
179
|
+
</code></pre>
|
|
180
|
+
<p>Tip: <code>--delete</code> keeps the bucket from accumulating old files when pages change names.</p>
|
|
181
|
+
<h2 id="cloudfront-recommended-for-production">CloudFront (recommended for production)</h2>
|
|
182
|
+
<p>S3 alone can serve a website, but CloudFront gives you:</p>
|
|
183
|
+
<ul>
|
|
184
|
+
<li>HTTPS with a custom domain (via ACM certificate)</li>
|
|
185
|
+
<li>Global CDN caching</li>
|
|
186
|
+
<li>Better control over headers and redirects</li>
|
|
187
|
+
</ul>
|
|
188
|
+
<h3 id="basic-cloudfront-setup-shape">Basic CloudFront setup shape</h3>
|
|
189
|
+
<ol>
|
|
190
|
+
<li><p><strong>Create an ACM certificate</strong> for your domain (must be in <code>us-east-1</code> for CloudFront):</p>
|
|
191
|
+
<pre><code class="language-bash">aws acm request-certificate \
|
|
192
|
+
--domain-name docs.example.com \
|
|
193
|
+
--validation-method DNS \
|
|
194
|
+
--region us-east-1
|
|
195
|
+
</code></pre>
|
|
196
|
+
</li>
|
|
197
|
+
<li><p><strong>Create a CloudFront distribution</strong> with S3 as origin:</p>
|
|
198
|
+
<ul>
|
|
199
|
+
<li>Origin: your S3 bucket (use the S3 REST endpoint, not the website endpoint)</li>
|
|
200
|
+
<li>Use Origin Access Control (OAC) so the bucket doesn't need to be public</li>
|
|
201
|
+
<li>Set default root object: <code>index.html</code></li>
|
|
202
|
+
<li>Attach your ACM certificate</li>
|
|
203
|
+
<li>Set alternate domain name: <code>docs.example.com</code></li>
|
|
204
|
+
</ul>
|
|
205
|
+
</li>
|
|
206
|
+
<li><p><strong>Point DNS</strong> at CloudFront:</p>
|
|
207
|
+
<ul>
|
|
208
|
+
<li><code>docs.example.com</code> → <strong>CNAME</strong> to your CloudFront distribution domain (<code>d1234.cloudfront.net</code>)</li>
|
|
209
|
+
</ul>
|
|
210
|
+
</li>
|
|
211
|
+
<li><p><strong>Invalidate cache after deploy:</strong></p>
|
|
212
|
+
<pre><code class="language-bash">aws cloudfront create-invalidation \
|
|
213
|
+
--distribution-id "$KITFLY_AWS_CLOUDFRONT_DISTRIBUTION_ID" \
|
|
214
|
+
--paths "/*"
|
|
215
|
+
</code></pre>
|
|
216
|
+
</li>
|
|
217
|
+
</ol>
|
|
218
|
+
<p>The CloudFront console and <code>aws cloudfront create-distribution</code> CLI have many options. Start simple and add complexity as needed.</p>
|
|
219
|
+
<h2 id="dns-basics-without-cloudfront">DNS basics (without CloudFront)</h2>
|
|
220
|
+
<p>If you use S3 website hosting directly (no CloudFront):</p>
|
|
221
|
+
<ul>
|
|
222
|
+
<li><code>docs.example.com</code> → <strong>CNAME</strong> to <code>YOUR_BUCKET_NAME.s3-website-REGION.amazonaws.com</code></li>
|
|
223
|
+
</ul>
|
|
224
|
+
<p>Note: S3 website hosting does not support HTTPS on custom domains. For HTTPS, put CloudFront in front.</p>
|
|
225
|
+
<h2 id="about-s3-static-website-hosting">About "S3 static website hosting"</h2>
|
|
226
|
+
<p>AWS has an S3 "website hosting" mode. It works for simple cases, but most teams prefer putting S3 behind CloudFront:</p>
|
|
227
|
+
<ul>
|
|
228
|
+
<li>HTTPS on custom domains</li>
|
|
229
|
+
<li>Global edge caching</li>
|
|
230
|
+
<li>Better security (bucket stays private via OAC)</li>
|
|
231
|
+
</ul>
|
|
232
|
+
<p>If you're not sure: start with the simplest approach your org already uses.</p>
|
|
233
|
+
<h2 id="verify-rollback">Verify + Rollback</h2>
|
|
234
|
+
<ul>
|
|
235
|
+
<li>Verify: load the site, click a few pages, hard refresh, confirm HTTPS lock icon</li>
|
|
236
|
+
<li>Rollback: re-sync the previous build output (or use S3 versioning if you enabled it)</li>
|
|
237
|
+
</ul>
|
|
238
|
+
<p>See: <a href="../preflight.html">Preflight and Rollback</a></p>
|
|
239
|
+
|
|
240
|
+
</article>
|
|
241
|
+
<aside class="toc"><span class="toc-title">On this page</span><ul><li><a href="#prerequisites">Prerequisites</a></li><li><a href="#secrets-important">Secrets (important)</a></li><li><a href="#build">Build</a></li><li><a href="#cloudfront-recommended-for-production">CloudFront (recommended for production)</a></li><li class="toc-h3"><a href="#basic-cloudfront-setup-shape">Basic CloudFront setup shape</a></li><li><a href="#dns-basics-without-cloudfront">DNS basics (without CloudFront)</a></li><li><a href="#about-s3-static-website-hosting">About "S3 static website hosting"</a></li><li><a href="#verify-rollback">Verify + Rollback</a></li></ul></aside>
|
|
242
|
+
</main>
|
|
243
|
+
</div>
|
|
244
|
+
|
|
245
|
+
<footer class="site-footer">
|
|
246
|
+
<div class="footer-content">
|
|
247
|
+
<div class="footer-left">
|
|
248
|
+
<span class="footer-version">v0.2.0</span>
|
|
249
|
+
<span class="footer-separator">·</span>
|
|
250
|
+
<span class="footer-commit" title="Commit: 33ccd68">Published 2026-02-15</span>
|
|
251
|
+
</div>
|
|
252
|
+
<div class="footer-center">
|
|
253
|
+
<span class="footer-copyright"><a href="https://3leaps.net" class="footer-link">© 2026 3 Leaps, LLC</a></span>
|
|
254
|
+
<span class="footer-separator">·</span><a href="/" class="footer-link">Kitfly</a>
|
|
255
|
+
</div>
|
|
256
|
+
<div class="footer-right">
|
|
257
|
+
<a href="https://kitfly.dev" class="footer-link">Built with Kitfly</a>
|
|
258
|
+
</div>
|
|
259
|
+
</div>
|
|
260
|
+
</footer>
|
|
261
|
+
<!-- Syntax highlighting - Prism.js -->
|
|
262
|
+
<script src="https://cdn.jsdelivr.net/npm/prismjs@1/components/prism-core.min.js"></script>
|
|
263
|
+
<script src="https://cdn.jsdelivr.net/npm/prismjs@1/plugins/autoloader/prism-autoloader.min.js"></script>
|
|
264
|
+
<!-- Mermaid diagram support -->
|
|
265
|
+
<script type="module">
|
|
266
|
+
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
|
|
267
|
+
|
|
268
|
+
function getMermaidTheme() {
|
|
269
|
+
const theme = document.documentElement.getAttribute('data-theme');
|
|
270
|
+
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
271
|
+
const isDark = theme === 'dark' || (!theme && prefersDark);
|
|
272
|
+
return isDark ? 'dark' : 'neutral';
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
mermaid.initialize({
|
|
276
|
+
startOnLoad: true,
|
|
277
|
+
theme: getMermaidTheme()
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
// Re-render mermaid diagrams when theme changes
|
|
281
|
+
window.reinitMermaid = async function() {
|
|
282
|
+
mermaid.initialize({ startOnLoad: false, theme: getMermaidTheme() });
|
|
283
|
+
const diagrams = document.querySelectorAll('.mermaid');
|
|
284
|
+
for (const el of diagrams) {
|
|
285
|
+
const code = el.getAttribute('data-mermaid-source');
|
|
286
|
+
if (code) {
|
|
287
|
+
el.innerHTML = code;
|
|
288
|
+
el.removeAttribute('data-processed');
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
await mermaid.run({ nodes: diagrams });
|
|
292
|
+
};
|
|
293
|
+
</script>
|
|
294
|
+
|
|
295
|
+
<script>
|
|
296
|
+
function toggleTheme() {
|
|
297
|
+
const html = document.documentElement;
|
|
298
|
+
const current = html.getAttribute('data-theme');
|
|
299
|
+
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
300
|
+
|
|
301
|
+
let next;
|
|
302
|
+
if (current === 'dark') {
|
|
303
|
+
next = 'light';
|
|
304
|
+
} else if (current === 'light') {
|
|
305
|
+
next = 'dark';
|
|
306
|
+
} else {
|
|
307
|
+
// No explicit theme set, toggle from system preference
|
|
308
|
+
next = prefersDark ? 'light' : 'dark';
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
html.setAttribute('data-theme', next);
|
|
312
|
+
localStorage.setItem('theme', next);
|
|
313
|
+
|
|
314
|
+
// Switch Prism theme
|
|
315
|
+
const prismLight = document.getElementById('prism-light');
|
|
316
|
+
const prismDark = document.getElementById('prism-dark');
|
|
317
|
+
if (next === 'dark') {
|
|
318
|
+
prismLight?.setAttribute('disabled', '');
|
|
319
|
+
prismDark?.removeAttribute('disabled');
|
|
320
|
+
} else {
|
|
321
|
+
prismLight?.removeAttribute('disabled');
|
|
322
|
+
prismDark?.setAttribute('disabled', '');
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Re-render mermaid diagrams with new theme
|
|
326
|
+
if (window.reinitMermaid) {
|
|
327
|
+
window.reinitMermaid();
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Slides mode hash routing
|
|
332
|
+
(function initSlidesMode() {
|
|
333
|
+
const shell = document.querySelector('.slides-shell');
|
|
334
|
+
if (!shell) return;
|
|
335
|
+
|
|
336
|
+
const slides = Array.from(document.querySelectorAll('.slide'));
|
|
337
|
+
if (!slides.length) return;
|
|
338
|
+
|
|
339
|
+
const prevBtn = document.querySelector('.slide-prev');
|
|
340
|
+
const nextBtn = document.querySelector('.slide-next');
|
|
341
|
+
const counter = document.querySelector('.slide-counter');
|
|
342
|
+
const progressBar = document.querySelector('.slide-progress-bar');
|
|
343
|
+
const navLinks = Array.from(document.querySelectorAll('.sidebar-nav a[href^="#slide-"]'));
|
|
344
|
+
let current = 0;
|
|
345
|
+
|
|
346
|
+
function setActive(n) {
|
|
347
|
+
current = Math.max(0, Math.min(n, slides.length - 1));
|
|
348
|
+
slides.forEach((slide, idx) => slide.classList.toggle('active', idx === current));
|
|
349
|
+
navLinks.forEach((link) => {
|
|
350
|
+
const active = link.getAttribute('href') === '#' + slides[current].id;
|
|
351
|
+
link.classList.toggle('active', active);
|
|
352
|
+
});
|
|
353
|
+
if (counter) counter.textContent = (current + 1) + ' / ' + slides.length;
|
|
354
|
+
if (progressBar) progressBar.style.width = (((current + 1) / slides.length) * 100) + '%';
|
|
355
|
+
if (prevBtn) prevBtn.disabled = current === 0;
|
|
356
|
+
if (nextBtn) nextBtn.disabled = current === slides.length - 1;
|
|
357
|
+
history.replaceState(null, '', '#' + slides[current].id);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function setFromHash() {
|
|
361
|
+
const hash = window.location.hash || '';
|
|
362
|
+
const idx = slides.findIndex((s) => '#' + s.id === hash);
|
|
363
|
+
if (idx >= 0) setActive(idx);
|
|
364
|
+
else setActive(0);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
prevBtn?.addEventListener('click', () => setActive(current - 1));
|
|
368
|
+
nextBtn?.addEventListener('click', () => setActive(current + 1));
|
|
369
|
+
|
|
370
|
+
document.addEventListener('keydown', (e) => {
|
|
371
|
+
if (e.key === 'ArrowRight' || e.key === ' ') {
|
|
372
|
+
e.preventDefault();
|
|
373
|
+
setActive(current + 1);
|
|
374
|
+
} else if (e.key === 'ArrowLeft') {
|
|
375
|
+
e.preventDefault();
|
|
376
|
+
setActive(current - 1);
|
|
377
|
+
} else if (e.key === 'Home') {
|
|
378
|
+
e.preventDefault();
|
|
379
|
+
setActive(0);
|
|
380
|
+
} else if (e.key === 'End') {
|
|
381
|
+
e.preventDefault();
|
|
382
|
+
setActive(slides.length - 1);
|
|
383
|
+
}
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
window.addEventListener('hashchange', setFromHash);
|
|
387
|
+
setFromHash();
|
|
388
|
+
})();
|
|
389
|
+
|
|
390
|
+
// Copy code button
|
|
391
|
+
document.querySelectorAll('.prose pre code').forEach(block => {
|
|
392
|
+
const button = document.createElement('button');
|
|
393
|
+
button.className = 'copy-button';
|
|
394
|
+
button.textContent = 'Copy';
|
|
395
|
+
button.onclick = async () => {
|
|
396
|
+
await navigator.clipboard.writeText(block.textContent);
|
|
397
|
+
button.textContent = 'Copied!';
|
|
398
|
+
setTimeout(() => button.textContent = 'Copy', 2000);
|
|
399
|
+
};
|
|
400
|
+
block.parentElement.appendChild(button);
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
// Mobile nav toggle
|
|
404
|
+
function toggleNav() {
|
|
405
|
+
document.querySelector('.sidebar').classList.toggle('open');
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Close nav when clicking outside on mobile
|
|
409
|
+
document.addEventListener('click', (e) => {
|
|
410
|
+
const sidebar = document.querySelector('.sidebar');
|
|
411
|
+
const toggle = document.querySelector('.nav-toggle');
|
|
412
|
+
if (sidebar.classList.contains('open') &&
|
|
413
|
+
!sidebar.contains(e.target) &&
|
|
414
|
+
!toggle.contains(e.target)) {
|
|
415
|
+
sidebar.classList.remove('open');
|
|
416
|
+
}
|
|
417
|
+
});
|
|
418
|
+
</script>
|
|
419
|
+
|
|
420
|
+
</body>
|
|
421
|
+
</html>
|