@arkvenai/cli 0.5.1 → 0.6.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 (139) hide show
  1. package/README.md +80 -242
  2. package/dist/befehle.js +138 -77
  3. package/dist/index.js +63 -334
  4. package/dist/projekt.js +121 -0
  5. package/dist/ruf.js +45 -0
  6. package/dist/uebertragen.js +185 -0
  7. package/dist/zugang.js +114 -0
  8. package/package.json +7 -39
  9. package/CHANGELOG.md +0 -105
  10. package/dist/agents.d.ts +0 -11
  11. package/dist/agents.d.ts.map +0 -1
  12. package/dist/agents.js +0 -76
  13. package/dist/agents.js.map +0 -1
  14. package/dist/auftrag.d.ts +0 -3
  15. package/dist/auftrag.d.ts.map +0 -1
  16. package/dist/auftrag.js +0 -45
  17. package/dist/auftrag.js.map +0 -1
  18. package/dist/bauplan.d.ts +0 -15
  19. package/dist/bauplan.d.ts.map +0 -1
  20. package/dist/bauplan.js +0 -219
  21. package/dist/bauplan.js.map +0 -1
  22. package/dist/befehle.d.ts +0 -12
  23. package/dist/befehle.d.ts.map +0 -1
  24. package/dist/befehle.js.map +0 -1
  25. package/dist/befund.d.ts +0 -21
  26. package/dist/befund.d.ts.map +0 -1
  27. package/dist/befund.js +0 -107
  28. package/dist/befund.js.map +0 -1
  29. package/dist/codeAblage.d.ts +0 -31
  30. package/dist/codeAblage.d.ts.map +0 -1
  31. package/dist/codeAblage.js +0 -80
  32. package/dist/codeAblage.js.map +0 -1
  33. package/dist/deklaration.d.ts +0 -3
  34. package/dist/deklaration.d.ts.map +0 -1
  35. package/dist/deklaration.js +0 -14
  36. package/dist/deklaration.js.map +0 -1
  37. package/dist/diff.d.ts +0 -10
  38. package/dist/diff.d.ts.map +0 -1
  39. package/dist/diff.js +0 -72
  40. package/dist/diff.js.map +0 -1
  41. package/dist/erkennung.d.ts +0 -26
  42. package/dist/erkennung.d.ts.map +0 -1
  43. package/dist/erkennung.js +0 -174
  44. package/dist/erkennung.js.map +0 -1
  45. package/dist/fix.d.ts +0 -26
  46. package/dist/fix.d.ts.map +0 -1
  47. package/dist/fix.js +0 -101
  48. package/dist/fix.js.map +0 -1
  49. package/dist/geteilt/bauplan/form.d.ts +0 -100
  50. package/dist/geteilt/bauplan/form.d.ts.map +0 -1
  51. package/dist/geteilt/bauplan/form.js +0 -21
  52. package/dist/geteilt/bauplan/form.js.map +0 -1
  53. package/dist/geteilt/bauplan/index.d.ts +0 -4
  54. package/dist/geteilt/bauplan/index.d.ts.map +0 -1
  55. package/dist/geteilt/bauplan/index.js +0 -4
  56. package/dist/geteilt/bauplan/index.js.map +0 -1
  57. package/dist/geteilt/bauplan/lesen.d.ts +0 -6
  58. package/dist/geteilt/bauplan/lesen.d.ts.map +0 -1
  59. package/dist/geteilt/bauplan/lesen.js +0 -344
  60. package/dist/geteilt/bauplan/lesen.js.map +0 -1
  61. package/dist/geteilt/bauplan/schreiben.d.ts +0 -3
  62. package/dist/geteilt/bauplan/schreiben.d.ts.map +0 -1
  63. package/dist/geteilt/bauplan/schreiben.js +0 -80
  64. package/dist/geteilt/bauplan/schreiben.js.map +0 -1
  65. package/dist/index.d.ts +0 -3
  66. package/dist/index.d.ts.map +0 -1
  67. package/dist/index.js.map +0 -1
  68. package/dist/init.d.ts +0 -28
  69. package/dist/init.d.ts.map +0 -1
  70. package/dist/init.js +0 -324
  71. package/dist/init.js.map +0 -1
  72. package/dist/login.d.ts +0 -45
  73. package/dist/login.d.ts.map +0 -1
  74. package/dist/login.js +0 -162
  75. package/dist/login.js.map +0 -1
  76. package/dist/mcp.d.ts +0 -18
  77. package/dist/mcp.d.ts.map +0 -1
  78. package/dist/mcp.js +0 -115
  79. package/dist/mcp.js.map +0 -1
  80. package/dist/neu.d.ts +0 -17
  81. package/dist/neu.d.ts.map +0 -1
  82. package/dist/neu.js +0 -118
  83. package/dist/neu.js.map +0 -1
  84. package/dist/npmrc.d.ts +0 -9
  85. package/dist/npmrc.d.ts.map +0 -1
  86. package/dist/npmrc.js +0 -49
  87. package/dist/npmrc.js.map +0 -1
  88. package/dist/projekte.d.ts +0 -23
  89. package/dist/projekte.d.ts.map +0 -1
  90. package/dist/projekte.js +0 -82
  91. package/dist/projekte.js.map +0 -1
  92. package/dist/pull.d.ts +0 -15
  93. package/dist/pull.d.ts.map +0 -1
  94. package/dist/pull.js +0 -301
  95. package/dist/pull.js.map +0 -1
  96. package/dist/push/bericht.d.ts +0 -8
  97. package/dist/push/bericht.d.ts.map +0 -1
  98. package/dist/push/bericht.js +0 -85
  99. package/dist/push/bericht.js.map +0 -1
  100. package/dist/push/form.d.ts +0 -67
  101. package/dist/push/form.d.ts.map +0 -1
  102. package/dist/push/form.js +0 -6
  103. package/dist/push/form.js.map +0 -1
  104. package/dist/push/medien.d.ts +0 -7
  105. package/dist/push/medien.d.ts.map +0 -1
  106. package/dist/push/medien.js +0 -154
  107. package/dist/push/medien.js.map +0 -1
  108. package/dist/push/sammeln.d.ts +0 -34
  109. package/dist/push/sammeln.d.ts.map +0 -1
  110. package/dist/push/sammeln.js +0 -112
  111. package/dist/push/sammeln.js.map +0 -1
  112. package/dist/push/verzeichnisse.d.ts +0 -40
  113. package/dist/push/verzeichnisse.d.ts.map +0 -1
  114. package/dist/push/verzeichnisse.js +0 -158
  115. package/dist/push/verzeichnisse.js.map +0 -1
  116. package/dist/push.d.ts +0 -8
  117. package/dist/push.d.ts.map +0 -1
  118. package/dist/push.js +0 -306
  119. package/dist/push.js.map +0 -1
  120. package/dist/starter.d.ts +0 -4
  121. package/dist/starter.d.ts.map +0 -1
  122. package/dist/starter.js +0 -587
  123. package/dist/starter.js.map +0 -1
  124. package/dist/tiefpruefung.d.ts +0 -39
  125. package/dist/tiefpruefung.d.ts.map +0 -1
  126. package/dist/tiefpruefung.js +0 -56
  127. package/dist/tiefpruefung.js.map +0 -1
  128. package/dist/vertrag.d.ts +0 -6
  129. package/dist/vertrag.d.ts.map +0 -1
  130. package/dist/vertrag.js +0 -267
  131. package/dist/vertrag.js.map +0 -1
  132. package/dist/vertragSpaet.d.ts +0 -4
  133. package/dist/vertragSpaet.d.ts.map +0 -1
  134. package/dist/vertragSpaet.js +0 -158
  135. package/dist/vertragSpaet.js.map +0 -1
  136. package/dist/vorlagen.d.ts +0 -7
  137. package/dist/vorlagen.d.ts.map +0 -1
  138. package/dist/vorlagen.js +0 -83
  139. package/dist/vorlagen.js.map +0 -1
