@angular-wave/angular.ts 0.12.0 → 0.13.0

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,4 +1,4 @@
1
- /* Version: 0.12.0 - November 29, 2025 19:42:34 */
1
+ /* Version: 0.13.0 - December 2, 2025 01:18:44 */
2
2
  (function (global, factory) {
3
3
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
4
4
  typeof define === 'function' && define.amd ? define(['exports'], factory) :
@@ -28,6 +28,9 @@
28
28
  };
29
29
 
30
30
  const isProxySymbol = Symbol("isProxy");
31
+ const BADARG = "badarg";
32
+ const BADARGKEY = "badarg: key";
33
+ const BADARGVALUE = "badarg: value";
31
34
 
32
35
  /**
33
36
  *
@@ -1760,7 +1763,7 @@
1760
1763
  * @returns {void}
1761
1764
  */
1762
1765
  function dealoc(element, onlyDescendants) {
1763
- if (!element) return;
1766
+ if (!element || element instanceof Comment) return;
1764
1767
  if (Array.isArray(element)) {
1765
1768
  element.forEach((x) => dealoc(x, onlyDescendants));
1766
1769
  } else {
@@ -1899,7 +1902,7 @@
1899
1902
  * Set scope for a given element.
1900
1903
  *
1901
1904
  * @param {Element|Node|ChildNode} element - The DOM element to set data on.
1902
- * @param {import("../core/scope/scope.js").Scope} scope - The Scope attached to this element
1905
+ * @param {ng.Scope} scope - The Scope attached to this element
1903
1906
  */
1904
1907
  function setScope(element, scope) {
1905
1908
  return setCacheData(element, SCOPE_KEY, scope);
@@ -1909,7 +1912,7 @@
1909
1912
  * Set isolate scope for a given element.
1910
1913
  *
1911
1914
  * @param {Element} element - The DOM element to set data on.
1912
- * @param {import("../core/scope/scope.js").Scope} scope - The Scope attached to this element
1915
+ * @param {ng.Scope} scope - The Scope attached to this element
1913
1916
  */
1914
1917
  function setIsolateScope(element, scope) {
1915
1918
  return setCacheData(element, ISOLATE_SCOPE_KEY, scope);
@@ -1920,7 +1923,7 @@
1920
1923
  *
1921
1924
  * @param {Element} element - The DOM element to get data from.
1922
1925
  * @param {string} [name] - Controller name.
1923
- * @returns {import("../core/scope/scope.js").Scope|undefined} - The retrieved data
1926
+ * @returns {ng.Scope|undefined} - The retrieved data
1924
1927
  */
1925
1928
  function getController(element, name) {
1926
1929
  return getInheritedData(element, `$${name || "ngController"}Controller`);
@@ -2209,6 +2212,7 @@
2209
2212
  $animateCss: "$animateCss",
2210
2213
  $aria: "$aria",
2211
2214
  $compile: "$compile",
2215
+ $cookie: "$cookie",
2212
2216
  $controller: "$controller",
2213
2217
  $document: "$document",
2214
2218
  $eventBus: "$eventBus",
@@ -2221,6 +2225,7 @@
2221
2225
  $log: "$log",
2222
2226
  $viewScroll: "$viewScroll",
2223
2227
  $parse: "$parse",
2228
+ $rest: "$rest",
2224
2229
  $rootScope: "$rootScope",
2225
2230
  $rootElement: "$rootElement",
2226
2231
  $router: "$router",
@@ -3017,6 +3022,8 @@
3017
3022
  this.services = [];
3018
3023
 
3019
3024
  this.wasmModules = [];
3025
+
3026
+ this.restDefinitions = [];
3020
3027
  }
3021
3028
 
3022
3029
  /**
@@ -3266,31 +3273,50 @@
3266
3273
  /**
3267
3274
  * @param {string} name
3268
3275
  * @param {Function} ctor
3276
+ * @param {ng.StorageType} type
3277
+ * @param {ng.StorageBackend} [backendOrConfig]
3269
3278
  * @returns {NgModule}
3270
3279
  */
3271
- local(name, ctor) {
3280
+ store(name, ctor, type, backendOrConfig) {
3272
3281
  if (ctor && isFunction(ctor)) {
3273
3282
  ctor["$$moduleName"] = name;
3274
3283
  }
3275
- this.invokeQueue.push([$injectTokens.$provide, "local", [name, ctor]]);
3284
+ this.invokeQueue.push([
3285
+ $injectTokens.$provide,
3286
+ "store",
3287
+ [name, ctor, type, backendOrConfig],
3288
+ ]);
3276
3289
  return this;
3277
3290
  }
3278
3291
 
3279
3292
  /**
3280
- * @param {string} name
3281
- * @param {Function} ctor
3282
- * @param {ng.StorageBackend} backendOrConfig
3293
+ * @template T, ID
3294
+ * Register a REST resource during module configuration.
3295
+ * @param {string} name - Service name
3296
+ * @param {string} url - Base URL or URI template
3297
+ * @param {ng.EntityClass<T>} entityClass - Optional constructor for mapping JSON
3298
+ * @param {Object=} options - Optional RestService options (interceptors, etc)
3283
3299
  * @returns {NgModule}
3284
3300
  */
3285
- store(name, ctor, backendOrConfig) {
3286
- if (ctor && isFunction(ctor)) {
3287
- ctor["$$moduleName"] = name;
3288
- }
3301
+ rest(name, url, entityClass, options = {}) {
3302
+ const def = { name, url, entityClass, options };
3303
+ this.restDefinitions.push(def);
3304
+
3305
+ // push provider/factory to invokeQueue
3289
3306
  this.invokeQueue.push([
3290
3307
  $injectTokens.$provide,
3291
- "store",
3292
- [name, ctor, backendOrConfig],
3308
+ "factory",
3309
+ [
3310
+ name,
3311
+ [
3312
+ $injectTokens.$rest,
3313
+ /** @param {(baseUrl:string, entityClass?:Function, options?:object) => ng.RestService<T, ID>} $rest */ (
3314
+ $rest,
3315
+ ) => $rest(url, entityClass, options),
3316
+ ],
3317
+ ],
3293
3318
  ]);
3319
+
3294
3320
  return this;
3295
3321
  }
3296
3322
  }
@@ -3613,8 +3639,8 @@
3613
3639
  obj[prop] = value;
3614
3640
  try {
3615
3641
  storage.setItem(key, serialize(obj));
3616
- } catch {
3617
- console.warn(`Failed to persist data for key "${key}"`);
3642
+ } catch (e) {
3643
+ console.warn(`Failed to persist data for key "${e}"`);
3618
3644
  }
3619
3645
  return true;
3620
3646
  },
@@ -3652,9 +3678,7 @@
3652
3678
  service: supportObject(service),
3653
3679
  value: supportObject(value),
3654
3680
  constant: supportObject(constant),
3655
- session: supportObject(session),
3656
- local: supportObject(local),
3657
- store: supportObject(store),
3681
+ store,
3658
3682
  decorator,
3659
3683
  },
3660
3684
  };
@@ -3789,66 +3813,80 @@
3789
3813
  }
3790
3814
 
3791
3815
  /**
3792
- * Registers a session-persistent service
3793
- */
3794
- function session(name, ctor) {
3795
- return provider(name, {
3796
- $get: ($injector) => {
3797
- const instance = $injector.instantiate(ctor);
3798
- return createPersistentProxy(instance, name, sessionStorage);
3799
- },
3800
- });
3801
- }
3802
-
3803
- /**
3804
- * Registers a localStorage-persistent service
3805
- */
3806
- function local(name, ctor) {
3807
- return provider(name, {
3808
- $get: ($injector) => {
3809
- const instance = $injector.instantiate(ctor);
3810
- return createPersistentProxy(instance, name, localStorage);
3811
- },
3812
- });
3813
- }
3814
-
3815
- /**
3816
- * Registers a service persisted in a custom storage
3816
+ * Registers a service persisted in a storage
3817
3817
  *
3818
3818
  * @param {string} name - Service name
3819
3819
  * @param {Function} ctor - Constructor for the service
3820
+ * @param {ng.StorageType} type - Type of storage to be instantiated
3820
3821
  * @param {Storage|Object} backendOrConfig - Either a Storage-like object (getItem/setItem) or a config object
3821
3822
  * with { backend, serialize, deserialize }
3822
3823
  */
3823
- function store(name, ctor, backendOrConfig) {
3824
+ function store(name, ctor, type, backendOrConfig = {}) {
3824
3825
  return provider(name, {
3825
- $get: ($injector) => {
3826
- const instance = $injector.instantiate(ctor);
3827
-
3828
- let backend;
3829
- let serialize = JSON.stringify;
3830
- let deserialize = JSON.parse;
3831
-
3832
- if (backendOrConfig) {
3833
- if (typeof backendOrConfig.getItem === "function") {
3834
- // raw Storage object
3835
- backend = backendOrConfig;
3836
- } else if (isObject(backendOrConfig)) {
3837
- backend = backendOrConfig.backend || localStorage;
3838
- if (backendOrConfig.serialize)
3839
- serialize = backendOrConfig.serialize;
3840
- if (backendOrConfig.deserialize)
3841
- deserialize = backendOrConfig.deserialize;
3826
+ $get: /** @param {ng.InjectorService} $injector */ ($injector) => {
3827
+ switch (type) {
3828
+ case "session": {
3829
+ const instance = $injector.instantiate(ctor);
3830
+ return createPersistentProxy(instance, name, sessionStorage);
3831
+ }
3832
+ case "local": {
3833
+ const instance = $injector.instantiate(ctor);
3834
+ return createPersistentProxy(instance, name, localStorage);
3835
+ }
3836
+ case "cookie": {
3837
+ const instance = $injector.instantiate(ctor);
3838
+ const $cookie = $injector.get($injectTokens.$cookie);
3839
+ const serialize = backendOrConfig.serialize ?? JSON.stringify;
3840
+ const deserialize = backendOrConfig.deserialize ?? JSON.parse;
3841
+ const cookieOpts = backendOrConfig.cookie ?? {};
3842
+
3843
+ return createPersistentProxy(instance, name, {
3844
+ getItem(key) {
3845
+ const raw = $cookie.get(key);
3846
+ return raw == null ? null : raw;
3847
+ },
3848
+
3849
+ setItem(key, value) {
3850
+ $cookie.put(key, value, cookieOpts);
3851
+ },
3852
+
3853
+ removeItem(key) {
3854
+ $cookie.remove(key, cookieOpts);
3855
+ },
3856
+
3857
+ serialize,
3858
+ deserialize,
3859
+ });
3842
3860
  }
3843
- } else {
3844
- // fallback default
3845
- backend = localStorage;
3846
- }
3861
+ case "custom": {
3862
+ const instance = $injector.instantiate(ctor);
3863
+
3864
+ let backend;
3865
+ let serialize = JSON.stringify;
3866
+ let deserialize = JSON.parse;
3867
+
3868
+ if (backendOrConfig) {
3869
+ if (typeof backendOrConfig.getItem === "function") {
3870
+ // raw Storage object
3871
+ backend = backendOrConfig;
3872
+ } else if (isObject(backendOrConfig)) {
3873
+ backend = backendOrConfig.backend || localStorage;
3874
+ if (backendOrConfig.serialize)
3875
+ serialize = backendOrConfig.serialize;
3876
+ if (backendOrConfig.deserialize)
3877
+ deserialize = backendOrConfig.deserialize;
3878
+ }
3879
+ } else {
3880
+ // fallback default
3881
+ backend = localStorage;
3882
+ }
3847
3883
 
3848
- return createPersistentProxy(instance, name, backend, {
3849
- serialize,
3850
- deserialize,
3851
- });
3884
+ return createPersistentProxy(instance, name, backend, {
3885
+ serialize,
3886
+ deserialize,
3887
+ });
3888
+ }
3889
+ }
3852
3890
  },
3853
3891
  });
3854
3892
  }
@@ -5713,7 +5751,7 @@
5713
5751
  const bindingCache = Object.create(null);
5714
5752
 
5715
5753
  /**
5716
- * @param {import("../scope/scope.js").Scope} scope
5754
+ * @param {ng.Scope} scope
5717
5755
  * @param {string} directiveName
5718
5756
  * @param {boolean} isController
5719
5757
  * @returns {Object} a configuration object for attribute bindings
@@ -5851,8 +5889,8 @@
5851
5889
  "$injector",
5852
5890
  "$exceptionHandler",
5853
5891
  /**
5854
- * @param {import("../../core/di/internal-injector.js").InjectorService} $injector
5855
- * @param {import('../../services/exception/exception-handler.js').ErrorHandler} $exceptionHandler
5892
+ * @param {ng.InjectorService} $injector
5893
+ * @param {ng.ExceptionHandlerService} $exceptionHandler
5856
5894
  */
5857
5895
  function ($injector, $exceptionHandler) {
5858
5896
  const directives = [];
@@ -6180,11 +6218,11 @@
6180
6218
  /**
6181
6219
  * @param {ng.InjectorService} $injector
6182
6220
  * @param {*} $interpolate
6183
- * @param {import("../../services/exception/exception-handler.js").ErrorHandler} $exceptionHandler
6221
+ * @param {ng.ExceptionHandlerService} $exceptionHandler
6184
6222
  * @param {ng.TemplateRequestService} $templateRequest
6185
6223
  * @param {ng.ParseService} $parse
6186
6224
  * @param {*} $controller
6187
- * @param {import('../scope/scope.js').Scope} $rootScope
6225
+ * @param {ng.Scope} $rootScope
6188
6226
  * @param {*} $sce
6189
6227
  * @param {ng.AnimateService} $animate
6190
6228
  * @returns
@@ -6471,7 +6509,7 @@
6471
6509
  /**
6472
6510
  * The composite link function links all the individual nodes
6473
6511
  *
6474
- * @param {import("../scope/scope.js").Scope} scope
6512
+ * @param {ng.Scope} scope
6475
6513
  * @param {NodeRef} nodeRef
6476
6514
  * @param {*} [parentBoundTranscludeFn]
6477
6515
  */
@@ -7031,7 +7069,7 @@
7031
7069
  transcludeFn,
7032
7070
  );
7033
7071
  } catch (e) {
7034
- $exceptionHandler(e, startingTag($element.getAny()));
7072
+ $exceptionHandler(e);
7035
7073
  }
7036
7074
  }
7037
7075
 
@@ -7084,7 +7122,7 @@
7084
7122
  transcludeFn,
7085
7123
  );
7086
7124
  } catch (e) {
7087
- $exceptionHandler(e, startingTag($element.getAny()));
7125
+ $exceptionHandler(e);
7088
7126
  }
7089
7127
  }
