@cldmv/slothlet 3.4.1 → 3.5.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/README.md +12 -8
- package/bin/slothlet.mjs +218 -0
- package/dist/lib/builders/api_builder.mjs +1 -1
- package/dist/lib/handlers/api-manager.mjs +1 -1
- package/dist/lib/handlers/ownership.mjs +1 -1
- package/dist/lib/i18n/languages/de-de.json +4 -1
- package/dist/lib/i18n/languages/en-gb.json +4 -1
- package/dist/lib/i18n/languages/en-us.json +4 -1
- package/dist/lib/i18n/languages/es-es.json +4 -1
- package/dist/lib/i18n/languages/es-mx.json +4 -1
- package/dist/lib/i18n/languages/fr-fr.json +4 -1
- package/dist/lib/i18n/languages/hi-in.json +4 -1
- package/dist/lib/i18n/languages/ja-jp.json +4 -1
- package/dist/lib/i18n/languages/ko-kr.json +4 -1
- package/dist/lib/i18n/languages/pt-br.json +4 -1
- package/dist/lib/i18n/languages/ru-ru.json +4 -1
- package/dist/lib/i18n/languages/zh-cn.json +4 -1
- package/dist/lib/processors/loader.mjs +1 -1
- package/dist/lib/processors/typescript.mjs +1 -1
- package/dist/lib/runtime/runtime-asynclocalstorage.mjs +1 -1
- package/dist/lib/runtime/runtime-livebindings.mjs +1 -1
- package/dist/lib/runtime/runtime.mjs +1 -1
- package/dist/lib/typegen/typegen.mjs +17 -0
- package/dist/slothlet.mjs +1 -1
- package/package.json +14 -2
- package/types/dist/lib/builders/api_builder.d.mts.map +1 -1
- package/types/dist/lib/handlers/api-manager.d.mts +1 -0
- package/types/dist/lib/handlers/api-manager.d.mts.map +1 -1
- package/types/dist/lib/handlers/ownership.d.mts +3 -0
- package/types/dist/lib/handlers/ownership.d.mts.map +1 -1
- package/types/dist/lib/processors/loader.d.mts.map +1 -1
- package/types/dist/lib/processors/typescript.d.mts +4 -0
- package/types/dist/lib/processors/typescript.d.mts.map +1 -1
- package/types/dist/lib/runtime/runtime-asynclocalstorage.d.mts.map +1 -1
- package/types/dist/lib/runtime/runtime-livebindings.d.mts.map +1 -1
- package/types/dist/lib/runtime/runtime.d.mts.map +1 -1
- package/types/dist/lib/typegen/typegen.d.mts +5 -0
- package/types/dist/lib/typegen/typegen.d.mts.map +1 -0
- package/types/dist/slothlet.d.mts.map +1 -1
|
@@ -14,4 +14,4 @@
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import fs from"node:fs/promises";import path from"node:path";import{translate}from"@cldmv/slothlet/i18n";import{ComponentBase}from"@cldmv/slothlet/factories/component-base";import{UnifiedWrapper,resolveWrapper}from"@cldmv/slothlet/handlers/unified-wrapper";class ApiManager extends ComponentBase{static slothletProperty="apiManager";constructor(slothlet){super(slothlet);this.state={addHistory:[],initialConfig:slothlet?.config||null,operationHistory:[]}}normalizeApiPath(apiPath){if(apiPath===""||apiPath===null||apiPath===void 0){return{apiPath:"",parts:[]}}if(Array.isArray(apiPath)){if(apiPath.length===0){return{apiPath:"",parts:[]}}for(let i=0;i<apiPath.length;i++){if(typeof apiPath[i]!=="string"){throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID",{apiPath,segment:apiPath[i],index:i,reason:translate("API_PATH_REASON_ARRAY_ELEMENTS"),validationError:true})}if(apiPath[i].trim()===""){throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID",{apiPath,segment:apiPath[i],index:i,reason:translate("API_PATH_REASON_ARRAY_EMPTY_SEGMENTS"),validationError:true})}}if(apiPath[0]==="slothlet"||apiPath.length===1&&(apiPath[0]==="shutdown"||apiPath[0]==="destroy")){throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID",{apiPath,reason:translate("API_PATH_REASON_RESERVED_NAME"),index:void 0,segment:void 0,validationError:true})}return{apiPath:apiPath.join("."),parts:apiPath}}if(typeof apiPath!=="string"){throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID",{apiPath,reason:translate("API_PATH_REASON_INVALID_TYPE"),index:void 0,segment:void 0,validationError:true})}const normalized=apiPath.trim();if(normalized===""){return{apiPath:"",parts:[]}}const parts=normalized.split(".");if(parts.length===0||parts.some(part=>part.trim()==="")){throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID",{apiPath:normalized,reason:translate("API_PATH_REASON_EMPTY_SEGMENTS"),index:void 0,segment:void 0,validationError:true})}if(parts[0]==="slothlet"||normalized==="shutdown"||normalized==="destroy"){throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID",{apiPath:normalized,reason:translate("API_PATH_REASON_RESERVED_NAME"),index:void 0,segment:void 0,validationError:true})}return{apiPath:normalized,parts}}async resolvePath(inputPath){if(!inputPath||typeof inputPath!=="string"){throw new this.SlothletError("INVALID_CONFIG_DIR_INVALID",{dir:inputPath,validationError:true})}const resolvedPath=this.slothlet.helpers.resolver.resolvePathFromCaller(inputPath);try{const stats=await fs.stat(resolvedPath);return{resolvedPath,isDirectory:stats.isDirectory(),isFile:stats.isFile()}}catch(error){if(error instanceof this.SlothletError){throw error}throw new this.SlothletError("INVALID_CONFIG_DIR_INVALID",{dir:resolvedPath,validationError:true})}}async resolveFolderPath(folderPath){if(!folderPath||typeof folderPath!=="string"){throw new this.SlothletError("INVALID_CONFIG_DIR_INVALID",{dir:folderPath,validationError:true})}const resolvedPath=this.slothlet.helpers.resolver.resolvePathFromCaller(folderPath);try{const stats=await fs.stat(resolvedPath);if(!stats.isDirectory()){throw new this.SlothletError("INVALID_CONFIG_DIR_INVALID",{dir:resolvedPath,validationError:true})}}catch(error){if(error instanceof this.SlothletError){throw error}throw new this.SlothletError("INVALID_CONFIG_DIR_INVALID",{dir:resolvedPath,validationError:true})}return resolvedPath}buildDefaultModuleId(apiPath,____resolvedFolderPath){const randomSuffix=Math.random().toString(36).substring(2,8);const prefix=apiPath||"auto";return`${prefix}_${randomSuffix}`}getValueAtPath(root,parts){let current=root;for(const part of parts){if(!current||typeof current!=="object"&&typeof current!=="function"){return void 0}current=current[part]}return current}ensureParentPath(root,parts,options={}){const{moduleID,sourceFolder}=options;let current=root;for(let i=0;i<parts.length-1;i+=1){const part=parts[i];const next=current[part];if(next===void 0){const containerPath=parts.slice(0,i+1).join(".");const containerWrapper=new UnifiedWrapper(this.slothlet,{mode:this.____config.mode,apiPath:containerPath,moduleID,sourceFolder});containerWrapper.___setImpl({},moduleID);current[part]=containerWrapper.createProxy();current=current[part];continue}if(next&&(typeof next==="object"||typeof next==="function")){current=next;continue}throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID",{apiPath:parts.slice(0,i+1).join("."),reason:translate("API_PATH_REASON_NOT_TRAVERSABLE"),index:void 0,segment:void 0,validationError:true})}return current}isWrapperProxy(value){return!!(value&&(typeof value==="object"||typeof value==="function")&&resolveWrapper(value)!==null)}async syncWrapper(existingProxy,nextProxy,config,collisionMode="replace",moduleID=null){if(config?.debug?.api){this.slothlet.debug("api",{key:"DEBUG_MODE_SYNC_WRAPPER_ENTRY_EXISTING",apiPath:resolveWrapper(existingProxy)?.apiPath});this.slothlet.debug("api",{key:"DEBUG_MODE_SYNC_WRAPPER_ENTRY_NEXT",apiPath:resolveWrapper(nextProxy)?.apiPath})}if(!this.isWrapperProxy(existingProxy)||!this.isWrapperProxy(nextProxy)){return false}const existingWrapper=resolveWrapper(existingProxy)??existingProxy;const nextWrapper=resolveWrapper(nextProxy)??nextProxy;if(config?.debug?.api){this.slothlet.debug("api",{key:"DEBUG_MODE_SYNC_WRAPPER_EXISTING",apiPath:existingWrapper.apiPath});this.slothlet.debug("api",{key:"DEBUG_MODE_SYNC_WRAPPER_NEXT",apiPath:nextWrapper.apiPath})}if(nextWrapper.____slothletInternal.materializeFunc&&collisionMode!=="merge"){existingWrapper.____slothletInternal.materializeFunc=nextWrapper.____slothletInternal.materializeFunc}const existingChildKeys=Object.keys(existingWrapper).filter(k=>!k.startsWith("_")&&!k.startsWith("__"));const nextChildKeys=Object.keys(nextWrapper).filter(k=>!k.startsWith("_")&&!k.startsWith("__"));if(config?.debug?.api){this.slothlet.debug("api",{key:"DEBUG_MODE_SYNC_WRAPPER_BEFORE_MERGE",existingCacheSize:existingChildKeys.length,nextCacheSize:nextChildKeys.length});this.slothlet.debug("api",{key:"DEBUG_MODE_SYNC_WRAPPER_NEXT_IMPL_KEYS",implKeys:Object.keys(nextWrapper.____slothletInternal.impl||{})});this.slothlet.debug("api",{key:"DEBUG_MODE_SYNC_WRAPPER_NEXT_CHILDCACHE_KEYS",childCacheKeys:nextChildKeys})}if(collisionMode==="replace"){if(existingWrapper.___setImpl&&nextWrapper.____slothletInternal.impl!==void 0){existingWrapper.___setImpl(nextWrapper.____slothletInternal.impl,moduleID)}else if(nextWrapper.____slothletInternal.impl===void 0){existingWrapper.____slothletInternal.impl=null}else{if(nextWrapper.____slothletInternal.impl!==void 0){existingWrapper.____slothletInternal.impl=nextWrapper.____slothletInternal.impl;if(typeof nextWrapper.____slothletInternal.impl==="function"||nextWrapper.____slothletInternal.impl&&typeof nextWrapper.____slothletInternal.impl.default==="function"){existingWrapper.isCallable=true}}}for(const key of existingChildKeys){delete existingWrapper[key]}existingWrapper.___adoptImplChildren();for(const key of nextChildKeys){const childValue=nextWrapper[key];Object.defineProperty(existingWrapper,key,{value:childValue,writable:false,enumerable:true,configurable:true})}}else if(collisionMode==="merge"){for(const key of nextChildKeys){const isInternal=typeof key==="string"&&(key.startsWith("_")||key.startsWith("__"));if(!isInternal&&!Object.prototype.hasOwnProperty.call(existingWrapper,key)){const childValue=nextWrapper[key];Object.defineProperty(existingWrapper,key,{value:childValue,writable:false,enumerable:true,configurable:true})}else if(!isInternal){const existingChild=existingWrapper[key];const nextChild=nextWrapper[key];if(this.isWrapperProxy(existingChild)&&this.isWrapperProxy(nextChild)){const syncWrapper_nextChildWrapper=resolveWrapper(nextChild)??nextChild;const syncWrapper_hasGrandChildren=Object.keys(syncWrapper_nextChildWrapper).some(k=>!k.startsWith("_")&&!k.startsWith("__"));if(syncWrapper_hasGrandChildren){await this.syncWrapper(existingChild,nextChild,config,collisionMode,moduleID)}}}}}else if(collisionMode==="merge-replace"){for(const key of nextChildKeys){const childValue=nextWrapper[key];const isInternal=typeof key==="string"&&(key.startsWith("_")||key.startsWith("__"));if(!isInternal&&Object.prototype.hasOwnProperty.call(existingWrapper,key)){const existingChild=existingWrapper[key];if(this.isWrapperProxy(existingChild)&&this.isWrapperProxy(childValue)){await this.syncWrapper(existingChild,childValue,config,collisionMode,moduleID)}else{delete existingWrapper[key];Object.defineProperty(existingWrapper,key,{value:childValue,writable:false,enumerable:true,configurable:true})}}else if(!isInternal){Object.defineProperty(existingWrapper,key,{value:childValue,writable:false,enumerable:true,configurable:true})}}}if(existingWrapper.____slothletInternal.state){const isActuallyMaterialized=existingWrapper.____slothletInternal.impl&&typeof existingWrapper.____slothletInternal.impl!=="function";existingWrapper.____slothletInternal.state.materialized=isActuallyMaterialized;existingWrapper.____slothletInternal.state.inFlight=false}return true}async mutateApiValue(existingValue,nextValue,options,config){if(config?.debug?.api){this.slothlet.debug("api",{key:"DEBUG_MODE_MUTATE_API_VALUE_CALLED",existingType:typeof existingValue,nextType:typeof nextValue});this.slothlet.debug("api",{key:"DEBUG_MODE_MUTATE_API_VALUE_WRAPPER_STATUS",existingIsWrapper:this.isWrapperProxy(existingValue),nextIsWrapper:this.isWrapperProxy(nextValue)});this.slothlet.debug("api",{key:"DEBUG_MODE_MUTATE_API_VALUE_NEXT_VALUE",nextValue});this.slothlet.debug("api",{key:"DEBUG_MODE_MUTATE_API_VALUE_NEXT_VALUE_KEYS",nextValueKeys:nextValue?Object.keys(nextValue):[]})}if(existingValue===nextValue){return}if(this.isWrapperProxy(existingValue)&&this.isWrapperProxy(nextValue)){if(config?.debug?.api){this.slothlet.debug("api",{key:"DEBUG_MODE_MUTATE_API_VALUE_SYNC_WRAPPERS"})}await this.syncWrapper(existingValue,nextValue,config,options.collisionMode,options.moduleID);return}if(this.isWrapperProxy(existingValue)&&!this.isWrapperProxy(nextValue)){const nextIsObjectLike=nextValue&&(typeof nextValue==="object"||typeof nextValue==="function");const nextHasKeys=nextIsObjectLike&&Object.keys(nextValue).length>0;if(nextHasKeys){if(config?.debug?.api){this.slothlet.debug("api",{key:"DEBUG_MODE_MUTATE_API_VALUE_MERGE_INTO_WRAPPER"});this.slothlet.debug("api",{key:"DEBUG_MODE_MUTATE_API_VALUE_MERGE_KEYS",keys:Object.keys(nextValue)})}await this.slothlet.builders.apiAssignment.mergeApiObjects(existingValue,nextValue,{removeMissing:options.removeMissing,mutateExisting:true,allowOverwrite:true,syncWrapper:this.syncWrapper.bind(this),collisionMode:options.collisionMode,moduleID:options.moduleID});return}const existingValueRaw=resolveWrapper(existingValue);if(existingValueRaw!==null){if(config?.debug?.api){this.slothlet.debug("api",{key:"DEBUG_MODE_MUTATE_API_VALUE_SETIMPL_FALLBACK"})}existingValueRaw.___setImpl(resolveWrapper(nextValue)?.__impl??nextValue);return}}if(existingValue&&typeof existingValue==="object"&&nextValue&&typeof nextValue==="object"){await this.slothlet.builders.apiAssignment.mergeApiObjects(existingValue,nextValue,{removeMissing:options.removeMissing,mutateExisting:true,allowOverwrite:true,syncWrapper:this.syncWrapper.bind(this),collisionMode:options.collisionMode,moduleID:options.moduleID});return existingValue}return nextValue}async setValueAtPath(root,parts,value,options){const parent=this.ensureParentPath(root,parts,{moduleID:options.moduleID,sourceFolder:options.sourceFolder});const finalKey=parts[parts.length-1];const existing=parent?parent[finalKey]:void 0;const collisionMode=options.collisionMode||"merge";const moduleID=options.moduleID;this.slothlet.debug("api",{key:"DEBUG_MODE_SET_VALUE_AT_PATH",finalKey,existingType:typeof existing,valueType:typeof value,collisionMode,options});if(existing!==void 0){if(collisionMode==="error"){throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID",{apiPath:parts.join("."),reason:translate("API_PATH_REASON_COLLISION_ERROR"),index:void 0,segment:void 0,validationError:true})}if(collisionMode==="skip"){this.slothlet.debug("api",{key:"DEBUG_MODE_SET_VALUE_AT_PATH_SKIP_COLLISION",path:parts.join("."),mode:"skip"});return false}if(collisionMode==="warn"){if(this.slothlet&&!this.____config?.silent){new this.SlothletWarning("WARNING_HOT_RELOAD_PATH_COLLISION",{apiPath:parts.join(".")})}return false}if(collisionMode==="replace"){const existingIsObject=typeof existing==="object"||typeof existing==="function";const valueIsObject=typeof value==="object"||typeof value==="function";if(existingIsObject&&valueIsObject){this.slothlet.debug("api",{key:"DEBUG_MODE_SET_VALUE_AT_PATH_REPLACE_MERGE",path:parts.join("."),mode:"replace"});await this.mutateApiValue(existing,value,{removeMissing:false,allowOverwrite:true,collisionMode:"replace",moduleID},this.____config);return true}else{parent[finalKey]=value;return true}}if(collisionMode==="merge"||collisionMode==="merge-replace"){const existingIsObject=typeof existing==="object"||typeof existing==="function";const valueIsObject=typeof value==="object"||typeof value==="function";if(existingIsObject&&valueIsObject){this.slothlet.debug("api",{key:"DEBUG_MODE_SET_VALUE_AT_PATH_MERGE_PROPS",mode:collisionMode});await this.mutateApiValue(existing,value,{removeMissing:false,allowOverwrite:true,collisionMode},this.____config);return true}else{if(this.slothlet&&!this.____config?.silent){new this.SlothletWarning("WARNING_HOT_RELOAD_MERGE_PRIMITIVES",{apiPath:parts.join(".")})}return false}}}this.slothlet.debug("api",{key:"DEBUG_MODE_SET_VALUE_AT_PATH_ASSIGN",finalKey});parent[finalKey]=value;return true}async deletePath(root,parts){let current=root;const stack=[];for(const part of parts.slice(0,-1)){if(!current||typeof current!=="object"&&typeof current!=="function"){return false}stack.push({parent:current,key:part});current=current[part]}const finalKey=parts[parts.length-1];if(!current||typeof current!=="object"&&typeof current!=="function"){return false}if(!Object.prototype.hasOwnProperty.call(current,finalKey)){return false}const removedImpl=current[finalKey];const apiPath=parts.join(".");if(removedImpl&&this.slothlet.handlers?.lifecycle){const metadata=this.slothlet.handlers.metadata?.getMetadata?.(removedImpl);await this.slothlet.handlers.lifecycle.emit("impl:removed",{apiPath,impl:removedImpl,source:"removal",moduleID:metadata?.moduleID,filePath:metadata?.filePath,sourceFolder:metadata?.sourceFolder})}if(resolveWrapper(current)){const wrapper=resolveWrapper(current);const isInternal=typeof finalKey==="string"&&(finalKey.startsWith("_")||finalKey.startsWith("__"));if(!isInternal&&finalKey in wrapper){delete wrapper[finalKey]}if(wrapper.____slothletInternal.impl&&typeof wrapper.____slothletInternal.impl==="object"){delete wrapper.____slothletInternal.impl[finalKey]}}delete current[finalKey];if(removedImpl&&(typeof removedImpl==="object"||typeof removedImpl==="function")){if(resolveWrapper(removedImpl)){const wrapper=resolveWrapper(removedImpl);if(wrapper.____slothletInternal.impl!==void 0){wrapper.____slothletInternal.impl=null}const childKeys=Object.keys(wrapper).filter(k=>!k.startsWith("_")&&!k.startsWith("__"));for(const key of childKeys){delete wrapper[key]}if(wrapper.____slothletInternal.state){wrapper.____slothletInternal.state.materialized=false;wrapper.____slothletInternal.state.inFlight=false}}}if(this.slothlet.handlers?.metadata){const rootSegment=apiPath.split(".")[0];this.slothlet.handlers.metadata.removeUserMetadataByApiPath(rootSegment)}for(let i=stack.length-1;i>=0;i-=1){const{parent,key}=stack[i];const value=parent[key];if(value&&(typeof value==="object"||typeof value==="function")&&Object.keys(value).length===0){delete parent[key]}}return true}async restoreApiPath(apiPath,moduleID){const normalizedModuleId=moduleID||null;const historyEntry=this.state.addHistory.slice().reverse().find(entry=>entry.apiPath===apiPath&&(normalizedModuleId?entry.moduleID===normalizedModuleId:true));if(historyEntry){await this.addApiComponent({apiPath:historyEntry.apiPath,folderPath:historyEntry.folderPath,options:{...historyEntry.options,metadata:historyEntry.metadata,mutateExisting:true,forceOverwrite:true,collisionMode:"replace",recordHistory:false}});return}if(normalizedModuleId==="base"||normalizedModuleId==="core"){const baseApi=await this.slothlet.builders.builder.buildAPI({dir:this.____config.dir,mode:this.____config.mode,moduleID:"base"});const{parts}=this.normalizeApiPath(apiPath);let baseValue=this.getValueAtPath(baseApi,parts);if(baseValue===void 0){await this.deletePath(this.slothlet.api,parts);await this.deletePath(this.slothlet.boundApi,parts);return}const baseValueRaw=resolveWrapper(baseValue);if(baseValue&&baseValueRaw!==null){baseValue=baseValueRaw.__impl}await this.setValueAtPath(this.slothlet.api,parts,baseValue,{mutateExisting:true,allowOverwrite:true,collisionMode:"replace",moduleID:normalizedModuleId});await this.setValueAtPath(this.slothlet.boundApi,parts,baseValue,{mutateExisting:true,allowOverwrite:true,collisionMode:"replace",moduleID:normalizedModuleId})}}#normalizePermissionShorthandEntry(entry){const getValueType=value=>{if(value===null)return"null";if(Array.isArray(value))return"array";return typeof value};if(typeof entry==="string"){return{target:entry,condition:void 0}}if(!entry||typeof entry!=="object"||Array.isArray(entry)){throw new this.SlothletError("INVALID_PERMISSION_RULE",{reason:translate("PERM_RULE_NOT_OBJECT"),received:getValueType(entry),validationError:true})}if(typeof entry.target!=="string"||!entry.target){throw new this.SlothletError("INVALID_PERMISSION_RULE",{reason:translate("PERM_RULE_TARGET_REQUIRED"),received:typeof entry.target,validationError:true})}return{target:entry.target,condition:entry.condition}}async addApiComponent(params){const{apiPath,folderPath,options={},versionConfig=null}=params||{};if(Array.isArray(folderPath)){const moduleIDs=[];for(const singlePath of folderPath){const moduleID2=await this.addApiComponent({apiPath,folderPath:singlePath,options,versionConfig});moduleIDs.push(moduleID2)}return moduleIDs}const{metadata={},...restOptions}=options;if(!this.slothlet||!this.slothlet.isLoaded){throw new this.SlothletError("INVALID_CONFIG_NOT_LOADED",{operation:"addApi",validationError:true})}const{apiPath:normalizedPath,parts}=this.normalizeApiPath(apiPath);let effectivePath=normalizedPath;let effectiveParts=parts;if(versionConfig?.version!==void 0&&versionConfig?.version!==null){if(normalizedPath===""){throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID",{apiPath,reason:translate("API_PATH_REASON_VERSIONED_ROOT"),index:void 0,segment:void 0,validationError:true})}if(typeof versionConfig.version!=="string"||!String(versionConfig.version).trim()){throw new this.SlothletError("INVALID_CONFIG_VERSION_TAG",{received:versionConfig.version,validationError:true})}const versionTag=String(versionConfig.version).trim();effectiveParts=[versionTag,...parts];effectivePath=effectiveParts.join(".")}const{resolvedPath,isDirectory,isFile}=await this.resolvePath(folderPath);if(!isDirectory&&!isFile){throw new this.SlothletError("INVALID_CONFIG_PATH_TYPE",{path:resolvedPath,validationError:true})}if(isFile){const ext=path.extname(resolvedPath);if(![".mjs",".cjs",".js"].includes(ext)){throw new this.SlothletError("INVALID_CONFIG_FILE_TYPE",{path:resolvedPath,extension:ext,validationError:true})}}const resolvedFolderPath=resolvedPath;let collisionMode;if(restOptions.forceOverwrite){collisionMode="replace"}else{collisionMode=restOptions.collisionMode||this.____config.api?.collision?.api||"error"}const mutateExisting=!!(restOptions.mutateExisting||collisionMode==="merge");const moduleID=restOptions.moduleID?String(restOptions.moduleID):this.buildDefaultModuleId(normalizedPath,resolvedFolderPath);if(restOptions.forceOverwrite&&!moduleID){throw new this.SlothletError("INVALID_CONFIG_FORCE_OVERWRITE_REQUIRES_MODULE_ID",{apiPath:normalizedPath,validationError:true})}let dirForBuild=resolvedFolderPath;let fileFilter=null;if(isFile){dirForBuild=path.dirname(resolvedFolderPath);const fileName=path.basename(resolvedFolderPath);fileFilter=file=>file===fileName}const newApi=await this.slothlet.builders.builder.buildAPI({dir:dirForBuild,mode:this.____config.mode,apiPathPrefix:effectivePath,collisionContext:"addApi",moduleID,collisionMode,fileFilter});if(this.slothlet.handlers.apiCacheManager){this.slothlet.handlers.apiCacheManager.set(moduleID,{endpoint:effectivePath,moduleID,api:newApi,folderPath:resolvedFolderPath,mode:this.____config.mode,sanitizeOptions:this.____config.sanitize||{},collisionMode,config:{...this.____config},timestamp:Date.now()})}this.slothlet.debug("api",{key:"DEBUG_MODE_ADD_API_COMPONENT_BUILD_RETURN",topLevelKeys:Object.keys(newApi),dottedKeys:Object.keys(newApi).filter(k=>k.includes(".")),wrappers:Object.keys(newApi).filter(k=>resolveWrapper(newApi[k])!==null).map(k=>{const _w=resolveWrapper(newApi[k]);return{key:k,apiPath:_w.apiPath,implKeys:Object.keys(_w.____slothletInternal.impl||{}),childCacheSize:Object.keys(_w).filter(k2=>!k2.startsWith("_")&&!k2.startsWith("__")).length,childCacheKeys:Object.keys(_w).filter(k2=>!k2.startsWith("_")&&!k2.startsWith("__"))}}),nonWrappers:Object.keys(newApi).filter(k=>resolveWrapper(newApi[k])===null).map(k=>({key:k,type:typeof newApi[k]}))});let apiToMerge=newApi;if(isFile&&Object.keys(newApi).length===1){const fileName=Object.keys(newApi)[0];apiToMerge=newApi[fileName]}if(!isFile&&normalizedPath){const lastPart=normalizedPath.includes(".")?normalizedPath.split(".").pop():normalizedPath;if(lastPart&&Object.prototype.hasOwnProperty.call(apiToMerge,lastPart)){const dupValue=apiToMerge[lastPart];const dupType=typeof dupValue;if(dupValue!==null&&(dupType==="object"||dupType==="function")){const dupWrapper=resolveWrapper(dupValue);const dupFilePath=dupWrapper?.____slothletInternal?.filePath;const dupFileDir=dupFilePath?dupFilePath.replace(/\\/g,"/").split("/").slice(0,-1).join("/"):null;const normalizedFolderPath=resolvedFolderPath.replace(/\\/g,"/").replace(/\/$/,"");const expectedDir=normalizedFolderPath+"/"+lastPart;const isDirectChild=dupFileDir===expectedDir||dupFileDir===normalizedFolderPath;if(isDirectChild){const hoisted={};for(const k of Object.keys(apiToMerge)){if(k!==lastPart)hoisted[k]=apiToMerge[k]}if(dupWrapper){for(const k of Object.keys(dupWrapper).filter(k2=>!k2.startsWith("_")&&!k2.startsWith("__"))){hoisted[k]=dupWrapper[k]}}else{for(const k of Object.keys(dupValue)){hoisted[k]=dupValue[k]}}apiToMerge=hoisted;this.slothlet.debug("api",{key:"DEBUG_MODE_RULE_13_DEDUP_HOISTED_KEY",lastPart,newKeys:Object.keys(apiToMerge)})}}}}if(this.____config.debug?.api){this.slothlet.debug("api",{key:"DEBUG_MODE_ADD_API_COMPONENT_MERGE_KEYS",keys:Object.keys(apiToMerge),isRootLevel:parts.length===0})}let anyAssignmentSucceeded=false;if(parts.length===0){for(const key of Object.keys(newApi)){const result1=await this.setValueAtPath(this.slothlet.api,[key],newApi[key],{mutateExisting,collisionMode,moduleID,sourceFolder:resolvedFolderPath});const result2=await this.setValueAtPath(this.slothlet.boundApi,[key],newApi[key],{mutateExisting,collisionMode,moduleID,sourceFolder:resolvedFolderPath});if(result1||result2){anyAssignmentSucceeded=true}}}else{if(resolveWrapper(apiToMerge)===null){const isCallableNamespace=typeof apiToMerge==="function";const containerWrapper=new UnifiedWrapper(this.slothlet,{apiPath:effectivePath,mode:this.____config.mode,isCallable:isCallableNamespace,moduleID,filePath:resolvedFolderPath,sourceFolder:resolvedFolderPath});containerWrapper.___setImpl(apiToMerge,moduleID);apiToMerge=containerWrapper.createProxy()}const result1=await this.setValueAtPath(this.slothlet.api,effectiveParts,apiToMerge,{mutateExisting,collisionMode,moduleID,sourceFolder:resolvedFolderPath});const result2=await this.setValueAtPath(this.slothlet.boundApi,effectiveParts,apiToMerge,{mutateExisting,collisionMode,moduleID,sourceFolder:resolvedFolderPath});if(result1||result2){anyAssignmentSucceeded=true}}if(anyAssignmentSucceeded){const pendingMaterializations=[];const seenWrappers=new Set;const collectPendingMaterializations=(obj,depth=0)=>{if(!obj||typeof obj!=="object"||depth>10)return;if(obj.__isVersionDispatcher===true)return;const wrapper=resolveWrapper(obj);if(wrapper){if(seenWrappers.has(wrapper))return;seenWrappers.add(wrapper);if(wrapper.____slothletInternal.materializationPromise){pendingMaterializations.push(wrapper.____slothletInternal.materializationPromise)}const childKeys=Object.keys(wrapper).filter(k=>!k.startsWith("_")&&!k.startsWith("__"));for(const key of childKeys){collectPendingMaterializations(wrapper[key],depth+1)}}for(const key of Object.keys(obj)){if(key!=="____slothletInternal"){collectPendingMaterializations(obj[key],depth+1)}}};if(effectiveParts.length===0){for(const key of Object.keys(newApi)){if(this.slothlet.api[key]){collectPendingMaterializations(this.slothlet.api[key])}}}else{let current=this.slothlet.api;for(const part of effectiveParts){if(current&¤t[part]){current=current[part]}else{break}}if(current){collectPendingMaterializations(current)}}if(pendingMaterializations.length>0){if(this.____config.debug?.api){this.slothlet.debug("api",{key:"DEBUG_MODE_AWAITING_PENDING_MATERIALIZATIONS",count:pendingMaterializations.length,apiPath:normalizedPath})}await Promise.all(pendingMaterializations)}}if(anyAssignmentSucceeded&&metadata&&Object.keys(metadata).length>0&&this.slothlet.handlers.metadata){if(parts.length===0){for(const key of Object.keys(newApi)){this.slothlet.handlers.metadata.registerUserMetadata(key,metadata)}}else{const rootSegment=effectiveParts[0];this.slothlet.handlers.metadata.registerUserMetadata(rootSegment,metadata)}}if(this.slothlet.handlers.ownership&&moduleID){this.slothlet.handlers.ownership.registerSubtree(apiToMerge,moduleID,effectivePath)}if(this.slothlet.handlers.ownership){if(restOptions.recordHistory!==false){this.state.addHistory.push({apiPath:normalizedPath,folderPath:resolvedFolderPath,options:{...restOptions,metadata,moduleID},moduleID,versionConfig:versionConfig||null});this.state.operationHistory.push({type:"add",apiPath:normalizedPath,folderPath:resolvedFolderPath,options:{...restOptions,metadata,moduleID},moduleID,versionConfig:versionConfig||null})}}if(versionConfig?.version&&this.slothlet.handlers.versionManager){const versionTag=String(versionConfig.version).trim();try{this.slothlet.handlers.versionManager.registerVersion(normalizedPath,versionTag,moduleID,versionConfig.metadata??{},versionConfig.default??false)}catch(error){await this._rollbackFailedVersionedAdd({moduleID,effectivePath,normalizedPath});throw error}}if(restOptions.permissions&&this.slothlet.handlers?.permissionManager){const perms=restOptions.permissions;const callerPattern=`${normalizedPath}.**`;if(Array.isArray(perms.deny)){for(const entry of perms.deny){const{target,condition}=this.#normalizePermissionShorthandEntry(entry);this.slothlet.handlers.permissionManager.addRule({caller:callerPattern,target,effect:"deny",condition},moduleID)}}if(Array.isArray(perms.allow)){for(const entry of perms.allow){const{target,condition}=this.#normalizePermissionShorthandEntry(entry);this.slothlet.handlers.permissionManager.addRule({caller:callerPattern,target,effect:"allow",condition},moduleID)}}}return moduleID}async _rollbackFailedVersionedAdd({moduleID,effectivePath,normalizedPath}){let addIndex=-1;for(let i=this.state.operationHistory.length-1;i>=0;i--){const entry=this.state.operationHistory[i];if(entry?.type==="add"&&entry?.apiPath===normalizedPath&&entry?.moduleID===moduleID){addIndex=i;break}}if(addIndex!==-1){this.state.operationHistory.splice(addIndex,1)}this.state.addHistory=this.state.addHistory.filter(entry=>entry?.moduleID!==moduleID);try{await this.removeApiComponent(moduleID||effectivePath,{recordHistory:false})}catch{}}async removeApiComponent(pathOrModuleId,options={}){const recordHistory=options.recordHistory!==false;if(typeof pathOrModuleId!=="string"||!pathOrModuleId){throw new this.SlothletError("INVALID_ARGUMENT",{argument:"pathOrModuleId",expected:"non-empty string",received:typeof pathOrModuleId,validationError:true})}let apiPath=null;let moduleID=null;if(this.slothlet.handlers.ownership){const candidateModuleID=pathOrModuleId.split(":")[0];const registeredModules=Array.from(this.slothlet.handlers.ownership.moduleToPath.keys());let matchingModule=null;for(let i=registeredModules.length-1;i>=0;i--){const candidate=registeredModules[i];if(candidate===candidateModuleID||candidate.startsWith(`${candidateModuleID}_`)){matchingModule=candidate;break}}if(matchingModule){moduleID=matchingModule}else{const owner=this.slothlet.handlers.ownership.getCurrentOwner(pathOrModuleId);if(owner){apiPath=pathOrModuleId;moduleID=owner.moduleID}else{return false}}}else{const isModuleId=!pathOrModuleId.includes(".");apiPath=isModuleId?null:pathOrModuleId;moduleID=isModuleId?pathOrModuleId.split(":")[0]:null}if(!this.slothlet||!this.slothlet.isLoaded){throw new this.SlothletError("INVALID_CONFIG_NOT_LOADED",{operation:"removeApi",validationError:true})}if(apiPath&&moduleID){const normalizedPath2=this.normalizeApiPath(apiPath).apiPath;const moduleIDKey=String(moduleID);const history=this.slothlet.handlers.ownership?.getPathHistory?.(normalizedPath2)||[];const ownershipResult2=this.slothlet.handlers.ownership?.removePath?.(normalizedPath2,moduleIDKey)||{action:"none",removedModuleId:null,restoreModuleId:null};const pathParts=this.normalizeApiPath(apiPath).parts;if(ownershipResult2.action==="delete"){await this.deletePath(this.slothlet.api,pathParts);await this.deletePath(this.slothlet.boundApi,pathParts);if(this.slothlet.handlers.metadata){const rootSegment=normalizedPath2.split(".")[0];this.slothlet.handlers.metadata.removeUserMetadataByApiPath(rootSegment)}if(this.slothlet.handlers.versionManager){const versionKey=this.slothlet.handlers.versionManager.getVersionKeyForModule(moduleIDKey);if(versionKey){this.slothlet.handlers.versionManager.unregisterVersion(versionKey.logicalPath,versionKey.versionTag)}if(this.slothlet.handlers.versionManager.hasDispatcher(normalizedPath2)){this.slothlet.handlers.versionManager.teardownDispatcher(normalizedPath2)}}this.state.operationHistory.push({type:"remove",apiPath:normalizedPath2});return true}if(ownershipResult2.action==="restore"){const restoredValue=this.slothlet.handlers.ownership?.getCurrentValue?.(normalizedPath2);const restoredModuleId=this.slothlet.handlers.ownership?.getCurrentOwner?.(normalizedPath2)?.moduleID;if(restoredValue!==void 0&&restoredModuleId){await this.setValueAtPath(this.slothlet.api,pathParts,restoredValue,{mutateExisting:true,allowOverwrite:true,collisionMode:"replace",moduleID:restoredModuleId});await this.setValueAtPath(this.slothlet.boundApi,pathParts,restoredValue,{mutateExisting:true,allowOverwrite:true,collisionMode:"replace",moduleID:restoredModuleId});this.state.operationHistory.push({type:"remove",apiPath:normalizedPath2});return true}await this.restoreApiPath(normalizedPath2,ownershipResult2.restoreModuleId);this.state.operationHistory.push({type:"remove",apiPath:normalizedPath2});return true}if(ownershipResult2.action==="none"&&history.length===0){await this.deletePath(this.slothlet.api,pathParts);await this.deletePath(this.slothlet.boundApi,pathParts);return true}return false}if(moduleID){const moduleIDKey=String(moduleID);const result=this.slothlet.handlers.ownership?.unregister?.(moduleIDKey)||{removed:[],rolledBack:[]};if(this.slothlet.handlers.versionManager){const versionKey=this.slothlet.handlers.versionManager.getVersionKeyForModule(moduleIDKey);if(versionKey){this.slothlet.handlers.versionManager.unregisterVersion(versionKey.logicalPath,versionKey.versionTag)}}const allPaths=[...result.removed,...result.rolledBack.map(r=>r.apiPath)];const uniquePaths=[...new Set(allPaths)];const pathsToDelete=[];const pathsToRollback=[];for(const path2 of uniquePaths){const currentOwner=this.slothlet.handlers.ownership?.getCurrentOwner?.(path2);const hasChildrenWithOtherOwners=uniquePaths.some(p=>{if(p===path2||!p.startsWith(path2+"."))return false;const childOwner=this.slothlet.handlers.ownership?.getCurrentOwner?.(p);return childOwner&&childOwner.moduleID!==moduleIDKey});if(currentOwner&¤tOwner.moduleID!==moduleIDKey){pathsToRollback.push({apiPath:path2,restoredTo:currentOwner.moduleID})}else if(!hasChildrenWithOtherOwners){pathsToDelete.push(path2)}}pathsToDelete.sort((a,b)=>{const depthA=(a.match(/\./g)||[]).length;const depthB=(b.match(/\./g)||[]).length;return depthB-depthA});for(const removedPath of pathsToDelete){const{parts:parts2}=this.normalizeApiPath(removedPath);await this.deletePath(this.slothlet.api,parts2);await this.deletePath(this.slothlet.boundApi,parts2);if(this.slothlet.handlers.metadata){const rootSegment=removedPath.split(".")[0];this.slothlet.handlers.metadata.removeUserMetadataByApiPath(rootSegment)}}if(pathsToDelete.length>0){const rootSegment=pathsToDelete[0].split(".")[0];if(rootSegment in this.slothlet.api){delete this.slothlet.api[rootSegment]}if(rootSegment in this.slothlet.boundApi){delete this.slothlet.boundApi[rootSegment]}}for(const rollback of pathsToRollback){const{parts:parts2}=this.normalizeApiPath(rollback.apiPath);const previousImpl=this.slothlet.handlers.ownership?.getCurrentValue?.(rollback.apiPath);if(previousImpl!==void 0){const existingWrapper=this.getValueAtPath(this.slothlet.api,parts2);const existingWrapperRaw=resolveWrapper(existingWrapper);if(existingWrapperRaw){existingWrapperRaw.___setImpl(previousImpl,rollback.restoredTo)}const existingBoundWrapper=this.getValueAtPath(this.slothlet.boundApi,parts2);const existingBoundWrapperRaw=resolveWrapper(existingBoundWrapper);if(existingBoundWrapperRaw){existingBoundWrapperRaw.___setImpl(previousImpl,rollback.restoredTo)}}}this.state.addHistory=this.state.addHistory.filter(entry=>String(entry.moduleID)!==moduleIDKey);if(this.slothlet.handlers.apiCacheManager){const deleted=this.slothlet.handlers.apiCacheManager.delete(moduleIDKey);if(deleted){this.slothlet.debug("cache",{key:"DEBUG_MODE_CACHE_DELETED_MODULE_REMOVED",moduleID:moduleIDKey})}}if(recordHistory&&pathsToDelete.length>0){const rootSegment=pathsToDelete[0].split(".")[0];this.state.operationHistory.push({type:"remove",apiPath:rootSegment})}return pathsToDelete.length>0||pathsToRollback.length>0}if(!apiPath){throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID",{apiPath,reason:translate("API_PATH_REASON_REQUIRED"),index:void 0,segment:void 0,validationError:true})}const{apiPath:normalizedPath,parts}=this.normalizeApiPath(apiPath);const ownershipResult=this.slothlet.handlers.ownership?.removePath?.(normalizedPath,null)||{action:"none",removedModuleId:null,restoreModuleId:null};const pathExists=this.getValueAtPath(this.slothlet.api,parts)!==void 0;if(ownershipResult.action==="none"){if(pathExists){await this.deletePath(this.slothlet.api,parts);await this.deletePath(this.slothlet.boundApi,parts);if(this.slothlet.handlers.metadata){this.slothlet.handlers.metadata.removeUserMetadataByApiPath(normalizedPath)}if(recordHistory){this.state.operationHistory.push({type:"remove",apiPath:normalizedPath})}return true}return false}if(ownershipResult.action==="delete"){await this.deletePath(this.slothlet.api,parts);await this.deletePath(this.slothlet.boundApi,parts);if(this.slothlet.handlers.metadata){this.slothlet.handlers.metadata.removeUserMetadataByApiPath(normalizedPath)}if(recordHistory){this.state.operationHistory.push({type:"remove",apiPath:normalizedPath})}return true}if(ownershipResult.action==="restore"){const restoredValue=this.slothlet.handlers.ownership?.getCurrentValue?.(normalizedPath);const restoredModuleId=this.slothlet.handlers.ownership?.getCurrentOwner?.(normalizedPath)?.moduleID;if(restoredValue!==void 0&&restoredModuleId){await this.setValueAtPath(this.slothlet.api,parts,restoredValue,{mutateExisting:true,allowOverwrite:true,collisionMode:"replace",moduleID:restoredModuleId});await this.setValueAtPath(this.slothlet.boundApi,parts,restoredValue,{mutateExisting:true,allowOverwrite:true,collisionMode:"replace",moduleID:restoredModuleId});if(recordHistory){this.state.operationHistory.push({type:"remove",apiPath:normalizedPath})}return true}await this.restoreApiPath(normalizedPath,ownershipResult.restoreModuleId);if(recordHistory){this.state.operationHistory.push({type:"remove",apiPath:normalizedPath})}return true}return false}async reloadApiComponent(params){const{apiPath,moduleID,options}=params||{};if(!this.slothlet||!this.slothlet.isLoaded){throw new this.SlothletError("INVALID_CONFIG_NOT_LOADED",{operation:"reloadApi",validationError:true})}if(moduleID){await this._reloadByModuleID(moduleID);return}if(apiPath){await this._reloadByApiPath(apiPath,options);return}throw new this.SlothletError("INVALID_ARGUMENT",{argument:"params",expected:"{ moduleID } or { apiPath }",received:params,validationError:true})}async _reloadByModuleID(moduleID,{forceReplace=true}={}){const cacheManager=this.slothlet.handlers.apiCacheManager;if(!cacheManager){throw new this.SlothletError("CACHE_MANAGER_NOT_AVAILABLE",{operation:"reload",validationError:true})}if(!cacheManager.has(moduleID)){throw new this.SlothletError("CACHE_NOT_FOUND",{moduleID,operation:"reload",validationError:true})}const oldEntry=cacheManager.get(moduleID);this.slothlet.debug("reload",{key:"DEBUG_MODE_RELOADING_MODULE_BY_ID",moduleID,endpoint:oldEntry.endpoint,folderPath:oldEntry.folderPath});const freshApi=await cacheManager.rebuildCache(moduleID);cacheManager.set(moduleID,{...oldEntry,api:freshApi,timestamp:Date.now()});this.slothlet.debug("reload",{key:"DEBUG_MODE_FRESH_API_KEYS_BEFORE_RESTORE",moduleID,endpoint:oldEntry.endpoint,freshApiKeys:Object.keys(freshApi||{})});await this._restoreApiTree(freshApi,oldEntry.endpoint,moduleID,oldEntry.collisionMode,forceReplace);this.slothlet.debug("reload",{key:"DEBUG_MODE_FRESH_API_KEYS_AFTER_RESTORE",moduleID,endpoint:oldEntry.endpoint,freshApiKeys:Object.keys(freshApi||{})});this.slothlet.debug("reload",{key:"DEBUG_MODE_MODULE_RELOAD_COMPLETE",moduleID});if(this.slothlet.handlers.versionManager){this.slothlet.handlers.versionManager.onVersionedModuleReload(moduleID)}}async _reloadByApiPath(apiPath,options={}){this.slothlet.debug("reload",{key:"DEBUG_MODE_RELOADING_BY_API_PATH",apiPath});const moduleIDsToReload=this._findAffectedCaches(apiPath);if(moduleIDsToReload.length===0){this.slothlet.debug("reload",{key:"DEBUG_MODE_NO_CACHES_ATTEMPTING_RESTORE",apiPath});if(apiPath!=="."&&apiPath!==""){await this.restoreApiPath(apiPath,"base")}return}const cacheManager=this.slothlet.handlers.apiCacheManager;moduleIDsToReload.sort((a,b)=>{const entryA=cacheManager.get(a);const entryB=cacheManager.get(b);if(entryA?.endpoint==="."&&entryB?.endpoint!==".")return-1;if(entryB?.endpoint==="."&&entryA?.endpoint!==".")return 1;const indexA=this.state.addHistory.findIndex(h=>h.moduleID===a);const indexB=this.state.addHistory.findIndex(h=>h.moduleID===b);return indexA-indexB});const endpointOrder=new Map;for(const moduleID of moduleIDsToReload){const entry=cacheManager.get(moduleID);const ep=entry?.endpoint??".";if(!endpointOrder.has(ep))endpointOrder.set(ep,[]);endpointOrder.get(ep).push(moduleID)}for(const[,moduleIDs]of endpointOrder){for(let i=0;i<moduleIDs.length;i++){await this._reloadByModuleID(moduleIDs[i],{forceReplace:i===0})}}const reloadMetadata=options?.metadata;if(reloadMetadata&&typeof reloadMetadata==="object"&&Object.keys(reloadMetadata).length>0){if(this.slothlet.handlers.metadata){const targetPath=apiPath==="."?null:apiPath.split(".")[0];if(targetPath){this.slothlet.handlers.metadata.registerUserMetadata(targetPath,reloadMetadata)}}}this.slothlet.debug("reload",{key:"DEBUG_MODE_API_PATH_RELOAD_COMPLETE",apiPath,reloadedModules:moduleIDsToReload.length,loadOrder:moduleIDsToReload})}_findAffectedCaches(apiPath){const cacheManager=this.slothlet.handlers.apiCacheManager;if(!cacheManager)return[];const allModuleIDs=cacheManager.getAllModuleIDs();if(apiPath==="."||apiPath===""||apiPath==null){const baseModules=[];for(const moduleID of allModuleIDs){const entry=cacheManager.get(moduleID);if(entry&&entry.endpoint==="."){baseModules.push(moduleID)}}return baseModules}const exactMatches=[];for(const moduleID of allModuleIDs){const entry=cacheManager.get(moduleID);if(entry&&entry.endpoint===apiPath){exactMatches.push(moduleID)}}if(exactMatches.length>0)return exactMatches;const children=[];const pathPrefix=apiPath+".";for(const moduleID of allModuleIDs){const entry=cacheManager.get(moduleID);if(entry?.endpoint?.startsWith(pathPrefix)){children.push(moduleID)}}if(children.length>0)return children;const ownership=this.slothlet.handlers.ownership;const history=ownership?.getPathHistory?.(apiPath);if(history&&history.length>0){const owned=[];for(const{moduleID}of history){if(cacheManager.has(moduleID)){owned.push(moduleID)}}if(owned.length>0)return owned}let bestMatch=null;let bestLength=-1;for(const moduleID of allModuleIDs){const entry=cacheManager.get(moduleID);if(!entry?.endpoint)continue;const ep=entry.endpoint;if(ep==="."||apiPath.startsWith(ep+".")){if(ep.length>bestLength){bestLength=ep.length;bestMatch=moduleID}}}if(bestMatch)return[bestMatch];return[]}_collectCustomProperties(existingProxy,freshApi){const customProps={};if(!existingProxy||typeof existingProxy!=="object"&&typeof existingProxy!=="function"){return customProps}const wrapper=resolveWrapper(existingProxy);if(!wrapper){return customProps}const freshKeys=new Set(freshApi?Object.keys(freshApi):[]);const ownKeys=Object.keys(wrapper).filter(k=>!ComponentBase.INTERNAL_KEYS.has(k));for(const key of ownKeys){try{const val=wrapper[key];if(val&&(typeof val==="object"||typeof val==="function")&&resolveWrapper(val)){continue}if(!freshKeys.has(key)){customProps[key]=val}else{customProps[key]=val}}catch{}}return customProps}_restoreCustomProperties(proxy,customProps){if(!proxy||!customProps||typeof customProps!=="object"){return}for(const[key,value]of Object.entries(customProps)){try{proxy[key]=value}catch{}}}async _restoreApiTree(freshApi,endpoint,moduleID,collisionMode,forceReplace=true){if(!freshApi||typeof freshApi!=="object"&&typeof freshApi!=="function"){return}const parts=endpoint==="."?[]:endpoint.split(".");if(parts.length===0){for(const key of Object.keys(freshApi)){if(typeof key==="string"&&(key.startsWith("_")||key.startsWith("__")))continue;if(key==="slothlet"||key==="shutdown"||key==="destroy")continue;const existingAtKey=this.slothlet.api[key];const freshValue=freshApi[key];if(existingAtKey&&resolveWrapper(existingAtKey)!==null){const customProps=this._collectCustomProperties(existingAtKey,freshValue);const freshWrapper=resolveWrapper(freshValue);const isLazyFresh=freshWrapper&&freshWrapper.____slothletInternal.mode==="lazy"&&!freshWrapper.____slothletInternal.state.materialized&&typeof freshWrapper.____slothletInternal.materializeFunc==="function";this.slothlet.debug("reload",{key:"DEBUG_MODE_RESTORE_ROOT_KEY_INSPECT",rootKey:key,hasFreshWrapper:!!freshWrapper,freshMode:freshWrapper?.____slothletInternal.mode,freshMaterialized:freshWrapper?.____slothletInternal.state?.materialized,hasMaterializeFunc:typeof freshWrapper?.____slothletInternal.materializeFunc==="function",isLazyFresh,existingMaterialized:resolveWrapper(existingAtKey)?.____slothletInternal?.state?.materialized});if(isLazyFresh){resolveWrapper(existingAtKey).___resetLazy(freshWrapper.____slothletInternal.materializeFunc);this._restoreCustomProperties(existingAtKey,customProps);this.slothlet.debug("reload",{key:"DEBUG_MODE_ROOT_KEY_RESET_LAZY",rootKey:key,restoredCustomProps:Object.keys(customProps)})}else{let implForReload;if(freshValue&&resolveWrapper(freshValue)!==null){implForReload=freshWrapper?UnifiedWrapper._extractFullImpl(freshWrapper):freshValue}else{implForReload=freshValue}if(typeof implForReload==="function"){const extracted={};for(const k of Object.keys(implForReload)){extracted[k]=implForReload[k]}implForReload=extracted}const wrapper=resolveWrapper(existingAtKey);const originalCollisionMode=wrapper?wrapper.____slothletInternal.state.collisionMode:null;if(forceReplace&&wrapper){wrapper.____slothletInternal.state.collisionMode="replace"}if(wrapper&&originalCollisionMode!==null){wrapper.____slothletInternal.state.collisionMode=originalCollisionMode}this._restoreCustomProperties(existingAtKey,customProps);this.slothlet.debug("reload",{key:"DEBUG_MODE_ROOT_KEY_UPDATED_SETIMPL",rootKey:key,restoredCustomProps:Object.keys(customProps)})}}else if(existingAtKey===void 0){const cacheManager=this.slothlet.handlers.apiCacheManager;const cacheEntry=cacheManager.get(moduleID);const resolvedFolderPath=cacheEntry?.folderPath||"";await this.setValueAtPath(this.slothlet.api,[key],freshValue,{mutateExisting:true,collisionMode,moduleID,sourceFolder:resolvedFolderPath});if(this.slothlet.boundApi){await this.setValueAtPath(this.slothlet.boundApi,[key],freshValue,{mutateExisting:true,collisionMode,moduleID,sourceFolder:resolvedFolderPath})}}}}else{const existing=this.getValueAtPath(this.slothlet.api,parts);this.slothlet.debug("reload",{key:"DEBUG_MODE_RESTORE_NESTED_PATH",endpoint,moduleID,partsPath:parts.join("."),existingFound:!!existing,hasSetImpl:existing?resolveWrapper(existing)!==null:false,freshApiKeys:Object.keys(freshApi||{})});if(existing&&resolveWrapper(existing)!==null){const customProps=this._collectCustomProperties(existing,freshApi);const wrapper=resolveWrapper(existing);const originalCollisionMode=wrapper?wrapper.____slothletInternal.state.collisionMode:null;if(forceReplace&&wrapper){wrapper.____slothletInternal.state.collisionMode="replace";this.slothlet.debug("reload",{key:"DEBUG_MODE_RESTORE_FORCING_REPLACE",endpoint,originalCollisionMode,wrapperApiPath:wrapper.____slothletInternal.apiPath})}let implForReload;if(resolveWrapper(freshApi)!==null){const freshWrapper=resolveWrapper(freshApi);implForReload=freshWrapper?UnifiedWrapper._extractFullImpl(freshWrapper):freshApi}else if(typeof freshApi==="function"){implForReload={};for(const key of Object.keys(freshApi)){implForReload[key]=freshApi[key]}}else{implForReload=freshApi}if(parts.length>0&&implForReload&&typeof implForReload==="object"){const lastEndpointPart=parts[parts.length-1];if(lastEndpointPart&&Object.prototype.hasOwnProperty.call(implForReload,lastEndpointPart)){const dupValue=implForReload[lastEndpointPart];const dupWrapperForDedup=resolveWrapper(dupValue);if(dupWrapperForDedup){const hoisted={};for(const k of Object.keys(implForReload)){if(k!==lastEndpointPart)hoisted[k]=implForReload[k]}for(const k of Object.keys(dupWrapperForDedup).filter(k2=>!k2.startsWith("_")&&!k2.startsWith("__"))){hoisted[k]=dupWrapperForDedup[k]}implForReload=hoisted}}}if(implForReload&&typeof implForReload==="object"){for(const key of Object.keys(implForReload)){const val=implForReload[key];if(resolveWrapper(val)!==null){const childWrapper=resolveWrapper(val);if(childWrapper.____slothletInternal.state.materialized){implForReload[key]=UnifiedWrapper._extractFullImpl(childWrapper)}}}}resolveWrapper(existing).___setImpl(implForReload,moduleID);if(wrapper&&originalCollisionMode!==null){wrapper.____slothletInternal.state.collisionMode=originalCollisionMode}this._restoreCustomProperties(existing,customProps);this.slothlet.debug("reload",{key:"DEBUG_MODE_UPDATED_WRAPPER_IMPL",endpoint,moduleID,forcedReplaceMode:true,restoredCustomProps:Object.keys(customProps)})}else{const cacheManager=this.slothlet.handlers.apiCacheManager;const cacheEntry=cacheManager.get(moduleID);const resolvedFolderPath=cacheEntry?.folderPath||"";let implForContainer=freshApi;if(typeof freshApi==="function"){implForContainer={};for(const key of Object.keys(freshApi)){implForContainer[key]=freshApi[key]}}const containerWrapper=new UnifiedWrapper(this.slothlet,{apiPath:endpoint,mode:this.____config.mode,moduleID,filePath:resolvedFolderPath,sourceFolder:resolvedFolderPath});containerWrapper.___setImpl(implForContainer,moduleID);const apiToSet=containerWrapper.createProxy();await this.setValueAtPath(this.slothlet.api,parts,apiToSet,{mutateExisting:true,collisionMode,moduleID,sourceFolder:resolvedFolderPath});if(this.slothlet.boundApi){await this.setValueAtPath(this.slothlet.boundApi,parts,apiToSet,{mutateExisting:true,collisionMode,moduleID,sourceFolder:resolvedFolderPath})}this.slothlet.debug("reload",{key:"DEBUG_MODE_CREATED_NEW_WRAPPER_UNEXPECTED",endpoint,moduleID})}}}}export{ApiManager};
|
|
17
|
+
import fs from"node:fs/promises";import path from"node:path";import{translate}from"@cldmv/slothlet/i18n";import{ComponentBase}from"@cldmv/slothlet/factories/component-base";import{UnifiedWrapper,resolveWrapper}from"@cldmv/slothlet/handlers/unified-wrapper";class ApiManager extends ComponentBase{static slothletProperty="apiManager";constructor(slothlet){super(slothlet);this.state={addHistory:[],initialConfig:slothlet?.config||null,operationHistory:[]}}normalizeApiPath(apiPath){if(apiPath===""||apiPath===null||apiPath===void 0){return{apiPath:"",parts:[]}}if(Array.isArray(apiPath)){if(apiPath.length===0){return{apiPath:"",parts:[]}}for(let i=0;i<apiPath.length;i++){if(typeof apiPath[i]!=="string"){throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID",{apiPath,segment:apiPath[i],index:i,reason:translate("API_PATH_REASON_ARRAY_ELEMENTS"),validationError:true})}if(apiPath[i].trim()===""){throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID",{apiPath,segment:apiPath[i],index:i,reason:translate("API_PATH_REASON_ARRAY_EMPTY_SEGMENTS"),validationError:true})}}if(apiPath[0]==="slothlet"||apiPath.length===1&&(apiPath[0]==="shutdown"||apiPath[0]==="destroy")){throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID",{apiPath,reason:translate("API_PATH_REASON_RESERVED_NAME"),index:void 0,segment:void 0,validationError:true})}return{apiPath:apiPath.join("."),parts:apiPath}}if(typeof apiPath!=="string"){throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID",{apiPath,reason:translate("API_PATH_REASON_INVALID_TYPE"),index:void 0,segment:void 0,validationError:true})}const normalized=apiPath.trim();if(normalized===""){return{apiPath:"",parts:[]}}const parts=normalized.split(".");if(parts.length===0||parts.some(part=>part.trim()==="")){throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID",{apiPath:normalized,reason:translate("API_PATH_REASON_EMPTY_SEGMENTS"),index:void 0,segment:void 0,validationError:true})}if(parts[0]==="slothlet"||normalized==="shutdown"||normalized==="destroy"){throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID",{apiPath:normalized,reason:translate("API_PATH_REASON_RESERVED_NAME"),index:void 0,segment:void 0,validationError:true})}return{apiPath:normalized,parts}}async resolvePath(inputPath){if(!inputPath||typeof inputPath!=="string"){throw new this.SlothletError("INVALID_CONFIG_DIR_INVALID",{dir:inputPath,validationError:true})}const resolvedPath=this.slothlet.helpers.resolver.resolvePathFromCaller(inputPath);try{const stats=await fs.stat(resolvedPath);return{resolvedPath,isDirectory:stats.isDirectory(),isFile:stats.isFile()}}catch(error){if(error instanceof this.SlothletError){throw error}throw new this.SlothletError("INVALID_CONFIG_DIR_INVALID",{dir:resolvedPath,validationError:true})}}async resolveFolderPath(folderPath){if(!folderPath||typeof folderPath!=="string"){throw new this.SlothletError("INVALID_CONFIG_DIR_INVALID",{dir:folderPath,validationError:true})}const resolvedPath=this.slothlet.helpers.resolver.resolvePathFromCaller(folderPath);try{const stats=await fs.stat(resolvedPath);if(!stats.isDirectory()){throw new this.SlothletError("INVALID_CONFIG_DIR_INVALID",{dir:resolvedPath,validationError:true})}}catch(error){if(error instanceof this.SlothletError){throw error}throw new this.SlothletError("INVALID_CONFIG_DIR_INVALID",{dir:resolvedPath,validationError:true})}return resolvedPath}buildDefaultModuleId(apiPath,____resolvedFolderPath){const randomSuffix=Math.random().toString(36).substring(2,8);const prefix=apiPath||"auto";return`${prefix}_${randomSuffix}`}getValueAtPath(root,parts){let current=root;for(const part of parts){if(!current||typeof current!=="object"&&typeof current!=="function"){return void 0}current=current[part]}return current}ensureParentPath(root,parts,options={}){const{moduleID,sourceFolder}=options;let current=root;for(let i=0;i<parts.length-1;i+=1){const part=parts[i];const next=current[part];if(next===void 0){const containerPath=parts.slice(0,i+1).join(".");const containerWrapper=new UnifiedWrapper(this.slothlet,{mode:this.____config.mode,apiPath:containerPath,moduleID,sourceFolder});containerWrapper.___setImpl({},moduleID);current[part]=containerWrapper.createProxy();current=current[part];continue}if(next&&(typeof next==="object"||typeof next==="function")){current=next;continue}throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID",{apiPath:parts.slice(0,i+1).join("."),reason:translate("API_PATH_REASON_NOT_TRAVERSABLE"),index:void 0,segment:void 0,validationError:true})}return current}setOwnedProperty(apiPath,value,callerWrapper){const{parts}=this.normalizeApiPath(apiPath);if(parts.length===0){throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID",{apiPath:String(apiPath??""),reason:translate("API_PATH_REASON_EMPTY_SEGMENTS"),index:void 0,segment:void 0,validationError:true})}const RESERVED=new Set(["__proto__","prototype","constructor"]);for(const segment of parts){if(RESERVED.has(segment)){throw new this.SlothletError("LOOSE_SET_RESERVED_KEY",{apiPath:parts.join("."),segment,validationError:true})}}const callerModuleID=callerWrapper?.____slothletInternal?.moduleID??null;const callerWrapperPath=callerWrapper?.____slothletInternal?.apiPath??null;let ownedRoot=null;if(callerModuleID&&this.slothlet.handlers?.ownership?.getModuleEndpoint){const endpoint=this.slothlet.handlers.ownership.getModuleEndpoint(callerModuleID);if(endpoint!=null){ownedRoot=endpoint}}if(ownedRoot===null)ownedRoot=".";if(ownedRoot&&ownedRoot!=="."&&ownedRoot!==""){const ownedParts=String(ownedRoot).split(".").filter(Boolean);const isUnderOwn=ownedParts.length<=parts.length&&ownedParts.every((seg,i)=>parts[i]===seg);if(!isUnderOwn){const callerApiPath=callerWrapperPath??ownedRoot;throw new this.SlothletError("LOOSE_SET_NOT_OWNED",{apiPath:parts.join("."),callerApiPath,validationError:true})}}const callerSourceFolder=callerWrapper?.____slothletInternal?.sourceFolder??null;const root=this.slothlet.boundApi;const parent=this.ensureParentPath(root,parts,{moduleID:callerModuleID,sourceFolder:callerSourceFolder});const finalKey=parts[parts.length-1];const isWrappable=typeof value==="function"||value!==null&&typeof value==="object";if(isWrappable){const wrapper=new UnifiedWrapper(this.slothlet,{mode:this.____config.mode,apiPath:parts.join("."),moduleID:callerModuleID,isCallable:typeof value==="function",sourceFolder:callerSourceFolder});wrapper.___setImpl(value,callerModuleID);parent[finalKey]=wrapper.createProxy()}else{parent[finalKey]=value}}isWrapperProxy(value){return!!(value&&(typeof value==="object"||typeof value==="function")&&resolveWrapper(value)!==null)}async syncWrapper(existingProxy,nextProxy,config,collisionMode="replace",moduleID=null){if(config?.debug?.api){this.slothlet.debug("api",{key:"DEBUG_MODE_SYNC_WRAPPER_ENTRY_EXISTING",apiPath:resolveWrapper(existingProxy)?.apiPath});this.slothlet.debug("api",{key:"DEBUG_MODE_SYNC_WRAPPER_ENTRY_NEXT",apiPath:resolveWrapper(nextProxy)?.apiPath})}if(!this.isWrapperProxy(existingProxy)||!this.isWrapperProxy(nextProxy)){return false}const existingWrapper=resolveWrapper(existingProxy)??existingProxy;const nextWrapper=resolveWrapper(nextProxy)??nextProxy;if(config?.debug?.api){this.slothlet.debug("api",{key:"DEBUG_MODE_SYNC_WRAPPER_EXISTING",apiPath:existingWrapper.apiPath});this.slothlet.debug("api",{key:"DEBUG_MODE_SYNC_WRAPPER_NEXT",apiPath:nextWrapper.apiPath})}if(nextWrapper.____slothletInternal.materializeFunc&&collisionMode!=="merge"){existingWrapper.____slothletInternal.materializeFunc=nextWrapper.____slothletInternal.materializeFunc}const existingChildKeys=Object.keys(existingWrapper).filter(k=>!k.startsWith("_")&&!k.startsWith("__"));const nextChildKeys=Object.keys(nextWrapper).filter(k=>!k.startsWith("_")&&!k.startsWith("__"));if(config?.debug?.api){this.slothlet.debug("api",{key:"DEBUG_MODE_SYNC_WRAPPER_BEFORE_MERGE",existingCacheSize:existingChildKeys.length,nextCacheSize:nextChildKeys.length});this.slothlet.debug("api",{key:"DEBUG_MODE_SYNC_WRAPPER_NEXT_IMPL_KEYS",implKeys:Object.keys(nextWrapper.____slothletInternal.impl||{})});this.slothlet.debug("api",{key:"DEBUG_MODE_SYNC_WRAPPER_NEXT_CHILDCACHE_KEYS",childCacheKeys:nextChildKeys})}if(collisionMode==="replace"){if(existingWrapper.___setImpl&&nextWrapper.____slothletInternal.impl!==void 0){existingWrapper.___setImpl(nextWrapper.____slothletInternal.impl,moduleID)}else if(nextWrapper.____slothletInternal.impl===void 0){existingWrapper.____slothletInternal.impl=null}else{if(nextWrapper.____slothletInternal.impl!==void 0){existingWrapper.____slothletInternal.impl=nextWrapper.____slothletInternal.impl;if(typeof nextWrapper.____slothletInternal.impl==="function"||nextWrapper.____slothletInternal.impl&&typeof nextWrapper.____slothletInternal.impl.default==="function"){existingWrapper.isCallable=true}}}for(const key of existingChildKeys){delete existingWrapper[key]}existingWrapper.___adoptImplChildren();for(const key of nextChildKeys){const childValue=nextWrapper[key];Object.defineProperty(existingWrapper,key,{value:childValue,writable:false,enumerable:true,configurable:true})}}else if(collisionMode==="merge"){for(const key of nextChildKeys){const isInternal=typeof key==="string"&&(key.startsWith("_")||key.startsWith("__"));if(!isInternal&&!Object.prototype.hasOwnProperty.call(existingWrapper,key)){const childValue=nextWrapper[key];Object.defineProperty(existingWrapper,key,{value:childValue,writable:false,enumerable:true,configurable:true})}else if(!isInternal){const existingChild=existingWrapper[key];const nextChild=nextWrapper[key];if(this.isWrapperProxy(existingChild)&&this.isWrapperProxy(nextChild)){const syncWrapper_nextChildWrapper=resolveWrapper(nextChild)??nextChild;const syncWrapper_hasGrandChildren=Object.keys(syncWrapper_nextChildWrapper).some(k=>!k.startsWith("_")&&!k.startsWith("__"));if(syncWrapper_hasGrandChildren){await this.syncWrapper(existingChild,nextChild,config,collisionMode,moduleID)}}}}}else if(collisionMode==="merge-replace"){for(const key of nextChildKeys){const childValue=nextWrapper[key];const isInternal=typeof key==="string"&&(key.startsWith("_")||key.startsWith("__"));if(!isInternal&&Object.prototype.hasOwnProperty.call(existingWrapper,key)){const existingChild=existingWrapper[key];if(this.isWrapperProxy(existingChild)&&this.isWrapperProxy(childValue)){await this.syncWrapper(existingChild,childValue,config,collisionMode,moduleID)}else{delete existingWrapper[key];Object.defineProperty(existingWrapper,key,{value:childValue,writable:false,enumerable:true,configurable:true})}}else if(!isInternal){Object.defineProperty(existingWrapper,key,{value:childValue,writable:false,enumerable:true,configurable:true})}}}if(existingWrapper.____slothletInternal.state){const isActuallyMaterialized=existingWrapper.____slothletInternal.impl&&typeof existingWrapper.____slothletInternal.impl!=="function";existingWrapper.____slothletInternal.state.materialized=isActuallyMaterialized;existingWrapper.____slothletInternal.state.inFlight=false}return true}async mutateApiValue(existingValue,nextValue,options,config){if(config?.debug?.api){this.slothlet.debug("api",{key:"DEBUG_MODE_MUTATE_API_VALUE_CALLED",existingType:typeof existingValue,nextType:typeof nextValue});this.slothlet.debug("api",{key:"DEBUG_MODE_MUTATE_API_VALUE_WRAPPER_STATUS",existingIsWrapper:this.isWrapperProxy(existingValue),nextIsWrapper:this.isWrapperProxy(nextValue)});this.slothlet.debug("api",{key:"DEBUG_MODE_MUTATE_API_VALUE_NEXT_VALUE",nextValue});this.slothlet.debug("api",{key:"DEBUG_MODE_MUTATE_API_VALUE_NEXT_VALUE_KEYS",nextValueKeys:nextValue?Object.keys(nextValue):[]})}if(existingValue===nextValue){return}if(this.isWrapperProxy(existingValue)&&this.isWrapperProxy(nextValue)){if(config?.debug?.api){this.slothlet.debug("api",{key:"DEBUG_MODE_MUTATE_API_VALUE_SYNC_WRAPPERS"})}await this.syncWrapper(existingValue,nextValue,config,options.collisionMode,options.moduleID);return}if(this.isWrapperProxy(existingValue)&&!this.isWrapperProxy(nextValue)){const nextIsObjectLike=nextValue&&(typeof nextValue==="object"||typeof nextValue==="function");const nextHasKeys=nextIsObjectLike&&Object.keys(nextValue).length>0;if(nextHasKeys){if(config?.debug?.api){this.slothlet.debug("api",{key:"DEBUG_MODE_MUTATE_API_VALUE_MERGE_INTO_WRAPPER"});this.slothlet.debug("api",{key:"DEBUG_MODE_MUTATE_API_VALUE_MERGE_KEYS",keys:Object.keys(nextValue)})}await this.slothlet.builders.apiAssignment.mergeApiObjects(existingValue,nextValue,{removeMissing:options.removeMissing,mutateExisting:true,allowOverwrite:true,syncWrapper:this.syncWrapper.bind(this),collisionMode:options.collisionMode,moduleID:options.moduleID});return}const existingValueRaw=resolveWrapper(existingValue);if(existingValueRaw!==null){if(config?.debug?.api){this.slothlet.debug("api",{key:"DEBUG_MODE_MUTATE_API_VALUE_SETIMPL_FALLBACK"})}existingValueRaw.___setImpl(resolveWrapper(nextValue)?.__impl??nextValue);return}}if(existingValue&&typeof existingValue==="object"&&nextValue&&typeof nextValue==="object"){await this.slothlet.builders.apiAssignment.mergeApiObjects(existingValue,nextValue,{removeMissing:options.removeMissing,mutateExisting:true,allowOverwrite:true,syncWrapper:this.syncWrapper.bind(this),collisionMode:options.collisionMode,moduleID:options.moduleID});return existingValue}return nextValue}async setValueAtPath(root,parts,value,options){const parent=this.ensureParentPath(root,parts,{moduleID:options.moduleID,sourceFolder:options.sourceFolder});const finalKey=parts[parts.length-1];const existing=parent?parent[finalKey]:void 0;const collisionMode=options.collisionMode||"merge";const moduleID=options.moduleID;this.slothlet.debug("api",{key:"DEBUG_MODE_SET_VALUE_AT_PATH",finalKey,existingType:typeof existing,valueType:typeof value,collisionMode,options});if(existing!==void 0){if(collisionMode==="error"){throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID",{apiPath:parts.join("."),reason:translate("API_PATH_REASON_COLLISION_ERROR"),index:void 0,segment:void 0,validationError:true})}if(collisionMode==="skip"){this.slothlet.debug("api",{key:"DEBUG_MODE_SET_VALUE_AT_PATH_SKIP_COLLISION",path:parts.join("."),mode:"skip"});return false}if(collisionMode==="warn"){if(this.slothlet&&!this.____config?.silent){new this.SlothletWarning("WARNING_HOT_RELOAD_PATH_COLLISION",{apiPath:parts.join(".")})}return false}if(collisionMode==="replace"){const existingIsObject=typeof existing==="object"||typeof existing==="function";const valueIsObject=typeof value==="object"||typeof value==="function";if(existingIsObject&&valueIsObject){this.slothlet.debug("api",{key:"DEBUG_MODE_SET_VALUE_AT_PATH_REPLACE_MERGE",path:parts.join("."),mode:"replace"});await this.mutateApiValue(existing,value,{removeMissing:false,allowOverwrite:true,collisionMode:"replace",moduleID},this.____config);return true}else{parent[finalKey]=value;return true}}if(collisionMode==="merge"||collisionMode==="merge-replace"){const existingIsObject=typeof existing==="object"||typeof existing==="function";const valueIsObject=typeof value==="object"||typeof value==="function";if(existingIsObject&&valueIsObject){this.slothlet.debug("api",{key:"DEBUG_MODE_SET_VALUE_AT_PATH_MERGE_PROPS",mode:collisionMode});await this.mutateApiValue(existing,value,{removeMissing:false,allowOverwrite:true,collisionMode},this.____config);return true}else{if(this.slothlet&&!this.____config?.silent){new this.SlothletWarning("WARNING_HOT_RELOAD_MERGE_PRIMITIVES",{apiPath:parts.join(".")})}return false}}}this.slothlet.debug("api",{key:"DEBUG_MODE_SET_VALUE_AT_PATH_ASSIGN",finalKey});parent[finalKey]=value;return true}async deletePath(root,parts){let current=root;const stack=[];for(const part of parts.slice(0,-1)){if(!current||typeof current!=="object"&&typeof current!=="function"){return false}stack.push({parent:current,key:part});current=current[part]}const finalKey=parts[parts.length-1];if(!current||typeof current!=="object"&&typeof current!=="function"){return false}if(!Object.prototype.hasOwnProperty.call(current,finalKey)){return false}const removedImpl=current[finalKey];const apiPath=parts.join(".");if(removedImpl&&this.slothlet.handlers?.lifecycle){const metadata=this.slothlet.handlers.metadata?.getMetadata?.(removedImpl);await this.slothlet.handlers.lifecycle.emit("impl:removed",{apiPath,impl:removedImpl,source:"removal",moduleID:metadata?.moduleID,filePath:metadata?.filePath,sourceFolder:metadata?.sourceFolder})}if(resolveWrapper(current)){const wrapper=resolveWrapper(current);const isInternal=typeof finalKey==="string"&&(finalKey.startsWith("_")||finalKey.startsWith("__"));if(!isInternal&&finalKey in wrapper){delete wrapper[finalKey]}if(wrapper.____slothletInternal.impl&&typeof wrapper.____slothletInternal.impl==="object"){delete wrapper.____slothletInternal.impl[finalKey]}}delete current[finalKey];if(removedImpl&&(typeof removedImpl==="object"||typeof removedImpl==="function")){if(resolveWrapper(removedImpl)){const wrapper=resolveWrapper(removedImpl);if(wrapper.____slothletInternal.impl!==void 0){wrapper.____slothletInternal.impl=null}const childKeys=Object.keys(wrapper).filter(k=>!k.startsWith("_")&&!k.startsWith("__"));for(const key of childKeys){delete wrapper[key]}if(wrapper.____slothletInternal.state){wrapper.____slothletInternal.state.materialized=false;wrapper.____slothletInternal.state.inFlight=false}}}if(this.slothlet.handlers?.metadata){const rootSegment=apiPath.split(".")[0];this.slothlet.handlers.metadata.removeUserMetadataByApiPath(rootSegment)}for(let i=stack.length-1;i>=0;i-=1){const{parent,key}=stack[i];const value=parent[key];if(value&&(typeof value==="object"||typeof value==="function")&&Object.keys(value).length===0){delete parent[key]}}return true}async restoreApiPath(apiPath,moduleID){const normalizedModuleId=moduleID||null;const historyEntry=this.state.addHistory.slice().reverse().find(entry=>entry.apiPath===apiPath&&(normalizedModuleId?entry.moduleID===normalizedModuleId:true));if(historyEntry){await this.addApiComponent({apiPath:historyEntry.apiPath,folderPath:historyEntry.folderPath,options:{...historyEntry.options,metadata:historyEntry.metadata,mutateExisting:true,forceOverwrite:true,collisionMode:"replace",recordHistory:false}});return}if(normalizedModuleId==="base"||normalizedModuleId==="core"){const baseApi=await this.slothlet.builders.builder.buildAPI({dir:this.____config.dir,mode:this.____config.mode,moduleID:"base"});const{parts}=this.normalizeApiPath(apiPath);let baseValue=this.getValueAtPath(baseApi,parts);if(baseValue===void 0){await this.deletePath(this.slothlet.api,parts);await this.deletePath(this.slothlet.boundApi,parts);return}const baseValueRaw=resolveWrapper(baseValue);if(baseValue&&baseValueRaw!==null){baseValue=baseValueRaw.__impl}await this.setValueAtPath(this.slothlet.api,parts,baseValue,{mutateExisting:true,allowOverwrite:true,collisionMode:"replace",moduleID:normalizedModuleId});await this.setValueAtPath(this.slothlet.boundApi,parts,baseValue,{mutateExisting:true,allowOverwrite:true,collisionMode:"replace",moduleID:normalizedModuleId})}}#normalizePermissionShorthandEntry(entry){const getValueType=value=>{if(value===null)return"null";if(Array.isArray(value))return"array";return typeof value};if(typeof entry==="string"){return{target:entry,condition:void 0}}if(!entry||typeof entry!=="object"||Array.isArray(entry)){throw new this.SlothletError("INVALID_PERMISSION_RULE",{reason:translate("PERM_RULE_NOT_OBJECT"),received:getValueType(entry),validationError:true})}if(typeof entry.target!=="string"||!entry.target){throw new this.SlothletError("INVALID_PERMISSION_RULE",{reason:translate("PERM_RULE_TARGET_REQUIRED"),received:typeof entry.target,validationError:true})}return{target:entry.target,condition:entry.condition}}async addApiComponent(params){const{apiPath,folderPath,options={},versionConfig=null}=params||{};if(Array.isArray(folderPath)){const moduleIDs=[];for(const singlePath of folderPath){const moduleID2=await this.addApiComponent({apiPath,folderPath:singlePath,options,versionConfig});moduleIDs.push(moduleID2)}return moduleIDs}const{metadata={},...restOptions}=options;if(!this.slothlet||!this.slothlet.isLoaded){throw new this.SlothletError("INVALID_CONFIG_NOT_LOADED",{operation:"addApi",validationError:true})}const{apiPath:normalizedPath,parts}=this.normalizeApiPath(apiPath);let effectivePath=normalizedPath;let effectiveParts=parts;if(versionConfig?.version!==void 0&&versionConfig?.version!==null){if(normalizedPath===""){throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID",{apiPath,reason:translate("API_PATH_REASON_VERSIONED_ROOT"),index:void 0,segment:void 0,validationError:true})}if(typeof versionConfig.version!=="string"||!String(versionConfig.version).trim()){throw new this.SlothletError("INVALID_CONFIG_VERSION_TAG",{received:versionConfig.version,validationError:true})}const versionTag=String(versionConfig.version).trim();effectiveParts=[versionTag,...parts];effectivePath=effectiveParts.join(".")}const{resolvedPath,isDirectory,isFile}=await this.resolvePath(folderPath);if(!isDirectory&&!isFile){throw new this.SlothletError("INVALID_CONFIG_PATH_TYPE",{path:resolvedPath,validationError:true})}if(isFile){const ext=path.extname(resolvedPath);if(![".mjs",".cjs",".js"].includes(ext)){throw new this.SlothletError("INVALID_CONFIG_FILE_TYPE",{path:resolvedPath,extension:ext,validationError:true})}}const resolvedFolderPath=resolvedPath;let collisionMode;if(restOptions.forceOverwrite){collisionMode="replace"}else{collisionMode=restOptions.collisionMode||this.____config.api?.collision?.api||"error"}const mutateExisting=!!(restOptions.mutateExisting||collisionMode==="merge");const moduleID=restOptions.moduleID?String(restOptions.moduleID):this.buildDefaultModuleId(normalizedPath,resolvedFolderPath);if(restOptions.forceOverwrite&&!moduleID){throw new this.SlothletError("INVALID_CONFIG_FORCE_OVERWRITE_REQUIRES_MODULE_ID",{apiPath:normalizedPath,validationError:true})}let dirForBuild=resolvedFolderPath;let fileFilter=null;if(isFile){dirForBuild=path.dirname(resolvedFolderPath);const fileName=path.basename(resolvedFolderPath);fileFilter=file=>file===fileName}const newApi=await this.slothlet.builders.builder.buildAPI({dir:dirForBuild,mode:this.____config.mode,apiPathPrefix:effectivePath,collisionContext:"addApi",moduleID,collisionMode,fileFilter});if(this.slothlet.handlers.apiCacheManager){this.slothlet.handlers.apiCacheManager.set(moduleID,{endpoint:effectivePath,moduleID,api:newApi,folderPath:resolvedFolderPath,mode:this.____config.mode,sanitizeOptions:this.____config.sanitize||{},collisionMode,config:{...this.____config},timestamp:Date.now()})}this.slothlet.debug("api",{key:"DEBUG_MODE_ADD_API_COMPONENT_BUILD_RETURN",topLevelKeys:Object.keys(newApi),dottedKeys:Object.keys(newApi).filter(k=>k.includes(".")),wrappers:Object.keys(newApi).filter(k=>resolveWrapper(newApi[k])!==null).map(k=>{const _w=resolveWrapper(newApi[k]);return{key:k,apiPath:_w.apiPath,implKeys:Object.keys(_w.____slothletInternal.impl||{}),childCacheSize:Object.keys(_w).filter(k2=>!k2.startsWith("_")&&!k2.startsWith("__")).length,childCacheKeys:Object.keys(_w).filter(k2=>!k2.startsWith("_")&&!k2.startsWith("__"))}}),nonWrappers:Object.keys(newApi).filter(k=>resolveWrapper(newApi[k])===null).map(k=>({key:k,type:typeof newApi[k]}))});let apiToMerge=newApi;if(isFile&&Object.keys(newApi).length===1){const fileName=Object.keys(newApi)[0];apiToMerge=newApi[fileName]}if(!isFile&&normalizedPath){const lastPart=normalizedPath.includes(".")?normalizedPath.split(".").pop():normalizedPath;if(lastPart&&Object.prototype.hasOwnProperty.call(apiToMerge,lastPart)){const dupValue=apiToMerge[lastPart];const dupType=typeof dupValue;if(dupValue!==null&&(dupType==="object"||dupType==="function")){const dupWrapper=resolveWrapper(dupValue);const dupFilePath=dupWrapper?.____slothletInternal?.filePath;const dupFileDir=dupFilePath?dupFilePath.replace(/\\/g,"/").split("/").slice(0,-1).join("/"):null;const normalizedFolderPath=resolvedFolderPath.replace(/\\/g,"/").replace(/\/$/,"");const expectedDir=normalizedFolderPath+"/"+lastPart;const isDirectChild=dupFileDir===expectedDir||dupFileDir===normalizedFolderPath;if(isDirectChild){const hoisted={};for(const k of Object.keys(apiToMerge)){if(k!==lastPart)hoisted[k]=apiToMerge[k]}if(dupWrapper){for(const k of Object.keys(dupWrapper).filter(k2=>!k2.startsWith("_")&&!k2.startsWith("__"))){hoisted[k]=dupWrapper[k]}}else{for(const k of Object.keys(dupValue)){hoisted[k]=dupValue[k]}}apiToMerge=hoisted;this.slothlet.debug("api",{key:"DEBUG_MODE_RULE_13_DEDUP_HOISTED_KEY",lastPart,newKeys:Object.keys(apiToMerge)})}}}}if(this.____config.debug?.api){this.slothlet.debug("api",{key:"DEBUG_MODE_ADD_API_COMPONENT_MERGE_KEYS",keys:Object.keys(apiToMerge),isRootLevel:parts.length===0})}let anyAssignmentSucceeded=false;if(parts.length===0){for(const key of Object.keys(newApi)){const result1=await this.setValueAtPath(this.slothlet.api,[key],newApi[key],{mutateExisting,collisionMode,moduleID,sourceFolder:resolvedFolderPath});const result2=await this.setValueAtPath(this.slothlet.boundApi,[key],newApi[key],{mutateExisting,collisionMode,moduleID,sourceFolder:resolvedFolderPath});if(result1||result2){anyAssignmentSucceeded=true}}}else{if(resolveWrapper(apiToMerge)===null){const isCallableNamespace=typeof apiToMerge==="function";const containerWrapper=new UnifiedWrapper(this.slothlet,{apiPath:effectivePath,mode:this.____config.mode,isCallable:isCallableNamespace,moduleID,filePath:resolvedFolderPath,sourceFolder:resolvedFolderPath});containerWrapper.___setImpl(apiToMerge,moduleID);apiToMerge=containerWrapper.createProxy()}const result1=await this.setValueAtPath(this.slothlet.api,effectiveParts,apiToMerge,{mutateExisting,collisionMode,moduleID,sourceFolder:resolvedFolderPath});const result2=await this.setValueAtPath(this.slothlet.boundApi,effectiveParts,apiToMerge,{mutateExisting,collisionMode,moduleID,sourceFolder:resolvedFolderPath});if(result1||result2){anyAssignmentSucceeded=true}}if(anyAssignmentSucceeded){const pendingMaterializations=[];const seenWrappers=new Set;const collectPendingMaterializations=(obj,depth=0)=>{if(!obj||typeof obj!=="object"||depth>10)return;if(obj.__isVersionDispatcher===true)return;const wrapper=resolveWrapper(obj);if(wrapper){if(seenWrappers.has(wrapper))return;seenWrappers.add(wrapper);if(wrapper.____slothletInternal.materializationPromise){pendingMaterializations.push(wrapper.____slothletInternal.materializationPromise)}const childKeys=Object.keys(wrapper).filter(k=>!k.startsWith("_")&&!k.startsWith("__"));for(const key of childKeys){collectPendingMaterializations(wrapper[key],depth+1)}}for(const key of Object.keys(obj)){if(key!=="____slothletInternal"){collectPendingMaterializations(obj[key],depth+1)}}};if(effectiveParts.length===0){for(const key of Object.keys(newApi)){if(this.slothlet.api[key]){collectPendingMaterializations(this.slothlet.api[key])}}}else{let current=this.slothlet.api;for(const part of effectiveParts){if(current&¤t[part]){current=current[part]}else{break}}if(current){collectPendingMaterializations(current)}}if(pendingMaterializations.length>0){if(this.____config.debug?.api){this.slothlet.debug("api",{key:"DEBUG_MODE_AWAITING_PENDING_MATERIALIZATIONS",count:pendingMaterializations.length,apiPath:normalizedPath})}await Promise.all(pendingMaterializations)}}if(anyAssignmentSucceeded&&metadata&&Object.keys(metadata).length>0&&this.slothlet.handlers.metadata){if(parts.length===0){for(const key of Object.keys(newApi)){this.slothlet.handlers.metadata.registerUserMetadata(key,metadata)}}else{const rootSegment=effectiveParts[0];this.slothlet.handlers.metadata.registerUserMetadata(rootSegment,metadata)}}if(this.slothlet.handlers.ownership&&moduleID){this.slothlet.handlers.ownership.registerSubtree(apiToMerge,moduleID,effectivePath);this.slothlet.handlers.ownership.setModuleEndpoint(moduleID,effectivePath)}if(this.slothlet.handlers.ownership){if(restOptions.recordHistory!==false){this.state.addHistory.push({apiPath:normalizedPath,folderPath:resolvedFolderPath,options:{...restOptions,metadata,moduleID},moduleID,versionConfig:versionConfig||null});this.state.operationHistory.push({type:"add",apiPath:normalizedPath,folderPath:resolvedFolderPath,options:{...restOptions,metadata,moduleID},moduleID,versionConfig:versionConfig||null})}}if(versionConfig?.version&&this.slothlet.handlers.versionManager){const versionTag=String(versionConfig.version).trim();try{this.slothlet.handlers.versionManager.registerVersion(normalizedPath,versionTag,moduleID,versionConfig.metadata??{},versionConfig.default??false)}catch(error){await this._rollbackFailedVersionedAdd({moduleID,effectivePath,normalizedPath});throw error}}if(restOptions.permissions&&this.slothlet.handlers?.permissionManager){const perms=restOptions.permissions;const callerPattern=`${normalizedPath}.**`;if(Array.isArray(perms.deny)){for(const entry of perms.deny){const{target,condition}=this.#normalizePermissionShorthandEntry(entry);this.slothlet.handlers.permissionManager.addRule({caller:callerPattern,target,effect:"deny",condition},moduleID)}}if(Array.isArray(perms.allow)){for(const entry of perms.allow){const{target,condition}=this.#normalizePermissionShorthandEntry(entry);this.slothlet.handlers.permissionManager.addRule({caller:callerPattern,target,effect:"allow",condition},moduleID)}}}return moduleID}async _rollbackFailedVersionedAdd({moduleID,effectivePath,normalizedPath}){let addIndex=-1;for(let i=this.state.operationHistory.length-1;i>=0;i--){const entry=this.state.operationHistory[i];if(entry?.type==="add"&&entry?.apiPath===normalizedPath&&entry?.moduleID===moduleID){addIndex=i;break}}if(addIndex!==-1){this.state.operationHistory.splice(addIndex,1)}this.state.addHistory=this.state.addHistory.filter(entry=>entry?.moduleID!==moduleID);try{await this.removeApiComponent(moduleID||effectivePath,{recordHistory:false})}catch{}}#sweepOrphanedCaches(){const cacheManager=this.slothlet.handlers?.apiCacheManager;if(!cacheManager)return;const root=this.slothlet.boundApi;for(const moduleID of cacheManager.getAllModuleIDs()){const entry=cacheManager.get(moduleID);if(!entry||entry.endpoint==="."||entry.endpoint==="")continue;const parts=String(entry.endpoint).split(".").filter(Boolean);if(this.getValueAtPath(root,parts)===void 0){cacheManager.delete(moduleID)}}}async removeApiComponent(pathOrModuleId,options={}){const recordHistory=options.recordHistory!==false;if(typeof pathOrModuleId!=="string"||!pathOrModuleId){throw new this.SlothletError("INVALID_ARGUMENT",{argument:"pathOrModuleId",expected:"non-empty string",received:typeof pathOrModuleId,validationError:true})}let apiPath=null;let moduleID=null;if(this.slothlet.handlers.ownership){const candidateModuleID=pathOrModuleId.split(":")[0];const registeredModules=Array.from(this.slothlet.handlers.ownership.moduleToPath.keys());let matchingModule=null;for(let i=registeredModules.length-1;i>=0;i--){const candidate=registeredModules[i];if(candidate===candidateModuleID||candidate.startsWith(`${candidateModuleID}_`)){matchingModule=candidate;break}}if(matchingModule){moduleID=matchingModule}else{const owner=this.slothlet.handlers.ownership.getCurrentOwner(pathOrModuleId);if(owner){apiPath=pathOrModuleId;moduleID=owner.moduleID}else{return false}}}else{const isModuleId=!pathOrModuleId.includes(".");apiPath=isModuleId?null:pathOrModuleId;moduleID=isModuleId?pathOrModuleId.split(":")[0]:null}if(!this.slothlet||!this.slothlet.isLoaded){throw new this.SlothletError("INVALID_CONFIG_NOT_LOADED",{operation:"removeApi",validationError:true})}if(apiPath&&moduleID){const normalizedPath2=this.normalizeApiPath(apiPath).apiPath;const moduleIDKey=String(moduleID);const history=this.slothlet.handlers.ownership?.getPathHistory?.(normalizedPath2)||[];const ownershipResult2=this.slothlet.handlers.ownership?.removePath?.(normalizedPath2,moduleIDKey)||{action:"none",removedModuleId:null,restoreModuleId:null};const pathParts=this.normalizeApiPath(apiPath).parts;if(ownershipResult2.action==="delete"){await this.deletePath(this.slothlet.api,pathParts);await this.deletePath(this.slothlet.boundApi,pathParts);if(this.slothlet.handlers.metadata){const rootSegment=normalizedPath2.split(".")[0];this.slothlet.handlers.metadata.removeUserMetadataByApiPath(rootSegment)}if(this.slothlet.handlers.versionManager){const versionKey=this.slothlet.handlers.versionManager.getVersionKeyForModule(moduleIDKey);if(versionKey){this.slothlet.handlers.versionManager.unregisterVersion(versionKey.logicalPath,versionKey.versionTag)}if(this.slothlet.handlers.versionManager.hasDispatcher(normalizedPath2)){this.slothlet.handlers.versionManager.teardownDispatcher(normalizedPath2)}}this.#sweepOrphanedCaches();this.state.operationHistory.push({type:"remove",apiPath:normalizedPath2});return true}if(ownershipResult2.action==="restore"){const restoredValue=this.slothlet.handlers.ownership?.getCurrentValue?.(normalizedPath2);const restoredModuleId=this.slothlet.handlers.ownership?.getCurrentOwner?.(normalizedPath2)?.moduleID;if(restoredValue!==void 0&&restoredModuleId){await this.setValueAtPath(this.slothlet.api,pathParts,restoredValue,{mutateExisting:true,allowOverwrite:true,collisionMode:"replace",moduleID:restoredModuleId});await this.setValueAtPath(this.slothlet.boundApi,pathParts,restoredValue,{mutateExisting:true,allowOverwrite:true,collisionMode:"replace",moduleID:restoredModuleId});this.state.operationHistory.push({type:"remove",apiPath:normalizedPath2});return true}await this.restoreApiPath(normalizedPath2,ownershipResult2.restoreModuleId);this.state.operationHistory.push({type:"remove",apiPath:normalizedPath2});return true}if(ownershipResult2.action==="none"&&history.length===0){await this.deletePath(this.slothlet.api,pathParts);await this.deletePath(this.slothlet.boundApi,pathParts);return true}return false}if(moduleID){const moduleIDKey=String(moduleID);const result=this.slothlet.handlers.ownership?.unregister?.(moduleIDKey)||{removed:[],rolledBack:[]};if(this.slothlet.handlers.versionManager){const versionKey=this.slothlet.handlers.versionManager.getVersionKeyForModule(moduleIDKey);if(versionKey){this.slothlet.handlers.versionManager.unregisterVersion(versionKey.logicalPath,versionKey.versionTag)}}const allPaths=[...result.removed,...result.rolledBack.map(r=>r.apiPath)];const uniquePaths=[...new Set(allPaths)];const pathsToDelete=[];const pathsToRollback=[];for(const path2 of uniquePaths){const currentOwner=this.slothlet.handlers.ownership?.getCurrentOwner?.(path2);const hasChildrenWithOtherOwners=uniquePaths.some(p=>{if(p===path2||!p.startsWith(path2+"."))return false;const childOwner=this.slothlet.handlers.ownership?.getCurrentOwner?.(p);return childOwner&&childOwner.moduleID!==moduleIDKey});if(currentOwner&¤tOwner.moduleID!==moduleIDKey){pathsToRollback.push({apiPath:path2,restoredTo:currentOwner.moduleID})}else if(!hasChildrenWithOtherOwners){pathsToDelete.push(path2)}}pathsToDelete.sort((a,b)=>{const depthA=(a.match(/\./g)||[]).length;const depthB=(b.match(/\./g)||[]).length;return depthB-depthA});for(const removedPath of pathsToDelete){const{parts:parts2}=this.normalizeApiPath(removedPath);await this.deletePath(this.slothlet.api,parts2);await this.deletePath(this.slothlet.boundApi,parts2);if(this.slothlet.handlers.metadata){const rootSegment=removedPath.split(".")[0];this.slothlet.handlers.metadata.removeUserMetadataByApiPath(rootSegment)}}if(pathsToDelete.length>0){const rootSegment=pathsToDelete[0].split(".")[0];if(rootSegment in this.slothlet.api){delete this.slothlet.api[rootSegment]}if(rootSegment in this.slothlet.boundApi){delete this.slothlet.boundApi[rootSegment]}}for(const rollback of pathsToRollback){const{parts:parts2}=this.normalizeApiPath(rollback.apiPath);const previousImpl=this.slothlet.handlers.ownership?.getCurrentValue?.(rollback.apiPath);if(previousImpl!==void 0){const existingWrapper=this.getValueAtPath(this.slothlet.api,parts2);const existingWrapperRaw=resolveWrapper(existingWrapper);if(existingWrapperRaw){existingWrapperRaw.___setImpl(previousImpl,rollback.restoredTo)}const existingBoundWrapper=this.getValueAtPath(this.slothlet.boundApi,parts2);const existingBoundWrapperRaw=resolveWrapper(existingBoundWrapper);if(existingBoundWrapperRaw){existingBoundWrapperRaw.___setImpl(previousImpl,rollback.restoredTo)}}}this.state.addHistory=this.state.addHistory.filter(entry=>String(entry.moduleID)!==moduleIDKey);if(this.slothlet.handlers.apiCacheManager){const deleted=this.slothlet.handlers.apiCacheManager.delete(moduleIDKey);if(deleted){this.slothlet.debug("cache",{key:"DEBUG_MODE_CACHE_DELETED_MODULE_REMOVED",moduleID:moduleIDKey})}}this.#sweepOrphanedCaches();if(recordHistory&&pathsToDelete.length>0){const rootSegment=pathsToDelete[0].split(".")[0];this.state.operationHistory.push({type:"remove",apiPath:rootSegment})}return pathsToDelete.length>0||pathsToRollback.length>0}if(!apiPath){throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID",{apiPath,reason:translate("API_PATH_REASON_REQUIRED"),index:void 0,segment:void 0,validationError:true})}const{apiPath:normalizedPath,parts}=this.normalizeApiPath(apiPath);const ownershipResult=this.slothlet.handlers.ownership?.removePath?.(normalizedPath,null)||{action:"none",removedModuleId:null,restoreModuleId:null};const pathExists=this.getValueAtPath(this.slothlet.api,parts)!==void 0;if(ownershipResult.action==="none"){if(pathExists){await this.deletePath(this.slothlet.api,parts);await this.deletePath(this.slothlet.boundApi,parts);if(this.slothlet.handlers.metadata){this.slothlet.handlers.metadata.removeUserMetadataByApiPath(normalizedPath)}if(recordHistory){this.state.operationHistory.push({type:"remove",apiPath:normalizedPath})}return true}return false}if(ownershipResult.action==="delete"){await this.deletePath(this.slothlet.api,parts);await this.deletePath(this.slothlet.boundApi,parts);if(this.slothlet.handlers.metadata){this.slothlet.handlers.metadata.removeUserMetadataByApiPath(normalizedPath)}if(recordHistory){this.state.operationHistory.push({type:"remove",apiPath:normalizedPath})}return true}if(ownershipResult.action==="restore"){const restoredValue=this.slothlet.handlers.ownership?.getCurrentValue?.(normalizedPath);const restoredModuleId=this.slothlet.handlers.ownership?.getCurrentOwner?.(normalizedPath)?.moduleID;if(restoredValue!==void 0&&restoredModuleId){await this.setValueAtPath(this.slothlet.api,parts,restoredValue,{mutateExisting:true,allowOverwrite:true,collisionMode:"replace",moduleID:restoredModuleId});await this.setValueAtPath(this.slothlet.boundApi,parts,restoredValue,{mutateExisting:true,allowOverwrite:true,collisionMode:"replace",moduleID:restoredModuleId});if(recordHistory){this.state.operationHistory.push({type:"remove",apiPath:normalizedPath})}return true}await this.restoreApiPath(normalizedPath,ownershipResult.restoreModuleId);if(recordHistory){this.state.operationHistory.push({type:"remove",apiPath:normalizedPath})}return true}return false}async reloadApiComponent(params){const{apiPath,moduleID,options}=params||{};if(!this.slothlet||!this.slothlet.isLoaded){throw new this.SlothletError("INVALID_CONFIG_NOT_LOADED",{operation:"reloadApi",validationError:true})}if(moduleID){await this._reloadByModuleID(moduleID);return}if(apiPath){await this._reloadByApiPath(apiPath,options);return}throw new this.SlothletError("INVALID_ARGUMENT",{argument:"params",expected:"{ moduleID } or { apiPath }",received:params,validationError:true})}async _reloadByModuleID(moduleID,{forceReplace=true}={}){const cacheManager=this.slothlet.handlers.apiCacheManager;if(!cacheManager){throw new this.SlothletError("CACHE_MANAGER_NOT_AVAILABLE",{operation:"reload",validationError:true})}if(!cacheManager.has(moduleID)){throw new this.SlothletError("CACHE_NOT_FOUND",{moduleID,operation:"reload",validationError:true})}const oldEntry=cacheManager.get(moduleID);this.slothlet.debug("reload",{key:"DEBUG_MODE_RELOADING_MODULE_BY_ID",moduleID,endpoint:oldEntry.endpoint,folderPath:oldEntry.folderPath});const freshApi=await cacheManager.rebuildCache(moduleID);cacheManager.set(moduleID,{...oldEntry,api:freshApi,timestamp:Date.now()});this.slothlet.debug("reload",{key:"DEBUG_MODE_FRESH_API_KEYS_BEFORE_RESTORE",moduleID,endpoint:oldEntry.endpoint,freshApiKeys:Object.keys(freshApi||{})});await this._restoreApiTree(freshApi,oldEntry.endpoint,moduleID,oldEntry.collisionMode,forceReplace);this.slothlet.debug("reload",{key:"DEBUG_MODE_FRESH_API_KEYS_AFTER_RESTORE",moduleID,endpoint:oldEntry.endpoint,freshApiKeys:Object.keys(freshApi||{})});this.slothlet.debug("reload",{key:"DEBUG_MODE_MODULE_RELOAD_COMPLETE",moduleID});if(this.slothlet.handlers.versionManager){this.slothlet.handlers.versionManager.onVersionedModuleReload(moduleID)}}async _reloadByApiPath(apiPath,options={}){this.slothlet.debug("reload",{key:"DEBUG_MODE_RELOADING_BY_API_PATH",apiPath});const moduleIDsToReload=this._findAffectedCaches(apiPath);if(moduleIDsToReload.length===0){this.slothlet.debug("reload",{key:"DEBUG_MODE_NO_CACHES_ATTEMPTING_RESTORE",apiPath});if(apiPath!=="."&&apiPath!==""){await this.restoreApiPath(apiPath,"base")}return}const cacheManager=this.slothlet.handlers.apiCacheManager;moduleIDsToReload.sort((a,b)=>{const entryA=cacheManager.get(a);const entryB=cacheManager.get(b);if(entryA?.endpoint==="."&&entryB?.endpoint!==".")return-1;if(entryB?.endpoint==="."&&entryA?.endpoint!==".")return 1;const indexA=this.state.addHistory.findIndex(h=>h.moduleID===a);const indexB=this.state.addHistory.findIndex(h=>h.moduleID===b);return indexA-indexB});const endpointOrder=new Map;for(const moduleID of moduleIDsToReload){const entry=cacheManager.get(moduleID);const ep=entry?.endpoint??".";if(!endpointOrder.has(ep))endpointOrder.set(ep,[]);endpointOrder.get(ep).push(moduleID)}for(const[,moduleIDs]of endpointOrder){for(let i=0;i<moduleIDs.length;i++){await this._reloadByModuleID(moduleIDs[i],{forceReplace:i===0})}}const reloadMetadata=options?.metadata;if(reloadMetadata&&typeof reloadMetadata==="object"&&Object.keys(reloadMetadata).length>0){if(this.slothlet.handlers.metadata){const targetPath=apiPath==="."?null:apiPath.split(".")[0];if(targetPath){this.slothlet.handlers.metadata.registerUserMetadata(targetPath,reloadMetadata)}}}this.slothlet.debug("reload",{key:"DEBUG_MODE_API_PATH_RELOAD_COMPLETE",apiPath,reloadedModules:moduleIDsToReload.length,loadOrder:moduleIDsToReload})}_findAffectedCaches(apiPath){const cacheManager=this.slothlet.handlers.apiCacheManager;if(!cacheManager)return[];const allModuleIDs=cacheManager.getAllModuleIDs();if(apiPath==="."||apiPath===""||apiPath==null){const baseModules=[];for(const moduleID of allModuleIDs){const entry=cacheManager.get(moduleID);if(entry&&entry.endpoint==="."){baseModules.push(moduleID)}}return baseModules}const exactMatches=[];for(const moduleID of allModuleIDs){const entry=cacheManager.get(moduleID);if(entry&&entry.endpoint===apiPath){exactMatches.push(moduleID)}}if(exactMatches.length>0)return exactMatches;const children=[];const pathPrefix=apiPath+".";for(const moduleID of allModuleIDs){const entry=cacheManager.get(moduleID);if(entry?.endpoint?.startsWith(pathPrefix)){children.push(moduleID)}}if(children.length>0)return children;const ownership=this.slothlet.handlers.ownership;const history=ownership?.getPathHistory?.(apiPath);if(history&&history.length>0){const owned=[];for(const{moduleID}of history){if(cacheManager.has(moduleID)){owned.push(moduleID)}}if(owned.length>0)return owned}let bestMatch=null;let bestLength=-1;for(const moduleID of allModuleIDs){const entry=cacheManager.get(moduleID);if(!entry?.endpoint)continue;const ep=entry.endpoint;if(ep==="."||apiPath.startsWith(ep+".")){if(ep.length>bestLength){bestLength=ep.length;bestMatch=moduleID}}}if(bestMatch)return[bestMatch];return[]}_collectCustomProperties(existingProxy,freshApi){const customProps={};if(!existingProxy||typeof existingProxy!=="object"&&typeof existingProxy!=="function"){return customProps}const wrapper=resolveWrapper(existingProxy);if(!wrapper){return customProps}const freshKeys=new Set(freshApi?Object.keys(freshApi):[]);const ownKeys=Object.keys(wrapper).filter(k=>!ComponentBase.INTERNAL_KEYS.has(k));for(const key of ownKeys){try{const val=wrapper[key];if(val&&(typeof val==="object"||typeof val==="function")&&resolveWrapper(val)){continue}if(!freshKeys.has(key)){customProps[key]=val}else{customProps[key]=val}}catch{}}return customProps}_restoreCustomProperties(proxy,customProps){if(!proxy||!customProps||typeof customProps!=="object"){return}for(const[key,value]of Object.entries(customProps)){try{proxy[key]=value}catch{}}}async _restoreApiTree(freshApi,endpoint,moduleID,collisionMode,forceReplace=true){if(!freshApi||typeof freshApi!=="object"&&typeof freshApi!=="function"){return}const parts=endpoint==="."?[]:endpoint.split(".");if(parts.length===0){for(const key of Object.keys(freshApi)){if(typeof key==="string"&&(key.startsWith("_")||key.startsWith("__")))continue;if(key==="slothlet"||key==="shutdown"||key==="destroy")continue;const existingAtKey=this.slothlet.api[key];const freshValue=freshApi[key];if(existingAtKey&&resolveWrapper(existingAtKey)!==null){const customProps=this._collectCustomProperties(existingAtKey,freshValue);const freshWrapper=resolveWrapper(freshValue);const isLazyFresh=freshWrapper&&freshWrapper.____slothletInternal.mode==="lazy"&&!freshWrapper.____slothletInternal.state.materialized&&typeof freshWrapper.____slothletInternal.materializeFunc==="function";this.slothlet.debug("reload",{key:"DEBUG_MODE_RESTORE_ROOT_KEY_INSPECT",rootKey:key,hasFreshWrapper:!!freshWrapper,freshMode:freshWrapper?.____slothletInternal.mode,freshMaterialized:freshWrapper?.____slothletInternal.state?.materialized,hasMaterializeFunc:typeof freshWrapper?.____slothletInternal.materializeFunc==="function",isLazyFresh,existingMaterialized:resolveWrapper(existingAtKey)?.____slothletInternal?.state?.materialized});if(isLazyFresh){resolveWrapper(existingAtKey).___resetLazy(freshWrapper.____slothletInternal.materializeFunc);this._restoreCustomProperties(existingAtKey,customProps);this.slothlet.debug("reload",{key:"DEBUG_MODE_ROOT_KEY_RESET_LAZY",rootKey:key,restoredCustomProps:Object.keys(customProps)})}else{let implForReload;if(freshValue&&resolveWrapper(freshValue)!==null){implForReload=freshWrapper?UnifiedWrapper._extractFullImpl(freshWrapper):freshValue}else{implForReload=freshValue}if(typeof implForReload==="function"){const extracted={};for(const k of Object.keys(implForReload)){extracted[k]=implForReload[k]}implForReload=extracted}const wrapper=resolveWrapper(existingAtKey);const originalCollisionMode=wrapper?wrapper.____slothletInternal.state.collisionMode:null;if(forceReplace&&wrapper){wrapper.____slothletInternal.state.collisionMode="replace"}if(wrapper&&originalCollisionMode!==null){wrapper.____slothletInternal.state.collisionMode=originalCollisionMode}this._restoreCustomProperties(existingAtKey,customProps);this.slothlet.debug("reload",{key:"DEBUG_MODE_ROOT_KEY_UPDATED_SETIMPL",rootKey:key,restoredCustomProps:Object.keys(customProps)})}}else if(existingAtKey===void 0){const cacheManager=this.slothlet.handlers.apiCacheManager;const cacheEntry=cacheManager.get(moduleID);const resolvedFolderPath=cacheEntry?.folderPath||"";await this.setValueAtPath(this.slothlet.api,[key],freshValue,{mutateExisting:true,collisionMode,moduleID,sourceFolder:resolvedFolderPath});if(this.slothlet.boundApi){await this.setValueAtPath(this.slothlet.boundApi,[key],freshValue,{mutateExisting:true,collisionMode,moduleID,sourceFolder:resolvedFolderPath})}}}}else{const existing=this.getValueAtPath(this.slothlet.api,parts);this.slothlet.debug("reload",{key:"DEBUG_MODE_RESTORE_NESTED_PATH",endpoint,moduleID,partsPath:parts.join("."),existingFound:!!existing,hasSetImpl:existing?resolveWrapper(existing)!==null:false,freshApiKeys:Object.keys(freshApi||{})});if(existing&&resolveWrapper(existing)!==null){const customProps=this._collectCustomProperties(existing,freshApi);const wrapper=resolveWrapper(existing);const originalCollisionMode=wrapper?wrapper.____slothletInternal.state.collisionMode:null;if(forceReplace&&wrapper){wrapper.____slothletInternal.state.collisionMode="replace";this.slothlet.debug("reload",{key:"DEBUG_MODE_RESTORE_FORCING_REPLACE",endpoint,originalCollisionMode,wrapperApiPath:wrapper.____slothletInternal.apiPath})}let implForReload;if(resolveWrapper(freshApi)!==null){const freshWrapper=resolveWrapper(freshApi);implForReload=freshWrapper?UnifiedWrapper._extractFullImpl(freshWrapper):freshApi}else if(typeof freshApi==="function"){implForReload={};for(const key of Object.keys(freshApi)){implForReload[key]=freshApi[key]}}else{implForReload=freshApi}if(parts.length>0&&implForReload&&typeof implForReload==="object"){const lastEndpointPart=parts[parts.length-1];if(lastEndpointPart&&Object.prototype.hasOwnProperty.call(implForReload,lastEndpointPart)){const dupValue=implForReload[lastEndpointPart];const dupWrapperForDedup=resolveWrapper(dupValue);if(dupWrapperForDedup){const hoisted={};for(const k of Object.keys(implForReload)){if(k!==lastEndpointPart)hoisted[k]=implForReload[k]}for(const k of Object.keys(dupWrapperForDedup).filter(k2=>!k2.startsWith("_")&&!k2.startsWith("__"))){hoisted[k]=dupWrapperForDedup[k]}implForReload=hoisted}}}if(implForReload&&typeof implForReload==="object"){for(const key of Object.keys(implForReload)){const val=implForReload[key];if(resolveWrapper(val)!==null){const childWrapper=resolveWrapper(val);if(childWrapper.____slothletInternal.state.materialized){implForReload[key]=UnifiedWrapper._extractFullImpl(childWrapper)}}}}resolveWrapper(existing).___setImpl(implForReload,moduleID);if(wrapper&&originalCollisionMode!==null){wrapper.____slothletInternal.state.collisionMode=originalCollisionMode}this._restoreCustomProperties(existing,customProps);this.slothlet.debug("reload",{key:"DEBUG_MODE_UPDATED_WRAPPER_IMPL",endpoint,moduleID,forcedReplaceMode:true,restoredCustomProps:Object.keys(customProps)})}else{const cacheManager=this.slothlet.handlers.apiCacheManager;const cacheEntry=cacheManager.get(moduleID);const resolvedFolderPath=cacheEntry?.folderPath||"";let implForContainer=freshApi;if(typeof freshApi==="function"){implForContainer={};for(const key of Object.keys(freshApi)){implForContainer[key]=freshApi[key]}}const containerWrapper=new UnifiedWrapper(this.slothlet,{apiPath:endpoint,mode:this.____config.mode,moduleID,filePath:resolvedFolderPath,sourceFolder:resolvedFolderPath});containerWrapper.___setImpl(implForContainer,moduleID);const apiToSet=containerWrapper.createProxy();await this.setValueAtPath(this.slothlet.api,parts,apiToSet,{mutateExisting:true,collisionMode,moduleID,sourceFolder:resolvedFolderPath});if(this.slothlet.boundApi){await this.setValueAtPath(this.slothlet.boundApi,parts,apiToSet,{mutateExisting:true,collisionMode,moduleID,sourceFolder:resolvedFolderPath})}this.slothlet.debug("reload",{key:"DEBUG_MODE_CREATED_NEW_WRAPPER_UNEXPECTED",endpoint,moduleID})}}}}export{ApiManager};
|
|
@@ -14,4 +14,4 @@
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import{ComponentBase}from"@cldmv/slothlet/factories/component-base";import{resolveWrapper}from"@cldmv/slothlet/handlers/unified-wrapper";class OwnershipManager extends ComponentBase{static slothletProperty="ownership";constructor(slothlet){super(slothlet);this.moduleToPath=new Map;this.pathToModule=new Map;this._unregisteredModules=new Set}register({moduleID,apiPath,value,source="core",collisionMode="error",config=null,filePath=null}){if(!moduleID||typeof moduleID!=="string"){throw new this.SlothletError("OWNERSHIP_INVALID_MODULE_ID",{moduleID},null,{validationError:true})}if(apiPath!==""&&(!apiPath||typeof apiPath!=="string")){throw new this.SlothletError("OWNERSHIP_INVALID_API_PATH",{apiPath},null,{validationError:true})}if(this._unregisteredModules.has(moduleID)){return null}const currentOwner=this.getCurrentOwner(apiPath);if(currentOwner&¤tOwner.moduleID!==moduleID){if(collisionMode==="merge"||collisionMode==="replace"||collisionMode==="merge-replace"){}else if(collisionMode==="skip"){return null}else if(collisionMode==="warn"){if(!config?.silent){new this.SlothletWarning("WARNING_OWNERSHIP_CONFLICT",{apiPath,existingModuleId:currentOwner.moduleID,newModuleId:moduleID})}return null}else{throw new this.SlothletError("OWNERSHIP_CONFLICT",{apiPath,existingModuleId:currentOwner.moduleID,newModuleId:moduleID,validationError:true})}}if(!this.moduleToPath.has(moduleID)){this.moduleToPath.set(moduleID,new Set)}this.moduleToPath.get(moduleID).add(apiPath);if(!this.pathToModule.has(apiPath)){this.pathToModule.set(apiPath,[])}const stack=this.pathToModule.get(apiPath);const existingEntry=stack.find(entry2=>entry2.moduleID===moduleID);if(existingEntry){existingEntry.source=source;existingEntry.timestamp=Date.now();existingEntry.value=value;if(filePath!==null){existingEntry.filePath=filePath}return existingEntry}const entry={moduleID,source,timestamp:Date.now(),value,filePath};this.pathToModule.get(apiPath).push(entry);return entry}unregister(moduleID){const paths=this.moduleToPath.get(moduleID);if(!paths){return{removed:[],rolledBack:[]}}this._unregisteredModules.add(moduleID);const removed=[];const rolledBack=[];for(const apiPath of paths){const result=this.removePath(apiPath,moduleID);if(result.action==="delete"){removed.push(apiPath)}else if(result.action==="restore"){rolledBack.push({apiPath,restoredTo:result.restoreModuleId})}}this.moduleToPath.delete(moduleID);return{removed,rolledBack}}removePath(apiPath,moduleID=null){const stack=this.pathToModule.get(apiPath);if(!stack){return{action:"none",removedModuleId:null,restoreModuleId:null}}const index=moduleID?stack.findIndex(entry=>entry.moduleID===moduleID):stack.length-1;if(index===-1){return{action:"none",removedModuleId:null,restoreModuleId:null}}const[removed]=stack.splice(index,1);const removedModuleId=removed.moduleID;if(removedModuleId&&this.moduleToPath.has(removedModuleId)){const pathSet=this.moduleToPath.get(removedModuleId);pathSet.delete(apiPath);if(pathSet.size===0){this.moduleToPath.delete(removedModuleId)}}if(stack.length===0){this.pathToModule.delete(apiPath);return{action:"delete",removedModuleId,restoreModuleId:null}}const previous=stack[stack.length-1];return{action:"restore",removedModuleId,restoreModuleId:previous.moduleID}}getCurrentOwner(apiPath){const stack=this.pathToModule.get(apiPath);if(!stack||stack.length===0)return null;return stack[stack.length-1]}getCurrentValue(apiPath){const owner=this.getCurrentOwner(apiPath);if(!owner)return void 0;const value=owner.value;const rawWrapper=resolveWrapper(value);if(rawWrapper){return rawWrapper.__impl}return value}getModulePaths(moduleID){return Array.from(this.moduleToPath.get(moduleID)||[])}getPathHistory(apiPath){return this.pathToModule.get(apiPath)||[]}ownsPath(moduleID,apiPath){const owner=this.getCurrentOwner(apiPath);return owner&&owner.moduleID===moduleID}getDiagnostics(){return{totalModules:this.moduleToPath.size,totalPaths:this.pathToModule.size,modules:Array.from(this.moduleToPath.entries()).map(([id,paths])=>({moduleID:id,pathCount:paths.size})),conflictedPaths:Array.from(this.pathToModule.entries()).filter(([_,stack])=>stack.length>1).map(([path,stack])=>({apiPath:path,ownerStack:stack.map(e=>e.moduleID)}))}}getPathOwnership(apiPath){const stack=this.pathToModule.get(apiPath);if(!stack||stack.length===0){return null}return new Set(stack.map(entry=>entry.moduleID))}registerSubtree(api,moduleID,path,visited=new WeakSet){if(!api||typeof api!=="object")return;if(visited.has(api)){return}visited.add(api);if(path){this.register({moduleID,apiPath:path,value:api,source:"core",collisionMode:"merge",filePath:null})}for(const[key,value]of Object.entries(api)){const skipProps=["__metadata","__type","_materialize","_impl","____slothletInternal"];if(skipProps.includes(key)){continue}const childPath=path?`${path}.${key}`:key;if(typeof value==="function"||value&&typeof value==="object"){this.register({moduleID,apiPath:childPath,value,source:"core",collisionMode:"merge",filePath:null});if(typeof value==="object"&&!Array.isArray(value)){this.registerSubtree(value,moduleID,childPath,visited)}}}}clear(){this.moduleToPath.clear();this.pathToModule.clear();this._unregisteredModules.clear()}exportState(){return{moduleToPath:Array.from(this.moduleToPath.entries()).map(([id,paths])=>[id,Array.from(paths)]),pathToModule:Array.from(this.pathToModule.entries())}}importState(state){this.clear();for(const[id,paths]of state.moduleToPath){this.moduleToPath.set(id,new Set(paths))}for(const[path,stack]of state.pathToModule){this.pathToModule.set(path,stack)}}}export{OwnershipManager};
|
|
17
|
+
import{ComponentBase}from"@cldmv/slothlet/factories/component-base";import{resolveWrapper}from"@cldmv/slothlet/handlers/unified-wrapper";class OwnershipManager extends ComponentBase{static slothletProperty="ownership";constructor(slothlet){super(slothlet);this.moduleToPath=new Map;this.pathToModule=new Map;this._unregisteredModules=new Set;this.moduleEndpoints=new Map}setModuleEndpoint(moduleID,endpoint){if(typeof moduleID==="string"&&moduleID){this.moduleEndpoints.set(moduleID,endpoint)}}getModuleEndpoint(moduleID){return this.moduleEndpoints.get(moduleID)}register({moduleID,apiPath,value,source="core",collisionMode="error",config=null,filePath=null}){if(!moduleID||typeof moduleID!=="string"){throw new this.SlothletError("OWNERSHIP_INVALID_MODULE_ID",{moduleID},null,{validationError:true})}if(apiPath!==""&&(!apiPath||typeof apiPath!=="string")){throw new this.SlothletError("OWNERSHIP_INVALID_API_PATH",{apiPath},null,{validationError:true})}if(this._unregisteredModules.has(moduleID)){return null}const currentOwner=this.getCurrentOwner(apiPath);if(currentOwner&¤tOwner.moduleID!==moduleID){if(collisionMode==="merge"||collisionMode==="replace"||collisionMode==="merge-replace"){}else if(collisionMode==="skip"){return null}else if(collisionMode==="warn"){if(!config?.silent){new this.SlothletWarning("WARNING_OWNERSHIP_CONFLICT",{apiPath,existingModuleId:currentOwner.moduleID,newModuleId:moduleID})}return null}else{throw new this.SlothletError("OWNERSHIP_CONFLICT",{apiPath,existingModuleId:currentOwner.moduleID,newModuleId:moduleID,validationError:true})}}if(!this.moduleToPath.has(moduleID)){this.moduleToPath.set(moduleID,new Set)}this.moduleToPath.get(moduleID).add(apiPath);if(!this.pathToModule.has(apiPath)){this.pathToModule.set(apiPath,[])}const stack=this.pathToModule.get(apiPath);const existingEntry=stack.find(entry2=>entry2.moduleID===moduleID);if(existingEntry){existingEntry.source=source;existingEntry.timestamp=Date.now();existingEntry.value=value;if(filePath!==null){existingEntry.filePath=filePath}return existingEntry}const entry={moduleID,source,timestamp:Date.now(),value,filePath};this.pathToModule.get(apiPath).push(entry);return entry}unregister(moduleID){const paths=this.moduleToPath.get(moduleID);if(!paths){return{removed:[],rolledBack:[]}}this._unregisteredModules.add(moduleID);const removed=[];const rolledBack=[];for(const apiPath of paths){const result=this.removePath(apiPath,moduleID);if(result.action==="delete"){removed.push(apiPath)}else if(result.action==="restore"){rolledBack.push({apiPath,restoredTo:result.restoreModuleId})}}this.moduleToPath.delete(moduleID);this.moduleEndpoints.delete(moduleID);return{removed,rolledBack}}removePath(apiPath,moduleID=null){const stack=this.pathToModule.get(apiPath);if(!stack){return{action:"none",removedModuleId:null,restoreModuleId:null}}const index=moduleID?stack.findIndex(entry=>entry.moduleID===moduleID):stack.length-1;if(index===-1){return{action:"none",removedModuleId:null,restoreModuleId:null}}const[removed]=stack.splice(index,1);const removedModuleId=removed.moduleID;if(removedModuleId&&this.moduleToPath.has(removedModuleId)){const pathSet=this.moduleToPath.get(removedModuleId);pathSet.delete(apiPath);if(pathSet.size===0){this.moduleToPath.delete(removedModuleId)}}if(stack.length===0){this.pathToModule.delete(apiPath);return{action:"delete",removedModuleId,restoreModuleId:null}}const previous=stack[stack.length-1];return{action:"restore",removedModuleId,restoreModuleId:previous.moduleID}}getCurrentOwner(apiPath){const stack=this.pathToModule.get(apiPath);if(!stack||stack.length===0)return null;return stack[stack.length-1]}getCurrentValue(apiPath){const owner=this.getCurrentOwner(apiPath);if(!owner)return void 0;const value=owner.value;const rawWrapper=resolveWrapper(value);if(rawWrapper){return rawWrapper.__impl}return value}getModulePaths(moduleID){return Array.from(this.moduleToPath.get(moduleID)||[])}getPathHistory(apiPath){return this.pathToModule.get(apiPath)||[]}ownsPath(moduleID,apiPath){const owner=this.getCurrentOwner(apiPath);return owner&&owner.moduleID===moduleID}getDiagnostics(){return{totalModules:this.moduleToPath.size,totalPaths:this.pathToModule.size,modules:Array.from(this.moduleToPath.entries()).map(([id,paths])=>({moduleID:id,pathCount:paths.size})),conflictedPaths:Array.from(this.pathToModule.entries()).filter(([_,stack])=>stack.length>1).map(([path,stack])=>({apiPath:path,ownerStack:stack.map(e=>e.moduleID)}))}}getPathOwnership(apiPath){const stack=this.pathToModule.get(apiPath);if(!stack||stack.length===0){return null}return new Set(stack.map(entry=>entry.moduleID))}registerSubtree(api,moduleID,path,visited=new WeakSet){if(!api||typeof api!=="object")return;if(visited.has(api)){return}visited.add(api);if(path){this.register({moduleID,apiPath:path,value:api,source:"core",collisionMode:"merge",filePath:null})}for(const[key,value]of Object.entries(api)){const skipProps=["__metadata","__type","_materialize","_impl","____slothletInternal"];if(skipProps.includes(key)){continue}const childPath=path?`${path}.${key}`:key;if(typeof value==="function"||value&&typeof value==="object"){this.register({moduleID,apiPath:childPath,value,source:"core",collisionMode:"merge",filePath:null});if(typeof value==="object"&&!Array.isArray(value)){this.registerSubtree(value,moduleID,childPath,visited)}}}}clear(){this.moduleToPath.clear();this.pathToModule.clear();this._unregisteredModules.clear();this.moduleEndpoints.clear()}exportState(){return{moduleToPath:Array.from(this.moduleToPath.entries()).map(([id,paths])=>[id,Array.from(paths)]),pathToModule:Array.from(this.pathToModule.entries())}}importState(state){this.clear();for(const[id,paths]of state.moduleToPath){this.moduleToPath.set(id,new Set(paths))}for(const[path,stack]of state.pathToModule){this.pathToModule.set(path,stack)}}}export{OwnershipManager};
|
|
@@ -35,6 +35,8 @@
|
|
|
35
35
|
"OWNERSHIP_INVALID_MODULE_ID": "Ungültige Eigentumsregistrierung: moduleID '{moduleID}' ist ungültig.",
|
|
36
36
|
"OWNERSHIP_INVALID_API_PATH": "Ungültige Eigentumsregistrierung: apiPath '{apiPath}' ist ungültig.",
|
|
37
37
|
"OWNERSHIP_CONFLICT": "Eigentumskonflikt: Pfad '{apiPath}' gehört bereits dem Modul '{existingModuleId}' und kann nicht '{newModuleId}' zugewiesen werden. Verwenden Sie forceOverwrite: true oder rufen Sie zuerst removeApi auf.",
|
|
38
|
+
"LOOSE_SET_NOT_OWNED": "Modul {callerApiPath} darf nicht in {apiPath} schreiben. Ein Modul darf nur unterhalb seines eigenen apiPath schreiben; verwenden Sie api.slothlet.api.add(), um einen anderen Pfad zu registrieren.",
|
|
39
|
+
"LOOSE_SET_RESERVED_KEY": "Zuweisung an reserviertes Schlüsselsegment '{segment}' im apiPath '{apiPath}' nicht möglich. Namen wie __proto__, prototype und constructor sind blockiert, um Prototype-Pollution des API-Baums zu verhindern.",
|
|
38
40
|
"INVALID_DIRECTORY": "Ungültiges Verzeichnis '{dir}': {error}. Stellen Sie sicher, dass das Verzeichnis existiert und zugänglich ist.",
|
|
39
41
|
"WARN_DIRECTORY_EMPTY": "Das Verzeichnis '{dir}' (aufgelöst: '{resolvedPath}') ist leer oder enthält keine ladbaren Module (.mjs, .cjs, .js Dateien). Dies ist für add-api Workflows gültig, kann aber auf einen falsch konfigurierten Pfad hindeuten.",
|
|
40
42
|
"INTERNAL_HOOK_STATE_CORRUPT": "[INTERNER FEHLER] Zustand des HookManagers ist korrupt: Hook \"{hookId}\" ({type}/{subset}/{pattern}) — {detail}. Dies ist ein Fehler in Slothlet — bitte melden Sie ihn.",
|
|
@@ -270,6 +272,8 @@
|
|
|
270
272
|
"HINT_INVALID_CONFIG_LAZY_ASYNC_REQUIRED": "Der Lazy-Modus erfordert asynchrone Operationen. Benutzen Sie den Eager-Modus oder implementieren Sie korrekte asynchrone Muster.",
|
|
271
273
|
"HINT_RUNTIME_NO_ACTIVE_CONTEXT_SELF": "Zugriff auf 'self' außerhalb eines Slothlet-API-Funktionskontexts ist nicht möglich.",
|
|
272
274
|
"HINT_RUNTIME_NO_ACTIVE_CONTEXT_CONTEXT": "Zugriff auf 'context' ist nur innerhalb einer Slothlet-API-Funktion möglich. Der Kontext kann nur innerhalb von Slothlet-API-Aufrufen geändert werden.",
|
|
275
|
+
"HINT_LOOSE_SET_NOT_OWNED": "Ein Modul mit apiPath \"P\" darf nur in \"P.*\" schreiben. Verwenden Sie api.slothlet.api.add(), um Code in einem anderen Namensraum zu registrieren.",
|
|
276
|
+
"HINT_LOOSE_SET_RESERVED_KEY": "Die Segmente __proto__, prototype und constructor dürfen nicht als apiPath-Namen verwendet werden, da das Schreiben darauf die JavaScript-Prototyp-Kette verändern würde. Wählen Sie einen anderen Namen.",
|
|
273
277
|
"HINT_INVALID_CONFIG_NOT_LOADED": "Die Instanz muss geladen sein, bevor diese Operation ausgeführt werden kann. Rufen Sie zuerst load() auf.",
|
|
274
278
|
"HINT_INVALID_CONFIG_RELOAD_NOT_IMPL": "Hinweis: Die Reload-Funktionalität ist noch nicht implementiert. Sie wird in einer zukünftigen Iteration hinzugefügt.",
|
|
275
279
|
"HINT_WARNING_RESERVED_PROPERTY_CONFLICT": "Der Eigenschaftsname 'slothlet' ist für interne API-Methoden reserviert. Benennen Sie Ihr API-Modul oder Ihre Eigenschaft um, um Konflikte mit slothlet.shutdown(), slothlet.api.* usw. zu vermeiden.",
|
|
@@ -382,7 +386,6 @@
|
|
|
382
386
|
"DEBUG_VERSION_DEFAULT_USED": "Keine Discriminator-Übereinstimmung für Pfad {apiPath}; Standardversion {version} wird verwendet.",
|
|
383
387
|
"DEBUG_VERSION_REGISTERED": "Version {version} unter {logicalPath} registriert (moduleID: {moduleID})",
|
|
384
388
|
"DEBUG_VERSION_UNREGISTERED": "Version {version} von {logicalPath} abgemeldet",
|
|
385
|
-
|
|
386
389
|
"PERMISSION_DENIED": "Zugriff verweigert: Aufrufer '{caller}' darf nicht auf '{target}' zugreifen",
|
|
387
390
|
"HINT_PERMISSION_DENIED": "Prüfen Sie die Berechtigungsregeln für dieses Aufrufer/Ziel-Paar. Verwenden Sie api.slothlet.permissions.global.rulesForPath('{target}'), um die aktiven Regeln einzusehen.",
|
|
388
391
|
"PERMISSION_SELF_MODIFY": "Modul '{moduleID}' kann seine eigene Berechtigungsregel '{ruleId}' nicht entfernen",
|
|
@@ -35,6 +35,8 @@
|
|
|
35
35
|
"OWNERSHIP_INVALID_MODULE_ID": "Invalid ownership registration: moduleID '{moduleID}' is invalid.",
|
|
36
36
|
"OWNERSHIP_INVALID_API_PATH": "Invalid ownership registration: apiPath '{apiPath}' is invalid.",
|
|
37
37
|
"OWNERSHIP_CONFLICT": "Ownership conflict: Path '{apiPath}' is already owned by module '{existingModuleId}', cannot assign to '{newModuleId}'. Use forceOverwrite: true or removeApi first.",
|
|
38
|
+
"LOOSE_SET_NOT_OWNED": "Module {callerApiPath} is not permitted to write to {apiPath}. A module may write only under its own apiPath; use api.slothlet.api.add() to register at a different path.",
|
|
39
|
+
"LOOSE_SET_RESERVED_KEY": "Cannot assign to reserved key segment '{segment}' in apiPath '{apiPath}'. Names like __proto__, prototype, and constructor are blocked to prevent prototype pollution of the API tree.",
|
|
38
40
|
"INVALID_DIRECTORY": "Invalid directory '{dir}': {error}. Ensure the directory exists and is accessible.",
|
|
39
41
|
"WARN_DIRECTORY_EMPTY": "Directory '{dir}' (resolved: '{resolvedPath}') is empty or contains no loadable modules (.mjs, .cjs, .js files). This is valid for add-api workflows but may indicate a misconfigured path.",
|
|
40
42
|
"INTERNAL_HOOK_STATE_CORRUPT": "[INTERNAL ERROR] HookManager state is corrupt: hook \"{hookId}\" ({type}/{subset}/{pattern}) — {detail}. This is a bug in Slothlet — please report it.",
|
|
@@ -270,6 +272,8 @@
|
|
|
270
272
|
"HINT_INVALID_CONFIG_LAZY_ASYNC_REQUIRED": "Lazy mode requires async operations. Use eager mode or implement proper async patterns.",
|
|
271
273
|
"HINT_RUNTIME_NO_ACTIVE_CONTEXT_SELF": "Cannot access 'self' outside of a slothlet API function context.",
|
|
272
274
|
"HINT_RUNTIME_NO_ACTIVE_CONTEXT_CONTEXT": "Cannot access 'context' outside of a slothlet API function. Context can only be modified within slothlet API calls.",
|
|
275
|
+
"HINT_LOOSE_SET_NOT_OWNED": "A module owning apiPath \"P\" may write only under \"P.*\". To register code at a different namespace, call api.slothlet.api.add() instead.",
|
|
276
|
+
"HINT_LOOSE_SET_RESERVED_KEY": "The segments __proto__, prototype, and constructor cannot be used as apiPath names because writing to them would mutate the JavaScript prototype chain. Choose a different name.",
|
|
273
277
|
"HINT_INVALID_CONFIG_NOT_LOADED": "The instance must be loaded before performing this operation. Call load() first.",
|
|
274
278
|
"HINT_INVALID_CONFIG_RELOAD_NOT_IMPL": "Reload functionality is not yet implemented. This will be added in a future iteration.",
|
|
275
279
|
"HINT_WARNING_RESERVED_PROPERTY_CONFLICT": "The 'slothlet' property name is reserved for built-in API methods. Rename your API module or property to avoid conflicts with slothlet.shutdown(), slothlet.api.*, etc.",
|
|
@@ -382,7 +386,6 @@
|
|
|
382
386
|
"DEBUG_VERSION_DEFAULT_USED": "No discriminator match for path {apiPath}; using default version {version}",
|
|
383
387
|
"DEBUG_VERSION_REGISTERED": "Version {version} registered at {logicalPath} (moduleID: {moduleID})",
|
|
384
388
|
"DEBUG_VERSION_UNREGISTERED": "Version {version} unregistered from {logicalPath}",
|
|
385
|
-
|
|
386
389
|
"PERMISSION_DENIED": "Access denied: caller '{caller}' is not permitted to access '{target}'",
|
|
387
390
|
"HINT_PERMISSION_DENIED": "Check permission rules for this caller/target pair. Use api.slothlet.permissions.global.rulesForPath('{target}') to inspect active rules.",
|
|
388
391
|
"PERMISSION_SELF_MODIFY": "Module '{moduleID}' cannot remove its own permission rule '{ruleId}'",
|
|
@@ -35,6 +35,8 @@
|
|
|
35
35
|
"OWNERSHIP_INVALID_MODULE_ID": "Invalid ownership registration: moduleID '{moduleID}' is invalid.",
|
|
36
36
|
"OWNERSHIP_INVALID_API_PATH": "Invalid ownership registration: apiPath '{apiPath}' is invalid.",
|
|
37
37
|
"OWNERSHIP_CONFLICT": "Ownership conflict: Path '{apiPath}' is already owned by module '{existingModuleId}', cannot assign to '{newModuleId}'. Use forceOverwrite: true or removeApi first.",
|
|
38
|
+
"LOOSE_SET_NOT_OWNED": "Module {callerApiPath} is not allowed to write to {apiPath}. A module may only write under its own apiPath; use api.slothlet.api.add() to register at a different path.",
|
|
39
|
+
"LOOSE_SET_RESERVED_KEY": "Cannot assign to reserved key segment '{segment}' in apiPath '{apiPath}'. Names like __proto__, prototype, and constructor are blocked to prevent prototype pollution of the API tree.",
|
|
38
40
|
"INVALID_DIRECTORY": "Invalid directory '{dir}': {error}. Ensure the directory exists and is accessible.",
|
|
39
41
|
"WARN_DIRECTORY_EMPTY": "Directory '{dir}' (resolved: '{resolvedPath}') is empty or contains no loadable modules (.mjs, .cjs, .js files). This is valid for add-api workflows but may indicate a misconfigured path.",
|
|
40
42
|
"INTERNAL_HOOK_STATE_CORRUPT": "[INTERNAL ERROR] HookManager state is corrupt: hook \"{hookId}\" ({type}/{subset}/{pattern}) — {detail}. This is a bug in Slothlet — please report it.",
|
|
@@ -270,6 +272,8 @@
|
|
|
270
272
|
"HINT_INVALID_CONFIG_LAZY_ASYNC_REQUIRED": "Lazy mode requires async operations. Use eager mode or implement proper async patterns.",
|
|
271
273
|
"HINT_RUNTIME_NO_ACTIVE_CONTEXT_SELF": "Cannot access 'self' outside of a slothlet API function context.",
|
|
272
274
|
"HINT_RUNTIME_NO_ACTIVE_CONTEXT_CONTEXT": "Cannot access 'context' outside of a slothlet API function. Context can only be modified within slothlet API calls.",
|
|
275
|
+
"HINT_LOOSE_SET_NOT_OWNED": "A module owning apiPath \"P\" may write only under \"P.*\". To register code at a different namespace, call api.slothlet.api.add() instead.",
|
|
276
|
+
"HINT_LOOSE_SET_RESERVED_KEY": "The segments __proto__, prototype, and constructor cannot be used as apiPath names because writing to them would mutate the JavaScript prototype chain. Choose a different name.",
|
|
273
277
|
"HINT_INVALID_CONFIG_NOT_LOADED": "The instance must be loaded before performing this operation. Call load() first.",
|
|
274
278
|
"HINT_INVALID_CONFIG_RELOAD_NOT_IMPL": "Reload functionality is not yet implemented. This will be added in a future iteration.",
|
|
275
279
|
"HINT_WARNING_RESERVED_PROPERTY_CONFLICT": "The 'slothlet' property name is reserved for built-in API methods. Rename your API module or property to avoid conflicts with slothlet.shutdown(), slothlet.api.*, etc.",
|
|
@@ -382,7 +386,6 @@
|
|
|
382
386
|
"DEBUG_VERSION_DEFAULT_USED": "No discriminator match for path {apiPath}; using default version {version}",
|
|
383
387
|
"DEBUG_VERSION_REGISTERED": "Version {version} registered at {logicalPath} (moduleID: {moduleID})",
|
|
384
388
|
"DEBUG_VERSION_UNREGISTERED": "Version {version} unregistered from {logicalPath}",
|
|
385
|
-
|
|
386
389
|
"PERMISSION_DENIED": "Access denied: caller '{caller}' is not permitted to access '{target}'",
|
|
387
390
|
"HINT_PERMISSION_DENIED": "Check permission rules for this caller/target pair. Use api.slothlet.permissions.global.rulesForPath('{target}') to inspect active rules.",
|
|
388
391
|
"PERMISSION_SELF_MODIFY": "Module '{moduleID}' cannot remove its own permission rule '{ruleId}'",
|
|
@@ -35,6 +35,8 @@
|
|
|
35
35
|
"OWNERSHIP_INVALID_MODULE_ID": "Registro de propiedad inválido: el moduleID '{moduleID}' es inválido.",
|
|
36
36
|
"OWNERSHIP_INVALID_API_PATH": "Registro de propiedad inválido: el apiPath '{apiPath}' es inválido.",
|
|
37
37
|
"OWNERSHIP_CONFLICT": "Conflicto de propiedad: la ruta '{apiPath}' ya es propiedad del módulo '{existingModuleId}', no se puede asignar a '{newModuleId}'. Usa forceOverwrite: true o removeApi primero.",
|
|
38
|
+
"LOOSE_SET_NOT_OWNED": "El módulo {callerApiPath} no puede escribir en {apiPath}. Un módulo solo puede escribir debajo de su propio apiPath; use api.slothlet.api.add() para registrar otra ruta.",
|
|
39
|
+
"LOOSE_SET_RESERVED_KEY": "No se puede asignar al segmento de clave reservada '{segment}' en apiPath '{apiPath}'. Nombres como __proto__, prototype y constructor están bloqueados para prevenir la contaminación del prototipo del árbol de la API.",
|
|
38
40
|
"INVALID_DIRECTORY": "Directorio no válido '{dir}': {error}. Asegúrate de que el directorio existe y es accesible.",
|
|
39
41
|
"WARN_DIRECTORY_EMPTY": "El directorio '{dir}' (resuelto como: '{resolvedPath}') está vacío o no contiene módulos cargables (ficheros .mjs, .cjs, .js). Esto es válido para los flujos de trabajo de add-api, pero puede indicar una ruta mal configurada.",
|
|
40
42
|
"INTERNAL_HOOK_STATE_CORRUPT": "[ERROR INTERNO] El estado de HookManager está corrupto: gancho \"{hookId}\" ({type}/{subset}/{pattern}) — {detail}. Esto es un bug en Slothlet — por favor infórmalo.",
|
|
@@ -270,6 +272,8 @@
|
|
|
270
272
|
"HINT_INVALID_CONFIG_LAZY_ASYNC_REQUIRED": "Sugerencia: El modo diferido requiere operaciones asíncronas. Usa el modo inmediato (eager) o implementa patrones asíncronos adecuados.",
|
|
271
273
|
"HINT_RUNTIME_NO_ACTIVE_CONTEXT_SELF": "No se puede acceder a 'self' fuera del contexto de una función de la API de slothlet.",
|
|
272
274
|
"HINT_RUNTIME_NO_ACTIVE_CONTEXT_CONTEXT": "No se puede acceder a 'context' fuera de una función de la API de slothlet. El contexto solo puede modificarse dentro de llamadas a la API de slothlet.",
|
|
275
|
+
"HINT_LOOSE_SET_NOT_OWNED": "Un módulo cuyo apiPath es \"P\" solo puede escribir bajo \"P.*\". Para registrar código en otro espacio de nombres, llame a api.slothlet.api.add().",
|
|
276
|
+
"HINT_LOOSE_SET_RESERVED_KEY": "Los segmentos __proto__, prototype y constructor no pueden usarse como nombres de apiPath porque escribir en ellos mutaría la cadena de prototipos de JavaScript. Elija otro nombre.",
|
|
273
277
|
"HINT_INVALID_CONFIG_NOT_LOADED": "La instancia debe estar cargada antes de realizar esta operación. Llama a load() primero.",
|
|
274
278
|
"HINT_INVALID_CONFIG_RELOAD_NOT_IMPL": "Sugerencia: La funcionalidad de recarga aún no está implementada y se añadirá en una iteración futura.",
|
|
275
279
|
"HINT_WARNING_RESERVED_PROPERTY_CONFLICT": "El nombre de propiedad 'slothlet' está reservado para métodos integrados de la API. Renombra tu módulo de API o tu propiedad para evitar conflictos con slothlet.shutdown(), slothlet.api.*, etc.",
|
|
@@ -382,7 +386,6 @@
|
|
|
382
386
|
"DEBUG_VERSION_DEFAULT_USED": "Sin coincidencia del discriminador para la ruta {apiPath}; se usa la versión predeterminada {version}.",
|
|
383
387
|
"DEBUG_VERSION_REGISTERED": "Versión {version} registrada en {logicalPath} (moduleID: {moduleID})",
|
|
384
388
|
"DEBUG_VERSION_UNREGISTERED": "Versión {version} dada de baja de {logicalPath}",
|
|
385
|
-
|
|
386
389
|
"PERMISSION_DENIED": "Acceso denegado: el llamador '{caller}' no tiene permiso para acceder a '{target}'",
|
|
387
390
|
"HINT_PERMISSION_DENIED": "Verifica las reglas de permisos para este par llamador/objetivo. Usa api.slothlet.permissions.global.rulesForPath('{target}') para inspeccionar las reglas activas.",
|
|
388
391
|
"PERMISSION_SELF_MODIFY": "El módulo '{moduleID}' no puede eliminar su propia regla de permiso '{ruleId}'",
|
|
@@ -35,6 +35,8 @@
|
|
|
35
35
|
"OWNERSHIP_INVALID_MODULE_ID": "Registro de propiedad inválido: el moduleID '{moduleID}' es inválido.",
|
|
36
36
|
"OWNERSHIP_INVALID_API_PATH": "Registro de propiedad inválido: el apiPath '{apiPath}' es inválido.",
|
|
37
37
|
"OWNERSHIP_CONFLICT": "Conflicto de propiedad: la ruta '{apiPath}' ya es propiedad del módulo '{existingModuleId}', no se puede asignar a '{newModuleId}'. Use forceOverwrite: true o removeApi primero.",
|
|
38
|
+
"LOOSE_SET_NOT_OWNED": "El módulo {callerApiPath} no puede escribir en {apiPath}. Un módulo solo puede escribir debajo de su propio apiPath; usa api.slothlet.api.add() para registrar otra ruta.",
|
|
39
|
+
"LOOSE_SET_RESERVED_KEY": "No se puede asignar al segmento de clave reservada '{segment}' en apiPath '{apiPath}'. Nombres como __proto__, prototype y constructor están bloqueados para prevenir la contaminación del prototipo del árbol de la API.",
|
|
38
40
|
"INVALID_DIRECTORY": "Directorio inválido '{dir}': {error}. Asegúrese de que el directorio existe y es accesible.",
|
|
39
41
|
"WARN_DIRECTORY_EMPTY": "El directorio '{dir}' (resuelto como: '{resolvedPath}') está vacío o no contiene módulos cargables (archivos .mjs, .cjs, .js). Esto es válido para los flujos de trabajo de add-api, pero puede indicar una ruta mal configurada.",
|
|
40
42
|
"INTERNAL_HOOK_STATE_CORRUPT": "[ERROR INTERNO] El estado de HookManager está corrupto: gancho \"{hookId}\" ({type}/{subset}/{pattern}) — {detail}. Esto es un bug en Slothlet — por favor infórmelo.",
|
|
@@ -270,6 +272,8 @@
|
|
|
270
272
|
"HINT_INVALID_CONFIG_LAZY_ASYNC_REQUIRED": "Sugerencia: El modo diferido requiere operaciones asíncronas. Use el modo inmediato (eager) o implemente patrones asíncronos adecuados.",
|
|
271
273
|
"HINT_RUNTIME_NO_ACTIVE_CONTEXT_SELF": "No se puede acceder a 'self' fuera del contexto de una función de la API de slothlet.",
|
|
272
274
|
"HINT_RUNTIME_NO_ACTIVE_CONTEXT_CONTEXT": "No se puede acceder a 'context' fuera de una función de la API de slothlet. El contexto solo puede modificarse dentro de llamadas a la API de slothlet.",
|
|
275
|
+
"HINT_LOOSE_SET_NOT_OWNED": "Un módulo cuyo apiPath es \"P\" solo puede escribir bajo \"P.*\". Para registrar código en otro espacio de nombres, llama a api.slothlet.api.add().",
|
|
276
|
+
"HINT_LOOSE_SET_RESERVED_KEY": "Los segmentos __proto__, prototype y constructor no pueden usarse como nombres de apiPath porque escribir en ellos mutaría la cadena de prototipos de JavaScript. Elige otro nombre.",
|
|
273
277
|
"HINT_INVALID_CONFIG_NOT_LOADED": "La instancia debe estar cargada antes de realizar esta operación. Llame a load() primero.",
|
|
274
278
|
"HINT_INVALID_CONFIG_RELOAD_NOT_IMPL": "Sugerencia: La funcionalidad de recarga aún no está implementada y se añadirá en una iteración futura.",
|
|
275
279
|
"HINT_WARNING_RESERVED_PROPERTY_CONFLICT": "El nombre de propiedad 'slothlet' está reservado para métodos integrados de la API. Renombre su módulo de API o su propiedad para evitar conflictos con slothlet.shutdown(), slothlet.api.*, etc.",
|
|
@@ -382,7 +386,6 @@
|
|
|
382
386
|
"DEBUG_VERSION_DEFAULT_USED": "Sin coincidencia del discriminador para la ruta {apiPath}; se usa la versión predeterminada {version}.",
|
|
383
387
|
"DEBUG_VERSION_REGISTERED": "Versión {version} registrada en {logicalPath} (moduleID: {moduleID})",
|
|
384
388
|
"DEBUG_VERSION_UNREGISTERED": "Versión {version} dada de baja de {logicalPath}",
|
|
385
|
-
|
|
386
389
|
"PERMISSION_DENIED": "Acceso denegado: el llamador '{caller}' no tiene permiso para acceder a '{target}'",
|
|
387
390
|
"HINT_PERMISSION_DENIED": "Verifique las reglas de permisos para este par llamador/objetivo. Use api.slothlet.permissions.global.rulesForPath('{target}') para inspeccionar las reglas activas.",
|
|
388
391
|
"PERMISSION_SELF_MODIFY": "El módulo '{moduleID}' no puede eliminar su propia regla de permiso '{ruleId}'",
|
|
@@ -35,6 +35,8 @@
|
|
|
35
35
|
"OWNERSHIP_INVALID_MODULE_ID": "Enregistrement de propriété invalide : le moduleID '{moduleID}' est invalide.",
|
|
36
36
|
"OWNERSHIP_INVALID_API_PATH": "Enregistrement de propriété invalide : le chemin d'API '{apiPath}' est invalide.",
|
|
37
37
|
"OWNERSHIP_CONFLICT": "Conflit de propriété : le chemin '{apiPath}' appartient déjà au module '{existingModuleId}', impossible de l'assigner à '{newModuleId}'. Utilisez forceOverwrite: true ou removeApi d'abord.",
|
|
38
|
+
"LOOSE_SET_NOT_OWNED": "Le module {callerApiPath} n’est pas autorisé à écrire dans {apiPath}. Un module ne peut écrire que sous son propre apiPath ; utilisez api.slothlet.api.add() pour enregistrer un autre chemin.",
|
|
39
|
+
"LOOSE_SET_RESERVED_KEY": "Impossible d’assigner au segment de clé réservée '{segment}' dans apiPath '{apiPath}'. Les noms tels que __proto__, prototype et constructor sont bloqués pour éviter la pollution du prototype de l’arbre d’API.",
|
|
38
40
|
"INVALID_DIRECTORY": "Répertoire invalide '{dir}' : {error}. Assurez-vous que le répertoire existe et est accessible.",
|
|
39
41
|
"WARN_DIRECTORY_EMPTY": "Le répertoire '{dir}' (résolu : '{resolvedPath}') est vide ou ne contient aucun module chargeable (fichiers .mjs, .cjs, .js). Ceci est valide pour les flux de travail add-api mais peut indiquer un chemin mal configuré.",
|
|
40
42
|
"INTERNAL_HOOK_STATE_CORRUPT": "[ERREUR INTERNE] L'état de HookManager est corrompu : hook \"{hookId}\" ({type}/{subset}/{pattern}) — {detail}. C'est un bug dans Slothlet — veuillez le signaler.",
|
|
@@ -270,6 +272,8 @@
|
|
|
270
272
|
"HINT_INVALID_CONFIG_LAZY_ASYNC_REQUIRED": "Le mode lazy nécessite des opérations asynchrones. Utilisez le mode eager ou implémentez des modèles asynchrones appropriés.",
|
|
271
273
|
"HINT_RUNTIME_NO_ACTIVE_CONTEXT_SELF": "Impossible d'accéder à 'self' en dehors du contexte d'une fonction de l'API Slothlet.",
|
|
272
274
|
"HINT_RUNTIME_NO_ACTIVE_CONTEXT_CONTEXT": "Impossible d'accéder à 'context' en dehors d'une fonction de l'API Slothlet. Le contexte ne peut être modifié que lors d'appels d'API Slothlet.",
|
|
275
|
+
"HINT_LOOSE_SET_NOT_OWNED": "Un module dont l’apiPath est \"P\" ne peut écrire que sous \"P.*\". Pour enregistrer du code dans un autre espace de noms, appelez api.slothlet.api.add().",
|
|
276
|
+
"HINT_LOOSE_SET_RESERVED_KEY": "Les segments __proto__, prototype et constructor ne peuvent pas être utilisés comme noms d’apiPath car écrire dessus modifierait la chaîne de prototypes JavaScript. Choisissez un autre nom.",
|
|
273
277
|
"HINT_INVALID_CONFIG_NOT_LOADED": "L'instance doit être chargée avant d'effectuer cette opération. Appelez d'abord load().",
|
|
274
278
|
"HINT_INVALID_CONFIG_RELOAD_NOT_IMPL": "Conseil : la fonctionnalité de rechargement n'est pas encore implémentée et sera ajoutée dans une future version.",
|
|
275
279
|
"HINT_WARNING_RESERVED_PROPERTY_CONFLICT": "Le nom de propriété 'slothlet' est réservé aux méthodes d'API intégrées. Renommez votre module ou propriété d'API pour éviter les conflits avec slothlet.shutdown(), slothlet.api.*, etc.",
|
|
@@ -382,7 +386,6 @@
|
|
|
382
386
|
"DEBUG_VERSION_DEFAULT_USED": "Aucune correspondance de discriminateur pour le chemin {apiPath} ; utilisation de la version par défaut {version}.",
|
|
383
387
|
"DEBUG_VERSION_REGISTERED": "Version {version} enregistrée à {logicalPath} (moduleID : {moduleID})",
|
|
384
388
|
"DEBUG_VERSION_UNREGISTERED": "Version {version} désinscrite de {logicalPath}",
|
|
385
|
-
|
|
386
389
|
"PERMISSION_DENIED": "Accès refusé : l'appelant '{caller}' n'est pas autorisé à accéder à '{target}'",
|
|
387
390
|
"HINT_PERMISSION_DENIED": "Vérifiez les règles d'autorisation pour ce couple appelant/cible. Utilisez api.slothlet.permissions.global.rulesForPath('{target}') pour inspecter les règles actives.",
|
|
388
391
|
"PERMISSION_SELF_MODIFY": "Le module '{moduleID}' ne peut pas supprimer sa propre règle d'autorisation '{ruleId}'",
|
|
@@ -35,6 +35,8 @@
|
|
|
35
35
|
"OWNERSHIP_INVALID_MODULE_ID": "अमान्य स्वामित्व पंजीकरण: moduleID '{moduleID}' अमान्य है।",
|
|
36
36
|
"OWNERSHIP_INVALID_API_PATH": "अमान्य स्वामित्व पंजीकरण: apiPath '{apiPath}' अमान्य है।",
|
|
37
37
|
"OWNERSHIP_CONFLICT": "स्वामित्व संघर्ष: पथ '{apiPath}' पहले से ही मॉड्यूल '{existingModuleId}' के स्वामित्व में है, '{newModuleId}' को नहीं सौंपा जा सकता। forceOverwrite: true या removeApi का उपयोग करें।",
|
|
38
|
+
"LOOSE_SET_NOT_OWNED": "मॉड्यूल {callerApiPath} {apiPath} में नहीं लिख सकता। एक मॉड्यूल केवल अपने स्वयं के apiPath के अंतर्गत लिख सकता है; किसी अन्य पथ पर पंजीकरण के लिए api.slothlet.api.add() का उपयोग करें।",
|
|
39
|
+
"LOOSE_SET_RESERVED_KEY": "apiPath '{apiPath}' में आरक्षित कुंजी सेगमेंट '{segment}' को असाइन नहीं किया जा सकता। __proto__, prototype, और constructor जैसे नाम API ट्री के प्रोटोटाइप प्रदूषण को रोकने के लिए अवरुद्ध हैं।",
|
|
38
40
|
"INVALID_DIRECTORY": "अमान्य निर्देशिका '{dir}': {error}। सुनिश्चित करें कि निर्देशिका मौजूद है और सुलभ है।",
|
|
39
41
|
"WARN_DIRECTORY_EMPTY": "निर्देशिका '{dir}' (समाधान: '{resolvedPath}') खाली है या इसमें कोई लोड करने योग्य मॉड्यूल (.mjs, .cjs, .js फ़ाइलें) नहीं हैं। यह add-api वर्कफ़्लोज़ के लिए मान्य है लेकिन गलत कॉन्फ़िगर किए गए पथ का संकेत दे सकता है।",
|
|
40
42
|
"INTERNAL_HOOK_STATE_CORRUPT": "[आंतरिक त्रुटि] HookManager स्थिति भ्रष्ट है: हुक \"{hookId}\" ({type}/{subset}/{pattern}) — {detail}। यह Slothlet में एक बग है — कृपया इसकी रिपोर्ट करें।",
|
|
@@ -270,6 +272,8 @@
|
|
|
270
272
|
"HINT_INVALID_CONFIG_LAZY_ASYNC_REQUIRED": "lazy मोड के लिए एसिंक्रोनस संचालन की आवश्यकता होती है। eager मोड का उपयोग करें या उचित एसिंक्रोनस पैटर्न लागू करें।",
|
|
271
273
|
"HINT_RUNTIME_NO_ACTIVE_CONTEXT_SELF": "slothlet API फ़ंक्शन संदर्भ के बाहर 'self' तक नहीं पहुँचा जा सकता।",
|
|
272
274
|
"HINT_RUNTIME_NO_ACTIVE_CONTEXT_CONTEXT": "slothlet API फ़ंक्शन के बाहर 'context' तक नहीं पहुँचा जा सकता। संदर्भ को केवल slothlet API कॉल के भीतर ही संशोधित किया जा सकता है।",
|
|
275
|
+
"HINT_LOOSE_SET_NOT_OWNED": "जिस मॉड्यूल का apiPath \"P\" है, वह केवल \"P.*\" के अंतर्गत लिख सकता है। किसी अन्य नेमस्पेस में कोड पंजीकृत करने के लिए api.slothlet.api.add() को कॉल करें।",
|
|
276
|
+
"HINT_LOOSE_SET_RESERVED_KEY": "सेगमेंट __proto__, prototype, और constructor को apiPath नामों के रूप में उपयोग नहीं किया जा सकता क्योंकि उन पर लिखने से JavaScript प्रोटोटाइप श्रृंखला उत्परिवर्तित हो जाएगी। अलग नाम चुनें।",
|
|
273
277
|
"HINT_INVALID_CONFIG_NOT_LOADED": "इस ऑपरेशन को करने से पहले इंस्टेंस लोड होना चाहिए। पहले load() कॉल करें।",
|
|
274
278
|
"HINT_INVALID_CONFIG_RELOAD_NOT_IMPL": "रीलोड कार्यक्षमता अभी लागू नहीं हुई है। इसे भविष्य के पुनरावृत्ति में जोड़ा जाएगा।",
|
|
275
279
|
"HINT_WARNING_RESERVED_PROPERTY_CONFLICT": "'slothlet' गुण नाम बिल्ट-इन API विधियों के लिए आरक्षित है। slothlet.shutdown(), slothlet.api.*, आदि के साथ संघर्ष से बचने के लिए अपने API मॉड्यूल या गुण का नाम बदलें।",
|
|
@@ -382,7 +386,6 @@
|
|
|
382
386
|
"DEBUG_VERSION_DEFAULT_USED": "पथ {apiPath} के लिए कोई Discriminator मिलान नहीं; डिफ़ॉल्ट संस्करण {version} का उपयोग किया जा रहा है।",
|
|
383
387
|
"DEBUG_VERSION_REGISTERED": "संस्करण {version} को {logicalPath} पर पंजीकृत किया गया (moduleID: {moduleID})",
|
|
384
388
|
"DEBUG_VERSION_UNREGISTERED": "संस्करण {version} को {logicalPath} से हटाया गया",
|
|
385
|
-
|
|
386
389
|
"PERMISSION_DENIED": "पहुँच अस्वीकृत: कॉलर '{caller}' को '{target}' तक पहुँचने की अनुमति नहीं है",
|
|
387
390
|
"HINT_PERMISSION_DENIED": "इस कॉलर/लक्ष्य जोड़ी के लिए अनुमति नियम जाँचें। सक्रिय नियमों का निरीक्षण करने के लिए api.slothlet.permissions.global.rulesForPath('{target}') का उपयोग करें।",
|
|
388
391
|
"PERMISSION_SELF_MODIFY": "मॉड्यूल '{moduleID}' अपना स्वयं का अनुमति नियम '{ruleId}' नहीं हटा सकता",
|
|
@@ -35,6 +35,8 @@
|
|
|
35
35
|
"OWNERSHIP_INVALID_MODULE_ID": "無効な所有権登録: moduleID '{moduleID}' が無効です。",
|
|
36
36
|
"OWNERSHIP_INVALID_API_PATH": "無効な所有権登録: apiPath '{apiPath}' が無効です。",
|
|
37
37
|
"OWNERSHIP_CONFLICT": "所有権の競合: パス '{apiPath}' は既にモジュール '{existingModuleId}' によって所有されており、'{newModuleId}' に割り当てることはできません。forceOverwrite: true を使用するか、先に removeApi を呼び出してください。",
|
|
38
|
+
"LOOSE_SET_NOT_OWNED": "モジュール {callerApiPath} は {apiPath} に書き込めません。モジュールは自身の apiPath 配下にのみ書き込めます。別のパスに登録するには api.slothlet.api.add() を使用してください。",
|
|
39
|
+
"LOOSE_SET_RESERVED_KEY": "apiPath '{apiPath}' の予約キーセグメント '{segment}' に代入できません。__proto__、prototype、constructor などの名前は API ツリーのプロトタイプ汚染を防ぐためブロックされています。",
|
|
38
40
|
"INVALID_DIRECTORY": "無効なディレクトリ '{dir}' です: {error}。ディレクトリが存在し、アクセス可能であることを確認してください。",
|
|
39
41
|
"WARN_DIRECTORY_EMPTY": "ディレクトリ '{dir}' (解決済みパス: '{resolvedPath}') は空であるか、読み込み可能なモジュール (.mjs, .cjs, .js ファイル) が含まれていません。これは add-api ワークフローでは有効ですが、構成ミスの可能性があります。",
|
|
40
42
|
"INTERNAL_HOOK_STATE_CORRUPT": "[内部エラー] HookManager の状態が破損しています: フック \"{hookId}\" ({type}/{subset}/{pattern}) — {detail}。これは Slothlet のバグです。報告してください。",
|
|
@@ -270,6 +272,8 @@
|
|
|
270
272
|
"HINT_INVALID_CONFIG_LAZY_ASYNC_REQUIRED": "lazy モードには非同期操作が必要です。 eager モードを使用するか、適切な非同期パターンを実装してください。",
|
|
271
273
|
"HINT_RUNTIME_NO_ACTIVE_CONTEXT_SELF": "Slothlet API 関数のコンテキスト外から 'self' にアクセスすることはできません。",
|
|
272
274
|
"HINT_RUNTIME_NO_ACTIVE_CONTEXT_CONTEXT": "Slothlet API 関数の外から 'context' にアクセスすることはできません。コンテキストは Slothlet API 呼び出し内でのみ変更可能です。",
|
|
275
|
+
"HINT_LOOSE_SET_NOT_OWNED": "apiPath が \"P\" のモジュールは \"P.*\" の下にのみ書き込めます。別の名前空間にコードを登録するには api.slothlet.api.add() を呼び出してください。",
|
|
276
|
+
"HINT_LOOSE_SET_RESERVED_KEY": "__proto__、prototype、constructor のセグメントは、それらに書き込むと JavaScript のプロトタイプチェーンが変更されてしまうため、apiPath 名として使用できません。別の名前を選んでください。",
|
|
273
277
|
"HINT_INVALID_CONFIG_NOT_LOADED": "この操作を実行する前にインスタンスを読み込む必要があります。先に load() を呼び出してください。",
|
|
274
278
|
"HINT_INVALID_CONFIG_RELOAD_NOT_IMPL": "ヒント: 再読み込み機能はまだ実装されていません。将来のイテレーションで追加される予定です。",
|
|
275
279
|
"HINT_WARNING_RESERVED_PROPERTY_CONFLICT": "'slothlet' というプロパティ名はビルトイン API メソッド用に予約されています。 slothlet.shutdown() や slothlet.api.* との衝突を避けるために、 API モジュール名またはプロパティ名を変更してください。",
|
|
@@ -382,7 +386,6 @@
|
|
|
382
386
|
"DEBUG_VERSION_DEFAULT_USED": "パス {apiPath} の Discriminator に一致なし。デフォルトバージョン {version} を使用します。",
|
|
383
387
|
"DEBUG_VERSION_REGISTERED": "バージョン {version} が {logicalPath} に登録されました(moduleID: {moduleID})",
|
|
384
388
|
"DEBUG_VERSION_UNREGISTERED": "バージョン {version} が {logicalPath} から登録解除されました",
|
|
385
|
-
|
|
386
389
|
"PERMISSION_DENIED": "アクセス拒否: 呼び出し元 '{caller}' は '{target}' へのアクセスが許可されていません",
|
|
387
390
|
"HINT_PERMISSION_DENIED": "この呼び出し元/ターゲットのペアのパーミッションルールを確認してください。api.slothlet.permissions.global.rulesForPath('{target}') を使用してアクティブなルールを検査できます。",
|
|
388
391
|
"PERMISSION_SELF_MODIFY": "モジュール '{moduleID}' は自身のパーミッションルール '{ruleId}' を削除できません",
|
|
@@ -35,6 +35,8 @@
|
|
|
35
35
|
"OWNERSHIP_INVALID_MODULE_ID": "유효하지 않은 소유권 등록: moduleID '{moduleID}'이(가) 유효하지 않습니다.",
|
|
36
36
|
"OWNERSHIP_INVALID_API_PATH": "유효하지 않은 소유권 등록: apiPath '{apiPath}'이(가) 유효하지 않습니다.",
|
|
37
37
|
"OWNERSHIP_CONFLICT": "소유권 충돌: 경로 '{apiPath}'은(는) 이미 모듈 '{existingModuleId}'이(가) 소유하고 있습니다. '{newModuleId}'에 할당할 수 없습니다. forceOverwrite: true를 사용하거나 먼저 removeApi를 호출하십시오.",
|
|
38
|
+
"LOOSE_SET_NOT_OWNED": "모듈 {callerApiPath}은(는) {apiPath}에 쓸 수 없습니다. 모듈은 자신의 apiPath 아래에서만 쓸 수 있습니다. 다른 경로에 등록하려면 api.slothlet.api.add()를 사용하세요.",
|
|
39
|
+
"LOOSE_SET_RESERVED_KEY": "apiPath '{apiPath}'의 예약된 키 세그먼트 '{segment}'에 할당할 수 없습니다. __proto__, prototype, constructor와 같은 이름은 API 트리의 프로토타입 오염을 방지하기 위해 차단됩니다.",
|
|
38
40
|
"INVALID_DIRECTORY": "유효하지 않은 디렉토리 '{dir}': {error}. 디렉토리가 존재하고 액세스 가능한지 확인하십시오.",
|
|
39
41
|
"WARN_DIRECTORY_EMPTY": "디렉토리 '{dir}'(확인된 경로: '{resolvedPath}')이(가) 비어 있거나 로드 가능한 모듈(.mjs, .cjs, .js 파일)이 없습니다. 이는 add-api 워크플로에서는 유효하지만 잘못 설정된 경로일 수 있습니다.",
|
|
40
42
|
"INTERNAL_HOOK_STATE_CORRUPT": "[내부 오류] HookManager 상태가 손상됨: 후크 \"{hookId}\" ({type}/{subset}/{pattern}) — {detail}. 이는 Slothlet의 버그입니다. 보고해 주십시오.",
|
|
@@ -270,6 +272,8 @@
|
|
|
270
272
|
"HINT_INVALID_CONFIG_LAZY_ASYNC_REQUIRED": "지연 모드에는 비동기 작업이 필요합니다. 즉시 모드를 사용하거나 적절한 비동기 패턴을 구현하십시오.",
|
|
271
273
|
"HINT_RUNTIME_NO_ACTIVE_CONTEXT_SELF": "Slothlet API 함수 컨텍스트 밖에서는 'self'에 액세스할 수 없습니다.",
|
|
272
274
|
"HINT_RUNTIME_NO_ACTIVE_CONTEXT_CONTEXT": "Slothlet API 함수 밖에서는 'context'에 액세스할 수 없습니다. 컨텍스트는 Slothlet API 호출 내에서만 수정할 수 있습니다.",
|
|
275
|
+
"HINT_LOOSE_SET_NOT_OWNED": "apiPath가 \"P\"인 모듈은 \"P.*\" 아래에만 쓸 수 있습니다. 다른 네임스페이스에 코드를 등록하려면 api.slothlet.api.add()를 호출하세요.",
|
|
276
|
+
"HINT_LOOSE_SET_RESERVED_KEY": "__proto__, prototype, constructor 세그먼트는 이들에 쓰면 JavaScript 프로토타입 체인이 변경되므로 apiPath 이름으로 사용할 수 없습니다. 다른 이름을 선택하세요.",
|
|
273
277
|
"HINT_INVALID_CONFIG_NOT_LOADED": "이 작업을 수행하기 전에 인스턴스를 로드해야 합니다. 먼저 load()를 호출하십시오.",
|
|
274
278
|
"HINT_INVALID_CONFIG_RELOAD_NOT_IMPL": "힌트: 재로드 기능은 아직 구현되지 않았습니다. 향후 버전에서 추가될 예정입니다.",
|
|
275
279
|
"HINT_WARNING_RESERVED_PROPERTY_CONFLICT": "'slothlet' 속성 이름은 내장 API 메서드를 위해 예약되어 있습니다. slothlet.shutdown(), slothlet.api.* 등과의 충돌을 피하기 위해 API 모듈이나 속성 이름을 바꾸십시오.",
|
|
@@ -382,7 +386,6 @@
|
|
|
382
386
|
"DEBUG_VERSION_DEFAULT_USED": "경로 {apiPath}에 대한 Discriminator 일치 없음; 기본 버전 {version}을(를) 사용합니다.",
|
|
383
387
|
"DEBUG_VERSION_REGISTERED": "버전 {version}이(가) {logicalPath}에 등록됨 (moduleID: {moduleID})",
|
|
384
388
|
"DEBUG_VERSION_UNREGISTERED": "버전 {version}이(가) {logicalPath}에서 등록 취소됨",
|
|
385
|
-
|
|
386
389
|
"PERMISSION_DENIED": "접근 거부: 호출자 '{caller}'은(는) '{target}'에 접근할 권한이 없습니다",
|
|
387
390
|
"HINT_PERMISSION_DENIED": "이 호출자/대상 쌍의 권한 규칙을 확인하세요. api.slothlet.permissions.global.rulesForPath('{target}')를 사용하여 활성 규칙을 검사할 수 있습니다.",
|
|
388
391
|
"PERMISSION_SELF_MODIFY": "모듈 '{moduleID}'은(는) 자신의 권한 규칙 '{ruleId}'을(를) 제거할 수 없습니다",
|