@juggernautlabs/views 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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Juggernaut Labs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
File without changes
package/dist/cli.js ADDED
@@ -0,0 +1,320 @@
1
+ #!/usr/bin/env node
2
+
3
+ // bin/cli.js
4
+ import { cac } from "cac";
5
+
6
+ // src/bundler.js
7
+ import { build } from "vite";
8
+ import react from "@vitejs/plugin-react";
9
+ import { viteSingleFile } from "vite-plugin-singlefile";
10
+ import path3 from "path";
11
+
12
+ // src/uploader.js
13
+ import fs2 from "fs";
14
+ import path2 from "path";
15
+ import JSON52 from "json5";
16
+
17
+ // src/api.js
18
+ import fs from "fs";
19
+ import path from "path";
20
+ import JSON5 from "json5";
21
+ import dotenv from "dotenv";
22
+ var BASE_ROUTE = "https://core.juggernautlabs.ai";
23
+ var buildRoute = (route) => {
24
+ return `${BASE_ROUTE}${route}`;
25
+ };
26
+ var isEnvLoaded = false;
27
+ var loadEnvVariable = (key, defaultValue = "") => {
28
+ if (!isEnvLoaded) {
29
+ const envPath = path.resolve(process.cwd(), ".env");
30
+ if (fs.existsSync(envPath)) {
31
+ dotenv.config({ path: envPath });
32
+ }
33
+ isEnvLoaded = true;
34
+ }
35
+ const value = import.meta.env?.[key] || process.env?.[key];
36
+ return value || defaultValue;
37
+ };
38
+ var executeRequest = async (route, method = "POST", body = null, headers = {}) => {
39
+ const url = buildRoute(route);
40
+ const options = {
41
+ method,
42
+ headers: {
43
+ ...headers,
44
+ authorization: `Bearer ${await loadEnvVariable("JUGGERNAUT_API_KEY")}`,
45
+ "client-key": `${await loadEnvVariable("JUGGERNAUT_CLIENT_ID")}`
46
+ }
47
+ };
48
+ if (body) {
49
+ if (body instanceof FormData) {
50
+ options.body = body;
51
+ } else {
52
+ options.headers["Content-Type"] = "application/json";
53
+ options.body = JSON.stringify(body);
54
+ }
55
+ }
56
+ const response = await fetch(url, options);
57
+ if (!response.ok) {
58
+ const errorText = await response.text();
59
+ throw new Error(`Request failed with status ${response.status}: ${errorText}`);
60
+ }
61
+ return response.json();
62
+ };
63
+
64
+ // src/uploader.js
65
+ import { promises } from "fs";
66
+ var ROUTES = {
67
+ ingest: "/api/resource/ui/template/ingest",
68
+ uploadSolution: "/api/resource/ui/template/upload/solution",
69
+ create: "/api/resource/ui/template"
70
+ };
71
+
72
+ class TemplateUploader {
73
+ static async parseMetadataFile(filePath) {
74
+ if (!fs2.existsSync(filePath)) {
75
+ throw new Error(`Metadata file not found at path: ${filePath}`);
76
+ }
77
+ try {
78
+ const fileContent = await fs2.readFileSync(filePath, "utf-8");
79
+ const metadata = JSON52.parse(fileContent) || {};
80
+ return {
81
+ name: metadata.name || "Untitled Template",
82
+ description: metadata.description || "",
83
+ tags: metadata.tags || [],
84
+ target: {
85
+ role: metadata.target?.role,
86
+ resource: metadata.target?.resource,
87
+ resourceType: metadata.target?.resourceType
88
+ }
89
+ };
90
+ } catch (e) {
91
+ throw new Error("Invalid JSON in metadata file");
92
+ }
93
+ }
94
+ static async findConfigFile() {
95
+ const currentDir = process.cwd();
96
+ const configFilePath = path2.join(currentDir, ".juggernauttemplate");
97
+ if (fs2.existsSync(configFilePath)) {
98
+ return this.parseMetadataFile(configFilePath);
99
+ } else {
100
+ return null;
101
+ }
102
+ }
103
+ static async upload(file, metadataPath = null) {
104
+ let templateDetails = {};
105
+ if (metadataPath) {
106
+ templateDetails = await this.parseMetadataFile(metadataPath);
107
+ }
108
+ return this.uploadTemplate(templateDetails, file);
109
+ }
110
+ static async getUploadURL() {
111
+ const { data } = await executeRequest(ROUTES.uploadSolution, "GET");
112
+ return data;
113
+ }
114
+ static createTemplate(templateDetails) {
115
+ return executeRequest(ROUTES.ingest, "POST", templateDetails);
116
+ }
117
+ static async uploadTemplate(viewTemplateDetails, templatePath) {
118
+ const { name, url } = await this.getUploadURL();
119
+ const fileContent = await promises.readFile(templatePath);
120
+ const uploadSolutionResponse = await fetch(url, {
121
+ method: "PUT",
122
+ headers: {
123
+ "Content-Type": "application/html",
124
+ "x-ms-blob-type": "BlockBlob"
125
+ },
126
+ body: fileContent
127
+ });
128
+ if (uploadSolutionResponse.ok) {
129
+ console.log("Upload successful!");
130
+ } else {
131
+ const errorText = await uploadSolutionResponse.text();
132
+ throw new Error(`Failed to upload: ${uploadSolutionResponse.status} - ${errorText}`);
133
+ }
134
+ return this.createTemplate({
135
+ ...viewTemplateDetails,
136
+ solution: name
137
+ });
138
+ }
139
+ }
140
+
141
+ // src/bundler.js
142
+ import fs3 from "fs";
143
+ async function bundle({ entry, outDir, root, template = {} }) {
144
+ const isScript = /\.(js|ts|jsx|tsx)$/.test(entry);
145
+ let viteConfig = {
146
+ root,
147
+ configFile: false,
148
+ plugins: [
149
+ react(),
150
+ viteSingleFile({
151
+ useRecommendedBuildConfig: true,
152
+ removeViteModuleLoader: true
153
+ }),
154
+ isScript && virtualHtmlPlugin(entry)
155
+ ],
156
+ build: {
157
+ outDir,
158
+ emptyOutDir: true,
159
+ minify: true
160
+ }
161
+ };
162
+ await build(viteConfig);
163
+ const bundlePath = path3.join(outDir, "index.html");
164
+ return TemplateUploader.uploadTemplate(template, bundlePath);
165
+ }
166
+ function virtualHtmlPlugin(entryScriptPath) {
167
+ const virtualHtmlId = "index.html";
168
+ const resolvedHtmlId = "/index.html";
169
+ const virtualEntryId = "virtual-auto-mount.tsx";
170
+ const resolvedEntryId = "\x00virtual-auto-mount.tsx";
171
+ return {
172
+ name: "auto-html-entry",
173
+ resolveId(id) {
174
+ if (id === virtualHtmlId)
175
+ return resolvedHtmlId;
176
+ if (id === virtualEntryId)
177
+ return resolvedEntryId;
178
+ },
179
+ load(id) {
180
+ if (id === resolvedHtmlId) {
181
+ const code = fs3.readFileSync(entryScriptPath, "utf-8");
182
+ const hasRender = code.includes("ReactDOM") || code.includes("createRoot");
183
+ const entrySrc = hasRender ? `/${path3.relative(process.cwd(), entryScriptPath)}` : virtualEntryId;
184
+ return `
185
+ <!DOCTYPE html>
186
+ <html>
187
+ <head>
188
+ <meta charset="utf-8">
189
+ <title>React App</title>
190
+ <style>html,body,#root{height:100%;margin:0;}</style>
191
+ </head>
192
+ <body>
193
+ <div id="root"></div>
194
+ <script type="module" src="${entrySrc}"></script>
195
+ </body>
196
+ </html>
197
+ `;
198
+ }
199
+ if (id === resolvedEntryId) {
200
+ const importPath = entryScriptPath.split(path3.sep).join("/");
201
+ return `
202
+ import React from 'react';
203
+ import ReactDOM from 'react-dom/client';
204
+ import UserApp from '${importPath}';
205
+
206
+ const root = ReactDOM.createRoot(document.getElementById('root'));
207
+ root.render(
208
+ React.createElement(React.StrictMode, null,
209
+ React.createElement(UserApp)
210
+ )
211
+ );
212
+ `;
213
+ }
214
+ },
215
+ configureServer(server) {
216
+ server.middlewares.use((req, res, next) => {
217
+ if (req.url === "/")
218
+ req.url = "/index.html";
219
+ next();
220
+ });
221
+ },
222
+ config() {
223
+ return {
224
+ build: {
225
+ rollupOptions: { input: resolvedHtmlId }
226
+ }
227
+ };
228
+ }
229
+ };
230
+ }
231
+ function getViteConfig({ entry, root, outDir, isDev = false }) {
232
+ const isScript = /\.(js|ts|jsx|tsx)$/.test(entry);
233
+ const plugins = [
234
+ react(),
235
+ !isDev && viteSingleFile({
236
+ useRecommendedBuildConfig: true,
237
+ removeViteModuleLoader: true
238
+ }),
239
+ isScript && virtualHtmlPlugin(entry)
240
+ ].filter(Boolean);
241
+ return {
242
+ root,
243
+ configFile: false,
244
+ plugins,
245
+ server: {
246
+ fs: {
247
+ allow: [root, process.cwd()]
248
+ }
249
+ },
250
+ build: {
251
+ outDir,
252
+ emptyOutDir: true,
253
+ minify: !isDev,
254
+ assetsInlineLimit: 1e8,
255
+ rollupOptions: {
256
+ output: {
257
+ inlineDynamicImports: !isDev
258
+ }
259
+ }
260
+ }
261
+ };
262
+ }
263
+
264
+ // bin/cli.js
265
+ import path4 from "path";
266
+ import { createServer } from "vite";
267
+ var cli = cac("templatebundler");
268
+ cli.command("<entry>", "Bundle the React app into a single file").option("-o, --output <path>", "Output directory", { default: "dist" }).option("-c, --config <path>", "Input config file (.juggernauttemplate) or a .json file").action(async (entry, options) => {
269
+ try {
270
+ const rootDir = process.cwd();
271
+ const absoluteEntry = path4.resolve(rootDir, entry);
272
+ const absoluteOutput = path4.resolve(rootDir, options.output);
273
+ const configPath = options.config ? path4.resolve(rootDir, options.config) : null;
274
+ console.log(`\uD83D\uDCE6 Bundling ${entry}...`);
275
+ const template = configPath ? await TemplateUploader.parseMetadataFile(configPath) : await TemplateUploader.findConfigFile();
276
+ if (template)
277
+ console.log(`\uD83D\uDCDD Using template config: ${configPath || ".juggernauttemplate"}`);
278
+ else
279
+ throw new Error("No template configuration provided and no .juggernauttemplate file found.");
280
+ await bundle({
281
+ entry: absoluteEntry,
282
+ outDir: absoluteOutput,
283
+ root: rootDir,
284
+ template
285
+ });
286
+ console.log(`✅ Done! Output saved to ${options.output}`);
287
+ } catch (e) {
288
+ console.error("❌ Build failed:", e);
289
+ process.exit(1);
290
+ }
291
+ });
292
+ cli.command("dev <entry>", "Start a dev server with HMR").option("-p, --port <port>", "Port to listen on", { default: 5173 }).action(async (entry, options) => {
293
+ try {
294
+ const rootDir = process.cwd();
295
+ const absoluteEntry = path4.resolve(rootDir, entry);
296
+ console.log(`⚡ Starting Dev Server for ${entry}...`);
297
+ const viteConfig = getViteConfig({
298
+ entry: absoluteEntry,
299
+ root: rootDir,
300
+ outDir: "dist",
301
+ isDev: true
302
+ });
303
+ const server = await createServer({
304
+ ...viteConfig,
305
+ server: {
306
+ port: options.port,
307
+ host: true
308
+ }
309
+ });
310
+ await server.listen();
311
+ server.printUrls();
312
+ console.log(`
313
+ \uD83E\uDDEA Demo Data: ensure you have a .demo.json in your root to mock server data.`);
314
+ } catch (e) {
315
+ console.error("❌ Dev server failed:", e);
316
+ process.exit(1);
317
+ }
318
+ });
319
+ cli.help();
320
+ cli.parse();
package/dist/index.js ADDED
@@ -0,0 +1,308 @@
1
+ // src/hooks.jsx
2
+ import { useCallback, useEffect, useRef, useState } from "react";
3
+
4
+ // src/context.js
5
+ var STORE_KEY = Symbol("ContextStore");
6
+
7
+ class ContextManager {
8
+ static [STORE_KEY] = {
9
+ job: null,
10
+ record: null,
11
+ data: null,
12
+ plugin: null,
13
+ process: null,
14
+ actions: [],
15
+ system: null
16
+ };
17
+ }
18
+
19
+ // src/hooks.jsx
20
+ var IS_DEV_MODE = import.meta.env ? import.meta.env.DEV : process?.env.NODE_ENV === "development";
21
+ var DEMO_FILE_PATH = "/.demo.json";
22
+ var subscribers = new Set;
23
+ var isListening = false;
24
+ var handleContextWindowEvents = (event) => {
25
+ const { type, payload } = event.data || {};
26
+ if (!type || !payload)
27
+ return;
28
+ const { resourceType, resource } = payload;
29
+ switch (type) {
30
+ case "juggernaut:context:request":
31
+ if (ContextManager[STORE_KEY] && ContextManager[STORE_KEY].hasOwnProperty(resourceType)) {
32
+ const currentResource = ContextManager[STORE_KEY][resourceType];
33
+ window.parent.postMessage({
34
+ type: "juggernaut:context:response",
35
+ payload: { resourceType, resource: currentResource }
36
+ }, "*");
37
+ }
38
+ break;
39
+ case "juggernaut:context:update":
40
+ if (ContextManager[STORE_KEY]) {
41
+ ContextManager[STORE_KEY][resourceType] = resource;
42
+ subscribers.forEach((callback) => callback(resourceType, resource));
43
+ }
44
+ break;
45
+ default:
46
+ break;
47
+ }
48
+ };
49
+ var listenForJuggernautPlatformEvents = () => {
50
+ if (typeof window === "undefined")
51
+ return;
52
+ if (!window.parent && !IS_DEV_MODE)
53
+ return;
54
+ if (!isListening) {
55
+ window.addEventListener("message", handleContextWindowEvents);
56
+ isListening = true;
57
+ }
58
+ };
59
+ var useJuggernautResource = (resourceType) => {
60
+ const [resource, setResource] = useState(() => {
61
+ if (ContextManager[STORE_KEY] && ContextManager[STORE_KEY][resourceType]) {
62
+ return ContextManager[STORE_KEY][resourceType];
63
+ }
64
+ return null;
65
+ });
66
+ useEffect(() => {
67
+ let isMounted = true;
68
+ const tryLoadDemoData = async () => {
69
+ try {
70
+ const response = await fetch(DEMO_FILE_PATH);
71
+ if (!response.ok)
72
+ throw new Error("No demo file");
73
+ const demoData = await response.json();
74
+ if (demoData && demoData[resourceType] && isMounted) {
75
+ console.log(`[Juggernaut SDK] Loaded DEMO data for: ${resourceType}`);
76
+ if (ContextManager[STORE_KEY]) {
77
+ ContextManager[STORE_KEY][resourceType] = demoData[resourceType];
78
+ }
79
+ setResource(demoData[resourceType]);
80
+ return true;
81
+ }
82
+ } catch (err) {
83
+ console.warn("[Juggernaut SDK] Dev mode active, but no valid .demo.json found.");
84
+ }
85
+ return false;
86
+ };
87
+ const initialize = async () => {
88
+ if (IS_DEV_MODE) {
89
+ const loadedFromDemo = await tryLoadDemoData();
90
+ if (loadedFromDemo)
91
+ return;
92
+ }
93
+ listenForJuggernautPlatformEvents();
94
+ const handleUpdate = (type, data) => {
95
+ if (isMounted && type === resourceType) {
96
+ setResource(data);
97
+ }
98
+ };
99
+ subscribers.add(handleUpdate);
100
+ if (!resource && window.parent) {
101
+ window.parent.postMessage({
102
+ type: "juggernaut:context:request",
103
+ payload: { resourceType }
104
+ }, "*");
105
+ }
106
+ return () => {
107
+ subscribers.delete(handleUpdate);
108
+ };
109
+ };
110
+ const cleanupFnPromise = initialize();
111
+ return () => {
112
+ isMounted = false;
113
+ cleanupFnPromise.then((cleanup) => cleanup && cleanup());
114
+ };
115
+ }, [resourceType]);
116
+ return resource;
117
+ };
118
+ var sendJuggernautActionEvent = (actionType, payload) => {
119
+ if (typeof window === "undefined")
120
+ return;
121
+ if (IS_DEV_MODE) {
122
+ console.log(`[Juggernaut Action] ${actionType}`, payload);
123
+ }
124
+ if (!window.parent)
125
+ return;
126
+ window.parent.postMessage({
127
+ type: actionType,
128
+ payload
129
+ }, "*");
130
+ };
131
+ var useJuggernautJob = () => useJuggernautResource("job");
132
+ var useJuggernautRecord = () => useJuggernautResource("record");
133
+ var useJuggernautData = () => useJuggernautResource("data");
134
+ var useJuggernautPlugin = () => useJuggernautResource("plugin");
135
+ var useJuggernautProcess = () => useJuggernautResource("process");
136
+ var useJuggernautActions = () => useJuggernautResource("actions");
137
+ var useJuggernautSystem = () => useJuggernautResource("system");
138
+ var FILE_EVENTS = {
139
+ GET_FILE: "juggernaut:file:get",
140
+ GET_JOB_FILE: "juggernaut:job:file:get",
141
+ UPLOAD_FILE: "juggernaut:file:upload"
142
+ };
143
+ var useJuggernautFileSystem = () => {
144
+ const requestFile = useCallback((actionType, payload) => {
145
+ return new Promise((resolve, reject) => {
146
+ if (IS_DEV_MODE) {
147
+ console.log(`[Dev Mode] Fetching file for ${actionType}`, payload);
148
+ const fileName = payload.fileId || payload.attachmentName || "demo.png";
149
+ fetch(`/demo-files/${fileName}`).then((res) => {
150
+ if (!res.ok)
151
+ throw new Error("Demo file not found");
152
+ return res.blob();
153
+ }).then((blob) => {
154
+ const file = new File([blob], fileName, { type: blob.type });
155
+ resolve(file);
156
+ }).catch((err) => {
157
+ console.warn("[Dev Mode] Could not load demo file:", err);
158
+ reject(err);
159
+ });
160
+ return;
161
+ }
162
+ if (typeof window === "undefined" || !window.parent) {
163
+ return reject(new Error("No parent window found"));
164
+ }
165
+ const requestId = crypto.randomUUID();
166
+ const handleResponse = (event) => {
167
+ const { requestId: resId, payload: responsePayload, error } = event.data || {};
168
+ if (resId === requestId) {
169
+ window.removeEventListener("message", handleResponse);
170
+ if (error) {
171
+ reject(new Error(error));
172
+ } else {
173
+ resolve(responsePayload);
174
+ }
175
+ }
176
+ };
177
+ window.addEventListener("message", handleResponse);
178
+ setTimeout(() => {
179
+ window.removeEventListener("message", handleResponse);
180
+ reject(new Error("File request timed out"));
181
+ }, 1e4);
182
+ window.parent.postMessage({
183
+ type: actionType,
184
+ requestId,
185
+ payload
186
+ }, "*");
187
+ });
188
+ }, []);
189
+ const getFile = useCallback((fileId) => {
190
+ if (!fileId)
191
+ return Promise.reject("No fileId provided");
192
+ return requestFile(FILE_EVENTS.GET_FILE, { fileId });
193
+ }, [requestFile]);
194
+ const getJobFile = useCallback((link, jobId = null) => {
195
+ if (!jobId || !link)
196
+ return Promise.reject("Missing jobId or link");
197
+ return requestFile(FILE_EVENTS.GET_JOB_FILE, { link, jobId });
198
+ }, [requestFile]);
199
+ return {
200
+ getFile,
201
+ getJobFile
202
+ };
203
+ };
204
+ // src/actions.jsx
205
+ import { useEffect as useEffect2, useCallback as useCallback2, useRef as useRef2 } from "react";
206
+ var ActionEvents = {
207
+ START_JOB: "start:job",
208
+ SCHEDULE_JOB: "schedule:job",
209
+ OPEN_RESOURCE: "open:resource",
210
+ START_PLUGIN: "start:plugin",
211
+ DATABASE_OPERATION: "database:operation",
212
+ TRIGGER_ACTION: "trigger:action",
213
+ CONTEXT_REQUEST: "juggernaut:context:request",
214
+ CONTEXT_RESPONSE: "juggernaut:context:response",
215
+ CONTEXT_UPDATE: "juggernaut:context:update"
216
+ };
217
+ var RESPONSE_TIMEOUT_MS = 5000;
218
+ var useBridge = () => {
219
+ const pendingRequests = useRef2(new Map);
220
+ useEffect2(() => {
221
+ const handleMessage = (event) => {
222
+ const { requestId, payload, error } = event.data || {};
223
+ if (requestId && pendingRequests.current.has(requestId)) {
224
+ const { resolve, reject, timer } = pendingRequests.current.get(requestId);
225
+ clearTimeout(timer);
226
+ pendingRequests.current.delete(requestId);
227
+ if (error) {
228
+ reject(new Error(error));
229
+ } else {
230
+ resolve(payload);
231
+ }
232
+ }
233
+ };
234
+ window.addEventListener("message", handleMessage);
235
+ return () => {
236
+ window.removeEventListener("message", handleMessage);
237
+ pendingRequests.current.forEach(({ reject, timer }) => {
238
+ clearTimeout(timer);
239
+ reject(new Error("Component unmounted"));
240
+ });
241
+ pendingRequests.current.clear();
242
+ };
243
+ }, []);
244
+ const sendEvent = useCallback2((actionType, payload = {}) => {
245
+ return new Promise((resolve, reject) => {
246
+ if (typeof window === "undefined" || !window?.parent) {
247
+ return reject(new Error("Parent window not found"));
248
+ }
249
+ const requestId = crypto.randomUUID ? crypto.randomUUID() : Date.now().toString();
250
+ const timer = setTimeout(() => {
251
+ if (pendingRequests.current.has(requestId)) {
252
+ pendingRequests.current.delete(requestId);
253
+ reject(new Error(`Request timed out after ${RESPONSE_TIMEOUT_MS}ms`));
254
+ }
255
+ }, RESPONSE_TIMEOUT_MS);
256
+ pendingRequests.current.set(requestId, { resolve, reject, timer });
257
+ window.parent.postMessage({
258
+ type: actionType,
259
+ requestId,
260
+ payload
261
+ }, "*");
262
+ });
263
+ }, []);
264
+ const startJob = useCallback2((processId, inputs, metadata = {}) => {
265
+ return sendEvent(ActionEvents.START_JOB, { processId, inputs, metadata });
266
+ }, [sendEvent]);
267
+ const scheduleJob = useCallback2((processId, inputs, schedule, metadata = {}) => {
268
+ return sendEvent(ActionEvents.SCHEDULE_JOB, { processId, inputs, schedule, metadata });
269
+ }, [sendEvent]);
270
+ const openResource = useCallback2((type, id, extra = {}) => {
271
+ return sendEvent(ActionEvents.OPEN_RESOURCE, { resourceType: type, resourceId: id, ...extra });
272
+ }, [sendEvent]);
273
+ const triggerAction = useCallback2((payload) => sendEvent(ActionEvents.TRIGGER_ACTION, payload), [sendEvent]);
274
+ const launchProcess = (id, options = {}) => openResource("process", id, options);
275
+ const launchJob = (id, options = {}) => openResource("job", id, options);
276
+ const launchPlugin = (id, options = {}) => openResource("plugin", id, options);
277
+ const launchDatabase = (id, options = {}) => openResource("database", id, options);
278
+ const launchRecord = (dbId, recordId, options = {}) => openResource("database:record", recordId, { database: dbId, ...options });
279
+ const startPlugin = useCallback2((payload) => sendEvent(ActionEvents.START_PLUGIN, payload), [sendEvent]);
280
+ const databaseOperation = useCallback2((payload) => sendEvent(ActionEvents.DATABASE_OPERATION, payload), [sendEvent]);
281
+ return {
282
+ startJob,
283
+ scheduleJob,
284
+ launchProcess,
285
+ launchJob,
286
+ launchPlugin,
287
+ launchDatabase,
288
+ launchRecord,
289
+ startPlugin,
290
+ databaseOperation,
291
+ triggerAction,
292
+ sendEvent
293
+ };
294
+ };
295
+ export {
296
+ useJuggernautSystem,
297
+ useJuggernautRecord,
298
+ useJuggernautProcess,
299
+ useJuggernautPlugin,
300
+ useJuggernautJob,
301
+ useJuggernautFileSystem,
302
+ useJuggernautData,
303
+ useJuggernautActions,
304
+ useBridge,
305
+ sendJuggernautActionEvent,
306
+ listenForJuggernautPlatformEvents,
307
+ ActionEvents
308
+ };
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@juggernautlabs/views",
3
+ "version": "0.0.1",
4
+ "description": "A way to build Juggernaut view templates using React.",
5
+ "type": "module",
6
+ "bin": {
7
+ "juggernauttemplates": "./dist/cli.js"
8
+ },
9
+ "main": "./dist/index.js",
10
+ "module": "./dist/index.js",
11
+ "scripts": {
12
+ "test": "bun test --preload ./bun-setup.js",
13
+ "build": "bun run build:cli && bun run build:lib",
14
+ "build:cli": "bun build ./bin/cli.js --outfile dist/cli.js --target node --packages external",
15
+ "build:lib": "bun build ./src/index.js --outfile dist/index.js --target browser --packages external --format esm"
16
+ },
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "keywords": [
21
+ "platform",
22
+ "cloud",
23
+ "sdk",
24
+ "api",
25
+ "plugin",
26
+ "juggernaut",
27
+ "juggernautlabs",
28
+ "plugins",
29
+ "llm integration",
30
+ "mcp alternative",
31
+ "workflow automation",
32
+ "developer tools"
33
+ ],
34
+ "author": "Juggernaut Labs",
35
+ "license": "MIT",
36
+ "dependencies": {
37
+ "@vitejs/plugin-react": "^4.2.0",
38
+ "bun": "^1.3.4",
39
+ "cac": "^6.7.0",
40
+ "dotenv": "^17.2.3",
41
+ "json5": "^2.2.3",
42
+ "react": "^19.2.1",
43
+ "vite": "^6.0.0",
44
+ "vite-plugin-singlefile": "^2.0.0"
45
+ },
46
+ "devDependencies": {
47
+ "@happy-dom/global-registrator": "^20.0.11",
48
+ "@testing-library/react": "^16.3.0",
49
+ "@types/jest": "^30.0.0",
50
+ "happy-dom": "^20.0.11",
51
+ "jest": "^30.2.0",
52
+ "jest-environment-jsdom": "^30.2.0",
53
+ "ts-jest": "^29.4.6"
54
+ }
55
+ }