@superzerodev/client 0.1.1
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/LICENSE +31 -0
- package/dist/auth.d.ts +94 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +99 -0
- package/dist/auth.js.map +1 -0
- package/dist/index.d.ts +58 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +57 -0
- package/dist/index.js.map +1 -0
- package/dist/push.d.ts +41 -0
- package/dist/push.d.ts.map +1 -0
- package/dist/push.js +43 -0
- package/dist/push.js.map +1 -0
- package/dist/transport.d.ts +33 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +123 -0
- package/dist/transport.js.map +1 -0
- package/dist/types.d.ts +82 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +10 -0
- package/dist/types.js.map +1 -0
- package/package.json +38 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
Copyright (c) 2026 mashihara. All rights reserved.
|
|
2
|
+
|
|
3
|
+
This software is proprietary. It is published on the npm registry so that
|
|
4
|
+
customers of the Superzero service can install and run it. Publication here
|
|
5
|
+
does not place it in the public domain and does not grant any open source
|
|
6
|
+
licence.
|
|
7
|
+
|
|
8
|
+
You may:
|
|
9
|
+
|
|
10
|
+
- install, copy and run this software for the purpose of using the
|
|
11
|
+
Superzero service, including inside automated build and deployment
|
|
12
|
+
systems;
|
|
13
|
+
- retain copies in your own dependency caches, lockfiles and internal
|
|
14
|
+
mirrors as needed for that purpose.
|
|
15
|
+
|
|
16
|
+
You may not, without prior written permission from the copyright holder:
|
|
17
|
+
|
|
18
|
+
- redistribute, sublicense, sell or publish this software or any part of
|
|
19
|
+
it, whether modified or not;
|
|
20
|
+
- modify, adapt, translate or create derivative works from it;
|
|
21
|
+
- decompile, disassemble or otherwise reverse engineer it, except to the
|
|
22
|
+
extent that applicable law expressly permits despite this restriction;
|
|
23
|
+
- remove or alter this notice.
|
|
24
|
+
|
|
25
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
26
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
27
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
28
|
+
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
29
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
|
30
|
+
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
31
|
+
SOFTWARE.
|
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 認証(メール / OAuth / SMS OTP)
|
|
3
|
+
*
|
|
4
|
+
* ★ ここはサーバーで動かすこと。鍵(SUPERZERO_KEY)が全能なので、
|
|
5
|
+
* ブラウザやモバイルアプリに配ってはいけない。理由は index.ts に書いてある。
|
|
6
|
+
*/
|
|
7
|
+
import type { Transport } from './transport.js';
|
|
8
|
+
import type { AuthorizationRequest, Session, Tokens, User } from './types.js';
|
|
9
|
+
export type OAuthProvider = 'google' | 'apple';
|
|
10
|
+
export declare class Auth {
|
|
11
|
+
#private;
|
|
12
|
+
constructor(transport: Transport);
|
|
13
|
+
/**
|
|
14
|
+
* メールとパスワードで登録する。
|
|
15
|
+
* ★ 弱いパスワードはサーバーが AUTH_PASSWORD_WEAK で断る。
|
|
16
|
+
* 手前で独自に検査しない(規則が2箇所になり、必ず食い違う)。
|
|
17
|
+
*/
|
|
18
|
+
signUp(input: {
|
|
19
|
+
email: string;
|
|
20
|
+
password: string;
|
|
21
|
+
}): Promise<Session>;
|
|
22
|
+
/**
|
|
23
|
+
* ★ 失敗はメールの有無で区別されない。
|
|
24
|
+
* 「そのメールは存在しません」と出したくなるが、出せば総当たりで
|
|
25
|
+
* 利用者の一覧を作られる。サーバーが意図的に同じ形で返している。
|
|
26
|
+
*/
|
|
27
|
+
signIn(input: {
|
|
28
|
+
email: string;
|
|
29
|
+
password: string;
|
|
30
|
+
}): Promise<Session>;
|
|
31
|
+
/**
|
|
32
|
+
* アクセストークンを取り直す。
|
|
33
|
+
* ★ 使い終わった refreshToken は無効になる。返ってきた新しいほうを保存すること。
|
|
34
|
+
* 古いものを使い回すと、セッションごと失効する(再利用の検出)。
|
|
35
|
+
*/
|
|
36
|
+
refresh(refreshToken: string): Promise<Tokens>;
|
|
37
|
+
/** ★ 知らないトークンでも成功する。存在の有無を漏らさないため */
|
|
38
|
+
signOut(refreshToken: string): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* アクセストークンから利用者を引く。
|
|
41
|
+
* ★ 署名と有効期限はサーバーが検証する。ここで JWT を自前で開かないこと。
|
|
42
|
+
*/
|
|
43
|
+
user(accessToken: string): Promise<User>;
|
|
44
|
+
readonly sms: SmsAuth;
|
|
45
|
+
readonly oauth: OAuthAuth;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* SMS ワンタイムコード。
|
|
49
|
+
*
|
|
50
|
+
* ★ Free プランでは閉じている。叩くと BILLING_REQUIRED(HTTP 402)が返る。
|
|
51
|
+
* 費用が出て、かつ SMS pumping の標的になるため。
|
|
52
|
+
*/
|
|
53
|
+
export declare class SmsAuth {
|
|
54
|
+
#private;
|
|
55
|
+
constructor(transport: () => Transport);
|
|
56
|
+
/**
|
|
57
|
+
* コードを送る。
|
|
58
|
+
*
|
|
59
|
+
* ★ 応答に「その番号が登録済みか」は含まれない。含めれば列挙の手掛かりになる。
|
|
60
|
+
* ★ 断られる理由は国・回数・リスク・支出上限のいずれかで、
|
|
61
|
+
* どれもエラーの code で区別できる(AUTH_SMS_* / BILLING_*)。
|
|
62
|
+
*/
|
|
63
|
+
start(input: {
|
|
64
|
+
phone: string;
|
|
65
|
+
}): Promise<{
|
|
66
|
+
expiresInSeconds: number;
|
|
67
|
+
}>;
|
|
68
|
+
/** 成功するとトークンが返る。利用者が居なければその場で作られる */
|
|
69
|
+
verify(input: {
|
|
70
|
+
phone: string;
|
|
71
|
+
code: string;
|
|
72
|
+
}): Promise<Tokens>;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* OAuth(Google / Apple)。
|
|
76
|
+
*
|
|
77
|
+
* ★ 2段階で使う。start() が返す url へ利用者を送り、
|
|
78
|
+
* プロバイダから戻ってきた state と code を callback() に渡す。
|
|
79
|
+
* code_verifier も nonce もサーバーが持つので、アプリ側で保管しない。
|
|
80
|
+
*/
|
|
81
|
+
export declare class OAuthAuth {
|
|
82
|
+
#private;
|
|
83
|
+
constructor(transport: () => Transport);
|
|
84
|
+
start(provider: OAuthProvider): Promise<AuthorizationRequest>;
|
|
85
|
+
/**
|
|
86
|
+
* ★ Apple は form_post で戻る。Google は既定でクエリだが、
|
|
87
|
+
* data-plane はどちらも同じ形で受けるので、アプリ側の分岐は要らない。
|
|
88
|
+
*/
|
|
89
|
+
callback(input: {
|
|
90
|
+
state: string;
|
|
91
|
+
code: string;
|
|
92
|
+
}): Promise<Tokens>;
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC/C,OAAO,KAAK,EAAE,oBAAoB,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAE7E,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,OAAO,CAAA;AAE9C,qBAAa,IAAI;;gBAGH,SAAS,EAAE,SAAS;IAIhC;;;;OAIG;IACG,MAAM,CAAC,KAAK,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAI1E;;;;OAIG;IACG,MAAM,CAAC,KAAK,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAI1E;;;;OAIG;IACG,OAAO,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAIpD,qCAAqC;IAC/B,OAAO,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlD;;;OAGG;IACG,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI9C,QAAQ,CAAC,GAAG,UAAqC;IACjD,QAAQ,CAAC,KAAK,YAAuC;CACtD;AAED;;;;;GAKG;AACH,qBAAa,OAAO;;gBAGN,SAAS,EAAE,MAAM,SAAS;IAItC;;;;;;OAMG;IACG,KAAK,CAAC,KAAK,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,gBAAgB,EAAE,MAAM,CAAA;KAAE,CAAC;IAI5E,qCAAqC;IAC/B,MAAM,CAAC,KAAK,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC;CAGtE;AAED;;;;;;GAMG;AACH,qBAAa,SAAS;;gBAGR,SAAS,EAAE,MAAM,SAAS;IAIhC,KAAK,CAAC,QAAQ,EAAE,aAAa,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAOnE;;;OAGG;IACG,QAAQ,CAAC,KAAK,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC;CAGxE"}
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 認証(メール / OAuth / SMS OTP)
|
|
3
|
+
*
|
|
4
|
+
* ★ ここはサーバーで動かすこと。鍵(SUPERZERO_KEY)が全能なので、
|
|
5
|
+
* ブラウザやモバイルアプリに配ってはいけない。理由は index.ts に書いてある。
|
|
6
|
+
*/
|
|
7
|
+
export class Auth {
|
|
8
|
+
#transport;
|
|
9
|
+
constructor(transport) {
|
|
10
|
+
this.#transport = transport;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* メールとパスワードで登録する。
|
|
14
|
+
* ★ 弱いパスワードはサーバーが AUTH_PASSWORD_WEAK で断る。
|
|
15
|
+
* 手前で独自に検査しない(規則が2箇所になり、必ず食い違う)。
|
|
16
|
+
*/
|
|
17
|
+
async signUp(input) {
|
|
18
|
+
return this.#transport.call('POST', '/v1/auth/signup', input);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* ★ 失敗はメールの有無で区別されない。
|
|
22
|
+
* 「そのメールは存在しません」と出したくなるが、出せば総当たりで
|
|
23
|
+
* 利用者の一覧を作られる。サーバーが意図的に同じ形で返している。
|
|
24
|
+
*/
|
|
25
|
+
async signIn(input) {
|
|
26
|
+
return this.#transport.call('POST', '/v1/auth/signin', input);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* アクセストークンを取り直す。
|
|
30
|
+
* ★ 使い終わった refreshToken は無効になる。返ってきた新しいほうを保存すること。
|
|
31
|
+
* 古いものを使い回すと、セッションごと失効する(再利用の検出)。
|
|
32
|
+
*/
|
|
33
|
+
async refresh(refreshToken) {
|
|
34
|
+
return this.#transport.call('POST', '/v1/auth/refresh', { refreshToken });
|
|
35
|
+
}
|
|
36
|
+
/** ★ 知らないトークンでも成功する。存在の有無を漏らさないため */
|
|
37
|
+
async signOut(refreshToken) {
|
|
38
|
+
await this.#transport.call('POST', '/v1/auth/signout', { refreshToken });
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* アクセストークンから利用者を引く。
|
|
42
|
+
* ★ 署名と有効期限はサーバーが検証する。ここで JWT を自前で開かないこと。
|
|
43
|
+
*/
|
|
44
|
+
async user(accessToken) {
|
|
45
|
+
return this.#transport.callWithAccessToken('GET', '/v1/auth/user', accessToken);
|
|
46
|
+
}
|
|
47
|
+
sms = new SmsAuth(() => this.#transport);
|
|
48
|
+
oauth = new OAuthAuth(() => this.#transport);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* SMS ワンタイムコード。
|
|
52
|
+
*
|
|
53
|
+
* ★ Free プランでは閉じている。叩くと BILLING_REQUIRED(HTTP 402)が返る。
|
|
54
|
+
* 費用が出て、かつ SMS pumping の標的になるため。
|
|
55
|
+
*/
|
|
56
|
+
export class SmsAuth {
|
|
57
|
+
#transport;
|
|
58
|
+
constructor(transport) {
|
|
59
|
+
this.#transport = transport;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* コードを送る。
|
|
63
|
+
*
|
|
64
|
+
* ★ 応答に「その番号が登録済みか」は含まれない。含めれば列挙の手掛かりになる。
|
|
65
|
+
* ★ 断られる理由は国・回数・リスク・支出上限のいずれかで、
|
|
66
|
+
* どれもエラーの code で区別できる(AUTH_SMS_* / BILLING_*)。
|
|
67
|
+
*/
|
|
68
|
+
async start(input) {
|
|
69
|
+
return this.#transport().call('POST', '/v1/auth/sms/start', input);
|
|
70
|
+
}
|
|
71
|
+
/** 成功するとトークンが返る。利用者が居なければその場で作られる */
|
|
72
|
+
async verify(input) {
|
|
73
|
+
return this.#transport().call('POST', '/v1/auth/sms/verify', input);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* OAuth(Google / Apple)。
|
|
78
|
+
*
|
|
79
|
+
* ★ 2段階で使う。start() が返す url へ利用者を送り、
|
|
80
|
+
* プロバイダから戻ってきた state と code を callback() に渡す。
|
|
81
|
+
* code_verifier も nonce もサーバーが持つので、アプリ側で保管しない。
|
|
82
|
+
*/
|
|
83
|
+
export class OAuthAuth {
|
|
84
|
+
#transport;
|
|
85
|
+
constructor(transport) {
|
|
86
|
+
this.#transport = transport;
|
|
87
|
+
}
|
|
88
|
+
async start(provider) {
|
|
89
|
+
return this.#transport().call('POST', `/v1/auth/oauth/${provider}/start`);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* ★ Apple は form_post で戻る。Google は既定でクエリだが、
|
|
93
|
+
* data-plane はどちらも同じ形で受けるので、アプリ側の分岐は要らない。
|
|
94
|
+
*/
|
|
95
|
+
async callback(input) {
|
|
96
|
+
return this.#transport().call('POST', '/v1/auth/oauth/callback', input);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=auth.js.map
|
package/dist/auth.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,MAAM,OAAO,IAAI;IACN,UAAU,CAAW;IAE9B,YAAY,SAAoB;QAC9B,IAAI,CAAC,UAAU,GAAG,SAAS,CAAA;IAC7B,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,MAAM,CAAC,KAA0C;QACrD,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,CAAU,MAAM,EAAE,iBAAiB,EAAE,KAAK,CAAC,CAAA;IACxE,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,MAAM,CAAC,KAA0C;QACrD,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,CAAU,MAAM,EAAE,iBAAiB,EAAE,KAAK,CAAC,CAAA;IACxE,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,OAAO,CAAC,YAAoB;QAChC,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,CAAS,MAAM,EAAE,kBAAkB,EAAE,EAAE,YAAY,EAAE,CAAC,CAAA;IACnF,CAAC;IAED,qCAAqC;IACrC,KAAK,CAAC,OAAO,CAAC,YAAoB;QAChC,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,kBAAkB,EAAE,EAAE,YAAY,EAAE,CAAC,CAAA;IAC1E,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,IAAI,CAAC,WAAmB;QAC5B,OAAO,IAAI,CAAC,UAAU,CAAC,mBAAmB,CAAO,KAAK,EAAE,eAAe,EAAE,WAAW,CAAC,CAAA;IACvF,CAAC;IAEQ,GAAG,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;IACxC,KAAK,GAAG,IAAI,SAAS,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;CACtD;AAED;;;;;GAKG;AACH,MAAM,OAAO,OAAO;IACT,UAAU,CAAiB;IAEpC,YAAY,SAA0B;QACpC,IAAI,CAAC,UAAU,GAAG,SAAS,CAAA;IAC7B,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,KAAK,CAAC,KAAwB;QAClC,OAAO,IAAI,CAAC,UAAU,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,oBAAoB,EAAE,KAAK,CAAC,CAAA;IACpE,CAAC;IAED,qCAAqC;IACrC,KAAK,CAAC,MAAM,CAAC,KAAsC;QACjD,OAAO,IAAI,CAAC,UAAU,EAAE,CAAC,IAAI,CAAS,MAAM,EAAE,qBAAqB,EAAE,KAAK,CAAC,CAAA;IAC7E,CAAC;CACF;AAED;;;;;;GAMG;AACH,MAAM,OAAO,SAAS;IACX,UAAU,CAAiB;IAEpC,YAAY,SAA0B;QACpC,IAAI,CAAC,UAAU,GAAG,SAAS,CAAA;IAC7B,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,QAAuB;QACjC,OAAO,IAAI,CAAC,UAAU,EAAE,CAAC,IAAI,CAC3B,MAAM,EACN,kBAAkB,QAAQ,QAAQ,CACnC,CAAA;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,QAAQ,CAAC,KAAsC;QACnD,OAAO,IAAI,CAAC,UAAU,EAAE,CAAC,IAAI,CAAS,MAAM,EAAE,yBAAyB,EAAE,KAAK,CAAC,CAAA;IACjF,CAAC;CACF"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Superzero の統一クライアント(P0設計 §5.8)
|
|
3
|
+
*
|
|
4
|
+
* 顧客のアプリが使う唯一のライブラリ。「1つで完結」の体験はここに集約される。
|
|
5
|
+
*
|
|
6
|
+
* ```ts
|
|
7
|
+
* import { createClient } from '@superzerodev/client'
|
|
8
|
+
*
|
|
9
|
+
* const superzero = createClient() // 環境変数から読む。引数は要らない
|
|
10
|
+
*
|
|
11
|
+
* const { user, tokens } = await superzero.auth.signIn({ email, password })
|
|
12
|
+
* await superzero.push.send({ to: { userIds: [user.id] }, title: 'Hi', body: '...' })
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* ★ 引数ゼロで動くのが要点。`superzero apply` が SUPERZERO_URL と SUPERZERO_KEY を
|
|
16
|
+
* アプリの環境に注入するので、設定を書き写す手順が存在しない。
|
|
17
|
+
* 書き写す手順があると、そこが必ずずれる。
|
|
18
|
+
*
|
|
19
|
+
* ★★ この鍵はサーバーだけで使うこと。ブラウザにも、モバイルアプリにも入れない。★★
|
|
20
|
+
*
|
|
21
|
+
* SUPERZERO_KEY は環境そのものの鍵で、できることに区別が無い。
|
|
22
|
+
* これを持っていれば、誰でも全利用者へ Push を送れるし、任意のアクセストークンから
|
|
23
|
+
* 利用者を引ける。Supabase の anon key のような「公開してよい鍵」は
|
|
24
|
+
* data-plane にまだ無い。
|
|
25
|
+
*
|
|
26
|
+
* モバイルアプリから使いたい場合は、アプリ → あなたのサーバー → Superzero と
|
|
27
|
+
* 経由させること。あなたのサーバーは、その利用者が本当にその操作をしてよいかを
|
|
28
|
+
* 知っている唯一の場所でもある。
|
|
29
|
+
*
|
|
30
|
+
* ★ DB はここに無い。`superzero apply` が DATABASE_URL を注入しているので、
|
|
31
|
+
* pg などの好きなクライアントでそのまま繋がる。薄い層をもう1枚重ねる価値より、
|
|
32
|
+
* 既存の道具がそのまま使えることを採った。
|
|
33
|
+
*/
|
|
34
|
+
import { Auth } from './auth.js';
|
|
35
|
+
import { Push } from './push.js';
|
|
36
|
+
import { type TransportOptions } from './transport.js';
|
|
37
|
+
export type { OAuthProvider } from './auth.js';
|
|
38
|
+
export type { TransportOptions } from './transport.js';
|
|
39
|
+
export * from './types.js';
|
|
40
|
+
export { Auth, Push };
|
|
41
|
+
/** SuperzeroError をそのまま投げる。code と suggestedFix を読んで分岐できる */
|
|
42
|
+
export { SuperzeroError, isSuperzeroError } from '@superzerodev/core';
|
|
43
|
+
export interface SuperzeroClient {
|
|
44
|
+
auth: Auth;
|
|
45
|
+
push: Push;
|
|
46
|
+
}
|
|
47
|
+
export type CreateClientOptions = TransportOptions;
|
|
48
|
+
/**
|
|
49
|
+
* クライアントを作る。
|
|
50
|
+
*
|
|
51
|
+
* @param options 省略時は環境変数 SUPERZERO_URL と SUPERZERO_KEY を読む。
|
|
52
|
+
* どちらも `superzero apply` が注入する。
|
|
53
|
+
*
|
|
54
|
+
* ★ 足りなければここで throw する。作れてしまうと、最初の呼び出しが 401 になり、
|
|
55
|
+
* 「鍵が違う」のか「渡していない」のかが区別できない。
|
|
56
|
+
*/
|
|
57
|
+
export declare function createClient(options?: CreateClientOptions): SuperzeroClient;
|
|
58
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAa,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAEjE,YAAY,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA;AAC9C,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AACtD,cAAc,YAAY,CAAA;AAC1B,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAA;AAErB,4DAA4D;AAC5D,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAErE,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,IAAI,CAAA;IACV,IAAI,EAAE,IAAI,CAAA;CACX;AAED,MAAM,MAAM,mBAAmB,GAAG,gBAAgB,CAAA;AAElD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAE,mBAAwB,GAAG,eAAe,CAM/E"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Superzero の統一クライアント(P0設計 §5.8)
|
|
3
|
+
*
|
|
4
|
+
* 顧客のアプリが使う唯一のライブラリ。「1つで完結」の体験はここに集約される。
|
|
5
|
+
*
|
|
6
|
+
* ```ts
|
|
7
|
+
* import { createClient } from '@superzerodev/client'
|
|
8
|
+
*
|
|
9
|
+
* const superzero = createClient() // 環境変数から読む。引数は要らない
|
|
10
|
+
*
|
|
11
|
+
* const { user, tokens } = await superzero.auth.signIn({ email, password })
|
|
12
|
+
* await superzero.push.send({ to: { userIds: [user.id] }, title: 'Hi', body: '...' })
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* ★ 引数ゼロで動くのが要点。`superzero apply` が SUPERZERO_URL と SUPERZERO_KEY を
|
|
16
|
+
* アプリの環境に注入するので、設定を書き写す手順が存在しない。
|
|
17
|
+
* 書き写す手順があると、そこが必ずずれる。
|
|
18
|
+
*
|
|
19
|
+
* ★★ この鍵はサーバーだけで使うこと。ブラウザにも、モバイルアプリにも入れない。★★
|
|
20
|
+
*
|
|
21
|
+
* SUPERZERO_KEY は環境そのものの鍵で、できることに区別が無い。
|
|
22
|
+
* これを持っていれば、誰でも全利用者へ Push を送れるし、任意のアクセストークンから
|
|
23
|
+
* 利用者を引ける。Supabase の anon key のような「公開してよい鍵」は
|
|
24
|
+
* data-plane にまだ無い。
|
|
25
|
+
*
|
|
26
|
+
* モバイルアプリから使いたい場合は、アプリ → あなたのサーバー → Superzero と
|
|
27
|
+
* 経由させること。あなたのサーバーは、その利用者が本当にその操作をしてよいかを
|
|
28
|
+
* 知っている唯一の場所でもある。
|
|
29
|
+
*
|
|
30
|
+
* ★ DB はここに無い。`superzero apply` が DATABASE_URL を注入しているので、
|
|
31
|
+
* pg などの好きなクライアントでそのまま繋がる。薄い層をもう1枚重ねる価値より、
|
|
32
|
+
* 既存の道具がそのまま使えることを採った。
|
|
33
|
+
*/
|
|
34
|
+
import { Auth } from './auth.js';
|
|
35
|
+
import { Push } from './push.js';
|
|
36
|
+
import { Transport } from './transport.js';
|
|
37
|
+
export * from './types.js';
|
|
38
|
+
export { Auth, Push };
|
|
39
|
+
/** SuperzeroError をそのまま投げる。code と suggestedFix を読んで分岐できる */
|
|
40
|
+
export { SuperzeroError, isSuperzeroError } from '@superzerodev/core';
|
|
41
|
+
/**
|
|
42
|
+
* クライアントを作る。
|
|
43
|
+
*
|
|
44
|
+
* @param options 省略時は環境変数 SUPERZERO_URL と SUPERZERO_KEY を読む。
|
|
45
|
+
* どちらも `superzero apply` が注入する。
|
|
46
|
+
*
|
|
47
|
+
* ★ 足りなければここで throw する。作れてしまうと、最初の呼び出しが 401 になり、
|
|
48
|
+
* 「鍵が違う」のか「渡していない」のかが区別できない。
|
|
49
|
+
*/
|
|
50
|
+
export function createClient(options = {}) {
|
|
51
|
+
const transport = new Transport(options);
|
|
52
|
+
return {
|
|
53
|
+
auth: new Auth(transport),
|
|
54
|
+
push: new Push(transport),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,SAAS,EAAyB,MAAM,gBAAgB,CAAA;AAIjE,cAAc,YAAY,CAAA;AAC1B,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAA;AAErB,4DAA4D;AAC5D,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AASrE;;;;;;;;GAQG;AACH,MAAM,UAAU,YAAY,CAAC,UAA+B,EAAE;IAC5D,MAAM,SAAS,GAAG,IAAI,SAAS,CAAC,OAAO,CAAC,CAAA;IACxC,OAAO;QACL,IAAI,EAAE,IAAI,IAAI,CAAC,SAAS,CAAC;QACzB,IAAI,EAAE,IAAI,IAAI,CAAC,SAAS,CAAC;KAC1B,CAAA;AACH,CAAC"}
|
package/dist/push.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* モバイル Push(APNs / FCM)
|
|
3
|
+
*
|
|
4
|
+
* ★ 送信は必ずサーバーから行う。この鍵は誰にでも送れるので、
|
|
5
|
+
* アプリに埋めると全利用者へ好きな通知を出せる。
|
|
6
|
+
*/
|
|
7
|
+
import type { Transport } from './transport.js';
|
|
8
|
+
import type { PushMessage, RegisteredToken, SendResult } from './types.js';
|
|
9
|
+
export declare class Push {
|
|
10
|
+
#private;
|
|
11
|
+
constructor(transport: Transport);
|
|
12
|
+
/**
|
|
13
|
+
* 端末を登録する。
|
|
14
|
+
*
|
|
15
|
+
* ★ accessToken が要る。任意の userId を受け取る形にしていないのは、
|
|
16
|
+
* それを許すと他人宛の通知を購読できてしまうため。
|
|
17
|
+
* 紐づく利用者はサーバーがトークンから決める。
|
|
18
|
+
*/
|
|
19
|
+
registerToken(input: {
|
|
20
|
+
accessToken: string;
|
|
21
|
+
platform: 'ios' | 'android';
|
|
22
|
+
token: string;
|
|
23
|
+
}): Promise<RegisteredToken>;
|
|
24
|
+
/**
|
|
25
|
+
* 端末の登録を消す(ログアウト時など)。
|
|
26
|
+
*
|
|
27
|
+
* ★ 無効になった端末トークンは、送信時にサーバーが自分で消す
|
|
28
|
+
* (APNs の 410 / BadDeviceToken、FCM の UNREGISTERED)。
|
|
29
|
+
* ここで消すのは「この端末では受け取らない」という利用者の意思のほう。
|
|
30
|
+
*/
|
|
31
|
+
removeToken(token: string): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* 送る。
|
|
34
|
+
*
|
|
35
|
+
* ★ 結果は「送れたか」で終わらない。requested / delivered / failed と、
|
|
36
|
+
* 端末ごとの outcomes が返る。1台に届かなかったことを、
|
|
37
|
+
* 全体の失敗として扱わないため。
|
|
38
|
+
*/
|
|
39
|
+
send(message: PushMessage): Promise<SendResult>;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=push.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../src/push.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC/C,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAE1E,qBAAa,IAAI;;gBAGH,SAAS,EAAE,SAAS;IAIhC;;;;;;OAMG;IACG,aAAa,CAAC,KAAK,EAAE;QACzB,WAAW,EAAE,MAAM,CAAA;QACnB,QAAQ,EAAE,KAAK,GAAG,SAAS,CAAA;QAC3B,KAAK,EAAE,MAAM,CAAA;KACd,GAAG,OAAO,CAAC,eAAe,CAAC;IAI5B;;;;;;OAMG;IACG,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI/C;;;;;;OAMG;IACG,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;CAGtD"}
|
package/dist/push.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* モバイル Push(APNs / FCM)
|
|
3
|
+
*
|
|
4
|
+
* ★ 送信は必ずサーバーから行う。この鍵は誰にでも送れるので、
|
|
5
|
+
* アプリに埋めると全利用者へ好きな通知を出せる。
|
|
6
|
+
*/
|
|
7
|
+
export class Push {
|
|
8
|
+
#transport;
|
|
9
|
+
constructor(transport) {
|
|
10
|
+
this.#transport = transport;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* 端末を登録する。
|
|
14
|
+
*
|
|
15
|
+
* ★ accessToken が要る。任意の userId を受け取る形にしていないのは、
|
|
16
|
+
* それを許すと他人宛の通知を購読できてしまうため。
|
|
17
|
+
* 紐づく利用者はサーバーがトークンから決める。
|
|
18
|
+
*/
|
|
19
|
+
async registerToken(input) {
|
|
20
|
+
return this.#transport.call('POST', '/v1/push/tokens', input);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* 端末の登録を消す(ログアウト時など)。
|
|
24
|
+
*
|
|
25
|
+
* ★ 無効になった端末トークンは、送信時にサーバーが自分で消す
|
|
26
|
+
* (APNs の 410 / BadDeviceToken、FCM の UNREGISTERED)。
|
|
27
|
+
* ここで消すのは「この端末では受け取らない」という利用者の意思のほう。
|
|
28
|
+
*/
|
|
29
|
+
async removeToken(token) {
|
|
30
|
+
await this.#transport.call('DELETE', `/v1/push/tokens/${encodeURIComponent(token)}`);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* 送る。
|
|
34
|
+
*
|
|
35
|
+
* ★ 結果は「送れたか」で終わらない。requested / delivered / failed と、
|
|
36
|
+
* 端末ごとの outcomes が返る。1台に届かなかったことを、
|
|
37
|
+
* 全体の失敗として扱わないため。
|
|
38
|
+
*/
|
|
39
|
+
async send(message) {
|
|
40
|
+
return this.#transport.call('POST', '/v1/push/send', message);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=push.js.map
|
package/dist/push.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"push.js","sourceRoot":"","sources":["../src/push.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,MAAM,OAAO,IAAI;IACN,UAAU,CAAW;IAE9B,YAAY,SAAoB;QAC9B,IAAI,CAAC,UAAU,GAAG,SAAS,CAAA;IAC7B,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,aAAa,CAAC,KAInB;QACC,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,CAAkB,MAAM,EAAE,iBAAiB,EAAE,KAAK,CAAC,CAAA;IAChF,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,WAAW,CAAC,KAAa;QAC7B,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,mBAAmB,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;IACtF,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,IAAI,CAAC,OAAoB;QAC7B,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,CAAa,MAAM,EAAE,eAAe,EAAE,OAAO,CAAC,CAAA;IAC3E,CAAC;CACF"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* data-plane への HTTP 呼び出し(P0設計 §5.8)
|
|
3
|
+
*
|
|
4
|
+
* ★ ここだけが fetch を知る。auth も push も、この層より下を見ない。
|
|
5
|
+
* 経路の作法(鍵の載せ方・タイムアウト・エラーの復元)を1箇所に閉じるため。
|
|
6
|
+
*
|
|
7
|
+
* ★ サーバーのエラーは必ず SuperzeroError に戻す。
|
|
8
|
+
* suggestedFix を落とすと、この製品の核(エージェントが自力で直せる)が消える。
|
|
9
|
+
* CLI が同じことをしている(apps/cli/src/client.ts)。形を揃えてある。
|
|
10
|
+
*/
|
|
11
|
+
export interface TransportOptions {
|
|
12
|
+
/** data-plane の URL。既定は環境変数 SUPERZERO_URL */
|
|
13
|
+
url?: string | undefined;
|
|
14
|
+
/** 環境の API キー。既定は環境変数 SUPERZERO_KEY */
|
|
15
|
+
key?: string | undefined;
|
|
16
|
+
/** 1回の呼び出しの上限。既定 30 秒 */
|
|
17
|
+
timeoutMs?: number | undefined;
|
|
18
|
+
/** テストと計測のための差し替え口 */
|
|
19
|
+
fetchImpl?: typeof fetch | undefined;
|
|
20
|
+
}
|
|
21
|
+
export declare const DEFAULT_TIMEOUT_MS = 30000;
|
|
22
|
+
export declare class Transport {
|
|
23
|
+
#private;
|
|
24
|
+
constructor(options?: TransportOptions);
|
|
25
|
+
call<T>(method: string, path: string, body?: unknown): Promise<T>;
|
|
26
|
+
/**
|
|
27
|
+
* ★ ヘッダに載せる値は、この層より外へ出さない。
|
|
28
|
+
* アクセストークンは本文かこの専用ヘッダでだけ運ぶ。
|
|
29
|
+
* クエリ文字列に載せるとプロキシとアクセスログに平文で残る。
|
|
30
|
+
*/
|
|
31
|
+
callWithAccessToken<T>(method: string, path: string, accessToken: string): Promise<T>;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,MAAM,WAAW,gBAAgB;IAC/B,6CAA6C;IAC7C,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACxB,uCAAuC;IACvC,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACxB,yBAAyB;IACzB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC9B,sBAAsB;IACtB,SAAS,CAAC,EAAE,OAAO,KAAK,GAAG,SAAS,CAAA;CACrC;AAED,eAAO,MAAM,kBAAkB,QAAS,CAAA;AAExC,qBAAa,SAAS;;gBAMR,OAAO,GAAE,gBAAqB;IAmBpC,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAqBvE;;;;OAIG;IACG,mBAAmB,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;CAmB5F"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* data-plane への HTTP 呼び出し(P0設計 §5.8)
|
|
3
|
+
*
|
|
4
|
+
* ★ ここだけが fetch を知る。auth も push も、この層より下を見ない。
|
|
5
|
+
* 経路の作法(鍵の載せ方・タイムアウト・エラーの復元)を1箇所に閉じるため。
|
|
6
|
+
*
|
|
7
|
+
* ★ サーバーのエラーは必ず SuperzeroError に戻す。
|
|
8
|
+
* suggestedFix を落とすと、この製品の核(エージェントが自力で直せる)が消える。
|
|
9
|
+
* CLI が同じことをしている(apps/cli/src/client.ts)。形を揃えてある。
|
|
10
|
+
*/
|
|
11
|
+
import { SuperzeroError } from '@superzerodev/core';
|
|
12
|
+
export const DEFAULT_TIMEOUT_MS = 30_000;
|
|
13
|
+
export class Transport {
|
|
14
|
+
#url;
|
|
15
|
+
#key;
|
|
16
|
+
#timeoutMs;
|
|
17
|
+
#fetch;
|
|
18
|
+
constructor(options = {}) {
|
|
19
|
+
const url = options.url ?? process.env['SUPERZERO_URL'];
|
|
20
|
+
const key = options.key ?? process.env['SUPERZERO_KEY'];
|
|
21
|
+
/**
|
|
22
|
+
* ★ 足りないまま作らせない。
|
|
23
|
+
* 作れてしまうと、最初の呼び出しで 401 になり、
|
|
24
|
+
* 「鍵が違う」のか「そもそも渡していない」のかが区別できない。
|
|
25
|
+
* どちらの名前も、apply が自動で注入するもの(adapter-auth)。
|
|
26
|
+
*/
|
|
27
|
+
if (!url)
|
|
28
|
+
throw missingConfig('SUPERZERO_URL', 'url');
|
|
29
|
+
if (!key)
|
|
30
|
+
throw missingConfig('SUPERZERO_KEY', 'key');
|
|
31
|
+
this.#url = url.replace(/\/$/, '');
|
|
32
|
+
this.#key = key;
|
|
33
|
+
this.#timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
34
|
+
this.#fetch = options.fetchImpl ?? fetch;
|
|
35
|
+
}
|
|
36
|
+
async call(method, path, body) {
|
|
37
|
+
let res;
|
|
38
|
+
try {
|
|
39
|
+
res = await this.#fetch(`${this.#url}${path}`, {
|
|
40
|
+
method,
|
|
41
|
+
headers: {
|
|
42
|
+
accept: 'application/json',
|
|
43
|
+
authorization: `Bearer ${this.#key}`,
|
|
44
|
+
...(body === undefined ? {} : { 'content-type': 'application/json' }),
|
|
45
|
+
},
|
|
46
|
+
...(body === undefined ? {} : { body: JSON.stringify(body) }),
|
|
47
|
+
signal: AbortSignal.timeout(this.#timeoutMs),
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
throw unreachable(this.#url, err);
|
|
52
|
+
}
|
|
53
|
+
if (!res.ok)
|
|
54
|
+
throw await fromResponse(res);
|
|
55
|
+
return (await res.json());
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* ★ ヘッダに載せる値は、この層より外へ出さない。
|
|
59
|
+
* アクセストークンは本文かこの専用ヘッダでだけ運ぶ。
|
|
60
|
+
* クエリ文字列に載せるとプロキシとアクセスログに平文で残る。
|
|
61
|
+
*/
|
|
62
|
+
async callWithAccessToken(method, path, accessToken) {
|
|
63
|
+
let res;
|
|
64
|
+
try {
|
|
65
|
+
res = await this.#fetch(`${this.#url}${path}`, {
|
|
66
|
+
method,
|
|
67
|
+
headers: {
|
|
68
|
+
accept: 'application/json',
|
|
69
|
+
authorization: `Bearer ${this.#key}`,
|
|
70
|
+
'x-superzero-access-token': accessToken,
|
|
71
|
+
},
|
|
72
|
+
signal: AbortSignal.timeout(this.#timeoutMs),
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
throw unreachable(this.#url, err);
|
|
77
|
+
}
|
|
78
|
+
if (!res.ok)
|
|
79
|
+
throw await fromResponse(res);
|
|
80
|
+
return (await res.json());
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function missingConfig(envName, optionName) {
|
|
84
|
+
return new SuperzeroError({
|
|
85
|
+
code: 'CONFIG_INVALID',
|
|
86
|
+
message: `${envName} is not set, so the Superzero client cannot be created.`,
|
|
87
|
+
suggestedFix: {
|
|
88
|
+
description: `Superzero injects ${envName} into apps it deploys, so this usually means the app is ` +
|
|
89
|
+
`running outside a Superzero environment. Pass it explicitly as "${optionName}" to ` +
|
|
90
|
+
`createClient(), or set ${envName} in the environment.`,
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
/** サーバーの正規化エラーを復元する。suggestedFix を落とさない */
|
|
95
|
+
async function fromResponse(res) {
|
|
96
|
+
let body = {};
|
|
97
|
+
try {
|
|
98
|
+
body = (await res.json());
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
// JSON でない応答(プロキシのエラーページなど)
|
|
102
|
+
}
|
|
103
|
+
return new SuperzeroError({
|
|
104
|
+
code: body.code ?? 'PROVIDER_UNEXPECTED_ERROR',
|
|
105
|
+
message: body.message ?? `Superzero returned ${res.status} ${res.statusText}.`,
|
|
106
|
+
...(body.cause ? { cause: body.cause } : {}),
|
|
107
|
+
...(body.suggestedFix ? { suggestedFix: body.suggestedFix } : {}),
|
|
108
|
+
...(body.docsUrl ? { docsUrl: body.docsUrl } : {}),
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
/** 接続できない。設定ミスか、環境がまだ立っていないかのどちらか */
|
|
112
|
+
function unreachable(url, err) {
|
|
113
|
+
return new SuperzeroError({
|
|
114
|
+
code: 'DATA_PLANE_UNREACHABLE',
|
|
115
|
+
message: `Could not reach Superzero at ${url}: ${err instanceof Error ? err.message : String(err)}`,
|
|
116
|
+
suggestedFix: {
|
|
117
|
+
description: 'Check that SUPERZERO_URL points at a running environment. If the environment was ' +
|
|
118
|
+
'just created, run apply and wait for it to report ready.',
|
|
119
|
+
command: 'superzero verify',
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=transport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 顧客のアプリが読む型。
|
|
3
|
+
*
|
|
4
|
+
* ★ auth-core / push-gateway の型をそのまま re-export しない。
|
|
5
|
+
* あちらはサーバー内部の型で、保管用のフィールド(passwordHash など)や
|
|
6
|
+
* Date が混ざる。ここは HTTP を越えてきた形(JSON)を書く。
|
|
7
|
+
* 同じ名前で中身が違うものを配ると、顧客の型検査が嘘をつく。
|
|
8
|
+
*/
|
|
9
|
+
export interface User {
|
|
10
|
+
id: string;
|
|
11
|
+
email: string | null;
|
|
12
|
+
phone: string | null;
|
|
13
|
+
emailVerified: boolean;
|
|
14
|
+
phoneVerified: boolean;
|
|
15
|
+
/** ISO 8601。JSON を越えてくるので Date ではない */
|
|
16
|
+
createdAt: string;
|
|
17
|
+
}
|
|
18
|
+
export interface Tokens {
|
|
19
|
+
/** JWT。既定15分で切れる */
|
|
20
|
+
accessToken: string;
|
|
21
|
+
/** 不透明な文字列。失効させられる */
|
|
22
|
+
refreshToken: string;
|
|
23
|
+
/** accessToken が切れるまでの秒数 */
|
|
24
|
+
expiresIn: number;
|
|
25
|
+
tokenType: 'Bearer';
|
|
26
|
+
}
|
|
27
|
+
export interface Session {
|
|
28
|
+
user: User;
|
|
29
|
+
tokens: Tokens;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* OAuth の開始。
|
|
33
|
+
* ★ url へブラウザを送る。state / nonce / code_verifier はサーバーが持つので、
|
|
34
|
+
* アプリ側で保管する必要はない。
|
|
35
|
+
*/
|
|
36
|
+
export interface AuthorizationRequest {
|
|
37
|
+
url: string;
|
|
38
|
+
state: string;
|
|
39
|
+
}
|
|
40
|
+
export interface PushMessage {
|
|
41
|
+
to: {
|
|
42
|
+
userIds: string[];
|
|
43
|
+
} | {
|
|
44
|
+
tokens: string[];
|
|
45
|
+
};
|
|
46
|
+
title: string;
|
|
47
|
+
body: string;
|
|
48
|
+
/** 値は文字列に限る(iOS と Android の両方の制約) */
|
|
49
|
+
data?: Record<string, string>;
|
|
50
|
+
ios?: {
|
|
51
|
+
badge?: number;
|
|
52
|
+
sound?: string;
|
|
53
|
+
/** 'passive' | 'active' | 'time-sensitive' | 'critical' */
|
|
54
|
+
interruptionLevel?: string;
|
|
55
|
+
threadId?: string;
|
|
56
|
+
};
|
|
57
|
+
android?: {
|
|
58
|
+
channelId?: string;
|
|
59
|
+
priority?: 'high' | 'normal';
|
|
60
|
+
collapseKey?: string;
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
export type DeliveryStatus = 'delivered' | 'failed' | 'token_removed';
|
|
64
|
+
export interface DeliveryOutcome {
|
|
65
|
+
token: string;
|
|
66
|
+
platform: 'ios' | 'android';
|
|
67
|
+
status: DeliveryStatus;
|
|
68
|
+
error?: string;
|
|
69
|
+
}
|
|
70
|
+
export interface SendResult {
|
|
71
|
+
requested: number;
|
|
72
|
+
delivered: number;
|
|
73
|
+
failed: number;
|
|
74
|
+
/** 無効と判定して削除した端末トークンの数 */
|
|
75
|
+
tokensRemoved: number;
|
|
76
|
+
outcomes: DeliveryOutcome[];
|
|
77
|
+
}
|
|
78
|
+
export interface RegisteredToken {
|
|
79
|
+
id: string;
|
|
80
|
+
platform: 'ios' | 'android';
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,IAAI;IACnB,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,aAAa,EAAE,OAAO,CAAA;IACtB,aAAa,EAAE,OAAO,CAAA;IACtB,uCAAuC;IACvC,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,MAAM;IACrB,oBAAoB;IACpB,WAAW,EAAE,MAAM,CAAA;IACnB,sBAAsB;IACtB,YAAY,EAAE,MAAM,CAAA;IACpB,4BAA4B;IAC5B,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,QAAQ,CAAA;CACpB;AAED,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,IAAI,CAAA;IACV,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE;QAAE,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG;QAAE,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,CAAA;IAChD,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,qCAAqC;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC7B,GAAG,CAAC,EAAE;QACJ,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,2DAA2D;QAC3D,iBAAiB,CAAC,EAAE,MAAM,CAAA;QAC1B,QAAQ,CAAC,EAAE,MAAM,CAAA;KAClB,CAAA;IACD,OAAO,CAAC,EAAE;QACR,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,QAAQ,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAA;QAC5B,WAAW,CAAC,EAAE,MAAM,CAAA;KACrB,CAAA;CACF;AAED,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,QAAQ,GAAG,eAAe,CAAA;AAErE,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,KAAK,GAAG,SAAS,CAAA;IAC3B,MAAM,EAAE,cAAc,CAAA;IACtB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,0BAA0B;IAC1B,aAAa,EAAE,MAAM,CAAA;IACrB,QAAQ,EAAE,eAAe,EAAE,CAAA;CAC5B;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAA;IACV,QAAQ,EAAE,KAAK,GAAG,SAAS,CAAA;CAC5B"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@superzerodev/client",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"!dist/__tests__"
|
|
16
|
+
],
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@superzerodev/core": "0.1.1"
|
|
19
|
+
},
|
|
20
|
+
"description": "Server-side client for Superzero: authentication (email, OAuth, SMS) and mobile push.",
|
|
21
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "git+https://github.com/mashihara/superzero.git",
|
|
25
|
+
"directory": "packages/client"
|
|
26
|
+
},
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=22"
|
|
29
|
+
},
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "tsc -p tsconfig.json",
|
|
35
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
36
|
+
"test": "tsc -p tsconfig.json && node --test \"dist/__tests__/*.test.js\""
|
|
37
|
+
}
|
|
38
|
+
}
|