@stonyx/oauth 0.1.1-beta.2 → 0.1.1-beta.200
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 +193 -2
- package/dist/auth-request.d.ts +147 -0
- package/dist/auth-request.js +247 -0
- package/dist/main.d.ts +121 -0
- package/dist/main.js +194 -0
- package/dist/oauth-flow.d.ts +30 -0
- package/dist/oauth-flow.js +83 -0
- package/dist/providers/discord.d.ts +30 -0
- package/dist/providers/discord.js +43 -0
- package/dist/session-manager.d.ts +20 -0
- package/dist/session-manager.js +30 -0
- package/dist/ticket-store.d.ts +134 -0
- package/dist/ticket-store.js +140 -0
- package/dist/token-manager.d.ts +15 -0
- package/dist/token-manager.js +24 -0
- package/package.json +45 -8
- package/src/auth-request.ts +348 -0
- package/src/main.ts +259 -0
- package/src/{oauth-flow.js → oauth-flow.ts} +31 -7
- package/src/providers/{discord.js → discord.ts} +29 -3
- package/src/{session-manager.js → session-manager.ts} +19 -6
- package/src/ticket-store.ts +158 -0
- package/src/token-manager.ts +35 -0
- package/src/types/node.d.ts +19 -0
- package/src/types/stonyx-events.d.ts +4 -0
- package/src/types/stonyx-rest-server.d.ts +11 -0
- package/src/types/stonyx.d.ts +38 -0
- package/.github/workflows/ci.yml +0 -16
- package/.github/workflows/publish.yml +0 -51
- package/src/auth-request.js +0 -74
- package/src/main.js +0 -79
- package/src/token-manager.js +0 -26
- package/test/config/environment.js +0 -18
- package/test/integration/oauth-test.js +0 -149
- package/test/sample/providers/mock.js +0 -40
- package/test/sample/requests/.gitkeep +0 -0
- package/test/unit/oauth-flow-test.js +0 -137
- package/test/unit/providers/discord-test.js +0 -115
- package/test/unit/session-manager-test.js +0 -85
- package/test/unit/state-validation-test.js +0 -118
- package/test/unit/token-manager-test.js +0 -76
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lifetime of an exchange ticket.
|
|
3
|
+
*
|
|
4
|
+
* Sized for one redirect plus one page load, and deliberately two orders of
|
|
5
|
+
* magnitude tighter than the 10-minute state TTL: the ticket is a bearer value
|
|
6
|
+
* travelling in a URL, and the whole point of #45 is that a bearer value in a
|
|
7
|
+
* URL must not be long-lived — in the fragment, so it reaches no server, but
|
|
8
|
+
* still into browser history and readable by scripts on the landing page.
|
|
9
|
+
*/
|
|
10
|
+
export declare const TICKET_TTL_MS: number;
|
|
11
|
+
/** Entropy of a ticket, in bytes. */
|
|
12
|
+
export declare const TICKET_BYTES = 32;
|
|
13
|
+
interface TicketRecord {
|
|
14
|
+
sessionId: string;
|
|
15
|
+
expiresAt: number;
|
|
16
|
+
createdAt: number;
|
|
17
|
+
}
|
|
18
|
+
export interface RedeemedTicket {
|
|
19
|
+
sessionId: string;
|
|
20
|
+
expiresAt: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Single-use, short-lived tickets that stand in for a session id on the wire.
|
|
24
|
+
*
|
|
25
|
+
* The callback redirect hands the browser a ticket instead of the session id
|
|
26
|
+
* (#45), in the URL *fragment*, which no user agent transmits to any server.
|
|
27
|
+
* The ticket authenticates nothing — `GET /auth` reads the `session-id` header
|
|
28
|
+
* and knows only about `SessionManager` — so a ticket observed in history or
|
|
29
|
+
* by a script reading `location.hash` is worth something only inside the
|
|
30
|
+
* sub-second window before the landing page redeems it, and nothing at all
|
|
31
|
+
* afterwards.
|
|
32
|
+
*
|
|
33
|
+
* Known residual, stated rather than papered over: a ticket observed *within*
|
|
34
|
+
* that window is redeemable by the observer, because nothing here binds a
|
|
35
|
+
* ticket to the client that started the flow. Closing it means binding the way
|
|
36
|
+
* #36 bound the state, and that binding has to travel on a cookie the
|
|
37
|
+
* cross-origin exchange cannot carry.
|
|
38
|
+
*
|
|
39
|
+
* The blocker is `abofs/stonyx-rest-server#63`: `@stonyx/rest-server` calls
|
|
40
|
+
* `cors({ origin, methods })` and has no `credentials` support at all. It is
|
|
41
|
+
* *not* `abofs/stonyx-rest-server#45` — that issue is the response-header half
|
|
42
|
+
* and is already worked around in `auth-request.ts`, which sets and clears the
|
|
43
|
+
* binding cookie on a redirect by reaching through `req.res`. Closing #45
|
|
44
|
+
* would not make this residual closeable. It is a reduction, not an
|
|
45
|
+
* elimination.
|
|
46
|
+
*
|
|
47
|
+
* Like `OAuth.pendingStates`, an abandoned ticket is never collected. That is
|
|
48
|
+
* a pre-existing pattern in this module, not something this store introduces,
|
|
49
|
+
* and it is bounded here by a 60-second TTL rather than a 10-minute one.
|
|
50
|
+
* Tracked, with both maps named, at `abofs/stonyx-oauth#43`.
|
|
51
|
+
*
|
|
52
|
+
* ---
|
|
53
|
+
*
|
|
54
|
+
* **Why this is a second store rather than a reuse of `OAuth.pendingStates`.**
|
|
55
|
+
*
|
|
56
|
+
* The duplication is real and is not an oversight: `pendingStates` is also a
|
|
57
|
+
* single-use, TTL-bounded, consume-on-recognition map keyed by a
|
|
58
|
+
* `randomBytes`-minted opaque token, with the same delete-before-TTL-check
|
|
59
|
+
* ordering and the same never-collected caveat. The shared shape could be
|
|
60
|
+
* extracted into one primitive, and the two constants homes (`STATE_TTL_MS`
|
|
61
|
+
* and `BINDING_VALUE_BYTES` in `main.ts`, `TICKET_TTL_MS` and `TICKET_BYTES`
|
|
62
|
+
* here) could then live together.
|
|
63
|
+
*
|
|
64
|
+
* It is deliberately not done in the change that fixes #45. Widening a
|
|
65
|
+
* security fix into a refactor of the CSRF store means the #36 binding
|
|
66
|
+
* mechanism — whose invariants are load-bearing and separately guarded — moves
|
|
67
|
+
* in the same commit as the fix, for no security gain in either. The two also
|
|
68
|
+
* do not have the same invariants: `pendingStates` is a security control fed
|
|
69
|
+
* by an unauthenticated `GET`, holding a *digest* of a client secret, with a
|
|
70
|
+
* 10-minute budget sized for a provider round trip; this is a delivery
|
|
71
|
+
* convenience reachable only after a successfully bound callback, holding a
|
|
72
|
+
* value it hands back, with a 60-second budget sized for a page load.
|
|
73
|
+
* Collapsing them would couple the control to the convenience.
|
|
74
|
+
*
|
|
75
|
+
* The extraction is tracked at `abofs/stonyx-oauth#58`.
|
|
76
|
+
*/
|
|
77
|
+
export default class TicketStore {
|
|
78
|
+
/**
|
|
79
|
+
* Live tickets, keyed by the **SHA-256 of the ticket**, never by the ticket.
|
|
80
|
+
*
|
|
81
|
+
* Keying by the digest means the map holds no redeemable *ticket*: a ticket
|
|
82
|
+
* is a client-presented secret looked up server-side, so what a reader of
|
|
83
|
+
* this map gets is a digest, and a digest cannot be presented to `redeem`.
|
|
84
|
+
*
|
|
85
|
+
* That does not make the map safe to expose. The record *value* holds a
|
|
86
|
+
* plaintext, live `sessionId` — the 24-hour bearer credential this store
|
|
87
|
+
* exists to keep out of URLs — so a heap dump, a debug serialisation or an
|
|
88
|
+
* accidental log of this map yields live session ids. The map is sensitive
|
|
89
|
+
* on that basis and must not be dumped or logged. Whether the stored
|
|
90
|
+
* `sessionId` should itself be protected is a separate question, and is not
|
|
91
|
+
* settled here.
|
|
92
|
+
*
|
|
93
|
+
* This is the mirror image of `OAuth.pendingStates`, not the same shape:
|
|
94
|
+
* there the *key* is the plaintext state token and the digest
|
|
95
|
+
* (`bindingHash`) sits in the value, so that record unlocks nothing on its
|
|
96
|
+
* own; here the digest is the key and the value is a live credential. What
|
|
97
|
+
* the two stores share is the discipline of never keeping a
|
|
98
|
+
* client-presented secret in the clear — neither the ticket nor the binding
|
|
99
|
+
* value is on the heap — but they place the digest on opposite sides of the
|
|
100
|
+
* entry.
|
|
101
|
+
*
|
|
102
|
+
* No constant-time comparison is needed and none is used: lookup is a hash
|
|
103
|
+
* probe on a 256-bit high-entropy key, not a secret-dependent byte
|
|
104
|
+
* comparison, so there is no early-exit timing signal to exploit. That is
|
|
105
|
+
* the same reason `redeem` can stay an ordinary `Map.get`.
|
|
106
|
+
*/
|
|
107
|
+
tickets: Map<string, TicketRecord>;
|
|
108
|
+
ttl: number;
|
|
109
|
+
/** SHA-256 of a ticket, hex — the only form of the *ticket* this store keeps. */
|
|
110
|
+
static hash(ticket: string): string;
|
|
111
|
+
/**
|
|
112
|
+
* Mints a ticket for a freshly created session.
|
|
113
|
+
*
|
|
114
|
+
* The ticket is independent entropy, never a transform of the session id:
|
|
115
|
+
* anything derived from the credential is the credential.
|
|
116
|
+
*/
|
|
117
|
+
issue(sessionId: string, expiresAt: number): string;
|
|
118
|
+
/**
|
|
119
|
+
* Spends a ticket, if it is live.
|
|
120
|
+
*
|
|
121
|
+
* Consumed on recognition, *before* the TTL check, for the same reason
|
|
122
|
+
* `OAuth.handleCallback` consumes a pending state before validating its
|
|
123
|
+
* binding: every ticket gets exactly one attempt whatever the outcome, so
|
|
124
|
+
* this endpoint is never a repeatable oracle. Deleting after the TTL check
|
|
125
|
+
* instead would leave an expired ticket in the map answering `400` forever
|
|
126
|
+
* while a live one answers `200` — an unauthenticated distinguisher.
|
|
127
|
+
*
|
|
128
|
+
* Returns `null` for unknown, spent and expired tickets alike. The caller
|
|
129
|
+
* maps all three to the same `400`; telling them apart is information the
|
|
130
|
+
* holder of a ticket they did not mint has no business having.
|
|
131
|
+
*/
|
|
132
|
+
redeem(ticket: string): RedeemedTicket | null;
|
|
133
|
+
}
|
|
134
|
+
export {};
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
2
|
+
/**
|
|
3
|
+
* Lifetime of an exchange ticket.
|
|
4
|
+
*
|
|
5
|
+
* Sized for one redirect plus one page load, and deliberately two orders of
|
|
6
|
+
* magnitude tighter than the 10-minute state TTL: the ticket is a bearer value
|
|
7
|
+
* travelling in a URL, and the whole point of #45 is that a bearer value in a
|
|
8
|
+
* URL must not be long-lived — in the fragment, so it reaches no server, but
|
|
9
|
+
* still into browser history and readable by scripts on the landing page.
|
|
10
|
+
*/
|
|
11
|
+
export const TICKET_TTL_MS = 60 * 1000;
|
|
12
|
+
/** Entropy of a ticket, in bytes. */
|
|
13
|
+
export const TICKET_BYTES = 32;
|
|
14
|
+
/**
|
|
15
|
+
* Single-use, short-lived tickets that stand in for a session id on the wire.
|
|
16
|
+
*
|
|
17
|
+
* The callback redirect hands the browser a ticket instead of the session id
|
|
18
|
+
* (#45), in the URL *fragment*, which no user agent transmits to any server.
|
|
19
|
+
* The ticket authenticates nothing — `GET /auth` reads the `session-id` header
|
|
20
|
+
* and knows only about `SessionManager` — so a ticket observed in history or
|
|
21
|
+
* by a script reading `location.hash` is worth something only inside the
|
|
22
|
+
* sub-second window before the landing page redeems it, and nothing at all
|
|
23
|
+
* afterwards.
|
|
24
|
+
*
|
|
25
|
+
* Known residual, stated rather than papered over: a ticket observed *within*
|
|
26
|
+
* that window is redeemable by the observer, because nothing here binds a
|
|
27
|
+
* ticket to the client that started the flow. Closing it means binding the way
|
|
28
|
+
* #36 bound the state, and that binding has to travel on a cookie the
|
|
29
|
+
* cross-origin exchange cannot carry.
|
|
30
|
+
*
|
|
31
|
+
* The blocker is `abofs/stonyx-rest-server#63`: `@stonyx/rest-server` calls
|
|
32
|
+
* `cors({ origin, methods })` and has no `credentials` support at all. It is
|
|
33
|
+
* *not* `abofs/stonyx-rest-server#45` — that issue is the response-header half
|
|
34
|
+
* and is already worked around in `auth-request.ts`, which sets and clears the
|
|
35
|
+
* binding cookie on a redirect by reaching through `req.res`. Closing #45
|
|
36
|
+
* would not make this residual closeable. It is a reduction, not an
|
|
37
|
+
* elimination.
|
|
38
|
+
*
|
|
39
|
+
* Like `OAuth.pendingStates`, an abandoned ticket is never collected. That is
|
|
40
|
+
* a pre-existing pattern in this module, not something this store introduces,
|
|
41
|
+
* and it is bounded here by a 60-second TTL rather than a 10-minute one.
|
|
42
|
+
* Tracked, with both maps named, at `abofs/stonyx-oauth#43`.
|
|
43
|
+
*
|
|
44
|
+
* ---
|
|
45
|
+
*
|
|
46
|
+
* **Why this is a second store rather than a reuse of `OAuth.pendingStates`.**
|
|
47
|
+
*
|
|
48
|
+
* The duplication is real and is not an oversight: `pendingStates` is also a
|
|
49
|
+
* single-use, TTL-bounded, consume-on-recognition map keyed by a
|
|
50
|
+
* `randomBytes`-minted opaque token, with the same delete-before-TTL-check
|
|
51
|
+
* ordering and the same never-collected caveat. The shared shape could be
|
|
52
|
+
* extracted into one primitive, and the two constants homes (`STATE_TTL_MS`
|
|
53
|
+
* and `BINDING_VALUE_BYTES` in `main.ts`, `TICKET_TTL_MS` and `TICKET_BYTES`
|
|
54
|
+
* here) could then live together.
|
|
55
|
+
*
|
|
56
|
+
* It is deliberately not done in the change that fixes #45. Widening a
|
|
57
|
+
* security fix into a refactor of the CSRF store means the #36 binding
|
|
58
|
+
* mechanism — whose invariants are load-bearing and separately guarded — moves
|
|
59
|
+
* in the same commit as the fix, for no security gain in either. The two also
|
|
60
|
+
* do not have the same invariants: `pendingStates` is a security control fed
|
|
61
|
+
* by an unauthenticated `GET`, holding a *digest* of a client secret, with a
|
|
62
|
+
* 10-minute budget sized for a provider round trip; this is a delivery
|
|
63
|
+
* convenience reachable only after a successfully bound callback, holding a
|
|
64
|
+
* value it hands back, with a 60-second budget sized for a page load.
|
|
65
|
+
* Collapsing them would couple the control to the convenience.
|
|
66
|
+
*
|
|
67
|
+
* The extraction is tracked at `abofs/stonyx-oauth#58`.
|
|
68
|
+
*/
|
|
69
|
+
export default class TicketStore {
|
|
70
|
+
/**
|
|
71
|
+
* Live tickets, keyed by the **SHA-256 of the ticket**, never by the ticket.
|
|
72
|
+
*
|
|
73
|
+
* Keying by the digest means the map holds no redeemable *ticket*: a ticket
|
|
74
|
+
* is a client-presented secret looked up server-side, so what a reader of
|
|
75
|
+
* this map gets is a digest, and a digest cannot be presented to `redeem`.
|
|
76
|
+
*
|
|
77
|
+
* That does not make the map safe to expose. The record *value* holds a
|
|
78
|
+
* plaintext, live `sessionId` — the 24-hour bearer credential this store
|
|
79
|
+
* exists to keep out of URLs — so a heap dump, a debug serialisation or an
|
|
80
|
+
* accidental log of this map yields live session ids. The map is sensitive
|
|
81
|
+
* on that basis and must not be dumped or logged. Whether the stored
|
|
82
|
+
* `sessionId` should itself be protected is a separate question, and is not
|
|
83
|
+
* settled here.
|
|
84
|
+
*
|
|
85
|
+
* This is the mirror image of `OAuth.pendingStates`, not the same shape:
|
|
86
|
+
* there the *key* is the plaintext state token and the digest
|
|
87
|
+
* (`bindingHash`) sits in the value, so that record unlocks nothing on its
|
|
88
|
+
* own; here the digest is the key and the value is a live credential. What
|
|
89
|
+
* the two stores share is the discipline of never keeping a
|
|
90
|
+
* client-presented secret in the clear — neither the ticket nor the binding
|
|
91
|
+
* value is on the heap — but they place the digest on opposite sides of the
|
|
92
|
+
* entry.
|
|
93
|
+
*
|
|
94
|
+
* No constant-time comparison is needed and none is used: lookup is a hash
|
|
95
|
+
* probe on a 256-bit high-entropy key, not a secret-dependent byte
|
|
96
|
+
* comparison, so there is no early-exit timing signal to exploit. That is
|
|
97
|
+
* the same reason `redeem` can stay an ordinary `Map.get`.
|
|
98
|
+
*/
|
|
99
|
+
tickets = new Map();
|
|
100
|
+
ttl = TICKET_TTL_MS;
|
|
101
|
+
/** SHA-256 of a ticket, hex — the only form of the *ticket* this store keeps. */
|
|
102
|
+
static hash(ticket) {
|
|
103
|
+
return createHash('sha256').update(ticket).digest('hex');
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Mints a ticket for a freshly created session.
|
|
107
|
+
*
|
|
108
|
+
* The ticket is independent entropy, never a transform of the session id:
|
|
109
|
+
* anything derived from the credential is the credential.
|
|
110
|
+
*/
|
|
111
|
+
issue(sessionId, expiresAt) {
|
|
112
|
+
const ticket = randomBytes(TICKET_BYTES).toString('base64url');
|
|
113
|
+
this.tickets.set(TicketStore.hash(ticket), { sessionId, expiresAt, createdAt: Date.now() });
|
|
114
|
+
return ticket;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Spends a ticket, if it is live.
|
|
118
|
+
*
|
|
119
|
+
* Consumed on recognition, *before* the TTL check, for the same reason
|
|
120
|
+
* `OAuth.handleCallback` consumes a pending state before validating its
|
|
121
|
+
* binding: every ticket gets exactly one attempt whatever the outcome, so
|
|
122
|
+
* this endpoint is never a repeatable oracle. Deleting after the TTL check
|
|
123
|
+
* instead would leave an expired ticket in the map answering `400` forever
|
|
124
|
+
* while a live one answers `200` — an unauthenticated distinguisher.
|
|
125
|
+
*
|
|
126
|
+
* Returns `null` for unknown, spent and expired tickets alike. The caller
|
|
127
|
+
* maps all three to the same `400`; telling them apart is information the
|
|
128
|
+
* holder of a ticket they did not mint has no business having.
|
|
129
|
+
*/
|
|
130
|
+
redeem(ticket) {
|
|
131
|
+
const key = ticket ? TicketStore.hash(ticket) : null;
|
|
132
|
+
const record = key ? this.tickets.get(key) : undefined;
|
|
133
|
+
if (!record)
|
|
134
|
+
return null;
|
|
135
|
+
this.tickets.delete(key);
|
|
136
|
+
if (Date.now() - record.createdAt > this.ttl)
|
|
137
|
+
return null;
|
|
138
|
+
return { sessionId: record.sessionId, expiresAt: record.expiresAt };
|
|
139
|
+
}
|
|
140
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type OAuthFlow from './oauth-flow.js';
|
|
2
|
+
import type { TokenResult } from './oauth-flow.js';
|
|
3
|
+
export interface TokenData extends TokenResult {
|
|
4
|
+
expiresAt: number;
|
|
5
|
+
}
|
|
6
|
+
export default class TokenManager {
|
|
7
|
+
flow: OAuthFlow;
|
|
8
|
+
constructor(flow: OAuthFlow);
|
|
9
|
+
getTokens(code: string): Promise<TokenData>;
|
|
10
|
+
refresh(refreshToken: string): Promise<TokenData>;
|
|
11
|
+
revoke(accessToken: string): Promise<void>;
|
|
12
|
+
isExpired(tokenData: {
|
|
13
|
+
expiresAt?: number;
|
|
14
|
+
} | null | undefined): boolean;
|
|
15
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export default class TokenManager {
|
|
2
|
+
flow;
|
|
3
|
+
constructor(flow) {
|
|
4
|
+
this.flow = flow;
|
|
5
|
+
}
|
|
6
|
+
async getTokens(code) {
|
|
7
|
+
const tokens = await this.flow.exchangeCode(code);
|
|
8
|
+
tokens.expiresAt = Date.now() + (tokens.expiresIn * 1000);
|
|
9
|
+
return tokens;
|
|
10
|
+
}
|
|
11
|
+
async refresh(refreshToken) {
|
|
12
|
+
const tokens = await this.flow.refreshAccessToken(refreshToken);
|
|
13
|
+
tokens.expiresAt = Date.now() + (tokens.expiresIn * 1000);
|
|
14
|
+
return tokens;
|
|
15
|
+
}
|
|
16
|
+
async revoke(accessToken) {
|
|
17
|
+
return this.flow.revokeToken(accessToken);
|
|
18
|
+
}
|
|
19
|
+
isExpired(tokenData) {
|
|
20
|
+
if (!tokenData?.expiresAt)
|
|
21
|
+
return true;
|
|
22
|
+
return Date.now() >= tokenData.expiresAt;
|
|
23
|
+
}
|
|
24
|
+
}
|
package/package.json
CHANGED
|
@@ -4,39 +4,76 @@
|
|
|
4
4
|
"stonyx-async",
|
|
5
5
|
"stonyx-module"
|
|
6
6
|
],
|
|
7
|
-
"version": "0.1.1-beta.
|
|
7
|
+
"version": "0.1.1-beta.200",
|
|
8
8
|
"description": "OAuth2 authentication module for the Stonyx framework",
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
11
11
|
"url": "git+https://github.com/abofs/stonyx-oauth.git"
|
|
12
12
|
},
|
|
13
|
-
"main": "
|
|
13
|
+
"main": "dist/main.js",
|
|
14
14
|
"type": "module",
|
|
15
15
|
"exports": {
|
|
16
|
-
".":
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./dist/main.d.ts",
|
|
18
|
+
"default": "./dist/main.js"
|
|
19
|
+
},
|
|
20
|
+
"./oauth-flow": {
|
|
21
|
+
"types": "./dist/oauth-flow.d.ts",
|
|
22
|
+
"default": "./dist/oauth-flow.js"
|
|
23
|
+
},
|
|
24
|
+
"./auth-request": {
|
|
25
|
+
"types": "./dist/auth-request.d.ts",
|
|
26
|
+
"default": "./dist/auth-request.js"
|
|
27
|
+
},
|
|
28
|
+
"./session-manager": {
|
|
29
|
+
"types": "./dist/session-manager.d.ts",
|
|
30
|
+
"default": "./dist/session-manager.js"
|
|
31
|
+
},
|
|
32
|
+
"./token-manager": {
|
|
33
|
+
"types": "./dist/token-manager.d.ts",
|
|
34
|
+
"default": "./dist/token-manager.js"
|
|
35
|
+
},
|
|
36
|
+
"./providers/discord": {
|
|
37
|
+
"types": "./dist/providers/discord.d.ts",
|
|
38
|
+
"default": "./dist/providers/discord.js"
|
|
39
|
+
}
|
|
17
40
|
},
|
|
18
41
|
"author": "Stone Costa",
|
|
19
42
|
"license": "Apache-2.0",
|
|
20
43
|
"contributors": [
|
|
21
44
|
"Stone Costa <stone.costa@synamicd.com>"
|
|
22
45
|
],
|
|
46
|
+
"files": [
|
|
47
|
+
"dist",
|
|
48
|
+
"src",
|
|
49
|
+
"config",
|
|
50
|
+
"README.md"
|
|
51
|
+
],
|
|
23
52
|
"publishConfig": {
|
|
24
53
|
"access": "public",
|
|
25
54
|
"provenance": true
|
|
26
55
|
},
|
|
27
56
|
"dependencies": {
|
|
28
|
-
"stonyx": "0.
|
|
57
|
+
"@stonyx/events": "0.1.1-beta.64",
|
|
58
|
+
"stonyx": "0.2.3-beta.96"
|
|
29
59
|
},
|
|
30
60
|
"peerDependencies": {
|
|
31
61
|
"@stonyx/rest-server": ">=0.2.1-beta.11"
|
|
32
62
|
},
|
|
33
63
|
"devDependencies": {
|
|
34
|
-
"@stonyx/rest-server": "0.2.1-beta.
|
|
35
|
-
"@stonyx/utils": "0.2.3-beta.
|
|
64
|
+
"@stonyx/rest-server": "0.2.1-beta.135",
|
|
65
|
+
"@stonyx/utils": "0.2.3-beta.27",
|
|
66
|
+
"@stonyx/logs": "1.0.1-beta.21",
|
|
67
|
+
"@types/qunit": "^2.19.13",
|
|
68
|
+
"@types/sinon": "^21.0.1",
|
|
36
69
|
"qunit": "^2.24.1",
|
|
37
|
-
"sinon": "^21.0.0"
|
|
70
|
+
"sinon": "^21.0.0",
|
|
71
|
+
"tsx": "^4.21.0",
|
|
72
|
+
"typescript": "^5.8.3"
|
|
38
73
|
},
|
|
39
74
|
"scripts": {
|
|
40
|
-
"
|
|
75
|
+
"build": "tsc",
|
|
76
|
+
"build:test": "tsc -p tsconfig.test.json",
|
|
77
|
+
"test": "pnpm build && NODE_ENV=test node --import tsx/esm --import ./test/setup.ts node_modules/qunit/bin/qunit.js 'test/**/*-test.ts'"
|
|
41
78
|
}
|
|
42
79
|
}
|