costaff-workspace 0.1.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/LICENSE +21 -0
- package/README.md +91 -0
- package/dist/bundle.js +83 -0
- package/dist/bundle.js.map +1 -0
- package/dist/cli-pull.js +60 -0
- package/dist/cli-pull.js.map +1 -0
- package/dist/cli.js +173 -0
- package/dist/cli.js.map +1 -0
- package/dist/client.js +123 -0
- package/dist/client.js.map +1 -0
- package/dist/credentials.js +56 -0
- package/dist/credentials.js.map +1 -0
- package/dist/defaults.js +81 -0
- package/dist/defaults.js.map +1 -0
- package/dist/entries.js +42 -0
- package/dist/entries.js.map +1 -0
- package/dist/integration.js +229 -0
- package/dist/integration.js.map +1 -0
- package/dist/main.js +53 -0
- package/dist/main.js.map +1 -0
- package/dist/project.js +117 -0
- package/dist/project.js.map +1 -0
- package/dist/protocol.js +28 -0
- package/dist/protocol.js.map +1 -0
- package/dist/pull.js +64 -0
- package/dist/pull.js.map +1 -0
- package/dist/push.js +152 -0
- package/dist/push.js.map +1 -0
- package/dist/source.js +107 -0
- package/dist/source.js.map +1 -0
- package/dist/stage.js +50 -0
- package/dist/stage.js.map +1 -0
- package/dist/state.js +101 -0
- package/dist/state.js.map +1 -0
- package/package.json +51 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Simon Liu
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# costaff-workspace
|
|
2
|
+
|
|
3
|
+
Publish documents, decks and workbooks to [CoStaff Workspace](https://workspace.costaffs.app),
|
|
4
|
+
and pull their source back out to keep working on them.
|
|
5
|
+
|
|
6
|
+
Built for projects made with [open-doc](https://github.com/simonliu-ai-product/open-doc),
|
|
7
|
+
open-slide and open-sheet.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm i -g costaff-workspace
|
|
11
|
+
|
|
12
|
+
cd my-doc
|
|
13
|
+
costaff-workspace push
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
That is the whole command. In a project folder it works the rest out: the folder
|
|
17
|
+
name is the slug, `package.json` says whether this is a document, a deck or a
|
|
18
|
+
workbook, `dist/` is the built bundle, and the source travels with it so the file
|
|
19
|
+
can be pulled back and edited. Every guess is printed, and a guess it cannot make
|
|
20
|
+
stops the push rather than inventing something.
|
|
21
|
+
|
|
22
|
+
The first push shows a device code to authorise in a browser. That machine stays
|
|
23
|
+
signed in afterwards.
|
|
24
|
+
|
|
25
|
+
## Commands
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
costaff-workspace push # push this folder
|
|
29
|
+
costaff-workspace pull <token> [dir] # fetch a published file's source
|
|
30
|
+
costaff-workspace login # sign this machine in
|
|
31
|
+
costaff-workspace logout # forget this machine's sign-in
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Useful flags on `push`:
|
|
35
|
+
|
|
36
|
+
| Flag | |
|
|
37
|
+
| --- | --- |
|
|
38
|
+
| `--slug`, `--kind`, `--title` | override a guess |
|
|
39
|
+
| `--site-dir <dir>` | the built bundle (default `dist`) |
|
|
40
|
+
| `--no-source` | publish the bundle alone — nothing can be pulled back out of it |
|
|
41
|
+
| `--dry-run` | package and report, without uploading or needing a network |
|
|
42
|
+
| `--endpoint <url>` | a receiver you run yourself |
|
|
43
|
+
|
|
44
|
+
## A project is a site of many documents
|
|
45
|
+
|
|
46
|
+
An open-doc project is not one file — it is a site holding several documents
|
|
47
|
+
under `docs/`, and one `open-doc build` puts all of their chunks in a single
|
|
48
|
+
`assets/` directory. So `push` builds each document into a bundle of its own and
|
|
49
|
+
pushes them one at a time.
|
|
50
|
+
|
|
51
|
+
That is not tidiness. A share can only withhold the rest of a workspace if the
|
|
52
|
+
rest of the workspace is not in the bundle: inside a shared build, anyone holding
|
|
53
|
+
a link to one document can fetch the chunks of the others. The cost is real — N
|
|
54
|
+
builds instead of one, and assets repeated across them.
|
|
55
|
+
|
|
56
|
+
## The protocol is public
|
|
57
|
+
|
|
58
|
+
`src/protocol.ts` is a specification, not an implementation detail. Anyone can
|
|
59
|
+
run their own receiver and point `--endpoint` at it; `COSTAFF_WORKSPACE_ENDPOINT`
|
|
60
|
+
does the same thing for a whole shell.
|
|
61
|
+
|
|
62
|
+
Adding a required field is a breaking change for every third-party receiver, so
|
|
63
|
+
optional is the default. `source` is optional: a push without it is still valid,
|
|
64
|
+
and a receiver that ignores it still serves. `pull` is optional in the discovery
|
|
65
|
+
document — a receiver that cannot hand source back omits the field rather than
|
|
66
|
+
answering 404 to a command the CLI thought it had.
|
|
67
|
+
|
|
68
|
+
## What travels with the source
|
|
69
|
+
|
|
70
|
+
`node_modules` and dotfiles are not collected. `.env` is inside that.
|
|
71
|
+
|
|
72
|
+
Local dependency specs (`link:`, `file:`, `workspace:`, `portal:`) are replaced
|
|
73
|
+
with the version actually installed, because a path on the author's disk exists
|
|
74
|
+
on no other machine. A version that cannot be resolved refuses the push — a
|
|
75
|
+
published path nobody can resolve buys an install failure that talks about a
|
|
76
|
+
missing directory instead of a broken dependency.
|
|
77
|
+
|
|
78
|
+
## What comes back depends on who you are
|
|
79
|
+
|
|
80
|
+
The server decides, not the client.
|
|
81
|
+
|
|
82
|
+
| | You get | Pushing it back |
|
|
83
|
+
| --- | --- | --- |
|
|
84
|
+
| Owner | includes the token | updates the same link |
|
|
85
|
+
| Shared with you | no token | publishes at a new link of your own; the original is untouched |
|
|
86
|
+
|
|
87
|
+
Two clients each deciding who owns a file would eventually disagree.
|
|
88
|
+
|
|
89
|
+
## Licence
|
|
90
|
+
|
|
91
|
+
MIT
|
package/dist/bundle.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { PUSH_PROTOCOL } from "./protocol.js";
|
|
4
|
+
async function collect(dir, rel = '') {
|
|
5
|
+
const out = new Map();
|
|
6
|
+
for (const entry of await fs.readdir(dir, { withFileTypes: true })) {
|
|
7
|
+
const abs = path.join(dir, entry.name);
|
|
8
|
+
const key = rel ? `${rel}/${entry.name}` : entry.name;
|
|
9
|
+
if (entry.isDirectory()) {
|
|
10
|
+
for (const [k, v] of await collect(abs, key))
|
|
11
|
+
out.set(k, v);
|
|
12
|
+
}
|
|
13
|
+
else if (entry.isFile()) {
|
|
14
|
+
out.set(key, new Uint8Array(await fs.readFile(abs)));
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return out;
|
|
18
|
+
}
|
|
19
|
+
function nest(files) {
|
|
20
|
+
const tree = {};
|
|
21
|
+
for (const [key, bytes] of files) {
|
|
22
|
+
const parts = key.split('/');
|
|
23
|
+
let node = tree;
|
|
24
|
+
for (const part of parts.slice(0, -1)) {
|
|
25
|
+
const next = node[part];
|
|
26
|
+
if (next === undefined || next instanceof Uint8Array) {
|
|
27
|
+
const created = {};
|
|
28
|
+
node[part] = created;
|
|
29
|
+
node = created;
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
node = next;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
node[parts[parts.length - 1]] = bytes;
|
|
36
|
+
}
|
|
37
|
+
return tree;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Zips one file's built site plus its manifest.
|
|
41
|
+
*
|
|
42
|
+
* The manifest travels inside the zip rather than in a header so a receiver
|
|
43
|
+
* never has to parse metadata out of a size-capped header, and so the archive
|
|
44
|
+
* is self-describing once it lands in storage.
|
|
45
|
+
*
|
|
46
|
+
* Nothing here inspects the site: what the manifest says is what the caller
|
|
47
|
+
* stated. A publisher that guessed would sooner or later name something the
|
|
48
|
+
* recipient was never given.
|
|
49
|
+
*/
|
|
50
|
+
export async function createBundle(input) {
|
|
51
|
+
const stat = await fs.stat(input.siteDir).catch(() => null);
|
|
52
|
+
if (stat === null || !stat.isDirectory()) {
|
|
53
|
+
throw new Error(`No site at ${input.siteDir} — build it first.`);
|
|
54
|
+
}
|
|
55
|
+
const files = await collect(input.siteDir);
|
|
56
|
+
const entry = input.manifest.entry;
|
|
57
|
+
if (!files.has(entry)) {
|
|
58
|
+
throw new Error(`${input.siteDir} has no ${entry} — it is not a publishable site.`);
|
|
59
|
+
}
|
|
60
|
+
const manifest = {
|
|
61
|
+
...input.manifest,
|
|
62
|
+
protocol: PUSH_PROTOCOL,
|
|
63
|
+
pushedAt: input.pushedAt,
|
|
64
|
+
...(input.source === undefined
|
|
65
|
+
? {}
|
|
66
|
+
: {
|
|
67
|
+
source: {
|
|
68
|
+
dir: input.source.dir,
|
|
69
|
+
entry: input.source.entry,
|
|
70
|
+
dependencies: input.source.dependencies,
|
|
71
|
+
devDependencies: input.source.devDependencies,
|
|
72
|
+
},
|
|
73
|
+
}),
|
|
74
|
+
};
|
|
75
|
+
const { zipSync } = await import('fflate');
|
|
76
|
+
const tree = {
|
|
77
|
+
'manifest.json': new TextEncoder().encode(`${JSON.stringify(manifest, null, 2)}\n`),
|
|
78
|
+
site: nest(files),
|
|
79
|
+
...(input.source === undefined ? {} : { source: nest(input.source.files) }),
|
|
80
|
+
};
|
|
81
|
+
return { bytes: zipSync(tree), manifest };
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=bundle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundle.js","sourceRoot":"","sources":["../src/bundle.ts"],"names":[],"mappings":"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"}
|
package/dist/cli-pull.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { PublishError, discover } from "./client.js";
|
|
2
|
+
import { DEFAULT_ENDPOINT } from "./defaults.js";
|
|
3
|
+
import { pull } from "./pull.js";
|
|
4
|
+
import { login } from "./push.js";
|
|
5
|
+
const USAGE = `costaff-workspace pull — fetch a published file's source back out
|
|
6
|
+
|
|
7
|
+
costaff-workspace pull <token> [dir] [options]
|
|
8
|
+
|
|
9
|
+
--endpoint <url> receiver (default ${DEFAULT_ENDPOINT},
|
|
10
|
+
or COSTAFF_WORKSPACE_ENDPOINT)
|
|
11
|
+
--force write into a directory that is not empty
|
|
12
|
+
--bearer <token> auth token instead of a stored login
|
|
13
|
+
|
|
14
|
+
If you own the file, what comes back can be pushed straight back to the
|
|
15
|
+
same link. If it was shared with you, it comes back as a copy: pushing it
|
|
16
|
+
publishes at a new link under your own workspace.
|
|
17
|
+
`;
|
|
18
|
+
export async function runPull(argv) {
|
|
19
|
+
if (argv.length === 0 || argv.includes('--help') || argv.includes('-h')) {
|
|
20
|
+
process.stdout.write(USAGE);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const flags = {};
|
|
24
|
+
const positional = [];
|
|
25
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
26
|
+
const arg = argv[i];
|
|
27
|
+
if (!arg.startsWith('--')) {
|
|
28
|
+
positional.push(arg);
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
const next = argv[i + 1];
|
|
32
|
+
if (next !== undefined && !next.startsWith('--')) {
|
|
33
|
+
flags[arg.slice(2)] = next;
|
|
34
|
+
i += 1;
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
flags[arg.slice(2)] = true;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const str = (k) => typeof flags[k] === 'string' ? flags[k] : undefined;
|
|
41
|
+
const endpoint = str('endpoint') ?? process.env.COSTAFF_WORKSPACE_ENDPOINT ?? DEFAULT_ENDPOINT;
|
|
42
|
+
if (endpoint === '')
|
|
43
|
+
throw new PublishError('missing --endpoint');
|
|
44
|
+
const token = positional[0];
|
|
45
|
+
if (token === undefined)
|
|
46
|
+
throw new PublishError('missing <token>');
|
|
47
|
+
const out = (line) => {
|
|
48
|
+
process.stdout.write(line);
|
|
49
|
+
};
|
|
50
|
+
await pull({
|
|
51
|
+
endpoint,
|
|
52
|
+
token,
|
|
53
|
+
dir: positional[1],
|
|
54
|
+
force: flags.force === true,
|
|
55
|
+
bearer: str('bearer'),
|
|
56
|
+
out,
|
|
57
|
+
signIn: async () => login({ endpoint, out }, (await discover(endpoint)).name),
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=cli-pull.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-pull.js","sourceRoot":"","sources":["../src/cli-pull.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAElC,MAAM,KAAK,GAAG;;;;2CAI6B,gBAAgB;;;;;;;;CAQ1D,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,IAAc;IAC1C,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACxE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC5B,OAAO;IACT,CAAC;IAED,MAAM,KAAK,GAAqC,EAAE,CAAC;IACnD,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACxC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1B,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACrB,SAAS;QACX,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACzB,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACjD,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;YAC3B,CAAC,IAAI,CAAC,CAAC;QACT,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;QAC7B,CAAC;IACH,CAAC;IACD,MAAM,GAAG,GAAG,CAAC,CAAS,EAAsB,EAAE,CAC5C,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAE,KAAK,CAAC,CAAC,CAAY,CAAC,CAAC,CAAC,SAAS,CAAC;IAElE,MAAM,QAAQ,GAAG,GAAG,CAAC,UAAU,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,0BAA0B,IAAI,gBAAgB,CAAC;IAC/F,IAAI,QAAQ,KAAK,EAAE;QAAE,MAAM,IAAI,YAAY,CAAC,oBAAoB,CAAC,CAAC;IAClE,MAAM,KAAK,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;IAC5B,IAAI,KAAK,KAAK,SAAS;QAAE,MAAM,IAAI,YAAY,CAAC,iBAAiB,CAAC,CAAC;IAEnE,MAAM,GAAG,GAAG,CAAC,IAAY,EAAQ,EAAE;QACjC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC,CAAC;IACF,MAAM,IAAI,CAAC;QACT,QAAQ;QACR,KAAK;QACL,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC;QAClB,KAAK,EAAE,KAAK,CAAC,KAAK,KAAK,IAAI;QAC3B,MAAM,EAAE,GAAG,CAAC,QAAQ,CAAC;QACrB,GAAG;QACH,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC,KAAK,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,EAAE,CAAC,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;KAC9E,CAAC,CAAC;AACL,CAAC"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { PublishError } from "./client.js";
|
|
2
|
+
import { DEFAULT_ENDPOINT, kindFromManifest, readManifest, slugFromDir, titleFromManifest, } from "./defaults.js";
|
|
3
|
+
import { pushProject, realBuildItem, scanProject } from "./project.js";
|
|
4
|
+
import { push } from "./push.js";
|
|
5
|
+
const USAGE = `costaff-workspace push — push one built file into your workspace
|
|
6
|
+
|
|
7
|
+
costaff-workspace push [options]
|
|
8
|
+
|
|
9
|
+
Run it in the project folder with nothing else and it works out the rest:
|
|
10
|
+
the folder name is the slug, package.json says which kind, dist/ is the
|
|
11
|
+
bundle, and the source travels with it so it can be pulled back and edited.
|
|
12
|
+
|
|
13
|
+
--endpoint <url> receiver (default ${DEFAULT_ENDPOINT},
|
|
14
|
+
or COSTAFF_WORKSPACE_ENDPOINT)
|
|
15
|
+
--slug <slug> the file's identity (default: the folder's name)
|
|
16
|
+
--kind <kind> document | workbook | deck (default: read from
|
|
17
|
+
package.json, else document)
|
|
18
|
+
--site-dir <dir> built bundle to upload (default dist)
|
|
19
|
+
--title <text> display name (default: package.json description,
|
|
20
|
+
else the slug)
|
|
21
|
+
--subtitle <text>
|
|
22
|
+
--folder <name> folder in the file manager
|
|
23
|
+
--route <path> route inside the bundle (default /)
|
|
24
|
+
--entry <file> entry file inside the bundle (default index.html)
|
|
25
|
+
--source-dir <dir> the item's source, relative to --source-root
|
|
26
|
+
(default: the whole project)
|
|
27
|
+
--source-root <dir> workspace root the source and its build setup live in
|
|
28
|
+
(default: the current directory)
|
|
29
|
+
--no-source publish the built bundle alone; nothing can be pulled
|
|
30
|
+
back out of it afterwards
|
|
31
|
+
--source-entry <f> file a rebuild starts from (default index.tsx)
|
|
32
|
+
--token <token> override the remembered public address
|
|
33
|
+
--bearer <token> auth token instead of a stored login
|
|
34
|
+
--login sign in again even if a token is stored;
|
|
35
|
+
on its own (with --endpoint) it just signs in
|
|
36
|
+
--logout forget the stored login for this endpoint
|
|
37
|
+
--dry-run package and report, do not upload
|
|
38
|
+
`;
|
|
39
|
+
const KINDS = new Set(['document', 'workbook', 'deck']);
|
|
40
|
+
function parse(argv) {
|
|
41
|
+
const flags = {};
|
|
42
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
43
|
+
const arg = argv[i];
|
|
44
|
+
if (!arg.startsWith('--'))
|
|
45
|
+
continue;
|
|
46
|
+
const name = arg.slice(2);
|
|
47
|
+
const next = argv[i + 1];
|
|
48
|
+
if (next !== undefined && !next.startsWith('--')) {
|
|
49
|
+
flags[name] = next;
|
|
50
|
+
i += 1;
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
flags[name] = true;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const str = (k) => typeof flags[k] === 'string' ? flags[k] : undefined;
|
|
57
|
+
const kind = (str('kind') ?? 'document');
|
|
58
|
+
if (!KINDS.has(kind))
|
|
59
|
+
throw new PublishError(`--kind must be one of ${[...KINDS].join(', ')}`);
|
|
60
|
+
const slug = str('slug') ?? '';
|
|
61
|
+
return {
|
|
62
|
+
help: flags.help === true || flags.h === true,
|
|
63
|
+
/* 猜測要能被說出來,所以記下哪些是使用者自己給的。 */
|
|
64
|
+
given: {
|
|
65
|
+
slug: str('slug') !== undefined,
|
|
66
|
+
kind: str('kind') !== undefined,
|
|
67
|
+
title: str('title') !== undefined,
|
|
68
|
+
},
|
|
69
|
+
noSource: flags['no-source'] === true,
|
|
70
|
+
endpoint: str('endpoint') ?? process.env.COSTAFF_WORKSPACE_ENDPOINT ?? DEFAULT_ENDPOINT,
|
|
71
|
+
slug,
|
|
72
|
+
kind,
|
|
73
|
+
title: str('title') ?? slug,
|
|
74
|
+
subtitle: str('subtitle'),
|
|
75
|
+
folder: str('folder') ?? null,
|
|
76
|
+
route: str('route') ?? '/',
|
|
77
|
+
entry: str('entry'),
|
|
78
|
+
siteDir: str('site-dir') ?? 'dist',
|
|
79
|
+
sourceRoot: str('source-root') ?? process.cwd(),
|
|
80
|
+
sourceDir: str('source-dir'),
|
|
81
|
+
sourceEntry: str('source-entry'),
|
|
82
|
+
token: str('token'),
|
|
83
|
+
bearer: str('bearer'),
|
|
84
|
+
login: flags.login === true,
|
|
85
|
+
logout: flags.logout === true,
|
|
86
|
+
dryRun: flags['dry-run'] === true,
|
|
87
|
+
out: (line) => process.stdout.write(line),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/*
|
|
91
|
+
* 補上沒給的值,並且把每一個猜測印出來。安靜猜錯比要求人打完整指令更糟 —— 推錯
|
|
92
|
+
* 位置的人得看得出來是哪一個猜測害的。
|
|
93
|
+
*/
|
|
94
|
+
async function fill(opts) {
|
|
95
|
+
const root = opts.sourceRoot ?? process.cwd();
|
|
96
|
+
const guessed = [];
|
|
97
|
+
if (!opts.given.slug) {
|
|
98
|
+
const slug = slugFromDir(root);
|
|
99
|
+
if (slug === null) {
|
|
100
|
+
throw new PublishError(`cannot make a slug out of the folder name — pass --slug (lowercase letters, digits and dashes)`);
|
|
101
|
+
}
|
|
102
|
+
opts.slug = slug;
|
|
103
|
+
guessed.push(`--slug ${slug}`);
|
|
104
|
+
}
|
|
105
|
+
const manifest = await readManifest(root);
|
|
106
|
+
if (!opts.given.kind) {
|
|
107
|
+
const kind = kindFromManifest(manifest);
|
|
108
|
+
if (kind !== null) {
|
|
109
|
+
opts.kind = kind;
|
|
110
|
+
guessed.push(`--kind ${kind}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (!opts.given.title) {
|
|
114
|
+
opts.title = titleFromManifest(manifest, opts.slug);
|
|
115
|
+
}
|
|
116
|
+
/*
|
|
117
|
+
* 原始碼預設跟著走:沒有它,推上去的東西之後拉不回來改,而那正是這個服務
|
|
118
|
+
* 存在的理由。--no-source 是明確的退出方式。
|
|
119
|
+
*/
|
|
120
|
+
if (opts.noSource) {
|
|
121
|
+
opts.sourceRoot = undefined;
|
|
122
|
+
opts.sourceDir = undefined;
|
|
123
|
+
}
|
|
124
|
+
else if (opts.sourceDir === undefined) {
|
|
125
|
+
opts.sourceDir = '.';
|
|
126
|
+
guessed.push('--source-dir .');
|
|
127
|
+
}
|
|
128
|
+
if (guessed.length > 0)
|
|
129
|
+
opts.out?.(` guessed ${guessed.join(' ')}\n`);
|
|
130
|
+
}
|
|
131
|
+
export async function runPush(argv) {
|
|
132
|
+
const opts = parse(argv);
|
|
133
|
+
if (opts.help) {
|
|
134
|
+
process.stdout.write(USAGE);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (!opts.logout && opts.endpoint === '') {
|
|
138
|
+
process.stderr.write(`error: missing --endpoint\n\n${USAGE}`);
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
if (opts.logout || opts.login) {
|
|
142
|
+
await push(opts);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
/*
|
|
146
|
+
* 一個 open-doc/open-slide/open-sheet 專案是一個站台含多份文件,不是一份
|
|
147
|
+
* 檔案。每一份各自建置、各自推 —— 共用的 bundle 裡,拿到其中一份連結的人就能
|
|
148
|
+
* 抓到其餘的 chunk,那樣分享設定擋不住任何東西。
|
|
149
|
+
*
|
|
150
|
+
* 明確給了 --slug 的人是在手動推某一份,照他說的做。
|
|
151
|
+
*/
|
|
152
|
+
const root = opts.sourceRoot ?? process.cwd();
|
|
153
|
+
const items = opts.given.slug ? [] : await scanProject(root);
|
|
154
|
+
if (items.length > 0) {
|
|
155
|
+
const by = new Map();
|
|
156
|
+
for (const it of items)
|
|
157
|
+
by.set(it.surface.label, (by.get(it.surface.label) ?? 0) + 1);
|
|
158
|
+
const shape = [...by].map(([label, n]) => `${n} ${label}`).join('、');
|
|
159
|
+
process.stdout.write(` ${shape} —— 各自建置成獨立的 bundle\n`);
|
|
160
|
+
await pushProject({
|
|
161
|
+
root,
|
|
162
|
+
endpoint: opts.endpoint,
|
|
163
|
+
items,
|
|
164
|
+
dryRun: opts.dryRun,
|
|
165
|
+
buildItem: realBuildItem,
|
|
166
|
+
base: { bearer: opts.bearer, folder: opts.folder },
|
|
167
|
+
});
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
await fill(opts);
|
|
171
|
+
await push(opts);
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { DISCOVERY_PATH, isDiscovery, isPushResult, PUSH_PROTOCOL, } from "./protocol.js";
|
|
2
|
+
export class PublishError extends Error {
|
|
3
|
+
status;
|
|
4
|
+
code;
|
|
5
|
+
constructor(message, status,
|
|
6
|
+
/** Machine-readable reason from the receiver; a status alone is ambiguous. */
|
|
7
|
+
code) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.status = status;
|
|
10
|
+
this.code = code;
|
|
11
|
+
this.name = 'PublishError';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function resolveAgainst(endpoint, target) {
|
|
15
|
+
return new URL(target, endpoint.endsWith('/') ? endpoint : `${endpoint}/`).toString();
|
|
16
|
+
}
|
|
17
|
+
async function readJson(res) {
|
|
18
|
+
const text = await res.text();
|
|
19
|
+
if (text.trim() === '')
|
|
20
|
+
return null;
|
|
21
|
+
try {
|
|
22
|
+
return JSON.parse(text);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
throw new PublishError(`${res.url} returned ${res.status} with a non-JSON body.`, res.status);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function errorCode(body) {
|
|
29
|
+
if (typeof body === 'object' && body !== null) {
|
|
30
|
+
const code = body.code;
|
|
31
|
+
if (typeof code === 'string' && code !== '')
|
|
32
|
+
return code;
|
|
33
|
+
}
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
function errorMessage(body, fallback) {
|
|
37
|
+
if (typeof body === 'object' && body !== null) {
|
|
38
|
+
const message = body.message;
|
|
39
|
+
if (typeof message === 'string' && message.trim() !== '')
|
|
40
|
+
return message;
|
|
41
|
+
}
|
|
42
|
+
return fallback;
|
|
43
|
+
}
|
|
44
|
+
export async function discover(endpoint) {
|
|
45
|
+
const url = resolveAgainst(endpoint, `.${DISCOVERY_PATH}`);
|
|
46
|
+
let res;
|
|
47
|
+
try {
|
|
48
|
+
res = await fetch(url, { headers: { accept: 'application/json' } });
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
throw new PublishError(`Cannot reach ${endpoint} — ${err instanceof Error ? err.message : String(err)}`);
|
|
52
|
+
}
|
|
53
|
+
if (!res.ok) {
|
|
54
|
+
throw new PublishError(`${endpoint} is not a CoStaff Workspace endpoint (${res.status}).`);
|
|
55
|
+
}
|
|
56
|
+
const body = await readJson(res);
|
|
57
|
+
if (!isDiscovery(body)) {
|
|
58
|
+
throw new PublishError(`${endpoint} returned a discovery document we cannot read.`);
|
|
59
|
+
}
|
|
60
|
+
if (body.protocol > PUSH_PROTOCOL) {
|
|
61
|
+
throw new PublishError(`${body.name} speaks push protocol ${body.protocol}; this open-doc understands ${PUSH_PROTOCOL}. Upgrade costaff-workspace.`);
|
|
62
|
+
}
|
|
63
|
+
return body;
|
|
64
|
+
}
|
|
65
|
+
export async function startDeviceAuth(endpoint, discovery) {
|
|
66
|
+
const res = await fetch(resolveAgainst(endpoint, discovery.deviceAuthStart), {
|
|
67
|
+
method: 'POST',
|
|
68
|
+
headers: { 'content-type': 'application/json', accept: 'application/json' },
|
|
69
|
+
body: JSON.stringify({ protocol: PUSH_PROTOCOL }),
|
|
70
|
+
});
|
|
71
|
+
const body = await readJson(res);
|
|
72
|
+
if (!res.ok) {
|
|
73
|
+
throw new PublishError(errorMessage(body, `Login could not start (${res.status}).`), res.status);
|
|
74
|
+
}
|
|
75
|
+
return body;
|
|
76
|
+
}
|
|
77
|
+
export async function pollDeviceAuth(endpoint, discovery, deviceCode) {
|
|
78
|
+
const res = await fetch(resolveAgainst(endpoint, discovery.deviceAuthPoll), {
|
|
79
|
+
method: 'POST',
|
|
80
|
+
headers: { 'content-type': 'application/json', accept: 'application/json' },
|
|
81
|
+
body: JSON.stringify({ deviceCode }),
|
|
82
|
+
});
|
|
83
|
+
const body = await readJson(res);
|
|
84
|
+
if (res.status === 428)
|
|
85
|
+
return { status: 'pending' };
|
|
86
|
+
if (!res.ok) {
|
|
87
|
+
throw new PublishError(errorMessage(body, `Login failed (${res.status}).`), res.status);
|
|
88
|
+
}
|
|
89
|
+
return body;
|
|
90
|
+
}
|
|
91
|
+
export async function upload(endpoint, discovery, token, bytes) {
|
|
92
|
+
if (bytes.byteLength > discovery.maxBytes) {
|
|
93
|
+
throw new PublishError(`Bundle is ${formatBytes(bytes.byteLength)}; ${discovery.name} accepts at most ${formatBytes(discovery.maxBytes)}.`);
|
|
94
|
+
}
|
|
95
|
+
const res = await fetch(resolveAgainst(endpoint, discovery.push), {
|
|
96
|
+
method: 'POST',
|
|
97
|
+
headers: {
|
|
98
|
+
authorization: `Bearer ${token}`,
|
|
99
|
+
'content-type': 'application/zip',
|
|
100
|
+
accept: 'application/json',
|
|
101
|
+
},
|
|
102
|
+
body: bytes,
|
|
103
|
+
});
|
|
104
|
+
const body = await readJson(res);
|
|
105
|
+
if (res.status === 401 || res.status === 403) {
|
|
106
|
+
throw new PublishError(errorMessage(body, 'Login expired — run with --login.'), res.status);
|
|
107
|
+
}
|
|
108
|
+
if (!res.ok) {
|
|
109
|
+
throw new PublishError(errorMessage(body, `Publish failed (${res.status}).`), res.status, errorCode(body));
|
|
110
|
+
}
|
|
111
|
+
if (!isPushResult(body)) {
|
|
112
|
+
throw new PublishError(`${discovery.name} accepted the upload but returned no share URL.`);
|
|
113
|
+
}
|
|
114
|
+
return body;
|
|
115
|
+
}
|
|
116
|
+
export function formatBytes(n) {
|
|
117
|
+
if (n < 1024)
|
|
118
|
+
return `${n} B`;
|
|
119
|
+
if (n < 1024 * 1024)
|
|
120
|
+
return `${(n / 1024).toFixed(1)} kB`;
|
|
121
|
+
return `${(n / (1024 * 1024)).toFixed(1)} MB`;
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
export function credentialsPath(env = process.env) {
|
|
5
|
+
const base = env.XDG_CONFIG_HOME?.trim() || path.join(os.homedir(), '.config');
|
|
6
|
+
return path.join(base, 'costaff-workspace', 'credentials.json');
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Tokens are filed per endpoint origin, not per full URL: a receiver that moves
|
|
10
|
+
* its publish path must not orphan a valid login, and two endpoints on one host
|
|
11
|
+
* would share a session anyway.
|
|
12
|
+
*/
|
|
13
|
+
export function credentialKey(endpoint) {
|
|
14
|
+
return new URL(endpoint).origin;
|
|
15
|
+
}
|
|
16
|
+
async function readStore(file) {
|
|
17
|
+
try {
|
|
18
|
+
const parsed = JSON.parse(await fs.readFile(file, 'utf8'));
|
|
19
|
+
if (typeof parsed === 'object' &&
|
|
20
|
+
parsed !== null &&
|
|
21
|
+
typeof parsed.endpoints === 'object') {
|
|
22
|
+
return parsed;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
// A missing or corrupt store is a logged-out state, not a failure.
|
|
27
|
+
}
|
|
28
|
+
return { version: 1, endpoints: {} };
|
|
29
|
+
}
|
|
30
|
+
export function isExpired(cred, now = Date.now()) {
|
|
31
|
+
if (cred.expiresAt === undefined)
|
|
32
|
+
return false;
|
|
33
|
+
const at = Date.parse(cred.expiresAt);
|
|
34
|
+
return Number.isFinite(at) && at <= now;
|
|
35
|
+
}
|
|
36
|
+
export async function loadCredential(endpoint, file = credentialsPath()) {
|
|
37
|
+
const store = await readStore(file);
|
|
38
|
+
const cred = store.endpoints[credentialKey(endpoint)];
|
|
39
|
+
if (cred === undefined || isExpired(cred))
|
|
40
|
+
return null;
|
|
41
|
+
return cred;
|
|
42
|
+
}
|
|
43
|
+
export async function saveCredential(endpoint, cred, file = credentialsPath()) {
|
|
44
|
+
const store = await readStore(file);
|
|
45
|
+
store.endpoints[credentialKey(endpoint)] = cred;
|
|
46
|
+
await fs.mkdir(path.dirname(file), { recursive: true });
|
|
47
|
+
await fs.writeFile(file, `${JSON.stringify(store, null, 2)}\n`, { mode: 0o600 });
|
|
48
|
+
await fs.chmod(file, 0o600).catch(() => { });
|
|
49
|
+
}
|
|
50
|
+
export async function clearCredential(endpoint, file = credentialsPath()) {
|
|
51
|
+
const store = await readStore(file);
|
|
52
|
+
delete store.endpoints[credentialKey(endpoint)];
|
|
53
|
+
await fs.mkdir(path.dirname(file), { recursive: true });
|
|
54
|
+
await fs.writeFile(file, `${JSON.stringify(store, null, 2)}\n`, { mode: 0o600 });
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=credentials.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentials.js","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"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"}
|