test-bdk-cli 1.0.5 → 1.0.7

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.
@@ -38,6 +38,7 @@ const http = __importStar(require("http"));
38
38
  const fs = __importStar(require("fs"));
39
39
  const path = __importStar(require("path"));
40
40
  const child_process_1 = require("child_process");
41
+ const localstore_1 = require("../lib/localstore");
41
42
  function registerRun(program) {
42
43
  program
43
44
  .command('run [source]')
@@ -56,8 +57,71 @@ function registerRun(program) {
56
57
  console.error(`manifest.json not found in ${sourceDir}`);
57
58
  process.exit(1);
58
59
  }
59
- // Create a simple HTTP server
60
+ // Initialize LocalStore for local database operations
61
+ const localStore = new localstore_1.LocalStore(path.join(sourceDir, '.bdk/localstore'));
62
+ // Handler for database operations
63
+ const handleDBRequest = (operation, key, value) => {
64
+ try {
65
+ switch (operation) {
66
+ case 'set':
67
+ return localStore.store(key, value);
68
+ case 'get':
69
+ return localStore.fetch(key);
70
+ case 'update':
71
+ return localStore.update(key, value);
72
+ case 'delete':
73
+ return localStore.delete(key);
74
+ default:
75
+ throw new Error(`Unknown database operation: ${operation}`);
76
+ }
77
+ }
78
+ catch (error) {
79
+ console.error(`[DB] ERROR (${operation} ${key}): ${error.message}`);
80
+ throw error;
81
+ }
82
+ };
83
+ // Create HTTP server
60
84
  const server = http.createServer((req, res) => {
85
+ var _a;
86
+ // Add CORS headers
87
+ res.setHeader('Access-Control-Allow-Origin', '*');
88
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
89
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
90
+ // Handle OPTIONS requests
91
+ if (req.method === 'OPTIONS') {
92
+ res.writeHead(200);
93
+ res.end();
94
+ return;
95
+ }
96
+ // Handle database API requests
97
+ if (((_a = req.url) === null || _a === void 0 ? void 0 : _a.startsWith('/api/db')) && req.method === 'POST') {
98
+ let body = '';
99
+ req.on('data', (chunk) => {
100
+ body += chunk.toString();
101
+ });
102
+ req.on('error', (error) => {
103
+ console.error(`[DB] Request error:`, error);
104
+ res.writeHead(500, { 'Content-Type': 'application/json' });
105
+ res.end(JSON.stringify({ success: false, error: 'Request error' }));
106
+ });
107
+ req.on('end', () => {
108
+ try {
109
+ if (!body)
110
+ throw new Error('Empty request body');
111
+ const payload = JSON.parse(body);
112
+ const result = handleDBRequest(payload.operation, payload.key, payload.value);
113
+ res.writeHead(200, { 'Content-Type': 'application/json' });
114
+ res.end(JSON.stringify({ success: true, data: result }));
115
+ }
116
+ catch (error) {
117
+ console.error(`[DB] Error:`, error);
118
+ res.writeHead(400, { 'Content-Type': 'application/json' });
119
+ res.end(JSON.stringify({ success: false, error: error.message }));
120
+ }
121
+ });
122
+ return;
123
+ }
124
+ // Handle static file requests
61
125
  let filePath = req.url || '/';
62
126
  if (filePath === '/') {
63
127
  filePath = '/assets/iframe.html';
@@ -123,6 +187,7 @@ function registerRun(program) {
123
187
  console.log(`✅ Dev server started on http://localhost:${port} (env=${env})`);
124
188
  console.log(`📁 Serving from: ${sourceDir}`);
125
189
  console.log(`📄 Iframe: http://localhost:${port}/assets/iframe.html`);
190
+ console.log(`💾 Database: Local storage at ${localStore.getFilePath()}`);
126
191
  console.log(`\nPress Ctrl+C to stop the server\n`);
127
192
  // Open browser if --open flag is set
128
193
  if (opts.open) {
@@ -0,0 +1,208 @@
1
+ "use strict";
2
+ /**
3
+ * BoldDesk LocalStore - Persistent local data storage during development
4
+ *
5
+ * Similar to Freshdesk FDK's DataStore (lib/utils/data.js)
6
+ * Stores key-value data in .bolddesk/localstore JSON file
7
+ *
8
+ * Key Format: '1234' or '1234:{jiraId:9878}' (first part is the key)
9
+ */
10
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ var desc = Object.getOwnPropertyDescriptor(m, k);
13
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
14
+ desc = { enumerable: true, get: function() { return m[k]; } };
15
+ }
16
+ Object.defineProperty(o, k2, desc);
17
+ }) : (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ o[k2] = m[k];
20
+ }));
21
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
22
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
23
+ }) : function(o, v) {
24
+ o["default"] = v;
25
+ });
26
+ var __importStar = (this && this.__importStar) || (function () {
27
+ var ownKeys = function(o) {
28
+ ownKeys = Object.getOwnPropertyNames || function (o) {
29
+ var ar = [];
30
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
31
+ return ar;
32
+ };
33
+ return ownKeys(o);
34
+ };
35
+ return function (mod) {
36
+ if (mod && mod.__esModule) return mod;
37
+ var result = {};
38
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
39
+ __setModuleDefault(result, mod);
40
+ return result;
41
+ };
42
+ })();
43
+ Object.defineProperty(exports, "__esModule", { value: true });
44
+ exports.LocalStore = void 0;
45
+ const fs = __importStar(require("fs"));
46
+ const path = __importStar(require("path"));
47
+ /**
48
+ * LocalStore class for managing persistent local storage
49
+ * Mirrors Freshdesk's DataStore functionality
50
+ */
51
+ class LocalStore {
52
+ constructor(filePath = '.bdk/localstore') {
53
+ this.data = {};
54
+ this.filePath = filePath;
55
+ this.ensureStore();
56
+ }
57
+ /**
58
+ * Ensure localstore folder and file exist
59
+ * Creates folder and file if they don't exist
60
+ * Handles case where folder is deleted mid-operation
61
+ */
62
+ ensureStore() {
63
+ const folder = path.dirname(this.filePath);
64
+ try {
65
+ // Create folder if it doesn't exist
66
+ if (!fs.existsSync(folder)) {
67
+ fs.mkdirSync(folder, { recursive: true });
68
+ }
69
+ // Create file if it doesn't exist
70
+ if (!fs.existsSync(this.filePath)) {
71
+ fs.writeFileSync(this.filePath, JSON.stringify({}));
72
+ }
73
+ }
74
+ catch (error) {
75
+ console.error(`[LocalStore] Error in ensureStore: ${error.message}`);
76
+ throw error;
77
+ }
78
+ }
79
+ /**
80
+ * Validate key format and constraints
81
+ * - Max 60 characters
82
+ * - Cannot be empty
83
+ * - Alphanumeric, dash, underscore, colon allowed
84
+ */
85
+ validateKey(key) {
86
+ if (!key || typeof key !== 'string') {
87
+ throw new Error('The key cannot be blank');
88
+ }
89
+ }
90
+ /**
91
+ * Read localstore file from disk
92
+ * Returns parsed JSON object
93
+ * Returns empty object on error
94
+ */
95
+ readLocalStore() {
96
+ try {
97
+ if (!fs.existsSync(this.filePath)) {
98
+ return {};
99
+ }
100
+ const content = fs.readFileSync(this.filePath, 'utf-8');
101
+ return JSON.parse(content);
102
+ }
103
+ catch (e) {
104
+ console.error(`[LocalStore] Error reading file: ${e.message}`);
105
+ return {};
106
+ }
107
+ }
108
+ /**
109
+ * Write localstore file to disk
110
+ * Pretty-prints JSON for human readability
111
+ * Recreates folder if it was deleted
112
+ */
113
+ writeLocalStore(data) {
114
+ try {
115
+ const folder = path.dirname(this.filePath);
116
+ // Explicitly ensure folder exists before writing
117
+ if (!fs.existsSync(folder)) {
118
+ fs.mkdirSync(folder, { recursive: true });
119
+ }
120
+ // Write the file
121
+ fs.writeFileSync(this.filePath, JSON.stringify(data, null, 2));
122
+ }
123
+ catch (e) {
124
+ console.error(`[LocalStore] Error writing file: ${e.message}`);
125
+ throw e;
126
+ }
127
+ }
128
+ /**
129
+ * Store operation - SET key/value
130
+ * Stores data as-is without modification
131
+ *
132
+ * @param key Storage key (e.g., 'ticket-001')
133
+ * @param data Value to store (JSON object)
134
+ * @returns { Created: true }
135
+ */
136
+ store(key, data) {
137
+ this.validateKey(key);
138
+ this.ensureStore();
139
+ const localStoreData = this.readLocalStore();
140
+ localStoreData[key] = data;
141
+ this.writeLocalStore(localStoreData);
142
+ return { Created: true };
143
+ }
144
+ /**
145
+ * Fetch operation - GET key/value
146
+ * Retrieves stored data for a key
147
+ *
148
+ * @param key Storage key to retrieve
149
+ * @returns Stored data or undefined if not found
150
+ */
151
+ fetch(key) {
152
+ const localStoreData = this.readLocalStore();
153
+ return localStoreData[key];
154
+ }
155
+ /**
156
+ * Update operation - PATCH key with new values
157
+ * Merges attributes with existing data
158
+ *
159
+ * @param key Storage key to update
160
+ * @param attributes Object with fields to merge
161
+ * @returns { Updated: true }
162
+ */
163
+ update(key, attributes) {
164
+ this.validateKey(key);
165
+ this.ensureStore();
166
+ const localStoreData = this.readLocalStore();
167
+ const existingRecord = localStoreData[key];
168
+ if (!existingRecord) {
169
+ throw new Error(`Record not found for key: ${key}`);
170
+ }
171
+ localStoreData[key] = { ...existingRecord, ...attributes };
172
+ this.writeLocalStore(localStoreData);
173
+ return { Updated: true };
174
+ }
175
+ /**
176
+ * Delete operation - DELETE key
177
+ * Similar to Freshdesk DataStore.delete()
178
+ *
179
+ * @param key Storage key to delete
180
+ * @returns { Deleted: true }
181
+ */
182
+ delete(key) {
183
+ this.ensureStore();
184
+ const localStoreData = this.readLocalStore();
185
+ delete localStoreData[key];
186
+ this.writeLocalStore(localStoreData);
187
+ return { Deleted: true };
188
+ }
189
+ /**
190
+ * Get the file path to localstore
191
+ */
192
+ getFilePath() {
193
+ return this.filePath;
194
+ }
195
+ /**
196
+ * Get all stored data (for debugging)
197
+ */
198
+ getAll() {
199
+ return this.readLocalStore();
200
+ }
201
+ /**
202
+ * Clear all data from localstore
203
+ */
204
+ clear() {
205
+ this.writeLocalStore({});
206
+ }
207
+ }
208
+ exports.LocalStore = LocalStore;
@@ -129,6 +129,19 @@ async function validatePkg(appPath) {
129
129
  errors.push(`settings.fields[${fieldLabel}]: 'type' is required for each field object.`);
130
130
  }
