Found on Twitter
Imagine you've got some type that's got a bunch of intersections. When you hover over it, it gives you a bunch of gross intersections instead of displaying the resolved type
export type Flatten<T> = {
[K in keyof T]: T[K];
} & {}
export const isObject = <T = Record<string, unknown>>(value: unknown): value is T => {
// https://github.com/lodash/lodash/blob/4.17.21-es/isObject.js
// https://github.com/DefinitelyTyped/DefinitelyTyped/blob/ca8d73db/types/lodash.isobject/index.d.ts
const type = typeof value
return value != null && (type == 'object' || type == 'function')
}
export const isHtmlElement = (maybe: unknown): maybe is HTMLElement => {
if (maybe && typeof maybe === 'function' && 'prototype' in maybe) {
return 'title' in maybe || 'lang' in maybe || 'offsetParent' in maybe
}
return false
}
export const isElement = (maybe: unknown): maybe is Element => {
if (isHtmlElement(maybe)) {
return 'localName' in maybe || 'shadowRoot' in maybe
}
return false
}
export const isNode = (maybe: unknown): maybe is Node => {
if (isElement(maybe)) {
return 'ATTRIBUTE_NODE' in maybe || 'nodeName' in maybe || 'parentElement' in maybe || 'parentNode' in maybe
}
return false
}
export const toAnonymizedCredentialMapString: (input: Record<string, unknown>) => string = input => {
assert.equal(isObject(input), true, `Is not an HashMap object`)
const copy: Record<string, string> = JSON.parse(JSON.stringify(input || {}))
for (const [field, value] of Object.entries(copy)) {
if (field === 'password' && value !== '') {
copy[field] = '***'
}
}
return JSON.stringify(copy)
}
export const isObject = <T = Record<string, unknown>>(value: unknown): value is T => {
// https://github.com/lodash/lodash/blob/4.17.21-es/isObject.js
// https://github.com/DefinitelyTyped/DefinitelyTyped/blob/ca8d73db/types/lodash.isobject/index.d.ts
const type = typeof value
return value != null && (type == 'object' || type == 'function')
}
export const TestUser = Object.freeze({
/**
* Account of someone who did upload their avatar photo
*/
someUserAccountWithParticularRole: 'someUserAccountWithParticularRole',
} as const)
/**
* Based on TestUser enum above, what are the acceptable keys.
*
* See:
* - https://gist.github.com/renoirb/e8f48e4b54f16f9fb8f0bf38b6bc9c8d#using-read-only-array-of-strings-as-source-for-type
* - https://gist.github.com/renoirb/d30e7edbe32d9c6b9c2006b13db7a016
*/
export const TEST_USERS_KEYS = Object.freeze(
Object.entries(TestUser)
.filter(i => 1 in i && typeof i[1] === 'string')
.map(i => i[0])
.sort(),
) as TestUserKeys[]
export type ITestUser = typeof TEST_USERS_KEYS[number]
export type ICredential {
username: string
password: string
}
export type ITestUserCredientialMap = Record<ITestUser, ICredential>
export const readCredentialMapFromProcessEnv = (
input: ITestUserCredientialMap,
env: NodeJS.ProcessEnv
): ITestUserCredientialMap => {
const username = Reflect.has(env, input.username) ? Reflect.get(env, input.username) : null
const password = Reflect.has(env, input.password) ? Reflect.get(env, input.password) : null
const missing = Object.entries<Record<string, string | void>>({ username, password })
.map(v => (v[1] === null ? `${v[0]}: ${Reflect.get(input, v[0])}` : undefined))
.filter(Boolean)
if (missing.length > 0) {
const message = `We could not find process.env.<key> for: ${missing.join(', ')}`
throw new Error(message)
}
const out: ITestUserCredientialMap = {
username,
password,
}
// assertIsTestUserCredientialMap(out)
return out
}
export const TEST_USERS_CREDENTIAL_MAP: ITestUserCredientialMap = {
someUserAccountWithParticularRole: {
username: 'jdoe',
password: 'password123!',
},
}
/**
* Subclass of standard `Error` that eagerly collects the callstack of the error
* that caused it. This way you can investigate the core problem that happened
* by looking at the callstack from up to bottom (from higher level errors to
* lower level).
*
* Bookmark:
* - https://github.com/Veetaha/ts-nested-error/blob/2bb11d66/src/nested-error.ts#L7-L86
*/
export class NestedError extends Error {
/**
* Combined callstack of this error and the errors that it wraps.
* If the JavaScript runtime doesn't support `Error::stack` property
* this will contain only the concatenated messages.
*/
readonly stack: string
/**
* The list of lower-level errors wrapped by this error.
*/
readonly innerErrors: Error[]
private static readonly getErrorReport =
typeof new Error().stack === 'string' ? (err: Error) => err?.stack : (err: Error) => `${err.name}: ${err.message}`
/**
* Returns the function that accepts any value that was thrown as the first argument and
* throws it wrapped into `NestedError` or class derived from `NestedError` (provided
* this method was called directly in the context of that dervied class constructor)
* with the given `message`.
* Returned function will pass accepted `Error` object directly to `NestedError`
* as `innerErrors` by invoking `toError(err)` on it.
*
* You'll most likely want to use this method with promises:
*
* ```ts
* userService.getPage().then(
* data => console.log(`Hooray! data: ${data}`),
* NestedError.rethrow('failed to fetch users page')
* );
* ```
*
* @param message Message to attach `NestedError` created by the returned function.
*/
static rethrow(message: string) {
return (...errs: unknown[]) => {
throw new this(message, ...errs)
}
}
/**
* Allocates an instance of `NestedError` with the given error `message` and
* optional `innerError` (which will be automatically coerced using `toError()`).
*
* @param message Laconic error message to attach to the created `NestedError`.
* @param innerErrors Optional errors that will be wrapped by this higher level
* error. This value will be automatically coerced using `toError()`.
*/
constructor(message?: string, ...innerErrors: unknown[]) {
super(message)
const thisErrorReport = NestedError.getErrorReport(this)
if (innerErrors.length === 1) {
const innerError = toError(innerErrors[0])
this.innerErrors = [innerError]
const errReport = NestedError.getErrorReport(innerError)
this.stack = `${thisErrorReport}\n\n======= INNER ERROR =======\n\n${errReport}`
return
}
this.innerErrors = innerErrors.map(err => toError(err))
const innerErrorReports = this.innerErrors
.map((error, idx) => {
const errReport = NestedError.getErrorReport(error)
return `======= INNER ERROR (${idx + 1} of ${innerErrors.length}) =======\n\n${errReport}`
})
.join('\n\n')
this.stack = `${thisErrorReport}\n\n${innerErrorReports}`
}
}
/**
* Returns `err` itself if `err instanceof Error === true`, otherwise attemts to
* stringify it and wrap into `Error` object to be returned.
*
* **This function is guaranteed never to throw.**
*
* @param err Possbile `instanceof Error` to return or value of any type that will
* be wrapped into a fully-fledged `Error` object.
*
* Bookmark:
* - https://github.com/Veetaha/ts-nested-error/blob/2bb11d66/src/nested-error.ts#L97-L120
*/
export function toError(err: unknown): Error
export function toError(err: unknown) {
try {
return err instanceof Error ? err : new Error(`Value that is not an instance of Error was thrown: ${err}`)
} catch {
return new Error(
'Failed to stringify non-instance of Error that was thrown.' +
'This is possibly due to the fact that toString() method of the value' +
"doesn't return a primitive value.",
)
}
}
/**
* Not Implemented Exception
*
* To signify that we should implement this method or function
*/
export class NotImplementedException extends NestedError {
constructor(message = '', innerException?: Error) {
const fallbackMessage = 'not implemented'
const msg = message !== '' && /\s/.test(message) === false ? `${message} is ${fallbackMessage}` : fallbackMessage
super(msg, innerException)
}
}
export const createReadOnlySet = <T>(items: readonly string[] = []): ReadonlySet<T> => {
const message = `Invalid argument, we only support an array of strings`
if (Array.isArray(items) === false) {
throw new TypeError(message)
}
if (items.filter(maybe => typeof maybe !== 'string' || maybe === '').length > 0) {
throw new TypeError(message)
}
const applicable = (items ?? []).filter(maybe => typeof maybe === 'string' && maybe === '')
const mutableSet = new Set(applicable)
const has = (maybe: T | unknown) => mutableSet.has(maybe as string)
const keys = (): IterableIterator<T> => mutableSet.keys.bind(mutableSet)
const iterator = (): IterableIterator<T> => mutableSet[Symbol.iterator].bind(mutableSet)
return {
[Symbol.iterator]: iterator,
forEach: mutableSet.forEach.bind(mutableSet),
entries: mutableSet.entries.bind(mutableSet),
has,
keys,
values: keys,
size: mutableSet.size,
} as ReadonlySet<T>
}
/**
* Load data from static files.
*
* ```ts
* // JSON files
* import { loadAllJsonFiles, loadAllFilesAsOneString } from 'file-loading'
* export const users = loadAllJsonFiles(/^user.*\.json$/, 'fixtures')
*
* // Stitch GraphQL
* const typeDefs = `
* ${loadAllFilesAsOneString(/\.graphql$/, 'schema').join('\n')}
* type Query {
* hello(name: String): String!
* users: [User]!
* }
* `
* ```
*/
import { readdirSync, statSync, readFileSync } from 'fs'
import { join } from 'path'
import * as loadJsonFile from 'load-json-file'
/**
* Find files and return only ones with contents matching name
*/
const getFilePathsFinder = (
fileNameRegExp: RegExp,
fullPath: string,
): string[] => {
const files: string[] = []
const directoryExists = statSync(fullPath).isDirectory()
if (directoryExists) {
const fileNames: string[] = readdirSync(fullPath, { encoding: 'utf-8' })
.filter(fileName => fileNameRegExp.test(fileName))
.map(fileName => join(fullPath, fileName))
files.push(...fileNames)
}
return files
}
export const getFilePaths = (
fileNameRegExp: RegExp,
relativeDirName?: string,
): string[] => {
const fullPath = relativeDirName
? join(__dirname, relativeDirName)
: join(__dirname)
// console.log('getFilePaths', { relativeDirName, fullPath })
const files = getFilePathsFinder(fileNameRegExp, fullPath)
return files
}
export const loadAllJsonFiles = (pattern: RegExp, relativeDirName: string) =>
(() => {
const files = getFilePaths(pattern, relativeDirName)
const parsed: any[] = []
for (const filePath of files) {
try {
const contents = loadJsonFile.sync(filePath)
if (Array.isArray(contents)) {
parsed.push(...contents)
}
} catch (e) {
console.log('loadAllFiles loading error', e)
}
}
return parsed
})()
export const loadAllFilesAsOneString = (
pattern: RegExp,
relativeDirName: string,
) =>
(() => {
const files = getFilePaths(pattern, relativeDirName)
// console.log('loadAllFilesAsOneString', files)
const contents: string[] = []
for (const filePath of files) {
try {
const fileContents = readFileSync(filePath, { encoding: 'utf-8' })
contents.push(fileContents)
} catch (e) {
console.log('loadAllFiles loading error', e)
}
}
return contents
})()
Parts of the mechanics to observe the current window size and tell a different variant. Here only one variant (either "mobile" or "desktop"), but could be adapted and configurable to not be bound to a framework
source from PR on vue-admin-template
const WIDTH = 992 // refer to Bootstrap's responsive design
export function onMounted() {
const hasMethod = Reflect.has(this, 'checkIfMobile')
const hasResizeHandler = Reflect.has(this, 'onResize')
const hasEl = Reflect.has(this, '$el')
if (hasMethod && hasResizeHandler && hasEl) {
const isMobile = this.checkIfMobile()
const { defaultView } = this.$el.ownerDocument
const handler = this.onResize
const layoutVariant = isMobile ? 'mobile' : 'desktop'
this.layoutVariant = layoutVariant
this.$emit('layout-variant', layoutVariant)
defaultView.addEventListener('resize', handler)
if (isMobile) {
this.$emit('layout-sidebar', { opened: false, withoutAnimation: true })
}
} else {
const message = `Please make sure you attach onMounted event handler to a Vue mounted lifecycle hook`
throw new Error(message)
}
}
export function checkIfMobile() {
const hasEl = Reflect.has(this, '$el')
const mobileBreakPoint = this.mobileBreakPoint ? this.mobileBreakPoint : WIDTH
let isMobile = false
if (hasEl) {
const $el = this.$el
let bodyRectWidth = mobileBreakPoint
const hasOwnerDocument = 'ownerDocument' in $el
let hasMethodName = false
if (hasOwnerDocument) {
const { body } = $el.ownerDocument
hasMethodName = 'getBoundingClientRect' in body
if (hasMethodName) {
const bodyRect = body.getBoundingClientRect()
bodyRectWidth = bodyRect.width
}
}
isMobile = bodyRectWidth - 1 < mobileBreakPoint
return isMobile
} else {
const message = `Please make sure you attach checkIfMobile to a Vue component as a method`
throw new Error(message)
}
}
/** @type {import('vue').VueConstructor} */
const main = {
data() {
const layoutVariant = 'desktop'
const layoutIsHidden = false
return {
layoutVariant,
layoutIsHidden
}
},
props: {
mobileBreakPoint: {
type: Number,
default: WIDTH
},
checkIfMobile: {
type: Function,
default() {
return checkIfMobile.call(this)
}
}
},
computed: {
isMobile() {
const layoutVariant = this.layoutVariant
return layoutVariant === 'mobile'
}
},
async beforeDestroy() {
const handler = this.onResize
const $el = this.$el
await this.$nextTick(async() => {
const { defaultView } = $el.ownerDocument
defaultView.removeEventListener('resize', handler)
})
},
async mounted() {
await this.$nextTick(onMounted.bind(this))
},
methods: {
onResize() {
const { hidden = false } = this.$el.ownerDocument
const isMobile = this.checkIfMobile(this)
const layoutVariant = isMobile ? 'mobile' : 'desktop'
const layoutVariantChanged = layoutVariant !== this.layoutVariant
this.layoutVariant = layoutVariant
if (!hidden && layoutVariantChanged) {
this.$emit('layout-variant', layoutVariant)
this.$emit('layout-navbar', {
fixed: isMobile
})
this.$emit('layout-sidebar', {
opened: !isMobile,
withoutAnimation: true
})
}
}
}
}
export default main
IndexedDB
indexDB.open(DB_NAME, VERSION)
(link to MDN) andonupgradeneeded
(link to MDN)Creating a fresh collection of items
Basically anything indexedDB is a "transaction", reading it should be a "readonly" and tell the search criteria ("IDBKeyrange") and iterate over the result ("cursor" MDN link)
See indexedDB example "
displayData
" used in MDN IDBCursor But, adapted here to return an object instead of mutating the DOM. I've also mixed with IDBKeyRange example from MDN IDBKeyRange pageFor async bit, read more from this article IndexedDB with Promises and async/await, and the suggested use of Jake Archibald (a core Google Chrome dev) "idb" (while reading code; oh(!) look at this actually useful use of WeakMap)