@better-zap/cli 0.0.1
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/LICENSE +15 -0
- package/README.md +3 -0
- package/dist/cli.cjs +55 -0
- package/dist/cli.d.cts +5 -0
- package/dist/cli.d.mts +5 -0
- package/dist/cli.mjs +52 -0
- package/dist/index.cjs +9 -0
- package/dist/index.d.cts +75 -0
- package/dist/index.d.mts +75 -0
- package/dist/index.mjs +2 -0
- package/dist/template-generator-CQM-kLEi.cjs +307 -0
- package/dist/template-generator-DQI3p1ce.mjs +236 -0
- package/package.json +46 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Better Zap
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
10
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
11
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
12
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
13
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
14
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
15
|
+
PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
package/dist/cli.cjs
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const require_template_generator = require("./template-generator-CQM-kLEi.cjs");
|
|
4
|
+
let node_url = require("node:url");
|
|
5
|
+
let node_process = require("node:process");
|
|
6
|
+
node_process = require_template_generator.__toESM(node_process);
|
|
7
|
+
//#region src/cli.ts
|
|
8
|
+
function shouldPrintHelp(command, flags) {
|
|
9
|
+
return !command || command === "--help" || command === "-h" || Boolean(flags.help);
|
|
10
|
+
}
|
|
11
|
+
async function runCli(argv = node_process.default.argv.slice(2)) {
|
|
12
|
+
const { command, flags } = require_template_generator.parseCliArgs(argv);
|
|
13
|
+
const wantsHelp = shouldPrintHelp(command, flags);
|
|
14
|
+
if (!command || wantsHelp) {
|
|
15
|
+
printHelp();
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
if (command !== "generate") throw new Error(`[better-zap] Unknown command "${command}". Supported commands: generate.`);
|
|
19
|
+
const options = await require_template_generator.resolveGenerateOptions({
|
|
20
|
+
cwd: node_process.default.cwd(),
|
|
21
|
+
env: node_process.default.env,
|
|
22
|
+
flags
|
|
23
|
+
});
|
|
24
|
+
const result = await require_template_generator.runGenerateCommand(options);
|
|
25
|
+
if (options.check) {
|
|
26
|
+
console.log(`[better-zap] ${result.output} is up to date (${result.templateCount} templates).`);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
console.log(`[better-zap] Generated ${result.templateCount} templates at ${result.output}.`);
|
|
30
|
+
}
|
|
31
|
+
function printHelp() {
|
|
32
|
+
console.log(`better-zap
|
|
33
|
+
|
|
34
|
+
Usage:
|
|
35
|
+
better-zap generate [--access-token <token>] [--waba-id <id>] [--output <path>] [--api-version <version>] [--check]
|
|
36
|
+
|
|
37
|
+
Options:
|
|
38
|
+
--access-token Meta access token. Falls back to WHATSAPP_TOKEN or META_ACCESS_TOKEN.
|
|
39
|
+
--waba-id WhatsApp Business Account ID. Falls back to WHATSAPP_BUSINESS_ACCOUNT_ID.
|
|
40
|
+
--output Output path for the generated registry file.
|
|
41
|
+
--api-version Graph API version. Defaults to v25.0.
|
|
42
|
+
--config Optional config file path. Also auto-discovers better-zap.config.(mjs|js|json).
|
|
43
|
+
--check Validate that the generated file is already up to date.
|
|
44
|
+
--help Show this help message.
|
|
45
|
+
`);
|
|
46
|
+
}
|
|
47
|
+
const entrypoint = node_process.default.argv[1];
|
|
48
|
+
if (entrypoint && require("url").pathToFileURL(__filename).href === (0, node_url.pathToFileURL)(entrypoint).href) runCli().catch((error) => {
|
|
49
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
50
|
+
console.error(message);
|
|
51
|
+
node_process.default.exitCode = 1;
|
|
52
|
+
});
|
|
53
|
+
//#endregion
|
|
54
|
+
exports.runCli = runCli;
|
|
55
|
+
exports.shouldPrintHelp = shouldPrintHelp;
|
package/dist/cli.d.cts
ADDED
package/dist/cli.d.mts
ADDED
package/dist/cli.mjs
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { a as resolveGenerateOptions, i as parseCliArgs, o as runGenerateCommand } from "./template-generator-DQI3p1ce.mjs";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
import process from "node:process";
|
|
5
|
+
//#region src/cli.ts
|
|
6
|
+
function shouldPrintHelp(command, flags) {
|
|
7
|
+
return !command || command === "--help" || command === "-h" || Boolean(flags.help);
|
|
8
|
+
}
|
|
9
|
+
async function runCli(argv = process.argv.slice(2)) {
|
|
10
|
+
const { command, flags } = parseCliArgs(argv);
|
|
11
|
+
const wantsHelp = shouldPrintHelp(command, flags);
|
|
12
|
+
if (!command || wantsHelp) {
|
|
13
|
+
printHelp();
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
if (command !== "generate") throw new Error(`[better-zap] Unknown command "${command}". Supported commands: generate.`);
|
|
17
|
+
const options = await resolveGenerateOptions({
|
|
18
|
+
cwd: process.cwd(),
|
|
19
|
+
env: process.env,
|
|
20
|
+
flags
|
|
21
|
+
});
|
|
22
|
+
const result = await runGenerateCommand(options);
|
|
23
|
+
if (options.check) {
|
|
24
|
+
console.log(`[better-zap] ${result.output} is up to date (${result.templateCount} templates).`);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
console.log(`[better-zap] Generated ${result.templateCount} templates at ${result.output}.`);
|
|
28
|
+
}
|
|
29
|
+
function printHelp() {
|
|
30
|
+
console.log(`better-zap
|
|
31
|
+
|
|
32
|
+
Usage:
|
|
33
|
+
better-zap generate [--access-token <token>] [--waba-id <id>] [--output <path>] [--api-version <version>] [--check]
|
|
34
|
+
|
|
35
|
+
Options:
|
|
36
|
+
--access-token Meta access token. Falls back to WHATSAPP_TOKEN or META_ACCESS_TOKEN.
|
|
37
|
+
--waba-id WhatsApp Business Account ID. Falls back to WHATSAPP_BUSINESS_ACCOUNT_ID.
|
|
38
|
+
--output Output path for the generated registry file.
|
|
39
|
+
--api-version Graph API version. Defaults to v25.0.
|
|
40
|
+
--config Optional config file path. Also auto-discovers better-zap.config.(mjs|js|json).
|
|
41
|
+
--check Validate that the generated file is already up to date.
|
|
42
|
+
--help Show this help message.
|
|
43
|
+
`);
|
|
44
|
+
}
|
|
45
|
+
const entrypoint = process.argv[1];
|
|
46
|
+
if (entrypoint && import.meta.url === pathToFileURL(entrypoint).href) runCli().catch((error) => {
|
|
47
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
48
|
+
console.error(message);
|
|
49
|
+
process.exitCode = 1;
|
|
50
|
+
});
|
|
51
|
+
//#endregion
|
|
52
|
+
export { runCli, shouldPrintHelp };
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_template_generator = require("./template-generator-CQM-kLEi.cjs");
|
|
3
|
+
exports.fetchAllMetaTemplates = require_template_generator.fetchAllMetaTemplates;
|
|
4
|
+
exports.generateTemplateRegistryFileContent = require_template_generator.generateTemplateRegistryFileContent;
|
|
5
|
+
exports.normalizeMetaTemplates = require_template_generator.normalizeMetaTemplates;
|
|
6
|
+
exports.parseCliArgs = require_template_generator.parseCliArgs;
|
|
7
|
+
exports.resolveGenerateOptions = require_template_generator.resolveGenerateOptions;
|
|
8
|
+
exports.runGenerateCommand = require_template_generator.runGenerateCommand;
|
|
9
|
+
exports.writeGeneratedTemplateFile = require_template_generator.writeGeneratedTemplateFile;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { TemplateRegistry } from "better-zap";
|
|
2
|
+
|
|
3
|
+
//#region src/template-generator.d.ts
|
|
4
|
+
type CliFlagValue = boolean | string;
|
|
5
|
+
type CliFlags = Record<string, CliFlagValue>;
|
|
6
|
+
type GenerateOptions = {
|
|
7
|
+
accessToken?: string;
|
|
8
|
+
apiVersion?: string;
|
|
9
|
+
check?: boolean;
|
|
10
|
+
fetchImpl?: typeof fetch;
|
|
11
|
+
output: string;
|
|
12
|
+
wabaId?: string;
|
|
13
|
+
};
|
|
14
|
+
type ResolveGenerateOptionsInput = {
|
|
15
|
+
cwd: string;
|
|
16
|
+
env: NodeJS.ProcessEnv;
|
|
17
|
+
flags: CliFlags;
|
|
18
|
+
};
|
|
19
|
+
type MetaTemplateButton = {
|
|
20
|
+
type?: string;
|
|
21
|
+
phone_number?: string;
|
|
22
|
+
text?: string;
|
|
23
|
+
url?: string;
|
|
24
|
+
};
|
|
25
|
+
type MetaTemplateComponent = {
|
|
26
|
+
type?: string;
|
|
27
|
+
format?: string;
|
|
28
|
+
text?: string;
|
|
29
|
+
url?: string;
|
|
30
|
+
buttons?: MetaTemplateButton[];
|
|
31
|
+
};
|
|
32
|
+
type MetaTemplate = {
|
|
33
|
+
name?: string;
|
|
34
|
+
language?: string;
|
|
35
|
+
components?: MetaTemplateComponent[];
|
|
36
|
+
};
|
|
37
|
+
declare function parseCliArgs(argv: string[]): {
|
|
38
|
+
command: string | undefined;
|
|
39
|
+
flags: CliFlags;
|
|
40
|
+
};
|
|
41
|
+
declare function resolveGenerateOptions({
|
|
42
|
+
cwd,
|
|
43
|
+
env,
|
|
44
|
+
flags
|
|
45
|
+
}: ResolveGenerateOptionsInput): Promise<GenerateOptions>;
|
|
46
|
+
declare function fetchAllMetaTemplates({
|
|
47
|
+
accessToken,
|
|
48
|
+
apiVersion,
|
|
49
|
+
fetchImpl,
|
|
50
|
+
wabaId
|
|
51
|
+
}: Required<Pick<GenerateOptions, "accessToken" | "wabaId">> & Pick<GenerateOptions, "apiVersion" | "fetchImpl">): Promise<MetaTemplate[]>;
|
|
52
|
+
declare function normalizeMetaTemplates(metaTemplates: MetaTemplate[]): TemplateRegistry;
|
|
53
|
+
declare function generateTemplateRegistryFileContent(registry: TemplateRegistry): string;
|
|
54
|
+
declare function writeGeneratedTemplateFile({
|
|
55
|
+
check,
|
|
56
|
+
content,
|
|
57
|
+
output
|
|
58
|
+
}: {
|
|
59
|
+
check?: boolean;
|
|
60
|
+
content: string;
|
|
61
|
+
output: string;
|
|
62
|
+
}): Promise<void>;
|
|
63
|
+
declare function runGenerateCommand({
|
|
64
|
+
accessToken,
|
|
65
|
+
apiVersion,
|
|
66
|
+
check,
|
|
67
|
+
fetchImpl,
|
|
68
|
+
output,
|
|
69
|
+
wabaId
|
|
70
|
+
}: GenerateOptions): Promise<{
|
|
71
|
+
output: string;
|
|
72
|
+
templateCount: number;
|
|
73
|
+
}>;
|
|
74
|
+
//#endregion
|
|
75
|
+
export { fetchAllMetaTemplates, generateTemplateRegistryFileContent, normalizeMetaTemplates, parseCliArgs, resolveGenerateOptions, runGenerateCommand, writeGeneratedTemplateFile };
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { TemplateRegistry } from "better-zap";
|
|
2
|
+
|
|
3
|
+
//#region src/template-generator.d.ts
|
|
4
|
+
type CliFlagValue = boolean | string;
|
|
5
|
+
type CliFlags = Record<string, CliFlagValue>;
|
|
6
|
+
type GenerateOptions = {
|
|
7
|
+
accessToken?: string;
|
|
8
|
+
apiVersion?: string;
|
|
9
|
+
check?: boolean;
|
|
10
|
+
fetchImpl?: typeof fetch;
|
|
11
|
+
output: string;
|
|
12
|
+
wabaId?: string;
|
|
13
|
+
};
|
|
14
|
+
type ResolveGenerateOptionsInput = {
|
|
15
|
+
cwd: string;
|
|
16
|
+
env: NodeJS.ProcessEnv;
|
|
17
|
+
flags: CliFlags;
|
|
18
|
+
};
|
|
19
|
+
type MetaTemplateButton = {
|
|
20
|
+
type?: string;
|
|
21
|
+
phone_number?: string;
|
|
22
|
+
text?: string;
|
|
23
|
+
url?: string;
|
|
24
|
+
};
|
|
25
|
+
type MetaTemplateComponent = {
|
|
26
|
+
type?: string;
|
|
27
|
+
format?: string;
|
|
28
|
+
text?: string;
|
|
29
|
+
url?: string;
|
|
30
|
+
buttons?: MetaTemplateButton[];
|
|
31
|
+
};
|
|
32
|
+
type MetaTemplate = {
|
|
33
|
+
name?: string;
|
|
34
|
+
language?: string;
|
|
35
|
+
components?: MetaTemplateComponent[];
|
|
36
|
+
};
|
|
37
|
+
declare function parseCliArgs(argv: string[]): {
|
|
38
|
+
command: string | undefined;
|
|
39
|
+
flags: CliFlags;
|
|
40
|
+
};
|
|
41
|
+
declare function resolveGenerateOptions({
|
|
42
|
+
cwd,
|
|
43
|
+
env,
|
|
44
|
+
flags
|
|
45
|
+
}: ResolveGenerateOptionsInput): Promise<GenerateOptions>;
|
|
46
|
+
declare function fetchAllMetaTemplates({
|
|
47
|
+
accessToken,
|
|
48
|
+
apiVersion,
|
|
49
|
+
fetchImpl,
|
|
50
|
+
wabaId
|
|
51
|
+
}: Required<Pick<GenerateOptions, "accessToken" | "wabaId">> & Pick<GenerateOptions, "apiVersion" | "fetchImpl">): Promise<MetaTemplate[]>;
|
|
52
|
+
declare function normalizeMetaTemplates(metaTemplates: MetaTemplate[]): TemplateRegistry;
|
|
53
|
+
declare function generateTemplateRegistryFileContent(registry: TemplateRegistry): string;
|
|
54
|
+
declare function writeGeneratedTemplateFile({
|
|
55
|
+
check,
|
|
56
|
+
content,
|
|
57
|
+
output
|
|
58
|
+
}: {
|
|
59
|
+
check?: boolean;
|
|
60
|
+
content: string;
|
|
61
|
+
output: string;
|
|
62
|
+
}): Promise<void>;
|
|
63
|
+
declare function runGenerateCommand({
|
|
64
|
+
accessToken,
|
|
65
|
+
apiVersion,
|
|
66
|
+
check,
|
|
67
|
+
fetchImpl,
|
|
68
|
+
output,
|
|
69
|
+
wabaId
|
|
70
|
+
}: GenerateOptions): Promise<{
|
|
71
|
+
output: string;
|
|
72
|
+
templateCount: number;
|
|
73
|
+
}>;
|
|
74
|
+
//#endregion
|
|
75
|
+
export { fetchAllMetaTemplates, generateTemplateRegistryFileContent, normalizeMetaTemplates, parseCliArgs, resolveGenerateOptions, runGenerateCommand, writeGeneratedTemplateFile };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as resolveGenerateOptions, i as parseCliArgs, n as generateTemplateRegistryFileContent, o as runGenerateCommand, r as normalizeMetaTemplates, s as writeGeneratedTemplateFile, t as fetchAllMetaTemplates } from "./template-generator-DQI3p1ce.mjs";
|
|
2
|
+
export { fetchAllMetaTemplates, generateTemplateRegistryFileContent, normalizeMetaTemplates, parseCliArgs, resolveGenerateOptions, runGenerateCommand, writeGeneratedTemplateFile };
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
//#region \0rolldown/runtime.js
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
10
|
+
key = keys[i];
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
12
|
+
get: ((k) => from[k]).bind(null, key),
|
|
13
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
19
|
+
value: mod,
|
|
20
|
+
enumerable: true
|
|
21
|
+
}) : target, mod));
|
|
22
|
+
//#endregion
|
|
23
|
+
let node_fs_promises = require("node:fs/promises");
|
|
24
|
+
node_fs_promises = __toESM(node_fs_promises);
|
|
25
|
+
let node_path = require("node:path");
|
|
26
|
+
node_path = __toESM(node_path);
|
|
27
|
+
let node_url = require("node:url");
|
|
28
|
+
//#region src/template-generator.ts
|
|
29
|
+
const DEFAULT_API_VERSION = "v25.0";
|
|
30
|
+
const DEFAULT_CONFIG_FILES = [
|
|
31
|
+
"better-zap.config.mjs",
|
|
32
|
+
"better-zap.config.js",
|
|
33
|
+
"better-zap.config.json"
|
|
34
|
+
];
|
|
35
|
+
function parseCliArgs(argv) {
|
|
36
|
+
const args = [...argv];
|
|
37
|
+
const command = args.shift();
|
|
38
|
+
const flags = {};
|
|
39
|
+
while (args.length > 0) {
|
|
40
|
+
const current = args.shift();
|
|
41
|
+
if (!current?.startsWith("--")) continue;
|
|
42
|
+
const flagName = current.slice(2);
|
|
43
|
+
const next = args[0];
|
|
44
|
+
if (!next || next.startsWith("--")) {
|
|
45
|
+
flags[flagName] = true;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
const value = args.shift();
|
|
49
|
+
if (value) flags[flagName] = value;
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
command,
|
|
53
|
+
flags
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
async function resolveGenerateOptions({ cwd, env, flags }) {
|
|
57
|
+
const configPath = typeof flags.config === "string" ? node_path.default.resolve(cwd, flags.config) : await findDefaultConfigPath(cwd);
|
|
58
|
+
const fileConfig = configPath ? await loadConfigFile(configPath) : {};
|
|
59
|
+
const templatesConfig = fileConfig.templates ?? fileConfig.whatsappTemplates ?? fileConfig;
|
|
60
|
+
const outputValue = firstDefined(asString(flags.output), templatesConfig.output, env.BETTER_ZAP_TEMPLATE_OUTPUT) ?? "";
|
|
61
|
+
const output = outputValue ? node_path.default.resolve(cwd, outputValue) : "";
|
|
62
|
+
return {
|
|
63
|
+
accessToken: firstDefined(asString(flags["access-token"]), templatesConfig.accessToken, env.WHATSAPP_TOKEN, env.META_ACCESS_TOKEN),
|
|
64
|
+
wabaId: firstDefined(asString(flags["waba-id"]), templatesConfig.wabaId, env.WHATSAPP_BUSINESS_ACCOUNT_ID),
|
|
65
|
+
apiVersion: firstDefined(asString(flags["api-version"]), templatesConfig.apiVersion, DEFAULT_API_VERSION),
|
|
66
|
+
output,
|
|
67
|
+
check: Boolean(flags.check)
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
async function fetchAllMetaTemplates({ accessToken, apiVersion, fetchImpl = fetch, wabaId }) {
|
|
71
|
+
let nextUrl = `https://graph.facebook.com/${apiVersion ?? DEFAULT_API_VERSION}/${wabaId}/message_templates?limit=100`;
|
|
72
|
+
const templates = [];
|
|
73
|
+
while (nextUrl) {
|
|
74
|
+
const response = await fetchImpl(nextUrl, { headers: { Authorization: `Bearer ${accessToken}` } });
|
|
75
|
+
if (!response.ok) {
|
|
76
|
+
const errorPayload = await response.json().catch(() => ({ error: { message: `HTTP ${response.status}` } }));
|
|
77
|
+
throw new Error(`[better-zap generate] Meta request failed: ${errorPayload.error?.message ?? `HTTP ${response.status}`}`);
|
|
78
|
+
}
|
|
79
|
+
const payload = await response.json();
|
|
80
|
+
templates.push(...payload.data ?? []);
|
|
81
|
+
nextUrl = payload.paging?.next ?? null;
|
|
82
|
+
}
|
|
83
|
+
return templates;
|
|
84
|
+
}
|
|
85
|
+
function normalizeMetaTemplates(metaTemplates) {
|
|
86
|
+
const normalizedEntries = metaTemplates.map((template) => [template.name, normalizeMetaTemplate(template)]).sort(([left], [right]) => (left ?? "").localeCompare(right ?? ""));
|
|
87
|
+
return Object.fromEntries(normalizedEntries);
|
|
88
|
+
}
|
|
89
|
+
function generateTemplateRegistryFileContent(registry) {
|
|
90
|
+
return `/* eslint-disable */
|
|
91
|
+
// Generated by better-zap generate. Edit the source templates in Meta and re-run the generator.
|
|
92
|
+
|
|
93
|
+
import { defineTemplates } from "better-zap";
|
|
94
|
+
|
|
95
|
+
export const whatsappTemplates = defineTemplates(${JSON.stringify(registry, null, 2)} as const);
|
|
96
|
+
|
|
97
|
+
export type AppWhatsAppTemplates = typeof whatsappTemplates;
|
|
98
|
+
`;
|
|
99
|
+
}
|
|
100
|
+
async function writeGeneratedTemplateFile({ check = false, content, output }) {
|
|
101
|
+
let currentContent = "";
|
|
102
|
+
try {
|
|
103
|
+
currentContent = await node_fs_promises.default.readFile(output, "utf8");
|
|
104
|
+
} catch (error) {
|
|
105
|
+
if (!(error instanceof Error) || !error.code || error.code !== "ENOENT") throw error;
|
|
106
|
+
}
|
|
107
|
+
if (check) {
|
|
108
|
+
if (currentContent !== content) throw new Error(`[better-zap generate] ${output} is out of date. Re-run the generator.`);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
await node_fs_promises.default.mkdir(node_path.default.dirname(output), { recursive: true });
|
|
112
|
+
await node_fs_promises.default.writeFile(output, content, "utf8");
|
|
113
|
+
}
|
|
114
|
+
async function runGenerateCommand({ accessToken, apiVersion = DEFAULT_API_VERSION, check = false, fetchImpl = fetch, output, wabaId }) {
|
|
115
|
+
if (!accessToken || !wabaId || !output) throw new Error("[better-zap generate] Missing required options. Expected access token, WABA ID, and output path.");
|
|
116
|
+
const registry = normalizeMetaTemplates(await fetchAllMetaTemplates({
|
|
117
|
+
accessToken,
|
|
118
|
+
apiVersion,
|
|
119
|
+
fetchImpl,
|
|
120
|
+
wabaId
|
|
121
|
+
}));
|
|
122
|
+
await writeGeneratedTemplateFile({
|
|
123
|
+
check,
|
|
124
|
+
content: generateTemplateRegistryFileContent(registry),
|
|
125
|
+
output
|
|
126
|
+
});
|
|
127
|
+
return {
|
|
128
|
+
output,
|
|
129
|
+
templateCount: Object.keys(registry).length
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
async function findDefaultConfigPath(cwd) {
|
|
133
|
+
for (const configFile of DEFAULT_CONFIG_FILES) {
|
|
134
|
+
const absolutePath = node_path.default.resolve(cwd, configFile);
|
|
135
|
+
try {
|
|
136
|
+
await node_fs_promises.default.access(absolutePath);
|
|
137
|
+
return absolutePath;
|
|
138
|
+
} catch {}
|
|
139
|
+
}
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
async function loadConfigFile(configPath) {
|
|
143
|
+
if (configPath.endsWith(".json")) {
|
|
144
|
+
const rawConfig = await node_fs_promises.default.readFile(configPath, "utf8");
|
|
145
|
+
return JSON.parse(rawConfig);
|
|
146
|
+
}
|
|
147
|
+
const module = await import((0, node_url.pathToFileURL)(configPath).href);
|
|
148
|
+
return module.default ?? module;
|
|
149
|
+
}
|
|
150
|
+
function normalizeMetaTemplate(template) {
|
|
151
|
+
if (!template.name || !template.language) throw new Error("[better-zap generate] Template entry is missing name or language.");
|
|
152
|
+
const normalizedComponents = [];
|
|
153
|
+
for (const component of template.components ?? []) {
|
|
154
|
+
const type = toUpper(component.type);
|
|
155
|
+
if (type === "BODY") {
|
|
156
|
+
normalizedComponents.push(normalizeBodyComponent(component));
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
if (type === "HEADER") {
|
|
160
|
+
normalizedComponents.push(normalizeHeaderComponent(component));
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
if (type === "BUTTONS") {
|
|
164
|
+
normalizedComponents.push(...normalizeButtonsComponent(component));
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
if (type === "FOOTER") continue;
|
|
168
|
+
throw new Error(`[better-zap generate] Unsupported template component type "${component.type}" in "${template.name}".`);
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
language: template.language,
|
|
172
|
+
components: normalizedComponents
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function normalizeBodyComponent(component) {
|
|
176
|
+
return {
|
|
177
|
+
type: "body",
|
|
178
|
+
parameters: extractPlaceholderTokens(component.text).map((token, index) => ({
|
|
179
|
+
name: createTextParameterName("body", token, index + 1),
|
|
180
|
+
type: "text"
|
|
181
|
+
}))
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
function normalizeHeaderComponent(component) {
|
|
185
|
+
const format = toUpper(component.format ?? "TEXT");
|
|
186
|
+
if (format === "LOCATION") return {
|
|
187
|
+
type: "header",
|
|
188
|
+
parameters: [{
|
|
189
|
+
name: "header_location",
|
|
190
|
+
type: "location"
|
|
191
|
+
}]
|
|
192
|
+
};
|
|
193
|
+
if (format === "TEXT") return {
|
|
194
|
+
type: "header",
|
|
195
|
+
parameters: extractPlaceholderTokens(component.text).map((token, index) => ({
|
|
196
|
+
name: createTextParameterName("header", token, index + 1),
|
|
197
|
+
type: "text"
|
|
198
|
+
}))
|
|
199
|
+
};
|
|
200
|
+
if (format === "IMAGE" || format === "VIDEO" || format === "DOCUMENT") return {
|
|
201
|
+
type: "header",
|
|
202
|
+
parameters: [{
|
|
203
|
+
name: `header_${format.toLowerCase()}`,
|
|
204
|
+
type: format.toLowerCase()
|
|
205
|
+
}]
|
|
206
|
+
};
|
|
207
|
+
throw new Error(`[better-zap generate] Unsupported header format "${component.format}".`);
|
|
208
|
+
}
|
|
209
|
+
function normalizeButtonsComponent(component) {
|
|
210
|
+
return (component.buttons ?? []).map((button, index) => normalizeButton(button, index));
|
|
211
|
+
}
|
|
212
|
+
function normalizeButton(button, index) {
|
|
213
|
+
const type = toUpper(button.type);
|
|
214
|
+
if (type === "QUICK_REPLY") return {
|
|
215
|
+
type: "button",
|
|
216
|
+
subType: "quick_reply",
|
|
217
|
+
index: String(index),
|
|
218
|
+
parameters: [{
|
|
219
|
+
name: `button_${index}_payload`,
|
|
220
|
+
type: "payload"
|
|
221
|
+
}]
|
|
222
|
+
};
|
|
223
|
+
if (type === "URL") {
|
|
224
|
+
const tokens = extractPlaceholderTokens(button.url);
|
|
225
|
+
return {
|
|
226
|
+
type: "button",
|
|
227
|
+
subType: "url",
|
|
228
|
+
index: String(index),
|
|
229
|
+
parameters: tokens.map((token, tokenIndex) => ({
|
|
230
|
+
name: createTextParameterName(`button_${index}_text`, token, tokenIndex + 1),
|
|
231
|
+
type: "text"
|
|
232
|
+
}))
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
throw new Error(`[better-zap generate] Unsupported button type "${button.type}" at index ${index}.`);
|
|
236
|
+
}
|
|
237
|
+
function extractPlaceholderTokens(value) {
|
|
238
|
+
if (typeof value !== "string" || value.length === 0) return [];
|
|
239
|
+
const tokens = [];
|
|
240
|
+
for (const match of value.matchAll(/\{\{\s*([A-Za-z0-9_]+)\s*\}\}/g)) tokens.push(match[1]);
|
|
241
|
+
return tokens;
|
|
242
|
+
}
|
|
243
|
+
function createTextParameterName(prefix, token, position) {
|
|
244
|
+
if (token && !/^\d+$/.test(token)) return `${prefix}_${sanitizeName(token)}`;
|
|
245
|
+
return `${prefix}_${position}`;
|
|
246
|
+
}
|
|
247
|
+
function sanitizeName(value) {
|
|
248
|
+
return value.toLowerCase().replace(/[^a-z0-9_]+/g, "_");
|
|
249
|
+
}
|
|
250
|
+
function toUpper(value) {
|
|
251
|
+
return typeof value === "string" ? value.toUpperCase() : "";
|
|
252
|
+
}
|
|
253
|
+
function firstDefined(...values) {
|
|
254
|
+
return values.find((value) => typeof value === "string" && value.length > 0);
|
|
255
|
+
}
|
|
256
|
+
function asString(value) {
|
|
257
|
+
return typeof value === "string" ? value : void 0;
|
|
258
|
+
}
|
|
259
|
+
//#endregion
|
|
260
|
+
Object.defineProperty(exports, "__toESM", {
|
|
261
|
+
enumerable: true,
|
|
262
|
+
get: function() {
|
|
263
|
+
return __toESM;
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
Object.defineProperty(exports, "fetchAllMetaTemplates", {
|
|
267
|
+
enumerable: true,
|
|
268
|
+
get: function() {
|
|
269
|
+
return fetchAllMetaTemplates;
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
Object.defineProperty(exports, "generateTemplateRegistryFileContent", {
|
|
273
|
+
enumerable: true,
|
|
274
|
+
get: function() {
|
|
275
|
+
return generateTemplateRegistryFileContent;
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
Object.defineProperty(exports, "normalizeMetaTemplates", {
|
|
279
|
+
enumerable: true,
|
|
280
|
+
get: function() {
|
|
281
|
+
return normalizeMetaTemplates;
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
Object.defineProperty(exports, "parseCliArgs", {
|
|
285
|
+
enumerable: true,
|
|
286
|
+
get: function() {
|
|
287
|
+
return parseCliArgs;
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
Object.defineProperty(exports, "resolveGenerateOptions", {
|
|
291
|
+
enumerable: true,
|
|
292
|
+
get: function() {
|
|
293
|
+
return resolveGenerateOptions;
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
Object.defineProperty(exports, "runGenerateCommand", {
|
|
297
|
+
enumerable: true,
|
|
298
|
+
get: function() {
|
|
299
|
+
return runGenerateCommand;
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
Object.defineProperty(exports, "writeGeneratedTemplateFile", {
|
|
303
|
+
enumerable: true,
|
|
304
|
+
get: function() {
|
|
305
|
+
return writeGeneratedTemplateFile;
|
|
306
|
+
}
|
|
307
|
+
});
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
//#region src/template-generator.ts
|
|
5
|
+
const DEFAULT_API_VERSION = "v25.0";
|
|
6
|
+
const DEFAULT_CONFIG_FILES = [
|
|
7
|
+
"better-zap.config.mjs",
|
|
8
|
+
"better-zap.config.js",
|
|
9
|
+
"better-zap.config.json"
|
|
10
|
+
];
|
|
11
|
+
function parseCliArgs(argv) {
|
|
12
|
+
const args = [...argv];
|
|
13
|
+
const command = args.shift();
|
|
14
|
+
const flags = {};
|
|
15
|
+
while (args.length > 0) {
|
|
16
|
+
const current = args.shift();
|
|
17
|
+
if (!current?.startsWith("--")) continue;
|
|
18
|
+
const flagName = current.slice(2);
|
|
19
|
+
const next = args[0];
|
|
20
|
+
if (!next || next.startsWith("--")) {
|
|
21
|
+
flags[flagName] = true;
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
const value = args.shift();
|
|
25
|
+
if (value) flags[flagName] = value;
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
command,
|
|
29
|
+
flags
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
async function resolveGenerateOptions({ cwd, env, flags }) {
|
|
33
|
+
const configPath = typeof flags.config === "string" ? path.resolve(cwd, flags.config) : await findDefaultConfigPath(cwd);
|
|
34
|
+
const fileConfig = configPath ? await loadConfigFile(configPath) : {};
|
|
35
|
+
const templatesConfig = fileConfig.templates ?? fileConfig.whatsappTemplates ?? fileConfig;
|
|
36
|
+
const outputValue = firstDefined(asString(flags.output), templatesConfig.output, env.BETTER_ZAP_TEMPLATE_OUTPUT) ?? "";
|
|
37
|
+
const output = outputValue ? path.resolve(cwd, outputValue) : "";
|
|
38
|
+
return {
|
|
39
|
+
accessToken: firstDefined(asString(flags["access-token"]), templatesConfig.accessToken, env.WHATSAPP_TOKEN, env.META_ACCESS_TOKEN),
|
|
40
|
+
wabaId: firstDefined(asString(flags["waba-id"]), templatesConfig.wabaId, env.WHATSAPP_BUSINESS_ACCOUNT_ID),
|
|
41
|
+
apiVersion: firstDefined(asString(flags["api-version"]), templatesConfig.apiVersion, DEFAULT_API_VERSION),
|
|
42
|
+
output,
|
|
43
|
+
check: Boolean(flags.check)
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
async function fetchAllMetaTemplates({ accessToken, apiVersion, fetchImpl = fetch, wabaId }) {
|
|
47
|
+
let nextUrl = `https://graph.facebook.com/${apiVersion ?? DEFAULT_API_VERSION}/${wabaId}/message_templates?limit=100`;
|
|
48
|
+
const templates = [];
|
|
49
|
+
while (nextUrl) {
|
|
50
|
+
const response = await fetchImpl(nextUrl, { headers: { Authorization: `Bearer ${accessToken}` } });
|
|
51
|
+
if (!response.ok) {
|
|
52
|
+
const errorPayload = await response.json().catch(() => ({ error: { message: `HTTP ${response.status}` } }));
|
|
53
|
+
throw new Error(`[better-zap generate] Meta request failed: ${errorPayload.error?.message ?? `HTTP ${response.status}`}`);
|
|
54
|
+
}
|
|
55
|
+
const payload = await response.json();
|
|
56
|
+
templates.push(...payload.data ?? []);
|
|
57
|
+
nextUrl = payload.paging?.next ?? null;
|
|
58
|
+
}
|
|
59
|
+
return templates;
|
|
60
|
+
}
|
|
61
|
+
function normalizeMetaTemplates(metaTemplates) {
|
|
62
|
+
const normalizedEntries = metaTemplates.map((template) => [template.name, normalizeMetaTemplate(template)]).sort(([left], [right]) => (left ?? "").localeCompare(right ?? ""));
|
|
63
|
+
return Object.fromEntries(normalizedEntries);
|
|
64
|
+
}
|
|
65
|
+
function generateTemplateRegistryFileContent(registry) {
|
|
66
|
+
return `/* eslint-disable */
|
|
67
|
+
// Generated by better-zap generate. Edit the source templates in Meta and re-run the generator.
|
|
68
|
+
|
|
69
|
+
import { defineTemplates } from "better-zap";
|
|
70
|
+
|
|
71
|
+
export const whatsappTemplates = defineTemplates(${JSON.stringify(registry, null, 2)} as const);
|
|
72
|
+
|
|
73
|
+
export type AppWhatsAppTemplates = typeof whatsappTemplates;
|
|
74
|
+
`;
|
|
75
|
+
}
|
|
76
|
+
async function writeGeneratedTemplateFile({ check = false, content, output }) {
|
|
77
|
+
let currentContent = "";
|
|
78
|
+
try {
|
|
79
|
+
currentContent = await fs.readFile(output, "utf8");
|
|
80
|
+
} catch (error) {
|
|
81
|
+
if (!(error instanceof Error) || !error.code || error.code !== "ENOENT") throw error;
|
|
82
|
+
}
|
|
83
|
+
if (check) {
|
|
84
|
+
if (currentContent !== content) throw new Error(`[better-zap generate] ${output} is out of date. Re-run the generator.`);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
await fs.mkdir(path.dirname(output), { recursive: true });
|
|
88
|
+
await fs.writeFile(output, content, "utf8");
|
|
89
|
+
}
|
|
90
|
+
async function runGenerateCommand({ accessToken, apiVersion = DEFAULT_API_VERSION, check = false, fetchImpl = fetch, output, wabaId }) {
|
|
91
|
+
if (!accessToken || !wabaId || !output) throw new Error("[better-zap generate] Missing required options. Expected access token, WABA ID, and output path.");
|
|
92
|
+
const registry = normalizeMetaTemplates(await fetchAllMetaTemplates({
|
|
93
|
+
accessToken,
|
|
94
|
+
apiVersion,
|
|
95
|
+
fetchImpl,
|
|
96
|
+
wabaId
|
|
97
|
+
}));
|
|
98
|
+
await writeGeneratedTemplateFile({
|
|
99
|
+
check,
|
|
100
|
+
content: generateTemplateRegistryFileContent(registry),
|
|
101
|
+
output
|
|
102
|
+
});
|
|
103
|
+
return {
|
|
104
|
+
output,
|
|
105
|
+
templateCount: Object.keys(registry).length
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
async function findDefaultConfigPath(cwd) {
|
|
109
|
+
for (const configFile of DEFAULT_CONFIG_FILES) {
|
|
110
|
+
const absolutePath = path.resolve(cwd, configFile);
|
|
111
|
+
try {
|
|
112
|
+
await fs.access(absolutePath);
|
|
113
|
+
return absolutePath;
|
|
114
|
+
} catch {}
|
|
115
|
+
}
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
async function loadConfigFile(configPath) {
|
|
119
|
+
if (configPath.endsWith(".json")) {
|
|
120
|
+
const rawConfig = await fs.readFile(configPath, "utf8");
|
|
121
|
+
return JSON.parse(rawConfig);
|
|
122
|
+
}
|
|
123
|
+
const module = await import(pathToFileURL(configPath).href);
|
|
124
|
+
return module.default ?? module;
|
|
125
|
+
}
|
|
126
|
+
function normalizeMetaTemplate(template) {
|
|
127
|
+
if (!template.name || !template.language) throw new Error("[better-zap generate] Template entry is missing name or language.");
|
|
128
|
+
const normalizedComponents = [];
|
|
129
|
+
for (const component of template.components ?? []) {
|
|
130
|
+
const type = toUpper(component.type);
|
|
131
|
+
if (type === "BODY") {
|
|
132
|
+
normalizedComponents.push(normalizeBodyComponent(component));
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (type === "HEADER") {
|
|
136
|
+
normalizedComponents.push(normalizeHeaderComponent(component));
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
if (type === "BUTTONS") {
|
|
140
|
+
normalizedComponents.push(...normalizeButtonsComponent(component));
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (type === "FOOTER") continue;
|
|
144
|
+
throw new Error(`[better-zap generate] Unsupported template component type "${component.type}" in "${template.name}".`);
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
language: template.language,
|
|
148
|
+
components: normalizedComponents
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
function normalizeBodyComponent(component) {
|
|
152
|
+
return {
|
|
153
|
+
type: "body",
|
|
154
|
+
parameters: extractPlaceholderTokens(component.text).map((token, index) => ({
|
|
155
|
+
name: createTextParameterName("body", token, index + 1),
|
|
156
|
+
type: "text"
|
|
157
|
+
}))
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function normalizeHeaderComponent(component) {
|
|
161
|
+
const format = toUpper(component.format ?? "TEXT");
|
|
162
|
+
if (format === "LOCATION") return {
|
|
163
|
+
type: "header",
|
|
164
|
+
parameters: [{
|
|
165
|
+
name: "header_location",
|
|
166
|
+
type: "location"
|
|
167
|
+
}]
|
|
168
|
+
};
|
|
169
|
+
if (format === "TEXT") return {
|
|
170
|
+
type: "header",
|
|
171
|
+
parameters: extractPlaceholderTokens(component.text).map((token, index) => ({
|
|
172
|
+
name: createTextParameterName("header", token, index + 1),
|
|
173
|
+
type: "text"
|
|
174
|
+
}))
|
|
175
|
+
};
|
|
176
|
+
if (format === "IMAGE" || format === "VIDEO" || format === "DOCUMENT") return {
|
|
177
|
+
type: "header",
|
|
178
|
+
parameters: [{
|
|
179
|
+
name: `header_${format.toLowerCase()}`,
|
|
180
|
+
type: format.toLowerCase()
|
|
181
|
+
}]
|
|
182
|
+
};
|
|
183
|
+
throw new Error(`[better-zap generate] Unsupported header format "${component.format}".`);
|
|
184
|
+
}
|
|
185
|
+
function normalizeButtonsComponent(component) {
|
|
186
|
+
return (component.buttons ?? []).map((button, index) => normalizeButton(button, index));
|
|
187
|
+
}
|
|
188
|
+
function normalizeButton(button, index) {
|
|
189
|
+
const type = toUpper(button.type);
|
|
190
|
+
if (type === "QUICK_REPLY") return {
|
|
191
|
+
type: "button",
|
|
192
|
+
subType: "quick_reply",
|
|
193
|
+
index: String(index),
|
|
194
|
+
parameters: [{
|
|
195
|
+
name: `button_${index}_payload`,
|
|
196
|
+
type: "payload"
|
|
197
|
+
}]
|
|
198
|
+
};
|
|
199
|
+
if (type === "URL") {
|
|
200
|
+
const tokens = extractPlaceholderTokens(button.url);
|
|
201
|
+
return {
|
|
202
|
+
type: "button",
|
|
203
|
+
subType: "url",
|
|
204
|
+
index: String(index),
|
|
205
|
+
parameters: tokens.map((token, tokenIndex) => ({
|
|
206
|
+
name: createTextParameterName(`button_${index}_text`, token, tokenIndex + 1),
|
|
207
|
+
type: "text"
|
|
208
|
+
}))
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
throw new Error(`[better-zap generate] Unsupported button type "${button.type}" at index ${index}.`);
|
|
212
|
+
}
|
|
213
|
+
function extractPlaceholderTokens(value) {
|
|
214
|
+
if (typeof value !== "string" || value.length === 0) return [];
|
|
215
|
+
const tokens = [];
|
|
216
|
+
for (const match of value.matchAll(/\{\{\s*([A-Za-z0-9_]+)\s*\}\}/g)) tokens.push(match[1]);
|
|
217
|
+
return tokens;
|
|
218
|
+
}
|
|
219
|
+
function createTextParameterName(prefix, token, position) {
|
|
220
|
+
if (token && !/^\d+$/.test(token)) return `${prefix}_${sanitizeName(token)}`;
|
|
221
|
+
return `${prefix}_${position}`;
|
|
222
|
+
}
|
|
223
|
+
function sanitizeName(value) {
|
|
224
|
+
return value.toLowerCase().replace(/[^a-z0-9_]+/g, "_");
|
|
225
|
+
}
|
|
226
|
+
function toUpper(value) {
|
|
227
|
+
return typeof value === "string" ? value.toUpperCase() : "";
|
|
228
|
+
}
|
|
229
|
+
function firstDefined(...values) {
|
|
230
|
+
return values.find((value) => typeof value === "string" && value.length > 0);
|
|
231
|
+
}
|
|
232
|
+
function asString(value) {
|
|
233
|
+
return typeof value === "string" ? value : void 0;
|
|
234
|
+
}
|
|
235
|
+
//#endregion
|
|
236
|
+
export { resolveGenerateOptions as a, parseCliArgs as i, generateTemplateRegistryFileContent as n, runGenerateCommand as o, normalizeMetaTemplates as r, writeGeneratedTemplateFile as s, fetchAllMetaTemplates as t };
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@better-zap/cli",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "CLI tools for Better Zap.",
|
|
5
|
+
"license": "ISC",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.cjs",
|
|
8
|
+
"module": "./dist/index.mjs",
|
|
9
|
+
"types": "./dist/index.d.mts",
|
|
10
|
+
"bin": {
|
|
11
|
+
"better-zap": "./dist/cli.mjs"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"README.md",
|
|
16
|
+
"LICENSE"
|
|
17
|
+
],
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./dist/index.d.mts",
|
|
21
|
+
"import": "./dist/index.mjs",
|
|
22
|
+
"require": "./dist/index.cjs"
|
|
23
|
+
},
|
|
24
|
+
"./package.json": "./package.json"
|
|
25
|
+
},
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=20"
|
|
28
|
+
},
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"better-zap": "0.0.1"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/node": "^25.4.0",
|
|
37
|
+
"tsdown": "^0.21.2",
|
|
38
|
+
"typescript": "^5.9.3",
|
|
39
|
+
"vitest": "^4.1.0"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "tsdown",
|
|
43
|
+
"typecheck": "tsc --noEmit",
|
|
44
|
+
"test": "vitest run"
|
|
45
|
+
}
|
|
46
|
+
}
|