From f90b566d6df3a98a6f22bbb2803e2f7e32dc49ed Mon Sep 17 00:00:00 2001 From: waynemwashuma <94756970+waynemwashuma@users.noreply.github.com> Date: Sun, 31 May 2026 21:11:50 +0300 Subject: [PATCH 01/10] Add `Exporter` interface --- src/asset/core/exporter.js | 41 ++++++++++++++++++++++++++++++++++++++ src/asset/core/index.js | 1 + 2 files changed, 42 insertions(+) create mode 100644 src/asset/core/exporter.js diff --git a/src/asset/core/exporter.js b/src/asset/core/exporter.js new file mode 100644 index 00000000..5c275030 --- /dev/null +++ b/src/asset/core/exporter.js @@ -0,0 +1,41 @@ +/** @import { Constructor } from '../../type/index.js' */ +import { throws } from '../../logger/index.js' + +/** + * @abstract + * @template T + */ +export class Exporter { + + /** + * @readonly + * @type {Constructor} + */ + asset + + /** + * @param {Constructor} asset + */ + constructor(asset) { + this.asset = asset + } + + /** + * @param {T} _asset + * @returns {Promise} + */ + async serialize(_asset) { + throws(`Implement the method \`serialize\` on \`${this.constructor.name}\``) + + return undefined + } + + /** + * @returns {string[]} + */ + getExtensions() { + throws(`Implement the method \`getExtensions\` on \`${this.constructor.name}\``) + + return [] + } +} diff --git a/src/asset/core/index.js b/src/asset/core/index.js index 89c11167..6d2ebfac 100644 --- a/src/asset/core/index.js +++ b/src/asset/core/index.js @@ -1,2 +1,3 @@ export * from './asset.js' +export * from './exporter.js' export * from './parser.js' From 339cbe61f480a770dc881eab7d66c04a5c914f98 Mon Sep 17 00:00:00 2001 From: waynemwashuma <94756970+waynemwashuma@users.noreply.github.com> Date: Sun, 31 May 2026 21:13:26 +0300 Subject: [PATCH 02/10] Add `AssetLoadOperation` --- src/asset/events/fail.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/asset/events/fail.js b/src/asset/events/fail.js index f997ed7b..4364cb57 100644 --- a/src/asset/events/fail.js +++ b/src/asset/events/fail.js @@ -1,5 +1,15 @@ /** @import {TypeId} from '../../type/index.js' */ /** @import {AssetId} from '../types/index.js' */ + +/** + * @readonly + * @enum {number} + */ +export const AssetLoadOperation = { + Loading: 1, + Saving: 2 +} + export class AssetLoadFail { /** From 32070e4893578e990e6518e76e43e87de646b99b Mon Sep 17 00:00:00 2001 From: waynemwashuma <94756970+waynemwashuma@users.noreply.github.com> Date: Sun, 31 May 2026 21:17:28 +0300 Subject: [PATCH 03/10] Update systems to support asset exports --- src/asset/events/fail.js | 9 ++++++++- src/asset/systems/server.js | 21 ++++++++++++++++++--- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/asset/events/fail.js b/src/asset/events/fail.js index 4364cb57..8111bd22 100644 --- a/src/asset/events/fail.js +++ b/src/asset/events/fail.js @@ -32,16 +32,23 @@ export class AssetLoadFail { */ reason + /** + * @type {number} + */ + operation + /** * @param {TypeId} typeId * @param {AssetId} assetId * @param {string} path * @param {string} reason + * @param {number} [operation=AssetLoadOperation.Loading] */ - constructor(typeId, assetId, path, reason) { + constructor(typeId, assetId, path, reason, operation = AssetLoadOperation.Loading) { this.typeId = typeId this.assetId = assetId this.path = path this.reason = reason + this.operation = operation } } diff --git a/src/asset/systems/server.js b/src/asset/systems/server.js index 574c7c5d..65eb4484 100644 --- a/src/asset/systems/server.js +++ b/src/asset/systems/server.js @@ -1,11 +1,11 @@ /** @import { SystemFunc, World } from '../../ecs/index.js' */ /** @import { Constructor } from '../../type/index.js' */ -/** @import { AssetDropped, AssetEvent, Parser } from '../index.js' */ +/** @import { AssetDropped, AssetEvent, Parser, Exporter } from '../index.js' */ import { Events } from '../../event/index.js' import { typeidGeneric } from '../../type/index.js' import { Assets } from '../core/index.js' import { AssetServer } from '../resources/index.js' -import { AssetLoadFail } from '../events/index.js' +import { AssetLoadFail, AssetLoadOperation } from '../events/index.js' import { error } from '../../logger/index.js' /** @@ -36,6 +36,20 @@ export function registerAssetParserOnAssetServer(type, parser) { } } +/** + * @template T + * @param {Constructor} type + * @param {Exporter} exporter + * @returns {SystemFunc} + */ +export function registerAssetExporterOnAssetServer(type, exporter) { + return function registerAssetExportedOnAssetServer(world) { + const server = world.getResource(AssetServer) + + server.registerExporter(type, exporter) + } +} + /** * @param {World} world */ @@ -63,8 +77,9 @@ export function logFailedLoads(world) { events.each((event) => { const { data } = event + const operation = data.operation === AssetLoadOperation.Saving ? 'saving' : 'loading' - error(`\`AssetServer\` error loading "${data.path}": ${data.reason}`) + error(`\`AssetServer\` error ${operation} "${data.path}": ${data.reason}`) }) } From 3c89b03370f7a883b12ad086f84f57047f333a6a Mon Sep 17 00:00:00 2001 From: waynemwashuma <94756970+waynemwashuma@users.noreply.github.com> Date: Sun, 31 May 2026 21:18:15 +0300 Subject: [PATCH 04/10] Add ``AssetExporterPlugin --- src/asset/plugins/exporter.js | 61 +++++++++++++++++++++++++++++++++++ src/asset/plugins/index.js | 1 + 2 files changed, 62 insertions(+) create mode 100644 src/asset/plugins/exporter.js diff --git a/src/asset/plugins/exporter.js b/src/asset/plugins/exporter.js new file mode 100644 index 00000000..ca823395 --- /dev/null +++ b/src/asset/plugins/exporter.js @@ -0,0 +1,61 @@ +/** @import {Constructor} from '../../type/index.js' */ +import { App, Plugin } from '../../app/index.js' +import { AppSchedule, CoreSystems } from '../../core/index.js' +import { typeid, typeidGeneric } from '../../type/index.js' +import { Exporter } from '../core/index.js' +import { registerAssetExporterOnAssetServer } from '../systems/index.js' + +/** + * @template T + */ +export class AssetExporterPlugin extends Plugin { + + /** + * @readonly + * @type {Constructor} + */ + asset + + /** + * @readonly + * @type {Exporter} + */ + exporter + + /** + * @param {AssetExporterPluginOptions} options + */ + constructor(options) { + super() + const { asset, exporter } = options + + this.asset = asset + this.exporter = exporter + } + + /** + * @param {App} app + */ + register(app) { + const { asset, exporter } = this + + app + .registerSystem({ + label: `registerAssetExporterOnAssetServer<${typeid(asset)}>`, + schedule: AppSchedule.Startup, + systemGroup: CoreSystems.Start, + system: registerAssetExporterOnAssetServer(asset, exporter) + }) + } + + name() { + return typeidGeneric(AssetExporterPlugin, [this.asset]) + } +} + +/** + * @template T + * @typedef AssetExporterPluginOptions + * @property {Constructor} asset + * @property {Exporter} exporter + */ diff --git a/src/asset/plugins/index.js b/src/asset/plugins/index.js index 6d756bc7..268a09b6 100644 --- a/src/asset/plugins/index.js +++ b/src/asset/plugins/index.js @@ -1,3 +1,4 @@ export * from './asset.js' export * from './assetServer.js' +export * from './exporter.js' export * from './parser.js' From 2cd60730b6688e3a8496bac704b1137d71cffff9 Mon Sep 17 00:00:00 2001 From: waynemwashuma <94756970+waynemwashuma@users.noreply.github.com> Date: Sun, 31 May 2026 21:20:06 +0300 Subject: [PATCH 05/10] Add exporters interface to asset server --- src/asset/resources/assetserver.js | 88 ++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/src/asset/resources/assetserver.js b/src/asset/resources/assetserver.js index 86d841e3..b32570f7 100644 --- a/src/asset/resources/assetserver.js +++ b/src/asset/resources/assetserver.js @@ -77,6 +77,78 @@ export class Parsers { return /** @type {Parser} */(parser) } } + +/** + * @typedef {number} ExporterId + */ +export class Exporters { + + /** + * @private + * @type {Exporter[]} + */ + exporters = [] + + /** + * @private + * @type {Map>} + */ + extensions = new Map() + + /** + * @template T + * @param {Exporter} exporter + */ + add(exporter) { + const id = this.exporters.length + const typeId = typeid(exporter.asset) + const extensions = exporter.getExtensions() + + this.exporters.push(exporter) + + for (let i = 0; i < extensions.length; i++) { + const extension = extensions[i] + const extensionMap = this.extensions.get(extension) + + if (extensionMap) { + if (extensionMap.has(typeId)) { + warn(`Overriding an exporter already present with asset type \`${typeId}\` and with extension "${extension}"".`) + } + + extensionMap.set(typeId, id) + } else { + this.extensions.set(extension, new Map([[typeId, id]])) + } + } + } + + /** + * @template T + * @param {TypeId} type + * @param {string} extension + * @returns {Exporter} + * @throws {string} + */ + get(type, extension) { + const extensions = this.extensions.get(extension) + + if (!extensions) { + throw 'The given extension does not have an exporter registered' + } + + const exporterId = extensions.get(type) + + if (exporterId === undefined) { + throw 'The given asset type does not support the given extension' + } + + const exporter = this.exporters[exporterId] + + assert(exporter, 'Internal error: The givk&en exporter index is invalid.') + + return /** @type {Exporter} */(exporter) + } +} export class AssetServer { /** @@ -93,6 +165,13 @@ export class AssetServer { */ parsers = new Parsers() + /** + * @private + * @readonly + * @type {Exporters} + */ + exporters = new Exporters() + /** * @private * @readonly @@ -143,6 +222,15 @@ export class AssetServer { this.parsers.add(parser) } + /** + * @template T + * @param {Constructor} type + * @param {Exporter} exporter + */ + registerExporter(type, exporter) { + this.exporters.add(exporter) + } + /** * @template T * @param {Constructor} type From 25ffc820d1dc441cab84f744b99da205daed1134 Mon Sep 17 00:00:00 2001 From: waynemwashuma <94756970+waynemwashuma@users.noreply.github.com> Date: Sun, 31 May 2026 21:20:45 +0300 Subject: [PATCH 06/10] Add `save` method to `AssetServer` --- src/asset/resources/assetserver.js | 113 ++++++++++++++++++++++++----- 1 file changed, 95 insertions(+), 18 deletions(-) diff --git a/src/asset/resources/assetserver.js b/src/asset/resources/assetserver.js index b32570f7..fc5ecda7 100644 --- a/src/asset/resources/assetserver.js +++ b/src/asset/resources/assetserver.js @@ -3,8 +3,8 @@ import { typeid } from '../../type/index.js' import { assert, warn } from '../../logger/index.js' import { getFileExtension, swapRemove } from '../../utils/index.js' -import { Assets, Handle, Parser } from '../core/index.js' -import { AssetLoadSuccess, AssetLoadFail } from '../events/index.js' +import { Assets, Handle, Parser, Exporter } from '../core/index.js' +import { AssetLoadSuccess, AssetLoadFail, AssetLoadOperation } from '../events/index.js' /** * @typedef {number} ParserId @@ -264,6 +264,32 @@ export class AssetServer { return handle } + /** + * @template T + * @param {Handle} handle + * @param {string} [path] + */ + save(handle, path) { + const typeId = typeid(handle.type) + const assetId = handle.id() + const info = this.assetInfos.getByAssetId(assetId) + const targetPath = path ?? info?.path + + if (!targetPath) { + this.recordFailure( + typeId, + assetId, + path || "", + 'The given asset handle does not have a registered asset path.', + AssetLoadOperation.Saving + ) + + return + } + + this.post(assetId, typeId, targetPath) + } + /** * @param {AssetId} assetId * @param {TypeId} typeId @@ -277,29 +303,80 @@ export class AssetServer { this.loaded.push(new AssetLoadSuccess(typeId, assetId, path)) this.loadedAssets.push(asset) info.loadstate = LoadState.Loaded - } catch(error) { - let message = '' + } catch (error) { + this.recordFailure(typeId, assetId, path, error) + info.loadstate = LoadState.Failed + } + } - if (typeof error === 'string') { - message = error - } else if (error instanceof Error) { + /** + * @private + * @param {AssetId} assetId + * @param {TypeId} typeId + * @param {string} path + */ + async post(assetId, typeId, path) { + try { + const response = await fetch(path, { + method: 'POST', + body: await this.serialize(assetId, typeId, path) + }) - // eslint-disable-next-line prefer-destructuring - message = error.message - } else { - console.error('Unhandled Error: ', error) + if (!response.ok) { + this.recordFailure(typeId, assetId, path, response.statusText, AssetLoadOperation.Saving) } + } catch(error) { + const message = typeof error === 'string' + ? error + : error instanceof Error + ? error.message + : 'Could not export the asset.' - this.failed.push(new AssetLoadFail( - typeId, - assetId, - path, - message - )) - info.loadstate = LoadState.Failed + this.recordFailure(typeId, assetId, path, message, AssetLoadOperation.Saving) } } + /** + * @private + * @param {AssetId} assetId + * @param {TypeId} typeId + * @param {string} path + * @returns {Promise} + */ + async serialize(assetId, typeId, path) { + const extension = getFileExtension(path) + const assets = this.assets.get(typeId) + const exporter = this.exporters.get(typeId, extension) + + assert(assets, `No assets registered for the asset type \`${typeId}\` on \`AssetServer\``) + + const asset = assets.getByAssetId(assetId) + + if (asset === undefined) { + throw 'Could not find the asset to export.' + } + + return exporter.serialize(asset) + } + + /** + * @private + * @param {TypeId} typeId + * @param {AssetId} assetId + * @param {string} path + * @param {string} message + * @param {number} [operation=AssetLoadOperation.Loading] + */ + recordFailure(typeId, assetId, path, message, operation = AssetLoadOperation.Loading) { + this.failed.push(new AssetLoadFail( + typeId, + assetId, + path, + message, + operation + )) + } + /** * @private * @param {AssetId} assetId From 33b23191e35c9ca59b2e46be944dda2d813194cc Mon Sep 17 00:00:00 2001 From: waynemwashuma <94756970+waynemwashuma@users.noreply.github.com> Date: Sun, 31 May 2026 21:22:06 +0300 Subject: [PATCH 07/10] Add tests for asset server --- src/asset/tests/assetserver.test.js | 54 +++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/src/asset/tests/assetserver.test.js b/src/asset/tests/assetserver.test.js index ffbf01fa..c7ac06be 100644 --- a/src/asset/tests/assetserver.test.js +++ b/src/asset/tests/assetserver.test.js @@ -1,6 +1,7 @@ import { deepStrictEqual, notDeepStrictEqual } from "assert"; import test, { describe,todo } from "node:test"; -import { Assets, AssetServer, Parser } from "../index.js"; +import { Assets, AssetServer, Exporter, Parser } from "../index.js"; +import { typeid } from "../../type/index.js"; class Text { inner = '' @@ -35,6 +36,29 @@ class TextParser extends Parser { } } +/** + * @extends {Exporter} + */ +class TextExporter extends Exporter { + constructor(){ + super(Text) + } + + /** + * @override + */ + getExtensions(){ + return ['txt'] + } + + /** + * @param {Text} asset + */ + async serialize(asset){ + return JSON.stringify(asset) + } +} + describe('Testing `AssetServer`', () => { test('Asset is cached by server.', () => { const server = createServer() @@ -61,6 +85,31 @@ describe('Testing `AssetServer`', () => { test('Asset load state cycle.', () => { todo() }) + + test('Asset save uses exporter.', async () => { + const server = createServer() + const assets = server.getAssets(typeid(Text)) + const handle = assets.add(new Text('hello')) + + const originalFetch = globalThis.fetch + let body + + globalThis.fetch = async (_path, init) => { + body = init.body + + return /**@type {Response}*/({ + ok: true, + statusText: 'OK' + }) + } + + try { + await server.save(handle, '/assets/text/sample.txt') + deepStrictEqual(body, JSON.stringify({ inner: 'hello' })) + } finally { + globalThis.fetch = originalFetch + } + }) }) function createServer() { @@ -69,6 +118,7 @@ function createServer() { server.registerAsset(assets) server.registerParser(Text,new TextParser()) + server.registerExporter(Text,new TextExporter()) return server -} \ No newline at end of file +} From 7d5e2686369605daf8e676d50d8de64a98f9f75e Mon Sep 17 00:00:00 2001 From: waynemwashuma <94756970+waynemwashuma@users.noreply.github.com> Date: Sun, 31 May 2026 22:27:21 +0300 Subject: [PATCH 08/10] Lint diles --- src/asset/resources/assetserver.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/asset/resources/assetserver.js b/src/asset/resources/assetserver.js index fc5ecda7..f90fba1f 100644 --- a/src/asset/resources/assetserver.js +++ b/src/asset/resources/assetserver.js @@ -279,7 +279,7 @@ export class AssetServer { this.recordFailure( typeId, assetId, - path || "", + path || '', 'The given asset handle does not have a registered asset path.', AssetLoadOperation.Saving ) @@ -287,7 +287,7 @@ export class AssetServer { return } - this.post(assetId, typeId, targetPath) + return this.post(assetId, typeId, targetPath) } /** @@ -303,7 +303,7 @@ export class AssetServer { this.loaded.push(new AssetLoadSuccess(typeId, assetId, path)) this.loadedAssets.push(asset) info.loadstate = LoadState.Loaded - } catch (error) { + } catch(error) { this.recordFailure(typeId, assetId, path, error) info.loadstate = LoadState.Failed } @@ -326,11 +326,11 @@ export class AssetServer { this.recordFailure(typeId, assetId, path, response.statusText, AssetLoadOperation.Saving) } } catch(error) { - const message = typeof error === 'string' - ? error - : error instanceof Error - ? error.message - : 'Could not export the asset.' + const message = typeof error === 'string' ? + error : + error instanceof Error ? + error.message : + 'Could not export the asset.' this.recordFailure(typeId, assetId, path, message, AssetLoadOperation.Saving) } From 28c4b20c0b83bb6f64a4258ca32ba594ddaa1d6c Mon Sep 17 00:00:00 2001 From: waynemwashuma <94756970+waynemwashuma@users.noreply.github.com> Date: Sun, 31 May 2026 22:38:59 +0300 Subject: [PATCH 09/10] Add `AssetSaveSuccess` event --- src/asset/events/index.js | 1 + src/asset/events/save.js | 31 ++++++++++++++++++++++++++++++ src/asset/plugins/assetServer.js | 8 ++++++-- src/asset/resources/assetserver.js | 21 +++++++++++++++++++- src/asset/systems/events.js | 19 +++++++++++++++++- src/asset/systems/types.js | 7 +++++-- 6 files changed, 81 insertions(+), 6 deletions(-) create mode 100644 src/asset/events/save.js diff --git a/src/asset/events/index.js b/src/asset/events/index.js index a57bc1d9..d4c025a2 100644 --- a/src/asset/events/index.js +++ b/src/asset/events/index.js @@ -1,3 +1,4 @@ export * from './fail.js' export * from './success.js' +export * from './save.js' export * from './assets.js' diff --git a/src/asset/events/save.js b/src/asset/events/save.js new file mode 100644 index 00000000..d57b2f3f --- /dev/null +++ b/src/asset/events/save.js @@ -0,0 +1,31 @@ +/** @import {TypeId} from '../../type/index.js' */ +/** @import {AssetId} from '../types/index.js' */ + +export class AssetSaveSuccess { + + /** + * @type {TypeId} + */ + typeId + + /** + * @type {AssetId} + */ + assetId + + /** + * @type {string} + */ + path + + /** + * @param {TypeId} typeId + * @param {AssetId} assetId + * @param {string} path + */ + constructor(typeId, assetId, path) { + this.path = path + this.typeId = typeId + this.assetId = assetId + } +} diff --git a/src/asset/plugins/assetServer.js b/src/asset/plugins/assetServer.js index d6e088d5..6e49a80b 100644 --- a/src/asset/plugins/assetServer.js +++ b/src/asset/plugins/assetServer.js @@ -1,8 +1,8 @@ import { App, Plugin } from '../../app/index.js' import { EventPlugin } from '../../event/index.js' import { AssetServer } from '../resources/index.js' -import { AssetLoadFail, AssetLoadSuccess } from '../events/index.js' -import { updateAssets, updateAssetLoadEvents, logFailedLoads, registerAssetServerTypes } from '../systems/index.js' +import { AssetLoadFail, AssetLoadSuccess, AssetSaveSuccess } from '../events/index.js' +import { updateAssets, updateAssetLoadEvents, updateAssetSaveEvents, logFailedLoads, registerAssetServerTypes } from '../systems/index.js' import { AppSchedule, CoreSystems } from '../../core/index.js' export class AssetServerPlugin extends Plugin { @@ -16,12 +16,16 @@ export class AssetServerPlugin extends Plugin { .registerPlugin(new EventPlugin({ event: AssetLoadSuccess })) + .registerPlugin(new EventPlugin({ + event: AssetSaveSuccess + })) .registerPlugin(new EventPlugin({ event: AssetLoadFail })) .registerSystem({ schedule: AppSchedule.Startup, systemGroup: CoreSystems.Start, system: registerAssetServerTypes }) .registerSystem({ schedule: AppSchedule.Update, systemGroup: CoreSystems.End, system: updateAssets }) .registerSystem({ schedule: AppSchedule.Update, systemGroup: CoreSystems.End, system: updateAssetLoadEvents }) + .registerSystem({ schedule: AppSchedule.Update, systemGroup: CoreSystems.End, system: updateAssetSaveEvents }) .registerSystem({ schedule: AppSchedule.Update, systemGroup: CoreSystems.End, system: logFailedLoads }) } } diff --git a/src/asset/resources/assetserver.js b/src/asset/resources/assetserver.js index f90fba1f..ce2759b0 100644 --- a/src/asset/resources/assetserver.js +++ b/src/asset/resources/assetserver.js @@ -4,7 +4,7 @@ import { typeid } from '../../type/index.js' import { assert, warn } from '../../logger/index.js' import { getFileExtension, swapRemove } from '../../utils/index.js' import { Assets, Handle, Parser, Exporter } from '../core/index.js' -import { AssetLoadSuccess, AssetLoadFail, AssetLoadOperation } from '../events/index.js' +import { AssetLoadSuccess, AssetSaveSuccess, AssetLoadFail, AssetLoadOperation } from '../events/index.js' /** * @typedef {number} ParserId @@ -198,6 +198,12 @@ export class AssetServer { */ loaded = [] + /** + * @private + * @type {AssetSaveSuccess[]} + */ + saved = [] + /** * @type {AssetLoadFail[]} */ @@ -324,6 +330,8 @@ export class AssetServer { if (!response.ok) { this.recordFailure(typeId, assetId, path, response.statusText, AssetLoadOperation.Saving) + } else { + this.saved.push(new AssetSaveSuccess(typeId, assetId, path)) } } catch(error) { const message = typeof error === 'string' ? @@ -452,6 +460,17 @@ export class AssetServer { return buffer } + /** + * @returns {readonly AssetSaveSuccess[]} + */ + flushSaveSuccess() { + const buffer = this.saved + + this.saved = [] + + return buffer + } + /** * @returns {readonly AssetLoadFail[]} */ diff --git a/src/asset/systems/events.js b/src/asset/systems/events.js index c565440c..03af7ccd 100644 --- a/src/asset/systems/events.js +++ b/src/asset/systems/events.js @@ -5,7 +5,7 @@ import { Assets } from '../core/index.js' import { Events } from '../../event/index.js' import { typeid, typeidGeneric } from '../../type/index.js' import { AssetServer } from '../resources/index.js' -import { AssetAdded, AssetDropped, AssetModified, AssetLoadSuccess, AssetLoadFail } from '../events/index.js' +import { AssetAdded, AssetDropped, AssetModified, AssetLoadSuccess, AssetSaveSuccess, AssetLoadFail } from '../events/index.js' import { warnOnce } from '../../logger/index.js' /** @@ -79,3 +79,20 @@ export function updateAssetLoadEvents(world) { failEvents.write(failed[i]) } } + +/** + * @param {World} world + * @returns {void} + */ +export function updateAssetSaveEvents(world) { + const server = world.getResource(AssetServer) + + /** @type {Events} */ + const saveEvents = world.getResourceByTypeId(typeidGeneric(Events, [AssetSaveSuccess])) + + const saved = server.flushSaveSuccess() + + for (let i = 0; i < saved.length; i++) { + saveEvents.write(saved[i]) + } +} diff --git a/src/asset/systems/types.js b/src/asset/systems/types.js index 91f7aa18..2325a4b6 100644 --- a/src/asset/systems/types.js +++ b/src/asset/systems/types.js @@ -6,7 +6,7 @@ import { TypeRegistry } from '../../reflect/resources/index.js' import { Assets, Handle } from '../core/index.js' import { typeid, typeidGeneric } from '../../type/index.js' import { AssetServer } from '../resources/index.js' -import { AssetLoadFail } from '../events/index.js' +import { AssetLoadFail, AssetSaveSuccess } from '../events/index.js' /** * @template T @@ -38,10 +38,13 @@ export function registerAssetServerTypes(world) { const registry = world.getResource(TypeRegistry) const assetLoadFailArrayId = typeidGeneric(Array, [AssetLoadFail]) + const assetSaveSuccessArrayId = typeidGeneric(Array, [AssetSaveSuccess]) registry.registerTypeId(assetLoadFailArrayId, new ArrayInfo(typeid(AssetLoadFail))) + registry.registerTypeId(assetSaveSuccessArrayId, new ArrayInfo(typeid(AssetSaveSuccess))) registry.register(AssetServer, new StructInfo({ - failed: new Field(assetLoadFailArrayId) + failed: new Field(assetLoadFailArrayId), + saved: new Field(assetSaveSuccessArrayId) })) } From 321f482e221290b0684cc840bdc35ea2866a3eea Mon Sep 17 00:00:00 2001 From: waynemwashuma <94756970+waynemwashuma@users.noreply.github.com> Date: Sun, 31 May 2026 22:59:31 +0300 Subject: [PATCH 10/10] Lint files --- src/asset/resources/assetserver.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/asset/resources/assetserver.js b/src/asset/resources/assetserver.js index ce2759b0..9dba33b4 100644 --- a/src/asset/resources/assetserver.js +++ b/src/asset/resources/assetserver.js @@ -334,11 +334,15 @@ export class AssetServer { this.saved.push(new AssetSaveSuccess(typeId, assetId, path)) } } catch(error) { - const message = typeof error === 'string' ? - error : - error instanceof Error ? - error.message : - 'Could not export the asset.' + let message = 'Could not export the asset.' + + if (typeof error === 'string') { + message = error + } else if (error instanceof Error) { + const { message: errorMessage } = error + + message = errorMessage + } this.recordFailure(typeId, assetId, path, message, AssetLoadOperation.Saving) }