reflet-sdk 0.6.5 → 0.6.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.
Files changed (59) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +100 -2
  3. package/dist/{client-DAdWdIFY.js → client-oFFfRxAM.js} +135 -59
  4. package/dist/client-oFFfRxAM.js.map +1 -0
  5. package/dist/client-transport-B7BdVU7J.d.ts +150 -0
  6. package/dist/client-transport-B7BdVU7J.d.ts.map +1 -0
  7. package/dist/{devtools-layer-CEjzYGBB.js → devtools-layer-B91EVgqg.js} +417 -916
  8. package/dist/devtools-layer-B91EVgqg.js.map +1 -0
  9. package/dist/devtools-next.js +1 -1
  10. package/dist/devtools-vite.js +2 -2
  11. package/dist/feedback.d.ts +7 -1
  12. package/dist/feedback.d.ts.map +1 -1
  13. package/dist/feedback.js +105 -57
  14. package/dist/feedback.js.map +1 -1
  15. package/dist/{handler-BSNmdHXv.js → handler-yA_knIf3.js} +4 -4
  16. package/dist/{handler-BSNmdHXv.js.map → handler-yA_knIf3.js.map} +1 -1
  17. package/dist/index-Buum9tIY.d.ts +230 -0
  18. package/dist/index-Buum9tIY.d.ts.map +1 -0
  19. package/dist/index.d.ts +3 -3
  20. package/dist/index.js +1 -1
  21. package/dist/options-CIBazDPp.d.ts.map +1 -1
  22. package/dist/protocol-vJhQHTcB.js +390 -0
  23. package/dist/protocol-vJhQHTcB.js.map +1 -0
  24. package/dist/{react-context-Caq1fodf.js → react-context-Kazo_JCz.js} +3 -14
  25. package/dist/react-context-Kazo_JCz.js.map +1 -0
  26. package/dist/react.d.ts +43 -3
  27. package/dist/react.d.ts.map +1 -1
  28. package/dist/react.js +389 -3
  29. package/dist/react.js.map +1 -1
  30. package/dist/selection-outline-NzAAAZXR.js +51 -0
  31. package/dist/selection-outline-NzAAAZXR.js.map +1 -0
  32. package/dist/server.js +1 -1
  33. package/dist/server.js.map +1 -1
  34. package/dist/sessions-panel-DVyWzZ3C.js +6011 -0
  35. package/dist/sessions-panel-DVyWzZ3C.js.map +1 -0
  36. package/dist/{submit-MvVjNdYc.js → submit-DYGZQVdG.js} +44 -70
  37. package/dist/submit-DYGZQVdG.js.map +1 -0
  38. package/dist/surveys.d.ts +3 -0
  39. package/dist/surveys.js +2 -0
  40. package/dist/team-activation-DCqh71bl.js +24 -0
  41. package/dist/team-activation-DCqh71bl.js.map +1 -0
  42. package/dist/team-layer-F-9BE4hc.js +423 -0
  43. package/dist/team-layer-F-9BE4hc.js.map +1 -0
  44. package/dist/{types-DnaDEN9Y.d.ts → types-B3XiHPqy.d.ts} +4 -9
  45. package/dist/types-B3XiHPqy.d.ts.map +1 -0
  46. package/dist/use-survey-session-LK6sN1Fg.js +1247 -0
  47. package/dist/use-survey-session-LK6sN1Fg.js.map +1 -0
  48. package/dist/z-index-CwokqVq_.js +15 -0
  49. package/dist/z-index-CwokqVq_.js.map +1 -0
  50. package/package.json +6 -2
  51. package/dist/client-DAdWdIFY.js.map +0 -1
  52. package/dist/client-DF1fdsSC.d.ts +0 -131
  53. package/dist/client-DF1fdsSC.d.ts.map +0 -1
  54. package/dist/devtools-layer-CEjzYGBB.js.map +0 -1
  55. package/dist/protocol-ClqTFzwR.js +0 -35
  56. package/dist/protocol-ClqTFzwR.js.map +0 -1
  57. package/dist/react-context-Caq1fodf.js.map +0 -1
  58. package/dist/submit-MvVjNdYc.js.map +0 -1
  59. package/dist/types-DnaDEN9Y.d.ts.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-survey-session-LK6sN1Fg.js","names":[],"sources":["../../survey-core/src/defaults.ts","../../survey-core/src/answers.ts","../../survey-core/src/flow.ts","../../survey-core/src/session.ts","../../survey-core/src/links.ts","../src/surveys/card/card-keyboard.ts","../src/surveys/card/card-styles.ts","../src/surveys/card/choice-fields.tsx","../src/surveys/card/scale-fields.tsx","../src/surveys/card/question-body.tsx","../src/surveys/card/survey-steps.tsx","../src/surveys/card/question-step.tsx","../src/surveys/card/survey-card.tsx","../src/surveys/client-transport.ts","../src/surveys/use-survey-session.ts"],"sourcesContent":["import type {\n LogicOperator,\n QuestionConfig,\n QuestionType,\n SurveyDisplay,\n SurveyEnding,\n} from \"./types\";\n\nexport const DEFAULT_ENDING: SurveyEnding = {\n description: \"Your answers have been recorded.\",\n id: \"default\",\n title: \"Thank you!\",\n};\n\nexport const DEFAULT_DISPLAY: SurveyDisplay = {\n frequency: \"once\",\n position: \"bottom_right\",\n};\n\n/** Wait between showings when a survey may be shown again. */\nexport const DEFAULT_RECONTACT_DAYS = 7;\nexport const DEFAULT_TIME_DELAY_MS = 5000;\nexport const DEFAULT_TEXT_MAX_CHARS = 1000;\nexport const MAX_OTHER_CHOICE_CHARS = 200;\nexport const NPS_MIN = 0;\nexport const NPS_MAX = 10;\nconst DEFAULT_RATING_MIN = 1;\nconst DEFAULT_RATING_MAX = 5;\n\n/** Every survey has at least one ending: stored endings, or the default thank-you. */\nexport const endingsOf = (\n endings: readonly SurveyEnding[] | undefined\n): SurveyEnding[] =>\n endings && endings.length > 0 ? [...endings] : [DEFAULT_ENDING];\n\nexport const displayOf = (display: SurveyDisplay | undefined): SurveyDisplay =>\n display ?? DEFAULT_DISPLAY;\n\nexport const ratingRange = (\n config: QuestionConfig | undefined\n): { max: number; min: number } => ({\n max: config?.maxValue ?? DEFAULT_RATING_MAX,\n min: config?.minValue ?? DEFAULT_RATING_MIN,\n});\n\nexport const textMaxChars = (config: QuestionConfig | undefined): number =>\n config?.maxLength ?? DEFAULT_TEXT_MAX_CHARS;\n\n/** Statements show a message and a button; they never collect an answer. */\nexport const takesAnswer = (type: QuestionType): boolean =>\n type !== \"statement\";\n\nconst NUMERIC_OPERATORS: readonly LogicOperator[] = [\n \"equals\",\n \"not_equals\",\n \"greater_than\",\n \"less_than\",\n \"answered\",\n \"skipped\",\n];\n\nexport const OPERATORS_BY_TYPE: Record<QuestionType, readonly LogicOperator[]> =\n {\n boolean: [\"equals\", \"skipped\"],\n multiple_choice: [\"includes\", \"answered\", \"skipped\"],\n nps: NUMERIC_OPERATORS,\n rating: NUMERIC_OPERATORS,\n single_choice: [\"equals\", \"not_equals\", \"answered\", \"skipped\"],\n statement: [],\n text: [\"includes\", \"answered\", \"skipped\"],\n };\n\n/** `answered` and `skipped` look only at whether an answer exists. */\nexport const operatorTakesValue = (operator: LogicOperator): boolean =>\n operator !== \"answered\" && operator !== \"skipped\";\n","import {\n MAX_OTHER_CHOICE_CHARS,\n NPS_MAX,\n NPS_MIN,\n ratingRange,\n textMaxChars,\n} from \"./defaults\";\nimport type { AnswerValue, QuestionConfig, SurveyQuestion } from \"./types\";\n\ntype AnsweredQuestion = Pick<SurveyQuestion, \"config\" | \"type\">;\n\nconst isIntegerBetween = (value: AnswerValue, min: number, max: number) =>\n typeof value === \"number\" &&\n Number.isInteger(value) &&\n value >= min &&\n value <= max;\n\n/** A choice outside the list is accepted only as the respondent's \"Other\" text. */\nconst choiceIssue = (\n choice: unknown,\n config: QuestionConfig | undefined\n): string | null => {\n if (typeof choice !== \"string\" || choice.trim() === \"\") {\n return \"Pick one of the options.\";\n }\n if (config?.choices?.includes(choice)) {\n return null;\n }\n if (!config?.allowOther) {\n return \"Pick one of the options.\";\n }\n return choice.length > MAX_OTHER_CHOICE_CHARS\n ? `Keep “Other” under ${MAX_OTHER_CHOICE_CHARS} characters.`\n : null;\n};\n\nconst multipleChoiceIssue = (\n value: AnswerValue,\n config: QuestionConfig | undefined\n): string | null => {\n if (!Array.isArray(value)) {\n return \"Pick at least one option.\";\n }\n if (new Set(value).size !== value.length) {\n return \"Each option can only be picked once.\";\n }\n const listed = config?.choices ?? [];\n const others = value.filter((choice) => !listed.includes(choice));\n if (others.length > 1) {\n return \"Only one “Other” answer is allowed.\";\n }\n return others.length === 1 ? choiceIssue(others[0], config) : null;\n};\n\n/** Why `value` can't be stored for `question`, or null when it's a valid answer. */\nexport const answerIssue = (\n question: AnsweredQuestion,\n value: AnswerValue\n): string | null => {\n switch (question.type) {\n case \"rating\": {\n const { max, min } = ratingRange(question.config);\n return isIntegerBetween(value, min, max)\n ? null\n : `Pick a rating from ${min} to ${max}.`;\n }\n case \"nps\":\n return isIntegerBetween(value, NPS_MIN, NPS_MAX)\n ? null\n : `Pick a score from ${NPS_MIN} to ${NPS_MAX}.`;\n case \"boolean\":\n return typeof value === \"boolean\" ? null : \"Answer yes or no.\";\n case \"text\": {\n if (typeof value !== \"string\") {\n return \"Type an answer.\";\n }\n const max = textMaxChars(question.config);\n return value.length > max\n ? `Keep your answer under ${max} characters.`\n : null;\n }\n case \"single_choice\":\n return choiceIssue(value, question.config);\n case \"multiple_choice\":\n return multipleChoiceIssue(value, question.config);\n case \"statement\":\n return \"This step doesn’t take an answer.\";\n default:\n return \"Unknown question type.\";\n }\n};\n","import { endingsOf, takesAnswer } from \"./defaults\";\nimport type {\n Answers,\n AnswerValue,\n FlowQuestion,\n FlowTarget,\n LogicRule,\n SurveyEnding,\n} from \"./types\";\n\nexport type FlowStep<Q extends FlowQuestion> =\n | { kind: \"question\"; question: Q }\n | { kind: \"ending\"; ending: SurveyEnding };\n\nexport interface FlowPath<Q extends FlowQuestion> {\n ending: SurveyEnding;\n questions: Q[];\n}\n\nexport const isEmptyAnswer = (value: AnswerValue | undefined): boolean => {\n if (value === undefined) {\n return true;\n }\n if (typeof value === \"string\") {\n return value.trim() === \"\";\n }\n if (Array.isArray(value)) {\n return value.length === 0;\n }\n return false;\n};\n\nconst includesValue = (answer: AnswerValue, expected: unknown): boolean => {\n if (typeof expected !== \"string\") {\n return false;\n }\n if (Array.isArray(answer)) {\n return answer.includes(expected);\n }\n return (\n typeof answer === \"string\" &&\n answer.toLowerCase().includes(expected.toLowerCase())\n );\n};\n\nconst compareNumbers = (\n answer: AnswerValue,\n expected: unknown,\n compare: (answer: number, expected: number) => boolean\n): boolean =>\n typeof answer === \"number\" &&\n typeof expected === \"number\" &&\n compare(answer, expected);\n\nexport const evaluateRule = (\n rule: Pick<LogicRule, \"operator\" | \"value\">,\n answer: AnswerValue | undefined\n): boolean => {\n const empty = isEmptyAnswer(answer);\n if (rule.operator === \"answered\") {\n return !empty;\n }\n if (rule.operator === \"skipped\") {\n return empty;\n }\n if (answer === undefined || empty) {\n return false;\n }\n switch (rule.operator) {\n case \"equals\":\n return answer === rule.value;\n case \"not_equals\":\n return answer !== rule.value;\n case \"greater_than\":\n return compareNumbers(answer, rule.value, (a, b) => a > b);\n case \"less_than\":\n return compareNumbers(answer, rule.value, (a, b) => a < b);\n case \"includes\":\n return includesValue(answer, rule.value);\n default:\n return false;\n }\n};\n\nexport const sortByOrder = <Q extends FlowQuestion>(\n questions: readonly Q[]\n): Q[] => [...questions].sort((a, b) => a.order - b.order);\n\n/** `endingsOf` never returns an empty list, so the first ending always exists. */\nconst firstEnding = (endings: readonly SurveyEnding[]): SurveyEnding =>\n endings[0] as SurveyEnding;\n\n/**\n * Jumps only go forward in question order, so every flow ends. A target that\n * points backward or at a deleted question is ignored and the default path wins.\n */\nconst stepForTarget = <Q extends FlowQuestion>(\n sorted: readonly Q[],\n from: Q,\n target: FlowTarget,\n endings: readonly SurveyEnding[]\n): FlowStep<Q> | null => {\n if (target.kind === \"ending\") {\n const ending =\n endings.find((candidate) => candidate.id === target.endingId) ??\n firstEnding(endings);\n return { ending, kind: \"ending\" };\n }\n const question = sorted.find((q) => q._id === target.questionId);\n return question && question.order > from.order\n ? { kind: \"question\", question }\n : null;\n};\n\nconst defaultStepAfter = <Q extends FlowQuestion>(\n sorted: readonly Q[],\n from: Q,\n endings: readonly SurveyEnding[]\n): FlowStep<Q> => {\n const following = sorted.find((q) => q.order > from.order);\n return following\n ? { kind: \"question\", question: following }\n : { ending: firstEnding(endings), kind: \"ending\" };\n};\n\nexport const firstStep = <Q extends FlowQuestion>(\n questions: readonly Q[],\n storedEndings?: readonly SurveyEnding[]\n): FlowStep<Q> => {\n const [first] = sortByOrder(questions);\n return first\n ? { kind: \"question\", question: first }\n : { ending: firstEnding(endingsOf(storedEndings)), kind: \"ending\" };\n};\n\nexport const resolveNextStep = <Q extends FlowQuestion>(\n questions: readonly Q[],\n current: Q,\n answer: AnswerValue | undefined,\n storedEndings?: readonly SurveyEnding[]\n): FlowStep<Q> => {\n const sorted = sortByOrder(questions);\n const endings = endingsOf(storedEndings);\n const matchedRule = takesAnswer(current.type)\n ? current.logic?.find((rule) => evaluateRule(rule, answer))\n : undefined;\n const target = matchedRule?.target ?? current.next;\n const jumped = target\n ? stepForTarget(sorted, current, target, endings)\n : null;\n return jumped ?? defaultStepAfter(sorted, current, endings);\n};\n\n/** Follows the flow from `start` using known answers; unanswered questions take their skipped path. */\nexport const walkFrom = <Q extends FlowQuestion>(\n questions: readonly Q[],\n start: FlowStep<Q>,\n answers: Answers,\n storedEndings?: readonly SurveyEnding[]\n): FlowPath<Q> => {\n const visited: Q[] = [];\n let step = start;\n while (step.kind === \"question\" && visited.length <= questions.length) {\n visited.push(step.question);\n step = resolveNextStep(\n questions,\n step.question,\n answers.get(step.question._id),\n storedEndings\n );\n }\n const ending =\n step.kind === \"ending\"\n ? step.ending\n : firstEnding(endingsOf(storedEndings));\n return { ending, questions: visited };\n};\n\nexport const walkPath = <Q extends FlowQuestion>(\n questions: readonly Q[],\n answers: Answers,\n storedEndings?: readonly SurveyEnding[]\n): FlowPath<Q> =>\n walkFrom(\n questions,\n firstStep(questions, storedEndings),\n answers,\n storedEndings\n );\n\n/** Required questions on the respondent's actual path that still have no answer. */\nexport const missingRequiredAnswers = <Q extends FlowQuestion>(\n questions: readonly Q[],\n answers: Answers,\n storedEndings?: readonly SurveyEnding[]\n): Q[] =>\n walkPath(questions, answers, storedEndings).questions.filter(\n (question) =>\n question.required &&\n takesAnswer(question.type) &&\n isEmptyAnswer(answers.get(question._id))\n );\n\nexport type FlowEdge =\n | { from: string; kind: \"default\"; target: FlowTarget }\n | { from: string; kind: \"rule\"; rule: LogicRule; target: FlowTarget };\n\nconst asTarget = <Q extends FlowQuestion>(step: FlowStep<Q>): FlowTarget =>\n step.kind === \"question\"\n ? { kind: \"question\", questionId: step.question._id }\n : { endingId: step.ending.id, kind: \"ending\" };\n\n/** Every connection a respondent can take, as the runtime would resolve it. */\nexport const flowEdges = <Q extends FlowQuestion>(\n questions: readonly Q[],\n storedEndings?: readonly SurveyEnding[]\n): FlowEdge[] => {\n const sorted = sortByOrder(questions);\n const endings = endingsOf(storedEndings);\n return sorted.flatMap((question): FlowEdge[] => {\n const ruleEdges: FlowEdge[] = takesAnswer(question.type)\n ? (question.logic ?? []).map((rule) => ({\n from: question._id,\n kind: \"rule\",\n rule,\n target: rule.target,\n }))\n : [];\n const defaultStep =\n (question.next &&\n stepForTarget(sorted, question, question.next, endings)) ||\n defaultStepAfter(sorted, question, endings);\n return [\n ...ruleEdges,\n { from: question._id, kind: \"default\", target: asTarget(defaultStep) },\n ];\n });\n};\n","import { answerIssue } from \"./answers\";\nimport { endingsOf, takesAnswer } from \"./defaults\";\nimport { firstStep, isEmptyAnswer, resolveNextStep, walkFrom } from \"./flow\";\nimport type {\n Answers,\n AnswerValue,\n PublicSurvey,\n SurveyEnding,\n SurveyQuestion,\n} from \"./types\";\n\n/** How a session talks to Reflet. `value: null` clears an answer the respondent emptied. */\nexport interface SurveyTransport {\n answer: (input: {\n questionId: string;\n responseId: string;\n value: AnswerValue | null;\n }) => Promise<unknown>;\n complete: (input: { responseId: string }) => Promise<unknown>;\n dismiss: (input: { responseId: string }) => Promise<unknown>;\n start: (input: { surveyId: string }) => Promise<{ responseId: string }>;\n}\n\nexport interface SurveySessionCallbacks {\n onAnswer?: (event: {\n questionId: string;\n surveyId: string;\n value: AnswerValue;\n }) => void;\n onComplete?: (event: {\n endingId: string;\n responseId: string;\n surveyId: string;\n }) => void;\n onDismiss?: (event: {\n answeredCount: number;\n responseId: string | null;\n surveyId: string;\n }) => void;\n onStart?: (event: { responseId: string; surveyId: string }) => void;\n}\n\nexport type SurveySessionPhase =\n | \"starting\"\n | \"start_failed\"\n | \"question\"\n | \"ending\"\n | \"dismissed\";\n\nexport interface SurveySessionSnapshot {\n answers: Answers;\n canGoBack: boolean;\n ending: SurveyEnding | null;\n error: string | null;\n /** The current answer leads straight to an ending, so the button submits. */\n isLastStep: boolean;\n isSubmitting: boolean;\n phase: SurveySessionPhase;\n /** 0–1, estimated along the path the current answers lead to. */\n progress: number;\n question: SurveyQuestion | null;\n}\n\nexport const REQUIRED_ANSWER_MESSAGE = \"This question needs an answer.\";\nexport const SAVE_FAILED_MESSAGE = \"Couldn’t save your answer. Try again.\";\nexport const START_FAILED_MESSAGE = \"Couldn’t load this survey. Try again.\";\n\nexport const previewTransport: SurveyTransport = {\n answer: () => Promise.resolve(),\n complete: () => Promise.resolve(),\n dismiss: () => Promise.resolve(),\n start: () => Promise.resolve({ responseId: \"preview\" }),\n};\n\nexport class SurveySession {\n readonly survey: PublicSurvey;\n private readonly transport: SurveyTransport;\n private readonly callbacks: SurveySessionCallbacks;\n private readonly endings: SurveyEnding[];\n private readonly listeners = new Set<() => void>();\n private readonly submitted = new Set<string>();\n private history: SurveyQuestion[] = [];\n private answers: Map<string, AnswerValue> = new Map();\n private current: SurveyQuestion | null = null;\n private ending: SurveyEnding | null = null;\n private error: string | null = null;\n private isSubmitting = false;\n private phase: SurveySessionPhase = \"starting\";\n private responseId: string | null = null;\n private snapshot: SurveySessionSnapshot;\n\n constructor(options: {\n callbacks?: SurveySessionCallbacks;\n survey: PublicSurvey;\n transport: SurveyTransport;\n }) {\n this.survey = options.survey;\n this.transport = options.transport;\n this.callbacks = options.callbacks ?? {};\n this.endings = endingsOf(options.survey.endings);\n this.snapshot = this.buildSnapshot();\n }\n\n readonly subscribe = (listener: () => void): (() => void) => {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n };\n\n readonly getSnapshot = (): SurveySessionSnapshot => this.snapshot;\n\n /** A method, not a field read, so TS doesn't narrow `phase` across awaits. */\n private isDismissed(): boolean {\n return this.phase === \"dismissed\";\n }\n\n async start(): Promise<void> {\n if (this.phase !== \"starting\" && this.phase !== \"start_failed\") {\n return;\n }\n this.update({ error: null, phase: \"starting\" });\n try {\n const { responseId } = await this.transport.start({\n surveyId: this.survey._id,\n });\n this.responseId = responseId;\n if (this.isDismissed()) {\n this.transport.dismiss({ responseId }).catch(() => undefined);\n return;\n }\n this.callbacks.onStart?.({ responseId, surveyId: this.survey._id });\n const step = firstStep(this.survey.questions, this.endings);\n if (step.kind === \"question\") {\n this.current = step.question;\n this.update({ phase: \"question\" });\n } else {\n await this.finish(step.ending);\n }\n } catch {\n if (!this.isDismissed()) {\n this.update({ error: START_FAILED_MESSAGE, phase: \"start_failed\" });\n }\n }\n }\n\n setAnswer(value: AnswerValue | undefined): void {\n if (!this.current || this.phase !== \"question\") {\n return;\n }\n const answers = new Map(this.answers);\n if (value === undefined) {\n answers.delete(this.current._id);\n } else {\n answers.set(this.current._id, value);\n }\n this.answers = answers;\n this.update({ error: null });\n }\n\n async next(): Promise<void> {\n const question = this.current;\n const responseId = this.responseId;\n if (!(question && responseId) || this.isSubmitting) {\n return;\n }\n const value = this.answers.get(question._id);\n const validationError = this.validate(question, value);\n if (validationError) {\n this.update({ error: validationError });\n return;\n }\n this.update({ error: null, isSubmitting: true });\n try {\n await this.saveAnswer(question, responseId, value);\n if (this.isDismissed()) {\n return;\n }\n const step = resolveNextStep(\n this.survey.questions,\n question,\n value,\n this.endings\n );\n if (step.kind === \"question\") {\n this.history = [...this.history, question];\n this.current = step.question;\n this.update({ isSubmitting: false });\n } else {\n await this.finish(step.ending);\n }\n } catch {\n if (!this.isDismissed()) {\n this.update({ error: SAVE_FAILED_MESSAGE, isSubmitting: false });\n }\n }\n }\n\n back(): void {\n const previous = this.history.at(-1);\n if (!previous || this.isSubmitting || this.phase !== \"question\") {\n return;\n }\n this.history = this.history.slice(0, -1);\n this.current = previous;\n this.update({ error: null });\n }\n\n dismiss(): void {\n if (this.phase === \"dismissed\") {\n return;\n }\n const closesAfterEnding = this.phase === \"ending\";\n if (this.phase === \"question\" && this.responseId) {\n this.transport\n .dismiss({ responseId: this.responseId })\n .catch(() => undefined);\n }\n if (!closesAfterEnding) {\n this.callbacks.onDismiss?.({\n answeredCount: this.submitted.size,\n responseId: this.responseId,\n surveyId: this.survey._id,\n });\n }\n this.update({ phase: \"dismissed\" });\n }\n\n private validate(\n question: SurveyQuestion,\n value: AnswerValue | undefined\n ): string | null {\n if (!takesAnswer(question.type)) {\n return null;\n }\n if (value === undefined || isEmptyAnswer(value)) {\n return question.required ? REQUIRED_ANSWER_MESSAGE : null;\n }\n return answerIssue(question, value);\n }\n\n private async saveAnswer(\n question: SurveyQuestion,\n responseId: string,\n value: AnswerValue | undefined\n ): Promise<void> {\n if (!takesAnswer(question.type)) {\n return;\n }\n if (value !== undefined && !isEmptyAnswer(value)) {\n await this.transport.answer({\n questionId: question._id,\n responseId,\n value,\n });\n this.submitted.add(question._id);\n this.callbacks.onAnswer?.({\n questionId: question._id,\n surveyId: this.survey._id,\n value,\n });\n return;\n }\n if (this.submitted.has(question._id)) {\n await this.transport.answer({\n questionId: question._id,\n responseId,\n value: null,\n });\n this.submitted.delete(question._id);\n }\n }\n\n private async finish(ending: SurveyEnding): Promise<void> {\n const responseId = this.responseId;\n if (!responseId) {\n return;\n }\n await this.transport.complete({ responseId });\n if (this.isDismissed()) {\n return;\n }\n this.ending = ending;\n this.current = null;\n this.update({ isSubmitting: false, phase: \"ending\" });\n this.callbacks.onComplete?.({\n endingId: ending.id,\n responseId,\n surveyId: this.survey._id,\n });\n }\n\n private update(patch: {\n error?: string | null;\n isSubmitting?: boolean;\n phase?: SurveySessionPhase;\n }): void {\n if (patch.error !== undefined) {\n this.error = patch.error;\n }\n if (patch.isSubmitting !== undefined) {\n this.isSubmitting = patch.isSubmitting;\n }\n if (patch.phase !== undefined) {\n this.phase = patch.phase;\n }\n this.snapshot = this.buildSnapshot();\n for (const listener of this.listeners) {\n listener();\n }\n }\n\n private buildSnapshot(): SurveySessionSnapshot {\n const question = this.phase === \"question\" ? this.current : null;\n const answered = this.history.length;\n const remaining = question\n ? walkFrom(\n this.survey.questions,\n { kind: \"question\", question },\n this.answers,\n this.endings\n ).questions.length\n : 0;\n const isLastStep = question\n ? resolveNextStep(\n this.survey.questions,\n question,\n this.answers.get(question._id),\n this.endings\n ).kind === \"ending\"\n : false;\n return {\n answers: this.answers,\n canGoBack: question !== null && answered > 0,\n ending: this.phase === \"ending\" ? this.ending : null,\n error: this.error,\n isLastStep,\n isSubmitting: this.isSubmitting,\n phase: this.phase,\n progress:\n this.phase === \"ending\" ? 1 : answered / (answered + remaining || 1),\n question,\n };\n }\n}\n","const RELATIVE_LINK_BASE = \"https://relative.invalid\";\n\n/** Only http(s) or relative links reach a button href: survey content is rendered on customer sites. */\nexport const safeLinkUrl = (url: string | undefined): string | null => {\n if (!url) {\n return null;\n }\n try {\n const { protocol } = new URL(url, RELATIVE_LINK_BASE);\n return protocol === \"http:\" || protocol === \"https:\" ? url : null;\n } catch {\n return null;\n }\n};\n","import { ratingRange, type SurveyQuestion } from \"@reflet/survey-core\";\n\nexport type CardKeyAction =\n | { kind: \"close\" }\n | { kind: \"next\" }\n | { kind: \"pick\"; value: boolean | number | string };\n\nconst DIGIT = /^\\d$/;\n\nconst pickByKey = (\n key: string,\n question: SurveyQuestion\n): CardKeyAction | null => {\n if (question.type === \"boolean\") {\n const lower = key.toLowerCase();\n if (lower === \"y\") {\n return { kind: \"pick\", value: true };\n }\n return lower === \"n\" ? { kind: \"pick\", value: false } : null;\n }\n if (!DIGIT.test(key)) {\n return null;\n }\n const digit = Number(key);\n if (question.type === \"nps\") {\n return { kind: \"pick\", value: digit };\n }\n if (question.type === \"rating\") {\n const { max, min } = ratingRange(question.config);\n return digit >= min && digit <= max ? { kind: \"pick\", value: digit } : null;\n }\n if (question.type === \"single_choice\") {\n const choice = question.config?.choices?.[digit - 1];\n return choice === undefined ? null : { kind: \"pick\", value: choice };\n }\n return null;\n};\n\n/**\n * Enter moves on, Escape closes, and digits (or Y/N) answer single-click\n * questions — except while typing, where keys belong to the field.\n */\nexport const cardKeyAction = (\n event: {\n ctrlKey: boolean;\n key: string;\n metaKey: boolean;\n target: EventTarget;\n },\n question: SurveyQuestion | null\n): CardKeyAction | null => {\n const { target } = event;\n if (event.key === \"Escape\") {\n return { kind: \"close\" };\n }\n const isTextArea = target instanceof HTMLTextAreaElement;\n const isTextInput = target instanceof HTMLInputElement;\n if (event.key === \"Enter\") {\n if (isTextArea) {\n return event.metaKey || event.ctrlKey ? { kind: \"next\" } : null;\n }\n const isActivatable =\n target instanceof HTMLButtonElement ||\n target instanceof HTMLAnchorElement;\n return isActivatable || !question ? null : { kind: \"next\" };\n }\n if (isTextArea || isTextInput || !question) {\n return null;\n }\n return pickByKey(event.key, question);\n};\n","const LIGHT_TOKENS = `\n --rfs-bg: #ffffff;\n --rfs-text: #18181b;\n --rfs-muted: #71717a;\n --rfs-border: rgb(24 24 27 / 10%);\n --rfs-subtle: rgb(24 24 27 / 4%);\n --rfs-hover: rgb(24 24 27 / 7%);\n --rfs-danger: #dc2626;\n --rfs-success: #16a34a;\n --rfs-primary: var(--rf-survey-primary, #18181b);\n --rfs-primary-text: var(--rf-survey-primary-text, #ffffff);\n --rfs-shadow: 0 0 0 1px var(--rfs-border), 0 2px 6px -1px rgb(15 15 15 / 8%), 0 18px 44px -14px rgb(15 15 15 / 24%);\n color-scheme: light;\n`;\n\nconst DARK_TOKENS = `\n --rfs-bg: #1c1c1f;\n --rfs-text: #f4f4f5;\n --rfs-muted: #a1a1aa;\n --rfs-border: rgb(255 255 255 / 10%);\n --rfs-subtle: rgb(255 255 255 / 5%);\n --rfs-hover: rgb(255 255 255 / 9%);\n --rfs-danger: #f87171;\n --rfs-success: #4ade80;\n --rfs-primary: var(--rf-survey-primary, #fafafa);\n --rfs-primary-text: var(--rf-survey-primary-text, #18181b);\n --rfs-shadow: 0 0 0 1px var(--rfs-border), 0 2px 6px -1px rgb(0 0 0 / 30%), 0 18px 44px -14px rgb(0 0 0 / 60%);\n color-scheme: dark;\n`;\n\n/** Every selector hangs off `.rf-survey`, so the card looks the same in a shadow root and in a page. */\nexport const SURVEY_CARD_STYLES = `\n.rf-survey {\n ${LIGHT_TOKENS}\n --rfs-radius: 14px;\n --rfs-control-radius: 9px;\n --rfs-ease: cubic-bezier(0.16, 1, 0.3, 1);\n position: relative;\n box-sizing: border-box;\n width: 100%;\n padding: 20px;\n border-radius: var(--rfs-radius);\n background: var(--rfs-bg);\n color: var(--rfs-text);\n font-family: ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif;\n font-size: 14px;\n line-height: 1.45;\n text-align: left;\n -webkit-font-smoothing: antialiased;\n overflow: hidden;\n}\n.rf-survey[data-theme=\"dark\"] { ${DARK_TOKENS} }\n@media (prefers-color-scheme: dark) {\n .rf-survey[data-theme=\"auto\"] { ${DARK_TOKENS} }\n}\n.rf-survey *, .rf-survey *::before, .rf-survey *::after { box-sizing: border-box; }\n.rf-survey :where(h2, p) { margin: 0; }\n.rf-survey :where(fieldset) { min-width: 0; margin: 0; padding: 0; border: 0; }\n.rf-survey :where(button, input, textarea, a) {\n margin: 0;\n font: inherit;\n color: inherit;\n letter-spacing: inherit;\n}\n.rf-survey :where(button) { cursor: pointer; background: none; border: 0; padding: 0; }\n.rf-survey :where(button:disabled) { cursor: default; }\n.rf-survey :where(button, a, input, textarea):focus-visible {\n outline: 2px solid var(--rfs-primary);\n outline-offset: 2px;\n}\n.rf-survey[data-variant=\"floating\"] { box-shadow: var(--rfs-shadow); animation: rfs-enter 360ms var(--rfs-ease); }\n.rf-survey[data-variant=\"inline\"] { box-shadow: 0 0 0 1px var(--rfs-border); }\n.rf-survey[data-variant=\"page\"] {\n max-width: 560px;\n margin: 0 auto;\n padding: 32px;\n box-shadow: var(--rfs-shadow);\n font-size: 15px;\n}\n.rfs-progress { position: absolute; inset: 0 0 auto; height: 3px; background: var(--rfs-subtle); }\n.rfs-progress-fill {\n height: 100%;\n background: var(--rfs-primary);\n transform-origin: left;\n transition: transform 400ms var(--rfs-ease);\n}\n.rfs-close {\n position: absolute;\n top: 12px;\n right: 12px;\n display: grid;\n place-items: center;\n width: 28px;\n height: 28px;\n border-radius: 8px;\n color: var(--rfs-muted);\n transition: background-color 150ms ease, color 150ms ease;\n}\n.rfs-close:hover { background: var(--rfs-hover); color: var(--rfs-text); }\n.rfs-step { display: flex; flex-direction: column; gap: 14px; animation: rfs-step-in 280ms var(--rfs-ease); }\n.rfs-heading { display: flex; flex-direction: column; gap: 4px; padding-right: 28px; }\n.rfs-title { font-size: 1.07em; font-weight: 600; line-height: 1.35; text-wrap: balance; outline: none; }\n.rfs-description { color: var(--rfs-muted); text-wrap: pretty; white-space: pre-line; }\n.rfs-scale { display: flex; gap: 6px; }\n.rfs-scale[data-kind=\"nps\"] { display: grid; grid-template-columns: repeat(11, minmax(0, 1fr)); gap: 4px; }\n.rfs-scale-option {\n flex: 1;\n min-width: 0;\n height: 40px;\n border-radius: var(--rfs-control-radius);\n background: var(--rfs-subtle);\n box-shadow: inset 0 0 0 1px var(--rfs-border);\n font-weight: 500;\n font-variant-numeric: tabular-nums;\n transition: background-color 150ms ease, color 150ms ease, transform 150ms var(--rfs-ease);\n}\n.rfs-scale[data-kind=\"nps\"] .rfs-scale-option { height: 36px; font-size: 13px; }\n.rfs-scale-option:hover { background: var(--rfs-hover); }\n.rfs-scale-option:active { transform: scale(0.96); }\n.rfs-scale-option[aria-pressed=\"true\"] { background: var(--rfs-primary); color: var(--rfs-primary-text); box-shadow: none; }\n.rfs-scale[data-style=\"star\"], .rfs-scale[data-style=\"emoji\"] { gap: 2px; justify-content: center; }\n.rfs-scale[data-style=\"star\"] .rfs-scale-option,\n.rfs-scale[data-style=\"emoji\"] .rfs-scale-option {\n flex: 0 1 44px;\n height: 44px;\n background: none;\n box-shadow: none;\n color: var(--rfs-border);\n}\n.rfs-scale[data-style=\"star\"] .rfs-scale-option[data-lit=\"true\"] { color: #f5a524; }\n.rfs-scale[data-style=\"emoji\"] .rfs-scale-option { font-size: 26px; filter: grayscale(1); opacity: 0.55; }\n.rfs-scale[data-style=\"emoji\"] .rfs-scale-option:hover,\n.rfs-scale[data-style=\"emoji\"] .rfs-scale-option[aria-pressed=\"true\"] { filter: none; opacity: 1; transform: scale(1.12); background: none; }\n.rfs-scale-labels { display: flex; justify-content: space-between; gap: 12px; margin-top: -6px; color: var(--rfs-muted); font-size: 12px; }\n.rfs-choices { display: flex; flex-direction: column; gap: 6px; }\n.rfs-choice {\n display: flex;\n align-items: center;\n gap: 10px;\n width: 100%;\n min-height: 40px;\n padding: 8px 12px;\n border-radius: var(--rfs-control-radius);\n background: var(--rfs-subtle);\n box-shadow: inset 0 0 0 1px var(--rfs-border);\n text-align: left;\n transition: background-color 150ms ease, box-shadow 150ms ease;\n}\n.rfs-choice:hover { background: var(--rfs-hover); }\n.rfs-choice[aria-pressed=\"true\"] { box-shadow: inset 0 0 0 1.5px var(--rfs-primary); background: var(--rfs-bg); }\n.rfs-indicator {\n flex: none;\n display: grid;\n place-items: center;\n width: 16px;\n height: 16px;\n border-radius: 999px;\n box-shadow: inset 0 0 0 1.5px var(--rfs-muted);\n color: var(--rfs-primary-text);\n transition: background-color 150ms ease, box-shadow 150ms ease;\n}\n.rfs-indicator[data-shape=\"square\"] { border-radius: 5px; }\n.rfs-choice[aria-pressed=\"true\"] .rfs-indicator { background: var(--rfs-primary); box-shadow: none; }\n.rfs-boolean { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }\n.rfs-boolean .rfs-choice { justify-content: center; font-weight: 500; }\n.rfs-input {\n display: block;\n width: 100%;\n padding: 10px 12px;\n border: 0;\n border-radius: var(--rfs-control-radius);\n background: var(--rfs-subtle);\n box-shadow: inset 0 0 0 1px var(--rfs-border);\n resize: vertical;\n transition: box-shadow 150ms ease;\n}\n.rfs-input::placeholder { color: var(--rfs-muted); }\n.rfs-input:focus { outline: none; box-shadow: inset 0 0 0 1.5px var(--rfs-primary); }\n.rfs-input-wrap { display: flex; flex-direction: column; gap: 4px; }\n.rfs-counter { align-self: flex-end; color: var(--rfs-muted); font-size: 12px; font-variant-numeric: tabular-nums; }\n.rfs-counter[data-full=\"true\"] { color: var(--rfs-danger); }\n.rfs-error { color: var(--rfs-danger); font-size: 13px; }\n.rfs-footer { display: flex; align-items: center; justify-content: flex-end; gap: 8px; margin-top: 2px; }\n.rfs-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n height: 36px;\n padding: 0 16px;\n border-radius: var(--rfs-control-radius);\n font-weight: 500;\n text-decoration: none;\n transition: background-color 150ms ease, opacity 150ms ease, transform 150ms var(--rfs-ease);\n}\n.rfs-button:active:not(:disabled) { transform: scale(0.97); }\n.rfs-button[data-tone=\"primary\"] { background: var(--rfs-primary); color: var(--rfs-primary-text); }\n.rfs-button[data-tone=\"primary\"]:hover:not(:disabled) { opacity: 0.88; }\n.rfs-button[data-tone=\"primary\"]:disabled { opacity: 0.55; }\n.rfs-button[data-tone=\"ghost\"] { margin-right: auto; color: var(--rfs-muted); }\n.rfs-button[data-tone=\"ghost\"]:hover { background: var(--rfs-hover); color: var(--rfs-text); }\n.rfs-ending { align-items: center; padding: 8px 0 4px; text-align: center; }\n.rfs-ending .rfs-heading { padding-right: 0; align-items: center; }\n.rfs-ending-mark {\n display: grid;\n place-items: center;\n width: 40px;\n height: 40px;\n border-radius: 999px;\n background: var(--rfs-subtle);\n color: var(--rfs-success);\n animation: rfs-pop 420ms var(--rfs-ease);\n}\n.rfs-skeleton { display: flex; flex-direction: column; gap: 10px; }\n.rfs-skeleton-line { height: 12px; border-radius: 6px; background: var(--rfs-subtle); animation: rfs-pulse 1.4s ease-in-out infinite; }\n.rfs-skeleton-line:first-child { width: 70%; height: 16px; }\n@keyframes rfs-enter { from { opacity: 0; transform: translateY(12px) scale(0.98); } }\n@keyframes rfs-step-in { from { opacity: 0; transform: translateX(8px); } }\n@keyframes rfs-pop { from { opacity: 0; transform: scale(0.6); } }\n@keyframes rfs-pulse { 50% { opacity: 0.5; } }\n@media (prefers-reduced-motion: reduce) {\n .rf-survey, .rf-survey * { animation: none !important; transition: none !important; }\n}\n`;\n","import {\n type AnswerValue,\n MAX_OTHER_CHOICE_CHARS,\n type SurveyQuestion,\n} from \"@reflet/survey-core\";\nimport { useEffect, useRef, useState } from \"react\";\n\nconst OTHER_LABEL = \"Other\";\n\ninterface ChoiceFieldProps {\n labelledBy: string;\n onChange: (value: AnswerValue | undefined) => void;\n onPick: (value: AnswerValue) => void;\n question: SurveyQuestion;\n value: AnswerValue | undefined;\n}\n\nfunction CheckIcon() {\n return (\n <svg\n aria-hidden=\"true\"\n fill=\"none\"\n height=\"10\"\n stroke=\"currentColor\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth=\"3\"\n viewBox=\"0 0 24 24\"\n width=\"10\"\n >\n <path d=\"M5 12.5l4.5 4.5L19 7.5\" />\n </svg>\n );\n}\n\nfunction ChoiceOption({\n checked,\n label,\n multiple,\n onSelect,\n}: {\n checked: boolean;\n label: string;\n multiple: boolean;\n onSelect: () => void;\n}) {\n return (\n <button\n aria-pressed={checked}\n className=\"rfs-choice\"\n onClick={onSelect}\n type=\"button\"\n >\n <span\n className=\"rfs-indicator\"\n data-shape={multiple ? \"square\" : \"round\"}\n >\n {checked && <CheckIcon />}\n </span>\n {label}\n </button>\n );\n}\n\nfunction OtherInput({\n onChange,\n value,\n}: {\n onChange: (text: string) => void;\n value: string;\n}) {\n const inputRef = useRef<HTMLInputElement>(null);\n\n useEffect(() => {\n inputRef.current?.focus({ preventScroll: true });\n }, []);\n\n return (\n <input\n aria-label=\"Other answer\"\n className=\"rfs-input\"\n maxLength={MAX_OTHER_CHOICE_CHARS}\n onChange={(event) => onChange(event.target.value)}\n placeholder=\"Type your answer…\"\n ref={inputRef}\n type=\"text\"\n value={value}\n />\n );\n}\n\nexport function SingleChoiceField({\n labelledBy,\n onChange,\n onPick,\n question,\n value,\n}: ChoiceFieldProps) {\n const choices = question.config?.choices ?? [];\n const typedOther =\n typeof value === \"string\" && !choices.includes(value) ? value : \"\";\n const [otherOpen, setOtherOpen] = useState(typedOther !== \"\");\n\n return (\n <fieldset aria-labelledby={labelledBy} className=\"rfs-choices\">\n {choices.map((choice) => (\n <ChoiceOption\n checked={!otherOpen && value === choice}\n key={choice}\n label={choice}\n multiple={false}\n onSelect={() => {\n setOtherOpen(false);\n onPick(choice);\n }}\n />\n ))}\n {question.config?.allowOther && (\n <ChoiceOption\n checked={otherOpen}\n label={OTHER_LABEL}\n multiple={false}\n onSelect={() => {\n setOtherOpen(true);\n onChange(typedOther || undefined);\n }}\n />\n )}\n {otherOpen && (\n <OtherInput\n onChange={(text) => onChange(text || undefined)}\n value={typedOther}\n />\n )}\n </fieldset>\n );\n}\n\nexport function MultipleChoiceField({\n labelledBy,\n onChange,\n question,\n value,\n}: Omit<ChoiceFieldProps, \"onPick\">) {\n const choices = question.config?.choices ?? [];\n const picked = Array.isArray(value) ? value : [];\n const listed = picked.filter((choice) => choices.includes(choice));\n const typedOther = picked.find((choice) => !choices.includes(choice)) ?? \"\";\n const [otherOpen, setOtherOpen] = useState(typedOther !== \"\");\n\n const commit = (nextListed: string[], other: string) => {\n const next = other ? [...nextListed, other] : nextListed;\n onChange(next.length > 0 ? next : undefined);\n };\n\n return (\n <fieldset aria-labelledby={labelledBy} className=\"rfs-choices\">\n {choices.map((choice) => {\n const checked = listed.includes(choice);\n return (\n <ChoiceOption\n checked={checked}\n key={choice}\n label={choice}\n multiple={true}\n onSelect={() =>\n commit(\n checked\n ? listed.filter((item) => item !== choice)\n : choices.filter(\n (item) => item === choice || listed.includes(item)\n ),\n typedOther\n )\n }\n />\n );\n })}\n {question.config?.allowOther && (\n <ChoiceOption\n checked={otherOpen}\n label={OTHER_LABEL}\n multiple={true}\n onSelect={() => {\n setOtherOpen(!otherOpen);\n commit(listed, otherOpen ? \"\" : typedOther);\n }}\n />\n )}\n {otherOpen && (\n <OtherInput\n onChange={(text) => commit(listed, text)}\n value={typedOther}\n />\n )}\n </fieldset>\n );\n}\n\nexport function BooleanField({\n labelledBy,\n onPick,\n value,\n}: Pick<ChoiceFieldProps, \"labelledBy\" | \"onPick\" | \"value\">) {\n return (\n <fieldset aria-labelledby={labelledBy} className=\"rfs-boolean\">\n {[\n { answer: true, label: \"Yes\" },\n { answer: false, label: \"No\" },\n ].map(({ answer, label }) => (\n <button\n aria-pressed={value === answer}\n className=\"rfs-choice\"\n key={label}\n onClick={() => onPick(answer)}\n type=\"button\"\n >\n {label}\n </button>\n ))}\n </fieldset>\n );\n}\n","import {\n NPS_MAX,\n NPS_MIN,\n ratingRange,\n type SurveyQuestion,\n} from \"@reflet/survey-core\";\nimport { useState } from \"react\";\n\nconst EMOJI_FACES = [\"😞\", \"🙁\", \"😐\", \"🙂\", \"😄\"] as const;\nconst DEFAULT_NPS_MIN_LABEL = \"Not likely\";\nconst DEFAULT_NPS_MAX_LABEL = \"Very likely\";\n\ninterface ScaleFieldProps {\n labelledBy: string;\n onPick: (value: number) => void;\n question: SurveyQuestion;\n value: number | undefined;\n}\n\nconst valuesBetween = (min: number, max: number): number[] =>\n Array.from({ length: max - min + 1 }, (_, index) => min + index);\n\nfunction StarIcon() {\n return (\n <svg\n aria-hidden=\"true\"\n fill=\"currentColor\"\n height=\"28\"\n viewBox=\"0 0 24 24\"\n width=\"28\"\n >\n <path d=\"M12 2.8l2.83 5.74 6.33.92-4.58 4.46 1.08 6.3L12 17.25l-5.66 2.97 1.08-6.3L2.84 9.46l6.33-.92z\" />\n </svg>\n );\n}\n\nfunction ScaleLabels({ max, min }: { max?: string; min?: string }) {\n if (!(min || max)) {\n return null;\n }\n return (\n <div aria-hidden=\"true\" className=\"rfs-scale-labels\">\n <span>{min}</span>\n <span>{max}</span>\n </div>\n );\n}\n\nexport function RatingField({\n labelledBy,\n onPick,\n question,\n value,\n}: ScaleFieldProps) {\n const [hovered, setHovered] = useState<number | null>(null);\n const { max, min } = ratingRange(question.config);\n const style = question.config?.ratingStyle ?? \"number\";\n const values = valuesBetween(min, max);\n const lit = hovered ?? value ?? Number.NEGATIVE_INFINITY;\n\n const faceFor = (option: number) => {\n const step = values.length > 1 ? (option - min) / (max - min) : 1;\n return EMOJI_FACES[Math.round(step * (EMOJI_FACES.length - 1))];\n };\n\n return (\n <>\n <fieldset\n aria-labelledby={labelledBy}\n className=\"rfs-scale\"\n data-style={style}\n >\n {values.map((option) => (\n <button\n aria-label={`${option} of ${max}`}\n aria-pressed={value === option}\n className=\"rfs-scale-option\"\n data-lit={option <= lit}\n key={option}\n onClick={() => onPick(option)}\n onPointerEnter={() => setHovered(option)}\n onPointerLeave={() => setHovered(null)}\n type=\"button\"\n >\n {style === \"number\" && option}\n {style === \"star\" && <StarIcon />}\n {style === \"emoji\" && faceFor(option)}\n </button>\n ))}\n </fieldset>\n <ScaleLabels\n max={question.config?.maxLabel}\n min={question.config?.minLabel}\n />\n </>\n );\n}\n\nexport function NpsField({\n labelledBy,\n onPick,\n question,\n value,\n}: ScaleFieldProps) {\n return (\n <>\n <fieldset\n aria-labelledby={labelledBy}\n className=\"rfs-scale\"\n data-kind=\"nps\"\n >\n {valuesBetween(NPS_MIN, NPS_MAX).map((option) => (\n <button\n aria-pressed={value === option}\n className=\"rfs-scale-option\"\n key={option}\n onClick={() => onPick(option)}\n type=\"button\"\n >\n {option}\n </button>\n ))}\n </fieldset>\n <ScaleLabels\n max={question.config?.maxLabel ?? DEFAULT_NPS_MAX_LABEL}\n min={question.config?.minLabel ?? DEFAULT_NPS_MIN_LABEL}\n />\n </>\n );\n}\n","import {\n type AnswerValue,\n type SurveyQuestion,\n textMaxChars,\n} from \"@reflet/survey-core\";\nimport {\n BooleanField,\n MultipleChoiceField,\n SingleChoiceField,\n} from \"./choice-fields\";\nimport { NpsField, RatingField } from \"./scale-fields\";\n\ninterface QuestionBodyProps {\n labelledBy: string;\n onChange: (value: AnswerValue | undefined) => void;\n /** Answer and move on: single-click questions don't need a Next press. */\n onPick: (value: AnswerValue) => void;\n question: SurveyQuestion;\n value: AnswerValue | undefined;\n}\n\nfunction TextField({\n labelledBy,\n onChange,\n question,\n value,\n}: Omit<QuestionBodyProps, \"onPick\">) {\n const max = textMaxChars(question.config);\n const text = typeof value === \"string\" ? value : \"\";\n return (\n <div className=\"rfs-input-wrap\">\n <textarea\n aria-labelledby={labelledBy}\n className=\"rfs-input\"\n maxLength={max}\n onChange={(event) => onChange(event.target.value || undefined)}\n placeholder={question.config?.placeholder ?? \"Type your answer…\"}\n rows={3}\n value={text}\n />\n <span className=\"rfs-counter\" data-full={text.length >= max}>\n {text.length}/{max}\n </span>\n </div>\n );\n}\n\nexport function QuestionBody(props: QuestionBodyProps) {\n const { labelledBy, onPick, question, value } = props;\n const numericValue = typeof value === \"number\" ? value : undefined;\n switch (question.type) {\n case \"rating\":\n return (\n <RatingField\n labelledBy={labelledBy}\n onPick={onPick}\n question={question}\n value={numericValue}\n />\n );\n case \"nps\":\n return (\n <NpsField\n labelledBy={labelledBy}\n onPick={onPick}\n question={question}\n value={numericValue}\n />\n );\n case \"single_choice\":\n return <SingleChoiceField {...props} />;\n case \"multiple_choice\":\n return <MultipleChoiceField {...props} />;\n case \"boolean\":\n return <BooleanField {...props} />;\n case \"text\":\n return <TextField {...props} />;\n default:\n return null;\n }\n}\n","import { type SurveyEnding, safeLinkUrl } from \"@reflet/survey-core\";\nimport type { ReactNode, RefObject } from \"react\";\n\nexport const DEFAULT_BUTTON_LABEL = \"Continue\";\n\nexport function StepHeading({\n description,\n headingId,\n headingRef,\n title,\n}: {\n description?: string;\n headingId: string;\n headingRef: RefObject<HTMLHeadingElement | null>;\n title: string;\n}) {\n return (\n <div className=\"rfs-heading\">\n <h2 className=\"rfs-title\" id={headingId} ref={headingRef} tabIndex={-1}>\n {title}\n </h2>\n {description && <p className=\"rfs-description\">{description}</p>}\n </div>\n );\n}\n\nexport function ActionButton({\n children,\n disabled,\n onClick,\n url,\n}: {\n children: ReactNode;\n disabled?: boolean;\n onClick: () => void;\n url?: string;\n}) {\n const safeUrl = safeLinkUrl(url);\n if (safeUrl) {\n return (\n <a\n className=\"rfs-button\"\n data-tone=\"primary\"\n href={safeUrl}\n onClick={onClick}\n rel=\"noopener noreferrer\"\n target=\"_blank\"\n >\n {children}\n </a>\n );\n }\n return (\n <button\n className=\"rfs-button\"\n data-tone=\"primary\"\n disabled={disabled}\n onClick={onClick}\n type=\"button\"\n >\n {children}\n </button>\n );\n}\n\nfunction SuccessMark() {\n return (\n <span className=\"rfs-ending-mark\">\n <svg\n aria-hidden=\"true\"\n fill=\"none\"\n height=\"20\"\n stroke=\"currentColor\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth=\"2.5\"\n viewBox=\"0 0 24 24\"\n width=\"20\"\n >\n <path d=\"M5 12.5l4.5 4.5L19 7.5\" />\n </svg>\n </span>\n );\n}\n\nexport function EndingStep({\n ending,\n headingId,\n headingRef,\n onClose,\n}: {\n ending: SurveyEnding;\n headingId: string;\n headingRef: RefObject<HTMLHeadingElement | null>;\n onClose: () => void;\n}) {\n const hasButton = Boolean(ending.buttonUrl || ending.buttonLabel);\n return (\n <div className=\"rfs-step rfs-ending\">\n <SuccessMark />\n <StepHeading\n description={ending.description}\n headingId={headingId}\n headingRef={headingRef}\n title={ending.title}\n />\n {hasButton && (\n <ActionButton onClick={onClose} url={ending.buttonUrl}>\n {ending.buttonLabel ?? DEFAULT_BUTTON_LABEL}\n </ActionButton>\n )}\n </div>\n );\n}\n\nexport function LoadingStep() {\n return (\n <div\n aria-busy=\"true\"\n aria-label=\"Loading survey\"\n className=\"rfs-skeleton\"\n role=\"status\"\n >\n <span className=\"rfs-skeleton-line\" />\n <span className=\"rfs-skeleton-line\" />\n <span className=\"rfs-skeleton-line\" />\n </div>\n );\n}\n\nexport function StartFailedStep({\n message,\n onRetry,\n}: {\n message: string | null;\n onRetry: () => void;\n}) {\n return (\n <div className=\"rfs-step\">\n <p className=\"rfs-error\" role=\"alert\">\n {message}\n </p>\n <div className=\"rfs-footer\">\n <ActionButton onClick={onRetry}>Try again</ActionButton>\n </div>\n </div>\n );\n}\n","import type { AnswerValue, SurveyQuestion } from \"@reflet/survey-core\";\nimport type { RefObject } from \"react\";\nimport type { SurveySessionHandle } from \"../use-survey-session\";\nimport { QuestionBody } from \"./question-body\";\nimport {\n ActionButton,\n DEFAULT_BUTTON_LABEL,\n StepHeading,\n} from \"./survey-steps\";\n\nexport function QuestionStep({\n headingId,\n headingRef,\n onPick,\n question,\n session,\n}: {\n headingId: string;\n headingRef: RefObject<HTMLHeadingElement | null>;\n onPick: (value: AnswerValue) => void;\n question: SurveyQuestion;\n session: SurveySessionHandle;\n}) {\n const { snapshot } = session;\n const isStatement = question.type === \"statement\";\n const nextLabel = snapshot.isLastStep ? \"Submit\" : \"Next\";\n\n return (\n <div className=\"rfs-step\">\n <StepHeading\n description={question.description}\n headingId={headingId}\n headingRef={headingRef}\n title={question.title}\n />\n <QuestionBody\n labelledBy={headingId}\n onChange={session.setAnswer}\n onPick={onPick}\n question={question}\n value={snapshot.answers.get(question._id)}\n />\n {snapshot.error && (\n <p className=\"rfs-error\" role=\"alert\">\n {snapshot.error}\n </p>\n )}\n <div className=\"rfs-footer\">\n {snapshot.canGoBack && (\n <button\n className=\"rfs-button\"\n data-tone=\"ghost\"\n disabled={snapshot.isSubmitting}\n onClick={session.back}\n type=\"button\"\n >\n Back\n </button>\n )}\n <ActionButton\n disabled={snapshot.isSubmitting}\n onClick={() => session.next()}\n url={isStatement ? question.config?.buttonUrl : undefined}\n >\n {isStatement\n ? (question.config?.buttonLabel ?? DEFAULT_BUTTON_LABEL)\n : nextLabel}\n </ActionButton>\n </div>\n </div>\n );\n}\n","import type { AnswerValue } from \"@reflet/survey-core\";\nimport {\n type CSSProperties,\n type KeyboardEvent,\n useEffect,\n useId,\n useRef,\n} from \"react\";\nimport type { SurveySessionHandle } from \"../use-survey-session\";\nimport { cardKeyAction } from \"./card-keyboard\";\nimport { SURVEY_CARD_STYLES } from \"./card-styles\";\nimport { QuestionStep } from \"./question-step\";\nimport { EndingStep, LoadingStep, StartFailedStep } from \"./survey-steps\";\n\nexport type SurveyCardVariant = \"floating\" | \"inline\" | \"page\";\nexport type SurveyTheme = \"auto\" | \"dark\" | \"light\";\n\nexport interface SurveyCardProps {\n className?: string;\n /** Shows a close button. Closing mid-survey dismisses the response first. */\n onClose?: () => void;\n /** Accent for buttons, selections and progress. Any CSS color. */\n primaryColor?: string;\n session: SurveySessionHandle;\n theme?: SurveyTheme;\n variant?: SurveyCardVariant;\n}\n\nconst HEX_COLOR = /^#([\\da-f]{3}|[\\da-f]{6})$/i;\nconst LIGHT_LUMINANCE_THRESHOLD = 0.6;\nconst DARK_TEXT = \"#18181b\";\nconst LIGHT_TEXT = \"#ffffff\";\n\n/** Dark text on light brand colors, white text otherwise. */\nconst readableTextOn = (color: string): string => {\n const match = HEX_COLOR.exec(color.trim());\n if (!match?.[1]) {\n return LIGHT_TEXT;\n }\n const hex =\n match[1].length === 3\n ? [...match[1]].map((digit) => digit + digit).join(\"\")\n : match[1];\n const [red = 0, green = 0, blue = 0] = [0, 2, 4].map(\n (offset) => Number.parseInt(hex.slice(offset, offset + 2), 16) / 255\n );\n const luminance = 0.2126 * red + 0.7152 * green + 0.0722 * blue;\n return luminance > LIGHT_LUMINANCE_THRESHOLD ? DARK_TEXT : LIGHT_TEXT;\n};\n\nfunction CloseIcon() {\n return (\n <svg\n aria-hidden=\"true\"\n fill=\"none\"\n height=\"16\"\n stroke=\"currentColor\"\n strokeLinecap=\"round\"\n strokeWidth=\"2\"\n viewBox=\"0 0 24 24\"\n width=\"16\"\n >\n <path d=\"M6 6l12 12M18 6L6 18\" />\n </svg>\n );\n}\n\n/**\n * Renders any survey session: questions, branching, endings and errors.\n * Ships its own scoped styles, so it works in a shadow root or a page.\n */\nexport function SurveyCard({\n className,\n onClose,\n primaryColor,\n session,\n theme = \"auto\",\n variant = \"inline\",\n}: SurveyCardProps) {\n const { snapshot } = session;\n const { ending, phase, question } = snapshot;\n const headingId = useId();\n const headingRef = useRef<HTMLHeadingElement>(null);\n const hasInteracted = useRef(false);\n const stepKey = question?._id ?? phase;\n\n useEffect(() => {\n if (stepKey && hasInteracted.current) {\n headingRef.current?.focus({ preventScroll: true });\n }\n }, [stepKey]);\n\n if (phase === \"dismissed\") {\n return null;\n }\n\n const closable = onClose !== undefined || variant === \"floating\";\n const close = () => {\n session.dismiss();\n onClose?.();\n };\n const pick = (value: AnswerValue) => {\n session.setAnswer(value);\n session.next();\n };\n\n const onKeyDown = (event: KeyboardEvent<HTMLElement>) => {\n hasInteracted.current = true;\n if (event.nativeEvent.isComposing) {\n return;\n }\n const action = cardKeyAction(\n {\n ctrlKey: event.ctrlKey,\n key: event.key,\n metaKey: event.metaKey,\n target: event.target,\n },\n question\n );\n if (!action || (action.kind === \"close\" && !closable)) {\n return;\n }\n event.preventDefault();\n if (action.kind === \"close\") {\n close();\n } else if (action.kind === \"next\") {\n session.next();\n } else {\n pick(action.value);\n }\n };\n\n const themeStyle: CSSProperties & Record<`--${string}`, string> = primaryColor\n ? {\n \"--rf-survey-primary\": primaryColor,\n \"--rf-survey-primary-text\": readableTextOn(primaryColor),\n }\n : {};\n const showsProgress = phase === \"question\" || phase === \"ending\";\n\n return (\n // biome-ignore lint/a11y/noNoninteractiveElementInteractions: keyboard shortcuts for the controls inside the card\n <section\n aria-label={question || ending ? undefined : session.survey.title}\n aria-labelledby={question || ending ? headingId : undefined}\n className={className ? `rf-survey ${className}` : \"rf-survey\"}\n data-theme={theme}\n data-variant={variant}\n onKeyDown={onKeyDown}\n onPointerDown={() => {\n hasInteracted.current = true;\n }}\n style={themeStyle}\n >\n <style>{SURVEY_CARD_STYLES}</style>\n {showsProgress && (\n <div\n aria-label=\"Survey progress\"\n aria-valuemax={100}\n aria-valuemin={0}\n aria-valuenow={Math.round(snapshot.progress * 100)}\n className=\"rfs-progress\"\n role=\"progressbar\"\n >\n <div\n className=\"rfs-progress-fill\"\n style={{ transform: `scaleX(${snapshot.progress})` }}\n />\n </div>\n )}\n {closable && (\n <button\n aria-label=\"Close survey\"\n className=\"rfs-close\"\n onClick={close}\n type=\"button\"\n >\n <CloseIcon />\n </button>\n )}\n {phase === \"starting\" && <LoadingStep />}\n {phase === \"start_failed\" && (\n <StartFailedStep\n message={snapshot.error}\n onRetry={() => session.start()}\n />\n )}\n {ending && (\n <EndingStep\n ending={ending}\n headingId={headingId}\n headingRef={headingRef}\n key={ending.id}\n onClose={close}\n />\n )}\n {question && (\n <QuestionStep\n headingId={headingId}\n headingRef={headingRef}\n key={question._id}\n onPick={pick}\n question={question}\n session={session}\n />\n )}\n </section>\n );\n}\n","import type { SurveyTransport } from \"@reflet/survey-core/client\";\nimport type { Reflet } from \"../client\";\n\n/** Runs a survey session against the Reflet REST API with this client's key and user. */\nexport const createRefletSurveyTransport = (\n client: Reflet,\n options: { pageUrl?: string } = {}\n): SurveyTransport => ({\n answer: (input) => client.submitSurveyAnswer(input),\n complete: ({ responseId }) => client.completeSurveyResponse(responseId),\n dismiss: ({ responseId }) => client.dismissSurveyResponse(responseId),\n start: ({ surveyId }) =>\n client.startSurveyResponse({ pageUrl: options.pageUrl, surveyId }),\n});\n","import type { AnswerValue, PublicSurvey } from \"@reflet/survey-core\";\nimport {\n previewTransport,\n SurveySession,\n type SurveySessionCallbacks,\n type SurveySessionSnapshot,\n type SurveyTransport,\n} from \"@reflet/survey-core/client\";\nimport {\n useContext,\n useEffect,\n useMemo,\n useRef,\n useSyncExternalStore,\n} from \"react\";\nimport { RefletContext } from \"../react-context\";\nimport { createRefletSurveyTransport } from \"./client-transport\";\n\n/** A running survey: its current state plus the actions a UI needs. */\nexport interface SurveySessionHandle {\n back: () => void;\n dismiss: () => void;\n next: () => Promise<void>;\n setAnswer: (value: AnswerValue | undefined) => void;\n snapshot: SurveySessionSnapshot;\n start: () => Promise<void>;\n survey: PublicSurvey;\n}\n\nexport interface UseSurveySessionOptions {\n /** Start the response on mount. Defaults to true. */\n autoStart?: boolean;\n callbacks?: SurveySessionCallbacks;\n /**\n * Where answers go. Defaults to the Reflet API inside `RefletProvider`,\n * otherwise to `previewTransport`, which saves nothing.\n */\n transport?: SurveyTransport;\n}\n\nconst startedSessions = new WeakSet<SurveySession>();\n\n/** Subscribes a component to an existing session. */\nexport function useSessionHandle(session: SurveySession): SurveySessionHandle {\n const snapshot = useSyncExternalStore(\n session.subscribe,\n session.getSnapshot,\n session.getSnapshot\n );\n return useMemo(\n () => ({\n back: () => session.back(),\n dismiss: () => session.dismiss(),\n next: () => session.next(),\n setAnswer: (value: AnswerValue | undefined) => session.setAnswer(value),\n snapshot,\n start: () => session.start(),\n survey: session.survey,\n }),\n [session, snapshot]\n );\n}\n\n/**\n * Headless survey runner for custom UIs. A new session begins whenever\n * `survey` or `transport` changes identity, so memoize them.\n */\nexport function useSurveySession(\n survey: PublicSurvey,\n options: UseSurveySessionOptions = {}\n): SurveySessionHandle {\n const { autoStart = true, callbacks, transport } = options;\n const client = useContext(RefletContext)?.client;\n const callbacksRef = useRef(callbacks);\n\n useEffect(() => {\n callbacksRef.current = callbacks;\n });\n\n const resolvedTransport = useMemo(() => {\n if (transport) {\n return transport;\n }\n return client ? createRefletSurveyTransport(client) : previewTransport;\n }, [client, transport]);\n\n const session = useMemo(\n () =>\n new SurveySession({\n callbacks: {\n onAnswer: (event) => callbacksRef.current?.onAnswer?.(event),\n onComplete: (event) => callbacksRef.current?.onComplete?.(event),\n onDismiss: (event) => callbacksRef.current?.onDismiss?.(event),\n onStart: (event) => callbacksRef.current?.onStart?.(event),\n },\n survey,\n transport: resolvedTransport,\n }),\n [survey, resolvedTransport]\n );\n\n useEffect(() => {\n if (!autoStart || startedSessions.has(session)) {\n return;\n }\n startedSessions.add(session);\n session.start();\n }, [autoStart, session]);\n\n return useSessionHandle(session);\n}\n"],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ //#region src/z-index.ts
