@content-island/vscode-api-client 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/README.md ADDED
@@ -0,0 +1,58 @@
1
+ # @content-island/vscode-api-client
2
+
3
+ ## Installation
4
+
5
+ ```bash
6
+ npm install @content-island/vscode-api-client
7
+ ```
8
+
9
+ ## Examples
10
+
11
+ ### Basic Usage
12
+
13
+ _./src/client.ts_
14
+
15
+ ```ts
16
+ import { createClient } from '@content-island/vscode-api-client';
17
+
18
+ export const client = createClient();
19
+ ```
20
+
21
+ _./src/index.ts_
22
+
23
+ ```ts
24
+ import { client } from './client';
25
+ import * as vscode from 'vscode';
26
+
27
+ export async function activate(context: vscode.ExtensionContext) {
28
+ client.setContext(context);
29
+
30
+ const uriHandler = vscode.window.registerUriHandler({
31
+ handleUri: async (uri: vscode.Uri) => {
32
+ const params = new URLSearchParams(uri.query);
33
+ const authorizationCode = params.get('authorizationCode');
34
+ const userId = params.get('userId');
35
+
36
+ await client.authorize(authorizationCode, userId);
37
+ },
38
+ });
39
+ }
40
+ ```
41
+
42
+ _./src/commands/pull-content.ts_
43
+
44
+ ```ts
45
+ import { client } from '../client';
46
+ import * as vscode from 'vscode';
47
+
48
+ export const pullContent = async () => {
49
+ const project = await client.getProject();
50
+ if (!project) {
51
+ vscode.window.showErrorMessage('No project found. Please link your project first.');
52
+ return;
53
+ }
54
+
55
+ // Logic to pull content from the project
56
+ vscode.window.showInformationMessage(`Content pulled for project: ${project.name}`);
57
+ };
58
+ ```
@@ -0,0 +1,324 @@
1
+ import * as vscode from 'vscode';
2
+
3
+ declare type AllowedModelFields<M extends Model, ValueType> = Partial<Omit<{
4
+ [K in keyof M as M[K] extends string | number | boolean ? `fields.${string & K}` : never]?: ValueType;
5
+ }, 'fields.id' | 'fields.language'>>;
6
+
7
+ declare interface ApiClient {
8
+ getProject: () => Promise<Project>;
9
+ getContentList: <M extends Model = Model & Record<string, any>>(queryParam?: ContentListQueryParams<M>) => Promise<M[]>;
10
+ getContent: <M extends Model = Model & Record<string, any>>(queryParam: ContentQueryParams<M>) => Promise<M>;
11
+ getRawContentList: <M extends Model = Model & Record<string, any>>(queryParam?: ContentListQueryParams<M>) => Promise<Content[]>;
12
+ getRawContent: <M extends Model = Model & Record<string, any>>(queryParam: ContentQueryParams<M>) => Promise<Content>;
13
+ getContentListSize: <M extends Model = Model & Record<string, any>>(queryParam?: ContentListSizeQueryParams<M>) => Promise<number>;
14
+ updateContentFieldValue: (contentId: string, fieldId: string, value: any) => Promise<boolean>;
15
+ }
16
+
17
+ export declare type ClientFilter<Type = string | boolean> = Type | {
18
+ in?: Type[];
19
+ };
20
+
21
+ export declare interface Content {
22
+ id: string;
23
+ name: string;
24
+ contentType: Lookup;
25
+ lastUpdate: string;
26
+ fields: Field[];
27
+ }
28
+
29
+ export declare type ContentListQueryParams<M extends Model = Model & Record<string, any>> = QueryParams<M>;
30
+
31
+ export declare type ContentListSizeQueryParams<M extends Model = Model & Record<string, any>> = Omit<QueryParams<M>, 'sort' | 'pagination' | 'includeRelatedContent'>;
32
+
33
+ export declare type ContentQueryParams<M extends Model = Model & Record<string, any>> = Omit<QueryParams<M>, 'sort' | 'pagination'>;
34
+
35
+ declare interface ContentType extends Lookup {
36
+ fields: ContentTypeField[];
37
+ }
38
+
39
+ declare interface ContentTypeField extends Lookup {
40
+ type: FieldType;
41
+ tsType: string;
42
+ isArray: boolean;
43
+ isRequired?: boolean;
44
+ }
45
+
46
+ export declare const createClient: (options?: VSCodeClientOptions) => VSCodeApiClient;
47
+
48
+ export declare interface Field {
49
+ id: string;
50
+ name: string;
51
+ value: any;
52
+ type: FieldType;
53
+ isArray: boolean;
54
+ language: string;
55
+ }
56
+
57
+ declare type FieldEntityType = `${string}|${string}`;
58
+
59
+ export declare type FieldType =
60
+ | 'short-text'
61
+ | 'long-text'
62
+ | 'number'
63
+ | 'date'
64
+ | 'date-time'
65
+ | 'media'
66
+ | 'boolean'
67
+ | 'color'
68
+ | FieldEntityType;
69
+
70
+ declare type FilterableFields<M extends Model = Model> = {
71
+ id?: ClientFilter;
72
+ lastUpdate?: ClientFilter;
73
+ language?: ClientFilter<M['language'] extends undefined ? string : M['language']>;
74
+ contentType?: ClientFilter;
75
+ includeRelatedContent?: boolean;
76
+ } & AllowedModelFields<M, ClientFilter>;
77
+
78
+ export declare type LanguageCode =
79
+ | 'aa'
80
+ | 'ab'
81
+ | 'ae'
82
+ | 'af'
83
+ | 'ak'
84
+ | 'am'
85
+ | 'an'
86
+ | 'ar'
87
+ | 'as'
88
+ | 'av'
89
+ | 'ay'
90
+ | 'az'
91
+ | 'ba'
92
+ | 'be'
93
+ | 'bg'
94
+ | 'bh'
95
+ | 'bi'
96
+ | 'bm'
97
+ | 'bn'
98
+ | 'bo'
99
+ | 'br'
100
+ | 'bs'
101
+ | 'ca'
102
+ | 'ce'
103
+ | 'ch'
104
+ | 'co'
105
+ | 'cr'
106
+ | 'cs'
107
+ | 'cu'
108
+ | 'cv'
109
+ | 'cy'
110
+ | 'da'
111
+ | 'de'
112
+ | 'dv'
113
+ | 'dz'
114
+ | 'ee'
115
+ | 'el'
116
+ | 'en'
117
+ | 'eo'
118
+ | 'es'
119
+ | 'et'
120
+ | 'eu'
121
+ | 'fa'
122
+ | 'ff'
123
+ | 'fi'
124
+ | 'fj'
125
+ | 'fo'
126
+ | 'fr'
127
+ | 'fy'
128
+ | 'ga'
129
+ | 'gd'
130
+ | 'gl'
131
+ | 'gn'
132
+ | 'gu'
133
+ | 'gv'
134
+ | 'ha'
135
+ | 'he'
136
+ | 'hi'
137
+ | 'ho'
138
+ | 'hr'
139
+ | 'ht'
140
+ | 'hu'
141
+ | 'hy'
142
+ | 'hz'
143
+ | 'ia'
144
+ | 'id'
145
+ | 'ie'
146
+ | 'ig'
147
+ | 'ii'
148
+ | 'ik'
149
+ | 'io'
150
+ | 'is'
151
+ | 'it'
152
+ | 'iu'
153
+ | 'ja'
154
+ | 'jv'
155
+ | 'ka'
156
+ | 'kg'
157
+ | 'ki'
158
+ | 'kj'
159
+ | 'kk'
160
+ | 'kl'
161
+ | 'km'
162
+ | 'kn'
163
+ | 'ko'
164
+ | 'kr'
165
+ | 'ks'
166
+ | 'ku'
167
+ | 'kv'
168
+ | 'kw'
169
+ | 'ky'
170
+ | 'la'
171
+ | 'lb'
172
+ | 'lg'
173
+ | 'li'
174
+ | 'ln'
175
+ | 'lo'
176
+ | 'lt'
177
+ | 'lu'
178
+ | 'lv'
179
+ | 'mg'
180
+ | 'mh'
181
+ | 'mi'
182
+ | 'mk'
183
+ | 'ml'
184
+ | 'mn'
185
+ | 'mr'
186
+ | 'ms'
187
+ | 'mt'
188
+ | 'my'
189
+ | 'na'
190
+ | 'nb'
191
+ | 'nd'
192
+ | 'ne'
193
+ | 'ng'
194
+ | 'nl'
195
+ | 'nn'
196
+ | 'no'
197
+ | 'nr'
198
+ | 'nv'
199
+ | 'ny'
200
+ | 'oc'
201
+ | 'oj'
202
+ | 'om'
203
+ | 'or'
204
+ | 'os'
205
+ | 'pa'
206
+ | 'pi'
207
+ | 'pl'
208
+ | 'ps'
209
+ | 'pt'
210
+ | 'qu'
211
+ | 'rm'
212
+ | 'rn'
213
+ | 'ro'
214
+ | 'ru'
215
+ | 'rw'
216
+ | 'sa'
217
+ | 'sc'
218
+ | 'sd'
219
+ | 'se'
220
+ | 'sg'
221
+ | 'si'
222
+ | 'sk'
223
+ | 'sl'
224
+ | 'sm'
225
+ | 'sn'
226
+ | 'so'
227
+ | 'sq'
228
+ | 'sr'
229
+ | 'ss'
230
+ | 'st'
231
+ | 'su'
232
+ | 'sv'
233
+ | 'sw'
234
+ | 'ta'
235
+ | 'te'
236
+ | 'tg'
237
+ | 'th'
238
+ | 'ti'
239
+ | 'tk'
240
+ | 'tl'
241
+ | 'tn'
242
+ | 'to'
243
+ | 'tr'
244
+ | 'ts'
245
+ | 'tt'
246
+ | 'tw'
247
+ | 'ty'
248
+ | 'ug'
249
+ | 'uk'
250
+ | 'ur'
251
+ | 'uz'
252
+ | 've'
253
+ | 'vi'
254
+ | 'vo'
255
+ | 'wa'
256
+ | 'wo'
257
+ | 'xh'
258
+ | 'yi'
259
+ | 'yo'
260
+ | 'za'
261
+ | 'zh'
262
+ | 'zu';
263
+
264
+ export declare interface Lookup<ID = string> {
265
+ id: ID;
266
+ name: string;
267
+ }
268
+
269
+ export declare const mapContentToModel: <M extends Model<Language> = Model<any> & Record<string, any>, Language = M["language"]>(content: Content, language?: Language) => M;
270
+
271
+ export declare interface Media {
272
+ name: string;
273
+ url: string;
274
+ }
275
+
276
+ export declare type Model<Language = string> = {
277
+ id: string;
278
+ language?: Language;
279
+ lastUpdate?: string;
280
+ };
281
+
282
+ declare interface Options {
283
+ accessToken: string;
284
+ domain?: string;
285
+ secureProtocol?: boolean;
286
+ apiVersion?: string;
287
+ metadata?: string;
288
+ }
289
+
290
+ declare type Pagination = {
291
+ take?: number;
292
+ skip?: number;
293
+ };
294
+
295
+ export declare interface Project {
296
+ id: string;
297
+ name: string;
298
+ languages: LanguageCode[];
299
+ contentTypes?: ContentType[];
300
+ }
301
+
302
+ declare type QueryParams<M extends Model = Model & Record<string, any>> = FilterableFields<M> & {
303
+ sort?: SortableFields<M>;
304
+ pagination?: Pagination;
305
+ };
306
+
307
+ declare type SortableFields<M extends Model> = {
308
+ contentType?: SortOrder;
309
+ lastUpdate?: SortOrder;
310
+ } & AllowedModelFields<M, SortOrder>;
311
+
312
+ declare type SortOrder = 'asc' | 'desc';
313
+
314
+ export declare interface VSCodeApiClient extends ApiClient {
315
+ setVSCodeExtensionContext: (context: vscode.ExtensionContext) => void;
316
+ authorize: (authorizationCode: string, metadata: string) => Promise<void>;
317
+ authorizeByProjectId: (projectId: string) => Promise<void>;
318
+ }
319
+
320
+ export declare type VSCodeClientOptions = Omit<Options, 'accessToken'> & {
321
+ loginDomain?: string;
322
+ };
323
+
324
+ export { }
package/dist/index.js ADDED
@@ -0,0 +1,155 @@
1
+ import { createClient as I } from "@content-island/api-client";
2
+ import { mapContentToModel as Y } from "@content-island/api-client";
3
+ import * as a from "vscode";
4
+ import _ from "node:crypto";
5
+ import A from "node:util";
6
+ let E;
7
+ const P = () => {
8
+ if (!E)
9
+ throw new Error("Extension context has not been set.");
10
+ return E;
11
+ }, S = (t) => {
12
+ E = t;
13
+ };
14
+ let l = {
15
+ getContext: P,
16
+ setContext: S
17
+ };
18
+ const g = {
19
+ IS_PRODUCTION: !0,
20
+ DEFAULT_API_CLIENT_DOMAIN: "api.contentisland.net",
21
+ DEFAULT_API_CLIENT_VERSION: "1.0",
22
+ DEFAULT_LOGIN_DOMAIN: "app.contentisland.net"
23
+ }, d = "content-island-vscode", u = {
24
+ SALT: `${d}.salt`,
25
+ ACCESS_TOKEN_BY_PROJECT_ID: (t) => `${d}.access-token.${t}`,
26
+ METADATA_BY_PROJECT_ID: (t) => `${d}.metadata.${t}`
27
+ }, h = {
28
+ get: async (t) => await l.getContext().secrets.get(u.METADATA_BY_PROJECT_ID(t)),
29
+ set: async (t, o) => {
30
+ await l.getContext().secrets.store(u.METADATA_BY_PROJECT_ID(t), o);
31
+ }
32
+ }, C = {
33
+ get: async (t) => await l.getContext().secrets.get(u.ACCESS_TOKEN_BY_PROJECT_ID(t)),
34
+ set: async (t, o) => {
35
+ await l.getContext().secrets.store(u.ACCESS_TOKEN_BY_PROJECT_ID(t), o);
36
+ }
37
+ }, L = (t) => {
38
+ const s = (t.secureProtocol === void 0 ? g.IS_PRODUCTION : t.secureProtocol) ? "https" : "http", e = t.domain ? t.domain : g.DEFAULT_API_CLIENT_DOMAIN, n = t.apiVersion ? t.apiVersion : g.DEFAULT_API_CLIENT_VERSION;
39
+ return `${s}://${e}/api/${n}`;
40
+ }, y = (t) => {
41
+ const s = (t.secureProtocol === void 0 ? g.IS_PRODUCTION : t.secureProtocol) ? "https" : "http", e = t.loginDomain ? t.loginDomain : g.DEFAULT_LOGIN_DOMAIN;
42
+ return `${s}://${e}/#/?redirect=vscode`;
43
+ }, f = 16, p = (t = f) => _.randomBytes(t).toString("hex"), O = 64, m = "sha512", x = 1e5, D = async (t, o, s = O) => (await A.promisify(_.pbkdf2)(t, o, x, s, m)).toString("hex"), N = async () => {
44
+ const t = l.getContext();
45
+ let o = await t.secrets.get(u.SALT);
46
+ return o || (o = p(32), await t.secrets.store(u.SALT, o)), o;
47
+ }, k = 32, R = async (t) => {
48
+ const o = await N();
49
+ return await D(t, o, k);
50
+ }, w = async (t) => {
51
+ const o = "Open Login Page";
52
+ if (await a.window.showInformationMessage(
53
+ "You need to log in to Content Island to continue. Do you want to open the login page?",
54
+ o
55
+ ) !== o)
56
+ return;
57
+ const e = a.Uri.parse(y(t));
58
+ await a.env.openExternal(e);
59
+ }, M = (t) => {
60
+ try {
61
+ return JSON.parse(t.message);
62
+ } catch {
63
+ return null;
64
+ }
65
+ }, i = (t, o) => async (...s) => {
66
+ try {
67
+ return await t(...s);
68
+ } catch (e) {
69
+ const n = M(e);
70
+ if (n) {
71
+ if (n.status === 401)
72
+ throw await w(o), new Error("Unauthorized: Please log in to continue.");
73
+ n.status === 403 && a.window.showErrorMessage("Access forbidden: You do not have permission to perform this action.");
74
+ }
75
+ throw e;
76
+ }
77
+ }, U = async (t, o, s) => {
78
+ const e = L(t);
79
+ let n;
80
+ const c = {
81
+ authorizationCode: o,
82
+ metadata: s
83
+ };
84
+ try {
85
+ n = await fetch(`${e}/security/vscode/token`, {
86
+ method: "POST",
87
+ headers: {
88
+ "Content-Type": "application/json"
89
+ },
90
+ body: JSON.stringify(c)
91
+ });
92
+ } catch {
93
+ a.window.showErrorMessage(
94
+ "Unable to connect to the authorization server. Check your network connection and try again."
95
+ );
96
+ }
97
+ n.ok || (n.status === 401 ? await w(t) : n.status === 403 && a.window.showErrorMessage(
98
+ "You do not have permission to access this resource. Please check your access rights in Content Island."
99
+ ), a.window.showErrorMessage(
100
+ "Failed to obtain access token. Please complete the authorization in Content Island."
101
+ ));
102
+ try {
103
+ const r = await n.json();
104
+ return (!r?.accessToken || typeof r.accessToken != "string") && (a.window.showErrorMessage("Invalid response from the authorization server."), await w(t)), r.accessToken;
105
+ } catch {
106
+ a.window.showErrorMessage("Error processing response from the authorization server.");
107
+ }
108
+ }, T = (t) => {
109
+ const o = `PREVIEW_${t.accessToken}`, s = I({ ...t, accessToken: o });
110
+ return {
111
+ ...s,
112
+ getProject: async () => {
113
+ const e = await s.getProject();
114
+ return {
115
+ ...e,
116
+ id: await R(e.id)
117
+ };
118
+ }
119
+ };
120
+ }, B = (t = {}) => {
121
+ let o = T({ ...t, accessToken: "" });
122
+ const s = (e, n) => {
123
+ o = T({ ...t, accessToken: e, metadata: n });
124
+ };
125
+ return {
126
+ authorize: async (e, n) => {
127
+ const c = await U(t, e, n);
128
+ s(c, n);
129
+ const r = await o.getProject();
130
+ await C.set(r.id, c), await h.set(r.id, n);
131
+ },
132
+ authorizeByProjectId: async (e) => {
133
+ const n = await C.get(e), c = await h.get(e);
134
+ if (!n || !c) {
135
+ await w(t);
136
+ return;
137
+ }
138
+ s(n, c);
139
+ },
140
+ setVSCodeExtensionContext: (e) => {
141
+ l.setContext(e);
142
+ },
143
+ getProject: (...e) => i(() => o.getProject(...e), t)(),
144
+ getContentList: (...e) => i(() => o.getContentList(...e), t)(),
145
+ getContent: (...e) => i(() => o.getContent(...e), t)(),
146
+ getRawContentList: (...e) => i(() => o.getRawContentList(...e), t)(),
147
+ getRawContent: (...e) => i(() => o.getRawContent(...e), t)(),
148
+ getContentListSize: (...e) => i(() => o.getContentListSize(...e), t)(),
149
+ updateContentFieldValue: (...e) => i(() => o.updateContentFieldValue(...e), t)()
150
+ };
151
+ };
152
+ export {
153
+ B as createClient,
154
+ Y as mapContentToModel
155
+ };
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@content-island/vscode-api-client",
3
+ "version": "0.1.0",
4
+ "description": "Content Island - VSCode Extension API Client",
5
+ "private": false,
6
+ "sideEffects": false,
7
+ "author": "Lemoncode",
8
+ "license": "MIT",
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "type": "module",
13
+ "module": "./dist/index.js",
14
+ "main": "./dist/index.umd.cjs",
15
+ "types": "./dist/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/index.d.ts",
19
+ "import": "./dist/index.js",
20
+ "require": "./dist/index.umd.cjs"
21
+ }
22
+ },
23
+ "imports": {
24
+ "#*": "./src/*"
25
+ },
26
+ "scripts": {
27
+ "prebuild:dev": "sh ./create-dev-env.sh",
28
+ "build:dev": "vite build --mode development",
29
+ "build": "vite build",
30
+ "type-check": "tsc --noEmit --preserveWatchOutput",
31
+ "test": "vitest run -c ./config/test/config.ts",
32
+ "test:watch": "vitest -c ./config/test/config.ts"
33
+ },
34
+ "dependencies": {
35
+ "@content-island/api-client": "0.14.0"
36
+ },
37
+ "devDependencies": {
38
+ "@content-island/common-backend": "*",
39
+ "@types/vscode": "^1.105.0"
40
+ }
41
+ }