nuxt-google-sheets-import 0.1.4 → 0.1.7
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/README.md +110 -123
- package/dist/module.d.mts +0 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +21 -18
- package/dist/runtime/components/GoogleSheetsImportExecute.vue +3 -0
- package/dist/runtime/components/GoogleSheetsImportExport.d.vue.ts +3 -0
- package/dist/runtime/components/GoogleSheetsImportExport.vue +196 -0
- package/dist/runtime/components/GoogleSheetsImportExport.vue.d.ts +3 -0
- package/dist/runtime/components/GoogleSheetsImportSchemaGuide.vue +29 -48
- package/dist/runtime/components/GoogleSheetsImportSource.vue +3 -0
- package/dist/runtime/pages/google-sheets-import.vue +34 -3
- package/dist/runtime/server/api/collection-type.get.js +1 -2
- package/dist/runtime/server/api/schema-columns.get.js +5 -6
- package/dist/runtime/server/api/values.post.js +2 -3
- package/dist/runtime/server/api/write.post.js +2 -3
- package/dist/runtime/server/utils/collectionType.d.ts +1 -1
- package/dist/runtime/server/utils/collectionType.js +39 -30
- package/dist/runtime/server/utils/schemaColumns.js +5 -2
- package/dist/runtime/server/utils/transform.js +1 -42
- package/dist/runtime/utils/clipboard.d.ts +15 -0
- package/dist/runtime/utils/clipboard.js +12 -0
- package/dist/runtime/utils/delimited.d.ts +2 -0
- package/dist/runtime/utils/delimited.js +9 -0
- package/dist/runtime/utils/pathMapping.d.ts +4 -0
- package/dist/runtime/utils/pathMapping.js +86 -0
- package/package.json +1 -1
- package/dist/runtime/import/schemas.d.ts +0 -67
- package/dist/runtime/import/schemas.js +0 -35
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { computed, onMounted, ref, watch } from "vue";
|
|
3
|
+
import { useToast } from "#imports";
|
|
3
4
|
import { useGoogleSheetsImport } from "../composables/useGoogleSheetsImport";
|
|
5
|
+
import { copyTextWithSuccessToast } from "../utils/clipboard";
|
|
6
|
+
import { toTsvRow } from "../utils/delimited";
|
|
4
7
|
const props = defineProps({
|
|
5
8
|
initialSchema: { type: String, required: false, default: "" }
|
|
6
9
|
});
|
|
@@ -60,58 +63,36 @@ onMounted(async () => {
|
|
|
60
63
|
await loadColumns(selectedSchema.value);
|
|
61
64
|
}
|
|
62
65
|
});
|
|
63
|
-
function copyColumns() {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
navigator.clipboard.writeText(content);
|
|
69
|
-
toast.add({
|
|
70
|
-
title: "Copied",
|
|
66
|
+
async function copyColumns() {
|
|
67
|
+
await copyTextWithSuccessToast({
|
|
68
|
+
text: columns.value.join("\n"),
|
|
69
|
+
toast,
|
|
71
70
|
description: "Column names copied to clipboard.",
|
|
72
|
-
|
|
71
|
+
title: "Copied"
|
|
73
72
|
});
|
|
74
73
|
}
|
|
75
|
-
function
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
function copyColumnsCsv() {
|
|
82
|
-
const content = csvRow(columns.value);
|
|
83
|
-
if (!content) {
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
navigator.clipboard.writeText(content);
|
|
87
|
-
toast.add({
|
|
88
|
-
title: "Copied",
|
|
89
|
-
description: "Column names copied as a CSV row.",
|
|
90
|
-
color: "success"
|
|
74
|
+
async function copyColumnsTsv() {
|
|
75
|
+
await copyTextWithSuccessToast({
|
|
76
|
+
text: toTsvRow(columns.value),
|
|
77
|
+
toast,
|
|
78
|
+
description: "Column names copied as a tab-separated row for Google Sheets.",
|
|
79
|
+
title: "Copied"
|
|
91
80
|
});
|
|
92
81
|
}
|
|
93
|
-
function copyPageOverrideColumns() {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
98
|
-
navigator.clipboard.writeText(content);
|
|
99
|
-
toast.add({
|
|
100
|
-
title: "Copied",
|
|
82
|
+
async function copyPageOverrideColumns() {
|
|
83
|
+
await copyTextWithSuccessToast({
|
|
84
|
+
text: pageOverrideColumns.value.join("\n"),
|
|
85
|
+
toast,
|
|
101
86
|
description: "Page override column names copied to clipboard.",
|
|
102
|
-
|
|
87
|
+
title: "Copied"
|
|
103
88
|
});
|
|
104
89
|
}
|
|
105
|
-
function
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
toast.add({
|
|
112
|
-
title: "Copied",
|
|
113
|
-
description: "Page override column names copied as a CSV row.",
|
|
114
|
-
color: "success"
|
|
90
|
+
async function copyPageOverrideColumnsTsv() {
|
|
91
|
+
await copyTextWithSuccessToast({
|
|
92
|
+
text: toTsvRow(pageOverrideColumns.value),
|
|
93
|
+
toast,
|
|
94
|
+
description: "Page override column names copied as a tab-separated row for Google Sheets.",
|
|
95
|
+
title: "Copied"
|
|
115
96
|
});
|
|
116
97
|
}
|
|
117
98
|
</script>
|
|
@@ -169,11 +150,11 @@ function copyPageOverrideColumnsCsv() {
|
|
|
169
150
|
@click="copyColumns"
|
|
170
151
|
/>
|
|
171
152
|
<UButton
|
|
172
|
-
label="Copy as
|
|
153
|
+
label="Copy as tab-separated row"
|
|
173
154
|
icon="i-heroicons-table-cells-20-solid"
|
|
174
155
|
color="neutral"
|
|
175
156
|
variant="subtle"
|
|
176
|
-
@click="
|
|
157
|
+
@click="copyColumnsTsv"
|
|
177
158
|
/>
|
|
178
159
|
</div>
|
|
179
160
|
<pre class="text-xs whitespace-pre-wrap">{{ columns.join("\n") }}</pre>
|
|
@@ -199,11 +180,11 @@ function copyPageOverrideColumnsCsv() {
|
|
|
199
180
|
@click="copyPageOverrideColumns"
|
|
200
181
|
/>
|
|
201
182
|
<UButton
|
|
202
|
-
label="Copy overrides as
|
|
183
|
+
label="Copy overrides as tab-separated row"
|
|
203
184
|
icon="i-heroicons-table-cells-20-solid"
|
|
204
185
|
color="neutral"
|
|
205
186
|
variant="subtle"
|
|
206
|
-
@click="
|
|
187
|
+
@click="copyPageOverrideColumnsTsv"
|
|
207
188
|
/>
|
|
208
189
|
</div>
|
|
209
190
|
<pre class="text-xs whitespace-pre-wrap">{{ pageOverrideColumns.join("\n") }}</pre>
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
import { useGoogleSheetsImportWorkflow } from "../composables/useGoogleSheetsImportWorkflow";
|
|
4
|
+
import { useToast } from "#imports";
|
|
5
|
+
import { watch } from "vue";
|
|
3
6
|
const { googleSheets } = defineProps({
|
|
4
7
|
googleSheets: { type: Array, required: true }
|
|
5
8
|
});
|
|
@@ -4,11 +4,42 @@ const sheetsList = ref([
|
|
|
4
4
|
{ id: "1NKS0cTX6u5urtgQ3Q4Z2motiR2-9JmyPxcd05yVc1bc", label: "Metzner" },
|
|
5
5
|
{ id: "1tGZCEoiikXfg3mOpfVWWTS1SSSsj18xv6Z3owrnnt4s", label: "Example Sheet" }
|
|
6
6
|
]);
|
|
7
|
+
const items = ref([
|
|
8
|
+
{
|
|
9
|
+
label: "Setup Google Sheet",
|
|
10
|
+
icon: "i-lucide-layout-dashboard",
|
|
11
|
+
slot: "sheet"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
label: "Import data",
|
|
15
|
+
icon: "i-lucide-upload",
|
|
16
|
+
slot: "import"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
label: "Export data",
|
|
20
|
+
icon: "i-lucide-download",
|
|
21
|
+
slot: "export"
|
|
22
|
+
}
|
|
23
|
+
]);
|
|
7
24
|
</script>
|
|
8
25
|
|
|
9
26
|
<template>
|
|
10
|
-
<UContainer>
|
|
11
|
-
<
|
|
12
|
-
|
|
27
|
+
<UContainer class="max-w-36">
|
|
28
|
+
<UTabs
|
|
29
|
+
:items="items"
|
|
30
|
+
variant="link"
|
|
31
|
+
color="neutral"
|
|
32
|
+
class="w-full"
|
|
33
|
+
>
|
|
34
|
+
<template #sheet>
|
|
35
|
+
<GoogleSheetsImportSchemaGuide :initial-schema="'Example Sheet'" />
|
|
36
|
+
</template>
|
|
37
|
+
<template #import>
|
|
38
|
+
<GoogleSheetsImportSource :google-sheets="sheetsList" />
|
|
39
|
+
</template>
|
|
40
|
+
<template #export>
|
|
41
|
+
<GoogleSheetsImportExport />
|
|
42
|
+
</template>
|
|
43
|
+
</UTabs>
|
|
13
44
|
</UContainer>
|
|
14
45
|
</template>
|
|
@@ -9,8 +9,7 @@ export default defineEventHandler(async (event) => {
|
|
|
9
9
|
const { schema } = await getValidatedQuery(event, (query) => querySchema.parse(query));
|
|
10
10
|
const config = useRuntimeConfig(event);
|
|
11
11
|
const moduleConfig = config.googleSheetsImport;
|
|
12
|
-
const
|
|
13
|
-
const collectionType = await resolveCollectionTypeBySchema(schema, fromConfig);
|
|
12
|
+
const collectionType = await resolveCollectionTypeBySchema(schema);
|
|
14
13
|
const baseContentDir = collectionType === "page" ? "content" : collectionType === "data" ? "content/data" : moduleConfig.defaultContentDir;
|
|
15
14
|
return {
|
|
16
15
|
schema: schema ?? null,
|
|
@@ -2,15 +2,13 @@ import { z } from "zod";
|
|
|
2
2
|
import { resolveCollectionTypeBySchema } from "../utils/collectionType.js";
|
|
3
3
|
import { getSchemaColumns, PAGE_SCHEMA_OVERRIDE_COLUMNS } from "../utils/schemaColumns.js";
|
|
4
4
|
import { getValidatedQuery, defineEventHandler, createError } from "h3";
|
|
5
|
-
import {
|
|
5
|
+
import { googleSheetsImportSchemas } from "#imports";
|
|
6
6
|
const querySchema = z.object({
|
|
7
7
|
schema: z.string().optional()
|
|
8
8
|
});
|
|
9
9
|
export default defineEventHandler(async (event) => {
|
|
10
10
|
const { schema } = await getValidatedQuery(event, (query) => querySchema.parse(query));
|
|
11
|
-
const
|
|
12
|
-
const moduleConfig = config.googleSheetsImport;
|
|
13
|
-
const schemaMap = {};
|
|
11
|
+
const schemaMap = googleSheetsImportSchemas ?? {};
|
|
14
12
|
const availableSchemas = Object.keys(schemaMap).sort((left, right) => left.localeCompare(right));
|
|
15
13
|
if (!schema) {
|
|
16
14
|
return {
|
|
@@ -21,7 +19,7 @@ export default defineEventHandler(async (event) => {
|
|
|
21
19
|
pageOverrideColumns: []
|
|
22
20
|
};
|
|
23
21
|
}
|
|
24
|
-
const collectionType = await resolveCollectionTypeBySchema(schema
|
|
22
|
+
const collectionType = await resolveCollectionTypeBySchema(schema);
|
|
25
23
|
const selectedSchema = schemaMap[schema];
|
|
26
24
|
if (!selectedSchema) {
|
|
27
25
|
throw createError({
|
|
@@ -29,10 +27,11 @@ export default defineEventHandler(async (event) => {
|
|
|
29
27
|
statusMessage: `Unknown schema: ${schema}`
|
|
30
28
|
});
|
|
31
29
|
}
|
|
30
|
+
const columns = getSchemaColumns(selectedSchema);
|
|
32
31
|
return {
|
|
33
32
|
schema,
|
|
34
33
|
schemas: availableSchemas,
|
|
35
|
-
columns
|
|
34
|
+
columns,
|
|
36
35
|
collectionType,
|
|
37
36
|
pageOverrideColumns: collectionType === "page" ? PAGE_SCHEMA_OVERRIDE_COLUMNS : []
|
|
38
37
|
};
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { transformAndValidateRows } from "../utils/transform.js";
|
|
3
3
|
import { readBody, defineEventHandler, createError } from "h3";
|
|
4
|
-
import { useRuntimeConfig } from "#imports";
|
|
5
|
-
import { schemas } from "../../import/schemas.js";
|
|
4
|
+
import { useRuntimeConfig, googleSheetsImportSchemas } from "#imports";
|
|
6
5
|
const bodySchema = z.object({
|
|
7
6
|
spreadsheetId: z.string().length(44),
|
|
8
7
|
sheetTitle: z.string().min(1),
|
|
@@ -19,7 +18,7 @@ export default defineEventHandler(async (event) => {
|
|
|
19
18
|
const encodedRange = encodeURIComponent(`${body.sheetTitle}!${body.range}`);
|
|
20
19
|
const googleResponse = await $fetch(`https://sheets.googleapis.com/v4/spreadsheets/${body.spreadsheetId}/values/${encodedRange}?key=${apiKey}`);
|
|
21
20
|
const values = googleResponse.values ?? [];
|
|
22
|
-
const schemaMap =
|
|
21
|
+
const schemaMap = googleSheetsImportSchemas ?? {};
|
|
23
22
|
const schema = schemaMap[body.schema];
|
|
24
23
|
if (!schema) {
|
|
25
24
|
throw createError({ statusCode: 400, statusMessage: `Unknown schema: ${body.schema}` });
|
|
@@ -25,7 +25,7 @@ function resolveContentDirByCollectionType(collectionType, schemaKey, fallback)
|
|
|
25
25
|
}
|
|
26
26
|
const normalizedSchemaKey = schemaKey.trim();
|
|
27
27
|
console.warn(
|
|
28
|
-
`[google-sheets-import] No collection type mapping found for schema "${normalizedSchemaKey}". Using fallback content directory "${fallback}".
|
|
28
|
+
`[google-sheets-import] No collection type mapping found for schema "${normalizedSchemaKey}". Using fallback content directory "${fallback}". Ensure the matching Nuxt Content collection is defined with type "page" or "data" in content.config.ts.`
|
|
29
29
|
);
|
|
30
30
|
return fallback;
|
|
31
31
|
}
|
|
@@ -33,8 +33,7 @@ export default defineEventHandler(async (event) => {
|
|
|
33
33
|
const body = bodySchema.parse(await readBody(event));
|
|
34
34
|
const config = useRuntimeConfig(event);
|
|
35
35
|
const moduleConfig = config.googleSheetsImport;
|
|
36
|
-
const
|
|
37
|
-
const resolvedCollectionType = await resolveCollectionTypeBySchema(body.schema, mappedInConfig);
|
|
36
|
+
const resolvedCollectionType = await resolveCollectionTypeBySchema(body.schema);
|
|
38
37
|
const resolvedContentDir = body.contentDir ?? resolveContentDirByCollectionType(resolvedCollectionType, body.schema, moduleConfig.defaultContentDir);
|
|
39
38
|
const logs = [];
|
|
40
39
|
const summary = {
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
type CollectionType = 'page' | 'data';
|
|
2
|
-
export declare function resolveCollectionTypeBySchema(schemaKey: string | undefined
|
|
2
|
+
export declare function resolveCollectionTypeBySchema(schemaKey: string | undefined): Promise<CollectionType | 'unknown'>;
|
|
3
3
|
export {};
|
|
@@ -12,46 +12,55 @@ function setCollectionType(map, key, type) {
|
|
|
12
12
|
map[normalized] = type;
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
|
-
|
|
16
|
-
if (cachedCollectionTypeBySchema) {
|
|
17
|
-
return cachedCollectionTypeBySchema;
|
|
18
|
-
}
|
|
15
|
+
function parseManifestSource(source) {
|
|
19
16
|
const result = {};
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
17
|
+
const schemas = /* @__PURE__ */ new Set();
|
|
18
|
+
const defaultExportIndex = source.indexOf("export default");
|
|
19
|
+
const manifestCollectionsSource = defaultExportIndex === -1 ? source : source.slice(defaultExportIndex);
|
|
20
|
+
const collectionTypeMatches = manifestCollectionsSource.matchAll(/"([^"\n]+)"\s*:\s*\{[\s\S]{0,500}?"type"\s*:\s*"(page|data)"/g);
|
|
21
|
+
for (const match of collectionTypeMatches) {
|
|
22
|
+
const collectionName = match[1];
|
|
23
|
+
const collectionType = match[2];
|
|
24
|
+
if (collectionName) {
|
|
25
|
+
schemas.add(collectionName);
|
|
26
|
+
setCollectionType(result, collectionName, collectionType);
|
|
30
27
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
collectionTypeBySchema: result,
|
|
31
|
+
schemas: Array.from(schemas).sort((left, right) => left.localeCompare(right))
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
async function readManifestData() {
|
|
35
|
+
const candidates = [
|
|
36
|
+
path.resolve(process.cwd(), ".nuxt/content/manifest.ts"),
|
|
37
|
+
path.resolve(process.cwd(), ".nuxt/content/manifest.mjs")
|
|
38
|
+
];
|
|
39
|
+
for (const manifestPath of candidates) {
|
|
40
|
+
try {
|
|
41
|
+
const source = await readFile(manifestPath, "utf-8");
|
|
42
|
+
return parseManifestSource(source);
|
|
43
|
+
} catch {
|
|
38
44
|
}
|
|
39
|
-
}
|
|
40
|
-
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
collectionTypeBySchema: {},
|
|
48
|
+
schemas: []
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
async function readCollectionTypeBySchema() {
|
|
52
|
+
if (cachedCollectionTypeBySchema) {
|
|
41
53
|
return cachedCollectionTypeBySchema;
|
|
42
54
|
}
|
|
43
|
-
|
|
55
|
+
const fromManifest = await readManifestData();
|
|
56
|
+
cachedCollectionTypeBySchema = fromManifest.collectionTypeBySchema;
|
|
44
57
|
return cachedCollectionTypeBySchema;
|
|
45
58
|
}
|
|
46
|
-
export async function resolveCollectionTypeBySchema(schemaKey
|
|
59
|
+
export async function resolveCollectionTypeBySchema(schemaKey) {
|
|
47
60
|
if (!schemaKey) {
|
|
48
61
|
return "unknown";
|
|
49
62
|
}
|
|
50
|
-
const
|
|
51
|
-
const map = {
|
|
52
|
-
...mappedInContentConfig,
|
|
53
|
-
...collectionTypeBySchemaFromConfig
|
|
54
|
-
};
|
|
63
|
+
const map = await readCollectionTypeBySchema();
|
|
55
64
|
const normalizedSchemaKey = schemaKey.trim();
|
|
56
65
|
const collectionType = normalizeSchemaKey(normalizedSchemaKey).map((key) => map[key]).find((value) => value === "page" || value === "data");
|
|
57
66
|
return collectionType ?? "unknown";
|
|
@@ -66,8 +66,11 @@ function collectSchemaColumns(schema, prefix = "") {
|
|
|
66
66
|
const arrayPrefix = `${prefix}[0]`;
|
|
67
67
|
const elementCandidate = getDefValue(unwrapped, "element");
|
|
68
68
|
const arrayElement = isZodType(elementCandidate) ? elementCandidate : null;
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
if (arrayElement && getObjectShape(arrayElement)) {
|
|
70
|
+
const nested = collectSchemaColumns(arrayElement, arrayPrefix);
|
|
71
|
+
return nested.length ? nested : [arrayPrefix];
|
|
72
|
+
}
|
|
73
|
+
return prefix ? [prefix] : [arrayPrefix];
|
|
71
74
|
}
|
|
72
75
|
const objectShape = getObjectShape(unwrapped);
|
|
73
76
|
if (objectShape) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { parseHeaderPath, setDeep } from "../../utils/pathMapping.js";
|
|
2
3
|
function isZodType(value) {
|
|
3
4
|
return typeof value === "object" && value !== null && "_def" in value;
|
|
4
5
|
}
|
|
@@ -47,48 +48,6 @@ function hasWrapper(schema, wrapper) {
|
|
|
47
48
|
}
|
|
48
49
|
return false;
|
|
49
50
|
}
|
|
50
|
-
function parseHeaderPath(header) {
|
|
51
|
-
const tokens = [];
|
|
52
|
-
const parts = header.split(".");
|
|
53
|
-
for (const part of parts) {
|
|
54
|
-
const matches = part.matchAll(/([^[]+)|(\[(\d+)\])/g);
|
|
55
|
-
for (const match of matches) {
|
|
56
|
-
if (match[1]) {
|
|
57
|
-
tokens.push(match[1]);
|
|
58
|
-
}
|
|
59
|
-
if (match[3]) {
|
|
60
|
-
tokens.push(Number.parseInt(match[3], 10));
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
return tokens;
|
|
65
|
-
}
|
|
66
|
-
function setDeep(target, path, value) {
|
|
67
|
-
if (!path.length) {
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
let cursor = target;
|
|
71
|
-
for (let index = 0; index < path.length; index++) {
|
|
72
|
-
const key = path[index];
|
|
73
|
-
if (key === void 0) {
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
const isLast = index === path.length - 1;
|
|
77
|
-
const nextKey = path[index + 1];
|
|
78
|
-
if (isLast) {
|
|
79
|
-
cursor[key] = value;
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
if (cursor[key] === void 0) {
|
|
83
|
-
cursor[key] = typeof nextKey === "number" ? [] : {};
|
|
84
|
-
}
|
|
85
|
-
const nextCursor = cursor[key];
|
|
86
|
-
if (!nextCursor || typeof nextCursor !== "object") {
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
cursor = nextCursor;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
51
|
function getObjectShape(schema) {
|
|
93
52
|
const unwrapped = unwrapSchema(schema);
|
|
94
53
|
if (!unwrapped) {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
interface ToastLike {
|
|
2
|
+
add: (payload: {
|
|
3
|
+
title?: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
color?: 'success' | 'error' | 'warning' | 'info' | 'neutral';
|
|
6
|
+
}) => void;
|
|
7
|
+
}
|
|
8
|
+
interface CopyTextWithToastOptions {
|
|
9
|
+
text: string;
|
|
10
|
+
toast: ToastLike;
|
|
11
|
+
description: string;
|
|
12
|
+
title?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function copyTextWithSuccessToast(options: CopyTextWithToastOptions): Promise<boolean>;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export async function copyTextWithSuccessToast(options) {
|
|
2
|
+
if (!options.text) {
|
|
3
|
+
return false;
|
|
4
|
+
}
|
|
5
|
+
await navigator.clipboard.writeText(options.text);
|
|
6
|
+
options.toast.add({
|
|
7
|
+
title: options.title ?? "Copied",
|
|
8
|
+
description: options.description,
|
|
9
|
+
color: "success"
|
|
10
|
+
});
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export function toCsvRow(values) {
|
|
2
|
+
return values.map((value) => {
|
|
3
|
+
const escaped = value.replaceAll('"', '""');
|
|
4
|
+
return `"${escaped}"`;
|
|
5
|
+
}).join(",");
|
|
6
|
+
}
|
|
7
|
+
export function toTsvRow(values) {
|
|
8
|
+
return values.map((value) => value.replace(/[\t\r\n]+/g, " ")).join(" ");
|
|
9
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export type PathSegment = string | number;
|
|
2
|
+
export declare function parseHeaderPath(header: string): PathSegment[];
|
|
3
|
+
export declare function setDeep(target: Record<string, unknown>, path: PathSegment[], value: unknown): void;
|
|
4
|
+
export declare function flattenRecordToStringMap(record: Record<string, unknown>, prefix?: string): Map<string, string>;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
const EXCLUDED_EXPORT_PATHS = /* @__PURE__ */ new Set(["__hash__", "body"]);
|
|
2
|
+
export function parseHeaderPath(header) {
|
|
3
|
+
const tokens = [];
|
|
4
|
+
const parts = header.split(".");
|
|
5
|
+
for (const part of parts) {
|
|
6
|
+
const matches = part.matchAll(/([^[]+)|(\[(\d+)\])/g);
|
|
7
|
+
for (const match of matches) {
|
|
8
|
+
if (match[1]) {
|
|
9
|
+
tokens.push(match[1]);
|
|
10
|
+
}
|
|
11
|
+
if (match[3]) {
|
|
12
|
+
tokens.push(Number.parseInt(match[3], 10));
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return tokens;
|
|
17
|
+
}
|
|
18
|
+
export function setDeep(target, path, value) {
|
|
19
|
+
if (!path.length) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
let cursor = target;
|
|
23
|
+
for (let index = 0; index < path.length; index++) {
|
|
24
|
+
const key = path[index];
|
|
25
|
+
if (key === void 0) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const isLast = index === path.length - 1;
|
|
29
|
+
const nextKey = path[index + 1];
|
|
30
|
+
if (isLast) {
|
|
31
|
+
cursor[key] = value;
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (cursor[key] === void 0) {
|
|
35
|
+
cursor[key] = typeof nextKey === "number" ? [] : {};
|
|
36
|
+
}
|
|
37
|
+
const nextCursor = cursor[key];
|
|
38
|
+
if (!nextCursor || typeof nextCursor !== "object") {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
cursor = nextCursor;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export function flattenRecordToStringMap(record, prefix = "") {
|
|
45
|
+
const out = /* @__PURE__ */ new Map();
|
|
46
|
+
for (const [key, value] of Object.entries(record)) {
|
|
47
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
48
|
+
if (EXCLUDED_EXPORT_PATHS.has(path)) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (value === null || value === void 0) {
|
|
52
|
+
out.set(path, "");
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (Array.isArray(value)) {
|
|
56
|
+
if (value.every((item) => item === null || ["string", "number", "boolean"].includes(typeof item))) {
|
|
57
|
+
out.set(path, value.map((item) => item == null ? "" : String(item)).join(", "));
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
for (let index = 0; index < value.length; index++) {
|
|
61
|
+
const item = value[index];
|
|
62
|
+
const arrayPath = `${path}[${index}]`;
|
|
63
|
+
if (item && typeof item === "object" && !Array.isArray(item)) {
|
|
64
|
+
flattenRecordToStringMap(item, arrayPath).forEach((nestedValue, nestedKey) => {
|
|
65
|
+
out.set(nestedKey, nestedValue);
|
|
66
|
+
});
|
|
67
|
+
} else {
|
|
68
|
+
out.set(arrayPath, item == null ? "" : String(item));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (value instanceof Date) {
|
|
74
|
+
out.set(path, value.toISOString());
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if (typeof value === "object") {
|
|
78
|
+
flattenRecordToStringMap(value, path).forEach((nestedValue, nestedKey) => {
|
|
79
|
+
out.set(nestedKey, nestedValue);
|
|
80
|
+
});
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
out.set(path, String(value));
|
|
84
|
+
}
|
|
85
|
+
return out;
|
|
86
|
+
}
|
package/package.json
CHANGED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
export declare const machinesSmoke: z.ZodObject<{
|
|
3
|
-
pageOrder: z.ZodNumber;
|
|
4
|
-
modelId: z.ZodString;
|
|
5
|
-
machineName: z.ZodString;
|
|
6
|
-
cutRate: z.ZodNumber;
|
|
7
|
-
featurePrimary: z.ZodOptional<z.ZodString>;
|
|
8
|
-
}, z.core.$strip>;
|
|
9
|
-
export declare const example: z.ZodObject<{
|
|
10
|
-
slug: z.ZodString;
|
|
11
|
-
pageOrder: z.ZodCoercedNumber<unknown>;
|
|
12
|
-
number: z.ZodCoercedNumber<unknown>;
|
|
13
|
-
string: z.ZodString;
|
|
14
|
-
enumString: z.ZodEnum<{
|
|
15
|
-
foo: "foo";
|
|
16
|
-
bar: "bar";
|
|
17
|
-
baz: "baz";
|
|
18
|
-
}>;
|
|
19
|
-
literalString: z.ZodLiteral<"foo">;
|
|
20
|
-
unionString: z.ZodUnion<readonly [z.ZodLiteral<"foo">, z.ZodLiteral<"bar">]>;
|
|
21
|
-
unionStringArray: z.ZodArray<z.ZodUnion<readonly [z.ZodLiteral<"foo">, z.ZodLiteral<"bar">]>>;
|
|
22
|
-
exclusiveUnionString: z.ZodXor<readonly [z.ZodLiteral<"foo">, z.ZodLiteral<"bar">]>;
|
|
23
|
-
stringArray: z.ZodArray<z.ZodString>;
|
|
24
|
-
boolean: z.ZodCoercedBoolean<unknown>;
|
|
25
|
-
object: z.ZodObject<{
|
|
26
|
-
key1: z.ZodString;
|
|
27
|
-
key2: z.ZodString;
|
|
28
|
-
}, z.core.$strip>;
|
|
29
|
-
objectArray: z.ZodArray<z.ZodObject<{
|
|
30
|
-
keyA: z.ZodString;
|
|
31
|
-
keyB: z.ZodString;
|
|
32
|
-
}, z.core.$strip>>;
|
|
33
|
-
}, z.core.$strip>;
|
|
34
|
-
export declare const schemas: {
|
|
35
|
-
machinesSmoke: z.ZodObject<{
|
|
36
|
-
pageOrder: z.ZodNumber;
|
|
37
|
-
modelId: z.ZodString;
|
|
38
|
-
machineName: z.ZodString;
|
|
39
|
-
cutRate: z.ZodNumber;
|
|
40
|
-
featurePrimary: z.ZodOptional<z.ZodString>;
|
|
41
|
-
}, z.core.$strip>;
|
|
42
|
-
example: z.ZodObject<{
|
|
43
|
-
slug: z.ZodString;
|
|
44
|
-
pageOrder: z.ZodCoercedNumber<unknown>;
|
|
45
|
-
number: z.ZodCoercedNumber<unknown>;
|
|
46
|
-
string: z.ZodString;
|
|
47
|
-
enumString: z.ZodEnum<{
|
|
48
|
-
foo: "foo";
|
|
49
|
-
bar: "bar";
|
|
50
|
-
baz: "baz";
|
|
51
|
-
}>;
|
|
52
|
-
literalString: z.ZodLiteral<"foo">;
|
|
53
|
-
unionString: z.ZodUnion<readonly [z.ZodLiteral<"foo">, z.ZodLiteral<"bar">]>;
|
|
54
|
-
unionStringArray: z.ZodArray<z.ZodUnion<readonly [z.ZodLiteral<"foo">, z.ZodLiteral<"bar">]>>;
|
|
55
|
-
exclusiveUnionString: z.ZodXor<readonly [z.ZodLiteral<"foo">, z.ZodLiteral<"bar">]>;
|
|
56
|
-
stringArray: z.ZodArray<z.ZodString>;
|
|
57
|
-
boolean: z.ZodCoercedBoolean<unknown>;
|
|
58
|
-
object: z.ZodObject<{
|
|
59
|
-
key1: z.ZodString;
|
|
60
|
-
key2: z.ZodString;
|
|
61
|
-
}, z.core.$strip>;
|
|
62
|
-
objectArray: z.ZodArray<z.ZodObject<{
|
|
63
|
-
keyA: z.ZodString;
|
|
64
|
-
keyB: z.ZodString;
|
|
65
|
-
}, z.core.$strip>>;
|
|
66
|
-
}, z.core.$strip>;
|
|
67
|
-
};
|