@robono/server 0.20.1 → 0.22.0

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.
Files changed (38) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +36 -4
  3. package/SBOM.spdx.json +9 -9
  4. package/dist/messenger-connected-templates.d.ts.map +1 -1
  5. package/dist/messenger-connected-templates.js +6 -3
  6. package/dist/messenger-connected-templates.js.map +1 -1
  7. package/dist/messenger-customer-templates.d.ts +4 -0
  8. package/dist/messenger-customer-templates.d.ts.map +1 -0
  9. package/dist/messenger-customer-templates.js +111 -0
  10. package/dist/messenger-customer-templates.js.map +1 -0
  11. package/dist/messenger-init.d.ts.map +1 -1
  12. package/dist/messenger-init.js +3 -2
  13. package/dist/messenger-init.js.map +1 -1
  14. package/dist/messenger-install-cli.js +28 -8
  15. package/dist/messenger-install-cli.js.map +1 -1
  16. package/dist/messenger-install.d.ts +14 -1
  17. package/dist/messenger-install.d.ts.map +1 -1
  18. package/dist/messenger-install.js +244 -48
  19. package/dist/messenger-install.js.map +1 -1
  20. package/dist/messenger-node.d.ts +14 -0
  21. package/dist/messenger-node.d.ts.map +1 -0
  22. package/dist/messenger-node.js +127 -0
  23. package/dist/messenger-node.js.map +1 -0
  24. package/dist/messenger-upgrade.d.ts +21 -0
  25. package/dist/messenger-upgrade.d.ts.map +1 -0
  26. package/dist/messenger-upgrade.js +136 -0
  27. package/dist/messenger-upgrade.js.map +1 -0
  28. package/dist/messenger-vite-templates.d.ts +12 -0
  29. package/dist/messenger-vite-templates.d.ts.map +1 -0
  30. package/dist/messenger-vite-templates.js +89 -0
  31. package/dist/messenger-vite-templates.js.map +1 -0
  32. package/legacy/0.21.0/messenger-connected-templates.js +138 -0
  33. package/legacy/0.21.0/messenger-customer-templates.js +111 -0
  34. package/legacy/0.21.0/messenger-init.js +121 -0
  35. package/legacy/0.21.0/messenger-install.js +243 -0
  36. package/legacy/0.21.0/messenger-preview-template.js +43 -0
  37. package/legacy/0.21.0/package.json +1 -0
  38. package/package.json +11 -2
