@scenar/cli 0.1.20 → 0.2.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/package.json +17 -5
- package/src/__tests__/publish-command.test.ts +46 -0
- package/src/__tests__/publish-flow.test.ts +220 -0
- package/src/__tests__/read-viewport.test.ts +45 -0
- package/src/__tests__/run-publish.test.ts +92 -0
- package/src/__tests__/serve-command.test.ts +44 -0
- package/src/__tests__/serve-request-path.test.ts +67 -0
- package/src/__tests__/serve-static-server.test.ts +88 -0
- package/src/api.d.ts +31 -0
- package/src/api.d.ts.map +1 -0
- package/src/api.js +28 -0
- package/src/api.js.map +1 -0
- package/src/api.ts +55 -0
- package/src/bundle/read-viewport.d.ts +23 -0
- package/src/bundle/read-viewport.d.ts.map +1 -0
- package/src/bundle/read-viewport.js +25 -0
- package/src/bundle/read-viewport.js.map +1 -0
- package/src/bundle/read-viewport.ts +36 -0
- package/src/commands/deploy.d.ts.map +1 -1
- package/src/commands/deploy.js +8 -24
- package/src/commands/deploy.js.map +1 -1
- package/src/commands/deploy.ts +10 -28
- package/src/commands/narrate.d.ts.map +1 -1
- package/src/commands/narrate.js +39 -170
- package/src/commands/narrate.js.map +1 -1
- package/src/commands/narrate.ts +48 -289
- package/src/commands/pack.d.ts.map +1 -1
- package/src/commands/pack.js +12 -112
- package/src/commands/pack.js.map +1 -1
- package/src/commands/pack.ts +12 -128
- package/src/commands/publish.d.ts +3 -0
- package/src/commands/publish.d.ts.map +1 -0
- package/src/commands/publish.js +64 -0
- package/src/commands/publish.js.map +1 -0
- package/src/commands/publish.ts +86 -0
- package/src/commands/render.d.ts.map +1 -1
- package/src/commands/render.js +12 -194
- package/src/commands/render.js.map +1 -1
- package/src/commands/render.ts +19 -286
- package/src/commands/serve.d.ts +3 -0
- package/src/commands/serve.d.ts.map +1 -0
- package/src/commands/serve.js +91 -0
- package/src/commands/serve.js.map +1 -0
- package/src/commands/serve.ts +112 -0
- package/src/index.d.ts.map +1 -1
- package/src/index.js +4 -0
- package/src/index.js.map +1 -1
- package/src/index.ts +4 -0
- package/src/narrate/run-narrate.d.ts +43 -0
- package/src/narrate/run-narrate.d.ts.map +1 -0
- package/src/narrate/run-narrate.js +174 -0
- package/src/narrate/run-narrate.js.map +1 -0
- package/src/narrate/run-narrate.ts +298 -0
- package/src/pack/run-pack.d.ts +34 -0
- package/src/pack/run-pack.d.ts.map +1 -0
- package/src/pack/run-pack.js +117 -0
- package/src/pack/run-pack.js.map +1 -0
- package/src/pack/run-pack.ts +163 -0
- package/src/publish/github.d.ts +11 -0
- package/src/publish/github.d.ts.map +1 -0
- package/src/publish/github.js +197 -0
- package/src/publish/github.js.map +1 -0
- package/src/publish/github.ts +216 -0
- package/src/publish/publish-flow.d.ts +110 -0
- package/src/publish/publish-flow.d.ts.map +1 -0
- package/src/publish/publish-flow.js +117 -0
- package/src/publish/publish-flow.js.map +1 -0
- package/src/publish/publish-flow.ts +199 -0
- package/src/publish/run-publish.d.ts +41 -0
- package/src/publish/run-publish.d.ts.map +1 -0
- package/src/publish/run-publish.js +77 -0
- package/src/publish/run-publish.js.map +1 -0
- package/src/publish/run-publish.ts +131 -0
- package/src/render/run-render.d.ts +31 -0
- package/src/render/run-render.d.ts.map +1 -0
- package/src/render/run-render.js +196 -0
- package/src/render/run-render.js.map +1 -0
- package/src/render/run-render.ts +287 -0
- package/src/serve/request-path.d.ts +23 -0
- package/src/serve/request-path.d.ts.map +1 -0
- package/src/serve/request-path.js +52 -0
- package/src/serve/request-path.js.map +1 -0
- package/src/serve/request-path.ts +55 -0
- package/src/serve/run-serve.d.ts +31 -0
- package/src/serve/run-serve.d.ts.map +1 -0
- package/src/serve/run-serve.js +35 -0
- package/src/serve/run-serve.js.map +1 -0
- package/src/serve/run-serve.ts +68 -0
- package/src/serve/static-server.d.ts +38 -0
- package/src/serve/static-server.d.ts.map +1 -0
- package/src/serve/static-server.js +91 -0
- package/src/serve/static-server.js.map +1 -0
- package/src/serve/static-server.ts +118 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Pages publishing — the OSS "permanent public URL" path of Phase 1.
|
|
3
|
+
*
|
|
4
|
+
* The flow is dependency-injected (mirroring deploy-flow.ts) so its ordering and
|
|
5
|
+
* payloads are unit-testable against fakes, with zero live GitHub calls in the
|
|
6
|
+
* test suite. The real `gh`/`git` implementations live in ./github.ts.
|
|
7
|
+
*/
|
|
8
|
+
/** Identity for the target GitHub repository + how to write to it. */
|
|
9
|
+
export interface PublishOptions {
|
|
10
|
+
/** The packed bundle directory to publish. */
|
|
11
|
+
readonly bundleDir: string;
|
|
12
|
+
/** Target repository name (default: the scenario slug). */
|
|
13
|
+
readonly repo: string;
|
|
14
|
+
/**
|
|
15
|
+
* Subdirectory within the repo to publish into, normalized (no leading/
|
|
16
|
+
* trailing slashes). Empty string means the repo root — a dedicated,
|
|
17
|
+
* single-tour repo whose every publish replaces the whole branch. A non-empty
|
|
18
|
+
* path (e.g. `welcome-tour`) lets many tours coexist in one repo; publishing
|
|
19
|
+
* replaces only that subfolder and preserves its siblings.
|
|
20
|
+
*/
|
|
21
|
+
readonly path: string;
|
|
22
|
+
/** Target owner (a GitHub org); omitted means the authenticated user. */
|
|
23
|
+
readonly org?: string;
|
|
24
|
+
/** Create the repo private rather than public (Pages needs Pro for private). */
|
|
25
|
+
readonly private: boolean;
|
|
26
|
+
/** Commit message for the published snapshot. */
|
|
27
|
+
readonly message: string;
|
|
28
|
+
}
|
|
29
|
+
/** The published location. */
|
|
30
|
+
export interface PublishResult {
|
|
31
|
+
readonly owner: string;
|
|
32
|
+
readonly repo: string;
|
|
33
|
+
/** The subdirectory the embed was published into ("" for the repo root). */
|
|
34
|
+
readonly path: string;
|
|
35
|
+
/** The repository's web URL. */
|
|
36
|
+
readonly repoUrl: string;
|
|
37
|
+
/** The GitHub Pages URL the embed is served at (includes the path). */
|
|
38
|
+
readonly pagesUrl: string;
|
|
39
|
+
/** True when the repo was created by this run (vs. reused). */
|
|
40
|
+
readonly created: boolean;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The side-effecting operations the flow depends on, injected so the ordering
|
|
44
|
+
* can be tested without `gh`, `git`, or the network.
|
|
45
|
+
*/
|
|
46
|
+
export interface PublishFlowDeps {
|
|
47
|
+
/** Verify `gh` + `git` are installed and `gh` is authenticated; else throw. */
|
|
48
|
+
ensureToolsAvailable(): Promise<void>;
|
|
49
|
+
/** Resolve the target owner: the explicit org, or the authenticated user. */
|
|
50
|
+
resolveOwner(explicitOrg: string | undefined): Promise<string>;
|
|
51
|
+
/** Whether owner/repo already exists. */
|
|
52
|
+
repoExists(owner: string, repo: string): Promise<boolean>;
|
|
53
|
+
/** Create owner/repo (public unless private requested). */
|
|
54
|
+
createRepo(owner: string, repo: string, opts: {
|
|
55
|
+
private: boolean;
|
|
56
|
+
description: string;
|
|
57
|
+
}): Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* Publish the bundle (plus a root .nojekyll) to the repo's gh-pages branch.
|
|
60
|
+
* An empty `path` replaces the whole branch (dedicated repo); a non-empty
|
|
61
|
+
* `path` updates only that subfolder, preserving sibling tours already on the
|
|
62
|
+
* branch.
|
|
63
|
+
*/
|
|
64
|
+
pushBundleToPages(input: {
|
|
65
|
+
bundleDir: string;
|
|
66
|
+
owner: string;
|
|
67
|
+
repo: string;
|
|
68
|
+
path: string;
|
|
69
|
+
message: string;
|
|
70
|
+
}): Promise<void>;
|
|
71
|
+
/** Enable GitHub Pages from gh-pages (idempotent — a no-op if already on). */
|
|
72
|
+
enablePages(owner: string, repo: string): Promise<void>;
|
|
73
|
+
/** Progress sink (stderr in the command; captured in tests). */
|
|
74
|
+
log(message: string): void;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Validate a GitHub repository name. Returns an error message, or null if valid.
|
|
78
|
+
* The scenario slug (the default) is always valid; this guards an explicit
|
|
79
|
+
* `--repo` before any repo is touched.
|
|
80
|
+
*/
|
|
81
|
+
export declare function validateRepoName(repo: string): string | null;
|
|
82
|
+
/**
|
|
83
|
+
* Normalize a publish path to a clean, slash-separated subpath with no leading,
|
|
84
|
+
* trailing, or doubled slashes. `/`, `.`, and empty all normalize to "" (root).
|
|
85
|
+
*/
|
|
86
|
+
export declare function normalizePath(path: string | undefined): string;
|
|
87
|
+
/**
|
|
88
|
+
* Validate a (pre-normalized) publish subpath. Returns an error message, or null
|
|
89
|
+
* if valid. "" (root) is always valid; each segment must be a safe slug and
|
|
90
|
+
* never `..` (no directory traversal into the branch).
|
|
91
|
+
*/
|
|
92
|
+
export declare function validatePublishPath(path: string): string | null;
|
|
93
|
+
/**
|
|
94
|
+
* The GitHub Pages URL for owner/repo, optionally under a subpath. A repository
|
|
95
|
+
* named exactly `<owner>.github.io` is the user/org site and serves at the apex;
|
|
96
|
+
* every other repo serves under its name as a path (which is why packed bundles
|
|
97
|
+
* use a relative base so they work from a subpath). A non-empty `path` appends a
|
|
98
|
+
* further subdirectory so many tours can share one repo.
|
|
99
|
+
*/
|
|
100
|
+
export declare function pagesUrl(owner: string, repo: string, path?: string): string;
|
|
101
|
+
/** The repository's web URL. */
|
|
102
|
+
export declare function repoUrl(owner: string, repo: string): string;
|
|
103
|
+
/**
|
|
104
|
+
* Drive the publish: verify tooling, resolve the owner, ensure the repo exists,
|
|
105
|
+
* push the bundle to gh-pages, and enable Pages. Ordering is load-bearing — the
|
|
106
|
+
* repo must exist before the push, and the gh-pages branch must exist (from the
|
|
107
|
+
* push) before Pages can be pointed at it.
|
|
108
|
+
*/
|
|
109
|
+
export declare function runPublishFlow(deps: PublishFlowDeps, opts: PublishOptions): Promise<PublishResult>;
|
|
110
|
+
//# sourceMappingURL=publish-flow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publish-flow.d.ts","sourceRoot":"","sources":["../../../src/publish/publish-flow.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAWH,sEAAsE;AACtE,MAAM,WAAW,cAAc;IAC7B,8CAA8C;IAC9C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,2DAA2D;IAC3D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;;;OAMG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,yEAAyE;IACzE,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,gFAAgF;IAChF,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,iDAAiD;IACjD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,8BAA8B;AAC9B,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4EAA4E;IAC5E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,gCAAgC;IAChC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,uEAAuE;IACvE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,+EAA+E;IAC/E,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,6EAA6E;IAC7E,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/D,yCAAyC;IACzC,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1D,2DAA2D;IAC3D,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxG;;;;;OAKG;IACH,iBAAiB,CAAC,KAAK,EAAE;QACvB,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;KACjB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClB,8EAA8E;IAC9E,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,gEAAgE;IAChE,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAW5D;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAM9D;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAS/D;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAK3E;AAED,gCAAgC;AAChC,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,eAAe,EACrB,IAAI,EAAE,cAAc,GACnB,OAAO,CAAC,aAAa,CAAC,CAuCxB"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Pages publishing — the OSS "permanent public URL" path of Phase 1.
|
|
3
|
+
*
|
|
4
|
+
* The flow is dependency-injected (mirroring deploy-flow.ts) so its ordering and
|
|
5
|
+
* payloads are unit-testable against fakes, with zero live GitHub calls in the
|
|
6
|
+
* test suite. The real `gh`/`git` implementations live in ./github.ts.
|
|
7
|
+
*/
|
|
8
|
+
/** A repo name's allowed characters (GitHub: alphanumerics, ., _, -). */
|
|
9
|
+
const REPO_NAME_PATTERN = /^[A-Za-z0-9._-]+$/;
|
|
10
|
+
/** GitHub caps repository names at 100 characters. */
|
|
11
|
+
const MAX_REPO_NAME_LENGTH = 100;
|
|
12
|
+
/** Allowed characters for a single publish path segment. */
|
|
13
|
+
const PATH_SEGMENT_PATTERN = /^[A-Za-z0-9._-]+$/;
|
|
14
|
+
/**
|
|
15
|
+
* Validate a GitHub repository name. Returns an error message, or null if valid.
|
|
16
|
+
* The scenario slug (the default) is always valid; this guards an explicit
|
|
17
|
+
* `--repo` before any repo is touched.
|
|
18
|
+
*/
|
|
19
|
+
export function validateRepoName(repo) {
|
|
20
|
+
if (repo.length === 0 || repo.length > MAX_REPO_NAME_LENGTH) {
|
|
21
|
+
return `repo name must be 1-${MAX_REPO_NAME_LENGTH} characters`;
|
|
22
|
+
}
|
|
23
|
+
if (repo === "." || repo === "..") {
|
|
24
|
+
return `repo name must not be "." or ".."`;
|
|
25
|
+
}
|
|
26
|
+
if (!REPO_NAME_PATTERN.test(repo)) {
|
|
27
|
+
return `invalid repo name "${repo}"; allowed: letters, digits, ".", "_", "-"`;
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Normalize a publish path to a clean, slash-separated subpath with no leading,
|
|
33
|
+
* trailing, or doubled slashes. `/`, `.`, and empty all normalize to "" (root).
|
|
34
|
+
*/
|
|
35
|
+
export function normalizePath(path) {
|
|
36
|
+
return (path ?? "")
|
|
37
|
+
.split("/")
|
|
38
|
+
.map((seg) => seg.trim())
|
|
39
|
+
.filter((seg) => seg.length > 0 && seg !== ".")
|
|
40
|
+
.join("/");
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Validate a (pre-normalized) publish subpath. Returns an error message, or null
|
|
44
|
+
* if valid. "" (root) is always valid; each segment must be a safe slug and
|
|
45
|
+
* never `..` (no directory traversal into the branch).
|
|
46
|
+
*/
|
|
47
|
+
export function validatePublishPath(path) {
|
|
48
|
+
if (path.length === 0)
|
|
49
|
+
return null;
|
|
50
|
+
for (const seg of path.split("/")) {
|
|
51
|
+
if (seg === "..")
|
|
52
|
+
return `path must not contain ".."`;
|
|
53
|
+
if (!PATH_SEGMENT_PATTERN.test(seg)) {
|
|
54
|
+
return `invalid path segment "${seg}"; allowed: letters, digits, ".", "_", "-"`;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* The GitHub Pages URL for owner/repo, optionally under a subpath. A repository
|
|
61
|
+
* named exactly `<owner>.github.io` is the user/org site and serves at the apex;
|
|
62
|
+
* every other repo serves under its name as a path (which is why packed bundles
|
|
63
|
+
* use a relative base so they work from a subpath). A non-empty `path` appends a
|
|
64
|
+
* further subdirectory so many tours can share one repo.
|
|
65
|
+
*/
|
|
66
|
+
export function pagesUrl(owner, repo, path) {
|
|
67
|
+
const host = `${owner.toLowerCase()}.github.io`;
|
|
68
|
+
const base = repo.toLowerCase() === host ? `https://${host}/` : `https://${host}/${repo}/`;
|
|
69
|
+
const clean = normalizePath(path);
|
|
70
|
+
return clean ? `${base}${clean}/` : base;
|
|
71
|
+
}
|
|
72
|
+
/** The repository's web URL. */
|
|
73
|
+
export function repoUrl(owner, repo) {
|
|
74
|
+
return `https://github.com/${owner}/${repo}`;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Drive the publish: verify tooling, resolve the owner, ensure the repo exists,
|
|
78
|
+
* push the bundle to gh-pages, and enable Pages. Ordering is load-bearing — the
|
|
79
|
+
* repo must exist before the push, and the gh-pages branch must exist (from the
|
|
80
|
+
* push) before Pages can be pointed at it.
|
|
81
|
+
*/
|
|
82
|
+
export async function runPublishFlow(deps, opts) {
|
|
83
|
+
await deps.ensureToolsAvailable();
|
|
84
|
+
const owner = await deps.resolveOwner(opts.org);
|
|
85
|
+
const exists = await deps.repoExists(owner, opts.repo);
|
|
86
|
+
if (!exists) {
|
|
87
|
+
deps.log(`Creating ${opts.private ? "private" : "public"} repo ${owner}/${opts.repo}...`);
|
|
88
|
+
await deps.createRepo(owner, opts.repo, {
|
|
89
|
+
private: opts.private,
|
|
90
|
+
description: "Scenar embed — published with `scenar publish`.",
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
deps.log(`Reusing existing repo ${owner}/${opts.repo}...`);
|
|
95
|
+
}
|
|
96
|
+
deps.log(opts.path
|
|
97
|
+
? `Publishing into ${opts.repo}/${opts.path}/ on the gh-pages branch...`
|
|
98
|
+
: "Pushing bundle to the gh-pages branch...");
|
|
99
|
+
await deps.pushBundleToPages({
|
|
100
|
+
bundleDir: opts.bundleDir,
|
|
101
|
+
owner,
|
|
102
|
+
repo: opts.repo,
|
|
103
|
+
path: opts.path,
|
|
104
|
+
message: opts.message,
|
|
105
|
+
});
|
|
106
|
+
deps.log("Enabling GitHub Pages...");
|
|
107
|
+
await deps.enablePages(owner, opts.repo);
|
|
108
|
+
return {
|
|
109
|
+
owner,
|
|
110
|
+
repo: opts.repo,
|
|
111
|
+
path: opts.path,
|
|
112
|
+
repoUrl: repoUrl(owner, opts.repo),
|
|
113
|
+
pagesUrl: pagesUrl(owner, opts.repo, opts.path),
|
|
114
|
+
created: !exists,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=publish-flow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publish-flow.js","sourceRoot":"","sources":["../../../src/publish/publish-flow.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Pages publishing — the OSS "permanent public URL" path of Phase 1.
|
|
3
|
+
*
|
|
4
|
+
* The flow is dependency-injected (mirroring deploy-flow.ts) so its ordering and
|
|
5
|
+
* payloads are unit-testable against fakes, with zero live GitHub calls in the
|
|
6
|
+
* test suite. The real `gh`/`git` implementations live in ./github.ts.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** A repo name's allowed characters (GitHub: alphanumerics, ., _, -). */
|
|
10
|
+
const REPO_NAME_PATTERN = /^[A-Za-z0-9._-]+$/;
|
|
11
|
+
|
|
12
|
+
/** GitHub caps repository names at 100 characters. */
|
|
13
|
+
const MAX_REPO_NAME_LENGTH = 100;
|
|
14
|
+
|
|
15
|
+
/** Allowed characters for a single publish path segment. */
|
|
16
|
+
const PATH_SEGMENT_PATTERN = /^[A-Za-z0-9._-]+$/;
|
|
17
|
+
|
|
18
|
+
/** Identity for the target GitHub repository + how to write to it. */
|
|
19
|
+
export interface PublishOptions {
|
|
20
|
+
/** The packed bundle directory to publish. */
|
|
21
|
+
readonly bundleDir: string;
|
|
22
|
+
/** Target repository name (default: the scenario slug). */
|
|
23
|
+
readonly repo: string;
|
|
24
|
+
/**
|
|
25
|
+
* Subdirectory within the repo to publish into, normalized (no leading/
|
|
26
|
+
* trailing slashes). Empty string means the repo root — a dedicated,
|
|
27
|
+
* single-tour repo whose every publish replaces the whole branch. A non-empty
|
|
28
|
+
* path (e.g. `welcome-tour`) lets many tours coexist in one repo; publishing
|
|
29
|
+
* replaces only that subfolder and preserves its siblings.
|
|
30
|
+
*/
|
|
31
|
+
readonly path: string;
|
|
32
|
+
/** Target owner (a GitHub org); omitted means the authenticated user. */
|
|
33
|
+
readonly org?: string;
|
|
34
|
+
/** Create the repo private rather than public (Pages needs Pro for private). */
|
|
35
|
+
readonly private: boolean;
|
|
36
|
+
/** Commit message for the published snapshot. */
|
|
37
|
+
readonly message: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** The published location. */
|
|
41
|
+
export interface PublishResult {
|
|
42
|
+
readonly owner: string;
|
|
43
|
+
readonly repo: string;
|
|
44
|
+
/** The subdirectory the embed was published into ("" for the repo root). */
|
|
45
|
+
readonly path: string;
|
|
46
|
+
/** The repository's web URL. */
|
|
47
|
+
readonly repoUrl: string;
|
|
48
|
+
/** The GitHub Pages URL the embed is served at (includes the path). */
|
|
49
|
+
readonly pagesUrl: string;
|
|
50
|
+
/** True when the repo was created by this run (vs. reused). */
|
|
51
|
+
readonly created: boolean;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The side-effecting operations the flow depends on, injected so the ordering
|
|
56
|
+
* can be tested without `gh`, `git`, or the network.
|
|
57
|
+
*/
|
|
58
|
+
export interface PublishFlowDeps {
|
|
59
|
+
/** Verify `gh` + `git` are installed and `gh` is authenticated; else throw. */
|
|
60
|
+
ensureToolsAvailable(): Promise<void>;
|
|
61
|
+
/** Resolve the target owner: the explicit org, or the authenticated user. */
|
|
62
|
+
resolveOwner(explicitOrg: string | undefined): Promise<string>;
|
|
63
|
+
/** Whether owner/repo already exists. */
|
|
64
|
+
repoExists(owner: string, repo: string): Promise<boolean>;
|
|
65
|
+
/** Create owner/repo (public unless private requested). */
|
|
66
|
+
createRepo(owner: string, repo: string, opts: { private: boolean; description: string }): Promise<void>;
|
|
67
|
+
/**
|
|
68
|
+
* Publish the bundle (plus a root .nojekyll) to the repo's gh-pages branch.
|
|
69
|
+
* An empty `path` replaces the whole branch (dedicated repo); a non-empty
|
|
70
|
+
* `path` updates only that subfolder, preserving sibling tours already on the
|
|
71
|
+
* branch.
|
|
72
|
+
*/
|
|
73
|
+
pushBundleToPages(input: {
|
|
74
|
+
bundleDir: string;
|
|
75
|
+
owner: string;
|
|
76
|
+
repo: string;
|
|
77
|
+
path: string;
|
|
78
|
+
message: string;
|
|
79
|
+
}): Promise<void>;
|
|
80
|
+
/** Enable GitHub Pages from gh-pages (idempotent — a no-op if already on). */
|
|
81
|
+
enablePages(owner: string, repo: string): Promise<void>;
|
|
82
|
+
/** Progress sink (stderr in the command; captured in tests). */
|
|
83
|
+
log(message: string): void;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Validate a GitHub repository name. Returns an error message, or null if valid.
|
|
88
|
+
* The scenario slug (the default) is always valid; this guards an explicit
|
|
89
|
+
* `--repo` before any repo is touched.
|
|
90
|
+
*/
|
|
91
|
+
export function validateRepoName(repo: string): string | null {
|
|
92
|
+
if (repo.length === 0 || repo.length > MAX_REPO_NAME_LENGTH) {
|
|
93
|
+
return `repo name must be 1-${MAX_REPO_NAME_LENGTH} characters`;
|
|
94
|
+
}
|
|
95
|
+
if (repo === "." || repo === "..") {
|
|
96
|
+
return `repo name must not be "." or ".."`;
|
|
97
|
+
}
|
|
98
|
+
if (!REPO_NAME_PATTERN.test(repo)) {
|
|
99
|
+
return `invalid repo name "${repo}"; allowed: letters, digits, ".", "_", "-"`;
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Normalize a publish path to a clean, slash-separated subpath with no leading,
|
|
106
|
+
* trailing, or doubled slashes. `/`, `.`, and empty all normalize to "" (root).
|
|
107
|
+
*/
|
|
108
|
+
export function normalizePath(path: string | undefined): string {
|
|
109
|
+
return (path ?? "")
|
|
110
|
+
.split("/")
|
|
111
|
+
.map((seg) => seg.trim())
|
|
112
|
+
.filter((seg) => seg.length > 0 && seg !== ".")
|
|
113
|
+
.join("/");
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Validate a (pre-normalized) publish subpath. Returns an error message, or null
|
|
118
|
+
* if valid. "" (root) is always valid; each segment must be a safe slug and
|
|
119
|
+
* never `..` (no directory traversal into the branch).
|
|
120
|
+
*/
|
|
121
|
+
export function validatePublishPath(path: string): string | null {
|
|
122
|
+
if (path.length === 0) return null;
|
|
123
|
+
for (const seg of path.split("/")) {
|
|
124
|
+
if (seg === "..") return `path must not contain ".."`;
|
|
125
|
+
if (!PATH_SEGMENT_PATTERN.test(seg)) {
|
|
126
|
+
return `invalid path segment "${seg}"; allowed: letters, digits, ".", "_", "-"`;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* The GitHub Pages URL for owner/repo, optionally under a subpath. A repository
|
|
134
|
+
* named exactly `<owner>.github.io` is the user/org site and serves at the apex;
|
|
135
|
+
* every other repo serves under its name as a path (which is why packed bundles
|
|
136
|
+
* use a relative base so they work from a subpath). A non-empty `path` appends a
|
|
137
|
+
* further subdirectory so many tours can share one repo.
|
|
138
|
+
*/
|
|
139
|
+
export function pagesUrl(owner: string, repo: string, path?: string): string {
|
|
140
|
+
const host = `${owner.toLowerCase()}.github.io`;
|
|
141
|
+
const base = repo.toLowerCase() === host ? `https://${host}/` : `https://${host}/${repo}/`;
|
|
142
|
+
const clean = normalizePath(path);
|
|
143
|
+
return clean ? `${base}${clean}/` : base;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** The repository's web URL. */
|
|
147
|
+
export function repoUrl(owner: string, repo: string): string {
|
|
148
|
+
return `https://github.com/${owner}/${repo}`;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Drive the publish: verify tooling, resolve the owner, ensure the repo exists,
|
|
153
|
+
* push the bundle to gh-pages, and enable Pages. Ordering is load-bearing — the
|
|
154
|
+
* repo must exist before the push, and the gh-pages branch must exist (from the
|
|
155
|
+
* push) before Pages can be pointed at it.
|
|
156
|
+
*/
|
|
157
|
+
export async function runPublishFlow(
|
|
158
|
+
deps: PublishFlowDeps,
|
|
159
|
+
opts: PublishOptions,
|
|
160
|
+
): Promise<PublishResult> {
|
|
161
|
+
await deps.ensureToolsAvailable();
|
|
162
|
+
|
|
163
|
+
const owner = await deps.resolveOwner(opts.org);
|
|
164
|
+
const exists = await deps.repoExists(owner, opts.repo);
|
|
165
|
+
if (!exists) {
|
|
166
|
+
deps.log(`Creating ${opts.private ? "private" : "public"} repo ${owner}/${opts.repo}...`);
|
|
167
|
+
await deps.createRepo(owner, opts.repo, {
|
|
168
|
+
private: opts.private,
|
|
169
|
+
description: "Scenar embed — published with `scenar publish`.",
|
|
170
|
+
});
|
|
171
|
+
} else {
|
|
172
|
+
deps.log(`Reusing existing repo ${owner}/${opts.repo}...`);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
deps.log(
|
|
176
|
+
opts.path
|
|
177
|
+
? `Publishing into ${opts.repo}/${opts.path}/ on the gh-pages branch...`
|
|
178
|
+
: "Pushing bundle to the gh-pages branch...",
|
|
179
|
+
);
|
|
180
|
+
await deps.pushBundleToPages({
|
|
181
|
+
bundleDir: opts.bundleDir,
|
|
182
|
+
owner,
|
|
183
|
+
repo: opts.repo,
|
|
184
|
+
path: opts.path,
|
|
185
|
+
message: opts.message,
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
deps.log("Enabling GitHub Pages...");
|
|
189
|
+
await deps.enablePages(owner, opts.repo);
|
|
190
|
+
|
|
191
|
+
return {
|
|
192
|
+
owner,
|
|
193
|
+
repo: opts.repo,
|
|
194
|
+
path: opts.path,
|
|
195
|
+
repoUrl: repoUrl(owner, opts.repo),
|
|
196
|
+
pagesUrl: pagesUrl(owner, opts.repo, opts.path),
|
|
197
|
+
created: !exists,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Viewport } from "../pack/viewport.js";
|
|
2
|
+
import { type PublishFlowDeps, type PublishResult } from "./publish-flow.js";
|
|
3
|
+
/** Options for {@link runPublish}. */
|
|
4
|
+
export interface RunPublishOptions {
|
|
5
|
+
readonly bundleDir: string;
|
|
6
|
+
/** Target repository name (default: {@link DEFAULT_REPO}). */
|
|
7
|
+
readonly repo?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Subdirectory within the repo (default: the packed scenario slug). Pass "/"
|
|
10
|
+
* or "" to publish at the repo root (a dedicated single-tour repo).
|
|
11
|
+
*/
|
|
12
|
+
readonly path?: string;
|
|
13
|
+
/** Target GitHub org (default: the authenticated user). */
|
|
14
|
+
readonly org?: string;
|
|
15
|
+
/** Create the repo private (GitHub Pages needs Pro for private repos). */
|
|
16
|
+
readonly private?: boolean;
|
|
17
|
+
/** Commit message for the published snapshot. */
|
|
18
|
+
readonly message?: string;
|
|
19
|
+
/** Progress sink for flow messages. */
|
|
20
|
+
readonly onLog?: (message: string) => void;
|
|
21
|
+
/**
|
|
22
|
+
* Override the publish backend (the gh/git deps). Defaults to the real
|
|
23
|
+
* {@link createGithubPublisher}; tests inject a fake to avoid live calls.
|
|
24
|
+
*/
|
|
25
|
+
readonly deps?: Omit<PublishFlowDeps, "log">;
|
|
26
|
+
}
|
|
27
|
+
/** The published location plus the derived embed snippet. */
|
|
28
|
+
export interface PublishRunResult {
|
|
29
|
+
readonly result: PublishResult;
|
|
30
|
+
readonly viewport: Viewport;
|
|
31
|
+
readonly recordedViewport: boolean;
|
|
32
|
+
readonly snippet: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Validate a packed bundle and publish it to GitHub Pages — the orchestration
|
|
36
|
+
* behind `scenar publish`, with no process/exit coupling. Resolves the default
|
|
37
|
+
* repo name, runs the publish flow, and builds the embed snippet. Throws on an
|
|
38
|
+
* invalid bundle, an invalid repo name, or any flow failure.
|
|
39
|
+
*/
|
|
40
|
+
export declare function runPublish(options: RunPublishOptions): Promise<PublishRunResult>;
|
|
41
|
+
//# sourceMappingURL=run-publish.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-publish.d.ts","sourceRoot":"","sources":["../../../src/publish/run-publish.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,EAKL,KAAK,eAAe,EACpB,KAAK,aAAa,EACnB,MAAM,mBAAmB,CAAC;AAc3B,sCAAsC;AACtC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,8DAA8D;IAC9D,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,2DAA2D;IAC3D,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,0EAA0E;IAC1E,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,iDAAiD;IACjD,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,uCAAuC;IACvC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C;;;OAGG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,eAAe,EAAE,KAAK,CAAC,CAAC;CAC9C;AAED,6DAA6D;AAC7D,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA+CtF"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { resolve, join, basename } from "node:path";
|
|
2
|
+
import { stat, readFile } from "node:fs/promises";
|
|
3
|
+
import { PACK_MANIFEST_FILE } from "../pack/pack-manifest.js";
|
|
4
|
+
import { buildEmbedSnippet } from "../deploy/embed-snippet.js";
|
|
5
|
+
import { readBundleViewport } from "../bundle/read-viewport.js";
|
|
6
|
+
import { createGithubPublisher } from "./github.js";
|
|
7
|
+
import { runPublishFlow, validateRepoName, validatePublishPath, normalizePath, } from "./publish-flow.js";
|
|
8
|
+
/** The servable entry every packed bundle contains at its root. */
|
|
9
|
+
const INDEX_FILE = "index.html";
|
|
10
|
+
/** Repo-name rule for the default (same shape as scenario slugs). */
|
|
11
|
+
const REPO_FALLBACK_PATTERN = /^[a-z][a-z0-9-]*[a-z0-9]$/;
|
|
12
|
+
/**
|
|
13
|
+
* Default shared repo for embeds. Many tours live here, each under its own slug
|
|
14
|
+
* path, so previews never land in the user's source repo. Override with --repo.
|
|
15
|
+
*/
|
|
16
|
+
const DEFAULT_REPO = "scenar-embeds";
|
|
17
|
+
/**
|
|
18
|
+
* Validate a packed bundle and publish it to GitHub Pages — the orchestration
|
|
19
|
+
* behind `scenar publish`, with no process/exit coupling. Resolves the default
|
|
20
|
+
* repo name, runs the publish flow, and builds the embed snippet. Throws on an
|
|
21
|
+
* invalid bundle, an invalid repo name, or any flow failure.
|
|
22
|
+
*/
|
|
23
|
+
export async function runPublish(options) {
|
|
24
|
+
const onLog = options.onLog ?? (() => { });
|
|
25
|
+
const resolvedDir = resolve(options.bundleDir);
|
|
26
|
+
const info = await stat(resolvedDir).catch(() => null);
|
|
27
|
+
if (!info || !info.isDirectory()) {
|
|
28
|
+
throw new Error(`${options.bundleDir} is not a directory. Pass a bundle produced by \`scenar pack\`.`);
|
|
29
|
+
}
|
|
30
|
+
const indexInfo = await stat(join(resolvedDir, INDEX_FILE)).catch(() => null);
|
|
31
|
+
if (!indexInfo || !indexInfo.isFile()) {
|
|
32
|
+
throw new Error(`no ${INDEX_FILE} in ${options.bundleDir}. Run \`scenar pack\` to produce a servable bundle first.`);
|
|
33
|
+
}
|
|
34
|
+
const slug = await scenarioSlug(resolvedDir);
|
|
35
|
+
const repo = options.repo ?? DEFAULT_REPO;
|
|
36
|
+
const repoError = validateRepoName(repo);
|
|
37
|
+
if (repoError) {
|
|
38
|
+
throw new Error(`${repoError}. Pass --repo <name> to override.`);
|
|
39
|
+
}
|
|
40
|
+
// Path defaults to the scenario slug so tours coexist in one repo; an explicit
|
|
41
|
+
// "/" (or "") publishes at the repo root.
|
|
42
|
+
const path = normalizePath(options.path ?? slug);
|
|
43
|
+
const pathError = validatePublishPath(path);
|
|
44
|
+
if (pathError) {
|
|
45
|
+
throw new Error(`${pathError}. Pass --path <subpath> to override.`);
|
|
46
|
+
}
|
|
47
|
+
const deps = { ...(options.deps ?? createGithubPublisher()), log: onLog };
|
|
48
|
+
const result = await runPublishFlow(deps, {
|
|
49
|
+
bundleDir: resolvedDir,
|
|
50
|
+
repo,
|
|
51
|
+
path,
|
|
52
|
+
org: options.org,
|
|
53
|
+
private: Boolean(options.private),
|
|
54
|
+
message: options.message ?? "Publish Scenar embed",
|
|
55
|
+
});
|
|
56
|
+
const { viewport, recorded } = await readBundleViewport(resolvedDir);
|
|
57
|
+
const snippet = buildEmbedSnippet({ embedUrl: result.pagesUrl, viewport });
|
|
58
|
+
return { result, viewport, recordedViewport: recorded, snippet };
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* The packed scenario slug (from pack-manifest.json), falling back to the bundle
|
|
62
|
+
* directory's base name. Used as the default publish subpath. The downstream
|
|
63
|
+
* validatePublishPath guards the result.
|
|
64
|
+
*/
|
|
65
|
+
async function scenarioSlug(bundleDir) {
|
|
66
|
+
try {
|
|
67
|
+
const raw = await readFile(join(bundleDir, PACK_MANIFEST_FILE), "utf-8");
|
|
68
|
+
const id = JSON.parse(raw).scenarioId;
|
|
69
|
+
if (typeof id === "string" && REPO_FALLBACK_PATTERN.test(id))
|
|
70
|
+
return id;
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// No/invalid manifest — fall back to the directory name.
|
|
74
|
+
}
|
|
75
|
+
return basename(bundleDir);
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=run-publish.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-publish.js","sourceRoot":"","sources":["../../../src/publish/run-publish.ts"],"names":[],"mappings":"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"}
|