statikapi 0.3.0 → 0.5.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/README.md
CHANGED
|
@@ -23,10 +23,8 @@ pnpm add -D statikapi
|
|
|
23
23
|
statikapi <command> [options]
|
|
24
24
|
|
|
25
25
|
Commands:
|
|
26
|
-
init Scaffold a new StatikAPI project
|
|
27
26
|
build Build static JSON endpoints
|
|
28
27
|
dev Watch & rebuild on changes
|
|
29
|
-
preview Serve the built JSON files + UI
|
|
30
28
|
|
|
31
29
|
Global:
|
|
32
30
|
-h, --help Show help
|
|
@@ -124,10 +122,6 @@ You can override via flags: `--srcDir <dir>`, `--outDir <dir>`.
|
|
|
124
122
|
- Rebuilds on changes, updates the preview UI via SSE.
|
|
125
123
|
- `--previewHost`, `--previewPort` — where to notify the preview server.
|
|
126
124
|
- `--srcDir`, `--outDir` — override config paths.
|
|
127
|
-
|
|
128
|
-
`preview`
|
|
129
|
-
|
|
130
|
-
- Serves `api-out/` and the UI at `/\_ui`.
|
|
131
125
|
- `--host` (default 127.0.0.1)
|
|
132
126
|
- `--port` (default 8788)
|
|
133
127
|
- `--open` — try to open the browser
|
|
@@ -144,15 +138,17 @@ There are two example projects in this repo under `example/`:
|
|
|
144
138
|
# from repo root
|
|
145
139
|
|
|
146
140
|
pnpm -C example/basic dev
|
|
147
|
-
pnpm -C example/basic
|
|
141
|
+
pnpm -C example/basic build
|
|
148
142
|
|
|
149
143
|
pnpm -C example/dynamic dev
|
|
150
|
-
pnpm -C example/dynamic
|
|
144
|
+
pnpm -C example/dynamic build
|
|
145
|
+
|
|
146
|
+
pnpm -C example/showcase dev
|
|
147
|
+
pnpm -C example/showcase build
|
|
151
148
|
```
|
|
152
149
|
|
|
153
150
|
## Troubleshooting
|
|
154
151
|
|
|
155
|
-
- UI doesn’t load: ensure `preview` is running; if you’re developing the UI separately, start Vite on port 5173 or pass `--uiDir` to serve a built UI.
|
|
156
152
|
- Dynamic routes not emitted: make sure the file exports a valid `paths()` function returning strings (or arrays of strings for catch-all).
|
|
157
153
|
|
|
158
154
|
License
|
package/package.json
CHANGED
|
@@ -1,21 +1,47 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "statikapi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/zonayedpca/statikapi",
|
|
7
7
|
"directory": "packages/cli"
|
|
8
8
|
},
|
|
9
|
-
"bugs": {
|
|
9
|
+
"bugs": {
|
|
10
|
+
"url": "https://github.com/zonayedpca/statikapi/issues"
|
|
11
|
+
},
|
|
10
12
|
"homepage": "https://github.com/zonayedpca/statikapi#readme",
|
|
11
13
|
"type": "module",
|
|
12
|
-
"bin": {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
"
|
|
14
|
+
"bin": {
|
|
15
|
+
"statikapi": "bin/statikapi.js"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"bin",
|
|
19
|
+
"dist",
|
|
20
|
+
"ui",
|
|
21
|
+
"src",
|
|
22
|
+
"README.md",
|
|
23
|
+
"LICENSE"
|
|
24
|
+
],
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"import": "./src/index.js"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=18"
|
|
32
|
+
},
|
|
16
33
|
"license": "MIT",
|
|
17
|
-
"keywords": [
|
|
18
|
-
|
|
34
|
+
"keywords": [
|
|
35
|
+
"static",
|
|
36
|
+
"json",
|
|
37
|
+
"api",
|
|
38
|
+
"cli",
|
|
39
|
+
"ssg"
|
|
40
|
+
],
|
|
41
|
+
"publishConfig": {
|
|
42
|
+
"access": "public",
|
|
43
|
+
"provenance": true
|
|
44
|
+
},
|
|
19
45
|
"dependencies": {
|
|
20
46
|
"chokidar": "^3.6.0",
|
|
21
47
|
"sirv": "^2.0.4",
|
package/src/commands/dev.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import chokidar from 'chokidar';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import fs from 'node:fs/promises';
|
|
4
|
-
import fss from 'node:fs';
|
|
4
|
+
import fss from 'node:fs';
|
|
5
5
|
import crypto from 'node:crypto';
|
|
6
|
-
import http from 'node:http';
|
|
7
|
-
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import http from 'node:http';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
8
|
|
|
9
9
|
import { loadConfig } from '../config/loadConfig.js';
|
|
10
10
|
import { loadModuleValue } from '../loader/loadModuleValue.js';
|
|
@@ -72,13 +72,11 @@ export default async function devCmd(argv) {
|
|
|
72
72
|
const { config } = await loadConfig({ flags });
|
|
73
73
|
|
|
74
74
|
// Where to notify preview
|
|
75
|
-
// NEW: dev server + UI defaults
|
|
76
75
|
const host = String(flags.host ?? '127.0.0.1');
|
|
77
76
|
const port = Number.isFinite(flags.port) ? Number(flags.port) : 8788;
|
|
78
77
|
const noUi = !!(flags['no-ui'] || flags.noUi);
|
|
79
78
|
const noOpen = !!(flags['no-open'] || flags.noOpen);
|
|
80
79
|
|
|
81
|
-
// NEW: live SSE clients
|
|
82
80
|
const sseClients = new Set(); // each entry: { id, res }
|
|
83
81
|
function sseBroadcast(msg) {
|
|
84
82
|
const line = `data: ${msg}\n\n`;
|
|
@@ -274,7 +272,6 @@ export default async function devCmd(argv) {
|
|
|
274
272
|
await writeManifest();
|
|
275
273
|
console.log(`[statikapi] ready. Watching ${path.relative(process.cwd(), config.paths.srcAbs)}/`);
|
|
276
274
|
|
|
277
|
-
// NEW: start HTTP server (UI + JSON helpers + SSE)
|
|
278
275
|
const server = http.createServer(async (req, res) => {
|
|
279
276
|
try {
|
|
280
277
|
let url;
|
|
@@ -286,7 +283,6 @@ export default async function devCmd(argv) {
|
|
|
286
283
|
}
|
|
287
284
|
const pathname = url.pathname;
|
|
288
285
|
|
|
289
|
-
// 1) SSE: /_ui/events
|
|
290
286
|
if (pathname === '/_ui/events') {
|
|
291
287
|
res.writeHead(200, {
|
|
292
288
|
'Content-Type': 'text/event-stream',
|
|
@@ -301,7 +297,6 @@ export default async function devCmd(argv) {
|
|
|
301
297
|
return;
|
|
302
298
|
}
|
|
303
299
|
|
|
304
|
-
// 2) Manifest JSON for UI: /ui/index
|
|
305
300
|
if (pathname === '/ui/index' && req.method === 'GET') {
|
|
306
301
|
const list = Array.from(manifestByRoute.values()).sort((a, b) =>
|
|
307
302
|
a.route.localeCompare(b.route)
|
|
@@ -312,7 +307,6 @@ export default async function devCmd(argv) {
|
|
|
312
307
|
return;
|
|
313
308
|
}
|
|
314
309
|
|
|
315
|
-
// 3) Serve built file content: /_ui/file?route=/path
|
|
316
310
|
if (pathname === '/_ui/file' && req.method === 'GET') {
|
|
317
311
|
const route = url.searchParams.get('route') || '';
|
|
318
312
|
const outFile = routeToOutPath({ outAbs: config.paths.outAbs, route });
|
|
@@ -332,7 +326,6 @@ export default async function devCmd(argv) {
|
|
|
332
326
|
return;
|
|
333
327
|
}
|
|
334
328
|
|
|
335
|
-
// 4) Static React UI at /_ui/* (unless --no-ui)
|
|
336
329
|
if (!noUi && pathname.startsWith('/_ui/')) {
|
|
337
330
|
const uiRoot = resolveUiDist();
|
|
338
331
|
const rel = pathname.replace(/^\/_ui\//, '') || 'index.html';
|
|
@@ -360,13 +353,45 @@ export default async function devCmd(argv) {
|
|
|
360
353
|
return;
|
|
361
354
|
}
|
|
362
355
|
|
|
363
|
-
// 5) Root → redirect to UI (unless --no-ui)
|
|
364
356
|
if (!noUi && pathname === '/') {
|
|
365
357
|
res.writeHead(302, { Location: '/_ui/' });
|
|
366
358
|
res.end();
|
|
367
359
|
return;
|
|
368
360
|
}
|
|
369
361
|
|
|
362
|
+
// Serve built JSON directly from api-out
|
|
363
|
+
{
|
|
364
|
+
const outRoot = config.paths.outAbs;
|
|
365
|
+
// strip leading slash and normalize
|
|
366
|
+
const rel = pathname.replace(/^\/+/, '');
|
|
367
|
+
const candidates = [];
|
|
368
|
+
|
|
369
|
+
// If the request ends with .json, try that file directly
|
|
370
|
+
if (rel.endsWith('.json')) {
|
|
371
|
+
candidates.push(path.join(outRoot, rel));
|
|
372
|
+
} else {
|
|
373
|
+
// Otherwise, try a folder with index.json (e.g. "/" or "/users/1/")
|
|
374
|
+
candidates.push(path.join(outRoot, rel, 'index.json'));
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
for (const cand of candidates) {
|
|
378
|
+
const file = path.resolve(cand);
|
|
379
|
+
// prevent path traversal
|
|
380
|
+
if (!file.startsWith(path.resolve(outRoot))) continue;
|
|
381
|
+
|
|
382
|
+
try {
|
|
383
|
+
const st = await fs.stat(file);
|
|
384
|
+
if (st.isFile()) {
|
|
385
|
+
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
386
|
+
fss.createReadStream(file).pipe(res);
|
|
387
|
+
return; // served
|
|
388
|
+
}
|
|
389
|
+
} catch {
|
|
390
|
+
// try next candidate
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
370
395
|
// Otherwise: 404
|
|
371
396
|
res.statusCode = 404;
|
|
372
397
|
res.end('Not Found');
|
|
@@ -406,7 +431,6 @@ export default async function devCmd(argv) {
|
|
|
406
431
|
return 0;
|
|
407
432
|
}
|
|
408
433
|
|
|
409
|
-
// NEW: helpers (static file & UI dist resolver & opener)
|
|
410
434
|
function streamFile(file, res) {
|
|
411
435
|
const ext = path.extname(file).toLowerCase();
|
|
412
436
|
const ctype =
|
|
@@ -428,19 +452,19 @@ function streamFile(file, res) {
|
|
|
428
452
|
}
|
|
429
453
|
|
|
430
454
|
function resolveUiDist() {
|
|
431
|
-
//
|
|
455
|
+
// Optional override for power users
|
|
432
456
|
const fromEnv = process.env.STATIKAPI_UI_DIR;
|
|
433
457
|
if (fromEnv && hasIndex(fromEnv)) return fromEnv;
|
|
434
458
|
|
|
435
|
-
//
|
|
459
|
+
// Bundled with the CLI: packages/cli/ui/
|
|
436
460
|
const bundled = path.resolve(__dirname, '..', '..', 'ui');
|
|
437
461
|
if (hasIndex(bundled)) return bundled;
|
|
438
462
|
|
|
439
|
-
//
|
|
463
|
+
// Monorepo dev fallback: packages/ui/dist
|
|
440
464
|
const monorepoDist = path.resolve(__dirname, '..', '..', '..', 'ui', 'dist');
|
|
441
465
|
if (hasIndex(monorepoDist)) return monorepoDist;
|
|
442
466
|
|
|
443
|
-
//
|
|
467
|
+
// Last resort: throw with a helpful hint
|
|
444
468
|
throw new Error(
|
|
445
469
|
'StatikAPI UI build not found. ' +
|
|
446
470
|
'Either keep a built UI at packages/cli/ui/ (index.html present), ' +
|
package/src/help.js
CHANGED
|
@@ -3,11 +3,9 @@ export const HELP = `statikapi — Static API generator
|
|
|
3
3
|
Usage:
|
|
4
4
|
statikapi <command> [options]
|
|
5
5
|
|
|
6
|
-
Commands:
|
|
7
|
-
init Scaffold a new StatikAPI project
|
|
6
|
+
Commands:Scaffold a new StatikAPI project
|
|
8
7
|
build Build static JSON endpoints
|
|
9
8
|
dev Start dev mode (watch & rebuild)
|
|
10
|
-
preview Serve the built JSON files
|
|
11
9
|
|
|
12
10
|
Global options:
|
|
13
11
|
-h, --help Show help
|