@object-ui/data-objectstack 2.0.0 → 3.0.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.
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: resultObj.value || [],
515
- total: resultObj.count || (resultObj.value ? resultObj.value.length : 0),
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 ? (resultObj.value?.length || 0) === params.$top : false
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.getObject(objectName);
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
  });