Monorepo with yarn workspaces requires yarn 3.2

This commit is contained in:
Zef Hemel
2022-04-21 13:57:45 +02:00
parent 32f3501773
commit 1f842ec1d6
167 changed files with 10424 additions and 8263 deletions
-105
View File
@@ -1,105 +0,0 @@
syntax:
HashTag:
firstCharacters:
- "#"
regex: "#[A-Za-z\\.]+"
styles:
color: blue
AtMention:
firstCharacters:
- "@"
regex: "@[A-Za-z\\.]+"
styles:
color: blue
NakedURL:
firstCharacters:
- "h"
regex: "https?:\\/\\/[-a-zA-Z0-9@:%._\\+~#=]{1,256}([-a-zA-Z0-9()@:%_\\+.~#?&=\\/]*)"
styles:
color: "#0330cb"
textDecoration: underline
functions:
clearPageIndex:
path: "./page.ts:clearPageIndex"
env: server
events:
- page:saved
- page:deleted
pageQueryProvider:
path: ./page.ts:pageQueryProvider
events:
- query:page
parseIndexTextRepublish:
path: "./page.ts:parseIndexTextRepublish"
events:
- page:index_text
indexLinks:
path: "./page.ts:indexLinks"
events:
- page:index
linkQueryProvider:
path: ./page.ts:linkQueryProvider
events:
- query:link
indexItems:
path: "./item.ts:indexItems"
events:
- page:index
itemQueryProvider:
path: ./item.ts:queryProvider
events:
- query:item
deletePage:
path: "./page.ts:deletePage"
command:
name: "Page: Delete"
reindexSpaceCommand:
path: "./page.ts:reindexCommand"
command:
name: "Space: Reindex"
reindexSpace:
path: "./page.ts:reindexSpace"
env: server
renamePage:
path: "./page.ts:renamePage"
command:
name: "Page: Rename"
mac: Cmd-Alt-r
key: Ctrl-Alt-r
pageComplete:
path: "./page.ts:pageComplete"
events:
- page:complete
linkNavigate:
path: "./navigate.ts:linkNavigate"
command:
name: Navigate To page
key: Ctrl-Enter
mac: Cmd-Enter
clickNavigate:
path: "./navigate.ts:clickNavigate"
events:
- page:click
insertToday:
path: "./dates.ts:insertToday"
slashCommand:
name: today
insertTomorrow:
path: "./dates.ts:insertTomorrow"
slashCommand:
name: tomorrow
parseServerCommand:
path: ./page.ts:parseServerPageCommand
command:
name: "Debug: Parse Document on Server"
parsePage:
path: ./page.ts:parsePage
parseCommand:
path: ./page.ts:parsePageCommand
command:
name: "Debug: Parse Document"
instantiateTemplateCommand:
path: ./template.ts:instantiateTemplateCommand
command:
name: "Template: Instantiate for Page"
-17
View File
@@ -1,17 +0,0 @@
import { insertAtCursor } from "plugos-silverbullet-syscall/editor";
const dateMatchRegex = /(\d{4}\-\d{2}\-\d{2})/g;
export function niceDate(d: Date): string {
return d.toISOString().split("T")[0];
}
export async function insertToday() {
await insertAtCursor(niceDate(new Date()));
}
export async function insertTomorrow() {
let d = new Date();
d.setDate(d.getDate() + 1);
await insertAtCursor(niceDate(d));
}
-71
View File
@@ -1,71 +0,0 @@
import { IndexTreeEvent } from "../../webapp/app_event";
import { batchSet, scanPrefixGlobal } from "plugos-silverbullet-syscall/index";
import { collectNodesOfType, ParseTree, renderToText } from "../../common/tree";
import { removeQueries } from "../query/util";
import { applyQuery, QueryProviderEvent } from "../query/engine";
export type Item = {
name: string;
nested?: string;
// Not stored in DB
page?: string;
pos?: number;
};
export async function indexItems({ name, tree }: IndexTreeEvent) {
let items: { key: string; value: Item }[] = [];
removeQueries(tree);
console.log("Indexing items", name);
let coll = collectNodesOfType(tree, "ListItem");
coll.forEach((n) => {
if (!n.children) {
return;
}
let textNodes: ParseTree[] = [];
let nested: string | undefined;
for (let child of n.children!.slice(1)) {
if (child.type === "OrderedList" || child.type === "BulletList") {
nested = renderToText(child);
break;
}
textNodes.push(child);
}
let item = textNodes.map(renderToText).join("").trim();
let value: Item = {
name: item,
};
if (nested) {
value.nested = nested;
}
items.push({
key: `it:${n.from}`,
value,
});
});
console.log("Found", items.length, "item(s)");
await batchSet(name, items);
}
export async function queryProvider({
query,
}: QueryProviderEvent): Promise<string> {
let allItems: Item[] = [];
for (let { key, page, value } of await scanPrefixGlobal("it:")) {
let [, pos] = key.split(":");
allItems.push({
...value,
page: page,
pos: +pos,
});
}
let markdownItems = applyQuery(query, allItems).map(
(item) =>
`* [[${item.page}@${item.pos}]] ${item.name}` +
(item.nested ? "\n " + item.nested : "")
);
return markdownItems.join("\n");
}
-38
View File
@@ -1,38 +0,0 @@
import {
getCursor,
getText,
insertAtPos,
replaceRange,
} from "plugos-silverbullet-syscall/editor";
export async function toggleH1() {
await togglePrefix("# ");
}
export async function toggleH2() {
await togglePrefix("## ");
}
function lookBack(s: string, pos: number, backString: string): boolean {
return s.substring(pos - backString.length, pos) === backString;
}
async function togglePrefix(prefix: string) {
let text = await getText();
let pos = await getCursor();
if (text[pos] === "\n") {
pos--;
}
while (pos > 0 && text[pos] !== "\n") {
if (lookBack(text, pos, prefix)) {
// Already has this prefix, let's flip it
await replaceRange(pos - prefix.length, pos, "");
return;
}
pos--;
}
if (pos) {
pos++;
}
await insertAtPos(prefix, pos);
}
-46
View File
@@ -1,46 +0,0 @@
import { ClickEvent } from "../../webapp/app_event";
import { getCursor, getText, navigate as navigateTo, openUrl } from "plugos-silverbullet-syscall/editor";
import { parseMarkdown } from "plugos-silverbullet-syscall/markdown";
import { nodeAtPos, ParseTree } from "../../common/tree";
const materializedQueryPrefix = /<!--\s*#query\s+/;
async function actionClickOrActionEnter(mdTree: ParseTree | null) {
if (!mdTree) {
return;
}
console.log("Attempting to navigate based on syntax node", mdTree);
switch (mdTree.type) {
case "WikiLinkPage":
let pageLink = mdTree.children![0].text!;
let pos = "0";
if (pageLink.includes("@")) {
[pageLink, pos] = pageLink.split("@");
}
await navigateTo(pageLink, +pos);
break;
case "URL":
case "NakedURL":
await openUrl(mdTree.children![0].text!);
break;
case "Link":
await openUrl(mdTree.children![4].children![0].text!);
break;
}
}
export async function linkNavigate() {
let mdTree = await parseMarkdown(await getText());
let newNode = nodeAtPos(mdTree, await getCursor());
await actionClickOrActionEnter(newNode);
}
export async function clickNavigate(event: ClickEvent) {
// Navigate by default, don't navigate when Ctrl or Cmd is held
if (event.ctrlKey || event.metaKey) {
return;
}
let mdTree = await parseMarkdown(await getText());
let newNode = nodeAtPos(mdTree, event.pos);
await actionClickOrActionEnter(newNode);
}
-253
View File
@@ -1,253 +0,0 @@
import { IndexEvent, IndexTreeEvent } from "../../webapp/app_event";
import {
batchSet,
clearPageIndex as clearPageIndexSyscall,
clearPageIndexForPage,
scanPrefixGlobal,
set
} from "plugos-silverbullet-syscall/index";
import {
flashNotification,
getCurrentPage,
getText,
matchBefore,
navigate,
prompt
} from "plugos-silverbullet-syscall/editor";
import { dispatch } from "plugos-syscall/event";
import { deletePage as deletePageSyscall, listPages, readPage, writePage } from "plugos-silverbullet-syscall/space";
import { invokeFunction } from "plugos-silverbullet-syscall/system";
import { parseMarkdown } from "plugos-silverbullet-syscall/markdown";
import {
addParentPointers,
collectNodesMatching,
ParseTree,
renderToText,
replaceNodesMatching
} from "../../common/tree";
import { applyQuery, QueryProviderEvent } from "../query/engine";
import { PageMeta } from "../../common/types";
import { extractMeta } from "../query/data";
import { jsonToMDTable } from "../query/util";
// Key space:
// pl:toPage:pos => pageName
// meta => metaJson
export async function indexLinks({ name, tree }: IndexTreeEvent) {
let backLinks: { key: string; value: string }[] = [];
// [[Style Links]]
console.log("Now indexing", name);
let pageMeta = extractMeta(tree);
if (Object.keys(pageMeta).length > 0) {
await set(name, "meta:", pageMeta);
}
collectNodesMatching(tree, (n) => n.type === "WikiLinkPage").forEach((n) => {
let toPage = n.children![0].text!;
if (toPage.includes("@")) {
toPage = toPage.split("@")[0];
}
backLinks.push({
key: `pl:${toPage}:${n.from}`,
value: name,
});
});
console.log("Found", backLinks.length, "wiki link(s)");
await batchSet(name, backLinks);
}
export async function pageQueryProvider({
query,
}: QueryProviderEvent): Promise<string> {
let allPages = await listPages();
if (query.select) {
let allPageMap: Map<string, any> = new Map(
allPages.map((pm) => [pm.name, pm])
);
for (let { page, value } of await scanPrefixGlobal("meta:")) {
let p = allPageMap.get(page);
if (p) {
for (let [k, v] of Object.entries(value)) {
p[k] = v;
}
}
}
allPages = [...allPageMap.values()];
return jsonToMDTable(applyQuery(query, allPages), (k, v) =>
k === "name" ? `[[${v}]]` : v
);
} else {
return applyQuery(query, allPages)
.map((pageMeta: PageMeta) => `* [[${pageMeta.name}]]`)
.join("\n");
}
}
export async function linkQueryProvider({
query,
pageName,
}: QueryProviderEvent): Promise<string> {
let uniqueLinks = new Set<string>();
for (let { value: name } of await scanPrefixGlobal(`pl:${pageName}:`)) {
uniqueLinks.add(name);
}
let markdownLinks = applyQuery(
query,
[...uniqueLinks].map((l) => ({ name: l }))
).map((pageMeta) => `* [[${pageMeta.name}]]`);
return markdownLinks.join("\n");
}
export async function deletePage() {
let pageName = await getCurrentPage();
console.log("Navigating to start page");
await navigate("start");
console.log("Deleting page from space");
await deletePageSyscall(pageName);
}
export async function renamePage() {
const oldName = await getCurrentPage();
console.log("Old name is", oldName);
const newName = await prompt(`Rename ${oldName} to:`, oldName);
if (!newName) {
return;
}
console.log("New name", newName);
let pagesToUpdate = await getBackLinks(oldName);
console.log("All pages containing backlinks", pagesToUpdate);
let text = await getText();
console.log("Writing new page to space");
await writePage(newName, text);
console.log("Navigating to new page");
await navigate(newName);
console.log("Deleting page from space");
await deletePageSyscall(oldName);
let pageToUpdateSet = new Set<string>();
for (let pageToUpdate of pagesToUpdate) {
pageToUpdateSet.add(pageToUpdate.page);
}
for (let pageToUpdate of pageToUpdateSet) {
if (pageToUpdate === oldName) {
continue;
}
console.log("Now going to update links in", pageToUpdate);
let { text } = await readPage(pageToUpdate);
// console.log("Received text", text);
if (!text) {
// Page likely does not exist, but at least we can skip it
continue;
}
let mdTree = await parseMarkdown(text);
addParentPointers(mdTree);
replaceNodesMatching(mdTree, (n): ParseTree | undefined | null => {
if (n.type === "WikiLinkPage") {
let pageName = n.children![0].text!;
if (pageName === oldName) {
n.children![0].text = newName;
return n;
}
// page name with @pos position
if (pageName.startsWith(`${oldName}@`)) {
let [, pos] = pageName.split("@");
n.children![0].text = `${newName}@${pos}`;
return n;
}
}
return;
});
// let newText = text.replaceAll(`[[${oldName}]]`, `[[${newName}]]`);
let newText = renderToText(mdTree);
if (text !== newText) {
console.log("Changes made, saving...");
await writePage(pageToUpdate, newText);
}
}
}
type BackLink = {
page: string;
pos: number;
};
async function getBackLinks(pageName: string): Promise<BackLink[]> {
let allBackLinks = await scanPrefixGlobal(`pl:${pageName}:`);
let pagesToUpdate: BackLink[] = [];
for (let { key, value } of allBackLinks) {
let keyParts = key.split(":");
pagesToUpdate.push({
page: value,
pos: +keyParts[keyParts.length - 1],
});
}
return pagesToUpdate;
}
export async function reindexCommand() {
await flashNotification("Reindexing...");
await invokeFunction("server", "reindexSpace");
await flashNotification("Reindexing done");
}
// Completion
export async function pageComplete() {
let prefix = await matchBefore("\\[\\[[\\w\\s]*");
if (!prefix) {
return null;
}
let allPages = await listPages();
return {
from: prefix.from + 2,
options: allPages.map((pageMeta) => ({
label: pageMeta.name,
type: "page",
})),
};
}
// Server functions
export async function reindexSpace() {
console.log("Clearing page index...");
await clearPageIndexSyscall();
console.log("Listing all pages");
let pages = await listPages();
for (let { name } of pages) {
console.log("Indexing", name);
const { text } = await readPage(name);
let parsed = await parseMarkdown(text);
await dispatch("page:index", {
name,
tree: parsed,
});
}
}
export async function clearPageIndex(page: string) {
console.log("Clearing page index for page", page);
await clearPageIndexForPage(page);
}
export async function parseIndexTextRepublish({ name, text }: IndexEvent) {
await dispatch("page:index", {
name,
tree: await parseMarkdown(text),
});
}
export async function parseServerPageCommand() {
console.log(await invokeFunction("server", "parsePage", await getText()));
}
export async function parsePageCommand() {
parsePage(await getText());
}
export async function parsePage(text: string) {
console.log("AST", JSON.stringify(await parseMarkdown(text), null, 2));
}
-9
View File
@@ -1,9 +0,0 @@
import { EndpointRequest, EndpointResponse } from "../../plugos/hooks/endpoint";
export function endpointTest(req: EndpointRequest): EndpointResponse {
console.log("I'm running on the server!", req);
return {
status: 200,
body: "Hello world!",
};
}
-58
View File
@@ -1,58 +0,0 @@
import { listPages, readPage, writePage } from "plugos-silverbullet-syscall/space";
import { filterBox, navigate, prompt } from "plugos-silverbullet-syscall/editor";
import { parseMarkdown } from "plugos-silverbullet-syscall/markdown";
import { extractMeta } from "../query/data";
import { renderToText } from "../../common/tree";
import { niceDate } from "./dates";
const pageTemplatePrefix = `template/page/`;
export async function instantiateTemplateCommand() {
let allPages = await listPages();
let allPageTemplates = allPages.filter((pageMeta) =>
pageMeta.name.startsWith(pageTemplatePrefix)
);
let selectedTemplate = await filterBox(
"Template",
allPageTemplates,
"Select the template to create a new page from"
);
if (!selectedTemplate) {
return;
}
console.log("Selected template", selectedTemplate);
let { text } = await readPage(selectedTemplate.name);
let parseTree = await parseMarkdown(text);
let additionalPageMeta = extractMeta(parseTree, true);
console.log("Page meta", additionalPageMeta);
let pageName = await prompt("Name of new page", additionalPageMeta.name);
if (!pageName) {
return;
}
let pageText = replaceTemplateVars(renderToText(parseTree), pageName);
await writePage(pageName, pageText);
await navigate(pageName);
}
export function replaceTemplateVars(s: string, pageName: string): string {
return s.replaceAll(/\{\{([^\}]+)\}\}/g, (match, v) => {
switch (v) {
case "today":
return niceDate(new Date());
case "yesterday":
let yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1);
return niceDate(yesterday);
case "lastWeek":
let lastWeek = new Date();
lastWeek.setDate(lastWeek.getDate() - 7);
return niceDate(lastWeek);
}
return match;
});
}
-9
View File
@@ -1,9 +0,0 @@
function countWords(str: string): number {
const matches = str.match(/[\w\d\'-]+/gi);
return matches ? matches.length : 0;
}
function readingTime(wordCount: number): number {
// 225 is average word reading speed for adults
return Math.ceil(wordCount / 225);
}
-1
View File
@@ -1 +0,0 @@
emoji-data.txt
-4
View File
@@ -1,4 +0,0 @@
build:
curl https://unicode.org/Public/emoji/14.0/emoji-test.txt > emoji-data.txt
node build.js
rm emoji-data.txt
-17
View File
@@ -1,17 +0,0 @@
// Generates emoji.json from emoji-data.txt
const { readFileSync, writeFileSync } = require("fs");
const emojiRe = /#\s([^\s]+)\s+E[^\s]+\s+(.+)$/;
let text = readFileSync("emoji-data.txt", "utf-8");
const lines = text.split("\n").filter((line) => !line.startsWith("#"));
let emoji = [];
for (const line of lines) {
let match = emojiRe.exec(line);
if (match) {
emoji.push([match[1], match[2].toLowerCase().replaceAll(/\W+/g, "_")]);
}
}
writeFileSync("emoji.json", JSON.stringify(emoji));
File diff suppressed because one or more lines are too long
-5
View File
@@ -1,5 +0,0 @@
functions:
emojiCompleter:
path: "./emoji.ts:emojiCompleter"
events:
- page:complete
-26
View File
@@ -1,26 +0,0 @@
// @ts-ignore
import emojis from "./emoji.json";
import { matchBefore } from "plugos-silverbullet-syscall/editor";
const emojiMatcher = /\(([^\)]+)\)\s+(.+)$/;
export async function emojiCompleter() {
let prefix = await matchBefore(":[\\w]+");
if (!prefix) {
return null;
}
const textPrefix = prefix.text.substring(1); // Cut off the initial :
let filteredEmoji = emojis.filter(([_, shortcode]) =>
shortcode.includes(textPrefix)
);
return {
from: prefix.from,
filter: false,
options: filteredEmoji.map(([emoji, shortcode]) => ({
detail: shortcode,
label: emoji,
type: "emoji",
})),
};
}
-15
View File
@@ -1,15 +0,0 @@
functions:
downloadAllPostsCommand:
path: "./ghost.ts:downloadAllPostsCommand"
command:
name: "Ghost: Download Posts"
downloadAllPosts:
path: "./ghost.ts:downloadAllPosts"
env: server
publishCommand:
path: "./ghost.ts:publishCommand"
command:
name: "Ghost: Publish"
publish:
path: "./ghost.ts:publish"
env: server
-232
View File
@@ -1,232 +0,0 @@
import { readPage, writePage } from "plugos-silverbullet-syscall/space";
import { json } from "plugos-syscall/fetch";
import { invokeFunction } from "plugos-silverbullet-syscall/system";
import { getCurrentPage, getText } from "plugos-silverbullet-syscall/editor";
import { cleanMarkdown } from "../markdown/util";
import { parseMarkdown } from "plugos-silverbullet-syscall/markdown";
import { extractMeta } from "../query/data";
type GhostConfig = {
url: string;
adminKey: string;
postPrefix: string;
pagePrefix: string;
};
type Post = {
id: string;
uuid: string;
title: string;
slug: string;
mobiledoc: string;
status: "draft" | "published";
visibility: string;
created_at: string;
upblished_at: string;
updated_at: string;
tags: Tag[];
primary_tag: Tag;
url: string;
excerpt: string;
};
type Tag = {
id: string;
name: string;
slug: string;
description: string | null;
};
type MobileDoc = {
version: string;
atoms: any[];
cards: Card[];
};
type Card = any[];
function mobileDocToMarkdown(doc: string): string | null {
let mobileDoc = JSON.parse(doc) as MobileDoc;
if (mobileDoc.cards.length > 0 && mobileDoc.cards[0][0] === "markdown") {
return mobileDoc.cards[0][1].markdown;
}
return null;
}
function markdownToMobileDoc(text: string): string {
return JSON.stringify({
version: "0.3.1",
atoms: [],
cards: [["markdown", { markdown: text }]],
markups: [],
sections: [
[10, 0],
[1, "p", []],
],
});
}
class GhostAdmin {
private token?: string;
constructor(private url: string, private key: string) {}
async init() {
const [id, secret] = this.key.split(":");
this.token = await self.syscall(
"jwt.jwt",
secret,
id,
"HS256",
"5m",
"/v3/admin/"
);
}
async listPosts(): Promise<Post[]> {
let result = await json(
`${this.url}/ghost/api/v3/admin/posts?order=published_at+DESC`,
{
headers: {
Authorization: `Ghost ${this.token}`,
},
}
);
return result.posts;
}
async listMarkdownPosts(): Promise<Post[]> {
let markdownPosts: Post[] = [];
for (let post of await this.listPosts()) {
let mobileDoc = JSON.parse(post.mobiledoc) as MobileDoc;
if (mobileDoc.cards.length > 0 && mobileDoc.cards[0][0] === "markdown") {
markdownPosts.push(post);
}
}
return markdownPosts;
}
publishPost(post: Partial<Post>): Promise<any> {
return this.publish("posts", post);
}
publishPage(post: Partial<Post>): Promise<any> {
return this.publish("pages", post);
}
async publish(what: "pages" | "posts", post: Partial<Post>): Promise<any> {
let oldPostQuery = await json(
`${this.url}/ghost/api/v3/admin/${what}/slug/${post.slug}`,
{
headers: {
Authorization: `Ghost ${this.token}`,
"Content-Type": "application/json",
},
}
);
if (!oldPostQuery[what]) {
// New!
if (!post.status) {
post.status = "draft";
}
let result = await json(`${this.url}/ghost/api/v3/admin/${what}`, {
method: "POST",
headers: {
Authorization: `Ghost ${this.token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
[what]: [post],
}),
});
return result[what][0];
} else {
let oldPost: Post = oldPostQuery[what][0];
post.updated_at = oldPost.updated_at;
let result = await json(
`${this.url}/ghost/api/v3/admin/${what}/${oldPost.id}`,
{
method: "PUT",
headers: {
Authorization: `Ghost ${this.token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
[what]: [post],
}),
}
);
return result[what][0];
}
}
}
function postToMarkdown(post: Post): string {
let text = mobileDocToMarkdown(post.mobiledoc);
return `# ${post.title}\n${text}`;
}
const postRegex = /#\s*([^\n]+)\n([^$]+)$/;
async function markdownToPost(text: string): Promise<Partial<Post>> {
let match = postRegex.exec(text);
if (match) {
let [, title, content] = match;
return {
title,
mobiledoc: markdownToMobileDoc(await cleanMarkdown(content)),
};
}
throw Error("Post should stat with a # header");
}
async function getConfig(): Promise<GhostConfig> {
let { text } = await readPage("ghost-config");
let parsedContent = await parseMarkdown(text);
let pageMeta = await extractMeta(parsedContent);
return pageMeta as GhostConfig;
}
export async function downloadAllPostsCommand() {
await invokeFunction("server", "downloadAllPosts");
}
export async function downloadAllPosts() {
let config = await getConfig();
let admin = new GhostAdmin(config.url, config.adminKey);
await admin.init();
let allPosts = await admin.listMarkdownPosts();
for (let post of allPosts) {
let text = mobileDocToMarkdown(post.mobiledoc);
text = `# ${post.title}\n${text}`;
await writePage(`${config.postPrefix}/${post.slug}`, text);
}
}
export async function publishCommand() {
await invokeFunction(
"server",
"publish",
await getCurrentPage(),
await getText()
);
}
export async function publish(name: string, text: string) {
let config = await getConfig();
let admin = new GhostAdmin(config.url, config.adminKey);
await admin.init();
let post = await markdownToPost(text);
if (name.startsWith(config.postPrefix)) {
post.slug = name.substring(config.postPrefix.length + 1);
await admin.publishPost(post);
console.log("Done!");
} else if (name.startsWith(config.pagePrefix)) {
post.slug = name.substring(config.pagePrefix.length + 1);
await admin.publishPage(post);
console.log("Done!");
} else {
console.error("Not in either the post or page prefix");
}
}
-20
View File
@@ -1,20 +0,0 @@
requiredPermissions:
- shell
functions:
snapshotCommand:
path: "./git.ts:snapshotCommand"
env: client
command:
name: "Git: Snapshot"
syncCommand:
path: "./git.ts:syncCommand"
env: client
command:
name: "Git: Sync"
commit:
path: "./git.ts:commit"
env: server
sync:
path: "./git.ts:sync"
env: server
-45
View File
@@ -1,45 +0,0 @@
import { run } from "plugos-syscall/shell";
import { flashNotification, prompt } from "plugos-silverbullet-syscall/editor";
import { invokeFunction } from "plugos-silverbullet-syscall/system";
export async function commit(message?: string) {
if (!message) {
message = "Snapshot";
}
console.log(
"Snapshotting the current space to git with commit message",
message
);
await run("git", ["add", "./*.md"]);
try {
await run("git", ["commit", "-a", "-m", message]);
} catch (e) {
// We can ignore, this happens when there's no changes to commit
}
console.log("Done!");
}
export async function snapshotCommand() {
let revName = await prompt(`Revision name:`);
if (!revName) {
revName = "Snapshot";
}
console.log("Revision name", revName);
await invokeFunction("server", "commit", revName);
}
export async function syncCommand() {
await flashNotification("Syncing with git");
await invokeFunction("server", "sync");
await flashNotification("Git sync complete!");
}
export async function sync() {
console.log("Going to sync with git");
await commit();
console.log("Then pulling from remote");
await run("git", ["pull"]);
console.log("And then pushing to remote");
await run("git", ["push"]);
console.log("Done!");
}
-14
View File
@@ -1,14 +0,0 @@
export async function replaceAsync(
str: string,
regex: RegExp,
asyncFn: (match: string, ...args: any[]) => Promise<string>
) {
const promises: Promise<string>[] = [];
str.replace(regex, (match: string, ...args: any[]): string => {
const promise = asyncFn(match, ...args);
promises.push(promise);
return "";
});
const data = await Promise.all(promises);
return str.replace(regex, () => data.shift()!);
}
-14
View File
@@ -1,14 +0,0 @@
functions:
toggle:
path: "./markdown.ts:togglePreview"
command:
name: "Toggle Markdown Preview"
key: Ctrl-p
mac: Cmd-p
preview:
path: "./preview.ts:updateMarkdownPreview"
env: client
events:
- plug:load
- editor:updated
- editor:pageSwitched
-18
View File
@@ -1,18 +0,0 @@
import { hideRhs } from "plugos-silverbullet-syscall/editor";
import { invokeFunction } from "plugos-silverbullet-syscall/system";
import * as clientStore from "plugos-silverbullet-syscall/clientStore";
export async function togglePreview() {
let currentValue = !!(await clientStore.get("enableMarkdownPreview"));
await clientStore.set("enableMarkdownPreview", !currentValue);
if (!currentValue) {
await invokeFunction("client", "preview");
// updateMarkdownPreview();
} else {
await hideMarkdownPreview();
}
}
async function hideMarkdownPreview() {
await hideRhs();
}
-12
View File
@@ -1,12 +0,0 @@
{
"name": "markdown",
"dependencies": {
"commonmark": "^0.30.0",
"markdown-it": "^12.3.2",
"markdown-it-task-lists": "^2.1.1"
},
"devDependencies": {
"@types/commonmark": "^0.27.5",
"@types/markdown-it": "^12.2.3"
}
}
-80
View File
@@ -1,80 +0,0 @@
import MarkdownIt from "markdown-it";
import { getText, showRhs } from "plugos-silverbullet-syscall/editor";
import * as clientStore from "plugos-silverbullet-syscall/clientStore";
import { cleanMarkdown } from "./util";
const css = `
<style>
body {
font-family: georgia,times,serif;
font-size: 14pt;
max-width: 800px;
margin-left: auto;
margin-right: auto;
padding-left: 20px;
padding-right: 20px;
}
table {
width: 100%;
border-spacing: 0;
}
thead tr {
background-color: #333;
color: #eee;
}
th, td {
padding: 8px;
}
tbody tr:nth-of-type(even) {
background-color: #f3f3f3;
}
a[href] {
text-decoration: none;
}
blockquote {
border-left: 1px solid #333;
margin-left: 2px;
padding-left: 10px;
}
hr {
margin: 1em 0 1em 0;
text-align: center;
border-color: #777;
border-width: 0;
border-style: dotted;
}
hr:after {
content: "···";
letter-spacing: 1em;
}
</style>
`;
var taskLists = require("markdown-it-task-lists");
const md = new MarkdownIt({
linkify: true,
html: false,
typographer: true,
}).use(taskLists);
export async function updateMarkdownPreview() {
if (!(await clientStore.get("enableMarkdownPreview"))) {
return;
}
let text = await getText();
let cleanMd = await cleanMarkdown(text);
await showRhs(
`<html><head>${css}</head><body>${md.render(cleanMd)}</body></html>`,
2
);
}
-33
View File
@@ -1,33 +0,0 @@
import { findNodeOfType, renderToText, replaceNodesMatching } from "../../common/tree";
import { parseMarkdown } from "plugos-silverbullet-syscall/markdown";
export function encodePageUrl(name: string): string {
return name.replaceAll(" ", "_");
}
export async function cleanMarkdown(text: string): Promise<string> {
let mdTree = await parseMarkdown(text);
replaceNodesMatching(mdTree, (n) => {
if (n.type === "WikiLink") {
const page = n.children![1].children![0].text!;
return {
// HACK
text: `[${page}](/${encodePageUrl(page)})`,
};
}
// Simply get rid of these
if (n.type === "CommentBlock" || n.type === "Comment") {
return null;
}
if (n.type === "FencedCode") {
let codeInfoNode = findNodeOfType(n, "CodeInfo");
if (!codeInfoNode) {
return;
}
if (codeInfoNode.children![0].text === "meta") {
return null;
}
}
});
return renderToText(mdTree);
}
-94
View File
@@ -1,94 +0,0 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@types/commonmark@^0.27.5":
version "0.27.5"
resolved "https://registry.yarnpkg.com/@types/commonmark/-/commonmark-0.27.5.tgz#008f2e8fb845c906146aa97510d66953d916aed2"
integrity sha512-vIqgmHyLsc8Or3EWLz6QkhI8/v61FNeH0yxRupA7VqSbA2eFMoHHJAhZSHudplAV89wqg1CKSmShE016ziRXuw==
"@types/linkify-it@*":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@types/linkify-it/-/linkify-it-3.0.2.tgz#fd2cd2edbaa7eaac7e7f3c1748b52a19143846c9"
integrity sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==
"@types/markdown-it@^12.2.3":
version "12.2.3"
resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-12.2.3.tgz#0d6f6e5e413f8daaa26522904597be3d6cd93b51"
integrity sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==
dependencies:
"@types/linkify-it" "*"
"@types/mdurl" "*"
"@types/mdurl@*":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@types/mdurl/-/mdurl-1.0.2.tgz#e2ce9d83a613bacf284c7be7d491945e39e1f8e9"
integrity sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==
argparse@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
commonmark@^0.30.0:
version "0.30.0"
resolved "https://registry.yarnpkg.com/commonmark/-/commonmark-0.30.0.tgz#38811dc7bbf0f59d277ae09054d4d73a332f2e45"
integrity sha512-j1yoUo4gxPND1JWV9xj5ELih0yMv1iCWDG6eEQIPLSWLxzCXiFoyS7kvB+WwU+tZMf4snwJMMtaubV0laFpiBA==
dependencies:
entities "~2.0"
mdurl "~1.0.1"
minimist ">=1.2.2"
string.prototype.repeat "^0.2.0"
entities@~2.0:
version "2.0.3"
resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.3.tgz#5c487e5742ab93c15abb5da22759b8590ec03b7f"
integrity sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==
entities@~2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5"
integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==
linkify-it@^3.0.1:
version "3.0.3"
resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-3.0.3.tgz#a98baf44ce45a550efb4d49c769d07524cc2fa2e"
integrity sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==
dependencies:
uc.micro "^1.0.1"
markdown-it-task-lists@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/markdown-it-task-lists/-/markdown-it-task-lists-2.1.1.tgz#f68f4d2ac2bad5a2c373ba93081a1a6848417088"
integrity sha512-TxFAc76Jnhb2OUu+n3yz9RMu4CwGfaT788br6HhEDlvWfdeJcLUsxk1Hgw2yJio0OXsxv7pyIPmvECY7bMbluA==
markdown-it@^12.3.2:
version "12.3.2"
resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-12.3.2.tgz#bf92ac92283fe983fe4de8ff8abfb5ad72cd0c90"
integrity sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==
dependencies:
argparse "^2.0.1"
entities "~2.1.0"
linkify-it "^3.0.1"
mdurl "^1.0.1"
uc.micro "^1.0.5"
mdurl@^1.0.1, mdurl@~1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"
integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=
minimist@>=1.2.2:
version "1.2.6"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
string.prototype.repeat@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/string.prototype.repeat/-/string.prototype.repeat-0.2.0.tgz#aba36de08dcee6a5a337d49b2ea1da1b28fc0ecf"
integrity sha1-q6Nt4I3O5qWjN9SbLqHaGyj8Ds8=
uc.micro@^1.0.1, uc.micro@^1.0.5:
version "1.0.6"
resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"
integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==
-5
View File
@@ -1,5 +0,0 @@
functions:
test:
path: mattermost.ts:savedPostsQueryProvider
events:
- query:mm-saved
-80
View File
@@ -1,80 +0,0 @@
import { Client4 } from "@mattermost/client";
import { applyQuery, QueryProviderEvent } from "../query/engine";
import { readPage } from "plugos-silverbullet-syscall/space";
import { parseMarkdown } from "plugos-silverbullet-syscall/markdown";
import { extractMeta } from "../query/data";
import { niceDate } from "../core/dates";
import { Post } from "@mattermost/types/lib/posts";
type AugmentedPost = Post & {
// Dates we can use to filter
createdAt: string;
updatedAt: string;
editedAt: string;
};
// https://community.mattermost.com/private-core/pl/rbp7a7jtr3f89nzsefo6ftqt3o
function mattermostDesktopUrlForPost(
url: string,
teamName: string,
postId: string
) {
return `${url.replace("https://", "mattermost://")}/${teamName}/pl/${postId}`;
}
type MattermostConfig = {
url: string;
token: string;
};
async function getConfig(): Promise<MattermostConfig> {
let { text } = await readPage("mattermost-config");
let parsedContent = await parseMarkdown(text);
let pageMeta = await extractMeta(parsedContent);
return pageMeta as MattermostConfig;
}
function augmentPost(post: AugmentedPost) {
if (post.create_at) {
post.createdAt = niceDate(new Date(post.create_at));
}
if (post.update_at) {
post.updatedAt = niceDate(new Date(post.update_at));
}
if (post.edit_at) {
post.editedAt = niceDate(new Date(post.edit_at));
}
}
export async function savedPostsQueryProvider({
query,
}: QueryProviderEvent): Promise<string> {
let config = await getConfig();
let client = new Client4();
client.setUrl(config.url);
client.setToken(config.token);
let me = await client.getMe();
let postCollection = await client.getFlaggedPosts(me.id);
let savedPosts: AugmentedPost[] = [];
for (let order of postCollection.order) {
let post = postCollection.posts[order];
augmentPost(post);
savedPosts.push(post);
}
let savedPostsMd = [];
savedPosts = applyQuery(query, savedPosts);
for (let savedPost of savedPosts) {
let channel = await client.getChannel(savedPost.channel_id);
let team = await client.getTeam(channel.team_id);
savedPostsMd.push(
`@${(await client.getUser(savedPost.user_id)).username} [${
savedPost.createdAt
}](${mattermostDesktopUrlForPost(
client.url,
team.name,
savedPost.id
)}):\n> ${savedPost.message.substring(0, 1000).replaceAll(/\n/g, "\n> ")}`
);
}
return savedPostsMd.join("\n\n");
}
-1086
View File
File diff suppressed because it is too large Load Diff
-18
View File
@@ -1,18 +0,0 @@
{
"name": "plugs",
"version": "1.0.0",
"scripts": {
"generate": "lezer-generator query/query.grammar -o query/parse-query.js"
},
"dependencies": {
"@jest/globals": "^27.5.1",
"@lezer/generator": "^0.15.4",
"@lezer/lr": "^0.15.8",
"@mattermost/client": "^6.7.0-0",
"@mattermost/types": "^6.7.0-0",
"@types/yaml": "^1.9.7",
"plugos-silverbullet-syscall": "file:../plugos-silverbullet-syscall",
"plugos-syscall": "file:../plugos-syscall",
"yaml": "^2.0.0"
}
}
-7
View File
@@ -1,7 +0,0 @@
import { getCursor, insertAtCursor, moveCursor } from "plugos-silverbullet-syscall/editor";
export async function insertQuery() {
let cursorPos = await getCursor();
await insertAtCursor(`<!-- #query -->\n\n<!-- #end -->`);
await moveCursor(cursorPos + 12);
}
-93
View File
@@ -1,93 +0,0 @@
// Index key space:
// data:page@pos
import { IndexTreeEvent } from "../../webapp/app_event";
import { batchSet, scanPrefixGlobal } from "plugos-silverbullet-syscall";
import { collectNodesOfType, findNodeOfType, ParseTree, replaceNodesMatching } from "../../common/tree";
import { parse as parseYaml, parseAllDocuments } from "yaml";
import type { QueryProviderEvent } from "./engine";
import { applyQuery } from "./engine";
import { jsonToMDTable, removeQueries } from "./util";
export async function indexData({ name, tree }: IndexTreeEvent) {
let dataObjects: { key: string; value: Object }[] = [];
removeQueries(tree);
collectNodesOfType(tree, "FencedCode").forEach((t) => {
let codeInfoNode = findNodeOfType(t, "CodeInfo");
if (!codeInfoNode) {
return;
}
if (codeInfoNode.children![0].text !== "data") {
return;
}
let codeTextNode = findNodeOfType(t, "CodeText");
if (!codeTextNode) {
// Honestly, this shouldn't happen
return;
}
let codeText = codeTextNode.children![0].text!;
try {
// We support multiple YAML documents in one block
for (let doc of parseAllDocuments(codeText)) {
if (!doc.contents) {
continue;
}
console.log(doc.contents.toJSON());
dataObjects.push({
key: `data:${name}@${t.from! + doc.range[0]}`,
value: doc.contents.toJSON(),
});
}
// console.log("Parsed data", parsedData);
} catch (e) {
console.error("Could not parse data", codeText, "error:", e);
return;
}
});
console.log("Found", dataObjects.length, "data objects");
await batchSet(name, dataObjects);
}
export function extractMeta(parseTree: ParseTree, remove = false): any {
let data = {};
replaceNodesMatching(parseTree, (t) => {
if (t.type !== "FencedCode") {
return;
}
let codeInfoNode = findNodeOfType(t, "CodeInfo");
if (!codeInfoNode) {
return;
}
if (codeInfoNode.children![0].text !== "meta") {
return;
}
let codeTextNode = findNodeOfType(t, "CodeText");
if (!codeTextNode) {
// Honestly, this shouldn't happen
return;
}
let codeText = codeTextNode.children![0].text!;
data = parseYaml(codeText);
return remove ? null : undefined;
});
return data;
}
export async function queryProvider({
query,
}: QueryProviderEvent): Promise<string> {
let allData: any[] = [];
for (let { key, page, value } of await scanPrefixGlobal("data:")) {
let [, pos] = key.split("@");
allData.push({
...value,
page: page,
pos: +pos,
});
}
let resultData = applyQuery(query, allData);
return jsonToMDTable(resultData);
}
-95
View File
@@ -1,95 +0,0 @@
import { expect, test } from "@jest/globals";
import { applyQuery, parseQuery } from "./engine";
test("Test parser", () => {
let parsedBasicQuery = parseQuery(`page`);
expect(parsedBasicQuery.table).toBe("page");
let parsedQuery1 = parseQuery(
`task where completed = false and dueDate <= "{{today}}" order by dueDate desc limit 5`
);
expect(parsedQuery1.table).toBe("task");
expect(parsedQuery1.orderBy).toBe("dueDate");
expect(parsedQuery1.orderDesc).toBe(true);
expect(parsedQuery1.limit).toBe(5);
expect(parsedQuery1.filter.length).toBe(2);
expect(parsedQuery1.filter[0]).toStrictEqual({
op: "=",
prop: "completed",
value: false,
});
expect(parsedQuery1.filter[1]).toStrictEqual({
op: "<=",
prop: "dueDate",
value: "{{today}}",
});
let parsedQuery2 = parseQuery(`page where name =~ /interview\\/.*/"`);
expect(parsedQuery2.table).toBe("page");
expect(parsedQuery2.filter.length).toBe(1);
expect(parsedQuery2.filter[0]).toStrictEqual({
op: "=~",
prop: "name",
value: "interview\\/.*",
});
let parsedQuery3 = parseQuery(`page where something != null`);
expect(parsedQuery3.table).toBe("page");
expect(parsedQuery3.filter.length).toBe(1);
expect(parsedQuery3.filter[0]).toStrictEqual({
op: "!=",
prop: "something",
value: null,
});
expect(parseQuery(`page select name`).select).toStrictEqual(["name"]);
expect(parseQuery(`page select name, age`).select).toStrictEqual([
"name",
"age",
]);
});
test("Test performing the queries", () => {
let data: any[] = [
{ name: "interview/My Interview", lastModified: 1 },
{ name: "interview/My Interview 2", lastModified: 2 },
{ name: "Pete", age: 38 },
{ name: "Angie", age: 28 },
];
expect(
applyQuery(parseQuery(`page where name =~ /interview\\/.*/`), data)
).toStrictEqual([
{ name: "interview/My Interview", lastModified: 1 },
{ name: "interview/My Interview 2", lastModified: 2 },
]);
expect(
applyQuery(
parseQuery(`page where name =~ /interview\\/.*/ order by lastModified`),
data
)
).toStrictEqual([
{ name: "interview/My Interview", lastModified: 1 },
{ name: "interview/My Interview 2", lastModified: 2 },
]);
expect(
applyQuery(
parseQuery(
`page where name =~ /interview\\/.*/ order by lastModified desc`
),
data
)
).toStrictEqual([
{ name: "interview/My Interview 2", lastModified: 2 },
{ name: "interview/My Interview", lastModified: 1 },
]);
expect(applyQuery(parseQuery(`page where age > 30`), data)).toStrictEqual([
{ name: "Pete", age: 38 },
]);
expect(
applyQuery(parseQuery(`page where age > 28 and age < 38`), data)
).toStrictEqual([]);
expect(
applyQuery(parseQuery(`page where age > 30 select name`), data)
).toStrictEqual([{ name: "Pete" }]);
});
-193
View File
@@ -1,193 +0,0 @@
import { collectNodesOfType, findNodeOfType, replaceNodesMatching } from "../../common/tree";
import { lezerToParseTree } from "../../common/parse_tree";
// @ts-ignore
import { parser } from "./parse-query";
export type QueryProviderEvent = {
query: ParsedQuery;
pageName: string;
};
export type Filter = {
op: string;
prop: string;
value: any;
};
export type ParsedQuery = {
table: string;
orderBy?: string;
orderDesc?: boolean;
limit?: number;
filter: Filter[];
select?: string[];
};
export function parseQuery(query: string): ParsedQuery {
let n = lezerToParseTree(query, parser.parse(query).topNode);
// Clean the tree a bit
replaceNodesMatching(n, (n) => {
if (!n.type) {
let trimmed = n.text!.trim();
if (!trimmed) {
return null;
}
n.text = trimmed;
}
});
// console.log("Parsed", JSON.stringify(n, null, 2));
let queryNode = n.children![0];
let parsedQuery: ParsedQuery = {
table: queryNode.children![0].children![0].text!,
filter: [],
};
let orderByNode = findNodeOfType(queryNode, "OrderClause");
if (orderByNode) {
let nameNode = findNodeOfType(orderByNode, "Name");
parsedQuery.orderBy = nameNode!.children![0].text!;
let orderNode = findNodeOfType(orderByNode, "Order");
parsedQuery.orderDesc = orderNode
? orderNode.children![0].text! === "desc"
: false;
}
let limitNode = findNodeOfType(queryNode, "LimitClause");
if (limitNode) {
let nameNode = findNodeOfType(limitNode, "Number");
parsedQuery.limit = +nameNode!.children![0].text!;
}
let filterNodes = collectNodesOfType(queryNode, "FilterExpr");
for (let filterNode of filterNodes) {
let val: any = undefined;
let valNode = filterNode.children![2].children![0];
switch (valNode.type) {
case "Number":
val = valNode.children![0].text!;
break;
case "Bool":
val = valNode.children![0].text! === "true";
break;
case "Null":
val = null;
break;
case "Name":
val = valNode.children![0].text!;
break;
case "Regex":
val = valNode.children![0].text!;
val = val.substring(1, val.length - 1);
break;
case "String":
val = valNode.children![0].text!;
val = val.substring(1, val.length - 1);
break;
}
let f: Filter = {
prop: filterNode.children![0].children![0].text!,
op: filterNode.children![1].text!,
value: val,
};
parsedQuery.filter.push(f);
}
let selectNode = findNodeOfType(queryNode, "SelectClause");
if (selectNode) {
console.log("Select node", JSON.stringify(selectNode));
parsedQuery.select = [];
collectNodesOfType(selectNode, "Name").forEach((t) => {
parsedQuery.select!.push(t.children![0].text!);
});
// let nameNode = findNodeOfType(selectNode, "Number");
// parsedQuery.limit = +nameNode!.children![0].text!;
}
// console.log(JSON.stringify(queryNode, null, 2));
return parsedQuery;
}
export function applyQuery<T>(parsedQuery: ParsedQuery, records: T[]): T[] {
let resultRecords: any[] = [];
if (parsedQuery.filter.length === 0) {
resultRecords = records.slice();
} else {
recordLoop: for (let record of records) {
const recordAny: any = record;
for (let { op, prop, value } of parsedQuery.filter) {
switch (op) {
case "=":
if (!(recordAny[prop] == value)) {
continue recordLoop;
}
break;
case "!=":
if (!(recordAny[prop] != value)) {
continue recordLoop;
}
break;
case "<":
if (!(recordAny[prop] < value)) {
continue recordLoop;
}
break;
case "<=":
if (!(recordAny[prop] <= value)) {
continue recordLoop;
}
break;
case ">":
if (!(recordAny[prop] > value)) {
continue recordLoop;
}
break;
case ">=":
if (!(recordAny[prop] >= value)) {
continue recordLoop;
}
break;
case "=~":
// TODO: Cache regexps somehow
if (!new RegExp(value).exec(recordAny[prop])) {
continue recordLoop;
}
break;
case "!=~":
if (new RegExp(value).exec(recordAny[prop])) {
continue recordLoop;
}
break;
}
}
resultRecords.push(recordAny);
}
}
// Now the sorting
if (parsedQuery.orderBy) {
resultRecords = resultRecords.sort((a: any, b: any) => {
const orderBy = parsedQuery.orderBy!;
const orderDesc = parsedQuery.orderDesc!;
if (a[orderBy] === b[orderBy]) {
return 0;
}
if (a[orderBy] < b[orderBy]) {
return orderDesc ? 1 : -1;
} else {
return orderDesc ? -1 : 1;
}
});
}
if (parsedQuery.limit) {
resultRecords = resultRecords.slice(0, parsedQuery.limit);
}
if (parsedQuery.select) {
resultRecords = resultRecords.map((rec) => {
let newRec: any = {};
for (let k of parsedQuery.select!) {
newRec[k] = rec[k];
}
return newRec;
});
}
return resultRecords;
}
-60
View File
@@ -1,60 +0,0 @@
import { flashNotification, getCurrentPage, getText, reloadPage, save } from "plugos-silverbullet-syscall/editor";
import { readPage, writePage } from "plugos-silverbullet-syscall/space";
import { invokeFunction } from "plugos-silverbullet-syscall/system";
import { parseQuery } from "./engine";
import { replaceTemplateVars } from "../core/template";
import { queryRegex, removeQueries } from "./util";
import { dispatch } from "plugos-syscall/event";
import { replaceAsync } from "../lib/util";
import { parseMarkdown } from "plugos-silverbullet-syscall/markdown";
export async function updateMaterializedQueriesCommand() {
const currentPage = await getCurrentPage();
await save();
await flashNotification("Updating materialized queries...");
await invokeFunction(
"server",
"updateMaterializedQueriesOnPage",
currentPage
);
await reloadPage();
await flashNotification("Updated materialized queries");
}
export async function whiteOutQueriesCommand() {
const text = await getText();
const parsed = await parseMarkdown(text);
console.log(removeQueries(parsed));
}
// Called from client, running on server
export async function updateMaterializedQueriesOnPage(pageName: string) {
let { text } = await readPage(pageName);
text = await replaceAsync(
text,
queryRegex,
async (fullMatch, startQuery, query, body, endQuery) => {
let parsedQuery = parseQuery(replaceTemplateVars(query, pageName));
console.log("Parsed query", parsedQuery);
// Let's dispatch an event and see what happens
let results = await dispatch(
`query:${parsedQuery.table}`,
{ query: parsedQuery, pageName: pageName },
10 * 1000
);
if (results.length === 0) {
return `${startQuery}\n${endQuery}`;
} else if (results.length === 1) {
return `${startQuery}\n${results[0]}\n${endQuery}`;
} else {
console.error("Too many query results", results);
return fullMatch;
}
}
);
// console.log("New text", text);
await writePage(pageName, text);
}
-17
View File
@@ -1,17 +0,0 @@
// This file was generated by lezer-generator. You probably shouldn't edit it.
import { LRParser } from "@lezer/lr";
export const parser = LRParser.deserialize({
version: 13,
states: "%WOVQPOOO[QQO'#C^QOQPOOOmQPO'#C`OrQQO'#CjOwQPO'#ClO|QPO'#CmOOQO'#Cn'#CnO!RQQO,58xO!dQPO'#CcO#OQQO'#CaOOQO'#Ca'#CaOOQO,58z,58zO#dQPO,59UOOQO,59W,59WO#iQQO'#DWOOQO,59X,59XOOQO-E6l-E6lO#}QQO,58}OmQPO,58|O$cQQO1G.pO$zQPO'#CoO%PQQO,59rOOQO'#Cg'#CgOOQO'#Ci'#CiOOQO'#Cd'#CdOOQO1G.i1G.iOOQO1G.h1G.hOOQO'#Ck'#CkOOQO7+$[7+$[OOQO,59Z,59ZOOQO-E6m-E6m",
stateData: "%e~OfOS~ORPO~OgROtSOxTOyUOdQX~ORXO~Ou]O~OX^O~OR_O~OgROtSOxTOyUOdQa~OhbOlbOmbOnbOobOpbOqbOrbO~OscOdTXgTXtTXxTXyTX~ORdO~O{eOdzXgzXtzXxzXyzX~OXiOYiO[iOigOjgOkhO~OvlOwlOd^ig^it^ix^iy^i~ORnO~O{eOdzagzatzaxzayza~O",
goto: "!y{PP|P!P!T!W!Z!aPP!dP!d!P!g!P!P!j!pPPPPPPPPPPPPPPPPPPPPPP!vRQOTVPWR[RRZRQYRRkcRjbRibRmdQWPRaWQf_RofR`U",
nodeNames: "⚠ Program Query Name WhereClause LogicalExpr AndExpr FilterExpr Value Number String Bool Regex Null OrderClause Order LimitClause SelectClause",
maxTerm: 43,
skippedNodes: [0],
repeatNodeCount: 2,
tokenData: "=_~RxX^#opq#oqr$drs$w|}%c}!O%h!P!Q%y!Q![&p!^!_&x!_!`'V!`!a'd!c!}%h#R#S%h#T#U'q#U#V*W#V#W%h#W#X+S#X#Y%h#Y#Z-O#Z#`%h#`#a/`#a#b%h#b#c1s#c#d3o#d#g%h#g#h6S#h#i9O#i#k%h#k#l:z#l#o%h#y#z#o$f$g#o#BY#BZ#o$IS$I_#o$Ip$Iq$w$Iq$Ir$w$I|$JO#o$JT$JU#o$KV$KW#o&FU&FV#o~#tYf~X^#opq#o#y#z#o$f$g#o#BY#BZ#o$IS$I_#o$I|$JO#o$JT$JU#o$KV$KW#o&FU&FV#o~$gP!_!`$j~$oPn~#r#s$r~$wOr~~$zUOr$wrs%^s$Ip$w$Ip$Iq%^$Iq$Ir%^$Ir~$w~%cOY~~%hO{~P%mSRP}!O%h!c!}%h#R#S%h#T#o%h~&OV[~OY%yZ]%y^!P%y!P!Q&e!Q#O%y#O#P&j#P~%y~&jO[~~&mPO~%y~&uPX~!Q![&p~&}Ph~!_!`'Q~'VOl~~'[Pm~#r#s'_~'dOq~~'iPp~!_!`'l~'qOo~R'vWRP}!O%h!c!}%h#R#S%h#T#b%h#b#c(`#c#g%h#g#h)[#h#o%hR(eURP}!O%h!c!}%h#R#S%h#T#W%h#W#X(w#X#o%hR)OSsQRP}!O%h!c!}%h#R#S%h#T#o%hR)aURP}!O%h!c!}%h#R#S%h#T#V%h#V#W)s#W#o%hR)zSwQRP}!O%h!c!}%h#R#S%h#T#o%hR*]URP}!O%h!c!}%h#R#S%h#T#m%h#m#n*o#n#o%hR*vSuQRP}!O%h!c!}%h#R#S%h#T#o%hR+XURP}!O%h!c!}%h#R#S%h#T#X%h#X#Y+k#Y#o%hR+pURP}!O%h!c!}%h#R#S%h#T#g%h#g#h,S#h#o%hR,XURP}!O%h!c!}%h#R#S%h#T#V%h#V#W,k#W#o%hR,rSvQRP}!O%h!c!}%h#R#S%h#T#o%hR-TTRP}!O%h!c!}%h#R#S%h#T#U-d#U#o%hR-iURP}!O%h!c!}%h#R#S%h#T#`%h#`#a-{#a#o%hR.QURP}!O%h!c!}%h#R#S%h#T#g%h#g#h.d#h#o%hR.iURP}!O%h!c!}%h#R#S%h#T#X%h#X#Y.{#Y#o%hR/SSjQRP}!O%h!c!}%h#R#S%h#T#o%hR/eURP}!O%h!c!}%h#R#S%h#T#]%h#]#^/w#^#o%hR/|URP}!O%h!c!}%h#R#S%h#T#a%h#a#b0`#b#o%hR0eURP}!O%h!c!}%h#R#S%h#T#]%h#]#^0w#^#o%hR0|URP}!O%h!c!}%h#R#S%h#T#h%h#h#i1`#i#o%hR1gSxQRP}!O%h!c!}%h#R#S%h#T#o%hR1xURP}!O%h!c!}%h#R#S%h#T#i%h#i#j2[#j#o%hR2aURP}!O%h!c!}%h#R#S%h#T#`%h#`#a2s#a#o%hR2xURP}!O%h!c!}%h#R#S%h#T#`%h#`#a3[#a#o%hR3cSkQRP}!O%h!c!}%h#R#S%h#T#o%hR3tURP}!O%h!c!}%h#R#S%h#T#f%h#f#g4W#g#o%hR4]URP}!O%h!c!}%h#R#S%h#T#W%h#W#X4o#X#o%hR4tURP}!O%h!c!}%h#R#S%h#T#X%h#X#Y5W#Y#o%hR5]URP}!O%h!c!}%h#R#S%h#T#f%h#f#g5o#g#o%hR5vStQRP}!O%h!c!}%h#R#S%h#T#o%hR6XURP}!O%h!c!}%h#R#S%h#T#X%h#X#Y6k#Y#o%hR6pURP}!O%h!c!}%h#R#S%h#T#`%h#`#a7S#a#o%hR7XURP}!O%h!c!}%h#R#S%h#T#X%h#X#Y7k#Y#o%hR7pURP}!O%h!c!}%h#R#S%h#T#V%h#V#W8S#W#o%hR8XURP}!O%h!c!}%h#R#S%h#T#h%h#h#i8k#i#o%hR8rSyQRP}!O%h!c!}%h#R#S%h#T#o%hR9TURP}!O%h!c!}%h#R#S%h#T#f%h#f#g9g#g#o%hR9lURP}!O%h!c!}%h#R#S%h#T#i%h#i#j:O#j#o%hR:TURP}!O%h!c!}%h#R#S%h#T#X%h#X#Y:g#Y#o%hR:nSiQRP}!O%h!c!}%h#R#S%h#T#o%hR;PURP}!O%h!c!}%h#R#S%h#T#[%h#[#];c#]#o%hR;hURP}!O%h!c!}%h#R#S%h#T#X%h#X#Y;z#Y#o%hR<PURP}!O%h!c!}%h#R#S%h#T#f%h#f#g<c#g#o%hR<hURP}!O%h!c!}%h#R#S%h#T#X%h#X#Y<z#Y#o%hR=RSgQRP}!O%h!c!}%h#R#S%h#T#o%h",
tokenizers: [0, 1],
topRules: {"Program":[0,1]},
tokenPrec: 0
})
-19
View File
@@ -1,19 +0,0 @@
// This file was generated by lezer-generator. You probably shouldn't edit it.
export const
Program = 1,
Query = 2,
Name = 3,
WhereClause = 4,
LogicalExpr = 5,
AndExpr = 6,
FilterExpr = 7,
Value = 8,
Number = 9,
String = 10,
Bool = 11,
Regex = 12,
Null = 13,
OrderClause = 14,
Order = 15,
LimitClause = 16,
SelectClause = 17
-58
View File
@@ -1,58 +0,0 @@
@precedence { logic @left }
@top Program { Query }
Query {
Name ( WhereClause | OrderClause | LimitClause | SelectClause )*
}
commaSep<content> { content ("," content)* }
WhereClause { "where" LogicalExpr }
OrderClause { "order" "by" Name Order? }
LimitClause { "limit" Number }
SelectClause { "select" commaSep<Name> }
Order {
"desc" | "asc"
}
Value { Number | String | Bool | Regex | Null }
LogicalExpr { AndExpr | FilterExpr }
AndExpr { FilterExpr !logic "and" FilterExpr }
FilterExpr {
Name "<" Value
| Name "<=" Value
| Name "=" Value
| Name "!=" Value
| Name ">=" Value
| Name ">" Value
| Name "=~" Value
| Name "!=~" Value
}
@skip { space }
Bool {
"true" | "false"
}
Null {
"null"
}
@tokens {
space { std.whitespace+ }
Name { (std.asciiLetter | "-" | "_")+ }
String {
("\"" | "“" | "”") ![\"”“]* ("\"" | "“" | "”")
}
Regex { "/" ( ![/\\\n\r] | "\\" _ )* "/"? }
Number { std.digit+ }
}
-24
View File
@@ -1,24 +0,0 @@
functions:
updateMaterializedQueriesOnPage:
path: ./materialized_queries.ts:updateMaterializedQueriesOnPage
updateMaterializedQueriesCommand:
path: ./materialized_queries.ts:updateMaterializedQueriesCommand
command:
name: "Materialized Queries: Update"
key: "Alt-q"
whiteOutQueriesCommand:
path: ./materialized_queries.ts:whiteOutQueriesCommand
command:
name: "Debug: Whiteout Queries"
indexData:
path: ./data.ts:indexData
events:
- page:index
dataQueryProvider:
path: ./data.ts:queryProvider
events:
- query:data
insertQueryCommand:
path: ./command.ts:insertQuery
slashCommand:
name: query
-70
View File
@@ -1,70 +0,0 @@
import { addParentPointers, collectNodesMatching, ParseTree, renderToText } from "../../common/tree";
export const queryRegex =
/(<!--\s*#query\s+(.+?)-->)(.+?)(<!--\s*#end\s*-->)/gs;
export const queryStartRegex = /<!--\s*#query\s+(.+?)-->/s;
export const queryEndRegex = /<!--\s*#end\s*-->/s;
// export function whiteOutQueries(text: string): string {
// return text.replaceAll(queryRegex, (match) =>
// new Array(match.length + 1).join(" ")
// );
// }
export function removeQueries(pt: ParseTree) {
addParentPointers(pt);
collectNodesMatching(pt, (t) => {
if (t.type !== "CommentBlock") {
return false;
}
let text = t.children![0].text!;
if (!queryStartRegex.exec(text)) {
return false;
}
let parentChildren = t.parent!.children!;
let index = parentChildren.indexOf(t);
let nodesToReplace: ParseTree[] = [];
for (let i = index + 1; i < parentChildren.length; i++) {
let n = parentChildren[i];
if (n.type === "CommentBlock") {
let text = n.children![0].text!;
if (queryEndRegex.exec(text)) {
break;
}
}
nodesToReplace.push(n);
}
let renderedText = nodesToReplace.map(renderToText).join("");
parentChildren.splice(index + 1, nodesToReplace.length, {
text: new Array(renderedText.length + 1).join(" "),
});
return true;
});
}
// Nicely format an array of JSON objects as a Markdown table
export function jsonToMDTable(
jsonArray: any[],
valueTransformer?: (k: string, v: any) => string | undefined
): string {
let headers = new Set<string>();
for (let entry of jsonArray) {
for (let k of Object.keys(entry)) {
headers.add(k);
}
}
let headerList = [...headers];
let lines = [];
lines.push("|" + headerList.join("|") + "|");
lines.push("|" + headerList.map((title) => "----").join("|") + "|");
for (const val of jsonArray) {
let el = [];
for (let prop of headerList) {
el.push(valueTransformer ? valueTransformer(prop, val[prop]) : val[prop]);
}
lines.push("|" + el.join("|") + "|");
}
return lines.join("\n");
}
-203
View File
@@ -1,203 +0,0 @@
import type { ClickEvent, IndexTreeEvent } from "../../webapp/app_event";
import { batchSet, scanPrefixGlobal } from "plugos-silverbullet-syscall/index";
import { readPage, writePage } from "plugos-silverbullet-syscall/space";
import { parseMarkdown } from "plugos-silverbullet-syscall/markdown";
import { dispatch, filterBox, getCursor, getText } from "plugos-silverbullet-syscall/editor";
import {
addParentPointers,
collectNodesMatching,
collectNodesOfType,
findNodeOfType,
nodeAtPos,
ParseTree,
renderToText
} from "../../common/tree";
import { removeQueries } from "../query/util";
import { applyQuery, QueryProviderEvent } from "../query/engine";
import { niceDate } from "../core/dates";
export type Task = {
name: string;
done: boolean;
deadline?: string;
nested?: string;
// Not saved in DB, just added when pulled out (from key)
pos?: number;
page?: string;
};
function getDeadline(deadlineNode: ParseTree): string {
return deadlineNode.children![0].text!.replace(/📅\s*/, "");
}
export async function indexTasks({ name, tree }: IndexTreeEvent) {
// console.log("Indexing tasks");
let tasks: { key: string; value: Task }[] = [];
removeQueries(tree);
collectNodesOfType(tree, "Task").forEach((n) => {
let task = n.children!.slice(1).map(renderToText).join("").trim();
let complete = n.children![0].children![0].text! !== "[ ]";
let value: Task = {
name: task,
done: complete,
};
let deadlineNode = findNodeOfType(n, "DeadlineDate");
if (deadlineNode) {
value.deadline = getDeadline(deadlineNode);
}
let taskIndex = n.parent!.children!.indexOf(n);
let nestedItems = n.parent!.children!.slice(taskIndex + 1);
if (nestedItems.length > 0) {
value.nested = nestedItems.map(renderToText).join("").trim();
}
tasks.push({
key: `task:${n.from}`,
value,
});
// console.log("Task", value);
});
console.log("Found", tasks.length, "task(s)");
await batchSet(name, tasks);
}
export async function taskToggle(event: ClickEvent) {
return taskToggleAtPos(event.pos);
}
async function toggleTaskMarker(node: ParseTree, moveToPos: number) {
let changeTo = "[x]";
if (node.children![0].text === "[x]" || node.children![0].text === "[X]") {
changeTo = "[ ]";
}
await dispatch({
changes: {
from: node.from,
to: node.to,
insert: changeTo,
},
selection: {
anchor: moveToPos,
},
});
let parentWikiLinks = collectNodesMatching(
node.parent!,
(n) => n.type === "WikiLinkPage"
);
for (let wikiLink of parentWikiLinks) {
let ref = wikiLink.children![0].text!;
if (ref.includes("@")) {
let [page, pos] = ref.split("@");
let text = (await readPage(page)).text;
let referenceMdTree = await parseMarkdown(text);
// Adding +1 to immediately hit the task marker
let taskMarkerNode = nodeAtPos(referenceMdTree, +pos + 1);
if (!taskMarkerNode || taskMarkerNode.type !== "TaskMarker") {
console.error(
"Reference not a task marker, out of date?",
taskMarkerNode
);
return;
}
taskMarkerNode.children![0].text = changeTo;
text = renderToText(referenceMdTree);
console.log("Updated reference paged text", text);
await writePage(page, text);
}
}
}
export async function taskToggleAtPos(pos: number) {
let text = await getText();
let mdTree = await parseMarkdown(text);
addParentPointers(mdTree);
let node = nodeAtPos(mdTree, pos);
if (node && node.type === "TaskMarker") {
await toggleTaskMarker(node, pos);
}
}
export async function taskToggleCommand() {
let text = await getText();
let pos = await getCursor();
let tree = await parseMarkdown(text);
addParentPointers(tree);
let node = nodeAtPos(tree, pos);
// We kwow node.type === Task (due to the task context)
let taskMarker = findNodeOfType(node!, "TaskMarker");
await toggleTaskMarker(taskMarker!, pos);
}
export async function postponeCommand() {
let text = await getText();
let pos = await getCursor();
let tree = await parseMarkdown(text);
addParentPointers(tree);
let node = nodeAtPos(tree, pos)!;
// We kwow node.type === DeadlineDate (due to the task context)
let date = getDeadline(node);
let option = await filterBox(
"Postpone for...",
[
{ name: "a day", orderId: 1 },
{ name: "a week", orderId: 2 },
{ name: "following Monday", orderId: 3 },
],
"Select the desired time span to delay this task"
);
if (!option) {
return;
}
let d = new Date(date);
switch (option.name) {
case "a day":
d.setDate(d.getDate() + 1);
break;
case "a week":
d.setDate(d.getDate() + 7);
break;
case "following Monday":
d.setDate(d.getDate() + ((7 - d.getDay() + 1) % 7 || 7));
break;
}
await dispatch({
changes: {
from: node.from,
to: node.to,
insert: `📅 ${niceDate(d)}`,
},
selection: {
anchor: pos,
},
});
// await toggleTaskMarker(taskMarker!, pos);
}
export async function queryProvider({
query,
}: QueryProviderEvent): Promise<string> {
let allTasks: Task[] = [];
for (let { key, page, value } of await scanPrefixGlobal("task:")) {
let [, pos] = key.split(":");
allTasks.push({
...value,
page: page,
pos: pos,
});
}
let markdownTasks = applyQuery(query, allTasks).map(
(t) =>
`* [${t.done ? "x" : " "}] [[${t.page}@${t.pos}]] ${t.name}` +
(t.nested ? "\n " + t.nested : "")
);
return markdownTasks.join("\n");
}
-46
View File
@@ -1,46 +0,0 @@
syntax:
DeadlineDate:
firstCharacters:
- "📅"
regex: "📅\\s*\\d{4}\\-\\d{2}\\-\\d{2}"
styles:
backgroundColor: "rgba(22,22,22,0.07)"
CompletedDate:
firstCharacters:
- "✅"
regex: "✅\\s*\\d{4}\\-\\d{2}\\-\\d{2}"
styles:
backgroundColor: "rgba(22,22,22,0.07)"
RepeatInterval:
firstCharacters:
- "🔁"
regex: "🔁\\s*every\\s+\\w+"
styles:
backgroundColor: "rgba(22,22,22,0.07)"
functions:
indexTasks:
path: "./task.ts:indexTasks"
events:
- page:index
taskToggle:
path: "./task.ts:taskToggle"
events:
- page:click
itemQueryProvider:
path: ./task.ts:queryProvider
events:
- query:task
taskToggleCommand:
path: ./task.ts:taskToggleCommand
command:
name: "Task: Toggle"
key: Alt-t
contexts:
- Task
taskPostponeCommand:
path: ./task.ts:postponeCommand
command:
name: "Task: Postpone by 1 day"
key: Alt-+
contexts:
- DeadlineDate
-468
View File
@@ -1,468 +0,0 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@babel/code-frame@^7.12.13":
version "7.16.7"
resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz"
integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==
dependencies:
"@babel/highlight" "^7.16.7"
"@babel/helper-validator-identifier@^7.16.7":
version "7.16.7"
resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz"
integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==
"@babel/highlight@^7.16.7":
version "7.16.10"
resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz"
integrity sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==
dependencies:
"@babel/helper-validator-identifier" "^7.16.7"
chalk "^2.0.0"
js-tokens "^4.0.0"
"@jest/environment@^27.5.1":
version "27.5.1"
resolved "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz"
integrity sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==
dependencies:
"@jest/fake-timers" "^27.5.1"
"@jest/types" "^27.5.1"
"@types/node" "*"
jest-mock "^27.5.1"
"@jest/fake-timers@^27.5.1":
version "27.5.1"
resolved "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz"
integrity sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==
dependencies:
"@jest/types" "^27.5.1"
"@sinonjs/fake-timers" "^8.0.1"
"@types/node" "*"
jest-message-util "^27.5.1"
jest-mock "^27.5.1"
jest-util "^27.5.1"
"@jest/globals@^27.5.1":
version "27.5.1"
resolved "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz"
integrity sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==
dependencies:
"@jest/environment" "^27.5.1"
"@jest/types" "^27.5.1"
expect "^27.5.1"
"@jest/types@^27.5.1":
version "27.5.1"
resolved "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz"
integrity sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==
dependencies:
"@types/istanbul-lib-coverage" "^2.0.0"
"@types/istanbul-reports" "^3.0.0"
"@types/node" "*"
"@types/yargs" "^16.0.0"
chalk "^4.0.0"
"@lezer/common@^0.15.0":
version "0.15.12"
resolved "https://registry.npmjs.org/@lezer/common/-/common-0.15.12.tgz"
integrity sha512-edfwCxNLnzq5pBA/yaIhwJ3U3Kz8VAUOTRg0hhxaizaI1N+qxV7EXDv/kLCkLeq2RzSFvxexlaj5Mzfn2kY0Ig==
"@lezer/generator@^0.15.4":
version "0.15.4"
resolved "https://registry.npmjs.org/@lezer/generator/-/generator-0.15.4.tgz"
integrity sha512-9bBwU2TzKMBQ6OCEDevuMNWGOBKlkq5YIGEhjrz9pb3MLb+oYYR4dVFZ7ehwLcDoSecsSA7PdlAy0thJO5pt2w==
dependencies:
"@lezer/common" "^0.15.0"
"@lezer/lr" "^0.15.0"
"@lezer/lr@^0.15.0", "@lezer/lr@^0.15.8":
version "0.15.8"
resolved "https://registry.npmjs.org/@lezer/lr/-/lr-0.15.8.tgz"
integrity sha512-bM6oE6VQZ6hIFxDNKk8bKPa14hqFrV07J/vHGOeiAbJReIaQXmkVb6xQu4MR+JBTLa5arGRyAAjJe1qaQt3Uvg==
dependencies:
"@lezer/common" "^0.15.0"
"@mattermost/client@^6.7.0-0":
version "6.7.0-0"
resolved "https://registry.yarnpkg.com/@mattermost/client/-/client-6.7.0-0.tgz#85d79d364f1f37f3eab1b77ae2e823a51254f89e"
integrity sha512-XIKEWYmadlCh3Bb3JwMi65raKe7jhBLPJ5DPPu6FPMZc4FoM1YoHdPfrkuT1c2KgcTX5HNbkGd1cXV6cLXxHUw==
dependencies:
form-data "^4.0.0"
"@mattermost/types@^6.7.0-0":
version "6.7.0-0"
resolved "https://registry.npmjs.org/@mattermost/types/-/types-6.7.0-0.tgz"
integrity sha512-mT8wJwWEp20KPo9D12y7bW7EdUHO7VhUHxr3gH8nPGapWooGcl0Ra0H3u1iCjPpqPWvp7LiodcneU0IysunYKQ==
"@sinonjs/commons@^1.7.0":
version "1.8.3"
resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz"
integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==
dependencies:
type-detect "4.0.8"
"@sinonjs/fake-timers@^8.0.1":
version "8.1.0"
resolved "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz"
integrity sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==
dependencies:
"@sinonjs/commons" "^1.7.0"
"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0":
version "2.0.4"
resolved "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz"
integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==
"@types/istanbul-lib-report@*":
version "3.0.0"
resolved "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz"
integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==
dependencies:
"@types/istanbul-lib-coverage" "*"
"@types/istanbul-reports@^3.0.0":
version "3.0.1"
resolved "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz"
integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==
dependencies:
"@types/istanbul-lib-report" "*"
"@types/node@*":
version "17.0.23"
resolved "https://registry.npmjs.org/@types/node/-/node-17.0.23.tgz"
integrity sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw==
"@types/stack-utils@^2.0.0":
version "2.0.1"
resolved "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz"
integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==
"@types/yaml@^1.9.7":
version "1.9.7"
resolved "https://registry.npmjs.org/@types/yaml/-/yaml-1.9.7.tgz"
integrity sha512-8WMXRDD1D+wCohjfslHDgICd2JtMATZU8CkhH8LVJqcJs6dyYj5TGptzP8wApbmEullGBSsCEzzap73DQ1HJaA==
dependencies:
yaml "*"
"@types/yargs-parser@*":
version "21.0.0"
resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz"
integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==
"@types/yargs@^16.0.0":
version "16.0.4"
resolved "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz"
integrity sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==
dependencies:
"@types/yargs-parser" "*"
ansi-regex@^5.0.1:
version "5.0.1"
resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz"
integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
ansi-styles@^3.2.1:
version "3.2.1"
resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz"
integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
dependencies:
color-convert "^1.9.0"
ansi-styles@^4.1.0:
version "4.3.0"
resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz"
integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
dependencies:
color-convert "^2.0.1"
ansi-styles@^5.0.0:
version "5.2.0"
resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz"
integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
braces@^3.0.2:
version "3.0.2"
resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz"
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
dependencies:
fill-range "^7.0.1"
chalk@^2.0.0:
version "2.4.2"
resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz"
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
dependencies:
ansi-styles "^3.2.1"
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
chalk@^4.0.0:
version "4.1.2"
resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz"
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
dependencies:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
ci-info@^3.2.0:
version "3.3.0"
resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.3.0.tgz"
integrity sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==
color-convert@^1.9.0:
version "1.9.3"
resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz"
integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
dependencies:
color-name "1.1.3"
color-convert@^2.0.1:
version "2.0.1"
resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz"
integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
dependencies:
color-name "~1.1.4"
color-name@1.1.3:
version "1.1.3"
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz"
integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
color-name@~1.1.4:
version "1.1.4"
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
combined-stream@^1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
dependencies:
delayed-stream "~1.0.0"
delayed-stream@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
diff-sequences@^27.5.1:
version "27.5.1"
resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz"
integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==
escape-string-regexp@^1.0.5:
version "1.0.5"
resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz"
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
escape-string-regexp@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz"
integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==
expect@^27.5.1:
version "27.5.1"
resolved "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz"
integrity sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==
dependencies:
"@jest/types" "^27.5.1"
jest-get-type "^27.5.1"
jest-matcher-utils "^27.5.1"
jest-message-util "^27.5.1"
fill-range@^7.0.1:
version "7.0.1"
resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz"
integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
dependencies:
to-regex-range "^5.0.1"
form-data@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.8"
mime-types "^2.1.12"
graceful-fs@^4.2.9:
version "4.2.9"
resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz"
integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==
has-flag@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz"
integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
has-flag@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz"
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
is-number@^7.0.0:
version "7.0.0"
resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz"
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
jest-diff@^27.5.1:
version "27.5.1"
resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz"
integrity sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==
dependencies:
chalk "^4.0.0"
diff-sequences "^27.5.1"
jest-get-type "^27.5.1"
pretty-format "^27.5.1"
jest-get-type@^27.5.1:
version "27.5.1"
resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz"
integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==
jest-matcher-utils@^27.5.1:
version "27.5.1"
resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz"
integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==
dependencies:
chalk "^4.0.0"
jest-diff "^27.5.1"
jest-get-type "^27.5.1"
pretty-format "^27.5.1"
jest-message-util@^27.5.1:
version "27.5.1"
resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz"
integrity sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==
dependencies:
"@babel/code-frame" "^7.12.13"
"@jest/types" "^27.5.1"
"@types/stack-utils" "^2.0.0"
chalk "^4.0.0"
graceful-fs "^4.2.9"
micromatch "^4.0.4"
pretty-format "^27.5.1"
slash "^3.0.0"
stack-utils "^2.0.3"
jest-mock@^27.5.1:
version "27.5.1"
resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz"
integrity sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==
dependencies:
"@jest/types" "^27.5.1"
"@types/node" "*"
jest-util@^27.5.1:
version "27.5.1"
resolved "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz"
integrity sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==
dependencies:
"@jest/types" "^27.5.1"
"@types/node" "*"
chalk "^4.0.0"
ci-info "^3.2.0"
graceful-fs "^4.2.9"
picomatch "^2.2.3"
js-tokens@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
micromatch@^4.0.4:
version "4.0.5"
resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz"
integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
dependencies:
braces "^3.0.2"
picomatch "^2.3.1"
mime-db@1.52.0:
version "1.52.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
mime-types@^2.1.12:
version "2.1.35"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
dependencies:
mime-db "1.52.0"
picomatch@^2.2.3, picomatch@^2.3.1:
version "2.3.1"
resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
"plugos-silverbullet-syscall@file:../plugos-silverbullet-syscall":
version "1.0.0"
"plugos-syscall@file:../plugos-syscall":
version "1.0.0"
pretty-format@^27.5.1:
version "27.5.1"
resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz"
integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==
dependencies:
ansi-regex "^5.0.1"
ansi-styles "^5.0.0"
react-is "^17.0.1"
react-is@^17.0.1:
version "17.0.2"
resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz"
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
slash@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz"
integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
stack-utils@^2.0.3:
version "2.0.5"
resolved "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz"
integrity sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==
dependencies:
escape-string-regexp "^2.0.0"
supports-color@^5.3.0:
version "5.5.0"
resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz"
integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
dependencies:
has-flag "^3.0.0"
supports-color@^7.1.0:
version "7.2.0"
resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz"
integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
dependencies:
has-flag "^4.0.0"
to-regex-range@^5.0.1:
version "5.0.1"
resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz"
integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
dependencies:
is-number "^7.0.0"
type-detect@4.0.8:
version "4.0.8"
resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz"
integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==
yaml@*, yaml@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/yaml/-/yaml-2.0.0.tgz"
integrity sha512-JbfdlHKGP2Ik9IHylzWlGd4pPK++EU46/IxMykphS2ZKw7a7h+dHNmcXObLgpRDriBY+rpWslldikckX8oruWQ==