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.
@@ -0,0 +1,81 @@
1
+ /**
2
+ * 猜得出來的東西就不要叫人打。
3
+ *
4
+ * 一行 `costaff-workspace push` 應該就能把當前資料夾推上去。每一個猜測都必須是可以被旗標
5
+ * 蓋掉的,而且猜錯的時候要說出它猜了什麼 —— 一個安靜猜錯的預設值,比要求人打
6
+ * 完整指令更糟。
7
+ */
8
+ import fs from 'node:fs/promises';
9
+ import path from 'node:path';
10
+ /**
11
+ * 正式站。自架 receiver 的人用 --endpoint 或 COSTAFF_WORKSPACE_ENDPOINT 指過去;
12
+ * 協定是公開規格,這裡只是把最常見的那一個變成不必打的那一個。
13
+ */
14
+ export const DEFAULT_ENDPOINT = 'https://workspace.costaffs.app';
15
+ /** 伺服器認的 slug:小寫英數與連字號,開頭必須是英數,最長 63。 */
16
+ const SLUG = /^[a-z0-9][a-z0-9-]{0,62}$/;
17
+ /**
18
+ * 資料夾名字就是這份檔案的身分。
19
+ *
20
+ * 正規化到伺服器認得的形狀,正規化不出東西就回 null 而不是硬湊一個 —— 中文
21
+ * 資料夾名會被清成空字串,這時候該讓人自己給 --slug,而不是推上一份叫「-」的檔案。
22
+ */
23
+ export function slugFromDir(dir) {
24
+ const slug = path
25
+ .basename(path.resolve(dir))
26
+ .toLowerCase()
27
+ .replace(/[^a-z0-9]+/g, '-')
28
+ .replace(/^-+|-+$/g, '')
29
+ .slice(0, 63)
30
+ .replace(/-+$/g, '');
31
+ return SLUG.test(slug) ? slug : null;
32
+ }
33
+ /** 讀 package.json,沒有或壞掉都當作沒有 —— 這只是猜測的材料,不是必要條件。 */
34
+ export async function readManifest(root) {
35
+ try {
36
+ const raw = await fs.readFile(path.join(root, 'package.json'), 'utf8');
37
+ const parsed = JSON.parse(raw);
38
+ return typeof parsed === 'object' && parsed !== null ? parsed : null;
39
+ }
40
+ catch {
41
+ return null;
42
+ }
43
+ }
44
+ /*
45
+ * 三個框架各自的套件前綴。相依套件是專案自己寫下的事實,比副檔名或資料夾名可靠。
46
+ */
47
+ const BY_PREFIX = [
48
+ ['@open-document/', 'document'],
49
+ ['@open-slide/', 'deck'],
50
+ ['@open-sheet/', 'workbook'],
51
+ ];
52
+ /** 從相依套件認出這是哪一種檔案;認不出來回 null,由呼叫端決定要不要退回預設。 */
53
+ export function kindFromManifest(manifest) {
54
+ if (manifest === null)
55
+ return null;
56
+ const names = new Set();
57
+ for (const field of ['dependencies', 'devDependencies', 'peerDependencies']) {
58
+ const deps = manifest[field];
59
+ if (typeof deps === 'object' && deps !== null) {
60
+ for (const name of Object.keys(deps))
61
+ names.add(name);
62
+ }
63
+ }
64
+ for (const [prefix, kind] of BY_PREFIX) {
65
+ for (const name of names)
66
+ if (name.startsWith(prefix))
67
+ return kind;
68
+ }
69
+ return null;
70
+ }
71
+ /**
72
+ * 顯示名稱。package.json 的 description 是作者寫給人看的一句話,比 slug 好;
73
+ * 沒有就退回 slug,而不是退回套件名 —— 套件名通常和 slug 一樣,多一層沒有意義。
74
+ */
75
+ export function titleFromManifest(manifest, slug) {
76
+ const description = manifest?.description;
77
+ if (typeof description === 'string' && description.trim() !== '')
78
+ return description.trim();
79
+ return slug;
80
+ }
81
+ //# sourceMappingURL=defaults.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaults.js","sourceRoot":"","sources":["../src/defaults.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B;;;GAGG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,gCAAgC,CAAC;AAEjE,yCAAyC;AACzC,MAAM,IAAI,GAAG,2BAA2B,CAAC;AAEzC;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,MAAM,IAAI,GAAG,IAAI;SACd,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;SAC3B,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;SACZ,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACvB,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;AACvC,CAAC;AAID,oDAAoD;AACpD,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAY;IAC7C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAC;QACvE,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACxC,OAAO,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,CAAC,CAAC,CAAE,MAAmB,CAAC,CAAC,CAAC,IAAI,CAAC;IACrF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,SAAS,GAAyB;IACtC,CAAC,iBAAiB,EAAE,UAAU,CAAC;IAC/B,CAAC,cAAc,EAAE,MAAM,CAAC;IACxB,CAAC,cAAc,EAAE,UAAU,CAAC;CAC7B,CAAC;AAEF,+CAA+C;AAC/C,MAAM,UAAU,gBAAgB,CAAC,QAAyB;IACxD,IAAI,QAAQ,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACnC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAChC,KAAK,MAAM,KAAK,IAAI,CAAC,cAAc,EAAE,iBAAiB,EAAE,kBAAkB,CAAC,EAAE,CAAC;QAC5E,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;QAC7B,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YAC9C,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;gBAAE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACxD,CAAC;IACH,CAAC;IACD,KAAK,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,SAAS,EAAE,CAAC;QACvC,KAAK,MAAM,IAAI,IAAI,KAAK;YAAE,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;gBAAE,OAAO,IAAI,CAAC;IACrE,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAyB,EAAE,IAAY;IACvE,MAAM,WAAW,GAAG,QAAQ,EAAE,WAAW,CAAC;IAC1C,IAAI,OAAO,WAAW,KAAK,QAAQ,IAAI,WAAW,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,WAAW,CAAC,IAAI,EAAE,CAAC;IAC5F,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,42 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ /** Same technique the frameworks use to read their own `meta` — never evaluate the module. */
4
+ function metaField(source, field) {
5
+ const m = source.match(new RegExp(`${field}\\s*:\\s*(['"\`])((?:\\\\.|(?!\\1).)*)\\1`));
6
+ return m === null ? undefined : m[2];
7
+ }
8
+ export async function readEntries(root, surface) {
9
+ const base = path.join(root, surface.dir);
10
+ let ids;
11
+ try {
12
+ ids = (await fs.readdir(base, { withFileTypes: true }))
13
+ .filter((e) => e.isDirectory())
14
+ .map((e) => e.name)
15
+ .sort();
16
+ }
17
+ catch {
18
+ return [];
19
+ }
20
+ const entries = [];
21
+ for (const id of ids) {
22
+ let source = '';
23
+ for (const ext of ['tsx', 'jsx', 'ts', 'js']) {
24
+ try {
25
+ source = await fs.readFile(path.join(base, id, `index.${ext}`), 'utf8');
26
+ break;
27
+ }
28
+ catch {
29
+ // Try the next extension.
30
+ }
31
+ }
32
+ if (source === '')
33
+ continue;
34
+ const entry = { id };
35
+ for (const f of surface.metaFields) {
36
+ entry[f] = metaField(source, f);
37
+ }
38
+ entries.push(entry);
39
+ }
40
+ return entries;
41
+ }
42
+ //# sourceMappingURL=entries.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entries.js","sourceRoot":"","sources":["../src/entries.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,8FAA8F;AAC9F,SAAS,SAAS,CAAC,MAAc,EAAE,KAAa;IAC9C,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,GAAG,KAAK,2CAA2C,CAAC,CAAC,CAAC;IACxF,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACvC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAAY,EAAE,OAAgB;IAC9D,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;IAC1C,IAAI,GAAa,CAAC;IAClB,IAAI,CAAC;QACH,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;aACpD,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;aAC9B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;aAClB,IAAI,EAAE,CAAC;IACZ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,MAAM,OAAO,GAAY,EAAE,CAAC;IAC5B,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;QACrB,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,KAAK,MAAM,GAAG,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;YAC7C,IAAI,CAAC;gBACH,MAAM,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,EAAE,SAAS,GAAG,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC;gBACxE,MAAM;YACR,CAAC;YAAC,MAAM,CAAC;gBACP,0BAA0B;YAC5B,CAAC;QACH,CAAC;QACD,IAAI,MAAM,KAAK,EAAE;YAAE,SAAS;QAC5B,MAAM,KAAK,GAAU,EAAE,EAAE,EAAE,CAAC;QAC5B,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;YAClC,KAAiC,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAC/D,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,229 @@
1
+ /**
2
+ * Every assumption the workspace shell makes about the three frameworks.
3
+ *
4
+ * The shell is downstream of three independently released packages. When one of
5
+ * them changes something we lean on, this file is what must break — loudly, and
6
+ * naming the assumption — instead of the site quietly rendering wrong. Anything
7
+ * coupled to a framework belongs here and nowhere else.
8
+ *
9
+ * The assumptions are ranked by how likely they are to move:
10
+ *
11
+ * CLI flags, `base`, `home` public config, semver-protected low
12
+ * build output layout conventional but undocumented medium
13
+ * `meta` read by regex same technique the frameworks use medium
14
+ * design tokens internal — so we derive, not copy high
15
+ */
16
+ export const CONTRACT_VERSION = 1;
17
+ export const SURFACES = [
18
+ {
19
+ id: 'docs',
20
+ label: '文件',
21
+ kindLabel: 'Document',
22
+ dir: 'docs',
23
+ cli: 'open-doc',
24
+ mount: 'd',
25
+ type: 'spa',
26
+ stage: 'docs',
27
+ buildArgs: (out) => ['build', '--out-dir', out],
28
+ /** Written into the staged project so the item builds at its own root. */
29
+ itemConfig: {
30
+ file: 'open-doc.config.ts',
31
+ /*
32
+ * The document browser is switched off. A per-item bundle holds one
33
+ * document, so its index is a list of one that the receiver redirects
34
+ * past anyway — and while it was on, the viewer's back arrow was a
35
+ * `<Link to="/">` into that index, which bounced straight back here.
36
+ * Off, open-doc puts the theme toggle in the document header instead,
37
+ * which is the only place a reader could reach it.
38
+ *
39
+ * `home` is deliberately not set: the workspace bar the receiver injects
40
+ * is the way back, and a second arrow pointing at the same place — one
41
+ * the bundle would have to bake an address for — is one too many.
42
+ */
43
+ body: (token) => `export default {\n` +
44
+ ` base: '/${token}/',\n` +
45
+ ` build: { showDocBrowser: false },\n` +
46
+ `};\n`,
47
+ },
48
+ /** Where the framework's own router puts the item once mounted at root. */
49
+ itemRoute: (id) => `/d/${id}`,
50
+ metaFields: ['title', 'subtitle', 'pageSize'],
51
+ /*
52
+ * The receiver's injected bar carries the way back. open-doc does NOT drop
53
+ * its own arrow when the browser is off — showDocBrowser:false swaps `/`
54
+ * for a 404 page and leaves the arrow pointing at it, so a reader sees two
55
+ * back arrows and the inner one says Nothing here. The receiver hides that
56
+ * link (BUNDLE_TRIM in content-bar.ts); this note is here because that is
57
+ * the assumption, and this file is where assumptions about the frameworks
58
+ * are supposed to be written down.
59
+ */
60
+ backLink: 'bar',
61
+ href: (id) => `/d/d/${id}`,
62
+ detail: (e) => `${e.pageSize ?? 'A4'} · open-doc`,
63
+ /** The palette the shell adopts comes from this surface's built stylesheet. */
64
+ paletteSource: true,
65
+ },
66
+ {
67
+ id: 'sheets',
68
+ label: '試算表',
69
+ kindLabel: 'Workbook',
70
+ dir: 'sheets',
71
+ cli: 'open-sheet',
72
+ mount: 's',
73
+ type: 'files',
74
+ stage: 'sheets',
75
+ // open-sheet has no static-site build — `build` compiles to xlsx/csv, and
76
+ // `--html` adds a self-contained page per workbook. The workspace wraps
77
+ // that page in its own chrome (see `wrap`) rather than asking the export to
78
+ // carry a link back, which would be dead once the file is downloaded.
79
+ buildArgs: (out) => ['build', '--out', out, '--html'],
80
+ itemRoute: () => '/',
81
+ metaFields: ['title', 'description'],
82
+ wrap: true,
83
+ href: (id) => `/s/${id}/`,
84
+ detail: () => 'HTML · xlsx · open-sheet',
85
+ },
86
+ {
87
+ id: 'slides',
88
+ label: '簡報',
89
+ kindLabel: 'Deck',
90
+ dir: 'slides',
91
+ cli: 'open-slide',
92
+ mount: 'p',
93
+ type: 'spa',
94
+ stage: 'slides',
95
+ buildArgs: (out) => ['build', '--out-dir', out],
96
+ itemConfig: {
97
+ file: 'open-slide.config.ts',
98
+ /*
99
+ * Same reasoning as open-doc: one deck per bundle, so its browser is a
100
+ * list of one and the back arrow that points at it only loops. open-slide
101
+ * has no `home` option — not ours to add — and switching the browser off
102
+ * removes that arrow, leaving the receiver's bar as the way back.
103
+ */
104
+ body: (token) => `export default {\n` +
105
+ ` base: '/${token}/',\n` +
106
+ ` build: { showSlideBrowser: false },\n` +
107
+ `};\n`,
108
+ },
109
+ itemRoute: (id) => `/s/${id}`,
110
+ metaFields: ['title'],
111
+ // Not ours to change: the deck viewer's back affordance is tied to its own
112
+ // slide browser, so switching that browser off takes the arrow with it.
113
+ // The receiver's bar carries the way back for every surface instead.
114
+ backLink: 'bar',
115
+ href: (id) => `/p/s/${id}`,
116
+ detail: () => '16:9 · open-slide',
117
+ },
118
+ ];
119
+ /** Token names the shell reads out of the palette source's stylesheet. */
120
+ export const TOKENS = [
121
+ 'background',
122
+ 'foreground',
123
+ 'canvas',
124
+ 'muted',
125
+ 'muted-foreground',
126
+ 'accent',
127
+ 'border',
128
+ ];
129
+ /**
130
+ * Used only when extraction fails, so a framework reshuffling its stylesheet
131
+ * degrades the shell to "slightly off" rather than "unreadable". The build
132
+ * warns when this is in play.
133
+ */
134
+ export const FALLBACK_PALETTE = {
135
+ light: {
136
+ background: 'oklch(99% 0 0)',
137
+ foreground: 'oklch(14.5% 0 0)',
138
+ canvas: 'oklch(93% 0 0)',
139
+ muted: 'oklch(95.5% 0 0)',
140
+ 'muted-foreground': 'oklch(50% 0 0)',
141
+ accent: 'oklch(94.5% 0 0)',
142
+ border: 'oklch(90% 0 0)',
143
+ },
144
+ dark: {
145
+ background: 'oklch(16% 0 0)',
146
+ foreground: 'oklch(97% 0 0)',
147
+ canvas: 'oklch(12% 0 0)',
148
+ muted: 'oklch(24% 0 0)',
149
+ 'muted-foreground': 'oklch(68% 0 0)',
150
+ accent: 'oklch(25% 0 0)',
151
+ border: 'oklch(28% 0 0)',
152
+ },
153
+ };
154
+ function readBlock(css, selector) {
155
+ const at = css.indexOf(`${selector}{`);
156
+ if (at === -1)
157
+ return null;
158
+ const end = css.indexOf('}', at);
159
+ if (end === -1)
160
+ return null;
161
+ const body = css.slice(at + selector.length + 1, end);
162
+ const out = {};
163
+ for (const decl of body.split(';')) {
164
+ const colon = decl.indexOf(':');
165
+ if (colon === -1)
166
+ continue;
167
+ const name = decl.slice(0, colon).trim();
168
+ if (!name.startsWith('--'))
169
+ continue;
170
+ out[name.slice(2)] = decl.slice(colon + 1).trim();
171
+ }
172
+ return out;
173
+ }
174
+ /**
175
+ * Pulls the viewer's own palette out of its built stylesheet so the shell
176
+ * tracks it. Copying the values would mean a palette change upstream silently
177
+ * leaves the index looking like a different product — which is the whole thing
178
+ * this integration is trying to avoid.
179
+ */
180
+ export function extractPalette(css) {
181
+ const light = readBlock(css, ':root');
182
+ const dark = readBlock(css, '.dark');
183
+ if (light === null || dark === null) {
184
+ return { palette: FALLBACK_PALETTE, missing: TOKENS, derived: false };
185
+ }
186
+ const missing = TOKENS.filter((t) => light[t] === undefined || dark[t] === undefined);
187
+ if (missing.length > 0) {
188
+ return { palette: FALLBACK_PALETTE, missing, derived: false };
189
+ }
190
+ const pick = (src) => Object.fromEntries(TOKENS.map((t) => [t, src[t]]));
191
+ return { palette: { light: pick(light), dark: pick(dark) }, missing: [], derived: true };
192
+ }
193
+ /**
194
+ * next-themes with `attribute="class"` and no `storageKey`. The shell reads the
195
+ * same key so toggling the theme inside a viewer carries back to the index —
196
+ * two surfaces disagreeing about light or dark is the most visible way an
197
+ * integration looks unintegrated.
198
+ */
199
+ export const THEME = { storageKey: 'theme', darkClass: 'dark' };
200
+ /** Assumptions about a surface's build output, checked after every build. */
201
+ export function checkOutput(surface, files) {
202
+ const problems = [];
203
+ if (surface.type === 'spa') {
204
+ if (!files.includes('index.html')) {
205
+ problems.push(`${surface.cli} build produced no index.html — the mount at /${surface.mount}/ needs one.`);
206
+ }
207
+ if (!files.some((f) => f.startsWith('assets/'))) {
208
+ problems.push(`${surface.cli} build produced no assets/ directory — the base may have moved.`);
209
+ }
210
+ }
211
+ if (surface.type === 'files' && !files.some((f) => f.endsWith('.html'))) {
212
+ problems.push(`${surface.cli} build produced no .html — did --html stop working?`);
213
+ }
214
+ return problems;
215
+ }
216
+ /** The mounted SPA must reference its assets under its own mount, not the root. */
217
+ export function checkBase(surface, indexHtml) {
218
+ if (surface.type !== 'spa')
219
+ return [];
220
+ const refs = [...indexHtml.matchAll(/(?:src|href)="([^"]+)"/g)].map((m) => m[1]);
221
+ const wrong = refs.filter((r) => r.startsWith('/') && !r.startsWith(`/${surface.mount}/`));
222
+ return wrong.length === 0
223
+ ? []
224
+ : [
225
+ `${surface.cli} emitted root-absolute asset paths (${wrong.slice(0, 2).join(', ')}) — ` +
226
+ `\`base: '/${surface.mount}/'\` did not take effect, so every asset will 404.`,
227
+ ];
228
+ }
229
+ //# sourceMappingURL=integration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"integration.js","sourceRoot":"","sources":["../src/integration.ts"],"names":[],"mappings":"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"}
package/dist/main.js ADDED
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * costaff-workspace —— 一個入口,動作放在子指令。
4
+ *
5
+ * 兩個獨立執行檔的時候,每加一個功能就要多開一個 costaff-xxx,而且沒有任何地方
6
+ * 能一次看完全部。子指令讓 --help 就是那份清單 —— 對 Agent 尤其重要,它不會去猜
7
+ * 還有哪些兄弟指令存在。
8
+ */
9
+ import { runPull } from "./cli-pull.js";
10
+ import { runPush } from "./cli.js";
11
+ import { DEFAULT_ENDPOINT } from "./defaults.js";
12
+ const USAGE = `costaff-workspace — publish to and pull back from CoStaff Workspace
13
+
14
+ costaff-workspace <command> [options]
15
+
16
+ push push this folder up (run it with nothing else)
17
+ pull <token> fetch a published file's source back out
18
+ login sign this machine in
19
+ logout forget this machine's sign-in
20
+
21
+ costaff-workspace <command> --help for that command's options
22
+
23
+ Endpoint defaults to ${DEFAULT_ENDPOINT}; --endpoint or
24
+ COSTAFF_WORKSPACE_ENDPOINT points at a receiver you run yourself.
25
+ `;
26
+ async function main() {
27
+ const [command, ...rest] = process.argv.slice(2);
28
+ switch (command) {
29
+ case 'push':
30
+ return runPush(rest);
31
+ case 'pull':
32
+ return runPull(rest);
33
+ /* login 和 logout 走的是 push 那條路:它們本來就是同一組憑證。 */
34
+ case 'login':
35
+ return runPush([...rest, '--login']);
36
+ case 'logout':
37
+ return runPush([...rest, '--logout']);
38
+ case undefined:
39
+ case '--help':
40
+ case '-h':
41
+ case 'help':
42
+ process.stdout.write(USAGE);
43
+ return;
44
+ default:
45
+ process.stderr.write(`error: unknown command '${command}'\n\n${USAGE}`);
46
+ process.exit(1);
47
+ }
48
+ }
49
+ main().catch((err) => {
50
+ process.stderr.write(`error: ${err instanceof Error ? err.message : String(err)}\n`);
51
+ process.exit(1);
52
+ });
53
+ //# sourceMappingURL=main.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";AACA;;;;;;GAMG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACnC,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEjD,MAAM,KAAK,GAAG;;;;;;;;;;;uBAWS,gBAAgB;;CAEtC,CAAC;AAEF,KAAK,UAAU,IAAI;IACjB,MAAM,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAEjD,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,MAAM;YACT,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC;QACvB,KAAK,MAAM;YACT,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC;QACvB,8CAA8C;QAC9C,KAAK,OAAO;YACV,OAAO,OAAO,CAAC,CAAC,GAAG,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC;QACvC,KAAK,QAAQ;YACX,OAAO,OAAO,CAAC,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC;QACxC,KAAK,SAAS,CAAC;QACf,KAAK,QAAQ,CAAC;QACd,KAAK,IAAI,CAAC;QACV,KAAK,MAAM;YACT,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAC5B,OAAO;QACT;YACE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,2BAA2B,OAAO,QAAQ,KAAK,EAAE,CAAC,CAAC;YACxE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IAC5B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACrF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,117 @@
1
+ /**
2
+ * 一個專案裡的每一份文件,各自一個 bundle。
3
+ *
4
+ * 這是整份程式碼裡最容易被「簡化」掉的地方,所以說清楚為什麼不能:一個
5
+ * open-doc 專案是一個站台含多份文件,`open-doc build` 產出的 dist/ 裡四份文件
6
+ * 共用同一個 assets/ 目錄。把那個 dist/ 當成一份檔案推上去,分享其中一份,拿到
7
+ * 連結的人就能抓到另外三份的 chunk —— 分享設定要能擋住其餘的東西,唯一的辦法
8
+ * 是其餘的東西根本不在那個 bundle 裡。
9
+ *
10
+ * 代價是真的:N 次建置而不是一次,資產在各個 bundle 之間重複。
11
+ *
12
+ * token 必須在建置之前決定,不是之後:它是檔案的公開位址,而 bundle 的資產路徑
13
+ * 是照著它烘進去的,晚一步決定就是決定錯。
14
+ */
15
+ import fs from 'node:fs/promises';
16
+ import path from 'node:path';
17
+ import { readEntries } from "./entries.js";
18
+ import { SURFACES } from "./integration.js";
19
+ import { ensureBearer, push } from "./push.js";
20
+ import { tokenFor } from "./state.js";
21
+ /** 這個資料夾裡有哪些可以推的東西。空陣列表示它不是多文件專案。 */
22
+ export async function scanProject(root) {
23
+ const items = [];
24
+ for (const surface of SURFACES) {
25
+ for (const entry of await readEntries(root, surface))
26
+ items.push({ surface, entry });
27
+ }
28
+ return items;
29
+ }
30
+ /** open-sheet 產出的是匯出檔不是網頁,所以 bundle 需要一個入口頁。 */
31
+ function sheetPage(entry, token) {
32
+ const esc = (s) => String(s ?? '').replace(/[&<>"']/g, (c) => `&#${c.charCodeAt(0)};`);
33
+ const title = entry.title ?? entry.id;
34
+ return `<!doctype html><html lang="zh-Hant"><head><meta charset="utf-8">
35
+ <meta name="viewport" content="width=device-width,initial-scale=1"><title>${esc(title)}</title>
36
+ <style>
37
+ html,body{height:100%;margin:0;display:flex;flex-direction:column;
38
+ font:14px/1.6 -apple-system,"PingFang TC",system-ui,sans-serif;background:#fff;color:#14171f}
39
+ header{display:flex;height:48px;flex:none;align-items:center;gap:12px;padding:0 16px;
40
+ border-bottom:1px solid #e3e6ec}
41
+ h1{margin:0;font-size:14px;font-weight:500}
42
+ a.dl{margin-left:auto;font-size:13px;color:#1e4fd8;text-decoration:none}
43
+ main{flex:1;min-height:0;background:#f1f5f9}
44
+ iframe{display:block;width:100%;height:100%;border:0;color-scheme:light}
45
+ </style></head><body>
46
+ <header><h1>${esc(title)}</h1><a class="dl" href="/${esc(token)}/${esc(entry.id)}.xlsx" download>下載 xlsx</a></header>
47
+ <main><iframe src="/${esc(token)}/${esc(entry.id)}.html" title="${esc(title)}"></iframe></main>
48
+ </body></html>`;
49
+ }
50
+ export async function pushProject(opts) {
51
+ const { root, endpoint, items, buildItem } = opts;
52
+ const write = opts.out ?? ((line) => process.stdout.write(line));
53
+ const dist = path.join(root, 'dist');
54
+ const scratch = path.join(root, '.build');
55
+ await fs.rm(scratch, { recursive: true, force: true });
56
+ /*
57
+ * 一次登入,不是每一份各登一次。裝置授權碼是印給人看的,而每一份推送的輸出是
58
+ * 靜音的(否則四份的進度會蓋掉彼此)—— 靜音的 out 也會把授權碼吞掉,然後整件事
59
+ * 停在那裡輪詢到過期。乾跑不需要憑證,所以也不必連線。
60
+ */
61
+ const bearer = opts.dryRun === true ? undefined : (opts.base?.bearer ?? (await ensureBearer({ endpoint, out: write })));
62
+ for (const { surface, entry } of items) {
63
+ const kind = surface.kindLabel.toLowerCase();
64
+ const token = await tokenFor(root, endpoint, kind, entry.id);
65
+ const dir = path.join(dist, entry.id);
66
+ write(` ${entry.id.padEnd(28)} `);
67
+ if (surface.type === 'files') {
68
+ const staged = path.join(scratch, surface.stage, entry.id);
69
+ await buildItem({ root, surface, id: entry.id, outDir: staged });
70
+ await fs.mkdir(dir, { recursive: true });
71
+ for (const f of await fs.readdir(staged)) {
72
+ if (f.startsWith(`${entry.id}.`))
73
+ await fs.cp(path.join(staged, f), path.join(dir, f));
74
+ }
75
+ await fs.rm(path.join(scratch, surface.stage), { recursive: true, force: true });
76
+ await fs.writeFile(path.join(dir, 'index.html'), sheetPage(entry, token));
77
+ }
78
+ else {
79
+ const itemConfig = surface.itemConfig;
80
+ await buildItem({
81
+ root,
82
+ surface,
83
+ id: entry.id,
84
+ outDir: dir,
85
+ config: itemConfig === undefined
86
+ ? undefined
87
+ : { file: itemConfig.file, body: itemConfig.body(token) },
88
+ });
89
+ }
90
+ await push({
91
+ ...opts.base,
92
+ bearer,
93
+ endpoint,
94
+ slug: entry.id,
95
+ kind,
96
+ title: entry.title ?? entry.id,
97
+ subtitle: entry.subtitle ?? entry.description,
98
+ route: surface.itemRoute(entry.id),
99
+ siteDir: dir,
100
+ /* 原始碼跟著走,否則推上去的只有產物,沒有人能在上面繼續工作。 */
101
+ sourceRoot: root,
102
+ sourceDir: path.join(surface.dir, entry.id),
103
+ token,
104
+ dryRun: opts.dryRun,
105
+ cwd: root,
106
+ out: () => { },
107
+ });
108
+ write(`→ ${new URL(endpoint).origin}/${token}\n`);
109
+ }
110
+ await fs.rm(scratch, { recursive: true, force: true });
111
+ }
112
+ /** 預設的建置器:呼叫框架自己的 CLI。測試會換掉它。 */
113
+ export const realBuildItem = async (o) => {
114
+ const { buildItem } = await import("./stage.js");
115
+ await buildItem(o);
116
+ };
117
+ //# sourceMappingURL=project.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project.js","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * The wire contract between the CLI and a receiver.
3
+ *
4
+ * Anyone may host a receiver — `--endpoint` points the CLI at it — so this file
5
+ * is a public specification, not an implementation detail. Adding a required
6
+ * field is a breaking change for every third-party receiver; bump
7
+ * `PUSH_PROTOCOL` and keep reading the old shape when that happens.
8
+ */
9
+ export const PUSH_PROTOCOL = 1;
10
+ export const DISCOVERY_PATH = '/.well-known/costaff-workspace-push';
11
+ export function isDiscovery(value) {
12
+ if (typeof value !== 'object' || value === null)
13
+ return false;
14
+ const v = value;
15
+ return (typeof v.protocol === 'number' &&
16
+ typeof v.name === 'string' &&
17
+ typeof v.deviceAuthStart === 'string' &&
18
+ typeof v.deviceAuthPoll === 'string' &&
19
+ typeof v.push === 'string' &&
20
+ typeof v.maxBytes === 'number');
21
+ }
22
+ export function isPushResult(value) {
23
+ if (typeof value !== 'object' || value === null)
24
+ return false;
25
+ const v = value;
26
+ return typeof v.url === 'string' && typeof v.slug === 'string';
27
+ }
28
+ //# sourceMappingURL=protocol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,CAAC;AAC/B,MAAM,CAAC,MAAM,cAAc,GAAG,qCAAqC,CAAC;AAiFpE,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,CAAC,GAAG,KAAgC,CAAC;IAC3C,OAAO,CACL,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ;QAC9B,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ;QAC1B,OAAO,CAAC,CAAC,eAAe,KAAK,QAAQ;QACrC,OAAO,CAAC,CAAC,cAAc,KAAK,QAAQ;QACpC,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ;QAC1B,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAC/B,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,CAAC,GAAG,KAAgC,CAAC;IAC3C,OAAO,OAAO,CAAC,CAAC,GAAG,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC;AACjE,CAAC"}