gencow 0.1.247 → 0.1.249

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.
Files changed (46) hide show
  1. package/bin/gencow.mjs +4 -15
  2. package/core/index.js +255 -77
  3. package/core/package.json +1 -1
  4. package/dashboard/assets/index-DvEb2ey9.css +1 -0
  5. package/dashboard/assets/index-PfrmDrvH.js +401 -0
  6. package/dashboard/index.html +2 -2
  7. package/lib/backup-command.mjs +11 -0
  8. package/lib/cli-command-runner.mjs +23 -0
  9. package/lib/cli-project-runtime.mjs +12 -3
  10. package/lib/codegen-command.mjs +25 -9
  11. package/lib/dashboard-assets.mjs +22 -0
  12. package/lib/db-command.mjs +17 -15
  13. package/lib/deploy-auditor.mjs +15 -2
  14. package/lib/deploy-bundle-staging.mjs +1 -1
  15. package/lib/deploy-bundle-summary.mjs +47 -0
  16. package/lib/deploy-command.mjs +3 -0
  17. package/lib/deploy-failure-diagnostic.mjs +212 -8
  18. package/lib/deploy-package-runtime.mjs +25 -30
  19. package/lib/deploy-runtime-asset-audit.mjs +81 -0
  20. package/lib/deploy-runtime-path-audit.mjs +81 -0
  21. package/lib/deploy-runtime.mjs +0 -4
  22. package/lib/deploy-static-fullstack-runtime.mjs +0 -5
  23. package/lib/deployment-artifact-provenance.mjs +9 -4
  24. package/lib/deployment-operation-poll.mjs +44 -0
  25. package/lib/dev-cloud-bundle.mjs +11 -0
  26. package/lib/dev-cloud-migrations.mjs +0 -14
  27. package/lib/dev-local-command.mjs +8 -19
  28. package/lib/doctor-command.mjs +36 -27
  29. package/lib/jobs-command.d.mts +14 -0
  30. package/lib/jobs-command.mjs +51 -7
  31. package/lib/migration-diagnostic.mjs +4 -1
  32. package/lib/platform-client.mjs +121 -2
  33. package/lib/procedure-rpc-diagnostic.mjs +47 -0
  34. package/lib/project-migration-manifest.mjs +1 -0
  35. package/lib/release-client.mjs +18 -0
  36. package/lib/runtime-bom-diagnostic.mjs +155 -0
  37. package/lib/runtime-bom-resolve.mjs +275 -212
  38. package/lib/runtime-command.mjs +34 -32
  39. package/lib/ts-codegen-import-extension.mjs +329 -0
  40. package/package.json +3 -3
  41. package/runtime/config.mjs +5 -2
  42. package/runtime/server.mjs +29543 -21790
  43. package/runtime/tooling.mjs +46 -12
  44. package/scripts/bundle-server.mjs +2 -9
  45. package/dashboard/assets/index-BIg8EAY8.css +0 -1
  46. package/dashboard/assets/index-CcD-YPfN.js +0 -408
package/bin/gencow.mjs CHANGED
@@ -16,7 +16,7 @@
16
16
  * gencow help — show this help
17
17
  */
18
18
 
19
- import { execSync, spawn } from "child_process";
19
+ import { execSync } from "child_process";
20
20
  import { existsSync, readFileSync, writeFileSync, unlinkSync, cpSync, symlinkSync, copyFileSync } from "fs";
21
21
  import { resolve, dirname } from "path";
22
22
  import { fileURLToPath } from "url";
@@ -146,7 +146,9 @@ const runDoctorCommand = createDoctorCommand({
146
146
  warnImpl: warn,
147
147
  errorImpl: error,
148
148
  successImpl: success,
149
- exitImpl: (code) => process.exit(code),
149
+ exitImpl: (code) => {
150
+ process.exitCode = code;
151
+ },
150
152
  cwdImpl: () => process.cwd(),
151
153
  BOLD,
152
154
  CYAN,
@@ -546,19 +548,6 @@ ${BOLD}Examples:${RESET}
546
548
  // ── env — 환경변수 관리 (Cloud only) ─────────
547
549
  env: runEnvCommand,
548
550
 
549
- async platform() {
550
- log(`\n${BOLD}${CYAN}Gencow Platform${RESET} ${DIM}— Control Plane${RESET}\n`);
551
- info("Starting Control Plane on port 4000...");
552
- const cwd = process.cwd();
553
- const proc = spawn("pnpm", ["--filter", "@gencow/platform", "dev"], {
554
- cwd,
555
- stdio: "inherit",
556
- shell: true,
557
- });
558
- proc.on("exit", (code) => process.exit(code ?? 0));
559
- process.on("SIGINT", () => proc.kill());
560
- },
561
-
562
551
  // ── dev:cloud (watch + auto-deploy + live log streaming) ──
563
552
  "dev:cloud": runDevCloudCommand,
564
553
 
package/core/index.js CHANGED
@@ -2256,11 +2256,11 @@ async function countWorkflowV1Backlog(db, workflowName) {
2256
2256
  pendingByName: numberFrom(row?.pending_by_name)
2257
2257
  };
2258
2258
  }
2259
- async function countWorkflowBacklog(db, workflowName) {
2259
+ async function countWorkflowBacklog(db, workflowName, requireV2 = false) {
2260
2260
  try {
2261
2261
  return await countWorkflowV2Backlog(db, workflowName);
2262
2262
  } catch (error) {
2263
- if (!isMissingWorkflowV2SchemaError(error)) throw error;
2263
+ if (!isMissingWorkflowV2SchemaError(error) || requireV2) throw error;
2264
2264
  return countWorkflowV1Backlog(db, workflowName);
2265
2265
  }
2266
2266
  }
@@ -2301,7 +2301,7 @@ async function applyWorkflowStartAdmission(options) {
2301
2301
  });
2302
2302
  }
