@cullit/core 1.18.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -71,7 +71,7 @@ interface PipelineResult {
71
71
  duration: number;
72
72
  }
73
73
 
74
- declare const VERSION = "1.18.1";
74
+ declare const VERSION = "2.0.0";
75
75
  declare const DEFAULT_CATEGORIES: string[];
76
76
  declare const DEFAULT_MODELS: Record<string, string>;
77
77
  declare const AI_PROVIDERS: readonly ["anthropic", "openai", "gemini", "ollama", "none"];
@@ -82,6 +82,12 @@ declare const CHANGE_CATEGORIES: readonly ["features", "fixes", "breaking", "imp
82
82
  declare const AUDIENCES: readonly ["developer", "end-user", "executive"];
83
83
  declare const TONES: readonly ["professional", "casual", "terse", "edgy", "hype", "snarky"];
84
84
  declare const SOURCE_TYPES: readonly ["local", "jira", "linear", "gitlab", "bitbucket", "multi-repo"];
85
+ declare const TIERS: readonly ["free", "basic", "pro", "team", "enterprise"];
86
+ declare const PAID_TIERS: readonly ["basic", "pro", "team", "enterprise"];
87
+ declare const TEAM_TIERS: readonly ["team", "enterprise"];
88
+ declare const TEAM_PLANS: readonly ["team-5", "team-10", "team-25"];
89
+ type TeamPlan = (typeof TEAM_PLANS)[number];
90
+ declare const TEAM_PLAN_SEATS: Record<TeamPlan, number>;
85
91
 
86
92
  type LogLevel = 'quiet' | 'normal' | 'verbose';
87
93
  interface Logger {
@@ -219,7 +225,7 @@ declare function analyzeReleaseReadiness(cwd?: string): ReleaseAdvisory;
219
225
  * validateLicense() performs async remote validation with caching.
220
226
  * resolveLicense() remains sync for quick format-only checks (display).
221
227
  */
222
- type LicenseTier = 'free' | 'pro' | 'team' | 'enterprise';
228
+ type LicenseTier = 'free' | 'basic' | 'pro' | 'team' | 'enterprise';
223
229
  interface LicenseStatus {
224
230
  tier: LicenseTier;
225
231
  valid: boolean;
@@ -315,9 +321,13 @@ declare function fetchWithTimeout(url: string, init: RequestInit, timeoutMs?: nu
315
321
  /**
316
322
  * Rate Limiter — Sliding-window rate limiter with pluggable backends.
317
323
  *
324
+ * Backends:
325
+ * - MemoryRateLimiter (default) — in-process, single instance only
326
+ * - RedisRateLimiter — shared across instances via REDIS_URL
327
+ *
318
328
  * Usage:
319
329
  * const limiter = createRateLimiter({ limit: 30, windowMs: 60_000 });
320
- * const result = limiter.check('user-ip-or-key');
330
+ * const result = await limiter.check('user-ip-or-key');
321
331
  * if (!result.allowed) { // reject }
322
332
  */
323
333
  interface RateLimitResult {
@@ -327,9 +337,9 @@ interface RateLimitResult {
327
337
  resetAt: number;
328
338
  }
329
339
  interface RateLimiter {
330
- check(key: string): RateLimitResult;
340
+ check(key: string): RateLimitResult | Promise<RateLimitResult>;
331
341
  /** Remove all tracked entries */
332
- reset(): void;
342
+ reset(): void | Promise<void>;
333
343
  }
334
344
  interface RateLimiterOptions {
335
345
  /** Max requests per window (default: 30) */
@@ -378,4 +388,4 @@ declare function runPipeline(from: string, to: string, config: CullConfig, optio
378
388
  templateProfile?: string;
379
389
  }): Promise<PipelineResult>;
380
390
 
381
- export { AI_PROVIDERS, AUDIENCES, CHANGE_CATEGORIES, type ChangeCategory, type ChangeEntry, type Collector, type CollectorFactory, CoreErrorCode, type CoreErrorCodeValue, CullitError, DEFAULT_CATEGORIES, DEFAULT_MODELS, ENRICHMENT_TYPES, type EnrichedContext, type EnrichedTicket, type Enricher, type EnricherFactory, FilePublisher, type Generator, type GeneratorFactory, GitCollector, type GitCommit, type GitDiff, type LicenseStatus, type LicenseTier, type LogLevel, type Logger, MultiRepoCollector, OUTPUT_FORMATS, PUBLISHER_TYPES, type PipelineResult, type Publisher, type PublisherFactory, type RateLimitResult, type RateLimiter, type RateLimiterOptions, type ReleaseAdvisory, type ReleaseNotes, SOURCE_TYPES, type SemverBump, StdoutPublisher, TONES, type TeamFeature, TemplateGenerator, type UsageLimits, VERSION, analyzeReleaseReadiness, createLogger, createRateLimiter, escapeHtml, fetchWithTimeout, formatNotes, getCollector, getEnricher, getFeatureGating, getFormatter, getGenerator, getLatestTag, getPublisher, getRecentTags, getTierLimits, hasCollector, hasEnricher, hasGenerator, hasPublisher, isEnrichmentAllowed, isFeatureAllowed, isProviderAllowed, isPublisherAllowed, listCollectors, listEnrichers, listFormatters, listGenerators, listPublishers, registerCollector, registerEnricher, registerFormatter, registerGenerator, registerPublisher, reportUsage, resolveLicense, runPipeline, upgradeMessage, validateLicense };
391
+ export { AI_PROVIDERS, AUDIENCES, CHANGE_CATEGORIES, type ChangeCategory, type ChangeEntry, type Collector, type CollectorFactory, CoreErrorCode, type CoreErrorCodeValue, CullitError, DEFAULT_CATEGORIES, DEFAULT_MODELS, ENRICHMENT_TYPES, type EnrichedContext, type EnrichedTicket, type Enricher, type EnricherFactory, FilePublisher, type Generator, type GeneratorFactory, GitCollector, type GitCommit, type GitDiff, type LicenseStatus, type LicenseTier, type LogLevel, type Logger, MultiRepoCollector, OUTPUT_FORMATS, PAID_TIERS, PUBLISHER_TYPES, type PipelineResult, type Publisher, type PublisherFactory, type RateLimitResult, type RateLimiter, type RateLimiterOptions, type ReleaseAdvisory, type ReleaseNotes, SOURCE_TYPES, type SemverBump, StdoutPublisher, TEAM_PLANS, TEAM_PLAN_SEATS, TEAM_TIERS, TIERS, TONES, type TeamFeature, type TeamPlan, TemplateGenerator, type UsageLimits, VERSION, analyzeReleaseReadiness, createLogger, createRateLimiter, escapeHtml, fetchWithTimeout, formatNotes, getCollector, getEnricher, getFeatureGating, getFormatter, getGenerator, getLatestTag, getPublisher, getRecentTags, getTierLimits, hasCollector, hasEnricher, hasGenerator, hasPublisher, isEnrichmentAllowed, isFeatureAllowed, isProviderAllowed, isPublisherAllowed, listCollectors, listEnrichers, listFormatters, listGenerators, listPublishers, registerCollector, registerEnricher, registerFormatter, registerGenerator, registerPublisher, reportUsage, resolveLicense, runPipeline, upgradeMessage, validateLicense };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/constants.ts
2
- var VERSION = "1.18.1";
2
+ var VERSION = "2.0.0";
3
3
  var DEFAULT_CATEGORIES = ["features", "fixes", "breaking", "improvements", "chores"];
4
4
  var DEFAULT_MODELS = {
5
5
  anthropic: "claude-sonnet-4-20250514",
@@ -15,6 +15,15 @@ var CHANGE_CATEGORIES = ["features", "fixes", "breaking", "improvements", "chore
15
15
  var AUDIENCES = ["developer", "end-user", "executive"];
16
16
  var TONES = ["professional", "casual", "terse", "edgy", "hype", "snarky"];
17
17
  var SOURCE_TYPES = ["local", "jira", "linear", "gitlab", "bitbucket", "multi-repo"];
18
+ var TIERS = ["free", "basic", "pro", "team", "enterprise"];
19
+ var PAID_TIERS = ["basic", "pro", "team", "enterprise"];
20
+ var TEAM_TIERS = ["team", "enterprise"];
21
+ var TEAM_PLANS = ["team-5", "team-10", "team-25"];
22
+ var TEAM_PLAN_SEATS = {
23
+ "team-5": 5,
24
+ "team-10": 10,
25
+ "team-25": 25
26
+ };
18
27
 
19
28
  // src/logger.ts
20
29
  function createLogger(level = "normal") {
@@ -796,7 +805,7 @@ async function validateLicense() {
796
805
  if (res.ok) {
797
806
  const data = await res.json();
798
807
  const status2 = {
799
- tier: data.tier === "team" || data.tier === "enterprise" ? data.tier : data.tier === "pro" ? "pro" : "free",
808
+ tier: data.tier === "team" || data.tier === "enterprise" ? data.tier : data.tier === "pro" ? "pro" : data.tier === "basic" ? "basic" : "free",
800
809
  valid: data.valid !== false,
801
810
  message: data.message
802
811
  };
@@ -838,6 +847,7 @@ function upgradeMessage(feature) {
838
847
  }
839
848
  var TIER_LIMITS = {
840
849
  free: { generationsPerMonth: 5, maxProjects: 3 },
850
+ basic: { generationsPerMonth: 50, maxProjects: 10 },
841
851
  pro: { generationsPerMonth: 500, maxProjects: 100 },
842
852
  team: { generationsPerMonth: 2e3, maxProjects: 250 },
843
853
  enterprise: { generationsPerMonth: Infinity, maxProjects: Infinity }
@@ -850,7 +860,7 @@ var FEATURE_TIERS = {
850
860
  approvals: /* @__PURE__ */ new Set(["team", "enterprise"]),
851
861
  shared_history: /* @__PURE__ */ new Set(["team", "enterprise"]),
852
862
  project_templates: /* @__PURE__ */ new Set(["team", "enterprise"]),
853
- hosted_changelog: /* @__PURE__ */ new Set(["pro", "team", "enterprise"]),
863
+ hosted_changelog: /* @__PURE__ */ new Set(["basic", "pro", "team", "enterprise"]),
854
864
  branded_widget: /* @__PURE__ */ new Set(["team", "enterprise"]),
855
865
  team_publishers: /* @__PURE__ */ new Set(["team", "enterprise"]),
856
866
  org_settings: /* @__PURE__ */ new Set(["team", "enterprise"]),
@@ -986,8 +996,58 @@ var MemoryRateLimiter = class {
986
996
  }
987
997
  };
988
998
  function createRateLimiter(opts) {
999
+ const redisUrl = process.env["REDIS_URL"];
1000
+ if (redisUrl) {
1001
+ return new RedisRateLimiter(redisUrl, opts);
1002
+ }
989
1003
  return new MemoryRateLimiter(opts);
990
1004
  }
1005
+ var RedisRateLimiter = class {
1006
+ limit;
1007
+ windowMs;
1008
+ redisUrl;
1009
+ prefix;
1010
+ constructor(redisUrl, opts = {}) {
1011
+ this.limit = opts.limit ?? 30;
1012
+ this.windowMs = opts.windowMs ?? 6e4;
1013
+ this.redisUrl = redisUrl;
1014
+ this.prefix = "cullit:rl:";
1015
+ }
1016
+ async check(key) {
1017
+ const now = Date.now();
1018
+ const windowStart = now - this.windowMs;
1019
+ const redisKey = this.prefix + key;
1020
+ try {
1021
+ const res = await fetch(this.redisUrl, {
1022
+ method: "POST",
1023
+ headers: { "Content-Type": "application/json" },
1024
+ body: JSON.stringify([
1025
+ ["ZREMRANGEBYSCORE", redisKey, "0", String(windowStart)],
1026
+ ["ZCARD", redisKey],
1027
+ ["ZADD", redisKey, String(now), `${now}-${Math.random().toString(36).slice(2, 8)}`],
1028
+ ["PEXPIRE", redisKey, String(this.windowMs)]
1029
+ ]),
1030
+ signal: AbortSignal.timeout(3e3)
1031
+ });
1032
+ if (!res.ok) return this.fallbackAllow(now);
1033
+ const results = await res.json();
1034
+ const count = results[1]?.result ?? 0;
1035
+ const remaining = Math.max(0, this.limit - count);
1036
+ const resetAt = Math.ceil((now + this.windowMs) / 1e3);
1037
+ if (count >= this.limit) {
1038
+ return { allowed: false, remaining: 0, resetAt };
1039
+ }
1040
+ return { allowed: true, remaining: remaining - 1, resetAt };
1041
+ } catch {
1042
+ return this.fallbackAllow(now);
1043
+ }
1044
+ }
1045
+ fallbackAllow(now) {
1046
+ return { allowed: true, remaining: this.limit, resetAt: Math.ceil((now + this.windowMs) / 1e3) };
1047
+ }
1048
+ async reset() {
1049
+ }
1050
+ };
991
1051
 
992
1052
  // src/index.ts
993
1053
  registerCollector("local", (config) => new GitCollector(config.source?.repoPath));
@@ -1210,9 +1270,14 @@ export {
1210
1270
  GitCollector,
1211
1271
  MultiRepoCollector,
1212
1272
  OUTPUT_FORMATS,
1273
+ PAID_TIERS,
1213
1274
  PUBLISHER_TYPES,
1214
1275
  SOURCE_TYPES,
1215
1276
  StdoutPublisher,
1277
+ TEAM_PLANS,
1278
+ TEAM_PLAN_SEATS,
1279
+ TEAM_TIERS,
1280
+ TIERS,
1216
1281
  TONES,
1217
1282
  TemplateGenerator,
1218
1283
  VERSION,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cullit/core",
3
- "version": "1.18.1",
3
+ "version": "2.0.0",
4
4
  "type": "module",
5
5
  "description": "Core engine for Cullit — AI-powered release note generation.",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -38,7 +38,7 @@
38
38
  "access": "public"
39
39
  },
40
40
  "dependencies": {
41
- "@cullit/config": "1.18.1"
41
+ "@cullit/config": "2.0.0"
42
42
  },
43
43
  "scripts": {
44
44
  "build": "tsup src/index.ts --format esm --dts --clean",