@arkstack/database 0.12.36 → 0.13.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.
@@ -0,0 +1,176 @@
1
+ import { Arkorm, DB, Model, createKyselyAdapter, defineConfig } from "arkormx";
2
+ import { IDatabaseDriver } from "kanun";
3
+ import { config, outputDir } from "@arkstack/common";
4
+ import { Pool } from "pg";
5
+ import { Kysely, PostgresDialect } from "kysely";
6
+ import { Arkstack } from "@arkstack/contract";
7
+ import { createArkormCurrentPageResolver } from "resora";
8
+ import { existsSync } from "node:fs";
9
+ import path from "node:path";
10
+ //#region src/ValidatorDBDriver.ts
11
+ var ValidatorDBDriver = class extends IDatabaseDriver {
12
+ async exists({ table, column, value, ignore }) {
13
+ try {
14
+ const query = DB.table(table).where({ [column]: value });
15
+ if (ignore) query.whereNot({ [column]: ignore });
16
+ return await query.exists();
17
+ } catch {
18
+ return false;
19
+ }
20
+ }
21
+ };
22
+ //#endregion
23
+ //#region src/config.ts
24
+ /**
25
+ * Read a value from the `database` configuration namespace with a fallback.
26
+ *
27
+ * Never throws when the config file is missing; returns the default instead.
28
+ *
29
+ * @param key Dot path within the database config.
30
+ * @param defaultValue Value returned when the key is not set.
31
+ */
32
+ const configure = (key, defaultValue) => {
33
+ try {
34
+ return config(`database.${key}`, defaultValue);
35
+ } catch {
36
+ return defaultValue;
37
+ }
38
+ };
39
+ /**
40
+ * Resolve a configured {@link ConnectionConfig} by name, or the default
41
+ * connection when none is given.
42
+ *
43
+ * @param name The connection name. Defaults to `database.default`.
44
+ * @throws when the named connection is not configured.
45
+ */
46
+ const resolveConnection = (name) => {
47
+ const connection = name ?? configure("default", "pgsql");
48
+ const resolved = configure("connections", {})[connection];
49
+ if (!resolved) throw new Error(`Database connection "${connection}" is not configured.`);
50
+ return resolved;
51
+ };
52
+ //#endregion
53
+ //#region src/kysely.ts
54
+ /**
55
+ * Build a pg {@link Pool} from a {@link ConnectionConfig}. A `url` (or
56
+ * `DATABASE_URL`) wins over the discrete `host`/`port`/`user`/... fields.
57
+ *
58
+ * @param connection
59
+ * @returns
60
+ */
61
+ const createPool = (connection) => {
62
+ const poolConfig = connection.url ? { connectionString: connection.url } : {
63
+ host: connection.host,
64
+ port: connection.port,
65
+ user: connection.user,
66
+ database: connection.database,
67
+ password: connection.password
68
+ };
69
+ if (connection.ssl !== void 0) poolConfig.ssl = connection.ssl;
70
+ if (connection.pool?.max !== void 0) poolConfig.max = connection.pool.max;
71
+ if (connection.pool?.idleTimeoutMillis !== void 0) poolConfig.idleTimeoutMillis = connection.pool.idleTimeoutMillis;
72
+ if (connection.pool?.connectionTimeoutMillis !== void 0) poolConfig.connectionTimeoutMillis = connection.pool.connectionTimeoutMillis;
73
+ return new Pool(poolConfig);
74
+ };
75
+ /**
76
+ * Build a Kysely instance for the given connection using the Postgres dialect.
77
+ *
78
+ * @param connection
79
+ * @returns
80
+ */
81
+ const createKysely = (connection) => {
82
+ return new Kysely({ dialect: new PostgresDialect({ pool: createPool(connection) }) });
83
+ };
84
+ /**
85
+ * Build an ArkORM Kysely adapter for the given connection. This is what binds
86
+ * models to the database.
87
+ *
88
+ * @param connection
89
+ * @returns
90
+ */
91
+ const createAdapter = (connection) => {
92
+ return createKyselyAdapter(createKysely(connection));
93
+ };
94
+ //#endregion
95
+ //#region src/arkorm.ts
96
+ /**
97
+ * Default ArkORM paths matching the scaffolded application structure. Apps with
98
+ * a different layout can override them by adding an `arkormx.config.ts`.
99
+ */
100
+ const defaultPaths = () => {
101
+ return {
102
+ models: "./src/app/models",
103
+ factories: "./src/database/factories",
104
+ seeders: "./src/database/seeders",
105
+ migrations: "./src/database/migrations",
106
+ buildOutput: path.relative(Arkstack.rootDir(), outputDir())
107
+ };
108
+ };
109
+ /**
110
+ * Whether the application provides its own `arkormx.config.{ts,js}`. When it
111
+ * does, ArkORM loads it and it takes precedence over the framework defaults.
112
+ */
113
+ const hasUserArkormConfig = () => {
114
+ const root = Arkstack.rootDir();
115
+ return existsSync(path.join(root, "arkormx.config.ts")) || existsSync(path.join(root, "arkormx.config.js"));
116
+ };
117
+ /**
118
+ * Build the ArkORM config object for an application from its database
119
+ * configuration. Use this only when authoring an explicit `arkormx.config.ts`;
120
+ * by default the framework configures ArkORM for you (see {@link bootArkorm}).
121
+ *
122
+ * @param options Arkorm config plus the app's database config.
123
+ */
124
+ const defineArkormConfig = (options = {}) => {
125
+ const { database, connection, adapter, pagination, ...rest } = options;
126
+ let resolvedAdapter = adapter;
127
+ if (!resolvedAdapter && database) {
128
+ const name = connection ?? database.default;
129
+ const resolved = database.connections[name];
130
+ if (!resolved) throw new Error(`Database connection "${name}" is not configured.`);
131
+ resolvedAdapter = createAdapter(resolved);
132
+ }
133
+ return defineConfig({
134
+ ...rest,
135
+ ...resolvedAdapter ? { adapter: resolvedAdapter } : {},
136
+ pagination: {
137
+ resolveCurrentPage: createArkormCurrentPageResolver(),
138
+ ...pagination ?? {}
139
+ }
140
+ });
141
+ };
142
+ /**
143
+ * Configure ArkORM natively from `src/config/database.ts`, so applications work
144
+ * without an `arkormx.config.ts`.
145
+ *
146
+ * Builds the adapter from the default (or named) connection, registers the
147
+ * conventional paths and the resora pagination resolver, and binds models. A
148
+ * user-provided `arkormx.config.{ts,js}` always wins — in that case this is a
149
+ * no-op and ArkORM loads the file instead.
150
+ *
151
+ * @param options Optional overrides merged over the derived config.
152
+ * @returns Whether the framework applied its configuration.
153
+ */
154
+ const bootArkorm = (options = {}) => {
155
+ if (hasUserArkormConfig()) return false;
156
+ const { connection, adapter, paths, pagination, ...rest } = options;
157
+ const resolvedAdapter = adapter ?? createAdapter(resolveConnection(connection));
158
+ Arkorm.configure({
159
+ adapter: resolvedAdapter,
160
+ paths: {
161
+ ...defaultPaths(),
162
+ ...paths ?? {}
163
+ },
164
+ pagination: {
165
+ resolveCurrentPage: createArkormCurrentPageResolver(),
166
+ ...pagination ?? {}
167
+ },
168
+ outputExt: "ts",
169
+ ...rest
170
+ });
171
+ Model.setAdapter(resolvedAdapter);
172
+ DB.setAdapter(resolvedAdapter);
173
+ return true;
174
+ };
175
+ //#endregion
176
+ export { createPool as a, ValidatorDBDriver as c, createKysely as i, defineArkormConfig as n, configure as o, createAdapter as r, resolveConnection as s, bootArkorm as t };
@@ -1,4 +1,4 @@
1
- import { t as Rebuilder } from "./Rebuilder-a3FLKQ9t.js";
1
+ import { t as Rebuilder } from "./chunks/Rebuilder-DiWBr60E.js";
2
2
  import { CliApp, MakeFactoryCommand as MakeFactoryCommand$1 } from "arkormx";
