@gblikas/querykit 0.0.0 → 0.2.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/.github/workflows/publish.yml +5 -7
- package/README.md +76 -0
- package/dist/parser/parser.d.ts +34 -0
- package/dist/parser/parser.js +164 -6
- package/dist/security/types.d.ts +48 -0
- package/dist/security/types.js +2 -0
- package/dist/security/validator.d.ts +35 -0
- package/dist/security/validator.js +108 -0
- package/examples/qk-next/app/globals.css +23 -0
- package/examples/qk-next/app/hooks/use-viewport-info.ts +89 -0
- package/examples/qk-next/app/layout.tsx +26 -7
- package/examples/qk-next/app/page.tsx +423 -121
- package/examples/qk-next/lib/utils.ts +74 -0
- package/examples/qk-next/package.json +5 -3
- package/examples/qk-next/pnpm-lock.yaml +112 -47
- package/package.json +5 -1
- package/src/parser/parser.test.ts +209 -1
- package/src/parser/parser.ts +234 -25
- package/src/security/types.ts +52 -0
- package/src/security/validator.test.ts +368 -0
- package/src/security/validator.ts +117 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState } from 'react';
|
|
4
|
+
|
|
5
|
+
export interface IViewportInfo {
|
|
6
|
+
innerWidth: number;
|
|
7
|
+
innerHeight: number;
|
|
8
|
+
shortViewportHeightPx: number;
|
|
9
|
+
isLessThanHeight: (px: number) => boolean;
|
|
10
|
+
shortSidePx: number;
|
|
11
|
+
isShortSideLessThan: (px: number) => boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Returns robust viewport dimensions using small viewport units (svh/svw) fallbacks.
|
|
16
|
+
* Ensures height reflects the visual viewport on mobile, avoiding URL bar issues.
|
|
17
|
+
*/
|
|
18
|
+
export function useViewportInfo(): IViewportInfo {
|
|
19
|
+
const readInnerHeight = (): number => {
|
|
20
|
+
// Prefer visualViewport when available to avoid browser UI chrome affecting measurements
|
|
21
|
+
const visual = window.visualViewport;
|
|
22
|
+
if (visual && typeof visual.height === 'number')
|
|
23
|
+
return Math.round(visual.height);
|
|
24
|
+
// Fallbacks in order of reliability
|
|
25
|
+
return Math.round(
|
|
26
|
+
window.innerHeight || document.documentElement.clientHeight
|
|
27
|
+
);
|
|
28
|
+
};
|
|
29
|
+
const readInnerWidth = (): number => {
|
|
30
|
+
const visual = window.visualViewport;
|
|
31
|
+
if (visual && typeof visual.width === 'number')
|
|
32
|
+
return Math.round(visual.width);
|
|
33
|
+
return Math.round(
|
|
34
|
+
window.innerWidth || document.documentElement.clientWidth
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const [state, setState] = useState<IViewportInfo>((): IViewportInfo => {
|
|
39
|
+
const w =
|
|
40
|
+
typeof window !== 'undefined'
|
|
41
|
+
? typeof window.visualViewport?.width === 'number'
|
|
42
|
+
? Math.round(window.visualViewport.width)
|
|
43
|
+
: window.innerWidth
|
|
44
|
+
: 0;
|
|
45
|
+
const h = typeof window !== 'undefined' ? readInnerHeight() : 0;
|
|
46
|
+
const shortSide = Math.min(w, h);
|
|
47
|
+
return {
|
|
48
|
+
innerWidth: w,
|
|
49
|
+
innerHeight: h,
|
|
50
|
+
shortViewportHeightPx: h,
|
|
51
|
+
isLessThanHeight: (px: number) => h < px,
|
|
52
|
+
shortSidePx: shortSide,
|
|
53
|
+
isShortSideLessThan: (px: number) => shortSide < px
|
|
54
|
+
};
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
let frame = 0;
|
|
59
|
+
const measure = (): void => {
|
|
60
|
+
const w = readInnerWidth();
|
|
61
|
+
const h = readInnerHeight();
|
|
62
|
+
const shortSide = Math.min(w, h);
|
|
63
|
+
setState({
|
|
64
|
+
innerWidth: w,
|
|
65
|
+
innerHeight: h,
|
|
66
|
+
shortViewportHeightPx: h,
|
|
67
|
+
isLessThanHeight: (px: number) => h < px,
|
|
68
|
+
shortSidePx: shortSide,
|
|
69
|
+
isShortSideLessThan: (px: number) => shortSide < px
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
frame = requestAnimationFrame(measure);
|
|
73
|
+
const onResize = (): void => {
|
|
74
|
+
cancelAnimationFrame(frame);
|
|
75
|
+
frame = requestAnimationFrame(measure);
|
|
76
|
+
};
|
|
77
|
+
window.addEventListener('resize', onResize);
|
|
78
|
+
window.visualViewport?.addEventListener('resize', onResize);
|
|
79
|
+
window.visualViewport?.addEventListener('scroll', onResize);
|
|
80
|
+
return (): void => {
|
|
81
|
+
cancelAnimationFrame(frame);
|
|
82
|
+
window.removeEventListener('resize', onResize);
|
|
83
|
+
window.visualViewport?.removeEventListener('resize', onResize);
|
|
84
|
+
window.visualViewport?.removeEventListener('scroll', onResize);
|
|
85
|
+
};
|
|
86
|
+
}, []);
|
|
87
|
+
|
|
88
|
+
return state;
|
|
89
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Metadata } from 'next';
|
|
1
|
+
import type { Metadata, Viewport } from 'next';
|
|
2
2
|
import { Geist, Geist_Mono } from 'next/font/google';
|
|
3
3
|
import './globals.css';
|
|
4
4
|
import { Toaster } from '@/components/ui/sonner';
|
|
@@ -7,6 +7,8 @@ import { ThemeProvider } from '@/components/theme-provider';
|
|
|
7
7
|
import { GitHubStars } from '@/components/github-stars';
|
|
8
8
|
import AuroraBackground from '@/components/aurora-background';
|
|
9
9
|
import { JSX } from 'react';
|
|
10
|
+
import { Analytics } from '@vercel/analytics/next';
|
|
11
|
+
import { SpeedInsights } from '@vercel/speed-insights/next';
|
|
10
12
|
|
|
11
13
|
const geistSans = Geist({
|
|
12
14
|
variable: '--font-geist-sans',
|
|
@@ -19,9 +21,7 @@ const geistMono = Geist_Mono({
|
|
|
19
21
|
});
|
|
20
22
|
|
|
21
23
|
export const metadata: Metadata = {
|
|
22
|
-
metadataBase: new URL(
|
|
23
|
-
process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000'
|
|
24
|
-
),
|
|
24
|
+
metadataBase: new URL('https://www.querykit.dev/'),
|
|
25
25
|
title: {
|
|
26
26
|
default: 'QueryKit · Next.js Demo',
|
|
27
27
|
template: '%s · QueryKit'
|
|
@@ -37,7 +37,12 @@ export const metadata: Metadata = {
|
|
|
37
37
|
'drizzle orm',
|
|
38
38
|
'pglite',
|
|
39
39
|
'typescript',
|
|
40
|
-
'next.js'
|
|
40
|
+
'next.js',
|
|
41
|
+
'drizzle',
|
|
42
|
+
'sql',
|
|
43
|
+
'npm',
|
|
44
|
+
'npm-package',
|
|
45
|
+
'nextjs'
|
|
41
46
|
],
|
|
42
47
|
authors: [{ name: 'gblikas', url: 'https://github.com/gblikas' }],
|
|
43
48
|
creator: 'QueryKit',
|
|
@@ -79,15 +84,27 @@ export const metadata: Metadata = {
|
|
|
79
84
|
]
|
|
80
85
|
};
|
|
81
86
|
|
|
87
|
+
export const viewport: Viewport = {
|
|
88
|
+
width: 'device-width',
|
|
89
|
+
initialScale: 1,
|
|
90
|
+
maximumScale: 1,
|
|
91
|
+
userScalable: false,
|
|
92
|
+
viewportFit: 'cover'
|
|
93
|
+
};
|
|
94
|
+
|
|
82
95
|
export default function RootLayout({
|
|
83
96
|
children
|
|
84
97
|
}: {
|
|
85
98
|
children: React.ReactNode;
|
|
86
99
|
}): JSX.Element {
|
|
87
100
|
return (
|
|
88
|
-
<html
|
|
101
|
+
<html
|
|
102
|
+
lang="en"
|
|
103
|
+
suppressHydrationWarning
|
|
104
|
+
className="min-h-screen overflow-hidden"
|
|
105
|
+
>
|
|
89
106
|
<body
|
|
90
|
-
className={`${geistSans.variable} ${geistMono.variable} antialiased min-h-screen`}
|
|
107
|
+
className={`${geistSans.variable} ${geistMono.variable} antialiased min-h-screen overflow-hidden`}
|
|
91
108
|
>
|
|
92
109
|
<ThemeProvider
|
|
93
110
|
attribute="class"
|
|
@@ -113,6 +130,8 @@ export default function RootLayout({
|
|
|
113
130
|
</div>
|
|
114
131
|
<Providers>{children}</Providers>
|
|
115
132
|
<Toaster />
|
|
133
|
+
<Analytics />
|
|
134
|
+
<SpeedInsights />
|
|
116
135
|
</div>
|
|
117
136
|
</ThemeProvider>
|
|
118
137
|
</body>
|