@branchly/datasource-postgres 0.1.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.
- package/dist/index.d.ts +14 -0
- package/dist/index.js +60 -0
- package/dist/index.js.map +1 -0
- package/package.json +34 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { DatasourceAdapter } from 'branchly';
|
|
2
|
+
|
|
3
|
+
interface SqlResult {
|
|
4
|
+
readonly rows: readonly Record<string, unknown>[];
|
|
5
|
+
}
|
|
6
|
+
type SqlRunner = (sql: string, params?: readonly unknown[]) => Promise<SqlResult>;
|
|
7
|
+
interface PostgresDatasourceOptions {
|
|
8
|
+
readonly admin: string;
|
|
9
|
+
readonly prefix?: string;
|
|
10
|
+
readonly query?: SqlRunner;
|
|
11
|
+
}
|
|
12
|
+
declare const createPostgresDatasource: (options: PostgresDatasourceOptions) => DatasourceAdapter;
|
|
13
|
+
|
|
14
|
+
export { type PostgresDatasourceOptions, type SqlResult, type SqlRunner, createPostgresDatasource, createPostgresDatasource as default };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
var PG_NAME_PATTERN = /^[a-z0-9_]+$/;
|
|
3
|
+
var PG_NAME_MAX_LENGTH = 63;
|
|
4
|
+
var DEFAULT_PREFIX = "app";
|
|
5
|
+
var databaseName = (prefix, key) => {
|
|
6
|
+
const name = `${prefix}_${key}`;
|
|
7
|
+
if (!PG_NAME_PATTERN.test(name) || name.length > PG_NAME_MAX_LENGTH) {
|
|
8
|
+
throw new Error(`branchly: branch key "${key}" does not map to a valid Postgres database name ("${name}").`);
|
|
9
|
+
}
|
|
10
|
+
return name;
|
|
11
|
+
};
|
|
12
|
+
var createDefaultRunner = (admin) => async (sql, params = []) => {
|
|
13
|
+
const { Client } = await import("pg");
|
|
14
|
+
const client = new Client({ connectionString: admin });
|
|
15
|
+
await client.connect();
|
|
16
|
+
try {
|
|
17
|
+
const result = await client.query(sql, [...params]);
|
|
18
|
+
return { rows: result.rows };
|
|
19
|
+
} finally {
|
|
20
|
+
await client.end();
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
var createPostgresDatasource = (options) => {
|
|
24
|
+
const prefix = options.prefix ?? DEFAULT_PREFIX;
|
|
25
|
+
const query = options.query ?? createDefaultRunner(options.admin);
|
|
26
|
+
const nameOf = (key) => databaseName(prefix, key);
|
|
27
|
+
return {
|
|
28
|
+
id: "postgres",
|
|
29
|
+
apiVersion: 1,
|
|
30
|
+
capabilities: { instantClone: true, snapshot: true, isolatedPerBranch: true },
|
|
31
|
+
resolve: (key) => {
|
|
32
|
+
const url = new URL(options.admin);
|
|
33
|
+
url.pathname = `/${nameOf(key)}`;
|
|
34
|
+
return url.toString();
|
|
35
|
+
},
|
|
36
|
+
exists: async (key) => {
|
|
37
|
+
const result = await query("SELECT 1 FROM pg_database WHERE datname = $1", [nameOf(key)]);
|
|
38
|
+
return result.rows.length > 0;
|
|
39
|
+
},
|
|
40
|
+
list: async () => {
|
|
41
|
+
const result = await query("SELECT datname FROM pg_database WHERE datname LIKE $1", [`${prefix}_%`]);
|
|
42
|
+
return result.rows.map((row) => row.datname).filter((name) => typeof name === "string").map((name) => name.slice(prefix.length + 1));
|
|
43
|
+
},
|
|
44
|
+
create: async (key) => {
|
|
45
|
+
await query(`CREATE DATABASE "${nameOf(key)}"`);
|
|
46
|
+
},
|
|
47
|
+
clone: async (from, to) => {
|
|
48
|
+
await query(`CREATE DATABASE "${nameOf(to)}" TEMPLATE "${nameOf(from)}"`);
|
|
49
|
+
},
|
|
50
|
+
destroy: async (key) => {
|
|
51
|
+
await query(`DROP DATABASE IF EXISTS "${nameOf(key)}"`);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
var index_default = createPostgresDatasource;
|
|
56
|
+
export {
|
|
57
|
+
createPostgresDatasource,
|
|
58
|
+
index_default as default
|
|
59
|
+
};
|
|
60
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { BranchKey, DatasourceAdapter } from 'branchly';\n\nconst PG_NAME_PATTERN = /^[a-z0-9_]+$/;\nconst PG_NAME_MAX_LENGTH = 63;\nconst DEFAULT_PREFIX = 'app';\n\nexport interface SqlResult {\n readonly rows: readonly Record<string, unknown>[];\n}\n\nexport type SqlRunner = (sql: string, params?: readonly unknown[]) => Promise<SqlResult>;\n\nexport interface PostgresDatasourceOptions {\n readonly admin: string;\n readonly prefix?: string;\n readonly query?: SqlRunner;\n}\n\nconst databaseName = (prefix: string, key: BranchKey): string => {\n const name = `${prefix}_${key}`;\n if (!PG_NAME_PATTERN.test(name) || name.length > PG_NAME_MAX_LENGTH) {\n throw new Error(`branchly: branch key \"${key}\" does not map to a valid Postgres database name (\"${name}\").`);\n }\n return name;\n};\n\nconst createDefaultRunner =\n (admin: string): SqlRunner =>\n async (sql, params = []) => {\n const { Client } = await import('pg');\n const client = new Client({ connectionString: admin });\n await client.connect();\n try {\n const result = await client.query(sql, [...params]);\n return { rows: result.rows as Record<string, unknown>[] };\n } finally {\n await client.end();\n }\n };\n\nexport const createPostgresDatasource = (options: PostgresDatasourceOptions): DatasourceAdapter => {\n const prefix = options.prefix ?? DEFAULT_PREFIX;\n const query = options.query ?? createDefaultRunner(options.admin);\n const nameOf = (key: BranchKey): string => databaseName(prefix, key);\n return {\n id: 'postgres',\n apiVersion: 1,\n capabilities: { instantClone: true, snapshot: true, isolatedPerBranch: true },\n resolve: (key) => {\n const url = new URL(options.admin);\n url.pathname = `/${nameOf(key)}`;\n return url.toString();\n },\n exists: async (key) => {\n const result = await query('SELECT 1 FROM pg_database WHERE datname = $1', [nameOf(key)]);\n return result.rows.length > 0;\n },\n list: async () => {\n const result = await query('SELECT datname FROM pg_database WHERE datname LIKE $1', [`${prefix}_%`]);\n return result.rows\n .map((row) => row.datname)\n .filter((name): name is string => typeof name === 'string')\n .map((name) => name.slice(prefix.length + 1));\n },\n create: async (key) => {\n await query(`CREATE DATABASE \"${nameOf(key)}\"`);\n },\n clone: async (from, to) => {\n await query(`CREATE DATABASE \"${nameOf(to)}\" TEMPLATE \"${nameOf(from)}\"`);\n },\n destroy: async (key) => {\n await query(`DROP DATABASE IF EXISTS \"${nameOf(key)}\"`);\n },\n };\n};\n\nexport default createPostgresDatasource;\n"],"mappings":";AAEA,IAAM,kBAAkB;AACxB,IAAM,qBAAqB;AAC3B,IAAM,iBAAiB;AAcvB,IAAM,eAAe,CAAC,QAAgB,QAA2B;AAC/D,QAAM,OAAO,GAAG,MAAM,IAAI,GAAG;AAC7B,MAAI,CAAC,gBAAgB,KAAK,IAAI,KAAK,KAAK,SAAS,oBAAoB;AACnE,UAAM,IAAI,MAAM,yBAAyB,GAAG,sDAAsD,IAAI,KAAK;AAAA,EAC7G;AACA,SAAO;AACT;AAEA,IAAM,sBACJ,CAAC,UACD,OAAO,KAAK,SAAS,CAAC,MAAM;AAC1B,QAAM,EAAE,OAAO,IAAI,MAAM,OAAO,IAAI;AACpC,QAAM,SAAS,IAAI,OAAO,EAAE,kBAAkB,MAAM,CAAC;AACrD,QAAM,OAAO,QAAQ;AACrB,MAAI;AACF,UAAM,SAAS,MAAM,OAAO,MAAM,KAAK,CAAC,GAAG,MAAM,CAAC;AAClD,WAAO,EAAE,MAAM,OAAO,KAAkC;AAAA,EAC1D,UAAE;AACA,UAAM,OAAO,IAAI;AAAA,EACnB;AACF;AAEK,IAAM,2BAA2B,CAAC,YAA0D;AACjG,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,QAAQ,QAAQ,SAAS,oBAAoB,QAAQ,KAAK;AAChE,QAAM,SAAS,CAAC,QAA2B,aAAa,QAAQ,GAAG;AACnE,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,YAAY;AAAA,IACZ,cAAc,EAAE,cAAc,MAAM,UAAU,MAAM,mBAAmB,KAAK;AAAA,IAC5E,SAAS,CAAC,QAAQ;AAChB,YAAM,MAAM,IAAI,IAAI,QAAQ,KAAK;AACjC,UAAI,WAAW,IAAI,OAAO,GAAG,CAAC;AAC9B,aAAO,IAAI,SAAS;AAAA,IACtB;AAAA,IACA,QAAQ,OAAO,QAAQ;AACrB,YAAM,SAAS,MAAM,MAAM,gDAAgD,CAAC,OAAO,GAAG,CAAC,CAAC;AACxF,aAAO,OAAO,KAAK,SAAS;AAAA,IAC9B;AAAA,IACA,MAAM,YAAY;AAChB,YAAM,SAAS,MAAM,MAAM,yDAAyD,CAAC,GAAG,MAAM,IAAI,CAAC;AACnG,aAAO,OAAO,KACX,IAAI,CAAC,QAAQ,IAAI,OAAO,EACxB,OAAO,CAAC,SAAyB,OAAO,SAAS,QAAQ,EACzD,IAAI,CAAC,SAAS,KAAK,MAAM,OAAO,SAAS,CAAC,CAAC;AAAA,IAChD;AAAA,IACA,QAAQ,OAAO,QAAQ;AACrB,YAAM,MAAM,oBAAoB,OAAO,GAAG,CAAC,GAAG;AAAA,IAChD;AAAA,IACA,OAAO,OAAO,MAAM,OAAO;AACzB,YAAM,MAAM,oBAAoB,OAAO,EAAE,CAAC,eAAe,OAAO,IAAI,CAAC,GAAG;AAAA,IAC1E;AAAA,IACA,SAAS,OAAO,QAAQ;AACtB,YAAM,MAAM,4BAA4B,OAAO,GAAG,CAAC,GAAG;AAAA,IACxD;AAAA,EACF;AACF;AAEA,IAAO,gBAAQ;","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@branchly/datasource-postgres",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Reference Postgres datasource adapter for branchly.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"pg": "^8.13.1"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@types/pg": "^8.11.10",
|
|
23
|
+
"branchly": "0.1.0",
|
|
24
|
+
"@branchly/adapter-test-kit": "0.1.0"
|
|
25
|
+
},
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsup",
|
|
31
|
+
"dev": "tsup --watch",
|
|
32
|
+
"typecheck": "tsc --noEmit"
|
|
33
|
+
}
|
|
34
|
+
}
|