@cycle/time
Advanced tools
Comparing version 0.15.0 to 0.16.0
@@ -0,1 +1,15 @@ | ||
## 0.16.0 (2018-10-17) | ||
* fix(time): support TypeScript 3.1 ([0c61222](https://github.com/cyclejs/cyclejs/commit/0c61222)) | ||
* fix(time): update cycle/run to v4 ([fe3175c](https://github.com/cyclejs/cyclejs/commit/fe3175c)) | ||
* test(time): fix time tests ([f365dec](https://github.com/cyclejs/cyclejs/commit/f365dec)) | ||
### BREAKING CHANGE | ||
* If you use JavaScript, there are no breaking changes. If you use | ||
TypeScript, this package may not work anymore with versions of TS below | ||
3.1. | ||
<a name="0.15.0"></a> | ||
@@ -2,0 +16,0 @@ # 0.15.0 (2018-07-30) |
@@ -1,2 +0,2 @@ | ||
import xs from 'xstream'; | ||
import { Stream } from 'xstream'; | ||
export declare type Frame = { | ||
@@ -7,3 +7,3 @@ delta: number; | ||
}; | ||
declare function makeAnimationFrames(addFrameCallback: any, currentTime: () => number): () => xs<Frame>; | ||
declare function makeAnimationFrames(addFrameCallback: any, currentTime: () => number): () => Stream<Frame>; | ||
export { makeAnimationFrames }; |
@@ -1,6 +0,5 @@ | ||
import xs from 'xstream'; | ||
declare function makeAssertEqual(timeSource: any, schedule: any, currentTime: () => number, interval: number, addAssert: any): (actual: xs<any>, expected: xs<any>, comparator?: { | ||
(actual: any, expected: any, message?: string | undefined): void; | ||
(actual: any, expected: any, message?: string | undefined): void; | ||
}) => void; | ||
/// <reference types="node" /> | ||
import { Stream } from 'xstream'; | ||
import { deepEqual } from 'assert'; | ||
declare function makeAssertEqual(timeSource: any, schedule: any, currentTime: () => number, interval: number, addAssert: any): (actual: Stream<any>, expected: Stream<any>, comparator?: typeof deepEqual) => void; | ||
export { makeAssertEqual }; |
@@ -9,7 +9,7 @@ "use strict"; | ||
var failReasons = []; | ||
if (completeStore['actual'].length !== completeStore['expected'].length) { | ||
if (completeStore.actual.length !== completeStore.expected.length) { | ||
failReasons.push("Length of actual and expected differs"); | ||
} | ||
completeStore['actual'].forEach(function (actual, index) { | ||
var expected = completeStore['expected'][index]; | ||
completeStore.actual.forEach(function (actual, index) { | ||
var expected = completeStore.expected[index]; | ||
if (actual === undefined) { | ||
@@ -90,3 +90,3 @@ failReasons.push("Actual at index " + index + " was undefined"); | ||
assert.state = 'failed'; | ||
assert.error = new Error(strip("\nExpected\n\n" + diagramString(completeStore['expected'], interval) + "\n\nGot\n\n" + diagramString(completeStore['actual'], interval) + "\n\nFailed because:\n\n" + failReasons.map(function (reason) { return " * " + reason; }).join('\n') + "\n\n" + displayUnexpectedErrors(assert.unexpectedErrors) + "\n ")); | ||
assert.error = new Error(strip("\nExpected\n\n" + diagramString(completeStore.expected, interval) + "\n\nGot\n\n" + diagramString(completeStore.actual, interval) + "\n\nFailed because:\n\n" + failReasons.map(function (reason) { return " * " + reason; }).join('\n') + "\n\n" + displayUnexpectedErrors(assert.unexpectedErrors) + "\n ")); | ||
} | ||
@@ -110,9 +110,7 @@ } | ||
var expectedLog$ = Time.record(expected); | ||
xstream_1.default | ||
.combine(xstream_1.default.fromObservable(actualLog$), xstream_1.default.fromObservable(expectedLog$)) | ||
.addListener({ | ||
xstream_1.default.combine(xstream_1.default.fromObservable(actualLog$), xstream_1.default.fromObservable(expectedLog$)).addListener({ | ||
next: function (_a) { | ||
var aLog = _a[0], bLog = _a[1]; | ||
completeStore['actual'] = aLog; | ||
completeStore['expected'] = bLog; | ||
completeStore.actual = aLog; | ||
completeStore.expected = bLog; | ||
}, | ||
@@ -119,0 +117,0 @@ complete: function () { |
@@ -1,4 +0,4 @@ | ||
import xs from 'xstream'; | ||
import { Stream } from 'xstream'; | ||
import { OperatorArgs } from './types'; | ||
declare function makeDebounce(createOperator: () => OperatorArgs<any>): (debounceInterval: number) => <T>(stream: xs<T>) => xs<T>; | ||
declare function makeDebounce(createOperator: () => OperatorArgs<any>): (debounceInterval: number) => <T>(stream: Stream<T>) => Stream<T>; | ||
export { makeDebounce }; |
@@ -1,4 +0,4 @@ | ||
import xs from 'xstream'; | ||
import { Stream } from 'xstream'; | ||
import { OperatorArgs } from './types'; | ||
declare function makeDelay(createOperator: () => OperatorArgs<any>): (delayTime: number) => <T>(stream: xs<T>) => xs<T>; | ||
declare function makeDelay(createOperator: () => OperatorArgs<any>): (delayTime: number) => <T>(stream: Stream<T>) => Stream<T>; | ||
export { makeDelay }; |
@@ -1,3 +0,3 @@ | ||
import xs from 'xstream'; | ||
declare function makeDiagram(schedule: any, currentTime: () => number, interval: number, setMaxTime: any): (diagramString: string, values?: {}) => xs<any>; | ||
import { Stream } from 'xstream'; | ||
declare function makeDiagram(schedule: any, currentTime: () => number, interval: number, setMaxTime: any): (diagramString: string, values?: {}) => Stream<any>; | ||
export { makeDiagram }; |
@@ -1,4 +0,5 @@ | ||
declare function mockTimeSource({interval}?: { | ||
interval?: number; | ||
/// <reference path="../../../custom-typings.d.ts" /> | ||
declare function mockTimeSource({ interval }?: { | ||
interval?: number | undefined; | ||
}): any; | ||
export { mockTimeSource }; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
///<reference path="../custom-typings.d.ts" /> | ||
var adapt_1 = require("@cycle/run/lib/adapt"); | ||
@@ -4,0 +5,0 @@ var xstream_1 = require("xstream"); |
@@ -1,4 +0,4 @@ | ||
import xs from 'xstream'; | ||
import { Stream } from 'xstream'; | ||
import { OperatorArgs } from './types'; | ||
declare function makePeriodic(createOperator: () => OperatorArgs<any>): (period: number) => xs<number>; | ||
declare function makePeriodic(createOperator: () => OperatorArgs<any>): (period: number) => Stream<number>; | ||
export { makePeriodic }; |
@@ -1,3 +0,3 @@ | ||
import xs from 'xstream'; | ||
declare function makeRecord(schedule: any, currentTime: () => number, interval: number): (stream: xs<any>) => xs<any>; | ||
import { Stream } from 'xstream'; | ||
declare function makeRecord(schedule: any, currentTime: () => number, interval: number): (stream: Stream<any>) => Stream<any>; | ||
export { makeRecord }; |
@@ -29,5 +29,3 @@ "use strict"; | ||
start: function (listener) { | ||
xstream_1.default | ||
.fromObservable(stream) | ||
.addListener(recordListener(currentTime, listener)); | ||
xstream_1.default.fromObservable(stream).addListener(recordListener(currentTime, listener)); | ||
}, | ||
@@ -34,0 +32,0 @@ stop: function () { }, |
@@ -1,3 +0,3 @@ | ||
import xs from 'xstream'; | ||
declare function makeThrottleAnimation(timeSource: any, schedule: any, currentTime: () => number): <T>(stream: xs<T>) => xs<T>; | ||
import { Stream } from 'xstream'; | ||
declare function makeThrottleAnimation(timeSource: any, schedule: any, currentTime: () => number): <T>(stream: Stream<T>) => Stream<T>; | ||
export { makeThrottleAnimation }; |
@@ -1,4 +0,4 @@ | ||
import xs from 'xstream'; | ||
import { Stream } from 'xstream'; | ||
import { OperatorArgs } from './types'; | ||
declare function makeThrottle(createOperator: () => OperatorArgs<any>): (period: number) => <T>(stream: xs<T>) => xs<T>; | ||
declare function makeThrottle(createOperator: () => OperatorArgs<any>): (period: number) => <T>(stream: Stream<T>) => Stream<T>; | ||
export { makeThrottle }; |
@@ -1,2 +0,2 @@ | ||
import xs from 'xstream'; | ||
import { Stream } from 'xstream'; | ||
export declare type Frame = { | ||
@@ -7,3 +7,3 @@ delta: number; | ||
}; | ||
declare function makeAnimationFrames(addFrameCallback: any, currentTime: () => number): () => xs<Frame>; | ||
declare function makeAnimationFrames(addFrameCallback: any, currentTime: () => number): () => Stream<Frame>; | ||
export { makeAnimationFrames }; |
@@ -1,6 +0,5 @@ | ||
import xs from 'xstream'; | ||
declare function makeAssertEqual(timeSource: any, schedule: any, currentTime: () => number, interval: number, addAssert: any): (actual: xs<any>, expected: xs<any>, comparator?: { | ||
(actual: any, expected: any, message?: string | undefined): void; | ||
(actual: any, expected: any, message?: string | undefined): void; | ||
}) => void; | ||
/// <reference types="node" /> | ||
import { Stream } from 'xstream'; | ||
import { deepEqual } from 'assert'; | ||
declare function makeAssertEqual(timeSource: any, schedule: any, currentTime: () => number, interval: number, addAssert: any): (actual: Stream<any>, expected: Stream<any>, comparator?: typeof deepEqual) => void; | ||
export { makeAssertEqual }; |
@@ -7,7 +7,7 @@ import xs from 'xstream'; | ||
var failReasons = []; | ||
if (completeStore['actual'].length !== completeStore['expected'].length) { | ||
if (completeStore.actual.length !== completeStore.expected.length) { | ||
failReasons.push("Length of actual and expected differs"); | ||
} | ||
completeStore['actual'].forEach(function (actual, index) { | ||
var expected = completeStore['expected'][index]; | ||
completeStore.actual.forEach(function (actual, index) { | ||
var expected = completeStore.expected[index]; | ||
if (actual === undefined) { | ||
@@ -88,3 +88,3 @@ failReasons.push("Actual at index " + index + " was undefined"); | ||
assert.state = 'failed'; | ||
assert.error = new Error(strip("\nExpected\n\n" + diagramString(completeStore['expected'], interval) + "\n\nGot\n\n" + diagramString(completeStore['actual'], interval) + "\n\nFailed because:\n\n" + failReasons.map(function (reason) { return " * " + reason; }).join('\n') + "\n\n" + displayUnexpectedErrors(assert.unexpectedErrors) + "\n ")); | ||
assert.error = new Error(strip("\nExpected\n\n" + diagramString(completeStore.expected, interval) + "\n\nGot\n\n" + diagramString(completeStore.actual, interval) + "\n\nFailed because:\n\n" + failReasons.map(function (reason) { return " * " + reason; }).join('\n') + "\n\n" + displayUnexpectedErrors(assert.unexpectedErrors) + "\n ")); | ||
} | ||
@@ -108,9 +108,7 @@ } | ||
var expectedLog$ = Time.record(expected); | ||
xs | ||
.combine(xs.fromObservable(actualLog$), xs.fromObservable(expectedLog$)) | ||
.addListener({ | ||
xs.combine(xs.fromObservable(actualLog$), xs.fromObservable(expectedLog$)).addListener({ | ||
next: function (_a) { | ||
var aLog = _a[0], bLog = _a[1]; | ||
completeStore['actual'] = aLog; | ||
completeStore['expected'] = bLog; | ||
completeStore.actual = aLog; | ||
completeStore.expected = bLog; | ||
}, | ||
@@ -117,0 +115,0 @@ complete: function () { |
@@ -1,4 +0,4 @@ | ||
import xs from 'xstream'; | ||
import { Stream } from 'xstream'; | ||
import { OperatorArgs } from './types'; | ||
declare function makeDebounce(createOperator: () => OperatorArgs<any>): (debounceInterval: number) => <T>(stream: xs<T>) => xs<T>; | ||
declare function makeDebounce(createOperator: () => OperatorArgs<any>): (debounceInterval: number) => <T>(stream: Stream<T>) => Stream<T>; | ||
export { makeDebounce }; |
@@ -1,4 +0,4 @@ | ||
import xs from 'xstream'; | ||
import { Stream } from 'xstream'; | ||
import { OperatorArgs } from './types'; | ||
declare function makeDelay(createOperator: () => OperatorArgs<any>): (delayTime: number) => <T>(stream: xs<T>) => xs<T>; | ||
declare function makeDelay(createOperator: () => OperatorArgs<any>): (delayTime: number) => <T>(stream: Stream<T>) => Stream<T>; | ||
export { makeDelay }; |
@@ -1,3 +0,3 @@ | ||
import xs from 'xstream'; | ||
declare function makeDiagram(schedule: any, currentTime: () => number, interval: number, setMaxTime: any): (diagramString: string, values?: {}) => xs<any>; | ||
import { Stream } from 'xstream'; | ||
declare function makeDiagram(schedule: any, currentTime: () => number, interval: number, setMaxTime: any): (diagramString: string, values?: {}) => Stream<any>; | ||
export { makeDiagram }; |
@@ -1,4 +0,5 @@ | ||
declare function mockTimeSource({interval}?: { | ||
interval?: number; | ||
/// <reference path="../../../custom-typings.d.ts" /> | ||
declare function mockTimeSource({ interval }?: { | ||
interval?: number | undefined; | ||
}): any; | ||
export { mockTimeSource }; |
@@ -0,1 +1,2 @@ | ||
///<reference path="../custom-typings.d.ts" /> | ||
import { adapt } from '@cycle/run/lib/adapt'; | ||
@@ -2,0 +3,0 @@ import xs from 'xstream'; |
@@ -1,4 +0,4 @@ | ||
import xs from 'xstream'; | ||
import { Stream } from 'xstream'; | ||
import { OperatorArgs } from './types'; | ||
declare function makePeriodic(createOperator: () => OperatorArgs<any>): (period: number) => xs<number>; | ||
declare function makePeriodic(createOperator: () => OperatorArgs<any>): (period: number) => Stream<number>; | ||
export { makePeriodic }; |
@@ -1,3 +0,3 @@ | ||
import xs from 'xstream'; | ||
declare function makeRecord(schedule: any, currentTime: () => number, interval: number): (stream: xs<any>) => xs<any>; | ||
import { Stream } from 'xstream'; | ||
declare function makeRecord(schedule: any, currentTime: () => number, interval: number): (stream: Stream<any>) => Stream<any>; | ||
export { makeRecord }; |
@@ -27,5 +27,3 @@ import xs from 'xstream'; | ||
start: function (listener) { | ||
xs | ||
.fromObservable(stream) | ||
.addListener(recordListener(currentTime, listener)); | ||
xs.fromObservable(stream).addListener(recordListener(currentTime, listener)); | ||
}, | ||
@@ -32,0 +30,0 @@ stop: function () { }, |
@@ -1,3 +0,3 @@ | ||
import xs from 'xstream'; | ||
declare function makeThrottleAnimation(timeSource: any, schedule: any, currentTime: () => number): <T>(stream: xs<T>) => xs<T>; | ||
import { Stream } from 'xstream'; | ||
declare function makeThrottleAnimation(timeSource: any, schedule: any, currentTime: () => number): <T>(stream: Stream<T>) => Stream<T>; | ||
export { makeThrottleAnimation }; |
@@ -1,4 +0,4 @@ | ||
import xs from 'xstream'; | ||
import { Stream } from 'xstream'; | ||
import { OperatorArgs } from './types'; | ||
declare function makeThrottle(createOperator: () => OperatorArgs<any>): (period: number) => <T>(stream: xs<T>) => xs<T>; | ||
declare function makeThrottle(createOperator: () => OperatorArgs<any>): (period: number) => <T>(stream: Stream<T>) => Stream<T>; | ||
export { makeThrottle }; |
import { Stream } from 'most'; | ||
import { Frame } from './lib/cjs/src/animation-frames'; | ||
import { Comparator, OperatorArgs } from './lib/cjs/src/types'; | ||
import { Comparator, OperatorArgs } from './src/types'; | ||
declare type Operator = <T>(stream: Stream<T>) => Stream<T>; | ||
@@ -5,0 +5,0 @@ interface TimeSource { |
@@ -5,4 +5,4 @@ "use strict"; | ||
var adapt_1 = require("@cycle/run/lib/adapt"); | ||
var mock_time_source_1 = require("./lib/cjs/src/mock-time-source"); | ||
var time_driver_1 = require("./lib/cjs/src/time-driver"); | ||
var mock_time_source_1 = require("./src/mock-time-source"); | ||
var time_driver_1 = require("./src/time-driver"); | ||
adapt_1.setAdapt(function (stream) { return most.from(stream); }); | ||
@@ -9,0 +9,0 @@ function mockTimeSource(args) { |
{ | ||
"name": "@cycle/time", | ||
"version": "0.15.0", | ||
"version": "0.16.0", | ||
"description": "A time driver designed to enable awesome testing and dev tooling", | ||
@@ -15,23 +15,28 @@ "license": "MIT", | ||
"dependencies": { | ||
"@cycle/run": "3.x", | ||
"@cycle/run": "^5.1.0", | ||
"combine-errors": "3.0.x", | ||
"most": "*", | ||
"performance-now": "^2.1.0", | ||
"raf": "3.3.x", | ||
"raf": "3.4.x", | ||
"rxjs": "*", | ||
"setimmediate": "1.0.x", | ||
"sorted-immutable-list": "1.1.x", | ||
"variable-diff": "1.1.x", | ||
"xstream": "*", | ||
"most": "*", | ||
"rxjs": "*" | ||
"xstream": "*" | ||
}, | ||
"devDependencies": { | ||
"@cycle/dom": "17.6", | ||
"@cycle/rxjs-run": "7.0", | ||
"@types/mocha": "2.2.x", | ||
"@types/node": "7.0.x", | ||
"@cycle/dom": "^22.0.0", | ||
"@cycle/rxjs-run": "^10.0.0", | ||
"@types/mocha": "5.2.x", | ||
"@types/node": "10.11.x", | ||
"budo": "^11.5.0", | ||
"garnish": "5.2.x", | ||
"markdown-doctest": "0.9.x", | ||
"most": "1.5.x", | ||
"rxjs": "6.2.x", | ||
"snabbdom-selector": "1.2.x" | ||
"mocha": "^5.2.0", | ||
"most": "1.7.x", | ||
"rxjs": "6.3.x", | ||
"snabbdom-selector": "4.0.x", | ||
"ts-node": "^7.0.1", | ||
"tslint": "^5.11.0", | ||
"typescript": "3.1.x" | ||
}, | ||
@@ -49,5 +54,2 @@ "publishConfig": { | ||
], | ||
"browserify-shim": { | ||
"xstream": "global:xstream" | ||
}, | ||
"react-native": { | ||
@@ -57,12 +59,17 @@ "variable-diff": false | ||
"scripts": { | ||
"start": "../node_modules/.bin/budo -d example example/index.ts:index.js --live -- -p tsify", | ||
"test-watch": "../node_modules/.bin/mocha 'test/**/*.ts' --require ts-node/register --watch -R min", | ||
"test-node": "../node_modules/.bin/mocha 'test/**/*.ts' --require ts-node/register --exit", | ||
"test": "npm run test-node && npm run test-docs", | ||
"test-ci": "npm run test", | ||
"build": "pnpm run build-cjs && pnpm run build-es6", | ||
"build-cjs": "tsc --module commonjs --outDir ./lib/cjs", | ||
"build-es6": "tsc --module es6 --outDir ./lib/es6", | ||
"docs": ":", | ||
"lint": "tslint --project tsconfig.lint.json --config ../tslint.json", | ||
"changelog": "cd .. && node .scripts/update-changelogs.js time", | ||
"start": "budo -d example example/index.ts:index.js --live -- -p tsify", | ||
"test-watch": "mocha 'test/**/*.ts' --require ts-node/register --watch -R min", | ||
"test-node": "mocha 'test/**/*.ts' --require ts-node/register --exit", | ||
"test": "pnpm run test-node && pnpm run test-docs", | ||
"test-ci": "pnpm run test", | ||
"test-docs": "markdown-doctest", | ||
"browserify": "../node_modules/.bin/browserify lib/cjs/index.js --global-transform=browserify-shim --standalone CycleTime --exclude xstream --outfile dist/cycle-time.js", | ||
"minify": "node ../.scripts/minify.js dist/cycle-time.js dist/cycle-time.min.js", | ||
"postlib": "../node_modules/.bin/tsc rxjs.ts most.ts --declaration --lib DOM,ES5,ES6" | ||
"postbuild": "tsc rxjs.ts most.ts --declaration --lib DOM,ES5,ES6 && rm src/*.d.ts && rm src/*.js", | ||
"prepublishOnly": "pnpm run build && pnpm run test" | ||
} | ||
} |
import { Observable } from 'rxjs'; | ||
import { Frame } from './lib/cjs/src/animation-frames'; | ||
import { Comparator, OperatorArgs } from './lib/cjs/src/types'; | ||
import { Comparator, OperatorArgs } from './src/types'; | ||
declare type Operator = <T>(observable: Observable<T>) => Observable<T>; | ||
@@ -5,0 +5,0 @@ interface TimeSource { |
@@ -6,4 +6,4 @@ "use strict"; | ||
var adapt_1 = require("@cycle/run/lib/adapt"); | ||
var mock_time_source_1 = require("./lib/cjs/src/mock-time-source"); | ||
var time_driver_1 = require("./lib/cjs/src/time-driver"); | ||
var mock_time_source_1 = require("./src/mock-time-source"); | ||
var time_driver_1 = require("./src/time-driver"); | ||
adapt_1.setAdapt(rxjs_1.from); | ||
@@ -10,0 +10,0 @@ function mockTimeSource(args) { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 3 instances in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
1
1
1
154085
14
104
1970
+ Added@cycle/run@5.7.0(transitive)
+ Addedquicktask@1.2.0(transitive)
+ Addedraf@3.4.1(transitive)
- Removed@cycle/run@3.4.0(transitive)
- Removedraf@3.3.2(transitive)
Updated@cycle/run@^5.1.0
Updatedraf@3.4.x