third-audience-mdx 1.0.7 → 1.0.8
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/dashboard/admin-store.d.mts +28 -0
- package/dist/dashboard/admin-store.d.ts +28 -0
- package/dist/dashboard/admin-store.js +191 -0
- package/dist/dashboard/admin-store.js.map +1 -0
- package/dist/dashboard/admin-store.mjs +142 -0
- package/dist/dashboard/admin-store.mjs.map +1 -0
- package/dist/dashboard/ui/components/Card.d.mts +11 -0
- package/dist/dashboard/ui/components/Card.d.ts +11 -0
- package/dist/dashboard/ui/components/Card.js +40 -0
- package/dist/dashboard/ui/components/Card.js.map +1 -0
- package/dist/dashboard/ui/components/Card.mjs +15 -0
- package/dist/dashboard/ui/components/Card.mjs.map +1 -0
- package/dist/dashboard/ui/components/HeroCard.d.mts +13 -0
- package/dist/dashboard/ui/components/HeroCard.d.ts +13 -0
- package/dist/dashboard/ui/components/HeroCard.js +41 -0
- package/dist/dashboard/ui/components/HeroCard.js.map +1 -0
- package/dist/dashboard/ui/components/HeroCard.mjs +16 -0
- package/dist/dashboard/ui/components/HeroCard.mjs.map +1 -0
- package/dist/dashboard/ui/components/VisitsChart.d.mts +13 -0
- package/dist/dashboard/ui/components/VisitsChart.d.ts +13 -0
- package/dist/dashboard/ui/components/VisitsChart.js +82 -0
- package/dist/dashboard/ui/components/VisitsChart.js.map +1 -0
- package/dist/dashboard/ui/components/VisitsChart.mjs +58 -0
- package/dist/dashboard/ui/components/VisitsChart.mjs.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
interface AdminRecord {
|
|
2
|
+
passwordHash: string;
|
|
3
|
+
isDefaultPassword: boolean;
|
|
4
|
+
createdAt: string;
|
|
5
|
+
lastLoginAt: string | null;
|
|
6
|
+
apiKey?: string;
|
|
7
|
+
}
|
|
8
|
+
declare function generateDefaultPassword(): string;
|
|
9
|
+
declare function hashPassword(password: string): string;
|
|
10
|
+
declare function loadAdmin(): AdminRecord | null;
|
|
11
|
+
declare function saveAdmin(record: AdminRecord): void;
|
|
12
|
+
declare const DEFAULT_PASSWORD = "Chang3M3Now!";
|
|
13
|
+
declare function initAdmin(): {
|
|
14
|
+
password: string;
|
|
15
|
+
apiKey: string;
|
|
16
|
+
isNew: boolean;
|
|
17
|
+
};
|
|
18
|
+
declare function verifyPassword(password: string): boolean;
|
|
19
|
+
declare function updatePassword(newPassword: string): void;
|
|
20
|
+
declare function recordLogin(): void;
|
|
21
|
+
declare function generateApiKey(): string;
|
|
22
|
+
declare function getApiKey(): string | null;
|
|
23
|
+
declare function rotateApiKey(): string;
|
|
24
|
+
declare function verifyApiKey(key: string): boolean;
|
|
25
|
+
declare function signSession(payload: string): string;
|
|
26
|
+
declare function verifySession(token: string): boolean;
|
|
27
|
+
|
|
28
|
+
export { type AdminRecord, DEFAULT_PASSWORD, generateApiKey, generateDefaultPassword, getApiKey, hashPassword, initAdmin, loadAdmin, recordLogin, rotateApiKey, saveAdmin, signSession, updatePassword, verifyApiKey, verifyPassword, verifySession };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
interface AdminRecord {
|
|
2
|
+
passwordHash: string;
|
|
3
|
+
isDefaultPassword: boolean;
|
|
4
|
+
createdAt: string;
|
|
5
|
+
lastLoginAt: string | null;
|
|
6
|
+
apiKey?: string;
|
|
7
|
+
}
|
|
8
|
+
declare function generateDefaultPassword(): string;
|
|
9
|
+
declare function hashPassword(password: string): string;
|
|
10
|
+
declare function loadAdmin(): AdminRecord | null;
|
|
11
|
+
declare function saveAdmin(record: AdminRecord): void;
|
|
12
|
+
declare const DEFAULT_PASSWORD = "Chang3M3Now!";
|
|
13
|
+
declare function initAdmin(): {
|
|
14
|
+
password: string;
|
|
15
|
+
apiKey: string;
|
|
16
|
+
isNew: boolean;
|
|
17
|
+
};
|
|
18
|
+
declare function verifyPassword(password: string): boolean;
|
|
19
|
+
declare function updatePassword(newPassword: string): void;
|
|
20
|
+
declare function recordLogin(): void;
|
|
21
|
+
declare function generateApiKey(): string;
|
|
22
|
+
declare function getApiKey(): string | null;
|
|
23
|
+
declare function rotateApiKey(): string;
|
|
24
|
+
declare function verifyApiKey(key: string): boolean;
|
|
25
|
+
declare function signSession(payload: string): string;
|
|
26
|
+
declare function verifySession(token: string): boolean;
|
|
27
|
+
|
|
28
|
+
export { type AdminRecord, DEFAULT_PASSWORD, generateApiKey, generateDefaultPassword, getApiKey, hashPassword, initAdmin, loadAdmin, recordLogin, rotateApiKey, saveAdmin, signSession, updatePassword, verifyApiKey, verifyPassword, verifySession };
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"use strict";
|
|
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 __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/dashboard/admin-store.ts
|
|
31
|
+
var admin_store_exports = {};
|
|
32
|
+
__export(admin_store_exports, {
|
|
33
|
+
DEFAULT_PASSWORD: () => DEFAULT_PASSWORD,
|
|
34
|
+
generateApiKey: () => generateApiKey,
|
|
35
|
+
generateDefaultPassword: () => generateDefaultPassword,
|
|
36
|
+
getApiKey: () => getApiKey,
|
|
37
|
+
hashPassword: () => hashPassword,
|
|
38
|
+
initAdmin: () => initAdmin,
|
|
39
|
+
loadAdmin: () => loadAdmin,
|
|
40
|
+
recordLogin: () => recordLogin,
|
|
41
|
+
rotateApiKey: () => rotateApiKey,
|
|
42
|
+
saveAdmin: () => saveAdmin,
|
|
43
|
+
signSession: () => signSession,
|
|
44
|
+
updatePassword: () => updatePassword,
|
|
45
|
+
verifyApiKey: () => verifyApiKey,
|
|
46
|
+
verifyPassword: () => verifyPassword,
|
|
47
|
+
verifySession: () => verifySession
|
|
48
|
+
});
|
|
49
|
+
module.exports = __toCommonJS(admin_store_exports);
|
|
50
|
+
var import_fs = __toESM(require("fs"));
|
|
51
|
+
var import_path = __toESM(require("path"));
|
|
52
|
+
var import_crypto = __toESM(require("crypto"));
|
|
53
|
+
function adminFilePath() {
|
|
54
|
+
const dataDir = process.env.TA_DATA_DIR ?? "data";
|
|
55
|
+
return import_path.default.join(process.cwd(), dataDir, "ta-admin.json");
|
|
56
|
+
}
|
|
57
|
+
function generateDefaultPassword() {
|
|
58
|
+
return import_crypto.default.randomBytes(6).toString("hex");
|
|
59
|
+
}
|
|
60
|
+
function hashPassword(password) {
|
|
61
|
+
const secret = process.env.THIRD_AUDIENCE_SECRET ?? "ta-salt";
|
|
62
|
+
return import_crypto.default.createHash("sha256").update(secret + password).digest("hex");
|
|
63
|
+
}
|
|
64
|
+
function loadAdmin() {
|
|
65
|
+
const filePath = adminFilePath();
|
|
66
|
+
if (!import_fs.default.existsSync(filePath)) return null;
|
|
67
|
+
try {
|
|
68
|
+
return JSON.parse(import_fs.default.readFileSync(filePath, "utf-8"));
|
|
69
|
+
} catch {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function saveAdmin(record) {
|
|
74
|
+
const filePath = adminFilePath();
|
|
75
|
+
const dir = import_path.default.dirname(filePath);
|
|
76
|
+
if (!import_fs.default.existsSync(dir)) import_fs.default.mkdirSync(dir, { recursive: true });
|
|
77
|
+
import_fs.default.writeFileSync(filePath, JSON.stringify(record, null, 2), "utf-8");
|
|
78
|
+
}
|
|
79
|
+
var DEFAULT_PASSWORD = "Chang3M3Now!";
|
|
80
|
+
function initAdmin() {
|
|
81
|
+
const existing = loadAdmin();
|
|
82
|
+
if (existing) return { password: "", apiKey: "", isNew: false };
|
|
83
|
+
const apiKey = generateApiKey();
|
|
84
|
+
saveAdmin({
|
|
85
|
+
passwordHash: hashPassword(DEFAULT_PASSWORD),
|
|
86
|
+
isDefaultPassword: true,
|
|
87
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
88
|
+
lastLoginAt: null,
|
|
89
|
+
apiKey: encryptApiKey(apiKey)
|
|
90
|
+
});
|
|
91
|
+
return { password: DEFAULT_PASSWORD, apiKey, isNew: true };
|
|
92
|
+
}
|
|
93
|
+
function verifyPassword(password) {
|
|
94
|
+
const record = loadAdmin();
|
|
95
|
+
if (!record) return false;
|
|
96
|
+
return record.passwordHash === hashPassword(password);
|
|
97
|
+
}
|
|
98
|
+
function updatePassword(newPassword) {
|
|
99
|
+
const record = loadAdmin();
|
|
100
|
+
if (!record) return;
|
|
101
|
+
saveAdmin({
|
|
102
|
+
...record,
|
|
103
|
+
passwordHash: hashPassword(newPassword),
|
|
104
|
+
isDefaultPassword: false
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
function recordLogin() {
|
|
108
|
+
const record = loadAdmin();
|
|
109
|
+
if (!record) return;
|
|
110
|
+
saveAdmin({ ...record, lastLoginAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
111
|
+
}
|
|
112
|
+
var CIPHER = "aes-256-gcm";
|
|
113
|
+
function getEncryptionKey() {
|
|
114
|
+
const secret = process.env.THIRD_AUDIENCE_SECRET ?? "ta-fallback-key-change-me";
|
|
115
|
+
return import_crypto.default.createHash("sha256").update(secret).digest();
|
|
116
|
+
}
|
|
117
|
+
function encryptApiKey(plaintext) {
|
|
118
|
+
const iv = import_crypto.default.randomBytes(12);
|
|
119
|
+
const key = getEncryptionKey();
|
|
120
|
+
const cipher = import_crypto.default.createCipheriv(CIPHER, key, iv);
|
|
121
|
+
const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
|
|
122
|
+
const tag = cipher.getAuthTag();
|
|
123
|
+
return iv.toString("hex") + tag.toString("hex") + encrypted.toString("hex");
|
|
124
|
+
}
|
|
125
|
+
function decryptApiKey(encoded) {
|
|
126
|
+
try {
|
|
127
|
+
const iv = Buffer.from(encoded.slice(0, 24), "hex");
|
|
128
|
+
const tag = Buffer.from(encoded.slice(24, 56), "hex");
|
|
129
|
+
const encrypted = Buffer.from(encoded.slice(56), "hex");
|
|
130
|
+
const key = getEncryptionKey();
|
|
131
|
+
const decipher = import_crypto.default.createDecipheriv(CIPHER, key, iv);
|
|
132
|
+
decipher.setAuthTag(tag);
|
|
133
|
+
return decipher.update(encrypted) + decipher.final("utf8");
|
|
134
|
+
} catch {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function generateApiKey() {
|
|
139
|
+
return "ta_" + import_crypto.default.randomBytes(24).toString("hex");
|
|
140
|
+
}
|
|
141
|
+
function getApiKey() {
|
|
142
|
+
const record = loadAdmin();
|
|
143
|
+
if (!record?.apiKey) return null;
|
|
144
|
+
return decryptApiKey(record.apiKey);
|
|
145
|
+
}
|
|
146
|
+
function rotateApiKey() {
|
|
147
|
+
const record = loadAdmin();
|
|
148
|
+
if (!record) throw new Error("Admin store not initialised");
|
|
149
|
+
const newKey = generateApiKey();
|
|
150
|
+
saveAdmin({ ...record, apiKey: encryptApiKey(newKey) });
|
|
151
|
+
return newKey;
|
|
152
|
+
}
|
|
153
|
+
function verifyApiKey(key) {
|
|
154
|
+
const stored = getApiKey();
|
|
155
|
+
if (!stored) return false;
|
|
156
|
+
if (key.length !== stored.length) return false;
|
|
157
|
+
return import_crypto.default.timingSafeEqual(Buffer.from(key), Buffer.from(stored));
|
|
158
|
+
}
|
|
159
|
+
function signSession(payload) {
|
|
160
|
+
const secret = process.env.THIRD_AUDIENCE_SECRET ?? "ta-salt";
|
|
161
|
+
const sig = import_crypto.default.createHmac("sha256", secret).update(payload).digest("hex");
|
|
162
|
+
return `${payload}.${sig}`;
|
|
163
|
+
}
|
|
164
|
+
function verifySession(token) {
|
|
165
|
+
const lastDot = token.lastIndexOf(".");
|
|
166
|
+
if (lastDot === -1) return false;
|
|
167
|
+
const payload = token.slice(0, lastDot);
|
|
168
|
+
const sig = token.slice(lastDot + 1);
|
|
169
|
+
const expected = import_crypto.default.createHmac("sha256", process.env.THIRD_AUDIENCE_SECRET ?? "ta-salt").update(payload).digest("hex");
|
|
170
|
+
if (sig.length !== expected.length) return false;
|
|
171
|
+
return import_crypto.default.timingSafeEqual(Buffer.from(sig, "hex"), Buffer.from(expected, "hex"));
|
|
172
|
+
}
|
|
173
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
174
|
+
0 && (module.exports = {
|
|
175
|
+
DEFAULT_PASSWORD,
|
|
176
|
+
generateApiKey,
|
|
177
|
+
generateDefaultPassword,
|
|
178
|
+
getApiKey,
|
|
179
|
+
hashPassword,
|
|
180
|
+
initAdmin,
|
|
181
|
+
loadAdmin,
|
|
182
|
+
recordLogin,
|
|
183
|
+
rotateApiKey,
|
|
184
|
+
saveAdmin,
|
|
185
|
+
signSession,
|
|
186
|
+
updatePassword,
|
|
187
|
+
verifyApiKey,
|
|
188
|
+
verifyPassword,
|
|
189
|
+
verifySession
|
|
190
|
+
});
|
|
191
|
+
//# sourceMappingURL=admin-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/dashboard/admin-store.ts"],"sourcesContent":["import fs from 'fs'\nimport path from 'path'\nimport crypto from 'crypto'\n\nexport interface AdminRecord {\n passwordHash: string // sha256(secret + password)\n isDefaultPassword: boolean\n createdAt: string\n lastLoginAt: string | null\n apiKey?: string // AES-256-GCM encrypted, for headless/external API callers\n}\n\nfunction adminFilePath(): string {\n const dataDir = process.env.TA_DATA_DIR ?? 'data'\n return path.join(process.cwd(), dataDir, 'ta-admin.json')\n}\n\nexport function generateDefaultPassword(): string {\n return crypto.randomBytes(6).toString('hex') // 12-char hex, easy to type\n}\n\nexport function hashPassword(password: string): string {\n const secret = process.env.THIRD_AUDIENCE_SECRET ?? 'ta-salt'\n return crypto.createHash('sha256').update(secret + password).digest('hex')\n}\n\nexport function loadAdmin(): AdminRecord | null {\n const filePath = adminFilePath()\n if (!fs.existsSync(filePath)) return null\n try {\n return JSON.parse(fs.readFileSync(filePath, 'utf-8')) as AdminRecord\n } catch {\n return null\n }\n}\n\nexport function saveAdmin(record: AdminRecord): void {\n const filePath = adminFilePath()\n const dir = path.dirname(filePath)\n if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true })\n fs.writeFileSync(filePath, JSON.stringify(record, null, 2), 'utf-8')\n}\n\nexport const DEFAULT_PASSWORD = 'Chang3M3Now!'\n\nexport function initAdmin(): { password: string; apiKey: string; isNew: boolean } {\n const existing = loadAdmin()\n if (existing) return { password: '', apiKey: '', isNew: false }\n\n const apiKey = generateApiKey()\n saveAdmin({\n passwordHash: hashPassword(DEFAULT_PASSWORD),\n isDefaultPassword: true,\n createdAt: new Date().toISOString(),\n lastLoginAt: null,\n apiKey: encryptApiKey(apiKey),\n })\n return { password: DEFAULT_PASSWORD, apiKey, isNew: true }\n}\n\nexport function verifyPassword(password: string): boolean {\n const record = loadAdmin()\n if (!record) return false\n return record.passwordHash === hashPassword(password)\n}\n\nexport function updatePassword(newPassword: string): void {\n const record = loadAdmin()\n if (!record) return\n saveAdmin({\n ...record,\n passwordHash: hashPassword(newPassword),\n isDefaultPassword: false,\n })\n}\n\nexport function recordLogin(): void {\n const record = loadAdmin()\n if (!record) return\n saveAdmin({ ...record, lastLoginAt: new Date().toISOString() })\n}\n\n// ---------------------------------------------------------------------------\n// API key — AES-256-GCM encrypted at rest, mirroring WP's SECURE_AUTH_KEY approach\n// ---------------------------------------------------------------------------\n\nconst CIPHER = 'aes-256-gcm'\n\nfunction getEncryptionKey(): Buffer {\n const secret = process.env.THIRD_AUDIENCE_SECRET ?? 'ta-fallback-key-change-me'\n // Derive a 32-byte key from the secret using SHA-256\n return crypto.createHash('sha256').update(secret).digest()\n}\n\nfunction encryptApiKey(plaintext: string): string {\n const iv = crypto.randomBytes(12)\n const key = getEncryptionKey()\n const cipher = crypto.createCipheriv(CIPHER, key, iv) as crypto.CipherGCM\n const encrypted = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()])\n const tag = cipher.getAuthTag()\n // Format: iv(24 hex) + tag(32 hex) + encrypted(hex)\n return iv.toString('hex') + tag.toString('hex') + encrypted.toString('hex')\n}\n\nfunction decryptApiKey(encoded: string): string | null {\n try {\n const iv = Buffer.from(encoded.slice(0, 24), 'hex')\n const tag = Buffer.from(encoded.slice(24, 56), 'hex')\n const encrypted = Buffer.from(encoded.slice(56), 'hex')\n const key = getEncryptionKey()\n const decipher = crypto.createDecipheriv(CIPHER, key, iv) as crypto.DecipherGCM\n decipher.setAuthTag(tag)\n return decipher.update(encrypted) + decipher.final('utf8')\n } catch {\n return null\n }\n}\n\nexport function generateApiKey(): string {\n return 'ta_' + crypto.randomBytes(24).toString('hex') // 51-char key\n}\n\nexport function getApiKey(): string | null {\n const record = loadAdmin()\n if (!record?.apiKey) return null\n return decryptApiKey(record.apiKey)\n}\n\nexport function rotateApiKey(): string {\n const record = loadAdmin()\n if (!record) throw new Error('Admin store not initialised')\n const newKey = generateApiKey()\n saveAdmin({ ...record, apiKey: encryptApiKey(newKey) })\n return newKey\n}\n\nexport function verifyApiKey(key: string): boolean {\n const stored = getApiKey()\n if (!stored) return false\n if (key.length !== stored.length) return false\n return crypto.timingSafeEqual(Buffer.from(key), Buffer.from(stored))\n}\n\n// ---------------------------------------------------------------------------\n// Session cookie: HMAC-SHA256(secret, userId + timestamp) — stateless, no DB\n// ---------------------------------------------------------------------------\nexport function signSession(payload: string): string {\n const secret = process.env.THIRD_AUDIENCE_SECRET ?? 'ta-salt'\n const sig = crypto.createHmac('sha256', secret).update(payload).digest('hex')\n return `${payload}.${sig}`\n}\n\nexport function verifySession(token: string): boolean {\n const lastDot = token.lastIndexOf('.')\n if (lastDot === -1) return false\n const payload = token.slice(0, lastDot)\n const sig = token.slice(lastDot + 1)\n const expected = crypto.createHmac('sha256', process.env.THIRD_AUDIENCE_SECRET ?? 'ta-salt')\n .update(payload).digest('hex')\n // Constant-time comparison\n if (sig.length !== expected.length) return false\n return crypto.timingSafeEqual(Buffer.from(sig, 'hex'), Buffer.from(expected, 'hex'))\n}\n"],"mappings":"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","names":["path","crypto","fs"]}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// src/dashboard/admin-store.ts
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import crypto from "crypto";
|
|
5
|
+
function adminFilePath() {
|
|
6
|
+
const dataDir = process.env.TA_DATA_DIR ?? "data";
|
|
7
|
+
return path.join(process.cwd(), dataDir, "ta-admin.json");
|
|
8
|
+
}
|
|
9
|
+
function generateDefaultPassword() {
|
|
10
|
+
return crypto.randomBytes(6).toString("hex");
|
|
11
|
+
}
|
|
12
|
+
function hashPassword(password) {
|
|
13
|
+
const secret = process.env.THIRD_AUDIENCE_SECRET ?? "ta-salt";
|
|
14
|
+
return crypto.createHash("sha256").update(secret + password).digest("hex");
|
|
15
|
+
}
|
|
16
|
+
function loadAdmin() {
|
|
17
|
+
const filePath = adminFilePath();
|
|
18
|
+
if (!fs.existsSync(filePath)) return null;
|
|
19
|
+
try {
|
|
20
|
+
return JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
21
|
+
} catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function saveAdmin(record) {
|
|
26
|
+
const filePath = adminFilePath();
|
|
27
|
+
const dir = path.dirname(filePath);
|
|
28
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
29
|
+
fs.writeFileSync(filePath, JSON.stringify(record, null, 2), "utf-8");
|
|
30
|
+
}
|
|
31
|
+
var DEFAULT_PASSWORD = "Chang3M3Now!";
|
|
32
|
+
function initAdmin() {
|
|
33
|
+
const existing = loadAdmin();
|
|
34
|
+
if (existing) return { password: "", apiKey: "", isNew: false };
|
|
35
|
+
const apiKey = generateApiKey();
|
|
36
|
+
saveAdmin({
|
|
37
|
+
passwordHash: hashPassword(DEFAULT_PASSWORD),
|
|
38
|
+
isDefaultPassword: true,
|
|
39
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
40
|
+
lastLoginAt: null,
|
|
41
|
+
apiKey: encryptApiKey(apiKey)
|
|
42
|
+
});
|
|
43
|
+
return { password: DEFAULT_PASSWORD, apiKey, isNew: true };
|
|
44
|
+
}
|
|
45
|
+
function verifyPassword(password) {
|
|
46
|
+
const record = loadAdmin();
|
|
47
|
+
if (!record) return false;
|
|
48
|
+
return record.passwordHash === hashPassword(password);
|
|
49
|
+
}
|
|
50
|
+
function updatePassword(newPassword) {
|
|
51
|
+
const record = loadAdmin();
|
|
52
|
+
if (!record) return;
|
|
53
|
+
saveAdmin({
|
|
54
|
+
...record,
|
|
55
|
+
passwordHash: hashPassword(newPassword),
|
|
56
|
+
isDefaultPassword: false
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
function recordLogin() {
|
|
60
|
+
const record = loadAdmin();
|
|
61
|
+
if (!record) return;
|
|
62
|
+
saveAdmin({ ...record, lastLoginAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
63
|
+
}
|
|
64
|
+
var CIPHER = "aes-256-gcm";
|
|
65
|
+
function getEncryptionKey() {
|
|
66
|
+
const secret = process.env.THIRD_AUDIENCE_SECRET ?? "ta-fallback-key-change-me";
|
|
67
|
+
return crypto.createHash("sha256").update(secret).digest();
|
|
68
|
+
}
|
|
69
|
+
function encryptApiKey(plaintext) {
|
|
70
|
+
const iv = crypto.randomBytes(12);
|
|
71
|
+
const key = getEncryptionKey();
|
|
72
|
+
const cipher = crypto.createCipheriv(CIPHER, key, iv);
|
|
73
|
+
const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
|
|
74
|
+
const tag = cipher.getAuthTag();
|
|
75
|
+
return iv.toString("hex") + tag.toString("hex") + encrypted.toString("hex");
|
|
76
|
+
}
|
|
77
|
+
function decryptApiKey(encoded) {
|
|
78
|
+
try {
|
|
79
|
+
const iv = Buffer.from(encoded.slice(0, 24), "hex");
|
|
80
|
+
const tag = Buffer.from(encoded.slice(24, 56), "hex");
|
|
81
|
+
const encrypted = Buffer.from(encoded.slice(56), "hex");
|
|
82
|
+
const key = getEncryptionKey();
|
|
83
|
+
const decipher = crypto.createDecipheriv(CIPHER, key, iv);
|
|
84
|
+
decipher.setAuthTag(tag);
|
|
85
|
+
return decipher.update(encrypted) + decipher.final("utf8");
|
|
86
|
+
} catch {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function generateApiKey() {
|
|
91
|
+
return "ta_" + crypto.randomBytes(24).toString("hex");
|
|
92
|
+
}
|
|
93
|
+
function getApiKey() {
|
|
94
|
+
const record = loadAdmin();
|
|
95
|
+
if (!record?.apiKey) return null;
|
|
96
|
+
return decryptApiKey(record.apiKey);
|
|
97
|
+
}
|
|
98
|
+
function rotateApiKey() {
|
|
99
|
+
const record = loadAdmin();
|
|
100
|
+
if (!record) throw new Error("Admin store not initialised");
|
|
101
|
+
const newKey = generateApiKey();
|
|
102
|
+
saveAdmin({ ...record, apiKey: encryptApiKey(newKey) });
|
|
103
|
+
return newKey;
|
|
104
|
+
}
|
|
105
|
+
function verifyApiKey(key) {
|
|
106
|
+
const stored = getApiKey();
|
|
107
|
+
if (!stored) return false;
|
|
108
|
+
if (key.length !== stored.length) return false;
|
|
109
|
+
return crypto.timingSafeEqual(Buffer.from(key), Buffer.from(stored));
|
|
110
|
+
}
|
|
111
|
+
function signSession(payload) {
|
|
112
|
+
const secret = process.env.THIRD_AUDIENCE_SECRET ?? "ta-salt";
|
|
113
|
+
const sig = crypto.createHmac("sha256", secret).update(payload).digest("hex");
|
|
114
|
+
return `${payload}.${sig}`;
|
|
115
|
+
}
|
|
116
|
+
function verifySession(token) {
|
|
117
|
+
const lastDot = token.lastIndexOf(".");
|
|
118
|
+
if (lastDot === -1) return false;
|
|
119
|
+
const payload = token.slice(0, lastDot);
|
|
120
|
+
const sig = token.slice(lastDot + 1);
|
|
121
|
+
const expected = crypto.createHmac("sha256", process.env.THIRD_AUDIENCE_SECRET ?? "ta-salt").update(payload).digest("hex");
|
|
122
|
+
if (sig.length !== expected.length) return false;
|
|
123
|
+
return crypto.timingSafeEqual(Buffer.from(sig, "hex"), Buffer.from(expected, "hex"));
|
|
124
|
+
}
|
|
125
|
+
export {
|
|
126
|
+
DEFAULT_PASSWORD,
|
|
127
|
+
generateApiKey,
|
|
128
|
+
generateDefaultPassword,
|
|
129
|
+
getApiKey,
|
|
130
|
+
hashPassword,
|
|
131
|
+
initAdmin,
|
|
132
|
+
loadAdmin,
|
|
133
|
+
recordLogin,
|
|
134
|
+
rotateApiKey,
|
|
135
|
+
saveAdmin,
|
|
136
|
+
signSession,
|
|
137
|
+
updatePassword,
|
|
138
|
+
verifyApiKey,
|
|
139
|
+
verifyPassword,
|
|
140
|
+
verifySession
|
|
141
|
+
};
|
|
142
|
+
//# sourceMappingURL=admin-store.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/dashboard/admin-store.ts"],"sourcesContent":["import fs from 'fs'\nimport path from 'path'\nimport crypto from 'crypto'\n\nexport interface AdminRecord {\n passwordHash: string // sha256(secret + password)\n isDefaultPassword: boolean\n createdAt: string\n lastLoginAt: string | null\n apiKey?: string // AES-256-GCM encrypted, for headless/external API callers\n}\n\nfunction adminFilePath(): string {\n const dataDir = process.env.TA_DATA_DIR ?? 'data'\n return path.join(process.cwd(), dataDir, 'ta-admin.json')\n}\n\nexport function generateDefaultPassword(): string {\n return crypto.randomBytes(6).toString('hex') // 12-char hex, easy to type\n}\n\nexport function hashPassword(password: string): string {\n const secret = process.env.THIRD_AUDIENCE_SECRET ?? 'ta-salt'\n return crypto.createHash('sha256').update(secret + password).digest('hex')\n}\n\nexport function loadAdmin(): AdminRecord | null {\n const filePath = adminFilePath()\n if (!fs.existsSync(filePath)) return null\n try {\n return JSON.parse(fs.readFileSync(filePath, 'utf-8')) as AdminRecord\n } catch {\n return null\n }\n}\n\nexport function saveAdmin(record: AdminRecord): void {\n const filePath = adminFilePath()\n const dir = path.dirname(filePath)\n if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true })\n fs.writeFileSync(filePath, JSON.stringify(record, null, 2), 'utf-8')\n}\n\nexport const DEFAULT_PASSWORD = 'Chang3M3Now!'\n\nexport function initAdmin(): { password: string; apiKey: string; isNew: boolean } {\n const existing = loadAdmin()\n if (existing) return { password: '', apiKey: '', isNew: false }\n\n const apiKey = generateApiKey()\n saveAdmin({\n passwordHash: hashPassword(DEFAULT_PASSWORD),\n isDefaultPassword: true,\n createdAt: new Date().toISOString(),\n lastLoginAt: null,\n apiKey: encryptApiKey(apiKey),\n })\n return { password: DEFAULT_PASSWORD, apiKey, isNew: true }\n}\n\nexport function verifyPassword(password: string): boolean {\n const record = loadAdmin()\n if (!record) return false\n return record.passwordHash === hashPassword(password)\n}\n\nexport function updatePassword(newPassword: string): void {\n const record = loadAdmin()\n if (!record) return\n saveAdmin({\n ...record,\n passwordHash: hashPassword(newPassword),\n isDefaultPassword: false,\n })\n}\n\nexport function recordLogin(): void {\n const record = loadAdmin()\n if (!record) return\n saveAdmin({ ...record, lastLoginAt: new Date().toISOString() })\n}\n\n// ---------------------------------------------------------------------------\n// API key — AES-256-GCM encrypted at rest, mirroring WP's SECURE_AUTH_KEY approach\n// ---------------------------------------------------------------------------\n\nconst CIPHER = 'aes-256-gcm'\n\nfunction getEncryptionKey(): Buffer {\n const secret = process.env.THIRD_AUDIENCE_SECRET ?? 'ta-fallback-key-change-me'\n // Derive a 32-byte key from the secret using SHA-256\n return crypto.createHash('sha256').update(secret).digest()\n}\n\nfunction encryptApiKey(plaintext: string): string {\n const iv = crypto.randomBytes(12)\n const key = getEncryptionKey()\n const cipher = crypto.createCipheriv(CIPHER, key, iv) as crypto.CipherGCM\n const encrypted = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()])\n const tag = cipher.getAuthTag()\n // Format: iv(24 hex) + tag(32 hex) + encrypted(hex)\n return iv.toString('hex') + tag.toString('hex') + encrypted.toString('hex')\n}\n\nfunction decryptApiKey(encoded: string): string | null {\n try {\n const iv = Buffer.from(encoded.slice(0, 24), 'hex')\n const tag = Buffer.from(encoded.slice(24, 56), 'hex')\n const encrypted = Buffer.from(encoded.slice(56), 'hex')\n const key = getEncryptionKey()\n const decipher = crypto.createDecipheriv(CIPHER, key, iv) as crypto.DecipherGCM\n decipher.setAuthTag(tag)\n return decipher.update(encrypted) + decipher.final('utf8')\n } catch {\n return null\n }\n}\n\nexport function generateApiKey(): string {\n return 'ta_' + crypto.randomBytes(24).toString('hex') // 51-char key\n}\n\nexport function getApiKey(): string | null {\n const record = loadAdmin()\n if (!record?.apiKey) return null\n return decryptApiKey(record.apiKey)\n}\n\nexport function rotateApiKey(): string {\n const record = loadAdmin()\n if (!record) throw new Error('Admin store not initialised')\n const newKey = generateApiKey()\n saveAdmin({ ...record, apiKey: encryptApiKey(newKey) })\n return newKey\n}\n\nexport function verifyApiKey(key: string): boolean {\n const stored = getApiKey()\n if (!stored) return false\n if (key.length !== stored.length) return false\n return crypto.timingSafeEqual(Buffer.from(key), Buffer.from(stored))\n}\n\n// ---------------------------------------------------------------------------\n// Session cookie: HMAC-SHA256(secret, userId + timestamp) — stateless, no DB\n// ---------------------------------------------------------------------------\nexport function signSession(payload: string): string {\n const secret = process.env.THIRD_AUDIENCE_SECRET ?? 'ta-salt'\n const sig = crypto.createHmac('sha256', secret).update(payload).digest('hex')\n return `${payload}.${sig}`\n}\n\nexport function verifySession(token: string): boolean {\n const lastDot = token.lastIndexOf('.')\n if (lastDot === -1) return false\n const payload = token.slice(0, lastDot)\n const sig = token.slice(lastDot + 1)\n const expected = crypto.createHmac('sha256', process.env.THIRD_AUDIENCE_SECRET ?? 'ta-salt')\n .update(payload).digest('hex')\n // Constant-time comparison\n if (sig.length !== expected.length) return false\n return crypto.timingSafeEqual(Buffer.from(sig, 'hex'), Buffer.from(expected, 'hex'))\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
interface CardProps {
|
|
5
|
+
title: string;
|
|
6
|
+
action?: ReactNode;
|
|
7
|
+
children: ReactNode;
|
|
8
|
+
}
|
|
9
|
+
declare function Card({ title, action, children }: CardProps): react.JSX.Element;
|
|
10
|
+
|
|
11
|
+
export { Card };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
interface CardProps {
|
|
5
|
+
title: string;
|
|
6
|
+
action?: ReactNode;
|
|
7
|
+
children: ReactNode;
|
|
8
|
+
}
|
|
9
|
+
declare function Card({ title, action, children }: CardProps): react.JSX.Element;
|
|
10
|
+
|
|
11
|
+
export { Card };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/dashboard/ui/components/Card.tsx
|
|
21
|
+
var Card_exports = {};
|
|
22
|
+
__export(Card_exports, {
|
|
23
|
+
Card: () => Card
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(Card_exports);
|
|
26
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
27
|
+
function Card({ title, action, children }) {
|
|
28
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ta-card ta-section", children: [
|
|
29
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ta-card-header", children: [
|
|
30
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { children: title }),
|
|
31
|
+
action
|
|
32
|
+
] }),
|
|
33
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ta-card-body", children })
|
|
34
|
+
] });
|
|
35
|
+
}
|
|
36
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
37
|
+
0 && (module.exports = {
|
|
38
|
+
Card
|
|
39
|
+
});
|
|
40
|
+
//# sourceMappingURL=Card.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/dashboard/ui/components/Card.tsx"],"sourcesContent":["import type { ReactNode } from 'react'\n\ninterface CardProps {\n title: string\n action?: ReactNode\n children: ReactNode\n}\n\nexport function Card({ title, action, children }: CardProps) {\n return (\n <div className=\"ta-card ta-section\">\n <div className=\"ta-card-header\">\n <h2>{title}</h2>\n {action}\n </div>\n <div className=\"ta-card-body\">{children}</div>\n </div>\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAWM;AAHC,SAAS,KAAK,EAAE,OAAO,QAAQ,SAAS,GAAc;AAC3D,SACE,6CAAC,SAAI,WAAU,sBACb;AAAA,iDAAC,SAAI,WAAU,kBACb;AAAA,kDAAC,QAAI,iBAAM;AAAA,MACV;AAAA,OACH;AAAA,IACA,4CAAC,SAAI,WAAU,gBAAgB,UAAS;AAAA,KAC1C;AAEJ;","names":[]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// src/dashboard/ui/components/Card.tsx
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
function Card({ title, action, children }) {
|
|
4
|
+
return /* @__PURE__ */ jsxs("div", { className: "ta-card ta-section", children: [
|
|
5
|
+
/* @__PURE__ */ jsxs("div", { className: "ta-card-header", children: [
|
|
6
|
+
/* @__PURE__ */ jsx("h2", { children: title }),
|
|
7
|
+
action
|
|
8
|
+
] }),
|
|
9
|
+
/* @__PURE__ */ jsx("div", { className: "ta-card-body", children })
|
|
10
|
+
] });
|
|
11
|
+
}
|
|
12
|
+
export {
|
|
13
|
+
Card
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=Card.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/dashboard/ui/components/Card.tsx"],"sourcesContent":["import type { ReactNode } from 'react'\n\ninterface CardProps {\n title: string\n action?: ReactNode\n children: ReactNode\n}\n\nexport function Card({ title, action, children }: CardProps) {\n return (\n <div className=\"ta-card ta-section\">\n <div className=\"ta-card-header\">\n <h2>{title}</h2>\n {action}\n </div>\n <div className=\"ta-card-body\">{children}</div>\n </div>\n )\n}\n"],"mappings":";AAWM,SACE,KADF;AAHC,SAAS,KAAK,EAAE,OAAO,QAAQ,SAAS,GAAc;AAC3D,SACE,qBAAC,SAAI,WAAU,sBACb;AAAA,yBAAC,SAAI,WAAU,kBACb;AAAA,0BAAC,QAAI,iBAAM;AAAA,MACV;AAAA,OACH;AAAA,IACA,oBAAC,SAAI,WAAU,gBAAgB,UAAS;AAAA,KAC1C;AAEJ;","names":[]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
interface HeroCardProps {
|
|
5
|
+
label: string;
|
|
6
|
+
value: string | number;
|
|
7
|
+
meta?: string;
|
|
8
|
+
color?: 'blue' | 'green' | 'orange' | 'teal';
|
|
9
|
+
icon: ReactNode;
|
|
10
|
+
}
|
|
11
|
+
declare function HeroCard({ label, value, meta, color, icon }: HeroCardProps): react.JSX.Element;
|
|
12
|
+
|
|
13
|
+
export { HeroCard };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
interface HeroCardProps {
|
|
5
|
+
label: string;
|
|
6
|
+
value: string | number;
|
|
7
|
+
meta?: string;
|
|
8
|
+
color?: 'blue' | 'green' | 'orange' | 'teal';
|
|
9
|
+
icon: ReactNode;
|
|
10
|
+
}
|
|
11
|
+
declare function HeroCard({ label, value, meta, color, icon }: HeroCardProps): react.JSX.Element;
|
|
12
|
+
|
|
13
|
+
export { HeroCard };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/dashboard/ui/components/HeroCard.tsx
|
|
21
|
+
var HeroCard_exports = {};
|
|
22
|
+
__export(HeroCard_exports, {
|
|
23
|
+
HeroCard: () => HeroCard
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(HeroCard_exports);
|
|
26
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
27
|
+
function HeroCard({ label, value, meta, color = "blue", icon }) {
|
|
28
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: `ta-hero-card ta-hero-card--${color}`, children: [
|
|
29
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ta-hero-icon", children: icon }),
|
|
30
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
31
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ta-hero-label", children: label }),
|
|
32
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ta-hero-value", children: value }),
|
|
33
|
+
meta && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ta-hero-meta", children: meta })
|
|
34
|
+
] })
|
|
35
|
+
] });
|
|
36
|
+
}
|
|
37
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
38
|
+
0 && (module.exports = {
|
|
39
|
+
HeroCard
|
|
40
|
+
});
|
|
41
|
+
//# sourceMappingURL=HeroCard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/dashboard/ui/components/HeroCard.tsx"],"sourcesContent":["import type { ReactNode } from 'react'\n\ninterface HeroCardProps {\n label: string\n value: string | number\n meta?: string\n color?: 'blue' | 'green' | 'orange' | 'teal'\n icon: ReactNode\n}\n\nexport function HeroCard({ label, value, meta, color = 'blue', icon }: HeroCardProps) {\n return (\n <div className={`ta-hero-card ta-hero-card--${color}`}>\n <div className=\"ta-hero-icon\">{icon}</div>\n <div>\n <div className=\"ta-hero-label\">{label}</div>\n <div className=\"ta-hero-value\">{value}</div>\n {meta && <div className=\"ta-hero-meta\">{meta}</div>}\n </div>\n </div>\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAaM;AAHC,SAAS,SAAS,EAAE,OAAO,OAAO,MAAM,QAAQ,QAAQ,KAAK,GAAkB;AACpF,SACE,6CAAC,SAAI,WAAW,8BAA8B,KAAK,IACjD;AAAA,gDAAC,SAAI,WAAU,gBAAgB,gBAAK;AAAA,IACpC,6CAAC,SACC;AAAA,kDAAC,SAAI,WAAU,iBAAiB,iBAAM;AAAA,MACtC,4CAAC,SAAI,WAAU,iBAAiB,iBAAM;AAAA,MACrC,QAAQ,4CAAC,SAAI,WAAU,gBAAgB,gBAAK;AAAA,OAC/C;AAAA,KACF;AAEJ;","names":[]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// src/dashboard/ui/components/HeroCard.tsx
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
function HeroCard({ label, value, meta, color = "blue", icon }) {
|
|
4
|
+
return /* @__PURE__ */ jsxs("div", { className: `ta-hero-card ta-hero-card--${color}`, children: [
|
|
5
|
+
/* @__PURE__ */ jsx("div", { className: "ta-hero-icon", children: icon }),
|
|
6
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
7
|
+
/* @__PURE__ */ jsx("div", { className: "ta-hero-label", children: label }),
|
|
8
|
+
/* @__PURE__ */ jsx("div", { className: "ta-hero-value", children: value }),
|
|
9
|
+
meta && /* @__PURE__ */ jsx("div", { className: "ta-hero-meta", children: meta })
|
|
10
|
+
] })
|
|
11
|
+
] });
|
|
12
|
+
}
|
|
13
|
+
export {
|
|
14
|
+
HeroCard
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=HeroCard.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/dashboard/ui/components/HeroCard.tsx"],"sourcesContent":["import type { ReactNode } from 'react'\n\ninterface HeroCardProps {\n label: string\n value: string | number\n meta?: string\n color?: 'blue' | 'green' | 'orange' | 'teal'\n icon: ReactNode\n}\n\nexport function HeroCard({ label, value, meta, color = 'blue', icon }: HeroCardProps) {\n return (\n <div className={`ta-hero-card ta-hero-card--${color}`}>\n <div className=\"ta-hero-icon\">{icon}</div>\n <div>\n <div className=\"ta-hero-label\">{label}</div>\n <div className=\"ta-hero-value\">{value}</div>\n {meta && <div className=\"ta-hero-meta\">{meta}</div>}\n </div>\n </div>\n )\n}\n"],"mappings":";AAaM,cACA,YADA;AAHC,SAAS,SAAS,EAAE,OAAO,OAAO,MAAM,QAAQ,QAAQ,KAAK,GAAkB;AACpF,SACE,qBAAC,SAAI,WAAW,8BAA8B,KAAK,IACjD;AAAA,wBAAC,SAAI,WAAU,gBAAgB,gBAAK;AAAA,IACpC,qBAAC,SACC;AAAA,0BAAC,SAAI,WAAU,iBAAiB,iBAAM;AAAA,MACtC,oBAAC,SAAI,WAAU,iBAAiB,iBAAM;AAAA,MACrC,QAAQ,oBAAC,SAAI,WAAU,gBAAgB,gBAAK;AAAA,OAC/C;AAAA,KACF;AAEJ;","names":[]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
|
|
3
|
+
interface DayData {
|
|
4
|
+
date: string;
|
|
5
|
+
visits: number;
|
|
6
|
+
}
|
|
7
|
+
interface VisitsChartProps {
|
|
8
|
+
data: DayData[];
|
|
9
|
+
height?: number;
|
|
10
|
+
}
|
|
11
|
+
declare function VisitsChart({ data, height }: VisitsChartProps): react.JSX.Element;
|
|
12
|
+
|
|
13
|
+
export { VisitsChart };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
|
|
3
|
+
interface DayData {
|
|
4
|
+
date: string;
|
|
5
|
+
visits: number;
|
|
6
|
+
}
|
|
7
|
+
interface VisitsChartProps {
|
|
8
|
+
data: DayData[];
|
|
9
|
+
height?: number;
|
|
10
|
+
}
|
|
11
|
+
declare function VisitsChart({ data, height }: VisitsChartProps): react.JSX.Element;
|
|
12
|
+
|
|
13
|
+
export { VisitsChart };
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
"use client";
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
|
|
21
|
+
// src/dashboard/ui/components/VisitsChart.tsx
|
|
22
|
+
var VisitsChart_exports = {};
|
|
23
|
+
__export(VisitsChart_exports, {
|
|
24
|
+
VisitsChart: () => VisitsChart
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(VisitsChart_exports);
|
|
27
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
28
|
+
function VisitsChart({ data, height = 160 }) {
|
|
29
|
+
if (!data.length) {
|
|
30
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ta-empty", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: "No visit data yet." }) });
|
|
31
|
+
}
|
|
32
|
+
const max = Math.max(...data.map((d) => d.visits), 1);
|
|
33
|
+
const barWidth = Math.max(4, Math.floor(560 / data.length) - 2);
|
|
34
|
+
const showLabel = data.length <= 14;
|
|
35
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { overflowX: "auto" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
36
|
+
"svg",
|
|
37
|
+
{
|
|
38
|
+
width: "100%",
|
|
39
|
+
viewBox: `0 0 ${Math.max(data.length * (barWidth + 2), 560)} ${height + 40}`,
|
|
40
|
+
style: { display: "block", minWidth: 320 },
|
|
41
|
+
children: data.map((d, i) => {
|
|
42
|
+
const barH = Math.max(2, Math.round(d.visits / max * height));
|
|
43
|
+
const x = i * (barWidth + 2);
|
|
44
|
+
const y = height - barH;
|
|
45
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { children: [
|
|
46
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
47
|
+
"rect",
|
|
48
|
+
{
|
|
49
|
+
x,
|
|
50
|
+
y,
|
|
51
|
+
width: barWidth,
|
|
52
|
+
height: barH,
|
|
53
|
+
rx: 3,
|
|
54
|
+
fill: "var(--ta-blue)",
|
|
55
|
+
opacity: 0.85,
|
|
56
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("title", { children: `${d.date}: ${d.visits} visits` })
|
|
57
|
+
}
|
|
58
|
+
),
|
|
59
|
+
showLabel && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
60
|
+
"text",
|
|
61
|
+
{
|
|
62
|
+
x: x + barWidth / 2,
|
|
63
|
+
y: height + 16,
|
|
64
|
+
textAnchor: "middle",
|
|
65
|
+
fontSize: 9,
|
|
66
|
+
fill: "var(--ta-gray-500)",
|
|
67
|
+
children: [
|
|
68
|
+
d.date.slice(5),
|
|
69
|
+
" "
|
|
70
|
+
]
|
|
71
|
+
}
|
|
72
|
+
)
|
|
73
|
+
] }, d.date);
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
) });
|
|
77
|
+
}
|
|
78
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
79
|
+
0 && (module.exports = {
|
|
80
|
+
VisitsChart
|
|
81
|
+
});
|
|
82
|
+
//# sourceMappingURL=VisitsChart.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/dashboard/ui/components/VisitsChart.tsx"],"sourcesContent":["'use client'\n\ninterface DayData { date: string; visits: number }\n\ninterface VisitsChartProps {\n data: DayData[]\n height?: number\n}\n\nexport function VisitsChart({ data, height = 160 }: VisitsChartProps) {\n if (!data.length) {\n return <div className=\"ta-empty\"><p>No visit data yet.</p></div>\n }\n\n const max = Math.max(...data.map(d => d.visits), 1)\n const barWidth = Math.max(4, Math.floor(560 / data.length) - 2)\n const showLabel = data.length <= 14\n\n return (\n <div style={{ overflowX: 'auto' }}>\n <svg\n width=\"100%\"\n viewBox={`0 0 ${Math.max(data.length * (barWidth + 2), 560)} ${height + 40}`}\n style={{ display: 'block', minWidth: 320 }}\n >\n {data.map((d, i) => {\n const barH = Math.max(2, Math.round((d.visits / max) * height))\n const x = i * (barWidth + 2)\n const y = height - barH\n return (\n <g key={d.date}>\n <rect\n x={x} y={y}\n width={barWidth} height={barH}\n rx={3}\n fill=\"var(--ta-blue)\"\n opacity={0.85}\n >\n <title>{`${d.date}: ${d.visits} visits`}</title>\n </rect>\n {showLabel && (\n <text\n x={x + barWidth / 2} y={height + 16}\n textAnchor=\"middle\"\n fontSize={9}\n fill=\"var(--ta-gray-500)\"\n >\n {d.date.slice(5)} {/* MM-DD */}\n </text>\n )}\n </g>\n )\n })}\n </svg>\n </div>\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAWqC;AAF9B,SAAS,YAAY,EAAE,MAAM,SAAS,IAAI,GAAqB;AACpE,MAAI,CAAC,KAAK,QAAQ;AAChB,WAAO,4CAAC,SAAI,WAAU,YAAW,sDAAC,OAAE,gCAAkB,GAAI;AAAA,EAC5D;AAEA,QAAM,MAAM,KAAK,IAAI,GAAG,KAAK,IAAI,OAAK,EAAE,MAAM,GAAG,CAAC;AAClD,QAAM,WAAW,KAAK,IAAI,GAAG,KAAK,MAAM,MAAM,KAAK,MAAM,IAAI,CAAC;AAC9D,QAAM,YAAY,KAAK,UAAU;AAEjC,SACE,4CAAC,SAAI,OAAO,EAAE,WAAW,OAAO,GAC9B;AAAA,IAAC;AAAA;AAAA,MACC,OAAM;AAAA,MACN,SAAS,OAAO,KAAK,IAAI,KAAK,UAAU,WAAW,IAAI,GAAG,CAAC,IAAI,SAAS,EAAE;AAAA,MAC1E,OAAO,EAAE,SAAS,SAAS,UAAU,IAAI;AAAA,MAExC,eAAK,IAAI,CAAC,GAAG,MAAM;AAClB,cAAM,OAAO,KAAK,IAAI,GAAG,KAAK,MAAO,EAAE,SAAS,MAAO,MAAM,CAAC;AAC9D,cAAM,IAAI,KAAK,WAAW;AAC1B,cAAM,IAAI,SAAS;AACnB,eACE,6CAAC,OACC;AAAA;AAAA,YAAC;AAAA;AAAA,cACC;AAAA,cAAM;AAAA,cACN,OAAO;AAAA,cAAU,QAAQ;AAAA,cACzB,IAAI;AAAA,cACJ,MAAK;AAAA,cACL,SAAS;AAAA,cAET,sDAAC,WAAO,aAAG,EAAE,IAAI,KAAK,EAAE,MAAM,WAAU;AAAA;AAAA,UAC1C;AAAA,UACC,aACC;AAAA,YAAC;AAAA;AAAA,cACC,GAAG,IAAI,WAAW;AAAA,cAAG,GAAG,SAAS;AAAA,cACjC,YAAW;AAAA,cACX,UAAU;AAAA,cACV,MAAK;AAAA,cAEJ;AAAA,kBAAE,KAAK,MAAM,CAAC;AAAA,gBAAE;AAAA;AAAA;AAAA,UACnB;AAAA,aAlBI,EAAE,IAoBV;AAAA,MAEJ,CAAC;AAAA;AAAA,EACH,GACF;AAEJ;","names":[]}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/dashboard/ui/components/VisitsChart.tsx
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
function VisitsChart({ data, height = 160 }) {
|
|
6
|
+
if (!data.length) {
|
|
7
|
+
return /* @__PURE__ */ jsx("div", { className: "ta-empty", children: /* @__PURE__ */ jsx("p", { children: "No visit data yet." }) });
|
|
8
|
+
}
|
|
9
|
+
const max = Math.max(...data.map((d) => d.visits), 1);
|
|
10
|
+
const barWidth = Math.max(4, Math.floor(560 / data.length) - 2);
|
|
11
|
+
const showLabel = data.length <= 14;
|
|
12
|
+
return /* @__PURE__ */ jsx("div", { style: { overflowX: "auto" }, children: /* @__PURE__ */ jsx(
|
|
13
|
+
"svg",
|
|
14
|
+
{
|
|
15
|
+
width: "100%",
|
|
16
|
+
viewBox: `0 0 ${Math.max(data.length * (barWidth + 2), 560)} ${height + 40}`,
|
|
17
|
+
style: { display: "block", minWidth: 320 },
|
|
18
|
+
children: data.map((d, i) => {
|
|
19
|
+
const barH = Math.max(2, Math.round(d.visits / max * height));
|
|
20
|
+
const x = i * (barWidth + 2);
|
|
21
|
+
const y = height - barH;
|
|
22
|
+
return /* @__PURE__ */ jsxs("g", { children: [
|
|
23
|
+
/* @__PURE__ */ jsx(
|
|
24
|
+
"rect",
|
|
25
|
+
{
|
|
26
|
+
x,
|
|
27
|
+
y,
|
|
28
|
+
width: barWidth,
|
|
29
|
+
height: barH,
|
|
30
|
+
rx: 3,
|
|
31
|
+
fill: "var(--ta-blue)",
|
|
32
|
+
opacity: 0.85,
|
|
33
|
+
children: /* @__PURE__ */ jsx("title", { children: `${d.date}: ${d.visits} visits` })
|
|
34
|
+
}
|
|
35
|
+
),
|
|
36
|
+
showLabel && /* @__PURE__ */ jsxs(
|
|
37
|
+
"text",
|
|
38
|
+
{
|
|
39
|
+
x: x + barWidth / 2,
|
|
40
|
+
y: height + 16,
|
|
41
|
+
textAnchor: "middle",
|
|
42
|
+
fontSize: 9,
|
|
43
|
+
fill: "var(--ta-gray-500)",
|
|
44
|
+
children: [
|
|
45
|
+
d.date.slice(5),
|
|
46
|
+
" "
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
)
|
|
50
|
+
] }, d.date);
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
) });
|
|
54
|
+
}
|
|
55
|
+
export {
|
|
56
|
+
VisitsChart
|
|
57
|
+
};
|
|
58
|
+
//# sourceMappingURL=VisitsChart.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/dashboard/ui/components/VisitsChart.tsx"],"sourcesContent":["'use client'\n\ninterface DayData { date: string; visits: number }\n\ninterface VisitsChartProps {\n data: DayData[]\n height?: number\n}\n\nexport function VisitsChart({ data, height = 160 }: VisitsChartProps) {\n if (!data.length) {\n return <div className=\"ta-empty\"><p>No visit data yet.</p></div>\n }\n\n const max = Math.max(...data.map(d => d.visits), 1)\n const barWidth = Math.max(4, Math.floor(560 / data.length) - 2)\n const showLabel = data.length <= 14\n\n return (\n <div style={{ overflowX: 'auto' }}>\n <svg\n width=\"100%\"\n viewBox={`0 0 ${Math.max(data.length * (barWidth + 2), 560)} ${height + 40}`}\n style={{ display: 'block', minWidth: 320 }}\n >\n {data.map((d, i) => {\n const barH = Math.max(2, Math.round((d.visits / max) * height))\n const x = i * (barWidth + 2)\n const y = height - barH\n return (\n <g key={d.date}>\n <rect\n x={x} y={y}\n width={barWidth} height={barH}\n rx={3}\n fill=\"var(--ta-blue)\"\n opacity={0.85}\n >\n <title>{`${d.date}: ${d.visits} visits`}</title>\n </rect>\n {showLabel && (\n <text\n x={x + barWidth / 2} y={height + 16}\n textAnchor=\"middle\"\n fontSize={9}\n fill=\"var(--ta-gray-500)\"\n >\n {d.date.slice(5)} {/* MM-DD */}\n </text>\n )}\n </g>\n )\n })}\n </svg>\n </div>\n )\n}\n"],"mappings":";;;AAWqC,cA8BrB,YA9BqB;AAF9B,SAAS,YAAY,EAAE,MAAM,SAAS,IAAI,GAAqB;AACpE,MAAI,CAAC,KAAK,QAAQ;AAChB,WAAO,oBAAC,SAAI,WAAU,YAAW,8BAAC,OAAE,gCAAkB,GAAI;AAAA,EAC5D;AAEA,QAAM,MAAM,KAAK,IAAI,GAAG,KAAK,IAAI,OAAK,EAAE,MAAM,GAAG,CAAC;AAClD,QAAM,WAAW,KAAK,IAAI,GAAG,KAAK,MAAM,MAAM,KAAK,MAAM,IAAI,CAAC;AAC9D,QAAM,YAAY,KAAK,UAAU;AAEjC,SACE,oBAAC,SAAI,OAAO,EAAE,WAAW,OAAO,GAC9B;AAAA,IAAC;AAAA;AAAA,MACC,OAAM;AAAA,MACN,SAAS,OAAO,KAAK,IAAI,KAAK,UAAU,WAAW,IAAI,GAAG,CAAC,IAAI,SAAS,EAAE;AAAA,MAC1E,OAAO,EAAE,SAAS,SAAS,UAAU,IAAI;AAAA,MAExC,eAAK,IAAI,CAAC,GAAG,MAAM;AAClB,cAAM,OAAO,KAAK,IAAI,GAAG,KAAK,MAAO,EAAE,SAAS,MAAO,MAAM,CAAC;AAC9D,cAAM,IAAI,KAAK,WAAW;AAC1B,cAAM,IAAI,SAAS;AACnB,eACE,qBAAC,OACC;AAAA;AAAA,YAAC;AAAA;AAAA,cACC;AAAA,cAAM;AAAA,cACN,OAAO;AAAA,cAAU,QAAQ;AAAA,cACzB,IAAI;AAAA,cACJ,MAAK;AAAA,cACL,SAAS;AAAA,cAET,8BAAC,WAAO,aAAG,EAAE,IAAI,KAAK,EAAE,MAAM,WAAU;AAAA;AAAA,UAC1C;AAAA,UACC,aACC;AAAA,YAAC;AAAA;AAAA,cACC,GAAG,IAAI,WAAW;AAAA,cAAG,GAAG,SAAS;AAAA,cACjC,YAAW;AAAA,cACX,UAAU;AAAA,cACV,MAAK;AAAA,cAEJ;AAAA,kBAAE,KAAK,MAAM,CAAC;AAAA,gBAAE;AAAA;AAAA;AAAA,UACnB;AAAA,aAlBI,EAAE,IAoBV;AAAA,MAEJ,CAAC;AAAA;AAAA,EACH,GACF;AAEJ;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "third-audience-mdx",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"description": "Serve AI-optimized Markdown to LLM crawlers from MDX content sites. Track bot visits, citations, and AI discoverability.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
"third-audience": "./dist/cli/index.js"
|
|
113
113
|
},
|
|
114
114
|
"scripts": {
|
|
115
|
-
"build": "tsup
|
|
115
|
+
"build": "tsup",
|
|
116
116
|
"dev": "tsup src/index.ts --watch",
|
|
117
117
|
"typecheck": "tsc --noEmit",
|
|
118
118
|
"test": "vitest run",
|