@nu-art/build-and-install 0.401.9 → 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 (67) hide show
  1. package/BuildAndInstall.d.ts +2 -2
  2. package/BuildAndInstall.js +10 -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 +17 -2
  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 +12 -3
  14. package/core/types.d.ts +2 -2
  15. package/dependencies/UnitsDependencyMapper.d.ts +8 -0
  16. package/dependencies/UnitsDependencyMapper.js +58 -4
  17. package/exceptions/UnitPhaseException.d.ts +1 -1
  18. package/exports/IndicesMcpServer.js +1 -1
  19. package/package.json +5 -5
  20. package/phases/PhaseManager.d.ts +2 -2
  21. package/phases/PhaseManager.js +4 -4
  22. package/phases/definitions/consts.d.ts +3 -0
  23. package/phases/definitions/consts.js +7 -0
  24. package/phases/definitions/types.d.ts +1 -1
  25. package/templates/backend/proxy/proxy._ts +36 -31
  26. package/templates/firebase/config/database.rules.json +6 -0
  27. package/units/base/BaseUnit.d.ts +1 -1
  28. package/units/discovery/UnitsMapper.d.ts +3 -2
  29. package/units/discovery/resolvers/UnitMapper_Base.d.ts +2 -2
  30. package/units/discovery/resolvers/UnitMapper_FirebaseFunction.d.ts +12 -1
  31. package/units/discovery/resolvers/UnitMapper_FirebaseFunction.js +48 -18
  32. package/units/discovery/resolvers/UnitMapper_FirebaseHosting.d.ts +5 -9
  33. package/units/discovery/resolvers/UnitMapper_FirebaseHosting.js +3 -1
  34. package/units/discovery/resolvers/UnitMapper_Node.d.ts +1 -1
  35. package/units/discovery/resolvers/UnitMapper_Node.js +8 -15
  36. package/units/discovery/resolvers/UnitMapper_ViteHosting.d.ts +46 -0
  37. package/units/discovery/resolvers/UnitMapper_ViteHosting.js +59 -0
  38. package/units/implementations/Unit_NodeProject.d.ts +3 -2
  39. package/units/implementations/Unit_NodeProject.js +30 -6
  40. package/units/implementations/Unit_PackageJson.d.ts +8 -2
  41. package/units/implementations/Unit_PackageJson.js +46 -11
  42. package/units/implementations/Unit_TypescriptLib.d.ts +8 -2
  43. package/units/implementations/Unit_TypescriptLib.js +155 -4
  44. package/units/implementations/firebase/Unit_FirebaseFunctionsApp.d.ts +12 -2
  45. package/units/implementations/firebase/Unit_FirebaseFunctionsApp.js +73 -17
  46. package/units/implementations/firebase/Unit_FirebaseHostingApp.d.ts +19 -97
  47. package/units/implementations/firebase/Unit_FirebaseHostingApp.js +28 -290
  48. package/units/implementations/firebase/Unit_HostingApp.d.ts +59 -0
  49. package/units/implementations/firebase/Unit_HostingApp.js +225 -0
  50. package/units/implementations/firebase/Unit_ViteHostingApp.d.ts +10 -0
  51. package/units/implementations/firebase/Unit_ViteHostingApp.js +28 -0
  52. package/workspace/Workspace.d.ts +3 -1
  53. package/workspace/Workspace.js +2 -1
  54. package/config/types/configs/index.d.ts +0 -3
  55. package/config/types/configs/index.js +0 -3
  56. package/config/types/index.d.ts +0 -4
  57. package/config/types/index.js +0 -4
  58. package/config/types/package/index.d.ts +0 -2
  59. package/config/types/package/index.js +0 -2
  60. package/phases/definitions/index.d.ts +0 -2
  61. package/phases/definitions/index.js +0 -2
  62. package/phases/index.d.ts +0 -2
  63. package/phases/index.js +0 -2
  64. package/units/discovery/resolvers/index.d.ts +0 -4
  65. package/units/discovery/resolvers/index.js +0 -4
  66. package/units/index.d.ts +0 -6
  67. package/units/index.js +0 -6
@@ -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 { TypedMap } from '@nu-art/ts-common';
2
2
  import { UnitMapper_Node, UnitMapper_NodeContext } from './UnitMapper_Node.js';
