npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@ooopenlab/quiz-shared

v1.0.0

Published

Shared utilities and components for SuperQuiz modules

Downloads

26

Readme

測驗共享程式庫

跨測驗系統使用的共享工具、常數和輔助函數。

概述

此程式庫包含在測驗系統不同部分間共享的通用工具、常數和輔助函數。它為可重複使用的程式碼提供了集中式位置。

工具

ID 生成

import { generateId } from '@ooopenlab/quiz-shared';

const quizId = generateId('quiz');     // quiz_1234567890_abc123def
const moduleId = generateId('module'); // module_1234567890_xyz789ghi

驗證輔助器

import { isValidEmail, isValidUrl } from '@ooopenlab/quiz-shared';

const emailValid = isValidEmail('[email protected]'); // true
const urlValid = isValidUrl('https://example.com');  // true

HTML 清理

import { sanitizeHtml } from '@ooopenlab/quiz-shared';

const cleanHtml = sanitizeHtml('<p>Safe content</p><script>alert("xss")</script>');
// 回傳: '<p>Safe content</p>'

物件工具

import { deepClone } from '@ooopenlab/quiz-shared';

const original = { quiz: { title: 'Test' } };
const copy = deepClone(original);
copy.quiz.title = 'New Title'; // original 保持不變

函數工具

import { debounce, throttle } from '@ooopenlab/quiz-shared';

// 防抖 API 呼叫
const debouncedSave = debounce(saveQuiz, 300);

// 節流滾動事件
const throttledScroll = throttle(handleScroll, 100);

常數

測驗常數

import { QUIZ_CONSTANTS } from '@ooopenlab/quiz-shared';

// 畫面類型
QUIZ_CONSTANTS.SCREEN_TYPES.COVER;    // 'cover'
QUIZ_CONSTANTS.SCREEN_TYPES.QUESTION; // 'question'
QUIZ_CONSTANTS.SCREEN_TYPES.RESULT;   // 'result'

// 路由
QUIZ_CONSTANTS.ROUTES.QUIZ_BASE;      // '/quiz/[quizId]'
QUIZ_CONSTANTS.ROUTES.QUIZ_QUESTION;  // '/quiz/[quizId]/question/[questionIndex]'
QUIZ_CONSTANTS.ROUTES.QUIZ_RESULT;    // '/quiz/[quizId]/result'

// 功能標誌
QUIZ_CONSTANTS.FEATURES.MULTI_QUESTIONS; // 'multiQuestions'
QUIZ_CONSTANTS.FEATURES.IS_FACTOR;       // 'isFactor'
QUIZ_CONSTANTS.FEATURES.SCORED_RESULT;   // 'scoredResult'
QUIZ_CONSTANTS.FEATURES.HAS_GEN_AI;      // 'hasGenAI'

// 預設值
QUIZ_CONSTANTS.DEFAULTS.MODULE_VERSION;   // '1.0.0'
QUIZ_CONSTANTS.DEFAULTS.TEXTAREA_ROWS;    // 4
QUIZ_CONSTANTS.DEFAULTS.MAX_FILE_SIZE;    // 5242880 (5MB)
QUIZ_CONSTANTS.DEFAULTS.DEBOUNCE_DELAY;   // 300ms
QUIZ_CONSTANTS.DEFAULTS.THROTTLE_LIMIT;   // 100ms

// 驗證規則
QUIZ_CONSTANTS.VALIDATION.MIN_PASSWORD_LENGTH; // 8
QUIZ_CONSTANTS.VALIDATION.MAX_TEXT_LENGTH;     // 1000
QUIZ_CONSTANTS.VALIDATION.MAX_TEXTAREA_LENGTH; // 5000
QUIZ_CONSTANTS.VALIDATION.MAX_FILE_COUNT;      // 10

類型定義

import type { ScreenType, FeatureFlag } from '@ooopenlab/quiz-shared';

const screen: ScreenType = 'cover'; // 'cover' | 'question' | 'result'
const feature: FeatureFlag = 'multiQuestions'; // 有效的功能標誌

API 參考

函數

| 函數 | 描述 | 參數 | 回傳值 | |----------|-------------|------------|---------| | generateId(prefix?) | 生成唯一 ID | prefix: string | string | | isValidEmail(email) | 驗證電子郵件格式 | email: string | boolean | | isValidUrl(url) | 驗證 URL 格式 | url: string | boolean | | sanitizeHtml(html) | 移除危險的 HTML | html: string | string | | deepClone(obj) | 深度複製物件 | obj: T | T | | debounce(func, wait) | 防抖函數呼叫 | func: Function, wait: number | Function | | throttle(func, limit) | 節流函數呼叫 | func: Function, limit: number | Function |

檔案結構

libs/quiz-shared/
├── src/
│   ├── utils.ts          # 工具函數
│   ├── constants.ts      # 常數和類型
│   └── index.ts          # 公開 API 匯出
├── project.json          # NX 專案設定
└── README.md            # 此檔案

依賴規則與循環依賴預防

架構分層

quiz-shared (基底層)
    ↑
quiz-form-fields (UI 層)
    ↑
quiz-modules (業務邏輯層)

依賴規則

  1. quiz-shared (type:util):

    • 🚫 不可依賴任何其他 quiz 程式庫
    • ✅ 只提供純工具函數、常數和類型定義
    • ✅ 可被其他所有程式庫使用
  2. 其他程式庫對 quiz-shared 的使用

    • ✅ quiz-form-fields 可以使用 quiz-shared
    • ✅ quiz-modules 可以使用 quiz-shared
    • ✅ 應用程式可以直接使用 quiz-shared

防止循環依賴的檢查清單

  • [ ] quiz-shared 中不包含任何 import '@ooopenlab/quiz-*'
  • [ ] 新增功能時先檢查是否屬於基礎工具,應放在 quiz-shared
  • [ ] 使用 nx graph 指令檢查依賴圖
  • [ ] 利用 NX tags 進行依賴限制 ("tags": ["scope:quiz", "type:util"])

在其他程式庫中的使用

// 在 quiz-modules 中
import { QUIZ_CONSTANTS, generateId } from '@ooopenlab/quiz-shared';

// 在 quiz-form-fields 中
import { debounce, sanitizeHtml } from '@ooopenlab/quiz-shared';