@cldmv/slothlet 1.0.1 → 2.0.1
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 +862 -73
- package/dist/lib/engine/README.md +21 -0
- package/dist/lib/engine/slothlet_child.mjs +58 -0
- package/dist/lib/engine/slothlet_engine.mjs +371 -0
- package/dist/lib/engine/slothlet_esm.mjs +229 -0
- package/dist/lib/engine/slothlet_helpers.mjs +454 -0
- package/dist/lib/engine/slothlet_worker.mjs +148 -0
- package/dist/lib/helpers/resolve-from-caller.mjs +141 -0
- package/dist/lib/helpers/sanitize.mjs +78 -0
- package/dist/lib/modes/slothlet_eager.mjs +80 -0
- package/dist/lib/modes/slothlet_lazy.mjs +342 -0
- package/dist/lib/runtime/runtime.mjs +249 -0
- package/dist/slothlet.mjs +1092 -0
- package/index.cjs +81 -0
- package/index.mjs +76 -0
- package/package.json +136 -14
- package/types/dist/lib/engine/slothlet_child.d.mts +2 -0
- package/types/dist/lib/engine/slothlet_child.d.mts.map +1 -0
- package/types/dist/lib/engine/slothlet_engine.d.mts +31 -0
- package/types/dist/lib/engine/slothlet_engine.d.mts.map +1 -0
- package/types/dist/lib/engine/slothlet_esm.d.mts +19 -0
- package/types/dist/lib/engine/slothlet_esm.d.mts.map +1 -0
- package/types/dist/lib/engine/slothlet_helpers.d.mts +24 -0
- package/types/dist/lib/engine/slothlet_helpers.d.mts.map +1 -0
- package/types/dist/lib/engine/slothlet_worker.d.mts +2 -0
- package/types/dist/lib/engine/slothlet_worker.d.mts.map +1 -0
- package/types/dist/lib/helpers/resolve-from-caller.d.mts +149 -0
- package/types/dist/lib/helpers/resolve-from-caller.d.mts.map +1 -0
- package/types/dist/lib/helpers/sanitize.d.mts +138 -0
- package/types/dist/lib/helpers/sanitize.d.mts.map +1 -0
- package/types/dist/lib/modes/slothlet_eager.d.mts +66 -0
- package/types/dist/lib/modes/slothlet_eager.d.mts.map +1 -0
- package/types/dist/lib/modes/slothlet_lazy.d.mts +32 -0
- package/types/dist/lib/modes/slothlet_lazy.d.mts.map +1 -0
- package/types/dist/lib/runtime/runtime.d.mts +49 -0
- package/types/dist/lib/runtime/runtime.d.mts.map +1 -0
- package/types/dist/slothlet.d.mts +110 -0
- package/types/dist/slothlet.d.mts.map +1 -0
- package/types/index.d.mts +23 -0
- package/slothlet.mjs +0 -1218
|
@@ -0,0 +1,1092 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2025 CLDMV/Shinrai
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
import fs from "fs/promises";
|
|
19
|
+
import path from "path";
|
|
20
|
+
import { fileURLToPath, pathToFileURL } from "url";
|
|
21
|
+
|
|
22
|
+
import { resolvePathFromCaller } from "@cldmv/slothlet/helpers/resolve-from-caller";
|
|
23
|
+
import { sanitizePathName } from "@cldmv/slothlet/helpers/sanitize";
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
28
|
+
const __dirname = path.dirname(__filename);
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
let DEBUG = process.argv.includes("--slothletdebug")
|
|
35
|
+
? true
|
|
36
|
+
: process.env.SLOTHLET_DEBUG === "1" || process.env.SLOTHLET_DEBUG === "true"
|
|
37
|
+
? true
|
|
38
|
+
: false;
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
export let _slothlet = new URL(import.meta.url).searchParams.get("_slothlet") || "";
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
export const self = {};
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
export const context = {};
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
export const reference = {};
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
async function slothlet(options = {}) {
|
|
57
|
+
|
|
58
|
+
const instance = createFreshInstance();
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
for (const key of Object.keys(slothlet)) {
|
|
62
|
+
if (key.startsWith("_")) continue;
|
|
63
|
+
|
|
64
|
+
if (key === "name" || key === "length") continue;
|
|
65
|
+
try {
|
|
66
|
+
delete slothlet[key];
|
|
67
|
+
} catch {
|
|
68
|
+
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
for (const [k, v] of Object.entries(instance)) {
|
|
74
|
+
if (typeof v === "function") {
|
|
75
|
+
|
|
76
|
+
Object.defineProperty(slothlet, k, { value: v.bind(instance), writable: false, enumerable: true, configurable: true });
|
|
77
|
+
} else {
|
|
78
|
+
|
|
79
|
+
Object.defineProperty(slothlet, k, { value: instance[k], writable: true, enumerable: true, configurable: true });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
Object.defineProperty(slothlet, "_instance", { value: instance, writable: false, enumerable: false, configurable: true });
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
return await instance.create(options);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
function createFreshInstance() {
|
|
90
|
+
const instance = {};
|
|
91
|
+
for (const [k, v] of Object.entries(slothletObject)) {
|
|
92
|
+
if (typeof v === "function") {
|
|
93
|
+
|
|
94
|
+
instance[k] = v;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (k === "config") instance[k] = { ...v };
|
|
99
|
+
else if (k === "boundapi" || k === "context" || k === "reference") instance[k] = {};
|
|
100
|
+
else instance[k] = v;
|
|
101
|
+
}
|
|
102
|
+
return instance;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
const slothletObject = {
|
|
107
|
+
|
|
108
|
+
api: null,
|
|
109
|
+
self: {},
|
|
110
|
+
boundapi: {},
|
|
111
|
+
context: {},
|
|
112
|
+
reference: {},
|
|
113
|
+
mode: "singleton",
|
|
114
|
+
loaded: false,
|
|
115
|
+
config: { lazy: false, apiDepth: Infinity, debug: DEBUG, dir: null },
|
|
116
|
+
_dispose: null,
|
|
117
|
+
_boundAPIShutdown: null,
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
async create(options = {}) {
|
|
121
|
+
if (this.loaded) {
|
|
122
|
+
console.warn("[slothlet] create: API already loaded, returning existing instance.");
|
|
123
|
+
return this.boundapi;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
if (!this.modes) {
|
|
128
|
+
this.modes = {};
|
|
129
|
+
const modesDir = path.join(__dirname, "lib", "modes");
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
const dirents = await fs.readdir(modesDir, { withFileTypes: true });
|
|
133
|
+
const modeFiles = dirents.filter((d) => d.isFile()).map((d) => path.join(modesDir, d.name));
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
for (const file of modeFiles) {
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
const modePath = file;
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
const modeName = path.parse(file).name.replace(/^slothlet_/, "");
|
|
148
|
+
if (!modeName || modeName.includes(" ")) continue;
|
|
149
|
+
try {
|
|
150
|
+
|
|
151
|
+
const modeUrl = pathToFileURL(modePath).href;
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
const imported = await import(modeUrl);
|
|
157
|
+
if (imported && typeof imported === "object") {
|
|
158
|
+
this.modes[modeName] = imported.default || imported;
|
|
159
|
+
}
|
|
160
|
+
} catch (err) {
|
|
161
|
+
console.error(`[slothlet] Could not import mode '${modeName}':`, err);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
const { entry = import.meta.url, mode = "singleton", api_mode = "auto" } = options ?? {};
|
|
169
|
+
|
|
170
|
+
_slothlet = new URL(import.meta.url).searchParams.get("_slothlet") || "";
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
this.mode = mode;
|
|
178
|
+
this.api_mode = api_mode;
|
|
179
|
+
let api;
|
|
180
|
+
let dispose;
|
|
181
|
+
if (mode === "singleton") {
|
|
182
|
+
const { context = null, reference = null, ...loadConfig } = options;
|
|
183
|
+
this.context = context;
|
|
184
|
+
this.reference = reference;
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
if (this.api_mode === "function") {
|
|
189
|
+
this.boundapi = function (...args) {
|
|
190
|
+
if (typeof this.boundapi._impl === "function") {
|
|
191
|
+
return this.boundapi._impl(...args);
|
|
192
|
+
}
|
|
193
|
+
}.bind(this);
|
|
194
|
+
this.boundapi._impl = () => {};
|
|
195
|
+
} else if (this.api_mode === "auto") {
|
|
196
|
+
|
|
197
|
+
this.boundapi = {};
|
|
198
|
+
} else {
|
|
199
|
+
this.boundapi = {};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
await this.load(loadConfig, { context, reference });
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
return this.boundapi;
|
|
207
|
+
} else {
|
|
208
|
+
const { createEngine } = await import("./lib/engine/slothlet_engine.mjs");
|
|
209
|
+
({ api, dispose } = await createEngine({ entry, ...options }));
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
if (typeof dispose === "function") {
|
|
213
|
+
Object.defineProperty(api, "__dispose__", {
|
|
214
|
+
value: dispose,
|
|
215
|
+
writable: false,
|
|
216
|
+
enumerable: false,
|
|
217
|
+
configurable: false
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
this._dispose = dispose;
|
|
221
|
+
this.loaded = true;
|
|
222
|
+
return api;
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
async load(config = {}, ctxRef = { context: null, reference: null }) {
|
|
228
|
+
this.config = { ...this.config, ...config };
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
let apiDir = this.config.dir || "api";
|
|
232
|
+
|
|
233
|
+
if (apiDir && !path.isAbsolute(apiDir)) {
|
|
234
|
+
|
|
235
|
+
apiDir = resolvePathFromCaller(apiDir);
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
if (this.loaded) return this.api;
|
|
245
|
+
if (this.config.lazy) {
|
|
246
|
+
this.api = await this.modes.lazy.create.call(this, apiDir, true, this.config.apiDepth || Infinity, 0);
|
|
247
|
+
} else {
|
|
248
|
+
this.api = await this.modes.eager.create.call(this, apiDir, true, this.config.apiDepth || Infinity, 0);
|
|
249
|
+
}
|
|
250
|
+
if (this.config.debug) console.log(this.api);
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
if (this.api_mode === "auto") {
|
|
254
|
+
this.api_mode = typeof this.api === "function" ? "function" : "object";
|
|
255
|
+
if (this.config.debug) {
|
|
256
|
+
console.log(`[DEBUG] Auto-detected api_mode: ${this.api_mode} (API is ${typeof this.api})`);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
if (this.api_mode === "function" && typeof this.boundapi !== "function") {
|
|
261
|
+
this.boundapi = function (...args) {
|
|
262
|
+
|
|
263
|
+
if (typeof this.boundapi._impl === "function") {
|
|
264
|
+
return this.boundapi._impl(...args);
|
|
265
|
+
}
|
|
266
|
+
}.bind(this);
|
|
267
|
+
this.boundapi._impl = () => {};
|
|
268
|
+
} else if (this.api_mode === "object" && typeof this.boundapi === "function") {
|
|
269
|
+
this.boundapi = {};
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
const l_ctxRef = { ...{ context: null, reference: null }, ...ctxRef };
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
const _boundapi = this.createBoundApi(l_ctxRef.reference);
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
mutateLiveBindingFunction(this.boundapi, _boundapi);
|
|
285
|
+
|
|
286
|
+
this.updateBindings(this.context, this.reference, this.boundapi);
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
this.loaded = true;
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
return this.boundapi;
|
|
333
|
+
},
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
_toApiKey(name) {
|
|
337
|
+
return sanitizePathName(name);
|
|
338
|
+
|
|
339
|
+
},
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
async _buildCategory(categoryPath, options = {}) {
|
|
343
|
+
const { currentDepth = 0, maxDepth = Infinity, mode = "eager", subdirHandler } = options;
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
if (this.config.debug) {
|
|
347
|
+
console.log(`[DEBUG] _buildCategory called with path: ${categoryPath}, mode: ${mode}`);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
const files = await fs.readdir(categoryPath, { withFileTypes: true });
|
|
351
|
+
const moduleFiles = files.filter((f) => this._shouldIncludeFile(f));
|
|
352
|
+
const categoryName = this._toApiKey(path.basename(categoryPath));
|
|
353
|
+
const subDirs = files.filter((e) => e.isDirectory() && !e.name.startsWith("."));
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
if (moduleFiles.length === 1 && subDirs.length === 0) {
|
|
357
|
+
const moduleExt = path.extname(moduleFiles[0].name);
|
|
358
|
+
const moduleName = this._toApiKey(path.basename(moduleFiles[0].name, moduleExt));
|
|
359
|
+
const mod = await this._loadSingleModule(path.join(categoryPath, moduleFiles[0].name));
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
const functionNameMatchesFolder = typeof mod === "function" && mod.name && mod.name.toLowerCase() === categoryName.toLowerCase();
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
const functionNameMatchesFilename =
|
|
366
|
+
typeof mod === "function" &&
|
|
367
|
+
mod.name &&
|
|
368
|
+
this._toApiKey(mod.name).toLowerCase() === this._toApiKey(moduleName).toLowerCase() &&
|
|
369
|
+
mod.name !== this._toApiKey(moduleName);
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
if (moduleName === categoryName && typeof mod === "function") {
|
|
381
|
+
try {
|
|
382
|
+
Object.defineProperty(mod, "name", { value: categoryName, configurable: true });
|
|
383
|
+
} catch {
|
|
384
|
+
|
|
385
|
+
}
|
|
386
|
+
return mod;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
if (functionNameMatchesFolder) {
|
|
391
|
+
try {
|
|
392
|
+
|
|
393
|
+
Object.defineProperty(mod, "name", { value: mod.name, configurable: true });
|
|
394
|
+
} catch {
|
|
395
|
+
|
|
396
|
+
}
|
|
397
|
+
return mod;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
if (functionNameMatchesFilename) {
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
return { [mod.name]: mod };
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
if (
|
|
411
|
+
typeof mod === "function" &&
|
|
412
|
+
(!mod.name || mod.name === "default" || mod.__slothletDefault === true)
|
|
413
|
+
) {
|
|
414
|
+
try {
|
|
415
|
+
Object.defineProperty(mod, "name", { value: categoryName, configurable: true });
|
|
416
|
+
} catch {
|
|
417
|
+
|
|
418
|
+
}
|
|
419
|
+
return mod;
|
|
420
|
+
}
|
|
421
|
+
if (moduleName === categoryName && mod && typeof mod === "object" && !mod.default) {
|
|
422
|
+
return { ...mod };
|
|
423
|
+
}
|
|
424
|
+
return { [moduleName]: mod };
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
const categoryModules = {};
|
|
429
|
+
for (const file of moduleFiles) {
|
|
430
|
+
const moduleExt = path.extname(file.name);
|
|
431
|
+
const moduleName = this._toApiKey(path.basename(file.name, moduleExt));
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
const mod = await this._loadSingleModule(path.join(categoryPath, file.name));
|
|
439
|
+
if (moduleName === categoryName && mod && typeof mod === "object") {
|
|
440
|
+
if (
|
|
441
|
+
Object.prototype.hasOwnProperty.call(mod, categoryName) &&
|
|
442
|
+
typeof mod[categoryName] === "object" &&
|
|
443
|
+
mod[categoryName] !== null
|
|
444
|
+
) {
|
|
445
|
+
Object.assign(categoryModules, mod[categoryName]);
|
|
446
|
+
for (const [key, value] of Object.entries(mod)) {
|
|
447
|
+
if (key !== categoryName) categoryModules[this._toApiKey(key)] = value;
|
|
448
|
+
}
|
|
449
|
+
} else {
|
|
450
|
+
Object.assign(categoryModules, mod);
|
|
451
|
+
}
|
|
452
|
+
} else if (typeof mod === "function") {
|
|
453
|
+
const fnName = mod.name && mod.name !== "default" ? mod.name : moduleName;
|
|
454
|
+
try {
|
|
455
|
+
Object.defineProperty(mod, "name", { value: fnName, configurable: true });
|
|
456
|
+
} catch {
|
|
457
|
+
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
let apiKey;
|
|
463
|
+
if (fnName && fnName.toLowerCase() === moduleName.toLowerCase() && fnName !== moduleName) {
|
|
464
|
+
|
|
465
|
+
apiKey = fnName;
|
|
466
|
+
if (this.config.debug) {
|
|
467
|
+
console.log(`[DEBUG] Using function name '${fnName}' instead of module name '${moduleName}'`);
|
|
468
|
+
}
|
|
469
|
+
} else {
|
|
470
|
+
|
|
471
|
+
apiKey = this._toApiKey(fnName);
|
|
472
|
+
if (this.config.debug) {
|
|
473
|
+
console.log(`[DEBUG] Using sanitized key '${apiKey}' for function '${fnName}' (module: '${moduleName}')`);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
categoryModules[apiKey] = mod;
|
|
478
|
+
} else {
|
|
479
|
+
|
|
480
|
+
let hasPreferredName = false;
|
|
481
|
+
const modWithPreferredNames = {};
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
for (const [exportName, exportValue] of Object.entries(mod)) {
|
|
507
|
+
if (
|
|
508
|
+
typeof exportValue === "function" &&
|
|
509
|
+
exportValue.name &&
|
|
510
|
+
this._toApiKey(exportValue.name).toLowerCase() === this._toApiKey(moduleName).toLowerCase() &&
|
|
511
|
+
exportValue.name !== this._toApiKey(moduleName)
|
|
512
|
+
) {
|
|
513
|
+
|
|
514
|
+
modWithPreferredNames[exportValue.name] = exportValue;
|
|
515
|
+
hasPreferredName = true;
|
|
516
|
+
if (this.config.debug) {
|
|
517
|
+
console.log("[DEBUG] Using preferred name:", exportValue.name, "instead of:", this._toApiKey(moduleName));
|
|
518
|
+
}
|
|
519
|
+
} else {
|
|
520
|
+
modWithPreferredNames[this._toApiKey(exportName)] = exportValue;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
if (hasPreferredName) {
|
|
525
|
+
Object.assign(categoryModules, modWithPreferredNames);
|
|
526
|
+
|
|
527
|
+
} else {
|
|
528
|
+
categoryModules[this._toApiKey(moduleName)] = mod;
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
for (const subDirEntry of subDirs) {
|
|
538
|
+
if (currentDepth < maxDepth) {
|
|
539
|
+
const key = this._toApiKey(subDirEntry.name);
|
|
540
|
+
const subDirPath = path.join(categoryPath, subDirEntry.name);
|
|
541
|
+
let subModule;
|
|
542
|
+
|
|
543
|
+
if (mode === "lazy" && typeof subdirHandler === "function") {
|
|
544
|
+
subModule = subdirHandler({
|
|
545
|
+
subDirEntry,
|
|
546
|
+
subDirPath,
|
|
547
|
+
key,
|
|
548
|
+
categoryModules,
|
|
549
|
+
currentDepth,
|
|
550
|
+
maxDepth
|
|
551
|
+
});
|
|
552
|
+
} else {
|
|
553
|
+
subModule = await this._buildCategory(subDirPath, {
|
|
554
|
+
currentDepth: currentDepth + 1,
|
|
555
|
+
maxDepth,
|
|
556
|
+
mode: "eager"
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
if (
|
|
563
|
+
typeof subModule === "function" &&
|
|
564
|
+
subModule.name &&
|
|
565
|
+
subModule.name.toLowerCase() === key.toLowerCase() &&
|
|
566
|
+
subModule.name !== key
|
|
567
|
+
) {
|
|
568
|
+
|
|
569
|
+
categoryModules[subModule.name] = subModule;
|
|
570
|
+
} else {
|
|
571
|
+
categoryModules[key] = subModule;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
|
|
577
|
+
const keys = Object.keys(categoryModules);
|
|
578
|
+
if (keys.length === 1) {
|
|
579
|
+
const singleKey = keys[0];
|
|
580
|
+
if (singleKey === categoryName) {
|
|
581
|
+
const single = categoryModules[singleKey];
|
|
582
|
+
if (typeof single === "function") {
|
|
583
|
+
if (single.name !== categoryName) {
|
|
584
|
+
try {
|
|
585
|
+
Object.defineProperty(single, "name", { value: categoryName, configurable: true });
|
|
586
|
+
} catch {
|
|
587
|
+
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
return single;
|
|
591
|
+
} else if (single && typeof single === "object" && !Array.isArray(single)) {
|
|
592
|
+
return single;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
return categoryModules;
|
|
597
|
+
},
|
|
598
|
+
|
|
599
|
+
|
|
600
|
+
async _loadCategory(categoryPath, currentDepth = 0, maxDepth = Infinity) {
|
|
601
|
+
return this._buildCategory(categoryPath, { currentDepth, maxDepth, mode: "eager" });
|
|
602
|
+
},
|
|
603
|
+
|
|
604
|
+
|
|
605
|
+
async _loadSingleModule(modulePath, rootLevel = false) {
|
|
606
|
+
const moduleUrl = pathToFileURL(modulePath).href;
|
|
607
|
+
|
|
608
|
+
|
|
609
|
+
|
|
610
|
+
const module = await import(moduleUrl);
|
|
611
|
+
|
|
612
|
+
|
|
613
|
+
|
|
614
|
+
|
|
615
|
+
|
|
616
|
+
|
|
617
|
+
|
|
618
|
+
if (this.config.debug) console.log("module: ", module);
|
|
619
|
+
|
|
620
|
+
if (typeof module.default === "function") {
|
|
621
|
+
let fn;
|
|
622
|
+
if (rootLevel) {
|
|
623
|
+
fn = module;
|
|
624
|
+
} else {
|
|
625
|
+
fn = module.default;
|
|
626
|
+
|
|
627
|
+
try {
|
|
628
|
+
Object.defineProperty(fn, "__slothletDefault", { value: true, enumerable: false });
|
|
629
|
+
} catch {
|
|
630
|
+
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
|
|
634
|
+
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
|
|
638
|
+
|
|
639
|
+
for (const [exportName, exportValue] of Object.entries(module)) {
|
|
640
|
+
if (exportName !== "default") {
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
|
|
644
|
+
|
|
645
|
+
|
|
646
|
+
fn[this._toApiKey(exportName)] = exportValue;
|
|
647
|
+
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
if (this.config.debug) console.log("fn: ", fn);
|
|
651
|
+
}
|
|
652
|
+
return fn;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
const moduleExports = Object.entries(module);
|
|
656
|
+
|
|
657
|
+
if (!moduleExports.length) {
|
|
658
|
+
throw new Error(
|
|
659
|
+
`slothlet: No exports found in module '${modulePath}'. The file is empty or does not export any function/object/variable.`
|
|
660
|
+
);
|
|
661
|
+
}
|
|
662
|
+
if (this.config.debug) console.log("moduleExports: ", moduleExports);
|
|
663
|
+
|
|
664
|
+
|
|
665
|
+
const defaultExportObj =
|
|
666
|
+
typeof module.default === "object" && module.default !== null
|
|
667
|
+
? module.default
|
|
668
|
+
: typeof moduleExports[0][1] === "object" && typeof moduleExports[0][1].default === "function" && moduleExports[0][1] !== null
|
|
669
|
+
? moduleExports[0][1]
|
|
670
|
+
: null;
|
|
671
|
+
let objectName = null;
|
|
672
|
+
if (typeof module.default === "function" && module.default.name) {
|
|
673
|
+
objectName = module.default.name;
|
|
674
|
+
} else if (moduleExports[0] && moduleExports[0][0] !== "default") {
|
|
675
|
+
objectName = moduleExports[0][0];
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
|
|
679
|
+
|
|
680
|
+
if (this.config.debug) console.log("defaultExportObj: ", defaultExportObj);
|
|
681
|
+
if (this.config.debug) console.log("objectName: ", objectName);
|
|
682
|
+
|
|
683
|
+
if (defaultExportObj && typeof defaultExportObj.default === "function") {
|
|
684
|
+
if (this.config.debug) console.log("DEFAULT FUNCTION FOUND FOR: ", module);
|
|
685
|
+
|
|
686
|
+
const callableApi = {
|
|
687
|
+
[objectName]: function (...args) {
|
|
688
|
+
return defaultExportObj.default.apply(defaultExportObj, args);
|
|
689
|
+
}
|
|
690
|
+
}[objectName];
|
|
691
|
+
for (const [methodName, method] of Object.entries(defaultExportObj)) {
|
|
692
|
+
if (methodName === "default") continue;
|
|
693
|
+
callableApi[methodName] = method;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
|
|
697
|
+
|
|
698
|
+
|
|
699
|
+
|
|
700
|
+
if (this.config.debug) console.log("callableApi", callableApi);
|
|
701
|
+
return callableApi;
|
|
702
|
+
} else if (defaultExportObj) {
|
|
703
|
+
if (this.config.debug) console.log("DEFAULT FOUND FOR: ", module);
|
|
704
|
+
|
|
705
|
+
const obj = { ...defaultExportObj };
|
|
706
|
+
|
|
707
|
+
|
|
708
|
+
|
|
709
|
+
|
|
710
|
+
|
|
711
|
+
|
|
712
|
+
|
|
713
|
+
|
|
714
|
+
|
|
715
|
+
|
|
716
|
+
|
|
717
|
+
|
|
718
|
+
for (const [exportName, exportValue] of Object.entries(module)) {
|
|
719
|
+
if (exportName !== "default" && exportValue !== obj) {
|
|
720
|
+
|
|
721
|
+
|
|
722
|
+
|
|
723
|
+
|
|
724
|
+
|
|
725
|
+
obj[exportName] = exportValue;
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
return obj;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
const namedExports = Object.entries(module).filter(([k]) => k !== "default");
|
|
733
|
+
if (this.config.debug) console.log("namedExports: ", namedExports);
|
|
734
|
+
if (namedExports.length === 1 && !module.default) {
|
|
735
|
+
if (typeof namedExports[0][1] === "object") {
|
|
736
|
+
|
|
737
|
+
if (this.config.debug) console.log("namedExports[0][1] === object: ", namedExports[0][1]);
|
|
738
|
+
const obj = { ...namedExports[0][1] };
|
|
739
|
+
|
|
740
|
+
|
|
741
|
+
|
|
742
|
+
|
|
743
|
+
|
|
744
|
+
|
|
745
|
+
|
|
746
|
+
|
|
747
|
+
|
|
748
|
+
|
|
749
|
+
return obj;
|
|
750
|
+
}
|
|
751
|
+
if (typeof namedExports[0][1] === "function") {
|
|
752
|
+
if (this.config.debug) console.log("namedExports[0][1] === function: ", namedExports[0][1]);
|
|
753
|
+
|
|
754
|
+
|
|
755
|
+
return namedExports[0][1];
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
const apiExport = {};
|
|
759
|
+
for (const [exportName, exportValue] of namedExports) {
|
|
760
|
+
|
|
761
|
+
|
|
762
|
+
apiExport[exportName] = exportValue;
|
|
763
|
+
}
|
|
764
|
+
return apiExport;
|
|
765
|
+
},
|
|
766
|
+
|
|
767
|
+
|
|
768
|
+
_shouldIncludeFile(entry) {
|
|
769
|
+
|
|
770
|
+
if (!entry.isFile()) return false;
|
|
771
|
+
|
|
772
|
+
if (!(entry.name.endsWith(".mjs") || entry.name.endsWith(".cjs") || entry.name.endsWith(".js"))) return false;
|
|
773
|
+
|
|
774
|
+
if (entry.name.startsWith(".")) return false;
|
|
775
|
+
|
|
776
|
+
if (entry.name.startsWith("__slothlet_")) return false;
|
|
777
|
+
return true;
|
|
778
|
+
},
|
|
779
|
+
|
|
780
|
+
|
|
781
|
+
updateBoundApiProperty(key, materializedValue) {
|
|
782
|
+
if (this.boundapi && key) {
|
|
783
|
+
try {
|
|
784
|
+
|
|
785
|
+
Object.defineProperty(this.boundapi, key, {
|
|
786
|
+
value: materializedValue,
|
|
787
|
+
writable: true,
|
|
788
|
+
enumerable: true,
|
|
789
|
+
configurable: true
|
|
790
|
+
});
|
|
791
|
+
|
|
792
|
+
|
|
793
|
+
if (self && typeof self === "object") {
|
|
794
|
+
Object.defineProperty(self, key, {
|
|
795
|
+
value: materializedValue,
|
|
796
|
+
writable: true,
|
|
797
|
+
enumerable: true,
|
|
798
|
+
configurable: true
|
|
799
|
+
});
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
|
|
803
|
+
|
|
804
|
+
const currentApi = this.api;
|
|
805
|
+
if (currentApi && currentApi[key] === materializedValue) {
|
|
806
|
+
mutateLiveBindingFunction(this.boundapi, currentApi);
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
if (this.config.debug) {
|
|
810
|
+
console.log(`[DEBUG] Updated boundapi.${key} with materialized value`);
|
|
811
|
+
console.log(`[DEBUG] boundapi.${key} is now:`, typeof this.boundapi[key]);
|
|
812
|
+
console.log(`[DEBUG] boundapi.${key} keys:`, Object.keys(this.boundapi[key] || {}));
|
|
813
|
+
}
|
|
814
|
+
} catch (error) {
|
|
815
|
+
console.warn(`[slothlet] Failed to update boundapi.${key}:`, error.message);
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
},
|
|
819
|
+
|
|
820
|
+
|
|
821
|
+
updateBindings(newContext = null, newReference = null, newSelf = null) {
|
|
822
|
+
if (newContext === null || (typeof newContext === "object" && Object.keys(newContext).length === 0)) newContext = { ...context };
|
|
823
|
+
if (newReference === null || (typeof newReference === "object" && Object.keys(newReference).length === 0))
|
|
824
|
+
newReference = { ...reference };
|
|
825
|
+
if (newSelf === null || (typeof newSelf === "object" && Object.keys(newSelf).length === 0)) newSelf = this.boundapi;
|
|
826
|
+
|
|
827
|
+
|
|
828
|
+
|
|
829
|
+
|
|
830
|
+
mutateLiveBindingFunction(self, newSelf);
|
|
831
|
+
Object.assign(context, newContext || {});
|
|
832
|
+
Object.assign(reference, newReference || {});
|
|
833
|
+
|
|
834
|
+
this.safeDefine(this.boundapi, "__ctx", {
|
|
835
|
+
self: this.boundapi,
|
|
836
|
+
context: this.context,
|
|
837
|
+
reference: this.reference
|
|
838
|
+
});
|
|
839
|
+
},
|
|
840
|
+
|
|
841
|
+
|
|
842
|
+
createBoundApi(ref = null) {
|
|
843
|
+
if (!this.api) throw new Error("BindleApi modules not loaded. Call load() first.");
|
|
844
|
+
|
|
845
|
+
|
|
846
|
+
|
|
847
|
+
|
|
848
|
+
let boundApi;
|
|
849
|
+
|
|
850
|
+
|
|
851
|
+
boundApi = this.api;
|
|
852
|
+
|
|
853
|
+
|
|
854
|
+
if (ref && typeof ref === "object") {
|
|
855
|
+
for (const [key, value] of Object.entries(ref)) {
|
|
856
|
+
if (!(key in boundApi)) {
|
|
857
|
+
try {
|
|
858
|
+
boundApi[key] = value;
|
|
859
|
+
} catch {
|
|
860
|
+
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
|
|
867
|
+
|
|
868
|
+
|
|
869
|
+
|
|
870
|
+
|
|
871
|
+
|
|
872
|
+
|
|
873
|
+
|
|
874
|
+
|
|
875
|
+
|
|
876
|
+
|
|
877
|
+
|
|
878
|
+
|
|
879
|
+
|
|
880
|
+
|
|
881
|
+
|
|
882
|
+
|
|
883
|
+
this.safeDefine(boundApi, "describe", function (showAll = false) {
|
|
884
|
+
|
|
885
|
+
if (this.config && this.config.lazy) {
|
|
886
|
+
if (!showAll) {
|
|
887
|
+
return Reflect.ownKeys(boundApi);
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
async function resolveAll(obj) {
|
|
891
|
+
const keys = Reflect.ownKeys(obj);
|
|
892
|
+
const entries = await Promise.all(
|
|
893
|
+
keys.map(async (key) => {
|
|
894
|
+
const value = obj[key];
|
|
895
|
+
|
|
896
|
+
if (typeof value === "function" && value.constructor.name === "Proxy") {
|
|
897
|
+
let resolved;
|
|
898
|
+
try {
|
|
899
|
+
resolved = await value();
|
|
900
|
+
} catch {
|
|
901
|
+
resolved = value;
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
if (resolved && typeof resolved === "object" && !Array.isArray(resolved)) {
|
|
905
|
+
return [key, await resolveAll(resolved)];
|
|
906
|
+
}
|
|
907
|
+
return [key, resolved];
|
|
908
|
+
} else if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
909
|
+
|
|
910
|
+
return [key, await resolveAll(value)];
|
|
911
|
+
} else {
|
|
912
|
+
return [key, value];
|
|
913
|
+
}
|
|
914
|
+
})
|
|
915
|
+
);
|
|
916
|
+
const apiObj = {};
|
|
917
|
+
for (const [key, val] of entries) {
|
|
918
|
+
apiObj[key] = val;
|
|
919
|
+
}
|
|
920
|
+
return apiObj;
|
|
921
|
+
}
|
|
922
|
+
return resolveAll(boundApi);
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
return { ...boundApi };
|
|
926
|
+
});
|
|
927
|
+
|
|
928
|
+
|
|
929
|
+
if (
|
|
930
|
+
typeof boundApi.shutdown === "function" &&
|
|
931
|
+
boundApi.shutdown !== this.shutdown &&
|
|
932
|
+
boundApi.shutdown.name !== "bound get" &&
|
|
933
|
+
boundApi.shutdown.name !== "bound shutdown" &&
|
|
934
|
+
boundApi.shutdown.toString().indexOf("[native code]") === -1 &&
|
|
935
|
+
boundApi.shutdown.toString() !== this.shutdown.bind(this).toString()
|
|
936
|
+
) {
|
|
937
|
+
this._boundAPIShutdown = boundApi.shutdown;
|
|
938
|
+
} else {
|
|
939
|
+
this._boundAPIShutdown = null;
|
|
940
|
+
}
|
|
941
|
+
const shutdownDesc = Object.getOwnPropertyDescriptor(boundApi, "shutdown");
|
|
942
|
+
if (!shutdownDesc || shutdownDesc.configurable) {
|
|
943
|
+
Object.defineProperty(boundApi, "shutdown", {
|
|
944
|
+
value: this.shutdown.bind(this),
|
|
945
|
+
writable: true,
|
|
946
|
+
configurable: true,
|
|
947
|
+
enumerable: true
|
|
948
|
+
});
|
|
949
|
+
} else if (this.config && this.config.debug) {
|
|
950
|
+
console.warn("Could not redefine boundApi.shutdown: not configurable");
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
|
|
954
|
+
|
|
955
|
+
|
|
956
|
+
|
|
957
|
+
|
|
958
|
+
|
|
959
|
+
return boundApi;
|
|
960
|
+
},
|
|
961
|
+
|
|
962
|
+
|
|
963
|
+
safeDefine(obj, key, value) {
|
|
964
|
+
const desc = Object.getOwnPropertyDescriptor(obj, key);
|
|
965
|
+
if (!desc) {
|
|
966
|
+
Object.defineProperty(obj, key, {
|
|
967
|
+
value,
|
|
968
|
+
writable: true,
|
|
969
|
+
configurable: true,
|
|
970
|
+
enumerable: true
|
|
971
|
+
});
|
|
972
|
+
} else if (desc.configurable) {
|
|
973
|
+
Object.defineProperty(obj, key, {
|
|
974
|
+
value,
|
|
975
|
+
writable: true,
|
|
976
|
+
configurable: true,
|
|
977
|
+
enumerable: true
|
|
978
|
+
});
|
|
979
|
+
} else if (this.config && this.config.debug) {
|
|
980
|
+
console.warn(`Could not redefine boundApi.${key}: not configurable`);
|
|
981
|
+
}
|
|
982
|
+
},
|
|
983
|
+
|
|
984
|
+
|
|
985
|
+
isLoaded() {
|
|
986
|
+
return this.loaded;
|
|
987
|
+
},
|
|
988
|
+
|
|
989
|
+
|
|
990
|
+
getApi() {
|
|
991
|
+
return this.api;
|
|
992
|
+
},
|
|
993
|
+
|
|
994
|
+
|
|
995
|
+
getBoundApi() {
|
|
996
|
+
return this.boundapi;
|
|
997
|
+
},
|
|
998
|
+
|
|
999
|
+
|
|
1000
|
+
async shutdown() {
|
|
1001
|
+
|
|
1002
|
+
|
|
1003
|
+
if (this._shutdownInProgress) {
|
|
1004
|
+
if (!this.loaded) return;
|
|
1005
|
+
console.warn("[slothlet] shutdown already in progress – ignoring nested call.");
|
|
1006
|
+
return;
|
|
1007
|
+
}
|
|
1008
|
+
this._shutdownInProgress = true;
|
|
1009
|
+
try {
|
|
1010
|
+
|
|
1011
|
+
if (this.loaded) {
|
|
1012
|
+
const TIMEOUT_MS = 5000;
|
|
1013
|
+
let apiError, internalError;
|
|
1014
|
+
if (typeof this._boundAPIShutdown === "function") {
|
|
1015
|
+
try {
|
|
1016
|
+
await Promise.race([
|
|
1017
|
+
this._boundAPIShutdown.call(this.boundapi),
|
|
1018
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("API shutdown timeout")), TIMEOUT_MS))
|
|
1019
|
+
]);
|
|
1020
|
+
} catch (err) {
|
|
1021
|
+
apiError = err;
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
const disposeFn = this.boundapi && typeof this.boundapi.__dispose__ === "function" ? this.boundapi.__dispose__ : this._dispose;
|
|
1026
|
+
if (typeof disposeFn === "function") {
|
|
1027
|
+
try {
|
|
1028
|
+
await disposeFn();
|
|
1029
|
+
} catch (err) {
|
|
1030
|
+
internalError = err;
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
this.loaded = false;
|
|
1034
|
+
this.api = null;
|
|
1035
|
+
this.boundapi = {};
|
|
1036
|
+
this.context = {};
|
|
1037
|
+
this.reference = {};
|
|
1038
|
+
this._dispose = null;
|
|
1039
|
+
this._boundAPIShutdown = null;
|
|
1040
|
+
if (apiError || internalError) throw apiError || internalError;
|
|
1041
|
+
}
|
|
1042
|
+
} finally {
|
|
1043
|
+
|
|
1044
|
+
this._shutdownInProgress = false;
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
};
|
|
1048
|
+
|
|
1049
|
+
|
|
1050
|
+
export function mutateLiveBindingFunction(target, source) {
|
|
1051
|
+
if (typeof source === "function") {
|
|
1052
|
+
target._impl = (...args) => source(...args);
|
|
1053
|
+
|
|
1054
|
+
for (const key of Object.keys(target)) {
|
|
1055
|
+
if (key !== "_impl" && key !== "__ctx") delete target[key];
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
for (const key of Object.getOwnPropertyNames(source)) {
|
|
1059
|
+
if (key !== "length" && key !== "name" && key !== "prototype" && key !== "_impl" && key !== "__ctx") {
|
|
1060
|
+
try {
|
|
1061
|
+
target[key] = source[key];
|
|
1062
|
+
} catch {
|
|
1063
|
+
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
} else if (typeof source === "object" && source !== null) {
|
|
1068
|
+
|
|
1069
|
+
for (const key of Object.keys(target)) {
|
|
1070
|
+
if (key !== "_impl" && key !== "__ctx") delete target[key];
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
for (const [key, value] of Object.entries(source)) {
|
|
1074
|
+
if (key !== "__ctx") {
|
|
1075
|
+
target[key] = value;
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
if (typeof source._impl === "function") {
|
|
1080
|
+
target._impl = source._impl;
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
export { slothlet };
|
|
1086
|
+
export default slothlet;
|
|
1087
|
+
|
|
1088
|
+
|
|
1089
|
+
|
|
1090
|
+
|
|
1091
|
+
|
|
1092
|
+
|