@eide/foir-cli 0.1.16 → 0.1.18
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/cli.js +3 -0
- package/dist/codegen/field-mapping.d.ts +1 -0
- package/dist/codegen/field-mapping.d.ts.map +1 -1
- package/dist/codegen/field-mapping.js +8 -0
- package/dist/codegen/generators/customer-profile-hooks.d.ts +5 -0
- package/dist/codegen/generators/customer-profile-hooks.d.ts.map +1 -0
- package/dist/codegen/generators/customer-profile-hooks.js +78 -0
- package/dist/codegen/generators/customer-profile-loaders.d.ts +5 -0
- package/dist/codegen/generators/customer-profile-loaders.d.ts.map +1 -0
- package/dist/codegen/generators/customer-profile-loaders.js +67 -0
- package/dist/codegen/generators/customer-profile-operations.d.ts +5 -0
- package/dist/codegen/generators/customer-profile-operations.d.ts.map +1 -0
- package/dist/codegen/generators/customer-profile-operations.js +126 -0
- package/dist/codegen/generators/documents.d.ts.map +1 -1
- package/dist/codegen/generators/documents.js +4 -0
- package/dist/codegen/generators/public-schema-content.d.ts +14 -0
- package/dist/codegen/generators/public-schema-content.d.ts.map +1 -0
- package/dist/codegen/generators/public-schema-content.js +22 -0
- package/dist/codegen/generators/react-hooks-index.d.ts +6 -0
- package/dist/codegen/generators/react-hooks-index.d.ts.map +1 -0
- package/dist/codegen/generators/react-hooks-index.js +20 -0
- package/dist/codegen/generators/react-hooks.d.ts +7 -0
- package/dist/codegen/generators/react-hooks.d.ts.map +1 -0
- package/dist/codegen/generators/react-hooks.js +139 -0
- package/dist/codegen/generators/remix-loaders-index.d.ts +6 -0
- package/dist/codegen/generators/remix-loaders-index.d.ts.map +1 -0
- package/dist/codegen/generators/remix-loaders-index.js +20 -0
- package/dist/codegen/generators/remix-loaders.d.ts +7 -0
- package/dist/codegen/generators/remix-loaders.d.ts.map +1 -0
- package/dist/codegen/generators/remix-loaders.js +107 -0
- package/dist/codegen/generators/static-documents.d.ts +14 -0
- package/dist/codegen/generators/static-documents.d.ts.map +1 -0
- package/dist/codegen/generators/static-documents.js +766 -0
- package/dist/codegen/generators/typed-operations-common.d.ts +6 -0
- package/dist/codegen/generators/typed-operations-common.d.ts.map +1 -0
- package/dist/codegen/generators/typed-operations-common.js +74 -0
- package/dist/codegen/generators/typed-operations-index.d.ts +6 -0
- package/dist/codegen/generators/typed-operations-index.d.ts.map +1 -0
- package/dist/codegen/generators/typed-operations-index.js +22 -0
- package/dist/codegen/generators/typed-operations.d.ts +11 -0
- package/dist/codegen/generators/typed-operations.d.ts.map +1 -0
- package/dist/codegen/generators/typed-operations.js +251 -0
- package/dist/commands/create-extension.d.ts +4 -0
- package/dist/commands/create-extension.d.ts.map +1 -0
- package/dist/commands/create-extension.js +60 -0
- package/dist/commands/pull.d.ts.map +1 -1
- package/dist/commands/pull.js +135 -25
- package/dist/config/pull-config.d.ts +6 -0
- package/dist/config/pull-config.d.ts.map +1 -1
- package/dist/config/pull-config.js +50 -1
- package/dist/config/types.d.ts +23 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/scaffold/package-manager.d.ts +12 -0
- package/dist/scaffold/package-manager.d.ts.map +1 -0
- package/dist/scaffold/package-manager.js +51 -0
- package/dist/scaffold/scaffold.d.ts +4 -0
- package/dist/scaffold/scaffold.d.ts.map +1 -0
- package/dist/scaffold/scaffold.js +462 -0
- package/package.json +1 -1
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { detectPackageManager } from './package-manager.js';
|
|
4
|
+
export async function scaffold(projectName, extensionType, apiUrl) {
|
|
5
|
+
const projectDir = path.resolve(process.cwd(), projectName);
|
|
6
|
+
if (fs.existsSync(projectDir)) {
|
|
7
|
+
throw new Error(`Directory "${projectName}" already exists. Choose a different name or remove the existing directory.`);
|
|
8
|
+
}
|
|
9
|
+
// Create directory structure
|
|
10
|
+
const files = getFiles(projectName, extensionType, apiUrl);
|
|
11
|
+
for (const [filePath, content] of Object.entries(files)) {
|
|
12
|
+
const fullPath = path.join(projectDir, filePath);
|
|
13
|
+
const dir = path.dirname(fullPath);
|
|
14
|
+
if (!fs.existsSync(dir)) {
|
|
15
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
16
|
+
}
|
|
17
|
+
fs.writeFileSync(fullPath, content, 'utf-8');
|
|
18
|
+
}
|
|
19
|
+
// Detect package manager and print instructions
|
|
20
|
+
const pm = detectPackageManager();
|
|
21
|
+
console.log(' Files created:');
|
|
22
|
+
console.log();
|
|
23
|
+
for (const filePath of Object.keys(files)) {
|
|
24
|
+
console.log(` ${filePath}`);
|
|
25
|
+
}
|
|
26
|
+
console.log();
|
|
27
|
+
console.log(' Done! Next steps:');
|
|
28
|
+
console.log();
|
|
29
|
+
console.log(` cd ${projectName}`);
|
|
30
|
+
console.log(` ${pm.installCommand}`);
|
|
31
|
+
console.log(` cp ui/.env.example ui/.env.local`);
|
|
32
|
+
console.log(` cp api/.env.example api/.env.local`);
|
|
33
|
+
console.log(` ${pm.name === 'npm' ? 'npm run' : pm.name} dev`);
|
|
34
|
+
console.log();
|
|
35
|
+
}
|
|
36
|
+
function getFiles(projectName, extensionType, apiUrl) {
|
|
37
|
+
return {
|
|
38
|
+
// Root
|
|
39
|
+
'package.json': getRootPackageJson(projectName),
|
|
40
|
+
'extension.manifest.json': getManifest(projectName, extensionType),
|
|
41
|
+
// UI (Vite SPA)
|
|
42
|
+
'ui/package.json': getUiPackageJson(projectName),
|
|
43
|
+
'ui/tsconfig.json': getUiTsconfig(),
|
|
44
|
+
'ui/vite.config.ts': getUiViteConfig(),
|
|
45
|
+
'ui/index.html': getUiIndexHtml(projectName),
|
|
46
|
+
'ui/.env.example': getUiEnvExample(apiUrl),
|
|
47
|
+
'ui/.gitignore': getUiGitignore(),
|
|
48
|
+
'ui/src/main.tsx': getUiMain(),
|
|
49
|
+
'ui/src/App.tsx': getUiApp(extensionType),
|
|
50
|
+
'ui/src/index.css': getUiCss(),
|
|
51
|
+
'ui/src/vite-env.d.ts': '/// <reference types="vite/client" />\n',
|
|
52
|
+
// API (Hono)
|
|
53
|
+
'api/package.json': getApiPackageJson(projectName),
|
|
54
|
+
'api/tsconfig.json': getApiTsconfig(),
|
|
55
|
+
'api/.env.example': getApiEnvExample(apiUrl),
|
|
56
|
+
'api/.gitignore': 'node_modules\ndist\n.env\n.env.local\n',
|
|
57
|
+
'api/src/index.ts': getApiIndex(),
|
|
58
|
+
'api/src/routes/webhooks.ts': getApiWebhooks(),
|
|
59
|
+
'api/src/routes/health.ts': getApiHealth(),
|
|
60
|
+
'api/src/lib/platform.ts': getApiPlatform(),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
// Root
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
function getRootPackageJson(name) {
|
|
67
|
+
const pkg = {
|
|
68
|
+
name,
|
|
69
|
+
version: '0.1.0',
|
|
70
|
+
private: true,
|
|
71
|
+
scripts: {
|
|
72
|
+
dev: 'concurrently "pnpm --filter ./ui dev" "pnpm --filter ./api dev"',
|
|
73
|
+
build: 'pnpm --filter ./ui build && pnpm --filter ./api build',
|
|
74
|
+
},
|
|
75
|
+
devDependencies: {
|
|
76
|
+
concurrently: '^9.0.0',
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
return JSON.stringify(pkg, null, 2) + '\n';
|
|
80
|
+
}
|
|
81
|
+
function getManifest(name, extensionType) {
|
|
82
|
+
const manifest = {
|
|
83
|
+
name: name.replace(/-/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase()),
|
|
84
|
+
version: '0.1.0',
|
|
85
|
+
type: extensionType,
|
|
86
|
+
description: `${extensionType} extension`,
|
|
87
|
+
entityTypes: [],
|
|
88
|
+
};
|
|
89
|
+
return JSON.stringify(manifest, null, 2) + '\n';
|
|
90
|
+
}
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
// UI (Vite SPA)
|
|
93
|
+
// ---------------------------------------------------------------------------
|
|
94
|
+
function getUiPackageJson(name) {
|
|
95
|
+
const pkg = {
|
|
96
|
+
name: `${name}-ui`,
|
|
97
|
+
version: '0.1.0',
|
|
98
|
+
private: true,
|
|
99
|
+
type: 'module',
|
|
100
|
+
scripts: {
|
|
101
|
+
dev: 'vite',
|
|
102
|
+
build: 'tsc && vite build',
|
|
103
|
+
preview: 'vite preview',
|
|
104
|
+
},
|
|
105
|
+
dependencies: {
|
|
106
|
+
'@eide/extension-sdk': '^0.1.0',
|
|
107
|
+
react: '^19.0.0',
|
|
108
|
+
'react-dom': '^19.0.0',
|
|
109
|
+
},
|
|
110
|
+
devDependencies: {
|
|
111
|
+
'@tailwindcss/vite': '^4.0.6',
|
|
112
|
+
'@types/react': '^19.0.0',
|
|
113
|
+
'@types/react-dom': '^19.0.0',
|
|
114
|
+
'@vitejs/plugin-react': '^4.3.4',
|
|
115
|
+
tailwindcss: '^4.0.0',
|
|
116
|
+
typescript: '^5.0.0',
|
|
117
|
+
vite: '^6.0.7',
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
return JSON.stringify(pkg, null, 2) + '\n';
|
|
121
|
+
}
|
|
122
|
+
function getUiTsconfig() {
|
|
123
|
+
const config = {
|
|
124
|
+
compilerOptions: {
|
|
125
|
+
target: 'ES2020',
|
|
126
|
+
lib: ['ES2020', 'DOM', 'DOM.Iterable'],
|
|
127
|
+
module: 'ESNext',
|
|
128
|
+
skipLibCheck: true,
|
|
129
|
+
moduleResolution: 'bundler',
|
|
130
|
+
allowImportingTsExtensions: true,
|
|
131
|
+
resolveJsonModule: true,
|
|
132
|
+
isolatedModules: true,
|
|
133
|
+
noEmit: true,
|
|
134
|
+
jsx: 'react-jsx',
|
|
135
|
+
strict: true,
|
|
136
|
+
baseUrl: '.',
|
|
137
|
+
paths: { '@/*': ['./src/*'] },
|
|
138
|
+
},
|
|
139
|
+
include: ['src'],
|
|
140
|
+
};
|
|
141
|
+
return JSON.stringify(config, null, 2) + '\n';
|
|
142
|
+
}
|
|
143
|
+
function getUiViteConfig() {
|
|
144
|
+
return `import { defineConfig } from 'vite';
|
|
145
|
+
import react from '@vitejs/plugin-react';
|
|
146
|
+
import tailwindcss from '@tailwindcss/vite';
|
|
147
|
+
import path from 'path';
|
|
148
|
+
|
|
149
|
+
export default defineConfig({
|
|
150
|
+
plugins: [react(), tailwindcss()],
|
|
151
|
+
resolve: {
|
|
152
|
+
alias: {
|
|
153
|
+
'@': path.resolve(__dirname, './src'),
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
server: {
|
|
157
|
+
port: 3001,
|
|
158
|
+
host: '127.0.0.1',
|
|
159
|
+
cors: true,
|
|
160
|
+
headers: {
|
|
161
|
+
'X-Frame-Options': 'ALLOWALL',
|
|
162
|
+
'Content-Security-Policy': 'frame-ancestors *',
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
build: {
|
|
166
|
+
outDir: 'dist',
|
|
167
|
+
sourcemap: true,
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
`;
|
|
171
|
+
}
|
|
172
|
+
function getUiIndexHtml(name) {
|
|
173
|
+
const title = name
|
|
174
|
+
.replace(/-/g, ' ')
|
|
175
|
+
.replace(/\b\w/g, (c) => c.toUpperCase());
|
|
176
|
+
return `<!doctype html>
|
|
177
|
+
<html lang="en">
|
|
178
|
+
<head>
|
|
179
|
+
<meta charset="UTF-8" />
|
|
180
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
181
|
+
<title>${title}</title>
|
|
182
|
+
</head>
|
|
183
|
+
<body>
|
|
184
|
+
<div id="root"></div>
|
|
185
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
186
|
+
</body>
|
|
187
|
+
</html>
|
|
188
|
+
`;
|
|
189
|
+
}
|
|
190
|
+
function getUiEnvExample(apiUrl) {
|
|
191
|
+
return `VITE_PARENT_ORIGIN=http://localhost:3000
|
|
192
|
+
VITE_API_URL=${apiUrl}
|
|
193
|
+
`;
|
|
194
|
+
}
|
|
195
|
+
function getUiGitignore() {
|
|
196
|
+
return `node_modules
|
|
197
|
+
dist
|
|
198
|
+
.env
|
|
199
|
+
.env.local
|
|
200
|
+
`;
|
|
201
|
+
}
|
|
202
|
+
function getUiMain() {
|
|
203
|
+
return `import { StrictMode } from 'react';
|
|
204
|
+
import { createRoot } from 'react-dom/client';
|
|
205
|
+
import { ExtensionProvider, useExtension } from '@eide/extension-sdk';
|
|
206
|
+
import { useEffect } from 'react';
|
|
207
|
+
import { App } from './App';
|
|
208
|
+
import './index.css';
|
|
209
|
+
|
|
210
|
+
function ThemeSync({ children }: { children: React.ReactNode }) {
|
|
211
|
+
const { theme } = useExtension();
|
|
212
|
+
|
|
213
|
+
useEffect(() => {
|
|
214
|
+
const root = document.documentElement;
|
|
215
|
+
root.classList.remove('light', 'dark');
|
|
216
|
+
root.classList.add(theme);
|
|
217
|
+
}, [theme]);
|
|
218
|
+
|
|
219
|
+
return <>{children}</>;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
createRoot(document.getElementById('root')!).render(
|
|
223
|
+
<StrictMode>
|
|
224
|
+
<ExtensionProvider>
|
|
225
|
+
<ThemeSync>
|
|
226
|
+
<App />
|
|
227
|
+
</ThemeSync>
|
|
228
|
+
</ExtensionProvider>
|
|
229
|
+
</StrictMode>
|
|
230
|
+
);
|
|
231
|
+
`;
|
|
232
|
+
}
|
|
233
|
+
function getUiApp(extensionType) {
|
|
234
|
+
switch (extensionType) {
|
|
235
|
+
case 'custom-editor':
|
|
236
|
+
return getCustomEditorApp();
|
|
237
|
+
case 'widget':
|
|
238
|
+
return getWidgetApp();
|
|
239
|
+
case 'workflow':
|
|
240
|
+
return getWorkflowApp();
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
function getCustomEditorApp() {
|
|
244
|
+
return `import { useExtension, useAutoResize } from '@eide/extension-sdk';
|
|
245
|
+
|
|
246
|
+
export function App() {
|
|
247
|
+
const { isReady, init, updateField, setDirty } = useExtension();
|
|
248
|
+
const containerRef = useAutoResize({ minHeight: 600 });
|
|
249
|
+
|
|
250
|
+
if (!isReady) return null;
|
|
251
|
+
|
|
252
|
+
return (
|
|
253
|
+
<div ref={containerRef} className="p-6 space-y-4">
|
|
254
|
+
<h1 className="text-lg font-semibold">
|
|
255
|
+
Editing: {init?.entityModelKey}
|
|
256
|
+
</h1>
|
|
257
|
+
<p className="text-sm text-gray-500">
|
|
258
|
+
Record: {init?.recordId}
|
|
259
|
+
</p>
|
|
260
|
+
{/* Add your editor form fields here */}
|
|
261
|
+
</div>
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
`;
|
|
265
|
+
}
|
|
266
|
+
function getWidgetApp() {
|
|
267
|
+
return `import { useExtension, useAutoResize } from '@eide/extension-sdk';
|
|
268
|
+
|
|
269
|
+
export function App() {
|
|
270
|
+
const { isReady, init, client } = useExtension();
|
|
271
|
+
const containerRef = useAutoResize({ minHeight: 300 });
|
|
272
|
+
|
|
273
|
+
if (!isReady) return null;
|
|
274
|
+
|
|
275
|
+
return (
|
|
276
|
+
<div ref={containerRef} className="p-6 space-y-4">
|
|
277
|
+
<h2 className="text-lg font-semibold">Dashboard Widget</h2>
|
|
278
|
+
<p className="text-sm text-gray-500">
|
|
279
|
+
Connected to: {init?.entityModelKey}
|
|
280
|
+
</p>
|
|
281
|
+
{/* Add your widget content here */}
|
|
282
|
+
</div>
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
`;
|
|
286
|
+
}
|
|
287
|
+
function getWorkflowApp() {
|
|
288
|
+
return `import { useExtension, useAutoResize } from '@eide/extension-sdk';
|
|
289
|
+
|
|
290
|
+
export function App() {
|
|
291
|
+
const { isReady, init, client, requestSave } = useExtension();
|
|
292
|
+
const containerRef = useAutoResize({ minHeight: 400 });
|
|
293
|
+
|
|
294
|
+
if (!isReady) return null;
|
|
295
|
+
|
|
296
|
+
return (
|
|
297
|
+
<div ref={containerRef} className="p-6 space-y-4">
|
|
298
|
+
<h1 className="text-lg font-semibold">Workflow Extension</h1>
|
|
299
|
+
<p className="text-sm text-gray-500">
|
|
300
|
+
Processing: {init?.entityModelKey} / {init?.recordId}
|
|
301
|
+
</p>
|
|
302
|
+
{/* Add your workflow steps here */}
|
|
303
|
+
</div>
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
`;
|
|
307
|
+
}
|
|
308
|
+
function getUiCss() {
|
|
309
|
+
return `@import 'tailwindcss';
|
|
310
|
+
|
|
311
|
+
:root {
|
|
312
|
+
--background: #ffffff;
|
|
313
|
+
--foreground: #171717;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
.dark {
|
|
317
|
+
--background: #0a0a0a;
|
|
318
|
+
--foreground: #ededed;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
body {
|
|
322
|
+
background: var(--background);
|
|
323
|
+
color: var(--foreground);
|
|
324
|
+
font-family: system-ui, sans-serif;
|
|
325
|
+
}
|
|
326
|
+
`;
|
|
327
|
+
}
|
|
328
|
+
// ---------------------------------------------------------------------------
|
|
329
|
+
// API (Hono)
|
|
330
|
+
// ---------------------------------------------------------------------------
|
|
331
|
+
function getApiPackageJson(name) {
|
|
332
|
+
const pkg = {
|
|
333
|
+
name: `${name}-api`,
|
|
334
|
+
version: '0.1.0',
|
|
335
|
+
private: true,
|
|
336
|
+
type: 'module',
|
|
337
|
+
scripts: {
|
|
338
|
+
dev: 'tsx watch src/index.ts',
|
|
339
|
+
build: 'tsx src/index.ts',
|
|
340
|
+
start: 'node dist/index.js',
|
|
341
|
+
},
|
|
342
|
+
dependencies: {
|
|
343
|
+
'@eide/extension-sdk': '^0.1.0',
|
|
344
|
+
hono: '^4.0.0',
|
|
345
|
+
'@hono/node-server': '^1.0.0',
|
|
346
|
+
},
|
|
347
|
+
devDependencies: {
|
|
348
|
+
tsx: '^4.0.0',
|
|
349
|
+
typescript: '^5.0.0',
|
|
350
|
+
},
|
|
351
|
+
};
|
|
352
|
+
return JSON.stringify(pkg, null, 2) + '\n';
|
|
353
|
+
}
|
|
354
|
+
function getApiTsconfig() {
|
|
355
|
+
const config = {
|
|
356
|
+
compilerOptions: {
|
|
357
|
+
target: 'ES2020',
|
|
358
|
+
module: 'ESNext',
|
|
359
|
+
moduleResolution: 'bundler',
|
|
360
|
+
outDir: './dist',
|
|
361
|
+
rootDir: './src',
|
|
362
|
+
strict: true,
|
|
363
|
+
esModuleInterop: true,
|
|
364
|
+
skipLibCheck: true,
|
|
365
|
+
resolveJsonModule: true,
|
|
366
|
+
isolatedModules: true,
|
|
367
|
+
},
|
|
368
|
+
include: ['src'],
|
|
369
|
+
exclude: ['dist', 'node_modules'],
|
|
370
|
+
};
|
|
371
|
+
return JSON.stringify(config, null, 2) + '\n';
|
|
372
|
+
}
|
|
373
|
+
function getApiEnvExample(apiUrl) {
|
|
374
|
+
const baseUrl = apiUrl.replace(/\/graphql$/, '');
|
|
375
|
+
return `# Platform API
|
|
376
|
+
PLATFORM_BASE_URL=${baseUrl}
|
|
377
|
+
PLATFORM_API_KEY=sk_your_api_key_here
|
|
378
|
+
|
|
379
|
+
# Extension
|
|
380
|
+
EXTENSION_KEY=${'{your-extension-key}'}
|
|
381
|
+
WEBHOOK_SECRET=your_webhook_secret_here
|
|
382
|
+
|
|
383
|
+
# Server
|
|
384
|
+
PORT=3002
|
|
385
|
+
`;
|
|
386
|
+
}
|
|
387
|
+
function getApiIndex() {
|
|
388
|
+
return `import { Hono } from 'hono';
|
|
389
|
+
import { serve } from '@hono/node-server';
|
|
390
|
+
import { webhooks } from './routes/webhooks';
|
|
391
|
+
import { health } from './routes/health';
|
|
392
|
+
|
|
393
|
+
const app = new Hono();
|
|
394
|
+
|
|
395
|
+
// Routes
|
|
396
|
+
app.route('/webhooks', webhooks);
|
|
397
|
+
app.route('/', health);
|
|
398
|
+
|
|
399
|
+
const port = parseInt(process.env.PORT || '3002', 10);
|
|
400
|
+
|
|
401
|
+
console.log(\`Extension API running on http://localhost:\${port}\`);
|
|
402
|
+
|
|
403
|
+
serve({ fetch: app.fetch, port });
|
|
404
|
+
`;
|
|
405
|
+
}
|
|
406
|
+
function getApiWebhooks() {
|
|
407
|
+
return `import { Hono } from 'hono';
|
|
408
|
+
import { verifyWebhookSignature } from '@eide/extension-sdk/server';
|
|
409
|
+
|
|
410
|
+
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET || '';
|
|
411
|
+
|
|
412
|
+
export const webhooks = new Hono();
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Receive entity lifecycle events from the platform.
|
|
416
|
+
*/
|
|
417
|
+
webhooks.post('/entity-changed', async (c) => {
|
|
418
|
+
const body = await c.req.text();
|
|
419
|
+
const signature = c.req.header('x-eide-signature') ?? '';
|
|
420
|
+
|
|
421
|
+
const valid = await verifyWebhookSignature(body, signature, WEBHOOK_SECRET);
|
|
422
|
+
if (!valid) {
|
|
423
|
+
return c.json({ error: 'Invalid signature' }, 401);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
const payload = JSON.parse(body);
|
|
427
|
+
console.log('[Webhook] Entity changed:', payload.event, payload.entityId);
|
|
428
|
+
|
|
429
|
+
// TODO: Handle the event (sync, transform, notify, etc.)
|
|
430
|
+
|
|
431
|
+
return c.json({ received: true });
|
|
432
|
+
});
|
|
433
|
+
`;
|
|
434
|
+
}
|
|
435
|
+
function getApiHealth() {
|
|
436
|
+
return `import { Hono } from 'hono';
|
|
437
|
+
|
|
438
|
+
export const health = new Hono();
|
|
439
|
+
|
|
440
|
+
health.get('/health', (c) => {
|
|
441
|
+
return c.json({ status: 'ok', timestamp: new Date().toISOString() });
|
|
442
|
+
});
|
|
443
|
+
`;
|
|
444
|
+
}
|
|
445
|
+
function getApiPlatform() {
|
|
446
|
+
return `import { createExtensionClient } from '@eide/extension-sdk/server';
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Pre-configured platform client for this extension.
|
|
450
|
+
*
|
|
451
|
+
* Uses env vars for configuration:
|
|
452
|
+
* - PLATFORM_BASE_URL: Platform API base URL
|
|
453
|
+
* - PLATFORM_API_KEY: Project-scoped API key (sk_*)
|
|
454
|
+
* - EXTENSION_KEY: This extension's extension key
|
|
455
|
+
*/
|
|
456
|
+
export const platform = createExtensionClient({
|
|
457
|
+
baseUrl: process.env.PLATFORM_BASE_URL || 'http://localhost:4000',
|
|
458
|
+
apiKey: process.env.PLATFORM_API_KEY || '',
|
|
459
|
+
extensionKey: process.env.EXTENSION_KEY || '',
|
|
460
|
+
});
|
|
461
|
+
`;
|
|
462
|
+
}
|