7090
7128
 
@@ -7549,7 +7587,7 @@
7549
7587
  );
7550
7588
  }
7551
7589
  } catch (e) {
7552
- $exceptionHandler(e, startingTag(compileNodeRef.getAny()));
7590
+ $exceptionHandler(e);
7553
7591
  }
7554
7592
  }
7555
7593
 
@@ -8500,9 +8538,9 @@
8500
8538
  // Set up $watches for isolate scope and controller bindings.
8501
8539
  /**
8502
8540
  *
8503
- * @param {import('../scope/scope.js').Scope} scope
8541
+ * @param {ng.Scope} scope
8504
8542
  * @param {*} attrs
8505
- * @param {import('../scope/scope.js').Scope} destination - child scope or isolate scope
8543
+ * @param {ng.Scope} destination - child scope or isolate scope
8506
8544
  * @param {*} bindings
8507
8545
  * @param {*} directive
8508
8546
  * @returns
@@ -9710,11 +9748,11 @@
9710
9748
  ];
9711
9749
 
9712
9750
  /**
9713
- * @param {import('../../core/scope/scope.js').Scope} $scope
9714
- * @param {import('../../services/exception/exception-handler.js').ErrorHandler} $exceptionHandler
9715
- * @param {import('../../core/compile/attributes.js').Attributes} $attr
9751
+ * @param {ng.Scope} $scope
9752
+ * @param {ng.ExceptionHandlerService} $exceptionHandler
9753
+ * @param {ng.Attributes} $attr
9716
9754
  * @param {Element} $element
9717
- * @param {import("../../core/parse/interface.ts").ParseService} $parse
9755
+ * @param {ng.ParseService} $parse
9718
9756
  * @param {ng.AnimateService} $animate
9719
9757
  * @param {*} $interpolate
9720
9758
  */
