@checkstack/backend-api 0.4.1 → 0.5.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/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # @checkstack/backend-api
2
2
 
3
+ ## 0.5.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [db1f56f]
8
+ - @checkstack/common@0.6.0
9
+ - @checkstack/signal-common@0.1.4
10
+ - @checkstack/queue-api@0.2.1
11
+
12
+ ## 0.5.0
13
+
14
+ ### Minor Changes
15
+
16
+ - 66a3963: Add `SafeDatabase` type to prevent Drizzle relational query API usage at compile-time
17
+
18
+ - Added `SafeDatabase<S>` type that omits the `query` field from Drizzle's `NodePgDatabase`
19
+ - Updated `DatabaseDeps` to use `SafeDatabase` for all plugin database injection
20
+ - Updated `RpcContext.db` and `coreServices.database` to use the safe type
21
+ - Updated test utilities to use `SafeDatabase`
22
+
23
+ This change prevents accidental usage of the relational query API (`db.query`) which is blocked at runtime by the scoped database proxy.
24
+
25
+ ### Patch Changes
26
+
27
+ - Updated dependencies [2c0822d]
28
+ - @checkstack/queue-api@0.2.0
29
+
3
30
  ## 0.4.1
4
31
 
5
32
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@checkstack/backend-api",
3
- "version": "0.4.1",
3
+ "version": "0.5.1",
4
4
  "type": "module",
5
5
  "main": "./src/index.ts",
