@mythryl/app-sdk 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +74 -2
- package/dist/channel.d.ts +11 -0
- package/dist/channel.js +55 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +9 -0
- package/dist/jwt-expiry.d.ts +2 -0
- package/dist/jwt-expiry.js +13 -0
- package/dist/lazy.d.ts +6 -0
- package/dist/lazy.js +20 -0
- package/dist/sdk.d.ts +36 -0
- package/dist/sdk.js +35 -0
- package/dist/token-cache.d.ts +10 -0
- package/dist/token-cache.js +28 -0
- package/dist/transport.d.ts +14 -0
- package/dist/transport.js +17 -0
- package/package.json +41 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mythryl
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,75 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @mythryl/app-sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The browser SDK for Mythryl mini apps. It connects an app to the Mythryl window that embeds it.
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
npm install @mythryl/app-sdk
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import sdk from "@mythryl/app-sdk";
|
|
11
|
+
|
|
12
|
+
await sdk.ready();
|
|
13
|
+
|
|
14
|
+
const context = await sdk.getContext();
|
|
15
|
+
console.log(context.user.handle);
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The default export is created on first use, so importing it has no side effects and is safe in code that also renders
|
|
19
|
+
on the server. Call it from effects and event handlers, which run in the browser.
|
|
20
|
+
|
|
21
|
+
## API
|
|
22
|
+
|
|
23
|
+
| Call | Result |
|
|
24
|
+
| --- | --- |
|
|
25
|
+
| `sdk.ready()` | Signals that the app has loaded. The host removes its loading screen. |
|
|
26
|
+
| `sdk.close()` | Closes the app. |
|
|
27
|
+
| `sdk.getCapabilities()` | The capabilities the host supports. Check before calling optional features. |
|
|
28
|
+
| `sdk.getContext()` | The signed-in person, how the app was launched, and the client environment. |
|
|
29
|
+
| `sdk.auth.signin({ force? })` | `{ token }`: a JWT that authenticates the person to the app's own server. |
|
|
30
|
+
| `sdk.on(event, listener)` | Subscribes to a host event. Returns a function that removes the listener. |
|
|
31
|
+
| `sdk.dispose()` | Removes listeners and rejects pending calls with `cancelled`. |
|
|
32
|
+
|
|
33
|
+
### Events
|
|
34
|
+
|
|
35
|
+
`minimised` and `maximised` are sent when the person hides the app and brings it back. `closing` and `resized` are
|
|
36
|
+
defined by the protocol; hosts do not send them yet. `closing` is best effort and must not be relied on to save state.
|
|
37
|
+
|
|
38
|
+
### Signing in to your server
|
|
39
|
+
|
|
40
|
+
`sdk.auth.signin()` returns a token valid for 10 minutes, issued for the app's domain. Send it to your server as a
|
|
41
|
+
bearer token and verify it there with [`@mythryl/server-sdk`](https://www.npmjs.com/package/@mythryl/server-sdk). The SDK
|
|
42
|
+
caches the token and requests a new one when less than a minute of validity remains; `signin({ force: true })` always
|
|
43
|
+
requests a new one.
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
const { token } = await sdk.auth.signin();
|
|
47
|
+
|
|
48
|
+
const headers = { authorization: `Bearer ${token}` };
|
|
49
|
+
const response = await fetch("/api/me", { headers });
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Errors
|
|
53
|
+
|
|
54
|
+
A call that fails rejects with a `ProtocolError`. Its `code` is `cancelled`, `unsupported`, `not_consented`,
|
|
55
|
+
`rate_limited` or `unavailable`. A call that gets no answer within 30 seconds rejects with `unavailable`.
|
|
56
|
+
|
|
57
|
+
## Outside a host
|
|
58
|
+
|
|
59
|
+
Outside a Mythryl window there is no host to answer, and calls time out. `isEmbedded()` returns whether the page is
|
|
60
|
+
framed:
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import sdk, { isEmbedded } from "@mythryl/app-sdk";
|
|
64
|
+
|
|
65
|
+
if (isEmbedded()) await sdk.ready();
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Other transports
|
|
69
|
+
|
|
70
|
+
`createSdk(transport)` builds an SDK over any `Transport`, which is how the SDK runs in a webview or a test. The default
|
|
71
|
+
export uses `windowTransport()`, which talks to the embedding window.
|
|
72
|
+
|
|
73
|
+
## License
|
|
74
|
+
|
|
75
|
+
MIT
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type EventName, type Events, type MethodName, type Methods } from "@mythryl/protocol";
|
|
2
|
+
import type { Transport } from "./transport.js";
|
|
3
|
+
export interface Channel {
|
|
4
|
+
/** Sends a call. Resolves with the result, or rejects with a `ProtocolError`. */
|
|
5
|
+
request: <M extends MethodName>(method: M, params: Methods[M]["params"], version: number) => Promise<Methods[M]["result"]>;
|
|
6
|
+
subscribe: <E extends EventName>(event: E, listener: (data: Events[E]) => void) => () => void;
|
|
7
|
+
/** Removes the transport listener and rejects pending calls with `cancelled`. */
|
|
8
|
+
dispose: () => void;
|
|
9
|
+
}
|
|
10
|
+
/** Correlates responses with requests by id, enforces the call timeout and dispatches events. Independent of which calls exist. */
|
|
11
|
+
export declare const createChannel: (transport: Transport, timeoutMs?: number) => Channel;
|
package/dist/channel.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { CALL_TIMEOUT_MS, MESSAGE_SOURCE, ProtocolError, isEventMessage, isResponseMessage, } from "@mythryl/protocol";
|
|
2
|
+
/** Correlates responses with requests by id, enforces the call timeout and dispatches events. Independent of which calls exist. */
|
|
3
|
+
export const createChannel = (transport, timeoutMs = CALL_TIMEOUT_MS) => {
|
|
4
|
+
const pending = new Map();
|
|
5
|
+
const listeners = new Map();
|
|
6
|
+
let lastId = 0;
|
|
7
|
+
const stopListening = transport.listen((data) => {
|
|
8
|
+
if (isResponseMessage(data))
|
|
9
|
+
pending.get(data.id)?.settle(data);
|
|
10
|
+
else if (isEventMessage(data))
|
|
11
|
+
listeners.get(data.event)?.forEach((listener) => listener(data.data));
|
|
12
|
+
});
|
|
13
|
+
return {
|
|
14
|
+
request: (method, params, version) => new Promise((resolve, reject) => {
|
|
15
|
+
const id = String((lastId += 1));
|
|
16
|
+
const done = () => {
|
|
17
|
+
clearTimeout(timer);
|
|
18
|
+
pending.delete(id);
|
|
19
|
+
};
|
|
20
|
+
const timer = setTimeout(() => {
|
|
21
|
+
done();
|
|
22
|
+
reject(new ProtocolError("unavailable", `${method} did not answer in ${timeoutMs} ms.`));
|
|
23
|
+
}, timeoutMs);
|
|
24
|
+
pending.set(id, {
|
|
25
|
+
timer,
|
|
26
|
+
fail: (error) => {
|
|
27
|
+
done();
|
|
28
|
+
reject(error);
|
|
29
|
+
},
|
|
30
|
+
settle: (response) => {
|
|
31
|
+
done();
|
|
32
|
+
// The result is cast without validation: the host returns the type declared for the method.
|
|
33
|
+
if (response.ok)
|
|
34
|
+
resolve(response.result);
|
|
35
|
+
else
|
|
36
|
+
reject(new ProtocolError(response.error.code, response.error.message));
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
transport.post({ source: MESSAGE_SOURCE, v: version, type: "request", id, method, params });
|
|
40
|
+
}),
|
|
41
|
+
subscribe: (event, listener) => {
|
|
42
|
+
const forEvent = (data) => listener(data);
|
|
43
|
+
const set = listeners.get(event) ?? new Set();
|
|
44
|
+
set.add(forEvent);
|
|
45
|
+
listeners.set(event, set);
|
|
46
|
+
return () => void set.delete(forEvent);
|
|
47
|
+
},
|
|
48
|
+
dispose: () => {
|
|
49
|
+
stopListening();
|
|
50
|
+
listeners.clear();
|
|
51
|
+
for (const call of [...pending.values()])
|
|
52
|
+
call.fail(new ProtocolError("cancelled", "The SDK was disposed."));
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Default SDK instance. Communicates with the window that embeds the app and is created on first call. */
|
|
2
|
+
declare const sdk: import("./sdk.js").Sdk;
|
|
3
|
+
export default sdk;
|
|
4
|
+
export { sdk };
|
|
5
|
+
export { createSdk, type Sdk } from "./sdk.js";
|
|
6
|
+
export { isEmbedded, windowTransport, type Transport } from "./transport.js";
|
|
7
|
+
export { EVENT_NAMES, ProtocolError, type AppContext, type Capability, type ErrorCode, type EventName, type Events } from "@mythryl/protocol";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { lazySdk } from "./lazy.js";
|
|
2
|
+
import { createSdk } from "./sdk.js";
|
|
3
|
+
/** Default SDK instance. Communicates with the window that embeds the app and is created on first call. */
|
|
4
|
+
const sdk = lazySdk(() => createSdk());
|
|
5
|
+
export default sdk;
|
|
6
|
+
export { sdk };
|
|
7
|
+
export { createSdk } from "./sdk.js";
|
|
8
|
+
export { isEmbedded, windowTransport } from "./transport.js";
|
|
9
|
+
export { EVENT_NAMES, ProtocolError } from "@mythryl/protocol";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Returns the `exp` claim of a JWT in milliseconds, or `undefined` if it is absent or unreadable. The signature is not verified. */
|
|
2
|
+
export const expiryOf = (token) => {
|
|
3
|
+
const payload = token.split(".")[1];
|
|
4
|
+
if (!payload)
|
|
5
|
+
return undefined;
|
|
6
|
+
try {
|
|
7
|
+
const { exp } = JSON.parse(atob(payload.replace(/-/g, "+").replace(/_/g, "/")));
|
|
8
|
+
return typeof exp === "number" ? exp * 1000 : undefined;
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
};
|
package/dist/lazy.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Sdk } from "./sdk.js";
|
|
2
|
+
/**
|
|
3
|
+
* Wraps an SDK so that it is created on first use. Importing the wrapper has no side effects, so it is safe where no
|
|
4
|
+
* `window` exists (server rendering, builds). After `dispose()`, the next call creates a new instance.
|
|
5
|
+
*/
|
|
6
|
+
export declare const lazySdk: (create: () => Sdk) => Sdk;
|
package/dist/lazy.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wraps an SDK so that it is created on first use. Importing the wrapper has no side effects, so it is safe where no
|
|
3
|
+
* `window` exists (server rendering, builds). After `dispose()`, the next call creates a new instance.
|
|
4
|
+
*/
|
|
5
|
+
export const lazySdk = (create) => {
|
|
6
|
+
let current;
|
|
7
|
+
const sdk = () => (current ??= create());
|
|
8
|
+
return {
|
|
9
|
+
ready: () => sdk().ready(),
|
|
10
|
+
close: () => sdk().close(),
|
|
11
|
+
getCapabilities: () => sdk().getCapabilities(),
|
|
12
|
+
getContext: () => sdk().getContext(),
|
|
13
|
+
auth: { signin: (options) => sdk().auth.signin(options) },
|
|
14
|
+
on: (event, listener) => sdk().on(event, listener),
|
|
15
|
+
dispose: () => {
|
|
16
|
+
current?.dispose();
|
|
17
|
+
current = undefined;
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
};
|
package/dist/sdk.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type AppContext, type Capability, type EventName, type Events } from "@mythryl/protocol";
|
|
2
|
+
import { type Transport } from "./transport.js";
|
|
3
|
+
export interface Sdk {
|
|
4
|
+
/** Signals that the app has loaded. The host removes its loading screen. */
|
|
5
|
+
ready: () => Promise<void>;
|
|
6
|
+
/** Closes the app. */
|
|
7
|
+
close: () => Promise<void>;
|
|
8
|
+
/** Returns the capabilities the host supports. Check before calling optional features. */
|
|
9
|
+
getCapabilities: () => Promise<readonly Capability[]>;
|
|
10
|
+
getContext: () => Promise<AppContext>;
|
|
11
|
+
auth: {
|
|
12
|
+
/**
|
|
13
|
+
* Returns a token that authenticates the person to the app's own server.
|
|
14
|
+
*
|
|
15
|
+
* The token is a JWT signed by Mythryl. Its `aud` is the app's domain and it is valid for 10 minutes. Send it as a
|
|
16
|
+
* bearer token and verify it on the server. The SDK caches the token and requests a new one when less than a
|
|
17
|
+
* minute of validity remains.
|
|
18
|
+
*
|
|
19
|
+
* @param options.force Skip the cache and request a new token.
|
|
20
|
+
*/
|
|
21
|
+
signin: (options?: {
|
|
22
|
+
force?: boolean;
|
|
23
|
+
}) => Promise<{
|
|
24
|
+
token: string;
|
|
25
|
+
}>;
|
|
26
|
+
};
|
|
27
|
+
/** Subscribes to a host event. Returns a function that removes the listener. */
|
|
28
|
+
on: <E extends EventName>(event: E, listener: (data: Events[E]) => void) => () => void;
|
|
29
|
+
/** Removes listeners and rejects pending calls with `cancelled`. */
|
|
30
|
+
dispose: () => void;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Creates an SDK over a transport. The first call performs the `hello` handshake, which selects the protocol version.
|
|
34
|
+
* Later calls use the selected version.
|
|
35
|
+
*/
|
|
36
|
+
export declare const createSdk: (transport?: Transport) => Sdk;
|
package/dist/sdk.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { LATEST_VERSION, SUPPORTED_VERSIONS, } from "@mythryl/protocol";
|
|
2
|
+
import { createChannel } from "./channel.js";
|
|
3
|
+
import { createTokenCache } from "./token-cache.js";
|
|
4
|
+
import { windowTransport } from "./transport.js";
|
|
5
|
+
/**
|
|
6
|
+
* Creates an SDK over a transport. The first call performs the `hello` handshake, which selects the protocol version.
|
|
7
|
+
* Later calls use the selected version.
|
|
8
|
+
*/
|
|
9
|
+
export const createSdk = (transport = windowTransport()) => {
|
|
10
|
+
const channel = createChannel(transport);
|
|
11
|
+
let handshake;
|
|
12
|
+
// A failed handshake is discarded, so the next call retries it.
|
|
13
|
+
const connect = () => {
|
|
14
|
+
handshake ??= channel.request("hello", { versions: SUPPORTED_VERSIONS }, LATEST_VERSION).catch((error) => {
|
|
15
|
+
handshake = undefined;
|
|
16
|
+
throw error;
|
|
17
|
+
});
|
|
18
|
+
return handshake;
|
|
19
|
+
};
|
|
20
|
+
const call = async (method, params) => channel.request(method, params, (await connect()).version);
|
|
21
|
+
const tokens = createTokenCache(() => call("auth.signin", undefined));
|
|
22
|
+
return {
|
|
23
|
+
ready: () => call("ready", undefined),
|
|
24
|
+
close: () => call("close", undefined),
|
|
25
|
+
getCapabilities: async () => (await connect()).capabilities,
|
|
26
|
+
getContext: () => call("getContext", undefined),
|
|
27
|
+
auth: { signin: (options) => tokens.get(options?.force) },
|
|
28
|
+
// The host sends events only after the handshake, so subscribing starts it.
|
|
29
|
+
on: (event, listener) => {
|
|
30
|
+
connect().catch(() => undefined);
|
|
31
|
+
return channel.subscribe(event, listener);
|
|
32
|
+
},
|
|
33
|
+
dispose: channel.dispose,
|
|
34
|
+
};
|
|
35
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface TokenCache {
|
|
2
|
+
/** Returns the cached token while it has more than a minute of validity left, otherwise requests a new one. `force` skips the cache. */
|
|
3
|
+
get: (force?: boolean) => Promise<{
|
|
4
|
+
token: string;
|
|
5
|
+
}>;
|
|
6
|
+
}
|
|
7
|
+
/** Caches the most recent token until shortly before it expires. Concurrent calls share a single request. */
|
|
8
|
+
export declare const createTokenCache: (fetchToken: () => Promise<{
|
|
9
|
+
token: string;
|
|
10
|
+
}>, now?: () => number) => TokenCache;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { expiryOf } from "./jwt-expiry.js";
|
|
2
|
+
/** Time in milliseconds before expiry at which a cached token is replaced. */
|
|
3
|
+
const REFRESH_BEFORE_MS = 60_000;
|
|
4
|
+
/** Caches the most recent token until shortly before it expires. Concurrent calls share a single request. */
|
|
5
|
+
export const createTokenCache = (fetchToken, now = Date.now) => {
|
|
6
|
+
let cached;
|
|
7
|
+
let pending;
|
|
8
|
+
const request = () => {
|
|
9
|
+
pending ??= fetchToken()
|
|
10
|
+
.then(({ token }) => {
|
|
11
|
+
// A token without an `exp` claim is returned but not cached.
|
|
12
|
+
const expiresAt = expiryOf(token);
|
|
13
|
+
cached = expiresAt === undefined ? undefined : { token, expiresAt };
|
|
14
|
+
return { token };
|
|
15
|
+
})
|
|
16
|
+
.finally(() => {
|
|
17
|
+
pending = undefined;
|
|
18
|
+
});
|
|
19
|
+
return pending;
|
|
20
|
+
};
|
|
21
|
+
return {
|
|
22
|
+
get: async (force = false) => {
|
|
23
|
+
if (!force && cached && cached.expiresAt - now() > REFRESH_BEFORE_MS)
|
|
24
|
+
return { token: cached.token };
|
|
25
|
+
return request();
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Message } from "@mythryl/protocol";
|
|
2
|
+
/** Message transport between the app and the host. The SDK depends only on this interface, so it runs unchanged over a frame, a webview or a test double. */
|
|
3
|
+
export interface Transport {
|
|
4
|
+
post: (message: Message) => void;
|
|
5
|
+
/** Registers a handler for incoming data, passed unvalidated. Returns a function that removes the handler. */
|
|
6
|
+
listen: (handler: (data: unknown) => void) => () => void;
|
|
7
|
+
}
|
|
8
|
+
/** Returns whether the page is embedded in a host frame. Outside a frame there is no host to talk to. */
|
|
9
|
+
export declare const isEmbedded: () => boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Transport over `postMessage` with the embedding window. Accepts messages only from that window. The host origin is
|
|
12
|
+
* not known in advance, so outgoing messages use the `*` target origin and must not contain secrets.
|
|
13
|
+
*/
|
|
14
|
+
export declare const windowTransport: (host?: Window) => Transport;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Returns whether the page is embedded in a host frame. Outside a frame there is no host to talk to. */
|
|
2
|
+
export const isEmbedded = () => window.parent !== window;
|
|
3
|
+
/**
|
|
4
|
+
* Transport over `postMessage` with the embedding window. Accepts messages only from that window. The host origin is
|
|
5
|
+
* not known in advance, so outgoing messages use the `*` target origin and must not contain secrets.
|
|
6
|
+
*/
|
|
7
|
+
export const windowTransport = (host = window.parent) => ({
|
|
8
|
+
post: (message) => host.postMessage(message, "*"),
|
|
9
|
+
listen: (handler) => {
|
|
10
|
+
const onMessage = (event) => {
|
|
11
|
+
if (event.source === host)
|
|
12
|
+
handler(event.data);
|
|
13
|
+
};
|
|
14
|
+
window.addEventListener("message", onMessage);
|
|
15
|
+
return () => window.removeEventListener("message", onMessage);
|
|
16
|
+
},
|
|
17
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,44 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mythryl/app-sdk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "The browser SDK for Mythryl mini apps.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/mythryl-studio/mythryl-sdk.git",
|
|
9
|
+
"directory": "packages/app-sdk"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"mythryl",
|
|
13
|
+
"mini-apps",
|
|
14
|
+
"sdk"
|
|
15
|
+
],
|
|
16
|
+
"type": "module",
|
|
17
|
+
"sideEffects": false,
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"!dist/**/*.map"
|
|
21
|
+
],
|
|
22
|
+
"exports": {
|
|
23
|
+
".": {
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"default": "./dist/index.js"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@mythryl/protocol": "0.1.0"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"typescript": "~6.0.3",
|
|
33
|
+
"vitest": "^5.0.5"
|
|
34
|
+
},
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "tsc -p tsconfig.build.json",
|
|
40
|
+
"dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput",
|
|
41
|
+
"typecheck": "tsc --noEmit",
|
|
42
|
+
"test": "vitest run"
|
|
43
|
+
}
|
|
6
44
|
}
|