@@ -9772,7 +9810,7 @@
9772
9810
 
9773
9811
  /**
9774
9812
  * @type {import("../../core/parse/interface.ts").CompiledExpression |
9775
- * (function(import("../../core/scope/scope.js").Scope): any)}
9813
+ * (function(ng.Scope): any)}
9776
9814
  */
9777
9815
  this.$$ngModelGet = this.$$parsedNgModel;
9778
9816
  this.$$ngModelSet = this.$$parsedNgModelAssign;
@@ -9785,7 +9823,7 @@
9785
9823
  /** @type {number} */
9786
9824
  this.$$currentValidationRunId = 0;
9787
9825
 
9788
- /** @type {import('../../core/scope/scope.js').Scope} */
9826
+ /** @type {ng.Scope} */
9789
9827
  this.$$scope = $scope; // attempt to bind to nearest controller if present
9790
9828
  this.$$attr = $attr;
9791
9829
  this.$$element = $element;
@@ -11891,13 +11929,13 @@
11891
11929
 
11892
11930
  /**
11893
11931
  * @param {HTMLSelectElement} $element
11894
- * @param {import('../../core/scope/scope.js').Scope} $scope
11932
+ * @param {ng.Scope} $scope
11895
11933
  */
11896
11934
  constructor($element, $scope) {
11897
11935
  /** @type {HTMLSelectElement} */
11898
11936
  this.$element = $element;
11899
11937
 
11900
- /** @type {import('../../core/scope/scope.js').Scope} */
11938
+ /** @type {ng.Scope} */
11901
11939
  this.$scope = $scope;
11902
11940
 
11903
11941
  /** @type {Object<string, any>} */
@@ -12452,14 +12490,14 @@
12452
12490
  }
12453
12491
 
12454
12492
  /**
12455
- * @returns {import('../../interface.ts').Directive}
12493
+ * @returns {ng.Directive}
12456
12494
  */
