@amritk/asyncapi 0.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.
@@ -0,0 +1,20 @@
1
+ const ASYNCAPI_DIALECT = /^application\/vnd\.aai\.asyncapi([+;]|$)/;
2
+ const OPENAPI_DIALECT = /^application\/vnd\.oai\.openapi([+;]|$)/;
3
+ const JSON_SCHEMA_DIALECT = /^application\/schema\+(?:json|yaml);\s*version=(draft-07|draft-2020-12)\s*$/;
4
+ const classifySchemaFormat = (schemaFormat) => {
5
+ if (schemaFormat === void 0)
6
+ return "asyncapi";
7
+ if (typeof schemaFormat !== "string")
8
+ return "unsupported";
9
+ if (ASYNCAPI_DIALECT.test(schemaFormat))
10
+ return "asyncapi";
11
+ if (OPENAPI_DIALECT.test(schemaFormat))
12
+ return "openapi";
13
+ const jsonSchema = JSON_SCHEMA_DIALECT.exec(schemaFormat);
14
+ if (jsonSchema)
15
+ return jsonSchema[1] === "draft-07" ? "draft-07" : "2020-12";
16
+ return "unsupported";
17
+ };
18
+ export {
19
+ classifySchemaFormat
20
+ };
@@ -0,0 +1,84 @@
1
+ /**
2
+ * The normalized shape both AsyncAPI majors are extracted into.
3
+ *
4
+ * The model is 3.0-shaped: 2.x documents are mapped onto it during extraction
5
+ * (`publish`/`subscribe` become `receive`/`send`, sibling `schemaFormat` keys
6
+ * become per-schema classifications) so every consumer sees one structure
7
+ * regardless of which major the author wrote.
8
+ */
9
+ /**
10
+ * A problem found while extracting, tied to the document location it was found
11
+ * at. Issues are collected rather than thrown: a document with one Avro payload
12
+ * still yields every JSON-Schema payload it declares, and the caller decides
13
+ * whether the skipped parts are worth failing over.
14
+ */
15
+ export type ExtractionIssue = {
16
+ /** JSON-Pointer-ish location of the problem (e.g. `#/channels/foo/messages/bar`). */
17
+ readonly path: string;
18
+ readonly message: string;
19
+ };
20
+ /**
21
+ * Which way a message flows, named from the application's point of view — the
22
+ * same convention `@amritk/api` uses. AsyncAPI 2.x's `publish` (clients
23
+ * publish, the application receives) maps to `receive`; `subscribe` maps to
24
+ * `send`; 3.0's `action` is already spelled this way.
25
+ */
26
+ export type MessageDirection = 'send' | 'receive';
27
+ /** One message on a channel, its schemas already normalized to JSON Schema 2020-12. */
28
+ export type NormalizedMessage = {
29
+ /**
30
+ * The message's identity: the 3.0 channel-messages key, or in 2.x the
31
+ * message `name`, falling back to `messageId` and then a positional
32
+ * `message-<n>`. Doubles as the wire discriminator value in a later
33
+ * message-contract projection.
34
+ */
35
+ readonly name: string;
36
+ /** Key of the channel the message was declared on. */
37
+ readonly channelKey: string;
38
+ /** Absent when no operation names the message, so its flow is undeclared. */
39
+ readonly direction?: MessageDirection;
40
+ readonly contentType?: string;
41
+ /**
42
+ * The declared payload `schemaFormat`, kept verbatim (absent = the AsyncAPI
43
+ * default dialect) so a consumer can see *why* a payload was or was not
44
+ * extracted.
45
+ */
46
+ readonly schemaFormat?: string;
47
+ /**
48
+ * The payload as a self-contained JSON Schema 2020-12 document: the dialect
49
+ * normalized, and every `#/components/schemas/...` reference rebased into a
50
+ * local `$defs`. Absent when the message declares none, or when its
51
+ * `schemaFormat` is not a JSON Schema dialect (an issue records which).
52
+ */
53
+ readonly payload?: Record<string, unknown>;
54
+ /** The headers schema, normalized the same way as {@link payload}. */
55
+ readonly headers?: Record<string, unknown>;
56
+ };
57
+ export type NormalizedChannel = {
58
+ /** The channel's key in the document's `channels` map. */
59
+ readonly key: string;
60
+ /** The 3.0 `address`; for 2.x the channel key, which *is* the topic/path. */
61
+ readonly address?: string;
62
+ readonly messages: readonly NormalizedMessage[];
63
+ };
64
+ export type AsyncApiModel = {
65
+ /** The declared `asyncapi` version, verbatim (e.g. `2.6.0`, `3.0.0`). */
66
+ readonly version: string;
67
+ readonly major: 2 | 3;
68
+ /** The document's `info.title`, when it is a string. */
69
+ readonly title?: string;
70
+ readonly channels: readonly NormalizedChannel[];
71
+ readonly issues: readonly ExtractionIssue[];
72
+ };
73
+ /**
74
+ * One generatable schema pulled out of the model: what to hand `buildSchema`
75
+ * and where its output tree belongs, mirroring how `--schema-dir` maps each
76
+ * schema file to its own subdirectory.
77
+ */
78
+ export type ExtractedSchema = {
79
+ /** Output subdirectory, e.g. `channels/user-signed-up/user-event`. */
80
+ readonly subDir: string;
81
+ /** PascalCase root type name derived from the message identity, not the schema `title`. */
82
+ readonly rootTypeName: string;
83
+ readonly schema: Record<string, unknown>;
84
+ };
package/dist/types.js ADDED
File without changes
@@ -0,0 +1,14 @@
1
+ export type UnwrappedSchema = {
2
+ /** The wrapper's `schemaFormat`, or `undefined` when the node was a bare schema. */
3
+ readonly schemaFormat?: unknown;
4
+ readonly schema: unknown;
5
+ };
6
+ /**
7
+ * Unwraps an AsyncAPI 3.0 Multi Format Schema Object (`{ schemaFormat,
8
+ * schema }`) into its parts, passing a bare Schema Object through untouched.
9
+ *
10
+ * The wrapper is recognized only when *both* keys are present: `schemaFormat`
11
+ * is required on the wrapper, and demanding `schema` too keeps a plain schema
12
+ * that merely declares a property named `schemaFormat` from losing its body.
13
+ */
14
+ export declare const unwrapMultiFormat: (node: unknown) => UnwrappedSchema;
@@ -0,0 +1,14 @@
1
+ import { readKey } from "@amritk/helpers/read-key";
2
+ const unwrapMultiFormat = (node) => {
3
+ if (typeof node === "object" && node !== null && !Array.isArray(node)) {
4
+ const record = node;
5
+ const schemaFormat = readKey(record, "schemaFormat");
6
+ if (schemaFormat !== void 0 && Object.hasOwn(record, "schema")) {
7
+ return { schemaFormat, schema: readKey(record, "schema") };
8
+ }
9
+ }
10
+ return { schema: node };
11
+ };
12
+ export {
13
+ unwrapMultiFormat
14
+ };
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@amritk/asyncapi",
3
+ "version": "0.1.0",
4
+ "description": "Extract JSON Schemas from AsyncAPI 2.x/3.0 documents for the mjst generators.",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "engines": {
8
+ "node": ">=20"
9
+ },
10
+ "license": "MIT",
11
+ "author": "amritk",
12
+ "keywords": [
13
+ "asyncapi",
14
+ "json-schema",
15
+ "typescript",
16
+ "codegen",
17
+ "mjst"
18
+ ],
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/amritk/mjst.git",
22
+ "directory": "packages/asyncapi"
23
+ },
24
+ "homepage": "https://github.com/amritk/mjst/tree/main/packages/asyncapi#readme",
25
+ "bugs": {
26
+ "url": "https://github.com/amritk/mjst/issues"
27
+ },
28
+ "files": [
29
+ "dist",
30
+ "AI.md"
31
+ ],
32
+ "publishConfig": {
33
+ "access": "public"
34
+ },
35
+ "scripts": {
36
+ "build": "tsgo -p tsconfig.build.json && tsc-alias -p tsconfig.build.json -f && node ../../scripts/strip-comments.mjs",
37
+ "prepublishOnly": "node ../../scripts/check-publishable.mjs",
38
+ "types:check": "tsgo -p . --noEmit",
39
+ "test": "NODE_ENV=production vitest run --root ../.. packages/asyncapi"
40
+ },
41
+ "dependencies": {
42
+ "@amritk/helpers": "^0.19.0"
43
+ },
44
+ "exports": {
45
+ "./package.json": "./package.json",
46
+ ".": {
47
+ "types": "./dist/index.d.ts",
48
+ "default": "./dist/index.js"
49
+ }
50
+ },
51
+ "typesVersions": {
52
+ "*": {
53
+ "*": [
54
+ "./dist/*.d.ts"
55
+ ]
56
+ }
57
+ }
58
+ }