@cldmv/slothlet 3.12.0 → 3.12.2
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 +7 -7
- package/REFERENCE.md +1 -0
- package/devcheck.mjs +99 -0
- package/dist/lib/handlers/trusted-root.mjs +1 -1
- package/dist/lib/helpers/generate-manifest.mjs +1 -1
- package/dist/lib/processors/type-generator.mjs +1 -1
- package/dist/lib/runtime/runtime-asynclocalstorage.mjs +1 -1
- package/dist/lib/runtime/runtime-livebindings.mjs +1 -1
- package/package.json +7 -6
package/README.md
CHANGED
|
@@ -43,19 +43,18 @@ Every feature has been hardened with a comprehensive test suite - over **5,300 t
|
|
|
43
43
|
|
|
44
44
|
## ✨ What's New
|
|
45
45
|
|
|
46
|
-
### Latest: v3.12.
|
|
46
|
+
### Latest: v3.12.2 (July 2026)
|
|
47
47
|
|
|
48
|
-
- **
|
|
49
|
-
- **
|
|
50
|
-
-
|
|
51
|
-
- [View full v3.12.0 Changelog](./docs/changelog/v3/v3.12.0.md)
|
|
48
|
+
- **Type generation now types both JS and TS leaves faithfully (#213, #219)** — a generated `.d.ts` carries the real JSDoc `@param` / `@returns` types of JavaScript leaves instead of `any` (extraction runs through the TypeScript checker, so dotted `@param {object}` shapes, optionals, unions, and generics all reflect into proper signatures), and it now compiles for TypeScript leaves that reference a local `interface` / `type` / `enum` — those types are emitted alongside the declaration via TypeScript's own emitter, where the output previously referenced an undefined name. The generator's acceptance test `tsc`-compiles the emitted declaration to prove it.
|
|
49
|
+
- **Consumer-coverage testing guide (#217)** — new [docs/TESTING.md](./docs/TESTING.md) explains why a slothlet-composed leaf reads as near-zero covered in a **consumer** project (its externalized `import()` bypasses vitest's module runner, so `@vitest/coverage-v8` never attributes it) and fixes it with one config line: add `@cldmv/slothlet` to `server.deps.inline`.
|
|
50
|
+
- [View full v3.12.2 Changelog](./docs/changelog/v3/v3.12.2.md)
|
|
52
51
|
|
|
53
52
|
### Recent Releases
|
|
54
53
|
|
|
54
|
+
- **v3.12.1** (July 2026) — Security patch: nested values of `scope({ protect, owners })` context keys are now guarded to depth — `context.auth.userId = …` throws `CONTEXT_KEY_PROTECTED` with the full path — plus the `./devcheck` export now ships the file the npm whitelist never included ([Changelog](./docs/changelog/v3/v3.12.1.md))
|
|
55
|
+
- **v3.12.0** (July 2026) — Security-and-observability: permission enforcement fails closed on an absent/forged caller (opt-out `permissions.failOpenOnAbsentCaller`), inter-module construction + class-instance methods are permission-checked, the engine-internal `handlers/`/`factories/` subpaths leave `exports`, an opt-in control-surface `seal()`, owner-locked/write-protected context keys via `scope({ protect, owners })`, `impl:warning`/`impl:error` diagnostic lifecycle events, and nested `shutdown`/`destroy` leaves no longer dropped ([Changelog](./docs/changelog/v3/v3.12.0.md))
|
|
55
56
|
- **v3.11.1** (June 2026) — OpenSSF Scorecard publishing fixed: the workflow calls the org reusable `reusable-scorecard.yml@v4` instead of an inline job whose composite checkout step failed Scorecard's publish-verification allowlist (CI-only, #173) ([Changelog](./docs/changelog/v3/v3.11.1.md))
|
|
56
57
|
- **v3.11.0** (June 2026) — Satellite split: non-base locales and TypeScript declarations move to the optional `@cldmv/slothlet-i18n` and `@cldmv/slothlet-types` packages (en-us-only lean core); loader `hidden` option (`.`/`__` hidden by default); browser-mode v8 coverage with an unbalanced-`v8 ignore` analyze gate ([Changelog](./docs/changelog/v3/v3.11.0.md))
|
|
57
|
-
- **v3.10.0** (June 2026) — Synthetic / in-memory leaves for `api.slothlet.api.add()` (inline function or export map, no temp file); hooks integrated with the permission system (gated registration/firing, owner-pinned, `pattern:type` selectors); browser importmap built from the full public export surface ([Changelog](./docs/changelog/v3/v3.10.0.md))
|
|
58
|
-
- **v3.9.2** (May 2026) — Browser mode actually loadable: `generateBrowserAssets()` returns the API manifest **and** slothlet's own importmap; fixes an async double-wrap blow-up on chainable instances (#124), the dead global hook pattern filter (#125), and `npm run docs:build` (#121) ([Changelog](./docs/changelog/v3/v3.9.2.md))
|
|
59
58
|
|
|
60
59
|
📚 **For complete version history and detailed release notes, see [docs/changelog/](./docs/changelog/) folder.**
|
|
61
60
|
|
|
@@ -409,6 +408,7 @@ try {
|
|
|
409
408
|
- **[Sanitization](./docs/SANITIZATION.md)** — filename → property-name transformation rules
|
|
410
409
|
- **[TypeScript Support](./docs/TYPESCRIPT.md)** — fast mode (esbuild), strict mode (tsc), `.d.ts` generation
|
|
411
410
|
- **[Internationalization](./docs/I18N.md)** — supported languages and configuration
|
|
411
|
+
- **[Testing & Coverage](./docs/TESTING.md)** — measuring coverage of composition-loaded leaves in a consumer project (the `server.deps.inline` requirement)
|
|
412
412
|
|
|
413
413
|
### API Rules & Transformation
|
|
414
414
|
|
package/REFERENCE.md
CHANGED
|
@@ -21,5 +21,6 @@ Full documentation lives in the GitHub repository.
|
|
|
21
21
|
| Lifecycle | [docs/LIFECYCLE.md](https://github.com/CLDMV/slothlet/blob/master/docs/LIFECYCLE.md) |
|
|
22
22
|
| Hot reload | [docs/RELOAD.md](https://github.com/CLDMV/slothlet/blob/master/docs/RELOAD.md) |
|
|
23
23
|
| Performance | [docs/PERFORMANCE.md](https://github.com/CLDMV/slothlet/blob/master/docs/PERFORMANCE.md) |
|
|
24
|
+
| Testing & coverage | [docs/TESTING.md](https://github.com/CLDMV/slothlet/blob/master/docs/TESTING.md) |
|
|
24
25
|
| Changelog | [docs/changelog/](https://github.com/CLDMV/slothlet/tree/master/docs/changelog) |
|
|
25
26
|
| Agent/AI usage guide | [AGENT-USAGE.md](https://github.com/CLDMV/slothlet/blob/master/AGENT-USAGE.md) |
|
package/devcheck.mjs
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @Project: @cldmv/slothlet
|
|
3
|
+
* @Filename: /devcheck.mjs
|
|
4
|
+
* @Date: 2025-09-09T08:06:19-07:00 (1757430379)
|
|
5
|
+
* @Author: Nate Corcoran <CLDMV>
|
|
6
|
+
* @Email: <Shinrai@users.noreply.github.com>
|
|
7
|
+
* -----
|
|
8
|
+
* @Last modified by: Nate Corcoran <CLDMV> (Shinrai@users.noreply.github.com)
|
|
9
|
+
* @Last modified time: 2026-03-01 20:21:36 -08:00 (1772425296)
|
|
10
|
+
* -----
|
|
11
|
+
* @Copyright: Copyright (c) 2013-2026 Catalyzed Motivation Inc. All rights reserved.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { existsSync } from "node:fs";
|
|
15
|
+
import { fileURLToPath } from "node:url";
|
|
16
|
+
import path from "node:path";
|
|
17
|
+
|
|
18
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
19
|
+
const __dirname = path.dirname(__filename);
|
|
20
|
+
const srcPath = path.join(__dirname, "src");
|
|
21
|
+
const distPath = path.join(__dirname, "dist");
|
|
22
|
+
|
|
23
|
+
// Detect if we're running in a CI environment
|
|
24
|
+
const isCI = !!(
|
|
25
|
+
process.env.CI || // Generic CI flag
|
|
26
|
+
process.env.GITHUB_ACTIONS || // GitHub Actions
|
|
27
|
+
process.env.TRAVIS || // Travis CI
|
|
28
|
+
process.env.CIRCLECI || // CircleCI
|
|
29
|
+
process.env.GITLAB_CI || // GitLab CI
|
|
30
|
+
process.env.BUILDKITE || // Buildkite
|
|
31
|
+
process.env.JENKINS_URL || // Jenkins
|
|
32
|
+
process.env.TF_BUILD // Azure DevOps
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
if (existsSync(srcPath) && !existsSync(distPath) && !isCI) {
|
|
36
|
+
const nodeEnv = process.env.NODE_ENV?.toLowerCase();
|
|
37
|
+
const nodeOptions = process.env.NODE_OPTIONS || "";
|
|
38
|
+
|
|
39
|
+
// Check if running from node_modules (parent folder is node_modules)
|
|
40
|
+
const parentFolder = path.basename(path.dirname(__dirname));
|
|
41
|
+
const isInstalledPackage = parentFolder === "node_modules";
|
|
42
|
+
|
|
43
|
+
// Parse conditions from NODE_OPTIONS
|
|
44
|
+
const hasSlothletDev = nodeOptions.indexOf("--conditions=slothlet-dev") !== -1;
|
|
45
|
+
const hasGenericDev = nodeOptions.indexOf("--conditions=development") !== -1;
|
|
46
|
+
const hasDevEnv = nodeEnv === "dev" || nodeEnv === "development";
|
|
47
|
+
|
|
48
|
+
// Only check if we're in the slothlet repo (not installed in node_modules)
|
|
49
|
+
if (!isInstalledPackage && !hasSlothletDev) {
|
|
50
|
+
let errorMessage;
|
|
51
|
+
|
|
52
|
+
// Determine which error message to show based on current state
|
|
53
|
+
if (hasDevEnv && hasGenericDev) {
|
|
54
|
+
// They have generic dev settings - tell them to switch to slothlet-dev
|
|
55
|
+
errorMessage =
|
|
56
|
+
"❌ Incorrect development environment detected!\n" +
|
|
57
|
+
"📁 You have generic development settings (NODE_ENV=development, --conditions=development).\n" +
|
|
58
|
+
"\n" +
|
|
59
|
+
"⚠️ This causes slothlet to load from src/ which breaks tests and builds!\n" +
|
|
60
|
+
"\n" +
|
|
61
|
+
"🔧 Switch to slothlet-specific development settings:\n" +
|
|
62
|
+
" Windows (cmd):\n" +
|
|
63
|
+
" set NODE_OPTIONS=--conditions=slothlet-dev\n" +
|
|
64
|
+
"\n" +
|
|
65
|
+
" Windows (PowerShell):\n" +
|
|
66
|
+
" $env:NODE_OPTIONS='--conditions=slothlet-dev'\n" +
|
|
67
|
+
"\n" +
|
|
68
|
+
" Unix/Linux/macOS:\n" +
|
|
69
|
+
" export NODE_OPTIONS=--conditions=slothlet-dev\n" +
|
|
70
|
+
"\n" +
|
|
71
|
+
"💡 Using 'slothlet-dev' prevents conflicts with apps that use slothlet.";
|
|
72
|
+
} else {
|
|
73
|
+
// They don't have proper settings at all
|
|
74
|
+
errorMessage =
|
|
75
|
+
"❌ Development environment not properly configured!\n" +
|
|
76
|
+
"📁 Source folder detected but NODE_ENV/NODE_OPTIONS not set for slothlet development.\n" +
|
|
77
|
+
"\n" +
|
|
78
|
+
"🔧 To fix this, run one of these commands:\n" +
|
|
79
|
+
" Windows (cmd):\n" +
|
|
80
|
+
" set NODE_ENV=development\n" +
|
|
81
|
+
" set NODE_OPTIONS=--conditions=slothlet-dev\n" +
|
|
82
|
+
"\n" +
|
|
83
|
+
" Windows (PowerShell):\n" +
|
|
84
|
+
" $env:NODE_ENV='development'\n" +
|
|
85
|
+
" $env:NODE_OPTIONS='--conditions=slothlet-dev'\n" +
|
|
86
|
+
"\n" +
|
|
87
|
+
" Unix/Linux/macOS:\n" +
|
|
88
|
+
" export NODE_ENV=development\n" +
|
|
89
|
+
" export NODE_OPTIONS=--conditions=slothlet-dev\n" +
|
|
90
|
+
"\n" +
|
|
91
|
+
"💡 This ensures slothlet loads from src/ instead of dist/ for development.\n" +
|
|
92
|
+
"🔧 Using 'slothlet-dev' prevents conflicts with consumer development settings.\n" +
|
|
93
|
+
"🚀 CI environments automatically skip this check.";
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
console.error(errorMessage);
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -14,4 +14,4 @@
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import{SlothletError}from"@cldmv/slothlet/errors";const TRUSTED_ROOT=Symbol("slothlet.trustedRoot");const genuineWrappers=new WeakSet;const PROTECT_SENTINEL=Symbol("slothlet.protect");function enforceContextKeyWrite(ctx,prop){const owners=ctx?.__contextOwners;if(!owners||!Object.prototype.hasOwnProperty.call(owners,
|
|
17
|
+
import{SlothletError}from"@cldmv/slothlet/errors";const TRUSTED_ROOT=Symbol("slothlet.trustedRoot");const genuineWrappers=new WeakSet;const PROTECT_SENTINEL=Symbol("slothlet.protect");function enforceContextKeyWrite(ctx,prop){enforceOwnedContextWrite(ctx,prop,String(prop))}function enforceOwnedContextWrite(ctx,ownerKey,pathKey,writerCtx=ctx){const owners=ctx?.__contextOwners;if(!owners||!Object.prototype.hasOwnProperty.call(owners,ownerKey))return;const owner=owners[ownerKey];const writer=writerCtx.currentWrapper?.____slothletInternal?.apiPath??null;if(owner===PROTECT_SENTINEL){throw new SlothletError("CONTEXT_KEY_PROTECTED",{key:pathKey,owner:"protected",writer:writer??"host"},null,{validationError:true})}const owned=writer!==null&&(writer===owner||writer.startsWith(owner+"."));if(!owned){throw new SlothletError("CONTEXT_KEY_PROTECTED",{key:pathKey,owner:String(owner),writer:writer??"host"},null,{validationError:true})}}const PROTECTED_VIEW_CACHE=Symbol("slothlet.protectedViewCache");function isWrappableContextValue(value){if(value===null||typeof value!=="object")return false;if(Array.isArray(value))return true;const proto=Object.getPrototypeOf(value);return proto===Object.prototype||proto===null}function makeProtectedContextView(ctx,getContext,ownerKey,value,path){if(!isWrappableContextValue(value))return value;let caches=ctx[PROTECTED_VIEW_CACHE];if(!caches)caches=ctx[PROTECTED_VIEW_CACHE]=new Map;let cache=caches.get(ownerKey);if(!cache)caches.set(ownerKey,cache=new WeakMap);const cached=cache.get(value);if(cached)return cached;const view=new Proxy(value,{get(target,prop,receiver){return makeProtectedContextView(ctx,getContext,ownerKey,Reflect.get(target,prop,receiver),`${path}.${String(prop)}`)},set(target,prop,newValue,receiver){enforceOwnedContextWrite(ctx,ownerKey,`${path}.${String(prop)}`,getContext()??ctx);return Reflect.set(target,prop,newValue,receiver)},defineProperty(target,prop,descriptor){enforceOwnedContextWrite(ctx,ownerKey,`${path}.${String(prop)}`,getContext()??ctx);return Reflect.defineProperty(target,prop,descriptor)},deleteProperty(target,prop){enforceOwnedContextWrite(ctx,ownerKey,`${path}.${String(prop)}`,getContext()??ctx);return Reflect.deleteProperty(target,prop)}});cache.set(value,view);return view}function readProtectedContextValue(ctx,prop,getContext){const owners=ctx.__contextOwners;if(owners&&Object.prototype.hasOwnProperty.call(owners,prop)){return makeProtectedContextView(ctx,getContext,prop,ctx.context[prop],String(prop))}return ctx.context[prop]}export{PROTECT_SENTINEL,TRUSTED_ROOT,enforceContextKeyWrite,genuineWrappers,readProtectedContextValue};
|
|
@@ -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{fileURLToPath}from"node:url";import{SlothletError}from"@cldmv/slothlet/errors";const SLOTHLET_SPEC_RE=/["'](@cldmv\/slothlet(?:\/[^"']+)?)["']/g;const DEFAULT_SLOTHLET_BASE="/node_modules/@cldmv/slothlet/";const LOADABLE_EXTENSIONS=new Set([".mjs",".cjs",".js",".ts",".mts",".cts"]);const SKIP_PREFIXES=["__","."];function isApiFile(filename){const ext=path.extname(filename);return LOADABLE_EXTENSIONS.has(ext)}function makeFileEntry(relativePath){const fullName=path.basename(relativePath);const name=path.basename(relativePath,path.extname(relativePath));return{path:relativePath,name,fullName}}async function scanDir(absDir,rootDir){let entries;try{entries=await fs.readdir(absDir,{withFileTypes:true})}catch{return{files:[],directories:[]}}const files=[];const directories=[];for(const entry of entries){if(SKIP_PREFIXES.some(p=>entry.name.startsWith(p)))continue;if(entry.isFile()){if(isApiFile(entry.name)){const absPath=path.join(absDir,entry.name);const rel=path.relative(rootDir,absPath).replace(/\\/g,"/");files.push(makeFileEntry(rel))}}else if(entry.isDirectory()){const absSubDir=path.join(absDir,entry.name);const relDir=path.relative(rootDir,absSubDir).replace(/\\/g,"/");const children=await scanDir(absSubDir,rootDir);if(children.files.length>0||children.directories.length>0){directories.push({name:entry.name,path:relDir,children})}}}return{files,directories}}async function generateManifest(dir){if(!dir||typeof dir!=="string"){const received=typeof dir==="string"?"<empty>":dir===null?"null":Array.isArray(dir)?"array":typeof dir;throw new SlothletError("GENERATE_MANIFEST_DIR_INVALID",{received},null,{validationError:true})}const absDir=path.resolve(dir);let stat;try{stat=await fs.stat(absDir)}catch(err){throw new SlothletError("GENERATE_MANIFEST_DIR_UNREADABLE",{dir:absDir,reason:err.message},null,{validationError:true})}if(!stat.isDirectory()){throw new SlothletError("GENERATE_MANIFEST_NOT_DIRECTORY",{dir:absDir},null,{validationError:true})}return scanDir(absDir,absDir)}function slothletPackageRoot(){return path.resolve(path.dirname(fileURLToPath(import.meta.url)),"../../..")}async function collectSlothletSpecifiers(root){const specifiers=new Set(["@cldmv/slothlet"]);const pkg=JSON.parse(await fs.readFile(path.join(root,"package.json"),"utf8"));for(const key of Object.keys(pkg.exports)){if(key.includes("*")||key.endsWith(".json"))continue;specifiers.add(key==="."?"@cldmv/slothlet":`@cldmv/slothlet${key.slice(1)}`)}for(const[key,value]of Object.entries(pkg.exports)){if(!key.includes("*")||key.startsWith("./i18n/language/"))continue;const specPrefix=`@cldmv/slothlet${key.slice(1)}`.split("*")[0];const targets=[];(function collectTargets(v){if(typeof v==="string"){if(v.includes("*")&&v.endsWith(".mjs"))targets.push(v)}else if(v&&typeof v==="object"){for(const child of Object.values(v))collectTargets(child)}})(value);for(const tmpl of targets){const star=tmpl.indexOf("*");const dirRel=tmpl.slice(0,star);const suffix=tmpl.slice(star+1);let files;try{files=await fs.readdir(path.join(root,dirRel),{recursive:true})}catch{continue}for(const f of files){const norm=String(f).replace(/\\/g,"/");if(norm.endsWith(suffix))specifiers.add(specPrefix+norm.slice(0,-suffix.length))}}}for(const[key,value]of Object.entries(pkg.imports??{})){if(!key.includes("*"))continue;const specPrefix=key.split("*")[0];const targets=[];(function collectTargets(v){if(typeof v==="string"){if(v.includes("*")&&v.endsWith(".mjs"))targets.push(v)}else if(v&&typeof v==="object"){for(const child of Object.values(v))collectTargets(child)}})(value);for(const tmpl of targets){const star=tmpl.indexOf("*");const dirRel=tmpl.slice(0,star);const suffix=tmpl.slice(star+1);let files;try{files=await fs.readdir(path.join(root,dirRel),{recursive:true})}catch{continue}for(const f of files){const norm=String(f).replace(/\\/g,"/");if(norm.endsWith(suffix))specifiers.add(specPrefix+norm.slice(0,-suffix.length))}}}const SKIP_DIRS=new Set(["node_modules","types","coverage","tmp","tests","api_tests",".git","docs"]);async function scan(dir){const entries=await fs.readdir(dir,{withFileTypes:true});for(const e of entries){if(e.isDirectory()){if(!SKIP_DIRS.has(e.name)&&!e.name.startsWith("."))await scan(path.join(dir,e.name))}else if(/\.(mjs|cjs|js)$/.test(e.name)){const src=await fs.readFile(path.join(dir,e.name),"utf8");let m;SLOTHLET_SPEC_RE.lastIndex=0;while(m=SLOTHLET_SPEC_RE.exec(src)){const spec=m[1];if(spec.startsWith("@cldmv/slothlet/i18n/language/"))continue;if(/[*<>]|\.\.\.|\/$/.test(spec)||spec.split("/").some(s=>s===""))continue;specifiers.add(spec)}}}}await scan(root);return specifiers}async function generateImportMap(slothletBase=DEFAULT_SLOTHLET_BASE){const base=String(slothletBase).endsWith("/")?String(slothletBase):`${slothletBase}/`;const root=slothletPackageRoot();const imports={};for(const spec of await collectSlothletSpecifiers(root)){let resolved;try{resolved=import.meta.resolve(spec)}catch{continue}const rel=path.relative(root,fileURLToPath(resolved)).replace(/\\/g,"/");imports[spec]=base+rel}try{const sampleDir=path.dirname(fileURLToPath(import.meta.resolve("@cldmv/slothlet/i18n/language/en-us.json")));for(const f of(await fs.readdir(sampleDir)).filter(n=>n.endsWith(".json"))){const rel=path.relative(root,path.join(sampleDir,f)).replace(/\\/g,"/");imports[`@cldmv/slothlet/i18n/language/${f}`]=base+rel}}catch{}try{const packRoot=path.dirname(fileURLToPath(import.meta.resolve("@cldmv/slothlet-i18n/package.json")));const packBase=base.replace(/@cldmv\/slothlet(@[^/]+)?\/$/,"@cldmv/slothlet-i18n$1/");if(packBase!==base){for(const f of(await fs.readdir(path.join(packRoot,"languages"))).filter(n=>n.endsWith(".json"))){imports[`@cldmv/slothlet-i18n/language/${f}`]=`${packBase}languages/${f}`}}}catch{}return{imports}}async function generateBrowserAssets(apiDir,options={}){const{slothletBase=DEFAULT_SLOTHLET_BASE}=options;if(typeof slothletBase!=="string"){throw new SlothletError("GENERATE_BROWSER_ASSETS_SLOTHLET_BASE_INVALID",{received:typeof slothletBase},null,{validationError:true})}const[manifest,importmap]=await Promise.all([generateManifest(apiDir),generateImportMap(slothletBase)]);return{manifest,importmap}}export{collectSlothletSpecifiers,generateBrowserAssets,generateImportMap,generateManifest};
|
|
17
|
+
import fs from"node:fs/promises";import path from"node:path";import{fileURLToPath}from"node:url";import{SlothletError}from"@cldmv/slothlet/errors";const SLOTHLET_SPEC_RE=/["'](@cldmv\/slothlet(?:\/[^"']+)?)["']/g;const DEFAULT_SLOTHLET_BASE="/node_modules/@cldmv/slothlet/";const LOADABLE_EXTENSIONS=new Set([".mjs",".cjs",".js",".ts",".mts",".cts"]);const SKIP_PREFIXES=["__","."];function isApiFile(filename){const ext=path.extname(filename);return LOADABLE_EXTENSIONS.has(ext)}function makeFileEntry(relativePath){const fullName=path.basename(relativePath);const name=path.basename(relativePath,path.extname(relativePath));return{path:relativePath,name,fullName}}async function scanDir(absDir,rootDir){let entries;try{entries=await fs.readdir(absDir,{withFileTypes:true})}catch{return{files:[],directories:[]}}const files=[];const directories=[];for(const entry of entries){if(SKIP_PREFIXES.some(p=>entry.name.startsWith(p)))continue;if(entry.isFile()){if(isApiFile(entry.name)){const absPath=path.join(absDir,entry.name);const rel=path.relative(rootDir,absPath).replace(/\\/g,"/");files.push(makeFileEntry(rel))}}else if(entry.isDirectory()){const absSubDir=path.join(absDir,entry.name);const relDir=path.relative(rootDir,absSubDir).replace(/\\/g,"/");const children=await scanDir(absSubDir,rootDir);if(children.files.length>0||children.directories.length>0){directories.push({name:entry.name,path:relDir,children})}}}return{files,directories}}async function generateManifest(dir){if(!dir||typeof dir!=="string"){const received=typeof dir==="string"?"<empty>":dir===null?"null":Array.isArray(dir)?"array":typeof dir;throw new SlothletError("GENERATE_MANIFEST_DIR_INVALID",{received},null,{validationError:true})}const absDir=path.resolve(dir);let stat;try{stat=await fs.stat(absDir)}catch(err){throw new SlothletError("GENERATE_MANIFEST_DIR_UNREADABLE",{dir:absDir,reason:err.message},null,{validationError:true})}if(!stat.isDirectory()){throw new SlothletError("GENERATE_MANIFEST_NOT_DIRECTORY",{dir:absDir},null,{validationError:true})}return scanDir(absDir,absDir)}function slothletPackageRoot(){return path.resolve(path.dirname(fileURLToPath(import.meta.url)),"../../..")}async function collectSlothletSpecifiers(root){const specifiers=new Set(["@cldmv/slothlet"]);const pkg=JSON.parse(await fs.readFile(path.join(root,"package.json"),"utf8"));for(const key of Object.keys(pkg.exports)){if(key.includes("*")||key.endsWith(".json"))continue;specifiers.add(key==="."?"@cldmv/slothlet":`@cldmv/slothlet${key.slice(1)}`)}for(const[key,value]of Object.entries(pkg.exports)){if(!key.includes("*")||key.startsWith("./i18n/language/"))continue;const specPrefix=`@cldmv/slothlet${key.slice(1)}`.split("*")[0];const targets=[];(function collectTargets(v){if(typeof v==="string"){if(v.includes("*")&&v.endsWith(".mjs"))targets.push(v)}else if(v&&typeof v==="object"){for(const child of Object.values(v))collectTargets(child)}})(value);for(const tmpl of targets){const star=tmpl.indexOf("*");const dirRel=tmpl.slice(0,star);const suffix=tmpl.slice(star+1);let files;try{files=await fs.readdir(path.join(root,dirRel),{recursive:true})}catch{continue}for(const f of files){const norm=String(f).replace(/\\/g,"/");if(norm.endsWith(suffix))specifiers.add(specPrefix+norm.slice(0,-suffix.length))}}}for(const[key,value]of Object.entries(pkg.imports??{})){if(!key.includes("*"))continue;const specPrefix=key.split("*")[0];const targets=[];(function collectTargets(v){if(typeof v==="string"){if(v.includes("*")&&v.endsWith(".mjs"))targets.push(v)}else if(v&&typeof v==="object"){for(const child of Object.values(v))collectTargets(child)}})(value);for(const tmpl of targets){const star=tmpl.indexOf("*");const dirRel=tmpl.slice(0,star);const suffix=tmpl.slice(star+1);let files;try{files=await fs.readdir(path.join(root,dirRel),{recursive:true})}catch{continue}for(const f of files){const norm=String(f).replace(/\\/g,"/");if(norm.endsWith(suffix))specifiers.add(specPrefix+norm.slice(0,-suffix.length))}}}const SKIP_DIRS=new Set(["node_modules","types","coverage","tmp","tests","api_tests",".git","docs"]);async function scan(dir){const entries=await fs.readdir(dir,{withFileTypes:true});for(const e of entries){if(e.isDirectory()){if(!SKIP_DIRS.has(e.name)&&!e.name.startsWith("."))await scan(path.join(dir,e.name))}else if(/\.(mjs|cjs|js)$/.test(e.name)){const src=await fs.readFile(path.join(dir,e.name),"utf8");let m;SLOTHLET_SPEC_RE.lastIndex=0;while(m=SLOTHLET_SPEC_RE.exec(src)){const spec=m[1];if(spec.startsWith("@cldmv/slothlet/i18n/language/"))continue;if(/[*<>]|\.\.\.|\/$/.test(spec)||spec.split("/").some(s=>s===""))continue;specifiers.add(spec)}}}}await scan(root);return specifiers}async function generateImportMap(slothletBase=DEFAULT_SLOTHLET_BASE){const base=String(slothletBase).endsWith("/")?String(slothletBase):`${slothletBase}/`;const root=slothletPackageRoot();const imports={};for(const spec of await collectSlothletSpecifiers(root)){let resolved;try{resolved=import.meta.resolve(spec);await fs.access(fileURLToPath(resolved))}catch{continue}const rel=path.relative(root,fileURLToPath(resolved)).replace(/\\/g,"/");imports[spec]=base+rel}try{const sampleDir=path.dirname(fileURLToPath(import.meta.resolve("@cldmv/slothlet/i18n/language/en-us.json")));for(const f of(await fs.readdir(sampleDir)).filter(n=>n.endsWith(".json"))){const rel=path.relative(root,path.join(sampleDir,f)).replace(/\\/g,"/");imports[`@cldmv/slothlet/i18n/language/${f}`]=base+rel}}catch{}try{const packRoot=path.dirname(fileURLToPath(import.meta.resolve("@cldmv/slothlet-i18n/package.json")));const packBase=base.replace(/@cldmv\/slothlet(@[^/]+)?\/$/,"@cldmv/slothlet-i18n$1/");if(packBase!==base){for(const f of(await fs.readdir(path.join(packRoot,"languages"))).filter(n=>n.endsWith(".json"))){imports[`@cldmv/slothlet-i18n/language/${f}`]=`${packBase}languages/${f}`}}}catch{}return{imports}}async function generateBrowserAssets(apiDir,options={}){const{slothletBase=DEFAULT_SLOTHLET_BASE}=options;if(typeof slothletBase!=="string"){throw new SlothletError("GENERATE_BROWSER_ASSETS_SLOTHLET_BASE_INVALID",{received:typeof slothletBase},null,{validationError:true})}const[manifest,importmap]=await Promise.all([generateManifest(apiDir),generateImportMap(slothletBase)]);return{manifest,importmap}}export{collectSlothletSpecifiers,generateBrowserAssets,generateImportMap,generateManifest};
|
|
@@ -14,4 +14,4 @@
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import fs from"fs";import path from"path";import{SlothletError}from"@cldmv/slothlet/errors";let typescriptInstance=null;async function getTypeScript(){if(!typescriptInstance){try{typescriptInstance=await import("typescript")}catch(error){throw new SlothletError("TYPESCRIPT_NOT_INSTALLED",{feature:"type-generation"},error)}}return typescriptInstance}async function generateTypes(api,options){const ts=await getTypeScript();if(!options.output){throw new SlothletError("INVALID_CONFIG",{option:"types.output",expected:"a string output path",value:options.output,hint:"Provide a string output path for the generated .d.ts file, e.g. './types/api.d.ts'.",validationError:true})}if(!options.interfaceName){throw new SlothletError("INVALID_CONFIG",{option:"types.interfaceName",expected:"a string interface name",value:options.interfaceName,hint:"Provide a string interface name for the generated TypeScript interface, e.g. 'SlothletAPI'.",validationError:true})}const nodes=traverseAPI(api);for(const node of nodes){if(node.metadata?.filePath){node.typeInfo=
|
|
17
|
+
import fs from"fs";import path from"path";import{SlothletError}from"@cldmv/slothlet/errors";let typescriptInstance=null;async function getTypeScript(){if(!typescriptInstance){try{typescriptInstance=await import("typescript")}catch(error){throw new SlothletError("TYPESCRIPT_NOT_INSTALLED",{feature:"type-generation"},error)}}return typescriptInstance}async function generateTypes(api,options){const ts=await getTypeScript();if(!options.output){throw new SlothletError("INVALID_CONFIG",{option:"types.output",expected:"a string output path",value:options.output,hint:"Provide a string output path for the generated .d.ts file, e.g. './types/api.d.ts'.",validationError:true})}if(!options.interfaceName){throw new SlothletError("INVALID_CONFIG",{option:"types.interfaceName",expected:"a string interface name",value:options.interfaceName,hint:"Provide a string interface name for the generated TypeScript interface, e.g. 'SlothletAPI'.",validationError:true})}const nodes=traverseAPI(api);const filePaths=[...new Set(nodes.map(node=>node.metadata?.filePath).filter(Boolean))];const{byFile:exportsByFile,localTypes}=extractTypeInfo(filePaths,ts);for(const node of nodes){if(node.metadata?.filePath){node.typeInfo=exportsByFile.get(node.metadata.filePath)}}const declaration=generateDeclaration(nodes,options,localTypes);const outputPath=path.resolve(options.output);const outputDir=path.dirname(outputPath);fs.mkdirSync(outputDir,{recursive:true});fs.writeFileSync(outputPath,declaration,"utf8");return{output:declaration,filePath:outputPath}}function traverseAPI(api,currentPath=[],visited=new Set){const nodes=[];if(!api||typeof api!=="object"){return nodes}if(visited.has(api)){return nodes}visited.add(api);for(const[key,value]of Object.entries(api)){if(key.startsWith("_")||key.startsWith("__")||key==="slothlet"||key==="shutdown"||key==="destroy"){continue}const nodePath=[...currentPath,key];const metadata=value?.__metadata;if(typeof value==="function"){nodes.push({type:"function",path:nodePath,value,metadata})}else if(typeof value==="object"&&value!==null){nodes.push({type:"object",path:nodePath,value,metadata});const childNodes=traverseAPI(value,nodePath,visited);nodes.push(...childNodes)}}return nodes}function extractTypeInfo(filePaths,ts){const byFile=new Map;const program=ts.createProgram(filePaths,{allowJs:true,checkJs:false,declaration:true,emitDeclarationOnly:true,skipLibCheck:true,target:ts.ScriptTarget.Latest,module:ts.ModuleKind.ESNext,moduleResolution:ts.ModuleResolutionKind.Bundler});const checker=program.getTypeChecker();const isExported=node=>node.modifiers?.some(m=>m.kind===ts.SyntaxKind.ExportKeyword);const candidates=new Map;program.emit(void 0,(____fileName,text)=>{const dts=ts.createSourceFile("emitted.d.ts",text,ts.ScriptTarget.Latest,false,ts.ScriptKind.TS);for(const stmt of dts.statements){if(ts.isInterfaceDeclaration(stmt)||ts.isTypeAliasDeclaration(stmt)||ts.isEnumDeclaration(stmt)){candidates.set(stmt.name.text,stmt.getText(dts))}}},void 0,true);for(const filePath of filePaths){const exportsForFile=[];try{const visit=node=>{if(ts.isFunctionDeclaration(node)&&node.name&&isExported(node)){exportsForFile.push({name:node.name.text,type:"function",signature:signatureText(node,checker,ts)})}else if(ts.isVariableStatement(node)&&isExported(node)){for(const decl of node.declarationList.declarations){const init=decl.initializer;if(ts.isIdentifier(decl.name)&&init&&(ts.isArrowFunction(init)||ts.isFunctionExpression(init))){exportsForFile.push({name:decl.name.text,type:"function",signature:signatureText(init,checker,ts)})}}}ts.forEachChild(node,visit)};const sourceFile=program.getSourceFile(filePath);if(sourceFile){visit(sourceFile)}}catch(____error){}byFile.set(filePath,{exports:exportsForFile})}const localTypes=collectReferencedLocalTypes(candidates,byFile);return{byFile,localTypes}}function collectReferencedLocalTypes(candidates,byFile){const referenced=new Map;if(candidates.size===0)return referenced;const wholeWord=name=>new RegExp(`\\b${name.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}\\b`);const signatureBlob=[];for(const{exports}of byFile.values()){for(const e of exports)signatureBlob.push(e.signature)}const haystack=signatureBlob.join("\n");const consider=name=>{if(referenced.has(name))return;const text=candidates.get(name);referenced.set(name,text);for(const other of candidates.keys()){if(wholeWord(other).test(text))consider(other)}};for(const name of candidates.keys()){if(wholeWord(name).test(haystack))consider(name)}return referenced}function signatureText(fnNode,checker,ts){const signature=checker.getSignatureFromDeclaration(fnNode);return checker.signatureToString(signature,fnNode,ts.TypeFormatFlags.NoTruncation)}function generateDeclaration(nodes,options,localTypes){const interfaceName=options.interfaceName;const lines=[];lines.push("/**");lines.push(` * Generated TypeScript declarations for Slothlet API`);lines.push(` * @generated ${new Date().toISOString()}`);lines.push(" */");lines.push("");if(localTypes.size>0){for(const text of localTypes.values()){lines.push(text)}lines.push("")}const structure={};for(const node of nodes){if(node.type==="function"){const fnName=node.path[node.path.length-1];const exportInfo=node.typeInfo?.exports?.find(e=>e.name===fnName);const signature=exportInfo?.signature??"(...args: any[]): any";setNestedProperty(structure,node.path,{type:"function",signature})}}lines.push(`export interface ${interfaceName} {`);generateInterfaceContent(structure,lines,1);lines.push("}");lines.push("");lines.push(`declare const self: ${interfaceName};`);lines.push("");return lines.join("\n")}function setNestedProperty(obj,path2,value){let current=obj;for(let i=0;i<path2.length-1;i++){const key=path2[i];if(!current[key]){current[key]={}}current=current[key]}const lastKey=path2[path2.length-1];current[lastKey]=value}function generateInterfaceContent(structure,lines,indent){const indentation=" ".repeat(indent);for(const[key,value]of Object.entries(structure)){if(value.type==="function"){lines.push(`${indentation}${key}${value.signature};`)}else if(typeof value==="object"&&value!==null){lines.push(`${indentation}${key}: {`);generateInterfaceContent(value,lines,indent+1);lines.push(`${indentation}};`)}else{}}}export{generateTypes};
|
|
@@ -14,4 +14,4 @@
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import{asyncRuntime}from"#factories/context";import{SlothletError}from"@cldmv/slothlet/errors";import{enforceContextKeyWrite}from"#handlers/trusted-root";function safeGetContext(){try{return asyncRuntime.getContext()}catch{return null}}const self=new Proxy({},{get(_,prop){const ctx=safeGetContext();if(!ctx||!ctx.self){throw new SlothletError("RUNTIME_NO_ACTIVE_CONTEXT_SELF",{},null,{validationError:true})}return ctx.self[prop]},ownKeys(){const ctx=safeGetContext();if(!ctx||!ctx.self)return[];return Reflect.ownKeys(ctx.self)},has(_,prop){const ctx=safeGetContext();if(!ctx||!ctx.self)return false;return prop in ctx.self},getOwnPropertyDescriptor(_,prop){const ctx=safeGetContext();if(!ctx||!ctx.self)return void 0;const desc=Reflect.getOwnPropertyDescriptor(ctx.self,prop);if(desc){return{...desc,configurable:true}}return void 0},set(_,prop,value){const ctx=safeGetContext();if(!ctx||!ctx.self){throw new SlothletError("RUNTIME_NO_ACTIVE_CONTEXT_SELF",{},null,{validationError:true})}if(typeof prop==="symbol"){ctx.self[prop]=value;return true}if(ctx.slothlet?.boundApi&&ctx.self!==ctx.slothlet.boundApi){ctx.self[prop]=value;return true}const apiManager=ctx.slothlet?.handlers?.apiManager;if(apiManager&&typeof apiManager.setOwnedProperty==="function"){apiManager.setOwnedProperty(String(prop),value,ctx.currentWrapper??null)}else{ctx.self[prop]=value}return true}});const context=new Proxy({},{get(_,prop){const ctx=safeGetContext();if(!ctx||!ctx.context){return void 0}return ctx
|
|
17
|
+
import{asyncRuntime}from"#factories/context";import{SlothletError}from"@cldmv/slothlet/errors";import{enforceContextKeyWrite,readProtectedContextValue}from"#handlers/trusted-root";function safeGetContext(){try{return asyncRuntime.getContext()}catch{return null}}const self=new Proxy({},{get(_,prop){const ctx=safeGetContext();if(!ctx||!ctx.self){throw new SlothletError("RUNTIME_NO_ACTIVE_CONTEXT_SELF",{},null,{validationError:true})}return ctx.self[prop]},ownKeys(){const ctx=safeGetContext();if(!ctx||!ctx.self)return[];return Reflect.ownKeys(ctx.self)},has(_,prop){const ctx=safeGetContext();if(!ctx||!ctx.self)return false;return prop in ctx.self},getOwnPropertyDescriptor(_,prop){const ctx=safeGetContext();if(!ctx||!ctx.self)return void 0;const desc=Reflect.getOwnPropertyDescriptor(ctx.self,prop);if(desc){return{...desc,configurable:true}}return void 0},set(_,prop,value){const ctx=safeGetContext();if(!ctx||!ctx.self){throw new SlothletError("RUNTIME_NO_ACTIVE_CONTEXT_SELF",{},null,{validationError:true})}if(typeof prop==="symbol"){ctx.self[prop]=value;return true}if(ctx.slothlet?.boundApi&&ctx.self!==ctx.slothlet.boundApi){ctx.self[prop]=value;return true}const apiManager=ctx.slothlet?.handlers?.apiManager;if(apiManager&&typeof apiManager.setOwnedProperty==="function"){apiManager.setOwnedProperty(String(prop),value,ctx.currentWrapper??null)}else{ctx.self[prop]=value}return true}});const context=new Proxy({},{get(_,prop){const ctx=safeGetContext();if(!ctx||!ctx.context){return void 0}return readProtectedContextValue(ctx,prop,safeGetContext)},set(_,prop,value){const ctx=safeGetContext();if(!ctx||!ctx.context){throw new SlothletError("RUNTIME_NO_ACTIVE_CONTEXT_CONTEXT",{},null,{validationError:true})}enforceContextKeyWrite(ctx,prop);ctx.context[prop]=value;return true},ownKeys(){const ctx=safeGetContext();if(!ctx||!ctx.context)return[];return Reflect.ownKeys(ctx.context)},has(_,prop){const ctx=safeGetContext();if(!ctx||!ctx.context)return false;return prop in ctx.context},getOwnPropertyDescriptor(_,prop){const ctx=safeGetContext();if(!ctx||!ctx.context)return void 0;return Reflect.getOwnPropertyDescriptor(ctx.context,prop)}});const instanceID=new Proxy({},{get(_,prop){const ctx=safeGetContext();if(!ctx||!ctx.instanceID){return void 0}if(prop===Symbol.toPrimitive||prop==="toString"||prop==="valueOf"){return()=>ctx.instanceID}return ctx.instanceID[prop]},has(_,prop){const ctx=safeGetContext();if(!ctx||!ctx.instanceID)return false;return prop in Object(ctx.instanceID)}});export{context,instanceID,self};
|
|
@@ -14,4 +14,4 @@
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import{liveRuntime}from"#factories/context";import{SlothletError}from"@cldmv/slothlet/errors";import{enforceContextKeyWrite}from"#handlers/trusted-root";const self=new Proxy({},{get(_,prop){const ctx=liveRuntime.getContext();if(!ctx||!ctx.self){throw new SlothletError("RUNTIME_NO_ACTIVE_CONTEXT_SELF",{},null,{validationError:true})}return ctx.self[prop]},ownKeys(){const ctx=liveRuntime.getContext();if(!ctx||!ctx.self)return[];return Reflect.ownKeys(ctx.self)},has(_,prop){const ctx=liveRuntime.getContext();if(!ctx||!ctx.self)return false;return prop in ctx.self},getOwnPropertyDescriptor(_,prop){const ctx=liveRuntime.getContext();if(!ctx||!ctx.self)return void 0;const desc=Reflect.getOwnPropertyDescriptor(ctx.self,prop);if(desc){return{...desc,configurable:true}}return void 0},set(_,prop,value){const ctx=liveRuntime.getContext();if(!ctx||!ctx.self){throw new SlothletError("RUNTIME_NO_ACTIVE_CONTEXT_SELF",{},null,{validationError:true})}if(typeof prop==="symbol"){ctx.self[prop]=value;return true}if(ctx.slothlet?.boundApi&&ctx.self!==ctx.slothlet.boundApi){ctx.self[prop]=value;return true}const apiManager=ctx.slothlet?.handlers?.apiManager;if(apiManager&&typeof apiManager.setOwnedProperty==="function"){apiManager.setOwnedProperty(String(prop),value,ctx.currentWrapper??null)}else{ctx.self[prop]=value}return true}});const context=new Proxy({},{get(_,prop){const ctx=liveRuntime.getContext();if(!ctx||!ctx.context){return void 0}return ctx
|
|
17
|
+
import{liveRuntime}from"#factories/context";import{SlothletError}from"@cldmv/slothlet/errors";import{enforceContextKeyWrite,readProtectedContextValue}from"#handlers/trusted-root";const resolveActiveContext=()=>liveRuntime.getContext();const self=new Proxy({},{get(_,prop){const ctx=liveRuntime.getContext();if(!ctx||!ctx.self){throw new SlothletError("RUNTIME_NO_ACTIVE_CONTEXT_SELF",{},null,{validationError:true})}return ctx.self[prop]},ownKeys(){const ctx=liveRuntime.getContext();if(!ctx||!ctx.self)return[];return Reflect.ownKeys(ctx.self)},has(_,prop){const ctx=liveRuntime.getContext();if(!ctx||!ctx.self)return false;return prop in ctx.self},getOwnPropertyDescriptor(_,prop){const ctx=liveRuntime.getContext();if(!ctx||!ctx.self)return void 0;const desc=Reflect.getOwnPropertyDescriptor(ctx.self,prop);if(desc){return{...desc,configurable:true}}return void 0},set(_,prop,value){const ctx=liveRuntime.getContext();if(!ctx||!ctx.self){throw new SlothletError("RUNTIME_NO_ACTIVE_CONTEXT_SELF",{},null,{validationError:true})}if(typeof prop==="symbol"){ctx.self[prop]=value;return true}if(ctx.slothlet?.boundApi&&ctx.self!==ctx.slothlet.boundApi){ctx.self[prop]=value;return true}const apiManager=ctx.slothlet?.handlers?.apiManager;if(apiManager&&typeof apiManager.setOwnedProperty==="function"){apiManager.setOwnedProperty(String(prop),value,ctx.currentWrapper??null)}else{ctx.self[prop]=value}return true}});const context=new Proxy({},{get(_,prop){const ctx=liveRuntime.getContext();if(!ctx||!ctx.context){return void 0}return readProtectedContextValue(ctx,prop,resolveActiveContext)},set(_,prop,value){const ctx=liveRuntime.getContext();if(!ctx||!ctx.context){throw new SlothletError("RUNTIME_NO_ACTIVE_CONTEXT_CONTEXT",{},null,{validationError:true})}enforceContextKeyWrite(ctx,prop);ctx.context[prop]=value;return true},ownKeys(){const ctx=liveRuntime.getContext();if(!ctx||!ctx.context)return[];return Reflect.ownKeys(ctx.context)},has(_,prop){const ctx=liveRuntime.getContext();if(!ctx||!ctx.context)return false;return prop in ctx.context},getOwnPropertyDescriptor(_,prop){const ctx=liveRuntime.getContext();if(!ctx||!ctx.context)return void 0;return Reflect.getOwnPropertyDescriptor(ctx.context,prop)}});export{context,self};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cldmv/slothlet",
|
|
3
|
-
"version": "3.12.
|
|
3
|
+
"version": "3.12.2",
|
|
4
4
|
"moduleVersions": {
|
|
5
5
|
"lazy": "3.0.0",
|
|
6
6
|
"eager": "3.0.0",
|
|
@@ -314,11 +314,11 @@
|
|
|
314
314
|
"devDependencies": {
|
|
315
315
|
"@cldmv/fix-headers": "^1.3.0",
|
|
316
316
|
"@cldmv/vitest-runner": "^1.2.0",
|
|
317
|
-
"@eslint/css": "^1.
|
|
317
|
+
"@eslint/css": "^1.4.0",
|
|
318
318
|
"@eslint/js": "^10.0.1",
|
|
319
319
|
"@eslint/json": "^2.0.1",
|
|
320
320
|
"@eslint/markdown": "^8.0.3",
|
|
321
|
-
"@types/node": "^26.1.
|
|
321
|
+
"@types/node": "^26.1.1",
|
|
322
322
|
"@vitest/browser": "^4.1.10",
|
|
323
323
|
"@vitest/browser-playwright": "^4.1.10",
|
|
324
324
|
"@vitest/coverage-v8": "^4.1.10",
|
|
@@ -326,8 +326,8 @@
|
|
|
326
326
|
"chalk": "^5.6.2",
|
|
327
327
|
"chokidar": "^5.0.0",
|
|
328
328
|
"dmd": "^7.1.1",
|
|
329
|
-
"esbuild": "^0.28.
|
|
330
|
-
"eslint": "^10.
|
|
329
|
+
"esbuild": "^0.28.1",
|
|
330
|
+
"eslint": "^10.7.0",
|
|
331
331
|
"estree-walker": "^3.0.3",
|
|
332
332
|
"globals": "^17.7.0",
|
|
333
333
|
"jsdoc": "^4.0.5",
|
|
@@ -335,7 +335,7 @@
|
|
|
335
335
|
"jsdoc2md": "^1.0.0",
|
|
336
336
|
"jsonc-parser": "^3.3.1",
|
|
337
337
|
"playwright": "^1.61.1",
|
|
338
|
-
"prettier": "^3.9.
|
|
338
|
+
"prettier": "^3.9.5",
|
|
339
339
|
"shx": "^0.4.0",
|
|
340
340
|
"typescript": "^6.0.3",
|
|
341
341
|
"vitest": "^4.1.10"
|
|
@@ -357,6 +357,7 @@
|
|
|
357
357
|
"files": [
|
|
358
358
|
"index.mjs",
|
|
359
359
|
"index.cjs",
|
|
360
|
+
"devcheck.mjs",
|
|
360
361
|
"README.md",
|
|
361
362
|
"REFERENCE.md",
|
|
362
363
|
"LICENSE",
|