aios-core 4.2.13 → 4.2.15

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 (95) hide show
  1. package/.aios-core/core/code-intel/helpers/dev-helper.js +206 -0
  2. package/.aios-core/core/registry/registry-schema.json +166 -166
  3. package/.aios-core/core/synapse/diagnostics/collectors/hook-collector.js +3 -3
  4. package/.aios-core/data/entity-registry.yaml +27 -0
  5. package/.aios-core/development/scripts/approval-workflow.js +642 -642
  6. package/.aios-core/development/scripts/backup-manager.js +606 -606
  7. package/.aios-core/development/scripts/branch-manager.js +389 -389
  8. package/.aios-core/development/scripts/code-quality-improver.js +1311 -1311
  9. package/.aios-core/development/scripts/commit-message-generator.js +849 -849
  10. package/.aios-core/development/scripts/conflict-resolver.js +674 -674
  11. package/.aios-core/development/scripts/dependency-analyzer.js +637 -637
  12. package/.aios-core/development/scripts/diff-generator.js +351 -351
  13. package/.aios-core/development/scripts/elicitation-engine.js +384 -384
  14. package/.aios-core/development/scripts/elicitation-session-manager.js +299 -299
  15. package/.aios-core/development/scripts/git-wrapper.js +461 -461
  16. package/.aios-core/development/scripts/manifest-preview.js +244 -244
  17. package/.aios-core/development/scripts/metrics-tracker.js +775 -775
  18. package/.aios-core/development/scripts/modification-validator.js +554 -554
  19. package/.aios-core/development/scripts/pattern-learner.js +1224 -1224
  20. package/.aios-core/development/scripts/performance-analyzer.js +757 -757
  21. package/.aios-core/development/scripts/refactoring-suggester.js +1138 -1138
  22. package/.aios-core/development/scripts/rollback-handler.js +530 -530
  23. package/.aios-core/development/scripts/security-checker.js +358 -358
  24. package/.aios-core/development/scripts/template-engine.js +239 -239
  25. package/.aios-core/development/scripts/template-validator.js +278 -278
  26. package/.aios-core/development/scripts/test-generator.js +843 -843
  27. package/.aios-core/development/scripts/transaction-manager.js +589 -589
  28. package/.aios-core/development/scripts/usage-tracker.js +673 -673
  29. package/.aios-core/development/scripts/validate-filenames.js +226 -226
  30. package/.aios-core/development/scripts/version-tracker.js +526 -526
  31. package/.aios-core/development/scripts/yaml-validator.js +396 -396
  32. package/.aios-core/development/tasks/build-autonomous.md +10 -4
  33. package/.aios-core/development/tasks/create-service.md +23 -0
  34. package/.aios-core/development/tasks/dev-develop-story.md +12 -6
  35. package/.aios-core/development/tasks/dev-suggest-refactoring.md +7 -1
  36. package/.aios-core/development/tasks/publish-npm.md +3 -3
  37. package/.aios-core/hooks/unified/README.md +1 -1
  38. package/.aios-core/install-manifest.yaml +65 -61
  39. package/.aios-core/manifests/schema/manifest-schema.json +190 -190
  40. package/.aios-core/product/templates/component-react-tmpl.tsx +98 -98
  41. package/.aios-core/product/templates/engine/schemas/adr.schema.json +102 -102
  42. package/.aios-core/product/templates/engine/schemas/dbdr.schema.json +205 -205
  43. package/.aios-core/product/templates/engine/schemas/epic.schema.json +175 -175
  44. package/.aios-core/product/templates/engine/schemas/pmdr.schema.json +175 -175
  45. package/.aios-core/product/templates/engine/schemas/prd-v2.schema.json +300 -300
  46. package/.aios-core/product/templates/engine/schemas/prd.schema.json +152 -152
  47. package/.aios-core/product/templates/engine/schemas/story.schema.json +222 -222
  48. package/.aios-core/product/templates/engine/schemas/task.schema.json +154 -154
  49. package/.aios-core/product/templates/eslintrc-security.json +32 -32
  50. package/.aios-core/product/templates/github-actions-cd.yml +212 -212
  51. package/.aios-core/product/templates/github-actions-ci.yml +172 -172
  52. package/.aios-core/product/templates/shock-report-tmpl.html +502 -502
  53. package/.aios-core/product/templates/token-exports-css-tmpl.css +240 -240
  54. package/.aios-core/quality/schemas/quality-metrics.schema.json +233 -233
  55. package/.aios-core/scripts/migrate-framework-docs.sh +300 -300
  56. package/README.en.md +747 -0
  57. package/README.md +4 -2
  58. package/bin/aios.js +7 -4
  59. package/package.json +1 -1
  60. package/packages/aios-pro-cli/src/recover.js +1 -1
  61. package/packages/installer/src/wizard/ide-config-generator.js +6 -6
  62. package/packages/installer/src/wizard/pro-setup.js +3 -3
  63. package/pro/license/degradation.js +220 -220
  64. package/pro/license/errors.js +450 -450
  65. package/pro/license/feature-gate.js +354 -354
  66. package/pro/license/index.js +181 -181
  67. package/pro/license/license-cache.js +523 -523
  68. package/pro/license/license-crypto.js +303 -303
  69. package/scripts/package-synapse.js +5 -5
  70. package/scripts/validate-package-completeness.js +3 -3
  71. package/.aios-core/.session/current-session.json +0 -14
  72. package/.aios-core/data/registry-update-log.jsonl +0 -191
  73. package/.aios-core/docs/SHARD-TRANSLATION-GUIDE.md +0 -335
  74. package/.aios-core/docs/component-creation-guide.md +0 -458
  75. package/.aios-core/docs/session-update-pattern.md +0 -307
  76. package/.aios-core/docs/standards/AIOS-FRAMEWORK-MASTER.md +0 -1963
  77. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1-SUMMARY.md +0 -1190
  78. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1.md +0 -439
  79. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO.md +0 -5398
  80. package/.aios-core/docs/standards/V3-ARCHITECTURAL-DECISIONS.md +0 -523
  81. package/.aios-core/docs/template-syntax.md +0 -267
  82. package/.aios-core/docs/troubleshooting-guide.md +0 -625
  83. package/.aios-core/infrastructure/tests/utilities-audit-results.json +0 -501
  84. package/.aios-core/manifests/agents.csv +0 -29
  85. package/.aios-core/manifests/tasks.csv +0 -198
  86. package/.aios-core/manifests/workers.csv +0 -204
  87. package/.claude/rules/agent-authority.md +0 -105
  88. package/.claude/rules/coderabbit-integration.md +0 -93
  89. package/.claude/rules/ids-principles.md +0 -112
  90. package/.claude/rules/story-lifecycle.md +0 -139
  91. package/.claude/rules/workflow-execution.md +0 -150
  92. package/scripts/glue/README.md +0 -355
  93. package/scripts/glue/compose-agent-prompt.cjs +0 -362
  94. /package/.claude/hooks/{precompact-session-digest.js → precompact-session-digest.cjs} +0 -0
  95. /package/.claude/hooks/{synapse-engine.js → synapse-engine.cjs} +0 -0
