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.
Files changed (49) hide show
  1. package/README.i18n.yaml +6 -6
  2. package/README.md +25 -10
  3. package/README.zh.md +25 -10
  4. package/THIRD_PARTY_NOTICES.md +10019 -187
  5. package/dist/index.html +1 -1
  6. package/dist/plugins/@deepseek-ai/dsh-client-locale/client.js +7 -7
  7. package/dist/plugins/@deepseek-ai/dsh-client-ui-agent-preset/client.js +61 -61
  8. package/dist/plugins/@deepseek-ai/dsh-client-ui-commands/client.js +14 -14
  9. package/dist/plugins/@deepseek-ai/dsh-client-ui-conversation/client.js +287 -287
  10. package/dist/plugins/@deepseek-ai/dsh-client-ui-deliverables/client.js +10 -10
  11. package/dist/plugins/@deepseek-ai/dsh-client-ui-input-trigger/client.js +11 -11
  12. package/dist/plugins/@deepseek-ai/dsh-client-ui-jobs/client.js +15 -15
  13. package/dist/plugins/@deepseek-ai/dsh-client-ui-layout/client.js +8 -8
  14. package/dist/plugins/@deepseek-ai/dsh-client-ui-model-selection/client.js +27 -27
  15. package/dist/plugins/@deepseek-ai/dsh-client-ui-permission-presets/client.js +8 -8
  16. package/dist/plugins/@deepseek-ai/dsh-client-ui-settings-general/client.js +31 -31
  17. package/dist/plugins/@deepseek-ai/dsh-client-ui-sidebar/client.js +23 -23
  18. package/dist/plugins/@deepseek-ai/dsh-client-ui-skill/client.js +19 -19
  19. package/dist/plugins/@deepseek-ai/dsh-client-ui-subagent/client.js +29 -29
  20. package/dist/plugins/@deepseek-ai/dsh-client-ui-theme/client.js +7 -7
  21. package/dist/plugins/@deepseek-ai/dsh-client-ui-tool/client.js +64 -64
  22. package/dist/plugins/@deepseek-ai/dsh-client-ui-trajectory/client.js +183 -183
  23. package/dist/plugins/@deepseek-ai/dsh-client-ui-user-questions/client.js +44 -44
  24. package/dist/plugins/@deepseek-ai/dsh-client-ui-workflow-run/client.js +24 -24
  25. package/dist/plugins/@deepseek-ai/dsh-client-ui-workspace/client.js +79 -79
  26. package/dist/plugins/dsh-edge-client-ui/client.js +68 -53
  27. package/package.json +22 -22
  28. package/scripts/activation.d.mts +34 -0
  29. package/scripts/activation.mjs +133 -0
  30. package/scripts/cli.d.mts +9 -0
  31. package/scripts/cli.mjs +66 -3
  32. package/scripts/dev.mjs +38 -0
  33. package/scripts/install.d.mts +8 -0
  34. package/scripts/install.mjs +95 -4
  35. package/scripts/legal-files.d.mts +23 -0
  36. package/scripts/legal-files.mjs +222 -40
  37. package/scripts/pnpm-invocation.d.mts +14 -0
  38. package/scripts/pnpm-invocation.mjs +25 -0
  39. package/scripts/publish.d.mts +25 -0
  40. package/scripts/publish.mjs +145 -0
  41. package/scripts/smoke-installed.mjs +27 -22
  42. package/scripts/verify-packed.mjs +8 -0
  43. package/scripts/wrangler-config-core.mjs +107 -0
  44. package/scripts/wrangler-config.d.mts +2 -0
  45. package/scripts/wrangler-config.mjs +9 -78
  46. package/worker/direct/index.js +1136 -1139
  47. package/worker/isolated/index.js +872 -875
  48. package/scripts/assemble-web.mjs +0 -230
  49. 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 configFile = join(directory, 'wrangler.json')
23
- await writePrebuiltModeWranglerConfig('direct', configFile)
24
- worker = await unstable_dev(workerArtifactPath('direct'), {
25
- config: configFile,
26
- vars: {
27
- DEEPSEEK_API_KEY: 'packed-installer-test-key',
28
- DSH_EDGE_ACCESS_KEY: ACCESS_KEY,
29
- },
30
- logLevel: 'error',
31
- experimental: {
32
- disableExperimentalWarning: true,
33
- showInteractiveDevSession: false,
34
- watch: false,
35
- },
36
- })
37
- const response = await worker.fetch('http://dsh-edge.test/api/health')
38
- assert.equal(response.status, 200)
39
- const health = await response.json()
40
- assert.equal(health.service, 'dsh-edge')
41
- assert.equal(health.shell, 'just-bash-direct')
42
- assert.equal(health.deploymentId, `dsh-edge@${edgePackage.version}/direct`)
43
- process.stdout.write('Installed direct Worker artifact started successfully.\n')
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,7 +1,9 @@
1
1
  import type { RuntimeMode } from './install.mjs'
2
2
 
3
3
  export interface WranglerConfigOptions {
4
+ aliases?: Record<string, string>
4
5
  appDirectory?: string
6
+ assetsDirectory?: string
5
7
  sourceConfigPath?: string
6
8
  }
7
9
 
@@ -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
- const DIRECT_SHELL_CORE = '@cloudflare/computer/shell/core'
9
- const DIRECT_SHELL_CORE_REPLACEMENT = 'src/direct-shell-core-empty.ts'
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
- const config = structuredClone(parsed)
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
- const config = structuredClone(parsed)
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
- }