refactor: regenerate svelte kit
parent
d335d0ac45
commit
d88181665d
@ -1,20 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
root: true,
|
|
||||||
parser: '@typescript-eslint/parser',
|
|
||||||
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'],
|
|
||||||
plugins: ['svelte3', '@typescript-eslint'],
|
|
||||||
ignorePatterns: ['*.cjs'],
|
|
||||||
overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }],
|
|
||||||
settings: {
|
|
||||||
'svelte3/typescript': () => require('typescript')
|
|
||||||
},
|
|
||||||
parserOptions: {
|
|
||||||
sourceType: 'module',
|
|
||||||
ecmaVersion: 2019
|
|
||||||
},
|
|
||||||
env: {
|
|
||||||
browser: true,
|
|
||||||
es2017: true,
|
|
||||||
node: true
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"useTabs": true,
|
|
||||||
"singleQuote": false,
|
|
||||||
"trailingComma": "all",
|
|
||||||
"printWidth": 100
|
|
||||||
}
|
|
@ -0,0 +1,17 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/hhhapz/codequest/db"
|
||||||
|
"github.com/hhhapz/codequest/models"
|
||||||
|
"github.com/k0kubun/pp/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
d, _ := db.NewDB("db.sqlite")
|
||||||
|
models.SetLogger(log.Printf)
|
||||||
|
|
||||||
|
pp.Println(models.QuestionSubmissions(context.Background(), d.DB, "2", "idk", 2))
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -1,42 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "~TODO~",
|
|
||||||
"version": "0.0.1",
|
|
||||||
"scripts": {
|
|
||||||
"dev": "svelte-kit dev",
|
|
||||||
"build": "svelte-kit build",
|
|
||||||
"preview": "svelte-kit preview",
|
|
||||||
"check": "svelte-check --tsconfig ./tsconfig.json",
|
|
||||||
"check:watch": "svelte-check --tsconfig ./tsconfig.json --watch",
|
|
||||||
"lint": "prettier --ignore-path .gitignore --check --plugin-search-dir=. . && eslint --ignore-path .gitignore .",
|
|
||||||
"format": "prettier --ignore-path .gitignore --write --plugin-search-dir=. ."
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@sveltejs/adapter-static": "^1.0.0-next.18",
|
|
||||||
"@sveltejs/kit": "next",
|
|
||||||
"@types/cookie": "^0.4.0",
|
|
||||||
"@typescript-eslint/eslint-plugin": "^4.19.0",
|
|
||||||
"@typescript-eslint/parser": "^4.19.0",
|
|
||||||
"autoprefixer": "^10.3.4",
|
|
||||||
"cssnano": "^5.0.8",
|
|
||||||
"eslint": "^7.22.0",
|
|
||||||
"eslint-config-prettier": "^8.1.0",
|
|
||||||
"eslint-plugin-svelte3": "^3.2.0",
|
|
||||||
"postcss": "^8.3.6",
|
|
||||||
"postcss-load-config": "^3.1.0",
|
|
||||||
"prettier": "~2.2.1",
|
|
||||||
"prettier-plugin-svelte": "^2.2.0",
|
|
||||||
"svelte": "^3.34.0",
|
|
||||||
"svelte-check": "^2.0.0",
|
|
||||||
"svelte-preprocess": "^4.9.4",
|
|
||||||
"tailwindcss": "^2.2.15",
|
|
||||||
"tslib": "^2.0.0",
|
|
||||||
"typescript": "^4.0.0"
|
|
||||||
},
|
|
||||||
"type": "module",
|
|
||||||
"dependencies": {
|
|
||||||
"@fontsource/fira-mono": "^4.2.2",
|
|
||||||
"@lukeed/uuid": "^2.0.0",
|
|
||||||
"cookie": "^0.4.1",
|
|
||||||
"daisyui": "^1.14.1"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
const tailwindcss = require("tailwindcss");
|
|
||||||
const autoprefixer = require("autoprefixer");
|
|
||||||
const cssnano = require("cssnano");
|
|
||||||
|
|
||||||
const mode = process.env.NODE_ENV;
|
|
||||||
const dev = mode === "development";
|
|
||||||
|
|
||||||
const config = {
|
|
||||||
plugins: [
|
|
||||||
//Some plugins, like tailwindcss/nesting, need to run before Tailwind,
|
|
||||||
tailwindcss(),
|
|
||||||
//But others, like autoprefixer, need to run after,
|
|
||||||
autoprefixer(),
|
|
||||||
!dev &&
|
|
||||||
cssnano({
|
|
||||||
preset: "default",
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = config;
|
|
@ -1,28 +0,0 @@
|
|||||||
import preprocess from "svelte-preprocess";
|
|
||||||
import adapter from '@sveltejs/adapter-static'
|
|
||||||
|
|
||||||
/** @type {import('@sveltejs/kit').Config} */
|
|
||||||
const config = {
|
|
||||||
// Consult https://github.com/sveltejs/svelte-preprocess
|
|
||||||
// for more information about preprocessors
|
|
||||||
preprocess: [
|
|
||||||
preprocess({
|
|
||||||
postcss: true,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
|
|
||||||
kit: {
|
|
||||||
// hydrate the <div id="svelte"> element in src/app.html
|
|
||||||
target: "#svelte",
|
|
||||||
files: {
|
|
||||||
hooks: "web_src/hooks",
|
|
||||||
lib: "web_src/lib",
|
|
||||||
routes: "web_src/routes",
|
|
||||||
serviceWorker: "web_src/service-worker",
|
|
||||||
template: "web_src/app.html",
|
|
||||||
},
|
|
||||||
adapter: adapter({}),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default config;
|
|
@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"module": "es2020",
|
|
||||||
"lib": ["es2020", "DOM"],
|
|
||||||
"target": "es2019",
|
|
||||||
"importsNotUsedAsValues": "error",
|
|
||||||
"isolatedModules": true,
|
|
||||||
"resolveJsonModule": true,
|
|
||||||
"sourceMap": true,
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"forceConsistentCasingInFileNames": true,
|
|
||||||
"baseUrl": ".",
|
|
||||||
"allowJs": true,
|
|
||||||
"checkJs": true,
|
|
||||||
"paths": {
|
|
||||||
"$lib": ["web_src/lib"],
|
|
||||||
"$lib/*": ["web_src/lib/*"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"include": ["web_src/**/*.d.ts", "web_src/**/*.js", "web_src/**/*.ts", "web_src/**/*.svelte"]
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<link rel="icon" href="/favicon.png" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
|
|
||||||
%svelte.head%
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="svelte">%svelte.body%</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,5 +0,0 @@
|
|||||||
@import "@fontsource/fira-mono";
|
|
||||||
|
|
||||||
@tailwind base;
|
|
||||||
@tailwind components;
|
|
||||||
@tailwind utilities;
|
|
@ -1 +0,0 @@
|
|||||||
/// <reference types="@sveltejs/kit" />
|
|
@ -1,23 +0,0 @@
|
|||||||
import cookie from 'cookie';
|
|
||||||
import { v4 as uuid } from '@lukeed/uuid';
|
|
||||||
import type { Handle } from '@sveltejs/kit';
|
|
||||||
|
|
||||||
export const handle: Handle = async ({ request, resolve }) => {
|
|
||||||
const cookies = cookie.parse(request.headers.cookie || '');
|
|
||||||
request.locals.userid = cookies.userid || uuid();
|
|
||||||
|
|
||||||
// TODO https://github.com/sveltejs/kit/issues/1046
|
|
||||||
if (request.query.has('_method')) {
|
|
||||||
request.method = request.query.get('_method').toUpperCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await resolve(request);
|
|
||||||
|
|
||||||
if (!cookies.userid) {
|
|
||||||
// if this is the first time the user has visited this app,
|
|
||||||
// set a cookie so that we recognise them when they return
|
|
||||||
response.headers['set-cookie'] = `userid=${request.locals.userid}; Path=/; HttpOnly`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return response;
|
|
||||||
};
|
|
@ -1,5 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<header>
|
|
||||||
</header>
|
|
@ -1,160 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
// @ts-nocheck
|
|
||||||
/*
|
|
||||||
* This file is a generated Typescript file for GRPC Gateway, DO NOT MODIFY
|
|
||||||
*/
|
|
||||||
|
|
||||||
import * as fm from "../../../../../fetch.pb"
|
|
||||||
import * as GoogleProtobufEmpty from "../../../../../google/protobuf/empty.pb"
|
|
||||||
import * as GoogleProtobufTimestamp from "../../../../../google/protobuf/timestamp.pb"
|
|
||||||
export type Token = {
|
|
||||||
token?: string
|
|
||||||
expires?: GoogleProtobufTimestamp.Timestamp
|
|
||||||
}
|
|
||||||
|
|
||||||
export type OAuthCodeRequest = {
|
|
||||||
}
|
|
||||||
|
|
||||||
export type OAuthCodeResponse = {
|
|
||||||
redirectURI?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type TokenRequest = {
|
|
||||||
code?: string
|
|
||||||
state?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type DeleteTokenRequest = {
|
|
||||||
all?: boolean
|
|
||||||
token?: Token
|
|
||||||
}
|
|
||||||
|
|
||||||
export type User = {
|
|
||||||
id?: string
|
|
||||||
name?: string
|
|
||||||
email?: string
|
|
||||||
picture?: string
|
|
||||||
admin?: boolean
|
|
||||||
createdAt?: GoogleProtobufTimestamp.Timestamp
|
|
||||||
}
|
|
||||||
|
|
||||||
export type PartData = {
|
|
||||||
completed?: boolean
|
|
||||||
pointsWorth?: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Question = {
|
|
||||||
id?: string
|
|
||||||
title?: string
|
|
||||||
text?: string
|
|
||||||
part1?: PartData
|
|
||||||
part2?: PartData
|
|
||||||
}
|
|
||||||
|
|
||||||
export type QuestionsResponse = {
|
|
||||||
questions?: Question[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export type QuestionByIDRequest = {
|
|
||||||
id?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type QuestionInputRequest = {
|
|
||||||
id?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type QuestionInput = {
|
|
||||||
id?: string
|
|
||||||
input?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type SubmitRequestData = {
|
|
||||||
answer?: string
|
|
||||||
part?: number
|
|
||||||
code?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type SubmitRequest = {
|
|
||||||
id?: string
|
|
||||||
body?: SubmitRequestData
|
|
||||||
}
|
|
||||||
|
|
||||||
export type SubmitResponse = {
|
|
||||||
correct?: boolean
|
|
||||||
points?: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export type UpdateFields = {
|
|
||||||
name?: string
|
|
||||||
gradeLevel?: number
|
|
||||||
admin?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export type UserRequest = {
|
|
||||||
}
|
|
||||||
|
|
||||||
export type AllUsersRequest = {
|
|
||||||
}
|
|
||||||
|
|
||||||
export type UpdateUserRequest = {
|
|
||||||
email?: string
|
|
||||||
body?: UpdateFields
|
|
||||||
}
|
|
||||||
|
|
||||||
export type UserByEmailRequest = {
|
|
||||||
email?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type DeleteUserRequest = {
|
|
||||||
email?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type AllUsersResponse = {
|
|
||||||
users?: User[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export class AuthService {
|
|
||||||
static OAuthCode(req: OAuthCodeRequest, initReq?: fm.InitReq): Promise<OAuthCodeResponse> {
|
|
||||||
return fm.fetchReq<OAuthCodeRequest, OAuthCodeResponse>(`/v1/auth/code?${fm.renderURLSearchParams(req, [])}`, {...initReq, method: "GET"})
|
|
||||||
}
|
|
||||||
static Token(req: TokenRequest, initReq?: fm.InitReq): Promise<Token> {
|
|
||||||
return fm.fetchReq<TokenRequest, Token>(`/v1/auth/token?${fm.renderURLSearchParams(req, [])}`, {...initReq, method: "GET"})
|
|
||||||
}
|
|
||||||
static DeleteToken(req: DeleteTokenRequest, initReq?: fm.InitReq): Promise<GoogleProtobufEmpty.Empty> {
|
|
||||||
return fm.fetchReq<DeleteTokenRequest, GoogleProtobufEmpty.Empty>(`/v1/auth/token`, {...initReq, method: "DELETE"})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export class QuestService {
|
|
||||||
static Questions(req: GoogleProtobufEmpty.Empty, initReq?: fm.InitReq): Promise<QuestionsResponse> {
|
|
||||||
return fm.fetchReq<GoogleProtobufEmpty.Empty, QuestionsResponse>(`/v1/questions?${fm.renderURLSearchParams(req, [])}`, {...initReq, method: "GET"})
|
|
||||||
}
|
|
||||||
static QuestionByID(req: QuestionByIDRequest, initReq?: fm.InitReq): Promise<Question> {
|
|
||||||
return fm.fetchReq<QuestionByIDRequest, Question>(`/v1/questions/${req["id"]}?${fm.renderURLSearchParams(req, ["id"])}`, {...initReq, method: "GET"})
|
|
||||||
}
|
|
||||||
static QuestionInput(req: QuestionInputRequest, initReq?: fm.InitReq): Promise<QuestionInput> {
|
|
||||||
return fm.fetchReq<QuestionInputRequest, QuestionInput>(`/v1/questions/${req["id"]}/input?${fm.renderURLSearchParams(req, ["id"])}`, {...initReq, method: "GET"})
|
|
||||||
}
|
|
||||||
static Submit(req: SubmitRequest, initReq?: fm.InitReq): Promise<SubmitResponse> {
|
|
||||||
return fm.fetchReq<SubmitRequest, SubmitResponse>(`/v1/questions/${req["id"]}`, {...initReq, method: "POST", body: JSON.stringify(req["Body"])})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export class UserService {
|
|
||||||
static User(req: UserRequest, initReq?: fm.InitReq): Promise<User> {
|
|
||||||
return fm.fetchReq<UserRequest, User>(`/v1/users/me?${fm.renderURLSearchParams(req, [])}`, {...initReq, method: "GET"})
|
|
||||||
}
|
|
||||||
static UserByEmail(req: UserByEmailRequest, initReq?: fm.InitReq): Promise<User> {
|
|
||||||
return fm.fetchReq<UserByEmailRequest, User>(`/v1/admin/users/${req["email"]}?${fm.renderURLSearchParams(req, ["email"])}`, {...initReq, method: "GET"})
|
|
||||||
}
|
|
||||||
static AllUsers(req: AllUsersRequest, initReq?: fm.InitReq): Promise<AllUsersResponse> {
|
|
||||||
return fm.fetchReq<AllUsersRequest, AllUsersResponse>(`/v1/admin/users?${fm.renderURLSearchParams(req, [])}`, {...initReq, method: "GET"})
|
|
||||||
}
|
|
||||||
static UpdateUser(req: UpdateUserRequest, initReq?: fm.InitReq): Promise<User> {
|
|
||||||
return fm.fetchReq<UpdateUserRequest, User>(`/v1/users/me`, {...initReq, method: "PUT", body: JSON.stringify(req["Body"])})
|
|
||||||
}
|
|
||||||
static AdminUpdateUser(req: UpdateUserRequest, initReq?: fm.InitReq): Promise<User> {
|
|
||||||
return fm.fetchReq<UpdateUserRequest, User>(`/v1/admin/users/${req["email"]}`, {...initReq, method: "PUT", body: JSON.stringify(req["Body"])})
|
|
||||||
}
|
|
||||||
static DeleteUser(req: DeleteUserRequest, initReq?: fm.InitReq): Promise<User> {
|
|
||||||
return fm.fetchReq<DeleteUserRequest, User>(`/v1/admin/users/${req["email"]}`, {...initReq, method: "DELETE"})
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,232 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
// @ts-nocheck
|
|
||||||
/*
|
|
||||||
* This file is a generated Typescript file for GRPC Gateway, DO NOT MODIFY
|
|
||||||
*/
|
|
||||||
|
|
||||||
export interface InitReq extends RequestInit {
|
|
||||||
pathPrefix?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export function fetchReq<I, O>(path: string, init?: InitReq): Promise<O> {
|
|
||||||
const {pathPrefix, ...req} = init || {}
|
|
||||||
|
|
||||||
const url = pathPrefix ? `${pathPrefix}${path}` : path
|
|
||||||
|
|
||||||
return fetch(url, req).then(r => r.json().then((body: O) => {
|
|
||||||
if (!r.ok) { throw body; }
|
|
||||||
return body;
|
|
||||||
})) as Promise<O>
|
|
||||||
}
|
|
||||||
|
|
||||||
// NotifyStreamEntityArrival is a callback that will be called on streaming entity arrival
|
|
||||||
export type NotifyStreamEntityArrival<T> = (resp: T) => void
|
|
||||||
|
|
||||||
/**
|
|
||||||
* fetchStreamingRequest is able to handle grpc-gateway server side streaming call
|
|
||||||
* it takes NotifyStreamEntityArrival that lets users respond to entity arrival during the call
|
|
||||||
* all entities will be returned as an array after the call finishes.
|
|
||||||
**/
|
|
||||||
export async function fetchStreamingRequest<S, R>(path: string, callback?: NotifyStreamEntityArrival<R>, init?: InitReq) {
|
|
||||||
const {pathPrefix, ...req} = init || {}
|
|
||||||
const url = pathPrefix ?`${pathPrefix}${path}` : path
|
|
||||||
const result = await fetch(url, req)
|
|
||||||
// needs to use the .ok to check the status of HTTP status code
|
|
||||||
// http other than 200 will not throw an error, instead the .ok will become false.
|
|
||||||
// see https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#
|
|
||||||
if (!result.ok) {
|
|
||||||
const resp = await result.json()
|
|
||||||
const errMsg = resp.error && resp.error.message ? resp.error.message : ""
|
|
||||||
throw new Error(errMsg)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!result.body) {
|
|
||||||
throw new Error("response doesnt have a body")
|
|
||||||
}
|
|
||||||
|
|
||||||
await result.body
|
|
||||||
.pipeThrough(new TextDecoderStream())
|
|
||||||
.pipeThrough<R>(getNewLineDelimitedJSONDecodingStream<R>())
|
|
||||||
.pipeTo(getNotifyEntityArrivalSink((e: R) => {
|
|
||||||
if (callback) {
|
|
||||||
callback(e)
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
// wait for the streaming to finish and return the success respond
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* JSONStringStreamController represents the transform controller that's able to transform the incoming
|
|
||||||
* new line delimited json content stream into entities and able to push the entity to the down stream
|
|
||||||
*/
|
|
||||||
interface JSONStringStreamController<T> extends TransformStreamDefaultController {
|
|
||||||
buf?: string
|
|
||||||
pos?: number
|
|
||||||
enqueue: (s: T) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* getNewLineDelimitedJSONDecodingStream returns a TransformStream that's able to handle new line delimited json stream content into parsed entities
|
|
||||||
*/
|
|
||||||
function getNewLineDelimitedJSONDecodingStream<T>(): TransformStream<string, T> {
|
|
||||||
return new TransformStream({
|
|
||||||
start(controller: JSONStringStreamController<T>) {
|
|
||||||
controller.buf = ''
|
|
||||||
controller.pos = 0
|
|
||||||
},
|
|
||||||
|
|
||||||
transform(chunk: string, controller: JSONStringStreamController<T>) {
|
|
||||||
if (controller.buf === undefined) {
|
|
||||||
controller.buf = ''
|
|
||||||
}
|
|
||||||
if (controller.pos === undefined) {
|
|
||||||
controller.pos = 0
|
|
||||||
}
|
|
||||||
controller.buf += chunk
|
|
||||||
while (controller.pos < controller.buf.length) {
|
|
||||||
if (controller.buf[controller.pos] === '\n') {
|
|
||||||
const line = controller.buf.substring(0, controller.pos)
|
|
||||||
const response = JSON.parse(line)
|
|
||||||
controller.enqueue(response.result)
|
|
||||||
controller.buf = controller.buf.substring(controller.pos + 1)
|
|
||||||
controller.pos = 0
|
|
||||||
} else {
|
|
||||||
++controller.pos
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* getNotifyEntityArrivalSink takes the NotifyStreamEntityArrival callback and return
|
|
||||||
* a sink that will call the callback on entity arrival
|
|
||||||
* @param notifyCallback
|
|
||||||
*/
|
|
||||||
function getNotifyEntityArrivalSink<T>(notifyCallback: NotifyStreamEntityArrival<T>) {
|
|
||||||
return new WritableStream<T>({
|
|
||||||
write(entity: T) {
|
|
||||||
notifyCallback(entity)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
type Primitive = string | boolean | number;
|
|
||||||
type RequestPayload = Record<string, unknown>;
|
|
||||||
type FlattenedRequestPayload = Record<string, Primitive | Array<Primitive>>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if given value is a plain object
|
|
||||||
* Logic copied and adapted from below source:
|
|
||||||
* https://github.com/char0n/ramda-adjunct/blob/master/src/isPlainObj.js
|
|
||||||
* @param {unknown} value
|
|
||||||
* @return {boolean}
|
|
||||||
*/
|
|
||||||
function isPlainObject(value: unknown): boolean {
|
|
||||||
const isObject =
|
|
||||||
Object.prototype.toString.call(value).slice(8, -1) === "Object";
|
|
||||||
const isObjLike = value !== null && isObject;
|
|
||||||
|
|
||||||
if (!isObjLike || !isObject) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const proto = Object.getPrototypeOf(value);
|
|
||||||
|
|
||||||
const hasObjectConstructor =
|
|
||||||
typeof proto === "object" &&
|
|
||||||
proto.constructor === Object.prototype.constructor;
|
|
||||||
|
|
||||||
return hasObjectConstructor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if given value is of a primitive type
|
|
||||||
* @param {unknown} value
|
|
||||||
* @return {boolean}
|
|
||||||
*/
|
|
||||||
function isPrimitive(value: unknown): boolean {
|
|
||||||
return ["string", "number", "boolean"].some(t => typeof value === t);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if given primitive is zero-value
|
|
||||||
* @param {Primitive} value
|
|
||||||
* @return {boolean}
|
|
||||||
*/
|
|
||||||
function isZeroValuePrimitive(value: Primitive): boolean {
|
|
||||||
return value === false || value === 0 || value === "";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Flattens a deeply nested request payload and returns an object
|
|
||||||
* with only primitive values and non-empty array of primitive values
|
|
||||||
* as per https://github.com/googleapis/googleapis/blob/master/google/api/http.proto
|
|
||||||
* @param {RequestPayload} requestPayload
|
|
||||||
* @param {String} path
|
|
||||||
* @return {FlattenedRequestPayload>}
|
|
||||||
*/
|
|
||||||
function flattenRequestPayload<T extends RequestPayload>(
|
|
||||||
requestPayload: T,
|
|
||||||
path: string = ""
|
|
||||||
): FlattenedRequestPayload {
|
|
||||||
return Object.keys(requestPayload).reduce(
|
|
||||||
(acc: T, key: string): T => {
|
|
||||||
const value = requestPayload[key];
|
|
||||||
const newPath = path ? [path, key].join(".") : key;
|
|
||||||
|
|
||||||
const isNonEmptyPrimitiveArray =
|
|
||||||
Array.isArray(value) &&
|
|
||||||
value.every(v => isPrimitive(v)) &&
|
|
||||||
value.length > 0;
|
|
||||||
|
|
||||||
const isNonZeroValuePrimitive =
|
|
||||||
isPrimitive(value) && !isZeroValuePrimitive(value as Primitive);
|
|
||||||
|
|
||||||
let objectToMerge = {};
|
|
||||||
|
|
||||||
if (isPlainObject(value)) {
|
|
||||||
objectToMerge = flattenRequestPayload(value as RequestPayload, newPath);
|
|
||||||
} else if (isNonZeroValuePrimitive || isNonEmptyPrimitiveArray) {
|
|
||||||
objectToMerge = { [newPath]: value };
|
|
||||||
}
|
|
||||||
|
|
||||||
return { ...acc, ...objectToMerge };
|
|
||||||
},
|
|
||||||
{} as T
|
|
||||||
) as FlattenedRequestPayload;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders a deeply nested request payload into a string of URL search
|
|
||||||
* parameters by first flattening the request payload and then removing keys
|
|
||||||
* which are already present in the URL path.
|
|
||||||
* @param {RequestPayload} requestPayload
|
|
||||||
* @param {string[]} urlPathParams
|
|
||||||
* @return {string}
|
|
||||||
*/
|
|
||||||
export function renderURLSearchParams<T extends RequestPayload>(
|
|
||||||
requestPayload: T,
|
|
||||||
urlPathParams: string[] = []
|
|
||||||
): string {
|
|
||||||
const flattenedRequestPayload = flattenRequestPayload(requestPayload);
|
|
||||||
|
|
||||||
const urlSearchParams = Object.keys(flattenedRequestPayload).reduce(
|
|
||||||
(acc: string[][], key: string): string[][] => {
|
|
||||||
// key should not be present in the url path as a parameter
|
|
||||||
const value = flattenedRequestPayload[key];
|
|
||||||
if (urlPathParams.find(f => f === key)) {
|
|
||||||
return acc;
|
|
||||||
}
|
|
||||||
return Array.isArray(value)
|
|
||||||
? [...acc, ...value.map(m => [key, m.toString()])]
|
|
||||||
: (acc = [...acc, [key, value.toString()]]);
|
|
||||||
},
|
|
||||||
[] as string[][]
|
|
||||||
);
|
|
||||||
|
|
||||||
return new URLSearchParams(urlSearchParams).toString();
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
<script>
|
|
||||||
import "../app.postcss";
|
|
||||||
import Header from "$lib/header/Header.svelte";
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<Header />
|
|
||||||
<slot />
|
|
@ -1,19 +0,0 @@
|
|||||||
<script context="module">
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { onMount } from "svelte";
|
|
||||||
|
|
||||||
let dflt = "hello";
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
dflt = "sup";
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<title>About</title>
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
<p>{dflt}</p>
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue