@neupgroup/mapper 1.1.0 → 1.2.1

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 CHANGED
@@ -1,221 +1,37 @@
1
- # Neup.Mapper (Library)
2
-
3
- Neup.Mapper is a small, framework-agnostic TypeScript library that provides:
4
-
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")
8
-
9
- ## Install
10
-
11
- ```
12
- npm install @neupgroup/mapper
13
- ```
14
-
15
- If you’re working locally on the source:
16
-
17
- ```
18
- npm install
19
- npm run build
20
- ```
21
-
22
- Artifacts (`*.js`, `*.d.ts`) are emitted into the project root.
23
-
24
- ## Quick Start (Adapter Basics)
25
-
26
- ### Database
27
-
28
- ```
29
- import { createDb, IDbAdapter } from "@neupgroup/mapper";
30
-
31
- const adapter: IDbAdapter = {
32
- async connect(config) {
33
- // initialize your driver, e.g., pg, mysql2, sqlite, etc.
34
- },
35
- async query(sql, params) {
36
- // delegate to your driver
37
- return [];
38
- },
39
- async close() {
40
- // close driver resources
41
- },
42
- };
43
-
44
- const db = createDb(adapter);
45
- await db.connect({ host: "localhost" });
46
- const rows = await db.query("SELECT 1");
47
- await db.close();
48
- ```
49
-
50
- ### API
51
-
52
- ```
53
- import { createApiClient, IApiAdapter } from "@neupgroup/mapper";
54
-
55
- const http: IApiAdapter = {
56
- async request(req) {
57
- // use fetch/axios/undici, return { status, headers, data }
58
- return { status: 200, data: { ok: true } };
59
- },
60
- };
61
-
62
- const api = createApiClient("https://api.example.com", http, { Authorization: "Bearer token" });
63
- const res = await api.get("/users");
64
- ```
65
-
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
- ```
220
-
221
- Ensure your `package.json` has `"private": false` and `"publishConfig": { "access": "public" }`.
1
+ # @neupgroup/mapper
2
+
3
+ Core library for Neup.Mapper. Provides simple schema registration utilities that can be expanded over time.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @neupgroup/mapper
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```ts
14
+ import Mapper from '@neupgroup/mapper';
15
+
16
+ const mapper = new Mapper();
17
+
18
+ mapper.register({
19
+ name: 'User',
20
+ fields: [
21
+ { name: 'id', type: 'string' },
22
+ { name: 'email', type: 'string' },
23
+ { name: 'createdAt', type: 'date' },
24
+ ],
25
+ });
26
+
27
+ console.log(mapper.list());
28
+ ```
29
+
30
+ ## Build
31
+
32
+ ```bash
33
+ npx tsc -p ./tsconfig.json
34
+ ```
35
+
36
+ Outputs are generated to `dist/` with type declarations.
37
+
package/dist/docs.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export declare const documentationMd = "\n# Mapper Library Documentation\n\nWelcome to `@neupgroup/mapper`. This guide covers:\n- Installation\n- Configuring connections (DSL and UI)\n- Using connections in code\n- Creating schemas (ORM)\n- Configuring and using schemas\n- CRUD operations: insert, update, delete, fetch\n- Error handling and troubleshooting\n\n---\n\n## Installation\n\n- Install from npm:\n `npm install @neupgroup/mapper`\n- In this workspace, the app depends on the library via `workspace:*`. Build the library when you update it:\n `cd library && npm run build`\n- Import helpers:\n `import { parseConnectionsDsl, toNormalizedConnections } from '@neupgroup/mapper'`\n\n---\n\n## Configure Connections\n\nYou can configure connections in two ways:\n\n1) DSL File (recommended)\n2) UI Configure page (runtime setup)\n\n### 1) DSL Format\n\nCreate `connections.dsl` at your project root:\n\n```\nconnections = [\n mysql_prod: {\n type: mysql\n host: 127.0.0.1\n port: 3306\n user: root\n password: \"s3cr3t\"\n database: appdb\n }\n\n mongo_dev: {\n type: mongodb\n uri: \"mongodb://127.0.0.1:27017\"\n database: devdb\n }\n\n firestore_local: {\n type: firestore\n projectId: my-project\n applicationDefault: true\n }\n\n http_api: {\n type: api\n baseUrl: \"https://api.example.com\"\n token: \"abc123\"\n }\n]\n```\n\nNotes:\n- `type` (or `dbType`) defaults to `api` if omitted.\n- Values can be unquoted or quoted; comments using `#` are ignored.\n\nParse and normalize:\n\n```ts\nimport { parseConnectionsDsl, toNormalizedConnections } from '@neupgroup/mapper'\n\nconst text = await fs.promises.readFile('connections.dsl', 'utf8')\nconst envMap = parseConnectionsDsl(text)\nconst connections = toNormalizedConnections(envMap)\n// connections: Array<{ name, type, key }>\n```\n\n### 2) UI Configure Page\n\n- Go to `/configure` to define connections at runtime.\n- Use \"Generate collective env\" to produce `connections.dsl` from configured connections.\n- Download the file and commit it or load at startup.\n\n---\n\n## Use Connections in Code\n\n### Normalize and route by type\n\n```ts\nimport { parseConnectionsDsl, toNormalizedConnections } from '@neupgroup/mapper'\nimport { connection, schema } from '@neupgroup/mapper'\n\nconst text = await fs.promises.readFile('connections.dsl', 'utf8')\nconst envMap = parseConnectionsDsl(text)\nconst conns = toNormalizedConnections(envMap)\n\n// Register connections\nconst conRegistry = connection()\nfor (const c of conns) {\n conRegistry.register({ name: c.name, type: c.type, key: c.key })\n}\n\n// Use with schemas\nconst sm = schema(conRegistry)\n```\n\n### Direct construction\n\n```ts\nconst conRegistry = connection()\nconRegistry.create('mysql_prod', 'mysql').key({\n host: '127.0.0.1',\n port: 3306,\n user: 'root',\n password: 's3cr3t',\n database: 'appdb',\n})\n```\n\n---\n\n## Schemas and Models\n\nSchemas define structure for collections/tables bound to a connection.\n\n### Define and register a schema\n\n```ts\nimport { schema } from '@neupgroup/mapper'\n\nconst sm = schema(conRegistry)\n\nsm.create('User')\n .use({ connection: 'mysql_prod', collection: 'users' })\n .setStructure({\n id: 'string primary',\n email: 'string unique',\n name: 'string editable',\n createdAt: 'date',\n '?field': 'allow-undefined', // optional: permit fields not listed\n })\n\nconst User = sm.use('User')\n```\n\n---\n\n## CRUD Operations\n\nAll operations return Promises and may throw on errors.\n\n### Insert\n\n```ts\nconst createdId = await User.add({\n id: 'u_123',\n email: 'alice@example.com',\n name: 'Alice',\n createdAt: new Date(),\n})\n```\n\n### Update\n\n```ts\nawait User.where(['id', 'u_123']).to({ name: 'Alice Cooper' }).updateOne()\n```\n\n### Delete\n\n```ts\nawait User.where(['id', 'u_123']).deleteOne()\n```\n\n### Fetch / Query\n\n```ts\nconst one = await User.where(['id', 'u_123']).getOne()\nconst many = await User.where('email', '%@example.com', 'like').get()\n```\n\n---\n\n## Schema Configuration Details\n\n- `primary`: marks primary key; used for updates/deletes.\n- `unique`: enforces uniqueness in supported backends.\n- `editable`: indicates fields commonly modified via UI.\n- `type`: one of `string`, `number`, `boolean`, `date`, `int`.\n- `?field`: enables accepting fields not defined in the schema.\n\n---\n\n## Error Handling\n\nWrap operations in `try/catch` and inspect known error shapes.\n\n```ts\ntry {\n const user = await User.where(['id', 'u_404']).getOne()\n if (!user) {\n // handle not found gracefully\n }\n} catch (err) {\n if (err && typeof err === 'object' && 'code' in err) {\n console.error('Database error code:', (err as any).code)\n }\n console.error('Unexpected error', err)\n}\n```\n\nRecommendations:\n- Validate required creds before initializing connections.\n- Prefer parameterized queries or ORM filters over string concatenation.\n- Log request IDs and timestamps for audit trails.\n\n---\n\n## Troubleshooting\n\n- \"Connection refused\": check host/port/firewall and credentials.\n- \"Authentication failed\": verify tokens/passwords and token scopes.\n- \"Timeout\": review network paths and optimize query.\n- \"Schema mismatch\": ensure field names/types match the backend.\n\n---\n\n## API Quick Reference\n\n- `parseConnectionsDsl(text)` \u2192 Map of connectionName \u2192 key/value creds\n- `toNormalizedConnections(map)` \u2192 Array of { name, type, key }\n- `connection()` \u2192 Connection registry (create/register/list/get)\n- `schema(connections?)` \u2192 Schema manager; define, register, and use schemas\n\n---\n\n## End-to-End Example\n\n```ts\nimport { parseConnectionsDsl, toNormalizedConnections, connection, schema } from '@neupgroup/mapper'\n\nconst text = await fs.promises.readFile('connections.dsl', 'utf8')\nconst map = parseConnectionsDsl(text)\nconst conns = toNormalizedConnections(map)\n\nconst conRegistry = connection()\nfor (const c of conns) conRegistry.register({ name: c.name, type: c.type, key: c.key })\n\nconst sm = schema(conRegistry)\nsm.create('Product')\n .use({ connection: conns[0].name, collection: 'products' })\n .setStructure({\n id: 'string primary',\n title: 'string',\n price: 'number',\n tags: 'string',\n })\nconst Product = sm.use('Product')\n\nawait Product.add({ id: 'p_1', title: 'Widget', price: 9.99, tags: 'sale' })\nawait Product.where(['id', 'p_1']).to({ price: 7.99 }).updateOne()\nconst items = await Product.where('price', 10, '<').get()\nawait Product.where(['id', 'p_1']).deleteOne()\n```\n";
2
+ export declare function markdownToHtml(md: string): string;
3
+ export declare function getDocumentationHtml(): string;
package/dist/docs.js ADDED
@@ -0,0 +1,351 @@
1
+ // Centralized documentation for @neupgroup/mapper.
2
+ // Exports Markdown plus a minimal Markdown→HTML converter so apps can render
3
+ // without a heavy dependency. Keep content comprehensive and up-to-date.
4
+ export const documentationMd = `
5
+ # Mapper Library Documentation
6
+
7
+ Welcome to \`@neupgroup/mapper\`. This guide covers:
8
+ - Installation
9
+ - Configuring connections (DSL and UI)
10
+ - Using connections in code
11
+ - Creating schemas (ORM)
12
+ - Configuring and using schemas
13
+ - CRUD operations: insert, update, delete, fetch
14
+ - Error handling and troubleshooting
15
+
16
+ ---
17
+
18
+ ## Installation
19
+
20
+ - Install from npm:
21
+ \`npm install @neupgroup/mapper\`
22
+ - In this workspace, the app depends on the library via \`workspace:*\`. Build the library when you update it:
23
+ \`cd library && npm run build\`
24
+ - Import helpers:
25
+ \`import { parseConnectionsDsl, toNormalizedConnections } from '@neupgroup/mapper'\`
26
+
27
+ ---
28
+
29
+ ## Configure Connections
30
+
31
+ You can configure connections in two ways:
32
+
33
+ 1) DSL File (recommended)
34
+ 2) UI Configure page (runtime setup)
35
+
36
+ ### 1) DSL Format
37
+
38
+ Create \`connections.dsl\` at your project root:
39
+
40
+ \`\`\`
41
+ connections = [
42
+ mysql_prod: {
43
+ type: mysql
44
+ host: 127.0.0.1
45
+ port: 3306
46
+ user: root
47
+ password: "s3cr3t"
48
+ database: appdb
49
+ }
50
+
51
+ mongo_dev: {
52
+ type: mongodb
53
+ uri: "mongodb://127.0.0.1:27017"
54
+ database: devdb
55
+ }
56
+
57
+ firestore_local: {
58
+ type: firestore
59
+ projectId: my-project
60
+ applicationDefault: true
61
+ }
62
+
63
+ http_api: {
64
+ type: api
65
+ baseUrl: "https://api.example.com"
66
+ token: "abc123"
67
+ }
68
+ ]
69
+ \`\`\`
70
+
71
+ Notes:
72
+ - \`type\` (or \`dbType\`) defaults to \`api\` if omitted.
73
+ - Values can be unquoted or quoted; comments using \`#\` are ignored.
74
+
75
+ Parse and normalize:
76
+
77
+ \`\`\`ts
78
+ import { parseConnectionsDsl, toNormalizedConnections } from '@neupgroup/mapper'
79
+
80
+ const text = await fs.promises.readFile('connections.dsl', 'utf8')
81
+ const envMap = parseConnectionsDsl(text)
82
+ const connections = toNormalizedConnections(envMap)
83
+ // connections: Array<{ name, type, key }>
84
+ \`\`\`
85
+
86
+ ### 2) UI Configure Page
87
+
88
+ - Go to \`/configure\` to define connections at runtime.
89
+ - Use \"Generate collective env\" to produce \`connections.dsl\` from configured connections.
90
+ - Download the file and commit it or load at startup.
91
+
92
+ ---
93
+
94
+ ## Use Connections in Code
95
+
96
+ ### Normalize and route by type
97
+
98
+ \`\`\`ts
99
+ import { parseConnectionsDsl, toNormalizedConnections } from '@neupgroup/mapper'
100
+ import { connection, schema } from '@neupgroup/mapper'
101
+
102
+ const text = await fs.promises.readFile('connections.dsl', 'utf8')
103
+ const envMap = parseConnectionsDsl(text)
104
+ const conns = toNormalizedConnections(envMap)
105
+
106
+ // Register connections
107
+ const conRegistry = connection()
108
+ for (const c of conns) {
109
+ conRegistry.register({ name: c.name, type: c.type, key: c.key })
110
+ }
111
+
112
+ // Use with schemas
113
+ const sm = schema(conRegistry)
114
+ \`\`\`
115
+
116
+ ### Direct construction
117
+
118
+ \`\`\`ts
119
+ const conRegistry = connection()
120
+ conRegistry.create('mysql_prod', 'mysql').key({
121
+ host: '127.0.0.1',
122
+ port: 3306,
123
+ user: 'root',
124
+ password: 's3cr3t',
125
+ database: 'appdb',
126
+ })
127
+ \`\`\`
128
+
129
+ ---
130
+
131
+ ## Schemas and Models
132
+
133
+ Schemas define structure for collections/tables bound to a connection.
134
+
135
+ ### Define and register a schema
136
+
137
+ \`\`\`ts
138
+ import { schema } from '@neupgroup/mapper'
139
+
140
+ const sm = schema(conRegistry)
141
+
142
+ sm.create('User')
143
+ .use({ connection: 'mysql_prod', collection: 'users' })
144
+ .setStructure({
145
+ id: 'string primary',
146
+ email: 'string unique',
147
+ name: 'string editable',
148
+ createdAt: 'date',
149
+ '?field': 'allow-undefined', // optional: permit fields not listed
150
+ })
151
+
152
+ const User = sm.use('User')
153
+ \`\`\`
154
+
155
+ ---
156
+
157
+ ## CRUD Operations
158
+
159
+ All operations return Promises and may throw on errors.
160
+
161
+ ### Insert
162
+
163
+ \`\`\`ts
164
+ const createdId = await User.add({
165
+ id: 'u_123',
166
+ email: 'alice@example.com',
167
+ name: 'Alice',
168
+ createdAt: new Date(),
169
+ })
170
+ \`\`\`
171
+
172
+ ### Update
173
+
174
+ \`\`\`ts
175
+ await User.where(['id', 'u_123']).to({ name: 'Alice Cooper' }).updateOne()
176
+ \`\`\`
177
+
178
+ ### Delete
179
+
180
+ \`\`\`ts
181
+ await User.where(['id', 'u_123']).deleteOne()
182
+ \`\`\`
183
+
184
+ ### Fetch / Query
185
+
186
+ \`\`\`ts
187
+ const one = await User.where(['id', 'u_123']).getOne()
188
+ const many = await User.where('email', '%@example.com', 'like').get()
189
+ \`\`\`
190
+
191
+ ---
192
+
193
+ ## Schema Configuration Details
194
+
195
+ - \`primary\`: marks primary key; used for updates/deletes.
196
+ - \`unique\`: enforces uniqueness in supported backends.
197
+ - \`editable\`: indicates fields commonly modified via UI.
198
+ - \`type\`: one of \`string\`, \`number\`, \`boolean\`, \`date\`, \`int\`.
199
+ - \`?field\`: enables accepting fields not defined in the schema.
200
+
201
+ ---
202
+
203
+ ## Error Handling
204
+
205
+ Wrap operations in \`try/catch\` and inspect known error shapes.
206
+
207
+ \`\`\`ts
208
+ try {
209
+ const user = await User.where(['id', 'u_404']).getOne()
210
+ if (!user) {
211
+ // handle not found gracefully
212
+ }
213
+ } catch (err) {
214
+ if (err && typeof err === 'object' && 'code' in err) {
215
+ console.error('Database error code:', (err as any).code)
216
+ }
217
+ console.error('Unexpected error', err)
218
+ }
219
+ \`\`\`
220
+
221
+ Recommendations:
222
+ - Validate required creds before initializing connections.
223
+ - Prefer parameterized queries or ORM filters over string concatenation.
224
+ - Log request IDs and timestamps for audit trails.
225
+
226
+ ---
227
+
228
+ ## Troubleshooting
229
+
230
+ - \"Connection refused\": check host/port/firewall and credentials.
231
+ - \"Authentication failed\": verify tokens/passwords and token scopes.
232
+ - \"Timeout\": review network paths and optimize query.
233
+ - \"Schema mismatch\": ensure field names/types match the backend.
234
+
235
+ ---
236
+
237
+ ## API Quick Reference
238
+
239
+ - \`parseConnectionsDsl(text)\` → Map of connectionName → key/value creds
240
+ - \`toNormalizedConnections(map)\` → Array of { name, type, key }
241
+ - \`connection()\` → Connection registry (create/register/list/get)
242
+ - \`schema(connections?)\` → Schema manager; define, register, and use schemas
243
+
244
+ ---
245
+
246
+ ## End-to-End Example
247
+
248
+ \`\`\`ts
249
+ import { parseConnectionsDsl, toNormalizedConnections, connection, schema } from '@neupgroup/mapper'
250
+
251
+ const text = await fs.promises.readFile('connections.dsl', 'utf8')
252
+ const map = parseConnectionsDsl(text)
253
+ const conns = toNormalizedConnections(map)
254
+
255
+ const conRegistry = connection()
256
+ for (const c of conns) conRegistry.register({ name: c.name, type: c.type, key: c.key })
257
+
258
+ const sm = schema(conRegistry)
259
+ sm.create('Product')
260
+ .use({ connection: conns[0].name, collection: 'products' })
261
+ .setStructure({
262
+ id: 'string primary',
263
+ title: 'string',
264
+ price: 'number',
265
+ tags: 'string',
266
+ })
267
+ const Product = sm.use('Product')
268
+
269
+ await Product.add({ id: 'p_1', title: 'Widget', price: 9.99, tags: 'sale' })
270
+ await Product.where(['id', 'p_1']).to({ price: 7.99 }).updateOne()
271
+ const items = await Product.where('price', 10, '<').get()
272
+ await Product.where(['id', 'p_1']).deleteOne()
273
+ \`\`\`
274
+ `;
275
+ // Minimal Markdown → HTML converter (headings, lists, code fences, inline code, paragraphs)
276
+ export function markdownToHtml(md) {
277
+ const lines = md.split(/\r?\n/);
278
+ let html = [];
279
+ let inCode = false;
280
+ let codeBuf = [];
281
+ function escapeHtml(s) {
282
+ return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
283
+ }
284
+ function inline(s) {
285
+ s = s.replace(/`([^`]+)`/g, (_m, g1) => `<code>${escapeHtml(g1)}</code>`);
286
+ s = s.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
287
+ s = s.replace(/\*([^*]+)\*/g, '<em>$1</em>');
288
+ s = s.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2">$1</a>');
289
+ return s;
290
+ }
291
+ function flushParagraph(buf) {
292
+ const text = buf.join(' ').trim();
293
+ if (!text)
294
+ return;
295
+ html.push(`<p>${inline(escapeHtml(text))}</p>`);
296
+ buf.length = 0;
297
+ }
298
+ let paraBuf = [];
299
+ for (let i = 0; i < lines.length; i++) {
300
+ const line = lines[i];
301
+ if (line.trim().startsWith('```')) {
302
+ if (inCode) {
303
+ html.push(`<pre><code>${escapeHtml(codeBuf.join('\n'))}</code></pre>`);
304
+ codeBuf = [];
305
+ inCode = false;
306
+ }
307
+ else {
308
+ flushParagraph(paraBuf);
309
+ inCode = true;
310
+ }
311
+ continue;
312
+ }
313
+ if (inCode) {
314
+ codeBuf.push(line);
315
+ continue;
316
+ }
317
+ const hMatch = /^(#{1,6})\s+(.*)$/.exec(line);
318
+ if (hMatch) {
319
+ flushParagraph(paraBuf);
320
+ const level = hMatch[1].length;
321
+ const content = hMatch[2];
322
+ html.push(`<h${level}>${inline(escapeHtml(content))}</h${level}>`);
323
+ continue;
324
+ }
325
+ if (/^\s*[-*]\s+/.test(line)) {
326
+ flushParagraph(paraBuf);
327
+ let ulItems = [];
328
+ ulItems.push(line.replace(/^\s*[-*]\s+/, ''));
329
+ while (i + 1 < lines.length && /^\s*[-*]\s+/.test(lines[i + 1])) {
330
+ i++;
331
+ ulItems.push(lines[i].replace(/^\s*[-*]\s+/, ''));
332
+ }
333
+ const lis = ulItems.map(item => `<li>${inline(escapeHtml(item))}</li>`).join('');
334
+ html.push(`<ul>${lis}</ul>`);
335
+ continue;
336
+ }
337
+ if (/^\s*$/.test(line)) {
338
+ flushParagraph(paraBuf);
339
+ continue;
340
+ }
341
+ paraBuf.push(line);
342
+ }
343
+ if (inCode) {
344
+ html.push(`<pre><code>${codeBuf.map(s => s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')).join('\n')}</code></pre>`);
345
+ }
346
+ flushParagraph(paraBuf);
347
+ return html.join('\n');
348
+ }
349
+ export function getDocumentationHtml() {
350
+ return markdownToHtml(documentationMd);
351
+ }
package/dist/env.d.ts ADDED
@@ -0,0 +1,25 @@
1
+ export type EnvDslConnections = Record<string, Record<string, string>>;
2
+ /**
3
+ * Parse a simple DSL of the form:
4
+ * connections = [
5
+ * connectionName = [
6
+ * key: value,
7
+ * key2: "value2",
8
+ * ],
9
+ * other = [
10
+ * type: SQL,
11
+ * host: localhost,
12
+ * ]
13
+ * ]
14
+ */
15
+ export declare function parseConnectionsDsl(text: string): EnvDslConnections;
16
+ export type NormalizedConnection = {
17
+ name: string;
18
+ type: 'mysql' | 'sql' | 'firestore' | 'mongodb' | 'api';
19
+ key: Record<string, any>;
20
+ };
21
+ /**
22
+ * Convert DSL map to normalized connections compatible with library Connections.
23
+ * If no explicit type is provided, defaults to 'api'.
24
+ */
25
+ export declare function toNormalizedConnections(map: EnvDslConnections): NormalizedConnection[];