@quonfig/cli 0.0.50 → 0.0.51

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.
@@ -0,0 +1,282 @@
1
+ /**
2
+ * Clean typed structs for the slice of the Flagsmith REST API the Phase-1
3
+ * config-snapshot fetcher walks. Field names mirror Flagsmith's JSON exactly;
4
+ * optional fields are the ones Flagsmith omits when unset.
5
+ *
6
+ * Reference: https://api.flagsmith.com/api/v1/swagger.json — `GET
7
+ * /projects/{id}/`, `GET /environments/?project=`, `GET
8
+ * /projects/{pk}/features/`, `GET /features/featurestates/?environment=`, `GET
9
+ * /projects/{pk}/segments/`, `GET /features/feature-segments/?environment=`,
10
+ * `GET /environments/{api_key}/edge-identities/`, `GET
11
+ * /environments/{api_key}/edge-identity-overrides`, `GET
12
+ * /projects/{pk}/tags/`.
13
+ *
14
+ * Verified empirically against the live `Test1` project on 2026-05-17 — see
15
+ * `competitor-flagsmith/fixtures/CORPUS.md` API1..API9 for the quirks the
16
+ * structural docs don't capture.
17
+ */
18
+ /** Django REST style pagination envelope: `{count, next, previous, results}`. */
19
+ export interface FlagsmithPaginated<T> {
20
+ count: number;
21
+ next: null | string;
22
+ previous: null | string;
23
+ results: T[];
24
+ }
25
+ /** Edge-identity listing uses cursor pagination via `last_evaluated_key`, not page numbers. */
26
+ export interface FlagsmithEdgeIdentityListResponse {
27
+ last_evaluated_key: null | string;
28
+ results: FlagsmithEdgeIdentity[];
29
+ }
30
+ /** `GET /projects/{id}/` — project metadata, including the edge-identity branch flag. */
31
+ export interface FlagsmithProject {
32
+ enable_dynamo_db?: boolean;
33
+ enable_realtime_updates?: boolean;
34
+ feature_name_regex?: null | string;
35
+ hide_disabled_flags?: boolean;
36
+ id: number;
37
+ migration_status?: string;
38
+ name: string;
39
+ only_allow_lower_case_feature_names?: boolean;
40
+ organisation: number;
41
+ prevent_flag_defaults?: boolean;
42
+ total_features?: number;
43
+ total_segments?: number;
44
+ /** If true, identities live under `/edge-identities/` (per-env UUIDs); else under `/identities/`. */
45
+ use_edge_identities: boolean;
46
+ /** Affects WRITE-side paths only — read endpoints work the same on v1 and v2 envs. */
47
+ use_v2_feature_versioning?: boolean;
48
+ uuid: string;
49
+ }
50
+ /** `GET /environments/?project={id}` — env list, paginated. */
51
+ export interface FlagsmithEnvironment {
52
+ /** Stable per-env identifier the SDK uses; also threaded into URL paths as `{environment_api_key}`. */
53
+ api_key: string;
54
+ description?: null | string;
55
+ id: number;
56
+ name: string;
57
+ project: number;
58
+ use_v2_feature_versioning?: boolean;
59
+ uuid: string;
60
+ }
61
+ /** One row of a feature's `multivariate_options[]` (inline on the feature resource). */
62
+ export interface FlagsmithMultivariateOption {
63
+ boolean_value: boolean | null;
64
+ /** Project-level default weight — per-env overrides live on the featurestate's `multivariate_feature_state_values`. */
65
+ default_percentage_allocation: number;
66
+ id: number;
67
+ integer_value: integer | null;
68
+ string_value: null | string;
69
+ /** Discriminator: `unicode` (string), `int`, `bool`. */
70
+ type: string;
71
+ uuid: string;
72
+ }
73
+ type integer = number;
74
+ /** `GET /projects/{pk}/features/` row. Variations are inline as `multivariate_options[]`. */
75
+ export interface FlagsmithFeature {
76
+ created_date?: string;
77
+ default_enabled: boolean;
78
+ description?: null | string;
79
+ group_owners?: number[];
80
+ id: number;
81
+ initial_value?: null | string;
82
+ is_archived: boolean;
83
+ is_server_key_only?: boolean;
84
+ /**
85
+ * **API6 caveat**: the live API returns these in REVERSE creation order. The
86
+ * fetcher sorts by `id` ascending before exposing the feature to the
87
+ * converter, so by the time `LegacyChange.raw` is consumed the order matches
88
+ * definition order.
89
+ */
90
+ multivariate_options: FlagsmithMultivariateOption[];
91
+ name: string;
92
+ num_identity_overrides?: integer | null;
93
+ num_segment_overrides?: integer;
94
+ owners?: number[];
95
+ project: number;
96
+ tags: number[];
97
+ /** `STANDARD` for boolean/value flags; `MULTIVARIATE` when MV options are populated. */
98
+ type: string;
99
+ uuid: string;
100
+ }
101
+ /**
102
+ * The envelope shape for `feature_state_value` returned by
103
+ * `/features/featurestates/?environment=` (the endpoint the fetcher uses for
104
+ * per-env featurestates). Reads on the legacy `/environments/{api_key}/featurestates/`
105
+ * endpoint return a scalar instead; we deliberately do not use that endpoint
106
+ * because it does not surface segment-override rows on v2-versioned envs.
107
+ */
108
+ export interface FlagsmithFeatureStateValueEnvelope {
109
+ boolean_value: boolean | null;
110
+ integer_value: integer | null;
111
+ string_value: null | string;
112
+ type: string;
113
+ }
114
+ /** One inline MV weight on a per-env featurestate (overrides the project-level default allocation). */
115
+ export interface FlagsmithMultivariateFeatureStateValue {
116
+ id?: integer;
117
+ multivariate_feature_option: integer;
118
+ percentage_allocation: number;
119
+ uuid?: string;
120
+ }
121
+ /**
122
+ * A row from `GET /features/featurestates/?environment={id}` — the unified
123
+ * per-env featurestate listing. Includes env-default rows (`feature_segment ==
124
+ * null && identity == null`) AND segment-override rows (`feature_segment !=
125
+ * null`). Identity overrides are NOT in this listing on edge-enabled projects
126
+ * — see `/edge-identity-overrides` instead.
127
+ */
128
+ export interface FlagsmithFeatureState {
129
+ change_request?: integer | null;
130
+ created_at?: string;
131
+ enabled: boolean;
132
+ environment: integer | null;
133
+ /** Versions-API uuid; non-null on v2-versioned envs. Same value for env-default and segment-override rows on the same live version. */
134
+ environment_feature_version?: null | string;
135
+ feature: integer;
136
+ /**
137
+ * Inline feature_segment shape (id + segment id + priority + uuid) on the
138
+ * `/features/featurestates/` endpoint, else just the numeric ID. Null when
139
+ * this row is the env-default featurestate.
140
+ */
141
+ feature_segment: FlagsmithFeatureSegmentInline | integer | null;
142
+ /** Envelope shape on `/features/featurestates/`; scalar on legacy endpoints. */
143
+ feature_state_value: FlagsmithFeatureStateValueEnvelope | boolean | integer | null | string;
144
+ id: integer;
145
+ identity?: integer | null;
146
+ live_from?: null | string;
147
+ multivariate_feature_state_values: FlagsmithMultivariateFeatureStateValue[];
148
+ updated_at?: string;
149
+ uuid: string;
150
+ version?: integer | null;
151
+ }
152
+ /** Compact feature_segment payload inlined on featurestate rows. */
153
+ export interface FlagsmithFeatureSegmentInline {
154
+ id: integer;
155
+ priority: integer;
156
+ segment: integer;
157
+ uuid: string;
158
+ }
159
+ /** One condition inside a segment rule (siblings AND together by default). */
160
+ export interface FlagsmithSegmentCondition {
161
+ description?: null | string;
162
+ id?: integer;
163
+ /** Discriminator. 14-constant `ConditionOperatorEnum`; see plan §5.3. */
164
+ operator: string;
165
+ property?: null | string;
166
+ value?: null | string;
167
+ }
168
+ /** One rule inside a segment. Sibling rules under a parent rule AND or OR per `type`. */
169
+ export interface FlagsmithSegmentRule {
170
+ conditions: FlagsmithSegmentCondition[];
171
+ id?: integer;
172
+ rules: FlagsmithSegmentRule[];
173
+ /** `ALL` (AND), `ANY` (OR), `NONE` (NOT-ANY). */
174
+ type: string;
175
+ }
176
+ /** `GET /projects/{pk}/segments/` row. Project-scoped (NOT per-env). */
177
+ export interface FlagsmithSegment {
178
+ created_at?: null | string;
179
+ description?: null | string;
180
+ /** Non-null on "feature-specific" segments (created via the per-feature segment-override flow). */
181
+ feature?: integer | null;
182
+ id: integer;
183
+ name: string;
184
+ project: integer;
185
+ rules: FlagsmithSegmentRule[];
186
+ updated_at?: null | string;
187
+ uuid: string;
188
+ version_of?: integer | null;
189
+ }
190
+ /**
191
+ * `GET /features/feature-segments/?environment={id}&feature={fid}` row —
192
+ * gives a segment's priority for one feature in one env. Used for
193
+ * priority ordering when stitching segment-override rules.
194
+ */
195
+ export interface FlagsmithFeatureSegment {
196
+ environment: integer;
197
+ id: integer;
198
+ is_feature_specific?: boolean;
199
+ priority: integer;
200
+ segment: integer;
201
+ segment_name?: string;
202
+ uuid: string;
203
+ }
204
+ /** `GET /environments/{api_key}/edge-identities/` row. Per-env `identity_uuid`; identifier is stable. */
205
+ export interface FlagsmithEdgeIdentity {
206
+ dashboard_alias?: null | string;
207
+ identifier: string;
208
+ /** **API8 caveat**: this differs PER ENVIRONMENT for the same `identifier`. */
209
+ identity_uuid: string;
210
+ }
211
+ /**
212
+ * One row from `GET /environments/{api_key}/edge-identity-overrides` — listed
213
+ * across all identities in one env. Same shape as the per-identity
214
+ * `edge-featurestates` rows.
215
+ */
216
+ export interface FlagsmithEdgeIdentityOverride {
217
+ feature_state: FlagsmithEdgeIdentityFeatureState;
218
+ identifier: string;
219
+ identity_uuid: string;
220
+ }
221
+ /** Edge-identity featurestate read shape. Note: `feature_state_value` is SCALAR here, not envelope (API5). */
222
+ export interface FlagsmithEdgeIdentityFeatureState {
223
+ enabled: boolean;
224
+ feature: integer;
225
+ feature_state_value: boolean | integer | null | string;
226
+ featurestate_uuid?: string;
227
+ identity_uuid?: string;
228
+ multivariate_feature_state_values: FlagsmithMultivariateFeatureStateValue[];
229
+ }
230
+ /** `GET /projects/{pk}/tags/` row. Pure metadata; the migrator emits string labels into Quonfig `tags[]`. */
231
+ export interface FlagsmithTag {
232
+ color?: string;
233
+ description?: null | string;
234
+ id: integer;
235
+ is_permanent?: boolean;
236
+ is_system_tag?: boolean;
237
+ label: string;
238
+ project: integer;
239
+ type?: string;
240
+ uuid: string;
241
+ }
242
+ /**
243
+ * The full Phase-1 config snapshot — what the fetcher stitches together and
244
+ * what `translate()` consumes. One of these is produced per migration run.
245
+ *
246
+ * Indexed by `env.api_key` rather than env name because (a) one of the two
247
+ * `featurestates_by_env`/`feature_segments_by_env` maps below would otherwise
248
+ * disagree about the key, and (b) api_key is the path-segment Flagsmith uses
249
+ * in every per-env URL, so it's the canonical handle.
250
+ */
251
+ export interface FlagsmithSnapshot {
252
+ /** Environments in API order. */
253
+ environments: FlagsmithEnvironment[];
254
+ /** Per-feature stitched view of every featurestate (env-default + segment-overrides + identity-overrides). */
255
+ features: FlagsmithFeatureWithStates[];
256
+ project: FlagsmithProject;
257
+ /** Project-scoped segment pool, referenced by `feature_segments_by_env[*].segment`. */
258
+ segments: FlagsmithSegment[];
259
+ tags: FlagsmithTag[];
260
+ }
261
+ /**
262
+ * One feature stitched with all of its per-env state. This is the shape that
263
+ * goes into `LegacyChange.raw` as the per-feature payload.
264
+ */
265
+ export interface FlagsmithFeatureWithStates {
266
+ /** The feature record itself (mv_options sorted asc by id — API6). */
267
+ feature: FlagsmithFeature;
268
+ /** Per-env priority + segment-id rows for this feature's segment-overrides. Keyed by env api_key. */
269
+ feature_segments_by_env: Record<string, FlagsmithFeatureSegment[]>;
270
+ /** Per-env featurestates: env-default + segment-overrides + identity-overrides. Keyed by env api_key. */
271
+ featurestates_by_env: Record<string, FlagsmithFeatureStateBundle>;
272
+ }
273
+ /** Per-env featurestate bundle: env-default + segment-overrides + identity-overrides. */
274
+ export interface FlagsmithFeatureStateBundle {
275
+ /** The env-level default featurestate (`feature_segment == null && identity == null`). May be absent on partial-fetch errors. */
276
+ default: FlagsmithFeatureState | null;
277
+ /** Edge-identity overrides for this feature in this env. SCALAR `feature_state_value` (API5). */
278
+ identity_overrides: FlagsmithEdgeIdentityOverride[];
279
+ /** Segment-override featurestates (`feature_segment != null`), ordered ASC by priority via the feature_segments lookup. */
280
+ segment_overrides: FlagsmithFeatureState[];
281
+ }
282
+ export {};
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Clean typed structs for the slice of the Flagsmith REST API the Phase-1
3
+ * config-snapshot fetcher walks. Field names mirror Flagsmith's JSON exactly;
4
+ * optional fields are the ones Flagsmith omits when unset.
5
+ *
6
+ * Reference: https://api.flagsmith.com/api/v1/swagger.json — `GET
7
+ * /projects/{id}/`, `GET /environments/?project=`, `GET
8
+ * /projects/{pk}/features/`, `GET /features/featurestates/?environment=`, `GET
9
+ * /projects/{pk}/segments/`, `GET /features/feature-segments/?environment=`,
10
+ * `GET /environments/{api_key}/edge-identities/`, `GET
11
+ * /environments/{api_key}/edge-identity-overrides`, `GET
12
+ * /projects/{pk}/tags/`.
13
+ *
14
+ * Verified empirically against the live `Test1` project on 2026-05-17 — see
15
+ * `competitor-flagsmith/fixtures/CORPUS.md` API1..API9 for the quirks the
16
+ * structural docs don't capture.
17
+ */
18
+ export {};
@@ -1,2 +1,64 @@
1
+ /**
2
+ * `qfg migrate --from flagsmith` — the MigrationSource implementation.
3
+ *
4
+ * Wires the Flagsmith fetcher (`flagsmith/api.ts` + `flagsmith/types.ts`) and
5
+ * converter (`flagsmith/translate.ts`) into the source-agnostic migration
6
+ * framework. Epic 1 implemented the read side (validateAuth, listEnvironments,
7
+ * fetchChanges yielding LegacyChange[] carrying raw Flagsmith JSON); Epic 3
8
+ * adds the translate() side — `translateFeature` and `translateSegment`
9
+ * dispatched by `raw.kind` (see `FlagsmithRaw` below). Report accumulators
10
+ * (`getSkippedConfigs`, `getCoercedSentinels`, `getConversionNotes`) thread
11
+ * the per-source notes into MIGRATION_REPORT.md.
12
+ *
13
+ * Design is frozen in `project/plans/migrator-flagsmith.md`. Notable choices
14
+ * for this epic — the contract Epic 3 will consume:
15
+ *
16
+ * - `LegacyChange.raw` is a discriminated union over `{kind: 'feature' | 'segment'}`.
17
+ * Features carry the full per-feature stitched bundle (env-default +
18
+ * segment-overrides + identity-overrides + feature-segment priority +
19
+ * inline MV options sorted by id asc). Segments are emitted separately
20
+ * (project-scoped, referenced from multiple features).
21
+ *
22
+ * - Tags are NOT separate LegacyChanges — they're inline `feature.tags[]` as
23
+ * integer IDs, plus a project-wide tag pool stashed in the source state for
24
+ * the converter to resolve id → label.
25
+ *
26
+ * - Edge identities are NOT separate LegacyChanges. Identity overrides are
27
+ * bundled inside the feature payload (`identity_overrides`); the identity
28
+ * `identifier` is already on each override row. Traits are runtime context
29
+ * the SDK sends — no server-side artifact to migrate (plan §5.5, D-F6).
30
+ *
31
+ * - Project metadata (`use_edge_identities`, `only_allow_lower_case_feature_names`,
32
+ * …) is captured during `validateAuth` and stashed in source state. The
33
+ * converter can read it via `getProjectMetadata()` (a Flagsmith-specific
34
+ * getter, not part of the MigrationSource interface).
35
+ */
36
+ import { ConversionReport } from '../quonfig-target/report.js';
1
37
  import { type MigrationSource } from '../source.js';
38
+ import type { FlagsmithFeatureWithStates, FlagsmithProject, FlagsmithSegment, FlagsmithTag } from './flagsmith/types.js';
39
+ /**
40
+ * Raw payload carried on each LegacyChange — discriminated by `kind`. Epic 3's
41
+ * `translate()` switches on this to dispatch to the right shape converter.
42
+ */
43
+ export type FlagsmithRaw = {
44
+ data: FlagsmithFeatureWithStates;
45
+ kind: 'feature';
46
+ } | {
47
+ data: FlagsmithSegment;
48
+ kind: 'segment';
49
+ };
50
+ /**
51
+ * Override the Flagsmith project ID for this run. Called by `qfg migrate` from
52
+ * its `--project` flag (or the `FLAGSMITH_PROJECT_ID` env var) — mirrors
53
+ * `setLaunchDarklyProjectKey`. Flagsmith projects are numeric, but we accept
54
+ * either a number or a numeric-string and convert at the API boundary.
55
+ */
56
+ export declare function setFlagsmithProjectId(projectId: number | string): void;
57
+ /** The project metadata captured by validateAuth, or null if we haven't authed yet. */
58
+ export declare function getFlagsmithProject(): FlagsmithProject | null;
59
+ /** The tag pool from the last snapshot — Epic 3's converter resolves `feature.tags[]` IDs to labels via this. */
60
+ export declare function getFlagsmithTagPool(): FlagsmithTag[];
61
+ /** The full conversion report — every skip/drop/coerce note from this run. */
62
+ export declare function getConversionReport(): ConversionReport;
2
63
  export declare const flagsmithSource: MigrationSource;
64
+ export declare function __resetFlagsmithSourceForTests(): void;