@@ -1,354 +1,354 @@
1
- /**
2
- * Feature Gate Module
3
- *
4
- * Singleton class that validates feature availability based on license cache.
5
- * Supports exact feature ID matching and wildcard patterns.
6
- *
7
- * Usage:
8
- * const { featureGate } = require('./feature-gate');
9
- *
10
- * // Check availability
11
- * if (featureGate.isAvailable('pro.squads.premium')) {
12
- * // Feature is licensed
13
- * }
14
- *
15
- * // Require feature (throws if not available)
16
- * featureGate.require('pro.squads.premium', 'Premium Squads');
17
- *
18
- * @module pro/license/feature-gate
19
- * @see ADR-PRO-003 - Feature Gating & Licensing
20
- * @see Story PRO-6 - License Key & Feature Gating System
21
- */
22
-
23
- 'use strict';
24
-
25
- const fs = require('fs');
26
- const path = require('path');
27
- const yaml = require('yaml');
28
- const { readLicenseCache, isExpired, isInGracePeriod, getLicenseState } = require('./license-cache');
29
- const { ProFeatureError } = require('./errors');
30
-
31
- /**
32
- * Path to the feature registry YAML file.
33
- */
34
- const REGISTRY_PATH = path.join(__dirname, '..', 'feature-registry.yaml');
35
-
36
- /**
37
- * FeatureGate class - validates feature availability.
38
- *
39
- * Singleton pattern ensures consistent state across the application.
40
- */
41
- class FeatureGate {
42
- constructor() {
43
- this._cache = null;
44
- this._licensedFeatures = new Set();
45
- this._registeredFeatures = new Map();
46
- this._lastLoadTime = null;
47
- this._cacheLoaded = false;
48
- this._registryLoaded = false;
49
- }
50
-
51
- /**
52
- * Load the feature registry from YAML.
53
- *
54
- * @private
55
- */
56
- _loadRegistry() {
57
- if (this._registryLoaded) {
58
- return;
59
- }
60
-
61
- try {
62
- if (fs.existsSync(REGISTRY_PATH)) {
63
- const content = fs.readFileSync(REGISTRY_PATH, 'utf8');
64
- const registry = yaml.parse(content);
65
-
66
- // Extract all features from modules
67
- const modules = ['squads', 'memory', 'metrics', 'integrations', 'agents', 'cli', 'config'];
68
-
69
- for (const module of modules) {
70
- if (registry[module] && Array.isArray(registry[module])) {
71
- for (const feature of registry[module]) {
72
- this._registeredFeatures.set(feature.id, {
73
- id: feature.id,
74
- name: feature.name,
75
- description: feature.description,
76
- module: feature.module,
77
- });
78
- }
79
- }
80
- }
81
-
82
- this._registryLoaded = true;
83
- }
84
- } catch {
85
- // Registry load failed, features will still work via license patterns
86
- this._registryLoaded = true;
87
- }
88
- }
89
-
90
- /**
91
- * Load the license cache and populate licensed features.
92
- *
93
- * @private
94
- * @param {boolean} [force=false] - Force reload even if already loaded
95
- */
96
- _loadCache(force = false) {
97
- if (this._cacheLoaded && !force) {
98
- return;
99
- }
100
-
101
- // Load registry if not loaded
102
- this._loadRegistry();
103
-
104
- // Read license cache
105
- this._cache = readLicenseCache();
106
- this._licensedFeatures.clear();
107
- this._lastLoadTime = Date.now();
108
-
109
- if (!this._cache) {
110
- this._cacheLoaded = true;
111
- return;
112
- }
113
-
114
- // Check if cache is valid (not expired, or in grace period)
115
- const state = getLicenseState(this._cache);
116
-
117
- if (state === 'Expired') {
118
- // Expired and past grace period - no features available
119
- // Keep cache reference so getLicenseState can report 'Expired'
120
- this._cacheLoaded = true;
121
- return;
122
- }
123
-
124
- // Populate licensed features from cache
125
- if (this._cache.features && Array.isArray(this._cache.features)) {
126
- for (const featurePattern of this._cache.features) {
127
- this._licensedFeatures.add(featurePattern);
128
- }
129
- }
130
-
131
- this._cacheLoaded = true;
132
- }
133
-
134
- /**
135
- * Check if a feature pattern matches a feature ID using wildcard matching.
136
- *
137
- * Supports:
138
- * - Exact match: "pro.squads.premium" matches "pro.squads.premium"
139
- * - Wildcard: "pro.squads.*" matches "pro.squads.premium", "pro.squads.custom"
140
- * - Module wildcard: "pro.*" matches any pro feature
141
- *
142
- * @private
143
- * @param {string} featureId - Feature ID to check
144
- * @param {string} pattern - Pattern from license
145
- * @returns {boolean} true if pattern matches feature ID
146
- */
147
- _matchWildcard(featureId, pattern) {
148
- // Exact match
149
- if (pattern === featureId) {
150
- return true;
151
- }
152
-
153
- // Wildcard match: "pro.*" matches "pro.squads.premium"
154
- if (pattern.endsWith('.*')) {
155
- const prefix = pattern.slice(0, -1); // Remove trailing * → "pro."
156
- return featureId.startsWith(prefix);
157
- }
158
-
159
- // BUG-7 fix (INS-1): Bare module pattern without dots (e.g., "pro")
160
- // should match all features under that module (equivalent to "pro.*")
161
- if (!pattern.includes('.') && featureId.startsWith(pattern + '.')) {
162
- return true;
163
- }
164
-
165
- return false;
166
- }
167
-
168
- /**
169
- * Check if a feature is available (licensed).
170
- *
171
- * Performance: < 5ms (cache read only, per AC-13)
172
- *
173
- * @param {string} featureId - Feature ID to check (e.g., "pro.squads.premium")
174
- * @returns {boolean} true if feature is licensed and available
175
- */
176
- isAvailable(featureId) {
177
- this._loadCache();
178
-
179
- if (this._licensedFeatures.size === 0) {
180
- return false;
181
- }
182
-
183
- // Check direct match first (O(1))
184
- if (this._licensedFeatures.has(featureId)) {
185
- return true;
186
- }
187
-
188
- // Check wildcard patterns
189
- for (const pattern of this._licensedFeatures) {
190
- if (this._matchWildcard(featureId, pattern)) {
191
- return true;
192
- }
193
- }
194
-
195
- return false;
196
- }
197
-
198
- /**
199
- * Require a feature to be available, throw if not.
200
- *
201
- * Use this to gate premium functionality:
202
- *
203
- * featureGate.require('pro.squads.premium', 'Premium Squads');
204
- *
205
- * @param {string} featureId - Feature ID to require
206
- * @param {string} [friendlyName] - Human-friendly feature name for error message
207
- * @throws {ProFeatureError} If feature is not licensed
208
- */
209
- require(featureId, friendlyName) {
210
- if (!this.isAvailable(featureId)) {
211
- const name = friendlyName || this._getFeatureName(featureId) || featureId;
212
- throw new ProFeatureError(featureId, name);
213
- }
214
- }
215
-
216
- /**
217
- * Get the friendly name of a feature from registry.
218
- *
219
- * @private
220
- * @param {string} featureId - Feature ID
221
- * @returns {string|null} Feature name or null
222
- */
223
- _getFeatureName(featureId) {
224
- this._loadRegistry();
225
- const feature = this._registeredFeatures.get(featureId);
226
- return feature ? feature.name : null;
227
- }
228
-
229
- /**
230
- * List all available (licensed) feature IDs.
231
- *
232
- * Returns the resolved list of feature IDs, expanding wildcards
233
- * against the registry.
234
- *
235
- * @returns {string[]} Array of available feature IDs
236
- */
237
- listAvailable() {
238
- this._loadCache();
239
- this._loadRegistry();
240
-
241
- const available = new Set();
242
-
243
- // Check each registered feature against licensed patterns
244
- for (const [featureId] of this._registeredFeatures) {
245
- if (this.isAvailable(featureId)) {
246
- available.add(featureId);
247
- }
248
- }
249
-
250
- return Array.from(available).sort();
251
- }
252
-
253
- /**
254
- * List all registered features with their availability status.
255
- *
256
- * @returns {Array<{ id: string, name: string, description: string, module: string, available: boolean }>}
257
- */
258
- listAll() {
259
- this._loadCache();
260
- this._loadRegistry();
261
-
262
- const features = [];
263
-
264
- for (const [featureId, feature] of this._registeredFeatures) {
265
- features.push({
266
- ...feature,
267
- available: this.isAvailable(featureId),
268
- });
269
- }
270
-
271
- return features.sort((a, b) => a.id.localeCompare(b.id));
272
- }
273
-
274
- /**
275
- * List features grouped by module.
276
- *
277
- * @returns {Object<string, Array<{ id: string, name: string, available: boolean }>>}
278
- */
279
- listByModule() {
280
- const all = this.listAll();
281
- const grouped = {};
282
-
283
- for (const feature of all) {
284
- if (!grouped[feature.module]) {
285
- grouped[feature.module] = [];
286
- }
287
- grouped[feature.module].push(feature);
288
- }
289
-
290
- return grouped;
291
- }
292
-
293
- /**
294
- * Force reload of the license cache.
295
- *
296
- * Call this after activation to pick up new features.
297
- */
298
- reload() {
299
- this._cacheLoaded = false;
300
- this._loadCache(true);
301
- }
302
-
303
- /**
304
- * Get the current license state.
305
- *
306
- * @returns {'Active'|'Expired'|'Grace'|'Not Activated'} License state
307
- */
308
- getLicenseState() {
309
- this._loadCache();
310
- return getLicenseState(this._cache);
311
- }
312
-
313
- /**
314
- * Get license information for display.
315
- *
316
- * @returns {{ state: string, features: string[], inGrace: boolean } | null}
317
- */
318
- getLicenseInfo() {
319
- this._loadCache();
320
-
321
- if (!this._cache) {
322
- return null;
323
- }
324
-
325
- return {
326
- state: getLicenseState(this._cache),
327
- features: Array.from(this._licensedFeatures),
328
- inGrace: isInGracePeriod(this._cache),
329
- isExpired: isExpired(this._cache),
330
- };
331
- }
332
-
333
- /**
334
- * Clear internal state (for testing).
335
- *
336
- * @private
337
- */
338
- _reset() {
339
- this._cache = null;
340
- this._licensedFeatures.clear();
341
- this._registeredFeatures.clear();
342
- this._lastLoadTime = null;
343
- this._cacheLoaded = false;
344
- this._registryLoaded = false;
345
- }
346
- }
347
-
348
- // Singleton instance
349
- const featureGate = new FeatureGate();
350
-
351
- module.exports = {
352
- FeatureGate,
353
- featureGate,
354
- };
1
+ /**
2
+ * Feature Gate Module
3
+ *
4
+ * Singleton class that validates feature availability based on license cache.
5
+ * Supports exact feature ID matching and wildcard patterns.
6
+ *
7
+ * Usage:
8
+ * const { featureGate } = require('./feature-gate');
9
+ *
10
+ * // Check availability
11
+ * if (featureGate.isAvailable('pro.squads.premium')) {
12
+ * // Feature is licensed
13
+ * }
14
+ *
15
+ * // Require feature (throws if not available)
16
+ * featureGate.require('pro.squads.premium', 'Premium Squads');
17
+ *
18
+ * @module pro/license/feature-gate
19
+ * @see ADR-PRO-003 - Feature Gating & Licensing
20
+ * @see Story PRO-6 - License Key & Feature Gating System
21
+ */
22
+
23
+ 'use strict';
24
+
25
+ const fs = require('fs');
26
+ const path = require('path');
27
+ const yaml = require('yaml');
28
+ const { readLicenseCache, isExpired, isInGracePeriod, getLicenseState } = require('./license-cache');
29
+ const { ProFeatureError } = require('./errors');
30
+
31
+ /**
32
+ * Path to the feature registry YAML file.
33
+ */
34
+ const REGISTRY_PATH = path.join(__dirname, '..', 'feature-registry.yaml');
35
+
36
+ /**
37
+ * FeatureGate class - validates feature availability.
38
+ *
39
+ * Singleton pattern ensures consistent state across the application.
40
+ */
41
+ class FeatureGate {
42
+ constructor() {
43
+ this._cache = null;
44
+ this._licensedFeatures = new Set();
45
+ this._registeredFeatures = new Map();
46
+ this._lastLoadTime = null;
47
+ this._cacheLoaded = false;
48
+ this._registryLoaded = false;
49
+ }
50
+
51
+ /**
52
+ * Load the feature registry from YAML.
53
+ *
54
+ * @private
55
+ */
56
+ _loadRegistry() {
57
+ if (this._registryLoaded) {
58
+ return;
59
+ }
60
+
61
+ try {
62
+ if (fs.existsSync(REGISTRY_PATH)) {
63
+ const content = fs.readFileSync(REGISTRY_PATH, 'utf8');
64
+ const registry = yaml.parse(content);
65
+
66
+ // Extract all features from modules
67
+ const modules = ['squads', 'memory', 'metrics', 'integrations', 'agents', 'cli', 'config'];
68
+
69
+ for (const module of modules) {
70
+ if (registry[module] && Array.isArray(registry[module])) {
71
+ for (const feature of registry[module]) {
72
+ this._registeredFeatures.set(feature.id, {
73
+ id: feature.id,
74
+ name: feature.name,
75
+ description: feature.description,
76
+ module: feature.module,
77
+ });
78
+ }
79
+ }
80
+ }
81
+
82
+ this._registryLoaded = true;
83
+ }
84
+ } catch {
85
+ // Registry load failed, features will still work via license patterns
86
+ this._registryLoaded = true;
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Load the license cache and populate licensed features.
92
+ *
93
+ * @private
94
+ * @param {boolean} [force=false] - Force reload even if already loaded
95
+ */
96
+ _loadCache(force = false) {
97
+ if (this._cacheLoaded && !force) {
98
+ return;
99
+ }
100
+
101
+ // Load registry if not loaded
102
+ this._loadRegistry();
103
+
104
+ // Read license cache
105
+ this._cache = readLicenseCache();
106
+ this._licensedFeatures.clear();
107
+ this._lastLoadTime = Date.now();
108
+
109
+ if (!this._cache) {
110
+ this._cacheLoaded = true;
111
+ return;
112
+ }
113
+
114
+ // Check if cache is valid (not expired, or in grace period)
115
+ const state = getLicenseState(this._cache);
116
+
117
+ if (state === 'Expired') {
118
+ // Expired and past grace period - no features available
119
+ // Keep cache reference so getLicenseState can report 'Expired'
120
+ this._cacheLoaded = true;
121
+ return;
122
+ }
123
+
124
+ // Populate licensed features from cache
125
+ if (this._cache.features && Array.isArray(this._cache.features)) {
126
+ for (const featurePattern of this._cache.features) {
127
+ this._licensedFeatures.add(featurePattern);
128
+ }
129
+ }
130
+
131
+ this._cacheLoaded = true;
132
+ }
133
+
134
+ /**
135
+ * Check if a feature pattern matches a feature ID using wildcard matching.
136
+ *
137
+ * Supports:
138
+ * - Exact match: "pro.squads.premium" matches "pro.squads.premium"
139
+ * - Wildcard: "pro.squads.*" matches "pro.squads.premium", "pro.squads.custom"
140
+ * - Module wildcard: "pro.*" matches any pro feature
141
+ *
142
+ * @private
143
+ * @param {string} featureId - Feature ID to check
144
+ * @param {string} pattern - Pattern from license
145
+ * @returns {boolean} true if pattern matches feature ID
146
+ */
147
+ _matchWildcard(featureId, pattern) {
148
+ // Exact match
149
+ if (pattern === featureId) {
150
+ return true;
151
+ }
152
+
153
+ // Wildcard match: "pro.*" matches "pro.squads.premium"
154
+ if (pattern.endsWith('.*')) {
155
+ const prefix = pattern.slice(0, -1); // Remove trailing * → "pro."
156
+ return featureId.startsWith(prefix);
157
+ }
158
+
159
+ // BUG-7 fix (INS-1): Bare module pattern without dots (e.g., "pro")
160
+ // should match all features under that module (equivalent to "pro.*")
161
+ if (!pattern.includes('.') && featureId.startsWith(pattern + '.')) {
162
+ return true;
163
+ }
164
+
165
+ return false;
166
+ }
167
+
168
+ /**
169
+ * Check if a feature is available (licensed).
170
+ *
171
+ * Performance: < 5ms (cache read only, per AC-13)
172
+ *
173
+ * @param {string} featureId - Feature ID to check (e.g., "pro.squads.premium")
174
+ * @returns {boolean} true if feature is licensed and available
175
+ */
176
+ isAvailable(featureId) {
177
+ this._loadCache();
178
+
179
+ if (this._licensedFeatures.size === 0) {
180
+ return false;
181
+ }
182
+
183
+ // Check direct match first (O(1))
184
+ if (this._licensedFeatures.has(featureId)) {
185
+ return true;
186
+ }
187
+
188
+ // Check wildcard patterns
189
+ for (const pattern of this._licensedFeatures) {
190
+ if (this._matchWildcard(featureId, pattern)) {
191
+ return true;
192
+ }
193
+ }
194
+
195
+ return false;
196
+ }
197
+
198
+ /**
199
+ * Require a feature to be available, throw if not.
200
+ *
201
+ * Use this to gate premium functionality:
202
+ *
203
+ * featureGate.require('pro.squads.premium', 'Premium Squads');
204
+ *
205
+ * @param {string} featureId - Feature ID to require
206
+ * @param {string} [friendlyName] - Human-friendly feature name for error message
207
+ * @throws {ProFeatureError} If feature is not licensed
208
+ */
209
+ require(featureId, friendlyName) {
210
+ if (!this.isAvailable(featureId)) {
211
+ const name = friendlyName || this._getFeatureName(featureId) || featureId;
212
+ throw new ProFeatureError(featureId, name);
213
+ }
214
+ }
215
+
216
+ /**
217
+ * Get the friendly name of a feature from registry.
218
+ *
219
+ * @private
220
+ * @param {string} featureId - Feature ID
221
+ * @returns {string|null} Feature name or null
222
+ */
223
+ _getFeatureName(featureId) {
224
+ this._loadRegistry();
225
+ const feature = this._registeredFeatures.get(featureId);
226
+ return feature ? feature.name : null;
227
+ }
228
+
229
+ /**
230
+ * List all available (licensed) feature IDs.
231
+ *
232
+ * Returns the resolved list of feature IDs, expanding wildcards
233
+ * against the registry.
234
+ *
235
+ * @returns {string[]} Array of available feature IDs
236
+ */
237
+ listAvailable() {
238
+ this._loadCache();
239
+ this._loadRegistry();
240
+
241
+ const available = new Set();
242
+
243
+ // Check each registered feature against licensed patterns
244
+ for (const [featureId] of this._registeredFeatures) {
245
+ if (this.isAvailable(featureId)) {
246
+ available.add(featureId);
247
+ }
248
+ }
249
+
250
+ return Array.from(available).sort();
251
+ }
252
+
253
+ /**
254
+ * List all registered features with their availability status.
255
+ *
256
+ * @returns {Array<{ id: string, name: string, description: string, module: string, available: boolean }>}
257
+ */
258
+ listAll() {
259
+ this._loadCache();
260
+ this._loadRegistry();
261
+
262
+ const features = [];
263
+
264
+ for (const [featureId, feature] of this._registeredFeatures) {
265
+ features.push({
266
+ ...feature,
267
+ available: this.isAvailable(featureId),
268
+ });
269
+ }
270
+
271
+ return features.sort((a, b) => a.id.localeCompare(b.id));
272
+ }
273
+
274
+ /**
275
+ * List features grouped by module.
276
+ *
277
+ * @returns {Object<string, Array<{ id: string, name: string, available: boolean }>>}
278
+ */
279
+ listByModule() {
280
+ const all = this.listAll();
281
+ const grouped = {};
282
+
283
+ for (const feature of all) {
284
+ if (!grouped[feature.module]) {
285
+ grouped[feature.module] = [];
286
+ }
287
+ grouped[feature.module].push(feature);
288
+ }
289
+
290
+ return grouped;
291
+ }
292
+
293
+ /**
294
+ * Force reload of the license cache.
295
+ *
296
+ * Call this after activation to pick up new features.
297
+ */
298
+ reload() {
299
+ this._cacheLoaded = false;
300
+ this._loadCache(true);
301
+ }
302
+
303
+ /**
304
+ * Get the current license state.
305
+ *
306
+ * @returns {'Active'|'Expired'|'Grace'|'Not Activated'} License state
307
+ */
308
+ getLicenseState() {
309
+ this._loadCache();
310
+ return getLicenseState(this._cache);
311
+ }
312
+
313
+ /**
314
+ * Get license information for display.
315
+ *
316
+ * @returns {{ state: string, features: string[], inGrace: boolean } | null}
317
+ */
318
+ getLicenseInfo() {
319
+ this._loadCache();
320
+
321
+ if (!this._cache) {
322
+ return null;
323
+ }
324
+
325
+ return {
326
+ state: getLicenseState(this._cache),
327
+ features: Array.from(this._licensedFeatures),
328
+ inGrace: isInGracePeriod(this._cache),
329
+ isExpired: isExpired(this._cache),
330
+ };
331
+ }
332
+
333
+ /**
334
+ * Clear internal state (for testing).
335
+ *
336
+ * @private
337
+ */
338
+ _reset() {
339
+ this._cache = null;
340
+ this._licensedFeatures.clear();
341
+ this._registeredFeatures.clear();
342
+ this._lastLoadTime = null;
343
+ this._cacheLoaded = false;
344
+ this._registryLoaded = false;
345
+ }
346
+ }
347
+
348
+ // Singleton instance
349
+ const featureGate = new FeatureGate();
350
+
351
+ module.exports = {
352
+ FeatureGate,
353
+ featureGate,
354
+ };