@object-ui/data-objectstack 2.0.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +429 -6
- package/dist/index.d.cts +496 -1
- package/dist/index.d.ts +496 -1
- package/dist/index.js +420 -5
- package/package.json +6 -6
- package/src/cache/MetadataCache.ts +22 -0
- package/src/cloud.ts +109 -0
- package/src/contracts.ts +115 -0
- package/src/index.ts +73 -5
- package/src/integration.ts +192 -0
- package/src/security.ts +230 -0
- package/src/studio.ts +152 -0
- package/src/v3-compat.test.ts +240 -0
package/dist/index.cjs
CHANGED
|
@@ -24,15 +24,23 @@ var index_exports = {};
|
|
|
24
24
|
__export(index_exports, {
|
|
25
25
|
AuthenticationError: () => AuthenticationError,
|
|
26
26
|
BulkOperationError: () => BulkOperationError,
|
|
27
|
+
CloudOperations: () => CloudOperations,
|
|
27
28
|
ConnectionError: () => ConnectionError,
|
|
29
|
+
IntegrationManager: () => IntegrationManager,
|
|
28
30
|
MetadataNotFoundError: () => MetadataNotFoundError,
|
|
29
31
|
ObjectStackAdapter: () => ObjectStackAdapter,
|
|
30
32
|
ObjectStackError: () => ObjectStackError,
|
|
33
|
+
SecurityManager: () => SecurityManager,
|
|
31
34
|
ValidationError: () => ValidationError,
|
|
35
|
+
calculateAutoLayout: () => calculateAutoLayout,
|
|
36
|
+
createDefaultCanvasConfig: () => createDefaultCanvasConfig,
|
|
32
37
|
createErrorFromResponse: () => createErrorFromResponse,
|
|
33
38
|
createObjectStackAdapter: () => createObjectStackAdapter,
|
|
39
|
+
generateContractManifest: () => generateContractManifest,
|
|
34
40
|
isErrorType: () => isErrorType,
|
|
35
|
-
isObjectStackError: () => isObjectStackError
|
|
41
|
+
isObjectStackError: () => isObjectStackError,
|
|
42
|
+
snapToGrid: () => snapToGrid,
|
|
43
|
+
validatePluginContract: () => validatePluginContract
|
|
36
44
|
});
|
|
37
45
|
module.exports = __toCommonJS(index_exports);
|
|
38
46
|
var import_client = require("@objectstack/client");
|
|
@@ -157,6 +165,22 @@ var MetadataCache = class {
|
|
|
157
165
|
hitRate
|
|
158
166
|
};
|
|
159
167
|
}
|
|
168
|
+
/**
|
|
169
|
+
* Get a cached value synchronously without triggering a fetch.
|
|
170
|
+
* Returns undefined if not in cache or expired.
|
|
171
|
+
*/
|
|
172
|
+
getCachedSync(key) {
|
|
173
|
+
const entry = this.cache.get(key);
|
|
174
|
+
if (!entry) return void 0;
|
|
175
|
+
if (this.ttl > 0 && Date.now() - entry.timestamp > this.ttl) {
|
|
176
|
+
this.cache.delete(key);
|
|
177
|
+
return void 0;
|
|
178
|
+
}
|
|
179
|
+
this.cache.delete(key);
|
|
180
|
+
this.cache.set(key, entry);
|
|
181
|
+
this.stats.hits++;
|
|
182
|
+
return entry.data;
|
|
183
|
+
}
|
|
160
184
|
/**
|
|
161
185
|
* Check if a key exists in the cache (and is not expired)
|
|
162
186
|
*
|
|
@@ -364,6 +388,352 @@ function isErrorType(error, errorClass) {
|
|
|
364
388
|
return error instanceof errorClass;
|
|
365
389
|
}
|
|
366
390
|
|
|
391
|
+
// src/cloud.ts
|
|
392
|
+
var CloudOperations = class {
|
|
393
|
+
constructor(getClient) {
|
|
394
|
+
this.getClient = getClient;
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Deploy an application to the cloud.
|
|
398
|
+
*/
|
|
399
|
+
async deploy(appId, config) {
|
|
400
|
+
const client = this.getClient();
|
|
401
|
+
const result = await client.cloud?.deploy?.(appId, config);
|
|
402
|
+
return result ?? { deploymentId: `deploy-${Date.now()}`, status: "pending" };
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Get deployment status.
|
|
406
|
+
*/
|
|
407
|
+
async getDeploymentStatus(deploymentId) {
|
|
408
|
+
const client = this.getClient();
|
|
409
|
+
const result = await client.cloud?.getDeployment?.(deploymentId);
|
|
410
|
+
return result ?? { status: "unknown" };
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Search marketplace entries.
|
|
414
|
+
*/
|
|
415
|
+
async searchMarketplace(query, category) {
|
|
416
|
+
const client = this.getClient();
|
|
417
|
+
const result = await client.cloud?.marketplace?.search?.({ query, category });
|
|
418
|
+
return result?.items ?? [];
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Install a marketplace plugin.
|
|
422
|
+
*/
|
|
423
|
+
async installPlugin(pluginId) {
|
|
424
|
+
const client = this.getClient();
|
|
425
|
+
const result = await client.cloud?.marketplace?.install?.(pluginId);
|
|
426
|
+
return result ?? { success: false };
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
// src/contracts.ts
|
|
431
|
+
function validatePluginContract(contract) {
|
|
432
|
+
const errors = [];
|
|
433
|
+
const warnings = [];
|
|
434
|
+
if (!contract.name || contract.name.trim().length === 0) {
|
|
435
|
+
errors.push({ field: "name", message: "Plugin name is required", code: "MISSING_NAME" });
|
|
436
|
+
}
|
|
437
|
+
if (!contract.version || !/^\d+\.\d+\.\d+/.test(contract.version)) {
|
|
438
|
+
errors.push({ field: "version", message: "Valid semver version is required", code: "INVALID_VERSION" });
|
|
439
|
+
}
|
|
440
|
+
if (!contract.exports || contract.exports.length === 0) {
|
|
441
|
+
errors.push({ field: "exports", message: "At least one export is required", code: "NO_EXPORTS" });
|
|
442
|
+
}
|
|
443
|
+
if (contract.exports) {
|
|
444
|
+
const validTypes = ["component", "hook", "utility", "provider"];
|
|
445
|
+
for (const exp of contract.exports) {
|
|
446
|
+
if (!exp.name) {
|
|
447
|
+
errors.push({ field: "exports.name", message: "Export name is required", code: "MISSING_EXPORT_NAME" });
|
|
448
|
+
}
|
|
449
|
+
if (!validTypes.includes(exp.type)) {
|
|
450
|
+
errors.push({ field: "exports.type", message: `Invalid export type: ${exp.type}`, code: "INVALID_EXPORT_TYPE" });
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
if (!contract.permissions || contract.permissions.length === 0) {
|
|
455
|
+
warnings.push("No permissions declared \u2014 plugin will have minimal access");
|
|
456
|
+
}
|
|
457
|
+
return { valid: errors.length === 0, errors, warnings };
|
|
458
|
+
}
|
|
459
|
+
function generateContractManifest(contract) {
|
|
460
|
+
return {
|
|
461
|
+
$schema: "https://objectui.org/schemas/plugin-contract-v1.json",
|
|
462
|
+
name: contract.name,
|
|
463
|
+
version: contract.version,
|
|
464
|
+
peerDependencies: contract.peerDependencies ?? {},
|
|
465
|
+
exports: contract.exports.map((exp) => ({
|
|
466
|
+
name: exp.name,
|
|
467
|
+
type: exp.type,
|
|
468
|
+
description: exp.description ?? ""
|
|
469
|
+
})),
|
|
470
|
+
permissions: contract.permissions ?? [],
|
|
471
|
+
api: contract.api ?? {},
|
|
472
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// src/integration.ts
|
|
477
|
+
var IntegrationManager = class {
|
|
478
|
+
constructor() {
|
|
479
|
+
__publicField(this, "integrations", /* @__PURE__ */ new Map());
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Register a new integration.
|
|
483
|
+
*/
|
|
484
|
+
register(id, config) {
|
|
485
|
+
this.integrations.set(id, config);
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* Remove an integration.
|
|
489
|
+
*/
|
|
490
|
+
unregister(id) {
|
|
491
|
+
this.integrations.delete(id);
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Get all registered integrations.
|
|
495
|
+
*/
|
|
496
|
+
getAll() {
|
|
497
|
+
return new Map(this.integrations);
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Get integrations that match a specific event.
|
|
501
|
+
*/
|
|
502
|
+
getForEvent(event) {
|
|
503
|
+
return Array.from(this.integrations.values()).filter(
|
|
504
|
+
(integration) => integration.enabled && integration.triggers?.some((t) => t.event === event)
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* Dispatch an event to all matching integrations.
|
|
509
|
+
* Returns results for each integration.
|
|
510
|
+
*/
|
|
511
|
+
async dispatch(event, payload) {
|
|
512
|
+
const matching = this.getForEvent(event);
|
|
513
|
+
const results = [];
|
|
514
|
+
for (const [id, integration] of this.integrations) {
|
|
515
|
+
if (!matching.includes(integration)) continue;
|
|
516
|
+
try {
|
|
517
|
+
await this.send(integration, payload);
|
|
518
|
+
results.push({ id, success: true });
|
|
519
|
+
} catch (err) {
|
|
520
|
+
results.push({ id, success: false, error: err.message });
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
return results;
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Send payload to a specific integration.
|
|
527
|
+
*/
|
|
528
|
+
async send(integration, payload) {
|
|
529
|
+
switch (integration.provider) {
|
|
530
|
+
case "webhook": {
|
|
531
|
+
const cfg = integration.config;
|
|
532
|
+
const url = cfg.url;
|
|
533
|
+
try {
|
|
534
|
+
const parsed = new URL(url);
|
|
535
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
536
|
+
throw new Error(`Invalid URL protocol: ${parsed.protocol}`);
|
|
537
|
+
}
|
|
538
|
+
} catch (e) {
|
|
539
|
+
if (e instanceof TypeError) {
|
|
540
|
+
throw new Error(`Invalid webhook URL: ${url}`);
|
|
541
|
+
}
|
|
542
|
+
throw e;
|
|
543
|
+
}
|
|
544
|
+
await fetch(url, {
|
|
545
|
+
method: cfg.method,
|
|
546
|
+
headers: {
|
|
547
|
+
"Content-Type": "application/json",
|
|
548
|
+
...cfg.headers
|
|
549
|
+
},
|
|
550
|
+
body: JSON.stringify(payload)
|
|
551
|
+
});
|
|
552
|
+
break;
|
|
553
|
+
}
|
|
554
|
+
case "slack": {
|
|
555
|
+
const cfg = integration.config;
|
|
556
|
+
const url = cfg.webhookUrl;
|
|
557
|
+
try {
|
|
558
|
+
const parsed = new URL(url);
|
|
559
|
+
if (parsed.protocol !== "https:") {
|
|
560
|
+
throw new Error(`Invalid Slack webhook URL protocol: ${parsed.protocol}`);
|
|
561
|
+
}
|
|
562
|
+
} catch (e) {
|
|
563
|
+
if (e instanceof TypeError) {
|
|
564
|
+
throw new Error(`Invalid Slack webhook URL: ${url}`);
|
|
565
|
+
}
|
|
566
|
+
throw e;
|
|
567
|
+
}
|
|
568
|
+
await fetch(url, {
|
|
569
|
+
method: "POST",
|
|
570
|
+
headers: { "Content-Type": "application/json" },
|
|
571
|
+
body: JSON.stringify({
|
|
572
|
+
channel: cfg.channel,
|
|
573
|
+
username: cfg.username,
|
|
574
|
+
icon_emoji: cfg.iconEmoji,
|
|
575
|
+
text: JSON.stringify(payload)
|
|
576
|
+
})
|
|
577
|
+
});
|
|
578
|
+
break;
|
|
579
|
+
}
|
|
580
|
+
// Email and other providers would require server-side implementation
|
|
581
|
+
default:
|
|
582
|
+
break;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
};
|
|
586
|
+
|
|
587
|
+
// src/security.ts
|
|
588
|
+
var SecurityManager = class {
|
|
589
|
+
constructor(policy = {}) {
|
|
590
|
+
__publicField(this, "policy");
|
|
591
|
+
__publicField(this, "auditLog", []);
|
|
592
|
+
this.policy = policy;
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* Generate a CSP header string from the configuration.
|
|
596
|
+
*/
|
|
597
|
+
generateCSPHeader() {
|
|
598
|
+
const csp = this.policy.csp;
|
|
599
|
+
if (!csp) return "";
|
|
600
|
+
const directives = [];
|
|
601
|
+
if (csp.scriptSrc?.length) directives.push(`script-src ${csp.scriptSrc.join(" ")}`);
|
|
602
|
+
if (csp.styleSrc?.length) directives.push(`style-src ${csp.styleSrc.join(" ")}`);
|
|
603
|
+
if (csp.imgSrc?.length) directives.push(`img-src ${csp.imgSrc.join(" ")}`);
|
|
604
|
+
if (csp.connectSrc?.length) directives.push(`connect-src ${csp.connectSrc.join(" ")}`);
|
|
605
|
+
if (csp.fontSrc?.length) directives.push(`font-src ${csp.fontSrc.join(" ")}`);
|
|
606
|
+
if (csp.frameSrc?.length) directives.push(`frame-src ${csp.frameSrc.join(" ")}`);
|
|
607
|
+
if (csp.reportUri) directives.push(`report-uri ${csp.reportUri}`);
|
|
608
|
+
return directives.join("; ");
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* Record an audit log entry.
|
|
612
|
+
*/
|
|
613
|
+
recordAudit(entry) {
|
|
614
|
+
if (!this.policy.auditLog?.enabled) return;
|
|
615
|
+
if (this.policy.auditLog.events && !this.policy.auditLog.events.includes(entry.event)) return;
|
|
616
|
+
const fullEntry = {
|
|
617
|
+
...entry,
|
|
618
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
619
|
+
};
|
|
620
|
+
this.auditLog.push(fullEntry);
|
|
621
|
+
const dest = this.policy.auditLog.destination ?? "console";
|
|
622
|
+
if (dest === "console" || dest === "both") {
|
|
623
|
+
console.info("[AUDIT]", fullEntry.event, fullEntry.userId, fullEntry.resource ?? "");
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
/**
|
|
627
|
+
* Get audit log entries.
|
|
628
|
+
*/
|
|
629
|
+
getAuditLog(filter) {
|
|
630
|
+
let entries = [...this.auditLog];
|
|
631
|
+
if (filter?.event) entries = entries.filter((e) => e.event === filter.event);
|
|
632
|
+
if (filter?.userId) entries = entries.filter((e) => e.userId === filter.userId);
|
|
633
|
+
if (filter?.since) entries = entries.filter((e) => e.timestamp >= filter.since);
|
|
634
|
+
return entries;
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* Apply data masking to a record.
|
|
638
|
+
*/
|
|
639
|
+
maskRecord(record, userRoles = []) {
|
|
640
|
+
if (!this.policy.dataMasking?.rules?.length) return record;
|
|
641
|
+
const masked = { ...record };
|
|
642
|
+
const maskChar = this.policy.dataMasking.maskChar ?? "*";
|
|
643
|
+
for (const rule of this.policy.dataMasking.rules) {
|
|
644
|
+
if (!(rule.field in masked) || masked[rule.field] == null) continue;
|
|
645
|
+
if (rule.exemptRoles?.some((role) => userRoles.includes(role))) continue;
|
|
646
|
+
const value = String(masked[rule.field]);
|
|
647
|
+
switch (rule.strategy) {
|
|
648
|
+
case "full":
|
|
649
|
+
masked[rule.field] = maskChar.repeat(value.length);
|
|
650
|
+
break;
|
|
651
|
+
case "partial": {
|
|
652
|
+
const visible = rule.visibleChars ?? 4;
|
|
653
|
+
if (value.length <= visible) {
|
|
654
|
+
masked[rule.field] = maskChar.repeat(value.length);
|
|
655
|
+
} else {
|
|
656
|
+
masked[rule.field] = value.slice(0, visible) + maskChar.repeat(value.length - visible);
|
|
657
|
+
}
|
|
658
|
+
break;
|
|
659
|
+
}
|
|
660
|
+
case "hash":
|
|
661
|
+
masked[rule.field] = `[HASHED:${simpleHash(value)}]`;
|
|
662
|
+
break;
|
|
663
|
+
case "redact":
|
|
664
|
+
masked[rule.field] = "[REDACTED]";
|
|
665
|
+
break;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
return masked;
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Update the security policy.
|
|
672
|
+
*/
|
|
673
|
+
updatePolicy(policy) {
|
|
674
|
+
this.policy = { ...this.policy, ...policy };
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Get current security policy.
|
|
678
|
+
*/
|
|
679
|
+
getPolicy() {
|
|
680
|
+
return { ...this.policy };
|
|
681
|
+
}
|
|
682
|
+
};
|
|
683
|
+
function simpleHash(str) {
|
|
684
|
+
let hash = 0;
|
|
685
|
+
for (let i = 0; i < str.length; i++) {
|
|
686
|
+
const char = str.charCodeAt(i);
|
|
687
|
+
hash = (hash << 5) - hash + char;
|
|
688
|
+
hash = hash & hash;
|
|
689
|
+
}
|
|
690
|
+
return Math.abs(hash).toString(36);
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
// src/studio.ts
|
|
694
|
+
function createDefaultCanvasConfig(overrides) {
|
|
695
|
+
return {
|
|
696
|
+
width: 1200,
|
|
697
|
+
height: 800,
|
|
698
|
+
background: "grid",
|
|
699
|
+
gridSize: 8,
|
|
700
|
+
snapToGrid: true,
|
|
701
|
+
zoom: {
|
|
702
|
+
min: 0.25,
|
|
703
|
+
max: 3,
|
|
704
|
+
step: 0.1,
|
|
705
|
+
current: 1
|
|
706
|
+
},
|
|
707
|
+
panOffset: { x: 0, y: 0 },
|
|
708
|
+
showMinimap: false,
|
|
709
|
+
minimapPosition: "bottom-right",
|
|
710
|
+
...overrides
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
function snapToGrid(x, y, gridSize) {
|
|
714
|
+
return {
|
|
715
|
+
x: Math.round(x / gridSize) * gridSize,
|
|
716
|
+
y: Math.round(y / gridSize) * gridSize
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
function calculateAutoLayout(items, canvasWidth, padding = 40, gap = 40) {
|
|
720
|
+
const positions = [];
|
|
721
|
+
let currentX = padding;
|
|
722
|
+
let currentY = padding;
|
|
723
|
+
let rowMaxHeight = 0;
|
|
724
|
+
for (const item of items) {
|
|
725
|
+
if (currentX + item.width + padding > canvasWidth) {
|
|
726
|
+
currentX = padding;
|
|
727
|
+
currentY += rowMaxHeight + gap;
|
|
728
|
+
rowMaxHeight = 0;
|
|
729
|
+
}
|
|
730
|
+
positions.push({ id: item.id, x: currentX, y: currentY });
|
|
731
|
+
currentX += item.width + gap;
|
|
732
|
+
rowMaxHeight = Math.max(rowMaxHeight, item.height);
|
|
733
|
+
}
|
|
734
|
+
return positions;
|
|
735
|
+
}
|
|
736
|
+
|
|
367
737
|
// src/index.ts
|
|
368
738
|
var ObjectStackAdapter = class {
|
|
369
739
|
constructor(config) {
|
|
@@ -510,13 +880,15 @@ var ObjectStackAdapter = class {
|
|
|
510
880
|
};
|
|
511
881
|
}
|
|
512
882
|
const resultObj = result;
|
|
883
|
+
const records = resultObj.records || resultObj.value || [];
|
|
884
|
+
const total = resultObj.total ?? resultObj.count ?? records.length;
|
|
513
885
|
return {
|
|
514
|
-
data:
|
|
515
|
-
total
|
|
886
|
+
data: records,
|
|
887
|
+
total,
|
|
516
888
|
// Calculate page number safely
|
|
517
889
|
page: params?.$skip && params.$top ? Math.floor(params.$skip / params.$top) + 1 : 1,
|
|
518
890
|
pageSize: params?.$top,
|
|
519
|
-
hasMore: params?.$top ?
|
|
891
|
+
hasMore: params?.$top ? records.length === params.$top : false
|
|
520
892
|
};
|
|
521
893
|
}
|
|
522
894
|
/**
|
|
@@ -739,7 +1111,7 @@ var ObjectStackAdapter = class {
|
|
|
739
1111
|
await this.connect();
|
|
740
1112
|
try {
|
|
741
1113
|
const schema = await this.metadataCache.get(objectName, async () => {
|
|
742
|
-
const result = await this.client.meta.
|
|
1114
|
+
const result = await this.client.meta.getItem("object", objectName);
|
|
743
1115
|
if (result && result.item) {
|
|
744
1116
|
return result.item;
|
|
745
1117
|
}
|
|
@@ -825,6 +1197,49 @@ var ObjectStackAdapter = class {
|
|
|
825
1197
|
return null;
|
|
826
1198
|
}
|
|
827
1199
|
}
|
|
1200
|
+
/**
|
|
1201
|
+
* Get a page definition from ObjectStack.
|
|
1202
|
+
* Uses the metadata API to fetch page layouts.
|
|
1203
|
+
* Returns null if the server doesn't support page metadata.
|
|
1204
|
+
*/
|
|
1205
|
+
async getPage(pageId) {
|
|
1206
|
+
await this.connect();
|
|
1207
|
+
try {
|
|
1208
|
+
const cacheKey = `page:${pageId}`;
|
|
1209
|
+
return await this.metadataCache.get(cacheKey, async () => {
|
|
1210
|
+
const result = await this.client.meta.getItem("pages", pageId);
|
|
1211
|
+
if (result && result.item) return result.item;
|
|
1212
|
+
return result ?? null;
|
|
1213
|
+
});
|
|
1214
|
+
} catch {
|
|
1215
|
+
return null;
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
/**
|
|
1219
|
+
* Get multiple metadata items from ObjectStack.
|
|
1220
|
+
* Uses v3.0.0 metadata API pattern: getItems for batch retrieval.
|
|
1221
|
+
*/
|
|
1222
|
+
async getItems(category, names) {
|
|
1223
|
+
await this.connect();
|
|
1224
|
+
const results = await Promise.all(
|
|
1225
|
+
names.map(async (name) => {
|
|
1226
|
+
const cacheKey = `${category}:${name}`;
|
|
1227
|
+
return this.metadataCache.get(cacheKey, async () => {
|
|
1228
|
+
const result = await this.client.meta.getItem(category, name);
|
|
1229
|
+
if (result && result.item) return result.item;
|
|
1230
|
+
return result;
|
|
1231
|
+
});
|
|
1232
|
+
})
|
|
1233
|
+
);
|
|
1234
|
+
return results;
|
|
1235
|
+
}
|
|
1236
|
+
/**
|
|
1237
|
+
* Get cached metadata if available, without triggering a fetch.
|
|
1238
|
+
* Uses v3.0.0 metadata API pattern: getCached for synchronous cache access.
|
|
1239
|
+
*/
|
|
1240
|
+
getCached(key) {
|
|
1241
|
+
return this.metadataCache.getCachedSync(key);
|
|
1242
|
+
}
|
|
828
1243
|
/**
|
|
829
1244
|
* Get cache statistics for monitoring performance.
|
|
830
1245
|
*/
|
|
@@ -945,13 +1360,21 @@ function createObjectStackAdapter(config) {
|
|
|
945
1360
|
0 && (module.exports = {
|
|
946
1361
|
AuthenticationError,
|
|
947
1362
|
BulkOperationError,
|
|
1363
|
+
CloudOperations,
|
|
948
1364
|
ConnectionError,
|
|
1365
|
+
IntegrationManager,
|
|
949
1366
|
MetadataNotFoundError,
|
|
950
1367
|
ObjectStackAdapter,
|
|
951
1368
|
ObjectStackError,
|
|
1369
|
+
SecurityManager,
|
|
952
1370
|
ValidationError,
|
|
1371
|
+
calculateAutoLayout,
|
|
1372
|
+
createDefaultCanvasConfig,
|
|
953
1373
|
createErrorFromResponse,
|
|
954
1374
|
createObjectStackAdapter,
|
|
1375
|
+
generateContractManifest,
|
|
955
1376
|
isErrorType,
|
|
956
|
-
isObjectStackError
|
|
1377
|
+
isObjectStackError,
|
|
1378
|
+
snapToGrid,
|
|
1379
|
+
validatePluginContract
|
|
957
1380
|
});
|