@seletech/sele 0.0.0-stage → 0.1.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.
@@ -0,0 +1,41 @@
1
+ import { type ClientOptions } from './http.ts';
2
+ export interface StoredObject {
3
+ key: string;
4
+ size: number;
5
+ uploaded: string | null;
6
+ }
7
+ export interface ObjectList {
8
+ objects: StoredObject[];
9
+ truncated: boolean;
10
+ cursor: string | null;
11
+ }
12
+ export interface SignedUrl {
13
+ url: string;
14
+ method: 'GET' | 'PUT';
15
+ expires_at: string;
16
+ }
17
+ export declare class SeleStorage {
18
+ private readonly http;
19
+ constructor(o: ClientOptions);
20
+ put(key: string, body: Uint8Array | string, contentType?: string): Promise<{
21
+ key: string;
22
+ size: number;
23
+ }>;
24
+ get(key: string): Promise<Uint8Array>;
25
+ getText(key: string): Promise<string>;
26
+ delete(key: string): Promise<{
27
+ deleted: string;
28
+ }>;
29
+ list(o?: {
30
+ prefix?: string;
31
+ limit?: number;
32
+ cursor?: string;
33
+ }): Promise<ObjectList>;
34
+ /** A URL that lets a browser upload or download one object without your key. Relative to the storage base URL. */
35
+ signedUrl(o: {
36
+ method: 'GET' | 'PUT';
37
+ key: string;
38
+ expiresIn?: number;
39
+ }): Promise<SignedUrl>;
40
+ usage(): Promise<Record<string, unknown>>;
41
+ }
@@ -0,0 +1,19 @@
1
+ import { Http, enc } from "./http.js";
2
+ export class SeleStorage {
3
+ http;
4
+ constructor(o) { this.http = new Http({ baseUrl: 'https://storage.seleid.dev' }, o); }
5
+ async put(key, body, contentType = 'application/octet-stream') {
6
+ const bytes = typeof body === 'string' ? new TextEncoder().encode(body) : body;
7
+ const res = await this.http.raw('PUT', `/v1/objects/${enc(key)}`, { headers: { 'content-type': contentType, 'content-length': String(bytes.byteLength) }, body: bytes });
8
+ return (await res.json());
9
+ }
10
+ async get(key) { return new Uint8Array(await (await this.http.raw('GET', `/v1/objects/${enc(key)}`)).arrayBuffer()); }
11
+ async getText(key) { return new TextDecoder().decode(await this.get(key)); }
12
+ delete(key) { return this.http.json('DELETE', `/v1/objects/${enc(key)}`); }
13
+ list(o = {}) { return this.http.json('GET', '/v1/objects', undefined, o); }
14
+ /** A URL that lets a browser upload or download one object without your key. Relative to the storage base URL. */
15
+ signedUrl(o) {
16
+ return this.http.json('POST', '/v1/signed-urls', { method: o.method, key: o.key, expires_in: o.expiresIn });
17
+ }
18
+ usage() { return this.http.json('GET', '/v1/usage'); }
19
+ }
@@ -0,0 +1,18 @@
1
+ import type { Tokens } from './auth.ts';
2
+ export interface SeleIdTokenSource {
3
+ /** Current session tokens, or null when signed out. Must include `id_token` (request the `openid` scope). */
4
+ getTokens(): Promise<Tokens | null> | Tokens | null;
5
+ /** Called with the refresh token when the ID token is (almost) expired. Typically `(rt) => auth.refresh(rt)`. */
6
+ refresh(refreshToken: string): Promise<Tokens>;
7
+ /** Called with the fresh tokens so your app can persist the rotated refresh token. */
8
+ onTokens?(tokens: Tokens): void | Promise<void>;
9
+ /** Refresh this many seconds before expiry. Default 60. */
10
+ skewSeconds?: number;
11
+ /** Clock override for tests (ms). */
12
+ now?: () => number;
13
+ }
14
+ /**
15
+ * Returns the callback for `createClient(url, anonKey, { accessToken })`. It returns a valid SeleID ID token,
16
+ * refreshing it (once, even for concurrent calls) when it is within `skewSeconds` of expiry, and null when signed out.
17
+ */
18
+ export declare function seleIdAccessToken(src: SeleIdTokenSource): () => Promise<string | null>;
@@ -0,0 +1,49 @@
1
+ function expOf(jwt) {
2
+ try {
3
+ const part = jwt.split('.')[1]?.replace(/-/g, '+').replace(/_/g, '/');
4
+ if (!part)
5
+ return null;
6
+ const json = JSON.parse(atob(part.padEnd(Math.ceil(part.length / 4) * 4, '=')));
7
+ return typeof json.exp === 'number' ? json.exp : null;
8
+ }
9
+ catch {
10
+ return null;
11
+ }
12
+ }
13
+ /**
14
+ * Returns the callback for `createClient(url, anonKey, { accessToken })`. It returns a valid SeleID ID token,
15
+ * refreshing it (once, even for concurrent calls) when it is within `skewSeconds` of expiry, and null when signed out.
16
+ */
17
+ export function seleIdAccessToken(src) {
18
+ const skew = (src.skewSeconds ?? 60) * 1000;
19
+ const now = src.now ?? (() => Date.now());
20
+ let inflight = null;
21
+ const fresh = (t) => {
22
+ const exp = t?.id_token ? expOf(t.id_token) : null;
23
+ return t?.id_token && exp !== null && exp * 1000 - now() > skew ? t.id_token : null;
24
+ };
25
+ return async () => {
26
+ const tokens = await src.getTokens();
27
+ if (!tokens)
28
+ return null;
29
+ const ok = fresh(tokens);
30
+ if (ok)
31
+ return ok;
32
+ if (!tokens.refresh_token)
33
+ return null;
34
+ inflight ??= (async () => {
35
+ try {
36
+ const next = await src.refresh(tokens.refresh_token);
37
+ await src.onTokens?.(next);
38
+ return fresh(next);
39
+ }
40
+ catch {
41
+ return null;
42
+ }
43
+ finally {
44
+ inflight = null;
45
+ }
46
+ })();
47
+ return inflight;
48
+ };
49
+ }
package/package.json CHANGED
@@ -1,6 +1,29 @@
1
1
  {
2
2
  "name": "@seletech/sele",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "TypeScript client for SeleID sign-in (PKCE), SeleStorage, SeleBase, events and SeleAI.",
5
+ "type": "module",
6
+ "license": "Apache-2.0",
7
+ "main": "./dist/cjs/index.js",
8
+ "module": "./dist/esm/index.js",
9
+ "types": "./dist/esm/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "import": { "types": "./dist/esm/index.d.ts", "default": "./dist/esm/index.js" },
13
+ "require": { "types": "./dist/cjs/index.d.ts", "default": "./dist/cjs/index.js" }
14
+ },
15
+ "./package.json": "./package.json"
16
+ },
17
+ "files": ["dist", "README.md", "CHANGELOG.md", "LICENSE"],
18
+ "sideEffects": false,
19
+ "engines": { "node": ">=20" },
20
+ "devDependencies": { "typescript": "^5.9.3" },
21
+ "scripts": {
22
+ "build": "node scripts/build.mjs",
23
+ "test": "node --test --experimental-strip-types test/*.test.mjs",
24
+ "prepublishOnly": "npm test && npm run build"
25
+ },
26
+ "publishConfig": { "access": "public" },
27
+ "repository": { "type": "git", "url": "git+https://github.com/seletech-ai/SeleID.git", "directory": "sdks/typescript" },
28
+ "keywords": ["seleid", "oidc", "pkce", "storage", "sql", "webhooks", "sdk"]
29
+ }