@web-applets/sdk 0.0.8 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
package/dist/client.js DELETED
@@ -1,190 +0,0 @@
1
- var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
2
- if (kind === "m") throw new TypeError("Private method is not writable");
3
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
4
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
5
- return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
6
- };
7
- var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
8
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
9
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
- return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
- };
12
- var _Applet_instances, _Applet_state, _Applet_dispatchEvent;
13
- import { AppletMessage, } from './types';
14
- const hiddenContainer = document.createElement('iframe');
15
- hiddenContainer.style.display = 'none';
16
- document.body.appendChild(hiddenContainer);
17
- export async function list(url) {
18
- url = parseUrl(url);
19
- try {
20
- const request = await fetch(`${url}/manifest.json`);
21
- const appManifest = await request.json();
22
- const appletUrls = appManifest.applets;
23
- const manifests = {};
24
- const manifestRequests = appletUrls.map(async (appletUrl) => {
25
- appletUrl = parseUrl(appletUrl, url);
26
- const request = await fetch(`${appletUrl}/manifest.json`);
27
- const manifest = await request.json();
28
- manifests[appletUrl] = manifest;
29
- });
30
- await Promise.all(manifestRequests);
31
- return manifests;
32
- }
33
- catch {
34
- return {};
35
- }
36
- }
37
- const defaultOpts = {
38
- headless: false,
39
- unsafe: false,
40
- };
41
- export async function load(url, container, opts) {
42
- const _opts = Object.assign(defaultOpts, opts ?? {});
43
- if (!container) {
44
- container = hiddenContainer;
45
- _opts.headless = true;
46
- }
47
- if (!_opts.unsafe)
48
- container.setAttribute('sandbox', 'allow-scripts allow-forms');
49
- url = parseUrl(url);
50
- const manifest = await loadManifest(`${url}`);
51
- const applet = new Applet();
52
- applet.manifest = manifest;
53
- applet.actions = manifest.actions;
54
- applet.container = container;
55
- container.src = applet.manifest.entrypoint;
56
- return new Promise((resolve) => {
57
- applet.on('ready', () => {
58
- const initMessage = new AppletMessage('init', {
59
- headless: _opts.headless,
60
- });
61
- applet.send(initMessage);
62
- resolve(applet);
63
- });
64
- });
65
- }
66
- export class Applet extends EventTarget {
67
- constructor() {
68
- super();
69
- _Applet_instances.add(this);
70
- this.actions = [];
71
- _Applet_state.set(this, void 0);
72
- this.on('state', (message) => {
73
- __classPrivateFieldSet(this, _Applet_state, message.state, "f");
74
- __classPrivateFieldGet(this, _Applet_instances, "m", _Applet_dispatchEvent).call(this, 'stateupdated', message.state);
75
- });
76
- this.on('resize', (message) => {
77
- this.resizeContainer(message.dimensions);
78
- });
79
- }
80
- get state() {
81
- return __classPrivateFieldGet(this, _Applet_state, "f");
82
- }
83
- set state(state) {
84
- __classPrivateFieldSet(this, _Applet_state, state, "f");
85
- this.send(new AppletMessage('state', { state }));
86
- }
87
- toJson() {
88
- return Object.fromEntries(Object.entries(this).filter(([_, value]) => {
89
- try {
90
- JSON.stringify(value);
91
- return true;
92
- }
93
- catch {
94
- return false;
95
- }
96
- }));
97
- }
98
- resizeContainer(dimensions) {
99
- this.container.style.height = `${dimensions.height + 2}px`;
100
- // if (!this.#styleOverrides) {
101
- // this.#container.style.height = `${dimensions.height}px`;
102
- // }
103
- }
104
- onstateupdated(event) { }
105
- disconnect() { }
106
- async dispatchAction(actionId, params) {
107
- const requestMessage = new AppletMessage('action', {
108
- actionId,
109
- params,
110
- });
111
- return await this.send(requestMessage);
112
- }
113
- async send(message) {
114
- this.container.contentWindow?.postMessage(message.toJson(), '*');
115
- return new Promise((resolve) => {
116
- const listener = (messageEvent) => {
117
- const responseMessage = new AppletMessage(messageEvent.data.type, messageEvent.data);
118
- if (responseMessage.type === 'resolve' &&
119
- responseMessage.id === message.id) {
120
- window.removeEventListener('message', listener);
121
- resolve(responseMessage);
122
- }
123
- };
124
- window.addEventListener('message', listener);
125
- });
126
- }
127
- async on(messageType, callback) {
128
- const listener = async (messageEvent) => {
129
- if (messageEvent.source !== this.container.contentWindow)
130
- return;
131
- if (messageEvent.data.type !== messageType)
132
- return;
133
- const message = new AppletMessage(messageEvent.data.type, messageEvent.data);
134
- await callback(message);
135
- this.container.contentWindow?.postMessage(new AppletMessage('resolve', { id: message.id }), '*');
136
- };
137
- window.addEventListener('message', listener);
138
- }
139
- }
140
- _Applet_state = new WeakMap(), _Applet_instances = new WeakSet(), _Applet_dispatchEvent = function _Applet_dispatchEvent(id, detail) {
141
- if (typeof this[`on${id}`] === 'function') {
142
- this[`on${id}`](detail);
143
- }
144
- this.dispatchEvent(new CustomEvent(id, { detail }));
145
- };
146
- /* Helpers */
147
- function parseUrl(url, base) {
148
- if (['http', 'https'].includes(url.split('://')[0])) {
149
- return url;
150
- }
151
- let path = trimSlashes(url);
152
- url = `${base || window.location.origin}/${path}`;
153
- return url;
154
- }
155
- function trimSlashes(str) {
156
- return str.replace(/^\/+|\/+$/g, '');
157
- }
158
- export async function loadManifest(url) {
159
- url = parseUrl(url);
160
- const request = await fetch(`${url}/manifest.json`);
161
- const appletManifest = await request.json();
162
- if (appletManifest.type !== 'applet') {
163
- throw new Error("URL doesn't point to a valid applet manifest.");
164
- }
165
- appletManifest.entrypoint = parseUrl(appletManifest.entrypoint, url);
166
- return appletManifest;
167
- }
168
- export async function getHeaders(url) {
169
- url = parseUrl(url);
170
- try {
171
- const request = await fetch(`${url}/manifest.json`);
172
- const appManifest = await request.json();
173
- const appletHeaders = appManifest.applets;
174
- return appletHeaders ?? [];
175
- }
176
- catch {
177
- return [];
178
- }
179
- }
180
- export async function getManifests(url) {
181
- url = parseUrl(url);
182
- const request = await fetch(`${url}/manifest.json`);
183
- const headers = (await request.json()).applets;
184
- const manifests = await Promise.all(headers.map(async (header) => {
185
- const appletUrl = parseUrl(header.url);
186
- const request = await fetch(`${appletUrl}/manifest.json`);
187
- return await request.json();
188
- }));
189
- return manifests ?? [];
190
- }
package/dist/context.d.ts DELETED
@@ -1,27 +0,0 @@
1
- import { ActionHandlerDict, AppletMessage, AppletMessageType, AppletMessageCallback, ActionParams, ActionHandler } from './types';
2
- /**
3
- * Context
4
- */
5
- export declare class AppletContext<StateType = any> extends EventTarget {
6
- #private;
7
- client: AppletClient;
8
- actionHandlers: ActionHandlerDict;
9
- headless: boolean;
10
- connect(): this;
11
- setActionHandler<T extends ActionParams>(actionId: string, handler: ActionHandler<T>): void;
12
- set state(state: StateType);
13
- get state(): StateType;
14
- setState(state: StateType, shouldRender?: boolean): Promise<void>;
15
- onload(): Promise<void> | void;
16
- onready(): Promise<void> | void;
17
- onrender(): void;
18
- }
19
- /**
20
- * Client
21
- */
22
- declare class AppletClient {
23
- on(messageType: AppletMessageType, callback: AppletMessageCallback): void;
24
- send(message: AppletMessage): Promise<void>;
25
- }
26
- export declare const appletContext: AppletContext<any>;
27
- export {};
package/dist/context.js DELETED
@@ -1,136 +0,0 @@
1
- var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
2
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
3
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
4
- return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
5
- };
6
- var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
7
- if (kind === "m") throw new TypeError("Private method is not writable");
8
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
9
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
10
- return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
11
- };
12
- var _AppletContext_state;
13
- import { AppletMessage, } from './types';
14
- /**
15
- * Context
16
- */
17
- export class AppletContext extends EventTarget {
18
- constructor() {
19
- super(...arguments);
20
- this.actionHandlers = {};
21
- _AppletContext_state.set(this, void 0);
22
- this.headless = false;
23
- }
24
- connect() {
25
- this.client = new AppletClient();
26
- const startup = async () => {
27
- await this.onload();
28
- this.client.send(new AppletMessage('ready'));
29
- this.dispatchEvent(new CustomEvent('ready'));
30
- await this.onready();
31
- };
32
- if (document.readyState === 'complete' ||
33
- document.readyState === 'interactive') {
34
- setTimeout(startup, 1);
35
- }
36
- else {
37
- window.addEventListener('DOMContentLoaded', startup);
38
- }
39
- const resizeObserver = new ResizeObserver((entries) => {
40
- for (let entry of entries) {
41
- const message = new AppletMessage('resize', {
42
- dimensions: {
43
- width: entry.contentRect.width,
44
- height: entry.contentRect.height,
45
- },
46
- });
47
- this.client.send(message);
48
- }
49
- });
50
- resizeObserver.observe(document.querySelector('html'));
51
- this.client.on('init', (message) => {
52
- const initMessage = message;
53
- this.headless = initMessage.headless;
54
- });
55
- this.client.on('state', (message) => {
56
- if (!isStateMessage(message)) {
57
- throw new TypeError("Message doesn't match type StateMessage");
58
- }
59
- // Don't render when state updates match the current state
60
- // this retains cursor positions in text fields, for example
61
- if (JSON.stringify(message.state) === JSON.stringify(__classPrivateFieldGet(this, _AppletContext_state, "f")))
62
- return;
63
- __classPrivateFieldSet(this, _AppletContext_state, message.state, "f");
64
- // BUG: For some reason regular applets were loading headless, when instantiated not on a page reload
65
- // if (!this.headless) {
66
- this.onrender();
67
- this.dispatchEvent(new CustomEvent('render'));
68
- // }
69
- });
70
- this.client.on('action', async (message) => {
71
- if (!isActionMessage(message)) {
72
- throw new TypeError("Message doesn't match type AppletMessage.");
73
- }
74
- if (Object.keys(this.actionHandlers).includes(message.actionId)) {
75
- await this.actionHandlers[message.actionId](message.params);
76
- }
77
- });
78
- return this;
79
- }
80
- setActionHandler(actionId, handler) {
81
- this.actionHandlers[actionId] = handler;
82
- }
83
- set state(state) {
84
- this.setState(state);
85
- }
86
- get state() {
87
- return __classPrivateFieldGet(this, _AppletContext_state, "f");
88
- }
89
- async setState(state, shouldRender) {
90
- const message = new AppletMessage('state', { state });
91
- await this.client.send(message);
92
- __classPrivateFieldSet(this, _AppletContext_state, state, "f");
93
- if (shouldRender !== false && !this.headless) {
94
- this.onrender();
95
- this.dispatchEvent(new CustomEvent('render'));
96
- }
97
- }
98
- onload() { }
99
- onready() { }
100
- onrender() { }
101
- }
102
- _AppletContext_state = new WeakMap();
103
- function isActionMessage(message) {
104
- return message.type === 'action';
105
- }
106
- function isStateMessage(message) {
107
- return message.type === 'state';
108
- }
109
- /**
110
- * Client
111
- */
112
- class AppletClient {
113
- on(messageType, callback) {
114
- window.addEventListener('message', async (messageEvent) => {
115
- if (messageEvent.data.type !== messageType)
116
- return;
117
- const message = new AppletMessage(messageEvent.data.type, messageEvent.data);
118
- await callback(message);
119
- window.parent.postMessage(new AppletMessage('resolve', { id: message.id }), '*');
120
- });
121
- }
122
- send(message) {
123
- window.parent.postMessage(message.toJson(), '*');
124
- return new Promise((resolve) => {
125
- const listener = (messageEvent) => {
126
- if (messageEvent.data.type === 'resolve' &&
127
- messageEvent.data.id === message.id) {
128
- window.removeEventListener('message', listener);
129
- resolve();
130
- }
131
- };
132
- window.addEventListener('message', listener);
133
- });
134
- }
135
- }
136
- export const appletContext = new AppletContext();
package/dist/types.d.ts DELETED
@@ -1,73 +0,0 @@
1
- export interface AppletManifest {
2
- type: 'applet';
3
- name: string;
4
- description?: string;
5
- icon?: string;
6
- frameless?: boolean;
7
- entrypoint?: string;
8
- actions?: AppletAction[];
9
- }
10
- export interface AppletManifestDict {
11
- [url: string]: AppletManifest;
12
- }
13
- export interface AppletAction {
14
- id: string;
15
- name?: string;
16
- description?: string;
17
- params?: ActionParamSchema;
18
- }
19
- export interface AppletHeader {
20
- name: string;
21
- description: string;
22
- url: string;
23
- actions: {
24
- id: string;
25
- description: string;
26
- params: {
27
- [key: string]: string;
28
- };
29
- }[];
30
- }
31
- export type AppletState = any;
32
- export type ActionParamSchema = Record<string, {
33
- description: string;
34
- type: 'string';
35
- }>;
36
- export type ActionParams<T = any> = Record<string, T>;
37
- export type ActionHandlerDict = {
38
- [key: string]: ActionHandler<any>;
39
- };
40
- export type ActionHandler<T extends ActionParams> = (params: T) => void | Promise<void>;
41
- export type AnyAppletMessage = AppletMessage | AppletStateMessage | AppletActionMessage;
42
- export interface AppletStateMessage<T = any> extends AppletMessage {
43
- type: 'state';
44
- state: T;
45
- }
46
- export interface AppletResizeMessage extends AppletMessage {
47
- type: 'resize';
48
- dimensions: {
49
- height: number;
50
- width: number;
51
- };
52
- }
53
- export interface AppletActionMessage<T = any> extends AppletMessage {
54
- type: 'action';
55
- actionId: string;
56
- params: T;
57
- }
58
- export interface AppletInitMessage extends AppletMessage {
59
- type: 'init';
60
- headless: boolean;
61
- }
62
- export declare class AppletMessage<T = any> {
63
- type: AppletMessageType;
64
- id: string;
65
- timeStamp: number;
66
- constructor(type: AppletMessageType, values?: T);
67
- toJson(): {
68
- [k: string]: any;
69
- };
70
- resolve(): void;
71
- }
72
- export type AppletMessageType = 'action' | 'actions' | 'render' | 'state' | 'init' | 'ready' | 'resolve' | 'resize';
73
- export type AppletMessageCallback = (message: AnyAppletMessage) => Promise<void> | void;
package/dist/types.js DELETED
@@ -1,21 +0,0 @@
1
- export class AppletMessage {
2
- constructor(type, values) {
3
- this.timeStamp = Date.now();
4
- this.type = type;
5
- this.id = crypto.randomUUID();
6
- if (values)
7
- Object.assign(this, values);
8
- }
9
- toJson() {
10
- return Object.fromEntries(Object.entries(this).filter(([_, value]) => {
11
- try {
12
- JSON.stringify(value);
13
- return true;
14
- }
15
- catch {
16
- return false;
17
- }
18
- }));
19
- }
20
- resolve() { }
21
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,52 +0,0 @@
1
- import { applets } from '../index';
2
- class AppletView extends HTMLElement {
3
- connectedCallback() {
4
- const styles = document.createElement('style');
5
- styles.textContent = this.styles;
6
- this.appendChild(styles);
7
- this.container = document.createElement('iframe');
8
- this.appendChild(this.container);
9
- }
10
- get styles() {
11
- return /*css*/ `
12
- applet-frame {
13
- display: flex;
14
- flex-direction: column;
15
- }
16
-
17
- applet-frame iframe {
18
- border: none;
19
- }
20
-
21
- applet-frame:not(.frameless) {
22
- border: 1px solid #ddd;
23
- }
24
-
25
- applet-frame.frameless {
26
- padding: 0 7px;
27
- }
28
- `;
29
- }
30
- set url(url) {
31
- setTimeout(() => this.loadApplet(url), 1);
32
- }
33
- async loadApplet(url) {
34
- if (!this.container)
35
- return;
36
- this.applet = await applets.load(url, this.container);
37
- if (this.applet.manifest.frameless)
38
- this.classList.add('frameless');
39
- this.applet.onstateupdated = () => {
40
- this.dispatchEvent(new CustomEvent('stateupdated', { detail: this.applet.state }));
41
- };
42
- this.dispatchEvent(new CustomEvent('load'));
43
- }
44
- set state(state) {
45
- if (this.applet)
46
- this.applet.state = state;
47
- this.addEventListener('load', () => {
48
- this.applet.state = state;
49
- });
50
- }
51
- }
52
- customElements.define('applet-frame', AppletView);
@@ -1 +0,0 @@
1
- export {};
@@ -1,52 +0,0 @@
1
- import { applets } from '../index.js';
2
- class AppletFrame extends HTMLElement {
3
- connectedCallback() {
4
- const styles = document.createElement('style');
5
- styles.textContent = this.styles;
6
- this.appendChild(styles);
7
- this.container = document.createElement('iframe');
8
- this.appendChild(this.container);
9
- }
10
- get styles() {
11
- return /*css*/ `
12
- applet-frame {
13
- display: flex;
14
- flex-direction: column;
15
- }
16
-
17
- applet-frame iframe {
18
- border: none;
19
- }
20
-
21
- applet-frame:not(.frameless) {
22
- border: 1px solid #ddd;
23
- }
24
-
25
- applet-frame.frameless {
26
- padding: 0 7px;
27
- }
28
- `;
29
- }
30
- set url(url) {
31
- setTimeout(() => this.loadApplet(url), 1);
32
- }
33
- async loadApplet(url) {
34
- if (!this.container)
35
- return;
36
- this.applet = await applets.load(url, this.container);
37
- if (this.applet.manifest.frameless)
38
- this.classList.add('frameless');
39
- this.applet.onstateupdated = () => {
40
- this.dispatchEvent(new CustomEvent('stateupdated', { detail: this.applet.state }));
41
- };
42
- this.dispatchEvent(new CustomEvent('load'));
43
- }
44
- set state(state) {
45
- if (this.applet)
46
- this.applet.state = state;
47
- this.addEventListener('load', () => {
48
- this.applet.state = state;
49
- });
50
- }
51
- }
52
- customElements.define('applet-frame', AppletFrame);