Skip to content

Commit

Permalink
lazy loading
Browse files Browse the repository at this point in the history
Summary:

Test Plan:
  • Loading branch information
janfjohannes committed Apr 26, 2024
1 parent b223ff7 commit 1d8d7fb
Show file tree
Hide file tree
Showing 32 changed files with 940 additions and 12,467 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -375,3 +375,7 @@ inlang/source-code/paraglide/paraglide-solidstart/example/.solid
*.h.ts.mjs
**/vite.config.ts.timestamp-*
**/vite.config.js.timestamp-*


# gitea test instance data
lix/packages/gitea
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,7 @@ function Breadcrumbs() {
* The menu to select the branch.
*/
function BranchMenu() {
const { activeBranch, setActiveBranch, branchNames, currentBranch } = useEditorState()
const { activeBranch, setActiveBranch, branchNames, currentBranch, repo } = useEditorState()
return (
<sl-tooltip
prop:content="Select branch"
Expand All @@ -490,7 +490,10 @@ function BranchMenu() {
class="small"
style={{ "--show-delay": "1s" }}
>
<sl-dropdown prop:distance={8}>
<sl-dropdown
prop:distance={8}
// prop:addOpenListeners={() => console.log(repo()?.re fetch branches())}
>
<sl-button
slot="trigger"
prop:caret={true}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ export function EditorStateProvider(props: { children: JSXElement }) {
// experimentalFeatures: {
// lazyClone: true,
// lixCommit: true,
// }
// },
}
)

Expand Down Expand Up @@ -539,6 +539,7 @@ export function EditorStateProvider(props: { children: JSXElement }) {
if (
repo() &&
!isForkSyncDisabled() &&
false &&
repoMeta &&
!("error" in repoMeta) &&
repoMeta.isFork
Expand Down
2 changes: 1 addition & 1 deletion inlang/source-code/plugins/i18next/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ async function loadMessages(args: {
)

// split languageTags into batches, based on experiements < 20 is slow for too many iterations and > 50 is slow for too many parallel file handlings so it seems like a good default.
const batchSize = 30
const batchSize = 50
const languageTagBatches: LanguageTag[][] = []
for (let i = 0; i < languageTags.length; i += batchSize) {
languageTagBatches.push(languageTags.slice(i, i + batchSize))
Expand Down
1 change: 1 addition & 0 deletions lix/packages/.dockerignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules
docker-compose.yaml
gitea
Original file line number Diff line number Diff line change
Expand Up @@ -5,51 +5,39 @@
* @property {string} phase
* @property {number} loaded
* @property {number} total
*/

/**
* @callback ProgressCallback
* @param {GitProgressEvent} progress
* @returns {void | Promise<void>}
*/

/**
* @typedef {Object} GitHttpRequest
* @property {string} url - The URL to request
* @property {string} [method='GET'] - The HTTP method to use
* @property {Object<string, string>} [headers={}] - Headers to include in the HTTP request
* @property {Object} [agent] - An HTTP or HTTPS agent that manages connections for the HTTP client (Node.js only)
* @property {AsyncIterableIterator<Uint8Array>} [body] - An async iterator of Uint8Arrays that make up the body of POST requests
* @property {ProgressCallback} [onProgress] - Reserved for future use (emitting `GitProgressEvent`s)
* @property {object} [signal] - Reserved for future use (canceling a request)
*/

/**
* @typedef {Object} GitHttpResponse
* @property {string} url - The final URL that was fetched after any redirects
* @property {string} [method] - The HTTP method that was used
* @property {Object<string, string>} [headers] - HTTP response headers
* @property {AsyncIterableIterator<Uint8Array>} [body] - An async iterator of Uint8Arrays that make up the body of the response
* @property {number} statusCode - The HTTP status code
* @property {string} statusMessage - The HTTP status message
*/
interface GitHttpRequest {
url: string
method?: string
headers?: Record<string, string>
agent?: object
body?: AsyncIterableIterator<Uint8Array>
onProgress?: any // Replace 'any' with the actual type ProgressCallback if available
signal?: object
}

/**
* @callback HttpFetch
* @param {GitHttpRequest} request
* @returns {Promise<GitHttpResponse>}
*/
interface GitHttpResponse {
url: string
method?: string
headers?: Record<string, string>
body?: AsyncIterableIterator<Uint8Array>
statusCode: number
statusMessage: string
}

/**
* @typedef {Object} HttpClient
* @property {HttpFetch} request
*/
type HttpFetch = (request: GitHttpRequest) => Promise<GitHttpResponse>

// @ts-nocheck
interface HttpClient {
request: HttpFetch
}

// Convert a value to an Async Iterator
// This will be easier with async generator functions.
function fromValue(value) {
function fromValue(value: any) {
let queue = [value]
return {
next() {
Expand All @@ -65,7 +53,7 @@ function fromValue(value) {
}
}

function getIterator(iterable) {
function getIterator(iterable: any) {
if (iterable[Symbol.asyncIterator]) {
return iterable[Symbol.asyncIterator]()
}
Expand All @@ -79,7 +67,7 @@ function getIterator(iterable) {
}

// Currently 'for await' upsets my linters.
async function forAwait(iterable, cb) {
async function forAwait(iterable: any, cb: (arg: any) => void) {
const iter = getIterator(iterable)
// eslint-disable-next-line no-constant-condition
while (true) {
Expand All @@ -90,11 +78,11 @@ async function forAwait(iterable, cb) {
if (iter.return) iter.return()
}

async function collect(iterable) {
async function collect(iterable: any) {
let size = 0
const buffers = []
const buffers: any[] = []
// This will be easier once `for await ... of` loops are available.
await forAwait(iterable, (value) => {
await forAwait(iterable, (value: any) => {
buffers.push(value)
size += value.byteLength
})
Expand All @@ -109,7 +97,7 @@ async function collect(iterable) {

// Convert a web ReadableStream (not Node stream!) to an Async Iterator
// adapted from https://jakearchibald.com/2017/async-iterators-and-generators/
function fromStream(stream) {
function fromStream(stream: any) {
// Use native async iteration if it's available.
if (stream[Symbol.asyncIterator]) return stream
const reader = stream.getReader()
Expand All @@ -126,34 +114,59 @@ function fromStream(stream) {
},
}
}
type MakeHttpClientArgs = {
debug?: boolean
description?: string
onRes?: ({
usedUrl,
origUrl,
resBody,
statusCode,
resHeaders,
}: {
usedUrl: string
origUrl: string
resBody: Uint8Array
statusCode: number
resHeaders: Record<string, string>
}) => any
onReq?: ({ body, url, method }: { body: any; url: string; method: string }) => any
}

/* eslint-env browser */

/**
* MakeHttpClient
*
* @param { verbose?: boolean, desciption?: string, onReq: ({body: any, url: string }) => {body: any, url: string} }
* @returns HttpClient
*/
export function makeHttpClient({ verbose, description, onReq, onRes }) {
/**
* HttpClient
*
* @param {GitHttpRequest} request
* @returns {Promise<GitHttpResponse>}
*/
async function request({ url, method = "GET", headers = {}, body }) {
const cache = new Map()
export function makeHttpClient({
debug,
description,
onReq,
onRes,
}: MakeHttpClientArgs): HttpClient {
async function request({
url,
method = "GET",
headers = {},
body: rawBody,
}: GitHttpRequest): Promise<GitHttpResponse> {
// onProgress param not used
// streaming uploads aren't possible yet in the browser
let body = rawBody ? await collect(rawBody) : undefined

if (body) {
body = await collect(body)
}
const origUrl = url
const origMethod = method

if (origMethod === "GET" && cache.has(origUrl)) {
const { resHeaders, resBody } = cache.get(origUrl)
return {
url: origUrl,
method: origMethod,
statusCode: 200,
statusMessage: "OK",
body: resBody,
headers: resHeaders,
}
}

if (onReq) {
const rewritten = await onReq({ body, url })
const rewritten = await onReq({ body, url, method })

method = rewritten?.method || method
headers = rewritten?.headers || headers
Expand All @@ -164,12 +177,13 @@ export function makeHttpClient({ verbose, description, onReq, onRes }) {
const res = await fetch(url, { method, headers, body, credentials: "include" })

// convert Header object to ordinary JSON
let resHeaders = {}
let resHeaders: Record<string, string> = {}
// @ts-ignore -- headers has entries but ts complains
for (const [key, value] of res.headers.entries()) {
resHeaders[key] = value
}

if (verbose) {
if (debug) {
console.warn(`${description} git req:`, origUrl)
}

Expand All @@ -192,11 +206,16 @@ export function makeHttpClient({ verbose, description, onReq, onRes }) {

if (!resBody) {
resBody =
// @ts-ignore -- done by isogit, not sure why
res.body && res.body.getReader
? fromStream(res.body)
: [new Uint8Array(await res.arrayBuffer())]
}

if (statusCode === 200 && origMethod === "GET") {
cache.set(origUrl, { resHeaders, resBody })
}

return {
url: origUrl,
method: origMethod,
Expand Down
Loading

0 comments on commit 1d8d7fb

Please sign in to comment.