@kokimoki/app 0.1.0 → 0.2.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/dist/index.d.ts CHANGED
@@ -1,11 +1,8 @@
1
- import { KokimokiClient } from "./kokimoki-client";
2
- import { UserMediaHandler } from "./user-media-handler";
3
- export * from "./types/api";
4
1
  export * from "./types/tasks";
5
2
  export * from "./types/common";
6
3
  export * from "./types/events";
4
+ export * from "./types/upload";
7
5
  export * from "./fields";
8
6
  export * from "./kokimoki-client";
9
7
  export * from "./synced-store";
10
- export declare const kmClient: KokimokiClient<any>;
11
- export declare const userMediaHandler: UserMediaHandler;
8
+ export * from "./user-media-handler";
package/dist/index.js CHANGED
@@ -1,11 +1,8 @@
1
- import { KokimokiClient } from "./kokimoki-client";
2
- import { UserMediaHandler } from "./user-media-handler";
3
- export * from "./types/api";
4
1
  export * from "./types/tasks";
5
2
  export * from "./types/common";
6
3
  export * from "./types/events";
4
+ export * from "./types/upload";
7
5
  export * from "./fields";
8
6
  export * from "./kokimoki-client";
9
7
  export * from "./synced-store";
10
- export const kmClient = new KokimokiClient();
11
- export const userMediaHandler = new UserMediaHandler();
8
+ export * from "./user-media-handler";
@@ -2,7 +2,6 @@ import { HocuspocusProvider } from "@hocuspocus/provider";
2
2
  import type TypedEmitter from "typed-emitter";
3
3
  import type { SyncedStore } from "./synced-store";
4
4
  import type { DocTypeDescription } from "@syncedstore/core/types/doc";
5
- import type { UploadedMedia } from "./types/api";
6
5
  import type { KokimokiClientEvents } from "./types/events";
7
6
  declare const KokimokiClient_base: new <T>() => TypedEmitter<KokimokiClientEvents<T>>;
8
7
  export declare class KokimokiClient<StatelessDataT = any> extends KokimokiClient_base<StatelessDataT> {
@@ -10,12 +9,14 @@ export declare class KokimokiClient<StatelessDataT = any> extends KokimokiClient
10
9
  private _appId?;
11
10
  private _wsUrl?;
12
11
  private _apiUrl?;
13
- private _id;
14
- private _token;
12
+ private _clientId?;
13
+ private _token?;
15
14
  private _providers;
16
15
  constructor();
17
- get id(): string;
16
+ get clientId(): string;
17
+ get appId(): string;
18
18
  get token(): string;
19
+ get apiUrl(): string;
19
20
  connect(host: string, appId: string): Promise<void>;
20
21
  setProvider<T extends DocTypeDescription>(name: string, store: SyncedStore<T>): Promise<void>;
21
22
  removeProvider(name: string): void;
@@ -23,6 +24,6 @@ export declare class KokimokiClient<StatelessDataT = any> extends KokimokiClient
23
24
  serverTimestamp(): number;
24
25
  sendStatelessToClient(room: string, clientId: string, data: StatelessDataT): void;
25
26
  sendStatelessToRoom(room: string, data: StatelessDataT, self?: boolean): void;
26
- mediaUpload(blob: Blob): Promise<UploadedMedia>;
27
27
  }
28
+ export declare const kmClient: KokimokiClient<any>;
28
29
  export {};
@@ -1,26 +1,27 @@
1
1
  import { HocuspocusProvider } from "@hocuspocus/provider";
2
2
  import EventEmitter from "events";
