@instructure/platform-modules 0.3.0 → 0.4.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.
Files changed (73) hide show
  1. package/dist/adapters/canvasGraphql/index.d.ts +7 -33
  2. package/dist/adapters/canvasGraphql/index.d.ts.map +1 -1
  3. package/dist/adapters/canvasGraphql/normalize.d.ts +44 -186
  4. package/dist/adapters/canvasGraphql/normalize.d.ts.map +1 -1
  5. package/dist/adapters/canvasGraphql/queries.d.ts +8 -36
  6. package/dist/adapters/canvasGraphql/queries.d.ts.map +1 -1
  7. package/dist/adapters/inMemory.d.ts +1 -25
  8. package/dist/adapters/inMemory.d.ts.map +1 -1
  9. package/dist/adapters/limits.d.ts +17 -74
  10. package/dist/adapters/limits.d.ts.map +1 -1
  11. package/dist/adapters/types.d.ts +10 -40
  12. package/dist/adapters/types.d.ts.map +1 -1
  13. package/dist/cache/invalidate.d.ts +0 -23
  14. package/dist/cache/invalidate.d.ts.map +1 -1
  15. package/dist/cache/keys.d.ts +2 -43
  16. package/dist/cache/keys.d.ts.map +1 -1
  17. package/dist/components/FilterChips.d.ts +4 -10
  18. package/dist/components/FilterChips.d.ts.map +1 -1
  19. package/dist/components/ModuleCard.d.ts +10 -25
  20. package/dist/components/ModuleCard.d.ts.map +1 -1
  21. package/dist/components/ModuleCard.stories.d.ts +15 -0
  22. package/dist/components/ModuleCard.stories.d.ts.map +1 -1
  23. package/dist/components/ModuleItemRow.d.ts.map +1 -1
  24. package/dist/components/ModuleNumberBadge.d.ts.map +1 -1
  25. package/dist/components/ModulesList.d.ts +2 -23
  26. package/dist/components/ModulesList.d.ts.map +1 -1
  27. package/dist/components/ModulesList.stories.d.ts +22 -81
  28. package/dist/components/ModulesList.stories.d.ts.map +1 -1
  29. package/dist/components/StatusPill.d.ts +0 -3
  30. package/dist/components/StatusPill.d.ts.map +1 -1
  31. package/dist/components/index.d.ts +1 -1
  32. package/dist/components/index.d.ts.map +1 -1
  33. package/dist/components/labels.d.ts +26 -43
  34. package/dist/components/labels.d.ts.map +1 -1
  35. package/dist/config/context.d.ts +0 -1
  36. package/dist/config/context.d.ts.map +1 -1
  37. package/dist/config/registry.d.ts +5 -13
  38. package/dist/config/registry.d.ts.map +1 -1
  39. package/dist/config/types.d.ts +0 -3
  40. package/dist/config/types.d.ts.map +1 -1
  41. package/dist/fixtures/generate.d.ts +6 -17
  42. package/dist/fixtures/generate.d.ts.map +1 -1
  43. package/dist/fixtures/index.d.ts +1 -1
  44. package/dist/fixtures/index.d.ts.map +1 -1
  45. package/dist/fixtures/scenarios.d.ts +7 -32
  46. package/dist/fixtures/scenarios.d.ts.map +1 -1
  47. package/dist/hooks/constants.d.ts +4 -21
  48. package/dist/hooks/constants.d.ts.map +1 -1
  49. package/dist/hooks/queryOptions.d.ts +3 -24
  50. package/dist/hooks/queryOptions.d.ts.map +1 -1
  51. package/dist/hooks/useItemsMode.d.ts +3 -52
  52. package/dist/hooks/useItemsMode.d.ts.map +1 -1
  53. package/dist/hooks/useModuleItemQueries.d.ts +0 -38
  54. package/dist/hooks/useModuleItemQueries.d.ts.map +1 -1
  55. package/dist/hooks/useModulesPageData.d.ts +3 -29
  56. package/dist/hooks/useModulesPageData.d.ts.map +1 -1
  57. package/dist/i18n.d.ts +13 -20
  58. package/dist/i18n.d.ts.map +1 -1
  59. package/dist/index.d.ts +3 -10
  60. package/dist/index.d.ts.map +1 -1
  61. package/dist/index.js +1223 -1076
  62. package/dist/types/domain.d.ts +25 -80
  63. package/dist/types/domain.d.ts.map +1 -1
  64. package/dist/types/enums.d.ts +10 -41
  65. package/dist/types/enums.d.ts.map +1 -1
  66. package/dist/types/extensions.d.ts +0 -21
  67. package/dist/types/extensions.d.ts.map +1 -1
  68. package/dist/types/index.d.ts +2 -2
  69. package/dist/types/index.d.ts.map +1 -1
  70. package/dist/types/schemas.d.ts +5 -26
  71. package/dist/types/schemas.d.ts.map +1 -1
  72. package/locales/en.json +7 -7
  73. package/package.json +22 -19
@@ -1,14 +1,5 @@
1
1
  import { ModulesDataAdapter } from '../types';
2
2
  import { UnsupportedModuleItem } from './normalize';
3
- /**
4
- * The Canvas GraphQL implementation of `ModulesDataAdapter`.
5
- *
6
- * `executeQuery` is injected rather than pulled from `usePlatformUi()`. Two
7
- * reasons, both load-bearing: the adapter stays a plain function that a test
8
- * can drive with `vi.fn()` and no React, and it stays host-agnostic — Canvas
9
- * Career supplies a different `executeQuery` (different endpoint, different
10
- * auth) for the identical documents.
11
- */
12
3
  export type ExecuteQuery = <TResult, TVariables>(query: unknown, variables: TVariables) => Promise<TResult>;
13
4
  export { INLINE_ITEM_LIMIT, INLINE_QUERY_NODE_BUDGET } from '../limits';
