@critical-path/core

中文文档
0.62.0发布于 2026/11/05MITTypeScriptES Module构建来源已验证近 30 天下载 4894

Core headless domain engine, plugin architecture, and data stores for Critical Path

原始使用文档
文档目录13 节
  1. 📦 Features
  2. 🚀 Usage Examples
  3. 1. Initializing the Engine & Subscribing to Domain Events
  4. 2. Invariants Live in the Engine
  5. 3. DAG Dependency Cycle Prevention
  6. 4. Tracking Creative Deliverables & Rollup Metrics
  7. 5. Evaluating Implied Statuses & Dependency Readiness
  8. 6. Task Metrics, Inferred Actuals & EVM
  9. 7. Headless Workload & Capacity Distribution (Streamgraphs)
  10. 8. Configuring Work Schedules, Working Hours & Holidays
  11. 9. Fractional Lexical Indexing (Kanban Reordering)
  12. 10. Code-Immune Mention & Reference Extraction
  13. 🔌 Creating a Custom Plugin

@critical-path/core

The foundational domain engine, domain event bus, graph cycle detection, and storage adapters for Critical Path.

Upgrading? Upgrade every @critical-path/* package together. Coding agents: follow UPGRADING.md; breaking changes are marked BREAKING in this package's CHANGELOG.md.


📦 Features

  • One engine, one set of rules: CriticalPathEngine enforces every invariant (workflow statuses and transitions, derived fields, references, permissions) for direct callers, the HTTP API, the client and MCP alike.
  • Typed Domain Event Bus: In-memory pub/sub DomainEventBus enabling reactive subscriptions to granular domain events (task.created, task.status_changed, time.logged, dependency.added, etc.).
  • DAG Graph Cycle Invariant: Built-in topological validation (detectDependencyCycle, CircularDependencyError) to ensure strict acyclic task dependency graphs. Dependencies are blocking or relates_to (informational, never part of the schedule or cycle check); precedenceOf reads a dependency's direction.
  • Custom Field Value Object Validation: Strict type-checking (validateCustomFieldValues, CustomFieldValidationError) against configured project field schemas.
  • Interface Segregated Repositories: Focused repository contracts (ProjectRepository, TaskRepository, WorkflowRepository, etc.) composed into StorageAdapter.
  • Plugin Lifecycle Architecture: Extensible hooks (beforeTaskCreate, afterTaskUpdate, beforeTaskDelete, etc.).
  • Multiple Built-in Storage Adapters:
    • InMemoryStore: Fast, zero-config in-memory storage for local dev and testing.
    • SQLiteStore: Embedded relational database storage powered by native Node.js SQLite (node:sqlite).
    • FirebaseStore: Firestore collection mapping for cloud-native web and mobile backends.
  • File Storage Adapters for Attachments:
    • InMemoryFileStore: Lightweight in-memory binary asset storage with presigned URL simulation.
    • S3StorageAdapter: S3 / MinIO / Cloudflare R2 adapter driven by your AWS SDK v3 client, command classes and getSignedUrl presigner (core itself has no AWS dependency). Supports signed uploads and private-bucket signed downloads.
    • FirebaseStorageAdapter: Google Cloud Storage & Firebase Storage bucket adapter.
  • Threaded Conversations, Discussions & Emoji Reactions:
    • Nested replies (parentId), multi-author taxonomy (user, agent, system), emoji reactions (addCommentReaction, removeCommentReaction), and real-time domain event streaming (comment.created, comment.updated, comment.deleted, comment.reaction.added, comment.reaction.removed).
  • Multi-Assignee Support & Agent Collaboration:
    • First-class TaskAssignee taxonomy supporting co-assignments across users, autonomous AI agents, and teams with role metadata and custom avatar URLs.
  • Bidirectional Workflow Transitions:
    • Symmetrical transition helpers (getAllowedNextStatuses, getAllowedPreviousStatuses) and engine methods for moving tasks backwards and forwards through customized workflow states.
  • Universal Semantic Status & Implied Status Framework:
    • Clean 3-tier status architecture: Universal Semantic Statuses (not_started, in_progress, completed, canceled), customizable workflow-defined statuses mapped by category, and automatic system-derived implied statuses (isReady, isBlocked, blockingTaskIds, isOverdue, isUpcoming, isUnplanned, isUnassigned, isStalled, isOverEstimate, isPaceWarning).
  • Deliverables:
    • Outputs a project produces, linked to the tasks that produce them, with a configurable status set mapped to semantic categories (deliverableStatusDefinitions; default DEFAULT_DELIVERABLE_STATUSES), an automatic deliveredAt, and outputUrls.
    • Presets for creative production: CREATIVE_DELIVERABLE_STATUSES and the DEFAULT_CREATIVE_WORKFLOW task workflow.
    • Rollup calculations via getDeliverableSummary() delivering progress percentages, completed tasks, and total estimated/logged hours across assigned tasks.
  • Ladder of Abstraction & Critical Path Method (CPM):
    • Multi-scale timeline synthesis: Macro bird's-eye phase rollups (getTimelineLadder({ level: 'macro' })), Standard Gantt view with topological CPM forward/backward passes and total float/slack, and Concrete grounding (attachments, deliverables, checklist items, daily effort histograms, and reality deltas).
    • Single-task contextual drilldown via getTaskLadder(taskId).
    • Comprehensive CPM analysis via calculateCriticalPath(projectId) identifying project bottleneck tasks and critical path duration.
  • Work Schedules, Working Hours, Working Days & Holidays Subsystem:
    • Configurable WorkSchedule data model defining day-by-day active working hours (e.g. 09:00–17:00), non-working days/weekends, and organization or regional Holiday exemptions.
    • Multi-level schedule inheritance hierarchy: Task Assignee / Team -> Project -> Global Engine Default (DEFAULT_WORK_SCHEDULE), used by workload and capacity calculations. CPM date projections use the project (or engine default) schedule.
    • Calendar math domain operations (addWorkingHours, subtractWorkingHours, getWorkingHoursBetween, getWorkingDaysBetween, getNetAvailableCapacity).
    • Automatic exclusion of weekends and holidays during Critical Path Method (CPM) forward and backward schedule passes (earlyStartDate, earlyFinishDate, lateStartDate, lateFinishDate, totalWorkingHours, projectEndDate).
    • Calendar-aware capacity reductions in Workload Distribution and working-day schedule variance metrics (scheduleVarianceWorkingDays).
  • Headless Workload & Capacity Distribution (Streamgraphs & Capacity Planning):
    • Time-series aggregations across customizable intervals (day, week, month) and dimensions (assignee, team, taskType, priority, status).
    • Pluggable effort distribution metrics (scheduled, logged, remaining, blended) with contiguous, gap-free calendar buckets and zero-filled tabular series matrices ready for D3 (d3.stack().offset(d3.stackOffsetWiggle)).
    • Dynamic capacity modeling per person and team, reporting bucket-level capacity thresholds and utilization ratios (totalHours / totalCapacity).
  • Base-62 Fractional Lexical Indexing:
    • Zero-dependency Base-62 fractional indexing (generateKeyBetween, generateNKeysBetween) for instant Kanban and backlog reordering without re-indexing or array shifting.
  • Code-Immune Mention & Tag Extraction:
    • High-performance mention parsing (extractMentions) with automatic markdown code suppression (stripMarkdownCode) ignoring code blocks, inline code, and URLs while parsing user and agent handles.
  • Task Lifecycle & Semantic Status Predicates:
    • Full suite of functional predicates (isDraftTask, isArchivedTask, isTrashedTask, isTaskCompleted, isTaskInProgress, isTaskActive, isTempTaskId) for headless UI rendering.
  • Completion Timestamping & Progress Invariant:
    • Automatic actualEndDate and completedAt lifecycle timestamping upon entering completed or canceled statuses, with automatic timestamp clearing and progress reset (100 -> 0) when reopening tasks.

🚀 Usage Examples

1. Initializing the Engine & Subscribing to Domain Events

import { CriticalPathEngine, SQLiteStore, type TaskStatusChangedEvent } from '@critical-path/core';

const store = new SQLiteStore({ filename: 'critical-path.db' });
const engine = new CriticalPathEngine({ store });

// Subscribe to specific typed domain events
engine.events.subscribe<TaskStatusChangedEvent>('task.status_changed', (event) => {
  console.log(`Task ${event.aggregateId} moved from ${event.payload.previousStatus} to ${event.payload.newStatus}`);
});

// Or subscribe to all domain events with wildcard
engine.events.subscribe('*', (event) => {
  console.log(`[Domain Event] ${event.name}`, event);
});

store must be an adapter instance (InMemoryStore by default). Critical Path does not store users: pass your app's user directory as users (an array, or a function called with the acting user) so workload and capacity calculations use real names, weekly capacity and schedules:

const engine = new CriticalPathEngine({
  store,
  users: async (actor) => (await myAuth.listUsers(actor?.tenantId)).map(toCriticalPathUser)
});

2. Invariants Live in the Engine

import { CriticalPathEngine } from '@critical-path/core';

const engine = new CriticalPathEngine();
const project = await engine.createProject({ name: 'Payments' });
const task = await engine.createTask({ projectId: project.id, title: 'Implement Payment Gateway', priority: 'high' });

// Workflow transitions are enforced; semanticStatus and timestamps are derived
await engine.updateTask(task.id, { status: 'in_progress' });

// Hours come from time entries
await engine.logTime({ taskId: task.id, hours: 3.5, isBillable: true });

3. DAG Dependency Cycle Prevention

import { CircularDependencyError } from '@critical-path/core';

try {
  await engine.addDependency({
    taskId: 'task_C',
    dependsOnTaskId: 'task_A',
    type: 'blocking'
  });
} catch (error) {
  if (error instanceof CircularDependencyError) {
    console.error(`Blocked cyclic dependency! Cycle path: ${error.cyclePath.join(' -> ')}`);
  }
}

Remove a dependency with engine.removeDependency(dependencyId) (publishes dependency.removed).

Deletes cascade: deleteTask removes subtasks (or detaches them with { subtasks: 'detach' }), dependencies, comments, attachments with their stored files, and time entries; deleteProject also removes containers, iterations and deliverables; deleting a container, iteration or deliverable clears the reference on its tasks. The activity log is kept as an audit trail. On stores with transaction (such as SQLiteStore), each cascade is atomic: if any step fails, for example because a beforeTaskDelete hook throws for a subtask, nothing is deleted. Events, after-hooks and file deletions run only after commit.

Cycle detection follows the full upstream chain, so indirect cycles (A → B → C → D → A) are rejected as well as direct ones.

Request Payload Schemas (@critical-path/core/schemas)

import { CreateTaskSchema, UpdateTaskSchema, parsePayload } from '@critical-path/core/schemas';

const input = parsePayload(CreateTaskSchema, await request.json()); // throws ValidationError with `issues`
await engine.createTask(input);

Create and update schemas exist for workflows, projects, tasks, dependencies, deliverables, teams, containers, iterations, comments, reactions, attachments and time entries. They are strict: server-assigned fields, identity fields and unknown keys are rejected. The schemas live on a subpath so importing @critical-path/core in a browser bundle does not pull in zod. A compile-time test fails the build if a domain type gains a field its schema lacks.

Custom Storage Adapters & Conformance

Implement StorageAdapter for your database and verify it with the same suite the built-in adapters pass:

import { describe, it, expect } from 'vitest';
import { runStorageAdapterConformance } from '@critical-path/core/testing';

runStorageAdapterConformance({ name: 'PostgresStore', createStore: () => new PostgresStore(db), describe, it, expect });

Upload Limits

new CriticalPathEngine({
  fileStorage,
  uploads: { maxBytes: 25 * 1024 * 1024, allowedMimeTypes: ['image/*', 'application/pdf'] }
});

Limits apply to direct uploads (checked against the decoded size), presigned uploads (content type) and registered attachments (declared sizeBytes and mimeType). Presigned uploads go straight to storage, so also cap their size in your bucket policy.

Querying & Pagination

const page = await engine.queryTasks({ projectId, status: ['todo', 'in_progress'], assigneeId: 'ana', limit: 50 });
const next = await engine.queryTasks({ projectId, limit: 50, cursor: page.nextCursor });
const feed = await engine.queryActivities({ projectId, limit: 20 }); // newest first
const thread = await engine.queryComments({ taskId, limit: 50 }); // oldest first

Tasks and comments page oldest first and activities newest first, using keyset cursors (createdAt, then id). SQLiteStore filters and pages in SQL with indexes. FirebaseStore pushes down the project filter and applies the rest in memory. Without projectId, results are limited to projects the actor can read.

Authorization & Multi-Tenancy

import { CriticalPathEngine, createRolePolicy } from '@critical-path/core';

const engine = new CriticalPathEngine({ store, authorize: createRolePolicy() });
const asVic = engine.withActor({ userId: 'vic', tenantId: 'acme' });
await asVic.createTask({ projectId, title: 'x' }); // ForbiddenError if vic is only a viewer

Checks run on withActor views (the base engine is trusted). Roles come from project.members: users ({ userId, role }) or whole teams ({ teamId, role }), with roles viewer < contributor < project_manager < admin. Project creators become admin. Actors with roles: ['admin'] are superusers. Projects the actor cannot read, or that belong to another tenant, behave as if they do not exist (NotFoundError). Lists are filtered. Records created through a view are stamped with the actor's tenantId. See the docs site's Authorization page for the full permission matrix and custom policies.

Custom fields

Every entity (projects, workflows, teams, tasks, containers, iterations, deliverables, comments, attachments, time entries, dependencies, webhooks) accepts a customFields JSON object on create and update for your own data. Task custom fields are validated against the project's customFieldDefinitions; on other entities any JSON object is stored as-is. Updates replace the whole object.

Webhooks

const { webhook, secret } = await engine.createWebhook({
  name: 'CI',
  url: 'https://ci.example.com/hooks',
  events: ['task.created', 'task.status_changed'] // or ['*']
});
// Receivers: verifyWebhookSignature({ secret, body, timestamp, signature })

Every domain event can be delivered. Deliveries carry X-CriticalPath-Signature (HMAC-SHA256 over "<timestamp>.<body>"), time out after 10s, and retry with exponential backoff (webhookDelivery options). The default queue is in-process. For durable delivery, pass new OutboxWebhookQueue(store) as webhookDelivery.queue and call outbox.start() (or processDue() from a cron job). On SQLiteStore, jobs are written in the same transaction as the mutation that produced them. You can also bring your own queue that calls engine.webhooks.deliver(job). Targets resolving to private addresses are refused before every attempt, and redirects are not followed. Secrets are only returned on creation.

Attributing Mutations to a User (withActor)

const asAlice = engine.withActor({ userId: 'alice', username: 'Alice' });
await asAlice.updateTask(taskId, { status: 'in_progress' }); // activity actorId: 'alice'
await asAlice.addComment({ taskId, content: 'On it' }); // authorId: 'alice'

withActor returns a per-request view that shares the store, plugins and event bus with the base engine. On the view, every write is attributed to the actor: activity log entries, comment authors, reactions, time entries, attachment uploaders, and the default task reporterId. updateTask never reads identity from the update payload; trusted server-side automation can pass updateTask(id, updates, { actorId }) explicitly. Without a view, addComment, reactions and createAttachment accept an optional authorId / userId / uploaderId and fall back to 'system'. @critical-path/server runs every request through a view (the resolved user, or anonymous).

Other domain errors are exported for callers and route handlers to map: ValidationError (bad input, e.g. non-positive logged hours) and NotFoundError (missing referenced entity). engine.deleteProject(id) deletes a project and its tasks through deleteTask, then publishes project.deleted.

4. Tracking Creative Deliverables & Rollup Metrics

import { CriticalPathEngine, CREATIVE_DELIVERABLE_STATUSES } from '@critical-path/core';

const engine = new CriticalPathEngine();
// Creative statuses for this project's deliverables (the default set is planned / in_progress / in_review / delivered / canceled)
await engine.updateProject('proj_1', { deliverableStatusDefinitions: CREATIVE_DELIVERABLE_STATUSES });

// 1. Create deliverable; domain-specific details go in customFields
const deliverable = await engine.createDeliverable({
  projectId: 'proj_1',
  title: 'Brand Hero Video 30s',
  customFields: { format: 'ProRes 422HQ', resolution: '3840x2160', fps: 24 }
});

// 2. Attach tasks to deliverable
const storyboard = await engine.createTask({
  projectId: 'proj_1',
  deliverableId: deliverable.id,
  title: 'Storyboard & Animatic',
  status: 'approved',
  estimatedHours: 12,
  progress: 100
});
// Logged hours come from time entries
await engine.logTime({ taskId: storyboard.id, hours: 12 });

// 3. Rollup metrics
const summary = await engine.getDeliverableSummary(deliverable.id);
console.log(`Progress: ${summary?.progressPercentage}%, Total tasks: ${summary?.totalTasks}`);

5. Evaluating Implied Statuses & Dependency Readiness

// Evaluates task status category, upstream dependency states, schedules, assignees, and pace
const state = await engine.getTaskLifecycleState('task_123');

if (state?.isBlocked) {
  console.warn(`Task blocked by upstream tasks: ${state.blockingTaskIds.join(', ')}`);
} else if (state?.isReady) {
  console.log('All dependencies satisfied! Task is ready to start.');
}

if (state?.isOverEstimate) {
  console.warn('Task logged hours have exceeded estimated hours!');
}
if (state?.isPaceWarning) {
  console.warn('Task has been active longer than its estimated duration!');
}

6. Task Metrics, Inferred Actuals & EVM

// Calculate comprehensive task metrics
const metrics = await engine.getTaskMetrics('task_123');

console.log('Inferred Actuals:', metrics?.inferredActuals);
// { actualStartDate: '...', isStartDateInferred: false, activeWorkingHours: 18.5 }

console.log('Progress Inference:', metrics?.progress);
// { progressPercentage: 80, source: 'todos', breakdown: { todoProgress: 80 } }

console.log('Earned Value Management:', metrics?.evm);
// { plannedValue: 20, earnedValue: 16, actualCost: 18, cpi: 0.89, spi: 0.8 }

// Reconstruct historical progress time-series and curve profile
const history = await engine.getTaskProgressHistory('task_123');
console.log('Curve Profile:', history?.curveProfile);
// 's_curve' | 'linear' | 'early_surge' | 'late_rush' | 'stalled'

7. Headless Workload & Capacity Distribution (Streamgraphs)

// Calculate weekly capacity and workload distribution across assignees
const workload = await engine.getWorkloadDistribution('proj_123', {
  interval: 'week',
  groupBy: 'assignee',
  metric: 'blended'
});

console.log('Series Keys (Assignee IDs):', workload.seriesKeys);
console.log('Contiguous Weekly Buckets:', workload.buckets);
// Each bucket contains zero-filled numeric values for all seriesKeys:
// {
//   date: '2026-09-01',
//   timestamp: 1788220800000,
//   values: { alice: 32, bob: 18, unassigned: 0 },
//   capacity: { alice: 40, bob: 40 },
//   totalHours: 50,
//   totalCapacity: 80,
//   utilizationRatio: 0.625
// }

8. Configuring Work Schedules, Working Hours & Holidays

import { CriticalPathEngine, type WorkSchedule, addWorkingHours } from '@critical-path/core';

// Define a project schedule with 4-day workweeks and holidays
const engineeringSchedule: WorkSchedule = {
  timezone: 'UTC',
  days: [
    { dayOfWeek: 0, isWorkingDay: false }, // Sunday
    { dayOfWeek: 1, isWorkingDay: true, hours: [{ start: '09:00', end: '17:00' }] }, // Monday
    { dayOfWeek: 2, isWorkingDay: true, hours: [{ start: '09:00', end: '17:00' }] }, // Tuesday
    { dayOfWeek: 3, isWorkingDay: true, hours: [{ start: '09:00', end: '17:00' }] }, // Wednesday
    { dayOfWeek: 4, isWorkingDay: true, hours: [{ start: '09:00', end: '17:00' }] }, // Thursday
    { dayOfWeek: 5, isWorkingDay: false }, // Friday
    { dayOfWeek: 6, isWorkingDay: false }  // Saturday
  ],
  holidays: [
    { date: '2026-12-25', name: 'Christmas Day' },
    { date: '2026-12-26', name: 'Boxing Day' }
  ]
};

const engine = new CriticalPathEngine({
  defaultSchedule: engineeringSchedule
});

// CPM automatically rolls over non-working days and holidays
const cpm = await engine.calculateCriticalPath('proj_123', {
  projectStartDate: '2026-09-01T09:00:00Z',
  schedule: engineeringSchedule
});

console.log('Project End Date (skipping weekends & holidays):', cpm.projectEndDate);
console.log('Total Working Hours on Critical Path:', cpm.totalWorkingHours);

// Schedule each task on its assignee's (or team's) calendar instead, using the `users` directory
const byAssignee = await engine.calculateCriticalPath('proj_123', { calendars: 'assignee' });

// ...and limit each assignee to one task at a time (resource levelling)
const leveled = await engine.calculateCriticalPath('proj_123', { calendars: 'assignee', levelResources: true });
console.log(leveled.unleveledProjectEndDate, '->', leveled.projectEndDate);

With calendars: 'assignee' (or criticalPathCalendars: 'assignee' on the engine), each task uses its assignee's schedule, then its team's, then the project calendar. Passes run on dates, slack is measured in each task's calendar, and each calendar is evaluated in its own timezone (IANA name; default UTC), including daylight-saving changes. levelResources: true additionally delays tasks so nobody works on two at once (priority rule levelingPriority, default least slack), reporting levelingDelayHours and waitingOn per task. Tasks with allocation below 1 take that share of their assignee (a 16h task at 0.5 spans 32 working hours); levelling fits them within 100% per person, and overallocations lists who is booked above 100% and when. Teams act as pools of headcount people (default: member count) shared by team-only tasks and members' own tasks. Finished tasks take no time. calculatePortfolioCriticalPath({ projectIds }) levels several projects together, sharing people and teams across them; includeHiddenWork also counts unreadable projects' work as 'hidden'. effort: 'remaining' schedules only what is left on in-progress tasks.

Save the plan with engine.createBaseline(projectId, { name }) and pass baselineId to later analyses to see how far each task and the project end moved. Tasks with subtasks are summary tasks, reported as roll-ups of their leaves (isSummary); dependencies and deadlines on them apply to every leaf. Unestimated tasks use defaultEstimateHours (default 1) and are listed in unestimatedTaskIds. Tasks with isMilestone: true are zero-duration checkpoints (their estimates are ignored). A deadline on any task caps its late finish: a forecast that misses it shows as negative slack along the path driving it, with deadlineDate and deadlineVarianceHours per task and missedDeadlineTaskIds on the analysis. A date-only deadline means the end of that working day. earliestStart (start no earlier than) and dependency lagHours (wait after the predecessor; negative for a lead) constrain the forward pass. Project-calendar mode needs a start date to apply these date constraints.

9. Fractional Lexical Indexing (Kanban Reordering)

import { generateKeyBetween, generateNKeysBetween } from '@critical-path/core';

// Insert an item between two adjacent items without shifting arrays:
const firstItemOrder = 'a0';
const secondItemOrder = 'a1';

const newItemOrder = generateKeyBetween(firstItemOrder, secondItemOrder);
// -> 'a0V' (lexicographically between 'a0' and 'a1')

// Generate keys for multiple items inserted at once:
const newKeys = generateNKeysBetween(firstItemOrder, secondItemOrder, 3);

10. Code-Immune Mention & Reference Extraction

import { extractMentions } from '@critical-path/core';

const markdown = `
Check with @alice and agent @gemini-bot.
Ignore \`@not_a_user\` in inline code and code blocks:
\`\`\`ts
const query = "@ignored";
\`\`\`
`;

const mentions = extractMentions(markdown);
// -> ['alice', 'gemini-bot']

🔌 Creating a Custom Plugin

import type { CriticalPathPlugin } from '@critical-path/core';

export const auditPlugin: CriticalPathPlugin = {
  id: 'audit-logger',
  name: 'Audit Logger',
  version: '1.0.0',
  hooks: {
    beforeTaskCreate: (task) => {
      return { ...task, tags: [...(task.tags || []), 'AUDITED'] };
    }
  }
};

Plugins can also contribute init(engine) (awaited via engine.ready), customFieldTypes (validated custom field types for project customFieldDefinitions), and HTTP routes / middleware served by @critical-path/server (routes appear in /openapi.json, described by their optional openapi metadata). Before-hook output is validated like caller input, and after-hook errors are logged instead of failing the write.

版本与发布记录

@critical-path/core 共发布 81 个版本,首次发布于 2026/08/24;过去 12 个月发布了 81 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.62.0latest2026/11/055 天前
0.61.02026/11/037 天前
0.60.02026/11/038 天前
0.59.02026/10/2813 天前
0.58.02026/10/2813 天前
0.57.02026/10/2813 天前

关于 @critical-path/core 的常见问题

@critical-path/core 是什么?

Core headless domain engine, plugin architecture, and data stores for Critical Path

如何安装 @critical-path/core?

在项目目录运行 npm install @critical-path/core,或使用 pnpm add / yarn add / bun add。

@critical-path/core 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@critical-path/core 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@critical-path/core 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=24.0.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@critical-path/core 可以商用吗?

@critical-path/core 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@critical-path/core 的下载量有多少?

根据 npm 官方下载统计,@critical-path/core 在 2026/10/11 – 2026/11/09 这 30 天内被下载 4,894 次,日均约 163 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@critical-path/core 还在维护吗?

最新版本 0.62.0 发布于 2026/11/05(5 天前);至今共发布 81 个版本,其中过去 12 个月发布了 81 个;首次发布于 2026/08/24。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。