@atproto/bsync 0.0.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.
Files changed (52) hide show
  1. package/LICENSE.txt +7 -0
  2. package/README.md +15 -0
  3. package/babel.config.js +3 -0
  4. package/bin/migration-create.ts +38 -0
  5. package/buf.gen.yaml +13 -0
  6. package/build.js +18 -0
  7. package/dist/client.d.ts +6 -0
  8. package/dist/config.d.ts +36 -0
  9. package/dist/context.d.ts +19 -0
  10. package/dist/db/index.d.ts +31 -0
  11. package/dist/db/migrations/20240108T220751294Z-init.d.ts +3 -0
  12. package/dist/db/migrations/index.d.ts +1 -0
  13. package/dist/db/migrations/provider.d.ts +6 -0
  14. package/dist/db/schema/index.d.ts +6 -0
  15. package/dist/db/schema/mute_item.d.ts +11 -0
  16. package/dist/db/schema/mute_op.d.ts +15 -0
  17. package/dist/db/types.d.ts +12 -0
  18. package/dist/gen/bsync_connect.d.ts +25 -0
  19. package/dist/gen/bsync_pb.d.ts +90 -0
  20. package/dist/index.d.ts +26 -0
  21. package/dist/index.js +76455 -0
  22. package/dist/index.js.map +7 -0
  23. package/dist/logger.d.ts +4 -0
  24. package/dist/routes/add-mute-operation.d.ts +5 -0
  25. package/dist/routes/auth.d.ts +3 -0
  26. package/dist/routes/index.d.ts +4 -0
  27. package/dist/routes/scan-mute-operations.d.ts +5 -0
  28. package/jest.config.js +6 -0
  29. package/package.json +49 -0
  30. package/proto/bsync.proto +55 -0
  31. package/src/client.ts +25 -0
  32. package/src/config.ts +90 -0
  33. package/src/context.ts +42 -0
  34. package/src/db/index.ts +194 -0
  35. package/src/db/migrations/20240108T220751294Z-init.ts +26 -0
  36. package/src/db/migrations/index.ts +5 -0
  37. package/src/db/migrations/provider.ts +8 -0
  38. package/src/db/schema/index.ts +9 -0
  39. package/src/db/schema/mute_item.ts +13 -0
  40. package/src/db/schema/mute_op.ts +18 -0
  41. package/src/db/types.ts +15 -0
  42. package/src/gen/bsync_connect.ts +54 -0
  43. package/src/gen/bsync_pb.ts +459 -0
  44. package/src/index.ts +91 -0
  45. package/src/logger.ts +22 -0
  46. package/src/routes/add-mute-operation.ts +173 -0
  47. package/src/routes/auth.ts +15 -0
  48. package/src/routes/index.ts +18 -0
  49. package/src/routes/scan-mute-operations.ts +69 -0
  50. package/tests/mutes.test.ts +350 -0
  51. package/tsconfig.build.json +4 -0
  52. package/tsconfig.json +14 -0
