zooid 0.6.0 → 0.7.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 +1 -1
- package/README.md +36 -444
- package/dist/bin.js +1797 -0
- package/dist/bin.js.map +1 -0
- package/dist/chunk-N5POSZX5.js +31000 -0
- package/dist/chunk-N5POSZX5.js.map +1 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.js +4 -3788
- package/dist/index.js.map +1 -0
- package/dist/web/assets/geist-cyrillic-wght-normal-CHSlOQsW.woff2 +0 -0
- package/dist/web/assets/geist-latin-ext-wght-normal-DMtmJ5ZE.woff2 +0 -0
- package/dist/web/assets/geist-latin-wght-normal-Dm3htQBi.woff2 +0 -0
- package/dist/web/assets/index-BT_v3DKu.js +295 -0
- package/dist/web/assets/index-DJTghnF-.css +1 -0
- package/dist/web/assets/index-JZMMlqDP.js +118066 -0
- package/dist/web/assets/reaction-picker-emoji-Kh7emgFa.js +716 -0
- package/dist/web/favicon.svg +1 -0
- package/dist/web/index.html +14 -0
- package/package.json +42 -22
- package/src/bin.ts +112 -0
- package/src/bootstrap/admin.test.ts +69 -0
- package/src/bootstrap/admin.ts +37 -0
- package/src/bootstrap/configs.test.ts +24 -0
- package/src/bootstrap/configs.ts +64 -0
- package/src/bootstrap/data-layout.test.ts +25 -0
- package/src/bootstrap/data-layout.ts +20 -0
- package/src/bootstrap/derive.test.ts +74 -0
- package/src/bootstrap/derive.ts +40 -0
- package/src/bootstrap/paths.test.ts +22 -0
- package/src/bootstrap/paths.ts +27 -0
- package/src/bootstrap/tokens.test.ts +44 -0
- package/src/bootstrap/tokens.ts +43 -0
- package/src/build-registry.context.test.ts +61 -0
- package/src/build-registry.test.ts +59 -0
- package/src/build-registry.ts +142 -0
- package/src/build-registry.zod043.test.ts +64 -0
- package/src/commands/dev-cascade.test.ts +38 -0
- package/src/commands/dev-cascade.ts +35 -0
- package/src/commands/dev-data-layout.test.ts +55 -0
- package/src/commands/dev.ts +308 -0
- package/src/commands/init/generators.test.ts +97 -0
- package/src/commands/init/generators.ts +119 -0
- package/src/commands/init/prompts.ts +123 -0
- package/src/commands/init/registry.test.ts +33 -0
- package/src/commands/init/registry.ts +75 -0
- package/src/commands/init/sniff.test.ts +41 -0
- package/src/commands/init/sniff.ts +30 -0
- package/src/commands/init.test.ts +131 -0
- package/src/commands/init.ts +158 -0
- package/src/commands/logs.test.ts +105 -0
- package/src/commands/logs.ts +108 -0
- package/src/commands/start.ts +28 -0
- package/src/commands/status.test.ts +73 -0
- package/src/commands/status.ts +100 -0
- package/src/daemon/start-daemon.test.ts +67 -0
- package/src/daemon/start-daemon.ts +243 -0
- package/src/index.ts +2 -0
- package/src/observability/capture-agent.ts +58 -0
- package/src/observability/capture-tuwunel.test.ts +57 -0
- package/src/observability/capture-tuwunel.ts +24 -0
- package/src/observability/file-sink.test.ts +68 -0
- package/src/observability/file-sink.ts +78 -0
- package/src/observability/observability.integration.test.ts +162 -0
- package/src/observability/paths.test.ts +104 -0
- package/src/observability/paths.ts +94 -0
- package/src/services/tuwunel.test.ts +64 -0
- package/src/services/tuwunel.ts +91 -0
- package/src/web/resolve.test.ts +50 -0
- package/src/web/resolve.ts +31 -0
- package/src/web/static.test.ts +51 -0
- package/src/web/static.ts +61 -0
- package/src/web/watch.ts +103 -0
- package/dist/chunk-67ZRMVHO.js +0 -174
- package/dist/chunk-AR456MHY.js +0 -29
- package/dist/chunk-VBGU2NST.js +0 -139
- package/dist/client-4VMFEFDX.js +0 -22
- package/dist/config-2KK5GX42.js +0 -27
- package/dist/template-T5IB4YWC.js +0 -92
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
|
|
2
|
+
import { tmpdir } from 'node:os'
|
|
3
|
+
import { join } from 'node:path'
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
|
|
5
|
+
import { resolveWebRoot, webSourcePackage } from './resolve.js'
|
|
6
|
+
|
|
7
|
+
let root: string
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
root = mkdtempSync(join(tmpdir(), 'zooid-resolve-'))
|
|
10
|
+
})
|
|
11
|
+
afterEach(() => rmSync(root, { recursive: true, force: true }))
|
|
12
|
+
|
|
13
|
+
describe('resolveWebRoot', () => {
|
|
14
|
+
it('returns dist/web when present (published-package layout)', () => {
|
|
15
|
+
const distWeb = join(root, 'dist', 'web')
|
|
16
|
+
mkdirSync(distWeb, { recursive: true })
|
|
17
|
+
writeFileSync(join(distWeb, 'index.html'), '<html/>')
|
|
18
|
+
expect(resolveWebRoot(root)).toBe(distWeb)
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('falls back to ../../zoon/packages/web/dist when running from source', () => {
|
|
22
|
+
const cliRoot = join(root, 'zooid', 'packages', 'cli')
|
|
23
|
+
mkdirSync(cliRoot, { recursive: true })
|
|
24
|
+
const webPkg = join(root, 'zoon', 'packages', 'web')
|
|
25
|
+
const webDist = join(webPkg, 'dist')
|
|
26
|
+
mkdirSync(webDist, { recursive: true })
|
|
27
|
+
writeFileSync(join(webPkg, 'package.json'), '{"name":"@zoon/web"}')
|
|
28
|
+
writeFileSync(join(webDist, 'index.html'), '<html/>')
|
|
29
|
+
expect(resolveWebRoot(cliRoot)).toBe(webDist)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('throws a helpful error when neither layout has been built', () => {
|
|
33
|
+
expect(() => resolveWebRoot(root)).toThrow(/@zoon\/web.*build/i)
|
|
34
|
+
})
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
describe('webSourcePackage', () => {
|
|
38
|
+
it('returns the source package dir when sibling zoon/packages/web exists', () => {
|
|
39
|
+
const cliRoot = join(root, 'zooid', 'packages', 'cli')
|
|
40
|
+
mkdirSync(cliRoot, { recursive: true })
|
|
41
|
+
const webPkg = join(root, 'zoon', 'packages', 'web')
|
|
42
|
+
mkdirSync(webPkg, { recursive: true })
|
|
43
|
+
writeFileSync(join(webPkg, 'package.json'), '{"name":"@zoon/web"}')
|
|
44
|
+
expect(webSourcePackage(cliRoot)).toBe(webPkg)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('returns null outside the monorepo', () => {
|
|
48
|
+
expect(webSourcePackage(root)).toBeNull()
|
|
49
|
+
})
|
|
50
|
+
})
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs'
|
|
2
|
+
import { dirname, join, resolve } from 'node:path'
|
|
3
|
+
|
|
4
|
+
const ENV_OVERRIDE = 'ZOOID_DEV_WEB_ROOT_OVERRIDE'
|
|
5
|
+
|
|
6
|
+
export function resolveWebRoot(cliRoot: string): string {
|
|
7
|
+
const override = process.env[ENV_OVERRIDE]
|
|
8
|
+
if (override && existsSync(join(override, 'index.html'))) return resolve(override)
|
|
9
|
+
|
|
10
|
+
const published = join(cliRoot, 'dist', 'web')
|
|
11
|
+
if (existsSync(join(published, 'index.html'))) return published
|
|
12
|
+
|
|
13
|
+
const fromSource = webSourcePackage(cliRoot)
|
|
14
|
+
if (fromSource && existsSync(join(fromSource, 'dist', 'index.html'))) {
|
|
15
|
+
return join(fromSource, 'dist')
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
throw new Error(
|
|
19
|
+
`@zoon/web build not found.\n Tried: ${published}\n Tried: ${fromSource ?? '(no monorepo source)'}\n` +
|
|
20
|
+
`Run \`pnpm -C zoon/packages/web build\` (or set ${ENV_OVERRIDE} to a built dist).`,
|
|
21
|
+
)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Returns the path to the in-tree @zoon/web package (zoon/packages/web) if the
|
|
25
|
+
// CLI is running from the monorepo source. Returns null when running from an
|
|
26
|
+
// installed package (no sibling zoon/ tree).
|
|
27
|
+
export function webSourcePackage(cliRoot: string): string | null {
|
|
28
|
+
const workspaceRoot = dirname(dirname(dirname(cliRoot)))
|
|
29
|
+
const candidate = join(workspaceRoot, 'zoon', 'packages', 'web')
|
|
30
|
+
return existsSync(join(candidate, 'package.json')) ? candidate : null
|
|
31
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
|
|
2
|
+
import { tmpdir } from 'node:os'
|
|
3
|
+
import { join } from 'node:path'
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
|
|
5
|
+
import { webStatic } from './static.js'
|
|
6
|
+
|
|
7
|
+
let dir: string
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
dir = mkdtempSync(join(tmpdir(), 'zooid-web-'))
|
|
10
|
+
mkdirSync(join(dir, 'assets'), { recursive: true })
|
|
11
|
+
writeFileSync(join(dir, 'index.html'), '<!doctype html><div id=root></div>')
|
|
12
|
+
writeFileSync(join(dir, 'assets', 'main.js'), 'console.log("ui")')
|
|
13
|
+
})
|
|
14
|
+
afterEach(() => rmSync(dir, { recursive: true, force: true }))
|
|
15
|
+
|
|
16
|
+
describe('webStatic', () => {
|
|
17
|
+
it('serves index.html at /', async () => {
|
|
18
|
+
const app = webStatic({ webRoot: dir, homeserverUrl: 'http://localhost:8448' })
|
|
19
|
+
const r = await app.request('/')
|
|
20
|
+
expect(r.status).toBe(200)
|
|
21
|
+
expect(await r.text()).toContain('id=root')
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('serves static assets', async () => {
|
|
25
|
+
const app = webStatic({ webRoot: dir, homeserverUrl: 'http://localhost:8448' })
|
|
26
|
+
const r = await app.request('/assets/main.js')
|
|
27
|
+
expect(r.status).toBe(200)
|
|
28
|
+
expect(await r.text()).toContain('console.log("ui")')
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('synthesizes /config.json with the homeserver URL', async () => {
|
|
32
|
+
const app = webStatic({ webRoot: dir, homeserverUrl: 'http://localhost:8448' })
|
|
33
|
+
const r = await app.request('/config.json')
|
|
34
|
+
expect(r.status).toBe(200)
|
|
35
|
+
expect(r.headers.get('content-type')).toMatch(/application\/json/)
|
|
36
|
+
expect(await r.json()).toEqual({ homeserver_url: 'http://localhost:8448' })
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('falls back to index.html for unknown routes (SPA history)', async () => {
|
|
40
|
+
const app = webStatic({ webRoot: dir, homeserverUrl: 'http://localhost:8448' })
|
|
41
|
+
const r = await app.request('/room/!abc:localhost')
|
|
42
|
+
expect(r.status).toBe(200)
|
|
43
|
+
expect(await r.text()).toContain('id=root')
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('returns 404 for a missing asset', async () => {
|
|
47
|
+
const app = webStatic({ webRoot: dir, homeserverUrl: 'http://localhost:8448' })
|
|
48
|
+
const r = await app.request('/assets/nope.js')
|
|
49
|
+
expect(r.status).toBe(404)
|
|
50
|
+
})
|
|
51
|
+
})
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { readFileSync, statSync } from 'node:fs'
|
|
2
|
+
import { extname, join, normalize } from 'node:path'
|
|
3
|
+
import { Hono } from 'hono'
|
|
4
|
+
|
|
5
|
+
export interface WebStaticOpts {
|
|
6
|
+
webRoot: string
|
|
7
|
+
homeserverUrl: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const MIME: Record<string, string> = {
|
|
11
|
+
'.html': 'text/html; charset=utf-8',
|
|
12
|
+
'.js': 'application/javascript; charset=utf-8',
|
|
13
|
+
'.mjs': 'application/javascript; charset=utf-8',
|
|
14
|
+
'.css': 'text/css; charset=utf-8',
|
|
15
|
+
'.json': 'application/json; charset=utf-8',
|
|
16
|
+
'.svg': 'image/svg+xml',
|
|
17
|
+
'.png': 'image/png',
|
|
18
|
+
'.jpg': 'image/jpeg',
|
|
19
|
+
'.jpeg': 'image/jpeg',
|
|
20
|
+
'.woff': 'font/woff',
|
|
21
|
+
'.woff2': 'font/woff2',
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function isAssetPath(p: string): boolean {
|
|
25
|
+
return p.startsWith('/assets/') || /\.[a-z0-9]+$/i.test(p)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function webStatic(opts: WebStaticOpts): Hono {
|
|
29
|
+
const app = new Hono()
|
|
30
|
+
|
|
31
|
+
app.get('/config.json', (c) => c.json({ homeserver_url: opts.homeserverUrl }))
|
|
32
|
+
|
|
33
|
+
app.get('*', (c) => {
|
|
34
|
+
const url = new URL(c.req.url)
|
|
35
|
+
const requested = decodeURIComponent(url.pathname)
|
|
36
|
+
const wantFile = requested === '/' ? '/index.html' : requested
|
|
37
|
+
|
|
38
|
+
const safe = normalize(wantFile).replace(/^(\.\.[/\\])+/g, '')
|
|
39
|
+
const filePath = join(opts.webRoot, safe)
|
|
40
|
+
if (!filePath.startsWith(opts.webRoot)) return c.notFound()
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
const stat = statSync(filePath)
|
|
44
|
+
if (stat.isFile()) {
|
|
45
|
+
const body = readFileSync(filePath)
|
|
46
|
+
const ct = MIME[extname(filePath)] ?? 'application/octet-stream'
|
|
47
|
+
return c.body(body as unknown as ArrayBuffer, 200, { 'content-type': ct })
|
|
48
|
+
}
|
|
49
|
+
} catch {
|
|
50
|
+
// fall through to history fallback
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (isAssetPath(requested)) return c.notFound()
|
|
54
|
+
const indexBytes = readFileSync(join(opts.webRoot, 'index.html'))
|
|
55
|
+
return c.body(indexBytes as unknown as ArrayBuffer, 200, {
|
|
56
|
+
'content-type': MIME['.html']!,
|
|
57
|
+
})
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
return app
|
|
61
|
+
}
|
package/src/web/watch.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { spawn, type ChildProcess } from 'node:child_process'
|
|
2
|
+
import { existsSync, statSync } from 'node:fs'
|
|
3
|
+
import { join } from 'node:path'
|
|
4
|
+
|
|
5
|
+
export interface WebWatchHandle {
|
|
6
|
+
distPath: string
|
|
7
|
+
// Pipe captured child output onwards. Call once Listr / interactive
|
|
8
|
+
// renderers are done, otherwise vite output will get garbled.
|
|
9
|
+
attachStdio: () => void
|
|
10
|
+
stop: () => Promise<void>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface WebWatchOpts {
|
|
14
|
+
webPackageDir: string
|
|
15
|
+
// Wait up to this many ms for vite's first build to finish before resolving.
|
|
16
|
+
firstBuildTimeoutMs?: number
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Spawns `vite build --watch` in webPackageDir and resolves once the first
|
|
20
|
+
// build has completed (detected via index.html mtime > spawn time).
|
|
21
|
+
export async function startWebWatch(opts: WebWatchOpts): Promise<WebWatchHandle> {
|
|
22
|
+
const distPath = join(opts.webPackageDir, 'dist')
|
|
23
|
+
const indexPath = join(distPath, 'index.html')
|
|
24
|
+
const timeoutMs = opts.firstBuildTimeoutMs ?? 60_000
|
|
25
|
+
const spawnTime = Date.now()
|
|
26
|
+
|
|
27
|
+
const child: ChildProcess = spawn(
|
|
28
|
+
'pnpm',
|
|
29
|
+
['-C', opts.webPackageDir, 'exec', 'vite', 'build', '--watch'],
|
|
30
|
+
{ stdio: ['ignore', 'pipe', 'pipe'] },
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
let buffered = ''
|
|
34
|
+
const onData = (chunk: Buffer): void => {
|
|
35
|
+
buffered += chunk.toString('utf8')
|
|
36
|
+
}
|
|
37
|
+
child.stdout?.on('data', onData)
|
|
38
|
+
child.stderr?.on('data', onData)
|
|
39
|
+
|
|
40
|
+
let attached = false
|
|
41
|
+
const attachStdio = (): void => {
|
|
42
|
+
if (attached) return
|
|
43
|
+
attached = true
|
|
44
|
+
child.stdout?.off('data', onData)
|
|
45
|
+
child.stderr?.off('data', onData)
|
|
46
|
+
if (buffered) process.stdout.write(buffered)
|
|
47
|
+
buffered = ''
|
|
48
|
+
child.stdout?.pipe(process.stdout)
|
|
49
|
+
child.stderr?.pipe(process.stderr)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
let exited = false
|
|
53
|
+
child.once('exit', () => {
|
|
54
|
+
exited = true
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
while (Date.now() - spawnTime < timeoutMs) {
|
|
58
|
+
if (exited) {
|
|
59
|
+
attachStdio()
|
|
60
|
+
throw new Error(`vite build --watch exited before first build (code ${child.exitCode})`)
|
|
61
|
+
}
|
|
62
|
+
if (existsSync(indexPath)) {
|
|
63
|
+
const m = statSync(indexPath).mtimeMs
|
|
64
|
+
// Slack for clock granularity on macOS HFS+/APFS.
|
|
65
|
+
if (m >= spawnTime - 500) return makeHandle()
|
|
66
|
+
}
|
|
67
|
+
await new Promise((r) => setTimeout(r, 200))
|
|
68
|
+
}
|
|
69
|
+
// Timed out: clean up and surface what vite said.
|
|
70
|
+
attachStdio()
|
|
71
|
+
await stopChild(child)
|
|
72
|
+
throw new Error(`vite build --watch did not produce ${indexPath} within ${timeoutMs}ms`)
|
|
73
|
+
|
|
74
|
+
function makeHandle(): WebWatchHandle {
|
|
75
|
+
return {
|
|
76
|
+
distPath,
|
|
77
|
+
attachStdio,
|
|
78
|
+
stop: () => stopChild(child),
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function stopChild(child: ChildProcess): Promise<void> {
|
|
84
|
+
if (child.exitCode !== null) return
|
|
85
|
+
child.kill('SIGTERM')
|
|
86
|
+
await new Promise<void>((resolve) => {
|
|
87
|
+
let done = false
|
|
88
|
+
const finish = (): void => {
|
|
89
|
+
if (done) return
|
|
90
|
+
done = true
|
|
91
|
+
resolve()
|
|
92
|
+
}
|
|
93
|
+
child.once('exit', finish)
|
|
94
|
+
setTimeout(() => {
|
|
95
|
+
try {
|
|
96
|
+
child.kill('SIGKILL')
|
|
97
|
+
} catch {
|
|
98
|
+
// already gone
|
|
99
|
+
}
|
|
100
|
+
finish()
|
|
101
|
+
}, 2000)
|
|
102
|
+
})
|
|
103
|
+
}
|
package/dist/chunk-67ZRMVHO.js
DELETED
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// src/lib/config.ts
|
|
4
|
-
import fs from "fs";
|
|
5
|
-
import os from "os";
|
|
6
|
-
import path from "path";
|
|
7
|
-
|
|
8
|
-
// src/lib/constants.ts
|
|
9
|
-
var STATE_FILENAME = "state.json";
|
|
10
|
-
var LEGACY_STATE_FILENAME = "config.json";
|
|
11
|
-
|
|
12
|
-
// src/lib/config.ts
|
|
13
|
-
function getConfigDir() {
|
|
14
|
-
return process.env.ZOOID_CONFIG_DIR ?? path.join(os.homedir(), ".zooid");
|
|
15
|
-
}
|
|
16
|
-
function getStatePath() {
|
|
17
|
-
const dir = getConfigDir();
|
|
18
|
-
const statePath = path.join(dir, STATE_FILENAME);
|
|
19
|
-
if (!fs.existsSync(statePath)) {
|
|
20
|
-
const legacyPath = path.join(dir, LEGACY_STATE_FILENAME);
|
|
21
|
-
if (fs.existsSync(legacyPath)) {
|
|
22
|
-
fs.renameSync(legacyPath, statePath);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
return statePath;
|
|
26
|
-
}
|
|
27
|
-
var getConfigPath = getStatePath;
|
|
28
|
-
function loadConfigFile() {
|
|
29
|
-
try {
|
|
30
|
-
const raw = fs.readFileSync(getStatePath(), "utf-8");
|
|
31
|
-
const parsed = JSON.parse(raw);
|
|
32
|
-
if (parsed.server && !parsed.servers) {
|
|
33
|
-
const serverUrl = parsed.server;
|
|
34
|
-
const migrated = {
|
|
35
|
-
current: serverUrl,
|
|
36
|
-
servers: {
|
|
37
|
-
[serverUrl]: {
|
|
38
|
-
worker_url: parsed.worker_url,
|
|
39
|
-
admin_token: parsed.admin_token,
|
|
40
|
-
channels: parsed.channels
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
const dir = getConfigDir();
|
|
45
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
46
|
-
fs.writeFileSync(
|
|
47
|
-
getConfigPath(),
|
|
48
|
-
JSON.stringify(migrated, null, 2) + "\n"
|
|
49
|
-
);
|
|
50
|
-
return migrated;
|
|
51
|
-
}
|
|
52
|
-
return parsed;
|
|
53
|
-
} catch {
|
|
54
|
-
return {};
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
var serverNoteShown = false;
|
|
58
|
-
function resolveServer() {
|
|
59
|
-
const file = loadConfigFile();
|
|
60
|
-
let cwdUrl;
|
|
61
|
-
try {
|
|
62
|
-
const zooidJsonPath = path.join(process.cwd(), "zooid.json");
|
|
63
|
-
if (fs.existsSync(zooidJsonPath)) {
|
|
64
|
-
const raw = fs.readFileSync(zooidJsonPath, "utf-8");
|
|
65
|
-
const parsed = JSON.parse(raw);
|
|
66
|
-
cwdUrl = parsed.url || void 0;
|
|
67
|
-
}
|
|
68
|
-
} catch {
|
|
69
|
-
}
|
|
70
|
-
if (cwdUrl) {
|
|
71
|
-
if (file.current && file.current !== cwdUrl && !serverNoteShown) {
|
|
72
|
-
serverNoteShown = true;
|
|
73
|
-
console.log(
|
|
74
|
-
` Note: using server from zooid.json (${cwdUrl}), current is ${file.current}`
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
return cwdUrl;
|
|
78
|
-
}
|
|
79
|
-
return file.current;
|
|
80
|
-
}
|
|
81
|
-
function loadConfig() {
|
|
82
|
-
const file = loadConfigFile();
|
|
83
|
-
const serverUrl = resolveServer();
|
|
84
|
-
const entry = serverUrl ? file.servers?.[serverUrl] : void 0;
|
|
85
|
-
return {
|
|
86
|
-
server: serverUrl,
|
|
87
|
-
worker_url: entry?.worker_url,
|
|
88
|
-
admin_token: entry?.admin_token,
|
|
89
|
-
channels: entry?.channels
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
function saveConfig(partial, serverUrl, options) {
|
|
93
|
-
const dir = getConfigDir();
|
|
94
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
95
|
-
const file = loadConfigFile();
|
|
96
|
-
const url = serverUrl ?? resolveServer();
|
|
97
|
-
if (!url) {
|
|
98
|
-
throw new Error(
|
|
99
|
-
"No server URL to save config for. Deploy first or set url in zooid.json."
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
if (!file.servers) file.servers = {};
|
|
103
|
-
const existing = file.servers[url] ?? {};
|
|
104
|
-
const merged = { ...existing, ...partial };
|
|
105
|
-
if (partial.channels && existing.channels) {
|
|
106
|
-
merged.channels = { ...existing.channels };
|
|
107
|
-
for (const [chId, chData] of Object.entries(partial.channels)) {
|
|
108
|
-
merged.channels[chId] = { ...existing.channels[chId], ...chData };
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
file.servers[url] = merged;
|
|
112
|
-
if (options?.setCurrent !== false) {
|
|
113
|
-
file.current = url;
|
|
114
|
-
}
|
|
115
|
-
fs.writeFileSync(getConfigPath(), JSON.stringify(file, null, 2) + "\n");
|
|
116
|
-
}
|
|
117
|
-
function loadDirectoryToken() {
|
|
118
|
-
const file = loadConfigFile();
|
|
119
|
-
return file.directory_token;
|
|
120
|
-
}
|
|
121
|
-
function saveDirectoryToken(token) {
|
|
122
|
-
const dir = getConfigDir();
|
|
123
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
124
|
-
const file = loadConfigFile();
|
|
125
|
-
file.directory_token = token;
|
|
126
|
-
fs.writeFileSync(getConfigPath(), JSON.stringify(file, null, 2) + "\n");
|
|
127
|
-
}
|
|
128
|
-
function recordTailHistory(channelId, serverUrl, name, lastEventId) {
|
|
129
|
-
const url = serverUrl ?? resolveServer();
|
|
130
|
-
if (!url) return;
|
|
131
|
-
const file = loadConfigFile();
|
|
132
|
-
if (!file.servers) file.servers = {};
|
|
133
|
-
if (!file.servers[url]) file.servers[url] = {};
|
|
134
|
-
if (!file.servers[url].channels) file.servers[url].channels = {};
|
|
135
|
-
const channel = file.servers[url].channels[channelId] ?? {};
|
|
136
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
137
|
-
const existing = channel.stats;
|
|
138
|
-
channel.stats = {
|
|
139
|
-
num_tails: (existing?.num_tails ?? 0) + 1,
|
|
140
|
-
last_tailed_at: now,
|
|
141
|
-
first_tailed_at: existing?.first_tailed_at ?? now,
|
|
142
|
-
last_event_id: lastEventId ?? existing?.last_event_id
|
|
143
|
-
};
|
|
144
|
-
if (name) {
|
|
145
|
-
channel.name = name;
|
|
146
|
-
}
|
|
147
|
-
file.servers[url].channels[channelId] = channel;
|
|
148
|
-
const dir = getConfigDir();
|
|
149
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
150
|
-
fs.writeFileSync(getConfigPath(), JSON.stringify(file, null, 2) + "\n");
|
|
151
|
-
}
|
|
152
|
-
function switchServer(url) {
|
|
153
|
-
const dir = getConfigDir();
|
|
154
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
155
|
-
const file = loadConfigFile();
|
|
156
|
-
if (!file.servers) file.servers = {};
|
|
157
|
-
if (!file.servers[url]) file.servers[url] = {};
|
|
158
|
-
file.current = url;
|
|
159
|
-
fs.writeFileSync(getConfigPath(), JSON.stringify(file, null, 2) + "\n");
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
export {
|
|
163
|
-
getConfigDir,
|
|
164
|
-
getStatePath,
|
|
165
|
-
getConfigPath,
|
|
166
|
-
loadConfigFile,
|
|
167
|
-
resolveServer,
|
|
168
|
-
loadConfig,
|
|
169
|
-
saveConfig,
|
|
170
|
-
loadDirectoryToken,
|
|
171
|
-
saveDirectoryToken,
|
|
172
|
-
recordTailHistory,
|
|
173
|
-
switchServer
|
|
174
|
-
};
|
package/dist/chunk-AR456MHY.js
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// src/lib/github.ts
|
|
4
|
-
function parseGitHubUrl(url) {
|
|
5
|
-
try {
|
|
6
|
-
const u = new URL(url);
|
|
7
|
-
if (u.hostname !== "github.com") return null;
|
|
8
|
-
const parts = u.pathname.replace(/^\/|\/$/g, "").split("/");
|
|
9
|
-
if (parts.length < 2) return null;
|
|
10
|
-
const owner = parts[0];
|
|
11
|
-
const repo = parts[1];
|
|
12
|
-
if (!owner || !repo) return null;
|
|
13
|
-
if (parts.length === 2) {
|
|
14
|
-
return { owner, repo, ref: "main", path: "" };
|
|
15
|
-
}
|
|
16
|
-
if (parts[2] === "tree" && parts.length >= 4) {
|
|
17
|
-
const ref = parts[3];
|
|
18
|
-
const subPath = parts.slice(4).join("/");
|
|
19
|
-
return { owner, repo, ref, path: subPath };
|
|
20
|
-
}
|
|
21
|
-
return null;
|
|
22
|
-
} catch {
|
|
23
|
-
return null;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export {
|
|
28
|
-
parseGitHubUrl
|
|
29
|
-
};
|
package/dist/chunk-VBGU2NST.js
DELETED
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
loadConfig,
|
|
4
|
-
loadConfigFile,
|
|
5
|
-
saveConfig
|
|
6
|
-
} from "./chunk-67ZRMVHO.js";
|
|
7
|
-
|
|
8
|
-
// src/lib/client.ts
|
|
9
|
-
import { ZooidClient } from "@zooid/sdk";
|
|
10
|
-
function createClient(token) {
|
|
11
|
-
const config = loadConfig();
|
|
12
|
-
const server = config.server;
|
|
13
|
-
if (!server) {
|
|
14
|
-
throw new Error(
|
|
15
|
-
"No server configured. Run: npx zooid config set server <url>"
|
|
16
|
-
);
|
|
17
|
-
}
|
|
18
|
-
return new ZooidClient({ server, token: token ?? config.admin_token });
|
|
19
|
-
}
|
|
20
|
-
function getChannelToken(channelTokens, tokenType) {
|
|
21
|
-
if (!channelTokens) return void 0;
|
|
22
|
-
if (channelTokens.token) return channelTokens.token;
|
|
23
|
-
if (tokenType === "publish") return channelTokens.publish_token;
|
|
24
|
-
if (tokenType === "subscribe") return channelTokens.subscribe_token;
|
|
25
|
-
return channelTokens.publish_token ?? channelTokens.subscribe_token;
|
|
26
|
-
}
|
|
27
|
-
function createChannelClient(channelId, tokenType) {
|
|
28
|
-
const config = loadConfig();
|
|
29
|
-
const server = config.server;
|
|
30
|
-
if (!server) {
|
|
31
|
-
throw new Error(
|
|
32
|
-
"No server configured. Run: npx zooid config set server <url>"
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
|
-
const channelToken = getChannelToken(config.channels?.[channelId], tokenType);
|
|
36
|
-
return new ZooidClient({ server, token: channelToken ?? config.admin_token });
|
|
37
|
-
}
|
|
38
|
-
var createPublishClient = (channelId) => createChannelClient(channelId, "publish");
|
|
39
|
-
var createSubscribeClient = (channelId) => createChannelClient(channelId, "subscribe");
|
|
40
|
-
var PRIVATE_HOST_RE = /^(localhost|127\.\d+\.\d+\.\d+|10\.\d+\.\d+\.\d+|192\.168\.\d+\.\d+|172\.(1[6-9]|2\d|3[01])\.\d+\.\d+)$/;
|
|
41
|
-
function normalizeServerUrl(url) {
|
|
42
|
-
let normalized = url.replace(/\/+$/, "");
|
|
43
|
-
try {
|
|
44
|
-
const parsed = new URL(normalized);
|
|
45
|
-
if (parsed.protocol === "http:" && !PRIVATE_HOST_RE.test(parsed.hostname)) {
|
|
46
|
-
normalized = normalized.replace(/^http:\/\//, "https://");
|
|
47
|
-
}
|
|
48
|
-
} catch {
|
|
49
|
-
}
|
|
50
|
-
return normalized;
|
|
51
|
-
}
|
|
52
|
-
function parseChannelUrl(channel) {
|
|
53
|
-
let raw = channel;
|
|
54
|
-
if (!raw.startsWith("http") && raw.includes("/")) {
|
|
55
|
-
if (PRIVATE_HOST_RE.test(raw.split(/[:/]/)[0])) {
|
|
56
|
-
raw = `http://${raw}`;
|
|
57
|
-
} else if (raw.includes(".")) {
|
|
58
|
-
raw = `https://${raw}`;
|
|
59
|
-
} else if (/^[^/]+:\d+\//.test(raw)) {
|
|
60
|
-
raw = `http://${raw}`;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
if (!raw.startsWith("http")) return null;
|
|
64
|
-
try {
|
|
65
|
-
const url = new URL(raw);
|
|
66
|
-
const channelsMatch = url.pathname.match(/^\/channels\/([^/]+)/);
|
|
67
|
-
if (channelsMatch) {
|
|
68
|
-
return { server: url.origin, channelId: channelsMatch[1] };
|
|
69
|
-
}
|
|
70
|
-
const segments = url.pathname.split("/").filter(Boolean);
|
|
71
|
-
if (segments.length === 1) {
|
|
72
|
-
return { server: url.origin, channelId: segments[0] };
|
|
73
|
-
}
|
|
74
|
-
} catch {
|
|
75
|
-
}
|
|
76
|
-
return null;
|
|
77
|
-
}
|
|
78
|
-
function resolveChannel(channel, opts) {
|
|
79
|
-
const parsed = parseChannelUrl(channel);
|
|
80
|
-
if (parsed) {
|
|
81
|
-
const { server: server2, channelId } = parsed;
|
|
82
|
-
let token2 = opts?.token;
|
|
83
|
-
let tokenSaved2 = false;
|
|
84
|
-
if (token2) {
|
|
85
|
-
saveConfig({ channels: { [channelId]: { token: token2 } } }, server2, {
|
|
86
|
-
setCurrent: false
|
|
87
|
-
});
|
|
88
|
-
tokenSaved2 = true;
|
|
89
|
-
}
|
|
90
|
-
if (!token2) {
|
|
91
|
-
const file = loadConfigFile();
|
|
92
|
-
const channelTokens = file.servers?.[server2]?.channels?.[channelId];
|
|
93
|
-
token2 = getChannelToken(channelTokens, opts?.tokenType);
|
|
94
|
-
}
|
|
95
|
-
return {
|
|
96
|
-
client: new ZooidClient({ server: server2, token: token2 }),
|
|
97
|
-
channelId,
|
|
98
|
-
server: server2,
|
|
99
|
-
tokenSaved: tokenSaved2
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
const config = loadConfig();
|
|
103
|
-
const server = config.server;
|
|
104
|
-
if (!server) {
|
|
105
|
-
throw new Error(
|
|
106
|
-
"No server configured. Run: npx zooid config set server <url>"
|
|
107
|
-
);
|
|
108
|
-
}
|
|
109
|
-
let token = opts?.token;
|
|
110
|
-
let tokenSaved = false;
|
|
111
|
-
if (token) {
|
|
112
|
-
saveConfig({ channels: { [channel]: { token } } });
|
|
113
|
-
tokenSaved = true;
|
|
114
|
-
}
|
|
115
|
-
if (!token) {
|
|
116
|
-
const channelToken = getChannelToken(
|
|
117
|
-
config.channels?.[channel],
|
|
118
|
-
opts?.tokenType
|
|
119
|
-
);
|
|
120
|
-
token = channelToken ?? config.admin_token;
|
|
121
|
-
}
|
|
122
|
-
return {
|
|
123
|
-
client: new ZooidClient({ server, token }),
|
|
124
|
-
channelId: channel,
|
|
125
|
-
server,
|
|
126
|
-
tokenSaved
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
export {
|
|
131
|
-
createClient,
|
|
132
|
-
createChannelClient,
|
|
133
|
-
createPublishClient,
|
|
134
|
-
createSubscribeClient,
|
|
135
|
-
PRIVATE_HOST_RE,
|
|
136
|
-
normalizeServerUrl,
|
|
137
|
-
parseChannelUrl,
|
|
138
|
-
resolveChannel
|
|
139
|
-
};
|
package/dist/client-4VMFEFDX.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
PRIVATE_HOST_RE,
|
|
4
|
-
createChannelClient,
|
|
5
|
-
createClient,
|
|
6
|
-
createPublishClient,
|
|
7
|
-
createSubscribeClient,
|
|
8
|
-
normalizeServerUrl,
|
|
9
|
-
parseChannelUrl,
|
|
10
|
-
resolveChannel
|
|
11
|
-
} from "./chunk-VBGU2NST.js";
|
|
12
|
-
import "./chunk-67ZRMVHO.js";
|
|
13
|
-
export {
|
|
14
|
-
PRIVATE_HOST_RE,
|
|
15
|
-
createChannelClient,
|
|
16
|
-
createClient,
|
|
17
|
-
createPublishClient,
|
|
18
|
-
createSubscribeClient,
|
|
19
|
-
normalizeServerUrl,
|
|
20
|
-
parseChannelUrl,
|
|
21
|
-
resolveChannel
|
|
22
|
-
};
|
package/dist/config-2KK5GX42.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
getConfigDir,
|
|
4
|
-
getConfigPath,
|
|
5
|
-
getStatePath,
|
|
6
|
-
loadConfig,
|
|
7
|
-
loadConfigFile,
|
|
8
|
-
loadDirectoryToken,
|
|
9
|
-
recordTailHistory,
|
|
10
|
-
resolveServer,
|
|
11
|
-
saveConfig,
|
|
12
|
-
saveDirectoryToken,
|
|
13
|
-
switchServer
|
|
14
|
-
} from "./chunk-67ZRMVHO.js";
|
|
15
|
-
export {
|
|
16
|
-
getConfigDir,
|
|
17
|
-
getConfigPath,
|
|
18
|
-
getStatePath,
|
|
19
|
-
loadConfig,
|
|
20
|
-
loadConfigFile,
|
|
21
|
-
loadDirectoryToken,
|
|
22
|
-
recordTailHistory,
|
|
23
|
-
resolveServer,
|
|
24
|
-
saveConfig,
|
|
25
|
-
saveDirectoryToken,
|
|
26
|
-
switchServer
|
|
27
|
-
};
|