@ichinese/open-sdk 1.0.0-rc.2 → 1.0.0-rc.4

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 CHANGED
@@ -31,6 +31,7 @@ __export(index_exports, {
31
31
  SCOPE_ATTENDANCE_READONLY: () => SCOPE_ATTENDANCE_READONLY,
32
32
  SCOPE_CLASS_MEMBER: () => SCOPE_CLASS_MEMBER,
33
33
  SCOPE_CLASS_READONLY: () => SCOPE_CLASS_READONLY,
34
+ SCOPE_COMMUNITY_POST: () => SCOPE_COMMUNITY_POST,
34
35
  SCOPE_COURSE: () => SCOPE_COURSE,
35
36
  SCOPE_COURSEWORK: () => SCOPE_COURSEWORK,
36
37
  SCOPE_EVENT: () => SCOPE_EVENT,
@@ -38,12 +39,18 @@ __export(index_exports, {
38
39
  SCOPE_GRADE_READONLY: () => SCOPE_GRADE_READONLY,
39
40
  SCOPE_IM_CHAT_READONLY: () => SCOPE_IM_CHAT_READONLY,
40
41
  SCOPE_IM_MESSAGE_SEND_AS_BOT: () => SCOPE_IM_MESSAGE_SEND_AS_BOT,
42
+ SCOPE_INSIGHTS_READONLY: () => SCOPE_INSIGHTS_READONLY,
41
43
  SCOPE_JOB: () => SCOPE_JOB,
42
44
  SCOPE_JOB_CANDIDATE_READONLY: () => SCOPE_JOB_CANDIDATE_READONLY,
43
45
  SCOPE_LEARNING_PROGRESS_READONLY: () => SCOPE_LEARNING_PROGRESS_READONLY,
44
46
  SCOPE_LIVE_RECORDING_READONLY: () => SCOPE_LIVE_RECORDING_READONLY,
47
+ SCOPE_ME_LEARNING_READONLY: () => SCOPE_ME_LEARNING_READONLY,
48
+ SCOPE_ME_SCHEDULE_READONLY: () => SCOPE_ME_SCHEDULE_READONLY,
49
+ SCOPE_ME_WORDS: () => SCOPE_ME_WORDS,
50
+ SCOPE_ME_WORDS_READONLY: () => SCOPE_ME_WORDS_READONLY,
45
51
  SCOPE_NOTIFY_SEND: () => SCOPE_NOTIFY_SEND,
46
52
  SCOPE_OPENID: () => SCOPE_OPENID,
53
+ SCOPE_OPENING_READONLY: () => SCOPE_OPENING_READONLY,
47
54
  SCOPE_ORG_MEMBER: () => SCOPE_ORG_MEMBER,
48
55
  SCOPE_ORG_MEMBER_READONLY: () => SCOPE_ORG_MEMBER_READONLY,
49
56
  SCOPE_PROFILE: () => SCOPE_PROFILE,
@@ -54,8 +61,11 @@ __export(index_exports, {
54
61
  SCOPE_REVIEW: () => SCOPE_REVIEW,
55
62
  SCOPE_SCHEDULE: () => SCOPE_SCHEDULE,
56
63
  SCOPE_SCHEDULE_READONLY: () => SCOPE_SCHEDULE_READONLY,
64
+ SCOPE_TRADE_BOOKING_READONLY: () => SCOPE_TRADE_BOOKING_READONLY,
57
65
  SCOPE_TRADE_ORDER_READONLY: () => SCOPE_TRADE_ORDER_READONLY,
58
66
  SCOPE_TRIAL: () => SCOPE_TRIAL,
67
+ SCOPE_VENUE: () => SCOPE_VENUE,
68
+ SCOPE_VENUE_READONLY: () => SCOPE_VENUE_READONLY,
59
69
  VERSION: () => VERSION,
60
70
  challengeResponse: () => challengeResponse,
61
71
  decrypt: () => decrypt,
@@ -98,6 +108,13 @@ var OpenApiError = class extends Error {
98
108
  get neverAsk() {
99
109
  return this.details.never_ask === true;
100
110
  }
111
+ /**
112
+ * Where the person agrees to show their name on posts (409 `consent_required`): send them there, an
113
+ * app cannot agree for them.
114
+ */
115
+ get consentUrl() {
116
+ return typeof this.details.consent_url === "string" ? this.details.consent_url : null;
117
+ }
101
118
  /** A refused, not yet asked or undeclared scope (403): degrade gracefully. */
102
119
  get isScopeError() {
103
120
  return this.status === 403 && ["scope_denied", "scope_pending", "scope_not_declared", "gold_only"].includes(String(this.reason));
@@ -106,7 +123,7 @@ var OpenApiError = class extends Error {
106
123
  var isOpenApiError = (e) => e instanceof OpenApiError;
107
124
 
108
125
  // src/gen/meta.ts
109
- var VERSION = "1.0.0-rc.2";
126
+ var VERSION = "1.0.0-rc.4";
110
127
  var DEFAULT_BASE_URL = "https://open.ichinese.cn";
111
128
  var API_PREFIX = "/open-apis/v1";
112
129
  var SCOPES = [
@@ -133,11 +150,21 @@ var SCOPES = [
133
150
  "job:candidate:readonly",
134
151
  "trial",
135
152
  "trade:order:readonly",
153
+ "trade:booking:readonly",
154
+ "venue:readonly",
155
+ "venue",
156
+ "opening:readonly",
136
157
  "review",
137
158
  "profile:interest:readonly",
138
159
  "profile:interest",
139
160
  "profile:learning:readonly",
140
- "report:create"
161
+ "report:create",
162
+ "community:post",
163
+ "me:schedule:readonly",
164
+ "me:learning:readonly",
165
+ "me:words:readonly",
166
+ "me:words",
167
+ "insights:readonly"
141
168
  ];
142
169
  var EVENT_TYPES = [
143
170
  "app.installed",
@@ -151,7 +178,18 @@ var EVENT_TYPES = [
151
178
  "trade.order_paid",
152
179
  "review.created",
153
180
  "trial.booked",
154
- "im.message_received"
181
+ "im.message_received",
182
+ "class.member_changed",
183
+ "course.completed",
184
+ "exam.results_released",
185
+ "event.checked_in",
186
+ "live.recording_ready",
187
+ "trade.order_refunded",
188
+ "trial.confirmed",
189
+ "trial.cancelled",
190
+ "community.post_reviewed",
191
+ "community.video_ready",
192
+ "booking.changed"
155
193
  ];
156
194
  var ERROR_REASONS = [
157
195
  "invalid_credentials",
@@ -172,7 +210,30 @@ var ERROR_REASONS = [
172
210
  "code_invalid",
173
211
  "redirect_uri_mismatch",
174
212
  "origin_not_trusted",
175
- "ip_not_allowed"
213
+ "ip_not_allowed",
214
+ "org_not_installed",
215
+ "minor_not_allowed",
216
+ "birth_year_required",
217
+ "feature_disabled",
218
+ "app_post_stopped",
219
+ "consent_required",
220
+ "video_not_ready",
221
+ "video_failed",
222
+ "post_rate_limited",
223
+ "app_user_daily_limit",
224
+ "post_in_progress",
225
+ "no_org_account",
226
+ "teacher_busy",
227
+ "online_only",
228
+ "venue_unavailable",
229
+ "venue_not_found",
230
+ "slot_overlap",
231
+ "slot_taken",
232
+ "not_draft",
233
+ "not_editable",
234
+ "org_not_verified",
235
+ "opening_ended",
236
+ "opening_incomplete"
176
237
  ];
177
238
  var OPERATION_TOKENS = {
178
239
  getUserInfo: ["user"],
@@ -185,6 +246,8 @@ var OPERATION_TOKENS = {
185
246
  addClassMember: ["tenant", "user"],
186
247
  removeClassMember: ["tenant", "user"],
187
248
  listClassSessions: ["tenant", "user"],
249
+ createClassSessions: ["tenant", "user"],
250
+ setClassSessionPlace: ["tenant", "user"],
188
251
  cancelClassSession: ["tenant", "user"],
189
252
  rescheduleClassSession: ["tenant", "user"],
190
253
  getSessionAttendance: ["tenant", "user"],
@@ -212,16 +275,40 @@ var OPERATION_TOKENS = {
212
275
  listTrialSlots: ["tenant"],
213
276
  createTrialSlots: ["tenant"],
214
277
  cancelTrialSlot: ["tenant"],
278
+ setTrialTeacher: ["tenant"],
215
279
  listTrialBookings: ["tenant"],
216
280
  confirmTrialBooking: ["tenant"],
281
+ listVenues: ["tenant"],
282
+ createVenue: ["tenant"],
283
+ getVenue: ["tenant"],
284
+ listOpenings: ["tenant", "user"],
285
+ getOpening: ["tenant", "user"],
286
+ getInsights: ["tenant"],
217
287
  listTradeOrders: ["tenant"],
288
+ listBookings: ["tenant"],
289
+ getBooking: ["tenant"],
218
290
  listTenantReviews: ["tenant"],
219
291
  replyReview: ["tenant"],
220
292
  listPublicContent: ["app", "tenant", "user"],
221
293
  getUserInterests: ["user"],
222
294
  addInterestSignal: ["user"],
223
295
  getUserLearningProfile: ["user"],
224
- reportOnBehalf: ["user"]
296
+ reportOnBehalf: ["user"],
297
+ createCommunityImageUpload: ["user", "tenant"],
298
+ createCommunityVideoUpload: ["user", "tenant"],
299
+ completeCommunityVideoUpload: ["user", "tenant"],
300
+ getCommunityVideo: ["user", "tenant"],
301
+ listCommunityPosts: ["user", "tenant"],
302
+ createCommunityPost: ["user", "tenant"],
303
+ getCommunityPost: ["user", "tenant"],
304
+ deleteCommunityPost: ["user", "tenant"],
305
+ listCommunityCategories: ["user", "tenant"],
306
+ getMySchedule: ["user"],
307
+ listMyCourses: ["user"],
308
+ listMyWords: ["user"],
309
+ addMyWord: ["user"],
310
+ listClassExams: ["tenant"],
311
+ listClassExamResults: ["tenant"]
225
312
  };
226
313
  var SCOPE_OPENID = "openid";
227
314
  var SCOPE_PROFILE = "profile";
@@ -246,11 +333,21 @@ var SCOPE_JOB = "job";
246
333
  var SCOPE_JOB_CANDIDATE_READONLY = "job:candidate:readonly";
247
334
  var SCOPE_TRIAL = "trial";
248
335
  var SCOPE_TRADE_ORDER_READONLY = "trade:order:readonly";
336
+ var SCOPE_TRADE_BOOKING_READONLY = "trade:booking:readonly";
337
+ var SCOPE_VENUE_READONLY = "venue:readonly";
338
+ var SCOPE_VENUE = "venue";
339
+ var SCOPE_OPENING_READONLY = "opening:readonly";
249
340
  var SCOPE_REVIEW = "review";
250
341
  var SCOPE_PROFILE_INTEREST_READONLY = "profile:interest:readonly";
251
342
  var SCOPE_PROFILE_INTEREST = "profile:interest";
252
343
  var SCOPE_PROFILE_LEARNING_READONLY = "profile:learning:readonly";
253
344
  var SCOPE_REPORT_CREATE = "report:create";
345
+ var SCOPE_COMMUNITY_POST = "community:post";
346
+ var SCOPE_ME_SCHEDULE_READONLY = "me:schedule:readonly";
347
+ var SCOPE_ME_LEARNING_READONLY = "me:learning:readonly";
348
+ var SCOPE_ME_WORDS_READONLY = "me:words:readonly";
349
+ var SCOPE_ME_WORDS = "me:words";
350
+ var SCOPE_INSIGHTS_READONLY = "insights:readonly";
254
351
 
255
352
  // src/gen/operations.ts
256
353
  var Operations = class {
@@ -310,6 +407,14 @@ var Operations = class {
310
407
  listClassSessionsAll(classId, query, options) {
311
408
  return this.iterate({ id: "listClassSessions", method: "GET", path: "/classes/{class_id}/sessions", pathParams: { class_id: classId }, query, tokenTypes: ["tenant", "user"] }, options);
312
409
  }
410
+ /** Schedule a class's sessions (once, or repeating weekly), online or at one of the institution's teaching sites (`POST /classes/{class_id}/sessions`; tenant / user token; scopes: schedule) */
411
+ createClassSessions(classId, body, options) {
412
+ return this.call({ id: "createClassSessions", method: "POST", path: "/classes/{class_id}/sessions", pathParams: { class_id: classId }, body, tokenTypes: ["tenant", "user"] }, options);
413
+ }
414
+ /** Change where one session (or this one and the later ones of its series) is held (`PUT /classes/{class_id}/sessions/{session_id}/place`; tenant / user token; scopes: schedule) */
415
+ setClassSessionPlace(classId, sessionId, body, options) {
416
+ return this.call({ id: "setClassSessionPlace", method: "PUT", path: "/classes/{class_id}/sessions/{session_id}/place", pathParams: { class_id: classId, session_id: sessionId }, body, tokenTypes: ["tenant", "user"] }, options);
417
+ }
313
418
  /** Cancel a class session (members are notified, the same as cancelling in the timetable) (`POST /classes/{class_id}/sessions/{session_id}/cancel`; tenant / user token; scopes: schedule) */
314
419
  cancelClassSession(classId, sessionId, options) {
315
420
  return this.call({ id: "cancelClassSession", method: "POST", path: "/classes/{class_id}/sessions/{session_id}/cancel", pathParams: { class_id: classId, session_id: sessionId }, tokenTypes: ["tenant", "user"] }, options);
@@ -454,7 +559,7 @@ var Operations = class {
454
559
  listTrialVenuesAll(query, options) {
455
560
  return this.iterate({ id: "listTrialVenues", method: "GET", path: "/trial/venues", pathParams: {}, query, tokenTypes: ["tenant"] }, options);
456
561
  }
457
- /** Trial lesson slots (by default only those not over yet, by time) (`GET /trial/slots`; tenant token; scopes: trial) */
562
+ /** Trial times (a trial lesson's own times at teaching sites; by default only those not over yet, by time) (`GET /trial/slots`; tenant token; scopes: trial) */
458
563
  listTrialSlots(query, options) {
459
564
  return this.call({ id: "listTrialSlots", method: "GET", path: "/trial/slots", pathParams: {}, query, tokenTypes: ["tenant"] }, options);
460
565
  }
@@ -462,14 +567,18 @@ var Operations = class {
462
567
  listTrialSlotsAll(query, options) {
463
568
  return this.iterate({ id: "listTrialSlots", method: "GET", path: "/trial/slots", pathParams: {}, query, tokenTypes: ["tenant"] }, options);
464
569
  }
465
- /** Schedule trial lesson slots (they can repeat weekly); the teaching site must be published (`POST /trial/slots`; tenant token; scopes: trial) */
570
+ /** Open trial times (they can repeat weekly): add them to an existing trial lesson, or open a new trial draft (`POST /trial/slots`; tenant token; scopes: trial) */
466
571
  createTrialSlots(body, options) {
467
572
  return this.call({ id: "createTrialSlots", method: "POST", path: "/trial/slots", pathParams: {}, body, tokenTypes: ["tenant"] }, options);
468
573
  }
469
- /** Cancel a trial lesson slot (whoever booked it is notified as usual; the booking is cancelled with it) (`POST /trial/slots/{slot_id}/cancel`; tenant token; scopes: trial) */
574
+ /** Close a trial time (no more bookings; bookings already made stay as they are, and nobody is notified) (`POST /trial/slots/{slot_id}/cancel`; tenant token; scopes: trial) */
470
575
  cancelTrialSlot(slotId, body, options) {
471
576
  return this.call({ id: "cancelTrialSlot", method: "POST", path: "/trial/slots/{slot_id}/cancel", pathParams: { slot_id: slotId }, body, tokenTypes: ["tenant"] }, options);
472
577
  }
578
+ /** Fill in (or change) the teacher of a trial draft (`PUT /trial/openings/{opening_id}/teacher`; tenant token; scopes: trial) */
579
+ setTrialTeacher(openingId, body, options) {
580
+ return this.call({ id: "setTrialTeacher", method: "PUT", path: "/trial/openings/{opening_id}/teacher", pathParams: { opening_id: openingId }, body, tokenTypes: ["tenant"] }, options);
581
+ }
473
582
  /** Trial lesson bookings at the institution's teaching sites (`GET /trial/bookings`; tenant token; scopes: trial) */
474
583
  listTrialBookings(query, options) {
475
584
  return this.call({ id: "listTrialBookings", method: "GET", path: "/trial/bookings", pathParams: {}, query, tokenTypes: ["tenant"] }, options);
@@ -482,6 +591,38 @@ var Operations = class {
482
591
  confirmTrialBooking(bookingId, options) {
483
592
  return this.call({ id: "confirmTrialBooking", method: "POST", path: "/trial/bookings/{booking_id}/confirm", pathParams: { booking_id: bookingId }, tokenTypes: ["tenant"] }, options);
484
593
  }
594
+ /** The institution's teaching sites (campuses), newest first (`GET /venues`; tenant token; scopes: venue:readonly, venue, trial) */
595
+ listVenues(query, options) {
596
+ return this.call({ id: "listVenues", method: "GET", path: "/venues", pathParams: {}, query, tokenTypes: ["tenant"] }, options);
597
+ }
598
+ /** Every item of `listVenues`, page after page. */
599
+ listVenuesAll(query, options) {
600
+ return this.iterate({ id: "listVenues", method: "GET", path: "/venues", pathParams: {}, query, tokenTypes: ["tenant"] }, options);
601
+ }
602
+ /** Create a draft teaching site (campus); it is published in the platform and reviewed as usual (`POST /venues`; tenant token; scopes: venue) */
603
+ createVenue(body, options) {
604
+ return this.call({ id: "createVenue", method: "POST", path: "/venues", pathParams: {}, body, tokenTypes: ["tenant"] }, options);
605
+ }
606
+ /** One teaching site (of this institution only) (`GET /venues/{venue_id}`; tenant token; scopes: venue:readonly, venue, trial) */
607
+ getVenue(venueId, options) {
608
+ return this.call({ id: "getVenue", method: "GET", path: "/venues/{venue_id}", pathParams: { venue_id: venueId }, tokenTypes: ["tenant"] }, options);
609
+ }
610
+ /** Openings: every class and course opened (group classes, one-to-ones, open classes, trial lessons, video courses), newest first (`GET /openings`; tenant / user token; scopes: opening:readonly) */
611
+ listOpenings(query, options) {
612
+ return this.call({ id: "listOpenings", method: "GET", path: "/openings", pathParams: {}, query, tokenTypes: ["tenant", "user"] }, options);
613
+ }
614
+ /** Every item of `listOpenings`, page after page. */
615
+ listOpeningsAll(query, options) {
616
+ return this.iterate({ id: "listOpenings", method: "GET", path: "/openings", pathParams: {}, query, tokenTypes: ["tenant", "user"] }, options);
617
+ }
618
+ /** One opening (`GET /openings/{opening_id}`; tenant / user token; scopes: opening:readonly) */
619
+ getOpening(openingId, options) {
620
+ return this.call({ id: "getOpening", method: "GET", path: "/openings/{opening_id}", pathParams: { opening_id: openingId }, tokenTypes: ["tenant", "user"] }, options);
621
+ }
622
+ /** One page of the institution's dashboard (read only): enrolment, teaching, teaching sites, income, community and events (`GET /insights`; tenant token; scopes: insights:readonly) */
623
+ getInsights(query, options) {
624
+ return this.call({ id: "getInsights", method: "GET", path: "/insights", pathParams: {}, query, tokenTypes: ["tenant"] }, options);
625
+ }
485
626
  /** The institution's orders (read-only, for reconciliation). Write operations involving money are not open (`GET /trade/orders`; tenant token; scopes: trade:order:readonly) */
486
627
  listTradeOrders(query, options) {
487
628
  return this.call({ id: "listTradeOrders", method: "GET", path: "/trade/orders", pathParams: {}, query, tokenTypes: ["tenant"] }, options);
@@ -490,6 +631,18 @@ var Operations = class {
490
631
  listTradeOrdersAll(query, options) {
491
632
  return this.iterate({ id: "listTradeOrders", method: "GET", path: "/trade/orders", pathParams: {}, query, tokenTypes: ["tenant"] }, options);
492
633
  }
634
+ /** The institution's bookings (one-to-ones and trial lessons, read only for reconciliation), latest lesson first (`GET /bookings`; tenant token; scopes: trade:booking:readonly) */
635
+ listBookings(query, options) {
636
+ return this.call({ id: "listBookings", method: "GET", path: "/bookings", pathParams: {}, query, tokenTypes: ["tenant"] }, options);
637
+ }
638
+ /** Every item of `listBookings`, page after page. */
639
+ listBookingsAll(query, options) {
640
+ return this.iterate({ id: "listBookings", method: "GET", path: "/bookings", pathParams: {}, query, tokenTypes: ["tenant"] }, options);
641
+ }
642
+ /** One booked lesson (`GET /bookings/{booking_id}`; tenant token; scopes: trade:booking:readonly) */
643
+ getBooking(bookingId, options) {
644
+ return this.call({ id: "getBooking", method: "GET", path: "/bookings/{booking_id}", pathParams: { booking_id: bookingId }, tokenTypes: ["tenant"] }, options);
645
+ }
493
646
  /** Reviews of this institution (and its courses and teaching sites) (`GET /reviews`; tenant token; scopes: review) */
494
647
  listTenantReviews(query, options) {
495
648
  return this.call({ id: "listTenantReviews", method: "GET", path: "/reviews", pathParams: {}, query, tokenTypes: ["tenant"] }, options);
@@ -526,6 +679,90 @@ var Operations = class {
526
679
  reportOnBehalf(body, options) {
527
680
  return this.call({ id: "reportOnBehalf", method: "POST", path: "/reports", pathParams: {}, body, tokenTypes: ["user"] }, options);
528
681
  }
682
+ /** Get an upload URL for an image (it is re-encoded and its EXIF removed when the post is made) (`POST /community/images`; user / tenant token; scopes: community:post) */
683
+ createCommunityImageUpload(body, options) {
684
+ return this.call({ id: "createCommunityImageUpload", method: "POST", path: "/community/images", pathParams: {}, body, tokenTypes: ["user", "tenant"] }, options);
685
+ }
686
+ /** Get an upload URL for a video (up to 4 GB, 1 hour) (`POST /community/videos`; user / tenant token; scopes: community:post) */
687
+ createCommunityVideoUpload(body, options) {
688
+ return this.call({ id: "createCommunityVideoUpload", method: "POST", path: "/community/videos", pathParams: {}, body, tokenTypes: ["user", "tenant"] }, options);
689
+ }
690
+ /** The upload is done; start transcoding (calling it again has no side effects) (`POST /community/videos/{asset_id}/complete`; user / tenant token; scopes: community:post) */
691
+ completeCommunityVideoUpload(assetId, options) {
692
+ return this.call({ id: "completeCommunityVideoUpload", method: "POST", path: "/community/videos/{asset_id}/complete", pathParams: { asset_id: assetId }, tokenTypes: ["user", "tenant"] }, options);
693
+ }
694
+ /** Transcoding status (only for videos this app uploaded for this person) (`GET /community/videos/{asset_id}`; user / tenant token; scopes: community:post) */
695
+ getCommunityVideo(assetId, options) {
696
+ return this.call({ id: "getCommunityVideo", method: "GET", path: "/community/videos/{asset_id}", pathParams: { asset_id: assetId }, tokenTypes: ["user", "tenant"] }, options);
697
+ }
698
+ /** Posts this app made in this person's name (the person's own posts and other apps' posts are not visible) (`GET /community/posts`; user / tenant token; scopes: community:post) */
699
+ listCommunityPosts(query, options) {
700
+ return this.call({ id: "listCommunityPosts", method: "GET", path: "/community/posts", pathParams: {}, query, tokenTypes: ["user", "tenant"] }, options);
701
+ }
702
+ /** Every item of `listCommunityPosts`, page after page. */
703
+ listCommunityPostsAll(query, options) {
704
+ return this.iterate({ id: "listCommunityPosts", method: "GET", path: "/community/posts", pathParams: {}, query, tokenTypes: ["user", "tenant"] }, options);
705
+ }
706
+ /** Publish a public post in this person's name (text, up to 9 images or 1 video, up to 10 topics) (`POST /community/posts`; user / tenant token; scopes: community:post) */
707
+ createCommunityPost(body, options) {
708
+ return this.call({ id: "createCommunityPost", method: "POST", path: "/community/posts", pathParams: {}, body, tokenTypes: ["user", "tenant"] }, options);
709
+ }
710
+ /** The status of one post (only posts this app made; anything else is 404) (`GET /community/posts/{post_id}`; user / tenant token; scopes: community:post) */
711
+ getCommunityPost(postId, options) {
712
+ return this.call({ id: "getCommunityPost", method: "GET", path: "/community/posts/{post_id}", pathParams: { post_id: postId }, tokenTypes: ["user", "tenant"] }, options);
713
+ }
714
+ /** Delete a post (only posts this app made; anything else is 404) (`DELETE /community/posts/{post_id}`; user / tenant token; scopes: community:post) */
715
+ deleteCommunityPost(postId, options) {
716
+ return this.call({ id: "deleteCommunityPost", method: "DELETE", path: "/community/posts/{post_id}", pathParams: { post_id: postId }, tokenTypes: ["user", "tenant"] }, options);
717
+ }
718
+ /** Categories a post can be filed under (those enabled in this region) (`GET /community/categories`; user / tenant token; scopes: community:post) */
719
+ listCommunityCategories(options) {
720
+ return this.call({ id: "listCommunityCategories", method: "GET", path: "/community/categories", pathParams: {}, tokenTypes: ["user", "tenant"] }, options);
721
+ }
722
+ /** This person's timetable: their sessions, the events they signed up for, confirmed trial lessons and exams (for calendar apps; read-only) (`GET /me/schedule`; user token; scopes: me:schedule:readonly) */
723
+ getMySchedule(query, options) {
724
+ return this.call({ id: "getMySchedule", method: "GET", path: "/me/schedule", pathParams: {}, query, tokenTypes: ["user"] }, options);
725
+ }
726
+ /** Every item of `getMySchedule`, page after page. */
727
+ getMyScheduleAll(query, options) {
728
+ return this.iterate({ id: "getMySchedule", method: "GET", path: "/me/schedule", pathParams: {}, query, tokenTypes: ["user"] }, options);
729
+ }
730
+ /** The courses this person is studying and their progress (lessons completed, last studied; level and goals belong to the gold-only profile and are not here) (`GET /me/courses`; user token; scopes: me:learning:readonly) */
731
+ listMyCourses(query, options) {
732
+ return this.call({ id: "listMyCourses", method: "GET", path: "/me/courses", pathParams: {}, query, tokenTypes: ["user"] }, options);
733
+ }
734
+ /** Every item of `listMyCourses`, page after page. */
735
+ listMyCoursesAll(query, options) {
736
+ return this.iterate({ id: "listMyCourses", method: "GET", path: "/me/courses", pathParams: {}, query, tokenTypes: ["user"] }, options);
737
+ }
738
+ /** This person's word bank: words, pinyin, review state (no meanings, and review records cannot be changed) (`GET /me/words`; user token; scopes: me:words:readonly, me:words) */
739
+ listMyWords(query, options) {
740
+ return this.call({ id: "listMyWords", method: "GET", path: "/me/words", pathParams: {}, query, tokenTypes: ["user"] }, options);
741
+ }
742
+ /** Every item of `listMyWords`, page after page. */
743
+ listMyWordsAll(query, options) {
744
+ return this.iterate({ id: "listMyWords", method: "GET", path: "/me/words", pathParams: {}, query, tokenTypes: ["user"] }, options);
745
+ }
746
+ /** Add a word to this person's word bank (unchanged if it is already there; its source is recorded as this app) (`POST /me/words`; user token; scopes: me:words) */
747
+ addMyWord(body, options) {
748
+ return this.call({ id: "addMyWord", method: "POST", path: "/me/words", pathParams: {}, body, tokenTypes: ["user"] }, options);
749
+ }
750
+ /** A class's exams (tenant tokens only, own institution) (`GET /classes/{class_id}/exams`; tenant token; scopes: grade:readonly) */
751
+ listClassExams(classId, query, options) {
752
+ return this.call({ id: "listClassExams", method: "GET", path: "/classes/{class_id}/exams", pathParams: { class_id: classId }, query, tokenTypes: ["tenant"] }, options);
753
+ }
754
+ /** Every item of `listClassExams`, page after page. */
755
+ listClassExamsAll(classId, query, options) {
756
+ return this.iterate({ id: "listClassExams", method: "GET", path: "/classes/{class_id}/exams", pathParams: { class_id: classId }, query, tokenTypes: ["tenant"] }, options);
757
+ }
758
+ /** Each learner's result in one exam (scores only once released; tenant tokens only, own institution) (`GET /classes/{class_id}/exams/{exam_id}/results`; tenant token; scopes: grade:readonly) */
759
+ listClassExamResults(classId, examId, query, options) {
760
+ return this.call({ id: "listClassExamResults", method: "GET", path: "/classes/{class_id}/exams/{exam_id}/results", pathParams: { class_id: classId, exam_id: examId }, query, tokenTypes: ["tenant"] }, options);
761
+ }
762
+ /** Every item of `listClassExamResults`, page after page. */
763
+ listClassExamResultsAll(classId, examId, query, options) {
764
+ return this.iterate({ id: "listClassExamResults", method: "GET", path: "/classes/{class_id}/exams/{exam_id}/results", pathParams: { class_id: classId, exam_id: examId }, query, tokenTypes: ["tenant"] }, options);
765
+ }
529
766
  };
530
767
 
531
768
  // src/client.ts
@@ -607,6 +844,129 @@ var Client = class extends Operations {
607
844
  clearTokenCache() {
608
845
  this.tokens.clear();
609
846
  }
847
+ // ── posting in a person's name (D105), and as the institution (M6.21 K2) ─────
848
+ /**
849
+ * Uploads an image for a post: asks for an upload slot, PUTs the bytes there and returns the `key` to
850
+ * put in `images[].key`. Up to 10 MB; jpeg, png, webp or gif.
851
+ */
852
+ uploadImage(userAccessToken, data, contentType, options = {}) {
853
+ return this.uploadImageWith({ userAccessToken, signal: options.signal }, data, contentType);
854
+ }
855
+ /** `uploadImage` for a post of the institution (its tenant token, `community:post` granted by it). */
856
+ uploadImageAsOrg(tenantKey, data, contentType, options = {}) {
857
+ return this.uploadImageWith({ tenantKey, signal: options.signal }, data, contentType);
858
+ }
859
+ /**
860
+ * Uploads a video for a post (up to 4 GB, 1 hour): asks for an upload slot, PUTs the file there and
861
+ * starts transcoding. A large file goes as a Blob that reads from disk (`await fs.openAsBlob(path)`).
862
+ * Returns the video's status (`asset_id`); wait for `ready` with `waitVideoReady` before posting.
863
+ */
864
+ uploadVideo(userAccessToken, data, file, options = {}) {
865
+ return this.uploadVideoWith({ userAccessToken, signal: options.signal }, data, file);
866
+ }
867
+ /** `uploadVideo` for a post of the institution. */
868
+ uploadVideoAsOrg(tenantKey, data, file, options = {}) {
869
+ return this.uploadVideoWith({ tenantKey, signal: options.signal }, data, file);
870
+ }
871
+ /**
872
+ * Waits until a video has been transcoded and returns it (`status: ready`). A failed transcode throws
873
+ * an OpenApiError with reason `video_failed` (upload another file); running out of time throws one
874
+ * with code TIMEOUT (reason `video_not_ready`). You also get the `community.video_ready` event.
875
+ */
876
+ waitVideoReady(userAccessToken, assetId, options = {}) {
877
+ return this.waitVideoWith({ userAccessToken }, assetId, options);
878
+ }
879
+ /** `waitVideoReady` for a video uploaded for the institution. */
880
+ waitVideoReadyAsOrg(tenantKey, assetId, options = {}) {
881
+ return this.waitVideoWith({ tenantKey }, assetId, options);
882
+ }
883
+ /**
884
+ * Posts in the person's name. Without a `uuid` the SDK makes one, so its own retries never post twice;
885
+ * give your own `uuid` to make your retries safe too (one post per `uuid` within an hour). Errors keep the server's reason: `consent_required`
886
+ * (send the person to `error.consentUrl`), `video_not_ready`, `video_failed`, `post_rate_limited`,
887
+ * `app_user_daily_limit`, `daily_quota_exceeded`, `minor_not_allowed`, `app_post_stopped`, …
888
+ */
889
+ createPost(userAccessToken, post, options = {}) {
890
+ return this.createPostWith({ userAccessToken, signal: options.signal }, post);
891
+ }
892
+ /**
893
+ * Posts as the institution, with its community account (its name and logo; the post says it came from
894
+ * this app). The institution must have installed the app and granted `community:post`; before its
895
+ * homepage was first published there is no account yet (409 `no_org_account`). The same limits,
896
+ * review and `uuid` handling as `createPost`.
897
+ */
898
+ createPostAsOrg(tenantKey, post, options = {}) {
899
+ return this.createPostWith({ tenantKey, signal: options.signal }, post);
900
+ }
901
+ // ── the institution's dashboard (M6.21 K2) ────────────────────────────────
902
+ /**
903
+ * Every page of the institution's dashboard the API offers (`GET /insights`, `insights:readonly`): the
904
+ * default page first, then each other tab it lists, with the same query (range, dates, filters). Returns
905
+ * tab → page. Fewer than five people come back as `null` with `withheld: true`; money is per currency.
906
+ */
907
+ async getAllInsights(tenantKey, query = {}, options = {}) {
908
+ const who = { tenantKey, signal: options.signal };
909
+ const first = await this.getInsights(query, who);
910
+ const out = { [first.tab]: first };
911
+ for (const tab of first.tabs) {
912
+ if (out[tab]) continue;
913
+ out[tab] = await this.getInsights({ ...query, tab }, who);
914
+ }
915
+ return out;
916
+ }
917
+ async uploadImageWith(who, data, contentType) {
918
+ const slot = await this.createCommunityImageUpload({ content_type: contentType, size_bytes: sizeOf(data) }, who);
919
+ await this.putUpload(slot.upload_url, slot.headers, data, who.signal);
920
+ return slot.key;
921
+ }
922
+ async uploadVideoWith(who, data, file) {
923
+ const slot = await this.createCommunityVideoUpload(
924
+ { filename: file.filename, content_type: file.contentType, size_bytes: sizeOf(data) },
925
+ who
926
+ );
927
+ await this.putUpload(slot.upload.url, slot.upload.headers, data, who.signal);
928
+ return this.send(
929
+ "POST",
930
+ `/community/videos/${encodeURIComponent(slot.asset_id)}/complete`,
931
+ { auth: this.postingAuth("completeCommunityVideoUpload", who), retryPost: true }
932
+ );
933
+ }
934
+ async waitVideoWith(who, assetId, options) {
935
+ const deadline = Date.now() + (options.timeoutMs ?? 30 * 6e4);
936
+ let interval = options.intervalMs ?? 2e3;
937
+ const maxInterval = options.maxIntervalMs ?? 15e3;
938
+ for (; ; ) {
939
+ const video = await this.getCommunityVideo(assetId, { ...who, signal: options.signal });
940
+ if (video.status === "ready") return video;
941
+ if (video.status === "failed") {
942
+ throw new OpenApiError({
943
+ status: 409,
944
+ code: "CONFLICT",
945
+ message: `video ${assetId} could not be transcoded${video.error ? ` (${video.error})` : ""}`,
946
+ details: { reason: "video_failed", asset_id: assetId, error: video.error ?? null }
947
+ });
948
+ }
949
+ const left = deadline - Date.now();
950
+ if (left <= 0) {
951
+ throw new OpenApiError({
952
+ status: 0,
953
+ code: "TIMEOUT",
954
+ message: `video ${assetId} is not ready yet (${video.status})`,
955
+ details: { reason: "video_not_ready", asset_id: assetId, status: video.status }
956
+ });
957
+ }
958
+ await this.sleep(Math.min(interval, left), options.signal);
959
+ interval = Math.min(interval * 2, maxInterval);
960
+ }
961
+ }
962
+ createPostWith(who, post) {
963
+ const body = post.uuid ? post : { ...post, uuid: crypto.randomUUID() };
964
+ return this.send("POST", "/community/posts", {
965
+ body,
966
+ auth: this.postingAuth("createCommunityPost", who),
967
+ retryPost: true
968
+ });
969
+ }
610
970
  // ── calls ─────────────────────────────────────────────────────────────────
611
971
  /** Any open API call by method and path (`/tenant/members`), for what the generated methods do not cover. */
612
972
  request(method, path, init = {}, options) {
@@ -648,6 +1008,31 @@ var Client = class extends Operations {
648
1008
  credentials() {
649
1009
  return { app_id: this.appId, app_secret: this.appSecret };
650
1010
  }
1011
+ postingAuth(id, who) {
1012
+ const spec = { id, method: "POST", path: "", pathParams: {}, tokenTypes: ["user", "tenant"] };
1013
+ return { spec, options: who };
1014
+ }
1015
+ /**
1016
+ * PUTs an upload to the slot the platform gave (object storage, not the open API: no token, its own
1017
+ * headers). Not limited by `timeoutMs`, a large video takes as long as it takes; cancel with the signal.
1018
+ */
1019
+ async putUpload(url, headers, data, signal) {
1020
+ let res;
1021
+ try {
1022
+ res = await this.fetchFn(url, { method: "PUT", headers, body: data, signal });
1023
+ } catch (err) {
1024
+ if (signal?.aborted) throw err;
1025
+ throw new OpenApiError({ status: 0, code: "NETWORK", message: `PUT upload: ${err.message}` });
1026
+ }
1027
+ if (!res.ok) {
1028
+ throw new OpenApiError({
1029
+ status: res.status,
1030
+ code: "UPLOAD_FAILED",
1031
+ message: `PUT upload: ${res.status} ${res.statusText}`,
1032
+ details: { reason: "upload_failed", body: (await res.text().catch(() => "")).slice(0, 500) }
1033
+ });
1034
+ }
1035
+ }
651
1036
  async cached(key, fetchToken) {
652
1037
  const hit = this.tokens.get(key);
653
1038
  if (hit && hit.expiresAt - this.refreshBefore * 1e3 > Date.now()) return hit;
@@ -718,7 +1103,7 @@ var Client = class extends Operations {
718
1103
  const timeout = AbortSignal.timeout(this.timeoutMs);
719
1104
  const signal = options.signal ? AbortSignal.any([options.signal, timeout]) : timeout;
720
1105
  const canRetry = attempt < this.maxRetries;
721
- const idempotent = IDEMPOTENT.has(method) || method === "POST" && init.retryPost !== false && !init.auth;
1106
+ const idempotent = IDEMPOTENT.has(method) || method === "POST" && (init.retryPost === true || init.retryPost !== false && !init.auth);
722
1107
  let res;
723
1108
  try {
724
1109
  res = await this.fetchFn(this.url(path, init.query), {
@@ -768,6 +1153,7 @@ var Client = class extends Operations {
768
1153
  }
769
1154
  }
770
1155
  };
1156
+ var sizeOf = (data) => data instanceof Uint8Array ? data.byteLength : data.size;
771
1157
  async function toError(res) {
772
1158
  let body = {};
773
1159
  try {
@@ -854,6 +1240,7 @@ var challengeResponse = (v) => ({ challenge: v.challenge });
854
1240
  SCOPE_ATTENDANCE_READONLY,
855
1241
  SCOPE_CLASS_MEMBER,
856
1242
  SCOPE_CLASS_READONLY,
1243
+ SCOPE_COMMUNITY_POST,
857
1244
  SCOPE_COURSE,
858
1245
  SCOPE_COURSEWORK,
859
1246
  SCOPE_EVENT,
@@ -861,12 +1248,18 @@ var challengeResponse = (v) => ({ challenge: v.challenge });
861
1248
  SCOPE_GRADE_READONLY,
862
1249
  SCOPE_IM_CHAT_READONLY,
863
1250
  SCOPE_IM_MESSAGE_SEND_AS_BOT,
1251
+ SCOPE_INSIGHTS_READONLY,
864
1252
  SCOPE_JOB,
865
1253
  SCOPE_JOB_CANDIDATE_READONLY,
866
1254
  SCOPE_LEARNING_PROGRESS_READONLY,
867
1255
  SCOPE_LIVE_RECORDING_READONLY,
1256
+ SCOPE_ME_LEARNING_READONLY,
1257
+ SCOPE_ME_SCHEDULE_READONLY,
1258
+ SCOPE_ME_WORDS,
1259
+ SCOPE_ME_WORDS_READONLY,
868
1260
  SCOPE_NOTIFY_SEND,
869
1261
  SCOPE_OPENID,
1262
+ SCOPE_OPENING_READONLY,
870
1263
  SCOPE_ORG_MEMBER,
871
1264
  SCOPE_ORG_MEMBER_READONLY,
872
1265
  SCOPE_PROFILE,
@@ -877,8 +1270,11 @@ var challengeResponse = (v) => ({ challenge: v.challenge });
877
1270
  SCOPE_REVIEW,
878
1271
  SCOPE_SCHEDULE,
879
1272
  SCOPE_SCHEDULE_READONLY,
1273
+ SCOPE_TRADE_BOOKING_READONLY,
880
1274
  SCOPE_TRADE_ORDER_READONLY,
881
1275
  SCOPE_TRIAL,
1276
+ SCOPE_VENUE,
1277
+ SCOPE_VENUE_READONLY,
882
1278
  VERSION,
883
1279
  challengeResponse,
884
1280
  decrypt,
package/dist/index.d.ts CHANGED
@@ -4,4 +4,4 @@ export { type CardAction, challengeResponse, decrypt, type EventEnvelope, isUrlV
4
4
  export * from './gen/meta.ts';
5
5
  export type { BodyOf, ItemOf, QueryOf, ResultOf } from './gen/operations.ts';
6
6
  export type { components, operations } from './gen/schema.ts';
7
- export type { CallSpec, ClientOptions, RequestOptions, TenantAccessToken, TokenType, UserAccessToken, } from './types.ts';
7
+ export type { CallSpec, ClientOptions, RequestOptions, TenantAccessToken, TokenType, UserAccessToken, WaitVideoOptions, } from './types.ts';