@cosmicdrift/kumiko-bundled-features 0.168.0 → 0.171.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-bundled-features",
3
- "version": "0.168.0",
3
+ "version": "0.171.0",
4
4
  "description": "Built-in features — tenant, user, auth, delivery. The stuff you'd rewrite anyway, already typed.",
5
5
  "license": "BUSL-1.1",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
@@ -119,16 +119,17 @@
119
119
  "./step-dispatcher": "./src/step-dispatcher/index.ts"
120
120
  },
121
121
  "dependencies": {
122
- "@cosmicdrift/kumiko-dispatcher-live": "0.168.0",
123
- "@cosmicdrift/kumiko-framework": "0.168.0",
124
- "@cosmicdrift/kumiko-headless": "0.168.0",
125
- "@cosmicdrift/kumiko-renderer": "0.168.0",
126
- "@cosmicdrift/kumiko-renderer-web": "0.168.0",
127
- "@cosmicdrift/kumiko-types": "0.168.0",
122
+ "@cosmicdrift/kumiko-dispatcher-live": "0.171.0",
123
+ "@cosmicdrift/kumiko-framework": "0.171.0",
124
+ "@cosmicdrift/kumiko-headless": "0.171.0",
125
+ "@cosmicdrift/kumiko-renderer": "0.171.0",
126
+ "@cosmicdrift/kumiko-renderer-web": "0.171.0",
127
+ "@cosmicdrift/kumiko-types": "0.171.0",
128
128
  "@mollie/api-client": "^4.5.0",
129
129
  "imapflow": "^1.3.3",
130
130
  "mailparser": "^3.9.8",
131
131
  "@node-rs/argon2": "^2.0.2",
132
+ "@types/mailparser": "^3.4.6",
132
133
  "@types/nodemailer": "^8.0.0",
133
134
  "clsx": "^2.1.1",
134
135
  "lucide-react": "^1.14.0",
@@ -150,7 +151,6 @@
150
151
  ],
151
152
  "devDependencies": {
152
153
  "@testing-library/user-event": "^14.6.1",
153
- "@types/mailparser": "^3.4.6",
154
154
  "@types/qrcode": "^1.5.5"
155
155
  }
156
156
  }
@@ -190,6 +190,9 @@ export type SeedAdminOptions = {
190
190
  * membership-Rollen. Typischer use-case: `["SystemAdmin"]` für
191
191
  * einen Plattform-Operator. Default: leer. */
192
192
  readonly globalRoles?: readonly string[];
193
+ /** Initial-emailVerified-Flag — Default false (unverified),
194
+ * gleicher Mechanismus wie seedUserWithPassword.emailVerified. */
195
+ readonly emailVerified?: boolean;
193
196
  readonly by?: SessionUser;
194
197
  };
195
198
 
@@ -213,6 +216,7 @@ export async function seedAdmin(
213
216
  password: options.password,
214
217
  displayName: options.displayName,
215
218
  ...(options.globalRoles !== undefined && { roles: options.globalRoles }),
219
+ ...(options.emailVerified !== undefined && { emailVerified: options.emailVerified }),
216
220
  by,
217
221
  });
218
222
 
@@ -102,6 +102,28 @@ describe("GDPR-storage boot guards V2-V4 (via r.bootCheck)", () => {
102
102
  expect(() => validateBoot([...baseFeatures(), bad])).toThrow(/EXT_USER_DATA hook.*Art\.17 gap/);
103
103
  });
104
104
 
105
+ test("V3: subjectRef field (FK to user, no pii/userOwned annotation) without EXT_USER_DATA hook → throws", () => {
106
+ const bad = defineFeature("tasks", (r) => {
107
+ r.entity(
108
+ "task",
109
+ createEntity({ fields: { assigneeId: createTextField({ subjectRef: true }) } }),
110
+ );
111
+ });
112
+ expect(() => validateBoot([...baseFeatures(), bad])).toThrow(/EXT_USER_DATA hook.*Art\.17 gap/);
113
+ });
114
+
115
+ test("V3: subjectRef field WITH EXT_USER_DATA hook registered → no throw", () => {
116
+ const hooked = defineFeature("tasks-hooked", (r) => {
117
+ r.requires("user-data-rights");
118
+ r.entity(
119
+ "task",
120
+ createEntity({ fields: { assigneeId: createTextField({ subjectRef: true }) } }),
121
+ );
122
+ r.useExtension(EXT_USER_DATA, "task", { export: async () => null, delete: async () => {} });
123
+ });
124
+ expect(() => validateBoot([...baseFeatures(), hooked])).not.toThrow();
125
+ });
126
+
105
127
  test("V3: projection-only pii entity WITH EXT_USER_DATA hook registered for its entity name → no throw", () => {
106
128
  const projectionTable = projectionProbeTable("crm_contact_summary_hooked");
107
129
  const hooked = defineFeature("crm-hooked", (r) => {
@@ -35,8 +35,9 @@ export function validateGdprHookCompleteness(features: readonly FeatureDefinitio
35
35
 
36
36
  // V3: PII-entity-without-hook gate. V2 checks registered hooks for
37
37
  // completeness; V3 catches the entity nobody registered at all — fields
38
- // annotated as user-subject data (pii / userOwned) yet invisible to the
39
- // Art.15/20 export and Art.17 forget pipeline. Hard boot failure: a
38
+ // annotated as user-subject data (pii / userOwned / subjectRef) yet
39
+ // invisible to the Art.15/20 export and Art.17 forget pipeline. Hard boot
40
+ // failure: a
40
41
  // subject-data entity that skips the pipeline is exactly the "feature built
41
42
  // past GDPR" leak this gate exists to stop. No "is user-data-rights mounted"
42
43
  // guard needed here (unlike the framework-internal predecessor) — this runs
@@ -58,7 +59,7 @@ export function validateGdprPiiHookCoverage(features: readonly FeatureDefinition
58
59
  const subjectFields = Object.entries(entity.fields)
59
60
  .filter(([, field]) => {
60
61
  const annot = field as PiiAnnotations; // @cast-boundary schema-walk
61
- return Boolean(annot.pii) || Boolean(annot.userOwned);
62
+ return Boolean(annot.pii) || Boolean(annot.userOwned) || Boolean(annot.subjectRef);
62
63
  })
63
64
  .map(([name]) => name);
64
65
  if (subjectFields.length === 0) continue;