@hyperfrontend/versioning 0.1.0 → 0.2.0

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 (158) hide show
  1. package/ARCHITECTURE.md +50 -1
  2. package/CHANGELOG.md +23 -23
  3. package/README.md +12 -9
  4. package/changelog/index.cjs.js +23 -2
  5. package/changelog/index.cjs.js.map +1 -1
  6. package/changelog/index.esm.js +23 -2
  7. package/changelog/index.esm.js.map +1 -1
  8. package/changelog/models/entry.d.ts +5 -0
  9. package/changelog/models/entry.d.ts.map +1 -1
  10. package/changelog/models/index.cjs.js +2 -0
  11. package/changelog/models/index.cjs.js.map +1 -1
  12. package/changelog/models/index.esm.js +2 -0
  13. package/changelog/models/index.esm.js.map +1 -1
  14. package/changelog/operations/index.cjs.js.map +1 -1
  15. package/changelog/operations/index.esm.js.map +1 -1
  16. package/changelog/parse/index.cjs.js +23 -2
  17. package/changelog/parse/index.cjs.js.map +1 -1
  18. package/changelog/parse/index.esm.js +23 -2
  19. package/changelog/parse/index.esm.js.map +1 -1
  20. package/changelog/parse/line.d.ts.map +1 -1
  21. package/commits/classify/classifier.d.ts +73 -0
  22. package/commits/classify/classifier.d.ts.map +1 -0
  23. package/commits/classify/index.cjs.js +705 -0
  24. package/commits/classify/index.cjs.js.map +1 -0
  25. package/commits/classify/index.d.ts +8 -0
  26. package/commits/classify/index.d.ts.map +1 -0
  27. package/commits/classify/index.esm.js +678 -0
  28. package/commits/classify/index.esm.js.map +1 -0
  29. package/commits/classify/infrastructure.d.ts +205 -0
  30. package/commits/classify/infrastructure.d.ts.map +1 -0
  31. package/commits/classify/models.d.ts +108 -0
  32. package/commits/classify/models.d.ts.map +1 -0
  33. package/commits/classify/project-scopes.d.ts +59 -0
  34. package/commits/classify/project-scopes.d.ts.map +1 -0
  35. package/commits/index.cjs.js +702 -0
  36. package/commits/index.cjs.js.map +1 -1
  37. package/commits/index.d.ts +1 -0
  38. package/commits/index.d.ts.map +1 -1
  39. package/commits/index.esm.js +677 -1
  40. package/commits/index.esm.js.map +1 -1
  41. package/flow/executor/execute.d.ts +6 -0
  42. package/flow/executor/execute.d.ts.map +1 -1
  43. package/flow/executor/index.cjs.js +1604 -42
  44. package/flow/executor/index.cjs.js.map +1 -1
  45. package/flow/executor/index.esm.js +1610 -48
  46. package/flow/executor/index.esm.js.map +1 -1
  47. package/flow/index.cjs.js +6651 -2893
  48. package/flow/index.cjs.js.map +1 -1
  49. package/flow/index.esm.js +6655 -2899
  50. package/flow/index.esm.js.map +1 -1
  51. package/flow/models/index.cjs.js +125 -0
  52. package/flow/models/index.cjs.js.map +1 -1
  53. package/flow/models/index.esm.js +125 -0
  54. package/flow/models/index.esm.js.map +1 -1
  55. package/flow/models/types.d.ts +148 -3
  56. package/flow/models/types.d.ts.map +1 -1
  57. package/flow/presets/conventional.d.ts +9 -8
  58. package/flow/presets/conventional.d.ts.map +1 -1
  59. package/flow/presets/independent.d.ts.map +1 -1
  60. package/flow/presets/index.cjs.js +3588 -298
  61. package/flow/presets/index.cjs.js.map +1 -1
  62. package/flow/presets/index.esm.js +3588 -298
  63. package/flow/presets/index.esm.js.map +1 -1
  64. package/flow/presets/synced.d.ts.map +1 -1
  65. package/flow/steps/analyze-commits.d.ts +9 -6
  66. package/flow/steps/analyze-commits.d.ts.map +1 -1
  67. package/flow/steps/calculate-bump.d.ts.map +1 -1
  68. package/flow/steps/fetch-registry.d.ts.map +1 -1
  69. package/flow/steps/generate-changelog.d.ts.map +1 -1
  70. package/flow/steps/index.cjs.js +3604 -318
  71. package/flow/steps/index.cjs.js.map +1 -1
  72. package/flow/steps/index.d.ts +1 -0
  73. package/flow/steps/index.d.ts.map +1 -1
  74. package/flow/steps/index.esm.js +3603 -319
  75. package/flow/steps/index.esm.js.map +1 -1
  76. package/flow/steps/resolve-repository.d.ts +36 -0
  77. package/flow/steps/resolve-repository.d.ts.map +1 -0
  78. package/flow/steps/update-packages.d.ts.map +1 -1
  79. package/git/factory.d.ts +14 -0
  80. package/git/factory.d.ts.map +1 -1
  81. package/git/index.cjs.js +65 -0
  82. package/git/index.cjs.js.map +1 -1
  83. package/git/index.esm.js +66 -2
  84. package/git/index.esm.js.map +1 -1
  85. package/git/operations/index.cjs.js +40 -0
  86. package/git/operations/index.cjs.js.map +1 -1
  87. package/git/operations/index.d.ts +1 -1
  88. package/git/operations/index.d.ts.map +1 -1
  89. package/git/operations/index.esm.js +41 -2
  90. package/git/operations/index.esm.js.map +1 -1
  91. package/git/operations/log.d.ts +23 -0
  92. package/git/operations/log.d.ts.map +1 -1
  93. package/index.cjs.js +6962 -4413
  94. package/index.cjs.js.map +1 -1
  95. package/index.esm.js +6964 -4415
  96. package/index.esm.js.map +1 -1
  97. package/package.json +26 -1
  98. package/registry/index.cjs.js +3 -3
  99. package/registry/index.cjs.js.map +1 -1
  100. package/registry/index.esm.js +3 -3
  101. package/registry/index.esm.js.map +1 -1
  102. package/registry/models/index.cjs.js +2 -0
  103. package/registry/models/index.cjs.js.map +1 -1
  104. package/registry/models/index.esm.js +2 -0
  105. package/registry/models/index.esm.js.map +1 -1
  106. package/registry/models/version-info.d.ts +10 -0
  107. package/registry/models/version-info.d.ts.map +1 -1
  108. package/registry/npm/client.d.ts.map +1 -1
  109. package/registry/npm/index.cjs.js +1 -3
  110. package/registry/npm/index.cjs.js.map +1 -1
  111. package/registry/npm/index.esm.js +1 -3
  112. package/registry/npm/index.esm.js.map +1 -1
  113. package/repository/index.cjs.js +998 -0
  114. package/repository/index.cjs.js.map +1 -0
  115. package/repository/index.d.ts +4 -0
  116. package/repository/index.d.ts.map +1 -0
  117. package/repository/index.esm.js +981 -0
  118. package/repository/index.esm.js.map +1 -0
  119. package/repository/models/index.cjs.js +301 -0
  120. package/repository/models/index.cjs.js.map +1 -0
  121. package/repository/models/index.d.ts +7 -0
  122. package/repository/models/index.d.ts.map +1 -0
  123. package/repository/models/index.esm.js +290 -0
  124. package/repository/models/index.esm.js.map +1 -0
  125. package/repository/models/platform.d.ts +58 -0
  126. package/repository/models/platform.d.ts.map +1 -0
  127. package/repository/models/repository-config.d.ts +132 -0
  128. package/repository/models/repository-config.d.ts.map +1 -0
  129. package/repository/models/resolution.d.ts +121 -0
  130. package/repository/models/resolution.d.ts.map +1 -0
  131. package/repository/parse/index.cjs.js +755 -0
  132. package/repository/parse/index.cjs.js.map +1 -0
  133. package/repository/parse/index.d.ts +5 -0
  134. package/repository/parse/index.d.ts.map +1 -0
  135. package/repository/parse/index.esm.js +749 -0
  136. package/repository/parse/index.esm.js.map +1 -0
  137. package/repository/parse/package-json.d.ts +100 -0
  138. package/repository/parse/package-json.d.ts.map +1 -0
  139. package/repository/parse/url.d.ts +81 -0
  140. package/repository/parse/url.d.ts.map +1 -0
  141. package/repository/url/compare.d.ts +84 -0
  142. package/repository/url/compare.d.ts.map +1 -0
  143. package/repository/url/index.cjs.js +178 -0
  144. package/repository/url/index.cjs.js.map +1 -0
  145. package/repository/url/index.d.ts +3 -0
  146. package/repository/url/index.d.ts.map +1 -0
  147. package/repository/url/index.esm.js +176 -0
  148. package/repository/url/index.esm.js.map +1 -0
  149. package/workspace/discovery/index.cjs.js +324 -330
  150. package/workspace/discovery/index.cjs.js.map +1 -1
  151. package/workspace/discovery/index.esm.js +324 -330
  152. package/workspace/discovery/index.esm.js.map +1 -1
  153. package/workspace/discovery/packages.d.ts +0 -6
  154. package/workspace/discovery/packages.d.ts.map +1 -1
  155. package/workspace/index.cjs.js +0 -6
  156. package/workspace/index.cjs.js.map +1 -1
  157. package/workspace/index.esm.js +0 -6
  158. package/workspace/index.esm.js.map +1 -1
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  var node_path = require('node:path');
4
- require('node:util');
5
4
  var node_fs = require('node:fs');
