@happyvertical/smrt-education 0.60.4 → 0.60.6

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.
@@ -125,6 +125,15 @@ skipped) or `memoryClassCrunchLabsSource(files)`. It never reads video files.
125
125
  number), so `attachCourse` merges the parts in lesson order whatever order they
126
126
  arrive in.
127
127
 
128
+ - Lesson steps (#4656): the slides are step `launch` (order 0, "Launch"); each
129
+ matched video is a step keyed by its real step code (`6`, `1b`) with the
130
+ video's title as label, ordered numerically (`classCrunchLabsStepOrder`: `1` <
131
+ `1a` < `1b` < `2`; videos sharing a code share the step, offset by 0.001 each);
132
+ the status check assessment (printable PDF and the editable master) is step
133
+ `check` (order 9999, "Status check"). The mission log, handouts, printables, the
134
+ teacher manual and the assessment guidance carry no step: the first three are the
135
+ learner's setup, the others stay guardian material.
136
+
128
137
  The `report` lists lessons, members, parts, pages, matched and unmatched videos,
129
138
  duplicates, ignored files and warnings. Fixtures in tests are synthetic files
130
139
  with the same naming; no real download is ever copied into the repository.
@@ -221,10 +221,17 @@ the same way: an unknown key anywhere is `manifest_unknown_key` (with its path i
221
221
  once), `role` (required) one of `teacher-guide`, `slides`, `learner-pages`,
222
222
  `assessment`, `assessment-guidance`, `handout`, `printable`, `answer-key`,
223
223
  `attachment` (`manifest_role` otherwise; a video is a reference, never a file),
224
- optional `audience` and `title`. The audience is the role default
224
+ optional `audience`, `title` and (lesson members only) `step`. The audience is the role default
225
225
  (`LESSON_ASSET_DEFAULT_AUDIENCE`) unless stated; `answer-key` and
226
226
  `assessment-guidance` are always guardian (a learner audience is
227
227
  `manifest_audience`). Members not listed stay recorded evidence with no role.