14
5
  export interface CanvasGraphqlAdapterOptions {
@@ -17,35 +8,18 @@ export interface CanvasGraphqlAdapterOptions {
17
8
  inlineItemLimit?: number;
18
9
  /** @see INLINE_QUERY_NODE_BUDGET */
19
10
  nodeBudget?: number;
20
- /**
21
- * Notified when an item is dropped for having a content type outside the
22
- * closed domain enum. Unset by default: dropping is the deliberate policy,
23
- * this is the seam for making it visible.
24
- */
11
+ /** Unset by default: dropping is the policy, this is the seam for seeing it. */
25
12
  onUnsupportedItem?: (item: UnsupportedModuleItem) => void;
26
13
  /**
27
- * The student whose submissions to select, when there is one.
28
- *
29
- * Omit for a teacher, an admin, an observer, or any viewer without a single
30
- * subject. Omitting it is *safe by construction*: the submission field set is
31
- * then not requested at all, so `ModuleItem.status` comes back undefined —
32
- * already the contract's "not fetched" — rather than filled with somebody
33
- * else's data.
14
+ * Omit for any viewer without a single subject. Omitting is safe by construction: the
15
+ * submission field set is then not requested, so no status can be filled with somebody
16
+ * else's data. `progression` is still fetched, so a student viewer without an id gets a
17
+ * status derived from requirements alone which is why `complete` is withheld there.
34
18
  *
35
- * Supplying it is what makes per-item status available, and it must be the
36
- * viewer's own id. Canvas's `submissionsConnection` returns *every* visible
37
- * student's submissions to a viewer holding `manage_grades` or
38
- * `view_all_grades`, ordered by `user_id`. @see the `SUBMISSION_FIELDS`
39
- * docstring in `./queries`.
19
+ * When supplied it must be the viewer's own id. @see `SUBMISSION_FIELDS` in `./queries`
40
20
  */
41
21
  studentId?: string | null;
42
- /**
43
- * Validate normalized output against the Zod schemas before returning.
44
- *
45
- * On by default. This is the check that keeps the GraphQL and REST adapters
46
- * from drifting, and it is the only place the contract is enforced at
47
- * runtime — turn it off only if a profile says it costs something real.
48
- */
22
+ /** On by default: the only place the contract is enforced at runtime. */
49
23
  validate?: boolean;
50
24
  }
51
25
  export declare function createCanvasGraphqlAdapter({ executeQuery, inlineItemLimit, nodeBudget, onUnsupportedItem, studentId, validate, }: CanvasGraphqlAdapterOptions): ModulesDataAdapter;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/adapters/canvasGraphql/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAwC,kBAAkB,EAAE,MAAM,UAAU,CAAA;AACxF,OAAO,EAKL,KAAK,qBAAqB,EAC3B,MAAM,aAAa,CAAA;AAQpB;;;;;;;;GAQG;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,EAAE,UAAU,EAC7C,KAAK,EAAE,OAAO,EACd,SAAS,EAAE,UAAU,KAClB,OAAO,CAAC,OAAO,CAAC,CAAA;AAUrB,OAAO,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAA;AAEvE,MAAM,WAAW,2BAA2B;IAC1C,YAAY,EAAE,YAAY,CAAA;IAC1B,6BAA6B;IAC7B,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,oCAAoC;IACpC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,qBAAqB,KAAK,IAAI,CAAA;IACzD;;;;;;;;;;;;;;OAcG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AA6BD,wBAAgB,0BAA0B,CAAC,EACzC,YAAY,EACZ,eAAmC,EACnC,UAAqC,EACrC,iBAAiB,EACjB,SAAS,EACT,QAAe,GAChB,EAAE,2BAA2B,GAAG,kBAAkB,CAgFlD;AAED,OAAO,EACL,gBAAgB,EAChB,WAAW,EACX,KAAK,uBAAuB,EAC5B,KAAK,mBAAmB,GACzB,MAAM,WAAW,CAAA;AAClB,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,oBAAoB,EACpB,kBAAkB,EAClB,eAAe,EACf,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,4BAA4B,EACjC,KAAK,uBAAuB,EAC5B,KAAK,2BAA2B,EAChC,KAAK,wBAAwB,EAC7B,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EACtB,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAC5B,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,2BAA2B,EAChC,KAAK,oBAAoB,EACzB,iBAAiB,EACjB,eAAe,EACf,mBAAmB,EACnB,wBAAwB,EACxB,oBAAoB,EACpB,iBAAiB,EACjB,KAAK,gBAAgB,EACrB,0BAA0B,EAC1B,KAAK,sBAAsB,EAC3B,oBAAoB,EACpB,KAAK,qBAAqB,GAC3B,MAAM,aAAa,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/adapters/canvasGraphql/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAwC,kBAAkB,EAAE,MAAM,UAAU,CAAA;AACxF,OAAO,EAKL,KAAK,qBAAqB,EAC3B,MAAM,aAAa,CAAA;AAWpB,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,EAAE,UAAU,EAC7C,KAAK,EAAE,OAAO,EACd,SAAS,EAAE,UAAU,KAClB,OAAO,CAAC,OAAO,CAAC,CAAA;AAErB,OAAO,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAA;AAEvE,MAAM,WAAW,2BAA2B;IAC1C,YAAY,EAAE,YAAY,CAAA;IAC1B,6BAA6B;IAC7B,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,oCAAoC;IACpC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,gFAAgF;IAChF,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,qBAAqB,KAAK,IAAI,CAAA;IACzD;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAyBD,wBAAgB,0BAA0B,CAAC,EACzC,YAAY,EACZ,eAAmC,EACnC,UAAqC,EACrC,iBAAiB,EACjB,SAAS,EACT,QAAe,GAChB,EAAE,2BAA2B,GAAG,kBAAkB,CAuElD;AAED,OAAO,EACL,gBAAgB,EAChB,WAAW,EACX,KAAK,uBAAuB,EAC5B,KAAK,mBAAmB,GACzB,MAAM,WAAW,CAAA;AAClB,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,oBAAoB,EACpB,kBAAkB,EAClB,eAAe,EACf,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,4BAA4B,EACjC,KAAK,uBAAuB,EAC5B,KAAK,2BAA2B,EAChC,KAAK,wBAAwB,EAC7B,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EACtB,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAC5B,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,2BAA2B,EAChC,KAAK,oBAAoB,EACzB,iBAAiB,EACjB,eAAe,EACf,mBAAmB,EACnB,wBAAwB,EACxB,oBAAoB,EACpB,iBAAiB,EACjB,KAAK,gBAAgB,EACrB,0BAA0B,EAC1B,KAAK,sBAAsB,EAC3B,oBAAoB,EACpB,KAAK,qBAAqB,GAC3B,MAAM,aAAa,CAAA"}
@@ -1,19 +1,4 @@
1
1
  import { CompletionRequirement, CompletionRequirementType, Module, ModuleItem, ModuleItemsPage, ModuleItemStatus, ModuleItemType, ModuleProgress, ModulesPage, ModuleStatus, PageInfo } from '../../types/domain';
2
- /**
3
- * GraphQL node → domain model.
4
- *
5
- * Everything transport-shaped stops here. The three normalization rules from
6
- * `types/domain.ts` are enforced in this file and nowhere else:
7
- *
8
- * 1. **Ids are strings.** Canvas hands back `ID`, which the JSON transport
9
- * may render as a number; every id goes through `String()`.
10
- * 2. **`published` is always populated.** On this transport Canvas's resolver
11
- * cannot return null for any viewer, so a null is a partial response and
12
- * throws — see `resolvePublished`, which explains why the REST rule about
13
- * absence meaning "published" does not apply here.
14
- * 3. **One closed item enum.** `__typename` is the discriminator, with
15
- * `Assignment { isNewQuiz: true }` collapsing to `'quiz'`.
16
- */
17
2
  export interface GraphqlPageInfo {
18
3
  hasNextPage: boolean;
19
4
  endCursor: string | null;
@@ -24,7 +9,7 @@ export interface GraphqlCompletionRequirement {
24
9
  minScore?: number | null;
25
10
  minPercentage?: number | null;
26
11
  }
27
- /** One row of `submissionsConnection`. Present only for a student viewer. */
12
+ /** Present only for a student viewer. */
28
13
  export interface GraphqlSubmission {
29
14
  _id?: string | number | null;
30
15
  /** `SubmissionState`: unsubmitted | submitted | pending_review | graded | ungraded | deleted. */
@@ -48,7 +33,6 @@ export interface GraphqlModuleItemContent {
48
33
  pointsPossible?: number | null;
49
34
  submissionsConnection?: GraphqlSubmissionConnection | null;
50
35
  }
51
- /** A `Requirement` as it appears in `ModuleProgression.requirementsMet`. */
52
36
  export interface GraphqlRequirement {
53
37
  id: string | number;
54
38
  type: string;
@@ -63,7 +47,6 @@ export interface GraphqlModuleProgression {
63
47
  started?: boolean | null;
64
48
  requirementsMet?: (GraphqlRequirement | null)[] | null;
65
49
  }
66
- /** `Module.submissionStatistics`, i.e. Canvas's `ModuleStatistics`. */
67
50
  export interface GraphqlModuleStatistics {
68
51
  latestDueAt?: string | null;
69
52
  missingAssignmentCount?: number | null;
@@ -100,6 +83,7 @@ export interface GraphqlModuleNode {
100
83
  requireSequentialProgress?: boolean | null;
101
84
  prerequisites?: ({
102
85
  id: string | number;
86
+ name?: string | null;
103
87
  } | null)[] | null;
104
88
  completionRequirements?: (GraphqlCompletionRequirement | null)[] | null;
105
89
  /** Absent for a teacher/anonymous viewer, and `null` when Canvas has none. */
@@ -123,221 +107,95 @@ export interface GetModuleItemsResult {
123
107
  moduleItemsConnection?: GraphqlModuleItemConnection | null;
124
108
  } | null;
125
109
  }
126
- /** What a `PartialCanvasResponseError` is about. */
127
110
  export type PartialResponseSubject = 'module' | 'moduleItem';
128
111
  /**
129
- * A row arrived without a field this adapter cannot responsibly guess.
130
- *
131
- * Thrown rather than defaulted, because in both current cases the
132
- * sensible-looking default is the dangerous one:
133
- *
134
- * - `moduleItemsTotalCount` is `Int!`. Coercing a missing value to `0` makes
135
- * `resolveItemsMode` see an empty course and answer `inline`, so the page
136
- * asks Canvas to inline every item it has — the most expensive transport
137
- * setting, chosen when the response is least trustworthy.
138
- * - `published` is null when the viewer cannot manage the object, which for
139
- * such a viewer means "published". For a viewer who *can* manage, Canvas
140
- * always sends a boolean, so a null contradicts what the host told us and
141
- * the wrong guess renders an unpublished thing as published — to the one
142
- * audience that acts on that badge.
143
- *
144
- * Failing is recoverable in a way silence is not: the hook surfaces the error,
145
- * the host can retry, and `useItemsMode` already falls through to `onDemand` on
146
- * a failed probe rather than deadlocking the page.
112
+ * Thrown rather than defaulted, because the sensible-looking default is the dangerous one:
113
+ * a missing `moduleItemsTotalCount` coerced to `0` makes `resolveItemsMode` answer `inline`
114
+ * exactly when the response is least trustworthy, and a null `published` guessed `true`
115
+ * renders unpublished content as published to the audience that acts on that badge.
147
116
  */
148
117
  export declare class PartialCanvasResponseError extends Error {
149
118
  readonly subject: PartialResponseSubject;
150
- /** The module or item id, so a host can log which row came back incomplete. */
151
119
  readonly id: string;
152
- /** The field that was missing. */
153
120
  readonly field: string;
154
121
  constructor(subject: PartialResponseSubject, id: string, field: string);
155
122
  }
156
- /** Reported when a `__typename` has no place in the closed domain enum. */
157
123
  export interface UnsupportedModuleItem {
158
- /** The module item id, so a host can look the row up in Canvas. */
159
124
  id: string;
160
125
  /** The `content.__typename` we could not map, or `null` when content was absent. */
161
126
  typename: string | null;
162
127
  }
163
128
  export interface NormalizeOptions {
164
129
  /**
165
- * Called for every item dropped because its content type is outside the
166
- * closed enum (`AiExperience`, `PeerReviewSubAssignment`, or content that
167
- * was deleted and came back `null`).
168
- *
169
- * The alternatives were worse: widening the enum makes the shared contract
170
- * chase Canvas's release cadence, and throwing takes the whole page down for
171
- * one unknown row.
172
- *
173
- * Dropping is silent data loss, so it must not *also* be unobservable. This
174
- * defaults to a `console.warn` rather than to nothing — matching
175
- * `platform-datetime` and ~30 other files in the repo — because "a host can log
176
- * it" is only true if the host knows to opt in. Pass a no-op to silence it.
130
+ * Dropping an unmappable item is silent data loss, so this defaults to `console.warn`
131
+ * rather than to nothing. Pass a no-op to silence it.
177
132
  */
178
133
  onUnsupportedItem?: (item: UnsupportedModuleItem) => void;
134
+ /**
135
+ * Whether the query selected the submission field set. Absent means it did.
136
+ *
137
+ * With no `studentId` the adapter omits submissions but still fetches
138
+ * `progression`, so a met requirement is the only per-item signal left. Without
139
+ * this, `complete` would be claimed on work whose submission was never read.
140
+ */
141
+ submissionsIncluded?: boolean;
179
142
  }
180
- /**
181
- * `__typename` (+ `isNewQuiz`) → domain type, or `null` when unmappable.
182
- *
183
- * A New Quiz is an `Assignment` with an LTI submission type in Canvas's data
184
- * model; every UI that shows it calls it a quiz, so rule 3 collapses it here
185
- * rather than leaving each caller to re-derive it.
186
- */
187
143
  export declare function normalizeItemType(content: GraphqlModuleItemContent | null | undefined): ModuleItemType | null;
188
- /**
189
- * ISO 8601 duration → whole minutes.
190
- *
191
- * Canvas exposes `estimatedDuration` as an `ISO8601Duration` scalar (`PT45M`)
192
- * while the domain model carries minutes, because that is the only unit either
193
- * host's UI renders. Year and month designators are rejected rather than
194
- * guessed at — they are not calendar-independent, and Canvas never emits them
195
- * for this field.
196
- */
197
144
  export declare function parseDurationMinutes(value: string | null | undefined): number | null;
198
- /**
199
- * Wire string → closed enum, with anything unrecognized becoming `'unknown'`.
200
- *
201
- * Unlike an unmappable item `__typename`, an unknown requirement type is not
202
- * dropped: the requirement still exists, the student still has to satisfy it,
203
- * and hiding the row would be a worse lie than labeling it vaguely.
204
- */
205
145
  export declare function normalizeCompletionRequirementType(type: string): CompletionRequirementType;
206
146
  export declare function normalizePageInfo(pageInfo: GraphqlPageInfo | null | undefined): PageInfo;
207
- /**
208
- * The module-level student state every item in that module is read against.
209
- *
210
- * Built once per module and threaded into the item normalizer so the inline and
211
- * on-demand paths derive the identical status for the identical item — which is
212
- * why `GET_MODULE_ITEMS` re-reads `progression` alongside its items.
213
- */
214
147
  export interface ModuleStudentContext {
215
- /** Canvas reports the module locked for this student. */
216
148
  locked: boolean;
217
149
  /** Raw `ModuleProgression.workflowState`: locked | unlocked | started | completed. */
218
150
  workflowState: string | null;
219
151
  completed: boolean;
220
152
  started: boolean;
221
- /**
222
- * Module *item* ids whose completion requirement the student has satisfied.
223
- *
224
- * `ModuleProgression.requirementsMet` is a list of `Requirement`, and
225
- * `Requirement.id` is the module item id — the same join `completionRequirements`
226
- * uses. The domain model wants a count, so the list collapses here.
227
- */
153
+ /** `Requirement.id` is the module *item* id — the same join `completionRequirements` uses. */
228
154
  requirementsMetItemIds: ReadonlySet<string>;
229
155
  }
230
- /**
231
- * The student's submission for an item, or `null`.
232
- *
233
- * Canvas returns a connection because the field is shared with the teacher view
234
- * (where it is every student's submission); for a student it holds at most one
235
- * row, so the first non-null node is the answer.
236
- */
237
156
  export declare function firstSubmission(content: GraphqlModuleItemContent | null | undefined): GraphqlSubmission | null;
238
157
  /**
239
- * `progression` the per-module student context, or `undefined` when there is
240
- * no student. This is the gate described at the top of this block.
241
- *
242
- * `requirementsMet` is filtered against the requirements the module currently
243
- * defines, matching Canvas's own `filterRequirementsMet`: a progression can
244
- * outlive the requirement that produced it, and counting a stale entry would
245
- * show a student 4 of 3. Canvas matches on id, type, `minScore` *and*
246
- * `minPercentage`; we match on id and type only, because the score bounds are
247
- * the part a teacher edits in place and an edited bound should not silently
248
- * un-meet a requirement the student genuinely satisfied.
158
+ * `requirementsMet` is filtered against the requirements the module currently defines, as
159
+ * Canvas's own `filterRequirementsMet` does a progression outliving its requirement would
160
+ * show a student 4 of 3. We match on id and type only, not Canvas's score bounds too: those
161
+ * are what a teacher edits in place, and an edit should not silently un-meet a requirement.
249
162
  */
250
163
  export declare function buildStudentContext(progression: GraphqlModuleProgression | null | undefined, completionRequirements: readonly CompletionRequirement[]): ModuleStudentContext | undefined;
251
164
  /**
252
- * Per-item status. First rule that matches wins:
165
+ * `excused` precedes the other submission rules as it does in Canvas — `Submission#missing?`
166
+ * and `#late?` both return false for an excused submission.
253
167
  *
254
- * 1. `locked` the module is locked for this student. Canvas exposes no
255
- * per-item lock in this field set (`masteryPaths.locked` is
256
- * a host-specific affordance we deliberately do not fetch),
257
- * and a locked module is locked for every item in it.
258
- * 2. `missing` — `submission.missing`. Ahead of `graded` on purpose: this
259
- * is the precedence Canvas's own student pill uses, and a
260
- * teacher entering a zero on a missing submission should
261
- * not make the "Missing" chip stop finding it.
262
- * 3. `graded` — `state === 'graded'`, or `gradingStatus` is `graded`/
263
- * `excused`. Excused counts as graded because there is
264
- * nothing left for the student to do.
265
- * 4. `submitted` — handed in and awaiting a grade: `submittedAt` is set, or
266
- * the state is `submitted`/`pending_review`.
267
- * 5. `in_progress` — the student has engaged but produced nothing gradable:
268
- * either an attempt is open (`attempt > 0` with no
269
- * `submittedAt`), or the item's completion requirement is
270
- * already met with no submission at all — a viewed page, a
271
- * followed link, a contribution to an ungraded discussion.
272
- * 6. `not_started` — everything else.
168
+ * `missing` ahead of `complete` is a **deliberate divergence**: Canvas's
169
+ * `ModuleItemStatusIcon` falls through to Complete on a met requirement, so a `must_view` met
170
+ * on a past-due assignment hides the missing work. That pill is flag-gated `context_modules_v2`.
273
171
  *
274
- * Rule 5 is the one to argue with. `ModuleItemStatus` has no "complete" member,
275
- * so a satisfied `must_view` requirement has nowhere else to land; the domain
276
- * comment already flags the same gap at module level ("All items complete" is
277
- * not in the designer's list). If the designer adds one, this is the rule to
278
- * change and the only one.
172
+ * `complete` does not test gradability: Canvas stores `requirements_met` per ContentTag with
173
+ * no reference to content type.
279
174
  */
280
- export declare function deriveModuleItemStatus(content: GraphqlModuleItemContent | null | undefined, itemId: string, student: ModuleStudentContext): ModuleItemStatus;
175
+ export declare function deriveModuleItemStatus(content: GraphqlModuleItemContent | null | undefined, itemId: string, student: ModuleStudentContext, submissionsIncluded?: boolean): ModuleItemStatus;
281
176
  /**
282
- * Per-module rollup. First rule that matches wins:
177
+ * Per-module rollup.
283
178
  *
284
- * 1. `locked` `progression.locked` (or `workflowState === 'locked'`).
285
- * 2. `missing` — `submissionStatistics.missingAssignmentCount > 0` and
286
- * the module is not completed. That is the design's
287
- * "1+ item past due", read from the module-level
288
- * statistic rather than from items, so the filter row
289
- * works with nothing expanded.
290
- * 3. `all_graded` — completed, *and* items were fetched, *and* every
291
- * gradable one is `graded`.
292
- * 4. `all_submitted` — completed. The honest floor: `progression.completed`
293
- * means every requirement is met, which is submission,
294
- * not grading. See the caveat below.
295
- * 5. `in_progress` — started, or at least one requirement met.
296
- * 6. `not_started` — everything else.
179
+ * `missing` is read from `submissionStatistics`, not from items, so the filter row
180
+ * works with nothing expanded.
297
181
  *
298
- * Caveat on rule 3: Canvas has no module-level "everything is graded" signal
299
- * `ModuleStatistics` is only `latestDueAt` + `missingAssignmentCount`. So
300
- * `all_graded` is reachable **only in inline mode**, where the items ride along
301
- * with the module in the same response. Upgrading on the evidence we have beats
302
- * claiming a grade we cannot see.
182
+ * Excused items leave the `all_graded` population rather than counting as graded —
183
+ * an exempted item was never assessed, the same reading the item pill takes. A
184
+ * module whose every gradable item is excused therefore reports `all_submitted`.
303
185
  *
304
- * In `onDemand` mode a completed module reports `all_submitted` and stays there,
305
- * even after its items are fetched and every one of them is `graded`. That is a
306
- * real limitation, not a temporary state: `progress` is derived once, from the
307
- * list response, and nothing merges items back into it
308
- * `normalizeModuleItemsPage` returns `{ items, pageInfo }` and `useModulesPageData`
309
- * never revisits the module. No upgrade to `all_graded` ever happens.
310
- *
311
- * Closing it means either recomputing the module's status in the UI from the items
312
- * it holds — which reintroduces the inline/onDemand asymmetry the module-level
313
- * design exists to avoid — or Canvas exposing the signal. Left open deliberately
314
- * rather than papered over.
186
+ * `all_graded` is reachable **only in inline mode**: Canvas has no module-level
187
+ * "everything is graded" signal, so it takes the items riding along in the same
188
+ * response. In `onDemand` mode a completed module reports `all_submitted` and stays
189
+ * there even once its items arrive all graded, because `progress` is derived once
190
+ * from the list response. Known limitation, left open rather than papered over.
315
191
  */
316
192
  export declare function deriveModuleStatus(student: ModuleStudentContext, missingCount: number, requirementsMet: number, items: ModuleItem[] | undefined): ModuleStatus;
317
- /**
318
- * The module's `ModuleProgress`, or `undefined` when there is no student.
319
- *
320
- * `requirementCount` is the module's own when Canvas set one and the number of
321
- * completion requirements otherwise — Canvas uses `requirement_count` for
322
- * "complete any N of these", so it is the denominator whenever it is present,
323
- * and the full list is the denominator when it is not. `requirementsMet` is
324
- * clamped to it so the pair can never read "3 of 1", and `percentComplete` is
325
- * derived from that same clamped pair rather than computed independently, so a
326
- * host can never render a bar that disagrees with the fraction beside it.
327
- */
328
193
  export declare function deriveModuleProgress(node: GraphqlModuleNode, completionRequirements: readonly CompletionRequirement[], student: ModuleStudentContext | undefined, items: ModuleItem[] | undefined): ModuleProgress | undefined;
329
194
  /**
330
- * A single item, or `null` when its content type is outside the closed enum.
331
- *
332
- * `dueAt` prefers `submission.cachedDueDate` the per-student, override-applied
333
- * date Canvas denormalizes onto the submission row and falls back to
334
- * `content.dueAt`. That ordering is what closes the gap a live-Canvas probe
335
- * found: `dueAt` exists on `Assignment` only, `Quiz` has none, and a graded
336
- * `Discussion` keeps its date on the backing assignment, but all three carry a
337
- * submission. The fallback still matters, because a teacher viewer gets an
338
- * assignment's `dueAt` and no submission at all.
339
- *
340
- * `status` is present only when `student` is — see the student-state block.
195
+ * `dueAt` prefers `submission.cachedDueDate`, the per-student override-applied date: `dueAt`
196
+ * exists on `Assignment` only, a `Quiz` has none, and a graded `Discussion` keeps its date on
197
+ * the backing assignment — but all three carry a submission. The `content.dueAt` fallback is
198
+ * for a teacher viewer, who gets the assignment's date and no submission.
341
199
  */
342
200
  export declare function normalizeModuleItem(node: GraphqlModuleItemNode, moduleId: string, completionRequirementsByItemId: ReadonlyMap<string, CompletionRequirement>, options?: NormalizeOptions, student?: ModuleStudentContext): ModuleItem | null;
343
201
  export declare function normalizeModule(node: GraphqlModuleNode, options?: NormalizeOptions): Module;
@@ -1 +1 @@
1
- {"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../../src/adapters/canvasGraphql/normalize.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../../src/adapters/canvasGraphql/normalize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EACrB,yBAAyB,EACzB,MAAM,EACN,UAAU,EACV,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,WAAW,EACX,YAAY,EACZ,QAAQ,EACT,MAAM,oBAAoB,CAAA;AAM3B,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,OAAO,CAAA;IACpB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CACzB;AAED,MAAM,WAAW,4BAA4B;IAC3C,EAAE,EAAE,MAAM,GAAG,MAAM,CAAA;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B;AAED,yCAAyC;AACzC,MAAM,WAAW,iBAAiB;IAChC,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;IAC5B,iGAAiG;IACjG,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,kFAAkF;IAClF,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,CAAA;IACxB,gFAAgF;IAChF,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B;AAED,MAAM,WAAW,2BAA2B;IAC1C,KAAK,CAAC,EAAE,CAAC,iBAAiB,GAAG,IAAI,CAAC,EAAE,GAAG,IAAI,CAAA;CAC5C;AAED,MAAM,WAAW,wBAAwB;IACvC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,SAAS,CAAC,EAAE,OAAO,GAAG,IAAI,CAAA;IAC1B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,mFAAmF;IACnF,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,qBAAqB,CAAC,EAAE,2BAA2B,GAAG,IAAI,CAAA;CAC3D;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,GAAG,MAAM,CAAA;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B;AAED,MAAM,WAAW,wBAAwB;IACvC,+CAA+C;IAC/C,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,SAAS,CAAC,EAAE,OAAO,GAAG,IAAI,CAAA;IAC1B,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,CAAA;IACvB,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,CAAA;IACxB,eAAe,CAAC,EAAE,CAAC,kBAAkB,GAAG,IAAI,CAAC,EAAE,GAAG,IAAI,CAAA;CACvD;AAED,MAAM,WAAW,uBAAuB;IACtC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,sBAAsB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACvC;AAED,MAAM,WAAW,qBAAqB;IACpC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;IACpB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,CAAA;IACvB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,SAAS,CAAC,EAAE,OAAO,GAAG,IAAI,CAAA;IAC1B,2DAA2D;IAC3D,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,OAAO,CAAC,EAAE,wBAAwB,GAAG,IAAI,CAAA;CAC1C;AAED,MAAM,WAAW,2BAA2B;IAC1C,KAAK,CAAC,EAAE,CAAC,qBAAqB,GAAG,IAAI,CAAC,EAAE,GAAG,IAAI,CAAA;IAC/C,QAAQ,EAAE,eAAe,CAAA;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;IACpB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,SAAS,CAAC,EAAE,OAAO,GAAG,IAAI,CAAA;IAC1B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrC,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAChC,yBAAyB,CAAC,EAAE,OAAO,GAAG,IAAI,CAAA;IAC1C,aAAa,CAAC,EAAE,CAAC;QAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC,EAAE,GAAG,IAAI,CAAA;IAC/E,sBAAsB,CAAC,EAAE,CAAC,4BAA4B,GAAG,IAAI,CAAC,EAAE,GAAG,IAAI,CAAA;IACvE,8EAA8E;IAC9E,WAAW,CAAC,EAAE,wBAAwB,GAAG,IAAI,CAAA;IAC7C,oBAAoB,CAAC,EAAE,uBAAuB,GAAG,IAAI,CAAA;IACrD,qBAAqB,CAAC,EAAE,2BAA2B,GAAG,IAAI,CAAA;CAC3D;AAED,MAAM,WAAW,uBAAuB;IACtC,KAAK,CAAC,EAAE,CAAC,iBAAiB,GAAG,IAAI,CAAC,EAAE,GAAG,IAAI,CAAA;IAC3C,QAAQ,EAAE,eAAe,CAAA;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,CAAC,EAAE;QAAE,iBAAiB,CAAC,EAAE,uBAAuB,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAA;CAC3E;AAED,MAAM,WAAW,oBAAoB;IACnC,UAAU,CAAC,EAAE;QACX,sBAAsB,CAAC,EAAE,CAAC,4BAA4B,GAAG,IAAI,CAAC,EAAE,GAAG,IAAI,CAAA;QACvE,WAAW,CAAC,EAAE,wBAAwB,GAAG,IAAI,CAAA;QAC7C,qBAAqB,CAAC,EAAE,2BAA2B,GAAG,IAAI,CAAA;KAC3D,GAAG,IAAI,CAAA;CACT;AAED,MAAM,MAAM,sBAAsB,GAAG,QAAQ,GAAG,YAAY,CAAA;AAE5D;;;;;GAKG;AACH,qBAAa,0BAA2B,SAAQ,KAAK;IACnD,QAAQ,CAAC,OAAO,EAAE,sBAAsB,CAAA;IACxC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;gBAEV,OAAO,EAAE,sBAAsB,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;CAWvE;AAED,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAA;IACV,oFAAoF;IACpF,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,qBAAqB,KAAK,IAAI,CAAA;IAEzD;;;;;;OAMG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAA;CAC9B;AAoDD,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,wBAAwB,GAAG,IAAI,GAAG,SAAS,GACnD,cAAc,GAAG,IAAI,CAQvB;AAOD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAcpF;AAaD,wBAAgB,kCAAkC,CAAC,IAAI,EAAE,MAAM,GAAG,yBAAyB,CAE1F;AAaD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI,GAAG,SAAS,GAAG,QAAQ,CAMxF;AAWD,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,OAAO,CAAA;IACf,sFAAsF;IACtF,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,SAAS,EAAE,OAAO,CAAA;IAClB,OAAO,EAAE,OAAO,CAAA;IAChB,8FAA8F;IAC9F,sBAAsB,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;CAC5C;AAQD,wBAAgB,eAAe,CAC7B,OAAO,EAAE,wBAAwB,GAAG,IAAI,GAAG,SAAS,GACnD,iBAAiB,GAAG,IAAI,CAK1B;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,wBAAwB,GAAG,IAAI,GAAG,SAAS,EACxD,sBAAsB,EAAE,SAAS,qBAAqB,EAAE,GACvD,oBAAoB,GAAG,SAAS,CAmBlC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,wBAAwB,GAAG,IAAI,GAAG,SAAS,EACpD,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,oBAAoB,EAC7B,mBAAmB,UAAO,GACzB,gBAAgB,CAwBlB;AAKD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,oBAAoB,EAC7B,YAAY,EAAE,MAAM,EACpB,eAAe,EAAE,MAAM,EACvB,KAAK,EAAE,UAAU,EAAE,GAAG,SAAS,GAC9B,YAAY,CAYd;AAID,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,iBAAiB,EACvB,sBAAsB,EAAE,SAAS,qBAAqB,EAAE,EACxD,OAAO,EAAE,oBAAoB,GAAG,SAAS,EACzC,KAAK,EAAE,UAAU,EAAE,GAAG,SAAS,GAC9B,cAAc,GAAG,SAAS,CAgB5B;AAID;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,qBAAqB,EAC3B,QAAQ,EAAE,MAAM,EAChB,8BAA8B,EAAE,WAAW,CAAC,MAAM,EAAE,qBAAqB,CAAC,EAC1E,OAAO,GAAE,gBAAqB,EAC9B,OAAO,CAAC,EAAE,oBAAoB,GAC7B,UAAU,GAAG,IAAI,CAkCnB;AA4CD,wBAAgB,eAAe,CAAC,IAAI,EAAE,iBAAiB,EAAE,OAAO,GAAE,gBAAqB,GAAG,MAAM,CAuC/F;AAED,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,EAC3C,OAAO,GAAE,gBAAqB,GAC7B,WAAW,CAUb;AAED,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,oBAAoB,GAAG,IAAI,GAAG,SAAS,EAC/C,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,gBAAqB,GAC7B,eAAe,CAYjB"}
@@ -1,45 +1,17 @@
1
1
  import { DocumentNode } from 'graphql';
2
2
  /**
3
- * Module list, optionally with each module's items inlined.
3
+ * `includeItems` is an `@include` directive rather than a second document, so there is one
4
+ * operation name and one persisted-query hash.
4
5
  *
5
- * `includeItems` is expressed as an `@include` directive on the nested
6
- * `moduleItemsConnection` rather than as a second document. Reasoning:
7
- *
8
- * 1. One document means one operation name, which means one persisted-query
9
- * hash to register with Canvas. Two documents would double that
10
- * registration surface for what is a single conceptual read.
11
- * 2. The directive is evaluated server-side, so `includeItems: false` skips
12
- * resolving every per-item field — the title, content type, points and
13
- * submissions for each row.
14
- *
15
- * What it does *not* skip is the
16
- * `ModuleItemsVisibleLoader`. `moduleItemsTotalCount` and
17
- * `completionRequirements` are selected unconditionally and
18
- * `module_type.rb:137-141,166-169` resolves both through that same loader,
19
- * so it runs whatever `includeItems` is. The saving is per-item field
20
- * resolution, not the visibility query.
21
- * 3. The response type is a single shape with an optional key, which is
22
- * exactly how the domain model already treats it (`items?: ModuleItem[]`,
23
- * where `undefined` means "not fetched"). A second document would force
24
- * the normalizer to branch on which document produced the payload.
25
- *
26
- * `itemsFirst` bounds the inline item fetch. When a module has more items than
27
- * that, the normalizer drops the partial list rather than reporting a
28
- * truncated one — see `normalize.ts`.
6
+ * It skips per-item field resolution but **not** the `ModuleItemsVisibleLoader`:
7
+ * `moduleItemsTotalCount` and `completionRequirements` are selected unconditionally and
8
+ * `module_type.rb:137-141,166-169` resolves both through it regardless.
29
9
  */
30
10
  export declare const GET_MODULES: DocumentNode;
31
11
  /**
32
- * A single module's items, paged on demand.
33
- *
34
- * `completionRequirements` is re-read at the module level here because a
35
- * requirement is stored on the module, keyed by item id — without it, items
36
- * fetched on demand would lose the requirement that items fetched inline keep,
37
- * and the two paths would produce different domain objects for the same item.
38
- *
39
- * `progression` is re-read for the same reason: `ModuleItem.status` depends on
40
- * module-level state (is the module locked for me, which of its requirements
41
- * have I met), so an item fetched on demand would otherwise carry a different
42
- * status from the identical item fetched inline.
12
+ * `completionRequirements` and `progression` are re-read at module level because an item's
13
+ * requirement and status both depend on them — without that, an item fetched on demand would
14
+ * differ from the identical item fetched inline.
43
15
  */
44
16
  export declare const GET_MODULE_ITEMS: DocumentNode;
45
17
  export interface GetModulesVariables {
@@ -1 +1 @@
1
- {"version":3,"file":"queries.d.ts","sourceRoot":"","sources":["../../../src/adapters/canvasGraphql/queries.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAkL3C;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,WAAW,EAAE,YA0DzB,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,gBAAgB,EAAE,YAiC9B,CAAA;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,YAAY,EAAE,OAAO,CAAA;IACrB,UAAU,EAAE,MAAM,CAAA;IAClB;;;;;;OAMG;IACH,kBAAkB,EAAE,OAAO,CAAA;IAC3B,uFAAuF;IACvF,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1B;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,MAAM,CAAA;IAChB,kBAAkB,EAAE,OAAO,CAAA;IAC3B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACtB"}
1
+ {"version":3,"file":"queries.d.ts","sourceRoot":"","sources":["../../../src/adapters/canvasGraphql/queries.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAgH3C;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,EAAE,YA2DzB,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,EAAE,YAiC9B,CAAA;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,YAAY,EAAE,OAAO,CAAA;IACrB,UAAU,EAAE,MAAM,CAAA;IAClB;;;;;;OAMG;IACH,kBAAkB,EAAE,OAAO,CAAA;IAC3B,uFAAuF;IACvF,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1B;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,MAAM,CAAA;IAChB,kBAAkB,EAAE,OAAO,CAAA;IAC3B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACtB"}
@@ -1,36 +1,12 @@
1
1
  import { Module, ModuleItem } from '../types/domain';
2
2
  import { ModulesDataAdapter } from './types';
3
- /**
4
- * An adapter backed by a plain object instead of a network.
5
- *
6
- * It exists so the hooks can be built and tested without waiting on a real
7
- * adapter, but it is not scaffolding — it stays as the test double for hook
8
- * tests and as the fastest way to render a modules UI in Storybook with no
9
- * server at all.
10
- *
11
- * It is a real implementation of the contract, deliberately: it paginates with
12
- * opaque cursors, honors `AbortSignal`, and declines to inline items for modules
13
- * over `INLINE_ITEM_LIMIT` — so hooks written against it meet the same edges
14
- * Canvas will hand them, including the one Canvas Career gets wrong.
15
- */
16
3
  export { INLINE_ITEM_LIMIT } from './limits';
17
4
  export interface InMemoryFixture {
18
- /**
19
- * The course these modules belong to.
20
- *
21
- * Present so the adapter can *check* it. Without it, `courseId` was
22
- * destructured and dropped, which meant one adapter instance served identical
23
- * modules for every course id in existence — and since `cache/keys.ts` keys the
24
- * query cache by `courseId`, the entire class of bug where it is mis-threaded,
25
- * defaulted or collided across courses was unfalsifiable against the package's
26
- * primary test double. A hook that hardcoded `courseId: '1'` internally would
27
- * have passed every test and every story.
28
- */
5
+ /** Checked, not just stored: without it one instance would serve every course id. */
29
6
  courseId: string;
30
7
  modules: Module[];
31
8
  itemsByModuleId: Record<string, ModuleItem[]>;
32
9
  }
33
- /** What a served request was for. Handed to `onRequest`. */
34
10
  export interface InMemoryRequest {
35
11
  operation: 'listModules' | 'listModuleItems';
36
12
  courseId: string;
@@ -1 +1 @@
1
- {"version":3,"file":"inMemory.d.ts","sourceRoot":"","sources":["../../src/adapters/inMemory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAgC,MAAM,iBAAiB,CAAA;AAEvF,OAAO,KAAK,EAAwC,kBAAkB,EAAE,MAAM,SAAS,CAAA;AAEvF;;;;;;;;;;;;GAYG;AAIH,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AAE5C,MAAM,WAAW,eAAe;IAC9B;;;;;;;;;;OAUG;IACH,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,EAAE,CAAC,CAAA;CAC9C;AAED,4DAA4D;AAC5D,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,aAAa,GAAG,iBAAiB,CAAA;IAC5C,QAAQ,EAAE,MAAM,CAAA;IAChB,sCAAsC;IACtC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,sBAAsB;IACrC;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,CAAC,SAAS,EAAE,aAAa,GAAG,iBAAiB,EAAE,OAAO,EAAE,eAAe,KAAK,IAAI,CAAA;CAC7F;AAED;;;;;;GAMG;AACH,qBAAa,2BAA4B,SAAQ,KAAK;IACpD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;gBAEb,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;CAS/C;AA4BD,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,eAAe,EACxB,OAAO,GAAE,sBAA2B,GACnC,kBAAkB,CAiEpB"}
1
+ {"version":3,"file":"inMemory.d.ts","sourceRoot":"","sources":["../../src/adapters/inMemory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAgC,MAAM,iBAAiB,CAAA;AAEvF,OAAO,KAAK,EAAwC,kBAAkB,EAAE,MAAM,SAAS,CAAA;AAEvF,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AAE5C,MAAM,WAAW,eAAe;IAC9B,qFAAqF;IACrF,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,EAAE,CAAC,CAAA;CAC9C;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,aAAa,GAAG,iBAAiB,CAAA;IAC5C,QAAQ,EAAE,MAAM,CAAA;IAChB,sCAAsC;IACtC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,sBAAsB;IACrC;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,CAAC,SAAS,EAAE,aAAa,GAAG,iBAAiB,EAAE,OAAO,EAAE,eAAe,KAAK,IAAI,CAAA;CAC7F;AAED;;;;;;GAMG;AACH,qBAAa,2BAA4B,SAAQ,KAAK;IACpD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;gBAEb,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;CAS/C;AA2BD,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,eAAe,EACxB,OAAO,GAAE,sBAA2B,GACnC,kBAAkB,CA0DpB"}