yap2app 1.1.2 → 1.1.4

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/bridge.js CHANGED
@@ -12,18 +12,21 @@ const fs = fsSync.promises || {
12
12
  stat: util.promisify(fsSync.stat)
13
13
  };
14
14
 
15
- const PORT = process.env.YAP2APP_PORT || 10420;
15
+ const PORT = Number(process.env.YAP2APP_PORT) || 10420;
16
16
  const PROJECT_ROOT = process.cwd();
17
17
 
18
18
  // Bulletproof W3C Private Network Access (PNA) and Dynamic Origin CORS Headers
19
19
  function getCorsHeaders(req) {
20
20
  const origin = req?.headers?.origin || '*';
21
+ const requestedHeaders = req?.headers?.['access-control-request-headers'] || '*';
21
22
  return {
22
23
  'Access-Control-Allow-Origin': origin,
23
- 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS, PUT, DELETE, HEAD',
24
- 'Access-Control-Allow-Headers': req?.headers?.['access-control-request-headers'] || 'Content-Type, Authorization, Access-Control-Request-Private-Network, X-Requested-With, *',
24
+ 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS, PUT, DELETE, HEAD, PATCH',
25
+ 'Access-Control-Allow-Headers': requestedHeaders === '*' ? 'Content-Type, Authorization, Access-Control-Request-Private-Network, X-Requested-With, Accept, Origin, *' : requestedHeaders,
25
26
  'Access-Control-Allow-Private-Network': 'true',
27
+ 'Access-Control-Allow-Credentials': 'true',
26
28
  'Access-Control-Max-Age': '86400',
29
+ 'Vary': 'Origin, Access-Control-Request-Headers, Access-Control-Request-Private-Network',
27
30
  'Content-Type': 'application/json'
28
31
  };
29
32
  }
