@strifeapp/astro 1.2.0 → 1.4.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/README.md CHANGED
@@ -10,6 +10,7 @@ Official [Strife](https://strife.app) integration for [Astro](https://astro.buil
10
10
  - [Requirements](#requirements)
11
11
  - [Usage](#usage)
12
12
  - [Reading content](#reading-content)
13
+ - [Telemetry: `<Insights />`](#telemetry-insights)
13
14
  - [TypeScript: typing `strife:store`](#typescript-typing-strifestore)
14
15
  - [Direct Vite plugin](#direct-vite-plugin)
15
16
  - [Configuration](#configuration)
@@ -68,14 +69,37 @@ Read from the store anywhere in your site through the `strife:store` virtual mod
68
69
  import { store } from 'strife:store';
69
70
 
70
71
  const session = store.openSession();
71
- // The integration deploys a `Content_ByUrl` index for URL-based lookups.
72
+ // The integration deploys a `Content/ByUrl` index for URL-based lookups.
72
73
  const page = await session
73
- .query({ indexName: 'Content_ByUrl' })
74
+ .query({ indexName: 'Content/ByUrl' })
74
75
  .whereEquals('url', Astro.url.pathname)
75
76
  .firstOrNull();
76
77
  ---
77
78
  ```
78
79
 
80
+ ### Telemetry: `<Insights />`
81
+
82
+ Drop-in Web Vitals + accessibility telemetry. Add it once to your layout `<head>`, the same way you'd add Astro's `<ClientRouter />`:
83
+
84
+ ```astro
85
+ ---
86
+ import Insights from '@strifeapp/astro/Insights.astro';
87
+ ---
88
+ <head>
89
+
90
+ <Insights />
91
+ </head>
92
+ ```
93
+
94
+ On every page load it reports Core Web Vitals (LCP, INP, CLS, FCP, TTFB) and, when the browser is idle, an [axe-core](https://github.com/dequelabs/axe-core) accessibility audit (re-running on Astro View Transitions). Both are sent with `navigator.sendBeacon` to the hosted Strife insights API and stored as RavenDB time series on the page's content document. The beacon carries your **public team id** — resolved server-side from your Strife env, so no secret reaches the browser — letting the API attribute metrics to your workspace and validate the request against your registered domains.
95
+
96
+ | Prop | Type | Default | Description |
97
+ | --- | --- | --- | --- |
98
+ | `endpoint` | `string` | `https://api.strife.app/insights/collect` | Override the collection endpoint. |
99
+ | `accessibility` | `boolean` | `true` | Set `false` to skip the axe-core audit (Web Vitals still reported). |
100
+
101
+ > The API only stores a beacon when its `Origin` is one of the team's registered domains. For local development, run the API in `Development` and add `localhost` to your team's `Domains`.
102
+
79
103
  ### TypeScript: typing `strife:store`
80
104
 
81
105
  Typed ambient declarations for the `strife:store` virtual module are not bundled in this release. Until a typed surface is published, add your own declaration (e.g. in `src/env.d.ts`):
@@ -168,11 +192,11 @@ On `astro:config:setup` the integration registers the `STRIFE_*` env schema and
168
192
 
169
193
  1. Resolves configuration at runtime via `getSecret` (never baked into the bundle).
170
194
  2. Connects to RavenDB using the resolved URLs, database, and client certificate.
171
- 3. Deploys a `Content_ByUrl` multi-map index (`deploymentMode: 'Rolling'`) for URL-based content lookup.
195
+ 3. Deploys a `Content/ByUrl` multi-map index (`deploymentMode: 'Rolling'`) for URL-based content lookup.
172
196
  4. Bulk-inserts the configured collections into the `Templates` collection.
173
197
  5. Exposes the initialized RavenDB `DocumentStore` through the `strife:store` virtual module.
174
198
 
175
- > The `Content_ByUrl` index is deployed with a bundled helper source (`localized-content-index.js`) that runs inside RavenDB's Jint engine. It is intentionally plain ES5 — do not transpile it.
199
+ > The `Content/ByUrl` index is deployed with a bundled helper source (`localized-content-index.js`) that runs inside RavenDB's Jint engine. It is intentionally plain ES5 — do not transpile it.
176
200
 
177
201
  ## License
178
202
 
@@ -0,0 +1,243 @@
1
+ ---
2
+ /**
3
+ * Strife Insights — drop-in Web Vitals + accessibility telemetry.
4
+ *
5
+ * Add once to your layout <head>, the same way you'd add Astro's <ClientRouter />:
6
+ *
7
+ * ---
8
+ * import Insights from '@strifeapp/astro/Insights.astro';
9
+ * ---
10
+ * <head>…<Insights /></head>
11
+ *
12
+ * Real visitors report Core Web Vitals (LCP, INP, CLS, FCP, TTFB), batched into a
13
+ * single beacon when the page is backgrounded. The heavier axe-core accessibility
14
+ * audit runs ONLY inside the Strife live preview (edit mode), so axe-core is never
15
+ * downloaded or run by a real visitor. Both are beaconed to the central Strife
16
+ * insights endpoint and stored as time series, tagged by device class (mobile/desktop).
17
+ *
18
+ * The beacon carries a PUBLIC `site` id (this team's id, resolved server-side from
19
+ * the same Strife env the integration already reads) so the endpoint can attribute
20
+ * metrics to your workspace and validate the request Origin against your registered
21
+ * domains. No secret is ever exposed to the browser.
22
+ */
23
+ // @ts-ignore — `astro:env/server` is a virtual module Astro provides in the
24
+ // consumer's runtime/build (see the integration's env schema).
25
+ import { getSecret } from 'astro:env/server';
26
+ import { decodeSecrets } from '@strifeapp/strife/secrets';
27
+
28
+ export interface Props {
29
+ /** Override the collection endpoint. Defaults to the hosted Strife API. */
30
+ endpoint?: string;
31
+ /** Set false to skip the axe-core accessibility audit (Web Vitals still reported). */
32
+ accessibility?: boolean;
33
+ }
34
+
35
+ const {
36
+ endpoint = 'https://api.strife.app/insights/collect',
37
+ accessibility = true,
38
+ } = Astro.props;
39
+
40
+ /**
41
+ * Resolve this team's PUBLIC id from the same sources the integration reads: the
42
+ * consolidated STRIFE_SECRET blob (teamId), the legacy TEAM_ID env, or — last
43
+ * resort — the database name (`wieldy_{id}`). Returns null when no Strife env is
44
+ * configured, in which case the component renders nothing.
45
+ */
46
+ function resolveSiteId(): string | null {
47
+ const blob = getSecret('STRIFE_SECRET');
48
+ if (blob) {
49
+ try {
50
+ const packed = decodeSecrets(blob);
51
+ if (packed?.teamId) return packed.teamId;
52
+ } catch {
53
+ // Malformed blob — fall through to the individual vars.
54
+ }
55
+ }
56
+ const teamId = getSecret('TEAM_ID');
57
+ if (teamId) return teamId;
58
+ const database = getSecret('STRIFE_DATABASE');
59
+ const match = database?.match(/^wieldy_(.+)$/);
60
+ return match ? match[1] : null;
61
+ }
62
+
63
+ const site = resolveSiteId();
64
+
65
+ // `editMode` is true inside the Strife live preview (set by the edit-mode
66
+ // middleware). We split the work by audience: Web Vitals run only for REAL visitors
67
+ // (they need real-user device/network/interaction variance), while the heavy
68
+ // axe-core accessibility audit runs only in the editor's live preview — a11y is
69
+ // deterministic per content version, so the editor is the right place for it, and it
70
+ // keeps axe-core off every real visitor entirely. Without the middleware, editMode
71
+ // defaults to false, so plain sites get Web Vitals exactly as before.
72
+ const editMode = Astro.locals.editMode ?? false;
73
+ ---
74
+
75
+ {site && (
76
+ <Fragment>
77
+ <script is:inline define:vars={{ site, endpoint, accessibility, editMode }}>
78
+ window.__strifeInsights = { site, endpoint, accessibility, editMode };
79
+ </script>
80
+ <script>
81
+ import { onLCP, onINP, onCLS, onFCP, onTTFB } from 'web-vitals';
82
+
83
+ const cfg = (window as any).__strifeInsights || {};
84
+ const ENDPOINT: string = cfg.endpoint;
85
+ const SITE: string = cfg.site;
86
+ const RUN_A11Y: boolean = cfg.accessibility !== false;
87
+ const EDIT_MODE: boolean = cfg.editMode === true;
88
+
89
+ /** Beacon a payload to the collection endpoint (fire-and-forget). */
90
+ function send(payload: Record<string, unknown>) {
91
+ const body = JSON.stringify({ site: SITE, ...payload });
92
+ try {
93
+ // Send as a plain string → text/plain, a CORS-safelisted content type.
94
+ // That keeps the cross-origin beacon a "simple request" (no preflight),
95
+ // so it is delivered from ANY registered custom domain — not just the
96
+ // origins in the API's global CORS list. The server parses the body
97
+ // itself; we never read the response, so CORS never blocks the write.
98
+ if (navigator.sendBeacon && navigator.sendBeacon(ENDPOINT, body)) {
99
+ return;
100
+ }
101
+ } catch {
102
+ // Fall through to fetch.
103
+ }
104
+ // No JSON content-type header here either, so the fallback stays
105
+ // preflight-free (a string body defaults to text/plain).
106
+ fetch(ENDPOINT, {
107
+ method: 'POST',
108
+ body,
109
+ keepalive: true,
110
+ }).catch(() => {});
111
+ }
112
+
113
+ function sendAccessibilityMetrics(
114
+ metrics: Record<string, number>,
115
+ tag: string,
116
+ violations: unknown[],
117
+ ) {
118
+ send({ page: window.location.pathname, metrics, tag, violations });
119
+ }
120
+
121
+ // Collect the finalised Core Web Vitals and send them as ONE batched beacon when
122
+ // the page is backgrounded/unloaded — the standard web-vitals RUM pattern.
123
+ // (reportAllChanges defaults to false, so each metric reports once.) Far fewer
124
+ // beacons + time-series writes than reporting on every change.
125
+ function initWebVitals() {
126
+ const speed: string | undefined = (navigator as any).connection?.effectiveType;
127
+ // Tag each metric by device class so Experience scores can be sliced by device
128
+ // in the Studio — same convention the accessibility audit uses.
129
+ const tag = navigator.userAgent.includes('Mobile') ? 'mobile' : 'desktop';
130
+ const metrics: Record<string, number> = {};
131
+ const add = (m: { name: string; value: number }) => {
132
+ metrics[m.name] = m.value;
133
+ };
134
+ onLCP(add);
135
+ onINP(add);
136
+ onCLS(add);
137
+ onFCP(add);
138
+ onTTFB(add);
139
+
140
+ const flush = () => {
141
+ const names = Object.keys(metrics);
142
+ if (names.length === 0) return;
143
+ // Read the pathname at flush time, not at init time: on Astro View Transition
144
+ // sites the URL changes via soft nav without re-running initWebVitals, so this
145
+ // attributes the batch to the page actually being left rather than the first one.
146
+ const page = window.location.pathname;
147
+ const batch: Record<string, number> = {};
148
+ for (const name of names) {
149
+ batch[name] = metrics[name];
150
+ delete metrics[name]; // send each metric at most once across flushes
151
+ }
152
+ send({ page, metrics: batch, speed, tag });
153
+ };
154
+ addEventListener('visibilitychange', () => {
155
+ if (document.visibilityState === 'hidden') flush();
156
+ });
157
+ addEventListener('pagehide', flush);
158
+ }
159
+
160
+ async function runAccessibilityAudit() {
161
+ try {
162
+ const axe = await import('axe-core');
163
+ const results = await axe.default.run(document, { resultTypes: ['violations'] });
164
+
165
+ const byImpact = { critical: 0, serious: 0, moderate: 0, minor: 0 };
166
+ for (const violation of results.violations) {
167
+ const impact = violation.impact as keyof typeof byImpact;
168
+ if (impact && impact in byImpact) byImpact[impact] += violation.nodes.length;
169
+ }
170
+ const total = byImpact.critical + byImpact.serious + byImpact.moderate + byImpact.minor;
171
+ const penalty =
172
+ byImpact.critical * 10 + byImpact.serious * 5 + byImpact.moderate * 2 + byImpact.minor * 1;
173
+ const score = Math.max(0, 100 - penalty);
174
+
175
+ // Compact, capped violation details for the "what's actually wrong" list.
176
+ // axe-core categories live in the `cat.*` tag (e.g. `cat.color` → `color`).
177
+ const details = results.violations
178
+ .map((v) => {
179
+ const cat = (v.tags || []).find((t: string) => t.indexOf('cat.') === 0);
180
+ const firstTarget = (v.nodes[0] as any)?.target;
181
+ return {
182
+ id: v.id,
183
+ impact: v.impact || 'minor',
184
+ category: cat ? cat.slice(4) : 'other',
185
+ help: v.help,
186
+ helpUrl: v.helpUrl,
187
+ count: v.nodes.length,
188
+ sample: Array.isArray(firstTarget) ? firstTarget.join(' ') : '',
189
+ };
190
+ })
191
+ .slice(0, 50);
192
+
193
+ sendAccessibilityMetrics(
194
+ {
195
+ A11Y_SCORE: score,
196
+ A11Y_VIOLATIONS: total,
197
+ A11Y_CRITICAL: byImpact.critical,
198
+ A11Y_SERIOUS: byImpact.serious,
199
+ A11Y_MODERATE: byImpact.moderate,
200
+ A11Y_MINOR: byImpact.minor,
201
+ },
202
+ navigator.userAgent.includes('Mobile') ? 'mobile' : 'desktop',
203
+ details,
204
+ );
205
+ } catch (error) {
206
+ // Best-effort — accessibility metrics never break the page.
207
+ console.warn('[Insights] Accessibility audit failed:', error);
208
+ }
209
+ }
210
+
211
+ function scheduleAccessibilityAudit() {
212
+ if ('requestIdleCallback' in window) {
213
+ (window as any).requestIdleCallback(() => runAccessibilityAudit(), { timeout: 10000 });
214
+ } else {
215
+ setTimeout(runAccessibilityAudit, 3000);
216
+ }
217
+ }
218
+
219
+ // Track audited pages to avoid duplicate audits on soft navigations.
220
+ const auditedPages = new Set<string>();
221
+
222
+ function auditOnce() {
223
+ if (!RUN_A11Y) return;
224
+ const path = window.location.pathname;
225
+ if (auditedPages.has(path)) return;
226
+ auditedPages.add(path);
227
+ scheduleAccessibilityAudit();
228
+ }
229
+
230
+ if (EDIT_MODE) {
231
+ // In the Strife live preview: only the accessibility audit runs. The editor is
232
+ // the right place for the heavy axe-core scan (a11y is deterministic per content
233
+ // version), and it keeps axe-core off every real visitor.
234
+ auditOnce();
235
+ // Astro View Transitions: re-audit the newly loaded page.
236
+ document.addEventListener('astro:page-load', auditOnce);
237
+ } else {
238
+ // A real visitor: Web Vitals only, batched on pagehide. No axe-core download/run.
239
+ initWebVitals();
240
+ }
241
+ </script>
242
+ </Fragment>
243
+ )}
package/dist/edit-mode.js CHANGED
@@ -1,71 +1,36 @@
1
- import { getSecret as c } from "astro:env/server";
2
- import { jwtVerify as l } from "jose";
3
- const m = "v1", f = /^[A-Za-z0-9_-]+$/;
4
- class s extends Error {
5
- constructor(e) {
6
- super(`STRIFE_SECRET: ${e}`), this.name = "SecretsDecodeError";
7
- }
8
- }
9
- function u(t) {
10
- const e = t.split(".");
11
- if (e.length !== 3)
12
- throw new s(
13
- `expected 3 dot-separated sections, got ${e.length}`
14
- );
15
- const [n, i, d] = e;
16
- if (n !== m) {
17
- if (/^v\d+$/.test(n)) return null;
18
- throw new s("unrecognised format (missing version prefix)");
19
- }
20
- if (!f.test(i))
21
- throw new s("meta section is not valid base64url");
22
- if (!f.test(d))
23
- throw new s("cert section is not valid base64url");
24
- let o;
25
- try {
26
- o = JSON.parse(Buffer.from(i, "base64url").toString("utf8"));
27
- } catch {
28
- throw new s("meta section is not valid JSON");
29
- }
30
- if (typeof o != "object" || o === null || !Array.isArray(o.urls) || typeof o.database != "string")
31
- throw new s("meta section missing required fields (urls, database)");
32
- const r = o, a = {
33
- urls: r.urls,
34
- database: r.database,
35
- certificate: Buffer.from(d, "base64url")
36
- };
37
- return r.password !== void 0 && (a.password = r.password), r.type !== void 0 && (a.type = r.type), r.teamId !== void 0 && (a.teamId = r.teamId), r.previewSecret !== void 0 && (a.previewSecret = r.previewSecret), a;
38
- }
39
- async function p(t, e, n) {
1
+ import { getSecret as n } from "astro:env/server";
2
+ import { jwtVerify as c } from "jose";
3
+ import { decodeSecrets as o } from "@strifeapp/strife/secrets";
4
+ async function d(e, t, r) {
40
5
  try {
41
- const { payload: i } = await l(t, new TextEncoder().encode(e), {
6
+ const { payload: a } = await c(e, new TextEncoder().encode(t), {
42
7
  algorithms: ["HS256"]
43
8
  });
44
- return i.workspace === n;
9
+ return a.workspace === r;
45
10
  } catch {
46
11
  return !1;
47
12
  }
48
13
  }
49
- function v(t) {
50
- if (t.blob)
14
+ function i(e) {
15
+ if (e.blob)
51
16
  try {
52
- const e = u(t.blob);
53
- if (e?.previewSecret && e.teamId)
54
- return { secret: e.previewSecret, teamId: e.teamId };
17
+ const t = o(e.blob);
18
+ if (t?.previewSecret && t.teamId)
19
+ return { secret: t.previewSecret, teamId: t.teamId };
55
20
  } catch {
56
21
  }
57
- return t.envSecret && t.envTeamId ? { secret: t.envSecret, teamId: t.envTeamId } : null;
22
+ return e.envSecret && e.envTeamId ? { secret: e.envSecret, teamId: e.envTeamId } : null;
58
23
  }
59
- async function E(t) {
60
- const e = t.url.searchParams.get("token");
61
- if (!e) return !1;
62
- const n = v({
63
- blob: c("STRIFE_SECRET"),
64
- envSecret: c("SECRET"),
65
- envTeamId: c("TEAM_ID")
24
+ async function S(e) {
25
+ const t = e.url.searchParams.get("token");
26
+ if (!t) return !1;
27
+ const r = i({
28
+ blob: n("STRIFE_SECRET"),
29
+ envSecret: n("SECRET"),
30
+ envTeamId: n("TEAM_ID")
66
31
  });
67
- return n ? p(e, n.secret, n.teamId) : !1;
32
+ return r ? d(t, r.secret, r.teamId) : !1;
68
33
  }
69
34
  export {
70
- E as editMode
35
+ S as editMode
71
36
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAI5C,MAAM,WAAW,kBAAmB,SAAQ,YAAY;IACtD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,GAAG,CAAC;QACZ,UAAU,MAAM;YACd;;;;;eAKG;YACH,QAAQ,CAAC,EAAE,OAAO,CAAC;SACpB;KACF;CACF;AAMD,MAAM,CAAC,OAAO,UAAU,iBAAiB,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,gBAAgB,CAsDxF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAI5C,MAAM,WAAW,kBAAmB,SAAQ,YAAY;IACtD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,GAAG,CAAC;QACZ,UAAU,MAAM;YACd;;;;;eAKG;YACH,QAAQ,CAAC,EAAE,OAAO,CAAC;SACpB;KACF;CACF;AAMD,MAAM,CAAC,OAAO,UAAU,iBAAiB,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,gBAAgB,CAwDxF"}
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { envField as e } from "astro/config";
2
- import { v as c } from "./vite-plugin-strife-store-Tvau120y.js";
2
+ import { v as c } from "./vite-plugin-strife-store-B-B_HXbA.js";
3
3
  const i = {
4
4
  type: "pfx"
5
5
  };
@@ -1,29 +1,22 @@
1
- import { readFileSync as l } from "fs";
2
- import { dirname as f, resolve as u } from "path";
3
- import { fileURLToPath as d } from "url";
4
- import r from "serialize-javascript";
5
- const s = "strife:store", o = "\0" + s;
6
- function h(i) {
7
- const n = d(import.meta.url), a = f(n), c = l(
8
- u(a, "./localized-content-index.js"),
9
- "utf-8"
10
- ), e = { ...i };
1
+ import a from "serialize-javascript";
2
+ const s = "strife:store", r = "\0" + s;
3
+ function n(o) {
4
+ const e = { ...o };
11
5
  return delete e.certificate, delete e.password, {
12
6
  name: "strife:store",
13
7
  resolveId(t) {
14
8
  if (t === s)
15
- return o;
9
+ return r;
16
10
  },
17
11
  load(t) {
18
- if (t === o)
12
+ if (t === r)
19
13
  return `
20
- import { DocumentStore, AbstractJavaScriptMultiMapIndexCreationTask } from "ravendb";
14
+ import { DocumentStore } from "ravendb";
21
15
  import { getSecret } from "astro:env/server";
22
16
 
23
- const indexMappingSource = ${r(c)};
24
- const defaultConfig = ${r(e)};
17
+ const defaultConfig = ${a(e)};
25
18
 
26
- // --- STRIFE_SECRET decode (keep in sync with src/secrets-codec.ts; format v1.<meta>.<cert>) ---
19
+ // --- STRIFE_SECRET decode (keep in sync with @strifeapp/strife/secrets; format v1.<meta>.<cert>) ---
27
20
  function decodeStrifeSecrets(value) {
28
21
  const sections = value.split('.');
29
22
  if (sections.length !== 3) {
@@ -99,45 +92,14 @@ function h(i) {
99
92
  authOptions || undefined,
100
93
  ).initialize();
101
94
 
102
- class Content_ByUrl extends AbstractJavaScriptMultiMapIndexCreationTask {
103
- constructor() {
104
- super();
105
-
106
- this.additionalSources = {"Helper": indexMappingSource};
107
-
108
- const collections = (defaultConfig.collections && defaultConfig.collections.length)
109
- ? defaultConfig.collections
110
- : ['Posts'];
111
-
112
- for (const collection of collections) {
113
- const name = typeof collection === 'string' ? collection : collection.name;
114
- this.map(name, function (doc) { return mapDocument(doc); });
115
- }
116
-
117
- this.deploymentMode = 'Rolling';
118
- this.searchEngineType = 'Lucene';
119
- }
120
- }
121
-
122
- console.log('Deploying index...');
123
-
124
- await store.executeIndex(new Content_ByUrl());
125
-
126
- const bulkInsert = store.bulkInsert();
127
-
128
- if (defaultConfig.collections && defaultConfig.collections.length) {
129
- for (const collection of defaultConfig.collections) {
130
- await bulkInsert.store(collection, 'templates/' + collection.name, { '@collection': 'Templates' });
131
- }
132
- }
133
-
134
- await bulkInsert.finish();
135
-
95
+ // Content/ByUrl + templates are deployed by 'strife push' (the versioned,
96
+ // operator-controlled path). The store only connects and reads — Astro no
97
+ // longer deploys the index or seeds templates.
136
98
  export { store };
137
99
  `;
138
100
  }
139
101
  };
140
102
  }
141
103
  export {
142
- h as v
104
+ n as v
143
105
  };
@@ -1,4 +1,4 @@
1
- import { v as t } from "./vite-plugin-strife-store-Tvau120y.js";
1
+ import { v as t } from "./vite-plugin-strife-store-B-B_HXbA.js";
2
2
  export {
3
3
  t as vitePluginStrifeStore
4
4
  };
@@ -1 +1 @@
1
- {"version":3,"file":"vite-plugin-strife-store.d.ts","sourceRoot":"","sources":["../src/vite-plugin-strife-store.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,MAAM,CAAC;AAEzC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAKlD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,kBAAkB,GAAG,YAAY,CAqJ9E"}
1
+ {"version":3,"file":"vite-plugin-strife-store.d.ts","sourceRoot":"","sources":["../src/vite-plugin-strife-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,MAAM,CAAC;AAEzC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAKlD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,kBAAkB,GAAG,YAAY,CA6G9E"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@strifeapp/astro",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "description": "Official Strife integration for Astro — connect your Astro site to a RavenDB-backed Strife content store via a strife:store virtual module.",
5
5
  "keywords": [
6
6
  "astro-integration",
@@ -40,6 +40,7 @@
40
40
  "import": "./dist/edit-mode-middleware.js"
41
41
  },
42
42
  "./LivePreview.astro": "./dist/LivePreview.astro",
43
+ "./Insights.astro": "./dist/Insights.astro",
43
44
  "./vite-plugin-strife-store": "./dist/vite-plugin-strife-store-entry.js"
44
45
  },
45
46
  "files": [
@@ -59,11 +60,13 @@
59
60
  "prepublishOnly": "npm run build"
60
61
  },
61
62
  "dependencies": {
62
- "@strifeapp/strife": "^1.0.1",
63
+ "@strifeapp/strife": "^1.1.0",
64
+ "axe-core": "^4.11.0",
63
65
  "dotenv": "^17.2.3",
64
66
  "jose": "^5.9.6",
65
67
  "ravendb": "^7.1.4",
66
- "serialize-javascript": "^6.0.2"
68
+ "serialize-javascript": "^7.0.5",
69
+ "web-vitals": "^5.1.0"
67
70
  },
68
71
  "devDependencies": {
69
72
  "@types/serialize-javascript": "^5.0.4",