5
+ require('node:util');
6
6
  require('node:os');
7
7
 
8
8
  /**
@@ -75,6 +75,109 @@ const _Reflect = globalThis.Reflect;
75
75
  */
76
76
  const createSet = (iterable) => _Reflect.construct(_Set, iterable ? [iterable] : []);
77
77
 
78
+ /**
79
+ * Safe copies of Array built-in static methods.
80
+ *
81
+ * These references are captured at module initialization time to protect against
82
+ * prototype pollution attacks. Import only what you need for tree-shaking.
83
+ *
84
+ * @module @hyperfrontend/immutable-api-utils/built-in-copy/array
85
+ */
86
+ // Capture references at module initialization time
87
+ const _Array = globalThis.Array;
88
+ /**
89
+ * (Safe copy) Determines whether the passed value is an Array.
90
+ */
91
+ const isArray = _Array.isArray;
92
+
93
+ /**
94
+ * Safe copies of JSON built-in methods.
95
+ *
96
+ * These references are captured at module initialization time to protect against
97
+ * prototype pollution attacks. Import only what you need for tree-shaking.
98
+ *
99
+ * @module @hyperfrontend/immutable-api-utils/built-in-copy/json
100
+ */
101
+ // Capture references at module initialization time
102
+ const _JSON = globalThis.JSON;
103
+ /**
104
+ * (Safe copy) Converts a JavaScript Object Notation (JSON) string into an object.
105
+ */
106
+ const parse = _JSON.parse;
107
+ /**
108
+ * (Safe copy) Converts a JavaScript value to a JavaScript Object Notation (JSON) string.
109
+ */
110
+ const stringify = _JSON.stringify;
111
+
112
+ /**
113
+ * Safe copies of Object built-in methods.
114
+ *
115
+ * These references are captured at module initialization time to protect against
116
+ * prototype pollution attacks. Import only what you need for tree-shaking.
117
+ *
118
+ * @module @hyperfrontend/immutable-api-utils/built-in-copy/object
119
+ */
120
+ // Capture references at module initialization time
121
+ const _Object = globalThis.Object;
122
+ /**
123
+ * (Safe copy) Prevents modification of existing property attributes and values,
124
+ * and prevents the addition of new properties.
125
+ */
126
+ const freeze = _Object.freeze;
127
+ /**
128
+ * (Safe copy) Returns the names of the enumerable string properties and methods of an object.
129
+ */
130
+ const keys = _Object.keys;
131
+ /**
132
+ * (Safe copy) Returns an array of key/values of the enumerable own properties of an object.
133
+ */
134
+ const entries = _Object.entries;
135
+ /**
136
+ * (Safe copy) Returns an array of values of the enumerable own properties of an object.
137
+ */
138
+ const values = _Object.values;
139
+ /**
140
+ * (Safe copy) Adds one or more properties to an object, and/or modifies attributes of existing properties.
141
+ */
142
+ const defineProperties = _Object.defineProperties;
143
+
144
+ /**
145
+ * Create a structured error with code and optional context.
146
+ *
147
+ * @param message - The human-readable error message
148
+ * @param code - The machine-readable error code for programmatic handling
149
+ * @param context - Additional contextual information about the error
150
+ * @returns Structured error instance with code and context properties
151
+ *
152
+ * @example
153
+ * ```typescript
154
+ * import { createStructuredError } from '@hyperfrontend/project-scope'
155
+ *
156
+ * throw createStructuredError(
157
+ * 'Configuration file not found',
158
+ * 'CONFIG_NOT_FOUND',
159
+ * { path: './config.json', searched: ['./config.json', './settings.json'] }
160
+ * )
161
+ * ```
162
+ */
163
+ function createStructuredError(message, code, context) {
164
+ const error = createError(message);
165
+ error.code = code;
166
+ error.context = context ?? {};
167
+ return error;
168
+ }
169
+ /**
170
+ * Create a configuration-related error.
171
+ *
172
+ * @param message - The human-readable error message
173
+ * @param code - The machine-readable error code for programmatic handling
174
+ * @param context - Additional contextual information (e.g., file path, config key)
175
+ * @returns Structured error instance tagged with type 'config'
176
+ */
177
+ function createConfigError(message, code, context) {
178
+ return createStructuredError(message, code, { ...context, type: 'config' });
179
+ }
180
+
78
181
  /**
79
182
  * Safe copies of Console built-in methods.
80
183
  *
@@ -158,72 +261,6 @@ _console.timeEnd.bind(_console);
158
261
  */
