nfkit 1.0.20 → 1.0.21

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
@@ -89,6 +89,10 @@ function makeProxy(node) {
89
89
  }
90
90
 
91
91
  // src/dual-object.ts
92
+ var DUAL_PENDING = Symbol("dual.pending");
93
+ var throwDualPending = () => {
94
+ throw DUAL_PENDING;
95
+ };
92
96
  var isThenKey = (k) => k === "then" || k === "catch" || k === "finally";
93
97
  function dualizeAny(sync, asyncFn, options) {
94
98
  let state = "undecided";
@@ -122,6 +126,10 @@ function dualizeAny(sync, asyncFn, options) {
122
126
  value = sync();
123
127
  state = "fulfilled";
124
128
  } catch (e) {
129
+ if (e === DUAL_PENDING) {
130
+ startAsync();
131
+ return;
132
+ }
125
133
  reason = e;
126
134
  state = "rejected";
127
135
  }
@@ -180,6 +188,9 @@ function dualizeAny(sync, asyncFn, options) {
180
188
  case "undecided": {
181
189
  ensureSync();
182
190
  if (state === "fulfilled") return getFrom(value, prop);
191
+ if (state === "pending") {
192
+ throw new TypeError("Value is not ready yet. Please await it first.");
193
+ }
183
194
  if (state === "rejected") throw reason;
184
195
  throw new TypeError("Invalid state transition");
185
196
  }
@@ -1190,11 +1201,11 @@ var observeDiff = (obj, cb) => {
1190
1201
  // src/memorize.ts
1191
1202
  var Memorize = () => {
1192
1203
  const cache = /* @__PURE__ */ new WeakMap();
1193
- const isPromiseLike = (v) => v != null && typeof v.then === "function" && typeof v.catch === "function";
1204
+ const isPromiseLike2 = (v) => v != null && typeof v.then === "function" && typeof v.catch === "function";
1194
1205
  const getOrSet = (instance, compute) => {
1195
1206
  if (cache.has(instance)) return cache.get(instance);
1196
1207
  const result = compute();
1197
- if (isPromiseLike(result)) {
1208
+ if (isPromiseLike2(result)) {
1198
1209
  const wrapped = result.catch((err) => {
1199
1210
  cache.delete(instance);
1200
1211
  throw err;
@@ -1227,8 +1238,271 @@ var Memorize = () => {
1227
1238
  var makeArray = (value) => {
1228
1239
  return Array.isArray(value) ? value : [value];
1229
1240
  };
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
+ // src/app-context/app-context.ts
1291
+ var ProvidePrefix = "provide:";
1292
+ var getMethodDescriptor = (cls, key) => {
1293
+ let proto = cls.prototype;
1294
+ while (proto && proto !== Object.prototype) {
1295
+ const desc = Object.getOwnPropertyDescriptor(proto, key);
1296
+ if (desc) return desc;
1297
+ proto = Object.getPrototypeOf(proto);
1298
+ }
1299
+ return void 0;
1300
+ };
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
+ var AppContextCore = class {
1317
+ constructor() {
1318
+ this.registry = /* @__PURE__ */ new Map();
1319
+ this.loadSeq = [];
1320
+ this.objectSteps = [];
1321
+ }
1322
+ provide(cls, ...args) {
1323
+ const last = args[args.length - 1];
1324
+ const hasOptions = !!last && typeof last === "object" && ("provide" in last || "merge" in last || "useValue" in last || "useFactory" in last || "useClass" in last);
1325
+ const options = hasOptions ? last : void 0;
1326
+ 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
+ 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 = {
1339
+ 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);
1347
+ if (options?.provide) {
1348
+ const prop = options.provide;
1349
+ const step = (ctx) => {
1350
+ Object.defineProperty(ctx, prop, {
1351
+ enumerable: true,
1352
+ configurable: true,
1353
+ get: () => {
1354
+ const currentEntry = ctx.registry.get(classRef);
1355
+ return wrapMaybePromise(currentEntry?.inst, { methodKeys });
1356
+ }
1357
+ });
1358
+ };
1359
+ step(this);
1360
+ this.objectSteps.push(step);
1361
+ }
1362
+ if (options?.merge?.length) {
1363
+ for (const key of options.merge) {
1364
+ const desc = getMethodDescriptor(cls, key);
1365
+ const isMethod = !!desc && typeof desc.value === "function";
1366
+ const step = (ctx) => {
1367
+ if (isMethod) {
1368
+ Object.defineProperty(ctx, key, {
1369
+ enumerable: true,
1370
+ configurable: true,
1371
+ get: () => {
1372
+ const currentEntry = ctx.registry.get(classRef);
1373
+ const currentInst = currentEntry?.inst;
1374
+ if (isPromiseLike(currentInst))
1375
+ return createAsyncMethod(currentInst, key);
1376
+ const fn = currentInst?.[key];
1377
+ return typeof fn === "function" ? fn.bind(currentInst) : fn;
1378
+ }
1379
+ });
1380
+ return;
1381
+ }
1382
+ Object.defineProperty(ctx, key, {
1383
+ enumerable: true,
1384
+ configurable: true,
1385
+ get: () => {
1386
+ const currentEntry = ctx.registry.get(classRef);
1387
+ const target = wrapMaybePromise(currentEntry?.inst);
1388
+ return target?.[key];
1389
+ },
1390
+ set: (value) => {
1391
+ const currentEntry = ctx.registry.get(classRef);
1392
+ 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 });
1406
+ }
1407
+ });
1408
+ };
1409
+ step(this);
1410
+ this.objectSteps.push(step);
1411
+ }
1412
+ }
1413
+ return this;
1414
+ }
1415
+ get(cls) {
1416
+ const key = cls;
1417
+ if (!this.registry.has(key)) {
1418
+ throw new Error(`Service not provided: ${cls.name}`);
1419
+ }
1420
+ const entry = this.registry.get(key);
1421
+ const inst = entry.inst;
1422
+ if (isPromiseLike(inst)) {
1423
+ const state = trackPromise(inst);
1424
+ if (state.status === "fulfilled") {
1425
+ entry.inst = state.value;
1426
+ flushPendingSets(entry);
1427
+ return state.value;
1428
+ }
1429
+ if (state.status === "rejected") throw state.error;
1430
+ return wrapMaybePromise(inst, {
1431
+ methodKeys: entry.methodKeys
1432
+ });
1433
+ }
1434
+ flushPendingSets(entry);
1435
+ return inst;
1436
+ }
1437
+ async getAsync(cls) {
1438
+ const key = cls;
1439
+ if (!this.registry.has(key)) {
1440
+ throw new Error(`Service not provided: ${cls.name}`);
1441
+ }
1442
+ const entry = this.registry.get(key);
1443
+ const resolved = await resolveEntryIfNeeded(entry);
1444
+ return resolved;
1445
+ }
1446
+ use(...ctxes) {
1447
+ for (const ctx of ctxes) {
1448
+ const other = ctx;
1449
+ const entryMap = /* @__PURE__ */ new Map();
1450
+ if (Array.isArray(other?.loadSeq)) {
1451
+ const copiedSeq = other.loadSeq.map((item) => ({
1452
+ ...item,
1453
+ methodKeys: new Set(item.methodKeys ?? []),
1454
+ pendingSets: [...item.pendingSets ?? []]
1455
+ }));
1456
+ other.loadSeq.forEach((item, index) => {
1457
+ entryMap.set(item, copiedSeq[index]);
1458
+ });
1459
+ this.loadSeq.push(...copiedSeq);
1460
+ }
1461
+ if (other?.registry instanceof Map) {
1462
+ for (const [key, value] of other.registry.entries()) {
1463
+ this.registry.set(key, entryMap.get(value) ?? value);
1464
+ }
1465
+ }
1466
+ if (Array.isArray(other?.objectSteps)) {
1467
+ this.objectSteps.push(...other.objectSteps);
1468
+ for (const step of other.objectSteps) {
1469
+ step(this);
1470
+ }
1471
+ }
1472
+ }
1473
+ return this;
1474
+ }
1475
+ define() {
1476
+ return this;
1477
+ }
1478
+ async start() {
1479
+ for (const entry of this.loadSeq) {
1480
+ await resolveEntryIfNeeded(entry);
1481
+ }
1482
+ for (const entry of this.loadSeq) {
1483
+ const inst = entry.inst;
1484
+ if (inst && typeof inst.init === "function") {
1485
+ await inst.init();
1486
+ }
1487
+ }
1488
+ return this;
1489
+ }
1490
+ };
1491
+ var createAppContext = () => new AppContextCore();
1492
+
1493
+ // src/app-context/app-service-base.ts
1494
+ var AppServiceBase = class {
1495
+ constructor(ctx, ...args) {
1496
+ this.ctx = ctx;
1497
+ }
1498
+ async init() {
1499
+ }
1500
+ };
1230
1501
  export {
1231
1502
  AbortedError,
1503
+ AppContextCore,
1504
+ AppServiceBase,
1505
+ DUAL_PENDING,
1232
1506
  DynamicMiddlewareDispatcher,
1233
1507
  I18n,
1234
1508
  I18nLookupMiddleware,
@@ -1239,11 +1513,13 @@ export {
1239
1513
  WF_NODE,
1240
1514
  WorkflowDispatcher,
1241
1515
  abortable,
1516
+ createAppContext,
1242
1517
  createI18nLookupMiddleware,
1243
1518
  dualizeAny,
1244
1519
  makeArray,
1245
1520
  observeDiff,
1246
1521
  patchStringInObject,
1522
+ throwDualPending,
1247
1523
  workflow
1248
1524
  };
1249
1525
  //# sourceMappingURL=index.mjs.map