131
131
  });
132
+ // ── Rule: settings.fields key uniqueness ──
133
+ const seenKeys = new Map();
134
+ settings.fields.forEach((field, index) => {
135
+ if (field && typeof field.key === 'string' && field.key.trim() !== '') {
136
+ const key = field.key.trim();
137
+ if (seenKeys.has(key)) {
138
+ errors.push(`settings.fields: duplicate key "${key}" found at index ${index} (first used at index ${seenKeys.get(key)}). Each field key must be unique.`);
139
+ }
140
+ else {
141
+ seenKeys.set(key, index);
142
+ }
143
+ }
144
+ });
132
145
  }
133
146
  // ── Rule: enablePermission / enableReadPermission / enableWritePermission ──
134
147
  // If enablePermission is true → at least one of enableReadPermission or enableWritePermission must be true.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "test-bdk-cli",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "test CLI",
5
5
  "author": "@BoldDesk",
6
6
  "bin": {
@@ -61,7 +61,10 @@
61
61
  "/src/"
62
62
  ],
63
63
  "transform": {
64
- "^.+\\.tsx?$": ["ts-jest", {}]
64
+ "^.+\\.tsx?$": [
65
+ "ts-jest",
66
+ {}
67
+ ]
65
68
  }
66
69
  }
67
70
  }