@churchapps/content-providers 0.6.0 → 0.7.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.cts CHANGED
@@ -16,6 +16,8 @@ interface ContentProviderConfig {
16
16
  apiBase: string;
17
17
  oauthBase: string;
18
18
  clientId: string;
19
+ /** For confidential OAuth clients (e.g. WP OAuth Server); sent on token exchange and refresh. */
20
+ clientSecret?: string;
19
21
  scopes: string[];
20
22
  supportsDeviceFlow?: boolean;
21
23
  deviceAuthEndpoint?: string;
@@ -269,6 +271,8 @@ declare class DeviceFlowHelper {
269
271
  calculatePollDelay(baseInterval?: number, slowDownCount?: number): number;
270
272
  }
271
273
 
274
+ declare const setProviderSecret: (providerId: string, secret: string) => void;
275
+
272
276
  /** Base for providers; auth-flow methods are intentionally not here so consumers can feature-detect with `"method" in provider`. */
273
277
  declare abstract class BaseProvider implements IProvider {
274
278
  abstract readonly id: string;
@@ -393,7 +397,7 @@ declare class B1ChurchProvider extends BaseProvider {
393
397
  private fetchPlanImages;
394
398
  private planTypeId;
395
399
  setPairingData(data: unknown): void;
396
- getCurrentPlan(_authData?: ContentProviderAuthData | null): Promise<CurrentPlan | null>;
400
+ getCurrentPlan(authData?: ContentProviderAuthData | null): Promise<CurrentPlan | null>;
397
401
  getInstructions(path: string, authData?: ContentProviderAuthData | null): Promise<Instructions | null>;
398
402
  private processInstructionItems;
399
403
  private findItemByPath;
@@ -573,4 +577,4 @@ declare function getAvailableProviders(ids?: string[]): ProviderInfo[];
573
577
  */
574
578
  declare const VERSION: string;
575
579
 
576
- export { APlayProvider, type AuthType, B1ChurchProvider, BaseProvider, BibleProjectProvider, CbnProvider, type ContentFile, type ContentFolder, type ContentItem, type ContentProviderAuthData, type ContentProviderConfig, type CurrentPlan, type DeviceAuthorizationResponse, DeviceFlowHelper, type DeviceFlowPollResult, type DeviceFlowState, DropboxProvider, type EndpointValue, type EndpointsConfig, type FeedActionInterface, type FeedFileInterface, type FeedSectionInterface, type FeedVenueInterface, HighVoltageKidsProvider, type IProvider, type InstructionItem, type Instructions, JesusFilmProvider, LessonsChurchProvider, LifeChurchProvider, type MediaLicenseResult, type MediaLicenseStatus, type MessageFileInterface, OAuthHelper, type Plan, type PlanPresentation, type PlanSection, type PlanTimelineItem, PlanningCenterProvider, type ProviderCapabilities, type ProviderInfo, type ProviderLogos, SignPresenterProvider, TokenHelper, VERSION, type VenueActionInterface, type VenueActionsResponseInterface, type VenueSectionActionsInterface, getAllProviders, getAvailableProviders, getProvider, getProviderConfig, isContentFile, isContentFolder, navigateToPath, registerProvider, toAuthData };
580
+ export { APlayProvider, type AuthType, B1ChurchProvider, BaseProvider, BibleProjectProvider, CbnProvider, type ContentFile, type ContentFolder, type ContentItem, type ContentProviderAuthData, type ContentProviderConfig, type CurrentPlan, type DeviceAuthorizationResponse, DeviceFlowHelper, type DeviceFlowPollResult, type DeviceFlowState, DropboxProvider, type EndpointValue, type EndpointsConfig, type FeedActionInterface, type FeedFileInterface, type FeedSectionInterface, type FeedVenueInterface, HighVoltageKidsProvider, type IProvider, type InstructionItem, type Instructions, JesusFilmProvider, LessonsChurchProvider, LifeChurchProvider, type MediaLicenseResult, type MediaLicenseStatus, type MessageFileInterface, OAuthHelper, type Plan, type PlanPresentation, type PlanSection, type PlanTimelineItem, PlanningCenterProvider, type ProviderCapabilities, type ProviderInfo, type ProviderLogos, SignPresenterProvider, TokenHelper, VERSION, type VenueActionInterface, type VenueActionsResponseInterface, type VenueSectionActionsInterface, getAllProviders, getAvailableProviders, getProvider, getProviderConfig, isContentFile, isContentFolder, navigateToPath, registerProvider, setProviderSecret, toAuthData };
package/dist/index.d.ts CHANGED
@@ -16,6 +16,8 @@ interface ContentProviderConfig {
16
16
  apiBase: string;
17
17
  oauthBase: string;
18
18
  clientId: string;
19
+ /** For confidential OAuth clients (e.g. WP OAuth Server); sent on token exchange and refresh. */
20
+ clientSecret?: string;
19
21
  scopes: string[];
20
22
  supportsDeviceFlow?: boolean;
21
23
  deviceAuthEndpoint?: string;
@@ -269,6 +271,8 @@ declare class DeviceFlowHelper {
269
271
  calculatePollDelay(baseInterval?: number, slowDownCount?: number): number;
270
272
  }
271
273
 
274
+ declare const setProviderSecret: (providerId: string, secret: string) => void;
275
+
272
276
  /** Base for providers; auth-flow methods are intentionally not here so consumers can feature-detect with `"method" in provider`. */
273
277
  declare abstract class BaseProvider implements IProvider {
274
278
  abstract readonly id: string;
@@ -393,7 +397,7 @@ declare class B1ChurchProvider extends BaseProvider {
393
397
  private fetchPlanImages;
394
398
  private planTypeId;
395
399
  setPairingData(data: unknown): void;
396
- getCurrentPlan(_authData?: ContentProviderAuthData | null): Promise<CurrentPlan | null>;
400
+ getCurrentPlan(authData?: ContentProviderAuthData | null): Promise<CurrentPlan | null>;
397
401
  getInstructions(path: string, authData?: ContentProviderAuthData | null): Promise<Instructions | null>;
398
402
  private processInstructionItems;
399
403
  private findItemByPath;
@@ -573,4 +577,4 @@ declare function getAvailableProviders(ids?: string[]): ProviderInfo[];
573
577
  */
574
578
  declare const VERSION: string;
575
579
 
576
- export { APlayProvider, type AuthType, B1ChurchProvider, BaseProvider, BibleProjectProvider, CbnProvider, type ContentFile, type ContentFolder, type ContentItem, type ContentProviderAuthData, type ContentProviderConfig, type CurrentPlan, type DeviceAuthorizationResponse, DeviceFlowHelper, type DeviceFlowPollResult, type DeviceFlowState, DropboxProvider, type EndpointValue, type EndpointsConfig, type FeedActionInterface, type FeedFileInterface, type FeedSectionInterface, type FeedVenueInterface, HighVoltageKidsProvider, type IProvider, type InstructionItem, type Instructions, JesusFilmProvider, LessonsChurchProvider, LifeChurchProvider, type MediaLicenseResult, type MediaLicenseStatus, type MessageFileInterface, OAuthHelper, type Plan, type PlanPresentation, type PlanSection, type PlanTimelineItem, PlanningCenterProvider, type ProviderCapabilities, type ProviderInfo, type ProviderLogos, SignPresenterProvider, TokenHelper, VERSION, type VenueActionInterface, type VenueActionsResponseInterface, type VenueSectionActionsInterface, getAllProviders, getAvailableProviders, getProvider, getProviderConfig, isContentFile, isContentFolder, navigateToPath, registerProvider, toAuthData };
580
+ export { APlayProvider, type AuthType, B1ChurchProvider, BaseProvider, BibleProjectProvider, CbnProvider, type ContentFile, type ContentFolder, type ContentItem, type ContentProviderAuthData, type ContentProviderConfig, type CurrentPlan, type DeviceAuthorizationResponse, DeviceFlowHelper, type DeviceFlowPollResult, type DeviceFlowState, DropboxProvider, type EndpointValue, type EndpointsConfig, type FeedActionInterface, type FeedFileInterface, type FeedSectionInterface, type FeedVenueInterface, HighVoltageKidsProvider, type IProvider, type InstructionItem, type Instructions, JesusFilmProvider, LessonsChurchProvider, LifeChurchProvider, type MediaLicenseResult, type MediaLicenseStatus, type MessageFileInterface, OAuthHelper, type Plan, type PlanPresentation, type PlanSection, type PlanTimelineItem, PlanningCenterProvider, type ProviderCapabilities, type ProviderInfo, type ProviderLogos, SignPresenterProvider, TokenHelper, VERSION, type VenueActionInterface, type VenueActionsResponseInterface, type VenueSectionActionsInterface, getAllProviders, getAvailableProviders, getProvider, getProviderConfig, isContentFile, isContentFolder, navigateToPath, registerProvider, setProviderSecret, toAuthData };
package/dist/index.js CHANGED
@@ -47,6 +47,7 @@ var TokenHelper = class {
47
47
  refresh_token: auth.refresh_token,
48
48
  client_id: config.clientId
49
49
  });
50
+ if (config.clientSecret) params.set("client_secret", config.clientSecret);
50
51
  const response = await fetch(`${config.oauthBase}/token`, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: params.toString() });
51
52
  if (!response.ok) {
52
53
  console.warn(`[TokenHelper] Token refresh failed: ${response.status} ${response.statusText}`);
@@ -116,6 +117,7 @@ var OAuthHelper = class {
116
117
  client_id: config.clientId,
117
118
  code_verifier: codeVerifier
118
119
  });
120
+ if (config.clientSecret) params.set("client_secret", config.clientSecret);
119
121
  const tokenUrl = `${config.oauthBase}/token`;
120
122
  const response = await fetch(tokenUrl, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: params.toString() });
121
123
  if (!response.ok) {
@@ -199,6 +201,13 @@ var ApiHelper = class {
199
201
  }
200
202
  };
201
203
 
204
+ // src/helpers/ProviderSecrets.ts
205
+ var secrets = {};
206
+ var setProviderSecret = (providerId, secret) => {
207
+ secrets[providerId] = secret;
208
+ };
209
+ var getProviderSecret = (providerId) => secrets[providerId] || "";
210
+
202
211
  // src/providers/BaseProvider.ts
203
212
  var BaseProvider = class {
204
213
  constructor() {
@@ -933,18 +942,6 @@ var LessonsChurchProvider = class extends BaseProvider {
933
942
  }
934
943
  };
935
944
 
936
- // src/providers/registry.ts
937
- var providerRegistry = /* @__PURE__ */ new Map();
938
- function getProvider(providerId) {
939
- return providerRegistry.get(providerId) || null;
940
- }
941
- function getAllProviders() {
942
- return Array.from(providerRegistry.values());
943
- }
944
- function registerProvider(provider) {
945
- providerRegistry.set(provider.id, provider);
946
- }
947
-
948
945
  // src/providers/b1Church/B1ChurchAuth.ts
949
946
  function buildB1AuthUrlFromChallenge(config, appBase, redirectUri, codeChallenge, state) {
950
947
  const oauthParams = new URLSearchParams({
@@ -1403,14 +1400,12 @@ var B1ChurchProvider = class extends BaseProvider {
1403
1400
  this.planTypeId = null;
1404
1401
  }
1405
1402
  }
1406
- async getCurrentPlan(_authData) {
1403
+ async getCurrentPlan(authData) {
1407
1404
  if (!this.planTypeId) return null;
1408
1405
  const plan = await fetchCurrentPlanByType(this.planTypeId);
1409
- if (!plan?.providerId || !plan.providerPlanId) return null;
1406
+ if (!plan?.providerId || !plan.providerPlanId || !plan.ministryId) return null;
1410
1407
  const items = plan.churchId && plan.id ? await fetchPlanItems(plan.churchId, plan.id) : [];
1411
- const innerProvider = getProvider(plan.providerId);
1412
- if (!innerProvider?.getInstructions) return null;
1413
- const instructions = await innerProvider.getInstructions(plan.providerPlanId, null);
1408
+ const instructions = await fetchFromProviderProxy("getInstructions", plan.ministryId, plan.providerId, plan.providerPlanId, authData);
1414
1409
  if (!instructions) return null;
1415
1410
  const files = getOrderedFiles(instructions, items);
1416
1411
  const thumbnail = findFirstThumbnail(instructions.items);
@@ -16253,6 +16248,113 @@ var LifeChurchProvider = class extends BaseProvider {
16253
16248
  }
16254
16249
  };
16255
16250
 
16251
+ // src/providers/registry.ts
16252
+ var providerRegistry = /* @__PURE__ */ new Map();
16253
+ function getProvider(providerId) {
16254
+ return providerRegistry.get(providerId) || null;
16255
+ }
16256
+ function getAllProviders() {
16257
+ return Array.from(providerRegistry.values());
16258
+ }
16259
+ function registerProvider(provider) {
16260
+ providerRegistry.set(provider.id, provider);
16261
+ }
16262
+
16263
+ // src/providers/goCurriculum/data.json
16264
+ var data_default4 = {
16265
+ catalog: []
16266
+ };
16267
+
16268
+ // src/providers/goCurriculum/GoCurriculumProvider.ts
16269
+ var GoCurriculumProvider = class extends BaseProvider {
16270
+ constructor() {
16271
+ super(...arguments);
16272
+ this.oauthHelper = new OAuthHelper();
16273
+ this.verifiedTokens = /* @__PURE__ */ new Set();
16274
+ this.id = "gocurriculum";
16275
+ this.name = "Go Curriculum";
16276
+ this.logos = {
16277
+ light: "https://gocurriculum.com/wp-content/uploads/go-logo-curriculum-v2.png",
16278
+ dark: "https://gocurriculum.com/wp-content/uploads/go-logo-curriculum-v2.png"
16279
+ };
16280
+ this.config = {
16281
+ id: "gocurriculum",
16282
+ name: "Go Curriculum",
16283
+ apiBase: "https://gocurriculum.com",
16284
+ oauthBase: "https://gocurriculum.com/oauth",
16285
+ clientId: "Rdrvz9jz9nTNf8rCsbpuAgVLdoWHZmcjdmmrP0Bw",
16286
+ // supplied at runtime via setProviderSecret("gocurriculum", ...) — never commit the value
16287
+ get clientSecret() {
16288
+ return getProviderSecret("gocurriculum");
16289
+ },
16290
+ scopes: ["basic"]
16291
+ };
16292
+ this.data = data_default4;
16293
+ this.requiresAuth = true;
16294
+ this.authTypes = ["oauth_pkce"];
16295
+ this.capabilities = { browse: true, playlist: true, instructions: true, mediaLicensing: false };
16296
+ }
16297
+ // ponytail: any valid login unlocks everything for the pilot — per-tier (MemberPress) filtering waits on Go's membership endpoint
16298
+ async verifyAuth(auth) {
16299
+ const token = auth?.access_token;
16300
+ if (!token) return false;
16301
+ if (this.verifiedTokens.has(token)) return true;
16302
+ try {
16303
+ const response = await fetch(`${this.config.oauthBase}/me`, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: new URLSearchParams({ access_token: token }).toString() });
16304
+ if (!response.ok) return false;
16305
+ this.verifiedTokens.add(token);
16306
+ return true;
16307
+ } catch {
16308
+ return false;
16309
+ }
16310
+ }
16311
+ async browse(path, auth) {
16312
+ if (!await this.verifyAuth(auth)) return [];
16313
+ const { segments, depth } = parsePath(path);
16314
+ if (depth === 0) return this.data.catalog.map((c) => createFolder(c.id, c.name, `/${c.id}`, c.thumbnail));
16315
+ const collection = this.data.catalog.find((c) => c.id === segments[0]);
16316
+ if (!collection) return [];
16317
+ if (depth === 1) return collection.lessons.map((l) => createFolder(l.id, l.name, `/${collection.id}/${l.id}`, l.thumbnail || collection.thumbnail, true));
16318
+ if (depth === 2) return this.getLessonFiles(collection, segments[1]);
16319
+ return [];
16320
+ }
16321
+ getLessonFiles(collection, lessonId) {
16322
+ const lesson = collection.lessons.find((l) => l.id === lessonId);
16323
+ if (!lesson) return [];
16324
+ return lesson.playlist.map((f) => createFile(slugify(f.file), f.title, f.url, { mediaType: detectMediaType(`${f.url} ${f.file}`, f.mediaType), thumbnail: f.thumbnail || lesson.thumbnail || collection.thumbnail, seconds: f.duration }));
16325
+ }
16326
+ async getPlaylist(path, auth, _resolution) {
16327
+ if (!await this.verifyAuth(auth)) return null;
16328
+ const { segments, depth } = parsePath(path);
16329
+ if (depth !== 2) return null;
16330
+ const collection = this.data.catalog.find((c) => c.id === segments[0]);
16331
+ if (!collection) return null;
16332
+ const files = this.getLessonFiles(collection, segments[1]);
16333
+ return files.length > 0 ? files : null;
16334
+ }
16335
+ async getInstructions(path, auth) {
16336
+ const { segments, depth } = parsePath(path);
16337
+ if (depth !== 2) return null;
16338
+ const files = await this.getPlaylist(path, auth);
16339
+ if (!files) return null;
16340
+ const collection = this.data.catalog.find((c) => c.id === segments[0]);
16341
+ const lesson = collection?.lessons.find((l) => l.id === segments[1]);
16342
+ return filesToInstructions(lesson?.name || "Lesson", files);
16343
+ }
16344
+ generateCodeVerifier() {
16345
+ return this.oauthHelper.generateCodeVerifier();
16346
+ }
16347
+ async buildAuthUrl(codeVerifier, redirectUri, state) {
16348
+ return this.oauthHelper.buildAuthUrl(this.config, codeVerifier, redirectUri, state || this.id);
16349
+ }
16350
+ buildAuthUrlFromChallenge(codeChallenge, redirectUri, state) {
16351
+ return this.oauthHelper.buildAuthUrlFromChallenge(this.config, codeChallenge, redirectUri, state);
16352
+ }
16353
+ async exchangeCodeForTokens(code, codeVerifier, redirectUri) {
16354
+ return this.oauthHelper.exchangeCodeForTokens(this.config, this.id, code, codeVerifier, redirectUri);
16355
+ }
16356
+ };
16357
+
16256
16358
  // src/providers/index.ts
16257
16359
  var unimplementedProviders = [
16258
16360
  {
@@ -16271,14 +16373,6 @@ var unimplementedProviders = [
16271
16373
  dark: "https://freeshow.app/images/favicon.png"
16272
16374
  }
16273
16375
  },
16274
- {
16275
- id: "gocurriculum",
16276
- name: "Go Curriculum",
16277
- logos: {
16278
- light: "https://gocurriculum.com/wp-content/uploads/go-logo-curriculum-v2.png",
16279
- dark: "https://gocurriculum.com/wp-content/uploads/go-logo-curriculum-v2.png"
16280
- }
16281
- },
16282
16376
  {
16283
16377
  id: "iteachchurch",
16284
16378
  name: "iTeachChurch",
@@ -16301,6 +16395,7 @@ function initializeProviders() {
16301
16395
  new APlayProvider(),
16302
16396
  new B1ChurchProvider(),
16303
16397
  new DropboxProvider(),
16398
+ new GoCurriculumProvider(),
16304
16399
  new BibleProjectProvider(),
16305
16400
  new HighVoltageKidsProvider(),
16306
16401
  new JesusFilmProvider(),
@@ -16345,7 +16440,7 @@ function getAvailableProviders(ids) {
16345
16440
  }
16346
16441
 
16347
16442
  // src/index.ts
16348
- var VERSION = true ? "0.6.0" : "dev";
16443
+ var VERSION = true ? "0.7.0" : "dev";
16349
16444
  export {
16350
16445
  APlayProvider,
16351
16446
  B1ChurchProvider,
@@ -16371,6 +16466,7 @@ export {
16371
16466
  isContentFolder,
16372
16467
  navigateToPath,
16373
16468
  registerProvider,
16469
+ setProviderSecret,
16374
16470
  toAuthData
16375
16471
  };
16376
16472
  //# sourceMappingURL=index.js.map