@platforma-sdk/block-tools 2.1.5 → 2.1.6
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/cli.js +1 -756
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +187 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/cmd/build-meta.d.ts +10 -0
- package/dist/cmd/build-meta.d.ts.map +1 -0
- package/dist/cmd/build-model.d.ts +11 -0
- package/dist/cmd/build-model.d.ts.map +1 -0
- package/dist/cmd/index.d.ts +11 -0
- package/dist/cmd/index.d.ts.map +1 -0
- package/dist/cmd/pack-block.d.ts +10 -0
- package/dist/cmd/pack-block.d.ts.map +1 -0
- package/dist/cmd/upload-package-v1.d.ts +15 -0
- package/dist/cmd/upload-package-v1.d.ts.map +1 -0
- package/dist/common_types.d.ts +3 -0
- package/dist/common_types.d.ts.map +1 -0
- package/dist/config-BJognM_j.mjs +536 -0
- package/dist/config-BJognM_j.mjs.map +1 -0
- package/dist/config-CfA0Dj6h.js +3 -0
- package/dist/config-CfA0Dj6h.js.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +43 -0
- package/dist/index.mjs.map +1 -0
- package/dist/lib/storage.d.ts +29 -0
- package/dist/lib/storage.d.ts.map +1 -0
- package/dist/lib.d.ts +3 -2637
- package/dist/lib.d.ts.map +1 -0
- package/dist/registry_v1/config.d.ts +12 -0
- package/dist/registry_v1/config.d.ts.map +1 -0
- package/dist/registry_v1/config_schema.d.ts +94 -0
- package/dist/registry_v1/config_schema.d.ts.map +1 -0
- package/dist/registry_v1/flags.d.ts +9 -0
- package/dist/registry_v1/flags.d.ts.map +1 -0
- package/dist/registry_v1/index.d.ts +4 -0
- package/dist/registry_v1/index.d.ts.map +1 -0
- package/dist/registry_v1/registry.d.ts +46 -0
- package/dist/registry_v1/registry.d.ts.map +1 -0
- package/dist/registry_v1/v1_repo_schema.d.ts +25 -0
- package/dist/registry_v1/v1_repo_schema.d.ts.map +1 -0
- package/dist/util.d.ts +4 -0
- package/dist/util.d.ts.map +1 -0
- package/dist/v2/build_dist.d.ts +3 -0
- package/dist/v2/build_dist.d.ts.map +1 -0
- package/dist/v2/index.d.ts +4 -0
- package/dist/v2/index.d.ts.map +1 -0
- package/dist/v2/model/block_components.d.ts +384 -0
- package/dist/v2/model/block_components.d.ts.map +1 -0
- package/dist/v2/model/common.d.ts +3 -0
- package/dist/v2/model/common.d.ts.map +1 -0
- package/dist/v2/model/content_conversion.d.ts +35 -0
- package/dist/v2/model/content_conversion.d.ts.map +1 -0
- package/dist/v2/model/content_types.d.ts +478 -0
- package/dist/v2/model/content_types.d.ts.map +1 -0
- package/dist/{lib.d.cts → v2/model/index.d.ts} +449 -1005
- package/dist/v2/model/index.d.ts.map +1 -0
- package/dist/v2/model/meta.d.ts +805 -0
- package/dist/v2/model/meta.d.ts.map +1 -0
- package/dist/v2/registry/schema.d.ts +15 -0
- package/dist/v2/registry/schema.d.ts.map +1 -0
- package/dist/v2/source_package.d.ts +8 -0
- package/dist/v2/source_package.d.ts.map +1 -0
- package/package.json +21 -17
- package/src/cmd/build-meta.ts +38 -0
- package/src/cmd/build-model.ts +76 -0
- package/src/cmd/index.ts +12 -0
- package/src/cmd/pack-block.ts +32 -0
- package/src/cmd/upload-package-v1.ts +105 -0
- package/src/common_types.ts +3 -0
- package/src/lib/storage.test.ts +91 -0
- package/src/lib/storage.ts +140 -0
- package/src/lib.ts +2 -0
- package/src/registry_v1/config.ts +90 -0
- package/src/registry_v1/config_schema.ts +30 -0
- package/src/registry_v1/flags.ts +23 -0
- package/src/registry_v1/index.ts +3 -0
- package/src/registry_v1/registry.test.ts +122 -0
- package/src/registry_v1/registry.ts +253 -0
- package/src/registry_v1/v1_repo_schema.ts +42 -0
- package/src/util.ts +25 -0
- package/src/v2/build_dist.test.ts +16 -0
- package/src/v2/build_dist.ts +29 -0
- package/src/v2/index.ts +3 -0
- package/src/v2/model/block_components.ts +32 -0
- package/src/v2/model/common.ts +2 -0
- package/src/v2/model/content_conversion.ts +178 -0
- package/src/v2/model/content_types.ts +233 -0
- package/src/v2/model/index.ts +46 -0
- package/src/v2/model/meta.ts +36 -0
- package/src/v2/registry/schema.ts +29 -0
- package/src/v2/source_package.test.ts +27 -0
- package/src/v2/source_package.ts +82 -0
- package/dist/cli.cjs +0 -786
- package/dist/cli.cjs.map +0 -1
- package/dist/cli.d.cts +0 -58
- package/dist/cli.d.ts +0 -58
- package/dist/lib.cjs +0 -629
- package/dist/lib.cjs.map +0 -1
- package/dist/lib.js +0 -577
- package/dist/lib.js.map +0 -1
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import fsp from 'node:fs/promises';
|
|
4
|
+
import * as mime from 'mime-types';
|
|
5
|
+
import * as tar from 'tar';
|
|
6
|
+
import {
|
|
7
|
+
ContentAbsoluteBinaryLocal,
|
|
8
|
+
ContentAbsoluteFile,
|
|
9
|
+
ContentAbsoluteFolder,
|
|
10
|
+
ContentAbsoluteTextLocal,
|
|
11
|
+
ContentAbsoluteUrl,
|
|
12
|
+
ContentAnyLocal,
|
|
13
|
+
ContentExplicitBase64,
|
|
14
|
+
ContentRelative
|
|
15
|
+
} from '@milaboratories/pl-model-middle-layer';
|
|
16
|
+
|
|
17
|
+
type ContentCtxFs = {
|
|
18
|
+
type: 'local';
|
|
19
|
+
/** Folder relative to which content should be resolved */
|
|
20
|
+
path: string;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
type ContentCtxUrl = {
|
|
24
|
+
type: 'remote';
|
|
25
|
+
/** URL prefix from which content should be resolved */
|
|
26
|
+
url: string;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/** Describes a place relative to which any content references should be interpreted */
|
|
30
|
+
export type ContentCtx = ContentCtxFs | ContentCtxUrl;
|
|
31
|
+
|
|
32
|
+
function tryResolve(root: string, request: string): string | undefined {
|
|
33
|
+
try {
|
|
34
|
+
return require.resolve(request, {
|
|
35
|
+
paths: [root]
|
|
36
|
+
});
|
|
37
|
+
} catch (err: any) {
|
|
38
|
+
if (err.code !== 'MODULE_NOT_FOUND') throw err;
|
|
39
|
+
}
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function mustResolve(root: string, request: string): string {
|
|
44
|
+
const res = tryResolve(root, request);
|
|
45
|
+
if (res === undefined) throw new Error(`Can't resolve ${request} against ${root}`);
|
|
46
|
+
return res;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Zod type that resolves node module request into absolute content */
|
|
50
|
+
export function ResolvedModuleFile(moduleRoot: string) {
|
|
51
|
+
return z.string().transform<ContentAbsoluteFile>((request, ctx) => {
|
|
52
|
+
const result = tryResolve(moduleRoot, request);
|
|
53
|
+
if (result === undefined) {
|
|
54
|
+
ctx.addIssue({
|
|
55
|
+
code: z.ZodIssueCode.custom,
|
|
56
|
+
message: `Can't resolve ${request} against ${moduleRoot}`
|
|
57
|
+
});
|
|
58
|
+
return z.NEVER;
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
type: 'absolute-file',
|
|
62
|
+
file: result
|
|
63
|
+
};
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Zod type that resolves node module request for folder into absolute folder,
|
|
69
|
+
* given a list of expected index files in that folder
|
|
70
|
+
* */
|
|
71
|
+
export function ResolvedModuleFolder(
|
|
72
|
+
moduleRoot: string,
|
|
73
|
+
...indexFilesToLookFor: [string, ...string[]]
|
|
74
|
+
) {
|
|
75
|
+
return z.string().transform<ContentAbsoluteFolder>((request, ctx) => {
|
|
76
|
+
const requestWithSlash = request.endsWith('/') ? request : `${request}/`;
|
|
77
|
+
|
|
78
|
+
for (const idxFile of indexFilesToLookFor) {
|
|
79
|
+
const result = tryResolve(moduleRoot, requestWithSlash + idxFile);
|
|
80
|
+
if (result !== undefined) {
|
|
81
|
+
if (!result.endsWith(idxFile))
|
|
82
|
+
throw new Error(`Unexpected resolve result ${result} with index file ${idxFile}`);
|
|
83
|
+
return {
|
|
84
|
+
type: 'absolute-folder',
|
|
85
|
+
folder: result.slice(0, result.length - idxFile.length)
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
ctx.addIssue({
|
|
91
|
+
code: z.ZodIssueCode.custom,
|
|
92
|
+
message: `Can't resolve ${request} folder against ${moduleRoot}, no index file found (${indexFilesToLookFor.join(', ')})`
|
|
93
|
+
});
|
|
94
|
+
return z.NEVER;
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function mapLocalToAbsolute(
|
|
99
|
+
root: string
|
|
100
|
+
): <T extends ContentAnyLocal>(value: T) => Exclude<T, ContentRelative> | ContentAbsoluteFile {
|
|
101
|
+
return <T extends ContentAnyLocal>(value: T) =>
|
|
102
|
+
value.type === 'relative'
|
|
103
|
+
? { type: 'absolute-file', file: path.resolve(root, value.path) }
|
|
104
|
+
: (value as Exclude<T, ContentRelative>);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function absoluteToString(): (value: ContentAbsoluteTextLocal) => Promise<string> {
|
|
108
|
+
return async (value) => {
|
|
109
|
+
if (value.type === 'absolute-file')
|
|
110
|
+
return await fsp.readFile(value.file, { encoding: 'utf-8' });
|
|
111
|
+
else return value.content;
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// TODO add type and size limitations
|
|
116
|
+
export function absoluteToBase64(): (
|
|
117
|
+
value: ContentAbsoluteBinaryLocal
|
|
118
|
+
) => Promise<ContentExplicitBase64> {
|
|
119
|
+
return async (value) => {
|
|
120
|
+
if (value.type === 'absolute-file') {
|
|
121
|
+
const mimeType = mime.lookup(value.file);
|
|
122
|
+
if (!mimeType) throw new Error(`Can't recognize mime type of the file: ${value.file}.`);
|
|
123
|
+
return {
|
|
124
|
+
type: 'explicit-base64',
|
|
125
|
+
mimeType,
|
|
126
|
+
content: await fsp.readFile(value.file, { encoding: 'base64' })
|
|
127
|
+
};
|
|
128
|
+
} else return value;
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function cpAbsoluteToRelative(
|
|
133
|
+
dstFolder: string,
|
|
134
|
+
fileAccumulator?: string[]
|
|
135
|
+
): <T extends Exclude<ContentAnyLocal, ContentRelative>>(
|
|
136
|
+
value: T
|
|
137
|
+
) => Promise<Exclude<T, ContentAbsoluteFile> | ContentRelative> {
|
|
138
|
+
return async <T extends Exclude<ContentAnyLocal, ContentRelative>>(value: T) => {
|
|
139
|
+
if (value.type === 'absolute-file') {
|
|
140
|
+
const fileName = path.basename(value.file);
|
|
141
|
+
const dst = path.resolve(dstFolder, fileName);
|
|
142
|
+
fileAccumulator?.push(fileName);
|
|
143
|
+
await fsp.cp(value.file, dst);
|
|
144
|
+
return { type: 'relative', path: fileName };
|
|
145
|
+
} else return value as Exclude<T, ContentAbsoluteFile>;
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function packFolderToRelativeTgz(
|
|
150
|
+
dstFolder: string,
|
|
151
|
+
tgzName: string,
|
|
152
|
+
fileAccumulator?: string[]
|
|
153
|
+
): (value: ContentAbsoluteFolder) => Promise<ContentRelative> {
|
|
154
|
+
if (!tgzName.endsWith('.tgz')) throw new Error(`Unexpected tgz file name: ${tgzName}`);
|
|
155
|
+
return async (value: ContentAbsoluteFolder) => {
|
|
156
|
+
const dst = path.resolve(dstFolder, tgzName);
|
|
157
|
+
await tar.create(
|
|
158
|
+
{
|
|
159
|
+
gzip: true,
|
|
160
|
+
file: dst,
|
|
161
|
+
cwd: value.folder
|
|
162
|
+
},
|
|
163
|
+
[value.folder]
|
|
164
|
+
);
|
|
165
|
+
fileAccumulator?.push(tgzName);
|
|
166
|
+
return { type: 'relative', path: tgzName };
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function mapRemoteToAbsolute(
|
|
171
|
+
rootUrl: string
|
|
172
|
+
): <T extends ContentAnyLocal>(value: T) => Exclude<T, ContentRelative> | ContentAbsoluteUrl {
|
|
173
|
+
const rootWithSlash = rootUrl.endsWith('/') ? rootUrl : `${rootUrl}/`;
|
|
174
|
+
return <T extends ContentAnyLocal>(value: T) =>
|
|
175
|
+
value.type === 'relative'
|
|
176
|
+
? { type: 'absolute-url', url: rootWithSlash + value.path }
|
|
177
|
+
: (value as Exclude<T, ContentRelative>);
|
|
178
|
+
}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
//
|
|
4
|
+
// Base content types
|
|
5
|
+
//
|
|
6
|
+
|
|
7
|
+
export const ContentExplicitString = z
|
|
8
|
+
.object({
|
|
9
|
+
type: z.literal('explicit-string'),
|
|
10
|
+
content: z.string().describe('Actual string value')
|
|
11
|
+
})
|
|
12
|
+
.strict();
|
|
13
|
+
export type ContentExplicitString = z.infer<typeof ContentExplicitString>;
|
|
14
|
+
|
|
15
|
+
export const ContentExplicitBase64 = z
|
|
16
|
+
.object({
|
|
17
|
+
type: z.literal('explicit-base64'),
|
|
18
|
+
mimeType: z
|
|
19
|
+
.string()
|
|
20
|
+
.regex(/\w+\/[-+.\w]+/)
|
|
21
|
+
.describe('MIME type to interpret content'),
|
|
22
|
+
content: z.string().base64().describe('Base64 encoded binary value')
|
|
23
|
+
})
|
|
24
|
+
.strict();
|
|
25
|
+
export type ContentExplicitBase64 = z.infer<typeof ContentExplicitBase64>;
|
|
26
|
+
|
|
27
|
+
export const ContentRelative = z
|
|
28
|
+
.object({
|
|
29
|
+
type: z.literal('relative'),
|
|
30
|
+
path: z
|
|
31
|
+
.string()
|
|
32
|
+
.describe(
|
|
33
|
+
'Address of the file, in most cases relative to the file which this structure is a part of'
|
|
34
|
+
)
|
|
35
|
+
})
|
|
36
|
+
.strict();
|
|
37
|
+
export type ContentRelative = z.infer<typeof ContentRelative>;
|
|
38
|
+
|
|
39
|
+
export const ContentAbsoluteFile = z
|
|
40
|
+
.object({
|
|
41
|
+
type: z.literal('absolute-file'),
|
|
42
|
+
file: z.string().startsWith('/').describe('Absolute address of the file in local file system')
|
|
43
|
+
})
|
|
44
|
+
.strict();
|
|
45
|
+
export type ContentAbsoluteFile = z.infer<typeof ContentAbsoluteFile>;
|
|
46
|
+
|
|
47
|
+
export const ContentAbsoluteUrl = z
|
|
48
|
+
.object({
|
|
49
|
+
type: z.literal('absolute-url'),
|
|
50
|
+
url: z.string().url().describe('Global URL to reach the requested file')
|
|
51
|
+
})
|
|
52
|
+
.strict();
|
|
53
|
+
export type ContentAbsoluteUrl = z.infer<typeof ContentAbsoluteUrl>;
|
|
54
|
+
|
|
55
|
+
//
|
|
56
|
+
// Special content types
|
|
57
|
+
//
|
|
58
|
+
|
|
59
|
+
export const ContentExplicit = z
|
|
60
|
+
.object({
|
|
61
|
+
type: z.literal('explicit'),
|
|
62
|
+
content: z.instanceof(Uint8Array).describe('Raw content')
|
|
63
|
+
})
|
|
64
|
+
.strict();
|
|
65
|
+
export type ContentExplicit = z.infer<typeof ContentExplicit>;
|
|
66
|
+
|
|
67
|
+
export const ContentAbsoluteFolder = z
|
|
68
|
+
.object({
|
|
69
|
+
type: z.literal('absolute-folder'),
|
|
70
|
+
folder: z
|
|
71
|
+
.string()
|
|
72
|
+
.startsWith('/')
|
|
73
|
+
.describe('Absolute address of the folder in local file system')
|
|
74
|
+
})
|
|
75
|
+
.strict();
|
|
76
|
+
export type ContentAbsoluteFolder = z.infer<typeof ContentAbsoluteFolder>;
|
|
77
|
+
|
|
78
|
+
//
|
|
79
|
+
// Unions
|
|
80
|
+
//
|
|
81
|
+
|
|
82
|
+
export const ContentAny = z.discriminatedUnion('type', [
|
|
83
|
+
ContentExplicitString,
|
|
84
|
+
ContentExplicitBase64,
|
|
85
|
+
ContentRelative,
|
|
86
|
+
ContentAbsoluteFile,
|
|
87
|
+
ContentAbsoluteUrl
|
|
88
|
+
]);
|
|
89
|
+
export type ContentAny = z.infer<typeof ContentAny>;
|
|
90
|
+
|
|
91
|
+
export const ContentAnyLocal = z.discriminatedUnion('type', [
|
|
92
|
+
ContentExplicitString,
|
|
93
|
+
ContentExplicitBase64,
|
|
94
|
+
ContentRelative,
|
|
95
|
+
ContentAbsoluteFile
|
|
96
|
+
]);
|
|
97
|
+
export type ContentAnyLocal = z.infer<typeof ContentAnyLocal>;
|
|
98
|
+
|
|
99
|
+
export const ContentAnyRemote = z.discriminatedUnion('type', [
|
|
100
|
+
ContentExplicitString,
|
|
101
|
+
ContentExplicitBase64,
|
|
102
|
+
ContentRelative,
|
|
103
|
+
ContentAbsoluteUrl
|
|
104
|
+
]);
|
|
105
|
+
export type ContentAnyRemote = z.infer<typeof ContentAnyRemote>;
|
|
106
|
+
|
|
107
|
+
//
|
|
108
|
+
// Narrow types with relative option
|
|
109
|
+
//
|
|
110
|
+
|
|
111
|
+
// export const ContentAnyBinaryRemote = z.discriminatedUnion('type', [
|
|
112
|
+
// ContentExplicitBase64,
|
|
113
|
+
// ContentRelative,
|
|
114
|
+
// ContentAbsoluteUrl
|
|
115
|
+
// ]);
|
|
116
|
+
// export type ContentAnyBinaryRemote = z.infer<typeof ContentAnyBinaryRemote>;
|
|
117
|
+
|
|
118
|
+
export const ContentAnyBinaryLocal = z.discriminatedUnion('type', [
|
|
119
|
+
ContentExplicitBase64,
|
|
120
|
+
ContentRelative,
|
|
121
|
+
ContentAbsoluteFile
|
|
122
|
+
]);
|
|
123
|
+
export type ContentAnyBinaryLocal = z.infer<typeof ContentAnyBinaryLocal>;
|
|
124
|
+
|
|
125
|
+
// export const ContentAnyTextRemote = z.discriminatedUnion('type', [
|
|
126
|
+
// ContentExplicitString,
|
|
127
|
+
// ContentRelative,
|
|
128
|
+
// ContentAbsoluteUrl
|
|
129
|
+
// ]);
|
|
130
|
+
// export type ContentAnyTextRemote = z.infer<typeof ContentAnyTextRemote>;
|
|
131
|
+
|
|
132
|
+
export const ContentAnyTextLocal = z.discriminatedUnion('type', [
|
|
133
|
+
ContentExplicitString,
|
|
134
|
+
ContentRelative,
|
|
135
|
+
ContentAbsoluteFile
|
|
136
|
+
]);
|
|
137
|
+
export type ContentAnyTextLocal = z.infer<typeof ContentAnyTextLocal>;
|
|
138
|
+
|
|
139
|
+
//
|
|
140
|
+
// Narrow absolute types
|
|
141
|
+
//
|
|
142
|
+
|
|
143
|
+
export const ContentAbsoluteBinaryRemote = z.discriminatedUnion('type', [
|
|
144
|
+
ContentExplicitBase64,
|
|
145
|
+
ContentAbsoluteUrl
|
|
146
|
+
]);
|
|
147
|
+
export type ContentAbsoluteBinaryRemote = z.infer<typeof ContentAbsoluteBinaryRemote>;
|
|
148
|
+
|
|
149
|
+
export const ContentAbsoluteBinaryLocal = z.discriminatedUnion('type', [
|
|
150
|
+
ContentExplicitBase64,
|
|
151
|
+
ContentAbsoluteFile
|
|
152
|
+
]);
|
|
153
|
+
export type ContentAbsoluteBinaryLocal = z.infer<typeof ContentAbsoluteBinaryLocal>;
|
|
154
|
+
|
|
155
|
+
export const ContentAbsoluteTextRemote = z.discriminatedUnion('type', [
|
|
156
|
+
ContentExplicitString,
|
|
157
|
+
ContentAbsoluteUrl
|
|
158
|
+
]);
|
|
159
|
+
export type ContentAbsoluteTextRemote = z.infer<typeof ContentAbsoluteTextRemote>;
|
|
160
|
+
|
|
161
|
+
export const ContentAbsoluteTextLocal = z.discriminatedUnion('type', [
|
|
162
|
+
ContentExplicitString,
|
|
163
|
+
ContentAbsoluteFile
|
|
164
|
+
]);
|
|
165
|
+
export type ContentAbsoluteTextLocal = z.infer<typeof ContentAbsoluteTextLocal>;
|
|
166
|
+
|
|
167
|
+
//
|
|
168
|
+
// Narrow relative types
|
|
169
|
+
//
|
|
170
|
+
|
|
171
|
+
export const ContentRelativeBinary = z.discriminatedUnion('type', [
|
|
172
|
+
ContentExplicitBase64,
|
|
173
|
+
ContentRelative
|
|
174
|
+
]);
|
|
175
|
+
export type ContentRelativeBinary = z.infer<typeof ContentRelativeBinary>;
|
|
176
|
+
|
|
177
|
+
export const ContentRelativeText = z.discriminatedUnion('type', [
|
|
178
|
+
ContentExplicitString,
|
|
179
|
+
ContentRelative
|
|
180
|
+
]);
|
|
181
|
+
export type ContentRelativeText = z.infer<typeof ContentRelativeText>;
|
|
182
|
+
|
|
183
|
+
// export function ConstructContent(
|
|
184
|
+
// contentType: 'text',
|
|
185
|
+
// contextType: 'local'
|
|
186
|
+
// ): typeof ContentAnyTextLocal;
|
|
187
|
+
// export function ConstructContent(
|
|
188
|
+
// contentType: 'text',
|
|
189
|
+
// contextType: 'remote'
|
|
190
|
+
// ): typeof ContentAnyTextRemote;
|
|
191
|
+
// export function ConstructContent(
|
|
192
|
+
// contentType: 'binary',
|
|
193
|
+
// contextType: 'local'
|
|
194
|
+
// ): typeof ContentAnyBinaryLocal;
|
|
195
|
+
// export function ConstructContent(
|
|
196
|
+
// contentType: 'binary',
|
|
197
|
+
// contextType: 'remote'
|
|
198
|
+
// ): typeof ContentAnyBinaryRemote;
|
|
199
|
+
// export function ConstructContent(
|
|
200
|
+
// contentType: ContentType,
|
|
201
|
+
// contextType: ContextType
|
|
202
|
+
// ):
|
|
203
|
+
// | typeof ContentAnyTextLocal
|
|
204
|
+
// | typeof ContentAnyTextRemote
|
|
205
|
+
// | typeof ContentAnyBinaryLocal
|
|
206
|
+
// | typeof ContentAnyBinaryRemote;
|
|
207
|
+
// export function ConstructContent(contentType: ContentType, contextType: ContextType) {
|
|
208
|
+
// return contentType === 'text'
|
|
209
|
+
// ? contextType === 'local'
|
|
210
|
+
// ? ContentAnyTextLocal
|
|
211
|
+
// : ContentAnyTextRemote
|
|
212
|
+
// : contextType === 'local'
|
|
213
|
+
// ? ContentAnyBinaryLocal
|
|
214
|
+
// : ContentAnyBinaryRemote;
|
|
215
|
+
// }
|
|
216
|
+
|
|
217
|
+
export const DescriptionContentBinary = z.union([
|
|
218
|
+
z
|
|
219
|
+
.string()
|
|
220
|
+
.startsWith('file:')
|
|
221
|
+
.transform<ContentRelativeBinary>((value, ctx) => ({ type: 'relative', path: value.slice(5) })),
|
|
222
|
+
ContentAnyBinaryLocal
|
|
223
|
+
]);
|
|
224
|
+
export type DescriptionContentBinary = z.infer<typeof DescriptionContentBinary>;
|
|
225
|
+
|
|
226
|
+
export const DescriptionContentText = z.union([
|
|
227
|
+
z.string().transform<ContentRelativeText>((value, ctx) => {
|
|
228
|
+
if (value.startsWith('file:')) return { type: 'relative', path: value.slice(5) };
|
|
229
|
+
else return { type: 'explicit-string', content: value };
|
|
230
|
+
}),
|
|
231
|
+
ContentAnyTextLocal
|
|
232
|
+
]);
|
|
233
|
+
export type DescriptionContentText = z.infer<typeof DescriptionContentText>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { BlockComponentsConsolidate, BlockComponentsDescription } from './block_components';
|
|
3
|
+
import {
|
|
4
|
+
BlockComponentsManifest,
|
|
5
|
+
BlockPackMetaManifest,
|
|
6
|
+
CreateBlockPackDescriptionSchema
|
|
7
|
+
} from '@milaboratories/pl-model-middle-layer';
|
|
8
|
+
import { BlockPackMetaConsolidate, BlockPackMetaDescription } from './meta';
|
|
9
|
+
|
|
10
|
+
export * from './block_components';
|
|
11
|
+
export * from './content_conversion';
|
|
12
|
+
|
|
13
|
+
export function ResolvedBlockPackDescriptionFromPackageJson(root: string) {
|
|
14
|
+
return CreateBlockPackDescriptionSchema(
|
|
15
|
+
BlockComponentsDescription(root),
|
|
16
|
+
BlockPackMetaDescription(root)
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
export type BlockPackDescriptionAbsolute = z.infer<
|
|
20
|
+
ReturnType<typeof ResolvedBlockPackDescriptionFromPackageJson>
|
|
21
|
+
>;
|
|
22
|
+
|
|
23
|
+
export function BlockPackDescriptionConsolidateToFolder(
|
|
24
|
+
dstFolder: string,
|
|
25
|
+
fileAccumulator?: string[]
|
|
26
|
+
) {
|
|
27
|
+
return CreateBlockPackDescriptionSchema(
|
|
28
|
+
BlockComponentsConsolidate(dstFolder, fileAccumulator),
|
|
29
|
+
//BlockPackMetaToExplicit
|
|
30
|
+
BlockPackMetaConsolidate(dstFolder, fileAccumulator)
|
|
31
|
+
).pipe(BlockPackDescriptionManifest);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const BlockPackDescriptionManifest = CreateBlockPackDescriptionSchema(
|
|
35
|
+
BlockComponentsManifest,
|
|
36
|
+
BlockPackMetaManifest
|
|
37
|
+
);
|
|
38
|
+
export type BlockPackDescriptionManifest = z.infer<typeof BlockPackDescriptionManifest>;
|
|
39
|
+
|
|
40
|
+
export const BlockPackManifest = BlockPackDescriptionManifest.extend({
|
|
41
|
+
schema: z.literal('v1'),
|
|
42
|
+
files: z.array(z.string())
|
|
43
|
+
});
|
|
44
|
+
export type BlockPackManifest = z.infer<typeof BlockPackManifest>;
|
|
45
|
+
|
|
46
|
+
export const BlockPackManifestFile = 'manifest.json';
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BlockPackMeta,
|
|
3
|
+
ContentAbsoluteBinaryLocal,
|
|
4
|
+
ContentAbsoluteTextLocal,
|
|
5
|
+
DescriptionContentBinary,
|
|
6
|
+
DescriptionContentText
|
|
7
|
+
} from '@milaboratories/pl-model-middle-layer';
|
|
8
|
+
import {
|
|
9
|
+
absoluteToBase64,
|
|
10
|
+
absoluteToString,
|
|
11
|
+
cpAbsoluteToRelative,
|
|
12
|
+
mapLocalToAbsolute
|
|
13
|
+
} from './content_conversion';
|
|
14
|
+
import { z } from 'zod';
|
|
15
|
+
import { BlockPackMetaEmbeddedContent } from '@milaboratories/pl-model-middle-layer';
|
|
16
|
+
|
|
17
|
+
export function BlockPackMetaDescription(root: string) {
|
|
18
|
+
return BlockPackMeta(
|
|
19
|
+
DescriptionContentText.transform(mapLocalToAbsolute(root)),
|
|
20
|
+
DescriptionContentBinary.transform(mapLocalToAbsolute(root))
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
export type BlockPackMetaDescription = z.infer<ReturnType<typeof BlockPackMetaDescription>>;
|
|
24
|
+
|
|
25
|
+
export function BlockPackMetaConsolidate(dstFolder: string, fileAccumulator?: string[]) {
|
|
26
|
+
return BlockPackMeta(
|
|
27
|
+
ContentAbsoluteTextLocal.transform(cpAbsoluteToRelative(dstFolder, fileAccumulator)),
|
|
28
|
+
ContentAbsoluteBinaryLocal.transform(cpAbsoluteToRelative(dstFolder, fileAccumulator))
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const BlockPackMetaEmbed = BlockPackMeta(
|
|
33
|
+
ContentAbsoluteTextLocal.transform(absoluteToString()),
|
|
34
|
+
ContentAbsoluteBinaryLocal.transform(absoluteToBase64())
|
|
35
|
+
).pipe(BlockPackMetaEmbeddedContent);
|
|
36
|
+
export type BlockPackMetaEmbed = z.infer<typeof BlockPackMetaEmbed>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { BlockPackId, BlockPackIdNoVersion } from '@milaboratories/pl-model-middle-layer';
|
|
2
|
+
|
|
3
|
+
const MainPrefix = 'v2/';
|
|
4
|
+
|
|
5
|
+
export function packageContentPrefix(bp: BlockPackId): string {
|
|
6
|
+
return `${MainPrefix}${bp.organization}/${bp.name}/${bp.version}`;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function payloadFilePath(bp: BlockPackId, file: string): string {
|
|
10
|
+
return `${MainPrefix}${bp.organization}/${bp.name}/${bp.version}/${file}`;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function packageOverviewPath(bp: BlockPackIdNoVersion): string {
|
|
14
|
+
return `${MainPrefix}${bp.organization}/${bp.name}/overview.json`;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const GlobalOverviewPath = `${MainPrefix}overview.json`;
|
|
18
|
+
|
|
19
|
+
export const ManifestFile = 'manifest.json';
|
|
20
|
+
|
|
21
|
+
export interface GlobalOverviewEntry {
|
|
22
|
+
organization: string;
|
|
23
|
+
package: string;
|
|
24
|
+
allVersions: string[];
|
|
25
|
+
latestVersion: string;
|
|
26
|
+
latestMeta: object;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type GlobalOverview = GlobalOverviewEntry[];
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { loadPackDescription, parsePackageName } from './source_package';
|
|
2
|
+
|
|
3
|
+
test('test parsing of convention package names', () => {
|
|
4
|
+
expect(parsePackageName('@milaboratory/milaboratories.block-template')).toStrictEqual({
|
|
5
|
+
organization: 'milaboratories',
|
|
6
|
+
name: 'block-template'
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
expect(parsePackageName('milaboratories.block-template')).toStrictEqual({
|
|
10
|
+
organization: 'milaboratories',
|
|
11
|
+
name: 'block-template'
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
expect(parsePackageName('mi-laboratories.block-template')).toStrictEqual({
|
|
15
|
+
organization: 'mi-laboratories',
|
|
16
|
+
name: 'block-template'
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
expect(() => parsePackageName('block-template')).toThrow(/Malformed/);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test.skip('full description parsing test', async () => {
|
|
23
|
+
const description = await loadPackDescription(
|
|
24
|
+
'/Volumes/Data/Projects/MiLaboratory/blocks-beta/block-template'
|
|
25
|
+
);
|
|
26
|
+
console.dir(description, { depth: 5 });
|
|
27
|
+
});
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { tryLoadFile } from '../util';
|
|
3
|
+
import { ResolvedBlockPackDescriptionFromPackageJson, BlockPackDescriptionAbsolute } from './model';
|
|
4
|
+
import { notEmpty } from '@milaboratories/ts-helpers';
|
|
5
|
+
import fsp from 'node:fs/promises';
|
|
6
|
+
import {
|
|
7
|
+
BlockPackDescriptionFromPackageJsonRaw,
|
|
8
|
+
BlockPackDescriptionRaw,
|
|
9
|
+
BlockPackId,
|
|
10
|
+
BlockPackMetaDescriptionRaw,
|
|
11
|
+
SemVer
|
|
12
|
+
} from '@milaboratories/pl-model-middle-layer';
|
|
13
|
+
|
|
14
|
+
export const BlockDescriptionPackageJsonField = 'block';
|
|
15
|
+
|
|
16
|
+
const ConventionPackageNamePattern =
|
|
17
|
+
/(?:@[a-zA-Z0-9-.]+\/)?(?<organization>[a-zA-Z0-9-]+)\.(?<name>[a-zA-Z0-9-]+)/;
|
|
18
|
+
|
|
19
|
+
export function parsePackageName(packageName: string): Pick<BlockPackId, 'organization' | 'name'> {
|
|
20
|
+
const match = packageName.match(ConventionPackageNamePattern);
|
|
21
|
+
if (!match)
|
|
22
|
+
throw new Error(
|
|
23
|
+
`Malformed package name (${packageName}), can't infer organization and block pack name.`
|
|
24
|
+
);
|
|
25
|
+
const { name, organization } = match.groups!;
|
|
26
|
+
return { name, organization };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function tryLoadPackDescription(
|
|
30
|
+
moduleRoot: string
|
|
31
|
+
): Promise<BlockPackDescriptionAbsolute | undefined> {
|
|
32
|
+
const fullPackageJsonPath = path.resolve(moduleRoot, 'package.json');
|
|
33
|
+
try {
|
|
34
|
+
const packageJson = await tryLoadFile(fullPackageJsonPath, (buf) =>
|
|
35
|
+
JSON.parse(buf.toString('utf-8'))
|
|
36
|
+
);
|
|
37
|
+
if (packageJson === undefined) return undefined;
|
|
38
|
+
const descriptionNotParsed = packageJson[BlockDescriptionPackageJsonField];
|
|
39
|
+
if (descriptionNotParsed === undefined) return undefined;
|
|
40
|
+
const descriptionRaw = {
|
|
41
|
+
...BlockPackDescriptionFromPackageJsonRaw.parse(descriptionNotParsed),
|
|
42
|
+
id: {
|
|
43
|
+
...parsePackageName(
|
|
44
|
+
notEmpty(packageJson['name'], `"name" not found in ${fullPackageJsonPath}`)
|
|
45
|
+
),
|
|
46
|
+
version: SemVer.parse(packageJson['version'])
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
const descriptionParsingResult =
|
|
50
|
+
await ResolvedBlockPackDescriptionFromPackageJson(moduleRoot).safeParseAsync(descriptionRaw);
|
|
51
|
+
if (descriptionParsingResult.success) return descriptionParsingResult.data;
|
|
52
|
+
return undefined;
|
|
53
|
+
} catch (e: any) {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export async function loadPackDescriptionRaw(moduleRoot: string): Promise<BlockPackDescriptionRaw> {
|
|
59
|
+
const fullPackageJsonPath = path.resolve(moduleRoot, 'package.json');
|
|
60
|
+
const packageJson = JSON.parse(await fsp.readFile(fullPackageJsonPath, { encoding: 'utf-8' }));
|
|
61
|
+
const descriptionNotParsed = packageJson[BlockDescriptionPackageJsonField];
|
|
62
|
+
if (descriptionNotParsed === undefined)
|
|
63
|
+
throw new Error(
|
|
64
|
+
`Block description (field ${BlockDescriptionPackageJsonField}) not found in ${fullPackageJsonPath}.`
|
|
65
|
+
);
|
|
66
|
+
return {
|
|
67
|
+
...BlockPackDescriptionFromPackageJsonRaw.parse(descriptionNotParsed),
|
|
68
|
+
id: {
|
|
69
|
+
...parsePackageName(
|
|
70
|
+
notEmpty(packageJson['name'], `"name" not found in ${fullPackageJsonPath}`)
|
|
71
|
+
),
|
|
72
|
+
version: SemVer.parse(packageJson['version'])
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export async function loadPackDescription(
|
|
78
|
+
moduleRoot: string
|
|
79
|
+
): Promise<BlockPackDescriptionAbsolute> {
|
|
80
|
+
const descriptionRaw = await loadPackDescriptionRaw(moduleRoot);
|
|
81
|
+
return await ResolvedBlockPackDescriptionFromPackageJson(moduleRoot).parseAsync(descriptionRaw);
|
|
82
|
+
}
|