@qikdev/mcp 6.7.8 → 6.8.2

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.
@@ -0,0 +1,390 @@
1
+ /**
2
+ * Schema-based validation for content payloads.
3
+ * Fetches content type schemas from the glossary API and validates/coerces payloads.
4
+ */
5
+ import { ConfigManager } from "../config.js";
6
+ // ============================================================================
7
+ // CACHE
8
+ // ============================================================================
9
+ const CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
10
+ const schemaCache = {
11
+ index: null,
12
+ schemas: new Map()
13
+ };
14
+ function isCacheValid(entry) {
15
+ return entry !== null && (Date.now() - entry.fetchedAt) < CACHE_TTL_MS;
16
+ }
17
+ // ============================================================================
18
+ // SCHEMA FETCHING
19
+ // ============================================================================
20
+ async function getConfig() {
21
+ const configManager = new ConfigManager();
22
+ const config = await configManager.loadConfig();
23
+ if (!config) {
24
+ throw new Error('Qik MCP server not configured');
25
+ }
26
+ return config;
27
+ }
28
+ async function fetchGlossaryIndex() {
29
+ if (isCacheValid(schemaCache.index)) {
30
+ return schemaCache.index.data;
31
+ }
32
+ const config = await getConfig();
33
+ const response = await fetch(`${config.apiUrl || 'https://api.qik.dev'}/glossary/ai`, {
34
+ headers: {
35
+ 'Authorization': `Bearer ${config.accessToken}`,
36
+ 'Content-Type': 'application/json',
37
+ }
38
+ });
39
+ if (!response.ok) {
40
+ throw new Error(`Failed to fetch glossary: ${response.status}`);
41
+ }
42
+ const data = await response.json();
43
+ schemaCache.index = { data, fetchedAt: Date.now() };
44
+ return data;
45
+ }
46
+ export async function getContentTypeSchema(typeKey) {
47
+ // Check cache
48
+ const cached = schemaCache.schemas.get(typeKey) || null;
49
+ if (isCacheValid(cached)) {
50
+ return cached.data;
51
+ }
52
+ try {
53
+ // Get glossary index
54
+ const index = await fetchGlossaryIndex();
55
+ // Find the content type
56
+ const contentType = index.find(ct => ct.key === typeKey);
57
+ if (!contentType) {
58
+ return null; // Unknown content type
59
+ }
60
+ // Fetch detailed schema
61
+ const config = await getConfig();
62
+ const schemaUrl = contentType.urls.documentation.url;
63
+ const response = await fetch(schemaUrl, {
64
+ headers: {
65
+ 'Authorization': `Bearer ${config.accessToken}`,
66
+ 'Content-Type': 'application/json',
67
+ }
68
+ });
69
+ if (!response.ok) {
70
+ throw new Error(`Failed to fetch schema for ${typeKey}: ${response.status}`);
71
+ }
72
+ const schema = await response.json();
73
+ schemaCache.schemas.set(typeKey, { data: schema, fetchedAt: Date.now() });
74
+ return schema;
75
+ }
76
+ catch (error) {
77
+ console.error(`Error fetching schema for ${typeKey}:`, error);
78
+ return null;
79
+ }
80
+ }
81
+ // ============================================================================
82
+ // TYPE COERCION
83
+ // ============================================================================
84
+ /**
85
+ * Try to parse a string as JSON
86
+ */
87
+ function tryParseJson(value) {
88
+ const trimmed = value.trim();
89
+ if ((trimmed.startsWith('{') && trimmed.endsWith('}')) ||
90
+ (trimmed.startsWith('[') && trimmed.endsWith(']'))) {
91
+ try {
92
+ return JSON.parse(value);
93
+ }
94
+ catch {
95
+ return null;
96
+ }
97
+ }
98
+ return null;
99
+ }
100
+ /**
101
+ * Coerce a value to boolean
102
+ */
103
+ function coerceToBoolean(value) {
104
+ if (typeof value === 'boolean') {
105
+ return { value };
106
+ }
107
+ if (typeof value === 'string') {
108
+ const lower = value.toLowerCase().trim();
109
+ if (['true', 'yes', '1', 'on'].includes(lower)) {
110
+ return { value: true };
111
+ }
112
+ if (['false', 'no', '0', 'off', ''].includes(lower)) {
113
+ return { value: false };
114
+ }
115
+ return { value, error: `Cannot convert "${value}" to boolean` };
116
+ }
117
+ if (typeof value === 'number') {
118
+ return { value: value !== 0 };
119
+ }
120
+ return { value: Boolean(value) };
121
+ }
122
+ /**
123
+ * Coerce a value to date (ISO string)
124
+ */
125
+ function coerceToDate(value) {
126
+ if (value instanceof Date) {
127
+ return { value: value.toISOString() };
128
+ }
129
+ if (typeof value === 'string') {
130
+ const date = new Date(value);
131
+ if (!isNaN(date.getTime())) {
132
+ return { value: value }; // Keep original string if valid
133
+ }
134
+ return { value, error: `Cannot parse "${value}" as date` };
135
+ }
136
+ if (typeof value === 'number') {
137
+ const date = new Date(value);
138
+ if (!isNaN(date.getTime())) {
139
+ return { value: date.toISOString() };
140
+ }
141
+ }
142
+ return { value, error: `Cannot convert to date` };
143
+ }
144
+ /**
145
+ * Coerce a value to the expected type based on field definition
146
+ */
147
+ export function coerceValue(value, field) {
148
+ // Handle null/undefined
149
+ if (value === null || value === undefined) {
150
+ return { value };
151
+ }
152
+ // First, try to parse JSON strings (LLM sends nested objects as strings)
153
+ if (typeof value === 'string' && field.type !== 'string') {
154
+ const parsed = tryParseJson(value);
155
+ if (parsed !== null) {
156
+ value = parsed;
157
+ }
158
+ }
159
+ // Handle array wrapping based on field cardinality
160
+ const isArrayField = field.maximum === 0 || (field.maximum !== undefined && field.maximum > 1);
161
+ switch (field.type) {
162
+ case 'string':
163
+ case 'email':
164
+ case 'url':
165
+ if (typeof value !== 'string') {
166
+ return { value: String(value) };
167
+ }
168
+ return { value };
169
+ case 'number':
170
+ if (typeof value === 'number') {
171
+ return { value };
172
+ }
173
+ if (typeof value === 'string') {
174
+ // Handle currency formatting (remove $, commas, etc.)
175
+ const cleaned = value.replace(/[$,\s]/g, '');
176
+ const num = parseFloat(cleaned);
177
+ if (!isNaN(num)) {
178
+ return { value: num };
179
+ }
180
+ return { value, error: `Cannot convert "${value}" to number` };
181
+ }
182
+ return { value, error: `Cannot convert to number` };
183
+ case 'integer':
184
+ if (typeof value === 'number') {
185
+ return { value: Math.round(value) };
186
+ }
187
+ if (typeof value === 'string') {
188
+ const int = parseInt(value, 10);
189
+ if (!isNaN(int)) {
190
+ return { value: int };
191
+ }
192
+ return { value, error: `Cannot convert "${value}" to integer` };
193
+ }
194
+ return { value, error: `Cannot convert to integer` };
195
+ case 'boolean':
196
+ return coerceToBoolean(value);
197
+ case 'date':
198
+ return coerceToDate(value);
199
+ case 'reference':
200
+ // References should be arrays of IDs (or single ID for maximum: 1)
201
+ if (Array.isArray(value)) {
202
+ // Recursively coerce array items (handle stringified arrays)
203
+ const coerced = value.map(v => {
204
+ if (typeof v === 'string') {
205
+ const parsed = tryParseJson(v);
206
+ return parsed !== null ? parsed : v;
207
+ }
208
+ return v;
209
+ });
210
+ return { value: coerced };
211
+ }
212
+ if (typeof value === 'string') {
213
+ // Single ID - wrap in array if needed
214
+ if (isArrayField) {
215
+ return { value: [value] };
216
+ }
217
+ return { value };
218
+ }
219
+ return { value };
220
+ case 'object':
221
+ // Already handled JSON parsing above
222
+ if (typeof value === 'object') {
223
+ return { value };
224
+ }
225
+ return { value, error: `Expected object for field "${field.title}"` };
226
+ case 'group':
227
+ // Visual-only field, no data
228
+ return { value: undefined };
229
+ default:
230
+ return { value };
231
+ }
232
+ }
233
+ // ============================================================================
234
+ // FIELD VALIDATION
235
+ // ============================================================================
236
+ function isEmpty(value) {
237
+ if (value === null || value === undefined)
238
+ return true;
239
+ if (value === '')
240
+ return true;
241
+ if (Array.isArray(value) && value.length === 0)
242
+ return true;
243
+ return false;
244
+ }
245
+ /**
246
+ * Validate a field value against its definition
247
+ */
248
+ export function validateField(value, field) {
249
+ const errors = [];
250
+ // Skip group fields (visual only)
251
+ if (field.type === 'group') {
252
+ return errors;
253
+ }
254
+ // Required check
255
+ if (field.minimum !== undefined && field.minimum >= 1) {
256
+ if (isEmpty(value)) {
257
+ errors.push(`"${field.title}" is required`);
258
+ return errors; // No point continuing
259
+ }
260
+ }
261
+ // Skip further validation if empty and optional
262
+ if (isEmpty(value)) {
263
+ return errors;
264
+ }
265
+ // Cardinality check
266
+ const values = Array.isArray(value) ? value : [value];
267
+ if (field.maximum === 1 && values.length > 1) {
268
+ errors.push(`"${field.title}" accepts only one value (received ${values.length})`);
269
+ }
270
+ if (field.maximum !== undefined && field.maximum > 1 && values.length > field.maximum) {
271
+ errors.push(`"${field.title}" accepts at most ${field.maximum} values (received ${values.length})`);
272
+ }
273
+ // Options validation (for select/button widgets)
274
+ if (field.options && field.options.length > 0) {
275
+ const validValues = new Set(field.options.map(o => o.value));
276
+ for (const v of values) {
277
+ if (!validValues.has(v)) {
278
+ const optionsList = field.options.map(o => o.value).join(', ');
279
+ errors.push(`"${field.title}" value "${v}" is not valid. Options: ${optionsList}`);
280
+ }
281
+ }
282
+ }
283
+ return errors;
284
+ }
285
+ // ============================================================================
286
+ // MAIN VALIDATION FUNCTION
287
+ // ============================================================================
288
+ // System fields that shouldn't be validated
289
+ const SYSTEM_FIELDS = new Set(['_id', 'meta', 'organisation', 'createdAt', 'updatedAt']);
290
+ /**
291
+ * Coerce a payload using content type schema for type information.
292
+ * Does NOT validate - just coerces types. Let the API handle validation.
293
+ */
294
+ export async function validateAndCoercePayload(typeKey, payload, options = {}) {
295
+ const result = {
296
+ valid: true, // Always valid - we just coerce, don't validate
297
+ errors: [],
298
+ warnings: [],
299
+ coercedPayload: {}
300
+ };
301
+ // Try to get schema for type coercion
302
+ let schema = null;
303
+ try {
304
+ schema = await getContentTypeSchema(typeKey);
305
+ }
306
+ catch (error) {
307
+ result.warnings.push(`Could not fetch schema: ${error.message}`);
308
+ }
309
+ // If no schema, pass through with JSON string parsing only
310
+ if (!schema) {
311
+ result.coercedPayload = parsePayloadJsonStrings(payload);
312
+ return result;
313
+ }
314
+ // Build a map of field keys for quick lookup
315
+ const fieldMap = new Map();
316
+ for (const field of schema.fields) {
317
+ if (field.key) {
318
+ fieldMap.set(field.key, field);
319
+ }
320
+ }
321
+ // Process all fields in the payload - coerce types but don't validate
322
+ for (const [key, value] of Object.entries(payload)) {
323
+ // System fields - just parse JSON strings
324
+ if (SYSTEM_FIELDS.has(key)) {
325
+ result.coercedPayload[key] = parseValueJsonStrings(value);
326
+ continue;
327
+ }
328
+ const field = fieldMap.get(key);
329
+ if (!field) {
330
+ // Unknown field - pass through with JSON parsing
331
+ result.coercedPayload[key] = parseValueJsonStrings(value);
332
+ continue;
333
+ }
334
+ // Coerce the value to the correct type
335
+ const coerced = coerceValue(value, field);
336
+ if (coerced.error) {
337
+ result.warnings.push(`${field.title}: ${coerced.error}`);
338
+ }
339
+ result.coercedPayload[key] = coerced.value;
340
+ }
341
+ // No validation - let the API handle it
342
+ return result;
343
+ }
344
+ // ============================================================================
345
+ // HELPER FUNCTIONS
346
+ // ============================================================================
347
+ /**
348
+ * Recursively parse JSON strings in a value
349
+ */
350
+ function parseValueJsonStrings(value) {
351
+ if (value === null || value === undefined) {
352
+ return value;
353
+ }
354
+ if (typeof value === 'string') {
355
+ const parsed = tryParseJson(value);
356
+ if (parsed !== null) {
357
+ return parseValueJsonStrings(parsed);
358
+ }
359
+ return value;
360
+ }
361
+ if (Array.isArray(value)) {
362
+ return value.map(v => parseValueJsonStrings(v));
363
+ }
364
+ if (typeof value === 'object') {
365
+ const result = {};
366
+ for (const [k, v] of Object.entries(value)) {
367
+ result[k] = parseValueJsonStrings(v);
368
+ }
369
+ return result;
370
+ }
371
+ return value;
372
+ }
373
+ /**
374
+ * Parse JSON strings throughout a payload object
375
+ */
376
+ function parsePayloadJsonStrings(payload) {
377
+ const result = {};
378
+ for (const [key, value] of Object.entries(payload)) {
379
+ result[key] = parseValueJsonStrings(value);
380
+ }
381
+ return result;
382
+ }
383
+ /**
384
+ * Clear the schema cache (useful for testing)
385
+ */
386
+ export function clearSchemaCache() {
387
+ schemaCache.index = null;
388
+ schemaCache.schemas.clear();
389
+ }
390
+ //# sourceMappingURL=schema-validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-validator.js","sourceRoot":"","sources":["../../../src/tools/schema-validator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AA0D7C,+EAA+E;AAC/E,QAAQ;AACR,+EAA+E;AAE/E,MAAM,YAAY,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,YAAY;AAEhD,MAAM,WAAW,GAAgB;IAC/B,KAAK,EAAE,IAAI;IACX,OAAO,EAAE,IAAI,GAAG,EAAE;CACnB,CAAC;AAEF,SAAS,YAAY,CAAI,KAA2B;IAClD,OAAO,KAAK,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,SAAS,CAAC,GAAG,YAAY,CAAC;AACzE,CAAC;AAED,+EAA+E;AAC/E,kBAAkB;AAClB,+EAA+E;AAE/E,KAAK,UAAU,SAAS;IACtB,MAAM,aAAa,GAAG,IAAI,aAAa,EAAE,CAAC;IAC1C,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,UAAU,EAAE,CAAC;IAChD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;IACnD,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,KAAK,UAAU,kBAAkB;IAC/B,IAAI,YAAY,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;QACpC,OAAO,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC;IAChC,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;IACjC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,MAAM,CAAC,MAAM,IAAI,qBAAqB,cAAc,EAAE;QACpF,OAAO,EAAE;YACP,eAAe,EAAE,UAAU,MAAM,CAAC,WAAW,EAAE;YAC/C,cAAc,EAAE,kBAAkB;SACnC;KACF,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,6BAA6B,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IAClE,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IACnC,WAAW,CAAC,KAAK,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;IACpD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,OAAe;IACxD,cAAc;IACd,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC;IACxD,IAAI,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC;QACzB,OAAO,MAAM,CAAC,IAAI,CAAC;IACrB,CAAC;IAED,IAAI,CAAC;QACH,qBAAqB;QACrB,MAAM,KAAK,GAAG,MAAM,kBAAkB,EAAE,CAAC;QAEzC,wBAAwB;QACxB,MAAM,WAAW,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,KAAK,OAAO,CAAC,CAAC;QACzD,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,OAAO,IAAI,CAAC,CAAC,uBAAuB;QACtC,CAAC;QAED,wBAAwB;QACxB,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;QACjC,MAAM,SAAS,GAAG,WAAW,CAAC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC;QAErD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,SAAS,EAAE;YACtC,OAAO,EAAE;gBACP,eAAe,EAAE,UAAU,MAAM,CAAC,WAAW,EAAE;gBAC/C,cAAc,EAAE,kBAAkB;aACnC;SACF,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,8BAA8B,OAAO,KAAK,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QAC/E,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACrC,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QAC1E,OAAO,MAAM,CAAC;IAEhB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,6BAA6B,OAAO,GAAG,EAAE,KAAK,CAAC,CAAC;QAC9D,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,+EAA+E;AAC/E,gBAAgB;AAChB,+EAA+E;AAE/E;;GAEG;AACH,SAAS,YAAY,CAAC,KAAa;IACjC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QAClD,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QACvD,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC3B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,SAAS,eAAe,CAAC,KAAU;IACjC,IAAI,OAAO,KAAK,KAAK,SAAS,EAAE,CAAC;QAC/B,OAAO,EAAE,KAAK,EAAE,CAAC;IACnB,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,KAAK,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;QACzC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAC/C,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QACzB,CAAC;QACD,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YACpD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;QAC1B,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,mBAAmB,KAAK,cAAc,EAAE,CAAC;IAClE,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,EAAE,KAAK,EAAE,KAAK,KAAK,CAAC,EAAE,CAAC;IAChC,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;AACnC,CAAC;AAED;;GAEG;AACH,SAAS,YAAY,CAAC,KAAU;IAC9B,IAAI,KAAK,YAAY,IAAI,EAAE,CAAC;QAC1B,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;IACxC,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC;QAC7B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;YAC3B,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,gCAAgC;QAC3D,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,iBAAiB,KAAK,WAAW,EAAE,CAAC;IAC7D,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC;QAC7B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;YAC3B,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;QACvC,CAAC;IACH,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,wBAAwB,EAAE,CAAC;AACpD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,KAAU,EAAE,KAAsB;IAC5D,wBAAwB;IACxB,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QAC1C,OAAO,EAAE,KAAK,EAAE,CAAC;IACnB,CAAC;IAED,yEAAyE;IACzE,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QACzD,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;QACnC,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YACpB,KAAK,GAAG,MAAM,CAAC;QACjB,CAAC;IACH,CAAC;IAED,mDAAmD;IACnD,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,KAAK,SAAS,IAAI,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC;IAE/F,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,KAAK,QAAQ,CAAC;QACd,KAAK,OAAO,CAAC;QACb,KAAK,KAAK;YACR,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;gBAC9B,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;YAClC,CAAC;YACD,OAAO,EAAE,KAAK,EAAE,CAAC;QAEnB,KAAK,QAAQ;YACX,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;gBAC9B,OAAO,EAAE,KAAK,EAAE,CAAC;YACnB,CAAC;YACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;gBAC9B,sDAAsD;gBACtD,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;gBAC7C,MAAM,GAAG,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;gBAChC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;oBAChB,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;gBACxB,CAAC;gBACD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,mBAAmB,KAAK,aAAa,EAAE,CAAC;YACjE,CAAC;YACD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,0BAA0B,EAAE,CAAC;QAEtD,KAAK,SAAS;YACZ,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;gBAC9B,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;YACtC,CAAC;YACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;gBAC9B,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;gBAChC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;oBAChB,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;gBACxB,CAAC;gBACD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,mBAAmB,KAAK,cAAc,EAAE,CAAC;YAClE,CAAC;YACD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,2BAA2B,EAAE,CAAC;QAEvD,KAAK,SAAS;YACZ,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;QAEhC,KAAK,MAAM;YACT,OAAO,YAAY,CAAC,KAAK,CAAC,CAAC;QAE7B,KAAK,WAAW;YACd,mEAAmE;YACnE,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;gBACzB,6DAA6D;gBAC7D,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;oBAC5B,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;wBAC1B,MAAM,MAAM,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;wBAC/B,OAAO,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;oBACtC,CAAC;oBACD,OAAO,CAAC,CAAC;gBACX,CAAC,CAAC,CAAC;gBACH,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;YAC5B,CAAC;YACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;gBAC9B,sCAAsC;gBACtC,IAAI,YAAY,EAAE,CAAC;oBACjB,OAAO,EAAE,KAAK,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC5B,CAAC;gBACD,OAAO,EAAE,KAAK,EAAE,CAAC;YACnB,CAAC;YACD,OAAO,EAAE,KAAK,EAAE,CAAC;QAEnB,KAAK,QAAQ;YACX,qCAAqC;YACrC,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;gBAC9B,OAAO,EAAE,KAAK,EAAE,CAAC;YACnB,CAAC;YACD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,8BAA8B,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC;QAExE,KAAK,OAAO;YACV,6BAA6B;YAC7B,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;QAE9B;YACE,OAAO,EAAE,KAAK,EAAE,CAAC;IACrB,CAAC;AACH,CAAC;AAED,+EAA+E;AAC/E,mBAAmB;AACnB,+EAA+E;AAE/E,SAAS,OAAO,CAAC,KAAU;IACzB,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACvD,IAAI,KAAK,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IAC9B,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAC5D,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,KAAU,EAAE,KAAsB;IAC9D,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,kCAAkC;IAClC,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC3B,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,iBAAiB;IACjB,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS,IAAI,KAAK,CAAC,OAAO,IAAI,CAAC,EAAE,CAAC;QACtD,IAAI,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACnB,MAAM,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,KAAK,eAAe,CAAC,CAAC;YAC5C,OAAO,MAAM,CAAC,CAAC,sBAAsB;QACvC,CAAC;IACH,CAAC;IAED,gDAAgD;IAChD,IAAI,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACnB,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,oBAAoB;IACpB,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IAEtD,IAAI,KAAK,CAAC,OAAO,KAAK,CAAC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7C,MAAM,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,KAAK,sCAAsC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;IACrF,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS,IAAI,KAAK,CAAC,OAAO,GAAG,CAAC,IAAI,MAAM,CAAC,MAAM,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC;QACtF,MAAM,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,KAAK,qBAAqB,KAAK,CAAC,OAAO,qBAAqB,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;IACtG,CAAC;IAED,iDAAiD;IACjD,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9C,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;QAC7D,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;YACvB,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;gBACxB,MAAM,WAAW,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAC/D,MAAM,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,KAAK,YAAY,CAAC,4BAA4B,WAAW,EAAE,CAAC,CAAC;YACrF,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,+EAA+E;AAC/E,2BAA2B;AAC3B,+EAA+E;AAE/E,4CAA4C;AAC5C,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC,CAAC;AAMzF;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,OAAe,EACf,OAA4B,EAC5B,UAA2B,EAAE;IAE7B,MAAM,MAAM,GAAqB;QAC/B,KAAK,EAAE,IAAI,EAAE,gDAAgD;QAC7D,MAAM,EAAE,EAAE;QACV,QAAQ,EAAE,EAAE;QACZ,cAAc,EAAE,EAAE;KACnB,CAAC;IAEF,sCAAsC;IACtC,IAAI,MAAM,GAA6B,IAAI,CAAC;IAC5C,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAC/C,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QACpB,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,2BAA2B,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;IACnE,CAAC;IAED,2DAA2D;IAC3D,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,CAAC,cAAc,GAAG,uBAAuB,CAAC,OAAO,CAAC,CAAC;QACzD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,6CAA6C;IAC7C,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA2B,CAAC;IACpD,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClC,IAAI,KAAK,CAAC,GAAG,EAAE,CAAC;YACd,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACjC,CAAC;IACH,CAAC;IAED,sEAAsE;IACtE,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACnD,0CAA0C;QAC1C,IAAI,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YAC3B,MAAM,CAAC,cAAc,CAAC,GAAG,CAAC,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAC;YAC1D,SAAS;QACX,CAAC;QAED,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAEhC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,iDAAiD;YACjD,MAAM,CAAC,cAAc,CAAC,GAAG,CAAC,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAC;YAC1D,SAAS;QACX,CAAC;QAED,uCAAuC;QACvC,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QAC1C,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YAClB,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,KAAK,KAAK,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;QAC3D,CAAC;QACD,MAAM,CAAC,cAAc,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC;IAC7C,CAAC;IAED,wCAAwC;IACxC,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,+EAA+E;AAC/E,mBAAmB;AACnB,+EAA+E;AAE/E;;GAEG;AACH,SAAS,qBAAqB,CAAC,KAAU;IACvC,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QAC1C,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;QACnC,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YACpB,OAAO,qBAAqB,CAAC,MAAM,CAAC,CAAC;QACvC,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,CAAC;IAClD,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,MAAM,GAAQ,EAAE,CAAC;QACvB,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YAC3C,MAAM,CAAC,CAAC,CAAC,GAAG,qBAAqB,CAAC,CAAC,CAAC,CAAC;QACvC,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,SAAS,uBAAuB,CAAC,OAA4B;IAC3D,MAAM,MAAM,GAAwB,EAAE,CAAC;IACvC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACnD,MAAM,CAAC,GAAG,CAAC,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAC;IAC7C,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB;IAC9B,WAAW,CAAC,KAAK,GAAG,IAAI,CAAC;IACzB,WAAW,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;AAC9B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../../src/tools/update.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAG1D,eAAO,MAAM,iBAAiB,EAAE,IAyC/B,CAAC;AAEF,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,GAAG,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,CAAC,CAgDhH"}
1
+ {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../../src/tools/update.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAI1D,eAAO,MAAM,iBAAiB,EAAE,IAgD/B,CAAC;AAEF,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,GAAG,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,CAAC,CA4DhH"}
@@ -1,4 +1,5 @@
1
1
  import { ConfigManager } from "../config.js";
2
+ import { validateAndCoercePayload } from "./schema-validator.js";
2
3
  export const updateContentTool = {
3
4
  name: "update_content",
4
5
  description: `Update existing content items in Qik.
@@ -10,8 +11,10 @@ export const updateContentTool = {
10
11
 
11
12
  **Optional:**
12
13
  - updateMethod: "patch" (default) for partial updates, "put" for full replacement
14
+ - typeKey: Content type key for schema validation (e.g., 'article', 'profile'). If provided, payload will be validated and coerced against the schema.
13
15
 
14
16
  All other properties are passed directly to the API as the update payload.
17
+ The MCP server will automatically validate and coerce values if typeKey is provided.
15
18
 
16
19
  **Tags Shorthand:**
17
20
  Pass \`tags: ['green', 'red']\` as string names and the backend auto-creates tags.
@@ -20,6 +23,7 @@ Pass \`tags: ['green', 'red']\` as string names and the backend auto-creates tag
20
23
  \`\`\`json
21
24
  {
22
25
  "contentId": "abc123",
26
+ "typeKey": "article",
23
27
  "title": "Updated Title",
24
28
  "tags": ["featured", "breaking"]
25
29
  }
@@ -31,6 +35,10 @@ Pass \`tags: ['green', 'red']\` as string names and the backend auto-creates tag
31
35
  type: "string",
32
36
  description: "The ID of the content item to update"
33
37
  },
38
+ typeKey: {
39
+ type: "string",
40
+ description: "Content type key for schema validation (e.g., 'article', 'profile')"
41
+ },
34
42
  updateMethod: {
35
43
  type: "string",
36
44
  enum: ["patch", "put"],
@@ -48,11 +56,22 @@ export async function handleUpdateContent(args) {
48
56
  if (!config) {
49
57
  throw new Error('Qik MCP server not configured. Run setup first.');
50
58
  }
51
- const { contentId, updateMethod, ...payload } = args;
59
+ const { contentId, typeKey, updateMethod, ...rawPayload } = args;
52
60
  if (!contentId) {
53
61
  throw new Error('contentId is required');
54
62
  }
55
63
  const method = (updateMethod || 'patch').toUpperCase();
64
+ const isPartialUpdate = method === 'PATCH';
65
+ let payload;
66
+ // If typeKey is provided, coerce using schema (fixes stringified JSON from LLMs)
67
+ if (typeKey) {
68
+ const validation = await validateAndCoercePayload(typeKey, rawPayload);
69
+ payload = validation.coercedPayload;
70
+ }
71
+ else {
72
+ // No typeKey provided - just do basic JSON string parsing
73
+ payload = parsePayloadJsonStrings(rawPayload);
74
+ }
56
75
  const response = await fetch(`${config.apiUrl || 'https://api.qik.dev'}/content/${contentId}`, {
57
76
  method,
58
77
  headers: {
@@ -82,4 +101,43 @@ export async function handleUpdateContent(args) {
82
101
  };
83
102
  }
84
103
  }
104
+ /**
105
+ * Basic JSON string parsing for payloads without schema validation
106
+ */
107
+ function parsePayloadJsonStrings(payload) {
108
+ const result = {};
109
+ for (const [key, value] of Object.entries(payload)) {
110
+ result[key] = parseValueJsonStrings(value);
111
+ }
112
+ return result;
113
+ }
114
+ function parseValueJsonStrings(value) {
115
+ if (value === null || value === undefined) {
116
+ return value;
117
+ }
118
+ if (typeof value === 'string') {
119
+ const trimmed = value.trim();
120
+ if ((trimmed.startsWith('{') && trimmed.endsWith('}')) ||
121
+ (trimmed.startsWith('[') && trimmed.endsWith(']'))) {
122
+ try {
123
+ return parseValueJsonStrings(JSON.parse(value));
124
+ }
125
+ catch {
126
+ return value;
127
+ }
128
+ }
129
+ return value;
130
+ }
131
+ if (Array.isArray(value)) {
132
+ return value.map(v => parseValueJsonStrings(v));
133
+ }
134
+ if (typeof value === 'object') {
135
+ const result = {};
136
+ for (const [k, v] of Object.entries(value)) {
137
+ result[k] = parseValueJsonStrings(v);
138
+ }
139
+ return result;
140
+ }
141
+ return value;
142
+ }
85
143
  //# sourceMappingURL=update.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"update.js","sourceRoot":"","sources":["../../../src/tools/update.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,MAAM,CAAC,MAAM,iBAAiB,GAAS;IACrC,IAAI,EAAE,gBAAgB;IACtB,WAAW,EAAE;;;;;;;;;;;;;;;;;;;;;;OAsBR;IACL,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,SAAS,EAAE;gBACT,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,sCAAsC;aACpD;YACD,YAAY,EAAE;gBACZ,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC;gBACtB,WAAW,EAAE,mEAAmE;aACjF;SACF;QACD,QAAQ,EAAE,CAAC,WAAW,CAAC;QACvB,oBAAoB,EAAE,IAAI;KAC3B;CACF,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,IAAS;IACjD,IAAI,CAAC;QACH,MAAM,aAAa,GAAG,IAAI,aAAa,EAAE,CAAC;QAC1C,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,UAAU,EAAE,CAAC;QAEhD,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;QACrE,CAAC;QAED,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,GAAG,OAAO,EAAE,GAAG,IAAI,CAAC;QAErD,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;QAC3C,CAAC;QAED,MAAM,MAAM,GAAG,CAAC,YAAY,IAAI,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;QAEvD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,MAAM,CAAC,MAAM,IAAI,qBAAqB,YAAY,SAAS,EAAE,EAAE;YAC7F,MAAM;YACN,OAAO,EAAE;gBACP,eAAe,EAAE,UAAU,MAAM,CAAC,WAAW,EAAE;gBAC/C,cAAc,EAAE,kBAAkB;aACnC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;SAC9B,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YACxC,MAAM,IAAI,KAAK,CAAC,QAAQ,QAAQ,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC,CAAC;QAC3D,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QAEnC,OAAO;YACL,OAAO,EAAE,CAAC;oBACR,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;iBACpC,CAAC;SACH,CAAC;IAEJ,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QACpB,OAAO;YACL,OAAO,EAAE,CAAC;oBACR,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;iBACxD,CAAC;SACH,CAAC;IACJ,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"update.js","sourceRoot":"","sources":["../../../src/tools/update.ts"],"names":[],"mappings":"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"}
@@ -40,5 +40,41 @@ export declare function hasPermission(userSession: UserSession, permission: stri
40
40
  export declare function getAvailableScopes(userSession: UserSession, permission: string): string[];
41
41
  export declare function hasPermissionInScope(userSession: UserSession, permission: string, scopeId: string): boolean;
42
42
  export declare function getScopeTitle(userSession: UserSession, scopeId: string): string;
43
+ export declare function hasAnyPermission(userSession: UserSession, permissions: string[]): boolean;
44
+ export declare function hasFieldPermission(userSession: UserSession, basePermission: string): boolean;
45
+ export interface PermissionCheck {
46
+ permission: string;
47
+ alternatives?: string[];
48
+ description: string;
49
+ }
50
+ export declare function checkPermissions(userSession: UserSession, required: PermissionCheck[]): {
51
+ hasAll: boolean;
52
+ missing: PermissionCheck[];
53
+ };
54
+ export declare function formatPermissionError(operation: string, missing: PermissionCheck[]): string;
55
+ export declare const WORKFLOW_PERMISSIONS: {
56
+ view: ({
57
+ permission: string;
58
+ alternatives: string[];
59
+ description: string;
60
+ } | {
61
+ permission: string;
62
+ description: string;
63
+ alternatives?: never;
64
+ })[];
65
+ edit: ({
66
+ permission: string;
67
+ alternatives: string[];
68
+ description: string;
69
+ } | {
70
+ permission: string;
71
+ description: string;
72
+ alternatives?: never;
73
+ })[];
74
+ create: {
75
+ permission: string;
76
+ description: string;
77
+ }[];
78
+ };
43
79
  export declare function getUserSessionData(): Promise<UserSession>;
44
80
  //# sourceMappingURL=user.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../../src/tools/user.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAG1D,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,CAAC,MAAM,GAAG;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KAAE,CAAC,EAAE,CAAC;IAC3D,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE;QACb,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE;QACL,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,WAAW,EAAE;QACX,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,CAAC;KACrC,CAAC;IACF,kBAAkB,CAAC,EAAE,GAAG,EAAE,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,eAAO,MAAM,kBAAkB,EAAE,IAOhC,CAAC;AAEF,wBAAsB,oBAAoB,IAAI,OAAO,CAAC;IAAE,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,CAAC,CAqCxG;AAGD,wBAAgB,aAAa,CAAC,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAiBnF;AAGD,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CA+BzF;AAGD,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAW3G;AAGD,wBAAgB,aAAa,CAAC,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAO/E;AAGD,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,WAAW,CAAC,CAoB/D"}
1
+ {"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../../src/tools/user.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAG1D,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,CAAC,MAAM,GAAG;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KAAE,CAAC,EAAE,CAAC;IAC3D,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE;QACb,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE;QACL,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,WAAW,EAAE;QACX,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,CAAC;KACrC,CAAC;IACF,kBAAkB,CAAC,EAAE,GAAG,EAAE,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,eAAO,MAAM,kBAAkB,EAAE,IAOhC,CAAC;AAEF,wBAAsB,oBAAoB,IAAI,OAAO,CAAC;IAAE,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,CAAC,CAqCxG;AAGD,wBAAgB,aAAa,CAAC,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAiBnF;AAGD,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CA+BzF;AAGD,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAW3G;AAGD,wBAAgB,aAAa,CAAC,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAO/E;AAGD,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAOzF;AAGD,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAuB5F;AAGD,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,WAAW,EAAE,QAAQ,EAAE,eAAe,EAAE,GAAG;IACvF,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,eAAe,EAAE,CAAC;CAC5B,CAoBA;AAGD,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,EAAE,GAAG,MAAM,CAe3F;AAGD,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;CAYhC,CAAC;AAGF,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,WAAW,CAAC,CAoB/D"}
@@ -106,6 +106,83 @@ export function getScopeTitle(userSession, scopeId) {
106
106
  const scope = userSession.permissions[scopeId];
107
107
  return scope?.title || scopeId;
108
108
  }
109
+ // Helper function to check if user has any matching permission (supports wildcards)
110
+ export function hasAnyPermission(userSession, permissions) {
111
+ for (const perm of permissions) {
112
+ if (hasPermission(userSession, perm)) {
113
+ return true;
114
+ }
115
+ }
116
+ return false;
117
+ }
118
+ // Helper function to check for wildcard field permissions (e.g., definition.viewfield.*)
119
+ export function hasFieldPermission(userSession, basePermission) {
120
+ // Check for wildcard permission (e.g., definition.viewfield.*)
121
+ if (hasPermission(userSession, `${basePermission}.*`)) {
122
+ return true;
123
+ }
124
+ // Check if any specific field permission exists
125
+ if (!userSession.permissions || typeof userSession.permissions !== 'object') {
126
+ return false;
127
+ }
128
+ for (const scopeId in userSession.permissions) {
129
+ const scope = userSession.permissions[scopeId];
130
+ if (scope.permissions && Array.isArray(scope.permissions)) {
131
+ for (const perm of scope.permissions) {
132
+ if (perm.startsWith(`${basePermission}.`)) {
133
+ return true;
134
+ }
135
+ }
136
+ }
137
+ }
138
+ return false;
139
+ }
140
+ export function checkPermissions(userSession, required) {
141
+ const missing = [];
142
+ for (const req of required) {
143
+ const allToCheck = [req.permission, ...(req.alternatives || [])];
144
+ const hasPerm = allToCheck.some(p => {
145
+ // Handle field permissions specially
146
+ if (p.includes('.viewfield.') || p.includes('.editfield.')) {
147
+ const basePerm = p.replace('.*', '');
148
+ return hasFieldPermission(userSession, basePerm);
149
+ }
150
+ return hasPermission(userSession, p);
151
+ });
152
+ if (!hasPerm) {
153
+ missing.push(req);
154
+ }
155
+ }
156
+ return { hasAll: missing.length === 0, missing };
157
+ }
158
+ // Generate user-friendly permission error message
159
+ export function formatPermissionError(operation, missing) {
160
+ let message = `**Permission Error:** Cannot ${operation}.\n\n`;
161
+ message += `The following permissions are missing:\n\n`;
162
+ for (const m of missing) {
163
+ message += `- \`${m.permission}\``;
164
+ if (m.alternatives?.length) {
165
+ message += ` (or ${m.alternatives.map(a => `\`${a}\``).join(', ')})`;
166
+ }
167
+ message += ` - ${m.description}\n`;
168
+ }
169
+ message += `\n**To fix:** Update the application token permissions in the Qik admin panel.`;
170
+ return message;
171
+ }
172
+ // Pre-defined permission requirements for common operations
173
+ export const WORKFLOW_PERMISSIONS = {
174
+ view: [
175
+ { permission: 'definition.viewany', alternatives: ['definition.viewown'], description: 'View workflow definitions' },
176
+ { permission: 'definition.viewfield.*', description: 'View definition fields (title, workflow structure, etc.)' }
177
+ ],
178
+ edit: [
179
+ { permission: 'definition.editany', alternatives: ['definition.editown'], description: 'Edit workflow definitions' },
180
+ { permission: 'definition.editfield.*', description: 'Edit definition fields (title, workflow structure, etc.)' }
181
+ ],
182
+ create: [
183
+ { permission: 'definition.create', description: 'Create new definitions' }
184
+ ]
185
+ };
109
186
  // Helper function to get user session data
110
187
  export async function getUserSessionData() {
111
188
  const configManager = new ConfigManager();