@nu-art/build-and-install 0.500.0 → 0.500.6

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.
Files changed (52) hide show
  1. package/BuildAndInstall.d.ts +2 -2
  2. package/BuildAndInstall.js +9 -2
  3. package/config/consts.d.ts +2 -2
  4. package/config/consts.js +1 -1
  5. package/config/package/consts.d.ts +1 -1
  6. package/config/types/package/runtime-package.d.ts +1 -1
  7. package/config/types/project-config.d.ts +3 -1
  8. package/core/FilesCache.d.ts +5 -1
  9. package/core/FilesCache.js +5 -1
  10. package/core/Unit_HelpPrinter.d.ts +4 -7
  11. package/core/Unit_HelpPrinter.js +3 -11
  12. package/core/params.d.ts +2 -1
  13. package/core/params.js +10 -1
  14. package/core/types.d.ts +2 -2
  15. package/exceptions/UnitPhaseException.d.ts +1 -1
  16. package/package.json +5 -5
  17. package/phases/PhaseManager.d.ts +2 -2
  18. package/phases/definitions/consts.d.ts +3 -0
  19. package/phases/definitions/consts.js +7 -0
  20. package/templates/firebase/functions/service.yaml +2 -0
  21. package/units/base/BaseUnit.d.ts +1 -1
  22. package/units/discovery/UnitsMapper.d.ts +3 -2
  23. package/units/discovery/resolvers/UnitMapper_Base.d.ts +2 -2
  24. package/units/discovery/resolvers/UnitMapper_FirebaseFunction.d.ts +12 -1
  25. package/units/discovery/resolvers/UnitMapper_FirebaseFunction.js +48 -18
  26. package/units/discovery/resolvers/UnitMapper_Node.d.ts +1 -1
  27. package/units/discovery/resolvers/UnitMapper_Node.js +2 -13
  28. package/units/implementations/Unit_NodeProject.d.ts +3 -2
  29. package/units/implementations/Unit_NodeProject.js +30 -6
  30. package/units/implementations/Unit_PackageJson.d.ts +6 -1
  31. package/units/implementations/Unit_PackageJson.js +38 -11
  32. package/units/implementations/Unit_TypescriptLib.d.ts +1 -1
  33. package/units/implementations/Unit_TypescriptLib.js +29 -2
  34. package/units/implementations/firebase/Unit_FirebaseFunctionsApp.d.ts +12 -2
  35. package/units/implementations/firebase/Unit_FirebaseFunctionsApp.js +49 -2
  36. package/units/implementations/firebase/Unit_HostingApp.d.ts +2 -2
  37. package/workspace/Workspace.d.ts +3 -1
  38. package/workspace/Workspace.js +2 -1
  39. package/config/types/configs/index.d.ts +0 -3
  40. package/config/types/configs/index.js +0 -3
  41. package/config/types/index.d.ts +0 -4
  42. package/config/types/index.js +0 -4
  43. package/config/types/package/index.d.ts +0 -2
  44. package/config/types/package/index.js +0 -2
  45. package/phases/definitions/index.d.ts +0 -2
  46. package/phases/definitions/index.js +0 -2
  47. package/phases/index.d.ts +0 -2
  48. package/phases/index.js +0 -2
  49. package/units/discovery/resolvers/index.d.ts +0 -5
  50. package/units/discovery/resolvers/index.js +0 -5
  51. package/units/index.d.ts +0 -6
  52. package/units/index.js +0 -6
@@ -1,10 +1,10 @@
1
1
  import { Logger } from '@nu-art/ts-common';
2
2
  import { BaiParams } from './core/params.js';
3
- import { Phase } from './phases/definitions/index.js';
3
+ import type { Phase } from './phases/definitions/types.js';
4
4
  import { UnitsMapper } from './units/discovery/UnitsMapper.js';
5
5
  import { ProjectUnit } from './units/base/ProjectUnit.js';
6
6
  import { PhaseManager } from './phases/PhaseManager.js';
7
- import { Unit_NodeProject } from './units/index.js';
7
+ import { Unit_NodeProject } from './units/implementations/Unit_NodeProject.js';
8
8
  import { Workspace } from './workspace/Workspace.js';
9
9
  import { BaseCliParam } from '@nu-art/cli-params';
10
10
  import { RunningStatusHandler } from './runtime/RunningStatusHandler.js';
@@ -1,6 +1,7 @@
1
1
  import { BeLogged, DebugFlag, filterDuplicates, ImplementationMissingException, LogClient_Terminal, Logger, LogLevel, merge } from '@nu-art/ts-common';
2
2
  import { AllBaiParams } from './core/params.js';
3
- import { phases_Build, phases_Deploy, phases_Launch, phases_Terminating } from './phases/definitions/index.js';
3
+ import { phase_Help, Unit_HelpPrinter } from './core/Unit_HelpPrinter.js';
4
+ import { phases_Build, phases_Deploy, phases_Launch, phases_Terminating } from './phases/definitions/consts.js';
4
5
  import { UnitsMapper } from './units/discovery/UnitsMapper.js';
5
6
  import { FilesCache } from './core/FilesCache.js';
6
7
  import { ProjectUnit } from './units/base/ProjectUnit.js';
@@ -8,11 +9,16 @@ import { PhaseManager } from './phases/PhaseManager.js';
8
9
  import { Workspace } from './workspace/Workspace.js';
9
10
  import { resolve } from 'path';
10
11
  import { CONST_BaiConfig, CONST_NodeModules, CONST_VersionApp } from './config/consts.js';
11
- import { UnitMapper_FirebaseFunction, UnitMapper_FirebaseHosting, UnitMapper_NodeLib, UnitMapper_NodeProject, UnitMapper_ViteHosting } from './units/discovery/resolvers/index.js';
12
+ import { UnitMapper_FirebaseFunction } from './units/discovery/resolvers/UnitMapper_FirebaseFunction.js';
13
+ import { UnitMapper_FirebaseHosting } from './units/discovery/resolvers/UnitMapper_FirebaseHosting.js';
14
+ import { UnitMapper_NodeLib } from './units/discovery/resolvers/UnitMapper_NodeLib.js';
15
+ import { UnitMapper_NodeProject } from './units/discovery/resolvers/UnitMapper_NodeProject.js';
16
+ import { UnitMapper_ViteHosting } from './units/discovery/resolvers/UnitMapper_ViteHosting.js';
12
17
  import { CLIParamsResolver } from '@nu-art/cli-params';
13
18
  import { RunningStatusHandler } from './runtime/RunningStatusHandler.js';
14
19
  import { FileSystemUtils } from '@nu-art/ts-common/utils/FileSystemUtils';
