@adonisjs/content 1.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.md +9 -0
- package/README.md +290 -0
- package/build/chunk-LB6JFRVG.js +7 -0
- package/build/chunk-ZX3KSI7U.js +130 -0
- package/build/configure.d.ts +5 -0
- package/build/index.d.ts +2 -0
- package/build/index.js +16 -0
- package/build/providers/content_provider.d.ts +46 -0
- package/build/providers/content_provider.js +45 -0
- package/build/src/collection.d.ts +112 -0
- package/build/src/debug.d.ts +11 -0
- package/build/src/loaders/gh_contributors.d.ts +49 -0
- package/build/src/loaders/gh_releases.d.ts +52 -0
- package/build/src/loaders/gh_sponsors.d.ts +56 -0
- package/build/src/loaders/json.d.ts +43 -0
- package/build/src/loaders/main.d.ts +91 -0
- package/build/src/loaders/main.js +648 -0
- package/build/src/types.d.ts +280 -0
- package/build/src/utils.d.ts +82 -0
- package/package.json +146 -0
|
@@ -0,0 +1,648 @@
|
|
|
1
|
+
import {
|
|
2
|
+
debug_default
|
|
3
|
+
} from "../../chunk-LB6JFRVG.js";
|
|
4
|
+
|
|
5
|
+
// src/loaders/gh_sponsors.ts
|
|
6
|
+
import dayjs from "dayjs";
|
|
7
|
+
import vine from "@vinejs/vine";
|
|
8
|
+
import { dirname } from "path";
|
|
9
|
+
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
10
|
+
|
|
11
|
+
// src/utils.ts
|
|
12
|
+
import { Octokit } from "@octokit/rest";
|
|
13
|
+
import { graphql } from "@octokit/graphql";
|
|
14
|
+
async function fetchAllSponsors({
|
|
15
|
+
login,
|
|
16
|
+
isOrg,
|
|
17
|
+
ghToken
|
|
18
|
+
}) {
|
|
19
|
+
let hasNext = true;
|
|
20
|
+
let cursor = null;
|
|
21
|
+
const allSponsors = [];
|
|
22
|
+
const query = `
|
|
23
|
+
query($login: String!, $cursor: String) {
|
|
24
|
+
${isOrg ? `organization(login: $login)` : `user(login: $login)`} {
|
|
25
|
+
sponsorshipsAsMaintainer(first: 100, after: $cursor) {
|
|
26
|
+
nodes {
|
|
27
|
+
id
|
|
28
|
+
createdAt
|
|
29
|
+
privacyLevel
|
|
30
|
+
tier {
|
|
31
|
+
name
|
|
32
|
+
monthlyPriceInCents
|
|
33
|
+
}
|
|
34
|
+
sponsorEntity {
|
|
35
|
+
__typename
|
|
36
|
+
... on User {
|
|
37
|
+
login
|
|
38
|
+
name
|
|
39
|
+
avatarUrl
|
|
40
|
+
url
|
|
41
|
+
}
|
|
42
|
+
... on Organization {
|
|
43
|
+
login
|
|
44
|
+
name
|
|
45
|
+
avatarUrl
|
|
46
|
+
url
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
pageInfo {
|
|
51
|
+
hasNextPage
|
|
52
|
+
endCursor
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
`;
|
|
58
|
+
while (hasNext) {
|
|
59
|
+
const data = await graphql(query, {
|
|
60
|
+
headers: {
|
|
61
|
+
authorization: `token ${ghToken}`
|
|
62
|
+
},
|
|
63
|
+
login,
|
|
64
|
+
cursor
|
|
65
|
+
});
|
|
66
|
+
const root = isOrg ? data.organization : data.user;
|
|
67
|
+
if (!root) {
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
const conn = root.sponsorshipsAsMaintainer;
|
|
71
|
+
if (!conn || !conn.nodes) {
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
for (const node of conn.nodes) {
|
|
75
|
+
const sponsorEntity = node.sponsorEntity;
|
|
76
|
+
allSponsors.push({
|
|
77
|
+
id: node.id,
|
|
78
|
+
createdAt: node.createdAt,
|
|
79
|
+
privacyLevel: node.privacyLevel,
|
|
80
|
+
tierName: node.tier?.name ?? null,
|
|
81
|
+
tierMonthlyPriceInCents: node.tier?.monthlyPriceInCents ?? null,
|
|
82
|
+
sponsorType: sponsorEntity.__typename,
|
|
83
|
+
sponsorLogin: sponsorEntity.login,
|
|
84
|
+
sponsorName: sponsorEntity.name ?? null,
|
|
85
|
+
sponsorAvatarUrl: sponsorEntity.avatarUrl ?? null,
|
|
86
|
+
sponsorUrl: sponsorEntity.url ?? null
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
hasNext = conn.pageInfo.hasNextPage;
|
|
90
|
+
cursor = conn.pageInfo.endCursor;
|
|
91
|
+
}
|
|
92
|
+
return allSponsors;
|
|
93
|
+
}
|
|
94
|
+
async function fetchReleases({
|
|
95
|
+
org,
|
|
96
|
+
ghToken,
|
|
97
|
+
filters
|
|
98
|
+
}) {
|
|
99
|
+
let hasMoreRepos = true;
|
|
100
|
+
let orgCursor = null;
|
|
101
|
+
const allReleases = [];
|
|
102
|
+
while (hasMoreRepos) {
|
|
103
|
+
const query = `
|
|
104
|
+
query($cursor: String) {
|
|
105
|
+
organization(login: "${org}") {
|
|
106
|
+
repositories(
|
|
107
|
+
first: 10
|
|
108
|
+
after: $cursor
|
|
109
|
+
privacy: PUBLIC
|
|
110
|
+
isArchived: false
|
|
111
|
+
) {
|
|
112
|
+
nodes {
|
|
113
|
+
name
|
|
114
|
+
releases(first: 50, orderBy: {field: CREATED_AT, direction: DESC}) {
|
|
115
|
+
nodes {
|
|
116
|
+
name
|
|
117
|
+
tagName
|
|
118
|
+
publishedAt
|
|
119
|
+
url
|
|
120
|
+
description
|
|
121
|
+
}
|
|
122
|
+
pageInfo {
|
|
123
|
+
endCursor
|
|
124
|
+
hasNextPage
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
pageInfo {
|
|
129
|
+
endCursor
|
|
130
|
+
hasNextPage
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
`;
|
|
136
|
+
const data = await graphql(query, {
|
|
137
|
+
headers: {
|
|
138
|
+
authorization: `token ${ghToken}`
|
|
139
|
+
},
|
|
140
|
+
cursor: orgCursor
|
|
141
|
+
});
|
|
142
|
+
for (const repo of data.organization.repositories.nodes) {
|
|
143
|
+
const filtered = repo.releases.nodes.filter((r) => {
|
|
144
|
+
if (!filters) {
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
let pickRelease = true;
|
|
148
|
+
if (filters.nameDoesntInclude) {
|
|
149
|
+
pickRelease = !filters.nameDoesntInclude.some((substr) => r.name.includes(substr));
|
|
150
|
+
}
|
|
151
|
+
if (pickRelease && filters.nameIncludes) {
|
|
152
|
+
pickRelease = filters.nameIncludes.some((substr) => r.name.includes(substr));
|
|
153
|
+
}
|
|
154
|
+
return pickRelease;
|
|
155
|
+
}).map((r) => ({
|
|
156
|
+
repo: repo.name,
|
|
157
|
+
...r
|
|
158
|
+
}));
|
|
159
|
+
allReleases.push(...filtered);
|
|
160
|
+
}
|
|
161
|
+
hasMoreRepos = data.organization.repositories.pageInfo.hasNextPage;
|
|
162
|
+
orgCursor = data.organization.repositories.pageInfo.endCursor;
|
|
163
|
+
}
|
|
164
|
+
return allReleases;
|
|
165
|
+
}
|
|
166
|
+
async function fetchContributorsForOrg({
|
|
167
|
+
org,
|
|
168
|
+
ghToken
|
|
169
|
+
}) {
|
|
170
|
+
const REPO_PAGE_SIZE = 100;
|
|
171
|
+
const CONTRIB_PAGE_SIZE = 100;
|
|
172
|
+
const octokit = new Octokit({ auth: ghToken });
|
|
173
|
+
const repos = await octokit.paginate(octokit.repos.listForOrg, {
|
|
174
|
+
org,
|
|
175
|
+
type: "public",
|
|
176
|
+
per_page: REPO_PAGE_SIZE
|
|
177
|
+
});
|
|
178
|
+
const activeRepos = repos.filter((r) => !r.archived);
|
|
179
|
+
const result = [];
|
|
180
|
+
for (const repo of activeRepos) {
|
|
181
|
+
const repoName = repo.name;
|
|
182
|
+
try {
|
|
183
|
+
const contributors = await octokit.paginate(
|
|
184
|
+
octokit.repos.listContributors,
|
|
185
|
+
{
|
|
186
|
+
owner: org,
|
|
187
|
+
repo: repoName,
|
|
188
|
+
per_page: CONTRIB_PAGE_SIZE
|
|
189
|
+
},
|
|
190
|
+
(response) => response.data
|
|
191
|
+
);
|
|
192
|
+
contributors.forEach((c) => {
|
|
193
|
+
if (c.login) {
|
|
194
|
+
result.push({
|
|
195
|
+
login: c.login,
|
|
196
|
+
id: c.id,
|
|
197
|
+
avatar_url: c.avatar_url ?? null,
|
|
198
|
+
html_url: c.html_url ?? null,
|
|
199
|
+
contributions: c.contributions ?? 0
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
} catch (err) {
|
|
204
|
+
console.warn(
|
|
205
|
+
`Warning: failed to fetch contributors for ${org}/${repoName}: ${err?.message ?? err}`
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return result;
|
|
210
|
+
}
|
|
211
|
+
function mergeArrays(existing, fresh, key) {
|
|
212
|
+
const seen = /* @__PURE__ */ new Set();
|
|
213
|
+
const deduped = [...existing];
|
|
214
|
+
for (const r of fresh) {
|
|
215
|
+
if (!seen.has(r[key])) {
|
|
216
|
+
seen.add(r[key]);
|
|
217
|
+
deduped.push(r);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return deduped;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// src/loaders/gh_sponsors.ts
|
|
224
|
+
var GithubSponsorsLoader = class {
|
|
225
|
+
/** Configuration options for the GitHub sponsors loader */
|
|
226
|
+
#options;
|
|
227
|
+
/**
|
|
228
|
+
* Creates a new GitHub sponsors loader instance.
|
|
229
|
+
*
|
|
230
|
+
* @param options - Configuration options for loading GitHub sponsors
|
|
231
|
+
* @param options.login - GitHub username or organization name
|
|
232
|
+
* @param options.isOrg - Whether the login is an organization (true) or user (false)
|
|
233
|
+
* @param options.ghToken - GitHub personal access token for authentication
|
|
234
|
+
* @param options.outputPath - Path where cached sponsors will be stored
|
|
235
|
+
* @param options.refresh - Refresh schedule: 'daily', 'weekly', or 'monthly'
|
|
236
|
+
*
|
|
237
|
+
* @example
|
|
238
|
+
* ```ts
|
|
239
|
+
* const loader = new GithubSponsorsLoader({
|
|
240
|
+
* login: 'thetutlage',
|
|
241
|
+
* isOrg: false,
|
|
242
|
+
* ghToken: process.env.GITHUB_TOKEN,
|
|
243
|
+
* outputPath: './cache/sponsors.json',
|
|
244
|
+
* refresh: 'weekly'
|
|
245
|
+
* })
|
|
246
|
+
* ```
|
|
247
|
+
*/
|
|
248
|
+
constructor(options) {
|
|
249
|
+
this.#options = options;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Loads previously cached sponsors from the output file.
|
|
253
|
+
* Returns an object containing lastFetched timestamp and sponsors array,
|
|
254
|
+
* or null if the file doesn't exist.
|
|
255
|
+
*
|
|
256
|
+
* @internal
|
|
257
|
+
*/
|
|
258
|
+
async #loadExistingSponsors() {
|
|
259
|
+
try {
|
|
260
|
+
debug_default('loading existing sponsors file "%s"', this.#options.outputPath);
|
|
261
|
+
return JSON.parse(await readFile(this.#options.outputPath, "utf-8"));
|
|
262
|
+
} catch (error) {
|
|
263
|
+
if (error.code !== "ENOENT") {
|
|
264
|
+
throw error;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Writes sponsors to the cache file with the current timestamp.
|
|
271
|
+
* Creates the output directory if it doesn't exist.
|
|
272
|
+
*
|
|
273
|
+
* @param sponsors - Array of GitHub sponsors to cache
|
|
274
|
+
* @internal
|
|
275
|
+
*/
|
|
276
|
+
async #cacheSponsors(sponsors) {
|
|
277
|
+
debug_default('caching sponsors "%s"', this.#options.outputPath);
|
|
278
|
+
const fileContents = { lastFetched: (/* @__PURE__ */ new Date()).toISOString(), sponsors };
|
|
279
|
+
await mkdir(dirname(this.#options.outputPath), { recursive: true });
|
|
280
|
+
await writeFile(this.#options.outputPath, JSON.stringify(fileContents));
|
|
281
|
+
return fileContents;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Determines if the cached data has expired based on the refresh schedule.
|
|
285
|
+
*
|
|
286
|
+
* @param fetchDate - The date when data was last fetched
|
|
287
|
+
* @internal
|
|
288
|
+
*/
|
|
289
|
+
#isExpired(fetchDate) {
|
|
290
|
+
switch (this.#options.refresh) {
|
|
291
|
+
case "daily":
|
|
292
|
+
return dayjs().isAfter(fetchDate, "day");
|
|
293
|
+
case "weekly":
|
|
294
|
+
return dayjs().isAfter(fetchDate, "week");
|
|
295
|
+
case "monthly":
|
|
296
|
+
return dayjs().isAfter(fetchDate, "month");
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Loads and validates GitHub sponsors data.
|
|
301
|
+
* Uses cached data if available and not expired, otherwise fetches fresh data
|
|
302
|
+
* from GitHub API and updates the cache.
|
|
303
|
+
*
|
|
304
|
+
* @param schema - VineJS schema to validate the sponsors data against
|
|
305
|
+
* @param metadata - Optional metadata to pass to the validator
|
|
306
|
+
*
|
|
307
|
+
* @example
|
|
308
|
+
* ```ts
|
|
309
|
+
* const sponsors = await loader.load(sponsorsSchema)
|
|
310
|
+
* ```
|
|
311
|
+
*/
|
|
312
|
+
async load(schema, metadata) {
|
|
313
|
+
let existingSponsors = await this.#loadExistingSponsors();
|
|
314
|
+
if (!existingSponsors || this.#isExpired(new Date(existingSponsors.lastFetched))) {
|
|
315
|
+
debug_default('fetching sponsors from github "%s"', this.#options.login);
|
|
316
|
+
const sponsors = await fetchAllSponsors(this.#options);
|
|
317
|
+
existingSponsors = await this.#cacheSponsors(sponsors);
|
|
318
|
+
}
|
|
319
|
+
return vine.validate({
|
|
320
|
+
schema,
|
|
321
|
+
data: existingSponsors.sponsors,
|
|
322
|
+
meta: metadata
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
// src/loaders/gh_releases.ts
|
|
328
|
+
import dayjs2 from "dayjs";
|
|
329
|
+
import vine2 from "@vinejs/vine";
|
|
330
|
+
import { dirname as dirname2 } from "path";
|
|
331
|
+
import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
332
|
+
var GithubReleasesLoader = class {
|
|
333
|
+
/** Configuration options for the GitHub release loader */
|
|
334
|
+
#options;
|
|
335
|
+
/**
|
|
336
|
+
* Creates a new GitHub release loader instance.
|
|
337
|
+
*
|
|
338
|
+
* @param options - Configuration options for loading GitHub releases
|
|
339
|
+
*
|
|
340
|
+
* @example
|
|
341
|
+
* ```ts
|
|
342
|
+
* const loader = new GithubReleasesLoader({
|
|
343
|
+
* org: 'adonisjs',
|
|
344
|
+
* ghToken: process.env.GITHUB_TOKEN,
|
|
345
|
+
* outputPath: './cache/releases.json',
|
|
346
|
+
* refresh: 'weekly'
|
|
347
|
+
* })
|
|
348
|
+
* ```
|
|
349
|
+
*/
|
|
350
|
+
constructor(options) {
|
|
351
|
+
this.#options = options;
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Loads previously cached releases from the output file.
|
|
355
|
+
* Returns an object containing lastFetched timestamp and releases array,
|
|
356
|
+
* or null if the file doesn't exist.
|
|
357
|
+
*
|
|
358
|
+
* @internal
|
|
359
|
+
*/
|
|
360
|
+
async #loadExistingReleases() {
|
|
361
|
+
try {
|
|
362
|
+
debug_default('loading existing releases file "%s"', this.#options.outputPath);
|
|
363
|
+
return JSON.parse(await readFile2(this.#options.outputPath, "utf-8"));
|
|
364
|
+
} catch (error) {
|
|
365
|
+
if (error.code !== "ENOENT") {
|
|
366
|
+
throw error;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
return null;
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Writes releases to the cache file with the current timestamp.
|
|
373
|
+
* Creates the output directory if it doesn't exist.
|
|
374
|
+
*
|
|
375
|
+
* @param releases - Array of GitHub releases to cache
|
|
376
|
+
* @internal
|
|
377
|
+
*/
|
|
378
|
+
async #cacheReleases(releases) {
|
|
379
|
+
debug_default('caching releasing "%s"', this.#options.outputPath);
|
|
380
|
+
const fileContents = { lastFetched: (/* @__PURE__ */ new Date()).toISOString(), releases };
|
|
381
|
+
await mkdir2(dirname2(this.#options.outputPath), { recursive: true });
|
|
382
|
+
await writeFile2(this.#options.outputPath, JSON.stringify(fileContents));
|
|
383
|
+
return fileContents;
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Determines if the cached data has expired based on the refresh schedule.
|
|
387
|
+
*
|
|
388
|
+
* @param fetchDate - The date when data was last fetched
|
|
389
|
+
* @internal
|
|
390
|
+
*/
|
|
391
|
+
#isExpired(fetchDate) {
|
|
392
|
+
switch (this.#options.refresh) {
|
|
393
|
+
case "daily":
|
|
394
|
+
return dayjs2().isAfter(fetchDate, "day");
|
|
395
|
+
case "weekly":
|
|
396
|
+
return dayjs2().isAfter(fetchDate, "week");
|
|
397
|
+
case "monthly":
|
|
398
|
+
return dayjs2().isAfter(fetchDate, "month");
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Loads and validates GitHub releases data.
|
|
403
|
+
* Uses cached data if available and not expired, otherwise fetches fresh data
|
|
404
|
+
* from GitHub API and merges with existing releases.
|
|
405
|
+
*
|
|
406
|
+
* @param schema - VineJS schema to validate the releases data against
|
|
407
|
+
* @param metadata - Optional metadata to pass to the validator
|
|
408
|
+
*
|
|
409
|
+
* @example
|
|
410
|
+
* ```ts
|
|
411
|
+
* const releases = await loader.load(releasesSchema)
|
|
412
|
+
* ```
|
|
413
|
+
*/
|
|
414
|
+
async load(schema, metadata) {
|
|
415
|
+
let existingReleases = await this.#loadExistingReleases();
|
|
416
|
+
if (!existingReleases || this.#isExpired(new Date(existingReleases.lastFetched))) {
|
|
417
|
+
debug_default('fetching releases from github "%s"', this.#options.org);
|
|
418
|
+
const releases = await fetchReleases(this.#options);
|
|
419
|
+
const mergedReleases = existingReleases ? mergeArrays(existingReleases.releases, releases, "url") : releases;
|
|
420
|
+
existingReleases = await this.#cacheReleases(mergedReleases);
|
|
421
|
+
}
|
|
422
|
+
return vine2.validate({
|
|
423
|
+
schema,
|
|
424
|
+
data: existingReleases.releases,
|
|
425
|
+
meta: metadata
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
// src/loaders/gh_contributors.ts
|
|
431
|
+
import dayjs3 from "dayjs";
|
|
432
|
+
import vine3 from "@vinejs/vine";
|
|
433
|
+
import { dirname as dirname3 } from "path";
|
|
434
|
+
import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
|
|
435
|
+
var GithubContributorsLoader = class {
|
|
436
|
+
/** Configuration options for the GitHub contributors loader */
|
|
437
|
+
#options;
|
|
438
|
+
/**
|
|
439
|
+
* Creates a new GitHub contributors loader instance.
|
|
440
|
+
*
|
|
441
|
+
* @param options - Configuration options for loading GitHub contributors
|
|
442
|
+
*
|
|
443
|
+
* @example
|
|
444
|
+
* ```ts
|
|
445
|
+
* const loader = new GithubContributorsLoader({
|
|
446
|
+
* org: 'adonisjs',
|
|
447
|
+
* ghToken: process.env.GITHUB_TOKEN,
|
|
448
|
+
* outputPath: './cache/contributors.json',
|
|
449
|
+
* refresh: 'weekly'
|
|
450
|
+
* })
|
|
451
|
+
* ```
|
|
452
|
+
*/
|
|
453
|
+
constructor(options) {
|
|
454
|
+
this.#options = options;
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Loads previously cached contributors from the output file.
|
|
458
|
+
* Returns an object containing lastFetched timestamp and contributors array,
|
|
459
|
+
* or null if the file doesn't exist.
|
|
460
|
+
*
|
|
461
|
+
* @internal
|
|
462
|
+
*/
|
|
463
|
+
async #loadExistingContributors() {
|
|
464
|
+
try {
|
|
465
|
+
debug_default('loading existing contributors file "%s"', this.#options.outputPath);
|
|
466
|
+
return JSON.parse(await readFile3(this.#options.outputPath, "utf-8"));
|
|
467
|
+
} catch (error) {
|
|
468
|
+
if (error.code !== "ENOENT") {
|
|
469
|
+
throw error;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
return null;
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* Writes contributors to the cache file with the current timestamp.
|
|
476
|
+
* Creates the output directory if it doesn't exist.
|
|
477
|
+
*
|
|
478
|
+
* @param contributors - Array of GitHub contributors to cache
|
|
479
|
+
* @internal
|
|
480
|
+
*/
|
|
481
|
+
async #cacheContributors(contributors) {
|
|
482
|
+
debug_default('caching contributors "%s"', this.#options.outputPath);
|
|
483
|
+
const fileContents = { lastFetched: (/* @__PURE__ */ new Date()).toISOString(), contributors };
|
|
484
|
+
await mkdir3(dirname3(this.#options.outputPath), { recursive: true });
|
|
485
|
+
await writeFile3(this.#options.outputPath, JSON.stringify(fileContents));
|
|
486
|
+
return fileContents;
|
|
487
|
+
}
|
|
488
|
+
/**
|
|
489
|
+
* Determines if the cached data has expired based on the refresh schedule.
|
|
490
|
+
*
|
|
491
|
+
* @param fetchDate - The date when data was last fetched
|
|
492
|
+
* @internal
|
|
493
|
+
*/
|
|
494
|
+
#isExpired(fetchDate) {
|
|
495
|
+
switch (this.#options.refresh) {
|
|
496
|
+
case "daily":
|
|
497
|
+
return dayjs3().isAfter(fetchDate, "day");
|
|
498
|
+
case "weekly":
|
|
499
|
+
return dayjs3().isAfter(fetchDate, "week");
|
|
500
|
+
case "monthly":
|
|
501
|
+
return dayjs3().isAfter(fetchDate, "month");
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* Loads and validates GitHub contributors data.
|
|
506
|
+
* Uses cached data if available and not expired, otherwise fetches fresh data
|
|
507
|
+
* from GitHub API and updates the cache.
|
|
508
|
+
*
|
|
509
|
+
* @param schema - VineJS schema to validate the contributors data against
|
|
510
|
+
* @param metadata - Optional metadata to pass to the validator
|
|
511
|
+
*
|
|
512
|
+
* @example
|
|
513
|
+
* ```ts
|
|
514
|
+
* const contributors = await loader.load(contributorsSchema)
|
|
515
|
+
* ```
|
|
516
|
+
*/
|
|
517
|
+
async load(schema, metadata) {
|
|
518
|
+
let existingContributors = await this.#loadExistingContributors();
|
|
519
|
+
if (!existingContributors || this.#isExpired(new Date(existingContributors.lastFetched))) {
|
|
520
|
+
debug_default('fetching contributors from github "%s"', this.#options.org);
|
|
521
|
+
const contributors = await fetchContributorsForOrg(this.#options);
|
|
522
|
+
existingContributors = await this.#cacheContributors(contributors);
|
|
523
|
+
}
|
|
524
|
+
return vine3.validate({
|
|
525
|
+
schema,
|
|
526
|
+
data: existingContributors.contributors,
|
|
527
|
+
meta: metadata
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
};
|
|
531
|
+
|
|
532
|
+
// src/loaders/json.ts
|
|
533
|
+
import vine4 from "@vinejs/vine";
|
|
534
|
+
import { dirname as dirname4 } from "path";
|
|
535
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
536
|
+
var JsonLoader = class {
|
|
537
|
+
/** Path to the JSON file to load */
|
|
538
|
+
#source;
|
|
539
|
+
/**
|
|
540
|
+
* Creates a new JSON loader instance.
|
|
541
|
+
*
|
|
542
|
+
* @param source - Absolute or relative path to the JSON file
|
|
543
|
+
*
|
|
544
|
+
* @example
|
|
545
|
+
* ```ts
|
|
546
|
+
* const loader = new JsonLoader('./data/menu.json')
|
|
547
|
+
* ```
|
|
548
|
+
*/
|
|
549
|
+
constructor(source) {
|
|
550
|
+
this.#source = source;
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* Loads and validates JSON data from the configured file.
|
|
554
|
+
* The directory of the source file is provided as metadata during validation.
|
|
555
|
+
*
|
|
556
|
+
* @param schema - VineJS schema to validate the loaded JSON data against
|
|
557
|
+
* @param metadata - Optional metadata to pass to the validator
|
|
558
|
+
*
|
|
559
|
+
* @example
|
|
560
|
+
* ```ts
|
|
561
|
+
* const data = await loader.load(menuSchema)
|
|
562
|
+
* ```
|
|
563
|
+
*/
|
|
564
|
+
async load(schema, metadata) {
|
|
565
|
+
const menuFileRoot = dirname4(this.#source);
|
|
566
|
+
const menu = JSON.parse(await readFile4(this.#source, "utf-8"));
|
|
567
|
+
debug_default('loading file "%s"', this.#source);
|
|
568
|
+
return vine4.validate({ schema, data: menu, meta: { menuFileRoot, ...metadata } });
|
|
569
|
+
}
|
|
570
|
+
};
|
|
571
|
+
|
|
572
|
+
// src/loaders/main.ts
|
|
573
|
+
var loaders = {
|
|
574
|
+
/**
|
|
575
|
+
* Creates a GitHub sponsors loader instance.
|
|
576
|
+
*
|
|
577
|
+
* @param options - Configuration options for the sponsors loader
|
|
578
|
+
*
|
|
579
|
+
* @example
|
|
580
|
+
* ```ts
|
|
581
|
+
* const loader = loaders.ghSponsors({
|
|
582
|
+
* login: 'adonisjs',
|
|
583
|
+
* isOrg: true,
|
|
584
|
+
* ghToken: process.env.GITHUB_TOKEN,
|
|
585
|
+
* outputPath: './cache/sponsors.json',
|
|
586
|
+
* refresh: 'daily'
|
|
587
|
+
* })
|
|
588
|
+
* ```
|
|
589
|
+
*/
|
|
590
|
+
ghSponsors(options) {
|
|
591
|
+
return new GithubSponsorsLoader(options);
|
|
592
|
+
},
|
|
593
|
+
/**
|
|
594
|
+
* Creates a GitHub contributors loader instance.
|
|
595
|
+
*
|
|
596
|
+
* @param options - Configuration options for the contributors loader
|
|
597
|
+
*
|
|
598
|
+
* @example
|
|
599
|
+
* ```ts
|
|
600
|
+
* const loader = loaders.ghContributors({
|
|
601
|
+
* org: 'adonisjs',
|
|
602
|
+
* ghToken: process.env.GITHUB_TOKEN,
|
|
603
|
+
* outputPath: './cache/contributors.json',
|
|
604
|
+
* refresh: 'weekly'
|
|
605
|
+
* })
|
|
606
|
+
* ```
|
|
607
|
+
*/
|
|
608
|
+
ghContributors(options) {
|
|
609
|
+
return new GithubContributorsLoader(options);
|
|
610
|
+
},
|
|
611
|
+
/**
|
|
612
|
+
* Creates a GitHub releases loader instance.
|
|
613
|
+
*
|
|
614
|
+
* @param options - Configuration options for the releases loader
|
|
615
|
+
*
|
|
616
|
+
* @example
|
|
617
|
+
* ```ts
|
|
618
|
+
* const loader = loaders.ghReleases({
|
|
619
|
+
* org: 'adonisjs',
|
|
620
|
+
* ghToken: process.env.GITHUB_TOKEN,
|
|
621
|
+
* outputPath: './cache/releases.json',
|
|
622
|
+
* refresh: 'daily',
|
|
623
|
+
* filters: {
|
|
624
|
+
* nameDoesntInclude: ['alpha', 'beta']
|
|
625
|
+
* }
|
|
626
|
+
* })
|
|
627
|
+
* ```
|
|
628
|
+
*/
|
|
629
|
+
ghReleases(options) {
|
|
630
|
+
return new GithubReleasesLoader(options);
|
|
631
|
+
},
|
|
632
|
+
/**
|
|
633
|
+
* Creates a JSON file loader instance.
|
|
634
|
+
*
|
|
635
|
+
* @param source - Path to the JSON file to load
|
|
636
|
+
*
|
|
637
|
+
* @example
|
|
638
|
+
* ```ts
|
|
639
|
+
* const loader = loaders.jsonLoader('./data/menu.json')
|
|
640
|
+
* ```
|
|
641
|
+
*/
|
|
642
|
+
jsonLoader(source) {
|
|
643
|
+
return new JsonLoader(source);
|
|
644
|
+
}
|
|
645
|
+
};
|
|
646
|
+
export {
|
|
647
|
+
loaders
|
|
648
|
+
};
|