@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 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
- export { type DirectoryEntrySource, type DirectorySource, type DirectoryTeam, type DirectoryUser, defineDirectorySource, githubDirectory, microsoftEntraDirectory };
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
- export { type DirectoryEntrySource, type DirectorySource, type DirectoryTeam, type DirectoryUser, defineDirectorySource, githubDirectory, microsoftEntraDirectory };
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
  };
@@ -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.0",
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",