@@ -0,0 +1,4 @@
1
+ import { subsystemLogger } from '@atproto/common';
2
+ export declare const dbLogger: ReturnType<typeof subsystemLogger>;
3
+ export declare const httpLogger: ReturnType<typeof subsystemLogger>;
4
+ export declare const loggerMiddleware: import("pino-http").HttpLogger;
@@ -0,0 +1,5 @@
1
+ import { ServiceImpl } from '@connectrpc/connect';
2
+ import { Service } from '../gen/bsync_connect';
3
+ import AppContext from '../context';
4
+ declare const _default: (ctx: AppContext) => Partial<ServiceImpl<typeof Service>>;
5
+ export default _default;
@@ -0,0 +1,3 @@
1
+ import { HandlerContext } from '@connectrpc/connect';
2
+ import AppContext from '../context';
3
+ export declare const authWithApiKey: (ctx: AppContext, handlerCtx: HandlerContext) => void;
@@ -0,0 +1,4 @@
1
+ import { ConnectRouter } from '@connectrpc/connect';
2
+ import AppContext from '../context';
3
+ declare const _default: (ctx: AppContext) => (router: ConnectRouter) => ConnectRouter;
4
+ export default _default;
@@ -0,0 +1,5 @@
1
+ import { ServiceImpl } from '@connectrpc/connect';
2
+ import { Service } from '../gen/bsync_connect';
3
+ import AppContext from '../context';
4
+ declare const _default: (ctx: AppContext) => Partial<ServiceImpl<typeof Service>>;
5
+ export default _default;
package/jest.config.js ADDED
@@ -0,0 +1,6 @@
1
+ const base = require('../../jest.config.base.js')
2
+
3
+ module.exports = {
4
+ ...base,
5
+ displayName: 'Bsync',
6
+ }
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@atproto/bsync",
3
+ "version": "0.0.0",
4
+ "license": "MIT",
5
+ "description": "Sychronizing service for app.bsky App View (Bluesky API)",
6
+ "keywords": [
7
+ "atproto",
8
+ "bluesky"
9
+ ],
10
+ "homepage": "https://atproto.com",
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "https://github.com/bluesky-social/atproto",
14
+ "directory": "packages/bsync"
15
+ },
16
+ "main": "dist/index.js",
17
+ "dependencies": {
18
+ "@bufbuild/protobuf": "^1.5.0",
19
+ "@connectrpc/connect": "^1.1.4",
20
+ "@connectrpc/connect-express": "^1.1.4",
21
+ "@connectrpc/connect-node": "^1.1.4",
22
+ "http-terminator": "^3.2.0",
23
+ "kysely": "^0.22.0",
24
+ "pg": "^8.10.0",
25
+ "pino": "^8.15.0",
26
+ "pino-http": "^8.2.1",
27
+ "typed-emitter": "^2.1.0",
28
+ "@atproto/common": "^0.3.3",
29
+ "@atproto/syntax": "^0.1.5"
30
+ },
31
+ "devDependencies": {
32
+ "@bufbuild/buf": "^1.28.1",
33
+ "@bufbuild/protoc-gen-es": "^1.5.0",
34
+ "@connectrpc/protoc-gen-connect-es": "^1.1.4",
35
+ "@types/pg": "^8.6.6"
36
+ },
37
+ "scripts": {
38
+ "build": "node ./build.js",
39
+ "postbuild": "tsc --build tsconfig.build.json",
40
+ "update-main-to-dist": "node ../../update-main-to-dist.js packages/bsync",
41
+ "start": "node --enable-source-maps dist/bin.js",
42
+ "test": "../dev-infra/with-test-db.sh jest",
43
+ "test:log": "tail -50 test.log | pino-pretty",
44
+ "test:updateSnapshot": "jest --updateSnapshot",
45
+ "migration:create": "ts-node ./bin/migration-create.ts",
46
+ "buf:gen": "buf generate proto"
47
+ },
48
+ "types": "dist/index.d.ts"
49
+ }
@@ -0,0 +1,55 @@
1
+ syntax = "proto3";
2
+
3
+ package bsync;
4
+ option go_package = "./;bsync";
5
+
6
+ //
7
+ // Sync
8
+ //
9
+
10
+
11
+ message MuteOperation {
12
+ enum Type {
13
+ TYPE_UNSPECIFIED = 0;
14
+ TYPE_ADD = 1;
15
+ TYPE_REMOVE = 2;
16
+ TYPE_CLEAR = 3;
17
+ }
18
+ string id = 1;
19
+ Type type = 2;
20
+ string actor_did = 3;
21
+ string subject = 4;
22
+ }
23
+
24
+ message AddMuteOperationRequest {
25
+ MuteOperation.Type type = 1;
26
+ string actor_did = 2;
27
+ string subject = 3;
28
+ }
29
+
30
+ message AddMuteOperationResponse {
31
+ MuteOperation operation = 1;
32
+ }
33
+
34
+ message ScanMuteOperationsRequest {
35
+ string cursor = 1;
36
+ int32 limit = 2;
37
+ }
38
+
39
+ message ScanMuteOperationsResponse {
40
+ repeated MuteOperation operations = 1;
41
+ string cursor = 2;
42
+ }
43
+
44
+ // Ping
45
+ message PingRequest {}
46
+ message PingResponse {}
47
+
48
+
49
+ service Service {
50
+ // Sync
51
+ rpc AddMuteOperation(AddMuteOperationRequest) returns (AddMuteOperationResponse);
52
+ rpc ScanMuteOperations(ScanMuteOperationsRequest) returns (ScanMuteOperationsResponse);
53
+ // Ping
54
+ rpc Ping(PingRequest) returns (PingResponse);
55
+ }
package/src/client.ts ADDED
@@ -0,0 +1,25 @@
1
+ import {
2
+ Interceptor,
3
+ PromiseClient,
4
+ createPromiseClient,
5
+ } from '@connectrpc/connect'
6
+ import {
7
+ ConnectTransportOptions,
8
+ createConnectTransport,
9
+ } from '@connectrpc/connect-node'
10
+ import { Service } from './gen/bsync_connect'
11
+
12
+ export type BsyncClient = PromiseClient<typeof Service>
13
+
14
+ export const createClient = (opts: ConnectTransportOptions): BsyncClient => {
15
+ const transport = createConnectTransport(opts)
16
+ return createPromiseClient(Service, transport)
17
+ }
18
+
19
+ export const authWithApiKey =
20
+ (apiKey: string): Interceptor =>
21
+ (next) =>
22
+ (req) => {
23
+ req.header.set('authorization', `Bearer ${apiKey}`)
24
+ return next(req)
25
+ }
package/src/config.ts ADDED
@@ -0,0 +1,90 @@
1
+ import assert from 'node:assert'
2
+ import { envInt, envStr, envList, envBool } from '@atproto/common'
3
+
4
+ export const envToCfg = (env: ServerEnvironment): ServerConfig => {
5
+ const serviceCfg: ServerConfig['service'] = {
6
+ port: env.port ?? 2585,
7
+ version: env.version ?? 'unknown',
8
+ longPollTimeoutMs: env.longPollTimeoutMs ?? 10000,
9
+ }
10
+
11
+ assert(env.dbUrl, 'missing postgres url')
12
+ const dbCfg: ServerConfig['db'] = {
13
+ url: env.dbUrl,
14
+ schema: env.dbSchema,
15
+ poolSize: env.dbPoolSize,
16
+ poolMaxUses: env.dbPoolMaxUses,
17
+ poolIdleTimeoutMs: env.dbPoolIdleTimeoutMs,
18
+ migrate: env.dbMigrate,
19
+ }
20
+
21
+ assert(env.apiKeys.length > 0, 'missing api keys')
22
+ const authCfg: ServerConfig['auth'] = {
23
+ apiKeys: new Set(env.apiKeys),
24
+ }
25
+
26
+ return {
27
+ service: serviceCfg,
28
+ db: dbCfg,
29
+ auth: authCfg,
30
+ }
31
+ }
32
+
33
+ export type ServerConfig = {
34
+ service: ServiceConfig
35
+ db: DatabaseConfig
36
+ auth: AuthConfig
37
+ }
38
+
39
+ type ServiceConfig = {
40
+ port: number
41
+ version?: string
42
+ longPollTimeoutMs: number
43
+ }
44
+
45
+ type DatabaseConfig = {
46
+ url: string
47
+ schema?: string
48
+ poolSize?: number
49
+ poolMaxUses?: number
50
+ poolIdleTimeoutMs?: number
51
+ migrate?: boolean
52
+ }
53
+
54
+ type AuthConfig = {
55
+ apiKeys: Set<string>
56
+ }
57
+
58
+ export const readEnv = (): ServerEnvironment => {
59
+ return {
60
+ // service
61
+ port: envInt('BSYNC_PORT'),
62
+ version: envStr('BSYNC_VERSION'),
63
+ longPollTimeoutMs: envInt('BSYNC_LONG_POLL_TIMEOUT_MS'),
64
+ // database
65
+ dbUrl: envStr('BSYNC_DB_POSTGRES_URL'),
66
+ dbSchema: envStr('BSYNC_DB_POSTGRES_SCHEMA'),
67
+ dbPoolSize: envInt('BSYNC_DB_POOL_SIZE'),
68
+ dbPoolMaxUses: envInt('BSYNC_DB_POOL_MAX_USES'),
69
+ dbPoolIdleTimeoutMs: envInt('BSYNC_DB_POOL_IDLE_TIMEOUT_MS'),
70
+ dbMigrate: envBool('BSYNC_DB_MIGRATE'),
71
+ // secrets
72
+ apiKeys: envList('BSYNC_API_KEYS'),
73
+ }
74
+ }
75
+
76
+ export type ServerEnvironment = {
77
+ // service
78
+ port?: number
79
+ version?: string
80
+ longPollTimeoutMs?: number
81
+ // database
82
+ dbUrl?: string
83
+ dbSchema?: string
84
+ dbPoolSize?: number
85
+ dbPoolMaxUses?: number
86
+ dbPoolIdleTimeoutMs?: number
87
+ dbMigrate?: boolean
88
+ // secrets
89
+ apiKeys: string[]
90
+ }
package/src/context.ts ADDED
@@ -0,0 +1,42 @@
1
+ import TypedEventEmitter from 'typed-emitter'
2
+ import { ServerConfig } from './config'
3
+ import Database from './db'
4
+ import { createMuteOpChannel } from './db/schema/mute_op'
5
+ import { EventEmitter } from 'stream'
6
+
7
+ export type AppContextOptions = {
8
+ db: Database
9
+ cfg: ServerConfig
10
+ }
11
+
12
+ export class AppContext {
13
+ db: Database
14
+ cfg: ServerConfig
15
+ events: TypedEventEmitter<AppEvents>
16
+
17
+ constructor(opts: AppContextOptions) {
18
+ this.db = opts.db
19
+ this.cfg = opts.cfg
20
+ this.events = new EventEmitter() as TypedEventEmitter<AppEvents>
21
+ }
22
+
23
+ static async fromConfig(
24
+ cfg: ServerConfig,
25
+ overrides?: Partial<AppContextOptions>,
26
+ ): Promise<AppContext> {
27
+ const db = new Database({
28
+ url: cfg.db.url,
29
+ schema: cfg.db.schema,
30
+ poolSize: cfg.db.poolSize,
31
+ poolMaxUses: cfg.db.poolMaxUses,
32
+ poolIdleTimeoutMs: cfg.db.poolIdleTimeoutMs,
33
+ })
34
+ return new AppContext({ db, cfg, ...overrides })
35
+ }
36
+ }
37
+
38
+ export default AppContext
39
+
40
+ export type AppEvents = {
41
+ [createMuteOpChannel]: () => void
42
+ }
@@ -0,0 +1,194 @@
1
+ import assert from 'assert'
2
+ import {
3
+ Kysely,
4
+ PostgresDialect,
5
+ Migrator,
6
+ KyselyPlugin,
7
+ PluginTransformQueryArgs,
8
+ PluginTransformResultArgs,
9
+ RootOperationNode,
10
+ QueryResult,
11
+ UnknownRow,
12
+ } from 'kysely'
13
+ import TypedEmitter from 'typed-emitter'
14
+ import { Pool as PgPool, types as pgTypes } from 'pg'
15
+ import DatabaseSchema, { DatabaseSchemaType } from './schema'
16
+ import { PgOptions } from './types'
17
+ import { dbLogger } from '../logger'
18
+ import { EventEmitter } from 'stream'
19
+ import * as migrations from './migrations'
20
+ import { DbMigrationProvider } from './migrations/provider'
21
+
22
+ export class Database {
23
+ pool: PgPool
24
+ db: DatabaseSchema
25
+ migrator: Migrator
26
+ txEvt = new EventEmitter() as TxnEmitter
27
+ destroyed = false
28
+
29
+ constructor(
30
+ public opts: PgOptions,
31
+ instances?: { db: DatabaseSchema; pool: PgPool },
32
+ ) {
33
+ // if instances are provided, use those
34
+ if (instances) {
35
+ this.db = instances.db
36
+ this.pool = instances.pool
37
+ return
38
+ }
39
+
40
+ // else create a pool & connect
41
+ const { schema, url } = opts
42
+ const pool =
43
+ opts.pool ??
44
+ new PgPool({
45
+ connectionString: url,
46
+ max: opts.poolSize,
47
+ maxUses: opts.poolMaxUses,
48
+ idleTimeoutMillis: opts.poolIdleTimeoutMs,
49
+ })
50
+
51
+ // Select count(*) and other pg bigints as js integer
52
+ pgTypes.setTypeParser(pgTypes.builtins.INT8, (n) => parseInt(n, 10))
53
+
54
+ // Setup schema usage, primarily for test parallelism (each test suite runs in its own pg schema)
55
+ if (schema && !/^[a-z_]+$/i.test(schema)) {
56
+ throw new Error(`Postgres schema must only contain [A-Za-z_]: ${schema}`)
57
+ }
58
+
59
+ pool.on('error', onPoolError)
60
+ pool.on('connect', (client) => {
61
+ client.on('error', onClientError)
62
+ if (schema) {
63
+ // Shared objects such as extensions will go in the public schema
64
+ client.query(`SET search_path TO "${schema}",public;`)
65
+ }
66
+ })
67
+
68
+ this.pool = pool
69
+ this.db = new Kysely<DatabaseSchemaType>({
70
+ dialect: new PostgresDialect({ pool }),
71
+ })
72
+ this.migrator = new Migrator({
73
+ db: this.db,
74
+ migrationTableSchema: opts.schema,
75
+ provider: new DbMigrationProvider(migrations),
76
+ })
77
+ }
78
+
79
+ get schema(): string | undefined {
80
+ return this.opts.schema
81
+ }
82
+
83
+ get isTransaction() {
84
+ return this.db.isTransaction
85
+ }
86
+
87
+ assertTransaction() {
88
+ assert(this.isTransaction, 'Transaction required')
89
+ }
90
+
91
+ assertNotTransaction() {
92
+ assert(!this.isTransaction, 'Cannot be in a transaction')
93
+ }
94
+
95
+ async transaction<T>(fn: (db: Database) => Promise<T>): Promise<T> {
96
+ const leakyTxPlugin = new LeakyTxPlugin()
97
+ const { dbTxn, txRes } = await this.db
98
+ .withPlugin(leakyTxPlugin)
99
+ .transaction()
100
+ .execute(async (txn) => {
101
+ const dbTxn = new Database(this.opts, {
102
+ db: txn,
103
+ pool: this.pool,
104
+ })
105
+ const txRes = await fn(dbTxn)
106
+ .catch(async (err) => {
107
+ leakyTxPlugin.endTx()
108
+ // ensure that all in-flight queries are flushed & the connection is open
109
+ await dbTxn.db.getExecutor().provideConnection(noopAsync)
110
+ throw err
111
+ })
112
+ .finally(() => leakyTxPlugin.endTx())
113
+ return { dbTxn, txRes }
114
+ })
115
+ dbTxn?.txEvt.emit('commit')
116
+ return txRes
117
+ }
118
+
119
+ onCommit(fn: () => void) {
120
+ this.assertTransaction()
121
+ this.txEvt.once('commit', fn)
122
+ }
123
+
124
+ async close(): Promise<void> {
125
+ if (this.destroyed) return
126
+ await this.db.destroy()
127
+ this.destroyed = true
128
+ }
129
+
130
+ async migrateToOrThrow(migration: string) {
131
+ if (this.schema) {
132
+ await this.db.schema.createSchema(this.schema).ifNotExists().execute()
133
+ }
134
+ const { error, results } = await this.migrator.migrateTo(migration)
135
+ if (error) {
136
+ throw error
137
+ }
138
+ if (!results) {
139
+ throw new Error('An unknown failure occurred while migrating')
140
+ }
141
+ return results
142
+ }
143
+
144
+ async migrateToLatestOrThrow() {
145
+ if (this.schema) {
146
+ await this.db.schema.createSchema(this.schema).ifNotExists().execute()
147
+ }
148
+ const { error, results } = await this.migrator.migrateToLatest()
149
+ if (error) {
150
+ throw error
151
+ }
152
+ if (!results) {
153
+ throw new Error('An unknown failure occurred while migrating')
154
+ }
155
+ return results
156
+ }
157
+ }
158
+
159
+ export default Database
160
+
161
+ const onPoolError = (err: Error) => dbLogger.error({ err }, 'db pool error')
162
+ const onClientError = (err: Error) => dbLogger.error({ err }, 'db client error')
163
+
164
+ // utils
165
+ // -------
166
+
167
+ class LeakyTxPlugin implements KyselyPlugin {
168
+ private txOver: boolean
169
+
170
+ endTx() {
171
+ this.txOver = true
172
+ }
173
+
174
+ transformQuery(args: PluginTransformQueryArgs): RootOperationNode {
175
+ if (this.txOver) {
176
+ throw new Error('tx already failed')
177
+ }
178
+ return args.node
179
+ }
180
+
181
+ async transformResult(
182
+ args: PluginTransformResultArgs,
183
+ ): Promise<QueryResult<UnknownRow>> {
184
+ return args.result
185
+ }
186
+ }
187
+
188
+ type TxnEmitter = TypedEmitter<TxnEvents>
189
+
190
+ type TxnEvents = {
191
+ commit: () => void
192
+ }
193
+
194
+ const noopAsync = async () => {}
@@ -0,0 +1,26 @@
1
+ import { Kysely, sql } from 'kysely'
2
+
3
+ export async function up(db: Kysely<unknown>): Promise<void> {
4
+ await db.schema
5
+ .createTable('mute_op')
6
+ .addColumn('id', 'bigserial', (col) => col.primaryKey())
7
+ .addColumn('type', 'int2', (col) => col.notNull()) // integer enum: 0->add, 1->remove, 2->clear
8
+ .addColumn('actorDid', 'varchar', (col) => col.notNull())
9
+ .addColumn('subject', 'varchar', (col) => col.notNull())
10
+ .addColumn('createdAt', 'timestamptz', (col) =>
11
+ col.notNull().defaultTo(sql`CURRENT_TIMESTAMP`),
12
+ )
13
+ .execute()
14
+ await db.schema
15
+ .createTable('mute_item')
16
+ .addColumn('actorDid', 'varchar', (col) => col.notNull())
17
+ .addColumn('subject', 'varchar', (col) => col.notNull())
18
+ .addColumn('fromId', 'bigint', (col) => col.notNull())
19
+ .addPrimaryKeyConstraint('mute_item_pkey', ['actorDid', 'subject'])
20
+ .execute()
21
+ }
22
+
23
+ export async function down(db: Kysely<unknown>): Promise<void> {
24
+ await db.schema.dropTable('mute_item').execute()
25
+ await db.schema.dropTable('mute_op').execute()
26
+ }
@@ -0,0 +1,5 @@
1
+ // NOTE this file can be edited by hand, but it is also appended to by the migration:create command.
2
+ // It's important that every migration is exported from here with the proper name. We'd simplify
3
+ // this with kysely's FileMigrationProvider, but it doesn't play nicely with the build process.
4
+
5
+ export * as _20240108T220751294Z from './20240108T220751294Z-init'
@@ -0,0 +1,8 @@
1
+ import { Migration, MigrationProvider } from 'kysely'
2
+
3
+ export class DbMigrationProvider implements MigrationProvider {
4
+ constructor(private migrations: Record<string, Migration>) {}
5
+ async getMigrations(): Promise<Record<string, Migration>> {
6
+ return this.migrations
7
+ }
8
+ }
@@ -0,0 +1,9 @@
1
+ import { Kysely } from 'kysely'
2
+ import * as muteOp from './mute_op'
3
+ import * as muteItem from './mute_item'
4
+
5
+ export type DatabaseSchemaType = muteItem.PartialDB & muteOp.PartialDB
6
+
7
+ export type DatabaseSchema = Kysely<DatabaseSchemaType>
8
+
9
+ export default DatabaseSchema
@@ -0,0 +1,13 @@
1
+ import { Selectable } from 'kysely'
2
+
3
+ export interface MuteItem {
4
+ actorDid: string
5
+ subject: string // did or aturi for list
6
+ fromId: number
7
+ }
8
+
9
+ export type MuteItemEntry = Selectable<MuteItem>
10
+
11
+ export const tableName = 'mute_item'
12
+
13
+ export type PartialDB = { [tableName]: MuteItem }
@@ -0,0 +1,18 @@
1
+ import { GeneratedAlways, Selectable } from 'kysely'
2
+ import { MuteOperation_Type } from '../../gen/bsync_pb'
3
+
4
+ export interface MuteOp {
5
+ id: GeneratedAlways<number>
6
+ type: MuteOperation_Type // integer enum: 0->add, 1->remove, 2->clear
7
+ actorDid: string
8
+ subject: string // did or aturi for list
9
+ createdAt: GeneratedAlways<Date>
10
+ }
11
+
12
+ export type MuteOpEntry = Selectable<MuteOp>
13
+
14
+ export const tableName = 'mute_op'
15
+
16
+ export type PartialDB = { [tableName]: MuteOp }
17
+
18
+ export const createMuteOpChannel = 'mute_op_create' // used with listen/notify
@@ -0,0 +1,15 @@
1
+ import { Pool as PgPool } from 'pg'
2
+ import { DynamicModule, RawBuilder, SelectQueryBuilder } from 'kysely'
3
+
4
+ export type DbRef = RawBuilder | ReturnType<DynamicModule['ref']>
5
+
6
+ export type AnyQb = SelectQueryBuilder<any, any, any>
7
+
8
+ export type PgOptions = {
9
+ url: string
10
+ pool?: PgPool
11
+ schema?: string
12
+ poolSize?: number
13
+ poolMaxUses?: number
14
+ poolIdleTimeoutMs?: number
15
+ }
@@ -0,0 +1,54 @@
1
+ // @generated by protoc-gen-connect-es v1.3.0 with parameter "target=ts,import_extension=.ts"
2
+ // @generated from file bsync.proto (package bsync, syntax proto3)
3
+ /* eslint-disable */
4
+ // @ts-nocheck
5
+
6
+ import {
7
+ AddMuteOperationRequest,
8
+ AddMuteOperationResponse,
9
+ PingRequest,
10
+ PingResponse,
11
+ ScanMuteOperationsRequest,
12
+ ScanMuteOperationsResponse,
13
+ } from './bsync_pb.ts'
14
+ import { MethodKind } from '@bufbuild/protobuf'
15
+
16
+ /**
17
+ * @generated from service bsync.Service
18
+ */
19
+ export const Service = {
20
+ typeName: 'bsync.Service',
21
+ methods: {
22
+ /**
23
+ * Sync
24
+ *
25
+ * @generated from rpc bsync.Service.AddMuteOperation
26
+ */
27
+ addMuteOperation: {
28
+ name: 'AddMuteOperation',
29
+ I: AddMuteOperationRequest,
30
+ O: AddMuteOperationResponse,
31
+ kind: MethodKind.Unary,
32
+ },
33
+ /**
34
+ * @generated from rpc bsync.Service.ScanMuteOperations
35
+ */
36
+ scanMuteOperations: {
37
+ name: 'ScanMuteOperations',
38
+ I: ScanMuteOperationsRequest,
39
+ O: ScanMuteOperationsResponse,
40
+ kind: MethodKind.Unary,
41
+ },
42
+ /**
43
+ * Ping
44
+ *
45
+ * @generated from rpc bsync.Service.Ping
46
+ */
47
+ ping: {
48
+ name: 'Ping',
49
+ I: PingRequest,
50
+ O: PingResponse,
51
+ kind: MethodKind.Unary,
52
+ },
53
+ },
54
+ } as const