gencow 0.1.147 → 0.1.148

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/core/index.js CHANGED
@@ -3258,49 +3258,57 @@ async function withRlsConnection(session, rls, reuseOuterConnection, fn) {
3258
3258
  }
3259
3259
  function wrapPreparedQuery(pq, session, rls, reuseOuterConnection) {
3260
3260
  const origExecute = pq.execute.bind(pq);
3261
- const origAll = pq.all.bind(pq);
3261
+ const origAll = typeof pq.all === "function" ? pq.all.bind(pq) : null;
3262
+ const swapClient = (client) => {
3263
+ const prevPq = pq.client;
3264
+ const prevSession = session.client;
3265
+ pq.client = client;
3266
+ session.client = client;
3267
+ return () => {
3268
+ pq.client = prevPq;
3269
+ session.client = prevSession;
3270
+ };
3271
+ };
3262
3272
  pq.execute = async (placeholderValues) => {
3263
3273
  const active = rlsExecClient.getStore();
3264
3274
  if (active) {
3265
- const prev = pq.client;
3266
- pq.client = active;
3275
+ const restore = swapClient(active);
3267
3276
  try {
3268
3277
  return await origExecute(placeholderValues);
3269
3278
  } finally {
3270
- pq.client = prev;
3279
+ restore();
3271
3280
  }
3272
3281
  }
3273
3282
  return withRlsConnection(session, rls, reuseOuterConnection, async (client) => {
3274
- const prev = pq.client;
3275
- pq.client = client;
3283
+ const restore = swapClient(client);
3276
3284
  try {
3277
3285
  return await origExecute(placeholderValues);
3278
3286
  } finally {
3279
- pq.client = prev;
3287
+ restore();
3280
3288
  }
3281
3289
  });
3282
3290
  };
3283
- pq.all = async (placeholderValues) => {
3284
- const active = rlsExecClient.getStore();
3285
- if (active) {
3286
- const prev = pq.client;
3287
- pq.client = active;
3288
- try {
3289
- return await origAll(placeholderValues);
3290
- } finally {
3291
- pq.client = prev;
3292
- }
3293
- }
3294
- return withRlsConnection(session, rls, reuseOuterConnection, async (client) => {
3295
- const prev = pq.client;
3296
- pq.client = client;
3297
- try {
3298
- return await origAll(placeholderValues);
3299
- } finally {
3300
- pq.client = prev;
3291
+ if (origAll) {
3292
+ pq.all = async (placeholderValues) => {
3293
+ const active = rlsExecClient.getStore();
3294
+ if (active) {
3295
+ const restore = swapClient(active);
3296
+ try {
3297
+ return await origAll(placeholderValues);
3298
+ } finally {
3299
+ restore();
3300
+ }
3301
3301
  }
3302
- });
3303
- };
3302
+ return withRlsConnection(session, rls, reuseOuterConnection, async (client) => {
3303
+ const restore = swapClient(client);
3304
+ try {
3305
+ return await origAll(placeholderValues);
3306
+ } finally {
3307
+ restore();
3308
+ }
3309
+ });
3310
+ };
3311
+ }
3304
3312
  }
3305
3313
  function wrapSession(session, rls, reuseOuterConnection) {
3306
3314
  return new Proxy(session, {
@@ -5,7 +5,7 @@ import { dirname, relative, resolve } from "path";
5
5
  export const ADD_COMPONENTS = {
6
6
  ai: {
7
7
  label: "AI Engine",
8
- deps: ["ai", "@ai-sdk/openai"],
8
+ deps: ["ai", "@ai-sdk/openai", "@ai-sdk/provider-utils"],
9
9
  files: [{ src: "ai.ts", dest: "gencow/ai.ts" }],
10
10
  env: { OPENAI_API_KEY: "" },
11
11
  requires: [],
@@ -3,6 +3,7 @@ export const REQUIRED_CLI_TARBALL_FILES = Object.freeze([
3
3
  "bin/gencow-mcp.mjs",
4
4
  "lib/readme-codegen.mjs",
5
5
  "lib/deploy-auditor.mjs",
6
+ "lib/cron-manifest.mjs",
6
7
  "lib/dev-cloud-bundle.mjs",
7
8
  "lib/codegen/index.mjs",
8
9
  "server/index.js",
@@ -8,7 +8,6 @@ import {
8
8
  resolveCodegenConfig,
9
9
  resolveCodegenPublishDir,
10
10
  } from "./codegen-command.mjs";
11
- import { generateAuthSchema } from "./codegen/index.mjs";
12
11
  import { updateComponentReadme } from "./component-readme.mjs";
13
12
  import { buildReadmeMarkdown, extractComponentsBlock } from "./readme-codegen.mjs";
14
13
  import { CYAN, DIM, RED, RESET, YELLOW, error, info, log, success, warn } from "./output.mjs";
@@ -146,7 +145,7 @@ export function createApiCodegenRuntime(options = {}) {
146
145
  resolvePathImpl = resolve,
147
146
  successImpl = success,
148
147
  updateComponentReadmeImpl = updateComponentReadme,
149
- generateAuthSchemaImpl = generateAuthSchema,
148
+ generateAuthSchemaImpl,
150
149
  warnImpl = warn,
151
150
  writeFileSyncImpl = writeFileSync,
152
151
  } = options;
@@ -176,6 +175,7 @@ export function createApiCodegenRuntime(options = {}) {
176
175
  if (typeof bundled.createNodeProcessRunner !== "function") {
177
176
  throw new Error("Invalid codegen bundle: createNodeProcessRunner export not found");
178
177
  }
178
+ const generateAuthSchema = generateAuthSchemaImpl ?? bundled.generateAuthSchema;
179
179
 
180
180
  const result = await bundled.runCodegen({
181
181
  projectRoot: cwd,
@@ -185,7 +185,7 @@ export function createApiCodegenRuntime(options = {}) {
185
185
  finalOutDir: publishDir,
186
186
  authSchema: {
187
187
  enabled: resolveCodegenConfig(config).authSchema,
188
- generate: generateAuthSchemaImpl,
188
+ generate: generateAuthSchema,
189
189
  },
190
190
  });
191
191
  if (result.authSchema?.changed) {
@@ -6557,49 +6557,57 @@ async function withRlsConnection(session, rls, reuseOuterConnection, fn) {
6557
6557
  }
6558
6558
  function wrapPreparedQuery(pq, session, rls, reuseOuterConnection) {
6559
6559
  const origExecute = pq.execute.bind(pq);
6560
- const origAll = pq.all.bind(pq);
6560
+ const origAll = typeof pq.all === "function" ? pq.all.bind(pq) : null;
6561
+ const swapClient = (client) => {
6562
+ const prevPq = pq.client;
6563
+ const prevSession = session.client;
6564
+ pq.client = client;
6565
+ session.client = client;
6566
+ return () => {
6567
+ pq.client = prevPq;
6568
+ session.client = prevSession;
6569
+ };
6570
+ };
6561
6571
  pq.execute = async (placeholderValues) => {
6562
6572
  const active = rlsExecClient.getStore();
6563
6573
  if (active) {
6564
- const prev = pq.client;
6565
- pq.client = active;
6574
+ const restore = swapClient(active);
6566
6575
  try {
6567
6576
  return await origExecute(placeholderValues);
6568
6577
  } finally {
6569
- pq.client = prev;
6578
+ restore();
6570
6579
  }
6571
6580
  }
6572
6581
  return withRlsConnection(session, rls, reuseOuterConnection, async (client) => {
6573
- const prev = pq.client;
6574
- pq.client = client;
6582
+ const restore = swapClient(client);
6575
6583
  try {
6576
6584
  return await origExecute(placeholderValues);
6577
6585
  } finally {
6578
- pq.client = prev;
6586
+ restore();
6579
6587
  }
6580
6588
  });
6581
6589
  };
6582
- pq.all = async (placeholderValues) => {
6583
- const active = rlsExecClient.getStore();
6584
- if (active) {
6585
- const prev = pq.client;
6586
- pq.client = active;
6587
- try {
6588
- return await origAll(placeholderValues);
6589
- } finally {
6590
- pq.client = prev;
6591
- }
6592
- }
6593
- return withRlsConnection(session, rls, reuseOuterConnection, async (client) => {
6594
- const prev = pq.client;
6595
- pq.client = client;
6596
- try {
6597
- return await origAll(placeholderValues);
6598
- } finally {
6599
- pq.client = prev;
6590
+ if (origAll) {
6591
+ pq.all = async (placeholderValues) => {
6592
+ const active = rlsExecClient.getStore();
6593
+ if (active) {
6594
+ const restore = swapClient(active);
6595
+ try {
6596
+ return await origAll(placeholderValues);
6597
+ } finally {
6598
+ restore();
6599
+ }
6600
6600
  }
6601
- });
6602
- };
6601
+ return withRlsConnection(session, rls, reuseOuterConnection, async (client) => {
6602
+ const restore = swapClient(client);
6603
+ try {
6604
+ return await origAll(placeholderValues);
6605
+ } finally {
6606
+ restore();
6607
+ }
6608
+ });
6609
+ };
6610
+ }
6603
6611
  }
6604
6612
  function wrapSession(session, rls, reuseOuterConnection) {
6605
6613
  return new Proxy(session, {
@@ -1,6 +1,5 @@
1
1
  import { existsSync } from "fs";
2
2
  import { resolve, relative } from "path";
3
- import { generateAuthSchema } from "./codegen/index.mjs";
4
3
 
5
4
  export function resolveCodegenConfig(config = {}) {
6
5
  const outDir =
@@ -85,6 +84,7 @@ export function createCodegenCommand(deps) {
85
84
  if (typeof bundled.runCodegen !== "function") {
86
85
  throw new Error("Invalid codegen bundle: runCodegen export not found");
87
86
  }
87
+ const generateAuthSchema = deps.generateAuthSchemaImpl ?? bundled.generateAuthSchema;
88
88
  const result = await bundled.runCodegen({
89
89
  projectRoot: cwd,
90
90
  functionsDir: config.functionsDir,
@@ -93,7 +93,7 @@ export function createCodegenCommand(deps) {
93
93
  finalOutDir: publishDir,
94
94
  authSchema: {
95
95
  enabled: codegen.authSchema,
96
- generate: deps.generateAuthSchemaImpl ?? generateAuthSchema,
96
+ generate: generateAuthSchema,
97
97
  },
98
98
  });
99
99
  if (result.authSchema?.changed) {
@@ -0,0 +1,142 @@
1
+ import { createHash } from "crypto";
2
+ import { cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
3
+ import { dirname, resolve } from "path";
4
+ import { pathToFileURL } from "url";
5
+
6
+ export const CRON_MANIFEST_RELATIVE_PATH = ".gencow/cron-jobs.json";
7
+
8
+ function resolveCronDefinitionsFile({ cwd, functionsDir, existsSyncImpl, resolvePathImpl }) {
9
+ const cronsTs = resolvePathImpl(cwd, functionsDir, "crons.ts");
10
+ const cronsJs = resolvePathImpl(cwd, functionsDir, "crons.js");
11
+ if (existsSyncImpl(cronsTs)) return cronsTs;
12
+ if (existsSyncImpl(cronsJs)) return cronsJs;
13
+ return null;
14
+ }
15
+
16
+ function readNonEmptyString(value, field) {
17
+ if (typeof value !== "string" || value.trim() === "") {
18
+ throw new Error(`cron job ${field} must be a non-empty string`);
19
+ }
20
+ return value;
21
+ }
22
+
23
+ function normalizeCronJobs(rawJobs) {
24
+ if (!Array.isArray(rawJobs)) throw new Error("crons.getJobs() must return an array");
25
+
26
+ const jobs = [];
27
+ const skipped = [];
28
+ rawJobs.forEach((job, index) => {
29
+ if (!job || typeof job !== "object" || Array.isArray(job)) {
30
+ throw new Error(`cron job ${index} must be an object`);
31
+ }
32
+ if (typeof job.action !== "string") {
33
+ skipped.push({
34
+ name: typeof job.name === "string" && job.name.trim() ? job.name : `jobs[${index}]`,
35
+ reason: "inline action is not supported in cloud cron manifest",
36
+ });
37
+ return;
38
+ }
39
+ jobs.push({
40
+ name: readNonEmptyString(job.name, `${index}.name`),
41
+ pattern: readNonEmptyString(job.pattern, `${index}.pattern`),
42
+ action: readNonEmptyString(job.action, `${index}.action`),
43
+ });
44
+ });
45
+
46
+ return { jobs, skipped, skippedInlineCount: skipped.length };
47
+ }
48
+
49
+ function writeFallbackCoreShim(coreTarget, mkdirSyncImpl, writeFileSyncImpl) {
50
+ mkdirSyncImpl(coreTarget, { recursive: true });
51
+ writeFileSyncImpl(
52
+ resolve(coreTarget, "package.json"),
53
+ JSON.stringify({ name: "@gencow/core", type: "module", main: "index.js" }, null, 2),
54
+ );
55
+ writeFileSyncImpl(
56
+ resolve(coreTarget, "index.js"),
57
+ [
58
+ "export function cronJobs() {",
59
+ " const jobs = [];",
60
+ " const builder = {",
61
+ " interval(name, options, action) {",
62
+ " if (options?.seconds) jobs.push({ name, pattern: `*/${options.seconds} * * * * *`, action });",
63
+ " else if (options?.minutes) jobs.push({ name, pattern: `*/${options.minutes} * * * *`, action });",
64
+ " else if (options?.hours) jobs.push({ name, pattern: `0 */${options.hours} * * *`, action });",
65
+ " else throw new Error(\"interval cron requires seconds, minutes, or hours\");",
66
+ " return builder;",
67
+ " },",
68
+ " daily(name, options, action) { jobs.push({ name, pattern: `${options.minute ?? 0} ${options.hour} * * *`, action }); return builder; },",
69
+ " weekly(name, options, action) { jobs.push({ name, pattern: `${options.minute ?? 0} ${options.hour} * * ${options.dayOfWeek}`, action }); return builder; },",
70
+ " cron(name, pattern, action) { jobs.push({ name, pattern, action }); return builder; },",
71
+ " getJobs() { return [...jobs]; },",
72
+ " };",
73
+ " return builder;",
74
+ "}",
75
+ "",
76
+ ].join("\n"),
77
+ );
78
+ }
79
+
80
+ function installCronCoreShim({ workDir, mkdirSyncImpl, resolvePathImpl, writeFileSyncImpl }) {
81
+ const coreTarget = resolvePathImpl(workDir, "node_modules", "@gencow", "core");
82
+ mkdirSyncImpl(dirname(coreTarget), { recursive: true });
83
+ writeFallbackCoreShim(coreTarget, mkdirSyncImpl, writeFileSyncImpl);
84
+ }
85
+
86
+ export async function writeCronManifestForProject({
87
+ cwd,
88
+ functionsDir = "gencow",
89
+ cpSyncImpl = cpSync,
90
+ existsSyncImpl = existsSync,
91
+ mkdirSyncImpl = mkdirSync,
92
+ readFileSyncImpl = readFileSync,
93
+ resolvePathImpl = resolve,
94
+ rmSyncImpl = rmSync,
95
+ writeFileSyncImpl = writeFileSync,
96
+ } = {}) {
97
+ if (!cwd) throw new Error("cwd is required to write cron manifest");
98
+
99
+ const cronsFile = resolveCronDefinitionsFile({ cwd, functionsDir, existsSyncImpl, resolvePathImpl });
100
+ const manifestPath = resolvePathImpl(cwd, CRON_MANIFEST_RELATIVE_PATH);
101
+ if (!cronsFile) {
102
+ rmSyncImpl(manifestPath, { force: true });
103
+ return null;
104
+ }
105
+
106
+ const workDir = resolvePathImpl(cwd, ".gencow", "cron-manifest-workdir");
107
+ const stagedFunctionsDir = resolvePathImpl(workDir, functionsDir);
108
+ rmSyncImpl(workDir, { recursive: true, force: true });
109
+ mkdirSyncImpl(dirname(stagedFunctionsDir), { recursive: true });
110
+
111
+ try {
112
+ cpSyncImpl(resolvePathImpl(cwd, functionsDir), stagedFunctionsDir, { recursive: true });
113
+ installCronCoreShim({ workDir, mkdirSyncImpl, resolvePathImpl, writeFileSyncImpl });
114
+
115
+ const cronsFileName = cronsFile.endsWith(".js") ? "crons.js" : "crons.ts";
116
+ const stagedCronsFile = resolvePathImpl(stagedFunctionsDir, cronsFileName);
117
+ const importUrl = `${pathToFileURL(stagedCronsFile).href}?v=${Date.now()}-${Math.random()}`;
118
+ const module = await import(importUrl);
119
+ const builder = module.default ?? module.crons;
120
+ if (!builder || typeof builder.getJobs !== "function") {
121
+ throw new Error("crons.ts must export default cronJobs() builder");
122
+ }
123
+
124
+ const { jobs, skipped, skippedInlineCount } = normalizeCronJobs(builder.getJobs());
125
+ const sourceHash = createHash("sha256").update(readFileSyncImpl(cronsFile)).digest("hex");
126
+ const manifest = {
127
+ version: 1,
128
+ generatedAt: new Date().toISOString(),
129
+ source: `${functionsDir}/${cronsFileName}`,
130
+ sourceHash: `sha256:${sourceHash}`,
131
+ skipped,
132
+ skippedInlineCount,
133
+ jobs,
134
+ };
135
+
136
+ mkdirSyncImpl(dirname(manifestPath), { recursive: true });
137
+ writeFileSyncImpl(manifestPath, JSON.stringify(manifest, null, 2));
138
+ return { manifestPath, manifest };
139
+ } finally {
140
+ rmSyncImpl(workDir, { recursive: true, force: true });
141
+ }
142
+ }
@@ -10,7 +10,7 @@ export const DEPENDENCY_COMPAT_MATRIX = Object.freeze([
10
10
  },
11
11
  {
12
12
  packageName: "drizzle-orm",
13
- expectedRange: "workspace:* || ^1.0.0-beta || ^1.0.0",
13
+ expectedRange: "workspace:* || ^1.0.0-beta || ^1.0.0-rc || ^1.0.0",
14
14
  installRange: "drizzle-orm@^1.0.0",
15
15
  policy: "block",
16
16
  },
@@ -145,10 +145,7 @@ function clausesCompatible(current, expected) {
145
145
 
146
146
  if (expectedVersion.major === 0) {
147
147
  if (currentVersion.major !== 0 || currentVersion.minor !== expectedVersion.minor) return false;
148
- if (currentVersion.operator === "exact") {
149
- return compareVersions(currentVersion, expectedVersion) >= 0;
150
- }
151
- return true;
148
+ return compareVersions(currentVersion, expectedVersion) >= 0;
152
149
  }
153
150
 
154
151
  if (currentVersion.major !== expectedVersion.major) return false;
@@ -1,6 +1,7 @@
1
1
  import { execSync } from "child_process";
2
2
  import { existsSync, mkdirSync, readFileSync, statSync, unlinkSync, writeFileSync } from "fs";
3
3
  import { dirname, resolve } from "path";
4
+ import { CRON_MANIFEST_RELATIVE_PATH, writeCronManifestForProject } from "./cron-manifest.mjs";
4
5
  import { CYAN, DIM, RED, RESET, YELLOW, error, info, log, success, warn } from "./output.mjs";
5
6
  import { platformFetch, rpcMutation } from "./platform-client.mjs";
6
7
 
@@ -49,6 +50,7 @@ export function createDeployPackageRuntime({
49
50
  unlinkSyncImpl = unlinkSync,
50
51
  verifyAppReadyImpl,
51
52
  warnImpl = warn,
53
+ writeCronManifestForProjectImpl = writeCronManifestForProject,
52
54
  writeFileSyncImpl = writeFileSync,
53
55
  updateEnvLocalUrlImpl,
54
56
  } = {}) {
@@ -163,6 +165,16 @@ export function createDeployPackageRuntime({
163
165
  return null;
164
166
  }
165
167
 
168
+ let cronManifestPath = null;
169
+ try {
170
+ const cronManifest = await writeCronManifestForProjectImpl({ cwd, functionsDir: "gencow" });
171
+ cronManifestPath = cronManifest?.manifestPath ?? null;
172
+ } catch (caught) {
173
+ errorImpl(`Cron manifest generation failed: ${caught.message}`);
174
+ exitImpl(1);
175
+ return null;
176
+ }
177
+
166
178
  let auditResult = null;
167
179
  if (!forceDeploy) {
168
180
  try {
@@ -181,6 +193,7 @@ export function createDeployPackageRuntime({
181
193
  let useFilteredPkg = false;
182
194
  const filesToPack = ["gencow/", "package.json"];
183
195
  if (dependencyAuditManifestPath) filesToPack.push(".gencow/deploy-dependency-audit.json");
196
+ if (cronManifestPath) filesToPack.push(CRON_MANIFEST_RELATIVE_PATH);
184
197
  if (existsSyncImpl(resolvePathImpl(cwd, "bun.lockb"))) filesToPack.push("bun.lockb");
185
198
  if (existsSyncImpl(resolvePathImpl(cwd, "package-lock.json"))) filesToPack.push("package-lock.json");
186
199
  if (existsSyncImpl(resolvePathImpl(cwd, "tsconfig.json"))) filesToPack.push("tsconfig.json");
@@ -2,6 +2,7 @@ import { copyFileSync, cpSync, existsSync, mkdirSync, readFileSync, rmSync, writ
2
2
  import { dirname, resolve } from "path";
3
3
  import { create as tarCreate } from "tar";
4
4
 
5
+ import { CRON_MANIFEST_RELATIVE_PATH, writeCronManifestForProject } from "./cron-manifest.mjs";
5
6
  import {
6
7
  DEPLOY_DEPENDENCY_AUDIT_MANIFEST,
7
8
  auditDependencyVersions,
@@ -78,6 +79,7 @@ export async function createDevCloudDeployBundle({
78
79
  rmSyncImpl = rmSync,
79
80
  tarCreateImpl = tarCreate,
80
81
  warnImpl = () => {},
82
+ writeCronManifestForProjectImpl = writeCronManifestForProject,
81
83
  writeFileSyncImpl = writeFileSync,
82
84
  } = {}) {
83
85
  if (!cwd || !functionsDir) throw new Error("cwd and functionsDir are required to create a dev cloud bundle");
@@ -112,6 +114,9 @@ export async function createDevCloudDeployBundle({
112
114
  mkdirSyncImpl(dirname(manifestPath), { recursive: true });
113
115
  writeFileSyncImpl(manifestPath, JSON.stringify(dependencyVersionResult.manifest, null, 2));
114
116
 
117
+ const cronManifest = await writeCronManifestForProjectImpl({ cwd: stagingDir, functionsDir });
118
+ if (cronManifest) filesToPack.push(CRON_MANIFEST_RELATIVE_PATH);
119
+
115
120
  const filteredPackage = buildFilteredPackageJson(projectPkg, auditResult?.runtimeDeps);
116
121
  writeFileSyncImpl(resolvePathImpl(stagingDir, "package.json"), JSON.stringify(filteredPackage, null, 2));
117
122
 
@@ -27,8 +27,8 @@ const BASE_DEPS = {
27
27
  "@gencow/core": "latest",
28
28
  "@electric-sql/pglite": "^0.3.15",
29
29
  "better-auth": "^1.5.1",
30
- "drizzle-orm": "^1.0.0-beta || ^1.0.0",
31
- "drizzle-kit": "^1.0.0-beta || ^1.0.0",
30
+ "drizzle-orm": "^1.0.0-beta || ^1.0.0-rc || ^1.0.0",
31
+ "drizzle-kit": "^1.0.0-beta || ^1.0.0-rc || ^1.0.0",
32
32
  hono: "^4.12.0",
33
33
  postgres: "^3.4.8",
34
34
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gencow",
3
- "version": "0.1.147",
3
+ "version": "0.1.148",
4
4
  "description": "Gencow — AI Backend Engine",
5
5
  "type": "module",
6
6
  "bin": {