@tothalex/nulljs 0.0.48 ā 0.0.54
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/package.json +25 -32
- package/scripts/install-server.js +0 -199
- package/src/commands/api.ts +0 -16
- package/src/commands/auth.ts +0 -54
- package/src/commands/create.ts +0 -43
- package/src/commands/deploy.ts +0 -160
- package/src/commands/dev/function/index.ts +0 -221
- package/src/commands/dev/function/utils.ts +0 -99
- package/src/commands/dev/index.tsx +0 -126
- package/src/commands/dev/logging-manager.ts +0 -87
- package/src/commands/dev/server/index.ts +0 -48
- package/src/commands/dev/server/utils.ts +0 -37
- package/src/commands/dev/ui/components/scroll-area.tsx +0 -141
- package/src/commands/dev/ui/components/tab-bar.tsx +0 -67
- package/src/commands/dev/ui/index.tsx +0 -71
- package/src/commands/dev/ui/logging-context.tsx +0 -76
- package/src/commands/dev/ui/tabs/functions-tab.tsx +0 -35
- package/src/commands/dev/ui/tabs/server-tab.tsx +0 -36
- package/src/commands/dev/ui/tabs/vite-tab.tsx +0 -35
- package/src/commands/dev/ui/use-logging.tsx +0 -34
- package/src/commands/dev/vite/index.ts +0 -54
- package/src/commands/dev/vite/utils.ts +0 -71
- package/src/commands/host.ts +0 -339
- package/src/commands/index.ts +0 -8
- package/src/commands/profile.ts +0 -189
- package/src/commands/secret.ts +0 -79
- package/src/index.ts +0 -346
- package/src/lib/api.ts +0 -189
- package/src/lib/bundle/external.ts +0 -23
- package/src/lib/bundle/function/index.ts +0 -46
- package/src/lib/bundle/index.ts +0 -2
- package/src/lib/bundle/react/index.ts +0 -2
- package/src/lib/bundle/react/spa.ts +0 -77
- package/src/lib/bundle/react/ssr/client.ts +0 -93
- package/src/lib/bundle/react/ssr/config.ts +0 -77
- package/src/lib/bundle/react/ssr/index.ts +0 -4
- package/src/lib/bundle/react/ssr/props.ts +0 -71
- package/src/lib/bundle/react/ssr/server.ts +0 -83
- package/src/lib/bundle/types.ts +0 -4
- package/src/lib/config.ts +0 -347
- package/src/lib/deployment.ts +0 -244
- package/src/lib/update-server.ts +0 -262
package/src/index.ts
DELETED
|
@@ -1,346 +0,0 @@
|
|
|
1
|
-
#! /usr/bin/env bun
|
|
2
|
-
|
|
3
|
-
import yargs from 'yargs'
|
|
4
|
-
import { hideBin } from 'yargs/helpers'
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
deploy,
|
|
8
|
-
auth,
|
|
9
|
-
createSecret,
|
|
10
|
-
listSecretKeys,
|
|
11
|
-
setApiUrl,
|
|
12
|
-
create,
|
|
13
|
-
createSecretsFromFile,
|
|
14
|
-
dev,
|
|
15
|
-
host,
|
|
16
|
-
unhost,
|
|
17
|
-
listProfilesCommand,
|
|
18
|
-
createProfile,
|
|
19
|
-
useProfile,
|
|
20
|
-
showProfile,
|
|
21
|
-
removeProfile,
|
|
22
|
-
copyProfile
|
|
23
|
-
} from './commands'
|
|
24
|
-
import chalk from 'chalk'
|
|
25
|
-
import { getActiveProfile, listProfiles, profileExists, loadProfile } from './lib/config'
|
|
26
|
-
|
|
27
|
-
const showWelcome = () => {
|
|
28
|
-
console.log(chalk.bold.blue('\nš NullJS CLI'))
|
|
29
|
-
console.log(chalk.gray('ā'.repeat(40)))
|
|
30
|
-
|
|
31
|
-
// Show current profile
|
|
32
|
-
const activeProfile = getActiveProfile()
|
|
33
|
-
if (activeProfile && profileExists(activeProfile)) {
|
|
34
|
-
const config = loadProfile(activeProfile)
|
|
35
|
-
console.log(chalk.bold(`\nš Current Profile: ${chalk.green(activeProfile)}`))
|
|
36
|
-
|
|
37
|
-
if (config.api) {
|
|
38
|
-
console.log(` ${chalk.blue('API URL:')} ${config.api}`)
|
|
39
|
-
} else {
|
|
40
|
-
console.log(` ${chalk.blue('API URL:')} ${chalk.red('(not configured)')}`)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
if (config.key?.public && config.key?.private) {
|
|
44
|
-
console.log(` ${chalk.blue('Authentication:')} ${chalk.green('ā configured')}`)
|
|
45
|
-
} else {
|
|
46
|
-
console.log(` ${chalk.blue('Authentication:')} ${chalk.red('ā not configured')}`)
|
|
47
|
-
}
|
|
48
|
-
} else {
|
|
49
|
-
console.log(chalk.yellow('\nā ļø No active profile selected'))
|
|
50
|
-
const profiles = listProfiles()
|
|
51
|
-
if (profiles.length > 0) {
|
|
52
|
-
console.log(chalk.gray(' Available profiles:'))
|
|
53
|
-
profiles.forEach((profile) => console.log(chalk.gray(` - ${profile}`)))
|
|
54
|
-
console.log(chalk.gray(' Switch to a profile: nulljs profile use <name>'))
|
|
55
|
-
} else {
|
|
56
|
-
console.log(chalk.gray(' Create your first profile: nulljs profile create <name>'))
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Show available commands
|
|
61
|
-
console.log(chalk.bold('\nš Available Commands:'))
|
|
62
|
-
console.log(chalk.blue(' nulljs create <name>') + chalk.gray(' Create a new project'))
|
|
63
|
-
console.log(chalk.blue(' nulljs deploy [path]') + chalk.gray(' Deploy your project'))
|
|
64
|
-
console.log(chalk.blue(' nulljs dev [path]') + chalk.gray(' Start development mode'))
|
|
65
|
-
console.log(
|
|
66
|
-
chalk.blue(' nulljs auth') + chalk.gray(' Generate authentication keys')
|
|
67
|
-
)
|
|
68
|
-
console.log(chalk.blue(' nulljs config set-api <url>') + chalk.gray(' Set API URL'))
|
|
69
|
-
console.log(chalk.blue(' nulljs secret <action>') + chalk.gray(' Manage secrets'))
|
|
70
|
-
console.log(chalk.blue(' nulljs profile <action>') + chalk.gray(' Manage profiles'))
|
|
71
|
-
console.log(
|
|
72
|
-
chalk.blue(' nulljs host [cloud-path]') + chalk.gray(' Set up production hosting')
|
|
73
|
-
)
|
|
74
|
-
console.log(
|
|
75
|
-
chalk.blue(' nulljs host --update') + chalk.gray(' Update server binary and restart service')
|
|
76
|
-
)
|
|
77
|
-
console.log(chalk.blue(' nulljs unhost') + chalk.gray(' Remove production hosting'))
|
|
78
|
-
|
|
79
|
-
console.log(chalk.gray('\nš” Quick Start:'))
|
|
80
|
-
if (!activeProfile) {
|
|
81
|
-
console.log(chalk.gray(' 1. nulljs profile create my-profile'))
|
|
82
|
-
console.log(chalk.gray(' 2. nulljs auth'))
|
|
83
|
-
console.log(chalk.gray(' 3. nulljs config set-api <your-api-url>'))
|
|
84
|
-
console.log(chalk.gray(' 4. nulljs create my-project'))
|
|
85
|
-
} else {
|
|
86
|
-
console.log(chalk.gray(' ⢠nulljs create my-project (create a new project)'))
|
|
87
|
-
console.log(chalk.gray(' ⢠nulljs dev (start development)'))
|
|
88
|
-
console.log(chalk.gray(' ⢠nulljs deploy (deploy to production)'))
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
console.log(chalk.gray('\n Run "nulljs --help" for detailed usage\n'))
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const parser = yargs(hideBin(process.argv))
|
|
95
|
-
.scriptName('nulljs')
|
|
96
|
-
.usage('Usage: $0 <command> [options]')
|
|
97
|
-
.command(
|
|
98
|
-
'create <name>',
|
|
99
|
-
'Create a nulljs project with <name>',
|
|
100
|
-
(yargs) =>
|
|
101
|
-
yargs.positional('name', {
|
|
102
|
-
description: 'Project name',
|
|
103
|
-
type: 'string',
|
|
104
|
-
demandOption: true
|
|
105
|
-
}),
|
|
106
|
-
(argv) => create(argv.name)
|
|
107
|
-
)
|
|
108
|
-
.command(
|
|
109
|
-
'deploy [path]',
|
|
110
|
-
'Deploy a project to the platform',
|
|
111
|
-
(yargs) =>
|
|
112
|
-
yargs.positional('path', {
|
|
113
|
-
description:
|
|
114
|
-
'Path to the project directory or file (optional, defaults to current directory)',
|
|
115
|
-
type: 'string',
|
|
116
|
-
demandOption: false
|
|
117
|
-
}),
|
|
118
|
-
(argv) => deploy(argv.path)
|
|
119
|
-
)
|
|
120
|
-
.command(
|
|
121
|
-
'dev [path]',
|
|
122
|
-
'Start dev mode with Vite server and function watching',
|
|
123
|
-
(yargs) =>
|
|
124
|
-
yargs.positional('path', {
|
|
125
|
-
description: 'Path to the src directory (defaults to ./src)',
|
|
126
|
-
type: 'string',
|
|
127
|
-
default: './src'
|
|
128
|
-
}),
|
|
129
|
-
(argv) => dev(argv.path)
|
|
130
|
-
)
|
|
131
|
-
.command(
|
|
132
|
-
'host [cloud-path]',
|
|
133
|
-
'Set up production hosting with systemd service (Linux only)',
|
|
134
|
-
(yargs) =>
|
|
135
|
-
yargs
|
|
136
|
-
.positional('cloud-path', {
|
|
137
|
-
description: 'Path to cloud data directory (defaults to ~/.nulljs)',
|
|
138
|
-
type: 'string',
|
|
139
|
-
demandOption: false
|
|
140
|
-
})
|
|
141
|
-
.option('update', {
|
|
142
|
-
description: 'Update the server binary to the latest version and restart the service',
|
|
143
|
-
type: 'boolean',
|
|
144
|
-
default: false
|
|
145
|
-
}),
|
|
146
|
-
(argv) => host(argv['cloud-path'], { update: argv.update })
|
|
147
|
-
)
|
|
148
|
-
.command(
|
|
149
|
-
'unhost',
|
|
150
|
-
'Remove production hosting setup (Linux only)',
|
|
151
|
-
(yargs) =>
|
|
152
|
-
yargs.option('keep-data', {
|
|
153
|
-
description: 'Keep the cloud data directory',
|
|
154
|
-
type: 'boolean',
|
|
155
|
-
default: false
|
|
156
|
-
}),
|
|
157
|
-
(argv) => unhost({ keepData: argv['keep-data'] })
|
|
158
|
-
)
|
|
159
|
-
.command(
|
|
160
|
-
'auth',
|
|
161
|
-
'Generate or regenerate authentication keys',
|
|
162
|
-
(yargs) =>
|
|
163
|
-
yargs.option('profile', {
|
|
164
|
-
description: 'Save keys to a specific profile',
|
|
165
|
-
type: 'string',
|
|
166
|
-
demandOption: false
|
|
167
|
-
}),
|
|
168
|
-
(argv) => auth(argv.profile)
|
|
169
|
-
)
|
|
170
|
-
.command(
|
|
171
|
-
'config',
|
|
172
|
-
'Configuration management',
|
|
173
|
-
(yargs) =>
|
|
174
|
-
yargs
|
|
175
|
-
.command(
|
|
176
|
-
'set-api <url>',
|
|
177
|
-
'Set the API URL for the platform',
|
|
178
|
-
(yargs) =>
|
|
179
|
-
yargs
|
|
180
|
-
.positional('url', {
|
|
181
|
-
description: 'API URL to save in configuration',
|
|
182
|
-
type: 'string',
|
|
183
|
-
demandOption: true
|
|
184
|
-
})
|
|
185
|
-
.option('profile', {
|
|
186
|
-
description: 'Save to a specific profile',
|
|
187
|
-
type: 'string',
|
|
188
|
-
demandOption: false
|
|
189
|
-
}),
|
|
190
|
-
(argv) => setApiUrl(argv.url, argv.profile)
|
|
191
|
-
)
|
|
192
|
-
.demandCommand(1, 'You need to specify a config action'),
|
|
193
|
-
() => {}
|
|
194
|
-
)
|
|
195
|
-
.command(
|
|
196
|
-
'secret <action>',
|
|
197
|
-
'Secret management operations',
|
|
198
|
-
(yargs) =>
|
|
199
|
-
yargs
|
|
200
|
-
.command(
|
|
201
|
-
'create',
|
|
202
|
-
'Create a new secret',
|
|
203
|
-
(yargs) =>
|
|
204
|
-
yargs
|
|
205
|
-
.option('key', {
|
|
206
|
-
description: 'Key name for the secret',
|
|
207
|
-
type: 'string',
|
|
208
|
-
demandOption: false
|
|
209
|
-
})
|
|
210
|
-
.option('value', {
|
|
211
|
-
description: 'Value for the secret',
|
|
212
|
-
type: 'string',
|
|
213
|
-
demandOption: false
|
|
214
|
-
})
|
|
215
|
-
.option('file', {
|
|
216
|
-
description: 'Path to file containing the secret value',
|
|
217
|
-
type: 'string',
|
|
218
|
-
demandOption: false
|
|
219
|
-
})
|
|
220
|
-
.check((argv) => {
|
|
221
|
-
// Either file is provided OR both key and value are provided
|
|
222
|
-
if (argv.file && (argv.key || argv.value)) {
|
|
223
|
-
throw new Error('Cannot use --file with --key or --value options')
|
|
224
|
-
}
|
|
225
|
-
if (!argv.file && (!argv.key || !argv.value)) {
|
|
226
|
-
throw new Error('Must provide either --file OR both --key and --value')
|
|
227
|
-
}
|
|
228
|
-
return true
|
|
229
|
-
}),
|
|
230
|
-
(argv) => {
|
|
231
|
-
if (argv.file) {
|
|
232
|
-
createSecretsFromFile({
|
|
233
|
-
filePath: argv.file
|
|
234
|
-
})
|
|
235
|
-
} else {
|
|
236
|
-
createSecret({ key: argv.key!, value: argv.value! })
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
)
|
|
240
|
-
.command('list', 'List all available secret keys', {}, () => listSecretKeys())
|
|
241
|
-
.demandCommand(1, 'You need to specify a secret action'),
|
|
242
|
-
() => {}
|
|
243
|
-
)
|
|
244
|
-
.command(
|
|
245
|
-
'profile <action>',
|
|
246
|
-
'Profile management operations',
|
|
247
|
-
(yargs) =>
|
|
248
|
-
yargs
|
|
249
|
-
.command('list', 'List all available profiles', {}, () => listProfilesCommand())
|
|
250
|
-
.command(
|
|
251
|
-
'create <name>',
|
|
252
|
-
'Create a new profile',
|
|
253
|
-
(yargs) =>
|
|
254
|
-
yargs.positional('name', {
|
|
255
|
-
description: 'Profile name',
|
|
256
|
-
type: 'string',
|
|
257
|
-
demandOption: true
|
|
258
|
-
}),
|
|
259
|
-
(argv) => createProfile(argv.name)
|
|
260
|
-
)
|
|
261
|
-
.command(
|
|
262
|
-
'use <name>',
|
|
263
|
-
'Switch to a profile',
|
|
264
|
-
(yargs) =>
|
|
265
|
-
yargs.positional('name', {
|
|
266
|
-
description: 'Profile name to switch to',
|
|
267
|
-
type: 'string',
|
|
268
|
-
demandOption: true
|
|
269
|
-
}),
|
|
270
|
-
(argv) => useProfile(argv.name)
|
|
271
|
-
)
|
|
272
|
-
.command(
|
|
273
|
-
'show [name]',
|
|
274
|
-
'Show profile configuration',
|
|
275
|
-
(yargs) =>
|
|
276
|
-
yargs.positional('name', {
|
|
277
|
-
description: 'Profile name (defaults to active profile)',
|
|
278
|
-
type: 'string',
|
|
279
|
-
demandOption: false
|
|
280
|
-
}),
|
|
281
|
-
(argv) => showProfile(argv.name)
|
|
282
|
-
)
|
|
283
|
-
.command(
|
|
284
|
-
'remove <name>',
|
|
285
|
-
'Remove a profile',
|
|
286
|
-
(yargs) =>
|
|
287
|
-
yargs.positional('name', {
|
|
288
|
-
description: 'Profile name to remove',
|
|
289
|
-
type: 'string',
|
|
290
|
-
demandOption: true
|
|
291
|
-
}),
|
|
292
|
-
(argv) => removeProfile(argv.name)
|
|
293
|
-
)
|
|
294
|
-
.command(
|
|
295
|
-
'copy <from> <to>',
|
|
296
|
-
'Copy a profile to a new profile',
|
|
297
|
-
(yargs) =>
|
|
298
|
-
yargs
|
|
299
|
-
.positional('from', {
|
|
300
|
-
description: 'Source profile name',
|
|
301
|
-
type: 'string',
|
|
302
|
-
demandOption: true
|
|
303
|
-
})
|
|
304
|
-
.positional('to', {
|
|
305
|
-
description: 'Destination profile name',
|
|
306
|
-
type: 'string',
|
|
307
|
-
demandOption: true
|
|
308
|
-
}),
|
|
309
|
-
(argv) => copyProfile(argv.from, argv.to)
|
|
310
|
-
)
|
|
311
|
-
.demandCommand(1, 'You need to specify a profile action'),
|
|
312
|
-
() => {}
|
|
313
|
-
)
|
|
314
|
-
.help('h')
|
|
315
|
-
.alias('h', 'help')
|
|
316
|
-
.version('0.0.3')
|
|
317
|
-
.alias('v', 'version')
|
|
318
|
-
.example('$0 create my-project', 'Create project called my-project')
|
|
319
|
-
.example('$0 deploy ./my-project', 'Deploy a project from the current directory')
|
|
320
|
-
.example('$0 dev', 'Start dev mode for current project (./src)')
|
|
321
|
-
.example('$0 dev ./src', 'Start dev mode for specific src directory')
|
|
322
|
-
.example('$0 host', 'Set up production hosting with default cloud path (~/.nulljs)')
|
|
323
|
-
.example('$0 host /var/nulljs', 'Set up production hosting with custom cloud path')
|
|
324
|
-
.example('$0 host --update', 'Update server binary to latest version and restart service')
|
|
325
|
-
.example('$0 unhost', 'Remove production hosting (removes service and data)')
|
|
326
|
-
.example('$0 unhost --keep-data', 'Remove hosting but keep cloud data')
|
|
327
|
-
.example('$0 auth', 'Generate authentication keys')
|
|
328
|
-
.example('$0 config set-api https://api.example.com', 'Set the API URL')
|
|
329
|
-
.example('$0 secret create --key MY_KEY --value "secret value"', 'Create a new secret')
|
|
330
|
-
.example('$0 secret create --file secrets.json', 'Create secrets from a file')
|
|
331
|
-
.example('$0 profile list', 'List all available profiles')
|
|
332
|
-
.example('$0 profile create development', 'Create a new profile called development')
|
|
333
|
-
.example('$0 profile use production', 'Switch to the production profile')
|
|
334
|
-
.example('$0 profile show', 'Show the current active profile configuration')
|
|
335
|
-
.example('$0 profile copy development staging', 'Copy development profile to staging')
|
|
336
|
-
.epilogue('For more information, visit: https://your-docs-url.com')
|
|
337
|
-
|
|
338
|
-
// Parse arguments and show welcome screen if no command is provided
|
|
339
|
-
async function main() {
|
|
340
|
-
const argv = await parser.parse()
|
|
341
|
-
if (argv._.length === 0) {
|
|
342
|
-
showWelcome()
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
main().catch(console.error)
|
package/src/lib/api.ts
DELETED
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
import { lookup } from 'mime-types'
|
|
2
|
-
import type { Deployment } from './deployment'
|
|
3
|
-
import chalk from 'chalk'
|
|
4
|
-
import { API, loadPrivateKey } from './config'
|
|
5
|
-
|
|
6
|
-
const formDataToString = async (formData: FormData): Promise<string> => {
|
|
7
|
-
const entries: string[] = []
|
|
8
|
-
|
|
9
|
-
const sortedEntries: [string, FormDataEntryValue][] = Array.from(formData.entries()).sort(
|
|
10
|
-
([a], [b]) => a.localeCompare(b)
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
for (const [key, value] of sortedEntries) {
|
|
14
|
-
if (value instanceof Blob) {
|
|
15
|
-
entries.push(`${key}:${value.size}:${value.type}`)
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
return entries.join('|')
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export const createDeployment = async (deployment: Deployment, logger: any = console) => {
|
|
23
|
-
const form = new FormData()
|
|
24
|
-
|
|
25
|
-
if (deployment.type === 'react') {
|
|
26
|
-
for (const asset of deployment.assets) {
|
|
27
|
-
const mime = lookup(asset.fileName)
|
|
28
|
-
|
|
29
|
-
if (!mime) {
|
|
30
|
-
throw new Error(chalk.yellow(`Couldn't get the mime type for ${asset.fileName}`))
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const buffer = Buffer.from(asset.code)
|
|
34
|
-
const blob = new Blob([buffer], { type: mime })
|
|
35
|
-
|
|
36
|
-
form.append(asset.fileName, blob)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
logger.log(chalk.yellow('Deploying ') + chalk.bgYellow.black(deployment.name))
|
|
40
|
-
deployment.assets.forEach((asset) => {
|
|
41
|
-
logger.log(`-> ${chalk.blue(asset.fileName)}`)
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
const privateKey = await loadPrivateKey()
|
|
45
|
-
|
|
46
|
-
const sign = await crypto.subtle.sign(
|
|
47
|
-
'Ed25519',
|
|
48
|
-
privateKey,
|
|
49
|
-
Buffer.from(await formDataToString(form))
|
|
50
|
-
)
|
|
51
|
-
|
|
52
|
-
const signature = btoa(String.fromCharCode(...new Uint8Array(sign)))
|
|
53
|
-
|
|
54
|
-
const response = await fetch(`${API}/deployment/react`, {
|
|
55
|
-
headers: {
|
|
56
|
-
Authorization: signature
|
|
57
|
-
},
|
|
58
|
-
method: 'POST',
|
|
59
|
-
body: form
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
if (response.status !== 200) {
|
|
63
|
-
throw new Error(
|
|
64
|
-
`${chalk.red('Deployment failed')} ${chalk.bgRed.black(response.status)}: ${chalk.red(await response.text())}`
|
|
65
|
-
)
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
logger.log(chalk.green('Deployed ') + chalk.bgGreen.black(deployment.name))
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (deployment.type === 'function') {
|
|
72
|
-
const handler = deployment.assets.find((asset) => asset.fileName === 'handler.js')
|
|
73
|
-
|
|
74
|
-
if (!handler) {
|
|
75
|
-
throw new Error(chalk.yellow(`Handler not found for ${deployment.name}`))
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const mime = lookup(handler.fileName)
|
|
79
|
-
|
|
80
|
-
if (!mime) {
|
|
81
|
-
throw new Error(chalk.yellow(`Couldn't get the mime type for ${handler.fileName}`))
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const buffer = Buffer.from(handler.code)
|
|
85
|
-
const blob = new Blob([buffer], { type: mime })
|
|
86
|
-
|
|
87
|
-
form.append(handler.fileName, blob)
|
|
88
|
-
|
|
89
|
-
logger.log(chalk.yellow('Deploying ') + chalk.bgYellow.black(deployment.name))
|
|
90
|
-
logger.log(`-> ${chalk.blue(handler.fileName)}`)
|
|
91
|
-
|
|
92
|
-
const privateKey = await loadPrivateKey()
|
|
93
|
-
|
|
94
|
-
const sign = await crypto.subtle.sign(
|
|
95
|
-
'Ed25519',
|
|
96
|
-
privateKey,
|
|
97
|
-
Buffer.from(await formDataToString(form))
|
|
98
|
-
)
|
|
99
|
-
|
|
100
|
-
const signature = btoa(String.fromCharCode(...new Uint8Array(sign)))
|
|
101
|
-
|
|
102
|
-
const response = await fetch(`${API}/deployment`, {
|
|
103
|
-
headers: {
|
|
104
|
-
authorization: signature
|
|
105
|
-
},
|
|
106
|
-
method: 'POST',
|
|
107
|
-
body: form
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
if (response.status !== 200) {
|
|
111
|
-
throw new Error(
|
|
112
|
-
`${chalk.red('Deployment failed')} ${chalk.bgRed.black(response.status)}: ${chalk.red(await response.text())}`
|
|
113
|
-
)
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
logger.log(chalk.green('Deployed ') + chalk.bgGreen.black(deployment.name))
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const createSignatureHeader = async (props: { method: string; url: string; body?: string }) => {
|
|
121
|
-
const timestamp = new Date().toISOString()
|
|
122
|
-
const privateKey = await loadPrivateKey()
|
|
123
|
-
|
|
124
|
-
const raw = `${props.method}-${props.url}-${timestamp}${props.body ? '-' + props.body : ''}`
|
|
125
|
-
|
|
126
|
-
const sign = await crypto.subtle.sign('Ed25519', privateKey, Buffer.from(raw))
|
|
127
|
-
const signature = btoa(String.fromCharCode(...new Uint8Array(sign)))
|
|
128
|
-
|
|
129
|
-
const header: { authorization: string; 'x-time': string; 'x-body'?: string } = {
|
|
130
|
-
authorization: signature,
|
|
131
|
-
'x-time': timestamp
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (props.body) {
|
|
135
|
-
header['x-body'] = btoa(props.body)
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
return header
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
export const fetchSecretKeys = async (): Promise<string[]> => {
|
|
142
|
-
const path = `${API}/secrets`
|
|
143
|
-
|
|
144
|
-
const headers = await createSignatureHeader({
|
|
145
|
-
method: 'GET',
|
|
146
|
-
url: path
|
|
147
|
-
})
|
|
148
|
-
|
|
149
|
-
const response = await fetch(path, {
|
|
150
|
-
headers,
|
|
151
|
-
method: 'GET'
|
|
152
|
-
})
|
|
153
|
-
|
|
154
|
-
if (response.status !== 200) {
|
|
155
|
-
console.log(chalk.red('Request failed'))
|
|
156
|
-
console.log(`${chalk.bgRed.black(response.status)}: ${chalk.red(await response.text())}`)
|
|
157
|
-
return []
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
const data = await response.json()
|
|
161
|
-
|
|
162
|
-
return data
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
export const postSecret = async (props: { key: string; value: string }) => {
|
|
166
|
-
const path = `${API}/secrets`
|
|
167
|
-
const body = JSON.stringify(props)
|
|
168
|
-
|
|
169
|
-
const headers = await createSignatureHeader({
|
|
170
|
-
method: 'POST',
|
|
171
|
-
url: path,
|
|
172
|
-
body
|
|
173
|
-
})
|
|
174
|
-
|
|
175
|
-
const response = await fetch(path, {
|
|
176
|
-
method: 'POST',
|
|
177
|
-
headers: {
|
|
178
|
-
'Content-Type': 'application/json',
|
|
179
|
-
...headers
|
|
180
|
-
},
|
|
181
|
-
body
|
|
182
|
-
})
|
|
183
|
-
|
|
184
|
-
if (response.status !== 200) {
|
|
185
|
-
throw new Error(
|
|
186
|
-
`${chalk.red('Request failed')} ${chalk.bgRed.black(response.status)}: ${chalk.red(await response.text())}`
|
|
187
|
-
)
|
|
188
|
-
}
|
|
189
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
export const external = [
|
|
2
|
-
'cloud/cache',
|
|
3
|
-
'cloud/event',
|
|
4
|
-
'cloud',
|
|
5
|
-
'cloud/postgres',
|
|
6
|
-
'cloud/secret',
|
|
7
|
-
'cloud/uuid',
|
|
8
|
-
'assert',
|
|
9
|
-
'buffer',
|
|
10
|
-
'crypto',
|
|
11
|
-
'dns',
|
|
12
|
-
'events',
|
|
13
|
-
'net',
|
|
14
|
-
'os',
|
|
15
|
-
'process',
|
|
16
|
-
'stream/web',
|
|
17
|
-
'string_decoder',
|
|
18
|
-
'timers',
|
|
19
|
-
'tty',
|
|
20
|
-
'url',
|
|
21
|
-
'util',
|
|
22
|
-
'zlib'
|
|
23
|
-
]
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import type { InlineConfig, Plugin, UserConfig } from 'vite'
|
|
2
|
-
import { external } from '../external'
|
|
3
|
-
import type { PluginOptions } from '../types'
|
|
4
|
-
|
|
5
|
-
const jsFunction = ({ filePath }: PluginOptions): Plugin => {
|
|
6
|
-
return {
|
|
7
|
-
name: 'nulljs-function-plugin',
|
|
8
|
-
apply: 'build',
|
|
9
|
-
config: async (config: UserConfig, { command }) => {
|
|
10
|
-
if (command !== 'build') {
|
|
11
|
-
return config
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
return {
|
|
15
|
-
build: {
|
|
16
|
-
rollupOptions: {
|
|
17
|
-
input: {
|
|
18
|
-
handler: filePath
|
|
19
|
-
},
|
|
20
|
-
external,
|
|
21
|
-
output: {
|
|
22
|
-
// preserveModules: false,
|
|
23
|
-
entryFileNames: '[name].js'
|
|
24
|
-
},
|
|
25
|
-
preserveEntrySignatures: 'strict'
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export const functionConfig = (filePath: string): InlineConfig => {
|
|
34
|
-
return {
|
|
35
|
-
logLevel: 'error',
|
|
36
|
-
plugins: [
|
|
37
|
-
jsFunction({
|
|
38
|
-
filePath
|
|
39
|
-
})
|
|
40
|
-
],
|
|
41
|
-
build: {
|
|
42
|
-
outDir: '/tmp',
|
|
43
|
-
minify: false
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
package/src/lib/bundle/index.ts
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { basename, extname } from 'path'
|
|
2
|
-
import type { InlineConfig, Plugin, UserConfig } from 'vite'
|
|
3
|
-
import react from '@vitejs/plugin-react'
|
|
4
|
-
import tailwindcss from '@tailwindcss/vite'
|
|
5
|
-
|
|
6
|
-
import type { PluginOptions } from '../types'
|
|
7
|
-
|
|
8
|
-
const jsSpa = ({ filePath }: PluginOptions): Plugin => {
|
|
9
|
-
const entry = basename(filePath, extname(filePath))
|
|
10
|
-
const virtualPrefix = `virtual:ssr/${entry}.tsx`
|
|
11
|
-
|
|
12
|
-
return {
|
|
13
|
-
name: 'nulljs-ssr-client-plugin',
|
|
14
|
-
apply: 'build',
|
|
15
|
-
config: async (config: UserConfig, { command }) => {
|
|
16
|
-
if (command !== 'build') {
|
|
17
|
-
return config
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return {
|
|
21
|
-
build: {
|
|
22
|
-
rollupOptions: {
|
|
23
|
-
input: {
|
|
24
|
-
[entry]: virtualPrefix
|
|
25
|
-
},
|
|
26
|
-
external: ['cloud'],
|
|
27
|
-
output: {
|
|
28
|
-
entryFileNames: '[name].js'
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
},
|
|
32
|
-
plugins: [react()]
|
|
33
|
-
}
|
|
34
|
-
},
|
|
35
|
-
|
|
36
|
-
resolveId: (id: string) => {
|
|
37
|
-
if (id === virtualPrefix) {
|
|
38
|
-
return id
|
|
39
|
-
}
|
|
40
|
-
return null
|
|
41
|
-
},
|
|
42
|
-
|
|
43
|
-
load: (id: string) => {
|
|
44
|
-
if (id === virtualPrefix) {
|
|
45
|
-
const script = `
|
|
46
|
-
import { StrictMode } from 'react'
|
|
47
|
-
import { createRoot } from 'react-dom/client'
|
|
48
|
-
|
|
49
|
-
import { Page } from "${filePath.replace(/\\/g, '\\\\')}";
|
|
50
|
-
|
|
51
|
-
createRoot(document.getElementById('root')!).render(
|
|
52
|
-
<StrictMode>
|
|
53
|
-
<Page />
|
|
54
|
-
</StrictMode>
|
|
55
|
-
)`
|
|
56
|
-
|
|
57
|
-
return script
|
|
58
|
-
}
|
|
59
|
-
return null
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export const spaClientConfig = (filePath: string): InlineConfig => {
|
|
65
|
-
return {
|
|
66
|
-
logLevel: 'error',
|
|
67
|
-
plugins: [
|
|
68
|
-
jsSpa({
|
|
69
|
-
filePath
|
|
70
|
-
}),
|
|
71
|
-
tailwindcss()
|
|
72
|
-
],
|
|
73
|
-
build: {
|
|
74
|
-
outDir: '/tmp'
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|