3
3
  //#region src/commands/MakeFactoryCommand.ts
4
4
  var MakeFactoryCommand = class extends MakeFactoryCommand$1 {
@@ -1,4 +1,4 @@
1
- import { t as Rebuilder } from "./Rebuilder-a3FLKQ9t.js";
1
+ import { t as Rebuilder } from "./chunks/Rebuilder-DiWBr60E.js";
2
2
  import { CliApp, MakeMigrationCommand as MakeMigrationCommand$1 } from "arkormx";
3
3
  //#region src/commands/MakeMigrationCommand.ts
4
4
  var MakeMigrationCommand = class extends MakeMigrationCommand$1 {
@@ -1,4 +1,4 @@
1
- import { t as Rebuilder } from "./Rebuilder-a3FLKQ9t.js";
1
+ import { t as Rebuilder } from "./chunks/Rebuilder-DiWBr60E.js";
2
2
  import { CliApp, MakeModelCommand as MakeModelCommand$1 } from "arkormx";
3
3
  //#region src/commands/MakeModelCommand.ts
4
4
  var MakeModelCommand = class extends MakeModelCommand$1 {
@@ -1,4 +1,4 @@
1
- import { t as Rebuilder } from "./Rebuilder-a3FLKQ9t.js";
1
+ import { t as Rebuilder } from "./chunks/Rebuilder-DiWBr60E.js";
2
2
  import { CliApp, MakeSeederCommand as MakeSeederCommand$1 } from "arkormx";
3
3
  //#region src/commands/MakeSeederCommand.ts
4
4
  var MakeSeederCommand = class extends MakeSeederCommand$1 {
package/dist/index.d.ts CHANGED
@@ -1,5 +1,9 @@
1
- import { DB as DB$1, FactoryAttributes, Migration as Migration$1, Model as Model$1, ModelAttributes, ModelAttributesOf, ModelFactory as ModelFactory$1, ModelQuerySchemaLike, SchemaBuilder as SchemaBuilder$1, Seeder as Seeder$1 } from "arkormx";
1
+ import * as _$arkormx from "arkormx";
2
+ import { ArkormConfig, DB as DB$1, FactoryAttributes, Migration as Migration$1, Model as Model$1, ModelAttributes, ModelAttributesOf, ModelFactory as ModelFactory$1, ModelQuerySchemaLike, SchemaBuilder as SchemaBuilder$1, Seeder as Seeder$1 } from "arkormx";
2
3
  import { IDatabaseDriver, ValidationDatabaseExistsInput } from "kanun";
4
+ import { DotPath, DotPathValue } from "@arkstack/common";
5
+ import { Pool } from "pg";
6
+ import { Kysely } from "kysely";
3
7
 
4
8
  //#region src/extensions/DB.d.ts
5
9
  declare class DB extends DB$1 {}
@@ -29,4 +33,135 @@ declare class ValidatorDBDriver extends IDatabaseDriver {
29
33
  }: ValidationDatabaseExistsInput): Promise<boolean>;
30
34
  }
31
35
  //#endregion
32
- export { DB, Migration, Model, ModelFactory, SchemaBuilder, Seeder, ValidatorDBDriver };
36
+ //#region src/types.d.ts
37
+ /**
38
+ * A connection password. pg accepts a static string or a (possibly async)
39
+ * function that resolves one, which is useful for rotating credentials / IAM
40
+ * auth tokens.
41
+ */
42
+ type ConnectionPassword = string | (() => string | Promise<string>);
43
+ /**
44
+ * A single database connection's settings.
45
+ *
46
+ * Credentials are provided as discrete fields (`host`, `port`, `user`,
47
+ * `database`, `password`). When `url` (or `DATABASE_URL`) is set it takes
48
+ * precedence and the discrete fields are ignored.
49
+ */
50
+ interface ConnectionConfig {
51
+ /** The SQL driver. Only `postgres` is wired up today. */
52
+ driver?: 'postgres' | 'pgsql';
53
+ /** A full connection string. Overrides the discrete fields when set. */
54
+ url?: string;
55
+ host?: string;
56
+ port?: number;
57
+ user?: string;
58
+ database?: string;
59
+ password?: ConnectionPassword;
60
+ /** pg SSL options: `true`, or a TLS options object. */
61
+ ssl?: boolean | Record<string, unknown>;
62
+ /** pg pool tuning. */
63
+ pool?: {
64
+ max?: number;
65
+ idleTimeoutMillis?: number;
66
+ connectionTimeoutMillis?: number;
67
+ };
68
+ }
69
+ /**
70
+ * Apps may augment this registry to type their named connections.
71
+ */
72
+ interface CustomConnectionRegistry {}
73
+ interface DatabaseConfig {
74
+ /**
75
+ * The name of the connection used when none is requested.
76
+ */
77
+ default: string;
78
+ /**
79
+ * The configured connections, keyed by the name referenced by `default` and
80
+ * by the model/query APIs.
81
+ */
82
+ connections: Record<string, ConnectionConfig> & CustomConnectionRegistry;
83
+ }
84
+ //#endregion
85
+ //#region src/config.d.ts
86
+ /**
87
+ * Read a value from the `database` configuration namespace with a fallback.
88
+ *
89
+ * Never throws when the config file is missing; returns the default instead.
90
+ *
91
+ * @param key Dot path within the database config.
92
+ * @param defaultValue Value returned when the key is not set.
93
+ */
94
+ declare const configure: <T extends DotPath<DatabaseConfig>>(key: T, defaultValue: unknown) => DotPathValue<DatabaseConfig, T>;
95
+ /**
96
+ * Resolve a configured {@link ConnectionConfig} by name, or the default
97
+ * connection when none is given.
98
+ *
99
+ * @param name The connection name. Defaults to `database.default`.
100
+ * @throws when the named connection is not configured.
101
+ */
102
+ declare const resolveConnection: (name?: string) => ConnectionConfig;
103
+ //#endregion
104
+ //#region src/kysely.d.ts
105
+ /**
106
+ * Build a pg {@link Pool} from a {@link ConnectionConfig}. A `url` (or
107
+ * `DATABASE_URL`) wins over the discrete `host`/`port`/`user`/... fields.
108
+ *
109
+ * @param connection
110
+ * @returns
111
+ */
112
+ declare const createPool: (connection: ConnectionConfig) => Pool;
113
+ /**
114
+ * Build a Kysely instance for the given connection using the Postgres dialect.
115
+ *
116
+ * @param connection
117
+ * @returns
118
+ */
119
+ declare const createKysely: <DB = Record<string, never>>(connection: ConnectionConfig) => Kysely<DB>;
120
+ /**
121
+ * Build an ArkORM Kysely adapter for the given connection. This is what binds
122
+ * models to the database.
123
+ *
124
+ * @param connection
125
+ * @returns
126
+ */
127
+ declare const createAdapter: (connection: ConnectionConfig) => _$arkormx.KyselyDatabaseAdapter;
128
+ //#endregion
129
+ //#region src/arkorm.d.ts
130
+ interface DefineArkormConfigOptions extends Partial<ArkormConfig> {
131
+ /**
132
+ * The application's database configuration. When provided (and no explicit
133
+ * `adapter` is set), the adapter is built from the resolved connection.
134
+ */
135
+ database?: DatabaseConfig;
136
+ /**
137
+ * The connection name to bind. Defaults to `database.default`.
138
+ */
139
+ connection?: string;
140
+ }
141
+ /**
142
+ * Build the ArkORM config object for an application from its database
143
+ * configuration. Use this only when authoring an explicit `arkormx.config.ts`;
144
+ * by default the framework configures ArkORM for you (see {@link bootArkorm}).
145
+ *
146
+ * @param options Arkorm config plus the app's database config.
147
+ */
148
+ declare const defineArkormConfig: (options?: DefineArkormConfigOptions) => ArkormConfig;
149
+ interface BootArkormOptions extends Partial<ArkormConfig> {
150
+ /** The connection name to bind. Defaults to `database.default`. */
151
+ connection?: string;
152
+ }
153
+ /**
154
+ * Configure ArkORM natively from `src/config/database.ts`, so applications work
155
+ * without an `arkormx.config.ts`.
156
+ *
157
+ * Builds the adapter from the default (or named) connection, registers the
158
+ * conventional paths and the resora pagination resolver, and binds models. A
159
+ * user-provided `arkormx.config.{ts,js}` always wins — in that case this is a
160
+ * no-op and ArkORM loads the file instead.
161
+ *
162
+ * @param options Optional overrides merged over the derived config.
163
+ * @returns Whether the framework applied its configuration.
164
+ */
165
+ declare const bootArkorm: (options?: BootArkormOptions) => boolean;
166
+ //#endregion
167
+ export { BootArkormOptions, ConnectionConfig, ConnectionPassword, CustomConnectionRegistry, DB, DatabaseConfig, DefineArkormConfigOptions, Migration, Model, ModelFactory, SchemaBuilder, Seeder, ValidatorDBDriver, bootArkorm, configure, createAdapter, createKysely, createPool, defineArkormConfig, resolveConnection };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as ValidatorDBDriver } from "./ValidatorDBDriver-DUvJ-_jF.js";
1
+ import { a as createPool, c as ValidatorDBDriver, i as createKysely, n as defineArkormConfig, o as configure, r as createAdapter, s as resolveConnection, t as bootArkorm } from "./arkorm-BdOIitRp.js";
2
2
  import { DB as DB$1, Migration as Migration$1, Model as Model$1, ModelFactory as ModelFactory$1, SchemaBuilder as SchemaBuilder$1, Seeder as Seeder$1 } from "arkormx";
3
3
  //#region src/extensions/DB.ts
4
4
  var DB = class extends DB$1 {};
@@ -18,4 +18,4 @@ var SchemaBuilder = class extends SchemaBuilder$1 {};
18
18
  //#region src/extensions/Seeder.ts
19
19
  var Seeder = class extends Seeder$1 {};
20
20
  //#endregion
21
- export { DB, Migration, Model, ModelFactory, SchemaBuilder, Seeder, ValidatorDBDriver };
21
+ export { DB, Migration, Model, ModelFactory, SchemaBuilder, Seeder, ValidatorDBDriver, bootArkorm, configure, createAdapter, createKysely, createPool, defineArkormConfig, resolveConnection };
package/dist/setup.js CHANGED
@@ -1,9 +1,18 @@
1
- import { t as ValidatorDBDriver } from "./ValidatorDBDriver-DUvJ-_jF.js";
1
+ import { c as ValidatorDBDriver, t as bootArkorm } from "./arkorm-BdOIitRp.js";
2
2
  import { Validator } from "kanun";
3
+ import "dotenv/config";
3
4
  import { CoreRouter } from "clear-router/core";
4
5
  import { clearRouterPlugin } from "@arkormx/plugin-clear-router";
5
6
  //#region src/setup.ts
6
7
  CoreRouter.use(clearRouterPlugin);
7
8
  Validator.useDatabase(new ValidatorDBDriver());
9
+ /**
10
+ * Configure ArkORM from `src/config/database.ts` so the application works
11
+ * without an `arkormx.config.ts`. A user-provided config file still takes
12
+ * precedence, and an unconfigured database (e.g. some CLI contexts) is ignored.
13
+ */
14
+ try {
15
+ bootArkorm();
16
+ } catch {}
8
17
  //#endregion
9
18
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arkstack/database",
3
- "version": "0.12.36",
3
+ "version": "0.13.0",
4
4
  "type": "module",
5
5
  "description": "Database module for Arkstack, providing core database integration and data layer features.",
6
6
  "homepage": "https://arkstack.toneflix.net",
@@ -48,10 +48,17 @@
48
48
  "clear-router": "^2.8.8"
49
49
  },
