@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.
Files changed (100) hide show
  1. package/dist/cli.js +1 -756
  2. package/dist/cli.js.map +1 -1
  3. package/dist/cli.mjs +187 -0
  4. package/dist/cli.mjs.map +1 -0
  5. package/dist/cmd/build-meta.d.ts +10 -0
  6. package/dist/cmd/build-meta.d.ts.map +1 -0
  7. package/dist/cmd/build-model.d.ts +11 -0
  8. package/dist/cmd/build-model.d.ts.map +1 -0
  9. package/dist/cmd/index.d.ts +11 -0
  10. package/dist/cmd/index.d.ts.map +1 -0
  11. package/dist/cmd/pack-block.d.ts +10 -0
  12. package/dist/cmd/pack-block.d.ts.map +1 -0
  13. package/dist/cmd/upload-package-v1.d.ts +15 -0
  14. package/dist/cmd/upload-package-v1.d.ts.map +1 -0
  15. package/dist/common_types.d.ts +3 -0
  16. package/dist/common_types.d.ts.map +1 -0
  17. package/dist/config-BJognM_j.mjs +536 -0
  18. package/dist/config-BJognM_j.mjs.map +1 -0
  19. package/dist/config-CfA0Dj6h.js +3 -0
  20. package/dist/config-CfA0Dj6h.js.map +1 -0
  21. package/dist/index.js +2 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/index.mjs +43 -0
  24. package/dist/index.mjs.map +1 -0
  25. package/dist/lib/storage.d.ts +29 -0
  26. package/dist/lib/storage.d.ts.map +1 -0
  27. package/dist/lib.d.ts +3 -2637
  28. package/dist/lib.d.ts.map +1 -0
  29. package/dist/registry_v1/config.d.ts +12 -0
  30. package/dist/registry_v1/config.d.ts.map +1 -0
  31. package/dist/registry_v1/config_schema.d.ts +94 -0
  32. package/dist/registry_v1/config_schema.d.ts.map +1 -0
  33. package/dist/registry_v1/flags.d.ts +9 -0
  34. package/dist/registry_v1/flags.d.ts.map +1 -0
  35. package/dist/registry_v1/index.d.ts +4 -0
  36. package/dist/registry_v1/index.d.ts.map +1 -0
  37. package/dist/registry_v1/registry.d.ts +46 -0
  38. package/dist/registry_v1/registry.d.ts.map +1 -0
  39. package/dist/registry_v1/v1_repo_schema.d.ts +25 -0
  40. package/dist/registry_v1/v1_repo_schema.d.ts.map +1 -0
  41. package/dist/util.d.ts +4 -0
  42. package/dist/util.d.ts.map +1 -0
  43. package/dist/v2/build_dist.d.ts +3 -0
  44. package/dist/v2/build_dist.d.ts.map +1 -0
  45. package/dist/v2/index.d.ts +4 -0
  46. package/dist/v2/index.d.ts.map +1 -0
  47. package/dist/v2/model/block_components.d.ts +384 -0
  48. package/dist/v2/model/block_components.d.ts.map +1 -0
  49. package/dist/v2/model/common.d.ts +3 -0
  50. package/dist/v2/model/common.d.ts.map +1 -0
  51. package/dist/v2/model/content_conversion.d.ts +35 -0
  52. package/dist/v2/model/content_conversion.d.ts.map +1 -0
  53. package/dist/v2/model/content_types.d.ts +478 -0
  54. package/dist/v2/model/content_types.d.ts.map +1 -0
  55. package/dist/{lib.d.cts → v2/model/index.d.ts} +449 -1005
  56. package/dist/v2/model/index.d.ts.map +1 -0
  57. package/dist/v2/model/meta.d.ts +805 -0
  58. package/dist/v2/model/meta.d.ts.map +1 -0
  59. package/dist/v2/registry/schema.d.ts +15 -0
  60. package/dist/v2/registry/schema.d.ts.map +1 -0
  61. package/dist/v2/source_package.d.ts +8 -0
  62. package/dist/v2/source_package.d.ts.map +1 -0
  63. package/package.json +21 -17
  64. package/src/cmd/build-meta.ts +38 -0
  65. package/src/cmd/build-model.ts +76 -0
  66. package/src/cmd/index.ts +12 -0
  67. package/src/cmd/pack-block.ts +32 -0
  68. package/src/cmd/upload-package-v1.ts +105 -0
  69. package/src/common_types.ts +3 -0
  70. package/src/lib/storage.test.ts +91 -0
  71. package/src/lib/storage.ts +140 -0
  72. package/src/lib.ts +2 -0
  73. package/src/registry_v1/config.ts +90 -0
  74. package/src/registry_v1/config_schema.ts +30 -0
  75. package/src/registry_v1/flags.ts +23 -0
  76. package/src/registry_v1/index.ts +3 -0
  77. package/src/registry_v1/registry.test.ts +122 -0
  78. package/src/registry_v1/registry.ts +253 -0
  79. package/src/registry_v1/v1_repo_schema.ts +42 -0
  80. package/src/util.ts +25 -0
  81. package/src/v2/build_dist.test.ts +16 -0
  82. package/src/v2/build_dist.ts +29 -0
  83. package/src/v2/index.ts +3 -0
  84. package/src/v2/model/block_components.ts +32 -0
  85. package/src/v2/model/common.ts +2 -0
  86. package/src/v2/model/content_conversion.ts +178 -0
  87. package/src/v2/model/content_types.ts +233 -0
  88. package/src/v2/model/index.ts +46 -0
  89. package/src/v2/model/meta.ts +36 -0
  90. package/src/v2/registry/schema.ts +29 -0
  91. package/src/v2/source_package.test.ts +27 -0
  92. package/src/v2/source_package.ts +82 -0
  93. package/dist/cli.cjs +0 -786
  94. package/dist/cli.cjs.map +0 -1
  95. package/dist/cli.d.cts +0 -58
  96. package/dist/cli.d.ts +0 -58
  97. package/dist/lib.cjs +0 -629
  98. package/dist/lib.cjs.map +0 -1
  99. package/dist/lib.js +0 -577
  100. 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
+ }