mythik-server 0.1.1 → 0.1.3
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 +164 -28
- package/dist/audit.js +1 -1
- package/dist/audit.js.map +1 -1
- package/dist/auth/db-auth-provider.d.ts +2 -2
- package/dist/auth/db-auth-provider.d.ts.map +1 -1
- package/dist/auth/db-auth-provider.js +17 -15
- package/dist/auth/db-auth-provider.js.map +1 -1
- package/dist/auth/scope-filter.d.ts +9 -1
- package/dist/auth/scope-filter.d.ts.map +1 -1
- package/dist/auth/scope-filter.js +27 -5
- package/dist/auth/scope-filter.js.map +1 -1
- package/dist/catalog-builder.d.ts +2 -1
- package/dist/catalog-builder.d.ts.map +1 -1
- package/dist/catalog-builder.js +24 -13
- package/dist/catalog-builder.js.map +1 -1
- package/dist/connection.d.ts +4 -2
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +74 -7
- package/dist/connection.js.map +1 -1
- package/dist/crud-builder.d.ts +8 -3
- package/dist/crud-builder.d.ts.map +1 -1
- package/dist/crud-builder.js +40 -16
- package/dist/crud-builder.js.map +1 -1
- package/dist/middleware/error-handler.js +1 -1
- package/dist/middleware/error-handler.js.map +1 -1
- package/dist/query-engine.d.ts +22 -5
- package/dist/query-engine.d.ts.map +1 -1
- package/dist/query-engine.js +47 -18
- package/dist/query-engine.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +114 -159
- package/dist/server.js.map +1 -1
- package/dist/spec-serving.d.ts +7 -3
- package/dist/spec-serving.d.ts.map +1 -1
- package/dist/spec-serving.js +36 -12
- package/dist/spec-serving.js.map +1 -1
- package/dist/types.d.ts +34 -11
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -3
package/README.md
CHANGED
|
@@ -1,6 +1,28 @@
|
|
|
1
1
|
# mythik-server
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The backend counterpart of `mythik-react`. Turns a Mythik `ApiSpec`
|
|
4
|
+
— a JSON document describing endpoints, auth, row-level security,
|
|
5
|
+
catalogs, and CRUD shape — into a running REST server. Same spec
|
|
6
|
+
model the frontend uses, same versioning, same atomic promote, just
|
|
7
|
+
covering the API contract instead of the UI.
|
|
8
|
+
|
|
9
|
+
> See [the framework README on GitHub](https://github.com/mldixdev/mythik#readme)
|
|
10
|
+
> for the full Mythik architecture and design philosophy. This file
|
|
11
|
+
> documents what `mythik-server` gives you and how to use it.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## What Mythik is, briefly
|
|
16
|
+
|
|
17
|
+
Mythik is an **AI-first, spec-driven framework**. Every UI screen
|
|
18
|
+
and API endpoint lives as a JSON spec in a database — not a `.tsx`
|
|
19
|
+
file or a hand-coded route handler in a repo. Apps grow by adding
|
|
20
|
+
rows, not files. The framework validates every change atomically
|
|
21
|
+
and versions everything automatically, so your favorite AI agent
|
|
22
|
+
can author and patch specs through the CLI without human supervision
|
|
23
|
+
in the loop. This package is the server runtime — it consumes
|
|
24
|
+
ApiSpecs and serves them as REST endpoints with auth, RLS, and CRUD
|
|
25
|
+
wired in.
|
|
4
26
|
|
|
5
27
|
## Install
|
|
6
28
|
|
|
@@ -8,44 +30,158 @@ Declarative REST server for Mythik ApiSpecs.
|
|
|
8
30
|
npm install mythik-server
|
|
9
31
|
```
|
|
10
32
|
|
|
11
|
-
|
|
33
|
+
`mythik` is included as a dependency — no separate install required.
|
|
34
|
+
|
|
35
|
+
## When to install this
|
|
36
|
+
|
|
37
|
+
Install `mythik-server` when you're building the **backend** of a
|
|
38
|
+
Mythik app: the REST API the frontend consumes.
|
|
39
|
+
|
|
40
|
+
For pure frontend work you don't need this package — but you DO need
|
|
41
|
+
it if you want the API contract to be a versioned, validated,
|
|
42
|
+
promotable spec instead of hand-written routes.
|
|
43
|
+
|
|
44
|
+
The frontend (built with `mythik-react`) calls endpoints declared in
|
|
45
|
+
your ApiSpec — typically via `fetch` dataSources or action chains in
|
|
46
|
+
screen specs. `mythik-cli` cross-validates frontend specs against the
|
|
47
|
+
ApiSpec via `mythik contract`, with four rules that go deeper than
|
|
48
|
+
just "does the endpoint exist?" — see the cross-validation section
|
|
49
|
+
below for what it actually checks.
|
|
12
50
|
|
|
13
|
-
|
|
14
|
-
- Query, handler, CRUD, and public endpoint patterns.
|
|
15
|
-
- JWT authentication helpers.
|
|
16
|
-
- Role policy evaluation.
|
|
17
|
-
- Row-level security helpers.
|
|
18
|
-
- Catalog endpoints.
|
|
19
|
-
- SQL identifier validation and ApiSpec validation.
|
|
20
|
-
- DB-backed auth provider helpers.
|
|
51
|
+
## What you get
|
|
21
52
|
|
|
22
|
-
|
|
53
|
+
- **`createServer({ spec, database })`** — main bootstrap. Reads an
|
|
54
|
+
ApiSpec, opens the database connection, registers all declared
|
|
55
|
+
endpoints, and exposes a `start(port)` method.
|
|
56
|
+
|
|
57
|
+
- **Dialect-aware SQL runtime** — generated CRUD, catalogs,
|
|
58
|
+
pagination, auth provider queries, and scope filters compile through
|
|
59
|
+
the selected database driver. Supported dialects are SQL Server,
|
|
60
|
+
PostgreSQL, MySQL, and SQLite.
|
|
61
|
+
|
|
62
|
+
- **Endpoint patterns** — your ApiSpec declares each endpoint as one
|
|
63
|
+
of four shapes; the server wires the route, validates inputs,
|
|
64
|
+
enforces auth, and applies RLS automatically:
|
|
65
|
+
- `query` — read with parameters
|
|
66
|
+
- `handler` — custom business logic with an escape hatch
|
|
67
|
+
- `crud` — create / read / update / delete a table row
|
|
68
|
+
- `public` — unauthenticated endpoint
|
|
69
|
+
|
|
70
|
+
- **Auth and security** — JWT issuance and validation, DB-backed auth
|
|
71
|
+
provider, role policy evaluation, row-level security expressions.
|
|
72
|
+
All declared in the ApiSpec; enforced uniformly by the server.
|
|
73
|
+
|
|
74
|
+
- **Catalogs** — auto-generated lookup endpoints (for dropdown options,
|
|
75
|
+
enums, reference data) declared once in the ApiSpec.
|
|
76
|
+
|
|
77
|
+
- **Validation** — `validateApiSpec()` runs the same atomic checks as
|
|
78
|
+
the CLI's `mythik validate` and `mythik patch`. The server refuses
|
|
79
|
+
to start if the ApiSpec is invalid; no half-broken backend ever
|
|
80
|
+
comes up.
|
|
81
|
+
|
|
82
|
+
## Minimal example
|
|
23
83
|
|
|
24
84
|
```ts
|
|
25
85
|
import { createServer } from 'mythik-server';
|
|
26
86
|
|
|
27
|
-
const server = createServer({
|
|
28
|
-
spec: './api-spec.json',
|
|
29
|
-
database: {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
87
|
+
const server = createServer({
|
|
88
|
+
spec: './api-spec.json',
|
|
89
|
+
database: {
|
|
90
|
+
type: 'postgres',
|
|
91
|
+
connectionString: process.env.DATABASE_URL!,
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
await server.start(3010);
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
`./api-spec.json` is a Mythik ApiSpec describing endpoints, auth
|
|
99
|
+
policy, catalogs, and CRUD shape. See `ai-context.md` (bundled in the
|
|
100
|
+
`mythik` package) for the ApiSpec schema and patterns.
|
|
101
|
+
|
|
102
|
+
For local demos and tests, SQLite is a one-file option:
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
const server = createServer({
|
|
106
|
+
spec: './api-spec.json',
|
|
107
|
+
database: { type: 'sqlite', filename: './mythik.db' },
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Set `spec.dialect` in the ApiSpec to match the server database when
|
|
112
|
+
Mythik should generate CRUD/catalog/pagination/scope SQL for that
|
|
113
|
+
dialect. Custom SQL remains dialect-native; Mythik compiles named
|
|
114
|
+
params (`@name`) but does not translate a SQL Server query into
|
|
115
|
+
PostgreSQL/MySQL/SQLite SQL at runtime. MySQL generated upsert SQL
|
|
116
|
+
targets MySQL 8.0.19+.
|
|
117
|
+
|
|
118
|
+
## ApiSpec philosophy
|
|
119
|
+
|
|
120
|
+
The ApiSpec describes **what** the API does — not **how**. Routes,
|
|
121
|
+
parameters, auth policy, RLS predicates, catalogs, CRUD shape: all
|
|
122
|
+
declared. The implementation details — database connection,
|
|
123
|
+
environment secrets, custom business logic for `handler` endpoints —
|
|
124
|
+
live in host configuration and code, not in the JSON spec.
|
|
125
|
+
|
|
126
|
+
This separation is load-bearing for the framework's AI-first axiom:
|
|
127
|
+
|
|
128
|
+
- The ApiSpec is **portable** across environments (`dev`, `staging`,
|
|
129
|
+
`production`) — the same spec serves all of them; only the
|
|
130
|
+
database connection differs.
|
|
131
|
+
- The ApiSpec is **versioned** — every change is a row in the spec
|
|
132
|
+
store, with author and timestamp.
|
|
133
|
+
- The ApiSpec is **promotable** — `mythik promote --from dev --to
|
|
134
|
+
production` moves the API contract atomically alongside the UI
|
|
135
|
+
contract, so frontend and backend stay in lockstep.
|
|
136
|
+
- The ApiSpec is **patchable** — an AI agent can edit endpoints with
|
|
137
|
+
the same RFC 6902 patches it uses for UI screens.
|
|
138
|
+
|
|
139
|
+
If a change requires touching host configuration or custom code (DB
|
|
140
|
+
migration, new third-party SDK), that's a release that goes through
|
|
141
|
+
the standard build pipeline. Most product evolution stays in the spec
|
|
142
|
+
store.
|
|
143
|
+
|
|
144
|
+
## Cross-validating frontend and backend
|
|
145
|
+
|
|
146
|
+
`mythik contract` runs 4 rules across screen specs and the ApiSpec at
|
|
147
|
+
author time, before deploy:
|
|
148
|
+
|
|
149
|
+
| Rule | Level | What it checks |
|
|
150
|
+
|---|---|---|
|
|
151
|
+
| `endpoints-exist` | error | Every fetch URL in screens matches an endpoint, catalog, or builtin in the ApiSpec |
|
|
152
|
+
| `fields-valid` | error | POST/PUT body fields match the CRUD `insertable` / `updatable` lists |
|
|
153
|
+
| `params-match` | warning | Query parameters match the endpoint's `params` config |
|
|
154
|
+
| `permissions-consistent` | warning | If the AppSpec's `roleAccess` grants a role access to a screen, every endpoint that screen uses must allow that role via `policies` |
|
|
155
|
+
|
|
156
|
+
All errors include Levenshtein-based "did you mean?" suggestions for
|
|
157
|
+
typos and field-name drift.
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
$ npx mythik contract --app app-demo --api api-demo
|
|
161
|
+
$ npx mythik contract --app app-demo --api api-demo --json # CI-friendly
|
|
39
162
|
```
|
|
40
163
|
|
|
41
|
-
|
|
164
|
+
Run it in CI. Run it after an AI agent patches a spec.
|
|
42
165
|
|
|
43
|
-
The
|
|
166
|
+
The fourth rule — **permissions-consistent** — is the distinctive
|
|
167
|
+
one. It cross-checks "Role X can navigate to screen Y" against "every
|
|
168
|
+
endpoint screen Y calls allows role X." When UI visibility and API
|
|
169
|
+
permissions live in separate code layers, this kind of drift is hard
|
|
170
|
+
to detect; in Mythik both `roleAccess` (frontend) and `policies`
|
|
171
|
+
(backend) are declarative JSON, so a single command can compare them.
|
|
44
172
|
|
|
45
|
-
##
|
|
173
|
+
## Related packages
|
|
46
174
|
|
|
47
|
-
|
|
175
|
+
- [`mythik`](https://github.com/mldixdev/mythik/tree/main/packages/core#readme) — the runtime this server uses (dependency)
|
|
176
|
+
- [`mythik-react`](https://github.com/mldixdev/mythik/tree/main/packages/react#readme) — the frontend consuming this API
|
|
177
|
+
- [`mythik-cli`](https://github.com/mldixdev/mythik/tree/main/packages/cli#readme) — author and cross-validate ApiSpecs (the AI-first surface)
|
|
48
178
|
|
|
49
179
|
## Status
|
|
50
180
|
|
|
51
|
-
|
|
181
|
+
Public release line. Use with `mythik contract` to cross-check
|
|
182
|
+
frontend specs against backend endpoints before deployment. APIs are
|
|
183
|
+
documented for real-world feedback as the framework evolves.
|
|
184
|
+
|
|
185
|
+
## License
|
|
186
|
+
|
|
187
|
+
Apache-2.0.
|
package/dist/audit.js
CHANGED
|
@@ -19,7 +19,7 @@ function getTimestamp(timezone) {
|
|
|
19
19
|
});
|
|
20
20
|
const parts = formatter.formatToParts(new Date());
|
|
21
21
|
const get = (type) => parts.find(p => p.type === type)?.value ?? '0';
|
|
22
|
-
// Use Date.UTC so
|
|
22
|
+
// Use Date.UTC so SQL Server receives the value as-is (no local timezone conversion)
|
|
23
23
|
return new Date(Date.UTC(parseInt(get('year')), parseInt(get('month')) - 1, parseInt(get('day')), parseInt(get('hour')), parseInt(get('minute')), parseInt(get('second'))));
|
|
24
24
|
}
|
|
25
25
|
/**
|
package/dist/audit.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"AAaA;;;;GAIG;AACH,SAAS,YAAY,CAAC,QAAiB;IACrC,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,IAAI,EAAE,CAAC;IAEjC,gEAAgE;IAChE,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE;QACjD,QAAQ,EAAE,QAAQ;QAClB,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,SAAS;QAChB,GAAG,EAAE,SAAS;QACd,IAAI,EAAE,SAAS;QACf,MAAM,EAAE,SAAS;QACjB,MAAM,EAAE,SAAS;QACjB,MAAM,EAAE,KAAK;KACd,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,SAAS,CAAC,aAAa,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;IAClD,MAAM,GAAG,GAAG,CAAC,IAAkC,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,KAAK,IAAI,GAAG,CAAC;IAEnG,
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"AAaA;;;;GAIG;AACH,SAAS,YAAY,CAAC,QAAiB;IACrC,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,IAAI,EAAE,CAAC;IAEjC,gEAAgE;IAChE,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE;QACjD,QAAQ,EAAE,QAAQ;QAClB,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,SAAS;QAChB,GAAG,EAAE,SAAS;QACd,IAAI,EAAE,SAAS;QACf,MAAM,EAAE,SAAS;QACjB,MAAM,EAAE,SAAS;QACjB,MAAM,EAAE,KAAK;KACd,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,SAAS,CAAC,aAAa,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;IAClD,MAAM,GAAG,GAAG,CAAC,IAAkC,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,KAAK,IAAI,GAAG,CAAC;IAEnG,qFAAqF;IACrF,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CACtB,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EACrB,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,EAC1B,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EACpB,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EACrB,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,EACvB,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CACxB,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAC/B,MAA+B,EAC/B,KAAkB,EAClB,QAAuB,EACvB,SAA8B;IAE9B,MAAM,GAAG,GAAG,YAAY,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAEzC,IAAI,SAAS,KAAK,QAAQ,EAAE,CAAC;QAC3B,IAAI,KAAK,CAAC,SAAS,IAAI,QAAQ;YAAE,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,QAAQ,CAAC;QACpE,IAAI,KAAK,CAAC,SAAS;YAAE,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,GAAG,CAAC;IACrD,CAAC;IAED,IAAI,KAAK,CAAC,SAAS,IAAI,QAAQ;QAAE,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,QAAQ,CAAC;IACpE,IAAI,KAAK,CAAC,SAAS;QAAE,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,GAAG,CAAC;AACrD,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { SqlDriver } from 'mythik/server';
|
|
2
2
|
import type { ProviderConfig, JwtConfig, LoginResponse } from './types.js';
|
|
3
3
|
import { type RefreshStore } from './refresh-store.js';
|
|
4
4
|
export interface DbAuthProvider {
|
|
@@ -6,5 +6,5 @@ export interface DbAuthProvider {
|
|
|
6
6
|
refresh(refreshToken: string): Promise<LoginResponse>;
|
|
7
7
|
getRefreshStore(): RefreshStore;
|
|
8
8
|
}
|
|
9
|
-
export declare function createDbAuthProvider(config: ProviderConfig, jwtConfig: JwtConfig,
|
|
9
|
+
export declare function createDbAuthProvider(config: ProviderConfig, jwtConfig: JwtConfig, db: SqlDriver): DbAuthProvider;
|
|
10
10
|
//# sourceMappingURL=db-auth-provider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db-auth-provider.d.ts","sourceRoot":"","sources":["../../src/auth/db-auth-provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"db-auth-provider.d.ts","sourceRoot":"","sources":["../../src/auth/db-auth-provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,aAAa,EAAqB,MAAM,YAAY,CAAC;AAG9F,OAAO,EAAsB,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAG3E,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IAClE,OAAO,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IACtD,eAAe,IAAI,YAAY,CAAC;CACjC;AAYD,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,cAAc,EACtB,SAAS,EAAE,SAAS,EACpB,EAAE,EAAE,SAAS,GACZ,cAAc,CA6HhB"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createJwtStrategy } from './jwt-strategy.js';
|
|
2
2
|
import { getPasswordVerifier } from './password-verifier.js';
|
|
3
3
|
import { createRefreshStore } from './refresh-store.js';
|
|
4
|
+
import { assertValidIdentifier } from '../validation/identifier-guard.js';
|
|
4
5
|
/** Case-insensitive field lookup — SQL Server column casing varies by driver/config */
|
|
5
6
|
function getField(row, fieldName) {
|
|
6
7
|
if (fieldName in row)
|
|
@@ -12,38 +13,34 @@ function getField(row, fieldName) {
|
|
|
12
13
|
}
|
|
13
14
|
return undefined;
|
|
14
15
|
}
|
|
15
|
-
export function createDbAuthProvider(config, jwtConfig,
|
|
16
|
+
export function createDbAuthProvider(config, jwtConfig, db) {
|
|
16
17
|
const jwtStrategy = createJwtStrategy(jwtConfig);
|
|
17
18
|
const passwordVerifier = getPasswordVerifier(config.passwordHash);
|
|
18
19
|
const refreshStore = createRefreshStore(jwtConfig.refreshExpiresIn ?? 10080);
|
|
19
20
|
const expiresInSeconds = (jwtConfig.expiresIn ?? 60) * 60;
|
|
20
21
|
const claimsMapping = jwtConfig.claims ?? {};
|
|
21
22
|
async function queryList(sql, username) {
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
const result = await req.query(sql);
|
|
25
|
-
return result.recordset.map((r) => {
|
|
23
|
+
const rows = await db.query(sql, { username });
|
|
24
|
+
return rows.map((r) => {
|
|
26
25
|
const keys = Object.keys(r);
|
|
27
26
|
return r.val ?? r[keys[0]];
|
|
28
27
|
});
|
|
29
28
|
}
|
|
30
29
|
async function queryScalar(sql, username) {
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
const result = await req.query(sql);
|
|
34
|
-
if (!result.recordset[0])
|
|
30
|
+
const rows = await db.query(sql, { username });
|
|
31
|
+
if (!rows[0])
|
|
35
32
|
return null;
|
|
36
|
-
const row =
|
|
33
|
+
const row = rows[0];
|
|
37
34
|
const keys = Object.keys(row);
|
|
38
35
|
return row.val ?? row[keys[0]] ?? null;
|
|
39
36
|
}
|
|
40
37
|
async function lookupUser(username) {
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
assertValidIdentifier(config.usersTable, 'auth.provider.usersTable');
|
|
39
|
+
assertValidIdentifier(config.usernameColumn, 'auth.provider.usernameColumn');
|
|
43
40
|
const condition = config.activeCondition ? ` AND ${config.activeCondition}` : '';
|
|
44
|
-
const sql = `SELECT * FROM ${config.usersTable} WHERE ${config.usernameColumn} = @username${condition}`;
|
|
45
|
-
const
|
|
46
|
-
return
|
|
41
|
+
const sql = `SELECT * FROM ${quoteIdentifierPath(db, config.usersTable)} WHERE ${quoteIdentifierPath(db, config.usernameColumn)} = @username${condition}`;
|
|
42
|
+
const rows = await db.query(sql, { username });
|
|
43
|
+
return rows[0] ?? null;
|
|
47
44
|
}
|
|
48
45
|
async function buildUserInfo(username, userRow) {
|
|
49
46
|
const promises = [
|
|
@@ -127,4 +124,9 @@ export function createDbAuthProvider(config, jwtConfig, pool) {
|
|
|
127
124
|
}
|
|
128
125
|
return { login, refresh, getRefreshStore };
|
|
129
126
|
}
|
|
127
|
+
function quoteIdentifierPath(driver, identifier) {
|
|
128
|
+
return identifier.includes('.')
|
|
129
|
+
? driver.quoteQualified(...identifier.split('.'))
|
|
130
|
+
: driver.quoteIdent(identifier);
|
|
131
|
+
}
|
|
130
132
|
//# sourceMappingURL=db-auth-provider.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db-auth-provider.js","sourceRoot":"","sources":["../../src/auth/db-auth-provider.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAqB,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"db-auth-provider.js","sourceRoot":"","sources":["../../src/auth/db-auth-provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import type { ScopeFilterConfig } from './types.js';
|
|
2
|
+
import type { SqlDriver } from 'mythik/server';
|
|
3
|
+
/** @internal Alias used when Mythik wraps a source query before applying scope filters. */
|
|
4
|
+
export declare const SCOPE_ALIAS = "_scoped";
|
|
2
5
|
export interface ScopeClause {
|
|
3
6
|
sql: string;
|
|
4
7
|
params: Record<string, unknown>;
|
|
5
8
|
}
|
|
9
|
+
export interface ScopeWhereOptions {
|
|
10
|
+
columnOverride?: string;
|
|
11
|
+
driver?: SqlDriver;
|
|
12
|
+
qualifier?: string | null;
|
|
13
|
+
}
|
|
6
14
|
export declare function resolveActiveScope(headerValue: string | undefined, type: 'int' | 'string' | undefined): unknown | null;
|
|
7
|
-
export declare function buildScopeWhereClause(config: ScopeFilterConfig, userScope: unknown[], activeScope: unknown | undefined, userRoles: string[],
|
|
15
|
+
export declare function buildScopeWhereClause(config: ScopeFilterConfig, userScope: unknown[], activeScope: unknown | undefined, userRoles: string[], columnOverrideOrOptions?: string | ScopeWhereOptions): ScopeClause | null;
|
|
8
16
|
export declare function validateScopeForInsert(config: ScopeFilterConfig, body: Record<string, unknown>, userScope: unknown[], userRoles: string[]): boolean;
|
|
9
17
|
export declare function wrapQueryWithScopeFilter(originalSql: string, scopeClause: ScopeClause): string;
|
|
10
18
|
//# sourceMappingURL=scope-filter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scope-filter.d.ts","sourceRoot":"","sources":["../../src/auth/scope-filter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"scope-filter.d.ts","sourceRoot":"","sources":["../../src/auth/scope-filter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAG/C,2FAA2F;AAC3F,eAAO,MAAM,WAAW,YAAY,CAAC;AAErC,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED,MAAM,WAAW,iBAAiB;IAChC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,IAAI,EAAE,KAAK,GAAG,QAAQ,GAAG,SAAS,GACjC,OAAO,GAAG,IAAI,CAKhB;AAMD,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,iBAAiB,EACzB,SAAS,EAAE,OAAO,EAAE,EACpB,WAAW,EAAE,OAAO,GAAG,SAAS,EAChC,SAAS,EAAE,MAAM,EAAE,EACnB,uBAAuB,CAAC,EAAE,MAAM,GAAG,iBAAiB,GACnD,WAAW,GAAG,IAAI,CA8BpB;AAED,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,iBAAiB,EACzB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,SAAS,EAAE,OAAO,EAAE,EACpB,SAAS,EAAE,MAAM,EAAE,GAClB,OAAO,CAST;AAED,wBAAgB,wBAAwB,CACtC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,WAAW,GACvB,MAAM,CAER"}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { assertValidIdentifier } from 'mythik';
|
|
2
|
+
/** @internal Alias used when Mythik wraps a source query before applying scope filters. */
|
|
3
|
+
export const SCOPE_ALIAS = '_scoped';
|
|
2
4
|
export function resolveActiveScope(headerValue, type) {
|
|
3
5
|
if (headerValue === undefined || headerValue === '')
|
|
4
6
|
return null;
|
|
@@ -10,16 +12,18 @@ export function resolveActiveScope(headerValue, type) {
|
|
|
10
12
|
function hasBypassRole(bypassRoles, userRoles) {
|
|
11
13
|
return bypassRoles.some(role => userRoles.includes(role));
|
|
12
14
|
}
|
|
13
|
-
export function buildScopeWhereClause(config, userScope, activeScope, userRoles,
|
|
15
|
+
export function buildScopeWhereClause(config, userScope, activeScope, userRoles, columnOverrideOrOptions) {
|
|
14
16
|
const bypassRoles = config.bypassRoles ?? [];
|
|
15
17
|
if (hasBypassRole(bypassRoles, userRoles))
|
|
16
18
|
return null;
|
|
17
|
-
const
|
|
19
|
+
const options = normalizeScopeWhereOptions(columnOverrideOrOptions);
|
|
20
|
+
const column = options.columnOverride ?? config.column;
|
|
18
21
|
assertValidIdentifier(column, 'scopeFilter.column');
|
|
19
22
|
const mode = config.mode ?? 'all';
|
|
23
|
+
const columnSql = scopeColumnSql(column, options);
|
|
20
24
|
if (mode === 'select') {
|
|
21
25
|
return {
|
|
22
|
-
sql:
|
|
26
|
+
sql: `${columnSql} = @_activeScope`,
|
|
23
27
|
params: { _activeScope: activeScope },
|
|
24
28
|
};
|
|
25
29
|
}
|
|
@@ -31,7 +35,7 @@ export function buildScopeWhereClause(config, userScope, activeScope, userRoles,
|
|
|
31
35
|
const params = {};
|
|
32
36
|
userScope.forEach((val, i) => { params[`_scope${i}`] = val; });
|
|
33
37
|
return {
|
|
34
|
-
sql:
|
|
38
|
+
sql: `${columnSql} IN (${paramNames.join(', ')})`,
|
|
35
39
|
params,
|
|
36
40
|
};
|
|
37
41
|
}
|
|
@@ -46,6 +50,24 @@ export function validateScopeForInsert(config, body, userScope, userRoles) {
|
|
|
46
50
|
return userScope.includes(bodyValue);
|
|
47
51
|
}
|
|
48
52
|
export function wrapQueryWithScopeFilter(originalSql, scopeClause) {
|
|
49
|
-
return `SELECT * FROM (\n${originalSql}\n) AS
|
|
53
|
+
return `SELECT * FROM (\n${originalSql}\n) AS ${SCOPE_ALIAS}\nWHERE ${scopeClause.sql}`;
|
|
54
|
+
}
|
|
55
|
+
function normalizeScopeWhereOptions(value) {
|
|
56
|
+
if (typeof value === 'string')
|
|
57
|
+
return { columnOverride: value };
|
|
58
|
+
return value ?? {};
|
|
59
|
+
}
|
|
60
|
+
function scopeColumnSql(column, options) {
|
|
61
|
+
const columnSql = options.driver ? quoteIdentifierPath(options.driver, column) : column;
|
|
62
|
+
const qualifier = options.qualifier === undefined ? SCOPE_ALIAS : options.qualifier;
|
|
63
|
+
if (qualifier === null || qualifier === '')
|
|
64
|
+
return columnSql;
|
|
65
|
+
assertValidIdentifier(qualifier, 'scopeFilter.qualifier');
|
|
66
|
+
return `${qualifier}.${columnSql}`;
|
|
67
|
+
}
|
|
68
|
+
function quoteIdentifierPath(driver, identifier) {
|
|
69
|
+
return identifier.includes('.')
|
|
70
|
+
? driver.quoteQualified(...identifier.split('.'))
|
|
71
|
+
: driver.quoteIdent(identifier);
|
|
50
72
|
}
|
|
51
73
|
//# sourceMappingURL=scope-filter.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scope-filter.js","sourceRoot":"","sources":["../../src/auth/scope-filter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"scope-filter.js","sourceRoot":"","sources":["../../src/auth/scope-filter.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import type { CatalogConfig } from './types.js';
|
|
2
|
-
|
|
2
|
+
import type { SqlDriver } from 'mythik/server';
|
|
3
|
+
export declare function buildCatalogQuery(driver: SqlDriver, config: CatalogConfig): string | null;
|
|
3
4
|
//# sourceMappingURL=catalog-builder.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catalog-builder.d.ts","sourceRoot":"","sources":["../src/catalog-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"catalog-builder.d.ts","sourceRoot":"","sources":["../src/catalog-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE/C,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI,CAoCzF"}
|
package/dist/catalog-builder.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { assertValidIdentifier } from './validation/identifier-guard.js';
|
|
2
|
-
export function buildCatalogQuery(config) {
|
|
2
|
+
export function buildCatalogQuery(driver, config) {
|
|
3
3
|
if (config.static)
|
|
4
4
|
return null;
|
|
5
5
|
if (!config.from)
|
|
@@ -8,28 +8,39 @@ export function buildCatalogQuery(config) {
|
|
|
8
8
|
if (config.distinct) {
|
|
9
9
|
assertValidIdentifier(config.distinct, 'catalog.distinct');
|
|
10
10
|
const orderByClause = config.orderBy
|
|
11
|
-
? `ORDER BY ${
|
|
12
|
-
: `ORDER BY
|
|
13
|
-
return `SELECT DISTINCT
|
|
11
|
+
? `ORDER BY ${quoteOrderBy(driver, config.orderBy)}`
|
|
12
|
+
: `ORDER BY ${quoteIdentifierPath(driver, config.distinct)} ASC`;
|
|
13
|
+
return `SELECT DISTINCT ${quoteIdentifierPath(driver, config.distinct)} AS ${driver.quoteIdent('value')}, ${quoteIdentifierPath(driver, config.distinct)} AS ${driver.quoteIdent('label')} FROM ${quoteIdentifierPath(driver, config.from)} ${orderByClause}`;
|
|
14
14
|
}
|
|
15
15
|
assertValidIdentifier(config.value, 'catalog.value');
|
|
16
16
|
assertValidIdentifier(config.label, 'catalog.label');
|
|
17
|
-
const selectParts = [
|
|
17
|
+
const selectParts = [
|
|
18
|
+
`${quoteIdentifierPath(driver, config.value)} AS ${driver.quoteIdent('value')}`,
|
|
19
|
+
`${quoteIdentifierPath(driver, config.label)} AS ${driver.quoteIdent('label')}`,
|
|
20
|
+
];
|
|
18
21
|
if (config.extra) {
|
|
19
22
|
for (const field of config.extra) {
|
|
20
23
|
assertValidIdentifier(field, 'catalog.extra');
|
|
21
|
-
selectParts.push(
|
|
24
|
+
selectParts.push(quoteIdentifierPath(driver, field));
|
|
22
25
|
}
|
|
23
26
|
}
|
|
24
27
|
const whereClause = config.where ? ` WHERE ${config.where}` : '';
|
|
25
28
|
const orderByClause = config.orderBy
|
|
26
|
-
? `ORDER BY ${
|
|
27
|
-
: `ORDER BY
|
|
28
|
-
return `SELECT ${selectParts.join(', ')} FROM
|
|
29
|
+
? `ORDER BY ${quoteOrderBy(driver, config.orderBy)}`
|
|
30
|
+
: `ORDER BY ${quoteIdentifierPath(driver, config.label)} ASC`;
|
|
31
|
+
return `SELECT ${selectParts.join(', ')} FROM ${quoteIdentifierPath(driver, config.from)}${whereClause} ${orderByClause}`;
|
|
29
32
|
}
|
|
30
|
-
function
|
|
31
|
-
return
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
function quoteIdentifierPath(driver, identifier) {
|
|
34
|
+
return identifier.includes('.')
|
|
35
|
+
? driver.quoteQualified(...identifier.split('.'))
|
|
36
|
+
: driver.quoteIdent(identifier);
|
|
37
|
+
}
|
|
38
|
+
function quoteOrderBy(driver, orderBy) {
|
|
39
|
+
const match = /^([a-zA-Z_][a-zA-Z0-9_.]*)(\s+(?:ASC|DESC))?$/i.exec(orderBy.trim());
|
|
40
|
+
if (!match)
|
|
41
|
+
return orderBy;
|
|
42
|
+
const [, column, direction] = match;
|
|
43
|
+
assertValidIdentifier(column, 'catalog.orderBy');
|
|
44
|
+
return `${quoteIdentifierPath(driver, column)}${direction ?? ''}`;
|
|
34
45
|
}
|
|
35
46
|
//# sourceMappingURL=catalog-builder.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catalog-builder.js","sourceRoot":"","sources":["../src/catalog-builder.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;
|
|
1
|
+
{"version":3,"file":"catalog-builder.js","sourceRoot":"","sources":["../src/catalog-builder.ts"],"names":[],"mappings":"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"}
|
package/dist/connection.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { SqlDialect, SqlDriver, SqlDriverConfig } from 'mythik/server';
|
|
2
2
|
import type { ConnectionConfig } from './types.js';
|
|
3
|
-
export declare function
|
|
3
|
+
export declare function resolveDatabaseDialect(config: ConnectionConfig, expectedDialect?: SqlDialect): SqlDialect;
|
|
4
|
+
export declare function toSqlDriverConfig(config: ConnectionConfig, expectedDialect?: SqlDialect): SqlDriverConfig;
|
|
5
|
+
export declare function createDatabaseDriver(config: ConnectionConfig, expectedDialect?: SqlDialect): SqlDriver;
|
|
4
6
|
//# sourceMappingURL=connection.d.ts.map
|
package/dist/connection.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC5E,OAAO,KAAK,EACV,gBAAgB,EAKjB,MAAM,YAAY,CAAC;AAgBpB,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,gBAAgB,EAAE,eAAe,CAAC,EAAE,UAAU,GAAG,UAAU,CAazG;AAsCD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,gBAAgB,EAAE,eAAe,CAAC,EAAE,UAAU,GAAG,eAAe,CA4BzG;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,gBAAgB,EAAE,eAAe,CAAC,EAAE,UAAU,GAAG,SAAS,CAEtG"}
|