@proappstore/sdk 1.1.1 → 1.3.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.
- package/README.md +138 -25
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/storage.d.ts +37 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +98 -0
- package/dist/storage.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
# @proappstore/sdk
|
|
2
2
|
|
|
3
|
-
Unified SDK for paid apps on **proappstore.online**. Includes everything from `@freeappstore/sdk` (auth, kv, counters, rooms, proxy) plus subscription management and
|
|
3
|
+
Unified SDK for paid apps on **proappstore.online**. Includes everything from `@freeappstore/sdk` (auth, kv, counters, rooms, proxy) plus subscription management, license keys, and a per-app SQL database.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
npm i @proappstore/sdk
|
|
9
|
+
# or
|
|
10
|
+
pnpm add @proappstore/sdk
|
|
9
11
|
```
|
|
10
12
|
|
|
11
13
|
## Usage
|
|
@@ -14,52 +16,163 @@ npm i @proappstore/sdk
|
|
|
14
16
|
import { initPro } from '@proappstore/sdk'
|
|
15
17
|
|
|
16
18
|
const app = initPro({ appId: 'my-app' })
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Options:
|
|
22
|
+
|
|
23
|
+
| Option | Default | Description |
|
|
24
|
+
|--------|---------|-------------|
|
|
25
|
+
| `appId` | (required) | Your app's unique identifier |
|
|
26
|
+
| `fasApiBase` | `https://api.freeappstore.online` | Free-tier backend URL |
|
|
27
|
+
| `proApiBase` | `https://api.proappstore.online` | Pro-tier backend URL |
|
|
28
|
+
| `dataApiBase` | `https://data-{appId}.proappstore.online` | Per-app data worker URL |
|
|
29
|
+
|
|
30
|
+
## Modules
|
|
31
|
+
|
|
32
|
+
### Auth
|
|
33
|
+
|
|
34
|
+
GitHub OAuth — shared identity across all FreeAppStore and ProAppStore apps.
|
|
17
35
|
|
|
18
|
-
|
|
36
|
+
```ts
|
|
19
37
|
await app.auth.init()
|
|
20
38
|
app.auth.onChange((user) => console.log(user))
|
|
21
39
|
app.auth.signIn()
|
|
40
|
+
app.auth.signOut()
|
|
41
|
+
```
|
|
22
42
|
|
|
23
|
-
|
|
43
|
+
### KV (Per-user key-value storage)
|
|
44
|
+
|
|
45
|
+
```ts
|
|
24
46
|
await app.kv.set('profile', { name: 'Alice' })
|
|
47
|
+
const profile = await app.kv.get('profile')
|
|
25
48
|
const keys = await app.kv.list({ prefix: 'note:' })
|
|
49
|
+
await app.kv.delete('profile')
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Counters (Shared atomic counters)
|
|
53
|
+
|
|
54
|
+
Cross-user counters for votes, views, leaderboards.
|
|
26
55
|
|
|
27
|
-
|
|
56
|
+
```ts
|
|
28
57
|
await app.counters.increment('views')
|
|
58
|
+
await app.counters.decrement('likes')
|
|
29
59
|
const all = await app.counters.list()
|
|
60
|
+
```
|
|
30
61
|
|
|
31
|
-
|
|
62
|
+
### Rooms (Real-time WebSocket)
|
|
63
|
+
|
|
64
|
+
```ts
|
|
32
65
|
const room = app.rooms.join('lobby')
|
|
33
66
|
room.send({ text: 'hello' })
|
|
34
67
|
room.onMessage((msg) => console.log(msg))
|
|
68
|
+
room.onPeers((peers) => console.log(peers))
|
|
69
|
+
room.leave()
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Proxy (Secret-injecting API proxy)
|
|
73
|
+
|
|
74
|
+
Call third-party APIs without exposing keys to the client.
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
const response = await app.proxy.fetch('/openai/chat/completions', {
|
|
78
|
+
method: 'POST',
|
|
79
|
+
body: JSON.stringify({ model: 'gpt-4', messages: [...] }),
|
|
80
|
+
})
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Database (Per-app SQL)
|
|
84
|
+
|
|
85
|
+
Each Pro app gets its own D1 SQL database accessed through a dedicated data worker at `data-{appId}.proappstore.online`.
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
// Query rows
|
|
89
|
+
const { rows } = await app.db.query('SELECT * FROM users WHERE active = ?', [true])
|
|
35
90
|
|
|
36
|
-
//
|
|
91
|
+
// Execute writes
|
|
92
|
+
const { meta } = await app.db.execute('INSERT INTO users (name) VALUES (?)', ['Alice'])
|
|
93
|
+
console.log(meta.last_row_id) // auto-increment id
|
|
94
|
+
|
|
95
|
+
// Batch (transactional)
|
|
96
|
+
const results = await app.db.batch([
|
|
97
|
+
{ sql: 'INSERT INTO orders (user_id, total) VALUES (?, ?)', params: [1, 99.99] },
|
|
98
|
+
{ sql: 'UPDATE users SET order_count = order_count + 1 WHERE id = ?', params: [1] },
|
|
99
|
+
])
|
|
100
|
+
|
|
101
|
+
// List tables
|
|
102
|
+
const tables = await app.db.tables()
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Subscription (Stripe-powered)
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
// Check subscription status
|
|
37
109
|
const sub = await app.subscription.status()
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
110
|
+
// Returns: { status, tier, priceId, currentPeriodEnd, cancelAtPeriodEnd } | null
|
|
111
|
+
|
|
112
|
+
// Open Stripe checkout (navigates away)
|
|
113
|
+
await app.subscription.openCheckout({
|
|
114
|
+
priceId: 'price_pro_monthly',
|
|
115
|
+
successUrl: 'https://my-app.proappstore.online/success',
|
|
116
|
+
cancelUrl: 'https://my-app.proappstore.online/',
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
// Open Stripe billing portal (navigates away)
|
|
45
120
|
await app.subscription.openPortal('https://my-app.proappstore.online/')
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### License
|
|
46
124
|
|
|
47
|
-
|
|
125
|
+
Per-app license key validation.
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
// Get current user's license (requires auth)
|
|
48
129
|
const license = await app.license.current()
|
|
49
|
-
|
|
130
|
+
// Returns: { key, appId, issuedAt, expiresAt } | null
|
|
131
|
+
|
|
132
|
+
// Validate any key (no auth required)
|
|
133
|
+
const valid = await app.license.validate('LIC-ABC-123')
|
|
50
134
|
```
|
|
51
135
|
|
|
52
|
-
##
|
|
136
|
+
## ProShell Component
|
|
137
|
+
|
|
138
|
+
A React component that handles auth gates, subscription checks, and renders a platform-level shell with topbar and user menu.
|
|
139
|
+
|
|
140
|
+
```tsx
|
|
141
|
+
import { initPro } from '@proappstore/sdk'
|
|
142
|
+
import { ProShell } from '@proappstore/sdk/shell'
|
|
143
|
+
|
|
144
|
+
const app = initPro({ appId: 'meetup' })
|
|
145
|
+
|
|
146
|
+
export default function App() {
|
|
147
|
+
return (
|
|
148
|
+
<ProShell app={app} appName="Meetup">
|
|
149
|
+
<MeetupApp />
|
|
150
|
+
</ProShell>
|
|
151
|
+
)
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Props:
|
|
156
|
+
|
|
157
|
+
| Prop | Type | Description |
|
|
158
|
+
|------|------|-------------|
|
|
159
|
+
| `app` | `ProAppStore` | SDK instance from `initPro()` |
|
|
160
|
+
| `children` | `ReactNode` | App content (rendered only when gates pass) |
|
|
161
|
+
| `appName` | `string?` | Name shown in the topbar |
|
|
162
|
+
| `allowFree` | `boolean?` | Skip subscription gate (default: `false`) |
|
|
163
|
+
|
|
164
|
+
ProShell handles:
|
|
165
|
+
- Auth initialization and sign-in gate
|
|
166
|
+
- Subscription check and upgrade wall (unless `allowFree=true`)
|
|
167
|
+
- Topbar with avatar, app name, and user menu (sign out, manage billing, delete account)
|
|
168
|
+
|
|
169
|
+
## Per-app SQL Database
|
|
170
|
+
|
|
171
|
+
Each Pro app is provisioned with a dedicated Cloudflare D1 database fronted by a data worker (`data-{appId}.proappstore.online`). The SDK's `db` module provides a typed client for this worker.
|
|
172
|
+
|
|
173
|
+
The database is per-user isolated at the auth layer — all requests require a valid Bearer token. The data worker validates the token against the FAS auth API before executing queries.
|
|
53
174
|
|
|
54
|
-
|
|
55
|
-
|--------|--------|-------------|
|
|
56
|
-
| `auth` | FAS | GitHub OAuth, SSO across all apps |
|
|
57
|
-
| `kv` | FAS | Per-user key-value store (list, get, set, delete, getMany) |
|
|
58
|
-
| `counters` | FAS | Shared atomic counters (votes, views, leaderboards) |
|
|
59
|
-
| `rooms` | FAS | Real-time WebSocket rooms (presence, chat, collab) |
|
|
60
|
-
| `proxy` | FAS | Secret-injecting API proxy for third-party services |
|
|
61
|
-
| `subscription` | Pro | Stripe checkout, portal, status |
|
|
62
|
-
| `license` | Pro | Per-app license key validation |
|
|
175
|
+
Tables are user-defined (create them via `db.execute('CREATE TABLE IF NOT EXISTS ...')`). The schema is entirely up to the app developer.
|
|
63
176
|
|
|
64
177
|
## License
|
|
65
178
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { FreeAppStore } from '@freeappstore/sdk';
|
|
2
2
|
import { Database } from './db.js';
|
|
3
|
+
import { Storage } from './storage.js';
|
|
3
4
|
import { SubscriptionApi } from './subscription.js';
|
|
4
5
|
import { LicenseApi } from './license.js';
|
|
5
6
|
import type { ProInitOptions } from './types.js';
|
|
@@ -16,6 +17,7 @@ export declare class ProAppStore extends FreeAppStore {
|
|
|
16
17
|
readonly subscription: SubscriptionApi;
|
|
17
18
|
readonly license: LicenseApi;
|
|
18
19
|
readonly db: Database;
|
|
20
|
+
readonly storage: Storage;
|
|
19
21
|
constructor(opts: ProInitOptions);
|
|
20
22
|
}
|
|
21
23
|
/** Create a new ProAppStore SDK instance. Includes all free + pro features. */
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGjD,YAAY,EACV,IAAI,EACJ,WAAW,EACX,cAAc,EACd,eAAe,EACf,IAAI,EACJ,WAAW,EACX,QAAQ,GACT,MAAM,mBAAmB,CAAC;AAE3B,YAAY,EACV,cAAc,EACd,YAAY,EACZ,kBAAkB,EAClB,eAAe,EACf,WAAW,GACZ,MAAM,YAAY,CAAC;AAEpB,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE1D;;;;;GAKG;AACH,qBAAa,WAAY,SAAQ,YAAY;IAC3C,QAAQ,CAAC,YAAY,EAAE,eAAe,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAC7B,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGjD,YAAY,EACV,IAAI,EACJ,WAAW,EACX,cAAc,EACd,eAAe,EACf,IAAI,EACJ,WAAW,EACX,QAAQ,GACT,MAAM,mBAAmB,CAAC;AAE3B,YAAY,EACV,cAAc,EACd,YAAY,EACZ,kBAAkB,EAClB,eAAe,EACf,WAAW,GACZ,MAAM,YAAY,CAAC;AAEpB,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE1D;;;;;GAKG;AACH,qBAAa,WAAY,SAAQ,YAAY;IAC3C,QAAQ,CAAC,YAAY,EAAE,eAAe,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAC7B,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;gBAEd,IAAI,EAAE,cAAc;CAQjC;AAED,+EAA+E;AAC/E,wBAAgB,OAAO,CAAC,IAAI,EAAE,cAAc,GAAG,WAAW,CAEzD"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { FreeAppStore } from '@freeappstore/sdk';
|
|
2
2
|
import { Database } from './db.js';
|
|
3
|
+
import { Storage } from './storage.js';
|
|
3
4
|
import { SubscriptionApi } from './subscription.js';
|
|
4
5
|
import { LicenseApi } from './license.js';
|
|
5
6
|
/**
|
|
@@ -12,12 +13,14 @@ export class ProAppStore extends FreeAppStore {
|
|
|
12
13
|
subscription;
|
|
13
14
|
license;
|
|
14
15
|
db;
|
|
16
|
+
storage;
|
|
15
17
|
constructor(opts) {
|
|
16
18
|
super({ appId: opts.appId, ...(opts.fasApiBase && { apiBase: opts.fasApiBase }) });
|
|
17
19
|
const proApiBase = opts.proApiBase ?? 'https://api.proappstore.online';
|
|
18
20
|
this.subscription = new SubscriptionApi(opts.appId, proApiBase, this.auth);
|
|
19
21
|
this.license = new LicenseApi(opts.appId, proApiBase, this.auth);
|
|
20
22
|
this.db = new Database(opts.appId, opts.dataApiBase ?? `https://data-${opts.appId}.proappstore.online`, this.auth);
|
|
23
|
+
this.storage = new Storage(opts.appId, proApiBase, this.auth);
|
|
21
24
|
}
|
|
22
25
|
}
|
|
23
26
|
/** Create a new ProAppStore SDK instance. Includes all free + pro features. */
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAwB1C;;;;;GAKG;AACH,MAAM,OAAO,WAAY,SAAQ,YAAY;IAClC,YAAY,CAAkB;IAC9B,OAAO,CAAa;IACpB,EAAE,CAAW;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAwB1C;;;;;GAKG;AACH,MAAM,OAAO,WAAY,SAAQ,YAAY;IAClC,YAAY,CAAkB;IAC9B,OAAO,CAAa;IACpB,EAAE,CAAW;IACb,OAAO,CAAU;IAE1B,YAAY,IAAoB;QAC9B,KAAK,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,CAAC,UAAU,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC,EAAE,CAAC,CAAC;QACnF,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,gCAAgC,CAAC;QACvE,IAAI,CAAC,YAAY,GAAG,IAAI,eAAe,CAAC,IAAI,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3E,IAAI,CAAC,OAAO,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QACjE,IAAI,CAAC,EAAE,GAAG,IAAI,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,WAAW,IAAI,gBAAgB,IAAI,CAAC,KAAK,qBAAqB,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QACnH,IAAI,CAAC,OAAO,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IAChE,CAAC;CACF;AAED,+EAA+E;AAC/E,MAAM,UAAU,OAAO,CAAC,IAAoB;IAC1C,OAAO,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC;AAC/B,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
interface AuthLike {
|
|
2
|
+
token: string | null;
|
|
3
|
+
handleUnauthorized(): void;
|
|
4
|
+
}
|
|
5
|
+
interface UploadResult {
|
|
6
|
+
key: string;
|
|
7
|
+
size: number;
|
|
8
|
+
contentType: string;
|
|
9
|
+
url: string;
|
|
10
|
+
}
|
|
11
|
+
interface FileInfo {
|
|
12
|
+
key: string;
|
|
13
|
+
size: number;
|
|
14
|
+
uploaded: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* File storage — upload, download, list, delete files.
|
|
18
|
+
* Backed by R2 on the PAS API, scoped to (appId, userId).
|
|
19
|
+
*/
|
|
20
|
+
export declare class Storage {
|
|
21
|
+
private readonly appId;
|
|
22
|
+
private readonly apiBase;
|
|
23
|
+
private readonly auth;
|
|
24
|
+
constructor(appId: string, apiBase: string, auth: AuthLike);
|
|
25
|
+
/** Upload a file. Returns the upload result with the file URL. */
|
|
26
|
+
upload(path: string, data: Blob | ArrayBuffer | Uint8Array, contentType?: string): Promise<UploadResult>;
|
|
27
|
+
/** Download a file. Returns the Response (use .blob(), .arrayBuffer(), etc.). */
|
|
28
|
+
download(path: string): Promise<Response>;
|
|
29
|
+
/** Get a URL for a file (for use in <img src> etc). Requires auth header. */
|
|
30
|
+
url(path: string): string;
|
|
31
|
+
/** List all files for the current user in this app. */
|
|
32
|
+
list(): Promise<FileInfo[]>;
|
|
33
|
+
/** Delete a file. */
|
|
34
|
+
delete(path: string): Promise<void>;
|
|
35
|
+
}
|
|
36
|
+
export {};
|
|
37
|
+
//# sourceMappingURL=storage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"AAAA,UAAU,QAAQ;IAChB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,kBAAkB,IAAI,IAAI,CAAC;CAC5B;AAED,UAAU,YAAY;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,UAAU,QAAQ;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,qBAAa,OAAO;IAEhB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAFJ,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,QAAQ;IAGjC,kEAAkE;IAC5D,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,WAAW,GAAG,UAAU,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IA0B9G,iFAAiF;IAC3E,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAmB/C,6EAA6E;IAC7E,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAIzB,uDAAuD;IACjD,IAAI,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;IAmBjC,qBAAqB;IACf,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAkB1C"}
|
package/dist/storage.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File storage — upload, download, list, delete files.
|
|
3
|
+
* Backed by R2 on the PAS API, scoped to (appId, userId).
|
|
4
|
+
*/
|
|
5
|
+
export class Storage {
|
|
6
|
+
appId;
|
|
7
|
+
apiBase;
|
|
8
|
+
auth;
|
|
9
|
+
constructor(appId, apiBase, auth) {
|
|
10
|
+
this.appId = appId;
|
|
11
|
+
this.apiBase = apiBase;
|
|
12
|
+
this.auth = auth;
|
|
13
|
+
}
|
|
14
|
+
/** Upload a file. Returns the upload result with the file URL. */
|
|
15
|
+
async upload(path, data, contentType) {
|
|
16
|
+
const token = this.auth.token;
|
|
17
|
+
if (!token)
|
|
18
|
+
throw new Error('Not signed in.');
|
|
19
|
+
const url = `${this.apiBase}/v1/apps/${encodeURIComponent(this.appId)}/storage/${path}`;
|
|
20
|
+
const response = await fetch(url, {
|
|
21
|
+
method: 'PUT',
|
|
22
|
+
headers: {
|
|
23
|
+
Authorization: `Bearer ${token}`,
|
|
24
|
+
'Content-Type': contentType || (data instanceof Blob ? data.type : 'application/octet-stream'),
|
|
25
|
+
},
|
|
26
|
+
body: data,
|
|
27
|
+
});
|
|
28
|
+
if (response.status === 401) {
|
|
29
|
+
this.auth.handleUnauthorized();
|
|
30
|
+
throw new Error('Not signed in.');
|
|
31
|
+
}
|
|
32
|
+
if (!response.ok) {
|
|
33
|
+
const text = await response.text();
|
|
34
|
+
throw new Error(`storage.upload failed (${response.status}): ${text}`);
|
|
35
|
+
}
|
|
36
|
+
return (await response.json());
|
|
37
|
+
}
|
|
38
|
+
/** Download a file. Returns the Response (use .blob(), .arrayBuffer(), etc.). */
|
|
39
|
+
async download(path) {
|
|
40
|
+
const token = this.auth.token;
|
|
41
|
+
if (!token)
|
|
42
|
+
throw new Error('Not signed in.');
|
|
43
|
+
const url = `${this.apiBase}/v1/apps/${encodeURIComponent(this.appId)}/storage/${path}`;
|
|
44
|
+
const response = await fetch(url, {
|
|
45
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
46
|
+
});
|
|
47
|
+
if (response.status === 401) {
|
|
48
|
+
this.auth.handleUnauthorized();
|
|
49
|
+
throw new Error('Not signed in.');
|
|
50
|
+
}
|
|
51
|
+
if (response.status === 404)
|
|
52
|
+
throw new Error('File not found.');
|
|
53
|
+
if (!response.ok)
|
|
54
|
+
throw new Error(`storage.download failed: ${response.status}`);
|
|
55
|
+
return response;
|
|
56
|
+
}
|
|
57
|
+
/** Get a URL for a file (for use in <img src> etc). Requires auth header. */
|
|
58
|
+
url(path) {
|
|
59
|
+
return `${this.apiBase}/v1/apps/${encodeURIComponent(this.appId)}/storage/${path}`;
|
|
60
|
+
}
|
|
61
|
+
/** List all files for the current user in this app. */
|
|
62
|
+
async list() {
|
|
63
|
+
const token = this.auth.token;
|
|
64
|
+
if (!token)
|
|
65
|
+
throw new Error('Not signed in.');
|
|
66
|
+
const url = `${this.apiBase}/v1/apps/${encodeURIComponent(this.appId)}/files`;
|
|
67
|
+
const response = await fetch(url, {
|
|
68
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
69
|
+
});
|
|
70
|
+
if (response.status === 401) {
|
|
71
|
+
this.auth.handleUnauthorized();
|
|
72
|
+
throw new Error('Not signed in.');
|
|
73
|
+
}
|
|
74
|
+
if (!response.ok)
|
|
75
|
+
throw new Error(`storage.list failed: ${response.status}`);
|
|
76
|
+
const data = (await response.json());
|
|
77
|
+
return data.files;
|
|
78
|
+
}
|
|
79
|
+
/** Delete a file. */
|
|
80
|
+
async delete(path) {
|
|
81
|
+
const token = this.auth.token;
|
|
82
|
+
if (!token)
|
|
83
|
+
throw new Error('Not signed in.');
|
|
84
|
+
const url = `${this.apiBase}/v1/apps/${encodeURIComponent(this.appId)}/storage/${path}`;
|
|
85
|
+
const response = await fetch(url, {
|
|
86
|
+
method: 'DELETE',
|
|
87
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
88
|
+
});
|
|
89
|
+
if (response.status === 401) {
|
|
90
|
+
this.auth.handleUnauthorized();
|
|
91
|
+
throw new Error('Not signed in.');
|
|
92
|
+
}
|
|
93
|
+
if (!response.ok && response.status !== 404) {
|
|
94
|
+
throw new Error(`storage.delete failed: ${response.status}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"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"}
|