mn-angular-lib 0.0.44 → 0.0.45

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.
@@ -1,13 +1,14 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { InjectionToken, Injectable, Optional, Inject, inject, Input, ChangeDetectionStrategy, Component, HostBinding, Self, APP_INITIALIZER, ElementRef, HostListener, forwardRef, Directive, EventEmitter, TemplateRef, Output, ViewChildren, ViewContainerRef, ViewChild, ApplicationRef, EnvironmentInjector, createComponent, SkipSelf, Attribute, Pipe } from '@angular/core';
3
3
  export { TemplateRef, Type } from '@angular/core';
4
- import { BehaviorSubject, firstValueFrom, Subject, debounceTime } from 'rxjs';
4
+ import { BehaviorSubject, firstValueFrom, Subject, debounceTime, map, catchError, of } from 'rxjs';
5
5
  import * as i1 from '@angular/common';
6
6
  import { CommonModule, NgClass, NgOptimizedImage, NgForOf, NgIf, NgTemplateOutlet } from '@angular/common';
7
7
  import { tv } from 'tailwind-variants';
8
8
  import * as i1$2 from '@angular/forms';
9
9
  import { Validators, NG_VALUE_ACCESSOR, ReactiveFormsModule } from '@angular/forms';
10
10
  import * as i1$1 from '@angular/common/http';
11
+ import { HttpClient, HttpErrorResponse, HttpStatusCode, HttpParams } from '@angular/common/http';
11
12
  import JSON5 from 'json5';
12
13
 
13
14
  // projects/mn-angular-lib/src/lib/mn-mn-alert/mn-mn-alert.tokens.ts
@@ -4292,6 +4293,364 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
4292
4293
  args: ['mn-instance']
4293
4294
  }] } });
4294
4295
 
