@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.
- package/dist/cli/flags-sync.d.ts +2 -0
- package/dist/cli/flags-sync.js +26 -0
- package/dist/cli/flags-sync.js.map +1 -0
- package/dist/cli/sync-engine.js +185 -34
- package/dist/cli/sync-engine.js.map +1 -1
- package/dist/flags/index.d.ts +49 -0
- package/dist/flags/index.js +49 -0
- package/dist/flags/index.js.map +1 -0
- package/dist/flags/plugin.d.ts +3 -0
- package/dist/flags/plugin.js +296 -0
- package/dist/flags/plugin.js.map +1 -0
- package/dist/flags/types.d.ts +159 -0
- package/dist/flags/types.js +11 -0
- package/dist/flags/types.js.map +1 -0
- package/package.json +7 -2
- package/prisma/fragments/flags.meta.json +4 -0
- package/prisma/fragments/flags.prisma +52 -0
- package/prisma/migrations/0003_app_kit_flags_init/migration.sql +16 -0
- package/prisma/migrations/manifest.json +7 -0
|
@@ -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"}
|
package/dist/cli/sync-engine.js
CHANGED
|
@@ -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
|
|
7
|
-
* into the *consumer's* own
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
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
|
-
/**
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
|
|
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
|
|
185
|
+
return {
|
|
186
|
+
kind: "flat-file",
|
|
187
|
+
file: schemaPath,
|
|
188
|
+
prismaRoot: path.dirname(schemaPath),
|
|
189
|
+
};
|
|
164
190
|
}
|
|
165
|
-
return
|
|
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
|
|
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], {
|
|
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
|
|
213
|
-
const
|
|
214
|
-
|
|
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:
|
|
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
|
|
285
|
-
const
|
|
286
|
-
|
|
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:
|
|
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
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
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 — ${
|
|
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,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.
|
|
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,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
|
]
|