ras-stack 0.11.0 → 0.12.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/README.md +16 -0
- package/dist/database/index.d.ts +12 -1
- package/dist/database/index.js +11 -1
- package/dist/database/index.js.map +1 -1
- package/dist/database/postgres.d.ts +16 -0
- package/dist/database/postgres.js +29 -0
- package/dist/database/postgres.js.map +1 -0
- package/package.json +10 -1
package/README.md
CHANGED
|
@@ -121,6 +121,22 @@ const database = openDrizzleSqlite({
|
|
|
121
121
|
|
|
122
122
|
Applications retain their schema, migrations, database path, repositories, transactions, and driver selection. Dual-database applications can use `openSqliteClient()` and `configureSqlite()` beneath their own wrapper without forcing PostgreSQL through a shared database interface.
|
|
123
123
|
|
|
124
|
+
The PostgreSQL entrypoint applies the same boundary to Postgres.js and Drizzle while returning both native objects:
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
import { databaseTarget } from 'ras-stack/database'
|
|
128
|
+
import { closeDrizzlePostgres, migrateDrizzlePostgres, openDrizzlePostgres } from 'ras-stack/database/postgres'
|
|
129
|
+
|
|
130
|
+
const target = databaseTarget({ databaseUrl: process.env.DATABASE_URL, sqliteFile })
|
|
131
|
+
if (target.provider === 'postgres') {
|
|
132
|
+
const connection = openDrizzlePostgres({ url: target.url, schema })
|
|
133
|
+
await migrateDrizzlePostgres(connection, postgresMigrationsFolder)
|
|
134
|
+
await closeDrizzlePostgres(connection)
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
The package does not make SQLite and PostgreSQL queries look identical. Applications keep driver-specific transaction and compatibility behavior while sharing target validation, pool defaults, numeric parsing, migrations, credential-safe display URLs, and shutdown.
|
|
139
|
+
|
|
124
140
|
## Realtime updates
|
|
125
141
|
|
|
126
142
|
Applications choose their channel names, authorize subscriptions, and define payloads. `ras-stack` handles Centrifugo's HTTP publication, signed tokens, and repeated browser lifecycle mechanics:
|
package/dist/database/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { URL } from 'node:url';
|
|
2
2
|
export type BundledDirectoryOptions = {
|
|
3
3
|
developmentUrl: URL;
|
|
4
4
|
production: boolean;
|
|
@@ -6,3 +6,14 @@ export type BundledDirectoryOptions = {
|
|
|
6
6
|
name: string;
|
|
7
7
|
};
|
|
8
8
|
export declare function bundledDirectory(options: BundledDirectoryOptions): string;
|
|
9
|
+
export type DatabaseTarget = {
|
|
10
|
+
provider: 'sqlite';
|
|
11
|
+
file: string;
|
|
12
|
+
} | {
|
|
13
|
+
provider: 'postgres';
|
|
14
|
+
url: string;
|
|
15
|
+
};
|
|
16
|
+
export declare function databaseTarget(options: {
|
|
17
|
+
databaseUrl?: string;
|
|
18
|
+
sqliteFile: string;
|
|
19
|
+
}): DatabaseTarget;
|
package/dist/database/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import { fileURLToPath } from 'node:url';
|
|
2
|
+
import { fileURLToPath, URL } from 'node:url';
|
|
3
3
|
export function bundledDirectory(options) {
|
|
4
4
|
if (!options.production)
|
|
5
5
|
return fileURLToPath(options.developmentUrl);
|
|
@@ -8,4 +8,14 @@ export function bundledDirectory(options) {
|
|
|
8
8
|
throw new Error('production entrypoint is required to resolve a bundled directory');
|
|
9
9
|
return path.join(path.dirname(entry), options.name);
|
|
10
10
|
}
|
|
11
|
+
export function databaseTarget(options) {
|
|
12
|
+
const configured = options.databaseUrl?.trim();
|
|
13
|
+
if (!configured)
|
|
14
|
+
return { provider: 'sqlite', file: options.sqliteFile };
|
|
15
|
+
const url = new URL(configured);
|
|
16
|
+
if (url.protocol !== 'postgres:' && url.protocol !== 'postgresql:') {
|
|
17
|
+
throw new Error('database URL must use a postgres:// or postgresql:// URL');
|
|
18
|
+
}
|
|
19
|
+
return { provider: 'postgres', url: configured };
|
|
20
|
+
}
|
|
11
21
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/database/index.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,aAAa,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/database/index.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAS7C,MAAM,UAAU,gBAAgB,CAAC,OAAgC;IAC/D,IAAI,CAAC,OAAO,CAAC,UAAU;QAAE,OAAO,aAAa,CAAC,OAAO,CAAC,cAAc,CAAC,CAAA;IACrE,MAAM,KAAK,GAAG,OAAO,CAAC,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACxD,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,kEAAkE,CAAC,CAAA;IAC/F,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;AACrD,CAAC;AAID,MAAM,UAAU,cAAc,CAAC,OAAqD;IAClF,MAAM,UAAU,GAAG,OAAO,CAAC,WAAW,EAAE,IAAI,EAAE,CAAA;IAC9C,IAAI,CAAC,UAAU;QAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,UAAU,EAAE,CAAA;IACxE,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAA;IAC/B,IAAI,GAAG,CAAC,QAAQ,KAAK,WAAW,IAAI,GAAG,CAAC,QAAQ,KAAK,aAAa,EAAE,CAAC;QACnE,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAA;IAC7E,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,EAAE,CAAA;AAClD,CAAC","sourcesContent":["import path from 'node:path'\nimport { fileURLToPath, URL } from 'node:url'\n\nexport type BundledDirectoryOptions = {\n developmentUrl: URL\n production: boolean\n productionEntry?: string\n name: string\n}\n\nexport function bundledDirectory(options: BundledDirectoryOptions) {\n if (!options.production) return fileURLToPath(options.developmentUrl)\n const entry = options.productionEntry ?? process.argv[1]\n if (!entry) throw new Error('production entrypoint is required to resolve a bundled directory')\n return path.join(path.dirname(entry), options.name)\n}\n\nexport type DatabaseTarget = { provider: 'sqlite'; file: string } | { provider: 'postgres'; url: string }\n\nexport function databaseTarget(options: { databaseUrl?: string; sqliteFile: string }): DatabaseTarget {\n const configured = options.databaseUrl?.trim()\n if (!configured) return { provider: 'sqlite', file: options.sqliteFile }\n const url = new URL(configured)\n if (url.protocol !== 'postgres:' && url.protocol !== 'postgresql:') {\n throw new Error('database URL must use a postgres:// or postgresql:// URL')\n }\n return { provider: 'postgres', url: configured }\n}\n"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type PostgresJsDatabase } from 'drizzle-orm/postgres-js';
|
|
2
|
+
import postgres, { type Sql } from 'postgres';
|
|
3
|
+
export type PostgresClientOptions = NonNullable<Parameters<typeof postgres>[1]>;
|
|
4
|
+
export type OpenDrizzlePostgresOptions<TSchema extends Record<string, unknown>> = {
|
|
5
|
+
url: string;
|
|
6
|
+
schema: TSchema;
|
|
7
|
+
client?: PostgresClientOptions;
|
|
8
|
+
};
|
|
9
|
+
export type DrizzlePostgresConnection<TSchema extends Record<string, unknown>> = {
|
|
10
|
+
database: PostgresJsDatabase<TSchema>;
|
|
11
|
+
client: Sql;
|
|
12
|
+
};
|
|
13
|
+
export declare function openDrizzlePostgres<TSchema extends Record<string, unknown>>(options: OpenDrizzlePostgresOptions<TSchema>): DrizzlePostgresConnection<TSchema>;
|
|
14
|
+
export declare function migrateDrizzlePostgres<TSchema extends Record<string, unknown>>(connection: DrizzlePostgresConnection<TSchema>, migrationsFolder: string): Promise<void>;
|
|
15
|
+
export declare function closeDrizzlePostgres(connection: Pick<DrizzlePostgresConnection<Record<string, unknown>>, 'client'>, timeout?: number): Promise<void>;
|
|
16
|
+
export declare function redactedPostgresUrl(source: string): string;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { drizzle } from 'drizzle-orm/postgres-js';
|
|
2
|
+
import { migrate } from 'drizzle-orm/postgres-js/migrator';
|
|
3
|
+
import postgres, {} from 'postgres';
|
|
4
|
+
export function openDrizzlePostgres(options) {
|
|
5
|
+
const client = postgres(options.url, {
|
|
6
|
+
max: 10,
|
|
7
|
+
onnotice: () => undefined,
|
|
8
|
+
types: {
|
|
9
|
+
bigint: { to: 20, from: [20], serialize: (value) => String(value), parse: Number },
|
|
10
|
+
numeric: { to: 1700, from: [1700], serialize: (value) => String(value), parse: Number },
|
|
11
|
+
},
|
|
12
|
+
...options.client,
|
|
13
|
+
});
|
|
14
|
+
const database = drizzle(client, { schema: options.schema });
|
|
15
|
+
return { database, client };
|
|
16
|
+
}
|
|
17
|
+
export async function migrateDrizzlePostgres(connection, migrationsFolder) {
|
|
18
|
+
await migrate(connection.database, { migrationsFolder });
|
|
19
|
+
}
|
|
20
|
+
export async function closeDrizzlePostgres(connection, timeout = 5) {
|
|
21
|
+
await connection.client.end({ timeout });
|
|
22
|
+
}
|
|
23
|
+
export function redactedPostgresUrl(source) {
|
|
24
|
+
const url = new URL(source);
|
|
25
|
+
url.username = '';
|
|
26
|
+
url.password = '';
|
|
27
|
+
return url.toString();
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=postgres.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres.js","sourceRoot":"","sources":["../../src/database/postgres.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAA2B,MAAM,yBAAyB,CAAA;AAC1E,OAAO,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAA;AAC1D,OAAO,QAAQ,EAAE,EAAY,MAAM,UAAU,CAAA;AAe7C,MAAM,UAAU,mBAAmB,CACjC,OAA4C;IAE5C,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE;QACnC,GAAG,EAAE,EAAE;QACP,QAAQ,EAAE,GAAG,EAAE,CAAC,SAAS;QACzB,KAAK,EAAE;YACL,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE;YAC1F,OAAO,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE;SAChG;QACD,GAAG,OAAO,CAAC,MAAM;KAClB,CAAC,CAAA;IACF,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;IAC5D,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAA;AAC7B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,UAA8C,EAC9C,gBAAwB;IAExB,MAAM,OAAO,CAAC,UAAU,CAAC,QAAQ,EAAE,EAAE,gBAAgB,EAAE,CAAC,CAAA;AAC1D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,UAA8E,EAAE,OAAO,GAAG,CAAC;IACpI,MAAM,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,CAAC,CAAA;AAC1C,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,MAAc;IAChD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAA;IAC3B,GAAG,CAAC,QAAQ,GAAG,EAAE,CAAA;IACjB,GAAG,CAAC,QAAQ,GAAG,EAAE,CAAA;IACjB,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAA;AACvB,CAAC","sourcesContent":["import { drizzle, type PostgresJsDatabase } from 'drizzle-orm/postgres-js'\nimport { migrate } from 'drizzle-orm/postgres-js/migrator'\nimport postgres, { type Sql } from 'postgres'\n\nexport type PostgresClientOptions = NonNullable<Parameters<typeof postgres>[1]>\n\nexport type OpenDrizzlePostgresOptions<TSchema extends Record<string, unknown>> = {\n url: string\n schema: TSchema\n client?: PostgresClientOptions\n}\n\nexport type DrizzlePostgresConnection<TSchema extends Record<string, unknown>> = {\n database: PostgresJsDatabase<TSchema>\n client: Sql\n}\n\nexport function openDrizzlePostgres<TSchema extends Record<string, unknown>>(\n options: OpenDrizzlePostgresOptions<TSchema>,\n): DrizzlePostgresConnection<TSchema> {\n const client = postgres(options.url, {\n max: 10,\n onnotice: () => undefined,\n types: {\n bigint: { to: 20, from: [20], serialize: (value: number) => String(value), parse: Number },\n numeric: { to: 1700, from: [1700], serialize: (value: number) => String(value), parse: Number },\n },\n ...options.client,\n })\n const database = drizzle(client, { schema: options.schema })\n return { database, client }\n}\n\nexport async function migrateDrizzlePostgres<TSchema extends Record<string, unknown>>(\n connection: DrizzlePostgresConnection<TSchema>,\n migrationsFolder: string,\n) {\n await migrate(connection.database, { migrationsFolder })\n}\n\nexport async function closeDrizzlePostgres(connection: Pick<DrizzlePostgresConnection<Record<string, unknown>>, 'client'>, timeout = 5) {\n await connection.client.end({ timeout })\n}\n\nexport function redactedPostgresUrl(source: string) {\n const url = new URL(source)\n url.username = ''\n url.password = ''\n return url.toString()\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ras-stack",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Composable full-stack primitives shared across Richard Solomou's applications.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"authentication",
|
|
@@ -42,6 +42,10 @@
|
|
|
42
42
|
"types": "./dist/database/sqlite.d.ts",
|
|
43
43
|
"default": "./dist/database/sqlite.js"
|
|
44
44
|
},
|
|
45
|
+
"./database/postgres": {
|
|
46
|
+
"types": "./dist/database/postgres.d.ts",
|
|
47
|
+
"default": "./dist/database/postgres.js"
|
|
48
|
+
},
|
|
45
49
|
"./policy": {
|
|
46
50
|
"types": "./dist/policy/index.d.ts",
|
|
47
51
|
"default": "./dist/policy/index.js"
|
|
@@ -112,6 +116,7 @@
|
|
|
112
116
|
"oxfmt": "^0.61.0",
|
|
113
117
|
"oxlint": "^1.74.0",
|
|
114
118
|
"oxlint-tsgolint": "^7.0.2001",
|
|
119
|
+
"postgres": "3.4.9",
|
|
115
120
|
"tus-js-client": "^4.3.1",
|
|
116
121
|
"typescript": "^7.0.2",
|
|
117
122
|
"vite": "8.2.1",
|
|
@@ -124,6 +129,7 @@
|
|
|
124
129
|
"centrifuge": ">=5 <6",
|
|
125
130
|
"drizzle-orm": ">=0.45 <1",
|
|
126
131
|
"nodemailer": ">=9 <10",
|
|
132
|
+
"postgres": ">=3.4 <4",
|
|
127
133
|
"tus-js-client": ">=4 <5"
|
|
128
134
|
},
|
|
129
135
|
"peerDependenciesMeta": {
|
|
@@ -145,6 +151,9 @@
|
|
|
145
151
|
"nodemailer": {
|
|
146
152
|
"optional": true
|
|
147
153
|
},
|
|
154
|
+
"postgres": {
|
|
155
|
+
"optional": true
|
|
156
|
+
},
|
|
148
157
|
"tus-js-client": {
|
|
149
158
|
"optional": true
|
|
150
159
|
}
|