@eventcatalog/sdk 2.9.1 → 2.9.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/channels.mjs +20 -27
- package/dist/channels.mjs.map +1 -1
- package/dist/commands.mjs +18 -19
- package/dist/commands.mjs.map +1 -1
- package/dist/containers.mjs +18 -19
- package/dist/containers.mjs.map +1 -1
- package/dist/custom-docs.mjs +15 -19
- package/dist/custom-docs.mjs.map +1 -1
- package/dist/data-stores.mjs +18 -19
- package/dist/data-stores.mjs.map +1 -1
- package/dist/domains.mjs +25 -26
- package/dist/domains.mjs.map +1 -1
- package/dist/entities.mjs +18 -19
- package/dist/entities.mjs.map +1 -1
- package/dist/eventcatalog.js +2 -2
- package/dist/eventcatalog.js.map +1 -1
- package/dist/eventcatalog.mjs +37 -37
- package/dist/eventcatalog.mjs.map +1 -1
- package/dist/events.mjs +18 -19
- package/dist/events.mjs.map +1 -1
- package/dist/index.js +6 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +38 -38
- package/dist/index.mjs.map +1 -1
- package/dist/messages.mjs +14 -19
- package/dist/messages.mjs.map +1 -1
- package/dist/queries.mjs +18 -19
- package/dist/queries.mjs.map +1 -1
- package/dist/services.mjs +31 -32
- package/dist/services.mjs.map +1 -1
- package/dist/teams.mjs +16 -21
- package/dist/teams.mjs.map +1 -1
- package/dist/types.d.d.mts +1 -0
- package/dist/types.d.d.ts +1 -0
- package/dist/types.d.js.map +1 -1
- package/dist/users.mjs +6 -7
- package/dist/users.mjs.map +1 -1
- package/package.json +5 -1
package/dist/custom-docs.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/custom-docs.ts","../src/internal/utils.ts","../src/internal/resources.ts"],"sourcesContent":["import path, { join } from 'node:path';\nimport { readMdxFile } from './internal/utils';\nimport type { CustomDoc } from './types';\nimport fsSync from 'node:fs';\nimport fs from 'node:fs/promises';\nimport matter from 'gray-matter';\nimport { getResources } from './internal/resources';\nimport slugify from 'slugify';\n\n/**\n * Returns a custom doc from EventCatalog by the given file path.\n *\n * @example\n * ```ts\n * import utils from '@eventcatalog/utils';\n *\n * const { getCustomDoc } = utils('/path/to/eventcatalog');\n *\n * // Gets the custom doc by the given file path\n * const customDoc = await getCustomDoc('/guides/inventory-management.mdx');\n * ```\n */\nexport const getCustomDoc =\n (directory: string) =>\n async (filePath: string): Promise<CustomDoc | undefined> => {\n const fullPath = path.join(directory, filePath);\n const fullPathWithExtension = fullPath.endsWith('.mdx') ? fullPath : `${fullPath}.mdx`;\n const fileExists = fsSync.existsSync(fullPathWithExtension);\n if (!fileExists) {\n return undefined;\n }\n return readMdxFile(fullPathWithExtension) as Promise<CustomDoc>;\n };\n\n/**\n * Returns all custom docs for the project.\n *\n * @example\n * ```ts\n * import utils from '@eventcatalog/utils';\n *\n * const { getCustomDocs } = utils('/path/to/eventcatalog');\n *\n * // Gets all custom docs from the catalog\n * const customDocs = await getCustomDocs();\n *\n * // Gets all custom docs from the given path\n * const customDocs = await getCustomDocs({ path: '/guides' });\n * ```\n */\nexport const getCustomDocs =\n (directory: string) =>\n async (options?: { path?: string }): Promise<CustomDoc[]> => {\n if (options?.path) {\n const pattern = `${directory}/${options.path}/**/*.{md,mdx}`;\n return getResources(directory, { type: 'docs', pattern }) as Promise<CustomDoc[]>;\n }\n return getResources(directory, { type: 'docs', pattern: `${directory}/**/*.{md,mdx}` }) as Promise<CustomDoc[]>;\n };\n\n/**\n * Write a custom doc to EventCatalog.\n *\n * You can optionally override the path of the custom doc.\n *\n * @example\n * ```ts\n * import utils from '@eventcatalog/utils';\n *\n * const { writeCustomDoc } = utils('/path/to/eventcatalog');\n *\n * // Write a custom doc to the catalog\n * // Custom doc would be written to docs/inventory-management.mdx\n * await writeCustomDoc({\n * title: 'Inventory Management',\n * summary: 'This is a summary',\n * owners: ['John Doe'],\n * badges: [{ content: 'Badge', backgroundColor: 'red', textColor: 'white' }],\n * markdown: '# Hello world',\n * fileName: 'inventory-management',\n * });\n *\n * // Write a custom doc to the catalog but override the path\n * // Custom doc would be written to docs/guides/inventory-management/introduction.mdx\n * await writeCustomDoc({\n * title: 'Inventory Management',\n * summary: 'This is a summary',\n * owners: ['John Doe'],\n * badges: [{ content: 'Badge', backgroundColor: 'red', textColor: 'white' }],\n * markdown: '# Hello world',\n * fileName: 'introduction',\n * }, { path: \"/guides/inventory-management\"});\n * ```\n */\nexport const writeCustomDoc =\n (directory: string) =>\n async (customDoc: CustomDoc, options: { path?: string } = { path: '' }): Promise<void> => {\n const { fileName, ...rest } = customDoc;\n const name = fileName || slugify(customDoc.title, { lower: true });\n const withExtension = name.endsWith('.mdx') ? name : `${name}.mdx`;\n const fullPath = path.join(directory, options.path || '', withExtension);\n\n fsSync.mkdirSync(path.dirname(fullPath), { recursive: true });\n const document = matter.stringify(customDoc.markdown.trim(), rest);\n fsSync.writeFileSync(fullPath, document);\n };\n\n/**\n * Delete a custom doc by its' path\n *\n * @example\n * ```ts\n * import utils from '@eventcatalog/utils';\n *\n * const { rmCustomDoc } = utils('/path/to/eventcatalog');\n *\n * // removes a custom doc at the given path\n * // Removes the custom doc at docs/guides/inventory-management/introduction.mdx\n * await rmCustomDoc('/guides/inventory-management/introduction');\n * ```\n */\nexport const rmCustomDoc = (directory: string) => async (filePath: string) => {\n const withExtension = filePath.endsWith('.mdx') ? filePath : `${filePath}.mdx`;\n await fs.rm(join(directory, withExtension), { recursive: true });\n};\n","import { globSync } from 'glob';\nimport fsSync from 'node:fs';\nimport { copy, CopyFilterAsync, CopyFilterSync } from 'fs-extra';\nimport { join, dirname, normalize, sep as pathSeparator, resolve, basename, relative } from 'node:path';\nimport matter from 'gray-matter';\nimport { satisfies, validRange, valid } from 'semver';\n\n/**\n * Returns true if a given version of a resource id exists in the catalog\n */\nexport const versionExists = async (catalogDir: string, id: string, version: string) => {\n const files = await getFiles(`${catalogDir}/**/index.{md,mdx}`);\n const matchedFiles = (await searchFilesForId(files, id, version)) || [];\n return matchedFiles.length > 0;\n};\n\nexport const findFileById = async (catalogDir: string, id: string, version?: string): Promise<string | undefined> => {\n const files = await getFiles(`${catalogDir}/**/index.{md,mdx}`);\n\n const matchedFiles = (await searchFilesForId(files, id)) || [];\n const latestVersion = matchedFiles.find((path) => !path.includes('versioned'));\n\n // If no version is provided, return the latest version\n if (!version) {\n return latestVersion;\n }\n\n // map files into gray matter to get versions\n const parsedFiles = matchedFiles.map((path) => {\n const { data } = matter.read(path);\n return { ...data, path };\n }) as any[];\n\n // Handle 'latest' version - return the latest (non-versioned) file\n if (version === 'latest') {\n return latestVersion;\n }\n\n // First, check for exact version match (handles non-semver versions like '1', '2', etc.)\n const exactMatch = parsedFiles.find((c) => c.version === version);\n if (exactMatch) {\n return exactMatch.path;\n }\n\n // Try semver range matching\n const semverRange = validRange(version);\n\n if (semverRange) {\n const match = parsedFiles.filter((c) => {\n try {\n return satisfies(c.version, semverRange);\n } catch (error) {\n // If satisfies fails (e.g., comparing semver range with non-semver version), skip this file\n return false;\n }\n });\n return match.length > 0 ? match[0].path : undefined;\n }\n\n // If no exact match and no valid semver range, return undefined\n return undefined;\n};\n\nexport const getFiles = async (pattern: string, ignore: string | string[] = '') => {\n try {\n // 1. Normalize the input pattern to handle mixed separators potentially\n const normalizedInputPattern = normalize(pattern);\n\n // 2. Determine the absolute base directory (cwd for glob)\n // Resolve ensures it's absolute. Handles cases with/without globstar.\n const absoluteBaseDir = resolve(\n normalizedInputPattern.includes('**') ? normalizedInputPattern.split('**')[0] : dirname(normalizedInputPattern)\n );\n\n // 3. Determine the pattern part relative to the absolute base directory\n // We extract the part of the normalized pattern that comes *after* the absoluteBaseDir\n let relativePattern = relative(absoluteBaseDir, normalizedInputPattern);\n\n // On Windows, relative() might return empty string if paths are identical,\n // or might need normalization if the original pattern didn't have `**`\n // Example: pattern = 'dir/file.md', absoluteBaseDir='.../dir', normalized='...\\dir\\file.md'\n // relative() -> 'file.md'\n // Example: pattern = 'dir/**/file.md', absoluteBaseDir='.../dir', normalized='...\\dir\\**\\file.md'\n // relative() -> '**\\file.md'\n // Convert separators in the relative pattern to forward slashes for glob\n relativePattern = relativePattern.replace(/\\\\/g, '/');\n\n const ignoreList = Array.isArray(ignore) ? ignore : [ignore];\n\n const files = globSync(relativePattern, {\n cwd: absoluteBaseDir,\n ignore: ['node_modules/**', ...ignoreList],\n absolute: true,\n nodir: true,\n });\n\n // 5. Normalize results for consistency before returning\n return files.map(normalize);\n } catch (error: any) {\n // Add more diagnostic info to the error\n const absoluteBaseDirForError = resolve(\n normalize(pattern).includes('**') ? normalize(pattern).split('**')[0] : dirname(normalize(pattern))\n );\n const relativePatternForError = relative(absoluteBaseDirForError, normalize(pattern)).replace(/\\\\/g, '/');\n throw new Error(\n `Error finding files for pattern \"${pattern}\" (using cwd: \"${absoluteBaseDirForError}\", globPattern: \"${relativePatternForError}\"): ${error.message}`\n );\n }\n};\n\nexport const readMdxFile = async (path: string) => {\n const { data } = matter.read(path);\n const { markdown, ...frontmatter } = data;\n return { ...frontmatter, markdown };\n};\n\nexport const searchFilesForId = async (files: string[], id: string, version?: string) => {\n // Escape the id to avoid regex issues\n const escapedId = id.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const idRegex = new RegExp(`^id:\\\\s*(['\"]|>-)?\\\\s*${escapedId}['\"]?\\\\s*$`, 'm');\n\n const versionRegex = new RegExp(`^version:\\\\s*['\"]?${version}['\"]?\\\\s*$`, 'm');\n\n const matches = files.map((file) => {\n const content = fsSync.readFileSync(file, 'utf-8');\n const hasIdMatch = content.match(idRegex);\n\n // Check version if provided\n if (version && !content.match(versionRegex)) {\n return undefined;\n }\n\n if (hasIdMatch) {\n return file;\n }\n });\n\n return matches.filter(Boolean).filter((file) => file !== undefined);\n};\n\n/**\n * Function to copy a directory from source to target, uses a tmp directory\n * @param catalogDir\n * @param source\n * @param target\n * @param filter\n */\nexport const copyDir = async (catalogDir: string, source: string, target: string, filter?: CopyFilterAsync | CopyFilterSync) => {\n const tmpDirectory = join(catalogDir, 'tmp');\n fsSync.mkdirSync(tmpDirectory, { recursive: true });\n\n // Copy everything over\n await copy(source, tmpDirectory, {\n overwrite: true,\n filter,\n });\n\n await copy(tmpDirectory, target, {\n overwrite: true,\n filter,\n });\n\n // Remove the tmp directory\n fsSync.rmSync(tmpDirectory, { recursive: true });\n};\n\n// Makes sure values in sends/recieves are unique\nexport const uniqueVersions = (messages: { id: string; version: string }[]): { id: string; version: string }[] => {\n const uniqueSet = new Set();\n\n return messages.filter((message) => {\n const key = `${message.id}-${message.version}`;\n if (!uniqueSet.has(key)) {\n uniqueSet.add(key);\n return true;\n }\n return false;\n });\n};\n","import { dirname, join } from 'path';\nimport { copyDir, findFileById, getFiles, searchFilesForId, versionExists } from './utils';\nimport matter from 'gray-matter';\nimport fs from 'node:fs/promises';\nimport fsSync from 'node:fs';\nimport { Message, Service, CustomDoc } from '../types';\nimport { satisfies } from 'semver';\nimport { lock, unlock } from 'proper-lockfile';\nimport { basename } from 'node:path';\nimport path from 'node:path';\n\ntype Resource = Service | Message | CustomDoc;\n\nexport const versionResource = async (catalogDir: string, id: string) => {\n // Find all the events in the directory\n const files = await getFiles(`${catalogDir}/**/index.{md,mdx}`);\n const matchedFiles = await searchFilesForId(files, id);\n\n if (matchedFiles.length === 0) {\n throw new Error(`No resource found with id: ${id}`);\n }\n\n // Event that is in the route of the project\n const file = matchedFiles[0];\n const sourceDirectory = dirname(file).replace(/\\/versioned?\\/[^/]+\\//, '/');\n const { data: { version = '0.0.1' } = {} } = matter.read(file);\n const targetDirectory = getVersionedDirectory(sourceDirectory, version);\n\n fsSync.mkdirSync(targetDirectory, { recursive: true });\n\n const ignoreListToCopy = ['events', 'commands', 'queries', 'versioned'];\n\n // Copy the event to the versioned directory\n await copyDir(catalogDir, sourceDirectory, targetDirectory, (src) => {\n // get the folder name\n const folderName = basename(src);\n\n if (ignoreListToCopy.includes(folderName)) {\n return false;\n }\n return true;\n });\n\n // Remove all the files in the root of the resource as they have now been versioned\n await fs.readdir(sourceDirectory).then(async (resourceFiles) => {\n await Promise.all(\n resourceFiles.map(async (file) => {\n // Dont remove anything in the ignore list\n if (ignoreListToCopy.includes(file)) {\n return;\n }\n if (file !== 'versioned') {\n fsSync.rmSync(join(sourceDirectory, file), { recursive: true });\n }\n })\n );\n });\n};\n\nexport const writeResource = async (\n catalogDir: string,\n resource: Resource,\n options: { path?: string; type: string; override?: boolean; versionExistingContent?: boolean; format?: 'md' | 'mdx' } = {\n path: '',\n type: '',\n override: false,\n versionExistingContent: false,\n format: 'mdx',\n }\n) => {\n const path = options.path || `/${resource.id}`;\n const fullPath = join(catalogDir, path);\n const format = options.format || 'mdx';\n\n // Create directory if it doesn't exist\n fsSync.mkdirSync(fullPath, { recursive: true });\n\n // Create or get lock file path\n const lockPath = join(fullPath, `index.${format}`);\n\n // Ensure the file exists before attempting to lock it\n if (!fsSync.existsSync(lockPath)) {\n fsSync.writeFileSync(lockPath, '');\n }\n\n try {\n // Acquire lock with retry\n await lock(lockPath, {\n retries: 5,\n stale: 10000, // 10 seconds\n });\n\n const exists = await versionExists(catalogDir, resource.id, resource.version);\n\n if (exists && !options.override) {\n throw new Error(`Failed to write ${resource.id} (${options.type}) as the version ${resource.version} already exists`);\n }\n\n const { markdown, ...frontmatter } = resource;\n\n if (options.versionExistingContent && !exists) {\n const currentResource = await getResource(catalogDir, resource.id);\n\n if (currentResource) {\n if (satisfies(resource.version, `>${currentResource.version}`)) {\n await versionResource(catalogDir, resource.id);\n } else {\n throw new Error(`New version ${resource.version} is not greater than current version ${currentResource.version}`);\n }\n }\n }\n\n const document = matter.stringify(markdown.trim(), frontmatter);\n fsSync.writeFileSync(lockPath, document);\n } finally {\n // Always release the lock\n await unlock(lockPath).catch(() => {});\n }\n};\n\nexport const getResource = async (\n catalogDir: string,\n id?: string,\n version?: string,\n options?: { type: string; attachSchema?: boolean },\n filePath?: string\n): Promise<Resource | undefined> => {\n const attachSchema = options?.attachSchema || false;\n const file = filePath || (id ? await findFileById(catalogDir, id, version) : undefined);\n if (!file || !fsSync.existsSync(file)) return;\n\n const { data, content } = matter.read(file);\n\n if (attachSchema && data?.schemaPath) {\n const resourceDirectory = dirname(file);\n const pathToSchema = join(resourceDirectory, data.schemaPath);\n if (fsSync.existsSync(pathToSchema)) {\n const schema = fsSync.readFileSync(pathToSchema, 'utf8');\n // Try to parse the schema\n try {\n data.schema = JSON.parse(schema);\n } catch (error) {\n data.schema = schema;\n }\n }\n }\n\n return {\n ...data,\n markdown: content.trim(),\n } as Resource;\n};\n\nexport const getResourcePath = async (catalogDir: string, id: string, version?: string) => {\n const file = await findFileById(catalogDir, id, version);\n if (!file) return;\n\n return {\n fullPath: file,\n relativePath: file.replace(catalogDir, ''),\n directory: dirname(file.replace(catalogDir, '')),\n };\n};\n\nexport const getResourceFolderName = async (catalogDir: string, id: string, version?: string) => {\n const paths = await getResourcePath(catalogDir, id, version);\n if (!paths) return;\n return paths?.directory.split(path.sep).filter(Boolean).pop();\n};\n\nexport const toResource = async (catalogDir: string, rawContents: string) => {\n const { data, content } = matter(rawContents);\n return {\n ...data,\n markdown: content.trim(),\n } as Resource;\n};\n\nexport const getResources = async (\n catalogDir: string,\n {\n type,\n latestOnly = false,\n ignore = [],\n pattern = '',\n attachSchema = false,\n }: { type: string; pattern?: string; latestOnly?: boolean; ignore?: string[]; attachSchema?: boolean }\n): Promise<Resource[] | undefined> => {\n const ignoreList = latestOnly ? `**/versioned/**` : '';\n const filePattern = pattern || `${catalogDir}/**/${type}/**/index.{md,mdx}`;\n const files = await getFiles(filePattern, [ignoreList, ...ignore]);\n\n if (files.length === 0) return;\n\n return files.map((file) => {\n const { data, content } = matter.read(file);\n\n // Attach the schema if the attachSchema option is set to true\n if (attachSchema && data?.schemaPath) {\n const resourceDirectory = dirname(file);\n const pathToSchema = join(resourceDirectory, data.schemaPath);\n if (fsSync.existsSync(pathToSchema)) {\n const schema = fsSync.readFileSync(pathToSchema, 'utf8');\n // Try to parse the schema\n try {\n data.schema = JSON.parse(schema);\n } catch (error) {\n data.schema = schema;\n }\n }\n }\n return {\n ...data,\n markdown: content.trim(),\n } as Resource;\n });\n};\n\nexport const rmResourceById = async (\n catalogDir: string,\n id: string,\n version?: string,\n options?: { type: string; persistFiles?: boolean }\n) => {\n const files = await getFiles(`${catalogDir}/**/index.{md,mdx}`);\n\n const matchedFiles = await searchFilesForId(files, id, version);\n\n if (matchedFiles.length === 0) {\n throw new Error(`No ${options?.type || 'resource'} found with id: ${id}`);\n }\n\n if (options?.persistFiles) {\n await Promise.all(\n matchedFiles.map(async (file) => {\n await fs.rm(file, { recursive: true });\n // Verify file is actually removed\n await waitForFileRemoval(file);\n })\n );\n } else {\n await Promise.all(\n matchedFiles.map(async (file) => {\n const directory = dirname(file);\n await fs.rm(directory, { recursive: true, force: true });\n // Verify directory is actually removed\n await waitForFileRemoval(directory);\n })\n );\n }\n};\n\n// Helper function to ensure file/directory is completely removed\nconst waitForFileRemoval = async (path: string, maxRetries: number = 50, delay: number = 10): Promise<void> => {\n for (let i = 0; i < maxRetries; i++) {\n try {\n await fs.access(path);\n // If access succeeds, file still exists, wait and retry\n await new Promise((resolve) => setTimeout(resolve, delay));\n } catch (error) {\n // If access fails, file is removed\n return;\n }\n }\n // If we reach here, file still exists after all retries\n throw new Error(`File/directory ${path} was not removed after ${maxRetries} attempts`);\n};\n\nexport const addFileToResource = async (\n catalogDir: string,\n id: string,\n file: { content: string; fileName: string },\n version?: string\n) => {\n const pathToResource = await findFileById(catalogDir, id, version);\n\n if (!pathToResource) throw new Error('Cannot find directory to write file to');\n\n let fileContent = file.content.trim();\n\n try {\n const json = JSON.parse(fileContent);\n fileContent = JSON.stringify(json, null, 2);\n } catch (error) {\n // Just silently fail if the file is not valid JSON\n // Write it as it is\n }\n\n fsSync.writeFileSync(join(dirname(pathToResource), file.fileName), fileContent);\n};\n\nexport const getFileFromResource = async (catalogDir: string, id: string, file: { fileName: string }, version?: string) => {\n const pathToResource = await findFileById(catalogDir, id, version);\n\n if (!pathToResource) throw new Error('Cannot find directory of resource');\n\n const exists = await fs\n .access(join(dirname(pathToResource), file.fileName))\n .then(() => true)\n .catch(() => false);\n if (!exists) throw new Error(`File ${file.fileName} does not exist in resource ${id} v(${version})`);\n\n return fsSync.readFileSync(join(dirname(pathToResource), file.fileName), 'utf-8');\n};\nexport const getVersionedDirectory = (sourceDirectory: string, version: any): string => {\n return join(sourceDirectory, 'versioned', version);\n};\n\nexport const isLatestVersion = async (catalogDir: string, id: string, version?: string) => {\n const resource = await getResource(catalogDir, id, version);\n if (!resource) return false;\n\n const pathToResource = await getResourcePath(catalogDir, id, version);\n\n return !pathToResource?.relativePath.replace(/\\\\/g, '/').includes('/versioned/');\n};\n"],"mappings":"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","names":["path","join","path","fsSync","fs","matter","dirname","join","matter","fsSync","satisfies","basename","matter","dirname","join","fsSync","path","fsSync","matter","fs","join"]}
|
|
1
|
+
{"version":3,"sources":["../src/custom-docs.ts","../src/internal/utils.ts","../src/internal/resources.ts"],"sourcesContent":["import path, { join } from 'node:path';\nimport { readMdxFile } from './internal/utils';\nimport type { CustomDoc } from './types';\nimport fsSync from 'node:fs';\nimport fs from 'node:fs/promises';\nimport matter from 'gray-matter';\nimport { getResources } from './internal/resources';\nimport slugify from 'slugify';\n\n/**\n * Returns a custom doc from EventCatalog by the given file path.\n *\n * @example\n * ```ts\n * import utils from '@eventcatalog/utils';\n *\n * const { getCustomDoc } = utils('/path/to/eventcatalog');\n *\n * // Gets the custom doc by the given file path\n * const customDoc = await getCustomDoc('/guides/inventory-management.mdx');\n * ```\n */\nexport const getCustomDoc =\n (directory: string) =>\n async (filePath: string): Promise<CustomDoc | undefined> => {\n const fullPath = path.join(directory, filePath);\n const fullPathWithExtension = fullPath.endsWith('.mdx') ? fullPath : `${fullPath}.mdx`;\n const fileExists = fsSync.existsSync(fullPathWithExtension);\n if (!fileExists) {\n return undefined;\n }\n return readMdxFile(fullPathWithExtension) as Promise<CustomDoc>;\n };\n\n/**\n * Returns all custom docs for the project.\n *\n * @example\n * ```ts\n * import utils from '@eventcatalog/utils';\n *\n * const { getCustomDocs } = utils('/path/to/eventcatalog');\n *\n * // Gets all custom docs from the catalog\n * const customDocs = await getCustomDocs();\n *\n * // Gets all custom docs from the given path\n * const customDocs = await getCustomDocs({ path: '/guides' });\n * ```\n */\nexport const getCustomDocs =\n (directory: string) =>\n async (options?: { path?: string }): Promise<CustomDoc[]> => {\n if (options?.path) {\n const pattern = `${directory}/${options.path}/**/*.{md,mdx}`;\n return getResources(directory, { type: 'docs', pattern }) as Promise<CustomDoc[]>;\n }\n return getResources(directory, { type: 'docs', pattern: `${directory}/**/*.{md,mdx}` }) as Promise<CustomDoc[]>;\n };\n\n/**\n * Write a custom doc to EventCatalog.\n *\n * You can optionally override the path of the custom doc.\n *\n * @example\n * ```ts\n * import utils from '@eventcatalog/utils';\n *\n * const { writeCustomDoc } = utils('/path/to/eventcatalog');\n *\n * // Write a custom doc to the catalog\n * // Custom doc would be written to docs/inventory-management.mdx\n * await writeCustomDoc({\n * title: 'Inventory Management',\n * summary: 'This is a summary',\n * owners: ['John Doe'],\n * badges: [{ content: 'Badge', backgroundColor: 'red', textColor: 'white' }],\n * markdown: '# Hello world',\n * fileName: 'inventory-management',\n * });\n *\n * // Write a custom doc to the catalog but override the path\n * // Custom doc would be written to docs/guides/inventory-management/introduction.mdx\n * await writeCustomDoc({\n * title: 'Inventory Management',\n * summary: 'This is a summary',\n * owners: ['John Doe'],\n * badges: [{ content: 'Badge', backgroundColor: 'red', textColor: 'white' }],\n * markdown: '# Hello world',\n * fileName: 'introduction',\n * }, { path: \"/guides/inventory-management\"});\n * ```\n */\nexport const writeCustomDoc =\n (directory: string) =>\n async (customDoc: CustomDoc, options: { path?: string } = { path: '' }): Promise<void> => {\n const { fileName, ...rest } = customDoc;\n const name = fileName || slugify(customDoc.title, { lower: true });\n const withExtension = name.endsWith('.mdx') ? name : `${name}.mdx`;\n const fullPath = path.join(directory, options.path || '', withExtension);\n\n fsSync.mkdirSync(path.dirname(fullPath), { recursive: true });\n const document = matter.stringify(customDoc.markdown.trim(), rest);\n fsSync.writeFileSync(fullPath, document);\n };\n\n/**\n * Delete a custom doc by its' path\n *\n * @example\n * ```ts\n * import utils from '@eventcatalog/utils';\n *\n * const { rmCustomDoc } = utils('/path/to/eventcatalog');\n *\n * // removes a custom doc at the given path\n * // Removes the custom doc at docs/guides/inventory-management/introduction.mdx\n * await rmCustomDoc('/guides/inventory-management/introduction');\n * ```\n */\nexport const rmCustomDoc = (directory: string) => async (filePath: string) => {\n const withExtension = filePath.endsWith('.mdx') ? filePath : `${filePath}.mdx`;\n await fs.rm(join(directory, withExtension), { recursive: true });\n};\n","import { globSync } from 'glob';\nimport fsSync from 'node:fs';\nimport { copy, CopyFilterAsync, CopyFilterSync } from 'fs-extra';\nimport { join, dirname, normalize, sep as pathSeparator, resolve, basename, relative } from 'node:path';\nimport matter from 'gray-matter';\nimport { satisfies, validRange, valid } from 'semver';\n\n/**\n * Returns true if a given version of a resource id exists in the catalog\n */\nexport const versionExists = async (catalogDir: string, id: string, version: string) => {\n const files = await getFiles(`${catalogDir}/**/index.{md,mdx}`);\n const matchedFiles = (await searchFilesForId(files, id, version)) || [];\n return matchedFiles.length > 0;\n};\n\nexport const findFileById = async (catalogDir: string, id: string, version?: string): Promise<string | undefined> => {\n const files = await getFiles(`${catalogDir}/**/index.{md,mdx}`);\n\n const matchedFiles = (await searchFilesForId(files, id)) || [];\n const latestVersion = matchedFiles.find((path) => !path.includes('versioned'));\n\n // If no version is provided, return the latest version\n if (!version) {\n return latestVersion;\n }\n\n // map files into gray matter to get versions\n const parsedFiles = matchedFiles.map((path) => {\n const { data } = matter.read(path);\n return { ...data, path };\n }) as any[];\n\n // Handle 'latest' version - return the latest (non-versioned) file\n if (version === 'latest') {\n return latestVersion;\n }\n\n // First, check for exact version match (handles non-semver versions like '1', '2', etc.)\n const exactMatch = parsedFiles.find((c) => c.version === version);\n if (exactMatch) {\n return exactMatch.path;\n }\n\n // Try semver range matching\n const semverRange = validRange(version);\n\n if (semverRange) {\n const match = parsedFiles.filter((c) => {\n try {\n return satisfies(c.version, semverRange);\n } catch (error) {\n // If satisfies fails (e.g., comparing semver range with non-semver version), skip this file\n return false;\n }\n });\n return match.length > 0 ? match[0].path : undefined;\n }\n\n // If no exact match and no valid semver range, return undefined\n return undefined;\n};\n\nexport const getFiles = async (pattern: string, ignore: string | string[] = '') => {\n try {\n // 1. Normalize the input pattern to handle mixed separators potentially\n const normalizedInputPattern = normalize(pattern);\n\n // 2. Determine the absolute base directory (cwd for glob)\n // Resolve ensures it's absolute. Handles cases with/without globstar.\n const absoluteBaseDir = resolve(\n normalizedInputPattern.includes('**') ? normalizedInputPattern.split('**')[0] : dirname(normalizedInputPattern)\n );\n\n // 3. Determine the pattern part relative to the absolute base directory\n // We extract the part of the normalized pattern that comes *after* the absoluteBaseDir\n let relativePattern = relative(absoluteBaseDir, normalizedInputPattern);\n\n // On Windows, relative() might return empty string if paths are identical,\n // or might need normalization if the original pattern didn't have `**`\n // Example: pattern = 'dir/file.md', absoluteBaseDir='.../dir', normalized='...\\dir\\file.md'\n // relative() -> 'file.md'\n // Example: pattern = 'dir/**/file.md', absoluteBaseDir='.../dir', normalized='...\\dir\\**\\file.md'\n // relative() -> '**\\file.md'\n // Convert separators in the relative pattern to forward slashes for glob\n relativePattern = relativePattern.replace(/\\\\/g, '/');\n\n const ignoreList = Array.isArray(ignore) ? ignore : [ignore];\n\n const files = globSync(relativePattern, {\n cwd: absoluteBaseDir,\n ignore: ['node_modules/**', ...ignoreList],\n absolute: true,\n nodir: true,\n });\n\n // 5. Normalize results for consistency before returning\n return files.map(normalize);\n } catch (error: any) {\n // Add more diagnostic info to the error\n const absoluteBaseDirForError = resolve(\n normalize(pattern).includes('**') ? normalize(pattern).split('**')[0] : dirname(normalize(pattern))\n );\n const relativePatternForError = relative(absoluteBaseDirForError, normalize(pattern)).replace(/\\\\/g, '/');\n throw new Error(\n `Error finding files for pattern \"${pattern}\" (using cwd: \"${absoluteBaseDirForError}\", globPattern: \"${relativePatternForError}\"): ${error.message}`\n );\n }\n};\n\nexport const readMdxFile = async (path: string) => {\n const { data } = matter.read(path);\n const { markdown, ...frontmatter } = data;\n return { ...frontmatter, markdown };\n};\n\nexport const searchFilesForId = async (files: string[], id: string, version?: string) => {\n // Escape the id to avoid regex issues\n const escapedId = id.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const idRegex = new RegExp(`^id:\\\\s*(['\"]|>-)?\\\\s*${escapedId}['\"]?\\\\s*$`, 'm');\n\n const versionRegex = new RegExp(`^version:\\\\s*['\"]?${version}['\"]?\\\\s*$`, 'm');\n\n const matches = files.map((file) => {\n const content = fsSync.readFileSync(file, 'utf-8');\n const hasIdMatch = content.match(idRegex);\n\n // Check version if provided\n if (version && !content.match(versionRegex)) {\n return undefined;\n }\n\n if (hasIdMatch) {\n return file;\n }\n });\n\n return matches.filter(Boolean).filter((file) => file !== undefined);\n};\n\n/**\n * Function to copy a directory from source to target, uses a tmp directory\n * @param catalogDir\n * @param source\n * @param target\n * @param filter\n */\nexport const copyDir = async (catalogDir: string, source: string, target: string, filter?: CopyFilterAsync | CopyFilterSync) => {\n const tmpDirectory = join(catalogDir, 'tmp');\n fsSync.mkdirSync(tmpDirectory, { recursive: true });\n\n // Copy everything over\n await copy(source, tmpDirectory, {\n overwrite: true,\n filter,\n });\n\n await copy(tmpDirectory, target, {\n overwrite: true,\n filter,\n });\n\n // Remove the tmp directory\n fsSync.rmSync(tmpDirectory, { recursive: true });\n};\n\n// Makes sure values in sends/recieves are unique\nexport const uniqueVersions = (messages: { id: string; version: string }[]): { id: string; version: string }[] => {\n const uniqueSet = new Set();\n\n return messages.filter((message) => {\n const key = `${message.id}-${message.version}`;\n if (!uniqueSet.has(key)) {\n uniqueSet.add(key);\n return true;\n }\n return false;\n });\n};\n","import { dirname, join } from 'path';\nimport { copyDir, findFileById, getFiles, searchFilesForId, versionExists } from './utils';\nimport matter from 'gray-matter';\nimport fs from 'node:fs/promises';\nimport fsSync from 'node:fs';\nimport { Message, Service, CustomDoc } from '../types';\nimport { satisfies } from 'semver';\nimport { lock, unlock } from 'proper-lockfile';\nimport { basename } from 'node:path';\nimport path from 'node:path';\n\ntype Resource = Service | Message | CustomDoc;\n\nexport const versionResource = async (catalogDir: string, id: string) => {\n // Find all the events in the directory\n const files = await getFiles(`${catalogDir}/**/index.{md,mdx}`);\n const matchedFiles = await searchFilesForId(files, id);\n\n if (matchedFiles.length === 0) {\n throw new Error(`No resource found with id: ${id}`);\n }\n\n // Event that is in the route of the project\n const file = matchedFiles[0];\n const sourceDirectory = dirname(file).replace(/\\/versioned?\\/[^/]+\\//, '/');\n const { data: { version = '0.0.1' } = {} } = matter.read(file);\n const targetDirectory = getVersionedDirectory(sourceDirectory, version);\n\n fsSync.mkdirSync(targetDirectory, { recursive: true });\n\n const ignoreListToCopy = ['events', 'commands', 'queries', 'versioned'];\n\n // Copy the event to the versioned directory\n await copyDir(catalogDir, sourceDirectory, targetDirectory, (src) => {\n // get the folder name\n const folderName = basename(src);\n\n if (ignoreListToCopy.includes(folderName)) {\n return false;\n }\n return true;\n });\n\n // Remove all the files in the root of the resource as they have now been versioned\n await fs.readdir(sourceDirectory).then(async (resourceFiles) => {\n await Promise.all(\n resourceFiles.map(async (file) => {\n // Dont remove anything in the ignore list\n if (ignoreListToCopy.includes(file)) {\n return;\n }\n if (file !== 'versioned') {\n fsSync.rmSync(join(sourceDirectory, file), { recursive: true });\n }\n })\n );\n });\n};\n\nexport const writeResource = async (\n catalogDir: string,\n resource: Resource,\n options: { path?: string; type: string; override?: boolean; versionExistingContent?: boolean; format?: 'md' | 'mdx' } = {\n path: '',\n type: '',\n override: false,\n versionExistingContent: false,\n format: 'mdx',\n }\n) => {\n const path = options.path || `/${resource.id}`;\n const fullPath = join(catalogDir, path);\n const format = options.format || 'mdx';\n\n // Create directory if it doesn't exist\n fsSync.mkdirSync(fullPath, { recursive: true });\n\n // Create or get lock file path\n const lockPath = join(fullPath, `index.${format}`);\n\n // Ensure the file exists before attempting to lock it\n if (!fsSync.existsSync(lockPath)) {\n fsSync.writeFileSync(lockPath, '');\n }\n\n try {\n // Acquire lock with retry\n await lock(lockPath, {\n retries: 5,\n stale: 10000, // 10 seconds\n });\n\n const exists = await versionExists(catalogDir, resource.id, resource.version);\n\n if (exists && !options.override) {\n throw new Error(`Failed to write ${resource.id} (${options.type}) as the version ${resource.version} already exists`);\n }\n\n const { markdown, ...frontmatter } = resource;\n\n if (options.versionExistingContent && !exists) {\n const currentResource = await getResource(catalogDir, resource.id);\n\n if (currentResource) {\n if (satisfies(resource.version, `>${currentResource.version}`)) {\n await versionResource(catalogDir, resource.id);\n } else {\n throw new Error(`New version ${resource.version} is not greater than current version ${currentResource.version}`);\n }\n }\n }\n\n const document = matter.stringify(markdown.trim(), frontmatter);\n fsSync.writeFileSync(lockPath, document);\n } finally {\n // Always release the lock\n await unlock(lockPath).catch(() => {});\n }\n};\n\nexport const getResource = async (\n catalogDir: string,\n id?: string,\n version?: string,\n options?: { type: string; attachSchema?: boolean },\n filePath?: string\n): Promise<Resource | undefined> => {\n const attachSchema = options?.attachSchema || false;\n const file = filePath || (id ? await findFileById(catalogDir, id, version) : undefined);\n if (!file || !fsSync.existsSync(file)) return;\n\n const { data, content } = matter.read(file);\n\n if (attachSchema && data?.schemaPath) {\n const resourceDirectory = dirname(file);\n const pathToSchema = join(resourceDirectory, data.schemaPath);\n if (fsSync.existsSync(pathToSchema)) {\n const schema = fsSync.readFileSync(pathToSchema, 'utf8');\n // Try to parse the schema\n try {\n data.schema = JSON.parse(schema);\n } catch (error) {\n data.schema = schema;\n }\n }\n }\n\n return {\n ...data,\n markdown: content.trim(),\n } as Resource;\n};\n\nexport const getResourcePath = async (catalogDir: string, id: string, version?: string) => {\n const file = await findFileById(catalogDir, id, version);\n if (!file) return;\n\n return {\n fullPath: file,\n relativePath: file.replace(catalogDir, ''),\n directory: dirname(file.replace(catalogDir, '')),\n };\n};\n\nexport const getResourceFolderName = async (catalogDir: string, id: string, version?: string) => {\n const paths = await getResourcePath(catalogDir, id, version);\n if (!paths) return;\n return paths?.directory.split(path.sep).filter(Boolean).pop();\n};\n\nexport const toResource = async (catalogDir: string, rawContents: string) => {\n const { data, content } = matter(rawContents);\n return {\n ...data,\n markdown: content.trim(),\n } as Resource;\n};\n\nexport const getResources = async (\n catalogDir: string,\n {\n type,\n latestOnly = false,\n ignore = [],\n pattern = '',\n attachSchema = false,\n }: { type: string; pattern?: string; latestOnly?: boolean; ignore?: string[]; attachSchema?: boolean }\n): Promise<Resource[] | undefined> => {\n const ignoreList = latestOnly ? `**/versioned/**` : '';\n const filePattern = pattern || `${catalogDir}/**/${type}/**/index.{md,mdx}`;\n const files = await getFiles(filePattern, [ignoreList, ...ignore]);\n\n if (files.length === 0) return;\n\n return files.map((file) => {\n const { data, content } = matter.read(file);\n\n // Attach the schema if the attachSchema option is set to true\n if (attachSchema && data?.schemaPath) {\n const resourceDirectory = dirname(file);\n const pathToSchema = join(resourceDirectory, data.schemaPath);\n if (fsSync.existsSync(pathToSchema)) {\n const schema = fsSync.readFileSync(pathToSchema, 'utf8');\n // Try to parse the schema\n try {\n data.schema = JSON.parse(schema);\n } catch (error) {\n data.schema = schema;\n }\n }\n }\n return {\n ...data,\n markdown: content.trim(),\n } as Resource;\n });\n};\n\nexport const rmResourceById = async (\n catalogDir: string,\n id: string,\n version?: string,\n options?: { type: string; persistFiles?: boolean }\n) => {\n const files = await getFiles(`${catalogDir}/**/index.{md,mdx}`);\n\n const matchedFiles = await searchFilesForId(files, id, version);\n\n if (matchedFiles.length === 0) {\n throw new Error(`No ${options?.type || 'resource'} found with id: ${id}`);\n }\n\n if (options?.persistFiles) {\n await Promise.all(\n matchedFiles.map(async (file) => {\n await fs.rm(file, { recursive: true });\n // Verify file is actually removed\n await waitForFileRemoval(file);\n })\n );\n } else {\n await Promise.all(\n matchedFiles.map(async (file) => {\n const directory = dirname(file);\n await fs.rm(directory, { recursive: true, force: true });\n // Verify directory is actually removed\n await waitForFileRemoval(directory);\n })\n );\n }\n};\n\n// Helper function to ensure file/directory is completely removed\nconst waitForFileRemoval = async (path: string, maxRetries: number = 50, delay: number = 10): Promise<void> => {\n for (let i = 0; i < maxRetries; i++) {\n try {\n await fs.access(path);\n // If access succeeds, file still exists, wait and retry\n await new Promise((resolve) => setTimeout(resolve, delay));\n } catch (error) {\n // If access fails, file is removed\n return;\n }\n }\n // If we reach here, file still exists after all retries\n throw new Error(`File/directory ${path} was not removed after ${maxRetries} attempts`);\n};\n\nexport const addFileToResource = async (\n catalogDir: string,\n id: string,\n file: { content: string; fileName: string },\n version?: string\n) => {\n const pathToResource = await findFileById(catalogDir, id, version);\n\n if (!pathToResource) throw new Error('Cannot find directory to write file to');\n\n let fileContent = file.content.trim();\n\n try {\n const json = JSON.parse(fileContent);\n fileContent = JSON.stringify(json, null, 2);\n } catch (error) {\n // Just silently fail if the file is not valid JSON\n // Write it as it is\n }\n\n fsSync.writeFileSync(join(dirname(pathToResource), file.fileName), fileContent);\n};\n\nexport const getFileFromResource = async (catalogDir: string, id: string, file: { fileName: string }, version?: string) => {\n const pathToResource = await findFileById(catalogDir, id, version);\n\n if (!pathToResource) throw new Error('Cannot find directory of resource');\n\n const exists = await fs\n .access(join(dirname(pathToResource), file.fileName))\n .then(() => true)\n .catch(() => false);\n if (!exists) throw new Error(`File ${file.fileName} does not exist in resource ${id} v(${version})`);\n\n return fsSync.readFileSync(join(dirname(pathToResource), file.fileName), 'utf-8');\n};\nexport const getVersionedDirectory = (sourceDirectory: string, version: any): string => {\n return join(sourceDirectory, 'versioned', version);\n};\n\nexport const isLatestVersion = async (catalogDir: string, id: string, version?: string) => {\n const resource = await getResource(catalogDir, id, version);\n if (!resource) return false;\n\n const pathToResource = await getResourcePath(catalogDir, id, version);\n\n return !pathToResource?.relativePath.replace(/\\\\/g, '/').includes('/versioned/');\n};\n"],"mappings":";AAAA,OAAO,QAAQ,QAAAA,aAAY;;;ACA3B,SAAS,gBAAgB;AAEzB,SAAS,YAA6C;AACtD,SAAS,MAAM,SAAS,WAAiC,SAAmB,gBAAgB;AAC5F,OAAO,YAAY;AACnB,SAAS,WAAW,kBAAyB;AA0DtC,IAAM,WAAW,OAAO,SAAiB,SAA4B,OAAO;AACjF,MAAI;AAEF,UAAM,yBAAyB,UAAU,OAAO;AAIhD,UAAM,kBAAkB;AAAA,MACtB,uBAAuB,SAAS,IAAI,IAAI,uBAAuB,MAAM,IAAI,EAAE,CAAC,IAAI,QAAQ,sBAAsB;AAAA,IAChH;AAIA,QAAI,kBAAkB,SAAS,iBAAiB,sBAAsB;AAStE,sBAAkB,gBAAgB,QAAQ,OAAO,GAAG;AAEpD,UAAM,aAAa,MAAM,QAAQ,MAAM,IAAI,SAAS,CAAC,MAAM;AAE3D,UAAM,QAAQ,SAAS,iBAAiB;AAAA,MACtC,KAAK;AAAA,MACL,QAAQ,CAAC,mBAAmB,GAAG,UAAU;AAAA,MACzC,UAAU;AAAA,MACV,OAAO;AAAA,IACT,CAAC;AAGD,WAAO,MAAM,IAAI,SAAS;AAAA,EAC5B,SAAS,OAAY;AAEnB,UAAM,0BAA0B;AAAA,MAC9B,UAAU,OAAO,EAAE,SAAS,IAAI,IAAI,UAAU,OAAO,EAAE,MAAM,IAAI,EAAE,CAAC,IAAI,QAAQ,UAAU,OAAO,CAAC;AAAA,IACpG;AACA,UAAM,0BAA0B,SAAS,yBAAyB,UAAU,OAAO,CAAC,EAAE,QAAQ,OAAO,GAAG;AACxG,UAAM,IAAI;AAAA,MACR,oCAAoC,OAAO,kBAAkB,uBAAuB,oBAAoB,uBAAuB,OAAO,MAAM,OAAO;AAAA,IACrJ;AAAA,EACF;AACF;AAEO,IAAM,cAAc,OAAOC,UAAiB;AACjD,QAAM,EAAE,KAAK,IAAI,OAAO,KAAKA,KAAI;AACjC,QAAM,EAAE,UAAU,GAAG,YAAY,IAAI;AACrC,SAAO,EAAE,GAAG,aAAa,SAAS;AACpC;;;AD/GA,OAAOC,aAAY;AACnB,OAAO,QAAQ;AACf,OAAOC,aAAY;;;AELnB,SAAS,WAAAC,UAAS,QAAAC,aAAY;AAE9B,OAAOC,aAAY;AAEnB,OAAO,YAAY;AAEnB,SAAS,aAAAC,kBAAiB;AAC1B,SAAS,MAAM,cAAc;AA2KtB,IAAM,eAAe,OAC1B,YACA;AAAA,EACE;AAAA,EACA,aAAa;AAAA,EACb,SAAS,CAAC;AAAA,EACV,UAAU;AAAA,EACV,eAAe;AACjB,MACoC;AACpC,QAAM,aAAa,aAAa,oBAAoB;AACpD,QAAM,cAAc,WAAW,GAAG,UAAU,OAAO,IAAI;AACvD,QAAM,QAAQ,MAAM,SAAS,aAAa,CAAC,YAAY,GAAG,MAAM,CAAC;AAEjE,MAAI,MAAM,WAAW,EAAG;AAExB,SAAO,MAAM,IAAI,CAAC,SAAS;AACzB,UAAM,EAAE,MAAM,QAAQ,IAAIC,QAAO,KAAK,IAAI;AAG1C,QAAI,gBAAgB,MAAM,YAAY;AACpC,YAAM,oBAAoBC,SAAQ,IAAI;AACtC,YAAM,eAAeC,MAAK,mBAAmB,KAAK,UAAU;AAC5D,UAAI,OAAO,WAAW,YAAY,GAAG;AACnC,cAAM,SAAS,OAAO,aAAa,cAAc,MAAM;AAEvD,YAAI;AACF,eAAK,SAAS,KAAK,MAAM,MAAM;AAAA,QACjC,SAAS,OAAO;AACd,eAAK,SAAS;AAAA,QAChB;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,MACL,GAAG;AAAA,MACH,UAAU,QAAQ,KAAK;AAAA,IACzB;AAAA,EACF,CAAC;AACH;;;AFjNA,OAAO,aAAa;AAeb,IAAM,eACX,CAAC,cACD,OAAO,aAAqD;AAC1D,QAAM,WAAW,KAAK,KAAK,WAAW,QAAQ;AAC9C,QAAM,wBAAwB,SAAS,SAAS,MAAM,IAAI,WAAW,GAAG,QAAQ;AAChF,QAAM,aAAaC,QAAO,WAAW,qBAAqB;AAC1D,MAAI,CAAC,YAAY;AACf,WAAO;AAAA,EACT;AACA,SAAO,YAAY,qBAAqB;AAC1C;AAkBK,IAAM,gBACX,CAAC,cACD,OAAO,YAAsD;AAC3D,MAAI,SAAS,MAAM;AACjB,UAAM,UAAU,GAAG,SAAS,IAAI,QAAQ,IAAI;AAC5C,WAAO,aAAa,WAAW,EAAE,MAAM,QAAQ,QAAQ,CAAC;AAAA,EAC1D;AACA,SAAO,aAAa,WAAW,EAAE,MAAM,QAAQ,SAAS,GAAG,SAAS,iBAAiB,CAAC;AACxF;AAoCK,IAAM,iBACX,CAAC,cACD,OAAO,WAAsB,UAA6B,EAAE,MAAM,GAAG,MAAqB;AACxF,QAAM,EAAE,UAAU,GAAG,KAAK,IAAI;AAC9B,QAAM,OAAO,YAAY,QAAQ,UAAU,OAAO,EAAE,OAAO,KAAK,CAAC;AACjE,QAAM,gBAAgB,KAAK,SAAS,MAAM,IAAI,OAAO,GAAG,IAAI;AAC5D,QAAM,WAAW,KAAK,KAAK,WAAW,QAAQ,QAAQ,IAAI,aAAa;AAEvE,EAAAA,QAAO,UAAU,KAAK,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAC5D,QAAM,WAAWC,QAAO,UAAU,UAAU,SAAS,KAAK,GAAG,IAAI;AACjE,EAAAD,QAAO,cAAc,UAAU,QAAQ;AACzC;AAgBK,IAAM,cAAc,CAAC,cAAsB,OAAO,aAAqB;AAC5E,QAAM,gBAAgB,SAAS,SAAS,MAAM,IAAI,WAAW,GAAG,QAAQ;AACxE,QAAM,GAAG,GAAGE,MAAK,WAAW,aAAa,GAAG,EAAE,WAAW,KAAK,CAAC;AACjE;","names":["join","path","fsSync","matter","dirname","join","matter","satisfies","matter","dirname","join","fsSync","matter","join"]}
|
package/dist/data-stores.mjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// src/containers.ts
|
|
2
|
-
import fs2 from "fs/promises";
|
|
3
|
-
import { join as join3 } from "path";
|
|
2
|
+
import fs2 from "node:fs/promises";
|
|
3
|
+
import { join as join3 } from "node:path";
|
|
4
4
|
|
|
5
5
|
// src/internal/utils.ts
|
|
6
6
|
import { globSync } from "glob";
|
|
7
|
-
import fsSync from "fs";
|
|
7
|
+
import fsSync from "node:fs";
|
|
8
8
|
import { copy } from "fs-extra";
|
|
9
|
-
import { join, dirname, normalize, resolve, relative } from "path";
|
|
9
|
+
import { join, dirname, normalize, resolve, relative } from "node:path";
|
|
10
10
|
import matter from "gray-matter";
|
|
11
11
|
import { satisfies, validRange } from "semver";
|
|
12
12
|
var versionExists = async (catalogDir, id, version) => {
|
|
@@ -17,13 +17,13 @@ var versionExists = async (catalogDir, id, version) => {
|
|
|
17
17
|
var findFileById = async (catalogDir, id, version) => {
|
|
18
18
|
const files = await getFiles(`${catalogDir}/**/index.{md,mdx}`);
|
|
19
19
|
const matchedFiles = await searchFilesForId(files, id) || [];
|
|
20
|
-
const latestVersion = matchedFiles.find((
|
|
20
|
+
const latestVersion = matchedFiles.find((path) => !path.includes("versioned"));
|
|
21
21
|
if (!version) {
|
|
22
22
|
return latestVersion;
|
|
23
23
|
}
|
|
24
|
-
const parsedFiles = matchedFiles.map((
|
|
25
|
-
const { data } = matter.read(
|
|
26
|
-
return { ...data, path
|
|
24
|
+
const parsedFiles = matchedFiles.map((path) => {
|
|
25
|
+
const { data } = matter.read(path);
|
|
26
|
+
return { ...data, path };
|
|
27
27
|
});
|
|
28
28
|
if (version === "latest") {
|
|
29
29
|
return latestVersion;
|
|
@@ -104,12 +104,11 @@ var copyDir = async (catalogDir, source, target, filter) => {
|
|
|
104
104
|
// src/internal/resources.ts
|
|
105
105
|
import { dirname as dirname2, join as join2 } from "path";
|
|
106
106
|
import matter2 from "gray-matter";
|
|
107
|
-
import fs from "fs/promises";
|
|
108
|
-
import fsSync2 from "fs";
|
|
107
|
+
import fs from "node:fs/promises";
|
|
108
|
+
import fsSync2 from "node:fs";
|
|
109
109
|
import { satisfies as satisfies2 } from "semver";
|
|
110
110
|
import { lock, unlock } from "proper-lockfile";
|
|
111
|
-
import { basename as basename2 } from "path";
|
|
112
|
-
import path from "path";
|
|
111
|
+
import { basename as basename2 } from "node:path";
|
|
113
112
|
var versionResource = async (catalogDir, id) => {
|
|
114
113
|
const files = await getFiles(`${catalogDir}/**/index.{md,mdx}`);
|
|
115
114
|
const matchedFiles = await searchFilesForId(files, id);
|
|
@@ -149,8 +148,8 @@ var writeResource = async (catalogDir, resource, options = {
|
|
|
149
148
|
versionExistingContent: false,
|
|
150
149
|
format: "mdx"
|
|
151
150
|
}) => {
|
|
152
|
-
const
|
|
153
|
-
const fullPath = join2(catalogDir,
|
|
151
|
+
const path = options.path || `/${resource.id}`;
|
|
152
|
+
const fullPath = join2(catalogDir, path);
|
|
154
153
|
const format = options.format || "mdx";
|
|
155
154
|
fsSync2.mkdirSync(fullPath, { recursive: true });
|
|
156
155
|
const lockPath = join2(fullPath, `index.${format}`);
|
|
@@ -270,16 +269,16 @@ var rmResourceById = async (catalogDir, id, version, options) => {
|
|
|
270
269
|
);
|
|
271
270
|
}
|
|
272
271
|
};
|
|
273
|
-
var waitForFileRemoval = async (
|
|
272
|
+
var waitForFileRemoval = async (path, maxRetries = 50, delay = 10) => {
|
|
274
273
|
for (let i = 0; i < maxRetries; i++) {
|
|
275
274
|
try {
|
|
276
|
-
await fs.access(
|
|
275
|
+
await fs.access(path);
|
|
277
276
|
await new Promise((resolve2) => setTimeout(resolve2, delay));
|
|
278
277
|
} catch (error) {
|
|
279
278
|
return;
|
|
280
279
|
}
|
|
281
280
|
}
|
|
282
|
-
throw new Error(`File/directory ${
|
|
281
|
+
throw new Error(`File/directory ${path} was not removed after ${maxRetries} attempts`);
|
|
283
282
|
};
|
|
284
283
|
var addFileToResource = async (catalogDir, id, file, version) => {
|
|
285
284
|
const pathToResource = await findFileById(catalogDir, id, version);
|
|
@@ -305,8 +304,8 @@ var writeContainer = (directory) => async (data, options = {
|
|
|
305
304
|
format: "mdx"
|
|
306
305
|
}) => writeResource(directory, { ...data }, { ...options, type: "container" });
|
|
307
306
|
var versionContainer = (directory) => async (id) => versionResource(directory, id);
|
|
308
|
-
var rmContainer = (directory) => async (
|
|
309
|
-
await fs2.rm(join3(directory,
|
|
307
|
+
var rmContainer = (directory) => async (path) => {
|
|
308
|
+
await fs2.rm(join3(directory, path), { recursive: true });
|
|
310
309
|
};
|
|
311
310
|
var rmContainerById = (directory) => async (id, version, persistFiles) => {
|
|
312
311
|
await rmResourceById(directory, id, version, { type: "container", persistFiles });
|
package/dist/data-stores.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/containers.ts","../src/internal/utils.ts","../src/internal/resources.ts","../src/data-stores.ts"],"sourcesContent":["import fs from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { findFileById } from './internal/utils';\nimport type { Container } from './types';\nimport {\n addFileToResource,\n getResource,\n getResourcePath,\n getResources,\n rmResourceById,\n versionResource,\n writeResource,\n} from './internal/resources';\n\n/**\n * Returns an container (e.g. data store) from EventCatalog.\n *\n * You can optionally specify a version to get a specific version of the container\n *\n * @example\n * ```ts\n * import utils from '@eventcatalog/utils';\n *\n * const { getContainer } = utils('/path/to/eventcatalog');\n *\n * // Gets the latest version of the container\n * const container = await getContainer('User');\n *\n * // Gets a version of the entity\n * const container = await getContainer('User', '0.0.1');\n *\n * ```\n */\nexport const getContainer =\n (directory: string) =>\n async (id: string, version?: string): Promise<Container> =>\n getResource(directory, id, version, { type: 'container' }) as Promise<Container>;\n\n/**\n * Returns all containers (e.g. data stores) from EventCatalog.\n *\n * You can optionally specify if you want to get the latest version of the containers.\n *\n * @example\n * ```ts\n * import utils from '@eventcatalog/utils';\n *\n * const { getContainers } = utils('/path/to/eventcatalog');\n *\n * // Gets all containers (and versions) from the catalog\n * const containers = await getContainers();\n *\n * // Gets all entities (only latest version) from the catalog\n * const containers = await getContainers({ latestOnly: true });\n *\n * ```\n */\nexport const getContainers =\n (directory: string) =>\n async (options?: { latestOnly?: boolean }): Promise<Container[]> =>\n getResources(directory, { type: 'containers', latestOnly: options?.latestOnly }) as Promise<Container[]>;\n\n/**\n * Write a container (e.g. data store) to EventCatalog.\n */\nexport const writeContainer =\n (directory: string) =>\n async (\n data: Container,\n options: { path?: string; override?: boolean; versionExistingContent?: boolean; format?: 'md' | 'mdx' } = {\n path: '',\n override: false,\n format: 'mdx',\n }\n ) =>\n writeResource(directory, { ...data }, { ...options, type: 'container' });\n\n/**\n * Version an container (e.g. data store) by its id.\n *\n * Takes the latest container and moves it to a versioned directory.\n * All files with this container are also versioned (e.g /containers/orders-db/schema.json)\n *\n * @example\n * ```ts\n * import utils from '@eventcatalog/utils';\n *\n * const { versionContainer } = utils('/path/to/eventcatalog');\n *\n * // moves the latest orders-db container to a versioned directory\n * // the version within that container is used as the version number.\n * await versionContainer('orders-db');\n *\n * ```\n */\nexport const versionContainer = (directory: string) => async (id: string) => versionResource(directory, id);\n\n/**\n * Delete an container (e.g. data store) at its given path.\n *\n * @example\n * ```ts\n * import utils from '@eventcatalog/utils';\n *\n * const { rmContainer } = utils('/path/to/eventcatalog');\n *\n * // removes an container at the given path (containers dir is appended to the given path)\n * // Removes the container at containers/orders-db\n * await rmContainer('/orders-db');\n * ```\n */\nexport const rmContainer = (directory: string) => async (path: string) => {\n await fs.rm(join(directory, path), { recursive: true });\n};\n\n/**\n * Delete an container (e.g. data store) by its id.\n *\n * Optionally specify a version to delete a specific version of the container.\n *\n * @example\n * ```ts\n * import utils from '@eventcatalog/utils';\n *\n * const { rmContainerById } = utils('/path/to/eventcatalog');\n *\n * // deletes the latest orders-db container\n * await rmContainerById('orders-db');\n *\n * // deletes a specific version of the orders-db container\n * await rmContainerById('orders-db', '0.0.1');\n * ```\n */\nexport const rmContainerById = (directory: string) => async (id: string, version?: string, persistFiles?: boolean) => {\n await rmResourceById(directory, id, version, { type: 'container', persistFiles });\n};\n\n/**\n * Check to see if the catalog has a version for the given container (e.g. data store).\n *\n * @example\n * ```ts\n * import utils from '@eventcatalog/utils';\n *\n * const { containerHasVersion } = utils('/path/to/eventcatalog');\n *\n * // returns true if version is found for the given entity and version (supports semver)\n * await containerHasVersion('orders-db', '0.0.1');\n * await containerHasVersion('orders-db', 'latest');\n * await containerHasVersion('orders-db', '0.0.x');\n *\n * ```\n */\nexport const containerHasVersion = (directory: string) => async (id: string, version?: string) => {\n const file = await findFileById(directory, id, version);\n return !!file;\n};\n\n/**\n * Add a file to a container (e.g. data store) by it's id.\n *\n * Optionally specify a version to add a file to a specific version of the container.\n *\n * @example\n * ```ts\n * import utils from '@eventcatalog/utils';\n *\n * const { addFileToContainer } = utils('/path/to/eventcatalog');\n *\n * // adds a file to the latest InventoryAdjusted event\n * await addFileToContainer('InventoryAdjusted', { content: 'Hello world', fileName: 'hello.txt' });\n *\n * // adds a file to a specific version of the InventoryAdjusted event\n * await addFileToContainer('InventoryAdjusted', { content: 'Hello world', fileName: 'hello.txt' }, '0.0.1');\n *\n * ```\n */\nexport const addFileToContainer =\n (directory: string) => async (id: string, file: { content: string; fileName: string }, version?: string) =>\n addFileToResource(directory, id, file, version);\n\n/**\n * Write an data store (e.g. data store) to a service in EventCatalog.\n *\n * You can optionally override the path of the data store.\n *\n * @example\n * ```ts\n * import utils from '@eventcatalog/utils';\n *\n * const { writeContainerToService } = utils('/path/to/eventcatalog');\n *\n * // Write a container to a given service in the catalog\n * // Container would be written to services/Inventory/containers/orders-db\n * await writeContainerToService({\n * id: 'orders-db',\n * name: 'Orders DB',\n * version: '0.0.1',\n * summary: 'This is a summary',\n * markdown: '# Hello world',\n * container_type: 'database',\n * }, { id: 'Inventory' });\n * ```\n */\nexport const writeContainerToService =\n (directory: string) =>\n async (\n container: Container,\n service: { id: string; version?: string },\n options: { path?: string; format?: 'md' | 'mdx'; override?: boolean } = { path: '', format: 'mdx', override: false }\n ) => {\n const resourcePath = await getResourcePath(directory, service.id, service.version);\n if (!resourcePath) {\n throw new Error('Service not found');\n }\n\n let pathForContainer =\n service.version && service.version !== 'latest'\n ? `${resourcePath.directory}/versioned/${service.version}/containers`\n : `${resourcePath.directory}/containers`;\n pathForContainer = join(pathForContainer, container.id);\n await writeResource(directory, { ...container }, { ...options, path: pathForContainer, type: 'container' });\n };\n","import { globSync } from 'glob';\nimport fsSync from 'node:fs';\nimport { copy, CopyFilterAsync, CopyFilterSync } from 'fs-extra';\nimport { join, dirname, normalize, sep as pathSeparator, resolve, basename, relative } from 'node:path';\nimport matter from 'gray-matter';\nimport { satisfies, validRange, valid } from 'semver';\n\n/**\n * Returns true if a given version of a resource id exists in the catalog\n */\nexport const versionExists = async (catalogDir: string, id: string, version: string) => {\n const files = await getFiles(`${catalogDir}/**/index.{md,mdx}`);\n const matchedFiles = (await searchFilesForId(files, id, version)) || [];\n return matchedFiles.length > 0;\n};\n\nexport const findFileById = async (catalogDir: string, id: string, version?: string): Promise<string | undefined> => {\n const files = await getFiles(`${catalogDir}/**/index.{md,mdx}`);\n\n const matchedFiles = (await searchFilesForId(files, id)) || [];\n const latestVersion = matchedFiles.find((path) => !path.includes('versioned'));\n\n // If no version is provided, return the latest version\n if (!version) {\n return latestVersion;\n }\n\n // map files into gray matter to get versions\n const parsedFiles = matchedFiles.map((path) => {\n const { data } = matter.read(path);\n return { ...data, path };\n }) as any[];\n\n // Handle 'latest' version - return the latest (non-versioned) file\n if (version === 'latest') {\n return latestVersion;\n }\n\n // First, check for exact version match (handles non-semver versions like '1', '2', etc.)\n const exactMatch = parsedFiles.find((c) => c.version === version);\n if (exactMatch) {\n return exactMatch.path;\n }\n\n // Try semver range matching\n const semverRange = validRange(version);\n\n if (semverRange) {\n const match = parsedFiles.filter((c) => {\n try {\n return satisfies(c.version, semverRange);\n } catch (error) {\n // If satisfies fails (e.g., comparing semver range with non-semver version), skip this file\n return false;\n }\n });\n return match.length > 0 ? match[0].path : undefined;\n }\n\n // If no exact match and no valid semver range, return undefined\n return undefined;\n};\n\nexport const getFiles = async (pattern: string, ignore: string | string[] = '') => {\n try {\n // 1. Normalize the input pattern to handle mixed separators potentially\n const normalizedInputPattern = normalize(pattern);\n\n // 2. Determine the absolute base directory (cwd for glob)\n // Resolve ensures it's absolute. Handles cases with/without globstar.\n const absoluteBaseDir = resolve(\n normalizedInputPattern.includes('**') ? normalizedInputPattern.split('**')[0] : dirname(normalizedInputPattern)\n );\n\n // 3. Determine the pattern part relative to the absolute base directory\n // We extract the part of the normalized pattern that comes *after* the absoluteBaseDir\n let relativePattern = relative(absoluteBaseDir, normalizedInputPattern);\n\n // On Windows, relative() might return empty string if paths are identical,\n // or might need normalization if the original pattern didn't have `**`\n // Example: pattern = 'dir/file.md', absoluteBaseDir='.../dir', normalized='...\\dir\\file.md'\n // relative() -> 'file.md'\n // Example: pattern = 'dir/**/file.md', absoluteBaseDir='.../dir', normalized='...\\dir\\**\\file.md'\n // relative() -> '**\\file.md'\n // Convert separators in the relative pattern to forward slashes for glob\n relativePattern = relativePattern.replace(/\\\\/g, '/');\n\n const ignoreList = Array.isArray(ignore) ? ignore : [ignore];\n\n const files = globSync(relativePattern, {\n cwd: absoluteBaseDir,\n ignore: ['node_modules/**', ...ignoreList],\n absolute: true,\n nodir: true,\n });\n\n // 5. Normalize results for consistency before returning\n return files.map(normalize);\n } catch (error: any) {\n // Add more diagnostic info to the error\n const absoluteBaseDirForError = resolve(\n normalize(pattern).includes('**') ? normalize(pattern).split('**')[0] : dirname(normalize(pattern))\n );\n const relativePatternForError = relative(absoluteBaseDirForError, normalize(pattern)).replace(/\\\\/g, '/');\n throw new Error(\n `Error finding files for pattern \"${pattern}\" (using cwd: \"${absoluteBaseDirForError}\", globPattern: \"${relativePatternForError}\"): ${error.message}`\n );\n }\n};\n\nexport const readMdxFile = async (path: string) => {\n const { data } = matter.read(path);\n const { markdown, ...frontmatter } = data;\n return { ...frontmatter, markdown };\n};\n\nexport const searchFilesForId = async (files: string[], id: string, version?: string) => {\n // Escape the id to avoid regex issues\n const escapedId = id.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const idRegex = new RegExp(`^id:\\\\s*(['\"]|>-)?\\\\s*${escapedId}['\"]?\\\\s*$`, 'm');\n\n const versionRegex = new RegExp(`^version:\\\\s*['\"]?${version}['\"]?\\\\s*$`, 'm');\n\n const matches = files.map((file) => {\n const content = fsSync.readFileSync(file, 'utf-8');\n const hasIdMatch = content.match(idRegex);\n\n // Check version if provided\n if (version && !content.match(versionRegex)) {\n return undefined;\n }\n\n if (hasIdMatch) {\n return file;\n }\n });\n\n return matches.filter(Boolean).filter((file) => file !== undefined);\n};\n\n/**\n * Function to copy a directory from source to target, uses a tmp directory\n * @param catalogDir\n * @param source\n * @param target\n * @param filter\n */\nexport const copyDir = async (catalogDir: string, source: string, target: string, filter?: CopyFilterAsync | CopyFilterSync) => {\n const tmpDirectory = join(catalogDir, 'tmp');\n fsSync.mkdirSync(tmpDirectory, { recursive: true });\n\n // Copy everything over\n await copy(source, tmpDirectory, {\n overwrite: true,\n filter,\n });\n\n await copy(tmpDirectory, target, {\n overwrite: true,\n filter,\n });\n\n // Remove the tmp directory\n fsSync.rmSync(tmpDirectory, { recursive: true });\n};\n\n// Makes sure values in sends/recieves are unique\nexport const uniqueVersions = (messages: { id: string; version: string }[]): { id: string; version: string }[] => {\n const uniqueSet = new Set();\n\n return messages.filter((message) => {\n const key = `${message.id}-${message.version}`;\n if (!uniqueSet.has(key)) {\n uniqueSet.add(key);\n return true;\n }\n return false;\n });\n};\n","import { dirname, join } from 'path';\nimport { copyDir, findFileById, getFiles, searchFilesForId, versionExists } from './utils';\nimport matter from 'gray-matter';\nimport fs from 'node:fs/promises';\nimport fsSync from 'node:fs';\nimport { Message, Service, CustomDoc } from '../types';\nimport { satisfies } from 'semver';\nimport { lock, unlock } from 'proper-lockfile';\nimport { basename } from 'node:path';\nimport path from 'node:path';\n\ntype Resource = Service | Message | CustomDoc;\n\nexport const versionResource = async (catalogDir: string, id: string) => {\n // Find all the events in the directory\n const files = await getFiles(`${catalogDir}/**/index.{md,mdx}`);\n const matchedFiles = await searchFilesForId(files, id);\n\n if (matchedFiles.length === 0) {\n throw new Error(`No resource found with id: ${id}`);\n }\n\n // Event that is in the route of the project\n const file = matchedFiles[0];\n const sourceDirectory = dirname(file).replace(/\\/versioned?\\/[^/]+\\//, '/');\n const { data: { version = '0.0.1' } = {} } = matter.read(file);\n const targetDirectory = getVersionedDirectory(sourceDirectory, version);\n\n fsSync.mkdirSync(targetDirectory, { recursive: true });\n\n const ignoreListToCopy = ['events', 'commands', 'queries', 'versioned'];\n\n // Copy the event to the versioned directory\n await copyDir(catalogDir, sourceDirectory, targetDirectory, (src) => {\n // get the folder name\n const folderName = basename(src);\n\n if (ignoreListToCopy.includes(folderName)) {\n return false;\n }\n return true;\n });\n\n // Remove all the files in the root of the resource as they have now been versioned\n await fs.readdir(sourceDirectory).then(async (resourceFiles) => {\n await Promise.all(\n resourceFiles.map(async (file) => {\n // Dont remove anything in the ignore list\n if (ignoreListToCopy.includes(file)) {\n return;\n }\n if (file !== 'versioned') {\n fsSync.rmSync(join(sourceDirectory, file), { recursive: true });\n }\n })\n );\n });\n};\n\nexport const writeResource = async (\n catalogDir: string,\n resource: Resource,\n options: { path?: string; type: string; override?: boolean; versionExistingContent?: boolean; format?: 'md' | 'mdx' } = {\n path: '',\n type: '',\n override: false,\n versionExistingContent: false,\n format: 'mdx',\n }\n) => {\n const path = options.path || `/${resource.id}`;\n const fullPath = join(catalogDir, path);\n const format = options.format || 'mdx';\n\n // Create directory if it doesn't exist\n fsSync.mkdirSync(fullPath, { recursive: true });\n\n // Create or get lock file path\n const lockPath = join(fullPath, `index.${format}`);\n\n // Ensure the file exists before attempting to lock it\n if (!fsSync.existsSync(lockPath)) {\n fsSync.writeFileSync(lockPath, '');\n }\n\n try {\n // Acquire lock with retry\n await lock(lockPath, {\n retries: 5,\n stale: 10000, // 10 seconds\n });\n\n const exists = await versionExists(catalogDir, resource.id, resource.version);\n\n if (exists && !options.override) {\n throw new Error(`Failed to write ${resource.id} (${options.type}) as the version ${resource.version} already exists`);\n }\n\n const { markdown, ...frontmatter } = resource;\n\n if (options.versionExistingContent && !exists) {\n const currentResource = await getResource(catalogDir, resource.id);\n\n if (currentResource) {\n if (satisfies(resource.version, `>${currentResource.version}`)) {\n await versionResource(catalogDir, resource.id);\n } else {\n throw new Error(`New version ${resource.version} is not greater than current version ${currentResource.version}`);\n }\n }\n }\n\n const document = matter.stringify(markdown.trim(), frontmatter);\n fsSync.writeFileSync(lockPath, document);\n } finally {\n // Always release the lock\n await unlock(lockPath).catch(() => {});\n }\n};\n\nexport const getResource = async (\n catalogDir: string,\n id?: string,\n version?: string,\n options?: { type: string; attachSchema?: boolean },\n filePath?: string\n): Promise<Resource | undefined> => {\n const attachSchema = options?.attachSchema || false;\n const file = filePath || (id ? await findFileById(catalogDir, id, version) : undefined);\n if (!file || !fsSync.existsSync(file)) return;\n\n const { data, content } = matter.read(file);\n\n if (attachSchema && data?.schemaPath) {\n const resourceDirectory = dirname(file);\n const pathToSchema = join(resourceDirectory, data.schemaPath);\n if (fsSync.existsSync(pathToSchema)) {\n const schema = fsSync.readFileSync(pathToSchema, 'utf8');\n // Try to parse the schema\n try {\n data.schema = JSON.parse(schema);\n } catch (error) {\n data.schema = schema;\n }\n }\n }\n\n return {\n ...data,\n markdown: content.trim(),\n } as Resource;\n};\n\nexport const getResourcePath = async (catalogDir: string, id: string, version?: string) => {\n const file = await findFileById(catalogDir, id, version);\n if (!file) return;\n\n return {\n fullPath: file,\n relativePath: file.replace(catalogDir, ''),\n directory: dirname(file.replace(catalogDir, '')),\n };\n};\n\nexport const getResourceFolderName = async (catalogDir: string, id: string, version?: string) => {\n const paths = await getResourcePath(catalogDir, id, version);\n if (!paths) return;\n return paths?.directory.split(path.sep).filter(Boolean).pop();\n};\n\nexport const toResource = async (catalogDir: string, rawContents: string) => {\n const { data, content } = matter(rawContents);\n return {\n ...data,\n markdown: content.trim(),\n } as Resource;\n};\n\nexport const getResources = async (\n catalogDir: string,\n {\n type,\n latestOnly = false,\n ignore = [],\n pattern = '',\n attachSchema = false,\n }: { type: string; pattern?: string; latestOnly?: boolean; ignore?: string[]; attachSchema?: boolean }\n): Promise<Resource[] | undefined> => {\n const ignoreList = latestOnly ? `**/versioned/**` : '';\n const filePattern = pattern || `${catalogDir}/**/${type}/**/index.{md,mdx}`;\n const files = await getFiles(filePattern, [ignoreList, ...ignore]);\n\n if (files.length === 0) return;\n\n return files.map((file) => {\n const { data, content } = matter.read(file);\n\n // Attach the schema if the attachSchema option is set to true\n if (attachSchema && data?.schemaPath) {\n const resourceDirectory = dirname(file);\n const pathToSchema = join(resourceDirectory, data.schemaPath);\n if (fsSync.existsSync(pathToSchema)) {\n const schema = fsSync.readFileSync(pathToSchema, 'utf8');\n // Try to parse the schema\n try {\n data.schema = JSON.parse(schema);\n } catch (error) {\n data.schema = schema;\n }\n }\n }\n return {\n ...data,\n markdown: content.trim(),\n } as Resource;\n });\n};\n\nexport const rmResourceById = async (\n catalogDir: string,\n id: string,\n version?: string,\n options?: { type: string; persistFiles?: boolean }\n) => {\n const files = await getFiles(`${catalogDir}/**/index.{md,mdx}`);\n\n const matchedFiles = await searchFilesForId(files, id, version);\n\n if (matchedFiles.length === 0) {\n throw new Error(`No ${options?.type || 'resource'} found with id: ${id}`);\n }\n\n if (options?.persistFiles) {\n await Promise.all(\n matchedFiles.map(async (file) => {\n await fs.rm(file, { recursive: true });\n // Verify file is actually removed\n await waitForFileRemoval(file);\n })\n );\n } else {\n await Promise.all(\n matchedFiles.map(async (file) => {\n const directory = dirname(file);\n await fs.rm(directory, { recursive: true, force: true });\n // Verify directory is actually removed\n await waitForFileRemoval(directory);\n })\n );\n }\n};\n\n// Helper function to ensure file/directory is completely removed\nconst waitForFileRemoval = async (path: string, maxRetries: number = 50, delay: number = 10): Promise<void> => {\n for (let i = 0; i < maxRetries; i++) {\n try {\n await fs.access(path);\n // If access succeeds, file still exists, wait and retry\n await new Promise((resolve) => setTimeout(resolve, delay));\n } catch (error) {\n // If access fails, file is removed\n return;\n }\n }\n // If we reach here, file still exists after all retries\n throw new Error(`File/directory ${path} was not removed after ${maxRetries} attempts`);\n};\n\nexport const addFileToResource = async (\n catalogDir: string,\n id: string,\n file: { content: string; fileName: string },\n version?: string\n) => {\n const pathToResource = await findFileById(catalogDir, id, version);\n\n if (!pathToResource) throw new Error('Cannot find directory to write file to');\n\n let fileContent = file.content.trim();\n\n try {\n const json = JSON.parse(fileContent);\n fileContent = JSON.stringify(json, null, 2);\n } catch (error) {\n // Just silently fail if the file is not valid JSON\n // Write it as it is\n }\n\n fsSync.writeFileSync(join(dirname(pathToResource), file.fileName), fileContent);\n};\n\nexport const getFileFromResource = async (catalogDir: string, id: string, file: { fileName: string }, version?: string) => {\n const pathToResource = await findFileById(catalogDir, id, version);\n\n if (!pathToResource) throw new Error('Cannot find directory of resource');\n\n const exists = await fs\n .access(join(dirname(pathToResource), file.fileName))\n .then(() => true)\n .catch(() => false);\n if (!exists) throw new Error(`File ${file.fileName} does not exist in resource ${id} v(${version})`);\n\n return fsSync.readFileSync(join(dirname(pathToResource), file.fileName), 'utf-8');\n};\nexport const getVersionedDirectory = (sourceDirectory: string, version: any): string => {\n return join(sourceDirectory, 'versioned', version);\n};\n\nexport const isLatestVersion = async (catalogDir: string, id: string, version?: string) => {\n const resource = await getResource(catalogDir, id, version);\n if (!resource) return false;\n\n const pathToResource = await getResourcePath(catalogDir, id, version);\n\n return !pathToResource?.relativePath.replace(/\\\\/g, '/').includes('/versioned/');\n};\n","import * as Containers from './containers';\n\n/**\n * Returns a data store (e.g. database, cache, etc.) from EventCatalog.\n *\n * You can optionally specify a version to get a specific version of the data store\n *\n * @example\n * ```ts\n * import utils from '@eventcatalog/utils';\n *\n * const { getContainer } = utils('/path/to/eventcatalog');\n *\n * // Gets the latest version of the data store\n * const container = await getDataStore('orders-db');\n *\n * // Gets a version of the entity\n * const container = await getDataStore('orders-db', '0.0.1');\n *\n * ```\n */\nexport const getDataStore = Containers.getContainer;\n\n/**\n * Returns all data stores (e.g. databases, caches, etc.) from EventCatalog.\n *\n * You can optionally specify if you want to get the latest version of the data stores.\n *\n * @example\n * ```ts\n * import utils from '@eventcatalog/utils';\n *\n * const { getDataStores } = utils('/path/to/eventcatalog');\n *\n * // Gets all data stores (and versions) from the catalog\n * const containers = await getDataStores();\n *\n * // Gets all data stores (only latest version) from the catalog\n * const containers = await getDataStores({ latestOnly: true });\n *\n * ```\n */\nexport const getDataStores = Containers.getContainers;\n\n/**\n * Write a data store (e.g. database, cache, etc.) to EventCatalog.\n */\nexport const writeDataStore = Containers.writeContainer;\n\n/**\n * Version an data store (e.g. database, cache, etc.) by its id.\n *\n * Takes the latest data store and moves it to a versioned directory.\n * All files with this data store are also versioned (e.g /containers/orders-db/schema.json)\n *\n * @example\n * ```ts\n * import utils from '@eventcatalog/utils';\n *\n * const { versionDataStore } = utils('/path/to/eventcatalog');\n *\n * // moves the latest orders-db data store to a versioned directory\n * // the version within that data store is used as the version number.\n * await versionDataStore('orders-db');\n *\n * ```\n */\nexport const versionDataStore = Containers.versionContainer;\n\n/**\n * Delete an data store (e.g. database, cache, etc.) at its given path.\n *\n * @example\n * ```ts\n * import utils from '@eventcatalog/utils';\n *\n * const { rmDataStore } = utils('/path/to/eventcatalog');\n *\n * // removes an data store at the given path (containers dir is appended to the given path)\n * // Removes the data store at containers/orders-db\n * await rmDataStore('/orders-db');\n * ```\n */\nexport const rmDataStore = Containers.rmContainer;\n\n/**\n * Delete an data store (e.g. database, cache, etc.) by its id.\n *\n * Optionally specify a version to delete a specific version of the data store.\n *\n * @example\n * ```ts\n * import utils from '@eventcatalog/utils';\n *\n * const { rmDataStoreById } = utils('/path/to/eventcatalog');\n *\n * // deletes the latest orders-db data store\n * await rmDataStoreById('orders-db');\n *\n * // deletes a specific version of the orders-db data store\n * await rmDataStoreById('orders-db', '0.0.1');\n * ```\n */\nexport const rmDataStoreById = Containers.rmContainerById;\n\n/**\n * Check to see if the catalog has a version for the given data store (e.g. database, cache, etc.).\n *\n * @example\n * ```ts\n * import utils from '@eventcatalog/utils';\n *\n * const { dataStoreHasVersion } = utils('/path/to/eventcatalog');\n *\n * // returns true if version is found for the given data store and version (supports semver)\n * await dataStoreHasVersion('orders-db', '0.0.1');\n * await dataStoreHasVersion('orders-db', 'latest');\n * await dataStoreHasVersion('orders-db', '0.0.x');\n *\n * ```\n */\nexport const dataStoreHasVersion = Containers.containerHasVersion;\n\n/**\n * Add a file to a data store (e.g. database, cache, etc.) by it's id.\n *\n * Optionally specify a version to add a file to a specific version of the data store.\n *\n * @example\n * ```ts\n * import utils from '@eventcatalog/utils';\n *\n * const { addFileToDataStore } = utils('/path/to/eventcatalog');\n *\n * // adds a file to the latest InventoryAdjusted data store\n * await addFileToDataStore('InventoryAdjusted', { content: 'Hello world', fileName: 'hello.txt' });\n *\n * // adds a file to a specific version of the InventoryAdjusted data store\n * await addFileToDataStore('InventoryAdjusted', { content: 'Hello world', fileName: 'hello.txt' }, '0.0.1');\n *\n * ```\n */\nexport const addFileToDataStore = Containers.addFileToContainer;\n\n/**\n * Write an data store (e.g. database, cache, etc.) to a service in EventCatalog.\n *\n * You can optionally override the path of the data store.\n *\n * @example\n * ```ts\n * import utils from '@eventcatalog/utils';\n *\n * const { writeDataStoreToService } = utils('/path/to/eventcatalog');\n *\n * // Write a data store to a given service in the catalog\n * // Data store would be written to services/Inventory/containers/orders-db\n * await writeDataStoreToService({\n * id: 'orders-db',\n * name: 'Orders DB',\n * version: '0.0.1',\n * summary: 'This is a summary',\n * markdown: '# Hello world',\n * container_type: 'database',\n * }, { id: 'Inventory' });\n * ```\n */\nexport const writeDataStoreToService = Containers.writeContainerToService;\n"],"mappings":"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","names":["fs","join","path","dirname","join","matter","fsSync","satisfies","basename","dirname","file","join","path","matter","dirname","join","fsSync","path","resolve","join","path","fs","join"]}
|
|
1
|
+
{"version":3,"sources":["../src/containers.ts","../src/internal/utils.ts","../src/internal/resources.ts","../src/data-stores.ts"],"sourcesContent":["import fs from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { findFileById } from './internal/utils';\nimport type { Container } from './types';\nimport {\n addFileToResource,\n getResource,\n getResourcePath,\n getResources,\n rmResourceById,\n versionResource,\n writeResource,\n} from './internal/resources';\n\n/**\n * Returns an container (e.g. data store) from EventCatalog.\n *\n * You can optionally specify a version to get a specific version of the container\n *\n * @example\n * ```ts\n * import utils from '@eventcatalog/utils';\n *\n * const { getContainer } = utils('/path/to/eventcatalog');\n *\n * // Gets the latest version of the container\n * const container = await getContainer('User');\n *\n * // Gets a version of the entity\n * const container = await getContainer('User', '0.0.1');\n *\n * ```\n */\nexport const getContainer =\n (directory: string) =>\n async (id: string, version?: string): Promise<Container> =>\n getResource(directory, id, version, { type: 'container' }) as Promise<Container>;\n\n/**\n * Returns all containers (e.g. data stores) from EventCatalog.\n *\n * You can optionally specify if you want to get the latest version of the containers.\n *\n * @example\n * ```ts\n * import utils from '@eventcatalog/utils';\n *\n * const { getContainers } = utils('/path/to/eventcatalog');\n *\n * // Gets all containers (and versions) from the catalog\n * const containers = await getContainers();\n *\n * // Gets all entities (only latest version) from the catalog\n * const containers = await getContainers({ latestOnly: true });\n *\n * ```\n */\nexport const getContainers =\n (directory: string) =>\n async (options?: { latestOnly?: boolean }): Promise<Container[]> =>\n getResources(directory, { type: 'containers', latestOnly: options?.latestOnly }) as Promise<Container[]>;\n\n/**\n * Write a container (e.g. data store) to EventCatalog.\n */\nexport const writeContainer =\n (directory: string) =>\n async (\n data: Container,\n options: { path?: string; override?: boolean; versionExistingContent?: boolean; format?: 'md' | 'mdx' } = {\n path: '',\n override: false,\n format: 'mdx',\n }\n ) =>\n writeResource(directory, { ...data }, { ...options, type: 'container' });\n\n/**\n * Version an container (e.g. data store) by its id.\n *\n * Takes the latest container and moves it to a versioned directory.\n * All files with this container are also versioned (e.g /containers/orders-db/schema.json)\n *\n * @example\n * ```ts\n * import utils from '@eventcatalog/utils';\n *\n * const { versionContainer } = utils('/path/to/eventcatalog');\n *\n * // moves the latest orders-db container to a versioned directory\n * // the version within that container is used as the version number.\n * await versionContainer('orders-db');\n *\n * ```\n */\nexport const versionContainer = (directory: string) => async (id: string) => versionResource(directory, id);\n\n/**\n * Delete an container (e.g. data store) at its given path.\n *\n * @example\n * ```ts\n * import utils from '@eventcatalog/utils';\n *\n * const { rmContainer } = utils('/path/to/eventcatalog');\n *\n * // removes an container at the given path (containers dir is appended to the given path)\n * // Removes the container at containers/orders-db\n * await rmContainer('/orders-db');\n * ```\n */\nexport const rmContainer = (directory: string) => async (path: string) => {\n await fs.rm(join(directory, path), { recursive: true });\n};\n\n/**\n * Delete an container (e.g. data store) by its id.\n *\n * Optionally specify a version to delete a specific version of the container.\n *\n * @example\n * ```ts\n * import utils from '@eventcatalog/utils';\n *\n * const { rmContainerById } = utils('/path/to/eventcatalog');\n *\n * // deletes the latest orders-db container\n * await rmContainerById('orders-db');\n *\n * // deletes a specific version of the orders-db container\n * await rmContainerById('orders-db', '0.0.1');\n * ```\n */\nexport const rmContainerById = (directory: string) => async (id: string, version?: string, persistFiles?: boolean) => {\n await rmResourceById(directory, id, version, { type: 'container', persistFiles });\n};\n\n/**\n * Check to see if the catalog has a version for the given container (e.g. data store).\n *\n * @example\n * ```ts\n * import utils from '@eventcatalog/utils';\n *\n * const { containerHasVersion } = utils('/path/to/eventcatalog');\n *\n * // returns true if version is found for the given entity and version (supports semver)\n * await containerHasVersion('orders-db', '0.0.1');\n * await containerHasVersion('orders-db', 'latest');\n * await containerHasVersion('orders-db', '0.0.x');\n *\n * ```\n */\nexport const containerHasVersion = (directory: string) => async (id: string, version?: string) => {\n const file = await findFileById(directory, id, version);\n return !!file;\n};\n\n/**\n * Add a file to a container (e.g. data store) by it's id.\n *\n * Optionally specify a version to add a file to a specific version of the container.\n *\n * @example\n * ```ts\n * import utils from '@eventcatalog/utils';\n *\n * const { addFileToContainer } = utils('/path/to/eventcatalog');\n *\n * // adds a file to the latest InventoryAdjusted event\n * await addFileToContainer('InventoryAdjusted', { content: 'Hello world', fileName: 'hello.txt' });\n *\n * // adds a file to a specific version of the InventoryAdjusted event\n * await addFileToContainer('InventoryAdjusted', { content: 'Hello world', fileName: 'hello.txt' }, '0.0.1');\n *\n * ```\n */\nexport const addFileToContainer =\n (directory: string) => async (id: string, file: { content: string; fileName: string }, version?: string) =>\n addFileToResource(directory, id, file, version);\n\n/**\n * Write an data store (e.g. data store) to a service in EventCatalog.\n *\n * You can optionally override the path of the data store.\n *\n * @example\n * ```ts\n * import utils from '@eventcatalog/utils';\n *\n * const { writeContainerToService } = utils('/path/to/eventcatalog');\n *\n * // Write a container to a given service in the catalog\n * // Container would be written to services/Inventory/containers/orders-db\n * await writeContainerToService({\n * id: 'orders-db',\n * name: 'Orders DB',\n * version: '0.0.1',\n * summary: 'This is a summary',\n * markdown: '# Hello world',\n * container_type: 'database',\n * }, { id: 'Inventory' });\n * ```\n */\nexport const writeContainerToService =\n (directory: string) =>\n async (\n container: Container,\n service: { id: string; version?: string },\n options: { path?: string; format?: 'md' | 'mdx'; override?: boolean } = { path: '', format: 'mdx', override: false }\n ) => {\n const resourcePath = await getResourcePath(directory, service.id, service.version);\n if (!resourcePath) {\n throw new Error('Service not found');\n }\n\n let pathForContainer =\n service.version && service.version !== 'latest'\n ? `${resourcePath.directory}/versioned/${service.version}/containers`\n : `${resourcePath.directory}/containers`;\n pathForContainer = join(pathForContainer, container.id);\n await writeResource(directory, { ...container }, { ...options, path: pathForContainer, type: 'container' });\n };\n","import { globSync } from 'glob';\nimport fsSync from 'node:fs';\nimport { copy, CopyFilterAsync, CopyFilterSync } from 'fs-extra';\nimport { join, dirname, normalize, sep as pathSeparator, resolve, basename, relative } from 'node:path';\nimport matter from 'gray-matter';\nimport { satisfies, validRange, valid } from 'semver';\n\n/**\n * Returns true if a given version of a resource id exists in the catalog\n */\nexport const versionExists = async (catalogDir: string, id: string, version: string) => {\n const files = await getFiles(`${catalogDir}/**/index.{md,mdx}`);\n const matchedFiles = (await searchFilesForId(files, id, version)) || [];\n return matchedFiles.length > 0;\n};\n\nexport const findFileById = async (catalogDir: string, id: string, version?: string): Promise<string | undefined> => {\n const files = await getFiles(`${catalogDir}/**/index.{md,mdx}`);\n\n const matchedFiles = (await searchFilesForId(files, id)) || [];\n const latestVersion = matchedFiles.find((path) => !path.includes('versioned'));\n\n // If no version is provided, return the latest version\n if (!version) {\n return latestVersion;\n }\n\n // map files into gray matter to get versions\n const parsedFiles = matchedFiles.map((path) => {\n const { data } = matter.read(path);\n return { ...data, path };\n }) as any[];\n\n // Handle 'latest' version - return the latest (non-versioned) file\n if (version === 'latest') {\n return latestVersion;\n }\n\n // First, check for exact version match (handles non-semver versions like '1', '2', etc.)\n const exactMatch = parsedFiles.find((c) => c.version === version);\n if (exactMatch) {\n return exactMatch.path;\n }\n\n // Try semver range matching\n const semverRange = validRange(version);\n\n if (semverRange) {\n const match = parsedFiles.filter((c) => {\n try {\n return satisfies(c.version, semverRange);\n } catch (error) {\n // If satisfies fails (e.g., comparing semver range with non-semver version), skip this file\n return false;\n }\n });\n return match.length > 0 ? match[0].path : undefined;\n }\n\n // If no exact match and no valid semver range, return undefined\n return undefined;\n};\n\nexport const getFiles = async (pattern: string, ignore: string | string[] = '') => {\n try {\n // 1. Normalize the input pattern to handle mixed separators potentially\n const normalizedInputPattern = normalize(pattern);\n\n // 2. Determine the absolute base directory (cwd for glob)\n // Resolve ensures it's absolute. Handles cases with/without globstar.\n const absoluteBaseDir = resolve(\n normalizedInputPattern.includes('**') ? normalizedInputPattern.split('**')[0] : dirname(normalizedInputPattern)\n );\n\n // 3. Determine the pattern part relative to the absolute base directory\n // We extract the part of the normalized pattern that comes *after* the absoluteBaseDir\n let relativePattern = relative(absoluteBaseDir, normalizedInputPattern);\n\n // On Windows, relative() might return empty string if paths are identical,\n // or might need normalization if the original pattern didn't have `**`\n // Example: pattern = 'dir/file.md', absoluteBaseDir='.../dir', normalized='...\\dir\\file.md'\n // relative() -> 'file.md'\n // Example: pattern = 'dir/**/file.md', absoluteBaseDir='.../dir', normalized='...\\dir\\**\\file.md'\n // relative() -> '**\\file.md'\n // Convert separators in the relative pattern to forward slashes for glob\n relativePattern = relativePattern.replace(/\\\\/g, '/');\n\n const ignoreList = Array.isArray(ignore) ? ignore : [ignore];\n\n const files = globSync(relativePattern, {\n cwd: absoluteBaseDir,\n ignore: ['node_modules/**', ...ignoreList],\n absolute: true,\n nodir: true,\n });\n\n // 5. Normalize results for consistency before returning\n return files.map(normalize);\n } catch (error: any) {\n // Add more diagnostic info to the error\n const absoluteBaseDirForError = resolve(\n normalize(pattern).includes('**') ? normalize(pattern).split('**')[0] : dirname(normalize(pattern))\n );\n const relativePatternForError = relative(absoluteBaseDirForError, normalize(pattern)).replace(/\\\\/g, '/');\n throw new Error(\n `Error finding files for pattern \"${pattern}\" (using cwd: \"${absoluteBaseDirForError}\", globPattern: \"${relativePatternForError}\"): ${error.message}`\n );\n }\n};\n\nexport const readMdxFile = async (path: string) => {\n const { data } = matter.read(path);\n const { markdown, ...frontmatter } = data;\n return { ...frontmatter, markdown };\n};\n\nexport const searchFilesForId = async (files: string[], id: string, version?: string) => {\n // Escape the id to avoid regex issues\n const escapedId = id.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const idRegex = new RegExp(`^id:\\\\s*(['\"]|>-)?\\\\s*${escapedId}['\"]?\\\\s*$`, 'm');\n\n const versionRegex = new RegExp(`^version:\\\\s*['\"]?${version}['\"]?\\\\s*$`, 'm');\n\n const matches = files.map((file) => {\n const content = fsSync.readFileSync(file, 'utf-8');\n const hasIdMatch = content.match(idRegex);\n\n // Check version if provided\n if (version && !content.match(versionRegex)) {\n return undefined;\n }\n\n if (hasIdMatch) {\n return file;\n }\n });\n\n return matches.filter(Boolean).filter((file) => file !== undefined);\n};\n\n/**\n * Function to copy a directory from source to target, uses a tmp directory\n * @param catalogDir\n * @param source\n * @param target\n * @param filter\n */\nexport const copyDir = async (catalogDir: string, source: string, target: string, filter?: CopyFilterAsync | CopyFilterSync) => {\n const tmpDirectory = join(catalogDir, 'tmp');\n fsSync.mkdirSync(tmpDirectory, { recursive: true });\n\n // Copy everything over\n await copy(source, tmpDirectory, {\n overwrite: true,\n filter,\n });\n\n await copy(tmpDirectory, target, {\n overwrite: true,\n filter,\n });\n\n // Remove the tmp directory\n fsSync.rmSync(tmpDirectory, { recursive: true });\n};\n\n// Makes sure values in sends/recieves are unique\nexport const uniqueVersions = (messages: { id: string; version: string }[]): { id: string; version: string }[] => {\n const uniqueSet = new Set();\n\n return messages.filter((message) => {\n const key = `${message.id}-${message.version}`;\n if (!uniqueSet.has(key)) {\n uniqueSet.add(key);\n return true;\n }\n return false;\n });\n};\n","import { dirname, join } from 'path';\nimport { copyDir, findFileById, getFiles, searchFilesForId, versionExists } from './utils';\nimport matter from 'gray-matter';\nimport fs from 'node:fs/promises';\nimport fsSync from 'node:fs';\nimport { Message, Service, CustomDoc } from '../types';\nimport { satisfies } from 'semver';\nimport { lock, unlock } from 'proper-lockfile';\nimport { basename } from 'node:path';\nimport path from 'node:path';\n\ntype Resource = Service | Message | CustomDoc;\n\nexport const versionResource = async (catalogDir: string, id: string) => {\n // Find all the events in the directory\n const files = await getFiles(`${catalogDir}/**/index.{md,mdx}`);\n const matchedFiles = await searchFilesForId(files, id);\n\n if (matchedFiles.length === 0) {\n throw new Error(`No resource found with id: ${id}`);\n }\n\n // Event that is in the route of the project\n const file = matchedFiles[0];\n const sourceDirectory = dirname(file).replace(/\\/versioned?\\/[^/]+\\//, '/');\n const { data: { version = '0.0.1' } = {} } = matter.read(file);\n const targetDirectory = getVersionedDirectory(sourceDirectory, version);\n\n fsSync.mkdirSync(targetDirectory, { recursive: true });\n\n const ignoreListToCopy = ['events', 'commands', 'queries', 'versioned'];\n\n // Copy the event to the versioned directory\n await copyDir(catalogDir, sourceDirectory, targetDirectory, (src) => {\n // get the folder name\n const folderName = basename(src);\n\n if (ignoreListToCopy.includes(folderName)) {\n return false;\n }\n return true;\n });\n\n // Remove all the files in the root of the resource as they have now been versioned\n await fs.readdir(sourceDirectory).then(async (resourceFiles) => {\n await Promise.all(\n resourceFiles.map(async (file) => {\n // Dont remove anything in the ignore list\n if (ignoreListToCopy.includes(file)) {\n return;\n }\n if (file !== 'versioned') {\n fsSync.rmSync(join(sourceDirectory, file), { recursive: true });\n }\n })\n );\n });\n};\n\nexport const writeResource = async (\n catalogDir: string,\n resource: Resource,\n options: { path?: string; type: string; override?: boolean; versionExistingContent?: boolean; format?: 'md' | 'mdx' } = {\n path: '',\n type: '',\n override: false,\n versionExistingContent: false,\n format: 'mdx',\n }\n) => {\n const path = options.path || `/${resource.id}`;\n const fullPath = join(catalogDir, path);\n const format = options.format || 'mdx';\n\n // Create directory if it doesn't exist\n fsSync.mkdirSync(fullPath, { recursive: true });\n\n // Create or get lock file path\n const lockPath = join(fullPath, `index.${format}`);\n\n // Ensure the file exists before attempting to lock it\n if (!fsSync.existsSync(lockPath)) {\n fsSync.writeFileSync(lockPath, '');\n }\n\n try {\n // Acquire lock with retry\n await lock(lockPath, {\n retries: 5,\n stale: 10000, // 10 seconds\n });\n\n const exists = await versionExists(catalogDir, resource.id, resource.version);\n\n if (exists && !options.override) {\n throw new Error(`Failed to write ${resource.id} (${options.type}) as the version ${resource.version} already exists`);\n }\n\n const { markdown, ...frontmatter } = resource;\n\n if (options.versionExistingContent && !exists) {\n const currentResource = await getResource(catalogDir, resource.id);\n\n if (currentResource) {\n if (satisfies(resource.version, `>${currentResource.version}`)) {\n await versionResource(catalogDir, resource.id);\n } else {\n throw new Error(`New version ${resource.version} is not greater than current version ${currentResource.version}`);\n }\n }\n }\n\n const document = matter.stringify(markdown.trim(), frontmatter);\n fsSync.writeFileSync(lockPath, document);\n } finally {\n // Always release the lock\n await unlock(lockPath).catch(() => {});\n }\n};\n\nexport const getResource = async (\n catalogDir: string,\n id?: string,\n version?: string,\n options?: { type: string; attachSchema?: boolean },\n filePath?: string\n): Promise<Resource | undefined> => {\n const attachSchema = options?.attachSchema || false;\n const file = filePath || (id ? await findFileById(catalogDir, id, version) : undefined);\n if (!file || !fsSync.existsSync(file)) return;\n\n const { data, content } = matter.read(file);\n\n if (attachSchema && data?.schemaPath) {\n const resourceDirectory = dirname(file);\n const pathToSchema = join(resourceDirectory, data.schemaPath);\n if (fsSync.existsSync(pathToSchema)) {\n const schema = fsSync.readFileSync(pathToSchema, 'utf8');\n // Try to parse the schema\n try {\n data.schema = JSON.parse(schema);\n } catch (error) {\n data.schema = schema;\n }\n }\n }\n\n return {\n ...data,\n markdown: content.trim(),\n } as Resource;\n};\n\nexport const getResourcePath = async (catalogDir: string, id: string, version?: string) => {\n const file = await findFileById(catalogDir, id, version);\n if (!file) return;\n\n return {\n fullPath: file,\n relativePath: file.replace(catalogDir, ''),\n directory: dirname(file.replace(catalogDir, '')),\n };\n};\n\nexport const getResourceFolderName = async (catalogDir: string, id: string, version?: string) => {\n const paths = await getResourcePath(catalogDir, id, version);\n if (!paths) return;\n return paths?.directory.split(path.sep).filter(Boolean).pop();\n};\n\nexport const toResource = async (catalogDir: string, rawContents: string) => {\n const { data, content } = matter(rawContents);\n return {\n ...data,\n markdown: content.trim(),\n } as Resource;\n};\n\nexport const getResources = async (\n catalogDir: string,\n {\n type,\n latestOnly = false,\n ignore = [],\n pattern = '',\n attachSchema = false,\n }: { type: string; pattern?: string; latestOnly?: boolean; ignore?: string[]; attachSchema?: boolean }\n): Promise<Resource[] | undefined> => {\n const ignoreList = latestOnly ? `**/versioned/**` : '';\n const filePattern = pattern || `${catalogDir}/**/${type}/**/index.{md,mdx}`;\n const files = await getFiles(filePattern, [ignoreList, ...ignore]);\n\n if (files.length === 0) return;\n\n return files.map((file) => {\n const { data, content } = matter.read(file);\n\n // Attach the schema if the attachSchema option is set to true\n if (attachSchema && data?.schemaPath) {\n const resourceDirectory = dirname(file);\n const pathToSchema = join(resourceDirectory, data.schemaPath);\n if (fsSync.existsSync(pathToSchema)) {\n const schema = fsSync.readFileSync(pathToSchema, 'utf8');\n // Try to parse the schema\n try {\n data.schema = JSON.parse(schema);\n } catch (error) {\n data.schema = schema;\n }\n }\n }\n return {\n ...data,\n markdown: content.trim(),\n } as Resource;\n });\n};\n\nexport const rmResourceById = async (\n catalogDir: string,\n id: string,\n version?: string,\n options?: { type: string; persistFiles?: boolean }\n) => {\n const files = await getFiles(`${catalogDir}/**/index.{md,mdx}`);\n\n const matchedFiles = await searchFilesForId(files, id, version);\n\n if (matchedFiles.length === 0) {\n throw new Error(`No ${options?.type || 'resource'} found with id: ${id}`);\n }\n\n if (options?.persistFiles) {\n await Promise.all(\n matchedFiles.map(async (file) => {\n await fs.rm(file, { recursive: true });\n // Verify file is actually removed\n await waitForFileRemoval(file);\n })\n );\n } else {\n await Promise.all(\n matchedFiles.map(async (file) => {\n const directory = dirname(file);\n await fs.rm(directory, { recursive: true, force: true });\n // Verify directory is actually removed\n await waitForFileRemoval(directory);\n })\n );\n }\n};\n\n// Helper function to ensure file/directory is completely removed\nconst waitForFileRemoval = async (path: string, maxRetries: number = 50, delay: number = 10): Promise<void> => {\n for (let i = 0; i < maxRetries; i++) {\n try {\n await fs.access(path);\n // If access succeeds, file still exists, wait and retry\n await new Promise((resolve) => setTimeout(resolve, delay));\n } catch (error) {\n // If access fails, file is removed\n return;\n }\n }\n // If we reach here, file still exists after all retries\n throw new Error(`File/directory ${path} was not removed after ${maxRetries} attempts`);\n};\n\nexport const addFileToResource = async (\n catalogDir: string,\n id: string,\n file: { content: string; fileName: string },\n version?: string\n) => {\n const pathToResource = await findFileById(catalogDir, id, version);\n\n if (!pathToResource) throw new Error('Cannot find directory to write file to');\n\n let fileContent = file.content.trim();\n\n try {\n const json = JSON.parse(fileContent);\n fileContent = JSON.stringify(json, null, 2);\n } catch (error) {\n // Just silently fail if the file is not valid JSON\n // Write it as it is\n }\n\n fsSync.writeFileSync(join(dirname(pathToResource), file.fileName), fileContent);\n};\n\nexport const getFileFromResource = async (catalogDir: string, id: string, file: { fileName: string }, version?: string) => {\n const pathToResource = await findFileById(catalogDir, id, version);\n\n if (!pathToResource) throw new Error('Cannot find directory of resource');\n\n const exists = await fs\n .access(join(dirname(pathToResource), file.fileName))\n .then(() => true)\n .catch(() => false);\n if (!exists) throw new Error(`File ${file.fileName} does not exist in resource ${id} v(${version})`);\n\n return fsSync.readFileSync(join(dirname(pathToResource), file.fileName), 'utf-8');\n};\nexport const getVersionedDirectory = (sourceDirectory: string, version: any): string => {\n return join(sourceDirectory, 'versioned', version);\n};\n\nexport const isLatestVersion = async (catalogDir: string, id: string, version?: string) => {\n const resource = await getResource(catalogDir, id, version);\n if (!resource) return false;\n\n const pathToResource = await getResourcePath(catalogDir, id, version);\n\n return !pathToResource?.relativePath.replace(/\\\\/g, '/').includes('/versioned/');\n};\n","import * as Containers from './containers';\n\n/**\n * Returns a data store (e.g. database, cache, etc.) from EventCatalog.\n *\n * You can optionally specify a version to get a specific version of the data store\n *\n * @example\n * ```ts\n * import utils from '@eventcatalog/utils';\n *\n * const { getContainer } = utils('/path/to/eventcatalog');\n *\n * // Gets the latest version of the data store\n * const container = await getDataStore('orders-db');\n *\n * // Gets a version of the entity\n * const container = await getDataStore('orders-db', '0.0.1');\n *\n * ```\n */\nexport const getDataStore = Containers.getContainer;\n\n/**\n * Returns all data stores (e.g. databases, caches, etc.) from EventCatalog.\n *\n * You can optionally specify if you want to get the latest version of the data stores.\n *\n * @example\n * ```ts\n * import utils from '@eventcatalog/utils';\n *\n * const { getDataStores } = utils('/path/to/eventcatalog');\n *\n * // Gets all data stores (and versions) from the catalog\n * const containers = await getDataStores();\n *\n * // Gets all data stores (only latest version) from the catalog\n * const containers = await getDataStores({ latestOnly: true });\n *\n * ```\n */\nexport const getDataStores = Containers.getContainers;\n\n/**\n * Write a data store (e.g. database, cache, etc.) to EventCatalog.\n */\nexport const writeDataStore = Containers.writeContainer;\n\n/**\n * Version an data store (e.g. database, cache, etc.) by its id.\n *\n * Takes the latest data store and moves it to a versioned directory.\n * All files with this data store are also versioned (e.g /containers/orders-db/schema.json)\n *\n * @example\n * ```ts\n * import utils from '@eventcatalog/utils';\n *\n * const { versionDataStore } = utils('/path/to/eventcatalog');\n *\n * // moves the latest orders-db data store to a versioned directory\n * // the version within that data store is used as the version number.\n * await versionDataStore('orders-db');\n *\n * ```\n */\nexport const versionDataStore = Containers.versionContainer;\n\n/**\n * Delete an data store (e.g. database, cache, etc.) at its given path.\n *\n * @example\n * ```ts\n * import utils from '@eventcatalog/utils';\n *\n * const { rmDataStore } = utils('/path/to/eventcatalog');\n *\n * // removes an data store at the given path (containers dir is appended to the given path)\n * // Removes the data store at containers/orders-db\n * await rmDataStore('/orders-db');\n * ```\n */\nexport const rmDataStore = Containers.rmContainer;\n\n/**\n * Delete an data store (e.g. database, cache, etc.) by its id.\n *\n * Optionally specify a version to delete a specific version of the data store.\n *\n * @example\n * ```ts\n * import utils from '@eventcatalog/utils';\n *\n * const { rmDataStoreById } = utils('/path/to/eventcatalog');\n *\n * // deletes the latest orders-db data store\n * await rmDataStoreById('orders-db');\n *\n * // deletes a specific version of the orders-db data store\n * await rmDataStoreById('orders-db', '0.0.1');\n * ```\n */\nexport const rmDataStoreById = Containers.rmContainerById;\n\n/**\n * Check to see if the catalog has a version for the given data store (e.g. database, cache, etc.).\n *\n * @example\n * ```ts\n * import utils from '@eventcatalog/utils';\n *\n * const { dataStoreHasVersion } = utils('/path/to/eventcatalog');\n *\n * // returns true if version is found for the given data store and version (supports semver)\n * await dataStoreHasVersion('orders-db', '0.0.1');\n * await dataStoreHasVersion('orders-db', 'latest');\n * await dataStoreHasVersion('orders-db', '0.0.x');\n *\n * ```\n */\nexport const dataStoreHasVersion = Containers.containerHasVersion;\n\n/**\n * Add a file to a data store (e.g. database, cache, etc.) by it's id.\n *\n * Optionally specify a version to add a file to a specific version of the data store.\n *\n * @example\n * ```ts\n * import utils from '@eventcatalog/utils';\n *\n * const { addFileToDataStore } = utils('/path/to/eventcatalog');\n *\n * // adds a file to the latest InventoryAdjusted data store\n * await addFileToDataStore('InventoryAdjusted', { content: 'Hello world', fileName: 'hello.txt' });\n *\n * // adds a file to a specific version of the InventoryAdjusted data store\n * await addFileToDataStore('InventoryAdjusted', { content: 'Hello world', fileName: 'hello.txt' }, '0.0.1');\n *\n * ```\n */\nexport const addFileToDataStore = Containers.addFileToContainer;\n\n/**\n * Write an data store (e.g. database, cache, etc.) to a service in EventCatalog.\n *\n * You can optionally override the path of the data store.\n *\n * @example\n * ```ts\n * import utils from '@eventcatalog/utils';\n *\n * const { writeDataStoreToService } = utils('/path/to/eventcatalog');\n *\n * // Write a data store to a given service in the catalog\n * // Data store would be written to services/Inventory/containers/orders-db\n * await writeDataStoreToService({\n * id: 'orders-db',\n * name: 'Orders DB',\n * version: '0.0.1',\n * summary: 'This is a summary',\n * markdown: '# Hello world',\n * container_type: 'database',\n * }, { id: 'Inventory' });\n * ```\n */\nexport const writeDataStoreToService = Containers.writeContainerToService;\n"],"mappings":"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","names":["fs","join","dirname","join","matter","fsSync","satisfies","basename","dirname","matter","fsSync","basename","file","join","satisfies","matter","dirname","join","fsSync","resolve","join","fs","join"]}
|
package/dist/domains.mjs
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
// src/domains.ts
|
|
2
|
-
import fs2 from "fs/promises";
|
|
3
|
-
import
|
|
4
|
-
import fsSync3 from "fs";
|
|
2
|
+
import fs2 from "node:fs/promises";
|
|
3
|
+
import path, { join as join3 } from "node:path";
|
|
4
|
+
import fsSync3 from "node:fs";
|
|
5
5
|
|
|
6
6
|
// src/internal/resources.ts
|
|
7
7
|
import { dirname as dirname2, join as join2 } from "path";
|
|
8
8
|
|
|
9
9
|
// src/internal/utils.ts
|
|
10
10
|
import { globSync } from "glob";
|
|
11
|
-
import fsSync from "fs";
|
|
11
|
+
import fsSync from "node:fs";
|
|
12
12
|
import { copy } from "fs-extra";
|
|
13
|
-
import { join, dirname, normalize, resolve, relative } from "path";
|
|
13
|
+
import { join, dirname, normalize, resolve, relative } from "node:path";
|
|
14
14
|
import matter from "gray-matter";
|
|
15
15
|
import { satisfies, validRange } from "semver";
|
|
16
16
|
var versionExists = async (catalogDir, id, version) => {
|
|
@@ -21,13 +21,13 @@ var versionExists = async (catalogDir, id, version) => {
|
|
|
21
21
|
var findFileById = async (catalogDir, id, version) => {
|
|
22
22
|
const files = await getFiles(`${catalogDir}/**/index.{md,mdx}`);
|
|
23
23
|
const matchedFiles = await searchFilesForId(files, id) || [];
|
|
24
|
-
const latestVersion = matchedFiles.find((
|
|
24
|
+
const latestVersion = matchedFiles.find((path2) => !path2.includes("versioned"));
|
|
25
25
|
if (!version) {
|
|
26
26
|
return latestVersion;
|
|
27
27
|
}
|
|
28
|
-
const parsedFiles = matchedFiles.map((
|
|
29
|
-
const { data } = matter.read(
|
|
30
|
-
return { ...data, path:
|
|
28
|
+
const parsedFiles = matchedFiles.map((path2) => {
|
|
29
|
+
const { data } = matter.read(path2);
|
|
30
|
+
return { ...data, path: path2 };
|
|
31
31
|
});
|
|
32
32
|
if (version === "latest") {
|
|
33
33
|
return latestVersion;
|
|
@@ -75,8 +75,8 @@ var getFiles = async (pattern, ignore = "") => {
|
|
|
75
75
|
);
|
|
76
76
|
}
|
|
77
77
|
};
|
|
78
|
-
var readMdxFile = async (
|
|
79
|
-
const { data } = matter.read(
|
|
78
|
+
var readMdxFile = async (path2) => {
|
|
79
|
+
const { data } = matter.read(path2);
|
|
80
80
|
const { markdown, ...frontmatter } = data;
|
|
81
81
|
return { ...frontmatter, markdown };
|
|
82
82
|
};
|
|
@@ -123,12 +123,11 @@ var uniqueVersions = (messages) => {
|
|
|
123
123
|
|
|
124
124
|
// src/internal/resources.ts
|
|
125
125
|
import matter2 from "gray-matter";
|
|
126
|
-
import fs from "fs/promises";
|
|
127
|
-
import fsSync2 from "fs";
|
|
126
|
+
import fs from "node:fs/promises";
|
|
127
|
+
import fsSync2 from "node:fs";
|
|
128
128
|
import { satisfies as satisfies2 } from "semver";
|
|
129
129
|
import { lock, unlock } from "proper-lockfile";
|
|
130
|
-
import { basename as basename2 } from "path";
|
|
131
|
-
import path from "path";
|
|
130
|
+
import { basename as basename2 } from "node:path";
|
|
132
131
|
var versionResource = async (catalogDir, id) => {
|
|
133
132
|
const files = await getFiles(`${catalogDir}/**/index.{md,mdx}`);
|
|
134
133
|
const matchedFiles = await searchFilesForId(files, id);
|
|
@@ -168,8 +167,8 @@ var writeResource = async (catalogDir, resource, options = {
|
|
|
168
167
|
versionExistingContent: false,
|
|
169
168
|
format: "mdx"
|
|
170
169
|
}) => {
|
|
171
|
-
const
|
|
172
|
-
const fullPath = join2(catalogDir,
|
|
170
|
+
const path2 = options.path || `/${resource.id}`;
|
|
171
|
+
const fullPath = join2(catalogDir, path2);
|
|
173
172
|
const format = options.format || "mdx";
|
|
174
173
|
fsSync2.mkdirSync(fullPath, { recursive: true });
|
|
175
174
|
const lockPath = join2(fullPath, `index.${format}`);
|
|
@@ -289,16 +288,16 @@ var rmResourceById = async (catalogDir, id, version, options) => {
|
|
|
289
288
|
);
|
|
290
289
|
}
|
|
291
290
|
};
|
|
292
|
-
var waitForFileRemoval = async (
|
|
291
|
+
var waitForFileRemoval = async (path2, maxRetries = 50, delay = 10) => {
|
|
293
292
|
for (let i = 0; i < maxRetries; i++) {
|
|
294
293
|
try {
|
|
295
|
-
await fs.access(
|
|
294
|
+
await fs.access(path2);
|
|
296
295
|
await new Promise((resolve2) => setTimeout(resolve2, delay));
|
|
297
296
|
} catch (error) {
|
|
298
297
|
return;
|
|
299
298
|
}
|
|
300
299
|
}
|
|
301
|
-
throw new Error(`File/directory ${
|
|
300
|
+
throw new Error(`File/directory ${path2} was not removed after ${maxRetries} attempts`);
|
|
302
301
|
};
|
|
303
302
|
var addFileToResource = async (catalogDir, id, file, version) => {
|
|
304
303
|
const pathToResource = await findFileById(catalogDir, id, version);
|
|
@@ -339,8 +338,8 @@ var writeDomain = (directory) => async (domain, options = {
|
|
|
339
338
|
return await writeResource(directory, resource, { ...options, type: "domain" });
|
|
340
339
|
};
|
|
341
340
|
var versionDomain = (directory) => async (id) => versionResource(directory, id);
|
|
342
|
-
var rmDomain = (directory) => async (
|
|
343
|
-
await fs2.rm(join3(directory,
|
|
341
|
+
var rmDomain = (directory) => async (path2) => {
|
|
342
|
+
await fs2.rm(join3(directory, path2), { recursive: true });
|
|
344
343
|
};
|
|
345
344
|
var rmDomainById = (directory) => async (id, version, persistFiles) => rmResourceById(directory, id, version, { type: "domain", persistFiles });
|
|
346
345
|
var addFileToDomain = (directory) => async (id, file, version) => addFileToResource(directory, id, file, version);
|
|
@@ -352,7 +351,7 @@ var addUbiquitousLanguageToDomain = (directory) => async (id, ubiquitousLanguage
|
|
|
352
351
|
};
|
|
353
352
|
var getUbiquitousLanguageFromDomain = (directory) => async (id, version) => {
|
|
354
353
|
const pathToDomain = await findFileById(directory, id, version) || "";
|
|
355
|
-
const pathToUbiquitousLanguage =
|
|
354
|
+
const pathToUbiquitousLanguage = path.join(path.dirname(pathToDomain), "ubiquitous-language.mdx");
|
|
356
355
|
const fileExists = fsSync3.existsSync(pathToUbiquitousLanguage);
|
|
357
356
|
if (!fileExists) {
|
|
358
357
|
return void 0;
|
|
@@ -367,7 +366,7 @@ var domainHasVersion = (directory) => async (id, version) => {
|
|
|
367
366
|
var addServiceToDomain = (directory) => async (id, service, version) => {
|
|
368
367
|
let domain = await getDomain(directory)(id, version);
|
|
369
368
|
const domainPath = await getResourcePath(directory, id, version);
|
|
370
|
-
const extension =
|
|
369
|
+
const extension = path.extname(domainPath?.fullPath || "");
|
|
371
370
|
if (domain.services === void 0) {
|
|
372
371
|
domain.services = [];
|
|
373
372
|
}
|
|
@@ -382,7 +381,7 @@ var addServiceToDomain = (directory) => async (id, service, version) => {
|
|
|
382
381
|
var addSubDomainToDomain = (directory) => async (id, subDomain, version) => {
|
|
383
382
|
let domain = await getDomain(directory)(id, version);
|
|
384
383
|
const domainPath = await getResourcePath(directory, id, version);
|
|
385
|
-
const extension =
|
|
384
|
+
const extension = path.extname(domainPath?.fullPath || "");
|
|
386
385
|
if (domain.domains === void 0) {
|
|
387
386
|
domain.domains = [];
|
|
388
387
|
}
|
|
@@ -397,7 +396,7 @@ var addSubDomainToDomain = (directory) => async (id, subDomain, version) => {
|
|
|
397
396
|
var addEntityToDomain = (directory) => async (id, entity, version) => {
|
|
398
397
|
let domain = await getDomain(directory)(id, version);
|
|
399
398
|
const domainPath = await getResourcePath(directory, id, version);
|
|
400
|
-
const extension =
|
|
399
|
+
const extension = path.extname(domainPath?.fullPath || "");
|
|
401
400
|
if (domain.entities === void 0) {
|
|
402
401
|
domain.entities = [];
|
|
403
402
|
}
|