6
6
  "scripts": {
@@ -5,7 +5,7 @@ import type { CollectorRegistry } from "./collector-registry";
5
5
  import type { QueuePluginRegistry, QueueManager } from "@checkstack/queue-api";
6
6
  import type { ConfigService } from "./config-service";
7
7
  import type { SignalService } from "@checkstack/signal-common";
8
- import { NodePgDatabase } from "drizzle-orm/node-postgres";
8
+ import { SafeDatabase } from "./plugin-system";
9
9
  import {
10
10
  Logger,
11
11
  Fetch,
@@ -18,26 +18,26 @@ import type { EventBus } from "./event-bus-types";
18
18
  export * from "./types";
19
19
 
20
20
  export const authenticationStrategyServiceRef = createServiceRef<unknown>(
21
- "internal.authenticationStrategy"
21
+ "internal.authenticationStrategy",
22
22
  );
23
23
 
24
24
  export const coreServices = {
25
25
  database:
26
- createServiceRef<NodePgDatabase<Record<string, unknown>>>("core.database"),
26
+ createServiceRef<SafeDatabase<Record<string, unknown>>>("core.database"),
27
27
  logger: createServiceRef<Logger>("core.logger"),
28
28
  fetch: createServiceRef<Fetch>("core.fetch"),
29
29
  auth: createServiceRef<AuthService>("core.auth"),
30
30
  healthCheckRegistry: createServiceRef<HealthCheckRegistry>(
31
- "core.healthCheckRegistry"
31
+ "core.healthCheckRegistry",
32
32
  ),
33
33
  collectorRegistry: createServiceRef<CollectorRegistry>(
34
- "core.collectorRegistry"
34
+ "core.collectorRegistry",
35
35
  ),
36
36
  pluginInstaller: createServiceRef<PluginInstaller>("core.pluginInstaller"),
37
37
  rpc: createServiceRef<RpcService>("core.rpc"),
38
38
  rpcClient: createServiceRef<RpcClient>("core.rpcClient"),
39
39
  queuePluginRegistry: createServiceRef<QueuePluginRegistry>(
40
- "core.queuePluginRegistry"
40
+ "core.queuePluginRegistry",
41
41
  ),
42
42
  queueManager: createServiceRef<QueueManager>("core.queueManager"),
43
43
  config: createServiceRef<ConfigService>("core.config"),
@@ -14,12 +14,24 @@ export type ResolvedDeps<T extends Deps> = {
14
14
  [K in keyof T]: T[K]["T"];
15
15
  };
16
16
 
17
+ /**
18
+ * Safe database type that omits Drizzle's relational query API.
19
+ * The relational query API bypasses schema isolation and is blocked
20
+ * at runtime by the scoped database proxy. This type prevents
21
+ * accidental usage at compile-time.
22
+ */
23
+ export type SafeDatabase<S extends Record<string, unknown>> = Omit<
24
+ NodePgDatabase<S>,
25
+ "query"
26
+ >;
27
+
17
28
  /**
18
29
  * Helper type for database dependency injection.
19
30
  * If schema S is provided, adds typed database; otherwise adds nothing.
31
+ * Uses SafeDatabase to prevent relational query API usage.
20
32
  */
21
33
  export type DatabaseDeps<S extends Record<string, unknown> | undefined> =
22
- S extends undefined ? unknown : { database: NodePgDatabase<NonNullable<S>> };
34
+ S extends undefined ? unknown : { database: SafeDatabase<NonNullable<S>> };
23
35
 
24
36
  export type PluginContext = {
25
37
  pluginId: string;
@@ -37,7 +49,7 @@ export type AfterPluginsReadyContext = {
37
49
  onHook: <T>(
38
50
  hook: Hook<T>,
39
51
  listener: (payload: T) => Promise<void>,
40
- options?: HookSubscribeOptions
52
+ options?: HookSubscribeOptions,
41
53
  ) => HookUnsubscribe;
42
54
  /**
43
55
  * Emit a hook event. Only available in afterPluginsReady phase.
@@ -48,7 +60,7 @@ export type AfterPluginsReadyContext = {
48
60
  export type BackendPluginRegistry = {
49
61
  registerInit: <
50
62
  D extends Deps,
51
- S extends Record<string, unknown> | undefined = undefined
63
+ S extends Record<string, unknown> | undefined = undefined,
52
64
  >(args: {
53
65
  deps: D;
54
66
  schema?: S;
@@ -64,7 +76,7 @@ export type BackendPluginRegistry = {
64
76
  * Receives the same deps as init, plus onHook and emitHook.
65
77
  */
66
78
  afterPluginsReady?: (
67
- deps: ResolvedDeps<D> & DatabaseDeps<S> & AfterPluginsReadyContext
79
+ deps: ResolvedDeps<D> & DatabaseDeps<S> & AfterPluginsReadyContext,
68
80
  ) => Promise<void>;
69
81
  }) => void;
70
82
  registerService: <S>(ref: ServiceRef<S>, impl: S) => void;
@@ -80,7 +92,7 @@ export type BackendPluginRegistry = {
80
92
  */
81
93
  registerRouter: <C extends AnyContractRouter>(
82
94
  router: Router<C, RpcContext>,
83
- contract: C
95
+ contract: C,
84
96
  ) => void;
85
97
  /**
86
98
  * Register cleanup logic to be called when the plugin is deregistered.
package/src/rpc.ts CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  qualifyAccessRuleId,
9
9
  qualifyResourceType,
10
10
  } from "@checkstack/common";
11
- import { NodePgDatabase } from "drizzle-orm/node-postgres";
11
+ import { SafeDatabase } from "./plugin-system";
12
12
  import {
13
13
  Logger,
14
14
  Fetch,
@@ -36,7 +36,7 @@ export interface RpcContext {
36
36
  */
37
37
  pluginMetadata: PluginMetadata;
38
38
 
39
- db: NodePgDatabase<Record<string, unknown>>;
39
+ db: SafeDatabase<Record<string, unknown>>;
40
40
  logger: Logger;
41
41
  fetch: Fetch;
42
42
  auth: AuthService;
package/src/test-utils.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { mock } from "bun:test";
2
2
  import { RpcContext, EmitHookFn } from "./rpc";
3
- import { NodePgDatabase } from "drizzle-orm/node-postgres";
3
+ import { SafeDatabase } from "./plugin-system";
4
4
  import { HealthCheckRegistry } from "./health-check";
5
5
  import { CollectorRegistry } from "./collector-registry";
6
6
  import { QueuePluginRegistry, QueueManager } from "@checkstack/queue-api";
@@ -10,11 +10,11 @@ import { QueuePluginRegistry, QueueManager } from "@checkstack/queue-api";
10
10
  * By default provides an authenticated user with wildcard access.
11
11
  */
12
12
  export function createMockRpcContext(
13
- overrides: Partial<RpcContext> = {}
13
+ overrides: Partial<RpcContext> = {},
14
14
  ): RpcContext {
15
15
  return {
16
16
  pluginMetadata: { pluginId: "test-plugin" },
17
- db: mock() as unknown as NodePgDatabase<Record<string, unknown>>,
17
+ db: mock() as unknown as SafeDatabase<Record<string, unknown>>,
18
18
  logger: {
19
19
  info: mock(),
20
20
  error: mock(),
@@ -39,7 +39,7 @@ export function createMockRpcContext(
39
39
  checkResourceTeamAccess: mock().mockResolvedValue({ hasAccess: true }),
40
40
  getAccessibleResourceIds: mock().mockImplementation(
41
41
  (params: { resourceIds: string[] }) =>
42
- Promise.resolve(params.resourceIds)
42
+ Promise.resolve(params.resourceIds),
43
43
  ),
44
44
  },
45
45
  healthCheckRegistry: {