dsh-edge 0.1.3 → 0.2.0-alpha.2
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/README.i18n.yaml +6 -6
- package/README.md +25 -10
- package/README.zh.md +25 -10
- package/THIRD_PARTY_NOTICES.md +10019 -187
- package/dist/index.html +1 -1
- package/dist/plugins/@deepseek-ai/dsh-client-locale/client.js +7 -7
- package/dist/plugins/@deepseek-ai/dsh-client-ui-agent-preset/client.js +61 -61
- package/dist/plugins/@deepseek-ai/dsh-client-ui-commands/client.js +14 -14
- package/dist/plugins/@deepseek-ai/dsh-client-ui-conversation/client.js +287 -287
- package/dist/plugins/@deepseek-ai/dsh-client-ui-deliverables/client.js +10 -10
- package/dist/plugins/@deepseek-ai/dsh-client-ui-input-trigger/client.js +11 -11
- package/dist/plugins/@deepseek-ai/dsh-client-ui-jobs/client.js +15 -15
- package/dist/plugins/@deepseek-ai/dsh-client-ui-layout/client.js +8 -8
- package/dist/plugins/@deepseek-ai/dsh-client-ui-model-selection/client.js +27 -27
- package/dist/plugins/@deepseek-ai/dsh-client-ui-permission-presets/client.js +8 -8
- package/dist/plugins/@deepseek-ai/dsh-client-ui-settings-general/client.js +31 -31
- package/dist/plugins/@deepseek-ai/dsh-client-ui-sidebar/client.js +23 -23
- package/dist/plugins/@deepseek-ai/dsh-client-ui-skill/client.js +19 -19
- package/dist/plugins/@deepseek-ai/dsh-client-ui-subagent/client.js +29 -29
- package/dist/plugins/@deepseek-ai/dsh-client-ui-theme/client.js +7 -7
- package/dist/plugins/@deepseek-ai/dsh-client-ui-tool/client.js +64 -64
- package/dist/plugins/@deepseek-ai/dsh-client-ui-trajectory/client.js +183 -183
- package/dist/plugins/@deepseek-ai/dsh-client-ui-user-questions/client.js +44 -44
- package/dist/plugins/@deepseek-ai/dsh-client-ui-workflow-run/client.js +24 -24
- package/dist/plugins/@deepseek-ai/dsh-client-ui-workspace/client.js +79 -79
- package/dist/plugins/dsh-edge-client-ui/client.js +68 -53
- package/package.json +22 -22
- package/scripts/activation.d.mts +34 -0
- package/scripts/activation.mjs +133 -0
- package/scripts/cli.d.mts +9 -0
- package/scripts/cli.mjs +66 -3
- package/scripts/dev.mjs +38 -0
- package/scripts/install.d.mts +8 -0
- package/scripts/install.mjs +95 -4
- package/scripts/legal-files.d.mts +23 -0
- package/scripts/legal-files.mjs +222 -40
- package/scripts/pnpm-invocation.d.mts +14 -0
- package/scripts/pnpm-invocation.mjs +25 -0
- package/scripts/publish.d.mts +25 -0
- package/scripts/publish.mjs +145 -0
- package/scripts/smoke-installed.mjs +27 -22
- package/scripts/verify-packed.mjs +8 -0
- package/scripts/wrangler-config-core.mjs +107 -0
- package/scripts/wrangler-config.d.mts +2 -0
- package/scripts/wrangler-config.mjs +9 -78
- package/worker/direct/index.js +1136 -1139
- package/worker/isolated/index.js +872 -875
- package/scripts/assemble-web.mjs +0 -230
- package/scripts/bundle.mjs +0 -86
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface PnpmInvocationOptions {
|
|
2
|
+
nodeExecutable?: string
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export interface PnpmInvocation {
|
|
6
|
+
command: string
|
|
7
|
+
args: string[]
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export declare function resolvePnpmInvocation(
|
|
11
|
+
pnpm: string | undefined,
|
|
12
|
+
args: string[],
|
|
13
|
+
options?: PnpmInvocationOptions,
|
|
14
|
+
): PnpmInvocation
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve a pnpm invocation across JavaScript-entry and standalone installs.
|
|
3
|
+
* @param {string | undefined} pnpm Package-manager path supplied by pnpm.
|
|
4
|
+
* @param {string[]} args Arguments to pass to pnpm.
|
|
5
|
+
* @param {{ nodeExecutable?: string }} [options] Resolution inputs.
|
|
6
|
+
* @returns {{ command: string, args: string[] }} Executable and arguments.
|
|
7
|
+
*/
|
|
8
|
+
export function resolvePnpmInvocation(pnpm, args, options = {}) {
|
|
9
|
+
if (pnpm === undefined || pnpm === '') {
|
|
10
|
+
throw new Error('Cannot run pnpm without a package-manager executable.')
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const nodeExecutable = options.nodeExecutable ?? process.execPath
|
|
14
|
+
if (/\.[cm]?js$/iu.test(pnpm)) {
|
|
15
|
+
return {
|
|
16
|
+
command: nodeExecutable,
|
|
17
|
+
args: [pnpm, ...args],
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
command: pnpm,
|
|
23
|
+
args,
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface NpmRuntime {
|
|
2
|
+
readonly platform: NodeJS.Platform
|
|
3
|
+
readonly nodeExecutable: string
|
|
4
|
+
readonly pathExists: (path: string) => boolean
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface NpmInvocation {
|
|
8
|
+
readonly command: string
|
|
9
|
+
readonly args: readonly string[]
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface PackedIdentity {
|
|
13
|
+
readonly name: 'dsh-edge'
|
|
14
|
+
readonly version: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function targetTag(version: string): 'latest' | 'next'
|
|
18
|
+
export function publishTag(version: string, current: string | undefined): 'latest' | 'next' | 'historical'
|
|
19
|
+
export function readPackedIdentity(tarball: string): PackedIdentity
|
|
20
|
+
export function assertReleaseIdentity(identity: PackedIdentity): PackedIdentity
|
|
21
|
+
export function resolveNpmInvocation(
|
|
22
|
+
args: readonly string[],
|
|
23
|
+
environment?: NodeJS.ProcessEnv,
|
|
24
|
+
runtime?: NpmRuntime,
|
|
25
|
+
): NpmInvocation
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto'
|
|
2
|
+
import { spawnSync } from 'node:child_process'
|
|
3
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
4
|
+
import { resolve, win32 } from 'node:path'
|
|
5
|
+
import { setTimeout as sleep } from 'node:timers/promises'
|
|
6
|
+
import { fileURLToPath } from 'node:url'
|
|
7
|
+
import { parseArgs } from 'node:util'
|
|
8
|
+
import { compareVersions, validate } from 'compare-versions'
|
|
9
|
+
import edgePackage from '../package.json' with { type: 'json' }
|
|
10
|
+
|
|
11
|
+
const TRANSIENT_CODES = ['E409', 'E429', 'E500', 'E502', 'E503', 'E504', 'ETIMEDOUT', 'ECONNRESET', 'EAI_AGAIN']
|
|
12
|
+
const PUBLISH_ATTEMPTS = 4
|
|
13
|
+
const RETRY_BASE_MS = 2_000
|
|
14
|
+
|
|
15
|
+
export function targetTag(version) {
|
|
16
|
+
return version.includes('-') ? 'next' : 'latest'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function publishTag(version, current) {
|
|
20
|
+
return current === undefined || compareVersions(version, current) > 0 ? targetTag(version) : 'historical'
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function resolveNpmInvocation(args, environment = process.env, runtime = {
|
|
24
|
+
platform: process.platform,
|
|
25
|
+
nodeExecutable: process.execPath,
|
|
26
|
+
pathExists: existsSync,
|
|
27
|
+
}) {
|
|
28
|
+
if (runtime.platform !== 'win32') return { command: 'npm', args }
|
|
29
|
+
const searchPath = Object.entries(environment).find(([name]) => name.toUpperCase() === 'PATH')?.[1]
|
|
30
|
+
if (searchPath === undefined) throw new Error('npm PATH is unavailable')
|
|
31
|
+
for (const rawDirectory of searchPath.split(win32.delimiter)) {
|
|
32
|
+
const directory = rawDirectory.startsWith('"') && rawDirectory.endsWith('"')
|
|
33
|
+
? rawDirectory.slice(1, -1)
|
|
34
|
+
: rawDirectory
|
|
35
|
+
const shim = win32.join(directory, 'npm.cmd')
|
|
36
|
+
const cli = win32.join(directory, 'node_modules', 'npm', 'bin', 'npm-cli.js')
|
|
37
|
+
if (runtime.pathExists(shim) && runtime.pathExists(cli)) {
|
|
38
|
+
return { command: runtime.nodeExecutable, args: [cli, ...args] }
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
throw new Error('npm.cmd with an adjacent npm-cli.js was not found on PATH')
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function npm(args, echo = false) {
|
|
45
|
+
const invocation = resolveNpmInvocation(args)
|
|
46
|
+
const result = spawnSync(invocation.command, [...invocation.args], {
|
|
47
|
+
encoding: 'utf8',
|
|
48
|
+
})
|
|
49
|
+
if (result.error !== undefined) throw result.error
|
|
50
|
+
if (echo) {
|
|
51
|
+
process.stdout.write(result.stdout ?? '')
|
|
52
|
+
process.stderr.write(result.stderr ?? '')
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
status: result.status,
|
|
56
|
+
stdout: result.stdout ?? '',
|
|
57
|
+
stderr: result.stderr ?? '',
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function registryIntegrity(name, version) {
|
|
62
|
+
const result = npm(['view', `${name}@${version}`, 'dist.integrity', '--json'])
|
|
63
|
+
if (result.status !== 0) {
|
|
64
|
+
const output = `${result.stdout}${result.stderr}`
|
|
65
|
+
if (output.includes('E404') || output.includes('404 Not Found')) return undefined
|
|
66
|
+
throw new Error(`npm view failed:\n${output}`)
|
|
67
|
+
}
|
|
68
|
+
const parsed = JSON.parse(result.stdout)
|
|
69
|
+
if (typeof parsed !== 'string' || parsed === '') throw new Error('registry returned invalid dist.integrity')
|
|
70
|
+
return parsed
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function registryTags(name) {
|
|
74
|
+
const result = npm(['view', name, 'dist-tags', '--json'])
|
|
75
|
+
if (result.status !== 0) {
|
|
76
|
+
const output = `${result.stdout}${result.stderr}`
|
|
77
|
+
if (output.includes('E404') || output.includes('404 Not Found')) return {}
|
|
78
|
+
throw new Error(`npm view failed:\n${output}`)
|
|
79
|
+
}
|
|
80
|
+
const parsed = JSON.parse(result.stdout)
|
|
81
|
+
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
82
|
+
throw new Error('registry returned invalid dist-tags')
|
|
83
|
+
}
|
|
84
|
+
return parsed
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function readPackedIdentity(tarball) {
|
|
88
|
+
const result = npm(['pack', tarball, '--dry-run', '--json', '--ignore-scripts', '--loglevel=error'])
|
|
89
|
+
if (result.status !== 0) {
|
|
90
|
+
throw new Error(`npm could not inspect the tarball:\n${result.stdout}${result.stderr}`)
|
|
91
|
+
}
|
|
92
|
+
const packed = JSON.parse(result.stdout)
|
|
93
|
+
const manifest = Array.isArray(packed) && packed.length === 1 ? packed[0] : undefined
|
|
94
|
+
if (manifest === null || typeof manifest !== 'object' || Array.isArray(manifest)) {
|
|
95
|
+
throw new Error('npm returned invalid packed metadata')
|
|
96
|
+
}
|
|
97
|
+
if (manifest.name !== edgePackage.name) {
|
|
98
|
+
throw new Error(`tarball contains ${String(manifest.name)} instead of ${edgePackage.name}`)
|
|
99
|
+
}
|
|
100
|
+
if (typeof manifest.version !== 'string' || !validate(manifest.version)) {
|
|
101
|
+
throw new Error('tarball contains an invalid package version')
|
|
102
|
+
}
|
|
103
|
+
if (manifest.id !== `${manifest.name}@${manifest.version}`) {
|
|
104
|
+
throw new Error('npm returned inconsistent packed identity')
|
|
105
|
+
}
|
|
106
|
+
return { name: manifest.name, version: manifest.version }
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function assertReleaseIdentity(identity) {
|
|
110
|
+
if (identity.version !== edgePackage.version) {
|
|
111
|
+
throw new Error(`tarball contains ${identity.name}@${identity.version}, but the checkout expects ${edgePackage.name}@${edgePackage.version}`)
|
|
112
|
+
}
|
|
113
|
+
return identity
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async function publish(tarball) {
|
|
117
|
+
const identity = assertReleaseIdentity(readPackedIdentity(tarball))
|
|
118
|
+
const localIntegrity = `sha512-${createHash('sha512').update(readFileSync(tarball)).digest('base64')}`
|
|
119
|
+
const existing = registryIntegrity(identity.name, identity.version)
|
|
120
|
+
if (existing !== undefined) {
|
|
121
|
+
if (existing !== localIntegrity) throw new Error(`${identity.name}@${identity.version} already has different content`)
|
|
122
|
+
console.log(`${identity.name}@${identity.version} is already published with the same integrity`)
|
|
123
|
+
return
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const desiredTag = targetTag(identity.version)
|
|
127
|
+
const tag = publishTag(identity.version, registryTags(identity.name)[desiredTag])
|
|
128
|
+
for (let attempt = 1; attempt <= PUBLISH_ATTEMPTS; attempt += 1) {
|
|
129
|
+
const result = npm(['publish', tarball, '--tag', tag, '--ignore-scripts'], true)
|
|
130
|
+
if (result.status === 0) return
|
|
131
|
+
if (registryIntegrity(identity.name, identity.version) === localIntegrity) return
|
|
132
|
+
const output = `${result.stdout}${result.stderr}`
|
|
133
|
+
if (attempt === PUBLISH_ATTEMPTS || !TRANSIENT_CODES.some(code => output.includes(`code ${code}`))) {
|
|
134
|
+
throw new Error(`npm publish failed:\n${output}`)
|
|
135
|
+
}
|
|
136
|
+
await sleep(RETRY_BASE_MS * 2 ** (attempt - 1))
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const invoked = process.argv[1] === fileURLToPath(import.meta.url)
|
|
141
|
+
if (invoked) {
|
|
142
|
+
const { values } = parseArgs({ options: { tarball: { type: 'string' } } })
|
|
143
|
+
if (values.tarball === undefined) throw new Error('Usage: publish.mjs --tarball <path>')
|
|
144
|
+
await publish(resolve(values.tarball))
|
|
145
|
+
}
|
|
@@ -19,28 +19,33 @@ try {
|
|
|
19
19
|
assert.equal((await stat(artifact)).isFile(), true, `missing ${mode} Worker artifact`)
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
22
|
+
for (const mode of ['direct', 'isolated']) {
|
|
23
|
+
const configFile = join(directory, `wrangler-${mode}.json`)
|
|
24
|
+
await writePrebuiltModeWranglerConfig(mode, configFile)
|
|
25
|
+
worker = await unstable_dev(workerArtifactPath(mode), {
|
|
26
|
+
config: configFile,
|
|
27
|
+
env: mode === 'direct' ? '' : 'isolated',
|
|
28
|
+
vars: {
|
|
29
|
+
DEEPSEEK_API_KEY: 'packed-installer-test-key',
|
|
30
|
+
DSH_EDGE_ACCESS_KEY: ACCESS_KEY,
|
|
31
|
+
},
|
|
32
|
+
logLevel: 'error',
|
|
33
|
+
experimental: {
|
|
34
|
+
disableExperimentalWarning: true,
|
|
35
|
+
showInteractiveDevSession: false,
|
|
36
|
+
watch: false,
|
|
37
|
+
},
|
|
38
|
+
})
|
|
39
|
+
const response = await worker.fetch('http://dsh-edge.test/api/health')
|
|
40
|
+
assert.equal(response.status, 200)
|
|
41
|
+
const health = await response.json()
|
|
42
|
+
assert.equal(health.service, 'dsh-edge')
|
|
43
|
+
assert.equal(health.shell, mode === 'direct' ? 'just-bash-direct' : 'just-bash-isolated')
|
|
44
|
+
assert.equal(health.deploymentId, `dsh-edge@${edgePackage.version}/${mode}`)
|
|
45
|
+
await worker.stop()
|
|
46
|
+
worker = undefined
|
|
47
|
+
process.stdout.write(`Installed ${mode} Worker artifact started successfully.\n`)
|
|
48
|
+
}
|
|
44
49
|
} finally {
|
|
45
50
|
await worker?.stop()
|
|
46
51
|
await rm(directory, { recursive: true, force: true })
|
|
@@ -77,6 +77,14 @@ export function verifyPacked(input) {
|
|
|
77
77
|
!noticesText.includes('# Third-Party Notices')
|
|
78
78
|
|| !noticesText.includes('not affiliated with or endorsed by DeepSeek')
|
|
79
79
|
|| !noticesText.includes('Copyright (c) 2026 DeepSeek')
|
|
80
|
+
|| !noticesText.includes('## Bundled component inventory')
|
|
81
|
+
|| !noticesText.includes('## Bundled license and notice texts')
|
|
82
|
+
|| !noticesText.includes('`@cloudflare/computer@0.2.0` | `MIT`')
|
|
83
|
+
|| !noticesText.includes('`diff@9.0.0` (`LICENSE`)')
|
|
84
|
+
|| !noticesText.includes('Copyright (c) 2009-2015, Kevin Decker')
|
|
85
|
+
|| !noticesText.includes('Redistributions in binary form must reproduce')
|
|
86
|
+
|| !noticesText.includes('Apache License')
|
|
87
|
+
|| noticesText.includes('apps/dsh-edge/standalone/pnpm-lock.yaml')
|
|
80
88
|
) {
|
|
81
89
|
throw new Error('packed dsh-edge is missing third-party notices')
|
|
82
90
|
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { resolve } from 'node:path'
|
|
2
|
+
import { fileURLToPath } from 'node:url'
|
|
3
|
+
|
|
4
|
+
const appDirectory = fileURLToPath(new URL('..', import.meta.url))
|
|
5
|
+
const DIRECT_SHELL_CORE = '@cloudflare/computer/shell/core'
|
|
6
|
+
const DIRECT_SHELL_CORE_REPLACEMENT = 'src/direct-shell-core-empty.ts'
|
|
7
|
+
const ISOLATED_DIRECT_SHELL = './direct-shell.ts'
|
|
8
|
+
const ISOLATED_DIRECT_SHELL_REPLACEMENT = 'src/isolated-direct-shell-unavailable.ts'
|
|
9
|
+
const RESERVED_ALIASES = new Set([DIRECT_SHELL_CORE, ISOLATED_DIRECT_SHELL])
|
|
10
|
+
const WORKER_ARTIFACTS = Object.freeze({
|
|
11
|
+
direct: 'worker/direct/index.js',
|
|
12
|
+
isolated: 'worker/isolated/index.js',
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
/** Render one mode-specific build configuration from an already parsed source object. */
|
|
16
|
+
export function renderParsedSourceModeWranglerConfig(mode, parsed, options = {}) {
|
|
17
|
+
requireRuntimeMode(mode)
|
|
18
|
+
requireSourceConfig(parsed)
|
|
19
|
+
const root = options.appDirectory ?? appDirectory
|
|
20
|
+
const aliases = options.aliases ?? {}
|
|
21
|
+
if (!isStringRecord(aliases)) {
|
|
22
|
+
throw new Error('Source-build aliases must map module names to paths.')
|
|
23
|
+
}
|
|
24
|
+
const reservedAlias = Object.keys(aliases).find(alias => RESERVED_ALIASES.has(alias))
|
|
25
|
+
if (reservedAlias !== undefined) {
|
|
26
|
+
throw new Error(`Source-build aliases reserve ${reservedAlias} for mode builds.`)
|
|
27
|
+
}
|
|
28
|
+
if (options.assetsDirectory !== undefined
|
|
29
|
+
&& (typeof options.assetsDirectory !== 'string' || options.assetsDirectory === '')) {
|
|
30
|
+
throw new Error('Source-build assetsDirectory must be a non-empty path.')
|
|
31
|
+
}
|
|
32
|
+
const config = structuredClone(parsed)
|
|
33
|
+
delete config.$schema
|
|
34
|
+
config.main = resolve(root, parsed.main)
|
|
35
|
+
config.assets.directory = options.assetsDirectory ?? resolve(root, parsed.assets.directory)
|
|
36
|
+
config.minify = true
|
|
37
|
+
if (mode === 'direct') {
|
|
38
|
+
config.alias = {
|
|
39
|
+
...config.alias,
|
|
40
|
+
...aliases,
|
|
41
|
+
[DIRECT_SHELL_CORE]: resolve(root, DIRECT_SHELL_CORE_REPLACEMENT),
|
|
42
|
+
}
|
|
43
|
+
} else {
|
|
44
|
+
config.alias = {
|
|
45
|
+
...config.alias,
|
|
46
|
+
...aliases,
|
|
47
|
+
[ISOLATED_DIRECT_SHELL]: resolve(root, ISOLATED_DIRECT_SHELL_REPLACEMENT),
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return `${JSON.stringify(config, undefined, 2)}\n`
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Render an upload configuration from an already parsed source object. */
|
|
54
|
+
export function renderParsedPrebuiltModeWranglerConfig(mode, parsed, options = {}) {
|
|
55
|
+
requireRuntimeMode(mode)
|
|
56
|
+
requireSourceConfig(parsed)
|
|
57
|
+
const root = options.appDirectory ?? appDirectory
|
|
58
|
+
const config = structuredClone(parsed)
|
|
59
|
+
delete config.$schema
|
|
60
|
+
delete config.alias
|
|
61
|
+
delete config.minify
|
|
62
|
+
config.main = resolve(root, WORKER_ARTIFACTS[mode])
|
|
63
|
+
config.assets.directory = resolve(root, parsed.assets.directory)
|
|
64
|
+
config.no_bundle = true
|
|
65
|
+
config.find_additional_modules = false
|
|
66
|
+
return `${JSON.stringify(config, undefined, 2)}\n`
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Return the released entrypoint for a runtime mode. */
|
|
70
|
+
export function workerArtifactPath(mode, options = {}) {
|
|
71
|
+
requireRuntimeMode(mode)
|
|
72
|
+
return resolve(options.appDirectory ?? appDirectory, WORKER_ARTIFACTS[mode])
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function requireSourceConfig(parsed) {
|
|
76
|
+
if (!isRecord(parsed)) throw new Error('wrangler.jsonc must contain one object.')
|
|
77
|
+
if (typeof parsed.main !== 'string' || parsed.main === '') {
|
|
78
|
+
throw new Error('wrangler.jsonc must declare a main entry point.')
|
|
79
|
+
}
|
|
80
|
+
if (!isRecord(parsed.assets) || typeof parsed.assets.directory !== 'string'
|
|
81
|
+
|| parsed.assets.directory === '') {
|
|
82
|
+
throw new Error('wrangler.jsonc must declare an assets directory.')
|
|
83
|
+
}
|
|
84
|
+
if (parsed.alias !== undefined && !isStringRecord(parsed.alias)) {
|
|
85
|
+
throw new Error('wrangler.jsonc aliases must map module names to paths.')
|
|
86
|
+
}
|
|
87
|
+
const reservedAlias = parsed.alias === undefined
|
|
88
|
+
? undefined
|
|
89
|
+
: Object.keys(parsed.alias).find(alias => RESERVED_ALIASES.has(alias))
|
|
90
|
+
if (reservedAlias !== undefined) {
|
|
91
|
+
throw new Error(`wrangler.jsonc reserves the ${reservedAlias} alias for mode builds.`)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function requireRuntimeMode(mode) {
|
|
96
|
+
if (mode !== 'direct' && mode !== 'isolated') {
|
|
97
|
+
throw new Error(`Unsupported runtime mode: ${String(mode)}`)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function isRecord(value) {
|
|
102
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function isStringRecord(value) {
|
|
106
|
+
return isRecord(value) && Object.values(value).every(entry => typeof entry === 'string')
|
|
107
|
+
}
|
|
@@ -1,58 +1,26 @@
|
|
|
1
1
|
import { readFile, writeFile } from 'node:fs/promises'
|
|
2
|
-
import { resolve } from 'node:path'
|
|
3
2
|
import { fileURLToPath } from 'node:url'
|
|
4
3
|
import { parse, printParseErrorCode } from 'jsonc-parser'
|
|
4
|
+
import {
|
|
5
|
+
renderParsedPrebuiltModeWranglerConfig,
|
|
6
|
+
renderParsedSourceModeWranglerConfig,
|
|
7
|
+
workerArtifactPath,
|
|
8
|
+
} from './wrangler-config-core.mjs'
|
|
5
9
|
|
|
6
|
-
const appDirectory = fileURLToPath(new URL('..', import.meta.url))
|
|
7
10
|
const sourceConfigPath = fileURLToPath(new URL('../wrangler.jsonc', import.meta.url))
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const ISOLATED_DIRECT_SHELL = './direct-shell.ts'
|
|
11
|
-
const ISOLATED_DIRECT_SHELL_REPLACEMENT = 'src/isolated-direct-shell-unavailable.ts'
|
|
12
|
-
const RESERVED_ALIASES = new Set([DIRECT_SHELL_CORE, ISOLATED_DIRECT_SHELL])
|
|
13
|
-
const WORKER_ARTIFACTS = Object.freeze({
|
|
14
|
-
direct: 'worker/direct/index.js',
|
|
15
|
-
isolated: 'worker/isolated/index.js',
|
|
16
|
-
})
|
|
11
|
+
|
|
12
|
+
export { workerArtifactPath }
|
|
17
13
|
|
|
18
14
|
/** Render one mode-specific build configuration from the repository source. */
|
|
19
15
|
export function renderSourceModeWranglerConfig(mode, source, options = {}) {
|
|
20
|
-
requireRuntimeMode(mode)
|
|
21
|
-
const root = options.appDirectory ?? appDirectory
|
|
22
16
|
const parsed = parseSourceConfig(source)
|
|
23
|
-
|
|
24
|
-
delete config.$schema
|
|
25
|
-
config.main = resolve(root, parsed.main)
|
|
26
|
-
config.assets.directory = resolve(root, parsed.assets.directory)
|
|
27
|
-
config.minify = true
|
|
28
|
-
if (mode === 'direct') {
|
|
29
|
-
config.alias = {
|
|
30
|
-
...config.alias,
|
|
31
|
-
[DIRECT_SHELL_CORE]: resolve(root, DIRECT_SHELL_CORE_REPLACEMENT),
|
|
32
|
-
}
|
|
33
|
-
} else {
|
|
34
|
-
config.alias = {
|
|
35
|
-
...config.alias,
|
|
36
|
-
[ISOLATED_DIRECT_SHELL]: resolve(root, ISOLATED_DIRECT_SHELL_REPLACEMENT),
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return `${JSON.stringify(config, undefined, 2)}\n`
|
|
17
|
+
return renderParsedSourceModeWranglerConfig(mode, parsed, options)
|
|
40
18
|
}
|
|
41
19
|
|
|
42
20
|
/** Render an upload configuration that deploys the released Worker without rebuilding it. */
|
|
43
21
|
export function renderPrebuiltModeWranglerConfig(mode, source, options = {}) {
|
|
44
|
-
requireRuntimeMode(mode)
|
|
45
|
-
const root = options.appDirectory ?? appDirectory
|
|
46
22
|
const parsed = parseSourceConfig(source)
|
|
47
|
-
|
|
48
|
-
delete config.$schema
|
|
49
|
-
delete config.alias
|
|
50
|
-
delete config.minify
|
|
51
|
-
config.main = resolve(root, WORKER_ARTIFACTS[mode])
|
|
52
|
-
config.assets.directory = resolve(root, parsed.assets.directory)
|
|
53
|
-
config.no_bundle = true
|
|
54
|
-
config.find_additional_modules = false
|
|
55
|
-
return `${JSON.stringify(config, undefined, 2)}\n`
|
|
23
|
+
return renderParsedPrebuiltModeWranglerConfig(mode, parsed, options)
|
|
56
24
|
}
|
|
57
25
|
|
|
58
26
|
/** Write the source configuration used to build one released Worker artifact. */
|
|
@@ -69,12 +37,6 @@ export async function writePrebuiltModeWranglerConfig(mode, destination, options
|
|
|
69
37
|
await writePrivateConfig(destination, rendered)
|
|
70
38
|
}
|
|
71
39
|
|
|
72
|
-
/** Return the released entrypoint for a runtime mode. */
|
|
73
|
-
export function workerArtifactPath(mode, options = {}) {
|
|
74
|
-
requireRuntimeMode(mode)
|
|
75
|
-
return resolve(options.appDirectory ?? appDirectory, WORKER_ARTIFACTS[mode])
|
|
76
|
-
}
|
|
77
|
-
|
|
78
40
|
async function writePrivateConfig(destination, rendered) {
|
|
79
41
|
await writeFile(destination, rendered, { encoding: 'utf8', mode: 0o600, flag: 'wx' })
|
|
80
42
|
}
|
|
@@ -90,36 +52,5 @@ function parseSourceConfig(source) {
|
|
|
90
52
|
const detail = errors.map(error => printParseErrorCode(error.error)).join(', ')
|
|
91
53
|
throw new Error(`Could not parse wrangler.jsonc: ${detail}.`)
|
|
92
54
|
}
|
|
93
|
-
if (!isRecord(parsed)) throw new Error('wrangler.jsonc must contain one object.')
|
|
94
|
-
if (typeof parsed.main !== 'string' || parsed.main === '') {
|
|
95
|
-
throw new Error('wrangler.jsonc must declare a main entry point.')
|
|
96
|
-
}
|
|
97
|
-
if (!isRecord(parsed.assets) || typeof parsed.assets.directory !== 'string'
|
|
98
|
-
|| parsed.assets.directory === '') {
|
|
99
|
-
throw new Error('wrangler.jsonc must declare an assets directory.')
|
|
100
|
-
}
|
|
101
|
-
if (parsed.alias !== undefined && !isStringRecord(parsed.alias)) {
|
|
102
|
-
throw new Error('wrangler.jsonc aliases must map module names to paths.')
|
|
103
|
-
}
|
|
104
|
-
const reservedAlias = parsed.alias === undefined
|
|
105
|
-
? undefined
|
|
106
|
-
: Object.keys(parsed.alias).find(alias => RESERVED_ALIASES.has(alias))
|
|
107
|
-
if (reservedAlias !== undefined) {
|
|
108
|
-
throw new Error(`wrangler.jsonc reserves the ${reservedAlias} alias for mode builds.`)
|
|
109
|
-
}
|
|
110
55
|
return parsed
|
|
111
56
|
}
|
|
112
|
-
|
|
113
|
-
function requireRuntimeMode(mode) {
|
|
114
|
-
if (mode !== 'direct' && mode !== 'isolated') {
|
|
115
|
-
throw new Error(`Unsupported runtime mode: ${String(mode)}`)
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function isRecord(value) {
|
|
120
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function isStringRecord(value) {
|
|
124
|
-
return isRecord(value) && Object.values(value).every(entry => typeof entry === 'string')
|
|
125
|
-
}
|