@scenar/cli 0.1.20 → 0.2.1

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 (112) hide show
  1. package/example-bundle/assets/index-BLQ14SEo.js +147 -0
  2. package/example-bundle/assets/logo-f-5LjVqJ.png +0 -0
  3. package/example-bundle/assets/style-Dnzx_jUO.css +1 -0
  4. package/example-bundle/index.html +13 -0
  5. package/example-bundle/pack-manifest.json +36 -0
  6. package/example-bundle/scenario.json +9 -0
  7. package/package.json +17 -5
  8. package/src/__tests__/pack-generate-embed-entry.test.ts +59 -6
  9. package/src/__tests__/publish-command.test.ts +46 -0
  10. package/src/__tests__/publish-flow.test.ts +220 -0
  11. package/src/__tests__/read-viewport.test.ts +45 -0
  12. package/src/__tests__/run-publish.test.ts +92 -0
  13. package/src/__tests__/serve-command.test.ts +44 -0
  14. package/src/__tests__/serve-request-path.test.ts +67 -0
  15. package/src/__tests__/serve-static-server.test.ts +88 -0
  16. package/src/__tests__/try-command.test.ts +21 -0
  17. package/src/api.d.ts +31 -0
  18. package/src/api.d.ts.map +1 -0
  19. package/src/api.js +28 -0
  20. package/src/api.js.map +1 -0
  21. package/src/api.ts +55 -0
  22. package/src/bundle/read-viewport.d.ts +23 -0
  23. package/src/bundle/read-viewport.d.ts.map +1 -0
  24. package/src/bundle/read-viewport.js +25 -0
  25. package/src/bundle/read-viewport.js.map +1 -0
  26. package/src/bundle/read-viewport.ts +36 -0
  27. package/src/commands/deploy.d.ts.map +1 -1
  28. package/src/commands/deploy.js +8 -24
  29. package/src/commands/deploy.js.map +1 -1
  30. package/src/commands/deploy.ts +10 -28
  31. package/src/commands/narrate.d.ts.map +1 -1
  32. package/src/commands/narrate.js +39 -170
  33. package/src/commands/narrate.js.map +1 -1
  34. package/src/commands/narrate.ts +48 -289
  35. package/src/commands/pack.d.ts.map +1 -1
  36. package/src/commands/pack.js +12 -112
  37. package/src/commands/pack.js.map +1 -1
  38. package/src/commands/pack.ts +12 -128
  39. package/src/commands/publish.d.ts +3 -0
  40. package/src/commands/publish.d.ts.map +1 -0
  41. package/src/commands/publish.js +64 -0
  42. package/src/commands/publish.js.map +1 -0
  43. package/src/commands/publish.ts +86 -0
  44. package/src/commands/render.d.ts.map +1 -1
  45. package/src/commands/render.js +12 -194
  46. package/src/commands/render.js.map +1 -1
  47. package/src/commands/render.ts +19 -286
  48. package/src/commands/serve.d.ts +3 -0
  49. package/src/commands/serve.d.ts.map +1 -0
  50. package/src/commands/serve.js +91 -0
  51. package/src/commands/serve.js.map +1 -0
  52. package/src/commands/serve.ts +112 -0
  53. package/src/commands/try.d.ts +3 -0
  54. package/src/commands/try.d.ts.map +1 -0
  55. package/src/commands/try.js +99 -0
  56. package/src/commands/try.js.map +1 -0
  57. package/src/commands/try.ts +122 -0
  58. package/src/index.d.ts.map +1 -1
  59. package/src/index.js +6 -0
  60. package/src/index.js.map +1 -1
  61. package/src/index.ts +6 -0
  62. package/src/narrate/run-narrate.d.ts +43 -0
  63. package/src/narrate/run-narrate.d.ts.map +1 -0
  64. package/src/narrate/run-narrate.js +174 -0
  65. package/src/narrate/run-narrate.js.map +1 -0
  66. package/src/narrate/run-narrate.ts +298 -0
  67. package/src/pack/generate-embed-entry.d.ts +11 -0
  68. package/src/pack/generate-embed-entry.d.ts.map +1 -1
  69. package/src/pack/generate-embed-entry.js +81 -10
  70. package/src/pack/generate-embed-entry.js.map +1 -1
  71. package/src/pack/generate-embed-entry.ts +81 -10
  72. package/src/pack/run-pack.d.ts +34 -0
  73. package/src/pack/run-pack.d.ts.map +1 -0
  74. package/src/pack/run-pack.js +117 -0
  75. package/src/pack/run-pack.js.map +1 -0
  76. package/src/pack/run-pack.ts +163 -0
  77. package/src/publish/github.d.ts +11 -0
  78. package/src/publish/github.d.ts.map +1 -0
  79. package/src/publish/github.js +197 -0
  80. package/src/publish/github.js.map +1 -0
  81. package/src/publish/github.ts +216 -0
  82. package/src/publish/publish-flow.d.ts +110 -0
  83. package/src/publish/publish-flow.d.ts.map +1 -0
  84. package/src/publish/publish-flow.js +117 -0
  85. package/src/publish/publish-flow.js.map +1 -0
  86. package/src/publish/publish-flow.ts +199 -0
  87. package/src/publish/run-publish.d.ts +41 -0
  88. package/src/publish/run-publish.d.ts.map +1 -0
  89. package/src/publish/run-publish.js +77 -0
  90. package/src/publish/run-publish.js.map +1 -0
  91. package/src/publish/run-publish.ts +131 -0
  92. package/src/render/run-render.d.ts +31 -0
  93. package/src/render/run-render.d.ts.map +1 -0
  94. package/src/render/run-render.js +196 -0
  95. package/src/render/run-render.js.map +1 -0
  96. package/src/render/run-render.ts +287 -0
  97. package/src/serve/request-path.d.ts +23 -0
  98. package/src/serve/request-path.d.ts.map +1 -0
  99. package/src/serve/request-path.js +52 -0
  100. package/src/serve/request-path.js.map +1 -0
  101. package/src/serve/request-path.ts +55 -0
  102. package/src/serve/run-serve.d.ts +31 -0
  103. package/src/serve/run-serve.d.ts.map +1 -0
  104. package/src/serve/run-serve.js +35 -0
  105. package/src/serve/run-serve.js.map +1 -0
  106. package/src/serve/run-serve.ts +68 -0
  107. package/src/serve/static-server.d.ts +38 -0
  108. package/src/serve/static-server.d.ts.map +1 -0
  109. package/src/serve/static-server.js +91 -0
  110. package/src/serve/static-server.js.map +1 -0
  111. package/src/serve/static-server.ts +118 -0
  112. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,91 @@
