@entur/function-tools 0.0.8 → 0.0.9

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.
@@ -5,7 +5,7 @@ import { registerStart } from '../lib/commands/start.js';
5
5
  import { registerUnusedExports } from '../lib/commands/unusedExports.js';
6
6
 
7
7
  const program = new Command();
8
- program.name("entur-functions").description("A multi-tool for Firebase functions at Entur").version("0.0.8").option("-v, --verbose", "Enable verbose output");
8
+ program.name("entur-functions").description("A multi-tool for Firebase functions at Entur").version("0.0.9").option("-v, --verbose", "Enable verbose output");
9
9
  registerBuild(program);
10
10
  registerDeploy(program);
11
11
  registerStart(program);
@@ -1,34 +1,121 @@
1
- import { writeFile } from 'node:fs/promises';
1
+ import { copyFile } from 'node:fs/promises';
2
2
  import { builtinModules } from 'node:module';
3
- import { dirname } from 'node:path';
4
- import { stringify } from 'yaml';
3
+ import { dirname, relative } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { writePackageJSON, readPackageJSON, packageUp } from '../packageJSON/index.js';
6
+ import { filterMap } from '../utils/array.js';
5
7
  import { groupByAsync, asyncMap } from '../utils/async.js';
6
8
  import { spawnAsync } from '../utils/exec.js';
7
- import { writePackageJSON, readPackageJSON, packageUp } from '../utils/packageJSON.js';
9
+ import { readYAML, writeYAML } from '../utils/fs.js';
8
10
 
9
11
  const alwaysIncludePackageNames = [
10
12
  "@google-cloud/functions-framework",
11
13
  "firebase-functions",
12
14
  "firebase-admin"
13
15
  ];
