tranfi 0.0.1

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/src/app.vue ADDED
@@ -0,0 +1,253 @@
1
+ <template>
2
+ <div>
3
+
4
+ <it-drawer placement="left" v-model="modalAddTransforms">
5
+ <nav class="panel">
6
+ <!--
7
+ <p class="panel-tabs">
8
+ <a class="is-active">All</a>
9
+ <a>Public</a>
10
+ <a>Private</a>
11
+ </p>
12
+ -->
13
+ <a class="panel-block" v-for="(Transform, i) in transforms" :key="i" @click="addTransform(Transform)">
14
+ <span class="panel-icon">
15
+ <i class="mdi mdi-import" aria-hidden="true" v-if="Transform.type === 'loader'"></i>
16
+ <i class="mdi mdi-shape" aria-hidden="true" v-if="Transform.type === 'parser'"></i>
17
+ <i class="mdi mdi-play-box-outline" aria-hidden="true" v-if="Transform.type === 'output'"></i>
18
+ </span>
19
+ {{ Transform.name }}
20
+ </a>
21
+ </nav>
22
+ </it-drawer>
23
+
24
+ <nav class="mainbar" role="navigation" aria-label="main navigation" style="background: #e6e6e6; position: fixed; width: 100%; top: 0; left: 0; z-index: 100">
25
+ <div class="columns">
26
+ <div class="column is-3 mainbar-left" v-if="!preview">
27
+ <div class="navbar-brand">
28
+ <a class="navbar-item" href="https://tranfi.com">
29
+ <img src="/logo.png" width="112">
30
+ </a>
31
+
32
+ <button class="button is-primary is-outlined" @click="modalAddTransforms = true">
33
+ <span class="icon is-small">
34
+ <i class="mdi mdi-playlist-plus" aria-hidden="true"></i>
35
+ </span>
36
+ </button>
37
+ </div>
38
+ </div>
39
+ <div class="column mainbar-right" v-bind:class="{ 'is-9': !preview, 'is-12': preview }">
40
+ <div class="navbar-start">
41
+ <div class="navbar-item mainbar-title">
42
+ <span v-if="preview">{{ title }}</span>
43
+ <input class="input" type="text" v-model="title" placeholder="Title..." v-else>
44
+ </div>
45
+ </div>
46
+ <div class="navbar-end">
47
+ <div class="navbar-item">
48
+ <div class="buttons">
49
+ <a class="button is-light is-outlined mobile-flexible" v-if="pipeline.length">
50
+ <span class="icon is-small">
51
+ <i class="mdi mdi-content-save" aria-hidden="true"></i>
52
+ </span>
53
+ <span class="desktop-only">
54
+ Save pipeline
55
+ </span>
56
+ </a>
57
+ <a class="button is-light is-outlined preview-button mobile-flexible" @click="preview=true" v-if="!preview">
58
+ <span class="icon is-small">
59
+ <i class="mdi mdi-eye" aria-hidden="true"></i>
60
+ </span>
61
+ <span class="desktop-only">
62
+ View
63
+ </span>
64
+ </a>
65
+ <a class="button is-light is-outlined preview-button mobile-flexible" @click="preview=false" v-if="preview">
66
+ <span class="icon is-small">
67
+ <i class="mdi mdi-pencil" aria-hidden="true"></i>
68
+ </span>
69
+ <span class="desktop-only">
70
+ Edit
71
+ </span>
72
+ </a>
73
+ </div>
74
+ </div>
75
+ </div>
76
+ </div>
77
+ </div>
78
+ </nav>
79
+
80
+ <div class="columns" style="margin-top: 77px">
81
+ <div class="pipeline column is-3" v-if="!preview">
82
+
83
+ <draggable
84
+ v-model="pipeline"
85
+ @start="drag=true"
86
+ @end="drag=false"
87
+ item-key="id"
88
+ handle=".card-header"
89
+ >
90
+ <template #item="{element}">
91
+ <div>
92
+ <div class="card" v-bind:class="{ active: element.expanded }">
93
+ <header class="card-header">
94
+ <p class="card-header-title">
95
+ {{ element.name }}
96
+ </p>
97
+ <button class="card-header-icon" aria-label="more options" v-on:click="element.expanded = !element.expanded">
98
+ <span class="icon">
99
+ <i class="mdi mdi-chevron-up" aria-hidden="true" v-if="element.expanded"></i>
100
+ <i class="mdi mdi-chevron-down" aria-hidden="true" v-else></i>
101
+ </span>
102
+ </button>
103
+ </header>
104
+ <div class="card-content" >
105
+ <ul>
106
+ <li v-for="(input, index) in element.inputs">
107
+ <vue-input v-bind:input="input"></vue-input>
108
+ </li>
109
+ <li v-if="element.inputs.length">
110
+ <div class="field">
111
+ <label v-bind:for="'disp' + element.id" class="is-size-7">Display on preview:</label>
112
+ <div class="control">
113
+ <div class="select is-multiple">
114
+ <select
115
+ v-model="element.displayInputs"
116
+ v-bind:id="'disp' + element.id"
117
+ multiple
118
+ size="3"
119
+ >
120
+ <option v-for="(input, index) in element.inputs" v-bind:value="index">
121
+ {{ input.name }}
122
+ </option>
123
+ </select>
124
+ </div>
125
+ </div>
126
+ </div>
127
+ </li>
128
+ <li>
129
+ <pre style="font-size: 10px;">{{ element }}</pre>
130
+ </li>
131
+ </ul>
132
+ </div>
133
+ <footer class="card-footer">
134
+ <a class="card-footer-item" @click="cloneTransform(element)">Clone</a>
135
+ <a class="card-footer-item" @click="resetTransform(element)">Reset</a>
136
+ <a class="card-footer-item" @click="deleteTransform(element)">Delete</a>
137
+ </footer>
138
+ </div>
139
+ <p style="text-align: center; font-size: 11px; color: #AAA;">
140
+ <span> ↓ </span>
141
+ <small>{{ element.outputType }}</small>
142
+ </p>
143
+ </div>
144
+ </template>
145
+ </draggable>
146
+
147
+ <button
148
+ @click="modalAddTransforms = true"
149
+ class="button is-primary is-outlined is-fullwidth"
150
+ style="margin-bottom: 10px; min-height: 45px;"
151
+ >
152
+ <span class="icon is-small">
153
+ <i class="mdi mdi-playlist-plus" aria-hidden="true"></i>
154
+ </span>
155
+ <span>
156
+ Add stream
157
+ </span>
158
+ </button>
159
+
160
+ </div>
161
+
162
+ <div class="column preview-content" v-bind:class="{ 'is-9': !preview, 'is-12': preview }">
163
+
164
+ <nav class="breadcrumb has-arrow-separator" aria-label="breadcrumbs">
165
+ <ul>
166
+ <li v-for="transform in pipeline">&nbsp;{{ transform.name }}&nbsp;</li>
167
+ </ul>
168
+ </nav>
169
+
170
+ <hr v-if="pipeline.length">
171
+
172
+ <nav class="panel preview-panel" v-if="pipeline.length">
173
+ <div class="panel-block" v-for="(input, index) in displayInputs">
174
+ <vue-input v-bind:input="input"></vue-input>
175
+ </div>
176
+ <div class="panel-block" v-if="pipeline.length">
177
+ <button class="button is-primary is-outlined is-fullwidth" @click="run(true)">
178
+ <span class="icon is-small">
179
+ <i class="mdi mdi-play-pause" aria-hidden="true"></i>
180
+ </span>
181
+ <span>
182
+ Load batch
183
+ </span>
184
+ </button>
185
+ </div>
186
+ <div class="panel-block" v-if="pipeline.length">
187
+ <button class="button is-primary is-large is-fullwidth" @click="run(false)">
188
+ <span class="icon is-small">
189
+ <i class="mdi mdi-play" aria-hidden="true"></i>
190
+ </span>
191
+ <span>
192
+ Load all
193
+ </span>
194
+ </button>
195
+ </div>
196
+ </nav>
197
+
198
+ <hr v-if="pipeline.length">
199
+
200
+ <pre id="text-output" style="display: none;"></pre>
201
+
202
+ <hr v-if="pipeline.length && (!loadedAllData)">
203
+
204
+ <nav class="panel preview-panel" v-if="pipeline.length && (!loadedAllData)">
205
+ <div class="panel-block" v-if="pipeline.length">
206
+ <button class="button is-primary is-outlined is-fullwidth" @click="runMore">
207
+ <span class="icon is-small">
208
+ <i class="mdi mdi-play-pause" aria-hidden="true"></i>
209
+ </span>
210
+ <span>
211
+ Load more
212
+ </span>
213
+ </button>
214
+ </div>
215
+ </nav>
216
+
217
+
218
+ <!--
219
+ <it-tag>Neutral</it-tag><it-tag>Neutral</it-tag><it-tag>Neutral</it-tag><it-tag>Neutral</it-tag>
220
+ <it-button @click="showMessage('Warning')" type="warning">Warning message</it-button>
221
+ <it-tabs box style="flex: 1">
222
+ <it-tab title="Tab 1">First tab</it-tab>
223
+ <it-tab title="Tab 2">Second tab</it-tab>
224
+ <it-tab title="Tab 3" :disabled="disabledTab">Third tab</it-tab>
225
+ </it-tabs>
226
+ -->
227
+ </div>
228
+ </div>
229
+
230
+ <!--
231
+ <nav class="bottombar" role="navigation" style="background: none; position: fixed; width: 100%; bottom: 0; left: 0; z-index: 100">
232
+ <div class="columns">
233
+ <div class="column is-3" v-if="!preview"></div>
234
+ <div class="column bottombar-right" v-bind:class="{ 'is-9': !preview, 'is-12': preview }" style="">
235
+ <div class="buttons" v-if="pipeline.length">
236
+ <button class="button is-primary" @click="run">
237
+ <span class="icon is-small">
238
+ <i class="mdi mdi-subdirectory-arrow-right" aria-hidden="true"></i>
239
+ </span>
240
+ <span>
241
+ Run
242
+ </span>
243
+ </button>
244
+ </div>
245
+ </div>
246
+ </div>
247
+ </nav>
248
+ -->
249
+
250
+ </div>
251
+ </template>
252
+ <style src="./app.css"></style>
253
+ <script src="./app.js" scoped></script>
@@ -0,0 +1,110 @@
1
+ <template>
2
+ <div class="field" v-if="input.type == 'int' || input.type == 'float' || input.type == 'number'">
3
+ <label v-bind:for="input.name" class="is-size-7">{{ input.name }}</label>
4
+ <div class="control">
5
+ <input
6
+ v-model="input.value"
7
+ v-bind:id="input.name"
8
+ v-bind:step="input.type == 'int' ? 1 : 0.001"
9
+ v-bind:placeholder="input.placeholder ? input.placeholder : input.name"
10
+ v-bind:min="input.min"
11
+ v-bind:max="input.max"
12
+ v-on:change="changeHandler"
13
+ class="input"
14
+ type="number"
15
+ >
16
+ </div>
17
+ </div>
18
+
19
+ <div class="field" v-if="input.type == 'string' || input.type == 'color'">
20
+ <label v-bind:for="input.name" class="is-size-7">{{ input.name }}</label>
21
+ <div class="control">
22
+ <input
23
+ v-model="input.value"
24
+ v-bind:id="input.name"
25
+ v-bind:placeholder="input.placeholder ? input.placeholder : input.name"
26
+ v-on:change="changeHandler"
27
+ class="input"
28
+ >
29
+ </div>
30
+ </div>
31
+
32
+ <div class="field" v-if="input.type == 'text'">
33
+ <label v-bind:for="input.name" class="is-size-7">{{ input.name }}</label>
34
+ <div class="control">
35
+ <textarea
36
+ v-model="input.value"
37
+ v-bind:id="input.name"
38
+ v-bind:placeholder="input.placeholder ? input.placeholder : input.name"
39
+ v-on:change="changeHandler"
40
+ class="textarea"
41
+ ></textarea>
42
+ </div>
43
+ </div>
44
+
45
+ <div class="field" v-if="input.type == 'checkbox' || input.type == 'bool'">
46
+ <div class="control">
47
+ <label class="checkbox is-size-7">
48
+ <input
49
+ v-model="input.value"
50
+ v-bind:id="input.name"
51
+ v-on:change="changeHandler"
52
+ type="checkbox"
53
+ >
54
+ {{ input.name }}
55
+ </label>
56
+ </div>
57
+ </div>
58
+
59
+ <div class="field" v-if="input.type == 'categorical' || input.type == 'select'">
60
+ <label v-bind:for="input.name" class="is-size-7">{{ input.name }}</label>
61
+ <div class="control">
62
+ <div class="select is-fullwidth">
63
+ <select
64
+ v-model="input.value"
65
+ v-bind:id="input.name"
66
+ v-on:change="changeHandler"
67
+ >
68
+ <option
69
+ v-for="(option, oi) in input.options"
70
+ >{{ option }}</option>
71
+ </select>
72
+ </div>
73
+ </div>
74
+ </div>
75
+
76
+ <div class="field" v-if="input.type == 'file'">
77
+ <label v-bind:for="input.name" class="is-size-7">{{ input.name }}</label>
78
+ <div class="control">
79
+ <div class="file has-name is-fullwidth" v-bind:class="{ 'is-primary': !input.file }">
80
+ <label class="file-label">
81
+ <input
82
+ v-bind:id="input.name"
83
+ v-on:change="loadFile"
84
+ class="file-input"
85
+ type="file"
86
+ >
87
+ <span class="file-cta">
88
+ <span class="file-label">
89
+ Open
90
+ </span>
91
+ </span>
92
+ <span class="file-name">
93
+ <span v-if="input.file && input.file.name">{{ input.file.name }}</span>
94
+ <span v-else>No file selected</span>
95
+ </span>
96
+ </label>
97
+ </div>
98
+ </div>
99
+ </div>
100
+
101
+ <div class="field is-horizontal" v-if="input.type == 'group'">
102
+ <div class="field-body">
103
+ <vue-input v-for="(el, index) in input.elements" v-bind:input="el" ></vue-input>
104
+ </div>
105
+ </div>
106
+ </template>
107
+
108
+ <script>
109
+ export { component as default } from "./common-inputs.js"
110
+ </script>
@@ -0,0 +1,28 @@
1
+ const FileReader = window['FileReader']
2
+
3
+ const component = {
4
+ props: ['input'],
5
+ emits: ['inchange'],
6
+ methods: {
7
+ changeHandler () {
8
+ if (this.input.reactive) {
9
+ this.$emit('inchange')
10
+ }
11
+ },
12
+ loadFile (e) {
13
+ const reader = new FileReader()
14
+ this.input.file = e.target.files[0]
15
+ // reader.readAsText(this.input.file)
16
+ // reader.onload = () => {
17
+ // this.input.value = reader.result
18
+ // if (typeof this.input.cb !== 'undefined') {
19
+ // this.input.cb.run()
20
+ // } else if (this.input.reactive) {
21
+ // this.$emit('inchange')
22
+ // }
23
+ // }
24
+ }
25
+ }
26
+ }
27
+
28
+ export { component }
package/src/main.js ADDED
@@ -0,0 +1,18 @@
1
+ import { createApp } from 'vue'
2
+
3
+ import App from './app.vue'
4
+
5
+ import Equal from 'equal-vue'
6
+ import 'equal-vue/dist/style.css'
7
+
8
+ import bulmaInput from './bulma-input.vue'
9
+ import draggable from 'vuedraggable'
10
+
11
+ const app = createApp(App)
12
+ app.use(Equal)
13
+
14
+ app.component('vue-input', bulmaInput)
15
+ app.component('draggable', draggable)
16
+
17
+ app.mount('#tranfi-container')
18
+
@@ -0,0 +1,166 @@
1
+ const ReadStream = require('filestream').read
2
+ const ParseStream = require('csv-parse')
3
+ const JSONStream = require('JSONStream')
4
+ const HTTPStream = require('stream-http')
5
+ const through2 = require('through2')
6
+
7
+ let FileLoader = class FileLoader {
8
+ static inputType = 'text'
9
+
10
+ static outputType = 'text'
11
+
12
+ static type = 'loader'
13
+
14
+ static initStream (params) {
15
+ const stream = new ReadStream(params['File'], {chunkSize: params['Chunk size']})
16
+ stream.setEncoding('utf8')
17
+ // if (params['Encoding'] === 'UTF-8') {}
18
+ return stream
19
+ }
20
+
21
+ constructor (id) {
22
+ this.name = this.constructor.name
23
+ this.inputs = [
24
+ { name: 'File', type: 'file' },
25
+ // { name: 'Encoding', type: 'categorical', options: ['UTF-8', 'Buffer'], default: 'UTF-8' },
26
+ { name: 'Chunk size', type: 'int', default: 10000 },
27
+ ]
28
+ this.inputType = this.constructor.inputType
29
+ this.outputType = this.constructor.outputType
30
+ this.id = id
31
+ this.expanded = false
32
+ this.displayInputs = [0]
33
+ }
34
+ }
35
+
36
+ let HTTPLoader = class HTTPLoader {
37
+ static inputType = 'text'
38
+
39
+ static outputType = 'text'
40
+
41
+ static type = 'loader'
42
+
43
+ static initStream (params) {
44
+ return new Promise((resolve, reject) => {
45
+ HTTPStream.get(params['URL'], function (stream) {
46
+ stream.setEncoding('utf8')
47
+ // if (params['Encoding'] === 'UTF-8') {}
48
+ // stream.setEncoding(params['Encoding'] === 'UTF-8' ? 'utf8' : 'buffer')
49
+ resolve(stream)
50
+ })
51
+ })
52
+ }
53
+
54
+ constructor (id) {
55
+ this.name = this.constructor.name
56
+ this.inputs = [
57
+ { name: 'URL', type: 'string' },
58
+ // { name: 'Encoding', type: 'categorical', options: ['UTF-8', 'Buffer'], default: 'UTF-8' },
59
+ ]
60
+ this.inputType = this.constructor.inputType
61
+ this.outputType = this.constructor.outputType
62
+ this.id = id
63
+ this.expanded = false
64
+ this.displayInputs = [0]
65
+ }
66
+ }
67
+
68
+ let CSVParser = class CSVParser {
69
+ static inputType = 'text'
70
+
71
+ static outputType = 'object'
72
+
73
+ static type = 'parser'
74
+
75
+ static initStream (params) {
76
+ const stream = ParseStream({
77
+ 'delimiter': params['Delimiter'],
78
+ 'cast': true,
79
+ 'columns': true,
80
+ 'comment': '#',
81
+ 'trim': params['Trim'],
82
+ 'relax_column_count': true,
83
+ 'skip_empty_lines': params['Skip empty lines'],
84
+ 'skip_lines_with_error': true
85
+ })
86
+ return stream
87
+ }
88
+
89
+ constructor (id) {
90
+ this.name = this.constructor.name
91
+ this.inputs = [
92
+ { name: 'Delimiter', type: 'string', default: ',' },
93
+ { name: 'Trim', type: 'bool', default: true },
94
+ { name: 'Skip empty lines', type: 'bool', default: true }
95
+ ]
96
+ this.inputType = this.constructor.inputType
97
+ this.outputType = this.constructor.outputType
98
+ this.id = id
99
+ this.expanded = false
100
+ this.displayInputs = []
101
+ }
102
+ }
103
+
104
+ let JSONParser = class JSONParser {
105
+ static inputType = 'text'
106
+
107
+ static outputType = 'object'
108
+
109
+ static type = 'parser'
110
+
111
+ static initStream (params) {
112
+ const stream = JSONStream.parse(params['Path'])
113
+ return stream
114
+ }
115
+
116
+ constructor (id) {
117
+ this.name = this.constructor.name
118
+ this.inputs = [
119
+ { name: 'Path', type: 'string', default: '*' },
120
+ ]
121
+ this.inputType = this.constructor.inputType
122
+ this.outputType = this.constructor.outputType
123
+ this.id = id
124
+ this.expanded = false
125
+ this.displayInputs = []
126
+ }
127
+ }
128
+
129
+ let TextOutput = class TextOutput {
130
+ static inputType = 'text'
131
+
132
+ static outputType = 'html'
133
+
134
+ static type = 'output'
135
+
136
+ static initStream (params) {
137
+ const textOutput = document.getElementById('text-output')
138
+ textOutput.style.display = 'block'
139
+ textOutput.innerText = ''
140
+ const stream = through2(function (chunk, enc, callback) {
141
+ textOutput.innerText += chunk
142
+ this.push(chunk)
143
+ callback()
144
+ })
145
+ return stream
146
+ }
147
+
148
+ constructor (id) {
149
+ this.name = this.constructor.name
150
+ this.inputs = []
151
+ this.inputType = this.constructor.inputType
152
+ this.outputType = this.constructor.outputType
153
+ this.id = id
154
+ this.expanded = false
155
+ this.displayInputs = []
156
+ }
157
+ }
158
+ module.exports = {
159
+ FileLoader,
160
+ HTTPLoader,
161
+ CSVParser,
162
+ JSONParser,
163
+ TextOutput
164
+ }
165
+
166
+
@@ -0,0 +1,108 @@
1
+ const path = require('path')
2
+ const { VueLoaderPlugin } = require('vue-loader') // Support Vue's SFCs
3
+ const TerserPlugin = require('terser-webpack-plugin') // Minimize code
4
+ const webpack = require('webpack')
5
+ const package = require('./package.json')
6
+
7
+ module.exports = (env) => {
8
+ const config = {
9
+ entry: './src/main.js',
10
+ output: {
11
+ filename: 'bundle.js',
12
+ path: path.resolve(__dirname, 'dist'),
13
+ },
14
+ module: {
15
+ rules: [
16
+ {
17
+ test: /\.vue$/,
18
+ loader: 'vue-loader'
19
+ },
20
+ {
21
+ test: /\.js$/,
22
+ loader: 'babel-loader'
23
+ },
24
+ {
25
+ test: /\.mjs$/i, // lodash error (element)
26
+ resolve: { byDependency: { esm: { fullySpecified: false } } }
27
+ },
28
+ {
29
+ test: /\.html/,
30
+ type: 'asset/source'
31
+ },
32
+ {
33
+ test: /\.css$/,
34
+ use: [
35
+ 'vue-style-loader',
36
+ 'css-loader',
37
+ ]
38
+ },
39
+ {
40
+ test: /worker\.js$/,
41
+ loader: "worker-loader",
42
+ options: {
43
+ inline: 'no-fallback'
44
+ },
45
+ },
46
+ {
47
+ test: /\.scss$/,
48
+ use: [
49
+ 'vue-style-loader',
50
+ 'css-loader',
51
+ 'sass-loader'
52
+ ]
53
+ }
54
+ ]
55
+ },
56
+ plugins: [
57
+ new VueLoaderPlugin(),
58
+ // Replace those values in the code
59
+ new webpack.DefinePlugin({
60
+ 'VERSION': JSON.stringify(package.version),
61
+ }),
62
+ // https://stackoverflow.com/a/68723223/2998960
63
+ // Work around for Buffer is undefined:
64
+ // https://github.com/webpack/changelog-v5/issues/10
65
+ new webpack.ProvidePlugin({
66
+ Buffer: ['buffer', 'Buffer'],
67
+ }),
68
+ new webpack.ProvidePlugin({
69
+ process: 'process/browser',
70
+ }),
71
+ ],
72
+ // Load different versions of vue based on RUNTIME value
73
+ resolve: {
74
+ fallback: {
75
+ 'buffer': require.resolve('buffer'),
76
+ 'stream': require.resolve('stream-browserify'),
77
+ 'url': require.resolve('url')
78
+ },
79
+ alias: {
80
+ vue$: 'vue/dist/vue.runtime.esm-bundler.js'
81
+ },
82
+ },
83
+ // Update recommended size limit
84
+ performance: {
85
+ hints: false,
86
+ maxEntrypointSize: 512000,
87
+ maxAssetSize: 512000
88
+ },
89
+ // Remove comments
90
+ optimization: {
91
+ minimize: true,
92
+ minimizer: [new TerserPlugin({
93
+ extractComments: false,
94
+ terserOptions: {
95
+ format: {
96
+ comments: false,
97
+ },
98
+ }
99
+ })],
100
+ },
101
+ // Source map
102
+ devtool: env.DEVELOPMENT
103
+ ? 'eval-source-map'
104
+ : false
105
+ }
106
+
107
+ return config
108
+ }