@@ -0,0 +1,127 @@
1
+ import { createServer } from 'node:http';
2
+ import { readFile, realpath, stat } from 'node:fs/promises';
3
+ import { resolve, relative, extname, sep, isAbsolute } from 'node:path';
4
+ const json = (res, status, code) => {
5
+ if (res.destroyed || res.writableEnded)
6
+ return;
7
+ res.writeHead(status, { 'content-type': 'application/json', 'cache-control': 'no-store' });
8
+ res.end(JSON.stringify({ error: { code } }));
9
+ };
10
+ /** Node/Express-compatible handler. Mount before body parsers; private keys stay in getMessenger. */
11
+ export function createMessengerNodeHandler(options) {
12
+ const origin = new URL(options.origin);
13
+ if (origin.origin !== options.origin || !['http:', 'https:'].includes(origin.protocol))
14
+ throw Error('Use an exact website origin.');
15
+ return async (req, res) => {
16
+ const path = req.url?.split('?')[0];
17
+ if (path !== '/api/robono')
18
+ return false;
19
+ if (!['GET', 'POST'].includes(req.method ?? '')) {
20
+ json(res, 405, 'method_not_allowed');
21
+ return true;
22
+ }
23
+ const controller = new AbortController();
24
+ const abort = () => controller.abort();
25
+ req.once('aborted', abort);
26
+ const close = () => { if (!res.writableFinished)
27
+ abort(); };
28
+ res.once('close', close);
29
+ try {
30
+ // Reject ambiguous authentication/origin headers instead of relying on Node's merging.
31
+ for (const name of ['authorization', 'origin', 'host', 'content-length']) {
32
+ let count = 0;
33
+ for (let i = 0; i < req.rawHeaders.length; i += 2)
34
+ if (req.rawHeaders[i]?.toLowerCase() === name)
35
+ count++;
36
+ if (count > 1) {
37
+ json(res, 400, 'duplicate_header');
38
+ return true;
39
+ }
40
+ }
41
+ const limit = 262144;
42
+ if (Number(req.headers['content-length']) > limit) {
43
+ json(res, 413, 'request_too_large');
44
+ req.resume();
45
+ return true;
46
+ }
47
+ const parts = [];
48
+ let length = 0;
49
+ for await (const part of req.iterator({ destroyOnReturn: false })) {
50
+ const bytes = Buffer.from(part);
51
+ length += bytes.length;
52
+ if (length > limit) {
53
+ json(res, 413, 'request_too_large');
54
+ req.resume();
55
+ return true;
56
+ }
57
+ parts.push(bytes);
58
+ }
59
+ const headers = new Headers();
60
+ for (const [name, value] of Object.entries(req.headers))
61
+ if (value !== undefined)
62
+ headers.set(name, Array.isArray(value) ? value.join(', ') : value);
63
+ const request = new Request(options.origin + (req.url ?? '/api/robono'), { method: req.method, headers, signal: controller.signal, ...(req.method === 'POST' ? { body: Buffer.concat(parts).toString('utf8') } : {}) });
64
+ const response = await options.getMessenger().handle(request);
65
+ if (res.destroyed)
66
+ return true;
67
+ res.writeHead(response.status, { 'cache-control': 'no-store', ...Object.fromEntries(response.headers) });
68
+ res.end(Buffer.from(await response.arrayBuffer()));
69
+ }
70
+ catch {
71
+ json(res, 503, 'messaging_unavailable');
72
+ }
73
+ finally {
74
+ req.off('aborted', abort);
75
+ res.off('close', close);
76
+ }
77
+ return true;
78
+ };
79
+ }
80
+ const types = { '.html': 'text/html; charset=utf-8', '.js': 'text/javascript; charset=utf-8', '.mjs': 'text/javascript; charset=utf-8', '.css': 'text/css; charset=utf-8', '.json': 'application/json', '.webmanifest': 'application/manifest+json', '.svg': 'image/svg+xml', '.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.webp': 'image/webp', '.gif': 'image/gif', '.ico': 'image/x-icon', '.woff': 'font/woff', '.woff2': 'font/woff2', '.ttf': 'font/ttf', '.txt': 'text/plain; charset=utf-8', '.mp4': 'video/mp4', '.mp3': 'audio/mpeg', '.m4a': 'audio/mp4', '.wasm': 'application/wasm' };
81
+ /** Serve only a built Vite frontend, never the application/source directory. */
82
+ export async function createMessengerWebsiteServer(options) {
83
+ const root = await realpath(resolve(options.staticDirectory));
84
+ await stat(resolve(root, 'index.html'));
85
+ const handler = createMessengerNodeHandler(options);
86
+ return createServer({ requestTimeout: 30000, headersTimeout: 15000, maxHeaderSize: 16384 }, async (req, res) => {
87
+ if (await handler(req, res))
88
+ return;
89
+ if (!['GET', 'HEAD'].includes(req.method ?? '')) {
90
+ json(res, 405, 'method_not_allowed');
91
+ return;
92
+ }
93
+ try {
94
+ const pathname = decodeURIComponent((req.url ?? '/').split('?')[0]);
95
+ if (!pathname.startsWith('/') || pathname.includes('\\') || pathname.includes('\0') || pathname.split('/').some(part => part.startsWith('.')) || pathname.startsWith('/api/') || pathname === '/robono-preview' || pathname.startsWith('/@')) {
96
+ json(res, 404, 'not_found');
97
+ return;
98
+ }
99
+ let file = resolve(root, '.' + pathname);
100
+ const inside = (value) => { const local = relative(root, value); return local !== '' && !isAbsolute(local) && !local.startsWith('..' + sep) && local !== '..'; };
101
+ if (!inside(file) && file !== root) {
102
+ json(res, 404, 'not_found');
103
+ return;
104
+ }
105
+ const found = await stat(file).catch(() => null);
106
+ if (!found?.isFile()) {
107
+ if (extname(pathname) || !(req.headers.accept ?? '').includes('text/html')) {
108
+ json(res, 404, 'not_found');
109
+ return;
110
+ }
111
+ file = resolve(root, 'index.html');
112
+ }
113
+ file = await realpath(file);
114
+ if (!inside(file) || !types[extname(file)]) {
115
+ json(res, 404, 'not_found');
116
+ return;
117
+ }
118
+ const data = await readFile(file);
119
+ res.writeHead(200, { 'content-type': types[extname(file)], 'x-content-type-options': 'nosniff', 'cache-control': extname(file) === '.html' ? 'no-cache' : 'public, max-age=0', 'content-length': data.length });
120
+ res.end(req.method === 'HEAD' ? undefined : data);
121
+ }
122
+ catch {
123
+ json(res, 404, 'not_found');
124
+ }
125
+ });
126
+ }
127
+ //# sourceMappingURL=messenger-node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"messenger-node.js","sourceRoot":"","sources":["../src/messenger-node.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import { type MessengerInstallPlan } from './messenger-install.js';
2
+ export interface MessengerUpgradeReport {
3
+ version: string;
4
+ updated: string[];
5
+ added: string[];
6
+ preserved: string[];
7
+ conflicts: {
8
+ file: string;
9
+ proposed: string;
10
+ reason: string;
11
+ }[];
12
+ backups: string[];
13
+ }
14
+ export interface MessengerUpgradePlan extends MessengerInstallPlan {
15
+ upgradeReport: MessengerUpgradeReport;
16
+ }
17
+ /** Changed customer files are never replaced or automatically merged. */
18
+ export declare function planMessengerUpgrade(directory: string, options?: {
19
+ companyName?: string;
20
+ }): Promise<MessengerUpgradePlan>;
21
+ //# sourceMappingURL=messenger-upgrade.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"messenger-upgrade.d.ts","sourceRoot":"","sources":["../src/messenger-upgrade.ts"],"names":[],"mappings":"AAKA,OAAO,EAAiJ,KAAK,oBAAoB,EAAC,MAAM,wBAAwB,CAAC;AAEjN,MAAM,WAAW,sBAAsB;IACtC,OAAO,EAAC,MAAM,CAAC;IACf,OAAO,EAAC,MAAM,EAAE,CAAC;IACjB,KAAK,EAAC,MAAM,EAAE,CAAC;IACf,SAAS,EAAC,MAAM,EAAE,CAAC;IACnB,SAAS,EAAC;QAAC,IAAI,EAAC,MAAM,CAAC;QAAA,QAAQ,EAAC,MAAM,CAAC;QAAA,MAAM,EAAC,MAAM,CAAA;KAAC,EAAE,CAAC;IACxD,OAAO,EAAC,MAAM,EAAE,CAAC;CACjB;AACD,MAAM,WAAW,oBAAqB,SAAQ,oBAAoB;IAAE,aAAa,EAAC,sBAAsB,CAAA;CAAC;AA6BzG,yEAAyE;AACzE,wBAAsB,oBAAoB,CAAC,SAAS,EAAC,MAAM,EAAC,OAAO,GAAC;IAAC,WAAW,CAAC,EAAC,MAAM,CAAA;CAAI,GAAE,OAAO,CAAC,oBAAoB,CAAC,CAoD1H"}
@@ -0,0 +1,136 @@
1
+ import { readFile, mkdir, mkdtemp, rm, realpath } from 'node:fs/promises';
2
+ import { resolve, join, relative, isAbsolute } from 'node:path';
3
+ import { tmpdir } from 'node:os';
4
+ import { randomUUID } from 'node:crypto';
5
+ import { writeFile } from 'node:fs/promises';
6
+ import { buildMessengerInstallation, exists, safePath, readMessengerSetup, generatedKey, generatedHash, messengerInstallerVersion } from './messenger-install.js';
7
+ /** Reproduce the previous published generator in an empty temporary project, never in the customer's app. */
8
+ async function legacyBaselines(setup, options, client, server) {
9
+ if (setup.layout === 'vite')
10
+ return {};
11
+ const folder = await realpath(await mkdtemp(join(tmpdir(), 'robono-legacy-')));
12
+ try {
13
+ const app = join(folder, 'client'), backend = setup.platform === 'expo' ? join(folder, 'server') : app;
14
+ const source = await exists(join(client, 'src')) ? 'src' : '';
15
+ const serverSource = await exists(join(server, 'src/app')) ? 'src' : '';
16
+ await mkdir(join(app, source, setup.platform === 'expo' ? '' : 'app'), { recursive: true });
17
+ await mkdir(join(backend, serverSource, 'app'), { recursive: true });
18
+ const provider = setup.auth === 'supabase' ? { '@supabase/supabase-js': '^2' } : setup.auth === 'firebase' ? { firebase: '^13' } : {};
19
+ await writeFile(join(app, 'package.json'), JSON.stringify({ private: true, dependencies: { ...(setup.platform === 'expo' ? { expo: '~55.0.0' } : { next: '16.4.5' }), ...provider } }));
20
+ if (backend !== app)
21
+ await writeFile(join(backend, 'package.json'), JSON.stringify({ private: true, dependencies: { next: '16.4.5' } }));
22
+ if (setup.platform === 'expo')
23
+ await writeFile(join(app, 'app.json'), JSON.stringify({ expo: { name: 'Legacy reconstruction', plugins: [] } }));
24
+ // These are five bundled, first-party generator modules from the verified 0.21.0 archive.
25
+ const legacy = await import(new URL('../legacy/0.21.0/messenger-install.js', import.meta.url).href);
26
+ const plan = await legacy.planMessengerInstallation({ ...options, project: app, ...(backend !== app ? { serverProject: backend } : {}) });
27
+ const hashes = {};
28
+ for (const file of plan.files) {
29
+ const inServer = backend !== app && !relative(backend, file.path).startsWith('..');
30
+ const target = join(inServer ? server : client, relative(inServer ? backend : app, file.path));
31
+ hashes[generatedKey(target, client, server)] = [generatedHash(file.content)];
32
+ }
33
+ return hashes;
34
+ }
35
+ finally {
36
+ await rm(folder, { recursive: true, force: true });
37
+ }
38
+ }
39
+ /** Changed customer files are never replaced or automatically merged. */
40
+ export async function planMessengerUpgrade(directory, options = {}) {
41
+ const root = resolve(directory), setupFile = join(root, '.robono/setup.json');
42
+ await safePath(setupFile);
43
+ const { text: original, setup } = await readMessengerSetup(setupFile);
44
+ if (![1, 2, 3].includes(setup.schema) || !['web', 'expo'].includes(setup.platform) || !['custom', 'supabase', 'firebase'].includes(setup.auth) || typeof setup.origin !== 'string' || typeof setup.serverProject !== 'string')
45
+ throw Error('Invalid saved installation. Existing files were not changed.');
46
+ const companyName = options.companyName ?? setup.companyName;
47
+ if (options.companyName && setup.companyName && options.companyName !== setup.companyName)
48
+ throw Error('Use the saved company name for upgrades; change branding separately.');
49
+ if (typeof companyName !== 'string' || !companyName.trim())
50
+ throw Error('This older installation did not save the company name. Add --company with your existing company name. Existing files were not changed.');
51
+ const server = resolve(root, setup.serverProject);
52
+ const input = { project: root, origin: setup.origin, companyName, auth: setup.auth, ...(setup.platform === 'expo' ? { serverProject: server } : {}) };
53
+ const desired = await buildMessengerInstallation(input, 'upgrade');
54
+ const desiredMeta = desired.files.find(file => file.path === setupFile);
55
+ const next = JSON.parse(desiredMeta.content);
56
+ if (setup.schema === 3 && (setup.layout !== next.layout || setup.clientBase !== next.clientBase || setup.serverBase !== next.serverBase))
57
+ throw Error('The project layout changed since installation. Move the integration deliberately before upgrading; no files were changed.');
58
+ const old = setup.schema === 3 ? Object.fromEntries(Object.entries(setup.generated ?? {}).filter((entry) => typeof entry[1] === 'string' && /^[a-f0-9]{64}$/.test(entry[1])).map(([key, value]) => [key, [value]])) : await legacyBaselines(setup, input, root, server);
59
+ const hashes = {};
60
+ const files = [], replacements = [];
61
+ const report = { version: messengerInstallerVersion, updated: [], added: [], preserved: [], conflicts: [], backups: [] };
62
+ const stamp = messengerInstallerVersion + '-' + randomUUID();
63
+ const artifact = (kind, key) => {
64
+ const [scope, ...parts] = key.split(':');
65
+ const local = parts.join(':');
66
+ if (!['client', 'server'].includes(scope) || isAbsolute(local) || local.split(/[\\/]/).includes('..'))
67
+ throw Error('Invalid generated file path.');
68
+ return join(root, '.robono', kind, stamp, scope, local);
69
+ };
70
+ for (const file of desired.files) {
71
+ if (file.path === setupFile || file.path.endsWith('.before-install'))
72
+ continue;
73
+ await safePath(file.path);
74
+ const key = generatedKey(file.path, root, server), wanted = generatedHash(file.content), baseline = old[key];
75
+ const present = await exists(file.path), current = present ? await readFile(file.path, 'utf8') : null;
76
+ if (current !== null && generatedHash(current) === wanted) {
77
+ hashes[key] = wanted;
78
+ continue;
79
+ }
80
+ if (current === null && !baseline) {
81
+ files.push(file);
82
+ hashes[key] = wanted;
83
+ report.added.push(file.path);
84
+ continue;
85
+ }
86
+ if (current !== null && baseline?.includes(generatedHash(current))) {
87
+ const backup = artifact('backups', key);
88
+ files.push({ path: backup, content: current });
89
+ report.backups.push(backup);
90
+ replacements.push({ path: file.path, before: current, after: file.content });
91
+ hashes[key] = wanted;
92
+ report.updated.push(file.path);
93
+ continue;
94
+ }
95
+ if (current !== null && baseline?.includes(wanted)) {
96
+ hashes[key] = wanted;
97
+ report.preserved.push(file.path);
98
+ continue;
99
+ }
100
+ // Unknown legacy edits and customer deletions require review. Only proposed generated content is saved.
101
+ let proposed = artifact('updates', key).replace(stamp, messengerInstallerVersion);
102
+ await safePath(proposed);
103
+ if (await exists(proposed)) {
104
+ if (await readFile(proposed, 'utf8') !== file.content) {
105
+ proposed = artifact('updates', key);
106
+ files.push({ path: proposed, content: file.content });
107
+ }
108
+ }
109
+ else
110
+ files.push({ path: proposed, content: file.content });
111
+ report.conflicts.push({ file: file.path, proposed, reason: current === null ? 'Previously generated file was removed.' : 'This file was edited, or its original contents could not be verified.' });
112
+ if (baseline?.[0])
113
+ hashes[key] = baseline[0];
114
+ }
115
+ // Expo plugin additions are structural merges preserving existing configuration.
116
+ for (const file of desired.replacements) {
117
+ const key = generatedKey(file.path, root, server), backup = artifact('backups', key);
118
+ files.push({ path: backup, content: file.before });
119
+ report.backups.push(backup);
120
+ replacements.push(file);
121
+ report.updated.push(file.path);
122
+ }
123
+ next.generated = hashes;
124
+ next.pendingReview = report.conflicts.map(conflict => generatedKey(conflict.file, root, server));
125
+ const alreadyCurrent = setup.installerVersion === messengerInstallerVersion && setup.dependencies === 'installed';
126
+ next.dependencies = alreadyCurrent ? 'installed' : 'pending';
127
+ replacements.push({ path: setupFile, before: original, after: JSON.stringify(next, null, 2) + '\n' });
128
+ const reportPath = join(root, '.robono/upgrade-report.json'), content = JSON.stringify(report, null, 2) + '\n';
129
+ await safePath(reportPath);
130
+ if (await exists(reportPath))
131
+ replacements.push({ path: reportPath, before: await readFile(reportPath, 'utf8'), after: content });
132
+ else
133
+ files.push({ path: reportPath, content });
134
+ return { ...desired, files, replacements: replacements.filter(file => file.before !== file.after), commands: alreadyCurrent ? [] : desired.commands, notes: [`Upgrade to ${messengerInstallerVersion}: ${report.updated.length} updates, ${report.added.length} additions, ${report.conflicts.length} files need review.`, 'Edited files are preserved. Proposed changes and backup paths are listed in .robono/upgrade-report.json. Review changes to account permissions before applying them.'], upgradeReport: report };
135
+ }
136
+ //# sourceMappingURL=messenger-upgrade.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"messenger-upgrade.js","sourceRoot":"","sources":["../src/messenger-upgrade.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { MessengerStarterOptions } from './messenger-init.js';
2
+ /** Preserve the customer's Vite configuration and application entry points. */
3
+ export declare function viteMessengerFiles(options: MessengerStarterOptions, clientBase: string): {
4
+ [x: string]: string;
5
+ '.robono/package.json': string;
6
+ '.robono/.gitignore': string;
7
+ '.robono/server/tsconfig.json': string;
8
+ '.robono/dev.ts': string;
9
+ '.robono/build.ts': string;
10
+ '.robono/server/start.ts': string;
11
+ };
12
+ //# sourceMappingURL=messenger-vite-templates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"messenger-vite-templates.d.ts","sourceRoot":"","sources":["../src/messenger-vite-templates.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,uBAAuB,EAAC,MAAM,qBAAqB,CAAC;AAEjE,+EAA+E;AAC/E,wBAAgB,kBAAkB,CAAC,OAAO,EAAC,uBAAuB,EAAC,UAAU,EAAC,MAAM;;;;;;;;EAsFnF"}
@@ -0,0 +1,89 @@
1
+ /** Preserve the customer's Vite configuration and application entry points. */
2
+ export function viteMessengerFiles(options, clientBase) {
3
+ const entry = '/' + [clientBase, 'robono/client/preview-entry.tsx'].filter(Boolean).join('/');
4
+ const env = `for(const file of ['.env.local','.env']){try{loadEnvFile(resolve(root,file));}catch(error){if((error as NodeJS.ErrnoException).code!=='ENOENT')throw error;}}`;
5
+ return {
6
+ '.robono/package.json': JSON.stringify({ private: true, type: 'module', scripts: { dev: 'tsx dev.ts', build: 'tsx build.ts', start: 'node server-build/start.js' } }, null, 2) + '\n',
7
+ '.robono/.gitignore': '.env*\nserver-build/\nbuild-info.json\nbackups/\nupdates/\ninstall.lock\n',
8
+ '.robono/server/tsconfig.json': JSON.stringify({ compilerOptions: { target: 'ES2022', module: 'NodeNext', moduleResolution: 'NodeNext', strict: true, skipLibCheck: true, outDir: '../server-build', types: ['node'] }, include: ['*.ts'] }, null, 2) + '\n',
9
+ '.robono/dev.ts': `import {createServer as createHttpServer} from 'node:http';
10
+ import {loadEnvFile} from 'node:process';
11
+ import {resolve} from 'node:path';
12
+ import {fileURLToPath} from 'node:url';
13
+ import {createServer as createViteServer} from 'vite';
14
+ import {createMessengerNodeHandler} from '@robono/server/node';
15
+ import {getMessenger} from './server/robono.js';
16
+ const root=fileURLToPath(new URL('../',import.meta.url));
17
+ process.chdir(root);
18
+ ${env}
19
+ process.env.NODE_ENV='development';
20
+ let handle:ReturnType<typeof createMessengerNodeHandler>;
21
+ let port=0;
22
+ const server=createHttpServer({requestTimeout:30000,headersTimeout:15000},async(req,res)=>{
23
+ if(!['localhost:'+port,'127.0.0.1:'+port].includes(req.headers.host??'')){res.writeHead(403);res.end('Unknown development host.');return;}
24
+ let path:string;try{path=decodeURIComponent((req.url??'/').split('?')[0]!);}catch{res.writeHead(400);res.end();return;}
25
+ if(path.split(/[\\\\/]/).some(part=>part==='.robono')||path.startsWith('/@fs/')){res.writeHead(404);res.end();return;}
26
+ if(await handle(req,res))return;
27
+ if(path==='/robono-preview'){
28
+ const html=await vite.transformIndexHtml('/robono-preview',${JSON.stringify('<!doctype html><html><head><meta name="viewport" content="width=device-width,initial-scale=1"></head><body><div id="robono-preview"></div><script type="module" src="' + entry + '"></script></body></html>')});
29
+ res.writeHead(200,{'content-type':'text/html; charset=utf-8','cache-control':'no-store'});res.end(html);return;
30
+ }
31
+ vite.middlewares(req,res,()=>{res.writeHead(404);res.end();});
32
+ });
33
+ const vite=await createViteServer({root,server:{middlewareMode:true,hmr:{server},fs:{strict:true,deny:['.env','.env.*','*.{crt,pem}','**/.git/**','**/.robono/**']}}});
34
+ if(resolve(vite.config.root)!==resolve(root))throw Error('This automatic setup expects the Vite root to be the project directory. Use the generated server handler with a custom-root project.');
35
+ port=Number(process.env.ROBONO_DEV_PORT??vite.config.server.port??5173);
36
+ if(!Number.isInteger(port)||port<1||port>65535)throw Error('Choose a development port from 1 to 65535.');
37
+ // Explicit loopback origins only, and only for this loopback-bound development process.
38
+ process.env.ROBONO_ALLOWED_ORIGINS=[process.env.ROBONO_ALLOWED_ORIGINS,${JSON.stringify(options.origin)},'http://localhost:'+port,'http://127.0.0.1:'+port].filter(Boolean).join(',');
39
+ handle=createMessengerNodeHandler({getMessenger,origin:'http://127.0.0.1:'+port});
40
+ await new Promise<void>((done,fail)=>{server.once('error',fail);server.listen(port,'127.0.0.1',done);});
41
+ console.log('Your website: http://127.0.0.1:'+port+' · Local messenger preview: /robono-preview');
42
+ for(const signal of ['SIGINT','SIGTERM'] as const)process.once(signal,()=>{void vite.close().then(()=>{server.closeAllConnections();server.close(()=>process.exit());});});
43
+ `,
44
+ '.robono/build.ts': `import {build} from 'vite';
45
+ import {resolve,relative,isAbsolute} from 'node:path';
46
+ import {fileURLToPath} from 'node:url';
47
+ import {writeFileSync} from 'node:fs';
48
+ import {createRequire} from 'node:module';
49
+ import {execFileSync} from 'node:child_process';
50
+ const root=fileURLToPath(new URL('../',import.meta.url));
51
+ process.chdir(root);
52
+ let output='';
53
+ await build({root,plugins:[{name:'robono-build-location',configResolved(config){
54
+ if(resolve(config.root)!==resolve(root))throw Error('Custom Vite roots need a manual server connection.');
55
+ output=relative(root,resolve(root,config.build.outDir));
56
+ if(!output||isAbsolute(output)||output.startsWith('..')||output.split(/[\\\\/]/).some(part=>part.startsWith('.')))throw Error('Use a dedicated build output directory inside this project.');
57
+ },transform(_code,id){const file=id.split('?')[0]!;const local=relative(root,file);if(local.split(/[\\\\/]/).includes('.robono'))throw Error('Private Robono server files cannot be imported into the website.');}}]});
58
+ const require=createRequire(import.meta.url);
59
+ execFileSync(process.execPath,[require.resolve('typescript/bin/tsc'),'--project',resolve(root,'.robono/server/tsconfig.json')],{stdio:'inherit'});
60
+ writeFileSync(resolve(root,'.robono/build-info.json'),JSON.stringify({staticDirectory:output})+'\\n');
61
+ `,
62
+ '.robono/server/start.ts': `import {createMessengerWebsiteServer} from '@robono/server/node';
63
+ import {loadEnvFile} from 'node:process';
64
+ import {readFile} from 'node:fs/promises';
65
+ import {resolve,relative,isAbsolute} from 'node:path';
66
+ import {fileURLToPath} from 'node:url';
67
+ import {getMessenger} from './robono.js';
68
+ const root=fileURLToPath(new URL('../../',import.meta.url));
69
+ process.chdir(root);
70
+ ${env}
71
+ process.env.NODE_ENV='production';
72
+ const build=JSON.parse(await readFile(resolve(root,'.robono/build-info.json'),'utf8'));
73
+ if(typeof build.staticDirectory!=='string')throw Error('Run the Robono build command first.');
74
+ const local=relative(root,resolve(root,build.staticDirectory));
75
+ if(!local||isAbsolute(local)||local.startsWith('..')||local.split(/[\\\\/]/).some(part=>part.startsWith('.')))throw Error('Invalid website build location.');
76
+ const origin=process.env.ROBONO_PUBLIC_ORIGIN??${JSON.stringify(options.origin)};
77
+ const server=await createMessengerWebsiteServer({getMessenger,origin,staticDirectory:resolve(root,local)});
78
+ const port=Number(process.env.PORT??3000);
79
+ if(!Number.isInteger(port)||port<1||port>65535)throw Error('Choose a server port from 1 to 65535.');
80
+ server.listen(port,process.env.HOST??'0.0.0.0',()=>console.log('Website and messaging server listening on port '+port));
81
+ `,
82
+ [entry.slice(1)]: `import {createRoot} from 'react-dom/client';
83
+ import Preview from './RobonoPreview';
84
+ if(import.meta.env.DEV)createRoot(document.getElementById('robono-preview')!).render(<Preview/>);
85
+ `,
86
+ [[clientBase, 'robono/client/vite-env.d.ts'].filter(Boolean).join('/')]: '/// <reference types="vite/client" />\n',
87
+ };
88
+ }
89
+ //# sourceMappingURL=messenger-vite-templates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"messenger-vite-templates.js","sourceRoot":"","sources":["../src/messenger-vite-templates.ts"],"names":[],"mappings":"AAEA,+EAA+E;AAC/E,MAAM,UAAU,kBAAkB,CAAC,OAA+B,EAAC,UAAiB;IACnF,MAAM,KAAK,GAAC,GAAG,GAAC,CAAC,UAAU,EAAC,iCAAiC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACzF,MAAM,GAAG,GAAC,+JAA+J,CAAC;IAC1K,OAAO;QACN,sBAAsB,EAAC,IAAI,CAAC,SAAS,CAAC,EAAC,OAAO,EAAC,IAAI,EAAC,IAAI,EAAC,QAAQ,EAAC,OAAO,EAAC,EAAC,GAAG,EAAC,YAAY,EAAC,KAAK,EAAC,cAAc,EAAC,KAAK,EAAC,4BAA4B,EAAC,EAAC,EAAC,IAAI,EAAC,CAAC,CAAC,GAAC,IAAI;QAClK,oBAAoB,EAAC,2EAA2E;QAChG,8BAA8B,EAAC,IAAI,CAAC,SAAS,CAAC,EAAC,eAAe,EAAC,EAAC,MAAM,EAAC,QAAQ,EAAC,MAAM,EAAC,UAAU,EAAC,gBAAgB,EAAC,UAAU,EAAC,MAAM,EAAC,IAAI,EAAC,YAAY,EAAC,IAAI,EAAC,MAAM,EAAC,iBAAiB,EAAC,KAAK,EAAC,CAAC,MAAM,CAAC,EAAC,EAAC,OAAO,EAAC,CAAC,MAAM,CAAC,EAAC,EAAC,IAAI,EAAC,CAAC,CAAC,GAAC,IAAI;QACnO,gBAAgB,EAAC;;;;;;;;;EASjB,GAAG;;;;;;;;;;+DAU0D,IAAI,CAAC,SAAS,CAAC,uKAAuK,GAAC,KAAK,GAAC,2BAA2B,CAAC;;;;;;;;;;yEAU/M,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC;;;;;CAKtG;QACC,kBAAkB,EAAC;;;;;;;;;;;;;;;;;CAiBpB;QACC,yBAAyB,EAAC;;;;;;;;EAQ1B,GAAG;;;;;;iDAM4C,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC;;;;;CAK9E;QACC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAC;;;CAGlB;QACC,CAAC,CAAC,UAAU,EAAC,6BAA6B,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAC,yCAAyC;KAChH,CAAC;AACH,CAAC"}
@@ -0,0 +1,138 @@
1
+ import { customerMessengerTemplates } from './messenger-customer-templates.js';
2
+ export function connectStarterTemplates(files, options) {
3
+ const provider = options.auth ?? 'custom', web = options.platform === 'web', q = JSON.stringify;
4
+ if (!['custom', 'supabase', 'firebase'].includes(provider))
5
+ throw Error('Choose custom, supabase or firebase sign-in.');
6
+ if (provider !== 'custom') {
7
+ const setup = provider === 'supabase' ? `import {createClient} from '@supabase/supabase-js';
8
+ import {supabaseMessengerAuthentication} from '@robono/server';
9
+ let verify:ReturnType<typeof supabaseMessengerAuthentication>|undefined;
10
+ export async function authenticate(request:Request){
11
+ if(verify)return verify(request);
12
+ const url=process.env.SUPABASE_URL??process.env.NEXT_PUBLIC_SUPABASE_URL;
13
+ const key=process.env.SUPABASE_PUBLISHABLE_KEY??process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY??process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
14
+ if(!url||!key)throw Error('Set your existing Supabase URL and publishable key on the server.');
15
+ const provider=createClient(url,key,{auth:{persistSession:false,autoRefreshToken:false,detectSessionInUrl:false}});
16
+ verify=supabaseMessengerAuthentication(provider.auth);
17
+ return verify(request);
18
+ }` : `import {getApps,initializeApp,applicationDefault} from 'firebase-admin/app';
19
+ import {getAuth} from 'firebase-admin/auth';
20
+ import {firebaseMessengerAuthentication} from '@robono/server';
21
+ // Use the same Firebase project as your existing sign-in. Keep credentials on the server.
22
+ let verify:ReturnType<typeof firebaseMessengerAuthentication>|undefined;
23
+ export async function authenticate(request:Request){
24
+ if(verify)return verify(request);
25
+ const provider=getApps().find(app=>app.name==='robono-auth')??initializeApp({credential:applicationDefault()},'robono-auth');
26
+ verify=firebaseMessengerAuthentication(getAuth(provider));
27
+ return verify(request);
28
+ }`;
29
+ files['server/robono-access.ts'] = `import type {MessengerAccess} from '@robono/server';\n${setup}\n\n// Keep YOUR current eligibility, suspension and membership checks here.
30
+ // Signing in does not grant permission to every conversation.
31
+ export async function authorize(access:MessengerAccess):Promise<boolean>{
32
+ throw Error('Connect your existing messaging permission check in robono-access.ts');
33
+ }
34
+ `;
35
+ files['.env.example'] += provider === 'supabase' ? '# Existing NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY\n# (or NEXT_PUBLIC_SUPABASE_ANON_KEY) are reused when present. Otherwise set:\n# SUPABASE_URL=\n# SUPABASE_PUBLISHABLE_KEY=\n' : '# Server only: path to your existing Firebase service-account file, or workload identity.\nGOOGLE_APPLICATION_CREDENTIALS=\n';
36
+ }
37
+ files['client/RobonoChat.tsx'] = web ? `'use client';
38
+ import {useEffect,useRef} from 'react';
39
+ import {mountRobonoMessenger} from '@robono/messaging-web';
40
+ type Options=Parameters<typeof mountRobonoMessenger>[1];
41
+ export function RobonoChat({userId,getAccessToken,contacts,onReady,onSignOut}:{userId:string;getAccessToken:Options['getAccessToken'];contacts?:Options['contacts'];onSignOut?:()=>Promise<void>;onReady?:(chat:ReturnType<typeof mountRobonoMessenger>|null)=>void}){
42
+ const element=useRef<HTMLDivElement>(null),latest=useRef({userId,getAccessToken,onReady,onSignOut}),active=useRef<ReturnType<typeof mountRobonoMessenger>|null>(null);
43
+ latest.current={userId,getAccessToken,onReady,onSignOut};
44
+ useEffect(()=>{
45
+ if(!element.current)return;
46
+ // Capture this account's token callback; never send another account's token for it.
47
+ const token=async()=>{if(latest.current.userId!==userId)throw Error('Account changed.');const value=await latest.current.getAccessToken();if(latest.current.userId!==userId)throw Error('Account changed.');return value;};
48
+ const chat=mountRobonoMessenger(element.current,{endpoint:'/api/robono',companyName:${q(options.companyName)},userId,getAccessToken:token,contacts,account:{name:'Account',onSignOut:async()=>{await latest.current.onSignOut?.();}}});
49
+ active.current=chat;latest.current.onReady?.(chat);
50
+ return()=>{active.current=null;latest.current.onReady?.(null);chat.unmount();};
51
+ },[userId]);
52
+ useEffect(()=>{active.current?.updateContacts(contacts);},[contacts]);
53
+ return <div ref={element} style={{height:'100%',minHeight:400}}/>;
54
+ }
55
+ ` : `import React,{useEffect,useRef,useState} from 'react';
56
+ import {createExpoMessenger,ExpoMessenger,type ExpoMessengerProps} from '@robono/messaging-react-native/expo';
57
+ type Connection=ReturnType<typeof createExpoMessenger>;
58
+ export function RobonoChat({userId,getAccessToken,contacts,isCallActive,onReady,onSignOut}:{userId:string;getAccessToken:()=>Promise<string>;contacts?:ExpoMessengerProps['contacts'];isCallActive?:()=>boolean;onSignOut?:()=>Promise<void>;onReady?:(connection:Connection|null)=>void}){
59
+ const latest=useRef({userId,getAccessToken,isCallActive,onReady});latest.current={userId,getAccessToken,isCallActive,onReady};
60
+ const [active,setActive]=useState<{userId:string;connection:Connection}|null>(null);
61
+ useEffect(()=>{
62
+ const token=async()=>{if(latest.current.userId!==userId)throw Error('Account changed.');const value=await latest.current.getAccessToken();if(latest.current.userId!==userId)throw Error('Account changed.');return value;};
63
+ const connection=createExpoMessenger({endpoint:${q(options.origin + '/api/robono')},userId,getAccessToken:token,isCallActive:()=>latest.current.isCallActive?.()??false});
64
+ setActive({userId,connection});latest.current.onReady?.(connection);
65
+ return()=>{latest.current.onReady?.(null);connection.dispose();};
66
+ },[userId]);
67
+ if(!active||active.userId!==userId)return null;
68
+ return <ExpoMessenger {...active.connection} userId={userId} companyName={${q(options.companyName)}} contacts={contacts} account={onSignOut?{name:'Account',onSignOut}:undefined}/>;
69
+ }
70
+ `;
71
+ files['client/ConnectedRobonoChat.tsx'] = `'use client';
72
+ import React,{useEffect,useRef,useState} from 'react';
73
+ import {RobonoChat} from './RobonoChat';
74
+ import type {ComponentProps} from 'react';
75
+ type ChatProps=ComponentProps<typeof RobonoChat>;
76
+ export interface RobonoAccount {id:string;getAccessToken:()=>Promise<string>}
77
+ export interface RobonoAccounts {
78
+ // Call the listener immediately with the current account, then on every account change.
79
+ subscribe(listener:(account:RobonoAccount|null)=>void):()=>void;
80
+ signOut():Promise<unknown>;
81
+ }
82
+ /** Keep mounted within your signed-in app. No second login or copied session is required. */
83
+ export function ConnectedRobonoChat({accounts,...props}:Omit<ChatProps,'userId'|'getAccessToken'|'onReady'|'onSignOut'>&{accounts:RobonoAccounts}){
84
+ const [account,setAccount]=useState<RobonoAccount|null>(null),current=useRef<RobonoAccount|null>(null);
85
+ const sourceVersion=useRef(0),[version,setVersion]=useState(0);
86
+ const chat=useRef<Parameters<NonNullable<ChatProps['onReady']>>[0]>(null);
87
+ useEffect(()=>{
88
+ let live=true;setVersion(++sourceVersion.current);
89
+ if(current.current){void chat.current?.signOut().catch(()=>{});chat.current=null;current.current=null;setAccount(null);}
90
+ const off=accounts.subscribe(next=>{
91
+ if(!live)return;
92
+ if(current.current?.id!==next?.id){void chat.current?.signOut().catch(()=>{});chat.current=null;}
93
+ current.current=next;setAccount(next);
94
+ });
95
+ return()=>{live=false;sourceVersion.current++;current.current=null;off();};
96
+ },[accounts]);
97
+ const token=async()=>{const active=current.current;if(!active||active.id!==account?.id||sourceVersion.current!==version)throw Error('Account changed.');const value=await active.getAccessToken();if(current.current?.id!==active.id||sourceVersion.current!==version)throw Error('Account changed.');return value;};
98
+ return account?<RobonoChat key={version+':'+account.id} {...props} userId={account.id} getAccessToken={token} onReady={value=>{chat.current=value;}} onSignOut={async()=>{try{await chat.current?.signOut();}finally{await accounts.signOut();}}}/>:null;
99
+ }
100
+ // Before your app signs out, await its messenger handle's signOut when available.
101
+ // A provider sign-out/account-change event also clears chat immediately. Notification removal
102
+ // may require a valid old session; revoke that session and synchronize account suspension too.
103
+ `;
104
+ if (provider === 'supabase')
105
+ files['client/robono-accounts.ts'] = `import type {SupabaseClient} from '@supabase/supabase-js';
106
+ import type {RobonoAccounts,RobonoAccount} from './ConnectedRobonoChat';
107
+ export function robonoAccounts(client:SupabaseClient):RobonoAccounts{
108
+ const account=(id:string):RobonoAccount=>({id,getAccessToken:async()=>{
109
+ const {data,error}=await client.auth.getSession();
110
+ if(error||data.session?.user.id!==id)throw Error('Sign in again.');
111
+ return data.session.access_token;
112
+ }});
113
+ return {subscribe(listener){
114
+ let live=true,events=0;
115
+ const {data}=client.auth.onAuthStateChange((_event,session)=>{events++;if(live)listener(session?account(session.user.id):null);});
116
+ const initial=events;
117
+ void client.auth.getSession().then(({data,error})=>{if(live&&events===initial)listener(!error&&data.session?account(data.session.user.id):null);}).catch(()=>{if(live&&events===initial)listener(null);});
118
+ return()=>{live=false;data.subscription.unsubscribe();};
119
+ },signOut:()=>client.auth.signOut()};
120
+ }
121
+ `;
122
+ if (provider === 'firebase')
123
+ files['client/robono-accounts.ts'] = `import {onIdTokenChanged,signOut,type Auth} from 'firebase/auth';
124
+ import type {RobonoAccounts} from './ConnectedRobonoChat';
125
+ export function robonoAccounts(auth:Auth):RobonoAccounts{
126
+ return {subscribe(listener){return onIdTokenChanged(auth,user=>listener(user?{id:user.uid,getAccessToken:async()=>{
127
+ if(auth.currentUser?.uid!==user.uid)throw Error('Sign in again.');
128
+ const token=await user.getIdToken();
129
+ if(auth.currentUser?.uid!==user.uid)throw Error('Sign in again.');
130
+ return token;
131
+ }}:null),()=>listener(null));},signOut:()=>signOut(auth)};
132
+ }
133
+ `;
134
+ files['ROBONO-SETUP.md'] += `\n## Connected accounts\n${provider === 'custom' ? 'Implement the small RobonoAccounts interface using your existing verified sign-in client.' : 'Import CustomerMessenger and pass your existing ' + (provider === 'supabase' ? 'Supabase client' : 'Firebase Auth instance') + ' as auth. The account wrapper is included.'} Pass your current contact dictionary as contacts; names and avatars update without restarting the chat. The connected screen listens for sign-in, sign-out and account switching. It uses a fresh token for each request and never substitutes one account’s token for another.\n\nKeep CustomerMessenger mounted in your signed-in application. Add setupChecks during development for a connection check using your existing login; no copied tokens are needed. The panel is hidden in production. Keep your own account suspension and permission checks in robono-access.ts; provider identity verification does not replace them. Missing permission code intentionally denies access. For explicit host sign-out, unregister messaging before revoking your session when possible. If the provider session has already expired, local content is still cleared; synchronize revoked account access on the server so notifications are also blocked.\n`;
135
+ Object.assign(files, customerMessengerTemplates(options));
136
+ return files;
137
+ }
138
+ //# sourceMappingURL=messenger-connected-templates.js.map