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 preview
141
+ pnpm -C example/basic build
148
142
 
149
143
  pnpm -C example/dynamic dev
150
- pnpm -C example/dynamic preview
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.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": { "url": "https://github.com/zonayedpca/statikapi/issues" },
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": { "statikapi": "bin/statikapi.js" },
13
- "files": ["bin", "dist", "ui", "src", "README.md", "LICENSE"],
14
- "exports": { ".": { "import": "./src/index.js" } },
15
- "engines": { "node": ">=18" },
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": ["static", "json", "api", "cli", "ssg"],
18
- "publishConfig": { "access": "public", "provenance": true },
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",
@@ -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'; // NEW: for createReadStream
4
+ import fss from 'node:fs';
5
5
  import crypto from 'node:crypto';
6
- import http from 'node:http'; // NEW: tiny HTTP server
7
- import { fileURLToPath } from 'node:url'; // NEW: resolve UI dist
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
- // 0) Optional override for power users
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
- // 1) Bundled with the CLI: packages/cli/ui/ (your screenshot)
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
- // 2) Monorepo dev fallback: packages/ui/dist
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
- // 3) Last resort: throw with a helpful hint
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