@trackunit/iris-app-sdk-vite 0.4.3 → 0.4.4

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/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## 0.4.4 (2026-04-30)
2
+
3
+ This was a version bump only for iris-app-sdk-vite to align it with other projects, there were no code changes.
4
+
1
5
  ## 0.4.3 (2026-04-30)
2
6
 
3
7
  ### 🧱 Updated Dependencies
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trackunit/iris-app-sdk-vite",
3
- "version": "0.4.3",
3
+ "version": "0.4.4",
4
4
  "license": "SEE LICENSE IN LICENSE.txt",
5
5
  "repository": "https://github.com/Trackunit/manager",
6
6
  "executors": "./executors.json",
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../libs/iris-app-sdk/vite/src/executors/build/executor.ts"],"names":[],"mappings":"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","sourcesContent":["import { ExecutorContext } from \"@nx/devkit\";\nimport { eachValueFrom } from \"@nx/devkit/src/utils/rxjs-for-await\";\nimport { checkPackageVersion, enableTsConfigPath } from \"@trackunit/iris-app-build-utilities\";\nimport { existsSync, rmSync } from \"fs\";\nimport { join, resolve } from \"path\";\nimport { Observable } from \"rxjs\";\nimport * as op from \"rxjs/operators\";\nimport \"win-ca\";\nimport { getDefaultViteConfig } from \"../utils/defaultViteConfig\";\nimport { BuildExecutorSchema } from \"./schema\";\n\nexport type NodeBuildEvent = {\n outfile: string;\n success: boolean;\n};\n\nconst deleteOutputDir = (root: string, outputPath: string) => {\n const resolvedOutputPath = resolve(root, outputPath);\n if (resolvedOutputPath === root) {\n throw new Error(\"Output path MUST not be project root directory!\");\n }\n rmSync(resolvedOutputPath, { recursive: true, force: true });\n};\n\n/**\n * Build executor for building Iris Apps with Vite.\n *\n * @param {BuildExecutorSchema} options build executor options for this nx executor\n * @param {ExecutorContext} context build executor context for this nx executor\n * @yields {NodeBuildEvent} the build event\n */\nexport default async function* buildExecutor(\n options: BuildExecutorSchema,\n context: ExecutorContext\n): AsyncGenerator<NodeBuildEvent> {\n await checkPackageVersion(false);\n const skipTypeChecking = options.skipTypeChecking ?? false;\n\n // eslint-disable-next-line @trackunit/no-typescript-assertion, @typescript-eslint/no-non-null-assertion\n const projectRoot = context.projectsConfigurations.projects[context.projectName!]!.root;\n const projectRootDir = join(context.root, projectRoot);\n const manifestPath = join(context.root, projectRoot, \"iris-app-manifest.ts\");\n\n // āœ… CRITICAL: Register tsconfig paths BEFORE any @trackunit/* imports\n enableTsConfigPath({\n projectRootDir,\n });\n\n // Now we can safely import the manifest (it uses @trackunit/* imports)\n const IrisAppManifest = (await import(manifestPath)).default;\n\n const defaultConfig = await getDefaultViteConfig(\n \"production\",\n context.root,\n projectRootDir,\n IrisAppManifest,\n context,\n {\n skipTypeChecking,\n }\n );\n\n // Load optional custom config\n let finalConfig = defaultConfig;\n if (options.viteConfig || existsSync(join(projectRootDir, \"vite.config.ts\"))) {\n const customConfigFile = await import(options.viteConfig ?? join(projectRootDir, \"vite.config.ts\"));\n let customConfig = customConfigFile.default(defaultConfig);\n const isPromise = customConfig instanceof Promise;\n if (isPromise) {\n customConfig = await customConfig;\n }\n finalConfig = customConfig;\n }\n\n // Set output path\n finalConfig.build = {\n ...finalConfig.build,\n outDir: resolve(context.root, options.outputPath),\n emptyOutDir: true,\n };\n\n // Delete existing output\n deleteOutputDir(context.root, options.outputPath);\n\n if (context.isVerbose) {\n // eslint-disable-next-line no-console\n console.log(\"Using Vite config\", JSON.stringify(finalConfig.plugins, null, 2));\n }\n\n const observable = new Observable<NodeBuildEvent>(subscriber => {\n void (async () => {\n try {\n const { build } = await import(\"vite\");\n\n const buildResult = await build({ ...finalConfig, configFile: false });\n\n // Log build stats if available\n if (context.isVerbose) {\n // eslint-disable-next-line no-console\n console.log(\"Build completed:\", buildResult);\n }\n\n subscriber.next({\n success: true,\n outfile: resolve(context.root, options.outputPath),\n });\n subscriber.complete();\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error(\"Vite build error:\", error);\n subscriber.next({\n success: false,\n outfile: resolve(context.root, options.outputPath),\n });\n subscriber.complete();\n }\n })();\n }).pipe(\n op.tap(({ success, outfile }) => {\n if (success) {\n // eslint-disable-next-line no-console\n console.info(`\\n āœ… Iris App build completed: ${outfile}\\n`);\n } else {\n // eslint-disable-next-line no-console\n console.error(`\\n āŒ Iris App build failed\\n`);\n }\n })\n );\n\n return yield* eachValueFrom(observable);\n}\n"]}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../libs/iris-app-sdk/vite/src/executors/serve/executor.ts"],"names":[],"mappings":"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","sourcesContent":["import { ExecutorContext } from \"@nx/devkit\";\nimport { eachValueFrom } from \"@nx/devkit/src/utils/rxjs-for-await\";\nimport { getSettings } from \"@trackunit/iris-app\";\nimport { IrisAppManifest } from \"@trackunit/iris-app-api\";\nimport {\n checkPackageVersion,\n enableTsConfigPath,\n spawnServersideExtensions,\n} from \"@trackunit/iris-app-build-utilities\";\nimport { existsSync } from \"fs\";\nimport { join } from \"path\";\nimport { Observable } from \"rxjs\";\nimport * as op from \"rxjs/operators\";\nimport \"win-ca\";\nimport { checkCustomFields } from \"../utils/checkCustomFields\";\nimport { getDefaultViteConfig } from \"../utils/defaultViteConfig\";\nimport { ServeExecutorSchema } from \"./schema\";\ntype ServeResult = { baseUrl: string; success: boolean };\n\n/**\n * Serve executor for serving Iris Apps with Vite.\n *\n * @param {ServeExecutorSchema} options serve executor options for this nx executor\n * @param {ExecutorContext} context serve executor context for this nx executor\n * @yields {ServeResult} the serve result\n */\nexport default async function* serveExecutor(\n options: ServeExecutorSchema,\n context: ExecutorContext\n): AsyncGenerator<ServeResult> {\n await checkPackageVersion(false);\n const settings = getSettings();\n const skipTypeChecking = options.skipTypeChecking ?? false;\n\n // eslint-disable-next-line @trackunit/no-typescript-assertion, @typescript-eslint/no-non-null-assertion\n const projectRoot = context.projectsConfigurations.projects[context.projectName!]!.root;\n const projectRootDir = join(context.root, projectRoot);\n const manifestPath = join(context.root, projectRoot, \"iris-app-manifest.ts\");\n\n // āœ… CRITICAL: Register tsconfig paths BEFORE any @trackunit/* imports\n enableTsConfigPath({\n projectRootDir,\n });\n\n // Now we can safely import the manifest (it uses @trackunit/* imports)\n const irisAppManifest: IrisAppManifest = (await import(manifestPath)).default;\n\n const checkCustomFieldsResult = await checkCustomFields(irisAppManifest, settings);\n if (checkCustomFieldsResult === \"out-of-sync\") {\n throw new Error(\n \"Custom fields are out of sync with the latest approved manifest, please submit and get this version approved before you continue developing the app.\"\n );\n }\n if (checkCustomFieldsResult === \"not-submitted\") {\n throw new Error(\n \"Custom fields are not submitted, please submit and get this version approved before you continue developing the app.\"\n );\n }\n\n const serversideResult = await spawnServersideExtensions(irisAppManifest, context.root);\n\n try {\n // Get default config (internally imports @trackunit/iris-app-vite-plugin)\n const defaultConfig = await getDefaultViteConfig(\n \"development\",\n context.root,\n projectRootDir,\n irisAppManifest,\n context,\n {\n serversidePortMap: serversideResult.portMap,\n skipTypeChecking,\n }\n );\n\n // Load optional custom config\n let finalConfig = defaultConfig;\n if (options.viteConfig || existsSync(join(projectRootDir, \"vite.config.ts\"))) {\n const customConfigFile = await import(options.viteConfig ?? join(projectRootDir, \"vite.config.ts\"));\n let customConfig = customConfigFile.default(defaultConfig);\n const isPromise = customConfig instanceof Promise;\n if (isPromise) {\n customConfig = await customConfig;\n }\n finalConfig = customConfig;\n }\n\n // Apply port override if specified\n if (options.port !== undefined) {\n finalConfig.server = { ...finalConfig.server, port: options.port };\n }\n if (options.host) {\n finalConfig.server = { ...finalConfig.server, host: options.host };\n }\n\n if (context.isVerbose) {\n // eslint-disable-next-line no-console\n console.log(\"Using Vite config\", JSON.stringify(finalConfig.plugins, null, 2));\n }\n\n // Create and start Vite dev server\n const observable = new Observable<{ baseUrl: string; success: boolean }>(subscriber => {\n void (async () => {\n try {\n // Dynamic import vite to avoid issues\n const { createServer } = await import(\"vite\");\n\n const server = await createServer({ ...finalConfig, configFile: false });\n await server.listen();\n\n const address = server.httpServer?.address();\n const port = typeof address === \"object\" && address !== null ? address.port : (options.port ?? 22220);\n const host = options.host ?? \"localhost\";\n const baseUrl = `http://${host}:${port}`;\n\n subscriber.next({\n baseUrl,\n success: true,\n });\n\n // Keep alive - server continues running until interrupted\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error(\"Vite server error:\", error);\n subscriber.error(error);\n }\n })();\n\n return () => {\n // Cleanup on unsubscribe - handled by Vite's signal handling\n };\n }).pipe(\n op.tap(({ baseUrl }) => {\n // eslint-disable-next-line no-console\n console.info(\n `\\n šŸš€ Iris App dev server running at: ${baseUrl} for iris app: ${irisAppManifest.moduleFederationName}\\n`\n );\n }),\n op.map(({ baseUrl, success }) => ({\n baseUrl,\n success,\n })),\n op.finalize(() => {\n serversideResult.cleanup();\n })\n );\n\n return yield* eachValueFrom(observable);\n } catch (error) {\n serversideResult.cleanup();\n throw error;\n }\n}\n"]}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accessTokenCache.js","sourceRoot":"","sources":["../../../../../../../libs/iris-app-sdk/vite/src/executors/utils/accessTokenCache.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Settings, getAccessToken } from \"@trackunit/iris-app\";\nimport { getPassword, setPassword } from \"cross-keychain\";\nimport jwtDecode from \"jwt-decode\";\n\nconst SERVICE = \"trackunit-iris-app-sdk\";\nconst SKEW_MS = 60_000;\n\ntype CachedTokenPayload = {\n readonly access_token: string;\n readonly token_type: string;\n readonly scope: string;\n readonly id_token: string;\n readonly expires_at: number;\n};\n\ntype TokenDataSuccess = {\n readonly token_type: string;\n readonly expires_in: number;\n readonly access_token: string;\n readonly scope: string;\n readonly id_token: string;\n};\n\n/**\n * Returns an access token, preferring a cached keychain entry when it is still\n * valid. Falls through to the interactive device-code login when the cache is\n * missing, expired, or unreadable, then persists the fresh token.\n *\n * CI paths (TU_TOKEN / TU_CLIENT_ID_*) bypass the cache entirely.\n */\nexport const getCachedAccessToken = async (settings: Settings): Promise<TokenDataSuccess> => {\n if (isCiEnvironment(settings.env)) {\n return unwrapTokenData(await getAccessToken(settings.env));\n }\n\n // on mac you can delete it using security delete-generic-password -s \"trackunit-iris-app-sdk\" -a \"iris-app-serve-access-token-LOCAL\"\n const account = `iris-app-serve-access-token-${settings.env}`;\n\n const cached = await safeReadToken(account);\n if (cached && cached.expires_at - SKEW_MS > Date.now()) {\n // eslint-disable-next-line no-console\n console.log(\"šŸ”‘ Using cached access token from system keychain.\");\n return {\n access_token: cached.access_token,\n token_type: cached.token_type,\n scope: cached.scope,\n id_token: cached.id_token,\n expires_in: Math.max(0, Math.floor((cached.expires_at - Date.now()) / 1000)),\n };\n }\n\n const fresh = unwrapTokenData(await getAccessToken(settings.env));\n await safeStoreToken(account, {\n access_token: fresh.access_token,\n token_type: fresh.token_type,\n scope: fresh.scope,\n id_token: fresh.id_token,\n expires_at: computeExpiresAt(fresh),\n });\n\n return fresh;\n};\n\nconst isCiEnvironment = (env: string): boolean =>\n Boolean(process.env.TU_TOKEN) || Boolean(process.env[`TU_CLIENT_ID_${env}`]);\n\nconst unwrapTokenData = (tokenData: Awaited<ReturnType<typeof getAccessToken>>): TokenDataSuccess => {\n if (!(\"access_token\" in tokenData)) {\n if (\"error\" in tokenData && tokenData.error === \"expired_token\") {\n throw new Error(\"Authentication attempt expired\");\n }\n const description = \"error_description\" in tokenData ? ` ${tokenData.error_description}` : \"\";\n const errorName = \"error\" in tokenData ? tokenData.error : \"unknown\";\n throw new Error(`Failed to get access token: ${errorName}${description}`);\n }\n return tokenData;\n};\n\nconst computeExpiresAt = (token: TokenDataSuccess): number => {\n try {\n const decoded: unknown = jwtDecode(token.access_token);\n if (decoded !== null && typeof decoded === \"object\" && \"exp\" in decoded && typeof decoded.exp === \"number\") {\n return decoded.exp * 1000;\n }\n } catch {\n // JWT decode failed — fall through to expires_in\n }\n return Date.now() + token.expires_in * 1000;\n};\n\nconst safeReadToken = async (account: string): Promise<CachedTokenPayload | undefined> => {\n try {\n const raw = await getPassword(SERVICE, account);\n if (!raw) {\n return undefined;\n }\n const parsed: unknown = JSON.parse(raw);\n if (!isValidCachedPayload(parsed)) {\n return undefined;\n }\n return parsed;\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error);\n // eslint-disable-next-line no-console\n console.warn(`āš ļø Could not read cached token from system keychain: ${reason}. Falling back to interactive login.`);\n return undefined;\n }\n};\n\nconst safeStoreToken = async (account: string, payload: CachedTokenPayload): Promise<void> => {\n try {\n await setPassword(SERVICE, account, JSON.stringify(payload));\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error);\n // eslint-disable-next-line no-console\n console.warn(`āš ļø Could not cache access token in system keychain: ${reason}. Token will not be persisted.`);\n }\n};\n\nconst isValidCachedPayload = (value: unknown): value is CachedTokenPayload =>\n value !== null &&\n typeof value === \"object\" &&\n \"access_token\" in value &&\n typeof value.access_token === \"string\" &&\n \"expires_at\" in value &&\n typeof value.expires_at === \"number\" &&\n \"token_type\" in value &&\n typeof value.token_type === \"string\" &&\n \"scope\" in value &&\n typeof value.scope === \"string\" &&\n \"id_token\" in value &&\n typeof value.id_token === \"string\";\n"]}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkCustomFields.js","sourceRoot":"","sources":["../../../../../../../libs/iris-app-sdk/vite/src/executors/utils/checkCustomFields.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Settings } from \"@trackunit/iris-app\";\nimport { CustomFieldDefinition, IrisAppManifest, PublicIrisAppManifest } from \"@trackunit/iris-app-api\";\nimport { getCachedAccessToken } from \"./accessTokenCache\";\nimport { deepEqual } from \"./deepEqual\";\n\nexport type CheckCustomFieldsResult = \"in-sync\" | \"out-of-sync\" | \"not-submitted\";\n\n/**\n * Checks if the custom field definitions are in sync with the latest approved manifest.\n *\n * @param irisAppManifest - The iris app manifest.\n */\nexport const checkCustomFields = async (\n irisAppManifest: IrisAppManifest,\n settings: Settings\n): Promise<CheckCustomFieldsResult> => {\n const localFields = irisAppManifest.customFieldDefinitions ?? [];\n\n if (localFields.length === 0) {\n return \"in-sync\";\n }\n\n // eslint-disable-next-line no-console\n console.log(\n \"This app is using custom fields, fetching the latest approved manifest to validate that the local custom field definitions are in sync.\"\n );\n const tokenData = await getCachedAccessToken(settings);\n\n const latestApprovedManifestIfAny = await fetchLatestApprovedManifest(\n irisAppManifest.moduleFederationName,\n tokenData.access_token,\n settings\n );\n\n if (!latestApprovedManifestIfAny) {\n return \"not-submitted\";\n }\n\n const approvedFields = latestApprovedManifestIfAny.customFieldDefinitions ?? [];\n\n // eslint-disable-next-line no-console\n console.log(\n `Latest approved manifest for ${irisAppManifest.moduleFederationName} has ${approvedFields.length} custom field definition(s).`\n );\n\n if (hasCustomFieldDrift(localFields, approvedFields)) {\n return \"out-of-sync\";\n }\n\n return \"in-sync\";\n};\n\nconst stripTranslations = (field: CustomFieldDefinition): Omit<CustomFieldDefinition, \"translations\"> => {\n const { translations, ...rest } = field;\n return rest;\n};\n\n/**\n * Returns true when the local and approved custom field sets differ.\n * The comparison is symmetric: added, removed, and modified fields are all detected.\n * Translations are excluded from the comparison.\n */\nconst hasCustomFieldDrift = (\n localFields: Array<CustomFieldDefinition>,\n approvedFields: Array<CustomFieldDefinition>\n): boolean => {\n if (localFields.length !== approvedFields.length) {\n return true;\n }\n\n return localFields.some(local => {\n const approved = approvedFields.find(a => a.key === local.key);\n if (!approved) {\n return true;\n }\n return !deepEqual(stripTranslations(local), stripTranslations(approved));\n });\n};\n\nconst LATEST_APPROVED_MANIFEST_QUERY = `query LatestApprovedManifest($irisAppId: String!) {\n manifest(irisAppId: $irisAppId)\n}`;\n\n/**\n * Fetches the latest approved public manifest for the given iris app id from the\n * internal GraphQL endpoint. Returns null if the app is not subscribed on the\n * caller's account or does not have an approved version yet.\n *\n * @param {string} irisAppId The iris app id, typically the manifest's moduleFederationName.\n * @param {string} accessToken The Trackunit access token to authenticate the request.\n * @param {Settings} settings The Iris App SDK settings.\n * @returns {Promise<PublicIrisAppManifest | null>} The latest approved manifest or null.\n */\nasync function fetchLatestApprovedManifest(\n irisAppId: string,\n accessToken: string,\n settings: Settings\n): Promise<PublicIrisAppManifest | null> {\n const response = await fetch(settings.graphqlInternalUrl.toString(), {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${accessToken}`,\n },\n body: JSON.stringify({\n operationName: \"LatestApprovedManifest\",\n query: LATEST_APPROVED_MANIFEST_QUERY,\n variables: { irisAppId },\n }),\n });\n\n if (!response.ok) {\n throw new Error(\n `Failed to fetch latest approved manifest: ${response.status} ${response.statusText} ${await response.text()}`\n );\n }\n\n const body: { data?: { manifest: PublicIrisAppManifest | null }; errors?: Array<{ message: string }> } =\n await response.json();\n\n if (body.errors && body.errors.length > 0) {\n throw new Error(`GraphQL error fetching latest approved manifest: ${body.errors.map(e => e.message).join(\", \")}`);\n }\n\n return body.data?.manifest ?? null;\n}\n"]}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deepEqual.js","sourceRoot":"","sources":["../../../../../../../libs/iris-app-sdk/vite/src/executors/utils/deepEqual.ts"],"names":[],"mappings":";;;AAAA;;;;;;GAMG;AACI,MAAM,SAAS,GAAG,CAAC,CAAU,EAAE,CAAU,EAAW,EAAE;IAC3D,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACZ,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;QAC/E,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;QAC1C,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;QACzC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC;YAC1B,OAAO,KAAK,CAAC;QACf,CAAC;QACD,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,IAAA,iBAAS,EAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACrD,CAAC;IACD,8DAA8D;IAC9D,MAAM,IAAI,GAAG,CAA4B,CAAC;IAC1C,8DAA8D;IAC9D,MAAM,IAAI,GAAG,CAA4B,CAAC;IAC1C,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,EAAE,CAAC;QAClC,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,IAAA,iBAAS,EAAC,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AAC7D,CAAC,CAAC;AA1BW,QAAA,SAAS,aA0BpB","sourcesContent":["/**\n * Deeply compares two values for equality.\n *\n * @param a - The first value to compare.\n * @param b - The second value to compare.\n * @returns {boolean} - true if the values are equal, false otherwise.\n */\nexport const deepEqual = (a: unknown, b: unknown): boolean => {\n if (a === b) {\n return true;\n }\n if (a === null || b === null || typeof a !== \"object\" || typeof b !== \"object\") {\n return false;\n }\n if (Array.isArray(a) !== Array.isArray(b)) {\n return false;\n }\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) {\n return false;\n }\n return a.every((item, i) => deepEqual(item, b[i]));\n }\n // eslint-disable-next-line @trackunit/no-typescript-assertion\n const aObj = a as Record<string, unknown>;\n // eslint-disable-next-line @trackunit/no-typescript-assertion\n const bObj = b as Record<string, unknown>;\n const aKeys = Object.keys(aObj);\n const bKeys = Object.keys(bObj);\n if (aKeys.length !== bKeys.length) {\n return false;\n }\n return aKeys.every(key => deepEqual(aObj[key], bObj[key]));\n};\n"]}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaultViteConfig.js","sourceRoot":"","sources":["../../../../../../../libs/iris-app-sdk/vite/src/executors/utils/defaultViteConfig.ts"],"names":[],"mappings":";;;AAIA,qFAAkF;AAElF,MAAM,gBAAgB,GAAG,wBAAwB,CAAC;AAClD,MAAM,yBAAyB,GAAG,KAAK,gBAAgB,EAAE,CAAC;AAE1D;;;;;;GAMG;AACH,MAAM,kBAAkB,GAAiB;IACvC,IAAI,EAAE,wBAAwB;IAC9B,SAAS,CAAC,EAAU;QAClB,IAAI,EAAE,KAAK,gBAAgB,EAAE,CAAC;YAC5B,OAAO,yBAAyB,CAAC;QACnC,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC,EAAU;QACb,IAAI,EAAE,KAAK,yBAAyB,EAAE,CAAC;YACrC,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAC;AAOF;;;;;;;;;GASG;AACI,MAAM,oBAAoB,GAAG,KAAK,EACvC,IAAkC,EAClC,SAAiB,EACjB,MAAc,EACd,gBAAiC,EACjC,OAAwB,EACxB,UAAuC,EAAE,EACpB,EAAE;IACvB,6DAA6D;IAE7D,MAAM,cAAc,GAAG,MAAM,IAAA,+DAA8B,EAAC;QAC1D,MAAM;QACN,aAAa,EAAE,SAAS;QACxB,MAAM,EAAE,EAAE,IAAI,EAAE;QAChB,iBAAiB,EAAE,OAAO,CAAC,iBAAiB;QAC5C,gBAAgB,EAAE,OAAO,CAAC,gBAAgB;KAC3C,CAAC,CAAC;IAEH,OAAO;QACL,IAAI,EAAE,MAAM;QACZ,IAAI;QACJ,OAAO,EAAE,CAAC,GAAG,cAAc,EAAE,kBAAkB,CAAC;QAChD,mCAAmC;QACnC,YAAY,EAAE;YACZ,mEAAmE;YACnE,OAAO,EAAE,CAAC,OAAO,EAAE,WAAW,EAAE,mBAAmB,CAAC;YACpD,6CAA6C;YAC7C,cAAc,EAAE;gBACd,MAAM,EAAE,QAAQ;aACjB;SACF;QACD,KAAK,EAAE;YACL,MAAM,EAAE,aAAa,OAAO,CAAC,WAAW,EAAE;YAC1C,WAAW,EAAE,IAAI;YACjB,+DAA+D;YAC/D,MAAM,EAAE,QAAQ;YAChB,yDAAyD;YACzD,MAAM,EAAE,SAAS;YACjB,sDAAsD;YACtD,SAAS,EAAE,KAAK;YAChB,oCAAoC;YACpC,qBAAqB,EAAE,IAAI;YAC3B,8DAA8D;YAC9D,SAAS,EAAE,EAAE;YACb,2EAA2E;YAC3E,oEAAoE;YACpE,aAAa,EAAE;gBACb,KAAK,EAAE,gBAAgB;gBACvB,mEAAmE;gBACnE,MAAM,EAAE;oBACN,mCAAmC;oBACnC,MAAM,EAAE,KAAK;oBACb,6CAA6C;oBAC7C,cAAc,EAAE,WAAW;oBAC3B,mBAAmB;oBACnB,cAAc,EAAE,kBAAkB;oBAClC,gBAAgB;oBAChB,cAAc,EAAE,+BAA+B;iBAChD;aACF;SACF;KACF,CAAC;AACJ,CAAC,CAAC;AA9DW,QAAA,oBAAoB,wBA8D/B","sourcesContent":["import { ExecutorContext } from \"@nx/devkit\";\nimport type { IrisAppManifest } from \"@trackunit/iris-app-api\";\nimport type { ServersidePortMap } from \"@trackunit/iris-app-build-utilities\";\nimport type { PluginOption, UserConfig } from \"vite\";\nimport { getTrackunitIrisAppVitePlugins } from \"./getTrackunitIrisAppVitePlugins\";\n\nconst VIRTUAL_ENTRY_ID = \"virtual:iris-app-entry\";\nconst RESOLVED_VIRTUAL_ENTRY_ID = `\\0${VIRTUAL_ENTRY_ID}`;\n\n/**\n * Rollup plugin providing a virtual empty entry module.\n * Iris apps use Module Federation which generates remoteEntry.js as the real entry,\n * so we just need a no-op input to satisfy Rollup's requirement for an entry point.\n * Using a virtual module avoids tsconfig path resolution issues that occur when\n * referencing physical files inside node_modules.\n */\nconst virtualEntryPlugin: PluginOption = {\n name: \"iris-app-virtual-entry\",\n resolveId(id: string) {\n if (id === VIRTUAL_ENTRY_ID) {\n return RESOLVED_VIRTUAL_ENTRY_ID;\n }\n return null;\n },\n load(id: string) {\n if (id === RESOLVED_VIRTUAL_ENTRY_ID) {\n return \"\";\n }\n return null;\n },\n};\n\ntype GetDefaultViteConfigOptions = {\n serversidePortMap?: ServersidePortMap;\n skipTypeChecking?: boolean;\n};\n\n/**\n * Gets the default Vite config for Iris Apps.\n *\n * @param mode - \"production\" or \"development\"\n * @param nxRootDir - the root of the nx workspace\n * @param appDir - the app directory\n * @param _irisAppManifest - the iris app manifest (used for logging/validation)\n * @param context - NX executor context\n * @returns {Promise<UserConfig>} the default Vite config\n */\nexport const getDefaultViteConfig = async (\n mode: \"production\" | \"development\",\n nxRootDir: string,\n appDir: string,\n _irisAppManifest: IrisAppManifest,\n context: ExecutorContext,\n options: GetDefaultViteConfigOptions = {}\n): Promise<UserConfig> => {\n // Dynamic import - safe after enableTsConfigPath() is called\n\n const irisAppPlugins = await getTrackunitIrisAppVitePlugins({\n appDir,\n workspaceRoot: nxRootDir,\n config: { mode },\n serversidePortMap: options.serversidePortMap,\n skipTypeChecking: options.skipTypeChecking,\n });\n\n return {\n root: appDir,\n mode,\n plugins: [...irisAppPlugins, virtualEntryPlugin],\n // Optimize dependency pre-bundling\n optimizeDeps: {\n // Force include common dependencies to speed up dev server startup\n include: [\"react\", \"react-dom\", \"react/jsx-runtime\"],\n // Use esbuild for faster dependency scanning\n esbuildOptions: {\n target: \"esnext\",\n },\n },\n build: {\n outDir: `dist/apps/${context.projectName}`,\n emptyOutDir: true,\n // Target modern browsers - faster builds, no legacy transforms\n target: \"esnext\",\n // Use esbuild for minification (MUCH faster than terser)\n minify: \"esbuild\",\n // Disable source maps in production for faster builds\n sourcemap: false,\n // Increase chunk size warning limit\n chunkSizeWarningLimit: 1000,\n // Put remoteEntry.js at root (not in assets/) to match rspack\n assetsDir: \"\",\n // Use JavaScript entry instead of index.html for Module Federation remotes\n // The MF plugin generates remoteEntry.js as the actual bundle entry\n rollupOptions: {\n input: VIRTUAL_ENTRY_ID,\n // Preserve the entry file name for Module Federation compatibility\n output: {\n // ESM format for Module Federation\n format: \"esm\",\n // Entry files at root level (same as rspack)\n entryFileNames: \"[name].js\",\n // Chunks with hash\n chunkFileNames: \"[name]-[hash].js\",\n // Static assets\n assetFileNames: \"static/[name]-[hash][extname]\",\n },\n },\n },\n };\n};\n"]}
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.wrapResponseTiming = exports.shortenPath = exports.timeAsync = exports.time = exports.logEvent = exports.logTiming = void 0;
4
+ // ---------------------------------------------------------------------------
5
+ // Dev-server timing instrumentation
6
+ //
7
+ // Lightweight, zero-dependency timing logs for the Iris-app dev server.
8
+ // Disabled by default; opt in with IRIS_TIMING=true. Logs to stdout with the
9
+ // "[iris-timing]" prefix so they're easy to grep / filter.
10
+ //
11
+ // Each log line looks like:
12
+ // [iris-timing] T+<elapsed_since_module_load> <duration> <label>
13
+ //
14
+ // Durations >1000ms are highlighted red, >200ms yellow, so the slow stuff
15
+ // jumps out in a typical terminal. Per-request lines below the
16
+ // TRANSFORM_LOG_THRESHOLD_MS are dropped to keep the output usable.
17
+ // ---------------------------------------------------------------------------
18
+ // Read lazily so tests / users can flip IRIS_TIMING at runtime.
19
+ // Disabled by default; set IRIS_TIMING=true to enable.
20
+ const isTimingEnabled = () => process.env.IRIS_TIMING === "true";
21
+ const TRANSFORM_LOG_THRESHOLD_MS = 50;
22
+ const TIMING_MODULE_START = performance.now();
23
+ const padMs = (ms) => `${ms.toFixed(0).padStart(6, " ")}ms`;
24
+ const colorize = (ms, text) => {
25
+ if (ms > 1000)
26
+ return `\x1b[31m${text}\x1b[0m`; // red
27
+ if (ms > 200)
28
+ return `\x1b[33m${text}\x1b[0m`; // yellow
29
+ return text;
30
+ };
31
+ /**
32
+ * Logs a single timing line: elapsed-since-module-load, duration, and label.
33
+ * Slow durations are colorized so they stand out in the terminal.
34
+ */
35
+ const logTiming = (durationMs, label) => {
36
+ if (!isTimingEnabled())
37
+ return;
38
+ const elapsed = padMs(performance.now() - TIMING_MODULE_START);
39
+ const dur = colorize(durationMs, padMs(durationMs));
40
+ // eslint-disable-next-line no-console
41
+ console.log(`\x1b[36m[iris-timing]\x1b[0m T+${elapsed} ${dur} ${label}`);
42
+ };
43
+ exports.logTiming = logTiming;
44
+ /**
45
+ * Logs a non-timed event (no duration column), e.g. "server: listening".
46
+ * Useful for marking moments in the dev-server lifecycle.
47
+ */
48
+ const logEvent = (label) => {
49
+ if (!isTimingEnabled())
50
+ return;
51
+ const elapsed = padMs(performance.now() - TIMING_MODULE_START);
52
+ // eslint-disable-next-line no-console
53
+ console.log(`\x1b[36m[iris-timing]\x1b[0m T+${elapsed} ${label}`);
54
+ };
55
+ exports.logEvent = logEvent;
56
+ /** Wraps a synchronous function and logs its duration. */
57
+ const time = (label, fn) => {
58
+ if (!isTimingEnabled())
59
+ return fn();
60
+ const start = performance.now();
61
+ try {
62
+ return fn();
63
+ }
64
+ finally {
65
+ (0, exports.logTiming)(performance.now() - start, label);
66
+ }
67
+ };
68
+ exports.time = time;
69
+ /** Wraps an async function and logs its duration. */
70
+ const timeAsync = async (label, fn) => {
71
+ if (!isTimingEnabled())
72
+ return fn();
73
+ const start = performance.now();
74
+ try {
75
+ return await fn();
76
+ }
77
+ finally {
78
+ (0, exports.logTiming)(performance.now() - start, label);
79
+ }
80
+ };
81
+ exports.timeAsync = timeAsync;
82
+ /**
83
+ * Shortens long absolute paths to a workspace-relative form for log lines.
84
+ * "/@fs/Users/mta/.../manager1/libs/css/core/src/lib/core.css"
85
+ * -> "libs/css/core/src/lib/core.css"
86
+ */
87
+ const shortenPath = (urlOrPath) => {
88
+ const idx = urlOrPath.indexOf("/manager1/");
89
+ if (idx >= 0)
90
+ return urlOrPath.substring(idx + "/manager1/".length);
91
+ const fsPrefix = "/@fs/";
92
+ if (urlOrPath.startsWith(fsPrefix))
93
+ return urlOrPath.substring(fsPrefix.length);
94
+ return urlOrPath;
95
+ };
96
+ exports.shortenPath = shortenPath;
97
+ /**
98
+ * Logs the total server-side response time per request via the `finish` event,
99
+ * which fires once the response has been fully written. This corresponds to
100
+ * the `wait` time you see in browser network panels.
101
+ *
102
+ * Combined with the per-handler timings inside our own middleware, it lets us
103
+ * see whether slowness is in our code or in downstream Vite work (transforms,
104
+ * optimizeDeps, etc).
105
+ */
106
+ const wrapResponseTiming = (req, res) => {
107
+ if (!isTimingEnabled())
108
+ return;
109
+ const start = performance.now();
110
+ res.on("finish", () => {
111
+ const durationMs = performance.now() - start;
112
+ if (durationMs >= TRANSFORM_LOG_THRESHOLD_MS) {
113
+ const url = req.url ?? "(no url)";
114
+ (0, exports.logTiming)(durationMs, `${req.method ?? "GET"} ${(0, exports.shortenPath)(url)}`);
115
+ }
116
+ });
117
+ };
118
+ exports.wrapResponseTiming = wrapResponseTiming;
119
+ //# sourceMappingURL=devServerTiming.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"devServerTiming.js","sourceRoot":"","sources":["../../../../../../../libs/iris-app-sdk/vite/src/executors/utils/devServerTiming.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IncomingMessage, ServerResponse } from \"node:http\";\n\n// ---------------------------------------------------------------------------\n// Dev-server timing instrumentation\n//\n// Lightweight, zero-dependency timing logs for the Iris-app dev server.\n// Disabled by default; opt in with IRIS_TIMING=true. Logs to stdout with the\n// \"[iris-timing]\" prefix so they're easy to grep / filter.\n//\n// Each log line looks like:\n// [iris-timing] T+<elapsed_since_module_load> <duration> <label>\n//\n// Durations >1000ms are highlighted red, >200ms yellow, so the slow stuff\n// jumps out in a typical terminal. Per-request lines below the\n// TRANSFORM_LOG_THRESHOLD_MS are dropped to keep the output usable.\n// ---------------------------------------------------------------------------\n\n// Read lazily so tests / users can flip IRIS_TIMING at runtime.\n// Disabled by default; set IRIS_TIMING=true to enable.\nconst isTimingEnabled = (): boolean => process.env.IRIS_TIMING === \"true\";\nconst TRANSFORM_LOG_THRESHOLD_MS = 50;\nconst TIMING_MODULE_START = performance.now();\n\nconst padMs = (ms: number): string => `${ms.toFixed(0).padStart(6, \" \")}ms`;\nconst colorize = (ms: number, text: string): string => {\n if (ms > 1000) return `\\x1b[31m${text}\\x1b[0m`; // red\n if (ms > 200) return `\\x1b[33m${text}\\x1b[0m`; // yellow\n return text;\n};\n\n/**\n * Logs a single timing line: elapsed-since-module-load, duration, and label.\n * Slow durations are colorized so they stand out in the terminal.\n */\nexport const logTiming = (durationMs: number, label: string): void => {\n if (!isTimingEnabled()) return;\n const elapsed = padMs(performance.now() - TIMING_MODULE_START);\n const dur = colorize(durationMs, padMs(durationMs));\n // eslint-disable-next-line no-console\n console.log(`\\x1b[36m[iris-timing]\\x1b[0m T+${elapsed} ${dur} ${label}`);\n};\n\n/**\n * Logs a non-timed event (no duration column), e.g. \"server: listening\".\n * Useful for marking moments in the dev-server lifecycle.\n */\nexport const logEvent = (label: string): void => {\n if (!isTimingEnabled()) return;\n const elapsed = padMs(performance.now() - TIMING_MODULE_START);\n // eslint-disable-next-line no-console\n console.log(`\\x1b[36m[iris-timing]\\x1b[0m T+${elapsed} ${label}`);\n};\n\n/** Wraps a synchronous function and logs its duration. */\nexport const time = <TResult>(label: string, fn: () => TResult): TResult => {\n if (!isTimingEnabled()) return fn();\n const start = performance.now();\n try {\n return fn();\n } finally {\n logTiming(performance.now() - start, label);\n }\n};\n\n/** Wraps an async function and logs its duration. */\nexport const timeAsync = async <TResult>(label: string, fn: () => Promise<TResult> | TResult): Promise<TResult> => {\n if (!isTimingEnabled()) return fn();\n const start = performance.now();\n try {\n return await fn();\n } finally {\n logTiming(performance.now() - start, label);\n }\n};\n\n/**\n * Shortens long absolute paths to a workspace-relative form for log lines.\n * \"/@fs/Users/mta/.../manager1/libs/css/core/src/lib/core.css\"\n * -> \"libs/css/core/src/lib/core.css\"\n */\nexport const shortenPath = (urlOrPath: string): string => {\n const idx = urlOrPath.indexOf(\"/manager1/\");\n if (idx >= 0) return urlOrPath.substring(idx + \"/manager1/\".length);\n const fsPrefix = \"/@fs/\";\n if (urlOrPath.startsWith(fsPrefix)) return urlOrPath.substring(fsPrefix.length);\n return urlOrPath;\n};\n\n/**\n * Logs the total server-side response time per request via the `finish` event,\n * which fires once the response has been fully written. This corresponds to\n * the `wait` time you see in browser network panels.\n *\n * Combined with the per-handler timings inside our own middleware, it lets us\n * see whether slowness is in our code or in downstream Vite work (transforms,\n * optimizeDeps, etc).\n */\nexport const wrapResponseTiming = (req: IncomingMessage, res: ServerResponse): void => {\n if (!isTimingEnabled()) return;\n const start = performance.now();\n res.on(\"finish\", () => {\n const durationMs = performance.now() - start;\n if (durationMs >= TRANSFORM_LOG_THRESHOLD_MS) {\n const url = req.url ?? \"(no url)\";\n logTiming(durationMs, `${req.method ?? \"GET\"} ${shortenPath(url)}`);\n }\n });\n};\n"]}
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createExtensionLoaderCache = void 0;
4
+ const devServerTiming_1 = require("./devServerTiming");
5
+ const DEFAULT_TTL_MS = 5 * 60 * 1000;
6
+ /**
7
+ * Creates an in-memory cache for the /extensionloader.js script.
8
+ *
9
+ * The /extensionloader.js handler proxies to https://iris.trackunit.app
10
+ * (or http://localhost:3000 when LOCAL=true). Without caching, every page
11
+ * load triggers a fresh network fetch -- and during a single cold load Vite
12
+ * typically reloads the page 2-3 times (because of optimizeDeps cascades),
13
+ * so the upstream is hit 3+ times. HAR profiling showed individual fetches
14
+ * costing 200ms-5s+ depending on CDN health, and one trace recorded a single
15
+ * ~5.2s fetch as the dominant gap in the dev waterfall.
16
+ *
17
+ * The script changes very rarely, so we cache it in memory with a TTL and
18
+ * dedupe concurrent fetches.
19
+ */
20
+ const createExtensionLoaderCache = ({ getUrl, ttlMs = DEFAULT_TTL_MS, shouldBypassCache, }) => {
21
+ let cached = null;
22
+ let inflight = null;
23
+ const getCachedExtensionLoaderBody = async () => {
24
+ const url = getUrl();
25
+ const now = Date.now();
26
+ const bypass = shouldBypassCache?.() === true;
27
+ if (!bypass && cached !== null && cached.url === url && now - cached.fetchedAt < ttlMs) {
28
+ return cached.body;
29
+ }
30
+ if (inflight !== null) {
31
+ const settled = await inflight;
32
+ return settled.body;
33
+ }
34
+ const fetchPromise = (async () => {
35
+ (0, devServerTiming_1.logEvent)(`extensionloader cache: cold load (${url})`);
36
+ const body = await (0, devServerTiming_1.timeAsync)(`fetch ${url}`, async () => {
37
+ const resp = await fetch(url);
38
+ if (!resp.ok) {
39
+ throw new Error(`upstream returned ${resp.status} ${resp.statusText}`);
40
+ }
41
+ return resp.text();
42
+ });
43
+ cached = { url, body, fetchedAt: Date.now() };
44
+ return { url, body };
45
+ })();
46
+ inflight = fetchPromise;
47
+ // Clear the inflight pointer once the fetch settles. Use then(cleanup, cleanup)
48
+ // instead of finally() so a rejection isn't re-thrown from this side-channel;
49
+ // the caller still observes the rejection via the awaited `fetchPromise`.
50
+ const clearInflightIfStillCurrent = () => {
51
+ if (inflight === fetchPromise) {
52
+ inflight = null;
53
+ }
54
+ };
55
+ fetchPromise.then(clearInflightIfStillCurrent, clearInflightIfStillCurrent);
56
+ const fetched = await fetchPromise;
57
+ return fetched.body;
58
+ };
59
+ return { getCachedExtensionLoaderBody };
60
+ };
61
+ exports.createExtensionLoaderCache = createExtensionLoaderCache;
62
+ //# sourceMappingURL=extensionLoaderCache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extensionLoaderCache.js","sourceRoot":"","sources":["../../../../../../../libs/iris-app-sdk/vite/src/executors/utils/extensionLoaderCache.ts"],"names":[],"mappings":";;;AAAA,uDAAwD;AAgBxD,MAAM,cAAc,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;AAErC;;;;;;;;;;;;;GAaG;AACI,MAAM,0BAA0B,GAAG,CAAC,EACzC,MAAM,EACN,KAAK,GAAG,cAAc,EACtB,iBAAiB,GACQ,EAAwB,EAAE;IACnD,IAAI,MAAM,GAA4D,IAAI,CAAC;IAC3E,IAAI,QAAQ,GAAkD,IAAI,CAAC;IAEnE,MAAM,4BAA4B,GAAG,KAAK,IAAqB,EAAE;QAC/D,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC;QACrB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAEvB,MAAM,MAAM,GAAG,iBAAiB,EAAE,EAAE,KAAK,IAAI,CAAC;QAC9C,IAAI,CAAC,MAAM,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,CAAC,GAAG,KAAK,GAAG,IAAI,GAAG,GAAG,MAAM,CAAC,SAAS,GAAG,KAAK,EAAE,CAAC;YACvF,OAAO,MAAM,CAAC,IAAI,CAAC;QACrB,CAAC;QACD,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YACtB,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC;YAC/B,OAAO,OAAO,CAAC,IAAI,CAAC;QACtB,CAAC;QAED,MAAM,YAAY,GAAG,CAAC,KAAK,IAA4C,EAAE;YACvE,IAAA,0BAAQ,EAAC,qCAAqC,GAAG,GAAG,CAAC,CAAC;YACtD,MAAM,IAAI,GAAG,MAAM,IAAA,2BAAS,EAAC,SAAS,GAAG,EAAE,EAAE,KAAK,IAAI,EAAE;gBACtD,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;gBAC9B,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;oBACb,MAAM,IAAI,KAAK,CAAC,qBAAqB,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;gBACzE,CAAC;gBACD,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC;YACrB,CAAC,CAAC,CAAC;YACH,MAAM,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;YAC9C,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;QACvB,CAAC,CAAC,EAAE,CAAC;QACL,QAAQ,GAAG,YAAY,CAAC;QAExB,gFAAgF;QAChF,8EAA8E;QAC9E,0EAA0E;QAC1E,MAAM,2BAA2B,GAAG,GAAS,EAAE;YAC7C,IAAI,QAAQ,KAAK,YAAY,EAAE,CAAC;gBAC9B,QAAQ,GAAG,IAAI,CAAC;YAClB,CAAC;QACH,CAAC,CAAC;QACF,YAAY,CAAC,IAAI,CAAC,2BAA2B,EAAE,2BAA2B,CAAC,CAAC;QAE5E,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC;QACnC,OAAO,OAAO,CAAC,IAAI,CAAC;IACtB,CAAC,CAAC;IAEF,OAAO,EAAE,4BAA4B,EAAE,CAAC;AAC1C,CAAC,CAAC;AAlDW,QAAA,0BAA0B,8BAkDrC","sourcesContent":["import { logEvent, timeAsync } from \"./devServerTiming\";\n\ninterface ExtensionLoaderCacheDeps {\n /** Returns the URL to fetch the extension loader from. Re-evaluated on every read so it can change at runtime. */\n getUrl: () => string;\n /** TTL in milliseconds for the cached body. Defaults to 5 minutes. */\n ttlMs?: number;\n /** When true, bypass the cache for this read; inflight dedup still applies. Defaults to never bypass. */\n shouldBypassCache?: () => boolean;\n}\n\nexport interface ExtensionLoaderCache {\n /** Fetches the extension loader script body. Caches with TTL and dedupes concurrent fetches. */\n getCachedExtensionLoaderBody: () => Promise<string>;\n}\n\nconst DEFAULT_TTL_MS = 5 * 60 * 1000;\n\n/**\n * Creates an in-memory cache for the /extensionloader.js script.\n *\n * The /extensionloader.js handler proxies to https://iris.trackunit.app\n * (or http://localhost:3000 when LOCAL=true). Without caching, every page\n * load triggers a fresh network fetch -- and during a single cold load Vite\n * typically reloads the page 2-3 times (because of optimizeDeps cascades),\n * so the upstream is hit 3+ times. HAR profiling showed individual fetches\n * costing 200ms-5s+ depending on CDN health, and one trace recorded a single\n * ~5.2s fetch as the dominant gap in the dev waterfall.\n *\n * The script changes very rarely, so we cache it in memory with a TTL and\n * dedupe concurrent fetches.\n */\nexport const createExtensionLoaderCache = ({\n getUrl,\n ttlMs = DEFAULT_TTL_MS,\n shouldBypassCache,\n}: ExtensionLoaderCacheDeps): ExtensionLoaderCache => {\n let cached: { url: string; body: string; fetchedAt: number } | null = null;\n let inflight: Promise<{ url: string; body: string }> | null = null;\n\n const getCachedExtensionLoaderBody = async (): Promise<string> => {\n const url = getUrl();\n const now = Date.now();\n\n const bypass = shouldBypassCache?.() === true;\n if (!bypass && cached !== null && cached.url === url && now - cached.fetchedAt < ttlMs) {\n return cached.body;\n }\n if (inflight !== null) {\n const settled = await inflight;\n return settled.body;\n }\n\n const fetchPromise = (async (): Promise<{ url: string; body: string }> => {\n logEvent(`extensionloader cache: cold load (${url})`);\n const body = await timeAsync(`fetch ${url}`, async () => {\n const resp = await fetch(url);\n if (!resp.ok) {\n throw new Error(`upstream returned ${resp.status} ${resp.statusText}`);\n }\n return resp.text();\n });\n cached = { url, body, fetchedAt: Date.now() };\n return { url, body };\n })();\n inflight = fetchPromise;\n\n // Clear the inflight pointer once the fetch settles. Use then(cleanup, cleanup)\n // instead of finally() so a rejection isn't re-thrown from this side-channel;\n // the caller still observes the rejection via the awaited `fetchPromise`.\n const clearInflightIfStillCurrent = (): void => {\n if (inflight === fetchPromise) {\n inflight = null;\n }\n };\n fetchPromise.then(clearInflightIfStillCurrent, clearInflightIfStillCurrent);\n\n const fetched = await fetchPromise;\n return fetched.body;\n };\n\n return { getCachedExtensionLoaderBody };\n};\n"]}
@@ -11,6 +11,9 @@ const path = tslib_1.__importStar(require("node:path"));
11
11
  const vite_2 = require("vite");
