@cavuno/board 4.15.0 → 4.16.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/doctor.js CHANGED
@@ -147,11 +147,83 @@ function summarize(results) {
147
147
  };
148
148
  }
149
149
 
150
- // src/doctor/cookie-conformance.ts
150
+ // src/doctor/analytics-surface.ts
151
151
  var import_node_fs2 = require("fs");
152
152
  var import_node_path2 = require("path");
153
+ var ANALYTICS = record("static.analytics-surface", 1);
154
+ var SOURCE_EXT = /\.(ts|tsx|js|jsx|mjs|cjs|astro|svelte|vue|mdx)$/;
155
+ var TEST_FILE = /\.(?:test|spec)\./;
156
+ var FORBIDDEN = [
157
+ { id: "window.Tinybird", re: /\bwindow\.Tinybird\b/ },
158
+ { id: "@tinybirdco/flock", re: /@tinybirdco\/flock/ },
159
+ { id: "CAVUNO_TRACKER_TOKEN", re: /\bCAVUNO_TRACKER_TOKEN\b/ }
160
+ ];
161
+ var SCAN_ROOTS = ["src", "app", "apps"];
162
+ var SKIP_DIR_NAMES = /* @__PURE__ */ new Set([
163
+ "node_modules",
164
+ "dist",
165
+ ".git",
166
+ ".turbo",
167
+ "coverage",
168
+ "paraglide"
169
+ ]);
170
+ function walkFiles(root, out) {
171
+ if (!(0, import_node_fs2.existsSync)(root)) return;
172
+ for (const entry of (0, import_node_fs2.readdirSync)(root)) {
173
+ if (SKIP_DIR_NAMES.has(entry)) continue;
174
+ const full = (0, import_node_path2.join)(root, entry);
175
+ const st = (0, import_node_fs2.statSync)(full);
176
+ if (st.isDirectory()) {
177
+ walkFiles(full, out);
178
+ continue;
179
+ }
180
+ if (SOURCE_EXT.test(entry) && !TEST_FILE.test(entry)) {
181
+ out.push(full);
182
+ }
183
+ }
184
+ }
185
+ function checkAnalyticsSurface(projectRoot) {
186
+ const files = [];
187
+ for (const name of SCAN_ROOTS) {
188
+ walkFiles((0, import_node_path2.join)(projectRoot, name), files);
189
+ }
190
+ if (files.length === 0) {
191
+ return ANALYTICS(
192
+ "skip",
193
+ "no src/, app/, or apps/ tree to scan for analytics surface"
194
+ );
195
+ }
196
+ const hits = [];
197
+ for (const file of files) {
198
+ let text;
199
+ try {
200
+ text = (0, import_node_fs2.readFileSync)(file, "utf8");
201
+ } catch {
202
+ continue;
203
+ }
204
+ for (const rule of FORBIDDEN) {
205
+ if (rule.re.test(text)) {
206
+ hits.push(`${(0, import_node_path2.relative)(projectRoot, file)}:${rule.id}`);
207
+ }
208
+ }
209
+ }
210
+ if (hits.length === 0) {
211
+ return ANALYTICS(
212
+ "pass",
213
+ "no Tinybird-branded analytics APIs in app source"
214
+ );
215
+ }
216
+ return ANALYTICS(
217
+ "fail",
218
+ `use @cavuno/board/analytics + CAVUNO_BOARD only \u2014 found ${hits.slice(0, 8).join(", ")}${hits.length > 8 ? ` (+${hits.length - 8} more)` : ""}`
219
+ );
220
+ }
221
+
222
+ // src/doctor/cookie-conformance.ts
223
+ var import_node_fs3 = require("fs");
224
+ var import_node_path3 = require("path");
153
225
  var COOKIE = record("static.cookie-codec", 1);
154
- var SOURCE_EXT = /\.(ts|tsx|js|jsx|mjs|cjs|astro|svelte|vue)$/;
226
+ var SOURCE_EXT2 = /\.(ts|tsx|js|jsx|mjs|cjs|astro|svelte|vue)$/;
155
227
  var REMEDIATION = "set cookies only through the SDK server cookie codec (buildCookie/buildClearCookie from @cavuno/board/server)";
156
228
  var SET_COOKIE_SINK = /set-cookie/i;
157
229
  var DOCUMENT_COOKIE_ASSIGN = /document\.cookie\s*\+?=(?!=)/i;