159
262
  _console.timeLog.bind(_console);
160
263
 
161
- /**
162
- * Safe copies of Array built-in static methods.
163
- *
164
- * These references are captured at module initialization time to protect against
165
- * prototype pollution attacks. Import only what you need for tree-shaking.
166
- *
167
- * @module @hyperfrontend/immutable-api-utils/built-in-copy/array
168
- */
169
- // Capture references at module initialization time
170
- const _Array = globalThis.Array;
171
- /**
172
- * (Safe copy) Determines whether the passed value is an Array.
173
- */
174
- const isArray = _Array.isArray;
175
-
176
- /**
177
- * Safe copies of JSON built-in methods.
178
- *
179
- * These references are captured at module initialization time to protect against
180
- * prototype pollution attacks. Import only what you need for tree-shaking.
181
- *
182
- * @module @hyperfrontend/immutable-api-utils/built-in-copy/json
183
- */
184
- // Capture references at module initialization time
185
- const _JSON = globalThis.JSON;
186
- /**
187
- * (Safe copy) Converts a JavaScript Object Notation (JSON) string into an object.
188
- */
189
- const parse = _JSON.parse;
190
- /**
191
- * (Safe copy) Converts a JavaScript value to a JavaScript Object Notation (JSON) string.
192
- */
193
- const stringify = _JSON.stringify;
194
-
195
- /**
196
- * Safe copies of Object built-in methods.
197
- *
198
- * These references are captured at module initialization time to protect against
199
- * prototype pollution attacks. Import only what you need for tree-shaking.
200
- *
201
- * @module @hyperfrontend/immutable-api-utils/built-in-copy/object
202
- */
203
- // Capture references at module initialization time
204
- const _Object = globalThis.Object;
205
- /**
206
- * (Safe copy) Prevents modification of existing property attributes and values,
207
- * and prevents the addition of new properties.
208
- */
209
- const freeze = _Object.freeze;
210
- /**
211
- * (Safe copy) Returns the names of the enumerable string properties and methods of an object.
212
- */
213
- const keys = _Object.keys;
214
- /**
215
- * (Safe copy) Returns an array of key/values of the enumerable own properties of an object.
216
- */
217
- const entries = _Object.entries;
218
- /**
219
- * (Safe copy) Returns an array of values of the enumerable own properties of an object.
220
- */
221
- const values = _Object.values;
222
- /**
223
- * (Safe copy) Adds one or more properties to an object, and/or modifies attributes of existing properties.
224
- */
225
- const defineProperties = _Object.defineProperties;
226
-
227
264
  const registeredClasses = [];
