naro-cli 0.14.0 → 0.15.0

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
@@ -173,7 +173,7 @@ naro policy set posts --from-file policy.json
173
173
  Shorthand: writes the policies naro_select … naro_delete
174
174
  naro policy remove todos Drop every policy on the table: back to deny (asks first)
175
175
 
176
- naro storage Naro Storage: on or off, limits, usage, buckets
176
+ naro storage Naro Storage: on or off, limits, usage, today's operations, buckets
177
177
  naro storage enable Turn it on: the project's R2 bucket, bound to the backend
178
178
  naro storage bucket create avatars --public --file-size-limit 5MiB --allowed-mime-types image/png,image/jpeg
179
179
  naro storage bucket update avatars --private | --file-size-limit none | --allowed-mime-types any
@@ -793,6 +793,20 @@ An `--update-check` you give replaces the default: without
793
793
 
794
794
  ## Release notes
795
795
 
796
+ ### 0.15.0
797
+
798
+ - `naro storage` shows the Storage meter's day between `Used:` and `Buckets:`:
799
+ `Today (UTC):` has the write operations (R2 Class A) against the daily limit
800
+ and the reads, `Measured:` the bucket size R2 reported and when R2 measured
801
+ it (R2 reports the size 30–45 minutes late, and the meter reads it every 15
802
+ minutes). A `✗` line
803
+ says uploads are stopped, over the storage limit until objects are deleted
804
+ or over the day's write operations until 00:00 UTC (downloads still work);
805
+ a `⚠` line warns from 80% of the storage limit or of the day's write
806
+ operations. Before the meter has run for the UTC day nothing is added, and
807
+ `--json` prints `storage.today` and `storage.limits.dailyOps` as the
808
+ control plane answered.
809
+
796
810
  ### 0.14.0
797
811
 
798
812
  - `naro analytics` shows the project's Web Analytics from the terminal: the
@@ -294,9 +294,6 @@ function pathnameOf(url2) {
294
294
  }
295
295
  }
296
296
 
297
- // ../../packages/core/src/internal-auth.ts
298
- import { Buffer as Buffer2 } from "buffer";
299
-
300
297
  // ../../packages/core/src/migration-names.ts
