UNPKG

@datalayer/core

Version:

[![Datalayer](https://assets.datalayer.tech/datalayer-25.svg)](https://datalayer.io)

187 lines (186 loc) 6.72 kB
/* * Copyright (c) 2023-2025 Datalayer, Inc. * Distributed under the terms of the Modified BSD License. */ import * as items from '../api/spacer/items'; /** * Abstract base class for all Datalayer content items. * Provides common functionality for content management including lifecycle tracking. * * @template TData - Raw data type from API * @template TUpdateRequest - Update request type for API */ export class ItemDTO { _data; _sdk; _deleted = false; /** * Create an Item instance. * @param data - Item data from API * @param sdk - SDK instance */ constructor(data, sdk) { this._data = data; this._sdk = sdk; } // ======================================================================== // Deletion State Management // ======================================================================== /** Check if this item has been deleted. */ get isDeleted() { return this._deleted; } /** * Check if this item has been deleted and throw error if so. * @throws Error if deleted */ _checkDeleted() { if (this._deleted) { throw new Error(`${this.constructor.name} ${this._data.id} has been deleted and no longer exists`); } } // ======================================================================== // Static Properties (set at creation, never change) // ======================================================================== /** Item ID. */ get id() { this._checkDeleted(); return this._data.id; } /** Unique identifier for the item. */ get uid() { this._checkDeleted(); return this._data.uid; } /** Parent space ID. */ get spaceId() { this._checkDeleted(); // Try the direct field first (if API provides it) if (this._data.space_id) { return this._data.space_id; } // Extract from s3_path_s if available: "datalayer.app/SPACE_ID/..." const s3Path = this._data.s3_path_s; if (s3Path && typeof s3Path === 'string') { const match = s3Path.match(/^datalayer\.app\/([^/]+)\//); if (match) { return match[1]; } } // Fallback to empty string if no space ID can be determined return ''; } /** Owner user ID. */ get ownerId() { this._checkDeleted(); return (this._data.owner_id || this._data.creator_uid || ''); } /** When the item was created. */ get createdAt() { this._checkDeleted(); const dateStr = this._data.creation_ts_dt || this._data.created_at; if (!dateStr) { throw new Error(`No creation timestamp available for ${this.constructor.name.toLowerCase()}`); } return new Date(dateStr); } /** The cached update time. */ get updatedAt() { this._checkDeleted(); const dateStr = this._data.last_update_ts_dt || this._data.updated_at || this._data.creation_ts_dt || this._data.created_at; if (!dateStr) { throw new Error(`No update timestamp available for ${this.constructor.name.toLowerCase()}`); } return new Date(dateStr); } // ======================================================================== // Action Methods // ======================================================================== /** * Delete this item permanently. * After deletion, all subsequent method calls will throw errors. */ async delete() { this._checkDeleted(); const token = this._sdk.getToken(); const spacerRunUrl = this._sdk.getSpacerRunUrl(); await items.deleteItem(token, this.uid, spacerRunUrl); this._deleted = true; } /** Get the document content from API. */ async getContent() { this._checkDeleted(); // First try: CDN URL (fastest) const cdnUrl = this._data.cdn_url_s; if (cdnUrl) { try { const response = await fetch(cdnUrl); if (!response.ok) { throw new Error('Failed to fetch content from CDN'); } const data = await response.json(); if (data.content !== undefined && data.content !== null) { return data.content; } } catch (error) { console.error('Error fetching content from CDN:', error); } } // Second try: Check if content is already loaded const cachedContent = this.content; if (cachedContent !== undefined && cachedContent !== null) { return cachedContent; } // Third try: Fetch full item details from API try { const token = this._sdk.getToken(); const spacerRunUrl = this._sdk.getSpacerRunUrl(); const response = await items.getItem(token, this.uid, spacerRunUrl); // Update internal data with full item details if (response.success && response.item) { this._data = response.item; const freshContent = this.content; if (freshContent !== undefined && freshContent !== null) { return freshContent; } } } catch (error) { console.error('Error fetching full item details:', error); } throw new Error(`Content is not available for item ${this.uid}. The item may not have been fully loaded or the content is not yet available.`); } // ======================================================================== // Utility Methods // ======================================================================== /** Get raw item data object. */ rawData() { this._checkDeleted(); return this._data; } /** String representation of the item. */ toString() { this._checkDeleted(); const name = this.name || 'Unnamed'; return `${this.constructor.name}(${this.id}, ${name})`; } // ======================================================================== // Protected Helper Methods // ======================================================================== /** Get SDK token for API calls. */ _getToken() { return this._sdk.getToken(); } /** Get spacer API URL for API calls. */ _getSpacerRunUrl() { return this._sdk.getSpacerRunUrl(); } /** Update internal data after API call. */ _updateData(newData) { this._data = newData; } }