@strifeapp/astro 1.4.1 → 1.5.1
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 +65 -0
- package/dist/Insights.astro +18 -14
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -11
- package/dist/insights-mode.d.ts +14 -0
- package/dist/insights-mode.d.ts.map +1 -0
- package/dist/insights-mode.js +6 -0
- package/dist/snapshot-middleware.d.ts +36 -0
- package/dist/snapshot-middleware.d.ts.map +1 -0
- package/dist/snapshot-middleware.js +49 -0
- package/dist/snapshot.d.ts +129 -0
- package/dist/snapshot.d.ts.map +1 -0
- package/dist/snapshot.js +104 -0
- package/dist/{vite-plugin-strife-store-B-B_HXbA.js → vite-plugin-strife-store-B5tSnH_D.js} +4 -0
- package/dist/vite-plugin-strife-store-entry.js +1 -1
- package/dist/vite-plugin-strife-store.d.ts.map +1 -1
- package/package.json +13 -5
package/README.md
CHANGED
|
@@ -11,6 +11,7 @@ Official [Strife](https://strife.app) integration for [Astro](https://astro.buil
|
|
|
11
11
|
- [Usage](#usage)
|
|
12
12
|
- [Reading content](#reading-content)
|
|
13
13
|
- [Telemetry: `<Insights />`](#telemetry-insights)
|
|
14
|
+
- [Snapshots](#snapshots)
|
|
14
15
|
- [TypeScript: typing `strife:store`](#typescript-typing-strifestore)
|
|
15
16
|
- [Direct Vite plugin](#direct-vite-plugin)
|
|
16
17
|
- [Configuration](#configuration)
|
|
@@ -100,6 +101,70 @@ On every page load it reports Core Web Vitals (LCP, INP, CLS, FCP, TTFB) and, wh
|
|
|
100
101
|
|
|
101
102
|
> 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
103
|
|
|
104
|
+
> **Content Security Policy:** if your site sends a CSP header, allow the insights endpoint in `connect-src` (e.g. `connect-src 'self' https://api.strife.app`) — otherwise the browser blocks the beacon (a CSP violation is logged to the console; no request is sent and nothing is stored). Use your custom `endpoint`'s origin if you overrode it.
|
|
105
|
+
|
|
106
|
+
Inside the Strife live preview (`Astro.locals.editMode`) only the accessibility audit runs; on a [snapshot](#snapshots) (`Astro.locals.snapshot`) the component renders nothing and sends no beacon at all.
|
|
107
|
+
|
|
108
|
+
### Snapshots
|
|
109
|
+
|
|
110
|
+
An editor can hand a reviewer a link to a frozen, unpublished version of a document: `{origin}{locale root prefix}/snapshot/{token}` — for example `https://example.com/snapshot/0123…cdef` or `https://example.com/sv/snapshot/0123…cdef`. The integration registers a `pre` middleware (`@strifeapp/astro/snapshot-middleware`) that resolves such a request to its snapshot in the `Content/ByUrl` index — for the locale Astro resolved (`context.currentLocale`) — and either sets `Astro.locals.snapshot` or answers with **your 404 page** (unknown, expired or revoked links look like content that does not exist). Both responses carry `X-Robots-Tag: noindex, nofollow`, `Cache-Control: private, no-store` and `Referrer-Policy: no-referrer`; edit mode is forced off on snapshot routes, so `<LivePreview />` never activates there.
|
|
111
|
+
|
|
112
|
+
You author the page — it renders in your own layout. Copy [`templates/snapshot-page.astro`](https://github.com/wieldyapp/wieldy/blob/master/src/sdk/js/src/packages/astro/templates/snapshot-page.astro) to `src/pages/snapshot/[token].astro`:
|
|
113
|
+
|
|
114
|
+
```astro
|
|
115
|
+
---
|
|
116
|
+
export const prerender = false; // rendered on demand — never prebuilt
|
|
117
|
+
import Layout from '../../layouts/Layout.astro';
|
|
118
|
+
|
|
119
|
+
const { name, expiresAt, locale, content } = Astro.locals.snapshot!;
|
|
120
|
+
---
|
|
121
|
+
<Layout title={content.displayName ?? 'Preview'}>
|
|
122
|
+
<aside role="status">{name ? `Preview “${name}”` : 'Preview'} — expires {expiresAt.toISOString()}</aside>
|
|
123
|
+
<h1>{content.heading}</h1>
|
|
124
|
+
</Layout>
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
`Astro.locals.snapshot` is `{ docId, name, expiresAt: Date, locale, content }`. `content` is the index row for that locale: `docId`, `locale`, `collection`, `displayName`, `url` (the snapshot path itself — a snapshot row's `url` is `{root slug}/snapshot/{token}` for its locale, never the canonical page's path), `snapshotName`, `expiresAt`, plus every field you list in `snapshot.fields`, so one lookup both decides the 404 and feeds the render:
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
strife({
|
|
131
|
+
snapshot: {
|
|
132
|
+
fields: ['heading', 'body'], // your template fields, projected onto Astro.locals.snapshot.content
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Without `snapshot.fields`, `content` carries the row fields only and the page queries its own fields by `docId` + `locale` (`whereEquals('docId', content.docId)`, `whereEquals('status', 'snapshot')`).
|
|
138
|
+
|
|
139
|
+
| Option | Where | Description |
|
|
140
|
+
| --- | --- | --- |
|
|
141
|
+
| `snapshot.fields` | integration | Extra `Content/ByUrl` fields to project onto `content`. |
|
|
142
|
+
| `snapshot.middleware: false` | integration | Skip the automatic registration; compose the middleware yourself (below). |
|
|
143
|
+
| `resolveLocale(context)` | `createSnapshotMiddleware` | Resolve the request's locale when your site does not use Astro i18n routing. Defaults to `context.currentLocale`. |
|
|
144
|
+
|
|
145
|
+
When the resolved locale has no row in the snapshot (a cookie- or Accept-Language-driven locale the draft never had, a link pasted under another prefix), the middleware serves the snapshot's own `snapshotLocale` row instead of the 404 — the same fallback as the .NET SDK. If your site resolves its locale in its own middleware (a cookie, a header, its own path scheme), Astro's `currentLocale` is not enough — opt out of the automatic registration and compose the middleware where your locale is known:
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
// src/middleware.ts
|
|
149
|
+
import { sequence } from 'astro:middleware';
|
|
150
|
+
import { createSnapshotMiddleware } from '@strifeapp/astro/snapshot-middleware';
|
|
151
|
+
|
|
152
|
+
export const onRequest = sequence(
|
|
153
|
+
myLocaleMiddleware,
|
|
154
|
+
createSnapshotMiddleware({ resolveLocale: (context) => context.locals.locale }),
|
|
155
|
+
);
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Keep these in mind:
|
|
159
|
+
|
|
160
|
+
- The snapshot token is the link's secret. Render `name` as text only (never `set:html`), emit no Open Graph tags on the page, keep `/snapshot/*` out of CDN caching rules and analytics, and keep access-log retention short — a bearer URL is inherently logged by proxies. The middleware logs nothing on a hit and never logs the token.
|
|
161
|
+
- `/snapshot/{token}` and `/{one segment}/snapshot/{token}` are reserved by the convention; the prefix segment is opaque (the locale comes from your resolution, not the path). Only a tail of 8 lowercase letters or digits is a snapshot request — a snapshot-shaped path with any other tail (`/help/snapshot/link`) is not claimed by the middleware and reaches your own routes, as in the .NET SDK.
|
|
162
|
+
- A miss is rendered through Astro's rewrite to `/404`. Astro forbids rewriting from an on-demand route to a **prerendered** page, so if your `src/pages/404.astro` is static the middleware answers a bare `404` (headers included) and your adapter or host serves the static 404 page; add `export const prerender = false` to `404.astro` to render it through the rewrite instead.
|
|
163
|
+
- Snapshots need a `Content/ByUrl` index built from a `@strifeapp/strife` release that includes snapshots, deployed with `strife push` (the developer docs state the exact versions); until then Strife refuses to create snapshots for the team, and the route answers your 404 page.
|
|
164
|
+
- The middleware is optional: because a snapshot row's `url` is the snapshot path, a site can serve snapshots through its ordinary exact-URL lookup instead — the raw contract (the one predicate clause, the load-bearing locale clause, per-request resolution, and the hygiene you then own) is documented in the Strife developer reference under Snapshots.
|
|
165
|
+
|
|
166
|
+
`resolveSnapshot(token, locale)` from `@strifeapp/astro/snapshot` is the same lookup as a function, returning the snapshot context or `null`.
|
|
167
|
+
|
|
103
168
|
### TypeScript: typing `strife:store`
|
|
104
169
|
|
|
105
170
|
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`):
|
package/dist/Insights.astro
CHANGED
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
// consumer's runtime/build (see the integration's env schema).
|
|
25
25
|
import { getSecret } from 'astro:env/server';
|
|
26
26
|
import { decodeSecrets } from '@strifeapp/strife/secrets';
|
|
27
|
+
import { insightsMode } from './insights-mode.js';
|
|
27
28
|
|
|
28
29
|
export interface Props {
|
|
29
30
|
/** Override the collection endpoint. Defaults to the hosted Strife API. */
|
|
@@ -62,29 +63,31 @@ function resolveSiteId(): string | null {
|
|
|
62
63
|
|
|
63
64
|
const site = resolveSiteId();
|
|
64
65
|
|
|
65
|
-
//
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
//
|
|
71
|
-
//
|
|
72
|
-
|
|
66
|
+
// The middleware-set locals decide the audience. We split the work: Web Vitals run
|
|
67
|
+
// only for REAL visitors (they need real-user device/network/interaction variance),
|
|
68
|
+
// while the heavy axe-core accessibility audit runs only in the editor's live
|
|
69
|
+
// preview (`editMode`) — a11y is deterministic per content version, so the editor is
|
|
70
|
+
// the right place for it, and it keeps axe-core off every real visitor entirely. A
|
|
71
|
+
// snapshot (`snapshot`, STR-2165) is neither: it sends nothing at all — it is not
|
|
72
|
+
// audience traffic, and a beacon would carry the snapshot path, i.e. the token. Without
|
|
73
|
+
// the middleware, plain sites get Web Vitals exactly as before.
|
|
74
|
+
const mode = insightsMode(Astro.locals);
|
|
73
75
|
---
|
|
74
76
|
|
|
75
|
-
{site && (
|
|
77
|
+
{site && mode !== 'off' && (
|
|
76
78
|
<Fragment>
|
|
77
|
-
<script is:inline define:vars={{ site, endpoint, accessibility,
|
|
78
|
-
window.__strifeInsights = { site, endpoint, accessibility,
|
|
79
|
+
<script is:inline define:vars={{ site, endpoint, accessibility, mode }}>
|
|
80
|
+
window.__strifeInsights = { site, endpoint, accessibility, mode };
|
|
79
81
|
</script>
|
|
80
82
|
<script>
|
|
81
83
|
import { onLCP, onINP, onCLS, onFCP, onTTFB } from 'web-vitals';
|
|
84
|
+
import type { InsightsMode } from './insights-mode.js';
|
|
82
85
|
|
|
83
86
|
const cfg = (window as any).__strifeInsights || {};
|
|
84
87
|
const ENDPOINT: string = cfg.endpoint;
|
|
85
88
|
const SITE: string = cfg.site;
|
|
86
89
|
const RUN_A11Y: boolean = cfg.accessibility !== false;
|
|
87
|
-
const
|
|
90
|
+
const MODE: InsightsMode = cfg.mode;
|
|
88
91
|
|
|
89
92
|
/** Beacon a payload to the collection endpoint (fire-and-forget). */
|
|
90
93
|
function send(payload: Record<string, unknown>) {
|
|
@@ -231,17 +234,18 @@ const editMode = Astro.locals.editMode ?? false;
|
|
|
231
234
|
scheduleAccessibilityAudit();
|
|
232
235
|
}
|
|
233
236
|
|
|
234
|
-
if (
|
|
237
|
+
if (MODE === 'audit') {
|
|
235
238
|
// In the Strife live preview: only the accessibility audit runs. The editor is
|
|
236
239
|
// the right place for the heavy axe-core scan (a11y is deterministic per content
|
|
237
240
|
// version), and it keeps axe-core off every real visitor.
|
|
238
241
|
auditOnce();
|
|
239
242
|
// Astro View Transitions: re-audit the newly loaded page.
|
|
240
243
|
document.addEventListener('astro:page-load', auditOnce);
|
|
241
|
-
} else {
|
|
244
|
+
} else if (MODE === 'vitals') {
|
|
242
245
|
// A real visitor: Web Vitals only, batched on pagehide. No axe-core download/run.
|
|
243
246
|
initWebVitals();
|
|
244
247
|
}
|
|
248
|
+
// 'off' (a snapshot) never reaches this script: the component renders nothing.
|
|
245
249
|
</script>
|
|
246
250
|
</Fragment>
|
|
247
251
|
)}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,31 @@
|
|
|
1
1
|
import { AstroIntegration } from 'astro';
|
|
2
2
|
import { IAuthOptions } from 'ravendb';
|
|
3
|
+
import { SnapshotContext } from './snapshot';
|
|
4
|
+
export type { SnapshotContent, SnapshotContext } from './snapshot';
|
|
3
5
|
export interface IntegrationOptions extends IAuthOptions {
|
|
4
6
|
certificate?: string;
|
|
5
7
|
password?: string;
|
|
6
8
|
urls?: string[];
|
|
7
9
|
database?: string;
|
|
8
10
|
collections?: Collection[];
|
|
11
|
+
/** Snapshots (`/snapshot/{token}`); see `@strifeapp/astro/snapshot-middleware`. */
|
|
12
|
+
snapshot?: SnapshotOptions;
|
|
13
|
+
}
|
|
14
|
+
export interface SnapshotOptions {
|
|
15
|
+
/**
|
|
16
|
+
* Index fields to project onto `Astro.locals.snapshot.content` beside the row fields
|
|
17
|
+
* (your template fields, e.g. `['heading', 'body']`), so the middleware's one
|
|
18
|
+
* lookup also feeds the render. Without it, `content` carries the row fields only
|
|
19
|
+
* and the snapshot page queries its own fields by `docId` + `locale`.
|
|
20
|
+
*/
|
|
21
|
+
fields?: string[];
|
|
22
|
+
/**
|
|
23
|
+
* `false` skips the automatic `pre` registration of the snapshot middleware. Do this
|
|
24
|
+
* when your site resolves its locale in its own middleware: compose
|
|
25
|
+
* `createSnapshotMiddleware({ resolveLocale })` from `@strifeapp/astro/snapshot-middleware`
|
|
26
|
+
* in `src/middleware.ts` instead.
|
|
27
|
+
*/
|
|
28
|
+
middleware?: boolean;
|
|
9
29
|
}
|
|
10
30
|
export interface Collection {
|
|
11
31
|
name: string;
|
|
@@ -21,6 +41,14 @@ declare global {
|
|
|
21
41
|
* e.g. render draft vs published content.
|
|
22
42
|
*/
|
|
23
43
|
editMode?: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Set by the snapshot middleware on a `/snapshot/{token}` request that resolved to a
|
|
46
|
+
* live snapshot: the snapshot's id, its editor-given name (text only —
|
|
47
|
+
* never `set:html`), when the link expires, the resolved locale, and the
|
|
48
|
+
* `Content/ByUrl` projection to render. Absent on every other request, and
|
|
49
|
+
* on every snapshot miss (the middleware answers those with the site's 404 page).
|
|
50
|
+
*/
|
|
51
|
+
snapshot?: SnapshotContext;
|
|
24
52
|
}
|
|
25
53
|
}
|
|
26
54
|
}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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;
|
|
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;AAG5C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAEnE,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;IAC3B,mFAAmF;IACnF,QAAQ,CAAC,EAAE,eAAe,CAAC;CAC5B;AAED,MAAM,WAAW,eAAe;IAC9B;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;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;YACnB;;;;;;eAMG;YACH,QAAQ,CAAC,EAAE,eAAe,CAAC;SAC5B;KACF;CACF;AAMD,MAAM,CAAC,OAAO,UAAU,iBAAiB,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,gBAAgB,CAoExF"}
|
package/dist/index.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import { envField as e } from "astro/config";
|
|
2
|
-
import { v as
|
|
3
|
-
const
|
|
2
|
+
import { v as i } from "./vite-plugin-strife-store-B5tSnH_D.js";
|
|
3
|
+
const c = {
|
|
4
4
|
type: "pfx"
|
|
5
5
|
};
|
|
6
|
-
function
|
|
6
|
+
function l(t) {
|
|
7
7
|
return {
|
|
8
8
|
name: "@strife/astro",
|
|
9
9
|
hooks: {
|
|
10
|
-
"astro:config:setup": ({ updateConfig:
|
|
11
|
-
const
|
|
12
|
-
...
|
|
10
|
+
"astro:config:setup": ({ updateConfig: o, addMiddleware: r }) => {
|
|
11
|
+
const s = {
|
|
12
|
+
...c,
|
|
13
13
|
...t ? Object.fromEntries(
|
|
14
|
-
Object.entries(t).filter(([,
|
|
14
|
+
Object.entries(t).filter(([, n]) => n != null)
|
|
15
15
|
) : {}
|
|
16
16
|
};
|
|
17
|
-
|
|
17
|
+
o({
|
|
18
18
|
env: {
|
|
19
19
|
schema: {
|
|
20
20
|
// Consolidated single-var secret (packs urls/database/certificate/password,
|
|
@@ -32,16 +32,19 @@ function p(t) {
|
|
|
32
32
|
}
|
|
33
33
|
},
|
|
34
34
|
vite: {
|
|
35
|
-
plugins: [
|
|
35
|
+
plugins: [i(s)]
|
|
36
36
|
}
|
|
37
|
-
}),
|
|
37
|
+
}), r({
|
|
38
38
|
entrypoint: "@strifeapp/astro/edit-mode-middleware",
|
|
39
39
|
order: "pre"
|
|
40
|
+
}), s.snapshot?.middleware !== !1 && r({
|
|
41
|
+
entrypoint: "@strifeapp/astro/snapshot-middleware",
|
|
42
|
+
order: "pre"
|
|
40
43
|
});
|
|
41
44
|
}
|
|
42
45
|
}
|
|
43
46
|
};
|
|
44
47
|
}
|
|
45
48
|
export {
|
|
46
|
-
|
|
49
|
+
l as default
|
|
47
50
|
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which telemetry `<Insights />` runs for a request, from the middleware-set locals:
|
|
3
|
+
* - `vitals`: a real visitor — Core Web Vitals only;
|
|
4
|
+
* - `audit`: the Strife live preview (edit mode) — the axe-core accessibility audit only;
|
|
5
|
+
* - `off`: a snapshot (R14) — nothing at all. A snapshot is not audience traffic, and a
|
|
6
|
+
* beacon would carry the snapshot path, i.e. the token.
|
|
7
|
+
* Pure, so the gate is unit-tested; Insights.astro only forwards the result.
|
|
8
|
+
*/
|
|
9
|
+
export type InsightsMode = 'vitals' | 'audit' | 'off';
|
|
10
|
+
export declare function insightsMode(locals: {
|
|
11
|
+
editMode?: boolean;
|
|
12
|
+
snapshot?: unknown;
|
|
13
|
+
}): InsightsMode;
|
|
14
|
+
//# sourceMappingURL=insights-mode.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"insights-mode.d.ts","sourceRoot":"","sources":["../src/insights-mode.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,OAAO,GAAG,KAAK,CAAC;AAEtD,wBAAgB,YAAY,CAAC,MAAM,EAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,YAAY,CAI7F"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { APIContext, MiddlewareHandler } from 'astro';
|
|
2
|
+
import { ResolveSnapshotOptions } from './snapshot';
|
|
3
|
+
/**
|
|
4
|
+
* Snapshot-link middleware (STR-2165). Registered `pre` by the integration (see
|
|
5
|
+
* src/index.ts) unless `snapshot.middleware` is `false`; also exported as
|
|
6
|
+
* `createSnapshotMiddleware` so a site that resolves its locale in its own middleware
|
|
7
|
+
* can compose it there with `resolveLocale`.
|
|
8
|
+
*
|
|
9
|
+
* For a snapshot path (`/snapshot/{token}` after an optional locale root prefix, where the
|
|
10
|
+
* tail is a valid token — 8 lowercase letters or digits) it looks the snapshot up for
|
|
11
|
+
* the site's resolved locale and:
|
|
12
|
+
* - on a hit sets `Astro.locals.snapshot = { docId, name, expiresAt, locale, content }`
|
|
13
|
+
* and lets the site's `src/pages/snapshot/[token].astro` render it;
|
|
14
|
+
* - on any miss renders the site's 404 page through Astro's rewrite, re-wrapped so
|
|
15
|
+
* the status is 404 (Astro answers a rewrite with 200).
|
|
16
|
+
* Both carry `X-Robots-Tag: noindex, nofollow`, `Cache-Control: private, no-store`
|
|
17
|
+
* and `Referrer-Policy: no-referrer`. Edit mode is forced off on snapshot routes, so
|
|
18
|
+
* `<LivePreview />` never activates there. Nothing is logged on a hit, and the token
|
|
19
|
+
* is never logged. Every other path passes through untouched — including a
|
|
20
|
+
* snapshot-shaped path whose tail is not a token (`/help/snapshot/link`): as in the .NET
|
|
21
|
+
* resolver, that is not a snapshot request but an ordinary site route.
|
|
22
|
+
*/
|
|
23
|
+
export interface SnapshotMiddlewareOptions extends ResolveSnapshotOptions {
|
|
24
|
+
/**
|
|
25
|
+
* Resolve the locale of the current request. Defaults to Astro's
|
|
26
|
+
* `context.currentLocale` (i18n routing). A site that resolves its locale itself
|
|
27
|
+
* — from a cookie, a header, its own path scheme — must pass this, or a
|
|
28
|
+
* multi-locale snapshot resolves the wrong (or no) row.
|
|
29
|
+
*/
|
|
30
|
+
resolveLocale?: (context: APIContext) => string | undefined | Promise<string | undefined>;
|
|
31
|
+
}
|
|
32
|
+
export declare const SNAPSHOT_RESPONSE_HEADERS: Readonly<Record<string, string>>;
|
|
33
|
+
export declare function createSnapshotMiddleware(options?: SnapshotMiddlewareOptions): MiddlewareHandler;
|
|
34
|
+
/** The default instance the integration registers via `addMiddleware`. */
|
|
35
|
+
export declare const onRequest: MiddlewareHandler;
|
|
36
|
+
//# sourceMappingURL=snapshot-middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot-middleware.d.ts","sourceRoot":"","sources":["../src/snapshot-middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAC3D,OAAO,EAAsD,KAAK,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAE7G;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,MAAM,WAAW,yBAA0B,SAAQ,sBAAsB;IACvE;;;;;OAKG;IACH,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,UAAU,KAAK,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;CAC3F;AAED,eAAO,MAAM,yBAAyB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAIrE,CAAC;AAEH,wBAAgB,wBAAwB,CAAC,OAAO,GAAE,yBAA8B,GAAG,iBAAiB,CA0BnG;AAED,0EAA0E;AAC1E,eAAO,MAAM,SAAS,EAAE,iBAA8C,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { matchSnapshotPath as u, isSnapshotToken as i, lookupSnapshot as h } from "./snapshot.js";
|
|
2
|
+
const f = Object.freeze({
|
|
3
|
+
"X-Robots-Tag": "noindex, nofollow",
|
|
4
|
+
"Cache-Control": "private, no-store",
|
|
5
|
+
"Referrer-Policy": "no-referrer"
|
|
6
|
+
});
|
|
7
|
+
function p(r = {}) {
|
|
8
|
+
return async (e, o) => {
|
|
9
|
+
const t = u(e.url.pathname);
|
|
10
|
+
if (t === null || !i(t)) return o();
|
|
11
|
+
e.locals.editMode = !1;
|
|
12
|
+
const a = r.resolveLocale ? await r.resolveLocale(e) : e.currentLocale, n = await h(t, a, r);
|
|
13
|
+
return n.snapshot === null ? (n.reason === "query_error" && console.warn("[strife] snapshot lookup failed (reason=query_error); answering 404"), d(o)) : (e.locals.snapshot = n.snapshot, l(await o()));
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
const y = p();
|
|
17
|
+
async function d(r) {
|
|
18
|
+
let e;
|
|
19
|
+
try {
|
|
20
|
+
e = await r("/404");
|
|
21
|
+
} catch {
|
|
22
|
+
e = new Response(null, { status: 404 });
|
|
23
|
+
}
|
|
24
|
+
return l(e, 404);
|
|
25
|
+
}
|
|
26
|
+
function l(r, e) {
|
|
27
|
+
const o = e !== void 0 && r.status !== e ? s(r, e) : r, t = (a) => {
|
|
28
|
+
for (const [n, c] of Object.entries(f))
|
|
29
|
+
a.set(n, c);
|
|
30
|
+
};
|
|
31
|
+
try {
|
|
32
|
+
return t(o.headers), o;
|
|
33
|
+
} catch {
|
|
34
|
+
const a = s(o, o.status);
|
|
35
|
+
return t(a.headers), a;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function s(r, e) {
|
|
39
|
+
return new Response(r.body, {
|
|
40
|
+
status: e,
|
|
41
|
+
statusText: r.statusText,
|
|
42
|
+
headers: new Headers(r.headers)
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
export {
|
|
46
|
+
f as SNAPSHOT_RESPONSE_HEADERS,
|
|
47
|
+
p as createSnapshotMiddleware,
|
|
48
|
+
y as onRequest
|
|
49
|
+
};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { IDocumentStore } from 'ravendb';
|
|
2
|
+
/**
|
|
3
|
+
* Snapshots (STR-2165): resolve a `/snapshot/{token}` request to the one live snapshot
|
|
4
|
+
* snapshot its token names, reading the `Content/ByUrl` projection for the resolved
|
|
5
|
+
* locale. Locale handling mirrors the .NET SDK's `DefaultRouteResolver` exactly:
|
|
6
|
+
* - with a resolved locale the query carries a locale clause; when it yields no row
|
|
7
|
+
* (a cookie- or Accept-Language-driven locale the draft never had, a link pasted
|
|
8
|
+
* under another prefix) the token is re-queried without the clause (bounded to
|
|
9
|
+
* `MAX_SNAPSHOT_LOCALE_ROWS`) and only the snapshot's own row — `locale` equal to
|
|
10
|
+
* its `snapshotLocale` — is kept, so the preview is served rather than hidden;
|
|
11
|
+
* - without a resolved locale the token-only query pages every row of the snapshot
|
|
12
|
+
* (again bounded to `MAX_SNAPSHOT_LOCALE_ROWS`, never just the first two), and more
|
|
13
|
+
* than one candidate row is decided by the same rule: the `snapshotLocale` row wins,
|
|
14
|
+
* and anything other than exactly one such row is an `ambiguous_locale` miss.
|
|
15
|
+
* Pure with respect to Astro — the store is injectable, so the lookup is
|
|
16
|
+
* unit-testable without RavenDB; `resolveSnapshot` without a store reads the
|
|
17
|
+
* integration's `strife:store` virtual module.
|
|
18
|
+
*
|
|
19
|
+
* The token is the bearer secret of the link: nothing in this module logs it, and
|
|
20
|
+
* a thrown query (an index build that predates snapshots, an unreachable store)
|
|
21
|
+
* is a miss, never an exception that could surface it on an error page.
|
|
22
|
+
*/
|
|
23
|
+
/** The content index the integration queries; deployed by `strife push`. */
|
|
24
|
+
export declare const SNAPSHOT_INDEX_NAME = "Content/ByUrl";
|
|
25
|
+
/** A snapshot token is exactly 8 lowercase letters or digits — nothing looser. */
|
|
26
|
+
export declare const SNAPSHOT_TOKEN_PATTERN: RegExp;
|
|
27
|
+
/**
|
|
28
|
+
* Upper bound on locales one snapshot can carry rows for; a token-only query (no
|
|
29
|
+
* resolved locale, or the locale fallback) never scans further. Same constant as the
|
|
30
|
+
* .NET resolver's `MaxSnapshotLocaleRows`.
|
|
31
|
+
*/
|
|
32
|
+
export declare const MAX_SNAPSHOT_LOCALE_ROWS = 64;
|
|
33
|
+
/**
|
|
34
|
+
* Row fields every snapshot lookup projects from the index: the snapshot metadata plus
|
|
35
|
+
* what a page needs to pick a layout (`collection`, `displayName`, `url`). The row's
|
|
36
|
+
* `url` is the snapshot path itself — `{root slug}/snapshot/{token}` for the row's locale,
|
|
37
|
+
* the exact-URL lookup key a raw site queries — never the canonical page's path. `deleted`
|
|
38
|
+
* is not stored in the index; RavenDB's default projection behaviour reads it from
|
|
39
|
+
* the document, which is exactly the soft-delete check we want. Site-specific
|
|
40
|
+
* template fields come from the `fields` option.
|
|
41
|
+
*/
|
|
42
|
+
export declare const SNAPSHOT_ROW_FIELDS: readonly ["docId", "locale", "collection", "displayName", "url", "status", "snapshotToken", "snapshotName", "expiresAt", "snapshotLocale", "createdAt", "changedAt", "publishedAt", "origin", "labels", "deleted"];
|
|
43
|
+
/**
|
|
44
|
+
* The `Content/ByUrl` row for the snapshot in the resolved locale — the same
|
|
45
|
+
* projected, locale-translated object a page gets when it queries the index, plus
|
|
46
|
+
* any site fields passed through `fields`.
|
|
47
|
+
*/
|
|
48
|
+
export interface SnapshotContent {
|
|
49
|
+
docId: string;
|
|
50
|
+
locale: string;
|
|
51
|
+
status: 'snapshot';
|
|
52
|
+
snapshotToken: string;
|
|
53
|
+
snapshotName?: string | null;
|
|
54
|
+
expiresAt: string;
|
|
55
|
+
/** The locale the editor was viewing when sharing — the one the link targets. */
|
|
56
|
+
snapshotLocale?: string | null;
|
|
57
|
+
collection?: string | null;
|
|
58
|
+
displayName?: string | null;
|
|
59
|
+
/** The snapshot path for this locale (`{root slug}/snapshot/{token}`) — not the canonical page's path. */
|
|
60
|
+
url?: string | null;
|
|
61
|
+
[field: string]: unknown;
|
|
62
|
+
}
|
|
63
|
+
/** What a snapshot page sees on `Astro.locals.snapshot`. */
|
|
64
|
+
export interface SnapshotContext {
|
|
65
|
+
/** The snapshot's document id (`{canonicalId}/snapshot/{token}`). Never log it. */
|
|
66
|
+
docId: string;
|
|
67
|
+
/** The editor-given name, or `null`. Render as text only — never `set:html`. */
|
|
68
|
+
name: string | null;
|
|
69
|
+
/** UTC instant after which the link stops resolving. */
|
|
70
|
+
expiresAt: Date;
|
|
71
|
+
/** The locale the row was resolved for. */
|
|
72
|
+
locale: string;
|
|
73
|
+
/** The index projection for that locale; render this. */
|
|
74
|
+
content: SnapshotContent;
|
|
75
|
+
}
|
|
76
|
+
export type SnapshotMissReason = 'malformed_token' | 'not_found' | 'ambiguous_locale' | 'deleted' | 'no_expiry' | 'expired' | 'query_error';
|
|
77
|
+
export type SnapshotLookup = {
|
|
78
|
+
snapshot: SnapshotContext;
|
|
79
|
+
reason: null;
|
|
80
|
+
} | {
|
|
81
|
+
snapshot: null;
|
|
82
|
+
reason: SnapshotMissReason;
|
|
83
|
+
};
|
|
84
|
+
export interface ResolveSnapshotOptions {
|
|
85
|
+
/** The document store to query. Defaults to the integration's `strife:store`. */
|
|
86
|
+
store?: IDocumentStore;
|
|
87
|
+
/**
|
|
88
|
+
* Extra index fields to project onto `content` (your template fields, e.g.
|
|
89
|
+
* `['heading', 'body']`), so one lookup both decides the 404 and feeds the render.
|
|
90
|
+
* Defaults to the integration's `snapshot.fields` option.
|
|
91
|
+
*/
|
|
92
|
+
fields?: readonly string[];
|
|
93
|
+
/** Clock, for tests. */
|
|
94
|
+
now?: () => Date;
|
|
95
|
+
}
|
|
96
|
+
export declare function isSnapshotToken(value: unknown): value is string;
|
|
97
|
+
/**
|
|
98
|
+
* Matches the snapshot convention `/snapshot/{token}` after an optional single locale root
|
|
99
|
+
* prefix segment (`/sv/snapshot/{token}`, `/svenska/snapshot/{token}`), with or without a
|
|
100
|
+
* trailing slash. Returns the raw token segment — validate it with `isSnapshotToken` — or
|
|
101
|
+
* `null` when the path is not snapshot-shaped. The prefix is opaque: the locale comes
|
|
102
|
+
* from the site's own resolution, never from the path.
|
|
103
|
+
*/
|
|
104
|
+
export declare function matchSnapshotPath(pathname: string): string | null;
|
|
105
|
+
/**
|
|
106
|
+
* Picks the single live snapshot row or names the miss. Mirrors the .NET SDK's
|
|
107
|
+
* `DefaultRouteResolver.ClassifySnapshotRows` so both site SDKs agree on "live": with
|
|
108
|
+
* more than one row (several locales of one snapshot and no locale resolver to pick
|
|
109
|
+
* between them) the snapshot's own locale wins — the row whose `locale` equals its
|
|
110
|
+
* `snapshotLocale` — and anything other than exactly one such row is `ambiguous_locale`.
|
|
111
|
+
*/
|
|
112
|
+
export declare function classifySnapshotRows(rows: readonly Record<string, unknown>[], now: Date): SnapshotLookup;
|
|
113
|
+
/**
|
|
114
|
+
* Looks a snapshot up and reports the outcome with its miss reason. A malformed token
|
|
115
|
+
* never reaches the store. `locale` is the site's resolved locale: the query carries
|
|
116
|
+
* it as a clause, and when that yields no row the token is re-queried without the
|
|
117
|
+
* clause and only the snapshot's own `snapshotLocale` row is kept (the .NET resolver's
|
|
118
|
+
* locale fallback). When `locale` is `undefined` (a single-locale site without Astro
|
|
119
|
+
* i18n) the clause is omitted from the start and the query pages every row of the
|
|
120
|
+
* snapshot; more than one row then resolves to the `snapshotLocale` row, or is an
|
|
121
|
+
* `ambiguous_locale` miss (see `classifySnapshotRows`).
|
|
122
|
+
*/
|
|
123
|
+
export declare function lookupSnapshot(token: string, locale: string | undefined, options?: ResolveSnapshotOptions): Promise<SnapshotLookup>;
|
|
124
|
+
/**
|
|
125
|
+
* Resolves `token` in `locale` to the snapshot context, or `null` on any miss
|
|
126
|
+
* (unknown, malformed, deleted, expired, missing expiry, query failure).
|
|
127
|
+
*/
|
|
128
|
+
export declare function resolveSnapshot(token: string, locale: string | undefined, options?: ResolveSnapshotOptions): Promise<SnapshotContext | null>;
|
|
129
|
+
//# sourceMappingURL=snapshot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot.d.ts","sourceRoot":"","sources":["../src/snapshot.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,4EAA4E;AAC5E,eAAO,MAAM,mBAAmB,kBAAkB,CAAC;AAEnD,kFAAkF;AAClF,eAAO,MAAM,sBAAsB,QAAkB,CAAC;AAEtD;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAE3C;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,oNAiBtB,CAAC;AAEX;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,UAAU,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,iFAAiF;IACjF,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,0GAA0G;IAC1G,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;CAC1B;AAED,4DAA4D;AAC5D,MAAM,WAAW,eAAe;IAC9B,mFAAmF;IACnF,KAAK,EAAE,MAAM,CAAC;IACd,gFAAgF;IAChF,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,wDAAwD;IACxD,SAAS,EAAE,IAAI,CAAC;IAChB,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,yDAAyD;IACzD,OAAO,EAAE,eAAe,CAAC;CAC1B;AAED,MAAM,MAAM,kBAAkB,GAC1B,iBAAiB,GACjB,WAAW,GACX,kBAAkB,GAClB,SAAS,GACT,WAAW,GACX,SAAS,GACT,aAAa,CAAC;AAElB,MAAM,MAAM,cAAc,GACtB;IAAE,QAAQ,EAAE,eAAe,CAAC;IAAC,MAAM,EAAE,IAAI,CAAA;CAAE,GAC3C;IAAE,QAAQ,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,kBAAkB,CAAA;CAAE,CAAC;AAEnD,MAAM,WAAW,sBAAsB;IACrC,iFAAiF;IACjF,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB;;;;OAIG;IACH,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,wBAAwB;IACxB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAE/D;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGjE;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,GAAG,EAAE,IAAI,GAAG,cAAc,CA2BxG;AAcD;;;;;;;;;GASG;AACH,wBAAsB,cAAc,CAClC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,cAAc,CAAC,CAezB;AAED;;;GAGG;AACH,wBAAsB,eAAe,CACnC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAEjC"}
|
package/dist/snapshot.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
const i = "Content/ByUrl", c = /^[a-z0-9]{8}$/, _ = 64, p = [
|
|
2
|
+
"docId",
|
|
3
|
+
"locale",
|
|
4
|
+
"collection",
|
|
5
|
+
"displayName",
|
|
6
|
+
"url",
|
|
7
|
+
"status",
|
|
8
|
+
"snapshotToken",
|
|
9
|
+
"snapshotName",
|
|
10
|
+
"expiresAt",
|
|
11
|
+
"snapshotLocale",
|
|
12
|
+
"createdAt",
|
|
13
|
+
"changedAt",
|
|
14
|
+
"publishedAt",
|
|
15
|
+
"origin",
|
|
16
|
+
"labels",
|
|
17
|
+
"deleted"
|
|
18
|
+
];
|
|
19
|
+
function h(e) {
|
|
20
|
+
return typeof e == "string" && c.test(e);
|
|
21
|
+
}
|
|
22
|
+
function w(e) {
|
|
23
|
+
const n = /^\/(?:[^/]+\/)?snapshot\/([^/]+)\/?$/.exec(e);
|
|
24
|
+
return n ? n[1] : null;
|
|
25
|
+
}
|
|
26
|
+
function f(e, n) {
|
|
27
|
+
if (e.length === 0) return { snapshot: null, reason: "not_found" };
|
|
28
|
+
let t = e[0];
|
|
29
|
+
if (e.length > 1) {
|
|
30
|
+
const r = e.filter(u);
|
|
31
|
+
if (r.length !== 1) return { snapshot: null, reason: "ambiguous_locale" };
|
|
32
|
+
t = r[0];
|
|
33
|
+
}
|
|
34
|
+
if (t.deleted === !0) return { snapshot: null, reason: "deleted" };
|
|
35
|
+
const o = d(t.expiresAt);
|
|
36
|
+
if (o === null) return { snapshot: null, reason: "no_expiry" };
|
|
37
|
+
if (o.getTime() <= n.getTime()) return { snapshot: null, reason: "expired" };
|
|
38
|
+
const s = t;
|
|
39
|
+
return {
|
|
40
|
+
snapshot: {
|
|
41
|
+
docId: String(s.docId),
|
|
42
|
+
name: typeof s.snapshotName == "string" ? s.snapshotName : null,
|
|
43
|
+
expiresAt: o,
|
|
44
|
+
locale: String(s.locale),
|
|
45
|
+
content: s
|
|
46
|
+
},
|
|
47
|
+
reason: null
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function u(e) {
|
|
51
|
+
return e.snapshotLocale != null && e.locale === e.snapshotLocale;
|
|
52
|
+
}
|
|
53
|
+
function d(e) {
|
|
54
|
+
if (e instanceof Date) return Number.isNaN(e.getTime()) ? null : e;
|
|
55
|
+
if (typeof e != "string" || e === "") return null;
|
|
56
|
+
const n = new Date(e);
|
|
57
|
+
return Number.isNaN(n.getTime()) ? null : n;
|
|
58
|
+
}
|
|
59
|
+
async function S(e, n, t = {}) {
|
|
60
|
+
if (!h(e)) return { snapshot: null, reason: "malformed_token" };
|
|
61
|
+
const o = t.now ?? (() => /* @__PURE__ */ new Date());
|
|
62
|
+
let s;
|
|
63
|
+
try {
|
|
64
|
+
const { store: r, fields: a } = await y(t);
|
|
65
|
+
s = await N(r, e, n, a);
|
|
66
|
+
} catch {
|
|
67
|
+
return { snapshot: null, reason: "query_error" };
|
|
68
|
+
}
|
|
69
|
+
return f(s, o());
|
|
70
|
+
}
|
|
71
|
+
async function T(e, n, t = {}) {
|
|
72
|
+
return (await S(e, n, t)).snapshot;
|
|
73
|
+
}
|
|
74
|
+
async function N(e, n, t, o) {
|
|
75
|
+
const s = e.openSession();
|
|
76
|
+
try {
|
|
77
|
+
const r = m(o);
|
|
78
|
+
let a = s.query({ indexName: i }).whereEquals("snapshotToken", n).whereEquals("status", "snapshot");
|
|
79
|
+
t && (a = a.whereEquals("locale", t));
|
|
80
|
+
const l = await a.selectFields(r).take(t ? 2 : 64).all();
|
|
81
|
+
return l.length > 0 || !t ? l : (await s.query({ indexName: i }).whereEquals("snapshotToken", n).whereEquals("status", "snapshot").selectFields(r).take(64).all()).filter(u);
|
|
82
|
+
} finally {
|
|
83
|
+
s.dispose();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function m(e) {
|
|
87
|
+
return [.../* @__PURE__ */ new Set([...p, ...e ?? []])];
|
|
88
|
+
}
|
|
89
|
+
async function y(e) {
|
|
90
|
+
if (e.store) return { store: e.store, fields: e.fields };
|
|
91
|
+
const n = await import("strife:store");
|
|
92
|
+
return { store: n.store, fields: e.fields ?? n.snapshotOptions?.fields };
|
|
93
|
+
}
|
|
94
|
+
export {
|
|
95
|
+
_ as MAX_SNAPSHOT_LOCALE_ROWS,
|
|
96
|
+
i as SNAPSHOT_INDEX_NAME,
|
|
97
|
+
p as SNAPSHOT_ROW_FIELDS,
|
|
98
|
+
c as SNAPSHOT_TOKEN_PATTERN,
|
|
99
|
+
f as classifySnapshotRows,
|
|
100
|
+
h as isSnapshotToken,
|
|
101
|
+
S as lookupSnapshot,
|
|
102
|
+
w as matchSnapshotPath,
|
|
103
|
+
T as resolveSnapshot
|
|
104
|
+
};
|
|
@@ -96,6 +96,10 @@ function n(o) {
|
|
|
96
96
|
// operator-controlled path). The store only connects and reads — Astro no
|
|
97
97
|
// longer deploys the index or seeds templates.
|
|
98
98
|
export { store };
|
|
99
|
+
|
|
100
|
+
// Non-secret snapshot-link options (@strifeapp/astro/snapshot reads the projection
|
|
101
|
+
// fields from here when the auto-registered middleware resolves a snapshot).
|
|
102
|
+
export const snapshotOptions = defaultConfig.snapshot || {};
|
|
99
103
|
`;
|
|
100
104
|
}
|
|
101
105
|
};
|
|
@@ -1 +1 @@
|
|
|
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,
|
|
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,CAiH9E"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@strifeapp/astro",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.1",
|
|
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",
|
|
@@ -39,6 +39,14 @@
|
|
|
39
39
|
"types": "./dist/edit-mode-middleware.d.ts",
|
|
40
40
|
"import": "./dist/edit-mode-middleware.js"
|
|
41
41
|
},
|
|
42
|
+
"./snapshot": {
|
|
43
|
+
"types": "./dist/snapshot.d.ts",
|
|
44
|
+
"import": "./dist/snapshot.js"
|
|
45
|
+
},
|
|
46
|
+
"./snapshot-middleware": {
|
|
47
|
+
"types": "./dist/snapshot-middleware.d.ts",
|
|
48
|
+
"import": "./dist/snapshot-middleware.js"
|
|
49
|
+
},
|
|
42
50
|
"./LivePreview.astro": "./dist/LivePreview.astro",
|
|
43
51
|
"./Insights.astro": "./dist/Insights.astro",
|
|
44
52
|
"./vite-plugin-strife-store": "./dist/vite-plugin-strife-store-entry.js"
|
|
@@ -63,17 +71,17 @@
|
|
|
63
71
|
"@strifeapp/strife": "^1.1.0",
|
|
64
72
|
"axe-core": "^4.11.0",
|
|
65
73
|
"dotenv": "^17.2.3",
|
|
66
|
-
"jose": "^
|
|
74
|
+
"jose": "^6.2.9",
|
|
67
75
|
"ravendb": "^7.1.4",
|
|
68
76
|
"serialize-javascript": "^7.0.5",
|
|
69
|
-
"web-vitals": "^
|
|
77
|
+
"web-vitals": "^6.1.1"
|
|
70
78
|
},
|
|
71
79
|
"devDependencies": {
|
|
72
80
|
"@types/serialize-javascript": "^5.0.4",
|
|
73
|
-
"astro": "^6.
|
|
81
|
+
"astro": "^6.4.8",
|
|
74
82
|
"rimraf": "^6.0.1",
|
|
75
83
|
"typescript": "^5.7.3",
|
|
76
|
-
"vite": "^7.
|
|
84
|
+
"vite": "^7.3.6",
|
|
77
85
|
"vite-plugin-dts": "^4.5.3",
|
|
78
86
|
"vitest": "^4.1.7"
|
|
79
87
|
},
|