nfkit 1.0.25 → 1.0.27

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/dist/index.cjs CHANGED
@@ -1247,11 +1247,11 @@ var observeDiff = (obj, cb) => {
1247
1247
  // src/memorize.ts
1248
1248
  var Memorize = () => {
1249
1249
  const cache = /* @__PURE__ */ new WeakMap();
1250
- const isPromiseLike2 = (v) => v != null && typeof v.then === "function" && typeof v.catch === "function";
1250
+ const isPromiseLike = (v) => v != null && typeof v.then === "function" && typeof v.catch === "function";
1251
1251
  const getOrSet = (instance, compute) => {
1252
1252
  if (cache.has(instance)) return cache.get(instance);
1253
1253
  const result = compute();
1254
- if (isPromiseLike2(result)) {
1254
+ if (isPromiseLike(result)) {
1255
1255
  const wrapped = result.catch((err) => {
1256
1256
  cache.delete(instance);
1257
1257
  throw err;
@@ -1285,56 +1285,7 @@ var makeArray = (value) => {
1285
1285
  return Array.isArray(value) ? value : [value];
1286
1286
  };
1287
1287
 
1288
- // src/app-context/promise-utils.ts
1289
- var promiseStates = /* @__PURE__ */ new WeakMap();
1290
- var isPromiseLike = (value) => !!value && typeof value.then === "function";
1291
- var trackPromise = (promise) => {
1292
- const existing = promiseStates.get(promise);
1293
- if (existing) return existing;
1294
- const state = { status: "pending" };
1295
- promiseStates.set(promise, state);
1296
- promise.then(
1297
- (value) => {
1298
- state.status = "fulfilled";
1299
- state.value = value;
1300
- },
1301
- (error) => {
1302
- state.status = "rejected";
1303
- state.error = error;
1304
- }
1305
- );
1306
- return state;
1307
- };
1308
- var wrapMaybePromise = (value, options) => {
1309
- if (!isPromiseLike(value)) return value;
1310
- const promise = Promise.resolve(value);
1311
- const state = trackPromise(promise);
1312
- if (state.status === "fulfilled") return state.value;
1313
- if (state.status === "rejected") throw state.error;
1314
- return dualizeAny(
1315
- () => {
1316
- const current = trackPromise(promise);
1317
- if (current.status === "fulfilled") return current.value;
1318
- if (current.status === "rejected") throw current.error;
1319
- throwDualPending();
1320
- },
1321
- () => promise,
1322
- {
1323
- // Intentionally hide strict method return type here.
1324
- asyncMethods: Array.from(options?.methodKeys ?? [])
1325
- }
1326
- );
1327
- };
1328
- var createAsyncMethod = (inst, key) => (...args) => Promise.resolve(inst).then((resolved) => {
1329
- const fn = resolved?.[key];
1330
- if (typeof fn !== "function") {
1331
- throw new TypeError("Target method is not a function");
1332
- }
1333
- return fn.apply(resolved, args);
1334
- });
1335
-
1336
1288
  // src/app-context/app-context.ts
1337
- var ProvidePrefix = "provide:";
1338
1289
  var getMethodDescriptor = (cls, key) => {
1339
1290
  let proto = cls.prototype;
1340
1291
  while (proto && proto !== Object.prototype) {
@@ -1344,52 +1295,24 @@ var getMethodDescriptor = (cls, key) => {
1344
1295
  }
1345
1296
  return void 0;
1346
1297
  };
1347
- var flushPendingSets = (entry) => {
1348
- if (isPromiseLike(entry.inst)) return;
1349
- if (!entry.pendingSets.length) return;
1350
- for (const item of entry.pendingSets) {
1351
- entry.inst[item.key] = item.value;
1352
- }
1353
- entry.pendingSets.length = 0;
1354
- };
1355
- var resolveEntryIfNeeded = async (entry) => {
1356
- if (isPromiseLike(entry.inst)) {
1357
- entry.inst = await entry.inst;
1358
- }
1359
- flushPendingSets(entry);
1360
- return entry.inst;
1361
- };
1362
1298
  var AppContextCore = class {
1363
1299
  constructor() {
1300
+ this.provideRecords = [];
1364
1301
  this.registry = /* @__PURE__ */ new Map();
1365
- this.loadSeq = [];
1366
1302
  this.objectSteps = [];
1303
+ this.started = false;
1367
1304
  }
1368
1305
  provide(cls, ...args) {
1369
1306
  const last = args[args.length - 1];
1370
1307
  const hasOptions = !!last && typeof last === "object" && ("provide" in last || "merge" in last || "useValue" in last || "useFactory" in last || "useClass" in last);
1371
1308
  const options = hasOptions ? last : void 0;
1372
1309
  const _args = hasOptions ? args.slice(0, -1) : args;
1373
- const inst = options?.useValue ?? (options?.useFactory ? options.useFactory(this, ..._args) : new (options?.useClass ?? cls)(this, ..._args));
1374
1310
  const classRef = cls;
1375
- const provideKey = options?.provide ? ProvidePrefix + String(options.provide) : void 0;
1376
- const methodKeys = /* @__PURE__ */ new Set();
1377
- for (const name of Object.getOwnPropertyNames(cls.prototype)) {
1378
- if (name === "constructor") continue;
1379
- const desc = Object.getOwnPropertyDescriptor(cls.prototype, name);
1380
- if (desc && typeof desc.value === "function") {
1381
- methodKeys.add(name);
1382
- }
1383
- }
1384
- const entry = {
1311
+ const factory = (ctx) => options?.useValue ?? (options?.useFactory ? options.useFactory(ctx, ..._args) : new (options?.useClass ?? cls)(ctx, ..._args));
1312
+ this.provideRecords.push({
1385
1313
  classRef,
1386
- inst,
1387
- methodKeys,
1388
- pendingSets: []
1389
- };
1390
- this.registry.set(classRef, entry);
1391
- if (provideKey) this.registry.set(provideKey, entry);
1392
- this.loadSeq.push(entry);
1314
+ factory
1315
+ });
1393
1316
  if (options?.provide) {
1394
1317
  const prop = options.provide;
1395
1318
  const step = (ctx) => {
@@ -1398,7 +1321,7 @@ var AppContextCore = class {
1398
1321
  configurable: true,
1399
1322
  get: () => {
1400
1323
  const currentEntry = ctx.registry.get(classRef);
1401
- return wrapMaybePromise(currentEntry?.inst, { methodKeys });
1324
+ return currentEntry?.inst;
1402
1325
  }
1403
1326
  });
1404
1327
  };
@@ -1417,8 +1340,6 @@ var AppContextCore = class {
1417
1340
  get: () => {
1418
1341
  const currentEntry = ctx.registry.get(classRef);
1419
1342
  const currentInst = currentEntry?.inst;
1420
- if (isPromiseLike(currentInst))
1421
- return createAsyncMethod(currentInst, key);
1422
1343
  const fn = currentInst?.[key];
1423
1344
  return typeof fn === "function" ? fn.bind(currentInst) : fn;
1424
1345
  }
@@ -1430,25 +1351,12 @@ var AppContextCore = class {
1430
1351
  configurable: true,
1431
1352
  get: () => {
1432
1353
  const currentEntry = ctx.registry.get(classRef);
1433
- const target = wrapMaybePromise(currentEntry?.inst);
1434
- return target?.[key];
1354
+ return currentEntry?.inst?.[key];
1435
1355
  },
1436
1356
  set: (value) => {
1437
1357
  const currentEntry = ctx.registry.get(classRef);
1438
1358
  if (!currentEntry) return;
1439
- if (!isPromiseLike(currentEntry.inst)) {
1440
- currentEntry.inst[key] = value;
1441
- return;
1442
- }
1443
- const state = trackPromise(currentEntry.inst);
1444
- if (state.status === "fulfilled") {
1445
- currentEntry.inst = state.value;
1446
- flushPendingSets(currentEntry);
1447
- currentEntry.inst[key] = value;
1448
- return;
1449
- }
1450
- if (state.status === "rejected") throw state.error;
1451
- currentEntry.pendingSets.push({ key, value });
1359
+ currentEntry.inst[key] = value;
1452
1360
  }
1453
1361
  });
1454
1362
  };
@@ -1467,53 +1375,24 @@ var AppContextCore = class {
1467
1375
  throw new Error(`Service not provided: ${key?.name ?? cls.name}`);
1468
1376
  }
1469
1377
  const entry = this.registry.get(key);
1470
- const inst = entry.inst;
1471
- if (isPromiseLike(inst)) {
1472
- const state = trackPromise(inst);
1473
- if (state.status === "fulfilled") {
1474
- entry.inst = state.value;
1475
- flushPendingSets(entry);
1476
- return state.value;
1477
- }
1478
- if (state.status === "rejected") throw state.error;
1479
- return wrapMaybePromise(inst, {
1480
- methodKeys: entry.methodKeys
1481
- });
1482
- }
1483
- flushPendingSets(entry);
1484
- return inst;
1378
+ return entry.inst;
1485
1379
  }
1380
+ /**
1381
+ * @deprecated Use get() instead. getAsync() is no longer needed as all services are loaded synchronously during start().
1382
+ */
1486
1383
  async getAsync(cls) {
1487
- let key = cls;
1488
- if (!this.registry.has(key) && typeof cls === "function" && !cls.prototype) {
1489
- key = cls();
1490
- }
1491
- if (!this.registry.has(key)) {
1492
- throw new Error(`Service not provided: ${cls.name}`);
1493
- }
1494
- const entry = this.registry.get(key);
1495
- const resolved = await resolveEntryIfNeeded(entry);
1496
- return resolved;
1384
+ return this.get(cls);
1497
1385
  }
1498
1386
  use(...ctxes) {
1499
1387
  for (const ctx of ctxes) {
1500
1388
  const other = ctx;
1501
- const entryMap = /* @__PURE__ */ new Map();
1502
- if (Array.isArray(other?.loadSeq)) {
1503
- const copiedSeq = other.loadSeq.map((item) => ({
1504
- ...item,
1505
- methodKeys: new Set(item.methodKeys ?? []),
1506
- pendingSets: [...item.pendingSets ?? []]
1507
- }));
1508
- other.loadSeq.forEach((item, index) => {
1509
- entryMap.set(item, copiedSeq[index]);
1510
- });
1511
- this.loadSeq.push(...copiedSeq);
1389
+ if (this.started && !other?.started) {
1390
+ throw new Error(
1391
+ "Cannot use an unstarted context into a started context."
1392
+ );
1512
1393
  }
1513
- if (other?.registry instanceof Map) {
1514
- for (const [key, value] of other.registry.entries()) {
1515
- this.registry.set(key, entryMap.get(value) ?? value);
1516
- }
1394
+ if (Array.isArray(other?.provideRecords)) {
1395
+ this.provideRecords.push(...other.provideRecords);
1517
1396
  }
1518
1397
  if (Array.isArray(other?.objectSteps)) {
1519
1398
  this.objectSteps.push(...other.objectSteps);
@@ -1521,6 +1400,13 @@ var AppContextCore = class {
1521
1400
  step(this);
1522
1401
  }
1523
1402
  }
1403
+ if (other?.started) {
1404
+ if (other?.registry instanceof Map) {
1405
+ for (const [key, value] of other.registry.entries()) {
1406
+ this.registry.set(key, value);
1407
+ }
1408
+ }
1409
+ }
1524
1410
  }
1525
1411
  return this;
1526
1412
  }
@@ -1528,15 +1414,35 @@ var AppContextCore = class {
1528
1414
  return this;
1529
1415
  }
1530
1416
  async start() {
1531
- for (const entry of this.loadSeq) {
1532
- await resolveEntryIfNeeded(entry);
1417
+ if (this.started) {
1418
+ return this;
1419
+ }
1420
+ const startedEntries = [];
1421
+ const preloadedKeys = new Set(this.registry.keys());
1422
+ for (const record of this.provideRecords) {
1423
+ if (preloadedKeys.has(record.classRef)) {
1424
+ continue;
1425
+ }
1426
+ const inst = record.factory(this);
1427
+ const entry = {
1428
+ classRef: record.classRef,
1429
+ inst
1430
+ };
1431
+ this.registry.set(record.classRef, entry);
1432
+ startedEntries.push(entry);
1433
+ }
1434
+ for (const entry of startedEntries) {
1435
+ if (entry.inst && typeof entry.inst.then === "function") {
1436
+ entry.inst = await entry.inst;
1437
+ }
1533
1438
  }
1534
- for (const entry of this.loadSeq) {
1439
+ for (const entry of startedEntries) {
1535
1440
  const inst = entry.inst;
1536
1441
  if (inst && typeof inst.init === "function") {
1537
1442
  await inst.init();
1538
1443
  }
1539
1444
  }
1445
+ this.started = true;
1540
1446
  return this;
1541
1447
  }
1542
1448
  };