naro-cli 0.6.0 → 0.6.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 CHANGED
@@ -27,6 +27,20 @@ Values you register are encrypted at rest, and `naro env pull` /
27
27
  `naro env list --reveal` can read them back — your account credentials are
28
28
  effectively your env credentials.
29
29
 
30
+ ## Build cache
31
+
32
+ Deploys reuse the dependency and framework caches from your previous build,
33
+ which are stored per project and restored before the install step. The
34
+ archives are zstd-compressed, so caching needs `zstd` on your PATH:
35
+
36
+ ```bash
37
+ brew install zstd # macOS
38
+ apt install zstd # Debian/Ubuntu
39
+ ```
40
+
41
+ Without it deploys still work — they just build from scratch every time and
42
+ say so in the build log.
43
+
30
44
  ## Commands
31
45
 
32
46
  ```text
@@ -92,19 +92,43 @@ async function computeDepsCacheKey(checkoutDir, packageManager) {
92
92
  async function log(observer, scope, level, message) {
93
93
  await observer.onLog({ level, stage: STAGE[scope], message });
94
94
  }
95
+ async function reportUnavailable(scope, ports, observer) {
96
+ if (ports.report?.unavailable === true) return;
97
+ if (ports.report) ports.report.unavailable = true;
98
+ await log(
99
+ observer,
100
+ scope,
101
+ "WARN",
102
+ "Build cache is unavailable - this build runs uncached and saves nothing for the next one."
103
+ );
104
+ }
95
105
  async function restoreCache(args) {
96
106
  const { scope, key, targetDir, ports, observer } = args;
97
107
  const urls = await ports.transport.resolveUrls(scope, key);
98
- if (!urls) return false;
108
+ if (!urls) {
109
+ await reportUnavailable(scope, ports, observer);
110
+ return false;
111
+ }
99
112
  const response = await ports.fetch(urls.getUrl);
100
- if (response.status !== 200) return false;
113
+ if (response.status !== 200) {
114
+ await log(
115
+ observer,
116
+ scope,
117
+ "INFO",
118
+ `No ${scope} cache stored for ${key} yet.`
119
+ );
120
+ return false;
121
+ }
101
122
  const archive = ports.tmpFile(".tar.zst");
102
123
  await writeFile(archive, Buffer.from(await response.arrayBuffer()));
103
124
  await mkdir(targetDir, { recursive: true });
104
125
  await ports.runCommand(
105
126
  {
106
127
  command: "tar",
107
- args: ["--use-compress-program=zstd", "-xf", archive, "-C", targetDir],
128
+ // --zstd, not --use-compress-program: BSD tar (macOS, where the CLI
129
+ // builds) feeds the external program wrong on extract and dies with
130
+ // "Broken pipe". Both GNU tar 1.31+ and bsdtar 3.x accept --zstd.
131
+ args: ["--zstd", "-xf", archive, "-C", targetDir],
108
132
  cwd: targetDir,
109
133
  timeoutMs: 12e4,
110
134
  stage: STAGE[scope],
@@ -119,22 +143,26 @@ async function saveCache(args) {
119
143
  const { scope, key, sourceDir, ports, observer } = args;
120
144
  try {
121
145
  const info2 = await stat(sourceDir);
122
- if (!info2.isDirectory()) return;
146
+ if (!info2.isDirectory()) throw new Error("not a directory");
123
147
  } catch {
148
+ await log(
149
+ observer,
150
+ scope,
151
+ "WARN",
152
+ `Nothing to save for the ${scope} cache - the build wrote nothing to that directory.`
153
+ );
154
+ return;
155
+ }
156
+ const urls = await ports.transport.resolveUrls(scope, key);
157
+ if (!urls) {
158
+ await reportUnavailable(scope, ports, observer);
124
159
  return;
125
160
  }
126
161
  const archive = ports.tmpFile(".tar.zst");
127
162
  await ports.runCommand(
128
163
  {
129
164
  command: "tar",
130
- args: [
131
- "--use-compress-program=zstd",
132
- "-cf",
133
- archive,
134
- "-C",
135
- sourceDir,
136
- "."
137
- ],
165
+ args: ["--zstd", "-cf", archive, "-C", sourceDir, "."],
138
166
  cwd: sourceDir,
139
167
  timeoutMs: 12e4,
140
168
  stage: STAGE[scope],
@@ -142,8 +170,6 @@ async function saveCache(args) {
142
170
  },
143
171
  observer
144
172
  );
145
- const urls = await ports.transport.resolveUrls(scope, key);
146
- if (!urls) return;
147
173
  const body = await readFile(archive);
148
174
  const response = await ports.fetch(urls.putUrl, { method: "PUT", body });
149
175
  if (!response.ok) {
@@ -158,10 +184,45 @@ async function saveCache(args) {
158
184
  await log(observer, scope, "INFO", `Saved ${scope} cache (${key}).`);
159
185
  }
160
186
 
187
+ // ../../packages/build/src/cache-transport.ts
188
+ function createCacheTransport(options) {
189
+ return {
190
+ async resolveUrls(scope, key) {
191
+ try {
192
+ const url2 = new URL(
193
+ `/api/internal/deployments/${options.deploymentId}/cache/url`,
194
+ options.controlPlaneUrl
195
+ );
196
+ const response = await options.fetch(url2, {
197
+ method: "POST",
198
+ headers: {
199
+ "Content-Type": "application/json",
200
+ Authorization: `Bearer ${options.callbackToken}`
201
+ },
202
+ body: JSON.stringify({ scope, key })
203
+ });
204
+ if (!response.ok) return null;
205
+ const data = await response.json();
206
+ if (data.disabled === true) return null;
207
+ if (typeof data.getUrl !== "string" || typeof data.putUrl !== "string") {
208
+ return null;
209
+ }
210
+ return { getUrl: data.getUrl, putUrl: data.putUrl };
211
+ } catch {
212
+ return null;
213
+ }
214
+ }
215
+ };
216
+ }
217
+
161
218
  // ../../packages/build/src/hash.ts
162
219
  import { createHash as createHash2 } from "crypto";
163
- function hashAsset(data) {
164
- return createHash2("sha256").update(data).digest("hex").slice(0, 32);
220
+ var FALLBACK_CONTENT_TYPE = "application/octet-stream";
221
+ function assetContentType(value) {
222
+ return value || FALLBACK_CONTENT_TYPE;
223
+ }
224
+ function hashAsset(data, contentType2) {
225
+ return createHash2("sha256").update(assetContentType(contentType2)).update("\0").update(data).digest("hex").slice(0, 32);
165
226
  }
166
227
 
167
228
  // ../../packages/build/src/local-build-executor.ts
@@ -14853,20 +14914,28 @@ async function detectPackageManager(root) {
14853
14914
  if (await exists(join2(root, "pnpm-lock.yaml"))) {
14854
14915
  return {
14855
14916
  name: "pnpm",
14856
- install: ["pnpm", "install", "--frozen-lockfile"]
14917
+ install: ["pnpm", "install", "--frozen-lockfile"],
14918
+ cacheArgs: (dir) => ["--store-dir", dir]
14857
14919
  };
14858
14920
  }
14859
14921
  if (await exists(join2(root, "package-lock.json"))) {
14860
- return { name: "npm", install: ["npm", "ci"] };
14922
+ return {
14923
+ name: "npm",
14924
+ install: ["npm", "ci"],
14925
+ cacheArgs: (dir) => ["--cache", dir]
14926
+ };
14861
14927
  }
14862
14928
  if (await exists(join2(root, "yarn.lock"))) {
14929
+ const modern = await usesModernYarn(root);
14863
14930
  return {
14864
14931
  name: "yarn",
14865
14932
  install: [
14866
14933
  "yarn",
14867
14934
  "install",
14868
- await usesModernYarn(root) ? "--immutable" : "--frozen-lockfile"
14869
- ]
14935
+ modern ? "--immutable" : "--frozen-lockfile"
14936
+ ],
14937
+ // Berry takes no install-time cache flag; YARN_CACHE_FOLDER covers it.
14938
+ cacheArgs: (dir) => modern ? [] : ["--cache-folder", dir]
14870
14939
  };
14871
14940
  }
14872
14941
  throw new DeploymentError(
@@ -15595,8 +15664,27 @@ var defaultDependencies = {
15595
15664
  openNextCommand: "opennextjs-cloudflare",
15596
15665
  wranglerCommand: "wrangler",
15597
15666
  cacheFetch: (input, init) => fetch(input, init),
15598
- convertProxyToMiddleware
15667
+ convertProxyToMiddleware,
15668
+ commandAvailable: hasCommand
15599
15669
  };
15670
+ async function hasCommand(command) {
15671
+ try {
15672
+ await runCommand(
15673
+ {
15674
+ command,
15675
+ args: ["--version"],
15676
+ cwd: tmpdir(),
15677
+ timeoutMs: 1e4,
15678
+ stage: "CLONING",
15679
+ errorCode: "CLONE_FAILED"
15680
+ },
15681
+ { onLog: () => void 0 }
15682
+ );
15683
+ return true;
15684
+ } catch {
15685
+ return false;
15686
+ }
15687
+ }
15600
15688
  function sanitizeCacheKey(value) {
15601
15689
  return value.replace(/[^A-Za-z0-9._-]/g, "-").slice(0, 128);
15602
15690
  }
@@ -15638,14 +15726,25 @@ var LocalBuildExecutor = class {
15638
15726
  const checkout = join8(workspace, "checkout");
15639
15727
  const pmCacheDir = join8(workspace, ".pm-store");
15640
15728
  let cacheSeq = 0;
15641
- const cachePorts = cache ? {
15729
+ const compressor = "zstd";
15730
+ const canCompress = cache ? await (this.dependencies.commandAvailable ?? hasCommand)(compressor) : false;
15731
+ const cachePorts = cache && canCompress ? {
15642
15732
  transport: cache,
15643
15733
  fetch: this.dependencies.cacheFetch ?? ((url2, init) => fetch(url2, init)),
15644
15734
  runCommand: this.dependencies.runCommand,
15645
- tmpFile: (suffix) => join8(workspace, `cache-${String(cacheSeq++)}${suffix}`)
15735
+ tmpFile: (suffix) => join8(workspace, `cache-${String(cacheSeq++)}${suffix}`),
15736
+ // One latch for the whole build so a disabled cache warns once.
15737
+ report: { unavailable: false }
15646
15738
  } : null;
15647
15739
  try {
15648
15740
  await notifyStatus(observer, "CLONING");
15741
+ if (cache && !canCompress) {
15742
+ await observer.onLog({
15743
+ level: "WARN",
15744
+ stage: "CLONING",
15745
+ message: `Build cache disabled: ${compressor} is not installed. Install it to reuse dependencies between deploys.`
15746
+ });
15747
+ }
15649
15748
  await this.dependencies.runCommand(
15650
15749
  {
15651
15750
  command: "git",
@@ -15757,7 +15856,7 @@ var LocalBuildExecutor = class {
15757
15856
  await this.dependencies.runCommand(
15758
15857
  {
15759
15858
  command: installCommand,
15760
- args: installArgs,
15859
+ args: cachePorts ? [...installArgs, ...packageManager.cacheArgs(pmCacheDir)] : installArgs,
15761
15860
  cwd: checkout,
15762
15861
  env: {
15763
15862
  NODE_ENV: "development",
@@ -15984,8 +16083,149 @@ function controlPlaneUrl(env = process.env) {
15984
16083
 
15985
16084
  // ../../packages/sdk/src/callback-server.ts
15986
16085
  import { createServer } from "http";
15987
- var DONE_HTML = "<html><body><p>\u2705 Logged in. You can close this tab and return to the terminal.</p></body></html>";
15988
- var FAIL_HTML = "<html><body><p>Login failed. You can close this tab and return to the terminal.</p></body></html>";
16086
+
16087
+ // ../../packages/sdk/src/callback-page.ts
16088
+ function escapeHtml(value) {
16089
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
16090
+ }
16091
+ var FAVICON = `data:image/svg+xml,${encodeURIComponent(
16092
+ '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><rect width="32" height="32" rx="8" fill="#ededed"/><text x="16" y="23" text-anchor="middle" fill="#000" font-family="system-ui,sans-serif" font-size="20" font-weight="700">n</text></svg>'
16093
+ )}`;
16094
+ var CHECK_ICON = '<svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M5 12.5 10 17.5 19 7.5" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/></svg>';
16095
+ var CROSS_ICON = '<svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M7 7 17 17M17 7 7 17" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/></svg>';
16096
+ var STYLES = `
16097
+ *{box-sizing:border-box}
16098
+ :root{
16099
+ --bg:#000;--surface:#0a0a0a;--surface-2:#111;--border:#232323;
16100
+ --text:#ededed;--muted:#a1a1a1;--faint:#6f6f6f;
16101
+ --sans:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
16102
+ --mono:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
16103
+ }
16104
+ html{color-scheme:dark;-webkit-font-smoothing:antialiased}
16105
+ body{
16106
+ margin:0;min-height:100vh;display:grid;place-items:center;padding:24px;
16107
+ background:var(--bg);color:var(--text);font-family:var(--sans);
16108
+ font-size:14px;line-height:1.5;letter-spacing:-.006em;
16109
+ }
16110
+ body::before{
16111
+ content:"";position:fixed;inset:0;pointer-events:none;
16112
+ background:radial-gradient(62% 46% at 50% 0%,var(--glow),transparent 72%);
16113
+ }
16114
+ .card{
16115
+ position:relative;width:100%;max-width:400px;padding:34px 32px 28px;
16116
+ background:var(--surface);border:1px solid var(--border);border-radius:14px;
16117
+ text-align:center;animation:rise .4s cubic-bezier(.2,.7,.3,1) both;
16118
+ }
16119
+ .brand{
16120
+ display:inline-flex;align-items:center;gap:8px;
16121
+ font-size:14px;font-weight:600;letter-spacing:-.02em;color:var(--muted);
16122
+ }
16123
+ .brand-mark{
16124
+ display:inline-grid;width:22px;height:22px;place-items:center;
16125
+ background:var(--text);color:#000;border-radius:6px;
16126
+ font-size:12px;font-weight:700;line-height:1;
16127
+ }
16128
+ .glyph{
16129
+ width:52px;height:52px;margin:26px auto 20px;display:grid;place-items:center;
16130
+ border-radius:50%;color:var(--tone);
16131
+ background:var(--tone-soft);border:1px solid var(--tone-edge);
16132
+ }
16133
+ .glyph svg{width:26px;height:26px}
16134
+ .glyph path{stroke-dasharray:32;stroke-dashoffset:32;animation:draw .5s .15s cubic-bezier(.2,.7,.3,1) forwards}
16135
+ h1{margin:0 0 8px;font-size:19px;font-weight:600;letter-spacing:-.02em}
16136
+ p{margin:0 auto;max-width:32ch;color:var(--muted)}
16137
+ .detail{
16138
+ margin-top:18px;padding:10px 12px;text-align:left;word-break:break-word;
16139
+ background:var(--surface-2);border:1px solid var(--border);border-radius:8px;
16140
+ font-family:var(--mono);font-size:12.5px;color:var(--muted);
16141
+ }
16142
+ .hint{
16143
+ display:flex;align-items:center;gap:10px;margin-top:22px;padding:10px 12px;
16144
+ background:var(--surface-2);border:1px solid var(--border);border-radius:8px;text-align:left;
16145
+ }
16146
+ .hint-label{
16147
+ flex:none;font-size:10px;font-weight:600;letter-spacing:.09em;color:var(--faint);
16148
+ }
16149
+ .hint code{font-family:var(--mono);font-size:13px;color:var(--text)}
16150
+ .hint code::before{content:"$ ";color:var(--faint)}
16151
+ footer{margin-top:20px;font-size:12.5px;color:var(--faint)}
16152
+ @keyframes rise{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
16153
+ @keyframes draw{to{stroke-dashoffset:0}}
16154
+ @media (prefers-reduced-motion:reduce){
16155
+ .card{animation:none}
16156
+ .glyph path{animation:none;stroke-dashoffset:0}
16157
+ }
16158
+ `;
16159
+ var TONES = {
16160
+ success: {
16161
+ color: "#4ade80",
16162
+ soft: "rgba(74,222,128,.12)",
16163
+ edge: "rgba(74,222,128,.3)",
16164
+ glow: "rgba(74,222,128,.09)",
16165
+ icon: CHECK_ICON
16166
+ },
16167
+ failure: {
16168
+ color: "#ff5c5c",
16169
+ soft: "rgba(255,92,92,.12)",
16170
+ edge: "rgba(255,92,92,.3)",
16171
+ glow: "rgba(255,92,92,.09)",
16172
+ icon: CROSS_ICON
16173
+ }
16174
+ };
16175
+ function page(options) {
16176
+ const tone = TONES[options.tone];
16177
+ const title = escapeHtml(options.title);
16178
+ const detail = options.detail ? `<div class="detail">${escapeHtml(options.detail)}</div>` : "";
16179
+ return `<!doctype html>
16180
+ <html lang="en">
16181
+ <head>
16182
+ <meta charset="utf-8">
16183
+ <meta name="viewport" content="width=device-width,initial-scale=1">
16184
+ <title>${title} \xB7 naro</title>
16185
+ <link rel="icon" href="${FAVICON}">
16186
+ <style>:root{--tone:${tone.color};--tone-soft:${tone.soft};--tone-edge:${tone.edge};--glow:${tone.glow}}${STYLES}</style>
16187
+ </head>
16188
+ <body>
16189
+ <main class="card">
16190
+ <span class="brand"><span class="brand-mark">n</span>naro</span>
16191
+ <div class="glyph">${tone.icon}</div>
16192
+ <h1>${title}</h1>
16193
+ <p>${escapeHtml(options.message)}</p>
16194
+ ${detail}
16195
+ <div class="hint">
16196
+ <span class="hint-label">${escapeHtml(options.hint.label)}</span>
16197
+ <code>${escapeHtml(options.hint.command)}</code>
16198
+ </div>
16199
+ <footer>You can close this tab.</footer>
16200
+ </main>
16201
+ </body>
16202
+ </html>`;
16203
+ }
16204
+ function successPage() {
16205
+ return page({
16206
+ tone: "success",
16207
+ title: "You're signed in",
16208
+ message: "The CLI saved your session. Head back to the terminal to keep going.",
16209
+ hint: { label: "NEXT", command: "naro deploy" }
16210
+ });
16211
+ }
16212
+ function failurePage(reason) {
16213
+ return page({
16214
+ tone: "failure",
16215
+ title: "Sign-in failed",
16216
+ message: "Nothing was saved. Return to the terminal and try again.",
16217
+ detail: reason,
16218
+ hint: { label: "RETRY", command: "naro login" }
16219
+ });
16220
+ }
16221
+
16222
+ // ../../packages/sdk/src/callback-server.ts
16223
+ function sendPage(res, status, html) {
16224
+ res.statusCode = status;
16225
+ res.setHeader("content-type", "text/html; charset=utf-8");
16226
+ res.setHeader("cache-control", "no-store");
16227
+ res.end(html);
16228
+ }
15989
16229
  async function startCallbackServer() {
15990
16230
  let resolveCode;
15991
16231
  let rejectCode;
@@ -16003,18 +16243,20 @@ async function startCallbackServer() {
16003
16243
  const error51 = url2.searchParams.get("error");
16004
16244
  const code = url2.searchParams.get("code");
16005
16245
  if (error51) {
16006
- res.setHeader("content-type", "text/html");
16007
- res.end(FAIL_HTML);
16246
+ const description = url2.searchParams.get("error_description");
16247
+ sendPage(res, 200, failurePage(description ?? error51));
16008
16248
  rejectCode(new Error(`GitHub authorization failed: ${error51}`));
16009
16249
  return;
16010
16250
  }
16011
16251
  if (!code) {
16012
- res.statusCode = 400;
16013
- res.end("Missing code");
16252
+ sendPage(
16253
+ res,
16254
+ 400,
16255
+ failurePage("The callback carried no authorization code.")
16256
+ );
16014
16257
  return;
16015
16258
  }
16016
- res.setHeader("content-type", "text/html");
16017
- res.end(DONE_HTML);
16259
+ sendPage(res, 200, successPage());
16018
16260
  resolveCode(code);
16019
16261
  });
16020
16262
  await new Promise((resolve5) => server.listen(0, "127.0.0.1", resolve5));
@@ -17062,7 +17304,7 @@ function manifest(artifact) {
17062
17304
  modules: [artifact.entrypoint, ...artifact.modules].map(meta3),
17063
17305
  assets: artifact.assets.map((file2) => ({
17064
17306
  ...meta3(file2),
17065
- hash: hashAsset(file2.data)
17307
+ hash: hashAsset(file2.data, file2.contentType)
17066
17308
  })),
17067
17309
  compatibilityDate: artifact.compatibilityDate,
17068
17310
  compatibilityFlags: artifact.compatibilityFlags,
@@ -17162,7 +17404,8 @@ async function detectPackageManagerLenient(root) {
17162
17404
  "--no-audit",
17163
17405
  "--no-fund",
17164
17406
  "--legacy-peer-deps"
17165
- ]
17407
+ ],
17408
+ cacheArgs: (dir) => ["--cache", dir]
17166
17409
  };
17167
17410
  }
17168
17411
  }
@@ -17288,7 +17531,13 @@ async function deployDirectory(sourceDir, options, log2) {
17288
17531
  rootDirectory: options.rootDirectory ?? null,
17289
17532
  env: { ...ctx.env, ...envToSend }
17290
17533
  },
17291
- observer
17534
+ observer,
17535
+ createCacheTransport({
17536
+ deploymentId: ctx.deploymentId,
17537
+ controlPlaneUrl: cpUrl,
17538
+ callbackToken: ctx.callbackToken,
17539
+ ...options.fetch ? { fetch: options.fetch } : { fetch }
17540
+ })
17292
17541
  );
17293
17542
  artifact = result.artifact;
17294
17543
  } catch (error51) {
package/dist/deploy.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { type CacheTransport } from "@next-deploy/build";
1
2
  import { type CliGitConnection } from "@next-deploy/sdk";
2
3
  import type { BuildObserver, DeploymentArtifact, DeploymentStatus } from "./artifact-types";
3
4
  import { type GitInfo } from "./git-info";
@@ -10,7 +11,7 @@ export interface DeployBuildExecutor {
10
11
  branch: string;
11
12
  rootDirectory: string | null;
12
13
  env: Record<string, string>;
13
- }, observer: BuildObserver): Promise<{
14
+ }, observer: BuildObserver, cache?: CacheTransport): Promise<{
14
15
  artifact: DeploymentArtifact;
15
16
  }>;
16
17
  }
package/dist/deploy.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  parseDotenv,
6
6
  readProjectEnv,
7
7
  slugify
8
- } from "./chunk-TYBC3YSK.js";
8
+ } from "./chunk-WKJELBOU.js";
9
9
  export {
10
10
  deployDirectory,
11
11
  eventsObserver,
package/dist/index.js CHANGED
@@ -26,7 +26,7 @@ import {
26
26
  slugify,
27
27
  toNaroError,
28
28
  writeProjectLink
29
- } from "./chunk-TYBC3YSK.js";
29
+ } from "./chunk-WKJELBOU.js";
30
30
 
31
31
  // src/args.ts
32
32
  var BOOLEAN_FLAGS = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "naro-cli",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "naro": "./dist/index.js"
@@ -29,9 +29,9 @@
29
29
  "typescript": "7.0.2",
30
30
  "vitest": "3.2.7",
31
31
  "@next-deploy/build": "0.1.0",
32
- "@next-deploy/config": "0.1.0",
32
+ "@next-deploy/core": "0.1.0",
33
33
  "@next-deploy/sdk": "0.1.0",
34
- "@next-deploy/core": "0.1.0"
34
+ "@next-deploy/config": "0.1.0"
35
35
  },
36
36
  "scripts": {
37
37
  "build": "tsup && pnpm build:types",