@ultimat3/flags 19.2.0 → 19.3.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ultimat3/flags",
3
- "version": "19.2.0",
3
+ "version": "19.3.2",
4
4
  "description": "Feature flags: permanent switches, and temporary ones that cannot be forgotten",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -31,6 +31,6 @@
31
31
  "test": "bun test"
32
32
  },
33
33
  "dependencies": {
34
- "@ultimat3/core": "19.2.0"
34
+ "@ultimat3/core": "19.3.2"
35
35
  }
36
36
  }
@@ -31,6 +31,7 @@ const LIST_EXAMPLE: Readonly<Record<IdListField, string>> = {
31
31
  * | `default: true` with a `rollout` | the two answer the same actors and disagree; there is no reading of "on for everyone, and also on for 10%" |
32
32
  * | `bucketBy` with no `rollout` | it names what a rollout divides, and there is no rollout to divide |
33
33
  * | a blank `bucketBy` | names no kind at all |
34
+ * | `subjects` that is not a map | reachable from a store snapshot; `null` reached `Object.entries` as a bare `TypeError` |
34
35
  * | `subjects.actor` / `subjects.org` | `actors` and `orgs` are the one spelling; two would disagree |
35
36
  * | a `subjects` entry that is not a list of non-empty ids | reachable from a store snapshot, and it matches nothing while reading as an allow list |
36
37
  *
@@ -133,8 +134,25 @@ function assertIdList(key: string, field: IdListField, list: unknown): void {
133
134
  }
134
135
  }
135
136
 
137
+ /**
138
+ * `subjects` is a MAP at all, established before it is walked — the check `assertObject` makes one
139
+ * level up, on the one rank it did not reach. The declared parameter type is a promise the caller
140
+ * makes and `applyFlagSnapshot` lands a store payload nobody made it for: `subjects: null` reached
141
+ * `Object.entries` as a bare `TypeError` out of the function whose whole job is a coded refusal,
142
+ * while `bucketBy: null`, `rollout: null` and `actors: null` all answered
143
+ * `X_FLAG_TARGETING_INVALID`. A bare string is the quieter half — `Object.entries('ab')` walks it
144
+ * by index, so it was reported as a kind called `0`, which describes nothing an author wrote.
145
+ */
136
146
  function assertSubjects(key: string, subjects: Readonly<Record<string, readonly string[]>>): void {
137
147
  const fix = `give each subjects entry a kind and a list of ids — { bank: ['bank_integration:bbva'] } — in defineFlag({ key: '${key}' })`;
148
+ const given: unknown = subjects;
149
+ if (typeof given !== 'object' || given === null || Array.isArray(given)) {
150
+ throw flagTargetingInvalid(
151
+ key,
152
+ `subjects is ${renderGiven(given)}, which is not a map of subject kind to ids`,
153
+ fix,
154
+ );
155
+ }
138
156
  for (const [kind, ids] of Object.entries<unknown>(subjects)) {
139
157
  if (kind.trim() === '') throw flagTargetingInvalid(key, 'a subjects kind is blank', fix);
140
158
  if (isBuiltInSubjectKind(kind)) {