@@ -254,23 +326,23 @@ function hasGeneratedBanner(text) {
254
326
  function walkSourceFiles(dir, out) {
255
327
  let entries;
256
328
  try {
257
- entries = (0, import_node_fs2.readdirSync)(dir);
329
+ entries = (0, import_node_fs3.readdirSync)(dir);
258
330
  } catch {
259
331
  return;
260
332
  }
261
333
  for (const name of entries) {
262
334
  if (name === "node_modules" || name.startsWith(".")) continue;
263
335
  if (GENERATED_DIRS.has(name) || GENERATED_FILE.test(name)) continue;
264
- const full = (0, import_node_path2.join)(dir, name);
336
+ const full = (0, import_node_path3.join)(dir, name);
265
337
  let st;
266
338
  try {
267
- st = (0, import_node_fs2.statSync)(full);
339
+ st = (0, import_node_fs3.statSync)(full);
268
340
  } catch {
269
341
  continue;
270
342
  }
271
343
  if (st.isDirectory()) {
272
344
  walkSourceFiles(full, out);
273
- } else if (st.isFile() && SOURCE_EXT.test(name)) {
345
+ } else if (st.isFile() && SOURCE_EXT2.test(name)) {
274
346
  out.push(full);
275
347
  }
276
348
  }
@@ -280,8 +352,8 @@ function truncate(line, max = 120) {
280
352
  return t.length > max ? `${t.slice(0, max)}\u2026` : t;
281
353
  }
282
354
  function checkCookieCodecConformance(projectRoot) {
283
- const srcDir = (0, import_node_path2.join)(projectRoot, "src");
284
- if (!(0, import_node_fs2.existsSync)(srcDir)) {
355
+ const srcDir = (0, import_node_path3.join)(projectRoot, "src");
356
+ if (!(0, import_node_fs3.existsSync)(srcDir)) {
285
357
  return [
286
358
  COOKIE(
287
359
  "skip",
@@ -293,10 +365,10 @@ function checkCookieCodecConformance(projectRoot) {
293
365
  walkSourceFiles(srcDir, files);
294
366
  const findings = [];
295
367
  for (const file of files) {
296
- const rel = (0, import_node_path2.relative)(projectRoot, file).split("\\").join("/");
368
+ const rel = (0, import_node_path3.relative)(projectRoot, file).split("\\").join("/");
297
369
  let text;
298
370
  try {
299
- text = (0, import_node_fs2.readFileSync)(file, "utf8");
371
+ text = (0, import_node_fs3.readFileSync)(file, "utf8");
300
372
  } catch {
301
373
  continue;
302
374
  }
@@ -556,8 +628,8 @@ async function runReadProbes(fetchImpl, frontendUrl, seo) {
556
628
  }
557
629
 
558
630
  // src/doctor/run.ts
559
- var import_node_fs3 = require("fs");
560
- var import_node_path3 = require("path");
631
+ var import_node_fs4 = require("fs");
632
+ var import_node_path4 = require("path");
561
633
  var STATIC_API = record("static.api", 1);
562
634
  var STATIC_BOARD = record("static.board", 1);
563
635
  var STATIC_SKILLS = record("static.skills", 1);
@@ -635,8 +707,8 @@ var SKILL_ROOTS = [
635
707
  ".cursor/skills"
636
708
  ];
637
709
  function checkSkillsFreshness(projectRoot) {
638
- const roots = SKILL_ROOTS.map((root) => (0, import_node_path3.join)(projectRoot, root)).filter(
639
- (root) => (0, import_node_fs3.existsSync)(root)
710
+ const roots = SKILL_ROOTS.map((root) => (0, import_node_path4.join)(projectRoot, root)).filter(
711
+ (root) => (0, import_node_fs4.existsSync)(root)
640
712
  );
641
713
  if (roots.length === 0) {
642
714
  return STATIC_SKILLS(
@@ -649,10 +721,10 @@ function checkSkillsFreshness(projectRoot) {
649
721
  const seen = /* @__PURE__ */ new Set();
650
722
  for (const root of roots) {
651
723
  for (const skill of corpus.skills) {
652
- const copied = (0, import_node_path3.join)(root, skill.name, "SKILL.md");
653
- if (!(0, import_node_fs3.existsSync)(copied)) continue;
724
+ const copied = (0, import_node_path4.join)(root, skill.name, "SKILL.md");
725
+ if (!(0, import_node_fs4.existsSync)(copied)) continue;
654
726
  seen.add(skill.name);
655
- if ((0, import_node_fs3.readFileSync)(copied, "utf8") !== skill.content) {
727
+ if ((0, import_node_fs4.readFileSync)(copied, "utf8") !== skill.content) {
656
728
  stale.add(skill.name);
657
729
  }
658
730
  }
@@ -700,10 +772,10 @@ async function runDoctor(options) {
700
772
  } else {
701
773
  results.push(STATIC_BOARD("skip", "env checks failed \u2014 fix them first"));
702
774
  }
703
- results.push(checkSkillsFreshness(options.projectRoot ?? process.cwd()));
704
- results.push(
705
- ...checkCookieCodecConformance(options.projectRoot ?? process.cwd())
706
- );
775
+ const projectRoot = options.projectRoot ?? process.cwd();
776
+ results.push(checkSkillsFreshness(projectRoot));
777
+ results.push(...checkCookieCodecConformance(projectRoot));
778
+ results.push(checkAnalyticsSurface(projectRoot));
707
779
  results.push(
708
780
  ...options.frontendUrl ? await runReadProbes(fetchImpl, options.frontendUrl, seo) : skipReadProbes("no --frontend <url> given")
709
781
  );
package/dist/doctor.mjs CHANGED
@@ -117,11 +117,83 @@ function summarize(results) {
117
117
  };
118
118
  }
119
119
 
120
- // src/doctor/cookie-conformance.ts
120
+ // src/doctor/analytics-surface.ts
121
121
  import { existsSync, readdirSync as readdirSync2, readFileSync as readFileSync2, statSync } from "fs";
122
122
  import { join, relative } from "path";
123
+ var ANALYTICS = record("static.analytics-surface", 1);
124
+ var SOURCE_EXT = /\.(ts|tsx|js|jsx|mjs|cjs|astro|svelte|vue|mdx)$/;
125
+ var TEST_FILE = /\.(?:test|spec)\./;
126
+ var FORBIDDEN = [
127
+ { id: "window.Tinybird", re: /\bwindow\.Tinybird\b/ },
128
+ { id: "@tinybirdco/flock", re: /@tinybirdco\/flock/ },
129
+ { id: "CAVUNO_TRACKER_TOKEN", re: /\bCAVUNO_TRACKER_TOKEN\b/ }
130
+ ];
131
+ var SCAN_ROOTS = ["src", "app", "apps"];
132
+ var SKIP_DIR_NAMES = /* @__PURE__ */ new Set([
133
+ "node_modules",
134
+ "dist",
135
+ ".git",
136
+ ".turbo",
137
+ "coverage",
138
+ "paraglide"
139
+ ]);
140
+ function walkFiles(root, out) {
141
+ if (!existsSync(root)) return;
142
+ for (const entry of readdirSync2(root)) {
143
+ if (SKIP_DIR_NAMES.has(entry)) continue;
144
+ const full = join(root, entry);
145
+ const st = statSync(full);
146
+ if (st.isDirectory()) {
147
+ walkFiles(full, out);
148
+ continue;
149
+ }
150
+ if (SOURCE_EXT.test(entry) && !TEST_FILE.test(entry)) {
151
+ out.push(full);
152
+ }
153
+ }
154
+ }
155
+ function checkAnalyticsSurface(projectRoot) {
156
+ const files = [];
157
+ for (const name of SCAN_ROOTS) {
158
+ walkFiles(join(projectRoot, name), files);
159
+ }
160
+ if (files.length === 0) {
161
+ return ANALYTICS(
162
+ "skip",
163
+ "no src/, app/, or apps/ tree to scan for analytics surface"
164
+ );
165
+ }
166
+ const hits = [];
167
+ for (const file of files) {
168
+ let text;
169
+ try {
170
+ text = readFileSync2(file, "utf8");
171
+ } catch {
172
+ continue;
173
+ }
174
+ for (const rule of FORBIDDEN) {
175
+ if (rule.re.test(text)) {
176
+ hits.push(`${relative(projectRoot, file)}:${rule.id}`);
177
+ }
178
+ }
179
+ }
180
+ if (hits.length === 0) {
181
+ return ANALYTICS(
182
+ "pass",
183
+ "no Tinybird-branded analytics APIs in app source"
184
+ );
185
+ }
186
+ return ANALYTICS(
187
+ "fail",
188
+ `use @cavuno/board/analytics + CAVUNO_BOARD only \u2014 found ${hits.slice(0, 8).join(", ")}${hits.length > 8 ? ` (+${hits.length - 8} more)` : ""}`
189
+ );
190
+ }
191
+
192
+ // src/doctor/cookie-conformance.ts
193
+ import { existsSync as existsSync2, readdirSync as readdirSync3, readFileSync as readFileSync3, statSync as statSync2 } from "fs";
194
+ import { join as join2, relative as relative2 } from "path";
123
195
  var COOKIE = record("static.cookie-codec", 1);
124
- var SOURCE_EXT = /\.(ts|tsx|js|jsx|mjs|cjs|astro|svelte|vue)$/;
196
+ var SOURCE_EXT2 = /\.(ts|tsx|js|jsx|mjs|cjs|astro|svelte|vue)$/;
125
197
  var REMEDIATION = "set cookies only through the SDK server cookie codec (buildCookie/buildClearCookie from @cavuno/board/server)";
126
198
  var SET_COOKIE_SINK = /set-cookie/i;
127
199
  var DOCUMENT_COOKIE_ASSIGN = /document\.cookie\s*\+?=(?!=)/i;
@@ -224,23 +296,23 @@ function hasGeneratedBanner(text) {
224
296
  function walkSourceFiles(dir, out) {
225
297
  let entries;
226
298
  try {
227
- entries = readdirSync2(dir);
299
+ entries = readdirSync3(dir);
228
300
  } catch {
229
301
  return;
230
302
  }
231
303
  for (const name of entries) {
232
304
  if (name === "node_modules" || name.startsWith(".")) continue;
233
305
  if (GENERATED_DIRS.has(name) || GENERATED_FILE.test(name)) continue;
234
- const full = join(dir, name);
306
+ const full = join2(dir, name);
235
307
  let st;
236
308
  try {
237
- st = statSync(full);
309
+ st = statSync2(full);
238
310
  } catch {
239
311
  continue;
240
312
  }
241
313
  if (st.isDirectory()) {
242
314
  walkSourceFiles(full, out);
243
- } else if (st.isFile() && SOURCE_EXT.test(name)) {
315
+ } else if (st.isFile() && SOURCE_EXT2.test(name)) {
244
316
  out.push(full);
245
317
  }
246
318
  }
@@ -250,8 +322,8 @@ function truncate(line, max = 120) {
250
322
  return t.length > max ? `${t.slice(0, max)}\u2026` : t;
251
323
  }
252
324
  function checkCookieCodecConformance(projectRoot) {
253
- const srcDir = join(projectRoot, "src");
254
- if (!existsSync(srcDir)) {
325
+ const srcDir = join2(projectRoot, "src");
326
+ if (!existsSync2(srcDir)) {
255
327
  return [
256
328
  COOKIE(
257
329
  "skip",
@@ -263,10 +335,10 @@ function checkCookieCodecConformance(projectRoot) {
263
335
  walkSourceFiles(srcDir, files);
264
336
  const findings = [];
265
337
  for (const file of files) {
266
- const rel = relative(projectRoot, file).split("\\").join("/");
338
+ const rel = relative2(projectRoot, file).split("\\").join("/");
267
339
  let text;
268
340
  try {
269
- text = readFileSync2(file, "utf8");
341
+ text = readFileSync3(file, "utf8");
270
342
  } catch {
271
343
  continue;
272
344
  }
@@ -526,8 +598,8 @@ async function runReadProbes(fetchImpl, frontendUrl, seo) {
526
598
  }
527
599
 
528
600
  // src/doctor/run.ts
529
- import { existsSync as existsSync2, readFileSync as readFileSync3 } from "fs";
530
- import { join as join2 } from "path";
601
+ import { existsSync as existsSync3, readFileSync as readFileSync4 } from "fs";
602
+ import { join as join3 } from "path";
531
603
  var STATIC_API = record("static.api", 1);
532
604
  var STATIC_BOARD = record("static.board", 1);
533
605
  var STATIC_SKILLS = record("static.skills", 1);
@@ -605,8 +677,8 @@ var SKILL_ROOTS = [
605
677
  ".cursor/skills"
606
678
  ];
607
679
  function checkSkillsFreshness(projectRoot) {
608
- const roots = SKILL_ROOTS.map((root) => join2(projectRoot, root)).filter(
609
- (root) => existsSync2(root)
680
+ const roots = SKILL_ROOTS.map((root) => join3(projectRoot, root)).filter(
681
+ (root) => existsSync3(root)
610
682
  );
611
683
  if (roots.length === 0) {
612
684
  return STATIC_SKILLS(
@@ -619,10 +691,10 @@ function checkSkillsFreshness(projectRoot) {
619
691
  const seen = /* @__PURE__ */ new Set();
620
692
  for (const root of roots) {
621
693
  for (const skill of corpus.skills) {
622
- const copied = join2(root, skill.name, "SKILL.md");
623
- if (!existsSync2(copied)) continue;
694
+ const copied = join3(root, skill.name, "SKILL.md");
695
+ if (!existsSync3(copied)) continue;
624
696
  seen.add(skill.name);
625
- if (readFileSync3(copied, "utf8") !== skill.content) {
697
+ if (readFileSync4(copied, "utf8") !== skill.content) {
626
698
  stale.add(skill.name);
627
699
  }
628
700
  }
@@ -670,10 +742,10 @@ async function runDoctor(options) {
670
742
  } else {
671
743
  results.push(STATIC_BOARD("skip", "env checks failed \u2014 fix them first"));
672
744
  }
673
- results.push(checkSkillsFreshness(options.projectRoot ?? process.cwd()));
674
- results.push(
675
- ...checkCookieCodecConformance(options.projectRoot ?? process.cwd())
676
- );
745
+ const projectRoot = options.projectRoot ?? process.cwd();
746
+ results.push(checkSkillsFreshness(projectRoot));
747
+ results.push(...checkCookieCodecConformance(projectRoot));
748
+ results.push(checkAnalyticsSurface(projectRoot));
677
749
  results.push(
678
750
  ...options.frontendUrl ? await runReadProbes(fetchImpl, options.frontendUrl, seo) : skipReadProbes("no --frontend <url> given")
679
751
  );
package/dist/index.d.mts CHANGED
@@ -143,7 +143,7 @@ declare function paginate<Q extends Record<string, unknown>, P extends PageShape
143
143
  * constant because the package is platform-neutral and cannot read
144
144
  * package.json at runtime.
145
145
  */
146
- declare const SDK_VERSION = "4.15.0";
146
+ declare const SDK_VERSION = "4.16.0";
147
147
 
148
148
  type SavedJob = Schemas['SavedJob'];
149
149
  type SavedJobsListQuery = {
package/dist/index.d.ts CHANGED
@@ -143,7 +143,7 @@ declare function paginate<Q extends Record<string, unknown>, P extends PageShape
143
143
  * constant because the package is platform-neutral and cannot read
144
144
  * package.json at runtime.
145
145
  */
146
- declare const SDK_VERSION = "4.15.0";
146
+ declare const SDK_VERSION = "4.16.0";
147
147
 
148
148
  type SavedJob = Schemas['SavedJob'];
149
149
  type SavedJobsListQuery = {
package/dist/index.js CHANGED
@@ -347,7 +347,7 @@ async function clearSession(storage) {
347
347
  }
348
348
 
349
349
  // src/version.ts
350
- var SDK_VERSION = "4.15.0";
350
+ var SDK_VERSION = "4.16.0";
351
351
 
352
352
  // src/client.ts
353
353
  function isRawBody(body) {
package/dist/index.mjs CHANGED
@@ -39,7 +39,7 @@ function toSearchParams(query) {
39
39
  }
40
40
 
41
41
  // src/version.ts
42
- var SDK_VERSION = "4.15.0";
42
+ var SDK_VERSION = "4.16.0";
43
43
 
44
44
  // src/client.ts
45
45
  function isRawBody(body) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cavuno/board",
3
- "version": "4.15.0",
3
+ "version": "4.16.0",
4
4
  "description": "Build custom job boards and careers pages with Cavuno’s TypeScript SDK.",
5
5
  "keywords": [
6
6
  "api-client",
@@ -154,6 +154,16 @@
154
154
  "default": "./dist/well-known.js"
155
155
  }
156
156
  },
157
+ "./analytics": {
158
+ "import": {
159
+ "types": "./dist/analytics.d.mts",
160
+ "default": "./dist/analytics.mjs"
161
+ },
162
+ "require": {
163
+ "types": "./dist/analytics.d.ts",
164
+ "default": "./dist/analytics.js"
165
+ }
166
+ },
157
167
  "./seo-files": {
158
168
  "import": {
159
169
  "types": "./dist/seo-files.d.mts",
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "4.15.0",
2
+ "version": "4.16.0",
3
3
  "skills": [
4
4
  {
5
5
  "name": "cavuno-board-account",