@neupgroup/mapper 1.0.0 → 1.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/README.md +168 -13
- package/config.d.ts +39 -0
- package/config.js +49 -0
- package/index.d.ts +2 -0
- package/index.js +2 -0
- package/mapper.d.ts +59 -0
- package/mapper.js +117 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,27 +1,32 @@
|
|
|
1
1
|
# Neup.Mapper (Library)
|
|
2
2
|
|
|
3
|
-
Neup.Mapper is
|
|
3
|
+
Neup.Mapper is a small, framework-agnostic TypeScript library that provides:
|
|
4
4
|
|
|
5
|
-
- Database connectivity via adapter pattern (bring your own driver)
|
|
6
|
-
- HTTP API access via adapter pattern (bring your own client)
|
|
5
|
+
- Database connectivity via an adapter pattern (bring your own driver)
|
|
6
|
+
- HTTP API access via an adapter pattern (bring your own client)
|
|
7
|
+
- A simple config/registry to define multiple named connections ("maps")
|
|
7
8
|
|
|
8
9
|
## Install
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
```
|
|
12
|
+
npm install @neupgroup/mapper
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
If you’re working locally on the source:
|
|
11
16
|
|
|
12
17
|
```
|
|
13
18
|
npm install
|
|
14
19
|
npm run build
|
|
15
20
|
```
|
|
16
21
|
|
|
17
|
-
Artifacts are emitted
|
|
22
|
+
Artifacts (`*.js`, `*.d.ts`) are emitted into the project root.
|
|
18
23
|
|
|
19
|
-
##
|
|
24
|
+
## Quick Start (Adapter Basics)
|
|
20
25
|
|
|
21
26
|
### Database
|
|
22
27
|
|
|
23
28
|
```
|
|
24
|
-
import { createDb, IDbAdapter } from "
|
|
29
|
+
import { createDb, IDbAdapter } from "@neupgroup/mapper";
|
|
25
30
|
|
|
26
31
|
const adapter: IDbAdapter = {
|
|
27
32
|
async connect(config) {
|
|
@@ -45,7 +50,7 @@ await db.close();
|
|
|
45
50
|
### API
|
|
46
51
|
|
|
47
52
|
```
|
|
48
|
-
import { createApiClient, IApiAdapter } from "
|
|
53
|
+
import { createApiClient, IApiAdapter } from "@neupgroup/mapper";
|
|
49
54
|
|
|
50
55
|
const http: IApiAdapter = {
|
|
51
56
|
async request(req) {
|
|
@@ -58,9 +63,159 @@ const api = createApiClient("https://api.example.com", http, { Authorization: "B
|
|
|
58
63
|
const res = await api.get("/users");
|
|
59
64
|
```
|
|
60
65
|
|
|
61
|
-
##
|
|
66
|
+
## Config & Registry (Multiple Named Maps)
|
|
67
|
+
|
|
68
|
+
You can define multiple named connections (maps), similar to Firebase/Laravel style configs, and then refer to them in your application.
|
|
69
|
+
|
|
70
|
+
Create a `mapper.config.ts` in your app:
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
// mapper.config.ts
|
|
74
|
+
import { defineConfig, IApiAdapter, IDbAdapter } from "@neupgroup/mapper";
|
|
75
|
+
|
|
76
|
+
// Provide your concrete adapters
|
|
77
|
+
const http: IApiAdapter = {
|
|
78
|
+
async request(req) {
|
|
79
|
+
// Make HTTP requests using fetch/axios and return { status, headers, data }
|
|
80
|
+
return { status: 200, data: { ok: true } };
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const dbAdapter: IDbAdapter = {
|
|
85
|
+
async connect(config) {
|
|
86
|
+
// Initialize DB driver with credentials from config
|
|
87
|
+
},
|
|
88
|
+
async query(sql, params) {
|
|
89
|
+
return [];
|
|
90
|
+
},
|
|
91
|
+
async close() {},
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export default defineConfig({
|
|
95
|
+
maps: [
|
|
96
|
+
{
|
|
97
|
+
kind: "api",
|
|
98
|
+
name: "core",
|
|
99
|
+
baseUrl: "https://api.example.com",
|
|
100
|
+
defaultHeaders: { Authorization: "Bearer <token>" },
|
|
101
|
+
adapter: http,
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
kind: "db",
|
|
105
|
+
name: "analytics",
|
|
106
|
+
connection: { host: "db.local", user: "app", password: "secret" },
|
|
107
|
+
adapter: dbAdapter,
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
});
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Use the registry in your app code:
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
import config from "./mapper.config";
|
|
117
|
+
import { loadConfig } from "@neupgroup/mapper";
|
|
118
|
+
|
|
119
|
+
const maps = loadConfig(config);
|
|
120
|
+
|
|
121
|
+
// Use API
|
|
122
|
+
const api = maps.api("core");
|
|
123
|
+
const res = await api.get("/users");
|
|
124
|
+
|
|
125
|
+
// Use DB
|
|
126
|
+
const db = maps.db("analytics");
|
|
127
|
+
await db.connect({ host: "db.local" });
|
|
128
|
+
const rows = await db.query("SELECT * FROM events WHERE kind = ?", ["click"]);
|
|
129
|
+
await db.close();
|
|
130
|
+
|
|
131
|
+
// Introspection
|
|
132
|
+
maps.list(); // [{ name: "core", kind: "api" }, { name: "analytics", kind: "db" }]
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Global `mapper()` API (No App Init)
|
|
136
|
+
|
|
137
|
+
If you prefer a PHP‑style fluent syntax without explicit app initialization, use the global `mapper()` function:
|
|
138
|
+
|
|
139
|
+
### Access existing maps (registered via config)
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
import appConfig from './mapper.config';
|
|
143
|
+
import { useConfig, mapper } from '@neupgroup/mapper';
|
|
144
|
+
|
|
145
|
+
// Register once (e.g., at app entry) — then use anywhere
|
|
146
|
+
useConfig(appConfig);
|
|
147
|
+
|
|
148
|
+
// DB usage
|
|
149
|
+
const usersTable = mapper('analytics').table('users');
|
|
150
|
+
const allUsers = await usersTable.selectAll();
|
|
151
|
+
const activeUsers = await usersTable.query('WHERE status = ?', ['active']);
|
|
152
|
+
|
|
153
|
+
// API usage
|
|
154
|
+
const usersEndpoint = mapper('core').path('/users');
|
|
155
|
+
const res = await usersEndpoint.get();
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Create ephemeral maps with chaining
|
|
159
|
+
|
|
160
|
+
```
|
|
161
|
+
import { mapper } from '@neupgroup/mapper';
|
|
162
|
+
import type { IDbAdapter, IApiAdapter } from '@neupgroup/mapper';
|
|
163
|
+
|
|
164
|
+
const dbAdapter: IDbAdapter = { /* implement connect/query/close */ };
|
|
165
|
+
const httpAdapter: IApiAdapter = { /* implement request */ };
|
|
166
|
+
|
|
167
|
+
// Create and register a DB map
|
|
168
|
+
mapper()
|
|
169
|
+
.create('tempDb', 'db')
|
|
170
|
+
.host('localhost')
|
|
171
|
+
.dbname('app')
|
|
172
|
+
.user('user')
|
|
173
|
+
.pass('secret')
|
|
174
|
+
.adapter(dbAdapter)
|
|
175
|
+
.save();
|
|
176
|
+
|
|
177
|
+
// Use it anywhere
|
|
178
|
+
const t = mapper('tempDb').table('logs');
|
|
179
|
+
const rows = await t.query('WHERE level = ?', ['error']);
|
|
180
|
+
|
|
181
|
+
// Create and register an API map
|
|
182
|
+
mapper()
|
|
183
|
+
.create('tempApi', 'api')
|
|
184
|
+
.baseUrl('https://api.example.com')
|
|
185
|
+
.header('Authorization', 'Bearer token')
|
|
186
|
+
.adapter(httpAdapter)
|
|
187
|
+
.save();
|
|
188
|
+
|
|
189
|
+
const endpoint = mapper('tempApi').path('/events');
|
|
190
|
+
await endpoint.post({ type: 'click' });
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Notes:
|
|
194
|
+
- `mapper('name').table('...')` is for DB maps; `mapper('name').path('...')` is for API maps.
|
|
195
|
+
- Duplicate map names are prevented; attempting to re‑use a name throws a clear error.
|
|
196
|
+
- The builder requires `create(name, kind)` and `adapter(...)` before `save()`.
|
|
197
|
+
|
|
198
|
+
### Notes
|
|
199
|
+
|
|
200
|
+
- The registry does not auto-connect databases; call `db.connect()` when appropriate.
|
|
201
|
+
- The API client merges `defaultHeaders` with any per-call headers.
|
|
202
|
+
- Each map is keyed by a unique `name`; use `maps.get(name)` if you don’t know the type at compile time.
|
|
203
|
+
|
|
204
|
+
## API Reference
|
|
205
|
+
|
|
206
|
+
- `createDb(adapter: IDbAdapter): Db`
|
|
207
|
+
- `createApiClient(baseUrl: string, adapter: IApiAdapter, defaultHeaders?: Record<string,string>): ApiClient`
|
|
208
|
+
- `defineConfig(config: MapperConfig): MapperConfig`
|
|
209
|
+
- `createRegistry(config: MapperConfig): MapperRegistry`
|
|
210
|
+
- `loadConfig(config: MapperConfig): MapperRegistry` (alias)
|
|
211
|
+
|
|
212
|
+
## Publish
|
|
213
|
+
|
|
214
|
+
This package is configured for public publish (scoped):
|
|
215
|
+
|
|
216
|
+
```
|
|
217
|
+
npm run build
|
|
218
|
+
npm publish --access public
|
|
219
|
+
```
|
|
62
220
|
|
|
63
|
-
|
|
64
|
-
- Build emits to `dist/` with `index.js` and `index.d.ts` for consumers.
|
|
65
|
-
- No React/Next.js UI is included; the project is library-only.
|
|
66
|
-
- Add any DB driver or HTTP client as dependencies in your own app.
|
|
221
|
+
Ensure your `package.json` has `"private": false` and `"publishConfig": { "access": "public" }`.
|
package/config.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type ApiAdapter, type ApiClient } from "./api";
|
|
2
|
+
import { type DbAdapter, type Db } from "./db";
|
|
3
|
+
export type MapKind = "api" | "db";
|
|
4
|
+
export interface ApiMapConfig {
|
|
5
|
+
kind: "api";
|
|
6
|
+
name: string;
|
|
7
|
+
baseUrl?: string;
|
|
8
|
+
defaultHeaders?: Record<string, string>;
|
|
9
|
+
adapter: ApiAdapter;
|
|
10
|
+
}
|
|
11
|
+
export interface DbMapConfig {
|
|
12
|
+
kind: "db";
|
|
13
|
+
name: string;
|
|
14
|
+
connection?: Record<string, unknown>;
|
|
15
|
+
adapter: DbAdapter;
|
|
16
|
+
}
|
|
17
|
+
export type MapConfig = ApiMapConfig | DbMapConfig;
|
|
18
|
+
export interface MapperConfig {
|
|
19
|
+
maps: MapConfig[];
|
|
20
|
+
}
|
|
21
|
+
export interface MapperRegistry {
|
|
22
|
+
/** Get a named API client */
|
|
23
|
+
api: (name: string) => ApiClient;
|
|
24
|
+
/** Get a named DB client */
|
|
25
|
+
db: (name: string) => Db;
|
|
26
|
+
/** Get any named client (API or DB) */
|
|
27
|
+
get: (name: string) => ApiClient | Db;
|
|
28
|
+
/** List registered maps */
|
|
29
|
+
list: () => {
|
|
30
|
+
name: string;
|
|
31
|
+
kind: MapKind;
|
|
32
|
+
}[];
|
|
33
|
+
}
|
|
34
|
+
/** Helper to define config with proper type inference */
|
|
35
|
+
export declare function defineConfig(config: MapperConfig): MapperConfig;
|
|
36
|
+
/** Create a registry of named API/DB maps from configuration */
|
|
37
|
+
export declare function createRegistry(config: MapperConfig): MapperRegistry;
|
|
38
|
+
/** alias */
|
|
39
|
+
export declare const loadConfig: typeof createRegistry;
|
package/config.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { createApiClient } from "./api";
|
|
2
|
+
import { createDb } from "./db";
|
|
3
|
+
/** Helper to define config with proper type inference */
|
|
4
|
+
export function defineConfig(config) {
|
|
5
|
+
return config;
|
|
6
|
+
}
|
|
7
|
+
/** Create a registry of named API/DB maps from configuration */
|
|
8
|
+
export function createRegistry(config) {
|
|
9
|
+
const entries = new Map();
|
|
10
|
+
for (const m of config.maps) {
|
|
11
|
+
if (!m.name)
|
|
12
|
+
throw new Error("Map config requires a unique 'name'");
|
|
13
|
+
if (entries.has(m.name))
|
|
14
|
+
throw new Error(`Duplicate map name '${m.name}'`);
|
|
15
|
+
if (m.kind === "api") {
|
|
16
|
+
const client = createApiClient(m.baseUrl ?? "", m.adapter, m.defaultHeaders);
|
|
17
|
+
entries.set(m.name, { kind: "api", value: client });
|
|
18
|
+
}
|
|
19
|
+
else if (m.kind === "db") {
|
|
20
|
+
const db = createDb(m.adapter);
|
|
21
|
+
// Optional: pre-connect using provided connection config.
|
|
22
|
+
// Consumers may choose to call db.connect() themselves.
|
|
23
|
+
// if (m.connection) await db.connect(m.connection);
|
|
24
|
+
entries.set(m.name, { kind: "db", value: db });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const getApi = (name) => {
|
|
28
|
+
const entry = entries.get(name);
|
|
29
|
+
if (!entry || entry.kind !== "api")
|
|
30
|
+
throw new Error(`API map '${name}' not found`);
|
|
31
|
+
return entry.value;
|
|
32
|
+
};
|
|
33
|
+
const getDb = (name) => {
|
|
34
|
+
const entry = entries.get(name);
|
|
35
|
+
if (!entry || entry.kind !== "db")
|
|
36
|
+
throw new Error(`DB map '${name}' not found`);
|
|
37
|
+
return entry.value;
|
|
38
|
+
};
|
|
39
|
+
const get = (name) => {
|
|
40
|
+
const entry = entries.get(name);
|
|
41
|
+
if (!entry)
|
|
42
|
+
throw new Error(`Map '${name}' not found`);
|
|
43
|
+
return entry.value;
|
|
44
|
+
};
|
|
45
|
+
const list = () => Array.from(entries.entries()).map(([name, e]) => ({ name, kind: e.kind }));
|
|
46
|
+
return { api: getApi, db: getDb, get, list };
|
|
47
|
+
}
|
|
48
|
+
/** alias */
|
|
49
|
+
export const loadConfig = createRegistry;
|
package/index.d.ts
CHANGED
package/index.js
CHANGED
package/mapper.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type ApiAdapter, type ApiClient } from "./api";
|
|
2
|
+
import { type DbAdapter, type Db, type QueryParams } from "./db";
|
|
3
|
+
import type { MapKind, MapperConfig } from "./config";
|
|
4
|
+
export declare function useConfig(config: MapperConfig): void;
|
|
5
|
+
export declare function list(): {
|
|
6
|
+
name: string;
|
|
7
|
+
kind: MapKind;
|
|
8
|
+
}[];
|
|
9
|
+
export interface ApiPath {
|
|
10
|
+
get<T = unknown>(headers?: Record<string, string>): Promise<{
|
|
11
|
+
status: number;
|
|
12
|
+
headers?: Record<string, string>;
|
|
13
|
+
data?: T;
|
|
14
|
+
}>;
|
|
15
|
+
post<T = unknown>(body?: unknown, headers?: Record<string, string>): Promise<{
|
|
16
|
+
status: number;
|
|
17
|
+
headers?: Record<string, string>;
|
|
18
|
+
data?: T;
|
|
19
|
+
}>;
|
|
20
|
+
put<T = unknown>(body?: unknown, headers?: Record<string, string>): Promise<{
|
|
21
|
+
status: number;
|
|
22
|
+
headers?: Record<string, string>;
|
|
23
|
+
data?: T;
|
|
24
|
+
}>;
|
|
25
|
+
delete<T = unknown>(headers?: Record<string, string>): Promise<{
|
|
26
|
+
status: number;
|
|
27
|
+
headers?: Record<string, string>;
|
|
28
|
+
data?: T;
|
|
29
|
+
}>;
|
|
30
|
+
}
|
|
31
|
+
export interface DbTable {
|
|
32
|
+
selectAll<T = unknown>(): Promise<T[]>;
|
|
33
|
+
query<T = unknown>(sqlSuffix?: string, params?: QueryParams): Promise<T[]>;
|
|
34
|
+
}
|
|
35
|
+
export interface MapperHandle {
|
|
36
|
+
name(): string;
|
|
37
|
+
kind(): MapKind;
|
|
38
|
+
client(): ApiClient | Db;
|
|
39
|
+
table(name: string): DbTable;
|
|
40
|
+
path(p: string): ApiPath;
|
|
41
|
+
}
|
|
42
|
+
export interface MapperBuilderStart {
|
|
43
|
+
create(name: string, kind: MapKind): MapperBuilder;
|
|
44
|
+
}
|
|
45
|
+
export interface MapperBuilder {
|
|
46
|
+
adapter(adapter: ApiAdapter | DbAdapter): MapperBuilder;
|
|
47
|
+
save(): MapperHandle;
|
|
48
|
+
baseUrl(url: string): MapperBuilder;
|
|
49
|
+
headers(headers: Record<string, string>): MapperBuilder;
|
|
50
|
+
header(key: string, value: string): MapperBuilder;
|
|
51
|
+
host(host: string): MapperBuilder;
|
|
52
|
+
dbname(name: string): MapperBuilder;
|
|
53
|
+
user(user: string): MapperBuilder;
|
|
54
|
+
pass(pass: string): MapperBuilder;
|
|
55
|
+
port(port: number): MapperBuilder;
|
|
56
|
+
option(key: string, value: unknown): MapperBuilder;
|
|
57
|
+
}
|
|
58
|
+
export declare function mapper(): MapperBuilderStart;
|
|
59
|
+
export declare function mapper(name: string): MapperHandle;
|
package/mapper.js
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { createApiClient } from "./api";
|
|
2
|
+
import { createDb } from "./db";
|
|
3
|
+
const registry = new Map();
|
|
4
|
+
function ensureUnique(name) {
|
|
5
|
+
if (registry.has(name))
|
|
6
|
+
throw new Error(`Duplicate map name '${name}'`);
|
|
7
|
+
}
|
|
8
|
+
export function useConfig(config) {
|
|
9
|
+
for (const m of config.maps) {
|
|
10
|
+
ensureUnique(m.name);
|
|
11
|
+
if (m.kind === "api") {
|
|
12
|
+
const client = createApiClient(m.baseUrl ?? "", m.adapter, m.defaultHeaders);
|
|
13
|
+
registry.set(m.name, { kind: "api", value: client });
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
const db = createDb(m.adapter);
|
|
17
|
+
registry.set(m.name, { kind: "db", value: db });
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export function list() {
|
|
22
|
+
return Array.from(registry.entries()).map(([name, e]) => ({ name, kind: e.kind }));
|
|
23
|
+
}
|
|
24
|
+
function asHandle(name, entry) {
|
|
25
|
+
const base = {
|
|
26
|
+
name: () => name,
|
|
27
|
+
kind: () => entry.kind,
|
|
28
|
+
client: () => entry.value,
|
|
29
|
+
};
|
|
30
|
+
const table = (t) => {
|
|
31
|
+
if (entry.kind !== "db")
|
|
32
|
+
throw new Error(`Map '${name}' is not a DB. Use path() for API.`);
|
|
33
|
+
const db = entry.value;
|
|
34
|
+
return {
|
|
35
|
+
selectAll: () => db.query(`SELECT * FROM ${t}`),
|
|
36
|
+
query: (suffix, params) => {
|
|
37
|
+
const sql = suffix ? `SELECT * FROM ${t} ${suffix}` : `SELECT * FROM ${t}`;
|
|
38
|
+
return db.query(sql, params);
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
const path = (p) => {
|
|
43
|
+
if (entry.kind !== "api")
|
|
44
|
+
throw new Error(`Map '${name}' is not an API. Use table() for DB.`);
|
|
45
|
+
const api = entry.value;
|
|
46
|
+
const normalized = p.startsWith("/") ? p : `/${p}`;
|
|
47
|
+
return {
|
|
48
|
+
get: (headers) => api.get(normalized, headers),
|
|
49
|
+
post: (body, headers) => api.post(normalized, body, headers),
|
|
50
|
+
put: (body, headers) => api.put(normalized, body, headers),
|
|
51
|
+
delete: (headers) => api.delete(normalized, headers),
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
return { ...base, table, path };
|
|
55
|
+
}
|
|
56
|
+
class Builder {
|
|
57
|
+
constructor() {
|
|
58
|
+
this.conf = {};
|
|
59
|
+
}
|
|
60
|
+
create(name, kind) {
|
|
61
|
+
this.conf.name = name;
|
|
62
|
+
this.conf.kind = kind;
|
|
63
|
+
return this;
|
|
64
|
+
}
|
|
65
|
+
adapter(adapter) {
|
|
66
|
+
this.conf.adapter = adapter;
|
|
67
|
+
return this;
|
|
68
|
+
}
|
|
69
|
+
// API
|
|
70
|
+
baseUrl(url) { var _a; (_a = this.conf).api ?? (_a.api = {}); this.conf.api.baseUrl = url; return this; }
|
|
71
|
+
headers(headers) {
|
|
72
|
+
var _a;
|
|
73
|
+
(_a = this.conf).api ?? (_a.api = {});
|
|
74
|
+
const current = this.conf.api.defaultHeaders ?? {};
|
|
75
|
+
this.conf.api.defaultHeaders = { ...current, ...headers };
|
|
76
|
+
return this;
|
|
77
|
+
}
|
|
78
|
+
header(key, value) { return this.headers({ [key]: value }); }
|
|
79
|
+
// DB
|
|
80
|
+
ensureConn() {
|
|
81
|
+
var _a;
|
|
82
|
+
(_a = this.conf).db ?? (_a.db = {});
|
|
83
|
+
if (!this.conf.db.connection)
|
|
84
|
+
this.conf.db.connection = {};
|
|
85
|
+
}
|
|
86
|
+
host(host) { this.ensureConn(); this.conf.db.connection.host = host; return this; }
|
|
87
|
+
dbname(name) { this.ensureConn(); this.conf.db.connection.dbname = name; return this; }
|
|
88
|
+
user(user) { this.ensureConn(); this.conf.db.connection.user = user; return this; }
|
|
89
|
+
pass(pass) { this.ensureConn(); this.conf.db.connection.pass = pass; return this; }
|
|
90
|
+
port(port) { this.ensureConn(); this.conf.db.connection.port = port; return this; }
|
|
91
|
+
option(key, value) { this.ensureConn(); this.conf.db.connection[key] = value; return this; }
|
|
92
|
+
save() {
|
|
93
|
+
const name = this.conf.name;
|
|
94
|
+
const kind = this.conf.kind;
|
|
95
|
+
const adapter = this.conf.adapter;
|
|
96
|
+
if (!name || !kind || !adapter)
|
|
97
|
+
throw new Error("Builder requires name, kind, and adapter before save()");
|
|
98
|
+
ensureUnique(name);
|
|
99
|
+
if (kind === "api") {
|
|
100
|
+
const client = createApiClient(this.conf.api?.baseUrl ?? "", adapter, this.conf.api?.defaultHeaders);
|
|
101
|
+
registry.set(name, { kind: "api", value: client });
|
|
102
|
+
return asHandle(name, registry.get(name));
|
|
103
|
+
}
|
|
104
|
+
const db = createDb(adapter);
|
|
105
|
+
registry.set(name, { kind: "db", value: db });
|
|
106
|
+
return asHandle(name, registry.get(name));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
export function mapper(name) {
|
|
110
|
+
if (typeof name === "string") {
|
|
111
|
+
const entry = registry.get(name);
|
|
112
|
+
if (!entry)
|
|
113
|
+
throw new Error(`Map '${name}' not found. Register with useConfig() or builder.`);
|
|
114
|
+
return asHandle(name, entry);
|
|
115
|
+
}
|
|
116
|
+
return new Builder();
|
|
117
|
+
}
|