228
265
 
229
266
  /**
@@ -781,43 +818,6 @@ function findUpwardWhere(startPath, test) {
781
818
  return traverseUpward(startPath, test);
782
819
  }
783
820
 
784
- /**
785
- * Create a structured error with code and optional context.
786
- *
787
- * @param message - The human-readable error message
788
- * @param code - The machine-readable error code for programmatic handling
789
- * @param context - Additional contextual information about the error
790
- * @returns Structured error instance with code and context properties
791
- *
792
- * @example
793
- * ```typescript
794
- * import { createStructuredError } from '@hyperfrontend/project-scope'
795
- *
796
- * throw createStructuredError(
797
- * 'Configuration file not found',
798
- * 'CONFIG_NOT_FOUND',
799
- * { path: './config.json', searched: ['./config.json', './settings.json'] }
800
- * )
801
- * ```
802
- */
803
- function createStructuredError(message, code, context) {
804
- const error = createError(message);
805
- error.code = code;
806
- error.context = context ?? {};
807
- return error;
808
- }
809
- /**
810
- * Create a configuration-related error.
811
- *
812
- * @param message - The human-readable error message
813
- * @param code - The machine-readable error code for programmatic handling
814
- * @param context - Additional contextual information (e.g., file path, config key)
815
- * @returns Structured error instance tagged with type 'config'
816
- */
817
- function createConfigError(message, code, context) {
818
- return createStructuredError(message, code, { ...context, type: 'config' });
819
- }
820
-
821
821
  const packageLogger = createScopedLogger('project-scope:project:package');
822
822
  /**
823
823
  * Verifies that a value is an object with only string values,
@@ -944,138 +944,51 @@ function findNearestPackageJson(startPath) {
944
944
  return locateByMarkers(startPath, ['package.json']);
945
945
  }
946
946
 
947
- createScopedLogger('project-scope:heuristics:deps');
948
-
947
+ const rootLogger = createScopedLogger('project-scope:root');
949
948
  /**
950
- * Global registry of all caches for bulk operations.
949
+ * Files indicating workspace/monorepo root.
951
950
  */
