@flytedesk/app-kit 0.2.0 → 0.3.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.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export declare function main(argv: string[]): Promise<void>;
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * flags-sync — the CLI half of @flytedesk/app-kit/flags (DEC-39).
4
+ *
5
+ * A thin wrapper around the generalized sync engine in ./sync-engine.js — see that
6
+ * file's doc comment for the full subcommand reference (init/diff/apply/status) and
7
+ * how fragments/migrations/lock files work. This file only supplies the "flags"
8
+ * fragment name and the "flags-sync" bin name; trace-sync.ts/profile-sync.ts are the
9
+ * same wrapper for the trace/profile fragments.
10
+ */
11
+ import { pathToFileURL } from "node:url";
12
+ import { runSyncCli, SyncCliError } from "./sync-engine.js";
13
+ export async function main(argv) {
14
+ return runSyncCli(argv, { name: "flags", binName: "flags-sync" });
15
+ }
16
+ const isMainModule = process.argv[1] !== undefined &&
17
+ import.meta.url === pathToFileURL(process.argv[1]).href;
18
+ if (isMainModule) {
19
+ main(process.argv.slice(2)).catch((err) => {
20
+ if (!(err instanceof SyncCliError)) {
21
+ console.error(err);
22
+ }
23
+ process.exitCode = process.exitCode ?? 1;
24
+ });
25
+ }
26
+ //# sourceMappingURL=flags-sync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flags-sync.js","sourceRoot":"","sources":["../../src/cli/flags-sync.ts"],"names":[],"mappings":";AACA;;;;;;;;GAQG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAE5D,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,IAAc;IACvC,OAAO,UAAU,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,CAAC;AACpE,CAAC;AAED,MAAM,YAAY,GAChB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,SAAS;IAC7B,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAE1D,IAAI,YAAY,EAAE,CAAC;IACjB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;QACjD,IAAI,CAAC,CAAC,GAAG,YAAY,YAAY,CAAC,EAAE,CAAC;YACnC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACrB,CAAC;QACD,OAAO,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -3,12 +3,26 @@
3
3
  * (DEC-39, generalized to a second fragment for the profile module).
4
4
  *
5
5
  * This package never generates its own Prisma Client for any of its fragments.
