@blu1606/create-walrus-app 0.1.4 → 2.0.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/generator/file-ops.d.ts +8 -0
- package/dist/generator/file-ops.js +20 -0
- package/dist/generator/index.js +37 -22
- package/dist/generator/layers.d.ts +15 -2
- package/dist/generator/layers.js +38 -47
- package/dist/generator/types.d.ts +9 -1
- package/dist/index.js +1 -2
- package/dist/post-install/git.d.ts +8 -0
- package/dist/post-install/git.js +2 -0
- package/dist/post-install/index.d.ts +0 -1
- package/dist/post-install/index.js +2 -15
- package/dist/post-install/messages.js +1 -1
- package/package.json +68 -68
- package/{templates/base → presets/react-mysten-gallery}/.env.example +31 -31
- package/presets/react-mysten-gallery/.gitkeep +4 -0
- package/{templates/gallery → presets/react-mysten-gallery}/README.md +25 -22
- package/presets/react-mysten-gallery/package.json +34 -0
- package/presets/react-mysten-gallery/src/App.tsx +23 -0
- package/presets/react-mysten-gallery/src/components/features/file-card.tsx +89 -0
- package/{templates/gallery/src/components/GalleryGrid.tsx → presets/react-mysten-gallery/src/components/features/gallery-grid.tsx} +5 -5
- package/presets/react-mysten-gallery/src/components/features/upload-modal.tsx +69 -0
- package/{templates/react/src/components/WalletConnect.tsx → presets/react-mysten-gallery/src/components/features/wallet-connect.tsx} +1 -1
- package/presets/react-mysten-gallery/src/components/layout/app-layout.tsx +21 -0
- package/{templates/react/src/hooks/useStorage.ts → presets/react-mysten-gallery/src/hooks/use-download.ts} +2 -18
- package/presets/react-mysten-gallery/src/hooks/use-upload.ts +49 -0
- package/{templates/react/src/hooks/useWallet.ts → presets/react-mysten-gallery/src/hooks/use-wallet.ts} +2 -7
- package/presets/react-mysten-gallery/src/index.css +384 -0
- package/presets/react-mysten-gallery/src/index.ts +17 -0
- package/presets/react-mysten-gallery/src/lib/walrus/adapter.ts +197 -0
- package/presets/react-mysten-gallery/src/lib/walrus/client.ts +87 -0
- package/presets/react-mysten-gallery/src/lib/walrus/index.ts +4 -0
- package/presets/react-mysten-gallery/src/lib/walrus/types.ts +101 -0
- package/{templates/react → presets/react-mysten-gallery}/src/main.tsx +0 -1
- package/{templates/react → presets/react-mysten-gallery}/src/providers/WalletProvider.tsx +16 -1
- package/{templates/base → presets/react-mysten-gallery}/src/utils/env.ts +41 -41
- package/{templates/gallery → presets/react-mysten-gallery}/src/utils/index-manager.ts +2 -2
- package/presets/react-mysten-gallery/src/utils/mime-type.ts +97 -0
- package/presets/react-mysten-gallery/src/utils/preview-generator.ts +134 -0
- package/{templates/react → presets/react-mysten-gallery}/tsconfig.json +20 -8
- package/presets/react-mysten-simple-upload/.env.example +31 -0
- package/presets/react-mysten-simple-upload/.gitkeep +4 -0
- package/presets/react-mysten-simple-upload/index.html +13 -0
- package/{templates/react → presets/react-mysten-simple-upload}/package.json +13 -11
- package/presets/react-mysten-simple-upload/src/App.tsx +27 -0
- package/presets/react-mysten-simple-upload/src/components/features/file-preview.tsx +73 -0
- package/{templates/simple-upload/src/components/UploadForm.tsx → presets/react-mysten-simple-upload/src/components/features/upload-form.tsx} +15 -5
- package/presets/react-mysten-simple-upload/src/components/features/wallet-connect.tsx +21 -0
- package/presets/react-mysten-simple-upload/src/components/layout/app-layout.tsx +21 -0
- package/presets/react-mysten-simple-upload/src/hooks/use-download.ts +24 -0
- package/presets/react-mysten-simple-upload/src/hooks/use-upload.ts +49 -0
- package/presets/react-mysten-simple-upload/src/hooks/use-wallet.ts +11 -0
- package/presets/react-mysten-simple-upload/src/index.css +252 -0
- package/presets/react-mysten-simple-upload/src/index.ts +16 -0
- package/presets/react-mysten-simple-upload/src/lib/walrus/adapter.ts +197 -0
- package/presets/react-mysten-simple-upload/src/lib/walrus/client.ts +87 -0
- package/presets/react-mysten-simple-upload/src/lib/walrus/index.ts +4 -0
- package/{templates/base/src/adapters/storage.ts → presets/react-mysten-simple-upload/src/lib/walrus/types.ts} +83 -58
- package/presets/react-mysten-simple-upload/src/main.tsx +16 -0
- package/presets/react-mysten-simple-upload/src/providers/QueryProvider.tsx +18 -0
- package/presets/react-mysten-simple-upload/src/providers/WalletProvider.tsx +52 -0
- package/presets/react-mysten-simple-upload/src/utils/env.ts +41 -0
- package/presets/react-mysten-simple-upload/src/utils/mime-type.ts +97 -0
- package/presets/react-mysten-simple-upload/tsconfig.json +39 -0
- package/presets/react-mysten-simple-upload/tsconfig.node.json +10 -0
- package/presets/react-mysten-simple-upload/vite.config.ts +19 -0
- package/dist/__tests__/helpers/adapter-compliance.d.ts +0 -2
- package/dist/__tests__/helpers/adapter-compliance.js +0 -47
- package/dist/__tests__/helpers/fixtures.d.ts +0 -21
- package/dist/__tests__/helpers/fixtures.js +0 -30
- package/dist/__tests__/helpers/fs-helpers.d.ts +0 -12
- package/dist/__tests__/helpers/fs-helpers.js +0 -35
- package/dist/__tests__/helpers/index.d.ts +0 -4
- package/dist/__tests__/helpers/index.js +0 -4
- package/dist/__tests__/helpers/test-hooks.d.ts +0 -3
- package/dist/__tests__/helpers/test-hooks.js +0 -18
- package/dist/context.test.d.ts +0 -1
- package/dist/context.test.js +0 -98
- package/dist/generator/index.test.d.ts +0 -1
- package/dist/generator/index.test.js +0 -143
- package/dist/generator/layers.test.d.ts +0 -1
- package/dist/generator/layers.test.js +0 -92
- package/dist/generator/merge.test.d.ts +0 -1
- package/dist/generator/merge.test.js +0 -79
- package/dist/generator/transform.test.d.ts +0 -1
- package/dist/generator/transform.test.js +0 -51
- package/dist/matrix.test.d.ts +0 -1
- package/dist/matrix.test.js +0 -70
- package/dist/types.test.d.ts +0 -1
- package/dist/types.test.js +0 -65
- package/dist/utils/detect-pm.test.d.ts +0 -1
- package/dist/utils/detect-pm.test.js +0 -52
- package/dist/validator.test.d.ts +0 -1
- package/dist/validator.test.js +0 -96
- package/templates/base/README.md +0 -54
- package/templates/base/package.json +0 -19
- package/templates/base/src/types/index.ts +0 -9
- package/templates/base/src/types/walrus.ts +0 -22
- package/templates/base/src/utils/format.ts +0 -29
- package/templates/base/tsconfig.json +0 -19
- package/templates/gallery/package.json +0 -6
- package/templates/gallery/src/App.tsx +0 -21
- package/templates/gallery/src/components/FileCard.tsx +0 -27
- package/templates/gallery/src/components/UploadModal.tsx +0 -45
- package/templates/gallery/src/styles.css +0 -58
- package/templates/gallery/src/types/gallery.ts +0 -13
- package/templates/react/.eslintrc.json +0 -26
- package/templates/react/README.md +0 -80
- package/templates/react/src/App.tsx +0 -14
- package/templates/react/src/components/Layout.tsx +0 -21
- package/templates/react/src/dapp-kit.css +0 -1
- package/templates/react/src/index.css +0 -50
- package/templates/react/src/index.ts +0 -10
- package/templates/sdk-mysten/README.md +0 -65
- package/templates/sdk-mysten/package.json +0 -14
- package/templates/sdk-mysten/src/adapter.ts +0 -80
- package/templates/sdk-mysten/src/client.ts +0 -45
- package/templates/sdk-mysten/src/config.ts +0 -33
- package/templates/sdk-mysten/src/index.ts +0 -11
- package/templates/sdk-mysten/src/types.ts +0 -19
- package/templates/sdk-mysten/test/adapter.test.ts +0 -20
- package/templates/simple-upload/package.json +0 -6
- package/templates/simple-upload/src/App.tsx +0 -27
- package/templates/simple-upload/src/components/FilePreview.tsx +0 -40
- package/templates/simple-upload/src/styles.css +0 -33
- /package/{templates/react → presets/react-mysten-gallery}/index.html +0 -0
- /package/{templates/react → presets/react-mysten-gallery}/src/providers/QueryProvider.tsx +0 -0
- /package/{templates/react → presets/react-mysten-gallery}/tsconfig.node.json +0 -0
- /package/{templates/react → presets/react-mysten-gallery}/vite.config.ts +0 -0
- /package/{templates/simple-upload → presets/react-mysten-simple-upload}/README.md +0 -0
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { deepMerge } from './merge.js';
|
|
3
|
-
describe('deepMerge', () => {
|
|
4
|
-
it('should merge objects recursively', () => {
|
|
5
|
-
const target = { a: 1, b: { c: 2 } };
|
|
6
|
-
const source = { b: { d: 3 }, e: 4 };
|
|
7
|
-
const result = deepMerge(target, source);
|
|
8
|
-
expect(result).toEqual({ a: 1, b: { c: 2, d: 3 }, e: 4 });
|
|
9
|
-
});
|
|
10
|
-
it('should replace arrays entirely', () => {
|
|
11
|
-
const target = { arr: [1, 2, 3] };
|
|
12
|
-
const source = { arr: [4, 5] };
|
|
13
|
-
const result = deepMerge(target, source);
|
|
14
|
-
expect(result).toEqual({ arr: [4, 5] });
|
|
15
|
-
});
|
|
16
|
-
it('should replace primitives', () => {
|
|
17
|
-
const target = { a: 1, b: 'old' };
|
|
18
|
-
const source = { a: 2, b: 'new' };
|
|
19
|
-
const result = deepMerge(target, source);
|
|
20
|
-
expect(result).toEqual({ a: 2, b: 'new' });
|
|
21
|
-
});
|
|
22
|
-
it('should handle null and undefined', () => {
|
|
23
|
-
// undefined: skip (return target)
|
|
24
|
-
const target1 = { a: 1 };
|
|
25
|
-
const source1 = undefined;
|
|
26
|
-
const result1 = deepMerge(target1, source1);
|
|
27
|
-
expect(result1).toEqual({ a: 1 });
|
|
28
|
-
// null: override (later layers win)
|
|
29
|
-
const target2 = { a: 1 };
|
|
30
|
-
const source2 = null;
|
|
31
|
-
const result2 = deepMerge(target2, source2);
|
|
32
|
-
expect(result2).toBeNull();
|
|
33
|
-
});
|
|
34
|
-
it('should merge nested objects deeply', () => {
|
|
35
|
-
const target = {
|
|
36
|
-
dependencies: { react: '^18.0.0' },
|
|
37
|
-
scripts: { build: 'tsc' },
|
|
38
|
-
};
|
|
39
|
-
const source = {
|
|
40
|
-
dependencies: { 'react-dom': '^18.0.0' },
|
|
41
|
-
scripts: { dev: 'vite' },
|
|
42
|
-
};
|
|
43
|
-
const result = deepMerge(target, source);
|
|
44
|
-
expect(result).toEqual({
|
|
45
|
-
dependencies: { react: '^18.0.0', 'react-dom': '^18.0.0' },
|
|
46
|
-
scripts: { build: 'tsc', dev: 'vite' },
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
it('should handle package.json-like merge', () => {
|
|
50
|
-
const base = {
|
|
51
|
-
name: 'base',
|
|
52
|
-
version: '1.0.0',
|
|
53
|
-
dependencies: {
|
|
54
|
-
commander: '^11.0.0',
|
|
55
|
-
},
|
|
56
|
-
};
|
|
57
|
-
const overlay = {
|
|
58
|
-
name: 'overlay',
|
|
59
|
-
dependencies: {
|
|
60
|
-
react: '^18.0.0',
|
|
61
|
-
},
|
|
62
|
-
devDependencies: {
|
|
63
|
-
typescript: '^5.0.0',
|
|
64
|
-
},
|
|
65
|
-
};
|
|
66
|
-
const result = deepMerge(base, overlay);
|
|
67
|
-
expect(result).toEqual({
|
|
68
|
-
name: 'overlay',
|
|
69
|
-
version: '1.0.0',
|
|
70
|
-
dependencies: {
|
|
71
|
-
commander: '^11.0.0',
|
|
72
|
-
react: '^18.0.0',
|
|
73
|
-
},
|
|
74
|
-
devDependencies: {
|
|
75
|
-
typescript: '^5.0.0',
|
|
76
|
-
},
|
|
77
|
-
});
|
|
78
|
-
});
|
|
79
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { transformString, buildVariables } from './transform.js';
|
|
3
|
-
describe('transformString', () => {
|
|
4
|
-
const vars = {
|
|
5
|
-
projectName: 'my-walrus-app',
|
|
6
|
-
sdkName: 'mysten',
|
|
7
|
-
framework: 'react',
|
|
8
|
-
useCase: 'simple-upload',
|
|
9
|
-
};
|
|
10
|
-
it('should replace projectName placeholder', () => {
|
|
11
|
-
const input = '# {{projectName}}';
|
|
12
|
-
const result = transformString(input, vars);
|
|
13
|
-
expect(result).toBe('# my-walrus-app');
|
|
14
|
-
});
|
|
15
|
-
it('should replace multiple placeholders', () => {
|
|
16
|
-
const input = 'Project: {{projectName}}, SDK: {{sdkName}}, Framework: {{framework}}';
|
|
17
|
-
const result = transformString(input, vars);
|
|
18
|
-
expect(result).toBe('Project: my-walrus-app, SDK: mysten, Framework: react');
|
|
19
|
-
});
|
|
20
|
-
it('should handle multiple occurrences of same placeholder', () => {
|
|
21
|
-
const input = '{{projectName}} is a {{projectName}} project';
|
|
22
|
-
const result = transformString(input, vars);
|
|
23
|
-
expect(result).toBe('my-walrus-app is a my-walrus-app project');
|
|
24
|
-
});
|
|
25
|
-
it('should not modify text without placeholders', () => {
|
|
26
|
-
const input = 'This is plain text';
|
|
27
|
-
const result = transformString(input, vars);
|
|
28
|
-
expect(result).toBe('This is plain text');
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
|
-
describe('buildVariables', () => {
|
|
32
|
-
it('should extract variables from context', () => {
|
|
33
|
-
const context = {
|
|
34
|
-
projectName: 'test-app',
|
|
35
|
-
projectPath: '/path/to/test-app',
|
|
36
|
-
sdk: 'mysten',
|
|
37
|
-
framework: 'react',
|
|
38
|
-
useCase: 'gallery',
|
|
39
|
-
analytics: false,
|
|
40
|
-
tailwind: true,
|
|
41
|
-
packageManager: 'pnpm',
|
|
42
|
-
};
|
|
43
|
-
const vars = buildVariables(context);
|
|
44
|
-
expect(vars).toEqual({
|
|
45
|
-
projectName: 'test-app',
|
|
46
|
-
sdkName: 'mysten',
|
|
47
|
-
framework: 'react',
|
|
48
|
-
useCase: 'gallery',
|
|
49
|
-
});
|
|
50
|
-
});
|
|
51
|
-
});
|
package/dist/matrix.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/matrix.test.js
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { COMPATIBILITY_MATRIX, SDK_METADATA } from './matrix.js';
|
|
3
|
-
describe('COMPATIBILITY_MATRIX', () => {
|
|
4
|
-
it('should have entries for all SDKs', () => {
|
|
5
|
-
expect(COMPATIBILITY_MATRIX).toHaveProperty('mysten');
|
|
6
|
-
expect(COMPATIBILITY_MATRIX).toHaveProperty('tusky');
|
|
7
|
-
expect(COMPATIBILITY_MATRIX).toHaveProperty('hibernuts');
|
|
8
|
-
});
|
|
9
|
-
it('should have frameworks and useCases for each SDK', () => {
|
|
10
|
-
Object.values(COMPATIBILITY_MATRIX).forEach((sdk) => {
|
|
11
|
-
expect(sdk).toHaveProperty('frameworks');
|
|
12
|
-
expect(sdk).toHaveProperty('useCases');
|
|
13
|
-
expect(Array.isArray(sdk.frameworks)).toBe(true);
|
|
14
|
-
expect(Array.isArray(sdk.useCases)).toBe(true);
|
|
15
|
-
});
|
|
16
|
-
});
|
|
17
|
-
it('should have mysten support all frameworks', () => {
|
|
18
|
-
expect(COMPATIBILITY_MATRIX.mysten.frameworks).toEqual(['react', 'vue', 'plain-ts']);
|
|
19
|
-
});
|
|
20
|
-
it('should have mysten support all use cases', () => {
|
|
21
|
-
expect(COMPATIBILITY_MATRIX.mysten.useCases).toEqual(['simple-upload', 'gallery', 'defi-nft']);
|
|
22
|
-
});
|
|
23
|
-
it('should have tusky support limited use cases', () => {
|
|
24
|
-
expect(COMPATIBILITY_MATRIX.tusky.useCases).toEqual(['simple-upload', 'gallery']);
|
|
25
|
-
expect(COMPATIBILITY_MATRIX.tusky.useCases).not.toContain('defi-nft');
|
|
26
|
-
});
|
|
27
|
-
it('should have hibernuts with most restricted support', () => {
|
|
28
|
-
expect(COMPATIBILITY_MATRIX.hibernuts.frameworks).toEqual(['react', 'plain-ts']);
|
|
29
|
-
expect(COMPATIBILITY_MATRIX.hibernuts.useCases).toEqual(['simple-upload']);
|
|
30
|
-
});
|
|
31
|
-
it('should not have hibernuts support vue', () => {
|
|
32
|
-
expect(COMPATIBILITY_MATRIX.hibernuts.frameworks).not.toContain('vue');
|
|
33
|
-
});
|
|
34
|
-
it('should not have hibernuts support gallery or defi-nft', () => {
|
|
35
|
-
expect(COMPATIBILITY_MATRIX.hibernuts.useCases).not.toContain('gallery');
|
|
36
|
-
expect(COMPATIBILITY_MATRIX.hibernuts.useCases).not.toContain('defi-nft');
|
|
37
|
-
});
|
|
38
|
-
});
|
|
39
|
-
describe('SDK_METADATA', () => {
|
|
40
|
-
it('should have metadata for all SDKs', () => {
|
|
41
|
-
expect(SDK_METADATA).toHaveProperty('mysten');
|
|
42
|
-
expect(SDK_METADATA).toHaveProperty('tusky');
|
|
43
|
-
expect(SDK_METADATA).toHaveProperty('hibernuts');
|
|
44
|
-
});
|
|
45
|
-
it('should have name, description, and docs for each SDK', () => {
|
|
46
|
-
Object.values(SDK_METADATA).forEach((metadata) => {
|
|
47
|
-
expect(metadata).toHaveProperty('name');
|
|
48
|
-
expect(metadata).toHaveProperty('description');
|
|
49
|
-
expect(metadata).toHaveProperty('docs');
|
|
50
|
-
expect(typeof metadata.name).toBe('string');
|
|
51
|
-
expect(typeof metadata.description).toBe('string');
|
|
52
|
-
expect(typeof metadata.docs).toBe('string');
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
it('should have valid package names', () => {
|
|
56
|
-
expect(SDK_METADATA.mysten.name).toBe('@mysten/walrus');
|
|
57
|
-
expect(SDK_METADATA.tusky.name).toBe('@tusky-io/ts-sdk');
|
|
58
|
-
expect(SDK_METADATA.hibernuts.name).toBe('@hibernuts/walrus-sdk');
|
|
59
|
-
});
|
|
60
|
-
it('should have URLs in docs field', () => {
|
|
61
|
-
Object.values(SDK_METADATA).forEach((metadata) => {
|
|
62
|
-
expect(metadata.docs).toMatch(/^https?:\/\//);
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
it('should have meaningful descriptions', () => {
|
|
66
|
-
expect(SDK_METADATA.mysten.description.length).toBeGreaterThan(10);
|
|
67
|
-
expect(SDK_METADATA.tusky.description.length).toBeGreaterThan(10);
|
|
68
|
-
expect(SDK_METADATA.hibernuts.description.length).toBeGreaterThan(10);
|
|
69
|
-
});
|
|
70
|
-
});
|
package/dist/types.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/types.test.js
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
describe('Type definitions', () => {
|
|
3
|
-
it('should allow valid SDK values', () => {
|
|
4
|
-
const sdks = ['mysten', 'tusky', 'hibernuts'];
|
|
5
|
-
expect(sdks).toHaveLength(3);
|
|
6
|
-
});
|
|
7
|
-
it('should allow valid Framework values', () => {
|
|
8
|
-
const frameworks = ['react', 'vue', 'plain-ts'];
|
|
9
|
-
expect(frameworks).toHaveLength(3);
|
|
10
|
-
});
|
|
11
|
-
it('should allow valid UseCase values', () => {
|
|
12
|
-
const useCases = ['simple-upload', 'gallery', 'defi-nft'];
|
|
13
|
-
expect(useCases).toHaveLength(3);
|
|
14
|
-
});
|
|
15
|
-
it('should allow valid PackageManager values', () => {
|
|
16
|
-
const pms = ['npm', 'pnpm', 'yarn', 'bun'];
|
|
17
|
-
expect(pms).toHaveLength(4);
|
|
18
|
-
});
|
|
19
|
-
it('should create valid Context object', () => {
|
|
20
|
-
const context = {
|
|
21
|
-
projectName: 'test-app',
|
|
22
|
-
projectPath: '/path/to/test-app',
|
|
23
|
-
sdk: 'mysten',
|
|
24
|
-
framework: 'react',
|
|
25
|
-
useCase: 'simple-upload',
|
|
26
|
-
analytics: false,
|
|
27
|
-
tailwind: true,
|
|
28
|
-
packageManager: 'pnpm',
|
|
29
|
-
};
|
|
30
|
-
expect(context.projectName).toBe('test-app');
|
|
31
|
-
expect(context.sdk).toBe('mysten');
|
|
32
|
-
expect(context.framework).toBe('react');
|
|
33
|
-
expect(context.useCase).toBe('simple-upload');
|
|
34
|
-
expect(context.analytics).toBe(false);
|
|
35
|
-
expect(context.tailwind).toBe(true);
|
|
36
|
-
expect(context.packageManager).toBe('pnpm');
|
|
37
|
-
});
|
|
38
|
-
it('should create valid ValidationResult with success', () => {
|
|
39
|
-
const result = {
|
|
40
|
-
valid: true,
|
|
41
|
-
};
|
|
42
|
-
expect(result.valid).toBe(true);
|
|
43
|
-
expect(result.error).toBeUndefined();
|
|
44
|
-
expect(result.suggestion).toBeUndefined();
|
|
45
|
-
});
|
|
46
|
-
it('should create valid ValidationResult with error', () => {
|
|
47
|
-
const result = {
|
|
48
|
-
valid: false,
|
|
49
|
-
error: 'Something went wrong',
|
|
50
|
-
suggestion: 'Try this instead',
|
|
51
|
-
};
|
|
52
|
-
expect(result.valid).toBe(false);
|
|
53
|
-
expect(result.error).toBe('Something went wrong');
|
|
54
|
-
expect(result.suggestion).toBe('Try this instead');
|
|
55
|
-
});
|
|
56
|
-
it('should allow ValidationResult with error but no suggestion', () => {
|
|
57
|
-
const result = {
|
|
58
|
-
valid: false,
|
|
59
|
-
error: 'Error without suggestion',
|
|
60
|
-
};
|
|
61
|
-
expect(result.valid).toBe(false);
|
|
62
|
-
expect(result.error).toBe('Error without suggestion');
|
|
63
|
-
expect(result.suggestion).toBeUndefined();
|
|
64
|
-
});
|
|
65
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import { detectPackageManager } from './detect-pm.js';
|
|
3
|
-
describe('detectPackageManager', () => {
|
|
4
|
-
let originalUserAgent;
|
|
5
|
-
beforeEach(() => {
|
|
6
|
-
originalUserAgent = process.env.npm_config_user_agent;
|
|
7
|
-
});
|
|
8
|
-
afterEach(() => {
|
|
9
|
-
if (originalUserAgent === undefined) {
|
|
10
|
-
delete process.env.npm_config_user_agent;
|
|
11
|
-
}
|
|
12
|
-
else {
|
|
13
|
-
process.env.npm_config_user_agent = originalUserAgent;
|
|
14
|
-
}
|
|
15
|
-
});
|
|
16
|
-
it('should detect pnpm from user agent', () => {
|
|
17
|
-
process.env.npm_config_user_agent = 'pnpm/8.6.0 npm/? node/v18.16.0 linux x64';
|
|
18
|
-
expect(detectPackageManager()).toBe('pnpm');
|
|
19
|
-
});
|
|
20
|
-
it('should detect yarn from user agent', () => {
|
|
21
|
-
process.env.npm_config_user_agent = 'yarn/1.22.19 npm/? node/v18.16.0 linux x64';
|
|
22
|
-
expect(detectPackageManager()).toBe('yarn');
|
|
23
|
-
});
|
|
24
|
-
it('should detect bun from user agent', () => {
|
|
25
|
-
process.env.npm_config_user_agent = 'bun/1.0.0';
|
|
26
|
-
expect(detectPackageManager()).toBe('bun');
|
|
27
|
-
});
|
|
28
|
-
it('should default to npm when no user agent', () => {
|
|
29
|
-
delete process.env.npm_config_user_agent;
|
|
30
|
-
expect(detectPackageManager()).toBe('npm');
|
|
31
|
-
});
|
|
32
|
-
it('should default to npm for unknown user agent', () => {
|
|
33
|
-
process.env.npm_config_user_agent = 'unknown/1.0.0';
|
|
34
|
-
expect(detectPackageManager()).toBe('npm');
|
|
35
|
-
});
|
|
36
|
-
it('should handle user agent with pnpm in different positions', () => {
|
|
37
|
-
process.env.npm_config_user_agent = 'npm/? pnpm/8.0.0 node/v18.0.0';
|
|
38
|
-
expect(detectPackageManager()).toBe('pnpm');
|
|
39
|
-
});
|
|
40
|
-
it('should prioritize pnpm over yarn when both present', () => {
|
|
41
|
-
process.env.npm_config_user_agent = 'pnpm/8.0.0 yarn/1.22.0';
|
|
42
|
-
expect(detectPackageManager()).toBe('pnpm');
|
|
43
|
-
});
|
|
44
|
-
it('should prioritize yarn over bun when both present', () => {
|
|
45
|
-
process.env.npm_config_user_agent = 'yarn/1.22.0 bun/1.0.0';
|
|
46
|
-
expect(detectPackageManager()).toBe('yarn');
|
|
47
|
-
});
|
|
48
|
-
it('should handle empty string user agent', () => {
|
|
49
|
-
process.env.npm_config_user_agent = '';
|
|
50
|
-
expect(detectPackageManager()).toBe('npm');
|
|
51
|
-
});
|
|
52
|
-
});
|
package/dist/validator.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/validator.test.js
DELETED
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { validateProjectName, validateContext } from './validator.js';
|
|
3
|
-
describe('validateProjectName', () => {
|
|
4
|
-
it('should accept valid project names', () => {
|
|
5
|
-
expect(validateProjectName('my-project')).toBe(true);
|
|
6
|
-
expect(validateProjectName('app123')).toBe(true);
|
|
7
|
-
expect(validateProjectName('walrus-app-v2')).toBe(true);
|
|
8
|
-
});
|
|
9
|
-
it('should reject names with path traversal', () => {
|
|
10
|
-
expect(validateProjectName('../my-project')).toContain('path separators');
|
|
11
|
-
expect(validateProjectName('../../escape')).toContain('path separators');
|
|
12
|
-
expect(validateProjectName('test/../bad')).toContain('path separators');
|
|
13
|
-
});
|
|
14
|
-
it('should reject names with forward slashes', () => {
|
|
15
|
-
expect(validateProjectName('my/project')).toContain('path separators');
|
|
16
|
-
expect(validateProjectName('/absolute/path')).toContain('path separators');
|
|
17
|
-
});
|
|
18
|
-
it('should reject names with backslashes', () => {
|
|
19
|
-
expect(validateProjectName('my\\project')).toContain('path separators');
|
|
20
|
-
expect(validateProjectName('C:\\Windows\\path')).toContain('path separators');
|
|
21
|
-
});
|
|
22
|
-
it('should reject absolute paths', () => {
|
|
23
|
-
expect(validateProjectName('/usr/local/bin')).toContain('path');
|
|
24
|
-
expect(validateProjectName('C:\\Program Files')).toContain('path');
|
|
25
|
-
});
|
|
26
|
-
it('should reject names with uppercase letters', () => {
|
|
27
|
-
expect(validateProjectName('MyProject')).toContain('lowercase');
|
|
28
|
-
expect(validateProjectName('TEST')).toContain('lowercase');
|
|
29
|
-
});
|
|
30
|
-
it('should reject names with special characters', () => {
|
|
31
|
-
expect(validateProjectName('my_project')).toContain('lowercase');
|
|
32
|
-
expect(validateProjectName('my.project')).toContain('lowercase');
|
|
33
|
-
expect(validateProjectName('my@project')).toContain('lowercase');
|
|
34
|
-
expect(validateProjectName('my project')).toContain('lowercase');
|
|
35
|
-
});
|
|
36
|
-
it('should reject names starting with hyphen', () => {
|
|
37
|
-
expect(validateProjectName('-myproject')).toContain('hyphen');
|
|
38
|
-
});
|
|
39
|
-
it('should reject names ending with hyphen', () => {
|
|
40
|
-
expect(validateProjectName('myproject-')).toContain('hyphen');
|
|
41
|
-
});
|
|
42
|
-
it('should accept names with hyphens in middle', () => {
|
|
43
|
-
expect(validateProjectName('my-awesome-project')).toBe(true);
|
|
44
|
-
});
|
|
45
|
-
it('should accept numbers in names', () => {
|
|
46
|
-
expect(validateProjectName('app123')).toBe(true);
|
|
47
|
-
expect(validateProjectName('2024-project')).toBe(true);
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
describe('validateContext', () => {
|
|
51
|
-
const createContext = (overrides = {}) => ({
|
|
52
|
-
projectName: 'test-app',
|
|
53
|
-
projectPath: '/path/to/test-app',
|
|
54
|
-
sdk: 'mysten',
|
|
55
|
-
framework: 'react',
|
|
56
|
-
useCase: 'simple-upload',
|
|
57
|
-
analytics: false,
|
|
58
|
-
tailwind: true,
|
|
59
|
-
packageManager: 'pnpm',
|
|
60
|
-
...overrides,
|
|
61
|
-
});
|
|
62
|
-
it('should validate compatible sdk and framework combinations', () => {
|
|
63
|
-
expect(validateContext(createContext({ sdk: 'mysten', framework: 'react' }))).toEqual({ valid: true });
|
|
64
|
-
expect(validateContext(createContext({ sdk: 'mysten', framework: 'vue' }))).toEqual({ valid: true });
|
|
65
|
-
expect(validateContext(createContext({ sdk: 'mysten', framework: 'plain-ts' }))).toEqual({ valid: true });
|
|
66
|
-
expect(validateContext(createContext({ sdk: 'tusky', framework: 'react' }))).toEqual({ valid: true });
|
|
67
|
-
expect(validateContext(createContext({ sdk: 'hibernuts', framework: 'react' }))).toEqual({ valid: true });
|
|
68
|
-
});
|
|
69
|
-
it('should reject incompatible sdk and framework combinations', () => {
|
|
70
|
-
const result = validateContext(createContext({ sdk: 'hibernuts', framework: 'vue' }));
|
|
71
|
-
expect(result.valid).toBe(false);
|
|
72
|
-
expect(result.error).toContain('incompatible');
|
|
73
|
-
expect(result.suggestion).toContain('Compatible frameworks');
|
|
74
|
-
});
|
|
75
|
-
it('should validate compatible sdk and useCase combinations', () => {
|
|
76
|
-
expect(validateContext(createContext({ sdk: 'mysten', useCase: 'simple-upload' }))).toEqual({ valid: true });
|
|
77
|
-
expect(validateContext(createContext({ sdk: 'mysten', useCase: 'gallery' }))).toEqual({ valid: true });
|
|
78
|
-
expect(validateContext(createContext({ sdk: 'mysten', useCase: 'defi-nft' }))).toEqual({ valid: true });
|
|
79
|
-
expect(validateContext(createContext({ sdk: 'tusky', useCase: 'simple-upload' }))).toEqual({ valid: true });
|
|
80
|
-
});
|
|
81
|
-
it('should reject incompatible sdk and useCase combinations', () => {
|
|
82
|
-
const result = validateContext(createContext({ sdk: 'tusky', useCase: 'defi-nft' }));
|
|
83
|
-
expect(result.valid).toBe(false);
|
|
84
|
-
expect(result.error).toContain('does not support');
|
|
85
|
-
expect(result.suggestion).toContain('Supported use cases');
|
|
86
|
-
});
|
|
87
|
-
it('should reject hibernuts with gallery use case', () => {
|
|
88
|
-
const result = validateContext(createContext({ sdk: 'hibernuts', useCase: 'gallery' }));
|
|
89
|
-
expect(result.valid).toBe(false);
|
|
90
|
-
expect(result.error).toBeDefined();
|
|
91
|
-
});
|
|
92
|
-
it('should provide helpful suggestions in error messages', () => {
|
|
93
|
-
const result = validateContext(createContext({ sdk: 'hibernuts', framework: 'vue' }));
|
|
94
|
-
expect(result.suggestion).toMatch(/react|plain-ts/);
|
|
95
|
-
});
|
|
96
|
-
});
|
package/templates/base/README.md
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
# {{projectName}}
|
|
2
|
-
|
|
3
|
-
This is a Walrus application generated by `create-walrus-app`.
|
|
4
|
-
|
|
5
|
-
## What's Included
|
|
6
|
-
|
|
7
|
-
### Adapter Interface
|
|
8
|
-
|
|
9
|
-
- `src/adapters/storage.ts` - Universal SDK-agnostic interface
|
|
10
|
-
- Allows use case code to work with any Walrus SDK
|
|
11
|
-
|
|
12
|
-
### Type Definitions
|
|
13
|
-
|
|
14
|
-
- `src/types/walrus.ts` - Walrus-specific types
|
|
15
|
-
- `src/types/index.ts` - Common utility types
|
|
16
|
-
|
|
17
|
-
### Utilities
|
|
18
|
-
|
|
19
|
-
- `src/utils/env.ts` - Environment validation
|
|
20
|
-
- `src/utils/format.ts` - Formatting helpers
|
|
21
|
-
|
|
22
|
-
### Configuration
|
|
23
|
-
|
|
24
|
-
- `.env.example` - Environment template
|
|
25
|
-
- `tsconfig.json` - TypeScript strict mode config
|
|
26
|
-
- `package.json` - Base dependencies
|
|
27
|
-
|
|
28
|
-
## Layer Composition
|
|
29
|
-
|
|
30
|
-
This base layer is **always included** and combined with:
|
|
31
|
-
|
|
32
|
-
1. **SDK Layer** (e.g., `sdk-mysten/`) - Implements `StorageAdapter`
|
|
33
|
-
2. **Framework Layer** (e.g., `react/`) - UI framework setup
|
|
34
|
-
3. **Use Case Layer** (e.g., `simple-upload/`) - Application logic
|
|
35
|
-
|
|
36
|
-
```
|
|
37
|
-
Base + SDK + Framework + UseCase = Your App
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
## Environment Setup
|
|
41
|
-
|
|
42
|
-
1. Copy `.env.example` to `.env`
|
|
43
|
-
2. Fill in required values:
|
|
44
|
-
- Walrus network URLs
|
|
45
|
-
- Sui RPC endpoint
|
|
46
|
-
3. Optional: Add Blockberry API key
|
|
47
|
-
|
|
48
|
-
## Next Steps
|
|
49
|
-
|
|
50
|
-
This base layer is completed by:
|
|
51
|
-
|
|
52
|
-
- **Phase 4**: SDK implementation
|
|
53
|
-
- **Phase 5**: Framework setup
|
|
54
|
-
- **Phase 6**: Use case logic
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "{{projectName}}",
|
|
3
|
-
"version": "0.1.0",
|
|
4
|
-
"private": true,
|
|
5
|
-
"type": "module",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"dev": "echo 'Override by framework layer'",
|
|
8
|
-
"build": "echo 'Override by framework layer'",
|
|
9
|
-
"preview": "echo 'Override by framework layer'",
|
|
10
|
-
"lint": "eslint . --ext .ts,.tsx",
|
|
11
|
-
"type-check": "tsc --noEmit"
|
|
12
|
-
},
|
|
13
|
-
"devDependencies": {
|
|
14
|
-
"typescript": "^5.3.3",
|
|
15
|
-
"eslint": "^8.56.0",
|
|
16
|
-
"@typescript-eslint/parser": "^6.19.1",
|
|
17
|
-
"@typescript-eslint/eslint-plugin": "^6.19.1"
|
|
18
|
-
}
|
|
19
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
export type WalrusNetwork = 'testnet' | 'mainnet' | 'devnet';
|
|
2
|
-
|
|
3
|
-
export interface WalrusConfig {
|
|
4
|
-
network: WalrusNetwork;
|
|
5
|
-
publisherUrl: string;
|
|
6
|
-
aggregatorUrl: string;
|
|
7
|
-
suiRpcUrl: string;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export interface BlobInfo {
|
|
11
|
-
blobId: string;
|
|
12
|
-
name?: string;
|
|
13
|
-
size: number;
|
|
14
|
-
contentType?: string;
|
|
15
|
-
uploadedAt: number;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface StorageStats {
|
|
19
|
-
totalBlobs: number;
|
|
20
|
-
totalSize: number;
|
|
21
|
-
usedEpochs: number;
|
|
22
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Format bytes to human-readable size
|
|
3
|
-
*/
|
|
4
|
-
export function formatBytes(bytes: number): string {
|
|
5
|
-
if (bytes === 0) return '0 Bytes';
|
|
6
|
-
|
|
7
|
-
const k = 1024;
|
|
8
|
-
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
|
9
|
-
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
10
|
-
|
|
11
|
-
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Format blob ID for display (truncate middle)
|
|
16
|
-
*/
|
|
17
|
-
export function formatBlobId(blobId: string, length = 12): string {
|
|
18
|
-
if (blobId.length <= length) return blobId;
|
|
19
|
-
|
|
20
|
-
const part = Math.floor((length - 3) / 2);
|
|
21
|
-
return `${blobId.slice(0, part)}...${blobId.slice(-part)}`;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Format timestamp to locale string
|
|
26
|
-
*/
|
|
27
|
-
export function formatDate(timestamp: number): string {
|
|
28
|
-
return new Date(timestamp).toLocaleString();
|
|
29
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2020",
|
|
4
|
-
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
5
|
-
"module": "ESNext",
|
|
6
|
-
"moduleResolution": "bundler",
|
|
7
|
-
"resolveJsonModule": true,
|
|
8
|
-
"allowImportingTsExtensions": true,
|
|
9
|
-
"strict": true,
|
|
10
|
-
"noUnusedLocals": true,
|
|
11
|
-
"noUnusedParameters": true,
|
|
12
|
-
"noFallthroughCasesInSwitch": true,
|
|
13
|
-
"esModuleInterop": true,
|
|
14
|
-
"skipLibCheck": true,
|
|
15
|
-
"types": ["vite/client"]
|
|
16
|
-
},
|
|
17
|
-
"include": ["src/**/*"],
|
|
18
|
-
"exclude": ["node_modules", "dist"]
|
|
19
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { useState } from 'react';
|
|
2
|
-
import { Layout } from './components/Layout.js';
|
|
3
|
-
import { GalleryGrid } from './components/GalleryGrid.js';
|
|
4
|
-
import { UploadModal } from './components/UploadModal.js';
|
|
5
|
-
import './styles.css';
|
|
6
|
-
|
|
7
|
-
function App() {
|
|
8
|
-
const [refreshKey, setRefreshKey] = useState(0);
|
|
9
|
-
|
|
10
|
-
return (
|
|
11
|
-
<Layout>
|
|
12
|
-
<div className="gallery-app">
|
|
13
|
-
<h2>🖼️ File Gallery</h2>
|
|
14
|
-
<UploadModal onSuccess={() => setRefreshKey((k) => k + 1)} />
|
|
15
|
-
<GalleryGrid key={refreshKey} />
|
|
16
|
-
</div>
|
|
17
|
-
</Layout>
|
|
18
|
-
);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export default App;
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { formatBytes, formatDate } from '../utils/format.js';
|
|
2
|
-
import { removeItem } from '../utils/index-manager.js';
|
|
3
|
-
import type { GalleryItem } from '../types/gallery.js';
|
|
4
|
-
|
|
5
|
-
interface FileCardProps {
|
|
6
|
-
item: GalleryItem;
|
|
7
|
-
onDelete: () => void;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function FileCard({ item, onDelete }: FileCardProps) {
|
|
11
|
-
const handleDelete = () => {
|
|
12
|
-
if (confirm(`Delete ${item.name}?`)) {
|
|
13
|
-
removeItem(item.blobId);
|
|
14
|
-
onDelete();
|
|
15
|
-
}
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
return (
|
|
19
|
-
<div className="file-card">
|
|
20
|
-
<h4>{item.name}</h4>
|
|
21
|
-
<p>Size: {formatBytes(item.size)}</p>
|
|
22
|
-
<p>Uploaded: {formatDate(item.uploadedAt)}</p>
|
|
23
|
-
<p className="blob-id">Blob ID: {item.blobId.slice(0, 12)}...</p>
|
|
24
|
-
<button onClick={handleDelete}>Delete</button>
|
|
25
|
-
</div>
|
|
26
|
-
);
|
|
27
|
-
}
|