@eventcatalog/connectors 0.2.0 → 0.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/dist/index.d.mts +69 -1
- package/dist/index.d.ts +69 -1
- package/dist/index.js +147 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +135 -0
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -1
package/dist/index.d.mts
CHANGED
|
@@ -65,6 +65,57 @@ type DirectorySource = {
|
|
|
65
65
|
* Defines a custom directory source with type inference for connector authors.
|
|
66
66
|
*/
|
|
67
67
|
declare const defineDirectorySource: <TSource extends DirectorySource>(source: TSource) => TSource;
|
|
68
|
+
/**
|
|
69
|
+
* Describes where a synced schema came from.
|
|
70
|
+
*/
|
|
71
|
+
type SchemaEntrySource = {
|
|
72
|
+
/** Provider identifier, for example `git`, `github`, `confluent`, or `eventbridge`. */
|
|
73
|
+
provider: string;
|
|
74
|
+
/** Optional provider-specific identifier for the external schema. */
|
|
75
|
+
id?: string;
|
|
76
|
+
/** Optional URL back to the schema source. */
|
|
77
|
+
url?: string;
|
|
78
|
+
/** Optional version/ref/branch for versioned sources. */
|
|
79
|
+
ref?: string;
|
|
80
|
+
/** Optional branch for git-backed schema sources. */
|
|
81
|
+
branch?: string;
|
|
82
|
+
/** Optional path inside the source. */
|
|
83
|
+
path?: string;
|
|
84
|
+
[key: string]: unknown;
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* Schema returned by an external schema source.
|
|
88
|
+
*/
|
|
89
|
+
type SchemaEntry = {
|
|
90
|
+
id: string;
|
|
91
|
+
name?: string;
|
|
92
|
+
format?: string;
|
|
93
|
+
content: string;
|
|
94
|
+
source: SchemaEntrySource;
|
|
95
|
+
};
|
|
96
|
+
type SchemaResolveContext = {
|
|
97
|
+
/** Absolute path to the message file that referenced the schema. */
|
|
98
|
+
messageFilePath?: string;
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* Loads schemas from an external source.
|
|
102
|
+
*
|
|
103
|
+
* EventCatalog calls schema sources during content sync to resolve schema
|
|
104
|
+
* references from messages into generated schema collection entries.
|
|
105
|
+
*/
|
|
106
|
+
type SchemaSource = {
|
|
107
|
+
type: "schemas";
|
|
108
|
+
/** Stable source identifier used in schema refs, for example `contracts`. */
|
|
109
|
+
name: string;
|
|
110
|
+
/** Returns true when this source can resolve the given schema ref. */
|
|
111
|
+
canResolve: (ref: string) => boolean;
|
|
112
|
+
/** Resolves a schema ref into schema content and source metadata. */
|
|
113
|
+
resolve: (ref: string, context?: SchemaResolveContext) => Promise<SchemaEntry | undefined>;
|
|
114
|
+
};
|
|
115
|
+
/**
|
|
116
|
+
* Defines a custom schema source with type inference for connector authors.
|
|
117
|
+
*/
|
|
118
|
+
declare const defineSchemaSource: <TSource extends SchemaSource>(source: TSource) => TSource;
|
|
68
119
|
|
|
69
120
|
type GitHubDirectoryOptions = {
|
|
70
121
|
org: string;
|
|
@@ -94,4 +145,21 @@ type MicrosoftEntraDirectoryOptions = {
|
|
|
94
145
|
};
|
|
95
146
|
declare const microsoftEntraDirectory: (options: MicrosoftEntraDirectoryOptions) => DirectorySource;
|
|
96
147
|
|
|
97
|
-
|
|
148
|
+
type GitSchemaSourceOptions = {
|
|
149
|
+
/**
|
|
150
|
+
* Stable source name used in schema refs, for example:
|
|
151
|
+
* `git://contracts/events/OrderPlaced.schema.json`.
|
|
152
|
+
*/
|
|
153
|
+
name: string;
|
|
154
|
+
/** Git repository URL. Supports any URL your local git can clone. */
|
|
155
|
+
url: string;
|
|
156
|
+
/** Branch to clone. */
|
|
157
|
+
branch?: string;
|
|
158
|
+
/** Optional directory inside the repository that contains schemas. */
|
|
159
|
+
directory?: string;
|
|
160
|
+
/** Optional HTTPS token. SSH auth is handled by the local git environment. */
|
|
161
|
+
token?: string;
|
|
162
|
+
};
|
|
163
|
+
declare const gitSchemaSource: (options: GitSchemaSourceOptions) => SchemaSource;
|
|
164
|
+
|
|
165
|
+
export { type DirectoryEntrySource, type DirectorySource, type DirectoryTeam, type DirectoryUser, type SchemaEntry, type SchemaEntrySource, type SchemaResolveContext, type SchemaSource, defineDirectorySource, defineSchemaSource, gitSchemaSource, githubDirectory, microsoftEntraDirectory };
|
package/dist/index.d.ts
CHANGED
|
@@ -65,6 +65,57 @@ type DirectorySource = {
|
|
|
65
65
|
* Defines a custom directory source with type inference for connector authors.
|
|
66
66
|
*/
|
|
67
67
|
declare const defineDirectorySource: <TSource extends DirectorySource>(source: TSource) => TSource;
|
|
68
|
+
/**
|
|
69
|
+
* Describes where a synced schema came from.
|
|
70
|
+
*/
|
|
71
|
+
type SchemaEntrySource = {
|
|
72
|
+
/** Provider identifier, for example `git`, `github`, `confluent`, or `eventbridge`. */
|
|
73
|
+
provider: string;
|
|
74
|
+
/** Optional provider-specific identifier for the external schema. */
|
|
75
|
+
id?: string;
|
|
76
|
+
/** Optional URL back to the schema source. */
|
|
77
|
+
url?: string;
|
|
78
|
+
/** Optional version/ref/branch for versioned sources. */
|
|
79
|
+
ref?: string;
|
|
80
|
+
/** Optional branch for git-backed schema sources. */
|
|
81
|
+
branch?: string;
|
|
82
|
+
/** Optional path inside the source. */
|
|
83
|
+
path?: string;
|
|
84
|
+
[key: string]: unknown;
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* Schema returned by an external schema source.
|
|
88
|
+
*/
|
|
89
|
+
type SchemaEntry = {
|
|
90
|
+
id: string;
|
|
91
|
+
name?: string;
|
|
92
|
+
format?: string;
|
|
93
|
+
content: string;
|
|
94
|
+
source: SchemaEntrySource;
|
|
95
|
+
};
|
|
96
|
+
type SchemaResolveContext = {
|
|
97
|
+
/** Absolute path to the message file that referenced the schema. */
|
|
98
|
+
messageFilePath?: string;
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* Loads schemas from an external source.
|
|
102
|
+
*
|
|
103
|
+
* EventCatalog calls schema sources during content sync to resolve schema
|
|
104
|
+
* references from messages into generated schema collection entries.
|
|
105
|
+
*/
|
|
106
|
+
type SchemaSource = {
|
|
107
|
+
type: "schemas";
|
|
108
|
+
/** Stable source identifier used in schema refs, for example `contracts`. */
|
|
109
|
+
name: string;
|
|
110
|
+
/** Returns true when this source can resolve the given schema ref. */
|
|
111
|
+
canResolve: (ref: string) => boolean;
|
|
112
|
+
/** Resolves a schema ref into schema content and source metadata. */
|
|
113
|
+
resolve: (ref: string, context?: SchemaResolveContext) => Promise<SchemaEntry | undefined>;
|
|
114
|
+
};
|
|
115
|
+
/**
|
|
116
|
+
* Defines a custom schema source with type inference for connector authors.
|
|
117
|
+
*/
|
|
118
|
+
declare const defineSchemaSource: <TSource extends SchemaSource>(source: TSource) => TSource;
|
|
68
119
|
|
|
69
120
|
type GitHubDirectoryOptions = {
|
|
70
121
|
org: string;
|
|
@@ -94,4 +145,21 @@ type MicrosoftEntraDirectoryOptions = {
|
|
|
94
145
|
};
|
|
95
146
|
declare const microsoftEntraDirectory: (options: MicrosoftEntraDirectoryOptions) => DirectorySource;
|
|
96
147
|
|
|
97
|
-
|
|
148
|
+
type GitSchemaSourceOptions = {
|
|
149
|
+
/**
|
|
150
|
+
* Stable source name used in schema refs, for example:
|
|
151
|
+
* `git://contracts/events/OrderPlaced.schema.json`.
|
|
152
|
+
*/
|
|
153
|
+
name: string;
|
|
154
|
+
/** Git repository URL. Supports any URL your local git can clone. */
|
|
155
|
+
url: string;
|
|
156
|
+
/** Branch to clone. */
|
|
157
|
+
branch?: string;
|
|
158
|
+
/** Optional directory inside the repository that contains schemas. */
|
|
159
|
+
directory?: string;
|
|
160
|
+
/** Optional HTTPS token. SSH auth is handled by the local git environment. */
|
|
161
|
+
token?: string;
|
|
162
|
+
};
|
|
163
|
+
declare const gitSchemaSource: (options: GitSchemaSourceOptions) => SchemaSource;
|
|
164
|
+
|
|
165
|
+
export { type DirectoryEntrySource, type DirectorySource, type DirectoryTeam, type DirectoryUser, type SchemaEntry, type SchemaEntrySource, type SchemaResolveContext, type SchemaSource, defineDirectorySource, defineSchemaSource, gitSchemaSource, githubDirectory, microsoftEntraDirectory };
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,12 +17,22 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/index.ts
|
|
21
31
|
var src_exports = {};
|
|
22
32
|
__export(src_exports, {
|
|
23
33
|
defineDirectorySource: () => defineDirectorySource,
|
|
34
|
+
defineSchemaSource: () => defineSchemaSource,
|
|
35
|
+
gitSchemaSource: () => gitSchemaSource,
|
|
24
36
|
githubDirectory: () => githubDirectory,
|
|
25
37
|
microsoftEntraDirectory: () => microsoftEntraDirectory
|
|
26
38
|
});
|
|
@@ -28,6 +40,7 @@ module.exports = __toCommonJS(src_exports);
|
|
|
28
40
|
|
|
29
41
|
// src/types.ts
|
|
30
42
|
var defineDirectorySource = (source) => source;
|
|
43
|
+
var defineSchemaSource = (source) => source;
|
|
31
44
|
|
|
32
45
|
// src/github-directory.ts
|
|
33
46
|
var GITHUB_API_VERSION = "2022-11-28";
|
|
@@ -502,9 +515,143 @@ var waitForRetry = async (retryAfter) => {
|
|
|
502
515
|
const delayMs = Number.isFinite(retryAfterSeconds) ? Math.max(retryAfterSeconds, 0) * 1e3 : 1e3;
|
|
503
516
|
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
504
517
|
};
|
|
518
|
+
|
|
519
|
+
// src/git-schema-source.ts
|
|
520
|
+
var import_promises = require("fs/promises");
|
|
521
|
+
var import_node_os = require("os");
|
|
522
|
+
var import_node_path = __toESM(require("path"));
|
|
523
|
+
var import_simple_git = __toESM(require("simple-git"));
|
|
524
|
+
var getSchemaFormat = (filePath) => {
|
|
525
|
+
const extension = import_node_path.default.extname(filePath).replace(".", "").toLowerCase();
|
|
526
|
+
if (extension === "avsc" || extension === "avro") return "avro";
|
|
527
|
+
if (extension === "proto") return "protobuf";
|
|
528
|
+
if (extension === "json") return "jsonschema";
|
|
529
|
+
if (extension === "yaml" || extension === "yml") return "yaml";
|
|
530
|
+
return extension || "unknown";
|
|
531
|
+
};
|
|
532
|
+
var parseGitSchemaRef = (ref) => {
|
|
533
|
+
let url;
|
|
534
|
+
try {
|
|
535
|
+
url = new URL(ref);
|
|
536
|
+
} catch {
|
|
537
|
+
throw new Error(
|
|
538
|
+
`Invalid git schema ref "${ref}". Expected git://<source-name>/<path>.`
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
if (url.protocol !== "git:") {
|
|
542
|
+
throw new Error(
|
|
543
|
+
`Invalid git schema ref "${ref}". Expected git://<source-name>/<path>.`
|
|
544
|
+
);
|
|
545
|
+
}
|
|
546
|
+
const filePath = decodeURIComponent(url.pathname.replace(/^\//, ""));
|
|
547
|
+
if (!url.hostname || !filePath) {
|
|
548
|
+
throw new Error(
|
|
549
|
+
`Invalid git schema ref "${ref}". Expected git://<source-name>/<path>.`
|
|
550
|
+
);
|
|
551
|
+
}
|
|
552
|
+
if (filePath.split(/[\\/]/).includes("..")) {
|
|
553
|
+
throw new Error(
|
|
554
|
+
`Schema path "${filePath}" resolves outside the git schema source directory.`
|
|
555
|
+
);
|
|
556
|
+
}
|
|
557
|
+
return {
|
|
558
|
+
sourceName: url.hostname,
|
|
559
|
+
filePath
|
|
560
|
+
};
|
|
561
|
+
};
|
|
562
|
+
var addTokenToHttpsUrl = (url, token) => {
|
|
563
|
+
if (!token || !url.startsWith("https://")) return url;
|
|
564
|
+
const parsed = new URL(url);
|
|
565
|
+
parsed.username = token;
|
|
566
|
+
parsed.password = "x-oauth-basic";
|
|
567
|
+
return parsed.toString();
|
|
568
|
+
};
|
|
569
|
+
var resolveInside = (basePath, ...segments) => {
|
|
570
|
+
const base = import_node_path.default.resolve(basePath);
|
|
571
|
+
const resolved = import_node_path.default.resolve(base, ...segments);
|
|
572
|
+
if (resolved !== base && !resolved.startsWith(`${base}${import_node_path.default.sep}`)) {
|
|
573
|
+
throw new Error(
|
|
574
|
+
`Schema path "${segments.join("/")}" resolves outside the git schema source directory.`
|
|
575
|
+
);
|
|
576
|
+
}
|
|
577
|
+
return resolved;
|
|
578
|
+
};
|
|
579
|
+
var isNodeFileNotFoundError = (error) => {
|
|
580
|
+
return error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
581
|
+
};
|
|
582
|
+
var gitSchemaSource = (options) => {
|
|
583
|
+
if (!options.name) {
|
|
584
|
+
throw new Error("Git schema source requires a name.");
|
|
585
|
+
}
|
|
586
|
+
if (!options.url) {
|
|
587
|
+
throw new Error("Git schema source requires a repository url.");
|
|
588
|
+
}
|
|
589
|
+
const branch = options.branch ?? "main";
|
|
590
|
+
const directory = options.directory ?? "";
|
|
591
|
+
let checkoutPath;
|
|
592
|
+
const checkout = async () => {
|
|
593
|
+
checkoutPath = checkoutPath ?? (async () => {
|
|
594
|
+
const target = await (0, import_promises.mkdtemp)(
|
|
595
|
+
import_node_path.default.join((0, import_node_os.tmpdir)(), "eventcatalog-schema-git-")
|
|
596
|
+
);
|
|
597
|
+
await (0, import_simple_git.default)().clone(
|
|
598
|
+
addTokenToHttpsUrl(options.url, options.token),
|
|
599
|
+
target,
|
|
600
|
+
{
|
|
601
|
+
"--branch": branch,
|
|
602
|
+
"--depth": 1,
|
|
603
|
+
"--single-branch": null
|
|
604
|
+
}
|
|
605
|
+
);
|
|
606
|
+
return target;
|
|
607
|
+
})();
|
|
608
|
+
return checkoutPath;
|
|
609
|
+
};
|
|
610
|
+
return defineSchemaSource({
|
|
611
|
+
type: "schemas",
|
|
612
|
+
name: options.name,
|
|
613
|
+
canResolve: (id) => {
|
|
614
|
+
if (!id.startsWith("git://")) return false;
|
|
615
|
+
return parseGitSchemaRef(id).sourceName === options.name;
|
|
616
|
+
},
|
|
617
|
+
resolve: async (id) => {
|
|
618
|
+
const { sourceName, filePath } = parseGitSchemaRef(id);
|
|
619
|
+
if (sourceName !== options.name) return void 0;
|
|
620
|
+
const repositoryPath = await checkout();
|
|
621
|
+
const schemaPath = resolveInside(repositoryPath, directory, filePath);
|
|
622
|
+
let content;
|
|
623
|
+
try {
|
|
624
|
+
content = await (0, import_promises.readFile)(schemaPath, "utf8");
|
|
625
|
+
} catch (error) {
|
|
626
|
+
if (isNodeFileNotFoundError(error)) {
|
|
627
|
+
const schemaSourcePath = [directory, filePath].filter(Boolean).join("/");
|
|
628
|
+
throw new Error(
|
|
629
|
+
`Git schema source "${options.name}" could not find schema file "${schemaSourcePath}" in "${options.url}" on branch "${branch}".`
|
|
630
|
+
);
|
|
631
|
+
}
|
|
632
|
+
throw error;
|
|
633
|
+
}
|
|
634
|
+
return {
|
|
635
|
+
id,
|
|
636
|
+
name: import_node_path.default.basename(filePath),
|
|
637
|
+
format: getSchemaFormat(filePath),
|
|
638
|
+
content,
|
|
639
|
+
source: {
|
|
640
|
+
provider: "git",
|
|
641
|
+
id: `${options.name}:${filePath}`,
|
|
642
|
+
url: options.url,
|
|
643
|
+
branch,
|
|
644
|
+
path: [directory, filePath].filter(Boolean).join("/")
|
|
645
|
+
}
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
});
|
|
649
|
+
};
|
|
505
650
|
// Annotate the CommonJS export names for ESM import in node:
|
|
506
651
|
0 && (module.exports = {
|
|
507
652
|
defineDirectorySource,
|
|
653
|
+
defineSchemaSource,
|
|
654
|
+
gitSchemaSource,
|
|
508
655
|
githubDirectory,
|
|
509
656
|
microsoftEntraDirectory
|
|
510
657
|
});
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/types.ts","../src/github-directory.ts","../src/microsoft-entra-directory.ts"],"sourcesContent":["export { githubDirectory } from \"./github-directory\";\nexport { microsoftEntraDirectory } from \"./microsoft-entra-directory\";\nexport { defineDirectorySource } from \"./types\";\nexport type {\n DirectoryEntrySource,\n DirectorySource,\n DirectoryTeam,\n DirectoryUser,\n} from \"./types\";\n","/**\n * Describes where a generated directory entry came from.\n *\n * EventCatalog uses the provider to show source badges and to treat generated\n * resources as externally managed.\n */\nexport type DirectoryEntrySource = {\n /** Provider identifier, for example `github`, `okta`, or `ldap`. */\n provider: string;\n /** Optional provider-specific identifier for the external resource. */\n id?: string;\n /** Optional URL back to the resource in the external provider. */\n url?: string;\n [key: string]: unknown;\n};\n\n/**\n * User returned by a directory source.\n *\n * Directory users are synced as read-only EventCatalog users and can be\n * referenced by teams through their `id`.\n */\nexport type DirectoryUser = {\n id: string;\n name: string;\n avatarUrl?: string;\n role?: string;\n email?: string;\n markdown?: string;\n source?: DirectoryEntrySource;\n [key: string]: unknown;\n};\n\n/**\n * Team returned by a directory source.\n *\n * Directory teams are synced as read-only EventCatalog teams.\n */\nexport type DirectoryTeam = {\n id: string;\n name: string;\n summary?: string;\n /** User IDs for members of this team. */\n members?: string[];\n email?: string;\n markdown?: string;\n source?: DirectoryEntrySource;\n [key: string]: unknown;\n};\n\n/**\n * Loads users and/or teams from an external directory provider.\n *\n * EventCatalog calls these loaders during content sync, writes the returned\n * entries into Astro collections, and mirrors them into the generated\n * `.eventcatalog/store/directory.json` file for SDK/editor reads.\n */\nexport type DirectorySource = {\n type: \"directory\";\n /** Stable source identifier, usually `<provider>:<account-or-org>`. */\n name: string;\n /** Load users from the external directory. */\n loadUsers?: () => Promise<DirectoryUser[]>;\n /** Load teams from the external directory. */\n loadTeams?: () => Promise<DirectoryTeam[]>;\n};\n\n/**\n * Defines a custom directory source with type inference for connector authors.\n */\nexport const defineDirectorySource = <TSource extends DirectorySource>(\n source: TSource,\n) => source;\n","import {\n defineDirectorySource,\n type DirectorySource,\n type DirectoryTeam,\n type DirectoryUser,\n} from \"./types\";\n\ntype GitHubDirectoryOptions = {\n org: string;\n token?: string;\n teams: string[];\n users?: boolean;\n baseUrl?: string;\n};\n\ntype GitHubTeam = {\n id: number;\n name: string;\n slug: string;\n description: string | null;\n html_url: string;\n};\n\ntype GitHubUser = {\n id: number;\n login: string;\n avatar_url: string;\n html_url: string;\n};\n\ntype GitHubDirectoryData = {\n teams: DirectoryTeam[];\n users: DirectoryUser[];\n};\n\nconst GITHUB_API_VERSION = \"2022-11-28\";\n\nexport const githubDirectory = (\n options: GitHubDirectoryOptions,\n): DirectorySource => {\n if (!Array.isArray(options.teams) || options.teams.length === 0) {\n throw new Error(\n 'GitHub directory connector requires at least one team slug. Set teams: [\"platform\"].',\n );\n }\n\n let cachedData: Promise<GitHubDirectoryData> | undefined;\n\n const load = () => {\n cachedData = cachedData ?? loadGitHubDirectory(options);\n return cachedData;\n };\n\n return defineDirectorySource({\n type: \"directory\",\n name: `github:${options.org}`,\n loadTeams: async () => {\n const data = await load();\n return data.teams;\n },\n loadUsers: async () => {\n const data = await load();\n return data.users;\n },\n });\n};\n\nconst loadGitHubDirectory = async (\n options: GitHubDirectoryOptions,\n): Promise<GitHubDirectoryData> => {\n const teams = await loadGitHubTeams(options);\n const shouldLoadUsers = options.users !== false;\n\n if (!shouldLoadUsers) {\n return { teams, users: [] };\n }\n\n const usersById = new Map<string, DirectoryUser>();\n const teamsWithMembers: DirectoryTeam[] = [];\n\n for (const team of teams) {\n const members = await loadGitHubTeamMembers(options, team.id);\n\n for (const member of members) {\n usersById.set(member.id, member);\n }\n\n teamsWithMembers.push({\n ...team,\n members: members.map((member) => member.id),\n });\n }\n\n return {\n teams: teamsWithMembers,\n users: Array.from(usersById.values()),\n };\n};\n\nconst loadGitHubTeams = async (\n options: GitHubDirectoryOptions,\n): Promise<DirectoryTeam[]> => {\n const teams: GitHubTeam[] = [];\n\n for (const teamSlug of options.teams) {\n teams.push(await loadGitHubTeam(options, teamSlug));\n }\n\n return teams.map((team) => ({\n id: team.slug,\n name: team.name,\n summary: team.description ?? undefined,\n markdown: createGitHubSyncedMarkdown(\"team\", team.html_url),\n source: {\n provider: \"github\",\n id: String(team.id),\n url: team.html_url,\n },\n }));\n};\n\nconst loadGitHubTeam = async (\n options: GitHubDirectoryOptions,\n teamSlug: string,\n) => {\n const response = await githubRequest<GitHubTeam>(\n options,\n buildGitHubUrl(\n options,\n `/orgs/${encodeURIComponent(options.org)}/teams/${encodeURIComponent(teamSlug)}`,\n ),\n );\n\n return response.data;\n};\n\nconst loadGitHubTeamMembers = async (\n options: GitHubDirectoryOptions,\n teamSlug: string,\n): Promise<DirectoryUser[]> => {\n const members = await paginate<GitHubUser>(\n options,\n `/orgs/${encodeURIComponent(options.org)}/teams/${encodeURIComponent(teamSlug)}/members`,\n );\n\n return members.map((member) => ({\n id: member.login,\n name: member.login,\n avatarUrl: member.avatar_url,\n markdown: createGitHubSyncedMarkdown(\"user\", member.html_url),\n source: {\n provider: \"github\",\n id: String(member.id),\n url: member.html_url,\n },\n }));\n};\n\nconst createGitHubSyncedMarkdown = (type: \"team\" | \"user\", url: string) => {\n if (type === \"team\") {\n return [\n \":::note\",\n \"This team is synced from GitHub and is read-only in EventCatalog.\",\n \"\",\n \"Manage the team and its members in GitHub.\",\n \"\",\n `[View team on GitHub](${url})`,\n \":::\",\n ].join(\"\\n\");\n }\n\n return [\n \":::note\",\n \"This user is synced from GitHub and is read-only in EventCatalog.\",\n \"\",\n \"Manage profile details and team membership in GitHub.\",\n \"\",\n `[View user on GitHub](${url})`,\n \":::\",\n ].join(\"\\n\");\n};\n\nconst paginate = async <T>(\n options: GitHubDirectoryOptions,\n pathname: string,\n): Promise<T[]> => {\n const results: T[] = [];\n let url: string | undefined = buildGitHubUrl(options, pathname);\n\n while (url) {\n const response = await githubRequest<T[]>(options, url);\n results.push(...response.data);\n url = getNextPageUrl(response.headers.get(\"link\"));\n }\n\n return results;\n};\n\nconst githubRequest = async <T>(\n options: GitHubDirectoryOptions,\n url: string,\n): Promise<{ data: T; headers: Headers }> => {\n const response = await fetch(url, {\n headers: {\n accept: \"application/vnd.github+json\",\n \"x-github-api-version\": GITHUB_API_VERSION,\n \"user-agent\": \"@eventcatalog/connectors\",\n ...(options.token ? { authorization: `Bearer ${options.token}` } : {}),\n },\n });\n\n if (!response.ok) {\n const message = await response.text();\n throw new Error(\n `GitHub directory connector failed (${response.status} ${response.statusText}): ${message}`,\n );\n }\n\n return {\n data: (await response.json()) as T,\n headers: response.headers,\n };\n};\n\nconst buildGitHubUrl = (options: GitHubDirectoryOptions, pathname: string) => {\n const baseUrl = options.baseUrl ?? \"https://api.github.com\";\n const normalizedBaseUrl = baseUrl.endsWith(\"/\") ? baseUrl : `${baseUrl}/`;\n const normalizedPathname = pathname.replace(/^\\//u, \"\");\n const url = new URL(normalizedPathname, normalizedBaseUrl);\n url.searchParams.set(\"per_page\", \"100\");\n return url.toString();\n};\n\nconst getNextPageUrl = (linkHeader: string | null) => {\n if (!linkHeader) return undefined;\n\n const nextLink = linkHeader\n .split(\",\")\n .find((link) => link.includes('rel=\"next\"'));\n const match = nextLink?.match(/<([^>]+)>/);\n\n return match?.[1];\n};\n","import {\n defineDirectorySource,\n type DirectorySource,\n type DirectoryTeam,\n type DirectoryUser,\n} from \"./types\";\n\ntype MicrosoftEntraGroupInput =\n | string\n | {\n id: string;\n alias?: string;\n }\n | {\n displayName: string;\n alias?: string;\n };\n\ntype MicrosoftEntraDirectoryOptions = {\n tenantId: string;\n clientId: string;\n clientSecret: string;\n groups: MicrosoftEntraGroupInput[];\n users?: boolean;\n includeDisabledUsers?: boolean;\n graphBaseUrl?: string;\n tokenUrl?: string;\n};\n\ntype MicrosoftEntraGroup = {\n id: string;\n displayName?: string | null;\n description?: string | null;\n mail?: string | null;\n};\n\ntype MicrosoftEntraUser = {\n id: string;\n displayName?: string | null;\n userPrincipalName?: string | null;\n mail?: string | null;\n jobTitle?: string | null;\n accountEnabled?: boolean | null;\n};\n\ntype MicrosoftEntraDirectoryData = {\n teams: DirectoryTeam[];\n users: DirectoryUser[];\n};\n\ntype MicrosoftGraphCollection<T> = {\n value?: T[];\n \"@odata.nextLink\"?: string;\n};\n\ntype ResolvedGroupInput = {\n group: MicrosoftEntraGroup;\n alias?: string;\n};\n\nconst DEFAULT_GRAPH_BASE_URL = \"https://graph.microsoft.com/v1.0\";\nconst GRAPH_RETRY_ATTEMPTS = 3;\nconst GRAPH_SELECT_GROUP = \"id,displayName,description,mail\";\nconst GRAPH_SELECT_USER =\n \"id,displayName,userPrincipalName,mail,jobTitle,accountEnabled\";\n\nexport const microsoftEntraDirectory = (\n options: MicrosoftEntraDirectoryOptions,\n): DirectorySource => {\n validateOptions(options);\n\n let cachedData: Promise<MicrosoftEntraDirectoryData> | undefined;\n\n const load = () => {\n cachedData = cachedData ?? loadMicrosoftEntraDirectory(options);\n return cachedData;\n };\n\n return defineDirectorySource({\n type: \"directory\",\n name: `microsoft-entra:${options.tenantId}`,\n loadTeams: async () => {\n const data = await load();\n return data.teams;\n },\n loadUsers: async () => {\n const data = await load();\n return data.users;\n },\n });\n};\n\nconst validateOptions = (options: MicrosoftEntraDirectoryOptions) => {\n if (!options.tenantId) {\n throw new Error(\"Microsoft Entra directory connector requires tenantId.\");\n }\n if (!options.clientId) {\n throw new Error(\"Microsoft Entra directory connector requires clientId.\");\n }\n if (!options.clientSecret) {\n throw new Error(\n \"Microsoft Entra directory connector requires clientSecret.\",\n );\n }\n if (!Array.isArray(options.groups) || options.groups.length === 0) {\n throw new Error(\n 'Microsoft Entra directory connector requires at least one group. Set groups: [{ id: \"00000000-0000-0000-0000-000000000000\" }].',\n );\n }\n};\n\nconst loadMicrosoftEntraDirectory = async (\n options: MicrosoftEntraDirectoryOptions,\n): Promise<MicrosoftEntraDirectoryData> => {\n const token = await getAccessToken(options);\n const resolvedGroups = await loadMicrosoftEntraGroups(options, token);\n const teams = mapMicrosoftEntraTeams(resolvedGroups);\n const shouldLoadUsers = options.users !== false;\n\n if (!shouldLoadUsers) {\n return { teams, users: [] };\n }\n\n const usersById = new Map<string, DirectoryUser>();\n const teamsWithMembers: DirectoryTeam[] = [];\n\n for (const team of teams) {\n const members = await loadMicrosoftEntraGroupMembers(\n options,\n token,\n String(team.source?.id ?? team.id),\n );\n\n for (const member of members) {\n usersById.set(member.id, member);\n }\n\n teamsWithMembers.push({\n ...team,\n members: members.map((member) => member.id),\n });\n }\n\n return {\n teams: teamsWithMembers,\n users: Array.from(usersById.values()),\n };\n};\n\nconst loadMicrosoftEntraGroups = async (\n options: MicrosoftEntraDirectoryOptions,\n token: string,\n): Promise<ResolvedGroupInput[]> => {\n const resolvedGroups: ResolvedGroupInput[] = [];\n\n for (const groupInput of options.groups) {\n resolvedGroups.push(\n await resolveMicrosoftEntraGroup(options, token, groupInput),\n );\n }\n\n assertUniqueTeamIds(resolvedGroups);\n\n return resolvedGroups;\n};\n\nconst resolveMicrosoftEntraGroup = async (\n options: MicrosoftEntraDirectoryOptions,\n token: string,\n groupInput: MicrosoftEntraGroupInput,\n): Promise<ResolvedGroupInput> => {\n if (typeof groupInput === \"string\") {\n return {\n group: await loadMicrosoftEntraGroupById(options, token, groupInput),\n };\n }\n\n if (\"id\" in groupInput) {\n return {\n group: await loadMicrosoftEntraGroupById(options, token, groupInput.id),\n alias: groupInput.alias,\n };\n }\n\n return {\n group: await loadMicrosoftEntraGroupByDisplayName(\n options,\n token,\n groupInput.displayName,\n ),\n alias: groupInput.alias,\n };\n};\n\nconst loadMicrosoftEntraGroupById = async (\n options: MicrosoftEntraDirectoryOptions,\n token: string,\n groupId: string,\n) => {\n return microsoftGraphRequest<MicrosoftEntraGroup>(\n options,\n token,\n buildMicrosoftGraphUrl(options, `/groups/${encodeURIComponent(groupId)}`, {\n $select: GRAPH_SELECT_GROUP,\n }),\n );\n};\n\nconst loadMicrosoftEntraGroupByDisplayName = async (\n options: MicrosoftEntraDirectoryOptions,\n token: string,\n displayName: string,\n) => {\n const groups = await paginate<MicrosoftEntraGroup>(\n options,\n token,\n buildMicrosoftGraphUrl(options, \"/groups\", {\n $filter: `displayName eq '${escapeODataString(displayName)}'`,\n $select: GRAPH_SELECT_GROUP,\n }),\n );\n\n if (groups.length === 0) {\n throw new Error(\n `Microsoft Entra directory connector could not find a group with displayName \"${displayName}\". Use the group id instead.`,\n );\n }\n\n if (groups.length > 1) {\n throw new Error(\n `Microsoft Entra directory connector found ${groups.length} groups with displayName \"${displayName}\". Use the group id instead.`,\n );\n }\n\n return groups[0];\n};\n\nconst mapMicrosoftEntraTeams = (groups: ResolvedGroupInput[]) => {\n return groups.map(({ group, alias }) => {\n const displayName = group.displayName ?? group.id;\n\n return {\n id: alias ?? slugify(displayName),\n name: displayName,\n summary: group.description ?? undefined,\n email: group.mail ?? undefined,\n markdown: createMicrosoftEntraSyncedMarkdown(\"team\"),\n source: {\n provider: \"microsoft-entra\",\n id: group.id,\n },\n };\n });\n};\n\nconst loadMicrosoftEntraGroupMembers = async (\n options: MicrosoftEntraDirectoryOptions,\n token: string,\n groupId: string,\n): Promise<DirectoryUser[]> => {\n const users = await paginate<MicrosoftEntraUser>(\n options,\n token,\n buildMicrosoftGraphUrl(\n options,\n `/groups/${encodeURIComponent(groupId)}/members/microsoft.graph.user`,\n {\n $select: GRAPH_SELECT_USER,\n },\n ),\n );\n\n return users\n .filter(\n (user) => options.includeDisabledUsers || user.accountEnabled !== false,\n )\n .map(mapMicrosoftEntraUser);\n};\n\nconst mapMicrosoftEntraUser = (user: MicrosoftEntraUser): DirectoryUser => {\n const userPrincipalName = user.userPrincipalName ?? undefined;\n const email = user.mail ?? userPrincipalName;\n const id = getMicrosoftEntraUserId(user);\n const name = user.displayName ?? userPrincipalName ?? user.id;\n\n return {\n id,\n name,\n email,\n role: user.jobTitle ?? undefined,\n markdown: createMicrosoftEntraSyncedMarkdown(\"user\"),\n source: {\n provider: \"microsoft-entra\",\n id: user.id,\n },\n };\n};\n\nconst getMicrosoftEntraUserId = (user: MicrosoftEntraUser) => {\n if (user.mail) return user.mail.toLowerCase();\n if (user.userPrincipalName)\n return normalizeMicrosoftEntraUserPrincipalName(user.userPrincipalName);\n return user.id;\n};\n\nconst normalizeMicrosoftEntraUserPrincipalName = (\n userPrincipalName: string,\n) => {\n const guestUserMatch = userPrincipalName.match(/^(.+)#EXT#@.+$/i);\n\n if (!guestUserMatch) return userPrincipalName.toLowerCase();\n\n const externalUserName = guestUserMatch[1];\n const lastUnderscoreIndex = externalUserName.lastIndexOf(\"_\");\n\n if (lastUnderscoreIndex === -1) return externalUserName.toLowerCase();\n\n return `${externalUserName.slice(0, lastUnderscoreIndex)}@${externalUserName.slice(lastUnderscoreIndex + 1)}`.toLowerCase();\n};\n\nconst createMicrosoftEntraSyncedMarkdown = (type: \"team\" | \"user\") => {\n if (type === \"team\") {\n return [\n \":::note\",\n \"This team is synced from Microsoft Entra ID and is read-only in EventCatalog.\",\n \"\",\n \"Manage the team and its members in Microsoft Entra ID.\",\n \":::\",\n ].join(\"\\n\");\n }\n\n return [\n \":::note\",\n \"This user is synced from Microsoft Entra ID and is read-only in EventCatalog.\",\n \"\",\n \"Manage profile details and team membership in Microsoft Entra ID.\",\n \":::\",\n ].join(\"\\n\");\n};\n\nconst getAccessToken = async (options: MicrosoftEntraDirectoryOptions) => {\n const tokenUrl =\n options.tokenUrl ??\n `https://login.microsoftonline.com/${encodeURIComponent(options.tenantId)}/oauth2/v2.0/token`;\n const body = new URLSearchParams({\n client_id: options.clientId,\n client_secret: options.clientSecret,\n grant_type: \"client_credentials\",\n scope: getMicrosoftGraphScope(options),\n });\n\n const response = await fetch(tokenUrl, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/x-www-form-urlencoded\",\n \"user-agent\": \"@eventcatalog/connectors\",\n },\n body,\n });\n\n if (!response.ok) {\n const message = await response.text();\n throw new Error(\n `Microsoft Entra directory connector failed to get an access token (${response.status} ${response.statusText}): ${message}`,\n );\n }\n\n const data = (await response.json()) as { access_token?: string };\n if (!data.access_token) {\n throw new Error(\n \"Microsoft Entra directory connector failed to get an access token: response did not include access_token.\",\n );\n }\n\n return data.access_token;\n};\n\nconst getMicrosoftGraphScope = (options: MicrosoftEntraDirectoryOptions) => {\n const graphBaseUrl = options.graphBaseUrl ?? DEFAULT_GRAPH_BASE_URL;\n return `${new URL(graphBaseUrl).origin}/.default`;\n};\n\nconst microsoftGraphRequest = async <T>(\n options: MicrosoftEntraDirectoryOptions,\n token: string,\n url: string,\n attempt = 0,\n): Promise<T> => {\n const response = await fetch(url, {\n headers: {\n accept: \"application/json\",\n authorization: `Bearer ${token}`,\n ConsistencyLevel: \"eventual\",\n \"user-agent\": \"@eventcatalog/connectors\",\n },\n });\n\n if (response.status === 429 && attempt < GRAPH_RETRY_ATTEMPTS) {\n await waitForRetry(response.headers.get(\"retry-after\"));\n return microsoftGraphRequest(options, token, url, attempt + 1);\n }\n\n if (!response.ok) {\n const message = await response.text();\n throw new Error(\n `Microsoft Entra directory connector failed (${response.status} ${response.statusText}): ${message}`,\n );\n }\n\n return (await response.json()) as T;\n};\n\nconst paginate = async <T>(\n options: MicrosoftEntraDirectoryOptions,\n token: string,\n url: string,\n): Promise<T[]> => {\n const results: T[] = [];\n let nextUrl: string | undefined = url;\n\n while (nextUrl) {\n const response: MicrosoftGraphCollection<T> = await microsoftGraphRequest<\n MicrosoftGraphCollection<T>\n >(options, token, nextUrl);\n results.push(...(response.value ?? []));\n nextUrl = response[\"@odata.nextLink\"];\n }\n\n return results;\n};\n\nconst buildMicrosoftGraphUrl = (\n options: MicrosoftEntraDirectoryOptions,\n pathname: string,\n query?: Record<string, string>,\n) => {\n const baseUrl = options.graphBaseUrl ?? DEFAULT_GRAPH_BASE_URL;\n const normalizedBaseUrl = baseUrl.endsWith(\"/\") ? baseUrl : `${baseUrl}/`;\n const normalizedPathname = pathname.replace(/^\\//u, \"\");\n const url = new URL(normalizedPathname, normalizedBaseUrl);\n\n for (const [key, value] of Object.entries(query ?? {})) {\n url.searchParams.set(key, value);\n }\n\n return url.toString();\n};\n\nconst assertUniqueTeamIds = (groups: ResolvedGroupInput[]) => {\n const teamIds = new Map<string, string>();\n\n for (const { group, alias } of groups) {\n const displayName = group.displayName ?? group.id;\n const teamId = alias ?? slugify(displayName);\n const existingGroupId = teamIds.get(teamId);\n\n if (existingGroupId) {\n throw new Error(\n `Microsoft Entra directory connector resolved duplicate team id \"${teamId}\" from groups \"${existingGroupId}\" and \"${group.id}\". Set alias for one of the groups.`,\n );\n }\n\n teamIds.set(teamId, group.id);\n }\n};\n\nconst slugify = (value: string) => {\n const slug = value\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n\n return slug || value;\n};\n\nconst escapeODataString = (value: string) => value.replace(/'/g, \"''\");\n\nconst waitForRetry = async (retryAfter: string | null) => {\n const retryAfterSeconds = retryAfter ? Number(retryAfter) : 1;\n const delayMs = Number.isFinite(retryAfterSeconds)\n ? Math.max(retryAfterSeconds, 0) * 1000\n : 1000;\n\n await new Promise((resolve) => setTimeout(resolve, delayMs));\n};\n"],"mappings":"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","names":["paginate"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/types.ts","../src/github-directory.ts","../src/microsoft-entra-directory.ts","../src/git-schema-source.ts"],"sourcesContent":["export { githubDirectory } from \"./github-directory\";\nexport { microsoftEntraDirectory } from \"./microsoft-entra-directory\";\nexport { gitSchemaSource } from \"./git-schema-source\";\nexport { defineDirectorySource, defineSchemaSource } from \"./types\";\nexport type {\n DirectoryEntrySource,\n DirectorySource,\n DirectoryTeam,\n DirectoryUser,\n SchemaEntry,\n SchemaEntrySource,\n SchemaResolveContext,\n SchemaSource,\n} from \"./types\";\n","/**\n * Describes where a generated directory entry came from.\n *\n * EventCatalog uses the provider to show source badges and to treat generated\n * resources as externally managed.\n */\nexport type DirectoryEntrySource = {\n /** Provider identifier, for example `github`, `okta`, or `ldap`. */\n provider: string;\n /** Optional provider-specific identifier for the external resource. */\n id?: string;\n /** Optional URL back to the resource in the external provider. */\n url?: string;\n [key: string]: unknown;\n};\n\n/**\n * User returned by a directory source.\n *\n * Directory users are synced as read-only EventCatalog users and can be\n * referenced by teams through their `id`.\n */\nexport type DirectoryUser = {\n id: string;\n name: string;\n avatarUrl?: string;\n role?: string;\n email?: string;\n markdown?: string;\n source?: DirectoryEntrySource;\n [key: string]: unknown;\n};\n\n/**\n * Team returned by a directory source.\n *\n * Directory teams are synced as read-only EventCatalog teams.\n */\nexport type DirectoryTeam = {\n id: string;\n name: string;\n summary?: string;\n /** User IDs for members of this team. */\n members?: string[];\n email?: string;\n markdown?: string;\n source?: DirectoryEntrySource;\n [key: string]: unknown;\n};\n\n/**\n * Loads users and/or teams from an external directory provider.\n *\n * EventCatalog calls these loaders during content sync, writes the returned\n * entries into Astro collections, and mirrors them into the generated\n * `.eventcatalog/store/directory.json` file for SDK/editor reads.\n */\nexport type DirectorySource = {\n type: \"directory\";\n /** Stable source identifier, usually `<provider>:<account-or-org>`. */\n name: string;\n /** Load users from the external directory. */\n loadUsers?: () => Promise<DirectoryUser[]>;\n /** Load teams from the external directory. */\n loadTeams?: () => Promise<DirectoryTeam[]>;\n};\n\n/**\n * Defines a custom directory source with type inference for connector authors.\n */\nexport const defineDirectorySource = <TSource extends DirectorySource>(\n source: TSource,\n) => source;\n\n/**\n * Describes where a synced schema came from.\n */\nexport type SchemaEntrySource = {\n /** Provider identifier, for example `git`, `github`, `confluent`, or `eventbridge`. */\n provider: string;\n /** Optional provider-specific identifier for the external schema. */\n id?: string;\n /** Optional URL back to the schema source. */\n url?: string;\n /** Optional version/ref/branch for versioned sources. */\n ref?: string;\n /** Optional branch for git-backed schema sources. */\n branch?: string;\n /** Optional path inside the source. */\n path?: string;\n [key: string]: unknown;\n};\n\n/**\n * Schema returned by an external schema source.\n */\nexport type SchemaEntry = {\n id: string;\n name?: string;\n format?: string;\n content: string;\n source: SchemaEntrySource;\n};\n\nexport type SchemaResolveContext = {\n /** Absolute path to the message file that referenced the schema. */\n messageFilePath?: string;\n};\n\n/**\n * Loads schemas from an external source.\n *\n * EventCatalog calls schema sources during content sync to resolve schema\n * references from messages into generated schema collection entries.\n */\nexport type SchemaSource = {\n type: \"schemas\";\n /** Stable source identifier used in schema refs, for example `contracts`. */\n name: string;\n /** Returns true when this source can resolve the given schema ref. */\n canResolve: (ref: string) => boolean;\n /** Resolves a schema ref into schema content and source metadata. */\n resolve: (\n ref: string,\n context?: SchemaResolveContext,\n ) => Promise<SchemaEntry | undefined>;\n};\n\n/**\n * Defines a custom schema source with type inference for connector authors.\n */\nexport const defineSchemaSource = <TSource extends SchemaSource>(\n source: TSource,\n) => source;\n","import {\n defineDirectorySource,\n type DirectorySource,\n type DirectoryTeam,\n type DirectoryUser,\n} from \"./types\";\n\ntype GitHubDirectoryOptions = {\n org: string;\n token?: string;\n teams: string[];\n users?: boolean;\n baseUrl?: string;\n};\n\ntype GitHubTeam = {\n id: number;\n name: string;\n slug: string;\n description: string | null;\n html_url: string;\n};\n\ntype GitHubUser = {\n id: number;\n login: string;\n avatar_url: string;\n html_url: string;\n};\n\ntype GitHubDirectoryData = {\n teams: DirectoryTeam[];\n users: DirectoryUser[];\n};\n\nconst GITHUB_API_VERSION = \"2022-11-28\";\n\nexport const githubDirectory = (\n options: GitHubDirectoryOptions,\n): DirectorySource => {\n if (!Array.isArray(options.teams) || options.teams.length === 0) {\n throw new Error(\n 'GitHub directory connector requires at least one team slug. Set teams: [\"platform\"].',\n );\n }\n\n let cachedData: Promise<GitHubDirectoryData> | undefined;\n\n const load = () => {\n cachedData = cachedData ?? loadGitHubDirectory(options);\n return cachedData;\n };\n\n return defineDirectorySource({\n type: \"directory\",\n name: `github:${options.org}`,\n loadTeams: async () => {\n const data = await load();\n return data.teams;\n },\n loadUsers: async () => {\n const data = await load();\n return data.users;\n },\n });\n};\n\nconst loadGitHubDirectory = async (\n options: GitHubDirectoryOptions,\n): Promise<GitHubDirectoryData> => {\n const teams = await loadGitHubTeams(options);\n const shouldLoadUsers = options.users !== false;\n\n if (!shouldLoadUsers) {\n return { teams, users: [] };\n }\n\n const usersById = new Map<string, DirectoryUser>();\n const teamsWithMembers: DirectoryTeam[] = [];\n\n for (const team of teams) {\n const members = await loadGitHubTeamMembers(options, team.id);\n\n for (const member of members) {\n usersById.set(member.id, member);\n }\n\n teamsWithMembers.push({\n ...team,\n members: members.map((member) => member.id),\n });\n }\n\n return {\n teams: teamsWithMembers,\n users: Array.from(usersById.values()),\n };\n};\n\nconst loadGitHubTeams = async (\n options: GitHubDirectoryOptions,\n): Promise<DirectoryTeam[]> => {\n const teams: GitHubTeam[] = [];\n\n for (const teamSlug of options.teams) {\n teams.push(await loadGitHubTeam(options, teamSlug));\n }\n\n return teams.map((team) => ({\n id: team.slug,\n name: team.name,\n summary: team.description ?? undefined,\n markdown: createGitHubSyncedMarkdown(\"team\", team.html_url),\n source: {\n provider: \"github\",\n id: String(team.id),\n url: team.html_url,\n },\n }));\n};\n\nconst loadGitHubTeam = async (\n options: GitHubDirectoryOptions,\n teamSlug: string,\n) => {\n const response = await githubRequest<GitHubTeam>(\n options,\n buildGitHubUrl(\n options,\n `/orgs/${encodeURIComponent(options.org)}/teams/${encodeURIComponent(teamSlug)}`,\n ),\n );\n\n return response.data;\n};\n\nconst loadGitHubTeamMembers = async (\n options: GitHubDirectoryOptions,\n teamSlug: string,\n): Promise<DirectoryUser[]> => {\n const members = await paginate<GitHubUser>(\n options,\n `/orgs/${encodeURIComponent(options.org)}/teams/${encodeURIComponent(teamSlug)}/members`,\n );\n\n return members.map((member) => ({\n id: member.login,\n name: member.login,\n avatarUrl: member.avatar_url,\n markdown: createGitHubSyncedMarkdown(\"user\", member.html_url),\n source: {\n provider: \"github\",\n id: String(member.id),\n url: member.html_url,\n },\n }));\n};\n\nconst createGitHubSyncedMarkdown = (type: \"team\" | \"user\", url: string) => {\n if (type === \"team\") {\n return [\n \":::note\",\n \"This team is synced from GitHub and is read-only in EventCatalog.\",\n \"\",\n \"Manage the team and its members in GitHub.\",\n \"\",\n `[View team on GitHub](${url})`,\n \":::\",\n ].join(\"\\n\");\n }\n\n return [\n \":::note\",\n \"This user is synced from GitHub and is read-only in EventCatalog.\",\n \"\",\n \"Manage profile details and team membership in GitHub.\",\n \"\",\n `[View user on GitHub](${url})`,\n \":::\",\n ].join(\"\\n\");\n};\n\nconst paginate = async <T>(\n options: GitHubDirectoryOptions,\n pathname: string,\n): Promise<T[]> => {\n const results: T[] = [];\n let url: string | undefined = buildGitHubUrl(options, pathname);\n\n while (url) {\n const response = await githubRequest<T[]>(options, url);\n results.push(...response.data);\n url = getNextPageUrl(response.headers.get(\"link\"));\n }\n\n return results;\n};\n\nconst githubRequest = async <T>(\n options: GitHubDirectoryOptions,\n url: string,\n): Promise<{ data: T; headers: Headers }> => {\n const response = await fetch(url, {\n headers: {\n accept: \"application/vnd.github+json\",\n \"x-github-api-version\": GITHUB_API_VERSION,\n \"user-agent\": \"@eventcatalog/connectors\",\n ...(options.token ? { authorization: `Bearer ${options.token}` } : {}),\n },\n });\n\n if (!response.ok) {\n const message = await response.text();\n throw new Error(\n `GitHub directory connector failed (${response.status} ${response.statusText}): ${message}`,\n );\n }\n\n return {\n data: (await response.json()) as T,\n headers: response.headers,\n };\n};\n\nconst buildGitHubUrl = (options: GitHubDirectoryOptions, pathname: string) => {\n const baseUrl = options.baseUrl ?? \"https://api.github.com\";\n const normalizedBaseUrl = baseUrl.endsWith(\"/\") ? baseUrl : `${baseUrl}/`;\n const normalizedPathname = pathname.replace(/^\\//u, \"\");\n const url = new URL(normalizedPathname, normalizedBaseUrl);\n url.searchParams.set(\"per_page\", \"100\");\n return url.toString();\n};\n\nconst getNextPageUrl = (linkHeader: string | null) => {\n if (!linkHeader) return undefined;\n\n const nextLink = linkHeader\n .split(\",\")\n .find((link) => link.includes('rel=\"next\"'));\n const match = nextLink?.match(/<([^>]+)>/);\n\n return match?.[1];\n};\n","import {\n defineDirectorySource,\n type DirectorySource,\n type DirectoryTeam,\n type DirectoryUser,\n} from \"./types\";\n\ntype MicrosoftEntraGroupInput =\n | string\n | {\n id: string;\n alias?: string;\n }\n | {\n displayName: string;\n alias?: string;\n };\n\ntype MicrosoftEntraDirectoryOptions = {\n tenantId: string;\n clientId: string;\n clientSecret: string;\n groups: MicrosoftEntraGroupInput[];\n users?: boolean;\n includeDisabledUsers?: boolean;\n graphBaseUrl?: string;\n tokenUrl?: string;\n};\n\ntype MicrosoftEntraGroup = {\n id: string;\n displayName?: string | null;\n description?: string | null;\n mail?: string | null;\n};\n\ntype MicrosoftEntraUser = {\n id: string;\n displayName?: string | null;\n userPrincipalName?: string | null;\n mail?: string | null;\n jobTitle?: string | null;\n accountEnabled?: boolean | null;\n};\n\ntype MicrosoftEntraDirectoryData = {\n teams: DirectoryTeam[];\n users: DirectoryUser[];\n};\n\ntype MicrosoftGraphCollection<T> = {\n value?: T[];\n \"@odata.nextLink\"?: string;\n};\n\ntype ResolvedGroupInput = {\n group: MicrosoftEntraGroup;\n alias?: string;\n};\n\nconst DEFAULT_GRAPH_BASE_URL = \"https://graph.microsoft.com/v1.0\";\nconst GRAPH_RETRY_ATTEMPTS = 3;\nconst GRAPH_SELECT_GROUP = \"id,displayName,description,mail\";\nconst GRAPH_SELECT_USER =\n \"id,displayName,userPrincipalName,mail,jobTitle,accountEnabled\";\n\nexport const microsoftEntraDirectory = (\n options: MicrosoftEntraDirectoryOptions,\n): DirectorySource => {\n validateOptions(options);\n\n let cachedData: Promise<MicrosoftEntraDirectoryData> | undefined;\n\n const load = () => {\n cachedData = cachedData ?? loadMicrosoftEntraDirectory(options);\n return cachedData;\n };\n\n return defineDirectorySource({\n type: \"directory\",\n name: `microsoft-entra:${options.tenantId}`,\n loadTeams: async () => {\n const data = await load();\n return data.teams;\n },\n loadUsers: async () => {\n const data = await load();\n return data.users;\n },\n });\n};\n\nconst validateOptions = (options: MicrosoftEntraDirectoryOptions) => {\n if (!options.tenantId) {\n throw new Error(\"Microsoft Entra directory connector requires tenantId.\");\n }\n if (!options.clientId) {\n throw new Error(\"Microsoft Entra directory connector requires clientId.\");\n }\n if (!options.clientSecret) {\n throw new Error(\n \"Microsoft Entra directory connector requires clientSecret.\",\n );\n }\n if (!Array.isArray(options.groups) || options.groups.length === 0) {\n throw new Error(\n 'Microsoft Entra directory connector requires at least one group. Set groups: [{ id: \"00000000-0000-0000-0000-000000000000\" }].',\n );\n }\n};\n\nconst loadMicrosoftEntraDirectory = async (\n options: MicrosoftEntraDirectoryOptions,\n): Promise<MicrosoftEntraDirectoryData> => {\n const token = await getAccessToken(options);\n const resolvedGroups = await loadMicrosoftEntraGroups(options, token);\n const teams = mapMicrosoftEntraTeams(resolvedGroups);\n const shouldLoadUsers = options.users !== false;\n\n if (!shouldLoadUsers) {\n return { teams, users: [] };\n }\n\n const usersById = new Map<string, DirectoryUser>();\n const teamsWithMembers: DirectoryTeam[] = [];\n\n for (const team of teams) {\n const members = await loadMicrosoftEntraGroupMembers(\n options,\n token,\n String(team.source?.id ?? team.id),\n );\n\n for (const member of members) {\n usersById.set(member.id, member);\n }\n\n teamsWithMembers.push({\n ...team,\n members: members.map((member) => member.id),\n });\n }\n\n return {\n teams: teamsWithMembers,\n users: Array.from(usersById.values()),\n };\n};\n\nconst loadMicrosoftEntraGroups = async (\n options: MicrosoftEntraDirectoryOptions,\n token: string,\n): Promise<ResolvedGroupInput[]> => {\n const resolvedGroups: ResolvedGroupInput[] = [];\n\n for (const groupInput of options.groups) {\n resolvedGroups.push(\n await resolveMicrosoftEntraGroup(options, token, groupInput),\n );\n }\n\n assertUniqueTeamIds(resolvedGroups);\n\n return resolvedGroups;\n};\n\nconst resolveMicrosoftEntraGroup = async (\n options: MicrosoftEntraDirectoryOptions,\n token: string,\n groupInput: MicrosoftEntraGroupInput,\n): Promise<ResolvedGroupInput> => {\n if (typeof groupInput === \"string\") {\n return {\n group: await loadMicrosoftEntraGroupById(options, token, groupInput),\n };\n }\n\n if (\"id\" in groupInput) {\n return {\n group: await loadMicrosoftEntraGroupById(options, token, groupInput.id),\n alias: groupInput.alias,\n };\n }\n\n return {\n group: await loadMicrosoftEntraGroupByDisplayName(\n options,\n token,\n groupInput.displayName,\n ),\n alias: groupInput.alias,\n };\n};\n\nconst loadMicrosoftEntraGroupById = async (\n options: MicrosoftEntraDirectoryOptions,\n token: string,\n groupId: string,\n) => {\n return microsoftGraphRequest<MicrosoftEntraGroup>(\n options,\n token,\n buildMicrosoftGraphUrl(options, `/groups/${encodeURIComponent(groupId)}`, {\n $select: GRAPH_SELECT_GROUP,\n }),\n );\n};\n\nconst loadMicrosoftEntraGroupByDisplayName = async (\n options: MicrosoftEntraDirectoryOptions,\n token: string,\n displayName: string,\n) => {\n const groups = await paginate<MicrosoftEntraGroup>(\n options,\n token,\n buildMicrosoftGraphUrl(options, \"/groups\", {\n $filter: `displayName eq '${escapeODataString(displayName)}'`,\n $select: GRAPH_SELECT_GROUP,\n }),\n );\n\n if (groups.length === 0) {\n throw new Error(\n `Microsoft Entra directory connector could not find a group with displayName \"${displayName}\". Use the group id instead.`,\n );\n }\n\n if (groups.length > 1) {\n throw new Error(\n `Microsoft Entra directory connector found ${groups.length} groups with displayName \"${displayName}\". Use the group id instead.`,\n );\n }\n\n return groups[0];\n};\n\nconst mapMicrosoftEntraTeams = (groups: ResolvedGroupInput[]) => {\n return groups.map(({ group, alias }) => {\n const displayName = group.displayName ?? group.id;\n\n return {\n id: alias ?? slugify(displayName),\n name: displayName,\n summary: group.description ?? undefined,\n email: group.mail ?? undefined,\n markdown: createMicrosoftEntraSyncedMarkdown(\"team\"),\n source: {\n provider: \"microsoft-entra\",\n id: group.id,\n },\n };\n });\n};\n\nconst loadMicrosoftEntraGroupMembers = async (\n options: MicrosoftEntraDirectoryOptions,\n token: string,\n groupId: string,\n): Promise<DirectoryUser[]> => {\n const users = await paginate<MicrosoftEntraUser>(\n options,\n token,\n buildMicrosoftGraphUrl(\n options,\n `/groups/${encodeURIComponent(groupId)}/members/microsoft.graph.user`,\n {\n $select: GRAPH_SELECT_USER,\n },\n ),\n );\n\n return users\n .filter(\n (user) => options.includeDisabledUsers || user.accountEnabled !== false,\n )\n .map(mapMicrosoftEntraUser);\n};\n\nconst mapMicrosoftEntraUser = (user: MicrosoftEntraUser): DirectoryUser => {\n const userPrincipalName = user.userPrincipalName ?? undefined;\n const email = user.mail ?? userPrincipalName;\n const id = getMicrosoftEntraUserId(user);\n const name = user.displayName ?? userPrincipalName ?? user.id;\n\n return {\n id,\n name,\n email,\n role: user.jobTitle ?? undefined,\n markdown: createMicrosoftEntraSyncedMarkdown(\"user\"),\n source: {\n provider: \"microsoft-entra\",\n id: user.id,\n },\n };\n};\n\nconst getMicrosoftEntraUserId = (user: MicrosoftEntraUser) => {\n if (user.mail) return user.mail.toLowerCase();\n if (user.userPrincipalName)\n return normalizeMicrosoftEntraUserPrincipalName(user.userPrincipalName);\n return user.id;\n};\n\nconst normalizeMicrosoftEntraUserPrincipalName = (\n userPrincipalName: string,\n) => {\n const guestUserMatch = userPrincipalName.match(/^(.+)#EXT#@.+$/i);\n\n if (!guestUserMatch) return userPrincipalName.toLowerCase();\n\n const externalUserName = guestUserMatch[1];\n const lastUnderscoreIndex = externalUserName.lastIndexOf(\"_\");\n\n if (lastUnderscoreIndex === -1) return externalUserName.toLowerCase();\n\n return `${externalUserName.slice(0, lastUnderscoreIndex)}@${externalUserName.slice(lastUnderscoreIndex + 1)}`.toLowerCase();\n};\n\nconst createMicrosoftEntraSyncedMarkdown = (type: \"team\" | \"user\") => {\n if (type === \"team\") {\n return [\n \":::note\",\n \"This team is synced from Microsoft Entra ID and is read-only in EventCatalog.\",\n \"\",\n \"Manage the team and its members in Microsoft Entra ID.\",\n \":::\",\n ].join(\"\\n\");\n }\n\n return [\n \":::note\",\n \"This user is synced from Microsoft Entra ID and is read-only in EventCatalog.\",\n \"\",\n \"Manage profile details and team membership in Microsoft Entra ID.\",\n \":::\",\n ].join(\"\\n\");\n};\n\nconst getAccessToken = async (options: MicrosoftEntraDirectoryOptions) => {\n const tokenUrl =\n options.tokenUrl ??\n `https://login.microsoftonline.com/${encodeURIComponent(options.tenantId)}/oauth2/v2.0/token`;\n const body = new URLSearchParams({\n client_id: options.clientId,\n client_secret: options.clientSecret,\n grant_type: \"client_credentials\",\n scope: getMicrosoftGraphScope(options),\n });\n\n const response = await fetch(tokenUrl, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/x-www-form-urlencoded\",\n \"user-agent\": \"@eventcatalog/connectors\",\n },\n body,\n });\n\n if (!response.ok) {\n const message = await response.text();\n throw new Error(\n `Microsoft Entra directory connector failed to get an access token (${response.status} ${response.statusText}): ${message}`,\n );\n }\n\n const data = (await response.json()) as { access_token?: string };\n if (!data.access_token) {\n throw new Error(\n \"Microsoft Entra directory connector failed to get an access token: response did not include access_token.\",\n );\n }\n\n return data.access_token;\n};\n\nconst getMicrosoftGraphScope = (options: MicrosoftEntraDirectoryOptions) => {\n const graphBaseUrl = options.graphBaseUrl ?? DEFAULT_GRAPH_BASE_URL;\n return `${new URL(graphBaseUrl).origin}/.default`;\n};\n\nconst microsoftGraphRequest = async <T>(\n options: MicrosoftEntraDirectoryOptions,\n token: string,\n url: string,\n attempt = 0,\n): Promise<T> => {\n const response = await fetch(url, {\n headers: {\n accept: \"application/json\",\n authorization: `Bearer ${token}`,\n ConsistencyLevel: \"eventual\",\n \"user-agent\": \"@eventcatalog/connectors\",\n },\n });\n\n if (response.status === 429 && attempt < GRAPH_RETRY_ATTEMPTS) {\n await waitForRetry(response.headers.get(\"retry-after\"));\n return microsoftGraphRequest(options, token, url, attempt + 1);\n }\n\n if (!response.ok) {\n const message = await response.text();\n throw new Error(\n `Microsoft Entra directory connector failed (${response.status} ${response.statusText}): ${message}`,\n );\n }\n\n return (await response.json()) as T;\n};\n\nconst paginate = async <T>(\n options: MicrosoftEntraDirectoryOptions,\n token: string,\n url: string,\n): Promise<T[]> => {\n const results: T[] = [];\n let nextUrl: string | undefined = url;\n\n while (nextUrl) {\n const response: MicrosoftGraphCollection<T> = await microsoftGraphRequest<\n MicrosoftGraphCollection<T>\n >(options, token, nextUrl);\n results.push(...(response.value ?? []));\n nextUrl = response[\"@odata.nextLink\"];\n }\n\n return results;\n};\n\nconst buildMicrosoftGraphUrl = (\n options: MicrosoftEntraDirectoryOptions,\n pathname: string,\n query?: Record<string, string>,\n) => {\n const baseUrl = options.graphBaseUrl ?? DEFAULT_GRAPH_BASE_URL;\n const normalizedBaseUrl = baseUrl.endsWith(\"/\") ? baseUrl : `${baseUrl}/`;\n const normalizedPathname = pathname.replace(/^\\//u, \"\");\n const url = new URL(normalizedPathname, normalizedBaseUrl);\n\n for (const [key, value] of Object.entries(query ?? {})) {\n url.searchParams.set(key, value);\n }\n\n return url.toString();\n};\n\nconst assertUniqueTeamIds = (groups: ResolvedGroupInput[]) => {\n const teamIds = new Map<string, string>();\n\n for (const { group, alias } of groups) {\n const displayName = group.displayName ?? group.id;\n const teamId = alias ?? slugify(displayName);\n const existingGroupId = teamIds.get(teamId);\n\n if (existingGroupId) {\n throw new Error(\n `Microsoft Entra directory connector resolved duplicate team id \"${teamId}\" from groups \"${existingGroupId}\" and \"${group.id}\". Set alias for one of the groups.`,\n );\n }\n\n teamIds.set(teamId, group.id);\n }\n};\n\nconst slugify = (value: string) => {\n const slug = value\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n\n return slug || value;\n};\n\nconst escapeODataString = (value: string) => value.replace(/'/g, \"''\");\n\nconst waitForRetry = async (retryAfter: string | null) => {\n const retryAfterSeconds = retryAfter ? Number(retryAfter) : 1;\n const delayMs = Number.isFinite(retryAfterSeconds)\n ? Math.max(retryAfterSeconds, 0) * 1000\n : 1000;\n\n await new Promise((resolve) => setTimeout(resolve, delayMs));\n};\n","import { mkdtemp, readFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport path from \"node:path\";\nimport simpleGit from \"simple-git\";\nimport {\n defineSchemaSource,\n type SchemaEntry,\n type SchemaSource,\n} from \"./types\";\n\ntype GitSchemaSourceOptions = {\n /**\n * Stable source name used in schema refs, for example:\n * `git://contracts/events/OrderPlaced.schema.json`.\n */\n name: string;\n /** Git repository URL. Supports any URL your local git can clone. */\n url: string;\n /** Branch to clone. */\n branch?: string;\n /** Optional directory inside the repository that contains schemas. */\n directory?: string;\n /** Optional HTTPS token. SSH auth is handled by the local git environment. */\n token?: string;\n};\n\nconst getSchemaFormat = (filePath: string) => {\n const extension = path.extname(filePath).replace(\".\", \"\").toLowerCase();\n\n if (extension === \"avsc\" || extension === \"avro\") return \"avro\";\n if (extension === \"proto\") return \"protobuf\";\n if (extension === \"json\") return \"jsonschema\";\n if (extension === \"yaml\" || extension === \"yml\") return \"yaml\";\n\n return extension || \"unknown\";\n};\n\nconst parseGitSchemaRef = (ref: string) => {\n let url: URL;\n\n try {\n url = new URL(ref);\n } catch {\n throw new Error(\n `Invalid git schema ref \"${ref}\". Expected git://<source-name>/<path>.`,\n );\n }\n\n if (url.protocol !== \"git:\") {\n throw new Error(\n `Invalid git schema ref \"${ref}\". Expected git://<source-name>/<path>.`,\n );\n }\n\n const filePath = decodeURIComponent(url.pathname.replace(/^\\//, \"\"));\n\n if (!url.hostname || !filePath) {\n throw new Error(\n `Invalid git schema ref \"${ref}\". Expected git://<source-name>/<path>.`,\n );\n }\n\n if (filePath.split(/[\\\\/]/).includes(\"..\")) {\n throw new Error(\n `Schema path \"${filePath}\" resolves outside the git schema source directory.`,\n );\n }\n\n return {\n sourceName: url.hostname,\n filePath,\n };\n};\n\nconst addTokenToHttpsUrl = (url: string, token?: string) => {\n if (!token || !url.startsWith(\"https://\")) return url;\n\n const parsed = new URL(url);\n parsed.username = token;\n parsed.password = \"x-oauth-basic\";\n return parsed.toString();\n};\n\nconst resolveInside = (basePath: string, ...segments: string[]) => {\n const base = path.resolve(basePath);\n const resolved = path.resolve(base, ...segments);\n\n if (resolved !== base && !resolved.startsWith(`${base}${path.sep}`)) {\n throw new Error(\n `Schema path \"${segments.join(\"/\")}\" resolves outside the git schema source directory.`,\n );\n }\n\n return resolved;\n};\n\nconst isNodeFileNotFoundError = (\n error: unknown,\n): error is NodeJS.ErrnoException => {\n return error instanceof Error && \"code\" in error && error.code === \"ENOENT\";\n};\n\nexport const gitSchemaSource = (\n options: GitSchemaSourceOptions,\n): SchemaSource => {\n if (!options.name) {\n throw new Error(\"Git schema source requires a name.\");\n }\n\n if (!options.url) {\n throw new Error(\"Git schema source requires a repository url.\");\n }\n\n const branch = options.branch ?? \"main\";\n const directory = options.directory ?? \"\";\n let checkoutPath: Promise<string> | undefined;\n\n const checkout = async () => {\n checkoutPath =\n checkoutPath ??\n (async () => {\n const target = await mkdtemp(\n path.join(tmpdir(), \"eventcatalog-schema-git-\"),\n );\n await simpleGit().clone(\n addTokenToHttpsUrl(options.url, options.token),\n target,\n {\n \"--branch\": branch,\n \"--depth\": 1,\n \"--single-branch\": null,\n },\n );\n return target;\n })();\n\n return checkoutPath;\n };\n\n return defineSchemaSource({\n type: \"schemas\",\n name: options.name,\n canResolve: (id) => {\n if (!id.startsWith(\"git://\")) return false;\n return parseGitSchemaRef(id).sourceName === options.name;\n },\n resolve: async (id): Promise<SchemaEntry | undefined> => {\n const { sourceName, filePath } = parseGitSchemaRef(id);\n if (sourceName !== options.name) return undefined;\n\n const repositoryPath = await checkout();\n const schemaPath = resolveInside(repositoryPath, directory, filePath);\n let content: string;\n\n try {\n content = await readFile(schemaPath, \"utf8\");\n } catch (error) {\n if (isNodeFileNotFoundError(error)) {\n const schemaSourcePath = [directory, filePath]\n .filter(Boolean)\n .join(\"/\");\n throw new Error(\n `Git schema source \"${options.name}\" could not find schema file \"${schemaSourcePath}\" in \"${options.url}\" on branch \"${branch}\".`,\n );\n }\n\n throw error;\n }\n\n return {\n id,\n name: path.basename(filePath),\n format: getSchemaFormat(filePath),\n content,\n source: {\n provider: \"git\",\n id: `${options.name}:${filePath}`,\n url: options.url,\n branch,\n path: [directory, filePath].filter(Boolean).join(\"/\"),\n },\n };\n },\n });\n};\n"],"mappings":"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","names":["paginate","path","simpleGit"]}
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// src/types.ts
|
|
2
2
|
var defineDirectorySource = (source) => source;
|
|
3
|
+
var defineSchemaSource = (source) => source;
|
|
3
4
|
|
|
4
5
|
// src/github-directory.ts
|
|
5
6
|
var GITHUB_API_VERSION = "2022-11-28";
|
|
@@ -474,8 +475,142 @@ var waitForRetry = async (retryAfter) => {
|
|
|
474
475
|
const delayMs = Number.isFinite(retryAfterSeconds) ? Math.max(retryAfterSeconds, 0) * 1e3 : 1e3;
|
|
475
476
|
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
476
477
|
};
|
|
478
|
+
|
|
479
|
+
// src/git-schema-source.ts
|
|
480
|
+
import { mkdtemp, readFile } from "fs/promises";
|
|
481
|
+
import { tmpdir } from "os";
|
|
482
|
+
import path from "path";
|
|
483
|
+
import simpleGit from "simple-git";
|
|
484
|
+
var getSchemaFormat = (filePath) => {
|
|
485
|
+
const extension = path.extname(filePath).replace(".", "").toLowerCase();
|
|
486
|
+
if (extension === "avsc" || extension === "avro") return "avro";
|
|
487
|
+
if (extension === "proto") return "protobuf";
|
|
488
|
+
if (extension === "json") return "jsonschema";
|
|
489
|
+
if (extension === "yaml" || extension === "yml") return "yaml";
|
|
490
|
+
return extension || "unknown";
|
|
491
|
+
};
|
|
492
|
+
var parseGitSchemaRef = (ref) => {
|
|
493
|
+
let url;
|
|
494
|
+
try {
|
|
495
|
+
url = new URL(ref);
|
|
496
|
+
} catch {
|
|
497
|
+
throw new Error(
|
|
498
|
+
`Invalid git schema ref "${ref}". Expected git://<source-name>/<path>.`
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
if (url.protocol !== "git:") {
|
|
502
|
+
throw new Error(
|
|
503
|
+
`Invalid git schema ref "${ref}". Expected git://<source-name>/<path>.`
|
|
504
|
+
);
|
|
505
|
+
}
|
|
506
|
+
const filePath = decodeURIComponent(url.pathname.replace(/^\//, ""));
|
|
507
|
+
if (!url.hostname || !filePath) {
|
|
508
|
+
throw new Error(
|
|
509
|
+
`Invalid git schema ref "${ref}". Expected git://<source-name>/<path>.`
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
if (filePath.split(/[\\/]/).includes("..")) {
|
|
513
|
+
throw new Error(
|
|
514
|
+
`Schema path "${filePath}" resolves outside the git schema source directory.`
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
return {
|
|
518
|
+
sourceName: url.hostname,
|
|
519
|
+
filePath
|
|
520
|
+
};
|
|
521
|
+
};
|
|
522
|
+
var addTokenToHttpsUrl = (url, token) => {
|
|
523
|
+
if (!token || !url.startsWith("https://")) return url;
|
|
524
|
+
const parsed = new URL(url);
|
|
525
|
+
parsed.username = token;
|
|
526
|
+
parsed.password = "x-oauth-basic";
|
|
527
|
+
return parsed.toString();
|
|
528
|
+
};
|
|
529
|
+
var resolveInside = (basePath, ...segments) => {
|
|
530
|
+
const base = path.resolve(basePath);
|
|
531
|
+
const resolved = path.resolve(base, ...segments);
|
|
532
|
+
if (resolved !== base && !resolved.startsWith(`${base}${path.sep}`)) {
|
|
533
|
+
throw new Error(
|
|
534
|
+
`Schema path "${segments.join("/")}" resolves outside the git schema source directory.`
|
|
535
|
+
);
|
|
536
|
+
}
|
|
537
|
+
return resolved;
|
|
538
|
+
};
|
|
539
|
+
var isNodeFileNotFoundError = (error) => {
|
|
540
|
+
return error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
541
|
+
};
|
|
542
|
+
var gitSchemaSource = (options) => {
|
|
543
|
+
if (!options.name) {
|
|
544
|
+
throw new Error("Git schema source requires a name.");
|
|
545
|
+
}
|
|
546
|
+
if (!options.url) {
|
|
547
|
+
throw new Error("Git schema source requires a repository url.");
|
|
548
|
+
}
|
|
549
|
+
const branch = options.branch ?? "main";
|
|
550
|
+
const directory = options.directory ?? "";
|
|
551
|
+
let checkoutPath;
|
|
552
|
+
const checkout = async () => {
|
|
553
|
+
checkoutPath = checkoutPath ?? (async () => {
|
|
554
|
+
const target = await mkdtemp(
|
|
555
|
+
path.join(tmpdir(), "eventcatalog-schema-git-")
|
|
556
|
+
);
|
|
557
|
+
await simpleGit().clone(
|
|
558
|
+
addTokenToHttpsUrl(options.url, options.token),
|
|
559
|
+
target,
|
|
560
|
+
{
|
|
561
|
+
"--branch": branch,
|
|
562
|
+
"--depth": 1,
|
|
563
|
+
"--single-branch": null
|
|
564
|
+
}
|
|
565
|
+
);
|
|
566
|
+
return target;
|
|
567
|
+
})();
|
|
568
|
+
return checkoutPath;
|
|
569
|
+
};
|
|
570
|
+
return defineSchemaSource({
|
|
571
|
+
type: "schemas",
|
|
572
|
+
name: options.name,
|
|
573
|
+
canResolve: (id) => {
|
|
574
|
+
if (!id.startsWith("git://")) return false;
|
|
575
|
+
return parseGitSchemaRef(id).sourceName === options.name;
|
|
576
|
+
},
|
|
577
|
+
resolve: async (id) => {
|
|
578
|
+
const { sourceName, filePath } = parseGitSchemaRef(id);
|
|
579
|
+
if (sourceName !== options.name) return void 0;
|
|
580
|
+
const repositoryPath = await checkout();
|
|
581
|
+
const schemaPath = resolveInside(repositoryPath, directory, filePath);
|
|
582
|
+
let content;
|
|
583
|
+
try {
|
|
584
|
+
content = await readFile(schemaPath, "utf8");
|
|
585
|
+
} catch (error) {
|
|
586
|
+
if (isNodeFileNotFoundError(error)) {
|
|
587
|
+
const schemaSourcePath = [directory, filePath].filter(Boolean).join("/");
|
|
588
|
+
throw new Error(
|
|
589
|
+
`Git schema source "${options.name}" could not find schema file "${schemaSourcePath}" in "${options.url}" on branch "${branch}".`
|
|
590
|
+
);
|
|
591
|
+
}
|
|
592
|
+
throw error;
|
|
593
|
+
}
|
|
594
|
+
return {
|
|
595
|
+
id,
|
|
596
|
+
name: path.basename(filePath),
|
|
597
|
+
format: getSchemaFormat(filePath),
|
|
598
|
+
content,
|
|
599
|
+
source: {
|
|
600
|
+
provider: "git",
|
|
601
|
+
id: `${options.name}:${filePath}`,
|
|
602
|
+
url: options.url,
|
|
603
|
+
branch,
|
|
604
|
+
path: [directory, filePath].filter(Boolean).join("/")
|
|
605
|
+
}
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
});
|
|
609
|
+
};
|
|
477
610
|
export {
|
|
478
611
|
defineDirectorySource,
|
|
612
|
+
defineSchemaSource,
|
|
613
|
+
gitSchemaSource,
|
|
479
614
|
githubDirectory,
|
|
480
615
|
microsoftEntraDirectory
|
|
481
616
|
};
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts","../src/github-directory.ts","../src/microsoft-entra-directory.ts"],"sourcesContent":["/**\n * Describes where a generated directory entry came from.\n *\n * EventCatalog uses the provider to show source badges and to treat generated\n * resources as externally managed.\n */\nexport type DirectoryEntrySource = {\n /** Provider identifier, for example `github`, `okta`, or `ldap`. */\n provider: string;\n /** Optional provider-specific identifier for the external resource. */\n id?: string;\n /** Optional URL back to the resource in the external provider. */\n url?: string;\n [key: string]: unknown;\n};\n\n/**\n * User returned by a directory source.\n *\n * Directory users are synced as read-only EventCatalog users and can be\n * referenced by teams through their `id`.\n */\nexport type DirectoryUser = {\n id: string;\n name: string;\n avatarUrl?: string;\n role?: string;\n email?: string;\n markdown?: string;\n source?: DirectoryEntrySource;\n [key: string]: unknown;\n};\n\n/**\n * Team returned by a directory source.\n *\n * Directory teams are synced as read-only EventCatalog teams.\n */\nexport type DirectoryTeam = {\n id: string;\n name: string;\n summary?: string;\n /** User IDs for members of this team. */\n members?: string[];\n email?: string;\n markdown?: string;\n source?: DirectoryEntrySource;\n [key: string]: unknown;\n};\n\n/**\n * Loads users and/or teams from an external directory provider.\n *\n * EventCatalog calls these loaders during content sync, writes the returned\n * entries into Astro collections, and mirrors them into the generated\n * `.eventcatalog/store/directory.json` file for SDK/editor reads.\n */\nexport type DirectorySource = {\n type: \"directory\";\n /** Stable source identifier, usually `<provider>:<account-or-org>`. */\n name: string;\n /** Load users from the external directory. */\n loadUsers?: () => Promise<DirectoryUser[]>;\n /** Load teams from the external directory. */\n loadTeams?: () => Promise<DirectoryTeam[]>;\n};\n\n/**\n * Defines a custom directory source with type inference for connector authors.\n */\nexport const defineDirectorySource = <TSource extends DirectorySource>(\n source: TSource,\n) => source;\n","import {\n defineDirectorySource,\n type DirectorySource,\n type DirectoryTeam,\n type DirectoryUser,\n} from \"./types\";\n\ntype GitHubDirectoryOptions = {\n org: string;\n token?: string;\n teams: string[];\n users?: boolean;\n baseUrl?: string;\n};\n\ntype GitHubTeam = {\n id: number;\n name: string;\n slug: string;\n description: string | null;\n html_url: string;\n};\n\ntype GitHubUser = {\n id: number;\n login: string;\n avatar_url: string;\n html_url: string;\n};\n\ntype GitHubDirectoryData = {\n teams: DirectoryTeam[];\n users: DirectoryUser[];\n};\n\nconst GITHUB_API_VERSION = \"2022-11-28\";\n\nexport const githubDirectory = (\n options: GitHubDirectoryOptions,\n): DirectorySource => {\n if (!Array.isArray(options.teams) || options.teams.length === 0) {\n throw new Error(\n 'GitHub directory connector requires at least one team slug. Set teams: [\"platform\"].',\n );\n }\n\n let cachedData: Promise<GitHubDirectoryData> | undefined;\n\n const load = () => {\n cachedData = cachedData ?? loadGitHubDirectory(options);\n return cachedData;\n };\n\n return defineDirectorySource({\n type: \"directory\",\n name: `github:${options.org}`,\n loadTeams: async () => {\n const data = await load();\n return data.teams;\n },\n loadUsers: async () => {\n const data = await load();\n return data.users;\n },\n });\n};\n\nconst loadGitHubDirectory = async (\n options: GitHubDirectoryOptions,\n): Promise<GitHubDirectoryData> => {\n const teams = await loadGitHubTeams(options);\n const shouldLoadUsers = options.users !== false;\n\n if (!shouldLoadUsers) {\n return { teams, users: [] };\n }\n\n const usersById = new Map<string, DirectoryUser>();\n const teamsWithMembers: DirectoryTeam[] = [];\n\n for (const team of teams) {\n const members = await loadGitHubTeamMembers(options, team.id);\n\n for (const member of members) {\n usersById.set(member.id, member);\n }\n\n teamsWithMembers.push({\n ...team,\n members: members.map((member) => member.id),\n });\n }\n\n return {\n teams: teamsWithMembers,\n users: Array.from(usersById.values()),\n };\n};\n\nconst loadGitHubTeams = async (\n options: GitHubDirectoryOptions,\n): Promise<DirectoryTeam[]> => {\n const teams: GitHubTeam[] = [];\n\n for (const teamSlug of options.teams) {\n teams.push(await loadGitHubTeam(options, teamSlug));\n }\n\n return teams.map((team) => ({\n id: team.slug,\n name: team.name,\n summary: team.description ?? undefined,\n markdown: createGitHubSyncedMarkdown(\"team\", team.html_url),\n source: {\n provider: \"github\",\n id: String(team.id),\n url: team.html_url,\n },\n }));\n};\n\nconst loadGitHubTeam = async (\n options: GitHubDirectoryOptions,\n teamSlug: string,\n) => {\n const response = await githubRequest<GitHubTeam>(\n options,\n buildGitHubUrl(\n options,\n `/orgs/${encodeURIComponent(options.org)}/teams/${encodeURIComponent(teamSlug)}`,\n ),\n );\n\n return response.data;\n};\n\nconst loadGitHubTeamMembers = async (\n options: GitHubDirectoryOptions,\n teamSlug: string,\n): Promise<DirectoryUser[]> => {\n const members = await paginate<GitHubUser>(\n options,\n `/orgs/${encodeURIComponent(options.org)}/teams/${encodeURIComponent(teamSlug)}/members`,\n );\n\n return members.map((member) => ({\n id: member.login,\n name: member.login,\n avatarUrl: member.avatar_url,\n markdown: createGitHubSyncedMarkdown(\"user\", member.html_url),\n source: {\n provider: \"github\",\n id: String(member.id),\n url: member.html_url,\n },\n }));\n};\n\nconst createGitHubSyncedMarkdown = (type: \"team\" | \"user\", url: string) => {\n if (type === \"team\") {\n return [\n \":::note\",\n \"This team is synced from GitHub and is read-only in EventCatalog.\",\n \"\",\n \"Manage the team and its members in GitHub.\",\n \"\",\n `[View team on GitHub](${url})`,\n \":::\",\n ].join(\"\\n\");\n }\n\n return [\n \":::note\",\n \"This user is synced from GitHub and is read-only in EventCatalog.\",\n \"\",\n \"Manage profile details and team membership in GitHub.\",\n \"\",\n `[View user on GitHub](${url})`,\n \":::\",\n ].join(\"\\n\");\n};\n\nconst paginate = async <T>(\n options: GitHubDirectoryOptions,\n pathname: string,\n): Promise<T[]> => {\n const results: T[] = [];\n let url: string | undefined = buildGitHubUrl(options, pathname);\n\n while (url) {\n const response = await githubRequest<T[]>(options, url);\n results.push(...response.data);\n url = getNextPageUrl(response.headers.get(\"link\"));\n }\n\n return results;\n};\n\nconst githubRequest = async <T>(\n options: GitHubDirectoryOptions,\n url: string,\n): Promise<{ data: T; headers: Headers }> => {\n const response = await fetch(url, {\n headers: {\n accept: \"application/vnd.github+json\",\n \"x-github-api-version\": GITHUB_API_VERSION,\n \"user-agent\": \"@eventcatalog/connectors\",\n ...(options.token ? { authorization: `Bearer ${options.token}` } : {}),\n },\n });\n\n if (!response.ok) {\n const message = await response.text();\n throw new Error(\n `GitHub directory connector failed (${response.status} ${response.statusText}): ${message}`,\n );\n }\n\n return {\n data: (await response.json()) as T,\n headers: response.headers,\n };\n};\n\nconst buildGitHubUrl = (options: GitHubDirectoryOptions, pathname: string) => {\n const baseUrl = options.baseUrl ?? \"https://api.github.com\";\n const normalizedBaseUrl = baseUrl.endsWith(\"/\") ? baseUrl : `${baseUrl}/`;\n const normalizedPathname = pathname.replace(/^\\//u, \"\");\n const url = new URL(normalizedPathname, normalizedBaseUrl);\n url.searchParams.set(\"per_page\", \"100\");\n return url.toString();\n};\n\nconst getNextPageUrl = (linkHeader: string | null) => {\n if (!linkHeader) return undefined;\n\n const nextLink = linkHeader\n .split(\",\")\n .find((link) => link.includes('rel=\"next\"'));\n const match = nextLink?.match(/<([^>]+)>/);\n\n return match?.[1];\n};\n","import {\n defineDirectorySource,\n type DirectorySource,\n type DirectoryTeam,\n type DirectoryUser,\n} from \"./types\";\n\ntype MicrosoftEntraGroupInput =\n | string\n | {\n id: string;\n alias?: string;\n }\n | {\n displayName: string;\n alias?: string;\n };\n\ntype MicrosoftEntraDirectoryOptions = {\n tenantId: string;\n clientId: string;\n clientSecret: string;\n groups: MicrosoftEntraGroupInput[];\n users?: boolean;\n includeDisabledUsers?: boolean;\n graphBaseUrl?: string;\n tokenUrl?: string;\n};\n\ntype MicrosoftEntraGroup = {\n id: string;\n displayName?: string | null;\n description?: string | null;\n mail?: string | null;\n};\n\ntype MicrosoftEntraUser = {\n id: string;\n displayName?: string | null;\n userPrincipalName?: string | null;\n mail?: string | null;\n jobTitle?: string | null;\n accountEnabled?: boolean | null;\n};\n\ntype MicrosoftEntraDirectoryData = {\n teams: DirectoryTeam[];\n users: DirectoryUser[];\n};\n\ntype MicrosoftGraphCollection<T> = {\n value?: T[];\n \"@odata.nextLink\"?: string;\n};\n\ntype ResolvedGroupInput = {\n group: MicrosoftEntraGroup;\n alias?: string;\n};\n\nconst DEFAULT_GRAPH_BASE_URL = \"https://graph.microsoft.com/v1.0\";\nconst GRAPH_RETRY_ATTEMPTS = 3;\nconst GRAPH_SELECT_GROUP = \"id,displayName,description,mail\";\nconst GRAPH_SELECT_USER =\n \"id,displayName,userPrincipalName,mail,jobTitle,accountEnabled\";\n\nexport const microsoftEntraDirectory = (\n options: MicrosoftEntraDirectoryOptions,\n): DirectorySource => {\n validateOptions(options);\n\n let cachedData: Promise<MicrosoftEntraDirectoryData> | undefined;\n\n const load = () => {\n cachedData = cachedData ?? loadMicrosoftEntraDirectory(options);\n return cachedData;\n };\n\n return defineDirectorySource({\n type: \"directory\",\n name: `microsoft-entra:${options.tenantId}`,\n loadTeams: async () => {\n const data = await load();\n return data.teams;\n },\n loadUsers: async () => {\n const data = await load();\n return data.users;\n },\n });\n};\n\nconst validateOptions = (options: MicrosoftEntraDirectoryOptions) => {\n if (!options.tenantId) {\n throw new Error(\"Microsoft Entra directory connector requires tenantId.\");\n }\n if (!options.clientId) {\n throw new Error(\"Microsoft Entra directory connector requires clientId.\");\n }\n if (!options.clientSecret) {\n throw new Error(\n \"Microsoft Entra directory connector requires clientSecret.\",\n );\n }\n if (!Array.isArray(options.groups) || options.groups.length === 0) {\n throw new Error(\n 'Microsoft Entra directory connector requires at least one group. Set groups: [{ id: \"00000000-0000-0000-0000-000000000000\" }].',\n );\n }\n};\n\nconst loadMicrosoftEntraDirectory = async (\n options: MicrosoftEntraDirectoryOptions,\n): Promise<MicrosoftEntraDirectoryData> => {\n const token = await getAccessToken(options);\n const resolvedGroups = await loadMicrosoftEntraGroups(options, token);\n const teams = mapMicrosoftEntraTeams(resolvedGroups);\n const shouldLoadUsers = options.users !== false;\n\n if (!shouldLoadUsers) {\n return { teams, users: [] };\n }\n\n const usersById = new Map<string, DirectoryUser>();\n const teamsWithMembers: DirectoryTeam[] = [];\n\n for (const team of teams) {\n const members = await loadMicrosoftEntraGroupMembers(\n options,\n token,\n String(team.source?.id ?? team.id),\n );\n\n for (const member of members) {\n usersById.set(member.id, member);\n }\n\n teamsWithMembers.push({\n ...team,\n members: members.map((member) => member.id),\n });\n }\n\n return {\n teams: teamsWithMembers,\n users: Array.from(usersById.values()),\n };\n};\n\nconst loadMicrosoftEntraGroups = async (\n options: MicrosoftEntraDirectoryOptions,\n token: string,\n): Promise<ResolvedGroupInput[]> => {\n const resolvedGroups: ResolvedGroupInput[] = [];\n\n for (const groupInput of options.groups) {\n resolvedGroups.push(\n await resolveMicrosoftEntraGroup(options, token, groupInput),\n );\n }\n\n assertUniqueTeamIds(resolvedGroups);\n\n return resolvedGroups;\n};\n\nconst resolveMicrosoftEntraGroup = async (\n options: MicrosoftEntraDirectoryOptions,\n token: string,\n groupInput: MicrosoftEntraGroupInput,\n): Promise<ResolvedGroupInput> => {\n if (typeof groupInput === \"string\") {\n return {\n group: await loadMicrosoftEntraGroupById(options, token, groupInput),\n };\n }\n\n if (\"id\" in groupInput) {\n return {\n group: await loadMicrosoftEntraGroupById(options, token, groupInput.id),\n alias: groupInput.alias,\n };\n }\n\n return {\n group: await loadMicrosoftEntraGroupByDisplayName(\n options,\n token,\n groupInput.displayName,\n ),\n alias: groupInput.alias,\n };\n};\n\nconst loadMicrosoftEntraGroupById = async (\n options: MicrosoftEntraDirectoryOptions,\n token: string,\n groupId: string,\n) => {\n return microsoftGraphRequest<MicrosoftEntraGroup>(\n options,\n token,\n buildMicrosoftGraphUrl(options, `/groups/${encodeURIComponent(groupId)}`, {\n $select: GRAPH_SELECT_GROUP,\n }),\n );\n};\n\nconst loadMicrosoftEntraGroupByDisplayName = async (\n options: MicrosoftEntraDirectoryOptions,\n token: string,\n displayName: string,\n) => {\n const groups = await paginate<MicrosoftEntraGroup>(\n options,\n token,\n buildMicrosoftGraphUrl(options, \"/groups\", {\n $filter: `displayName eq '${escapeODataString(displayName)}'`,\n $select: GRAPH_SELECT_GROUP,\n }),\n );\n\n if (groups.length === 0) {\n throw new Error(\n `Microsoft Entra directory connector could not find a group with displayName \"${displayName}\". Use the group id instead.`,\n );\n }\n\n if (groups.length > 1) {\n throw new Error(\n `Microsoft Entra directory connector found ${groups.length} groups with displayName \"${displayName}\". Use the group id instead.`,\n );\n }\n\n return groups[0];\n};\n\nconst mapMicrosoftEntraTeams = (groups: ResolvedGroupInput[]) => {\n return groups.map(({ group, alias }) => {\n const displayName = group.displayName ?? group.id;\n\n return {\n id: alias ?? slugify(displayName),\n name: displayName,\n summary: group.description ?? undefined,\n email: group.mail ?? undefined,\n markdown: createMicrosoftEntraSyncedMarkdown(\"team\"),\n source: {\n provider: \"microsoft-entra\",\n id: group.id,\n },\n };\n });\n};\n\nconst loadMicrosoftEntraGroupMembers = async (\n options: MicrosoftEntraDirectoryOptions,\n token: string,\n groupId: string,\n): Promise<DirectoryUser[]> => {\n const users = await paginate<MicrosoftEntraUser>(\n options,\n token,\n buildMicrosoftGraphUrl(\n options,\n `/groups/${encodeURIComponent(groupId)}/members/microsoft.graph.user`,\n {\n $select: GRAPH_SELECT_USER,\n },\n ),\n );\n\n return users\n .filter(\n (user) => options.includeDisabledUsers || user.accountEnabled !== false,\n )\n .map(mapMicrosoftEntraUser);\n};\n\nconst mapMicrosoftEntraUser = (user: MicrosoftEntraUser): DirectoryUser => {\n const userPrincipalName = user.userPrincipalName ?? undefined;\n const email = user.mail ?? userPrincipalName;\n const id = getMicrosoftEntraUserId(user);\n const name = user.displayName ?? userPrincipalName ?? user.id;\n\n return {\n id,\n name,\n email,\n role: user.jobTitle ?? undefined,\n markdown: createMicrosoftEntraSyncedMarkdown(\"user\"),\n source: {\n provider: \"microsoft-entra\",\n id: user.id,\n },\n };\n};\n\nconst getMicrosoftEntraUserId = (user: MicrosoftEntraUser) => {\n if (user.mail) return user.mail.toLowerCase();\n if (user.userPrincipalName)\n return normalizeMicrosoftEntraUserPrincipalName(user.userPrincipalName);\n return user.id;\n};\n\nconst normalizeMicrosoftEntraUserPrincipalName = (\n userPrincipalName: string,\n) => {\n const guestUserMatch = userPrincipalName.match(/^(.+)#EXT#@.+$/i);\n\n if (!guestUserMatch) return userPrincipalName.toLowerCase();\n\n const externalUserName = guestUserMatch[1];\n const lastUnderscoreIndex = externalUserName.lastIndexOf(\"_\");\n\n if (lastUnderscoreIndex === -1) return externalUserName.toLowerCase();\n\n return `${externalUserName.slice(0, lastUnderscoreIndex)}@${externalUserName.slice(lastUnderscoreIndex + 1)}`.toLowerCase();\n};\n\nconst createMicrosoftEntraSyncedMarkdown = (type: \"team\" | \"user\") => {\n if (type === \"team\") {\n return [\n \":::note\",\n \"This team is synced from Microsoft Entra ID and is read-only in EventCatalog.\",\n \"\",\n \"Manage the team and its members in Microsoft Entra ID.\",\n \":::\",\n ].join(\"\\n\");\n }\n\n return [\n \":::note\",\n \"This user is synced from Microsoft Entra ID and is read-only in EventCatalog.\",\n \"\",\n \"Manage profile details and team membership in Microsoft Entra ID.\",\n \":::\",\n ].join(\"\\n\");\n};\n\nconst getAccessToken = async (options: MicrosoftEntraDirectoryOptions) => {\n const tokenUrl =\n options.tokenUrl ??\n `https://login.microsoftonline.com/${encodeURIComponent(options.tenantId)}/oauth2/v2.0/token`;\n const body = new URLSearchParams({\n client_id: options.clientId,\n client_secret: options.clientSecret,\n grant_type: \"client_credentials\",\n scope: getMicrosoftGraphScope(options),\n });\n\n const response = await fetch(tokenUrl, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/x-www-form-urlencoded\",\n \"user-agent\": \"@eventcatalog/connectors\",\n },\n body,\n });\n\n if (!response.ok) {\n const message = await response.text();\n throw new Error(\n `Microsoft Entra directory connector failed to get an access token (${response.status} ${response.statusText}): ${message}`,\n );\n }\n\n const data = (await response.json()) as { access_token?: string };\n if (!data.access_token) {\n throw new Error(\n \"Microsoft Entra directory connector failed to get an access token: response did not include access_token.\",\n );\n }\n\n return data.access_token;\n};\n\nconst getMicrosoftGraphScope = (options: MicrosoftEntraDirectoryOptions) => {\n const graphBaseUrl = options.graphBaseUrl ?? DEFAULT_GRAPH_BASE_URL;\n return `${new URL(graphBaseUrl).origin}/.default`;\n};\n\nconst microsoftGraphRequest = async <T>(\n options: MicrosoftEntraDirectoryOptions,\n token: string,\n url: string,\n attempt = 0,\n): Promise<T> => {\n const response = await fetch(url, {\n headers: {\n accept: \"application/json\",\n authorization: `Bearer ${token}`,\n ConsistencyLevel: \"eventual\",\n \"user-agent\": \"@eventcatalog/connectors\",\n },\n });\n\n if (response.status === 429 && attempt < GRAPH_RETRY_ATTEMPTS) {\n await waitForRetry(response.headers.get(\"retry-after\"));\n return microsoftGraphRequest(options, token, url, attempt + 1);\n }\n\n if (!response.ok) {\n const message = await response.text();\n throw new Error(\n `Microsoft Entra directory connector failed (${response.status} ${response.statusText}): ${message}`,\n );\n }\n\n return (await response.json()) as T;\n};\n\nconst paginate = async <T>(\n options: MicrosoftEntraDirectoryOptions,\n token: string,\n url: string,\n): Promise<T[]> => {\n const results: T[] = [];\n let nextUrl: string | undefined = url;\n\n while (nextUrl) {\n const response: MicrosoftGraphCollection<T> = await microsoftGraphRequest<\n MicrosoftGraphCollection<T>\n >(options, token, nextUrl);\n results.push(...(response.value ?? []));\n nextUrl = response[\"@odata.nextLink\"];\n }\n\n return results;\n};\n\nconst buildMicrosoftGraphUrl = (\n options: MicrosoftEntraDirectoryOptions,\n pathname: string,\n query?: Record<string, string>,\n) => {\n const baseUrl = options.graphBaseUrl ?? DEFAULT_GRAPH_BASE_URL;\n const normalizedBaseUrl = baseUrl.endsWith(\"/\") ? baseUrl : `${baseUrl}/`;\n const normalizedPathname = pathname.replace(/^\\//u, \"\");\n const url = new URL(normalizedPathname, normalizedBaseUrl);\n\n for (const [key, value] of Object.entries(query ?? {})) {\n url.searchParams.set(key, value);\n }\n\n return url.toString();\n};\n\nconst assertUniqueTeamIds = (groups: ResolvedGroupInput[]) => {\n const teamIds = new Map<string, string>();\n\n for (const { group, alias } of groups) {\n const displayName = group.displayName ?? group.id;\n const teamId = alias ?? slugify(displayName);\n const existingGroupId = teamIds.get(teamId);\n\n if (existingGroupId) {\n throw new Error(\n `Microsoft Entra directory connector resolved duplicate team id \"${teamId}\" from groups \"${existingGroupId}\" and \"${group.id}\". Set alias for one of the groups.`,\n );\n }\n\n teamIds.set(teamId, group.id);\n }\n};\n\nconst slugify = (value: string) => {\n const slug = value\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n\n return slug || value;\n};\n\nconst escapeODataString = (value: string) => value.replace(/'/g, \"''\");\n\nconst waitForRetry = async (retryAfter: string | null) => {\n const retryAfterSeconds = retryAfter ? Number(retryAfter) : 1;\n const delayMs = Number.isFinite(retryAfterSeconds)\n ? Math.max(retryAfterSeconds, 0) * 1000\n : 1000;\n\n await new Promise((resolve) => setTimeout(resolve, delayMs));\n};\n"],"mappings":"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","names":["paginate"]}
|
|
1
|
+
{"version":3,"sources":["../src/types.ts","../src/github-directory.ts","../src/microsoft-entra-directory.ts","../src/git-schema-source.ts"],"sourcesContent":["/**\n * Describes where a generated directory entry came from.\n *\n * EventCatalog uses the provider to show source badges and to treat generated\n * resources as externally managed.\n */\nexport type DirectoryEntrySource = {\n /** Provider identifier, for example `github`, `okta`, or `ldap`. */\n provider: string;\n /** Optional provider-specific identifier for the external resource. */\n id?: string;\n /** Optional URL back to the resource in the external provider. */\n url?: string;\n [key: string]: unknown;\n};\n\n/**\n * User returned by a directory source.\n *\n * Directory users are synced as read-only EventCatalog users and can be\n * referenced by teams through their `id`.\n */\nexport type DirectoryUser = {\n id: string;\n name: string;\n avatarUrl?: string;\n role?: string;\n email?: string;\n markdown?: string;\n source?: DirectoryEntrySource;\n [key: string]: unknown;\n};\n\n/**\n * Team returned by a directory source.\n *\n * Directory teams are synced as read-only EventCatalog teams.\n */\nexport type DirectoryTeam = {\n id: string;\n name: string;\n summary?: string;\n /** User IDs for members of this team. */\n members?: string[];\n email?: string;\n markdown?: string;\n source?: DirectoryEntrySource;\n [key: string]: unknown;\n};\n\n/**\n * Loads users and/or teams from an external directory provider.\n *\n * EventCatalog calls these loaders during content sync, writes the returned\n * entries into Astro collections, and mirrors them into the generated\n * `.eventcatalog/store/directory.json` file for SDK/editor reads.\n */\nexport type DirectorySource = {\n type: \"directory\";\n /** Stable source identifier, usually `<provider>:<account-or-org>`. */\n name: string;\n /** Load users from the external directory. */\n loadUsers?: () => Promise<DirectoryUser[]>;\n /** Load teams from the external directory. */\n loadTeams?: () => Promise<DirectoryTeam[]>;\n};\n\n/**\n * Defines a custom directory source with type inference for connector authors.\n */\nexport const defineDirectorySource = <TSource extends DirectorySource>(\n source: TSource,\n) => source;\n\n/**\n * Describes where a synced schema came from.\n */\nexport type SchemaEntrySource = {\n /** Provider identifier, for example `git`, `github`, `confluent`, or `eventbridge`. */\n provider: string;\n /** Optional provider-specific identifier for the external schema. */\n id?: string;\n /** Optional URL back to the schema source. */\n url?: string;\n /** Optional version/ref/branch for versioned sources. */\n ref?: string;\n /** Optional branch for git-backed schema sources. */\n branch?: string;\n /** Optional path inside the source. */\n path?: string;\n [key: string]: unknown;\n};\n\n/**\n * Schema returned by an external schema source.\n */\nexport type SchemaEntry = {\n id: string;\n name?: string;\n format?: string;\n content: string;\n source: SchemaEntrySource;\n};\n\nexport type SchemaResolveContext = {\n /** Absolute path to the message file that referenced the schema. */\n messageFilePath?: string;\n};\n\n/**\n * Loads schemas from an external source.\n *\n * EventCatalog calls schema sources during content sync to resolve schema\n * references from messages into generated schema collection entries.\n */\nexport type SchemaSource = {\n type: \"schemas\";\n /** Stable source identifier used in schema refs, for example `contracts`. */\n name: string;\n /** Returns true when this source can resolve the given schema ref. */\n canResolve: (ref: string) => boolean;\n /** Resolves a schema ref into schema content and source metadata. */\n resolve: (\n ref: string,\n context?: SchemaResolveContext,\n ) => Promise<SchemaEntry | undefined>;\n};\n\n/**\n * Defines a custom schema source with type inference for connector authors.\n */\nexport const defineSchemaSource = <TSource extends SchemaSource>(\n source: TSource,\n) => source;\n","import {\n defineDirectorySource,\n type DirectorySource,\n type DirectoryTeam,\n type DirectoryUser,\n} from \"./types\";\n\ntype GitHubDirectoryOptions = {\n org: string;\n token?: string;\n teams: string[];\n users?: boolean;\n baseUrl?: string;\n};\n\ntype GitHubTeam = {\n id: number;\n name: string;\n slug: string;\n description: string | null;\n html_url: string;\n};\n\ntype GitHubUser = {\n id: number;\n login: string;\n avatar_url: string;\n html_url: string;\n};\n\ntype GitHubDirectoryData = {\n teams: DirectoryTeam[];\n users: DirectoryUser[];\n};\n\nconst GITHUB_API_VERSION = \"2022-11-28\";\n\nexport const githubDirectory = (\n options: GitHubDirectoryOptions,\n): DirectorySource => {\n if (!Array.isArray(options.teams) || options.teams.length === 0) {\n throw new Error(\n 'GitHub directory connector requires at least one team slug. Set teams: [\"platform\"].',\n );\n }\n\n let cachedData: Promise<GitHubDirectoryData> | undefined;\n\n const load = () => {\n cachedData = cachedData ?? loadGitHubDirectory(options);\n return cachedData;\n };\n\n return defineDirectorySource({\n type: \"directory\",\n name: `github:${options.org}`,\n loadTeams: async () => {\n const data = await load();\n return data.teams;\n },\n loadUsers: async () => {\n const data = await load();\n return data.users;\n },\n });\n};\n\nconst loadGitHubDirectory = async (\n options: GitHubDirectoryOptions,\n): Promise<GitHubDirectoryData> => {\n const teams = await loadGitHubTeams(options);\n const shouldLoadUsers = options.users !== false;\n\n if (!shouldLoadUsers) {\n return { teams, users: [] };\n }\n\n const usersById = new Map<string, DirectoryUser>();\n const teamsWithMembers: DirectoryTeam[] = [];\n\n for (const team of teams) {\n const members = await loadGitHubTeamMembers(options, team.id);\n\n for (const member of members) {\n usersById.set(member.id, member);\n }\n\n teamsWithMembers.push({\n ...team,\n members: members.map((member) => member.id),\n });\n }\n\n return {\n teams: teamsWithMembers,\n users: Array.from(usersById.values()),\n };\n};\n\nconst loadGitHubTeams = async (\n options: GitHubDirectoryOptions,\n): Promise<DirectoryTeam[]> => {\n const teams: GitHubTeam[] = [];\n\n for (const teamSlug of options.teams) {\n teams.push(await loadGitHubTeam(options, teamSlug));\n }\n\n return teams.map((team) => ({\n id: team.slug,\n name: team.name,\n summary: team.description ?? undefined,\n markdown: createGitHubSyncedMarkdown(\"team\", team.html_url),\n source: {\n provider: \"github\",\n id: String(team.id),\n url: team.html_url,\n },\n }));\n};\n\nconst loadGitHubTeam = async (\n options: GitHubDirectoryOptions,\n teamSlug: string,\n) => {\n const response = await githubRequest<GitHubTeam>(\n options,\n buildGitHubUrl(\n options,\n `/orgs/${encodeURIComponent(options.org)}/teams/${encodeURIComponent(teamSlug)}`,\n ),\n );\n\n return response.data;\n};\n\nconst loadGitHubTeamMembers = async (\n options: GitHubDirectoryOptions,\n teamSlug: string,\n): Promise<DirectoryUser[]> => {\n const members = await paginate<GitHubUser>(\n options,\n `/orgs/${encodeURIComponent(options.org)}/teams/${encodeURIComponent(teamSlug)}/members`,\n );\n\n return members.map((member) => ({\n id: member.login,\n name: member.login,\n avatarUrl: member.avatar_url,\n markdown: createGitHubSyncedMarkdown(\"user\", member.html_url),\n source: {\n provider: \"github\",\n id: String(member.id),\n url: member.html_url,\n },\n }));\n};\n\nconst createGitHubSyncedMarkdown = (type: \"team\" | \"user\", url: string) => {\n if (type === \"team\") {\n return [\n \":::note\",\n \"This team is synced from GitHub and is read-only in EventCatalog.\",\n \"\",\n \"Manage the team and its members in GitHub.\",\n \"\",\n `[View team on GitHub](${url})`,\n \":::\",\n ].join(\"\\n\");\n }\n\n return [\n \":::note\",\n \"This user is synced from GitHub and is read-only in EventCatalog.\",\n \"\",\n \"Manage profile details and team membership in GitHub.\",\n \"\",\n `[View user on GitHub](${url})`,\n \":::\",\n ].join(\"\\n\");\n};\n\nconst paginate = async <T>(\n options: GitHubDirectoryOptions,\n pathname: string,\n): Promise<T[]> => {\n const results: T[] = [];\n let url: string | undefined = buildGitHubUrl(options, pathname);\n\n while (url) {\n const response = await githubRequest<T[]>(options, url);\n results.push(...response.data);\n url = getNextPageUrl(response.headers.get(\"link\"));\n }\n\n return results;\n};\n\nconst githubRequest = async <T>(\n options: GitHubDirectoryOptions,\n url: string,\n): Promise<{ data: T; headers: Headers }> => {\n const response = await fetch(url, {\n headers: {\n accept: \"application/vnd.github+json\",\n \"x-github-api-version\": GITHUB_API_VERSION,\n \"user-agent\": \"@eventcatalog/connectors\",\n ...(options.token ? { authorization: `Bearer ${options.token}` } : {}),\n },\n });\n\n if (!response.ok) {\n const message = await response.text();\n throw new Error(\n `GitHub directory connector failed (${response.status} ${response.statusText}): ${message}`,\n );\n }\n\n return {\n data: (await response.json()) as T,\n headers: response.headers,\n };\n};\n\nconst buildGitHubUrl = (options: GitHubDirectoryOptions, pathname: string) => {\n const baseUrl = options.baseUrl ?? \"https://api.github.com\";\n const normalizedBaseUrl = baseUrl.endsWith(\"/\") ? baseUrl : `${baseUrl}/`;\n const normalizedPathname = pathname.replace(/^\\//u, \"\");\n const url = new URL(normalizedPathname, normalizedBaseUrl);\n url.searchParams.set(\"per_page\", \"100\");\n return url.toString();\n};\n\nconst getNextPageUrl = (linkHeader: string | null) => {\n if (!linkHeader) return undefined;\n\n const nextLink = linkHeader\n .split(\",\")\n .find((link) => link.includes('rel=\"next\"'));\n const match = nextLink?.match(/<([^>]+)>/);\n\n return match?.[1];\n};\n","import {\n defineDirectorySource,\n type DirectorySource,\n type DirectoryTeam,\n type DirectoryUser,\n} from \"./types\";\n\ntype MicrosoftEntraGroupInput =\n | string\n | {\n id: string;\n alias?: string;\n }\n | {\n displayName: string;\n alias?: string;\n };\n\ntype MicrosoftEntraDirectoryOptions = {\n tenantId: string;\n clientId: string;\n clientSecret: string;\n groups: MicrosoftEntraGroupInput[];\n users?: boolean;\n includeDisabledUsers?: boolean;\n graphBaseUrl?: string;\n tokenUrl?: string;\n};\n\ntype MicrosoftEntraGroup = {\n id: string;\n displayName?: string | null;\n description?: string | null;\n mail?: string | null;\n};\n\ntype MicrosoftEntraUser = {\n id: string;\n displayName?: string | null;\n userPrincipalName?: string | null;\n mail?: string | null;\n jobTitle?: string | null;\n accountEnabled?: boolean | null;\n};\n\ntype MicrosoftEntraDirectoryData = {\n teams: DirectoryTeam[];\n users: DirectoryUser[];\n};\n\ntype MicrosoftGraphCollection<T> = {\n value?: T[];\n \"@odata.nextLink\"?: string;\n};\n\ntype ResolvedGroupInput = {\n group: MicrosoftEntraGroup;\n alias?: string;\n};\n\nconst DEFAULT_GRAPH_BASE_URL = \"https://graph.microsoft.com/v1.0\";\nconst GRAPH_RETRY_ATTEMPTS = 3;\nconst GRAPH_SELECT_GROUP = \"id,displayName,description,mail\";\nconst GRAPH_SELECT_USER =\n \"id,displayName,userPrincipalName,mail,jobTitle,accountEnabled\";\n\nexport const microsoftEntraDirectory = (\n options: MicrosoftEntraDirectoryOptions,\n): DirectorySource => {\n validateOptions(options);\n\n let cachedData: Promise<MicrosoftEntraDirectoryData> | undefined;\n\n const load = () => {\n cachedData = cachedData ?? loadMicrosoftEntraDirectory(options);\n return cachedData;\n };\n\n return defineDirectorySource({\n type: \"directory\",\n name: `microsoft-entra:${options.tenantId}`,\n loadTeams: async () => {\n const data = await load();\n return data.teams;\n },\n loadUsers: async () => {\n const data = await load();\n return data.users;\n },\n });\n};\n\nconst validateOptions = (options: MicrosoftEntraDirectoryOptions) => {\n if (!options.tenantId) {\n throw new Error(\"Microsoft Entra directory connector requires tenantId.\");\n }\n if (!options.clientId) {\n throw new Error(\"Microsoft Entra directory connector requires clientId.\");\n }\n if (!options.clientSecret) {\n throw new Error(\n \"Microsoft Entra directory connector requires clientSecret.\",\n );\n }\n if (!Array.isArray(options.groups) || options.groups.length === 0) {\n throw new Error(\n 'Microsoft Entra directory connector requires at least one group. Set groups: [{ id: \"00000000-0000-0000-0000-000000000000\" }].',\n );\n }\n};\n\nconst loadMicrosoftEntraDirectory = async (\n options: MicrosoftEntraDirectoryOptions,\n): Promise<MicrosoftEntraDirectoryData> => {\n const token = await getAccessToken(options);\n const resolvedGroups = await loadMicrosoftEntraGroups(options, token);\n const teams = mapMicrosoftEntraTeams(resolvedGroups);\n const shouldLoadUsers = options.users !== false;\n\n if (!shouldLoadUsers) {\n return { teams, users: [] };\n }\n\n const usersById = new Map<string, DirectoryUser>();\n const teamsWithMembers: DirectoryTeam[] = [];\n\n for (const team of teams) {\n const members = await loadMicrosoftEntraGroupMembers(\n options,\n token,\n String(team.source?.id ?? team.id),\n );\n\n for (const member of members) {\n usersById.set(member.id, member);\n }\n\n teamsWithMembers.push({\n ...team,\n members: members.map((member) => member.id),\n });\n }\n\n return {\n teams: teamsWithMembers,\n users: Array.from(usersById.values()),\n };\n};\n\nconst loadMicrosoftEntraGroups = async (\n options: MicrosoftEntraDirectoryOptions,\n token: string,\n): Promise<ResolvedGroupInput[]> => {\n const resolvedGroups: ResolvedGroupInput[] = [];\n\n for (const groupInput of options.groups) {\n resolvedGroups.push(\n await resolveMicrosoftEntraGroup(options, token, groupInput),\n );\n }\n\n assertUniqueTeamIds(resolvedGroups);\n\n return resolvedGroups;\n};\n\nconst resolveMicrosoftEntraGroup = async (\n options: MicrosoftEntraDirectoryOptions,\n token: string,\n groupInput: MicrosoftEntraGroupInput,\n): Promise<ResolvedGroupInput> => {\n if (typeof groupInput === \"string\") {\n return {\n group: await loadMicrosoftEntraGroupById(options, token, groupInput),\n };\n }\n\n if (\"id\" in groupInput) {\n return {\n group: await loadMicrosoftEntraGroupById(options, token, groupInput.id),\n alias: groupInput.alias,\n };\n }\n\n return {\n group: await loadMicrosoftEntraGroupByDisplayName(\n options,\n token,\n groupInput.displayName,\n ),\n alias: groupInput.alias,\n };\n};\n\nconst loadMicrosoftEntraGroupById = async (\n options: MicrosoftEntraDirectoryOptions,\n token: string,\n groupId: string,\n) => {\n return microsoftGraphRequest<MicrosoftEntraGroup>(\n options,\n token,\n buildMicrosoftGraphUrl(options, `/groups/${encodeURIComponent(groupId)}`, {\n $select: GRAPH_SELECT_GROUP,\n }),\n );\n};\n\nconst loadMicrosoftEntraGroupByDisplayName = async (\n options: MicrosoftEntraDirectoryOptions,\n token: string,\n displayName: string,\n) => {\n const groups = await paginate<MicrosoftEntraGroup>(\n options,\n token,\n buildMicrosoftGraphUrl(options, \"/groups\", {\n $filter: `displayName eq '${escapeODataString(displayName)}'`,\n $select: GRAPH_SELECT_GROUP,\n }),\n );\n\n if (groups.length === 0) {\n throw new Error(\n `Microsoft Entra directory connector could not find a group with displayName \"${displayName}\". Use the group id instead.`,\n );\n }\n\n if (groups.length > 1) {\n throw new Error(\n `Microsoft Entra directory connector found ${groups.length} groups with displayName \"${displayName}\". Use the group id instead.`,\n );\n }\n\n return groups[0];\n};\n\nconst mapMicrosoftEntraTeams = (groups: ResolvedGroupInput[]) => {\n return groups.map(({ group, alias }) => {\n const displayName = group.displayName ?? group.id;\n\n return {\n id: alias ?? slugify(displayName),\n name: displayName,\n summary: group.description ?? undefined,\n email: group.mail ?? undefined,\n markdown: createMicrosoftEntraSyncedMarkdown(\"team\"),\n source: {\n provider: \"microsoft-entra\",\n id: group.id,\n },\n };\n });\n};\n\nconst loadMicrosoftEntraGroupMembers = async (\n options: MicrosoftEntraDirectoryOptions,\n token: string,\n groupId: string,\n): Promise<DirectoryUser[]> => {\n const users = await paginate<MicrosoftEntraUser>(\n options,\n token,\n buildMicrosoftGraphUrl(\n options,\n `/groups/${encodeURIComponent(groupId)}/members/microsoft.graph.user`,\n {\n $select: GRAPH_SELECT_USER,\n },\n ),\n );\n\n return users\n .filter(\n (user) => options.includeDisabledUsers || user.accountEnabled !== false,\n )\n .map(mapMicrosoftEntraUser);\n};\n\nconst mapMicrosoftEntraUser = (user: MicrosoftEntraUser): DirectoryUser => {\n const userPrincipalName = user.userPrincipalName ?? undefined;\n const email = user.mail ?? userPrincipalName;\n const id = getMicrosoftEntraUserId(user);\n const name = user.displayName ?? userPrincipalName ?? user.id;\n\n return {\n id,\n name,\n email,\n role: user.jobTitle ?? undefined,\n markdown: createMicrosoftEntraSyncedMarkdown(\"user\"),\n source: {\n provider: \"microsoft-entra\",\n id: user.id,\n },\n };\n};\n\nconst getMicrosoftEntraUserId = (user: MicrosoftEntraUser) => {\n if (user.mail) return user.mail.toLowerCase();\n if (user.userPrincipalName)\n return normalizeMicrosoftEntraUserPrincipalName(user.userPrincipalName);\n return user.id;\n};\n\nconst normalizeMicrosoftEntraUserPrincipalName = (\n userPrincipalName: string,\n) => {\n const guestUserMatch = userPrincipalName.match(/^(.+)#EXT#@.+$/i);\n\n if (!guestUserMatch) return userPrincipalName.toLowerCase();\n\n const externalUserName = guestUserMatch[1];\n const lastUnderscoreIndex = externalUserName.lastIndexOf(\"_\");\n\n if (lastUnderscoreIndex === -1) return externalUserName.toLowerCase();\n\n return `${externalUserName.slice(0, lastUnderscoreIndex)}@${externalUserName.slice(lastUnderscoreIndex + 1)}`.toLowerCase();\n};\n\nconst createMicrosoftEntraSyncedMarkdown = (type: \"team\" | \"user\") => {\n if (type === \"team\") {\n return [\n \":::note\",\n \"This team is synced from Microsoft Entra ID and is read-only in EventCatalog.\",\n \"\",\n \"Manage the team and its members in Microsoft Entra ID.\",\n \":::\",\n ].join(\"\\n\");\n }\n\n return [\n \":::note\",\n \"This user is synced from Microsoft Entra ID and is read-only in EventCatalog.\",\n \"\",\n \"Manage profile details and team membership in Microsoft Entra ID.\",\n \":::\",\n ].join(\"\\n\");\n};\n\nconst getAccessToken = async (options: MicrosoftEntraDirectoryOptions) => {\n const tokenUrl =\n options.tokenUrl ??\n `https://login.microsoftonline.com/${encodeURIComponent(options.tenantId)}/oauth2/v2.0/token`;\n const body = new URLSearchParams({\n client_id: options.clientId,\n client_secret: options.clientSecret,\n grant_type: \"client_credentials\",\n scope: getMicrosoftGraphScope(options),\n });\n\n const response = await fetch(tokenUrl, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/x-www-form-urlencoded\",\n \"user-agent\": \"@eventcatalog/connectors\",\n },\n body,\n });\n\n if (!response.ok) {\n const message = await response.text();\n throw new Error(\n `Microsoft Entra directory connector failed to get an access token (${response.status} ${response.statusText}): ${message}`,\n );\n }\n\n const data = (await response.json()) as { access_token?: string };\n if (!data.access_token) {\n throw new Error(\n \"Microsoft Entra directory connector failed to get an access token: response did not include access_token.\",\n );\n }\n\n return data.access_token;\n};\n\nconst getMicrosoftGraphScope = (options: MicrosoftEntraDirectoryOptions) => {\n const graphBaseUrl = options.graphBaseUrl ?? DEFAULT_GRAPH_BASE_URL;\n return `${new URL(graphBaseUrl).origin}/.default`;\n};\n\nconst microsoftGraphRequest = async <T>(\n options: MicrosoftEntraDirectoryOptions,\n token: string,\n url: string,\n attempt = 0,\n): Promise<T> => {\n const response = await fetch(url, {\n headers: {\n accept: \"application/json\",\n authorization: `Bearer ${token}`,\n ConsistencyLevel: \"eventual\",\n \"user-agent\": \"@eventcatalog/connectors\",\n },\n });\n\n if (response.status === 429 && attempt < GRAPH_RETRY_ATTEMPTS) {\n await waitForRetry(response.headers.get(\"retry-after\"));\n return microsoftGraphRequest(options, token, url, attempt + 1);\n }\n\n if (!response.ok) {\n const message = await response.text();\n throw new Error(\n `Microsoft Entra directory connector failed (${response.status} ${response.statusText}): ${message}`,\n );\n }\n\n return (await response.json()) as T;\n};\n\nconst paginate = async <T>(\n options: MicrosoftEntraDirectoryOptions,\n token: string,\n url: string,\n): Promise<T[]> => {\n const results: T[] = [];\n let nextUrl: string | undefined = url;\n\n while (nextUrl) {\n const response: MicrosoftGraphCollection<T> = await microsoftGraphRequest<\n MicrosoftGraphCollection<T>\n >(options, token, nextUrl);\n results.push(...(response.value ?? []));\n nextUrl = response[\"@odata.nextLink\"];\n }\n\n return results;\n};\n\nconst buildMicrosoftGraphUrl = (\n options: MicrosoftEntraDirectoryOptions,\n pathname: string,\n query?: Record<string, string>,\n) => {\n const baseUrl = options.graphBaseUrl ?? DEFAULT_GRAPH_BASE_URL;\n const normalizedBaseUrl = baseUrl.endsWith(\"/\") ? baseUrl : `${baseUrl}/`;\n const normalizedPathname = pathname.replace(/^\\//u, \"\");\n const url = new URL(normalizedPathname, normalizedBaseUrl);\n\n for (const [key, value] of Object.entries(query ?? {})) {\n url.searchParams.set(key, value);\n }\n\n return url.toString();\n};\n\nconst assertUniqueTeamIds = (groups: ResolvedGroupInput[]) => {\n const teamIds = new Map<string, string>();\n\n for (const { group, alias } of groups) {\n const displayName = group.displayName ?? group.id;\n const teamId = alias ?? slugify(displayName);\n const existingGroupId = teamIds.get(teamId);\n\n if (existingGroupId) {\n throw new Error(\n `Microsoft Entra directory connector resolved duplicate team id \"${teamId}\" from groups \"${existingGroupId}\" and \"${group.id}\". Set alias for one of the groups.`,\n );\n }\n\n teamIds.set(teamId, group.id);\n }\n};\n\nconst slugify = (value: string) => {\n const slug = value\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n\n return slug || value;\n};\n\nconst escapeODataString = (value: string) => value.replace(/'/g, \"''\");\n\nconst waitForRetry = async (retryAfter: string | null) => {\n const retryAfterSeconds = retryAfter ? Number(retryAfter) : 1;\n const delayMs = Number.isFinite(retryAfterSeconds)\n ? Math.max(retryAfterSeconds, 0) * 1000\n : 1000;\n\n await new Promise((resolve) => setTimeout(resolve, delayMs));\n};\n","import { mkdtemp, readFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport path from \"node:path\";\nimport simpleGit from \"simple-git\";\nimport {\n defineSchemaSource,\n type SchemaEntry,\n type SchemaSource,\n} from \"./types\";\n\ntype GitSchemaSourceOptions = {\n /**\n * Stable source name used in schema refs, for example:\n * `git://contracts/events/OrderPlaced.schema.json`.\n */\n name: string;\n /** Git repository URL. Supports any URL your local git can clone. */\n url: string;\n /** Branch to clone. */\n branch?: string;\n /** Optional directory inside the repository that contains schemas. */\n directory?: string;\n /** Optional HTTPS token. SSH auth is handled by the local git environment. */\n token?: string;\n};\n\nconst getSchemaFormat = (filePath: string) => {\n const extension = path.extname(filePath).replace(\".\", \"\").toLowerCase();\n\n if (extension === \"avsc\" || extension === \"avro\") return \"avro\";\n if (extension === \"proto\") return \"protobuf\";\n if (extension === \"json\") return \"jsonschema\";\n if (extension === \"yaml\" || extension === \"yml\") return \"yaml\";\n\n return extension || \"unknown\";\n};\n\nconst parseGitSchemaRef = (ref: string) => {\n let url: URL;\n\n try {\n url = new URL(ref);\n } catch {\n throw new Error(\n `Invalid git schema ref \"${ref}\". Expected git://<source-name>/<path>.`,\n );\n }\n\n if (url.protocol !== \"git:\") {\n throw new Error(\n `Invalid git schema ref \"${ref}\". Expected git://<source-name>/<path>.`,\n );\n }\n\n const filePath = decodeURIComponent(url.pathname.replace(/^\\//, \"\"));\n\n if (!url.hostname || !filePath) {\n throw new Error(\n `Invalid git schema ref \"${ref}\". Expected git://<source-name>/<path>.`,\n );\n }\n\n if (filePath.split(/[\\\\/]/).includes(\"..\")) {\n throw new Error(\n `Schema path \"${filePath}\" resolves outside the git schema source directory.`,\n );\n }\n\n return {\n sourceName: url.hostname,\n filePath,\n };\n};\n\nconst addTokenToHttpsUrl = (url: string, token?: string) => {\n if (!token || !url.startsWith(\"https://\")) return url;\n\n const parsed = new URL(url);\n parsed.username = token;\n parsed.password = \"x-oauth-basic\";\n return parsed.toString();\n};\n\nconst resolveInside = (basePath: string, ...segments: string[]) => {\n const base = path.resolve(basePath);\n const resolved = path.resolve(base, ...segments);\n\n if (resolved !== base && !resolved.startsWith(`${base}${path.sep}`)) {\n throw new Error(\n `Schema path \"${segments.join(\"/\")}\" resolves outside the git schema source directory.`,\n );\n }\n\n return resolved;\n};\n\nconst isNodeFileNotFoundError = (\n error: unknown,\n): error is NodeJS.ErrnoException => {\n return error instanceof Error && \"code\" in error && error.code === \"ENOENT\";\n};\n\nexport const gitSchemaSource = (\n options: GitSchemaSourceOptions,\n): SchemaSource => {\n if (!options.name) {\n throw new Error(\"Git schema source requires a name.\");\n }\n\n if (!options.url) {\n throw new Error(\"Git schema source requires a repository url.\");\n }\n\n const branch = options.branch ?? \"main\";\n const directory = options.directory ?? \"\";\n let checkoutPath: Promise<string> | undefined;\n\n const checkout = async () => {\n checkoutPath =\n checkoutPath ??\n (async () => {\n const target = await mkdtemp(\n path.join(tmpdir(), \"eventcatalog-schema-git-\"),\n );\n await simpleGit().clone(\n addTokenToHttpsUrl(options.url, options.token),\n target,\n {\n \"--branch\": branch,\n \"--depth\": 1,\n \"--single-branch\": null,\n },\n );\n return target;\n })();\n\n return checkoutPath;\n };\n\n return defineSchemaSource({\n type: \"schemas\",\n name: options.name,\n canResolve: (id) => {\n if (!id.startsWith(\"git://\")) return false;\n return parseGitSchemaRef(id).sourceName === options.name;\n },\n resolve: async (id): Promise<SchemaEntry | undefined> => {\n const { sourceName, filePath } = parseGitSchemaRef(id);\n if (sourceName !== options.name) return undefined;\n\n const repositoryPath = await checkout();\n const schemaPath = resolveInside(repositoryPath, directory, filePath);\n let content: string;\n\n try {\n content = await readFile(schemaPath, \"utf8\");\n } catch (error) {\n if (isNodeFileNotFoundError(error)) {\n const schemaSourcePath = [directory, filePath]\n .filter(Boolean)\n .join(\"/\");\n throw new Error(\n `Git schema source \"${options.name}\" could not find schema file \"${schemaSourcePath}\" in \"${options.url}\" on branch \"${branch}\".`,\n );\n }\n\n throw error;\n }\n\n return {\n id,\n name: path.basename(filePath),\n format: getSchemaFormat(filePath),\n content,\n source: {\n provider: \"git\",\n id: `${options.name}:${filePath}`,\n url: options.url,\n branch,\n path: [directory, filePath].filter(Boolean).join(\"/\"),\n },\n };\n },\n });\n};\n"],"mappings":"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","names":["paginate"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eventcatalog/connectors",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Connectors for syncing external systems into EventCatalog Collections",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -38,6 +38,9 @@
|
|
|
38
38
|
"type": "git",
|
|
39
39
|
"url": "https://github.com/event-catalog/eventcatalog"
|
|
40
40
|
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"simple-git": "^3.36.0"
|
|
43
|
+
},
|
|
41
44
|
"scripts": {
|
|
42
45
|
"build": "tsup",
|
|
43
46
|
"build:bin": "tsup",
|