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.mjs CHANGED
@@ -1201,11 +1201,11 @@ var observeDiff = (obj, cb) => {
1201
1201
  // src/memorize.ts
1202
1202
  var Memorize = () => {
1203
1203
  const cache = /* @__PURE__ */ new WeakMap();
1204
- const isPromiseLike2 = (v) => v != null && typeof v.then === "function" && typeof v.catch === "function";
1204
+ const isPromiseLike = (v) => v != null && typeof v.then === "function" && typeof v.catch === "function";
1205
1205
  const getOrSet = (instance, compute) => {
1206
1206
  if (cache.has(instance)) return cache.get(instance);
1207
1207
  const result = compute();
1208
- if (isPromiseLike2(result)) {
1208
+ if (isPromiseLike(result)) {
1209
1209
  const wrapped = result.catch((err) => {
1210
1210
  cache.delete(instance);
1211
1211
  throw err;
@@ -1239,56 +1239,7 @@ var makeArray = (value) => {
1239
1239
  return Array.isArray(value) ? value : [value];
1240
1240
  };
1241
1241
 
1242
- // src/app-context/promise-utils.ts
1243
- var promiseStates = /* @__PURE__ */ new WeakMap();
1244
- var isPromiseLike = (value) => !!value && typeof value.then === "function";
1245
- var trackPromise = (promise) => {
1246
- const existing = promiseStates.get(promise);
1247
- if (existing) return existing;
1248
- const state = { status: "pending" };
1249
- promiseStates.set(promise, state);
1250
- promise.then(
1251
- (value) => {
1252
- state.status = "fulfilled";
1253
- state.value = value;
1254
- },
1255
- (error) => {
1256
- state.status = "rejected";
1257
- state.error = error;
1258
- }
1259
- );
1260
- return state;
1261
- };
1262
- var wrapMaybePromise = (value, options) => {
1263
- if (!isPromiseLike(value)) return value;
1264
- const promise = Promise.resolve(value);
1265
- const state = trackPromise(promise);
1266
- if (state.status === "fulfilled") return state.value;
1267
- if (state.status === "rejected") throw state.error;
1268
- return dualizeAny(
1269
- () => {
1270
- const current = trackPromise(promise);
1271
- if (current.status === "fulfilled") return current.value;
1272
- if (current.status === "rejected") throw current.error;
1273
- throwDualPending();
1274
- },
1275
- () => promise,
1276
- {
1277
- // Intentionally hide strict method return type here.
1278
- asyncMethods: Array.from(options?.methodKeys ?? [])
1279
- }
1280
- );
1281
- };
1282
- var createAsyncMethod = (inst, key) => (...args) => Promise.resolve(inst).then((resolved) => {
1283
- const fn = resolved?.[key];
1284
- if (typeof fn !== "function") {
1285
- throw new TypeError("Target method is not a function");
1286
- }
1287
- return fn.apply(resolved, args);
1288
- });
1289
-
1290
1242
  // src/app-context/app-context.ts
1291
- var ProvidePrefix = "provide:";
1292
1243
  var getMethodDescriptor = (cls, key) => {
1293
1244
  let proto = cls.prototype;
1294
1245
  while (proto && proto !== Object.prototype) {
@@ -1298,52 +1249,24 @@ var getMethodDescriptor = (cls, key) => {
1298
1249
  }
1299
1250
  return void 0;
1300
1251
  };
1301
- var flushPendingSets = (entry) => {
1302
- if (isPromiseLike(entry.inst)) return;
1303
- if (!entry.pendingSets.length) return;
1304
- for (const item of entry.pendingSets) {
1305
- entry.inst[item.key] = item.value;
1306
- }
1307
- entry.pendingSets.length = 0;
1308
- };
1309
- var resolveEntryIfNeeded = async (entry) => {
1310
- if (isPromiseLike(entry.inst)) {
1311
- entry.inst = await entry.inst;
1312
- }
1313
- flushPendingSets(entry);
1314
- return entry.inst;
1315
- };
1316
1252
  var AppContextCore = class {
1317
1253
  constructor() {
1254
+ this.provideRecords = [];
1318
1255
  this.registry = /* @__PURE__ */ new Map();
1319
- this.loadSeq = [];
1320
1256
  this.objectSteps = [];
1257
+ this.started = false;
1321
1258
  }
1322
1259
  provide(cls, ...args) {
1323
1260
  const last = args[args.length - 1];
1324
1261
  const hasOptions = !!last && typeof last === "object" && ("provide" in last || "merge" in last || "useValue" in last || "useFactory" in last || "useClass" in last);
1325
1262
  const options = hasOptions ? last : void 0;
1326
1263
  const _args = hasOptions ? args.slice(0, -1) : args;
1327
- const inst = options?.useValue ?? (options?.useFactory ? options.useFactory(this, ..._args) : new (options?.useClass ?? cls)(this, ..._args));
1328
1264
  const classRef = cls;
1329
- const provideKey = options?.provide ? ProvidePrefix + String(options.provide) : void 0;
1330
- const methodKeys = /* @__PURE__ */ new Set();
1331
- for (const name of Object.getOwnPropertyNames(cls.prototype)) {
1332
- if (name === "constructor") continue;
1333
- const desc = Object.getOwnPropertyDescriptor(cls.prototype, name);
1334
- if (desc && typeof desc.value === "function") {
1335
- methodKeys.add(name);
1336
- }
1337
- }
1338
- const entry = {
1265
+ const factory = (ctx) => options?.useValue ?? (options?.useFactory ? options.useFactory(ctx, ..._args) : new (options?.useClass ?? cls)(ctx, ..._args));
1266
+ this.provideRecords.push({
1339
1267
  classRef,
1340
- inst,
1341
- methodKeys,
1342
- pendingSets: []
1343
- };
1344
- this.registry.set(classRef, entry);
1345
- if (provideKey) this.registry.set(provideKey, entry);
1346
- this.loadSeq.push(entry);
1268
+ factory
1269
+ });
1347
1270
  if (options?.provide) {
1348
1271
  const prop = options.provide;
1349
1272
  const step = (ctx) => {
@@ -1352,7 +1275,7 @@ var AppContextCore = class {
1352
1275
  configurable: true,
1353
1276
  get: () => {
1354
1277
  const currentEntry = ctx.registry.get(classRef);
1355
- return wrapMaybePromise(currentEntry?.inst, { methodKeys });
1278
+ return currentEntry?.inst;
1356
1279
  }
1357
1280
  });
1358
1281
  };
@@ -1371,8 +1294,6 @@ var AppContextCore = class {
1371
1294
  get: () => {
1372
1295
  const currentEntry = ctx.registry.get(classRef);
1373
1296
  const currentInst = currentEntry?.inst;
1374
- if (isPromiseLike(currentInst))
1375
- return createAsyncMethod(currentInst, key);
1376
1297
  const fn = currentInst?.[key];
1377
1298
  return typeof fn === "function" ? fn.bind(currentInst) : fn;
1378
1299
  }
@@ -1384,25 +1305,12 @@ var AppContextCore = class {
1384
1305
  configurable: true,
1385
1306
  get: () => {
1386
1307
  const currentEntry = ctx.registry.get(classRef);
1387
- const target = wrapMaybePromise(currentEntry?.inst);
1388
- return target?.[key];
1308
+ return currentEntry?.inst?.[key];
1389
1309
  },
1390
1310
  set: (value) => {
1391
1311
  const currentEntry = ctx.registry.get(classRef);
1392
1312
  if (!currentEntry) return;
1393
- if (!isPromiseLike(currentEntry.inst)) {
1394
- currentEntry.inst[key] = value;
1395
- return;
1396
- }
1397
- const state = trackPromise(currentEntry.inst);
1398
- if (state.status === "fulfilled") {
1399
- currentEntry.inst = state.value;
1400
- flushPendingSets(currentEntry);
1401
- currentEntry.inst[key] = value;
1402
- return;
1403
- }
1404
- if (state.status === "rejected") throw state.error;
1405
- currentEntry.pendingSets.push({ key, value });
1313
+ currentEntry.inst[key] = value;
1406
1314
  }
1407
1315
  });
1408
1316
  };
@@ -1421,53 +1329,24 @@ var AppContextCore = class {
1421
1329
  throw new Error(`Service not provided: ${key?.name ?? cls.name}`);
1422
1330
  }
1423
1331
  const entry = this.registry.get(key);
1424
- const inst = entry.inst;
1425
- if (isPromiseLike(inst)) {
1426
- const state = trackPromise(inst);
1427
- if (state.status === "fulfilled") {
1428
- entry.inst = state.value;
1429
- flushPendingSets(entry);
1430
- return state.value;
1431
- }
1432
- if (state.status === "rejected") throw state.error;
1433
- return wrapMaybePromise(inst, {
1434
- methodKeys: entry.methodKeys
1435
- });
1436
- }
1437
- flushPendingSets(entry);
1438
- return inst;
1332
+ return entry.inst;
1439
1333
  }
1334
+ /**
1335
+ * @deprecated Use get() instead. getAsync() is no longer needed as all services are loaded synchronously during start().
1336
+ */
1440
1337
  async getAsync(cls) {
1441
- let key = cls;
1442
- if (!this.registry.has(key) && typeof cls === "function" && !cls.prototype) {
1443
- key = cls();
1444
- }
1445
- if (!this.registry.has(key)) {
1446
- throw new Error(`Service not provided: ${cls.name}`);
1447
- }
1448
- const entry = this.registry.get(key);
1449
- const resolved = await resolveEntryIfNeeded(entry);
1450
- return resolved;
1338
+ return this.get(cls);
1451
1339
  }
1452
1340
  use(...ctxes) {
1453
1341
  for (const ctx of ctxes) {
1454
1342
  const other = ctx;
1455
- const entryMap = /* @__PURE__ */ new Map();
1456
- if (Array.isArray(other?.loadSeq)) {
1457
- const copiedSeq = other.loadSeq.map((item) => ({
1458
- ...item,
1459
- methodKeys: new Set(item.methodKeys ?? []),
1460
- pendingSets: [...item.pendingSets ?? []]
1461
- }));
1462
- other.loadSeq.forEach((item, index) => {
1463
- entryMap.set(item, copiedSeq[index]);
1464
- });
1465
- this.loadSeq.push(...copiedSeq);
1343
+ if (this.started && !other?.started) {
1344
+ throw new Error(
1345
+ "Cannot use an unstarted context into a started context."
1346
+ );
1466
1347
  }
1467
- if (other?.registry instanceof Map) {
1468
- for (const [key, value] of other.registry.entries()) {
1469
- this.registry.set(key, entryMap.get(value) ?? value);
1470
- }
1348
+ if (Array.isArray(other?.provideRecords)) {
1349
+ this.provideRecords.push(...other.provideRecords);
1471
1350
  }
1472
1351
  if (Array.isArray(other?.objectSteps)) {
1473
1352
  this.objectSteps.push(...other.objectSteps);
@@ -1475,6 +1354,13 @@ var AppContextCore = class {
1475
1354
  step(this);
1476
1355
  }
1477
1356
  }
1357
+ if (other?.started) {
1358
+ if (other?.registry instanceof Map) {
1359
+ for (const [key, value] of other.registry.entries()) {
1360
+ this.registry.set(key, value);
1361
+ }
1362
+ }
1363
+ }
1478
1364
  }
1479
1365
  return this;
1480
1366
  }
@@ -1482,15 +1368,35 @@ var AppContextCore = class {
1482
1368
  return this;
1483
1369
  }
1484
1370
  async start() {
1485
- for (const entry of this.loadSeq) {
1486
- await resolveEntryIfNeeded(entry);
1371
+ if (this.started) {
1372
+ return this;
1373
+ }
1374
+ const startedEntries = [];
1375
+ const preloadedKeys = new Set(this.registry.keys());
1376
+ for (const record of this.provideRecords) {
1377
+ if (preloadedKeys.has(record.classRef)) {
1378
+ continue;
1379
+ }
1380
+ const inst = record.factory(this);
1381
+ const entry = {
1382
+ classRef: record.classRef,
1383
+ inst
1384
+ };
1385
+ this.registry.set(record.classRef, entry);
1386
+ startedEntries.push(entry);
1387
+ }
1388
+ for (const entry of startedEntries) {
1389
+ if (entry.inst && typeof entry.inst.then === "function") {
1390
+ entry.inst = await entry.inst;
1391
+ }
1487
1392
  }
1488
- for (const entry of this.loadSeq) {
1393
+ for (const entry of startedEntries) {
1489
1394
  const inst = entry.inst;
1490
1395
  if (inst && typeof inst.init === "function") {
1491
1396
  await inst.init();
1492
1397
  }
1493
1398
  }
1399
+ this.started = true;
1494
1400
  return this;
1495
1401
  }
1496
1402
  };