@grainulation/barn 1.0.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 (50) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/LICENSE +21 -0
  3. package/README.md +87 -0
  4. package/bin/barn.js +98 -0
  5. package/lib/index.js +93 -0
  6. package/lib/server.js +368 -0
  7. package/package.json +52 -0
  8. package/public/grainulation-tokens.css +321 -0
  9. package/public/index.html +907 -0
  10. package/templates/README.md +48 -0
  11. package/templates/adr.html +223 -0
  12. package/templates/adr.json +29 -0
  13. package/templates/brief.html +297 -0
  14. package/templates/brief.json +26 -0
  15. package/templates/certificate.html +247 -0
  16. package/templates/certificate.json +23 -0
  17. package/templates/changelog.html +239 -0
  18. package/templates/changelog.json +19 -0
  19. package/templates/ci-workflow.yml +52 -0
  20. package/templates/comparison.html +248 -0
  21. package/templates/comparison.json +21 -0
  22. package/templates/conflict-map.html +240 -0
  23. package/templates/conflict-map.json +19 -0
  24. package/templates/dashboard.html +515 -0
  25. package/templates/dashboard.json +22 -0
  26. package/templates/email-digest.html +178 -0
  27. package/templates/email-digest.json +18 -0
  28. package/templates/evidence-matrix.html +232 -0
  29. package/templates/evidence-matrix.json +21 -0
  30. package/templates/explainer.html +342 -0
  31. package/templates/explainer.json +23 -0
  32. package/templates/handoff.html +343 -0
  33. package/templates/handoff.json +24 -0
  34. package/templates/one-pager.html +248 -0
  35. package/templates/one-pager.json +22 -0
  36. package/templates/postmortem.html +303 -0
  37. package/templates/postmortem.json +20 -0
  38. package/templates/rfc.html +199 -0
  39. package/templates/rfc.json +32 -0
  40. package/templates/risk-register.html +231 -0
  41. package/templates/risk-register.json +22 -0
  42. package/templates/slide-deck.html +239 -0
  43. package/templates/slide-deck.json +23 -0
  44. package/templates/template.schema.json +25 -0
  45. package/templates/wiki-page.html +222 -0
  46. package/templates/wiki-page.json +23 -0
  47. package/tools/README.md +31 -0
  48. package/tools/build-pdf.js +43 -0
  49. package/tools/detect-sprints.js +292 -0
  50. package/tools/generate-manifest.js +237 -0
