Digested 2024-09-24

Authors:: Unknown License:: Unspecified Doc Seal:: 203ae7061338

MarkdownTile

<%* // DS Tagger Chain for Obsidian Templater // Version 0.7.9 /* Fixes for 0.8.0 (changed versioning) - new versioning format - preserve Specs in comments - append DigestTag to tile file frontmatter[“aliases”] - bug: frontmatter for current file gets deleted - bug: add ###### #EOT\n--- to end of Title Structure */

/* * Preserve Specs for Tile and Blockchain formats * * Tile Spec:

  • Structure ###### #Digested #ds/{deformat-digest}/{YYYY-MM-DD}\nAuthors:: {authors}\nLicense:: {license}\nDoc Seal:: {doc-seal-hash}\n###### #MarkdownTile\n{tile}\n###### #DeformattedTile\n{deformatted-tile}\n###### #EOT\n---\n
  • Location: Notes/ds/tile/{deformat-digest}.md
  • Upsert Logic: Skip (with Notice message) if #ds/{deformat-digest}/{YYYY-MM-DD} exists, append otherwise Blockchain Spec:
  • Structure: ### [[${fileName}]] 🔒 #ds/seal/${docSealHash} chained ${YYYY-MM-DDTHH:mm} to #ds/block/${previousBlockchainEntryDigest}\n${digestTags.join(' ')}\n#ds/block/{currentBlockchainEntryDigest}
  • Location: Notes/ds/blockchains/bc-{YY-MM}.md
  • Blockchain Logic: {previousBlockchainEntryDigest} is a hash of {YY-MM} for initial block or the BlockchainEntryDigest of the previous entry Script Spec:
  • use Obsidian Templater scripting language for the script
  • use app.fileManager.processFrontMatter to update frontmatter
  • helper functions: generateHash(content, length = 12); deformatTile(tile); createDigestTag(hash, date); createTileID(digestTag, date); splitIntoTiles(content); createDocSealHash(digestTags); ensureDirectoryExists(path); upsertToFile(filePath, header, content); updateFrontMatter(file, updates); */

const crypto = require(‘crypto’);

// Helper functions

function generateHash(content, length = 12) { return crypto.createHash(‘sha256’).update(content).digest(‘hex’).substring(0, length); }