4296
+ /**
4297
+ * Injection token for the base URL used by all CRUD service requests.
4298
+ *
4299
+ * Provide this token at the application or module level to configure
4300
+ * the root API URL that `CrudService` prepends to every endpoint.
4301
+ */
4302
+ const API_BASE_URL = new InjectionToken('API_BASE_URL');
4303
+
4304
+ /**
4305
+ * Abstract base class for CRUD services.
4306
+ * Provides standard HTTP operations with typed `Result<T>` responses.
4307
+ *
4308
+ * @template TEntity The entity type returned by single-item operations.
4309
+ * @template TListResponse The response type for list operations (defaults to `TEntity[]`).
4310
+ * @template TCreatePayload The payload type for create operations (defaults to `Partial<TEntity>`).
4311
+ * @template TUpdatePayload The payload type for update operations (defaults to `Partial<TEntity>`).
4312
+ * @template TId The type of the entity identifier (defaults to `number`).
4313
+ * @template TGetByIdResponse The response type for getById (defaults to `TEntity`).
4314
+ * @template TCreateResponse The response type for create (defaults to `TEntity`).
4315
+ * @template TUpdateResponse The response type for update and patch (defaults to `TEntity`).
4316
+ * @template TDeleteResponse The response type for delete (defaults to `void`).
4317
+ */
4318
+ class CrudService {
4319
+ http = inject(HttpClient);
4320
+ baseUrl = inject(API_BASE_URL);
4321
+ endpoint;
4322
+ constructor(config) {
4323
+ this.endpoint = `${this.baseUrl}${config.endpoint}`;
4324
+ }
4325
+ /**
4326
+ * Retrieves all entities from the configured endpoint.
4327
+ *
4328
+ * Sends a GET request to the base endpoint. Query values are
4329
+ * converted to `HttpParams` before the request is sent.
4330
+ *
4331
+ * @param query Optional query parameters appended to the request URL.
4332
+ * @returns An observable emitting a `Result` with the list response or a structured failure.
4333
+ */
4334
+ getAll(query) {
4335
+ return this.http
4336
+ .get(this.endpoint, {
4337
+ params: this.toHttpParams(query)
4338
+ })
4339
+ .pipe(map((data) => this.success(data)), catchError((error) => of(this.failure(this.mapHttpError(error)))));
4340
+ }
4341
+ /**
4342
+ * Retrieves a single entity by its identifier.
4343
+ *
4344
+ * Sends a GET request to `{endpoint}/{id}`.
4345
+ *
4346
+ * @param id The unique identifier of the entity to retrieve.
4347
+ * @returns An observable emitting a `Result` with the entity or a structured failure.
4348
+ */
4349
+ getById(id) {
4350
+ return this.http
4351
+ .get(this.itemUrl(id))
4352
+ .pipe(map((data) => this.success(data)), catchError((error) => of(this.failure(this.mapHttpError(error)))));
4353
+ }
4354
+ /**
4355
+ * Creates a new entity at the configured endpoint.
4356
+ *
4357
+ * Sends a POST request with the provided payload as the request body.
4358
+ *
4359
+ * @param payload The data used to create the entity.
4360
+ * @returns An observable emitting a `Result` with the created entity or a structured failure.
4361
+ */
4362
+ create(payload) {
4363
+ return this.http
4364
+ .post(this.endpoint, payload)
4365
+ .pipe(map((data) => this.success(data)), catchError((error) => of(this.failure(this.mapHttpError(error)))));
4366
+ }
4367
+ /**
4368
+ * Fully replaces an existing entity.
4369
+ *
4370
+ * Sends a PUT request to `{endpoint}/{id}` with the provided payload,
4371
+ * replacing the entire entity.
4372
+ *
4373
+ * @param id The unique identifier of the entity to update.
4374
+ * @param payload The complete data to replace the existing entity with.
4375
+ * @returns An observable emitting a `Result` with the updated entity or a structured failure.
4376
+ */
4377
+ update(id, payload) {
4378
+ return this.http
4379
+ .put(this.itemUrl(id), payload)
4380
+ .pipe(map((data) => this.success(data)), catchError((error) => of(this.failure(this.mapHttpError(error)))));
4381
+ }
4382
+ /**
4383
+ * Partially updates an existing entity.
4384
+ *
4385
+ * Sends a PATCH request to `{endpoint}/{id}` with the provided payload,
4386
+ * merging changes into the existing entity.
4387
+ *
4388
+ * @param id The unique identifier of the entity to patch.
4389
+ * @param payload A partial set of fields to update on the existing entity.
4390
+ * @returns An observable emitting a `Result` with the updated entity or a structured failure.
4391
+ */
4392
+ patch(id, payload) {
4393
+ return this.http
4394
+ .patch(this.itemUrl(id), payload)
4395
+ .pipe(map((data) => this.success(data)), catchError((error) => of(this.failure(this.mapHttpError(error)))));
4396
+ }
4397
+ /**
4398
+ * Deletes an entity by its identifier.
4399
+ *
4400
+ * Sends a DELETE request to `{endpoint}/{id}`.
4401
+ *
4402
+ * @param id The unique identifier of the entity to delete.
4403
+ * @returns An observable emitting a `Result` with the delete response or a structured failure.
4404
+ */
4405
+ delete(id) {
4406
+ return this.http
4407
+ .delete(this.itemUrl(id))
4408
+ .pipe(map((data) => this.success(data)), catchError((error) => of(this.failure(this.mapHttpError(error)))));
4409
+ }
4410
+ /**
4411
+ * Retrieves all entities with the full `HttpResponse` wrapper.
4412
+ *
4413
+ * Behaves like {@link getAll} but observes the complete HTTP response,
4414
+ * giving access to headers, status code, and URL alongside the body.
4415
+ *
4416
+ * @param query Optional query parameters appended to the request URL.
4417
+ * @returns An observable emitting a `Result` with the full HTTP response or a structured failure.
4418
+ */
4419
+ getAllResponse(query) {
4420
+ return this.http
4421
+ .get(this.endpoint, {
4422
+ params: this.toHttpParams(query),
4423
+ observe: 'response',
4424
+ })
4425
+ .pipe(map((response) => this.success(response, {
4426
+ statusCode: response.status,
4427
+ headers: response.headers,
4428
+ url: response.url ?? undefined,
4429
+ })), catchError((error) => of(this.failure(this.mapHttpError(error)))));
4430
+ }
4431
+ /**
4432
+ * Builds the URL for a single entity by appending the identifier to the endpoint.
4433
+ *
4434
+ * @param id The unique identifier to append.
4435
+ * @returns The full URL targeting the specific entity.
4436
+ */
4437
+ itemUrl(id) {
4438
+ return `${this.endpoint}/${id}`;
4439
+ }
4440
+ /**
4441
+ * Wraps a value in a `SuccessResult`.
4442
+ *
4443
+ * @template T The type of the response data.
4444
+ * @param data The response data to wrap.
4445
+ * @param meta Optional metadata (status code, headers, URL) to attach.
4446
+ * @returns A `SuccessResult` containing the provided data.
4447
+ */
4448
+ success(data, meta) {
4449
+ return { ok: true, data, meta };
4450
+ }
4451
+ /**
4452
+ * Wraps an error in a `FailureResult`.
4453
+ *
4454
+ * When no explicit metadata is provided, metadata is derived from
4455
+ * the `ApiError` itself (status code, headers, URL).
4456
+ *
4457
+ * @param error The structured API error.
4458
+ * @param meta Optional metadata to override the error-derived values.
4459
+ * @returns A `FailureResult` containing the error and metadata.
4460
+ */
4461
+ failure(error, meta) {
4462
+ return {
4463
+ ok: false,
4464
+ error,
4465
+ meta: meta ?? {
4466
+ statusCode: error.status ?? undefined,
4467
+ headers: error.headers,
4468
+ url: error.url ?? undefined,
4469
+ },
4470
+ };
4471
+ }
4472
+ /**
4473
+ * Maps an unknown error into a structured `ApiError`.
4474
+ *
4475
+ * Handles both `HttpErrorResponse` instances and unexpected error types.
4476
+ * Extracts backend messages, validation errors, and retry information
4477
+ * so callers receive a consistent error shape.
4478
+ *
4479
+ * @param error The raw error caught from the HTTP pipeline.
4480
+ * @returns A fully populated `ApiError` object.
4481
+ */
4482
+ mapHttpError(error) {
4483
+ const timestamp = new Date().toISOString();
4484
+ if (!(error instanceof HttpErrorResponse)) {
4485
+ return {
4486
+ status: null,
4487
+ message: 'Unknown error',
4488
+ original: error instanceof Error ? error : new Error(String(error)),
4489
+ retryable: false,
4490
+ timestamp,
4491
+ };
4492
+ }
4493
+ const status = this.normalizeStatus(error.status);
4494
+ const details = error.error;
4495
+ const backendMessage = this.extractBackendMessage(details);
4496
+ const validationErrors = this.extractValidationErrors(details);
4497
+ return {
4498
+ status,
4499
+ message: backendMessage ?? this.defaultMessage(status),
4500
+ details,
4501
+ backendMessage,
4502
+ validationErrors,
4503
+ url: error.url,
4504
+ headers: error.headers,
4505
+ original: error,
4506
+ retryable: this.isRetryable(status),
4507
+ timestamp,
4508
+ };
4509
+ }
4510
+ /**
4511
+ * Extracts a human-readable message from the error response body.
4512
+ *
4513
+ * Checks common keys (`message`, `title`, `detail`, `error`) on the
4514
+ * body object and returns the first non-empty string found.
4515
+ *
4516
+ * @param body The parsed error response body.
4517
+ * @returns The extracted message, or `undefined` if none was found.
4518
+ */
4519
+ extractBackendMessage(body) {
4520
+ if (typeof body === 'string' && body.trim()) {
4521
+ return body;
4522
+ }
4523
+ if (!body || typeof body !== 'object') {
4524
+ return undefined;
4525
+ }
4526
+ const obj = body;
4527
+ for (const key of ['message', 'title', 'detail', 'error']) {
4528
+ const value = obj[key];
4529
+ if (typeof value === 'string' && value.trim()) {
4530
+ return value;
4531
+ }
4532
+ }
4533
+ return undefined;
4534
+ }
4535
+ /**
4536
+ * Extracts field-level validation errors from the error response body.
4537
+ *
4538
+ * Expects an `errors` property on the body containing a record of
4539
+ * field names to error messages (string or string array).
4540
+ *
4541
+ * @param body The parsed error response body.
4542
+ * @returns A record mapping field names to their error messages, or `undefined` if none were found.
4543
+ */
4544
+ extractValidationErrors(body) {
4545
+ if (!body || typeof body !== 'object') {
4546
+ return undefined;
4547
+ }
4548
+ const obj = body;
4549
+ const errors = obj['errors'];
4550
+ if (!errors || typeof errors !== 'object') {
4551
+ return undefined;
4552
+ }
4553
+ const result = {};
4554
+ for (const [key, value] of Object.entries(errors)) {
4555
+ if (Array.isArray(value)) {
4556
+ result[key] = value.map(String);
4557
+ }
4558
+ else if (typeof value === 'string') {
4559
+ result[key] = [value];
4560
+ }
4561
+ }
4562
+ return Object.keys(result).length ? result : undefined;
4563
+ }
4564
+ /**
4565
+ * Returns a default user-facing message for the given HTTP status code.
4566
+ *
4567
+ * Provides human-readable messages for common HTTP status codes.
4568
+ * Override this method to customise messages.
4569
+ *
4570
+ * @param status The HTTP status code, or `null` when unknown.
4571
+ * @returns A descriptive error message.
4572
+ */
4573
+ defaultMessage(status) {
4574
+ switch (status) {
4575
+ case HttpStatusCode.BadRequest:
4576
+ return 'Bad request';
4577
+ case HttpStatusCode.Unauthorized:
4578
+ return 'Unauthorized';
4579
+ case HttpStatusCode.Forbidden:
4580
+ return 'Forbidden';
4581
+ case HttpStatusCode.NotFound:
4582
+ return 'Not found';
4583
+ case HttpStatusCode.Conflict:
4584
+ return 'Conflict';
4585
+ case HttpStatusCode.UnprocessableEntity:
4586
+ return 'Unprocessable entity';
4587
+ case HttpStatusCode.InternalServerError:
4588
+ return 'Internal server error';
4589
+ case null:
4590
+ return 'Unknown error';
4591
+ default:
4592
+ return `Request failed with status ${status}`;
4593
+ }
4594
+ }
4595
+ /**
4596
+ * Determines whether a request with the given status can be retried.
4597
+ *
4598
+ * Timeouts, rate-limiting responses, and server errors
4599
+ * (5xx) are considered retryable by default.
4600
+ *
4601
+ * @param status The HTTP status code, or `null` when unknown.
4602
+ * @returns `true` if the request is safe to retry.
4603
+ */
4604
+ isRetryable(status) {
4605
+ if (status === HttpStatusCode.RequestTimeout)
4606
+ return true;
4607
+ if (status === HttpStatusCode.TooManyRequests)
4608
+ return true;
4609
+ if (typeof status === 'number' && status >= 500)
4610
+ return true;
4611
+ return false;
4612
+ }
4613
+ /**
4614
+ * Normalises a raw HTTP status into an `HttpStatusCode | null` value.
4615
+ *
4616
+ * Converts `undefined`, `NaN`, and `0` (network error) to `null`
4617
+ * so downstream code only needs to handle `HttpStatusCode | null`.
4618
+ *
4619
+ * @param status The raw status value from the HTTP response.
4620
+ * @returns The normalised status code, or `null` when indeterminate.
4621
+ */
4622
+ normalizeStatus(status) {
4623
+ if (status === null || status === undefined || status === 0 || Number.isNaN(status))
4624
+ return null;
4625
+ return status;
4626
+ }
4627
+ /**
4628
+ * Converts query parameters into Angular `HttpParams`.
4629
+ *
4630
+ * `null` and `undefined` values are silently skipped.
4631
+ * Array values are appended as multiple entries for the same key.
4632
+ *
4633
+ * @param query The query parameter record to convert.
4634
+ * @returns An `HttpParams` instance, or `undefined` when no parameters are provided.
4635
+ */
4636
+ toHttpParams(query) {
4637
+ if (!query)
4638
+ return undefined;
4639
+ let params = new HttpParams();
4640
+ for (const [key, rawValue] of Object.entries(query)) {
4641
+ if (rawValue === null || rawValue === undefined)
4642
+ continue;
4643
+ const values = Array.isArray(rawValue) ? rawValue : [rawValue];
4644
+ for (const value of values) {
4645
+ if (value === null || value === undefined)
4646
+ continue;
4647
+ params = params.append(key, String(value));
4648
+ }
4649
+ }
4650
+ return params;
4651
+ }
4652
+ }
4653
+
4295
4654
  /**
4296
4655
  * Provides an APP_INITIALIZER that configures the MnLanguageService and
4297
4656
  * preloads the requested locales during application bootstrap.
@@ -4356,5 +4715,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
4356
4715
  * Generated bundle index. Do not edit.
4357
4716
  */