50
50
  "dependencies": {
51
- "@arkormx/plugin-clear-router": "^0.1.45",
52
- "arkormx": "^2.9.0",
53
- "@arkstack/common": "^0.12.36",
54
- "@arkstack/contract": "^0.12.36"
51
+ "@arkormx/plugin-clear-router": "^0.1.46",
52
+ "dotenv": "^17.4.2",
53
+ "arkormx": "^2.9.2",
54
+ "kysely": "^0.28.15",
55
+ "pg": "^8.20.0",
56
+ "resora": "^1.3.26",
57
+ "@arkstack/common": "^0.13.0",
58
+ "@arkstack/contract": "^0.13.0"
59
+ },
60
+ "devDependencies": {
61
+ "@types/pg": "^8.16.0"
55
62
  },
56
63
  "scripts": {
57
64
  "build": "tsdown --config-loader unrun",
@@ -1,16 +0,0 @@
1
- import { DB } from "arkormx";
2
- import { IDatabaseDriver } from "kanun";
3
- //#region src/ValidatorDBDriver.ts
4
- var ValidatorDBDriver = class extends IDatabaseDriver {
5
- async exists({ table, column, value, ignore }) {
6
- try {
7
- const query = DB.table(table).where({ [column]: value });
8
- if (ignore) query.whereNot({ [column]: ignore });
9
- return await query.exists();
10
- } catch {
11
- return false;
12
- }
13
- }
14
- };
15
- //#endregion
16
- export { ValidatorDBDriver as t };