952
- const cacheRegistry = createSet();
951
+ const WORKSPACE_MARKERS = ['nx.json', 'turbo.json', 'lerna.json', 'pnpm-workspace.yaml', 'rush.json'];
953
952
  /**
954
- * Create a cache with optional TTL and size limits.
955
- *
956
- * The cache provides a simple key-value store with:
957
- * - Optional TTL (time-to-live) for automatic expiration
958
- * - Optional maxSize for limiting cache size with FIFO eviction
959
- * - Lazy expiration (entries are checked on access)
953
+ * Find workspace root (monorepo root).
954
+ * Searches up for workspace markers like nx.json, turbo.json, etc.
960
955
  *
961
- * @param options - Cache configuration options
962
- * @returns Cache instance
956
+ * @param startPath - Starting path
957
+ * @returns Workspace root path or null
963
958
  *
964
959
  * @example
965
960
  * ```typescript
966
- * // Basic cache
967
- * const cache = createCache<string, number>()
968
- * cache.set('answer', 42)
969
- * cache.get('answer') // 42
970
- *
971
- * // Cache with TTL (expires after 60 seconds)
972
- * const ttlCache = createCache<string, object>({ ttl: 60000 })
973
- *
974
- * // Cache with max size (evicts oldest when full)
975
- * const lruCache = createCache<string, object>({ maxSize: 100 })
961
+ * import { findWorkspaceRoot } from '@hyperfrontend/project-scope'
976
962
  *
977
- * // Combined options
978
- * const configCache = createCache<string, object>({
979
- * ttl: 30000,
980
- * maxSize: 50
981
- * })
963
+ * const root = findWorkspaceRoot('./libs/my-lib')
964
+ * if (root) {
965
+ * console.log('Monorepo root:', root) // e.g., '/home/user/my-monorepo'
966
+ * }
982
967
  * ```
983
968
  */