2303
2303
  await acquireWorkflowStartAdmissionLock(options.db);
2304
- const counts = await countWorkflowBacklog(options.db, options.workflowName);
2304
+ const counts = await countWorkflowBacklog(options.db, options.workflowName, options.requireV2);
2305
2305
  if (counts.pending >= policy.pendingPerApp) {
2306
2306
  throw new JobAdmissionError("job_backlog_limit_exceeded", "workflow pending backlog limit exceeded", {
2307
2307
  details: { limit: policy.pendingPerApp, pending: counts.pending }
@@ -2949,11 +2949,13 @@ function parseJsonField(value) {
2949
2949
  }
2950
2950
  }
2951
2951
  function toIsoString(value) {
2952
- if (value instanceof Date) return value.toISOString();
2952
+ if (typeof value === "number" && !Number.isFinite(value)) return String(/* @__PURE__ */ new Date(NaN));
2953
+ if (value instanceof Date) return Number.isFinite(value.getTime()) ? value.toISOString() : String(value);
2953
2954
  const parsed = new Date(value);
2954
2955
  return Number.isFinite(parsed.getTime()) ? parsed.toISOString() : String(value);
2955
2956
  }
2956
2957
  function toOptionalIsoString(value) {
2958
+ if (typeof value === "number" && !Number.isFinite(value)) return toIsoString(value);
2957
2959
  return value ? toIsoString(value) : null;
2958
2960
  }
2959
2961
  function publicV2Status(status) {
@@ -3628,6 +3630,7 @@ var RESERVED_TENANT_RUNTIME_ENV_KEYS = [
3628
3630
  "GENCOW_FUNCTIONS",
3629
3631
  "GENCOW_STORAGE",
3630
3632
  "GENCOW_STORAGE_REQUIRE_READ_GRANT",
3633
+ "GENCOW_LOCAL_STORAGE_READ_GRANT_SECRET",
3631
3634
  "GENCOW_STORAGE_BACKEND",
3632
3635
  "GENCOW_STORAGE_LEGACY_LOCAL_FALLBACK",
3633
3636
  "GENCOW_STORAGE_QUOTA_BYTES",
@@ -4097,8 +4100,157 @@ import { pgTable } from "drizzle-orm/pg-core";
4097
4100
  var pgBaseTable = pgTable;
4098
4101
 
4099
4102
  // ../core/src/rls-db.ts
4100
- import { AsyncLocalStorage } from "node:async_hooks";
4101
4103
  import { sql as sql7 } from "drizzle-orm";
4104
+
4105
+ // ../core/src/rls-db-scoped-query.ts
4106
+ import { AsyncLocalStorage } from "node:async_hooks";
4107
+ import { NoopCache } from "drizzle-orm/cache/core";
4108
+ import { RelationalQueryBuilder as PgRelationalQueryBuilder } from "drizzle-orm/pg-core/query-builders/query";
4109
+ import { PgDialect } from "drizzle-orm/pg-core/dialect";
4110
+ import { PgAsyncRelationalQuery } from "drizzle-orm/pg-core/async/query";
4111
+ var activeRlsExecution = new AsyncLocalStorage();
4112
+ function createRlsDiagnosticError(message, diagnostic) {
4113
+ return Object.assign(new Error(message), diagnostic);
4114
+ }
4115
+ function createRlsScopeToken() {
4116
+ return {};
4117
+ }
4118
+ function createRlsScopeConflictError() {
4119
+ return createRlsDiagnosticError("A query cannot switch database scopes while an RLS operation is active.", {
4120
+ code: "RLS_SCOPE_CONFLICT",
4121
+ status: 409,
4122
+ stage: "rls-query-execution",
4123
+ capability: "rls-scoped-query",
4124
+ stateImpact: "Rejected query executed no tenant SQL; enclosing operation may still finish independently",
4125
+ retryable: false,
4126
+ retryPrecondition: "Invoke only a database handle matching the active RLS scope, or wait until the active RLS execution completes.",
4127
+ nextAction: "EDIT_AND_CHECK"
4128
+ });
4129
+ }
4130
+ var unsupportedByReason = {
4131
+ cache: {
4132
+ message: "RLS-scoped queries do not support a shared Drizzle result cache.",
4133
+ retryPrecondition: "Remove the shared Drizzle cache configuration before retrying; shared result caches are not supported by RLS-scoped queries."
4134
+ },
4135
+ relations: {
4136
+ message: "This relational builder is not from the verified Drizzle v1 integration.",
4137
+ retryPrecondition: "Check the installed version with `pnpm why drizzle-orm`; use verified drizzle-orm@1.0.0-rc.4 without custom relational builders before retrying."
4138
+ },
4139
+ session: {
4140
+ message: "This Drizzle session cannot be safely bound to the transaction-scoped RLS client.",
4141
+ retryPrecondition: "Check the installed version with `pnpm why drizzle-orm`; use a verified Drizzle driver and session before retrying."
4142
+ }
4143
+ };
4144
+ function createRlsUnsupportedError(reason = "relations") {
4145
+ return createRlsDiagnosticError(unsupportedByReason[reason].message, {
4146
+ code: "RLS_DRIZZLE_INTEGRATION_UNSUPPORTED",
4147
+ status: 409,
4148
+ stage: "rls-query-setup",
4149
+ capability: "rls-scoped-query",
4150
+ stateImpact: "This rejected setup executed no tenant SQL and made no database state change",
4151
+ retryable: false,
4152
+ retryPrecondition: unsupportedByReason[reason].retryPrecondition,
4153
+ nextAction: "EDIT_AND_CHECK"
4154
+ });
4155
+ }
4156
+ function assertRlsScopeMayEnter(scope) {
4157
+ const active = activeRlsExecution.getStore();
4158
+ if (active?.active && active.scope !== scope) {
4159
+ throw createRlsScopeConflictError();
4160
+ }
4161
+ }
4162
+ function currentRlsExecution(scope) {
4163
+ const active = activeRlsExecution.getStore();
4164
+ if (!active?.active) return void 0;
4165
+ if (active.scope !== scope) throw createRlsScopeConflictError();
4166
+ return active;
4167
+ }
4168
+ function matchingRlsExecutionClient(scope) {
4169
+ const active = activeRlsExecution.getStore();
4170
+ return active?.active && active.scope === scope ? active.client : void 0;
4171
+ }
4172
+ async function runWithRlsScope(scope, client, work) {
4173
+ const active = { scope, client, active: true };
4174
+ return activeRlsExecution.run(active, async () => {
4175
+ try {
4176
+ return await work();
4177
+ } finally {
4178
+ active.active = false;
4179
+ }
4180
+ });
4181
+ }
4182
+ function runWithRlsTransactionScope(scope, tx, work) {
4183
+ assertRlsScopeMayEnter(scope);
4184
+ return runWithRlsScope(scope, tx.session?.client, work);
4185
+ }
4186
+ function createRlsRelationalQueryRegistry(query2, session, baseSession) {
4187
+ const scopedRegistry = /* @__PURE__ */ Object.create(null);
4188
+ for (const tableName of Reflect.ownKeys(query2)) {
4189
+ const descriptor = Object.getOwnPropertyDescriptor(query2, tableName);
4190
+ if (typeof tableName !== "string" || !descriptor || !descriptor.enumerable || !("value" in descriptor)) {
4191
+ throw createRlsUnsupportedError("relations");
4192
+ }
4193
+ const builder = descriptor.value;
4194
+ if (!builder || typeof builder !== "object" || builder.session !== baseSession || !builder.schema || typeof builder.schema !== "object" || !builder.table || typeof builder.table !== "object" || !builder.tableConfig || typeof builder.tableConfig !== "object" || !builder.dialect || typeof builder.dialect !== "object" || Object.getPrototypeOf(builder.dialect) !== PgDialect.prototype || Reflect.get(builder.dialect, "constructor") !== PgDialect || typeof builder.parseJson !== "boolean" || builder.builder !== PgAsyncRelationalQuery || Object.getPrototypeOf(builder) !== PgRelationalQueryBuilder.prototype || Reflect.get(builder, "constructor") !== PgRelationalQueryBuilder || Reflect.get(builder, "findMany") !== PgRelationalQueryBuilder.prototype.findMany || Reflect.get(builder, "findFirst") !== PgRelationalQueryBuilder.prototype.findFirst) {
4195
+ throw createRlsUnsupportedError("relations");
4196
+ }
4197
+ const scopedBuilder = Reflect.construct(PgRelationalQueryBuilder, [
4198
+ builder.schema,
4199
+ builder.table,
4200
+ builder.tableConfig,
4201
+ builder.dialect,
4202
+ session,
4203
+ builder.parseJson,
4204
+ PgAsyncRelationalQuery
4205
+ ]);
4206
+ const trustedFindMany = PgRelationalQueryBuilder.prototype.findMany;
4207
+ const trustedFindFirst = PgRelationalQueryBuilder.prototype.findFirst;
4208
+ Object.defineProperties(scopedBuilder, {
4209
+ findMany: {
4210
+ value: function(config) {
4211
+ return Reflect.apply(trustedFindMany, this, [config]);
4212
+ }
4213
+ },
4214
+ findFirst: {
4215
+ value: function(config) {
4216
+ return Reflect.apply(trustedFindFirst, this, [config]);
4217
+ }
4218
+ }
4219
+ });
4220
+ Object.freeze(scopedBuilder);
4221
+ Object.defineProperty(scopedRegistry, tableName, {
4222
+ value: scopedBuilder,
4223
+ enumerable: true,
4224
+ configurable: false,
4225
+ writable: false
4226
+ });
4227
+ }
4228
+ return Object.freeze(scopedRegistry);
4229
+ }
4230
+ function isVerifiedNoopCache(cache) {
4231
+ if (!cache || typeof cache !== "object") return false;
4232
+ const candidate = cache;
4233
+ const ownKeys = Reflect.ownKeys(cache);
4234
+ const ownPropertiesAreExpected = ownKeys.every((key) => {
4235
+ if (key !== "invalidate") return false;
4236
+ const descriptor = Object.getOwnPropertyDescriptor(cache, key);
4237
+ return descriptor && "value" in descriptor && descriptor.value === NoopCache.prototype.onMutate;
4238
+ });
4239
+ return candidate.constructor === NoopCache && Object.getPrototypeOf(cache) === NoopCache.prototype && ownPropertiesAreExpected && candidate.strategy === NoopCache.prototype.strategy && candidate.get === NoopCache.prototype.get && candidate.put === NoopCache.prototype.put && candidate.onMutate === NoopCache.prototype.onMutate;
4240
+ }
4241
+ function hasUnpartitionedRlsCache(db, session) {
4242
+ const databaseCache = db?.$cache;
4243
+ const sessionCache = session?.cache;
4244
+ const hasResultCacheSurface = (cache) => {
4245
+ if (!cache || typeof cache !== "object") return false;
4246
+ return ["strategy", "get", "put"].some((method) => typeof Reflect.get(cache, method) === "function");
4247
+ };
4248
+ const hasUnverifiedSessionCache = sessionCache !== void 0 && sessionCache !== null && !isVerifiedNoopCache(sessionCache);
4249
+ const hasUnverifiedDatabaseCache = hasResultCacheSurface(databaseCache) && !isVerifiedNoopCache(databaseCache);
4250
+ return hasUnverifiedSessionCache || hasUnverifiedDatabaseCache;
4251
+ }
4252
+
4253
+ // ../core/src/rls-db.ts
4102
4254
  var TRANSACTION_ISOLATION_LEVELS = /* @__PURE__ */ new Set([
4103
4255
  "read uncommitted",
4104
4256
  "read committed",
@@ -4140,11 +4292,6 @@ async function forEachSetConfig(rls, setOne) {
4140
4292
  await setOne(name, value);
4141
4293
  }
4142
4294
  }
4143
- var rlsExecClient = new AsyncLocalStorage();
4144
- function isBunSqlSession(session) {
4145
- const name = session?.constructor?.name;
4146
- return typeof name === "string" && name.includes("BunSQLSession");
4147
- }
4148
4295
  function isDrizzleTransactionDb(db) {
4149
4296
  const d = db;
4150
4297
  if (typeof d?.rollback === "function") {
@@ -4178,11 +4325,11 @@ async function injectRlsVarsOnTx(tx, rls, onTransactionContextFailure) {
4178
4325
  throw error;
4179
4326
  }
4180
4327
  }
4181
- async function withRlsLeasedConnection(leased, rls, fn) {
4328
+ async function withRlsLeasedConnection(leased, rls, fn, scope) {
4182
4329
  try {
4183
4330
  await leased.query("begin");
4184
4331
  await applyRlsSessionVars(leased, rls);
4185
- const result = await rlsExecClient.run(leased, () => fn(leased));
4332
+ const result = scope ? await runWithRlsScope(scope, leased, () => fn(leased)) : await fn(leased);
4186
4333
  await leased.query("commit");
4187
4334
  return result;
4188
4335
  } catch (e) {
@@ -4195,19 +4342,20 @@ async function withRlsLeasedConnection(leased, rls, fn) {
4195
4342
  leased.release();
4196
4343
  }
4197
4344
  }
4198
- async function withRlsConnection(session, rls, reuseOuterConnection, fn) {
4345
+ async function withRlsConnection(session, rls, reuseOuterConnection, scope, fn) {
4346
+ assertRlsScopeMayEnter(scope);
4199
4347
  if (reuseOuterConnection) {
4200
4348
  const c2 = session.client;
4201
- return rlsExecClient.run(c2, async () => {
4349
+ return runWithRlsScope(scope, c2, async () => {
4202
4350
  await applyRlsSessionVars(c2, rls);
4203
4351
  return fn(c2);
4204
4352
  });
4205
4353
  }
4206
4354
  const c = session.client;
4207
- const runInner = async (client) => {
4355
+ const runInner = async (client) => runWithRlsScope(scope, client, async () => {
4208
4356
  await applyRlsSessionVars(client, rls);
4209
- return rlsExecClient.run(client, () => fn(client));
4210
- };
4357
+ return fn(client);
4358
+ });
4211
4359
  if (typeof c.begin === "function") {
4212
4360
  return c.begin(runInner);
4213
4361
  }
@@ -4216,7 +4364,7 @@ async function withRlsConnection(session, rls, reuseOuterConnection, fn) {
4216
4364
  }
4217
4365
  if (typeof c.connect === "function" && typeof c.query === "function") {
4218
4366
  const leased = await c.connect();
4219
- return withRlsLeasedConnection(leased, rls, fn);
4367
+ return withRlsLeasedConnection(leased, rls, fn, scope);
4220
4368
  }
4221
4369
  return runInner(c);
4222
4370
  }
@@ -4231,29 +4379,7 @@ function createClientBoundSession(session, client) {
4231
4379
  }
4232
4380
  });
4233
4381
  }
4234
- async function withPreparedQueryClient(pq, client, fn) {
4235
- if (!("client" in pq)) {
4236
- return await fn();
4237
- }
4238
- const prevPq = pq.client;
4239
- pq.client = client;
4240
- try {
4241
- return await fn();
4242
- } finally {
4243
- pq.client = prevPq;
4244
- }
4245
- }
4246
- function createGenericPreparedQueryClientExecutors(pq) {
4247
- const origExecute = pq.execute.bind(pq);
4248
- const origAll = typeof pq.all === "function" ? pq.all.bind(pq) : null;
4249
- return {
4250
- execute(client, placeholderValues) {
4251
- return withPreparedQueryClient(pq, client, () => origExecute(placeholderValues));
4252
- },
4253
- all: origAll ? (client, placeholderValues) => withPreparedQueryClient(pq, client, () => origAll(placeholderValues)) : void 0
4254
- };
4255
- }
4256
- function createBunSqlPreparedQueryClientExecutors(baseSession, prepareQuery, prepareArgs) {
4382
+ function createSessionBoundPreparedQueryClientExecutors(pq, baseSession, prepareQuery, prepareArgs) {
4257
4383
  const reprepareOnClient = (client) => {
4258
4384
  const clientBoundSession = createClientBoundSession(baseSession, client);
4259
4385
  return Reflect.apply(prepareQuery, clientBoundSession, prepareArgs);
@@ -4262,60 +4388,74 @@ function createBunSqlPreparedQueryClientExecutors(baseSession, prepareQuery, pre
4262
4388
  execute(client, placeholderValues) {
4263
4389
  return reprepareOnClient(client).execute(placeholderValues);
4264
4390
  },
4265
- all(client, placeholderValues) {
4391
+ all: typeof pq.all === "function" ? (client, placeholderValues) => {
4266
4392
  const txPreparedQuery = reprepareOnClient(client);
4267
4393
  if (typeof txPreparedQuery.all === "function") {
4268
4394
  return txPreparedQuery.all(placeholderValues);
4269
4395
  }
4270
4396
  return txPreparedQuery.execute(placeholderValues);
4271
- }
4397
+ } : void 0
4272
4398
  };
4273
4399
  }
4274
4400
  function createPreparedQueryClientExecutors(params) {
4275
- if (isBunSqlSession(params.baseSession)) {
4276
- return createBunSqlPreparedQueryClientExecutors(
4277
- params.baseSession,
4278
- params.prepareQuery,
4279
- params.prepareArgs
4280
- );
4281
- }
4282
- return createGenericPreparedQueryClientExecutors(params.pq);
4401
+ return createSessionBoundPreparedQueryClientExecutors(
4402
+ params.pq,
4403
+ params.baseSession,
4404
+ params.prepareQuery,
4405
+ params.prepareArgs
4406
+ );
4283
4407
  }
4284
- async function runPreparedQueryWithRls(session, rls, reuseOuterConnection, runOnClient, placeholderValues) {
4285
- const active = rlsExecClient.getStore();
4408
+ async function runPreparedQueryWithRls(session, rls, reuseOuterConnection, scope, runOnClient, placeholderValues) {
4409
+ const active = currentRlsExecution(scope);
4286
4410
  if (active) {
4287
- return runOnClient(active, placeholderValues);
4411
+ return runOnClient(active.client, placeholderValues);
4288
4412
  }
4289
4413
  return withRlsConnection(
4290
4414
  session,
4291
4415
  rls,
4292
4416
  reuseOuterConnection,
4417
+ scope,
4293
4418
  (client) => runOnClient(client, placeholderValues)
4294
4419
  );
4295
4420
  }
4296
- function wrapPreparedQuery(pq, session, baseSession, prepareQuery, prepareArgs, rls, reuseOuterConnection) {
4421
+ function wrapPreparedQuery(pq, session, baseSession, prepareQuery, prepareArgs, rls, reuseOuterConnection, scope) {
4297
4422
  const clientExecutors = createPreparedQueryClientExecutors({
4298
4423
  pq,
4299
4424
  baseSession,
4300
4425
  prepareQuery,
4301
4426
  prepareArgs
4302
4427
  });
4303
- pq.execute = (placeholderValues) => runPreparedQueryWithRls(session, rls, reuseOuterConnection, clientExecutors.execute, placeholderValues);
4428
+ pq.execute = (placeholderValues) => runPreparedQueryWithRls(
4429
+ session,
4430
+ rls,
4431
+ reuseOuterConnection,
4432
+ scope,
4433
+ clientExecutors.execute,
4434
+ placeholderValues
4435
+ );
4304
4436
  if (clientExecutors.all) {
4305
- pq.all = (placeholderValues) => runPreparedQueryWithRls(session, rls, reuseOuterConnection, clientExecutors.all, placeholderValues);
4437
+ pq.all = (placeholderValues) => runPreparedQueryWithRls(
4438
+ session,
4439
+ rls,
4440
+ reuseOuterConnection,
4441
+ scope,
4442
+ clientExecutors.all,
4443
+ placeholderValues
4444
+ );
4306
4445
  }
4307
4446
  }
4308
- function wrapSession(session, rls, reuseOuterConnection) {
4447
+ function wrapSession(session, rls, reuseOuterConnection, scope) {
4309
4448
  return new Proxy(session, {
4310
4449
  get(sTarget, sProp, sRecv) {
4311
4450
  if (sProp === "client") {
4312
- return rlsExecClient.getStore() ?? Reflect.get(sTarget, sProp, sRecv);
4451
+ return matchingRlsExecutionClient(scope) ?? Reflect.get(sTarget, sProp, sRecv);
4313
4452
  }
4314
4453
  if (sProp === "prepareQuery") {
4315
4454
  return (...args) => {
4316
4455
  const prepareQuery = Reflect.get(sTarget, sProp, sRecv);
4456
+ if (typeof prepareQuery !== "function") throw createRlsUnsupportedError("session");
4317
4457
  const pq = Reflect.apply(prepareQuery, sRecv, args);
4318
- wrapPreparedQuery(pq, sRecv, sTarget, prepareQuery, args, rls, reuseOuterConnection);
4458
+ wrapPreparedQuery(pq, sRecv, sTarget, prepareQuery, args, rls, reuseOuterConnection, scope);
4319
4459
  return pq;
4320
4460
  };
4321
4461
  }
@@ -4326,15 +4466,32 @@ function wrapSession(session, rls, reuseOuterConnection) {
4326
4466
  }
4327
4467
  function createRlsDb(db, rls, options = {}) {
4328
4468
  const reuseOuterConnection = isDrizzleTransactionDb(db);
4329
- const baseSession = db.session;
4330
- const wrappedSession = wrapSession(baseSession, rls, reuseOuterConnection);
4469
+ const database = db;
4470
+ const baseSession = database.session ?? database._?.session;
4471
+ if (!baseSession || typeof baseSession !== "object") {
4472
+ throw createRlsUnsupportedError("session");
4473
+ }
4474
+ if (hasUnpartitionedRlsCache(db, baseSession)) {
4475
+ throw createRlsUnsupportedError("cache");
4476
+ }
4477
+ const scope = createRlsScopeToken();
4478
+ const wrappedSession = wrapSession(baseSession, rls, reuseOuterConnection, scope);
4479
+ const baseQuery = Reflect.get(db, "query", db);
4480
+ const scopedQuery = baseQuery && typeof baseQuery === "object" ? createRlsRelationalQueryRegistry(baseQuery, wrappedSession, baseSession) : baseQuery;
4331
4481
  return new Proxy(db, {
4332
4482
  get(target, prop, receiver) {
4483
+ if (prop === "query") {
4484
+ return scopedQuery;
4485
+ }
4486
+ if (prop === "$client") {
4487
+ return Reflect.get(target, prop, target);
4488
+ }
4333
4489
  if (prop === "session") {
4334
4490
  return wrappedSession;
4335
4491
  }
4336
4492
  if (prop === "_") {
4337
- const inner = target._;
4493
+ const inner = Reflect.get(target, prop, receiver);
4494
+ if (!inner || typeof inner !== "object") return inner;
4338
4495
  return new Proxy(inner, {
4339
4496
  get(i, p, r) {
4340
4497
  if (p === "session") return wrappedSession;
@@ -4344,6 +4501,7 @@ function createRlsDb(db, rls, options = {}) {
4344
4501
  }
4345
4502
  if (prop === "transaction") {
4346
4503
  return async (callback, ...rest) => {
4504
+ assertRlsScopeMayEnter(scope);
4347
4505
  let callbackStarted = false;
4348
4506
  const configStatement = transactionConfigSql(rest[0]);
4349
4507
  const transactionArgs = configStatement ? [] : rest;
@@ -4353,7 +4511,7 @@ function createRlsDb(db, rls, options = {}) {
4353
4511
  if (configStatement) await tx.execute(configStatement);
4354
4512
  await injectRlsVarsOnTx(tx, rls, options.onTransactionContextFailure);
4355
4513
  callbackStarted = true;
4356
- return await callback(tx);
4514
+ return await runWithRlsTransactionScope(scope, tx, () => callback(tx));
4357
4515
  },
4358
4516
  ...transactionArgs
4359
4517
  );
@@ -4371,13 +4529,14 @@ function createRlsDb(db, rls, options = {}) {
4371
4529
  }
4372
4530
  if (prop === "transactionWithCharacteristics") {
4373
4531
  return async (characteristics, callback) => {
4532
+ assertRlsScopeMayEnter(scope);
4374
4533
  return await target.transaction(async (tx) => {
4375
4534
  await applyTransactionConfig(tx, {
4376
4535
  ...characteristics?.isolationLevel ? { isolationLevel: characteristics.isolationLevel } : {},
4377
4536
  ...characteristics?.readOnly ? { accessMode: "read only" } : {}
4378
4537
  });
4379
4538
  await injectRlsVarsOnTx(tx, rls, options.onTransactionContextFailure);
4380
- return callback(tx);
4539
+ return runWithRlsTransactionScope(scope, tx, () => callback(tx));
4381
4540
  });
4382
4541
  };
4383
4542
  }
@@ -4401,6 +4560,7 @@ import {
4401
4560
  ilike,
4402
4561
  inArray,
4403
4562
  notInArray,
4563
+ isNull,
4404
4564
  or,
4405
4565
  and,
4406
4566
  count as drizzleCount,
@@ -4451,6 +4611,13 @@ function detectOwnerMeta(table) {
4451
4611
  }
4452
4612
  try {
4453
4613
  const config = getTableConfig(table);
4614
+ const declared = getOwnerRlsMeta(table);
4615
+ if (declared) {
4616
+ const propertyName = resolvePropertyName(table, declared.columnName);
4617
+ const column = anyTable[propertyName];
4618
+ if (column)
4619
+ return { column, columnName: declared.columnName, propertyName, readPublic: declared.readPublic };
4620
+ }
4454
4621
  if (config.policies && config.policies.length > 0) {
4455
4622
  const propName = anyTable["userId"] ? "userId" : anyTable["user_id"] ? "user_id" : null;
4456
4623
  if (propName) {
@@ -4571,7 +4738,7 @@ function crud(table, options) {
4571
4738
  const conditions = [];
4572
4739
  if (options?.softDelete) {
4573
4740
  const sdField = anyTable[options.softDelete.field];
4574
- conditions.push(eq(sdField, null));
4741
+ conditions.push(isNull(sdField));
4575
4742
  }
4576
4743
  if (args?.search && options?.searchFields?.length) {
4577
4744
  const searchConds = options.searchFields.map(
@@ -4662,7 +4829,7 @@ function crud(table, options) {
4662
4829
  }
4663
4830
  if (options?.softDelete) {
4664
4831
  const sdField = anyTable[options.softDelete.field];
4665
- whereCond = and(whereCond, eq(sdField, null));
4832
+ whereCond = and(whereCond, isNull(sdField));
4666
4833
  }
4667
4834
  return await inRlsOrPlainTx(ctx.db, async (tx) => {
4668
4835
  const [result] = await tx.select().from(anyTable).where(whereCond).limit(1);
@@ -4775,6 +4942,7 @@ import {
4775
4942
  ilike as ilike3,
4776
4943
  or as or3,
4777
4944
  and as and3,
4945
+ isNull as isNull2,
4778
4946
  count as drizzleCount2,
4779
4947
  getTableName as getTableName4,
4780
4948
  getTableColumns as getTableColumns3
@@ -4956,6 +5124,13 @@ function detectOwnerMeta2(table) {
4956
5124
  }
4957
5125
  try {
4958
5126
  const config = getTableConfig2(table);
5127
+ const declared = getOwnerRlsMeta(table);
5128
+ if (declared) {
5129
+ const propertyName = resolvePropertyName2(table, declared.columnName);
5130
+ const column = anyTable[propertyName];
5131
+ if (column)
5132
+ return { column, columnName: declared.columnName, propertyName, readPublic: declared.readPublic };
5133
+ }
4959
5134
  if (config.policies && config.policies.length > 0) {
4960
5135
  const propName = anyTable["userId"] ? "userId" : anyTable["user_id"] ? "user_id" : null;
4961
5136
  if (propName) {
@@ -5290,7 +5465,7 @@ function buildCrud(table, options, procedure) {
5290
5465
  const conditions = [];
5291
5466
  if (options?.softDelete) {
5292
5467
  const sdField = anyTable[options.softDelete.field];
5293
- conditions.push(eq3(sdField, null));
5468
+ conditions.push(isNull2(sdField));
5294
5469
  }
5295
5470
  if (args.search && options?.searchFields?.length) {
5296
5471
  if (disableILike) {
@@ -5568,7 +5743,7 @@ function buildCrud(table, options, procedure) {
5568
5743
  }
5569
5744
  if (options?.softDelete) {
5570
5745
  const sdField = anyTable[options.softDelete.field];
5571
- whereCond = and3(whereCond, eq3(sdField, null));
5746
+ whereCond = and3(whereCond, isNull2(sdField));
5572
5747
  }
5573
5748
  return whereCond;
5574
5749
  }
@@ -5677,15 +5852,18 @@ function buildCrud(table, options, procedure) {
5677
5852
  if (options?.hooks?.beforeUpdate) {
5678
5853
  updateData = await options.hooks.beforeUpdate(updateData);
5679
5854
  }
5680
- const { result, preRealtimeRow, postRealtimeRow } = await inRlsOrPlainTx(context.db, async (tx) => {
5681
- const preRow = crudRealtime.explicitScopedList ? await selectFullRowForRealtime(tx, updateWhere) : null;
5682
- const [updated] = listProjection !== void 0 ? await tx.update(anyTable).set(updateData).where(updateWhere).returning(listProjection) : await tx.update(anyTable).set(updateData).where(updateWhere).returning();
5683
- let postRow = updated ?? null;
5684
- if (crudRealtime.explicitScopedList && updated) {
5685
- postRow = await selectFullRowForRealtime(tx, eq3(pk, idVal));
5855
+ const { result, preRealtimeRow, postRealtimeRow } = await inRlsOrPlainTx(
5856
+ context.db,
5857
+ async (tx) => {
5858
+ const preRow = crudRealtime.explicitScopedList ? await selectFullRowForRealtime(tx, updateWhere) : null;
5859
+ const [updated] = listProjection !== void 0 ? await tx.update(anyTable).set(updateData).where(updateWhere).returning(listProjection) : await tx.update(anyTable).set(updateData).where(updateWhere).returning();
5860
+ let postRow = updated ?? null;
5861
+ if (crudRealtime.explicitScopedList && updated) {
5862
+ postRow = await selectFullRowForRealtime(tx, eq3(pk, idVal));
5863
+ }
5864
+ return { result: updated, preRealtimeRow: preRow, postRealtimeRow: postRow };
5686
5865
  }
5687
- return { result: updated, preRealtimeRow: preRow, postRealtimeRow: postRow };
5688
- });
5866
+ );
5689
5867
  if (crudRealtime.enabled) {
5690
5868
  const mutationEventId = createCrudRealtimeEventId();
5691
5869
  let listInvalidationError;
package/core/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gencow/core",
3
- "version": "0.1.45",
3
+ "version": "0.1.46",
4
4
  "type": "module",
5
5
  "main": "index.js"
6
6
  }