@singularlogic/coreplatts 0.0.5 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -57
- package/dist/index.d.ts +2 -1
- package/dist/index.js +65 -44
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# @singularlogic/coreplatts
|
|
2
2
|
|
|
3
|
-
A TypeScript client library to access the **BUILDSPACE** identity and storage APIs.
|
|
3
|
+
A TypeScript client library to access the **BUILDSPACE** identity and storage APIs.
|
|
4
|
+
|
|
4
5
|
Built to work with [Keycloak](https://www.keycloak.org/) and [MinIO](https://min.io/), this library wraps REST endpoints and handles authentication, token refresh, organization and folder management.
|
|
5
6
|
|
|
6
7
|
---
|
|
@@ -35,19 +36,34 @@ const token = await client.login('user@example.com', 'password');
|
|
|
35
36
|
console.log('Access token:', token.access_token);
|
|
36
37
|
```
|
|
37
38
|
|
|
38
|
-
Tokens are stored in `
|
|
39
|
+
Tokens are stored in `sessionStorage` and are **automatically refreshed when expired**.
|
|
40
|
+
|
|
41
|
+
### ⚠️ For Web Applications: Use an OIDC or Keycloak Client
|
|
42
|
+
|
|
43
|
+
We **strongly recommend** using an **OpenID Connect (OIDC)** or **Keycloak client** for web applications.
|
|
44
|
+
|
|
45
|
+
**Why?**
|
|
46
|
+
|
|
47
|
+
- ✅ More secure: handles redirect flows, token validation, and silent refresh securely.
|
|
48
|
+
- 🔄 Seamless session management: avoids manual token handling and refresh logic.
|
|
49
|
+
- 🔐 Built-in CSRF and token expiration protection.
|
|
50
|
+
|
|
51
|
+
#### Great frontend OIDC libraries:
|
|
52
|
+
|
|
53
|
+
- [oidc-client-ts](https://www.npmjs.com/package/oidc-client-ts)
|
|
54
|
+
- [keycloak-js](https://www.npmjs.com/package/keycloak-js)
|
|
55
|
+
|
|
56
|
+
> The `client.login(email, password)` method is designed for **CLI tools, backend services, development, and test automation** — not for production browser-based apps.
|
|
39
57
|
|
|
40
58
|
---
|
|
41
59
|
|
|
42
60
|
## 👤 User Management
|
|
43
61
|
|
|
44
62
|
```ts
|
|
45
|
-
const me = await client.myUser();
|
|
63
|
+
const me = await client.myUser();
|
|
46
64
|
console.log(me.username);
|
|
47
65
|
```
|
|
48
66
|
|
|
49
|
-
Register new users:
|
|
50
|
-
|
|
51
67
|
```ts
|
|
52
68
|
await client.register(
|
|
53
69
|
'new@user.com',
|
|
@@ -81,71 +97,30 @@ await client.addToOrganization('MyNewOrg', {
|
|
|
81
97
|
## 📂 Folder & File Management
|
|
82
98
|
|
|
83
99
|
```ts
|
|
84
|
-
const folder = await client.getFolderByName('project-data');
|
|
85
|
-
console.log(folder.files);
|
|
86
|
-
```
|
|
87
100
|
|
|
88
|
-
|
|
101
|
+
const folder = await client.getFolderByName('project-data');
|
|
89
102
|
|
|
90
|
-
|
|
103
|
+
const folderItems = await folder.listItems();
|
|
91
104
|
|
|
92
|
-
```ts
|
|
93
105
|
const file = await folder.getFileByName('report.pdf');
|
|
94
106
|
await file.download();
|
|
95
107
|
```
|
|
96
108
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
## 🔁 Automatic Token Handling
|
|
100
|
-
|
|
101
|
-
All token-sensitive methods use a utility that:
|
|
102
|
-
- Checks if the access token is expired
|
|
103
|
-
- Refreshes it using `refresh_token` if needed
|
|
104
|
-
- Uses `localStorage` or `sessionStorage` automatically
|
|
105
|
-
|
|
106
|
-
---
|
|
107
|
-
|
|
108
|
-
## 🧪 Example in Angular Service
|
|
109
|
+
### Upload a File
|
|
109
110
|
|
|
110
111
|
```ts
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
getCurrentUser() {
|
|
120
|
-
return this.client.myUser();
|
|
121
|
-
}
|
|
122
|
-
}
|
|
112
|
+
await folder.uploadFile(
|
|
113
|
+
myFile,
|
|
114
|
+
{ title: 'dataset.csv' },
|
|
115
|
+
5 * 1024 * 1024, // chunk size
|
|
116
|
+
10, // concurrency
|
|
117
|
+
3600 // timeout
|
|
118
|
+
);
|
|
123
119
|
```
|
|
124
120
|
|
|
125
121
|
---
|
|
126
122
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
### `Client`
|
|
130
|
-
|
|
131
|
-
- Handles auth, session, and proxies to:
|
|
132
|
-
- `UserConsumer`
|
|
133
|
-
- `GroupConsumer`
|
|
134
|
-
- `BucketConsumer`
|
|
135
|
-
- `FolderConsumer`
|
|
136
|
-
|
|
137
|
-
### `Folder`
|
|
138
|
-
|
|
139
|
-
- Can fetch files
|
|
140
|
-
- Can upload files with chunking and concurrency control
|
|
141
|
-
|
|
142
|
-
### `File`
|
|
143
|
-
|
|
144
|
-
- Can be downloaded by ID or name
|
|
145
|
-
|
|
146
|
-
---
|
|
147
|
-
|
|
148
|
-
## 📘 API Summary
|
|
123
|
+
### 📘 Main Client
|
|
149
124
|
|
|
150
125
|
| Method | Description |
|
|
151
126
|
|--------|-------------|
|
|
@@ -161,6 +136,34 @@ export class AuthService {
|
|
|
161
136
|
|
|
162
137
|
---
|
|
163
138
|
|
|
139
|
+
### 📘 Folder Class
|
|
140
|
+
|
|
141
|
+
| Method | Description |
|
|
142
|
+
|--------|-------------|
|
|
143
|
+
| `listItems()` | List folder contents |
|
|
144
|
+
| `getFileByID(id)` | Retrieve file by ID |
|
|
145
|
+
| `getFileByName(name)` | Retrieve file by name |
|
|
146
|
+
| `uploadFile(...)` | Upload file with chunked multipart support |
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
### 📘 File Class
|
|
151
|
+
|
|
152
|
+
| Method | Description |
|
|
153
|
+
|--------|-------------|
|
|
154
|
+
| `download()` | Download file with chunked concurrency |
|
|
155
|
+
| `getFilename()` | Get complete file name from metadata |
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## 🔁 Automatic Token Handling
|
|
160
|
+
|
|
161
|
+
All token-sensitive methods use a utility that:
|
|
162
|
+
|
|
163
|
+
- Checks token expiration
|
|
164
|
+
- Automatically refreshes access tokens
|
|
165
|
+
- Uses `sessionStorage` only
|
|
166
|
+
|
|
164
167
|
## 📄 License
|
|
165
168
|
|
|
166
169
|
MIT © [SingularLogic S.A.](https://www.singularlogic.eu)
|
package/dist/index.d.ts
CHANGED
|
@@ -155,7 +155,8 @@ declare class Client implements IClient$1 {
|
|
|
155
155
|
private _bucketConsumer;
|
|
156
156
|
private _folderConsumer;
|
|
157
157
|
constructor(_managementURL: string, _accountURL: string);
|
|
158
|
-
|
|
158
|
+
setSession(session: OidcToken): void;
|
|
159
|
+
getSessionVariable(key: string): string | null;
|
|
159
160
|
login(email: string, password: string): Promise<OidcToken>;
|
|
160
161
|
myUser(token?: string): Promise<User$1>;
|
|
161
162
|
allOrganizations(token?: string): Promise<Group$1[]>;
|
package/dist/index.js
CHANGED
|
@@ -272,20 +272,40 @@ var BucketConsumer = class extends BaseConsumer {
|
|
|
272
272
|
function withValidToken(fn, accountsAPI) {
|
|
273
273
|
return function(...args) {
|
|
274
274
|
return __async(this, null, function* () {
|
|
275
|
-
const isStorageAvailable = typeof localStorage !== "undefined";
|
|
276
275
|
const now = Math.floor(Date.now() / 1e3);
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
276
|
+
const storage = typeof window !== "undefined" ? window.sessionStorage : globalThis.sessionStorage;
|
|
277
|
+
if (!storage) {
|
|
278
|
+
console.warn("\u26A0\uFE0F sessionStorage not available");
|
|
279
|
+
return fn.apply(this, args);
|
|
280
280
|
}
|
|
281
|
-
let token =
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
281
|
+
let token = storage.getItem("access_token");
|
|
282
|
+
let refreshToken = storage.getItem("refresh_token");
|
|
283
|
+
let exp = 0;
|
|
284
|
+
let refreshExp = 0;
|
|
285
|
+
const claimsRaw = storage.getItem("id_token_claims_obj");
|
|
286
|
+
if (claimsRaw) {
|
|
287
|
+
try {
|
|
288
|
+
const claims = JSON.parse(claimsRaw);
|
|
289
|
+
if (claims.exp) exp = claims.exp;
|
|
290
|
+
if (claims.iat && claims.exp) {
|
|
291
|
+
const duration = claims.exp - claims.iat;
|
|
292
|
+
refreshExp = claims.iat + 3 * duration;
|
|
293
|
+
}
|
|
294
|
+
} catch (e) {
|
|
295
|
+
console.warn("\u26A0\uFE0F Could not parse id_token_claims_obj:", e);
|
|
296
|
+
}
|
|
297
|
+
} else {
|
|
298
|
+
exp = Number(storage.getItem("refresh_expires_in") || "0");
|
|
299
|
+
refreshExp = Number(storage.getItem("refresh_exp") || "0");
|
|
300
|
+
}
|
|
301
|
+
if (!token || !refreshToken) {
|
|
302
|
+
console.warn("\u26A0\uFE0F No tokens found in sessionStorage.");
|
|
303
|
+
return fn.apply(this, args);
|
|
304
|
+
}
|
|
305
|
+
if (now >= exp) {
|
|
306
|
+
if (now >= refreshExp) {
|
|
307
|
+
console.warn("\u26A0\uFE0F Both access and refresh tokens expired.");
|
|
308
|
+
return fn.apply(this, args);
|
|
289
309
|
}
|
|
290
310
|
try {
|
|
291
311
|
const response = yield fetch(`${accountsAPI}/user/refresh`, {
|
|
@@ -295,20 +315,20 @@ function withValidToken(fn, accountsAPI) {
|
|
|
295
315
|
});
|
|
296
316
|
if (!response.ok) throw new Error("Token refresh failed");
|
|
297
317
|
const session = yield response.json();
|
|
318
|
+
token = session.access_token;
|
|
319
|
+
refreshToken = session.refresh_token;
|
|
298
320
|
const newExp = now + session.expires_in;
|
|
299
321
|
const newRefreshExp = now + session.refresh_expires_in;
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
console.error("\u{1F510} Token refresh failed:", e);
|
|
322
|
+
storage.setItem("access_token", token);
|
|
323
|
+
storage.setItem("refresh_token", refreshToken);
|
|
324
|
+
storage.setItem("refresh_expires_in", String(newExp));
|
|
325
|
+
storage.setItem("refresh_exp", String(newRefreshExp));
|
|
326
|
+
} catch (err) {
|
|
327
|
+
console.error("\u{1F510} Token refresh failed:", err);
|
|
307
328
|
throw new Error("Could not refresh session.");
|
|
308
329
|
}
|
|
309
330
|
}
|
|
310
|
-
|
|
311
|
-
return result instanceof Promise ? result : Promise.resolve(result);
|
|
331
|
+
return yield fn.apply(this, [...args, token]);
|
|
312
332
|
});
|
|
313
333
|
};
|
|
314
334
|
}
|
|
@@ -787,36 +807,37 @@ var Client = class {
|
|
|
787
807
|
this.getFolderByName = withValidToken(this.getFolderByName.bind(this), _accountURL);
|
|
788
808
|
this.getFolderByID = withValidToken(this.getFolderByID.bind(this), _accountURL);
|
|
789
809
|
}
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
// if (storage) {
|
|
793
|
-
// storage.setItem('token', session.access_token);
|
|
794
|
-
// storage.setItem('refresh_token', session.refresh_token);
|
|
795
|
-
// storage.setItem('exp', String(session.expires_in));
|
|
796
|
-
// storage.setItem('refresh_exp', String(session.refresh_expires_in));
|
|
797
|
-
// } else {
|
|
798
|
-
// console.warn("Session storage is not available.");
|
|
799
|
-
// }
|
|
800
|
-
// }
|
|
801
|
-
// public getSessionVariable(key: string): string | null {
|
|
802
|
-
// const storage = typeof window !== "undefined" ? sessionStorage : global.sessionStorage;
|
|
803
|
-
// return storage ? storage.getItem(key) : null;
|
|
804
|
-
// }
|
|
805
|
-
_setSession(session) {
|
|
810
|
+
setSession(session) {
|
|
811
|
+
const storage = typeof window !== "undefined" ? sessionStorage : global.sessionStorage;
|
|
806
812
|
const now = Math.floor(Date.now() / 1e3);
|
|
807
|
-
if (
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
813
|
+
if (storage) {
|
|
814
|
+
storage.setItem("access_token", session.access_token);
|
|
815
|
+
storage.setItem("refresh_token", session.refresh_token);
|
|
816
|
+
storage.setItem("refresh_expires_in", String(now + session.expires_in));
|
|
817
|
+
storage.setItem("refresh_exp", String(now + session.refresh_expires_in));
|
|
812
818
|
} else {
|
|
813
|
-
console.warn("
|
|
819
|
+
console.warn("Session storage is not available.");
|
|
814
820
|
}
|
|
815
821
|
}
|
|
822
|
+
getSessionVariable(key) {
|
|
823
|
+
const storage = typeof window !== "undefined" ? sessionStorage : global.sessionStorage;
|
|
824
|
+
return storage ? storage.getItem(key) : null;
|
|
825
|
+
}
|
|
826
|
+
// private _setSession(session: OidcToken): void {
|
|
827
|
+
// const now = Math.floor(Date.now() / 1000);
|
|
828
|
+
// if (typeof localStorage !== "undefined") {
|
|
829
|
+
// localStorage.setItem('token', session.access_token);
|
|
830
|
+
// localStorage.setItem('refresh_token', session.refresh_token);
|
|
831
|
+
// localStorage.setItem('exp', String(now + session.expires_in));
|
|
832
|
+
// localStorage.setItem('refresh_exp', String(now + session.refresh_expires_in));
|
|
833
|
+
// } else {
|
|
834
|
+
// console.warn("⚠️ Local Storage not available. You will need to pass tokens manually.");
|
|
835
|
+
// }
|
|
836
|
+
// }
|
|
816
837
|
login(email, password) {
|
|
817
838
|
return this._userConsumer.login(email, password).then(
|
|
818
839
|
(resp) => {
|
|
819
|
-
this.
|
|
840
|
+
this.setSession(resp);
|
|
820
841
|
return resp;
|
|
821
842
|
}
|
|
822
843
|
);
|