feat: support for predefined variables (#3886)
Co-authored-by: Anwarul Islam <anwaarulislaam@gmail.com> Co-authored-by: jamesgeorge007 <25279263+jamesgeorge007@users.noreply.github.com>
This commit is contained in:
committed by
jamesgeorge007
parent
db8cf229ac
commit
e4d9f82a75
@@ -609,3 +609,17 @@ details[open] summary .indicator {
|
||||
.gql-operation-highlight {
|
||||
@apply opacity-100;
|
||||
}
|
||||
|
||||
.predefined-variable-highlight {
|
||||
color: inherit;
|
||||
|
||||
&.predefined-variable-valid {
|
||||
@apply bg-yellow-500;
|
||||
@apply hover:bg-yellow-600;
|
||||
}
|
||||
|
||||
&.predefined-variable-invalid {
|
||||
@apply hover:bg-red-300;
|
||||
@apply bg-red-300;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,7 +261,7 @@ const clearIcon = refAutoReset<typeof IconTrash2 | typeof IconDone>(
|
||||
1000
|
||||
)
|
||||
|
||||
const globalVars = useReadonlyStream(globalEnv$, {} as GlobalEnvironment)
|
||||
const globalEnv = useReadonlyStream(globalEnv$, {} as GlobalEnvironment)
|
||||
|
||||
type SelectedEnv = "variables" | "secret"
|
||||
|
||||
@@ -319,7 +319,7 @@ const liveEnvs = computed(() => {
|
||||
}
|
||||
return [
|
||||
...vars.value.map((x) => ({ ...x.env, source: editingName.value! })),
|
||||
...globalVars.value.variables.map((x) => ({ ...x, source: "Global" })),
|
||||
...globalEnv.value.variables.map((x) => ({ ...x, source: "Global" })),
|
||||
]
|
||||
})
|
||||
|
||||
|
||||
@@ -343,6 +343,7 @@ useCodemirror(
|
||||
linter,
|
||||
completer: null,
|
||||
environmentHighlights: true,
|
||||
predefinedVariablesHighlights: true,
|
||||
})
|
||||
)
|
||||
|
||||
|
||||
@@ -161,6 +161,7 @@ useCodemirror(
|
||||
linter,
|
||||
completer: null,
|
||||
environmentHighlights: true,
|
||||
predefinedVariablesHighlights: true,
|
||||
})
|
||||
)
|
||||
|
||||
|
||||
@@ -160,6 +160,7 @@ useCodemirror(
|
||||
linter: langLinter,
|
||||
completer: null,
|
||||
environmentHighlights: true,
|
||||
predefinedVariablesHighlights: true,
|
||||
})
|
||||
)
|
||||
|
||||
|
||||
@@ -234,6 +234,7 @@ useCodemirror(
|
||||
linter,
|
||||
completer: null,
|
||||
environmentHighlights: true,
|
||||
predefinedVariablesHighlights: true,
|
||||
})
|
||||
)
|
||||
|
||||
|
||||
@@ -246,6 +246,7 @@ useCodemirror(
|
||||
linter,
|
||||
completer: null,
|
||||
environmentHighlights: true,
|
||||
predefinedVariablesHighlights: true,
|
||||
})
|
||||
)
|
||||
|
||||
|
||||
@@ -78,6 +78,7 @@ import { clone } from "lodash-es"
|
||||
import { history, historyKeymap } from "@codemirror/commands"
|
||||
import { inputTheme } from "~/helpers/editor/themes/baseTheme"
|
||||
import { HoppReactiveEnvPlugin } from "~/helpers/editor/extensions/HoppEnvironment"
|
||||
import { HoppPredefinedVariablesPlugin } from "~/helpers/editor/extensions/HoppPredefinedVariables"
|
||||
import { useReadonlyStream } from "@composables/stream"
|
||||
import { AggregateEnvironment, aggregateEnvs$ } from "~/newstore/environments"
|
||||
import { platform } from "~/platform"
|
||||
@@ -103,6 +104,7 @@ const props = withDefaults(
|
||||
focus?: boolean
|
||||
selectTextOnMount?: boolean
|
||||
environmentHighlights?: boolean
|
||||
predefinedVariablesHighlights?: boolean
|
||||
readonly?: boolean
|
||||
autoCompleteSource?: string[]
|
||||
inspectionResults?: InspectorResult[] | undefined
|
||||
@@ -118,6 +120,7 @@ const props = withDefaults(
|
||||
focus: false,
|
||||
readonly: false,
|
||||
environmentHighlights: true,
|
||||
predefinedVariablesHighlights: true,
|
||||
autoCompleteSource: undefined,
|
||||
inspectionResult: undefined,
|
||||
inspectionResults: undefined,
|
||||
@@ -396,20 +399,22 @@ function envAutoCompletion(context: CompletionContext) {
|
||||
info: env?.value ?? "",
|
||||
apply: env?.key ? `<<${env.key}>>` : "",
|
||||
}))
|
||||
.filter((x) => x)
|
||||
.filter(Boolean)
|
||||
|
||||
const nodeBefore = syntaxTree(context.state).resolveInner(context.pos, -1)
|
||||
const textBefore = context.state.sliceDoc(nodeBefore.from, context.pos)
|
||||
const tagBefore = /<<\w*$/.exec(textBefore)
|
||||
const tagBefore = /<<\$?\w*$/.exec(textBefore) // Update regex to match <<$ as well
|
||||
|
||||
if (!tagBefore && !context.explicit) return null
|
||||
return {
|
||||
from: tagBefore ? nodeBefore.from + tagBefore.index : context.pos,
|
||||
options: options,
|
||||
validFor: /^(<<\w*)?$/,
|
||||
validFor: /^(<<\$?\w*)?$/,
|
||||
}
|
||||
}
|
||||
|
||||
const envTooltipPlugin = new HoppReactiveEnvPlugin(envVars, view)
|
||||
const predefinedVariablePlugin = new HoppPredefinedVariablesPlugin()
|
||||
|
||||
function handleTextSelection() {
|
||||
const selection = view.value?.state.selection.main
|
||||
@@ -490,6 +495,7 @@ const getExtensions = (readonly: boolean): Extension => {
|
||||
position: "absolute",
|
||||
}),
|
||||
props.environmentHighlights ? envTooltipPlugin : [],
|
||||
props.predefinedVariablesHighlights ? predefinedVariablePlugin : [],
|
||||
placeholderExt(props.placeholder),
|
||||
EditorView.domEventHandlers({
|
||||
paste(ev) {
|
||||
|
||||
@@ -47,6 +47,7 @@ import { useDebounceFn } from "@vueuse/core"
|
||||
// TODO: Migrate from legacy mode
|
||||
|
||||
import * as E from "fp-ts/Either"
|
||||
import { HoppPredefinedVariablesPlugin } from "~/helpers/editor/extensions/HoppPredefinedVariables"
|
||||
|
||||
type ExtendedEditorConfig = {
|
||||
mode: string
|
||||
@@ -63,6 +64,12 @@ type CodeMirrorOptions = {
|
||||
// NOTE: This property is not reactive
|
||||
environmentHighlights: boolean
|
||||
|
||||
/**
|
||||
* Whether or not to highlight predefined variables, such as: `<<$guid>>`.
|
||||
* - These are special variables that starts with a dolar sign.
|
||||
*/
|
||||
predefinedVariablesHighlights?: boolean
|
||||
|
||||
additionalExts?: Extension[]
|
||||
|
||||
contextMenuEnabled?: boolean
|
||||
@@ -251,6 +258,10 @@ export function useCodemirror(
|
||||
text: null,
|
||||
})
|
||||
}
|
||||
const predefinedVariable: HoppPredefinedVariablesPlugin | null =
|
||||
options.predefinedVariablesHighlights
|
||||
? new HoppPredefinedVariablesPlugin()
|
||||
: null
|
||||
|
||||
function handleTextSelection() {
|
||||
const selection = view.value?.state.selection.main
|
||||
@@ -396,6 +407,7 @@ export function useCodemirror(
|
||||
]
|
||||
|
||||
if (environmentTooltip) extensions.push(environmentTooltip.extension)
|
||||
if (predefinedVariable) extensions.push(predefinedVariable.extension)
|
||||
|
||||
view.value = new EditorView({
|
||||
parent: el,
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
import { Compartment } from "@codemirror/state"
|
||||
import {
|
||||
Decoration,
|
||||
MatchDecorator,
|
||||
ViewPlugin,
|
||||
hoverTooltip,
|
||||
} from "@codemirror/view"
|
||||
import IconSquareAsterisk from "~icons/lucide/square-asterisk?raw"
|
||||
import { HOPP_SUPPORTED_PREDEFINED_VARIABLES } from "@hoppscotch/data"
|
||||
|
||||
const HOPP_PREDEFINED_VARIABLES_REGEX = /(<<\$[a-zA-Z0-9-_]+>>)/g
|
||||
|
||||
const HOPP_PREDEFINED_VARIABLE_HIGHLIGHT =
|
||||
"cursor-help transition rounded px-1 focus:outline-none mx-0.5 predefined-variable-highlight"
|
||||
const HOPP_PREDEFINED_VARIABLE_HIGHLIGHT_VALID = "predefined-variable-valid"
|
||||
const HOPP_PREDEFINED_VARIABLE_HIGHLIGHT_INVALID = "predefined-variable-invalid"
|
||||
|
||||
const getMatchDecorator = () => {
|
||||
return new MatchDecorator({
|
||||
regexp: HOPP_PREDEFINED_VARIABLES_REGEX,
|
||||
decoration: (m) => checkPredefinedVariable(m[0]),
|
||||
})
|
||||
}
|
||||
|
||||
const cursorTooltipField = () =>
|
||||
hoverTooltip(
|
||||
(view, pos, side) => {
|
||||
const { from, to, text } = view.state.doc.lineAt(pos)
|
||||
|
||||
// TODO: When Codemirror 6 allows this to work (not make the
|
||||
// popups appear half of the time) use this implementation
|
||||
// const wordSelection = view.state.wordAt(pos)
|
||||
// if (!wordSelection) return null
|
||||
// const word = view.state.doc.sliceString(
|
||||
// wordSelection.from - 3,
|
||||
// wordSelection.to + 2
|
||||
// )
|
||||
// if (!HOPP_PREDEFINED_VARIABLES_REGEX.test(word)) return null
|
||||
|
||||
// Tracking the start and the end of the words
|
||||
let start = pos
|
||||
let end = pos
|
||||
|
||||
while (start > from && /[a-zA-Z0-9-_]+/.test(text[start - from - 1]))
|
||||
start--
|
||||
while (end < to && /[a-zA-Z0-9-_]+/.test(text[end - from])) end++
|
||||
|
||||
if (
|
||||
(start === pos && side < 0) ||
|
||||
(end === pos && side > 0) ||
|
||||
!HOPP_PREDEFINED_VARIABLES_REGEX.test(
|
||||
text.slice(start - from - 3, end - from + 2)
|
||||
)
|
||||
) {
|
||||
return null
|
||||
}
|
||||
|
||||
const variableName = text.slice(start - from - 1, end - from)
|
||||
|
||||
const variable = HOPP_SUPPORTED_PREDEFINED_VARIABLES.find(
|
||||
(VARIABLE) => VARIABLE.key === variableName
|
||||
)
|
||||
|
||||
const variableIcon = `<span class="inline-flex items-center justify-center my-1">${IconSquareAsterisk}</span>`
|
||||
const variableDescription =
|
||||
variable !== undefined
|
||||
? `${variableName} - ${variable.description}`
|
||||
: `${variableName} is not a valid predefined variable.`
|
||||
|
||||
return {
|
||||
pos: start,
|
||||
end: to,
|
||||
above: true,
|
||||
arrow: true,
|
||||
create() {
|
||||
const dom = document.createElement("div")
|
||||
dom.className = "tippy-box"
|
||||
dom.dataset.theme = "tooltip"
|
||||
|
||||
const icon = document.createElement("span")
|
||||
icon.innerHTML = variableIcon
|
||||
icon.className = "mr-2"
|
||||
|
||||
const tooltipContainer = document.createElement("span")
|
||||
tooltipContainer.className = "tippy-content"
|
||||
|
||||
tooltipContainer.appendChild(icon)
|
||||
tooltipContainer.appendChild(
|
||||
document.createTextNode(variableDescription)
|
||||
)
|
||||
|
||||
dom.appendChild(tooltipContainer)
|
||||
return { dom }
|
||||
},
|
||||
}
|
||||
},
|
||||
// HACK: This is a hack to fix hover tooltip not coming half of the time
|
||||
// https://github.com/codemirror/tooltip/blob/765c463fc1d5afcc3ec93cee47d72606bed27e1d/src/tooltip.ts#L622
|
||||
// Still doesn't fix the not showing up some of the time issue, but this is atleast more consistent
|
||||
{ hoverTime: 1 } as any
|
||||
)
|
||||
|
||||
const checkPredefinedVariable = (variable: string) => {
|
||||
const inputVariableKey = variable.slice(2, -2)
|
||||
|
||||
const className = HOPP_SUPPORTED_PREDEFINED_VARIABLES.find((v) => {
|
||||
return v.key === inputVariableKey
|
||||
})
|
||||
? HOPP_PREDEFINED_VARIABLE_HIGHLIGHT_VALID
|
||||
: HOPP_PREDEFINED_VARIABLE_HIGHLIGHT_INVALID
|
||||
|
||||
return Decoration.mark({
|
||||
class: `${HOPP_PREDEFINED_VARIABLE_HIGHLIGHT} ${className}`,
|
||||
})
|
||||
}
|
||||
|
||||
export const predefinedVariableHighlightStyle = () => {
|
||||
const decorator = getMatchDecorator()
|
||||
|
||||
return ViewPlugin.define(
|
||||
(view) => ({
|
||||
decorations: decorator.createDeco(view),
|
||||
update(u) {
|
||||
this.decorations = decorator.updateDeco(u, this.decorations)
|
||||
},
|
||||
}),
|
||||
{
|
||||
decorations: (v) => v.decorations,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export class HoppPredefinedVariablesPlugin {
|
||||
private compartment = new Compartment()
|
||||
|
||||
get extension() {
|
||||
return this.compartment.of([
|
||||
cursorTooltipField(),
|
||||
predefinedVariableHighlightStyle(),
|
||||
])
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import {
|
||||
Environment,
|
||||
GlobalEnvironment,
|
||||
GlobalEnvironmentVariable,
|
||||
HOPP_SUPPORTED_PREDEFINED_VARIABLES,
|
||||
} from "@hoppscotch/data"
|
||||
import { cloneDeep, isEqual } from "lodash-es"
|
||||
import { combineLatest, Observable } from "rxjs"
|
||||
@@ -407,24 +408,45 @@ export type AggregateEnvironment = {
|
||||
export const aggregateEnvs$: Observable<AggregateEnvironment[]> = combineLatest(
|
||||
[currentEnvironment$, globalEnv$]
|
||||
).pipe(
|
||||
map(([selectedEnv, globalVars]) => {
|
||||
const results: AggregateEnvironment[] = []
|
||||
map(([selectedEnv, globalEnv]) => {
|
||||
const effectiveAggregateEnvs: AggregateEnvironment[] = []
|
||||
|
||||
// Ensure pre-defined variables are prioritised over other environment variables with the same name
|
||||
HOPP_SUPPORTED_PREDEFINED_VARIABLES.forEach(({ key, getValue }) => {
|
||||
effectiveAggregateEnvs.push({
|
||||
key,
|
||||
value: getValue(),
|
||||
secret: false,
|
||||
sourceEnv: selectedEnv?.name ?? "Global",
|
||||
})
|
||||
})
|
||||
|
||||
const aggregateEnvKeys = effectiveAggregateEnvs.map(({ key }) => key)
|
||||
|
||||
selectedEnv?.variables.forEach((variable) => {
|
||||
const { key, secret } = variable
|
||||
const value = "value" in variable ? variable.value : ""
|
||||
|
||||
results.push({ key, value, secret, sourceEnv: selectedEnv.name })
|
||||
if (!aggregateEnvKeys.includes(key)) {
|
||||
effectiveAggregateEnvs.push({
|
||||
key,
|
||||
value,
|
||||
secret,
|
||||
sourceEnv: selectedEnv.name,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
globalVars.variables.forEach((variable) => {
|
||||
globalEnv.variables.forEach((variable) => {
|
||||
const { key, secret } = variable
|
||||
const value = "value" in variable ? variable.value : ""
|
||||
|
||||
results.push({ key, value, secret, sourceEnv: "Global" })
|
||||
if (!aggregateEnvKeys.includes(key)) {
|
||||
effectiveAggregateEnvs.push({ key, value, secret, sourceEnv: "Global" })
|
||||
}
|
||||
})
|
||||
|
||||
return results
|
||||
return effectiveAggregateEnvs
|
||||
}),
|
||||
distinctUntilChanged(isEqual)
|
||||
)
|
||||
@@ -503,7 +525,7 @@ export function getAggregateEnvsWithSecrets() {
|
||||
|
||||
export const aggregateEnvsWithSecrets$: Observable<AggregateEnvironment[]> =
|
||||
combineLatest([currentEnvironment$, globalEnv$]).pipe(
|
||||
map(([selectedEnv, globalVars]) => {
|
||||
map(([selectedEnv, globalEnv]) => {
|
||||
const results: AggregateEnvironment[] = []
|
||||
selectedEnv?.variables.map((x, index) => {
|
||||
let value
|
||||
@@ -523,7 +545,7 @@ export const aggregateEnvsWithSecrets$: Observable<AggregateEnvironment[]> =
|
||||
})
|
||||
})
|
||||
|
||||
globalVars.variables.map((x, index) => {
|
||||
globalEnv.variables.map((x, index) => {
|
||||
let value
|
||||
if (x.secret) {
|
||||
value = secretEnvironmentService.getSecretEnvironmentVariableValue(
|
||||
|
||||
@@ -6,6 +6,7 @@ import { z } from "zod"
|
||||
|
||||
import V0_VERSION from "./v/0"
|
||||
import V1_VERSION, { uniqueID } from "./v/1"
|
||||
import { HOPP_SUPPORTED_PREDEFINED_VARIABLES } from "../predefinedVariables"
|
||||
|
||||
const versionedObject = z.object({
|
||||
v: z.number(),
|
||||
@@ -58,12 +59,21 @@ export function parseBodyEnvVariablesE(
|
||||
|
||||
while (result.match(REGEX_ENV_VAR) != null && depth <= ENV_MAX_EXPAND_LIMIT) {
|
||||
result = result.replace(REGEX_ENV_VAR, (key) => {
|
||||
const found = env.find(
|
||||
(envVar) => envVar.key === key.replace(/[<>]/g, "")
|
||||
const variableName = key.replace(/[<>]/g, "")
|
||||
|
||||
// Prioritise predefined variable values over normal environment variables processing.
|
||||
const foundPredefinedVar = HOPP_SUPPORTED_PREDEFINED_VARIABLES.find(
|
||||
(preVar) => preVar.key === variableName
|
||||
)
|
||||
|
||||
if (found && "value" in found) {
|
||||
return found.value
|
||||
if (foundPredefinedVar) {
|
||||
return foundPredefinedVar.getValue()
|
||||
}
|
||||
|
||||
const foundEnv = env.find((envVar) => envVar.key === variableName)
|
||||
|
||||
if (foundEnv && "value" in foundEnv) {
|
||||
return foundEnv.value
|
||||
}
|
||||
return key
|
||||
})
|
||||
@@ -110,6 +120,15 @@ export function parseTemplateStringE(
|
||||
!isSecret
|
||||
) {
|
||||
result = decodeURI(encodeURI(result)).replace(REGEX_ENV_VAR, (_, p1) => {
|
||||
// Prioritise predefined variable values over normal environment variables processing.
|
||||
const foundPredefinedVar = HOPP_SUPPORTED_PREDEFINED_VARIABLES.find(
|
||||
(preVar) => preVar.key === p1
|
||||
)
|
||||
|
||||
if (foundPredefinedVar) {
|
||||
return foundPredefinedVar.getValue()
|
||||
}
|
||||
|
||||
const variable = variables.find((x) => x && x.key === p1)
|
||||
|
||||
if (variable && "value" in variable) {
|
||||
|
||||
@@ -4,3 +4,4 @@ export * from "./collection"
|
||||
export * from "./rawKeyValue"
|
||||
export * from "./environment"
|
||||
export * from "./global-environment"
|
||||
export * from "./predefinedVariables"
|
||||
|
||||
370
packages/hoppscotch-data/src/predefinedVariables.ts
Normal file
370
packages/hoppscotch-data/src/predefinedVariables.ts
Normal file
@@ -0,0 +1,370 @@
|
||||
export type PredefinedVariable = {
|
||||
key: `$${string}`
|
||||
description: string
|
||||
getValue: () => string
|
||||
}
|
||||
|
||||
export const HOPP_SUPPORTED_PREDEFINED_VARIABLES: PredefinedVariable[] = [
|
||||
// Common
|
||||
{
|
||||
key: "$guid",
|
||||
description: "A v4 style GUID.",
|
||||
getValue: () => {
|
||||
const characters = "0123456789abcdef"
|
||||
let guid = ""
|
||||
for (let i = 0; i < 36; i++) {
|
||||
if (i === 8 || i === 13 || i === 18 || i === 23) {
|
||||
guid += "-"
|
||||
} else if (i === 14) {
|
||||
guid += "4"
|
||||
} else if (i === 19) {
|
||||
guid += characters.charAt(8 + Math.floor(Math.random() * 4))
|
||||
} else {
|
||||
guid += characters.charAt(
|
||||
Math.floor(Math.random() * characters.length)
|
||||
)
|
||||
}
|
||||
}
|
||||
return guid
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "$nowISO",
|
||||
description: "Current date and time in ISO-8601 format.",
|
||||
getValue: () => new Date().toISOString(),
|
||||
},
|
||||
{
|
||||
key: "$timestamp",
|
||||
description: "The current UNIX timestamp in seconds.",
|
||||
getValue: () => Math.floor(Date.now() / 1000).toString(),
|
||||
},
|
||||
|
||||
{
|
||||
key: "$isoTimestamp",
|
||||
description: "The current ISO timestamp at zero UTC.",
|
||||
getValue: () => new Date().toISOString(),
|
||||
},
|
||||
{
|
||||
key: "$randomUUID",
|
||||
description: "A random 36-character UUID.",
|
||||
getValue: () => {
|
||||
const characters = "0123456789abcdef"
|
||||
let uuid = ""
|
||||
for (let i = 0; i < 36; i++) {
|
||||
if (i === 8 || i === 13 || i === 18 || i === 23) {
|
||||
uuid += "-"
|
||||
} else {
|
||||
uuid += characters.charAt(
|
||||
Math.floor(Math.random() * characters.length)
|
||||
)
|
||||
}
|
||||
}
|
||||
return uuid
|
||||
},
|
||||
},
|
||||
|
||||
// Text, numbers, and colors
|
||||
{
|
||||
key: "$randomAlphaNumeric",
|
||||
description: "A random alpha-numeric character.",
|
||||
getValue: () => {
|
||||
const characters =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||
return characters.charAt(Math.floor(Math.random() * characters.length))
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
key: "$randomBoolean",
|
||||
description: "A random boolean value.",
|
||||
getValue: () => (Math.random() < 0.5 ? "true" : "false"),
|
||||
},
|
||||
|
||||
{
|
||||
key: "$randomInt",
|
||||
description: "A random integer between 0 and 1000.",
|
||||
getValue: () => Math.floor(Math.random() * 1000).toString(),
|
||||
},
|
||||
|
||||
{
|
||||
key: "$randomColor",
|
||||
description: "A random color.",
|
||||
getValue: () => {
|
||||
const colors = ["red", "green", "blue", "yellow", "purple", "orange"]
|
||||
return colors[Math.floor(Math.random() * colors.length)]
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
key: "$randomHexColor",
|
||||
description: "A random hex value.",
|
||||
getValue: () => {
|
||||
const characters = "0123456789abcdef"
|
||||
let color = "#"
|
||||
for (let i = 0; i < 6; i++) {
|
||||
color += characters.charAt(
|
||||
Math.floor(Math.random() * characters.length)
|
||||
)
|
||||
}
|
||||
return color
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
key: "$randomAbbreviation",
|
||||
description: "A random abbreviation.",
|
||||
getValue: () => {
|
||||
const abbreviations = [
|
||||
"SQL",
|
||||
"PCI",
|
||||
"JSON",
|
||||
"HTML",
|
||||
"CSS",
|
||||
"JS",
|
||||
"TS",
|
||||
"API",
|
||||
]
|
||||
return abbreviations[Math.floor(Math.random() * abbreviations.length)]
|
||||
},
|
||||
},
|
||||
|
||||
// Internet and IP addresses
|
||||
{
|
||||
key: "$randomIP",
|
||||
description: "A random IPv4 address.",
|
||||
getValue: () => {
|
||||
const ip = Array.from({ length: 4 }, () =>
|
||||
Math.floor(Math.random() * 256)
|
||||
)
|
||||
return ip.join(".")
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
key: "$randomIPV6",
|
||||
description: "A random IPv6 address.",
|
||||
getValue: () => {
|
||||
const ip = Array.from({ length: 8 }, () =>
|
||||
Math.floor(Math.random() * 65536).toString(16)
|
||||
)
|
||||
return ip.join(":")
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
key: "$randomMACAddress",
|
||||
description: "A random MAC address.",
|
||||
getValue: () => {
|
||||
const mac = Array.from({ length: 6 }, () =>
|
||||
Math.floor(Math.random() * 256).toString(16)
|
||||
)
|
||||
return mac.join(":")
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
key: "$randomPassword",
|
||||
description: "A random 15-character alpha-numeric password.",
|
||||
getValue: () => {
|
||||
const characters =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||
let password = ""
|
||||
for (let i = 0; i < 15; i++) {
|
||||
password += characters.charAt(
|
||||
Math.floor(Math.random() * characters.length)
|
||||
)
|
||||
}
|
||||
return password
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
key: "$randomLocale",
|
||||
description: "A random two-letter language code (ISO 639-1).",
|
||||
getValue: () => {
|
||||
const locales = ["ny", "sr", "si"]
|
||||
return locales[Math.floor(Math.random() * locales.length)]
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
key: "$randomUserAgent",
|
||||
description: "A random user agent.",
|
||||
getValue: () => {
|
||||
const userAgents = [
|
||||
"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.9.8; rv:15.6) Gecko/20100101 Firefox/15.6.6",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:15.6) Gecko/20100101 Firefox/15.6.6",
|
||||
"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:15.6) Gecko/20100101 Firefox/15.6.6",
|
||||
]
|
||||
return userAgents[Math.floor(Math.random() * userAgents.length)]
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "$randomProtocol",
|
||||
description: "A random internet protocol.",
|
||||
getValue: () => {
|
||||
const protocols = ["http", "https"]
|
||||
return protocols[Math.floor(Math.random() * protocols.length)]
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
key: "$randomSemver",
|
||||
description: "A random semantic version number.",
|
||||
getValue: () => {
|
||||
const semver = Array.from({ length: 3 }, () =>
|
||||
Math.floor(Math.random() * 10)
|
||||
)
|
||||
return semver.join(".")
|
||||
},
|
||||
},
|
||||
|
||||
// Names
|
||||
{
|
||||
key: "$randomFirstName",
|
||||
description: "A random first name.",
|
||||
getValue: () => {
|
||||
const firstNames = [
|
||||
"Ethan",
|
||||
"Chandler",
|
||||
"Megane",
|
||||
"John",
|
||||
"Jane",
|
||||
"Alice",
|
||||
"Bob",
|
||||
]
|
||||
return firstNames[Math.floor(Math.random() * firstNames.length)]
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "$randomLastName",
|
||||
description: "A random last name.",
|
||||
getValue: () => {
|
||||
const lastNames = [
|
||||
"Schaden",
|
||||
"Schneider",
|
||||
"Willms",
|
||||
"Doe",
|
||||
"Smith",
|
||||
"Johnson",
|
||||
]
|
||||
return lastNames[Math.floor(Math.random() * lastNames.length)]
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "$randomFullName",
|
||||
description: "A random first and last name.",
|
||||
getValue: () => {
|
||||
const firstNames = [
|
||||
"Ethan",
|
||||
"Chandler",
|
||||
"Megane",
|
||||
"John",
|
||||
"Jane",
|
||||
"Alice",
|
||||
"Bob",
|
||||
]
|
||||
const lastNames = [
|
||||
"Schaden",
|
||||
"Schneider",
|
||||
"Willms",
|
||||
"Doe",
|
||||
"Smith",
|
||||
"Johnson",
|
||||
]
|
||||
return `${firstNames[Math.floor(Math.random() * firstNames.length)]} ${
|
||||
lastNames[Math.floor(Math.random() * lastNames.length)]
|
||||
}`
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "$randomNamePrefix",
|
||||
description: "A random name prefix.",
|
||||
getValue: () => {
|
||||
const prefixes = ["Dr.", "Ms.", "Mr.", "Mrs.", "Miss", "Prof."]
|
||||
return prefixes[Math.floor(Math.random() * prefixes.length)]
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "$randomNameSuffix",
|
||||
description: "A random name suffix.",
|
||||
getValue: () => {
|
||||
const suffixes = ["I", "MD", "DDS", "PhD", "Esq.", "Jr."]
|
||||
return suffixes[Math.floor(Math.random() * suffixes.length)]
|
||||
},
|
||||
},
|
||||
|
||||
// Addresses
|
||||
{
|
||||
key: "$randomCity",
|
||||
description: "A random city name.",
|
||||
getValue: () => {
|
||||
const cities = [
|
||||
"New York",
|
||||
"Los Angeles",
|
||||
"Chicago",
|
||||
"Houston",
|
||||
"Phoenix",
|
||||
"Philadelphia",
|
||||
]
|
||||
return cities[Math.floor(Math.random() * cities.length)]
|
||||
},
|
||||
},
|
||||
|
||||
// profession
|
||||
{
|
||||
key: "$randomJobArea",
|
||||
description: "A random job area.",
|
||||
getValue: () => {
|
||||
const jobAreas = [
|
||||
"Mobility",
|
||||
"Intranet",
|
||||
"Configuration",
|
||||
"Development",
|
||||
"Design",
|
||||
"Testing",
|
||||
]
|
||||
return jobAreas[Math.floor(Math.random() * jobAreas.length)]
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "$randomJobDescriptor",
|
||||
description: "A random job descriptor.",
|
||||
getValue: () => {
|
||||
const jobDescriptors = [
|
||||
"Forward",
|
||||
"Corporate",
|
||||
"Senior",
|
||||
"Junior",
|
||||
"Lead",
|
||||
"Principal",
|
||||
]
|
||||
return jobDescriptors[Math.floor(Math.random() * jobDescriptors.length)]
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "$randomJobTitle",
|
||||
description: "A random job title.",
|
||||
getValue: () => {
|
||||
const jobTitles = [
|
||||
"International Creative Liaison",
|
||||
"Global Branding Officer",
|
||||
"Dynamic Data Specialist",
|
||||
"Internal Communications Consultant",
|
||||
"Productivity Analyst",
|
||||
"Regional Applications Developer",
|
||||
]
|
||||
return jobTitles[Math.floor(Math.random() * jobTitles.length)]
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "$randomJobType",
|
||||
description: "A random job type.",
|
||||
getValue: () => {
|
||||
const jobTypes = ["Supervisor", "Manager", "Coordinator", "Director"]
|
||||
return jobTypes[Math.floor(Math.random() * jobTypes.length)]
|
||||
},
|
||||
},
|
||||
|
||||
// TODO: Support various other predefined variables
|
||||
]
|
||||
Reference in New Issue
Block a user