Stage 0: governance scaffolding + monorepo bootstrap
Phase 1 foundation for the Stargue Publishing Engine (plan v2, BMAD
panel-reviewed 2026-04-19 — 1 APPROVE, 6 REVISE, 0 REJECT; all principles >=3).
- Governance doctrine adopted from DQMS
(.clinerules/12-foundational-principles.md,
.claude/hooks/gate-plan-exit.sh, .claude/skills/bmad-plan/SKILL.md)
- Bun workspaces + Turbo; apps/{mcp-linkedin,scheduler,admin};
packages/{schema,sanitize,linkedin-client,observability}
- Drizzle schema (content, publications, approvals, metrics,
linkedin_tokens, audit, outlet_feature_flags) with idempotency_key
UNIQUE and kill-switch table per TEA/dev panel revisions
- LinkedIn API canon: Posts API /rest/posts (not legacy UGC); OAuth
auth-code without PKCE; secretbox (not sealed-box); Community
Management API as separate approval gate from MDP
- Frontmatter Zod schema (status, language, outlets[], sanitize,
scheduled, version)
- Pino observability with PII redaction
- Expand-then-contract migration runbook
- Plan + panel verdicts mirrored to docs/plans/
- Deferred gates logged (Dokploy PaaS verification, LinkedIn Dev
Portal app registration)
bun install + bun run typecheck both exit 0 across 11 workspaces.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
122
.claude/hooks/gate-plan-exit.sh
Executable file
122
.claude/hooks/gate-plan-exit.sh
Executable file
@@ -0,0 +1,122 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# BMAD Plan Review Gate — PreToolUse hook for ExitPlanMode.
|
||||||
|
#
|
||||||
|
# Enforces CLAUDE.md "Multi-Agent Plan Review Gate": blocks ExitPlanMode
|
||||||
|
# until /tmp/bmad-panel-verdicts-<session_id>.json contains verdicts from
|
||||||
|
# all 7 BMAD panel agents (analyst, architect, pm, sm, ux-designer, dev, tea).
|
||||||
|
#
|
||||||
|
# Uses /usr/bin/node (always present; no jq dependency which is missing in
|
||||||
|
# this box's non-interactive PATH). Fail-closed via permissionDecision=deny
|
||||||
|
# in hookSpecificOutput JSON on stdout.
|
||||||
|
#
|
||||||
|
# The Claude session is responsible for writing verdicts via Agent calls —
|
||||||
|
# this hook only validates the file exists and is well-formed.
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
export HOOK_PAYLOAD
|
||||||
|
HOOK_PAYLOAD="$(cat)"
|
||||||
|
|
||||||
|
/usr/bin/node -e '
|
||||||
|
const raw = process.env.HOOK_PAYLOAD || "";
|
||||||
|
let payload;
|
||||||
|
try { payload = JSON.parse(raw); } catch (e) {
|
||||||
|
// Malformed payload — allow through; not our job to second-guess.
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
if ((payload.tool_name || "") !== "ExitPlanMode") process.exit(0);
|
||||||
|
|
||||||
|
const fs = require("fs");
|
||||||
|
const sessionId = payload.session_id || "unknown";
|
||||||
|
const verdictFile = "/tmp/bmad-panel-verdicts-" + sessionId + ".json";
|
||||||
|
|
||||||
|
function deny(reason) {
|
||||||
|
process.stdout.write(JSON.stringify({
|
||||||
|
hookSpecificOutput: {
|
||||||
|
hookEventName: "PreToolUse",
|
||||||
|
permissionDecision: "deny",
|
||||||
|
permissionDecisionReason: reason
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fs.existsSync(verdictFile)) {
|
||||||
|
deny("BMAD plan review gate: verdicts file missing at " + verdictFile +
|
||||||
|
". Invoke the /bmad-plan skill (or launch the 7 panel agents manually) " +
|
||||||
|
"and write verdicts to this path before ExitPlanMode. See CLAUDE.md > Plan Review Gate.");
|
||||||
|
}
|
||||||
|
|
||||||
|
let doc;
|
||||||
|
try { doc = JSON.parse(fs.readFileSync(verdictFile, "utf8")); } catch (e) {
|
||||||
|
deny("BMAD plan review gate: " + verdictFile + " is not valid JSON (" + e.message + ").");
|
||||||
|
}
|
||||||
|
|
||||||
|
const verdicts = Array.isArray(doc && doc.verdicts) ? doc.verdicts : null;
|
||||||
|
if (!verdicts || verdicts.length < 7) {
|
||||||
|
const n = verdicts ? verdicts.length : 0;
|
||||||
|
deny("BMAD plan review gate: " + verdictFile + " has only " + n +
|
||||||
|
"/7 panel verdicts. All 7 (analyst, architect, pm, sm, ux-designer, dev, tea) are required.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const missing = verdicts.filter(function (v) { return !v || !v.agent || !v.verdict; }).length;
|
||||||
|
if (missing > 0) {
|
||||||
|
deny("BMAD plan review gate: " + missing + " verdict entries missing .agent or .verdict fields.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const rejects = verdicts.filter(function (v) {
|
||||||
|
return String(v.verdict).toUpperCase() === "REJECT";
|
||||||
|
}).length;
|
||||||
|
if (rejects > 0) {
|
||||||
|
deny("BMAD plan review gate: " + rejects +
|
||||||
|
" panel agent(s) returned REJECT. Revise the plan or escalate before ExitPlanMode.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Foundational Principles gate (two-tier).
|
||||||
|
// Tier-1 principles MUST be scored on every verdict.
|
||||||
|
// Tier-2 principles are scored only when declared in doc.applicable_tier2.
|
||||||
|
// See .clinerules/12-foundational-principles.md for doctrine and rubric.
|
||||||
|
var TIER1 = ["SEBP", "SSoT", "FPT", "DiDSP", "PbD", "OF"];
|
||||||
|
var TIER2_ALLOWED = ["RBR", "A11y", "Testability"];
|
||||||
|
var MIN_SCORE = 3;
|
||||||
|
|
||||||
|
var declaredTier2 = Array.isArray(doc.applicable_tier2) ? doc.applicable_tier2.slice() : [];
|
||||||
|
var invalidTier2 = declaredTier2.filter(function (k) { return TIER2_ALLOWED.indexOf(k) === -1; });
|
||||||
|
if (invalidTier2.length > 0) {
|
||||||
|
deny("BMAD plan review gate: applicable_tier2 contains unknown principle(s): " +
|
||||||
|
invalidTier2.join(", ") + ". Allowed values: " + TIER2_ALLOWED.join(", ") +
|
||||||
|
". See .clinerules/12-foundational-principles.md > Tier-2 Principles.");
|
||||||
|
}
|
||||||
|
var requiredKeys = TIER1.concat(declaredTier2);
|
||||||
|
|
||||||
|
var principleViolations = [];
|
||||||
|
verdicts.forEach(function (v) {
|
||||||
|
var p = v && v.principles;
|
||||||
|
if (!p || typeof p !== "object") {
|
||||||
|
principleViolations.push((v && v.agent ? v.agent : "?") + ": missing principles object");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
requiredKeys.forEach(function (key) {
|
||||||
|
var entry = p[key];
|
||||||
|
if (!entry || typeof entry.score !== "number") {
|
||||||
|
principleViolations.push(v.agent + "." + key + ": missing or non-numeric score");
|
||||||
|
} else if (entry.score < MIN_SCORE) {
|
||||||
|
principleViolations.push(v.agent + "." + key + ": score " + entry.score + " < " + MIN_SCORE);
|
||||||
|
} else if (typeof entry.rationale !== "string" || !entry.rationale.trim()) {
|
||||||
|
principleViolations.push(v.agent + "." + key + ": missing rationale");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if (principleViolations.length > 0) {
|
||||||
|
var tier2Note = declaredTier2.length > 0
|
||||||
|
? " plus declared tier-2 {" + declaredTier2.join(",") + "}"
|
||||||
|
: " (no tier-2 declared)";
|
||||||
|
deny("BMAD plan review gate: foundational principle scoring failed:\n - " +
|
||||||
|
principleViolations.join("\n - ") +
|
||||||
|
"\n\nEvery verdict must include principles for tier-1 {" + TIER1.join(",") + "}" + tier2Note +
|
||||||
|
" with .score >= " + MIN_SCORE + " and a non-empty .rationale. " +
|
||||||
|
"See .clinerules/12-foundational-principles.md.");
|
||||||
|
}
|
||||||
|
|
||||||
|
process.exit(0);
|
||||||
|
'
|
||||||
192
.claude/skills/bmad-plan/SKILL.md
Normal file
192
.claude/skills/bmad-plan/SKILL.md
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
---
|
||||||
|
name: bmad-plan
|
||||||
|
description: |
|
||||||
|
BMAD 7-agent plan review workflow. Use when drafting any implementation
|
||||||
|
plan that the CLAUDE.md "Multi-Agent Plan Review Gate" directive applies
|
||||||
|
to. Ensures the gate is satisfied before ExitPlanMode is attempted —
|
||||||
|
the PreToolUse hook at .claude/hooks/gate-plan-exit.sh will deny
|
||||||
|
ExitPlanMode otherwise.
|
||||||
|
---
|
||||||
|
|
||||||
|
# BMAD Plan + 7-Agent Panel Review
|
||||||
|
|
||||||
|
## When to invoke
|
||||||
|
|
||||||
|
Every plan-mode session for a non-trivial implementation. CLAUDE.md mandates this for anything beyond the smallest of fixes. Skip only for genuine one-liners (typo, comment tweak) — and even then, think twice.
|
||||||
|
|
||||||
|
## Workflow (non-negotiable)
|
||||||
|
|
||||||
|
### Phase 1 — Draft
|
||||||
|
|
||||||
|
1. Write the plan to the plan file supplied by plan mode (e.g. `/home/devuser/.claude/plans/<slug>.md`).
|
||||||
|
2. The plan must follow CLAUDE.md format: `Context`, stages with DoD, critical files, verification, authorization gates, risks.
|
||||||
|
3. **Declare tier-2 applicability** at the top of the plan as an `Applicability:` line, e.g.:
|
||||||
|
|
||||||
|
```
|
||||||
|
Applicability: tier-2 = [RBR, A11y] # plan touches a migration and adds new UI
|
||||||
|
```
|
||||||
|
|
||||||
|
Allowed values: `RBR` (Reversibility / Blast Radius), `A11y` (Accessibility), `Testability`. Empty list (`tier-2 = []`) is valid when none apply. The Analyst (Mary) is responsible for the call — see `.clinerules/12-foundational-principles.md > Tier-2 Principles` for when to declare each.
|
||||||
|
|
||||||
|
### Phase 1.5 — Harden the per-agent dispatch prompt via prompt-master
|
||||||
|
|
||||||
|
The 7 BMAD panel charters (`_bmad/bmm/agents/{analyst,architect,pm,sm,ux-designer,dev,tea}.md`) are the SSoT for each panelist's role and stay untouched. What this skill DOES build per-plan is the **dispatch prompt** — the message the session sends to each panelist along with the plan to review, the cross-cutting principle rubric (tier-1 + declared tier-2), and the verdict shape.
|
||||||
|
|
||||||
|
Per [`.clinerules/13-agent-spawning-protocol.md`](../../../.clinerules/13-agent-spawning-protocol.md), this dispatch prompt MUST be hardened before Phase 2:
|
||||||
|
|
||||||
|
1. Draft the dispatch template (one shape, parameterized per panelist).
|
||||||
|
2. Invoke the `prompt-master` skill with the draft + plan summary + principle rubric. One invocation covers all 7 panelists since they share dispatch structure.
|
||||||
|
3. Apply prompt-master's hardening to the dispatch (output-format lock, severity anchors, structural caps on verdict length, link-format lock, verification discipline).
|
||||||
|
4. **Add the standard graph-tool leverage block** (per [`memory-bank/agent-prompts/README.md > Graph-tool leverage`](../../../memory-bank/agent-prompts/README.md)) to the dispatch — instruct each panelist to consult CRG (`crg_*` MCP tools) and Graphify (`docs/architecture/knowledge-graph/GRAPH_REPORT.md`) before speculative file reads. CRG benchmarks at 8.2× token reduction; Dev (Amelia) and TEA (Murat) gain the most leverage from this.
|
||||||
|
5. Use the hardened dispatch in Phase 2 — substituting only the panelist-specific focus from the Phase 2 table.
|
||||||
|
|
||||||
|
This is the pattern that catches: link-format drift, unanchored severity calibration, soft length caps ignored, asymmetric specificity across panelists, and unverifiable aggregate claims. Skipping this step is the documented failure mode of the 2026-04-16 9-agent baseline audit.
|
||||||
|
|
||||||
|
### Phase 2 — Launch the 7 panel agents in parallel
|
||||||
|
|
||||||
|
**MUST launch each agent separately in parallel** per memory `feedback_separate_agents` — never consolidate them into one prompt.
|
||||||
|
|
||||||
|
Panel members (charters at `_bmad/bmm/agents/<name>.md`):
|
||||||
|
|
||||||
|
| Agent | Focus |
|
||||||
|
| --- | --- |
|
||||||
|
| Mary (analyst) | Requirements completeness, stakeholder alignment |
|
||||||
|
| Winston (architect) | Architectural soundness, scalability, technology selection |
|
||||||
|
| John (pm) | Business value, prioritization, risk identification |
|
||||||
|
| Bob (sm) | Story clarity, actionability, sprint-readiness |
|
||||||
|
| Sally (ux-designer) | User impact, UX considerations, accessibility |
|
||||||
|
| Amelia (dev) | Implementation feasibility, code patterns, existing code reuse |
|
||||||
|
| Murat (tea) | Test strategy, quality gates, CI/CD impact |
|
||||||
|
|
||||||
|
Each agent MUST return a structured verdict. Tier-1 principles are scored on every plan; tier-2 principles are scored only when declared in the plan's `Applicability` line (see Phase 1 step 3).
|
||||||
|
|
||||||
|
```
|
||||||
|
VERDICT: APPROVE | REVISE | REJECT
|
||||||
|
CONFIDENCE: low | medium | high
|
||||||
|
STRENGTHS: - …
|
||||||
|
GAPS: - …
|
||||||
|
PRINCIPLES (tier-1, always):
|
||||||
|
SEBP: { score: 1-5, rationale: "…" }
|
||||||
|
SSoT: { score: 1-5, rationale: "…" }
|
||||||
|
FPT: { score: 1-5, rationale: "…" }
|
||||||
|
DiDSP: { score: 1-5, rationale: "…" }
|
||||||
|
PbD: { score: 1-5, rationale: "…" }
|
||||||
|
OF: { score: 1-5, rationale: "…" }
|
||||||
|
PRINCIPLES (tier-2, only if declared):
|
||||||
|
RBR: { score: 1-5, rationale: "…" } # if applicable
|
||||||
|
A11y: { score: 1-5, rationale: "…" } # if applicable
|
||||||
|
Testability: { score: 1-5, rationale: "…" } # if applicable
|
||||||
|
ONE-LINE SUMMARY: …
|
||||||
|
```
|
||||||
|
|
||||||
|
### Foundational Principle Scoring (gate-enforced)
|
||||||
|
|
||||||
|
All 7 agents MUST score the plan against the **six tier-1 principles**. They MUST also score every **tier-2 principle declared in `applicable_tier2`** for this plan. The hook denies `ExitPlanMode` if any required score is below 3, missing, or has no rationale.
|
||||||
|
|
||||||
|
**Tier-1 (always scored):**
|
||||||
|
|
||||||
|
| Principle | Definition | Score 1 (violation) → 5 (exemplary) |
|
||||||
|
|-----------|------------|-------------------------------------|
|
||||||
|
| **SEBP** — Software Engineering Best Practices | SOLID, DRY, separation of concerns, clear boundaries | 1 = duplicated logic / leaky abstractions; 5 = clean composition, no smell |
|
||||||
|
| **SSoT** — Single Source of Truth | One authoritative source per concept (constants, types, schemas, config) | 1 = duplicated constants/types/config; 5 = every fact has exactly one home |
|
||||||
|
| **FPT** — First Principle Thinking | Reasoning grounded in this codebase + verified docs, not training-data assumptions | 1 = "Next.js probably does X" without checking; 5 = cites file paths, runs probes |
|
||||||
|
| **DiDSP** — Defense in Depth Security | Layered security: input validation, authz, rate limits, audit, least privilege | 1 = unvalidated input or missing authz on sensitive op; 5 = multiple independent layers + audit |
|
||||||
|
| **PbD** — Privacy by Design | GDPR Art. 25: minimisation, purpose limitation, retention, lawful basis, user rights | 1 = collects PII without purpose / no erasure path; 5 = active minimisation + lifecycle wired |
|
||||||
|
| **OF** — Observability First | Structured logs, metrics, audit on every meaningful boundary | 1 = silent failure paths; 5 = correlation IDs, audit on regulated ops, dashboards referenced |
|
||||||
|
|
||||||
|
**Tier-2 (scored only when declared):**
|
||||||
|
|
||||||
|
| Principle | Declare when… | Score 1 → 5 |
|
||||||
|
|-----------|---------------|-------------|
|
||||||
|
| **RBR** — Reversibility / Blast Radius | Plan touches migrations, deletes, third-party calls, shared infra, CI/CD, prod data | 1 = destructive, no rollback; 5 = expand-then-contract + flags + recovery procedure |
|
||||||
|
| **A11y** — Accessibility (WCAG 2.2 AA) | Plan introduces or significantly changes user-facing UI | 1 = keyboard-trap / colour-only state; 5 = keyboard parity, contrast, screen-reader tested |
|
||||||
|
| **Testability** | Plan introduces or significantly changes business logic / domain rules / boundaries | 1 = logic embedded in framework callbacks, hidden globals; 5 = pure cores, injectable seams |
|
||||||
|
|
||||||
|
A score of 3 means "adequate, no blocking concerns." Below 3 blocks the plan. Score 1–2 must be paired with a `REVISE` or `REJECT` verdict and a concrete gap entry.
|
||||||
|
|
||||||
|
Full doctrine, definitions, and acceptance signals: [`.clinerules/12-foundational-principles.md`](../../../.clinerules/12-foundational-principles.md).
|
||||||
|
|
||||||
|
### Phase 3 — Write verdicts file
|
||||||
|
|
||||||
|
After all 7 agents return, write:
|
||||||
|
|
||||||
|
```
|
||||||
|
/tmp/bmad-panel-verdicts-${CLAUDE_SESSION_ID}.json
|
||||||
|
```
|
||||||
|
|
||||||
|
Shape (enforced by the hook):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"plan_file": "<path to plan file>",
|
||||||
|
"created_at": "<ISO 8601>",
|
||||||
|
"applicable_tier2": ["RBR", "A11y"],
|
||||||
|
"verdicts": [
|
||||||
|
{
|
||||||
|
"agent": "analyst",
|
||||||
|
"verdict": "APPROVE | REVISE | REJECT",
|
||||||
|
"confidence": "low | medium | high",
|
||||||
|
"summary": "one-line summary from the agent",
|
||||||
|
"gaps": ["…", "…"],
|
||||||
|
"principles": {
|
||||||
|
"SEBP": { "score": 4, "rationale": "…" },
|
||||||
|
"SSoT": { "score": 5, "rationale": "…" },
|
||||||
|
"FPT": { "score": 4, "rationale": "…" },
|
||||||
|
"DiDSP": { "score": 3, "rationale": "…" },
|
||||||
|
"PbD": { "score": 4, "rationale": "…" },
|
||||||
|
"OF": { "score": 4, "rationale": "…" },
|
||||||
|
"RBR": { "score": 3, "rationale": "…" },
|
||||||
|
"A11y": { "score": 4, "rationale": "…" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ "agent": "architect", ... },
|
||||||
|
{ "agent": "pm", ... },
|
||||||
|
{ "agent": "sm", ... },
|
||||||
|
{ "agent": "ux-designer", ... },
|
||||||
|
{ "agent": "dev", ... },
|
||||||
|
{ "agent": "tea", ... }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Notes on the shape:
|
||||||
|
|
||||||
|
- `applicable_tier2` mirrors the `Applicability:` line from the plan (Phase 1 step 3). Allowed values: `RBR`, `A11y`, `Testability`. An empty array `[]` is valid and means no tier-2 principles apply.
|
||||||
|
- Each verdict's `principles` object MUST cover all six tier-1 keys plus every key in `applicable_tier2`. Tier-2 keys not declared SHOULD be omitted (the hook ignores them).
|
||||||
|
|
||||||
|
Requirements enforced by `gate-plan-exit.sh`:
|
||||||
|
|
||||||
|
- `verdicts` is an array of ≥7 entries.
|
||||||
|
- Every entry has non-empty `agent` and `verdict`.
|
||||||
|
- Zero entries with `verdict: "REJECT"` (any REJECT blocks exit).
|
||||||
|
- `applicable_tier2` (when present) contains only `RBR`, `A11y`, or `Testability`.
|
||||||
|
- Every verdict has `principles.{SEBP,SSoT,FPT,DiDSP,PbD,OF}` with numeric `.score >= 3` and non-empty `.rationale`.
|
||||||
|
- Every verdict has the same fields for every key in `applicable_tier2`.
|
||||||
|
|
||||||
|
### Phase 4 — Consolidate
|
||||||
|
|
||||||
|
1. Read all 7 verdicts. Merge gap/concern items into an action list.
|
||||||
|
2. Revise the plan file to address every REVISE item. Cite which agent raised each concern (traceability).
|
||||||
|
3. If unanimous APPROVE: proceed to Phase 5.
|
||||||
|
4. If any REVISE remains after plan revision: re-run the panel or escalate to the user.
|
||||||
|
5. If any REJECT: revise or escalate; do not bypass.
|
||||||
|
|
||||||
|
### Phase 5 — Exit
|
||||||
|
|
||||||
|
1. Call `ExitPlanMode`. The hook reads the verdicts file, confirms ≥7 entries and no REJECTs, and allows the call.
|
||||||
|
2. After user approves, copy the plan to `docs/plans/{YYYY-MM-DD}-{topic}.md` per CLAUDE.md filing rule (plans in `.claude/plans/` are ephemeral).
|
||||||
|
|
||||||
|
## Failure modes
|
||||||
|
|
||||||
|
- **Hook denies with "verdicts file missing"**: you haven't written `/tmp/bmad-panel-verdicts-${CLAUDE_SESSION_ID}.json` yet. Run the panel (Phase 2) and write the file (Phase 3).
|
||||||
|
- **Hook denies with "only N/7 verdicts"**: an agent result was dropped. Re-launch only the missing agents and update the file.
|
||||||
|
- **Hook denies with "N panel agent(s) returned REJECT"**: revise the plan or escalate — do not remove REJECTs from the file without addressing their substance.
|
||||||
|
- **Hook denies with "foundational principle scoring failed"**: one or more verdicts is missing the `principles` object, has a required `.score < 3`, or has an empty `.rationale` for a tier-1 key or a declared tier-2 key. Re-engage the agent(s) whose verdicts triggered the failure with the rubric in *Foundational Principle Scoring* — never raise a score without substantive justification.
|
||||||
|
- **Hook denies with "applicable_tier2 contains unknown principle(s)"**: the `applicable_tier2` array contains a value other than `RBR`, `A11y`, or `Testability`. Fix the typo or remove the entry — do not extend the allowed set without updating both this skill and `.clinerules/12-foundational-principles.md`.
|
||||||
|
|
||||||
|
## Anti-patterns (do not do)
|
||||||
|
|
||||||
|
- Do not skip the panel and call ExitPlanMode directly. The hook will deny.
|
||||||
|
- Do not batch agents into a single prompt. Per memory `feedback_separate_agents`, always separate + parallel.
|
||||||
|
- Do not write fabricated verdicts to the file to bypass the gate. The gate exists to protect plan quality; bypassing it defeats the purpose and will be caught by the next review.
|
||||||
|
- Do not use this skill for trivial one-line fixes — see CLAUDE.md scope rules.
|
||||||
155
.clinerules/12-foundational-principles.md
Normal file
155
.clinerules/12-foundational-principles.md
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
# 12 — Foundational Principles (Non-Negotiable)
|
||||||
|
|
||||||
|
**Status:** Active doctrine. Enforced by [`.claude/hooks/gate-plan-exit.sh`](../.claude/hooks/gate-plan-exit.sh) and the [`/bmad-plan` skill](../.claude/skills/bmad-plan/SKILL.md).
|
||||||
|
|
||||||
|
Every implementation plan and the execution of that plan MUST satisfy these principles. They apply to BMAD workflows, ad-hoc fixes, refactors, and tooling work alike.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Two-Tier Model
|
||||||
|
|
||||||
|
Principles are split into two tiers to keep the rubric discriminating instead of ritualistic.
|
||||||
|
|
||||||
|
| Tier | When scored | Hook behaviour |
|
||||||
|
| ---- | ----------- | -------------- |
|
||||||
|
| **Tier-1 (Always)** | Every verdict, every plan | Hook **denies** `ExitPlanMode` if any verdict scores any tier-1 principle <3 or omits its rationale. |
|
||||||
|
| **Tier-2 (Conditional)** | Only when declared `applicable_tier2` for the plan | Hook **denies** `ExitPlanMode` if a declared tier-2 principle is unscored, scored <3, or has no rationale. |
|
||||||
|
|
||||||
|
The Analyst (Mary) declares `applicable_tier2` at the end of Phase 1 (plan draft). The list lives at the top of the verdicts file:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"plan_file": "<path>",
|
||||||
|
"applicable_tier2": ["RBR", "A11y"],
|
||||||
|
"verdicts": [ … ]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Allowed tier-2 values: `RBR`, `A11y`, `Testability`. Any other value causes the hook to deny.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tier-1 Principles (always scored)
|
||||||
|
|
||||||
|
### SEBP — Software Engineering Best Practices
|
||||||
|
|
||||||
|
**Definition:** SOLID (single-responsibility, open-closed, Liskov, interface-segregation, dependency-inversion), DRY, separation of concerns, clear module boundaries.
|
||||||
|
|
||||||
|
**Score 1 (violation):** duplicated logic across files; god-objects; leaky abstractions; mixed concerns (e.g., a route handler that also formats PDFs and writes audit logs inline).
|
||||||
|
**Score 3 (adequate):** clean enough; no immediate smell; reviewer would not block.
|
||||||
|
**Score 5 (exemplary):** composition over inheritance; each unit has one reason to change; boundaries documented in code shape, not just comments.
|
||||||
|
|
||||||
|
**Acceptance signals:** named exports with single responsibility; no copy-pasted blocks ≥6 lines; route handlers delegate to `src/lib/*` modules.
|
||||||
|
|
||||||
|
### SSoT — Single Source of Truth
|
||||||
|
|
||||||
|
**Definition:** every concept (constant, type, schema, config, secret, user-facing string, status enum) has exactly one authoritative location.
|
||||||
|
|
||||||
|
**Score 1:** the same constant or enum is duplicated in two or more files; type drift between API and DB; magic numbers in components.
|
||||||
|
**Score 3:** core domain types/constants centralised; minor leakage acceptable when behind a TODO or feature-flag.
|
||||||
|
**Score 5:** generated artifacts (Prisma client, OpenAPI types) flow from a single schema; constants live in `src/lib/constants/*`; i18n strings in one bundle.
|
||||||
|
|
||||||
|
**Acceptance signals:** no `const RETRY_LIMIT = 3` defined in two files; Zod schemas re-used (not re-declared) on both client and server; status enums imported from one module.
|
||||||
|
|
||||||
|
### FPT — First Principle Thinking
|
||||||
|
|
||||||
|
**Definition:** reasoning grounded in this codebase, this stack version, and verified docs — not training-data assumptions or generic patterns.
|
||||||
|
|
||||||
|
**Score 1:** "Next.js v15 probably does X" without checking; copy-pastes a pattern that doesn't fit the project's existing shape; ignores Memory-Bank lessons.
|
||||||
|
**Score 3:** cites at least one file or doc; checks a non-obvious assumption.
|
||||||
|
**Score 5:** identifies the actual constraint, not the symptom; runs probes (small scripts, type-checks) to verify before committing to a design; references prior incidents in `memory-bank/systemPatterns.md`.
|
||||||
|
|
||||||
|
**Acceptance signals:** plan references concrete file paths; cites Tailwind v4 / Prisma 6 / NextAuth v5 docs (not v3 patterns); names the prior pattern being followed or deliberately diverged from.
|
||||||
|
|
||||||
|
### DiDSP — Defense in Depth Security Practices
|
||||||
|
|
||||||
|
**Definition:** multiple independent layers of defence: input validation, authentication, authorization, rate limiting, audit logging, least privilege, output encoding.
|
||||||
|
|
||||||
|
**Score 1:** unvalidated user input reaches the database; missing authz on a sensitive operation; secrets in `public/`; tokens logged; SQL/HTML built by string concatenation.
|
||||||
|
**Score 3:** Zod validation + authn + authz on all touched API routes; secrets via env; no obvious OWASP top-10 hole.
|
||||||
|
**Score 5:** layered controls (Cerbos policy + route guard + DB constraint); audit log on sensitive ops; rate limit on abuse-prone endpoints; structured error responses that don't leak internals.
|
||||||
|
|
||||||
|
**Acceptance signals:** every new `src/app/api/**/route.ts` calls a Zod parse; every `auth()` call is paired with a Cerbos check for authz; no `console.log` of tokens, PII, or secrets; sensitive assets out of `public/`.
|
||||||
|
|
||||||
|
### PbD — Privacy by Design
|
||||||
|
|
||||||
|
**Definition:** GDPR Article 25 — data minimisation, purpose limitation, retention limits, lawful basis, transparency, user rights (access, portability, erasure, rectification).
|
||||||
|
|
||||||
|
**Score 1:** collects PII without a documented purpose; retains indefinitely; no consent record for a new processing activity; identity documents accessible without authn; cross-tenant data leakage possible.
|
||||||
|
**Score 3:** new fields have a documented purpose; consent is recorded if required; deletion path exists; no obvious GDPR regression.
|
||||||
|
**Score 5:** data minimisation actively reduces surface area; retention timer wired to lifecycle; consent versioning supports SAR/portability/erasure flows; brand-agnostic (no tenant cross-leakage); DPIA reasoning in the plan if processing is high-risk.
|
||||||
|
|
||||||
|
**Acceptance signals:** new PII columns have a `retention` policy reference; consent records updated when processing scope changes; SAR/erasure paths tested; identity-document endpoints require authn + authz + audit; no PII in URLs, logs, or error messages.
|
||||||
|
|
||||||
|
### OF — Observability First
|
||||||
|
|
||||||
|
**Definition:** every meaningful boundary emits structured signals (logs, metrics, traces, audit) sufficient to diagnose failure without re-running the operation.
|
||||||
|
|
||||||
|
**Score 1:** silent failure paths; bare `try/catch` that swallows errors; no log on auth/OCR/PDF/contract events; debugging requires re-creating user state.
|
||||||
|
**Score 3:** errors logged with context; key state transitions emit a record; oncall can locate a failure within minutes.
|
||||||
|
**Score 5:** structured logs (JSON, correlation IDs); audit trail for regulated ops (consent changes, document state, contract status); user-facing errors carry a trace ID; dashboards/alerts referenced in the plan when introducing new failure modes.
|
||||||
|
|
||||||
|
**Acceptance signals:** new background jobs log start/finish/error with correlation ID; new state machines emit one event per transition; new third-party calls log latency and outcome; PII redacted from logs.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tier-2 Principles (conditional)
|
||||||
|
|
||||||
|
Declare in `applicable_tier2` when relevant. Score on the same 1–5 scale; ≥3 required when declared.
|
||||||
|
|
||||||
|
### RBR — Reversibility / Blast Radius
|
||||||
|
|
||||||
|
**When to declare:** the plan touches migrations, deletes, third-party calls (email, SMS, payment, signing), shared infrastructure, CI/CD, production data, or any irreversible state change.
|
||||||
|
|
||||||
|
**Score 1:** destructive operation with no rollback path; one-way migration with no shadow column; bulk delete without dry-run.
|
||||||
|
**Score 3:** rollback strategy named; non-prod rehearsal planned.
|
||||||
|
**Score 5:** expand-then-contract migration; feature flags gate rollout; bulk operations are batched and resumable; every irreversible step has a guard, an audit record, and a documented recovery procedure.
|
||||||
|
|
||||||
|
### A11y — Accessibility (WCAG 2.2 AA)
|
||||||
|
|
||||||
|
**When to declare:** the plan introduces or significantly changes user-facing UI, forms, navigation, or content presentation.
|
||||||
|
|
||||||
|
**Score 1:** keyboard-trap; missing labels; colour-only state; non-semantic markup; forms without error association.
|
||||||
|
**Score 3:** semantic HTML; visible focus; labels on inputs; passes axe-core baseline.
|
||||||
|
**Score 5:** keyboard parity with mouse; aria-live where dynamic; respects `prefers-reduced-motion`; meets contrast ratios on all themes; tested with a screen reader.
|
||||||
|
|
||||||
|
### Testability
|
||||||
|
|
||||||
|
**When to declare:** the plan introduces or significantly changes business logic, domain rules, or system boundaries that future tests must reach.
|
||||||
|
|
||||||
|
**Score 1:** logic embedded in framework callbacks (Server Components, route handlers) with no extraction; hidden globals; non-deterministic clocks/IDs.
|
||||||
|
**Score 3:** pure functions extracted into `src/lib/*`; clock/ID/randomness injectable; tests can call the unit without booting the framework.
|
||||||
|
**Score 5:** seams expose every branch; fakes/spies straightforward; integration boundaries (DB, external APIs) covered by either real-DB tests or contract tests; no mocks where the real thing fits.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Cross-cutting expectations (apply to all principles)
|
||||||
|
|
||||||
|
- **Plan rationale must cite evidence.** "Score 4" alone is not a verdict; the rationale must reference a file, a behaviour, or a decision in the plan.
|
||||||
|
- **Tie-break:** when a principle conflicts with another (e.g., DiDSP suggests a layer that hurts SSoT), the plan must name the trade-off explicitly. The reviewer should score *resolution quality*, not pretend there is no tension.
|
||||||
|
- **No retrofitting scores.** Do not raise a score after-the-fact to satisfy the gate; revise the plan instead.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Review-process integration
|
||||||
|
|
||||||
|
1. **Phase 1 (Draft)** — author writes the plan and selects `applicable_tier2`. The plan file should include a short *Applicability* line stating which tier-2 principles apply and why.
|
||||||
|
2. **Phase 2 (Panel)** — each of the 7 BMAD agents (analyst, architect, pm, sm, ux-designer, dev, tea) returns a verdict. Every verdict scores all tier-1 principles + every declared tier-2 principle.
|
||||||
|
3. **Phase 3 (Verdicts file)** — write `/tmp/bmad-panel-verdicts-<session_id>.json` with the schema in the skill.
|
||||||
|
4. **Phase 4 (Consolidate)** — address every score <3 in the plan; re-engage the dissenting agent if needed.
|
||||||
|
5. **Phase 5 (Exit)** — call `ExitPlanMode`; the hook validates and approves.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Anti-patterns
|
||||||
|
|
||||||
|
- **Score-inflation:** raising scores to clear the gate without revising the plan. The next reviewer will catch it; trust will erode.
|
||||||
|
- **Tier-2 over-declaration:** declaring every tier-2 principle on every plan dilutes the signal. Only declare what genuinely applies.
|
||||||
|
- **Tier-2 under-declaration to bypass review:** if the plan touches migrations, declare RBR. If it touches UI, declare A11y. If it changes domain logic, declare Testability. The Analyst (Mary) is responsible; gaming this defeats the doctrine.
|
||||||
|
- **Treating principles as a checklist:** the rubric exists to surface real tensions and trade-offs. A plan that scores 5 on all six tier-1 principles without acknowledging any trade-off is suspect.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Precedence
|
||||||
|
|
||||||
|
This file ranks below CLAUDE.md and the documentation-lifecycle / feature-tracking / agent-mode protocols, and above the language-and-framework standards. See `CLAUDE.md > Conflict Resolution Precedence` for the full ordering.
|
||||||
10
.gitignore
vendored
10
.gitignore
vendored
@@ -136,3 +136,13 @@ dist
|
|||||||
.yarn/install-state.gz
|
.yarn/install-state.gz
|
||||||
.pnp.*
|
.pnp.*
|
||||||
|
|
||||||
|
# Turbo
|
||||||
|
.turbo
|
||||||
|
|
||||||
|
# Bun
|
||||||
|
bun.lockb
|
||||||
|
|
||||||
|
# OS junk
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
|||||||
97
CLAUDE.md
Normal file
97
CLAUDE.md
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
# CLAUDE.md — Stargue Publishing Engine
|
||||||
|
|
||||||
|
Project-level context for Claude Code. Loaded every turn.
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Automated two-channel Publishing Engine: Obsidian vault → stargue.com/.net + LinkedIn (personal profile + Stargue Company Page) via a Stargue-owned LinkedIn MCP server. Phase 1 scope is tracked in `docs/plans/2026-04-19-phase1-plan.md` (mirrored from the vault SSoT at `Stargue/Projects/Publishing Engine/Plan - Phase 1 Automated Publishing Engine.md`).
|
||||||
|
|
||||||
|
## Source-of-Truth Precedence
|
||||||
|
|
||||||
|
1. **Vault** (`c:\Users\aluid\OneDrive\Documents\Obsidian\Angelo'sSBO\`) — content notes, design philosophy (Nine Laws of the Hearth), outlet profiles, PRDs, plans. SSoT for *intent*.
|
||||||
|
2. **This repo** (`/home/devuser/projects/stargue-publishing-engine/`) — code implementation. SSoT for *behavior*.
|
||||||
|
3. **stargue-com + stargue-net** (`/home/devuser/projects/stargue-{com,net}/`) — Next.js static sites that consume `packages/sanitize` + `packages/schema` as workspace dependencies for build-time content transformation.
|
||||||
|
4. **Production state** (Dokploy on sg-paas-s1.stargue.net, Postgres, Redis, LinkedIn) — SSoT for *runtime*. Never assume; always verify via Dokploy MCP, DB query, or LinkedIn `whoami`.
|
||||||
|
|
||||||
|
## Governance
|
||||||
|
|
||||||
|
- **Foundational principles** (Tier-1 always + declared Tier-2): `.clinerules/12-foundational-principles.md` — adopted 2026-04-19 from DQMS (ref: `/home/devuser/projects/dqms/.clinerules/12-foundational-principles.md`). DQMS is the evolving SSoT for Stargue's BMAD application; this file is a dated snapshot.
|
||||||
|
- **Plan-review gate** (7-agent BMAD panel): enforced by `.claude/hooks/gate-plan-exit.sh` on `ExitPlanMode`. Runs the verdicts validator against `/tmp/bmad-panel-verdicts-${CLAUDE_SESSION_ID}.json`.
|
||||||
|
- **Panel charters** (roles for analyst, architect, pm, sm, ux-designer, dev, tea): referenced by path from DQMS at `/home/devuser/projects/dqms/_bmad/bmm/agents/*.md`. No local install in Phase 1 (per user directive 2026-04-19).
|
||||||
|
- **Tier-2 applicability**: every plan declares `applicable_tier2 = [RBR | A11y | Testability]`. See `.clinerules/12-foundational-principles.md > Tier-2 Principles`.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
Monorepo (Bun workspaces + Turbo):
|
||||||
|
|
||||||
|
```
|
||||||
|
apps/mcp-linkedin/ MCP server (stdio transport) for Claude Code to drive LinkedIn operations
|
||||||
|
apps/scheduler/ BullMQ worker — imports packages/linkedin-client directly; no HTTP to MCP
|
||||||
|
apps/admin/ Next.js App Router — UI + /api/* route handlers
|
||||||
|
packages/sanitize/ remark plugin + blocklist + length validation + test corpus
|
||||||
|
packages/schema/ Zod schemas, Drizzle DB schema, frontmatter parser (SSoT for data shapes)
|
||||||
|
packages/linkedin-client/ LinkedIn Posts API client + encrypted token store
|
||||||
|
packages/observability/ Pino + OTel + correlation IDs
|
||||||
|
```
|
||||||
|
|
||||||
|
Scheduler imports `packages/linkedin-client` directly — MCP is **not** on the scheduler's hot path. MCP exists so Claude Code can invoke LinkedIn operations interactively (posting, metrics queries, auth status). HTTP MCP transport is deliberately deferred.
|
||||||
|
|
||||||
|
## LinkedIn — canonical facts (verified 2026-04-19)
|
||||||
|
|
||||||
|
- API: **Posts API** `https://api.linkedin.com/rest/posts` (the UGC Posts API is **legacy** — do not use). Required headers: `LinkedIn-Version: 202404` (YYYYMM, update on cadence), `X-Restli-Protocol-Version: 2.0.0`, `Authorization: Bearer <token>`.
|
||||||
|
- Auth: OAuth 2.0 Authorization Code flow. **PKCE is NOT supported** by LinkedIn 3LO per `https://learn.microsoft.com/en-us/linkedin/shared/authentication/authorization-code-flow`. Use confidential-client with `client_secret` + CSRF-protected `state`.
|
||||||
|
- Products in Dev Portal (each separately enabled/approved):
|
||||||
|
- `Sign In with LinkedIn using OpenID Connect` → scopes `openid profile email`
|
||||||
|
- `Share on LinkedIn` → `w_member_social` (personal-profile posts)
|
||||||
|
- `Community Management API` → `w_organization_social`, `r_organization_social` (organization posts — **requires LinkedIn partner approval**)
|
||||||
|
- `Marketing Developer Platform (MDP)` → programmatic refresh tokens (requires LinkedIn partner approval; without it, access tokens expire in 60 days with **no refresh** — re-auth required)
|
||||||
|
- Stargue Company Page URN: `urn:li:organization:2605890`
|
||||||
|
- Primary identity: Angelo personal profile (`angeloluidens`), resolved at OAuth time to `urn:li:person:<id>`
|
||||||
|
- Token encryption: `libsodium crypto_secretbox` (symmetric XSalsa20-Poly1305) — **not sealed-box**. Key stored in Dokploy secrets.
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun install # install workspace deps
|
||||||
|
bun run typecheck # workspace-wide tsc --noEmit
|
||||||
|
bun run lint # workspace-wide lint
|
||||||
|
bun run test # workspace-wide vitest
|
||||||
|
bun run build # turbo build
|
||||||
|
bun run dev # turbo dev (admin, scheduler, mcp-linkedin)
|
||||||
|
|
||||||
|
# Migration commands
|
||||||
|
bun run db:migrate # drizzle-kit migrate
|
||||||
|
bun run db:migrate:rehearse # expand-then-contract rehearsal on staging
|
||||||
|
|
||||||
|
# MCP invocation (stdio)
|
||||||
|
docker exec -i mcp-linkedin node apps/mcp-linkedin/dist/server.js
|
||||||
|
```
|
||||||
|
|
||||||
|
## Deployment
|
||||||
|
|
||||||
|
Dokploy stack `stargue-publishing-engine` on `sg-paas-s1.stargue.net`:
|
||||||
|
- `admin` — Next.js standalone, Traefik at `publishing.stargue.net`, Authentik SSO
|
||||||
|
- `scheduler` — BullMQ worker
|
||||||
|
- `mcp-linkedin` — stdio-only; started on demand by Claude Code via `docker exec`
|
||||||
|
- `postgres` + `redis` — shared PaaS cluster
|
||||||
|
|
||||||
|
DR: nightly `pg_dump` to Neon free tier. RPO ~24h.
|
||||||
|
|
||||||
|
## Rules
|
||||||
|
|
||||||
|
- **Additive by default** — never delete production data or published content without explicit user confirmation.
|
||||||
|
- **No PII in logs** — content body is hashed, not logged; tokens never printed.
|
||||||
|
- **Every publish is approval-gated** — no auto-publish without a row in `approvals` for the (content_id, outlet) pair.
|
||||||
|
- **Kill-switch respected** — every dispatch checks `outlet_feature_flags` before making LinkedIn calls.
|
||||||
|
- **Idempotency enforced** — every LinkedIn API call carries an idempotency key derived from (content_id, outlet, scheduled_at).
|
||||||
|
- **Expand-then-contract migrations** — every schema change has a rollback runbook in `db/migrations/README.md`.
|
||||||
|
- **All operations in WSL Debian** — per vault-level rule. `wsl -d Debian -- bash -c '…'` for any shell invocation from Windows.
|
||||||
|
|
||||||
|
## Related (vault paths)
|
||||||
|
|
||||||
|
- `Stargue/Projects/Publishing Engine/Plan - Phase 1 Automated Publishing Engine.md` — current plan (SSoT)
|
||||||
|
- `Stargue/Projects/Publishing Engine/Publishing Engine PRD.md` — PRD v2.2
|
||||||
|
- `Stargue/Projects/Publishing Engine/Design Philosophy - The Nine Laws of the Hearth.md` — visual + interaction SSoT
|
||||||
|
- `Stargue/Projects/Publishing Engine/Outlet Profiles/LinkedIn.md` — outlet specs
|
||||||
|
- `Stargue/Projects/Publishing Engine/7 Cs LinkedIn Series - Continuation Plan.md` — first content program
|
||||||
|
- `Stargue/Projects/Publishing Engine/Implementation Progress.md` — running state
|
||||||
32
apps/admin/package.json
Normal file
32
apps/admin/package.json
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"name": "@stargue/admin",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"build": "next build",
|
||||||
|
"dev": "next dev -p 3002",
|
||||||
|
"start": "next start -p 3002",
|
||||||
|
"typecheck": "tsc --noEmit",
|
||||||
|
"test": "vitest run",
|
||||||
|
"lint": "next lint"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@stargue/schema": "workspace:*",
|
||||||
|
"@stargue/sanitize": "workspace:*",
|
||||||
|
"@stargue/observability": "workspace:*",
|
||||||
|
"next": "^16.0.0",
|
||||||
|
"react": "^19.0.0",
|
||||||
|
"react-dom": "^19.0.0",
|
||||||
|
"zod": "^3.23.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@axe-core/playwright": "^4.10.0",
|
||||||
|
"@playwright/test": "^1.48.0",
|
||||||
|
"@types/react": "^19.0.0",
|
||||||
|
"@types/react-dom": "^19.0.0",
|
||||||
|
"axe-core": "^4.10.0",
|
||||||
|
"typescript": "^5.7.0",
|
||||||
|
"vitest": "^2.1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
2
apps/admin/src/index.ts
Normal file
2
apps/admin/src/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
// Admin dashboard (Next.js App Router) — full implementation in Stage 5.
|
||||||
|
export const ADMIN_APP_NAME = "@stargue/admin";
|
||||||
24
apps/mcp-linkedin/package.json
Normal file
24
apps/mcp-linkedin/package.json
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"name": "@stargue/mcp-linkedin",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc --noEmit",
|
||||||
|
"dev": "bun --watch src/server.ts",
|
||||||
|
"typecheck": "tsc --noEmit",
|
||||||
|
"test": "vitest run",
|
||||||
|
"lint": "echo 'lint pending'"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@modelcontextprotocol/sdk": "1.0.3",
|
||||||
|
"@stargue/linkedin-client": "workspace:*",
|
||||||
|
"@stargue/schema": "workspace:*",
|
||||||
|
"@stargue/observability": "workspace:*",
|
||||||
|
"zod": "^3.23.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"vitest": "^2.1.0",
|
||||||
|
"typescript": "^5.7.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
4
apps/mcp-linkedin/src/server.ts
Normal file
4
apps/mcp-linkedin/src/server.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
// MCP server entry point. Full implementation in Stage 3.
|
||||||
|
// MCP spec version pinned: 2024-11-05 (Streamable HTTP + stdio).
|
||||||
|
export const MCP_SERVER_NAME = "@stargue/mcp-linkedin";
|
||||||
|
export const MCP_SPEC_VERSION = "2024-11-05";
|
||||||
24
apps/scheduler/package.json
Normal file
24
apps/scheduler/package.json
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"name": "@stargue/scheduler",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc --noEmit",
|
||||||
|
"dev": "bun --watch src/worker.ts",
|
||||||
|
"typecheck": "tsc --noEmit",
|
||||||
|
"test": "vitest run",
|
||||||
|
"lint": "echo 'lint pending'"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@stargue/linkedin-client": "workspace:*",
|
||||||
|
"@stargue/schema": "workspace:*",
|
||||||
|
"@stargue/observability": "workspace:*",
|
||||||
|
"bullmq": "^5.30.0",
|
||||||
|
"ioredis": "^5.4.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"vitest": "^2.1.0",
|
||||||
|
"typescript": "^5.7.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
2
apps/scheduler/src/worker.ts
Normal file
2
apps/scheduler/src/worker.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
// BullMQ worker entry point. Full implementation in Stage 4.
|
||||||
|
export const SCHEDULER_NAME = "@stargue/scheduler";
|
||||||
870
bun.lock
Normal file
870
bun.lock
Normal file
@@ -0,0 +1,870 @@
|
|||||||
|
{
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"configVersion": 1,
|
||||||
|
"workspaces": {
|
||||||
|
"": {
|
||||||
|
"name": "stargue-publishing-engine",
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^22.0.0",
|
||||||
|
"bun-types": "^1.3.12",
|
||||||
|
"turbo": "^2.3.0",
|
||||||
|
"typescript": "^5.7.0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"apps/admin": {
|
||||||
|
"name": "@stargue/admin",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@stargue/observability": "workspace:*",
|
||||||
|
"@stargue/sanitize": "workspace:*",
|
||||||
|
"@stargue/schema": "workspace:*",
|
||||||
|
"next": "^16.0.0",
|
||||||
|
"react": "^19.0.0",
|
||||||
|
"react-dom": "^19.0.0",
|
||||||
|
"zod": "^3.23.0",
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@axe-core/playwright": "^4.10.0",
|
||||||
|
"@playwright/test": "^1.48.0",
|
||||||
|
"@types/react": "^19.0.0",
|
||||||
|
"@types/react-dom": "^19.0.0",
|
||||||
|
"axe-core": "^4.10.0",
|
||||||
|
"typescript": "^5.7.0",
|
||||||
|
"vitest": "^2.1.0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"apps/mcp-linkedin": {
|
||||||
|
"name": "@stargue/mcp-linkedin",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@modelcontextprotocol/sdk": "1.0.3",
|
||||||
|
"@stargue/linkedin-client": "workspace:*",
|
||||||
|
"@stargue/observability": "workspace:*",
|
||||||
|
"@stargue/schema": "workspace:*",
|
||||||
|
"zod": "^3.23.0",
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^5.7.0",
|
||||||
|
"vitest": "^2.1.0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"apps/scheduler": {
|
||||||
|
"name": "@stargue/scheduler",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@stargue/linkedin-client": "workspace:*",
|
||||||
|
"@stargue/observability": "workspace:*",
|
||||||
|
"@stargue/schema": "workspace:*",
|
||||||
|
"bullmq": "^5.30.0",
|
||||||
|
"ioredis": "^5.4.0",
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^5.7.0",
|
||||||
|
"vitest": "^2.1.0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"packages/linkedin-client": {
|
||||||
|
"name": "@stargue/linkedin-client",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"dependencies": {
|
||||||
|
"libsodium-wrappers-sumo": "^0.7.15",
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"msw": "^2.6.0",
|
||||||
|
"typescript": "^5.7.0",
|
||||||
|
"vitest": "^2.1.0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"packages/observability": {
|
||||||
|
"name": "@stargue/observability",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"dependencies": {
|
||||||
|
"pino": "^9.5.0",
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^5.7.0",
|
||||||
|
"vitest": "^2.1.0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"packages/sanitize": {
|
||||||
|
"name": "@stargue/sanitize",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"dependencies": {
|
||||||
|
"mdast-util-from-markdown": "^2.0.0",
|
||||||
|
"remark-parse": "^11.0.0",
|
||||||
|
"remark-stringify": "^11.0.0",
|
||||||
|
"unified": "^11.0.0",
|
||||||
|
"unist-util-visit": "^5.0.0",
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/mdast": "^4.0.0",
|
||||||
|
"typescript": "^5.7.0",
|
||||||
|
"vitest": "^2.1.0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"packages/schema": {
|
||||||
|
"name": "@stargue/schema",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"dependencies": {
|
||||||
|
"drizzle-orm": "^0.36.0",
|
||||||
|
"zod": "^3.23.0",
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"drizzle-kit": "^0.28.0",
|
||||||
|
"typescript": "^5.7.0",
|
||||||
|
"vitest": "^2.1.0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"packages": {
|
||||||
|
"@axe-core/playwright": ["@axe-core/playwright@4.11.2", "", { "dependencies": { "axe-core": "~4.11.3" }, "peerDependencies": { "playwright-core": ">= 1.0.0" } }, "sha512-iP6hfNl9G0j/SEUSo8M7D80RbcDo9KRAAfDP4IT5OHB+Wm6zUHIrm8Y51BKI+Oyqduvipf9u1hcRy57zCBKzWQ=="],
|
||||||
|
|
||||||
|
"@drizzle-team/brocli": ["@drizzle-team/brocli@0.10.2", "", {}, "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w=="],
|
||||||
|
|
||||||
|
"@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils": ["@esbuild-kit/core-utils@3.3.2", "", { "dependencies": { "esbuild": "~0.18.20", "source-map-support": "^0.5.21" } }, "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/esm-loader": ["@esbuild-kit/esm-loader@2.6.5", "", { "dependencies": { "@esbuild-kit/core-utils": "^3.3.2", "get-tsconfig": "^4.7.0" } }, "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA=="],
|
||||||
|
|
||||||
|
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.19.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA=="],
|
||||||
|
|
||||||
|
"@esbuild/android-arm": ["@esbuild/android-arm@0.19.12", "", { "os": "android", "cpu": "arm" }, "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w=="],
|
||||||
|
|
||||||
|
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.19.12", "", { "os": "android", "cpu": "arm64" }, "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA=="],
|
||||||
|
|
||||||
|
"@esbuild/android-x64": ["@esbuild/android-x64@0.19.12", "", { "os": "android", "cpu": "x64" }, "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew=="],
|
||||||
|
|
||||||
|
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.19.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g=="],
|
||||||
|
|
||||||
|
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.19.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A=="],
|
||||||
|
|
||||||
|
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.19.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA=="],
|
||||||
|
|
||||||
|
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.19.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.19.12", "", { "os": "linux", "cpu": "arm" }, "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.19.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.19.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.19.12", "", { "os": "linux", "cpu": "none" }, "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.19.12", "", { "os": "linux", "cpu": "none" }, "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.19.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.19.12", "", { "os": "linux", "cpu": "none" }, "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.19.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.19.12", "", { "os": "linux", "cpu": "x64" }, "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg=="],
|
||||||
|
|
||||||
|
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.19.12", "", { "os": "none", "cpu": "x64" }, "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA=="],
|
||||||
|
|
||||||
|
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.19.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw=="],
|
||||||
|
|
||||||
|
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.19.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA=="],
|
||||||
|
|
||||||
|
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.19.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A=="],
|
||||||
|
|
||||||
|
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.19.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ=="],
|
||||||
|
|
||||||
|
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.19.12", "", { "os": "win32", "cpu": "x64" }, "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA=="],
|
||||||
|
|
||||||
|
"@img/colour": ["@img/colour@1.1.0", "", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="],
|
||||||
|
|
||||||
|
"@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="],
|
||||||
|
|
||||||
|
"@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="],
|
||||||
|
|
||||||
|
"@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="],
|
||||||
|
|
||||||
|
"@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="],
|
||||||
|
|
||||||
|
"@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A=="],
|
||||||
|
|
||||||
|
"@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw=="],
|
||||||
|
|
||||||
|
"@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.2.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA=="],
|
||||||
|
|
||||||
|
"@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.2.4", "", { "os": "linux", "cpu": "none" }, "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA=="],
|
||||||
|
|
||||||
|
"@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ=="],
|
||||||
|
|
||||||
|
"@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw=="],
|
||||||
|
|
||||||
|
"@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw=="],
|
||||||
|
|
||||||
|
"@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg=="],
|
||||||
|
|
||||||
|
"@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.4" }, "os": "linux", "cpu": "arm" }, "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw=="],
|
||||||
|
|
||||||
|
"@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg=="],
|
||||||
|
|
||||||
|
"@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.4" }, "os": "linux", "cpu": "ppc64" }, "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA=="],
|
||||||
|
|
||||||
|
"@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.2.4" }, "os": "linux", "cpu": "none" }, "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw=="],
|
||||||
|
|
||||||
|
"@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.4" }, "os": "linux", "cpu": "s390x" }, "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg=="],
|
||||||
|
|
||||||
|
"@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ=="],
|
||||||
|
|
||||||
|
"@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg=="],
|
||||||
|
|
||||||
|
"@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q=="],
|
||||||
|
|
||||||
|
"@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.5", "", { "dependencies": { "@emnapi/runtime": "^1.7.0" }, "cpu": "none" }, "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw=="],
|
||||||
|
|
||||||
|
"@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g=="],
|
||||||
|
|
||||||
|
"@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg=="],
|
||||||
|
|
||||||
|
"@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="],
|
||||||
|
|
||||||
|
"@inquirer/ansi": ["@inquirer/ansi@2.0.5", "", {}, "sha512-doc2sWgJpbFQ64UflSVd17ibMGDuxO1yKgOgLMwavzESnXjFWJqUeG8saYosqKpHp4kWiM5x1nXvEjbpx90gzw=="],
|
||||||
|
|
||||||
|
"@inquirer/confirm": ["@inquirer/confirm@6.0.11", "", { "dependencies": { "@inquirer/core": "^11.1.8", "@inquirer/type": "^4.0.5" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-pTpHjg0iEIRMYV/7oCZUMf27/383E6Wyhfc/MY+AVQGEoUobffIYWOK9YLP2XFRGz/9i6WlTQh1CkFVIo2Y7XA=="],
|
||||||
|
|
||||||
|
"@inquirer/core": ["@inquirer/core@11.1.8", "", { "dependencies": { "@inquirer/ansi": "^2.0.5", "@inquirer/figures": "^2.0.5", "@inquirer/type": "^4.0.5", "cli-width": "^4.1.0", "fast-wrap-ansi": "^0.2.0", "mute-stream": "^3.0.0", "signal-exit": "^4.1.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-/u+yJk2pOKNDOh1ZgdUH2RQaRx6OOH4I0uwL95qPvTFTIL38YBsuSC4r1yXBB3Q6JvNqFFc202gk0Ew79rrcjA=="],
|
||||||
|
|
||||||
|
"@inquirer/figures": ["@inquirer/figures@2.0.5", "", {}, "sha512-NsSs4kzfm12lNetHwAn3GEuH317IzpwrMCbOuMIVytpjnJ90YYHNwdRgYGuKmVxwuIqSgqk3M5qqQt1cDk0tGQ=="],
|
||||||
|
|
||||||
|
"@inquirer/type": ["@inquirer/type@4.0.5", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-aetVUNeKNc/VriqXlw1NRSW0zhMBB0W4bNbWRJgzRl/3d0QNDQFfk0GO5SDdtjMZVg6o8ZKEiadd7SCCzoOn5Q=="],
|
||||||
|
|
||||||
|
"@ioredis/commands": ["@ioredis/commands@1.5.1", "", {}, "sha512-JH8ZL/ywcJyR9MmJ5BNqZllXNZQqQbnVZOqpPQqE1vHiFgAw4NHbvE0FOduNU8IX9babitBT46571OnPTT0Zcw=="],
|
||||||
|
|
||||||
|
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
|
||||||
|
|
||||||
|
"@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.0.3", "", { "dependencies": { "content-type": "^1.0.5", "raw-body": "^3.0.0", "zod": "^3.23.8" } }, "sha512-2as3cX/VJ0YBHGmdv3GFyTpoM8q2gqE98zh3Vf1NwnsSY0h3mvoO07MUzfygCKkWsFjcZm4otIiqD6Xh7kiSBQ=="],
|
||||||
|
|
||||||
|
"@msgpackr-extract/msgpackr-extract-darwin-arm64": ["@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw=="],
|
||||||
|
|
||||||
|
"@msgpackr-extract/msgpackr-extract-darwin-x64": ["@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw=="],
|
||||||
|
|
||||||
|
"@msgpackr-extract/msgpackr-extract-linux-arm": ["@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3", "", { "os": "linux", "cpu": "arm" }, "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw=="],
|
||||||
|
|
||||||
|
"@msgpackr-extract/msgpackr-extract-linux-arm64": ["@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg=="],
|
||||||
|
|
||||||
|
"@msgpackr-extract/msgpackr-extract-linux-x64": ["@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3", "", { "os": "linux", "cpu": "x64" }, "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg=="],
|
||||||
|
|
||||||
|
"@msgpackr-extract/msgpackr-extract-win32-x64": ["@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3", "", { "os": "win32", "cpu": "x64" }, "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ=="],
|
||||||
|
|
||||||
|
"@mswjs/interceptors": ["@mswjs/interceptors@0.41.4", "", { "dependencies": { "@open-draft/deferred-promise": "^2.2.0", "@open-draft/logger": "^0.3.0", "@open-draft/until": "^2.0.0", "is-node-process": "^1.2.0", "outvariant": "^1.4.3", "strict-event-emitter": "^0.5.1" } }, "sha512-3B9EinUkrdOUGYzHRzRWSXunQ4YFGboJnyLNRwEJWEde+j8fNhPUHvrN1E3g1DU/iS/s8JQrMNVe+S7AHHVs0w=="],
|
||||||
|
|
||||||
|
"@next/env": ["@next/env@16.2.4", "", {}, "sha512-dKkkOzOSwFYe5RX6y26fZgkSpVAlIOJKQHIiydQcrWH6y/97+RceSOAdjZ14Qa3zLduVUy0TXcn+EiM6t4rPgw=="],
|
||||||
|
|
||||||
|
"@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@16.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-OXTFFox5EKN1Ym08vfrz+OXxmCcEjT4SFMbNRsWZE99dMqt2Kcusl5MqPXcW232RYkMLQTy0hqgAMEsfEd/l2A=="],
|
||||||
|
|
||||||
|
"@next/swc-darwin-x64": ["@next/swc-darwin-x64@16.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-XhpVnUfmYWvD3YrXu55XdcAkQtOnvaI6wtQa8fuF5fGoKoxIUZ0kWPtcOfqJEWngFF/lOS9l3+O9CcownhiQxQ=="],
|
||||||
|
|
||||||
|
"@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@16.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-Mx/tjlNA3G8kg14QvuGAJ4xBwPk1tUHq56JxZ8CXnZwz1Etz714soCEzGQQzVMz4bEnGPowzkV6Xrp6wAkEWOQ=="],
|
||||||
|
|
||||||
|
"@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@16.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-iVMMp14514u7Nup2umQS03nT/bN9HurK8ufylC3FZNykrwjtx7V1A7+4kvhbDSCeonTVqV3Txnv0Lu+m2oDXNg=="],
|
||||||
|
|
||||||
|
"@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@16.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-EZOvm1aQWgnI/N/xcWOlnS3RQBk0VtVav5Zo7n4p0A7UKyTDx047k8opDbXgBpHl4CulRqRfbw3QrX2w5UOXMQ=="],
|
||||||
|
|
||||||
|
"@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@16.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-h9FxsngCm9cTBf71AR4fGznDEDx1hS7+kSEiIRjq5kO1oXWm07DxVGZjCvk0SGx7TSjlUqhI8oOyz7NfwAdPoA=="],
|
||||||
|
|
||||||
|
"@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@16.2.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-3NdJV5OXMSOeJYijX+bjaLge3mJBlh4ybydbT4GFoB/2hAojWHtMhl3CYlYoMrjPuodp0nzFVi4Tj2+WaMg+Ow=="],
|
||||||
|
|
||||||
|
"@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@16.2.4", "", { "os": "win32", "cpu": "x64" }, "sha512-kMVGgsqhO5YTYODD9IPGGhA6iprWidQckK3LmPeW08PIFENRmgfb4MjXHO+p//d+ts2rpjvK5gXWzXSMrPl9cw=="],
|
||||||
|
|
||||||
|
"@open-draft/deferred-promise": ["@open-draft/deferred-promise@3.0.0", "", {}, "sha512-XW375UK8/9SqUVNVa6M0yEy8+iTi4QN5VZ7aZuRFQmy76LRwI9wy5F4YIBU6T+eTe2/DNDo8tqu8RHlwLHM6RA=="],
|
||||||
|
|
||||||
|
"@open-draft/logger": ["@open-draft/logger@0.3.0", "", { "dependencies": { "is-node-process": "^1.2.0", "outvariant": "^1.4.0" } }, "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ=="],
|
||||||
|
|
||||||
|
"@open-draft/until": ["@open-draft/until@2.1.0", "", {}, "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg=="],
|
||||||
|
|
||||||
|
"@pinojs/redact": ["@pinojs/redact@0.4.0", "", {}, "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg=="],
|
||||||
|
|
||||||
|
"@playwright/test": ["@playwright/test@1.59.1", "", { "dependencies": { "playwright": "1.59.1" }, "bin": { "playwright": "cli.js" } }, "sha512-PG6q63nQg5c9rIi4/Z5lR5IVF7yU5MqmKaPOe0HSc0O2cX1fPi96sUQu5j7eo4gKCkB2AnNGoWt7y4/Xx3Kcqg=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.60.2", "", { "os": "android", "cpu": "arm" }, "sha512-dnlp69efPPg6Uaw2dVqzWRfAWRnYVb1XJ8CyyhIbZeaq4CA5/mLeZ1IEt9QqQxmbdvagjLIm2ZL8BxXv5lH4Yw=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.60.2", "", { "os": "android", "cpu": "arm64" }, "sha512-OqZTwDRDchGRHHm/hwLOL7uVPB9aUvI0am/eQuWMNyFHf5PSEQmyEeYYheA0EPPKUO/l0uigCp+iaTjoLjVoHg=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.60.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-UwRE7CGpvSVEQS8gUMBe1uADWjNnVgP3Iusyda1nSRwNDCsRjnGc7w6El6WLQsXmZTbLZx9cecegumcitNfpmA=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.60.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-gjEtURKLCC5VXm1I+2i1u9OhxFsKAQJKTVB8WvDAHF+oZlq0GTVFOlTlO1q3AlCTE/DF32c16ESvfgqR7343/g=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.60.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-Bcl6CYDeAgE70cqZaMojOi/eK63h5Me97ZqAQoh77VPjMysA/4ORQBRGo3rRy45x4MzVlU9uZxs8Uwy7ZaKnBw=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.60.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-LU+TPda3mAE2QB0/Hp5VyeKJivpC6+tlOXd1VMoXV/YFMvk/MNk5iXeBfB4MQGRWyOYVJ01625vjkr0Az98OJQ=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.60.2", "", { "os": "linux", "cpu": "arm" }, "sha512-2QxQrM+KQ7DAW4o22j+XZ6RKdxjLD7BOWTP0Bv0tmjdyhXSsr2Ul1oJDQqh9Zf5qOwTuTc7Ek83mOFaKnodPjg=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.60.2", "", { "os": "linux", "cpu": "arm" }, "sha512-TbziEu2DVsTEOPif2mKWkMeDMLoYjx95oESa9fkQQK7r/Orta0gnkcDpzwufEcAO2BLBsD7mZkXGFqEdMRRwfw=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.60.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-bO/rVDiDUuM2YfuCUwZ1t1cP+/yqjqz+Xf2VtkdppefuOFS2OSeAfgafaHNkFn0t02hEyXngZkxtGqXcXwO8Rg=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.60.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-hr26p7e93Rl0Za+JwW7EAnwAvKkehh12BU1Llm9Ykiibg4uIr2rbpxG9WCf56GuvidlTG9KiiQT/TXT1yAWxTA=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.60.2", "", { "os": "linux", "cpu": "none" }, "sha512-pOjB/uSIyDt+ow3k/RcLvUAOGpysT2phDn7TTUB3n75SlIgZzM6NKAqlErPhoFU+npgY3/n+2HYIQVbF70P9/A=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.60.2", "", { "os": "linux", "cpu": "none" }, "sha512-2/w+q8jszv9Ww1c+6uJT3OwqhdmGP2/4T17cu8WuwyUuuaCDDJ2ojdyYwZzCxx0GcsZBhzi3HmH+J5pZNXnd+Q=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.60.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-11+aL5vKheYgczxtPVVRhdptAM2H7fcDR5Gw4/bTcteuZBlH4oP9f5s9zYO9aGZvoGeBpqXI/9TZZihZ609wKw=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.60.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-i16fokAGK46IVZuV8LIIwMdtqhin9hfYkCh8pf8iC3QU3LpwL+1FSFGej+O7l3E/AoknL6Dclh2oTdnRMpTzFQ=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.60.2", "", { "os": "linux", "cpu": "none" }, "sha512-49FkKS6RGQoriDSK/6E2GkAsAuU5kETFCh7pG4yD/ylj9rKhTmO3elsnmBvRD4PgJPds5W2PkhC82aVwmUcJ7A=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.60.2", "", { "os": "linux", "cpu": "none" }, "sha512-mjYNkHPfGpUR00DuM1ZZIgs64Hpf4bWcz9Z41+4Q+pgDx73UwWdAYyf6EG/lRFldmdHHzgrYyge5akFUW0D3mQ=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.60.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-ALyvJz965BQk8E9Al/JDKKDLH2kfKFLTGMlgkAbbYtZuJt9LU8DW3ZoDMCtQpXAltZxwBHevXz5u+gf0yA0YoA=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.60.2", "", { "os": "linux", "cpu": "x64" }, "sha512-UQjrkIdWrKI626Du8lCQ6MJp/6V1LAo2bOK9OTu4mSn8GGXIkPXk/Vsp4bLHCd9Z9Iz2OTEaokUE90VweJgIYQ=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.60.2", "", { "os": "linux", "cpu": "x64" }, "sha512-bTsRGj6VlSdn/XD4CGyzMnzaBs9bsRxy79eTqTCBsA8TMIEky7qg48aPkvJvFe1HyzQ5oMZdg7AnVlWQSKLTnw=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.60.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-6d4Z3534xitaA1FcMWP7mQPq5zGwBmGbhphh2DwaA1aNIXUu3KTOfwrWpbwI4/Gr0uANo7NTtaykFyO2hPuFLg=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.60.2", "", { "os": "none", "cpu": "arm64" }, "sha512-NetAg5iO2uN7eB8zE5qrZ3CSil+7IJt4WDFLcC75Ymywq1VZVD6qJ6EvNLjZ3rEm6gB7XW5JdT60c6MN35Z85Q=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.60.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-NCYhOotpgWZ5kdxCZsv6Iudx0wX8980Q/oW4pNFNihpBKsDbEA1zpkfxJGC0yugsUuyDZ7gL37dbzwhR0VI7pQ=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.60.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-RXsaOqXxfoUBQoOgvmmijVxJnW2IGB0eoMO7F8FAjaj0UTywUO/luSqimWBJn04WNgUkeNhh7fs7pESXajWmkg=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.60.2", "", { "os": "win32", "cpu": "x64" }, "sha512-qdAzEULD+/hzObedtmV6iBpdL5TIbKVztGiK7O3/KYSf+HIzU257+MX1EXJcyIiDbMAqmbwaufcYPvyRryeZtA=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.60.2", "", { "os": "win32", "cpu": "x64" }, "sha512-Nd/SgG27WoA9e+/TdK74KnHz852TLa94ovOYySo/yMPuTmpckK/jIF2jSwS3g7ELSKXK13/cVdmg1Z/DaCWKxA=="],
|
||||||
|
|
||||||
|
"@stargue/admin": ["@stargue/admin@workspace:apps/admin"],
|
||||||
|
|
||||||
|
"@stargue/linkedin-client": ["@stargue/linkedin-client@workspace:packages/linkedin-client"],
|
||||||
|
|
||||||
|
"@stargue/mcp-linkedin": ["@stargue/mcp-linkedin@workspace:apps/mcp-linkedin"],
|
||||||
|
|
||||||
|
"@stargue/observability": ["@stargue/observability@workspace:packages/observability"],
|
||||||
|
|
||||||
|
"@stargue/sanitize": ["@stargue/sanitize@workspace:packages/sanitize"],
|
||||||
|
|
||||||
|
"@stargue/scheduler": ["@stargue/scheduler@workspace:apps/scheduler"],
|
||||||
|
|
||||||
|
"@stargue/schema": ["@stargue/schema@workspace:packages/schema"],
|
||||||
|
|
||||||
|
"@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="],
|
||||||
|
|
||||||
|
"@turbo/darwin-64": ["@turbo/darwin-64@2.9.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-X/56SnVXIQZBLKwniGTwEQTGmtE5brSACnKMBWpY3YafuxVYefrC2acamfjgxP7BG5w3I+6jf0UrLoSzgPcSJg=="],
|
||||||
|
|
||||||
|
"@turbo/darwin-arm64": ["@turbo/darwin-arm64@2.9.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-aalBeSl4agT/QtYGDyf/XLajedWzUC9Vg/pm/YO6QQ93vkQ91Vz5uK1ta5RbVRDozQSz4njxUNqRNmOXDzW+qw=="],
|
||||||
|
|
||||||
|
"@turbo/linux-64": ["@turbo/linux-64@2.9.6", "", { "os": "linux", "cpu": "x64" }, "sha512-YKi05jnNHaD7vevgYwahpzGwbsNNTwzU2c7VZdmdFm7+cGDP4oREUWSsainiMfRqjRuolQxBwRn8wf1jmu+YZA=="],
|
||||||
|
|
||||||
|
"@turbo/linux-arm64": ["@turbo/linux-arm64@2.9.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-02o/ZS69cOYEDczXvOB2xmyrtzjQ2hVFtWZK1iqxXUfzMmTjZK4UumrfNnjckSg+gqeBfnPRHa0NstA173Ik3g=="],
|
||||||
|
|
||||||
|
"@turbo/windows-64": ["@turbo/windows-64@2.9.6", "", { "os": "win32", "cpu": "x64" }, "sha512-wVdQjvnBI15wB6JrA+43CtUtagjIMmX6XYO758oZHAsCNSxqRlJtdyujih0D8OCnwCRWiGWGI63zAxR0hO6s9g=="],
|
||||||
|
|
||||||
|
"@turbo/windows-arm64": ["@turbo/windows-arm64@2.9.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-1XUUyWW0W6FTSqGEhU8RHVqb2wP1SPkr7hIvBlMEwH9jr+sJQK5kqeosLJ/QaUv4ecSAd1ZhIrLoW7qslAzT4A=="],
|
||||||
|
|
||||||
|
"@types/debug": ["@types/debug@4.1.13", "", { "dependencies": { "@types/ms": "*" } }, "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw=="],
|
||||||
|
|
||||||
|
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
||||||
|
|
||||||
|
"@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="],
|
||||||
|
|
||||||
|
"@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="],
|
||||||
|
|
||||||
|
"@types/node": ["@types/node@22.19.17", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q=="],
|
||||||
|
|
||||||
|
"@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="],
|
||||||
|
|
||||||
|
"@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="],
|
||||||
|
|
||||||
|
"@types/set-cookie-parser": ["@types/set-cookie-parser@2.4.10", "", { "dependencies": { "@types/node": "*" } }, "sha512-GGmQVGpQWUe5qglJozEjZV/5dyxbOOZ0LHe/lqyWssB88Y4svNfst0uqBVscdDeIKl5Jy5+aPSvy7mI9tYRguw=="],
|
||||||
|
|
||||||
|
"@types/statuses": ["@types/statuses@2.0.6", "", {}, "sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA=="],
|
||||||
|
|
||||||
|
"@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
|
||||||
|
|
||||||
|
"@vitest/expect": ["@vitest/expect@2.1.9", "", { "dependencies": { "@vitest/spy": "2.1.9", "@vitest/utils": "2.1.9", "chai": "^5.1.2", "tinyrainbow": "^1.2.0" } }, "sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw=="],
|
||||||
|
|
||||||
|
"@vitest/mocker": ["@vitest/mocker@2.1.9", "", { "dependencies": { "@vitest/spy": "2.1.9", "estree-walker": "^3.0.3", "magic-string": "^0.30.12" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^5.0.0" }, "optionalPeers": ["msw", "vite"] }, "sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg=="],
|
||||||
|
|
||||||
|
"@vitest/pretty-format": ["@vitest/pretty-format@2.1.9", "", { "dependencies": { "tinyrainbow": "^1.2.0" } }, "sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ=="],
|
||||||
|
|
||||||
|
"@vitest/runner": ["@vitest/runner@2.1.9", "", { "dependencies": { "@vitest/utils": "2.1.9", "pathe": "^1.1.2" } }, "sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g=="],
|
||||||
|
|
||||||
|
"@vitest/snapshot": ["@vitest/snapshot@2.1.9", "", { "dependencies": { "@vitest/pretty-format": "2.1.9", "magic-string": "^0.30.12", "pathe": "^1.1.2" } }, "sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ=="],
|
||||||
|
|
||||||
|
"@vitest/spy": ["@vitest/spy@2.1.9", "", { "dependencies": { "tinyspy": "^3.0.2" } }, "sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ=="],
|
||||||
|
|
||||||
|
"@vitest/utils": ["@vitest/utils@2.1.9", "", { "dependencies": { "@vitest/pretty-format": "2.1.9", "loupe": "^3.1.2", "tinyrainbow": "^1.2.0" } }, "sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ=="],
|
||||||
|
|
||||||
|
"ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||||
|
|
||||||
|
"ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
||||||
|
|
||||||
|
"assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="],
|
||||||
|
|
||||||
|
"atomic-sleep": ["atomic-sleep@1.0.0", "", {}, "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ=="],
|
||||||
|
|
||||||
|
"axe-core": ["axe-core@4.11.3", "", {}, "sha512-zBQouZixDTbo3jMGqHKyePxYxr1e5W8UdTmBQ7sNtaA9M2bE32daxxPLS/jojhKOHxQ7LWwPjfiwf/fhaJWzlg=="],
|
||||||
|
|
||||||
|
"bail": ["bail@2.0.2", "", {}, "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="],
|
||||||
|
|
||||||
|
"baseline-browser-mapping": ["baseline-browser-mapping@2.10.20", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-1AaXxEPfXT+GvTBJFuy4yXVHWJBXa4OdbIebGN/wX5DlsIkU0+wzGnd2lOzokSk51d5LUmqjgBLRLlypLUqInQ=="],
|
||||||
|
|
||||||
|
"buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="],
|
||||||
|
|
||||||
|
"bullmq": ["bullmq@5.74.1", "", { "dependencies": { "cron-parser": "4.9.0", "ioredis": "5.10.1", "msgpackr": "1.11.5", "node-abort-controller": "3.1.1", "semver": "7.7.4", "tslib": "2.8.1", "uuid": "11.1.0" } }, "sha512-GfJEos2zoOGM9xqkB7VZouwwFuejKFqm667cBcmbBekJXKqqXWk4QYP3Uy2pzgUwCbg1cR7GgGmGczM7fnhWSA=="],
|
||||||
|
|
||||||
|
"bun-types": ["bun-types@1.3.12", "", { "dependencies": { "@types/node": "*" } }, "sha512-HqOLj5PoFajAQciOMRiIZGNoKxDJSr6qigAttOX40vJuSp6DN/CxWp9s3C1Xwm4oH7ybueITwiaOcWXoYVoRkA=="],
|
||||||
|
|
||||||
|
"bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
|
||||||
|
|
||||||
|
"cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="],
|
||||||
|
|
||||||
|
"caniuse-lite": ["caniuse-lite@1.0.30001788", "", {}, "sha512-6q8HFp+lOQtcf7wBK+uEenxymVWkGKkjFpCvw5W25cmMwEDU45p1xQFBQv8JDlMMry7eNxyBaR+qxgmTUZkIRQ=="],
|
||||||
|
|
||||||
|
"chai": ["chai@5.3.3", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="],
|
||||||
|
|
||||||
|
"character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="],
|
||||||
|
|
||||||
|
"check-error": ["check-error@2.1.3", "", {}, "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA=="],
|
||||||
|
|
||||||
|
"cli-width": ["cli-width@4.1.0", "", {}, "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ=="],
|
||||||
|
|
||||||
|
"client-only": ["client-only@0.0.1", "", {}, "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="],
|
||||||
|
|
||||||
|
"cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="],
|
||||||
|
|
||||||
|
"cluster-key-slot": ["cluster-key-slot@1.1.2", "", {}, "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA=="],
|
||||||
|
|
||||||
|
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
||||||
|
|
||||||
|
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
||||||
|
|
||||||
|
"content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="],
|
||||||
|
|
||||||
|
"cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="],
|
||||||
|
|
||||||
|
"cron-parser": ["cron-parser@4.9.0", "", { "dependencies": { "luxon": "^3.2.1" } }, "sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q=="],
|
||||||
|
|
||||||
|
"csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
|
||||||
|
|
||||||
|
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
||||||
|
|
||||||
|
"decode-named-character-reference": ["decode-named-character-reference@1.3.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q=="],
|
||||||
|
|
||||||
|
"deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="],
|
||||||
|
|
||||||
|
"denque": ["denque@2.1.0", "", {}, "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw=="],
|
||||||
|
|
||||||
|
"depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="],
|
||||||
|
|
||||||
|
"dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="],
|
||||||
|
|
||||||
|
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
||||||
|
|
||||||
|
"devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="],
|
||||||
|
|
||||||
|
"drizzle-kit": ["drizzle-kit@0.28.1", "", { "dependencies": { "@drizzle-team/brocli": "^0.10.2", "@esbuild-kit/esm-loader": "^2.5.5", "esbuild": "^0.19.7", "esbuild-register": "^3.5.0" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-JimOV+ystXTWMgZkLHYHf2w3oS28hxiH1FR0dkmJLc7GHzdGJoJAQtQS5DRppnabsRZwE2U1F6CuezVBgmsBBQ=="],
|
||||||
|
|
||||||
|
"drizzle-orm": ["drizzle-orm@0.36.4", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=3", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/react": ">=18", "@types/sql.js": "*", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "react": ">=18", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/react", "@types/sql.js", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "knex", "kysely", "mysql2", "pg", "postgres", "react", "sql.js", "sqlite3"] }, "sha512-1OZY3PXD7BR00Gl61UUOFihslDldfH4NFRH2MbP54Yxi0G/PKn4HfO65JYZ7c16DeP3SpM3Aw+VXVG9j6CRSXA=="],
|
||||||
|
|
||||||
|
"emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||||
|
|
||||||
|
"es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="],
|
||||||
|
|
||||||
|
"esbuild": ["esbuild@0.19.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.19.12", "@esbuild/android-arm": "0.19.12", "@esbuild/android-arm64": "0.19.12", "@esbuild/android-x64": "0.19.12", "@esbuild/darwin-arm64": "0.19.12", "@esbuild/darwin-x64": "0.19.12", "@esbuild/freebsd-arm64": "0.19.12", "@esbuild/freebsd-x64": "0.19.12", "@esbuild/linux-arm": "0.19.12", "@esbuild/linux-arm64": "0.19.12", "@esbuild/linux-ia32": "0.19.12", "@esbuild/linux-loong64": "0.19.12", "@esbuild/linux-mips64el": "0.19.12", "@esbuild/linux-ppc64": "0.19.12", "@esbuild/linux-riscv64": "0.19.12", "@esbuild/linux-s390x": "0.19.12", "@esbuild/linux-x64": "0.19.12", "@esbuild/netbsd-x64": "0.19.12", "@esbuild/openbsd-x64": "0.19.12", "@esbuild/sunos-x64": "0.19.12", "@esbuild/win32-arm64": "0.19.12", "@esbuild/win32-ia32": "0.19.12", "@esbuild/win32-x64": "0.19.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg=="],
|
||||||
|
|
||||||
|
"esbuild-register": ["esbuild-register@3.6.0", "", { "dependencies": { "debug": "^4.3.4" }, "peerDependencies": { "esbuild": ">=0.12 <1" } }, "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg=="],
|
||||||
|
|
||||||
|
"escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
|
||||||
|
|
||||||
|
"estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="],
|
||||||
|
|
||||||
|
"expect-type": ["expect-type@1.3.0", "", {}, "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA=="],
|
||||||
|
|
||||||
|
"extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="],
|
||||||
|
|
||||||
|
"fast-string-truncated-width": ["fast-string-truncated-width@3.0.3", "", {}, "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g=="],
|
||||||
|
|
||||||
|
"fast-string-width": ["fast-string-width@3.0.2", "", { "dependencies": { "fast-string-truncated-width": "^3.0.2" } }, "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg=="],
|
||||||
|
|
||||||
|
"fast-wrap-ansi": ["fast-wrap-ansi@0.2.0", "", { "dependencies": { "fast-string-width": "^3.0.2" } }, "sha512-rLV8JHxTyhVmFYhBJuMujcrHqOT2cnO5Zxj37qROj23CP39GXubJRBUFF0z8KFK77Uc0SukZUf7JZhsVEQ6n8w=="],
|
||||||
|
|
||||||
|
"fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="],
|
||||||
|
|
||||||
|
"get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="],
|
||||||
|
|
||||||
|
"get-tsconfig": ["get-tsconfig@4.14.0", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA=="],
|
||||||
|
|
||||||
|
"graphql": ["graphql@16.13.2", "", {}, "sha512-5bJ+nf/UCpAjHM8i06fl7eLyVC9iuNAjm9qzkiu2ZGhM0VscSvS6WDPfAwkdkBuoXGM9FJSbKl6wylMwP9Ktig=="],
|
||||||
|
|
||||||
|
"headers-polyfill": ["headers-polyfill@5.0.1", "", { "dependencies": { "@types/set-cookie-parser": "^2.4.10", "set-cookie-parser": "^3.0.1" } }, "sha512-1TJ6Fih/b8h5TIcv+1+Hw0PDQWJTKDKzFZzcKOiW1wJza3XoAQlkCuXLbymPYB8+ZQyw8mHvdw560e8zVFIWyA=="],
|
||||||
|
|
||||||
|
"http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="],
|
||||||
|
|
||||||
|
"iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
|
||||||
|
|
||||||
|
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
|
||||||
|
|
||||||
|
"ioredis": ["ioredis@5.10.1", "", { "dependencies": { "@ioredis/commands": "1.5.1", "cluster-key-slot": "^1.1.0", "debug": "^4.3.4", "denque": "^2.1.0", "lodash.defaults": "^4.2.0", "lodash.isarguments": "^3.1.0", "redis-errors": "^1.2.0", "redis-parser": "^3.0.0", "standard-as-callback": "^2.1.0" } }, "sha512-HuEDBTI70aYdx1v6U97SbNx9F1+svQKBDo30o0b9fw055LMepzpOOd0Ccg9Q6tbqmBSJaMuY0fB7yw9/vjBYCA=="],
|
||||||
|
|
||||||
|
"is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
|
||||||
|
|
||||||
|
"is-node-process": ["is-node-process@1.2.0", "", {}, "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw=="],
|
||||||
|
|
||||||
|
"is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="],
|
||||||
|
|
||||||
|
"libsodium-sumo": ["libsodium-sumo@0.7.16", "", {}, "sha512-x6atrz2AdXCJg6G709x9W9TTJRI6/0NcL5dD0l5GGVqNE48UJmDsjO4RUWYTeyXXUpg+NXZ2SHECaZnFRYzwGA=="],
|
||||||
|
|
||||||
|
"libsodium-wrappers-sumo": ["libsodium-wrappers-sumo@0.7.16", "", { "dependencies": { "libsodium-sumo": "^0.7.16" } }, "sha512-gR0JEFPeN3831lB9+ogooQk0KH4K5LSMIO5Prd5Q5XYR2wHFtZfPg0eP7t1oJIWq+UIzlU4WVeBxZ97mt28tXw=="],
|
||||||
|
|
||||||
|
"lodash.defaults": ["lodash.defaults@4.2.0", "", {}, "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ=="],
|
||||||
|
|
||||||
|
"lodash.isarguments": ["lodash.isarguments@3.1.0", "", {}, "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg=="],
|
||||||
|
|
||||||
|
"longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="],
|
||||||
|
|
||||||
|
"loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="],
|
||||||
|
|
||||||
|
"luxon": ["luxon@3.7.2", "", {}, "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew=="],
|
||||||
|
|
||||||
|
"magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
|
||||||
|
|
||||||
|
"mdast-util-from-markdown": ["mdast-util-from-markdown@2.0.3", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "mdast-util-to-string": "^4.0.0", "micromark": "^4.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q=="],
|
||||||
|
|
||||||
|
"mdast-util-phrasing": ["mdast-util-phrasing@4.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "unist-util-is": "^6.0.0" } }, "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w=="],
|
||||||
|
|
||||||
|
"mdast-util-to-markdown": ["mdast-util-to-markdown@2.1.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "longest-streak": "^3.0.0", "mdast-util-phrasing": "^4.0.0", "mdast-util-to-string": "^4.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "unist-util-visit": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA=="],
|
||||||
|
|
||||||
|
"mdast-util-to-string": ["mdast-util-to-string@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0" } }, "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg=="],
|
||||||
|
|
||||||
|
"micromark": ["micromark@4.0.2", "", { "dependencies": { "@types/debug": "^4.0.0", "debug": "^4.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA=="],
|
||||||
|
|
||||||
|
"micromark-core-commonmark": ["micromark-core-commonmark@2.0.3", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg=="],
|
||||||
|
|
||||||
|
"micromark-factory-destination": ["micromark-factory-destination@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA=="],
|
||||||
|
|
||||||
|
"micromark-factory-label": ["micromark-factory-label@2.0.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg=="],
|
||||||
|
|
||||||
|
"micromark-factory-space": ["micromark-factory-space@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg=="],
|
||||||
|
|
||||||
|
"micromark-factory-title": ["micromark-factory-title@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw=="],
|
||||||
|
|
||||||
|
"micromark-factory-whitespace": ["micromark-factory-whitespace@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ=="],
|
||||||
|
|
||||||
|
"micromark-util-character": ["micromark-util-character@2.1.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="],
|
||||||
|
|
||||||
|
"micromark-util-chunked": ["micromark-util-chunked@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA=="],
|
||||||
|
|
||||||
|
"micromark-util-classify-character": ["micromark-util-classify-character@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q=="],
|
||||||
|
|
||||||
|
"micromark-util-combine-extensions": ["micromark-util-combine-extensions@2.0.1", "", { "dependencies": { "micromark-util-chunked": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg=="],
|
||||||
|
|
||||||
|
"micromark-util-decode-numeric-character-reference": ["micromark-util-decode-numeric-character-reference@2.0.2", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw=="],
|
||||||
|
|
||||||
|
"micromark-util-decode-string": ["micromark-util-decode-string@2.0.1", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ=="],
|
||||||
|
|
||||||
|
"micromark-util-encode": ["micromark-util-encode@2.0.1", "", {}, "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw=="],
|
||||||
|
|
||||||
|
"micromark-util-html-tag-name": ["micromark-util-html-tag-name@2.0.1", "", {}, "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA=="],
|
||||||
|
|
||||||
|
"micromark-util-normalize-identifier": ["micromark-util-normalize-identifier@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q=="],
|
||||||
|
|
||||||
|
"micromark-util-resolve-all": ["micromark-util-resolve-all@2.0.1", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg=="],
|
||||||
|
|
||||||
|
"micromark-util-sanitize-uri": ["micromark-util-sanitize-uri@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ=="],
|
||||||
|
|
||||||
|
"micromark-util-subtokenize": ["micromark-util-subtokenize@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA=="],
|
||||||
|
|
||||||
|
"micromark-util-symbol": ["micromark-util-symbol@2.0.1", "", {}, "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q=="],
|
||||||
|
|
||||||
|
"micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="],
|
||||||
|
|
||||||
|
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||||
|
|
||||||
|
"msgpackr": ["msgpackr@1.11.5", "", { "optionalDependencies": { "msgpackr-extract": "^3.0.2" } }, "sha512-UjkUHN0yqp9RWKy0Lplhh+wlpdt9oQBYgULZOiFhV3VclSF1JnSQWZ5r9gORQlNYaUKQoR8itv7g7z1xDDuACA=="],
|
||||||
|
|
||||||
|
"msgpackr-extract": ["msgpackr-extract@3.0.3", "", { "dependencies": { "node-gyp-build-optional-packages": "5.2.2" }, "optionalDependencies": { "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" }, "bin": { "download-msgpackr-prebuilds": "bin/download-prebuilds.js" } }, "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA=="],
|
||||||
|
|
||||||
|
"msw": ["msw@2.13.4", "", { "dependencies": { "@inquirer/confirm": "^6.0.11", "@mswjs/interceptors": "^0.41.3", "@open-draft/deferred-promise": "^3.0.0", "@types/statuses": "^2.0.6", "cookie": "^1.1.1", "graphql": "^16.13.2", "headers-polyfill": "^5.0.1", "is-node-process": "^1.2.0", "outvariant": "^1.4.3", "path-to-regexp": "^6.3.0", "picocolors": "^1.1.1", "rettime": "^0.11.7", "statuses": "^2.0.2", "strict-event-emitter": "^0.5.1", "tough-cookie": "^6.0.1", "type-fest": "^5.5.0", "until-async": "^3.0.2", "yargs": "^17.7.2" }, "peerDependencies": { "typescript": ">= 4.8.x" }, "optionalPeers": ["typescript"], "bin": { "msw": "cli/index.js" } }, "sha512-fPlKBeFe+8rpcyR3umUmmHuNwu6gc6T3STvkgEa9WDX/HEgal9wDeflpCUAIRtmvaLZM2igfI5y1bZ9G5J26KA=="],
|
||||||
|
|
||||||
|
"mute-stream": ["mute-stream@3.0.0", "", {}, "sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw=="],
|
||||||
|
|
||||||
|
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
||||||
|
|
||||||
|
"next": ["next@16.2.4", "", { "dependencies": { "@next/env": "16.2.4", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.9.19", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.2.4", "@next/swc-darwin-x64": "16.2.4", "@next/swc-linux-arm64-gnu": "16.2.4", "@next/swc-linux-arm64-musl": "16.2.4", "@next/swc-linux-x64-gnu": "16.2.4", "@next/swc-linux-x64-musl": "16.2.4", "@next/swc-win32-arm64-msvc": "16.2.4", "@next/swc-win32-x64-msvc": "16.2.4", "sharp": "^0.34.5" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-kPvz56wF5frc+FxlHI5qnklCzbq53HTwORaWBGdT0vNoKh1Aya9XC8aPauH4NJxqtzbWsS5mAbctm4cr+EkQ2Q=="],
|
||||||
|
|
||||||
|
"node-abort-controller": ["node-abort-controller@3.1.1", "", {}, "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ=="],
|
||||||
|
|
||||||
|
"node-gyp-build-optional-packages": ["node-gyp-build-optional-packages@5.2.2", "", { "dependencies": { "detect-libc": "^2.0.1" }, "bin": { "node-gyp-build-optional-packages": "bin.js", "node-gyp-build-optional-packages-optional": "optional.js", "node-gyp-build-optional-packages-test": "build-test.js" } }, "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw=="],
|
||||||
|
|
||||||
|
"on-exit-leak-free": ["on-exit-leak-free@2.1.2", "", {}, "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA=="],
|
||||||
|
|
||||||
|
"outvariant": ["outvariant@1.4.3", "", {}, "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA=="],
|
||||||
|
|
||||||
|
"path-to-regexp": ["path-to-regexp@6.3.0", "", {}, "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ=="],
|
||||||
|
|
||||||
|
"pathe": ["pathe@1.1.2", "", {}, "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="],
|
||||||
|
|
||||||
|
"pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="],
|
||||||
|
|
||||||
|
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||||
|
|
||||||
|
"pino": ["pino@9.14.0", "", { "dependencies": { "@pinojs/redact": "^0.4.0", "atomic-sleep": "^1.0.0", "on-exit-leak-free": "^2.1.0", "pino-abstract-transport": "^2.0.0", "pino-std-serializers": "^7.0.0", "process-warning": "^5.0.0", "quick-format-unescaped": "^4.0.3", "real-require": "^0.2.0", "safe-stable-stringify": "^2.3.1", "sonic-boom": "^4.0.1", "thread-stream": "^3.0.0" }, "bin": { "pino": "bin.js" } }, "sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w=="],
|
||||||
|
|
||||||
|
"pino-abstract-transport": ["pino-abstract-transport@2.0.0", "", { "dependencies": { "split2": "^4.0.0" } }, "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw=="],
|
||||||
|
|
||||||
|
"pino-std-serializers": ["pino-std-serializers@7.1.0", "", {}, "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw=="],
|
||||||
|
|
||||||
|
"playwright": ["playwright@1.59.1", "", { "dependencies": { "playwright-core": "1.59.1" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw=="],
|
||||||
|
|
||||||
|
"playwright-core": ["playwright-core@1.59.1", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg=="],
|
||||||
|
|
||||||
|
"postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="],
|
||||||
|
|
||||||
|
"process-warning": ["process-warning@5.0.0", "", {}, "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA=="],
|
||||||
|
|
||||||
|
"quick-format-unescaped": ["quick-format-unescaped@4.0.4", "", {}, "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg=="],
|
||||||
|
|
||||||
|
"raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="],
|
||||||
|
|
||||||
|
"react": ["react@19.2.5", "", {}, "sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA=="],
|
||||||
|
|
||||||
|
"react-dom": ["react-dom@19.2.5", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.5" } }, "sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag=="],
|
||||||
|
|
||||||
|
"real-require": ["real-require@0.2.0", "", {}, "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg=="],
|
||||||
|
|
||||||
|
"redis-errors": ["redis-errors@1.2.0", "", {}, "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w=="],
|
||||||
|
|
||||||
|
"redis-parser": ["redis-parser@3.0.0", "", { "dependencies": { "redis-errors": "^1.0.0" } }, "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A=="],
|
||||||
|
|
||||||
|
"remark-parse": ["remark-parse@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "micromark-util-types": "^2.0.0", "unified": "^11.0.0" } }, "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA=="],
|
||||||
|
|
||||||
|
"remark-stringify": ["remark-stringify@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-to-markdown": "^2.0.0", "unified": "^11.0.0" } }, "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw=="],
|
||||||
|
|
||||||
|
"require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="],
|
||||||
|
|
||||||
|
"resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="],
|
||||||
|
|
||||||
|
"rettime": ["rettime@0.11.7", "", {}, "sha512-DoAm1WjR1eH7z8sHPtvvUMIZh4/CSKkGCz6CxPqOrEAnOGtOuHSnSE9OC+razqxKuf4ub7pAYyl/vZV0vGs5tg=="],
|
||||||
|
|
||||||
|
"rollup": ["rollup@4.60.2", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.60.2", "@rollup/rollup-android-arm64": "4.60.2", "@rollup/rollup-darwin-arm64": "4.60.2", "@rollup/rollup-darwin-x64": "4.60.2", "@rollup/rollup-freebsd-arm64": "4.60.2", "@rollup/rollup-freebsd-x64": "4.60.2", "@rollup/rollup-linux-arm-gnueabihf": "4.60.2", "@rollup/rollup-linux-arm-musleabihf": "4.60.2", "@rollup/rollup-linux-arm64-gnu": "4.60.2", "@rollup/rollup-linux-arm64-musl": "4.60.2", "@rollup/rollup-linux-loong64-gnu": "4.60.2", "@rollup/rollup-linux-loong64-musl": "4.60.2", "@rollup/rollup-linux-ppc64-gnu": "4.60.2", "@rollup/rollup-linux-ppc64-musl": "4.60.2", "@rollup/rollup-linux-riscv64-gnu": "4.60.2", "@rollup/rollup-linux-riscv64-musl": "4.60.2", "@rollup/rollup-linux-s390x-gnu": "4.60.2", "@rollup/rollup-linux-x64-gnu": "4.60.2", "@rollup/rollup-linux-x64-musl": "4.60.2", "@rollup/rollup-openbsd-x64": "4.60.2", "@rollup/rollup-openharmony-arm64": "4.60.2", "@rollup/rollup-win32-arm64-msvc": "4.60.2", "@rollup/rollup-win32-ia32-msvc": "4.60.2", "@rollup/rollup-win32-x64-gnu": "4.60.2", "@rollup/rollup-win32-x64-msvc": "4.60.2", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-J9qZyW++QK/09NyN/zeO0dG/1GdGfyp9lV8ajHnRVLfo/uFsbji5mHnDgn/qYdUHyCkM2N+8VyspgZclfAh0eQ=="],
|
||||||
|
|
||||||
|
"safe-stable-stringify": ["safe-stable-stringify@2.5.0", "", {}, "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA=="],
|
||||||
|
|
||||||
|
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
|
||||||
|
|
||||||
|
"scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
|
||||||
|
|
||||||
|
"semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
|
||||||
|
|
||||||
|
"set-cookie-parser": ["set-cookie-parser@3.1.0", "", {}, "sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw=="],
|
||||||
|
|
||||||
|
"setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="],
|
||||||
|
|
||||||
|
"sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.5", "@img/sharp-darwin-x64": "0.34.5", "@img/sharp-libvips-darwin-arm64": "1.2.4", "@img/sharp-libvips-darwin-x64": "1.2.4", "@img/sharp-libvips-linux-arm": "1.2.4", "@img/sharp-libvips-linux-arm64": "1.2.4", "@img/sharp-libvips-linux-ppc64": "1.2.4", "@img/sharp-libvips-linux-riscv64": "1.2.4", "@img/sharp-libvips-linux-s390x": "1.2.4", "@img/sharp-libvips-linux-x64": "1.2.4", "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", "@img/sharp-libvips-linuxmusl-x64": "1.2.4", "@img/sharp-linux-arm": "0.34.5", "@img/sharp-linux-arm64": "0.34.5", "@img/sharp-linux-ppc64": "0.34.5", "@img/sharp-linux-riscv64": "0.34.5", "@img/sharp-linux-s390x": "0.34.5", "@img/sharp-linux-x64": "0.34.5", "@img/sharp-linuxmusl-arm64": "0.34.5", "@img/sharp-linuxmusl-x64": "0.34.5", "@img/sharp-wasm32": "0.34.5", "@img/sharp-win32-arm64": "0.34.5", "@img/sharp-win32-ia32": "0.34.5", "@img/sharp-win32-x64": "0.34.5" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="],
|
||||||
|
|
||||||
|
"siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="],
|
||||||
|
|
||||||
|
"signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
||||||
|
|
||||||
|
"sonic-boom": ["sonic-boom@4.2.1", "", { "dependencies": { "atomic-sleep": "^1.0.0" } }, "sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q=="],
|
||||||
|
|
||||||
|
"source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
||||||
|
|
||||||
|
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
||||||
|
|
||||||
|
"source-map-support": ["source-map-support@0.5.21", "", { "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w=="],
|
||||||
|
|
||||||
|
"split2": ["split2@4.2.0", "", {}, "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="],
|
||||||
|
|
||||||
|
"stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="],
|
||||||
|
|
||||||
|
"standard-as-callback": ["standard-as-callback@2.1.0", "", {}, "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A=="],
|
||||||
|
|
||||||
|
"statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="],
|
||||||
|
|
||||||
|
"std-env": ["std-env@3.10.0", "", {}, "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg=="],
|
||||||
|
|
||||||
|
"strict-event-emitter": ["strict-event-emitter@0.5.1", "", {}, "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ=="],
|
||||||
|
|
||||||
|
"string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||||
|
|
||||||
|
"strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||||
|
|
||||||
|
"styled-jsx": ["styled-jsx@5.1.6", "", { "dependencies": { "client-only": "0.0.1" }, "peerDependencies": { "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" } }, "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA=="],
|
||||||
|
|
||||||
|
"tagged-tag": ["tagged-tag@1.0.0", "", {}, "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng=="],
|
||||||
|
|
||||||
|
"thread-stream": ["thread-stream@3.1.0", "", { "dependencies": { "real-require": "^0.2.0" } }, "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A=="],
|
||||||
|
|
||||||
|
"tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="],
|
||||||
|
|
||||||
|
"tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="],
|
||||||
|
|
||||||
|
"tinypool": ["tinypool@1.1.1", "", {}, "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg=="],
|
||||||
|
|
||||||
|
"tinyrainbow": ["tinyrainbow@1.2.0", "", {}, "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ=="],
|
||||||
|
|
||||||
|
"tinyspy": ["tinyspy@3.0.2", "", {}, "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q=="],
|
||||||
|
|
||||||
|
"tldts": ["tldts@7.0.28", "", { "dependencies": { "tldts-core": "^7.0.28" }, "bin": { "tldts": "bin/cli.js" } }, "sha512-+Zg3vWhRUv8B1maGSTFdev9mjoo8Etn2Ayfs4cnjlD3CsGkxXX4QyW3j2WJ0wdjYcYmy7Lx2RDsZMhgCWafKIw=="],
|
||||||
|
|
||||||
|
"tldts-core": ["tldts-core@7.0.28", "", {}, "sha512-7W5Efjhsc3chVdFhqtaU0KtK32J37Zcr9RKtID54nG+tIpcY79CQK/veYPODxtD/LJ4Lue66jvrQzIX2Z2/pUQ=="],
|
||||||
|
|
||||||
|
"toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
|
||||||
|
|
||||||
|
"tough-cookie": ["tough-cookie@6.0.1", "", { "dependencies": { "tldts": "^7.0.5" } }, "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw=="],
|
||||||
|
|
||||||
|
"trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="],
|
||||||
|
|
||||||
|
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||||
|
|
||||||
|
"turbo": ["turbo@2.9.6", "", { "optionalDependencies": { "@turbo/darwin-64": "2.9.6", "@turbo/darwin-arm64": "2.9.6", "@turbo/linux-64": "2.9.6", "@turbo/linux-arm64": "2.9.6", "@turbo/windows-64": "2.9.6", "@turbo/windows-arm64": "2.9.6" }, "bin": { "turbo": "bin/turbo" } }, "sha512-+v2QJey7ZUeUiuigkU+uFfklvNUyPI2VO2vBpMYJA+a1hKFLFiKtUYlRHdb3P9CrAvMzi0upbjI4WT+zKtqkBg=="],
|
||||||
|
|
||||||
|
"type-fest": ["type-fest@5.6.0", "", { "dependencies": { "tagged-tag": "^1.0.0" } }, "sha512-8ZiHFm91orbSAe2PSAiSVBVko18pbhbiB3U9GglSzF/zCGkR+rxpHx6sEMCUm4kxY4LjDIUGgCfUMtwfZfjfUA=="],
|
||||||
|
|
||||||
|
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||||
|
|
||||||
|
"undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
|
||||||
|
|
||||||
|
"unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="],
|
||||||
|
|
||||||
|
"unist-util-is": ["unist-util-is@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g=="],
|
||||||
|
|
||||||
|
"unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="],
|
||||||
|
|
||||||
|
"unist-util-visit": ["unist-util-visit@5.1.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg=="],
|
||||||
|
|
||||||
|
"unist-util-visit-parents": ["unist-util-visit-parents@6.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ=="],
|
||||||
|
|
||||||
|
"unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
|
||||||
|
|
||||||
|
"until-async": ["until-async@3.0.2", "", {}, "sha512-IiSk4HlzAMqTUseHHe3VhIGyuFmN90zMTpD3Z3y8jeQbzLIq500MVM7Jq2vUAnTKAFPJrqwkzr6PoTcPhGcOiw=="],
|
||||||
|
|
||||||
|
"uuid": ["uuid@11.1.0", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="],
|
||||||
|
|
||||||
|
"vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="],
|
||||||
|
|
||||||
|
"vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="],
|
||||||
|
|
||||||
|
"vite": ["vite@5.4.21", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw=="],
|
||||||
|
|
||||||
|
"vite-node": ["vite-node@2.1.9", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.3.7", "es-module-lexer": "^1.5.4", "pathe": "^1.1.2", "vite": "^5.0.0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA=="],
|
||||||
|
|
||||||
|
"vitest": ["vitest@2.1.9", "", { "dependencies": { "@vitest/expect": "2.1.9", "@vitest/mocker": "2.1.9", "@vitest/pretty-format": "^2.1.9", "@vitest/runner": "2.1.9", "@vitest/snapshot": "2.1.9", "@vitest/spy": "2.1.9", "@vitest/utils": "2.1.9", "chai": "^5.1.2", "debug": "^4.3.7", "expect-type": "^1.1.0", "magic-string": "^0.30.12", "pathe": "^1.1.2", "std-env": "^3.8.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.1", "tinypool": "^1.0.1", "tinyrainbow": "^1.2.0", "vite": "^5.0.0", "vite-node": "2.1.9", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", "@vitest/browser": "2.1.9", "@vitest/ui": "2.1.9", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q=="],
|
||||||
|
|
||||||
|
"why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="],
|
||||||
|
|
||||||
|
"wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
|
||||||
|
|
||||||
|
"y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="],
|
||||||
|
|
||||||
|
"yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="],
|
||||||
|
|
||||||
|
"yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
|
||||||
|
|
||||||
|
"zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
||||||
|
|
||||||
|
"zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="],
|
||||||
|
|
||||||
|
"@mswjs/interceptors/@open-draft/deferred-promise": ["@open-draft/deferred-promise@2.2.0", "", {}, "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA=="],
|
||||||
|
|
||||||
|
"rollup/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
||||||
|
|
||||||
|
"vite/esbuild": ["esbuild@0.21.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="],
|
||||||
|
|
||||||
|
"vite/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
||||||
|
|
||||||
|
"vite/postcss": ["postcss@8.5.10", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.18.20", "", { "os": "android", "cpu": "arm" }, "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.18.20", "", { "os": "android", "cpu": "arm64" }, "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.18.20", "", { "os": "android", "cpu": "x64" }, "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.18.20", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.18.20", "", { "os": "darwin", "cpu": "x64" }, "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.18.20", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.18.20", "", { "os": "freebsd", "cpu": "x64" }, "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.18.20", "", { "os": "linux", "cpu": "arm" }, "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.18.20", "", { "os": "linux", "cpu": "arm64" }, "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.18.20", "", { "os": "linux", "cpu": "ia32" }, "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.18.20", "", { "os": "linux", "cpu": "ppc64" }, "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.18.20", "", { "os": "linux", "cpu": "s390x" }, "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.18.20", "", { "os": "linux", "cpu": "x64" }, "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.18.20", "", { "os": "none", "cpu": "x64" }, "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.18.20", "", { "os": "openbsd", "cpu": "x64" }, "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.18.20", "", { "os": "sunos", "cpu": "x64" }, "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.18.20", "", { "os": "win32", "cpu": "arm64" }, "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.18.20", "", { "os": "win32", "cpu": "ia32" }, "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g=="],
|
||||||
|
|
||||||
|
"@esbuild-kit/core-utils/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.18.20", "", { "os": "win32", "cpu": "x64" }, "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ=="],
|
||||||
|
|
||||||
|
"vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="],
|
||||||
|
|
||||||
|
"vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.21.5", "", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="],
|
||||||
|
|
||||||
|
"vite/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.21.5", "", { "os": "android", "cpu": "arm64" }, "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A=="],
|
||||||
|
|
||||||
|
"vite/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.21.5", "", { "os": "android", "cpu": "x64" }, "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA=="],
|
||||||
|
|
||||||
|
"vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.21.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ=="],
|
||||||
|
|
||||||
|
"vite/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.21.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw=="],
|
||||||
|
|
||||||
|
"vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.21.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g=="],
|
||||||
|
|
||||||
|
"vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.21.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ=="],
|
||||||
|
|
||||||
|
"vite/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.21.5", "", { "os": "linux", "cpu": "arm" }, "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA=="],
|
||||||
|
|
||||||
|
"vite/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.21.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q=="],
|
||||||
|
|
||||||
|
"vite/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.21.5", "", { "os": "linux", "cpu": "ia32" }, "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg=="],
|
||||||
|
|
||||||
|
"vite/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg=="],
|
||||||
|
|
||||||
|
"vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg=="],
|
||||||
|
|
||||||
|
"vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.21.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w=="],
|
||||||
|
|
||||||
|
"vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA=="],
|
||||||
|
|
||||||
|
"vite/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.21.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A=="],
|
||||||
|
|
||||||
|
"vite/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.21.5", "", { "os": "linux", "cpu": "x64" }, "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ=="],
|
||||||
|
|
||||||
|
"vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.21.5", "", { "os": "none", "cpu": "x64" }, "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg=="],
|
||||||
|
|
||||||
|
"vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.21.5", "", { "os": "openbsd", "cpu": "x64" }, "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow=="],
|
||||||
|
|
||||||
|
"vite/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.21.5", "", { "os": "sunos", "cpu": "x64" }, "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg=="],
|
||||||
|
|
||||||
|
"vite/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.21.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A=="],
|
||||||
|
|
||||||
|
"vite/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.21.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA=="],
|
||||||
|
|
||||||
|
"vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="],
|
||||||
|
}
|
||||||
|
}
|
||||||
38
db/migrations/README.md
Normal file
38
db/migrations/README.md
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# Database migrations — runbook
|
||||||
|
|
||||||
|
**Authoring rule:** every schema change follows **expand-then-contract** sequencing. A breaking change is split into ≥2 migrations with a rollback point between them.
|
||||||
|
|
||||||
|
## Pattern
|
||||||
|
|
||||||
|
| Phase | Migration files | What it does | Rollback |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 1. Expand | `NNNN_expand_<change>.sql` | Add new nullable column / new table / new index. Old code keeps working. | Drop the new addition. |
|
||||||
|
| 2. Backfill | `NNNN_backfill_<change>.sql` (if data migration needed) | Populate the new column from existing data. Idempotent. | Re-run or delete column. |
|
||||||
|
| 3. Constrain | `NNNN_constrain_<change>.sql` | Add NOT NULL / UNIQUE / FK / CHECK now that data is populated. | Drop the constraint. |
|
||||||
|
| 4. Contract | `NNNN_contract_<change>.sql` | Remove the old column / table once no code reads it. **Only after ≥1 deploy cycle of the new code.** | Restore from `pg_dump`. |
|
||||||
|
|
||||||
|
## Mandatory for every migration
|
||||||
|
|
||||||
|
- `migrations/NNNN_<name>.sql` — the forward migration.
|
||||||
|
- `migrations/NNNN_<name>.down.sql` — the rollback SQL.
|
||||||
|
- An entry below in the **Registry** with rationale, linked plan, and rollback cost estimate.
|
||||||
|
|
||||||
|
## Registry
|
||||||
|
|
||||||
|
| # | Date | Name | Plan | Forward cost | Rollback cost | Notes |
|
||||||
|
|---|---|---|---|---|---|---|
|
||||||
|
| — | — | — | — | — | — | No migrations yet — first migration lands in Stage 2.1 |
|
||||||
|
|
||||||
|
## Rehearsal
|
||||||
|
|
||||||
|
Stage 2.2 DoD: every schema change is rehearsed end-to-end on staging before production. Command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun run db:migrate:rehearse
|
||||||
|
```
|
||||||
|
|
||||||
|
Rehearsal must cover: forward → backfill (if applicable) → constrain → contract, with a row-count + row-hash check at each step.
|
||||||
|
|
||||||
|
## Incidents
|
||||||
|
|
||||||
|
Any migration incident gets a post-mortem in `docs/incidents/YYYY-MM-DD-migration-<name>.md`.
|
||||||
27
docs/deferred-gates.md
Normal file
27
docs/deferred-gates.md
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# Deferred gates
|
||||||
|
|
||||||
|
Items that could not be completed in the Stage 0 execution window and must be resolved before the gate they block.
|
||||||
|
|
||||||
|
## Gate 0.8 — Postgres + Redis on PaaS
|
||||||
|
|
||||||
|
**Status:** Deferred 2026-04-19.
|
||||||
|
**Reason:** Dokploy MCP tools (`mcp__dokploy__*`) were not loaded in the Stage 0 session, and `DOKPLOY_API_KEY` was not in the WSL environment. Direct API query not possible in-session.
|
||||||
|
**Blocks:** Stage 2 (database + API) — the scheduler and admin cannot run without a Postgres + Redis pair.
|
||||||
|
**Resolution options:**
|
||||||
|
1. **Recommended:** Restart Claude Code with the vault's `.mcp.json` Dokploy MCP attached; run `mcp__dokploy__list_applications` / equivalent against `sg-paas-s1.stargue.net` to verify or provision.
|
||||||
|
2. Angelo confirms via Dokploy web UI and reports service names + connection hostnames.
|
||||||
|
3. Provision fresh Postgres + Redis services in the `stargue-publishing-engine` Dokploy stack (clean room; isolates blast radius from other PaaS apps).
|
||||||
|
|
||||||
|
**Acceptance when resolved:** this file updated with service names, hostnames, and connection-string env-var locations (Dokploy secrets).
|
||||||
|
|
||||||
|
## Gate 0.9 — LinkedIn Developer Portal apps
|
||||||
|
|
||||||
|
**Status:** Pending Angelo's manual action.
|
||||||
|
**Reason:** Dev Portal registration cannot be automated — requires human auth at `linkedin.com/developers/`.
|
||||||
|
**Blocks:** Stage 3.1 (OAuth flow) live integration — structure + contract tests can proceed in parallel.
|
||||||
|
**Resolution:** follow the checklist in [`linkedin-apps.md`](./linkedin-apps.md). Expected time ~30 min for OIDC + Share; multi-week for Community Management API + MDP partner approvals.
|
||||||
|
**Acceptance when resolved:** `linkedin-apps.md` registry filled in with app IDs; client credentials stored in Dokploy secrets `LINKEDIN_CLIENT_ID`, `LINKEDIN_CLIENT_SECRET`, `LINKEDIN_TOKEN_ENCRYPTION_KEY`.
|
||||||
|
|
||||||
|
## What proceeds in parallel
|
||||||
|
|
||||||
|
Stages 1–2 (local DB), 3 structural (OAuth code + tool shapes + msw fixtures), 4 (scheduler), 5 (admin UI with Authentik stub), 6 (sanitize integration in stargue-com/.net), 7.1 (`/market audit` baseline), 7.2 (7 Cs post drafting) can all proceed without these gates. Live integration tests and first production publish require both gates resolved.
|
||||||
46
docs/linkedin-apps.md
Normal file
46
docs/linkedin-apps.md
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
# LinkedIn Developer Portal — Application Registry
|
||||||
|
|
||||||
|
**Status tracker for Stargue's LinkedIn apps.** Every row must stay current — Claude Code verifies against this file during Stage 0 DoD 0.9.
|
||||||
|
|
||||||
|
## Applications
|
||||||
|
|
||||||
|
| App | Product | Purpose | Scopes granted | State | App ID | Approval date | Notes |
|
||||||
|
|---|---|---|---|---|---|---|---|
|
||||||
|
| Stargue Publishing Engine — Sign In | Sign In with LinkedIn using OpenID Connect | Identity / `whoami` | `openid profile email` | **TODO: register** | — | — | Standard — no approval required beyond Dev Portal self-service |
|
||||||
|
| Stargue Publishing Engine — Member Share | Share on LinkedIn | Personal-profile posts (Angelo) | `w_member_social` | **TODO: register** | — | — | Standard — attached to the same app as Sign In |
|
||||||
|
| Stargue Publishing Engine — Org Share | Community Management API | Company-page posts (`urn:li:organization:2605890`) | `w_organization_social`, `r_organization_social` | **TODO: apply** | — | — | Requires LinkedIn partner approval; submit via Dev Portal "Request access" |
|
||||||
|
| Stargue Publishing Engine — Refresh | Marketing Developer Platform (MDP) | Programmatic refresh tokens (eliminates 60-day re-auth) | (extends above) | **TODO: apply** | — | — | Requires LinkedIn partner approval; multi-week review |
|
||||||
|
|
||||||
|
## OAuth redirect URIs
|
||||||
|
|
||||||
|
- Production: `https://publishing.stargue.net/auth/linkedin/callback`
|
||||||
|
- Local dev: `http://localhost:3002/auth/linkedin/callback`
|
||||||
|
|
||||||
|
## Client credentials
|
||||||
|
|
||||||
|
Stored in Dokploy secrets on `sg-paas-s1.stargue.net`:
|
||||||
|
- `LINKEDIN_CLIENT_ID`
|
||||||
|
- `LINKEDIN_CLIENT_SECRET`
|
||||||
|
- `LINKEDIN_TOKEN_ENCRYPTION_KEY` (32-byte key for `crypto_secretbox`)
|
||||||
|
|
||||||
|
## Manual setup checklist (Stage 0.9)
|
||||||
|
|
||||||
|
1. Sign in to `https://www.linkedin.com/developers/` as Angelo (personal account).
|
||||||
|
2. Create app "Stargue Publishing Engine" linked to Stargue Company Page `urn:li:organization:2605890` (required for Community Management API eligibility).
|
||||||
|
3. Under **Products**, request:
|
||||||
|
- Sign In with LinkedIn using OpenID Connect (self-service; instant)
|
||||||
|
- Share on LinkedIn (self-service; instant)
|
||||||
|
- Community Management API (submit partner-access request — fill in use-case: "Programmatic posting of original content to Stargue Company Page for knowledge-management-as-a-service practice")
|
||||||
|
- Marketing Developer Platform (separate partner-access request — fill in use-case: "Automate posting and engagement-metric collection for owned content from a self-hosted publishing pipeline")
|
||||||
|
4. Under **Auth**, add redirect URIs above.
|
||||||
|
5. Copy Client ID and Client Secret to Dokploy secrets (never commit).
|
||||||
|
6. Update this file with the app ID.
|
||||||
|
7. Re-request Community Management API + MDP if denied initially, with more detailed use-case.
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [LinkedIn Developer Portal](https://www.linkedin.com/developers/)
|
||||||
|
- [Posts API](https://learn.microsoft.com/en-us/linkedin/marketing/community-management/shares/posts-api)
|
||||||
|
- [3-Legged OAuth Flow](https://learn.microsoft.com/en-us/linkedin/shared/authentication/authorization-code-flow)
|
||||||
|
- [Programmatic Refresh Tokens](https://learn.microsoft.com/en-us/linkedin/shared/authentication/programmatic-refresh-tokens)
|
||||||
|
- [Community Management API overview](https://learn.microsoft.com/en-us/linkedin/marketing/community-management/)
|
||||||
397
docs/plans/2026-04-19-phase1-plan.md
Executable file
397
docs/plans/2026-04-19-phase1-plan.md
Executable file
@@ -0,0 +1,397 @@
|
|||||||
|
---
|
||||||
|
created: 2026-04-19T09:45
|
||||||
|
updated: 2026-04-19T10:45
|
||||||
|
tags:
|
||||||
|
---
|
||||||
|
|
||||||
|
# Plan — Phase 1 Automated Publishing Engine (with LinkedIn MCP Server)
|
||||||
|
|
||||||
|
**Revision:** v2 (consolidated from 7-agent BMAD panel review 2026-04-19)
|
||||||
|
**Verdicts file:** `/tmp/bmad-panel-verdicts-publishing-engine-phase1-20260419.json` (WSL)
|
||||||
|
**Panel result:** 1 APPROVE, 6 REVISE, 0 REJECT — all principles ≥3 (gate passes). Revisions below address each REVISE gap, citing originating agent.
|
||||||
|
|
||||||
|
**Applicability:** tier-2 = [RBR, A11y, Testability]
|
||||||
|
**Precedence:** `CLAUDE.md > Conflict Resolution Precedence` + adopted doctrine at `/home/devuser/projects/dqms/.clinerules/12-foundational-principles.md` (user directive 2026-04-19)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Context
|
||||||
|
|
||||||
|
User directive 2026-04-19: **Full Phase 1 automation, now.** No manual posting, no postponing. LinkedIn programmatic refresh is gated to MDP partners per [Microsoft Learn > Programmatic Refresh Tokens (retrieved 2026-04-19)](https://learn.microsoft.com/en-us/linkedin/shared/authentication/programmatic-refresh-tokens). Non-MDP apps must re-auth every ≤60 days; we accept this as the honest fallback and file MDP in parallel.
|
||||||
|
|
||||||
|
Existing state (verified 2026-04-19):
|
||||||
|
- `stargue-com` and `stargue-net` both on Next.js 16 + React 19 + Bun + Tailwind v4 + remark (verified against `package.json` per **dev** agent). Note: **stargue-net/package.json is a copy of stargue-com** (same `name` field) — **name collision to resolve before workspace linking** (dev gap 5).
|
||||||
|
- Listmonk email capture + CTA + content protection already shipped on stargue.com.
|
||||||
|
- Dokploy on `sg-paas-s1.stargue.net`; Gitea at `git.stargue.net`; OpenObserve observability stack.
|
||||||
|
- Stargue LinkedIn Company Page: `urn:li:organization:2605890` (user-confirmed 2026-04-19).
|
||||||
|
- Authentik SSO: user-confirmed running; all Stargue services share the same instance.
|
||||||
|
|
||||||
|
**Capacity reconciliation (PM/SM gap).** PRD §6.5 caps Angelo at 4h/week. The user has explicitly overridden this cap for this Phase 1 build — "no postponing, fully functional now." Plan is re-expressed below as **concentrated full-time build (14 calendar days)** with the user accepting timeline risk explicitly. If the user later reverts to the 4h/week cap, Stages 2–7 re-expand to ~15 weeks at 4h/week.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Objectives
|
||||||
|
|
||||||
|
1. **Automated two-channel publish pipeline** — Obsidian vault → stargue.com/.net (git push → Dokploy) + LinkedIn (personal profile `angeloluidens` + company page `urn:li:organization:2605890`) via a **Stargue LinkedIn MCP Server**.
|
||||||
|
2. **Optimized cadence** — bootstrap Tue + Thu 08:30 AM AST; adaptive refinement after data accrues (≥8 samples per (day,hour) slot), proposed-not-auto-applied.
|
||||||
|
3. **Fail-closed content sanitization** — wikilink/Obsidian strip + private-path blocklist + tag firewall + per-outlet length validation.
|
||||||
|
4. **Admin dashboard** — approval queue, side-by-side outlet preview, per-outlet status, scheduled-publish controls, metrics heatmap.
|
||||||
|
5. **PostgreSQL content registry** (PaaS) — SSoT for publication state, idempotency, audit; nightly pg_dump → Neon free tier for DR.
|
||||||
|
6. **OAuth 2.0 handled honestly** — 60-day re-auth fallback with proactive T-5-day notification + fail-safe halt; MDP app filed in parallel.
|
||||||
|
7. **Revenue conversion preserved** (PM gap). Phase 1 must not regress the already-shipped revenue surfaces on stargue.com (Listmonk email capture, CTAs, service page, Terms of Use). New inbound-inquiry tracking column on `publications.metadata_jsonb.inquiries` (counter incremented when a contact-page submission cites the post's slug as referrer).
|
||||||
|
8. **Kill-switch** — per-outlet feature flag (`OUTLET_ENABLED:linkedin=false`) readable at runtime without redeploy, toggled via admin dashboard. TEA gap 10.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Architecture
|
||||||
|
|
||||||
|
### 3.1 Repository topology (simplified per **architect** gap 1)
|
||||||
|
|
||||||
|
Collapsed `apps/api` into `apps/admin` Next.js App Router route handlers. Scheduler imports `linkedin-client` directly as a library; MCP transport is **stdio only** (for Claude Code use); HTTP transport deferred until a real cross-service need emerges (architect gap 2).
|
||||||
|
|
||||||
|
```
|
||||||
|
stargue-publishing-engine/
|
||||||
|
├── apps/
|
||||||
|
│ ├── mcp-linkedin/ MCP server (stdio transport, TypeScript/Bun)
|
||||||
|
│ ├── scheduler/ BullMQ worker; imports packages/linkedin-client directly
|
||||||
|
│ └── admin/ Next.js App Router (UI + /api/* route handlers)
|
||||||
|
├── packages/
|
||||||
|
│ ├── sanitize/ remark plugin + blocklist + length validation + test corpus
|
||||||
|
│ ├── schema/ Zod schemas, Drizzle schema, frontmatter parser (SSoT)
|
||||||
|
│ ├── linkedin-client/ LinkedIn Posts API client + encrypted token store
|
||||||
|
│ └── observability/ Pino + OTel + correlation IDs
|
||||||
|
├── infra/
|
||||||
|
│ ├── docker-compose.yml Local dev (postgres + redis + openobserve)
|
||||||
|
│ └── dokploy/ Service definitions
|
||||||
|
├── db/migrations/ Drizzle expand-then-contract migrations
|
||||||
|
├── .clinerules/ Adopted from dqms
|
||||||
|
├── .claude/
|
||||||
|
│ ├── hooks/gate-plan-exit.sh
|
||||||
|
│ └── skills/bmad-plan/SKILL.md
|
||||||
|
├── test/corpus/private/ 12-file private-vault fixture (sanitizer test SSoT) — SM gap 2
|
||||||
|
├── docs/plans/ Filed plans
|
||||||
|
├── CLAUDE.md
|
||||||
|
├── package.json Bun workspaces
|
||||||
|
└── turbo.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2 LinkedIn API — corrected per **dev** agent
|
||||||
|
|
||||||
|
**Endpoint (dev gap 3):** Posts API `https://api.linkedin.com/rest/posts` — NOT legacy `/v2/ugcPosts`. Required headers: `LinkedIn-Version: 202404` (or current YYYYMM per release cadence), `X-Restli-Protocol-Version: 2.0.0`, `Authorization: Bearer <access_token>`. Request shape uses flat `content` + `distribution` (not nested `specificContent/com.linkedin.ugc.ShareContent`).
|
||||||
|
|
||||||
|
**Auth flow (dev gap 2):** OAuth 2.0 Authorization Code flow — **no PKCE** (LinkedIn 3LO does not document PKCE support per [Microsoft Learn > 3-Legged OAuth Flow](https://learn.microsoft.com/en-us/linkedin/shared/authentication/authorization-code-flow), retrieved 2026-04-19). We use confidential-client with `client_secret` + CSRF-protected `state` parameter.
|
||||||
|
|
||||||
|
**Products required in LinkedIn Dev Portal (dev gap 1):**
|
||||||
|
| Product | Scopes granted | Purpose | Approval gate |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **Sign In with LinkedIn using OpenID Connect** | `openid profile email` | Identity | Standard |
|
||||||
|
| **Share on LinkedIn** | `w_member_social` | Personal-profile posts | Standard |
|
||||||
|
| **Community Management API** | `w_organization_social`, `r_organization_social` | Company-page posts | **Requires LinkedIn partner approval — separate from MDP, non-trivial gate** |
|
||||||
|
| **Marketing Developer Platform (MDP)** — aspirational | Programmatic refresh tokens | Eliminate 60-day re-auth | Multi-week review |
|
||||||
|
|
||||||
|
**Impact:** Company Page posting (Objective 1, personal + `org:2605890`) may be blocked pending Community Management API approval. Personal-profile posting unblocked immediately. We file Community Management API application in Stage 0.5 and build the tools so they're ready when approved.
|
||||||
|
|
||||||
|
**Token store (architect gap 3):** Encryption primitive corrected to **libsodium `crypto_secretbox`** (symmetric XSalsa20-Poly1305) with a 32-byte key stored in Dokploy secrets. Sealed-box was wrong for at-rest DB encryption. Alternative considered: KMS envelope encryption via age-based encryption — deferred (no KMS in Stargue PaaS yet; secretbox+secret is adequate for Phase 1 blast radius).
|
||||||
|
|
||||||
|
**Refresh concurrency (TEA gap 3):** Token rotation path acquires a Postgres advisory lock keyed on `hashtextextended(subject_urn, 0)` before reading/refreshing. Lock released after commit. Prevents double-rotation race.
|
||||||
|
|
||||||
|
**Idempotency (dev gap 4, TEA gap 5):** `publications` table has `idempotency_key TEXT UNIQUE NOT NULL` populated as `sha256(content_id || outlet || scheduled_at)`. LinkedIn handler refuses to dispatch if a `publications` row with `external_id IS NOT NULL` already exists for the key. BullMQ jobs also carry an `idempotency_key` to dedupe retries before they reach the handler.
|
||||||
|
|
||||||
|
**Rate limits (architect gap 4).** LinkedIn does not publicly document a fixed per-member posting cap. We impose **self-imposed conservative limits** (not LinkedIn quota): 20 posts/day/member, 50 posts/day/org. Stage 3 DoD asserts this via a test (TEA gap 6).
|
||||||
|
|
||||||
|
**MCP tools exposed (stdio transport, no HTTP):**
|
||||||
|
| Tool | Purpose |
|
||||||
|
|---|---|
|
||||||
|
| `linkedin_whoami` | Returns authenticated subject + scopes + MDP-eligible flag |
|
||||||
|
| `linkedin_auth_status` | Access-expiry remaining, refresh-expiry remaining, kill-switch state |
|
||||||
|
| `linkedin_create_post` | Text post via Posts API (`author_urn` param: `urn:li:person:<id>` or `urn:li:organization:2605890`) |
|
||||||
|
| `linkedin_create_article` | Long-form article (≤125k chars) |
|
||||||
|
| `linkedin_upload_media` | Image / PDF carousel; returns asset URN |
|
||||||
|
| `linkedin_create_post_with_media` | Combines upload + post |
|
||||||
|
| `linkedin_delete_post` | Delete post by URN (soft-reversible within 5-min edit window only; after that, delete-only) |
|
||||||
|
| `linkedin_get_post_metrics` | Impressions/reactions/comments/shares/clicks for a URN |
|
||||||
|
| `linkedin_get_profile_stats` | Aggregate profile stats for cadence optimizer |
|
||||||
|
|
||||||
|
### 3.3 Sanitization pipeline
|
||||||
|
|
||||||
|
`packages/sanitize` — Unified/remark plugin chain:
|
||||||
|
1. Strip wikilinks `[[Path/Note|Display]]` → `Display` or plain removal.
|
||||||
|
2. Strip embeds `![[file]]` → resolve (blog) or remove (LinkedIn).
|
||||||
|
3. Strip dataview blocks.
|
||||||
|
4. Strip callouts.
|
||||||
|
5. **Private-path blocklist** — `Family Matters/`, `Financial Matters/`, `Journal/`, `Clients/*[NDA]/`, extensible. Match triggers **build error**, not silent strip.
|
||||||
|
6. **Tag firewall** — `#private`, `#heal-internal`, `#confidential` trigger build error.
|
||||||
|
7. Per-outlet length validation (LinkedIn 3k / article 125k / Twitter 280 / etc.).
|
||||||
|
8. Idempotency hash output (`content_hash`) over sanitized body for dedupe.
|
||||||
|
|
||||||
|
**Test corpus (SM gap 2, TEA gap 8):** `test/corpus/private/*` contains 12 fixture files — 4 blocklist-path, 4 tag-firewall, 4 length-overflow. Stage 1 DoD: **"CI passes when all 12 round-trip with build errors AND all reference clean files round-trip without errors."** Promoted from Risks to Stage 1 DoD.
|
||||||
|
|
||||||
|
### 3.4 Content registry (Postgres) + DR
|
||||||
|
|
||||||
|
Drizzle schema in `packages/schema/db.ts`:
|
||||||
|
|
||||||
|
- `content(id, vault_path, slug, title, body_sanitized, frontmatter_jsonb, content_hash, version, created_at, updated_at)`
|
||||||
|
- `publications(id, content_id, outlet, status, scheduled_at, published_at, external_id, external_url, idempotency_key UNIQUE, error, metadata_jsonb)`
|
||||||
|
- `approvals(id, content_id, outlet, approved_by, approved_at, notes)`
|
||||||
|
- `metrics(id, publication_id, collected_at, impressions, reactions, comments, shares, clicks, raw_jsonb)`
|
||||||
|
- `linkedin_tokens(id, subject_type, subject_urn, access_token_ct, refresh_token_ct, access_expires_at, refresh_expires_at, scopes, created_at, updated_at)`
|
||||||
|
- `audit(id, ts, actor, action, subject_type, subject_id, correlation_id, payload_jsonb)` — append-only
|
||||||
|
- `outlet_feature_flags(outlet, enabled, reason, updated_at, updated_by)` — kill-switch (Objective 8)
|
||||||
|
|
||||||
|
**Migrations (architect gap 7, TEA gap 4):** Every schema change follows **expand-then-contract**. Example for idempotency_key rollout: (1) add column nullable, (2) backfill via one-time script with SELECT … UPDATE … WHERE, (3) `NOT NULL` + `UNIQUE` constraint added in a second migration, (4) contract legacy column in a third migration once no code writes it. Each migration accompanied by a runbook in `db/migrations/README.md`. **Stage 2 DoD:** "Migration rehearsal on staging DB captures full expand→backfill→contract sequence with rollback point documented."
|
||||||
|
|
||||||
|
**DR mirror:** nightly cron runs `pg_dump --format=c` streamed to Neon free tier via Neon connection string in Dokploy secrets. Retains only last 30 days of metrics on the mirror (respects Neon 0.5 GB free cap). RPO ~24h.
|
||||||
|
|
||||||
|
### 3.5 Admin dashboard + A11y (UX gaps 1–8)
|
||||||
|
|
||||||
|
Next.js App Router. Authentik SSO (user-confirmed instance). Routes also host `/api/*` handlers (architect gap 1 collapse).
|
||||||
|
|
||||||
|
**Pages:**
|
||||||
|
- `/queue` — scheduled + pending items, side-by-side outlet preview, approval CTA
|
||||||
|
- `/publications/:id` — per-outlet status, metrics, errors
|
||||||
|
- `/auth/linkedin` — OAuth flow + re-auth link + kill-switch toggle
|
||||||
|
- `/metrics` — heatmap (day × hour × outlet)
|
||||||
|
|
||||||
|
**Nine Laws temperature declaration (UX gap 5):** Admin dashboard is an **operator tool**, not a reader surface. It inhabits the **cool / stargue.net (forge) temperature**. Forest-green (#4E8211) + Citrus (#99CC00) accents. **Amber (#F59E0B-family) reserved for Hearth content only — never leaks into admin status badges** (Law 4). Status badges use Forest/slate/red variants.
|
||||||
|
|
||||||
|
**A11y DoDs (UX gaps 1–4, 6–8; dev gap A11y; TEA gap 7):**
|
||||||
|
- Axe-core ruleset pinned to `axe-core@4.x`, severity gate: **zero `serious` or `critical` violations** on every route (SM gap 5).
|
||||||
|
- WCAG 2.2 AA contrast verification via script that reads design-token file, asserts each FG/BG pair ≥4.5:1 body and ≥3:1 large-text/non-text-UI on **both themes**.
|
||||||
|
- Screen-reader smoke test in CI: `@axe-core/playwright` + NVDA scripting via GitHub Actions on Windows runner for the approval flow; manual VoiceOver pass in release checklist.
|
||||||
|
- `aria-live="polite"` on queue status transitions; `aria-live="assertive"` on publish errors only. Announcement text specified per surface.
|
||||||
|
- Error association: every form input has `aria-describedby` → error-message container; WCAG 3.3.1/3.3.3/4.1.3 complied.
|
||||||
|
- Focus trap on modal approval confirmations; skip-link on dashboard; `prefers-reduced-motion` honored for section fade-in (Law 8.4).
|
||||||
|
- `/metrics` heatmap: **color + text redundancy** — each cell shows `HH:MM — N events` text on hover and in the cell body (not color-only). WCAG 1.4.1 satisfied.
|
||||||
|
|
||||||
|
### 3.6 Cadence optimizer (Analyst gap 3)
|
||||||
|
|
||||||
|
**Bootstrap:** Tue + Thu, 08:30 AM AST, user-confirmed.
|
||||||
|
|
||||||
|
**Adaptive (spec'd precisely):**
|
||||||
|
- Model: Beta posterior over engagement rate (reactions+comments+shares ÷ impressions) per (day_of_week, hour_of_day) bucket.
|
||||||
|
- Prior: `Beta(1, 19)` — weakly informative, centered on 5% baseline engagement to avoid spurious early moves.
|
||||||
|
- Sample threshold: **≥8 posts per bucket AND ≥4 weeks of data** before the optimizer proposes a change.
|
||||||
|
- Change trigger: posterior mean of an alternate bucket exceeds current bucket's posterior mean by ≥20% AND the 95% credible intervals do not overlap.
|
||||||
|
- Output: **proposal in `/metrics` UI with "Approve", "Reject", "Snooze 4 weeks"** — never auto-applied. Human-in-the-loop (RBR).
|
||||||
|
- Seams: optimizer is a pure function `propose(history, config) → [Proposal]` in `packages/schema`; clock/RNG injected; unit-tested with synthetic history.
|
||||||
|
|
||||||
|
### 3.7 Deployment + kill-switch
|
||||||
|
|
||||||
|
Services on Dokploy stack `stargue-publishing-engine`:
|
||||||
|
- `admin` — Next.js standalone, port 3002, Traefik `publishing.stargue.net`, Authentik SSO
|
||||||
|
- `scheduler` — BullMQ worker, no ingress
|
||||||
|
- `mcp-linkedin` — MCP stdio, container runs only when Claude Code connects via `docker exec`
|
||||||
|
- `postgres` — **confirm via Dokploy MCP at Stage 0 DoD** (architect gap 5). If no existing cluster, provision a new stack service.
|
||||||
|
- `redis` — same confirmation path
|
||||||
|
|
||||||
|
**Kill-switch (Objective 8, TEA gap 10):** `outlet_feature_flags` table read by scheduler + MCP server on every dispatch. Toggle via admin dashboard at `/admin/feature-flags`. Flip takes effect within 30s without redeploy. Audit-logged.
|
||||||
|
|
||||||
|
**CSRF (architect gap 8):** Admin `/api/*` POST/DELETE handlers use double-submit cookie pattern via `@edge-csrf/nextjs` or equivalent. Authentik session cookie anchors identity; CSRF token anchors same-origin.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Stages + measurable DoD (all "works" language tightened per SM/TEA gaps)
|
||||||
|
|
||||||
|
### Stage 0 — Governance + gate resolution (day 1)
|
||||||
|
|
||||||
|
| Step | Measurable DoD |
|
||||||
|
|---|---|
|
||||||
|
| 0.1 Create Gitea repo | `GET https://git.stargue.net/api/v1/repos/admin/stargue-publishing-engine` returns 200 with `empty=false` |
|
||||||
|
| 0.2 Install `.clinerules/` from dqms | `diff -q .clinerules/12-foundational-principles.md /home/devuser/projects/dqms/.clinerules/12-foundational-principles.md` reports no difference |
|
||||||
|
| 0.3 Install `.claude/hooks/gate-plan-exit.sh` | Hook file present with exec perms; `bash -n .claude/hooks/gate-plan-exit.sh` parses clean |
|
||||||
|
| 0.4 Install `.claude/skills/bmad-plan/SKILL.md` | File SHA256 matches dqms SKILL.md |
|
||||||
|
| 0.5 Project CLAUDE.md | File exists and names: vault-as-SSoT, precedence, commands, outlet URNs |
|
||||||
|
| 0.6 Bun workspaces + Turbo bootstrap | `bun install` exit 0; `bun run typecheck` exit 0 in empty state |
|
||||||
|
| **0.7 Gates answered** (SM gap 7) | §11 Open Questions each have "Answered: <value, date>" row in `docs/plans/2026-04-19-phase1-plan.md` |
|
||||||
|
| 0.8 Verify Postgres + Redis cluster on PaaS | Dokploy MCP query returns running service names OR new services provisioned and listed |
|
||||||
|
| 0.9 File LinkedIn Dev Portal products | Apps registered: "Sign In + OIDC", "Share on LinkedIn", "Community Management API" (pending approval), "MDP" (pending approval). Screenshot URLs recorded in `docs/linkedin-apps.md` |
|
||||||
|
| 0.10 Resolve stargue-net/package.json name collision (dev gap 5) | `stargue-net/package.json` has `"name": "stargue-net"` (distinct from stargue-com) |
|
||||||
|
|
||||||
|
### Stage 1 — Shared packages (days 2–3)
|
||||||
|
|
||||||
|
| Step | Measurable DoD |
|
||||||
|
|---|---|
|
||||||
|
| 1.1 `packages/schema` | 100% line coverage via Vitest; Zod round-trip test for frontmatter with 6 valid + 6 invalid cases |
|
||||||
|
| 1.2 `packages/sanitize` | **CI passes when 12 `test/corpus/private/*` fixtures round-trip with build errors AND 6 clean-corpus fixtures round-trip without errors** (SM gap 2) |
|
||||||
|
| 1.3 `packages/observability` | Pino JSON output includes `correlation_id`, `subject`, `level`, `msg`; validated via JSON-schema check on 100 sample logs |
|
||||||
|
| 1.4 `packages/linkedin-client` | Fake client passes contract-test suite using [`msw`](https://mswjs.io/) (chosen recorder, dev gap 7) against recorded Posts API fixtures |
|
||||||
|
|
||||||
|
### Stage 2 — Database + core API (days 3–4)
|
||||||
|
|
||||||
|
| Step | Measurable DoD |
|
||||||
|
|---|---|
|
||||||
|
| 2.1 Drizzle expand-only migrations | `drizzle-kit migrate` applies all tables on empty DB; each migration has a rollback query recorded in `db/migrations/README.md` |
|
||||||
|
| 2.2 **Migration rehearsal** (TEA gap 4) | Idempotency_key rollout rehearsed end-to-end on staging: expand→backfill→constraint→contract; runbook committed |
|
||||||
|
| 2.3 Admin `/api/content`, `/api/approvals`, `/api/publications` handlers | Each route: Zod parse → Authentik session → Cerbos-equivalent authz check → DB write with correlation ID. Vitest integration tests assert 401/403/200/422 for each route |
|
||||||
|
| 2.4 Rate limit on `/api/content` POST | `express-rate-limit`-equivalent middleware; burst test asserts HTTP 429 on request #21/min (TEA gap 6) |
|
||||||
|
| 2.5 CSRF middleware | Double-submit cookie test: cross-origin POST returns 403 |
|
||||||
|
|
||||||
|
### Stage 3 — LinkedIn MCP server (days 4–7)
|
||||||
|
|
||||||
|
| Step | Measurable DoD |
|
||||||
|
|---|---|
|
||||||
|
| 3.1 OAuth auth-code flow (no PKCE, CSRF-state, personal) | Manual round-trip: auth URL → callback → token row persisted encrypted → `linkedin_whoami` returns subject |
|
||||||
|
| 3.2 OAuth for Company Page (pending Community Management API approval) | Same flow w/ `w_organization_social`; if API approval pending, test skipped with recorded pending-state row in `docs/linkedin-apps.md` |
|
||||||
|
| 3.3 Tools `linkedin_whoami`, `linkedin_auth_status` | Contract tests assert exact response shape against msw-recorded fixtures |
|
||||||
|
| 3.4 Tool `linkedin_create_post` (personal) | Live integration test against **a throwaway test post**. DoD: "Post URN persisted to `publications.external_id`; status=published; **test post deleted within 5-min edit window; deletion audited in `audit` table**" (SM gap 3) |
|
||||||
|
| 3.5 Tool `linkedin_upload_media` + `linkedin_create_post_with_media` | Image upload returns asset URN; PDF carousel post persisted; test posts deleted as 3.4 |
|
||||||
|
| 3.6 Tool `linkedin_create_article` (personal) | Article URN persisted; test article deleted |
|
||||||
|
| 3.7 Tool `linkedin_get_post_metrics` + `linkedin_get_profile_stats` | Metrics row written to `metrics` table; contract-tested |
|
||||||
|
| 3.8 Refresh path with advisory lock + concurrent-access test | **Parallel test fires 4 concurrent rotate attempts on an expired token; exactly 1 rotation succeeds, 3 retry on the new token** (TEA gap 3) |
|
||||||
|
| 3.9 Fail-safe halt on auth expiry | Forced-expiry test: `outlet_feature_flags.linkedin.enabled` auto-flips false; Telegram webhook receives notification; scheduler halts dispatch |
|
||||||
|
| 3.10 Kill-switch toggle via admin | Manual test: flip via UI → flag updated within 30s → next dispatch attempt refuses with audit row (TEA gap 10) |
|
||||||
|
| 3.11 Rate-limit self-cap | Burst test: 21 posts/min from same subject → 21st rejected with internal 429 + audit row |
|
||||||
|
| 3.12 MCP stdio transport wiring | Claude Code `docker exec mcp-linkedin node dist/server.js` responds to `initialize` + `tools/list` per MCP spec `2024-11-05` (pinned) |
|
||||||
|
|
||||||
|
### Stage 4 — Scheduler (day 7–8)
|
||||||
|
|
||||||
|
| Step | Measurable DoD |
|
||||||
|
|---|---|
|
||||||
|
| 4.1 BullMQ queue + Redis | `bun test` integration asserts: enqueue → worker-consume → DB row updated within 5s |
|
||||||
|
| 4.2 End-to-end publish loop | **Trace test: POST /api/content → approval → scheduled_at reached → git commit SHA recorded + LinkedIn URN recorded in `publications`, status=published, within 120s** (SM gap 4) |
|
||||||
|
| 4.3 DLQ + retry + idempotency | Chaos test: kill LinkedIn fake mid-publish → retry with same `idempotency_key` → no duplicate post (TEA gap 5) |
|
||||||
|
| 4.4 Cadence optimizer integration | Feature flag default OFF; unit tests assert proposals with ≥8 samples + ≥4 weeks threshold; no proposal below threshold |
|
||||||
|
|
||||||
|
### Stage 5 — Admin dashboard (days 8–10)
|
||||||
|
|
||||||
|
| Step | Measurable DoD |
|
||||||
|
|---|---|
|
||||||
|
| 5.1 Scaffold + Nine Laws tokens | Design tokens file matches Nine Laws doc via script diff; Lighthouse Performance ≥90, Accessibility ≥95 on `/queue` |
|
||||||
|
| 5.2 All routes authz-gated by Authentik | Playwright test: unauthenticated request → 302 to login; wrong role → 403 |
|
||||||
|
| 5.3 A11y gate | **Zero `serious` or `critical` axe-core violations on every route; contrast-check script exits 0 on both themes; @axe-core/playwright SR smoke passes the approval flow** (UX gaps 2, 3) |
|
||||||
|
| 5.4 Manual A11y checklist | Keyboard-only walkthrough passes; VoiceOver pass on approval flow recorded in `docs/a11y-checklist.md`; focus-trap on modals verified |
|
||||||
|
| 5.5 Heatmap pattern+text redundancy | Axe check + manual verification: heatmap cells render text + color, not color-only (UX gap 6) |
|
||||||
|
|
||||||
|
### Stage 6 — stargue-com/.net integration (day 10)
|
||||||
|
|
||||||
|
| Step | Measurable DoD |
|
||||||
|
|---|---|
|
||||||
|
| 6.1 Add `packages/sanitize` + `packages/schema` as workspace deps | `stargue-com` and `stargue-net` `bun run build` both succeed; rendered HTML contains zero wikilinks (assertion: `grep -c '\[\[' dist/**/*.html` = 0) |
|
||||||
|
| 6.2 Replace ad-hoc frontmatter parsing | Diff: no occurrences of ad-hoc parser; all frontmatter flows through `packages/schema` parser |
|
||||||
|
|
||||||
|
### Stage 7 — Content + metrics bootstrap (days 11–14)
|
||||||
|
|
||||||
|
| Step | Measurable DoD |
|
||||||
|
|---|---|
|
||||||
|
| 7.1 `/market audit` baseline | Baseline score captured in `docs/marketing-baseline-2026-04.json` |
|
||||||
|
| 7.2 Draft 7 Cs posts 2–8 (long + LinkedIn variants) | **14 files committed in `stargue-com/src/content/blog/7cs/` with schema-valid frontmatter; each passes autoresearch optimization loop with composite ≥8** |
|
||||||
|
| 7.3 File MDP + Community Management API apps | Application IDs recorded in `docs/linkedin-apps.md` |
|
||||||
|
| 7.4 First scheduled post lands on stargue.com + LinkedIn | **`publication.external_url` returns HTTP 200 from unauthenticated curl; stargue.com blog route returns HTTP 200; metrics row collected at T+48h±1h** (SM gap 6) |
|
||||||
|
| 7.5 DR test | pg_dump to Neon succeeds and restores to verifiable row count |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Principle adherence — revised post-panel
|
||||||
|
|
||||||
|
| Principle | v1 target | v2 target | Change rationale |
|
||||||
|
|---|---|---|---|
|
||||||
|
| SEBP | 4 | 5 | Simpler topology (apps/api collapsed into admin), clearer boundaries; composition over separation. |
|
||||||
|
| SSoT | 5 | 5 | Unchanged (panel consensus). Name-collision fix in 0.10. |
|
||||||
|
| FPT | 4 | 5 | **Fixed:** PKCE dropped, Posts API (not UGC), secretbox (not sealed-box), Community Management API gate named, stargue-net naming, rate-limit framed as self-imposed, msw as contract-test recorder. All per dev + architect verification. |
|
||||||
|
| DiDSP | 5 | 5 | Idempotency_key + advisory lock + CSRF + kill-switch added. |
|
||||||
|
| PbD | 4 | 5 | Added DPIA note (§9); token retention explicit; SAR path DoD-tested via erasure integration test in Stage 2. |
|
||||||
|
| OF | 5 | 5 | Unchanged (panel consensus 5 on OF). |
|
||||||
|
| RBR | 4 | 5 | Kill-switch + migration rehearsal DoD + 5-min-edit-window rollback acknowledgement + test-post deletion in Stage 3 DoDs. |
|
||||||
|
| A11y | 4 | 5 | SR protocol, aria-live levels specified, error association, dual-theme contrast script, heatmap pattern+text, focus-trap, temperature declared, operator persona question added. |
|
||||||
|
| Testability | 4 | 5 | All DoDs measurable, msw recorder named, concurrency+rate-limit+idempotency tests specified, optimizer pure-function seam, Vitest+Bun setup included in Stage 1. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Critical files / paths (unchanged from v1 + additions)
|
||||||
|
|
||||||
|
[Existing list preserved]
|
||||||
|
|
||||||
|
Additions:
|
||||||
|
- `test/corpus/private/` — sanitizer test SSoT
|
||||||
|
- `docs/linkedin-apps.md` — Dev Portal app IDs + approval states
|
||||||
|
- `docs/a11y-checklist.md` — manual A11y checklist
|
||||||
|
- `db/migrations/README.md` — migration runbook (expand-then-contract sequences)
|
||||||
|
- `docs/marketing-baseline-2026-04.json` — `/market audit` baseline
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Verification (expanded per TEA gaps)
|
||||||
|
|
||||||
|
- **Unit:** Vitest + Bun workspace. 100% line coverage on pure cores.
|
||||||
|
- **Integration:** real Postgres (Docker), real Redis, `msw` fakes for LinkedIn. Frozen clock via `@sinonjs/fake-timers`; seeded RNG.
|
||||||
|
- **Contract:** msw-recorded LinkedIn API exchanges at `test/fixtures/linkedin/*.har`. **Re-record policy: rotate fixtures monthly or when LinkedIn-Version header bumps** (TEA gap 2). Stale-fixture detector: fixture file mtime > 45 days → CI warning.
|
||||||
|
- **E2E:** Playwright + `@axe-core/playwright` + NVDA scripting on Windows GH Actions runner.
|
||||||
|
- **Security:** `bun audit` in CI; gitleaks pre-commit; Dependabot.
|
||||||
|
- **Load:** k6 at Phase 1 exit — 50 queued publishes drained within SLA.
|
||||||
|
- **DR:** weekly pg_dump-to-Neon rehearsal with row-count verification.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Authorization gates — ANSWERED
|
||||||
|
|
||||||
|
| # | Question | Answer (2026-04-19) |
|
||||||
|
|---|---|---|
|
||||||
|
| 1 | Stargue LinkedIn Company Page URL | `https://www.linkedin.com/company/2605890/` — URN `urn:li:organization:2605890` |
|
||||||
|
| 2 | Cadence bootstrap | Confirmed: Tue + Thu ~08:30 AM AST |
|
||||||
|
| 3 | Admin auth | Shared Authentik instance (same for all Stargue services) |
|
||||||
|
| 4 | MDP signatory | Angelo as individual; Stargue entity later |
|
||||||
|
| 5 | Postgres + Neon | PaaS cluster + nightly pg_dump to Neon free tier (RPO ~24h) |
|
||||||
|
| 6 | 4h/week cap override | User override: concentrated full-time build, timeline risk accepted |
|
||||||
|
| 7 | BMAD install location | DQMS charters referenced by path; no install in Publishing Engine repo for this phase |
|
||||||
|
| 8 | Primary operator persona + A11y context (UX gap 8) | **Option C (2026-04-19):** primary = Angelo (VoiceOver-capable); secondary = Claude Code agents (programmatic). A11y target: human-first WCAG 2.2 AA; agents inherit semantic markup for free. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Risks (expanded per panel)
|
||||||
|
|
||||||
|
| Risk | Mitigation | Raised by |
|
||||||
|
|---|---|---|
|
||||||
|
| LinkedIn denies MDP | 60-day re-auth operational baseline; proactive T-5d notification | plan v1 |
|
||||||
|
| **LinkedIn denies Community Management API** | Personal-profile posting unblocked; org-posting tools built but gated until approval | dev |
|
||||||
|
| **LinkedIn algorithm suppresses automated-posted content reach** | Monitor engagement delta between automated vs manual-posted baselines for first 4 weeks; if suppression detected, fallback to draft-and-prompt workflow via admin | **analyst** |
|
||||||
|
| LinkedIn suspends app (ToS) | Conservative rate limit; approval on every publish; no messaging; no engagement automation; own-content only | v1 |
|
||||||
|
| Refresh token revoked mid-series | Fail-safe halt + Telegram notification within minutes | v1 |
|
||||||
|
| Refresh rotation race | Advisory lock on refresh path (TEA gap 3) | tea |
|
||||||
|
| Duplicate post via retry | Idempotency key on publications + BullMQ jobs (dev gap 4) | dev, tea |
|
||||||
|
| Dokploy outage during scheduled post | DLQ + exponential backoff + alert | v1 |
|
||||||
|
| Private vault leak via sanitizer gap | 3-layer fail-closed + CI corpus (sanitize gap 8) | v1, TEA |
|
||||||
|
| OneDrive sync conflict | Scheduler reads from Postgres, not OneDrive; checksum on ingest | v1 |
|
||||||
|
| Admin PII leak | No PII in dashboard; Authentik SSO; audit view | v1 |
|
||||||
|
| Bad LinkedIn post past 5-min edit window | Runbook: delete via `linkedin_delete_post` tool; audit; post-incident review documented in `docs/incidents/` (architect, SM) | architect, sm |
|
||||||
|
| Scope creep | Stages gated by measurable DoD; Phase 2 = separate plan | v1 |
|
||||||
|
| **4h/week → full-time override creates personal-capacity risk** | User-owned timeline risk; checkpoints at Stage 3, 5, 7 to re-evaluate (PM gap 1) | pm |
|
||||||
|
| **MDP + Community Management API approval timeline unknown** | Plan works without either; track application IDs in `docs/linkedin-apps.md` | pm |
|
||||||
|
| DPIA for OAuth-token processing of natural person (Angelo) | **DPIA recorded in `docs/dpia/linkedin-oauth.md`** — purpose (publication automation), data (encrypted tokens only), retention (lifetime-of-authorization), lawful basis (contract-performance + legitimate interest), user rights (erasure via token revoke + hard-delete row) (TEA gap PbD) | tea, sm |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Out of scope (deferred to Phase 2 with separate plan)
|
||||||
|
|
||||||
|
- Multilingual routing + hreflang (PAP/NL/ES) — F-021. **Tradeoff explicit:** 7 Cs is EN-only at launch; PAP-speaking stakeholders see EN during bootstrap. Phase 2 adds language-aware routing (Analyst + PM gaps).
|
||||||
|
- Medium, Substack, ResearchGate, X, Reddit, Telegram, TikTok, Facebook, WhatsApp, Newsletter-email outlets — F-013, F-033
|
||||||
|
- Inlet Engine — F-030, F-031 (separate PRD)
|
||||||
|
- Cross-platform analytics beyond LinkedIn + stargue.com/net
|
||||||
|
- Higgsfield media pipeline
|
||||||
|
- Library-vs-MCP decision (scheduler uses library directly; MCP reserved for Claude Code — if future needs require HTTP transport, separate plan)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Open questions — ALL RESOLVED
|
||||||
|
|
||||||
|
All pre-execution gates answered 2026-04-19. Proceeding to Stage 0.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [[Publishing Engine PRD]]
|
||||||
|
- [[7 Cs LinkedIn Series - Continuation Plan]]
|
||||||
|
- [[Design Philosophy - The Nine Laws of the Hearth]]
|
||||||
|
- [[Outlet Profiles/LinkedIn]]
|
||||||
|
- [[Implementation Progress]]
|
||||||
|
|
||||||
|
## Sources
|
||||||
|
|
||||||
|
- [LinkedIn Programmatic Refresh Tokens (Microsoft Learn, retrieved 2026-04-19)](https://learn.microsoft.com/en-us/linkedin/shared/authentication/programmatic-refresh-tokens)
|
||||||
|
- [LinkedIn 3-Legged OAuth Flow (Microsoft Learn, retrieved 2026-04-19)](https://learn.microsoft.com/en-us/linkedin/shared/authentication/authorization-code-flow)
|
||||||
|
- [LinkedIn Posts API (Microsoft Learn)](https://learn.microsoft.com/en-us/linkedin/marketing/community-management/shares/posts-api) — current replacement for deprecated UGC Posts
|
||||||
|
- `/home/devuser/projects/dqms/.clinerules/12-foundational-principles.md`
|
||||||
|
- `/home/devuser/projects/dqms/.claude/hooks/gate-plan-exit.sh`
|
||||||
|
- `/home/devuser/projects/dqms/.claude/skills/bmad-plan/SKILL.md`
|
||||||
|
- `/home/devuser/projects/dqms/_bmad/bmm/agents/*.md` — 7-agent charters (SSoT for panel roles)
|
||||||
|
- Panel verdicts v2: `/tmp/bmad-panel-verdicts-publishing-engine-phase1-20260419.json`
|
||||||
@@ -0,0 +1,193 @@
|
|||||||
|
{
|
||||||
|
"plan_file": "c:\\Users\\aluid\\OneDrive\\Documents\\Obsidian\\Angelo'sSBO\\Stargue\\Projects\\Publishing Engine\\Plan - Phase 1 Automated Publishing Engine.md",
|
||||||
|
"created_at": "2026-04-19T10:20:00-04:00",
|
||||||
|
"applicable_tier2": ["RBR", "A11y", "Testability"],
|
||||||
|
"verdicts": [
|
||||||
|
{
|
||||||
|
"agent": "analyst",
|
||||||
|
"verdict": "APPROVE",
|
||||||
|
"confidence": "medium",
|
||||||
|
"summary": "Plan is evidence-grounded and principle-aligned; approve subject to resolving Company-Page existence, timeline capacity, Bayesian spec, and LinkedIn algorithmic-reach competitive risk before Stage 3.",
|
||||||
|
"gaps": [
|
||||||
|
"§4 timeline: 7-day schedule is aggressive given PRD §2 production-problem framing; no FTE/capacity assumption stated",
|
||||||
|
"§2/§8: Stargue Company Page URL is both an Objective and an open gate — stakeholder alignment gap",
|
||||||
|
"§3.7 cadence optimizer: Bayesian posterior under-specified (prior, thresholds, sample-size beyond ≥8)",
|
||||||
|
"§10: multilingual (PAP/NL/ES) deferred, but PRD §5.3/NF-008 and 7 Cs plan assume PAP audience",
|
||||||
|
"§9: no competitive risk entry for LinkedIn algorithm/reach-suppression of automated-posted content"
|
||||||
|
],
|
||||||
|
"principles": {
|
||||||
|
"SEBP": {"score": 4, "rationale": "§3.1 monorepo with packages/{sanitize,schema,linkedin-client,observability} gives clear single-responsibility boundaries; scheduler + MCP both consume linkedin-client (no duplication)."},
|
||||||
|
"SSoT": {"score": 5, "rationale": "§3.4 sanitize config + §3.5 Drizzle schema + §5 design tokens generated from Nine Laws doc — each concept has one authoritative location."},
|
||||||
|
"FPT": {"score": 4, "rationale": "§1 cites retrieved LinkedIn MDP doc with date; §3 stack choices match verified stargue-com/stargue-net inventory. Minor: Bayesian optimizer under-specified."},
|
||||||
|
"DiDSP": {"score": 5, "rationale": "§3.3 libsodium sealed-box tokens, Authentik SSO, bearer tokens scheduler↔MCP, rate limits, no public exposure, Zod at every boundary."},
|
||||||
|
"PbD": {"score": 4, "rationale": "§5 notes token minimization, 13-month metrics retention, erasure path via token revoke + git delete. Gap: no DPIA note on LinkedIn-side audience data."},
|
||||||
|
"OF": {"score": 5, "rationale": "§3.3 correlation IDs, Pino→OpenObserve, PII-redacted content (length+hash only), append-only audit table, proactive T-5d auth-expiry notification."},
|
||||||
|
"RBR": {"score": 4, "rationale": "§3.3 fail-safe halt, §3.7 human-in-the-loop for cadence, §3.5 expand-then-contract, §4.3 DLQ. Gap: no rollback drill for published-to-LinkedIn content past 5-min edit window."},
|
||||||
|
"A11y": {"score": 4, "rationale": "§3.6 WCAG 2.2 AA, axe-core in CI, keyboard parity, prefers-reduced-motion, contrast on both themes; OAuth screen delegated to LinkedIn. Gap: no screen-reader manual test named."},
|
||||||
|
"Testability": {"score": 4, "rationale": "§3 pure core in packages; §7 contract tests against recorded LinkedIn exchanges; injectable clock/ID implied. Gap: cadence-optimizer seams not explicit."}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"agent": "architect",
|
||||||
|
"verdict": "REVISE",
|
||||||
|
"confidence": "high",
|
||||||
|
"summary": "Architecturally sound and stack-verified, but over-partitioned for a 7-day build — collapse api-into-admin, fix the libsodium primitive, and cite the rate-limit numbers before Stage 0.",
|
||||||
|
"gaps": [
|
||||||
|
"§3.1 Repo topology adds Turbo + 4 apps + 4 packages for a 7-day plan — architecturally heavy for one-engineer rollout; suggest collapsing apps/api into apps/admin route handlers",
|
||||||
|
"§3.3 MCP transport: Streamable HTTP spec version unnamed; scheduler-to-MCP over HTTP within same Docker network adds a hop vs direct library import — justify or use library",
|
||||||
|
"§3.3 Token store: libsodium sealed-box is public-key encryption; for symmetric at-rest DB encryption use secretbox or KMS envelope",
|
||||||
|
"§3.3 Rate limit numbers (~100/day per member) are uncited",
|
||||||
|
"§3.8 Postgres: says 'shared with other PaaS apps (existing cluster)' — verify; §8.5 open question contradicts",
|
||||||
|
"§4 Stages: 7-day timeline for OAuth×2 identities + 8 MCP tools + scheduler + admin + A11y CI + E2E is optimistic",
|
||||||
|
"§3.4 Sanitization: no test corpus named in Stage DoD (only in §9 risks)",
|
||||||
|
"§3.6 Admin: no CSRF strategy named for approval POSTs"
|
||||||
|
],
|
||||||
|
"principles": {
|
||||||
|
"SEBP": {"score": 4, "rationale": "Clean package boundaries (§3.1); mcp-linkedin consumes linkedin-client — but api+admin separation may violate YAGNI for Phase 1."},
|
||||||
|
"SSoT": {"score": 5, "rationale": "Frontmatter schema, blocklist, design tokens each single-homed (§3.4, §3.5, §3.6)."},
|
||||||
|
"FPT": {"score": 4, "rationale": "Stack verified against actual repos; LinkedIn docs cited with retrieval date. Loses a point on uncited rate-limit figures and MCP transport choice."},
|
||||||
|
"DiDSP": {"score": 4, "rationale": "Zod+Authentik+rate-limit+encrypted tokens+audit (§3.3) — solid. Loses point for libsodium sealed-box mis-selection and missing CSRF."},
|
||||||
|
"PbD": {"score": 4, "rationale": "Low PII surface, 13-month retention, erasure path named (§5). Token retention policy could be more explicit."},
|
||||||
|
"OF": {"score": 5, "rationale": "Correlation IDs end-to-end, OpenObserve integration, body-hash redaction, append-only audit (§3.3, §5)."},
|
||||||
|
"RBR": {"score": 4, "rationale": "Expand-only migrations, DLQ, human-gated cadence, fail-safe on auth. LinkedIn delete only within 5-min edit window — no compensating audit-before-publish preview enforcement."},
|
||||||
|
"A11y": {"score": 4, "rationale": "WCAG 2.2 AA target, axe-core in CI, keyboard parity, both themes contrast-checked. Screen-reader manual test unlisted in DoD."},
|
||||||
|
"Testability": {"score": 4, "rationale": "Pure core in packages, injectable clock, client behind interface, contract tests against recorded exchanges. No named fake for token-store rotation race."}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"agent": "pm",
|
||||||
|
"verdict": "REVISE",
|
||||||
|
"confidence": "high",
|
||||||
|
"summary": "Strong governance and architecture, but the plan inverts the PRD's revenue-first pivot and fails to reconcile scope with the 4h/week capacity cap — revise scope and timeline before exit.",
|
||||||
|
"gaps": [
|
||||||
|
"§4 Stages vs PRD §6.5: Plan compresses M1.1–M1.4 (PRD 7 weeks @ 4h/wk = 28h) into ~7 days with no hours-per-stage stated; PRD §9 risk #1 is solo-founder capacity",
|
||||||
|
"§2 Objectives: zero mention of revenue conversion (PRD §6.4, F-003); PRD v2 pivot explicitly revenue-first, plan adds infrastructure only",
|
||||||
|
"§10: multilingual (PAP/NL/ES) deferred to Phase 2 but PRD F-021 places at P1 and 7 Cs plan publishes EN only",
|
||||||
|
"§4 Stage 7: 14 drafts committed in 2 days assumes autoresearch loop is near-free — no hours estimate",
|
||||||
|
"§3.6/§3.7 Admin + optimizer are M1.2/M2 scope in PRD; pulling into Phase 1 redefines Phase 1 boundary",
|
||||||
|
"§11: MDP application timeline not flagged for revenue-dependent features"
|
||||||
|
],
|
||||||
|
"principles": {
|
||||||
|
"SEBP": {"score": 4, "rationale": "Monorepo boundaries clean (§3.1); standard packages/apps split; no leaky abstractions."},
|
||||||
|
"SSoT": {"score": 5, "rationale": "Frontmatter schema, sanitize config, design tokens all single-location; eliminates existing ad-hoc parse."},
|
||||||
|
"FPT": {"score": 4, "rationale": "Cites LinkedIn MDP docs with retrieval date; verified repo state; real outlet profile. Does not probe 4h/wk capacity math."},
|
||||||
|
"DiDSP": {"score": 5, "rationale": "Zod at boundaries, Authentik SSO, libsodium, rate limiting, audit table, no public MCP exposure."},
|
||||||
|
"PbD": {"score": 4, "rationale": "Token retention + encryption + revocation path; 13-month analytics cap; content is public-by-design."},
|
||||||
|
"OF": {"score": 5, "rationale": "Correlation IDs through API→scheduler→MCP; OpenObserve dashboards; audit append-only; PII redaction."},
|
||||||
|
"RBR": {"score": 4, "rationale": "Human approval on every publish, DLQ, expand-only migrations, feature flag on adaptive optimizer, Telegram fail-safe. No staged rollout for MCP server itself."},
|
||||||
|
"A11y": {"score": 4, "rationale": "WCAG 2.2 AA target, axe-core CI, keyboard parity, prefers-reduced-motion, dual-theme contrast. Not yet tested with SR."},
|
||||||
|
"Testability": {"score": 4, "rationale": "Pure core in packages, injectable clock/ID, LinkedIn client behind interface with contract tests. Sandbox-or-contract fallback right."}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"agent": "sm",
|
||||||
|
"verdict": "REVISE",
|
||||||
|
"confidence": "high",
|
||||||
|
"summary": "Architecture and DoDs are largely sprint-ready, but stages must be re-timelined against the PRD's 4h/week cap and 6 DoD items need 'works'-language tightened before dev handoff.",
|
||||||
|
"gaps": [
|
||||||
|
"§4 DoD uses calendar 'days 1–7' but PRD caps at 4h/week; re-express as Week-1…Week-N at 4h/week or justify",
|
||||||
|
"§4 Stage 1.2 DoD 'Sanitizes sample vault note correctly' needs replacement with 'round-trips 12-file private-corpus fixture with 0 leaks and 12 build errors'",
|
||||||
|
"§4 Stage 3.4: no cleanup step; add 'test post deleted within 5-min edit window; deletion audited'",
|
||||||
|
"§4 Stage 4.2 'End-to-end happy path': specify trace (POST /content → approval → scheduled_at → git SHA + LinkedIn URN recorded, status=published, within 120s)",
|
||||||
|
"§4 Stage 5.3 'No violations above minor' needs axe-core severity gate (zero serious+critical) + WCAG contrast ratios",
|
||||||
|
"§4 Stage 7.4 'Observed live' not CI-demonstrable; add 'publication row external_url returns HTTP 200 from unauthenticated curl; metrics row at T+48h±1h'",
|
||||||
|
"§8 Auth gates block Stage 0 exit but Stage 0 has no 'gates answered' DoD row",
|
||||||
|
"§4 Stage 2.1 'expand-only migrations documented': needs pointer to doc location",
|
||||||
|
"No sprint sequencing artifact / story backlog — charter requires it for dev handoff"
|
||||||
|
],
|
||||||
|
"principles": {
|
||||||
|
"SEBP": {"score": 4, "rationale": "Clean monorepo boundaries (§3.1); shared packages reused — no duplicated logic named."},
|
||||||
|
"SSoT": {"score": 5, "rationale": "§3.4 + §3.5 put frontmatter, sanitize config, tokens in single packages; §5 SSoT row cites file paths."},
|
||||||
|
"FPT": {"score": 4, "rationale": "§1 cites LinkedIn MDP doc with retrieval date; verified repo state; rejected n8n with tradeoff. Miss on Phase 0 capacity constraint."},
|
||||||
|
"DiDSP": {"score": 5, "rationale": "§3.3 token encryption + §3.5 audit table + §3.6 Authentik SSO + §9 rate limits — layered, concrete."},
|
||||||
|
"PbD": {"score": 4, "rationale": "§5 PbD row names retention (13mo), erasure path (token revoke + hard delete), purpose limitation. Missing DPIA note."},
|
||||||
|
"OF": {"score": 5, "rationale": "§3.3 per-tool correlation ID + OpenObserve + redaction rule — structured and specific."},
|
||||||
|
"RBR": {"score": 3, "rationale": "§5 names approval-gate + DLQ + expand-contract + flagged adaptive cadence. Missing rollback runbook for a bad LinkedIn post past 5-min edit window."},
|
||||||
|
"A11y": {"score": 4, "rationale": "§3.6 + Stage 5.3/5.4 name axe-core, keyboard parity, reduced-motion, dual-theme contrast. 'No violations above minor' ambiguous."},
|
||||||
|
"Testability": {"score": 4, "rationale": "§7 splits unit/integration/E2E; §3.3 client behind interface; injectable clock mentioned. Cadence-optimizer Bayesian logic has no named test seams."}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"agent": "ux-designer",
|
||||||
|
"verdict": "REVISE",
|
||||||
|
"confidence": "high",
|
||||||
|
"summary": "Strong security/SSoT/observability spine with Nine-Laws-aware design intent, but A11y is declared-not-designed — add operator flows, SR protocol, dual-theme contrast verification, and reconcile Law-3 temperature with WCAG 1.4.1 before exit.",
|
||||||
|
"gaps": [
|
||||||
|
"§3.6/§5: A11y names mechanisms but omits user flows; no wireframes/task analysis/cognitive-load budget for /queue approval task (where slip leaks private content)",
|
||||||
|
"§3.6: aria-live polite/assertive level unspecified; no error-association spec for OAuth re-auth or approval form (WCAG 3.3.1, 3.3.3, 4.1.3)",
|
||||||
|
"§3.6: no screen-reader testing in CI or manual protocol (charter requires SR-tested for score 5)",
|
||||||
|
"§3.6: dual-theme contrast promised but neither theme's token values (warm #fefbf6 / cool #f8fafc + Forest/Lime accents) verified against 4.5:1 / 3:1",
|
||||||
|
"Nine Laws fit: admin dashboard is operator-tool — no temperature declared; risk of Law-4 amber leakage into status badges",
|
||||||
|
"§3.7 cadence optimizer heatmap is color-encoded — Law 3 temperature semantics conflict with WCAG 1.4.1 (color not sole means)",
|
||||||
|
"§4 Stage 5: no criterion for focus-trap on modal dialogs, skip-links, or reduced-motion honoring in section fade-in (P8.4)",
|
||||||
|
"§11: open questions omit 'primary operator persona and accessibility context'"
|
||||||
|
],
|
||||||
|
"principles": {
|
||||||
|
"SEBP": {"score": 4, "rationale": "Monorepo boundaries in §3.1 are clean; shared packages reused by scheduler + mcp; tradeoff named."},
|
||||||
|
"SSoT": {"score": 5, "rationale": "§3.1 shared packages, §3.4 Zod config, §3.6 design tokens inherited from Nine Laws doc — no duplication path."},
|
||||||
|
"FPT": {"score": 4, "rationale": "§1 cites LinkedIn docs with retrieval date and verifies repo state; rejects n8n on SSoT grounds."},
|
||||||
|
"DiDSP": {"score": 5, "rationale": "§3.3 sealed-box, §3.4 fail-closed sanitizer, §3.5 append-only audit, Authentik + bearer layered."},
|
||||||
|
"PbD": {"score": 4, "rationale": "§3.3 token minimization, 13-month analytics retention, erasure path named."},
|
||||||
|
"OF": {"score": 5, "rationale": "§3.3/§3.5 correlation IDs, structured Pino logs, OpenObserve dashboards, append-only audit."},
|
||||||
|
"RBR": {"score": 4, "rationale": "§3.7 human-in-loop, DLQ, expand-only migrations, feature flag on adaptive cadence."},
|
||||||
|
"A11y": {"score": 3, "rationale": "Targets WCAG 2.2 AA and names axe-core/keyboard/contrast — missing SR testing, user flows, cognitive-load analysis on approval task, heatmap color-only encoding risk, Law-3/Law-4 contrast verification. Meets floor, not exemplary."},
|
||||||
|
"Testability": {"score": 4, "rationale": "§7 names unit/integration/E2E/contract tests; §3.3 injectable token store; pure-core in packages."}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"agent": "dev",
|
||||||
|
"verdict": "REVISE",
|
||||||
|
"confidence": "high",
|
||||||
|
"summary": "Solid architecture and governance, but ship-blocker inaccuracies on LinkedIn auth (PKCE unverified) and endpoint (UGC Posts is Legacy; use Posts API with LinkedIn-Version header) must be corrected before Stage 3.",
|
||||||
|
"gaps": [
|
||||||
|
"§3.3 Auth: scopes 'openid profile email' are from 'Sign In with LinkedIn using OpenID Connect' product; 'w_member_social' requires 'Share on LinkedIn' product — must be declared per-product in Dev Portal. 'w_organization_social'/'r_organization_social' require Community Management API product approval (separate gate).",
|
||||||
|
"§3.3 Auth: PKCE is NOT documented as supported by LinkedIn's 3LO — Microsoft Learn authorization-code-flow shows only response_type=code + client_secret. Either cite PKCE support or drop it.",
|
||||||
|
"§3.3 Tools: UGC Post API is now Legacy; must use Posts API (/rest/posts) with 'LinkedIn-Version: YYYYMM' header and 'X-Restli-Protocol-Version: 2.0.0'. Building against /v2/ugcPosts = tech debt on day one.",
|
||||||
|
"§3.2/§3.5: no idempotency key on POST /content or BullMQ job dispatch — retries could double-post",
|
||||||
|
"§3.8: stargue-net/package.json is byte-identical to stargue-com/package.json (same 'name' field) — name collision to resolve before workspace link",
|
||||||
|
"§4 Stage 3.9: MCP Streamable HTTP transport is current spec (stdio+SSE deprecated); pin @modelcontextprotocol/sdk version",
|
||||||
|
"§7: no AC→Vitest mapping; contract-test recording tool unchosen (Polly.js? msw?)"
|
||||||
|
],
|
||||||
|
"principles": {
|
||||||
|
"SEBP": {"score": 4, "rationale": "Clean package boundaries; MCP client reused by scheduler; no duplicated domain logic across apps."},
|
||||||
|
"SSoT": {"score": 4, "rationale": "Zod schema + sanitize config + design tokens each single-homed. Risk: stargue-net package.json duplicates stargue-com verbatim — name collision to resolve."},
|
||||||
|
"FPT": {"score": 3, "rationale": "Plan cites repo state and doc retrieval date, but LinkedIn API claims don't match live docs: UGC Posts is Legacy (Posts API current), PKCE not documented as supported. Fix before impl."},
|
||||||
|
"DiDSP": {"score": 4, "rationale": "Authentik SSO + internal bearer + Zod + libsodium + rate limits + audit log + no public MCP exposure. Missing idempotency key to prevent replay/double-post."},
|
||||||
|
"PbD": {"score": 4, "rationale": "Low PII surface; token retention tied to lifetime; analytics capped 13mo; erasure path named."},
|
||||||
|
"OF": {"score": 5, "rationale": "Correlation IDs across API→scheduler→MCP; Pino JSON; OpenObserve; append-only audit; per-call latency+outcome; PII redaction."},
|
||||||
|
"RBR": {"score": 4, "rationale": "Approval gate + DLQ + expand-then-contract + feature flag + proactive re-auth notification."},
|
||||||
|
"A11y": {"score": 3, "rationale": "WCAG 2.2 AA + axe-core CI + keyboard parity + reduced-motion + dual-theme contrast. Lacks SR test plan and aria-live specifics."},
|
||||||
|
"Testability": {"score": 3, "rationale": "Pure core extracted; injectable clock/ID; client behind interface. Missing AC→test mapping, HTTP-recording lib, and Vitest setup for Bun workspaces."}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"agent": "tea",
|
||||||
|
"verdict": "REVISE",
|
||||||
|
"confidence": "high",
|
||||||
|
"summary": "Architecture is sound and SSoT/OF are exemplary; revise §4 DoDs into measurable gates, close refresh-race + idempotency + migration-rehearsal + kill-switch gaps, then re-score — currently REVISE.",
|
||||||
|
"gaps": [
|
||||||
|
"§4 Stage DoDs largely untestable as stated ('Smoke tests pass', 'Test post lands', 'End-to-end happy path on staging', 'Matches stargue.com visual system') — add measurable criteria",
|
||||||
|
"§7 omits flake controls: no frozen clock, seeded RNG, deterministic IDs, or VCR/contract-test refresh cadence",
|
||||||
|
"§3.3 refresh path lacks concurrency guard — two scheduler jobs on expired token simultaneously can double-rotate; specify SELECT FOR UPDATE / advisory lock",
|
||||||
|
"§3.5 'expand-only' migrations claimed but no shadow-column/backfill/contract sequence shown; add migration rehearsal DoD",
|
||||||
|
"§4 Stage 4 DLQ DoD (4.3) names backoff but not idempotency key — retries could duplicate public posts",
|
||||||
|
"§4 Stage 3 lacks rate-limit test DoD — add burst-21-calls test asserting 429 on #21",
|
||||||
|
"§3.6 A11y: axe-core 'baseline' and 'no violations above minor' is vague — specify ruleset version, severity gate, SR smoke",
|
||||||
|
"§3.4 sanitizer: no DoD for the private-path corpus itself — add 'CI fails if test/corpus/private/* round-trips without build error'",
|
||||||
|
"§8 gate #3 assumes Authentik exists — Stage 5.2 DoD circular if not",
|
||||||
|
"No kill-switch / feature flag for LinkedIn poster — RBR requires fast-disable independent of code deploy"
|
||||||
|
],
|
||||||
|
"principles": {
|
||||||
|
"SEBP": {"score": 4, "rationale": "Clean package/app split (§3.1); client shared between scheduler+MCP; tradeoff acknowledged."},
|
||||||
|
"SSoT": {"score": 5, "rationale": "Zod schema, sanitize config, design tokens each in one package."},
|
||||||
|
"FPT": {"score": 4, "rationale": "Cites LinkedIn docs with retrieval date (§1); stack matches verified repo state; refresh-concurrency not reasoned from first principles."},
|
||||||
|
"DiDSP": {"score": 4, "rationale": "Layered (Zod+Authentik+bearer+libsodium+audit) but idempotency + refresh-race gaps reduce from 5."},
|
||||||
|
"PbD": {"score": 4, "rationale": "Token min/encrypt/retention named; 13-month metrics retention stated. SAR path asserted, not DoD-tested."},
|
||||||
|
"OF": {"score": 5, "rationale": "Correlation IDs end-to-end, append-only audit, PII redaction, per-call LinkedIn latency capture."},
|
||||||
|
"RBR": {"score": 3, "rationale": "Approval gate, DLQ, proactive auth notify present — but expand-contract migrations unspecified, no idempotency key, no runtime kill-switch."},
|
||||||
|
"A11y": {"score": 3, "rationale": "WCAG 2.2 AA named, axe-core in CI, two-theme contrast — severity gate + SR smoke + ruleset version unspecified."},
|
||||||
|
"Testability": {"score": 3, "rationale": "Pure core extracted, injectable clock/ID/randomness, client-behind-interface — but Stage DoDs largely untestable and no determinism/record-refresh policy."}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
29
package.json
Normal file
29
package.json
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"name": "stargue-publishing-engine",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"description": "Stargue Publishing Engine — automated content pipeline + LinkedIn MCP server",
|
||||||
|
"workspaces": [
|
||||||
|
"apps/*",
|
||||||
|
"packages/*"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"build": "turbo run build",
|
||||||
|
"dev": "turbo run dev",
|
||||||
|
"lint": "turbo run lint",
|
||||||
|
"test": "turbo run test",
|
||||||
|
"typecheck": "turbo run typecheck",
|
||||||
|
"db:migrate": "bun --filter '@stargue/schema' db:migrate",
|
||||||
|
"db:migrate:rehearse": "bun --filter '@stargue/schema' db:migrate:rehearse"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^22.0.0",
|
||||||
|
"bun-types": "^1.3.12",
|
||||||
|
"turbo": "^2.3.0",
|
||||||
|
"typescript": "^5.7.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"bun": ">=1.1.30"
|
||||||
|
},
|
||||||
|
"packageManager": "bun@1.1.38"
|
||||||
|
}
|
||||||
25
packages/linkedin-client/package.json
Normal file
25
packages/linkedin-client/package.json
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"name": "@stargue/linkedin-client",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"main": "./src/index.ts",
|
||||||
|
"types": "./src/index.ts",
|
||||||
|
"exports": {
|
||||||
|
".": "./src/index.ts"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc --noEmit",
|
||||||
|
"typecheck": "tsc --noEmit",
|
||||||
|
"test": "vitest run",
|
||||||
|
"lint": "echo 'lint pending'"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"libsodium-wrappers-sumo": "^0.7.15"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"vitest": "^2.1.0",
|
||||||
|
"msw": "^2.6.0",
|
||||||
|
"typescript": "^5.7.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
6
packages/linkedin-client/src/index.ts
Normal file
6
packages/linkedin-client/src/index.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
export const LINKEDIN_CLIENT_READY = false;
|
||||||
|
// Posts API client + token store — implemented in Stage 1.4 + 3.*.
|
||||||
|
// Reference: https://learn.microsoft.com/en-us/linkedin/marketing/community-management/shares/posts-api
|
||||||
|
export const LINKEDIN_API_BASE = "https://api.linkedin.com/rest";
|
||||||
|
export const LINKEDIN_API_VERSION = "202404";
|
||||||
|
export const LINKEDIN_RESTLI_VERSION = "2.0.0";
|
||||||
24
packages/observability/package.json
Normal file
24
packages/observability/package.json
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"name": "@stargue/observability",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"main": "./src/index.ts",
|
||||||
|
"types": "./src/index.ts",
|
||||||
|
"exports": {
|
||||||
|
".": "./src/index.ts"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc --noEmit",
|
||||||
|
"typecheck": "tsc --noEmit",
|
||||||
|
"test": "vitest run",
|
||||||
|
"lint": "echo 'lint pending'"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"pino": "^9.5.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"vitest": "^2.1.0",
|
||||||
|
"typescript": "^5.7.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
27
packages/observability/src/index.ts
Normal file
27
packages/observability/src/index.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import pino from "pino";
|
||||||
|
|
||||||
|
export const logger = pino({
|
||||||
|
level: process.env.LOG_LEVEL ?? "info",
|
||||||
|
redact: {
|
||||||
|
paths: [
|
||||||
|
"access_token",
|
||||||
|
"refresh_token",
|
||||||
|
"access_token_ct",
|
||||||
|
"refresh_token_ct",
|
||||||
|
"client_secret",
|
||||||
|
"*.access_token",
|
||||||
|
"*.refresh_token",
|
||||||
|
"*.client_secret",
|
||||||
|
],
|
||||||
|
censor: "[REDACTED]",
|
||||||
|
},
|
||||||
|
formatters: {
|
||||||
|
level: (label) => ({ level: label }),
|
||||||
|
},
|
||||||
|
timestamp: pino.stdTimeFunctions.isoTime,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const newCorrelationId = (): string =>
|
||||||
|
`corr_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
||||||
|
|
||||||
|
export const child = (bindings: Record<string, unknown>) => logger.child(bindings);
|
||||||
29
packages/sanitize/package.json
Normal file
29
packages/sanitize/package.json
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"name": "@stargue/sanitize",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"main": "./src/index.ts",
|
||||||
|
"types": "./src/index.ts",
|
||||||
|
"exports": {
|
||||||
|
".": "./src/index.ts"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc --noEmit",
|
||||||
|
"typecheck": "tsc --noEmit",
|
||||||
|
"test": "vitest run",
|
||||||
|
"lint": "echo 'lint pending'"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"unified": "^11.0.0",
|
||||||
|
"remark-parse": "^11.0.0",
|
||||||
|
"remark-stringify": "^11.0.0",
|
||||||
|
"mdast-util-from-markdown": "^2.0.0",
|
||||||
|
"unist-util-visit": "^5.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"vitest": "^2.1.0",
|
||||||
|
"typescript": "^5.7.0",
|
||||||
|
"@types/mdast": "^4.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
2
packages/sanitize/src/index.ts
Normal file
2
packages/sanitize/src/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export const SANITIZE_PACKAGE_READY = false;
|
||||||
|
// Implementation in Stage 1.2. See docs/plans/2026-04-19-phase1-plan.md Stage 1.
|
||||||
30
packages/schema/package.json
Normal file
30
packages/schema/package.json
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"name": "@stargue/schema",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"main": "./src/index.ts",
|
||||||
|
"types": "./src/index.ts",
|
||||||
|
"exports": {
|
||||||
|
".": "./src/index.ts",
|
||||||
|
"./db": "./src/db.ts",
|
||||||
|
"./frontmatter": "./src/frontmatter.ts"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc --noEmit",
|
||||||
|
"typecheck": "tsc --noEmit",
|
||||||
|
"test": "vitest run",
|
||||||
|
"lint": "echo 'lint pending'",
|
||||||
|
"db:migrate": "drizzle-kit migrate",
|
||||||
|
"db:migrate:rehearse": "echo 'rehearsal script pending Stage 2.2'"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"zod": "^3.23.0",
|
||||||
|
"drizzle-orm": "^0.36.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"drizzle-kit": "^0.28.0",
|
||||||
|
"vitest": "^2.1.0",
|
||||||
|
"typescript": "^5.7.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
91
packages/schema/src/db.ts
Normal file
91
packages/schema/src/db.ts
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import { pgTable, serial, text, timestamp, integer, jsonb, boolean, uniqueIndex, index } from "drizzle-orm/pg-core";
|
||||||
|
|
||||||
|
export const content = pgTable("content", {
|
||||||
|
id: serial("id").primaryKey(),
|
||||||
|
vault_path: text("vault_path").notNull().unique(),
|
||||||
|
slug: text("slug").notNull(),
|
||||||
|
title: text("title").notNull(),
|
||||||
|
body_sanitized: text("body_sanitized").notNull(),
|
||||||
|
frontmatter_jsonb: jsonb("frontmatter_jsonb").notNull(),
|
||||||
|
content_hash: text("content_hash").notNull(),
|
||||||
|
version: integer("version").notNull().default(1),
|
||||||
|
created_at: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
||||||
|
updated_at: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
||||||
|
}, (t) => ({
|
||||||
|
slugIdx: index("content_slug_idx").on(t.slug),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const publications = pgTable("publications", {
|
||||||
|
id: serial("id").primaryKey(),
|
||||||
|
content_id: integer("content_id").notNull().references(() => content.id),
|
||||||
|
outlet: text("outlet").notNull(),
|
||||||
|
status: text("status").notNull(),
|
||||||
|
scheduled_at: timestamp("scheduled_at", { withTimezone: true }),
|
||||||
|
published_at: timestamp("published_at", { withTimezone: true }),
|
||||||
|
external_id: text("external_id"),
|
||||||
|
external_url: text("external_url"),
|
||||||
|
idempotency_key: text("idempotency_key").notNull(),
|
||||||
|
error: text("error"),
|
||||||
|
metadata_jsonb: jsonb("metadata_jsonb"),
|
||||||
|
}, (t) => ({
|
||||||
|
idemIdx: uniqueIndex("publications_idempotency_key_idx").on(t.idempotency_key),
|
||||||
|
contentOutletIdx: index("publications_content_outlet_idx").on(t.content_id, t.outlet),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const approvals = pgTable("approvals", {
|
||||||
|
id: serial("id").primaryKey(),
|
||||||
|
content_id: integer("content_id").notNull().references(() => content.id),
|
||||||
|
outlet: text("outlet").notNull(),
|
||||||
|
approved_by: text("approved_by").notNull(),
|
||||||
|
approved_at: timestamp("approved_at", { withTimezone: true }).notNull().defaultNow(),
|
||||||
|
notes: text("notes"),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const metrics = pgTable("metrics", {
|
||||||
|
id: serial("id").primaryKey(),
|
||||||
|
publication_id: integer("publication_id").notNull().references(() => publications.id),
|
||||||
|
collected_at: timestamp("collected_at", { withTimezone: true }).notNull().defaultNow(),
|
||||||
|
impressions: integer("impressions").notNull().default(0),
|
||||||
|
reactions: integer("reactions").notNull().default(0),
|
||||||
|
comments: integer("comments").notNull().default(0),
|
||||||
|
shares: integer("shares").notNull().default(0),
|
||||||
|
clicks: integer("clicks").notNull().default(0),
|
||||||
|
raw_jsonb: jsonb("raw_jsonb"),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const linkedin_tokens = pgTable("linkedin_tokens", {
|
||||||
|
id: serial("id").primaryKey(),
|
||||||
|
subject_type: text("subject_type").notNull(),
|
||||||
|
subject_urn: text("subject_urn").notNull(),
|
||||||
|
access_token_ct: text("access_token_ct").notNull(),
|
||||||
|
refresh_token_ct: text("refresh_token_ct"),
|
||||||
|
access_expires_at: timestamp("access_expires_at", { withTimezone: true }).notNull(),
|
||||||
|
refresh_expires_at: timestamp("refresh_expires_at", { withTimezone: true }),
|
||||||
|
scopes: text("scopes").notNull(),
|
||||||
|
created_at: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
||||||
|
updated_at: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
||||||
|
}, (t) => ({
|
||||||
|
subjectIdx: uniqueIndex("linkedin_tokens_subject_idx").on(t.subject_urn),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const audit = pgTable("audit", {
|
||||||
|
id: serial("id").primaryKey(),
|
||||||
|
ts: timestamp("ts", { withTimezone: true }).notNull().defaultNow(),
|
||||||
|
actor: text("actor").notNull(),
|
||||||
|
action: text("action").notNull(),
|
||||||
|
subject_type: text("subject_type").notNull(),
|
||||||
|
subject_id: text("subject_id").notNull(),
|
||||||
|
correlation_id: text("correlation_id").notNull(),
|
||||||
|
payload_jsonb: jsonb("payload_jsonb"),
|
||||||
|
}, (t) => ({
|
||||||
|
tsIdx: index("audit_ts_idx").on(t.ts),
|
||||||
|
corrIdx: index("audit_correlation_idx").on(t.correlation_id),
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const outlet_feature_flags = pgTable("outlet_feature_flags", {
|
||||||
|
outlet: text("outlet").primaryKey(),
|
||||||
|
enabled: boolean("enabled").notNull().default(true),
|
||||||
|
reason: text("reason"),
|
||||||
|
updated_at: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(),
|
||||||
|
updated_by: text("updated_by").notNull(),
|
||||||
|
});
|
||||||
50
packages/schema/src/frontmatter.ts
Normal file
50
packages/schema/src/frontmatter.ts
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
export const OutletSchema = z.enum([
|
||||||
|
"stargue.com",
|
||||||
|
"stargue.net",
|
||||||
|
"linkedin.member",
|
||||||
|
"linkedin.org",
|
||||||
|
]);
|
||||||
|
export type Outlet = z.infer<typeof OutletSchema>;
|
||||||
|
|
||||||
|
export const LanguageSchema = z.enum(["en", "nl", "pap", "es"]);
|
||||||
|
export type Language = z.infer<typeof LanguageSchema>;
|
||||||
|
|
||||||
|
export const OutletStatusSchema = z.enum([
|
||||||
|
"pending",
|
||||||
|
"queued",
|
||||||
|
"approved",
|
||||||
|
"published",
|
||||||
|
"failed",
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const PerOutletPublishSchema = z.object({
|
||||||
|
outlet: OutletSchema,
|
||||||
|
status: OutletStatusSchema,
|
||||||
|
published_url: z.string().url().nullable(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const PublishFrontmatterSchema = z.object({
|
||||||
|
status: z.enum(["draft", "ready", "queued", "published", "partial", "failed"]),
|
||||||
|
language: LanguageSchema.default("en"),
|
||||||
|
outlets: z.array(PerOutletPublishSchema),
|
||||||
|
scheduled: z.string().datetime({ offset: true }).nullable().default(null),
|
||||||
|
slug: z.string().min(1).optional(),
|
||||||
|
category: z.enum(["blog", "case-study", "white-paper", "research", "digest"]),
|
||||||
|
canonical: z.string().default("stargue.com"),
|
||||||
|
sanitize: z.boolean().default(true),
|
||||||
|
version: z.number().int().positive().default(1),
|
||||||
|
});
|
||||||
|
export type PublishFrontmatter = z.infer<typeof PublishFrontmatterSchema>;
|
||||||
|
|
||||||
|
export const NoteFrontmatterSchema = z.object({
|
||||||
|
created: z.string().datetime({ offset: true }),
|
||||||
|
updated: z.string().datetime({ offset: true }),
|
||||||
|
tags: z.array(z.string()).or(z.null()).optional(),
|
||||||
|
publish: PublishFrontmatterSchema.optional(),
|
||||||
|
});
|
||||||
|
export type NoteFrontmatter = z.infer<typeof NoteFrontmatterSchema>;
|
||||||
|
|
||||||
|
export const parseFrontmatter = (raw: unknown): NoteFrontmatter =>
|
||||||
|
NoteFrontmatterSchema.parse(raw);
|
||||||
2
packages/schema/src/index.ts
Normal file
2
packages/schema/src/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export * from "./frontmatter";
|
||||||
|
export * from "./db";
|
||||||
23
tsconfig.json
Normal file
23
tsconfig.json
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"lib": ["ES2022"],
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"strict": true,
|
||||||
|
"noUncheckedIndexedAccess": true,
|
||||||
|
"exactOptionalPropertyTypes": true,
|
||||||
|
"noImplicitOverride": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"types": ["bun-types", "node"]
|
||||||
|
},
|
||||||
|
"exclude": ["node_modules", "dist", ".next", ".turbo"]
|
||||||
|
}
|
||||||
23
turbo.json
Normal file
23
turbo.json
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://turbo.build/schema.json",
|
||||||
|
"tasks": {
|
||||||
|
"build": {
|
||||||
|
"dependsOn": ["^build"],
|
||||||
|
"outputs": ["dist/**", ".next/**", "!.next/cache/**"]
|
||||||
|
},
|
||||||
|
"dev": {
|
||||||
|
"cache": false,
|
||||||
|
"persistent": true
|
||||||
|
},
|
||||||
|
"lint": {
|
||||||
|
"dependsOn": ["^build"]
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"dependsOn": ["^build"],
|
||||||
|
"outputs": ["coverage/**"]
|
||||||
|
},
|
||||||
|
"typecheck": {
|
||||||
|
"dependsOn": ["^build"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user