6
- * Instead, this engine copies a package fragment (prisma/fragments/<fragment>.prisma)
7
- * into the *consumer's* own multi-file Prisma schema folder, applies that fragment's
8
- * raw SQL migrations directly against the consumer's configured datasource, and
9
- * tracks what's been applied in a lock file (`.app-kit-<fragment>.lock.json`) written
10
- * at the consumer project root. The consumer's own `prisma generate` then produces
11
- * one client that includes these models alongside their own.
6
+ * Instead, this engine installs a package fragment (prisma/fragments/<fragment>.prisma)
7
+ * into the *consumer's* own Prisma schema, applies that fragment's raw SQL migrations
8
+ * directly against the consumer's configured datasource, and tracks what's been
9
+ * applied in a lock file (`.app-kit-<fragment>.lock.json`) written at the consumer
10
+ * project root. The consumer's own `prisma generate` then produces one client that
11
+ * includes these models alongside their own.
12
+ *
13
+ * A consumer's `prisma.config.ts` `schema` setting points at one of two real layouts
14
+ * (see classifySchemaLayout below), and this engine handles both:
15
+ * - A multi-file schema FOLDER (`schema: "prisma/schema"`, no `.prisma` suffix):
16
+ * the fragment is copied as a sibling file, `prisma/schema/<fragment>.prisma`.
17
+ * - A flat single-file schema.prisma (`schema: "prisma/schema.prisma"`), which is
18
+ * what both real consumers (flytedesk-id, media-planner) actually use: the
19
+ * fragment's model(s) are merged directly into that one file, inside a
20
+ * delimited, app-kit-owned marker region (see mergeIntoFlatSchema) rather than
21
+ * copied as a sibling file — a sibling file next to a flat schema.prisma is never
22
+ * read by Prisma, since `schema` names that one file, not a folder. An earlier
23
+ * version of this engine always did the sibling-file copy regardless of layout,
24
+ * which is why the CLI had never successfully run against a real consumer
25
+ * (flytedesk-id's /profile integration had to be pasted in by hand).
12
26
  *
13
27
  * Both bin commands are thin wrappers around `runSyncCli` (see src/cli/trace-sync.ts
14
28
  * and src/cli/profile-sync.ts) — everything fragment-specific is passed in as a
@@ -53,6 +67,9 @@ function sha256(content) {
53
67
  function sha256File(filePath) {
54
68
  return sha256(readFileSync(filePath));
55
69
  }
70
+ function escapeRegExp(value) {
71
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
72
+ }
56
73
  // ---------------------------------------------------------------------------
57
74
  // Engine: bound to one SyncFragment + the consumer root it's operating on
58
75
  // ---------------------------------------------------------------------------
@@ -155,19 +172,31 @@ class SyncEngine {
155
172
  }
156
173
  return path.resolve(path.dirname(configFile), schemaPath);
157
174
  }
158
- /** Resolve the folder <fragment>.prisma gets copied into, from the config's
159
- * schema path (which may point at the folder itself, or at a single schema file
160
- * inside it, e.g. "prisma/schema.prisma"). */
161
- resolveSchemaFolder(schemaPath) {
175
+ /**
176
+ * Classifies the consumer's schema layout from their prisma.config.ts `schema`
177
+ * path: a path ending in `.prisma` names one specific file — Prisma reads only
178
+ * that file, so this is the flat single-file layout, and the fragment must be
179
+ * merged into it directly (mergeIntoFlatSchema). Anything else names a folder —
180
+ * the multi-file layout — and the fragment is copied in as a sibling file
181
+ * (copyFragment), same as before.
182
+ */
183
+ classifySchemaLayout(schemaPath) {
162
184
  if (schemaPath.endsWith(".prisma")) {
163
- return path.dirname(schemaPath);
185
+ return {
186
+ kind: "flat-file",
187
+ file: schemaPath,
188
+ prismaRoot: path.dirname(schemaPath),
189
+ };
164
190
  }
165
- return schemaPath;
191
+ return {
192
+ kind: "folder",
193
+ folder: schemaPath,
194
+ prismaRoot: this.resolvePrismaRoot(schemaPath),
195
+ };
166
196
  }
167
197
  /** The conventional "prisma root" a schema folder sits under, used to locate (or
168
198
  * create) the sibling `migrations/` directory. For the common multi-file layout
169
- * `prisma/schema/*.prisma`, that's `prisma/`; for a flat `prisma/schema.prisma`
170
- * layout, `resolveSchemaFolder` already returned `prisma/`, so this is a no-op. */
199
+ * `prisma/schema/*.prisma`, that's `prisma/`. */
171
200
  resolvePrismaRoot(schemaFolder) {
172
201
  return path.basename(schemaFolder) === "schema"
173
202
  ? path.dirname(schemaFolder)
@@ -182,7 +211,10 @@ class SyncEngine {
182
211
  // with an argument array is an unescaped-concatenation footgun (path arguments
183
212
  // here can contain spaces). cross-spawn resolves and correctly quotes .cmd/.bat
184
213
  // executables on Windows without going through a shell at all.
185
- const result = spawn.sync("npx", ["prisma", ...args], { cwd, stdio: "inherit" });
214
+ const result = spawn.sync("npx", ["prisma", ...args], {
215
+ cwd,
216
+ stdio: "inherit",
217
+ });
186
218
  if (result.error) {
187
219
  this.fail(`failed to run "npx prisma ${args.join(" ")}": ${result.error.message}`);
188
220
  }
@@ -204,16 +236,119 @@ class SyncEngine {
204
236
  cpSync(srcDir, destDir, { recursive: true });
205
237
  }
206
238
  // -------------------------------------------------------------------------
239
+ // Flat single-file schema.prisma support
240
+ // -------------------------------------------------------------------------
241
+ /**
242
+ * The portion of a fragment's .prisma source that gets merged into a consumer's
243
+ * flat schema.prisma — everything from the first `model` declaration onward,
244
+ * trimmed. Deliberately drops the fragment file's own leading design-rationale
245
+ * doc comment (see prisma/fragments/profile.prisma / trace.prisma's file
246
+ * headers): that comment describes the *fragment file itself* ("this file is
247
+ * copied by <bin> init...", "matching the raw SQL in prisma/migrations/...")
248
+ * which reads oddly once inlined into someone else's schema.prisma, whereas the
249
+ * marker region's own header (markerStart) already identifies the block as
250
+ * app-kit-owned and names which command manages it.
251
+ */
252
+ extractFragmentBody(fragmentSource) {
253
+ const lines = fragmentSource.split(/\r?\n/);
254
+ const firstModelLine = lines.findIndex((line) => /^model\s+\w+\s*\{/.test(line));
255
+ const body = firstModelLine === -1
256
+ ? fragmentSource
257
+ : lines.slice(firstModelLine).join("\n");
258
+ return body.trim();
259
+ }
260
+ markerStart() {
261
+ return `// --- @flytedesk/app-kit:${this.fragment.name} start (managed by ${this.fragment.binName} — do not hand-edit this region) ---`;
262
+ }
263
+ markerEnd() {
264
+ return `// --- @flytedesk/app-kit:${this.fragment.name} end ---`;
265
+ }
266
+ /**
267
+ * Extracts the current content of THIS fragment's own marker region from a flat
268
+ * schema.prisma's full text, or null if the region isn't present yet. Matching on
269
+ * this fragment's own name means two different fragments merged into the same
270
+ * flat file never see or touch each other's regions.
271
+ *
272
+ * Exact inverse of the `${start}\n${body}\n${end}` block mergeIntoFlatSchema
273
+ * writes, so a round trip (write, then extract) returns the same body
274
+ * byte-for-byte — this is what makes both mergeIntoFlatSchema's own
275
+ * no-op-if-unchanged check and cmdStatus's checksum comparison correct.
276
+ */
277
+ extractInstalledRegion(schemaContent) {
278
+ const re = new RegExp(`${escapeRegExp(this.markerStart())}\\n([\\s\\S]*?)\\n${escapeRegExp(this.markerEnd())}`);
279
+ const match = schemaContent.match(re);
280
+ return match ? match[1] : null;
281
+ }
282
+ /**
283
+ * Merges this fragment's model(s) into a consumer's flat single-file
284
+ * prisma/schema.prisma, inside a delimited, app-kit-owned marker region — the
285
+ * flat-file counterpart to copyFragment's sibling-file copy for the multi-file
286
+ * folder layout.
287
+ *
288
+ * Idempotent: running this twice with the same fragment content is a no-op (the
289
+ * installed region already matches, so the file isn't rewritten at all). Multiple
290
+ * fragments merged into the same file coexist as separate regions — this only
291
+ * ever reads or replaces text between ITS OWN start/end marker pair; everything
292
+ * else in the file (the consumer's own models, datasource/generator blocks,
293
+ * `schemas = [...]`, other fragments' regions) is left byte-for-byte untouched.
294
+ */
295
+ mergeIntoFlatSchema(schemaFilePath) {
296
+ if (!existsSync(schemaFilePath)) {
297
+ this.fail(`schema file not found at ${schemaFilePath} (expected a flat single-file prisma/schema.prisma)`);
298
+ }
299
+ const original = readFileSync(schemaFilePath, "utf8");
300
+ const body = this.extractFragmentBody(readFileSync(this.fragmentSrcPath, "utf8"));
301
+ const existingRegion = this.extractInstalledRegion(original);
302
+ if (existingRegion === body) {
303
+ return;
304
+ }
305
+ let updated;
306
+ if (existingRegion !== null) {
307
+ const re = new RegExp(`${escapeRegExp(this.markerStart())}\\n[\\s\\S]*?\\n${escapeRegExp(this.markerEnd())}`);
308
+ updated = original.replace(re, `${this.markerStart()}\n${body}\n${this.markerEnd()}`);
309
+ }
310
+ else {
311
+ updated = `${original.replace(/\s*$/, "")}\n\n${this.markerStart()}\n${body}\n${this.markerEnd()}\n`;
312
+ }
313
+ writeFileSync(schemaFilePath, updated, "utf8");
314
+ }
315
+ /** Installs the fragment for either layout — the one call site cmdInit/cmdApply
316
+ * both use, so they can never disagree about which path a given layout takes. */
317
+ installFragment(layout) {
318
+ const bin = this.fragment.binName;
319
+ if (layout.kind === "folder") {
320
+ console.log(`${bin}: copying ${this.fragment.name}.prisma into ${layout.folder}`);
321
+ this.copyFragment(layout.folder);
322
+ }
323
+ else {
324
+ console.log(`${bin}: merging ${this.fragment.name} model(s) into ${layout.file} (managed region)`);
325
+ this.mergeIntoFlatSchema(layout.file);
326
+ }
327
+ }
328
+ /**
329
+ * What gets recorded as `fragmentChecksum` in the lock file, and what cmdStatus
330
+ * recomputes from the consumer's tree to detect hand-edits — computed the same
331
+ * way installFragment actually wrote it, per layout: the exact fragment file
332
+ * bytes for the folder/sibling-copy case, or the extracted marker-region body for
333
+ * the flat-file/merge case (NOT the whole fragment source file, since the
334
+ * installed region deliberately excludes the fragment file's leading doc
335
+ * comment — see extractFragmentBody).
336
+ */
337
+ computeFragmentChecksum(layout) {
338
+ if (layout.kind === "folder") {
339
+ return sha256File(this.fragmentSrcPath);
340
+ }
341
+ return sha256(this.extractFragmentBody(readFileSync(this.fragmentSrcPath, "utf8")));
342
+ }
343
+ // -------------------------------------------------------------------------
207
344
  // Subcommands
208
345
  // -------------------------------------------------------------------------
209
346
  async cmdInit(consumerRoot) {
210
347
  const bin = this.fragment.binName;
211
348
  const schemaPath = await this.loadConsumerSchemaPath(consumerRoot);
212
- const schemaFolder = this.resolveSchemaFolder(schemaPath);
213
- const prismaRoot = this.resolvePrismaRoot(schemaFolder);
214
- const migrationsDestDir = path.join(prismaRoot, "migrations");
215
- console.log(`${bin}: copying ${this.fragment.name}.prisma into ${schemaFolder}`);
216
- this.copyFragment(schemaFolder);
349
+ const layout = this.classifySchemaLayout(schemaPath);
350
+ const migrationsDestDir = path.join(layout.prismaRoot, "migrations");
351
+ this.installFragment(layout);
217
352
  console.log(`${bin}: running "npx prisma validate" against ${consumerRoot}`);
218
353
  this.runPrisma(["validate"], consumerRoot);
219
354
  const manifest = this.readManifest();
@@ -236,7 +371,7 @@ class SyncEngine {
236
371
  const meta = this.readFragmentMeta();
237
372
  this.writeLockFile(consumerRoot, {
238
373
  fragmentVersion: meta.fragmentVersion,
239
- fragmentChecksum: sha256File(this.fragmentSrcPath),
374
+ fragmentChecksum: this.computeFragmentChecksum(layout),
240
375
  appliedMigrations,
241
376
  lastSyncedAt: new Date().toISOString(),
242
377
  });
@@ -281,11 +416,9 @@ class SyncEngine {
281
416
  const bin = this.fragment.binName;
282
417
  const report = this.computePending(consumerRoot);
283
418
  const schemaPath = await this.loadConsumerSchemaPath(consumerRoot);
284
- const schemaFolder = this.resolveSchemaFolder(schemaPath);
285
- const prismaRoot = this.resolvePrismaRoot(schemaFolder);
286
- const migrationsDestDir = path.join(prismaRoot, "migrations");
287
- console.log(`${bin}: refreshing ${this.fragment.name}.prisma in ${schemaFolder}`);
288
- this.copyFragment(schemaFolder);
419
+ const layout = this.classifySchemaLayout(schemaPath);
420
+ const migrationsDestDir = path.join(layout.prismaRoot, "migrations");
421
+ this.installFragment(layout);
289
422
  const lock = this.readLockFile(consumerRoot);
290
423
  if (!lock) {
291
424
  this.fail(`no ${this.lockFileName} found at ${consumerRoot}. Run "${bin} init" first.`);
@@ -308,7 +441,7 @@ class SyncEngine {
308
441
  const meta = this.readFragmentMeta();
309
442
  this.writeLockFile(consumerRoot, {
310
443
  fragmentVersion: meta.fragmentVersion,
311
- fragmentChecksum: sha256File(this.fragmentSrcPath),
444
+ fragmentChecksum: this.computeFragmentChecksum(layout),
312
445
  appliedMigrations,
313
446
  lastSyncedAt: new Date().toISOString(),
314
447
  });
@@ -330,18 +463,36 @@ class SyncEngine {
330
463
  }
331
464
  try {
332
465
  const schemaPath = await this.loadConsumerSchemaPath(consumerRoot);
333
- const schemaFolder = this.resolveSchemaFolder(schemaPath);
334
- const copiedFragmentPath = path.join(schemaFolder, `${this.fragment.name}.prisma`);
335
- if (!existsSync(copiedFragmentPath)) {
336
- console.log(`fragment copy: MISSING at ${copiedFragmentPath}`);
337
- return;
466
+ const layout = this.classifySchemaLayout(schemaPath);
467
+ let installedChecksum;
468
+ let location;
469
+ if (layout.kind === "folder") {
470
+ const copiedFragmentPath = path.join(layout.folder, `${this.fragment.name}.prisma`);
471
+ if (!existsSync(copiedFragmentPath)) {
472
+ console.log(`fragment copy: MISSING at ${copiedFragmentPath}`);
473
+ return;
474
+ }
475
+ installedChecksum = sha256File(copiedFragmentPath);
476
+ location = copiedFragmentPath;
477
+ }
478
+ else {
479
+ if (!existsSync(layout.file)) {
480
+ console.log(`fragment copy: MISSING — ${layout.file} does not exist`);
481
+ return;
482
+ }
483
+ const region = this.extractInstalledRegion(readFileSync(layout.file, "utf8"));
484
+ if (region === null) {
485
+ console.log(`fragment copy: MISSING — no managed region found in ${layout.file}`);
486
+ return;
487
+ }
488
+ installedChecksum = sha256(region);
489
+ location = `${layout.file} (managed region)`;
338
490
  }
339
- const installedChecksum = sha256File(copiedFragmentPath);
340
491
  if (installedChecksum === lock.fragmentChecksum) {
341
492
  console.log("fragment copy: unmodified");
342
493
  }
343
494
  else {
344
- console.log(`fragment copy: CHECKSUM MISMATCH — ${copiedFragmentPath} has been hand-edited ` +
495
+ console.log(`fragment copy: CHECKSUM MISMATCH — ${location} has been hand-edited ` +
345
496
  `(or is out of sync with the lock file)`);
346
497
  }
347
498
  }
@@ -1 +1 @@
1
- {"version":3,"file":"sync-engine.js","sourceRoot":"","sources":["../../src/cli/sync-engine.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sync-engine.js","sourceRoot":"","sources":["../../src/cli/sync-engine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Generic project-scoped feature-flag module (enabled/disabled, percentage
3
+ * rollout, role targeting, per-user overrides) — DEC-39. Ships a Prisma schema
4
+ * fragment + a raw SQL migration + a `flags-sync` CLI (all generalized from
5
+ * @flytedesk/app-kit/trace's and @flytedesk/app-kit/profile's DEC-39 machinery —
6
+ * see prisma/fragments/flags.prisma and src/cli/flags-sync.ts) plus a real Fastify
7
+ * plugin with GET/PATCH routes, ported from media-planner's own `flags.ts` so other
8
+ * flytedesk apps don't each hand-roll their own flag-admin routes + migration for
9
+ * this. The one genuinely app-specific piece of the original — resolving a flag key
10
+ * to its label/description/owner/project from media-planner's own hardcoded
11
+ * `FLAG_ROADMAP` — is generalized into a required `describeFlag` callback.
12
+ *
13
+ * Usage:
14
+ *
15
+ * import Fastify from "fastify";
16
+ * import { flytedeskAuth } from "@flytedesk/app-kit/auth";
17
+ * import { appKitFlags } from "@flytedesk/app-kit/flags";
18
+ *
19
+ * const app = Fastify();
20
+ * await app.register(flytedeskAuth, { ...authOptions });
21
+ * // Must come after flytedeskAuth (or whatever else decorates request.authUser)
22
+ * // — see plugin.ts's registration-time check.
23
+ * await app.register(appKitFlags, {
24
+ * store: {
25
+ * list: () => prisma.appKitFeatureFlag.findMany(),
26
+ * findByKey: (key) => prisma.appKitFeatureFlag.findUnique({ where: { key } }),
27
+ * upsert: (key, patch) =>
28
+ * prisma.appKitFeatureFlag.upsert({
29
+ * where: { key },
30
+ * create: { key, project: "unknown", ...patch },
31
+ * update: patch,
32
+ * }),
33
+ * },
34
+ * describeFlag: (key) => FLAG_ROADMAP.find((f) => f.key === key),
35
+ * onChange: async (key, changes, actor) => {
36
+ * await recordAudit({ actorId: actor.id, who: actor.name, action: "flag.update", target: key, ... });
37
+ * },
38
+ * });
39
+ *
40
+ * // GET /flags -> { rows, pager, sort, filter }
41
+ * // GET /flags/:key -> single flag, or 404
42
+ * // PATCH /flags/:key { enabled?, rollout?, roles?, users? } -> updated flag
43
+ *
44
+ * No permission check is baked in — register your own `preHandler` on top of these
45
+ * routes for whatever RBAC category gates flag administration in your app (same
46
+ * boundary @flytedesk/app-kit/profile draws).
47
+ */
48
+ export { appKitFlags } from "./plugin.js";
49
+ export type { AppKitFlagsOptions, FlagFields, FlagListResponseBody, FlagMeta, FlagPager, FlagPatch, FlagResponseBody, FlagRow, FlagStore, } from "./types.js";
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Generic project-scoped feature-flag module (enabled/disabled, percentage
3
+ * rollout, role targeting, per-user overrides) — DEC-39. Ships a Prisma schema
4
+ * fragment + a raw SQL migration + a `flags-sync` CLI (all generalized from
5
+ * @flytedesk/app-kit/trace's and @flytedesk/app-kit/profile's DEC-39 machinery —
6
+ * see prisma/fragments/flags.prisma and src/cli/flags-sync.ts) plus a real Fastify
7
+ * plugin with GET/PATCH routes, ported from media-planner's own `flags.ts` so other
8
+ * flytedesk apps don't each hand-roll their own flag-admin routes + migration for
9
+ * this. The one genuinely app-specific piece of the original — resolving a flag key
10
+ * to its label/description/owner/project from media-planner's own hardcoded
11
+ * `FLAG_ROADMAP` — is generalized into a required `describeFlag` callback.
12
+ *
13
+ * Usage:
14
+ *
15
+ * import Fastify from "fastify";
16
+ * import { flytedeskAuth } from "@flytedesk/app-kit/auth";
17
+ * import { appKitFlags } from "@flytedesk/app-kit/flags";
18
+ *
19
+ * const app = Fastify();
20
+ * await app.register(flytedeskAuth, { ...authOptions });
21
+ * // Must come after flytedeskAuth (or whatever else decorates request.authUser)
22
+ * // — see plugin.ts's registration-time check.
23
+ * await app.register(appKitFlags, {
24
+ * store: {
25
+ * list: () => prisma.appKitFeatureFlag.findMany(),
26
+ * findByKey: (key) => prisma.appKitFeatureFlag.findUnique({ where: { key } }),
27
+ * upsert: (key, patch) =>
28
+ * prisma.appKitFeatureFlag.upsert({
29
+ * where: { key },
30
+ * create: { key, project: "unknown", ...patch },
31
+ * update: patch,
32
+ * }),
33
+ * },
34
+ * describeFlag: (key) => FLAG_ROADMAP.find((f) => f.key === key),
35
+ * onChange: async (key, changes, actor) => {
36
+ * await recordAudit({ actorId: actor.id, who: actor.name, action: "flag.update", target: key, ... });
37
+ * },
38
+ * });
39
+ *
40
+ * // GET /flags -> { rows, pager, sort, filter }
41
+ * // GET /flags/:key -> single flag, or 404
42
+ * // PATCH /flags/:key { enabled?, rollout?, roles?, users? } -> updated flag
43
+ *
44
+ * No permission check is baked in — register your own `preHandler` on top of these
45
+ * routes for whatever RBAC category gates flag administration in your app (same
46
+ * boundary @flytedesk/app-kit/profile draws).
47
+ */
48
+ export { appKitFlags } from "./plugin.js";
49
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/flags/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { FastifyPluginAsync } from "fastify";
2
+ import type { AppKitFlagsOptions } from "./types.js";
3
+ export declare const appKitFlags: FastifyPluginAsync<AppKitFlagsOptions>;
@@ -0,0 +1,296 @@
1
+ import fp from "fastify-plugin";
2
+ const DEFAULT_ROUTE_PREFIX = "/flags";
3
+ const DEFAULT_PAGE_SIZE = 25;
4
+ const MAX_PAGE_SIZE = 200;
5
+ function assertRequiredOptions(options) {
6
+ if (!options.store) {
7
+ throw new Error('@flytedesk/app-kit/flags: missing required option "store"');
8
+ }
9
+ if (typeof options.store.list !== "function" ||
10
+ typeof options.store.findByKey !== "function" ||
11
+ typeof options.store.upsert !== "function") {
12
+ throw new Error("@flytedesk/app-kit/flags: options.store must implement list(), findByKey(key), and upsert(key, patch)");
13
+ }
14
+ if (typeof options.describeFlag !== "function") {
15
+ throw new Error('@flytedesk/app-kit/flags: missing required option "describeFlag"');
16
+ }
17
+ }
18
+ function toFlagResponse(row, meta) {
19
+ return {
20
+ key: row.key,
21
+ project: meta.project,
22
+ label: meta.label,
23
+ description: meta.description,
24
+ enabled: row.enabled,
25
+ rollout: row.rollout,
26
+ roles: row.roles,
27
+ users: row.users,
28
+ owner: meta.owner,
29
+ updated: row.updatedAt.toISOString(),
30
+ };
31
+ }
32
+ /**
33
+ * Not exported: this plugin's own minimal 401 check on `request.authUser`, kept
34
+ * local rather than importing ../auth/guards.js's requireAuth — identical contract
35
+ * and identical implementation to src/profile/plugin.ts's own requireAuthUser (see
36
+ * that file's doc comment for the full rationale: no hard dependency on the auth
37
+ * module's internals beyond the `authUser` decoration contract, so any consumer
38
+ * populating request.authUser some other way still works).
39
+ */
40
+ function requireAuthUser(request, reply) {
41
+ if (!request.authUser) {
42
+ reply
43
+ .code(401)
44
+ .send({ error: "Authentication required", code: "unauthenticated" });
45
+ return false;
46
+ }
47
+ return true;
48
+ }
49
+ function coercePositiveInt(value, fallback, max) {
50
+ const n = typeof value === "string"
51
+ ? Number(value)
52
+ : typeof value === "number"
53
+ ? value
54
+ : NaN;
55
+ if (!Number.isFinite(n) || !Number.isInteger(n) || n < 1)
56
+ return fallback;
57
+ return max != null ? Math.min(n, max) : n;
58
+ }
59
+ function parseListQuery(query) {
60
+ return {
61
+ page: coercePositiveInt(query.page, 1),
62
+ pageSize: coercePositiveInt(query.pageSize, DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE),
63
+ sort: typeof query.sort === "string" ? query.sort : undefined,
64
+ dir: query.dir === "asc" ? "asc" : "desc",
65
+ q: typeof query.q === "string" ? query.q : "",
66
+ };
67
+ }
68
+ /** Repeated querystring keys (?project=a&project=b) arrive as arrays already —
69
+ * Fastify's querystring parser gives us those natively, same as the reference's
70
+ * own asArray relies on. */
71
+ function asArray(value) {
72
+ if (value == null)
73
+ return [];
74
+ if (Array.isArray(value))
75
+ return value.filter((v) => typeof v === "string");
76
+ return typeof value === "string" ? [value] : [];
77
+ }
78
+ function buildPager(page, pageSize, total) {
79
+ const totalPages = Math.max(1, Math.ceil(total / pageSize));
80
+ const clampedPage = Math.min(Math.max(1, page), totalPages);
81
+ return {
82
+ page: clampedPage,
83
+ pageSize,
84
+ total,
85
+ totalPages,
86
+ from: total === 0 ? 0 : (clampedPage - 1) * pageSize + 1,
87
+ to: Math.min(total, clampedPage * pageSize),
88
+ hasPrev: clampedPage > 1,
89
+ hasNext: clampedPage < totalPages,
90
+ };
91
+ }
92
+ // ---------------------------------------------------------------------------
93
+ // PATCH body validation — hand-written for the same no-new-dependency reason as
94
+ // the list query parsing above. Same fields/constraints as the reference's zod
95
+ // schema: enabled (boolean), rollout (integer 0-100), roles (string[]), users
96
+ // (object of string -> boolean), all optional.
97
+ // ---------------------------------------------------------------------------
98
+ function parsePatchBody(body) {
99
+ if (typeof body !== "object" || body === null || Array.isArray(body)) {
100
+ return { error: "Request body must be a JSON object" };
101
+ }
102
+ const input = body;
103
+ const patch = {};
104
+ if ("enabled" in input) {
105
+ if (typeof input.enabled !== "boolean") {
106
+ return { error: '"enabled" must be a boolean' };
107
+ }
108
+ patch.enabled = input.enabled;
109
+ }
110
+ if ("rollout" in input) {
111
+ const rollout = input.rollout;
112
+ if (typeof rollout !== "number" ||
113
+ !Number.isInteger(rollout) ||
114
+ rollout < 0 ||
115
+ rollout > 100) {
116
+ return { error: '"rollout" must be an integer between 0 and 100' };
117
+ }
118
+ patch.rollout = rollout;
119
+ }
120
+ if ("roles" in input) {
121
+ const roles = input.roles;
122
+ if (!Array.isArray(roles) || roles.some((r) => typeof r !== "string")) {
123
+ return { error: '"roles" must be an array of strings' };
124
+ }
125
+ patch.roles = roles;
126
+ }
127
+ if ("users" in input) {
128
+ const users = input.users;
129
+ if (typeof users !== "object" ||
130
+ users === null ||
131
+ Array.isArray(users) ||
132
+ Object.values(users).some((v) => typeof v !== "boolean")) {
133
+ return {
134
+ error: '"users" must be an object mapping user ids to booleans',
135
+ };
136
+ }
137
+ patch.users = users;
138
+ }
139
+ return patch;
140
+ }
141
+ const appKitFlagsPlugin = async (fastify, options) => {
142
+ assertRequiredOptions(options);
143
+ // Same registration-time contract as src/profile/plugin.ts — see that file's
144
+ // matching check for the full rationale. Register @flytedesk/app-kit/auth (or
145
+ // your own equivalent) BEFORE this plugin.
146
+ if (!fastify.hasRequestDecorator("authUser")) {
147
+ throw new Error("@flytedesk/app-kit/flags: request.authUser is not decorated on this Fastify instance yet. " +
148
+ "This plugin resolves the caller from request.authUser and must be registered AFTER " +
149
+ "whatever populates it — @flytedesk/app-kit/auth's flytedeskAuth plugin, or the " +
150
+ "consuming app's own equivalent decorator.");
151
+ }
152
+ const routePrefix = options.routePrefix ?? DEFAULT_ROUTE_PREFIX;
153
+ const store = options.store;
154
+ const describeFlag = options.describeFlag;
155
+ /**
156
+ * GET {routePrefix} — search/filter/sort/paginate every flag that has metadata
157
+ * (ported from the reference GET /flags: same q/project/state filters, same
158
+ * project-then-arbitrary-column sort, same facet counts over the filtered set
159
+ * before pagination is applied). A flag with no `describeFlag` metadata is
160
+ * excluded entirely, matching the reference's `.filter((x) => x.meta)`.
161
+ *
162
+ * No permission check baked in here — media-planner's own `flags.manage` is its
163
+ * own RBAC category, entirely the consumer's concern via their own `preHandler`
164
+ * on this route (same boundary /profile draws around authorization).
165
+ */
166
+ fastify.get(routePrefix, async (request, reply) => {
167
+ if (!requireAuthUser(request, reply))
168
+ return;
169
+ const query = request.query;
170
+ const parsedQuery = parseListQuery(query);
171
+ const projectFilter = asArray(query.project);
172
+ const stateFilter = asArray(query.state);
173
+ const rows = await store.list();
174
+ let joined = rows
175
+ .map((row) => ({ row, meta: describeFlag(row.key) }))
176
+ .filter((x) => x.meta !== undefined);
177
+ if (parsedQuery.q) {
178
+ const term = parsedQuery.q.toLowerCase();
179
+ joined = joined.filter((x) => x.row.key.toLowerCase().includes(term) ||
180
+ x.meta.label.toLowerCase().includes(term) ||
181
+ x.meta.description.toLowerCase().includes(term) ||
182
+ x.meta.owner.toLowerCase().includes(term));
183
+ }
184
+ if (projectFilter.length) {
185
+ joined = joined.filter((x) => projectFilter.includes(x.meta.project));
186
+ }
187
+ if (stateFilter.length) {
188
+ joined = joined.filter((x) => stateFilter.includes(x.row.enabled ? "on" : "off"));
189
+ }
190
+ const projectFacets = {};
191
+ const stateFacets = {};
192
+ for (const x of joined) {
193
+ projectFacets[x.meta.project] = (projectFacets[x.meta.project] ?? 0) + 1;
194
+ const stateKey = x.row.enabled ? "on" : "off";
195
+ stateFacets[stateKey] = (stateFacets[stateKey] ?? 0) + 1;
196
+ }
197
+ const sortKey = parsedQuery.sort ?? "project";
198
+ joined.sort((a, b) => {
199
+ const av = sortKey === "project"
200
+ ? a.meta.project
201
+ : a.row[sortKey];
202
+ const bv = sortKey === "project"
203
+ ? b.meta.project
204
+ : b.row[sortKey];
205
+ const cmp = String(av).localeCompare(String(bv));
206
+ return parsedQuery.dir === "asc" ? cmp : -cmp;
207
+ });
208
+ const total = joined.length;
209
+ const page = Math.min(Math.max(1, parsedQuery.page), Math.max(1, Math.ceil(total / parsedQuery.pageSize)));
210
+ const paged = joined.slice((page - 1) * parsedQuery.pageSize, page * parsedQuery.pageSize);
211
+ const body = {
212
+ rows: paged.map((x) => toFlagResponse(x.row, x.meta)),
213
+ pager: buildPager(page, parsedQuery.pageSize, total),
214
+ sort: { key: sortKey, dir: parsedQuery.dir },
215
+ filter: {
216
+ applied: { project: projectFilter, state: stateFilter },
217
+ q: parsedQuery.q,
218
+ facets: { project: projectFacets, state: stateFacets },
219
+ },
220
+ };
221
+ return body;
222
+ });
223
+ /**
224
+ * GET {routePrefix}/:key — single flag lookup. Not present in the reference
225
+ * flags.ts, but a natural, trivial addition given FlagStore already exposes
226
+ * findByKey — completing the CRUD surface the store interface implies, not scope
227
+ * creep. 404s when the key has no store row OR no describeFlag metadata, so this
228
+ * agrees with the list endpoint about what counts as "a flag" rather than
229
+ * exposing a key here that the list silently hides.
230
+ */
231
+ fastify.get(`${routePrefix}/:key`, async (request, reply) => {
232
+ if (!requireAuthUser(request, reply))
233
+ return;
234
+ const meta = describeFlag(request.params.key);
235
+ if (!meta) {
236
+ return reply.code(404).send({ error: "Not found", code: "not_found" });
237
+ }
238
+ const row = await store.findByKey(request.params.key);
239
+ if (!row) {
240
+ return reply.code(404).send({ error: "Not found", code: "not_found" });
241
+ }
242
+ return toFlagResponse(row, meta);
243
+ });
244
+ /**
245
+ * PATCH {routePrefix}/:key — partial update of enabled/rollout/roles/users
246
+ * (ported from the reference PATCH /flags/:flagKey: same validation, same
247
+ * only-write-what-changed semantics). Requires the same describeFlag metadata as
248
+ * GET/:key (a deliberate generalization beyond the reference, which never
249
+ * consulted FLAG_ROADMAP for PATCH at all — see FlagMeta's doc comment on why: a
250
+ * uniform "no metadata means it isn't a flag through this API" contract across
251
+ * every route is more predictable for a package with multiple consumers than
252
+ * replicating that one route's inconsistency).
253
+ *
254
+ * Always calls `options.onChange` after a successful update, even when the patch
255
+ * changed nothing — mirrors the reference's own unconditional `recordAudit` call
256
+ * (whose `detail` falls back to "No change" rather than being skipped).
257
+ */
258
+ fastify.patch(`${routePrefix}/:key`, async (request, reply) => {
259
+ if (!requireAuthUser(request, reply))
260
+ return;
261
+ const parsed = parsePatchBody(request.body);
262
+ if ("error" in parsed) {
263
+ return reply
264
+ .code(400)
265
+ .send({ error: parsed.error, code: "invalid_body" });
266
+ }
267
+ const meta = describeFlag(request.params.key);
268
+ const existing = meta ? await store.findByKey(request.params.key) : null;
269
+ if (!meta || !existing) {
270
+ return reply.code(404).send({ error: "Not found", code: "not_found" });
271
+ }
272
+ const changes = {};
273
+ if (parsed.enabled !== undefined && parsed.enabled !== existing.enabled) {
274
+ changes.enabled = parsed.enabled;
275
+ }
276
+ if (parsed.rollout !== undefined && parsed.rollout !== existing.rollout) {
277
+ changes.rollout = parsed.rollout;
278
+ }
279
+ if (parsed.roles !== undefined) {
280
+ changes.roles = parsed.roles;
281
+ }
282
+ if (parsed.users !== undefined) {
283
+ changes.users = parsed.users;
284
+ }
285
+ const updated = await store.upsert(existing.key, changes);
286
+ await options.onChange?.(existing.key, changes, {
287
+ id: request.authUser.id,
288
+ name: request.authUser.name,
289
+ });
290
+ return toFlagResponse(updated, meta);
291
+ });
292
+ };
293
+ export const appKitFlags = fp(appKitFlagsPlugin, {
294
+ name: "@flytedesk/app-kit/flags",
295
+ });
296
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/flags/plugin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Hand-written row/option types for @flytedesk/app-kit/flags.
3
+ *
4
+ * Field names here mirror prisma/fragments/flags.prisma's camelCase model fields
5
+ * (key/project/enabled/rollout/roles/users/updatedAt) — a consumer's generated
6
+ * PrismaClient, once they've run `flags-sync init` and their own `prisma generate`,
7
+ * satisfies FlagStore directly via `prisma.appKitFeatureFlag.findMany` /
8
+ * `.findUnique` / `.upsert`.
9
+ */
10
+ export interface FlagRow {
11
+ key: string;
12
+ /** The row's own project column (mirrors media-planner's FeatureFlag.project
13
+ * exactly). NOT what list/GET responses display as `project`, or what the
14
+ * `project` list filter matches against — see FlagMeta.project below and
15
+ * plugin.ts's doc comment on why. */
16
+ project: string;
17
+ enabled: boolean;
18
+ rollout: number;
19
+ /** Opaque role ids from whatever IdP/RBAC system the consumer uses — this
20
+ * package has no opinion on what they mean, same as media-planner's own
21
+ * FeatureFlag.roles. */
22
+ roles: string[];
23
+ /** Per-user override bag, keyed by whatever user id the consumer uses, each
24
+ * value a hard on/off override that supersedes `enabled`/`rollout`/`roles` for
25
+ * that one user. Evaluating overrides against a specific caller is the
26
+ * consumer's own concern (same boundary media-planner draws) — this plugin only
27
+ * ever reads/writes the bag as a whole, never evaluates it. */
28
+ users: Record<string, boolean>;
29
+ updatedAt: Date;
30
+ }
31
+ /** The fields a PATCH may write — all optional, only the ones present in the
32
+ * request body (and that actually differ from the current row) are written. */
33
+ export type FlagFields = Pick<FlagRow, "enabled" | "rollout" | "roles" | "users">;
34
+ export type FlagPatch = Partial<FlagFields>;
35
+ /**
36
+ * Flag metadata a consumer resolves from its own source of truth (media-planner's
37
+ * own reference implementation used a hardcoded `FLAG_ROADMAP` array) — the one
38
+ * genuinely app-specific piece of the reference `flags.ts` this module generalizes
39
+ * away via `AppKitFlagsOptions.describeFlag`. `project` here (NOT `FlagRow.project`)
40
+ * is what every route actually filters/sorts/displays as "the flag's project" —
41
+ * ported faithfully from the reference, which reads `meta.project` everywhere and
42
+ * never reads the DB row's own `project` column.
43
+ */
44
+ export interface FlagMeta {
45
+ label: string;
46
+ description: string;
47
+ owner: string;
48
+ project: string;
49
+ }
50
+ /**
51
+ * Minimal persistence seam this plugin needs — deliberately NOT a dependency on
52
+ * `@prisma/client`, mirroring every other app-kit module's *Store interface (see
53
+ * src/profile/types.ts's ProfileStore, src/trace/types.ts's TracePrismaClient). A
54
+ * consumer's generated PrismaClient trivially satisfies this shape:
55
+ *
56
+ * const store: FlagStore = {
57
+ * list: () => prisma.appKitFeatureFlag.findMany(),
58
+ * findByKey: (key) => prisma.appKitFeatureFlag.findUnique({ where: { key } }),
59
+ * upsert: (key, patch) =>
60
+ * prisma.appKitFeatureFlag.upsert({
61
+ * where: { key },
62
+ * create: { key, project: "unknown", ...patch },
63
+ * update: patch,
64
+ * }),
65
+ * };
66
+ *
67
+ * A hand-rolled adapter can back it with anything else (raw SQL, a different ORM,
68
+ * an in-memory store for tests — see src/flags/plugin.test.ts).
69
+ */
70
+ export interface FlagStore {
71
+ list(): Promise<FlagRow[]>;
72
+ /** Must return `null`, not throw/404, when no row exists for `key`. */
73
+ findByKey(key: string): Promise<FlagRow | null>;
74
+ /** Upserts the row for `key`, writing only the fields present in `patch` (fields
75
+ * absent from `patch` are left unchanged on an existing row). This plugin only
76
+ * ever calls this on a key it already confirmed exists via findByKey first (see
77
+ * plugin.ts's PATCH handler) — the "upsert" naming matches ProfileStore's for
78
+ * interface-shape consistency across app-kit's *Store seams, not because this
79
+ * plugin ever creates a flag through it. */
80
+ upsert(key: string, patch: FlagPatch): Promise<FlagRow>;
81
+ }
82
+ export interface AppKitFlagsOptions {
83
+ /** The persistence seam — see FlagStore above. Required. */
84
+ store: FlagStore;
85
+ /**
86
+ * Resolves a flag key to its display metadata, or `undefined` if the key isn't
87
+ * known to the consumer's own catalog (e.g. media-planner's FLAG_ROADMAP). A flag
88
+ * with no metadata is treated as if it doesn't exist through this API at all —
89
+ * excluded from GET {routePrefix} (matching the reference's `.filter((x) =>
90
+ * x.meta)`), and 404s from GET/PATCH {routePrefix}/:key — so every route agrees
91
+ * on the same set of "real" flags rather than list hiding a key that :key would
92
+ * still happily serve. Required.
93
+ */
94
+ describeFlag: (key: string) => FlagMeta | undefined;
95
+ /**
96
+ * Fired after every successful PATCH, whether or not the patch actually changed
97
+ * anything (mirrors the reference's own unconditional `recordAudit` call, whose
98
+ * `detail` falls back to "No change" rather than skipping the call) — so a
99
+ * consumer replicating its own audit trail doesn't have to separately guess
100
+ * whether "nothing changed" is worth recording. `changes` contains only the
101
+ * fields that differed from the row's previous value, matching what was actually
102
+ * written. Not baked into a specific audit/trace mechanism (no dependency on
103
+ * @flytedesk/app-kit/trace or any other) — the consumer wires this to whatever
104
+ * they already use, same boundary `/profile` never needed to draw because it had
105
+ * no audit trail to begin with.
106
+ */
107
+ onChange?: (key: string, changes: FlagPatch, actor: {
108
+ id: string;
109
+ name: string;
110
+ }) => void | Promise<void>;
111
+ /** Base path the GET/PATCH routes are mounted under. Default "/flags". */
112
+ routePrefix?: string;
113
+ }
114
+ /** What GET `{routePrefix}` returns for one flag, GET `{routePrefix}/:key`, and
115
+ * PATCH `{routePrefix}/:key` all share. */
116
+ export interface FlagResponseBody {
117
+ key: string;
118
+ project: string;
119
+ label: string;
120
+ description: string;
121
+ enabled: boolean;
122
+ rollout: number;
123
+ roles: string[];
124
+ users: Record<string, boolean>;
125
+ owner: string;
126
+ /** ISO 8601. */
127
+ updated: string;
128
+ }
129
+ export interface FlagPager {
130
+ page: number;
131
+ pageSize: number;
132
+ total: number;
133
+ totalPages: number;
134
+ from: number;
135
+ to: number;
136
+ hasPrev: boolean;
137
+ hasNext: boolean;
138
+ }
139
+ /** What GET `{routePrefix}` returns — search/filter/sort/pagination/facets, same
140
+ * shape as the reference `GET /flags`. */
141
+ export interface FlagListResponseBody {
142
+ rows: FlagResponseBody[];
143
+ pager: FlagPager;
144
+ sort: {
145
+ key: string;
146
+ dir: "asc" | "desc";
147
+ };
148
+ filter: {
149
+ applied: {
150
+ project: string[];
151
+ state: string[];
152
+ };
153
+ q: string;
154
+ facets: {
155
+ project: Record<string, number>;
156
+ state: Record<string, number>;
157
+ };
158
+ };
159
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Hand-written row/option types for @flytedesk/app-kit/flags.
3
+ *
4
+ * Field names here mirror prisma/fragments/flags.prisma's camelCase model fields
5
+ * (key/project/enabled/rollout/roles/users/updatedAt) — a consumer's generated
6
+ * PrismaClient, once they've run `flags-sync init` and their own `prisma generate`,
7
+ * satisfies FlagStore directly via `prisma.appKitFeatureFlag.findMany` /
8
+ * `.findUnique` / `.upsert`.
9
+ */
10
+ export {};
11
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/flags/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flytedesk/app-kit",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Shared platform kit for flytedesk apps: flytedesk-id auth (BFF/OIDC client) and a Postgres-native trace/audit layer.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -29,7 +29,8 @@
29
29
  ],
30
30
  "bin": {
31
31
  "trace-sync": "./dist/cli/trace-sync.js",
32
- "profile-sync": "./dist/cli/profile-sync.js"
32
+ "profile-sync": "./dist/cli/profile-sync.js",
33
+ "flags-sync": "./dist/cli/flags-sync.js"
33
34
  },
34
35
  "exports": {
35
36
  "./auth": {
@@ -47,6 +48,10 @@
47
48
  "./profile": {
48
49
  "types": "./dist/profile/index.d.ts",
49
50
  "import": "./dist/profile/index.js"
51
+ },
52
+ "./flags": {
53
+ "types": "./dist/flags/index.d.ts",
54
+ "import": "./dist/flags/index.js"
50
55
  }
51
56
  },
52
57
  "scripts": {
@@ -0,0 +1,4 @@
1
+ {
2
+ "fragmentVersion": "1.0.0",
3
+ "minCliVersion": "1.0.0"
4
+ }
@@ -0,0 +1,52 @@
1
+ // @flytedesk/app-kit/flags — canonical Prisma model fragment (DEC-39, ported to a
2
+ // third fragment for the feature-flags module).
3
+ //
4
+ // This fragment is NOT built into a Prisma Client by this package. It is installed
5
+ // into the *consumer's* own Prisma schema by `flags-sync init` (see
6
+ // src/cli/flags-sync.ts, a thin wrapper around the generalized
7
+ // src/cli/sync-engine.ts, same as trace-sync/profile-sync), and the consumer
8
+ // generates their own client against their own schema + this fragment merged in.
9
+ //
10
+ // What this models: a generic project-scoped feature flag — enabled/disabled, a
11
+ // percentage rollout, role-based targeting, and a per-user override bag. Ported
12
+ // from media-planner's own `FeatureFlag` model (apps/api/prisma/schema.prisma) and
13
+ // its `GET/PATCH /flags` routes (apps/api/src/routes/flags.ts), generalizing the one
14
+ // genuinely app-specific piece: media-planner resolves flag metadata (label,
15
+ // description, owner, project) from its own hardcoded `FLAG_ROADMAP` — this
16
+ // fragment's row has no such columns at all, and @flytedesk/app-kit/flags's plugin
17
+ // (see src/flags/plugin.ts) instead takes a required `describeFlag` callback so any
18
+ // consumer can supply its own metadata source.
19
+ //
20
+ // Design constraints (deliberate, same reasoning as trace.prisma/profile.prisma):
21
+ // - No `@relation`/FK attributes anywhere. A package that ships models into a
22
+ // consumer's schema cannot see the consumer's own models to point a relation at.
23
+ // - No Prisma `enum`. Nothing here needs one today, but the same schema-global-
24
+ // collision reasoning applies if one ever would: it stays a plain `String`/`Int`
25
+ // column instead.
26
+ // - `String @id` on `key`: a flag's key is already a natural, stable, unique
27
+ // identifier (matches media-planner's own `FeatureFlag.key String @id`) — no
28
+ // separate surrogate key earns its keep here.
29
+ // - `roles String[]` and `users Json`: mirrors media-planner's own columns
30
+ // exactly (role ids are opaque strings from whatever IdP/RBAC system the
31
+ // consumer uses — this package has no opinion on what they mean; `users` is a
32
+ // free-form per-user override bag, `Json` because its shape is consumer-defined).
33
+ //
34
+ // Table name is prefixed `app_kit_` via `@@map` so it can't collide with a
35
+ // consumer's own tables (media-planner's own table is unprefixed `FeatureFlag` —
36
+ // this fragment intentionally does NOT reuse that name, since a consumer migrating
37
+ // onto this fragment keeps their own table and migrates data across on their own
38
+ // schedule, same as `/profile` did for flytedesk-id). Column names are snake_case
39
+ // via `@map`, matching the raw SQL in
40
+ // prisma/migrations/0003_app_kit_flags_init/migration.sql exactly.
41
+
42
+ model AppKitFeatureFlag {
43
+ key String @id
44
+ project String
45
+ enabled Boolean @default(true)
46
+ rollout Int @default(100)
47
+ roles String[] @default([])
48
+ users Json @default("{}")
49
+ updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
50
+
51
+ @@map("app_kit_feature_flag")
52
+ }
@@ -0,0 +1,16 @@
1
+ -- @flytedesk/app-kit/flags :: 0003_app_kit_flags_init
2
+ --
3
+ -- Hand-written raw SQL (this package does not run `prisma migrate dev` against its
4
+ -- own schema — see DEC-39, and 0001_app_kit_trace_init's migration.sql for the same
5
+ -- note). Column names, types, and defaults match prisma/fragments/flags.prisma
6
+ -- exactly: TEXT for the String @id/String columns, BOOLEAN/INTEGER for their Prisma
7
+ -- counterparts, TEXT[] for String[], JSONB for Json, TIMESTAMPTZ for DateTime.
8
+ CREATE TABLE app_kit_feature_flag (
9
+ key TEXT PRIMARY KEY,
10
+ project TEXT NOT NULL,
11
+ enabled BOOLEAN NOT NULL DEFAULT true,
12
+ rollout INTEGER NOT NULL DEFAULT 100,
13
+ roles TEXT[] NOT NULL DEFAULT '{}',
14
+ users JSONB NOT NULL DEFAULT '{}',
15
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
16
+ );
@@ -12,5 +12,12 @@
12
12
  "file": "0002_app_kit_profile_init/migration.sql",
13
13
  "checksum": "sha256:62d688a2db89c77f12c61a3c98be5cfe84a6e06f8a4dda9d24b0f9acd5511b32",
14
14
  "introducedInFragmentVersion": "1.0.0"
15
+ },
16
+ {
17
+ "id": "0003_app_kit_flags_init",
18
+ "fragment": "flags",
19
+ "file": "0003_app_kit_flags_init/migration.sql",
20
+ "checksum": "sha256:55a781c3118fdaf7c4aa9405a8867cecfd8c4b9de0a6e14ef90fc364c63f7d39",
21
+ "introducedInFragmentVersion": "1.0.0"
15
22
  }
16
23
  ]