@@ -83,25 +86,41 @@ const server = http.createServer(async (req, res) => {
83
86
  return;
84
87
  }
85
88
 
86
- // 2. Health & Root Info
87
- if (req.method === 'GET' && (req.url === '/' || req.url === '/health' || req.url === '/api/health')) {
89
+ let pathname = '/';
90
+ let searchParams = new URLSearchParams();
91
+ try {
92
+ const parsedUrl = new URL(req.url, `http://${req.headers.host || '127.0.0.1:10420'}`);
93
+ pathname = parsedUrl.pathname.replace(/\/+$/, '') || '/';
94
+ searchParams = parsedUrl.searchParams;
95
+ } catch {
96
+ pathname = (req.url || '/').split('?')[0].replace(/\/+$/, '') || '/';
97
+ }
98
+
99
+ // 2. Health & Root Info (fast ping endpoints)
100
+ if (req.method === 'GET' && (
101
+ pathname === '/' ||
102
+ pathname === '/health' ||
103
+ pathname === '/api/health' ||
104
+ pathname === '/ping' ||
105
+ pathname === '/api/ping'
106
+ )) {
88
107
  res.writeHead(200, headers);
89
108
  res.end(JSON.stringify({
90
109
  status: 'healthy',
91
110
  service: 'Yap2App-Localhost-Bridge',
92
- version: '1.0.0',
111
+ version: '1.1.4',
93
112
  port: PORT,
94
113
  project_root: PROJECT_ROOT,
95
- project_name: path.basename(PROJECT_ROOT)
114
+ project_name: path.basename(PROJECT_ROOT),
115
+ ready: true
96
116
  }));
97
117
  return;
98
118
  }
99
119
 
100
120
  // 3. GET /api/context (Shallow Codebase Scanner with Optional Prompt Query)
101
- if (req.method === 'GET' && req.url.startsWith('/api/context')) {
121
+ if (req.method === 'GET' && (pathname === '/api/context' || pathname === '/context')) {
102
122
  try {
103
- const parsedUrl = new URL(req.url, `http://${req.headers.host || 'localhost'}`);
104
- const promptQuery = parsedUrl.searchParams.get('q') || '';
123
+ const promptQuery = searchParams.get('q') || '';
105
124
  console.log(`[Yap2App Bridge] 🔍 Scanning shallow codebase context in: ${PROJECT_ROOT}${promptQuery ? ` (ranking for query: "${promptQuery}")` : ''}`);
106
125
  const context = await scanCodebase(PROJECT_ROOT, promptQuery);
107
126
  res.writeHead(200, headers);
@@ -115,7 +134,7 @@ const server = http.createServer(async (req, res) => {
115
134
  }
116
135
 
117
136
  // 4. POST /api/diff (Enterprise Diff & Safety Gate Review)
118
- if (req.method === 'POST' && req.url === '/api/diff') {
137
+ if (req.method === 'POST' && (pathname === '/api/diff' || pathname === '/diff')) {
119
138
  let body = '';
120
139
  req.on('data', chunk => body += chunk.toString());
121
140
  req.on('end', async () => {
@@ -159,7 +178,7 @@ const server = http.createServer(async (req, res) => {
159
178
  }
160
179
 
161
180
  // 5. POST /api/write (Approved Write to Disk)
162
- if (req.method === 'POST' && req.url === '/api/write') {
181
+ if (req.method === 'POST' && (pathname === '/api/write' || pathname === '/write')) {
163
182
  let body = '';
164
183
  req.on('data', chunk => body += chunk.toString());
165
184
  req.on('end', async () => {
@@ -199,11 +218,24 @@ const server = http.createServer(async (req, res) => {
199
218
 
200
219
  // 404 Fallback
201
220
  res.writeHead(404, headers);
202
- res.end(JSON.stringify({ error: 'Endpoint Not Found' }));
221
+ res.end(JSON.stringify({ error: 'Endpoint Not Found', path: pathname }));
222
+ });
223
+
224
+ // Gracefully handle EADDRINUSE without crashing
225
+ server.on('error', (err) => {
226
+ if (err && err.code === 'EADDRINUSE') {
227
+ console.log(`\n===============================================================`);
228
+ console.log(`📡 Yap2App Localhost Bridge is ALREADY active on port ${PORT}!`);
229
+ console.log(`📁 Active Workspace: ${PROJECT_ROOT}`);
230
+ console.log(`🌐 Ready for 1-click sync from Yap2App Web Studio UI...`);
231
+ console.log(`===============================================================\n`);
232
+ return;
233
+ }
234
+ console.error('[Yap2App Bridge Error]:', err);
203
235
  });
204
236
 
205
237
  export function startBridge(port = PORT) {
206
- server.listen(port, () => {
238
+ server.listen(port, '0.0.0.0', () => {
207
239
  console.log(`\n===============================================================`);
208
240
  console.log(`🚀 Yap2App Localhost Bridge & Shallow Scanner is ACTIVE!`);
209
241
  console.log(`📡 Listening on: http://127.0.0.1:${port}`);
@@ -215,6 +247,8 @@ export function startBridge(port = PORT) {
215
247
  }
216
248
 
217
249
  // Direct execution
218
- if (import.meta.url === `file://${process.argv[1]}` || process.argv[1]?.endsWith('bridge.js')) {
250
+ const isDirectRun = Boolean(typeof process !== 'undefined' && process.argv && process.argv[1] && (process.argv[1].endsWith('bridge.js') || process.argv[1].endsWith('bridge')));
251
+ if (isDirectRun) {
219
252
  startBridge();
220
253
  }
254
+
package/mcp.js CHANGED
@@ -158,7 +158,8 @@ export async function runMcpServer() {
158
158
  console.error("⚡ Yap2App MCP Server running on stdio");
159
159
  }
160
160
 
161
- if (import.meta.url === `file://${process.argv[1]}` || process.argv[1]?.endsWith('mcp.js')) {
161
+ const isDirectRun = Boolean(typeof process !== 'undefined' && process.argv && process.argv[1] && (process.argv[1].endsWith('mcp.js') || process.argv[1].endsWith('mcp')));
162
+ if (isDirectRun) {
162
163
  runMcpServer().catch(err => {
163
164
  console.error("MCP Server Error:", err);
164
165
  process.exit(1);
package/package.json CHANGED
@@ -1,16 +1,19 @@
1
1
  {
2
2
  "name": "yap2app",
3
- "version": "1.1.2",
4
- "type": "module",
3
+ "version": "1.1.4",
5
4
  "description": "Autonomous Vibe-to-Prod Localhost Bridge & Model Context Protocol (MCP) Server",
6
5
  "main": "bin/cli.bundle.js",
7
6
  "bin": {
8
7
  "yap2app": "./bin/cli.js"
9
8
  },
9
+ "engines": {
10
+ "node": ">=10.0.0"
11
+ },
10
12
  "files": [
11
13
  "bin",
12
14
  "bridge.js",
13
15
  "mcp.js",
16
+ "polyfills.js",
14
17
  "scanner.js",
15
18
  "v2p.js",
16
19
  "README.md"
package/polyfills.js ADDED
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Universal Node.js Polyfills & Warning Suppressors for Yap2App CLI
3
+ * Enables full backwards compatibility and clean startup across all NVM Node versions (Node 10.x to 23.x+)
4
+ */
5
+
6
+ // 0. Suppress noisy Node 18 experimental warnings and punycode deprecation warnings (DEP0040)
7
+ if (typeof process !== 'undefined' && process.emitWarning) {
8
+ var _originalEmitWarning = process.emitWarning;
9
+ process.emitWarning = function (warning) {
10
+ if (typeof warning === 'string') {
11
+ if (warning.includes('punycode') || warning.includes('ExperimentalWarning') || warning.includes('DEP0040')) {
12
+ return;
13
+ }
14
+ } else if (warning && typeof warning === 'object') {
15
+ if (warning.code === 'DEP0040' || warning.name === 'ExperimentalWarning') {
16
+ return;
17
+ }
18
+ if (String(warning.message || '').includes('punycode')) {
19
+ return;
20
+ }
21
+ }
22
+ return _originalEmitWarning.apply(process, arguments);
23
+ };
24
+ }
25
+
26
+ // 1. globalThis polyfill (Node <12)
27
+ if (typeof globalThis === 'undefined') {
28
+ if (typeof global !== 'undefined') {
29
+ global.globalThis = global;
30
+ } else if (typeof window !== 'undefined') {
31
+ window.globalThis = window;
32
+ } else if (typeof self !== 'undefined') {
33
+ self.globalThis = self;
34
+ }
35
+ }
36
+
37
+ // 2. Object.fromEntries polyfill (Node <12.0)
38
+ if (typeof Object.fromEntries === 'undefined') {
39
+ Object.fromEntries = function (entries) {
40
+ if (!entries) return {};
41
+ var obj = {};
42
+ var arr = Array.isArray(entries) ? entries : Array.from(entries);
43
+ for (var i = 0; i < arr.length; i++) {
44
+ var pair = arr[i];
45
+ if (pair && pair.length >= 2) {
46
+ obj[pair[0]] = pair[1];
47
+ }
48
+ }
49
+ return obj;
50
+ };
51
+ }
52
+
53
+ // 3. Object.hasOwn polyfill (Node <16.9)
54
+ if (typeof Object.hasOwn === 'undefined') {
55
+ Object.hasOwn = function (obj, prop) {
56
+ return obj != null && Object.prototype.hasOwnProperty.call(obj, prop);
57
+ };
58
+ }
59
+
60
+ // 4. Array.prototype.flat polyfill (Node <11.0)
61
+ if (!Array.prototype.flat) {
62
+ Array.prototype.flat = function (depth) {
63
+ var flattend = [];
64
+ var d = typeof depth === 'number' ? depth : 1;
65
+ (function flat(array, currentDepth) {
66
+ for (var i = 0; i < array.length; i++) {
67
+ if (Array.isArray(array[i]) && currentDepth > 0) {
68
+ flat(array[i], currentDepth - 1);
69
+ } else {
70
+ flattend.push(array[i]);
71
+ }
72
+ }
73
+ })(this, d);
74
+ return flattend;
75
+ };
76
+ }
77
+
78
+ // 5. String.prototype.replaceAll polyfill (Node <15.0)
79
+ if (!String.prototype.replaceAll) {
80
+ String.prototype.replaceAll = function (search, replacement) {
81
+ if (search instanceof RegExp) {
82
+ return this.replace(search, replacement);
83
+ }
84
+ return this.split(search).join(replacement);
85
+ };
86
+ }
87
+
88
+ // 6. String.prototype.matchAll polyfill (Node <12.0)
89
+ if (!String.prototype.matchAll) {
90
+ String.prototype.matchAll = function (regexp) {
91
+ var flags = regexp.flags;
92
+ if (!flags.includes('g')) {
93
+ flags += 'g';
94
+ }
95
+ var rx = new RegExp(regexp.source, flags);
96
+ var str = String(this);
97
+ var matches = [];
98
+ var match;
99
+ while ((match = rx.exec(str)) !== null) {
100
+ matches.push(match);
101
+ if (match.index === rx.lastIndex) {
102
+ rx.lastIndex++;
103
+ }
104
+ }
105
+ return matches[Symbol.iterator] ? matches[Symbol.iterator]() : matches;
106
+ };
107
+ }
108
+
109
+ // 7. Promise.allSettled polyfill (Node <12.9)
110
+ if (!Promise.allSettled) {
111
+ Promise.allSettled = function (promises) {
112
+ return Promise.all(
113
+ Array.from(promises).map(function (p) {
114
+ return Promise.resolve(p).then(
115
+ function (value) {
116
+ return { status: 'fulfilled', value: value };
117
+ },
118
+ function (reason) {
119
+ return { status: 'rejected', reason: reason };
120
+ }
121
+ );
122
+ })
123
+ );
124
+ };
125
+ }
126
+