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