@azure-utils/storybooks
Version:
Utils to upload and manage Storybooks via Azure Functions and storage.
1 lines • 62.1 kB
Source Map (JSON)
{"version":3,"file":"store-BL4RNiEp.mjs","names":["isVoidElement","end","length","start","attributesToString","contentsToString","escape","contentToString","contents","Fragment","contentsToString","contentToString","renderToStream","filepath: string","projectId: string","sha: string","labelSlug?: string","labelSlug: string","gitHubRepo: string","store","request?: HttpRequest","html: JSX.Element","error: unknown","context: InvocationContext","init?: ResponseInit | number","location: string","init: ResponseInit | number","throwError?: boolean","options: RouterHandlerOptions","handler: HttpHandler","permissions: Permission[]","storeValue: Store"],"sources":["../../../node_modules/@kitajs/html/index.js","../../../node_modules/@kitajs/html/suspense.js","../src/utils/url-builder.ts","../../../node_modules/@kitajs/html/jsx-runtime.js","../src/components/layout.tsx","../src/components/error-message.tsx","../src/utils/request-utils.ts","../src/utils/response-utils.tsx","../src/utils/store.ts"],"sourcesContent":["/// <reference path=\"./jsx.d.ts\" />\n/// <reference types=\"./suspense.d.ts\" />\n/// <reference types=\"./error-boundary.d.ts\" />\n\nconst ESCAPED_REGEX = /[<\"'&]/;\nconst CAMEL_REGEX = /[a-z][A-Z]/;\n\n/** @type {import('.').isUpper} */\nfunction isUpper(input, index) {\n const code = input.charCodeAt(index);\n return code >= 65 /* A */ && code <= 90; /* Z */\n}\n\n/** @type {import('.').toKebabCase} */\nfunction toKebabCase(camel) {\n // This is a optimization to avoid the whole conversion process when the\n // string does not contain any uppercase characters.\n if (!CAMEL_REGEX.test(camel)) {\n return camel;\n }\n\n const length = camel.length;\n\n let start = 0,\n end = 0,\n kebab = '',\n prev = true,\n curr = isUpper(camel, 0),\n next;\n\n for (; end < length; end++) {\n next = isUpper(camel, end + 1);\n\n // detects the start of a new camel case word and avoid lowercasing abbreviations.\n if (!prev && curr && !next) {\n // @ts-expect-error - this indexing is safe.\n kebab += camel.slice(start, end) + '-' + camel[end].toLowerCase();\n start = end + 1;\n }\n\n prev = curr;\n curr = next;\n }\n\n // Appends the remaining string.\n kebab += camel.slice(start, end);\n\n return kebab;\n}\n\n/** @type {import('.').escape} */\nfunction escape(strings, ...values) {\n const stringsLength = strings.length,\n valuesLength = values.length;\n\n let index = 0,\n result = '';\n\n for (; index < stringsLength; index++) {\n result += strings[index];\n\n if (index < valuesLength) {\n result += values[index];\n }\n }\n\n // Escape the entire string at once.\n // This is faster than escaping each piece individually.\n return escapeHtml(result);\n}\n\n/** @type {import('.').escapeHtml} */\nlet escapeHtml = function (value) {\n if (typeof value !== 'string') {\n value = value.toString();\n }\n\n // This is a optimization to avoid the whole conversion process when the\n // string does not contain any uppercase characters.\n if (!ESCAPED_REGEX.test(value)) {\n return value;\n }\n\n const length = value.length;\n\n let escaped = '',\n start = 0,\n end = 0;\n\n // Escapes double quotes to be used inside attributes\n // Faster than using regex\n // https://jsperf.app/kakihu\n for (; end < length; end++) {\n // https://wonko.com/post/html-escaping\n switch (value[end]) {\n case '&':\n escaped += value.slice(start, end) + '&';\n start = end + 1;\n continue;\n // We don't need to escape > because it is only used to close tags.\n // https://stackoverflow.com/a/9189067\n case '<':\n escaped += value.slice(start, end) + '<';\n start = end + 1;\n continue;\n case '\"':\n escaped += value.slice(start, end) + '"';\n start = end + 1;\n continue;\n case \"'\":\n escaped += value.slice(start, end) + ''';\n start = end + 1;\n continue;\n }\n }\n\n // Appends the remaining string.\n escaped += value.slice(start, end);\n\n return escaped;\n};\n\n/* c8 ignore next 2 */\n// @ts-ignore - bun runtime have its own escapeHTML function.\nif (typeof Bun !== 'undefined') escapeHtml = Bun.escapeHTML;\n\n/** @type {import('.').isVoidElement} */\nfunction isVoidElement(tag) {\n // Ordered by most common to least common.\n return (\n tag === 'meta' ||\n tag === 'link' ||\n tag === 'img' ||\n tag === 'br' ||\n tag === 'input' ||\n tag === 'hr' ||\n tag === 'area' ||\n tag === 'base' ||\n tag === 'col' ||\n tag === 'command' ||\n tag === 'embed' ||\n tag === 'keygen' ||\n tag === 'param' ||\n tag === 'source' ||\n tag === 'track' ||\n tag === 'wbr'\n );\n}\n\n/** @type {import('.').styleToString} */\nfunction styleToString(style) {\n // Faster escaping process that only looks for the \" character.\n // As we use the \" character to wrap the style string, we need to escape it.\n if (typeof style === 'string') {\n let end = style.indexOf('\"');\n\n // This is a optimization to avoid having to look twice for the \" character.\n // And make the loop already start in the middle\n if (end === -1) {\n return style;\n }\n\n const length = style.length;\n\n let escaped = '',\n start = 0;\n\n // Escapes double quotes to be used inside attributes\n // Faster than using regex\n // https://jsperf.app/kakihu\n for (; end < length; end++) {\n if (style[end] === '\"') {\n escaped += style.slice(start, end) + '"';\n start = end + 1;\n }\n }\n\n // Appends the remaining string.\n escaped += style.slice(start, end);\n\n return escaped;\n }\n\n const keys = Object.keys(style),\n length = keys.length;\n\n let key,\n value,\n end,\n start,\n index = 0,\n result = '';\n\n for (; index < length; index++) {\n key = keys[index];\n // @ts-expect-error - this indexing is safe.\n value = style[key];\n\n if (value === null || value === undefined) {\n continue;\n }\n\n // @ts-expect-error - this indexing is safe.\n result += toKebabCase(key) + ':';\n\n // Only needs escaping when the value is a string.\n if (typeof value !== 'string') {\n result += value.toString() + ';';\n continue;\n }\n\n end = value.indexOf('\"');\n\n // This is a optimization to avoid having to look twice for the \" character.\n // And make the loop already start in the middle\n if (end === -1) {\n result += value + ';';\n continue;\n }\n\n const length = value.length;\n start = 0;\n\n // Escapes double quotes to be used inside attributes\n // Faster than using regex\n // https://jsperf.app/kakihu\n for (; end < length; end++) {\n if (value[end] === '\"') {\n result += value.slice(start, end) + '"';\n start = end + 1;\n }\n }\n\n // Appends the remaining string.\n result += value.slice(start, end) + ';';\n }\n\n return result;\n}\n\n/** @type {import('.').attributesToString} */\nfunction attributesToString(attributes) {\n const keys = Object.keys(attributes);\n const length = keys.length;\n\n let key,\n value,\n type,\n end,\n start,\n classItems,\n valueLength,\n result = '',\n index = 0;\n\n for (; index < length; index++) {\n key = keys[index];\n\n // Skips all @kitajs/html specific attributes.\n if (key === 'children' || key === 'safe' || key === 'of') {\n continue;\n }\n\n // @ts-expect-error - this indexing is safe.\n value = attributes[key];\n\n if (value === null || value === undefined) {\n continue;\n }\n\n // React className compatibility.\n if (key === 'className') {\n // @ts-expect-error - both were provided, so use the class attribute.\n if (attributes.class !== undefined) {\n continue;\n }\n\n key = 'class';\n } else if (key === 'class' && Array.isArray(value)) {\n classItems = value;\n valueLength = value.length;\n\n // Reuses the value variable\n value = '';\n\n for (let i = 0; i < valueLength; i++) {\n if (classItems[i] && classItems[i].length > 0) {\n if (value) {\n value += ' ' + classItems[i].trim();\n } else {\n value += classItems[i].trim();\n }\n }\n }\n\n // All attributes may have been disabled.\n if (value.length === 0) {\n continue;\n }\n } else if (key === 'style') {\n result += ' style=\"' + styleToString(value) + '\"';\n continue;\n } else if (key === 'attrs') {\n if (typeof value === 'string') {\n result += ' ' + value;\n } else {\n result += attributesToString(value);\n }\n\n continue;\n }\n\n type = typeof value;\n\n if (type === 'boolean') {\n // Only add the attribute if the value is true.\n if (value) {\n result += ' ' + key;\n }\n\n continue;\n }\n\n result += ' ' + key;\n\n if (type !== 'string') {\n // Non objects are\n if (type !== 'object') {\n result += '=\"' + value.toString() + '\"';\n continue;\n }\n\n // Dates are always safe\n if (value instanceof Date) {\n result += '=\"' + value.toISOString() + '\"';\n continue;\n }\n\n // The object may have a overridden toString method.\n // Which results in a non escaped string.\n value = value.toString();\n }\n\n end = value.indexOf('\"');\n\n // This is a optimization to avoid having to look twice for the \" character.\n // And make the loop already start in the middle\n if (end === -1) {\n result += '=\"' + value + '\"';\n continue;\n }\n\n result += '=\"';\n\n valueLength = value.length;\n start = 0;\n\n // Escapes double quotes to be used inside attributes\n // Faster than using regex\n // https://jsperf.app/kakihu\n for (; end < valueLength; end++) {\n if (value[end] === '\"') {\n result += value.slice(start, end) + '"';\n start = end + 1;\n }\n }\n\n // Appends the remaining string.\n result += value.slice(start, end) + '\"';\n }\n\n return result;\n}\n\n/**\n * @type {import('.').contentsToString}\n * @returns {any}\n */\nfunction contentsToString(contents, escape) {\n let length = contents.length;\n let result = '';\n\n for (let index = 0; index < length; index++) {\n const content = contents[index];\n\n switch (typeof content) {\n case 'string':\n case 'number':\n // Bigint is the only case where it differs from React.\n // where React renders a empty string and we render the whole number.\n case 'bigint':\n result += content;\n continue;\n case 'boolean':\n continue;\n }\n\n if (!content) {\n continue;\n }\n\n if (Array.isArray(content)) {\n contents.splice(index--, 1, ...content);\n length += content.length - 1;\n continue;\n }\n\n if (typeof content.then === 'function') {\n // @ts-ignore - Type instantiation is excessively deep and possibly infinite.\n return Promise.all(contents.slice(index)).then(function resolveContents(resolved) {\n resolved.unshift(result);\n return contentsToString(resolved, escape);\n });\n }\n\n throw new Error('Objects are not valid as a KitaJSX child');\n }\n\n // escapeHtml is faster with longer strings, that's\n // why we escape the entire result once\n if (escape === true) {\n return escapeHtml(result);\n }\n\n return result;\n}\n\n/**\n * @param {import('./index').Children} content\n * @param {boolean} safe\n * @returns {JSX.Element}\n */\nfunction contentToString(content, safe) {\n switch (typeof content) {\n case 'string':\n return safe ? escapeHtml(content) : content;\n case 'number':\n // Bigint is the only case where it differs from React.\n // where React renders a empty string and we render the whole number.\n case 'bigint':\n return content.toString();\n case 'boolean':\n return '';\n }\n\n if (!content) {\n return '';\n }\n\n if (Array.isArray(content)) {\n return contentsToString(content, safe);\n }\n\n if (typeof content.then === 'function') {\n return content.then(function resolveContent(resolved) {\n return contentToString(resolved, safe);\n });\n }\n\n throw new Error('Objects are not valid as a KitaJSX child');\n}\n\n/**\n * Just to stop TS from complaining about the type.\n *\n * @type {import('.').createElement}\n * @param {any} name\n * @returns {any}\n */\nfunction createElement(name, attrs, ...children) {\n const hasAttrs = attrs !== null;\n\n // Calls the element creator function if the name is a function\n if (typeof name === 'function') {\n // We at least need to pass the children to the function component. We may receive null if this\n // component was called without any children.\n if (!hasAttrs) {\n return name({ children: children.length > 1 ? children : children[0] });\n }\n\n attrs.children = children.length > 1 ? children : children[0];\n return name(attrs);\n }\n\n // Switches the tag name when this custom `tag` is present.\n if (hasAttrs && name === 'tag') {\n name = /** @type {string} */ (attrs.of);\n }\n\n const attributes = hasAttrs ? attributesToString(attrs) : '';\n\n if (children.length === 0) {\n return isVoidElement(name)\n ? '<' + name + attributes + '/>'\n : '<' + name + attributes + '></' + name + '>';\n }\n\n const contents = contentsToString(children, hasAttrs && attrs.safe);\n\n if (typeof contents === 'string') {\n return '<' + name + attributes + '>' + contents + '</' + name + '>';\n }\n\n return contents.then(function resolveContents(contents) {\n return '<' + name + attributes + '>' + contents + '</' + name + '>';\n });\n}\n\n/** @type {import('.').Fragment} */\nfunction Fragment(props) {\n return contentsToString([props.children]);\n}\n\nexports.escape = escape;\nexports.e = escape;\nexports.escapeHtml = escapeHtml;\nexports.isVoidElement = isVoidElement;\nexports.attributesToString = attributesToString;\nexports.toKebabCase = toKebabCase;\nexports.isUpper = isUpper;\nexports.styleToString = styleToString;\nexports.createElement = createElement;\nexports.h = createElement;\nexports.contentsToString = contentsToString;\nexports.contentToString = contentToString;\nexports.Fragment = Fragment;\n//@ts-expect-error - global augmentation\nexports.Html = { ...exports };\n","const { contentsToString, contentToString } = require('./index');\nconst { Readable, PassThrough } = require('node:stream');\n\n// Avoids double initialization in case this file is not cached by\n// module bundlers.\nif (!globalThis.SUSPENSE_ROOT) {\n /* global SUSPENSE_ROOT */\n globalThis.SUSPENSE_ROOT = {\n requests: new Map(),\n requestCounter: 1,\n autoScript: true\n };\n}\n\nfunction noop() {}\n\n/**\n * Simple IE11 compatible replace child scripts to replace the template streamed by the\n * server.\n *\n * As this script is the only residue of this package that is actually sent to the client,\n * it's important to keep it as small as possible and also include the license to avoid\n * legal issues.\n */\n// Pending data-sr elements are kept pending if their fallback has not yet been\n// rendered, on each render a try to switch all pending data-sr is attempted until\n// no elements are substituted.\nconst SuspenseScript = /* html */ `\n <script id=\"kita-html-suspense\">\n /*! MIT License https://kita.js.org */\n function $KITA_RC(i){\n // simple aliases\n var d=document,q=d.querySelector.bind(d),\n // div sent as the fallback wrapper\n v=q('div[id=\"B:'+i+'\"][data-sf]'),\n // template and script sent after promise finishes\n t=q('template[id=\"N:'+i+'\"][data-sr]'),s=q('script[id=\"S:'+i+'\"][data-ss]'),\n // fragment created to avoid inserting element one by one\n f=d.createDocumentFragment(),\n // used by iterators\n c,j,\n // all pending hydrations\n r;\n\n // if div or template is not found, let this hydration as pending\n if(t&&v&&s){\n // appends into the fragment\n while(c=t.content.firstChild)\n f.appendChild(c);\n\n // replaces the div and removes the script and template\n v.parentNode.replaceChild(f,v);\n t.remove();\n s.remove();\n\n // looks for pending templates\n r=d.querySelectorAll('template[id][data-sr]');\n\n do{\n // resets j & c from previous loop\n c=j=0;\n\n // loops over every found pending template and \n for(;c<r.length;c++)\n if(r[c]!=t)\n // let j as true while at least on $KITA_RC call returns true\n j=$KITA_RC(r[c].id.slice(2))?!0:j;\n }while(j)\n\n // we know at least the original template was substituted\n return!0;\n }\n }\n </script>\n `\n // Removes comment lines\n .replace(/^\\s*\\/\\/.*/gm, '')\n // Removes line breaks added for readability\n .replace(/\\n\\s*/g, '');\n\n/** @type {import('./suspense').Suspense} */\nfunction Suspense(props) {\n const children = Array.isArray(props.children)\n ? contentsToString(props.children)\n : contentToString(props.children);\n\n // Returns content if it's not a promise\n if (typeof children === 'string') {\n return children;\n }\n\n if (!props.rid) {\n throw new Error('Suspense requires a `rid` to be specified.');\n }\n\n let data = SUSPENSE_ROOT.requests.get(props.rid);\n\n if (!data) {\n // Creating the request data lazily allows\n // faster render() calls when no suspense\n // components are used.\n data = {\n stream: new Readable({ read: noop }),\n running: 0,\n sent: false\n };\n\n SUSPENSE_ROOT.requests.set(props.rid, data);\n }\n\n // Gets the current run number for this request\n // Increments first so we can differ 0 as no suspenses\n // were used and 1 as the first suspense component\n const run = ++data.running;\n\n void children\n .then(writeStreamTemplate)\n .catch(function errorRecover(error) {\n // No catch block was specified, so we can\n // re-throw the error.\n if (!props.catch) {\n throw error;\n }\n\n let html;\n\n // Unwraps error handler\n if (typeof props.catch === 'function') {\n html = props.catch(error);\n } else {\n html = props.catch;\n }\n\n // handles if catch block returns a string\n if (typeof html === 'string') {\n return writeStreamTemplate(html);\n }\n\n // must be a promise\n return html.then(writeStreamTemplate);\n })\n .catch(function writeFatalError(error) {\n data.stream.emit('error', error);\n })\n .finally(function clearRequestData() {\n // reduces current suspense id\n if (data && data.running > 1) {\n data.running -= 1;\n return;\n }\n\n // Last suspense component, runs cleanup\n if (data && !data.stream.closed) {\n data.stream.push(null);\n }\n\n // Removes the current state\n SUSPENSE_ROOT.requests.delete(props.rid);\n });\n\n // Always will be a single children because multiple\n // root tags aren't a valid JSX syntax\n const fallback = contentToString(props.fallback);\n\n // Keeps string return type\n if (typeof fallback === 'string') {\n return '<div id=\"B:' + run + '\" data-sf>' + fallback + '</div>';\n }\n\n return fallback.then(function resolveCallback(resolved) {\n return '<div id=\"B:' + run + '\" data-sf>' + resolved + '</div>';\n });\n\n /**\n * This function may be called by the catch handler in case the error could be handled.\n *\n * @param {string} result\n */\n function writeStreamTemplate(result) {\n if (\n // Ensures the stream is still open (.closed may not be already defined at this point)\n !SUSPENSE_ROOT.requests.has(props.rid) ||\n // just to typecheck\n !data ||\n // Stream was already closed/cleared out.\n data.stream.closed\n ) {\n return;\n }\n\n // Writes the suspense script if its the first\n // suspense component in this request data. This way following\n // templates+scripts can be executed\n if (SUSPENSE_ROOT.autoScript && data.sent === false) {\n data.stream.push(SuspenseScript);\n data.sent = true;\n }\n\n // Writes the chunk\n data.stream.push(\n // prettier-ignore\n `<template id=\"N:${run}\" data-sr>${result}</template><script id=\"S:${run}\" data-ss>$KITA_RC(${run})</script>`\n );\n }\n}\n\n/** @type {import('./suspense').renderToStream} */\nfunction renderToStream(html, rid) {\n if (!rid) {\n rid = SUSPENSE_ROOT.requestCounter++;\n } else if (SUSPENSE_ROOT.requests.has(rid)) {\n // Ensures the request id is unique within the current request\n // error here to keep original stack trace\n const error = new Error(`The provided Request Id is already in use: ${rid}.`);\n\n // returns errored stream to avoid throws\n return new Readable({\n read() {\n this.emit('error', error);\n this.push(null);\n }\n });\n }\n\n if (typeof html === 'function') {\n try {\n html = html(rid);\n } catch (error) {\n // Avoids memory leaks by removing the request data\n SUSPENSE_ROOT.requests.delete(rid);\n\n // returns errored stream to avoid throws\n return new Readable({\n read() {\n this.emit('error', error);\n this.push(null);\n }\n });\n }\n }\n\n // If no suspense component was used, this will not be defined.\n const requestData = SUSPENSE_ROOT.requests.get(rid);\n\n // No suspense was used, just return the HTML as a stream\n if (!requestData) {\n if (typeof html === 'string') {\n return Readable.from([html]);\n }\n\n return new Readable({\n read() {\n void html\n .then((result) => {\n this.push(result);\n this.push(null);\n })\n .catch((error) => {\n this.emit('error', error);\n });\n }\n });\n }\n\n return resolveHtmlStream(html, requestData);\n}\n\n/** @type {import('./suspense').resolveHtmlStream} */\nfunction resolveHtmlStream(template, requestData) {\n // Impossible to sync templates have their\n // streams being written (sent = true) before the fallback\n if (typeof template === 'string') {\n requestData.stream.push(template);\n return requestData.stream;\n }\n\n const prepended = new PassThrough();\n\n void template.then(\n (result) => {\n prepended.push(result);\n requestData.stream.pipe(prepended);\n },\n (error) => {\n prepended.emit('error', error);\n }\n );\n\n return prepended;\n}\n\nexports.Suspense = Suspense;\nexports.renderToStream = renderToStream;\nexports.resolveHtmlStream = resolveHtmlStream;\nexports.SuspenseScript = SuspenseScript;\n","import { QUERY_PARAMS } from \"./constants\";\nimport { getStore } from \"./store\";\nimport { joinUrl } from \"./url-utils\";\n\n/**\n * URL builder for the Storybooks router.\n * @private\n */\nexport const urlBuilder = {\n root: (...pathnames: string[]) => {\n const { baseRoute, url: base } = getStore();\n const url = new URL(joinUrl(baseRoute, ...pathnames), base);\n return url.toString();\n },\n staticFile: (filepath: string) => {\n const { baseRoute, url: base } = getStore();\n const url = new URL(joinUrl(baseRoute, filepath), base);\n return url.toString();\n },\n allProjects: () => {\n const { baseRoute, url: base } = getStore();\n const url = new URL(joinUrl(baseRoute, \"projects\"), base);\n return url.toString();\n },\n projectCreate: () => {\n const { baseRoute, url: base } = getStore();\n const url = new URL(joinUrl(baseRoute, \"projects\"), base);\n url.searchParams.set(QUERY_PARAMS.mode, QUERY_PARAMS.newResource);\n return url.toString();\n },\n projectId: (projectId: string) => {\n const { baseRoute, url: base } = getStore();\n const url = new URL(joinUrl(baseRoute, \"projects\", projectId), base);\n return url.toString();\n },\n projectIdEdit: (projectId: string) => {\n const { baseRoute, url: base } = getStore();\n const url = new URL(joinUrl(baseRoute, \"projects\", projectId), base);\n url.searchParams.set(QUERY_PARAMS.mode, QUERY_PARAMS.editResource);\n return url.toString();\n },\n allBuilds: (projectId: string) => {\n const { baseRoute, url: base } = getStore();\n const url = new URL(\n joinUrl(baseRoute, \"projects\", projectId, \"builds\"),\n base\n );\n return url.toString();\n },\n buildSHA: (projectId: string, sha: string, labelSlug?: string) => {\n const { baseRoute, url: base } = getStore();\n const url = new URL(\n joinUrl(baseRoute, \"projects\", projectId, \"builds\", sha),\n base\n );\n if (labelSlug) {\n url.searchParams.set(QUERY_PARAMS.labelSlug, labelSlug);\n }\n return url.toString();\n },\n buildUpload: (projectId: string) => {\n const { baseRoute, url: base } = getStore();\n const url = new URL(\n joinUrl(baseRoute, \"projects\", projectId, \"builds\"),\n base\n );\n url.searchParams.set(QUERY_PARAMS.mode, QUERY_PARAMS.newResource);\n return url.toString();\n },\n allLabels: (projectId: string) => {\n const { baseRoute, url: base } = getStore();\n const url = new URL(\n joinUrl(baseRoute, \"projects\", projectId, \"labels\"),\n base\n );\n return url.toString();\n },\n labelCreate: (projectId: string) => {\n const { baseRoute, url: base } = getStore();\n const url = new URL(\n joinUrl(baseRoute, \"projects\", projectId, \"labels\"),\n base\n );\n url.searchParams.set(QUERY_PARAMS.mode, QUERY_PARAMS.newResource);\n return url.toString();\n },\n labelSlug: (projectId: string, labelSlug: string) => {\n const { baseRoute, url: base } = getStore();\n const url = new URL(\n joinUrl(baseRoute, \"projects\", projectId, \"labels\", labelSlug),\n base\n );\n return url.toString();\n },\n labelSlugEdit: (projectId: string, labelSlug: string) => {\n const { baseRoute, url: base } = getStore();\n const url = new URL(\n joinUrl(baseRoute, \"projects\", projectId, \"labels\", labelSlug),\n base\n );\n url.searchParams.set(QUERY_PARAMS.mode, QUERY_PARAMS.editResource);\n return url.toString();\n },\n labelSlugLatest: (projectId: string, labelSlug: string) => {\n const { baseRoute, url: base } = getStore();\n const url = new URL(\n joinUrl(baseRoute, \"projects\", projectId, \"labels\", labelSlug, \"latest\"),\n base\n );\n return url.toString();\n },\n storybookIndexHtml: (projectId: string, sha: string) => {\n const { baseRoute, url: base } = getStore();\n const url = new URL(\n joinUrl(baseRoute, \"_\", projectId, sha, \"index.html\"),\n base\n );\n return url.toString();\n },\n storybookTestReport: (projectId: string, sha: string) => {\n const { baseRoute, url: base } = getStore();\n const url = new URL(\n joinUrl(baseRoute, \"_\", projectId, sha, \"report\", \"index.html\"),\n base\n );\n return url.toString();\n },\n storybookCoverage: (projectId: string, sha: string) => {\n const { baseRoute, url: base } = getStore();\n const url = new URL(\n joinUrl(baseRoute, \"_\", projectId, sha, \"coverage\", \"index.html\"),\n base\n );\n return url.toString();\n },\n storybookZip: (projectId: string, sha: string) => {\n const { baseRoute, url: base } = getStore();\n const url = new URL(\n joinUrl(baseRoute, \"_\", projectId, sha, \"storybook.zip\"),\n base\n );\n return url.toString();\n },\n gitHub: (gitHubRepo: string, ...pathnames: string[]) => {\n const url = new URL(\n joinUrl(gitHubRepo, ...pathnames),\n \"https://github.com\"\n );\n return url.toString();\n },\n} satisfies Record<string, (...args: any[]) => string>;\n","/// <reference path=\"./jsx.d.ts\" />\n/// <reference types=\"./suspense.d.ts\" />\n/// <reference types=\"./error-boundary.d.ts\" />\n\nconst {\n Fragment,\n attributesToString,\n isVoidElement,\n contentsToString,\n contentToString\n} = require('./index');\n\n/** @type {import('./jsx-runtime').jsx} */\nfunction jsx(name, attrs) {\n // Calls the element creator function if the name is a function\n if (typeof name === 'function') {\n return name(attrs);\n }\n\n // Switches the tag name when this custom `tag` is present.\n if (name === 'tag') {\n name = /** @type {string} */ (attrs.of);\n }\n\n const attributes = attributesToString(attrs);\n\n if (attrs.children === undefined) {\n return isVoidElement(name)\n ? '<' + name + attributes + '/>'\n : '<' + name + attributes + '></' + name + '>';\n }\n\n const contents = contentToString(attrs.children, attrs.safe);\n\n if (contents instanceof Promise) {\n return contents.then(function resolveContents(child) {\n return '<' + name + attributes + '>' + child + '</' + name + '>';\n });\n }\n\n return '<' + name + attributes + '>' + contents + '</' + name + '>';\n}\n\n/** @type {import('./jsx-runtime').jsxs} */\nfunction jsxs(name, attrs) {\n // Calls the element creator function if the name is a function\n if (typeof name === 'function') {\n return name(attrs);\n }\n\n // Switches the tag name when this custom `tag` is present.\n if (name === 'tag') {\n name = /** @type {string} */ (attrs.of);\n }\n\n const attributes = attributesToString(attrs);\n\n if (attrs.children.length === 0) {\n return isVoidElement(name)\n ? '<' + name + attributes + '/>'\n : '<' + name + attributes + '></' + name + '>';\n }\n\n const contents = contentsToString(attrs.children, attrs.safe);\n\n if (contents instanceof Promise) {\n return contents.then(function resolveContents(child) {\n return '<' + name + attributes + '>' + child + '</' + name + '>';\n });\n }\n\n return '<' + name + attributes + '>' + contents + '</' + name + '>';\n}\n\nexports.jsx = jsx;\nexports.jsxs = jsxs;\n// According to the jsx-runtime spec we must export the fragment element also\nexports.Fragment = Fragment;\n","import { DEFAULT_SERVICE_NAME } from \"../utils/constants\";\nimport { getStore } from \"../utils/store\";\nimport { urlBuilder } from \"../utils/url-builder\";\nimport { joinUrl } from \"../utils/url-utils\";\n\nexport function DocumentLayout({\n title,\n breadcrumbs = [],\n children,\n footer,\n toolbar,\n}: {\n title: string;\n breadcrumbs?: string[] | Array<{ label: string; href?: string }>;\n children: JSX.Element;\n footer?: JSX.Element | null;\n toolbar?: JSX.Element | null;\n}) {\n const safeStylesheet = globalStyleSheet();\n const store = getStore();\n\n return (\n <>\n {\"<!DOCTYPE html>\"}\n <html lang=\"en\">\n <head>\n <title safe>\n {title} |{\" \"}\n {store.serviceName === DEFAULT_SERVICE_NAME\n ? \"StoryBooks\"\n : store.serviceName}\n </title>\n <style>{safeStylesheet}</style>\n <script\n src=\"https://cdn.jsdelivr.net/npm/htmx.org@2.0.6/dist/htmx.min.js\"\n crossorigin=\"anonymous\"\n ></script>\n <script\n src=\"https://cdn.jsdelivr.net/npm/htmx-ext-response-targets@2.0.2\"\n crossorigin=\"anonymous\"\n ></script>\n </head>\n <body>\n <header>\n <div\n style={{\n borderRight: \"1px solid var(--color-border)\",\n paddingRight: \"1rem\",\n }}\n >\n <a href={urlBuilder.root()} title=\"Home\">\n {store.serviceName === DEFAULT_SERVICE_NAME ? (\n <strong\n style={{\n fontFamily: \"monospace\",\n display: \"block\",\n textAlign: \"center\",\n color: \"var(--color-text-primary)\",\n }}\n >\n STORY\n <br />\n BOOKS\n </strong>\n ) : (\n <strong safe>{store.serviceName}</strong>\n )}\n </a>\n </div>\n\n <div class=\"page-heading\" style={{ flex: 1 }}>\n {breadcrumbs.length > 0 ? (\n <ul>\n {breadcrumbs.map((crumb, i, arr) => {\n const href =\n (typeof crumb === \"object\" ? crumb.href : \"\") ||\n joinUrl(\n store.url,\n ...Array.from({ length: arr.length - i }).map(\n () => \"..\"\n )\n );\n return (\n <li>\n <a safe href={href}>\n {typeof crumb === \"object\" ? crumb.label : crumb}\n </a>\n </li>\n );\n })}\n </ul>\n ) : null}\n <div safe>{title}</div>\n </div>\n\n {toolbar ? <div>{toolbar}</div> : null}\n </header>\n <main>{children}</main>\n {footer ? <footer>{footer}</footer> : null}\n\n <script\n defer\n src=\"https://unpkg.com/htmx-toaster/dist/htmx-toaster.min.js\"\n />\n </body>\n </html>\n </>\n );\n}\n\nfunction globalStyleSheet() {\n return /*css*/ `\n :root {\n --color-bg-base: #f2f2f2;\n --color-bg-card: #ffffff;\n --color-text-primary: #09090b;\n --color-text-secondary: #71717b;\n --color-text-accent: #2b7fff;\n --color-border: #e4e4e7;\n }\n\n @media (prefers-color-scheme: dark) {\n :root {\n --color-bg-base: #09090b;\n --color-bg-card: #18181b;\n --color-text-primary: #fafafa;\n --color-text-secondary: #9f9fa9;\n --color-text-accent: #2b7fff;\n --color-border: #ffffff1a;\n }\n }\n\n * {\n box-sizing: border-box;\n color-scheme: light dark;\n font-family: system-ui;\n border-color: var(--color-border);\n }\n\n body {\n font-size: 1rem;\n padding: 0px;\n margin: 0px;\n display: flex;\n flex-direction: column;\n gap: 1rem;\n min-height: 100vh;\n overflow-y: auto;\n overflow-x: hidden;\n background-color: var(--color-bg-base);\n color: var(--color-text-secondary);\n }\n\n body > header,\n body > footer,\n body > main,\n body > aside {\n margin: 0 1rem;\n background-color: var(--color-bg-card);\n color: var(--color-text-primary);\n border-radius: 0.5rem;\n overflow: hidden;\n padding: 1rem;\n }\n\n body > header {\n margin-top: 1rem; \n display: flex;\n align-items: center; \n gap: 1rem;\n }\n\n body > main {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 1rem;\n overflow: auto;\n }\n\n body > footer {\n margin-bottom: 1rem;\n }\n\n hr {\n color: var(--color-border);\n background: var(--color-border);\n border-color: var(--color-border);\n }\n\n table { \n height: 100%;\n width: 100%;\n border-radius: 0.25rem;\n overflow: hidden;\n }\n\n table caption {\n font-weight: bold;\n font-size: 1.25rem;\n padding: 0.5rem 1rem; \n text-align: start;\n }\n\n thead {\n background-color: var(--color-bg-base);\n color: var(--color-text-secondary);\n } \n\n th {\n color: var(--color-text-secondary);\n font-weight: medium;\n font-size: 0.9rem;\n text-align: start;\n padding: 0.25rem 1rem;\n }\n\n td {\n text-align: start;\n height: max-content;\n padding: 0.5rem 1rem;\n color: var(--color-text-primary);\n }\n\n time {\n font-family: monospace;\n font-size: 0.9rem;\n color: var(--color-text-secondary);\n }\n\n a,\n a:visited {\n color: var(--color-text-accent);\n text-decoration: none;\n }\n a:hover {\n color: var(--color-text-accent);\n text-decoration: underline;\n }\n\n .raw-data {\n margin: 0;\n background-color: #00000010;\n border: 1px solid var(--color-border);\n border-radius: 0.25rem;\n font-family: monospace;\n font-size: 0.9rem;\n white-space: pre-wrap;\n padding: 0.5rem;\n }\n .raw-data:empty{\n display: none;\n }\n \n .page-heading {\n display: flex;\n flex-direction: column;\n gap: 0.25rem;\n font-weight: bold;\n }\n\n .page-heading > ul {\n display: flex;\n align-items: center;\n padding: 0;\n margin: 0;\n list-style: none;\n }\n .page-heading > ul > li { \n font-weight: normal;\n list-style: none;\n font-size: 0.9em;\n }\n .page-heading > ul > li::after {\n content: \\\"/\\\";\n color: inherit;\n margin: 0 0.5rem;\n opacity: 0.5;\n }\n\n .description {\n color: var(--color-text-secondary);\n font-size: 0.9rem;\n }\n\n form {\n display: flex;\n flex-direction: column;\n gap: 1rem;\n }\n\n form fieldset {\n display: flex;\n flex-direction:column;\n gap: 1rem;\n border-radius: 0.5rem;\n }\n form legend {\n font-size:0.9rem;\n }\n\n form .field {\n display: flex;\n flex-direction: column;\n gap: 0.25rem;\n }\n\n form label {\n font-size:0.9rem;\n font-weight:600;\n }\n \n form input,\n form select,\n form textarea,\n form button\n {\n border-radius: 0.25rem;\n padding: 0.25rem 0.5rem;\n color: inherit;\n }\n form button[type=\"submit\"] {\n font-weight:bold;\n }\n\n form .description {\n font-size: 0.8rem;\n color: var(--color-text-secondary);\n }\n `.replace(/\\s+/g, \" \");\n}\n","export function ErrorMessage({\n children = \"\",\n id,\n}: {\n id?: string;\n children?: string;\n}) {\n return (\n <pre\n id={id}\n class=\"error-message raw-data\"\n style={{ background: \"#ff000020\" }}\n safe\n >\n {children.includes(\"{\")\n ? JSON.stringify(JSON.parse(children), null, 2)\n : children}\n </pre>\n );\n}\n","import type { HttpRequest } from \"@azure/functions\";\nimport { getStore } from \"./store\";\nimport { CONTENT_TYPES, QUERY_PARAMS } from \"./constants\";\n\nexport function checkIsHXRequest(request?: HttpRequest): boolean {\n const req = request || getStore().request;\n return req.headers.get(\"hx-request\") === \"true\";\n}\n\nexport function checkIsHTMLRequest(request?: HttpRequest): boolean {\n const req = request || getStore().request;\n const accept = req.headers.get(\"accept\");\n return !!accept?.includes(CONTENT_TYPES.HTML);\n}\n\nexport function checkIsNewMode(request?: HttpRequest): boolean {\n const req = request || getStore().request;\n return req.query.get(QUERY_PARAMS.mode) === QUERY_PARAMS.newResource;\n}\n\nexport function checkIsEditMode(request?: HttpRequest): boolean {\n const req = request || getStore().request;\n return req.query.get(QUERY_PARAMS.mode) === QUERY_PARAMS.editResource;\n}\n","import type { HttpResponseInit, InvocationContext } from \"@azure/functions\";\nimport { renderToStream } from \"@kitajs/html/suspense\";\nimport { DocumentLayout } from \"../components/layout\";\nimport { ErrorMessage } from \"../components/error-message\";\nimport { CONTENT_TYPES } from \"./constants\";\nimport { parseErrorMessage } from \"./error-utils\";\nimport { checkIsHTMLRequest, checkIsHXRequest } from \"./request-utils\";\n\nexport function responseHTML(html: JSX.Element): HttpResponseInit {\n return {\n status: 200,\n headers: { \"Content-Type\": CONTENT_TYPES.HTML },\n body: renderToStream(html),\n };\n}\n\nexport function responseError(\n error: unknown,\n context: InvocationContext,\n init?: ResponseInit | number\n): HttpResponseInit {\n try {\n const { errorMessage, errorStatus, errorType } = parseErrorMessage(error);\n context.error(\n `[${errorType}]`,\n errorMessage,\n error instanceof Error ? error.stack : \"\"\n );\n\n const status =\n errorStatus ?? (typeof init === \"number\" ? init : init?.status ?? 500);\n const headers = new Headers(typeof init === \"number\" ? {} : init?.headers);\n\n if (checkIsHXRequest()) {\n try {\n headers.set(\"HXToaster-Type\", \"error\");\n headers.set(\"HXToaster-Body\", errorMessage);\n } catch {}\n return { status, headers, body: errorMessage };\n }\n\n if (checkIsHTMLRequest()) {\n headers.set(\"Content-Type\", CONTENT_TYPES.HTML);\n\n return {\n status,\n headers,\n body: renderToStream(\n <DocumentLayout\n title={`Error ${status}`}\n breadcrumbs={[\n { label: \"< Back\", href: \"javascript:history.back()\" },\n ]}\n >\n <ErrorMessage>{errorMessage}</ErrorMessage>\n </DocumentLayout>\n ),\n };\n }\n\n headers.set(\"Content-Type\", \"application/json\");\n const jsonBody = { errorMessage };\n return { jsonBody, status, headers };\n } catch (err) {\n context.error(`[ErrOnErr]`, err);\n return {\n status: 500,\n body: typeof err === \"string\" ? err : undefined,\n jsonBody: typeof err === \"string\" ? undefined : err,\n };\n }\n}\n\nexport function responseRedirect(\n location: string,\n init: ResponseInit | number\n): HttpResponseInit {\n const status = typeof init === \"number\" ? init : init?.status ?? 303;\n const headers = new Headers(typeof init === \"number\" ? {} : init?.headers);\n\n if (checkIsHXRequest()) {\n headers.set(\"HX-redirect\", location);\n } else {\n headers.set(\"Location\", location);\n }\n\n return { status, headers };\n}\n","import { AsyncLocalStorage } from \"node:async_hooks\";\nimport type {\n HttpHandler,\n HttpRequest,\n InvocationContext,\n} from \"@azure/functions\";\nimport { CheckPermissionsCallback, OpenAPIOptions, Permission } from \"./types\";\nimport { responseError } from \"./response-utils\";\n\n/**\n * @private\n * Options for linking with Azure Blob Storage\n */\ninterface RouterHandlerOptions {\n authLevel?: \"admin\";\n serviceName: string;\n connectionString: string;\n baseRoute: string;\n staticDirs: string[];\n openapi: OpenAPIOptions | undefined;\n checkPermissions: CheckPermissionsCallback;\n}\n\ntype Store = RouterHandlerOptions & {\n accept: string | null;\n locale: string | undefined;\n url: string;\n connectionString: string;\n baseRoute: string;\n request: HttpRequest;\n context: InvocationContext;\n};\n\nconst store = new AsyncLocalStorage<Store>();\n\nexport function getStore(throwError: false): Store | undefined;\nexport function getStore(throwError?: true): Store;\nexport function getStore(throwError?: boolean) {\n const value = store.getStore();\n if (!value && throwError !== false) {\n throw new Error(\"Request store not found.\");\n }\n\n return value;\n}\n\nexport function wrapHttpHandlerWithStore(\n options: RouterHandlerOptions,\n handler: HttpHandler,\n permissions: Permission[]\n): HttpHandler {\n return function (request, context) {\n const locale = request.headers.get(\"accept-language\")?.split(\",\")[0];\n const accept = request.headers.get(\"accept\");\n const storeValue: Store = {\n ...options,\n accept,\n locale,\n url: request.url,\n request,\n context,\n };\n\n return store.run(storeValue, async () => {\n if (!permissions || permissions.length === 0) {\n return handler(request, context);\n }\n\n const { checkPermissions } = options;\n const { projectId } = request.params;\n\n const permitted = await checkPermissions(\n permissions.map((p) => ({ projectId, ...p })),\n context,\n request\n );\n\n if (permitted === true) {\n return handler(request, context);\n }\n\n const message = `Permission denied [${permissions\n .map((p) => `'${p.resource}:${p.action}'`)\n .join(\", \")}] (project: ${projectId})`;\n if (permitted === false) {\n return responseError(message, context, 403);\n }\n if (typeof permitted === \"object\" && \"status\" in permitted) {\n context.warn(message);\n return permitted;\n }\n if (permitted instanceof Response) {\n context.warn(message);\n return permitted;\n }\n\n return handler(request, context);\n });\n };\n}\n"],"x_google_ignoreList":[0,1,3],"mappings":"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