@@ -0,0 +1,247 @@
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>Compilation Certificate -- {{SPRINT_NAME}}</title>
7
+ <style>
8
+ /* barn/templates/certificate.html — compilation certificate as visual artifact
9
+ Centered formal layout with double border frame, monospace hash display.
10
+ System font stack, no external dependencies, fully self-contained. */
11
+
12
+ :root {
13
+ --bg: #0a0e1a;
14
+ --bg2: #111827;
15
+ --bg3: #1e293b;
16
+ --accent: #3b82f6;
17
+ --accent-light: #60a5fa;
18
+ --green: #22c55e;
19
+ --green-dim: rgba(34,197,94,0.12);
20
+ --red: #e11d48;
21
+ --red-dim: rgba(225,29,72,0.12);
22
+ --text: #f1f5f9;
23
+ --text-muted: #94a3b8;
24
+ --text-dim: #64748b;
25
+ }
26
+
27
+ * { margin: 0; padding: 0; box-sizing: border-box; }
28
+ html, body { height: 100%; }
29
+
30
+ body {
31
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Inter', sans-serif;
32
+ background: var(--bg);
33
+ color: var(--text);
34
+ -webkit-font-smoothing: antialiased;
35
+ display: flex;
36
+ align-items: center;
37
+ justify-content: center;
38
+ min-height: 100vh;
39
+ padding: 40px 20px;
40
+ }
41
+
42
+ .certificate {
43
+ width: 100%; max-width: 640px;
44
+ background: var(--bg2);
45
+ border: 2px solid rgba(59,130,246,0.2);
46
+ border-radius: 16px;
47
+ padding: 48px 40px;
48
+ position: relative;
49
+ text-align: center;
50
+ }
51
+
52
+ /* Double frame border effect */
53
+ .certificate::before {
54
+ content: '';
55
+ position: absolute; top: 8px; left: 8px; right: 8px; bottom: 8px;
56
+ border: 1px solid rgba(59,130,246,0.08);
57
+ border-radius: 12px; pointer-events: none;
58
+ }
59
+
60
+ .certificate::after {
61
+ content: '';
62
+ position: absolute; top: 16px; left: 16px; right: 16px; bottom: 16px;
63
+ border: 1px solid rgba(59,130,246,0.05);
64
+ border-radius: 10px; pointer-events: none;
65
+ }
66
+
67
+ .cert-label {
68
+ font-size: 8pt; font-weight: 600;
69
+ letter-spacing: 0.2em; text-transform: uppercase;
70
+ color: var(--accent-light); margin-bottom: 12px;
71
+ }
72
+
73
+ .cert-sprint {
74
+ font-size: 20pt; font-weight: 700;
75
+ color: #fff; margin-bottom: 4px;
76
+ }
77
+
78
+ .cert-divider {
79
+ width: 60px; height: 2px;
80
+ background: var(--accent); border-radius: 1px;
81
+ margin: 20px auto;
82
+ }
83
+
84
+ .cert-hash {
85
+ font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
86
+ font-size: 16pt; font-weight: 600;
87
+ color: var(--accent-light); letter-spacing: 0.06em;
88
+ word-break: break-all; padding: 16px 20px;
89
+ background: var(--bg); border-radius: 8px;
90
+ border: 1px solid rgba(59,130,246,0.15);
91
+ margin: 20px 0;
92
+ }
93
+
94
+ .cert-details {
95
+ display: grid;
96
+ grid-template-columns: 1fr 1fr 1fr;
97
+ gap: 16px;
98
+ margin: 24px 0;
99
+ }
100
+
101
+ .cert-detail {
102
+ padding: 12px 8px;
103
+ background: var(--bg);
104
+ border-radius: 8px;
105
+ }
106
+
107
+ .cert-detail-label {
108
+ font-size: 7pt; font-weight: 600;
109
+ text-transform: uppercase; letter-spacing: 0.12em;
110
+ color: var(--text-dim); margin-bottom: 4px;
111
+ }
112
+
113
+ .cert-detail-value {
114
+ font-size: 14pt; font-weight: 700;
115
+ color: var(--text);
116
+ font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
117
+ }
118
+
119
+ /* Evidence tier bars */
120
+ .cert-evidence { margin: 20px 0; text-align: left; }
121
+
122
+ .cert-evidence h3 {
123
+ font-size: 8pt; font-weight: 600;
124
+ text-transform: uppercase; letter-spacing: 0.1em;
125
+ color: var(--text-dim); margin-bottom: 10px;
126
+ text-align: center;
127
+ }
128
+
129
+ .tier-row {
130
+ display: flex; align-items: center;
131
+ gap: 10px; margin-bottom: 6px;
132
+ }
133
+
134
+ .tier-name {
135
+ min-width: 90px; color: var(--text-muted);
136
+ text-transform: uppercase; letter-spacing: 0.06em;
137
+ font-size: 8pt; text-align: right;
138
+ }
139
+
140
+ .tier-track {
141
+ flex: 1; height: 6px;
142
+ background: var(--bg); border-radius: 3px; overflow: hidden;
143
+ }
144
+
145
+ .tier-fill { height: 100%; border-radius: 3px; background: var(--accent); }
146
+ .tier-fill.stated { background: var(--text-dim); }
147
+ .tier-fill.web { background: #f59e0b; }
148
+ .tier-fill.documented { background: var(--accent); }
149
+ .tier-fill.tested { background: var(--green); }
150
+ .tier-fill.production { background: #a78bfa; }
151
+
152
+ .tier-count {
153
+ min-width: 28px; color: var(--text-dim);
154
+ font-family: monospace; font-size: 9pt;
155
+ }
156
+
157
+ /* Conflict status */
158
+ .cert-conflict {
159
+ margin: 16px 0; padding: 10px 16px;
160
+ border-radius: 6px; font-size: 10pt; font-weight: 500;
161
+ }
162
+
163
+ .cert-conflict.resolved { background: var(--green-dim); color: var(--green); }
164
+ .cert-conflict.unresolved { background: var(--red-dim); color: var(--red); }
165
+
166
+ .cert-statement {
167
+ margin-top: 24px; font-size: 9pt;
168
+ color: var(--text-dim); font-style: italic; line-height: 1.5;
169
+ }
170
+
171
+ .cert-footer {
172
+ margin-top: 20px; font-size: 8pt; color: var(--text-dim);
173
+ }
174
+
175
+ @media (max-width: 768px) {
176
+ body { padding: 16px; }
177
+ .certificate { padding: 32px 20px; }
178
+ .cert-sprint { font-size: 16pt; }
179
+ .cert-hash { font-size: 11pt; padding: 12px; }
180
+ .cert-details { grid-template-columns: 1fr; gap: 8px; }
181
+ .cert-detail-value { font-size: 12pt; }
182
+ .certificate::before, .certificate::after { display: none; }
183
+ }
184
+
185
+ @media print {
186
+ body { background: #fff; }
187
+ .certificate { background: #fff; border-color: #ccc; }
188
+ .cert-sprint { color: #111; }
189
+ .cert-hash { background: #f5f5f5; color: #333; border-color: #ddd; }
190
+ .cert-detail { background: #f9f9f9; }
191
+ .cert-detail-value { color: #111; }
192
+ .cert-label { color: #555; }
193
+ .tier-name, .tier-count { color: #555; }
194
+ .tier-track { background: #eee; }
195
+ .cert-statement { color: #666; }
196
+ .certificate::before { border-color: #ddd; }
197
+ .certificate::after { border-color: #eee; }
198
+ }
199
+ </style>
200
+ </head>
201
+ <body>
202
+
203
+ <article class="certificate" role="document" aria-label="Compilation certificate">
204
+ <div class="cert-header">
205
+ <div class="cert-label">Compilation Certificate</div>
206
+ <div class="cert-sprint">{{SPRINT_NAME}}</div>
207
+ </div>
208
+
209
+ <div class="cert-divider"></div>
210
+
211
+ <div class="cert-hash" aria-label="Compilation hash">{{HASH}}</div>
212
+
213
+ <div class="cert-details" aria-label="Compilation details">
214
+ <div class="cert-detail">
215
+ <div class="cert-detail-label">Claims</div>
216
+ <div class="cert-detail-value">{{CLAIM_COUNT}}</div>
217
+ </div>
218
+ <div class="cert-detail">
219
+ <div class="cert-detail-label">Compiled</div>
220
+ <div class="cert-detail-value">{{COMPILED_AT}}</div>
221
+ </div>
222
+ <div class="cert-detail">
223
+ <div class="cert-detail-label">Compiler</div>
224
+ <div class="cert-detail-value">{{COMPILER_VERSION}}</div>
225
+ </div>
226
+ </div>
227
+
228
+ <div class="cert-evidence" aria-label="Evidence tier breakdown">
229
+ <h3>Evidence Breakdown</h3>
230
+ {{EVIDENCE_BREAKDOWN}}
231
+ </div>
232
+
233
+ <div class="cert-conflict {{CONFLICT_STATUS}}" aria-label="Conflict status">
234
+ {{CONFLICT_STATUS}}
235
+ </div>
236
+
237
+ <p class="cert-statement">
238
+ This compilation is reproducible. The hash above uniquely identifies the claim
239
+ set and compiler version used. Re-running the compiler with identical inputs
240
+ will produce the same hash.
241
+ </p>
242
+
243
+ <div class="cert-footer">Built with barn</div>
244
+ </article>
245
+
246
+ </body>
247
+ </html>
@@ -0,0 +1,23 @@
1
+ {
2
+ "title": "Certificate",
3
+ "description": "Compilation certificate rendered as a formal visual artifact. Shows hash, claim count, evidence breakdown, and conflict status with a centered, framed layout.",
4
+ "tags": ["certificate", "compilation", "verification", "reproducible", "hash"],
5
+ "author": "grainulation",
6
+ "version": "1.0.0",
7
+ "exportPresets": [
8
+ { "name": "pdf", "format": "pdf" },
9
+ { "name": "static-site", "format": "html" }
10
+ ],
11
+ "seedPacks": [],
12
+ "scaffoldConfig": {
13
+ "defaultPlaceholders": {
14
+ "SPRINT_NAME": "Sprint",
15
+ "HASH": "",
16
+ "CLAIM_COUNT": "0",
17
+ "COMPILED_AT": "",
18
+ "COMPILER_VERSION": "1.0.0",
19
+ "CONFLICT_STATUS": "resolved"
20
+ },
21
+ "postInit": ["load-claims"]
22
+ }
23
+ }
@@ -0,0 +1,239 @@
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>{{TITLE}} -- {{SPRINT_NAME}}</title>
7
+ <style>
8
+ :root {
9
+ --bg: #0a0e1a;
10
+ --bg2: #111827;
11
+ --bg3: #1e293b;
12
+ --accent: #3b82f6;
13
+ --accent-light: #60a5fa;
14
+ --green: #22c55e;
15
+ --green-dim: rgba(34,197,94,0.15);
16
+ --orange: #f59e0b;
17
+ --orange-dim: rgba(245,158,11,0.15);
18
+ --red: #e11d48;
19
+ --red-dim: rgba(225,29,72,0.15);
20
+ --purple: #a78bfa;
21
+ --text: #f1f5f9;
22
+ --text-muted: #94a3b8;
23
+ --text-dim: #64748b;
24
+ --border: rgba(255,255,255,0.06);
25
+ }
26
+ * { margin: 0; padding: 0; box-sizing: border-box; }
27
+ html { scroll-snap-type: y mandatory; scroll-behavior: smooth; }
28
+ body {
29
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Inter', sans-serif;
30
+ background: var(--bg);
31
+ color: var(--text);
32
+ font-size: 10pt;
33
+ line-height: 1.5;
34
+ -webkit-font-smoothing: antialiased;
35
+ }
36
+ section {
37
+ scroll-snap-align: start;
38
+ min-height: 100vh;
39
+ padding: 40px;
40
+ display: flex;
41
+ flex-direction: column;
42
+ justify-content: center;
43
+ }
44
+ .header-section { justify-content: flex-end; padding-bottom: 80px; }
45
+ .header-section h1 {
46
+ font-size: 28pt;
47
+ font-weight: 800;
48
+ letter-spacing: -0.02em;
49
+ background: linear-gradient(135deg, #fff 0%, #94a3b8 100%);
50
+ -webkit-background-clip: text;
51
+ -webkit-text-fill-color: transparent;
52
+ margin-bottom: 12px;
53
+ }
54
+ .meta-row {
55
+ display: flex;
56
+ gap: 24px;
57
+ color: var(--text-muted);
58
+ font-size: 10pt;
59
+ font-family: monospace;
60
+ }
61
+ .stats-bar {
62
+ display: flex;
63
+ gap: 16px;
64
+ margin-top: 24px;
65
+ }
66
+ .stat-card {
67
+ background: var(--bg2);
68
+ border: 1px solid var(--border);
69
+ border-radius: 8px;
70
+ padding: 16px 24px;
71
+ text-align: center;
72
+ }
73
+ .stat-card .value {
74
+ font-size: 20pt;
75
+ font-weight: 800;
76
+ color: var(--accent-light);
77
+ }
78
+ .stat-card .label {
79
+ font-size: 8pt;
80
+ text-transform: uppercase;
81
+ letter-spacing: 0.1em;
82
+ color: var(--text-dim);
83
+ margin-top: 4px;
84
+ }
85
+ .panel {
86
+ background: var(--bg2);
87
+ border-radius: 12px;
88
+ padding: 24px;
89
+ border: 1px solid var(--border);
90
+ margin-bottom: 16px;
91
+ }
92
+ .panel-title {
93
+ font-size: 9pt;
94
+ font-weight: 700;
95
+ text-transform: uppercase;
96
+ letter-spacing: 0.1em;
97
+ color: var(--text-dim);
98
+ margin-bottom: 16px;
99
+ }
100
+ .timeline-entry {
101
+ display: flex;
102
+ gap: 16px;
103
+ padding: 12px 0;
104
+ border-bottom: 1px solid var(--border);
105
+ }
106
+ .timeline-entry:last-child { border-bottom: none; }
107
+ .timeline-ts {
108
+ font-family: monospace;
109
+ font-size: 9pt;
110
+ color: var(--text-dim);
111
+ width: 120px;
112
+ flex-shrink: 0;
113
+ }
114
+ .timeline-body { flex: 1; color: var(--text-muted); font-size: 10pt; }
115
+ .badge {
116
+ display: inline-block;
117
+ padding: 2px 8px;
118
+ border-radius: 4px;
119
+ font-size: 8pt;
120
+ font-weight: 600;
121
+ text-transform: uppercase;
122
+ letter-spacing: 0.04em;
123
+ }
124
+ .badge.new { color: var(--green); background: var(--green-dim); }
125
+ .badge.resolved { color: var(--accent-light); background: rgba(59,130,246,0.12); }
126
+ .badge.upgraded { color: var(--purple); background: rgba(167,139,250,0.12); }
127
+ .badge.constraint { color: var(--purple); background: rgba(167,139,250,0.12); }
128
+ .badge.factual { color: var(--accent-light); background: rgba(59,130,246,0.12); }
129
+ .badge.risk { color: var(--red); background: var(--red-dim); }
130
+ .badge.estimate { color: var(--orange); background: var(--orange-dim); }
131
+ .badge.recommendation { color: var(--green); background: var(--green-dim); }
132
+ .claim-row {
133
+ display: flex;
134
+ align-items: center;
135
+ gap: 12px;
136
+ padding: 10px 0;
137
+ border-bottom: 1px solid var(--border);
138
+ font-size: 9pt;
139
+ }
140
+ .claim-row:last-child { border-bottom: none; }
141
+ .claim-id { font-family: monospace; color: var(--text-dim); width: 60px; flex-shrink: 0; }
142
+ .claim-text { flex: 1; color: var(--text-muted); }
143
+ .upgrade-arrow {
144
+ font-family: monospace;
145
+ color: var(--text-dim);
146
+ padding: 0 8px;
147
+ }
148
+ .compilation-bar {
149
+ margin-top: 32px;
150
+ padding: 16px 24px;
151
+ background: var(--bg2);
152
+ border-radius: 8px;
153
+ border: 1px solid var(--border);
154
+ display: flex;
155
+ justify-content: space-between;
156
+ font-size: 9pt;
157
+ color: var(--text-dim);
158
+ }
159
+ @media (max-width: 768px) {
160
+ section { padding: 20px; min-height: auto; }
161
+ .header-section h1 { font-size: 18pt; }
162
+ .meta-row { flex-wrap: wrap; gap: 8px; font-size: 9pt; }
163
+ .stats-bar { flex-wrap: wrap; }
164
+ .stat-card { flex: 1 1 calc(50% - 8px); padding: 12px 16px; }
165
+ .stat-card .value { font-size: 16pt; }
166
+ .timeline-entry { flex-direction: column; gap: 4px; }
167
+ .timeline-ts { width: auto; }
168
+ }
169
+ @media print {
170
+ html { scroll-snap-type: none; }
171
+ section { min-height: auto; page-break-inside: avoid; padding: 16px 0; }
172
+ body { background: #fff; color: #111; }
173
+ .panel { border-color: #ccc; background: #f9f9f9; }
174
+ .badge { border: 1px solid #ccc; }
175
+ .compilation-bar { background: #f9f9f9; border-color: #ccc; }
176
+ }
177
+ </style>
178
+ </head>
179
+ <body>
180
+
181
+ <section class="header-section" aria-label="Changelog header">
182
+ <h1>{{TITLE}}</h1>
183
+ <div class="meta-row">
184
+ <span>Sprint: {{SPRINT_NAME}}</span>
185
+ <span>Compiled: {{COMPILED_AT}}</span>
186
+ </div>
187
+ <div class="stats-bar">
188
+ <div class="stat-card">
189
+ <div class="value">{{TOTAL_CHANGES}}</div>
190
+ <div class="label">Total Changes</div>
191
+ </div>
192
+ <div class="stat-card">
193
+ <div class="value">{{NEW_COUNT}}</div>
194
+ <div class="label">New Claims</div>
195
+ </div>
196
+ <div class="stat-card">
197
+ <div class="value">{{RESOLVED_COUNT}}</div>
198
+ <div class="label">Resolved</div>
199
+ </div>
200
+ </div>
201
+ </section>
202
+
203
+ <section aria-label="Timeline">
204
+ <div class="panel">
205
+ <div class="panel-title">Timeline</div>
206
+ {{TIMELINE_ENTRIES}}
207
+ </div>
208
+ </section>
209
+
210
+ <section aria-label="New claims">
211
+ <div class="panel">
212
+ <div class="panel-title">New Claims</div>
213
+ {{NEW_CLAIMS}}
214
+ </div>
215
+ </section>
216
+
217
+ <section aria-label="Resolved conflicts">
218
+ <div class="panel">
219
+ <div class="panel-title">Resolved Conflicts</div>
220
+ {{RESOLVED_CONFLICTS}}
221
+ </div>
222
+ </section>
223
+
224
+ <section aria-label="Evidence upgrades">
225
+ <div class="panel">
226
+ <div class="panel-title">Evidence Upgrades</div>
227
+ {{EVIDENCE_UPGRADES}}
228
+ </div>
229
+ </section>
230
+
231
+ <section aria-label="Compilation footer">
232
+ <div class="compilation-bar">
233
+ <div>Compiled with barn</div>
234
+ <div style="font-family: monospace; font-size: 8pt;">{{CERTIFICATE_HASH}}</div>
235
+ </div>
236
+ </section>
237
+
238
+ </body>
239
+ </html>
@@ -0,0 +1,19 @@
1
+ {
2
+ "title": "Changelog",
3
+ "description": "Sprint changelog showing what changed since last compile. Timeline view with new claims, resolved conflicts, and evidence upgrades.",
4
+ "tags": ["changelog", "history", "timeline", "diff"],
5
+ "author": "grainulation",
6
+ "version": "1.0.0",
7
+ "exportPresets": [
8
+ { "name": "pdf", "format": "pdf" },
9
+ { "name": "markdown-outline", "format": "md" }
10
+ ],
11
+ "seedPacks": [],
12
+ "scaffoldConfig": {
13
+ "defaultPlaceholders": {
14
+ "TITLE": "Sprint Changelog",
15
+ "SPRINT_NAME": "Untitled Sprint"
16
+ },
17
+ "postInit": ["load-claims", "diff-previous-compile"]
18
+ }
19
+ }
@@ -0,0 +1,52 @@
1
+ # Grainulation CI Workflow Template
2
+ #
3
+ # Reusable GitHub Actions workflow for all 8 grainulation repos.
4
+ # Copy this file to .github/workflows/ci.yml in each repo.
5
+ #
6
+ # What it does:
7
+ # 1. Runs npm test across Node 18, 20, 22
8
+ # 2. Verifies zero npm dependencies (core ecosystem constraint d001)
9
+ # 3. Dry-run npm pack to catch packaging issues before publish
10
+ #
11
+ # Addresses blind spots r042/r059: no GitHub Actions workflow for any of the 8 repos.
12
+
13
+ name: CI
14
+
15
+ on: [push, pull_request]
16
+
17
+ jobs:
18
+ test:
19
+ runs-on: ubuntu-latest
20
+ strategy:
21
+ matrix:
22
+ node-version: [18, 20, 22]
23
+ steps:
24
+ - uses: actions/checkout@v4
25
+ - uses: actions/setup-node@v4
26
+ with:
27
+ node-version: ${{ matrix.node-version }}
28
+ - run: npm test
29
+
30
+ zero-dep-check:
31
+ runs-on: ubuntu-latest
32
+ steps:
33
+ - uses: actions/checkout@v4
34
+ - name: Verify zero dependencies
35
+ run: |
36
+ node -e "
37
+ const pkg = require('./package.json');
38
+ if (pkg.dependencies && Object.keys(pkg.dependencies).length > 0) {
39
+ console.error('Found dependencies:', pkg.dependencies);
40
+ process.exit(1);
41
+ }
42
+ console.log('Zero dependencies verified');
43
+ "
44
+
45
+ publish-dry-run:
46
+ runs-on: ubuntu-latest
47
+ steps:
48
+ - uses: actions/checkout@v4
49
+ - uses: actions/setup-node@v4
50
+ with:
51
+ node-version: 20
52
+ - run: npm pack --dry-run