@svelte-vitals/core 0.31.1 → 0.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -544,6 +544,20 @@ interface KitModuleFacts {
544
544
  name: string;
545
545
  line: number;
546
546
  }[];
547
+ /**
548
+ * `.set()`/`.update()` in a handler on an import resolving under the `$lib` server root.
549
+ * The call shape alone cannot tell a persistence client (`db.set(…)`) from a hand-rolled
550
+ * in-memory store, so the decision needs the target module — which this pure parse cannot
551
+ * read. `collectKitModuleFacts` resolves each one and promotes the in-memory ones into
552
+ * `importedStateWrites`; a consumer that ignores this field sees the pre-arbitration
553
+ * behaviour, i.e. every one of these exempt.
554
+ */
555
+ pendingServerStoreWrites: {
556
+ name: string;
557
+ imported: string;
558
+ resolved: string;
559
+ line: number;
560
+ }[];
547
561
  /** Value imports whose specifier resolves to a repo-local `.svelte.ts`/`.svelte.js` runes module (security/shared-state-import). */
548
562
  runesModuleImports: {
549
563
  source: string;
package/dist/index.js CHANGED
@@ -1842,6 +1842,13 @@ function libServerRoot(aliases) {
1842
1842
  if (lib && lib.replacement === null) return void 0;
1843
1843
  return `${lib?.replacement ?? "src/lib"}/server`;
1844
1844
  }
1845
+ function serverRootRelativePath(spec, importerFile, aliases) {
1846
+ const serverRoot = libServerRoot(aliases);
1847
+ if (serverRoot === void 0) return void 0;
1848
+ const path = resolveRepoLocalPath(spec, importerFile, aliases);
1849
+ if (path === void 0) return void 0;
1850
+ return path === serverRoot || path.startsWith(`${serverRoot}/`) ? path : void 0;
1851
+ }
1845
1852
  function isLocalStateSpecifier(spec, importerFile, aliases) {
1846
1853
  const serverRoot = libServerRoot(aliases);
1847
1854
  if (serverRoot === void 0) return false;
@@ -1849,12 +1856,32 @@ function isLocalStateSpecifier(spec, importerFile, aliases) {
1849
1856
  if (path === void 0) return false;
1850
1857
  return path !== serverRoot && !path.startsWith(`${serverRoot}/`);
1851
1858
  }
1859
+ var IN_MEMORY_CTORS = /* @__PURE__ */ new Set(["Map", "Set", "WeakMap", "WeakSet"]);
1860
+ function isInMemoryInit(init) {
1861
+ if (!init) return false;
1862
+ if (init.type === "ObjectExpression" || init.type === "ArrayExpression") return true;
1863
+ return init.type === "NewExpression" && init.callee?.type === "Identifier" && IN_MEMORY_CTORS.has(init.callee.name);
1864
+ }
1865
+ function parseInMemoryExports(source, filename) {
1866
+ const names = /* @__PURE__ */ new Set();
1867
+ const { program } = parseModuleProgram(source, filename);
1868
+ for (const stmt of program?.body ?? []) {
1869
+ if (stmt?.type !== "ExportNamedDeclaration" || !stmt.declaration) continue;
1870
+ const decl = stmt.declaration;
1871
+ if (decl.type !== "VariableDeclaration") continue;
1872
+ for (const d of decl.declarations ?? []) {
1873
+ if (d?.id?.type === "Identifier" && isInMemoryInit(d.init)) names.add(d.id.name);
1874
+ }
1875
+ }
1876
+ return names;
1877
+ }
1852
1878
  function parseKitModuleFacts(source, filename, aliases) {
1853
1879
  const suppressions = collectSuppressions(source);
1854
1880
  const { program, wrapped } = parseModuleProgram(source, filename);
1855
1881
  const moduleStateReassignments = [];
1856
1882
  const importedStateWrites = [];
1857
1883
  const importedStateWritesOutsideHandlers = [];
1884
+ const pendingServerStoreWrites = [];
1858
1885
  const runesModuleImports = [];
1859
1886
  const lifecycleCalls = [];
1860
1887
  const browserGlobalRefs = [];
@@ -1863,6 +1890,7 @@ function parseKitModuleFacts(source, filename, aliases) {
1863
1890
  moduleStateReassignments,
1864
1891
  importedStateWrites,
1865
1892
  importedStateWritesOutsideHandlers,
1893
+ pendingServerStoreWrites,
1866
1894
  runesModuleImports,
1867
1895
  lifecycleCalls,
1868
1896
  browserGlobalRefs,
@@ -1872,6 +1900,7 @@ function parseKitModuleFacts(source, filename, aliases) {
1872
1900
  }
1873
1901
  const line = (start) => Math.max(0, lineOf(wrapped, start) - 1);
1874
1902
  const importedSpecifiers = /* @__PURE__ */ new Map();
1903
+ const importedNames = /* @__PURE__ */ new Map();
1875
1904
  for (const stmt of program.body ?? []) {
1876
1905
  if (stmt?.type !== "ImportDeclaration" || stmt.importKind === "type") continue;
1877
1906
  const spec = typeof stmt.source?.value === "string" ? stmt.source.value : "";
@@ -1880,6 +1909,10 @@ function parseKitModuleFacts(source, filename, aliases) {
1880
1909
  if (s?.importKind === "type" || s?.local?.type !== "Identifier") continue;
1881
1910
  names.push(s.local.name);
1882
1911
  importedSpecifiers.set(s.local.name, spec);
1912
+ importedNames.set(
1913
+ s.local.name,
1914
+ s.type === "ImportSpecifier" && s.imported?.type === "Identifier" ? s.imported.name : s.local.name
1915
+ );
1883
1916
  }
1884
1917
  if (names.length === 0) continue;
1885
1918
  const resolved = resolveRunesModuleSpecifier(spec, filename, aliases);
@@ -1956,8 +1989,21 @@ function parseKitModuleFacts(source, filename, aliases) {
1956
1989
  const method = n.callee.property?.type === "Identifier" ? n.callee.property.name : void 0;
1957
1990
  if (method === "set" || method === "update") {
1958
1991
  const r = importedRoot(n.callee.object);
1959
- if (r && isLocalStateSpecifier(importedSpecifiers.get(r), filename, aliases))
1960
- write = { name: r, via: "set-call" };
1992
+ const spec = r ? importedSpecifiers.get(r) : void 0;
1993
+ if (r && spec !== void 0) {
1994
+ if (isLocalStateSpecifier(spec, filename, aliases)) write = { name: r, via: "set-call" };
1995
+ else {
1996
+ const resolved = serverRootRelativePath(spec, filename, aliases);
1997
+ if (resolved !== void 0 && inHandler) {
1998
+ pendingServerStoreWrites.push({
1999
+ name: r,
2000
+ imported: importedNames.get(r) ?? r,
2001
+ resolved,
2002
+ line: line(n.start)
2003
+ });
2004
+ }
2005
+ }
2006
+ }
1961
2007
  }
1962
2008
  } else if (n.type === "AssignmentExpression" && (n.left?.type === "ObjectPattern" || n.left?.type === "ArrayPattern")) {
1963
2009
  const scanPatternTargets = (pat) => {
@@ -2005,6 +2051,7 @@ function parseKitModuleFacts(source, filename, aliases) {
2005
2051
  moduleStateReassignments: byLine(moduleStateReassignments),
2006
2052
  importedStateWrites: byLine(importedStateWrites),
2007
2053
  importedStateWritesOutsideHandlers: byLine(importedStateWritesOutsideHandlers),
2054
+ pendingServerStoreWrites: byLine(pendingServerStoreWrites),
2008
2055
  runesModuleImports: byLine(runesModuleImports),
2009
2056
  lifecycleCalls: byLine(lifecycleCalls),
2010
2057
  browserGlobalRefs: byLine(browserGlobalRefs),
@@ -2024,6 +2071,7 @@ function emptyKitModuleFacts(file, kind) {
2024
2071
  moduleStateReassignments: [],
2025
2072
  importedStateWrites: [],
2026
2073
  importedStateWritesOutsideHandlers: [],
2074
+ pendingServerStoreWrites: [],
2027
2075
  runesModuleImports: [],
2028
2076
  lifecycleCalls: [],
2029
2077
  browserGlobalRefs: [],
@@ -2044,7 +2092,7 @@ async function collectKitModuleFacts(rt, cwd, aliases) {
2044
2092
  ];
2045
2093
  const lists = await Promise.all(patterns.map((p) => rt.glob(p, cwd)));
2046
2094
  const files = [...new Set(lists.flat())];
2047
- return Promise.all(
2095
+ const facts = await Promise.all(
2048
2096
  files.sort().map(async (rel) => {
2049
2097
  const kind = kindOf(rel);
2050
2098
  try {
@@ -2055,6 +2103,38 @@ async function collectKitModuleFacts(rt, cwd, aliases) {
2055
2103
  }
2056
2104
  })
2057
2105
  );
2106
+ return arbitrateServerStoreWrites(rt, cwd, facts);
2107
+ }
2108
+ function moduleCandidates(repoPath) {
2109
+ if (repoPath.endsWith(".js")) return [repoPath, `${repoPath.slice(0, -3)}.ts`];
2110
+ if (repoPath.endsWith(".ts")) return [repoPath];
2111
+ return [`${repoPath}.ts`, `${repoPath}.js`, `${repoPath}/index.ts`, `${repoPath}/index.js`];
2112
+ }
2113
+ async function inMemoryExportsOf(rt, cwd, repoPath) {
2114
+ for (const rel of moduleCandidates(repoPath)) {
2115
+ try {
2116
+ if (!await rt.exists(rt.join(cwd, rel))) continue;
2117
+ return parseInMemoryExports(await rt.readFile(rt.join(cwd, rel)), rel);
2118
+ } catch {
2119
+ return /* @__PURE__ */ new Set();
2120
+ }
2121
+ }
2122
+ return /* @__PURE__ */ new Set();
2123
+ }
2124
+ async function arbitrateServerStoreWrites(rt, cwd, facts) {
2125
+ const targets = [...new Set(facts.flatMap((f) => f.pendingServerStoreWrites.map((w) => w.resolved)))];
2126
+ if (targets.length === 0) return facts;
2127
+ const byPath = new Map(
2128
+ await Promise.all(targets.map(async (t) => [t, await inMemoryExportsOf(rt, cwd, t)]))
2129
+ );
2130
+ return facts.map((f) => {
2131
+ const promoted = f.pendingServerStoreWrites.filter((w) => byPath.get(w.resolved)?.has(w.imported)).map((w) => ({ name: w.name, line: w.line, via: "set-call" }));
2132
+ if (promoted.length === 0) return f;
2133
+ return {
2134
+ ...f,
2135
+ importedStateWrites: [...f.importedStateWrites, ...promoted].sort((a, b) => a.line - b.line)
2136
+ };
2137
+ });
2058
2138
  }
2059
2139
 
2060
2140
  // src/config-object.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@svelte-vitals/core",
3
- "version": "0.31.1",
3
+ "version": "0.32.0",
4
4
  "description": "Shared, runtime-agnostic core for svelte-vitals (types, rule engine, scorer, reporter).",
5
5
  "type": "module",
6
6
  "license": "MIT",