pi-interview 0.6.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/schema.ts CHANGED
@@ -1,15 +1,28 @@
1
- export interface CodeBlock {
2
- code: string;
1
+ export interface BaseContentBlock {
2
+ source: string;
3
3
  lang?: string;
4
4
  file?: string;
5
+ title?: string;
6
+ }
7
+
8
+ export interface MarkdownContentBlock extends BaseContentBlock {
9
+ lang: "md" | "markdown";
10
+ showSource?: boolean;
11
+ lines?: never;
12
+ highlights?: never;
13
+ }
14
+
15
+ export interface CodeContentBlock extends BaseContentBlock {
5
16
  lines?: string;
6
17
  highlights?: number[];
7
- title?: string;
18
+ showSource?: never;
8
19
  }
9
20
 
21
+ export type ContentBlock = MarkdownContentBlock | CodeContentBlock;
22
+
10
23
  export interface RichOption {
11
24
  label: string;
12
- code?: CodeBlock;
25
+ content?: ContentBlock;
13
26
  }
14
27
 
15
28
  export type OptionValue = string | RichOption;
@@ -44,7 +57,7 @@ export interface Question {
44
57
  conviction?: "strong" | "slight";
45
58
  weight?: "critical" | "minor";
46
59
  context?: string;
47
- codeBlock?: CodeBlock;
60
+ content?: ContentBlock;
48
61
  media?: MediaBlock | MediaBlock[];
49
62
  }
50
63
 
@@ -127,34 +140,71 @@ const SCHEMA_EXAMPLE = `Expected format:
127
140
  ]
128
141
  }
129
142
  Valid types: single, multi, text, image, info
130
- Options: array of strings or objects with { label, code? }`;
143
+ Options: array of strings or objects with { label, content? }`;
144
+
145
+ function isMarkdownLang(lang: unknown): lang is "md" | "markdown" {
146
+ if (typeof lang !== "string") return false;
147
+ const normalized = lang.trim().toLowerCase();
148
+ return normalized === "md" || normalized === "markdown";
149
+ }
131
150
 
132
- function validateCodeBlock(block: unknown, context: string): CodeBlock {
151
+ function validateContentBlock(block: unknown, context: string): ContentBlock {
133
152
  if (!block || typeof block !== "object") {
134
- throw new Error(`${context}: codeBlock must be an object`);
153
+ throw new Error(`${context}: content must be an object`);
135
154
  }
136
155
  const b = block as Record<string, unknown>;
137
- if (typeof b.code !== "string") {
138
- throw new Error(`${context}: codeBlock.code must be a string`);
156
+ if (typeof b.source !== "string") {
157
+ throw new Error(`${context}: content.source must be a string`);
139
158
  }
140
159
  if (b.lang !== undefined && typeof b.lang !== "string") {
141
- throw new Error(`${context}: codeBlock.lang must be a string`);
160
+ throw new Error(`${context}: content.lang must be a string`);
142
161
  }
143
162
  if (b.file !== undefined && typeof b.file !== "string") {
144
- throw new Error(`${context}: codeBlock.file must be a string`);
163
+ throw new Error(`${context}: content.file must be a string`);
145
164
  }
146
165
  if (b.lines !== undefined && typeof b.lines !== "string") {
147
- throw new Error(`${context}: codeBlock.lines must be a string`);
166
+ throw new Error(`${context}: content.lines must be a string`);
148
167
  }
149
168
  if (b.title !== undefined && typeof b.title !== "string") {
150
- throw new Error(`${context}: codeBlock.title must be a string`);
169
+ throw new Error(`${context}: content.title must be a string`);
170
+ }
171
+ if (b.showSource !== undefined && typeof b.showSource !== "boolean") {
172
+ throw new Error(`${context}: content.showSource must be a boolean`);
151
173
  }
152
174
  if (b.highlights !== undefined) {
153
175
  if (!Array.isArray(b.highlights) || b.highlights.some((h) => typeof h !== "number")) {
154
- throw new Error(`${context}: codeBlock.highlights must be an array of numbers`);
176
+ throw new Error(`${context}: content.highlights must be an array of numbers`);
177
+ }
178
+ }
179
+
180
+ if (isMarkdownLang(b.lang)) {
181
+ if (b.lines !== undefined) {
182
+ throw new Error(`${context}: content.lines is not allowed for markdown content`);
183
+ }
184
+ if (b.highlights !== undefined) {
185
+ throw new Error(`${context}: content.highlights is not allowed for markdown content`);
155
186
  }
187
+ return {
188
+ source: b.source,
189
+ lang: b.lang.trim().toLowerCase() as "md" | "markdown",
190
+ file: b.file,
191
+ title: b.title,
192
+ showSource: b.showSource,
193
+ };
156
194
  }
157
- return b as unknown as CodeBlock;
195
+
196
+ if (b.showSource !== undefined) {
197
+ throw new Error(`${context}: content.showSource is only valid when content.lang is "md" or "markdown"`);
198
+ }
199
+
200
+ return {
201
+ source: b.source,
202
+ lang: b.lang,
203
+ file: b.file,
204
+ title: b.title,
205
+ lines: b.lines,
206
+ highlights: b.highlights as number[] | undefined,
207
+ };
158
208
  }
159
209
 
160
210
  function validateOption(option: unknown, questionId: string, index: number): OptionValue {
@@ -169,9 +219,17 @@ function validateOption(option: unknown, questionId: string, index: number): Opt
169
219
  );
170
220
  }
171
221
  if (o.code !== undefined) {
172
- validateCodeBlock(o.code, `Question "${questionId}" option "${o.label}"`);
222
+ throw new Error(
223
+ `Question "${questionId}" option "${o.label}": legacy "code" is no longer supported; use "content"`
224
+ );
173
225
  }
174
- return option as RichOption;
226
+ if (o.content !== undefined) {
227
+ return {
228
+ label: o.label,
229
+ content: validateContentBlock(o.content, `Question "${questionId}" option "${o.label}"`),
230
+ };
231
+ }
232
+ return { label: o.label };
175
233
  }
176
234
  throw new Error(
177
235
  `Question "${questionId}": option at index ${index} must be a string or object with label`
@@ -240,7 +298,7 @@ function validateBasicStructure(data: unknown): QuestionsFile {
240
298
  throw new Error(`Question "${q.id}": options must be a non-empty array`);
241
299
  }
242
300
  for (let j = 0; j < q.options.length; j++) {
243
- validateOption(q.options[j], q.id as string, j);
301
+ q.options[j] = validateOption(q.options[j], q.id as string, j);
244
302
  }
245
303
  }
246
304
 
@@ -249,7 +307,10 @@ function validateBasicStructure(data: unknown): QuestionsFile {
249
307
  }
250
308
 
251
309
  if (q.codeBlock !== undefined) {
252
- validateCodeBlock(q.codeBlock, `Question "${q.id}"`);
310
+ throw new Error(`Question "${q.id}": legacy "codeBlock" is no longer supported; use "content"`);
311
+ }
312
+ if (q.content !== undefined) {
313
+ q.content = validateContentBlock(q.content, `Question "${q.id}"`);
253
314
  }
254
315
 
255
316
  if (q.conviction !== undefined) {