2
+ const REFLET_Z_INDEX = {
3
+ annotatorOverlay: 2147483001,
4
+ captureHalo: 2147482999,
5
+ devtoolsBar: 2147483647,
6
+ dialogContent: 2147482991,
7
+ dialogOverlay: 2147482990,
8
+ elementPicker: 2147483647,
9
+ surveyLayer: 2147482995,
10
+ widgetRoot: 2147483e3
11
+ };
12
+ //#endregion
13
+ export { REFLET_Z_INDEX as t };
14
+
15
+ //# sourceMappingURL=z-index-CwokqVq_.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"z-index-CwokqVq_.js","names":[],"sources":["../src/z-index.ts"],"sourcesContent":["export const REFLET_Z_INDEX = {\n annotatorOverlay: 2_147_483_001,\n captureHalo: 2_147_482_999,\n devtoolsBar: 2_147_483_647,\n dialogContent: 2_147_482_991,\n dialogOverlay: 2_147_482_990,\n elementPicker: 2_147_483_647,\n surveyLayer: 2_147_482_995,\n widgetRoot: 2_147_483_000,\n} as const;\n"],"mappings":";AAAA,MAAa,iBAAiB;CAC5B,kBAAkB;CAClB,aAAa;CACb,aAAa;CACb,eAAe;CACf,eAAe;CACf,eAAe;CACf,aAAa;CACb,YAAY;AACd"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reflet-sdk",
3
- "version": "0.6.5",
3
+ "version": "0.6.6",
4
4
  "description": "Official Reflet SDK - Collect user feedback, feature requests, and bug reports from your app",
