@livestore/devtools-expo 0.4.0-dev.9 → 0.5.0-dev.0
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/dist/.tsbuildinfo +1 -1
- package/dist/index.cjs +1 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/metro-config.cjs +5 -4
- package/dist/metro-config.cjs.map +1 -1
- package/dist/metro-config.d.cts +1 -1
- package/dist/metro-config.d.cts.map +1 -1
- package/package.json +49 -18
- package/src/index.cts +1 -0
- package/src/metro-config.cts +18 -17
- package/webui/index.html +14 -11
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
// eslint-disable-next-line unicorn/prefer-module, @typescript-eslint/no-require-imports
|
|
4
|
+
// biome-ignore lint/correctness/useImportExtensions: TypeScript's rewriteRelativeImportExtensions doesn't rewrite require() calls, so we need to use the output extension directly
|
|
4
5
|
module.exports = require('./metro-config.cjs');
|
|
5
6
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sourceRoot":"","sources":["../src/index.cts"],"names":[],"mappings":";;AAAA,wFAAwF;AACxF,MAAM,CAAC,OAAO,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.cjs","sourceRoot":"","sources":["../src/index.cts"],"names":[],"mappings":";;AAAA,wFAAwF;AACxF,mLAAmL;AACnL,MAAM,CAAC,OAAO,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAA"}
|
package/dist/index.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../src/index.cts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../src/index.cts"],"names":[],"mappings":"AAIA,mBAAmB,oBAAoB,CAAA"}
|
package/dist/metro-config.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
// eslint-disable-next-line @typescript-eslint/no-require-imports, unicorn/prefer-module, @typescript-eslint/consistent-type-imports
|
|
4
|
-
const { Effect, Logger,
|
|
4
|
+
const { Effect, Logger, Layer, References } = require('@livestore/utils/effect');
|
|
5
5
|
// eslint-disable-next-line @typescript-eslint/no-require-imports, unicorn/prefer-module, @typescript-eslint/consistent-type-imports
|
|
6
6
|
const { PlatformNode } = require('@livestore/utils/node');
|
|
7
7
|
/**
|
|
@@ -9,7 +9,7 @@ const { PlatformNode } = require('@livestore/utils/node');
|
|
|
9
9
|
*/
|
|
10
10
|
const addLiveStoreDevtoolsMiddleware = (config, options) => {
|
|
11
11
|
// NOTE in CI we want to skip this
|
|
12
|
-
if (process.env.CI ||
|
|
12
|
+
if (process.env.CI !== undefined || process.stdout.isTTY === false) {
|
|
13
13
|
return;
|
|
14
14
|
}
|
|
15
15
|
const host = options.host ?? '0.0.0.0'; // Defaulting to a hostname that can be reached from the device
|
|
@@ -18,12 +18,13 @@ const addLiveStoreDevtoolsMiddleware = (config, options) => {
|
|
|
18
18
|
process.env.EXPO_PUBLIC_LIVESTORE_DEVTOOLS_URL = `ws://${host}:${port}`;
|
|
19
19
|
import('@livestore/adapter-node/devtools')
|
|
20
20
|
.then(async ({ startDevtoolsServer }) => {
|
|
21
|
+
const layer = Layer.mergeAll(PlatformNode.NodeHttpClient.layerUndici, Logger.layer([Logger.consolePretty()]));
|
|
21
22
|
startDevtoolsServer({
|
|
22
23
|
clientSessionInfo: undefined,
|
|
23
24
|
schemaPath: options.schemaPath,
|
|
24
25
|
host,
|
|
25
26
|
port,
|
|
26
|
-
}).pipe(Effect.provide(
|
|
27
|
+
}).pipe(Effect.provide(layer), Effect.provideService(References.MinimumLogLevel, 'Debug'), Effect.tapCauseLogPretty, Effect.runPromise);
|
|
27
28
|
})
|
|
28
29
|
.catch((error) => {
|
|
29
30
|
console.error(error);
|
|
@@ -42,7 +43,7 @@ const addLiveStoreDevtoolsMiddleware = (config, options) => {
|
|
|
42
43
|
};
|
|
43
44
|
const enhanceMiddleware = (metroMiddleware, server) => {
|
|
44
45
|
const enhancedMiddleware = previousEnhanceMiddleware(metroMiddleware, server);
|
|
45
|
-
return (req, res, next) => req.url?.startsWith('/_livestore')
|
|
46
|
+
return (req, res, next) => req.url?.startsWith('/_livestore') === true
|
|
46
47
|
? redirectMiddleware(req, res, () => enhancedMiddleware(req, res, next))
|
|
47
48
|
: enhancedMiddleware(req, res, next);
|
|
48
49
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metro-config.cjs","sourceRoot":"","sources":["../src/metro-config.cts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"metro-config.cjs","sourceRoot":"","sources":["../src/metro-config.cts"],"names":[],"mappings":"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"}
|
package/dist/metro-config.d.cts
CHANGED
|
@@ -8,6 +8,6 @@ declare const addLiveStoreDevtoolsMiddleware: (config: MutableDeep<MetroConfig>,
|
|
|
8
8
|
type MutableDeep<T> = {
|
|
9
9
|
-readonly [P in keyof T]: MutableDeep<T[P]>;
|
|
10
10
|
};
|
|
11
|
-
export type { addLiveStoreDevtoolsMiddleware };
|
|
12
11
|
export type { Options } from './types.ts';
|
|
12
|
+
export type { addLiveStoreDevtoolsMiddleware };
|
|
13
13
|
//# sourceMappingURL=metro-config.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metro-config.d.cts","sourceRoot":"","sources":["../src/metro-config.cts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"metro-config.d.cts","sourceRoot":"","sources":["../src/metro-config.cts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAEpD,OAAO,KAAK,EAAc,OAAO,EAAE,MAAM,YAAY,CAAA;AAQrD;;GAEG;AACH,QAAA,MAAM,8BAA8B,GAAI,QAAQ,WAAW,CAAC,WAAW,CAAC,EAAE,SAAS,OAAO,SA8DzF,CAAA;AAED,0CAA0C;AAC1C,KAAK,WAAW,CAAC,CAAC,IAAI;IACpB,CAAC,UAAU,CAAC,IAAI,MAAM,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAC5C,CAAA;AAOD,YAAY,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AACzC,YAAY,EAAE,8BAA8B,EAAE,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,32 +1,63 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@livestore/devtools-expo",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
"
|
|
8
|
-
"@livestore/utils": "0.4.0-dev.9"
|
|
9
|
-
},
|
|
10
|
-
"devDependencies": {
|
|
11
|
-
"@types/node": "24.5.2",
|
|
12
|
-
"expo": "^54.0.10",
|
|
13
|
-
"vite": "7.1.7"
|
|
3
|
+
"version": "0.5.0-dev.0",
|
|
4
|
+
"license": "Apache-2.0",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/livestorejs/livestore-contrib.git"
|
|
14
8
|
},
|
|
15
9
|
"files": [
|
|
10
|
+
"dist",
|
|
16
11
|
"package.json",
|
|
17
|
-
"expo-module.config.json",
|
|
18
|
-
"webui",
|
|
19
12
|
"src",
|
|
20
|
-
"
|
|
13
|
+
"expo-module.config.json",
|
|
14
|
+
"webui"
|
|
21
15
|
],
|
|
22
|
-
"
|
|
16
|
+
"type": "module",
|
|
17
|
+
"sideEffects": false,
|
|
23
18
|
"main": "./dist/index.cjs",
|
|
24
|
-
"
|
|
25
|
-
"expo": "^54.0.10"
|
|
26
|
-
},
|
|
19
|
+
"types": "./dist/index.d.cts",
|
|
27
20
|
"publishConfig": {
|
|
28
21
|
"access": "public"
|
|
29
22
|
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@livestore/adapter-node": "0.5.0-dev.0",
|
|
25
|
+
"@livestore/utils": "0.5.0-dev.0"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/node": "25.3.3",
|
|
29
|
+
"expo": "54.0.12",
|
|
30
|
+
"vite": "7.3.1"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"@effect/opentelemetry": "^4.0.0-rc.111",
|
|
34
|
+
"@effect/platform-browser": "^4.0.0-rc.111",
|
|
35
|
+
"@effect/platform-bun": "^4.0.0-rc.111",
|
|
36
|
+
"@effect/platform-node": "^4.0.0-rc.111",
|
|
37
|
+
"@effect/platform-node-shared": "^4.0.0-rc.111",
|
|
38
|
+
"@effect/vitest": "^4.0.0-rc.111",
|
|
39
|
+
"@livestore/devtools-vite": "0.5.0-dev.0",
|
|
40
|
+
"@opentelemetry/api": "^1.9.0",
|
|
41
|
+
"@opentelemetry/resources": "^2.2.0",
|
|
42
|
+
"@standard-schema/spec": "^1.1.0",
|
|
43
|
+
"effect": "^4.0.0-rc.111",
|
|
44
|
+
"expo": "^54.0.12"
|
|
45
|
+
},
|
|
46
|
+
"$genie": {
|
|
47
|
+
"source": "package.json.genie.ts",
|
|
48
|
+
"warning": "DO NOT EDIT - changes will be overwritten",
|
|
49
|
+
"workspaceClosureDirs": [
|
|
50
|
+
"packages/@livestore/adapter-node",
|
|
51
|
+
"packages/@livestore/devtools-expo",
|
|
52
|
+
"repos/livestore/packages/@livestore/common",
|
|
53
|
+
"repos/livestore/packages/@livestore/common-cf",
|
|
54
|
+
"repos/livestore/packages/@livestore/sqlite-wasm",
|
|
55
|
+
"repos/livestore/packages/@livestore/utils",
|
|
56
|
+
"repos/livestore/packages/@livestore/utils-dev",
|
|
57
|
+
"repos/livestore/packages/@livestore/wa-sqlite",
|
|
58
|
+
"repos/livestore/packages/@livestore/webmesh"
|
|
59
|
+
]
|
|
60
|
+
},
|
|
30
61
|
"scripts": {
|
|
31
62
|
"test": "echo No tests yet"
|
|
32
63
|
}
|
package/src/index.cts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// eslint-disable-next-line unicorn/prefer-module, @typescript-eslint/no-require-imports
|
|
2
|
+
// biome-ignore lint/correctness/useImportExtensions: TypeScript's rewriteRelativeImportExtensions doesn't rewrite require() calls, so we need to use the output extension directly
|
|
2
3
|
module.exports = require('./metro-config.cjs')
|
|
3
4
|
|
|
4
5
|
export type * from './metro-config.cts'
|
package/src/metro-config.cts
CHANGED
|
@@ -1,24 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
require('@livestore/utils/effect') as typeof import('@livestore/utils/effect', { with: {
|
|
4
|
-
'resolution-mode': 'import',
|
|
5
|
-
}})
|
|
6
|
-
|
|
7
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports, unicorn/prefer-module, @typescript-eslint/consistent-type-imports
|
|
8
|
-
const { PlatformNode } = require('@livestore/utils/node') as typeof import('@livestore/utils/node', { with: {
|
|
9
|
-
'resolution-mode': 'import',
|
|
10
|
-
}})
|
|
11
|
-
|
|
1
|
+
import type * as EffectModule from '@livestore/utils/effect' with { 'resolution-mode': 'import' }
|
|
2
|
+
import type * as PlatformNodeModule from '@livestore/utils/node' with { 'resolution-mode': 'import' }
|
|
12
3
|
import type { MetroConfig } from 'expo/metro-config'
|
|
13
4
|
|
|
14
5
|
import type { Middleware, Options } from './types.ts'
|
|
15
6
|
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports, unicorn/prefer-module, @typescript-eslint/consistent-type-imports
|
|
8
|
+
const { Effect, Logger, Layer, References } = require('@livestore/utils/effect') as typeof EffectModule
|
|
9
|
+
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports, unicorn/prefer-module, @typescript-eslint/consistent-type-imports
|
|
11
|
+
const { PlatformNode } = require('@livestore/utils/node') as typeof PlatformNodeModule
|
|
12
|
+
|
|
16
13
|
/**
|
|
17
14
|
* Patches the Metro config to add a middleware via `config.server.enhanceMiddleware`.
|
|
18
15
|
*/
|
|
19
16
|
const addLiveStoreDevtoolsMiddleware = (config: MutableDeep<MetroConfig>, options: Options) => {
|
|
20
17
|
// NOTE in CI we want to skip this
|
|
21
|
-
if (process.env.CI ||
|
|
18
|
+
if (process.env.CI !== undefined || process.stdout.isTTY === false) {
|
|
22
19
|
return
|
|
23
20
|
}
|
|
24
21
|
const host = options.host ?? '0.0.0.0' // Defaulting to a hostname that can be reached from the device
|
|
@@ -29,15 +26,19 @@ const addLiveStoreDevtoolsMiddleware = (config: MutableDeep<MetroConfig>, option
|
|
|
29
26
|
|
|
30
27
|
import('@livestore/adapter-node/devtools')
|
|
31
28
|
.then(async ({ startDevtoolsServer }) => {
|
|
29
|
+
const layer = Layer.mergeAll(
|
|
30
|
+
PlatformNode.NodeHttpClient.layerUndici,
|
|
31
|
+
Logger.layer([Logger.consolePretty()]),
|
|
32
|
+
)
|
|
33
|
+
|
|
32
34
|
startDevtoolsServer({
|
|
33
35
|
clientSessionInfo: undefined,
|
|
34
36
|
schemaPath: options.schemaPath,
|
|
35
37
|
host,
|
|
36
38
|
port,
|
|
37
39
|
}).pipe(
|
|
38
|
-
Effect.provide(
|
|
39
|
-
Effect.
|
|
40
|
-
Logger.withMinimumLogLevel(LogLevel.Debug),
|
|
40
|
+
Effect.provide(layer),
|
|
41
|
+
Effect.provideService(References.MinimumLogLevel, 'Debug'),
|
|
41
42
|
Effect.tapCauseLogPretty,
|
|
42
43
|
Effect.runPromise,
|
|
43
44
|
)
|
|
@@ -68,7 +69,7 @@ const addLiveStoreDevtoolsMiddleware = (config: MutableDeep<MetroConfig>, option
|
|
|
68
69
|
const enhancedMiddleware = previousEnhanceMiddleware(metroMiddleware, server)
|
|
69
70
|
|
|
70
71
|
return (req, res, next) =>
|
|
71
|
-
req.url?.startsWith('/_livestore')
|
|
72
|
+
req.url?.startsWith('/_livestore') === true
|
|
72
73
|
? redirectMiddleware(req, res, () => enhancedMiddleware(req, res, next))
|
|
73
74
|
: enhancedMiddleware(req, res, next)
|
|
74
75
|
}
|
|
@@ -86,5 +87,5 @@ module.exports = {
|
|
|
86
87
|
addLiveStoreDevtoolsMiddleware,
|
|
87
88
|
}
|
|
88
89
|
|
|
89
|
-
export type { addLiveStoreDevtoolsMiddleware }
|
|
90
90
|
export type { Options } from './types.ts'
|
|
91
|
+
export type { addLiveStoreDevtoolsMiddleware }
|
package/webui/index.html
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
|
-
<!
|
|
1
|
+
<!doctype html>
|
|
2
2
|
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8"
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0"
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>LiveStore DevTools Redirect</title>
|
|
7
7
|
<script>
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
const currentUrl = new URL(window.location.href)
|
|
9
|
+
const redirectUrl = `http://localhost:${currentUrl.port}/_livestore/node?autoconnect`
|
|
10
|
+
window.location.href = redirectUrl
|
|
11
11
|
</script>
|
|
12
|
-
</head>
|
|
13
|
-
<body>
|
|
14
|
-
<p>
|
|
15
|
-
|
|
12
|
+
</head>
|
|
13
|
+
<body>
|
|
14
|
+
<p>
|
|
15
|
+
If you are not redirected,
|
|
16
|
+
<a href="javascript:void(0);" onclick="window.location.href = redirectUrl">click here</a>.
|
|
17
|
+
</p>
|
|
18
|
+
</body>
|
|
16
19
|
</html>
|