3
- import { sendMediaUpload } from "./api/sendMediaUpload";
4
- import { getETags } from "./api/getETags";
5
- import { uploadMedia } from "./api/uploadMedia";
6
- import { errorMessage } from "./utils";
7
- import { fetchAuth } from "./api/fetchAuth";
8
3
  export class KokimokiClient extends EventEmitter {
9
4
  serverTimeOffset = 0;
10
5
  _appId;
11
6
  _wsUrl;
12
7
  _apiUrl;
13
- _id = "";
14
- _token = "";
8
+ _clientId;
9
+ _token;
15
10
  _providers = new Map();
16
11
  constructor() {
17
12
  super();
18
13
  }
19
- get id() {
20
- if (!this._id) {
14
+ get clientId() {
15
+ if (!this._clientId) {
21
16
  throw new Error("Client not connected");
22
17
  }
23
- return this._id;
18
+ return this._clientId;
19
+ }
20
+ get appId() {
21
+ if (!this._appId) {
22
+ throw new Error("Client not connected");
23
+ }
24
+ return this._appId;
24
25
  }
25
26
  get token() {
26
27
  if (!this._token) {
@@ -28,6 +29,12 @@ export class KokimokiClient extends EventEmitter {
28
29
  }
29
30
  return this._token;
30
31
  }
32
+ get apiUrl() {
33
+ if (!this._apiUrl) {
34
+ throw new Error("Client not connected");
35
+ }
36
+ return this._apiUrl;
37
+ }
31
38
  async connect(host, appId) {
32
39
  this._appId = appId;
33
40
  // Set up the URLs
@@ -37,14 +44,17 @@ export class KokimokiClient extends EventEmitter {
37
44
  // Fetch the auth token
38
45
  let clientToken = localStorage.getItem("KM_TOKEN");
39
46
  const startTime = Date.now();
40
- const { clientId, appToken, serverTime, token } = await fetchAuth({
41
- appId,
42
- clientToken,
43
- apiUrl: this._apiUrl,
47
+ const res = await fetch(`${this.apiUrl}/auth/token?appId=${appId}`, {
48
+ method: "GET",
49
+ headers: new Headers({
50
+ "Content-Type": "application/json",
51
+ Authorization: `Bearer ${clientToken}`,
52
+ }),
44
53
  });
54
+ const { clientId, appToken, serverTime, token } = await res.json();
45
55
  const endTime = Date.now();
46
56
  const ping = Math.round((endTime - startTime) / 2);
47
- this._id = clientId;
57
+ this._clientId = clientId;
48
58
  this._token = appToken;
49
59
  this.serverTimeOffset = Date.now() - serverTime - ping;
50
60
  localStorage.setItem("KM_TOKEN", token);
@@ -102,22 +112,5 @@ export class KokimokiClient extends EventEmitter {
102
112
  }
103
113
  provider.sendStateless(JSON.stringify({ data, self }));
104
114
  }
105
- async mediaUpload(blob) {
106
- if (!this._apiUrl) {
107
- throw new Error("Client not connected");
108
- }
109
- try {
110
- const uploadData = await sendMediaUpload(blob, {
111
- apiUrl: this._apiUrl,
112
- appToken: this.token,
113
- });
114
- const { id } = uploadData;
115
- const etags = await getETags(blob, uploadData);
116
- const { url } = await uploadMedia({ id, etags }, { apiUrl: this._apiUrl, appToken: this.token });
117
- return { url, id };
118
- }
119
- catch (error) {
120
- throw new Error(`Error while uploading media: ${errorMessage(error)}`);
121
- }
122
- }
123
115
  }
116
+ export const kmClient = new KokimokiClient();
@@ -0,0 +1,15 @@
1
+ import type { Upload } from "./types/upload";
2
+ export declare class KokimokiStorage {
3
+ private createUpload;
4
+ private uploadChunks;
5
+ private completeUpload;
6
+ upload(name: string, blob: Blob, tags?: string[]): Promise<Upload>;
7
+ listUploads(filter?: {
8
+ clientId?: string;
9
+ mimeTypes?: string[];
10
+ }, skip?: number, limit?: number): Promise<{
11
+ total: any;
12
+ items: any;
13
+ }>;
14
+ }
15
+ export declare const kmStorage: KokimokiStorage;
@@ -0,0 +1,64 @@
1
+ import { kmClient } from "./kokimoki-client";
2
+ export class KokimokiStorage {
3
+ async createUpload(name, blob, tags) {
4
+ const res = await fetch(`${kmClient.apiUrl}/uploads`, {
5
+ method: "POST",
6
+ headers: new Headers({
7
+ Authorization: `Bearer ${kmClient.token}`,
8
+ "Content-Type": "application/json",
9
+ }),
10
+ body: JSON.stringify({
11
+ name,
12
+ size: blob.size,
13
+ mimeType: blob.type,
14
+ tags,
15
+ }),
16
+ });
17
+ return await res.json();
18
+ }
19
+ async uploadChunks(blob, chunkSize, signedUrls) {
20
+ return await Promise.all(signedUrls.map(async (url, index) => {
21
+ const start = index * chunkSize;
22
+ const end = Math.min(start + chunkSize, blob.size);
23
+ const chunk = blob.slice(start, end);
24
+ const res = await fetch(url, { method: "PUT", body: chunk });
25
+ return JSON.parse(res.headers.get("ETag") || '""');
26
+ }));
27
+ }
28
+ async completeUpload(id, etags) {
29
+ const res = await fetch(`${kmClient.apiUrl}/uploads/${id}`, {
30
+ method: "PUT",
31
+ headers: new Headers({
32
+ Authorization: `Bearer ${kmClient.token}`,
33
+ "Content-Type": "application/json",
34
+ }),
35
+ body: JSON.stringify({ etags }),
36
+ });
37
+ return await res.json();
38
+ }
39
+ async upload(name, blob, tags = []) {
40
+ const { id, chunkSize, urls } = await this.createUpload(name, blob, tags);
41
+ const etags = await this.uploadChunks(blob, chunkSize, urls);
42
+ return await this.completeUpload(id, etags);
43
+ }
44
+ async listUploads(filter = {}, skip = 0, limit = 100) {
45
+ const url = new URL("/uploads", kmClient.apiUrl);
46
+ url.searchParams.set("skip", skip.toString());
47
+ url.searchParams.set("limit", limit.toString());
48
+ if (filter.clientId) {
49
+ url.searchParams.set("clientId", filter.clientId);
50
+ }
51
+ if (filter.mimeTypes) {
52
+ url.searchParams.set("mimeTypes", filter.mimeTypes.join());
53
+ }
54
+ const res = await fetch(url.href, {
55
+ headers: new Headers({
56
+ Authorization: `Bearer ${kmClient.token}`,
57
+ "Content-Type": "application/json",
58
+ }),
59
+ });
60
+ const { total, items } = await res.json();
61
+ return { total, items };
62
+ }
63
+ }
64
+ export const kmStorage = new KokimokiStorage();
@@ -0,0 +1,12 @@
1
+ export interface Upload {
2
+ id: string;
3
+ appId: string;
4
+ clientId: string;
5
+ createdAt: Date;
6
+ name: string;
7
+ url: string;
8
+ size: number;
9
+ mimeType: string;
10
+ completed: boolean;
11
+ tags: string[];
12
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -12,4 +12,5 @@ export declare class UserMediaHandler extends UserMediaHandler_base {
12
12
  setFacingMode(facingMode: "user" | "environment"): void;
13
13
  toggleFacingMode(): void;
14
14
  }
15
+ export declare const userMediaHandler: UserMediaHandler;
15
16
  export {};
@@ -59,3 +59,4 @@ export class UserMediaHandler extends EventEmitter {
59
59
  this.setFacingMode(this._facingMode === "user" ? "environment" : "user");
60
60
  }
61
61
  }
62
+ export const userMediaHandler = new UserMediaHandler();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kokimoki/app",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "description": "Kokimoki app",
6
6
  "main": "dist/index.js",