@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.js CHANGED
@@ -125,6 +125,22 @@ var MetadataCache = class {
125
125
  hitRate
126
126
  };
127
127
  }
128
+ /**
129
+ * Get a cached value synchronously without triggering a fetch.
130
+ * Returns undefined if not in cache or expired.
131
+ */
132
+ getCachedSync(key) {
133
+ const entry = this.cache.get(key);
134
+ if (!entry) return void 0;
135
+ if (this.ttl > 0 && Date.now() - entry.timestamp > this.ttl) {
136
+ this.cache.delete(key);
137
+ return void 0;
138
+ }
139
+ this.cache.delete(key);
140
+ this.cache.set(key, entry);
141
+ this.stats.hits++;
142
+ return entry.data;
143
+ }
128
144
  /**
129
145
  * Check if a key exists in the cache (and is not expired)
130
146
  *
@@ -332,6 +348,352 @@ function isErrorType(error, errorClass) {
332
348
  return error instanceof errorClass;
333
349
  }
334
350
 
351
+ // src/cloud.ts
352
+ var CloudOperations = class {
353
+ constructor(getClient) {
354
+ this.getClient = getClient;
355
+ }
356
+ /**
357
+ * Deploy an application to the cloud.
358
+ */
359
+ async deploy(appId, config) {
360
+ const client = this.getClient();
361
+ const result = await client.cloud?.deploy?.(appId, config);
362
+ return result ?? { deploymentId: `deploy-${Date.now()}`, status: "pending" };
363
+ }
364
+ /**
365
+ * Get deployment status.
366
+ */
367
+ async getDeploymentStatus(deploymentId) {
368
+ const client = this.getClient();
369
+ const result = await client.cloud?.getDeployment?.(deploymentId);
370
+ return result ?? { status: "unknown" };
371
+ }
372
+ /**
373
+ * Search marketplace entries.
374
+ */
375
+ async searchMarketplace(query, category) {
376
+ const client = this.getClient();
377
+ const result = await client.cloud?.marketplace?.search?.({ query, category });
378
+ return result?.items ?? [];
379
+ }
380
+ /**
381
+ * Install a marketplace plugin.
382
+ */
383
+ async installPlugin(pluginId) {
384
+ const client = this.getClient();
385
+ const result = await client.cloud?.marketplace?.install?.(pluginId);
386
+ return result ?? { success: false };
387
+ }
388
+ };
389
+
390
+ // src/contracts.ts
391
+ function validatePluginContract(contract) {
392
+ const errors = [];
393
+ const warnings = [];
394
+ if (!contract.name || contract.name.trim().length === 0) {
395
+ errors.push({ field: "name", message: "Plugin name is required", code: "MISSING_NAME" });
396
+ }
397
+ if (!contract.version || !/^\d+\.\d+\.\d+/.test(contract.version)) {
398
+ errors.push({ field: "version", message: "Valid semver version is required", code: "INVALID_VERSION" });
399
+ }
400
+ if (!contract.exports || contract.exports.length === 0) {
401
+ errors.push({ field: "exports", message: "At least one export is required", code: "NO_EXPORTS" });
402
+ }
403
+ if (contract.exports) {
404
+ const validTypes = ["component", "hook", "utility", "provider"];
405
+ for (const exp of contract.exports) {
406
+ if (!exp.name) {
407
+ errors.push({ field: "exports.name", message: "Export name is required", code: "MISSING_EXPORT_NAME" });
408
+ }
409
+ if (!validTypes.includes(exp.type)) {
410
+ errors.push({ field: "exports.type", message: `Invalid export type: ${exp.type}`, code: "INVALID_EXPORT_TYPE" });
411
+ }
412
+ }
413
+ }
414
+ if (!contract.permissions || contract.permissions.length === 0) {
415
+ warnings.push("No permissions declared \u2014 plugin will have minimal access");
416
+ }
417
+ return { valid: errors.length === 0, errors, warnings };
418
+ }
419
+ function generateContractManifest(contract) {
420
+ return {
421
+ $schema: "https://objectui.org/schemas/plugin-contract-v1.json",
422
+ name: contract.name,
423
+ version: contract.version,
424
+ peerDependencies: contract.peerDependencies ?? {},
425
+ exports: contract.exports.map((exp) => ({
426
+ name: exp.name,
427
+ type: exp.type,
428
+ description: exp.description ?? ""
429
+ })),
430
+ permissions: contract.permissions ?? [],
431
+ api: contract.api ?? {},
432
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString()
433
+ };
434
+ }
435
+
436
+ // src/integration.ts
437
+ var IntegrationManager = class {
438
+ constructor() {
439
+ __publicField(this, "integrations", /* @__PURE__ */ new Map());
440
+ }
441
+ /**
442
+ * Register a new integration.
443
+ */
444
+ register(id, config) {
445
+ this.integrations.set(id, config);
446
+ }
447
+ /**
448
+ * Remove an integration.
449
+ */
450
+ unregister(id) {
451
+ this.integrations.delete(id);
452
+ }
453
+ /**
454
+ * Get all registered integrations.
455
+ */
456
+ getAll() {
457
+ return new Map(this.integrations);
458
+ }
459
+ /**
460
+ * Get integrations that match a specific event.
461
+ */
462
+ getForEvent(event) {
463
+ return Array.from(this.integrations.values()).filter(
464
+ (integration) => integration.enabled && integration.triggers?.some((t) => t.event === event)
465
+ );
466
+ }
467
+ /**
468
+ * Dispatch an event to all matching integrations.
469
+ * Returns results for each integration.
470
+ */
471
+ async dispatch(event, payload) {
472
+ const matching = this.getForEvent(event);
473
+ const results = [];
474
+ for (const [id, integration] of this.integrations) {
475
+ if (!matching.includes(integration)) continue;
476
+ try {
477
+ await this.send(integration, payload);
478
+ results.push({ id, success: true });
479
+ } catch (err) {
480
+ results.push({ id, success: false, error: err.message });
481
+ }
482
+ }
483
+ return results;
484
+ }
485
+ /**
486
+ * Send payload to a specific integration.
487
+ */
488
+ async send(integration, payload) {
489
+ switch (integration.provider) {
490
+ case "webhook": {
491
+ const cfg = integration.config;
492
+ const url = cfg.url;
493
+ try {
494
+ const parsed = new URL(url);
495
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
496
+ throw new Error(`Invalid URL protocol: ${parsed.protocol}`);
497
+ }
498
+ } catch (e) {
499
+ if (e instanceof TypeError) {
500
+ throw new Error(`Invalid webhook URL: ${url}`);
501
+ }
502
+ throw e;
503
+ }
504
+ await fetch(url, {
505
+ method: cfg.method,
506
+ headers: {
507
+ "Content-Type": "application/json",
508
+ ...cfg.headers
509
+ },
510
+ body: JSON.stringify(payload)
511
+ });
512
+ break;
513
+ }
514
+ case "slack": {
515
+ const cfg = integration.config;
516
+ const url = cfg.webhookUrl;
517
+ try {
518
+ const parsed = new URL(url);
519
+ if (parsed.protocol !== "https:") {
520
+ throw new Error(`Invalid Slack webhook URL protocol: ${parsed.protocol}`);
521
+ }
522
+ } catch (e) {
523
+ if (e instanceof TypeError) {
524
+ throw new Error(`Invalid Slack webhook URL: ${url}`);
525
+ }
526
+ throw e;
527
+ }
528
+ await fetch(url, {
529
+ method: "POST",
530
+ headers: { "Content-Type": "application/json" },
531
+ body: JSON.stringify({
532
+ channel: cfg.channel,
533
+ username: cfg.username,
534
+ icon_emoji: cfg.iconEmoji,
535
+ text: JSON.stringify(payload)
536
+ })
537
+ });
538
+ break;
539
+ }
540
+ // Email and other providers would require server-side implementation
541
+ default:
542
+ break;
543
+ }
544
+ }
545
+ };
546
+
547
+ // src/security.ts
548
+ var SecurityManager = class {
549
+ constructor(policy = {}) {
550
+ __publicField(this, "policy");
551
+ __publicField(this, "auditLog", []);
552
+ this.policy = policy;
553
+ }
554
+ /**
555
+ * Generate a CSP header string from the configuration.
556
+ */
557
+ generateCSPHeader() {
558
+ const csp = this.policy.csp;
559
+ if (!csp) return "";
560
+ const directives = [];
561
+ if (csp.scriptSrc?.length) directives.push(`script-src ${csp.scriptSrc.join(" ")}`);
562
+ if (csp.styleSrc?.length) directives.push(`style-src ${csp.styleSrc.join(" ")}`);
563
+ if (csp.imgSrc?.length) directives.push(`img-src ${csp.imgSrc.join(" ")}`);
564
+ if (csp.connectSrc?.length) directives.push(`connect-src ${csp.connectSrc.join(" ")}`);
565
+ if (csp.fontSrc?.length) directives.push(`font-src ${csp.fontSrc.join(" ")}`);
566
+ if (csp.frameSrc?.length) directives.push(`frame-src ${csp.frameSrc.join(" ")}`);
567
+ if (csp.reportUri) directives.push(`report-uri ${csp.reportUri}`);
568
+ return directives.join("; ");
569
+ }
570
+ /**
571
+ * Record an audit log entry.
572
+ */
573
+ recordAudit(entry) {
574
+ if (!this.policy.auditLog?.enabled) return;
575
+ if (this.policy.auditLog.events && !this.policy.auditLog.events.includes(entry.event)) return;
576
+ const fullEntry = {
577
+ ...entry,
578
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
579
+ };
580
+ this.auditLog.push(fullEntry);
581
+ const dest = this.policy.auditLog.destination ?? "console";
582
+ if (dest === "console" || dest === "both") {
583
+ console.info("[AUDIT]", fullEntry.event, fullEntry.userId, fullEntry.resource ?? "");
584
+ }
585
+ }
586
+ /**
587
+ * Get audit log entries.
588
+ */
589
+ getAuditLog(filter) {
590
+ let entries = [...this.auditLog];
591
+ if (filter?.event) entries = entries.filter((e) => e.event === filter.event);
592
+ if (filter?.userId) entries = entries.filter((e) => e.userId === filter.userId);
593
+ if (filter?.since) entries = entries.filter((e) => e.timestamp >= filter.since);
594
+ return entries;
595
+ }
596
+ /**
597
+ * Apply data masking to a record.
598
+ */
599
+ maskRecord(record, userRoles = []) {
600
+ if (!this.policy.dataMasking?.rules?.length) return record;
601
+ const masked = { ...record };
602
+ const maskChar = this.policy.dataMasking.maskChar ?? "*";
603
+ for (const rule of this.policy.dataMasking.rules) {
604
+ if (!(rule.field in masked) || masked[rule.field] == null) continue;
605
+ if (rule.exemptRoles?.some((role) => userRoles.includes(role))) continue;
606
+ const value = String(masked[rule.field]);
607
+ switch (rule.strategy) {
608
+ case "full":
609
+ masked[rule.field] = maskChar.repeat(value.length);
610
+ break;
611
+ case "partial": {
612
+ const visible = rule.visibleChars ?? 4;
613
+ if (value.length <= visible) {
614
+ masked[rule.field] = maskChar.repeat(value.length);
615
+ } else {
616
+ masked[rule.field] = value.slice(0, visible) + maskChar.repeat(value.length - visible);
617
+ }
618
+ break;
619
+ }
620
+ case "hash":
621
+ masked[rule.field] = `[HASHED:${simpleHash(value)}]`;
622
+ break;
623
+ case "redact":
624
+ masked[rule.field] = "[REDACTED]";
625
+ break;
626
+ }
627
+ }
628
+ return masked;
629
+ }
630
+ /**
631
+ * Update the security policy.
632
+ */
633
+ updatePolicy(policy) {
634
+ this.policy = { ...this.policy, ...policy };
635
+ }
636
+ /**
637
+ * Get current security policy.
638
+ */
639
+ getPolicy() {
640
+ return { ...this.policy };
641
+ }
642
+ };
643
+ function simpleHash(str) {
644
+ let hash = 0;
645
+ for (let i = 0; i < str.length; i++) {
646
+ const char = str.charCodeAt(i);
647
+ hash = (hash << 5) - hash + char;
648
+ hash = hash & hash;
649
+ }
650
+ return Math.abs(hash).toString(36);
651
+ }
652
+
653
+ // src/studio.ts
654
+ function createDefaultCanvasConfig(overrides) {
655
+ return {
656
+ width: 1200,
657
+ height: 800,
658
+ background: "grid",
659
+ gridSize: 8,
660
+ snapToGrid: true,
661
+ zoom: {
662
+ min: 0.25,
663
+ max: 3,
664
+ step: 0.1,
665
+ current: 1
666
+ },
667
+ panOffset: { x: 0, y: 0 },
668
+ showMinimap: false,
669
+ minimapPosition: "bottom-right",
670
+ ...overrides
671
+ };
672
+ }
673
+ function snapToGrid(x, y, gridSize) {
674
+ return {
675
+ x: Math.round(x / gridSize) * gridSize,
676
+ y: Math.round(y / gridSize) * gridSize
677
+ };
678
+ }
679
+ function calculateAutoLayout(items, canvasWidth, padding = 40, gap = 40) {
680
+ const positions = [];
681
+ let currentX = padding;
682
+ let currentY = padding;
683
+ let rowMaxHeight = 0;
684
+ for (const item of items) {
685
+ if (currentX + item.width + padding > canvasWidth) {
686
+ currentX = padding;
687
+ currentY += rowMaxHeight + gap;
688
+ rowMaxHeight = 0;
689
+ }
690
+ positions.push({ id: item.id, x: currentX, y: currentY });
691
+ currentX += item.width + gap;
692
+ rowMaxHeight = Math.max(rowMaxHeight, item.height);
693
+ }
694
+ return positions;
695
+ }
696
+
335
697
  // src/index.ts