12457
12495
  function ngBindDirective() {
12458
12496
  return {
12459
12497
  /**
12460
- * @param {import('../../core/scope/scope.js').Scope} scope
12498
+ * @param {ng.Scope} scope
12461
12499
  * @param {Element} element
12462
- * @param {import('../../core/compile/attributes.js').Attributes} attr
12500
+ * @param {ng.Attributes} attr
12463
12501
  */
12464
12502
  link(scope, element, attr) {
12465
12503
  scope.$watch(
@@ -12481,7 +12519,7 @@
12481
12519
  function ngBindTemplateDirective() {
12482
12520
  return {
12483
12521
  /**
12484
- * @param {import('../../core/scope/scope.js').Scope} _scope
12522
+ * @param {ng.Scope} _scope
12485
12523
  * @param {Element} element
12486
12524
  * @param {import('../../core/compile/attributes.js').Attributes} attr
12487
12525
  */
@@ -12505,7 +12543,7 @@
12505
12543
  $parse(tAttrs["ngBindHtml"]); // checks for interpolation errors
12506
12544
  return (
12507
12545
  /**
12508
- * @param {import('../../core/scope/scope.js').Scope} scope
12546
+ * @param {ng.Scope} scope
12509
12547
  * @param {Element} element
12510
12548
  */
12511
12549
  (scope, element) => {
@@ -12876,8 +12914,8 @@
12876
12914
  * @param {ng.TemplateRequestService} $templateRequest
12877
12915
  * @param {import("../../services/anchor-scroll/anchor-scroll.js").AnchorScrollFunction} $anchorScroll
12878
12916
  * @param {ng.AnimateService} $animate
12879
- * @param {import('../../services/exception/interface.ts').ErrorHandler} $exceptionHandler
12880
- * @returns {import('../../interface.ts').Directive}
12917
+ * @param {ng.ExceptionHandlerService} $exceptionHandler
12918
+ * @returns {ng.Directive}
12881
12919
  */
12882
12920
  function ngIncludeDirective(
12883
12921
  $templateRequest,
@@ -13404,7 +13442,7 @@
13404
13442
  /**
13405
13443
  * Clone attach function
13406
13444
  * @param {Array<NodeList>} clone
13407
- * @param {import("../../core/scope/scope.js").Scope} scope
13445
+ * @param {ng.Scope} scope
13408
13446
  */
13409
13447
 
13410
13448
  (clone, scope) => {
@@ -13630,14 +13668,14 @@
13630
13668
  /**
13631
13669
  *
13632
13670
  * @param {ng.CompileService} $compile
13633
- * @param {import("../../core/parse/interface.ts").ParseService} $parse
13634
- * @returns {import("../../interface.ts").Directive}
13671
+ * @param {ng.ParseService} $parse
13672
+ * @returns {ng.Directive}
13635
13673
  */
13636
13674
  function ngOptionsDirective($compile, $parse) {
13637
13675
  /**
13638
13676
  * @param {import('../../interface.ts').Expression} optionsExp
13639
13677
  * @param {HTMLSelectElement} selectElement
13640
- * @param {import('../../core/scope/scope.js').Scope} scope
13678
+ * @param {ng.Scope} scope
13641
13679
  * @returns
13642
13680
  */
13643
13681
  function parseOptionsExpression(optionsExp, selectElement, scope) {
@@ -13817,9 +13855,9 @@
13817
13855
 
13818
13856
  /**
13819
13857
  *
13820
- * @param {import("../../core/scope/scope.js").Scope} scope
13858
+ * @param {ng.Scope} scope
13821
13859
  * @param {HTMLSelectElement} selectElement
13822
- * @param {import("../../core/compile/attributes.js").Attributes} attr
13860
+ * @param {ng.Attributes} attr
13823
13861
  * @param {*} ctrls
13824
13862
  */
13825
13863
  function ngOptionsPostLink(scope, selectElement, attr, ctrls) {
@@ -14207,7 +14245,7 @@
14207
14245
 
14208
14246
  /**
14209
14247
  * @param {NodeList | Node} clone
14210
- * @param {import("../../core/scope/scope.js").Scope} transcludedScope
14248
+ * @param {ng.Scope} transcludedScope
14211
14249
  */
14212
14250
  function ngTranscludeCloneAttachFn(clone, transcludedScope) {
14213
14251
  if (notWhitespace(clone)) {
@@ -14392,7 +14430,7 @@
14392
14430
  require: "?ngModel",
14393
14431
  link:
14394
14432
  /**
14395
- * @param {import("../../core/scope/scope.js").Scope} scope
14433
+ * @param {ng.Scope} scope
14396
14434
  * @param {Element} _elm
14397
14435
  * @param {import("../../core/compile/attributes.js").Attributes} attr
14398
14436
  * @param {import("../../interface.ts").NgModelController} ctrl
@@ -15321,78 +15359,66 @@
15321
15359
  }
15322
15360
 
15323
15361
  /**
15324
- * Handles uncaught exceptions thrown in AngularTS expressions.
15325
- *
15326
- * By default, this service delegates to `$log.error()`, logging the exception to the browser console.
15327
- * You can override this behavior to provide custom exception handling—such as reporting errors
15328
- * to a backend server, or altering the log level used.
15362
+ * Unified exception handler used throughout AngularTS.
15329
15363
  *
15330
- * ## Default Behavior
15364
+ * This service receives uncaught exceptions from both synchronous and asynchronous operations.
15365
+ * Its purpose is to provide a central point through which the framework
15366
+ * processes errors.
15331
15367
  *
15332
- * Uncaught exceptions within AngularTS expressions are intercepted and passed to this service.
15333
- * The default implementation logs the error using `$log.error(exception, cause)`.
15368
+ * By default, `$exceptionHandler` simply rethrows the exception. This ensures fail-fast
15369
+ * behavior, making errors visible immediately in development and in unit tests.
15370
+ * Applications may override this service to introduce custom error handling.
15334
15371
  *
15335
- * ## Custom Implementation
15336
- *
15337
- * You can override the default `$exceptionHandler` by providing your own factory. This allows you to:
15338
- * - Log errors to a remote server
15339
- * - Change the log level (e.g., from `error` to `warn`)
15340
- * - Trigger custom error-handling workflows
15341
- *
15342
- * ### Example: Overriding `$exceptionHandler`
15372
+ * ### Example: Custom `$exceptionHandler`
15343
15373
  *
15344
15374
  * ```js
15345
15375
  * angular
15346
- * .module('exceptionOverwrite', [])
15347
- * .factory('$exceptionHandler', ['$log', 'logErrorsToBackend', function($log, logErrorsToBackend) {
15348
- * return function myExceptionHandler(exception, cause) {
15349
- * logErrorsToBackend(exception, cause);
15350
- * $log.warn(exception, cause); // Use warn instead of error
15376
+ * .module('app')
15377
+ * .factory('$exceptionHandler', function(myLogger) {
15378
+ * return function handleError(error) {
15379
+ * myLogger.capture(error);
15380
+ * // Rethrow to preserve fail-fast behavior:
15381
+ * throw error;
15351
15382
  * };
15352
- * }]);
15383
+ * });
15353
15384
  * ```
15354
- * - You may also manually invoke the exception handler:
15385
+ *
15386
+ * IMPORTANT: custom implementation should always rethrow the error as the framework assumes that `$exceptionHandler` always does the throwing.
15387
+ *
15388
+ * ### Manual Invocation
15389
+ *
15390
+ * You can invoke the exception handler directly when catching errors in your own code:
15355
15391
  *
15356
15392
  * ```js
15357
15393
  * try {
15358
- * // Some code that might throw
15359
- * } catch (e) {
15360
- * $exceptionHandler(e, 'optional context');
15394
+ * riskyOperation();
15395
+ * } catch (err) {
15396
+ * $exceptionHandler(err);
15361
15397
  * }
15362
15398
  * ```
15363
15399
  *
15364
- * @see {@link angular.ErrorHandler AngularTS ErrorHandler}
15400
+ * @see {@link ng.ExceptionHandlerService ExceptionHandlerService}
15365
15401
  */
15366
15402
 
15367
- /** @typedef {import('../log/interface.ts').LogService} LogService */
15368
-
15369
- /** @typedef {import("./interface.ts").ErrorHandler} ErrorHandler */
15370
-
15371
15403
  /**
15372
- * Provider for `$exceptionHandler` service. Delegates uncaught exceptions to `$log.error()` by default.
15373
- * Can be overridden to implement custom error-handling logic.
15404
+ * Provider for the `$exceptionHandler` service.
15405
+ *
15406
+ * The default implementation rethrows exceptions, enabling strict fail-fast behavior.
15407
+ * Applications may replace the handler via by setting `errorHandler`property or by providing their own
15408
+ * `$exceptionHandler` factory.
15374
15409
  */
15375
15410
  class ExceptionHandlerProvider {
15376
15411
  constructor() {
15377
- /** @type {LogService} */
15378
- this.log = window.console;
15379
-
15380
- /** @type {ErrorHandler} */
15381
- this.errorHandler = (exception, cause) => {
15382
- this.log.error(exception, cause);
15412
+ /** @type {ng.ExceptionHandlerService} */
15413
+ this.handler = (exception) => {
15414
+ throw exception;
15383
15415
  };
15384
-
15385
- this.$get = [
15386
- "$log",
15387
- /**
15388
- * @param {LogService} $log
15389
- * @returns {ErrorHandler}
15390
- */
15391
- ($log) => {
15392
- this.log = $log;
15393
- return this.errorHandler;
15394
- },
15395
- ];
15416
+ }
15417
+ /**
15418
+ * @returns {ng.ExceptionHandlerService}
15419
+ */
15420
+ $get() {
15421
+ return (exception) => this.handler(exception);
15396
15422
  }
15397
15423
  }
15398
15424
 
@@ -18005,7 +18031,7 @@
18005
18031
 
18006
18032
  /**
18007
18033
  *
18008
- * @param {import('../scope/scope.js').Scope} scope
18034
+ * @param {ng.Scope} scope
18009
18035
  * @param {Function} listener
18010
18036
  * @param {*} objectEquality
18011
18037
  * @param {import('./interface').CompiledExpression} parsedExpression
@@ -25722,12 +25748,11 @@
25722
25748
 
25723
25749
  /**
25724
25750
  * @typedef {import('./interface.ts').RafScheduler} RafScheduler
25725
- * @typedef {import('../interface.ts').ServiceProvider} ServiceProvider
25726
25751
  */
25727
25752
 
25728
25753
  /**
25729
25754
  * Service provider that creates a requestAnimationFrame-based scheduler.
25730
- * @type {ServiceProvider}
25755
+ * @type {ng.ServiceProvider}
25731
25756
  */
25732
25757
  class RafSchedulerProvider {
25733
25758
  constructor() {
@@ -33923,8 +33948,8 @@
33923
33948
  $injectTokens.$rootScope,
33924
33949
  /**
33925
33950
  *
33926
- * @param {import('../../services/location/location.js').Location} $location
33927
- * @param {import('../../core/scope/scope.js').Scope} $rootScope
33951
+ * @param {ng.LocationService} $location
33952
+ * @param {ng.Scope} $rootScope
33928
33953
  * @returns {UrlService}
33929
33954
  */
33930
33955
  ($location, $rootScope) => {
@@ -36276,6 +36301,722 @@
36276
36301
  };
36277
36302
  }
36278
36303
 
36304
+ /**
36305
+ * Service provider that creates a {@link CookieService $cookie} service.
36306
+ * @type {ng.ServiceProvider}
36307
+ */
36308
+ class CookieProvider {
36309
+ constructor() {
36310
+ this.defaults = {};
36311
+ }
36312
+
36313
+ $get = [
36314
+ $injectTokens.$exceptionHandler,
36315
+ /** @param {ng.ExceptionHandlerService} $exceptionHandler */
36316
+ ($exceptionHandler) => new CookieService(this.defaults, $exceptionHandler),
36317
+ ];
36318
+ }
36319
+
36320
+ /**
36321
+ *
36322
+ * Provides high-level APIs for interacting with browser cookies:
36323
+ * - Raw get/set/remove
36324
+ * - JSON serialization helpers
36325
+ * - Global defaults supplied by $cookiesProvider
36326
+ */
36327
+ class CookieService {
36328
+ /**
36329
+ * @param {ng.CookieOptions} defaults
36330
+ * Default cookie attributes defined by `$cookiesProvider.defaults`.
36331
+ * @param {ng.ExceptionHandlerService} $exceptionHandler
36332
+ */
36333
+ constructor(defaults, $exceptionHandler) {
36334
+ assert(isObject(defaults), BADARG);
36335
+ assert(isFunction($exceptionHandler), BADARG);
36336
+ /** @type {ng.CookieOptions} */
36337
+ this.defaults = Object.freeze({ ...defaults });
36338
+ this.$exceptionHandler = $exceptionHandler;
36339
+ }
36340
+
36341
+ /**
36342
+ * Retrieves a raw cookie value.
36343
+ *
36344
+ * @param {string} key
36345
+ * @returns {string|null}
36346
+ */
36347
+ get(key) {
36348
+ assert(isString(key), BADARG);
36349
+ const all = parseCookies();
36350
+ return all[key] || null;
36351
+ }
36352
+
36353
+ /**
36354
+ * Retrieves a cookie and deserializes its JSON content.
36355
+ *
36356
+ * @template T
36357
+ * @param {string} key
36358
+ * @returns {T|null}
36359
+ * @throws {SyntaxError} if cookie JSON is invalid
36360
+ */
36361
+ getObject(key) {
36362
+ assert(isString(key), BADARG);
36363
+ const raw = this.get(key);
36364
+ if (!raw) return null;
36365
+ try {
36366
+ return /** @type {T} */ (JSON.parse(raw));
36367
+ } catch (err) {
36368
+ this.$exceptionHandler(
36369
+ new SyntaxError(`badparse: "${key}" => ${err.message}`),
36370
+ );
36371
+ }
36372
+ }
36373
+
36374
+ /**
36375
+ * Returns an object containing all raw cookies.
36376
+ *
36377
+ * @returns {Record<string, string>}
36378
+ */
36379
+ getAll() {
36380
+ return parseCookies();
36381
+ }
36382
+
36383
+ /**
36384
+ * Sets a raw cookie value.
36385
+ *
36386
+ * @param {string} key
36387
+ * @param {string} value
36388
+ * @param {ng.CookieOptions} [options]
36389
+ */
36390
+ put(key, value, options = {}) {
36391
+ assert(isString(key), BADARGKEY);
36392
+ assert(isString(value), BADARGVALUE);
36393
+ const encodedKey = encodeURIComponent(key);
36394
+ const encodedVal = encodeURIComponent(value);
36395
+
36396
+ try {
36397
+ document.cookie =
36398
+ `${encodedKey}=${encodedVal}` +
36399
+ buildOptions({ ...this.defaults, ...options });
36400
+ } catch (e) {
36401
+ this.$exceptionHandler(e);
36402
+ }
36403
+ }
36404
+
36405
+ /**
36406
+ * Serializes an object as JSON and stores it as a cookie.
36407
+ *
36408
+ * @param {string} key
36409
+ * @param {any} value
36410
+ * @param {ng.CookieOptions} [options]
36411
+ * @throws {TypeError} if Object cannot be converted to JSON
36412
+ */
36413
+ putObject(key, value, options) {
36414
+ assert(isString(key), BADARGKEY);
36415
+ assert(!isNullOrUndefined(value), BADARGVALUE);
36416
+ try {
36417
+ const str = JSON.stringify(value);
36418
+ this.put(key, str, options);
36419
+ } catch (err) {
36420
+ this.$exceptionHandler(
36421
+ new TypeError(`badserialize: "${key}" => ${err.message}`),
36422
+ );
36423
+ }
36424
+ }
36425
+
36426
+ /**
36427
+ * Removes a cookie by setting an expired date.
36428
+ *
36429
+ * @param {string} key
36430
+ * @param {ng.CookieOptions} [options]
36431
+ */
36432
+ remove(key, options = {}) {
36433
+ assert(isString(key), BADARG);
36434
+ this.put(key, "", {
36435
+ ...this.defaults,
36436
+ ...options,
36437
+ expires: new Date(0),
36438
+ });
36439
+ }
36440
+ }
36441
+
36442
+ /*----------Helpers----------*/
36443
+
36444
+ /**
36445
+ * @returns {Record<string,string>}
36446
+ */
36447
+ function parseCookies() {
36448
+ /** @type {Record<string, string>} */
36449
+ const out = {};
36450
+ if (!document.cookie) return out;
36451
+
36452
+ const parts = document.cookie.split("; ");
36453
+ for (const part of parts) {
36454
+ const eq = part.indexOf("=");
36455
+ if (eq === -1) continue; // skip malformed cookie
36456
+ const key = decodeURIComponent(part.substring(0, eq));
36457
+ const val = decodeURIComponent(part.substring(eq + 1));
36458
+ out[key] = val;
36459
+ }
36460
+ return out;
36461
+ }
36462
+
36463
+ /**
36464
+ * Build cookie options string from an options object.
36465
+ * Safely validates types for path, domain, expires, secure, and samesite.
36466
+ *
36467
+ * @param {ng.CookieOptions} opts
36468
+ * @returns {string}
36469
+ * @throws {TypeError} if any of options are invalid
36470
+ */
36471
+ function buildOptions(opts = {}) {
36472
+ const parts = [];
36473
+
36474
+ // Path
36475
+ if (isDefined(opts.path)) {
36476
+ if (!isString(opts.path))
36477
+ throw new TypeError(BADARG + `:path ${opts.path}`);
36478
+ parts.push(`path=${opts.path}`);
36479
+ }
36480
+
36481
+ // Domain
36482
+ if (isDefined(opts.domain)) {
36483
+ if (!isString(opts.domain))
36484
+ throw new TypeError(BADARG + `:domain ${opts.domain}`);
36485
+ parts.push(`domain=${opts.domain}`);
36486
+ }
36487
+
36488
+ // Expires
36489
+ if (opts.expires != null) {
36490
+ let expDate;
36491
+
36492
+ if (opts.expires instanceof Date) {
36493
+ expDate = opts.expires;
36494
+ } else if (isNumber(opts.expires) || isString(opts.expires)) {
36495
+ expDate = new Date(opts.expires);
36496
+ } else {
36497
+ throw new TypeError(BADARG + `:expires ${String(opts.expires)}`);
36498
+ }
36499
+
36500
+ if (isNaN(expDate.getTime())) {
36501
+ throw new TypeError(BADARG + `:expires ${String(opts.expires)}`);
36502
+ }
36503
+
36504
+ parts.push(`expires=${expDate.toUTCString()}`);
36505
+ }
36506
+
36507
+ // Secure
36508
+ if (opts.secure) {
36509
+ parts.push("secure");
36510
+ }
36511
+
36512
+ // SameSite
36513
+ if (isDefined(opts.samesite)) {
36514
+ if (!isString(opts.samesite))
36515
+ throw new TypeError(BADARG + `:samesite ${opts.samesite}`);
36516
+ const s = opts.samesite.toLowerCase();
36517
+ if (!["lax", "strict", "none"].includes(s)) {
36518
+ throw new TypeError(BADARG + `:samesite ${opts.samesite}`);
36519
+ }
36520
+ parts.push(`samesite=${s}`);
36521
+ }
36522
+
36523
+ // Join all parts with semicolons
36524
+ return parts.length ? ";" + parts.join(";") : "";
36525
+ }
36526
+
36527
+ /**
36528
+ * RFC 6570 Level 4 URI Template expander
36529
+ *
36530
+ * Supports operators: (none), +, #, ., /, ;, ?, &
36531
+ * Supports varspec modifiers: explode (*) and prefix (:len)
36532
+ *
36533
+ * Usage:
36534
+ * expandUriTemplate("/users/{id}", { id: 10 }) === "/users/10"
36535
+ * expandUriTemplate("/search{?q,lang}", { q: "a b", lang: "en" }) === "/search?q=a%20b&lang=en"
36536
+ * expandUriTemplate("/repos/{owner}/{repo}/issues{?labels*}", { labels: ["bug","ui"] }) === "/repos/x/y/issues?labels=bug&labels=ui"
36537
+ *
36538
+ * @param {string} template
36539
+ * @param {Object<string, any>} vars
36540
+ * @returns {string}
36541
+ */
36542
+ function expandUriTemplate(template, vars = {}) {
36543
+ if (typeof template !== "string")
36544
+ throw new TypeError("template must be a string");
36545
+
36546
+ return template.replace(/\{([^}]+)\}/g, (match, expression) => {
36547
+ return expandExpression(expression, vars);
36548
+ });
36549
+ }
36550
+
36551
+ /**
36552
+ * Helper: percent-encode a string. If allowReserved true, reserved chars are NOT encoded.
36553
+ * @param {string} str
36554
+ * @param {boolean} allowReserved
36555
+ * @returns {string}
36556
+ */
36557
+ function pctEncode(str, allowReserved) {
36558
+ // encodeURIComponent, then restore reserved if allowed
36559
+ const encoded = encodeURIComponent(String(str));
36560
+ if (allowReserved) {
36561
+ // Reserved characters per RFC 3986
36562
+ return encoded.replace(
36563
+ /(%3A|%2F|%3F|%23|%5B|%5D|%40|%21|%24|%26|%27|%28|%29|%2A|%2B|%2C|%3B|%3D)/gi,
36564
+ (m) => decodeURIComponent(m),
36565
+ );
36566
+ }
36567
+ return encoded;
36568
+ }
36569
+
36570
+ /**
36571
+ * Parse and expand a single expression (content between { and }).
36572
+ * @param {string} expression
36573
+ * @param {Object<string, any>} vars
36574
+ * @returns {string}
36575
+ */
36576
+ function expandExpression(expression, vars) {
36577
+ // Operator if first char in operator set
36578
+ const operator = /^[+#./;?&]/.test(expression) ? expression[0] : "";
36579
+ const op = operator;
36580
+ const varlist = op ? expression.slice(1) : expression;
36581
+
36582
+ // operator configuration (separator, prefix, named, ifEmpty, allowReserved)
36583
+ const OP = {
36584
+ "": {
36585
+ sep: ",",
36586
+ prefix: "",
36587
+ named: false,
36588
+ ifEmpty: "",
36589
+ allowReserved: false,
36590
+ },
36591
+ "+": {
36592
+ sep: ",",
36593
+ prefix: "",
36594
+ named: false,
36595
+ ifEmpty: "",
36596
+ allowReserved: true,
36597
+ },
36598
+ "#": {
36599
+ sep: ",",
36600
+ prefix: "#",
36601
+ named: false,
36602
+ ifEmpty: "",
36603
+ allowReserved: true,
36604
+ },
36605
+ ".": {
36606
+ sep: ".",
36607
+ prefix: ".",
36608
+ named: false,
36609
+ ifEmpty: "",
36610
+ allowReserved: false,
36611
+ },
36612
+ "/": {
36613
+ sep: "/",
36614
+ prefix: "/",
36615
+ named: false,
36616
+ ifEmpty: "",
36617
+ allowReserved: false,
36618
+ },
36619
+ ";": {
36620
+ sep: ";",
36621
+ prefix: ";",
36622
+ named: true,
36623
+ ifEmpty: "",
36624
+ allowReserved: false,
36625
+ },
36626
+ "?": {
36627
+ sep: "&",
36628
+ prefix: "?",
36629
+ named: true,
36630
+ ifEmpty: "=",
36631
+ allowReserved: false,
36632
+ },
36633
+ "&": {
36634
+ sep: "&",
36635
+ prefix: "&",
36636
+ named: true,
36637
+ ifEmpty: "=",
36638
+ allowReserved: false,
36639
+ },
36640
+ };
36641
+
36642
+ const conf = OP[op];
36643
+ if (!conf) throw new Error("Unsupported operator: " + op);
36644
+
36645
+ // split varspecs by comma, preserve whitespace trimmed
36646
+ const varspecs = varlist
36647
+ .split(",")
36648
+ .map((s) => s.trim())
36649
+ .filter(Boolean);
36650
+
36651
+ const expandedParts = [];
36652
+
36653
+ for (const spec of varspecs) {
36654
+ // parse varspec: name, explode (*), prefix (:len)
36655
+ const m = /^([A-Za-z0-9_.]+)(\*|(?::(\d+)))?$/.exec(spec);
36656
+ if (!m) throw new Error("Invalid varspec: " + spec);
36657
+ const varname = m[1];
36658
+ const explode = m[2] === "*";
36659
+ const prefixLength = m[3] ? parseInt(m[3], 10) : undefined;
36660
+
36661
+ const value = vars[varname];
36662
+
36663
+ // undefined or null = skip (no expansion)
36664
+ if (value === undefined || value === null) {
36665
+ continue;
36666
+ }
36667
+
36668
+ // PROCESS arrays
36669
+ if (Array.isArray(value)) {
36670
+ if (value.length === 0) {
36671
+ // empty array: for named operators, emit key with empty ifEmpty, otherwise skip
36672
+ if (conf.named) {
36673
+ // emit key without value or with = depending on ifEmpty
36674
+ if (conf.ifEmpty === "=") {
36675
+ expandedParts.push(
36676
+ `${pctEncode(varname, conf.allowReserved)}${conf.ifEmpty}`,
36677
+ );
36678
+ } else {
36679
+ expandedParts.push(pctEncode(varname, conf.allowReserved));
36680
+ }
36681
+ }
36682
+ continue;
36683
+ }
36684
+
36685
+ if (explode) {
36686
+ // each item becomes either 'k=v' (named) or 'v' (unnamed)
36687
+ for (const item of value) {
36688
+ if (item === null || item === undefined) continue;
36689
+ if (conf.named) {
36690
+ expandedParts.push(
36691
+ `${pctEncode(varname, conf.allowReserved)}=${pctEncode(item, conf.allowReserved)}`,
36692
+ );
36693
+ } else {
36694
+ expandedParts.push(pctEncode(item, conf.allowReserved));
36695
+ }
36696
+ }
36697
+ } else {
36698
+ // join by comma (or operator.sep?) — RFC: simple join with ','
36699
+ const joined = value
36700
+ .filter((v) => v !== null && v !== undefined)
36701
+ .map((v) => pctEncode(v, conf.allowReserved))
36702
+ .join(",");
36703
+ if (conf.named) {
36704
+ if (joined === "") {
36705
+ expandedParts.push(
36706
+ pctEncode(varname, conf.allowReserved) +
36707
+ (conf.ifEmpty === "=" ? conf.ifEmpty : ""),
36708
+ );
36709
+ } else {
36710
+ expandedParts.push(
36711
+ `${pctEncode(varname, conf.allowReserved)}=${joined}`,
36712
+ );
36713
+ }
36714
+ } else {
36715
+ expandedParts.push(joined);
36716
+ }
36717
+ }
36718
+ continue;
36719
+ }
36720
+
36721
+ // PROCESS objects (associative arrays)
36722
+ if (typeof value === "object") {
36723
+ const keys = Object.keys(value);
36724
+ if (keys.length === 0) {
36725
+ if (conf.named) {
36726
+ expandedParts.push(
36727
+ pctEncode(varname, conf.allowReserved) +
36728
+ (conf.ifEmpty === "=" ? conf.ifEmpty : ""),
36729
+ );
36730
+ }
36731
+ continue;
36732
+ }
36733
+
36734
+ if (explode) {
36735
+ // each key/value pair becomes k=v (named) or k,v? For explode + named, RFC says 'k=v'
36736
+ for (const k of keys) {
36737
+ const v = value[k];
36738
+ if (v === null || v === undefined) continue;
36739
+ if (conf.named) {
36740
+ expandedParts.push(
36741
+ `${pctEncode(k, conf.allowReserved)}=${pctEncode(v, conf.allowReserved)}`,
36742
+ );
36743
+ } else {
36744
+ // unnamed explode => k,v form pairs
36745
+ expandedParts.push(
36746
+ `${pctEncode(k, conf.allowReserved)}=${pctEncode(v, conf.allowReserved)}`,
36747
+ );
36748
+ }
36749
+ }
36750
+ } else {
36751
+ // not exploded: join k,v pairs by ','
36752
+ const pairs = keys
36753
+ .map(
36754
+ (k) =>
36755
+ `${pctEncode(k, conf.allowReserved)},${pctEncode(value[k], conf.allowReserved)}`,
36756
+ )
36757
+ .join(",");
36758
+ if (conf.named) {
36759
+ if (pairs === "") {
36760
+ expandedParts.push(
36761
+ pctEncode(varname, conf.allowReserved) +
36762
+ (conf.ifEmpty === "=" ? conf.ifEmpty : ""),
36763
+ );
36764
+ } else {
36765
+ expandedParts.push(
36766
+ `${pctEncode(varname, conf.allowReserved)}=${pairs}`,
36767
+ );
36768
+ }
36769
+ } else {
36770
+ expandedParts.push(pairs);
36771
+ }
36772
+ }
36773
+ continue;
36774
+ }
36775
+
36776
+ // PROCESS scalar (string/number/boolean)
36777
+ let str = String(value);
36778
+
36779
+ // apply prefix modifier if present
36780
+ if (typeof prefixLength === "number") {
36781
+ str = str.substring(0, prefixLength);
36782
+ }
36783
+
36784
+ // empty string handling
36785
+ if (str === "") {
36786
+ if (conf.named) {
36787
+ // for named operators, emit key or key= depending on ifEmpty
36788
+ if (conf.ifEmpty === "=") {
36789
+ expandedParts.push(
36790
+ `${pctEncode(varname, conf.allowReserved)}${conf.ifEmpty}`,
36791
+ );
36792
+ } else {
36793
+ expandedParts.push(pctEncode(varname, conf.allowReserved));
36794
+ }
36795
+ } else {
36796
+ // unnamed operators: empty string -> nothing (skip)
36797
+ if (op === "+" || op === "#") {
36798
+ // these allow empty expansions (produce nothing)
36799
+ expandedParts.push(pctEncode(str, conf.allowReserved));
36800
+ } else {
36801
+ // skip adding anything
36802
+ expandedParts.push(pctEncode(str, conf.allowReserved));
36803
+ }
36804
+ }
36805
+ continue;
36806
+ }
36807
+
36808
+ // default scalar behavior
36809
+ if (conf.named) {
36810
+ expandedParts.push(
36811
+ `${pctEncode(varname, conf.allowReserved)}=${pctEncode(str, conf.allowReserved)}`,
36812
+ );
36813
+ } else {
36814
+ expandedParts.push(pctEncode(str, conf.allowReserved));
36815
+ }
36816
+ } // end for varspecs
36817
+
36818
+ if (expandedParts.length === 0) return "";
36819
+
36820
+ // join parts with operator separator; prefix if needed
36821
+ return conf.prefix + expandedParts.join(conf.sep);
36822
+ }
36823
+
36824
+ /**
36825
+ * @template T, ID
36826
+ */
36827
+ class RestService {
36828
+ static $nonscope = true;
36829
+
36830
+ /**
36831
+ * Core REST service for CRUD operations.
36832
+ * Safe, predictable, and optionally maps raw JSON to entity class instances.
36833
+ *
36834
+ * @param {ng.HttpService} $http Angular-like $http service
36835
+ * @param {string} baseUrl Base URL or URI template
36836
+ * @param {{new(data: any): T}=} entityClass Optional constructor to map JSON to objects
36837
+ * @param {Object=} options Optional settings (interceptors, headers, etc.)
36838
+ */
36839
+ constructor($http, baseUrl, entityClass, options = {}) {
36840
+ assert(isString(baseUrl) && baseUrl.length > 0, "baseUrl required");
36841
+
36842
+ /** @private */
36843
+ this.$http = $http;
36844
+ /** @private */
36845
+ this.baseUrl = baseUrl;
36846
+ /** @private */
36847
+ this.entityClass = entityClass;
36848
+ /** @private */
36849
+ this.options = options;
36850
+ }
36851
+
36852
+ /**
36853
+ * Build full URL from template and parameters
36854
+ * @param {string} template
36855
+ * @param {Record<string, any>} params
36856
+ * @returns {string}
36857
+ */
36858
+ buildUrl(template, params) {
36859
+ // Safe: ensure params is an object
36860
+ return expandUriTemplate(template, params || {});
36861
+ }
36862
+
36863
+ /**
36864
+ * Map raw JSON to entity instance or return as-is
36865
+ * @param {any} data
36866
+ * @returns {T|any}
36867
+ */
36868
+ #mapEntity(data) {
36869
+ if (!data) return data;
36870
+ return this.entityClass ? new this.entityClass(data) : data;
36871
+ }
36872
+
36873
+ /**
36874
+ * List entities
36875
+ * @param {Record<string, any>=} params
36876
+ * @returns {Promise<T[]>}
36877
+ */
36878
+ async list(params = {}) {
36879
+ const url = this.buildUrl(this.baseUrl, params);
36880
+ const resp = await this.#request("get", url, null, params);
36881
+ if (!Array.isArray(resp.data)) return [];
36882
+ return resp.data.map((d) => this.#mapEntity(d));
36883
+ }
36884
+
36885
+ /**
36886
+ * Read single entity by ID
36887
+ * @param {ID} id
36888
+ * @param {Record<string, any>=} params
36889
+ * @returns {Promise<T|null>}
36890
+ */
36891
+ async read(id, params = {}) {
36892
+ if (id == null) return null;
36893
+ const url = this.buildUrl(`${this.baseUrl}/${id}`, params);
36894
+ try {
36895
+ const resp = await this.#request("get", url, null, params);
36896
+ return this.#mapEntity(resp.data);
36897
+ } catch {
36898
+ return null; // fail-safe
36899
+ }
36900
+ }
36901
+
36902
+ /**
36903
+ * Create a new entity
36904
+ * @param {T} item
36905
+ * @returns {Promise<T>}
36906
+ */
36907
+ async create(item) {
36908
+ assert(item != null, "item required for create");
36909
+ const resp = await this.#request("post", this.baseUrl, item);
36910
+ return this.#mapEntity(resp.data);
36911
+ }
36912
+
36913
+ /**
36914
+ * Update entity by ID
36915
+ * @param {ID} id
36916
+ * @param {Partial<T>} item
36917
+ * @returns {Promise<T|null>}
36918
+ */
36919
+ async update(id, item) {
36920
+ assert(id != null, "id required for update");
36921
+ const url = `${this.baseUrl}/${id}`;
36922
+ try {
36923
+ const resp = await this.#request("put", url, item);
36924
+ return this.#mapEntity(resp.data);
36925
+ } catch {
36926
+ return null;
36927
+ }
36928
+ }
36929
+
36930
+ /**
36931
+ * Delete entity by ID
36932
+ * @param {ID} id
36933
+ * @returns {Promise<boolean>}
36934
+ */
36935
+ async delete(id) {
36936
+ if (id == null) return false;
36937
+ const url = `${this.baseUrl}/${id}`;
36938
+ try {
36939
+ await this.#request("delete", url);
36940
+ return true;
36941
+ } catch {
36942
+ return false;
36943
+ }
36944
+ }
36945
+
36946
+ /**
36947
+ * Core HTTP request wrapper
36948
+ * @param {"get"|"post"|"put"|"delete"} method
36949
+ * @param {string} url
36950
+ * @param {any=} data
36951
+ * @param {Record<string, any>=} params
36952
+ * @returns {Promise<any>}
36953
+ */
36954
+ async #request(method, url, data = null, params = {}) {
36955
+ try {
36956
+ return await this.$http({
36957
+ method,
36958
+ url,
36959
+ data,
36960
+ params,
36961
+ ...this.options,
36962
+ });
36963
+ } catch (err) {
36964
+ console.error(`[RestService] HTTP ${method} failed for ${url}`, err);
36965
+ throw err; // propagate for caller handling
36966
+ }
36967
+ }
36968
+ }
36969
+
36970
+ /**
36971
+ * Provider for registering REST endpoints during module configuration.
36972
+ */
36973
+ class RestProvider {
36974
+ constructor() {
36975
+ /** @private @type {ng.RestDefinition<any>[]} */
36976
+ this.definitions = [];
36977
+ }
36978
+
36979
+ /**
36980
+ * Register a REST resource at config phase
36981
+ * @template T
36982
+ * @param {string} name Service name
36983
+ * @param {string} url Base URL or URI template
36984
+ * @param {{new(data:any):T}=} entityClass Optional entity constructor
36985
+ * @param {Object=} options Optional service options
36986
+ */
36987
+ rest(name, url, entityClass, options = {}) {
36988
+ this.definitions.push({ name, url, entityClass, options });
36989
+ }
36990
+
36991
+ /**
36992
+ * $get factory: returns a factory function and allows access to named services
36993
+ * @returns {(baseUrl:string, entityClass?:Function, options?:object) => RestService & { get(name:string): RestService, listNames(): string[] }}
36994
+ */
36995
+ $get = [
36996
+ $injectTokens.$http,
36997
+ ($http) => {
36998
+ const services = new Map();
36999
+
37000
+ const factory = (baseUrl, entityClass, options = {}) => {
37001
+ const svc = new RestService($http, baseUrl, entityClass, options);
37002
+ return svc;
37003
+ };
37004
+
37005
+ // create services from pre-registered definitions
37006
+ for (const def of this.definitions) {
37007
+ const svc = factory(def.url, def.entityClass, def.options);
37008
+ services.set(def.name, svc);
37009
+ }
37010
+
37011
+ // helpers to fetch named services
37012
+ factory.get = (name) => services.get(name);
37013
+ factory.listNames = () => Array.from(services.keys());
37014
+
37015
+ return factory;
37016
+ },
37017
+ ];
37018
+ }
37019
+
36279
37020
  /**
36280
37021
  * Initializes core `ng` module.
36281
37022
  * @param {ng.Angular} angular
@@ -36399,6 +37140,7 @@
36399
37140
  $$animateCache: AnimateCacheProvider,
36400
37141
  $$animateQueue: AnimateQueueProvider,
36401
37142
  $controller: ControllerProvider,
37143
+ $cookie: CookieProvider,
36402
37144
  $exceptionHandler: ExceptionHandlerProvider,
36403
37145
  $filter: FilterProvider,
36404
37146
  $interpolate: InterpolateProvider,
@@ -36408,6 +37150,7 @@
36408
37150
  $log: LogProvider,
36409
37151
  $parse: ParseProvider,
36410
37152
  $$rAFScheduler: RafSchedulerProvider,
37153
+ $rest: RestProvider,
36411
37154
  $rootScope: RootScopeProvider,
36412
37155
  $router: Router,
36413
37156
  $sce: SceProvider,
@@ -36455,7 +37198,7 @@
36455
37198
  /**
36456
37199
  * @type {string} `version` from `package.json`
36457
37200
  */
36458
- this.version = "0.12.0"; //inserted via rollup plugin
37201
+ this.version = "0.13.0"; //inserted via rollup plugin
36459
37202
 
36460
37203
  /** @type {!Array<string|any>} */
36461
37204
  this.bootsrappedModules = [];
@@ -36591,7 +37334,7 @@
36591
37334
  * function that will be invoked by the injector as a `config` block.
36592
37335
  * See: {@link angular.module modules}
36593
37336
  * @param {import("./interface.ts").AngularBootstrapConfig} [config]
36594
- * @returns {import('./core/di/internal-injector.js').InjectorService} The created injector instance for this application.
37337
+ * @returns {ng.InjectorService} The created injector instance for this application.
36595
37338
  */
36596
37339
  bootstrap(element, modules, config) {
36597
37340
  config = config || {