14
- async function writeDependencies(outputs, getOutputFilePath, outputDir) {
15
- const packages = [];
16
- for (const [packageJSONPath, packageJSON] of (await calculateDependencies(outputs))){
16
+ async function writeDependencies(outputs, getOutputFilePath, projectRoot, outputDir) {
17
+ const pnpmLockSourceURL = new URL("./pnpm-lock.yaml", projectRoot);
18
+ const pnpmLockOutputURL = new URL("./pnpm-lock.yaml", outputDir);
19
+ const pnpmWorkspaceSourceURL = new URL("./pnpm-workspace.yaml", projectRoot);
20
+ const pnpmWorkspaceOutputURL = new URL("./pnpm-workspace.yaml", outputDir);
21
+ await Promise.all([
22
+ copyFile(pnpmLockSourceURL, pnpmLockOutputURL),
23
+ copyFile(pnpmWorkspaceSourceURL, pnpmWorkspaceOutputURL)
24
+ ]);
25
+ const dependencies = await calculateDependencies(outputs);
26
+ await Promise.all([
27
+ writePackageJSONs(dependencies, getOutputFilePath, outputDir),
28
+ updateWorkspace(dependencies, getOutputFilePath, pnpmWorkspaceOutputURL),
29
+ updatePNPMLock(dependencies, getOutputFilePath, pnpmLockOutputURL, projectRoot)
30
+ ]);
31
+ }
32
+ async function writePackageJSONs(packageJSONMap, getOutputFilePath, outputDir) {
33
+ for (const [packageJSONPath, packageJSON] of packageJSONMap){
17
34
  const newPackageJSONPath = getOutputFilePath(packageJSONPath);
18
- const directory = dirname(newPackageJSONPath);
19
- if (directory === ".") {
20
- await writePackageJSON(new URL(`./${newPackageJSONPath}`, outputDir), {
21
- ...packageJSON,
22
- main: `./${outputs[0].fileName}`
23
- });
24
- } else {
25
- await writePackageJSON(new URL(`./${newPackageJSONPath}`, outputDir), packageJSON);
26
- packages.push(directory);
27
- }
35
+ await writePackageJSON(new URL(`./${newPackageJSONPath}`, outputDir), packageJSON);
28
36
  }
29
- await writeFile(new URL("./pnpm-workspace.yaml", outputDir), stringify({
37
+ }
38
+ async function updateWorkspace(packageJSONMap, getOutputFilePath, pnpmWorkspaceURL) {
39
+ const packages = filterMap([
40
+ ...packageJSONMap.keys()
41
+ ], (filePath)=>{
42
+ const outputFileName = dirname(getOutputFilePath(filePath));
43
+ return outputFileName !== "." ? outputFileName : undefined;
44
+ });
45
+ const workspace = await readYAML(pnpmWorkspaceURL);
46
+ await writeYAML(pnpmWorkspaceURL, {
47
+ ...workspace,
30
48
  packages
31
- }));
49
+ });
50
+ }
51
+ async function updatePNPMLock(packageJSONMap, getOutputFilePath, pnpmLockURL, projectRoot) {
52
+ const projectRootPath = fileURLToPath(projectRoot);
53
+ const lockfile = await readYAML(pnpmLockURL);
54
+ const updateDependencies = (dependencies, packageJSONDependencies)=>{
55
+ if (!dependencies || !packageJSONDependencies) {
56
+ return;
57
+ }
58
+ const entries = filterMap(Object.entries(dependencies), ([packageName, versionOrDependency])=>{
59
+ const specifier = packageJSONDependencies[packageName];
60
+ if (!specifier) return;
61
+ return typeof versionOrDependency === "string" ? [
62
+ packageName,
63
+ {
64
+ specifier,
65
+ version: versionOrDependency
66
+ }
67
+ ] : [
68
+ packageName,
69
+ versionOrDependency
70
+ ];
71
+ });
72
+ if (entries.length === 0) {
73
+ return;
74
+ }
75
+ return Object.fromEntries(entries);
76
+ };
77
+ const findSnapshot = (packageName)=>{
78
+ for (const { dependencies, devDependencies } of Object.values(lockfile.importers)){
79
+ const found = dependencies?.[packageName] || devDependencies?.[packageName];
80
+ if (found) {
81
+ const key = `${packageName}@${found.version}`;
82
+ const snapshot = lockfile.snapshots[key];
83
+ if (snapshot) return snapshot;
84
+ }
85
+ }
86
+ };
87
+ const entries = [
88
+ ...packageJSONMap
89
+ ].map(([packageJSONPath, packageJSON])=>{
90
+ const packageRootPath = dirname(packageJSONPath);
91
+ const key = relative(projectRootPath, packageRootPath);
92
+ const importer = lockfile.importers[key];
93
+ const updatedKey = getOutputFilePath(packageRootPath) || ".";
94
+ if (importer) {
95
+ return [
96
+ updatedKey,
97
+ {
98
+ dependencies: updateDependencies(importer.dependencies, packageJSON.dependencies),
99
+ devDependencies: updateDependencies(importer.devDependencies, packageJSON.devDependencies)
100
+ }
101
+ ];
102
+ }
103
+ const snapshot = findSnapshot(packageJSON.name);
104
+ if (snapshot) {
105
+ return [
106
+ updatedKey,
107
+ {
108
+ dependencies: updateDependencies(snapshot.dependencies, packageJSON.dependencies),
109
+ devDependencies: updateDependencies(snapshot.devDependencies, packageJSON.devDependencies)
110
+ }
111
+ ];
112
+ }
113
+ throw new Error(`Unable to create importer for ${packageJSON.name}`);
114
+ });
115
+ await writeYAML(pnpmLockURL, {
116
+ ...lockfile,
117
+ importers: Object.fromEntries(entries)
118
+ });
32
119
  }
33
120
  async function calculateDependencies(outputs) {
34
121
  const outputGroupedByPackage = await groupByAsync(outputs.filter((it)=>Boolean(it.type === "chunk" && it.facadeModuleId && it.imports.length > 0)), async (output)=>{
@@ -45,6 +132,7 @@ async function calculateDependencies(outputs) {
45
132
  const entries = Object.entries(dependencies).filter(([key])=>lookup.has(key));
46
133
  return entries.length > 0 ? Object.fromEntries(entries) : undefined;
47
134
  };
135
+ const entryFile = outputs[0];
48
136
  const entries = await asyncMap([
49
137
  ...outputGroupedByPackage
50
138
  ], async ([packageJSONPath, outputs])=>{
@@ -59,6 +147,7 @@ async function calculateDependencies(outputs) {
59
147
  name,
60
148
  type,
61
149
  version,
150
+ main: outputs.includes(entryFile) ? `./${entryFile.fileName}` : undefined,
62
151
  dependencies: filterDependencies(importedPackageNames, packageJSON.dependencies),
63
152
  devDependencies: filterDependencies(importedPackageNames, packageJSON.devDependencies),
64
153
  optionalDependencies: filterDependencies(importedPackageNames, packageJSON.optionalDependencies)
@@ -79,7 +168,7 @@ function isBuiltinImport(specifier) {
79
168
  async function linkDependencies(workingDir) {
80
169
  await spawnAsync("pnpm", [
81
170
  "install",
82
- "--prefer-offline"
171
+ "--fix-lockfile"
83
172
  ], {
84
173
  cwd: workingDir
85
174
  });
@@ -2,15 +2,13 @@ import { fileURLToPath } from 'node:url';
2
2
  import { nodeResolve } from '@rollup/plugin-node-resolve';
3
3
  import swc from '@rollup/plugin-swc';
4
4
  import { rollup, watch } from 'rollup';
5
- import { createOutputFileName } from './utils.js';
6
5
 
7
- async function bundle(entryFile, { outputDir, packageRoot, projectRoot, packagesToInline }) {
6
+ async function bundle(entryFile, { outputDir, outputFileName, shouldInlinePackage }) {
8
7
  const bundle = await rollup({
9
8
  input: fileURLToPath(entryFile),
10
- plugins: plugins(packagesToInline),
9
+ plugins: plugins(shouldInlinePackage),
11
10
  treeshake: "smallest"
12
11
  });
13
- const outputFileName = createOutputFileName(packageRoot, projectRoot);
14
12
  return bundle.write({
15
13
  preserveModules: true,
16
14
  format: "esm",
@@ -18,8 +16,7 @@ async function bundle(entryFile, { outputDir, packageRoot, projectRoot, packages
18
16
  entryFileNames: ({ facadeModuleId })=>outputFileName(facadeModuleId)
19
17
  });
20
18
  }
21
- async function bundleAndWatch(entryFile, { outputDir, packageRoot, projectRoot, packagesToInline, onBundleEnd }) {
22
- const outputFileName = createOutputFileName(packageRoot, projectRoot);
19
+ async function bundleAndWatch(entryFile, { outputDir, outputFileName, shouldInlinePackage, onBundleEnd }) {
23
20
  const output = {
24
21
  preserveModules: true,
25
22
  format: "esm",
@@ -27,7 +24,7 @@ async function bundleAndWatch(entryFile, { outputDir, packageRoot, projectRoot,
27
24
  entryFileNames: ({ facadeModuleId })=>outputFileName(facadeModuleId)
28
25
  };
29
26
  const watchOptions = {
30
- plugins: plugins(packagesToInline),
27
+ plugins: plugins(shouldInlinePackage),
31
28
  input: fileURLToPath(entryFile),
32
29
  treeshake: "smallest",
33
30
  output,
@@ -61,7 +58,7 @@ async function bundleAndWatch(entryFile, { outputDir, packageRoot, projectRoot,
61
58
  }
62
59
  });
63
60
  }
64
- const plugins = (workspacePackages)=>[
61
+ const plugins = (resolveOnly)=>[
65
62
  swc({
66
63
  swc: {
67
64
  jsc: {
@@ -70,7 +67,7 @@ const plugins = (workspacePackages)=>[
70
67
  }
71
68
  }),
72
69
  nodeResolve({
73
- resolveOnly: (moduleId)=>moduleId.startsWith(".") || workspacePackages?.some((it)=>moduleId.startsWith(it)) || moduleId.startsWith("@entur-private/") || false
70
+ resolveOnly
74
71
  })
75
72
  ];
76
73
 
@@ -5,19 +5,25 @@ function createOutputFileName(packageRootURL, projectRootURL) {
5
5
  const projectRoot = fileURLToPath(projectRootURL);
6
6
  const packageRoot = fileURLToPath(packageRootURL);
7
7
  return (facadeModuleId)=>{
8
- const index = facadeModuleId?.lastIndexOf(nodeModulesPartPath);
9
- if (facadeModuleId && index && index > -1) {
8
+ if (!facadeModuleId) {
9
+ throw new Error(`file path is null`);
10
+ }
11
+ const index = facadeModuleId.lastIndexOf(nodeModulesPartPath);
12
+ if (facadeModuleId && index > -1) {
10
13
  return `bundled_modules/${facadeModuleId.slice(index + nodeModulesPartPath.length)}`;
11
14
  }
12
- const filePath = facadeModuleId?.replace(".ts", ".js").replaceAll("/src/", "/lib/");
13
- if (filePath?.startsWith(packageRoot)) {
15
+ const filePath = facadeModuleId.replace(".ts", ".js").replaceAll("/src/", "/lib/");
16
+ if (filePath.startsWith(packageRoot) || packageRoot.slice(0, -1) === filePath) {
14
17
  return filePath.slice(packageRoot.length);
15
18
  }
16
- if (filePath?.startsWith(projectRoot)) {
19
+ if (filePath.startsWith(projectRoot)) {
17
20
  return `local_modules/${filePath.slice(projectRoot.length)}`;
18
21
  }
19
22
  throw new Error(`Unable to determine correct placement for file ${facadeModuleId}`);
20
23
  };
21
24
  }
25
+ function createShouldInlinePackage(packagesToInline) {
26
+ return (moduleId)=>moduleId.startsWith(".") || packagesToInline?.some((it)=>moduleId.startsWith(it)) || moduleId.startsWith("@entur-private/") || false;
27
+ }
22
28
 
23
- export { createOutputFileName };
29
+ export { createOutputFileName, createShouldInlinePackage };
@@ -1,8 +1,8 @@
1
- import { writeDependencies } from '../bundle/dependencies.js';
1
+ import { writeDependencies, linkDependencies } from '../bundle/dependencies.js';
2
2
  import { bundle } from '../bundle/index.js';
3
- import { createOutputFileName } from '../bundle/utils.js';
3
+ import { createOutputFileName, createShouldInlinePackage } from '../bundle/utils.js';
4
+ import { readPackageJSON } from '../packageJSON/index.js';
4
5
  import { cleanDir } from '../utils/fs.js';
5
- import { readPackageJSON } from '../utils/packageJSON.js';
6
6
  import { getWorkspacePackageNames } from '../utils/workspace.js';
7
7
  import { resolveProjectConfig } from './utils.js';
8
8
 
@@ -19,20 +19,22 @@ function registerBuild(program) {
19
19
  }
20
20
  });
21
21
  }
22
- async function build({ packageRoot, projectRoot, packageJSON, outputDir, pnpmWorkspaceYAML }) {
22
+ async function build(projectConfig) {
23
+ const { packageRoot, projectRoot, packageJSON, outputDir, pnpmWorkspaceYAML } = projectConfig;
23
24
  const { name, exports: exports$1 } = await readPackageJSON(packageJSON);
24
25
  console.log("🧹 Cleaning dist folder");
25
26
  await cleanDir(outputDir);
26
27
  console.log(`🔨 Building ${name}`);
27
- const packagesToInline = await getWorkspacePackageNames(pnpmWorkspaceYAML);
28
28
  const entryFile = new URL(exports$1?.["."] ?? "./index.js", packageRoot);
29
+ const outputFileName = createOutputFileName(packageRoot, projectRoot);
30
+ const shouldInlinePackage = createShouldInlinePackage(await getWorkspacePackageNames(pnpmWorkspaceYAML));
29
31
  const { output } = await bundle(entryFile, {
30
32
  outputDir,
31
- packageRoot,
32
- projectRoot,
33
- packagesToInline
33
+ outputFileName,
34
+ shouldInlinePackage
34
35
  });
35
- await writeDependencies(output, createOutputFileName(packageRoot, projectRoot), outputDir);
36
+ await writeDependencies(output, outputFileName, projectRoot, outputDir);
37
+ await linkDependencies(outputDir);
36
38
  }
37
39
 
38
40
  export { registerBuild };
@@ -1,10 +1,10 @@
1
1
  import { writeDependencies, linkDependencies } from '../bundle/dependencies.js';
2
2
  import { bundle } from '../bundle/index.js';
3
- import { createOutputFileName } from '../bundle/utils.js';
3
+ import { createOutputFileName, createShouldInlinePackage } from '../bundle/utils.js';
4
4
  import { deploy as deploy$1 } from '../firebase/index.js';
5
- import { getFirebaseJSON, writeFirebaseJSON } from '../utils/firebase.js';
5
+ import { getFirebaseJSON, writeFirebaseJSON } from '../firebase/utils.js';
6
+ import { readPackageJSON, getPackageName } from '../packageJSON/index.js';
6
7
  import { cleanDir, writeJSON } from '../utils/fs.js';
7
- import { readPackageJSON, getPackageName } from '../utils/packageJSON.js';
8
8
  import { getWorkspacePackageNames } from '../utils/workspace.js';
9
9
  import { resolveProjectConfig, copyEnvFiles } from './utils.js';
10
10
 
@@ -23,14 +23,13 @@ function registerDeploy(program) {
23
23
  });
24
24
  }
25
25
  async function deploy(options, projectConfig) {
26
- const { projectAlias, projectId, packageJSON, packageRoot, pnpmWorkspaceYAML, projectRoot, outputDir } = projectConfig;
26
+ const { projectAlias, projectId, packageJSON, packageRoot, projectRoot, outputDir } = projectConfig;
27
27
  const { name, exports: exports$1 } = await readPackageJSON(packageJSON);
28
- const workspacePackages = await getWorkspacePackageNames(pnpmWorkspaceYAML);
29
28
  const entryFile = new URL(exports$1?.["."] ?? "./index.js", packageRoot);
30
29
  console.log("🧹 Cleaning dist folder");
31
30
  await cleanDir(outputDir);
32
31
  console.log(`🔨 Building ${name}`);
33
- await build(entryFile, workspacePackages, projectConfig);
32
+ await build(entryFile, projectConfig);
34
33
  console.log("⛷️ Prepare deploy");
35
34
  const firebaseJSON = await getFirebaseJSON(new URL("firebase.json", projectRoot));
36
35
  await prepareDeploy(getPackageName(name), firebaseJSON, projectConfig);
@@ -41,14 +40,16 @@ async function deploy(options, projectConfig) {
41
40
  ...options
42
41
  });
43
42
  }
44
- async function build(entryFile, packagesToInline, { outputDir, packageRoot, projectRoot }) {
43
+ async function build(entryFile, { outputDir, packageRoot, projectRoot, pnpmWorkspaceYAML }) {
44
+ const outputFileName = createOutputFileName(packageRoot, projectRoot);
45
+ const shouldInlinePackage = createShouldInlinePackage(await getWorkspacePackageNames(pnpmWorkspaceYAML));
45
46
  const { output } = await bundle(entryFile, {
46
- packageRoot,
47
- projectRoot,
48
47
  outputDir,
49
- packagesToInline
48
+ outputFileName,
49
+ shouldInlinePackage
50
50
  });
51
- await writeDependencies(output, createOutputFileName(packageRoot, projectRoot), outputDir);
51
+ await writeDependencies(output, outputFileName, projectRoot, outputDir);
52
+ await linkDependencies(outputDir);
52
53
  }
53
54
  async function prepareDeploy(codebase, firebaseJSON, { projectAlias, projectId, outputDir, envFiles }) {
54
55
  const envPrefix = `FUNCTION_CODEBASE='${codebase}'\nENTUR_PROJECT_ALIAS='${projectAlias}'`;
@@ -1,10 +1,10 @@
1
1
  import { writeDependencies, linkDependencies } from '../bundle/dependencies.js';
2
2
  import { bundleAndWatch } from '../bundle/index.js';
3
- import { createOutputFileName } from '../bundle/utils.js';
3
+ import { createOutputFileName, createShouldInlinePackage } from '../bundle/utils.js';
4
4
  import { startEmulator } from '../firebase/index.js';
5
- import { getFirebaseJSON, writeFirebaseJSON } from '../utils/firebase.js';
5
+ import { getFirebaseJSON, writeFirebaseJSON } from '../firebase/utils.js';
6
+ import { readPackageJSON, getPackageName } from '../packageJSON/index.js';
6
7
  import { cleanDir, writeJSON } from '../utils/fs.js';
7
- import { readPackageJSON, getPackageName } from '../utils/packageJSON.js';
8
8
  import { getWorkspacePackageNames } from '../utils/workspace.js';
9
9
  import { resolveProjectConfig, copyEnvFiles } from './utils.js';
10
10
 
@@ -27,14 +27,16 @@ async function start(options, projectConfig) {
27
27
  const { packageRoot, packageJSON, projectId, projectRoot, pnpmWorkspaceYAML, outputDir } = projectConfig;
28
28
  const { name, exports: exports$1 } = await readPackageJSON(packageJSON);
29
29
  const codebase = getPackageName(name);
30
- const packagesToInline = await getWorkspacePackageNames(pnpmWorkspaceYAML);
31
30
  const entryFile = new URL(exports$1?.["."] ?? "./index.js", packageRoot);
32
31
  console.log("🧹 Cleaning dist folder");
33
32
  await cleanDir(outputDir);
34
33
  console.log(`🔨 Building ${name}`);
34
+ const outputFileName = createOutputFileName(packageRoot, projectRoot);
35
+ const shouldInlinePackage = createShouldInlinePackage(await getWorkspacePackageNames(pnpmWorkspaceYAML));
35
36
  let firstRun = true;
36
37
  const onBundleEnd = async ({ output })=>{
37
- await writeDependencies(output, createOutputFileName(packageRoot, projectRoot), outputDir);
38
+ await writeDependencies(output, outputFileName, projectRoot, outputDir);
39
+ await linkDependencies(outputDir);
38
40
  const firebaseJSON = await getFirebaseJSON(new URL("firebase.json", projectRoot));
39
41
  await prepareStart(codebase, firebaseJSON, projectConfig);
40
42
  if (firstRun) {
@@ -50,9 +52,8 @@ async function start(options, projectConfig) {
50
52
  };
51
53
  await bundleAndWatch(entryFile, {
52
54
  outputDir,
53
- projectRoot,
54
- packageRoot,
55
- packagesToInline,
55
+ outputFileName,
56
+ shouldInlinePackage,
56
57
  onBundleEnd
57
58
  });
58
59
  }
@@ -1,5 +1,5 @@
1
1
  import { writeFile } from 'node:fs/promises';
2
- import { readJSON } from './fs.js';
2
+ import { readJSON } from '../utils/fs.js';
3
3
 
4
4
  function getFirebaseJSON(file) {
5
5
  return readJSON(file);
@@ -1,6 +1,6 @@
1
1
  import process from 'node:process';
2
2
  import { findUp } from 'find-up-simple';
3
- import { readJSON, writeJSON } from './fs.js';
3
+ import { readJSON, writeJSON } from '../utils/fs.js';
4
4
 
5
5
  function readPackageJSON(file) {
6
6
  return readJSON(file);
package/lib/utils/fs.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { readFile, rm, mkdir, writeFile, access } from 'node:fs/promises';
2
+ import { parse, stringify } from 'yaml';
2
3
 
3
4
  async function cleanDir(dir) {
4
5
  await rm(dir, {
@@ -24,5 +25,12 @@ async function readJSON(file) {
24
25
  async function writeJSON(file, data) {
25
26
  await writeFile(file, JSON.stringify(data, undefined, 4));
26
27
  }
28
+ async function readYAML(file) {
29
+ const fileContent = await readFile(file, "utf-8");
30
+ return parse(fileContent);
31
+ }
32
+ async function writeYAML(file, data) {
33
+ await writeFile(file, stringify(data, undefined, 4));
34
+ }
27
35
 
28
- export { cleanDir, exists, readJSON, writeJSON };
36
+ export { cleanDir, exists, readJSON, readYAML, writeJSON, writeYAML };
@@ -1,6 +1,6 @@
1
1
  import { readFile, glob } from 'node:fs/promises';
2
2
  import { parse } from 'yaml';
3
- import { readPackageJSON } from './packageJSON.js';
3
+ import { readPackageJSON } from '../packageJSON/index.js';
4
4
 
5
5
  async function getWorkspacePackageNames(pnpmWorkspaceYAML) {
6
6
  const pnpmWorkspaceContent = await readFile(pnpmWorkspaceYAML, "utf-8");
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@entur/function-tools",
3
3
  "type": "module",
4
- "version": "0.0.8",
4
+ "version": "0.0.9",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },