@lvce-editor/shared-process 0.99.21 → 0.99.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/shared-process",
3
- "version": "0.99.21",
3
+ "version": "0.99.22",
4
4
  "main": "index.js",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -18,7 +18,7 @@
18
18
  },
19
19
  "dependencies": {
20
20
  "@lvce-editor/assert": "1.9.0",
21
- "@lvce-editor/extension-host-helper-process": "0.99.21",
21
+ "@lvce-editor/extension-host-helper-process": "0.99.22",
22
22
  "@lvce-editor/ipc": "16.10.0",
23
23
  "@lvce-editor/json-rpc": "8.6.0",
24
24
  "@lvce-editor/jsonc-parser": "1.5.0",
@@ -3,6 +3,6 @@ import { fileURLToPath } from 'url'
3
3
 
4
4
  export const getBuiltinExtensionsPath = () => {
5
5
  const staticServerPath = fileURLToPath(import.meta.resolve('@lvce-editor/static-server'))
6
- const builtinExtensionsPath = join(staticServerPath, '..', '..', 'static', 'c2789fb', 'extensions')
6
+ const builtinExtensionsPath = join(staticServerPath, '..', '..', 'static', '7d43ec7', 'extensions')
7
7
  return builtinExtensionsPath
8
8
  }
@@ -0,0 +1,43 @@
1
+
2
+
3
+
4
+
5
+
6
+ const byteRangeRegex = /^bytes=(\d*)-(\d*)$/
7
+
8
+ const isValidInteger = (value) => {
9
+ return Number.isSafeInteger(value) && value >= 0
10
+ }
11
+
12
+ export const getByteRange = (rangeHeader, size) => {
13
+ const match = byteRangeRegex.exec(rangeHeader)
14
+ if (!match || size <= 0) {
15
+ return undefined
16
+ }
17
+
18
+ const [, startText, endText] = match
19
+ if (!startText && !endText) {
20
+ return undefined
21
+ }
22
+
23
+ if (!startText) {
24
+ const suffixLength = Number(endText)
25
+ if (!isValidInteger(suffixLength) || suffixLength === 0) {
26
+ return undefined
27
+ }
28
+ return {
29
+ end: size - 1,
30
+ start: Math.max(0, size - suffixLength),
31
+ }
32
+ }
33
+
34
+ const start = Number(startText)
35
+ const requestedEnd = endText ? Number(endText) : size - 1
36
+ if (!isValidInteger(start) || !isValidInteger(requestedEnd) || start >= size || start > requestedEnd) {
37
+ return undefined
38
+ }
39
+ return {
40
+ end: Math.min(requestedEnd, size - 1),
41
+ start,
42
+ }
43
+ }
@@ -1,11 +1,11 @@
1
1
  import * as HttpStatusCode from '../HttpStatusCode/HttpStatusCode.js'
2
2
 
3
- export const getContentResponse = async (content, headers = {}) => {
3
+ export const getContentResponse = async (content, headers = {}, status = HttpStatusCode.Ok) => {
4
4
  return {
5
5
  body: content,
6
6
  init: {
7
7
  headers,
8
- status: HttpStatusCode.Ok,
8
+ status,
9
9
  },
10
10
  }
11
11
  }
@@ -1,4 +1,5 @@
1
1
  import { pathToFileURL } from 'node:url'
2
+ import * as GetByteRange from '../GetByteRange/GetByteRange.js'
2
3
  import * as GetContentResponse from '../GetContentResponse/GetContentResponse.js'
3
4
  import * as GetElectronFileResponseAbsolutePath from '../GetElectronFileResponseAbsolutePath/GetElectronFileResponseAbsolutePath.js'
4
5
  import * as GetElectronFileResponseContent from '../GetElectronFileResponseContent/GetElectronFileResponseContent.js'
@@ -10,6 +11,7 @@ import * as GetPathEtag from '../GetPathEtag/GetPathEtag.js'
10
11
  import * as GetServerErrorResponse from '../GetServerErrorResponse/GetServerErrorResponse.js'
11
12
  import * as GetTypeScriptSyntaxErrorResponse from '../GetTypeScriptSyntaxErrorResponse/GetTypeScriptSyntaxErrorResponse.js'
12
13
  import * as HttpHeader from '../HttpHeader/HttpHeader.js'
14
+ import * as HttpStatusCode from '../HttpStatusCode/HttpStatusCode.js'
13
15
  import * as IsEnoentError from '../IsEnoentError/IsEnoentError.js'
14
16
  import * as IsTypeScriptSyntaxError from '../IsTypeScriptSyntaxError/IsTypeScriptSyntaxError.js'
15
17
  import * as Logger from '../Logger/Logger.js'
@@ -29,12 +31,14 @@ export const getElectronFileResponse = async (url, request) => {
29
31
  let etag
30
32
  let preparedContent
31
33
  let stats
34
+ let totalSize
32
35
  // TODO when is there no request?
33
36
  if (request) {
34
37
  const info = await GetPathEtag.getPathEtag(absolutePath)
35
38
  etag = info.etag
36
39
  stats = info.stats
37
40
  let size = stats.size
41
+ totalSize = size
38
42
  if (absolutePath.endsWith('.html')) {
39
43
  // TODO since dynamic data is injected to the stat size is not accurate
40
44
  // which is why this workaround is needed
@@ -47,6 +51,21 @@ export const getElectronFileResponse = async (url, request) => {
47
51
  return GetNotModifiedResponse.getNotModifiedResponse(headers)
48
52
  }
49
53
  }
54
+ const rangeHeader = request?.headers?.[HttpHeader.Range]
55
+ if (rangeHeader && pathName.startsWith('/remote') && typeof totalSize === 'number') {
56
+ const range = GetByteRange.getByteRange(rangeHeader, totalSize)
57
+ if (!range) {
58
+ const headers = await GetHeaders.getHeaders(absolutePath, pathName, etag, url, 0)
59
+ headers[HttpHeader.CacheControl] = 'public, max-age=0, must-revalidate'
60
+ headers[HttpHeader.ContentRange] = `bytes */${totalSize}`
61
+ return GetContentResponse.getContentResponse(Buffer.alloc(0), headers, HttpStatusCode.RangeNotSatisfiable)
62
+ }
63
+ const content = await GetElectronFileResponseContent.getElectronFileResponseContent(request, absolutePath, url, range)
64
+ const headers = await GetHeaders.getHeaders(absolutePath, pathName, etag, url, content.byteLength)
65
+ headers[HttpHeader.CacheControl] = 'public, max-age=0, must-revalidate'
66
+ headers[HttpHeader.ContentRange] = `bytes ${range.start}-${range.end}/${totalSize}`
67
+ return GetContentResponse.getContentResponse(content, headers, HttpStatusCode.PartialContent)
68
+ }
50
69
  const content = preparedContent || (await GetElectronFileResponseContent.getElectronFileResponseContent(request, absolutePath, url))
51
70
  const size = content.byteLength
52
71
  const headers = await GetHeaders.getHeaders(absolutePath, pathName, etag, url, size)
@@ -1,4 +1,5 @@
1
- import { readFile } from 'node:fs/promises'
1
+ import { open, readFile } from 'node:fs/promises'
2
+
2
3
  import * as AddCustomPathsToIndexHtml from '../AddCustomPathsToIndexHtml/AddCustomPathsToIndexHtml.js'
3
4
  import * as Platform from '../Platform/Platform.js'
4
5
  import * as ShouldTranspileTypescript from '../ShouldTranspileTypescript/ShouldTranspileTypescript.js'
@@ -6,7 +7,22 @@ import * as TranspileTypeScript from '../TranspileTypeScript/TranspileTypeScript
6
7
 
7
8
  const useCache = false // TODO enable this
8
9
 
9
- export const getElectronFileResponseContent = async (request, absolutePath, url) => {
10
+ const readRange = async (absolutePath, range) => {
11
+ const length = range.end - range.start + 1
12
+ const buffer = Buffer.allocUnsafe(length)
13
+ const file = await open(absolutePath, 'r')
14
+ try {
15
+ const { bytesRead } = await file.read(buffer, 0, length, range.start)
16
+ return buffer.subarray(0, bytesRead)
17
+ } finally {
18
+ await file.close()
19
+ }
20
+ }
21
+
22
+ export const getElectronFileResponseContent = async (request, absolutePath, url, range) => {
23
+ if (range) {
24
+ return readRange(absolutePath, range)
25
+ }
10
26
  if (ShouldTranspileTypescript.shouldTranspileTypescript(request, url)) {
11
27
  const content = await readFile(absolutePath)
12
28
  const newContent = await TranspileTypeScript.transpileTypeScript(content.toString(), useCache)
@@ -15,6 +15,9 @@ export const getHeaders = async (absolutePath, pathName, etag, url, size) => {
15
15
  [HttpHeader.CrossOriginEmbedderPolicy]: CrossOriginEmbedderPolicy.value,
16
16
  [HttpHeader.CrossOriginResourcePolicy]: CrossOriginResourcePolicy.value,
17
17
  }
18
+ if (pathName.startsWith('/remote')) {
19
+ headers[HttpHeader.AcceptRanges] = 'bytes'
20
+ }
18
21
  const extraHeaders = await GetExtraHeaders.getExtraHeaders({
19
22
  absolutePath,
20
23
  etag,
@@ -1,4 +1,6 @@
1
+ export const AcceptRanges = 'Accept-Ranges'
1
2
  export const CacheControl = 'Cache-Control'
3
+ export const ContentRange = 'Content-Range'
2
4
  export const ContentType = 'Content-Type'
3
5
  export const ContentLength = 'Content-Length'
4
6
  export const ContentSecurityPolicy = 'Content-Security-Policy'
@@ -7,3 +9,4 @@ export const CrossOriginOpenerPolicy = 'Cross-Origin-Opener-Policy'
7
9
  export const CrossOriginResourcePolicy = 'Cross-Origin-Resource-Policy'
8
10
  export const Etag = 'Etag'
9
11
  export const IfNotMatch = 'if-none-match'
12
+ export const Range = 'range'
@@ -1,7 +1,9 @@
1
1
  export const NotFound = 404
2
2
  export const Ok = 200
3
+ export const PartialContent = 206
3
4
  export const Forbidden = 403
4
5
  export const UnprocessableContent = 422
5
6
  export const ServerError = 500
6
7
  export const NotModifed = 304
7
8
  export const MultipleChoices = 300
9
+ export const RangeNotSatisfiable = 416
@@ -61,11 +61,11 @@ export const getSetupName = () => {
61
61
  return 'Lvce-Setup'
62
62
  }
63
63
 
64
- export const version = '0.99.21'
64
+ export const version = '0.99.22'
65
65
 
66
- export const commit = 'c2789fb'
66
+ export const commit = '7d43ec7'
67
67
 
68
- export const date = '2026-08-08T13:19:54.000Z'
68
+ export const date = '2026-08-08T21:51:37.000Z'
69
69
 
70
70
  export const getVersion = () => {
71
71
  return version
@@ -2,5 +2,5 @@ import { join } from 'node:path'
2
2
  import * as Root from '../Root/Root.js'
3
3
 
4
4
  export const getPreloadUrl = () => {
5
- return join(Root.root, 'static', 'c2789fb', 'packages', 'preload', 'dist', 'index.js')
5
+ return join(Root.root, 'static', '7d43ec7', 'packages', 'preload', 'dist', 'index.js')
6
6
  }