@oh-my-ghaad/core 0.0.1
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.cjs +460 -0
- package/dist/index.d.cts +187 -0
- package/dist/index.d.ts +187 -0
- package/dist/index.js +430 -0
- package/package.json +23 -0
- package/src/adapter.ts +33 -0
- package/src/collection.ts +31 -0
- package/src/engine.ts +527 -0
- package/src/index.ts +5 -0
- package/src/types.ts +84 -0
- package/src/validators.ts +22 -0
- package/tsconfig.json +7 -0
package/src/engine.ts
ADDED
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
import { Collection } from "./collection";
|
|
4
|
+
import type { IAdapter, PrResponse, RepoStatus, Subscription } from "./types";
|
|
5
|
+
import { Adapter } from "./adapter";
|
|
6
|
+
import type { AppConfig, RepoConfig } from "./validators";
|
|
7
|
+
import { repoConfigSchema } from "./validators";
|
|
8
|
+
|
|
9
|
+
const storage = typeof window !== "undefined" ? window.localStorage : null;
|
|
10
|
+
|
|
11
|
+
export class Engine {
|
|
12
|
+
private status: RepoStatus = "unknown";
|
|
13
|
+
|
|
14
|
+
private subscriptions: Set<Subscription> = new Set();
|
|
15
|
+
|
|
16
|
+
private adapters: (Adapter & IAdapter)[] = [];
|
|
17
|
+
private currentAdapter: (Adapter & IAdapter) | null = null;
|
|
18
|
+
private collections: Collection[] = [];
|
|
19
|
+
|
|
20
|
+
private appConfig: AppConfig;
|
|
21
|
+
private repoConfig: RepoConfig;
|
|
22
|
+
|
|
23
|
+
private collectionItems: Record<string, z.infer<Collection["validator"]>[]> =
|
|
24
|
+
{};
|
|
25
|
+
|
|
26
|
+
constructor({
|
|
27
|
+
appConfig,
|
|
28
|
+
adapters,
|
|
29
|
+
collections,
|
|
30
|
+
}: {
|
|
31
|
+
adapters: (Adapter & IAdapter)[];
|
|
32
|
+
collections: Collection[];
|
|
33
|
+
appConfig: AppConfig;
|
|
34
|
+
}) {
|
|
35
|
+
this.appConfig = appConfig;
|
|
36
|
+
this.adapters = adapters;
|
|
37
|
+
this.collections = collections;
|
|
38
|
+
|
|
39
|
+
if (this.adapters.length === 1) {
|
|
40
|
+
this.setAdapter(this.adapters[0]);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (this.appConfig.persisted) {
|
|
44
|
+
const persistedAdapter = storage?.getItem(
|
|
45
|
+
this.appConfig.persistedAdapterKey || "adapter"
|
|
46
|
+
);
|
|
47
|
+
if (persistedAdapter) {
|
|
48
|
+
const adapter = this.adapters.find(
|
|
49
|
+
(adapter) => adapter.name === persistedAdapter
|
|
50
|
+
);
|
|
51
|
+
if (adapter) {
|
|
52
|
+
this.setAdapter(adapter);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const persistedToken = storage?.getItem(
|
|
57
|
+
this.appConfig.persistedTokenKey || "token"
|
|
58
|
+
);
|
|
59
|
+
if (persistedToken) {
|
|
60
|
+
this.setToken(persistedToken);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const persistedRepo = storage?.getItem(
|
|
64
|
+
this.appConfig.persistedRepoKey || "repo"
|
|
65
|
+
);
|
|
66
|
+
if (persistedRepo) {
|
|
67
|
+
this.getAdapter()?.setRepo(persistedRepo);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const persistedOwner = storage?.getItem(
|
|
71
|
+
this.appConfig.persistedOwnerKey || "owner"
|
|
72
|
+
);
|
|
73
|
+
if (persistedOwner) {
|
|
74
|
+
this.getAdapter()?.setOwner(persistedOwner);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
setUnauthorizedHandler(handler: () => void | Promise<void>) {
|
|
80
|
+
this.adapters.forEach((adapter) => {
|
|
81
|
+
adapter.setUnauthorizedHandler(handler);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
subscribe(subscription: Subscription) {
|
|
86
|
+
this.subscriptions.add(subscription);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
unsubscribe(subscription: Subscription) {
|
|
90
|
+
this.subscriptions.delete(subscription);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private notifySubscribers() {
|
|
94
|
+
this.subscriptions.forEach((subscription) =>
|
|
95
|
+
subscription(this.collections)
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
setRepoOwner(owner: string | null) {
|
|
100
|
+
if (this.appConfig.persisted) {
|
|
101
|
+
storage?.setItem(
|
|
102
|
+
this.appConfig.persistedOwnerKey || "owner",
|
|
103
|
+
owner || ""
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
this.adapters.forEach((adapter) => {
|
|
108
|
+
adapter.setOwner(owner);
|
|
109
|
+
});
|
|
110
|
+
this.notifySubscribers();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
setRepoName(name: string | null) {
|
|
114
|
+
if (this.appConfig.persisted) {
|
|
115
|
+
storage?.setItem(this.appConfig.persistedRepoKey || "repo", name || "");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
this.adapters.forEach((adapter) => {
|
|
119
|
+
adapter.setRepo(name);
|
|
120
|
+
});
|
|
121
|
+
this.notifySubscribers();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
getRepoStatus() {
|
|
125
|
+
return this.status;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
getAppConfig() {
|
|
129
|
+
return this.appConfig;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
getRepoConfig() {
|
|
133
|
+
return this.repoConfig;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async fetchRepoConfig(): Promise<RepoConfig> {
|
|
137
|
+
const rawConfigString = await this.currentAdapter
|
|
138
|
+
?.fetchFile("config.json")
|
|
139
|
+
.catch(() => {
|
|
140
|
+
console.log(
|
|
141
|
+
"OH My GHaaD: Could not fetch the config file, setting status to empty"
|
|
142
|
+
);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
if (!rawConfigString) {
|
|
146
|
+
this.status = "empty";
|
|
147
|
+
throw new Error("No config file found");
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const rawConfig = JSON.parse(rawConfigString);
|
|
151
|
+
const parsedConfig = repoConfigSchema.safeParse(rawConfig);
|
|
152
|
+
|
|
153
|
+
let partialConfig = parsedConfig.data as unknown as RepoConfig;
|
|
154
|
+
if (parsedConfig.success) {
|
|
155
|
+
this.status = "valid";
|
|
156
|
+
} else {
|
|
157
|
+
this.status = "invalid";
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
this.repoConfig = partialConfig;
|
|
161
|
+
|
|
162
|
+
return partialConfig;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async sync() {
|
|
166
|
+
const [repoConfig, ...fetchedCollectionItems] = await Promise.all([
|
|
167
|
+
this.fetchRepoConfig().catch((error) => {
|
|
168
|
+
return {
|
|
169
|
+
prBasedMutations: false,
|
|
170
|
+
};
|
|
171
|
+
}),
|
|
172
|
+
...this.collections.map(async (collection) => ({
|
|
173
|
+
collectionId: collection.id,
|
|
174
|
+
items: await this.fetchCollectionItems(collection.id).catch((error) => {
|
|
175
|
+
return [];
|
|
176
|
+
}),
|
|
177
|
+
})),
|
|
178
|
+
]);
|
|
179
|
+
|
|
180
|
+
this.notifySubscribers();
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
getToken() {
|
|
184
|
+
const currentAdapter = this.getAdapter();
|
|
185
|
+
if (currentAdapter) {
|
|
186
|
+
return currentAdapter.token;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
setToken(token: string | null) {
|
|
191
|
+
if (this.appConfig.persisted) {
|
|
192
|
+
storage?.setItem(
|
|
193
|
+
this.appConfig.persistedTokenKey || "token",
|
|
194
|
+
token || ""
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
this.adapters.forEach((adapter) => {
|
|
199
|
+
adapter.setToken(token);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
this.notifySubscribers();
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
getAdapters() {
|
|
206
|
+
return this.adapters;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
setAdapter(adapter: (Adapter & IAdapter) | null) {
|
|
210
|
+
if (this.appConfig.persisted) {
|
|
211
|
+
storage?.setItem(
|
|
212
|
+
this.appConfig.persistedAdapterKey || "adapter",
|
|
213
|
+
adapter?.name || ""
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
this.currentAdapter = adapter;
|
|
218
|
+
this.notifySubscribers();
|
|
219
|
+
return this;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
getAdapter() {
|
|
223
|
+
return this.currentAdapter;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
getCollections(): Collection[] {
|
|
227
|
+
return this.collections;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
getCollection<T extends Collection>(
|
|
231
|
+
collectionLookupValue:
|
|
232
|
+
| T["id"]
|
|
233
|
+
| T["names"]["singular"]
|
|
234
|
+
| T["names"]["plural"]
|
|
235
|
+
): Collection | undefined {
|
|
236
|
+
return this.collections.find(
|
|
237
|
+
(c) =>
|
|
238
|
+
c.id === collectionLookupValue ||
|
|
239
|
+
Object.values(c.names).includes(collectionLookupValue)
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
getCollectionItems<T extends Collection>(
|
|
244
|
+
collectionLookupValue:
|
|
245
|
+
| T["id"]
|
|
246
|
+
| T["names"]["singular"]
|
|
247
|
+
| T["names"]["plural"]
|
|
248
|
+
): z.infer<T["validator"]>[] {
|
|
249
|
+
const collection = this.getCollection(collectionLookupValue);
|
|
250
|
+
|
|
251
|
+
if (!collection) {
|
|
252
|
+
throw new Error("Collection not found");
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (!this.collectionItems[collection.id]) {
|
|
256
|
+
return [];
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return this.collectionItems[collection.id];
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
async fetchCollectionItems<T extends Collection>(
|
|
263
|
+
collectionLookupValue:
|
|
264
|
+
| T["id"]
|
|
265
|
+
| T["names"]["singular"]
|
|
266
|
+
| T["names"]["plural"],
|
|
267
|
+
force = false
|
|
268
|
+
): Promise<z.infer<T["validator"]>[]> {
|
|
269
|
+
if (!this.currentAdapter) {
|
|
270
|
+
throw new Error("No adapter selected");
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const collection = this.getCollection(collectionLookupValue);
|
|
274
|
+
|
|
275
|
+
if (!collection) {
|
|
276
|
+
throw new Error("Collection not found");
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const rawCollectionItems = await this.currentAdapter.fetchDirectory(
|
|
280
|
+
`collections/${collection.names.path}`
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
const collectionItems =
|
|
284
|
+
rawCollectionItems?.map((item) => {
|
|
285
|
+
return collection.validator.parse(JSON.parse(item)) as z.infer<
|
|
286
|
+
T["validator"]
|
|
287
|
+
>;
|
|
288
|
+
}) || [];
|
|
289
|
+
|
|
290
|
+
this.collectionItems[collection.id] = collectionItems;
|
|
291
|
+
|
|
292
|
+
this.notifySubscribers();
|
|
293
|
+
|
|
294
|
+
return collectionItems;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
async fetchCollectionItem<T extends Collection>(
|
|
298
|
+
collectionLookupValue:
|
|
299
|
+
| T["id"]
|
|
300
|
+
| T["names"]["singular"]
|
|
301
|
+
| T["names"]["plural"],
|
|
302
|
+
itemId: z.infer<T["validator"]>["id"]
|
|
303
|
+
): Promise<z.infer<T["validator"]> | null> {
|
|
304
|
+
const collection = this.getCollection(collectionLookupValue);
|
|
305
|
+
|
|
306
|
+
if (!collection) {
|
|
307
|
+
throw new Error("Collection not found");
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const rawCollectionItem = await this.currentAdapter.fetchFile(
|
|
311
|
+
`collections/${collection.names.path}/${itemId}.json`
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
if (!rawCollectionItem) {
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const collectionItem = collection.validator.parse(
|
|
319
|
+
JSON.parse(rawCollectionItem)
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
return collectionItem;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
async addToCollection<T extends Collection>(
|
|
326
|
+
collectionLookupValue:
|
|
327
|
+
| T["id"]
|
|
328
|
+
| T["names"]["singular"]
|
|
329
|
+
| T["names"]["plural"],
|
|
330
|
+
data: z.infer<T["validator"]>
|
|
331
|
+
): Promise<z.infer<T["validator"]>[]> {
|
|
332
|
+
if (!this.currentAdapter) {
|
|
333
|
+
throw new Error("No adapter selected");
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const collection = this.getCollection(collectionLookupValue);
|
|
337
|
+
|
|
338
|
+
if (!collection) {
|
|
339
|
+
throw new Error("Collection not found");
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const rawNewItem: z.infer<(typeof collection)["validator"]> = {
|
|
343
|
+
...data,
|
|
344
|
+
id: collection.idFunction(),
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
const item = collection.validator.parse(rawNewItem);
|
|
348
|
+
|
|
349
|
+
let request: ReturnType<
|
|
350
|
+
IAdapter["createCommit"] | IAdapter["createPullRequest"]
|
|
351
|
+
>;
|
|
352
|
+
|
|
353
|
+
request = this.currentAdapter.createFile(
|
|
354
|
+
`collections/${collection.names.path}/${item.id}.json`,
|
|
355
|
+
JSON.stringify(item, null, 2),
|
|
356
|
+
`Add ${collection.names.singular} ${item.id}`
|
|
357
|
+
);
|
|
358
|
+
|
|
359
|
+
return request.then(() => {
|
|
360
|
+
this.collectionItems[collection.id] = [
|
|
361
|
+
...(this.collectionItems[collection.id] || []),
|
|
362
|
+
item,
|
|
363
|
+
];
|
|
364
|
+
|
|
365
|
+
return this.collectionItems[collection.id];
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
async removeFromCollection<T extends Collection>(
|
|
370
|
+
collectionLookupValue:
|
|
371
|
+
| T["id"]
|
|
372
|
+
| T["names"]["singular"]
|
|
373
|
+
| T["names"]["plural"],
|
|
374
|
+
itemId: z.infer<T["validator"]>["id"]
|
|
375
|
+
): Promise<z.infer<T["validator"]>[]> {
|
|
376
|
+
if (!this.currentAdapter) {
|
|
377
|
+
throw new Error("No adapter selected");
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const collection = this.getCollection(collectionLookupValue);
|
|
381
|
+
|
|
382
|
+
if (!collection) {
|
|
383
|
+
throw new Error("Collection not found");
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
let request: ReturnType<
|
|
387
|
+
IAdapter["createCommit"] | IAdapter["createPullRequest"]
|
|
388
|
+
>;
|
|
389
|
+
|
|
390
|
+
request = this.currentAdapter.deleteFile(
|
|
391
|
+
`collections/${collection.names.path}/${itemId}.json`,
|
|
392
|
+
`Remove ${collection.names.singular} ${itemId}`
|
|
393
|
+
);
|
|
394
|
+
|
|
395
|
+
await request.then(() => {
|
|
396
|
+
this.collectionItems[collection.id] =
|
|
397
|
+
this.collectionItems[collection.id]?.filter(
|
|
398
|
+
(item) => item.id !== itemId
|
|
399
|
+
) || [];
|
|
400
|
+
return this.collectionItems[collection.id];
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
this.notifySubscribers();
|
|
404
|
+
|
|
405
|
+
return this.collectionItems[collection.id];
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
async updateInCollection<T extends Collection>(
|
|
409
|
+
collectionLookupValue:
|
|
410
|
+
| T["id"]
|
|
411
|
+
| T["names"]["singular"]
|
|
412
|
+
| T["names"]["plural"],
|
|
413
|
+
itemId: string,
|
|
414
|
+
data: z.infer<T["validator"]>
|
|
415
|
+
): Promise<z.infer<T["validator"]>[]> {
|
|
416
|
+
if (!this.currentAdapter) {
|
|
417
|
+
throw new Error("No adapter selected");
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const collection = this.getCollection(collectionLookupValue);
|
|
421
|
+
|
|
422
|
+
if (!collection) {
|
|
423
|
+
throw new Error("Collection not found");
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
const existingItem = this.collectionItems[collection.id].find(
|
|
427
|
+
(item) => item.id === itemId
|
|
428
|
+
);
|
|
429
|
+
|
|
430
|
+
if (!existingItem) {
|
|
431
|
+
throw new Error("Item not found");
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
const item = collection.validator.parse(data);
|
|
435
|
+
|
|
436
|
+
await this.currentAdapter.updateFile(
|
|
437
|
+
`collections/${collection.names.path}/${itemId}.json`,
|
|
438
|
+
JSON.stringify(
|
|
439
|
+
{
|
|
440
|
+
...item,
|
|
441
|
+
id: itemId,
|
|
442
|
+
},
|
|
443
|
+
null,
|
|
444
|
+
2
|
|
445
|
+
),
|
|
446
|
+
`Update ${collection.names.singular} ${itemId}`
|
|
447
|
+
);
|
|
448
|
+
|
|
449
|
+
this.collectionItems[collection.id] = this.collectionItems[
|
|
450
|
+
collection.id
|
|
451
|
+
].map((_item) => {
|
|
452
|
+
if (_item.id === itemId) {
|
|
453
|
+
return item;
|
|
454
|
+
}
|
|
455
|
+
return _item;
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
this.notifySubscribers();
|
|
459
|
+
|
|
460
|
+
return this.collectionItems[collection.id];
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
async initializeCollection(collection: Collection) {
|
|
464
|
+
const adapter = this.getAdapter();
|
|
465
|
+
|
|
466
|
+
if (!adapter) {
|
|
467
|
+
throw new Error("No adapter selected");
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
return adapter.createFile(
|
|
471
|
+
`collections/${collection.names.path}/.gitkeep`,
|
|
472
|
+
"",
|
|
473
|
+
`Create collection: ${collection.names.singular}`
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
async initializeRepoConfig(repoConfig: RepoConfig) {
|
|
478
|
+
const adapter = this.getAdapter();
|
|
479
|
+
|
|
480
|
+
if (!adapter) {
|
|
481
|
+
throw new Error("No adapter selected");
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
return adapter.createFile(
|
|
485
|
+
"config.json",
|
|
486
|
+
JSON.stringify(
|
|
487
|
+
repoConfig ?? {
|
|
488
|
+
prBasedMutations: false,
|
|
489
|
+
}
|
|
490
|
+
)
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
async initialize() {
|
|
495
|
+
const repoConfig = this.getRepoConfig();
|
|
496
|
+
|
|
497
|
+
await this.initializeRepoConfig(repoConfig);
|
|
498
|
+
|
|
499
|
+
for (const collection of this.collections) {
|
|
500
|
+
await this.initializeCollection(collection);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
this.status = "valid";
|
|
504
|
+
this.notifySubscribers();
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// Grouping together props and methods for caching PRs
|
|
508
|
+
private lastFetchPullRequestTimestamp = 0;
|
|
509
|
+
private cachedPullRequests: PrResponse[] = [];
|
|
510
|
+
async fetchPullRequests(force = false) {
|
|
511
|
+
const adapter = this.getAdapter();
|
|
512
|
+
|
|
513
|
+
if (!adapter) {
|
|
514
|
+
throw new Error("No adapter selected");
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
if (force) {
|
|
518
|
+
this.lastFetchPullRequestTimestamp = 0;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
if (this.lastFetchPullRequestTimestamp > Date.now() - 1000 * 60 * 5) {
|
|
522
|
+
return this.cachedPullRequests;
|
|
523
|
+
} else {
|
|
524
|
+
return adapter.fetchPullRequests();
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
package/src/index.ts
ADDED
package/src/types.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { Collection } from "./collection";
|
|
2
|
+
|
|
3
|
+
export type ValidRepoStatus = "valid" | "pending" | "empty";
|
|
4
|
+
export type InvalidRepoStatus = "invalid" | "unknown";
|
|
5
|
+
export type RepoStatus = ValidRepoStatus | InvalidRepoStatus;
|
|
6
|
+
|
|
7
|
+
export type CollectionStatus = "uninitialized" | "valid";
|
|
8
|
+
export type CollectionItemStatus = "pending" | "valid" | "invalid";
|
|
9
|
+
export type PendingCollectionItemType = "create" | "update" | "delete";
|
|
10
|
+
|
|
11
|
+
export interface AdapterProps {
|
|
12
|
+
clientId: string;
|
|
13
|
+
redirectUri: string;
|
|
14
|
+
accessManagementUrl: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type Subscription = (collections: Collection[]) => void | Promise<void>;
|
|
18
|
+
|
|
19
|
+
export interface RepositoryResponse {
|
|
20
|
+
id: string;
|
|
21
|
+
org: string;
|
|
22
|
+
name: string;
|
|
23
|
+
url: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface PrResponse {
|
|
27
|
+
id: string;
|
|
28
|
+
title: string;
|
|
29
|
+
description: string;
|
|
30
|
+
commitSha?: string;
|
|
31
|
+
createdAt: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export type PrRequest = Omit<PrResponse, "id" | "createdAt">;
|
|
35
|
+
|
|
36
|
+
export interface CommitResponse {
|
|
37
|
+
commitSha: string;
|
|
38
|
+
message: string;
|
|
39
|
+
commitDate: string;
|
|
40
|
+
authorName: string;
|
|
41
|
+
authorImageUrl: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export type CommitRequest = Pick<CommitResponse, "message">;
|
|
45
|
+
|
|
46
|
+
export interface IAdapter extends AdapterProps {
|
|
47
|
+
readonly name: string;
|
|
48
|
+
readonly icon: string;
|
|
49
|
+
readonly primaryColor: string;
|
|
50
|
+
readonly secondaryColor: string;
|
|
51
|
+
readonly oauthUrl: string;
|
|
52
|
+
readonly baseUrl: string;
|
|
53
|
+
|
|
54
|
+
fetchRepositories(): Promise<RepositoryResponse[]>;
|
|
55
|
+
|
|
56
|
+
fetchFile(filePath: string): Promise<string>;
|
|
57
|
+
fetchFileHistory(filePath: string): Promise<CommitResponse[]>;
|
|
58
|
+
fetchDirectory(directoryPath: string): Promise<string[]>;
|
|
59
|
+
|
|
60
|
+
createCommit(commitRequest: CommitRequest): Promise<void>;
|
|
61
|
+
// This method is ONLY used for initialization for now. We may end up using it for the non-PR workflow as well.
|
|
62
|
+
createFile(
|
|
63
|
+
filePath: string,
|
|
64
|
+
content: string,
|
|
65
|
+
message?: string
|
|
66
|
+
): Promise<void>;
|
|
67
|
+
updateFile(
|
|
68
|
+
filePath: string,
|
|
69
|
+
content: string,
|
|
70
|
+
message?: string
|
|
71
|
+
): Promise<void>;
|
|
72
|
+
deleteFile(filePath: string, message?: string): Promise<void>;
|
|
73
|
+
|
|
74
|
+
fetchPullRequests(): Promise<PrResponse[]>;
|
|
75
|
+
createPullRequest(prRequest: PrRequest): Promise<void>;
|
|
76
|
+
fetchPullRequest(): Promise<PrResponse>;
|
|
77
|
+
updatePullRequest(prRequest: PrRequest): Promise<void>;
|
|
78
|
+
deletePullRequest(id: string): Promise<void>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface BaseCollectionItem {
|
|
82
|
+
id: string;
|
|
83
|
+
fileName: string;
|
|
84
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { RepoStatus } from "./types";
|
|
3
|
+
|
|
4
|
+
export const appConfigSchema = z.object({
|
|
5
|
+
appName: z.string(),
|
|
6
|
+
appId: z.string(),
|
|
7
|
+
persisted: z.boolean().default(true),
|
|
8
|
+
persistedTokenKey: z.string().default("token"),
|
|
9
|
+
persistedRepoKey: z.string().default("repo"),
|
|
10
|
+
persistedOwnerKey: z.string().default("owner"),
|
|
11
|
+
persistedAdapterKey: z.string().default("adapter"),
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export type AppConfig = z.infer<typeof appConfigSchema>;
|
|
15
|
+
|
|
16
|
+
export const repoConfigSchema = z.object({
|
|
17
|
+
prBasedMutations: z.boolean().default(true),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export type RepoConfig = z.infer<typeof repoConfigSchema> & {
|
|
21
|
+
status: RepoStatus;
|
|
22
|
+
};
|