neonctl 2.23.1 → 2.24.1
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.md +109 -18
- package/commands/auth.js +12 -0
- package/commands/branches.js +14 -6
- package/commands/bucket.js +368 -0
- package/commands/checkout.js +49 -61
- package/commands/config.js +249 -0
- package/commands/deploy.js +25 -0
- package/commands/dev.js +743 -0
- package/commands/env.js +121 -0
- package/commands/index.js +10 -0
- package/commands/link.js +76 -12
- package/config_format.js +66 -0
- package/dev/env.js +199 -0
- package/dev/functions.js +72 -0
- package/dev/inputs.js +63 -0
- package/dev/runtime.js +146 -0
- package/env_file.js +146 -0
- package/index.js +2 -0
- package/package.json +7 -2
- package/pkg.js +23 -1
- package/psql/command/cmd_format.js +12 -12
- package/psql/command/cmd_io.js +2 -2
- package/psql/command/cmd_restrict.js +8 -8
- package/psql/describe/formatters.js +1 -1
- package/psql/print/aligned.js +30 -30
- package/psql/print/json.js +5 -5
- package/psql/scanner/sql.js +4 -4
- package/storage_api.js +114 -0
- package/utils/branch_picker.js +88 -0
- package/utils/esbuild.js +8 -5
- package/utils/zip.js +1 -1
package/psql/print/aligned.js
CHANGED
|
@@ -16,28 +16,28 @@ import { formatNumericLocale } from './units.js';
|
|
|
16
16
|
// East-Asian wide / fullwidth ranges from Unicode Standard Annex #11.
|
|
17
17
|
// Trimmed to the ranges psql's `ucs_wcwidth` considers width 2.
|
|
18
18
|
const WIDE_RANGES = [
|
|
19
|
-
[0x1100, 0x115f],
|
|
20
|
-
[0x2329, 0x232a],
|
|
21
|
-
[0x2e80, 0x303e],
|
|
22
|
-
[0x3041, 0x33ff],
|
|
23
|
-
[0x3400, 0x4dbf],
|
|
24
|
-
[0x4e00, 0x9fff],
|
|
25
|
-
[0xa000, 0xa4cf],
|
|
26
|
-
[0xac00, 0xd7a3],
|
|
27
|
-
[0xf900, 0xfaff],
|
|
28
|
-
[0xfe10, 0xfe19],
|
|
29
|
-
[0xfe30, 0xfe6f],
|
|
30
|
-
[0xff00, 0xff60],
|
|
31
|
-
[0xffe0, 0xffe6],
|
|
32
|
-
[0x1f300, 0x1f64f],
|
|
33
|
-
[0x1f900, 0x1f9ff],
|
|
34
|
-
[0x20000, 0x2fffd],
|
|
19
|
+
[0x1100, 0x115f], // Hangul Jamo
|
|
20
|
+
[0x2329, 0x232a], // Angle brackets
|
|
21
|
+
[0x2e80, 0x303e], // CJK Radicals, Kangxi, ...
|
|
22
|
+
[0x3041, 0x33ff], // Hiragana, Katakana, Bopomofo, etc.
|
|
23
|
+
[0x3400, 0x4dbf], // CJK Unified Ideographs Extension A
|
|
24
|
+
[0x4e00, 0x9fff], // CJK Unified Ideographs
|
|
25
|
+
[0xa000, 0xa4cf], // Yi Syllables
|
|
26
|
+
[0xac00, 0xd7a3], // Hangul Syllables
|
|
27
|
+
[0xf900, 0xfaff], // CJK Compatibility Ideographs
|
|
28
|
+
[0xfe10, 0xfe19], // Vertical forms
|
|
29
|
+
[0xfe30, 0xfe6f], // CJK Compatibility Forms, Small Form Variants
|
|
30
|
+
[0xff00, 0xff60], // Fullwidth Forms
|
|
31
|
+
[0xffe0, 0xffe6], // Fullwidth signs
|
|
32
|
+
[0x1f300, 0x1f64f], // Misc Symbols & Pictographs, Emoticons
|
|
33
|
+
[0x1f900, 0x1f9ff], // Supplemental Symbols & Pictographs
|
|
34
|
+
[0x20000, 0x2fffd], // CJK Extension B..F
|
|
35
35
|
[0x30000, 0x3fffd], // CJK Extension G
|
|
36
36
|
];
|
|
37
37
|
// Combining marks: width 0. From Unicode general categories Mn/Me/Cf
|
|
38
38
|
// and the zero-width controls upstream treats as width 0.
|
|
39
39
|
const ZERO_RANGES = [
|
|
40
|
-
[0x0300, 0x036f],
|
|
40
|
+
[0x0300, 0x036f], // Combining Diacritical Marks
|
|
41
41
|
[0x0483, 0x0489],
|
|
42
42
|
[0x0591, 0x05bd],
|
|
43
43
|
[0x05bf, 0x05bf],
|
|
@@ -190,7 +190,7 @@ const ZERO_RANGES = [
|
|
|
190
190
|
[0x1cf8, 0x1cf9],
|
|
191
191
|
[0x1dc0, 0x1df9],
|
|
192
192
|
[0x1dfb, 0x1dff],
|
|
193
|
-
[0x200b, 0x200f],
|
|
193
|
+
[0x200b, 0x200f], // zero-width space, ZWNJ, ZWJ, LRM, RLM
|
|
194
194
|
[0x202a, 0x202e],
|
|
195
195
|
[0x2060, 0x206f],
|
|
196
196
|
[0x20d0, 0x20f0],
|
|
@@ -233,9 +233,9 @@ const ZERO_RANGES = [
|
|
|
233
233
|
[0xabe8, 0xabe8],
|
|
234
234
|
[0xabed, 0xabed],
|
|
235
235
|
[0xfb1e, 0xfb1e],
|
|
236
|
-
[0xfe00, 0xfe0f],
|
|
237
|
-
[0xfe20, 0xfe2f],
|
|
238
|
-
[0xfeff, 0xfeff],
|
|
236
|
+
[0xfe00, 0xfe0f], // variation selectors
|
|
237
|
+
[0xfe20, 0xfe2f], // combining half-marks
|
|
238
|
+
[0xfeff, 0xfeff], // BOM
|
|
239
239
|
[0xfff9, 0xfffb],
|
|
240
240
|
[0x101fd, 0x101fd],
|
|
241
241
|
[0x102e0, 0x102e0],
|
|
@@ -342,15 +342,15 @@ export const padToWidth = (text, width, alignment) => {
|
|
|
342
342
|
// divergences — we previously right-aligned interval & pg_lsn and omitted
|
|
343
343
|
// xid & cid).
|
|
344
344
|
const RIGHT_ALIGNED_OIDS = new Set([
|
|
345
|
-
20,
|
|
346
|
-
21,
|
|
347
|
-
23,
|
|
348
|
-
26,
|
|
349
|
-
28,
|
|
350
|
-
29,
|
|
351
|
-
700,
|
|
352
|
-
701,
|
|
353
|
-
790,
|
|
345
|
+
20, // int8
|
|
346
|
+
21, // int2
|
|
347
|
+
23, // int4
|
|
348
|
+
26, // oid
|
|
349
|
+
28, // xid
|
|
350
|
+
29, // cid
|
|
351
|
+
700, // float4
|
|
352
|
+
701, // float8
|
|
353
|
+
790, // money (cash)
|
|
354
354
|
1700, // numeric
|
|
355
355
|
]);
|
|
356
356
|
const isRightAlignedField = (oid) => RIGHT_ALIGNED_OIDS.has(oid);
|
package/psql/print/json.js
CHANGED
|
@@ -22,11 +22,11 @@
|
|
|
22
22
|
// PostgreSQL type OIDs for the numeric family that map cleanly to
|
|
23
23
|
// JSON numbers. NUMERIC is included but guarded by isFinite().
|
|
24
24
|
const NUMERIC_TYPE_OIDS = new Set([
|
|
25
|
-
21,
|
|
26
|
-
23,
|
|
27
|
-
20,
|
|
28
|
-
700,
|
|
29
|
-
701,
|
|
25
|
+
21, // INT2
|
|
26
|
+
23, // INT4
|
|
27
|
+
20, // INT8
|
|
28
|
+
700, // FLOAT4
|
|
29
|
+
701, // FLOAT8
|
|
30
30
|
1700, // NUMERIC
|
|
31
31
|
]);
|
|
32
32
|
export const jsonPrinter = {
|
package/psql/scanner/sql.js
CHANGED
|
@@ -142,10 +142,10 @@ const cloneState = (s) => ({
|
|
|
142
142
|
// blocks, dollar-quoted bodies). Identifier letters are stored lower-cased.
|
|
143
143
|
// ---------------------------------------------------------------------------
|
|
144
144
|
const KEYWORD_PREFIX_LETTERS = new Set([
|
|
145
|
-
'c',
|
|
146
|
-
'f',
|
|
147
|
-
'p',
|
|
148
|
-
'o',
|
|
145
|
+
'c', // create
|
|
146
|
+
'f', // function
|
|
147
|
+
'p', // procedure
|
|
148
|
+
'o', // or
|
|
149
149
|
'r', // replace
|
|
150
150
|
]);
|
|
151
151
|
const PREFIX_MATCHES_CREATE_FN_OR_PROC = (letters) => {
|
package/storage_api.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// Typed client helpers for the branch object-storage (bucket/object) API.
|
|
2
|
+
//
|
|
3
|
+
// These endpoints are part of the Neon object-storage surface (the "Buckets"
|
|
4
|
+
// tag in the public API). They are not yet exposed as typed methods on the
|
|
5
|
+
// published `@neondatabase/api-client` package, so the request/response types
|
|
6
|
+
// and the thin call helpers live here. They are implemented on top of the
|
|
7
|
+
// api-client's public `request()` method, which means they reuse the exact
|
|
8
|
+
// same authentication, base URL, headers and retry behaviour as every other
|
|
9
|
+
// neonctl command. When the generated client gains these methods, the call
|
|
10
|
+
// sites in `src/commands/bucket.ts` can switch over with no behavioural
|
|
11
|
+
// change.
|
|
12
|
+
const bucketsPath = (projectId, branchId) => `/projects/${encodeURIComponent(projectId)}/branches/${encodeURIComponent(branchId)}/buckets`;
|
|
13
|
+
const bucketPath = (projectId, branchId, bucketName) => `${bucketsPath(projectId, branchId)}/${encodeURIComponent(bucketName)}`;
|
|
14
|
+
/**
|
|
15
|
+
* Create a bucket on a branch.
|
|
16
|
+
*
|
|
17
|
+
* @request POST /projects/{project_id}/branches/{branch_id}/buckets
|
|
18
|
+
*/
|
|
19
|
+
export const createProjectBranchBucket = (apiClient, { projectId, branchId, name, accessLevel, }) => {
|
|
20
|
+
const body = { name };
|
|
21
|
+
// Omit access_level entirely so the server default (`private`) applies.
|
|
22
|
+
if (accessLevel !== undefined) {
|
|
23
|
+
body.access_level = accessLevel;
|
|
24
|
+
}
|
|
25
|
+
return apiClient.request({
|
|
26
|
+
path: bucketsPath(projectId, branchId),
|
|
27
|
+
method: 'POST',
|
|
28
|
+
body,
|
|
29
|
+
format: 'json',
|
|
30
|
+
secure: true,
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* List the buckets on a branch.
|
|
35
|
+
*
|
|
36
|
+
* @request GET /projects/{project_id}/branches/{branch_id}/buckets
|
|
37
|
+
*/
|
|
38
|
+
export const listProjectBranchBuckets = (apiClient, { projectId, branchId }) => apiClient.request({
|
|
39
|
+
path: bucketsPath(projectId, branchId),
|
|
40
|
+
method: 'GET',
|
|
41
|
+
format: 'json',
|
|
42
|
+
secure: true,
|
|
43
|
+
});
|
|
44
|
+
/**
|
|
45
|
+
* Delete a bucket from a branch.
|
|
46
|
+
*
|
|
47
|
+
* @request DELETE /projects/{project_id}/branches/{branch_id}/buckets/{bucket_name}
|
|
48
|
+
*/
|
|
49
|
+
export const deleteProjectBranchBucket = (apiClient, { projectId, branchId, bucketName, }) => apiClient.request({
|
|
50
|
+
path: bucketPath(projectId, branchId, bucketName),
|
|
51
|
+
method: 'DELETE',
|
|
52
|
+
secure: true,
|
|
53
|
+
});
|
|
54
|
+
/**
|
|
55
|
+
* List objects (and collapsed folders) in a bucket on a branch.
|
|
56
|
+
*
|
|
57
|
+
* @request GET /projects/{project_id}/branches/{branch_id}/buckets/{bucket_name}/objects
|
|
58
|
+
*/
|
|
59
|
+
export const listProjectBranchBucketObjects = (apiClient, { projectId, branchId, bucketName, ...query }) => apiClient.request({
|
|
60
|
+
path: `${bucketPath(projectId, branchId, bucketName)}/objects`,
|
|
61
|
+
method: 'GET',
|
|
62
|
+
query,
|
|
63
|
+
format: 'json',
|
|
64
|
+
secure: true,
|
|
65
|
+
});
|
|
66
|
+
/**
|
|
67
|
+
* Download an object's raw bytes from a bucket on a branch.
|
|
68
|
+
*
|
|
69
|
+
* The server returns the body as `application/octet-stream` with a
|
|
70
|
+
* `Content-Disposition: attachment` header; the helper requests the body as a
|
|
71
|
+
* stream (`responseType: 'stream'`), so `.data` is a Node `Readable` the caller
|
|
72
|
+
* can pipe straight to disk without buffering the whole object in memory. The
|
|
73
|
+
* response headers are returned alongside so the caller can derive a filename
|
|
74
|
+
* from `Content-Disposition`.
|
|
75
|
+
*
|
|
76
|
+
* The object key may contain `/`; it is percent-encoded into a single path
|
|
77
|
+
* segment so nested keys are routed to the `{object_key}` parameter.
|
|
78
|
+
*
|
|
79
|
+
* @request GET /projects/{project_id}/branches/{branch_id}/buckets/{bucket_name}/objects/{object_key}/download
|
|
80
|
+
*/
|
|
81
|
+
export const getProjectBranchBucketObject = (apiClient, { projectId, branchId, bucketName, objectKey, }) => apiClient.request({
|
|
82
|
+
path: `${bucketPath(projectId, branchId, bucketName)}/objects/${encodeURIComponent(objectKey)}/download`,
|
|
83
|
+
method: 'GET',
|
|
84
|
+
format: 'stream',
|
|
85
|
+
secure: true,
|
|
86
|
+
});
|
|
87
|
+
/**
|
|
88
|
+
* Delete an object from a bucket on a branch.
|
|
89
|
+
*
|
|
90
|
+
* The object key may contain `/`; it is percent-encoded into a single path
|
|
91
|
+
* segment so nested keys are routed to the `{object_key}` parameter.
|
|
92
|
+
*
|
|
93
|
+
* @request DELETE /projects/{project_id}/branches/{branch_id}/buckets/{bucket_name}/objects/{object_key}
|
|
94
|
+
*/
|
|
95
|
+
export const deleteProjectBranchBucketObject = (apiClient, { projectId, branchId, bucketName, objectKey, }) => apiClient.request({
|
|
96
|
+
path: `${bucketPath(projectId, branchId, bucketName)}/objects/${encodeURIComponent(objectKey)}`,
|
|
97
|
+
method: 'DELETE',
|
|
98
|
+
secure: true,
|
|
99
|
+
});
|
|
100
|
+
/**
|
|
101
|
+
* Delete every object under a key prefix (folder) in a bucket on a branch.
|
|
102
|
+
*
|
|
103
|
+
* `prefix` must be non-empty and end with `/`; every object on this branch
|
|
104
|
+
* whose key starts with the prefix is soft-deleted in a single call.
|
|
105
|
+
*
|
|
106
|
+
* @request DELETE /projects/{project_id}/branches/{branch_id}/buckets/{bucket_name}/objects-by-prefix
|
|
107
|
+
*/
|
|
108
|
+
export const deleteProjectBranchBucketObjectsByPrefix = (apiClient, { projectId, branchId, bucketName, prefix, }) => apiClient.request({
|
|
109
|
+
path: `${bucketPath(projectId, branchId, bucketName)}/objects-by-prefix`,
|
|
110
|
+
method: 'DELETE',
|
|
111
|
+
query: { prefix },
|
|
112
|
+
format: 'json',
|
|
113
|
+
secure: true,
|
|
114
|
+
});
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { EndpointType } from '@neondatabase/api-client';
|
|
2
|
+
import prompts from 'prompts';
|
|
3
|
+
import { retryOnLock } from '../api.js';
|
|
4
|
+
import { log } from '../log.js';
|
|
5
|
+
import { isCi } from '../env.js';
|
|
6
|
+
/** Sentinel `value` for the "create a new branch" choice (no branch id can collide). */
|
|
7
|
+
const CREATE_BRANCH_CHOICE = Symbol('create-branch');
|
|
8
|
+
/**
|
|
9
|
+
* Prompt the user to pick a branch from `branches`, with a "+ Create a new branch…" option
|
|
10
|
+
* pinned to the top (mirroring the project/org pickers). The default selection is the
|
|
11
|
+
* project's default branch (the create option sits at index 0, so the default index is
|
|
12
|
+
* offset by one).
|
|
13
|
+
*
|
|
14
|
+
* Throws `opts.nonInteractiveMessage` when there is no TTY (or in CI): the caller knows the
|
|
15
|
+
* right guidance for its command, so the message is supplied rather than hard-coded here.
|
|
16
|
+
*/
|
|
17
|
+
export const pickBranchInteractively = async (branches, opts) => {
|
|
18
|
+
if (isCi() || !process.stdout.isTTY) {
|
|
19
|
+
throw new Error(opts.nonInteractiveMessage);
|
|
20
|
+
}
|
|
21
|
+
const defaultBranchIndex = branches.findIndex((b) => b.default);
|
|
22
|
+
const initial = defaultBranchIndex >= 0 ? defaultBranchIndex + 1 : 0;
|
|
23
|
+
const { choice } = await prompts({
|
|
24
|
+
type: 'select',
|
|
25
|
+
name: 'choice',
|
|
26
|
+
message: opts.message,
|
|
27
|
+
choices: [
|
|
28
|
+
{ title: '+ Create a new branch…', value: CREATE_BRANCH_CHOICE },
|
|
29
|
+
...branches.map((b) => ({
|
|
30
|
+
title: `${b.default ? '✱ ' : ''}${b.name} (${b.id})`,
|
|
31
|
+
value: b.id,
|
|
32
|
+
})),
|
|
33
|
+
],
|
|
34
|
+
initial,
|
|
35
|
+
});
|
|
36
|
+
if (choice === undefined) {
|
|
37
|
+
throw new Error('Aborted: no branch selected.');
|
|
38
|
+
}
|
|
39
|
+
if (choice === CREATE_BRANCH_CHOICE) {
|
|
40
|
+
return { kind: 'create', name: await promptNewBranchName(branches) };
|
|
41
|
+
}
|
|
42
|
+
return { kind: 'existing', branchId: choice };
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Prompt for a new branch name, rejecting empty input and names already taken on the
|
|
46
|
+
* project (so we never silently select a different, pre-existing branch).
|
|
47
|
+
*/
|
|
48
|
+
export const promptNewBranchName = async (branches) => {
|
|
49
|
+
const existing = new Set(branches.map((b) => b.name));
|
|
50
|
+
const { name } = await prompts({
|
|
51
|
+
type: 'text',
|
|
52
|
+
name: 'name',
|
|
53
|
+
message: 'New branch name:',
|
|
54
|
+
validate: (value) => {
|
|
55
|
+
const trimmed = value.trim();
|
|
56
|
+
if (trimmed === '')
|
|
57
|
+
return 'Branch name cannot be empty.';
|
|
58
|
+
if (existing.has(trimmed))
|
|
59
|
+
return `A branch named "${trimmed}" already exists.`;
|
|
60
|
+
return true;
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
const trimmed = typeof name === 'string' ? name.trim() : '';
|
|
64
|
+
if (trimmed === '') {
|
|
65
|
+
throw new Error('Aborted: no branch name provided.');
|
|
66
|
+
}
|
|
67
|
+
return trimmed;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Create a branch with the same defaults as `neonctl branch create --name <name>`:
|
|
71
|
+
* branched from the project's default branch with a read-write compute endpoint. Returns
|
|
72
|
+
* the new branch id.
|
|
73
|
+
*/
|
|
74
|
+
export const createBranch = async (apiClient, projectId, name, branches) => {
|
|
75
|
+
const defaultBranch = branches.find((b) => b.default);
|
|
76
|
+
if (!defaultBranch) {
|
|
77
|
+
throw new Error('No default branch found');
|
|
78
|
+
}
|
|
79
|
+
const { data } = await retryOnLock(() => apiClient.createProjectBranch(projectId, {
|
|
80
|
+
branch: { name, parent_id: defaultBranch.id },
|
|
81
|
+
endpoints: [{ type: EndpointType.ReadWrite }],
|
|
82
|
+
}));
|
|
83
|
+
if (defaultBranch.protected) {
|
|
84
|
+
log.warning('The parent branch is protected; a unique role password has been generated for the new branch.');
|
|
85
|
+
}
|
|
86
|
+
log.info('Created branch %s (%s).', data.branch.name, data.branch.id);
|
|
87
|
+
return data.branch.id;
|
|
88
|
+
};
|
package/utils/esbuild.js
CHANGED
|
@@ -48,7 +48,10 @@ const bundleViaModule = async (source, loadEsbuild) => {
|
|
|
48
48
|
.build({
|
|
49
49
|
entryPoints: [source],
|
|
50
50
|
bundle: true,
|
|
51
|
-
|
|
51
|
+
// Emit `index.mjs` (not `out.js`): the Functions runtime imports the archive's entry
|
|
52
|
+
// by the conventional `index.{js,mjs}` name, and `.mjs` makes Node treat the ESM
|
|
53
|
+
// output as a module without needing a `package.json` type marker alongside it.
|
|
54
|
+
outfile: 'index.mjs',
|
|
52
55
|
write: false,
|
|
53
56
|
sourcemap: true,
|
|
54
57
|
minify: true,
|
|
@@ -61,7 +64,7 @@ const bundleViaModule = async (source, loadEsbuild) => {
|
|
|
61
64
|
throw new Error(`Failed to bundle function from ${source}. ${message(err)}`.trim());
|
|
62
65
|
});
|
|
63
66
|
const files = result.outputFiles ?? [];
|
|
64
|
-
// write:false with one entry always yields
|
|
67
|
+
// write:false with one entry always yields index.mjs + index.mjs.map; an empty set
|
|
65
68
|
// means the API contract changed under us — fail loud rather than ship an
|
|
66
69
|
// empty archive.
|
|
67
70
|
if (files.length === 0) {
|
|
@@ -105,7 +108,7 @@ const runEsbuild = (bin, args) => new Promise((resolve, reject) => {
|
|
|
105
108
|
const bundleViaBinary = async (source) => {
|
|
106
109
|
const bin = resolveEsbuild();
|
|
107
110
|
const outDir = mkdtempSync(join(tmpdir(), 'neon-fn-bundle-'));
|
|
108
|
-
const outfile = join(outDir, '
|
|
111
|
+
const outfile = join(outDir, 'index.mjs');
|
|
109
112
|
try {
|
|
110
113
|
const { code, stderr } = await runEsbuild(bin, [
|
|
111
114
|
source,
|
|
@@ -122,8 +125,8 @@ const bundleViaBinary = async (source) => {
|
|
|
122
125
|
throw new Error(`Failed to bundle function from ${source}. ${stderr.trim()}`.trim());
|
|
123
126
|
}
|
|
124
127
|
return {
|
|
125
|
-
'
|
|
126
|
-
'
|
|
128
|
+
'index.mjs': new Uint8Array(readFileSync(outfile)),
|
|
129
|
+
'index.mjs.map': new Uint8Array(readFileSync(`${outfile}.map`)),
|
|
127
130
|
};
|
|
128
131
|
}
|
|
129
132
|
finally {
|
package/utils/zip.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { zipSync } from 'fflate';
|
|
2
|
-
// Zip the esbuild output (
|
|
2
|
+
// Zip the esbuild output (index.mjs + index.mjs.map) into the archive the Functions
|
|
3
3
|
// deploy endpoint expects. Compression level 6 matches the previous bundler.
|
|
4
4
|
export const zipBundle = (entries) => zipSync(entries, { level: 6 });
|