301
298
  function normalizeMigrationName(name) {
302
299
  return name.trim().toLowerCase().replace(/\.sql$/, "").replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
@@ -15253,6 +15250,20 @@ function classifyNextVersion(raw, now) {
15253
15250
  // ../../packages/build/src/package-manager.ts
15254
15251
  import { access, readFile as readFile2 } from "fs/promises";
15255
15252
  import { join as join2 } from "path";
15253
+ function cacheEnv(dir) {
15254
+ return {
15255
+ npm_config_store_dir: dir,
15256
+ npm_config_cache: dir,
15257
+ YARN_CACHE_FOLDER: dir
15258
+ };
15259
+ }
15260
+ function withSharedCache(spec, dir) {
15261
+ return {
15262
+ ...spec,
15263
+ addArgs: (specs) => [...spec.addArgs(specs), ...spec.cacheArgs(dir)],
15264
+ addEnv: { ...spec.addEnv, ...cacheEnv(dir) }
15265
+ };
15266
+ }
15256
15267
  async function exists(path) {
15257
15268
  try {
15258
15269
  await access(path);
@@ -15706,7 +15717,7 @@ export default defineCloudflareConfig({});
15706
15717
 
15707
15718
  // ../../packages/build/src/vinext.ts
15708
15719
  var VINEXT_TOOLCHAIN = [
15709
- "vinext@1.0.0-beta.9",
15720
+ "vinext@1.1.0",
15710
15721
  "@cloudflare/vite-plugin@1.54.7",
15711
15722
  "@vitejs/plugin-react@6.1.1",
15712
15723
  "@vitejs/plugin-rsc@0.5.34",
@@ -15722,7 +15733,7 @@ var VITE_CONFIG_NAMES = [
15722
15733
  "vite.config.mts",
15723
15734
  "vite.config.cts"
15724
15735
  ];
15725
- var GENERATED_VITE_CONFIG = "vite.config.mjs";
15736
+ var GENERATED_VITE_CONFIG = "vite.config.js";
15726
15737
  var OUTPUT_DIR = "dist";
15727
15738
  var SERVER_DIR = "server";
15728
15739
  var CLIENT_DIR = "client";
@@ -15787,6 +15798,8 @@ async function createVinextBuildConfig(appRoot, deploymentId, writer = async (pa
15787
15798
  }
15788
15799
  var AUTO_FIXED_ISSUES = [/Missing "type": "module"/i];
15789
15800
  var ISSUE_LINE = /^\s*✗\s+(.+)$/;
15801
+ var PARTIAL_LINE = /^\s*~\s+(.+)$/;
15802
+ var BLOCKING_PARTIALS = [/^webpack\b/i];
15790
15803
  var ANSI_ESCAPE = /\u001B\[[0-9;]*m/g;
15791
15804
  var SUMMARY_LINE = /Overall:\s*(\d+)%\s*compatible\s*\(\s*(\d+)\s+supported,\s*(\d+)\s+partial,\s*(\d+)\s+issues?\s*\)/i;
15792
15805
  function parseVinextCheckReport(output) {
@@ -15798,12 +15811,18 @@ function parseVinextCheckReport(output) {
15798
15811
  plain.split("\n").map((line) => ISSUE_LINE.exec(line)?.[1]?.trim()).filter((line) => Boolean(line))
15799
15812
  )
15800
15813
  ];
15814
+ const partialLines = [
15815
+ ...new Set(
15816
+ plain.split("\n").map((line) => PARTIAL_LINE.exec(line)?.[1]?.trim()).filter((line) => Boolean(line))
15817
+ )
15818
+ ];
15801
15819
  return {
15802
15820
  percent: Number(match[1]),
15803
15821
  supported: Number(match[2]),
15804
15822
  partial: Number(match[3]),
15805
15823
  issues: Number(match[4]),
15806
- issueLines
15824
+ issueLines,
15825
+ partialLines
15807
15826
  };
15808
15827
  }
15809
15828
  function vinextRouteFromCheck(report) {
@@ -15815,7 +15834,10 @@ function vinextRouteFromCheck(report) {
15815
15834
  const autoFixed = report.issueLines.filter(
15816
15835
  (line) => AUTO_FIXED_ISSUES.some((pattern) => pattern.test(line))
15817
15836
  );
15818
- const blocking = Math.max(0, report.issues - autoFixed.length);
15837
+ const blockingPartials = report.partialLines.filter(
15838
+ (line) => BLOCKING_PARTIALS.some((pattern) => pattern.test(line))
15839
+ );
15840
+ const blocking = Math.max(0, report.issues - autoFixed.length) + blockingPartials.length;
15819
15841
  if (blocking > 0)
15820
15842
  return {
15821
15843
  eligible: false,
@@ -17836,6 +17858,7 @@ function createVinextCloudflareStrategy(options) {
17836
17858
  // Not production: npm and classic Yarn read that as omit=dev and
17837
17859
  // would prune the project's devDependencies while adding ours.
17838
17860
  env: {
17861
+ ...context.packageManager.addEnv,
17839
17862
  NODE_ENV: "development",
17840
17863
  NPM_CONFIG_USERCONFIG: context.packageManagerConfigPath
17841
17864
  },
@@ -18217,11 +18240,7 @@ var LocalBuildExecutor = class {
18217
18240
  env: {
18218
18241
  NODE_ENV: "development",
18219
18242
  NPM_CONFIG_USERCONFIG: packageManagerConfigPath,
18220
- ...cachePorts ? {
18221
- npm_config_store_dir: pmCacheDir,
18222
- npm_config_cache: pmCacheDir,
18223
- YARN_CACHE_FOLDER: pmCacheDir
18224
- } : {}
18243
+ ...cachePorts ? cacheEnv(pmCacheDir) : {}
18225
18244
  },
18226
18245
  timeoutMs: 10 * 6e4,
18227
18246
  stage: "INSTALLING",
@@ -18305,7 +18324,9 @@ var LocalBuildExecutor = class {
18305
18324
  identity,
18306
18325
  branch: input.branch,
18307
18326
  env: input.env ?? {},
18308
- packageManager,
18327
+ // The toolchain add has to use the store the install linked
18328
+ // node_modules from (withSharedCache).
18329
+ packageManager: cachePorts ? withSharedCache(packageManager, pmCacheDir) : packageManager,
18309
18330
  packageManagerConfigPath,
18310
18331
  buildScript: resolved.buildScript,
18311
18332
  nextVersion: nextVersion ? `${String(nextVersion.major)}.${String(nextVersion.minor)}.${String(nextVersion.patch)}` : "unknown",
@@ -21109,6 +21130,12 @@ var CATALOG = {
21109
21130
  status: 507,
21110
21131
  message: () => "This project has used all of its storage. Delete objects to free space; uploads fail until then."
21111
21132
  },
21133
+ // Storage 3-1d (design §6.4): like WRITE_QUOTA_EXCEEDED, a 429 that clears
21134
+ // by itself at midnight; downloads, deletes and listings are not refused.
21135
+ STORAGE_OPS_QUOTA_EXCEEDED: {
21136
+ status: 429,
21137
+ message: () => "This project has reached its daily storage operation limit. Downloads still work; uploads resume at 00:00 UTC."
21138
+ },
21112
21139
  // R2 or its binding did not answer; the original error is logged, never sent.
21113
21140
  STORAGE_UNAVAILABLE: {
21114
21141
  status: 503,
package/dist/deploy.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  parseDotenv,
6
6
  readProjectEnv,
7
7
  slugify
8
- } from "./chunk-U7AZSAFS.js";
8
+ } from "./chunk-6WJ3QQ5Y.js";
9
9
  import "./chunk-K3NQKI34.js";
10
10
  export {
11
11
  deployDirectory,
package/dist/index.js CHANGED
@@ -83,7 +83,7 @@ import {
83
83
  toNaroError,
84
84
  unreadablePolicyComment,
85
85
  writeProjectLink
86
- } from "./chunk-U7AZSAFS.js";
86
+ } from "./chunk-6WJ3QQ5Y.js";
87
87
  import "./chunk-K3NQKI34.js";
88
88
 
89
89
  // src/args.ts
@@ -5272,7 +5272,7 @@ function parseSize(text) {
5272
5272
  throw new NaroError("VALIDATION_FAILED", SIZE_MESSAGE);
5273
5273
  return bytes;
5274
5274
  }
5275
- function binarySize(bytes) {
5275
+ function binarySize(bytes, decimals = 1) {
5276
5276
  const units = ["B", "KiB", "MiB", "GiB", "TiB"];
5277
5277
  let value = bytes;
5278
5278
  let unit = 0;
@@ -5280,7 +5280,7 @@ function binarySize(bytes) {
5280
5280
  value /= 1024;
5281
5281
  unit += 1;
5282
5282
  }
5283
- const shown = unit === 0 ? String(value) : String(Number(value.toFixed(1)));
5283
+ const shown = unit === 0 ? String(value) : String(Number(value.toFixed(decimals)));
5284
5284
  return `${shown} ${units[unit] ?? "B"}`;
5285
5285
  }
5286
5286
  function count(n, noun) {
@@ -5308,6 +5308,51 @@ function bucketLine(bucket) {
5308
5308
  const line = ` ${cells.join(" ")}`;
5309
5309
  return bucket.deletingAt === null ? line : `${line} (being removed: \`naro storage bucket remove ${name} --empty\` finishes it)`;
5310
5310
  }
5311
+ var WARN_PERCENT = 80;
5312
+ function utcClock(iso) {
5313
+ const at = new Date(iso);
5314
+ return Number.isNaN(at.getTime()) ? null : at.toISOString().slice(11, 16);
5315
+ }
5316
+ function warnedPercent(value, limit) {
5317
+ if (limit === null || !(limit > 0)) return null;
5318
+ const percent2 = Math.floor(value * 100 / limit);
5319
+ return percent2 >= WARN_PERCENT ? percent2 : null;
5320
+ }
5321
+ function meterLines(info) {
5322
+ const { today, limits } = info;
5323
+ if (today === null) return [];
5324
+ const reads = count(today.classBOps, "read");
5325
+ const writes = today.classAOps.toLocaleString("en-US");
5326
+ const dailyOps = limits.dailyOps?.toLocaleString("en-US") ?? null;
5327
+ const lines = [
5328
+ dailyOps === null ? `Today (UTC): ${writes} write operations, ${reads}` : `Today (UTC): ${writes} of ${dailyOps} write operations, ${reads}`
5329
+ ];
5330
+ if (today.measuredBytes === null)
5331
+ lines.push("Measured: not yet (the meter reads R2 every 15 minutes)");
5332
+ else {
5333
+ const time = today.measuredAt === null ? null : utcClock(today.measuredAt);
5334
+ lines.push(
5335
+ `Measured: ${binarySize(today.measuredBytes, 2)} in R2${time === null ? "" : ` at ${time} UTC`}`
5336
+ );
5337
+ }
5338
+ if (today.bytesBlocked)
5339
+ lines.push(
5340
+ today.measuredBytes === null ? `\u2717 Uploads are stopped: the bucket is at the storage limit of ${binarySize(limits.bytes)}. Delete objects; uploads reopen after the next measurement (usually within an hour).` : `\u2717 Uploads are stopped: R2 holds ${binarySize(today.measuredBytes, 2)} of ${binarySize(limits.bytes)}. Delete objects; uploads reopen after the next measurement (usually within an hour).`
5341
+ );
5342
+ if (today.opsBlocked)
5343
+ lines.push(
5344
+ dailyOps === null ? "\u2717 Uploads are stopped until 00:00 UTC: today's write operations are used. Downloads still work." : `\u2717 Uploads are stopped until 00:00 UTC: today's ${dailyOps} write operations are used. Downloads still work.`
5345
+ );
5346
+ const storage = today.bytesBlocked || today.measuredBytes === null ? null : warnedPercent(today.measuredBytes, limits.bytes);
5347
+ if (storage !== null)
5348
+ lines.push(`\u26A0 ${String(storage)}% of the storage limit is used.`);
5349
+ const operations = today.opsBlocked ? null : warnedPercent(today.classAOps, limits.dailyOps);
5350
+ if (operations !== null)
5351
+ lines.push(
5352
+ `\u26A0 ${String(operations)}% of today's write operations are used.`
5353
+ );
5354
+ return lines;
5355
+ }
5311
5356
  function storageLines(info) {
5312
5357
  if (!info.enabled)
5313
5358
  return [
@@ -5319,6 +5364,7 @@ function storageLines(info) {
5319
5364
  `Storage: on (R2 bucket ${printable(info.bucket ?? "unknown")})`,
5320
5365
  `Limits: ${binarySize(limits.bytes)} in all, ${binarySize(limits.objectBytes)} per object, ${binarySize(limits.singleUploadBytes)} per upload`,
5321
5366
  usage === null ? "Used: not known yet: the backend is not ready (`naro backend` shows where it got to)" : `Used: ${binarySize(usage.bytes)} in ${count(usage.objects, "object")}`,
5367
+ ...meterLines(info),
5322
5368
  info.buckets.length === 0 ? "Buckets: none yet (`naro storage bucket create <name>` makes one)" : `Buckets: ${String(info.buckets.length)}`,
5323
5369
  ...info.buckets.map(bucketLine)
5324
5370
  ];
@@ -8328,7 +8374,7 @@ Usage:
8328
8374
  naro_insert, naro_update, naro_delete (shorthand for CREATE POLICY)
8329
8375
  naro policy remove <table> Drop every policy on it: back to deny (asks first)
8330
8376
 
8331
- naro storage [status] Naro Storage: on or off, limits, what is stored, the buckets
8377
+ naro storage [status] Naro Storage: on or off, limits, what is stored, today's operations, the buckets
8332
8378
  naro storage enable Turn Storage on: the project's R2 bucket, bound to its backend
8333
8379
  (safe to re-run; applies a plan change's limits)
8334
8380
  naro storage bucket list The buckets: public or private, objects, bytes, limits
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "naro-cli",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "naro": "./dist/index.js"
@@ -25,7 +25,7 @@
25
25
  "@opennextjs/cloudflare": "1.20.2",
26
26
  "@supabase/supabase-js": "2.112.3",
27
27
  "@vercel/frameworks": "3.34.0",
28
- "vinext": "1.0.0-beta.9",
28
+ "vinext": "1.1.0",
29
29
  "wrangler": "4.124.0"
30
30
  },
31
31
  "devDependencies": {
@@ -34,11 +34,11 @@
34
34
  "typescript": "7.0.2",
35
35
  "vitest": "3.2.7",
36
36
  "naro-js": "0.3.0",
37
- "@next-deploy/build": "0.1.0",
38
- "@next-deploy/config": "0.1.0",
39
37
  "@next-deploy/core": "0.1.0",
38
+ "@next-deploy/data-core": "0.1.0",
40
39
  "@next-deploy/sdk": "0.1.0",
41
- "@next-deploy/data-core": "0.1.0"
40
+ "@next-deploy/config": "0.1.0",
41
+ "@next-deploy/build": "0.1.0"
42
42
  },
43
43
  "scripts": {
44
44
  "build": "tsup && pnpm build:types",