naro-js 0.1.0 → 0.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 +187 -3
- package/dist/auth/admin.d.ts +23 -0
- package/dist/auth/client.d.ts +24 -5
- package/dist/auth/flows.d.ts +1 -1
- package/dist/client.d.ts +11 -1
- package/dist/errors.d.ts +15 -5
- package/dist/http.d.ts +27 -4
- package/dist/index.d.ts +4 -1
- package/dist/index.js +571 -18
- package/dist/storage/client.d.ts +65 -0
- package/dist/storage/path.d.ts +76 -0
- package/dist/storage/types.d.ts +58 -0
- package/dist/types.d.ts +86 -4
- package/package.json +4 -4
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { AuthClient } from "../auth/client.js";
|
|
2
|
+
import { type HttpConfig } from "../http.js";
|
|
3
|
+
import type { NaroResponse } from "../types.js";
|
|
4
|
+
import type { DownloadOption, FileObject, FolderEntry, ListOptions, UpdateOptions, UploadBody, UploadOptions, UploadResult } from "./types.js";
|
|
5
|
+
/** One bucket's objects: supabase-js's StorageFileApi. */
|
|
6
|
+
export declare class StorageFileApi {
|
|
7
|
+
#private;
|
|
8
|
+
constructor(http: HttpConfig, auth: AuthClient, bucket: string);
|
|
9
|
+
/**
|
|
10
|
+
* A new object; with `upsert: true`, one of the same name is replaced.
|
|
11
|
+
* `null` options, as a JS caller may pass, are no options.
|
|
12
|
+
*/
|
|
13
|
+
upload(path: string, body: UploadBody, options?: UploadOptions): Promise<NaroResponse<UploadResult>>;
|
|
14
|
+
/** Replaces an object that is there (`OBJECT_NOT_FOUND` otherwise). */
|
|
15
|
+
update(path: string, body: UploadBody, options?: UpdateOptions): Promise<NaroResponse<UploadResult>>;
|
|
16
|
+
/**
|
|
17
|
+
* The object's bytes, as a Blob of the type the worker serves it under:
|
|
18
|
+
* the stored type for the images, audio, video, PDF, plain text, CSV,
|
|
19
|
+
* JSON and SVG it serves as themselves, `application/octet-stream` for
|
|
20
|
+
* anything else (HTML, scripts, archives, unknown types — `info` and
|
|
21
|
+
* `list` hold the stored type).
|
|
22
|
+
*/
|
|
23
|
+
download(path: string): Promise<NaroResponse<Blob>>;
|
|
24
|
+
info(path: string): Promise<NaroResponse<FileObject>>;
|
|
25
|
+
/**
|
|
26
|
+
* Whether the caller can see the object: `OBJECT_NOT_FOUND` is false,
|
|
27
|
+
* every other error comes back as it is (plan C1 — asked with `info`,
|
|
28
|
+
* not HEAD, whose answer has no body to tell a missing bucket from a
|
|
29
|
+
* missing object).
|
|
30
|
+
*/
|
|
31
|
+
exists(path: string): Promise<NaroResponse<boolean>>;
|
|
32
|
+
/**
|
|
33
|
+
* What is directly below `prefix` (the whole bucket by default): folders
|
|
34
|
+
* first, then files. The options go to the worker as they are given.
|
|
35
|
+
*/
|
|
36
|
+
list(prefix?: string, options?: ListOptions): Promise<NaroResponse<(FileObject | FolderEntry)[]>>;
|
|
37
|
+
/** Removes the objects named — 1 to 1,000 — and answers the ones it removed. */
|
|
38
|
+
remove(paths: string[]): Promise<NaroResponse<FileObject[]>>;
|
|
39
|
+
/**
|
|
40
|
+
* A link to the object for `expiresIn` seconds (1–604,800). It stops
|
|
41
|
+
* working once the object is replaced or removed.
|
|
42
|
+
*/
|
|
43
|
+
createSignedUrl(path: string, expiresIn: number, options?: DownloadOption): Promise<NaroResponse<{
|
|
44
|
+
signedUrl: string;
|
|
45
|
+
}>>;
|
|
46
|
+
/**
|
|
47
|
+
* A public bucket's URL for the object. No request is made, so whether
|
|
48
|
+
* the bucket is public is not checked (as in supabase-js); a path no URL
|
|
49
|
+
* can carry is INVALID_PATH (plan C3), and a download name none can carry
|
|
50
|
+
* is the worker's INVALID_REQUEST for it.
|
|
51
|
+
*/
|
|
52
|
+
getPublicUrl(path: string, options?: DownloadOption): NaroResponse<{
|
|
53
|
+
publicUrl: string;
|
|
54
|
+
}>;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* `naro.storage`. Buckets are made with `naro storage bucket create` or the
|
|
58
|
+
* MCP server, never from a client (original spec §6).
|
|
59
|
+
*/
|
|
60
|
+
export declare class StorageClient {
|
|
61
|
+
#private;
|
|
62
|
+
constructor(http: HttpConfig, auth: AuthClient);
|
|
63
|
+
/** The objects of one bucket. */
|
|
64
|
+
from(bucket: string): StorageFileApi;
|
|
65
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { NaroError } from "../errors.js";
|
|
2
|
+
/**
|
|
3
|
+
* The most one upload request carries (original spec §9.1): 50 MiB.
|
|
4
|
+
* data-core's MAX_SINGLE_UPLOAD_BYTES — storage/wire.test.ts holds them
|
|
5
|
+
* equal. Over it, the edge cuts the body off and the browser often cannot
|
|
6
|
+
* read the answer: the SDK refuses first.
|
|
7
|
+
*/
|
|
8
|
+
export declare const MAX_SINGLE_UPLOAD_BYTES = 52428800;
|
|
9
|
+
/** The worker's words (data-core's catalog) for what the SDK refuses itself. */
|
|
10
|
+
export declare const STORAGE_MESSAGES: {
|
|
11
|
+
readonly bucket: "The bucket name is not valid.";
|
|
12
|
+
readonly tooLarge: "Uploads over 50 MiB are not supported yet.";
|
|
13
|
+
readonly request: "The storage request is not valid.";
|
|
14
|
+
readonly body: "upload takes a Blob, File, ArrayBuffer, typed array or string.";
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* The words the worker's routes take as the first segment after `/object/`
|
|
18
|
+
* (data-core's STORAGE_RESERVED_WORDS): as a bucket, the URL would name
|
|
19
|
+
* another route. No bucket can have one of these names.
|
|
20
|
+
*/
|
|
21
|
+
export declare const RESERVED_BUCKETS: ReadonlySet<string>;
|
|
22
|
+
/** Why the worker refuses a `download` name (serve.ts's DOWNLOAD_NAME_REASON). */
|
|
23
|
+
export declare const DOWNLOAD_NAME_REASON = "must be a file name of up to 255 bytes";
|
|
24
|
+
export type Encoded = {
|
|
25
|
+
ok: true;
|
|
26
|
+
encoded: string;
|
|
27
|
+
} | {
|
|
28
|
+
ok: false;
|
|
29
|
+
error: NaroError;
|
|
30
|
+
};
|
|
31
|
+
export declare function invalidPath(reason: string): NaroError;
|
|
32
|
+
export declare function objectTooLarge(): NaroError;
|
|
33
|
+
/**
|
|
34
|
+
* An object path as a URL carries it, each segment encoded once and `/`
|
|
35
|
+
* kept as the separator — or the worker's INVALID_PATH for the paths no
|
|
36
|
+
* URL can carry as they are (original spec §13, plan C2): an empty name, a
|
|
37
|
+
* `/` at either end, `//`, a `.` or `..` segment (WHATWG URL parsing folds
|
|
38
|
+
* `..` and `%2E%2E` alike, so the worker would get another path), and a
|
|
39
|
+
* lone surrogate (`encodeURIComponent` throws on one). Every other rule is
|
|
40
|
+
* the worker's alone — one set of rules, in one place.
|
|
41
|
+
*/
|
|
42
|
+
export declare function encodePath(path: string): Encoded;
|
|
43
|
+
/**
|
|
44
|
+
* `?download` for the object's own name, `?download=<name>` for another, or
|
|
45
|
+
* the worker's own INVALID_REQUEST for a name `encodeURIComponent` throws on
|
|
46
|
+
* (a lone surrogate: the sign route refuses the same name in its body). Every
|
|
47
|
+
* other rule on the name is the worker's alone.
|
|
48
|
+
*/
|
|
49
|
+
export declare function downloadQuery(download: boolean | string | undefined): Encoded;
|
|
50
|
+
/**
|
|
51
|
+
* A bucket name as the URL's segment — encoded (a valid name is unchanged
|
|
52
|
+
* by it), or INVALID_PATH for the names that would not reach a bucket:
|
|
53
|
+
* empty, `.`, `..`, a lone surrogate and the routes' own words.
|
|
54
|
+
*/
|
|
55
|
+
export declare function encodeBucket(bucket: string): Encoded;
|
|
56
|
+
/** Why the worker refuses a cache-control it cannot read (its upload.ts). */
|
|
57
|
+
export declare const CACHE_CONTROL_REASON = "must be max-age=<seconds> from 0 to 31536000";
|
|
58
|
+
/**
|
|
59
|
+
* The upload's `contentType` and `cacheControl` as headers, or the worker's
|
|
60
|
+
* answer for a value it cannot read when no header can carry it (PR #90
|
|
61
|
+
* review 1): INVALID_CONTENT_TYPE with data-core's words (the type cut at
|
|
62
|
+
* 256 UTF-16 units, as `storageName` cuts it), and INVALID_REQUEST
|
|
63
|
+
* `cache-control`. Every other value is the worker's to judge.
|
|
64
|
+
*/
|
|
65
|
+
export declare function headerProblem(contentType: string | undefined, cacheControl: string): NaroError | null;
|
|
66
|
+
export type Payload = {
|
|
67
|
+
ok: true;
|
|
68
|
+
body: Blob | ArrayBuffer | Uint8Array<ArrayBuffer>;
|
|
69
|
+
size: number;
|
|
70
|
+
type: string;
|
|
71
|
+
} | {
|
|
72
|
+
ok: false;
|
|
73
|
+
error: NaroError;
|
|
74
|
+
};
|
|
75
|
+
/** The upload's body, its bytes and its type — or INVALID_BODY. */
|
|
76
|
+
export declare function payloadOf(body: unknown, contentType: string | undefined): Payload;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/** An object as the worker answers it. */
|
|
2
|
+
export type FileObject = {
|
|
3
|
+
id: string;
|
|
4
|
+
/** The full path from `info` and `remove`; the name below the prefix in `list`. */
|
|
5
|
+
name: string;
|
|
6
|
+
bucket_id: string;
|
|
7
|
+
/** Who uploaded it; null for the anon or the service key. */
|
|
8
|
+
owner: string | null;
|
|
9
|
+
owner_id: string | null;
|
|
10
|
+
/** The bytes' etag. */
|
|
11
|
+
version: string;
|
|
12
|
+
created_at: string;
|
|
13
|
+
updated_at: string;
|
|
14
|
+
/** `eTag`, `size`, `mimetype`, `cacheControl`, `lastModified`, `contentLength`, `httpStatusCode`. */
|
|
15
|
+
metadata: Record<string, unknown>;
|
|
16
|
+
};
|
|
17
|
+
/** A folder in a listing: a name and nothing else. Folders come first. */
|
|
18
|
+
export type FolderEntry = {
|
|
19
|
+
name: string;
|
|
20
|
+
id: null;
|
|
21
|
+
created_at: null;
|
|
22
|
+
updated_at: null;
|
|
23
|
+
metadata: null;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* What an upload sends: bytes whose length `fetch` knows, so it sets
|
|
27
|
+
* `Content-Length`. A string goes as UTF-8. Not a stream, not `FormData`.
|
|
28
|
+
*/
|
|
29
|
+
export type UploadBody = Blob | ArrayBuffer | ArrayBufferView | string;
|
|
30
|
+
export type UploadOptions = {
|
|
31
|
+
/** Default: the Blob's type, `text/plain;charset=utf-8` for a string, else `application/octet-stream`. */
|
|
32
|
+
contentType?: string;
|
|
33
|
+
/** Seconds a browser may keep it, as text. Default `"3600"`. */
|
|
34
|
+
cacheControl?: string;
|
|
35
|
+
/** Replace an object of the same name. Default false. */
|
|
36
|
+
upsert?: boolean;
|
|
37
|
+
};
|
|
38
|
+
export type UpdateOptions = Omit<UploadOptions, "upsert">;
|
|
39
|
+
export type UploadResult = {
|
|
40
|
+
id: string;
|
|
41
|
+
path: string;
|
|
42
|
+
fullPath: string;
|
|
43
|
+
};
|
|
44
|
+
export type ListOptions = {
|
|
45
|
+
/** 1–1,000; 100 by default. */
|
|
46
|
+
limit?: number;
|
|
47
|
+
/** 0–100,000. */
|
|
48
|
+
offset?: number;
|
|
49
|
+
/** Folders come first whatever the sort. Default name ascending. */
|
|
50
|
+
sortBy?: {
|
|
51
|
+
column?: "name" | "created_at" | "updated_at";
|
|
52
|
+
order?: "asc" | "desc";
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
/** `true` downloads under the object's own name, a string under that name. */
|
|
56
|
+
export type DownloadOption = {
|
|
57
|
+
download?: boolean | string;
|
|
58
|
+
};
|
package/dist/types.d.ts
CHANGED
|
@@ -81,10 +81,19 @@ export type OrderOptions = {
|
|
|
81
81
|
/** Where NULLs go. Omitted, SQLite's default: first ascending, last descending. */
|
|
82
82
|
nullsFirst?: boolean;
|
|
83
83
|
};
|
|
84
|
-
/**
|
|
84
|
+
/**
|
|
85
|
+
* The signed-in user, as naro Auth answers it (spec §13): dates are ISO
|
|
86
|
+
* strings. `email` is null for a user created with only a username
|
|
87
|
+
* (`auth.admin.createUser({ username, password })`).
|
|
88
|
+
*/
|
|
85
89
|
export type User = {
|
|
86
90
|
id: string;
|
|
87
|
-
email: string;
|
|
91
|
+
email: string | null;
|
|
92
|
+
/**
|
|
93
|
+
* Lowercased; null for a user without one. Absent from a backend whose
|
|
94
|
+
* Naro worker predates usernames (`naro backend enable` updates it).
|
|
95
|
+
*/
|
|
96
|
+
username?: string | null;
|
|
88
97
|
name: string;
|
|
89
98
|
emailVerified: boolean;
|
|
90
99
|
image?: string | null;
|
|
@@ -92,7 +101,7 @@ export type User = {
|
|
|
92
101
|
updatedAt: string;
|
|
93
102
|
[field: string]: unknown;
|
|
94
103
|
};
|
|
95
|
-
/**
|
|
104
|
+
/** The session, as naro Auth answers it. */
|
|
96
105
|
export type Session = {
|
|
97
106
|
id: string;
|
|
98
107
|
userId: string;
|
|
@@ -124,7 +133,16 @@ export type SignOutResponse = {
|
|
|
124
133
|
export type SignUpCredentials = {
|
|
125
134
|
email: string;
|
|
126
135
|
password: string;
|
|
127
|
-
/**
|
|
136
|
+
/**
|
|
137
|
+
* A name to sign in by as well as the email (spec 2026-09-26 §2.3): 3–30
|
|
138
|
+
* letters, digits, `_` and `.`, stored lowercased. One that is taken
|
|
139
|
+
* answers 409 USERNAME_TAKEN.
|
|
140
|
+
*/
|
|
141
|
+
username?: string;
|
|
142
|
+
/**
|
|
143
|
+
* Every user has a display name; omitted, the username is sent, or else
|
|
144
|
+
* the email's local part.
|
|
145
|
+
*/
|
|
128
146
|
name?: string;
|
|
129
147
|
/**
|
|
130
148
|
* Where the confirmation link sends the browser back to, when the project
|
|
@@ -133,8 +151,17 @@ export type SignUpCredentials = {
|
|
|
133
151
|
*/
|
|
134
152
|
redirectTo?: string;
|
|
135
153
|
};
|
|
154
|
+
/**
|
|
155
|
+
* An email and its password, or a username and its password (spec
|
|
156
|
+
* 2026-09-26 §2.3). A username is matched in any case.
|
|
157
|
+
*/
|
|
136
158
|
export type SignInCredentials = {
|
|
137
159
|
email: string;
|
|
160
|
+
username?: undefined;
|
|
161
|
+
password: string;
|
|
162
|
+
} | {
|
|
163
|
+
username: string;
|
|
164
|
+
email?: undefined;
|
|
138
165
|
password: string;
|
|
139
166
|
};
|
|
140
167
|
export type OAuthProvider = "google" | "github";
|
|
@@ -183,4 +210,59 @@ export type AuthActionResponse = {
|
|
|
183
210
|
data: null;
|
|
184
211
|
error: NaroError | null;
|
|
185
212
|
};
|
|
213
|
+
/**
|
|
214
|
+
* A user as `naro.auth.admin` answers it — never a password or a hash.
|
|
215
|
+
* `email` is null for a user created with only a username.
|
|
216
|
+
*/
|
|
217
|
+
export type AdminUser = {
|
|
218
|
+
id: string;
|
|
219
|
+
email: string | null;
|
|
220
|
+
/** Lowercased; null for a user without one. */
|
|
221
|
+
username: string | null;
|
|
222
|
+
emailVerified: boolean;
|
|
223
|
+
/** ISO 8601. */
|
|
224
|
+
createdAt: string;
|
|
225
|
+
};
|
|
226
|
+
/**
|
|
227
|
+
* `auth.admin.createUser()`: an email, a username or both, and a password
|
|
228
|
+
* of at least the project's minimum length (6 unless
|
|
229
|
+
* `naro backend auth password --min-length` changed it). The user is
|
|
230
|
+
* confirmed — signs in at once, even where sign-in waits for a confirmed
|
|
231
|
+
* email — unless `emailConfirm: false`, which needs an email. (Supabase's
|
|
232
|
+
* `email_confirm` is false by default; here confirmed is the default.)
|
|
233
|
+
*/
|
|
234
|
+
export type AdminUserAttributes = {
|
|
235
|
+
email?: string;
|
|
236
|
+
username?: string;
|
|
237
|
+
password: string;
|
|
238
|
+
emailConfirm?: boolean;
|
|
239
|
+
};
|
|
240
|
+
/** `auth.admin.listUsers()`: page 1 and 50 a page unless given; at most 1000. */
|
|
241
|
+
export type PageParams = {
|
|
242
|
+
page?: number;
|
|
243
|
+
perPage?: number;
|
|
244
|
+
};
|
|
245
|
+
export type AdminUserResponse = AuthResponse<{
|
|
246
|
+
user: AdminUser | null;
|
|
247
|
+
}>;
|
|
248
|
+
/** A page of users, newest first, and how many there are in all. */
|
|
249
|
+
export type AdminUserListResponse = AuthResponse<{
|
|
250
|
+
users: AdminUser[];
|
|
251
|
+
total: number;
|
|
252
|
+
}>;
|
|
253
|
+
/**
|
|
254
|
+
* The codes sign-up, sign-in and `auth.admin` answer that an app branches
|
|
255
|
+
* on. `NaroError.code` can be any other string too.
|
|
256
|
+
*/
|
|
257
|
+
export type AuthErrorCode =
|
|
258
|
+
/** Sign-ups are closed: only `auth.admin.createUser` makes users. */
|
|
259
|
+
"SIGNUPS_DISABLED"
|
|
260
|
+
/** Sign-up or `createUser` with a username someone has, in any case. */
|
|
261
|
+
| "USERNAME_TAKEN" | "USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL"
|
|
262
|
+
/** "Use at least {n} characters." — the project's minimum. */
|
|
263
|
+
| "PASSWORD_TOO_SHORT" | "PASSWORD_TOO_LONG" | "USERNAME_TOO_SHORT" | "USERNAME_TOO_LONG" | "INVALID_USERNAME" | "INVALID_EMAIL" | "INVALID_EMAIL_OR_PASSWORD" | "INVALID_USERNAME_OR_PASSWORD" | "EMAIL_NOT_VERIFIED" | "INVALID_SESSION"
|
|
264
|
+
/** `auth.admin` without the service key. */
|
|
265
|
+
| "PERMISSION_DENIED"
|
|
266
|
+
/** `auth.admin.deleteUser` with an id that is no user. */
|
|
267
|
+
| "USER_NOT_FOUND" | "RATE_LIMITED" | "WRITE_QUOTA_EXCEEDED";
|
|
186
268
|
export {};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "naro-js",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Auth and
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Auth, data and files for your Naro project, from the browser or the server.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"files": [
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
"vitest": "3.2.7",
|
|
26
26
|
"@next-deploy/backend-worker": "0.1.0",
|
|
27
27
|
"@next-deploy/config": "0.1.0",
|
|
28
|
-
"@next-deploy/
|
|
29
|
-
"@next-deploy/
|
|
28
|
+
"@next-deploy/data-core": "0.1.0",
|
|
29
|
+
"@next-deploy/sdk": "0.1.0"
|
|
30
30
|
},
|
|
31
31
|
"scripts": {
|
|
32
32
|
"build": "tsup && pnpm build:types",
|