@timeback/powerpath 0.2.1 → 0.2.2-beta.20260331190459
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-5a9p0re1.js → chunk-setst1c6.js} +56 -56
- package/dist/errors.d.ts +144 -1
- package/dist/errors.js +1 -1
- package/dist/index.d.ts +1349 -1004
- package/dist/index.js +216 -78
- package/dist/public-types.d.ts +2 -1184
- package/dist/public-types.js +1 -0
- package/package.json +2 -2
- package/dist/errors.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/public-types.d.ts.map +0 -1
|
@@ -636,11 +636,11 @@ class TimebackProvider {
|
|
|
636
636
|
env;
|
|
637
637
|
auth;
|
|
638
638
|
timeout;
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
639
|
+
_endpoints;
|
|
640
|
+
_authUrl;
|
|
641
|
+
_tokenScope;
|
|
642
|
+
_pathProfiles;
|
|
643
|
+
_tokenManagers = new Map;
|
|
644
644
|
constructor(config) {
|
|
645
645
|
this.timeout = config.timeout ?? 30000;
|
|
646
646
|
if (isEnvConfig(config)) {
|
|
@@ -653,89 +653,89 @@ class TimebackProvider {
|
|
|
653
653
|
if (!platformEndpoints) {
|
|
654
654
|
throw new Error(`Unknown platform: ${platform}`);
|
|
655
655
|
}
|
|
656
|
-
this.
|
|
657
|
-
this.
|
|
658
|
-
this.
|
|
659
|
-
this.
|
|
656
|
+
this._authUrl = platformEndpoints.token[env];
|
|
657
|
+
this._tokenScope = platformEndpoints.tokenScope ?? undefined;
|
|
658
|
+
this._pathProfiles = PLATFORM_PATHS[platform] ?? BEYONDAI_PATHS;
|
|
659
|
+
this._endpoints = {
|
|
660
660
|
oneroster: {
|
|
661
661
|
baseUrl: platformEndpoints.api[env],
|
|
662
|
-
authUrl: this.
|
|
662
|
+
authUrl: this._authUrl
|
|
663
663
|
},
|
|
664
664
|
edubridge: {
|
|
665
665
|
baseUrl: platformEndpoints.api[env],
|
|
666
|
-
authUrl: this.
|
|
666
|
+
authUrl: this._authUrl
|
|
667
667
|
},
|
|
668
668
|
powerpath: {
|
|
669
669
|
baseUrl: platformEndpoints.api[env],
|
|
670
|
-
authUrl: this.
|
|
670
|
+
authUrl: this._authUrl
|
|
671
671
|
},
|
|
672
672
|
clr: {
|
|
673
673
|
baseUrl: platformEndpoints.api[env],
|
|
674
|
-
authUrl: this.
|
|
674
|
+
authUrl: this._authUrl
|
|
675
675
|
},
|
|
676
676
|
case: {
|
|
677
677
|
baseUrl: platformEndpoints.api[env],
|
|
678
|
-
authUrl: this.
|
|
678
|
+
authUrl: this._authUrl
|
|
679
679
|
},
|
|
680
680
|
caliper: {
|
|
681
681
|
baseUrl: platformEndpoints.caliper[env],
|
|
682
|
-
authUrl: this.
|
|
682
|
+
authUrl: this._authUrl
|
|
683
683
|
},
|
|
684
684
|
webhooks: {
|
|
685
685
|
baseUrl: platformEndpoints.caliper[env],
|
|
686
|
-
authUrl: this.
|
|
686
|
+
authUrl: this._authUrl
|
|
687
687
|
},
|
|
688
688
|
reporting: {
|
|
689
689
|
baseUrl: platformEndpoints.api[env],
|
|
690
|
-
authUrl: this.
|
|
690
|
+
authUrl: this._authUrl
|
|
691
691
|
},
|
|
692
692
|
qti: {
|
|
693
693
|
baseUrl: platformEndpoints.qti[env],
|
|
694
|
-
authUrl: this.
|
|
694
|
+
authUrl: this._authUrl
|
|
695
695
|
}
|
|
696
696
|
};
|
|
697
697
|
} else if (isExplicitConfig(config)) {
|
|
698
698
|
this.auth = config.auth;
|
|
699
|
-
this.
|
|
700
|
-
this.
|
|
701
|
-
this.
|
|
702
|
-
oneroster: { baseUrl: config.baseUrl, authUrl: this.
|
|
703
|
-
edubridge: { baseUrl: config.baseUrl, authUrl: this.
|
|
704
|
-
powerpath: { baseUrl: config.baseUrl, authUrl: this.
|
|
705
|
-
clr: { baseUrl: config.baseUrl, authUrl: this.
|
|
706
|
-
case: { baseUrl: config.baseUrl, authUrl: this.
|
|
707
|
-
caliper: { baseUrl: config.baseUrl, authUrl: this.
|
|
708
|
-
webhooks: { baseUrl: config.baseUrl, authUrl: this.
|
|
709
|
-
reporting: { baseUrl: config.baseUrl, authUrl: this.
|
|
710
|
-
qti: { baseUrl: config.baseUrl, authUrl: this.
|
|
699
|
+
this._authUrl = config.authUrl;
|
|
700
|
+
this._pathProfiles = resolvePathProfiles(config.pathProfile, config.paths);
|
|
701
|
+
this._endpoints = {
|
|
702
|
+
oneroster: { baseUrl: config.baseUrl, authUrl: this._authUrl },
|
|
703
|
+
edubridge: { baseUrl: config.baseUrl, authUrl: this._authUrl },
|
|
704
|
+
powerpath: { baseUrl: config.baseUrl, authUrl: this._authUrl },
|
|
705
|
+
clr: { baseUrl: config.baseUrl, authUrl: this._authUrl },
|
|
706
|
+
case: { baseUrl: config.baseUrl, authUrl: this._authUrl },
|
|
707
|
+
caliper: { baseUrl: config.baseUrl, authUrl: this._authUrl },
|
|
708
|
+
webhooks: { baseUrl: config.baseUrl, authUrl: this._authUrl },
|
|
709
|
+
reporting: { baseUrl: config.baseUrl, authUrl: this._authUrl },
|
|
710
|
+
qti: { baseUrl: config.baseUrl, authUrl: this._authUrl }
|
|
711
711
|
};
|
|
712
712
|
} else if (isServicesConfig(config)) {
|
|
713
713
|
this.auth = config.auth;
|
|
714
|
-
this.
|
|
715
|
-
this.
|
|
716
|
-
this.
|
|
714
|
+
this._authUrl = config.authUrl;
|
|
715
|
+
this._pathProfiles = resolvePathProfiles(config.pathProfile, config.paths);
|
|
716
|
+
this._endpoints = {};
|
|
717
717
|
for (const [service, baseUrl] of Object.entries(config.services)) {
|
|
718
718
|
if (baseUrl) {
|
|
719
|
-
this.
|
|
719
|
+
this._endpoints[service] = {
|
|
720
720
|
baseUrl,
|
|
721
|
-
authUrl: this.
|
|
721
|
+
authUrl: this._authUrl
|
|
722
722
|
};
|
|
723
723
|
}
|
|
724
724
|
}
|
|
725
725
|
} else {
|
|
726
726
|
throw new Error("Invalid provider configuration");
|
|
727
727
|
}
|
|
728
|
-
for (const service of Object.keys(this.
|
|
729
|
-
if (this.
|
|
730
|
-
delete this.
|
|
728
|
+
for (const service of Object.keys(this._pathProfiles)) {
|
|
729
|
+
if (this._pathProfiles[service] === null) {
|
|
730
|
+
delete this._endpoints[service];
|
|
731
731
|
}
|
|
732
732
|
}
|
|
733
733
|
}
|
|
734
734
|
getEndpoint(service) {
|
|
735
|
-
const endpoint = this.
|
|
735
|
+
const endpoint = this._endpoints[service];
|
|
736
736
|
if (!endpoint) {
|
|
737
737
|
const pathKey = service;
|
|
738
|
-
if (pathKey in this.
|
|
738
|
+
if (pathKey in this._pathProfiles && this._pathProfiles[pathKey] === null) {
|
|
739
739
|
throw new Error(`Service "${service}" is not supported on ${this.platform ?? "this"} platform.`);
|
|
740
740
|
}
|
|
741
741
|
throw new Error(`Service "${service}" is not configured in this provider`);
|
|
@@ -743,13 +743,13 @@ class TimebackProvider {
|
|
|
743
743
|
return endpoint;
|
|
744
744
|
}
|
|
745
745
|
hasService(service) {
|
|
746
|
-
return service in this.
|
|
746
|
+
return service in this._endpoints;
|
|
747
747
|
}
|
|
748
748
|
getAvailableServices() {
|
|
749
|
-
return Object.keys(this.
|
|
749
|
+
return Object.keys(this._endpoints);
|
|
750
750
|
}
|
|
751
751
|
getTokenUrl() {
|
|
752
|
-
return this.
|
|
752
|
+
return this._authUrl;
|
|
753
753
|
}
|
|
754
754
|
getEndpointWithPaths(service) {
|
|
755
755
|
const endpoint = this.getEndpoint(service);
|
|
@@ -757,17 +757,17 @@ class TimebackProvider {
|
|
|
757
757
|
return { ...endpoint, paths };
|
|
758
758
|
}
|
|
759
759
|
getPaths() {
|
|
760
|
-
return this.
|
|
760
|
+
return this._pathProfiles;
|
|
761
761
|
}
|
|
762
762
|
getServicePaths(service) {
|
|
763
|
-
const paths = this.
|
|
763
|
+
const paths = this._pathProfiles[service];
|
|
764
764
|
if (!paths) {
|
|
765
765
|
throw new Error(`Service "${service}" is not supported on ${this.platform ?? "this"} platform.`);
|
|
766
766
|
}
|
|
767
767
|
return paths;
|
|
768
768
|
}
|
|
769
769
|
hasServiceSupport(service) {
|
|
770
|
-
return this.
|
|
770
|
+
return this._pathProfiles[service] !== null;
|
|
771
771
|
}
|
|
772
772
|
getTokenProvider(service) {
|
|
773
773
|
const endpoint = this.getEndpoint(service);
|
|
@@ -778,7 +778,7 @@ class TimebackProvider {
|
|
|
778
778
|
if (!this.auth) {
|
|
779
779
|
throw new Error(`Service "${service}" requires authentication but no credentials were provided`);
|
|
780
780
|
}
|
|
781
|
-
let manager = this.
|
|
781
|
+
let manager = this._tokenManagers.get(authUrl);
|
|
782
782
|
if (!manager) {
|
|
783
783
|
manager = new TokenManager({
|
|
784
784
|
tokenUrl: authUrl,
|
|
@@ -786,28 +786,28 @@ class TimebackProvider {
|
|
|
786
786
|
clientId: this.auth.clientId,
|
|
787
787
|
clientSecret: this.auth.clientSecret
|
|
788
788
|
},
|
|
789
|
-
scope: this.
|
|
789
|
+
scope: this._tokenScope
|
|
790
790
|
});
|
|
791
|
-
this.
|
|
791
|
+
this._tokenManagers.set(authUrl, manager);
|
|
792
792
|
}
|
|
793
793
|
return manager;
|
|
794
794
|
}
|
|
795
795
|
async checkAuth() {
|
|
796
|
-
if (!this.
|
|
796
|
+
if (!this._authUrl || !this.auth) {
|
|
797
797
|
throw new Error("No auth configured on this provider");
|
|
798
798
|
}
|
|
799
799
|
const startTime = Date.now();
|
|
800
|
-
let manager = this.
|
|
800
|
+
let manager = this._tokenManagers.get(this._authUrl);
|
|
801
801
|
if (!manager) {
|
|
802
802
|
manager = new TokenManager({
|
|
803
|
-
tokenUrl: this.
|
|
803
|
+
tokenUrl: this._authUrl,
|
|
804
804
|
credentials: {
|
|
805
805
|
clientId: this.auth.clientId,
|
|
806
806
|
clientSecret: this.auth.clientSecret
|
|
807
807
|
},
|
|
808
|
-
scope: this.
|
|
808
|
+
scope: this._tokenScope
|
|
809
809
|
});
|
|
810
|
-
this.
|
|
810
|
+
this._tokenManagers.set(this._authUrl, manager);
|
|
811
811
|
}
|
|
812
812
|
try {
|
|
813
813
|
await manager.getToken();
|
|
@@ -826,10 +826,10 @@ class TimebackProvider {
|
|
|
826
826
|
}
|
|
827
827
|
}
|
|
828
828
|
invalidateTokens() {
|
|
829
|
-
for (const manager of this.
|
|
829
|
+
for (const manager of this._tokenManagers.values()) {
|
|
830
830
|
manager.invalidate?.();
|
|
831
831
|
}
|
|
832
|
-
this.
|
|
832
|
+
this._tokenManagers.clear();
|
|
833
833
|
}
|
|
834
834
|
}
|
|
835
835
|
// ../../internal/client-infra/src/utils/utils.ts
|
package/dist/errors.d.ts
CHANGED
|
@@ -1 +1,144 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Error Types
|
|
3
|
+
*
|
|
4
|
+
* Shared types for error helpers.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Validation issue from client-side input validation.
|
|
8
|
+
*/
|
|
9
|
+
interface ValidationIssue {
|
|
10
|
+
/** Field path (e.g. "sourcedId", "metadata.email") */
|
|
11
|
+
path: string;
|
|
12
|
+
/** Human-readable error message */
|
|
13
|
+
message: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* API Error Classes
|
|
18
|
+
*
|
|
19
|
+
* Base error classes for HTTP API failures.
|
|
20
|
+
* Includes IMS Global error response parsing (OneRoster, QTI, etc.).
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Base error class for all API errors.
|
|
25
|
+
*
|
|
26
|
+
* Provides access to the HTTP status code and raw response body.
|
|
27
|
+
* Includes IMS Global error parsing (minorCodes, details) for
|
|
28
|
+
* IMS-standard APIs like OneRoster and QTI.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* // Catching and inspecting errors
|
|
33
|
+
* try {
|
|
34
|
+
* await client.users.get('non-existent-id')
|
|
35
|
+
* } catch (error) {
|
|
36
|
+
* if (error instanceof ApiError) {
|
|
37
|
+
* console.log(`Error ${error.statusCode}: ${error.message}`)
|
|
38
|
+
* console.log('Minor codes:', error.minorCodes)
|
|
39
|
+
* console.log('Details:', error.details)
|
|
40
|
+
* }
|
|
41
|
+
* }
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
declare class ApiError extends Error {
|
|
45
|
+
readonly statusCode?: number | undefined;
|
|
46
|
+
readonly response?: unknown;
|
|
47
|
+
readonly name: string;
|
|
48
|
+
/**
|
|
49
|
+
* Creates a new ApiError.
|
|
50
|
+
*
|
|
51
|
+
* @param message - Human-readable error message
|
|
52
|
+
* @param statusCode - HTTP status code
|
|
53
|
+
* @param response - Raw response body (if available)
|
|
54
|
+
*/
|
|
55
|
+
constructor(message: string, statusCode?: number | undefined, response?: unknown);
|
|
56
|
+
/**
|
|
57
|
+
* Minor error codes from IMS Global error response.
|
|
58
|
+
*
|
|
59
|
+
* For IMS-standard APIs (OneRoster, QTI), provides specific error codes
|
|
60
|
+
* like "unknownobject" or "invaliddata".
|
|
61
|
+
*
|
|
62
|
+
* @returns Array of field/value pairs, or empty array if not IMS format
|
|
63
|
+
*/
|
|
64
|
+
get minorCodes(): Array<{
|
|
65
|
+
field: string;
|
|
66
|
+
value: string;
|
|
67
|
+
}>;
|
|
68
|
+
/**
|
|
69
|
+
* Additional error details from IMS Global response.
|
|
70
|
+
*
|
|
71
|
+
* May contain field-level validation errors or other structured details.
|
|
72
|
+
*
|
|
73
|
+
* @returns Array of key-value objects, or empty array if not present
|
|
74
|
+
*/
|
|
75
|
+
get details(): Array<Record<string, string>>;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Error thrown when authentication fails (HTTP 401).
|
|
79
|
+
*
|
|
80
|
+
* Typically indicates invalid or expired credentials.
|
|
81
|
+
*/
|
|
82
|
+
declare class UnauthorizedError extends ApiError {
|
|
83
|
+
readonly name = "UnauthorizedError";
|
|
84
|
+
constructor(message?: string, response?: unknown);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Error thrown when the client lacks permission for the operation (HTTP 403).
|
|
88
|
+
*
|
|
89
|
+
* The credentials are valid, but the client is not authorized for this action.
|
|
90
|
+
*/
|
|
91
|
+
declare class ForbiddenError extends ApiError {
|
|
92
|
+
readonly name = "ForbiddenError";
|
|
93
|
+
constructor(message?: string, response?: unknown);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Error thrown when a requested resource is not found (HTTP 404).
|
|
97
|
+
*/
|
|
98
|
+
declare class NotFoundError extends ApiError {
|
|
99
|
+
readonly name = "NotFoundError";
|
|
100
|
+
constructor(message?: string, response?: unknown);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Error thrown when request data is invalid (HTTP 422).
|
|
104
|
+
*
|
|
105
|
+
* Check the `details` property for field-level validation errors.
|
|
106
|
+
*/
|
|
107
|
+
declare class ValidationError extends ApiError {
|
|
108
|
+
readonly name = "ValidationError";
|
|
109
|
+
constructor(message?: string, response?: unknown);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Validation issue from client-side input validation.
|
|
113
|
+
*/
|
|
114
|
+
/**
|
|
115
|
+
* Error thrown when client-side input validation fails.
|
|
116
|
+
*
|
|
117
|
+
* This is thrown **before** making a network request, providing fast feedback
|
|
118
|
+
* with actionable, path-based error messages.
|
|
119
|
+
*
|
|
120
|
+
* Uses statusCode 400 (Bad Request) to distinguish from server-side 422 errors.
|
|
121
|
+
* Formats like IMS errors via `imsx_error_details` so existing error formatters work.
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* ```typescript
|
|
125
|
+
* try {
|
|
126
|
+
* await client.users.create({}) // missing required fields
|
|
127
|
+
* } catch (error) {
|
|
128
|
+
* if (error instanceof InputValidationError) {
|
|
129
|
+
* console.log('Invalid input:', error.issues)
|
|
130
|
+
* // [{ path: 'sourcedId', message: 'Required' }]
|
|
131
|
+
* }
|
|
132
|
+
* }
|
|
133
|
+
* ```
|
|
134
|
+
*/
|
|
135
|
+
declare class InputValidationError extends ApiError {
|
|
136
|
+
readonly name = "InputValidationError";
|
|
137
|
+
/**
|
|
138
|
+
* The validation issues that caused this error.
|
|
139
|
+
*/
|
|
140
|
+
readonly issues: ValidationIssue[];
|
|
141
|
+
constructor(message: string, issues: ValidationIssue[]);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export { ForbiddenError, InputValidationError, NotFoundError, ApiError as PowerPathError, UnauthorizedError, ValidationError };
|