12
12
  const vite_plugin_checker_1 = tslib_1.__importDefault(require("vite-plugin-checker"));
13
13
  const vite_plugin_css_injected_by_js_1 = tslib_1.__importDefault(require("vite-plugin-css-injected-by-js"));
14
+ const devServerTiming_1 = require("./devServerTiming");
15
+ const extensionLoaderCache_1 = require("./extensionLoaderCache");
16
+ const manifestCache_1 = require("./manifestCache");
14
17
  // Only import enableTsConfigPath statically - other utilities are dynamically imported
15
18
  // AFTER tsconfig paths are registered to avoid oxc analysis warnings
16
19
  // MIME types for serving extension assets during development
@@ -57,7 +60,7 @@ const registerTsConfigPaths = (workspaceRoot, appDir) => {
57
60
  * Loads the Iris app manifest from the app directory.
58
61
  * Uses require() with cache clearing since Node.js doesn't support query strings in import paths.
59
62
  */
60
- const loadManifest = (workspaceRoot, appDir) => {
63
+ const loadManifest = (workspaceRoot, appDir) => (0, devServerTiming_1.time)("loadManifest (re-import iris-app-manifest.ts)", () => {
61
64
  // Register tsconfig paths before loading the manifest
62
65
  registerTsConfigPaths(workspaceRoot, appDir);
63
66
  const manifestPath = path.join(appDir, "iris-app-manifest.ts");
@@ -72,7 +75,7 @@ const loadManifest = (workspaceRoot, appDir) => {
72
75
  const manifestModule = require(manifestPath);
73
76
  manifestModule.default.moduleFormat = "esm";
74
77
  return manifestModule.default;
75
- };
78
+ });
76
79
  /**
77
80
  * Converts the shared dependencies from webpack format to Vite module federation format.
78
81
  * Handles both array and object variants of the Shared type.
@@ -138,7 +141,7 @@ const generateIndexHtml = ({ manifest, packageJson, options, }) => {
138
141
  </script>`
139
142
  : "";
140
143
  const devtools = options.config.mode === "development"
141
- ? `<script nonce="{{nonce}}" src="http://localhost:8097"></script>
144
+ ? `<script nonce="{{nonce}}" defer src="http://localhost:8097"></script>
142
145
  ${iframeRedirect}
143
146
  <script nonce="{{nonce}}">
144
147
  if (typeof global === 'undefined') { window.global = window; }
@@ -159,18 +162,20 @@ const generateIndexHtml = ({ manifest, packageJson, options, }) => {
159
162
  * Returns an array of plugins including Module Federation configuration.
160
163
  */
161
164
  async function getTrackunitIrisAppVitePlugins(options) {
165
+ const pluginConstructionStart = performance.now();
166
+ (0, devServerTiming_1.logEvent)("plugin construction: started");
162
167
  const resolvedAppDir = path.resolve(options.appDir);
163
168
  const workspaceRoot = options.workspaceRoot ?? path.resolve(options.appDir, "../..");
164
169
  // Load manifest upfront for federation config
165
170
  const initialManifest = loadManifest(workspaceRoot, resolvedAppDir);
166
171
  // Get build utilities (tsconfig paths now registered via loadManifest)
167
172
  // Get an available port in the Iris app port range
168
- const port = await (0, iris_app_build_utilities_1.getAvailablePort)(22220, 22229);
173
+ const port = await (0, devServerTiming_1.timeAsync)("getAvailablePort", () => (0, iris_app_build_utilities_1.getAvailablePort)(22220, 22229));
169
174
  // Get module federation configuration from manifest
170
- const webpackExposes = await (0, iris_app_build_utilities_1.getExposedExtensions)({
175
+ const webpackExposes = await (0, devServerTiming_1.timeAsync)(`getExposedExtensions (${initialManifest.extensions.length} extensions)`, () => (0, iris_app_build_utilities_1.getExposedExtensions)({
171
176
  nxRootDir: workspaceRoot,
172
177
  manifest: initialManifest,
173
- });
178
+ }));
174
179
  const shared = (0, iris_app_build_utilities_1.getSharedDependencies)({ manifest: initialManifest });
175
180
  const viteExposes = convertExposesToViteFormat(webpackExposes);
176
181
  const viteShared = convertSharedToViteFormat(shared);
@@ -246,7 +251,6 @@ async function getTrackunitIrisAppVitePlugins(options) {
246
251
  };
247
252
  // Create the iris app plugin
248
253
  let config;
249
- let manifest = initialManifest;
250
254
  const reloadManifest = () => {
251
255
  return loadManifest(workspaceRoot, resolvedAppDir);
252
256
  };
@@ -259,6 +263,23 @@ async function getTrackunitIrisAppVitePlugins(options) {
259
263
  manifestCopy.extensions = (0, iris_app_build_utilities_1.updateExtensions)([...manifestData.extensions]);
260
264
  return (0, iris_app_build_utilities_1.injectFullDescriptionImages)(manifestCopy, resolvedAppDir);
261
265
  };
266
+ const manifestCache = (0, manifestCache_1.createManifestCache)({
267
+ reloadManifest,
268
+ generateManifestJson,
269
+ initialManifest,
270
+ });
271
+ // The /extensionloader.js handler proxies to https://iris.trackunit.app
272
+ // (or http://localhost:3000 when LOCAL=true). The script changes very
273
+ // rarely, so we cache the body in memory with a TTL and dedupe concurrent
274
+ // fetches via createExtensionLoaderCache. Set IRIS_EXTENSIONLOADER_NO_CACHE=1
275
+ // to bypass the cache for debugging.
276
+ const getExtensionLoaderUrl = () => process.env.LOCAL === "true"
277
+ ? "http://localhost:3000/extensionloader.js"
278
+ : "https://iris.trackunit.app/extensionloader.js";
279
+ const extensionLoaderCache = (0, extensionLoaderCache_1.createExtensionLoaderCache)({
280
+ getUrl: getExtensionLoaderUrl,
281
+ shouldBypassCache: () => process.env.IRIS_EXTENSIONLOADER_NO_CACHE === "1",
282
+ });
262
283
  // No explicit type annotation to avoid excessive stack depth error with federation plugin's complex types
263
284
  const irisAppPlugin = {
264
285
  name: "trackunit-iris-app-vite-configuration",
@@ -285,7 +306,7 @@ async function getTrackunitIrisAppVitePlugins(options) {
285
306
  },
286
307
  },
287
308
  // PostCSS configuration for Tailwind CSS v4
288
- // Using @tailwindcss/postcss instead of @tailwindcss/vite for better monorepo support
309
+ // Using @tailwindcss/postcss instead of @tailwindcss/vite for better monorepo support.
289
310
  css: {
290
311
  postcss: {
291
312
  plugins: [require("@tailwindcss/postcss")({})],
@@ -358,15 +379,37 @@ async function getTrackunitIrisAppVitePlugins(options) {
358
379
  config = resolvedConfig;
359
380
  },
360
381
  buildStart() {
361
- manifest = reloadManifest();
382
+ manifestCache.reloadManifestSync();
362
383
  },
363
384
  configureServer(server) {
385
+ // Watch iris-app-manifest.ts and invalidate the manifest cache on change so
386
+ // HMR picks up edits (e.g. adding/removing extensions). All other paths
387
+ // serve from the cache populated by the first request.
388
+ const manifestFilePath = path.join(resolvedAppDir, "iris-app-manifest.ts");
389
+ server.watcher.add(manifestFilePath);
390
+ server.watcher.on("change", changedPath => {
391
+ if (path.normalize(changedPath) === path.normalize(manifestFilePath)) {
392
+ manifestCache.invalidateManifestCache();
393
+ (0, devServerTiming_1.logEvent)("manifest cache: invalidated (iris-app-manifest.ts changed)");
394
+ }
395
+ });
396
+ // Request timing middleware - MUST be registered before any other middleware
397
+ // so we observe the full server-side response time. Logs to stdout when
398
+ // the response takes longer than TRANSFORM_LOG_THRESHOLD_MS. Enable with
399
+ // IRIS_TIMING=true.
400
+ server.middlewares.use((req, res, next) => {
401
+ (0, devServerTiming_1.wrapResponseTiming)(req, res);
402
+ next();
403
+ });
364
404
  // Route /invoke requests to local serverside extensions when available.
365
405
  server.middlewares.use("/invoke", (0, iris_app_build_utilities_1.createInvokeProxyMiddleware)(options.serversidePortMap));
366
406
  // Serve extension assets from source during development (/{extensionId}/... → {sourceRoot}/...)
367
407
  server.middlewares.use((req, res, next) => {
368
408
  const url = req.url?.split("?")[0] ?? "/";
369
- const filePath = resolveExtensionAsset(url, manifest ?? reloadManifest(), workspaceRoot);
409
+ // Use the in-memory manifest if available; fall back to initialManifest
410
+ // (loaded once at plugin construction) so this synchronous middleware
411
+ // never blocks on a slow manifest reload.
412
+ const filePath = resolveExtensionAsset(url, manifestCache.getManifestSync() ?? initialManifest, workspaceRoot);
370
413
  if (filePath) {
371
414
  res.setHeader("Content-Type", getMimeType(filePath));
372
415
  res.end(fs.readFileSync(filePath));
@@ -415,8 +458,7 @@ async function getTrackunitIrisAppVitePlugins(options) {
415
458
  if (req.url === "/manifest.json") {
416
459
  void (async () => {
417
460
  try {
418
- manifest = reloadManifest();
419
- const manifestJson = generateManifestJson(manifest);
461
+ const manifestJson = await manifestCache.getCachedManifestJson();
420
462
  res.setHeader("Content-Type", "application/json");
421
463
  res.end(JSON.stringify(manifestJson, null, 2));
422
464
  }
@@ -444,14 +486,15 @@ async function getTrackunitIrisAppVitePlugins(options) {
444
486
  }
445
487
  }
446
488
  // Serve manifestAndToken during development (async route - handle response ourselves)
489
+ // Calls getCachedManifestJson directly instead of HTTP-fetching /manifest.json
490
+ // to avoid a redundant manifest reload on every request (the host calls this
491
+ // endpoint multiple times during initial app boot).
447
492
  if (req.url?.startsWith("/manifestAndToken")) {
448
493
  void (async () => {
449
494
  try {
450
- const host = req.headers.host || "localhost";
451
- const resp = await fetch(`http://${host}/manifest.json`);
452
- const body = await resp.json();
495
+ const manifestJson = await manifestCache.getCachedManifestJson();
453
496
  res.setHeader("Content-Type", "application/json");
454
- res.end(JSON.stringify({ manifest: body }));
497
+ res.end(JSON.stringify({ manifest: manifestJson }));
455
498
  }
456
499
  catch (e) {
457
500
  // eslint-disable-next-line no-console
@@ -462,17 +505,13 @@ async function getTrackunitIrisAppVitePlugins(options) {
462
505
  })();
463
506
  return; // Don't call next() - we're handling this route
464
507
  }
465
- // Serve extensionloader.js during development (async route - handle response ourselves)
508
+ // Serve extensionloader.js during development.
509
+ // To test extensionloader locally set LOCAL=true in front of the serve command.
510
+ // The fetched body is cached in memory; see createExtensionLoaderCache.
466
511
  if (req.url?.startsWith("/extensionloader.js")) {
467
512
  void (async () => {
468
513
  try {
469
- // To test extensionloader locally set LOCAL=true in front of the serve command
470
- let loaderUrl = "https://iris.trackunit.app/extensionloader.js";
471
- if (process.env.LOCAL === "true") {
472
- loaderUrl = "http://localhost:3000/extensionloader.js";
473
- }
474
- const resp = await fetch(loaderUrl);
475
- const body = await resp.text();
514
+ const body = await extensionLoaderCache.getCachedExtensionLoaderBody();
476
515
  res.setHeader("Content-Type", "application/javascript");
477
516
  res.end(body);
478
517
  }
@@ -494,11 +533,11 @@ async function getTrackunitIrisAppVitePlugins(options) {
494
533
  if (isRootOrSpaRoute) {
495
534
  void (async () => {
496
535
  try {
497
- manifest = reloadManifest();
536
+ const currentManifest = await manifestCache.getCachedManifest();
498
537
  const packageJsonPath = path.join(resolvedAppDir, "package.json");
499
538
  const packageJson = fs.readFileSync(packageJsonPath, "utf8");
500
- const html = generateIndexHtml({ manifest, packageJson, options });
501
- const transformedHtml = await server.transformIndexHtml(req.url ?? "/", html);
539
+ const html = generateIndexHtml({ manifest: currentManifest, packageJson, options });
540
+ const transformedHtml = await (0, devServerTiming_1.timeAsync)(`server.transformIndexHtml(${(0, devServerTiming_1.shortenPath)(url)})`, () => server.transformIndexHtml(req.url ?? "/", html));
502
541
  res.setHeader("Content-Type", "text/html");
503
542
  res.end(transformedHtml);
504
543
  }
@@ -516,15 +555,14 @@ async function getTrackunitIrisAppVitePlugins(options) {
516
555
  });
517
556
  // Log success message when server starts
518
557
  server.httpServer?.once("listening", () => {
558
+ (0, devServerTiming_1.logEvent)("server: listening");
519
559
  setTimeout(() => {
520
560
  (0, iris_app_build_utilities_1.logInfo)(`\n✨ Iris App is now started, check it out ✨\nhttps://new.manager.trackunit.com/goto/iris-app-dev\n`);
521
561
  }, 100);
522
562
  });
523
563
  },
524
564
  generateBundle() {
525
- if (!manifest) {
526
- manifest = reloadManifest();
527
- }
565
+ const manifest = manifestCache.getManifestSync() ?? manifestCache.reloadManifestSync();
528
566
  const packageJsonPath = path.join(resolvedAppDir, "package.json");
529
567
  const packageJson = fs.readFileSync(packageJsonPath, "utf8");
530
568
  // Generate and emit manifest.json
@@ -549,7 +587,7 @@ async function getTrackunitIrisAppVitePlugins(options) {
549
587
  },
550
588
  closeBundle() {
551
589
  if (config.command === "build") {
552
- const manifestForCopy = manifest ?? reloadManifest();
590
+ const manifestForCopy = manifestCache.getManifestSync() ?? manifestCache.reloadManifestSync();
553
591
  const copyPatterns = (0, iris_app_build_utilities_1.getCopyPatterns)({
554
592
  nxRootDir: workspaceRoot,
555
593
  appDir: resolvedAppDir,
@@ -604,7 +642,7 @@ async function getTrackunitIrisAppVitePlugins(options) {
604
642
  //
605
643
  // NOTE: Tailwind CSS is configured via PostCSS (in baseConfig.css.postcss above)
606
644
  // NOT using @tailwindcss/vite because it doesn't respect tailwind.config.js content paths
607
- // PostCSS approach matches Rspack and properly uses getTailwindContentForApp() from config files
645
+ // PostCSS approach matches Rspack and properly uses Tailwind v4 config files.
608
646
  const plugins = [
609
647
  (0, nx_tsconfig_paths_plugin_1.nxViteTsPaths)(),
610
648
  // React Fast Refresh — enables HMR for React components so file changes
@@ -626,6 +664,7 @@ async function getTrackunitIrisAppVitePlugins(options) {
626
664
  }),
627
665
  bundleAnalyzerPlugin,
628
666
  ];
667
+ (0, devServerTiming_1.logTiming)(performance.now() - pluginConstructionStart, "plugin construction: complete");
629
668
  return plugins.filter(Boolean);
630
669
  }
631
670
  //# sourceMappingURL=getTrackunitIrisAppVitePlugins.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getTrackunitIrisAppVitePlugins.js","sourceRoot":"","sources":["../../../../../../../libs/iris-app-sdk/vite/src/executors/utils/getTrackunitIrisAppVitePlugins.ts"],"names":[],"mappings":"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","sourcesContent":["import { federation } from \"@module-federation/vite\";\nimport { nxViteTsPaths } from \"@nx/vite/plugins/nx-tsconfig-paths.plugin\";\nimport type { Exposes, IrisAppManifest, Shared } from \"@trackunit/iris-app-api\";\nimport {\n CopyPattern,\n createInvokeProxyMiddleware,\n enableTsConfigPath,\n getAvailablePort,\n getCopyPatterns,\n getExposedExtensions,\n getSharedDependencies,\n injectFullDescriptionImages,\n logInfo,\n ServersidePortMap,\n updateCustomFields,\n updateExtensions,\n updateIndexHtml,\n} from \"@trackunit/iris-app-build-utilities\";\nimport react from \"@vitejs/plugin-react-swc\";\nimport * as fs from \"node:fs\";\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport * as path from \"node:path\";\nimport { mergeConfig, type PluginOption, type ResolvedConfig, type UserConfig, type ViteDevServer } from \"vite\";\nimport checker from \"vite-plugin-checker\";\nimport cssInjectedByJsPlugin from \"vite-plugin-css-injected-by-js\";\nimport { logEvent, logTiming, shortenPath, time, timeAsync, wrapResponseTiming } from \"./devServerTiming\";\nimport { createExtensionLoaderCache } from \"./extensionLoaderCache\";\nimport { createManifestCache } from \"./manifestCache\";\n\n// Only import enableTsConfigPath statically - other utilities are dynamically imported\n// AFTER tsconfig paths are registered to avoid oxc analysis warnings\n\n// MIME types for serving extension assets during development\nconst MIME_TYPES: Record<string, string> = {\n \".svg\": \"image/svg+xml\",\n \".png\": \"image/png\",\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".gif\": \"image/gif\",\n \".webp\": \"image/webp\",\n \".ico\": \"image/x-icon\",\n};\n\nconst getMimeType = (filePath: string): string =>\n MIME_TYPES[path.extname(filePath).toLowerCase()] || \"application/octet-stream\";\n\n/**\n * Resolves a URL path like \"/{extensionId}/assets/icon.svg\" to the actual file path on disk.\n * Returns null if no matching extension is found or the file doesn't exist.\n */\nconst resolveExtensionAsset = (url: string, manifest: IrisAppManifest, workspaceRoot: string): string | null => {\n for (const ext of manifest.extensions) {\n if (url.startsWith(`/${ext.id}/`)) {\n const assetPath = url.substring(ext.id.length + 2);\n const filePath = path.join(workspaceRoot, ext.sourceRoot, assetPath);\n\n if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {\n return filePath;\n }\n }\n }\n return null;\n};\n\ninterface TrackunitIrisAppVitePluginOptions {\n appDir: string;\n /** Path to the workspace root (where tsconfig.base.json is located) */\n workspaceRoot?: string;\n serversidePortMap?: ServersidePortMap;\n config: UserConfig;\n skipTypeChecking?: boolean;\n}\n\ninterface ViteSharedConfig {\n [key: string]: {\n singleton?: boolean;\n eager?: boolean;\n requiredVersion?: string;\n /** When false, the shared dependency is NOT bundled - it's loaded from the host at runtime */\n import?: false;\n };\n}\n\ninterface ViteExposesConfig {\n [key: string]: string;\n}\n\n// Flag to track if tsconfig paths have been registered\nlet tsconfigPathsRegistered = false;\n\n/**\n * Registers tsconfig paths for resolving @trackunit/* imports at runtime.\n * This is needed because the manifest file imports from @trackunit/* packages.\n */\nconst registerTsConfigPaths = (workspaceRoot: string, appDir: string): void => {\n if (tsconfigPathsRegistered) {\n return;\n }\n\n enableTsConfigPath({ projectRootDir: appDir, workspaceRoot });\n tsconfigPathsRegistered = true;\n};\n\n/**\n * Loads the Iris app manifest from the app directory.\n * Uses require() with cache clearing since Node.js doesn't support query strings in import paths.\n */\nconst loadManifest = (workspaceRoot: string, appDir: string): IrisAppManifest =>\n time(\"loadManifest (re-import iris-app-manifest.ts)\", () => {\n // Register tsconfig paths before loading the manifest\n registerTsConfigPaths(workspaceRoot, appDir);\n\n const manifestPath = path.join(appDir, \"iris-app-manifest.ts\");\n // Clear require cache to ensure fresh manifest on HMR\n try {\n delete require.cache[require.resolve(manifestPath)];\n } catch {\n // Ignore if not in cache\n }\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const manifestModule = require(manifestPath);\n manifestModule.default.moduleFormat = \"esm\";\n return manifestModule.default;\n });\n\n/**\n * Converts the shared dependencies from webpack format to Vite module federation format.\n * Handles both array and object variants of the Shared type.\n *\n * NOTE: We do NOT set `import: false` here because iris apps run in an iframe (separate\n * JavaScript context). The host's shared dependencies are NOT accessible across the iframe\n * boundary, so the iris app must bundle its own copies as a fallback. The `singleton: true`\n * config ensures that within the iframe context, only one instance is used.\n */\nconst convertSharedToViteFormat = (shared: Shared): ViteSharedConfig => {\n const result: ViteSharedConfig = {};\n\n const processItem = (item: unknown): void => {\n if (typeof item === \"string\") {\n // Bundle this dependency - iris apps need their own copy since they run in an iframe\n result[item] = {};\n } else if (item !== null && typeof item === \"object\") {\n for (const [key, value] of Object.entries(item)) {\n const baseConfig = typeof value === \"string\" ? {} : (value ?? {});\n // Remove import: false if it was set - we need to bundle shared deps for iframe isolation\n const { import: _import, ...restConfig } = baseConfig;\n result[key] = restConfig;\n }\n }\n };\n\n // Handle both array and object variants of Shared type\n if (Array.isArray(shared)) {\n for (const item of shared) {\n processItem(item);\n }\n } else {\n processItem(shared);\n }\n // For now we do not use any shared for vite build - as it create random crashes, it seemed like react was not garanteed to be loaded\n return {};\n // return result;\n};\n\n/**\n * Converts the webpack Exposes type to Vite's expected format.\n * getExposedExtensions returns an object with string values, but the Exposes type\n * is a union that includes arrays. This extracts just the object entries.\n *\n */\nconst convertExposesToViteFormat = (exposes: Exposes): ViteExposesConfig => {\n const result: ViteExposesConfig = {};\n\n if (typeof exposes === \"object\" && !Array.isArray(exposes)) {\n for (const [key, value] of Object.entries(exposes)) {\n if (typeof value === \"string\") {\n result[key] = value;\n }\n }\n }\n\n return result;\n};\n\nconst generateIndexHtml = ({\n manifest,\n packageJson,\n options,\n}: {\n manifest: IrisAppManifest;\n packageJson: string;\n options: TrackunitIrisAppVitePluginOptions;\n}): string => {\n // Register tsconfig paths to load the utility\n registerTsConfigPaths(options.workspaceRoot ?? options.appDir, options.appDir);\n\n // Only include dev scripts and React DevTools connection in development\n const iframeRedirect =\n options.config.mode === \"development\"\n ? `<script nonce=\"{{nonce}}\">\n if (window.self === window.top) { window.location.replace(\"https://new.manager.trackunit.com/goto/iris-app-dev\"); }\n </script>`\n : \"\";\n\n const devtools =\n options.config.mode === \"development\"\n ? `<script nonce=\"{{nonce}}\" defer src=\"http://localhost:8097\"></script>\n ${iframeRedirect}\n <script nonce=\"{{nonce}}\">\n if (typeof global === 'undefined') { window.global = window; }\n if (typeof process === 'undefined') { window.process = { env: {} }; }\n </script>\n`\n : `\n<script nonce=\"{{nonce}}\">\n if (typeof global === 'undefined') { window.global = window; }\n if (typeof process === 'undefined') { window.process = { env: {} }; }\n </script>`;\n\n return updateIndexHtml({ manifest, packageJson, devtools });\n};\n\n/**\n * Vite plugin that generates manifest.json, package.json, and index.html for Iris Apps.\n * This is the Vite equivalent of TrackunitIrisAppWebpackPlugin.\n *\n * Returns an array of plugins including Module Federation configuration.\n */\nexport async function getTrackunitIrisAppVitePlugins(\n options: TrackunitIrisAppVitePluginOptions\n): Promise<Array<PluginOption>> {\n const pluginConstructionStart = performance.now();\n logEvent(\"plugin construction: started\");\n\n const resolvedAppDir = path.resolve(options.appDir);\n const workspaceRoot = options.workspaceRoot ?? path.resolve(options.appDir, \"../..\");\n\n // Load manifest upfront for federation config\n const initialManifest = loadManifest(workspaceRoot, resolvedAppDir);\n\n // Get build utilities (tsconfig paths now registered via loadManifest)\n\n // Get an available port in the Iris app port range\n const port = await timeAsync(\"getAvailablePort\", () => getAvailablePort(22220, 22229));\n\n // Get module federation configuration from manifest\n const webpackExposes = await timeAsync(`getExposedExtensions (${initialManifest.extensions.length} extensions)`, () =>\n getExposedExtensions({\n nxRootDir: workspaceRoot,\n manifest: initialManifest,\n })\n );\n const shared = getSharedDependencies({ manifest: initialManifest });\n const viteExposes = convertExposesToViteFormat(webpackExposes);\n const viteShared = convertSharedToViteFormat(shared);\n const hasExposes = Object.keys(viteExposes).length > 0;\n\n // Only create the federation plugin when there are modules to expose.\n // @module-federation/vite deadlocks during build when exposes is empty because\n // its proxyRemoteEntry plugin waits for a module-parse promise that never resolves.\n let federationPlugin: PluginOption | null = null;\n if (hasExposes) {\n federationPlugin = federation({\n name: initialManifest.moduleFederationName,\n filename: \"remoteEntry.js\",\n library: { type: \"global\", name: initialManifest.moduleFederationName },\n exposes: viteExposes,\n shared: viteShared,\n remotes: {},\n manifest: false,\n dts: false,\n shareStrategy: \"loaded-first\",\n getPublicPath: `return (function() {\n var base;\n if (typeof import.meta !== 'undefined' && import.meta.url) {\n base = new URL('./', import.meta.url).href;\n } else if (typeof document !== 'undefined' && document.currentScript) {\n base = new URL('./', document.currentScript.src).href;\n } else {\n base = document.baseURI;\n }\n return base;\n })()`,\n });\n }\n\n // Conditionally create bundle analyzer plugin (same pattern as webpack/rspack)\n // Created here so we have access to the output directory path\n let bundleAnalyzerPlugin: PluginOption | null = null;\n if (process.env.BUNDLE_ANALYSE === \"true\") {\n // Compute output directory: dist/apps/{app-name}\n const appName = path.basename(resolvedAppDir);\n const statsPath = path.join(workspaceRoot, \"dist\", \"apps\", appName, \"stats.html\");\n\n // Dynamic ESM import for optional dependency\n const { visualizer } = await import(\"rollup-plugin-visualizer\");\n bundleAnalyzerPlugin = visualizer({\n filename: statsPath,\n template: \"treemap\",\n gzipSize: true,\n brotliSize: true,\n });\n\n // eslint-disable-next-line no-console\n console.log(`\\nšŸ“Š Bundle analyzer enabled - stats.html will be created at:\\n ${statsPath}\\n`);\n }\n\n // Plugin to disable hostAutoInit for pure remotes\n // The @module-federation/vite plugin always injects a hostAutoInit module\n // that auto-initializes the federation runtime. For pure remotes (Iris Apps),\n // we don't need this - the host handles initialization. This plugin intercepts\n // the virtual module and returns an empty module to prevent the build error.\n const disableHostAutoInitPlugin: PluginOption = {\n name: \"disable-host-auto-init\",\n enforce: \"pre\",\n resolveId(id) {\n // Match the hostAutoInit virtual module pattern: __H_A_I__hostAutoInit__H_A_I__\n if (id.includes(\"__H_A_I__hostAutoInit__H_A_I__\")) {\n return \"\\0virtual:empty-host-auto-init\";\n }\n return null;\n },\n load(id) {\n if (id === \"\\0virtual:empty-host-auto-init\") {\n // Return empty module - no auto-initialization needed for pure remotes\n return \"export default {}\";\n }\n return null;\n },\n };\n\n // Create the iris app plugin\n let config: ResolvedConfig;\n\n const reloadManifest = (): IrisAppManifest => {\n return loadManifest(workspaceRoot, resolvedAppDir);\n };\n\n const generateManifestJson = (manifestData: IrisAppManifest): IrisAppManifest => {\n // Register tsconfig paths to load utilities\n registerTsConfigPaths(workspaceRoot, resolvedAppDir);\n\n const manifestCopy = { ...manifestData };\n const appSrc = path.join(resolvedAppDir, \"src\");\n\n manifestCopy.customFieldDefinitions = updateCustomFields(manifestData.customFieldDefinitions, appSrc);\n manifestCopy.extensions = updateExtensions([...manifestData.extensions]);\n return injectFullDescriptionImages(manifestCopy, resolvedAppDir);\n };\n\n const manifestCache = createManifestCache({\n reloadManifest,\n generateManifestJson,\n initialManifest,\n });\n\n // The /extensionloader.js handler proxies to https://iris.trackunit.app\n // (or http://localhost:3000 when LOCAL=true). The script changes very\n // rarely, so we cache the body in memory with a TTL and dedupe concurrent\n // fetches via createExtensionLoaderCache. Set IRIS_EXTENSIONLOADER_NO_CACHE=1\n // to bypass the cache for debugging.\n const getExtensionLoaderUrl = (): string =>\n process.env.LOCAL === \"true\"\n ? \"http://localhost:3000/extensionloader.js\"\n : \"https://iris.trackunit.app/extensionloader.js\";\n\n const extensionLoaderCache = createExtensionLoaderCache({\n getUrl: getExtensionLoaderUrl,\n shouldBypassCache: () => process.env.IRIS_EXTENSIONLOADER_NO_CACHE === \"1\",\n });\n\n // No explicit type annotation to avoid excessive stack depth error with federation plugin's complex types\n const irisAppPlugin = {\n name: \"trackunit-iris-app-vite-configuration\",\n\n config(conf: UserConfig) {\n // Base config for both serve and build\n const baseConfig: UserConfig = {\n // Use empty base for runtime URL resolution - assets will use relative paths\n base: \"\",\n // Runtime URL resolution - equivalent to rspack's publicPath: \"auto\"\n // Uses document.currentScript to determine base URL at runtime\n experimental: {\n renderBuiltUrl(filename, { hostType }) {\n // CSS files cannot execute JavaScript runtime expressions - use relative paths instead\n // This applies to fonts, images, and other assets referenced via url() in CSS\n if (hostType === \"css\") {\n return { relative: true };\n }\n // For JS/HTML, use runtime resolution using document.currentScript (like webpack/rspack's publicPath: \"auto\")\n return {\n runtime: `((function() {\n return document.baseURI + ${JSON.stringify(filename)};\n })())`,\n };\n },\n },\n // PostCSS configuration for Tailwind CSS v4\n // Using @tailwindcss/postcss instead of @tailwindcss/vite for better monorepo support.\n css: {\n postcss: {\n plugins: [require(\"@tailwindcss/postcss\")({})],\n },\n },\n server: {\n port,\n strictPort: true,\n host: \"localhost\",\n // Enable CORS for cross-origin requests from new.manager.trackunit.com\n cors: {\n origin: true, // Reflect request origin (supports all origins)\n credentials: true,\n methods: [\"GET\", \"POST\", \"PUT\", \"DELETE\", \"OPTIONS\"],\n allowedHeaders: [\n \"X-Requested-With\",\n \"baggage\",\n \"content-type\",\n \"Authorization\",\n \"sentry-trace\",\n \"session-id\",\n \"commit-number\",\n \"x-trackunitappversion\",\n ],\n maxAge: 3600,\n },\n },\n // Production build optimization\n build: {\n // Use esbuild for fast minification (Vite's default)\n minify: \"esbuild\",\n // CSS code splitting is required for relativeCSSInjection to work\n // (cssInjectedByJsPlugin enforces cssCodeSplit: true when relativeCSSInjection is enabled)\n // Each chunk's CSS is injected when that chunk loads - ideal for Module Federation\n // NOTE: We don't set cssCodeSplit here because the plugin controls it\n cssCodeSplit: true,\n rollupOptions: {\n output: {\n // Chunk splitting strategy - bundle small modules together to reduce HTTP requests\n manualChunks(id: string) {\n // Skip Module Federation virtual modules and runtime - let MF handle these\n if (\n id.includes(\"__mf__virtual\") ||\n id.includes(\"@mf-types\") ||\n id.includes(\"virtualExposes\") ||\n id.includes(\"virtual_mf-exposes\") ||\n id.includes(\"remoteEntry\") ||\n id.includes(\"_mf_\")\n ) {\n return undefined;\n }\n\n // Skip node_modules - let Rollup handle vendor chunking\n if (id.includes(\"node_modules\")) {\n return undefined;\n }\n\n // 1. Split translations per language (like Rspack's splitChunks cacheGroups)\n const translationMatch = id.match(/[\\\\/]locales[\\\\/]([^/\\\\]+)[\\\\/]translation\\.json$/);\n if (translationMatch) {\n return `translations-${translationMatch[1]}`;\n }\n\n // Let Rollup handle everything else - default chunking is fine\n // Vite/Rollup will merge small chunks automatically\n return undefined;\n },\n },\n },\n },\n };\n\n return mergeConfig(conf, baseConfig, false); // false = don't concatenate root-level arrays\n },\n\n configResolved(resolvedConfig: ResolvedConfig) {\n config = resolvedConfig;\n },\n buildStart() {\n manifestCache.reloadManifestSync();\n },\n\n configureServer(server: ViteDevServer) {\n // Watch iris-app-manifest.ts and invalidate the manifest cache on change so\n // HMR picks up edits (e.g. adding/removing extensions). All other paths\n // serve from the cache populated by the first request.\n const manifestFilePath = path.join(resolvedAppDir, \"iris-app-manifest.ts\");\n server.watcher.add(manifestFilePath);\n server.watcher.on(\"change\", changedPath => {\n if (path.normalize(changedPath) === path.normalize(manifestFilePath)) {\n manifestCache.invalidateManifestCache();\n logEvent(\"manifest cache: invalidated (iris-app-manifest.ts changed)\");\n }\n });\n\n // Request timing middleware - MUST be registered before any other middleware\n // so we observe the full server-side response time. Logs to stdout when\n // the response takes longer than TRANSFORM_LOG_THRESHOLD_MS. Enable with\n // IRIS_TIMING=true.\n server.middlewares.use((req: IncomingMessage, res: ServerResponse, next: () => void) => {\n wrapResponseTiming(req, res);\n next();\n });\n\n // Route /invoke requests to local serverside extensions when available.\n server.middlewares.use(\"/invoke\", createInvokeProxyMiddleware(options.serversidePortMap));\n\n // Serve extension assets from source during development (/{extensionId}/... → {sourceRoot}/...)\n server.middlewares.use((req: IncomingMessage, res: ServerResponse, next: () => void) => {\n const url = req.url?.split(\"?\")[0] ?? \"/\";\n // Use the in-memory manifest if available; fall back to initialManifest\n // (loaded once at plugin construction) so this synchronous middleware\n // never blocks on a slow manifest reload.\n const filePath = resolveExtensionAsset(url, manifestCache.getManifestSync() ?? initialManifest, workspaceRoot);\n\n if (filePath) {\n res.setHeader(\"Content-Type\", getMimeType(filePath));\n res.end(fs.readFileSync(filePath));\n return;\n }\n next();\n });\n\n // Global CORS headers middleware - must be first to set headers on all responses\n server.middlewares.use((req: IncomingMessage, res: ServerResponse, next: () => void) => {\n // Set Access-Control-Allow-Origin based on request origin (needed for CORS preflight)\n const origin = req.headers.origin || \"https://dev.manager.trackunit.com\";\n res.setHeader(\"Access-Control-Allow-Origin\", origin);\n res.setHeader(\"Access-Control-Allow-Credentials\", \"true\");\n res.setHeader(\"Access-Control-Max-Age\", \"3600\");\n res.setHeader(\"Access-Control-Allow-Methods\", \"GET, POST, PUT, DELETE, OPTIONS\");\n res.setHeader(\n \"Access-Control-Allow-Headers\",\n \"X-Requested-With, baggage, content-type, Authorization, sentry-trace, session-id, commit-number, x-trackunitappversion\"\n );\n next();\n });\n\n // IMPORTANT: This middleware must be synchronous for requests we want to pass through to Vite.\n // Async middlewares break Connect's middleware chain when calling next().\n // We use a sync wrapper and only do async work for routes we handle ourselves.\n server.middlewares.use((req: IncomingMessage, res: ServerResponse, next: () => void) => {\n const url = req.url?.split(\"?\")[0] ?? \"/\";\n\n // Let Vite handle its internal routes (@fs, @vite, @react-refresh, node_modules, etc.)\n // MUST call next() synchronously for these to work properly\n const isViteInternal =\n url.startsWith(\"/@\") ||\n url.startsWith(\"/node_modules/\") ||\n url.startsWith(\"/__mf__virtual/\") ||\n url.startsWith(\"/__vite\");\n if (isViteInternal) {\n next();\n return;\n }\n\n // CORS preflight handler - respond to OPTIONS requests\n if (req.method === \"OPTIONS\") {\n res.statusCode = 204;\n res.end();\n return;\n }\n\n // For non-GET/HEAD/OPTIONS on root path, return allowed methods\n if (req.url?.endsWith(\"/\") && req.method !== \"GET\" && req.method !== \"HEAD\") {\n res.end(\"GET, HEAD\");\n return;\n }\n\n // Serve manifest.json during development (async route - handle response ourselves)\n if (req.url === \"/manifest.json\") {\n void (async () => {\n try {\n const manifestJson = await manifestCache.getCachedManifestJson();\n\n res.setHeader(\"Content-Type\", \"application/json\");\n res.end(JSON.stringify(manifestJson, null, 2));\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error(\"Error generating manifest.json:\", error);\n res.statusCode = 500;\n res.end(String(error));\n }\n })();\n return; // Don't call next() - we're handling this route\n }\n\n // Serve package.json during development\n if (req.url === \"/package.json\") {\n try {\n const packageJsonPath = path.join(resolvedAppDir, \"package.json\");\n const packageJson = fs.readFileSync(packageJsonPath, \"utf8\");\n\n res.setHeader(\"Content-Type\", \"application/json\");\n res.end(packageJson);\n return;\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error(\"Error serving package.json:\", error);\n }\n }\n\n // Serve manifestAndToken during development (async route - handle response ourselves)\n // Calls getCachedManifestJson directly instead of HTTP-fetching /manifest.json\n // to avoid a redundant manifest reload on every request (the host calls this\n // endpoint multiple times during initial app boot).\n if (req.url?.startsWith(\"/manifestAndToken\")) {\n void (async () => {\n try {\n const manifestJson = await manifestCache.getCachedManifestJson();\n res.setHeader(\"Content-Type\", \"application/json\");\n res.end(JSON.stringify({ manifest: manifestJson }));\n } catch (e) {\n // eslint-disable-next-line no-console\n console.error(\"ERROR: \", e);\n res.statusCode = 500;\n res.end(String(e));\n }\n })();\n return; // Don't call next() - we're handling this route\n }\n\n // Serve extensionloader.js during development.\n // To test extensionloader locally set LOCAL=true in front of the serve command.\n // The fetched body is cached in memory; see createExtensionLoaderCache.\n if (req.url?.startsWith(\"/extensionloader.js\")) {\n void (async () => {\n try {\n const body = await extensionLoaderCache.getCachedExtensionLoaderBody();\n res.setHeader(\"Content-Type\", \"application/javascript\");\n res.end(body);\n } catch (e) {\n // eslint-disable-next-line no-console\n console.error(\"ERROR: \", e);\n res.statusCode = 500;\n res.end(String(e));\n }\n })();\n return; // Don't call next() - we're handling this route\n }\n\n // Serve generated index.html for root path and SPA fallback routes\n // This handles the case where no physical index.html exists (async route - handle response ourselves)\n if (req.method === \"GET\") {\n const acceptsHtml = req.headers.accept?.includes(\"text/html\");\n const hasExtension = /\\.\\w+$/.test(url);\n const isRootOrSpaRoute = url === \"/\" || url === \"/index.html\" || (!hasExtension && acceptsHtml);\n\n if (isRootOrSpaRoute) {\n void (async () => {\n try {\n const currentManifest = await manifestCache.getCachedManifest();\n const packageJsonPath = path.join(resolvedAppDir, \"package.json\");\n const packageJson = fs.readFileSync(packageJsonPath, \"utf8\");\n const html = generateIndexHtml({ manifest: currentManifest, packageJson, options });\n\n const transformedHtml = await timeAsync(`server.transformIndexHtml(${shortenPath(url)})`, () =>\n server.transformIndexHtml(req.url ?? \"/\", html)\n );\n\n res.setHeader(\"Content-Type\", \"text/html\");\n res.end(transformedHtml);\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error(\"Error generating index.html:\", error);\n res.statusCode = 500;\n res.end(String(error));\n }\n })();\n return; // Don't call next() - we're handling this route\n }\n }\n next();\n });\n\n // Log success message when server starts\n server.httpServer?.once(\"listening\", () => {\n logEvent(\"server: listening\");\n setTimeout(() => {\n logInfo(\n `\\n✨ Iris App is now started, check it out ✨\\nhttps://new.manager.trackunit.com/goto/iris-app-dev\\n`\n );\n }, 100);\n });\n },\n\n generateBundle() {\n const manifest = manifestCache.getManifestSync() ?? manifestCache.reloadManifestSync();\n\n const packageJsonPath = path.join(resolvedAppDir, \"package.json\");\n const packageJson = fs.readFileSync(packageJsonPath, \"utf8\");\n\n // Generate and emit manifest.json\n const manifestJson = generateManifestJson(manifest);\n this.emitFile({\n type: \"asset\",\n fileName: \"manifest.json\",\n source: JSON.stringify(manifestJson, null, 2),\n });\n\n // Emit package.json\n this.emitFile({\n type: \"asset\",\n fileName: \"package.json\",\n source: packageJson,\n });\n\n const html = generateIndexHtml({ manifest, packageJson, options });\n this.emitFile({\n type: \"asset\",\n fileName: \"index.html\",\n source: html,\n });\n },\n\n closeBundle() {\n if (config.command === \"build\") {\n const manifestForCopy = manifestCache.getManifestSync() ?? manifestCache.reloadManifestSync();\n const copyPatterns = getCopyPatterns({\n nxRootDir: workspaceRoot,\n appDir: resolvedAppDir,\n manifest: manifestForCopy,\n mode: \"production\",\n });\n\n const nonExistingPatterns: Array<CopyPattern> = [];\n for (const pattern of copyPatterns) {\n const exists = fs.existsSync(pattern.from);\n if (!exists) {\n nonExistingPatterns.push(pattern);\n }\n }\n if (nonExistingPatterns.length > 0) {\n throw new Error(`Copy patterns do not exist: ${nonExistingPatterns.map(p => p.from).join(\", \")}`);\n }\n\n for (const pattern of copyPatterns) {\n try {\n if (fs.existsSync(pattern.from)) {\n const stat = fs.statSync(pattern.from);\n if (stat.isDirectory()) {\n // Copy directory recursively\n fs.cpSync(pattern.from, pattern.to, { recursive: true });\n } else {\n // Copy single file\n const targetDir = path.dirname(pattern.to);\n if (!fs.existsSync(targetDir)) {\n fs.mkdirSync(targetDir, { recursive: true });\n }\n fs.copyFileSync(pattern.from, pattern.to);\n }\n }\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error(`Error: Could not copy ${pattern.from} to ${pattern.to}:`, error);\n throw error;\n }\n }\n }\n },\n };\n\n // Return array of plugins:\n // 1. nxViteTsPaths - handles tsconfig path resolution\n // 2. disableHostAutoInitPlugin - intercepts hostAutoInit virtual module (must be before federation)\n // 3. federationPlugin - Module Federation for micro-frontends\n // 4. irisAppPlugin - Iris App specific handling\n // 5. cssInjectedByJsPlugin - Vite equivalent of rspack's \"style-loader\"\n // Injects CSS via JS as inline <style> tags instead of external CSS files\n // 6. bundleAnalyzerPlugin - optional bundle analysis\n //\n // NOTE: Tailwind CSS is configured via PostCSS (in baseConfig.css.postcss above)\n // NOT using @tailwindcss/vite because it doesn't respect tailwind.config.js content paths\n // PostCSS approach matches Rspack and properly uses Tailwind v4 config files.\n const plugins: Array<PluginOption> = [\n nxViteTsPaths(),\n // React Fast Refresh — enables HMR for React components so file changes\n // are hot-swapped without a full page reload. Safe with Module Federation\n // because Iris apps bundle their own React (iframe isolation).\n react({}),\n hasExposes && disableHostAutoInitPlugin,\n federationPlugin,\n !options.skipTypeChecking &&\n checker({ typescript: { tsconfigPath: path.join(resolvedAppDir, \"tsconfig.app.json\") } }),\n irisAppPlugin,\n // Equivalent to rspack's style-loader - injects CSS as inline <style> tags\n // topExecutionPriority ensures CSS is injected before other code runs,\n // relativeCSSInjection: true ensures CSS is injected per chunk (requires cssCodeSplit: true)\n // This is ideal for Module Federation - CSS loads with each remote chunk\n cssInjectedByJsPlugin({\n topExecutionPriority: true,\n relativeCSSInjection: true,\n }),\n bundleAnalyzerPlugin,\n ];\n logTiming(performance.now() - pluginConstructionStart, \"plugin construction: complete\");\n return plugins.filter(Boolean);\n}\n"]}
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createManifestCache = void 0;
4
+ const devServerTiming_1 = require("./devServerTiming");
5
+ /**
6
+ * Creates an in-memory cache for the Iris-app manifest.
7
+ *
8
+ * The cache deduplicates concurrent /manifest.json and /manifestAndToken
9
+ * requests so they don't re-resolve the manifest + all 25 extension entries
10
+ * on every call. Cold-load profiling showed each request taking ~3s; with the
11
+ * host firing several parallel calls during boot, that stacked into >6s of
12
+ * latency before any extension code could load.
13
+ *
14
+ * The cache is invalidated when iris-app-manifest.ts changes; callers wire
15
+ * that up through the dev server's file watcher.
16
+ */
17
+ const createManifestCache = ({ reloadManifest, generateManifestJson, initialManifest, }) => {
18
+ let manifest = initialManifest;
19
+ let manifestJsonCache = null;
20
+ // Shared promise for an in-flight reload, used to dedupe concurrent requests
21
+ // that arrive while the cache is empty.
22
+ let inflightManifestReload = null;
23
+ const ensureManifestLoaded = () => {
24
+ if (manifest !== undefined && manifestJsonCache !== null) {
25
+ return Promise.resolve(manifestJsonCache);
26
+ }
27
+ if (inflightManifestReload !== null) {
28
+ (0, devServerTiming_1.logEvent)("manifest cache: joining in-flight reload (deduped)");
29
+ return inflightManifestReload;
30
+ }
31
+ (0, devServerTiming_1.logEvent)("manifest cache: cold load (cache empty)");
32
+ const reloadPromise = (async () => {
33
+ const loadedManifest = manifest ?? reloadManifest();
34
+ manifest = loadedManifest;
35
+ manifestJsonCache = (0, devServerTiming_1.time)("generateManifestJson", () => generateManifestJson(loadedManifest));
36
+ return manifestJsonCache;
37
+ })();
38
+ inflightManifestReload = reloadPromise;
39
+ // Clear the inflight pointer once the reload settles so post-invalidate
40
+ // reads start a fresh load instead of joining this resolved/rejected
41
+ // promise. The identity check guards against `invalidateManifestCache`
42
+ // having already nulled it out (or replaced it with a newer reload). We
43
+ // attach the cleanup with then(cleanup, cleanup) instead of finally() so
44
+ // a rejection isn't re-thrown from this side-channel; the caller still
45
+ // observes the rejection via the returned `reloadPromise`.
46
+ const clearInflightIfStillCurrent = () => {
47
+ if (inflightManifestReload === reloadPromise) {
48
+ inflightManifestReload = null;
49
+ }
50
+ };
51
+ reloadPromise.then(clearInflightIfStillCurrent, clearInflightIfStillCurrent);
52
+ return reloadPromise;
53
+ };
54
+ const getCachedManifestJson = () => ensureManifestLoaded();
55
+ const getCachedManifest = async () => {
56
+ await ensureManifestLoaded();
57
+ if (manifest === undefined) {
58
+ throw new Error("Manifest unexpectedly undefined after reload");
59
+ }
60
+ return manifest;
61
+ };
62
+ const getManifestSync = () => manifest;
63
+ const reloadManifestSync = () => {
64
+ const fresh = reloadManifest();
65
+ manifest = fresh;
66
+ return fresh;
67
+ };
68
+ const invalidateManifestCache = () => {
69
+ manifestJsonCache = null;
70
+ manifest = undefined;
71
+ };
72
+ return {
73
+ getCachedManifestJson,
74
+ getCachedManifest,
75
+ getManifestSync,
76
+ reloadManifestSync,
77
+ invalidateManifestCache,
78
+ };
79
+ };
80
+ exports.createManifestCache = createManifestCache;
81
+ //# sourceMappingURL=manifestCache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifestCache.js","sourceRoot":"","sources":["../../../../../../../libs/iris-app-sdk/vite/src/executors/utils/manifestCache.ts"],"names":[],"mappings":";;;AACA,uDAAmD;AAwBnD;;;;;;;;;;;GAWG;AACI,MAAM,mBAAmB,GAAG,CAAC,EAClC,cAAc,EACd,oBAAoB,EACpB,eAAe,GACG,EAAiB,EAAE;IACrC,IAAI,QAAQ,GAAgC,eAAe,CAAC;IAC5D,IAAI,iBAAiB,GAA2B,IAAI,CAAC;IACrD,6EAA6E;IAC7E,wCAAwC;IACxC,IAAI,sBAAsB,GAAoC,IAAI,CAAC;IAEnE,MAAM,oBAAoB,GAAG,GAA6B,EAAE;QAC1D,IAAI,QAAQ,KAAK,SAAS,IAAI,iBAAiB,KAAK,IAAI,EAAE,CAAC;YACzD,OAAO,OAAO,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;QAC5C,CAAC;QACD,IAAI,sBAAsB,KAAK,IAAI,EAAE,CAAC;YACpC,IAAA,0BAAQ,EAAC,oDAAoD,CAAC,CAAC;YAC/D,OAAO,sBAAsB,CAAC;QAChC,CAAC;QACD,IAAA,0BAAQ,EAAC,yCAAyC,CAAC,CAAC;QACpD,MAAM,aAAa,GAAG,CAAC,KAAK,IAA8B,EAAE;YAC1D,MAAM,cAAc,GAAG,QAAQ,IAAI,cAAc,EAAE,CAAC;YACpD,QAAQ,GAAG,cAAc,CAAC;YAC1B,iBAAiB,GAAG,IAAA,sBAAI,EAAC,sBAAsB,EAAE,GAAG,EAAE,CAAC,oBAAoB,CAAC,cAAc,CAAC,CAAC,CAAC;YAC7F,OAAO,iBAAiB,CAAC;QAC3B,CAAC,CAAC,EAAE,CAAC;QACL,sBAAsB,GAAG,aAAa,CAAC;QACvC,wEAAwE;QACxE,qEAAqE;QACrE,uEAAuE;QACvE,wEAAwE;QACxE,yEAAyE;QACzE,uEAAuE;QACvE,2DAA2D;QAC3D,MAAM,2BAA2B,GAAG,GAAS,EAAE;YAC7C,IAAI,sBAAsB,KAAK,aAAa,EAAE,CAAC;gBAC7C,sBAAsB,GAAG,IAAI,CAAC;YAChC,CAAC;QACH,CAAC,CAAC;QACF,aAAa,CAAC,IAAI,CAAC,2BAA2B,EAAE,2BAA2B,CAAC,CAAC;QAC7E,OAAO,aAAa,CAAC;IACvB,CAAC,CAAC;IAEF,MAAM,qBAAqB,GAAG,GAA6B,EAAE,CAAC,oBAAoB,EAAE,CAAC;IAErF,MAAM,iBAAiB,GAAG,KAAK,IAA8B,EAAE;QAC7D,MAAM,oBAAoB,EAAE,CAAC;QAC7B,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;QAClE,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC,CAAC;IAEF,MAAM,eAAe,GAAG,GAAgC,EAAE,CAAC,QAAQ,CAAC;IAEpE,MAAM,kBAAkB,GAAG,GAAoB,EAAE;QAC/C,MAAM,KAAK,GAAG,cAAc,EAAE,CAAC;QAC/B,QAAQ,GAAG,KAAK,CAAC;QACjB,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;IAEF,MAAM,uBAAuB,GAAG,GAAS,EAAE;QACzC,iBAAiB,GAAG,IAAI,CAAC;QACzB,QAAQ,GAAG,SAAS,CAAC;IACvB,CAAC,CAAC;IAEF,OAAO;QACL,qBAAqB;QACrB,iBAAiB;QACjB,eAAe;QACf,kBAAkB;QAClB,uBAAuB;KACxB,CAAC;AACJ,CAAC,CAAC;AAzEW,QAAA,mBAAmB,uBAyE9B","sourcesContent":["import type { IrisAppManifest } from \"@trackunit/iris-app-api\";\nimport { logEvent, time } from \"./devServerTiming\";\n\ninterface ManifestCacheDeps {\n /** Synchronously loads (or reloads) the manifest from disk. */\n reloadManifest: () => IrisAppManifest;\n /** Processes the raw manifest into the JSON form served to clients. */\n generateManifestJson: (manifest: IrisAppManifest) => IrisAppManifest;\n /** Optional manifest already loaded by the caller (e.g. for federation config) so the first request is warm. */\n initialManifest?: IrisAppManifest;\n}\n\nexport interface ManifestCache {\n /** Returns the processed manifest JSON. Cached and deduplicates concurrent calls. */\n getCachedManifestJson: () => Promise<IrisAppManifest>;\n /** Returns the raw (un-processed) manifest. Triggers a load if the cache is empty. */\n getCachedManifest: () => Promise<IrisAppManifest>;\n /** Returns the current raw manifest synchronously, or undefined if it has not been loaded yet. */\n getManifestSync: () => IrisAppManifest | undefined;\n /** Forces a synchronous reload from disk and stores the fresh manifest. Does not clear the JSON cache. */\n reloadManifestSync: () => IrisAppManifest;\n /** Clears all cached state so the next read re-runs the full pipeline. */\n invalidateManifestCache: () => void;\n}\n\n/**\n * Creates an in-memory cache for the Iris-app manifest.\n *\n * The cache deduplicates concurrent /manifest.json and /manifestAndToken\n * requests so they don't re-resolve the manifest + all 25 extension entries\n * on every call. Cold-load profiling showed each request taking ~3s; with the\n * host firing several parallel calls during boot, that stacked into >6s of\n * latency before any extension code could load.\n *\n * The cache is invalidated when iris-app-manifest.ts changes; callers wire\n * that up through the dev server's file watcher.\n */\nexport const createManifestCache = ({\n reloadManifest,\n generateManifestJson,\n initialManifest,\n}: ManifestCacheDeps): ManifestCache => {\n let manifest: IrisAppManifest | undefined = initialManifest;\n let manifestJsonCache: IrisAppManifest | null = null;\n // Shared promise for an in-flight reload, used to dedupe concurrent requests\n // that arrive while the cache is empty.\n let inflightManifestReload: Promise<IrisAppManifest> | null = null;\n\n const ensureManifestLoaded = (): Promise<IrisAppManifest> => {\n if (manifest !== undefined && manifestJsonCache !== null) {\n return Promise.resolve(manifestJsonCache);\n }\n if (inflightManifestReload !== null) {\n logEvent(\"manifest cache: joining in-flight reload (deduped)\");\n return inflightManifestReload;\n }\n logEvent(\"manifest cache: cold load (cache empty)\");\n const reloadPromise = (async (): Promise<IrisAppManifest> => {\n const loadedManifest = manifest ?? reloadManifest();\n manifest = loadedManifest;\n manifestJsonCache = time(\"generateManifestJson\", () => generateManifestJson(loadedManifest));\n return manifestJsonCache;\n })();\n inflightManifestReload = reloadPromise;\n // Clear the inflight pointer once the reload settles so post-invalidate\n // reads start a fresh load instead of joining this resolved/rejected\n // promise. The identity check guards against `invalidateManifestCache`\n // having already nulled it out (or replaced it with a newer reload). We\n // attach the cleanup with then(cleanup, cleanup) instead of finally() so\n // a rejection isn't re-thrown from this side-channel; the caller still\n // observes the rejection via the returned `reloadPromise`.\n const clearInflightIfStillCurrent = (): void => {\n if (inflightManifestReload === reloadPromise) {\n inflightManifestReload = null;\n }\n };\n reloadPromise.then(clearInflightIfStillCurrent, clearInflightIfStillCurrent);\n return reloadPromise;\n };\n\n const getCachedManifestJson = (): Promise<IrisAppManifest> => ensureManifestLoaded();\n\n const getCachedManifest = async (): Promise<IrisAppManifest> => {\n await ensureManifestLoaded();\n if (manifest === undefined) {\n throw new Error(\"Manifest unexpectedly undefined after reload\");\n }\n return manifest;\n };\n\n const getManifestSync = (): IrisAppManifest | undefined => manifest;\n\n const reloadManifestSync = (): IrisAppManifest => {\n const fresh = reloadManifest();\n manifest = fresh;\n return fresh;\n };\n\n const invalidateManifestCache = (): void => {\n manifestJsonCache = null;\n manifest = undefined;\n };\n\n return {\n getCachedManifestJson,\n getCachedManifest,\n getManifestSync,\n reloadManifestSync,\n invalidateManifestCache,\n };\n};\n"]}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../libs/iris-app-sdk/vite/src/index.ts"],"names":[],"mappings":"","sourcesContent":[""]}