@apexkit/cli 1.0.3 → 1.0.5
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 +0 -0
- package/README.md +0 -0
- package/package.json +1 -1
- package/src/index.js +361 -33
package/LICENSE
CHANGED
|
File without changes
|
package/README.md
CHANGED
|
File without changes
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -138,10 +138,163 @@ const TRIGGER_DEFINITIONS = [
|
|
|
138
138
|
{ id: "after_ai_run", name: "after_ai_run", desc: "Runs after AI Prompt execution." },
|
|
139
139
|
{ id: "on_vectorization_start", name: "on_vectorization_start", desc: "Fires when revectorization job begins." },
|
|
140
140
|
{ id: "cron", name: "cron (Scheduled Job)", desc: "Executed periodically according to Cron expression settings." },
|
|
141
|
-
{ id: "graphql", name: "graphql (Custom Resolver)", desc: "Dynamic Query/Mutation field resolver for GraphQL schema." }
|
|
141
|
+
{ id: "graphql", name: "graphql (Custom Resolver)", desc: "Dynamic Query/Mutation field resolver for GraphQL schema." },
|
|
142
|
+
{ id: "before_realtime_connection_established", name: "before_realtime_connection_established", desc: "Runs before an incoming SSE or WebSocket connection is accepted. Can block connections." },
|
|
143
|
+
{ id: "before_realtime_data_pushed", name: "before_realtime_data_pushed", desc: "Intercepts and mutates real-time event payloads right before sending to a client." },
|
|
144
|
+
{ id: "after_realtime_connection_desconnected", name: "after_realtime_connection_desconnected", desc: "Runs after an SSE or WebSocket client disconnects or connection is dropped." },
|
|
142
145
|
];
|
|
143
146
|
|
|
144
|
-
// --- 4.
|
|
147
|
+
// --- 4. AUTOMATED REMOTE TYPE RESOLUTION & SCANNER ---
|
|
148
|
+
function scanWorkspaceForRemoteUrls() {
|
|
149
|
+
const urls = new Set();
|
|
150
|
+
const dirs = ["./webhooks", "./modules", "./templates"];
|
|
151
|
+
const urlRegex =
|
|
152
|
+
/(?:import|export)\s+(?:[\s\S]*?from\s+)?["'](https?:\/\/[^"']+)["']|import\s*\(\s*["'](https?:\/\/[^"']+)["']\s*\)|@jsxImportSource\s+(https?:\/\/[^\s*]+)/g;
|
|
153
|
+
|
|
154
|
+
dirs.forEach((dir) => {
|
|
155
|
+
if (!fs.existsSync(dir)) return;
|
|
156
|
+
const files = fs.readdirSync(dir, { recursive: true });
|
|
157
|
+
files.forEach((file) => {
|
|
158
|
+
const fullPath = path.join(dir, file);
|
|
159
|
+
if (fs.statSync(fullPath).isFile()) {
|
|
160
|
+
const ext = path.extname(fullPath);
|
|
161
|
+
if ([".js", ".ts", ".jsx", ".tsx", ".html"].includes(ext)) {
|
|
162
|
+
try {
|
|
163
|
+
const content = fs.readFileSync(fullPath, "utf-8");
|
|
164
|
+
let match;
|
|
165
|
+
while ((match = urlRegex.exec(content)) !== null) {
|
|
166
|
+
const url = match[1] || match[2] || match[3];
|
|
167
|
+
if (url) urls.add(url.trim());
|
|
168
|
+
}
|
|
169
|
+
} catch (e) {}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
return Array.from(urls);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async function resolveRemoteTypeUrl(importUrl) {
|
|
179
|
+
try {
|
|
180
|
+
const res = await fetch(importUrl, {
|
|
181
|
+
method: "GET",
|
|
182
|
+
headers: { "User-Agent": "ApexKit-CLI/1.0" },
|
|
183
|
+
redirect: "follow",
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
const dtsHeader = res.headers.get("x-typescript-types");
|
|
187
|
+
if (dtsHeader) {
|
|
188
|
+
return dtsHeader.startsWith("http") ? dtsHeader : new URL(dtsHeader, importUrl).href;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (importUrl.endsWith(".d.ts")) {
|
|
192
|
+
return importUrl;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (importUrl.includes("esm.sh")) {
|
|
196
|
+
const dtsParamUrl = importUrl.includes("?") ? `${importUrl}&dts` : `${importUrl}?dts`;
|
|
197
|
+
const paramRes = await fetch(dtsParamUrl, { method: "HEAD", redirect: "follow" });
|
|
198
|
+
const paramHeader = paramRes.headers.get("x-typescript-types");
|
|
199
|
+
if (paramHeader) {
|
|
200
|
+
return paramHeader.startsWith("http") ? paramHeader : new URL(paramHeader, importUrl).href;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
} catch (e) {}
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Recursively fetches a .d.ts file and all relative imports/exports inside it
|
|
209
|
+
*/
|
|
210
|
+
async function downloadDtsTree(currentDtsUrl, baseDir, relativeSubpath = "index.d.ts", visited = new Set()) {
|
|
211
|
+
if (visited.has(currentDtsUrl)) return;
|
|
212
|
+
visited.add(currentDtsUrl);
|
|
213
|
+
|
|
214
|
+
const localFilePath = path.join(baseDir, relativeSubpath);
|
|
215
|
+
fs.mkdirSync(path.dirname(localFilePath), { recursive: true });
|
|
216
|
+
|
|
217
|
+
try {
|
|
218
|
+
const res = await fetch(currentDtsUrl, {
|
|
219
|
+
headers: { "User-Agent": "ApexKit-CLI/1.0" },
|
|
220
|
+
redirect: "follow",
|
|
221
|
+
});
|
|
222
|
+
if (!res.ok) return;
|
|
223
|
+
|
|
224
|
+
let content = await res.text();
|
|
225
|
+
|
|
226
|
+
// Regex to locate relative imports or exports like:
|
|
227
|
+
// import { Hono } from './hono.d.ts';
|
|
228
|
+
// export * from './types.d.ts';
|
|
229
|
+
const relImportRegex = /(?:import|export)\s+[\s\S]*?from\s+["'](\.[^"']+)["']/g;
|
|
230
|
+
const relMatches = [];
|
|
231
|
+
let match;
|
|
232
|
+
while ((match = relImportRegex.exec(content)) !== null) {
|
|
233
|
+
relMatches.push(match[1]);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Write file locally
|
|
237
|
+
fs.writeFileSync(localFilePath, content);
|
|
238
|
+
|
|
239
|
+
// Recursively pull all dependencies relative to current URL
|
|
240
|
+
for (const relPath of relMatches) {
|
|
241
|
+
// Normalize extension: ensure it resolves to .d.ts on the CDN
|
|
242
|
+
let targetRel = relPath;
|
|
243
|
+
if (!targetRel.endsWith(".d.ts") && !targetRel.endsWith(".ts")) {
|
|
244
|
+
targetRel = `${targetRel}.d.ts`;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const nextUrl = new URL(targetRel, currentDtsUrl).href;
|
|
248
|
+
const nextLocalSubpath = path.normalize(
|
|
249
|
+
path.join(path.dirname(relativeSubpath), targetRel.replace(/^\.\//, ""))
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
await downloadDtsTree(nextUrl, baseDir, nextLocalSubpath, visited);
|
|
253
|
+
}
|
|
254
|
+
} catch (err) {
|
|
255
|
+
console.warn(` ⚠️ Failed to recursively pull: ${currentDtsUrl} -> ${err.message}`);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
async function pullRemoteModuleTypes(urls) {
|
|
260
|
+
const typesDir = path.join(".apexkit", "types");
|
|
261
|
+
fs.mkdirSync(typesDir, { recursive: true });
|
|
262
|
+
|
|
263
|
+
const resolvedPaths = {};
|
|
264
|
+
|
|
265
|
+
for (const rawUrl of urls) {
|
|
266
|
+
try {
|
|
267
|
+
// Use directory container for each package to support relative child imports cleanly
|
|
268
|
+
const urlFolder = rawUrl.replace(/[^a-zA-Z0-9_.-]/g, "_");
|
|
269
|
+
const packageDir = path.join(typesDir, urlFolder);
|
|
270
|
+
const rootDtsFile = path.join(packageDir, "index.d.ts");
|
|
271
|
+
|
|
272
|
+
if (fs.existsSync(rootDtsFile) && fs.statSync(rootDtsFile).size > 0) {
|
|
273
|
+
resolvedPaths[rawUrl] = `./${rootDtsFile.replace(/\\/g, "/")}`;
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
console.log(` 🌐 [Types]: Resolving remote type tree for ${rawUrl}...`);
|
|
278
|
+
const dtsUrl = await resolveRemoteTypeUrl(rawUrl);
|
|
279
|
+
|
|
280
|
+
if (dtsUrl) {
|
|
281
|
+
fs.mkdirSync(packageDir, { recursive: true });
|
|
282
|
+
await downloadDtsTree(dtsUrl, packageDir, "index.d.ts");
|
|
283
|
+
|
|
284
|
+
if (fs.existsSync(rootDtsFile)) {
|
|
285
|
+
resolvedPaths[rawUrl] = `./${rootDtsFile.replace(/\\/g, "/")}`;
|
|
286
|
+
console.log(` 📦 [Types]: Successfully cached dependency tree in ${packageDir}`);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
} catch (err) {
|
|
290
|
+
console.warn(` ⚠️ Could not pull remote types for ${rawUrl}: ${err.message}`);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return resolvedPaths;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// --- 5. ACCURATE WORKSPACE TYPES & CONFIG GENERATOR ---
|
|
145
298
|
async function generateWorkspaceFiles() {
|
|
146
299
|
console.log(" ⏳ Fetching collection schemas from DB...");
|
|
147
300
|
let collectionsData = [];
|
|
@@ -332,6 +485,13 @@ export interface FileMetadata {
|
|
|
332
485
|
visibility?: ScriptVisibility;
|
|
333
486
|
}
|
|
334
487
|
|
|
488
|
+
export type ApexKitDb = typeof $db;
|
|
489
|
+
export type ApexKitHttp = typeof $http;
|
|
490
|
+
export type ApexKitFiles = typeof $files;
|
|
491
|
+
export type ApexKitFs = typeof $fs;
|
|
492
|
+
export type ApexKitCache = typeof $cache;
|
|
493
|
+
export type ApexKitEnv = typeof $env;
|
|
494
|
+
|
|
335
495
|
export interface AuthContext {
|
|
336
496
|
id: number | string;
|
|
337
497
|
email: string;
|
|
@@ -369,6 +529,19 @@ export interface VoidHookEvent {
|
|
|
369
529
|
}
|
|
370
530
|
|
|
371
531
|
declare global {
|
|
532
|
+
namespace JSX {
|
|
533
|
+
interface Element extends Promise<string>, String {
|
|
534
|
+
[key: string]: any;
|
|
535
|
+
toString(): string;
|
|
536
|
+
}
|
|
537
|
+
interface IntrinsicElements {
|
|
538
|
+
[elemName: string]: any;
|
|
539
|
+
}
|
|
540
|
+
interface ElementChildrenAttribute {
|
|
541
|
+
children: {};
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
372
545
|
const __fileMetadata__: FileMetadata;
|
|
373
546
|
|
|
374
547
|
/** ApexKit Database Client */
|
|
@@ -597,50 +770,122 @@ declare global {
|
|
|
597
770
|
init?: { method?: string; headers?: any; body?: any; redirect?: "follow" | "manual" | "error" }
|
|
598
771
|
): Promise<Response>;
|
|
599
772
|
}
|
|
773
|
+
|
|
774
|
+
// --- RUNTIME MODULE DEFINITIONS ---
|
|
775
|
+
declare module "react/jsx-runtime" {
|
|
776
|
+
export const jsx: any;
|
|
777
|
+
export const jsxs: any;
|
|
778
|
+
export const Fragment: any;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
declare module "react/jsx-dev-runtime" {
|
|
782
|
+
export const jsxDEV: any;
|
|
783
|
+
export const Fragment: any;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
declare module "hono/jsx/jsx-runtime" {
|
|
787
|
+
export const jsx: any;
|
|
788
|
+
export const jsxs: any;
|
|
789
|
+
export const Fragment: any;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
declare module "hono/jsx" {
|
|
793
|
+
export const jsx: any;
|
|
794
|
+
export const jsxs: any;
|
|
795
|
+
export const Fragment: any;
|
|
796
|
+
export type FC<T = any> = (props: T) => any;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
// --- HONO CONTEXT OVERLOADS ---
|
|
800
|
+
declare module "hono" {
|
|
801
|
+
interface Context {
|
|
802
|
+
html(html: string | Promise<string> | JSX.Element | any, status?: any, headers?: any): Response | Promise<Response>;
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
// --- REMOTE URL MODULE DECLARATIONS & FALLBACKS ---
|
|
807
|
+
declare module "https://*" {
|
|
808
|
+
const content: any;
|
|
809
|
+
export default content;
|
|
810
|
+
export * from "https://*";
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
declare module "http://*" {
|
|
814
|
+
const content: any;
|
|
815
|
+
export default content;
|
|
816
|
+
export * from "http://*";
|
|
817
|
+
}
|
|
818
|
+
|
|
600
819
|
export {};
|
|
601
820
|
`;
|
|
602
821
|
fs.writeFileSync("apexkit.d.ts", types.trim());
|
|
603
|
-
console.log(" 📄 Generated apexkit.d.ts with Accurate Scaled &
|
|
822
|
+
console.log(" 📄 Generated apexkit.d.ts with Accurate Scaled Typings & Universal JSX");
|
|
823
|
+
|
|
824
|
+
// Dynamically scan all files and auto-pull remote module types
|
|
825
|
+
const remoteUrls = scanWorkspaceForRemoteUrls();
|
|
826
|
+
const resolvedRemoteTypes = await pullRemoteModuleTypes(remoteUrls);
|
|
827
|
+
|
|
828
|
+
const dynamicPaths = {
|
|
829
|
+
"@/custom/*": ["./modules/custom/*"],
|
|
830
|
+
"@/esm/*": ["./modules/esm/*"],
|
|
831
|
+
};
|
|
832
|
+
|
|
833
|
+
// Map each pulled remote URL to its local cached definition
|
|
834
|
+
for (const [remoteUrl, localPath] of Object.entries(resolvedRemoteTypes)) {
|
|
835
|
+
dynamicPaths[remoteUrl] = [localPath];
|
|
836
|
+
dynamicPaths[`${remoteUrl}/*`] = [`${localPath}/*`];
|
|
837
|
+
}
|
|
604
838
|
|
|
605
839
|
const jsconfig = {
|
|
606
840
|
compilerOptions: {
|
|
607
841
|
target: "ES2022",
|
|
608
842
|
module: "ESNext",
|
|
609
843
|
ignoreDeprecations: "6.0",
|
|
610
|
-
moduleResolution: "
|
|
844
|
+
moduleResolution: "bundler",
|
|
845
|
+
allowImportingTsExtensions: true,
|
|
846
|
+
jsx: "preserve",
|
|
611
847
|
baseUrl: ".",
|
|
612
|
-
paths:
|
|
613
|
-
"@/custom/*": ["./modules/custom/*"],
|
|
614
|
-
"@/esm/*": ["./modules/esm/*"],
|
|
615
|
-
},
|
|
848
|
+
paths: dynamicPaths,
|
|
616
849
|
allowJs: true,
|
|
617
850
|
checkJs: true,
|
|
618
851
|
},
|
|
619
|
-
include: [
|
|
852
|
+
include: [
|
|
853
|
+
"**/*.js",
|
|
854
|
+
"**/*.ts",
|
|
855
|
+
"**/*.jsx",
|
|
856
|
+
"**/*.tsx",
|
|
857
|
+
"**/*.html",
|
|
858
|
+
"apexkit.d.ts",
|
|
859
|
+
".apexkit/types/**/*.d.ts",
|
|
860
|
+
],
|
|
620
861
|
};
|
|
621
862
|
fs.writeFileSync("jsconfig.json", JSON.stringify(jsconfig, null, 2));
|
|
622
|
-
console.log(" 📄 Generated jsconfig.json
|
|
863
|
+
console.log(" 📄 Generated jsconfig.json with Auto-Mapped Remote Types & JSX Support");
|
|
623
864
|
}
|
|
624
865
|
|
|
625
866
|
// --- 5. INITIAL CODE GENERATOR ---
|
|
626
867
|
function getBoilerplateCode(fileType, cleanName, ext, triggerType, targetCollection) {
|
|
868
|
+
const metadataPath =
|
|
869
|
+
fileType === "template"
|
|
870
|
+
? "./templates/"
|
|
871
|
+
: fileType.includes("module")
|
|
872
|
+
? "./modules/custom/"
|
|
873
|
+
: "./webhooks/";
|
|
874
|
+
|
|
875
|
+
const typeImportPath = metadataPath.includes("modules/") ? "../../apexkit" : "../apexkit";
|
|
876
|
+
|
|
627
877
|
const metadata = {
|
|
628
878
|
name: cleanName,
|
|
629
879
|
extension: ext,
|
|
630
880
|
target_collection: targetCollection,
|
|
631
881
|
type: fileType,
|
|
632
|
-
path:
|
|
633
|
-
fileType === "template"
|
|
634
|
-
? "./templates/"
|
|
635
|
-
: fileType.includes("module")
|
|
636
|
-
? "./modules/custom/"
|
|
637
|
-
: "./webhooks/",
|
|
882
|
+
path: metadataPath,
|
|
638
883
|
trigger_type: triggerType,
|
|
639
884
|
active: true,
|
|
640
885
|
visibility: "private",
|
|
641
886
|
};
|
|
642
887
|
|
|
643
|
-
const header = `/** @type {import("
|
|
888
|
+
const header = `/** @type {import("${typeImportPath}").FileMetadata} */\nexport const __fileMetadata__ = ${JSON.stringify(
|
|
644
889
|
metadata,
|
|
645
890
|
null,
|
|
646
891
|
2
|
|
@@ -666,10 +911,11 @@ export function ${cleanName.replace(/-/g, "_")}(data = {}) {
|
|
|
666
911
|
__fileMetadata__ = ${JSON.stringify(metadata, null, 2)}
|
|
667
912
|
-->
|
|
668
913
|
<script server>
|
|
669
|
-
|
|
914
|
+
/** @type {import("../apexkit").ApexKitDb} */
|
|
915
|
+
const db = $db;
|
|
670
916
|
|
|
671
917
|
export default async function (context) {
|
|
672
|
-
const posts = await
|
|
918
|
+
const posts = await db.records.list(\`${targetCollection || "posts"}\`, { limit: 10 }).catch(() => ({ items: [] }));
|
|
673
919
|
return {
|
|
674
920
|
title: \`${cleanName.replace(/-/g, " ").toUpperCase()}\`,
|
|
675
921
|
items: posts.items
|
|
@@ -756,6 +1002,62 @@ export default async function (event) {
|
|
|
756
1002
|
data
|
|
757
1003
|
});
|
|
758
1004
|
}
|
|
1005
|
+
`;
|
|
1006
|
+
|
|
1007
|
+
case "before_realtime_connection_established":
|
|
1008
|
+
return `${header}/**
|
|
1009
|
+
* Hook: before_realtime_connection_established
|
|
1010
|
+
* Intercepts incoming Realtime connections before upgrading/accepting.
|
|
1011
|
+
*
|
|
1012
|
+
* @param {{ trigger: string, data: { ip: string, protocol: 'sse' | 'websocket', headers: Record<string, string> }, auth: import("${typeImportPath}").AuthContext | null }} event
|
|
1013
|
+
*/
|
|
1014
|
+
export default async function (event) {
|
|
1015
|
+
const { ip, protocol, headers } = event.data;
|
|
1016
|
+
|
|
1017
|
+
// Example: Block suspicious user-agents or IPs (throwing an error drops the connection)
|
|
1018
|
+
// if (headers["user-agent"]?.includes("BadBot")) {
|
|
1019
|
+
// throw new Error("Connection denied");
|
|
1020
|
+
// }
|
|
1021
|
+
|
|
1022
|
+
return true;
|
|
1023
|
+
}
|
|
1024
|
+
`;
|
|
1025
|
+
|
|
1026
|
+
case "before_realtime_data_pushed":
|
|
1027
|
+
return `${header}/**
|
|
1028
|
+
* Hook: before_realtime_data_pushed
|
|
1029
|
+
* Intercepts event data just before broadcasting to the connected client.
|
|
1030
|
+
*
|
|
1031
|
+
* @param {{ trigger: string, data: any, auth: import("${typeImportPath}").AuthContext | null }} event
|
|
1032
|
+
* @returns {Promise<any>} Return modified payload, or throw an error to send an empty object {}
|
|
1033
|
+
*/
|
|
1034
|
+
export default async function (event) {
|
|
1035
|
+
const eventPayload = event.data;
|
|
1036
|
+
|
|
1037
|
+
// Example: Redact sensitive information before the client receives it
|
|
1038
|
+
// (NOTE: $realtime.send is blocked inside this hook to avoid infinite loops)
|
|
1039
|
+
// if (eventPayload.data && eventPayload.data.internal_secret) {
|
|
1040
|
+
// delete eventPayload.data.internal_secret;
|
|
1041
|
+
// }
|
|
1042
|
+
|
|
1043
|
+
return eventPayload;
|
|
1044
|
+
}
|
|
1045
|
+
`;
|
|
1046
|
+
|
|
1047
|
+
case "after_realtime_connection_desconnected":
|
|
1048
|
+
return `${header}/**
|
|
1049
|
+
* Hook: after_realtime_connection_desconnected
|
|
1050
|
+
* Fires asynchronously when a client stream/socket disconnects or drops.
|
|
1051
|
+
*
|
|
1052
|
+
* @param {{ trigger: string, data: { ip: string, protocol: 'sse' | 'websocket', headers: Record<string, string> }, auth: import("${typeImportPath}").AuthContext | null }} event
|
|
1053
|
+
*/
|
|
1054
|
+
export default async function (event) {
|
|
1055
|
+
const { ip, protocol } = event.data;
|
|
1056
|
+
|
|
1057
|
+
console.log(\`Client disconnected (\${protocol}) from \${ip}\`);
|
|
1058
|
+
|
|
1059
|
+
return true;
|
|
1060
|
+
}
|
|
759
1061
|
`;
|
|
760
1062
|
|
|
761
1063
|
default:
|
|
@@ -802,10 +1104,12 @@ async function runInitFile() {
|
|
|
802
1104
|
}
|
|
803
1105
|
|
|
804
1106
|
const cleanName = rawName.replace(/[^a-zA-Z0-9_-]/g, "");
|
|
805
|
-
const
|
|
1107
|
+
const allowedScriptExts = ["js", "ts", "jsx", "tsx"];
|
|
1108
|
+
const rawExt =
|
|
806
1109
|
fileType === "template"
|
|
807
1110
|
? "html"
|
|
808
|
-
: ((await rl.question("Extension (js/ts) [js]: ")).trim() || "js");
|
|
1111
|
+
: ((await rl.question("Extension (js/ts/jsx/tsx) [js]: ")).trim().toLowerCase() || "js");
|
|
1112
|
+
const ext = fileType === "template" ? "html" : (allowedScriptExts.includes(rawExt) ? rawExt : "js");
|
|
809
1113
|
|
|
810
1114
|
let triggerType = "manual";
|
|
811
1115
|
let targetCollection = null;
|
|
@@ -816,7 +1120,7 @@ async function runInitFile() {
|
|
|
816
1120
|
console.log(` ${String(i + 1).padStart(2, " ")}) ${t.name}`);
|
|
817
1121
|
});
|
|
818
1122
|
|
|
819
|
-
const trigChoice = parseInt((await rl.question("\nChoice (1-
|
|
1123
|
+
const trigChoice = parseInt((await rl.question("\nChoice (1-45) [1]: ")).trim() || "1", 10);
|
|
820
1124
|
const selectedTrig = TRIGGER_DEFINITIONS[trigChoice - 1] || TRIGGER_DEFINITIONS[0];
|
|
821
1125
|
triggerType = selectedTrig.id;
|
|
822
1126
|
|
|
@@ -918,11 +1222,16 @@ async function runPull() {
|
|
|
918
1222
|
const payload = await scriptRes.json();
|
|
919
1223
|
const scripts = Array.isArray(payload) ? payload : payload.local || [];
|
|
920
1224
|
|
|
1225
|
+
const allowedScriptExts = ["js", "ts", "jsx", "tsx"];
|
|
1226
|
+
|
|
921
1227
|
scripts.forEach((script) => {
|
|
1228
|
+
const remoteExt = script.metadata?.extension?.toLowerCase();
|
|
1229
|
+
const extension = allowedScriptExts.includes(remoteExt) ? remoteExt : "js";
|
|
1230
|
+
|
|
922
1231
|
const meta = {
|
|
923
1232
|
id: script.id,
|
|
924
1233
|
name: script.name,
|
|
925
|
-
extension:
|
|
1234
|
+
extension: extension,
|
|
926
1235
|
target_collection: script.target_collection || null,
|
|
927
1236
|
type: script.metadata?.type || "webhook",
|
|
928
1237
|
path: script.metadata?.path || "./webhooks/",
|
|
@@ -934,7 +1243,9 @@ async function runPull() {
|
|
|
934
1243
|
const targetDir = meta.path.replace(/^\.\//, "");
|
|
935
1244
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
936
1245
|
|
|
937
|
-
const
|
|
1246
|
+
const typeImportPath = meta.path.includes("modules/") ? "../../apexkit" : "../apexkit";
|
|
1247
|
+
|
|
1248
|
+
const formattedContent = `/** @type {import("${typeImportPath}").FileMetadata} */\nexport const __fileMetadata__ = ${JSON.stringify(
|
|
938
1249
|
meta,
|
|
939
1250
|
null,
|
|
940
1251
|
2
|
|
@@ -996,9 +1307,11 @@ async function runPull() {
|
|
|
996
1307
|
console.warn(` ⚠️ AI Actions pull warning: ${e.message}`);
|
|
997
1308
|
}
|
|
998
1309
|
|
|
1310
|
+
// Ensure .apexkit/types is ready and regenerate workspace definitions + remote types
|
|
1311
|
+
fs.mkdirSync(path.join(".apexkit", "types"), { recursive: true });
|
|
999
1312
|
await generateWorkspaceFiles();
|
|
1000
1313
|
|
|
1001
|
-
console.log(`\n✨ Pull complete! Hydrated ${pulledCount} items
|
|
1314
|
+
console.log(`\n✨ Pull complete! Hydrated ${pulledCount} items and resolved remote types.`);
|
|
1002
1315
|
process.exit(0);
|
|
1003
1316
|
}
|
|
1004
1317
|
|
|
@@ -1039,7 +1352,7 @@ function inferFileMetadata(filePath, content) {
|
|
|
1039
1352
|
return content;
|
|
1040
1353
|
}
|
|
1041
1354
|
|
|
1042
|
-
// 3. Scripts / Webhooks / Modules -> JS Export metadata
|
|
1355
|
+
// 3. Scripts / Webhooks / Modules -> JS/TS/JSX/TSX Export metadata
|
|
1043
1356
|
let deducedType = "webhook";
|
|
1044
1357
|
let deducedPath = "./webhooks/";
|
|
1045
1358
|
let deducedTrigger = "manual";
|
|
@@ -1052,7 +1365,7 @@ function inferFileMetadata(filePath, content) {
|
|
|
1052
1365
|
deducedPath = "./modules/esm/";
|
|
1053
1366
|
}
|
|
1054
1367
|
|
|
1055
|
-
const jsMetaRe = /(
|
|
1368
|
+
const jsMetaRe = /(?:\/\*\*[\s\S]*?\*\/\s*)?export\s+const\s+__fileMetadata__\s*=\s*(\{[\s\S]*?\});?/;
|
|
1056
1369
|
const match = content.match(jsMetaRe);
|
|
1057
1370
|
|
|
1058
1371
|
let metaObj = {};
|
|
@@ -1062,9 +1375,14 @@ function inferFileMetadata(filePath, content) {
|
|
|
1062
1375
|
} catch (e) {}
|
|
1063
1376
|
}
|
|
1064
1377
|
|
|
1378
|
+
// Prioritize real file extension if js|ts|jsx|tsx, otherwise force to js
|
|
1379
|
+
const allowedScriptExts = ["js", "ts", "jsx", "tsx"];
|
|
1380
|
+
const fileExt = ext.toLowerCase();
|
|
1381
|
+
const prioritizedExt = allowedScriptExts.includes(fileExt) ? fileExt : "js";
|
|
1382
|
+
|
|
1065
1383
|
const finalMeta = {
|
|
1066
1384
|
name: metaObj.name || baseName,
|
|
1067
|
-
extension:
|
|
1385
|
+
extension: prioritizedExt,
|
|
1068
1386
|
target_collection: metaObj.target_collection || null,
|
|
1069
1387
|
type: metaObj.type || deducedType,
|
|
1070
1388
|
path: metaObj.path || deducedPath,
|
|
@@ -1073,12 +1391,20 @@ function inferFileMetadata(filePath, content) {
|
|
|
1073
1391
|
visibility: metaObj.visibility || "private",
|
|
1074
1392
|
};
|
|
1075
1393
|
|
|
1394
|
+
const typeImportPath = finalMeta.path.includes("modules/") ? "../../apexkit" : "../apexkit";
|
|
1395
|
+
const formattedMetadata = `/** @type {import("${typeImportPath}").FileMetadata} */\nexport const __fileMetadata__ = ${JSON.stringify(
|
|
1396
|
+
finalMeta,
|
|
1397
|
+
null,
|
|
1398
|
+
2
|
|
1399
|
+
)};`;
|
|
1400
|
+
|
|
1076
1401
|
if (!match) {
|
|
1077
|
-
return
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1402
|
+
return `${formattedMetadata}\n\n${content}`;
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
// If metadata extension differed or needs updating, replace with prioritized definition
|
|
1406
|
+
if (metaObj.extension !== prioritizedExt || content.includes('import("../apexkit")') && typeImportPath.includes("../..")) {
|
|
1407
|
+
return content.replace(jsMetaRe, formattedMetadata);
|
|
1082
1408
|
}
|
|
1083
1409
|
|
|
1084
1410
|
return content;
|
|
@@ -1393,6 +1719,8 @@ function startWatcher() {
|
|
|
1393
1719
|
if (
|
|
1394
1720
|
filename.endsWith(".js") ||
|
|
1395
1721
|
filename.endsWith(".ts") ||
|
|
1722
|
+
filename.endsWith(".jsx") ||
|
|
1723
|
+
filename.endsWith(".tsx") ||
|
|
1396
1724
|
filename.endsWith(".html") ||
|
|
1397
1725
|
filename.endsWith(".json")
|
|
1398
1726
|
) {
|