flaghoist 0.1.3 → 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/{chunk-S4XXPMMW.js → chunk-HOSNPNBP.js} +16 -1
- package/dist/index.js +225 -14
- package/dist/lib.d.ts +28 -1
- package/dist/lib.js +7 -1
- package/package.json +4 -4
|
@@ -3,6 +3,7 @@ import { parse } from "smol-toml";
|
|
|
3
3
|
var DEFAULT_CONFIG = {
|
|
4
4
|
name: "team-flags",
|
|
5
5
|
storage: "cloudflare-kv",
|
|
6
|
+
platform: "cloudflare",
|
|
6
7
|
auth: { admin: "bearer-token", read: "api-key" },
|
|
7
8
|
dashboard: true
|
|
8
9
|
};
|
|
@@ -12,21 +13,32 @@ var STORAGE_KINDS = [
|
|
|
12
13
|
"postgres",
|
|
13
14
|
"memory"
|
|
14
15
|
];
|
|
16
|
+
var PLATFORM_KINDS = ["cloudflare", "container"];
|
|
17
|
+
function containerStorageDefault(storage) {
|
|
18
|
+
return storage === "cloudflare-kv" ? "postgres" : storage;
|
|
19
|
+
}
|
|
20
|
+
function asContainer(config) {
|
|
21
|
+
return { ...config, platform: "container", storage: containerStorageDefault(config.storage) };
|
|
22
|
+
}
|
|
15
23
|
function parseConfig(text) {
|
|
16
24
|
const raw = parse(text);
|
|
17
25
|
const name = typeof raw.name === "string" && raw.name ? raw.name : DEFAULT_CONFIG.name;
|
|
18
26
|
const storage = STORAGE_KINDS.includes(raw.storage) ? raw.storage : DEFAULT_CONFIG.storage;
|
|
27
|
+
const platform = raw.platform === "container" ? "container" : DEFAULT_CONFIG.platform;
|
|
19
28
|
const authRaw = typeof raw.auth === "object" && raw.auth !== null ? raw.auth : {};
|
|
20
29
|
const admin = authRaw.admin === "oidc" ? "oidc" : "bearer-token";
|
|
21
30
|
const allowedOrigins = Array.isArray(raw.allowedOrigins) ? raw.allowedOrigins.filter((x) => typeof x === "string") : void 0;
|
|
22
31
|
const dashboard = raw.dashboard !== false;
|
|
23
|
-
return { name, storage, auth: { admin, read: "api-key" }, allowedOrigins, dashboard };
|
|
32
|
+
return { name, storage, platform, auth: { admin, read: "api-key" }, allowedOrigins, dashboard };
|
|
24
33
|
}
|
|
25
34
|
function serializeConfig(config) {
|
|
26
35
|
const lines = [
|
|
27
36
|
`name = ${JSON.stringify(config.name)}`,
|
|
28
37
|
`storage = ${JSON.stringify(config.storage)}`
|
|
29
38
|
];
|
|
39
|
+
if (config.platform !== "cloudflare") {
|
|
40
|
+
lines.push(`platform = ${JSON.stringify(config.platform)}`);
|
|
41
|
+
}
|
|
30
42
|
if (config.allowedOrigins && config.allowedOrigins.length > 0) {
|
|
31
43
|
lines.push(`allowedOrigins = ${JSON.stringify(config.allowedOrigins)}`);
|
|
32
44
|
}
|
|
@@ -46,6 +58,9 @@ function serializeConfig(config) {
|
|
|
46
58
|
export {
|
|
47
59
|
DEFAULT_CONFIG,
|
|
48
60
|
STORAGE_KINDS,
|
|
61
|
+
PLATFORM_KINDS,
|
|
62
|
+
containerStorageDefault,
|
|
63
|
+
asContainer,
|
|
49
64
|
parseConfig,
|
|
50
65
|
serializeConfig
|
|
51
66
|
};
|
package/dist/index.js
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
DEFAULT_CONFIG,
|
|
4
|
+
PLATFORM_KINDS,
|
|
4
5
|
STORAGE_KINDS,
|
|
6
|
+
asContainer,
|
|
7
|
+
containerStorageDefault,
|
|
5
8
|
parseConfig,
|
|
6
9
|
serializeConfig
|
|
7
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-HOSNPNBP.js";
|
|
8
11
|
|
|
9
12
|
// src/index.ts
|
|
10
13
|
import { spawnSync } from "child_process";
|
|
11
14
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
12
15
|
import { dirname, join } from "path";
|
|
16
|
+
import { createInterface } from "readline/promises";
|
|
13
17
|
import { parseArgs } from "util";
|
|
14
18
|
|
|
15
19
|
// src/admin.ts
|
|
@@ -87,6 +91,133 @@ async function setRules(client, key, rules) {
|
|
|
87
91
|
});
|
|
88
92
|
}
|
|
89
93
|
|
|
94
|
+
// src/generate-container.ts
|
|
95
|
+
var CONTAINER_DEPS = {
|
|
96
|
+
"@flaghoist/adapter-memory": "^0.1.2",
|
|
97
|
+
"@flaghoist/adapter-postgres": "^0.1.2",
|
|
98
|
+
"@flaghoist/adapter-redis": "^0.1.2",
|
|
99
|
+
"@flaghoist/server": "^0.3.0",
|
|
100
|
+
"@hono/node-server": "^2.1.1",
|
|
101
|
+
ioredis: "^5.4.0",
|
|
102
|
+
pg: "^8.13.0"
|
|
103
|
+
};
|
|
104
|
+
var CONTAINER_PORT = 8080;
|
|
105
|
+
function adminExpr(admin) {
|
|
106
|
+
return admin === "oidc" ? "oidc({ issuer: env.OIDC_ISSUER, audience: env.OIDC_AUDIENCE, groupsClaim: 'cognito:groups', allowedGroups: (env.ADMIN_GROUPS ?? '').split(',') })" : "bearerToken(env.ADMIN_TOKEN)";
|
|
107
|
+
}
|
|
108
|
+
function generateNodeEntry(config) {
|
|
109
|
+
const defaultStorage = containerStorageDefault(config.storage);
|
|
110
|
+
const serverImports = [
|
|
111
|
+
"apiKey",
|
|
112
|
+
"createFlagServer",
|
|
113
|
+
"memoryRateLimit",
|
|
114
|
+
config.auth.admin === "oidc" ? "oidc" : "bearerToken"
|
|
115
|
+
].sort();
|
|
116
|
+
const imports = [
|
|
117
|
+
`import { memoryAdapter } from '@flaghoist/adapter-memory'`,
|
|
118
|
+
`import { initPostgres, postgresAdapter } from '@flaghoist/adapter-postgres'`,
|
|
119
|
+
`import { redisAdapter } from '@flaghoist/adapter-redis'`,
|
|
120
|
+
`import { ${serverImports.join(", ")} } from '@flaghoist/server'`,
|
|
121
|
+
...config.dashboard ? [`import { dashboardHtml } from '@flaghoist/server/dashboard'`] : [],
|
|
122
|
+
`import { serve } from '@hono/node-server'`
|
|
123
|
+
].join("\n");
|
|
124
|
+
const dashboard = config.dashboard ? "\n dashboard: dashboardHtml," : "";
|
|
125
|
+
const origins = config.allowedOrigins && config.allowedOrigins.length > 0 ? JSON.stringify(config.allowedOrigins) : "undefined";
|
|
126
|
+
return `${imports}
|
|
127
|
+
|
|
128
|
+
// One image, any host. Everything is driven by environment variables, so the same container runs on
|
|
129
|
+
// a plain VPS, Fly.io, Railway, DigitalOcean, Cloud Run, ECS, or Kubernetes, with no code to edit.
|
|
130
|
+
const env = process.env
|
|
131
|
+
const kind = env.FLAGS_STORAGE ?? '${defaultStorage}'
|
|
132
|
+
|
|
133
|
+
async function makeStorage() {
|
|
134
|
+
if (kind === 'postgres') {
|
|
135
|
+
if (!env.DATABASE_URL) throw new Error('FLAGS_STORAGE=postgres requires DATABASE_URL')
|
|
136
|
+
const { Pool } = await import('pg')
|
|
137
|
+
const table = env.FLAGS_TABLE ?? 'flaghoist_flags'
|
|
138
|
+
const pool = new Pool({
|
|
139
|
+
connectionString: env.DATABASE_URL,
|
|
140
|
+
// Managed Postgres (Supabase, Neon, Render) requires TLS. Skip this if your URL sets sslmode.
|
|
141
|
+
ssl: /sslmode=/.test(env.DATABASE_URL) ? undefined : { rejectUnauthorized: false },
|
|
142
|
+
})
|
|
143
|
+
await initPostgres(pool, table)
|
|
144
|
+
return postgresAdapter(pool, { table })
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (kind === 'redis') {
|
|
148
|
+
if (!env.REDIS_URL) throw new Error('FLAGS_STORAGE=redis requires REDIS_URL')
|
|
149
|
+
const { default: Redis } = await import('ioredis')
|
|
150
|
+
return redisAdapter(new Redis(env.REDIS_URL), { hashKey: env.FLAGS_HASH_KEY ?? 'flaghoist:flags' })
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (kind === 'memory') {
|
|
154
|
+
console.warn('[flaghoist] FLAGS_STORAGE=memory: flags are in-process and are lost on restart.')
|
|
155
|
+
return memoryAdapter()
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
throw new Error(\`Unknown FLAGS_STORAGE "\${kind}". Use postgres, redis, or memory.\`)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const app = createFlagServer({
|
|
162
|
+
storage: await makeStorage(),${dashboard}
|
|
163
|
+
auth: {
|
|
164
|
+
admin: ${adminExpr(config.auth.admin)},
|
|
165
|
+
read: apiKey(env.READ_API_KEY),
|
|
166
|
+
},
|
|
167
|
+
rateLimit: memoryRateLimit(),
|
|
168
|
+
// Comma-separated list of browser origins allowed to read flags cross-origin.
|
|
169
|
+
allowedOrigins: env.FLAGS_CORS ? env.FLAGS_CORS.split(',').map((o) => o.trim()) : ${origins},
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
const port = Number(env.PORT ?? ${CONTAINER_PORT})
|
|
173
|
+
serve({ fetch: app.fetch, port, hostname: '0.0.0.0' })
|
|
174
|
+
console.log(\`[flaghoist] listening on :\${port} (storage: \${kind})\`)
|
|
175
|
+
`;
|
|
176
|
+
}
|
|
177
|
+
function generateDockerfile(config) {
|
|
178
|
+
const defaultStorage = containerStorageDefault(config.storage);
|
|
179
|
+
return `# Flaghoist as a portable container. Build once, run on any host: a VPS, Fly.io, Railway,
|
|
180
|
+
# DigitalOcean, Cloud Run, ECS, or Kubernetes. Configure it entirely with environment variables.
|
|
181
|
+
FROM node:22-alpine
|
|
182
|
+
|
|
183
|
+
WORKDIR /app
|
|
184
|
+
|
|
185
|
+
# Install the published Flaghoist packages. Copying only package.json first keeps this layer cached
|
|
186
|
+
# across code changes.
|
|
187
|
+
COPY package.json ./
|
|
188
|
+
RUN npm install --omit=dev --no-audit --no-fund
|
|
189
|
+
|
|
190
|
+
COPY server.mjs ./
|
|
191
|
+
|
|
192
|
+
ENV PORT=${CONTAINER_PORT}
|
|
193
|
+
ENV FLAGS_STORAGE=${defaultStorage}
|
|
194
|
+
EXPOSE ${CONTAINER_PORT}
|
|
195
|
+
|
|
196
|
+
# The /health route is unauthenticated, so it makes a good container healthcheck.
|
|
197
|
+
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s \\
|
|
198
|
+
CMD node -e "fetch('http://127.0.0.1:${CONTAINER_PORT}/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"
|
|
199
|
+
|
|
200
|
+
CMD ["node", "server.mjs"]
|
|
201
|
+
`;
|
|
202
|
+
}
|
|
203
|
+
function generateContainerPackageJson(config) {
|
|
204
|
+
const pkg = {
|
|
205
|
+
name: config.name,
|
|
206
|
+
version: "0.0.0",
|
|
207
|
+
private: true,
|
|
208
|
+
type: "module",
|
|
209
|
+
scripts: { start: "node server.mjs" },
|
|
210
|
+
dependencies: CONTAINER_DEPS
|
|
211
|
+
};
|
|
212
|
+
return `${JSON.stringify(pkg, null, 2)}
|
|
213
|
+
`;
|
|
214
|
+
}
|
|
215
|
+
function generateDockerignore() {
|
|
216
|
+
return ["node_modules", "npm-debug.log", "Dockerfile", ".dockerignore", "README.md", ""].join(
|
|
217
|
+
"\n"
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
|
|
90
221
|
// src/generate.ts
|
|
91
222
|
function storageSnippet(storage) {
|
|
92
223
|
switch (storage) {
|
|
@@ -124,7 +255,7 @@ function storageSnippet(storage) {
|
|
|
124
255
|
};
|
|
125
256
|
}
|
|
126
257
|
}
|
|
127
|
-
function
|
|
258
|
+
function adminExpr2(admin) {
|
|
128
259
|
return admin === "oidc" ? "oidc({ issuer: env.OIDC_ISSUER, audience: env.OIDC_AUDIENCE, groupsClaim: 'cognito:groups', allowedGroups: (env.ADMIN_GROUPS ?? '').split(',') })" : "bearerToken(env.ADMIN_TOKEN)";
|
|
129
260
|
}
|
|
130
261
|
function generateWorkerEntry(config) {
|
|
@@ -148,7 +279,7 @@ function generateWorkerEntry(config) {
|
|
|
148
279
|
export default createFlagServer((env) => ({
|
|
149
280
|
storage: ${storage.expr},
|
|
150
281
|
auth: {
|
|
151
|
-
admin: ${
|
|
282
|
+
admin: ${adminExpr2(config.auth.admin)},
|
|
152
283
|
read: apiKey(env.READ_API_KEY),
|
|
153
284
|
},${origins}${dashboard}
|
|
154
285
|
}))
|
|
@@ -316,27 +447,49 @@ function runInit(args) {
|
|
|
316
447
|
const { values } = parseArgs({
|
|
317
448
|
args,
|
|
318
449
|
allowPositionals: true,
|
|
319
|
-
options: {
|
|
450
|
+
options: {
|
|
451
|
+
name: { type: "string" },
|
|
452
|
+
storage: { type: "string" },
|
|
453
|
+
platform: { type: "string" }
|
|
454
|
+
}
|
|
320
455
|
});
|
|
321
456
|
if (existsSync("flaghoist.toml")) throw new Error("flaghoist.toml already exists.");
|
|
322
457
|
if (values.storage && !STORAGE_KINDS.includes(values.storage)) {
|
|
323
458
|
throw new Error(`Unknown storage "${values.storage}". One of: ${STORAGE_KINDS.join(", ")}.`);
|
|
324
459
|
}
|
|
325
|
-
|
|
460
|
+
if (values.platform && !PLATFORM_KINDS.includes(values.platform)) {
|
|
461
|
+
throw new Error(`Unknown platform "${values.platform}". One of: ${PLATFORM_KINDS.join(", ")}.`);
|
|
462
|
+
}
|
|
463
|
+
const base = {
|
|
326
464
|
...DEFAULT_CONFIG,
|
|
327
465
|
name: values.name ?? DEFAULT_CONFIG.name,
|
|
328
466
|
storage: values.storage ?? DEFAULT_CONFIG.storage
|
|
329
467
|
};
|
|
468
|
+
const config = values.platform === "container" ? asContainer(base) : base;
|
|
330
469
|
writeFileSync("flaghoist.toml", serializeConfig(config));
|
|
331
470
|
console.log("Created flaghoist.toml");
|
|
332
471
|
console.log("Next: `flaghoist deploy` to ship it, or `flaghoist eject` to own the code.");
|
|
333
472
|
}
|
|
334
|
-
function
|
|
335
|
-
|
|
473
|
+
function entryFile(platform) {
|
|
474
|
+
return platform === "container" ? "server.mjs" : "src/index.ts";
|
|
475
|
+
}
|
|
476
|
+
function projectFiles(config) {
|
|
477
|
+
if (config.platform === "container") {
|
|
478
|
+
return [
|
|
479
|
+
["server.mjs", generateNodeEntry(config)],
|
|
480
|
+
["Dockerfile", generateDockerfile(config)],
|
|
481
|
+
[".dockerignore", generateDockerignore()],
|
|
482
|
+
["package.json", generateContainerPackageJson(config)]
|
|
483
|
+
];
|
|
484
|
+
}
|
|
485
|
+
return [
|
|
336
486
|
["src/index.ts", generateWorkerEntry(config)],
|
|
337
487
|
["wrangler.toml", generateWranglerToml(config)],
|
|
338
488
|
["package.json", generatePackageJson(config)]
|
|
339
489
|
];
|
|
490
|
+
}
|
|
491
|
+
function writeProject(config, dir) {
|
|
492
|
+
const files = projectFiles(config);
|
|
340
493
|
const existing = files.map(([name]) => name).filter((name) => existsSync(join(dir, name)));
|
|
341
494
|
if (existing.length > 0) {
|
|
342
495
|
throw new Error(
|
|
@@ -349,8 +502,15 @@ Flaghoist deploys as its own service, so give it a directory of its own:
|
|
|
349
502
|
}
|
|
350
503
|
function runEject() {
|
|
351
504
|
const config = loadConfig();
|
|
352
|
-
|
|
505
|
+
const entry = entryFile(config.platform);
|
|
506
|
+
if (existsSync(entry)) throw new Error(`${entry} already exists. Already ejected?`);
|
|
353
507
|
writeProject(config, ".");
|
|
508
|
+
if (config.platform === "container") {
|
|
509
|
+
console.log("Ejected to a code project you own: server.mjs, Dockerfile, package.json");
|
|
510
|
+
console.log("Edit server.mjs freely, then build and run the container, or deploy to a host:");
|
|
511
|
+
console.log(" https://docs.flaghoist.dev/deploy/overview/");
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
354
514
|
console.log("Ejected to a code project you own: src/index.ts, wrangler.toml, package.json");
|
|
355
515
|
if (config.storage === "cloudflare-kv") {
|
|
356
516
|
console.log("Create your KV namespace and paste the id into wrangler.toml:");
|
|
@@ -392,8 +552,49 @@ function ensureDependencies() {
|
|
|
392
552
|
throw new Error("`npm install` failed. Run it yourself, then `flaghoist deploy` again.");
|
|
393
553
|
}
|
|
394
554
|
}
|
|
395
|
-
function
|
|
396
|
-
const
|
|
555
|
+
function parseDeployTarget(args) {
|
|
556
|
+
const i = args.indexOf("--target");
|
|
557
|
+
if (i < 0) return null;
|
|
558
|
+
const value = args[i + 1]?.toLowerCase();
|
|
559
|
+
if (value === "cloudflare" || value === "cf" || value === "workers") return "cloudflare";
|
|
560
|
+
return "other";
|
|
561
|
+
}
|
|
562
|
+
async function promptDeployTarget() {
|
|
563
|
+
console.log("Where do you want to deploy?");
|
|
564
|
+
console.log(" 1) Cloudflare Workers recommended, deploys in one command");
|
|
565
|
+
console.log(" 2) Another platform Render, a container, any Node host");
|
|
566
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
567
|
+
try {
|
|
568
|
+
const answer = (await rl.question("Choose [1]: ")).trim().toLowerCase();
|
|
569
|
+
return answer === "2" || answer.startsWith("o") || answer.startsWith("r") ? "other" : "cloudflare";
|
|
570
|
+
} finally {
|
|
571
|
+
rl.close();
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
function deployContainer(config) {
|
|
575
|
+
const container = asContainer(config);
|
|
576
|
+
if (config.platform !== container.platform || config.storage !== container.storage) {
|
|
577
|
+
writeFileSync("flaghoist.toml", serializeConfig(container));
|
|
578
|
+
}
|
|
579
|
+
if (!existsSync(entryFile("container"))) writeProject(container, ".");
|
|
580
|
+
console.log(`
|
|
581
|
+
Scaffolded a container project: server.mjs, Dockerfile, package.json.
|
|
582
|
+
|
|
583
|
+
It runs on any container or Node host, configured by environment variables (FLAGS_STORAGE,
|
|
584
|
+
DATABASE_URL, ADMIN_TOKEN, READ_API_KEY). Build and run it with Docker:
|
|
585
|
+
|
|
586
|
+
docker build -t ${container.name} .
|
|
587
|
+
docker run -p 8080:8080 -e ADMIN_TOKEN=... -e READ_API_KEY=... ${container.name}
|
|
588
|
+
|
|
589
|
+
Or deploy it to a host:
|
|
590
|
+
Render https://docs.flaghoist.dev/deploy/render/
|
|
591
|
+
Fly.io https://docs.flaghoist.dev/deploy/fly/
|
|
592
|
+
Railway https://docs.flaghoist.dev/deploy/railway/
|
|
593
|
+
All targets https://docs.flaghoist.dev/deploy/overview/
|
|
594
|
+
|
|
595
|
+
Missing a platform you need? Open an issue at https://github.com/flaghoist/flaghoist/issues.`);
|
|
596
|
+
}
|
|
597
|
+
async function deployCloudflare(config) {
|
|
397
598
|
if (!existsSync("src/index.ts")) writeProject(config, ".");
|
|
398
599
|
ensureDependencies();
|
|
399
600
|
ensureKvNamespace(config);
|
|
@@ -401,15 +602,25 @@ function runDeploy() {
|
|
|
401
602
|
const result = spawnSync("npx", ["wrangler", "deploy"], { stdio: "inherit" });
|
|
402
603
|
process.exit(result.status ?? 0);
|
|
403
604
|
}
|
|
605
|
+
async function runDeploy(args) {
|
|
606
|
+
const config = loadConfig();
|
|
607
|
+
let target = parseDeployTarget(args);
|
|
608
|
+
if (target === null) {
|
|
609
|
+
if (config.platform === "container") target = "other";
|
|
610
|
+
else target = process.stdin.isTTY ? await promptDeployTarget() : "cloudflare";
|
|
611
|
+
}
|
|
612
|
+
if (target === "other") return deployContainer(config);
|
|
613
|
+
return deployCloudflare(config);
|
|
614
|
+
}
|
|
404
615
|
function printHelp() {
|
|
405
616
|
console.log(`flaghoist ${VERSION} \u2014 hoist your own feature flags
|
|
406
617
|
|
|
407
618
|
Usage: flaghoist <command>
|
|
408
619
|
|
|
409
620
|
Scaffolding
|
|
410
|
-
init [--name N] [--storage cloudflare-kv|redis|postgres|memory]
|
|
411
|
-
eject Generate a code project you own
|
|
412
|
-
deploy
|
|
621
|
+
init [--name N] [--storage cloudflare-kv|redis|postgres|memory] [--platform cloudflare|container]
|
|
622
|
+
eject Generate a code project you own (a Worker, or a container)
|
|
623
|
+
deploy [--target T] Deploy (prompts for the platform; T is cloudflare or other)
|
|
413
624
|
|
|
414
625
|
Flag management (needs --url/--token or FLAGS_URL/FLAGS_ADMIN_TOKEN)
|
|
415
626
|
flag list
|
|
@@ -430,7 +641,7 @@ async function main() {
|
|
|
430
641
|
case "eject":
|
|
431
642
|
return runEject();
|
|
432
643
|
case "deploy":
|
|
433
|
-
return runDeploy();
|
|
644
|
+
return runDeploy(rest);
|
|
434
645
|
case "-v":
|
|
435
646
|
case "--version":
|
|
436
647
|
return console.log(VERSION);
|
package/dist/lib.d.ts
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
type StorageKind = 'cloudflare-kv' | 'redis' | 'postgres' | 'memory';
|
|
2
2
|
type AdminAuthKind = 'bearer-token' | 'oidc';
|
|
3
|
+
/**
|
|
4
|
+
* The shape of project `flaghoist deploy`/`eject` scaffolds. `cloudflare` is a Worker plus a
|
|
5
|
+
* `wrangler.toml`; `container` is a Node entry plus a `Dockerfile`, which runs on any container or
|
|
6
|
+
* Node host (Render, Fly, Railway, a VPS). Cloudflare is the default, so a config written before
|
|
7
|
+
* this key existed still scaffolds a Worker.
|
|
8
|
+
*/
|
|
9
|
+
type PlatformKind = 'cloudflare' | 'container';
|
|
3
10
|
interface FlaghoistConfig {
|
|
4
11
|
name: string;
|
|
5
12
|
storage: StorageKind;
|
|
13
|
+
/** Deploy shape to scaffold. Defaults to `cloudflare`. */
|
|
14
|
+
platform: PlatformKind;
|
|
6
15
|
auth: {
|
|
7
16
|
admin: AdminAuthKind;
|
|
8
17
|
read: 'api-key';
|
|
@@ -14,9 +23,27 @@ interface FlaghoistConfig {
|
|
|
14
23
|
declare const DEFAULT_CONFIG: FlaghoistConfig;
|
|
15
24
|
/** Every storage backend selectable by name in `flaghoist.toml`. */
|
|
16
25
|
declare const STORAGE_KINDS: readonly StorageKind[];
|
|
26
|
+
/** Every deploy shape selectable by name in `flaghoist.toml`. */
|
|
27
|
+
declare const PLATFORM_KINDS: readonly PlatformKind[];
|
|
28
|
+
/** The stores a container can reach. Cloudflare KV is a Worker binding, so it is not one of them. */
|
|
29
|
+
type ContainerStorage = 'postgres' | 'redis' | 'memory';
|
|
30
|
+
/**
|
|
31
|
+
* The storage a container project uses. Cloudflare KV cannot be reached off Workers, so a config
|
|
32
|
+
* that still names it (the scaffolding default) becomes postgres, the store every container deploy
|
|
33
|
+
* guide uses. This is the one rule for a container's storage: the CLI reuses it to write a coherent
|
|
34
|
+
* `flaghoist.toml`, and the generated entry bakes it as the `FLAGS_STORAGE` default. Every kind
|
|
35
|
+
* stays overridable at runtime via the env var.
|
|
36
|
+
*/
|
|
37
|
+
declare function containerStorageDefault(storage: StorageKind): ContainerStorage;
|
|
38
|
+
/**
|
|
39
|
+
* Recast a config for the container platform, running its storage through the container rule so the
|
|
40
|
+
* result never names a store the platform cannot use. This is the one place the platform switch
|
|
41
|
+
* rewrites a config, so `init`, `create-flaghoist`, `deploy` and `eject` all stay consistent.
|
|
42
|
+
*/
|
|
43
|
+
declare function asContainer(config: FlaghoistConfig): FlaghoistConfig;
|
|
17
44
|
/** Parse a `flaghoist.toml` into a validated config, falling back to defaults for unknown values. */
|
|
18
45
|
declare function parseConfig(text: string): FlaghoistConfig;
|
|
19
46
|
/** Render a config back to `flaghoist.toml` text. */
|
|
20
47
|
declare function serializeConfig(config: FlaghoistConfig): string;
|
|
21
48
|
|
|
22
|
-
export { type AdminAuthKind, DEFAULT_CONFIG, type FlaghoistConfig, STORAGE_KINDS, type StorageKind, parseConfig, serializeConfig };
|
|
49
|
+
export { type AdminAuthKind, type ContainerStorage, DEFAULT_CONFIG, type FlaghoistConfig, PLATFORM_KINDS, type PlatformKind, STORAGE_KINDS, type StorageKind, asContainer, containerStorageDefault, parseConfig, serializeConfig };
|
package/dist/lib.js
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DEFAULT_CONFIG,
|
|
3
|
+
PLATFORM_KINDS,
|
|
3
4
|
STORAGE_KINDS,
|
|
5
|
+
asContainer,
|
|
6
|
+
containerStorageDefault,
|
|
4
7
|
parseConfig,
|
|
5
8
|
serializeConfig
|
|
6
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-HOSNPNBP.js";
|
|
7
10
|
export {
|
|
8
11
|
DEFAULT_CONFIG,
|
|
12
|
+
PLATFORM_KINDS,
|
|
9
13
|
STORAGE_KINDS,
|
|
14
|
+
asContainer,
|
|
15
|
+
containerStorageDefault,
|
|
10
16
|
parseConfig,
|
|
11
17
|
serializeConfig
|
|
12
18
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flaghoist",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Scaffold, deploy, and manage your Flaghoist feature-flag service from the terminal.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
"smol-toml": "^1.8.0"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
|
-
"@flaghoist/adapter-memory": "0.1.
|
|
30
|
-
"@flaghoist/core": "0.1.
|
|
31
|
-
"@flaghoist/server": "0.1
|
|
29
|
+
"@flaghoist/adapter-memory": "0.1.2",
|
|
30
|
+
"@flaghoist/core": "0.1.2",
|
|
31
|
+
"@flaghoist/server": "0.3.1"
|
|
32
32
|
},
|
|
33
33
|
"publishConfig": {
|
|
34
34
|
"access": "public"
|