@reykjavik/webtools 0.0.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/http.d.ts ADDED
@@ -0,0 +1,82 @@
1
+ import { ServerResponse } from 'http';
2
+ export declare const HTTP_200_OK = 200;
3
+ /**
4
+ * The request succeeded, and a new resource was created as a result.
5
+ * This is typically the response sent after POST requests,
6
+ * or some PUT requests.
7
+ **/
8
+ export declare const HTTP_201_Created = 201;
9
+ export declare const HTTP_202_Accepted = 202;
10
+ /**
11
+ * Only safe to use in response to GET and HEAD requests
12
+ *
13
+ * @deprecated Instead use `HTTP_308_PermanentRedirect`.
14
+ */
15
+ export declare const HTTP_301_MovedPermanently = 301;
16
+ /**
17
+ * Only safe to use in response to GET and HEAD requests
18
+ *
19
+ * @deprecated Instead use `HTTP_307_TemporaryRedirect`.
20
+ */
21
+ export declare const HTTP_302_Found = 302;
22
+ /** Use when POST or PUT successfully rediects to the created resource */
23
+ export declare const HTTP_303_SeeOther = 303;
24
+ /** Use in response GET and HEAD requests with `If-Modified-Since`/`If-None-Match` heaaders */
25
+ export declare const HTTP_304_NotModified = 304;
26
+ export declare const HTTP_307_TemporaryRedirect = 307;
27
+ export declare const HTTP_308_PermanentRedirect = 308;
28
+ /** The request is malformed (e.g. a URL param is of a wrong type) */
29
+ export declare const HTTP_400_BadRequest = 400;
30
+ /** User is not authenticated (i.e. not logged in) */
31
+ export declare const HTTP_401_Unauthorized = 401;
32
+ /** User is logged in but doesn't have the necessary privileges */
33
+ export declare const HTTP_403_Forbidden = 403;
34
+ /** The request looks OK but the resource does not exist */
35
+ export declare const HTTP_404_NotFound = 404;
36
+ /**
37
+ * The resource has been permanently deleted from server, with no forwarding
38
+ * address.
39
+ */
40
+ export declare const HTTP_410_Gone = 410;
41
+ /** The server refuses the attempt to brew coffee with a teapot. */
42
+ export declare const HTTP_418_ImATeapot = 418;
43
+ export declare const HTTP_500_InternalServerError = 500;
44
+ export type HTTP_SUCCESS = typeof HTTP_200_OK | typeof HTTP_201_Created | typeof HTTP_202_Accepted;
45
+ export type HTTP_REDIRECTION = typeof HTTP_301_MovedPermanently | typeof HTTP_302_Found | typeof HTTP_303_SeeOther | typeof HTTP_304_NotModified | typeof HTTP_307_TemporaryRedirect | typeof HTTP_308_PermanentRedirect;
46
+ export type HTTP_NOTMODIFIED = typeof HTTP_304_NotModified;
47
+ export type HTTP_CLIENT_ERROR = typeof HTTP_400_BadRequest | typeof HTTP_401_Unauthorized | typeof HTTP_403_Forbidden | typeof HTTP_404_NotFound | typeof HTTP_410_Gone | typeof HTTP_418_ImATeapot;
48
+ export type HTTP_NOT_FOUND = typeof HTTP_400_BadRequest | typeof HTTP_404_NotFound | typeof HTTP_410_Gone;
49
+ export type HTTP_BANNED = typeof HTTP_401_Unauthorized | typeof HTTP_403_Forbidden;
50
+ export type HTTP_SERVER_ERROR = typeof HTTP_500_InternalServerError;
51
+ export type HTTP_ERROR = HTTP_CLIENT_ERROR | HTTP_SERVER_ERROR;
52
+ export type HTTP_STATUS = HTTP_SUCCESS | HTTP_REDIRECTION | HTTP_CLIENT_ERROR | HTTP_SERVER_ERROR;
53
+ type TimeUnit = 's' | 'm' | 'h' | 'd' | 'w';
54
+ type TTL = number | `${number}${TimeUnit}`;
55
+ type TTLKeywords = 'permanent' | 'unset' | 'no-cache';
56
+ type TTLObj = {
57
+ /** Sets the cache `max-age=` for the resource. */
58
+ maxAge: TTL | TTLKeywords;
59
+ /** Sets `stale-while-revalidate=` for the resource */
60
+ staleWhileRevalidate?: TTL;
61
+ staleIfError?: TTL;
62
+ /** Sets the response caching as "public", instead of the default "private" */
63
+ publ?: boolean;
64
+ /** Sets a 'must-revalidate' flag instead of the default 'immutable' */
65
+ stability?: 'revalidate' | 'immutable' | 'normal';
66
+ };
67
+ /**
68
+ * Configures quick TTL-related settings for a HTTP request object
69
+ *
70
+ * @see https://github.com/reykjavikcity/webtools/tree/v0.1#type-ttlconfig
71
+ */
72
+ export type TTLConfig = TTL | TTLKeywords | TTLObj;
73
+ /**
74
+ * Use this function to quickly set the `Cache-Control` header with a `max-age=`
75
+ * on a HTTP response
76
+ *
77
+ * @see https://github.com/reykjavikcity/webtools/tree/v0.1#getcssbundleurl
78
+ */
79
+ export declare const cacheControl: (response: ServerResponse | {
80
+ res: ServerResponse;
81
+ }, ttlCfg: TTLConfig, eTag?: string | number) => void;
82
+ export {};
package/http.js ADDED
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.cacheControl = exports.HTTP_500_InternalServerError = exports.HTTP_418_ImATeapot = exports.HTTP_410_Gone = exports.HTTP_404_NotFound = exports.HTTP_403_Forbidden = exports.HTTP_401_Unauthorized = exports.HTTP_400_BadRequest = exports.HTTP_308_PermanentRedirect = exports.HTTP_307_TemporaryRedirect = exports.HTTP_304_NotModified = exports.HTTP_303_SeeOther = exports.HTTP_302_Found = exports.HTTP_301_MovedPermanently = exports.HTTP_202_Accepted = exports.HTTP_201_Created = exports.HTTP_200_OK = void 0;
4
+ exports.HTTP_200_OK = 200;
5
+ /**
6
+ * The request succeeded, and a new resource was created as a result.
7
+ * This is typically the response sent after POST requests,
8
+ * or some PUT requests.
9
+ **/
10
+ exports.HTTP_201_Created = 201;
11
+ /*
12
+ * The request has been received but not yet acted upon.
13
+ * Uee in cases where another process or server handles the request.
14
+ */
15
+ exports.HTTP_202_Accepted = 202;
16
+ /**
17
+ * Only safe to use in response to GET and HEAD requests
18
+ *
19
+ * @deprecated Instead use `HTTP_308_PermanentRedirect`.
20
+ */
21
+ exports.HTTP_301_MovedPermanently = 301;
22
+ /**
23
+ * Only safe to use in response to GET and HEAD requests
24
+ *
25
+ * @deprecated Instead use `HTTP_307_TemporaryRedirect`.
26
+ */
27
+ exports.HTTP_302_Found = 302;
28
+ /** Use when POST or PUT successfully rediects to the created resource */
29
+ exports.HTTP_303_SeeOther = 303;
30
+ /** Use in response GET and HEAD requests with `If-Modified-Since`/`If-None-Match` heaaders */
31
+ exports.HTTP_304_NotModified = 304;
32
+ exports.HTTP_307_TemporaryRedirect = 307;
33
+ exports.HTTP_308_PermanentRedirect = 308;
34
+ /** The request is malformed (e.g. a URL param is of a wrong type) */
35
+ exports.HTTP_400_BadRequest = 400;
36
+ /** User is not authenticated (i.e. not logged in) */
37
+ exports.HTTP_401_Unauthorized = 401;
38
+ /** User is logged in but doesn't have the necessary privileges */
39
+ exports.HTTP_403_Forbidden = 403;
40
+ /** The request looks OK but the resource does not exist */
41
+ exports.HTTP_404_NotFound = 404;
42
+ /**
43
+ * The resource has been permanently deleted from server, with no forwarding
44
+ * address.
45
+ */
46
+ exports.HTTP_410_Gone = 410;
47
+ /** The server refuses the attempt to brew coffee with a teapot. */
48
+ exports.HTTP_418_ImATeapot = 418;
49
+ exports.HTTP_500_InternalServerError = 500;
50
+ const unitToSeconds = {
51
+ s: 1,
52
+ m: 60,
53
+ h: 3600,
54
+ d: 24 * 3600,
55
+ w: 7 * 24 * 3600,
56
+ };
57
+ const toSec = (ttl) => {
58
+ if (ttl == null) {
59
+ return;
60
+ }
61
+ if (typeof ttl === 'string') {
62
+ const value = parseFloat(ttl);
63
+ const factor = unitToSeconds[ttl.slice(-1)] || 1;
64
+ ttl = value * factor;
65
+ }
66
+ return !isNaN(ttl) ? ttl : undefined;
67
+ };
68
+ const stabilities = {
69
+ revalidate: ', must-revalidate',
70
+ immutable: ', immutable',
71
+ normal: '',
72
+ };
73
+ const setCC = (response, cc) => {
74
+ const devModeHeader = 'X-Cache-Control';
75
+ // Also set `X-Cache-Control` in dev mode, because some frameworks
76
+ // **cough** **nextjs** **cough** forcefully override the `Cache-Control`
77
+ // header when the server is in dev mode.
78
+ if (!cc) {
79
+ response.removeHeader('Cache-Control');
80
+ process.env.NODE_ENV !== 'production' && response.removeHeader(devModeHeader);
81
+ return;
82
+ }
83
+ response.setHeader('Cache-Control', cc);
84
+ process.env.NODE_ENV !== 'production' && response.setHeader(devModeHeader, cc);
85
+ };
86
+ /**
87
+ * Use this function to quickly set the `Cache-Control` header with a `max-age=`
88
+ * on a HTTP response
89
+ *
90
+ * @see https://github.com/reykjavikcity/webtools/tree/v0.1#getcssbundleurl
91
+ */
92
+ // eslint-disable-next-line complexity
93
+ const cacheControl = (response, ttlCfg, eTag) => {
94
+ response = 'res' in response ? response.res : response;
95
+ const opts = typeof ttlCfg === 'number' || typeof ttlCfg === 'string'
96
+ ? { maxAge: ttlCfg }
97
+ : ttlCfg;
98
+ let maxAge = opts.maxAge;
99
+ if (typeof maxAge === 'string') {
100
+ if (maxAge === 'permanent') {
101
+ maxAge = 365 * unitToSeconds.d;
102
+ }
103
+ else if (maxAge === 'no-cache') {
104
+ maxAge = 0;
105
+ }
106
+ else if (maxAge === 'unset') {
107
+ maxAge = undefined;
108
+ }
109
+ }
110
+ maxAge = toSec(maxAge);
111
+ if (maxAge == null) {
112
+ response.removeHeader('Cache-Control');
113
+ return;
114
+ }
115
+ const sWR_ttl = toSec(opts.staleWhileRevalidate);
116
+ const sWR = sWR_ttl != null ? `, stale-while-revalidate=${sWR_ttl}` : '';
117
+ const sIE_ttl = toSec(opts.staleIfError);
118
+ const sIE = sIE_ttl != null ? `, stale-if-error=${sIE_ttl}` : '';
119
+ maxAge = Math.round(maxAge);
120
+ if (maxAge <= 0) {
121
+ setCC(response, 'no-cache');
122
+ return;
123
+ }
124
+ const scope = opts.publ ? 'public' : 'private';
125
+ const stability = (opts.stability && stabilities[opts.stability]) || stabilities.immutable;
126
+ eTag != null && response.setHeader('ETag', eTag);
127
+ setCC(response, `${scope}, max-age=${maxAge + sWR + sIE + stability}`);
128
+ };
129
+ exports.cacheControl = cacheControl;
package/index.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ /// <reference path="./CookieHubConsent.d.tsx" />
2
+ /// <reference path="./http.d.ts" />
3
+ /// <reference path="./next/http.d.tsx" />
4
+ /// <reference path="./next/SiteImprove.d.tsx" />
5
+
6
+ export {};
package/index.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,81 @@
1
+ /// <reference types="react" />
2
+ import { EitherObj } from '@reykjavik/hanna-utils';
3
+ declare global {
4
+ interface Window {
5
+ _sz?: Array<SiteImproveEvent> & {
6
+ /**
7
+ * Set if posting a tracking event is attempted before SiteImprove has
8
+ * been initialized, and the window._sz array had to be initialized
9
+ * just-in-time.
10
+ */
11
+ _jit_defined_?: true;
12
+ };
13
+ }
14
+ }
15
+ type SiteImproveEvent = SiteImprovePageView | SiteImproveCustomEvent;
16
+ type SiteImprovePageView = [
17
+ type: 'trackdynamic',
18
+ data: {
19
+ /** New page URL */
20
+ url: string;
21
+ /** The previous (referer) URL */
22
+ ref: string;
23
+ /** New page title */
24
+ title?: string;
25
+ }
26
+ ];
27
+ type SiteImproveCustomEvent = [
28
+ type: 'event',
29
+ category: string,
30
+ action: string,
31
+ label?: string
32
+ ];
33
+ export type SiteImproveProps = EitherObj<{
34
+ /**
35
+ * Your SiteImprove account ID.
36
+ *
37
+ * It's a random-looking numerical string, and it can usually be
38
+ * extracted from the script embed URL like this:
39
+ * `"https://siteimproveanalytics.com/js/siteanalyze_[ACCOUNT_ID].js"`
40
+ */
41
+ accountId: string;
42
+ }, {
43
+ /**
44
+ * The full SiteImprove analytics script URL.
45
+ *
46
+ * Something like `"https://siteimproveanalytics.com/js/siteanalyze_[ACCOUNT_ID].js"`
47
+ */
48
+ scriptUrl: string;
49
+ }> & {
50
+ /**
51
+ * Manual GDPR 'analytics' consent flag.
52
+ *
53
+ * A value of `false` prevents the analytics script being loaded.
54
+ *
55
+ * Any other value causes the component to defer to the `CookieHubProvider`
56
+ * component, and only applies if the cookiehub flag is undefined.
57
+ */
58
+ hasConstented?: boolean;
59
+ /**
60
+ * Custom callback for when the SiteImprove script has loaded.
61
+ */
62
+ onLoad?: (e: unknown) => void;
63
+ /**
64
+ * Error callback for if the SiteImprove script fails to load.
65
+ */
66
+ onError?: (e: unknown) => void;
67
+ };
68
+ /**
69
+ * A component for loading a SiteImprove analytics script and set up page-view
70
+ * tracking across Next.js routes.
71
+ *
72
+ * @see https://github.com/reykjavikcity/webtools/tree/v0.1##siteimprove-component
73
+ */
74
+ export declare const SiteImprove: (props: SiteImproveProps) => JSX.Element | null;
75
+ /**
76
+ * A small helper for tracking custom UI events and reporting them to SiteImrove.
77
+ *
78
+ * @see https://github.com/reykjavikcity/webtools/tree/v0.1##pingsiteimprove-helper
79
+ */
80
+ export declare const pingSiteImprove: (category: string, action: string, label?: string) => void;
81
+ export {};
@@ -0,0 +1,131 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.pingSiteImprove = exports.SiteImprove = void 0;
30
+ const react_1 = __importStar(require("react"));
31
+ const router_1 = require("next/router");
32
+ const script_1 = __importDefault(require("next/script"));
33
+ const CookieHubConsent_1 = require("../CookieHubConsent");
34
+ // END: Mock typing of SiteImprove's event tracking API
35
+ // --------------------------------------------------------------------------
36
+ //
37
+ // ---------------------------------------------------------------------------
38
+ const _emitEvent = typeof window === 'undefined'
39
+ ? () => undefined
40
+ : (event) => {
41
+ let _sz = window._sz;
42
+ if (!_sz) {
43
+ _sz = window._sz = [];
44
+ _sz._jit_defined_ = true;
45
+ }
46
+ if (process.env.NODE_ENV === 'development') {
47
+ console.info('SiteImprove:', event);
48
+ }
49
+ else {
50
+ _sz.push(event);
51
+ }
52
+ };
53
+ /*
54
+ SiteImprove's "trackdynamic" (page view) event requires both the new URL
55
+ and the old (referer) URL.
56
+ Next router's `routeChangeComplete` (which is the correct point in time
57
+ to send the tracking event) does not provide access to the previous URL,
58
+ so we need to capture it during `routeChangeStart`.
59
+ We feel it's safe to assume that every `routeChangeComplete` event
60
+ always fires directly after its `routeChangeStart` counterpart,
61
+ and it is thus safe to simply store the old URL in a local variable.
62
+ This may look dodgy, but should prove reasonably safe in practice.
63
+ */
64
+ let refUrl = '';
65
+ const captureRefUrl = () => {
66
+ refUrl = document.location.pathname + document.location.search;
67
+ };
68
+ const sendRoutingEvent = (url) => _emitEvent([
69
+ 'trackdynamic',
70
+ {
71
+ url,
72
+ ref: refUrl,
73
+ title: document.title,
74
+ },
75
+ ]);
76
+ // ---------------------------------------------------------------------------
77
+ const idToken = '[ACCOUNT_ID]';
78
+ const scriptUrlTemplate = `https://siteimproveanalytics.com/js/siteanalyze_${idToken}.js`;
79
+ /**
80
+ * A component for loading a SiteImprove analytics script and set up page-view
81
+ * tracking across Next.js routes.
82
+ *
83
+ * @see https://github.com/reykjavikcity/webtools/tree/v0.1##siteimprove-component
84
+ */
85
+ const SiteImprove = (props) => {
86
+ const { analytics } = (0, CookieHubConsent_1.useCookieHubConsent)();
87
+ const consented = (analytics && props.hasConstented !== false) ||
88
+ (analytics === undefined && props.hasConstented);
89
+ (0, react_1.useEffect)(() => {
90
+ if (consented) {
91
+ const routerEvents = router_1.Router.events;
92
+ routerEvents.on('routeChangeStart', captureRefUrl);
93
+ routerEvents.on('routeChangeComplete', sendRoutingEvent);
94
+ return () => {
95
+ routerEvents.off('routeChangeStart', captureRefUrl);
96
+ routerEvents.off('routeChangeComplete', sendRoutingEvent);
97
+ };
98
+ }
99
+ }, [consented]);
100
+ if (!consented) {
101
+ return null;
102
+ }
103
+ if (process.env.NODE_ENV !== 'production') {
104
+ console.info('Mock loading SiteImprove in development mode.');
105
+ if (!window._sz) {
106
+ setTimeout(() => {
107
+ window._sz = window._sz || [];
108
+ }, 300);
109
+ }
110
+ return null;
111
+ }
112
+ const scriptUrl = props.scriptUrl != null
113
+ ? props.scriptUrl
114
+ : scriptUrlTemplate.replace(idToken, props.accountId);
115
+ return (react_1.default.createElement(script_1.default, { type: "text/javascript", strategy: "afterInteractive", src: scriptUrl, onLoad: props.onLoad, onError: props.onError }));
116
+ };
117
+ exports.SiteImprove = SiteImprove;
118
+ // ---------------------------------------------------------------------------
119
+ /**
120
+ * A small helper for tracking custom UI events and reporting them to SiteImrove.
121
+ *
122
+ * @see https://github.com/reykjavikcity/webtools/tree/v0.1##pingsiteimprove-helper
123
+ */
124
+ const pingSiteImprove = (category, action, label) => {
125
+ if (process.env.NODE_ENV === 'development' &&
126
+ (!window._sz || window._sz._jit_defined_)) {
127
+ console.warn('`pingSiteImprove` Was called before SiteImprove script was loaded.');
128
+ }
129
+ _emitEvent(['event', category, action, label]);
130
+ };
131
+ exports.pingSiteImprove = pingSiteImprove;
package/next/http.d.ts ADDED
@@ -0,0 +1,56 @@
1
+ /// <reference types="node" />
2
+ import React, { FunctionComponent } from 'react';
3
+ import { Cleanup } from '@reykjavik/hanna-utils';
4
+ import { ServerResponse } from 'http';
5
+ import type { AppType } from 'next/app';
6
+ import type { HTTP_ERROR, TTLConfig } from '../http';
7
+ export * from '../http';
8
+ type NextContextLike = {
9
+ res: ServerResponse;
10
+ };
11
+ export type ErrorProps = {
12
+ statusCode: HTTP_ERROR;
13
+ /** If a HTTP_ERROR code is passed, a default error message is displayed */
14
+ message?: string;
15
+ };
16
+ type ErrorizedPageProps<EP extends ErrorProps = ErrorProps> = {
17
+ __error: ErrorProps;
18
+ } & Omit<EP, keyof ErrorProps>;
19
+ type ShowErrorPageFn<EP extends ErrorProps = ErrorProps> = (response: ServerResponse | NextContextLike, error: (ErrorProps extends EP ? HTTP_ERROR : never) | EP,
20
+ /** Defaults to `"2s"`. Gets forwarded on to the `cacheControl` helper from `@reykjavik/webtools/http` */
21
+ ttl?: TTLConfig) => {
22
+ props: ErrorizedPageProps<EP>;
23
+ };
24
+ export type InferErrorPageProps<SEP extends ShowErrorPageFn<any>> = Cleanup<ReturnType<SEP>['props']>;
25
+ /**
26
+ * Hhigher-order component (HOC) factory for Next.js App compnents to handle
27
+ * cases when `getServerSideProps` returns an `__error` prop with `statusCode`
28
+ * and optional friendly `message`.
29
+ *
30
+ * @see https://github.com/reykjavikcity/webtools/tree/v0.1#makeerrorizeapphoc
31
+ */
32
+ export declare const makeErrorizeAppHOC: <EP extends Partial<ErrorProps>>(ErrorPage: React.FunctionComponent<EP>) => {
33
+ <P extends {
34
+ [key: string]: unknown;
35
+ __error?: undefined;
36
+ }>(App: AppType<P>): React.FunctionComponent<import("next/dist/shared/lib/utils").AppPropsType<any, P | ({
37
+ __error: ErrorProps;
38
+ } & Omit<ErrorProps & EP, keyof ErrorProps>)>> & {
39
+ getInitialProps?(context: import("next/dist/shared/lib/utils").AppContextType<import("next/router").NextRouter>): P | ({
40
+ __error: ErrorProps;
41
+ } & Omit<ErrorProps & EP, keyof ErrorProps>) | Promise<P | ({
42
+ __error: ErrorProps;
43
+ } & Omit<ErrorProps & EP, keyof ErrorProps>)>;
44
+ };
45
+ showErrorPage: ShowErrorPageFn<ErrorProps & EP>;
46
+ };
47
+ /**
48
+ * Use this method to inside a `getServerSideProps` method (or API route)
49
+ * to return an `HTTP_304_NotModified` response with an empty props object,
50
+ * in a way that doesn't make TypeScript at you.
51
+ *
52
+ * @see https://github.com/reykjavikcity/webtools/tree/v0.1#notmodified304-helper
53
+ */
54
+ export declare const notModified304: (response: ServerResponse | NextContextLike) => {
55
+ readonly props: any;
56
+ };
package/next/http.js ADDED
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ var __rest = (this && this.__rest) || function (s, e) {
17
+ var t = {};
18
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
19
+ t[p] = s[p];
20
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
21
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
22
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
23
+ t[p[i]] = s[p[i]];
24
+ }
25
+ return t;
26
+ };
27
+ var __importDefault = (this && this.__importDefault) || function (mod) {
28
+ return (mod && mod.__esModule) ? mod : { "default": mod };
29
+ };
30
+ Object.defineProperty(exports, "__esModule", { value: true });
31
+ exports.notModified304 = exports.makeErrorizeAppHOC = void 0;
32
+ const react_1 = __importDefault(require("react"));
33
+ const http_1 = require("../http");
34
+ /*
35
+ Re-export all of the base [http module](#reykjavikwebtoolshttp)'s exports,
36
+ purely for convenience.
37
+ */
38
+ __exportStar(require("../http"), exports);
39
+ /**
40
+ * Use this method inside a `getServerSideProps` method (or API route)
41
+ * to return an error page with proper HTTP status code and all the shit.
42
+ *
43
+ * @see https://github.com/reykjavikcity/webtools/tree/v0.1#showerrorpage-helper
44
+ */
45
+ const showErrorPage = (response, error, ttl = '2s') => {
46
+ error =
47
+ typeof error === 'number'
48
+ ? { statusCode: error }
49
+ : error;
50
+ const { statusCode, message } = error, otherProps = __rest(error, ["statusCode", "message"]);
51
+ response = 'res' in response ? response.res : response;
52
+ response.statusCode = error.statusCode;
53
+ (0, http_1.cacheControl)(response, ttl);
54
+ return {
55
+ props: Object.assign(Object.assign({}, otherProps), { __error: { statusCode, message } }),
56
+ };
57
+ };
58
+ // ===========================================================================
59
+ /**
60
+ * Hhigher-order component (HOC) factory for Next.js App compnents to handle
61
+ * cases when `getServerSideProps` returns an `__error` prop with `statusCode`
62
+ * and optional friendly `message`.
63
+ *
64
+ * @see https://github.com/reykjavikcity/webtools/tree/v0.1#makeerrorizeapphoc
65
+ */
66
+ const makeErrorizeAppHOC = (ErrorPage) => {
67
+ // the HOC
68
+ const withErrorHandling = (App) => {
69
+ const ErrorizedApp = (appProps) => {
70
+ const { pageProps } = appProps;
71
+ if (pageProps.__error) {
72
+ const { __error } = pageProps, otherProps = __rest(pageProps, ["__error"]);
73
+ return (react_1.default.createElement(App, Object.assign({}, appProps, { Component: ErrorPage, pageProps: Object.assign(Object.assign({}, otherProps), __error) })));
74
+ }
75
+ return react_1.default.createElement(App, Object.assign({}, appProps));
76
+ };
77
+ ErrorizedApp.getInitialProps = App.getInitialProps;
78
+ ErrorizedApp.displayName = 'Errorized' + (App.displayName || App.name || 'App');
79
+ return ErrorizedApp;
80
+ };
81
+ withErrorHandling.showErrorPage = showErrorPage;
82
+ return withErrorHandling;
83
+ };
84
+ exports.makeErrorizeAppHOC = makeErrorizeAppHOC;
85
+ // ===========================================================================
86
+ /**
87
+ * Use this method to inside a `getServerSideProps` method (or API route)
88
+ * to return an `HTTP_304_NotModified` response with an empty props object,
89
+ * in a way that doesn't make TypeScript at you.
90
+ *
91
+ * @see https://github.com/reykjavikcity/webtools/tree/v0.1#notmodified304-helper
92
+ */
93
+ const notModified304 = (response) => {
94
+ response = 'res' in response ? response.res : response;
95
+ response.statusCode = http_1.HTTP_304_NotModified;
96
+ return {
97
+ props:
98
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
99
+ {},
100
+ };
101
+ };
102
+ exports.notModified304 = notModified304;
103
+ // ---------------------------------------------------------------------------
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@reykjavik/webtools",
3
+ "version": "0.0.0",
4
+ "description": "Misc. JS/TS helpers used by Reykjavík City's web dev teams.",
5
+ "main": "index.js",
6
+ "repository": "ssh://git@github.com:reykjavikcity/webtools.git",
7
+ "author": "Reykjavík (http://www.reykjavik.is)",
8
+ "contributors": [
9
+ "Elías Nökkvi Gíslason",
10
+ "Már Örlygsson <mar.orlygsson@reykjavik.is>"
11
+ ],
12
+ "license": "MIT",
13
+ "dependencies": {
14
+ "@reykjavik/hanna-utils": "^0.2.3"
15
+ },
16
+ "peerDependencies": {
17
+ "next": ">12",
18
+ "react": ">16.8.0",
19
+ "react-dom": ">16.8.0"
20
+ },
21
+ "engines": {
22
+ "node": ">=16"
23
+ },
24
+ "sideEffects": false,
25
+ "exports": {
26
+ "./CookieHubConsent.tsx": {
27
+ "import": "./esm/CookieHubConsent.tsx.js",
28
+ "require": "./CookieHubConsent.tsx.js"
29
+ },
30
+ "./http": {
31
+ "import": "./esm/http.js",
32
+ "require": "./http.js"
33
+ },
34
+ ".": {
35
+ "import": "./esm/index.js",
36
+ "require": "./index.js"
37
+ },
38
+ "./next/http.tsx": {
39
+ "import": "./esm/next/http.tsx.js",
40
+ "require": "./next/http.tsx.js"
41
+ },
42
+ "./next/SiteImprove.tsx": {
43
+ "import": "./esm/next/SiteImprove.tsx.js",
44
+ "require": "./next/SiteImprove.tsx.js"
45
+ }
46
+ }
47
+ }