@dragonmastery/tamer 0.1.2 → 0.28.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/README.md +569 -18
- package/dist/CFApiClient-DhbyyV71.mjs +868 -0
- package/dist/CFApiClient-DhbyyV71.mjs.map +1 -0
- package/dist/StateManager-DTqtLLVX.mjs +760 -0
- package/dist/StateManager-DTqtLLVX.mjs.map +1 -0
- package/dist/apply-B0b_jjGv.mjs +423 -0
- package/dist/apply-B0b_jjGv.mjs.map +1 -0
- package/dist/applyTarget-BetDYdeS.mjs +152 -0
- package/dist/applyTarget-BetDYdeS.mjs.map +1 -0
- package/dist/bootstrap-CBzPilB1.mjs +33 -0
- package/dist/bootstrap-CBzPilB1.mjs.map +1 -0
- package/dist/buildDispatchUploadForm-BoUB93b3.mjs +38 -0
- package/dist/buildDispatchUploadForm-BoUB93b3.mjs.map +1 -0
- package/dist/cloudflareSnapshot-B4FOaNr0.mjs +163 -0
- package/dist/cloudflareSnapshot-B4FOaNr0.mjs.map +1 -0
- package/dist/deploy-gHEQxhmx.mjs +119 -0
- package/dist/deploy-gHEQxhmx.mjs.map +1 -0
- package/dist/destroy-B21f3wgq.mjs +215 -0
- package/dist/destroy-B21f3wgq.mjs.map +1 -0
- package/dist/destroy-tenant-BW2nasnK.mjs +103 -0
- package/dist/destroy-tenant-BW2nasnK.mjs.map +1 -0
- package/dist/dev-Dt26nzMJ.mjs +103 -0
- package/dist/dev-Dt26nzMJ.mjs.map +1 -0
- package/dist/dns-records.resolve-C2T0m4NG.mjs +3 -0
- package/dist/dns-records.resolve-DwBR_1WI.mjs +47 -0
- package/dist/dns-records.resolve-DwBR_1WI.mjs.map +1 -0
- package/dist/dns-records.sync-Bpzz9H0s.mjs +75 -0
- package/dist/dns-records.sync-Bpzz9H0s.mjs.map +1 -0
- package/dist/doctor-C_hs7k2D.mjs +34 -0
- package/dist/doctor-C_hs7k2D.mjs.map +1 -0
- package/dist/drift-D5qzCTft.mjs +10 -0
- package/dist/drift-D8ZrSgTn.mjs +323 -0
- package/dist/drift-D8ZrSgTn.mjs.map +1 -0
- package/dist/events-BSwGdkGj.mjs +68 -0
- package/dist/events-BSwGdkGj.mjs.map +1 -0
- package/dist/fetchStackImports-B4ZJahOt.mjs +3817 -0
- package/dist/fetchStackImports-B4ZJahOt.mjs.map +1 -0
- package/dist/generator-CIMbcPzv.mjs +77 -0
- package/dist/generator-CIMbcPzv.mjs.map +1 -0
- package/dist/import-BrduwA9Z.mjs +164 -0
- package/dist/import-BrduwA9Z.mjs.map +1 -0
- package/dist/index.d.mts +5568 -1297
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +18 -1
- package/dist/index.mjs.map +1 -0
- package/dist/loader-DP7yXqT6.mjs +518 -0
- package/dist/loader-DP7yXqT6.mjs.map +1 -0
- package/dist/logpush-job-xS7270FZ.mjs +1106 -0
- package/dist/logpush-job-xS7270FZ.mjs.map +1 -0
- package/dist/migrate-CahG6BYV.mjs +87 -0
- package/dist/migrate-CahG6BYV.mjs.map +1 -0
- package/dist/normalize-Bx0bpFop.mjs +236 -0
- package/dist/normalize-Bx0bpFop.mjs.map +1 -0
- package/dist/plan-DWvsvy1U.mjs +453 -0
- package/dist/plan-DWvsvy1U.mjs.map +1 -0
- package/dist/planFormat-CJw8Kq2s.mjs +119 -0
- package/dist/planFormat-CJw8Kq2s.mjs.map +1 -0
- package/dist/provision-tenant-WTKo93Y0.mjs +192 -0
- package/dist/provision-tenant-WTKo93Y0.mjs.map +1 -0
- package/dist/r2S3EmptyBucket-DD81ZWQ7.mjs +92 -0
- package/dist/r2S3EmptyBucket-DD81ZWQ7.mjs.map +1 -0
- package/dist/stackOutputs-W9mnnJuj.mjs +69 -0
- package/dist/stackOutputs-W9mnnJuj.mjs.map +1 -0
- package/dist/status-DLwREPjb.mjs +198 -0
- package/dist/status-DLwREPjb.mjs.map +1 -0
- package/dist/sync-f2K2blwm.mjs +90 -0
- package/dist/sync-f2K2blwm.mjs.map +1 -0
- package/dist/tamer.d.mts +1 -0
- package/dist/tamer.mjs +4553 -0
- package/dist/tamer.mjs.map +1 -0
- package/dist/tamerArtifactsR2-Ccgplu2Q.mjs +52 -0
- package/dist/tamerArtifactsR2-Ccgplu2Q.mjs.map +1 -0
- package/dist/types-CqxqYnrT.mjs +44 -0
- package/dist/types-CqxqYnrT.mjs.map +1 -0
- package/dist/verifyPlanFile-c16z1AMH.mjs +33 -0
- package/dist/verifyPlanFile-c16z1AMH.mjs.map +1 -0
- package/dist/wfp-delete-DysvX1u7.mjs +36 -0
- package/dist/wfp-delete-DysvX1u7.mjs.map +1 -0
- package/dist/wfp-put-jaVd_LjO.mjs +52 -0
- package/dist/wfp-put-jaVd_LjO.mjs.map +1 -0
- package/dist/worker-route-Be2IvOdr.mjs +263 -0
- package/dist/worker-route-Be2IvOdr.mjs.map +1 -0
- package/dist/workers-aGILs77X.mjs +87 -0
- package/dist/workers-aGILs77X.mjs.map +1 -0
- package/dist/wranglerSpawn-DmEz0ldT.mjs +24 -0
- package/dist/wranglerSpawn-DmEz0ldT.mjs.map +1 -0
- package/dist/zoneResolver-VoxLHM4N.mjs +32 -0
- package/dist/zoneResolver-VoxLHM4N.mjs.map +1 -0
- package/package.json +38 -3
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { a as resourceModules } from "./fetchStackImports-B4ZJahOt.mjs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { writeFileSync } from "fs";
|
|
4
|
+
|
|
5
|
+
//#region src/features/secrets-store/secrets-store.generate.ts
|
|
6
|
+
/**
|
|
7
|
+
* Resolve `secretsStoreSecrets[]` worker bindings into wrangler-shape
|
|
8
|
+
* `secrets_store_secrets[]` entries by looking up the referenced
|
|
9
|
+
* Tamer-managed store's `store_id` in state. Throws with an actionable
|
|
10
|
+
* message if a referenced store has never been applied — the binding
|
|
11
|
+
* would otherwise be deployed against a non-existent store.
|
|
12
|
+
*/
|
|
13
|
+
function secretsStoreSecretsGenerate(bindings, env, state, naming) {
|
|
14
|
+
const out = [];
|
|
15
|
+
for (const b of bindings) {
|
|
16
|
+
const derivedName = naming.secretsStoreName(b.store, env);
|
|
17
|
+
const entry = state.get(`secrets_store:${derivedName}`);
|
|
18
|
+
if (!entry || entry.type !== "secrets_store") throw new Error(`Secrets Store binding "${b.binding}" references store "${b.store}" — not in state. Declare it under \`resources.secretsStores\` and run 'tamer apply --env ${env}' first.`);
|
|
19
|
+
out.push({
|
|
20
|
+
binding: b.binding,
|
|
21
|
+
store_id: entry.cfId,
|
|
22
|
+
secret_name: b.secretName
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
return out;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region src/core/wrangler/generator.ts
|
|
30
|
+
/**
|
|
31
|
+
* Build the worker's `wrangler.json` by asking every registered resource
|
|
32
|
+
* module for its bindings fragment, then merging on top of the
|
|
33
|
+
* passthrough config (`resolved.wranglerConfig`). Adding a new resource
|
|
34
|
+
* kind requires zero edits here — register the module, done.
|
|
35
|
+
*/
|
|
36
|
+
function generateWranglerConfig(resolved, state, naming) {
|
|
37
|
+
let config = {
|
|
38
|
+
...resolved.wranglerConfig,
|
|
39
|
+
$schema: "node_modules/wrangler/config-schema.json"
|
|
40
|
+
};
|
|
41
|
+
const worker = {
|
|
42
|
+
workerKey: resolved.workerKey,
|
|
43
|
+
deployedName: resolved.workerName
|
|
44
|
+
};
|
|
45
|
+
for (const mod of resourceModules) {
|
|
46
|
+
const resources = mod.pickResources(resolved.resources);
|
|
47
|
+
const fragment = mod.generate({
|
|
48
|
+
resources,
|
|
49
|
+
env: resolved.env,
|
|
50
|
+
state,
|
|
51
|
+
naming,
|
|
52
|
+
passthrough: resolved.wranglerConfig,
|
|
53
|
+
worker
|
|
54
|
+
});
|
|
55
|
+
config = {
|
|
56
|
+
...config,
|
|
57
|
+
...fragment
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
const secretBindings = resolved.resources?.secretsStoreSecrets ?? [];
|
|
61
|
+
if (secretBindings.length > 0) {
|
|
62
|
+
const passthroughSecrets = resolved.wranglerConfig.secrets_store_secrets ?? [];
|
|
63
|
+
const generated = secretsStoreSecretsGenerate(secretBindings, resolved.env, state, naming);
|
|
64
|
+
config = {
|
|
65
|
+
...config,
|
|
66
|
+
secrets_store_secrets: [...passthroughSecrets, ...generated]
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
return config;
|
|
70
|
+
}
|
|
71
|
+
function writeWranglerJson(workerDir, config, outFile = "wrangler.json") {
|
|
72
|
+
writeFileSync(join(workerDir, outFile), JSON.stringify(config, null, 2), "utf-8");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
//#endregion
|
|
76
|
+
export { writeWranglerJson as n, generateWranglerConfig as t };
|
|
77
|
+
//# sourceMappingURL=generator-CIMbcPzv.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generator-CIMbcPzv.mjs","names":["out: GeneratedSecretsStoreSecretBinding[]","config: WranglerConfig"],"sources":["../src/features/secrets-store/secrets-store.generate.ts","../src/core/wrangler/generator.ts"],"sourcesContent":["import type { NamingEngine } from \"../../core/naming/NamingEngine.js\";\nimport type {\n StateEntry,\n SecretsStoreSecretBinding,\n SecretsStoreStateEntry,\n} from \"../../types.js\";\n\nexport interface SecretsStoreStateLike {\n get(key: string): StateEntry | undefined;\n getAll(): Record<string, StateEntry>;\n}\n\n/**\n * One row of wrangler's `secrets_store_secrets[]` array. Resolved from a\n * worker's declared `secretsStoreSecrets[]` by looking up the\n * Tamer-managed store's `store_id` in state. The named secret itself must\n * already exist inside the store (created out-of-band via wrangler\n * `secrets-store secret create`); Tamer never reads or writes secret\n * values.\n */\nexport interface GeneratedSecretsStoreSecretBinding {\n binding: string;\n store_id: string;\n secret_name: string;\n}\n\n/**\n * Resolve `secretsStoreSecrets[]` worker bindings into wrangler-shape\n * `secrets_store_secrets[]` entries by looking up the referenced\n * Tamer-managed store's `store_id` in state. Throws with an actionable\n * message if a referenced store has never been applied — the binding\n * would otherwise be deployed against a non-existent store.\n */\nexport function secretsStoreSecretsGenerate(\n bindings: SecretsStoreSecretBinding[],\n env: string,\n state: SecretsStoreStateLike,\n naming: NamingEngine,\n): GeneratedSecretsStoreSecretBinding[] {\n const out: GeneratedSecretsStoreSecretBinding[] = [];\n for (const b of bindings) {\n const derivedName = naming.secretsStoreName(b.store, env);\n const entry = state.get(`secrets_store:${derivedName}`) as\n | SecretsStoreStateEntry\n | undefined;\n if (!entry || entry.type !== \"secrets_store\") {\n throw new Error(\n `Secrets Store binding \"${b.binding}\" references store \"${b.store}\" — ` +\n `not in state. Declare it under \\`resources.secretsStores\\` and run ` +\n `'tamer apply --env ${env}' first.`,\n );\n }\n out.push({\n binding: b.binding,\n store_id: entry.cfId,\n secret_name: b.secretName,\n });\n }\n return out;\n}\n","import { writeFileSync } from \"fs\";\nimport { join } from \"path\";\nimport type { WranglerConfig } from \"../../generated/wrangler-types.js\";\nimport type { ResolvedWorkerConfig } from \"../config/resolver.js\";\nimport type { StateManager } from \"../state/StateManager.js\";\nimport type { NamingEngine } from \"../naming/NamingEngine.js\";\nimport { resourceModules } from \"../registry/registry.js\";\nimport { secretsStoreSecretsGenerate } from \"../../features/secrets-store/secrets-store.generate.js\";\n\nexport class CfiError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"CfiError\";\n }\n}\n\n/**\n * Build the worker's `wrangler.json` by asking every registered resource\n * module for its bindings fragment, then merging on top of the\n * passthrough config (`resolved.wranglerConfig`). Adding a new resource\n * kind requires zero edits here — register the module, done.\n */\nexport function generateWranglerConfig(\n resolved: ResolvedWorkerConfig,\n state: StateManager,\n naming: NamingEngine,\n): WranglerConfig {\n let config: WranglerConfig = {\n ...resolved.wranglerConfig,\n $schema: \"node_modules/wrangler/config-schema.json\",\n };\n\n const worker = {\n workerKey: resolved.workerKey,\n deployedName: resolved.workerName,\n };\n for (const mod of resourceModules) {\n const resources = mod.pickResources(resolved.resources);\n const fragment = mod.generate({\n resources,\n env: resolved.env,\n state,\n naming,\n passthrough: resolved.wranglerConfig,\n worker,\n });\n config = { ...config, ...fragment };\n }\n\n const secretBindings = resolved.resources?.secretsStoreSecrets ?? [];\n if (secretBindings.length > 0) {\n const passthroughSecrets =\n resolved.wranglerConfig.secrets_store_secrets ?? [];\n const generated = secretsStoreSecretsGenerate(\n secretBindings,\n resolved.env,\n state,\n naming,\n );\n config = {\n ...config,\n secrets_store_secrets: [...passthroughSecrets, ...generated],\n };\n }\n\n return config;\n}\n\nexport function writeWranglerJson(\n workerDir: string,\n config: WranglerConfig,\n outFile: string = \"wrangler.json\",\n): void {\n const path = join(workerDir, outFile);\n writeFileSync(path, JSON.stringify(config, null, 2), \"utf-8\");\n}\n"],"mappings":";;;;;;;;;;;;AAiCA,SAAgB,4BACd,UACA,KACA,OACA,QACsC;CACtC,MAAMA,MAA4C,EAAE;AACpD,MAAK,MAAM,KAAK,UAAU;EACxB,MAAM,cAAc,OAAO,iBAAiB,EAAE,OAAO,IAAI;EACzD,MAAM,QAAQ,MAAM,IAAI,iBAAiB,cAAc;AAGvD,MAAI,CAAC,SAAS,MAAM,SAAS,gBAC3B,OAAM,IAAI,MACR,0BAA0B,EAAE,QAAQ,sBAAsB,EAAE,MAAM,4FAE1C,IAAI,UAC7B;AAEH,MAAI,KAAK;GACP,SAAS,EAAE;GACX,UAAU,MAAM;GAChB,aAAa,EAAE;GAChB,CAAC;;AAEJ,QAAO;;;;;;;;;;;ACpCT,SAAgB,uBACd,UACA,OACA,QACgB;CAChB,IAAIC,SAAyB;EAC3B,GAAG,SAAS;EACZ,SAAS;EACV;CAED,MAAM,SAAS;EACb,WAAW,SAAS;EACpB,cAAc,SAAS;EACxB;AACD,MAAK,MAAM,OAAO,iBAAiB;EACjC,MAAM,YAAY,IAAI,cAAc,SAAS,UAAU;EACvD,MAAM,WAAW,IAAI,SAAS;GAC5B;GACA,KAAK,SAAS;GACd;GACA;GACA,aAAa,SAAS;GACtB;GACD,CAAC;AACF,WAAS;GAAE,GAAG;GAAQ,GAAG;GAAU;;CAGrC,MAAM,iBAAiB,SAAS,WAAW,uBAAuB,EAAE;AACpE,KAAI,eAAe,SAAS,GAAG;EAC7B,MAAM,qBACJ,SAAS,eAAe,yBAAyB,EAAE;EACrD,MAAM,YAAY,4BAChB,gBACA,SAAS,KACT,OACA,OACD;AACD,WAAS;GACP,GAAG;GACH,uBAAuB,CAAC,GAAG,oBAAoB,GAAG,UAAU;GAC7D;;AAGH,QAAO;;AAGT,SAAgB,kBACd,WACA,QACA,UAAkB,iBACZ;AAEN,eADa,KAAK,WAAW,QAAQ,EACjB,KAAK,UAAU,QAAQ,MAAM,EAAE,EAAE,QAAQ"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { n as loadConfig, t as getWorkers } from "./loader-DP7yXqT6.mjs";
|
|
2
|
+
import { n as cloudflareAccountIdFromEnv, t as CFApiClient } from "./CFApiClient-DhbyyV71.mjs";
|
|
3
|
+
import { _ as namingFromConfig, a as resourceModules, i as getResourceModule, p as resolveWorkerConfig, t as fetchStackImports } from "./fetchStackImports-B4ZJahOt.mjs";
|
|
4
|
+
import { f as stackNameForConfig, t as StateManager } from "./StateManager-DTqtLLVX.mjs";
|
|
5
|
+
import "./r2S3EmptyBucket-DD81ZWQ7.mjs";
|
|
6
|
+
import { n as workerRouteStateKey, t as findZoneIdByName } from "./zoneResolver-VoxLHM4N.mjs";
|
|
7
|
+
|
|
8
|
+
//#region src/cli/commands/import.ts
|
|
9
|
+
const REGISTRY_KINDS = new Set(resourceModules.map((m) => m.kind));
|
|
10
|
+
/**
|
|
11
|
+
* `tamer import` — register an existing Cloudflare resource into Tamer state
|
|
12
|
+
* by logical name (CloudFormation `import_resources` analogue). For all
|
|
13
|
+
* resource-registry kinds the work is delegated to the module's
|
|
14
|
+
* `importOne` hook; `dispatch_namespace` and `worker_route` remain inline
|
|
15
|
+
* because they aren't registry resources today.
|
|
16
|
+
*/
|
|
17
|
+
async function runImport(options) {
|
|
18
|
+
const env = options.env;
|
|
19
|
+
if (env === "local") throw new Error("import: --env local is not supported (no persisted state)");
|
|
20
|
+
const baseDir = process.cwd();
|
|
21
|
+
const config = await loadConfig(options.configPath, { env });
|
|
22
|
+
const accountId = config.account_id ?? cloudflareAccountIdFromEnv();
|
|
23
|
+
if (!accountId) throw new Error("account_id required in config or CLOUDFLARE_ACCOUNT_ID env var");
|
|
24
|
+
const api = new CFApiClient(accountId);
|
|
25
|
+
const naming = namingFromConfig(config);
|
|
26
|
+
const state = new StateManager(config.tenant.id, env, stackNameForConfig(config));
|
|
27
|
+
await state.hydrate(api);
|
|
28
|
+
state.beginOperation("import", `${options.kind}:${options.logical}`);
|
|
29
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
30
|
+
try {
|
|
31
|
+
if (REGISTRY_KINDS.has(options.kind)) {
|
|
32
|
+
const mod = getResourceModule(options.kind);
|
|
33
|
+
if (!mod) throw new Error(`unknown import kind "${options.kind}"`);
|
|
34
|
+
await mod.importOne({
|
|
35
|
+
options: {
|
|
36
|
+
env,
|
|
37
|
+
kind: options.kind,
|
|
38
|
+
logical: options.logical,
|
|
39
|
+
cfId: options.cfId,
|
|
40
|
+
shardDate: options.shardDate,
|
|
41
|
+
createdDate: options.createdDate,
|
|
42
|
+
routeId: options.routeId,
|
|
43
|
+
zoneName: options.zoneName
|
|
44
|
+
},
|
|
45
|
+
env,
|
|
46
|
+
api,
|
|
47
|
+
state,
|
|
48
|
+
naming,
|
|
49
|
+
ts
|
|
50
|
+
});
|
|
51
|
+
} else if (options.kind === "dispatch_namespace") await importDispatchNamespace({
|
|
52
|
+
options,
|
|
53
|
+
env,
|
|
54
|
+
api,
|
|
55
|
+
state,
|
|
56
|
+
ts
|
|
57
|
+
});
|
|
58
|
+
else if (options.kind === "dns_record") await importDnsRecord({
|
|
59
|
+
options,
|
|
60
|
+
env,
|
|
61
|
+
api,
|
|
62
|
+
state,
|
|
63
|
+
config,
|
|
64
|
+
ts
|
|
65
|
+
});
|
|
66
|
+
else if (options.kind === "worker_route") await importWorkerRoute({
|
|
67
|
+
options,
|
|
68
|
+
env,
|
|
69
|
+
baseDir,
|
|
70
|
+
accountId,
|
|
71
|
+
api,
|
|
72
|
+
naming,
|
|
73
|
+
state,
|
|
74
|
+
config,
|
|
75
|
+
ts
|
|
76
|
+
});
|
|
77
|
+
else throw new Error(`unknown import kind "${options.kind}"`);
|
|
78
|
+
state.finishOperation("imported");
|
|
79
|
+
await state.persist(api);
|
|
80
|
+
console.log(`Imported ${options.kind} "${options.logical}" into ${env} state.`);
|
|
81
|
+
} catch (err) {
|
|
82
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
83
|
+
state.failOperation(msg);
|
|
84
|
+
try {
|
|
85
|
+
await state.persist(api);
|
|
86
|
+
} catch {}
|
|
87
|
+
throw err;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async function importDispatchNamespace(ctx) {
|
|
91
|
+
const { options, api, state, ts } = ctx;
|
|
92
|
+
const hit = (await api.dispatchNamespaceListAll()).find((n) => n.namespace_name === options.cfId);
|
|
93
|
+
if (!hit) throw new Error(`import dispatch_namespace: namespace "${options.cfId}" not found`);
|
|
94
|
+
const derivedName = hit.namespace_name;
|
|
95
|
+
const key = `dispatch_ns:${derivedName}`;
|
|
96
|
+
const existing = state.get(key);
|
|
97
|
+
state.set(key, {
|
|
98
|
+
type: "dispatch_namespace",
|
|
99
|
+
logicalName: options.logical,
|
|
100
|
+
derivedName,
|
|
101
|
+
createdAt: existing?.type === "dispatch_namespace" ? existing.createdAt : ts,
|
|
102
|
+
updatedAt: ts
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
async function importDnsRecord(ctx) {
|
|
106
|
+
const { options, api, state, config, ts } = ctx;
|
|
107
|
+
if (!options.cfId) throw new Error("import dns_record: --cf-id <recordId> is required (the record id from `tamer drift` or the Cloudflare dashboard URL)");
|
|
108
|
+
const declared = (config.dnsRecords ?? []).find((r) => r.logicalName === options.logical);
|
|
109
|
+
if (!declared) throw new Error(`import dns_record: no dnsRecords entry with logicalName "${options.logical}" in the Tamer project config`);
|
|
110
|
+
const hit = (await api.zoneDnsRecordListAll(declared.zoneId)).find((r) => r.id === options.cfId);
|
|
111
|
+
if (!hit) throw new Error(`import dns_record: record id "${options.cfId}" not found in zone "${declared.zoneId}"`);
|
|
112
|
+
if (hit.type !== declared.type) throw new Error(`import dns_record: cf record type "${hit.type}" does not match declared type "${declared.type}"`);
|
|
113
|
+
const { dnsRecordStateKey } = await import("./dns-records.resolve-C2T0m4NG.mjs");
|
|
114
|
+
const key = dnsRecordStateKey(declared.zoneId, declared.type, declared.name);
|
|
115
|
+
const existing = state.get(key);
|
|
116
|
+
state.set(key, {
|
|
117
|
+
type: "dns_record",
|
|
118
|
+
logicalName: options.logical,
|
|
119
|
+
zoneId: declared.zoneId,
|
|
120
|
+
recordType: declared.type,
|
|
121
|
+
name: hit.name,
|
|
122
|
+
content: hit.content,
|
|
123
|
+
ttl: hit.ttl ?? 1,
|
|
124
|
+
proxied: hit.proxied ?? false,
|
|
125
|
+
priority: hit.priority,
|
|
126
|
+
comment: hit.comment ?? "",
|
|
127
|
+
recordId: hit.id,
|
|
128
|
+
createdAt: existing?.type === "dns_record" ? existing.createdAt : ts,
|
|
129
|
+
updatedAt: ts
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
async function importWorkerRoute(ctx) {
|
|
133
|
+
const { options, env, baseDir, accountId, api, naming, state, config, ts } = ctx;
|
|
134
|
+
if (!options.routeId) throw new Error("import worker_route: --route-id <id> is required");
|
|
135
|
+
const wc = (await getWorkers(config, baseDir)).find(([k]) => k === options.logical)?.[1];
|
|
136
|
+
if (!wc) throw new Error(`import worker_route: no worker key "${options.logical}" in the Tamer project config`);
|
|
137
|
+
const imports = await fetchStackImports(api, config, env);
|
|
138
|
+
const resolved = await resolveWorkerConfig(config, options.logical, wc, env, baseDir, accountId, naming, state, { imports });
|
|
139
|
+
if (resolved.apiManagedRoutes.length === 0) throw new Error(`import worker_route: worker "${options.logical}" declares no api-managed tamerRoutes for env ${env}`);
|
|
140
|
+
const route = options.zoneName ? resolved.apiManagedRoutes.find((r) => r.zone_name === options.zoneName) : resolved.apiManagedRoutes[0];
|
|
141
|
+
if (!route) throw new Error(`import worker_route: no route for zone "${options.zoneName}" in resolved config`);
|
|
142
|
+
const zoneId = await findZoneIdByName(api, route.zone_name);
|
|
143
|
+
if (!zoneId) throw new Error(`import worker_route: zone "${route.zone_name}" not found`);
|
|
144
|
+
const hit = (await api.zoneWorkerRoutesList(zoneId)).find((r) => r.id === options.routeId);
|
|
145
|
+
if (!hit) throw new Error(`import worker_route: route id "${options.routeId}" not found in zone "${route.zone_name}"`);
|
|
146
|
+
if (hit.pattern !== route.pattern) throw new Error(`import worker_route: cf pattern "${hit.pattern}" does not match resolved "${route.pattern}"`);
|
|
147
|
+
const key = workerRouteStateKey(zoneId, options.routeId);
|
|
148
|
+
const existing = state.get(key);
|
|
149
|
+
state.set(key, {
|
|
150
|
+
type: "worker_route",
|
|
151
|
+
workerKey: options.logical,
|
|
152
|
+
workerName: resolved.workerName,
|
|
153
|
+
zoneId,
|
|
154
|
+
zoneName: route.zone_name,
|
|
155
|
+
routeId: options.routeId,
|
|
156
|
+
pattern: route.pattern,
|
|
157
|
+
createdAt: existing?.type === "worker_route" ? existing.createdAt : ts,
|
|
158
|
+
updatedAt: ts
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
//#endregion
|
|
163
|
+
export { runImport };
|
|
164
|
+
//# sourceMappingURL=import-BrduwA9Z.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import-BrduwA9Z.mjs","names":[],"sources":["../src/cli/commands/import.ts"],"sourcesContent":["import { loadConfig, getWorkers } from \"../../core/config/loader.js\";\nimport { cloudflareAccountIdFromEnv } from \"../../core/cloudflareEnv.js\";\nimport { namingFromConfig } from \"../../core/config/namingFromConfig.js\";\nimport { CFApiClient } from \"../../core/api/CFApiClient.js\";\nimport { StateManager } from \"../../core/state/StateManager.js\";\nimport { stackNameForConfig } from \"../../core/state/stackName.js\";\nimport { resolveWorkerConfig } from \"../../core/config/resolver.js\";\nimport { fetchStackImports } from \"../../core/imports/fetchStackImports.js\";\nimport { workerRouteStateKey } from \"../../features/worker-route/worker-route.stateKey.js\";\nimport { findZoneIdByName } from \"../../core/routes/zoneResolver.js\";\nimport {\n getResourceModule,\n resourceModules,\n type ResourceKind,\n} from \"../../core/registry/registry.js\";\n\n/** All registered resource kinds plus the non-registry imports. */\nexport type ImportKind =\n | ResourceKind\n | \"dispatch_namespace\"\n | \"dns_record\"\n | \"worker_route\";\n\nexport interface ImportOptions {\n env: string;\n kind: ImportKind;\n /** Logical name from `tamer.config.ts` (or worker key, for `worker_route`). */\n logical: string;\n /** Cloudflare resource id (D1 uuid, KV id, R2 bucket name, or namespace name). */\n cfId?: string;\n /** Required for sharded D1 imports (YYYY-MM-DD). */\n shardDate?: string;\n /** R2 buckets only: creation date (YYYY-MM-DD) used in the binding key. */\n createdDate?: string;\n /** worker_route only: the route id from `/zones/{id}/workers/routes`. */\n routeId?: string;\n /** worker_route only: zone name from `tamerRoutes`. */\n zoneName?: string;\n configPath?: string;\n}\n\nconst REGISTRY_KINDS = new Set<string>(resourceModules.map((m) => m.kind));\n\n/**\n * `tamer import` — register an existing Cloudflare resource into Tamer state\n * by logical name (CloudFormation `import_resources` analogue). For all\n * resource-registry kinds the work is delegated to the module's\n * `importOne` hook; `dispatch_namespace` and `worker_route` remain inline\n * because they aren't registry resources today.\n */\nexport async function runImport(options: ImportOptions): Promise<void> {\n const env = options.env;\n if (env === \"local\") {\n throw new Error(\"import: --env local is not supported (no persisted state)\");\n }\n const baseDir = process.cwd();\n const config = await loadConfig(options.configPath, { env });\n const accountId = config.account_id ?? cloudflareAccountIdFromEnv();\n if (!accountId) {\n throw new Error(\n \"account_id required in config or CLOUDFLARE_ACCOUNT_ID env var\",\n );\n }\n const api = new CFApiClient(accountId);\n const naming = namingFromConfig(config);\n const state = new StateManager(\n config.tenant.id,\n env,\n stackNameForConfig(config),\n );\n await state.hydrate(api);\n state.beginOperation(\"import\", `${options.kind}:${options.logical}`);\n\n const ts = new Date().toISOString();\n\n try {\n if (REGISTRY_KINDS.has(options.kind)) {\n const mod = getResourceModule(options.kind as ResourceKind);\n if (!mod) throw new Error(`unknown import kind \"${options.kind}\"`);\n await mod.importOne({\n options: {\n env,\n kind: options.kind,\n logical: options.logical,\n cfId: options.cfId,\n shardDate: options.shardDate,\n createdDate: options.createdDate,\n routeId: options.routeId,\n zoneName: options.zoneName,\n },\n env,\n api,\n state,\n naming,\n ts,\n });\n } else if (options.kind === \"dispatch_namespace\") {\n await importDispatchNamespace({ options, env, api, state, ts });\n } else if (options.kind === \"dns_record\") {\n await importDnsRecord({ options, env, api, state, config, ts });\n } else if (options.kind === \"worker_route\") {\n await importWorkerRoute({\n options,\n env,\n baseDir,\n accountId,\n api,\n naming,\n state,\n config,\n ts,\n });\n } else {\n throw new Error(`unknown import kind \"${options.kind}\"`);\n }\n\n state.finishOperation(\"imported\");\n await state.persist(api);\n console.log(`Imported ${options.kind} \"${options.logical}\" into ${env} state.`);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n state.failOperation(msg);\n try {\n await state.persist(api);\n } catch {\n /* swallow secondary persist failure */\n }\n throw err;\n }\n}\n\nasync function importDispatchNamespace(ctx: {\n options: ImportOptions;\n env: string;\n api: CFApiClient;\n state: StateManager;\n ts: string;\n}): Promise<void> {\n const { options, api, state, ts } = ctx;\n const namespaces = await api.dispatchNamespaceListAll();\n const hit = namespaces.find((n) => n.namespace_name === options.cfId);\n if (!hit) {\n throw new Error(\n `import dispatch_namespace: namespace \"${options.cfId}\" not found`,\n );\n }\n const derivedName = hit.namespace_name;\n const key = `dispatch_ns:${derivedName}`;\n const existing = state.get(key);\n state.set(key, {\n type: \"dispatch_namespace\",\n logicalName: options.logical,\n derivedName,\n createdAt:\n existing?.type === \"dispatch_namespace\" ? existing.createdAt : ts,\n updatedAt: ts,\n });\n}\n\nasync function importDnsRecord(ctx: {\n options: ImportOptions;\n env: string;\n api: CFApiClient;\n state: StateManager;\n config: Awaited<ReturnType<typeof loadConfig>>;\n ts: string;\n}): Promise<void> {\n const { options, api, state, config, ts } = ctx;\n if (!options.cfId) {\n throw new Error(\n \"import dns_record: --cf-id <recordId> is required (the record id from `tamer drift` or the Cloudflare dashboard URL)\",\n );\n }\n const declared = (config.dnsRecords ?? []).find(\n (r) => r.logicalName === options.logical,\n );\n if (!declared) {\n throw new Error(\n `import dns_record: no dnsRecords entry with logicalName \"${options.logical}\" in the Tamer project config`,\n );\n }\n const list = await api.zoneDnsRecordListAll(declared.zoneId);\n const hit = list.find((r) => r.id === options.cfId);\n if (!hit) {\n throw new Error(\n `import dns_record: record id \"${options.cfId}\" not found in zone \"${declared.zoneId}\"`,\n );\n }\n if (hit.type !== declared.type) {\n throw new Error(\n `import dns_record: cf record type \"${hit.type}\" does not match declared type \"${declared.type}\"`,\n );\n }\n const { dnsRecordStateKey } = await import(\n \"../../features/dns-records/dns-records.resolve.js\"\n );\n const key = dnsRecordStateKey(declared.zoneId, declared.type, declared.name);\n const existing = state.get(key);\n state.set(key, {\n type: \"dns_record\",\n logicalName: options.logical,\n zoneId: declared.zoneId,\n recordType: declared.type,\n name: hit.name,\n content: hit.content,\n ttl: hit.ttl ?? 1,\n proxied: hit.proxied ?? false,\n priority: hit.priority,\n comment: hit.comment ?? \"\",\n recordId: hit.id,\n createdAt: existing?.type === \"dns_record\" ? existing.createdAt : ts,\n updatedAt: ts,\n });\n}\n\nasync function importWorkerRoute(ctx: {\n options: ImportOptions;\n env: string;\n baseDir: string;\n accountId: string;\n api: CFApiClient;\n naming: ReturnType<typeof namingFromConfig>;\n state: StateManager;\n config: Awaited<ReturnType<typeof loadConfig>>;\n ts: string;\n}): Promise<void> {\n const { options, env, baseDir, accountId, api, naming, state, config, ts } =\n ctx;\n if (!options.routeId) {\n throw new Error(\"import worker_route: --route-id <id> is required\");\n }\n const workers = await getWorkers(config, baseDir);\n const wc = workers.find(([k]) => k === options.logical)?.[1];\n if (!wc) {\n throw new Error(\n `import worker_route: no worker key \"${options.logical}\" in the Tamer project config`,\n );\n }\n const imports = await fetchStackImports(api, config, env);\n const resolved = await resolveWorkerConfig(\n config,\n options.logical,\n wc,\n env,\n baseDir,\n accountId,\n naming,\n state,\n { imports },\n );\n if (resolved.apiManagedRoutes.length === 0) {\n throw new Error(\n `import worker_route: worker \"${options.logical}\" declares no api-managed tamerRoutes for env ${env}`,\n );\n }\n\n const route = options.zoneName\n ? resolved.apiManagedRoutes.find((r) => r.zone_name === options.zoneName)\n : resolved.apiManagedRoutes[0];\n if (!route) {\n throw new Error(\n `import worker_route: no route for zone \"${options.zoneName}\" in resolved config`,\n );\n }\n const zoneId = await findZoneIdByName(api, route.zone_name);\n if (!zoneId) {\n throw new Error(`import worker_route: zone \"${route.zone_name}\" not found`);\n }\n const list = await api.zoneWorkerRoutesList(zoneId);\n const hit = list.find((r) => r.id === options.routeId);\n if (!hit) {\n throw new Error(\n `import worker_route: route id \"${options.routeId}\" not found in zone \"${route.zone_name}\"`,\n );\n }\n if (hit.pattern !== route.pattern) {\n throw new Error(\n `import worker_route: cf pattern \"${hit.pattern}\" does not match resolved \"${route.pattern}\"`,\n );\n }\n const key = workerRouteStateKey(zoneId, options.routeId);\n const existing = state.get(key);\n state.set(key, {\n type: \"worker_route\",\n workerKey: options.logical,\n workerName: resolved.workerName,\n zoneId,\n zoneName: route.zone_name,\n routeId: options.routeId,\n pattern: route.pattern,\n createdAt: existing?.type === \"worker_route\" ? existing.createdAt : ts,\n updatedAt: ts,\n });\n}\n"],"mappings":"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"}
|