@lucidcms/core
Version:
The core of the Lucid CMS. It's responsible for spinning up the API and serving the CMS.
1 lines • 5.53 kB
Source Map (JSON)
{"version":3,"file":"media-key-tenant.mjs","names":["constants"],"sources":["../../../src/utils/media/media-key-tenant.ts"],"sourcesContent":["import constants from \"../../constants/constants.js\";\nimport type { Config, TenantConfig } from \"../../types/config.js\";\nimport { getTenantConfig } from \"../helpers/tenants.js\";\n\ntype MediaVisibility =\n\t(typeof constants.media.visibilityKeys)[keyof typeof constants.media.visibilityKeys];\n\ntype MediaRoot = MediaVisibility | typeof constants.media.awaitingSyncKey;\n\nconst isVisibilityKey = (value: string | undefined): value is MediaVisibility =>\n\tvalue !== undefined &&\n\tObject.values(constants.media.visibilityKeys).includes(\n\t\tvalue as MediaVisibility,\n\t);\n\nconst isRootKey = (value: string | undefined): value is MediaRoot =>\n\tisVisibilityKey(value) || value === constants.media.awaitingSyncKey;\n\nconst isReservedTenantSegment = (value: string | undefined) =>\n\tvalue !== undefined &&\n\t(constants.media.reservedTenantKeys as readonly string[]).includes(value);\n\n/**\n * Generated media IDs use UUIDs without dashes.\n * This keeps global keys distinct when the tenant segment is omitted.\n */\nexport const isGeneratedMediaIdSegment = (value: string | undefined) =>\n\tvalue !== undefined && /^[a-f0-9]{32}$/i.test(value);\n\n/**\n * Parses the root, optional tenant, processed marker, and identity segments once.\n * Media helpers use this so the key shape stays consistent across adapters.\n */\nexport const getMediaKeyParts = (key: string | string[]) => {\n\tconst parts = Array.isArray(key) ? key : key.split(\"/\").filter(Boolean);\n\tconst root = isRootKey(parts[0]) ? parts[0] : null;\n\tconst rootIndex = root ? 0 : -1;\n\tconst visibility = isVisibilityKey(root ?? undefined) ? root : null;\n\tconst visibilityIndex = visibility ? rootIndex : -1;\n\tconst tenantCandidate = parts[rootIndex + 1];\n\tconst hasTenant =\n\t\trootIndex !== -1 &&\n\t\tparts[rootIndex + 2] !== undefined &&\n\t\t!isReservedTenantSegment(tenantCandidate) &&\n\t\t!isGeneratedMediaIdSegment(tenantCandidate);\n\tconst tenantIndex = hasTenant ? rootIndex + 1 : -1;\n\tconst scopedPathStartIndex =\n\t\trootIndex === -1\n\t\t\t? -1\n\t\t\t: tenantIndex === -1\n\t\t\t\t? rootIndex + 1\n\t\t\t\t: tenantIndex + 1;\n\tconst processedIndex =\n\t\tvisibilityIndex !== -1 &&\n\t\tparts[scopedPathStartIndex] === constants.media.processedKey\n\t\t\t? scopedPathStartIndex\n\t\t\t: -1;\n\tconst identityIndex =\n\t\trootIndex === -1\n\t\t\t? -1\n\t\t\t: processedIndex === -1\n\t\t\t\t? scopedPathStartIndex\n\t\t\t\t: processedIndex + 1;\n\n\treturn {\n\t\tparts,\n\t\troot,\n\t\trootIndex,\n\t\tvisibility,\n\t\tvisibilityIndex,\n\t\ttenantKey: tenantIndex === -1 ? null : (parts[tenantIndex] ?? null),\n\t\ttenantIndex,\n\t\tscopedPathStartIndex,\n\t\tprocessedIndex,\n\t\tidentityIndex,\n\t\tidentity: identityIndex === -1 ? null : (parts[identityIndex] ?? null),\n\t\tisProcessed: processedIndex !== -1,\n\t};\n};\n\n/**\n * Finds the public/private segment in the current root-first media key shape.\n */\nexport const getMediaKeyVisibilityIndex = (parts: string[]) => {\n\treturn getMediaKeyParts(parts).visibilityIndex;\n};\n\n/**\n * Finds the storage root in current media keys.\n * Handles canonical media and temporary awaiting-sync upload keys.\n */\nexport const getMediaKeyRootIndex = (parts: string[]) => {\n\treturn getMediaKeyParts(parts).rootIndex;\n};\n\n/**\n * Extracts the tenant key encoded in a media key.\n * Global media omits the tenant segment and resolves to null.\n */\nexport const getMediaKeyTenantKey = (key: string): string | null => {\n\treturn getMediaKeyParts(key).tenantKey;\n};\n\n/**\n * Resolves a configured tenant from a tenant key. Unknown or tenantless keys\n * intentionally resolve to null so adapters can fall back to global storage.\n */\nexport const resolveMediaTenant = (\n\tconfig: Config,\n\ttenantKey: string | null | undefined,\n): TenantConfig | null => {\n\tif (!tenantKey) return null;\n\tif (!Array.isArray(config.tenants)) return null;\n\treturn getTenantConfig(config, tenantKey) ?? null;\n};\n\n/**\n * Resolves the adapter tenant encoded in a media key.\n * CDN and signed storage routes use this because they do not receive tenant headers.\n */\nexport const resolveMediaKeyTenant = (\n\tconfig: Config,\n\tkey: string,\n): TenantConfig | null => resolveMediaTenant(config, getMediaKeyTenantKey(key));\n"],"mappings":"sGASA,MAAM,EAAmB,GACxB,IAAU,IAAA,IACV,OAAO,OAAOA,EAAU,MAAM,cAAc,CAAC,CAAC,SAC7C,CACD,EAEK,EAAa,GAClB,EAAgB,CAAK,GAAK,IAAUA,EAAU,MAAM,gBAE/C,EAA2B,GAChC,IAAU,IAAA,IACTA,EAAU,MAAM,mBAAyC,SAAS,CAAK,EAM5D,EAA6B,GACzC,IAAU,IAAA,IAAa,kBAAkB,KAAK,CAAK,EAMvC,EAAoB,GAA2B,CAC3D,IAAM,EAAQ,MAAM,QAAQ,CAAG,EAAI,EAAM,EAAI,MAAM,GAAG,CAAC,CAAC,OAAO,OAAO,EAChE,EAAO,EAAU,EAAM,EAAE,EAAI,EAAM,GAAK,KACxC,EAAY,EAAO,EAAI,GACvB,EAAa,EAAgB,GAAQ,IAAA,EAAS,EAAI,EAAO,KACzD,EAAkB,EAAa,EAAY,GAC3C,EAAkB,EAAM,EAAY,GAMpC,EAJL,IAAc,IACd,EAAM,EAAY,KAAO,IAAA,IACzB,CAAC,EAAwB,CAAe,GACxC,CAAC,EAA0B,CAAe,EACX,EAAY,EAAI,GAC1C,EACL,IAAc,GACX,GACA,IAAgB,GACf,EAAY,EACZ,EAAc,EACb,EACL,IAAoB,IACpB,EAAM,KAA0BA,EAAU,MAAM,aAC7C,EACA,GACE,EACL,IAAc,GACX,GACA,IAAmB,GAClB,EACA,EAAiB,EAEtB,MAAO,CACN,QACA,OACA,YACA,aACA,kBACA,UAAW,IAAgB,GAAK,KAAQ,EAAM,IAAgB,KAC9D,cACA,uBACA,iBACA,gBACA,SAAU,IAAkB,GAAK,KAAQ,EAAM,IAAkB,KACjE,YAAa,IAAmB,EACjC,CACD,EAqBa,EAAwB,GAC7B,EAAiB,CAAG,CAAC,CAAC,UAOjB,GACZ,EACA,IAEI,CAAC,GACD,CAAC,MAAM,QAAQ,EAAO,OAAO,EAAU,KACpC,EAAgB,EAAQ,CAAS,GAAK,KAOjC,GACZ,EACA,IACyB,EAAmB,EAAQ,EAAqB,CAAG,CAAC"}