function deformatTile(tile) { if (typeof tile !== ‘string’) { throw new Error(‘Deformat received non-string content’); }

const originalLength = tile.length;

// Remove Digest Tags and Tile IDs
tile = tile.replace(/\s*#ds\/[a-f0-9]+\/\d{4}-\d{2}-\d{2}/g, '');
tile = tile.replace(/\s*\^ds-[a-f0-9]+/g, '');

// Remove headers, emphasis, bold, strikethrough, blockquotes, horizontal rules
tile = tile.replace(/^#{1,6}\s+|[*_~>]|\-{3,}|\*{3,}|_{3,}/gm, '');

// Remove list markers and task list markers
tile = tile.replace(/^[\s]*[-*+]\s|^\s*\d+\.\s|^- \[[ x]\]\s/gm, '');

// Remove highlight markers
tile = tile.replace(/==/g, '');

// Collapse multiple newlines and trim
tile = tile.replace(/\n+/g, '\n').trim();

const charactersRemoved = originalLength - tile.length;

return { deformattedContent: tile, charactersRemoved };

}

function createDigestTag(hash, date) { return #ds/${hash}/${date}; }

function createTileID(digestTag, date) { const tagHash = generateHash(digestTag + date); return ^ds-${tagHash}; }

function splitIntoTiles(content) { // Remove frontmatter content = content.replace(/^---\n[\s\S]*?\n---\n/, ”);

const headerRegex = /^#{1,3}\s+.*$/gm;
const tiles = [];
let lastIndex = 0;
let match;

// Check if there's content before the first header
const firstHeaderMatch = content.match(headerRegex);
if (firstHeaderMatch && firstHeaderMatch.index > 0) {
    tiles.push(content.slice(0, firstHeaderMatch.index).trim());
    lastIndex = firstHeaderMatch.index;
}

while ((match = headerRegex.exec(content)) !== null) {
    if (lastIndex !== match.index) {
        tiles.push(content.slice(lastIndex, match.index).trim());
    }
    lastIndex = match.index;
}

if (lastIndex < content.length) {
    tiles.push(content.slice(lastIndex).trim());
}

return tiles;

}

function createDocSealHash(digestTags) { return generateHash(digestTags.join(’ ’), 12); }

async function ensureDirectoryExists(path) { const dirs = path.split(’/‘).slice(0, -1).join(’/’); if (!(await app.vault.adapter.exists(dirs))) { await app.vault.createFolder(dirs); } }

async function upsertToFile(filePath, header, content) { try { await ensureDirectoryExists(filePath);

    let file = app.vault.getAbstractFileByPath(filePath);
    let fileContent = '';

    if (file) {
        fileContent = await app.vault.read(file);
    }

    const escapedHeader = header.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
    const regex = new RegExp(`${escapedHeader}[\\s\\S]*?(?=######|$)`, 'g');

    if (regex.test(fileContent)) {
        if (fileContent.includes(content.trim())) {
            console.log(`File content already up to date: ${filePath}`);
            return { status: 'unchanged', file: file };
        }
        // For blockchain entries, check if the entry already exists
        if (header.startsWith('### [[') && header.includes('🔒 #ds/seal/')) {
            if (fileContent.includes(header)) {
                console.log(`Blockchain entry already exists: ${filePath}`);
                new Notice(`Blockchain entry already exists: ${filePath}`, 5000);
                return { status: 'unchanged', file: file };
            }
        }
        fileContent += `\n\n${content}\n`;
    } else {
        fileContent += `${content}\n\n`;
    }

    if (file) {
        await app.vault.modify(file, fileContent);
    } else {
        file = await app.vault.create(filePath, fileContent);
    }

    console.log(`File upserted successfully: ${filePath}`);
    return { status: 'updated', file: file };
} catch (error) {
    console.error(`Error upserting file ${filePath}:`, error);
    throw error;
}

}

async function updateFrontMatter(file, updates) { try { await app.fileManager.processFrontMatter(file, (frontmatter) { Object.entries(updates).forEach(([key, value]) { if (Array.isArray(value)) { if (!frontmatter[key]) { frontmatter[key] = []; } value.forEach(item { if (!frontmatter[key].includes(item)) { frontmatter[key].push(item); } }); } else { frontmatter[key] = value; } }); }); } catch (error) { console.error(Error updating frontmatter for ${file.path}:, error); throw error; } }

// Main function async function dsTaggerChain() { const version = “0.7.93”; const startTime = Date.now(); const currentDate = new Date().toISOString().split(‘T’)[0]; const currentTimestamp = new Date().toISOString(); console.log([${currentDate}] DS Tagger Chain v${version} started);

try {
    let content = tp.file.content;
    const fileName = tp.file.title;
    const currentFile = tp.file.find_tfile(fileName);

    if (typeof content !== 'string') {
        throw new Error('Received non-string content');
    }

    const authors = tp.frontmatter.authors || 'Unknown';
    const license = tp.frontmatter.license || 'Unspecified';
    console.log(`[${currentDate}] Authors extracted: ${authors}`);
    console.log(`[${currentDate}] License extracted: ${license}`);

    console.log(`[${currentDate}] File content length: ${content.length} characters`);

    let tiles = splitIntoTiles(content);
    console.log(`[${currentDate}] Number of tiles: ${tiles.length}`);

    let processedTiles = [];
    let digestedTiles = 0;
    let nonDigestedTiles = 0;
    let updatedTiles = 0;
    let digestTags = [];
    let tileFilesUpdated = {};

    for (let i = 0; i < tiles.length; i++) {
        let tile = tiles[i];
        const tileHash = generateHash(tile);
        console.log(`\n[${currentDate}] Processing tile ${i + 1} (${tileHash})`);

        let existingDigestMatch = tile.match(/\s*(#ds\/[a-f0-9]+\/\d{4}-\d{2}-\d{2})/);
        let existingTileIDMatch = tile.match(/\s*(\^ds-[a-f0-9]+)/);
        let existingDigest = existingDigestMatch ? existingDigestMatch[1] : null;
        let existingTileID = existingTileIDMatch ? existingTileIDMatch[1] : null;

        console.log(`Existing Digest Tag: ${existingDigest || 'None'}`);
        console.log(`Existing Tile ID: ${existingTileID || 'None'}`);

        let cleanTile = tile.replace(/\s*#ds\/[a-f0-9]+\/\d{4}-\d{2}-\d{2}/, '')
                            .replace(/\s*\^ds-[a-f0-9]+/, '')
                            .trim();

        let { deformattedContent, charactersRemoved } = deformatTile(cleanTile);
        let digest = generateHash(deformattedContent);
        let digestTag = createDigestTag(digest, currentDate);
        let tileID = createTileID(digestTag, currentDate);

        console.log(`New Digest Tag: ${digestTag}`);
        console.log(`New Tile ID: ${tileID}`);

        if (existingDigest && existingDigest.includes(digest) && existingDigest.includes(currentDate)) {
            console.log(`Content unchanged for tile ${tileHash}. Keeping existing Digest Tag and Tile ID.`);
            digestTag = existingDigest;
            tileID = existingTileID || createTileID(digestTag, currentDate);
            nonDigestedTiles++;
        } else {
            console.log(`Content changed or new for tile ${tileHash}. Updating Digest Tag and Tile ID.`);
            if (existingDigest) {
                updatedTiles++;
            } else {
                digestedTiles++;
            }
        }

        let processedTile = `${cleanTile} ${digestTag} ${tileID}`;
        processedTiles.push(processedTile);
        digestTags.push(digestTag);

        console.log(`Tile ${tileHash} stats: Characters removed: ${charactersRemoved}`);

        const tileFilePath = `Notes/ds/tile/${digest}.md`;
        const docSealHash = createDocSealHash(digestTags);
        const tileFileContent = `###### #Digested ${digestTag}

Authors:: {license} Doc Seal:: ${docSealHash}

MarkdownTile

${cleanTile}

DeformattedTile

{deformattedContent}`; const { status: tileUpdateStatus, file: tileFile } = await upsertToFile(tileFilePath, `###### #Digested {digestTag}`, tileFileContent); tileFilesUpdated[tileFilePath] = tileUpdateStatus;

        // Update tile file frontmatter
        if (tileFile) {
            await updateFrontMatter(tileFile, {
                aliases: [digestTag],
                authors: authors,
                license: license,
                'doc-seal': docSealHash
            });
        }
    }

    let result = processedTiles.join('\n\n');

    let docSealHash = createDocSealHash(digestTags);
    console.log(`\n[${currentDate}] Doc seal hash created: ${docSealHash}`);

    // Update current file frontmatter
    await updateFrontMatter(currentFile, {
        'doc-seal-hash': docSealHash,
        'doc-seal-timestamp': currentTimestamp,
        'digest-tags': digestTags
    });

    // Update blockchain file
    const blockchainFilePath = `Notes/ds/blockchains/bc-${currentDate.slice(2, 7)}.md`;
    let previousBlockchainEntryDigest = '';
    const blockchainFile = app.vault.getAbstractFileByPath(blockchainFilePath);
    
    if (blockchainFile) {
        const blockchainFileContent = await app.vault.read(blockchainFile);
        const lastEntryMatch = blockchainFileContent.match(/#ds\/block\/([a-f0-9]+)$/m);
        previousBlockchainEntryDigest = lastEntryMatch ? lastEntryMatch[1] : '';
    } else {
        previousBlockchainEntryDigest = generateHash(currentDate.substring(0, 7));
    }

    // Create the blockchain content without the final hash
    const blockchainContentWithoutHash = `### [[${fileName}]] 🔒 #ds/seal/${docSealHash} chained ${currentTimestamp} to #ds/block/${previousBlockchainEntryDigest}

${digestTags.join(’ ’)}`.trim();

    // Generate the hash for the blockchain entry
    const blockchainEntryHash = generateHash(blockchainContentWithoutHash);

    // Create the final blockchain content
    const blockchainFileContent = `${blockchainContentWithoutHash}

block/${blockchainEntryHash}`;

    const { status: blockchainUpdateStatus } = await upsertToFile(blockchainFilePath, `### [[${fileName}]] 🔒 #ds/seal/${docSealHash} chained ${currentTimestamp}`, blockchainFileContent);
    if (blockchainUpdateStatus === 'updated') {
        console.log(`Updated blockchain file: ${blockchainFilePath}`);
    } else {
        console.log(`Blockchain file already up to date: ${blockchainFilePath}`);
    }

    await app.vault.modify(currentFile, result);

    const endTime = Date.now();
    const runtime = ((endTime - startTime) / 1000).toFixed(2);
    console.log(`\n[${currentDate}] DS Tagger Chain process completed successfully`);
    
    const statusMessage = `DS Tagger Chain v${version} completed:

Runtime: {docSealHash} Tiles processed: {digestedTiles} Tiles not digested: {updatedTiles} Date: ${currentDate}`;

    const tileFilesMessage = `Tile files updated:

{Object.entries(tileFilesUpdated).map(([file, status]) => `{file}: ${status}).join('\n')};

    const blockchainEntryMessage = `Blockchain entry (${blockchainUpdateStatus}):

${blockchainFileContent}`;

    console.log(statusMessage);
    console.log(tileFilesMessage);
    console.log(blockchainEntryMessage);

    new Notice(statusMessage, 10000);
    new Notice(tileFilesMessage, 10000);
    new Notice(blockchainEntryMessage, 10000);

} catch (error) {
    console.error(`[${currentDate}] An error occurred:`, error);
    new Notice(`Error in DS Tagger Chain v${version}: ${error.message}. Check console for details.`, 10000);
}

}

// Run the script and output the result await dsTaggerChain(); %>

DeformattedTile

<% // DS Tagger Chain for Obsidian Templater // Version 0.7.9 / Fixes for 0.8.0 (changed versioning) new versioning format preserve Specs in comments append DigestTag to tile file frontmatter[“aliases”] bug: frontmatter for current file gets deleted bug: add ###### #EOT\n to end of Title Structure / / Preserve Specs for Tile and Blockchain formats
Tile Spec: Structure ###### #Digested #ds/{deformat-digest}/{YYYY-MM-DD}\nAuthors:: {authors}\nLicense:: {license}\nDoc Seal:: {doc-seal-hash}\n###### #MarkdownTile\n{tile}\n###### #DeformattedTile\n{deformatted-tile}\n###### #EOT\n\n Location: Notes/ds/tile/{deformat-digest}.md Upsert Logic: Skip (with Notice message) if #ds/{deformat-digest}/{YYYY-MM-DD} exists, append otherwise Blockchain Spec: Structure: ### [[${fileName}]] 🔒 #ds/seal/${docSealHash} chained ${YYYY-MM-DDTHH:mm} to #ds/block/${previousBlockchainEntryDigest}\n${digestTags.join(' ')}\n#ds/block/{currentBlockchainEntryDigest} Location: Notes/ds/blockchains/bc-{YY-MM}.md Blockchain Logic: {previousBlockchainEntryDigest} is a hash of {YY-MM} for initial block or the BlockchainEntryDigest of the previous entry Script Spec: use Obsidian Templater scripting language for the script use app.fileManager.processFrontMatter to update frontmatter helper functions: generateHash(content, length = 12); deformatTile(tile); createDigestTag(hash, date); createTileID(digestTag, date); splitIntoTiles(content); createDocSealHash(digestTags); ensureDirectoryExists(path); upsertToFile(filePath, header, content); updateFrontMatter(file, updates); / const crypto = require(‘crypto’); // Helper functions function generateHash(content, length = 12) { return crypto.createHash(‘sha256’).update(content).digest(‘hex’).substring(0, length); } function deformatTile(tile) { if (typeof tile ! ‘string’) { throw new Error(‘Deformat received non-string content’); }

const originalLength = tile.length;

// Remove Digest Tags and Tile IDs
tile = tile.replace(/\s#ds\/[a-f0-9]+\/\d{4}-\d{2}-\d{2}/g, '');
tile = tile.replace(/\s\^ds-[a-f0-9]+/g, '');

// Remove headers, emphasis, bold, strikethrough, blockquotes, horizontal rules
tile = tile.replace(/^#{1,6}\s+|[]|\-{3,}|\{3,}|{3,}/gm, '');

// Remove list markers and task list markers
tile = tile.replace(/^[\s][-+]\s|^\s\d+\.\s|^- \[[ x]\]\s/gm, '');

// Remove highlight markers
tile = tile.replace(//g, '');

// Collapse multiple newlines and trim
tile = tile.replace(/\n+/g, '\n').trim();

const charactersRemoved = originalLength - tile.length;

return { deformattedContent: tile, charactersRemoved };

} function createDigestTag(hash, date) { return #ds/${hash}/${date}; } function createTileID(digestTag, date) { const tagHash = generateHash(digestTag + date); return ^ds-${tagHash}; } function splitIntoTiles(content) { // Remove frontmatter content = content.replace(/^\n[\s\S]?\n\n/, ”); const headerRegex = /^#{1,3}\s+./gm; const tiles = []; let lastIndex = 0; let match; // Check if there's content before the first header const firstHeaderMatch = content.match(headerRegex); if (firstHeaderMatch && firstHeaderMatch.index 0) { tiles.push(content.slice(0, firstHeaderMatch.index).trim()); lastIndex = firstHeaderMatch.index; } while ((match = headerRegex.exec(content)) ! null) { if (lastIndex ! match.index) { tiles.push(content.slice(lastIndex, match.index).trim()); } lastIndex = match.index; } if (lastIndex < content.length) { tiles.push(content.slice(lastIndex).trim()); } return tiles; } function createDocSealHash(digestTags) { return generateHash(digestTags.join(' '), 12); } async function ensureDirectoryExists(path) { const dirs = path.split('/').slice(0, -1).join('/'); if (!(await app.vault.adapter.exists(dirs))) { await app.vault.createFolder(dirs); } } async function upsertToFile(filePath, header, content) { try { await ensureDirectoryExists(filePath); let file = app.vault.getAbstractFileByPath(filePath); let fileContent = ''; if (file) { fileContent = await app.vault.read(file); } const escapedHeader = header.replace(/[.+?^{}()|[]\]/g, ’\&'); const regex = new RegExp(`{escapedHeader}[\s\S]?(?=######|)`, 'g'); if (regex.test(fileContent)) { if (fileContent.includes(content.trim())) { console.log(`File content already up to date: {filePath}); return { status: 'unchanged', file: file }; } // For blockchain entries, check if the entry already exists if (header.startsWith('### [[') && header.includes('🔒 #ds/seal/')) { if (fileContent.includes(header)) { console.log(Blockchain entry already exists: {filePath}, 5000); return { status: 'unchanged', file: file }; } } fileContent += \n\n{content}\n`; } else { fileContent += `{content}\n\n; } if (file) { await app.vault.modify(file, fileContent); } else { file = await app.vault.create(filePath, fileContent); } console.log(File upserted successfully: {filePath}`); return { status: 'updated', file: file }; } catch (error) { console.error(`Error upserting file {filePath}:, error); throw error; } } async function updateFrontMatter(file, updates) { try { await app.fileManager.processFrontMatter(file, (frontmatter) = { Object.entries(updates).forEach(([key, value]) = { if (Array.isArray(value)) { if (!frontmatter[key]) { frontmatter[key] = []; } value.forEach(item = { if (!frontmatter[key].includes(item)) { frontmatter[key].push(item); } }); } else { frontmatter[key] = value; } }); }); } catch (error) { console.error(Error updating frontmatter for {file.path}:`, error); throw error; } } // Main function async function dsTaggerChain() { const version = "0.7.93"; const startTime = Date.now(); const currentDate = new Date().toISOString().split('T')[0]; const currentTimestamp = new Date().toISOString(); console.log(`[{currentDate}] DS Tagger Chain v{version} started`); try { let content = tp.file.content; const fileName = tp.file.title; const currentFile = tp.file.findtfile(fileName); if (typeof content ! 'string') { throw new Error('Received non-string content'); } const authors = tp.frontmatter.authors || 'Unknown'; const license = tp.frontmatter.license || 'Unspecified'; console.log(`[{currentDate}] Authors extracted: {currentDate}] License extracted: {currentDate}] File content length: {currentDate}] Number of tiles: {tiles.length}`); let processedTiles = []; let digestedTiles = 0; let nonDigestedTiles = 0; let updatedTiles = 0; let digestTags = []; let tileFilesUpdated = {}; for (let i = 0; i < tiles.length; i++) { let tile = tiles[i]; const tileHash = generateHash(tile); console.log(`\n[{currentDate}] Processing tile {tileHash})); let existingDigestMatch = tile.match(/\s(#ds\/[a-f0-9]+\/\d{4}-\d{2}-\d{2})/); let existingTileIDMatch = tile.match(/\s(\^ds-[a-f0-9]+)/); let existingDigest = existingDigestMatch ? existingDigestMatch[1] : null; let existingTileID = existingTileIDMatch ? existingTileIDMatch[1] : null; console.log(Existing Digest Tag: {existingTileID || ‘None’}); let cleanTile = tile.replace(/\s#ds\/[a-f0-9]+\/\d{4}-\d{2}-\d{2}/, '') .replace(/\s\^ds-[a-f0-9]+/, '') .trim(); let { deformattedContent, charactersRemoved } = deformatTile(cleanTile); let digest = generateHash(deformattedContent); let digestTag = createDigestTag(digest, currentDate); let tileID = createTileID(digestTag, currentDate); console.log(New Digest Tag: {tileID}); if (existingDigest && existingDigest.includes(digest) && existingDigest.includes(currentDate)) { console.log(Content unchanged for tile {tileHash}. Keeping existing Digest Tag and Tile ID.`); digestTag = existingDigest; tileID = existingTileID || createTileID(digestTag, currentDate); nonDigestedTiles++; } else { console.log(`Content changed or new for tile {tileHash}. Updating Digest Tag and Tile ID.); if (existingDigest) { updatedTiles++; } else { digestedTiles++; } } let processedTile = {digestTag} {tileHash} stats: Characters removed: {digest}.md; const docSealHash = createDocSealHash(digestTags); const tileFileContent = Digested {authors} License:: {docSealHash} #MarkdownTile {cleanTile} #DeformattedTile {deformattedContent}; const { status: tileUpdateStatus, file: tileFile } = await upsertToFile(tileFilePath, Digested {digestTag}`, tileFileContent); tileFilesUpdated[tileFilePath] = tileUpdateStatus; // Update tile file frontmatter if (tileFile) { await updateFrontMatter(tileFile, { aliases: [digestTag], authors: authors, license: license, 'doc-seal': docSealHash }); } } let result = processedTiles.join('\n\n'); let docSealHash = createDocSealHash(digestTags); console.log(`\n[{currentDate}] Doc seal hash created: {currentDate.slice(2, 7)}.md`; let previousBlockchainEntryDigest = ”; const blockchainFile = app.vault.getAbstractFileByPath(blockchainFilePath);

    if (blockchainFile) {
        const blockchainFileContent = await app.vault.read(blockchainFile);
        const lastEntryMatch = blockchainFileContent.match(/#ds\/block\/([a-f0-9]+)$/m);
        previousBlockchainEntryDigest = lastEntryMatch ? lastEntryMatch[1] : '';
    } else {
        previousBlockchainEntryDigest = generateHash(currentDate.substring(0, 7));
    }
    // Create the blockchain content without the final hash
    const blockchainContentWithoutHash = `### [[${fileName}]] 🔒 #ds/seal/${docSealHash} chained ${currentTimestamp} to #ds/block/${previousBlockchainEntryDigest}

{digestTags.join(' ')}`.trim(); // Generate the hash for the blockchain entry const blockchainEntryHash = generateHash(blockchainContentWithoutHash); // Create the final blockchain content const blockchainFileContent = `{blockchainContentWithoutHash} #ds/block/{blockchainEntryHash}`; const { status: blockchainUpdateStatus } = await upsertToFile(blockchainFilePath, `### [[{fileName}]] 🔒 seal/{currentTimestamp}, blockchainFileContent); if (blockchainUpdateStatus = 'updated') { console.log(Updated blockchain file: {blockchainFilePath}`); } else { console.log(`Blockchain file already up to date: {blockchainFilePath}); } await app.vault.modify(currentFile, result); const endTime = Date.now(); const runtime = ((endTime - startTime) / 1000).toFixed(2); console.log(\n[${currentDate}] DS Tagger Chain process completed successfully`);

    const statusMessage = `DS Tagger Chain v${version} completed:

Runtime: {docSealHash} Tiles processed: {digestedTiles} Tiles not digested: {updatedTiles} Date: {Object.entries(tileFilesUpdated).map(([file, status]) = ${file}: ${status}).join(‘\n’)}; const blockchainEntryMessage = Blockchain entry ({blockchainFileContent}; console.log(statusMessage); console.log(tileFilesMessage); console.log(blockchainEntryMessage); new Notice(statusMessage, 10000); new Notice(tileFilesMessage, 10000); new Notice(blockchainEntryMessage, 10000); } catch (error) { console.error([{version}: ${error.message}. Check console for details.`, 10000); } } // Run the script and output the result await dsTaggerChain(); %