@tstdl/base 0.93.139 → 0.93.141
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 +166 -0
- package/ai/genkit/multi-region.plugin.js +5 -3
- package/ai/genkit/tests/multi-region.test.d.ts +1 -0
- package/ai/genkit/tests/multi-region.test.js +5 -2
- package/ai/parser/parser.js +2 -2
- package/ai/prompts/build.js +1 -0
- package/ai/prompts/instructions-formatter.d.ts +15 -2
- package/ai/prompts/instructions-formatter.js +36 -31
- package/ai/prompts/prompt-builder.js +5 -5
- package/ai/prompts/steering.d.ts +3 -2
- package/ai/prompts/steering.js +3 -1
- package/ai/tests/instructions-formatter.test.js +1 -0
- package/api/README.md +403 -0
- package/api/client/client.js +7 -13
- package/api/client/tests/api-client.test.js +10 -10
- package/api/default-error-handlers.js +1 -1
- package/api/response.d.ts +2 -2
- package/api/response.js +22 -33
- package/api/server/api-controller.d.ts +1 -1
- package/api/server/api-controller.js +3 -3
- package/api/server/api-request-token.provider.d.ts +1 -0
- package/api/server/api-request-token.provider.js +1 -0
- package/api/server/middlewares/allowed-methods.middleware.js +2 -1
- package/api/server/middlewares/content-type.middleware.js +2 -1
- package/api/types.d.ts +3 -2
- package/application/README.md +240 -0
- package/application/application.d.ts +1 -1
- package/application/application.js +3 -3
- package/application/providers.d.ts +20 -2
- package/application/providers.js +34 -7
- package/audit/README.md +267 -0
- package/audit/module.d.ts +5 -0
- package/audit/module.js +9 -1
- package/authentication/README.md +288 -0
- package/authentication/client/authentication.service.d.ts +12 -11
- package/authentication/client/authentication.service.js +21 -21
- package/authentication/client/http-client.middleware.js +2 -2
- package/authentication/server/module.d.ts +5 -0
- package/authentication/server/module.js +9 -1
- package/authentication/tests/authentication.api-controller.test.js +1 -1
- package/authentication/tests/authentication.api-request-token.provider.test.js +1 -1
- package/authentication/tests/authentication.client-error-handling.test.js +2 -1
- package/authentication/tests/authentication.client-service-refresh.test.js +5 -3
- package/authentication/tests/authentication.client-service.test.js +1 -1
- package/browser/README.md +401 -0
- package/cancellation/README.md +156 -0
- package/cancellation/tests/coverage.test.d.ts +1 -0
- package/cancellation/tests/coverage.test.js +49 -0
- package/cancellation/tests/leak.test.js +24 -29
- package/cancellation/tests/token.test.d.ts +1 -0
- package/cancellation/tests/token.test.js +136 -0
- package/cancellation/token.d.ts +53 -177
- package/cancellation/token.js +132 -208
- package/circuit-breaker/postgres/module.d.ts +1 -0
- package/circuit-breaker/postgres/module.js +5 -1
- package/context/README.md +174 -0
- package/cookie/README.md +161 -0
- package/css/README.md +157 -0
- package/data-structures/README.md +320 -0
- package/decorators/README.md +140 -0
- package/distributed-loop/README.md +231 -0
- package/distributed-loop/distributed-loop.js +1 -1
- package/document-management/README.md +403 -0
- package/document-management/server/configure.js +5 -1
- package/document-management/server/module.d.ts +1 -1
- package/document-management/server/module.js +1 -1
- package/document-management/server/services/document-management-ancillary.service.js +1 -1
- package/document-management/server/services/document-management.service.js +9 -7
- package/document-management/tests/ai-config-hierarchy.test.js +0 -5
- package/document-management/tests/document-management-ai-overrides.test.js +0 -1
- package/document-management/tests/document-management-core.test.js +2 -7
- package/document-management/tests/document-management.api.test.js +6 -7
- package/document-management/tests/document-statistics.service.test.js +11 -12
- package/document-management/tests/document-validation-ai-overrides.test.js +0 -1
- package/document-management/tests/document.service.test.js +3 -3
- package/document-management/tests/enum-helpers.test.js +2 -3
- package/dom/README.md +213 -0
- package/enumerable/README.md +259 -0
- package/enumeration/README.md +121 -0
- package/errors/README.md +267 -0
- package/examples/document-management/main.d.ts +1 -0
- package/examples/document-management/main.js +14 -11
- package/file/README.md +191 -0
- package/formats/README.md +210 -0
- package/function/README.md +144 -0
- package/http/README.md +318 -0
- package/http/client/adapters/undici.adapter.js +1 -1
- package/http/client/http-client-request.d.ts +6 -5
- package/http/client/http-client-request.js +8 -9
- package/http/server/node/node-http-server.js +1 -2
- package/image-service/README.md +137 -0
- package/injector/README.md +491 -0
- package/intl/README.md +113 -0
- package/json-path/README.md +182 -0
- package/jsx/README.md +154 -0
- package/key-value-store/README.md +191 -0
- package/key-value-store/postgres/module.d.ts +1 -0
- package/key-value-store/postgres/module.js +5 -1
- package/lock/README.md +249 -0
- package/lock/postgres/module.d.ts +1 -0
- package/lock/postgres/module.js +5 -1
- package/lock/web/web-lock.js +119 -47
- package/logger/README.md +287 -0
- package/mail/README.md +256 -0
- package/mail/module.d.ts +5 -1
- package/mail/module.js +11 -6
- package/memory/README.md +144 -0
- package/message-bus/README.md +244 -0
- package/message-bus/message-bus-base.js +1 -1
- package/module/README.md +182 -0
- package/module/module.d.ts +1 -1
- package/module/module.js +77 -17
- package/module/modules/web-server.module.js +3 -4
- package/notification/server/module.d.ts +1 -0
- package/notification/server/module.js +5 -1
- package/notification/tests/notification-flow.test.js +2 -2
- package/notification/tests/notification-type.service.test.js +24 -15
- package/object-storage/README.md +300 -0
- package/openid-connect/README.md +274 -0
- package/orm/README.md +423 -0
- package/orm/decorators.d.ts +5 -1
- package/orm/decorators.js +1 -1
- package/orm/server/drizzle/schema-converter.js +17 -30
- package/orm/server/encryption.d.ts +0 -1
- package/orm/server/encryption.js +1 -4
- package/orm/server/index.d.ts +1 -6
- package/orm/server/index.js +1 -6
- package/orm/server/migration.d.ts +19 -0
- package/orm/server/migration.js +72 -0
- package/orm/server/repository.d.ts +1 -1
- package/orm/server/transaction.d.ts +5 -10
- package/orm/server/transaction.js +22 -26
- package/orm/server/transactional.js +3 -3
- package/orm/tests/database-migration.test.d.ts +1 -0
- package/orm/tests/database-migration.test.js +82 -0
- package/orm/tests/encryption.test.js +3 -4
- package/orm/utils.d.ts +17 -2
- package/orm/utils.js +49 -1
- package/package.json +9 -6
- package/password/README.md +164 -0
- package/pdf/README.md +246 -0
- package/polyfills.js +1 -0
- package/pool/README.md +198 -0
- package/process/README.md +237 -0
- package/promise/README.md +252 -0
- package/promise/cancelable-promise.js +1 -1
- package/random/README.md +193 -0
- package/rate-limit/postgres/module.d.ts +1 -0
- package/rate-limit/postgres/module.js +5 -1
- package/reflection/README.md +305 -0
- package/reflection/decorator-data.js +11 -12
- package/rpc/README.md +386 -0
- package/rxjs-utils/README.md +262 -0
- package/schema/README.md +342 -0
- package/serializer/README.md +342 -0
- package/signals/implementation/README.md +134 -0
- package/sse/README.md +278 -0
- package/task-queue/README.md +293 -0
- package/task-queue/postgres/drizzle/{0000_simple_invisible_woman.sql → 0000_wakeful_sunspot.sql} +22 -14
- package/task-queue/postgres/drizzle/meta/0000_snapshot.json +160 -82
- package/task-queue/postgres/drizzle/meta/_journal.json +2 -2
- package/task-queue/postgres/module.d.ts +1 -0
- package/task-queue/postgres/module.js +5 -1
- package/task-queue/postgres/schemas.d.ts +9 -6
- package/task-queue/postgres/schemas.js +4 -3
- package/task-queue/postgres/task-queue.d.ts +4 -13
- package/task-queue/postgres/task-queue.js +462 -355
- package/task-queue/postgres/task.model.d.ts +12 -5
- package/task-queue/postgres/task.model.js +51 -25
- package/task-queue/task-context.d.ts +2 -2
- package/task-queue/task-context.js +8 -8
- package/task-queue/task-queue.d.ts +53 -19
- package/task-queue/task-queue.js +121 -55
- package/task-queue/tests/cascading-cancellations.test.d.ts +1 -0
- package/task-queue/tests/cascading-cancellations.test.js +38 -0
- package/task-queue/tests/complex.test.js +45 -229
- package/task-queue/tests/coverage-branch.test.d.ts +1 -0
- package/task-queue/tests/coverage-branch.test.js +407 -0
- package/task-queue/tests/coverage-enhancement.test.d.ts +1 -0
- package/task-queue/tests/coverage-enhancement.test.js +144 -0
- package/task-queue/tests/dag-dependencies.test.d.ts +1 -0
- package/task-queue/tests/dag-dependencies.test.js +41 -0
- package/task-queue/tests/dependencies.test.js +28 -26
- package/task-queue/tests/extensive-dependencies.test.js +64 -139
- package/task-queue/tests/fan-out-spawning.test.d.ts +1 -0
- package/task-queue/tests/fan-out-spawning.test.js +53 -0
- package/task-queue/tests/idempotent-replacement.test.d.ts +1 -0
- package/task-queue/tests/idempotent-replacement.test.js +61 -0
- package/task-queue/tests/missing-idempotent-tasks.test.d.ts +1 -0
- package/task-queue/tests/missing-idempotent-tasks.test.js +38 -0
- package/task-queue/tests/queue.test.js +128 -8
- package/task-queue/tests/worker.test.js +39 -16
- package/task-queue/tests/zombie-parent.test.d.ts +1 -0
- package/task-queue/tests/zombie-parent.test.js +45 -0
- package/task-queue/tests/zombie-recovery.test.d.ts +1 -0
- package/task-queue/tests/zombie-recovery.test.js +51 -0
- package/templates/README.md +287 -0
- package/test5.js +5 -5
- package/testing/README.md +157 -0
- package/testing/integration-setup.d.ts +4 -4
- package/testing/integration-setup.js +54 -29
- package/text/README.md +346 -0
- package/text/localization.service.js +2 -2
- package/threading/README.md +238 -0
- package/types/README.md +311 -0
- package/utils/README.md +322 -0
- package/utils/async-iterable-helpers/observable-iterable.d.ts +1 -1
- package/utils/async-iterable-helpers/observable-iterable.js +4 -8
- package/utils/async-iterable-helpers/take-until.js +4 -4
- package/utils/backoff.js +89 -30
- package/utils/file-reader.js +1 -2
- package/utils/retry-with-backoff.js +1 -1
- package/utils/timer.d.ts +1 -1
- package/utils/timer.js +5 -7
- package/utils/timing.d.ts +1 -1
- package/utils/timing.js +2 -4
- package/utils/z-base32.d.ts +1 -0
- package/utils/z-base32.js +1 -0
|
@@ -9,7 +9,6 @@ import { DocumentValidationExecution, DocumentValidationExecutionState, Document
|
|
|
9
9
|
import { DocumentWorkflow, DocumentWorkflowState, DocumentWorkflowStep } from '../models/document-workflow.model.js';
|
|
10
10
|
import { DocumentApproval } from '../models/document.model.js';
|
|
11
11
|
import { configureDocumentManagement } from '../server/configure.js';
|
|
12
|
-
import { migrateDocumentManagementSchema } from '../server/module.js';
|
|
13
12
|
import { DocumentCategoryTypeService } from '../server/services/document-category-type.service.js';
|
|
14
13
|
import { DocumentCollectionService } from '../server/services/document-collection.service.js';
|
|
15
14
|
import { DocumentManagementAiService } from '../server/services/document-management-ai.service.js';
|
|
@@ -29,7 +28,7 @@ describe('DocumentStatisticsService', () => {
|
|
|
29
28
|
const tenantId = crypto.randomUUID();
|
|
30
29
|
beforeAll(async () => {
|
|
31
30
|
({ injector, database } = await setupIntegrationTest({
|
|
32
|
-
modules: { taskQueue: true },
|
|
31
|
+
modules: { taskQueue: true, documentManagement: true },
|
|
33
32
|
orm: { schema },
|
|
34
33
|
}));
|
|
35
34
|
const mockObjectStorage = {
|
|
@@ -56,8 +55,8 @@ describe('DocumentStatisticsService', () => {
|
|
|
56
55
|
fileObjectStorageModule: 'documents',
|
|
57
56
|
fileUploadObjectStorageModule: 'document-uploads',
|
|
58
57
|
filePreviewObjectStorageModule: 'document-previews',
|
|
58
|
+
injector,
|
|
59
59
|
});
|
|
60
|
-
await runInInjectionContext(injector, migrateDocumentManagementSchema);
|
|
61
60
|
documentService = await injector.resolveAsync(DocumentService);
|
|
62
61
|
statsService = await injector.resolveAsync(DocumentStatisticsService);
|
|
63
62
|
collectionService = await injector.resolveAsync(DocumentCollectionService);
|
|
@@ -288,7 +287,7 @@ describe('DocumentStatisticsService', () => {
|
|
|
288
287
|
const stats = await statsService.getStatistics(tenantId, {
|
|
289
288
|
includeTotalCount: true,
|
|
290
289
|
collectionIds: [collection1.id],
|
|
291
|
-
categoryIds: [category1.id]
|
|
290
|
+
categoryIds: [category1.id],
|
|
292
291
|
});
|
|
293
292
|
expect(stats.totalCount).toBe(1); // Only Doc 1 matches both
|
|
294
293
|
});
|
|
@@ -310,7 +309,7 @@ describe('DocumentStatisticsService', () => {
|
|
|
310
309
|
includeMimeTypeDistribution: true,
|
|
311
310
|
includeStorageUsage: true,
|
|
312
311
|
includeDataQuality: true,
|
|
313
|
-
collectionIds: [collection.id]
|
|
312
|
+
collectionIds: [collection.id],
|
|
314
313
|
});
|
|
315
314
|
expect(stats.totalCount).toBe(1);
|
|
316
315
|
expect(stats.typeBreakdown?.[type.id]).toBe(1);
|
|
@@ -328,7 +327,7 @@ describe('DocumentStatisticsService', () => {
|
|
|
328
327
|
const stats = await statsService.getStatistics(tenantId, {
|
|
329
328
|
includeTotalCount: true,
|
|
330
329
|
includeMimeTypeDistribution: true,
|
|
331
|
-
collectionIds: [collection.id]
|
|
330
|
+
collectionIds: [collection.id],
|
|
332
331
|
});
|
|
333
332
|
expect(stats.totalCount).toBe(1);
|
|
334
333
|
});
|
|
@@ -346,7 +345,7 @@ describe('DocumentStatisticsService', () => {
|
|
|
346
345
|
const stats = await statsService.getStatistics(tenantId, {
|
|
347
346
|
includeMimeTypeDistribution: true,
|
|
348
347
|
collectionIds: [collection.id],
|
|
349
|
-
categoryIds: [category.id]
|
|
348
|
+
categoryIds: [category.id],
|
|
350
349
|
});
|
|
351
350
|
expect(Object.keys(stats.mimeTypeDistribution ?? {}).length).toBeGreaterThan(0);
|
|
352
351
|
});
|
|
@@ -366,7 +365,7 @@ describe('DocumentStatisticsService', () => {
|
|
|
366
365
|
const stats = await statsService.getStatistics(tenantId, {
|
|
367
366
|
includeTagUsage: true,
|
|
368
367
|
collectionIds: [collection.id],
|
|
369
|
-
categoryIds: [category.id]
|
|
368
|
+
categoryIds: [category.id],
|
|
370
369
|
});
|
|
371
370
|
expect(stats.tagUsage?.[tag.id]).toBe(1);
|
|
372
371
|
});
|
|
@@ -410,7 +409,7 @@ describe('DocumentStatisticsService', () => {
|
|
|
410
409
|
});
|
|
411
410
|
const stats = await statsService.getStatistics(tenantId, {
|
|
412
411
|
includeValidationFailures: true,
|
|
413
|
-
collectionIds: [collection.id]
|
|
412
|
+
collectionIds: [collection.id],
|
|
414
413
|
});
|
|
415
414
|
expect(stats.validationFailures).toBe(1);
|
|
416
415
|
});
|
|
@@ -459,7 +458,7 @@ describe('DocumentStatisticsService', () => {
|
|
|
459
458
|
const stats = await statsService.getStatistics(tenantId, {
|
|
460
459
|
includeValidationFailures: true,
|
|
461
460
|
collectionIds: [collection.id],
|
|
462
|
-
categoryIds: [category.id]
|
|
461
|
+
categoryIds: [category.id],
|
|
463
462
|
});
|
|
464
463
|
expect(stats.validationFailures).toBe(1);
|
|
465
464
|
});
|
|
@@ -476,7 +475,7 @@ describe('DocumentStatisticsService', () => {
|
|
|
476
475
|
}, new Uint8Array([1]));
|
|
477
476
|
const stats = await statsService.getStatistics(tenantId, {
|
|
478
477
|
includeTypeBreakdown: true,
|
|
479
|
-
collectionIds: [collection.id]
|
|
478
|
+
collectionIds: [collection.id],
|
|
480
479
|
});
|
|
481
480
|
expect(stats.typeBreakdown?.[type.id]).toBe(1);
|
|
482
481
|
});
|
|
@@ -488,7 +487,7 @@ describe('DocumentStatisticsService', () => {
|
|
|
488
487
|
includeStorageUsage: true,
|
|
489
488
|
includeTotalPages: true,
|
|
490
489
|
includeValidationFailures: true,
|
|
491
|
-
collectionIds: []
|
|
490
|
+
collectionIds: [],
|
|
492
491
|
});
|
|
493
492
|
expect(stats.totalCount).toBe(0);
|
|
494
493
|
expect(stats.storageUsage).toBe(0);
|
|
@@ -32,7 +32,6 @@ describe('AiValidationExecutor Overrides', () => {
|
|
|
32
32
|
};
|
|
33
33
|
beforeAll(async () => {
|
|
34
34
|
({ injector } = await setupIntegrationTest({
|
|
35
|
-
orm: { schema: 'document_management' },
|
|
36
35
|
modules: { messageBus: true, signals: true, objectStorage: true },
|
|
37
36
|
}));
|
|
38
37
|
runInInjectionContext(injector, () => {
|
|
@@ -6,7 +6,7 @@ import { clearTenantData, setupIntegrationTest } from '../../testing/index.js';
|
|
|
6
6
|
import { DocumentWorkflowState, DocumentWorkflowStep } from '../models/document-workflow.model.js';
|
|
7
7
|
import { DocumentApproval } from '../models/document.model.js';
|
|
8
8
|
import { configureDocumentManagement } from '../server/configure.js';
|
|
9
|
-
import { DocumentManagementConfiguration
|
|
9
|
+
import { DocumentManagementConfiguration } from '../server/module.js';
|
|
10
10
|
import { DocumentCollectionService } from '../server/services/document-collection.service.js';
|
|
11
11
|
import { DocumentManagementAiService } from '../server/services/document-management-ai.service.js';
|
|
12
12
|
import { DocumentWorkflowService } from '../server/services/document-workflow.service.js';
|
|
@@ -22,7 +22,7 @@ describe('DocumentService', () => {
|
|
|
22
22
|
const tenantId = crypto.randomUUID();
|
|
23
23
|
beforeAll(async () => {
|
|
24
24
|
({ injector, database } = await setupIntegrationTest({
|
|
25
|
-
modules: { taskQueue: true },
|
|
25
|
+
modules: { taskQueue: true, documentManagement: true },
|
|
26
26
|
orm: { schema },
|
|
27
27
|
}));
|
|
28
28
|
const mockObjectStorage = {
|
|
@@ -49,8 +49,8 @@ describe('DocumentService', () => {
|
|
|
49
49
|
fileObjectStorageModule: 'documents',
|
|
50
50
|
fileUploadObjectStorageModule: 'document-uploads',
|
|
51
51
|
filePreviewObjectStorageModule: 'document-previews',
|
|
52
|
+
injector,
|
|
52
53
|
});
|
|
53
|
-
await runInInjectionContext(injector, migrateDocumentManagementSchema);
|
|
54
54
|
documentService = await injector.resolveAsync(DocumentService);
|
|
55
55
|
workflowService = await injector.resolveAsync(DocumentWorkflowService);
|
|
56
56
|
collectionService = await injector.resolveAsync(DocumentCollectionService);
|
|
@@ -7,7 +7,6 @@ import { TaskQueue } from '../../task-queue/index.js';
|
|
|
7
7
|
import { clearTenantData, setupIntegrationTest } from '../../testing/index.js';
|
|
8
8
|
import { DocumentPropertyDataType } from '../models/document-property.model.js';
|
|
9
9
|
import { configureDocumentManagement } from '../server/configure.js';
|
|
10
|
-
import { migrateDocumentManagementSchema } from '../server/module.js';
|
|
11
10
|
import { DocumentCategoryTypeService } from '../server/services/document-category-type.service.js';
|
|
12
11
|
import { DocumentManagementAiService } from '../server/services/document-management-ai.service.js';
|
|
13
12
|
import { DocumentManagementService } from '../server/services/document-management.service.js';
|
|
@@ -24,7 +23,7 @@ describe('Document Management Extended Suite', () => {
|
|
|
24
23
|
const otherTenantId = crypto.randomUUID();
|
|
25
24
|
beforeAll(async () => {
|
|
26
25
|
({ injector, database } = await setupIntegrationTest({
|
|
27
|
-
modules: { taskQueue: false, messageBus: true },
|
|
26
|
+
modules: { taskQueue: false, messageBus: true, documentManagement: true },
|
|
28
27
|
orm: { schema },
|
|
29
28
|
}));
|
|
30
29
|
injector.register(GenkitModuleOptions, { useValue: {} });
|
|
@@ -52,8 +51,8 @@ describe('Document Management Extended Suite', () => {
|
|
|
52
51
|
fileObjectStorageModule: 'documents',
|
|
53
52
|
fileUploadObjectStorageModule: 'document-uploads',
|
|
54
53
|
filePreviewObjectStorageModule: 'document-previews',
|
|
54
|
+
injector,
|
|
55
55
|
});
|
|
56
|
-
await runInInjectionContext(injector, migrateDocumentManagementSchema);
|
|
57
56
|
documentManagementService = await injector.resolveAsync(DocumentManagementService);
|
|
58
57
|
categoryTypeService = await injector.resolveAsync(DocumentCategoryTypeService);
|
|
59
58
|
propertyService = await injector.resolveAsync(DocumentPropertyService);
|
package/dom/README.md
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# DOM Utilities
|
|
2
|
+
|
|
3
|
+
A collection of browser-specific utilities for file interactions and reactive DOM observation. This module bridges standard DOM APIs with RxJS Observables and Signals to provide a modern, declarative developer experience for handling file downloads, uploads, and element observation.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
1. [✨ Features](#-features)
|
|
8
|
+
2. [Core Concepts](#core-concepts)
|
|
9
|
+
3. [🚀 Basic Usage](#-basic-usage)
|
|
10
|
+
- [File Download](#file-download)
|
|
11
|
+
- [File Selection](#file-selection)
|
|
12
|
+
- [Reactive Resize Observation](#reactive-resize-observation)
|
|
13
|
+
4. [🔧 Advanced Topics](#-advanced-topics)
|
|
14
|
+
- [Using Signals for Observation](#using-signals-for-observation)
|
|
15
|
+
- [Media Query Observation](#media-query-observation)
|
|
16
|
+
- [Touch Event Streams](#touch-event-streams)
|
|
17
|
+
- [Mutation & Performance Observation](#mutation--performance-observation)
|
|
18
|
+
5. [📚 API](#-api)
|
|
19
|
+
|
|
20
|
+
## ✨ Features
|
|
21
|
+
|
|
22
|
+
- **Programmatic File Download**: Trigger browser downloads from `Blob` or `File` objects without manual DOM manipulation.
|
|
23
|
+
- **File Selection Dialog**: Open the native file picker programmatically and await the result.
|
|
24
|
+
- **Reactive Observers**: RxJS Observable and Signal wrappers for standard browser observers:
|
|
25
|
+
- `IntersectionObserver` (Visibility)
|
|
26
|
+
- `ResizeObserver` (Dimensions)
|
|
27
|
+
- `MutationObserver` (DOM Tree Changes)
|
|
28
|
+
- `PerformanceObserver` (Metrics)
|
|
29
|
+
- `matchMedia` (Media Queries)
|
|
30
|
+
- **Touch Event Stream**: Normalized observable stream for touch interactions.
|
|
31
|
+
|
|
32
|
+
## Core Concepts
|
|
33
|
+
|
|
34
|
+
### File Handling
|
|
35
|
+
|
|
36
|
+
Standard HTML file inputs and download links often require imperative DOM manipulation (e.g., creating a hidden `<a>` tag, clicking it, and removing it). This module abstracts these patterns into simple function calls (`downloadFile`, `openFileSelectDialog`).
|
|
37
|
+
|
|
38
|
+
### Reactive Observation
|
|
39
|
+
|
|
40
|
+
Modern browser APIs like `ResizeObserver` and `IntersectionObserver` rely on callbacks. This module converts these APIs into **RxJS Observables** (suffixed with `$`) and **Signals** (no suffix). This allows you to compose DOM events with other asynchronous streams or use them directly in signal-based UI frameworks.
|
|
41
|
+
|
|
42
|
+
The implementation handles resource management automatically. For example, `observeIntersection$` uses a caching mechanism to share underlying `IntersectionObserver` instances across multiple subscriptions with identical configurations.
|
|
43
|
+
|
|
44
|
+
## 🚀 Basic Usage
|
|
45
|
+
|
|
46
|
+
### File Download
|
|
47
|
+
|
|
48
|
+
Trigger a download for a generated Blob.
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
import { downloadFile } from '@tstdl/base/dom';
|
|
52
|
+
|
|
53
|
+
const content = new Blob(['Hello, World!'], { type: 'text/plain' });
|
|
54
|
+
|
|
55
|
+
// Triggers a download of "hello.txt" in the browser
|
|
56
|
+
downloadFile(content, 'hello.txt');
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### File Selection
|
|
60
|
+
|
|
61
|
+
Open a file dialog and await the user's selection.
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import { openFileSelectDialog } from '@tstdl/base/dom';
|
|
65
|
+
|
|
66
|
+
async function handleUpload() {
|
|
67
|
+
// Opens the native file picker
|
|
68
|
+
const files = await openFileSelectDialog({
|
|
69
|
+
accept: ['.jpg', '.png', 'image/jpeg'],
|
|
70
|
+
multiple: true,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
if (files) {
|
|
74
|
+
console.log(`User selected ${files.length} files.`);
|
|
75
|
+
files.forEach((file) => console.log(file.name));
|
|
76
|
+
} else {
|
|
77
|
+
console.log('User cancelled the dialog.');
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Reactive Resize Observation
|
|
83
|
+
|
|
84
|
+
Observe changes to an element's size using RxJS.
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
import { observeResize$ } from '@tstdl/base/dom';
|
|
88
|
+
|
|
89
|
+
const element = document.querySelector('#my-component')!;
|
|
90
|
+
|
|
91
|
+
const subscription = observeResize$(element).subscribe((entry) => {
|
|
92
|
+
const { width, height } = entry.contentRect;
|
|
93
|
+
console.log(`New size: ${width}x${height}`);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Cleanup when done
|
|
97
|
+
// subscription.unsubscribe();
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## 🔧 Advanced Topics
|
|
101
|
+
|
|
102
|
+
### Using Signals for Observation
|
|
103
|
+
|
|
104
|
+
For state-driven applications, you can use the Signal variants of the observers. These provide the current state synchronously.
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
import { observeIntersection } from '@tstdl/base/dom';
|
|
108
|
+
import { effect } from '@tstdl/base/signals'; // Assuming a signal effect implementation
|
|
109
|
+
|
|
110
|
+
const element = document.querySelector('#lazy-image')!;
|
|
111
|
+
|
|
112
|
+
// Returns a Signal<IntersectionObserverEntry | undefined>
|
|
113
|
+
const intersectionSignal = observeIntersection(element, { threshold: 0.5 });
|
|
114
|
+
|
|
115
|
+
effect(() => {
|
|
116
|
+
const entry = intersectionSignal();
|
|
117
|
+
if (entry?.isIntersecting) {
|
|
118
|
+
console.log('Element is at least 50% visible!');
|
|
119
|
+
// Load image logic here...
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Media Query Observation
|
|
125
|
+
|
|
126
|
+
Reactively track media query matches (e.g., dark mode or viewport width).
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
import { observeMediaQuery$ } from '@tstdl/base/dom';
|
|
130
|
+
|
|
131
|
+
// RxJS Observable
|
|
132
|
+
observeMediaQuery$('(prefers-color-scheme: dark)').subscribe((isDark) => {
|
|
133
|
+
console.log('Dark mode is:', isDark ? 'Enabled' : 'Disabled');
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// Or as a Signal
|
|
137
|
+
import { observeMediaQuery } from '@tstdl/base/dom';
|
|
138
|
+
const isMobile = observeMediaQuery('(max-width: 768px)');
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Touch Event Streams
|
|
142
|
+
|
|
143
|
+
Normalize touch events (`start`, `move`, `end`, `cancel`) into a single observable stream. This is useful for building custom gestures.
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
import { observeTouch$ } from '@tstdl/base/dom';
|
|
147
|
+
|
|
148
|
+
const canvas = document.querySelector('canvas')!;
|
|
149
|
+
|
|
150
|
+
observeTouch$(canvas).subscribe((event) => {
|
|
151
|
+
if (event.start) {
|
|
152
|
+
console.log('Touch started at', event.start.touches[0].clientX);
|
|
153
|
+
}
|
|
154
|
+
if (event.move) {
|
|
155
|
+
// Handle drag
|
|
156
|
+
}
|
|
157
|
+
if (event.end) {
|
|
158
|
+
console.log('Touch ended');
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Mutation & Performance Observation
|
|
164
|
+
|
|
165
|
+
Observe DOM tree changes or performance metrics.
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
import { observeMutation$, observePerformance$ } from '@tstdl/base/dom';
|
|
169
|
+
|
|
170
|
+
// Mutation Observer
|
|
171
|
+
const list = document.querySelector('ul')!;
|
|
172
|
+
observeMutation$(list, { childList: true }).subscribe((records) => {
|
|
173
|
+
console.log('List changed:', records);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// Performance Observer
|
|
177
|
+
observePerformance$({ entryTypes: ['resource'] }).subscribe((list) => {
|
|
178
|
+
const entries = list.getEntries();
|
|
179
|
+
entries.forEach((entry) => console.log(`Resource loaded: ${entry.name}`));
|
|
180
|
+
});
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## 📚 API
|
|
184
|
+
|
|
185
|
+
### File Utilities
|
|
186
|
+
|
|
187
|
+
| Function | Description |
|
|
188
|
+
| :-------------------------------------------------------- | :----------------------------------------------------------------------------------- |
|
|
189
|
+
| `downloadFile(content: Blob \| File, fileName?: string)` | Triggers a browser download for the provided content. |
|
|
190
|
+
| `openFileSelectDialog(options?: FileSelectDialogOptions)` | Opens a file selection dialog and returns a Promise resolving to `File[]` or `null`. |
|
|
191
|
+
|
|
192
|
+
### Reactive Observers (RxJS & Signals)
|
|
193
|
+
|
|
194
|
+
| Function | Return Type | Description |
|
|
195
|
+
| :---------------------------------------- | :----------------------------------------------- | :-------------------------------------------------------- |
|
|
196
|
+
| `observeIntersection$(element, options?)` | `Observable<IntersectionObserverEntry>` | Observes element intersection changes. |
|
|
197
|
+
| `observeIntersection(element, options?)` | `Signal<IntersectionObserverEntry \| undefined>` | Signal version of intersection observer. |
|
|
198
|
+
| `observeResize$(element, options?)` | `Observable<ResizeObserverEntry>` | Observes element resize events. |
|
|
199
|
+
| `observeResize(element, options?)` | `Signal<ResizeObserverEntry \| undefined>` | Signal version of resize observer. |
|
|
200
|
+
| `observeMediaQuery$(query)` | `Observable<boolean>` | Observes media query match status. |
|
|
201
|
+
| `observeMediaQuery(query)` | `Signal<boolean>` | Signal version of media query observer. |
|
|
202
|
+
| `observeMutation$(nodes, options?)` | `Observable<MutationRecord[]>` | Observes DOM mutations. |
|
|
203
|
+
| `observePerformance$(inits?, options?)` | `Observable<PerformanceObserverEntryListLike>` | Observes performance entries. |
|
|
204
|
+
| `observeTouch$(element, options?)` | `Observable<TouchEvents>` | Observes touch events (`start`, `move`, `end`, `cancel`). |
|
|
205
|
+
|
|
206
|
+
### Types
|
|
207
|
+
|
|
208
|
+
| Type | Description |
|
|
209
|
+
| :-------------------------- | :----------------------------------------------------------------------- |
|
|
210
|
+
| `FileSelectDialogOptions` | Options for file selection (`accept`, `multiple`, `capture`). |
|
|
211
|
+
| `TouchEvents` | Object containing optional `start`, `move`, `end`, `cancel` TouchEvents. |
|
|
212
|
+
| `ObserveMutationOptions` | Extends `MutationObserverInit` with optional triggers. |
|
|
213
|
+
| `ObservePerformanceOptions` | Options for performance observation triggers. |
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
# Enumerable
|
|
2
|
+
|
|
3
|
+
A powerful, fluent, LINQ-inspired library for querying, transforming, and manipulating synchronous and asynchronous iterables in TypeScript. It unifies array processing, generators, and streams under a single, lazy-evaluation API.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [✨ Features](#-features)
|
|
8
|
+
- [Core Concepts](#core-concepts)
|
|
9
|
+
- [🚀 Basic Usage](#-basic-usage)
|
|
10
|
+
- [🔧 Advanced Topics](#-advanced-topics)
|
|
11
|
+
- [Asynchronous Streams](#asynchronous-streams)
|
|
12
|
+
- [Parallel Processing](#parallel-processing)
|
|
13
|
+
- [RxJS Observables](#rxjs-observables)
|
|
14
|
+
- [Multiplexing Streams](#multiplexing-streams)
|
|
15
|
+
- [Event Loop Interruption](#event-loop-interruption)
|
|
16
|
+
- [📚 API](#-api)
|
|
17
|
+
|
|
18
|
+
## ✨ Features
|
|
19
|
+
|
|
20
|
+
- **Fluent Interface**: Chain methods like `.filter()`, `.map()`, and `.reduce()` for readable and expressive data pipelines.
|
|
21
|
+
- **Lazy Evaluation**: Operations are deferred until results are materialized, optimizing performance and memory usage.
|
|
22
|
+
- **Unified API**: Consistent methods for both synchronous (`Enumerable`) and asynchronous (`AsyncEnumerable`) data sources.
|
|
23
|
+
- **Parallel Execution**: Built-in support for concurrent processing in asynchronous streams (`parallelMap`, `parallelFilter`, `parallelTap`) to speed up I/O-bound tasks.
|
|
24
|
+
- **Rich Toolset**: Includes advanced operations like `group`, `distinct`, `batch`, `pairwise`, `throttle`, and `retry`.
|
|
25
|
+
- **Interoperability**: Seamlessly convert between Arrays, Sets, Generators, Promises, and RxJS Observables.
|
|
26
|
+
- **Event Loop Friendly**: Built-in methods to yield control back to the event loop during heavy processing (`interruptEvery`, `interruptPerSecond`).
|
|
27
|
+
|
|
28
|
+
## Core Concepts
|
|
29
|
+
|
|
30
|
+
### `Enumerable<T>`
|
|
31
|
+
|
|
32
|
+
Wraps standard synchronous iterables (Arrays, Sets, Maps, Generators). It executes operations synchronously and lazily. No iteration happens until a terminal method (like `toArray`, `first`, or `reduce`) is called.
|
|
33
|
+
|
|
34
|
+
### `AsyncEnumerable<T>`
|
|
35
|
+
|
|
36
|
+
Wraps asynchronous iterables (`AsyncIterable`, `Promise<Iterable>`, `Observable`). It allows you to process streams of data over time. It supports **concurrency** control, allowing you to process multiple items in parallel while maintaining a simple fluent chain.
|
|
37
|
+
|
|
38
|
+
## 🚀 Basic Usage
|
|
39
|
+
|
|
40
|
+
### Synchronous Data Processing
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
import { Enumerable } from '@tstdl/base/enumerable';
|
|
44
|
+
|
|
45
|
+
const users = [
|
|
46
|
+
{ id: 1, name: 'Alice', age: 30, active: true },
|
|
47
|
+
{ id: 2, name: 'Bob', age: 25, active: false },
|
|
48
|
+
{ id: 3, name: 'Charlie', age: 35, active: true },
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
const names = Enumerable.from(users)
|
|
52
|
+
.filter((u) => u.active)
|
|
53
|
+
.sort((a, b) => a.age - b.age)
|
|
54
|
+
.map((u) => u.name)
|
|
55
|
+
.toArray();
|
|
56
|
+
|
|
57
|
+
console.log(names); // ['Alice', 'Charlie']
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Grouping Data
|
|
61
|
+
|
|
62
|
+
Easily group data into Maps or Arrays.
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
import { Enumerable } from '@tstdl/base/enumerable';
|
|
66
|
+
|
|
67
|
+
const numbers = [1, 2, 3, 4, 5, 6];
|
|
68
|
+
|
|
69
|
+
// Group by even/odd
|
|
70
|
+
const grouped = Enumerable.from(numbers).groupToMap((n) => (n % 2 === 0 ? 'even' : 'odd'));
|
|
71
|
+
|
|
72
|
+
console.log(grouped.get('even')); // [2, 4, 6]
|
|
73
|
+
console.log(grouped.get('odd')); // [1, 3, 5]
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## 🔧 Advanced Topics
|
|
77
|
+
|
|
78
|
+
### Asynchronous Streams
|
|
79
|
+
|
|
80
|
+
`AsyncEnumerable` works natively with async generators and APIs.
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
import { AsyncEnumerable } from '@tstdl/base/enumerable';
|
|
84
|
+
|
|
85
|
+
async function* fetchPages() {
|
|
86
|
+
yield [1, 2, 3];
|
|
87
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
88
|
+
yield [4, 5, 6];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const total = await AsyncEnumerable.from(fetchPages())
|
|
92
|
+
.mapMany((page) => page) // Flatten arrays
|
|
93
|
+
.filter((n) => n % 2 === 0)
|
|
94
|
+
.reduce((sum, n) => sum + n, 0);
|
|
95
|
+
|
|
96
|
+
console.log(total); // 12 (2 + 4 + 6)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Parallel Processing
|
|
100
|
+
|
|
101
|
+
Process async tasks concurrently to improve throughput. This is ideal for batch API calls or file processing.
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
import { AsyncEnumerable } from '@tstdl/base/enumerable';
|
|
105
|
+
|
|
106
|
+
const ids = [1, 2, 3, 4, 5];
|
|
107
|
+
|
|
108
|
+
const results = await AsyncEnumerable.from(ids)
|
|
109
|
+
.parallelMap(3, true, async (id) => {
|
|
110
|
+
// Simulate network request with concurrency of 3
|
|
111
|
+
// 'true' ensures output order matches input order
|
|
112
|
+
const data = await fetch(`https://api.example.com/items/${id}`);
|
|
113
|
+
return data.json();
|
|
114
|
+
})
|
|
115
|
+
.toArray();
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### RxJS Observables
|
|
119
|
+
|
|
120
|
+
Convert RxJS Observables directly into `AsyncEnumerable` to use LINQ-style operators on streams.
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
import { AsyncEnumerable } from '@tstdl/base/enumerable';
|
|
124
|
+
import { interval } from 'rxjs';
|
|
125
|
+
import { take } from 'rxjs/operators';
|
|
126
|
+
|
|
127
|
+
const observable = interval(100).pipe(take(5));
|
|
128
|
+
|
|
129
|
+
await AsyncEnumerable.fromObservable(observable)
|
|
130
|
+
.map((n) => n * 2)
|
|
131
|
+
.forEach((n) => console.log(n));
|
|
132
|
+
// Output: 0, 2, 4, 6, 8
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Multiplexing Streams
|
|
136
|
+
|
|
137
|
+
Split a single async source into multiple independent consumers. This is useful when you need to process the same stream in different ways simultaneously.
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
import { AsyncEnumerable } from '@tstdl/base/enumerable';
|
|
141
|
+
|
|
142
|
+
const source = AsyncEnumerable.fromRange(1, 5);
|
|
143
|
+
|
|
144
|
+
// Create 2 independent branches from the source
|
|
145
|
+
const [branchA, branchB] = source.multiplex(2);
|
|
146
|
+
|
|
147
|
+
const taskA = branchA.filter((n) => n % 2 === 0).toArray();
|
|
148
|
+
|
|
149
|
+
const taskB = branchB.map((n) => n * 10).toArray();
|
|
150
|
+
|
|
151
|
+
const [evens, multiplied] = await Promise.all([taskA, taskB]);
|
|
152
|
+
|
|
153
|
+
console.log(evens); // [2, 4]
|
|
154
|
+
console.log(multiplied); // [10, 20, 30, 40, 50]
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Event Loop Interruption
|
|
158
|
+
|
|
159
|
+
When processing large datasets asynchronously, you may want to prevent blocking the event loop for too long. `AsyncEnumerable` provides methods to automatically yield control back to the event loop.
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
import { AsyncEnumerable } from '@tstdl/base/enumerable';
|
|
163
|
+
|
|
164
|
+
const largeDataset = AsyncEnumerable.fromRange(1, 1_000_000);
|
|
165
|
+
|
|
166
|
+
await largeDataset
|
|
167
|
+
.interruptEvery(1000) // Yield control every 1000 items
|
|
168
|
+
.forEach((item) => {
|
|
169
|
+
// Heavy processing
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
await largeDataset
|
|
173
|
+
.interruptPerSecond(60) // Ensure the event loop is yielded at least 60 times per second
|
|
174
|
+
.forEach((item) => {
|
|
175
|
+
// Heavy processing
|
|
176
|
+
});
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## 📚 API
|
|
180
|
+
|
|
181
|
+
### Static Creation Methods
|
|
182
|
+
|
|
183
|
+
| Class | Method | Description |
|
|
184
|
+
| :---------------- | :---------------------- | :----------------------------------------------------------------------------- |
|
|
185
|
+
| `Enumerable` | `from(source)` | Creates an `Enumerable` from an `Iterable`. |
|
|
186
|
+
| `Enumerable` | `fromDeferred(factory)` | Creates an `Enumerable` that calls the factory function when iteration starts. |
|
|
187
|
+
| `Enumerable` | `fromRange(from, to)` | Creates an `Enumerable` of numbers in a range (inclusive). |
|
|
188
|
+
| `AsyncEnumerable` | `from(source)` | Creates an `AsyncEnumerable` from an `AnyIterable` (sync or async). |
|
|
189
|
+
| `AsyncEnumerable` | `fromDeferred(factory)` | Creates an `AsyncEnumerable` from a factory returning an iterable or promise. |
|
|
190
|
+
| `AsyncEnumerable` | `fromObservable(obs)` | Creates an `AsyncEnumerable` from an RxJS `Observable`. |
|
|
191
|
+
| `AsyncEnumerable` | `fromRange(from, to)` | Creates an `AsyncEnumerable` of numbers in a range. |
|
|
192
|
+
|
|
193
|
+
### Common Methods (Sync & Async)
|
|
194
|
+
|
|
195
|
+
These methods exist on both `Enumerable` and `AsyncEnumerable`. Async versions return `Promise` for terminal operations and `AsyncEnumerable` for transformations.
|
|
196
|
+
|
|
197
|
+
| Category | Method | Description |
|
|
198
|
+
| :----------------- | :------------------------ | :--------------------------------------------------------------------------------------- |
|
|
199
|
+
| **Filtering** | `filter(predicate)` | Filters elements based on a predicate. |
|
|
200
|
+
| | `filterNullOrUndefined()` | Removes `null` and `undefined` values. |
|
|
201
|
+
| | `distinct(selector?)` | Returns distinct elements (optionally by key). |
|
|
202
|
+
| | `take(count)` | Takes the first `N` elements. |
|
|
203
|
+
| | `skip(count)` | Skips the first `N` elements. |
|
|
204
|
+
| | `takeWhile(yieldLast, p)` | Takes elements while the predicate is true. |
|
|
205
|
+
| | `takeUntil(signal)` | Takes elements until a `CancellationSignal` is triggered. |
|
|
206
|
+
| | `while(predicate)` | Iterates while the predicate is true. |
|
|
207
|
+
| **Transformation** | `map(mapper)` | Projects each element into a new form. |
|
|
208
|
+
| | `mapMany(mapper)` | Projects each element to an iterable and flattens the result. |
|
|
209
|
+
| | `batch(size)` | Groups elements into arrays of a specific size. |
|
|
210
|
+
| | `pairwise()` | Emits the previous and current element as a tuple `[prev, curr]`. |
|
|
211
|
+
| | `materialize()` | Converts the sequence into a sequence of `Notification` objects (next, error, complete). |
|
|
212
|
+
| | `metadata()` | Wraps elements with metadata (index, isFirst, isLast). |
|
|
213
|
+
| **Set Operations** | `concat(...iterables)` | Concatenates multiple iterables. |
|
|
214
|
+
| | `difference(other, sel?)` | Returns elements present in source but not in other. |
|
|
215
|
+
| | `differenceMany(its, s?)` | Returns elements present in source but not in any of the others. |
|
|
216
|
+
| | `defaultIfEmpty(default)` | Returns a default value if the sequence is empty. |
|
|
217
|
+
| **Aggregation** | `reduce(reducer, init?)` | Aggregates the sequence into a single value. |
|
|
218
|
+
| | `toArray()` | Materializes the sequence into an array. |
|
|
219
|
+
| | `toSet()` | Materializes the sequence into a Set. |
|
|
220
|
+
| | `group(selector)` | Groups elements into `[Key, T[]]` pairs. |
|
|
221
|
+
| | `groupSingle(selector)` | Groups elements into `[Key, T]` pairs (last wins). |
|
|
222
|
+
| | `groupToMap(selector)` | Groups elements into a `Map<Key, T[]>`. |
|
|
223
|
+
| | `groupToSingleMap(sel)` | Groups elements into a `Map<Key, T>` (last wins). |
|
|
224
|
+
| **Inspection** | `all(predicate)` | Checks if all elements satisfy the predicate. |
|
|
225
|
+
| | `any(predicate)` | Checks if any element satisfies the predicate. |
|
|
226
|
+
| | `includes(value)` | Checks if the sequence contains a specific value. |
|
|
227
|
+
| | `first(predicate?)` | Returns the first element (throws if empty). |
|
|
228
|
+
| | `firstOrDefault(def, p?)` | Returns the first element or a default value. |
|
|
229
|
+
| | `last(predicate?)` | Returns the last element. |
|
|
230
|
+
| | `lastOrDefault(def, p?)` | Returns the last element or a default value. |
|
|
231
|
+
| | `single(predicate?)` | Returns the only element (throws if not exactly one). |
|
|
232
|
+
| | `singleOrDefault(def, p)` | Returns the single element or a default value. |
|
|
233
|
+
| **Sorting** | `sort(comparator?)` | Sorts the sequence lazily. |
|
|
234
|
+
| | `sortToArray(comparator?)`| Sorts the sequence and returns an array immediately. |
|
|
235
|
+
| **Side Effects** | `tap(action)` | Executes an action for each element without modifying the stream. |
|
|
236
|
+
| | `forEach(action)` | Iterates over the sequence (terminal operation). |
|
|
237
|
+
| | `drain()` | Consumes the entire sequence and discards results. |
|
|
238
|
+
| **Utility** | `assert(predicate)` | Verifies that each element satisfies the predicate, throws otherwise. |
|
|
239
|
+
| | `cast<TNew>()` | Casts the sequence to a new type (unsafe). |
|
|
240
|
+
| | `forceCast<TNew>()` | Force casts the sequence to a new type (unsafe). |
|
|
241
|
+
| | `toAsync()` | Converts the sequence to an `AsyncEnumerable`. |
|
|
242
|
+
| | `toIterator()` | Returns the underlying iterator. |
|
|
243
|
+
|
|
244
|
+
### Async-Only Methods (`AsyncEnumerable`)
|
|
245
|
+
|
|
246
|
+
| Method | Description |
|
|
247
|
+
| :-------------------------------------------------- | :--------------------------------------------------------------------------------------- |
|
|
248
|
+
| `parallelMap(concurrency, keepOrder, mapper)` | Maps elements concurrently. |
|
|
249
|
+
| `parallelFilter(concurrency, keepOrder, predicate)` | Filters elements concurrently. |
|
|
250
|
+
| `parallelTap(concurrency, keepOrder, action)` | Taps elements concurrently. |
|
|
251
|
+
| `parallelForEach(concurrency, action)` | Iterates elements concurrently. |
|
|
252
|
+
| `parallelGroup(concurrency, selector)` | Groups elements concurrently (returns `Promise<Map>`). |
|
|
253
|
+
| `interruptEvery(value)` | Yields control to the event loop every `N` elements. |
|
|
254
|
+
| `interruptPerSecond(value)` | Yields control to the event loop `N` times per second. |
|
|
255
|
+
| `throttle(delayOrFunction)` | Limits the rate of emission. |
|
|
256
|
+
| `retry(throwOnFalse, predicate)` | Retries the source sequence on error based on a predicate. |
|
|
257
|
+
| `multiplex(count, buffer?)` | Splits the stream into multiple independent streams. |
|
|
258
|
+
| `buffer(size)` | Buffers elements and emits them as soon as they are available (flow control). |
|
|
259
|
+
| `toSync()` | Resolves the entire async sequence into a synchronous `Enumerable` (returns `Promise`). |
|