@mostajs/orm-adapter 0.2.0 → 0.4.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/CHANGELOG.md +83 -0
- package/README.md +21 -2
- package/dist/adapters/jsonschema.adapter.d.ts +51 -0
- package/dist/adapters/jsonschema.adapter.d.ts.map +1 -0
- package/dist/adapters/jsonschema.adapter.js +336 -0
- package/dist/adapters/jsonschema.adapter.js.map +1 -0
- package/dist/adapters/openapi.adapter.d.ts +61 -0
- package/dist/adapters/openapi.adapter.d.ts.map +1 -0
- package/dist/adapters/openapi.adapter.js +220 -0
- package/dist/adapters/openapi.adapter.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/utils/jsonschema-flatten.d.ts +17 -0
- package/dist/utils/jsonschema-flatten.d.ts.map +1 -0
- package/dist/utils/jsonschema-flatten.js +63 -0
- package/dist/utils/jsonschema-flatten.js.map +1 -0
- package/dist/utils/jsonschema-type-mapper.d.ts +11 -0
- package/dist/utils/jsonschema-type-mapper.d.ts.map +1 -0
- package/dist/utils/jsonschema-type-mapper.js +113 -0
- package/dist/utils/jsonschema-type-mapper.js.map +1 -0
- package/dist/utils/jsonschema-types.d.ts +95 -0
- package/dist/utils/jsonschema-types.d.ts.map +1 -0
- package/dist/utils/jsonschema-types.js +41 -0
- package/dist/utils/jsonschema-types.js.map +1 -0
- package/dist/utils/openapi-normalize.d.ts +24 -0
- package/dist/utils/openapi-normalize.d.ts.map +1 -0
- package/dist/utils/openapi-normalize.js +118 -0
- package/dist/utils/openapi-normalize.js.map +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,89 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@mostajs/orm-adapter` will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.4.0] — 2026-04-12
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **OpenApiAdapter** : converts OpenAPI 2.0/3.0/3.1 specs to `EntitySchema[]`
|
|
10
|
+
- OpenAPI 3.1 : full JSON Schema 2020-12 semantics
|
|
11
|
+
- OpenAPI 3.0.x : auto-normalized to 3.1 shape before conversion
|
|
12
|
+
- `nullable: true` → `type: [T, "null"]`
|
|
13
|
+
- `example: X` → `examples: [X]`
|
|
14
|
+
- `exclusiveMinimum: true` + `minimum: X` → `exclusiveMinimum: X`
|
|
15
|
+
- Swagger 2.0 detected with PREVIEW_FEATURE warning
|
|
16
|
+
- Extracts all `components/schemas` as entities
|
|
17
|
+
- Delegates to JsonSchemaAdapter for the schema conversion pipeline
|
|
18
|
+
- **x-mostajs-relation preservation** : re-attaches extensions on $ref-bearing properties (dereference strips siblings)
|
|
19
|
+
- **Title injection** : auto-adds `title: <key>` to each schema for relation detection
|
|
20
|
+
- **Input forms** : object, JSON string (YAML requires pre-parsing)
|
|
21
|
+
- Uses `@readme/openapi-parser` for validation + dereferencing
|
|
22
|
+
- **Utils** : `openapi-normalize` (3.0 → 3.1 transformations)
|
|
23
|
+
- **JsonSchemaAdapter.schemasToEntities()** : public method to convert a named schema map without root-detection heuristic
|
|
24
|
+
- 50 new unit tests on 3 fixtures :
|
|
25
|
+
- `petstore-3.1.json` (3 entities, relations, discriminator, x-mostajs-*)
|
|
26
|
+
- `petstore-3.0.json` (legacy with all normalizations)
|
|
27
|
+
- `discriminator.json` (oneOf polymorphism)
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
|
|
31
|
+
- `createDefaultRegistry()` now includes OpenApiAdapter (4 adapters total)
|
|
32
|
+
- `JsonSchemaAdapter.toEntitySchema()` : uses `structuredClone` instead of JSON.parse/stringify to handle circular refs from OpenAPI's dereferenced tree
|
|
33
|
+
|
|
34
|
+
### Total test coverage
|
|
35
|
+
|
|
36
|
+
196 tests across 4 adapters :
|
|
37
|
+
- 31 native
|
|
38
|
+
- 55 prisma
|
|
39
|
+
- 60 jsonschema
|
|
40
|
+
- 50 openapi
|
|
41
|
+
|
|
42
|
+
## [0.3.0] — 2026-04-12
|
|
43
|
+
|
|
44
|
+
### Added
|
|
45
|
+
|
|
46
|
+
- **JsonSchemaAdapter** : converts JSON Schema to `EntitySchema[]`
|
|
47
|
+
- Drafts : Draft-07, Draft 2019-09, Draft 2020-12 (auto-detected via `$schema` URL)
|
|
48
|
+
- Types : `string`, `integer`, `number`, `boolean`, `array`, `object`, `null`
|
|
49
|
+
- Formats : `date-time`, `date`, `time`, `email`, `uuid`, `uri`, `uri-reference`, `ipv4`, `ipv6`, `regex`, `binary`, `byte`
|
|
50
|
+
- Nullable : both OpenAPI `nullable: true` and array form `type: [T, "null"]`
|
|
51
|
+
- Constraints : `enum`, `const`, `default`, `minLength`/`maxLength`, `pattern`, `minimum`/`maximum`
|
|
52
|
+
- Annotations : `readOnly`, `writeOnly`, `deprecated`, `title`, `description` (preserved)
|
|
53
|
+
- **allOf flattening** : merges parent properties into child (inheritance pattern)
|
|
54
|
+
- **oneOf discriminator** : maps to `EntitySchema.discriminator` + `discriminatorValue`
|
|
55
|
+
- **Entity detection** : top-level title/x-mostajs-entity + all `$defs` / `definitions`
|
|
56
|
+
- **$ref resolution** : internal + external via `@apidevtools/json-schema-ref-parser`
|
|
57
|
+
- **Cycle detection** : self-references flagged via CYCLIC_REFERENCE warning
|
|
58
|
+
- **Extensions** (all `x-*`) :
|
|
59
|
+
- `x-mostajs-entity` : `{ tableName, timestamps, softDelete, discriminator, discriminatorValue }`
|
|
60
|
+
- `x-mostajs-relation` : `{ type, target, foreignKey, otherKey, through, onDelete }`
|
|
61
|
+
- `x-primary`, `x-unique`, `x-index`, `x-indexes` (composite)
|
|
62
|
+
- `x-autoIncrement`
|
|
63
|
+
- **Auto-relation detection** :
|
|
64
|
+
- Property is object with title matching an entity → `many-to-one`
|
|
65
|
+
- Array items `$ref` to an entity → `one-to-many`
|
|
66
|
+
- Explicit `x-mostajs-relation` always wins
|
|
67
|
+
- **Input forms** : plain object, JSON string, or `$ref`-laden schema (auto-dereferenced)
|
|
68
|
+
- **Utils** : `jsonschema-types` (shared types + draft detection), `jsonschema-type-mapper`, `jsonschema-flatten`
|
|
69
|
+
- **Public types re-exports** : `JsonSchema`, `JsonSchemaType`, `XMostajsEntity`, `XMostajsRelation`, `DraftVersion`
|
|
70
|
+
- 60 new unit tests on 4 fixtures :
|
|
71
|
+
- `user-2020-12.json` (Draft 2020-12 + relations + indexes + x-mostajs-*)
|
|
72
|
+
- `post-draft-07.json` (Draft-07 compat + auto-relation via $ref title)
|
|
73
|
+
- `allof-composition.json` (allOf merge + required merge)
|
|
74
|
+
- `validators.json` (all validators + composite x-indexes)
|
|
75
|
+
|
|
76
|
+
### Changed
|
|
77
|
+
|
|
78
|
+
- `createDefaultRegistry()` now includes JsonSchemaAdapter in the detection chain
|
|
79
|
+
|
|
80
|
+
### Warnings emitted (new)
|
|
81
|
+
|
|
82
|
+
- `PREVIEW_FEATURE` on Draft-04/06 (legacy drafts)
|
|
83
|
+
- `LOSSY_CONVERSION` on `int64` format, tuple `items`, binary/byte format
|
|
84
|
+
- `AMBIGUOUS_MAPPING` on allOf property redefinition (last-wins)
|
|
85
|
+
- `CYCLIC_REFERENCE` on self-referencing entities
|
|
86
|
+
- `FALLBACK_APPLIED` on non-object $defs entries
|
|
87
|
+
|
|
5
88
|
## [0.2.0] — 2026-04-12
|
|
6
89
|
|
|
7
90
|
### Added
|
package/README.md
CHANGED
|
@@ -146,10 +146,29 @@ export class MyCustomAdapter extends AbstractAdapter {
|
|
|
146
146
|
|
|
147
147
|
- **v0.1.0** — NativeAdapter + core (AbstractAdapter, Registry) ✅
|
|
148
148
|
- **v0.2.0** — PrismaAdapter (scalars, relations, enums, implicit M-N) ✅
|
|
149
|
-
- **v0.3.0** — JsonSchemaAdapter (Draft 2020-12 + Draft-07)
|
|
150
|
-
- **v0.4.0** — OpenApiAdapter (3.1
|
|
149
|
+
- **v0.3.0** — JsonSchemaAdapter (Draft 2020-12 + Draft-07 + allOf + x-mostajs-*) ✅
|
|
150
|
+
- **v0.4.0** — OpenApiAdapter (3.1 + 3.0 normalization + Swagger 2.0 detect) ✅
|
|
151
151
|
- **v1.0.0** — Production-ready, all 4 adapters with reverse conversion
|
|
152
152
|
|
|
153
|
+
## OpenApiAdapter example
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
import { OpenApiAdapter } from '@mostajs/orm-adapter';
|
|
157
|
+
import { readFileSync } from 'fs';
|
|
158
|
+
|
|
159
|
+
const spec = JSON.parse(readFileSync('./openapi.json', 'utf8'));
|
|
160
|
+
const adapter = new OpenApiAdapter();
|
|
161
|
+
const entities = await adapter.toEntitySchema(spec);
|
|
162
|
+
|
|
163
|
+
// Extracts every components/schemas entry as an EntitySchema.
|
|
164
|
+
// Auto-handles:
|
|
165
|
+
// - OpenAPI 3.0 → 3.1 normalization (nullable, exclusiveMin, examples)
|
|
166
|
+
// - $ref resolution (internal)
|
|
167
|
+
// - allOf flattening (inheritance)
|
|
168
|
+
// - discriminator → EntitySchema.discriminator
|
|
169
|
+
// - x-mostajs-relation on $ref-bearing properties (preserved across dereference)
|
|
170
|
+
```
|
|
171
|
+
|
|
153
172
|
## PrismaAdapter example
|
|
154
173
|
|
|
155
174
|
```ts
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { EntitySchema } from '@mostajs/orm';
|
|
2
|
+
import { AbstractAdapter } from '../core/abstract.adapter.js';
|
|
3
|
+
import { type AdapterOptions } from '../core/types.js';
|
|
4
|
+
import { type JsonSchema } from '../utils/jsonschema-types.js';
|
|
5
|
+
/**
|
|
6
|
+
* JsonSchemaAdapter — converts JSON Schema to EntitySchema[].
|
|
7
|
+
*
|
|
8
|
+
* Supports (MVP v0.3.0):
|
|
9
|
+
* - Draft-07, Draft 2019-09, Draft 2020-12 (auto-detected)
|
|
10
|
+
* - type: string/integer/number/boolean/array/object/null (including array form)
|
|
11
|
+
* - formats: date-time, date, time, email, uuid, uri, ipv4, regex, binary
|
|
12
|
+
* - Constraints: enum, const, default, minLength/maxLength, pattern, min/max
|
|
13
|
+
* - Nullable (both OpenAPI nullable: true and type: [T, "null"])
|
|
14
|
+
* - readOnly / writeOnly / deprecated (annotations preserved)
|
|
15
|
+
* - allOf flattening (inheritance merge)
|
|
16
|
+
* - oneOf + discriminator → discriminator + discriminatorValue
|
|
17
|
+
* - Entity detection : top-level (title+x-mostajs-entity) + all $defs
|
|
18
|
+
* - Extensions : x-mostajs-entity, x-mostajs-relation, x-primary, x-unique, x-index, x-indexes
|
|
19
|
+
* - $ref resolution (cycles detected, self-relation auto-created)
|
|
20
|
+
*
|
|
21
|
+
* Not yet supported (v0.4+):
|
|
22
|
+
* - $dynamicRef / $dynamicAnchor late-binding
|
|
23
|
+
* - patternProperties as structured mapping (falls back to json)
|
|
24
|
+
* - if/then/else conditional
|
|
25
|
+
* - External $ref resolution (HTTP/file beyond basic dereference)
|
|
26
|
+
*/
|
|
27
|
+
export declare class JsonSchemaAdapter extends AbstractAdapter {
|
|
28
|
+
readonly name = "jsonschema";
|
|
29
|
+
readonly vendor = "json-schema.org";
|
|
30
|
+
readonly version = "0.3.0";
|
|
31
|
+
canParse(input: string | object): boolean;
|
|
32
|
+
toEntitySchema(input: string | object, opts?: AdapterOptions): Promise<EntitySchema[]>;
|
|
33
|
+
/**
|
|
34
|
+
* Convert a named map of schemas directly to EntitySchema[].
|
|
35
|
+
* Used by OpenApiAdapter when it has already extracted components.schemas.
|
|
36
|
+
* Skips the "root entity" detection heuristic.
|
|
37
|
+
*/
|
|
38
|
+
schemasToEntities(schemas: Record<string, JsonSchema>, opts?: AdapterOptions): Promise<EntitySchema[]>;
|
|
39
|
+
private collectEntities;
|
|
40
|
+
private nameFromId;
|
|
41
|
+
private convertEntity;
|
|
42
|
+
private processProperty;
|
|
43
|
+
private mapXMostajsRelation;
|
|
44
|
+
private toFieldDef;
|
|
45
|
+
/**
|
|
46
|
+
* Generate indexes from x-index and x-unique on individual fields.
|
|
47
|
+
*/
|
|
48
|
+
private applyFieldIndexes;
|
|
49
|
+
private tryParseJson;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=jsonschema.adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonschema.adapter.d.ts","sourceRoot":"","sources":["../../src/adapters/jsonschema.adapter.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,YAAY,EAA4D,MAAM,cAAc,CAAC;AAC3G,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAe,KAAK,cAAc,EAAuB,MAAM,kBAAkB,CAAC;AAEzF,OAAO,EACL,KAAK,UAAU,EAOhB,MAAM,8BAA8B,CAAC;AAStC;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,iBAAkB,SAAQ,eAAe;IACpD,QAAQ,CAAC,IAAI,gBAAgB;IAC7B,QAAQ,CAAC,MAAM,qBAAqB;IACpC,QAAQ,CAAC,OAAO,WAAW;IAE3B,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO;IAoBnC,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAwC5F;;;;OAIG;IACG,iBAAiB,CACrB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,EACnC,IAAI,CAAC,EAAE,cAAc,GACpB,OAAO,CAAC,YAAY,EAAE,CAAC;IAa1B,OAAO,CAAC,eAAe;IA+BvB,OAAO,CAAC,UAAU;IAUlB,OAAO,CAAC,aAAa;IAwErB,OAAO,CAAC,eAAe;IAgDvB,OAAO,CAAC,mBAAmB;IAoB3B,OAAO,CAAC,UAAU;IAgDlB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAwBzB,OAAO,CAAC,YAAY;CAQrB"}
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
// jsonschema.adapter.ts
|
|
2
|
+
// Converts JSON Schema (Draft-07, 2019-09, 2020-12) to EntitySchema[].
|
|
3
|
+
// Parser : @apidevtools/json-schema-ref-parser.
|
|
4
|
+
// Author: Dr Hamid MADANI drmdh@msn.com
|
|
5
|
+
// License: AGPL-3.0-or-later
|
|
6
|
+
import $RefParser from '@apidevtools/json-schema-ref-parser';
|
|
7
|
+
import { AbstractAdapter } from '../core/abstract.adapter.js';
|
|
8
|
+
import { WarningCode } from '../core/types.js';
|
|
9
|
+
import { InvalidSchemaError } from '../core/errors.js';
|
|
10
|
+
import { detectDraft, getDefinitions, primaryType, isNullable, } from '../utils/jsonschema-types.js';
|
|
11
|
+
import { mapJsonSchemaType, mapArrayItems } from '../utils/jsonschema-type-mapper.js';
|
|
12
|
+
import { flattenAllOf, detectDiscriminator } from '../utils/jsonschema-flatten.js';
|
|
13
|
+
/**
|
|
14
|
+
* JsonSchemaAdapter — converts JSON Schema to EntitySchema[].
|
|
15
|
+
*
|
|
16
|
+
* Supports (MVP v0.3.0):
|
|
17
|
+
* - Draft-07, Draft 2019-09, Draft 2020-12 (auto-detected)
|
|
18
|
+
* - type: string/integer/number/boolean/array/object/null (including array form)
|
|
19
|
+
* - formats: date-time, date, time, email, uuid, uri, ipv4, regex, binary
|
|
20
|
+
* - Constraints: enum, const, default, minLength/maxLength, pattern, min/max
|
|
21
|
+
* - Nullable (both OpenAPI nullable: true and type: [T, "null"])
|
|
22
|
+
* - readOnly / writeOnly / deprecated (annotations preserved)
|
|
23
|
+
* - allOf flattening (inheritance merge)
|
|
24
|
+
* - oneOf + discriminator → discriminator + discriminatorValue
|
|
25
|
+
* - Entity detection : top-level (title+x-mostajs-entity) + all $defs
|
|
26
|
+
* - Extensions : x-mostajs-entity, x-mostajs-relation, x-primary, x-unique, x-index, x-indexes
|
|
27
|
+
* - $ref resolution (cycles detected, self-relation auto-created)
|
|
28
|
+
*
|
|
29
|
+
* Not yet supported (v0.4+):
|
|
30
|
+
* - $dynamicRef / $dynamicAnchor late-binding
|
|
31
|
+
* - patternProperties as structured mapping (falls back to json)
|
|
32
|
+
* - if/then/else conditional
|
|
33
|
+
* - External $ref resolution (HTTP/file beyond basic dereference)
|
|
34
|
+
*/
|
|
35
|
+
export class JsonSchemaAdapter extends AbstractAdapter {
|
|
36
|
+
name = 'jsonschema';
|
|
37
|
+
vendor = 'json-schema.org';
|
|
38
|
+
version = '0.3.0';
|
|
39
|
+
canParse(input) {
|
|
40
|
+
const obj = this.tryParseJson(input);
|
|
41
|
+
if (!obj || typeof obj !== 'object')
|
|
42
|
+
return false;
|
|
43
|
+
// Strongest indicator : $schema URL
|
|
44
|
+
if (typeof obj.$schema === 'string' &&
|
|
45
|
+
/json-schema\.org/.test(obj.$schema)) {
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
// Fallback : has type + properties (or $defs / definitions)
|
|
49
|
+
const o = obj;
|
|
50
|
+
if ((o.type === 'object' || Array.isArray(o.type)) &&
|
|
51
|
+
(!!o.properties || !!o.$defs || !!o.definitions)) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
// OpenAPI components/schemas shape — let OpenApiAdapter handle first
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
async toEntitySchema(input, opts) {
|
|
58
|
+
const raw = this.tryParseJson(input);
|
|
59
|
+
if (!raw || typeof raw !== 'object') {
|
|
60
|
+
throw new InvalidSchemaError('JsonSchemaAdapter expects an object or JSON string');
|
|
61
|
+
}
|
|
62
|
+
let resolved;
|
|
63
|
+
try {
|
|
64
|
+
// Dereference : resolves all $ref into inline schemas.
|
|
65
|
+
// structuredClone handles circular references (unlike JSON.parse/stringify)
|
|
66
|
+
// which matter when this adapter is called from OpenApiAdapter on already-dereferenced specs.
|
|
67
|
+
const cloned = typeof structuredClone === 'function'
|
|
68
|
+
? structuredClone(raw)
|
|
69
|
+
: JSON.parse(JSON.stringify(raw));
|
|
70
|
+
resolved = await $RefParser.dereference(cloned);
|
|
71
|
+
}
|
|
72
|
+
catch (e) {
|
|
73
|
+
throw new InvalidSchemaError(`Failed to dereference JSON Schema: ${e instanceof Error ? e.message : String(e)}`, { cause: e });
|
|
74
|
+
}
|
|
75
|
+
const draft = detectDraft(resolved);
|
|
76
|
+
if (draft === 'draft-04' || draft === 'draft-06') {
|
|
77
|
+
this.warn(opts, {
|
|
78
|
+
code: WarningCode.PREVIEW_FEATURE,
|
|
79
|
+
message: `Legacy draft ${draft} — mapping may be incomplete. Recommend upgrading to Draft 2020-12.`,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
// Collect entity candidates : root schema + all $defs/definitions
|
|
83
|
+
const candidates = this.collectEntities(resolved, opts);
|
|
84
|
+
// Convert each candidate
|
|
85
|
+
const seen = new Set(); // for cycle detection
|
|
86
|
+
const entities = candidates.map(c => this.convertEntity(c, candidates, seen, opts));
|
|
87
|
+
return entities;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Convert a named map of schemas directly to EntitySchema[].
|
|
91
|
+
* Used by OpenApiAdapter when it has already extracted components.schemas.
|
|
92
|
+
* Skips the "root entity" detection heuristic.
|
|
93
|
+
*/
|
|
94
|
+
async schemasToEntities(schemas, opts) {
|
|
95
|
+
const candidates = Object.entries(schemas).map(([name, schema]) => ({
|
|
96
|
+
name: this.pascalCase(name),
|
|
97
|
+
schema,
|
|
98
|
+
}));
|
|
99
|
+
const seen = new Set();
|
|
100
|
+
return candidates.map(c => this.convertEntity(c, candidates, seen, opts));
|
|
101
|
+
}
|
|
102
|
+
// ============================================================
|
|
103
|
+
// Entity collection
|
|
104
|
+
// ============================================================
|
|
105
|
+
collectEntities(root, opts) {
|
|
106
|
+
const out = [];
|
|
107
|
+
// 1. Root if it's an object with title OR x-mostajs-entity
|
|
108
|
+
const rootIsObject = primaryType(root) === 'object';
|
|
109
|
+
const rootName = root.title ?? this.nameFromId(root.$id);
|
|
110
|
+
const rootHasEntityMarker = !!root['x-mostajs-entity'];
|
|
111
|
+
if (rootIsObject && (rootHasEntityMarker || rootName)) {
|
|
112
|
+
out.push({ name: rootName ?? 'RootEntity', schema: root });
|
|
113
|
+
}
|
|
114
|
+
// 2. All $defs / definitions that are objects
|
|
115
|
+
const defs = getDefinitions(root);
|
|
116
|
+
for (const [key, def] of Object.entries(defs)) {
|
|
117
|
+
if (primaryType(def) === 'object') {
|
|
118
|
+
out.push({ name: def.title ?? key, schema: def });
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
this.warn(opts, {
|
|
122
|
+
code: WarningCode.FALLBACK_APPLIED,
|
|
123
|
+
message: `$defs["${key}"] is not an object — ignored as entity candidate`,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// Deduplicate by name (last-wins)
|
|
128
|
+
const byName = new Map();
|
|
129
|
+
for (const c of out)
|
|
130
|
+
byName.set(this.pascalCase(c.name), c);
|
|
131
|
+
return Array.from(byName.values()).map(c => ({ ...c, name: this.pascalCase(c.name) }));
|
|
132
|
+
}
|
|
133
|
+
nameFromId(id) {
|
|
134
|
+
if (!id)
|
|
135
|
+
return undefined;
|
|
136
|
+
const segments = id.replace(/\.json$/, '').split(/[/#]/);
|
|
137
|
+
return segments[segments.length - 1] || undefined;
|
|
138
|
+
}
|
|
139
|
+
// ============================================================
|
|
140
|
+
// Candidate → EntitySchema
|
|
141
|
+
// ============================================================
|
|
142
|
+
convertEntity(candidate, allCandidates, seen, opts) {
|
|
143
|
+
const name = candidate.name;
|
|
144
|
+
if (seen.has(name)) {
|
|
145
|
+
this.warn(opts, {
|
|
146
|
+
code: WarningCode.CYCLIC_REFERENCE,
|
|
147
|
+
message: `Cyclic entity reference detected at "${name}"`,
|
|
148
|
+
entity: name,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
seen.add(name);
|
|
152
|
+
// Flatten allOf composition
|
|
153
|
+
const schema = flattenAllOf(candidate.schema, w => this.warn(opts, w), name);
|
|
154
|
+
// Extract x-mostajs-entity metadata
|
|
155
|
+
const entityMeta = schema['x-mostajs-entity'] ?? {};
|
|
156
|
+
const tableName = entityMeta.tableName ?? this.snakeCase(name);
|
|
157
|
+
const entity = {
|
|
158
|
+
name,
|
|
159
|
+
collection: tableName,
|
|
160
|
+
fields: {},
|
|
161
|
+
relations: {},
|
|
162
|
+
indexes: [],
|
|
163
|
+
timestamps: entityMeta.timestamps ?? false,
|
|
164
|
+
};
|
|
165
|
+
if (entityMeta.softDelete)
|
|
166
|
+
entity.softDelete = true;
|
|
167
|
+
if (entityMeta.discriminator)
|
|
168
|
+
entity.discriminator = entityMeta.discriminator;
|
|
169
|
+
if (entityMeta.discriminatorValue)
|
|
170
|
+
entity.discriminatorValue = entityMeta.discriminatorValue;
|
|
171
|
+
// Also detect discriminator from OpenAPI-style discriminator property
|
|
172
|
+
if (!entity.discriminator) {
|
|
173
|
+
const d = detectDiscriminator(schema);
|
|
174
|
+
if (d)
|
|
175
|
+
entity.discriminator = d.propertyName;
|
|
176
|
+
}
|
|
177
|
+
// Process properties
|
|
178
|
+
const required = new Set(schema.required ?? []);
|
|
179
|
+
const entityNames = new Set(allCandidates.map(c => c.name));
|
|
180
|
+
for (const [key, propSchema] of Object.entries(schema.properties ?? {})) {
|
|
181
|
+
this.processProperty(key, propSchema, entity, required.has(key), entityNames, opts);
|
|
182
|
+
}
|
|
183
|
+
// Process x-indexes (model-level composite indexes)
|
|
184
|
+
const xIndexes = schema['x-indexes'];
|
|
185
|
+
if (Array.isArray(xIndexes)) {
|
|
186
|
+
for (const idx of xIndexes) {
|
|
187
|
+
const fields = {};
|
|
188
|
+
for (const f of idx.fields)
|
|
189
|
+
fields[f] = 'asc';
|
|
190
|
+
entity.indexes.push({ fields, unique: !!idx.unique });
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
// Auto-timestamps detection
|
|
194
|
+
if (!entity.timestamps &&
|
|
195
|
+
'createdAt' in entity.fields && 'updatedAt' in entity.fields) {
|
|
196
|
+
entity.timestamps = true;
|
|
197
|
+
}
|
|
198
|
+
return entity;
|
|
199
|
+
}
|
|
200
|
+
// ============================================================
|
|
201
|
+
// Property → Field or Relation
|
|
202
|
+
// ============================================================
|
|
203
|
+
processProperty(key, propSchema, entity, required, entityNames, opts) {
|
|
204
|
+
// 1. Explicit x-mostajs-relation always wins
|
|
205
|
+
const xRel = propSchema['x-mostajs-relation'];
|
|
206
|
+
if (xRel) {
|
|
207
|
+
entity.relations[key] = this.mapXMostajsRelation(xRel, required);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
// 2. Detection by shape : object with title matching an entity = belongsTo
|
|
211
|
+
if (primaryType(propSchema) === 'object' && propSchema.title &&
|
|
212
|
+
entityNames.has(this.pascalCase(propSchema.title))) {
|
|
213
|
+
entity.relations[key] = {
|
|
214
|
+
target: this.pascalCase(propSchema.title),
|
|
215
|
+
type: 'many-to-one',
|
|
216
|
+
required: required && !isNullable(propSchema),
|
|
217
|
+
nullable: isNullable(propSchema),
|
|
218
|
+
};
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
// 3. Array of entities = hasMany
|
|
222
|
+
if (primaryType(propSchema) === 'array' && !Array.isArray(propSchema.items)) {
|
|
223
|
+
const item = propSchema.items;
|
|
224
|
+
if (item && primaryType(item) === 'object' && item.title &&
|
|
225
|
+
entityNames.has(this.pascalCase(item.title))) {
|
|
226
|
+
entity.relations[key] = {
|
|
227
|
+
target: this.pascalCase(item.title),
|
|
228
|
+
type: 'one-to-many',
|
|
229
|
+
};
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
// 4. Regular field
|
|
234
|
+
const field = this.toFieldDef(key, propSchema, required, entity.name, opts);
|
|
235
|
+
if (field) {
|
|
236
|
+
entity.fields[key] = field;
|
|
237
|
+
this.applyFieldIndexes(key, propSchema, entity);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
mapXMostajsRelation(xRel, required) {
|
|
241
|
+
const typeMap = {
|
|
242
|
+
belongsTo: 'many-to-one',
|
|
243
|
+
hasOne: 'one-to-one',
|
|
244
|
+
hasMany: 'one-to-many',
|
|
245
|
+
belongsToMany: 'many-to-many',
|
|
246
|
+
};
|
|
247
|
+
const rel = {
|
|
248
|
+
target: xRel.target,
|
|
249
|
+
type: typeMap[xRel.type],
|
|
250
|
+
};
|
|
251
|
+
if (xRel.required ?? required)
|
|
252
|
+
rel.required = true;
|
|
253
|
+
if (xRel.nullable)
|
|
254
|
+
rel.nullable = true;
|
|
255
|
+
if (xRel.foreignKey)
|
|
256
|
+
rel.joinColumn = xRel.foreignKey;
|
|
257
|
+
if (xRel.otherKey)
|
|
258
|
+
rel.inverseJoinColumn = xRel.otherKey;
|
|
259
|
+
if (xRel.through)
|
|
260
|
+
rel.through = xRel.through;
|
|
261
|
+
if (xRel.onDelete)
|
|
262
|
+
rel.onDelete = xRel.onDelete;
|
|
263
|
+
return rel;
|
|
264
|
+
}
|
|
265
|
+
toFieldDef(key, schema, required, entityName, opts) {
|
|
266
|
+
const type = mapJsonSchemaType(schema, key, entityName, w => this.warn(opts, w));
|
|
267
|
+
if (!type) {
|
|
268
|
+
this.warn(opts, {
|
|
269
|
+
code: WarningCode.UNSUPPORTED_FEATURE,
|
|
270
|
+
message: `Cannot map property "${key}" — no type information`,
|
|
271
|
+
entity: entityName,
|
|
272
|
+
field: key,
|
|
273
|
+
});
|
|
274
|
+
return null;
|
|
275
|
+
}
|
|
276
|
+
const nullable = isNullable(schema);
|
|
277
|
+
const fd = {
|
|
278
|
+
type,
|
|
279
|
+
required: required && !nullable,
|
|
280
|
+
};
|
|
281
|
+
// Array : resolve arrayOf
|
|
282
|
+
if (type === 'array') {
|
|
283
|
+
const inner = mapArrayItems(schema, key, entityName, w => this.warn(opts, w));
|
|
284
|
+
if (inner)
|
|
285
|
+
fd.arrayOf = inner;
|
|
286
|
+
}
|
|
287
|
+
// Enum
|
|
288
|
+
if (schema.enum?.length) {
|
|
289
|
+
fd.enum = schema.enum.filter(v => v != null).map(String);
|
|
290
|
+
}
|
|
291
|
+
// Default
|
|
292
|
+
if (schema.default !== undefined) {
|
|
293
|
+
fd.default = schema.default;
|
|
294
|
+
}
|
|
295
|
+
else if (schema.const !== undefined) {
|
|
296
|
+
fd.default = schema.const;
|
|
297
|
+
}
|
|
298
|
+
// x-primary / x-unique
|
|
299
|
+
if (schema['x-unique'] === true)
|
|
300
|
+
fd.unique = true;
|
|
301
|
+
return fd;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Generate indexes from x-index and x-unique on individual fields.
|
|
305
|
+
*/
|
|
306
|
+
applyFieldIndexes(key, schema, entity) {
|
|
307
|
+
const xIndex = schema['x-index'];
|
|
308
|
+
const xUnique = schema['x-unique'] === true;
|
|
309
|
+
if (xIndex === true) {
|
|
310
|
+
entity.indexes.push({ fields: { [key]: 'asc' } });
|
|
311
|
+
}
|
|
312
|
+
else if (xIndex && typeof xIndex === 'object') {
|
|
313
|
+
entity.indexes.push({
|
|
314
|
+
fields: { [key]: 'asc' },
|
|
315
|
+
unique: !!xIndex.unique,
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
else if (xUnique) {
|
|
319
|
+
entity.indexes.push({ fields: { [key]: 'asc' }, unique: true });
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
// ============================================================
|
|
323
|
+
// Utilities
|
|
324
|
+
// ============================================================
|
|
325
|
+
tryParseJson(input) {
|
|
326
|
+
if (typeof input !== 'string')
|
|
327
|
+
return input;
|
|
328
|
+
try {
|
|
329
|
+
return JSON.parse(input);
|
|
330
|
+
}
|
|
331
|
+
catch {
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
//# sourceMappingURL=jsonschema.adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonschema.adapter.js","sourceRoot":"","sources":["../../src/adapters/jsonschema.adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { EntitySchema } from '@mostajs/orm';
|
|
2
|
+
import { AbstractAdapter } from '../core/abstract.adapter.js';
|
|
3
|
+
import { type AdapterOptions } from '../core/types.js';
|
|
4
|
+
/**
|
|
5
|
+
* OpenApiAdapter — converts OpenAPI specs to EntitySchema[].
|
|
6
|
+
*
|
|
7
|
+
* Supported (MVP v0.4.0) :
|
|
8
|
+
* - OpenAPI 3.0.x (auto-normalized to 3.1 shape before conversion)
|
|
9
|
+
* - OpenAPI 3.1.x (JSON Schema 2020-12 compliant)
|
|
10
|
+
* - Extracts all `components/schemas` as entities
|
|
11
|
+
* - All features of JsonSchemaAdapter (allOf, discriminator, $ref, x-mostajs-*, etc.)
|
|
12
|
+
* - 3.0→3.1 normalizations :
|
|
13
|
+
* - `nullable: true` → `type: [T, "null"]`
|
|
14
|
+
* - `example: X` → `examples: [X]`
|
|
15
|
+
* - `exclusiveMinimum: true` → `exclusiveMinimum: <number>`
|
|
16
|
+
* - Input : object, JSON string, YAML string, or file path (via @readme/openapi-parser)
|
|
17
|
+
*
|
|
18
|
+
* Not supported yet (v0.5+) :
|
|
19
|
+
* - paths → CRUD endpoint deduction (opt-in flag planned)
|
|
20
|
+
* - Inline schemas inside `paths` (only `components/schemas` become entities)
|
|
21
|
+
* - Webhooks (detected, parsed, but not mapped to entities)
|
|
22
|
+
* - `pathItems` components (parsed, not used for entities)
|
|
23
|
+
* - File upload body (kept as string with warning)
|
|
24
|
+
*/
|
|
25
|
+
export declare class OpenApiAdapter extends AbstractAdapter {
|
|
26
|
+
readonly name = "openapi";
|
|
27
|
+
readonly vendor = "openapis.org";
|
|
28
|
+
readonly version = "0.4.0";
|
|
29
|
+
/** Internal JsonSchemaAdapter used to convert normalized schemas */
|
|
30
|
+
private readonly jsonSchemaAdapter;
|
|
31
|
+
canParse(input: string | object): boolean;
|
|
32
|
+
toEntitySchema(input: string | object, opts?: AdapterOptions): Promise<EntitySchema[]>;
|
|
33
|
+
/**
|
|
34
|
+
* Walk the ORIGINAL (pre-dereference) spec and record all x-mostajs-relation
|
|
35
|
+
* extensions on properties that also have a $ref. These siblings are stripped
|
|
36
|
+
* during dereference, so we capture them here and re-attach after.
|
|
37
|
+
*/
|
|
38
|
+
private extractRelationOverrides;
|
|
39
|
+
/**
|
|
40
|
+
* After dereference, re-attach the x-mostajs-relation extensions we extracted.
|
|
41
|
+
* The property schema becomes `{ ...inlinedRef, 'x-mostajs-relation': saved }`.
|
|
42
|
+
*/
|
|
43
|
+
private reattachRelationOverrides;
|
|
44
|
+
/** Clone input for the parser (must not mutate caller's input) */
|
|
45
|
+
private cloneForParser;
|
|
46
|
+
/**
|
|
47
|
+
* Normalize input to object form.
|
|
48
|
+
* @readme/openapi-parser treats strings as file paths (not content),
|
|
49
|
+
* so we JSON.parse strings ourselves here.
|
|
50
|
+
*/
|
|
51
|
+
private resolveInput;
|
|
52
|
+
/**
|
|
53
|
+
* Ensure each schema in components.schemas has `title: <key>`.
|
|
54
|
+
* After dereference, inlined schemas need a title for JsonSchemaAdapter's
|
|
55
|
+
* relation detection (which matches property schema titles against entity names).
|
|
56
|
+
*/
|
|
57
|
+
private injectSchemaTitles;
|
|
58
|
+
/** Parse JSON string if necessary — used for canParse detection only */
|
|
59
|
+
private tryParseAny;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=openapi.adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openapi.adapter.d.ts","sourceRoot":"","sources":["../../src/adapters/openapi.adapter.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAe,KAAK,cAAc,EAAuB,MAAM,kBAAkB,CAAC;AAkBzF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,cAAe,SAAQ,eAAe;IACjD,QAAQ,CAAC,IAAI,aAAa;IAC1B,QAAQ,CAAC,MAAM,kBAAkB;IACjC,QAAQ,CAAC,OAAO,WAAW;IAE3B,oEAAoE;IACpE,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAA2B;IAE7D,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO;IAcnC,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IA8E5F;;;;OAIG;IACH,OAAO,CAAC,wBAAwB;IAqBhC;;;OAGG;IACH,OAAO,CAAC,yBAAyB;IAuBjC,kEAAkE;IAClE,OAAO,CAAC,cAAc;IAMtB;;;;OAIG;IACH,OAAO,CAAC,YAAY;IAkBpB;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;IAU1B,wEAAwE;IACxE,OAAO,CAAC,WAAW;CAWpB"}
|