@principles/pd-cli 1.129.0 → 1.130.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.
- package/dist/commands/runtime-canary.d.ts.map +1 -1
- package/dist/commands/runtime-canary.js +10 -11
- package/dist/commands/runtime-canary.js.map +1 -1
- package/dist/commands/runtime-internalization-queue.d.ts.map +1 -1
- package/dist/commands/runtime-internalization-queue.js +13 -16
- package/dist/commands/runtime-internalization-queue.js.map +1 -1
- package/dist/services/__tests__/runtime-adapter-resolver.test.js +20 -12
- package/dist/services/__tests__/runtime-adapter-resolver.test.js.map +1 -1
- package/dist/services/runtime-adapter-resolver.d.ts +1 -1
- package/dist/services/runtime-adapter-resolver.js +3 -3
- package/dist/services/runtime-adapter-resolver.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/runtime-canary.ts +11 -13
- package/src/commands/runtime-internalization-queue.ts +14 -19
- package/src/services/__tests__/runtime-adapter-resolver.test.ts +20 -12
- package/src/services/runtime-adapter-resolver.ts +3 -3
- package/tests/commands/runtime-canary.test.ts +46 -36
- package/tests/commands/runtime-internalization-queue.test.ts +11 -24
- package/tests/commands/runtime-internalization-run-once.test.ts +14 -6
- package/dist/services/feature-flag-loader.d.ts +0 -6
- package/dist/services/feature-flag-loader.d.ts.map +0 -1
- package/dist/services/feature-flag-loader.js +0 -53
- package/dist/services/feature-flag-loader.js.map +0 -1
- package/src/services/feature-flag-loader.ts +0 -73
- package/tests/services/feature-flag-loader.test.ts +0 -207
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import * as fs from 'node:fs';
|
|
3
|
-
import * as path from 'node:path';
|
|
4
|
-
import * as os from 'node:os';
|
|
5
|
-
import {
|
|
6
|
-
loadEffectiveFeatureFlags,
|
|
7
|
-
getFeatureFlagsConfigPath,
|
|
8
|
-
FEATURE_FLAGS_CONFIG_FILENAME,
|
|
9
|
-
FEATURE_FLAGS_CONFIG_DIR,
|
|
10
|
-
} from '../../src/services/feature-flag-loader.js';
|
|
11
|
-
|
|
12
|
-
function createTempWorkspace(): string {
|
|
13
|
-
return fs.mkdtempSync(path.join(os.tmpdir(), 'pd-ffl-test-'));
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function writeConfig(tmpDir: string, content: string): void {
|
|
17
|
-
const configDir = path.join(tmpDir, FEATURE_FLAGS_CONFIG_DIR);
|
|
18
|
-
fs.mkdirSync(configDir, { recursive: true });
|
|
19
|
-
fs.writeFileSync(path.join(configDir, FEATURE_FLAGS_CONFIG_FILENAME), content, 'utf8');
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function cleanup(tmpDir: string): void {
|
|
23
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
describe('feature-flag-loader', () => {
|
|
27
|
-
describe('getFeatureFlagsConfigPath', () => {
|
|
28
|
-
it('should return path under .pd/feature-flags.yaml', () => {
|
|
29
|
-
const result = getFeatureFlagsConfigPath('/workspace/project');
|
|
30
|
-
expect(result).toBe(path.join('/workspace/project', '.pd', 'feature-flags.yaml'));
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it('should use FEATURE_FLAGS_CONFIG_DIR and FEATURE_FLAGS_CONFIG_FILENAME constants', () => {
|
|
34
|
-
const result = getFeatureFlagsConfigPath('/root');
|
|
35
|
-
expect(result).toContain(FEATURE_FLAGS_CONFIG_DIR);
|
|
36
|
-
expect(result).toContain(FEATURE_FLAGS_CONFIG_FILENAME);
|
|
37
|
-
});
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
describe('loadEffectiveFeatureFlags', () => {
|
|
41
|
-
it('should return defaults when config file does not exist', () => {
|
|
42
|
-
const tmpDir = createTempWorkspace();
|
|
43
|
-
try {
|
|
44
|
-
const result = loadEffectiveFeatureFlags(tmpDir);
|
|
45
|
-
expect(result.source).toBe('defaults');
|
|
46
|
-
expect(result.warnings).toEqual([]);
|
|
47
|
-
} finally {
|
|
48
|
-
cleanup(tmpDir);
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('should return defaults with YAML parse error warning for malformed YAML', () => {
|
|
53
|
-
const tmpDir = createTempWorkspace();
|
|
54
|
-
writeConfig(tmpDir, 'gfi: [unterminated');
|
|
55
|
-
try {
|
|
56
|
-
const result = loadEffectiveFeatureFlags(tmpDir);
|
|
57
|
-
expect(result.warnings.some(w => w.includes('YAML parse error'))).toBe(true);
|
|
58
|
-
} finally {
|
|
59
|
-
cleanup(tmpDir);
|
|
60
|
-
}
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('should return defaults with mapping warning when YAML contains an array', () => {
|
|
64
|
-
const tmpDir = createTempWorkspace();
|
|
65
|
-
writeConfig(tmpDir, '- item1\n- item2\n');
|
|
66
|
-
try {
|
|
67
|
-
const result = loadEffectiveFeatureFlags(tmpDir);
|
|
68
|
-
expect(result.warnings.some(w => w.includes('expected a mapping'))).toBe(true);
|
|
69
|
-
} finally {
|
|
70
|
-
cleanup(tmpDir);
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it('should return defaults with mapping warning when YAML contains null', () => {
|
|
75
|
-
const tmpDir = createTempWorkspace();
|
|
76
|
-
writeConfig(tmpDir, '---\nnull\n');
|
|
77
|
-
try {
|
|
78
|
-
const result = loadEffectiveFeatureFlags(tmpDir);
|
|
79
|
-
expect(result.warnings.some(w => w.includes('expected a mapping'))).toBe(true);
|
|
80
|
-
} finally {
|
|
81
|
-
cleanup(tmpDir);
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it('should return defaults with mapping warning when YAML is undefined after parse', () => {
|
|
86
|
-
const tmpDir = createTempWorkspace();
|
|
87
|
-
writeConfig(tmpDir, '---\n');
|
|
88
|
-
try {
|
|
89
|
-
const result = loadEffectiveFeatureFlags(tmpDir);
|
|
90
|
-
expect(result.source).toBe('defaults');
|
|
91
|
-
expect(result.warnings.some(w => w.includes('expected a mapping'))).toBe(true);
|
|
92
|
-
} finally {
|
|
93
|
-
cleanup(tmpDir);
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it('should return defaults with mapping warning when YAML contains a scalar string', () => {
|
|
98
|
-
const tmpDir = createTempWorkspace();
|
|
99
|
-
writeConfig(tmpDir, 'just a string\n');
|
|
100
|
-
try {
|
|
101
|
-
const result = loadEffectiveFeatureFlags(tmpDir);
|
|
102
|
-
expect(result.warnings.some(w => w.includes('expected a mapping'))).toBe(true);
|
|
103
|
-
} finally {
|
|
104
|
-
cleanup(tmpDir);
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it('should return defaults with mapping warning when YAML contains a number', () => {
|
|
109
|
-
const tmpDir = createTempWorkspace();
|
|
110
|
-
writeConfig(tmpDir, '42\n');
|
|
111
|
-
try {
|
|
112
|
-
const result = loadEffectiveFeatureFlags(tmpDir);
|
|
113
|
-
expect(result.warnings.some(w => w.includes('expected a mapping'))).toBe(true);
|
|
114
|
-
} finally {
|
|
115
|
-
cleanup(tmpDir);
|
|
116
|
-
}
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it('should reject __proto__ dangerous key', () => {
|
|
120
|
-
const tmpDir = createTempWorkspace();
|
|
121
|
-
writeConfig(tmpDir, '"__proto__":\n enabled: true\n');
|
|
122
|
-
try {
|
|
123
|
-
const result = loadEffectiveFeatureFlags(tmpDir);
|
|
124
|
-
expect(result.warnings.some(w => w.includes("dangerous key '__proto__' rejected"))).toBe(true);
|
|
125
|
-
expect(Object.hasOwn(result.flags, '__proto__')).toBe(false);
|
|
126
|
-
} finally {
|
|
127
|
-
cleanup(tmpDir);
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
it('should reject constructor dangerous key', () => {
|
|
132
|
-
const tmpDir = createTempWorkspace();
|
|
133
|
-
writeConfig(tmpDir, 'constructor:\n enabled: true\n');
|
|
134
|
-
try {
|
|
135
|
-
const result = loadEffectiveFeatureFlags(tmpDir);
|
|
136
|
-
expect(result.warnings.some(w => w.includes("dangerous key 'constructor' rejected"))).toBe(true);
|
|
137
|
-
expect(Object.hasOwn(result.flags, 'constructor')).toBe(false);
|
|
138
|
-
} finally {
|
|
139
|
-
cleanup(tmpDir);
|
|
140
|
-
}
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
it('should reject prototype dangerous key', () => {
|
|
144
|
-
const tmpDir = createTempWorkspace();
|
|
145
|
-
writeConfig(tmpDir, 'prototype:\n enabled: true\n');
|
|
146
|
-
try {
|
|
147
|
-
const result = loadEffectiveFeatureFlags(tmpDir);
|
|
148
|
-
expect(result.warnings.some(w => w.includes("dangerous key 'prototype' rejected"))).toBe(true);
|
|
149
|
-
expect(Object.hasOwn(result.flags, 'prototype')).toBe(false);
|
|
150
|
-
} finally {
|
|
151
|
-
cleanup(tmpDir);
|
|
152
|
-
}
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
it('should allow valid keys while rejecting dangerous ones', () => {
|
|
156
|
-
const tmpDir = createTempWorkspace();
|
|
157
|
-
writeConfig(tmpDir, 'gfi:\n enabled: true\nprototype:\n enabled: true\n');
|
|
158
|
-
try {
|
|
159
|
-
const result = loadEffectiveFeatureFlags(tmpDir);
|
|
160
|
-
expect(result.warnings.some(w => w.includes("dangerous key 'prototype' rejected"))).toBe(true);
|
|
161
|
-
expect(result.flags.gfi).toBeDefined();
|
|
162
|
-
if (result.flags.gfi) {
|
|
163
|
-
expect(result.flags.gfi.enabled).toBe(true);
|
|
164
|
-
}
|
|
165
|
-
} finally {
|
|
166
|
-
cleanup(tmpDir);
|
|
167
|
-
}
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
it('should parse valid YAML config and set source to workspace_file', () => {
|
|
171
|
-
const tmpDir = createTempWorkspace();
|
|
172
|
-
writeConfig(tmpDir, 'gfi:\n enabled: true\n');
|
|
173
|
-
try {
|
|
174
|
-
const result = loadEffectiveFeatureFlags(tmpDir);
|
|
175
|
-
expect(result.source).toBe('workspace_file');
|
|
176
|
-
expect(result.flags.gfi).toBeDefined();
|
|
177
|
-
if (result.flags.gfi) {
|
|
178
|
-
expect(result.flags.gfi.enabled).toBe(true);
|
|
179
|
-
}
|
|
180
|
-
} finally {
|
|
181
|
-
cleanup(tmpDir);
|
|
182
|
-
}
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
it('should handle empty YAML mapping with defaults source', () => {
|
|
186
|
-
const tmpDir = createTempWorkspace();
|
|
187
|
-
writeConfig(tmpDir, '{}\n');
|
|
188
|
-
try {
|
|
189
|
-
const result = loadEffectiveFeatureFlags(tmpDir);
|
|
190
|
-
expect(result.source).toBe('defaults');
|
|
191
|
-
expect(result.warnings).toEqual([]);
|
|
192
|
-
} finally {
|
|
193
|
-
cleanup(tmpDir);
|
|
194
|
-
}
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
it('should include configPath in result', () => {
|
|
198
|
-
const tmpDir = createTempWorkspace();
|
|
199
|
-
try {
|
|
200
|
-
const result = loadEffectiveFeatureFlags(tmpDir);
|
|
201
|
-
expect(result.configPath).toContain(FEATURE_FLAGS_CONFIG_FILENAME);
|
|
202
|
-
} finally {
|
|
203
|
-
cleanup(tmpDir);
|
|
204
|
-
}
|
|
205
|
-
});
|
|
206
|
-
});
|
|
207
|
-
});
|