1
+ import { createServer } from "node:http";
2
+ import { createReadStream } from "node:fs";
3
+ import { stat } from "node:fs/promises";
4
+ import { resolve, sep } from "node:path";
5
+ import { contentTypeFor, resolveStaticPath } from "./request-path.js";
6
+ /**
7
+ * Create (but do not start) an HTTP server that serves a packed bundle as a
8
+ * static site. Only GET/HEAD are allowed; every response carries the bundle
9
+ * contract's canonical content type and `Cache-Control: no-cache` (a local
10
+ * preview should always reflect the latest pack, never a stale asset).
11
+ *
12
+ * No CSP or other security headers are emitted: that envelope is the serving
13
+ * edge's responsibility (and is enforced + tested in scenar-cloud). A local
14
+ * preview deliberately matches the headerless behaviour of a plain static host
15
+ * such as GitHub Pages — the other Phase 1 target — so what you see locally is
16
+ * what a `scenar publish` visitor sees.
17
+ */
18
+ export function createBundleServer(rootDir) {
19
+ const root = resolve(rootDir);
20
+ return createServer((req, res) => {
21
+ void serveRequest(root, req, res).catch(() => {
22
+ if (!res.headersSent)
23
+ res.writeHead(500, { "content-type": "text/plain" });
24
+ res.end("Internal Server Error");
25
+ });
26
+ });
27
+ }
28
+ async function serveRequest(root, req, res) {
29
+ if (req.method !== "GET" && req.method !== "HEAD") {
30
+ res.writeHead(405, { allow: "GET, HEAD", "content-type": "text/plain" });
31
+ res.end("Method Not Allowed");
32
+ return;
33
+ }
34
+ const relativePath = resolveStaticPath(req.url ?? "/");
35
+ if (relativePath === null) {
36
+ res.writeHead(400, { "content-type": "text/plain" });
37
+ res.end("Bad Request");
38
+ return;
39
+ }
40
+ const absolutePath = resolve(root, relativePath);
41
+ // Defence in depth: the resolved path must stay within the bundle root even
42
+ // if resolveStaticPath ever let something through.
43
+ if (absolutePath !== root && !absolutePath.startsWith(root + sep)) {
44
+ res.writeHead(403, { "content-type": "text/plain" });
45
+ res.end("Forbidden");
46
+ return;
47
+ }
48
+ const info = await stat(absolutePath).catch(() => null);
49
+ if (!info || !info.isFile()) {
50
+ res.writeHead(404, { "content-type": "text/plain" });
51
+ res.end("Not Found");
52
+ return;
53
+ }
54
+ res.writeHead(200, {
55
+ "content-type": contentTypeFor(relativePath),
56
+ "content-length": info.size,
57
+ "cache-control": "no-cache",
58
+ });
59
+ if (req.method === "HEAD") {
60
+ res.end();
61
+ return;
62
+ }
63
+ createReadStream(absolutePath).pipe(res);
64
+ }
65
+ /**
66
+ * Start an HTTP server for a packed bundle and resolve once it is listening.
67
+ * Rejects if the port is already in use (the caller surfaces a clear message).
68
+ */
69
+ export function startBundleServer(options) {
70
+ const host = options.host ?? "localhost";
71
+ const server = createBundleServer(options.rootDir);
72
+ return new Promise((resolvePromise, reject) => {
73
+ const onError = (error) => reject(error);
74
+ server.once("error", onError);
75
+ server.listen(options.port, host, () => {
76
+ server.removeListener("error", onError);
77
+ const address = server.address();
78
+ const port = typeof address === "object" && address ? address.port : options.port;
79
+ // Wildcard binds are not dialable hostnames; advertise localhost instead.
80
+ const displayHost = host === "0.0.0.0" || host === "::" ? "localhost" : host;
81
+ resolvePromise({
82
+ url: `http://${displayHost}:${port}/`,
83
+ port,
84
+ close: () => new Promise((done, fail) => {
85
+ server.close((err) => (err ? fail(err) : done()));
86
+ }),
87
+ });
88
+ });
89
+ });
90
+ }
91
+ //# sourceMappingURL=static-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"static-server.js","sourceRoot":"","sources":["../../../src/serve/static-server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,118 @@
1
+ import { createServer, type IncomingMessage, type Server, type ServerResponse } from "node:http";
2
+ import { createReadStream } from "node:fs";
3
+ import { stat } from "node:fs/promises";
4
+ import { resolve, sep } from "node:path";
5
+ import { contentTypeFor, resolveStaticPath } from "./request-path.js";
6
+
7
+ /** A running bundle server: where it is reachable and how to stop it. */
8
+ export interface BundleServerHandle {
9
+ /** The base URL the bundle is served at (e.g. http://localhost:4173/). */
10
+ readonly url: string;
11
+ /** The port the server actually bound (useful when port 0 was requested). */
12
+ readonly port: number;
13
+ /** Stop accepting connections and release the port. */
14
+ close(): Promise<void>;
15
+ }
16
+
17
+ /** Options for {@link startBundleServer}. */
18
+ export interface StartBundleServerOptions {
19
+ /** Bundle directory to serve (a `scenar pack` output). */
20
+ readonly rootDir: string;
21
+ /** Port to bind; 0 lets the OS pick a free one (handy in tests). */
22
+ readonly port: number;
23
+ /** Host/interface to bind. Defaults to "localhost". */
24
+ readonly host?: string;
25
+ }
26
+
27
+ /**
28
+ * Create (but do not start) an HTTP server that serves a packed bundle as a
29
+ * static site. Only GET/HEAD are allowed; every response carries the bundle
30
+ * contract's canonical content type and `Cache-Control: no-cache` (a local
31
+ * preview should always reflect the latest pack, never a stale asset).
32
+ *
33
+ * No CSP or other security headers are emitted: that envelope is the serving
34
+ * edge's responsibility (and is enforced + tested in scenar-cloud). A local
35
+ * preview deliberately matches the headerless behaviour of a plain static host
36
+ * such as GitHub Pages — the other Phase 1 target — so what you see locally is
37
+ * what a `scenar publish` visitor sees.
38
+ */
39
+ export function createBundleServer(rootDir: string): Server {
40
+ const root = resolve(rootDir);
41
+ return createServer((req, res) => {
42
+ void serveRequest(root, req, res).catch(() => {
43
+ if (!res.headersSent) res.writeHead(500, { "content-type": "text/plain" });
44
+ res.end("Internal Server Error");
45
+ });
46
+ });
47
+ }
48
+
49
+ async function serveRequest(root: string, req: IncomingMessage, res: ServerResponse): Promise<void> {
50
+ if (req.method !== "GET" && req.method !== "HEAD") {
51
+ res.writeHead(405, { allow: "GET, HEAD", "content-type": "text/plain" });
52
+ res.end("Method Not Allowed");
53
+ return;
54
+ }
55
+
56
+ const relativePath = resolveStaticPath(req.url ?? "/");
57
+ if (relativePath === null) {
58
+ res.writeHead(400, { "content-type": "text/plain" });
59
+ res.end("Bad Request");
60
+ return;
61
+ }
62
+
63
+ const absolutePath = resolve(root, relativePath);
64
+ // Defence in depth: the resolved path must stay within the bundle root even
65
+ // if resolveStaticPath ever let something through.
66
+ if (absolutePath !== root && !absolutePath.startsWith(root + sep)) {
67
+ res.writeHead(403, { "content-type": "text/plain" });
68
+ res.end("Forbidden");
69
+ return;
70
+ }
71
+
72
+ const info = await stat(absolutePath).catch(() => null);
73
+ if (!info || !info.isFile()) {
74
+ res.writeHead(404, { "content-type": "text/plain" });
75
+ res.end("Not Found");
76
+ return;
77
+ }
78
+
79
+ res.writeHead(200, {
80
+ "content-type": contentTypeFor(relativePath),
81
+ "content-length": info.size,
82
+ "cache-control": "no-cache",
83
+ });
84
+ if (req.method === "HEAD") {
85
+ res.end();
86
+ return;
87
+ }
88
+ createReadStream(absolutePath).pipe(res);
89
+ }
90
+
91
+ /**
92
+ * Start an HTTP server for a packed bundle and resolve once it is listening.
93
+ * Rejects if the port is already in use (the caller surfaces a clear message).
94
+ */
95
+ export function startBundleServer(options: StartBundleServerOptions): Promise<BundleServerHandle> {
96
+ const host = options.host ?? "localhost";
97
+ const server = createBundleServer(options.rootDir);
98
+
99
+ return new Promise<BundleServerHandle>((resolvePromise, reject) => {
100
+ const onError = (error: Error) => reject(error);
101
+ server.once("error", onError);
102
+ server.listen(options.port, host, () => {
103
+ server.removeListener("error", onError);
104
+ const address = server.address();
105
+ const port = typeof address === "object" && address ? address.port : options.port;
106
+ // Wildcard binds are not dialable hostnames; advertise localhost instead.
107
+ const displayHost = host === "0.0.0.0" || host === "::" ? "localhost" : host;
108
+ resolvePromise({
109
+ url: `http://${displayHost}:${port}/`,
110
+ port,
111
+ close: () =>
112
+ new Promise<void>((done, fail) => {
113
+ server.close((err) => (err ? fail(err) : done()));
114
+ }),
115
+ });
116
+ });
117
+ });
118
+ }