984
- function createCache(options) {
985
- const { ttl, maxSize } = options ?? {};
986
- const store = createMap();
987
- // Track insertion order for FIFO eviction
988
- const insertionOrder = [];
989
- /**
990
- * Check if an entry is expired.
991
- *
992
- * @param entry - Cache entry to check
993
- * @returns True if entry is expired
994
- */
995
- function isExpired(entry) {
996
- if (ttl === undefined)
997
- return false;
998
- // eslint-disable-next-line workspace/no-unsafe-builtin-methods -- Date.now() is needed for Jest fake timers compatibility
999
- return Date.now() - entry.timestamp > ttl;
969
+ function findWorkspaceRoot(startPath) {
970
+ rootLogger.debug('Finding workspace root', { startPath });
971
+ const byMarker = locateByMarkers(startPath, WORKSPACE_MARKERS);
972
+ if (byMarker) {
973
+ rootLogger.debug('Found workspace root by marker', { root: byMarker });
974
+ return byMarker;
1000
975
  }
1001
- /**
1002
- * Evict oldest entries to make room for new ones.
1003
- */
1004
- function evictIfNeeded() {
1005
- if (maxSize === undefined)
1006
- return;
1007
- while (store.size >= maxSize && insertionOrder.length > 0) {
1008
- const oldestKey = insertionOrder.shift();
1009
- if (oldestKey !== undefined) {
1010
- store.delete(oldestKey);
1011
- }
1012
- }
976
+ const byWorkspaces = findUpwardWhere(startPath, (dir) => {
977
+ const pkg = readPackageJsonIfExists(dir);
978
+ return pkg?.workspaces !== undefined;
979
+ });
980
+ if (byWorkspaces) {
981
+ rootLogger.debug('Found workspace root by workspaces field', { root: byWorkspaces });
982
+ return byWorkspaces;
1013
983
  }
1014
- /**
1015
- * Remove key from insertion order tracking.
1016
- *
1017
- * @param key - Key to remove from order tracking
1018
- */
1019
- function removeFromOrder(key) {
1020
- const index = insertionOrder.indexOf(key);
1021
- if (index !== -1) {
1022
- insertionOrder.splice(index, 1);
1023
- }
984
+ const byPackage = findNearestPackageJson(startPath);
985
+ if (byPackage) {
986
+ rootLogger.debug('Found workspace root by package.json', { root: byPackage });
1024
987
  }
1025
- const cache = {
1026
- get(key) {
1027
- const entry = store.get(key);
1028
- if (!entry)
1029
- return undefined;
1030
- if (isExpired(entry)) {
1031
- store.delete(key);
1032
- removeFromOrder(key);
1033
- return undefined;
1034
- }
1035
- return entry.value;
1036
- },
1037
- set(key, value) {
1038
- // If key exists, remove from order first
1039
- if (store.has(key)) {
1040
- removeFromOrder(key);
1041
- }
1042
- else {
1043
- // Evict if needed before adding new entry
1044
- evictIfNeeded();
1045
- }
1046
- // eslint-disable-next-line workspace/no-unsafe-builtin-methods -- Date.now() is needed for Jest fake timers compatibility
1047
- store.set(key, { value, timestamp: Date.now() });
1048
- insertionOrder.push(key);
1049
- },
1050
- has(key) {
1051
- const entry = store.get(key);
1052
- if (!entry)
1053
- return false;
1054
- if (isExpired(entry)) {
1055
- store.delete(key);
1056
- removeFromOrder(key);
1057
- return false;
1058
- }
1059
- return true;
1060
- },
1061
- delete(key) {
1062
- removeFromOrder(key);
1063
- return store.delete(key);
1064
- },
1065
- clear() {
1066
- store.clear();
1067
- insertionOrder.length = 0;
1068
- },
1069
- size() {
1070
- return store.size;
1071
- },
1072
- keys() {
1073
- return [...insertionOrder];
1074
- },
1075
- };
1076
- // Register cache for global operations
1077
- cacheRegistry.add(cache);
1078
- return freeze(cache);
988
+ else {
989
+ rootLogger.debug('Workspace root not found');
990
+ }
991
+ return byPackage;
1079
992
  }
1080
993
 
1081
994
  /**
@@ -1483,107 +1396,6 @@ function findFiles(startPath, patterns, options) {
1483
1396
  return results;
1484
1397
  }
1485
1398
 
1486
- createScopedLogger('project-scope:heuristics:entry-points');
1487
- /**
1488
- * Cache for entry point discovery results.
1489
- * TTL: 60 seconds (entry points are relatively stable)
1490
- */
1491
- createCache({ ttl: 60000, maxSize: 50 });
1492
-
1493
- createScopedLogger('project-scope:tech');
1494
- /**
1495
- * Cache for tech detection results.
1496
- * TTL: 60 seconds (tech stack can change during active development)
1497
- */
1498
- createCache({ ttl: 60000, maxSize: 50 });
1499
-
1500
- createScopedLogger('project-scope:heuristics:project-type');
1501
-
1502
- const rootLogger = createScopedLogger('project-scope:root');
1503
- /**
1504
- * Files indicating workspace/monorepo root.
1505
- */
1506
- const WORKSPACE_MARKERS = ['nx.json', 'turbo.json', 'lerna.json', 'pnpm-workspace.yaml', 'rush.json'];
1507
- /**
1508
- * Find workspace root (monorepo root).
1509
- * Searches up for workspace markers like nx.json, turbo.json, etc.
1510
- *
1511
- * @param startPath - Starting path
1512
- * @returns Workspace root path or null
1513
- *
1514
- * @example
1515
- * ```typescript
1516
- * import { findWorkspaceRoot } from '@hyperfrontend/project-scope'
1517
- *
1518
- * const root = findWorkspaceRoot('./libs/my-lib')
1519
- * if (root) {
1520
- * console.log('Monorepo root:', root) // e.g., '/home/user/my-monorepo'
1521
- * }
1522
- * ```
1523
- */
1524
- function findWorkspaceRoot(startPath) {
1525
- rootLogger.debug('Finding workspace root', { startPath });
1526
- const byMarker = locateByMarkers(startPath, WORKSPACE_MARKERS);
1527
- if (byMarker) {
1528
- rootLogger.debug('Found workspace root by marker', { root: byMarker });
1529
- return byMarker;
1530
- }
1531
- const byWorkspaces = findUpwardWhere(startPath, (dir) => {
1532
- const pkg = readPackageJsonIfExists(dir);
1533
- return pkg?.workspaces !== undefined;
1534
- });
1535
- if (byWorkspaces) {
1536
- rootLogger.debug('Found workspace root by workspaces field', { root: byWorkspaces });
1537
- return byWorkspaces;
1538
- }
1539
- const byPackage = findNearestPackageJson(startPath);
1540
- if (byPackage) {
1541
- rootLogger.debug('Found workspace root by package.json', { root: byPackage });
1542
- }
1543
- else {
1544
- rootLogger.debug('Workspace root not found');
1545
- }
1546
- return byPackage;
1547
- }
1548
-
1549
- createScopedLogger('project-scope:nx');
1550
-
1551
- createScopedLogger('project-scope:nx:devkit');
1552
-
1553
- createScopedLogger('project-scope:nx:config');
1554
-
1555
- createScopedLogger('project-scope:config');
1556
- /**
1557
- * Cache for config detection results.
1558
- * TTL: 30 seconds (configs can change frequently during setup)
1559
- */
1560
- createCache({ ttl: 30000, maxSize: 50 });
1561
-
1562
- /** Logger for analysis operations */
1563
- createScopedLogger('project-scope:analyze');
1564
-
1565
- /** Logger for CLI operations */
1566
- createScopedLogger('project-scope:cli');
1567
-
1568
- createScopedLogger('project-scope:encoding');
1569
-
1570
- createScopedLogger('project-scope:encoding:convert');
1571
-
1572
- createScopedLogger('project-scope:heuristics:framework');
1573
- /**
1574
- * Cache for framework identification results.
1575
- * TTL: 60 seconds (frameworks are stable but can change during development)
1576
- */
1577
- createCache({ ttl: 60000, maxSize: 50 });
1578
-
1579
- createScopedLogger('project-scope:vfs:tree');
1580
-
1581
- createScopedLogger('project-scope:vfs:factory');
1582
-
1583
- createScopedLogger('project-scope:vfs');
1584
-
1585
- createScopedLogger('project-scope:vfs:diff');
1586
-
1587
1399
  /**
1588
1400
  * Project Model
1589
1401
  *
@@ -1967,6 +1779,194 @@ function transitivelyDependsOn(workspace, packageA, packageB) {
1967
1779
  return deps.has(packageB);
1968
1780
  }
1969
1781
 
1782
+ createScopedLogger('project-scope:heuristics:deps');
1783
+
1784
+ /**
1785
+ * Global registry of all caches for bulk operations.
1786
+ */
1787
+ const cacheRegistry = createSet();
1788
+ /**
1789
+ * Create a cache with optional TTL and size limits.
1790
+ *
1791
+ * The cache provides a simple key-value store with:
1792
+ * - Optional TTL (time-to-live) for automatic expiration
1793
+ * - Optional maxSize for limiting cache size with FIFO eviction
1794
+ * - Lazy expiration (entries are checked on access)
1795
+ *
1796
+ * @param options - Cache configuration options
1797
+ * @returns Cache instance
1798
+ *
1799
+ * @example
1800
+ * ```typescript
1801
+ * // Basic cache
1802
+ * const cache = createCache<string, number>()
1803
+ * cache.set('answer', 42)
1804
+ * cache.get('answer') // 42
1805
+ *
1806
+ * // Cache with TTL (expires after 60 seconds)
1807
+ * const ttlCache = createCache<string, object>({ ttl: 60000 })
1808
+ *
1809
+ * // Cache with max size (evicts oldest when full)
1810
+ * const lruCache = createCache<string, object>({ maxSize: 100 })
1811
+ *
1812
+ * // Combined options
1813
+ * const configCache = createCache<string, object>({
1814
+ * ttl: 30000,
1815
+ * maxSize: 50
1816
+ * })
1817
+ * ```
1818
+ */
1819
+ function createCache(options) {
1820
+ const { ttl, maxSize } = options ?? {};
1821
+ const store = createMap();
1822
+ // Track insertion order for FIFO eviction
1823
+ const insertionOrder = [];
1824
+ /**
1825
+ * Check if an entry is expired.
1826
+ *
1827
+ * @param entry - Cache entry to check
1828
+ * @returns True if entry is expired
1829
+ */
1830
+ function isExpired(entry) {
1831
+ if (ttl === undefined)
1832
+ return false;
1833
+ // eslint-disable-next-line workspace/no-unsafe-builtin-methods -- Date.now() is needed for Jest fake timers compatibility
1834
+ return Date.now() - entry.timestamp > ttl;
1835
+ }
1836
+ /**
1837
+ * Evict oldest entries to make room for new ones.
1838
+ */
1839
+ function evictIfNeeded() {
1840
+ if (maxSize === undefined)
1841
+ return;
1842
+ while (store.size >= maxSize && insertionOrder.length > 0) {
1843
+ const oldestKey = insertionOrder.shift();
1844
+ if (oldestKey !== undefined) {
1845
+ store.delete(oldestKey);
1846
+ }
1847
+ }
1848
+ }
1849
+ /**
1850
+ * Remove key from insertion order tracking.
1851
+ *
1852
+ * @param key - Key to remove from order tracking
1853
+ */
1854
+ function removeFromOrder(key) {
1855
+ const index = insertionOrder.indexOf(key);
1856
+ if (index !== -1) {
1857
+ insertionOrder.splice(index, 1);
1858
+ }
1859
+ }
1860
+ const cache = {
1861
+ get(key) {
1862
+ const entry = store.get(key);
1863
+ if (!entry)
1864
+ return undefined;
1865
+ if (isExpired(entry)) {
1866
+ store.delete(key);
1867
+ removeFromOrder(key);
1868
+ return undefined;
1869
+ }
1870
+ return entry.value;
1871
+ },
1872
+ set(key, value) {
1873
+ // If key exists, remove from order first
1874
+ if (store.has(key)) {
1875
+ removeFromOrder(key);
1876
+ }
1877
+ else {
1878
+ // Evict if needed before adding new entry
1879
+ evictIfNeeded();
1880
+ }
1881
+ // eslint-disable-next-line workspace/no-unsafe-builtin-methods -- Date.now() is needed for Jest fake timers compatibility
1882
+ store.set(key, { value, timestamp: Date.now() });
1883
+ insertionOrder.push(key);
1884
+ },
1885
+ has(key) {
1886
+ const entry = store.get(key);
1887
+ if (!entry)
1888
+ return false;
1889
+ if (isExpired(entry)) {
1890
+ store.delete(key);
1891
+ removeFromOrder(key);
1892
+ return false;
1893
+ }
1894
+ return true;
1895
+ },
1896
+ delete(key) {
1897
+ removeFromOrder(key);
1898
+ return store.delete(key);
1899
+ },
1900
+ clear() {
1901
+ store.clear();
1902
+ insertionOrder.length = 0;
1903
+ },
1904
+ size() {
1905
+ return store.size;
1906
+ },
1907
+ keys() {
1908
+ return [...insertionOrder];
1909
+ },
1910
+ };
1911
+ // Register cache for global operations
1912
+ cacheRegistry.add(cache);
1913
+ return freeze(cache);
1914
+ }
1915
+
1916
+ createScopedLogger('project-scope:heuristics:entry-points');
1917
+ /**
1918
+ * Cache for entry point discovery results.
1919
+ * TTL: 60 seconds (entry points are relatively stable)
1920
+ */
1921
+ createCache({ ttl: 60000, maxSize: 50 });
1922
+
1923
+ createScopedLogger('project-scope:tech');
1924
+ /**
1925
+ * Cache for tech detection results.
1926
+ * TTL: 60 seconds (tech stack can change during active development)
1927
+ */
1928
+ createCache({ ttl: 60000, maxSize: 50 });
1929
+
1930
+ createScopedLogger('project-scope:heuristics:project-type');
1931
+
1932
+ createScopedLogger('project-scope:nx');
1933
+
1934
+ createScopedLogger('project-scope:nx:devkit');
1935
+
1936
+ createScopedLogger('project-scope:nx:config');
1937
+
1938
+ createScopedLogger('project-scope:config');
1939
+ /**
1940
+ * Cache for config detection results.
1941
+ * TTL: 30 seconds (configs can change frequently during setup)
1942
+ */
1943
+ createCache({ ttl: 30000, maxSize: 50 });
1944
+
1945
+ /** Logger for analysis operations */
1946
+ createScopedLogger('project-scope:analyze');
1947
+
1948
+ /** Logger for CLI operations */
1949
+ createScopedLogger('project-scope:cli');
1950
+
1951
+ createScopedLogger('project-scope:encoding');
1952
+
1953
+ createScopedLogger('project-scope:encoding:convert');
1954
+
1955
+ createScopedLogger('project-scope:heuristics:framework');
1956
+ /**
1957
+ * Cache for framework identification results.
1958
+ * TTL: 60 seconds (frameworks are stable but can change during development)
1959
+ */
1960
+ createCache({ ttl: 60000, maxSize: 50 });
1961
+
1962
+ createScopedLogger('project-scope:vfs:tree');
1963
+
1964
+ createScopedLogger('project-scope:vfs:factory');
1965
+
1966
+ createScopedLogger('project-scope:vfs');
1967
+
1968
+ createScopedLogger('project-scope:vfs:diff');
1969
+
1970
1970
  /**
1971
1971
  * Changelog Discovery
1972
1972
  *
@@ -2057,12 +2057,6 @@ function discoverAllChangelogs(workspaceRoot, patterns = ['**/CHANGELOG.md', '**
2057
2057
  return results;
2058
2058
  }
2059
2059
 
2060
- /**
2061
- * Package Discovery
2062
- *
2063
- * Discovers packages within a workspace by finding package.json files
2064
- * and extracting relevant metadata. Uses project-scope for file operations.
2065
- */
2066
2060
  /**
2067
2061
  * Discovers all packages within a workspace.
2068
2062
  * Finds package.json files, parses them, and optionally discovers