package/dist/starter.js DELETED
@@ -1,587 +0,0 @@
1
- import { schemaWaechterVorlage } from './vorlagen.js';
2
- const ERSTE_TABELLE = 'app.einstellungen';
3
- export const NAME_MUSTER = /^[a-z0-9][a-z0-9._-]*$/;
4
- export function nameVorschlag(roh) {
5
- const vorschlag = roh
6
- .normalize('NFKD')
7
- // Die kombinierenden Akzente, die NFKD abgetrennt hat: aus einem Umlaut wird der Grundbuchstabe
8
- // und nicht ein Bindestrich. Sonst schluege der Vorschlag Namen mit Loechern darin vor.
9
- .replace(/[̀-ͯ]/g, '')
10
- .toLowerCase()
11
- .replace(/[^a-z0-9._-]+/g, '-')
12
- .replace(/-+/g, '-')
13
- .replace(/^[^a-z0-9]+/, '')
14
- .replace(/[-.]+$/, '');
15
- return vorschlag.slice(0, 214);
16
- }
17
- const ENV = `/**
18
- * env - die eine Stelle, an der diese App ihre Umgebung liest.
19
- *
20
- * ARKVEN GIBT PORT UND DATABASE_URL VOR. Im Container stehen beide in der Umgebung,
21
- * nicht in einer Datei. Ein fest verdrahteter Port ist von aussen nicht erreichbar,
22
- * und eine zweite Datenbank-Angabe waere eine zweite Wahrheit ueber dieselbe Sache.
23
- *
24
- * LOKAL DARF EINE .env DANEBENLIEGEN. Sie wird hier ohne Zusatzpaket gelesen, und ein
25
- * bereits gesetzter Wert gewinnt immer - sonst wuerde eine vergessene Zeile in .env
26
- * im Container die echte Verbindung ueberschreiben.
27
- */
28
-
29
- import fs from 'node:fs'
30
- import path from 'node:path'
31
- import { fileURLToPath } from 'node:url'
32
-
33
- /** src/config -> src -> Projektwurzel */
34
- const WURZEL = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..')
35
-
36
- function ladeDatei(datei: string): void {
37
- let text: string
38
- try {
39
- text = fs.readFileSync(datei, 'utf8')
40
- } catch {
41
- return // keine .env ist kein Fehler, im Container gibt es sie nie
42
- }
43
- for (const zeile of text.split(/\\r?\\n/)) {
44
- const t = zeile.trim()
45
- if (t === '' || t.startsWith('#')) continue
46
- const gleich = t.indexOf('=')
47
- if (gleich <= 0) continue
48
- const name = t.slice(0, gleich).trim()
49
- let wert = t.slice(gleich + 1).trim()
50
- if ((wert.startsWith('"') && wert.endsWith('"')) || (wert.startsWith("'") && wert.endsWith("'"))) {
51
- wert = wert.slice(1, -1)
52
- }
53
- if (process.env[name] === undefined) process.env[name] = wert
54
- }
55
- }
56
-
57
- ladeDatei(path.join(WURZEL, '.env'))
58
-
59
- export const env = {
60
- /** Arkven gibt den Port vor. Der Rueckfall gilt nur fuer lokale Laeufe. */
61
- port: Number(process.env['PORT'] ?? 3000),
62
- /** Genau eine Verbindung, gestellt von Arkven. Leer heisst: der Start bricht ab (src/index.ts). */
63
- datenbankUrl: process.env['DATABASE_URL'] ?? '',
64
- betrieb: process.env['NODE_ENV'] ?? 'development',
65
- }
66
-
67
- export const istProduktiv = env.betrieb === 'production'
68
- `;
69
- const SICHERHEITS_HEADER = `/**
70
- * Die Sicherheits-Grundhaertung dieser App - eine Quelle, ohne Zyklus.
71
- *
72
- * DIESES MODUL IMPORTIERT NICHTS. Der Einstiegspunkt prueft beim Start gegen diese
73
- * Tabelle (siehe src/index.ts), damit die Haertung dort nachlesbar ist. Laege die
74
- * Tabelle selbst in src/index.ts, entstuende ein Zyklus, sobald die Middleware sie
75
- * braucht - und die App waere beim ersten Zugriff undefiniert.
76
- *
77
- * WER HIER ETWAS LOCKERT, lockert es fuer die ganze App an einer Stelle. Das ist der
78
- * Sinn: die Frage 'wie streng wird diese Anwendung ausgeliefert?' hat genau eine
79
- * Antwort, und sie steht hier.
80
- */
81
- export const SICHERHEITS_HEADER: Record<string, string> = {
82
- 'X-Content-Type-Options': 'nosniff',
83
- 'X-Frame-Options': 'DENY',
84
- 'Referrer-Policy': 'strict-origin-when-cross-origin',
85
- 'Cross-Origin-Opener-Policy': 'same-origin',
86
- 'Permissions-Policy': 'geolocation=(), camera=(), microphone=()',
87
- 'Content-Security-Policy': [
88
- "default-src 'self'",
89
- "script-src 'self'",
90
- "style-src 'self' 'unsafe-inline'",
91
- "img-src 'self' data:",
92
- "connect-src 'self'",
93
- "frame-ancestors 'none'",
94
- "base-uri 'self'",
95
- "form-action 'self'",
96
- ].join('; '),
97
- }
98
-
99
- /** Nur im Produktivbetrieb gesetzt - lokal wuerde HTTPS-Zwang die Entwicklung blockieren. */
100
- export const HSTS = 'max-age=31536000; includeSubDomains'
101
- `;
102
- const EGRESS = `/**
103
- * egress - die EINE Stelle, an der diese App nach draussen spricht.
104
- *
105
- * WARUM ES SIE GIBT: Eine Allowlist an einer Stelle beantwortet die Frage 'mit wem
106
- * redet diese Anwendung?' abschliessend. Ein neuer Zielhost faellt beim ersten
107
- * Aufruf auf, statt ueber vier Dateien verteilt dazuzukommen. Arkvens Gate
108
- * verify:integrations verlangt genau das.
109
- *
110
- * DIE LISTE IST LEER, UND DAS IST RICHTIG SO: eine frische App spricht mit niemandem.
111
- * Der erste eigene Aufruf traegt seinen Host hier ein, mit Zweck - damit steht in
112
- * derselben Zeile, wohin diese App spricht und warum.
113
- *
114
- * Ein Host, der hier nicht steht, wird abgewiesen. Nicht geloggt und durchgelassen,
115
- * sondern abgewiesen: ein Egress-Schutz, der im Zweifel durchlaesst, ist keiner.
116
- */
117
-
118
- /**
119
- * Die erlaubten Ziele, je mit Grund. Muster fuer einen Eintrag:
120
- *
121
- * stripe: { basis: 'api.stripe.com', zweck: 'Zahlungen' },
122
- */
123
- export const ERLAUBTE_ZIELE: Record<string, { basis: string; zweck: string }> = {}
124
-
125
- /** Das Schema steht getrennt vom Host, damit keine vollstaendige URL als Literal neben einem Aufruf steht. */
126
- const SCHEMA = 'https://'
127
-
128
- function zielOderFehler(ziel: string): { basis: string; zweck: string } {
129
- const eintrag = ERLAUBTE_ZIELE[ziel]
130
- if (!eintrag) {
131
- throw new Error(
132
- 'egress: ' + ziel + ' steht nicht in der Allowlist. So kommst du weiter: trage den Host in ' +
133
- 'src/lib/egress.ts unter ERLAUBTE_ZIELE ein, mit Zweck.',
134
- )
135
- }
136
- return eintrag
137
- }
138
-
139
- /**
140
- * Ein ausgehender Aufruf an ein erlaubtes Ziel.
141
- *
142
- * @param ziel Schluessel aus ERLAUBTE_ZIELE - kein freier Host.
143
- * @param pfad Pfad inklusive fuehrendem Slash, z. B. '/v1/charges'.
144
- */
145
- export async function egressFetch(ziel: string, pfad: string, init?: RequestInit): Promise<Response> {
146
- const eintrag = zielOderFehler(ziel)
147
- if (!pfad.startsWith('/')) {
148
- throw new Error('egress: Pfad muss mit / beginnen (bekommen: ' + pfad + ')')
149
- }
150
- return fetch(SCHEMA + eintrag.basis + pfad, init)
151
- }
152
-
153
- /** Fuer Aufrufer, die eine vollstaendige URL brauchen, etwa um selbst Query-Werte anzuhaengen. */
154
- export function egressUrl(ziel: string, pfad: string): string {
155
- return SCHEMA + zielOderFehler(ziel).basis + pfad
156
- }
157
- `;
158
- const POOL = `/**
159
- * pool - die Verbindung zur Datenbank, aus DATABASE_URL.
160
- *
161
- * EINE VERBINDUNGSQUELLE, nicht mehrere: Arkven stellt genau eine Datenbank. Wer
162
- * daneben einen zweiten Pool aufmacht, hat zwei Wahrheiten ueber dieselbe Verbindung
163
- * und merkt beim naechsten Umzug, welche davon vergessen wurde.
164
- */
165
-
166
- import pg from 'pg'
167
- import { env } from '../config/env.js'
168
-
169
- export const pool = new pg.Pool({
170
- connectionString: env.datenbankUrl,
171
- max: 10,
172
- connectionTimeoutMillis: 10000,
173
- })
174
-
175
- /** Eine Abfrage, Zeilen zurueck. Werte immer als Parameter, nie in den Text gebaut. */
176
- export async function frage<T = Record<string, unknown>>(text: string, werte: unknown[] = []): Promise<T[]> {
177
- const ergebnis = await pool.query(text, werte)
178
- return ergebnis.rows as T[]
179
- }
180
-
181
- export async function schliessePool(): Promise<void> {
182
- await pool.end()
183
- }
184
- `;
185
- const SERVER = `/**
186
- * server - die HTTP-Schicht: Haertung, Routen, Fehlerbehandlung.
187
- *
188
- * DIE HAERTUNG STEHT VOR DEN ROUTEN. Eine Route, die vor der Header-Middleware
189
- * antwortet, geht ungehaertet raus - deshalb wird hier zuerst gesetzt und erst
190
- * danach gemountet.
191
- *
192
- * FEHLER GEHEN OHNE STACKTRACE NACH AUSSEN. Ein Stacktrace in der Antwort verraet
193
- * Pfade und Fassungen; die Ursache gehoert ins Log, der Ausweg in die Antwort.
194
- */
195
-
196
- import express from 'express'
197
- import type { Express, NextFunction, Request, Response } from 'express'
198
-
199
- import { istProduktiv } from '../config/env.js'
200
- import { frage } from '../db/pool.js'
201
- import { HSTS, SICHERHEITS_HEADER } from './sicherheitsHeader.js'
202
-
203
- export function baueApp(): Express {
204
- const app = express()
205
- app.disable('x-powered-by')
206
-
207
- app.use((_req: Request, res: Response, next: NextFunction) => {
208
- for (const [name, wert] of Object.entries(SICHERHEITS_HEADER)) res.setHeader(name, wert)
209
- if (istProduktiv) res.setHeader('Strict-Transport-Security', HSTS)
210
- next()
211
- })
212
-
213
- app.use(express.json({ limit: '1mb' }))
214
-
215
- /**
216
- * Die zwei Fragen, die eine Betriebsumgebung an einen Prozess stellt. Getrennt, weil sie
217
- * Verschiedenes bedeuten und Verschiedenes ausloesen:
218
- *
219
- * /health Lebt der Prozess? Ohne Datenbank, immer 200. Wer hier schweigt, wird neu gestartet.
220
- * /ready Kann er Arbeit annehmen? Mit echter Abfrage. Bei 503 wird NICHT neu gestartet,
221
- * sondern kein Verkehr geschickt - der Prozess wartet auf seine Datenbank.
222
- *
223
- * BEIDE VOR JEDEM RATE-LIMIT: Arkven fragt sie beim Umschalten bis zu 120-mal in 240 Sekunden ab,
224
- * alle von derselben Adresse. Hinter einem Limit sperrte die Bereitschaftspruefung sich selbst aus,
225
- * und der Deploy braeche mit "nicht gesund" ab, obwohl die App laengst laeuft.
226
- */
227
- app.get('/health', (_req: Request, res: Response) => {
228
- res.json({ ok: true })
229
- })
230
-
231
- app.get('/ready', (_req: Request, res: Response) => {
232
- frage('SELECT 1')
233
- .then(() => res.json({ bereit: true, db: true }))
234
- .catch((e: unknown) => {
235
- res.status(503).json({
236
- bereit: false,
237
- db: false,
238
- grund: e instanceof Error ? e.message : 'Datenbank nicht erreichbar',
239
- })
240
- })
241
- })
242
-
243
- /**
244
- * Die mitgelieferte Route: sie liest aus der Tabelle, die die erste Migration
245
- * anlegt. Damit laeuft die Kette Migration -> Waechter -> Start -> Abfrage
246
- * einmal vollstaendig durch, bevor die erste eigene Zeile geschrieben ist.
247
- */
248
- app.get('/api/einstellungen', (_req: Request, res: Response, next: NextFunction) => {
249
- frage('SELECT schluessel, wert FROM app.einstellungen ORDER BY schluessel')
250
- .then((zeilen) => res.json({ einstellungen: zeilen }))
251
- .catch(next)
252
- })
253
-
254
- app.use((_req: Request, res: Response) => {
255
- res.status(404).json({ fehler: 'Diese Adresse gibt es nicht.', ausweg: 'Prüfe den Pfad, oder lege die Route in src/http/server.ts an.' })
256
- })
257
-
258
- app.use((err: unknown, _req: Request, res: Response, _next: NextFunction) => {
259
- console.error('[http] ' + (err instanceof Error ? err.stack ?? err.message : String(err)))
260
- res.status(500).json({
261
- fehler: 'Die Anfrage liess sich nicht beantworten.',
262
- ausweg: 'Versuche es erneut. Bleibt es dabei, steht der Grund im Server-Log.',
263
- })
264
- })
265
-
266
- return app
267
- }
268
- `;
269
- const INDEX = `/**
270
- * Einstiegspunkt. Der Arkven-Container startet genau diese Datei ('tsx src/index.ts').
271
- *
272
- * ZWEI PRUEFUNGEN VOR DEM ERSTEN LAUSCHEN:
273
- *
274
- * 1. Die Haertung. Die vier Pflicht-Header stehen hier ausdruecklich, statt nur in
275
- * einer Datei tiefer im Baum: wer wissen will, wie streng diese Anwendung
276
- * ausgeliefert wird, liest hier. Faellt eine bei einem Umbau heraus, bricht der
277
- * Start ab, statt still ungehaertet zu laufen.
278
- * 2. Die Umgebung. Ohne DATABASE_URL wuerde jede Abfrage mit einem unverstaendlichen
279
- * Fehler enden, und die App saehe von aussen aus, als liefe sie.
280
- *
281
- * Was hier NICHT geprueft wird, ist das Schema - das macht src/schemaGate.ts, und der
282
- * Container ruft ihn vor dieser Datei.
283
- */
284
-
285
- import { env } from './config/env.js'
286
- import { schliessePool } from './db/pool.js'
287
- import { baueApp } from './http/server.js'
288
- import { SICHERHEITS_HEADER } from './http/sicherheitsHeader.js'
289
-
290
- const PFLICHT_HEADER = ['X-Content-Type-Options', 'X-Frame-Options', 'Content-Security-Policy', 'Referrer-Policy']
291
-
292
- function pruefeHaertung(): void {
293
- const fehlend = PFLICHT_HEADER.filter((h) => !SICHERHEITS_HEADER[h])
294
- if (fehlend.length > 0) {
295
- console.error('[start] Sicherheits-Header fehlen: ' + fehlend.join(', '))
296
- console.error(' So kommst du weiter: trage sie in src/http/sicherheitsHeader.ts wieder ein.')
297
- process.exit(1)
298
- }
299
- }
300
-
301
- function pruefeUmgebung(): void {
302
- if (env.datenbankUrl === '') {
303
- console.error('[start] DATABASE_URL fehlt, deshalb startet der Server nicht.')
304
- console.error(' Lokal: kopiere .env.example nach .env und trage deine Verbindung ein.')
305
- console.error(' Bei Arkven wird DATABASE_URL gestellt, dort ist nichts zu tun.')
306
- process.exit(1)
307
- }
308
- }
309
-
310
- async function main(): Promise<void> {
311
- pruefeHaertung()
312
- pruefeUmgebung()
313
-
314
- const app = baueApp()
315
- const server = app.listen(env.port, () => {
316
- console.log('')
317
- console.log(' Server läuft auf http://localhost:' + env.port)
318
- console.log(' Umgebung: ' + env.betrieb)
319
- console.log(' Pruefen: http://localhost:' + env.port + '/health')
320
- console.log('')
321
- })
322
-
323
- const beenden = (signal: string): void => {
324
- console.log('')
325
- console.log('[stop] ' + signal + ' empfangen, beende den Server.')
326
- server.close(() => {
327
- void schliessePool().then(() => process.exit(0))
328
- })
329
- // Notausgang, falls Verbindungen haengen.
330
- setTimeout(() => process.exit(1), 10000).unref()
331
- }
332
-
333
- process.on('SIGINT', () => beenden('SIGINT'))
334
- process.on('SIGTERM', () => beenden('SIGTERM'))
335
- }
336
-
337
- main().catch(async (err: unknown) => {
338
- console.error('[start] Fehlgeschlagen: ' + (err instanceof Error ? err.stack ?? err.message : String(err)))
339
- await schliessePool().catch(() => {})
340
- process.exit(1)
341
- })
342
- `;
343
- const MIGRATION = `-- 0000_start - das Fundament, das src/schemaGate.ts nach dem Migrieren nachmisst.
344
- --
345
- -- WARUM HIER SCHON EINE TABELLE STEHT: Der Waechter prueft gegen die laufende
346
- -- Datenbank. Eine leere Migration und ein Waechter ohne Pflicht-Tabelle waeren zwei
347
- -- gruene Haken ueber einer Pruefung, die nichts geprueft hat.
348
- --
349
- -- app.einstellungen haelt Werte, die sich ohne Deploy aendern duerfen. Brauchst du sie
350
- -- nicht, ersetze sie durch deine erste eigene Tabelle und ziehe src/schemaGate.ts nach -
351
- -- sonst prueft der Waechter etwas, das es nicht mehr gibt.
352
- --
353
- -- CREATE ... IF NOT EXISTS: der Container fuehrt Migrationen genau einmal aus, aber ein
354
- -- wiederholter Lauf von Hand soll nicht auf halber Strecke abbrechen.
355
-
356
- CREATE SCHEMA IF NOT EXISTS app;
357
-
358
- CREATE TABLE IF NOT EXISTS app.einstellungen (
359
- schluessel text PRIMARY KEY,
360
- wert jsonb NOT NULL,
361
- geaendert_am timestamptz NOT NULL DEFAULT now()
362
- );
363
- `;
364
- const JOURNAL = `{
365
- "version": "7",
366
- "dialect": "postgresql",
367
- "entries": [
368
- {
369
- "idx": 0,
370
- "version": "7",
371
- "when": 1767225600000,
372
- "tag": "0000_start",
373
- "breakpoints": false
374
- }
375
- ]
376
- }
377
- `;
378
- const DRIZZLE_CONFIG = `/**
379
- * drizzle.config.ts - fuer 'drizzle-kit migrate'.
380
- *
381
- * KEIN WURF BEI FEHLENDER DATABASE_URL. Arkven ruft drizzle-kit auch zur Bauzeit, und
382
- * dort gibt es die Verbindung noch nicht - sie wird erst in den Container gegeben. Ein
383
- * fail-closed an dieser Stelle wuerde genau das verhindern, was es schuetzen soll: die
384
- * Migrations-Erzeugung braeche ab, und der Deploy fiele auf einen blossen Schema-Abgleich
385
- * zurueck. Der Schutz gehoert an die Stelle, die wirklich verbindet - das ist 'migrate',
386
- * und dort sagt drizzle-kit es selbst.
387
- *
388
- * KEIN 'schema'-EINTRAG: die Migrationen unter drizzle/ sind handgeschriebenes SQL und
389
- * damit die einzige Quelle des Schemas. Ein schema-Eintrag eroeffnete eine zweite.
390
- */
391
-
392
- import type { Config } from 'drizzle-kit'
393
-
394
- export default {
395
- dialect: 'postgresql',
396
- out: './drizzle',
397
- dbCredentials: { url: process.env['DATABASE_URL'] ?? '' },
398
- } satisfies Config
399
- `;
400
- const TSCONFIG = `{
401
- "compilerOptions": {
402
- "target": "ES2022",
403
- "module": "NodeNext",
404
- "moduleResolution": "NodeNext",
405
- "lib": ["ES2022"],
406
- "types": ["node"],
407
- "strict": true,
408
- "noEmit": true,
409
- "skipLibCheck": true,
410
- "forceConsistentCasingInFileNames": true,
411
- "esModuleInterop": true,
412
- "resolveJsonModule": true
413
- },
414
- "include": ["src", "drizzle.config.ts"]
415
- }
416
- `;
417
- const ENV_BEISPIEL = `# Lokale Werte. Diese Datei nach .env kopieren und anpassen.
418
- # Bei Arkven werden PORT und DATABASE_URL gestellt, dort ist nichts zu tun.
419
-
420
- DATABASE_URL=postgres://postgres:postgres@localhost:5432/postgres
421
- PORT=3000
422
- NODE_ENV=development
423
- `;
424
- const GITIGNORE = `node_modules/
425
- dist/
426
- .env
427
- `;
428
- function paketJson(name) {
429
- return `${JSON.stringify({
430
- name,
431
- version: '0.1.0',
432
- private: true,
433
- type: 'module',
434
- engines: { node: '>=20' },
435
- scripts: {
436
- dev: 'tsx watch src/index.ts',
437
- start: 'tsx src/index.ts',
438
- migrate: 'drizzle-kit migrate',
439
- schema: 'tsx src/schemaGate.ts',
440
- typecheck: 'tsc --noEmit',
441
- },
442
- dependencies: {
443
- 'drizzle-kit': '^0.28.1',
444
- express: '^4.21.2',
445
- pg: '^8.13.1',
446
- tsx: '^4.19.2',
447
- },
448
- devDependencies: {
449
- '@types/express': '^4.17.21',
450
- '@types/node': '^20.19.0',
451
- '@types/pg': '^8.11.10',
452
- typescript: '^5.6.3',
453
- },
454
- }, null, 2)}\n`;
455
- }
456
- function readme(name) {
457
- return [
458
- `# ${name}`,
459
- '',
460
- 'Angelegt mit `arkven new`. Dieses Projekt erfüllt den Arkven-Container-Vertrag von der ersten',
461
- 'Zeile an: `arkven check` ist grün, bevor hier eigener Code steht.',
462
- '',
463
- '## Loslegen',
464
- '',
465
- '```bash',
466
- 'npm install',
467
- 'cp .env.example .env # DATABASE_URL eintragen',
468
- 'npm run migrate # legt app.einstellungen an',
469
- 'npm run dev # http://localhost:3000/health',
470
- '```',
471
- '',
472
- '## Was wo liegt',
473
- '',
474
- '| Datei | Wofür |',
475
- '|---|---|',
476
- '| `src/index.ts` | Einstieg. Der Container startet genau diese Datei. Prüft Härtung und Umgebung, bevor er lauscht. |',
477
- '| `src/schemaGate.ts` | Der Wächter nach der Migration. Fehlt eine Pflicht-Tabelle, bricht der Start ab. |',
478
- '| `src/http/sicherheitsHeader.ts` | Die Härtung an einer Stelle, ohne Importe. |',
479
- '| `src/http/server.ts` | Routen und Fehlerbehandlung. |',
480
- '| `src/config/env.ts` | Die einzige Stelle, die die Umgebung liest (PORT, DATABASE_URL). |',
481
- '| `src/db/pool.ts` | Die einzige Verbindung zur Datenbank. |',
482
- '| `src/lib/egress.ts` | Die einzige Stelle, an der die App nach draußen spricht. Host-Allowlist. |',
483
- '| `drizzle/` | Migrationen als SQL, mit Journal. Ohne Journal fahren sie nicht in der richtigen Reihenfolge. |',
484
- '',
485
- '## Die nächste Tabelle',
486
- '',
487
- 'Neue SQL-Datei unter `drizzle/` anlegen, Journal-Eintrag ergänzen (`npx drizzle-kit generate`',
488
- 'erzeugt beides), und die neue Pflicht-Tabelle in `src/schemaGate.ts` nachtragen. Der Wächter',
489
- 'prüft nur, was dort steht.',
490
- '',
491
- '## Arkven-Bausteine ergänzen',
492
- '',
493
- 'Dieses Projekt kommt ohne `@arkvenai/*`-Pakete, damit `npm install` sofort durchläuft. Die',
494
- 'Bausteine liegen in einer eigenen Registry; sobald du Zugang hast, holst du sie so dazu:',
495
- '',
496
- '```bash',
497
- 'arkven login',
498
- 'npm install @arkvenai/base @arkvenai/rate-limit',
499
- '```',
500
- '',
501
- 'Der Gewinn: ein Sicherheitsfix an einem Baustein erreicht diese App danach über eine',
502
- 'Aktualisierung statt über eine Kopie. Bis dahin meldet `arkven check` das als Hinweis, nicht',
503
- 'als Fehler.',
504
- '',
505
- '## Vor dem Veröffentlichen',
506
- '',
507
- '```bash',
508
- 'arkven check',
509
- '```',
510
- '',
511
- 'Was `check` hier nicht messen kann (Gates der Plattform, Verhaltens-Proben), nennt es',
512
- 'ausdrücklich. Es läuft beim Veröffentlichen mit.',
513
- '',
514
- ].join('\n');
515
- }
516
- function bauplan(name) {
517
- return `${JSON.stringify({
518
- $schema: 'https://app.arkven.de/schema/bauplan.json',
519
- summary: `${name} - hier steht in einem Satz, was die App für wen tut.`,
520
- userGroups: [{ name: 'Betreiber', can: 'Die App benutzen und ihre Einstellungen pflegen' }],
521
- screens: [
522
- {
523
- name: 'Start',
524
- purpose: 'Der Einstieg: zeigt, dass die App läuft, und führt weiter.',
525
- archetype: 'dashboard',
526
- sections: ['Begrüßung', 'Nächster Schritt'],
527
- actions: ['Zu den Einstellungen'],
528
- roles: ['Betreiber'],
529
- slug: '',
530
- },
531
- {
532
- name: 'Einstellungen',
533
- purpose: 'Werte, die sich ohne neuen Deploy ändern dürfen.',
534
- archetype: 'settings',
535
- sections: ['Liste der Werte'],
536
- actions: ['Wert ändern'],
537
- roles: ['Betreiber'],
538
- slug: 'einstellungen',
539
- },
540
- ],
541
- flows: [
542
- {
543
- name: 'Einstellung ändern',
544
- role: 'Betreiber',
545
- steps: [
546
- { screen: 'Start', action: 'Zu den Einstellungen gehen' },
547
- { screen: 'Einstellungen', action: 'Einen Wert ändern' },
548
- ],
549
- },
550
- ],
551
- }, null, 2)}
552
- `;
553
- }
554
- function auftrag(name) {
555
- return [
556
- `# Auftrag für ${name}`,
557
- '',
558
- 'Ersetze diesen Text durch das, was die App wirklich tun soll - in eigenen Worten, so wie du es',
559
- 'jemandem erzählen würdest. Ein Absatz genügt.',
560
- '',
561
- 'Er reist beim nächsten `arkven push` mit und steht danach im Cockpit unter „Das hast du gesagt".',
562
- 'Arkven ändert ihn nie von selbst.',
563
- '',
564
- ].join('\n');
565
- }
566
- export function starterDateien(name) {
567
- return {
568
- 'package.json': paketJson(name),
569
- 'tsconfig.json': TSCONFIG,
570
- 'drizzle.config.ts': DRIZZLE_CONFIG,
571
- '.gitignore': GITIGNORE,
572
- '.env.example': ENV_BEISPIEL,
573
- 'README.md': readme(name),
574
- 'src/index.ts': INDEX,
575
- 'src/schemaGate.ts': schemaWaechterVorlage({ tabellen: [ERSTE_TABELLE], angelegtVon: 'new' }),
576
- 'src/config/env.ts': ENV,
577
- 'src/http/server.ts': SERVER,
578
- 'src/http/sicherheitsHeader.ts': SICHERHEITS_HEADER,
579
- 'src/db/pool.ts': POOL,
580
- 'src/lib/egress.ts': EGRESS,
581
- 'drizzle/0000_start.sql': MIGRATION,
582
- 'drizzle/meta/_journal.json': JOURNAL,
583
- 'arkven/bauplan.json': bauplan(name),
584
- 'arkven/auftrag.md': auftrag(name),
585
- };
586
- }
587
- //# sourceMappingURL=starter.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"starter.js","sourceRoot":"","sources":["../src/starter.ts"],"names":[],"mappings":"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"}
@@ -1,39 +0,0 @@
1
- import type { Befund } from './befund.js';
2
- export interface TiefCheck {
3
- name: string;
4
- ok: boolean;
5
- required: boolean;
6
- details?: string;
7
- regel?: string;
8
- ort?: string;
9
- ausweg?: string;
10
- }
11
- export interface TiefErgebnis {
12
- ok: boolean;
13
- checks: TiefCheck[];
14
- ungeprueft: string[];
15
- mailArten?: string[];
16
- screens?: Array<{
17
- id: string;
18
- name: string;
19
- slug?: string;
20
- }>;
21
- }
22
- export type TiefAntwort = {
23
- erreichbar: true;
24
- ergebnis: TiefErgebnis;
25
- } | {
26
- erreichbar: false;
27
- grund: string;
28
- };
29
- export declare function tiefPruefen(dateien: ReadonlyArray<{
30
- pfad: string;
31
- inhalt: string;
32
- }>, serverDateien: ReadonlyArray<{
33
- pfad: string;
34
- inhalt: string;
35
- }>, opts?: {
36
- server?: string;
37
- }): Promise<TiefAntwort>;
38
- export declare function alsBefunde(e: TiefErgebnis): Befund[];
39
- //# sourceMappingURL=tiefpruefung.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tiefpruefung.d.ts","sourceRoot":"","sources":["../src/tiefpruefung.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,OAAO,CAAA;IACX,QAAQ,EAAE,OAAO,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,OAAO,CAAA;IACX,MAAM,EAAE,SAAS,EAAE,CAAA;IACnB,UAAU,EAAE,MAAM,EAAE,CAAA;IAEpB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAA;IAEpB,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAC7D;AAED,MAAM,MAAM,WAAW,GACnB;IAAE,UAAU,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,YAAY,CAAA;CAAE,GAC5C;IAAE,UAAU,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAExC,wBAAsB,WAAW,CAC/B,OAAO,EAAE,aAAa,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,EACxD,aAAa,EAAE,aAAa,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,EAC9D,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,GAC7B,OAAO,CAAC,WAAW,CAAC,CAsCtB;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,YAAY,GAAG,MAAM,EAAE,CAapD"}