@patternbank/core 0.1.1 → 0.1.2
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/README.md +90 -0
- package/package.json +2 -2
package/README.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# @patternbank/core
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@patternbank/core)
|
|
4
|
+
|
|
5
|
+
Shared domain logic for [PatternBank](https://pattern-bank.vercel.app) — spaced repetition, sync merge, and LeetCode import for the [web app](https://github.com/DerekZ-113/Pattern-Bank) and the iOS app. One implementation, tested once, shipped to both.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @patternbank/core
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
`@supabase/supabase-js ^2` is an **optional** peer dependency — only needed if you use the cloud-data factories. The domain logic (scheduling, merging, transforms) has zero runtime dependencies.
|
|
14
|
+
|
|
15
|
+
**How PatternBank consumes it:** the web app imports the package **as workspace source** (this repo's `packages/core`, via tsconfig paths — changes ship with the app, no publish step). The React Native app **exact-pins the npm release**. `dist/` (dual ESM + CJS, bundler-friendly types) exists for the npm consumers.
|
|
16
|
+
|
|
17
|
+
## Quick start
|
|
18
|
+
|
|
19
|
+
Bind the cloud layer once, injecting your platform's Supabase client:
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { createCloudData } from "@patternbank/core";
|
|
23
|
+
import { supabase } from "./supabaseClient"; // your platform client, or null
|
|
24
|
+
|
|
25
|
+
// null client → every function no-ops with { data: null, error: null },
|
|
26
|
+
// so signed-out / unconfigured builds need no branching.
|
|
27
|
+
export const cloudData = createCloudData({ supabase, timeoutMs: 10_000 });
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Run a full sign-in sync — fail-closed, platform persists the result:
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import { performFullSync } from "@patternbank/core";
|
|
34
|
+
|
|
35
|
+
const result = await performFullSync({
|
|
36
|
+
userId,
|
|
37
|
+
cloud: cloudData, // the functions createCloudData returned
|
|
38
|
+
storage, // StorageAdapter — used ONLY for the prune watermark
|
|
39
|
+
local: { problems, reviewLog, reviewEvents, preferences, problemTombstones, dataReset },
|
|
40
|
+
eventRetentionDays: null, // null = never prune (web); mobile passes 180
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
if (result.status === "success") {
|
|
44
|
+
// Merged state is RETURNED, never written by core — persist it yourself.
|
|
45
|
+
// A failed sync therefore leaves local state completely untouched.
|
|
46
|
+
persist(result);
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## API surface
|
|
51
|
+
|
|
52
|
+
Everything exports from the barrel (`import { … } from "@patternbank/core"`).
|
|
53
|
+
|
|
54
|
+
| Area | Modules | Highlights |
|
|
55
|
+
|---|---|---|
|
|
56
|
+
| Spaced repetition | `spacedRepetition` | Confidence 1–5 → 1/2/5/10/30-day intervals; repeated 5★ reviews graduate 60→120→240→365d; daily-cap priority sort (lowest confidence → most overdue → stable daily hash) |
|
|
57
|
+
| Domain transforms | `problemTransforms`, `todayView`, `projectionEngine`, `progressUtils`, `progressVisuals`, `preferences` | Import merge with canonical-id remapping (no duplicates on cross-device restore), Today feed builders, 30-day projection engine |
|
|
58
|
+
| Sync | `sync/fullSync`, `sync/merge`, `sync/reviewEvents` | `performFullSync` (fail-closed orchestration), `mergeProblems` / `mergeReviewLog` / `mergeReviewEvents` / `mergeProblemTombstones` (LWW), reset-marker + tombstone filters |
|
|
59
|
+
| Supabase | `supabase/mapping`, `supabase/data` | camelCase ↔ snake_case mapping, `createCloudData` (21 CRUD functions, injected client, per-op timeouts), `createLeetCodeActivityData` (11 functions) |
|
|
60
|
+
| LeetCode | `leetcode/*` | 3,846-problem static database with search, curated list definitions, import transforms, Today resolver, review actions |
|
|
61
|
+
| Storage | `storage/adapter`, `storage/logic` | `StorageAdapter` interface, `calculateStreak`, event-prune policy |
|
|
62
|
+
|
|
63
|
+
### Review-event identity (worth knowing)
|
|
64
|
+
|
|
65
|
+
Two review events are considered the same review only when they share the exact `problemId|timestamp` key, **or** they hit the same problem on the same calendar date with the **same confidence** within 5 seconds (legacy cross-platform timestamp drift). Near-midnight events on different dates are distinct streak days; a same-day re-rate with a different star count is a distinct review — merges keep the newest rating instead of silently dropping it.
|
|
66
|
+
|
|
67
|
+
## Adapter seams
|
|
68
|
+
|
|
69
|
+
Core is platform-free. Each app injects four things:
|
|
70
|
+
|
|
71
|
+
1. **`StorageAdapter`** — async `getItem`/`setItem`/`removeItem` (+ optional `multiRemove`). Web wraps `localStorage` in Promises; React Native passes `AsyncStorage` directly.
|
|
72
|
+
2. **Supabase client** — passed into `createCloudData`; nullable by design.
|
|
73
|
+
3. **Preferences extension** — `performFullSync<P extends CorePreferences>` is generic; platforms extend the base preferences (e.g. mobile's notification settings stay local-only) while the cloud layer serializes only the shared subset.
|
|
74
|
+
4. **Hooks** — optional `warn`/analytics callbacks; core never logs or throws to the UI on cloud failures.
|
|
75
|
+
|
|
76
|
+
## Design guarantees
|
|
77
|
+
|
|
78
|
+
- **Fail-closed sync** — any cloud error returns `{ status: "error" }` with the local snapshot untouched; nothing partial persists.
|
|
79
|
+
- **Newest-`updatedAt` wins** for problems, tombstones (LWW — a tombstone only deletes rows not updated after it), and preferences.
|
|
80
|
+
- **Prune watermark** — events pruned locally on purpose are never resurrected from the cloud.
|
|
81
|
+
- **Date-aware, confidence-aware event matching** — see above.
|
|
82
|
+
- Dual **ESM + CJS** builds with split type entries; `sideEffects: false`; Node ≥ 18.
|
|
83
|
+
|
|
84
|
+
## Versioning
|
|
85
|
+
|
|
86
|
+
Semver from `0.1.0`. Releases are tagged `core-v*` in the [Pattern-Bank repo](https://github.com/DerekZ-113/Pattern-Bank); the package directory is `packages/core`. Notable: `0.1.1` fixed same-day re-rates being collapsed by the 5-second drift window.
|
|
87
|
+
|
|
88
|
+
## License
|
|
89
|
+
|
|
90
|
+
[GPL-3.0](https://github.com/DerekZ-113/Pattern-Bank/blob/main/LICENSE), same as the PatternBank repo.
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@patternbank/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Shared domain logic for PatternBank (web + mobile): spaced repetition, sync merge, LeetCode import",
|
|
5
|
-
"license": "
|
|
5
|
+
"license": "GPL-3.0",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "git+https://github.com/DerekZ-113/Pattern-Bank.git",
|