228
+ - **Lesson steps** (SMRT #4656): a lesson member or video may declare
229
+ `step: { key, order, label? }` (a closed object; `manifest_invalid` for a key over
230
+ 32 characters or outside `A-Za-z0-9._:-`, a non-finite or non-number `order`
231
+ (|order| up to 1,000,000), or an empty label; `manifest_unknown_key` for anything
232
+ else, and a course member cannot carry one). Materials that share a `key` are
233
+ one step; fractions order `1b` after `1`. A video's `step` is still accepted as a
234
+ whole number (its rank); the object form also ranks it by `floor(order)`.
228
235
  - **Pages**: `{ member, from, to, label? }` ranges over a member of the same lesson
229
236
  (PDF, Word or PowerPoint; the rendered PDF for Office). Each page becomes a
230
237
  `LessonPage` keyed `p<n>`, labelled `<label> <n>` (default `Page <n>`), ordered by
@@ -232,7 +239,7 @@ the same way: an unknown key anywhere is `manifest_unknown_key` (with its path i
232
239
  500 pages per lesson.
233
240
  - **Videos**: `{ reference, title, step, audience? }` where `reference` is a bare id
234
241
  or an allowlisted https YouTube URL (`parseYouTubeReference`; anything else is
235
- `manifest_video`), `step` an integer 0-9999. They become `LessonExternalVideo`
242
+ `manifest_video`), `step` an integer 0-9999 or a lesson step object (see above). They become `LessonExternalVideo`
236
243
  rows; nothing is downloaded.
237
244
  - **Course members** become `CourseAsset` rows, not tied to any lesson.
238
245
  - **Unit and parts** (SMRT #4632): `course.key` (a manifest key) names the unit
@@ -260,7 +267,15 @@ manifest gave none or the member backs several materials with different titles.
260
267
  row), the same for the original and rendered halves of a pair. Lesson and course
261
268
  material views show the row title and fall back to the stored asset's name when
262
269
  there is none. A re-attach fills a missing title; a different one is a
263
- `conflict` unless `supersede` restates it. **Slides** keep the original
270
+ `conflict` unless `supersede` restates it. **Lesson steps** (#4656): the declared
271
+ step is recorded on the `lesson_assets` row (`step_key`, `step_order`, `step_label`;
272
+ both halves of an original/rendered pair share it) and on `lesson_external_videos`
273
+ (`step_key`, `step_position`, `step_label`; its existing integer `step_order` stays
274
+ the rank, `floor(order)` for an object step). All are additive nullable columns.
275
+ A re-attach fills a row's missing step without a conflict; a different step is a
276
+ `conflict` unless `supersede` restates it (replays are unchanged). Hosts read it
277
+ with `SubmissionService.listLessonSteps` ([lesson materials](lesson-materials.md)).
278
+ **Slides** keep the original
264
279
  deck (hand `bytes` to the host as a private asset: "Open in PowerPoint") and its
265
280
  rendered pages (`rendered`: "Present").
266
281
 
@@ -44,7 +44,7 @@ catalog projection without answer keys (every learner-facing catalog read) appli
44
44
  the same rule. `signLessonMaterial`/`signCourseMaterial` sign only a file in the
45
45
  actor's own list (claim scope `lesson:<id>` / `course:<id>`); a guessed id of
46
46
  guardian material is denied; `thumbnail: true` signs the thumbnail of a visible
47
- material instead. `signLessonPrintable` applies the same audience rule to the learner. Storage URIs never appear.
47
+ material instead. `signLessonPrintable` signs a lesson file for printing: a `printable`, `handout` or `learner-pages` row (handout and learner pages only when the stored asset is `application/pdf`), learner-audience for a learner and any audience for a guardian. A retired row, an `original` rendition (the editable file) and any other role, answer keys included, are refused (SMRT #4657). Storage URIs never appear.
48
48
 
49
49
  `setMaterialAudience({enrollmentId, scope, kind, materialId, audience})` lets a
50
50
  guardian with plan authority change a family-owned row within the role's bounds
@@ -54,6 +54,29 @@ family-owned, so this is the after-attach path; before attach use
54
54
  Authored courses change audience through course authoring (`CourseMaterialInput.audience`,
55
55
  carried to the released join); shared or other families' rows are denied.
56
56
 
57
+ ## Lesson steps (SMRT #4656)
58
+
59
+ `listLessonSteps({enrollmentId, lessonId})` walks a multi-step lesson in order,
60
+ filtered like `listLessonMaterials` (same actor and enrollment checks, learner
61
+ audience only for a learner, retired rows left out, same `LessonMaterialView`s):
62
+
63
+ ```ts
64
+ { setup: LessonMaterialView[]; // stepless printable / handout / learner-pages files
65
+ steps: Array<{ key: string; label: string; // label falls back to the key
66
+ kind: 'present' | 'watch' | 'do' | 'check';
67
+ order: number; // 1b sorts after 1
68
+ materials: LessonMaterialView[] }> }
69
+ ```
70
+
71
+ Steps come from the `step_key`/`step_order`/`step_label` recorded on lesson files and
72
+ (`step_position`) external videos by a course bundle ([manifest steps](course-bundle-import.md)).
73
+ Materials sharing a key are one step (the original and rendered halves of a deck stay
74
+ together); steps sort by order then key; `kind` is `present` if any material is
75
+ `slides`, else `watch` if any is an external video, else `check` if any is an
76
+ `assessment`, else `do`. A lesson with no declared steps returns `steps: []`.
77
+ Course-level materials (supply lists) are not part of a lesson listing and stay with
78
+ `listCourseMaterials`.
79
+
57
80
  ## External videos
58
81
 
59
82
  `LessonExternalVideo` references a video at an allowlisted provider (YouTube only):
@@ -637,6 +637,14 @@ function materialAudience(relationship, audience) {
637
637
  return effective;
638
638
  }
639
639
  var MATERIAL_RENDITIONS = ["original", "rendered"];
640
+ var LESSON_STEP_KEY_MAX = 32;
641
+ var LESSON_STEP_LABEL_MAX = 200;
642
+ var LESSON_STEP_ORDER_MAX = 1e6;
643
+ function checkStep(key, rawOrder, label) {
644
+ if (key === null && rawOrder === null && label === null) return;
645
+ const order = rawOrder === null ? null : Number(rawOrder);
646
+ if (typeof key !== "string" || !key.trim() || key.length > 32 || typeof order !== "number" || !Number.isFinite(order) || Math.abs(order) > 1e6 || label !== null && (typeof label !== "string" || label.length > 200)) throw new Error("Invalid lesson step");
647
+ }
640
648
  async function requirePrivateAsset(row, assetId = row.assetId) {
641
649
  const asset = await (await AssetCollection.create({ db: row.db })).get(assetId, { cache: false });
642
650
  if (!row.tenantId || !asset || asset.tenantId !== row.tenantId || asset.accessMode !== "private" || !asset.ownerProfileId) throw new Error("Private lesson material requires an authorized private asset");
@@ -681,6 +689,9 @@ var LessonAsset = class extends SmrtObject {
681
689
  title = null;
682
690
  retiredAt = null;
683
691
  sourceImportId = null;
692
+ stepKey = null;
693
+ stepOrder = null;
694
+ stepLabel = null;
684
695
  constructor(options = {}) {
685
696
  super(options);
686
697
  for (const [key, value] of Object.entries(options)) if (Object.hasOwn(this, key)) this[key] = value;
@@ -688,6 +699,7 @@ var LessonAsset = class extends SmrtObject {
688
699
  async save(options) {
689
700
  materialAudience(this.relationship, this.audience);
690
701
  if (this.title !== null && (typeof this.title !== "string" || this.title.length > 200)) throw new Error("Invalid lesson material title");
702
+ checkStep(this.stepKey, this.stepOrder, this.stepLabel);
691
703
  const lesson = await this.db.get("lessons", { id: this.lessonId });
692
704
  if (!lesson) throw new Error(`education lesson ${this.lessonId} does not exist`);
693
705
  if (!await this.db.get("lessons", {
@@ -725,6 +737,18 @@ __decorateClass$10([field({
725
737
  type: "text",
726
738
  nullable: true
727
739
  })], LessonAsset.prototype, "sourceImportId", 2);
740
+ __decorateClass$10([field({
741
+ type: "text",
742
+ nullable: true
743
+ })], LessonAsset.prototype, "stepKey", 2);
744
+ __decorateClass$10([field({
745
+ type: "decimal",
746
+ nullable: true
747
+ })], LessonAsset.prototype, "stepOrder", 2);
748
+ __decorateClass$10([field({
749
+ type: "text",
750
+ nullable: true
751
+ })], LessonAsset.prototype, "stepLabel", 2);
728
752
  LessonAsset = __decorateClass$10([TenantScoped({ mode: "optional" }), smrt({
729
753
  tableName: "lesson_assets",
730
754
  conflictColumns: [
@@ -840,6 +864,9 @@ var LessonExternalVideo = class extends SmrtObject {
840
864
  audience = "";
841
865
  retiredAt = null;
842
866
  sourceImportId = null;
867
+ stepKey = null;
868
+ stepPosition = null;
869
+ stepLabel = null;
843
870
  constructor(options = {}) {
844
871
  super(options);
845
872
  for (const [key, value] of Object.entries(options)) if (Object.hasOwn(this, key)) this[key] = value;
@@ -847,6 +874,7 @@ var LessonExternalVideo = class extends SmrtObject {
847
874
  async save(options) {
848
875
  materialAudience("video", this.audience);
849
876
  if (!EXTERNAL_VIDEO_PROVIDERS.includes(this.provider) || !YOUTUBE_ID.test(this.videoId) || typeof this.title !== "string" || !this.title.trim() || this.title.length > 200 || !Number.isSafeInteger(this.stepOrder) || this.stepOrder < 0) throw new Error("Invalid external video reference");
877
+ checkStep(this.stepKey, this.stepPosition, this.stepLabel);
850
878
  if (!await this.db.get("lessons", {
851
879
  id: this.lessonId,
852
880
  tenant_id: this.tenantId
@@ -865,6 +893,18 @@ __decorateClass$10([field({
865
893
  type: "text",
866
894
  nullable: true
867
895
  })], LessonExternalVideo.prototype, "sourceImportId", 2);
896
+ __decorateClass$10([field({
897
+ type: "text",
898
+ nullable: true
899
+ })], LessonExternalVideo.prototype, "stepKey", 2);
900
+ __decorateClass$10([field({
901
+ type: "decimal",
902
+ nullable: true
903
+ })], LessonExternalVideo.prototype, "stepPosition", 2);
904
+ __decorateClass$10([field({
905
+ type: "text",
906
+ nullable: true
907
+ })], LessonExternalVideo.prototype, "stepLabel", 2);
868
908
  LessonExternalVideo = __decorateClass$10([TenantScoped({ mode: "optional" }), smrt({
869
909
  tableName: "lesson_external_videos",
870
910
  conflictColumns: [
@@ -32467,6 +32507,21 @@ var ExternalVideoReferenceError = class extends LearnerValidationError {
32467
32507
  this.name = "ExternalVideoReferenceError";
32468
32508
  }
32469
32509
  };
32510
+ var PRINTABLE_RELATIONSHIPS = [
32511
+ "printable",
32512
+ "handout",
32513
+ "learner-pages"
32514
+ ];
32515
+ var stepOfRow = (row, orderField) => row.step_key ? {
32516
+ key: String(row.step_key),
32517
+ order: Number(row[orderField]) || 0,
32518
+ label: row.step_label ? String(row.step_label) : ""
32519
+ } : null;
32520
+ var SETUP_RELATIONSHIPS = /* @__PURE__ */ new Set([
32521
+ "printable",
32522
+ "handout",
32523
+ "learner-pages"
32524
+ ]);
32470
32525
  function pairRenditions(rows, views) {
32471
32526
  const out = views.map((v) => ({ ...v }));
32472
32527
  rows.forEach((row, i) => {
@@ -33333,6 +33388,9 @@ var SubmissionService = class SubmissionService extends LearnerService {
33333
33388
  * learner-audience material; keys and guidance never reach them.
33334
33389
  */
33335
33390
  async listLessonMaterials(request) {
33391
+ return (await this.collectLessonMaterials(request)).map((m) => m.view);
33392
+ }
33393
+ async collectLessonMaterials(request) {
33336
33394
  const enrollment = await this.requireEnrollment(request, "education.learner.read");
33337
33395
  if (!await this.lessonInScope(request, enrollment, request.lessonId)) throw new LearnerAuthorizationError();
33338
33396
  const lesson = await this.db.get("lessons", { id: request.lessonId });
@@ -33340,28 +33398,72 @@ var SubmissionService = class SubmissionService extends LearnerService {
33340
33398
  const tenant = lesson.tenant_id ?? null;
33341
33399
  const learnerOnly = await this.learnerAudienceOnly(request);
33342
33400
  const files = (await this.db.list("lesson_assets", { lesson_id: request.lessonId })).filter((row) => (row.tenant_id ?? null) === tenant && !row.retired_at && this.visibleTo(learnerOnly, String(row.relationship), String(row.audience ?? ""))).sort((a, b) => Number(a.sort_order) - Number(b.sort_order) || String(a.id).localeCompare(String(b.id)));
33343
- const videos = await this.db.tableExists("lesson_external_videos") ? (await this.db.list("lesson_external_videos", { lesson_id: request.lessonId })).filter((row) => (row.tenant_id ?? null) === tenant && !row.retired_at && this.visibleTo(learnerOnly, "video", String(row.audience ?? ""))).sort((a, b) => Number(a.step_order) - Number(b.step_order) || String(a.id).localeCompare(String(b.id))) : [];
33401
+ const videos = await this.db.tableExists("lesson_external_videos") ? (await this.db.list("lesson_external_videos", { lesson_id: request.lessonId })).filter((row) => (row.tenant_id ?? null) === tenant && !row.retired_at && this.visibleTo(learnerOnly, "video", String(row.audience ?? ""))).sort((a, b) => Number(a.step_order) - Number(b.step_order) || (Number(a.step_position ?? 0) || 0) - (Number(b.step_position ?? 0) || 0) || String(a.id).localeCompare(String(b.id))) : [];
33344
33402
  const fileViews = [];
33345
33403
  for (const row of files) fileViews.push(await this.fileView(row, "lesson"));
33346
33404
  const result = pairRenditions(files, fileViews);
33347
- for (const row of videos) result.push({
33348
- id: String(row.id),
33349
- kind: "external-video",
33350
- scope: "lesson",
33351
- relationship: "video",
33352
- audience: materialAudience("video", String(row.audience ?? "")),
33353
- title: String(row.title),
33354
- order: Number(row.step_order) || 0,
33355
- assetId: null,
33356
- provider: row.provider,
33357
- videoId: String(row.video_id),
33358
- embedUrl: externalVideoEmbedUrl(row.provider, String(row.video_id)),
33359
- mimeType: null,
33360
- rendition: null,
33361
- pairedMaterialId: null,
33362
- thumbnailAssetId: null
33363
- });
33364
- return result;
33405
+ const steps = files.map((row) => stepOfRow(row, "step_order"));
33406
+ for (const row of videos) {
33407
+ steps.push(stepOfRow(row, "step_position"));
33408
+ result.push({
33409
+ id: String(row.id),
33410
+ kind: "external-video",
33411
+ scope: "lesson",
33412
+ relationship: "video",
33413
+ audience: materialAudience("video", String(row.audience ?? "")),
33414
+ title: String(row.title),
33415
+ order: Number(row.step_order) || 0,
33416
+ assetId: null,
33417
+ provider: row.provider,
33418
+ videoId: String(row.video_id),
33419
+ embedUrl: externalVideoEmbedUrl(row.provider, String(row.video_id)),
33420
+ mimeType: null,
33421
+ rendition: null,
33422
+ pairedMaterialId: null,
33423
+ thumbnailAssetId: null
33424
+ });
33425
+ }
33426
+ return result.map((view, i) => ({
33427
+ view,
33428
+ step: steps[i] ?? null
33429
+ }));
33430
+ }
33431
+ /**
33432
+ * A lesson walked in order (SMRT #4656), audience-filtered exactly like
33433
+ * `listLessonMaterials`: stepless handouts, learner pages and printables as
33434
+ * `setup`, then the declared steps. A step groups every material sharing its
33435
+ * key (the original and rendered halves of a deck stay together). A lesson
33436
+ * with no declared steps returns `steps: []`. Course-level supply lists are
33437
+ * not part of a lesson's listing and stay with `listCourseMaterials`.
33438
+ */
33439
+ async listLessonSteps(request) {
33440
+ const collected = await this.collectLessonMaterials(request);
33441
+ const setup = collected.filter((m) => !m.step && m.view.kind === "file" && SETUP_RELATIONSHIPS.has(m.view.relationship)).map((m) => m.view);
33442
+ const groups = /* @__PURE__ */ new Map();
33443
+ for (const { view, step } of collected) {
33444
+ if (!step) continue;
33445
+ const group = groups.get(step.key) ?? {
33446
+ key: step.key,
33447
+ order: step.order,
33448
+ label: "",
33449
+ materials: []
33450
+ };
33451
+ group.order = Math.min(group.order, step.order);
33452
+ group.label = group.label || step.label;
33453
+ group.materials.push(view);
33454
+ groups.set(step.key, group);
33455
+ }
33456
+ const kindOf = (materials) => materials.some((m) => m.relationship === "slides") ? "present" : materials.some((m) => m.kind === "external-video") ? "watch" : materials.some((m) => m.relationship === "assessment") ? "check" : "do";
33457
+ return {
33458
+ setup,
33459
+ steps: [...groups.values()].sort((a, b) => a.order - b.order || (a.key < b.key ? -1 : a.key > b.key ? 1 : 0)).map((g) => ({
33460
+ key: g.key,
33461
+ label: g.label || g.key,
33462
+ kind: kindOf(g.materials),
33463
+ order: g.order,
33464
+ materials: g.materials
33465
+ }))
33466
+ };
33365
33467
  }
33366
33468
  /** Course-level materials (supply lists, posters) under the same audience rule. */
33367
33469
  async listCourseMaterials(request) {
@@ -33531,16 +33633,30 @@ var SubmissionService = class SubmissionService extends LearnerService {
33531
33633
  return view;
33532
33634
  });
33533
33635
  }
33636
+ /**
33637
+ * Sign a lesson file for printing: a `printable`, `handout` or `learner-pages`
33638
+ * PDF the actor may see (learner-audience, or any for a guardian). Never an
33639
+ * editable `original` rendition and never an answer key.
33640
+ */
33534
33641
  async signLessonPrintable(request) {
33535
33642
  const enrollment = await this.requireEnrollment(request, "education.learner.read");
33536
33643
  if (!await this.lessonInScope(request, enrollment, request.lessonId)) throw new LearnerAuthorizationError();
33537
- const link = await (await LessonAssetCollection.create({ db: this.db })).get({
33538
- tenantId: request.tenantId,
33539
- lessonId: request.lessonId,
33540
- assetId: request.assetId,
33541
- relationship: "printable"
33542
- }, { cache: false });
33543
- if (!link || !this.options.signAsset) throw new LearnerAuthorizationError();
33644
+ if (!this.options.signAsset) throw new LearnerAuthorizationError();
33645
+ const links = await LessonAssetCollection.create({ db: this.db });
33646
+ let link = null;
33647
+ for (const relationship of PRINTABLE_RELATIONSHIPS) {
33648
+ link = await links.get({
33649
+ tenantId: request.tenantId,
33650
+ lessonId: request.lessonId,
33651
+ assetId: request.assetId,
33652
+ relationship
33653
+ }, { cache: false });
33654
+ if (link?.retiredAt) link = null;
33655
+ if (link) break;
33656
+ }
33657
+ if (!link) throw new LearnerAuthorizationError();
33658
+ if (link.rendition === "original") throw new LearnerAuthorizationError();
33659
+ if (link.relationship !== "printable" && (await this.materialAsset(request.assetId)).mimeType !== "application/pdf") throw new LearnerAuthorizationError();
33544
33660
  if (await this.learnerAudienceOnly(request) && materialAudience(link.relationship, link.audience) !== "learner") throw new LearnerAuthorizationError();
33545
33661
  const expiresAt = new Date(Date.now() + 300 * 1e3);
33546
33662
  return {
@@ -36974,6 +37090,6 @@ var WorkbookPhotoService = class WorkbookPhotoService extends LessonWorkService
36974
37090
  }
36975
37091
  };
36976
37092
  //#endregion
36977
- export { LessonAlreadyCompleteError as $, Curriculum as $i, QUESTION_COMPLETION_MODES as $n, ReleasedLesson as $r, WorkFeedbackRelease as $t, QUESTION_EXTRACTION_VERSION as A, Attestation as Ai, QuestionRevision as An, CourseCoverChangeCollection as Ar, WorkbookPhotoDecision as At, SELF_ATTESTABLE_LESSON_KINDS as B, CourseAsset as Bi, normalizeQuestionContent as Bn, CourseUpgradeChoiceCollection as Br, WorkCompletionDecision as Bt, MAX_EXTRACTED_OMISSIONS as C, ReadingCollection as Ci, LearningObjectiveCollection as Cn, MAX_REFUSAL_EXPLANATION_LENGTH as Cr, canonicalGroupSharingPolicy as Ct, MAX_EXTRACTED_STUDENT_WORK as D, SubmissionIntakeBindingCollection as Di, QuestionCollection as Dn, CourseArchiveDecision as Dr, BookContextRevisionCollection as Dt, MAX_EXTRACTED_SECTIONS as E, SubmissionCollection as Ei, Question as En, readDelegation as Er, BookContextRevision as Et, normalizeWorkObservedLayout as F, Submission as Fi, resolveEnrollmentLessons as Fn, CourseRelease as Fr, FamilyIntakeAsset as Ft, EnrollmentNotActiveError as G, LESSON_ASSET_RELATIONSHIPS as Gi, MAX_PRINTABLE_ITEMS as Gn, EnrollmentAssignmentRecord as Gr, LessonWorkFiling as Gt, SubmissionStorageCleanupError as H, EXTERNAL_VIDEO_PROVIDERS as Hi, GENERATED_PRINTABLE_RELATIONSHIPS as Hn, CourseVisibilityChangeCollection as Hr, readWorkCompletion as Ht, normalizeWorkAnswerRegions as I, SubmissionAsset as Ii, applyProposalFields as In, CourseReleaseCollection as Ir, FamilyIntakeAssetCollection as It, InterestThemeError as J, LessonExternalVideoCollection as Ji, MAX_PRINTABLE_TOTAL_TEXT as Jn, EnrollmentAutoUpgradeSkipCollection as Jr, WorkAssessmentRevisionCollection as Jt, EnrollmentPaceError as K, LessonAsset as Ki, MAX_PRINTABLE_PAGES as Kn, EnrollmentAssignmentRecordCollection as Kr, LessonWorkFilingCollection as Kt, LessonWorkWorkflowRequiredError as L, SubmissionIntakeBinding as Li, draftFromRevision as Ln, CourseRevision as Lr, FamilyIntakeCollection as Lt, QuestionExtractionPendingError as M, Grade as Mi, CourseAuthoringService as Mn, CourseDiscardTombstoneCollection as Mr, WorkbookPhotoIntake as Mt, QuestionExtractionValidationError as N, LessonSelfAttestation as Ni, authoredVisibilityPermitsGroups as Nn, CourseDraftHead as Nr, WorkbookPhotoIntakeCollection as Nt, MAX_EXTRACTED_TEXT as O, VideoCollection as Oi, QuestionResponse as On, CourseArchiveDecisionCollection as Or, WorkbookPhotoAsset as Ot, LessonWorkService as P, ScannedSubmission as Pi, effectiveCourseVisibility as Pn, CourseDraftHeadCollection as Pr, FamilyIntake as Pt, LearnerValidationError as Q, parseYouTubeReference as Qi, PrintableStorageNotConfiguredError as Qn, LessonRevisionCollection as Qr, WorkFeedbackDeliveryCollection as Qt, OwnAttestationConflictError as R, parseQuestionRubric as Ri, planProposal as Rn, CourseRevisionCollection as Rr, FamilyIntakeDecision as Rt, MAX_EXTRACTED_ANCHORS as S, LessonSelfAttestationCollection as Si, LearningObjective as Sn, MAX_REFUSAL_CATEGORY_LENGTH as Sr, GroupSharingPolicyValidationError as St, MAX_EXTRACTED_QUESTIONS as T, SubmissionAssetCollection as Ti, LearningObjectiveRevisionCollection as Tn, delegationRecordFields as Tr, familyIntakeScopeId as Tt, submissionPageKey as U, GUARDIAN_ONLY_RELATIONSHIPS as Ui, LearningObjectiveKeyConflictError as Un, CurriculumCreationRecord as Ur, LessonActivity as Ut, SubmissionService as V, CourseAssetCollection as Vi, GENERATED_PRINTABLE_AUDIENCES as Vn, CourseVisibilityChange as Vr, WorkCompletionDecisionCollection as Vt, ENROLLMENT_STATUS_TRANSITIONS as W, LESSON_ASSET_DEFAULT_AUDIENCE as Wi, LearningObjectiveNotFoundError as Wn, CurriculumCreationRecordCollection as Wr, LessonActivityCollection as Wt, LearnerHistoryUnavailableError as X, externalVideoEmbedUrl as Xi, PrintableOwnerError as Xn, EnrollmentReleasePinCollection as Xr, WorkAttemptCollection as Xt, LearnerAuthorizationError as Y, MATERIAL_RENDITIONS as Yi, MAX_RESOLVE_QUESTION_REVISIONS as Yn, EnrollmentReleasePin as Yr, WorkAttempt as Yt, LearnerService as Z, materialAudience as Zi, PrintableStorageCleanupError as Zn, LessonRevision as Zr, WorkFeedbackDelivery as Zt, EXTRACTED_OMISSION_REASONS as _, GradeCollection as _i, renderGuardianWorksheetPdf as _n, CourseProposalFieldError as _r, LessonProgressTransitionCollection as _t, questionExtractionProposalDigest as a, interestThemeFromColumns as aa, mostRestrictiveLicense as ai, aggregateWorkQuestions as an, QuestionReferenceError as ar, MIN_LESSONS_PER_WEEK as at, EXTRACTED_QUESTION_STATUSES as b, LessonCollection as bi, GeneratedPrintable as bn, MAX_GENERATION_GRADE as br, GroupSharingAuthorizationError as bt, QuestionExtractionRecord as c, COURSE_VISIBILITIES as ca, AttestationCollection as ci, LessonPage as cn, COURSE_GENERATION_STATUSES as cr, EnrollmentPaceChange as ct, extractedQuestionContent as d, Exercise as da, CourseSectionCollection as di, validLessonPageKey as dn, CourseGenerationBriefError as dr, EnrollmentStatusChangeCollection as dt, Enrollment as ea, ReleasedLessonCollection as ei, WorkFeedbackReleaseCollection as en, QUESTION_FIELDS as er, LessonPutBackError as et, matchExtractedQuestions as f, ExternalCourse as fa, CurriculumCollection as fi, QuestionAuthoringService as fn, CourseGenerationInProgressError as fr, LearnerInterestThemeChange as ft, EXTRACTED_MEDIA_KINDS as g, Worksheet as ga, ExternalCourseCollection as gi, worksheetVersionId as gn, CourseGeneratorNotConfiguredError as gr, LessonProgressTransition as gt, EXTRACTED_LEGIBILITY as h, Video as ha, ExternalCompletionCollection as hi, canonical$2 as hn, CourseGenerationRefusal as hr, LessonCompletionRecordCollection as ht, QuestionExtractionService as i, MAX_INTEREST_THEME_LABEL_CHARS as ia, isGroupShareableLicense as ii, WorkCompletionConflictError as in, QuestionNotFoundError as ir, MAX_NEXT_LESSONS_COUNT as it, QuestionExtractionNotFoundError as j, ExternalCompletion as ji, QuestionRevisionCollection as jn, CourseDiscardTombstone as jr, WorkbookPhotoDecisionCollection as jt, QUESTION_EXTRACTION_SOURCE_KINDS as k, WorksheetCollection as ki, QuestionResponseCollection as kn, CourseCoverChange as kr, WorkbookPhotoAssetCollection as kt, QuestionExtractionRecordCollection as l, Course as la, AuthoredCourseCollection as li, LessonPageCollection as ln, COURSE_GENERATION_TARGETS as lr, EnrollmentPaceChangeCollection as lt, questionExtractionSourceIdentity as m, Reading as ma, ExerciseCollection as mi, buildLearnerWorksheet as mn, CourseGenerationOutputError as mr, LessonCompletionRecord as mt, preservePhotoProposal as n, LessonProgress as na, AUTHORED_RELEASE_SOURCE_KEY as ni, WORK_MATH_VOCABULARY as nn, QUESTION_PROVENANCES as nr, LessonWorkOwnedError as nt, ExtractedQuestionLink as o, parseInterestTheme as oa, CatalogAuthorizationError as oi, validateWorkAssessment as on, WorksheetVersionMismatchError as or, enrollmentView as ot, normalizeQuestionExtraction as p, Lesson as pa, EnrollmentCollection as pi, buildGuardianWorksheet as pn, CourseGenerationNotFoundError as pr, LearnerInterestThemeChangeCollection as pt, EnrollmentStatusTransitionError as q, LessonExternalVideo as qi, MAX_PRINTABLE_TEXT_LENGTH as qn, EnrollmentAutoUpgradeSkip as qr, WorkAssessmentRevision as qt, validateWorkbookPhotoIdentification as r, MAX_INTEREST_THEME_KEY_CHARS as ra, isAuthoredReleaseGroupShareable as ri, WorkAssessmentValidationError as rn, QUESTION_RESPONSE_TYPES as rr, MAX_LESSONS_PER_WEEK as rt, ExtractedQuestionLinkCollection as s, AuthoredCourse as sa, CatalogService as si, workAggregateFeedback as sn, groupSharingDigest as sr, requirePolicyBasis as st, WorkbookPhotoService as t, Learner as ta, AUTHORED_IDENTITY_SOURCE_KEY as ti, LessonWorkPlacementError as tn, QUESTION_PROMPT_SUMMARY_LENGTH as tr, LessonSkippedError as tt, QUESTION_EXTRACTION_ARGS_SCHEMA as u, CourseSection as ua, CourseCollection as ui, lessonPageView as un, COURSE_GENERATION_TERMINAL as ur, EnrollmentStatusChange as ut, EXTRACTED_ORIGINS as v, LearnerCollection as vi, renderLearnerWorksheetPdf as vn, CourseProposalNotFoundError as vr, evaluateGroupSharingPolicy as vt, MAX_EXTRACTED_PAGES as w, ScannedSubmissionCollection as wi, LearningObjectiveRevision as wn, delegationActorFields as wr, validateGroupSharingPolicy as wt, EXTRACTED_READABILITY as x, LessonProgressCollection as xi, GeneratedPrintableCollection as xn, MAX_GENERATION_OBJECTIVE_CHARS as xr, GroupSharingConflictError as xt, EXTRACTED_PAGE_ROLES as y, LessonAssetCollection as yi, es_exports as yn, MAX_GENERATION_BRIEF_FIELD_CHARS as yr, summarizeWorkEvidence as yt, OwnAttestationLessonKindError as z, validateRubricResults as zi, validateCourseGenerationOutput as zn, CourseUpgradeChoice as zr, FamilyIntakeDecisionCollection as zt };
37093
+ export { LessonAlreadyCompleteError as $, externalVideoEmbedUrl as $i, QUESTION_COMPLETION_MODES as $n, ReleasedLesson as $r, WorkFeedbackRelease as $t, QUESTION_EXTRACTION_VERSION as A, Attestation as Ai, QuestionRevision as An, CourseCoverChangeCollection as Ar, WorkbookPhotoDecision as At, SELF_ATTESTABLE_LESSON_KINDS as B, CourseAsset as Bi, normalizeQuestionContent as Bn, CourseUpgradeChoiceCollection as Br, WorkCompletionDecision as Bt, MAX_EXTRACTED_OMISSIONS as C, ReadingCollection as Ci, LearningObjectiveCollection as Cn, MAX_REFUSAL_EXPLANATION_LENGTH as Cr, canonicalGroupSharingPolicy as Ct, MAX_EXTRACTED_STUDENT_WORK as D, SubmissionIntakeBindingCollection as Di, QuestionCollection as Dn, CourseArchiveDecision as Dr, BookContextRevisionCollection as Dt, MAX_EXTRACTED_SECTIONS as E, SubmissionCollection as Ei, Question as En, readDelegation as Er, BookContextRevision as Et, normalizeWorkObservedLayout as F, Submission as Fi, resolveEnrollmentLessons as Fn, CourseRelease as Fr, FamilyIntakeAsset as Ft, EnrollmentNotActiveError as G, LESSON_ASSET_RELATIONSHIPS as Gi, MAX_PRINTABLE_ITEMS as Gn, EnrollmentAssignmentRecord as Gr, LessonWorkFiling as Gt, SubmissionStorageCleanupError as H, EXTERNAL_VIDEO_PROVIDERS as Hi, GENERATED_PRINTABLE_RELATIONSHIPS as Hn, CourseVisibilityChangeCollection as Hr, readWorkCompletion as Ht, normalizeWorkAnswerRegions as I, SubmissionAsset as Ii, applyProposalFields as In, CourseReleaseCollection as Ir, FamilyIntakeAssetCollection as It, InterestThemeError as J, LESSON_STEP_ORDER_MAX as Ji, MAX_PRINTABLE_TOTAL_TEXT as Jn, EnrollmentAutoUpgradeSkipCollection as Jr, WorkAssessmentRevisionCollection as Jt, EnrollmentPaceError as K, LESSON_STEP_KEY_MAX as Ki, MAX_PRINTABLE_PAGES as Kn, EnrollmentAssignmentRecordCollection as Kr, LessonWorkFilingCollection as Kt, LessonWorkWorkflowRequiredError as L, SubmissionIntakeBinding as Li, draftFromRevision as Ln, CourseRevision as Lr, FamilyIntakeCollection as Lt, QuestionExtractionPendingError as M, Grade as Mi, CourseAuthoringService as Mn, CourseDiscardTombstoneCollection as Mr, WorkbookPhotoIntake as Mt, QuestionExtractionValidationError as N, LessonSelfAttestation as Ni, authoredVisibilityPermitsGroups as Nn, CourseDraftHead as Nr, WorkbookPhotoIntakeCollection as Nt, MAX_EXTRACTED_TEXT as O, VideoCollection as Oi, QuestionResponse as On, CourseArchiveDecisionCollection as Or, WorkbookPhotoAsset as Ot, LessonWorkService as P, ScannedSubmission as Pi, effectiveCourseVisibility as Pn, CourseDraftHeadCollection as Pr, FamilyIntake as Pt, LearnerValidationError as Q, MATERIAL_RENDITIONS as Qi, PrintableStorageNotConfiguredError as Qn, LessonRevisionCollection as Qr, WorkFeedbackDeliveryCollection as Qt, OwnAttestationConflictError as R, parseQuestionRubric as Ri, planProposal as Rn, CourseRevisionCollection as Rr, FamilyIntakeDecision as Rt, MAX_EXTRACTED_ANCHORS as S, LessonSelfAttestationCollection as Si, LearningObjective as Sn, MAX_REFUSAL_CATEGORY_LENGTH as Sr, GroupSharingPolicyValidationError as St, MAX_EXTRACTED_QUESTIONS as T, SubmissionAssetCollection as Ti, LearningObjectiveRevisionCollection as Tn, delegationRecordFields as Tr, familyIntakeScopeId as Tt, submissionPageKey as U, GUARDIAN_ONLY_RELATIONSHIPS as Ui, LearningObjectiveKeyConflictError as Un, CurriculumCreationRecord as Ur, LessonActivity as Ut, SubmissionService as V, CourseAssetCollection as Vi, GENERATED_PRINTABLE_AUDIENCES as Vn, CourseVisibilityChange as Vr, WorkCompletionDecisionCollection as Vt, ENROLLMENT_STATUS_TRANSITIONS as W, LESSON_ASSET_DEFAULT_AUDIENCE as Wi, LearningObjectiveNotFoundError as Wn, CurriculumCreationRecordCollection as Wr, LessonActivityCollection as Wt, LearnerHistoryUnavailableError as X, LessonExternalVideo as Xi, PrintableOwnerError as Xn, EnrollmentReleasePinCollection as Xr, WorkAttemptCollection as Xt, LearnerAuthorizationError as Y, LessonAsset as Yi, MAX_RESOLVE_QUESTION_REVISIONS as Yn, EnrollmentReleasePin as Yr, WorkAttempt as Yt, LearnerService as Z, LessonExternalVideoCollection as Zi, PrintableStorageCleanupError as Zn, LessonRevision as Zr, WorkFeedbackDelivery as Zt, EXTRACTED_OMISSION_REASONS as _, Reading as _a, GradeCollection as _i, renderGuardianWorksheetPdf as _n, CourseProposalFieldError as _r, LessonProgressTransitionCollection as _t, questionExtractionProposalDigest as a, LessonProgress as aa, mostRestrictiveLicense as ai, aggregateWorkQuestions as an, QuestionReferenceError as ar, MIN_LESSONS_PER_WEEK as at, EXTRACTED_QUESTION_STATUSES as b, LessonCollection as bi, GeneratedPrintable as bn, MAX_GENERATION_GRADE as br, GroupSharingAuthorizationError as bt, QuestionExtractionRecord as c, interestThemeFromColumns as ca, AttestationCollection as ci, LessonPage as cn, COURSE_GENERATION_STATUSES as cr, EnrollmentPaceChange as ct, extractedQuestionContent as d, COURSE_VISIBILITIES as da, CourseSectionCollection as di, validLessonPageKey as dn, CourseGenerationBriefError as dr, EnrollmentStatusChangeCollection as dt, materialAudience as ea, ReleasedLessonCollection as ei, WorkFeedbackReleaseCollection as en, QUESTION_FIELDS as er, LessonPutBackError as et, matchExtractedQuestions as f, Course as fa, CurriculumCollection as fi, QuestionAuthoringService as fn, CourseGenerationInProgressError as fr, LearnerInterestThemeChange as ft, EXTRACTED_MEDIA_KINDS as g, Lesson as ga, ExternalCourseCollection as gi, worksheetVersionId as gn, CourseGeneratorNotConfiguredError as gr, LessonProgressTransition as gt, EXTRACTED_LEGIBILITY as h, ExternalCourse as ha, ExternalCompletionCollection as hi, canonical$2 as hn, CourseGenerationRefusal as hr, LessonCompletionRecordCollection as ht, QuestionExtractionService as i, Learner as ia, isGroupShareableLicense as ii, WorkCompletionConflictError as in, QuestionNotFoundError as ir, MAX_NEXT_LESSONS_COUNT as it, QuestionExtractionNotFoundError as j, ExternalCompletion as ji, QuestionRevisionCollection as jn, CourseDiscardTombstone as jr, WorkbookPhotoDecisionCollection as jt, QUESTION_EXTRACTION_SOURCE_KINDS as k, WorksheetCollection as ki, QuestionResponseCollection as kn, CourseCoverChange as kr, WorkbookPhotoAssetCollection as kt, QuestionExtractionRecordCollection as l, parseInterestTheme as la, AuthoredCourseCollection as li, LessonPageCollection as ln, COURSE_GENERATION_TARGETS as lr, EnrollmentPaceChangeCollection as lt, questionExtractionSourceIdentity as m, Exercise as ma, ExerciseCollection as mi, buildLearnerWorksheet as mn, CourseGenerationOutputError as mr, LessonCompletionRecord as mt, preservePhotoProposal as n, Curriculum as na, AUTHORED_RELEASE_SOURCE_KEY as ni, WORK_MATH_VOCABULARY as nn, QUESTION_PROVENANCES as nr, LessonWorkOwnedError as nt, ExtractedQuestionLink as o, MAX_INTEREST_THEME_KEY_CHARS as oa, CatalogAuthorizationError as oi, validateWorkAssessment as on, WorksheetVersionMismatchError as or, enrollmentView as ot, normalizeQuestionExtraction as p, CourseSection as pa, EnrollmentCollection as pi, buildGuardianWorksheet as pn, CourseGenerationNotFoundError as pr, LearnerInterestThemeChangeCollection as pt, EnrollmentStatusTransitionError as q, LESSON_STEP_LABEL_MAX as qi, MAX_PRINTABLE_TEXT_LENGTH as qn, EnrollmentAutoUpgradeSkip as qr, WorkAssessmentRevision as qt, validateWorkbookPhotoIdentification as r, Enrollment as ra, isAuthoredReleaseGroupShareable as ri, WorkAssessmentValidationError as rn, QUESTION_RESPONSE_TYPES as rr, MAX_LESSONS_PER_WEEK as rt, ExtractedQuestionLinkCollection as s, MAX_INTEREST_THEME_LABEL_CHARS as sa, CatalogService as si, workAggregateFeedback as sn, groupSharingDigest as sr, requirePolicyBasis as st, WorkbookPhotoService as t, parseYouTubeReference as ta, AUTHORED_IDENTITY_SOURCE_KEY as ti, LessonWorkPlacementError as tn, QUESTION_PROMPT_SUMMARY_LENGTH as tr, LessonSkippedError as tt, QUESTION_EXTRACTION_ARGS_SCHEMA as u, AuthoredCourse as ua, CourseCollection as ui, lessonPageView as un, COURSE_GENERATION_TERMINAL as ur, EnrollmentStatusChange as ut, EXTRACTED_ORIGINS as v, Video as va, LearnerCollection as vi, renderLearnerWorksheetPdf as vn, CourseProposalNotFoundError as vr, evaluateGroupSharingPolicy as vt, MAX_EXTRACTED_PAGES as w, ScannedSubmissionCollection as wi, LearningObjectiveRevision as wn, delegationActorFields as wr, validateGroupSharingPolicy as wt, EXTRACTED_READABILITY as x, LessonProgressCollection as xi, GeneratedPrintableCollection as xn, MAX_GENERATION_OBJECTIVE_CHARS as xr, GroupSharingConflictError as xt, EXTRACTED_PAGE_ROLES as y, Worksheet as ya, LessonAssetCollection as yi, es_exports as yn, MAX_GENERATION_BRIEF_FIELD_CHARS as yr, summarizeWorkEvidence as yt, OwnAttestationLessonKindError as z, validateRubricResults as zi, validateCourseGenerationOutput as zn, CourseUpgradeChoice as zr, FamilyIntakeDecisionCollection as zt };
36978
37094
 
36979
- //# sourceMappingURL=workbook-photo-service-wqZZWGLb.js.map
37095
+ //# sourceMappingURL=workbook-photo-service-DEb2LSC0.js.map