336
698
  var ObjectStackAdapter = class {
337
699
  constructor(config) {
@@ -345,11 +707,15 @@ var ObjectStackAdapter = class {
345
707
  __publicField(this, "maxReconnectAttempts");
346
708
  __publicField(this, "reconnectDelay");
347
709
  __publicField(this, "reconnectAttempts", 0);
710
+ __publicField(this, "baseUrl");
711
+ __publicField(this, "token");
348
712
  this.client = new ObjectStackClient(config);
349
713
  this.metadataCache = new MetadataCache(config.cache);
350
714
  this.autoReconnect = config.autoReconnect ?? true;
351
715
  this.maxReconnectAttempts = config.maxReconnectAttempts ?? 3;
352
716
  this.reconnectDelay = config.reconnectDelay ?? 1e3;
717
+ this.baseUrl = config.baseUrl;
718
+ this.token = config.token;
353
719
  }
354
720
  /**
355
721
  * Ensure the client is connected to the server.
@@ -474,13 +840,15 @@ var ObjectStackAdapter = class {
474
840
  };
475
841
  }
476
842
  const resultObj = result;
843
+ const records = resultObj.records || resultObj.value || [];
844
+ const total = resultObj.total ?? resultObj.count ?? records.length;
477
845
  return {
478
- data: resultObj.value || [],
479
- total: resultObj.count || (resultObj.value ? resultObj.value.length : 0),
846
+ data: records,
847
+ total,
480
848
  // Calculate page number safely
481
849
  page: params?.$skip && params.$top ? Math.floor(params.$skip / params.$top) + 1 : 1,
482
850
  pageSize: params?.$top,
483
- hasMore: params?.$top ? (resultObj.value?.length || 0) === params.$top : false
851
+ hasMore: params?.$top ? records.length === params.$top : false
484
852
  };
485
853
  }
486
854
  /**
@@ -489,8 +857,8 @@ var ObjectStackAdapter = class {
489
857
  async findOne(resource, id, _params) {
490
858
  await this.connect();
491
859
  try {
492
- const record = await this.client.data.get(resource, String(id));
493
- return record;
860
+ const result = await this.client.data.get(resource, String(id));
861
+ return result.record;
494
862
  } catch (error) {
495
863
  if (error?.status === 404) {
496
864
  return null;
@@ -503,14 +871,16 @@ var ObjectStackAdapter = class {
503
871
  */
504
872
  async create(resource, data) {
505
873
  await this.connect();
506
- return this.client.data.create(resource, data);
874
+ const result = await this.client.data.create(resource, data);
875
+ return result.record;
507
876
  }
508
877
  /**
509
878
  * Update an existing record.
510
879
  */
511
880
  async update(resource, id, data) {
512
881
  await this.connect();
513
- return this.client.data.update(resource, String(id), data);
882
+ const result = await this.client.data.update(resource, String(id), data);
883
+ return result.record;
514
884
  }
515
885
  /**
516
886
  * Delete a record.
@@ -518,7 +888,7 @@ var ObjectStackAdapter = class {
518
888
  async delete(resource, id) {
519
889
  await this.connect();
520
890
  const result = await this.client.data.delete(resource, String(id));
521
- return result.success;
891
+ return result.deleted;
522
892
  }
523
893
  /**
524
894
  * Bulk operations with optimized batch processing and error handling.
@@ -600,7 +970,7 @@ var ObjectStackAdapter = class {
600
970
  }
601
971
  try {
602
972
  const result = await this.client.data.update(resource, String(id), item);
603
- results.push(result);
973
+ results.push(result.record);
604
974
  completed++;
605
975
  emitProgress();
606
976
  } catch (error) {
@@ -701,7 +1071,7 @@ var ObjectStackAdapter = class {
701
1071
  await this.connect();
702
1072
  try {
703
1073
  const schema = await this.metadataCache.get(objectName, async () => {
704
- const result = await this.client.meta.getObject(objectName);
1074
+ const result = await this.client.meta.getItem("object", objectName);
705
1075
  if (result && result.item) {
706
1076
  return result.item;
707
1077
  }
@@ -725,6 +1095,111 @@ var ObjectStackAdapter = class {
725
1095
  getClient() {
726
1096
  return this.client;
727
1097
  }
1098
+ /**
1099
+ * Get the discovery information from the connected server.
1100
+ * Returns the capabilities and service status of the ObjectStack server.
1101
+ *
1102
+ * Note: This accesses an internal property of the ObjectStackClient.
1103
+ * The discovery data is populated during client.connect() and cached.
1104
+ *
1105
+ * @returns Promise resolving to discovery data, or null if not connected
1106
+ */
1107
+ async getDiscovery() {
1108
+ try {
1109
+ await this.connect();
1110
+ return this.client.discoveryInfo || null;
1111
+ } catch {
1112
+ return null;
1113
+ }
1114
+ }
1115
+ /**
1116
+ * Get a view definition for an object.
1117
+ * Attempts to fetch from the server metadata API.
1118
+ * Falls back to null if the server doesn't provide view definitions,
1119
+ * allowing the consumer to use static config.
1120
+ *
1121
+ * @param objectName - Object name
1122
+ * @param viewId - View identifier
1123
+ * @returns Promise resolving to the view definition or null
1124
+ */
1125
+ async getView(objectName, viewId) {
1126
+ await this.connect();
1127
+ try {
1128
+ const cacheKey = `view:${objectName}:${viewId}`;
1129
+ return await this.metadataCache.get(cacheKey, async () => {
1130
+ const result = await this.client.meta.getItem(objectName, `views/${viewId}`);
1131
+ if (result && result.item) return result.item;
1132
+ return result ?? null;
1133
+ });
1134
+ } catch {
1135
+ return null;
1136
+ }
1137
+ }
1138
+ /**
1139
+ * Get an application definition by name or ID.
1140
+ * Attempts to fetch from the server metadata API.
1141
+ * Falls back to null if the server doesn't provide app definitions,
1142
+ * allowing the consumer to use static config.
1143
+ *
1144
+ * @param appId - Application identifier
1145
+ * @returns Promise resolving to the app definition or null
1146
+ */
1147
+ async getApp(appId) {
1148
+ await this.connect();
1149
+ try {
1150
+ const cacheKey = `app:${appId}`;
1151
+ return await this.metadataCache.get(cacheKey, async () => {
1152
+ const result = await this.client.meta.getItem("apps", appId);
1153
+ if (result && result.item) return result.item;
1154
+ return result ?? null;
1155
+ });
1156
+ } catch {
1157
+ return null;
1158
+ }
1159
+ }
1160
+ /**
1161
+ * Get a page definition from ObjectStack.
1162
+ * Uses the metadata API to fetch page layouts.
1163
+ * Returns null if the server doesn't support page metadata.
1164
+ */
1165
+ async getPage(pageId) {
1166
+ await this.connect();
1167
+ try {
1168
+ const cacheKey = `page:${pageId}`;
1169
+ return await this.metadataCache.get(cacheKey, async () => {
1170
+ const result = await this.client.meta.getItem("pages", pageId);
1171
+ if (result && result.item) return result.item;
1172
+ return result ?? null;
1173
+ });
1174
+ } catch {
1175
+ return null;
1176
+ }
1177
+ }
1178
+ /**
1179
+ * Get multiple metadata items from ObjectStack.
1180
+ * Uses v3.0.0 metadata API pattern: getItems for batch retrieval.
1181
+ */
1182
+ async getItems(category, names) {
1183
+ await this.connect();
1184
+ const results = await Promise.all(
1185
+ names.map(async (name) => {
1186
+ const cacheKey = `${category}:${name}`;
1187
+ return this.metadataCache.get(cacheKey, async () => {
1188
+ const result = await this.client.meta.getItem(category, name);
1189
+ if (result && result.item) return result.item;
1190
+ return result;
1191
+ });
1192
+ })
1193
+ );
1194
+ return results;
1195
+ }
1196
+ /**
1197
+ * Get cached metadata if available, without triggering a fetch.
1198
+ * Uses v3.0.0 metadata API pattern: getCached for synchronous cache access.
1199
+ */
1200
+ getCached(key) {
1201
+ return this.metadataCache.getCachedSync(key);
1202
+ }
728
1203
  /**
729
1204
  * Get cache statistics for monitoring performance.
730
1205
  */
@@ -745,6 +1220,98 @@ var ObjectStackAdapter = class {
745
1220
  clearCache() {
746
1221
  this.metadataCache.clear();
747
1222
  }
1223
+ /**
1224
+ * Upload a single file to a resource.
1225
+ * Posts the file as multipart/form-data to the ObjectStack server.
1226
+ *
1227
+ * @param resource - The resource/object name to attach the file to
1228
+ * @param file - File object or Blob to upload
1229
+ * @param options - Additional upload options (recordId, fieldName, metadata)
1230
+ * @returns Promise resolving to the upload result (file URL, metadata)
1231
+ */
1232
+ async uploadFile(resource, file, options) {
1233
+ await this.connect();
1234
+ const formData = new FormData();
1235
+ formData.append("file", file);
1236
+ if (options?.recordId) {
1237
+ formData.append("recordId", options.recordId);
1238
+ }
1239
+ if (options?.fieldName) {
1240
+ formData.append("fieldName", options.fieldName);
1241
+ }
1242
+ if (options?.metadata) {
1243
+ formData.append("metadata", JSON.stringify(options.metadata));
1244
+ }
1245
+ const url = `${this.baseUrl}/api/data/${encodeURIComponent(resource)}/upload`;
1246
+ const response = await fetch(url, {
1247
+ method: "POST",
1248
+ body: formData,
1249
+ headers: {
1250
+ ...this.getAuthHeaders()
1251
+ }
1252
+ });
1253
+ if (!response.ok) {
1254
+ const error = await response.json().catch(() => ({ message: response.statusText }));
1255
+ throw new ObjectStackError(
1256
+ error.message || `Upload failed with status ${response.status}`,
1257
+ "UPLOAD_ERROR",
1258
+ response.status
1259
+ );
1260
+ }
1261
+ return response.json();
1262
+ }
1263
+ /**
1264
+ * Upload multiple files to a resource.
1265
+ * Posts all files as a single multipart/form-data request.
1266
+ *
1267
+ * @param resource - The resource/object name to attach the files to
1268
+ * @param files - Array of File objects or Blobs to upload
1269
+ * @param options - Additional upload options
1270
+ * @returns Promise resolving to array of upload results
1271
+ */
1272
+ async uploadFiles(resource, files, options) {
1273
+ await this.connect();
1274
+ const formData = new FormData();
1275
+ files.forEach((file, idx) => {
1276
+ formData.append(`files`, file, file.name || `file-${idx}`);
1277
+ });
1278
+ if (options?.recordId) {
1279
+ formData.append("recordId", options.recordId);
1280
+ }
1281
+ if (options?.fieldName) {
1282
+ formData.append("fieldName", options.fieldName);
1283
+ }
1284
+ if (options?.metadata) {
1285
+ formData.append("metadata", JSON.stringify(options.metadata));
1286
+ }
1287
+ const url = `${this.baseUrl}/api/data/${encodeURIComponent(resource)}/upload`;
1288
+ const response = await fetch(url, {
1289
+ method: "POST",
1290
+ body: formData,
1291
+ headers: {
1292
+ ...this.getAuthHeaders()
1293
+ }
1294
+ });
1295
+ if (!response.ok) {
1296
+ const error = await response.json().catch(() => ({ message: response.statusText }));
1297
+ throw new ObjectStackError(
1298
+ error.message || `Upload failed with status ${response.status}`,
1299
+ "UPLOAD_ERROR",
1300
+ response.status
1301
+ );
1302
+ }
1303
+ return response.json();
1304
+ }
1305
+ /**
1306
+ * Get authorization headers from the adapter config.
1307
+ */
1308
+ getAuthHeaders() {
1309
+ const headers = {};
1310
+ if (this.token) {
1311
+ headers["Authorization"] = `Bearer ${this.token}`;
1312
+ }
1313
+ return headers;
1314
+ }
748
1315
  };
749
1316
  function createObjectStackAdapter(config) {
750
1317
  return new ObjectStackAdapter(config);
@@ -752,13 +1319,21 @@ function createObjectStackAdapter(config) {
752
1319
  export {
753
1320
  AuthenticationError,
754
1321
  BulkOperationError,
1322
+ CloudOperations,
755
1323
  ConnectionError,
1324
+ IntegrationManager,
756
1325
  MetadataNotFoundError,
757
1326
  ObjectStackAdapter,
758
1327
  ObjectStackError,
1328
+ SecurityManager,
759
1329
  ValidationError,
1330
+ calculateAutoLayout,
1331
+ createDefaultCanvasConfig,
760
1332
  createErrorFromResponse,
761
1333
  createObjectStackAdapter,
1334
+ generateContractManifest,
762
1335
  isErrorType,
763
- isObjectStackError
1336
+ isObjectStackError,
1337
+ snapToGrid,
1338
+ validatePluginContract
764
1339
  };