3
- import { FirebaseHosting_EnvConfig, Unit_FirebaseHostingApp, UnitConfigJSON_FirebaseHosting } from '../../implementations/firebase/Unit_FirebaseHostingApp.js';
3
+ import { Unit_FirebaseHostingApp, UnitConfigJSON_FirebaseHosting, WebpackHosting_EnvConfig } from '../../implementations/firebase/Unit_FirebaseHostingApp.js';
4
4
  export declare class UnitMapper_FirebaseHosting_Class extends UnitMapper_Node<Unit_FirebaseHostingApp, UnitConfigJSON_FirebaseHosting> {
5
5
  static tsValidator_FirebaseHosting: {
6
6
  label: import("@nu-art/ts-common").Validator<string>;
@@ -9,17 +9,13 @@ export declare class UnitMapper_FirebaseHosting_Class extends UnitMapper_Node<Un
9
9
  hasSelfHotReload: import("@nu-art/ts-common").Validator<boolean>;
10
10
  type: import("@nu-art/ts-common").Validator<any>;
11
11
  servingPort: import("@nu-art/ts-common").Validator<number>;
12
- envs: (import("@nu-art/ts-common").ValidatorImpl<any> | ((input?: TypedMap<FirebaseHosting_EnvConfig> | undefined) => import("@nu-art/ts-common").InvalidResultObject<TypedMap<FirebaseHosting_EnvConfig>> | undefined))[];
12
+ envs: (import("@nu-art/ts-common").ValidatorImpl<any> | ((input?: TypedMap<WebpackHosting_EnvConfig> | undefined) => import("@nu-art/ts-common").InvalidResultObject<TypedMap<WebpackHosting_EnvConfig>> | undefined))[];
13
13
  hostingConfig: import("@nu-art/ts-common").ValidatorImpl<any>;
14
14
  hostingDeployment: (import("@nu-art/ts-common").ValidatorImpl<any> | ((input?: object | undefined) => string | object | undefined))[];
15
15
  };
16
16
  constructor();
17
17
  protected resolveNodeUnit(context: UnitMapper_NodeContext<UnitConfigJSON_FirebaseHosting>): Promise<Unit_FirebaseHostingApp<{
18
- envConfig: {
19
- config: import("@nu-art/ts-common").TS_Object;
20
- projectId: string;
21
- isLocal: boolean;
22
- };
18
+ envConfig: WebpackHosting_EnvConfig;
23
19
  isTopLevelApp: true;
24
20
  hasSelfHotReload: boolean;
25
21
  customESLintConfig: boolean;
@@ -28,8 +24,8 @@ export declare class UnitMapper_FirebaseHosting_Class extends UnitMapper_Node<Un
28
24
  packageJson: import("../types.js").TS_PackageJSON<UnitConfigJSON_FirebaseHosting>;
29
25
  label: string;
30
26
  servingPort: number;
31
- hostingConfig?: import("../../implementations/firebase/Unit_FirebaseHostingApp.js").FirebaseHostingConfig | undefined;
32
- envs: TypedMap<FirebaseHosting_EnvConfig>;
27
+ hostingConfig?: import("../../implementations/firebase/Unit_HostingApp.js").FirebaseHostingConfig | undefined;
28
+ envs: TypedMap<WebpackHosting_EnvConfig>;
33
29
  fullPath: import("@nu-art/ts-common").AbsolutePath;
34
30
  relativePath: import("@nu-art/ts-common").RelativePath;
35
31
  key: string;
@@ -7,6 +7,7 @@ const valuesValidator = {
7
7
  config: tsValidateMustExist,
8
8
  projectId: tsValidateAnyString,
9
9
  isLocal: tsValidateBoolean(false),
10
+ webpackConfig: tsValidateOptional
10
11
  };
11
12
  // Artifact Registry generic package name validation: lowercase, alphanumeric with dots, underscores, hyphens
12
13
  // Cannot start/end with separators, no consecutive separators
@@ -41,7 +42,8 @@ export class UnitMapper_FirebaseHosting_Class extends UnitMapper_Node {
41
42
  const envConfig = {
42
43
  config: envUnitConfig?.config,
43
44
  projectId: envUnitConfig?.projectId,
44
- isLocal: envUnitConfig?.isLocal ?? env === 'local'
45
+ isLocal: envUnitConfig?.isLocal ?? env === 'local',
46
+ webpackConfig: envUnitConfig?.webpackConfig,
45
47
  };
46
48
  const { type, ...unitConfig } = context.packageJson.unitConfig;
47
49
  return new Unit_FirebaseHostingApp({
@@ -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,7 +1,8 @@
1
- import { _keys, deepClone, tsValidate, 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';
5
+ import { Unit_PackageJson } from '../../implementations/Unit_PackageJson.js';
5
6
  export class UnitMapper_Node extends UnitMapper_Base {
6
7
  static invalidPaths = [];
7
8
  static tsValidator_Node = {
@@ -31,20 +32,9 @@ export class UnitMapper_Node extends UnitMapper_Base {
31
32
  }
32
33
  if (tsValidateResult(packageJson.unitConfig.type, this.validator.type))
33
34
  return; // not the expected type for this mapper
34
- packageJson = deepClone(packageJson);
35
- tsValidate(packageJson.unitConfig, this.validator);
35
+ packageJson = Unit_PackageJson.transformDependencyPlaceholders(packageJson);
36
+ const configValidationResult = tsValidateResult(packageJson.unitConfig, this.validator, undefined, false);
36
37
  const dependencies = packageJson.dependencies;
37
- if (dependencies)
38
- packageJson.dependencies = _keys(dependencies).reduce((acc, key) => {
39
- acc[key] = dependencies[key] === '?' ? `{{${key}}}` : dependencies[key];
40
- return acc;
41
- }, {});
42
- const devDependencies = packageJson.devDependencies;
43
- if (devDependencies)
44
- packageJson.devDependencies = _keys(devDependencies).reduce((acc, key) => {
45
- acc[key] = devDependencies[key] === '?' ? `{{${key}}}` : devDependencies[key];
46
- return acc;
47
- }, {});
48
38
  Object.freeze(packageJson);
49
39
  const baseConfig = {
50
40
  key: packageJson.name,
@@ -55,7 +45,10 @@ export class UnitMapper_Node extends UnitMapper_Base {
55
45
  };
56
46
  const customESLintConfig = packageJson.unitConfig.customESLintConfig ?? false;
57
47
  const customTSConfig = packageJson.unitConfig.customTSConfig ?? false;
58
- return this.resolveNodeUnit({ path, root, packageJson, baseConfig, customESLintConfig, customTSConfig });
48
+ const unit = await this.resolveNodeUnit({ path, root, packageJson, baseConfig, customESLintConfig, customTSConfig });
49
+ if (unit instanceof Unit_PackageJson)
50
+ unit.configValidationResult = configValidationResult;
51
+ return unit;
59
52
  }
60
53
  catch (e) {
61
54
  this.logError(`Failed to load package.json at: ${pathToFile}`, e);
@@ -0,0 +1,46 @@
1
+ import { TypedMap } from '@nu-art/ts-common';
2
+ import { UnitMapper_Node, UnitMapper_NodeContext } from './UnitMapper_Node.js';
3
+ import { FirebaseHosting_EnvConfig } from '../../implementations/firebase/Unit_HostingApp.js';
4
+ import { Unit_ViteHostingApp } from '../../implementations/firebase/Unit_ViteHostingApp.js';
5
+ type UnitConfigJSON_ViteHosting_Node = import('../../discovery/resolvers/UnitMapper_Node.js').UnitConfigJSON_Node & {
6
+ servingPort?: number;
7
+ hostingConfig?: import('../../implementations/firebase/Unit_HostingApp.js').FirebaseHostingConfig;
8
+ envs: TypedMap<FirebaseHosting_EnvConfig>;
9
+ };
10
+ export declare class UnitMapper_ViteHosting_Class extends UnitMapper_Node<Unit_ViteHostingApp, UnitConfigJSON_ViteHosting_Node> {
11
+ static tsValidator_ViteHosting: {
12
+ label: import("@nu-art/ts-common").Validator<string>;
13
+ customESLintConfig: import("@nu-art/ts-common").Validator<boolean>;
14
+ customTSConfig: import("@nu-art/ts-common").Validator<boolean>;
15
+ hasSelfHotReload: import("@nu-art/ts-common").Validator<boolean>;
16
+ type: import("@nu-art/ts-common").Validator<any>;
17
+ servingPort: import("@nu-art/ts-common").Validator<number>;
18
+ envs: (import("@nu-art/ts-common").ValidatorImpl<any> | ((input?: TypedMap<FirebaseHosting_EnvConfig> | undefined) => import("@nu-art/ts-common").InvalidResultObject<TypedMap<FirebaseHosting_EnvConfig>> | undefined))[];
19
+ hostingConfig: import("@nu-art/ts-common").ValidatorImpl<any>;
20
+ hostingDeployment: (import("@nu-art/ts-common").ValidatorImpl<any> | ((input?: object | undefined) => string | object | undefined))[];
21
+ };
22
+ constructor();
23
+ protected resolveNodeUnit(context: UnitMapper_NodeContext<UnitConfigJSON_ViteHosting_Node>): Promise<Unit_ViteHostingApp<{
24
+ envConfig: {
25
+ config: import("@nu-art/ts-common").TS_Object;
26
+ projectId: string;
27
+ isLocal: boolean;
28
+ };
29
+ isTopLevelApp: true;
30
+ hasSelfHotReload: boolean;
31
+ customESLintConfig: boolean;
32
+ customTSConfig: boolean;
33
+ output: string;
34
+ packageJson: import("../types.js").TS_PackageJSON<UnitConfigJSON_ViteHosting_Node>;
35
+ label: string;
36
+ servingPort: number;
37
+ hostingConfig?: import("../../implementations/firebase/Unit_HostingApp.js").FirebaseHostingConfig | undefined;
38
+ envs: TypedMap<FirebaseHosting_EnvConfig>;
39
+ fullPath: import("@nu-art/ts-common").AbsolutePath;
40
+ relativePath: import("@nu-art/ts-common").RelativePath;
41
+ key: string;
42
+ dependencies: import("@nu-art/ts-common").StringMap;
43
+ }>>;
44
+ }
45
+ export declare const UnitMapper_ViteHosting: UnitMapper_ViteHosting_Class;
46
+ export {};
@@ -0,0 +1,59 @@
1
+ import { tsValidateAnyString, tsValidateBoolean, tsValidateDynamicObject, tsValidateMustExist, tsValidateOptional, tsValidateOptionalAnyNumber, tsValidateOptionalObject, tsValidateRegexp, tsValidateValue } from '@nu-art/ts-common';
2
+ import { UnitMapper_Node } from './UnitMapper_Node.js';
3
+ import { Unit_HostingApp } from '../../implementations/firebase/Unit_HostingApp.js';
4
+ import { Unit_ViteHostingApp } from '../../implementations/firebase/Unit_ViteHostingApp.js';
5
+ import { resolve } from 'path';
6
+ import { BaiParam_SetEnv } from '../../../core/params.js';
7
+ const valuesValidator = {
8
+ config: tsValidateMustExist,
9
+ projectId: tsValidateAnyString,
10
+ isLocal: tsValidateBoolean(false),
11
+ };
12
+ const packageNameRegex = /^[a-z0-9]+([._-][a-z0-9]+)*$/;
13
+ const hostingDeploymentValidator = {
14
+ artifactRegistry: {
15
+ region: tsValidateAnyString,
16
+ repository: tsValidateAnyString,
17
+ projectId: tsValidateAnyString,
18
+ },
19
+ packageName: tsValidateRegexp(packageNameRegex, true),
20
+ };
21
+ export class UnitMapper_ViteHosting_Class extends UnitMapper_Node {
22
+ static tsValidator_ViteHosting = {
23
+ type: tsValidateValue(['vite-hosting']),
24
+ servingPort: tsValidateOptionalAnyNumber,
25
+ envs: tsValidateDynamicObject(valuesValidator, tsValidateAnyString),
26
+ hostingConfig: tsValidateOptional,
27
+ hostingDeployment: tsValidateOptionalObject(hostingDeploymentValidator),
28
+ ...UnitMapper_Node.tsValidator_Node,
29
+ };
30
+ constructor() {
31
+ super(UnitMapper_ViteHosting_Class.tsValidator_ViteHosting);
32
+ }
33
+ async resolveNodeUnit(context) {
34
+ const outputDir = context.packageJson.publishConfig?.directory;
35
+ const env = this.runtimeParams[BaiParam_SetEnv.keyName];
36
+ const envUnitConfig = context.packageJson.unitConfig.envs[env];
37
+ if (!envUnitConfig)
38
+ this.logWarning('Package Json config:', context.packageJson.unitConfig);
39
+ const envConfig = {
40
+ config: envUnitConfig?.config,
41
+ projectId: envUnitConfig?.projectId,
42
+ isLocal: envUnitConfig?.isLocal ?? env === 'local'
43
+ };
44
+ const { type, ...unitConfig } = context.packageJson.unitConfig;
45
+ return new Unit_ViteHostingApp({
46
+ ...context.baseConfig,
47
+ ...Unit_HostingApp.DefaultConfig_Hosting,
48
+ ...unitConfig,
49
+ envConfig,
50
+ isTopLevelApp: true,
51
+ hasSelfHotReload: unitConfig.hasSelfHotReload ?? false,
52
+ customESLintConfig: context.customESLintConfig,
53
+ customTSConfig: context.customTSConfig,
54
+ output: resolve(context.baseConfig.fullPath, outputDir ?? Unit_HostingApp.DefaultConfig_Hosting.output),
55
+ packageJson: context.packageJson,
56
+ });
57
+ }
58
+ }
59
+ export const UnitMapper_ViteHosting = new UnitMapper_ViteHosting_Class();
@@ -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();
@@ -1,8 +1,8 @@
1
- import { StringMap } from '@nu-art/ts-common';
1
+ 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
  */
@@ -31,6 +31,12 @@ export type Unit_PackageJson_Config = Config_ProjectUnit & {
31
31
  * **Base For**: Unit_NodeProject, Unit_TypescriptLib, Unit_FirebaseHosting, etc.
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
+ 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;
34
40
  constructor(config: C);
35
41
  protected npmCommand(command: string): string;
36
42
  protected deriveDistDependencies(): StringMap;
@@ -1,9 +1,11 @@
1
- import { CONST_NodeModules, CONST_PackageJSON } from '../../config/consts.js';
2
- import { __stringify } 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
+ import { existsSync } from 'fs';
5
6
  import { Commando_NVM } from '@nu-art/commando';
6
- 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';
7
9
  /**
8
10
  * Base class for all units that have a package.json file.
9
11
  *
@@ -26,25 +28,48 @@ import { DEFAULT_OLD_TEMPLATE_PATTERN, FileSystemUtils } from '@nu-art/ts-common
26
28
  * **Base For**: Unit_NodeProject, Unit_TypescriptLib, Unit_FirebaseHosting, etc.
27
29
  */
28
30
  export class Unit_PackageJson extends ProjectUnit {
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
+ }
29
52
  constructor(config) {
30
53
  super(config);
31
54
  this.addToClassStack(Unit_PackageJson);
32
55
  }
33
56
  //######################### Internal Logic #########################
34
57
  npmCommand(command) {
58
+ const packageBin = resolve(this.config.fullPath, './node_modules/.bin', command);
59
+ if (existsSync(packageBin))
60
+ return packageBin;
35
61
  return resolve(this.runtimeContext.parentUnit.config.fullPath, './node_modules/.bin', command);
36
62
  }
37
63
  deriveDistDependencies() {
64
+ const baseParams = this.runtimeContext.baiConfig.templateParams?.packageJson ?? {};
38
65
  const params = this.runtimeContext.parentUnit.innerUnits.reduce((dependencies, unit) => {
39
- 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);
40
68
  return dependencies;
41
69
  }, {
42
- ...this.runtimeContext.baiConfig.templateParams?.packageJson,
70
+ ...baseParams,
43
71
  });
44
- return {
45
- ...params,
46
- THUNDERSTORM_DEP_VERSION: this.runtimeContext.baiConfig.templateParams?.packageJson?.['THUNDERSTORM_VERSION'] ?? ''
47
- };
72
+ return params;
48
73
  }
49
74
  deriveLibDependencies() {
50
75
  return this.runtimeContext.childUnits.reduce((dependencies, unit) => {
@@ -64,16 +89,25 @@ export class Unit_PackageJson extends ProjectUnit {
64
89
  * **Template Params**: Includes THUNDERSTORM_VERSION, __ENV__, and child unit versions.
65
90
  */
66
91
  async prepare() {
92
+ if (this.configValidationResult)
93
+ throw new ValidationException(`Invalid unit config for '${this.config.key}'`, undefined, this.configValidationResult);
67
94
  await this._sharedPrepare();
68
95
  }
69
96
  async watchPrepare() {
70
- 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);
71
105
  }
72
106
  async _sharedPrepare() {
73
107
  const targetPath = resolve(this.config.fullPath, CONST_PackageJSON);
74
108
  const params = this.deriveLibDependencies();
75
109
  const packageJson = FileSystemUtils.file.template.transform(__stringify(this.config.packageJson, true), params);
76
- await FileSystemUtils.file.template.write(targetPath, packageJson, params, DEFAULT_OLD_TEMPLATE_PATTERN);
110
+ await FileSystemUtils.file.template.write(targetPath, packageJson, params, DEFAULT_TEMPLATE_PATTERN);
77
111
  }
78
112
  /**
79
113
  * Purges package.json and node_modules folder.
@@ -86,6 +120,7 @@ export class Unit_PackageJson extends ProjectUnit {
86
120
  }
87
121
  async releasePorts(allPorts) {
88
122
  const commando = this.allocateCommando(Commando_NVM).applyNVM();
123
+ this.logInfo('Releasing ports: ', allPorts);
89
124
  await commando.setUID(this.config.key)
90
125
  .append(`array=($(lsof -ti:${allPorts.join(',')}))`)
91
126
  .append(`((\${#array[@]} > 0)) && kill -9 "\${array[@]}"`)