@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:35 */
1
+ /* Version: 0.13.0 - December 2, 2025 01:18:46 */
2
2
  const VALID_CLASS = "ng-valid";
3
3
  const INVALID_CLASS = "ng-invalid";
4
4
  const PRISTINE_CLASS = "ng-pristine";
@@ -22,6 +22,9 @@ const ALIASED_ATTR = {
22
22
  };
23
23
 
24
24
  const isProxySymbol = Symbol("isProxy");
25
+ const BADARG = "badarg";
26
+ const BADARGKEY = "badarg: key";
27
+ const BADARGVALUE = "badarg: value";
25
28
 
26
29
  /**
27
30
  *
@@ -1754,7 +1757,7 @@ function elementAcceptsData(node) {
1754
1757
  * @returns {void}
1755
1758
  */
1756
1759
  function dealoc(element, onlyDescendants) {
1757
- if (!element) return;
1760
+ if (!element || element instanceof Comment) return;
1758
1761
  if (Array.isArray(element)) {
1759
1762
  element.forEach((x) => dealoc(x, onlyDescendants));
1760
1763
  } else {
@@ -1893,7 +1896,7 @@ function getScope(element) {
1893
1896
  * Set scope for a given element.
1894
1897
  *
1895
1898
  * @param {Element|Node|ChildNode} element - The DOM element to set data on.
1896
- * @param {import("../core/scope/scope.js").Scope} scope - The Scope attached to this element
1899
+ * @param {ng.Scope} scope - The Scope attached to this element
1897
1900
  */
1898
1901
  function setScope(element, scope) {
1899
1902
  return setCacheData(element, SCOPE_KEY, scope);
@@ -1903,7 +1906,7 @@ function setScope(element, scope) {
1903
1906
  * Set isolate scope for a given element.
1904
1907
  *
1905
1908
  * @param {Element} element - The DOM element to set data on.
1906
- * @param {import("../core/scope/scope.js").Scope} scope - The Scope attached to this element
1909
+ * @param {ng.Scope} scope - The Scope attached to this element
1907
1910
  */
1908
1911
  function setIsolateScope(element, scope) {
1909
1912
  return setCacheData(element, ISOLATE_SCOPE_KEY, scope);
@@ -1914,7 +1917,7 @@ function setIsolateScope(element, scope) {
1914
1917
  *
1915
1918
  * @param {Element} element - The DOM element to get data from.
1916
1919
  * @param {string} [name] - Controller name.
1917
- * @returns {import("../core/scope/scope.js").Scope|undefined} - The retrieved data
1920
+ * @returns {ng.Scope|undefined} - The retrieved data
1918
1921
  */
1919
1922
  function getController(element, name) {
1920
1923
  return getInheritedData(element, `$${name || "ngController"}Controller`);
@@ -2203,6 +2206,7 @@ const $injectTokens = Object.freeze({
2203
2206
  $animateCss: "$animateCss",
2204
2207
  $aria: "$aria",
2205
2208
  $compile: "$compile",
2209
+ $cookie: "$cookie",
2206
2210
  $controller: "$controller",
2207
2211
  $document: "$document",
2208
2212
  $eventBus: "$eventBus",
@@ -2215,6 +2219,7 @@ const $injectTokens = Object.freeze({
2215
2219
  $log: "$log",
2216
2220
  $viewScroll: "$viewScroll",
2217
2221
  $parse: "$parse",
2222
+ $rest: "$rest",
2218
2223
  $rootScope: "$rootScope",
2219
2224
  $rootElement: "$rootElement",
2220
2225
  $router: "$router",
@@ -3011,6 +3016,8 @@ class NgModule {
3011
3016
  this.services = [];
3012
3017
 
3013
3018
  this.wasmModules = [];
3019
+
3020
+ this.restDefinitions = [];
3014
3021
  }
3015
3022
 
3016
3023
  /**
@@ -3260,31 +3267,50 @@ class NgModule {
3260
3267
  /**
3261
3268
  * @param {string} name
3262
3269
  * @param {Function} ctor
3270
+ * @param {ng.StorageType} type
3271
+ * @param {ng.StorageBackend} [backendOrConfig]
3263
3272
  * @returns {NgModule}
3264
3273
  */
3265
- local(name, ctor) {
3274
+ store(name, ctor, type, backendOrConfig) {
3266
3275
  if (ctor && isFunction(ctor)) {
3267
3276
  ctor["$$moduleName"] = name;
3268
3277
  }
3269
- this.invokeQueue.push([$injectTokens.$provide, "local", [name, ctor]]);
3278
+ this.invokeQueue.push([
3279
+ $injectTokens.$provide,
3280
+ "store",
3281
+ [name, ctor, type, backendOrConfig],
3282
+ ]);
3270
3283
  return this;
3271
3284
  }
3272
3285
 
3273
3286
  /**
3274
- * @param {string} name
3275
- * @param {Function} ctor
3276
- * @param {ng.StorageBackend} backendOrConfig
3287
+ * @template T, ID
3288
+ * Register a REST resource during module configuration.
3289
+ * @param {string} name - Service name
3290
+ * @param {string} url - Base URL or URI template
3291
+ * @param {ng.EntityClass<T>} entityClass - Optional constructor for mapping JSON
3292
+ * @param {Object=} options - Optional RestService options (interceptors, etc)
3277
3293
  * @returns {NgModule}
3278
3294
  */
3279
- store(name, ctor, backendOrConfig) {
3280
- if (ctor && isFunction(ctor)) {
3281
- ctor["$$moduleName"] = name;
3282
- }
3295
+ rest(name, url, entityClass, options = {}) {
3296
+ const def = { name, url, entityClass, options };
3297
+ this.restDefinitions.push(def);
3298
+
3299
+ // push provider/factory to invokeQueue
3283
3300
  this.invokeQueue.push([
3284
3301
  $injectTokens.$provide,
3285
- "store",
3286
- [name, ctor, backendOrConfig],
3302
+ "factory",
3303
+ [
3304
+ name,
3305
+ [
3306
+ $injectTokens.$rest,
3307
+ /** @param {(baseUrl:string, entityClass?:Function, options?:object) => ng.RestService<T, ID>} $rest */ (
3308
+ $rest,
3309
+ ) => $rest(url, entityClass, options),
3310
+ ],
3311
+ ],
3287
3312
  ]);
3313
+
3288
3314
  return this;
3289
3315
  }
3290
3316
  }
@@ -3607,8 +3633,8 @@ function createPersistentProxy(target, key, storage, options = {}) {
3607
3633
  obj[prop] = value;
3608
3634
  try {
3609
3635
  storage.setItem(key, serialize(obj));
3610
- } catch {
3611
- console.warn(`Failed to persist data for key "${key}"`);
3636
+ } catch (e) {
3637
+ console.warn(`Failed to persist data for key "${e}"`);
3612
3638
  }
3613
3639
  return true;
3614
3640
  },
@@ -3646,9 +3672,7 @@ function createInjector(modulesToLoad, strictDi = false) {
3646
3672
  service: supportObject(service),
3647
3673
  value: supportObject(value),
3648
3674
  constant: supportObject(constant),
3649
- session: supportObject(session),
3650
- local: supportObject(local),
3651
- store: supportObject(store),
3675
+ store,
3652
3676
  decorator,
3653
3677
  },
3654
3678
  };
@@ -3783,66 +3807,80 @@ function createInjector(modulesToLoad, strictDi = false) {
3783
3807
  }
3784
3808
 
3785
3809
  /**
3786
- * Registers a session-persistent service
3787
- */
3788
- function session(name, ctor) {
3789
- return provider(name, {
3790
- $get: ($injector) => {
3791
- const instance = $injector.instantiate(ctor);
3792
- return createPersistentProxy(instance, name, sessionStorage);
3793
- },
3794
- });
3795
- }
3796
-
3797
- /**
3798
- * Registers a localStorage-persistent service
3799
- */
3800
- function local(name, ctor) {
3801
- return provider(name, {
3802
- $get: ($injector) => {
3803
- const instance = $injector.instantiate(ctor);
3804
- return createPersistentProxy(instance, name, localStorage);
3805
- },
3806
- });
3807
- }
3808
-
3809
- /**
3810
- * Registers a service persisted in a custom storage
3810
+ * Registers a service persisted in a storage
3811
3811
  *
3812
3812
  * @param {string} name - Service name
3813
3813
  * @param {Function} ctor - Constructor for the service
3814
+ * @param {ng.StorageType} type - Type of storage to be instantiated
3814
3815
  * @param {Storage|Object} backendOrConfig - Either a Storage-like object (getItem/setItem) or a config object
3815
3816
  * with { backend, serialize, deserialize }
3816
3817
  */
3817
- function store(name, ctor, backendOrConfig) {
3818
+ function store(name, ctor, type, backendOrConfig = {}) {
3818
3819
  return provider(name, {
3819
- $get: ($injector) => {
3820
- const instance = $injector.instantiate(ctor);
3821
-
3822
- let backend;
3823
- let serialize = JSON.stringify;
3824
- let deserialize = JSON.parse;
3825
-
3826
- if (backendOrConfig) {
3827
- if (typeof backendOrConfig.getItem === "function") {
3828
- // raw Storage object
3829
- backend = backendOrConfig;
3830
- } else if (isObject(backendOrConfig)) {
3831
- backend = backendOrConfig.backend || localStorage;
3832
- if (backendOrConfig.serialize)
3833
- serialize = backendOrConfig.serialize;
3834
- if (backendOrConfig.deserialize)
3835
- deserialize = backendOrConfig.deserialize;
3820
+ $get: /** @param {ng.InjectorService} $injector */ ($injector) => {
3821
+ switch (type) {
3822
+ case "session": {
3823
+ const instance = $injector.instantiate(ctor);
3824
+ return createPersistentProxy(instance, name, sessionStorage);
3825
+ }
3826
+ case "local": {
3827
+ const instance = $injector.instantiate(ctor);
3828
+ return createPersistentProxy(instance, name, localStorage);
3829
+ }
3830
+ case "cookie": {
3831
+ const instance = $injector.instantiate(ctor);
3832
+ const $cookie = $injector.get($injectTokens.$cookie);
3833
+ const serialize = backendOrConfig.serialize ?? JSON.stringify;
3834
+ const deserialize = backendOrConfig.deserialize ?? JSON.parse;
3835
+ const cookieOpts = backendOrConfig.cookie ?? {};
3836
+
3837
+ return createPersistentProxy(instance, name, {
3838
+ getItem(key) {
3839
+ const raw = $cookie.get(key);
3840
+ return raw == null ? null : raw;
3841
+ },
3842
+
3843
+ setItem(key, value) {
3844
+ $cookie.put(key, value, cookieOpts);
3845
+ },
3846
+
3847
+ removeItem(key) {
3848
+ $cookie.remove(key, cookieOpts);
3849
+ },
3850
+
3851
+ serialize,
3852
+ deserialize,
3853
+ });
3836
3854
  }
3837
- } else {
3838
- // fallback default
3839
- backend = localStorage;
3840
- }
3855
+ case "custom": {
3856
+ const instance = $injector.instantiate(ctor);
3857
+
3858
+ let backend;
3859
+ let serialize = JSON.stringify;
3860
+ let deserialize = JSON.parse;
3861
+
3862
+ if (backendOrConfig) {
3863
+ if (typeof backendOrConfig.getItem === "function") {
3864
+ // raw Storage object
3865
+ backend = backendOrConfig;
3866
+ } else if (isObject(backendOrConfig)) {
3867
+ backend = backendOrConfig.backend || localStorage;
3868
+ if (backendOrConfig.serialize)
3869
+ serialize = backendOrConfig.serialize;
3870
+ if (backendOrConfig.deserialize)
3871
+ deserialize = backendOrConfig.deserialize;
3872
+ }
3873
+ } else {
3874
+ // fallback default
3875
+ backend = localStorage;
3876
+ }
3841
3877
 
3842
- return createPersistentProxy(instance, name, backend, {
3843
- serialize,
3844
- deserialize,
3845
- });
3878
+ return createPersistentProxy(instance, name, backend, {
3879
+ serialize,
3880
+ deserialize,
3881
+ });
3882
+ }
3883
+ }
3846
3884
  },
3847
3885
  });
3848
3886
  }
@@ -5707,7 +5745,7 @@ class CompileProvider {
5707
5745
  const bindingCache = Object.create(null);
5708
5746
 
5709
5747
  /**
5710
- * @param {import("../scope/scope.js").Scope} scope
5748
+ * @param {ng.Scope} scope
5711
5749
  * @param {string} directiveName
5712
5750
  * @param {boolean} isController
5713
5751
  * @returns {Object} a configuration object for attribute bindings
@@ -5845,8 +5883,8 @@ class CompileProvider {
5845
5883
  "$injector",
5846
5884
  "$exceptionHandler",
5847
5885
  /**
5848
- * @param {import("../../core/di/internal-injector.js").InjectorService} $injector
5849
- * @param {import('../../services/exception/exception-handler.js').ErrorHandler} $exceptionHandler
5886
+ * @param {ng.InjectorService} $injector
5887
+ * @param {ng.ExceptionHandlerService} $exceptionHandler
5850
5888
  */
5851
5889
  function ($injector, $exceptionHandler) {
5852
5890
  const directives = [];
@@ -6174,11 +6212,11 @@ class CompileProvider {
6174
6212
  /**
6175
6213
  * @param {ng.InjectorService} $injector
6176
6214
  * @param {*} $interpolate
6177
- * @param {import("../../services/exception/exception-handler.js").ErrorHandler} $exceptionHandler
6215
+ * @param {ng.ExceptionHandlerService} $exceptionHandler
6178
6216
  * @param {ng.TemplateRequestService} $templateRequest
6179
6217
  * @param {ng.ParseService} $parse
6180
6218
  * @param {*} $controller
6181
- * @param {import('../scope/scope.js').Scope} $rootScope
6219
+ * @param {ng.Scope} $rootScope
6182
6220
  * @param {*} $sce
6183
6221
  * @param {ng.AnimateService} $animate
6184
6222
  * @returns
@@ -6465,7 +6503,7 @@ class CompileProvider {
6465
6503
  /**
6466
6504
  * The composite link function links all the individual nodes
6467
6505
  *
6468
- * @param {import("../scope/scope.js").Scope} scope
6506
+ * @param {ng.Scope} scope
6469
6507
  * @param {NodeRef} nodeRef
6470
6508
  * @param {*} [parentBoundTranscludeFn]
6471
6509
  */
@@ -7025,7 +7063,7 @@ class CompileProvider {
7025
7063
  transcludeFn,
7026
7064
  );
7027
7065
  } catch (e) {
7028
- $exceptionHandler(e, startingTag($element.getAny()));
7066
+ $exceptionHandler(e);
7029
7067
  }
7030
7068
  }
7031
7069
 
@@ -7078,7 +7116,7 @@ class CompileProvider {
7078
7116
  transcludeFn,
7079
7117
  );
7080
7118
  } catch (e) {
7081
- $exceptionHandler(e, startingTag($element.getAny()));
7119
+ $exceptionHandler(e);
7082
7120
  }
7083
7121
  }
7084
7122
 
@@ -7543,7 +7581,7 @@ class CompileProvider {
7543
7581
  );
7544
7582
  }
7545
7583
  } catch (e) {
7546
- $exceptionHandler(e, startingTag(compileNodeRef.getAny()));
7584
+ $exceptionHandler(e);
7547
7585
  }
7548
7586
  }
7549
7587
 
@@ -8494,9 +8532,9 @@ class CompileProvider {
8494
8532
  // Set up $watches for isolate scope and controller bindings.
8495
8533
  /**
8496
8534
  *
8497
- * @param {import('../scope/scope.js').Scope} scope
8535
+ * @param {ng.Scope} scope
8498
8536
  * @param {*} attrs
8499
- * @param {import('../scope/scope.js').Scope} destination - child scope or isolate scope
8537
+ * @param {ng.Scope} destination - child scope or isolate scope
8500
8538
  * @param {*} bindings
8501
8539
  * @param {*} directive
8502
8540
  * @returns
@@ -9704,11 +9742,11 @@ class NgModelController {
9704
9742
  ];
9705
9743
 
9706
9744
  /**
9707
- * @param {import('../../core/scope/scope.js').Scope} $scope
9708
- * @param {import('../../services/exception/exception-handler.js').ErrorHandler} $exceptionHandler
9709
- * @param {import('../../core/compile/attributes.js').Attributes} $attr
9745
+ * @param {ng.Scope} $scope
9746
+ * @param {ng.ExceptionHandlerService} $exceptionHandler
9747
+ * @param {ng.Attributes} $attr
9710
9748
  * @param {Element} $element
9711
- * @param {import("../../core/parse/interface.ts").ParseService} $parse
9749
+ * @param {ng.ParseService} $parse
9712
9750
  * @param {ng.AnimateService} $animate
9713
9751
  * @param {*} $interpolate
9714
9752
  */
@@ -9766,7 +9804,7 @@ class NgModelController {
9766
9804
 
9767
9805
  /**
9768
9806
  * @type {import("../../core/parse/interface.ts").CompiledExpression |
9769
- * (function(import("../../core/scope/scope.js").Scope): any)}
9807
+ * (function(ng.Scope): any)}
9770
9808
  */
9771
9809
  this.$$ngModelGet = this.$$parsedNgModel;
9772
9810
  this.$$ngModelSet = this.$$parsedNgModelAssign;
@@ -9779,7 +9817,7 @@ class NgModelController {
9779
9817
  /** @type {number} */
9780
9818
  this.$$currentValidationRunId = 0;
9781
9819
 
9782
- /** @type {import('../../core/scope/scope.js').Scope} */
9820
+ /** @type {ng.Scope} */
9783
9821
  this.$$scope = $scope; // attempt to bind to nearest controller if present
9784
9822
  this.$$attr = $attr;
9785
9823
  this.$$element = $element;
@@ -11885,13 +11923,13 @@ class SelectController {
11885
11923
 
11886
11924
  /**
11887
11925
  * @param {HTMLSelectElement} $element
11888
- * @param {import('../../core/scope/scope.js').Scope} $scope
11926
+ * @param {ng.Scope} $scope
11889
11927
  */
11890
11928
  constructor($element, $scope) {
11891
11929
  /** @type {HTMLSelectElement} */
11892
11930
  this.$element = $element;
11893
11931
 
11894
- /** @type {import('../../core/scope/scope.js').Scope} */
11932
+ /** @type {ng.Scope} */
11895
11933
  this.$scope = $scope;
11896
11934
 
11897
11935
  /** @type {Object<string, any>} */
@@ -12446,14 +12484,14 @@ function optionDirective($interpolate) {
12446
12484
  }
12447
12485
 
12448
12486
  /**
12449
- * @returns {import('../../interface.ts').Directive}
12487
+ * @returns {ng.Directive}
12450
12488
  */
12451
12489
  function ngBindDirective() {
12452
12490
  return {
12453
12491
  /**
12454
- * @param {import('../../core/scope/scope.js').Scope} scope
12492
+ * @param {ng.Scope} scope
12455
12493
  * @param {Element} element
12456
- * @param {import('../../core/compile/attributes.js').Attributes} attr
12494
+ * @param {ng.Attributes} attr
12457
12495
  */
12458
12496
  link(scope, element, attr) {
12459
12497
  scope.$watch(
@@ -12475,7 +12513,7 @@ function ngBindDirective() {
12475
12513
  function ngBindTemplateDirective() {
12476
12514
  return {
12477
12515
  /**
12478
- * @param {import('../../core/scope/scope.js').Scope} _scope
12516
+ * @param {ng.Scope} _scope
12479
12517
  * @param {Element} element
12480
12518
  * @param {import('../../core/compile/attributes.js').Attributes} attr
12481
12519
  */
@@ -12499,7 +12537,7 @@ function ngBindHtmlDirective($parse) {
12499
12537
  $parse(tAttrs["ngBindHtml"]); // checks for interpolation errors
12500
12538
  return (
12501
12539
  /**
12502
- * @param {import('../../core/scope/scope.js').Scope} scope
12540
+ * @param {ng.Scope} scope
12503
12541
  * @param {Element} element
12504
12542
  */
12505
12543
  (scope, element) => {
@@ -12870,8 +12908,8 @@ ngIncludeDirective.$inject = [
12870
12908
  * @param {ng.TemplateRequestService} $templateRequest
12871
12909
  * @param {import("../../services/anchor-scroll/anchor-scroll.js").AnchorScrollFunction} $anchorScroll
12872
12910
  * @param {ng.AnimateService} $animate
12873
- * @param {import('../../services/exception/interface.ts').ErrorHandler} $exceptionHandler
12874
- * @returns {import('../../interface.ts').Directive}
12911
+ * @param {ng.ExceptionHandlerService} $exceptionHandler
12912
+ * @returns {ng.Directive}
12875
12913
  */
12876
12914
  function ngIncludeDirective(
12877
12915
  $templateRequest,
@@ -13398,7 +13436,7 @@ function ngRepeatDirective($animate) {
13398
13436
  /**
13399
13437
  * Clone attach function
13400
13438
  * @param {Array<NodeList>} clone
13401
- * @param {import("../../core/scope/scope.js").Scope} scope
13439
+ * @param {ng.Scope} scope
13402
13440
  */
13403
13441
 
13404
13442
  (clone, scope) => {
@@ -13624,14 +13662,14 @@ ngOptionsDirective.$inject = ["$compile", "$parse"];
13624
13662
  /**
13625
13663
  *
13626
13664
  * @param {ng.CompileService} $compile
13627
- * @param {import("../../core/parse/interface.ts").ParseService} $parse
13628
- * @returns {import("../../interface.ts").Directive}
13665
+ * @param {ng.ParseService} $parse
13666
+ * @returns {ng.Directive}
13629
13667
  */
13630
13668
  function ngOptionsDirective($compile, $parse) {
13631
13669
  /**
13632
13670
  * @param {import('../../interface.ts').Expression} optionsExp
13633
13671
  * @param {HTMLSelectElement} selectElement
13634
- * @param {import('../../core/scope/scope.js').Scope} scope
13672
+ * @param {ng.Scope} scope
13635
13673
  * @returns
13636
13674
  */
13637
13675
  function parseOptionsExpression(optionsExp, selectElement, scope) {
@@ -13811,9 +13849,9 @@ function ngOptionsDirective($compile, $parse) {
13811
13849
 
13812
13850
  /**
13813
13851
  *
13814
- * @param {import("../../core/scope/scope.js").Scope} scope
13852
+ * @param {ng.Scope} scope
13815
13853
  * @param {HTMLSelectElement} selectElement
13816
- * @param {import("../../core/compile/attributes.js").Attributes} attr
13854
+ * @param {ng.Attributes} attr
13817
13855
  * @param {*} ctrls
13818
13856
  */
13819
13857
  function ngOptionsPostLink(scope, selectElement, attr, ctrls) {
@@ -14201,7 +14239,7 @@ function ngTranscludeDirective($compile) {
14201
14239
 
14202
14240
  /**
14203
14241
  * @param {NodeList | Node} clone
14204
- * @param {import("../../core/scope/scope.js").Scope} transcludedScope
14242
+ * @param {ng.Scope} transcludedScope
14205
14243
  */
14206
14244
  function ngTranscludeCloneAttachFn(clone, transcludedScope) {
14207
14245
  if (notWhitespace(clone)) {
@@ -14386,7 +14424,7 @@ const requiredDirective = [
14386
14424
  require: "?ngModel",
14387
14425
  link:
14388
14426
  /**
14389
- * @param {import("../../core/scope/scope.js").Scope} scope
14427
+ * @param {ng.Scope} scope
14390
14428
  * @param {Element} _elm
14391
14429
  * @param {import("../../core/compile/attributes.js").Attributes} attr
14392
14430
  * @param {import("../../interface.ts").NgModelController} ctrl
@@ -15315,78 +15353,66 @@ class TemplateCacheProvider {
15315
15353
  }
15316
15354
 
15317
15355
  /**
15318
- * Handles uncaught exceptions thrown in AngularTS expressions.
15319
- *
15320
- * By default, this service delegates to `$log.error()`, logging the exception to the browser console.
15321
- * You can override this behavior to provide custom exception handling—such as reporting errors
15322
- * to a backend server, or altering the log level used.
15356
+ * Unified exception handler used throughout AngularTS.
15323
15357
  *
15324
- * ## Default Behavior
15358
+ * This service receives uncaught exceptions from both synchronous and asynchronous operations.
15359
+ * Its purpose is to provide a central point through which the framework
15360
+ * processes errors.
15325
15361
  *
15326
- * Uncaught exceptions within AngularTS expressions are intercepted and passed to this service.
15327
- * The default implementation logs the error using `$log.error(exception, cause)`.
15362
+ * By default, `$exceptionHandler` simply rethrows the exception. This ensures fail-fast
15363
+ * behavior, making errors visible immediately in development and in unit tests.
15364
+ * Applications may override this service to introduce custom error handling.
15328
15365
  *
15329
- * ## Custom Implementation
15330
- *
15331
- * You can override the default `$exceptionHandler` by providing your own factory. This allows you to:
15332
- * - Log errors to a remote server
15333
- * - Change the log level (e.g., from `error` to `warn`)
15334
- * - Trigger custom error-handling workflows
15335
- *
15336
- * ### Example: Overriding `$exceptionHandler`
15366
+ * ### Example: Custom `$exceptionHandler`
15337
15367
  *
15338
15368
  * ```js
15339
15369
  * angular
15340
- * .module('exceptionOverwrite', [])
15341
- * .factory('$exceptionHandler', ['$log', 'logErrorsToBackend', function($log, logErrorsToBackend) {
15342
- * return function myExceptionHandler(exception, cause) {
15343
- * logErrorsToBackend(exception, cause);
15344
- * $log.warn(exception, cause); // Use warn instead of error
15370
+ * .module('app')
15371
+ * .factory('$exceptionHandler', function(myLogger) {
15372
+ * return function handleError(error) {
15373
+ * myLogger.capture(error);
15374
+ * // Rethrow to preserve fail-fast behavior:
15375
+ * throw error;
15345
15376
  * };
15346
- * }]);
15377
+ * });
15347
15378
  * ```
15348
- * - You may also manually invoke the exception handler:
15379
+ *
15380
+ * IMPORTANT: custom implementation should always rethrow the error as the framework assumes that `$exceptionHandler` always does the throwing.
15381
+ *
15382
+ * ### Manual Invocation
15383
+ *
15384
+ * You can invoke the exception handler directly when catching errors in your own code:
15349
15385
  *
15350
15386
  * ```js
15351
15387
  * try {
15352
- * // Some code that might throw
15353
- * } catch (e) {
15354
- * $exceptionHandler(e, 'optional context');
15388
+ * riskyOperation();
15389
+ * } catch (err) {
15390
+ * $exceptionHandler(err);
15355
15391
  * }
15356
15392
  * ```
15357
15393
  *
15358
- * @see {@link angular.ErrorHandler AngularTS ErrorHandler}
15394
+ * @see {@link ng.ExceptionHandlerService ExceptionHandlerService}
15359
15395
  */
15360
15396
 
15361
- /** @typedef {import('../log/interface.ts').LogService} LogService */
15362
-
15363
- /** @typedef {import("./interface.ts").ErrorHandler} ErrorHandler */
15364
-
15365
15397
  /**
15366
- * Provider for `$exceptionHandler` service. Delegates uncaught exceptions to `$log.error()` by default.
15367
- * Can be overridden to implement custom error-handling logic.
15398
+ * Provider for the `$exceptionHandler` service.
15399
+ *
15400
+ * The default implementation rethrows exceptions, enabling strict fail-fast behavior.
15401
+ * Applications may replace the handler via by setting `errorHandler`property or by providing their own
15402
+ * `$exceptionHandler` factory.
15368
15403
  */
15369
15404
  class ExceptionHandlerProvider {
15370
15405
  constructor() {
15371
- /** @type {LogService} */
15372
- this.log = window.console;
15373
-
15374
- /** @type {ErrorHandler} */
15375
- this.errorHandler = (exception, cause) => {
15376
- this.log.error(exception, cause);
15406
+ /** @type {ng.ExceptionHandlerService} */
15407
+ this.handler = (exception) => {
15408
+ throw exception;
15377
15409
  };
15378
-
15379
- this.$get = [
15380
- "$log",
15381
- /**
15382
- * @param {LogService} $log
15383
- * @returns {ErrorHandler}
15384
- */
15385
- ($log) => {
15386
- this.log = $log;
15387
- return this.errorHandler;
15388
- },
15389
- ];
15410
+ }
15411
+ /**
15412
+ * @returns {ng.ExceptionHandlerService}
15413
+ */
15414
+ $get() {
15415
+ return (exception) => this.handler(exception);
15390
15416
  }
15391
15417
  }
15392
15418
 
@@ -17999,7 +18025,7 @@ function addWatchDelegate(parsedExpression) {
17999
18025
 
18000
18026
  /**
18001
18027
  *
18002
- * @param {import('../scope/scope.js').Scope} scope
18028
+ * @param {ng.Scope} scope
18003
18029
  * @param {Function} listener
18004
18030
  * @param {*} objectEquality
18005
18031
  * @param {import('./interface').CompiledExpression} parsedExpression
@@ -25716,12 +25742,11 @@ function AnimationProvider() {
25716
25742
 
25717
25743
  /**
25718
25744
  * @typedef {import('./interface.ts').RafScheduler} RafScheduler
25719
- * @typedef {import('../interface.ts').ServiceProvider} ServiceProvider
25720
25745
  */
25721
25746
 
25722
25747
  /**
25723
25748
  * Service provider that creates a requestAnimationFrame-based scheduler.
25724
- * @type {ServiceProvider}
25749
+ * @type {ng.ServiceProvider}
25725
25750
  */
25726
25751
  class RafSchedulerProvider {
25727
25752
  constructor() {
@@ -33917,8 +33942,8 @@ class UrlService {
33917
33942
  $injectTokens.$rootScope,
33918
33943
  /**
33919
33944
  *
33920
- * @param {import('../../services/location/location.js').Location} $location
33921
- * @param {import('../../core/scope/scope.js').Scope} $rootScope
33945
+ * @param {ng.LocationService} $location
33946
+ * @param {ng.Scope} $rootScope
33922
33947
  * @returns {UrlService}
33923
33948
  */
33924
33949
  ($location, $rootScope) => {
@@ -36270,6 +36295,722 @@ function ngScopeDirective() {
36270
36295
  };
36271
36296
  }
36272
36297
 
36298
+ /**
36299
+ * Service provider that creates a {@link CookieService $cookie} service.
36300
+ * @type {ng.ServiceProvider}
36301
+ */
36302
+ class CookieProvider {
36303
+ constructor() {
36304
+ this.defaults = {};
36305
+ }
36306
+
36307
+ $get = [
36308
+ $injectTokens.$exceptionHandler,
36309
+ /** @param {ng.ExceptionHandlerService} $exceptionHandler */
36310
+ ($exceptionHandler) => new CookieService(this.defaults, $exceptionHandler),
36311
+ ];
36312
+ }
36313
+
36314
+ /**
36315
+ *
36316
+ * Provides high-level APIs for interacting with browser cookies:
36317
+ * - Raw get/set/remove
36318
+ * - JSON serialization helpers
36319
+ * - Global defaults supplied by $cookiesProvider
36320
+ */
36321
+ class CookieService {
36322
+ /**
36323
+ * @param {ng.CookieOptions} defaults
36324
+ * Default cookie attributes defined by `$cookiesProvider.defaults`.
36325
+ * @param {ng.ExceptionHandlerService} $exceptionHandler
36326
+ */
36327
+ constructor(defaults, $exceptionHandler) {
36328
+ assert(isObject(defaults), BADARG);
36329
+ assert(isFunction($exceptionHandler), BADARG);
36330
+ /** @type {ng.CookieOptions} */
36331
+ this.defaults = Object.freeze({ ...defaults });
36332
+ this.$exceptionHandler = $exceptionHandler;
36333
+ }
36334
+
36335
+ /**
36336
+ * Retrieves a raw cookie value.
36337
+ *
36338
+ * @param {string} key
36339
+ * @returns {string|null}
36340
+ */
36341
+ get(key) {
36342
+ assert(isString(key), BADARG);
36343
+ const all = parseCookies();
36344
+ return all[key] || null;
36345
+ }
36346
+
36347
+ /**
36348
+ * Retrieves a cookie and deserializes its JSON content.
36349
+ *
36350
+ * @template T
36351
+ * @param {string} key
36352
+ * @returns {T|null}
36353
+ * @throws {SyntaxError} if cookie JSON is invalid
36354
+ */
36355
+ getObject(key) {
36356
+ assert(isString(key), BADARG);
36357
+ const raw = this.get(key);
36358
+ if (!raw) return null;
36359
+ try {
36360
+ return /** @type {T} */ (JSON.parse(raw));
36361
+ } catch (err) {
36362
+ this.$exceptionHandler(
36363
+ new SyntaxError(`badparse: "${key}" => ${err.message}`),
36364
+ );
36365
+ }
36366
+ }
36367
+
36368
+ /**
36369
+ * Returns an object containing all raw cookies.
36370
+ *
36371
+ * @returns {Record<string, string>}
36372
+ */
36373
+ getAll() {
36374
+ return parseCookies();
36375
+ }
36376
+
36377
+ /**
36378
+ * Sets a raw cookie value.
36379
+ *
36380
+ * @param {string} key
36381
+ * @param {string} value
36382
+ * @param {ng.CookieOptions} [options]
36383
+ */
36384
+ put(key, value, options = {}) {
36385
+ assert(isString(key), BADARGKEY);
36386
+ assert(isString(value), BADARGVALUE);
36387
+ const encodedKey = encodeURIComponent(key);
36388
+ const encodedVal = encodeURIComponent(value);
36389
+
36390
+ try {
36391
+ document.cookie =
36392
+ `${encodedKey}=${encodedVal}` +
36393
+ buildOptions({ ...this.defaults, ...options });
36394
+ } catch (e) {
36395
+ this.$exceptionHandler(e);
36396
+ }
36397
+ }
36398
+
36399
+ /**
36400
+ * Serializes an object as JSON and stores it as a cookie.
36401
+ *
36402
+ * @param {string} key
36403
+ * @param {any} value
36404
+ * @param {ng.CookieOptions} [options]
36405
+ * @throws {TypeError} if Object cannot be converted to JSON
36406
+ */
36407
+ putObject(key, value, options) {
36408
+ assert(isString(key), BADARGKEY);
36409
+ assert(!isNullOrUndefined(value), BADARGVALUE);
36410
+ try {
36411
+ const str = JSON.stringify(value);
36412
+ this.put(key, str, options);
36413
+ } catch (err) {
36414
+ this.$exceptionHandler(
36415
+ new TypeError(`badserialize: "${key}" => ${err.message}`),
36416
+ );
36417
+ }
36418
+ }
36419
+
36420
+ /**
36421
+ * Removes a cookie by setting an expired date.
36422
+ *
36423
+ * @param {string} key
36424
+ * @param {ng.CookieOptions} [options]
36425
+ */
36426
+ remove(key, options = {}) {
36427
+ assert(isString(key), BADARG);
36428
+ this.put(key, "", {
36429
+ ...this.defaults,
36430
+ ...options,
36431
+ expires: new Date(0),
36432
+ });
36433
+ }
36434
+ }
36435
+
36436
+ /*----------Helpers----------*/
36437
+
36438
+ /**
36439
+ * @returns {Record<string,string>}
36440
+ */
36441
+ function parseCookies() {
36442
+ /** @type {Record<string, string>} */
36443
+ const out = {};
36444
+ if (!document.cookie) return out;
36445
+
36446
+ const parts = document.cookie.split("; ");
36447
+ for (const part of parts) {
36448
+ const eq = part.indexOf("=");
36449
+ if (eq === -1) continue; // skip malformed cookie
36450
+ const key = decodeURIComponent(part.substring(0, eq));
36451
+ const val = decodeURIComponent(part.substring(eq + 1));
36452
+ out[key] = val;
36453
+ }
36454
+ return out;
36455
+ }
36456
+
36457
+ /**
36458
+ * Build cookie options string from an options object.
36459
+ * Safely validates types for path, domain, expires, secure, and samesite.
36460
+ *
36461
+ * @param {ng.CookieOptions} opts
36462
+ * @returns {string}
36463
+ * @throws {TypeError} if any of options are invalid
36464
+ */
36465
+ function buildOptions(opts = {}) {
36466
+ const parts = [];
36467
+
36468
+ // Path
36469
+ if (isDefined(opts.path)) {
36470
+ if (!isString(opts.path))
36471
+ throw new TypeError(BADARG + `:path ${opts.path}`);
36472
+ parts.push(`path=${opts.path}`);
36473
+ }
36474
+
36475
+ // Domain
36476
+ if (isDefined(opts.domain)) {
36477
+ if (!isString(opts.domain))
36478
+ throw new TypeError(BADARG + `:domain ${opts.domain}`);
36479
+ parts.push(`domain=${opts.domain}`);
36480
+ }
36481
+
36482
+ // Expires
36483
+ if (opts.expires != null) {
36484
+ let expDate;
36485
+
36486
+ if (opts.expires instanceof Date) {
36487
+ expDate = opts.expires;
36488
+ } else if (isNumber(opts.expires) || isString(opts.expires)) {
36489
+ expDate = new Date(opts.expires);
36490
+ } else {
36491
+ throw new TypeError(BADARG + `:expires ${String(opts.expires)}`);
36492
+ }
36493
+
36494
+ if (isNaN(expDate.getTime())) {
36495
+ throw new TypeError(BADARG + `:expires ${String(opts.expires)}`);
36496
+ }
36497
+
36498
+ parts.push(`expires=${expDate.toUTCString()}`);
36499
+ }
36500
+
36501
+ // Secure
36502
+ if (opts.secure) {
36503
+ parts.push("secure");
36504
+ }
36505
+
36506
+ // SameSite
36507
+ if (isDefined(opts.samesite)) {
36508
+ if (!isString(opts.samesite))
36509
+ throw new TypeError(BADARG + `:samesite ${opts.samesite}`);
36510
+ const s = opts.samesite.toLowerCase();
36511
+ if (!["lax", "strict", "none"].includes(s)) {
36512
+ throw new TypeError(BADARG + `:samesite ${opts.samesite}`);
36513
+ }
36514
+ parts.push(`samesite=${s}`);
36515
+ }
36516
+
36517
+ // Join all parts with semicolons
36518
+ return parts.length ? ";" + parts.join(";") : "";
36519
+ }
36520
+
36521
+ /**
36522
+ * RFC 6570 Level 4 URI Template expander
36523
+ *
36524
+ * Supports operators: (none), +, #, ., /, ;, ?, &
36525
+ * Supports varspec modifiers: explode (*) and prefix (:len)
36526
+ *
36527
+ * Usage:
36528
+ * expandUriTemplate("/users/{id}", { id: 10 }) === "/users/10"
36529
+ * expandUriTemplate("/search{?q,lang}", { q: "a b", lang: "en" }) === "/search?q=a%20b&lang=en"
36530
+ * expandUriTemplate("/repos/{owner}/{repo}/issues{?labels*}", { labels: ["bug","ui"] }) === "/repos/x/y/issues?labels=bug&labels=ui"
36531
+ *
36532
+ * @param {string} template
36533
+ * @param {Object<string, any>} vars
36534
+ * @returns {string}
36535
+ */
36536
+ function expandUriTemplate(template, vars = {}) {
36537
+ if (typeof template !== "string")
36538
+ throw new TypeError("template must be a string");
36539
+
36540
+ return template.replace(/\{([^}]+)\}/g, (match, expression) => {
36541
+ return expandExpression(expression, vars);
36542
+ });
36543
+ }
36544
+
36545
+ /**
36546
+ * Helper: percent-encode a string. If allowReserved true, reserved chars are NOT encoded.
36547
+ * @param {string} str
36548
+ * @param {boolean} allowReserved
36549
+ * @returns {string}
36550
+ */
36551
+ function pctEncode(str, allowReserved) {
36552
+ // encodeURIComponent, then restore reserved if allowed
36553
+ const encoded = encodeURIComponent(String(str));
36554
+ if (allowReserved) {
36555
+ // Reserved characters per RFC 3986
36556
+ return encoded.replace(
36557
+ /(%3A|%2F|%3F|%23|%5B|%5D|%40|%21|%24|%26|%27|%28|%29|%2A|%2B|%2C|%3B|%3D)/gi,
36558
+ (m) => decodeURIComponent(m),
36559
+ );
36560
+ }
36561
+ return encoded;
36562
+ }
36563
+
36564
+ /**
36565
+ * Parse and expand a single expression (content between { and }).
36566
+ * @param {string} expression
36567
+ * @param {Object<string, any>} vars
36568
+ * @returns {string}
36569
+ */
36570
+ function expandExpression(expression, vars) {
36571
+ // Operator if first char in operator set
36572
+ const operator = /^[+#./;?&]/.test(expression) ? expression[0] : "";
36573
+ const op = operator;
36574
+ const varlist = op ? expression.slice(1) : expression;
36575
+
36576
+ // operator configuration (separator, prefix, named, ifEmpty, allowReserved)
36577
+ const OP = {
36578
+ "": {
36579
+ sep: ",",
36580
+ prefix: "",
36581
+ named: false,
36582
+ ifEmpty: "",
36583
+ allowReserved: false,
36584
+ },
36585
+ "+": {
36586
+ sep: ",",
36587
+ prefix: "",
36588
+ named: false,
36589
+ ifEmpty: "",
36590
+ allowReserved: true,
36591
+ },
36592
+ "#": {
36593
+ sep: ",",
36594
+ prefix: "#",
36595
+ named: false,
36596
+ ifEmpty: "",
36597
+ allowReserved: true,
36598
+ },
36599
+ ".": {
36600
+ sep: ".",
36601
+ prefix: ".",
36602
+ named: false,
36603
+ ifEmpty: "",
36604
+ allowReserved: false,
36605
+ },
36606
+ "/": {
36607
+ sep: "/",
36608
+ prefix: "/",
36609
+ named: false,
36610
+ ifEmpty: "",
36611
+ allowReserved: false,
36612
+ },
36613
+ ";": {
36614
+ sep: ";",
36615
+ prefix: ";",
36616
+ named: true,
36617
+ ifEmpty: "",
36618
+ allowReserved: false,
36619
+ },
36620
+ "?": {
36621
+ sep: "&",
36622
+ prefix: "?",
36623
+ named: true,
36624
+ ifEmpty: "=",
36625
+ allowReserved: false,
36626
+ },
36627
+ "&": {
36628
+ sep: "&",
36629
+ prefix: "&",
36630
+ named: true,
36631
+ ifEmpty: "=",
36632
+ allowReserved: false,
36633
+ },
36634
+ };
36635
+
36636
+ const conf = OP[op];
36637
+ if (!conf) throw new Error("Unsupported operator: " + op);
36638
+
36639
+ // split varspecs by comma, preserve whitespace trimmed
36640
+ const varspecs = varlist
36641
+ .split(",")
36642
+ .map((s) => s.trim())
36643
+ .filter(Boolean);
36644
+
36645
+ const expandedParts = [];
36646
+
36647
+ for (const spec of varspecs) {
36648
+ // parse varspec: name, explode (*), prefix (:len)
36649
+ const m = /^([A-Za-z0-9_.]+)(\*|(?::(\d+)))?$/.exec(spec);
36650
+ if (!m) throw new Error("Invalid varspec: " + spec);
36651
+ const varname = m[1];
36652
+ const explode = m[2] === "*";
36653
+ const prefixLength = m[3] ? parseInt(m[3], 10) : undefined;
36654
+
36655
+ const value = vars[varname];
36656
+
36657
+ // undefined or null = skip (no expansion)
36658
+ if (value === undefined || value === null) {
36659
+ continue;
36660
+ }
36661
+
36662
+ // PROCESS arrays
36663
+ if (Array.isArray(value)) {
36664
+ if (value.length === 0) {
36665
+ // empty array: for named operators, emit key with empty ifEmpty, otherwise skip
36666
+ if (conf.named) {
36667
+ // emit key without value or with = depending on ifEmpty
36668
+ if (conf.ifEmpty === "=") {
36669
+ expandedParts.push(
36670
+ `${pctEncode(varname, conf.allowReserved)}${conf.ifEmpty}`,
36671
+ );
36672
+ } else {
36673
+ expandedParts.push(pctEncode(varname, conf.allowReserved));
36674
+ }
36675
+ }
36676
+ continue;
36677
+ }
36678
+
36679
+ if (explode) {
36680
+ // each item becomes either 'k=v' (named) or 'v' (unnamed)
36681
+ for (const item of value) {
36682
+ if (item === null || item === undefined) continue;
36683
+ if (conf.named) {
36684
+ expandedParts.push(
36685
+ `${pctEncode(varname, conf.allowReserved)}=${pctEncode(item, conf.allowReserved)}`,
36686
+ );
36687
+ } else {
36688
+ expandedParts.push(pctEncode(item, conf.allowReserved));
36689
+ }
36690
+ }
36691
+ } else {
36692
+ // join by comma (or operator.sep?) — RFC: simple join with ','
36693
+ const joined = value
36694
+ .filter((v) => v !== null && v !== undefined)
36695
+ .map((v) => pctEncode(v, conf.allowReserved))
36696
+ .join(",");
36697
+ if (conf.named) {
36698
+ if (joined === "") {
36699
+ expandedParts.push(
36700
+ pctEncode(varname, conf.allowReserved) +
36701
+ (conf.ifEmpty === "=" ? conf.ifEmpty : ""),
36702
+ );
36703
+ } else {
36704
+ expandedParts.push(
36705
+ `${pctEncode(varname, conf.allowReserved)}=${joined}`,
36706
+ );
36707
+ }
36708
+ } else {
36709
+ expandedParts.push(joined);
36710
+ }
36711
+ }
36712
+ continue;
36713
+ }
36714
+
36715
+ // PROCESS objects (associative arrays)
36716
+ if (typeof value === "object") {
36717
+ const keys = Object.keys(value);
36718
+ if (keys.length === 0) {
36719
+ if (conf.named) {
36720
+ expandedParts.push(
36721
+ pctEncode(varname, conf.allowReserved) +
36722
+ (conf.ifEmpty === "=" ? conf.ifEmpty : ""),
36723
+ );
36724
+ }
36725
+ continue;
36726
+ }
36727
+
36728
+ if (explode) {
36729
+ // each key/value pair becomes k=v (named) or k,v? For explode + named, RFC says 'k=v'
36730
+ for (const k of keys) {
36731
+ const v = value[k];
36732
+ if (v === null || v === undefined) continue;
36733
+ if (conf.named) {
36734
+ expandedParts.push(
36735
+ `${pctEncode(k, conf.allowReserved)}=${pctEncode(v, conf.allowReserved)}`,
36736
+ );
36737
+ } else {
36738
+ // unnamed explode => k,v form pairs
36739
+ expandedParts.push(
36740
+ `${pctEncode(k, conf.allowReserved)}=${pctEncode(v, conf.allowReserved)}`,
36741
+ );
36742
+ }
36743
+ }
36744
+ } else {
36745
+ // not exploded: join k,v pairs by ','
36746
+ const pairs = keys
36747
+ .map(
36748
+ (k) =>
36749
+ `${pctEncode(k, conf.allowReserved)},${pctEncode(value[k], conf.allowReserved)}`,
36750
+ )
36751
+ .join(",");
36752
+ if (conf.named) {
36753
+ if (pairs === "") {
36754
+ expandedParts.push(
36755
+ pctEncode(varname, conf.allowReserved) +
36756
+ (conf.ifEmpty === "=" ? conf.ifEmpty : ""),
36757
+ );
36758
+ } else {
36759
+ expandedParts.push(
36760
+ `${pctEncode(varname, conf.allowReserved)}=${pairs}`,
36761
+ );
36762
+ }
36763
+ } else {
36764
+ expandedParts.push(pairs);
36765
+ }
36766
+ }
36767
+ continue;
36768
+ }
36769
+
36770
+ // PROCESS scalar (string/number/boolean)
36771
+ let str = String(value);
36772
+
36773
+ // apply prefix modifier if present
36774
+ if (typeof prefixLength === "number") {
36775
+ str = str.substring(0, prefixLength);
36776
+ }
36777
+
36778
+ // empty string handling
36779
+ if (str === "") {
36780
+ if (conf.named) {
36781
+ // for named operators, emit key or key= depending on ifEmpty
36782
+ if (conf.ifEmpty === "=") {
36783
+ expandedParts.push(
36784
+ `${pctEncode(varname, conf.allowReserved)}${conf.ifEmpty}`,
36785
+ );
36786
+ } else {
36787
+ expandedParts.push(pctEncode(varname, conf.allowReserved));
36788
+ }
36789
+ } else {
36790
+ // unnamed operators: empty string -> nothing (skip)
36791
+ if (op === "+" || op === "#") {
36792
+ // these allow empty expansions (produce nothing)
36793
+ expandedParts.push(pctEncode(str, conf.allowReserved));
36794
+ } else {
36795
+ // skip adding anything
36796
+ expandedParts.push(pctEncode(str, conf.allowReserved));
36797
+ }
36798
+ }
36799
+ continue;
36800
+ }
36801
+
36802
+ // default scalar behavior
36803
+ if (conf.named) {
36804
+ expandedParts.push(
36805
+ `${pctEncode(varname, conf.allowReserved)}=${pctEncode(str, conf.allowReserved)}`,
36806
+ );
36807
+ } else {
36808
+ expandedParts.push(pctEncode(str, conf.allowReserved));
36809
+ }
36810
+ } // end for varspecs
36811
+
36812
+ if (expandedParts.length === 0) return "";
36813
+
36814
+ // join parts with operator separator; prefix if needed
36815
+ return conf.prefix + expandedParts.join(conf.sep);
36816
+ }
36817
+
36818
+ /**
36819
+ * @template T, ID
36820
+ */
36821
+ class RestService {
36822
+ static $nonscope = true;
36823
+
36824
+ /**
36825
+ * Core REST service for CRUD operations.
36826
+ * Safe, predictable, and optionally maps raw JSON to entity class instances.
36827
+ *
36828
+ * @param {ng.HttpService} $http Angular-like $http service
36829
+ * @param {string} baseUrl Base URL or URI template
36830
+ * @param {{new(data: any): T}=} entityClass Optional constructor to map JSON to objects
36831
+ * @param {Object=} options Optional settings (interceptors, headers, etc.)
36832
+ */
36833
+ constructor($http, baseUrl, entityClass, options = {}) {
36834
+ assert(isString(baseUrl) && baseUrl.length > 0, "baseUrl required");
36835
+
36836
+ /** @private */
36837
+ this.$http = $http;
36838
+ /** @private */
36839
+ this.baseUrl = baseUrl;
36840
+ /** @private */
36841
+ this.entityClass = entityClass;
36842
+ /** @private */
36843
+ this.options = options;
36844
+ }
36845
+
36846
+ /**
36847
+ * Build full URL from template and parameters
36848
+ * @param {string} template
36849
+ * @param {Record<string, any>} params
36850
+ * @returns {string}
36851
+ */
36852
+ buildUrl(template, params) {
36853
+ // Safe: ensure params is an object
36854
+ return expandUriTemplate(template, params || {});
36855
+ }
36856
+
36857
+ /**
36858
+ * Map raw JSON to entity instance or return as-is
36859
+ * @param {any} data
36860
+ * @returns {T|any}
36861
+ */
36862
+ #mapEntity(data) {
36863
+ if (!data) return data;
36864
+ return this.entityClass ? new this.entityClass(data) : data;
36865
+ }
36866
+
36867
+ /**
36868
+ * List entities
36869
+ * @param {Record<string, any>=} params
36870
+ * @returns {Promise<T[]>}
36871
+ */
36872
+ async list(params = {}) {
36873
+ const url = this.buildUrl(this.baseUrl, params);
36874
+ const resp = await this.#request("get", url, null, params);
36875
+ if (!Array.isArray(resp.data)) return [];
36876
+ return resp.data.map((d) => this.#mapEntity(d));
36877
+ }
36878
+
36879
+ /**
36880
+ * Read single entity by ID
36881
+ * @param {ID} id
36882
+ * @param {Record<string, any>=} params
36883
+ * @returns {Promise<T|null>}
36884
+ */
36885
+ async read(id, params = {}) {
36886
+ if (id == null) return null;
36887
+ const url = this.buildUrl(`${this.baseUrl}/${id}`, params);
36888
+ try {
36889
+ const resp = await this.#request("get", url, null, params);
36890
+ return this.#mapEntity(resp.data);
36891
+ } catch {
36892
+ return null; // fail-safe
36893
+ }
36894
+ }
36895
+
36896
+ /**
36897
+ * Create a new entity
36898
+ * @param {T} item
36899
+ * @returns {Promise<T>}
36900
+ */
36901
+ async create(item) {
36902
+ assert(item != null, "item required for create");
36903
+ const resp = await this.#request("post", this.baseUrl, item);
36904
+ return this.#mapEntity(resp.data);
36905
+ }
36906
+
36907
+ /**
36908
+ * Update entity by ID
36909
+ * @param {ID} id
36910
+ * @param {Partial<T>} item
36911
+ * @returns {Promise<T|null>}
36912
+ */
36913
+ async update(id, item) {
36914
+ assert(id != null, "id required for update");
36915
+ const url = `${this.baseUrl}/${id}`;
36916
+ try {
36917
+ const resp = await this.#request("put", url, item);
36918
+ return this.#mapEntity(resp.data);
36919
+ } catch {
36920
+ return null;
36921
+ }
36922
+ }
36923
+
36924
+ /**
36925
+ * Delete entity by ID
36926
+ * @param {ID} id
36927
+ * @returns {Promise<boolean>}
36928
+ */
36929
+ async delete(id) {
36930
+ if (id == null) return false;
36931
+ const url = `${this.baseUrl}/${id}`;
36932
+ try {
36933
+ await this.#request("delete", url);
36934
+ return true;
36935
+ } catch {
36936
+ return false;
36937
+ }
36938
+ }
36939
+
36940
+ /**
36941
+ * Core HTTP request wrapper
36942
+ * @param {"get"|"post"|"put"|"delete"} method
36943
+ * @param {string} url
36944
+ * @param {any=} data
36945
+ * @param {Record<string, any>=} params
36946
+ * @returns {Promise<any>}
36947
+ */
36948
+ async #request(method, url, data = null, params = {}) {
36949
+ try {
36950
+ return await this.$http({
36951
+ method,
36952
+ url,
36953
+ data,
36954
+ params,
36955
+ ...this.options,
36956
+ });
36957
+ } catch (err) {
36958
+ console.error(`[RestService] HTTP ${method} failed for ${url}`, err);
36959
+ throw err; // propagate for caller handling
36960
+ }
36961
+ }
36962
+ }
36963
+
36964
+ /**
36965
+ * Provider for registering REST endpoints during module configuration.
36966
+ */
36967
+ class RestProvider {
36968
+ constructor() {
36969
+ /** @private @type {ng.RestDefinition<any>[]} */
36970
+ this.definitions = [];
36971
+ }
36972
+
36973
+ /**
36974
+ * Register a REST resource at config phase
36975
+ * @template T
36976
+ * @param {string} name Service name
36977
+ * @param {string} url Base URL or URI template
36978
+ * @param {{new(data:any):T}=} entityClass Optional entity constructor
36979
+ * @param {Object=} options Optional service options
36980
+ */
36981
+ rest(name, url, entityClass, options = {}) {
36982
+ this.definitions.push({ name, url, entityClass, options });
36983
+ }
36984
+
36985
+ /**
36986
+ * $get factory: returns a factory function and allows access to named services
36987
+ * @returns {(baseUrl:string, entityClass?:Function, options?:object) => RestService & { get(name:string): RestService, listNames(): string[] }}
36988
+ */
36989
+ $get = [
36990
+ $injectTokens.$http,
36991
+ ($http) => {
36992
+ const services = new Map();
36993
+
36994
+ const factory = (baseUrl, entityClass, options = {}) => {
36995
+ const svc = new RestService($http, baseUrl, entityClass, options);
36996
+ return svc;
36997
+ };
36998
+
36999
+ // create services from pre-registered definitions
37000
+ for (const def of this.definitions) {
37001
+ const svc = factory(def.url, def.entityClass, def.options);
37002
+ services.set(def.name, svc);
37003
+ }
37004
+
37005
+ // helpers to fetch named services
37006
+ factory.get = (name) => services.get(name);
37007
+ factory.listNames = () => Array.from(services.keys());
37008
+
37009
+ return factory;
37010
+ },
37011
+ ];
37012
+ }
37013
+
36273
37014
  /**
36274
37015
  * Initializes core `ng` module.
36275
37016
  * @param {ng.Angular} angular
@@ -36393,6 +37134,7 @@ function registerNgModule(angular) {
36393
37134
  $$animateCache: AnimateCacheProvider,
36394
37135
  $$animateQueue: AnimateQueueProvider,
36395
37136
  $controller: ControllerProvider,
37137
+ $cookie: CookieProvider,
36396
37138
  $exceptionHandler: ExceptionHandlerProvider,
36397
37139
  $filter: FilterProvider,
36398
37140
  $interpolate: InterpolateProvider,
@@ -36402,6 +37144,7 @@ function registerNgModule(angular) {
36402
37144
  $log: LogProvider,
36403
37145
  $parse: ParseProvider,
36404
37146
  $$rAFScheduler: RafSchedulerProvider,
37147
+ $rest: RestProvider,
36405
37148
  $rootScope: RootScopeProvider,
36406
37149
  $router: Router,
36407
37150
  $sce: SceProvider,
@@ -36449,7 +37192,7 @@ class Angular {
36449
37192
  /**
36450
37193
  * @type {string} `version` from `package.json`
36451
37194
  */
36452
- this.version = "0.12.0"; //inserted via rollup plugin
37195
+ this.version = "0.13.0"; //inserted via rollup plugin
36453
37196
 
36454
37197
  /** @type {!Array<string|any>} */
36455
37198
  this.bootsrappedModules = [];
@@ -36585,7 +37328,7 @@ class Angular {
36585
37328
  * function that will be invoked by the injector as a `config` block.
36586
37329
  * See: {@link angular.module modules}
36587
37330
  * @param {import("./interface.ts").AngularBootstrapConfig} [config]
36588
- * @returns {import('./core/di/internal-injector.js').InjectorService} The created injector instance for this application.
37331
+ * @returns {ng.InjectorService} The created injector instance for this application.
36589
37332
  */
36590
37333
  bootstrap(element, modules, config) {
36591
37334
  config = config || {