@object-ui/data-objectstack 0.5.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) {
@@ -377,11 +747,15 @@ var ObjectStackAdapter = class {
377
747
  __publicField(this, "maxReconnectAttempts");
378
748
  __publicField(this, "reconnectDelay");
379
749
  __publicField(this, "reconnectAttempts", 0);
750
+ __publicField(this, "baseUrl");
751
+ __publicField(this, "token");
380
752
  this.client = new import_client.ObjectStackClient(config);
381
753
  this.metadataCache = new MetadataCache(config.cache);
382
754
  this.autoReconnect = config.autoReconnect ?? true;
383
755
  this.maxReconnectAttempts = config.maxReconnectAttempts ?? 3;
384
756
  this.reconnectDelay = config.reconnectDelay ?? 1e3;
757
+ this.baseUrl = config.baseUrl;
758
+ this.token = config.token;
385
759
  }
386
760
  /**
387
761
  * Ensure the client is connected to the server.
@@ -506,13 +880,15 @@ var ObjectStackAdapter = class {
506
880
  };
507
881
  }
508
882
  const resultObj = result;
883
+ const records = resultObj.records || resultObj.value || [];
884
+ const total = resultObj.total ?? resultObj.count ?? records.length;
509
885
  return {
510
- data: resultObj.value || [],
511
- total: resultObj.count || (resultObj.value ? resultObj.value.length : 0),
886
+ data: records,
887
+ total,
512
888
  // Calculate page number safely
513
889
  page: params?.$skip && params.$top ? Math.floor(params.$skip / params.$top) + 1 : 1,
514
890
  pageSize: params?.$top,
515
- hasMore: params?.$top ? (resultObj.value?.length || 0) === params.$top : false
891
+ hasMore: params?.$top ? records.length === params.$top : false
516
892
  };
517
893
  }
518
894
  /**
@@ -521,8 +897,8 @@ var ObjectStackAdapter = class {
521
897
  async findOne(resource, id, _params) {
522
898
  await this.connect();
523
899
  try {
524
- const record = await this.client.data.get(resource, String(id));
525
- return record;
900
+ const result = await this.client.data.get(resource, String(id));
901
+ return result.record;
526
902
  } catch (error) {
527
903
  if (error?.status === 404) {
528
904
  return null;
@@ -535,14 +911,16 @@ var ObjectStackAdapter = class {
535
911
  */
536
912
  async create(resource, data) {
537
913
  await this.connect();
538
- return this.client.data.create(resource, data);
914
+ const result = await this.client.data.create(resource, data);
915
+ return result.record;
539
916
  }
540
917
  /**
541
918
  * Update an existing record.
542
919
  */
543
920
  async update(resource, id, data) {
544
921
  await this.connect();
545
- return this.client.data.update(resource, String(id), data);
922
+ const result = await this.client.data.update(resource, String(id), data);
923
+ return result.record;
546
924
  }
547
925
  /**
548
926
  * Delete a record.
@@ -550,7 +928,7 @@ var ObjectStackAdapter = class {
550
928
  async delete(resource, id) {
551
929
  await this.connect();
552
930
  const result = await this.client.data.delete(resource, String(id));
553
- return result.success;
931
+ return result.deleted;
554
932
  }
555
933
  /**
556
934
  * Bulk operations with optimized batch processing and error handling.
@@ -632,7 +1010,7 @@ var ObjectStackAdapter = class {
632
1010
  }
633
1011
  try {
634
1012
  const result = await this.client.data.update(resource, String(id), item);
635
- results.push(result);
1013
+ results.push(result.record);
636
1014
  completed++;
637
1015
  emitProgress();
638
1016
  } catch (error) {
@@ -733,7 +1111,7 @@ var ObjectStackAdapter = class {
733
1111
  await this.connect();
734
1112
  try {
735
1113
  const schema = await this.metadataCache.get(objectName, async () => {
736
- const result = await this.client.meta.getObject(objectName);
1114
+ const result = await this.client.meta.getItem("object", objectName);
737
1115
  if (result && result.item) {
738
1116
  return result.item;
739
1117
  }
@@ -757,6 +1135,111 @@ var ObjectStackAdapter = class {
757
1135
  getClient() {
758
1136
  return this.client;
759
1137
  }
1138
+ /**
1139
+ * Get the discovery information from the connected server.
1140
+ * Returns the capabilities and service status of the ObjectStack server.
1141
+ *
1142
+ * Note: This accesses an internal property of the ObjectStackClient.
1143
+ * The discovery data is populated during client.connect() and cached.
1144
+ *
1145
+ * @returns Promise resolving to discovery data, or null if not connected
1146
+ */
1147
+ async getDiscovery() {
1148
+ try {
1149
+ await this.connect();
1150
+ return this.client.discoveryInfo || null;
1151
+ } catch {
1152
+ return null;
1153
+ }
1154
+ }
1155
+ /**
1156
+ * Get a view definition for an object.
1157
+ * Attempts to fetch from the server metadata API.
1158
+ * Falls back to null if the server doesn't provide view definitions,
1159
+ * allowing the consumer to use static config.
1160
+ *
1161
+ * @param objectName - Object name
1162
+ * @param viewId - View identifier
1163
+ * @returns Promise resolving to the view definition or null
1164
+ */
1165
+ async getView(objectName, viewId) {
1166
+ await this.connect();
1167
+ try {
1168
+ const cacheKey = `view:${objectName}:${viewId}`;
1169
+ return await this.metadataCache.get(cacheKey, async () => {
1170
+ const result = await this.client.meta.getItem(objectName, `views/${viewId}`);
1171
+ if (result && result.item) return result.item;
1172
+ return result ?? null;
1173
+ });
1174
+ } catch {
1175
+ return null;
1176
+ }
1177
+ }
1178
+ /**
1179
+ * Get an application definition by name or ID.
1180
+ * Attempts to fetch from the server metadata API.
1181
+ * Falls back to null if the server doesn't provide app definitions,
1182
+ * allowing the consumer to use static config.
1183
+ *
1184
+ * @param appId - Application identifier
1185
+ * @returns Promise resolving to the app definition or null
1186
+ */
1187
+ async getApp(appId) {
1188
+ await this.connect();
1189
+ try {
1190
+ const cacheKey = `app:${appId}`;
1191
+ return await this.metadataCache.get(cacheKey, async () => {
1192
+ const result = await this.client.meta.getItem("apps", appId);
1193
+ if (result && result.item) return result.item;
1194
+ return result ?? null;
1195
+ });
1196
+ } catch {
1197
+ return null;
1198
+ }
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
+ }
760
1243
  /**
761
1244
  * Get cache statistics for monitoring performance.
762
1245
  */
@@ -777,6 +1260,98 @@ var ObjectStackAdapter = class {
777
1260
  clearCache() {
778
1261
  this.metadataCache.clear();
779
1262
  }
1263
+ /**
1264
+ * Upload a single file to a resource.
1265
+ * Posts the file as multipart/form-data to the ObjectStack server.
1266
+ *
1267
+ * @param resource - The resource/object name to attach the file to
1268
+ * @param file - File object or Blob to upload
1269
+ * @param options - Additional upload options (recordId, fieldName, metadata)
1270
+ * @returns Promise resolving to the upload result (file URL, metadata)
1271
+ */
1272
+ async uploadFile(resource, file, options) {
1273
+ await this.connect();
1274
+ const formData = new FormData();
1275
+ formData.append("file", file);
1276
+ if (options?.recordId) {
1277
+ formData.append("recordId", options.recordId);
1278
+ }
1279
+ if (options?.fieldName) {
1280
+ formData.append("fieldName", options.fieldName);
1281
+ }
1282
+ if (options?.metadata) {
1283
+ formData.append("metadata", JSON.stringify(options.metadata));
1284
+ }
1285
+ const url = `${this.baseUrl}/api/data/${encodeURIComponent(resource)}/upload`;
1286
+ const response = await fetch(url, {
1287
+ method: "POST",
1288
+ body: formData,
1289
+ headers: {
1290
+ ...this.getAuthHeaders()
1291
+ }
1292
+ });
1293
+ if (!response.ok) {
1294
+ const error = await response.json().catch(() => ({ message: response.statusText }));
1295
+ throw new ObjectStackError(
1296
+ error.message || `Upload failed with status ${response.status}`,
1297
+ "UPLOAD_ERROR",
1298
+ response.status
1299
+ );
1300
+ }
1301
+ return response.json();
1302
+ }
1303
+ /**
1304
+ * Upload multiple files to a resource.
1305
+ * Posts all files as a single multipart/form-data request.
1306
+ *
1307
+ * @param resource - The resource/object name to attach the files to
1308
+ * @param files - Array of File objects or Blobs to upload
1309
+ * @param options - Additional upload options
1310
+ * @returns Promise resolving to array of upload results
1311
+ */
1312
+ async uploadFiles(resource, files, options) {
1313
+ await this.connect();
1314
+ const formData = new FormData();
1315
+ files.forEach((file, idx) => {
1316
+ formData.append(`files`, file, file.name || `file-${idx}`);
1317
+ });
1318
+ if (options?.recordId) {
1319
+ formData.append("recordId", options.recordId);
1320
+ }
1321
+ if (options?.fieldName) {
1322
+ formData.append("fieldName", options.fieldName);
1323
+ }
1324
+ if (options?.metadata) {
1325
+ formData.append("metadata", JSON.stringify(options.metadata));
1326
+ }
1327
+ const url = `${this.baseUrl}/api/data/${encodeURIComponent(resource)}/upload`;
1328
+ const response = await fetch(url, {
1329
+ method: "POST",
1330
+ body: formData,
1331
+ headers: {
1332
+ ...this.getAuthHeaders()
1333
+ }
1334
+ });
1335
+ if (!response.ok) {
1336
+ const error = await response.json().catch(() => ({ message: response.statusText }));
1337
+ throw new ObjectStackError(
1338
+ error.message || `Upload failed with status ${response.status}`,
1339
+ "UPLOAD_ERROR",
1340
+ response.status
1341
+ );
1342
+ }
1343
+ return response.json();
1344
+ }
1345
+ /**
1346
+ * Get authorization headers from the adapter config.
1347
+ */
1348
+ getAuthHeaders() {
1349
+ const headers = {};
1350
+ if (this.token) {
1351
+ headers["Authorization"] = `Bearer ${this.token}`;
1352
+ }
1353
+ return headers;
1354
+ }
780
1355
  };
781
1356
  function createObjectStackAdapter(config) {
782
1357
  return new ObjectStackAdapter(config);
@@ -785,13 +1360,21 @@ function createObjectStackAdapter(config) {
785
1360
  0 && (module.exports = {
786
1361
  AuthenticationError,
787
1362
  BulkOperationError,
1363
+ CloudOperations,
788
1364
  ConnectionError,
1365
+ IntegrationManager,
789
1366
  MetadataNotFoundError,
790
1367
  ObjectStackAdapter,
791
1368
  ObjectStackError,
1369
+ SecurityManager,
792
1370
  ValidationError,
1371
+ calculateAutoLayout,
1372
+ createDefaultCanvasConfig,
793
1373
  createErrorFromResponse,
794
1374
  createObjectStackAdapter,
1375
+ generateContractManifest,
795
1376
  isErrorType,
796
- isObjectStackError
1377
+ isObjectStackError,
1378
+ snapToGrid,
1379
+ validatePluginContract
797
1380
  });