4358
4717
 
4359
- export { ActionStyle, BackdropMode, BaseModalBuilder, CloseMode, ColumnSortType, ConfirmationModalBuilder, ConfirmationTone, CustomModalBuilder, DEFAULT_MN_ALERT_CONFIG, FieldAppearance, FieldKind, FormLayoutMode, FormModalBuilder, KeyboardMode, MN_ALERT_CONFIG, MN_CHECKBOX_CONFIG, MN_DATETIME_CONFIG, MN_INPUT_FIELD_CONFIG, MN_INSTANCE_ID, MN_LIB_DUAL_HORIZONTAL_IMAGE, MN_MULTI_SELECT_CONFIG, MN_SECTION_PATH, MN_TEST_COMPONENT_CONFIG, MN_TEXTAREA_CONFIG, MnAlertOutletComponent, MnAlertService, MnAlertStore, MnButton, MnCheckbox, MnConfigService, MnConfirmationBodyComponent, MnCustomBodyHostComponent, MnDatetime, MnDualHorizontalImage, MnFormBodyComponent, MnInformationCard, MnInputField, MnInstanceDirective, MnLanguageService, MnModalRef, MnModalService, MnModalShellComponent, MnMultiSelect, MnSectionDirective, MnTable, MnTestComponent, MnTextarea, MnTranslatePipe, MnWizardBodyComponent, ModalBuilder, ModalCloseReason, ModalIntent, ModalKind, ModalSize, NavigationDirection, OptionState, SelectionMode, StepBuilder, StepState, SubmitMode, Test, ValidationCode, ValidationStatus, WizardFlowMode, WizardModalBuilder, dateTimeAdapter, defaultTextAdapter, isTranslatable, mnAlertVariants, mnButtonVariants, mnCheckboxVariants, mnDatetimeVariants, mnInformationCardVariants, mnInputFieldVariants, mnMultiSelectVariants, mnTextareaVariants, numberAdapter, pickAdapter, provideMnAlerts, provideMnComponentConfig, provideMnConfig, provideMnLanguage };
4718
+ export { API_BASE_URL, ActionStyle, BackdropMode, BaseModalBuilder, CloseMode, ColumnSortType, ConfirmationModalBuilder, ConfirmationTone, CrudService, CustomModalBuilder, DEFAULT_MN_ALERT_CONFIG, FieldAppearance, FieldKind, FormLayoutMode, FormModalBuilder, KeyboardMode, MN_ALERT_CONFIG, MN_CHECKBOX_CONFIG, MN_DATETIME_CONFIG, MN_INPUT_FIELD_CONFIG, MN_INSTANCE_ID, MN_LIB_DUAL_HORIZONTAL_IMAGE, MN_MULTI_SELECT_CONFIG, MN_SECTION_PATH, MN_TEST_COMPONENT_CONFIG, MN_TEXTAREA_CONFIG, MnAlertOutletComponent, MnAlertService, MnAlertStore, MnButton, MnCheckbox, MnConfigService, MnConfirmationBodyComponent, MnCustomBodyHostComponent, MnDatetime, MnDualHorizontalImage, MnFormBodyComponent, MnInformationCard, MnInputField, MnInstanceDirective, MnLanguageService, MnModalRef, MnModalService, MnModalShellComponent, MnMultiSelect, MnSectionDirective, MnTable, MnTestComponent, MnTextarea, MnTranslatePipe, MnWizardBodyComponent, ModalBuilder, ModalCloseReason, ModalIntent, ModalKind, ModalSize, NavigationDirection, OptionState, SelectionMode, StepBuilder, StepState, SubmitMode, Test, ValidationCode, ValidationStatus, WizardFlowMode, WizardModalBuilder, dateTimeAdapter, defaultTextAdapter, isTranslatable, mnAlertVariants, mnButtonVariants, mnCheckboxVariants, mnDatetimeVariants, mnInformationCardVariants, mnInputFieldVariants, mnMultiSelectVariants, mnTextareaVariants, numberAdapter, pickAdapter, provideMnAlerts, provideMnComponentConfig, provideMnConfig, provideMnLanguage };
4360
4719
  //# sourceMappingURL=mn-angular-lib.mjs.map