@makershq/react 0.0.0-stage → 0.3.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/LICENSE +21 -0
- package/README.md +56 -2
- package/THIRD_PARTY_NOTICES.md +77 -0
- package/dist/api.d.ts +82 -0
- package/dist/api.js +183 -0
- package/dist/cache.d.ts +22 -0
- package/dist/cache.js +86 -0
- package/dist/components/icons/external-link/index.d.ts +52 -0
- package/dist/components/icons/external-link/index.js +60 -0
- package/dist/components/icons/index.d.ts +31 -0
- package/dist/components/icons/index.js +56 -0
- package/dist/components/icons/reduced-motion.d.ts +4 -0
- package/dist/components/icons/reduced-motion.js +24 -0
- package/dist/components/icons/x/index.d.ts +98 -0
- package/dist/components/icons/x/index.js +87 -0
- package/dist/components/release-markdown/index.d.ts +22 -0
- package/dist/components/release-markdown/index.js +101 -0
- package/dist/components/release-notes/index.d.ts +51 -0
- package/dist/components/release-notes/index.js +89 -0
- package/dist/connectors/use-release-note-list-connector.d.ts +24 -0
- package/dist/connectors/use-release-note-list-connector.js +219 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +13 -0
- package/dist/styles.css +2 -0
- package/dist/styles.d.ts +8 -0
- package/dist/tauri/components/native-child/index.d.ts +19 -0
- package/dist/tauri/components/native-child/index.js +146 -0
- package/dist/tauri/components/native-child/theme.d.ts +12 -0
- package/dist/tauri/components/native-child/theme.js +45 -0
- package/dist/tauri/components/release-notes-host/index.d.ts +36 -0
- package/dist/tauri/components/release-notes-host/index.js +81 -0
- package/dist/tauri/components/release-notes-settings/index.d.ts +10 -0
- package/dist/tauri/components/release-notes-settings/index.js +23 -0
- package/dist/tauri/components/update-window/index.d.ts +20 -0
- package/dist/tauri/components/update-window/index.js +68 -0
- package/dist/tauri/context.d.ts +20 -0
- package/dist/tauri/context.js +22 -0
- package/dist/tauri/contracts.d.ts +84 -0
- package/dist/tauri/contracts.js +7 -0
- package/dist/tauri/hooks/use-release-notes.d.ts +92 -0
- package/dist/tauri/hooks/use-release-notes.js +101 -0
- package/dist/tauri/host-controller.d.ts +134 -0
- package/dist/tauri/host-controller.js +951 -0
- package/dist/tauri/index.d.ts +10 -0
- package/dist/tauri/index.js +9 -0
- package/dist/tauri/peer-protocol.d.ts +36 -0
- package/dist/tauri/peer-protocol.js +43 -0
- package/dist/tauri/preferences.d.ts +18 -0
- package/dist/tauri/preferences.js +39 -0
- package/dist/tauri/protocol.d.ts +71 -0
- package/dist/tauri/protocol.js +106 -0
- package/dist/tauri/remote-settings-client.d.ts +72 -0
- package/dist/tauri/remote-settings-client.js +265 -0
- package/dist/website/components/update-toast/index.d.ts +14 -0
- package/dist/website/components/update-toast/index.js +22 -0
- package/dist/website/components/website-updates/index.d.ts +24 -0
- package/dist/website/components/website-updates/index.js +35 -0
- package/dist/website/components/whats-new-panel/index.d.ts +23 -0
- package/dist/website/components/whats-new-panel/index.js +38 -0
- package/dist/website/preferences.d.ts +32 -0
- package/dist/website/preferences.js +85 -0
- package/dist/website/use-website-updates-connector.d.ts +34 -0
- package/dist/website/use-website-updates-connector.js +318 -0
- package/dist/website/version.d.ts +27 -0
- package/dist/website/version.js +73 -0
- package/package.json +71 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Maker’s HQ contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,57 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @makershq/react
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Add Maker’s HQ release notes and update notifications to your React app.
|
|
4
|
+
|
|
5
|
+
- Embed a release history or a specific app version.
|
|
6
|
+
- Notify website visitors when a new deployment is ready.
|
|
7
|
+
- Build custom UI with hooks and a safe Markdown renderer.
|
|
8
|
+
- Integrate with Tauri using native windows or your own UI.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
Requires React 19.
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
npm install @makershq/react
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Or with pnpm:
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
pnpm add @makershq/react
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quickstart
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
import { ReleaseNotes } from '@makershq/react'
|
|
28
|
+
import '@makershq/react/styles.css'
|
|
29
|
+
|
|
30
|
+
export default function App() {
|
|
31
|
+
return (
|
|
32
|
+
<main>
|
|
33
|
+
<h1>What's new</h1>
|
|
34
|
+
<ReleaseNotes projectId="YOUR_PROJECT_ID" theme="system" />
|
|
35
|
+
</main>
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Find your public project ID in **Release notes → Add to app** in Maker’s HQ. No API key or authentication provider is required.
|
|
41
|
+
|
|
42
|
+
Omit `version` to show the full history, or pass `version="1.2.3"` to open a specific release. Import the stylesheet once; it inherits your app's font and color variables.
|
|
43
|
+
|
|
44
|
+
For default Tauri native windows, give only the main host `core:window:allow-set-theme` and pass the app’s Light, Dark or System preference through `theme`. Native title bars and content update together. Headless mode leaves native appearance to your app and requires no SDK theme permission.
|
|
45
|
+
|
|
46
|
+
## Documentation
|
|
47
|
+
|
|
48
|
+
- [React reader](https://docs.makershq.dev/integrations/release-notes/react#embedded-release-reader)
|
|
49
|
+
- [Website update notifications](https://docs.makershq.dev/integrations/release-notes/react)
|
|
50
|
+
- [Custom UI](https://docs.makershq.dev/integrations/release-notes/react#custom-ui)
|
|
51
|
+
- [Tauri integration](https://docs.makershq.dev/integrations/release-notes/tauri)
|
|
52
|
+
- [Headless Tauri UI](https://docs.makershq.dev/integrations/release-notes/tauri#headless-tauri-ui)
|
|
53
|
+
- [HTTP API](https://docs.makershq.dev/integrations/release-notes/http-api)
|
|
54
|
+
|
|
55
|
+
## License
|
|
56
|
+
|
|
57
|
+
[MIT](https://opensource.org/license/mit). Third-party notices are included in `THIRD_PARTY_NOTICES.md` in the installed package.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Third-party notices
|
|
2
|
+
|
|
3
|
+
The SDK uses Animate UI icon implementations and original Lucide/Feather artwork privately within its release interfaces. Their licenses remain applicable to those portions; the SDK’s MIT license covers its own code.
|
|
4
|
+
|
|
5
|
+
## Animate UI icon source
|
|
6
|
+
|
|
7
|
+
MIT + Commons Clause License Condition
|
|
8
|
+
|
|
9
|
+
Copyright (c) 2025 Elliot Sutton
|
|
10
|
+
|
|
11
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
12
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
13
|
+
in the Software without restriction, including without limitation the rights
|
|
14
|
+
to use, copy, modify, merge, publish, and distribute the Software **as part of an application, website, or product**, subject to the following conditions:
|
|
15
|
+
|
|
16
|
+
The above copyright notice and this permission notice shall be included in all
|
|
17
|
+
copies or substantial portions of the Software.
|
|
18
|
+
|
|
19
|
+
## Commons Clause Restriction
|
|
20
|
+
|
|
21
|
+
You may use this Software, including for any commercial purpose, **so long as you do not sell or redistribute the components themselves in their original form—whether alone or in a bundle.**
|
|
22
|
+
|
|
23
|
+
## No Warranty
|
|
24
|
+
|
|
25
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
26
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
27
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
28
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
29
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
30
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
31
|
+
SOFTWARE.
|
|
32
|
+
|
|
33
|
+
## Lucide and Feather artwork
|
|
34
|
+
|
|
35
|
+
ISC License
|
|
36
|
+
|
|
37
|
+
Copyright (c) 2026 Lucide Icons and Contributors
|
|
38
|
+
|
|
39
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
40
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
41
|
+
copyright notice and this permission notice appear in all copies.
|
|
42
|
+
|
|
43
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
44
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
45
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
46
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
47
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
48
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
49
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
The following Lucide icons are derived from the Feather project:
|
|
54
|
+
|
|
55
|
+
airplay, alert-circle, alert-octagon, alert-triangle, aperture, arrow-down-circle, arrow-down-left, arrow-down-right, arrow-down, arrow-left-circle, arrow-left, arrow-right-circle, arrow-right, arrow-up-circle, arrow-up-left, arrow-up-right, arrow-up, at-sign, calendar, cast, check, chevron-down, chevron-left, chevron-right, chevron-up, chevrons-down, chevrons-left, chevrons-right, chevrons-up, circle, clipboard, clock, code, columns, command, compass, corner-down-left, corner-down-right, corner-left-down, corner-left-up, corner-right-down, corner-right-up, corner-up-left, corner-up-right, crosshair, database, divide-circle, divide-square, dollar-sign, download, external-link, feather, frown, hash, headphones, help-circle, info, italic, key, layout, life-buoy, link-2, link, loader, lock, log-in, log-out, maximize, meh, minimize, minimize-2, minus-circle, minus-square, minus, monitor, moon, more-horizontal, more-vertical, move, music, navigation-2, navigation, octagon, pause-circle, percent, plus-circle, plus-square, plus, power, radio, rss, search, server, share, shopping-bag, sidebar, smartphone, smile, square, table-2, tablet, target, terminal, trash-2, trash, triangle, tv, type, upload, x-circle, x-octagon, x-square, x, zoom-in, zoom-out
|
|
56
|
+
|
|
57
|
+
The MIT License (MIT) (for the icons listed above)
|
|
58
|
+
|
|
59
|
+
Copyright (c) 2013-present Cole Bemis
|
|
60
|
+
|
|
61
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
62
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
63
|
+
in the Software without restriction, including without limitation the rights
|
|
64
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
65
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
66
|
+
furnished to do so, subject to the following conditions:
|
|
67
|
+
|
|
68
|
+
The above copyright notice and this permission notice shall be included in all
|
|
69
|
+
copies or substantial portions of the Software.
|
|
70
|
+
|
|
71
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
72
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
73
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
74
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
75
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
76
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
77
|
+
SOFTWARE.
|
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/** A published release returned by the anonymous API. */
|
|
2
|
+
export interface Release {
|
|
3
|
+
id: string;
|
|
4
|
+
/** Stable public release identifier. */
|
|
5
|
+
version: string | null;
|
|
6
|
+
/** Optional exact application version. */
|
|
7
|
+
title: string;
|
|
8
|
+
/** Human-readable title. */
|
|
9
|
+
excerpt?: string;
|
|
10
|
+
/** Optional plain-text preview, at most 240 Unicode characters. */
|
|
11
|
+
previewImageSource?: string | null;
|
|
12
|
+
/** Optional managed or safe HTTP(S) image source; null when no preview is available. */
|
|
13
|
+
markdown: string;
|
|
14
|
+
/** Complete release document, including managed image references. */
|
|
15
|
+
date: string;
|
|
16
|
+
/** Calendar publication date in YYYY-MM-DD form. */
|
|
17
|
+
url: string;
|
|
18
|
+
}
|
|
19
|
+
/** A publication-ordered page of at most ten releases. */
|
|
20
|
+
export interface ReleasePage {
|
|
21
|
+
items: Release[];
|
|
22
|
+
/** Published notes, newest publication first. */
|
|
23
|
+
nextCursor: string | null;
|
|
24
|
+
}
|
|
25
|
+
/** Signals an authoritative unavailable project. */
|
|
26
|
+
export declare class ReleaseUnavailableError extends Error {
|
|
27
|
+
/** Creates a safe unavailable error without exposing gateway details. */
|
|
28
|
+
constructor();
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Builds a canonical anonymous project timeline URL.
|
|
32
|
+
* @param projectId - Public project identifier.
|
|
33
|
+
* @returns An encoded public timeline URL.
|
|
34
|
+
*/
|
|
35
|
+
export declare function releaseProjectUrl(projectId: string): string;
|
|
36
|
+
/**
|
|
37
|
+
* Accepts only explicit HTTP(S) destinations.
|
|
38
|
+
* @param value - Untrusted Markdown or API URL.
|
|
39
|
+
* @returns A normalized safe URL, or undefined for unsafe destinations.
|
|
40
|
+
*/
|
|
41
|
+
export declare function safeHttpUrl(value: string): string | undefined;
|
|
42
|
+
/**
|
|
43
|
+
* Resolves private image references through the release-scoped public gateway.
|
|
44
|
+
* @param projectId - Public project identifier.
|
|
45
|
+
* @param releaseId - Release which owns the image.
|
|
46
|
+
* @param source - Untrusted Markdown image source.
|
|
47
|
+
* @returns A managed gateway URL or a safe external image URL.
|
|
48
|
+
*/
|
|
49
|
+
export declare function releaseImageUrl(projectId: string, releaseId: string, source: string): string | undefined;
|
|
50
|
+
/**
|
|
51
|
+
* Parses an untrusted HTTP response before it reaches renderers or storage.
|
|
52
|
+
* @param value - JSON obtained from the gateway or local cache.
|
|
53
|
+
* @returns A fresh validated release page.
|
|
54
|
+
* @throws Error when the payload violates the bounded public contract.
|
|
55
|
+
*/
|
|
56
|
+
export declare function parseReleasePage(value: unknown): ReleasePage;
|
|
57
|
+
/** Options for one anonymous request. */
|
|
58
|
+
export interface FetchReleasePageOptions {
|
|
59
|
+
version?: string;
|
|
60
|
+
/** Exact application version; mutually exclusive with cursor. */
|
|
61
|
+
cursor?: string;
|
|
62
|
+
/** Opaque continuation token from a previous page. */
|
|
63
|
+
signal?: AbortSignal;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Canonicalizes a strict semantic version while retaining build metadata.
|
|
67
|
+
* @param value - Consumer or server version string.
|
|
68
|
+
* @returns Canonical exact version, including prerelease and build identifiers.
|
|
69
|
+
* @throws Error when the version is not valid semantic version syntax.
|
|
70
|
+
*/
|
|
71
|
+
export declare function normalizeReleaseVersion(value: string): string;
|
|
72
|
+
/**
|
|
73
|
+
* Requests one validated page without credentials or customer configuration.
|
|
74
|
+
* @param projectId - Public project identifier.
|
|
75
|
+
* @param options - Exact version or continuation and cancellation signal.
|
|
76
|
+
* @param options.version - Optional exact semantic version.
|
|
77
|
+
* @param options.cursor - Optional opaque pagination cursor.
|
|
78
|
+
* @param options.signal - Optional cancellation signal.
|
|
79
|
+
* @returns A publication-ordered validated page.
|
|
80
|
+
* @throws ReleaseUnavailableError on authoritative 404; Error on other failures.
|
|
81
|
+
*/
|
|
82
|
+
export declare function fetchReleasePage(projectId: string, options?: FetchReleasePageOptions): Promise<ReleasePage>;
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Defines and validates the anonymous release-note HTTP contract.
|
|
3
|
+
*
|
|
4
|
+
* Owns fixed-origin requests, safe public links and managed image addressing.
|
|
5
|
+
*
|
|
6
|
+
* Keep this wire contract compatible with the gateway when extending fields.
|
|
7
|
+
*/
|
|
8
|
+
import { SemVer, valid } from 'semver';
|
|
9
|
+
const origin = 'https://makershq.dev';
|
|
10
|
+
const managedImageSourcePattern = /^\.\/images\/[a-zA-Z0-9_-]+$/;
|
|
11
|
+
/** Signals an authoritative unavailable project. */
|
|
12
|
+
export class ReleaseUnavailableError extends Error {
|
|
13
|
+
/** Creates a safe unavailable error without exposing gateway details. */
|
|
14
|
+
constructor() {
|
|
15
|
+
super('These release notes are unavailable.');
|
|
16
|
+
this.name = 'ReleaseUnavailableError';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Builds a canonical anonymous project timeline URL.
|
|
21
|
+
* @param projectId - Public project identifier.
|
|
22
|
+
* @returns An encoded public timeline URL.
|
|
23
|
+
*/
|
|
24
|
+
export function releaseProjectUrl(projectId) {
|
|
25
|
+
return `${origin}/p/${encodeURIComponent(projectId)}/release-notes`;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Accepts only explicit HTTP(S) destinations.
|
|
29
|
+
* @param value - Untrusted Markdown or API URL.
|
|
30
|
+
* @returns A normalized safe URL, or undefined for unsafe destinations.
|
|
31
|
+
*/
|
|
32
|
+
export function safeHttpUrl(value) {
|
|
33
|
+
try {
|
|
34
|
+
const parsed = new URL(value);
|
|
35
|
+
return ['https:', 'http:'].includes(parsed.protocol) &&
|
|
36
|
+
!parsed.username &&
|
|
37
|
+
!parsed.password
|
|
38
|
+
? parsed.href
|
|
39
|
+
: undefined;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Resolves private image references through the release-scoped public gateway.
|
|
47
|
+
* @param projectId - Public project identifier.
|
|
48
|
+
* @param releaseId - Release which owns the image.
|
|
49
|
+
* @param source - Untrusted Markdown image source.
|
|
50
|
+
* @returns A managed gateway URL or a safe external image URL.
|
|
51
|
+
*/
|
|
52
|
+
export function releaseImageUrl(projectId, releaseId, source) {
|
|
53
|
+
if (managedImageSourcePattern.test(source)) {
|
|
54
|
+
return `${origin}/api/v1/projects/${encodeURIComponent(projectId)}/releases/${encodeURIComponent(releaseId)}/image?source=${encodeURIComponent(source)}`;
|
|
55
|
+
}
|
|
56
|
+
return safeHttpUrl(source);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Parses an untrusted HTTP response before it reaches renderers or storage.
|
|
60
|
+
* @param value - JSON obtained from the gateway or local cache.
|
|
61
|
+
* @returns A fresh validated release page.
|
|
62
|
+
* @throws Error when the payload violates the bounded public contract.
|
|
63
|
+
*/
|
|
64
|
+
export function parseReleasePage(value) {
|
|
65
|
+
if (!value || typeof value !== 'object')
|
|
66
|
+
throw new Error('Invalid release notes response.');
|
|
67
|
+
const page = value;
|
|
68
|
+
if (!Array.isArray(page.items) ||
|
|
69
|
+
page.items.length > 10 ||
|
|
70
|
+
!(page.nextCursor === null || typeof page.nextCursor === 'string') ||
|
|
71
|
+
(typeof page.nextCursor === 'string' && page.nextCursor.length > 4096))
|
|
72
|
+
throw new Error('Invalid release notes response.');
|
|
73
|
+
const items = page.items.map((item) => {
|
|
74
|
+
if (!item || typeof item !== 'object')
|
|
75
|
+
throw new Error('Invalid release note.');
|
|
76
|
+
const release = item;
|
|
77
|
+
if (typeof release.id !== 'string' ||
|
|
78
|
+
!release.id ||
|
|
79
|
+
release.id.length > 512 ||
|
|
80
|
+
!(release.version === null ||
|
|
81
|
+
(typeof release.version === 'string' && release.version.length <= 256)) ||
|
|
82
|
+
typeof release.title !== 'string' ||
|
|
83
|
+
release.title.length > 1000 ||
|
|
84
|
+
typeof release.markdown !== 'string' ||
|
|
85
|
+
release.markdown.length > 500000 ||
|
|
86
|
+
typeof release.date !== 'string' ||
|
|
87
|
+
!/^\d{4}-\d{2}-\d{2}$/.test(release.date) ||
|
|
88
|
+
Number.isNaN(Date.parse(`${release.date}T12:00:00Z`)) ||
|
|
89
|
+
new Date(`${release.date}T12:00:00Z`).toISOString().slice(0, 10) !==
|
|
90
|
+
release.date ||
|
|
91
|
+
typeof release.url !== 'string' ||
|
|
92
|
+
!safeHttpUrl(release.url) ||
|
|
93
|
+
(release.excerpt !== undefined &&
|
|
94
|
+
(typeof release.excerpt !== 'string' ||
|
|
95
|
+
[...release.excerpt].length > 240)) ||
|
|
96
|
+
(release.previewImageSource !== undefined &&
|
|
97
|
+
release.previewImageSource !== null &&
|
|
98
|
+
(typeof release.previewImageSource !== 'string' ||
|
|
99
|
+
release.previewImageSource.length > 2048)))
|
|
100
|
+
throw new Error('Invalid release note.');
|
|
101
|
+
return {
|
|
102
|
+
id: release.id,
|
|
103
|
+
version: release.version,
|
|
104
|
+
title: release.title,
|
|
105
|
+
...(release.excerpt === undefined ? {} : { excerpt: release.excerpt }),
|
|
106
|
+
...(release.previewImageSource === undefined
|
|
107
|
+
? {}
|
|
108
|
+
: {
|
|
109
|
+
previewImageSource: release.previewImageSource !== null &&
|
|
110
|
+
(managedImageSourcePattern.test(release.previewImageSource) ||
|
|
111
|
+
safeHttpUrl(release.previewImageSource))
|
|
112
|
+
? release.previewImageSource
|
|
113
|
+
: null,
|
|
114
|
+
}),
|
|
115
|
+
markdown: release.markdown,
|
|
116
|
+
date: release.date,
|
|
117
|
+
url: release.url,
|
|
118
|
+
};
|
|
119
|
+
});
|
|
120
|
+
if (new Set(items.map((item) => item.id)).size !== items.length)
|
|
121
|
+
throw new Error('Invalid release notes response.');
|
|
122
|
+
return { items, nextCursor: page.nextCursor };
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Canonicalizes a strict semantic version while retaining build metadata.
|
|
126
|
+
* @param value - Consumer or server version string.
|
|
127
|
+
* @returns Canonical exact version, including prerelease and build identifiers.
|
|
128
|
+
* @throws Error when the version is not valid semantic version syntax.
|
|
129
|
+
*/
|
|
130
|
+
export function normalizeReleaseVersion(value) {
|
|
131
|
+
if (!valid(value.trim()))
|
|
132
|
+
throw new Error('A valid semantic version is required.');
|
|
133
|
+
const version = new SemVer(value.trim());
|
|
134
|
+
return (version.version +
|
|
135
|
+
(version.build.length ? `+${version.build.join('.')}` : ''));
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Requests one validated page without credentials or customer configuration.
|
|
139
|
+
* @param projectId - Public project identifier.
|
|
140
|
+
* @param options - Exact version or continuation and cancellation signal.
|
|
141
|
+
* @param options.version - Optional exact semantic version.
|
|
142
|
+
* @param options.cursor - Optional opaque pagination cursor.
|
|
143
|
+
* @param options.signal - Optional cancellation signal.
|
|
144
|
+
* @returns A publication-ordered validated page.
|
|
145
|
+
* @throws ReleaseUnavailableError on authoritative 404; Error on other failures.
|
|
146
|
+
*/
|
|
147
|
+
export async function fetchReleasePage(projectId, options = {}) {
|
|
148
|
+
if (options.version !== undefined && options.cursor !== undefined)
|
|
149
|
+
throw new Error('Version and cursor cannot be combined.');
|
|
150
|
+
const exactVersion = options.version === undefined
|
|
151
|
+
? undefined
|
|
152
|
+
: normalizeReleaseVersion(options.version);
|
|
153
|
+
if (!projectId.trim() || projectId.length > 512)
|
|
154
|
+
throw new Error('A project ID is required.');
|
|
155
|
+
const url = new URL(`${origin}/api/v1/projects/${encodeURIComponent(projectId)}/releases`);
|
|
156
|
+
if (options.version !== undefined)
|
|
157
|
+
url.searchParams.set('version', exactVersion);
|
|
158
|
+
else if (options.cursor)
|
|
159
|
+
url.searchParams.set('cursor', options.cursor);
|
|
160
|
+
const response = await fetch(url, {
|
|
161
|
+
credentials: 'omit',
|
|
162
|
+
cache: 'no-store',
|
|
163
|
+
signal: options.signal,
|
|
164
|
+
headers: { Accept: 'application/json' },
|
|
165
|
+
});
|
|
166
|
+
if (response.status === 404)
|
|
167
|
+
throw new ReleaseUnavailableError();
|
|
168
|
+
if (!response.ok)
|
|
169
|
+
throw new Error('Release notes could not be loaded.');
|
|
170
|
+
const contentLength = Number(response.headers.get('content-length'));
|
|
171
|
+
if (contentLength > 6000000)
|
|
172
|
+
throw new Error('Release notes response is too large.');
|
|
173
|
+
const body = await response.text();
|
|
174
|
+
if (body.length > 6000000)
|
|
175
|
+
throw new Error('Release notes response is too large.');
|
|
176
|
+
const page = parseReleasePage(JSON.parse(body));
|
|
177
|
+
if (options.version !== undefined &&
|
|
178
|
+
(page.items.length > 1 ||
|
|
179
|
+
page.items.some((release) => release.version !== exactVersion) ||
|
|
180
|
+
page.nextCursor !== null))
|
|
181
|
+
throw new Error('Invalid exact-version release response.');
|
|
182
|
+
return page;
|
|
183
|
+
}
|
package/dist/cache.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stores a small best-effort offline copy of recently viewed public notes.
|
|
3
|
+
*
|
|
4
|
+
* Owns bounded, project-and-version-isolated local storage and invalidation.
|
|
5
|
+
*
|
|
6
|
+
* Keep storage optional and never allow cached data to override a server 404.
|
|
7
|
+
*/
|
|
8
|
+
import { type ReleasePage } from './api.js';
|
|
9
|
+
/**
|
|
10
|
+
* Reads one isolated offline snapshot.
|
|
11
|
+
* @param projectId - Public project identifier.
|
|
12
|
+
* @param version - Exact application version, or full-history first page.
|
|
13
|
+
* @returns Validated saved notes, when available.
|
|
14
|
+
*/
|
|
15
|
+
export declare function readReleaseCache(projectId: string, version?: string): ReleasePage | undefined;
|
|
16
|
+
/**
|
|
17
|
+
* Replaces one snapshot or invalidates every cached view of an unavailable project.
|
|
18
|
+
* @param projectId - Public project identifier.
|
|
19
|
+
* @param version - Exact requested version, or history.
|
|
20
|
+
* @param page - Validated first page; null removes all cached views for this project.
|
|
21
|
+
*/
|
|
22
|
+
export declare function writeReleaseCache(projectId: string, version: string | undefined, page: ReleasePage | null): void;
|
package/dist/cache.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stores a small best-effort offline copy of recently viewed public notes.
|
|
3
|
+
*
|
|
4
|
+
* Owns bounded, project-and-version-isolated local storage and invalidation.
|
|
5
|
+
*
|
|
6
|
+
* Keep storage optional and never allow cached data to override a server 404.
|
|
7
|
+
*/
|
|
8
|
+
import { parseReleasePage } from './api.js';
|
|
9
|
+
const key = 'makershq.release-notes.v1';
|
|
10
|
+
const maxEntries = 5;
|
|
11
|
+
const maxBytes = 1000000;
|
|
12
|
+
function cacheKey(projectId, version) {
|
|
13
|
+
return JSON.stringify([projectId, version?.trim() ?? null]);
|
|
14
|
+
}
|
|
15
|
+
function entries() {
|
|
16
|
+
try {
|
|
17
|
+
const raw = localStorage.getItem(key);
|
|
18
|
+
if (!raw ||
|
|
19
|
+
raw.length > maxBytes ||
|
|
20
|
+
new TextEncoder().encode(raw).length > maxBytes)
|
|
21
|
+
return [];
|
|
22
|
+
const parsed = JSON.parse(raw);
|
|
23
|
+
if (!Array.isArray(parsed))
|
|
24
|
+
return [];
|
|
25
|
+
return parsed.slice(0, maxEntries).flatMap((item) => {
|
|
26
|
+
if (!item || typeof item !== 'object')
|
|
27
|
+
return [];
|
|
28
|
+
const entry = item;
|
|
29
|
+
if (typeof entry.key !== 'string')
|
|
30
|
+
return [];
|
|
31
|
+
try {
|
|
32
|
+
return [
|
|
33
|
+
{
|
|
34
|
+
key: entry.key,
|
|
35
|
+
page: parseReleasePage(entry.page),
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Reads one isolated offline snapshot.
|
|
50
|
+
* @param projectId - Public project identifier.
|
|
51
|
+
* @param version - Exact application version, or full-history first page.
|
|
52
|
+
* @returns Validated saved notes, when available.
|
|
53
|
+
*/
|
|
54
|
+
export function readReleaseCache(projectId, version) {
|
|
55
|
+
const page = entries().find((entry) => entry.key === cacheKey(projectId, version))?.page;
|
|
56
|
+
if (version !== undefined &&
|
|
57
|
+
page?.items.some((release) => release.version !== version.trim()))
|
|
58
|
+
return undefined;
|
|
59
|
+
return page;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Replaces one snapshot or invalidates every cached view of an unavailable project.
|
|
63
|
+
* @param projectId - Public project identifier.
|
|
64
|
+
* @param version - Exact requested version, or history.
|
|
65
|
+
* @param page - Validated first page; null removes all cached views for this project.
|
|
66
|
+
*/
|
|
67
|
+
export function writeReleaseCache(projectId, version, page) {
|
|
68
|
+
try {
|
|
69
|
+
const id = cacheKey(projectId, version);
|
|
70
|
+
const retained = entries().filter((entry) => page
|
|
71
|
+
? entry.key !== id
|
|
72
|
+
: !entry.key.startsWith(JSON.stringify([projectId]).slice(0, -1) + ','));
|
|
73
|
+
if (page)
|
|
74
|
+
retained.unshift({ key: id, page });
|
|
75
|
+
let serialized = JSON.stringify(retained.slice(0, maxEntries));
|
|
76
|
+
while (new TextEncoder().encode(serialized).length > maxBytes &&
|
|
77
|
+
retained.length) {
|
|
78
|
+
retained.pop();
|
|
79
|
+
serialized = JSON.stringify(retained);
|
|
80
|
+
}
|
|
81
|
+
localStorage.setItem(key, serialized);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
/* Storage denial must not prevent reading public notes. */
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type IconProps } from '@animate-ui/react/icons';
|
|
2
|
+
type ExternalLinkProps = IconProps<keyof typeof animations>;
|
|
3
|
+
declare const animations: {
|
|
4
|
+
readonly default: {
|
|
5
|
+
group: {
|
|
6
|
+
initial: {
|
|
7
|
+
x: number;
|
|
8
|
+
y: number;
|
|
9
|
+
transition: {
|
|
10
|
+
duration: number;
|
|
11
|
+
ease: "easeInOut";
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
animate: {
|
|
15
|
+
x: number;
|
|
16
|
+
y: number;
|
|
17
|
+
transition: {
|
|
18
|
+
duration: number;
|
|
19
|
+
ease: "easeInOut";
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
path1: {};
|
|
24
|
+
path2: {};
|
|
25
|
+
path3: {};
|
|
26
|
+
};
|
|
27
|
+
readonly 'default-loop': {
|
|
28
|
+
group: {
|
|
29
|
+
initial: {
|
|
30
|
+
x: number;
|
|
31
|
+
y: number;
|
|
32
|
+
};
|
|
33
|
+
animate: {
|
|
34
|
+
x: number[];
|
|
35
|
+
y: number[];
|
|
36
|
+
transition: {
|
|
37
|
+
duration: number;
|
|
38
|
+
ease: "easeInOut";
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
path1: {};
|
|
43
|
+
path2: {};
|
|
44
|
+
path3: {};
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
/** Renders Animate UI's original external-link icon.
|
|
48
|
+
* @param props - Upstream SVG presentation and playback controls.
|
|
49
|
+
* @returns The original vector artwork and animation.
|
|
50
|
+
*/
|
|
51
|
+
export declare function ExternalLink(props: ExternalLinkProps): import("react").JSX.Element;
|
|
52
|
+
export {};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provides Animate UI's original external-link icon from the official registry.
|
|
3
|
+
*
|
|
4
|
+
* Preserves upstream rendering, variants, timing and easing without motion overrides.
|
|
5
|
+
*
|
|
6
|
+
* Follow README.md when updating registry sources; retain the adjacent license notices.
|
|
7
|
+
*/
|
|
8
|
+
'use client';
|
|
9
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
10
|
+
import { motion } from 'motion/react';
|
|
11
|
+
import { useIconReducedMotion } from '../reduced-motion.js';
|
|
12
|
+
import { getVariants, useAnimateIconContext, IconWrapper, } from '@animate-ui/react/icons';
|
|
13
|
+
const animations = {
|
|
14
|
+
default: {
|
|
15
|
+
group: {
|
|
16
|
+
initial: {
|
|
17
|
+
x: 0,
|
|
18
|
+
y: 0,
|
|
19
|
+
transition: { duration: 0.3, ease: 'easeInOut' },
|
|
20
|
+
},
|
|
21
|
+
animate: {
|
|
22
|
+
x: 2,
|
|
23
|
+
y: -2,
|
|
24
|
+
transition: { duration: 0.3, ease: 'easeInOut' },
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
path1: {},
|
|
28
|
+
path2: {},
|
|
29
|
+
path3: {},
|
|
30
|
+
},
|
|
31
|
+
'default-loop': {
|
|
32
|
+
group: {
|
|
33
|
+
initial: {
|
|
34
|
+
x: 0,
|
|
35
|
+
y: 0,
|
|
36
|
+
},
|
|
37
|
+
animate: {
|
|
38
|
+
x: [0, 2, 0],
|
|
39
|
+
y: [0, -2, 0],
|
|
40
|
+
transition: { duration: 0.6, ease: 'easeInOut' },
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
path1: {},
|
|
44
|
+
path2: {},
|
|
45
|
+
path3: {},
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
function IconComponent({ size, ...props }) {
|
|
49
|
+
const { controls } = useAnimateIconContext();
|
|
50
|
+
const reduced = useIconReducedMotion();
|
|
51
|
+
const variants = getVariants(animations);
|
|
52
|
+
return (_jsxs(motion.svg, { xmlns: "http://www.w3.org/2000/svg", width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", ...props, children: [_jsxs(motion.g, { variants: variants.group, initial: "initial", animate: reduced ? 'initial' : controls, children: [_jsx(motion.path, { d: "M15 3h6v6", variants: variants.path1, initial: "initial", animate: reduced ? 'initial' : controls }), _jsx(motion.path, { d: "M10 14 21 3", variants: variants.path2, initial: "initial", animate: reduced ? 'initial' : controls })] }), _jsx(motion.path, { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6", variants: variants.path3, initial: "initial", animate: reduced ? 'initial' : controls })] }, reduced ? 'static' : 'animated'));
|
|
53
|
+
}
|
|
54
|
+
/** Renders Animate UI's original external-link icon.
|
|
55
|
+
* @param props - Upstream SVG presentation and playback controls.
|
|
56
|
+
* @returns The original vector artwork and animation.
|
|
57
|
+
*/
|
|
58
|
+
export function ExternalLink(props) {
|
|
59
|
+
return _jsx(IconWrapper, { icon: IconComponent, ...props });
|
|
60
|
+
}
|