@convex-localfirst/cli 0.1.0 → 0.1.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.
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Which source files were modified after the manifest. `manifestMtimeMs` is null when the
3
+ * manifest doesn't exist (→ everything counts as stale, i.e. "run codegen"). Pure so it's
4
+ * unit-testable without fs.
5
+ */
6
+ export declare function staleManifestSources(manifestMtimeMs: number | null, sources: ReadonlyArray<{
7
+ readonly file: string;
8
+ readonly mtimeMs: number;
9
+ }>): string[];
10
+ /**
11
+ * Read the convex source dir + the generated manifest and warn (once) if the manifest looks
12
+ * stale — so editing a `lf.table` and forgetting `codegen` can't silently ship a mismatched
13
+ * client. Best-effort: silent when there's no convex dir. `warn` is injected for tests.
14
+ */
15
+ export declare function checkManifestFreshness(root: string, convexDir: string, generatedManifestPath: string, warn: (message: string) => void): void;
@@ -0,0 +1,36 @@
1
+ import { existsSync, readdirSync, statSync } from "node:fs";
2
+ import { join, resolve } from "node:path";
3
+ /**
4
+ * Which source files were modified after the manifest. `manifestMtimeMs` is null when the
5
+ * manifest doesn't exist (→ everything counts as stale, i.e. "run codegen"). Pure so it's
6
+ * unit-testable without fs.
7
+ */
8
+ export function staleManifestSources(manifestMtimeMs, sources) {
9
+ if (manifestMtimeMs == null) {
10
+ return sources.map((s) => s.file);
11
+ }
12
+ return sources.filter((s) => s.mtimeMs > manifestMtimeMs).map((s) => s.file);
13
+ }
14
+ /**
15
+ * Read the convex source dir + the generated manifest and warn (once) if the manifest looks
16
+ * stale — so editing a `lf.table` and forgetting `codegen` can't silently ship a mismatched
17
+ * client. Best-effort: silent when there's no convex dir. `warn` is injected for tests.
18
+ */
19
+ export function checkManifestFreshness(root, convexDir, generatedManifestPath, warn) {
20
+ const convexAbs = resolve(root, convexDir);
21
+ if (!existsSync(convexAbs)) {
22
+ return;
23
+ }
24
+ const manifestAbs = resolve(root, generatedManifestPath.replace(/^\//, ""));
25
+ const manifestMtimeMs = existsSync(manifestAbs) ? statSync(manifestAbs).mtimeMs : null;
26
+ const sources = readdirSync(convexAbs)
27
+ .filter((f) => /\.(ts|js)$/.test(f) && !f.startsWith("_") && !f.endsWith(".d.ts"))
28
+ .map((f) => ({ file: join(convexDir, f), mtimeMs: statSync(join(convexAbs, f)).mtimeMs }));
29
+ const stale = staleManifestSources(manifestMtimeMs, sources);
30
+ if (stale.length === 0) {
31
+ return;
32
+ }
33
+ const shown = stale.slice(0, 3).join(", ") + (stale.length > 3 ? `, +${stale.length - 3} more` : "");
34
+ warn(`[convex-localfirst] generated manifest looks stale: ${stale.length} convex source(s) changed after ` +
35
+ `"${generatedManifestPath}" (${shown}). Run \`npx convex-localfirst codegen\` to regenerate it.`);
36
+ }
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@ import { join, resolve } from "node:path";
4
4
  import { pathToFileURL } from "node:url";
5
5
  import { runCheck } from "./check.js";
6
6
  import { emitManifestSource, introspectExports } from "./codegen.js";
7
+ import { checkManifestFreshness } from "./freshness.js";
7
8
  const command = process.argv[2] ?? "help";
8
9
  if (command === "init") {
9
10
  init();
@@ -209,6 +210,9 @@ async function codegen() {
209
210
  const CHECK_SCOPE_NOTE = "note: catches ctx.db.insert + id-based patch/delete/replace whose id is traceable to a local-first table within one function; ids passed across functions or via ctx.db.get() are not traced — review those manually.";
210
211
  function check() {
211
212
  const dir = existsSync("convex") ? "convex" : ".";
213
+ // Warn (don't fail) if the generated manifest is older than a DSL source — the safety net
214
+ // that used to live in the Vite plugin, now folded in so CI `check` catches a stale manifest.
215
+ checkManifestFreshness(process.cwd(), dir, "src/convex-localfirst/generated.ts", console.warn);
212
216
  const violations = runCheck(dir);
213
217
  if (violations.length === 0) {
214
218
  console.log("convex-localfirst check: no direct writes to local-first tables found");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@convex-localfirst/cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "CLI for convex-localfirst: codegen (client manifest from lf.table), check (catch direct LF-table writes), dev.",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -9,6 +9,19 @@
9
9
  "cli",
10
10
  "codegen"
11
11
  ],
12
+ "author": "Fanzzzd",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/Fanzzzd/convex-localfirst.git",
16
+ "directory": "packages/cli"
17
+ },
18
+ "homepage": "https://github.com/Fanzzzd/convex-localfirst#readme",
19
+ "bugs": {
20
+ "url": "https://github.com/Fanzzzd/convex-localfirst/issues"
21
+ },
22
+ "engines": {
23
+ "node": ">=18"
24
+ },
12
25
  "type": "module",
13
26
  "bin": {
14
27
  "convex-localfirst": "./dist/index.js"
@@ -27,8 +40,8 @@
27
40
  "convex": ">=1.0.0",
28
41
  "typescript": "^5.7.0",
29
42
  "vitest": "^2.1.0",
30
- "@convex-localfirst/server": "^0.1.0",
31
- "@convex-localfirst/core": "^0.1.0"
43
+ "@convex-localfirst/core": "^0.1.1",
44
+ "@convex-localfirst/server": "^0.1.1"
32
45
  },
33
46
  "scripts": {
34
47
  "build": "tsc -p tsconfig.json --noEmit false --emitDeclarationOnly false",