15
20
  export const DefaultPhases = [
21
+ [phase_Help],
16
22
  ...phases_Build,
17
23
  ...phases_Terminating,
18
24
  ...phases_Launch,
@@ -100,6 +106,7 @@ export class BuildAndInstall extends Logger {
100
106
  }
101
107
  async build() {
102
108
  await this.init();
109
+ this.workspace.addProjectUnits([Unit_HelpPrinter]);
103
110
  // Scan units from workspace
104
111
  await this.workspace.scanUnits(this.pathToProject, this.unitsMapper);
105
112
  const nodeProjectUnit = this.workspace.getNodeProjectUnit();
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Constants for file names and configuration keys used throughout the build system.
3
3
  */
4
- import { MemKey } from '@nu-art/ts-common/mem-storage/index';
5
- import { RuntimeProjectConfig } from './types/index.js';
4
+ import { MemKey } from '@nu-art/ts-common/mem-storage';
5
+ import { RuntimeProjectConfig } from './types/project-config.js';
6
6
  /** Version file name (version-app.json) */
7
7
  export declare const CONST_VersionApp = "version-app.json";
8
8
  /** Package.json template file name (__package.json) */
package/config/consts.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Constants for file names and configuration keys used throughout the build system.
3
3
  */
4
- import { MemKey } from '@nu-art/ts-common/mem-storage/index';
4
+ import { MemKey } from '@nu-art/ts-common/mem-storage';
5
5
  /** Version file name (version-app.json) */
6
6
  export const CONST_VersionApp = 'version-app.json';
7
7
  /** Package.json template file name (__package.json) */
@@ -1,4 +1,4 @@
1
- import { FirebasePackageConfig } from '../types/index.js';
1
+ import { FirebasePackageConfig } from '../types/package/package.js';
2
2
  export type DefaultType_ProjectEnvs = 'local' | 'dev' | 'staging' | 'prod';
3
3
  export declare const Default_ProjectEnvs: ("local" | "staging" | "dev" | "prod")[];
4
4
  export declare const Default_FunctionsIgnoreFiles: string[];
@@ -1,4 +1,4 @@
1
- import { PackageJson } from '../configs/index.js';
1
+ import { PackageJson } from '../configs/package-json.js';
2
2
  import { Package, Package_Sourceless } from './package.js';
3
3
  type WithPackageJson = {
4
4
  packageJsonTemplate: PackageJson;
@@ -1,5 +1,6 @@
1
1
  import { StringMap, TypedMap } from '@nu-art/ts-common';
2
- import { Package, RuntimePackage } from './package/index.js';
2
+ import type { Package } from './package/package.js';
3
+ import type { RuntimePackage } from './package/runtime-package.js';
3
4
  export type ProjectConfig = {
4
5
  params: StringMap;
5
6
  packages: Package[];
@@ -28,6 +29,7 @@ export type BAI_Config = {
28
29
  'firebase.json'?: string;
29
30
  '.firebaserc'?: string;
30
31
  baseEmulationPort?: number;
32
+ mongoPort?: number;
31
33
  };
32
34
  playwright?: {
33
35
  browsers?: ('chromium' | 'firefox' | 'webkit')[];
@@ -16,11 +16,15 @@
16
16
  */
17
17
  export declare const FilesCache: {
18
18
  /**
19
- * Clears the file cache.
19
+ * Clears the entire file cache.
20
20
  *
21
21
  * Useful for tests or when files may have changed.
22
22
  */
23
23
  clear: () => {};
24
+ /**
25
+ * Invalidates a single cached entry so the next load re-reads from disk.
26
+ */
27
+ invalidate: (path: string) => void;
24
28
  load: {
25
29
  /**
26
30
  * Loads and caches a JSON file.
@@ -32,11 +32,15 @@ const readFile = async (path) => {
32
32
  */
33
33
  export const FilesCache = {
34
34
  /**
35
- * Clears the file cache.
35
+ * Clears the entire file cache.
36
36
  *
37
37
  * Useful for tests or when files may have changed.
38
38
  */
39
39
  clear: () => cachedFiles = {},
40
+ /**
41
+ * Invalidates a single cached entry so the next load re-reads from disk.
42
+ */
43
+ invalidate: (path) => { delete cachedFiles[path]; },
40
44
  load: {
41
45
  /**
42
46
  * Loads and caches a JSON file.
@@ -1,10 +1,8 @@
1
- import { BaseCliParam, CliParams } from '@nu-art/cli-params';
1
+ import { BaiParam_Help } from './params.js';
2
2
  import { UnitPhaseImplementor } from './types.js';
3
- import { Phase } from '../phases/definitions/index.js';
4
- import { ProjectUnit } from '../units/index.js';
5
- export declare const BaiParam_Help: BaseCliParam<'help', boolean>;
6
- declare const AllBaiParams_Help: (BaseCliParam<"allUnits", boolean> | BaseCliParam<"dependencyTree", boolean> | BaseCliParam<"environment", string> | BaseCliParam<"install", boolean> | BaseCliParam<"clean", boolean> | BaseCliParam<"purge", boolean> | BaseCliParam<"generate", boolean> | BaseCliParam<"generateDocs", boolean> | BaseCliParam<"noBuild", boolean> | BaseCliParam<"prepare", boolean> | BaseCliParam<"dryRun", boolean> | BaseCliParam<"lint", boolean> | BaseCliParam<"watch", boolean> | BaseCliParam<"watchBuildTree", boolean> | BaseCliParam<"continue", boolean> | BaseCliParam<"test", boolean> | BaseCliParam<"testType", string[]> | BaseCliParam<"testFiles", string[]> | BaseCliParam<"testCases", string[]> | BaseCliParam<"testDebugPort", number> | BaseCliParam<"launch", boolean> | BaseCliParam<"debugBackend", boolean> | BaseCliParam<"deploy", boolean> | BaseCliParam<"buildPushImage", string> | BaseCliParam<"deployImage", string> | BaseCliParam<"deployFunction", string> | BaseCliParam<"deleteFunctions", boolean> | BaseCliParam<"deleteFunction", string> | BaseCliParam<"debug", boolean> | BaseCliParam<"debugLifecycle", boolean> | BaseCliParam<"verbose", boolean> | BaseCliParam<"publish", "patch" | "minor" | "major"> | BaseCliParam<"usePackage", string[]> | BaseCliParam<"buildTree", boolean> | BaseCliParam<"includeApps", string[]> | BaseCliParam<"toESM", boolean> | BaseCliParam<"simulation", boolean> | BaseCliParam<"checkCyclicImports", boolean> | BaseCliParam<"extractDynamicDeps", boolean> | BaseCliParam<"mapExports", boolean> | BaseCliParam<"indicesMcpServer", boolean> | BaseCliParam<"indicesMcpPort", number> | BaseCliParam<"help", boolean>)[];
7
- export type Help_BaiParams = CliParams<typeof AllBaiParams_Help>;
3
+ import type { Phase } from '../phases/definitions/types.js';
4
+ import { ProjectUnit } from '../units/base/ProjectUnit.js';
5
+ export { BaiParam_Help };
8
6
  export type Phase_Help = typeof phase_Help;
9
7
  export declare const phaseKey_Help = "help";
10
8
  export declare const phase_Help: Phase<'printHelp'>;
@@ -13,4 +11,3 @@ declare class _Unit_HelpPrinter extends ProjectUnit implements UnitPhaseImplemen
13
11
  printHelp(): Promise<void>;
14
12
  }
15
13
  export declare const Unit_HelpPrinter: _Unit_HelpPrinter;
16
- export {};
@@ -1,15 +1,7 @@
1
1
  import { _keys, LogLevel, reduceToMap } from '@nu-art/ts-common';
2
- import { AllBaiParams, BaiParam_AllUnits, BaiParam_NoBuild, BaiParam_Prepare } from './params.js';
3
- import { ProjectUnit } from '../units/index.js';
4
- export const BaiParam_Help = {
5
- keys: ['--help', '-h'],
6
- keyName: 'help',
7
- type: 'boolean',
8
- group: 'General',
9
- description: 'This help menu',
10
- dependencies: [{ param: BaiParam_NoBuild, value: true }, { param: BaiParam_Prepare, value: false }, { param: BaiParam_AllUnits, value: true }],
11
- };
12
- const AllBaiParams_Help = [...AllBaiParams, BaiParam_Help];
2
+ import { AllBaiParams, BaiParam_Help } from './params.js';
3
+ import { ProjectUnit } from '../units/base/ProjectUnit.js';
4
+ export { BaiParam_Help };
13
5
  export const phaseKey_Help = 'help';
14
6
  export const phase_Help = {
15
7
  key: phaseKey_Help,
package/core/params.d.ts CHANGED
@@ -45,6 +45,7 @@ export declare const BaiParam_ExtractDynamicDeps: BaseCliParam<'extractDynamicDe
45
45
  export declare const BaiParam_MapExports: BaseCliParam<'mapExports', boolean>;
46
46
  export declare const BaiParam_IndicesMcpServer: BaseCliParam<'indicesMcpServer', boolean>;
47
47
  export declare const BaiParam_IndicesMcpPort: BaseCliParam<'indicesMcpPort', number>;
48
- export declare const AllBaiParams: (BaseCliParam<"allUnits", boolean> | BaseCliParam<"dependencyTree", boolean> | BaseCliParam<"environment", string> | BaseCliParam<"install", boolean> | BaseCliParam<"clean", boolean> | BaseCliParam<"purge", boolean> | BaseCliParam<"generate", boolean> | BaseCliParam<"generateDocs", boolean> | BaseCliParam<"noBuild", boolean> | BaseCliParam<"prepare", boolean> | BaseCliParam<"dryRun", boolean> | BaseCliParam<"lint", boolean> | BaseCliParam<"watch", boolean> | BaseCliParam<"watchBuildTree", boolean> | BaseCliParam<"continue", boolean> | BaseCliParam<"test", boolean> | BaseCliParam<"testType", string[]> | BaseCliParam<"testFiles", string[]> | BaseCliParam<"testCases", string[]> | BaseCliParam<"testDebugPort", number> | BaseCliParam<"launch", boolean> | BaseCliParam<"debugBackend", boolean> | BaseCliParam<"deploy", boolean> | BaseCliParam<"buildPushImage", string> | BaseCliParam<"deployImage", string> | BaseCliParam<"deployFunction", string> | BaseCliParam<"deleteFunctions", boolean> | BaseCliParam<"deleteFunction", string> | BaseCliParam<"debug", boolean> | BaseCliParam<"debugLifecycle", boolean> | BaseCliParam<"verbose", boolean> | BaseCliParam<"publish", PromoteType> | BaseCliParam<"usePackage", string[]> | BaseCliParam<"buildTree", boolean> | BaseCliParam<"includeApps", string[]> | BaseCliParam<"toESM", boolean> | BaseCliParam<"simulation", boolean> | BaseCliParam<"checkCyclicImports", boolean> | BaseCliParam<"extractDynamicDeps", boolean> | BaseCliParam<"mapExports", boolean> | BaseCliParam<"indicesMcpServer", boolean> | BaseCliParam<"indicesMcpPort", number>)[];
48
+ export declare const BaiParam_Help: BaseCliParam<'help', boolean>;
49
+ export declare const AllBaiParams: (BaseCliParam<"allUnits", boolean> | BaseCliParam<"dependencyTree", boolean> | BaseCliParam<"environment", string> | BaseCliParam<"install", boolean> | BaseCliParam<"clean", boolean> | BaseCliParam<"purge", boolean> | BaseCliParam<"generate", boolean> | BaseCliParam<"generateDocs", boolean> | BaseCliParam<"noBuild", boolean> | BaseCliParam<"prepare", boolean> | BaseCliParam<"dryRun", boolean> | BaseCliParam<"lint", boolean> | BaseCliParam<"watch", boolean> | BaseCliParam<"watchBuildTree", boolean> | BaseCliParam<"continue", boolean> | BaseCliParam<"test", boolean> | BaseCliParam<"testType", string[]> | BaseCliParam<"testFiles", string[]> | BaseCliParam<"testCases", string[]> | BaseCliParam<"testDebugPort", number> | BaseCliParam<"launch", boolean> | BaseCliParam<"debugBackend", boolean> | BaseCliParam<"deploy", boolean> | BaseCliParam<"buildPushImage", string> | BaseCliParam<"deployImage", string> | BaseCliParam<"deployFunction", string> | BaseCliParam<"deleteFunctions", boolean> | BaseCliParam<"deleteFunction", string> | BaseCliParam<"debug", boolean> | BaseCliParam<"debugLifecycle", boolean> | BaseCliParam<"verbose", boolean> | BaseCliParam<"publish", PromoteType> | BaseCliParam<"usePackage", string[]> | BaseCliParam<"buildTree", boolean> | BaseCliParam<"includeApps", string[]> | BaseCliParam<"toESM", boolean> | BaseCliParam<"simulation", boolean> | BaseCliParam<"checkCyclicImports", boolean> | BaseCliParam<"extractDynamicDeps", boolean> | BaseCliParam<"mapExports", boolean> | BaseCliParam<"indicesMcpServer", boolean> | BaseCliParam<"indicesMcpPort", number> | BaseCliParam<"help", boolean>)[];
49
50
  export type BaiParams = CliParams<typeof AllBaiParams>;
50
51
  export {};
package/core/params.js CHANGED
@@ -395,6 +395,14 @@ export const BaiParam_IndicesMcpPort = {
395
395
  description: 'Port for Export Indices MCP server (default: 3001)',
396
396
  dependencies: [{ param: BaiParam_IndicesMcpServer, value: true }]
397
397
  };
398
+ export const BaiParam_Help = {
399
+ keys: ['--help', '-h'],
400
+ keyName: 'help',
401
+ type: 'boolean',
402
+ group: 'General',
403
+ description: 'This help menu',
404
+ dependencies: [{ param: BaiParam_NoBuild, value: true }, { param: BaiParam_Prepare, value: false }, { param: BaiParam_AllUnits, value: true }],
405
+ };
398
406
  export const AllBaiParams = [
399
407
  BaiParam_AllUnits,
400
408
  BaiParam_DependencyTree,
@@ -437,5 +445,6 @@ export const AllBaiParams = [
437
445
  BaiParam_MapExports,
438
446
  BaiParam_IndicesMcpServer,
439
447
  BaiParam_IndicesMcpPort,
440
- BaiParam_DebugLifecycle
448
+ BaiParam_DebugLifecycle,
449
+ BaiParam_Help,
441
450
  ];
package/core/types.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { AsyncVoidFunction } from '@nu-art/ts-common';
2
- import { BaseUnit } from '../units/index.js';
3
- import { Phase } from '../phases/definitions/index.js';
2
+ import { BaseUnit } from '../units/base/BaseUnit.js';
3
+ import type { Phase } from '../phases/definitions/types.js';
4
4
  export type Unit<P extends Phase<string>[]> = BaseUnit & UnitPhaseImplementor<P>;
5
5
  export type UnitPhaseImplementor<P extends Phase<string>[]> = {
6
6
  [K in P[number]['method']]: AsyncVoidFunction;
@@ -1,5 +1,5 @@
1
1
  import { CustomException } from '@nu-art/ts-common';
2
- import { BaseUnit } from '../units/index.js';
2
+ import { BaseUnit } from '../units/base/BaseUnit.js';
3
3
  /**
4
4
  * Exception thrown when a specific phase fails on a specific unit.
5
5
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nu-art/build-and-install",
3
- "version": "0.500.0",
3
+ "version": "0.500.6",
4
4
  "description": "A build system for monorepos that orchestrates building, testing, and deploying units with dependency-aware phase execution",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -39,12 +39,12 @@
39
39
  },
40
40
  "dependencies": {
41
41
  "chokidar": "^3.6.0",
42
- "@nu-art/cli-params": "0.500.0",
43
- "@nu-art/ts-common": "0.500.0",
44
- "@nu-art/commando": "0.500.0"
42
+ "@nu-art/cli-params": "0.500.6",
43
+ "@nu-art/ts-common": "0.500.6",
44
+ "@nu-art/commando": "0.500.6"
45
45
  },
46
46
  "devDependencies": {
47
- "@nu-art/testalot": "0.500.0"
47
+ "@nu-art/testalot": "0.500.6"
48
48
  },
49
49
  "unitConfig": {
50
50
  "type": "typescript-lib"
@@ -1,7 +1,7 @@
1
1
  import { Logger } from '@nu-art/ts-common';
2
2
  import { RunningStatusHandler } from '../runtime/RunningStatusHandler.js';
3
- import { Phase } from './definitions/index.js';
4
- import { BaseUnit } from '../units/index.js';
3
+ import type { Phase } from './definitions/types.js';
4
+ import { BaseUnit } from '../units/base/BaseUnit.js';
5
5
  /**
6
6
  * Scheduled execution step (before mapping to actual phases/units).
7
7
  */
@@ -57,6 +57,9 @@ export declare const phase_Compile: Phase<'compile'>;
57
57
  export type Phase_PrepareWatch = typeof phase_PrepareWatch;
58
58
  export declare const phaseKey_PrepareWatch = "watchPrepare";
59
59
  export declare const phase_PrepareWatch: Phase<'watchPrepare'>;
60
+ export type Phase_InstallWatch = typeof phase_InstallWatch;
61
+ export declare const phaseKey_InstallWatch = "watchInstall";
62
+ export declare const phase_InstallWatch: Phase<'watchInstall'>;
60
63
  export type Phase_CompileWatch = typeof phase_CompileWatch;
61
64
  export declare const phaseKey_CompileWatch = "watchCompile";
62
65
  export declare const phase_CompileWatch: Phase<'watchCompile'>;
@@ -92,6 +92,13 @@ export const phase_PrepareWatch = {
92
92
  filter: (baiParams) => baiParams.prepare,
93
93
  unitCategory: 'project', // All project units need to be prepared
94
94
  };
95
+ export const phaseKey_InstallWatch = 'watchInstall';
96
+ export const phase_InstallWatch = {
97
+ key: phaseKey_InstallWatch,
98
+ name: 'Install Watch',
99
+ method: 'watchInstall',
100
+ dependencyPhase: [phase_PrepareWatch],
101
+ };
95
102
  export const phaseKey_CompileWatch = 'watchCompile';
96
103
  export const phase_CompileWatch = {
97
104
  key: phaseKey_CompileWatch,
@@ -12,6 +12,8 @@ metadata:
12
12
  cloudfunctions.googleapis.com/function-id: {{FUNCTION_NAME}}
13
13
  run.googleapis.com/ingress: all
14
14
  run.googleapis.com/ingress-status: all
15
+ run.googleapis.com/invoker-iam-disabled: 'true'
16
+ run.googleapis.com/maxScale: '100'
15
17
  spec:
16
18
  template:
17
19
  metadata:
@@ -3,7 +3,7 @@ import { CommandoInteractive } from '@nu-art/commando';
3
3
  import { BaseCommando } from '@nu-art/commando';
4
4
  import { MergeTypes } from '@nu-art/commando';
5
5
  import { Commando_Basic } from '@nu-art/commando';
6
- import { BAI_Config } from '../../config/types/index.js';
6
+ import { BAI_Config } from '../../config/types/project-config.js';
7
7
  import { UnitsDependencyMapper } from '../../dependencies/UnitsDependencyMapper.js';
8
8
  import { BaiParams } from '../../core/params.js';
9
9
  import { Workspace } from '../../workspace/Workspace.js';
@@ -1,4 +1,5 @@
1
- import { BaseUnit, ProjectUnit } from '../index.js';
1
+ import { BaseUnit } from '../base/BaseUnit.js';
2
+ import { ProjectUnit } from '../base/ProjectUnit.js';
2
3
  import { Logger } from '@nu-art/ts-common';
3
4
  import { UnitMapper_Base } from './resolvers/UnitMapper_Base.js';
4
5
  import { BaiParams } from '../../core/params.js';
@@ -34,7 +35,7 @@ export declare class UnitsMapper extends Logger {
34
35
  * @param projectRoot - The path to the project root
35
36
  * @param units - The project units derived from the file system
36
37
  */
37
- resolveUnits(path: string, projectRoot?: string, units?: BaseUnit<any>[]): Promise<BaseUnit<any, import("../index.js").UnitRuntimeContext>[]>;
38
+ resolveUnits(path: string, projectRoot?: string, units?: BaseUnit<any>[]): Promise<BaseUnit<any, import("../base/BaseUnit.js").UnitRuntimeContext>[]>;
38
39
  /**
39
40
  * Adds unit resolution rules.
40
41
  *
@@ -1,6 +1,6 @@
1
1
  import { AbsolutePath, Logger, RelativePath, StringMap, TypeValidator } from '@nu-art/ts-common';
2
- import { BaseUnit } from '../../index.js';
3
- import { BAI_Config } from '../../../config/types/index.js';
2
+ import { BaseUnit } from '../../base/BaseUnit.js';
3
+ import { BAI_Config } from '../../../config/types/project-config.js';
4
4
  import { BaiParams } from '../../../core/params.js';
5
5
  export type BaseUnitConfig = {
6
6
  fullPath: AbsolutePath;
@@ -1,11 +1,13 @@
1
1
  import { TypedMap } from '@nu-art/ts-common';
2
2
  import { UnitConfigJSON_Node, UnitMapper_Node, UnitMapper_NodeContext } from './UnitMapper_Node.js';
3
3
  import { FunctionConfig, Unit_FirebaseFunctionsApp } from '../../implementations/firebase/Unit_FirebaseFunctionsApp.js';
4
+ type EnvFunctionOverride = Pick<FunctionConfig, 'name'> & Partial<Omit<FunctionConfig, 'name'>>;
4
5
  type EnvConfig = {
5
6
  defaultConfig?: string;
6
7
  envConfig?: string;
7
8
  projectId: string;
8
9
  isLocal?: boolean;
10
+ functions?: EnvFunctionOverride[];
9
11
  };
10
12
  type UnitConfigJSON_FirebaseFunction = UnitConfigJSON_Node & {
11
13
  debugPort?: number;
@@ -15,6 +17,10 @@ type UnitConfigJSON_FirebaseFunction = UnitConfigJSON_Node & {
15
17
  sslKey?: string;
16
18
  sslCert?: string;
17
19
  functions: string[] | FunctionConfig[];
20
+ mongo?: {
21
+ port?: number;
22
+ dbName?: string;
23
+ };
18
24
  };
19
25
  export declare class UnitMapper_FirebaseFunction_Class extends UnitMapper_Node<Unit_FirebaseFunctionsApp, UnitConfigJSON_FirebaseFunction> {
20
26
  static tsValidator_FirebaseFunction: {
@@ -30,10 +36,12 @@ export declare class UnitMapper_FirebaseFunction_Class extends UnitMapper_Node<U
30
36
  sslKey: import("@nu-art/ts-common").Validator<string>;
31
37
  sslCert: import("@nu-art/ts-common").Validator<string>;
32
38
  functions: import("@nu-art/ts-common").Validator<FunctionConfig[]>;
39
+ mongo: (import("@nu-art/ts-common").ValidatorImpl<any> | ((input?: object | undefined) => string | object | undefined))[];
33
40
  containerDeployment: (import("@nu-art/ts-common").ValidatorImpl<any> | ((input?: object | undefined) => string | object | undefined))[];
34
41
  };
35
42
  constructor();
36
43
  protected resolveNodeUnit(context: UnitMapper_NodeContext<UnitConfigJSON_FirebaseFunction>): Promise<Unit_FirebaseFunctionsApp<{
44
+ functions: FunctionConfig[];
37
45
  envConfig: {
38
46
  defaultConfig: string | undefined;
39
47
  envConfig: string | undefined;
@@ -53,7 +61,10 @@ export declare class UnitMapper_FirebaseFunction_Class extends UnitMapper_Node<U
53
61
  basePort: number;
54
62
  envs: TypedMap<EnvConfig>;
55
63
  ignore?: string[] | undefined;
56
- functions: string[] | FunctionConfig[];
64
+ mongo?: {
65
+ port?: number;
66
+ dbName?: string;
67
+ } | undefined;
57
68
  pathToFirebaseConfig: string;
58
69
  pathToEmulatorData: string;
59
70
  fullPath: import("@nu-art/ts-common").AbsolutePath;
@@ -1,14 +1,8 @@
1
- import { ImplementationMissingException, tsValidate_OptionalArray, tsValidateAnyNumber, tsValidateAnyString, tsValidateBoolean, tsValidateDynamicObject, tsValidateOptionalAnyNumber, tsValidateOptionalAnyString, tsValidateOptionalObject, tsValidateRegexp, tsValidateResult, tsValidateValue } from '@nu-art/ts-common';
1
+ import { ImplementationMissingException, mergeObject, tsValidate_OptionalArray, tsValidateAnyNumber, tsValidateAnyString, tsValidateBoolean, tsValidateDynamicObject, tsValidateOptionalAnyNumber, tsValidateOptionalAnyString, tsValidateOptionalObject, tsValidateRegexp, tsValidateResult, tsValidateValue } from '@nu-art/ts-common';
2
2
  import { UnitMapper_Node } from './UnitMapper_Node.js';
3
3
  import { Unit_FirebaseFunctionsApp } from '../../implementations/firebase/Unit_FirebaseFunctionsApp.js';
4
4
  import { resolve } from 'path';
5
5
  import { BaiParam_SetEnv } from '../../../core/params.js';
6
- const valuesValidator = {
7
- defaultConfig: tsValidateOptionalAnyString,
8
- envConfig: tsValidateOptionalAnyString,
9
- projectId: tsValidateAnyString,
10
- isLocal: tsValidateBoolean(false),
11
- };
12
6
  // Docker image name validation: lowercase, alphanumeric with dots, underscores, hyphens
13
7
  // Cannot start/end with separators, no consecutive separators
14
8
  // Pattern: starts with alphanumeric, optionally followed by (separator + alphanumeric) groups
@@ -40,19 +34,54 @@ const functionConfigValidator = {
40
34
  resources: tsValidateOptionalObject(functionResourceConfigValidator),
41
35
  };
42
36
  // Validator for functions array: accepts either string[] or FunctionConfig[]
43
- // Uses custom validator to check type first, then validate accordingly
44
37
  const functionItemValidator = (input) => {
45
- if (typeof input === 'string') {
46
- // Legacy format: just a string (function name)
38
+ if (typeof input === 'string')
47
39
  return tsValidateResult(input, tsValidateAnyString);
48
- }
49
- if (typeof input === 'object' && input !== null) {
50
- // New format: FunctionConfig object
40
+ if (typeof input === 'object' && input !== null)
51
41
  return tsValidateResult(input, tsValidateOptionalObject(functionConfigValidator));
52
- }
53
42
  return 'Function item must be either a string (function name) or an object (FunctionConfig)';
54
43
  };
55
44
  const functionsArrayValidator = tsValidate_OptionalArray(functionItemValidator);
45
+ // Env override validators — all fields optional except name
46
+ const envFunctionResourceValidator = {
47
+ ...functionResourceConfigValidator,
48
+ cpu: tsValidateOptionalAnyNumber,
49
+ };
50
+ const envFunctionOverrideValidator = {
51
+ name: tsValidateAnyString,
52
+ trigger: tsValidateOptionalAnyString,
53
+ schedule: tsValidateOptionalAnyString,
54
+ serviceAccountName: tsValidateOptionalAnyString,
55
+ resources: tsValidateOptionalObject(envFunctionResourceValidator),
56
+ };
57
+ const envFunctionsValidator = tsValidate_OptionalArray((input) => {
58
+ if (typeof input === 'object' && input !== null)
59
+ return tsValidateResult(input, tsValidateOptionalObject(envFunctionOverrideValidator));
60
+ return 'Env function override must be an object with a name property';
61
+ });
62
+ const valuesValidator = {
63
+ defaultConfig: tsValidateOptionalAnyString,
64
+ envConfig: tsValidateOptionalAnyString,
65
+ projectId: tsValidateAnyString,
66
+ isLocal: tsValidateBoolean(false),
67
+ functions: envFunctionsValidator,
68
+ };
69
+ const mongoEmulatorValidator = {
70
+ port: tsValidateOptionalAnyNumber,
71
+ dbName: tsValidateOptionalAnyString,
72
+ };
73
+ function mergeFunctionConfigs(defaults, overrides) {
74
+ const normalized = defaults.map(f => typeof f === 'string' ? { name: f, trigger: 'http' } : f);
75
+ if (!overrides || overrides.length === 0)
76
+ return normalized;
77
+ for (const override of overrides) {
78
+ const index = normalized.findIndex(f => f.name === override.name);
79
+ if (index === -1)
80
+ throw new ImplementationMissingException(`Env function override references unknown function '${override.name}'. Available: ${normalized.map(f => f.name).join(', ')}`);
81
+ normalized[index] = mergeObject(normalized[index], override);
82
+ }
83
+ return normalized;
84
+ }
56
85
  export class UnitMapper_FirebaseFunction_Class extends UnitMapper_Node {
57
86
  static tsValidator_FirebaseFunction = {
58
87
  type: tsValidateValue(['firebase-function']),
@@ -62,7 +91,8 @@ export class UnitMapper_FirebaseFunction_Class extends UnitMapper_Node {
62
91
  basePort: tsValidateOptionalAnyNumber,
63
92
  sslKey: tsValidateOptionalAnyString,
64
93
  sslCert: tsValidateOptionalAnyString,
65
- functions: functionsArrayValidator, // Required: non-empty array validated in process, accepts string[] or FunctionConfig[]
94
+ functions: functionsArrayValidator,
95
+ mongo: tsValidateOptionalObject(mongoEmulatorValidator),
66
96
  containerDeployment: tsValidateOptionalObject(containerDeploymentValidator),
67
97
  ...UnitMapper_Node.tsValidator_Node,
68
98
  };
@@ -82,14 +112,14 @@ export class UnitMapper_FirebaseFunction_Class extends UnitMapper_Node {
82
112
  isLocal: envUnitConfig?.isLocal ?? env === 'local'
83
113
  };
84
114
  const { type, ...unitConfig } = context.packageJson.unitConfig;
85
- // Validate functions array is required and non-empty
86
- if (!unitConfig.functions || !Array.isArray(unitConfig.functions) || unitConfig.functions.length === 0) {
115
+ if (!unitConfig.functions || !Array.isArray(unitConfig.functions) || unitConfig.functions.length === 0)
87
116
  throw new ImplementationMissingException(`Missing or empty 'functions' array in unit config for ${context.baseConfig.key}. Functions must be explicitly declared.`);
88
- }
117
+ const functions = mergeFunctionConfigs(unitConfig.functions, envUnitConfig?.functions);
89
118
  return new Unit_FirebaseFunctionsApp({
90
119
  ...context.baseConfig,
91
120
  ...Unit_FirebaseFunctionsApp.DefaultConfig_FirebaseFunction,
92
121
  ...unitConfig,
122
+ functions,
93
123
  envConfig,
94
124
  isTopLevelApp: true,
95
125
  hasSelfHotReload: unitConfig.hasSelfHotReload ?? false,
@@ -1,6 +1,6 @@
1
1
  import { TS_PackageJSON } from '../types.js';
2
2
  import { TypeValidator } from '@nu-art/ts-common';
3
- import { BaseUnit } from '../../index.js';
3
+ import { BaseUnit } from '../../base/BaseUnit.js';
4
4
  import { BaseUnitConfig, UnitConfigJSON_Base, UnitMapper_Base } from './UnitMapper_Base.js';
5
5
  export type UnitConfigJSON_Node = UnitConfigJSON_Base & {
6
6
  label: string;
@@ -1,4 +1,4 @@
1
- import { _keys, deepClone, tsValidateBoolean, tsValidateOptionalAnyString, tsValidateResult } from '@nu-art/ts-common';
1
+ import { tsValidateBoolean, tsValidateOptionalAnyString, tsValidateResult } from '@nu-art/ts-common';
2
2
  import { FilesCache } from '../../../core/FilesCache.js';
3
3
  import { UnitMapper_Base } from './UnitMapper_Base.js';
4
4
  import { FileSystemUtils } from '@nu-art/ts-common/utils/FileSystemUtils';
@@ -32,20 +32,9 @@ export class UnitMapper_Node extends UnitMapper_Base {
32
32
  }
33
33
  if (tsValidateResult(packageJson.unitConfig.type, this.validator.type))
34
34
  return; // not the expected type for this mapper
35
- packageJson = deepClone(packageJson);
35
+ packageJson = Unit_PackageJson.transformDependencyPlaceholders(packageJson);
36
36
  const configValidationResult = tsValidateResult(packageJson.unitConfig, this.validator, undefined, false);
37
37
  const dependencies = packageJson.dependencies;
38
- if (dependencies)
39
- packageJson.dependencies = _keys(dependencies).reduce((acc, key) => {
40
- acc[key] = dependencies[key] === '?' ? `{{${key}}}` : dependencies[key];
41
- return acc;
42
- }, {});
43
- const devDependencies = packageJson.devDependencies;
44
- if (devDependencies)
45
- packageJson.devDependencies = _keys(devDependencies).reduce((acc, key) => {
46
- acc[key] = devDependencies[key] === '?' ? `{{${key}}}` : devDependencies[key];
47
- return acc;
48
- }, {});
49
38
  Object.freeze(packageJson);
50
39
  const baseConfig = {
51
40
  key: packageJson.name,
@@ -2,7 +2,7 @@ import { UnitPhaseImplementor } from '../../core/types.js';
2
2
  import { StringMap } from '@nu-art/ts-common/utils/types';
3
3
  import { Unit_PackageJson, Unit_PackageJson_Config } from './Unit_PackageJson.js';
4
4
  import { ProjectUnit } from '../base/ProjectUnit.js';
5
- import { Phase_IndicesMcpServer, Phase_Install, Phase_PostPublish, Phase_Watch } from '../../phases/definitions/index.js';
5
+ import { Phase_IndicesMcpServer, Phase_Install, Phase_InstallWatch, Phase_PostPublish, Phase_Watch } from '../../phases/definitions/consts.js';
6
6
  /**
7
7
  * Configuration for NodeProject (root project unit).
8
8
  */
@@ -35,7 +35,7 @@ type Unit_TypescriptProject_Config = Unit_PackageJson_Config & {
35
35
  * - Manages global packages installation
36
36
  * - Handles dependency tree for watch compilation
37
37
  */
38
- export declare class Unit_NodeProject<C extends Unit_TypescriptProject_Config = Unit_TypescriptProject_Config> extends Unit_PackageJson<C> implements UnitPhaseImplementor<[Phase_Install, Phase_Watch, Phase_PostPublish, Phase_IndicesMcpServer]> {
38
+ export declare class Unit_NodeProject<C extends Unit_TypescriptProject_Config = Unit_TypescriptProject_Config> extends Unit_PackageJson<C> implements UnitPhaseImplementor<[Phase_Install, Phase_InstallWatch, Phase_Watch, Phase_PostPublish, Phase_IndicesMcpServer]> {
39
39
  private watcher?;
40
40
  readonly innerUnits: Unit_PackageJson[];
41
41
  private readonly suffixesToWatch;
@@ -51,6 +51,7 @@ export declare class Unit_NodeProject<C extends Unit_TypescriptProject_Config =
51
51
  stopWatch(): Promise<void | undefined>;
52
52
  private findUnit;
53
53
  install(): Promise<void>;
54
+ watchInstall(): Promise<void>;
54
55
  watch(timeout?: number, maxTimeout?: number): Promise<void>;
55
56
  purge(): Promise<void>;
56
57
  postPublish(): Promise<void>;
@@ -5,9 +5,9 @@ import { Commando_NVM, Commando_PNPM, CommandoException, PNPM } from '@nu-art/co
5
5
  import { Unit_PackageJson } from './Unit_PackageJson.js';
6
6
  import { resolve } from 'path';
7
7
  import { PhaseManager } from '../../phases/PhaseManager.js';
8
- import { phase_CompileWatch, phase_PrepareWatch } from '../../phases/definitions/index.js';
8
+ import { phase_CompileWatch, phase_InstallWatch, phase_PrepareWatch } from '../../phases/definitions/consts.js';
9
9
  import { UnitsDependencyMapper } from '../../dependencies/UnitsDependencyMapper.js';
10
- import { CONST_PNPM_LOCK, CONST_PNPM_WORKSPACE } from '../../config/consts.js';
10
+ import { CONST_PackageJSONTemplate, CONST_PNPM_LOCK, CONST_PNPM_WORKSPACE } from '../../config/consts.js';
11
11
  import { RunningStatusHandler } from '../../runtime/RunningStatusHandler.js';
12
12
  import { FileSystemUtils } from '@nu-art/ts-common/utils/FileSystemUtils';
13
13
  import { IndicesMcpServer } from '../../exports/IndicesMcpServer.js';
@@ -71,11 +71,13 @@ export class Unit_NodeProject extends Unit_PackageJson {
71
71
  prepareWatchPaths() {
72
72
  // Using phase runner instance to resolve all project libs to watch
73
73
  const projectLibs = this.innerUnits.filter(unit => unit.isInstanceOf(Unit_TypescriptLib) && !unit.config.hasSelfHotReload);
74
- //return all paths to watch
75
74
  return projectLibs.map(lib => {
76
75
  const sourceFolder = `${lib.config.fullPath}/src/main`;
77
76
  return {
78
- paths: this.suffixesToWatch.map(suffix => `${sourceFolder}/**/*.${suffix}`),
77
+ paths: [
78
+ ...this.suffixesToWatch.map(suffix => `${sourceFolder}/**/*.${suffix}`),
79
+ `${lib.config.fullPath}/${CONST_PackageJSONTemplate}`,
80
+ ],
79
81
  unit: lib,
80
82
  fullPath: lib.config.fullPath
81
83
  };
@@ -106,6 +108,18 @@ export class Unit_NodeProject extends Unit_PackageJson {
106
108
  throw new CommandoException(`Error installing packages`, stdout, stderr, exitCode);
107
109
  });
108
110
  }
111
+ async watchInstall() {
112
+ const units = this.innerUnits.filter(unit => unit.isInstanceOf(Unit_TypescriptLib));
113
+ const packages = units.map(unit => unit.config.relativePath);
114
+ if (packages.length > 0)
115
+ await PNPM.createWorkspace(packages, this.config.fullPath);
116
+ const commando = this.allocateCommando(Commando_NVM, Commando_PNPM)
117
+ .cd(this.config.fullPath);
118
+ await this.executeAsyncCommando(commando, 'pnpm i --no-frozen-lockfile', (stdout, stderr, exitCode) => {
119
+ if (exitCode !== 0)
120
+ throw new CommandoException('Error installing packages during watch', stdout, stderr, exitCode);
121
+ });
122
+ }
109
123
  async watch(timeout = 2 * Second, maxTimeout = 10 * Second) {
110
124
  if (this.watcher)
111
125
  throw new BadImplementationException('Watcher already initialized, MUST call stopWatch() before calling watch()');
@@ -121,14 +135,16 @@ export class Unit_NodeProject extends Unit_PackageJson {
121
135
  this.logInfo('Starting the watcher...');
122
136
  const units = new Set();
123
137
  const pathsToDelete = [];
138
+ let needsInstall = false;
124
139
  const onUnitChange = (path) => {
125
140
  const unit = this.findUnit(pathDeclarations, path);
126
141
  const filesToIgnore = unit.ignoreWatchFiles();
127
142
  if (path.match(new RegExp(filesToIgnore.join('|'))))
128
143
  return;
144
+ if (path.endsWith(CONST_PackageJSONTemplate))
145
+ needsInstall = true;
129
146
  // @ts-ignore - FIXME: should be a better way
130
147
  unit.setStatus('Dirty');
131
- //add unit to set
132
148
  units.add(unit);
133
149
  return unit;
134
150
  };
@@ -152,9 +168,11 @@ export class Unit_NodeProject extends Unit_PackageJson {
152
168
  const _pathsToDelete = [...pathsToDelete];
153
169
  const unitsToCompile = Array.from(units.values());
154
170
  let unitDependencyTree = [unitsToCompile];
171
+ const _needsInstall = needsInstall;
155
172
  // clear values in order to start collecting values for next debounce
156
173
  pathsToDelete.length = 0;
157
174
  units.clear();
175
+ needsInstall = false;
158
176
  // fire all delete events
159
177
  await Promise_all_sequentially(_pathsToDelete.map(path => {
160
178
  return async () => path.unit.removeSpecificFileFromDist(path.path);
@@ -188,7 +206,13 @@ export class Unit_NodeProject extends Unit_PackageJson {
188
206
  continue: false
189
207
  };
190
208
  const activeUnitKeys = this.runtimeContext.childUnits.map(unit => unit.config.key);
191
- const phaseManager = new PhaseManager(new RunningStatusHandler(this.config.fullPath, watchRuntimeParams).isolate(), [[phase_PrepareWatch], [phase_CompileWatch]], unitDependencyTree, activeUnitKeys, activeUnitKeys);
209
+ const phases = _needsInstall
210
+ ? [[phase_PrepareWatch], [phase_InstallWatch], [phase_CompileWatch]]
211
+ : [[phase_PrepareWatch], [phase_CompileWatch]];
212
+ if (_needsInstall)
213
+ unitDependencyTree.push([this]);
214
+ const allUnitKeys = _needsInstall ? [...activeUnitKeys, this.config.key] : activeUnitKeys;
215
+ const phaseManager = new PhaseManager(new RunningStatusHandler(this.config.fullPath, watchRuntimeParams).isolate(), phases, unitDependencyTree, allUnitKeys, allUnitKeys);
192
216
  // @ts-ignore
193
217
  phaseManager.setTag('PhaseManager-Watcher');
194
218
  const executionPlan = await phaseManager.calculateExecutionSteps();
@@ -2,7 +2,7 @@ import { InvalidResult, StringMap } from '@nu-art/ts-common';
2
2
  import { UnitPhaseImplementor } from '../../core/types.js';
3
3
  import { Config_ProjectUnit, ProjectUnit } from '../base/ProjectUnit.js';
4
4
  import { TS_PackageJSON } from '../discovery/types.js';
5
- import { Phase_Prepare, Phase_PrepareWatch, Phase_Purge } from '../../phases/definitions/index.js';
5
+ import { Phase_Prepare, Phase_PrepareWatch, Phase_Purge } from '../../phases/definitions/consts.js';
6
6
  /**
7
7
  * Configuration for PackageJson units (units with package.json).
8
8
  */
@@ -32,6 +32,11 @@ export type Unit_PackageJson_Config = Config_ProjectUnit & {
32
32
  */
33
33
  export declare class Unit_PackageJson<C extends Unit_PackageJson_Config = Unit_PackageJson_Config> extends ProjectUnit<C> implements UnitPhaseImplementor<[Phase_Purge, Phase_Prepare, Phase_PrepareWatch]> {
34
34
  configValidationResult?: InvalidResult<any>;
35
+ /**
36
+ * Clones a raw __package.json and replaces '?' dependency values with template
37
+ * placeholders '{{key}}' so template-transform can resolve them later.
38
+ */
39
+ static transformDependencyPlaceholders<T extends TS_PackageJSON>(packageJson: T): T;
35
40
  constructor(config: C);
36
41
  protected npmCommand(command: string): string;
37
42
  protected deriveDistDependencies(): StringMap;
@@ -1,10 +1,11 @@
1
- import { CONST_NodeModules, CONST_PackageJSON } from '../../config/consts.js';
2
- import { __stringify, ValidationException } from '@nu-art/ts-common';
1
+ import { CONST_NodeModules, CONST_PackageJSON, CONST_PackageJSONTemplate } from '../../config/consts.js';
2
+ import { __stringify, _keys, deepClone, ValidationException } from '@nu-art/ts-common';
3
3
  import { ProjectUnit } from '../base/ProjectUnit.js';
4
4
  import { resolve } from 'path';
5
5
  import { existsSync } from 'fs';
6
6
  import { Commando_NVM } from '@nu-art/commando';
7
- import { DEFAULT_OLD_TEMPLATE_PATTERN, FileSystemUtils } from '@nu-art/ts-common/utils/FileSystemUtils';
7
+ import { DEFAULT_TEMPLATE_PATTERN, FileSystemUtils } from '@nu-art/ts-common/utils/FileSystemUtils';
8
+ import { FilesCache } from '../../core/FilesCache.js';
8
9
  /**
9
10
  * Base class for all units that have a package.json file.
10
11
  *
@@ -28,6 +29,26 @@ import { DEFAULT_OLD_TEMPLATE_PATTERN, FileSystemUtils } from '@nu-art/ts-common
28
29
  */
29
30
  export class Unit_PackageJson extends ProjectUnit {
30
31
  configValidationResult;
32
+ /**
33
+ * Clones a raw __package.json and replaces '?' dependency values with template
34
+ * placeholders '{{key}}' so template-transform can resolve them later.
35
+ */
36
+ static transformDependencyPlaceholders(packageJson) {
37
+ const result = deepClone(packageJson);
38
+ const dependencies = result.dependencies;
39
+ if (dependencies)
40
+ result.dependencies = _keys(dependencies).reduce((acc, key) => {
41
+ acc[key] = dependencies[key] === '?' ? `{{${key}}}` : dependencies[key];
42
+ return acc;
43
+ }, {});
44
+ const devDependencies = result.devDependencies;
45
+ if (devDependencies)
46
+ result.devDependencies = _keys(devDependencies).reduce((acc, key) => {
47
+ acc[key] = devDependencies[key] === '?' ? `{{${key}}}` : devDependencies[key];
48
+ return acc;
49
+ }, {});
50
+ return result;
51
+ }
31
52
  constructor(config) {
32
53
  super(config);
33
54
  this.addToClassStack(Unit_PackageJson);
@@ -40,16 +61,15 @@ export class Unit_PackageJson extends ProjectUnit {
40
61
  return resolve(this.runtimeContext.parentUnit.config.fullPath, './node_modules/.bin', command);
41
62
  }
42
63
  deriveDistDependencies() {
64
+ const baseParams = this.runtimeContext.baiConfig.templateParams?.packageJson ?? {};
43
65
  const params = this.runtimeContext.parentUnit.innerUnits.reduce((dependencies, unit) => {
44
- dependencies[unit.config.key] = unit.config.packageJson.version;
66
+ const rawVersion = unit.config.packageJson.version;
67
+ dependencies[unit.config.key] = FileSystemUtils.file.template.transform(rawVersion, baseParams);
45
68
  return dependencies;
46
69
  }, {
47
- ...this.runtimeContext.baiConfig.templateParams?.packageJson,
70
+ ...baseParams,
48
71
  });
49
- return {
50
- ...params,
51
- THUNDERSTORM_DEP_VERSION: this.runtimeContext.baiConfig.templateParams?.packageJson?.['THUNDERSTORM_VERSION'] ?? ''
52
- };
72
+ return params;
53
73
  }
54
74
  deriveLibDependencies() {
55
75
  return this.runtimeContext.childUnits.reduce((dependencies, unit) => {
@@ -74,13 +94,20 @@ export class Unit_PackageJson extends ProjectUnit {
74
94
  await this._sharedPrepare();
75
95
  }
76
96
  async watchPrepare() {
77
- await this._sharedPrepare();
97
+ const pathToFile = resolve(this.config.fullPath, CONST_PackageJSONTemplate);
98
+ FilesCache.invalidate(pathToFile);
99
+ const raw = await FilesCache.load.json(pathToFile);
100
+ const freshPackageJson = Unit_PackageJson.transformDependencyPlaceholders(raw);
101
+ const targetPath = resolve(this.config.fullPath, CONST_PackageJSON);
102
+ const params = this.deriveLibDependencies();
103
+ const content = FileSystemUtils.file.template.transform(__stringify(freshPackageJson, true), params);
104
+ await FileSystemUtils.file.template.write(targetPath, content, params, DEFAULT_TEMPLATE_PATTERN);
78
105
  }
79
106
  async _sharedPrepare() {
80
107
  const targetPath = resolve(this.config.fullPath, CONST_PackageJSON);
81
108
  const params = this.deriveLibDependencies();
82
109
  const packageJson = FileSystemUtils.file.template.transform(__stringify(this.config.packageJson, true), params);
83
- await FileSystemUtils.file.template.write(targetPath, packageJson, params, DEFAULT_OLD_TEMPLATE_PATTERN);
110
+ await FileSystemUtils.file.template.write(targetPath, packageJson, params, DEFAULT_TEMPLATE_PATTERN);
84
111
  }
85
112
  /**
86
113
  * Purges package.json and node_modules folder.
@@ -1,7 +1,7 @@
1
1
  import { RecursivePartial, TypedMap } from '@nu-art/ts-common';
2
2
  import { UnitPhaseImplementor } from '../../core/types.js';
3
3
  import { Unit_PackageJson, Unit_PackageJson_Config } from './Unit_PackageJson.js';
4
- import { Phase_CheckCyclicImports, Phase_Compile, Phase_ExtractDynamicDeps, Phase_Lint, Phase_MapExports, Phase_PreCompile, Phase_PrintDependencyTree, Phase_Publish, Phase_Test, Phase_ToESM } from '../../phases/definitions/index.js';
4
+ import { Phase_CheckCyclicImports, Phase_Compile, Phase_ExtractDynamicDeps, Phase_Lint, Phase_MapExports, Phase_PreCompile, Phase_PrintDependencyTree, Phase_Publish, Phase_Test, Phase_ToESM } from '../../phases/definitions/consts.js';
5
5
  import { TsConfig } from '../base/types.js';
6
6
  export type Unit_TypescriptLib_Config = Unit_PackageJson_Config & {
7
7
  customESLintConfig: boolean;
@@ -7,7 +7,7 @@ import { resolve, resolve as pathResolve } from 'path';
7
7
  import { Unit_PackageJson } from './Unit_PackageJson.js';
8
8
  import { glob } from 'node:fs/promises';
9
9
  import { TestTypes } from '../../core/params.js';
10
- import { DEFAULT_OLD_TEMPLATE_PATTERN, DEFAULT_TEMPLATE_PATTERN, FileSystemUtils } from '@nu-art/ts-common/utils/FileSystemUtils';
10
+ import { DEFAULT_TEMPLATE_PATTERN, FileSystemUtils } from '@nu-art/ts-common/utils/FileSystemUtils';
11
11
  import path from 'node:path';
12
12
  import { ExportMapper } from '../../exports/ExportMapper.js';
13
13
  const assets = [
@@ -156,6 +156,33 @@ export class Unit_TypescriptLib extends Unit_PackageJson {
156
156
  FIREBASE_FIRESTORE_INDICES: resolve(pathToProjectRoot, firebaseConfigFiles.firestoreIndexesRules),
157
157
  }, DEFAULT_TEMPLATE_PATTERN);
158
158
  await this.releasePorts(ports);
159
+ const mongoPort = runtimeContext.baiConfig.files?.tests?.firebase?.mongoPort;
160
+ if (mongoPort) {
161
+ const containerName = `mongo-test-${config.key.replace(/[^a-z0-9-]/gi, '-')}`;
162
+ this.logInfo(`Starting MongoDB test container on port ${mongoPort} (container: ${containerName})`);
163
+ await this.releasePorts([`${mongoPort}`]);
164
+ const commando = this.allocateCommando();
165
+ await this.executeAsyncCommando(commando, `docker rm -f ${containerName} 2>/dev/null; docker run -d --name ${containerName} -p ${mongoPort}:${mongoPort} mongo:7 --replSet rs0 --port ${mongoPort}`, (stdout, stderr, exitCode) => {
166
+ if (exitCode !== 0)
167
+ throw new CommandoException('Failed to start MongoDB test container', stdout, stderr, exitCode);
168
+ });
169
+ const initCommando = this.allocateCommando();
170
+ await this.executeAsyncCommando(initCommando, `sleep 3 && docker exec ${containerName} mongosh --port ${mongoPort} --quiet --eval "rs.initiate({_id:'rs0',members:[{_id:0,host:'localhost:${mongoPort}'}]}); while(!rs.status().members.some(m=>m.stateStr==='PRIMARY')){sleep(200)} print('PRIMARY ready')"`, (stdout, stderr, exitCode) => {
171
+ if (exitCode !== 0)
172
+ throw new CommandoException('Failed to initiate MongoDB test replica set', stdout, stderr, exitCode);
173
+ });
174
+ this.logInfo(`MongoDB test container started as replica set on port ${mongoPort}`);
175
+ this.registerTerminatable(async () => {
176
+ try {
177
+ const stopCommando = this.allocateCommando();
178
+ await this.executeAsyncCommando(stopCommando, `docker rm -f ${containerName}`, () => {
179
+ });
180
+ }
181
+ catch (e) {
182
+ this.logWarning(`Failed to stop MongoDB test container: ${e.message}`);
183
+ }
184
+ });
185
+ }
159
186
  },
160
187
  ui: async () => {
161
188
  throw new NotImplementedYetException('UI tests not implemented yet');
@@ -525,7 +552,7 @@ ${browserNames}
525
552
  const packageJson = FileSystemUtils.file.template.transform(__stringify(this.config.packageJson, true), params);
526
553
  this.logVerbose('Compiling params: ', params);
527
554
  this.logVerbose('Compiling from package.json: ', packageJson);
528
- await FileSystemUtils.file.template.write(targetPath, packageJson, params, DEFAULT_OLD_TEMPLATE_PATTERN);
555
+ await FileSystemUtils.file.template.write(targetPath, packageJson, params, DEFAULT_TEMPLATE_PATTERN);
529
556
  }
530
557
  async purge() {
531
558
  await FileSystemUtils.folder.delete(this.config.output);
@@ -1,7 +1,7 @@
1
1
  import { UnitPhaseImplementor } from '../../../core/types.js';
2
- import { FirebasePackageConfig } from '../../../config/types/index.js';
2
+ import { FirebasePackageConfig } from '../../../config/types/package/package.js';
3
3
  import { StringMap } from '@nu-art/ts-common';
4
- import { Phase_BuildPushImage, Phase_Deploy, Phase_DeployImage, Phase_Launch } from '../../../phases/definitions/index.js';
4
+ import { Phase_BuildPushImage, Phase_Deploy, Phase_DeployImage, Phase_Launch } from '../../../phases/definitions/consts.js';
5
5
  import { Unit_TypescriptLib, Unit_TypescriptLib_Config } from '../Unit_TypescriptLib.js';
6
6
  export declare const firebaseFunctionEmulator_ErrorStrings: string[];
7
7
  export declare const firebaseFunctionEmulator_WarningStrings: string[];
@@ -27,6 +27,10 @@ export type FunctionConfig = {
27
27
  serviceAccountName?: string;
28
28
  resources?: FunctionResourceConfig;
29
29
  };
30
+ export type MongoEmulatorConfig = {
31
+ port?: number;
32
+ dbName?: string;
33
+ };
30
34
  export type Unit_FirebaseFunctionsApp_Config = Unit_TypescriptLib_Config & {
31
35
  firebaseConfig?: FirebasePackageConfig;
32
36
  pathToFirebaseConfig: string;
@@ -39,6 +43,7 @@ export type Unit_FirebaseFunctionsApp_Config = Unit_TypescriptLib_Config & {
39
43
  pathToEmulatorData: string;
40
44
  sources?: string[];
41
45
  functions: string[] | FunctionConfig[];
46
+ mongo?: MongoEmulatorConfig;
42
47
  containerDeployment?: {
43
48
  artifactRegistry: {
44
49
  region: string;
@@ -98,6 +103,11 @@ export declare class Unit_FirebaseFunctionsApp<C extends Unit_FirebaseFunctionsA
98
103
  postCompile(): Promise<void>;
99
104
  launch(): Promise<void>;
100
105
  releaseEmulatorPorts(): Promise<void>;
106
+ private resolveMongoPort;
107
+ private resolveMongoContainerName;
108
+ private resolveMongoDataPath;
109
+ private startMongoEmulator;
110
+ private stopMongoEmulator;
101
111
  deploy(): Promise<void>;
102
112
  /**
103
113
  * Builds Docker container image using Google Cloud Build and pushes it to Artifact Registry.
@@ -3,7 +3,7 @@ import { __stringify, _keys, _logger_logPrefixes, deepClone, ImplementationMissi
3
3
  import { Const_FirebaseConfigKeys, Const_FirebaseDefaultsKeyToFile, Default_Files, FunctionBuildTemplateFiles } from '../../../templates/consts.js';
4
4
  import { Commando_NVM, CommandoException } from '@nu-art/commando';
5
5
  import { resolve } from 'path';
6
- import { DEFAULT_OLD_TEMPLATE_PATTERN, FileSystemUtils } from '@nu-art/ts-common/utils/FileSystemUtils';
6
+ import { DEFAULT_TEMPLATE_PATTERN, FileSystemUtils } from '@nu-art/ts-common/utils/FileSystemUtils';
7
7
  import { Unit_TypescriptLib } from '../Unit_TypescriptLib.js';
8
8
  import { deployLogFilter, ensureArtifactRegistryRepository } from './common.js';
9
9
  export const firebaseFunctionEmulator_ErrorStrings = [
@@ -85,7 +85,7 @@ export class Unit_FirebaseFunctionsApp extends Unit_TypescriptLib {
85
85
  return dependencies;
86
86
  }, dependencies);
87
87
  packageJson.dependencies = dependencies;
88
- await FileSystemUtils.file.template.write(targetPath, __stringify(packageJson, true), this.deriveDistDependencies(), DEFAULT_OLD_TEMPLATE_PATTERN);
88
+ await FileSystemUtils.file.template.write(targetPath, __stringify(packageJson, true), this.deriveDistDependencies(), DEFAULT_TEMPLATE_PATTERN);
89
89
  }
90
90
  async prepare() {
91
91
  await super.prepare();
@@ -113,6 +113,8 @@ export class Unit_FirebaseFunctionsApp extends Unit_TypescriptLib {
113
113
  async launch() {
114
114
  await sleep(2 * Second * Unit_FirebaseFunctionsApp.staggerCount++);
115
115
  await this.releaseEmulatorPorts();
116
+ if (this.config.mongo)
117
+ await this.startMongoEmulator();
116
118
  let emulatorPromise;
117
119
  const proxyPromise = this.runProxy(() => {
118
120
  emulatorPromise = this.runEmulator();
@@ -123,8 +125,51 @@ export class Unit_FirebaseFunctionsApp extends Unit_TypescriptLib {
123
125
  }
124
126
  async releaseEmulatorPorts() {
125
127
  const allPorts = Array.from({ length: 11 }, (_, i) => `${this.config.basePort + i}`);
128
+ if (this.config.mongo)
129
+ allPorts.push(`${this.resolveMongoPort()}`);
126
130
  return this.releasePorts(allPorts);
127
131
  }
132
+ //######################### MongoDB Emulator #########################
133
+ resolveMongoPort() {
134
+ return this.config.mongo?.port ?? this.config.basePort + 11;
135
+ }
136
+ resolveMongoContainerName() {
137
+ return `mongo-emu-${this.config.key.replace(/[^a-z0-9-]/gi, '-')}`;
138
+ }
139
+ resolveMongoDataPath() {
140
+ return resolve(this.config.fullPath, CONST_TrashDir, 'mongo-data');
141
+ }
142
+ async startMongoEmulator() {
143
+ const port = this.resolveMongoPort();
144
+ const containerName = this.resolveMongoContainerName();
145
+ const mongoDataPath = this.resolveMongoDataPath();
146
+ await FileSystemUtils.folder.create(mongoDataPath);
147
+ this.logInfo(`Starting MongoDB emulator on port ${port} (container: ${containerName}, data: ${mongoDataPath})`);
148
+ const stopAction = async () => this.stopMongoEmulator();
149
+ this.registerTerminatable(stopAction);
150
+ const commando = this.allocateCommando();
151
+ await this.executeAsyncCommando(commando, `docker rm -f ${containerName} 2>/dev/null; docker run -d --name ${containerName} -p ${port}:${port} -v ${mongoDataPath}:/data/db mongo:7 --replSet rs0 --port ${port}`, (stdout, stderr, exitCode) => {
152
+ if (exitCode !== 0)
153
+ throw new CommandoException(`Failed to start MongoDB emulator container`, stdout, stderr, exitCode);
154
+ });
155
+ const initCommando = this.allocateCommando();
156
+ await this.executeAsyncCommando(initCommando, `sleep 3 && docker exec ${containerName} mongosh --port ${port} --quiet --eval "try{rs.status()}catch(e){rs.initiate({_id:'rs0',members:[{_id:0,host:'localhost:${port}'}]})} while(!rs.status().members.some(m=>m.stateStr==='PRIMARY')){sleep(200)} print('PRIMARY ready')"`, (stdout, stderr, exitCode) => {
157
+ if (exitCode !== 0)
158
+ throw new CommandoException(`Failed to initiate MongoDB replica set`, stdout, stderr, exitCode);
159
+ });
160
+ this.logInfo(`MongoDB emulator started as replica set on port ${port}`);
161
+ }
162
+ async stopMongoEmulator() {
163
+ const containerName = this.resolveMongoContainerName();
164
+ this.logInfo(`Stopping MongoDB emulator (container: ${containerName})`);
165
+ try {
166
+ const commando = this.allocateCommando();
167
+ await this.executeAsyncCommando(commando, `docker rm -f ${containerName}`, () => { });
168
+ }
169
+ catch (e) {
170
+ this.logWarning(`Failed to stop MongoDB emulator container: ${e.message}`);
171
+ }
172
+ }
128
173
  async deploy() {
129
174
  const commando = this.allocateCommando(Commando_NVM).applyNVM()
130
175
  .cd(this.config.output)
@@ -806,6 +851,8 @@ export class Unit_FirebaseFunctionsApp extends Unit_TypescriptLib {
806
851
  return LogLevel.Warning;
807
852
  })
808
853
  .onLog(/.*Emulator Hub running.*/, () => this.setStatus('Launch Complete'));
854
+ if (this.config.mongo)
855
+ commando.custom(`export MONGODB_EMULATOR_HOST=localhost:${this.resolveMongoPort()}`);
809
856
  await this.executeAsyncCommando(commando, `${this.npmCommand('firebase')} emulators:start --project ${this.config.envConfig.projectId} --export-on-exit --import=${this.config.pathToEmulatorData} ${this.runtimeContext.runtimeParams.debugBackend
810
857
  ? `--inspect-functions ${this.config.debugPort}` : ''}`);
811
858
  this.logWarning('EMULATORS TERMINATED');
@@ -1,7 +1,7 @@
1
- import { FirebasePackageConfig } from '../../../config/types/index.js';
1
+ import { FirebasePackageConfig } from '../../../config/types/package/package.js';
2
2
  import { UnitPhaseImplementor } from '../../../core/types.js';
3
3
  import { StringMap, TS_Object } from '@nu-art/ts-common';
4
- import { Phase_BuildPushImage, Phase_Deploy, Phase_DeployImage, Phase_Launch } from '../../../phases/definitions/index.js';
4
+ import { Phase_BuildPushImage, Phase_Deploy, Phase_DeployImage, Phase_Launch } from '../../../phases/definitions/consts.js';
5
5
  import { Unit_TypescriptLib, Unit_TypescriptLib_Config } from '../Unit_TypescriptLib.js';
6
6
  export type FirebaseHostingConfig = {
7
7
  public: string;
@@ -2,7 +2,9 @@ import { AnyConstructor, Logger } from '@nu-art/ts-common';
2
2
  import { BaiParams } from '../core/params.js';
3
3
  import { UnitsMapper } from '../units/discovery/UnitsMapper.js';
4
4
  import { UnitsDependencyMapper } from '../dependencies/UnitsDependencyMapper.js';
5
- import { BaseUnit, ProjectUnit, Unit_NodeProject } from '../units/index.js';
5
+ import { BaseUnit } from '../units/base/BaseUnit.js';
6
+ import { ProjectUnit } from '../units/base/ProjectUnit.js';
7
+ import { Unit_NodeProject } from '../units/implementations/Unit_NodeProject.js';
6
8
  /**
7
9
  * Central workspace manager for all units in the build system.
8
10
  *
@@ -1,6 +1,7 @@
1
1
  import { _keys, arrayToMap, BadImplementationException, flatArray, ImplementationMissingException, Logger } from '@nu-art/ts-common';
2
2
  import { UnitsDependencyMapper } from '../dependencies/UnitsDependencyMapper.js';
3
- import { ProjectUnit, Unit_NodeProject } from '../units/index.js';
3
+ import { ProjectUnit } from '../units/base/ProjectUnit.js';
4
+ import { Unit_NodeProject } from '../units/implementations/Unit_NodeProject.js';
4
5
  /**
5
6
  * Central workspace manager for all units in the build system.
6
7
  *
@@ -1,3 +0,0 @@
1
- export * from './firebasejson.js';
2
- export * from './firebaserc.js';
3
- export * from './package-json.js';
@@ -1,3 +0,0 @@
1
- export * from './firebasejson.js';
2
- export * from './firebaserc.js';
3
- export * from './package-json.js';
@@ -1,4 +0,0 @@
1
- export * from './core.js';
2
- export * from './configs/index.js';
3
- export * from './package/index.js';
4
- export * from './project-config.js';
@@ -1,4 +0,0 @@
1
- export * from './core.js';
2
- export * from './configs/index.js';
3
- export * from './package/index.js';
4
- export * from './project-config.js';
@@ -1,2 +0,0 @@
1
- export * from './package.js';
2
- export * from './runtime-package.js';
@@ -1,2 +0,0 @@
1
- export * from './package.js';
2
- export * from './runtime-package.js';
@@ -1,2 +0,0 @@
1
- export * from './types.js';
2
- export * from './consts.js';
@@ -1,2 +0,0 @@
1
- export * from './types.js';
2
- export * from './consts.js';
package/phases/index.d.ts DELETED
@@ -1,2 +0,0 @@
1
- export * from './definitions/index.js';
2
- export * from './PhaseManager.js';
package/phases/index.js DELETED
@@ -1,2 +0,0 @@
1
- export * from './definitions/index.js';
2
- export * from './PhaseManager.js';
@@ -1,5 +0,0 @@
1
- export * from './UnitMapper_NodeLib.js';
2
- export * from './UnitMapper_NodeProject.js';
3
- export * from './UnitMapper_FirebaseFunction.js';
4
- export * from './UnitMapper_FirebaseHosting.js';
5
- export * from './UnitMapper_ViteHosting.js';
@@ -1,5 +0,0 @@
1
- export * from './UnitMapper_NodeLib.js';
2
- export * from './UnitMapper_NodeProject.js';
3
- export * from './UnitMapper_FirebaseFunction.js';
4
- export * from './UnitMapper_FirebaseHosting.js';
5
- export * from './UnitMapper_ViteHosting.js';
package/units/index.d.ts DELETED
@@ -1,6 +0,0 @@
1
- export * from './base/BaseUnit.js';
2
- export * from './base/ProjectUnit.js';
3
- export * from './implementations/Unit_TypescriptLib.js';
4
- export * from './implementations/Unit_PackageJson.js';
5
- export * from './implementations/Unit_NodeProject.js';
6
- export * from './base/types.js';
package/units/index.js DELETED
@@ -1,6 +0,0 @@
1
- export * from './base/BaseUnit.js';
2
- export * from './base/ProjectUnit.js';
3
- export * from './implementations/Unit_TypescriptLib.js';
4
- export * from './implementations/Unit_PackageJson.js';
5
- export * from './implementations/Unit_NodeProject.js';
6
- export * from './base/types.js';