5
5
  "keywords": [
6
6
  "feedback",
@@ -47,6 +47,10 @@
47
47
  "types": "./dist/server.d.ts",
48
48
  "import": "./dist/server.js"
49
49
  },
50
+ "./surveys": {
51
+ "types": "./dist/surveys.d.ts",
52
+ "import": "./dist/surveys.js"
53
+ },
50
54
  "./devtools/next": {
51
55
  "types": "./dist/devtools-next.d.ts",
52
56
  "import": "./dist/devtools-next.js"
@@ -82,9 +86,9 @@
82
86
  "zod": "^4.6.5"
83
87
  },
84
88
  "devDependencies": {
89
+ "@reflet/survey-core": "workspace:*",
85
90
  "@testing-library/dom": "^10.4.2",
86
91
  "@testing-library/react": "^16.3.3",
87
- "@testing-library/user-event": "^14.6.7",
88
92
  "@types/react": "^19.3.0",
89
93
  "@types/react-dom": "19.3.0",
90
94
  "bippy": "^0.7.3",
@@ -1 +0,0 @@
1
- {"version":3,"file":"client-DAdWdIFY.js","names":[],"sources":["../src/types.ts","../src/client.ts"],"sourcesContent":["/**\n * Reflet SDK Types\n */\n\n// ============================================\n// Configuration\n// ============================================\n\nexport interface RefletConfig {\n /** API base URL (optional, defaults to Reflet production API) */\n baseUrl?: string;\n /** Your organization's public API key (fb_pub_xxx) */\n publicKey: string;\n /** User identification for SSO */\n user?: RefletUser;\n /** Pre-signed user token (alternative to user object) */\n userToken?: string;\n}\n\nexport interface RefletUser {\n /** URL to user's avatar image */\n avatar?: string;\n /** User's email address */\n email?: string;\n /** Unique identifier for the user in your system */\n id: string;\n /** Additional metadata */\n metadata?: Record<string, unknown>;\n /** User's display name */\n name?: string;\n}\n\n// ============================================\n// API Responses\n// ============================================\n\nexport interface OrganizationConfig {\n description?: string;\n id: string;\n isPublic: boolean;\n logo?: string;\n name: string;\n primaryColor?: string;\n settings?: OrganizationSettings;\n slug: string;\n statuses: OrganizationStatus[];\n}\n\nexport interface OrganizationSettings {\n allowAnonymousVoting?: boolean;\n defaultStatus?: FeedbackStatus;\n requireApproval?: boolean;\n}\n\nexport interface OrganizationStatus {\n color: string;\n icon?: string;\n id: string;\n name: string;\n order: number;\n}\n\nexport type FeedbackStatus =\n | \"open\"\n | \"under_review\"\n | \"planned\"\n | \"in_progress\"\n | \"completed\"\n | \"closed\";\n\n// ============================================\n// Feedback\n// ============================================\n\nexport interface FeedbackItem {\n author: FeedbackAuthor | null;\n commentCount: number;\n completedAt?: number;\n createdAt: number;\n description: string;\n hasVoted: boolean;\n id: string;\n isPinned: boolean;\n organizationStatus: {\n id: string;\n name: string;\n color: string;\n } | null;\n status: FeedbackStatus;\n tags: FeedbackTag[];\n title: string;\n updatedAt: number;\n voteCount: number;\n}\n\nexport interface FeedbackDetail extends FeedbackItem {\n isSubscribed: boolean;\n}\n\nexport interface FeedbackTag {\n color: string;\n id: string;\n name: string;\n}\n\nexport interface FeedbackAuthor {\n avatar?: string;\n email?: string;\n isExternal: boolean;\n name?: string;\n}\n\nexport interface FeedbackListResponse {\n hasMore: boolean;\n items: FeedbackItem[];\n total: number;\n}\n\nexport interface FeedbackListParams {\n /** Number of items to return (max 100) */\n limit?: number;\n /** Offset for pagination */\n offset?: number;\n /** Search in title and description */\n search?: string;\n /** Sort order */\n sortBy?: \"votes\" | \"newest\" | \"oldest\" | \"comments\";\n /** Filter by status name */\n status?: FeedbackStatus;\n /** Filter by custom status ID */\n statusId?: string;\n}\n\nexport interface ElementRect {\n height: number;\n width: number;\n x: number;\n y: number;\n}\n\nexport interface ElementSelection {\n /** What the reporter wrote about this element while picking it. */\n comment?: string;\n /** React component names owning the element, innermost first. */\n componentStack: string[];\n /** Truncated, redacted outerHTML of the selected element. */\n html: string;\n /** Short human-readable description, e.g. `button \"Sign in\"`. */\n label: string;\n rect: ElementRect;\n /** Page area holding the element, e.g. `dialog \"Members\" › Danger zone`. */\n region?: string;\n selector: string;\n /** `src/components/login-form.tsx:46:19` when the React build exposes it. */\n sourceLocation?: string;\n /** Collapsed visible text of the element — survives markup truncation. */\n text?: string;\n}\n\nexport interface ConsoleEvent {\n level: \"error\" | \"warn\";\n message: string;\n timestamp: number;\n}\n\nexport interface PageContext {\n browser?: string;\n device?: string;\n language?: string;\n os?: string;\n pageTitle?: string;\n referrer?: string;\n screen?: { height: number; width: number };\n /** Where the viewport sat in the page when the screenshot was taken. */\n scroll?: { x: number; y: number };\n timezone?: string;\n url?: string;\n userAgent?: string;\n viewport?: { devicePixelRatio: number; height: number; width: number };\n}\n\n/** Everything the widget knows about where and how the feedback was written. */\nexport interface FeedbackContext extends PageContext {\n consoleEvents?: ConsoleEvent[];\n metadata?: Record<string, string>;\n sdkVersion?: string;\n selections?: ElementSelection[];\n}\n\nexport interface CreateFeedbackParams {\n context?: FeedbackContext;\n description: string;\n title: string;\n}\n\nexport interface ScreenshotAnnotation {\n color?: string;\n endX?: number;\n endY?: number;\n height?: number;\n points?: Array<{ x: number; y: number }>;\n text?: string;\n type:\n | \"arrow\"\n | \"blur\"\n | \"highlight\"\n | \"pen\"\n | \"rectangle\"\n | \"spotlight\"\n | \"text\";\n width?: number;\n x: number;\n y: number;\n}\n\nexport interface SaveScreenshotParams {\n annotatedStorageId?: string;\n annotations?: ScreenshotAnnotation[];\n /** `element` for the close-up of a picked element, `widget` for the page. */\n captureSource?: \"element\" | \"widget\";\n feedbackId: string;\n filename?: string;\n height?: number;\n mimeType?: string;\n pageUrl?: string;\n size?: number;\n storageId: string;\n width?: number;\n}\n\nexport interface CreateFeedbackResponse {\n feedbackId: string;\n isApproved: boolean;\n}\n\nexport interface VoteResponse {\n voteCount: number;\n voted: boolean;\n}\n\n// ============================================\n// Comments\n// ============================================\n\nexport interface Comment {\n author: FeedbackAuthor | null;\n body: string;\n createdAt: number;\n id: string;\n isOfficial: boolean;\n replies: CommentReply[];\n updatedAt: number;\n}\n\nexport interface CommentReply {\n author: FeedbackAuthor | null;\n body: string;\n createdAt: number;\n id: string;\n isOfficial: boolean;\n updatedAt: number;\n}\n\nexport interface AddCommentParams {\n body: string;\n feedbackId: string;\n parentId?: string;\n}\n\nexport interface AddCommentResponse {\n commentId: string;\n}\n\n// ============================================\n// Subscriptions\n// ============================================\n\nexport interface SubscribeResponse {\n alreadySubscribed?: boolean;\n subscribed: boolean;\n}\n\nexport interface UnsubscribeResponse {\n unsubscribed: boolean;\n wasNotSubscribed?: boolean;\n}\n\n// ============================================\n// Roadmap\n// ============================================\n\nexport interface Roadmap {\n lanes: RoadmapLane[];\n}\n\nexport interface RoadmapLane {\n color: string;\n id: string;\n items: RoadmapItem[];\n name: string;\n}\n\nexport interface RoadmapItem {\n id: string;\n status: FeedbackStatus;\n title: string;\n voteCount: number;\n}\n\n// ============================================\n// Changelog\n// ============================================\n\nexport interface ChangelogEntry {\n description?: string;\n feedback: { id: string; title: string }[];\n id: string;\n publishedAt?: number;\n title: string;\n version?: string;\n}\n\n// ============================================\n// Errors\n// ============================================\n\nexport class RefletError extends Error {\n readonly status: number;\n readonly code?: string;\n\n constructor(message: string, status: number, code?: string) {\n super(message);\n this.name = \"RefletError\";\n this.status = status;\n this.code = code;\n }\n}\n\nexport class RefletAuthError extends RefletError {\n constructor(message: string) {\n super(message, 401, \"AUTH_ERROR\");\n this.name = \"RefletAuthError\";\n }\n}\n\nexport class RefletValidationError extends RefletError {\n constructor(message: string) {\n super(message, 400, \"VALIDATION_ERROR\");\n this.name = \"RefletValidationError\";\n }\n}\n\nexport class RefletNotFoundError extends RefletError {\n constructor(message: string) {\n super(message, 404, \"NOT_FOUND\");\n this.name = \"RefletNotFoundError\";\n }\n}\n","import {\n type AddCommentParams,\n type AddCommentResponse,\n type ChangelogEntry,\n type Comment,\n type CreateFeedbackParams,\n type CreateFeedbackResponse,\n type FeedbackDetail,\n type FeedbackListParams,\n type FeedbackListResponse,\n type OrganizationConfig,\n RefletAuthError,\n type RefletConfig,\n RefletError,\n RefletNotFoundError,\n type RefletUser,\n type Roadmap,\n type SaveScreenshotParams,\n type SubscribeResponse,\n type UnsubscribeResponse,\n type VoteResponse,\n} from \"./types\";\n\nexport const DEFAULT_API_URL = \"https://harmless-clam-802.convex.site\";\nconst UNSIGNED_TOKEN_ALGORITHM = \"none\";\n\n/** btoa is Latin1-only — a name like \"José\" would throw. */\nfunction base64Utf8(value: string): string {\n let binary = \"\";\n for (const byte of new TextEncoder().encode(value)) {\n binary += String.fromCharCode(byte);\n }\n return btoa(binary);\n}\n\n/**\n * Reflet SDK Client\n *\n * @example\n * ```ts\n * import { Reflet } from 'reflet-sdk';\n *\n * const reflet = new Reflet({\n * publicKey: 'fb_pub_xxx',\n * user: { id: 'user_123', email: 'user@example.com', name: 'John' }\n * });\n *\n * // List feedback\n * const { items } = await reflet.list({ status: 'open' });\n *\n * // Vote on feedback\n * await reflet.vote('feedback_id');\n *\n * // Submit new feedback\n * await reflet.create({ title: 'Great idea', description: 'Details...' });\n * ```\n */\nexport class Reflet {\n private readonly publicKey: string;\n private readonly baseUrl: string;\n private userToken: string | undefined;\n private user: RefletUser | undefined;\n\n constructor(config: RefletConfig) {\n this.publicKey = config.publicKey;\n this.baseUrl = config.baseUrl ?? DEFAULT_API_URL;\n this.userToken = config.userToken;\n this.user = config.user;\n\n if (!this.publicKey) {\n throw new Error(\"Reflet: publicKey is required\");\n }\n }\n\n /**\n * Update the user identification\n */\n setUser(user: RefletUser | undefined): void {\n this.user = user;\n this.userToken = undefined;\n }\n\n /**\n * Set the user token directly (for server-signed tokens)\n */\n setUserToken(token: string | undefined): void {\n this.userToken = token;\n this.user = undefined;\n }\n\n /**\n * Get the organization configuration\n */\n async getConfig(): Promise<OrganizationConfig> {\n return await this.request<OrganizationConfig>(\"GET\", \"/api/v1/feedback\");\n }\n\n /**\n * List feedback items with optional filtering\n */\n async list(params?: FeedbackListParams): Promise<FeedbackListResponse> {\n const searchParams = new URLSearchParams();\n\n if (params?.statusId) {\n searchParams.set(\"statusId\", params.statusId);\n }\n if (params?.status) {\n searchParams.set(\"status\", params.status);\n }\n if (params?.search) {\n searchParams.set(\"search\", params.search);\n }\n if (params?.sortBy) {\n searchParams.set(\"sortBy\", params.sortBy);\n }\n if (params?.limit) {\n searchParams.set(\"limit\", String(params.limit));\n }\n if (params?.offset) {\n searchParams.set(\"offset\", String(params.offset));\n }\n\n const query = searchParams.toString();\n const url = `/api/v1/feedback/list${query ? `?${query}` : \"\"}`;\n\n return await this.request<FeedbackListResponse>(\"GET\", url);\n }\n\n /**\n * Get a single feedback item by ID\n */\n async get(feedbackId: string): Promise<FeedbackDetail> {\n const result = await this.request<FeedbackDetail | null>(\n \"GET\",\n `/api/v1/feedback/item?id=${encodeURIComponent(feedbackId)}`\n );\n\n if (!result) {\n throw new RefletNotFoundError(\"Feedback not found\");\n }\n\n return result;\n }\n\n /**\n * Create new feedback\n * Works anonymously or with user identification\n */\n async create(params: CreateFeedbackParams): Promise<CreateFeedbackResponse> {\n return await this.request<CreateFeedbackResponse>(\n \"POST\",\n \"/api/v1/feedback/create\",\n params\n );\n }\n\n /**\n * Ask the API for a one-shot storage URL to upload a screenshot to.\n */\n async getScreenshotUploadUrl(): Promise<{ uploadUrl: string }> {\n return await this.request<{ uploadUrl: string }>(\n \"POST\",\n \"/api/v1/feedback/screenshot/upload-url\"\n );\n }\n\n /**\n * Attach an already uploaded screenshot to a feedback item.\n */\n async saveScreenshot(\n params: SaveScreenshotParams\n ): Promise<{ screenshotId: string }> {\n return await this.request<{ screenshotId: string }>(\n \"POST\",\n \"/api/v1/feedback/screenshot/save\",\n params\n );\n }\n\n /**\n * Toggle vote on feedback\n * Requires user identification\n */\n async vote(\n feedbackId: string,\n type: \"upvote\" | \"downvote\" = \"upvote\"\n ): Promise<VoteResponse> {\n return await this.request<VoteResponse>(\"POST\", \"/api/v1/feedback/vote\", {\n feedbackId,\n voteType: type,\n });\n }\n\n /**\n * Get comments for a feedback item\n */\n async getComments(\n feedbackId: string,\n sortBy: \"newest\" | \"oldest\" = \"oldest\"\n ): Promise<Comment[]> {\n return await this.request<Comment[]>(\n \"GET\",\n `/api/v1/feedback/comments?feedbackId=${encodeURIComponent(feedbackId)}&sortBy=${sortBy}`\n );\n }\n\n /**\n * Add a comment to feedback\n * Requires user identification\n */\n async comment(params: AddCommentParams): Promise<AddCommentResponse> {\n return await this.request<AddCommentResponse>(\n \"POST\",\n \"/api/v1/feedback/comment\",\n params\n );\n }\n\n /**\n * Subscribe to feedback updates\n * Requires user identification\n */\n async subscribe(feedbackId: string): Promise<SubscribeResponse> {\n return await this.request<SubscribeResponse>(\n \"POST\",\n \"/api/v1/feedback/subscribe\",\n {\n feedbackId,\n }\n );\n }\n\n /**\n * Unsubscribe from feedback updates\n * Requires user identification\n */\n async unsubscribe(feedbackId: string): Promise<UnsubscribeResponse> {\n return await this.request<UnsubscribeResponse>(\n \"POST\",\n \"/api/v1/feedback/unsubscribe\",\n {\n feedbackId,\n }\n );\n }\n\n /**\n * Get roadmap data\n */\n async getRoadmap(): Promise<Roadmap> {\n return await this.request<Roadmap>(\"GET\", \"/api/v1/feedback/roadmap\");\n }\n\n /**\n * Get changelog entries\n */\n async getChangelog(limit?: number): Promise<ChangelogEntry[]> {\n const url = limit\n ? `/api/v1/feedback/changelog?limit=${limit}`\n : \"/api/v1/feedback/changelog\";\n\n return await this.request<ChangelogEntry[]>(\"GET\", url);\n }\n\n /**\n * Build request headers with authentication\n */\n private buildHeaders(): Record<string, string> {\n const headers: Record<string, string> = {\n Authorization: `Bearer ${this.publicKey}`,\n \"Content-Type\": \"application/json\",\n };\n\n const token = this.userToken ?? this.generateUserToken();\n if (token) {\n headers[\"X-User-Token\"] = token;\n }\n\n return headers;\n }\n\n /**\n * Parse JSON response text safely.\n * Returns `unknown` — callers must narrow before use.\n */\n private parseJsonSafely(text: string, status: number): unknown {\n try {\n return JSON.parse(text);\n } catch (parseError) {\n throw new RefletError(\n `Invalid response: ${parseError instanceof Error ? parseError.message : \"Failed to parse JSON\"}`,\n status\n );\n }\n }\n\n /**\n * Type guard for API error responses\n */\n private isErrorResponse(data: unknown): data is { error: string } {\n return (\n typeof data === \"object\" &&\n data !== null &&\n \"error\" in data &&\n typeof (data as Record<string, unknown>).error === \"string\"\n );\n }\n\n /**\n * Throw appropriate error based on status code\n */\n private throwHttpError(message: string, status: number): never {\n if (status === 401) {\n throw new RefletAuthError(message);\n }\n if (status === 404) {\n throw new RefletNotFoundError(message);\n }\n throw new RefletError(message, status);\n }\n\n /**\n * Make an authenticated API request\n */\n private async request<T>(\n method: string,\n path: string,\n body?: unknown\n ): Promise<T> {\n const response = await this.send(method, path, body);\n\n if (!response.ok) {\n return this.throwResponseError(response);\n }\n\n const text = await response.text();\n\n // Successful empty body (e.g. 204 No Content) — return empty object\n // narrowed through unknown for type-safe assertion\n if (!text) {\n const emptyResponse: unknown = {};\n return emptyResponse as T;\n }\n\n // Standard unknown → T assertion for runtime-parsed JSON\n return this.parseJsonSafely(text, response.status) as T;\n }\n\n private async send(\n method: string,\n path: string,\n body?: unknown\n ): Promise<Response> {\n try {\n return await fetch(`${this.baseUrl}${path}`, {\n body: body ? JSON.stringify(body) : undefined,\n headers: this.buildHeaders(),\n method,\n });\n } catch (networkError) {\n const message =\n networkError instanceof Error\n ? networkError.message\n : \"Failed to connect\";\n throw new RefletError(`Network error: ${message}`, 0);\n }\n }\n\n private async throwResponseError(response: Response): Promise<never> {\n const fallbackMessage = `Request failed with status ${response.status}`;\n const text = await response.text();\n if (!text) {\n this.throwHttpError(fallbackMessage, response.status);\n }\n\n const data = this.parseJsonSafely(text, response.status);\n const errorMessage = this.isErrorResponse(data)\n ? data.error\n : fallbackMessage;\n this.throwHttpError(errorMessage, response.status);\n }\n\n /**\n * Unsigned identity from the `user` option. The API attributes reports with\n * it but refuses voting, commenting and subscribing — those need `signUser`.\n */\n private generateUserToken(): string | undefined {\n if (!this.user) {\n return;\n }\n\n const payload = {\n email: this.user.email,\n exp: Math.floor(Date.now() / 1000) + 86_400, // 24 hours\n iat: Math.floor(Date.now() / 1000),\n id: this.user.id,\n name: this.user.name,\n };\n\n const header = base64Utf8(\n JSON.stringify({ alg: UNSIGNED_TOKEN_ALGORITHM, typ: \"JWT\" })\n );\n const payloadB64 = base64Utf8(JSON.stringify(payload));\n\n // Empty signature — the server treats this identity as unverified\n return `${header}.${payloadB64}.`;\n }\n}\n"],"mappings":"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"}
@@ -1,131 +0,0 @@
1
- import { A as SubscribeResponse, E as Roadmap, M as VoteResponse, _ as OrganizationConfig, d as FeedbackDetail, i as Comment, j as UnsubscribeResponse, k as SaveScreenshotParams, m as FeedbackListResponse, n as AddCommentResponse, o as CreateFeedbackParams, p as FeedbackListParams, r as ChangelogEntry, s as CreateFeedbackResponse, t as AddCommentParams, w as RefletUser, x as RefletConfig } from "./types-DnaDEN9Y.js";
2
- //#region src/client.d.ts
3
- /**
4
- * Reflet SDK Client
5
- *
6
- * @example
7
- * ```ts
8
- * import { Reflet } from 'reflet-sdk';
9
- *
10
- * const reflet = new Reflet({
11
- * publicKey: 'fb_pub_xxx',
12
- * user: { id: 'user_123', email: 'user@example.com', name: 'John' }
13
- * });
14
- *
15
- * // List feedback
16
- * const { items } = await reflet.list({ status: 'open' });
17
- *
18
- * // Vote on feedback
19
- * await reflet.vote('feedback_id');
20
- *
21
- * // Submit new feedback
22
- * await reflet.create({ title: 'Great idea', description: 'Details...' });
23
- * ```
24
- */
25
- declare class Reflet {
26
- private readonly publicKey;
27
- private readonly baseUrl;
28
- private userToken;
29
- private user;
30
- constructor(config: RefletConfig);
31
- /**
32
- * Update the user identification
33
- */
34
- setUser(user: RefletUser | undefined): void;
35
- /**
36
- * Set the user token directly (for server-signed tokens)
37
- */
38
- setUserToken(token: string | undefined): void;
39
- /**
40
- * Get the organization configuration
41
- */
42
- getConfig(): Promise<OrganizationConfig>;
43
- /**
44
- * List feedback items with optional filtering
45
- */
46
- list(params?: FeedbackListParams): Promise<FeedbackListResponse>;
47
- /**
48
- * Get a single feedback item by ID
49
- */
50
- get(feedbackId: string): Promise<FeedbackDetail>;
51
- /**
52
- * Create new feedback
53
- * Works anonymously or with user identification
54
- */
55
- create(params: CreateFeedbackParams): Promise<CreateFeedbackResponse>;
56
- /**
57
- * Ask the API for a one-shot storage URL to upload a screenshot to.
58
- */
59
- getScreenshotUploadUrl(): Promise<{
60
- uploadUrl: string;
61
- }>;
62
- /**
63
- * Attach an already uploaded screenshot to a feedback item.
64
- */
65
- saveScreenshot(params: SaveScreenshotParams): Promise<{
66
- screenshotId: string;
67
- }>;
68
- /**
69
- * Toggle vote on feedback
70
- * Requires user identification
71
- */
72
- vote(feedbackId: string, type?: "upvote" | "downvote"): Promise<VoteResponse>;
73
- /**
74
- * Get comments for a feedback item
75
- */
76
- getComments(feedbackId: string, sortBy?: "newest" | "oldest"): Promise<Comment[]>;
77
- /**
78
- * Add a comment to feedback
79
- * Requires user identification
80
- */
81
- comment(params: AddCommentParams): Promise<AddCommentResponse>;
82
- /**
83
- * Subscribe to feedback updates
84
- * Requires user identification
85
- */
86
- subscribe(feedbackId: string): Promise<SubscribeResponse>;
87
- /**
88
- * Unsubscribe from feedback updates
89
- * Requires user identification
90
- */
91
- unsubscribe(feedbackId: string): Promise<UnsubscribeResponse>;
92
- /**
93
- * Get roadmap data
94
- */
95
- getRoadmap(): Promise<Roadmap>;
96
- /**
97
- * Get changelog entries
98
- */
99
- getChangelog(limit?: number): Promise<ChangelogEntry[]>;
100
- /**
101
- * Build request headers with authentication
102
- */
103
- private buildHeaders;
104
- /**
105
- * Parse JSON response text safely.
106
- * Returns `unknown` — callers must narrow before use.
107
- */
108
- private parseJsonSafely;
109
- /**
110
- * Type guard for API error responses
111
- */
112
- private isErrorResponse;
113
- /**
114
- * Throw appropriate error based on status code
115
- */
116
- private throwHttpError;
117
- /**
118
- * Make an authenticated API request
119
- */
120
- private request;
121
- private send;
122
- private throwResponseError;
123
- /**
124
- * Unsigned identity from the `user` option. The API attributes reports with
125
- * it but refuses voting, commenting and subscribing — those need `signUser`.
126
- */
127
- private generateUserToken;
128
- }
129
- //#endregion
130
- export { Reflet as t };
131
- //# sourceMappingURL=client-DF1fdsSC.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"client-DF1fdsSC.d.ts","names":[],"sources":["../src/client.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;cAyDa;mBACM;mBACA;UACT;UACA;EAER,YAAY,QAAQ;;;;EAcpB,QAAQ,MAAM;;;;EAQd,aAAa;;;;EAQP,aAAa,QAAQ;;;;EAOrB,KAAK,SAAS,qBAAqB,QAAQ;;;;EA+B3C,IAAI,qBAAqB,QAAQ;;;;;EAiBjC,OAAO,QAAQ,uBAAuB,QAAQ;;;;EAW9C,0BAA0B;IAAU;;;;;EAUpC,eACJ,QAAQ,uBACP;IAAU;;;;;;EAYP,KACJ,oBACA,+BACC,QAAQ;;;;EAUL,YACJ,oBACA,+BACC,QAAQ;;;;;EAWL,QAAQ,QAAQ,mBAAmB,QAAQ;;;;;EAY3C,UAAU,qBAAqB,QAAQ;;;;;EAcvC,YAAY,qBAAqB,QAAQ;;;;EAazC,cAAc,QAAQ;;;;EAOtB,aAAa,iBAAiB,QAAQ;;;;UAWpC;;;;;UAkBA;;;;UAcA;;;;UAYA;;;;UAaM;UAwBA;UAoBA;;;;;UAkBN"}